1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19#include <linux/module.h>
20#include <linux/device.h>
21#include <linux/slab.h>
22
23#include <trace/events/host1x.h>
24
25#include "syncpt.h"
26#include "dev.h"
27#include "intr.h"
28#include "debug.h"
29
30#define SYNCPT_CHECK_PERIOD (2 * HZ)
31#define MAX_STUCK_CHECK_COUNT 15
32
33static struct host1x_syncpt_base *
34host1x_syncpt_base_request(struct host1x *host)
35{
36 struct host1x_syncpt_base *bases = host->bases;
37 unsigned int i;
38
39 for (i = 0; i < host->info->nb_bases; i++)
40 if (!bases[i].requested)
41 break;
42
43 if (i >= host->info->nb_bases)
44 return NULL;
45
46 bases[i].requested = true;
47 return &bases[i];
48}
49
50static void host1x_syncpt_base_free(struct host1x_syncpt_base *base)
51{
52 if (base)
53 base->requested = false;
54}
55
56static struct host1x_syncpt *host1x_syncpt_alloc(struct host1x *host,
57 struct host1x_client *client,
58 unsigned long flags)
59{
60 struct host1x_syncpt *sp = host->syncpt;
61 unsigned int i;
62 char *name;
63
64 mutex_lock(&host->syncpt_mutex);
65
66 for (i = 0; i < host->info->nb_pts && sp->name; i++, sp++)
67 ;
68
69 if (i >= host->info->nb_pts)
70 goto unlock;
71
72 if (flags & HOST1X_SYNCPT_HAS_BASE) {
73 sp->base = host1x_syncpt_base_request(host);
74 if (!sp->base)
75 goto unlock;
76 }
77
78 name = kasprintf(GFP_KERNEL, "%02u-%s", sp->id,
79 client ? dev_name(client->dev) : NULL);
80 if (!name)
81 goto free_base;
82
83 sp->client = client;
84 sp->name = name;
85
86 if (flags & HOST1X_SYNCPT_CLIENT_MANAGED)
87 sp->client_managed = true;
88 else
89 sp->client_managed = false;
90
91 mutex_unlock(&host->syncpt_mutex);
92 return sp;
93
94free_base:
95 host1x_syncpt_base_free(sp->base);
96 sp->base = NULL;
97unlock:
98 mutex_unlock(&host->syncpt_mutex);
99 return NULL;
100}
101
102
103
104
105
106
107
108
109
110u32 host1x_syncpt_id(struct host1x_syncpt *sp)
111{
112 return sp->id;
113}
114EXPORT_SYMBOL(host1x_syncpt_id);
115
116
117
118
119
120
121u32 host1x_syncpt_incr_max(struct host1x_syncpt *sp, u32 incrs)
122{
123 return (u32)atomic_add_return(incrs, &sp->max_val);
124}
125EXPORT_SYMBOL(host1x_syncpt_incr_max);
126
127
128
129
130void host1x_syncpt_restore(struct host1x *host)
131{
132 struct host1x_syncpt *sp_base = host->syncpt;
133 unsigned int i;
134
135 for (i = 0; i < host1x_syncpt_nb_pts(host); i++)
136 host1x_hw_syncpt_restore(host, sp_base + i);
137
138 for (i = 0; i < host1x_syncpt_nb_bases(host); i++)
139 host1x_hw_syncpt_restore_wait_base(host, sp_base + i);
140
141 wmb();
142}
143
144
145
146
147
148void host1x_syncpt_save(struct host1x *host)
149{
150 struct host1x_syncpt *sp_base = host->syncpt;
151 unsigned int i;
152
153 for (i = 0; i < host1x_syncpt_nb_pts(host); i++) {
154 if (host1x_syncpt_client_managed(sp_base + i))
155 host1x_hw_syncpt_load(host, sp_base + i);
156 else
157 WARN_ON(!host1x_syncpt_idle(sp_base + i));
158 }
159
160 for (i = 0; i < host1x_syncpt_nb_bases(host); i++)
161 host1x_hw_syncpt_load_wait_base(host, sp_base + i);
162}
163
164
165
166
167
168u32 host1x_syncpt_load(struct host1x_syncpt *sp)
169{
170 u32 val;
171
172 val = host1x_hw_syncpt_load(sp->host, sp);
173 trace_host1x_syncpt_load_min(sp->id, val);
174
175 return val;
176}
177
178
179
180
181u32 host1x_syncpt_load_wait_base(struct host1x_syncpt *sp)
182{
183 host1x_hw_syncpt_load_wait_base(sp->host, sp);
184
185 return sp->base_val;
186}
187
188
189
190
191
192int host1x_syncpt_incr(struct host1x_syncpt *sp)
193{
194 return host1x_hw_syncpt_cpu_incr(sp->host, sp);
195}
196EXPORT_SYMBOL(host1x_syncpt_incr);
197
198
199
200
201
202static bool syncpt_load_min_is_expired(struct host1x_syncpt *sp, u32 thresh)
203{
204 host1x_hw_syncpt_load(sp->host, sp);
205
206 return host1x_syncpt_is_expired(sp, thresh);
207}
208
209
210
211
212
213
214
215
216int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout,
217 u32 *value)
218{
219 DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
220 void *ref;
221 struct host1x_waitlist *waiter;
222 int err = 0, check_count = 0;
223 u32 val;
224
225 if (value)
226 *value = 0;
227
228
229 if (host1x_syncpt_is_expired(sp, thresh)) {
230 if (value)
231 *value = host1x_syncpt_load(sp);
232
233 return 0;
234 }
235
236
237 val = host1x_hw_syncpt_load(sp->host, sp);
238 if (host1x_syncpt_is_expired(sp, thresh)) {
239 if (value)
240 *value = val;
241
242 goto done;
243 }
244
245 if (!timeout) {
246 err = -EAGAIN;
247 goto done;
248 }
249
250
251 waiter = kzalloc(sizeof(*waiter), GFP_KERNEL);
252 if (!waiter) {
253 err = -ENOMEM;
254 goto done;
255 }
256
257
258 err = host1x_intr_add_action(sp->host, sp, thresh,
259 HOST1X_INTR_ACTION_WAKEUP_INTERRUPTIBLE,
260 &wq, waiter, &ref);
261 if (err)
262 goto done;
263
264 err = -EAGAIN;
265
266 if (timeout < 0)
267 timeout = LONG_MAX;
268
269
270 while (timeout) {
271 long check = min_t(long, SYNCPT_CHECK_PERIOD, timeout);
272 int remain;
273
274 remain = wait_event_interruptible_timeout(wq,
275 syncpt_load_min_is_expired(sp, thresh),
276 check);
277 if (remain > 0 || host1x_syncpt_is_expired(sp, thresh)) {
278 if (value)
279 *value = host1x_syncpt_load(sp);
280
281 err = 0;
282
283 break;
284 }
285
286 if (remain < 0) {
287 err = remain;
288 break;
289 }
290
291 timeout -= check;
292
293 if (timeout && check_count <= MAX_STUCK_CHECK_COUNT) {
294 dev_warn(sp->host->dev,
295 "%s: syncpoint id %u (%s) stuck waiting %d, timeout=%ld\n",
296 current->comm, sp->id, sp->name,
297 thresh, timeout);
298
299 host1x_debug_dump_syncpts(sp->host);
300
301 if (check_count == MAX_STUCK_CHECK_COUNT)
302 host1x_debug_dump(sp->host);
303
304 check_count++;
305 }
306 }
307
308 host1x_intr_put_ref(sp->host, sp->id, ref);
309
310done:
311 return err;
312}
313EXPORT_SYMBOL(host1x_syncpt_wait);
314
315
316
317
318bool host1x_syncpt_is_expired(struct host1x_syncpt *sp, u32 thresh)
319{
320 u32 current_val;
321 u32 future_val;
322
323 smp_rmb();
324
325 current_val = (u32)atomic_read(&sp->min_val);
326 future_val = (u32)atomic_read(&sp->max_val);
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370 if (!host1x_syncpt_client_managed(sp))
371 return future_val - thresh >= current_val - thresh;
372 else
373 return (s32)(current_val - thresh) >= 0;
374}
375
376int host1x_syncpt_init(struct host1x *host)
377{
378 struct host1x_syncpt_base *bases;
379 struct host1x_syncpt *syncpt;
380 unsigned int i;
381
382 syncpt = devm_kcalloc(host->dev, host->info->nb_pts, sizeof(*syncpt),
383 GFP_KERNEL);
384 if (!syncpt)
385 return -ENOMEM;
386
387 bases = devm_kcalloc(host->dev, host->info->nb_bases, sizeof(*bases),
388 GFP_KERNEL);
389 if (!bases)
390 return -ENOMEM;
391
392 for (i = 0; i < host->info->nb_pts; i++) {
393 syncpt[i].id = i;
394 syncpt[i].host = host;
395
396
397
398
399
400
401 host1x_hw_syncpt_assign_to_channel(host, &syncpt[i], NULL);
402 }
403
404 for (i = 0; i < host->info->nb_bases; i++)
405 bases[i].id = i;
406
407 mutex_init(&host->syncpt_mutex);
408 host->syncpt = syncpt;
409 host->bases = bases;
410
411 host1x_syncpt_restore(host);
412 host1x_hw_syncpt_enable_protection(host);
413
414
415 host->nop_sp = host1x_syncpt_alloc(host, NULL, 0);
416 if (!host->nop_sp)
417 return -ENOMEM;
418
419 return 0;
420}
421
422
423
424
425
426
427
428
429
430
431
432struct host1x_syncpt *host1x_syncpt_request(struct host1x_client *client,
433 unsigned long flags)
434{
435 struct host1x *host = dev_get_drvdata(client->parent->parent);
436
437 return host1x_syncpt_alloc(host, client, flags);
438}
439EXPORT_SYMBOL(host1x_syncpt_request);
440
441
442
443
444
445
446
447
448
449
450
451void host1x_syncpt_free(struct host1x_syncpt *sp)
452{
453 if (!sp)
454 return;
455
456 mutex_lock(&sp->host->syncpt_mutex);
457
458 host1x_syncpt_base_free(sp->base);
459 kfree(sp->name);
460 sp->base = NULL;
461 sp->client = NULL;
462 sp->name = NULL;
463 sp->client_managed = false;
464
465 mutex_unlock(&sp->host->syncpt_mutex);
466}
467EXPORT_SYMBOL(host1x_syncpt_free);
468
469void host1x_syncpt_deinit(struct host1x *host)
470{
471 struct host1x_syncpt *sp = host->syncpt;
472 unsigned int i;
473
474 for (i = 0; i < host->info->nb_pts; i++, sp++)
475 kfree(sp->name);
476}
477
478
479
480
481
482
483
484
485u32 host1x_syncpt_read_max(struct host1x_syncpt *sp)
486{
487 smp_rmb();
488
489 return (u32)atomic_read(&sp->max_val);
490}
491EXPORT_SYMBOL(host1x_syncpt_read_max);
492
493
494
495
496
497
498
499
500u32 host1x_syncpt_read_min(struct host1x_syncpt *sp)
501{
502 smp_rmb();
503
504 return (u32)atomic_read(&sp->min_val);
505}
506EXPORT_SYMBOL(host1x_syncpt_read_min);
507
508
509
510
511
512u32 host1x_syncpt_read(struct host1x_syncpt *sp)
513{
514 return host1x_syncpt_load(sp);
515}
516EXPORT_SYMBOL(host1x_syncpt_read);
517
518unsigned int host1x_syncpt_nb_pts(struct host1x *host)
519{
520 return host->info->nb_pts;
521}
522
523unsigned int host1x_syncpt_nb_bases(struct host1x *host)
524{
525 return host->info->nb_bases;
526}
527
528unsigned int host1x_syncpt_nb_mlocks(struct host1x *host)
529{
530 return host->info->nb_mlocks;
531}
532
533
534
535
536
537
538struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, unsigned int id)
539{
540 if (id >= host->info->nb_pts)
541 return NULL;
542
543 return host->syncpt + id;
544}
545EXPORT_SYMBOL(host1x_syncpt_get);
546
547
548
549
550
551struct host1x_syncpt_base *host1x_syncpt_get_base(struct host1x_syncpt *sp)
552{
553 return sp ? sp->base : NULL;
554}
555EXPORT_SYMBOL(host1x_syncpt_get_base);
556
557
558
559
560
561u32 host1x_syncpt_base_id(struct host1x_syncpt_base *base)
562{
563 return base->id;
564}
565EXPORT_SYMBOL(host1x_syncpt_base_id);
566