0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/module.h>
0009 #include <linux/device.h>
0010 #include <linux/slab.h>
0011
0012 #include <trace/events/host1x.h>
0013
0014 #include "syncpt.h"
0015 #include "dev.h"
0016 #include "intr.h"
0017 #include "debug.h"
0018
0019 #define SYNCPT_CHECK_PERIOD (2 * HZ)
0020 #define MAX_STUCK_CHECK_COUNT 15
0021
0022 static struct host1x_syncpt_base *
0023 host1x_syncpt_base_request(struct host1x *host)
0024 {
0025 struct host1x_syncpt_base *bases = host->bases;
0026 unsigned int i;
0027
0028 for (i = 0; i < host->info->nb_bases; i++)
0029 if (!bases[i].requested)
0030 break;
0031
0032 if (i >= host->info->nb_bases)
0033 return NULL;
0034
0035 bases[i].requested = true;
0036 return &bases[i];
0037 }
0038
0039 static void host1x_syncpt_base_free(struct host1x_syncpt_base *base)
0040 {
0041 if (base)
0042 base->requested = false;
0043 }
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057 struct host1x_syncpt *host1x_syncpt_alloc(struct host1x *host,
0058 unsigned long flags,
0059 const char *name)
0060 {
0061 struct host1x_syncpt *sp = host->syncpt;
0062 char *full_name;
0063 unsigned int i;
0064
0065 if (!name)
0066 return NULL;
0067
0068 mutex_lock(&host->syncpt_mutex);
0069
0070 for (i = 0; i < host->info->nb_pts && kref_read(&sp->ref); i++, sp++)
0071 ;
0072
0073 if (i >= host->info->nb_pts)
0074 goto unlock;
0075
0076 if (flags & HOST1X_SYNCPT_HAS_BASE) {
0077 sp->base = host1x_syncpt_base_request(host);
0078 if (!sp->base)
0079 goto unlock;
0080 }
0081
0082 full_name = kasprintf(GFP_KERNEL, "%u-%s", sp->id, name);
0083 if (!full_name)
0084 goto free_base;
0085
0086 sp->name = full_name;
0087
0088 if (flags & HOST1X_SYNCPT_CLIENT_MANAGED)
0089 sp->client_managed = true;
0090 else
0091 sp->client_managed = false;
0092
0093 kref_init(&sp->ref);
0094
0095 mutex_unlock(&host->syncpt_mutex);
0096 return sp;
0097
0098 free_base:
0099 host1x_syncpt_base_free(sp->base);
0100 sp->base = NULL;
0101 unlock:
0102 mutex_unlock(&host->syncpt_mutex);
0103 return NULL;
0104 }
0105 EXPORT_SYMBOL(host1x_syncpt_alloc);
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115 u32 host1x_syncpt_id(struct host1x_syncpt *sp)
0116 {
0117 return sp->id;
0118 }
0119 EXPORT_SYMBOL(host1x_syncpt_id);
0120
0121
0122
0123
0124
0125
0126 u32 host1x_syncpt_incr_max(struct host1x_syncpt *sp, u32 incrs)
0127 {
0128 return (u32)atomic_add_return(incrs, &sp->max_val);
0129 }
0130 EXPORT_SYMBOL(host1x_syncpt_incr_max);
0131
0132
0133
0134
0135 void host1x_syncpt_restore(struct host1x *host)
0136 {
0137 struct host1x_syncpt *sp_base = host->syncpt;
0138 unsigned int i;
0139
0140 for (i = 0; i < host1x_syncpt_nb_pts(host); i++) {
0141
0142
0143
0144
0145
0146 host1x_hw_syncpt_assign_to_channel(host, sp_base + i, NULL);
0147 host1x_hw_syncpt_restore(host, sp_base + i);
0148 }
0149
0150 for (i = 0; i < host1x_syncpt_nb_bases(host); i++)
0151 host1x_hw_syncpt_restore_wait_base(host, sp_base + i);
0152
0153 host1x_hw_syncpt_enable_protection(host);
0154
0155 wmb();
0156 }
0157
0158
0159
0160
0161
0162 void host1x_syncpt_save(struct host1x *host)
0163 {
0164 struct host1x_syncpt *sp_base = host->syncpt;
0165 unsigned int i;
0166
0167 for (i = 0; i < host1x_syncpt_nb_pts(host); i++) {
0168 if (host1x_syncpt_client_managed(sp_base + i))
0169 host1x_hw_syncpt_load(host, sp_base + i);
0170 else
0171 WARN_ON(!host1x_syncpt_idle(sp_base + i));
0172 }
0173
0174 for (i = 0; i < host1x_syncpt_nb_bases(host); i++)
0175 host1x_hw_syncpt_load_wait_base(host, sp_base + i);
0176 }
0177
0178
0179
0180
0181
0182 u32 host1x_syncpt_load(struct host1x_syncpt *sp)
0183 {
0184 u32 val;
0185
0186 val = host1x_hw_syncpt_load(sp->host, sp);
0187 trace_host1x_syncpt_load_min(sp->id, val);
0188
0189 return val;
0190 }
0191
0192
0193
0194
0195 u32 host1x_syncpt_load_wait_base(struct host1x_syncpt *sp)
0196 {
0197 host1x_hw_syncpt_load_wait_base(sp->host, sp);
0198
0199 return sp->base_val;
0200 }
0201
0202
0203
0204
0205
0206 int host1x_syncpt_incr(struct host1x_syncpt *sp)
0207 {
0208 return host1x_hw_syncpt_cpu_incr(sp->host, sp);
0209 }
0210 EXPORT_SYMBOL(host1x_syncpt_incr);
0211
0212
0213
0214
0215
0216 static bool syncpt_load_min_is_expired(struct host1x_syncpt *sp, u32 thresh)
0217 {
0218 host1x_hw_syncpt_load(sp->host, sp);
0219
0220 return host1x_syncpt_is_expired(sp, thresh);
0221 }
0222
0223
0224
0225
0226
0227
0228
0229
0230 int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout,
0231 u32 *value)
0232 {
0233 DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
0234 void *ref;
0235 struct host1x_waitlist *waiter;
0236 int err = 0, check_count = 0;
0237
0238 if (value)
0239 *value = host1x_syncpt_load(sp);
0240
0241 if (host1x_syncpt_is_expired(sp, thresh))
0242 return 0;
0243
0244 if (!timeout) {
0245 err = -EAGAIN;
0246 goto done;
0247 }
0248
0249
0250 waiter = kzalloc(sizeof(*waiter), GFP_KERNEL);
0251 if (!waiter) {
0252 err = -ENOMEM;
0253 goto done;
0254 }
0255
0256
0257 err = host1x_intr_add_action(sp->host, sp, thresh,
0258 HOST1X_INTR_ACTION_WAKEUP_INTERRUPTIBLE,
0259 &wq, waiter, &ref);
0260 if (err)
0261 goto done;
0262
0263 err = -EAGAIN;
0264
0265 if (timeout < 0)
0266 timeout = LONG_MAX;
0267
0268
0269 while (timeout) {
0270 long check = min_t(long, SYNCPT_CHECK_PERIOD, timeout);
0271 int remain;
0272
0273 remain = wait_event_interruptible_timeout(wq,
0274 syncpt_load_min_is_expired(sp, thresh),
0275 check);
0276 if (remain > 0 || host1x_syncpt_is_expired(sp, thresh)) {
0277 if (value)
0278 *value = host1x_syncpt_load(sp);
0279
0280 err = 0;
0281
0282 break;
0283 }
0284
0285 if (remain < 0) {
0286 err = remain;
0287 break;
0288 }
0289
0290 timeout -= check;
0291
0292 if (timeout && check_count <= MAX_STUCK_CHECK_COUNT) {
0293 dev_warn(sp->host->dev,
0294 "%s: syncpoint id %u (%s) stuck waiting %d, timeout=%ld\n",
0295 current->comm, sp->id, sp->name,
0296 thresh, timeout);
0297
0298 host1x_debug_dump_syncpts(sp->host);
0299
0300 if (check_count == MAX_STUCK_CHECK_COUNT)
0301 host1x_debug_dump(sp->host);
0302
0303 check_count++;
0304 }
0305 }
0306
0307 host1x_intr_put_ref(sp->host, sp->id, ref, true);
0308
0309 done:
0310 return err;
0311 }
0312 EXPORT_SYMBOL(host1x_syncpt_wait);
0313
0314
0315
0316
0317 bool host1x_syncpt_is_expired(struct host1x_syncpt *sp, u32 thresh)
0318 {
0319 u32 current_val;
0320
0321 smp_rmb();
0322
0323 current_val = (u32)atomic_read(&sp->min_val);
0324
0325 return ((current_val - thresh) & 0x80000000U) == 0U;
0326 }
0327
0328 int host1x_syncpt_init(struct host1x *host)
0329 {
0330 struct host1x_syncpt_base *bases;
0331 struct host1x_syncpt *syncpt;
0332 unsigned int i;
0333
0334 syncpt = devm_kcalloc(host->dev, host->info->nb_pts, sizeof(*syncpt),
0335 GFP_KERNEL);
0336 if (!syncpt)
0337 return -ENOMEM;
0338
0339 bases = devm_kcalloc(host->dev, host->info->nb_bases, sizeof(*bases),
0340 GFP_KERNEL);
0341 if (!bases)
0342 return -ENOMEM;
0343
0344 for (i = 0; i < host->info->nb_pts; i++) {
0345 syncpt[i].id = i;
0346 syncpt[i].host = host;
0347 }
0348
0349 for (i = 0; i < host->info->nb_bases; i++)
0350 bases[i].id = i;
0351
0352 mutex_init(&host->syncpt_mutex);
0353 host->syncpt = syncpt;
0354 host->bases = bases;
0355
0356
0357 host->nop_sp = host1x_syncpt_alloc(host, 0, "reserved-nop");
0358 if (!host->nop_sp)
0359 return -ENOMEM;
0360
0361 if (host->info->reserve_vblank_syncpts) {
0362 kref_init(&host->syncpt[26].ref);
0363 kref_init(&host->syncpt[27].ref);
0364 }
0365
0366 return 0;
0367 }
0368
0369
0370
0371
0372
0373
0374
0375
0376
0377
0378
0379 struct host1x_syncpt *host1x_syncpt_request(struct host1x_client *client,
0380 unsigned long flags)
0381 {
0382 struct host1x *host = dev_get_drvdata(client->host->parent);
0383
0384 return host1x_syncpt_alloc(host, flags, dev_name(client->dev));
0385 }
0386 EXPORT_SYMBOL(host1x_syncpt_request);
0387
0388 static void syncpt_release(struct kref *ref)
0389 {
0390 struct host1x_syncpt *sp = container_of(ref, struct host1x_syncpt, ref);
0391
0392 atomic_set(&sp->max_val, host1x_syncpt_read(sp));
0393
0394 sp->locked = false;
0395
0396 mutex_lock(&sp->host->syncpt_mutex);
0397
0398 host1x_syncpt_base_free(sp->base);
0399 kfree(sp->name);
0400 sp->base = NULL;
0401 sp->name = NULL;
0402 sp->client_managed = false;
0403
0404 mutex_unlock(&sp->host->syncpt_mutex);
0405 }
0406
0407
0408
0409
0410
0411
0412
0413
0414
0415 void host1x_syncpt_put(struct host1x_syncpt *sp)
0416 {
0417 if (!sp)
0418 return;
0419
0420 kref_put(&sp->ref, syncpt_release);
0421 }
0422 EXPORT_SYMBOL(host1x_syncpt_put);
0423
0424 void host1x_syncpt_deinit(struct host1x *host)
0425 {
0426 struct host1x_syncpt *sp = host->syncpt;
0427 unsigned int i;
0428
0429 for (i = 0; i < host->info->nb_pts; i++, sp++)
0430 kfree(sp->name);
0431 }
0432
0433
0434
0435
0436
0437
0438
0439
0440 u32 host1x_syncpt_read_max(struct host1x_syncpt *sp)
0441 {
0442 smp_rmb();
0443
0444 return (u32)atomic_read(&sp->max_val);
0445 }
0446 EXPORT_SYMBOL(host1x_syncpt_read_max);
0447
0448
0449
0450
0451
0452
0453
0454
0455 u32 host1x_syncpt_read_min(struct host1x_syncpt *sp)
0456 {
0457 smp_rmb();
0458
0459 return (u32)atomic_read(&sp->min_val);
0460 }
0461 EXPORT_SYMBOL(host1x_syncpt_read_min);
0462
0463
0464
0465
0466
0467 u32 host1x_syncpt_read(struct host1x_syncpt *sp)
0468 {
0469 return host1x_syncpt_load(sp);
0470 }
0471 EXPORT_SYMBOL(host1x_syncpt_read);
0472
0473 unsigned int host1x_syncpt_nb_pts(struct host1x *host)
0474 {
0475 return host->info->nb_pts;
0476 }
0477
0478 unsigned int host1x_syncpt_nb_bases(struct host1x *host)
0479 {
0480 return host->info->nb_bases;
0481 }
0482
0483 unsigned int host1x_syncpt_nb_mlocks(struct host1x *host)
0484 {
0485 return host->info->nb_mlocks;
0486 }
0487
0488
0489
0490
0491
0492
0493 struct host1x_syncpt *host1x_syncpt_get_by_id(struct host1x *host,
0494 unsigned int id)
0495 {
0496 if (id >= host->info->nb_pts)
0497 return NULL;
0498
0499 if (kref_get_unless_zero(&host->syncpt[id].ref))
0500 return &host->syncpt[id];
0501 else
0502 return NULL;
0503 }
0504 EXPORT_SYMBOL(host1x_syncpt_get_by_id);
0505
0506
0507
0508
0509
0510
0511
0512 struct host1x_syncpt *host1x_syncpt_get_by_id_noref(struct host1x *host,
0513 unsigned int id)
0514 {
0515 if (id >= host->info->nb_pts)
0516 return NULL;
0517
0518 return &host->syncpt[id];
0519 }
0520 EXPORT_SYMBOL(host1x_syncpt_get_by_id_noref);
0521
0522
0523
0524
0525
0526 struct host1x_syncpt *host1x_syncpt_get(struct host1x_syncpt *sp)
0527 {
0528 kref_get(&sp->ref);
0529
0530 return sp;
0531 }
0532 EXPORT_SYMBOL(host1x_syncpt_get);
0533
0534
0535
0536
0537
0538 struct host1x_syncpt_base *host1x_syncpt_get_base(struct host1x_syncpt *sp)
0539 {
0540 return sp ? sp->base : NULL;
0541 }
0542 EXPORT_SYMBOL(host1x_syncpt_get_base);
0543
0544
0545
0546
0547
0548 u32 host1x_syncpt_base_id(struct host1x_syncpt_base *base)
0549 {
0550 return base->id;
0551 }
0552 EXPORT_SYMBOL(host1x_syncpt_base_id);
0553
0554 static void do_nothing(struct kref *ref)
0555 {
0556 }
0557
0558
0559
0560
0561
0562
0563
0564
0565
0566
0567
0568
0569
0570 void host1x_syncpt_release_vblank_reservation(struct host1x_client *client,
0571 u32 syncpt_id)
0572 {
0573 struct host1x *host = dev_get_drvdata(client->host->parent);
0574
0575 if (!host->info->reserve_vblank_syncpts)
0576 return;
0577
0578 kref_put(&host->syncpt[syncpt_id].ref, do_nothing);
0579 }
0580 EXPORT_SYMBOL(host1x_syncpt_release_vblank_reservation);