static void debug_not_idle(struct nvhost_module *mod) { int i; bool lock_released = true; struct nvhost_master *dev = container_of(mod, struct nvhost_master, mod); for (i = 0; i < NVHOST_NUMCHANNELS; i++) { struct nvhost_module *m = &dev->channels[i].mod; if (m->name) pr_warn("tegra_grhost: %s: refcnt %d\n", m->name, atomic_read(&m->refcount)); } for (i = 0; i < NV_HOST1X_SYNC_MLOCK_NUM; i++) { int c = atomic_read(&dev->cpuaccess.lock_counts[i]); if (c) { pr_warn("tegra_grhost: lock id %d: refcnt %d\n", i, c); lock_released = false; } } if (lock_released) pr_warn("tegra_grhost: all locks released\n"); nvhost_debug_dump(); }
/** * Write a cpu syncpoint increment to the hardware, without touching * the cache. Caller is responsible for host being powered. */ static void t20_syncpt_cpu_incr(struct nvhost_syncpt *sp, u32 id) { struct nvhost_master *dev = syncpt_to_dev(sp); BUG_ON(!nvhost_module_powered(dev->dev)); if (!client_managed(id) && nvhost_syncpt_min_eq_max(sp, id)) { dev_err(&syncpt_to_dev(sp)->pdev->dev, "Trying to increment syncpoint id %d beyond max\n", id); nvhost_debug_dump(syncpt_to_dev(sp)); return; } writel(BIT(id), dev->sync_aperture + HOST1X_SYNC_SYNCPT_CPU_INCR); wmb(); }
/** * Write a cpu syncpoint increment to the hardware, without touching * the cache. Caller is responsible for host being powered. */ static void t20_syncpt_cpu_incr(struct nvhost_syncpt *sp, u32 id) { struct nvhost_master *dev = syncpt_to_dev(sp); u32 reg_offset = id / 32; if (!nvhost_syncpt_client_managed(sp, id) && nvhost_syncpt_min_eq_max(sp, id)) { dev_err(&syncpt_to_dev(sp)->dev->dev, "Trying to increment syncpoint id %d beyond max\n", id); nvhost_debug_dump(syncpt_to_dev(sp)); return; } writel(bit_mask(id), dev->sync_aperture + host1x_sync_syncpt_cpu_incr_r() + reg_offset * 4); }
void nvhost_module_suspend(struct nvhost_module *mod, bool system_suspend) { int ret; if (system_suspend && (!is_module_idle(mod))) debug_not_idle(mod); ret = wait_event_timeout(mod->idle, is_module_idle(mod), ACM_TIMEOUT + msecs_to_jiffies(500)); if (ret == 0) nvhost_debug_dump(); if (system_suspend) printk("tegra_grhost: entered idle\n"); flush_delayed_work(&mod->powerdown); if (system_suspend) printk("tegra_grhost: flushed delayed work\n"); BUG_ON(mod->powered); }
/** * Updates the last value read from hardware. * (was nvhost_syncpt_update_min) */ static u32 t20_syncpt_update_min(struct nvhost_syncpt *sp, u32 id) { struct nvhost_master *dev = syncpt_to_dev(sp); void __iomem *sync_regs = dev->sync_aperture; u32 old, live; do { old = nvhost_syncpt_read_min(sp, id); live = readl(sync_regs + (HOST1X_SYNC_SYNCPT_0 + id * 4)); } while ((u32)atomic_cmpxchg(&sp->min_val[id], old, live) != old); if (!nvhost_syncpt_check_max(sp, id, live)) { dev_err(&syncpt_to_dev(sp)->pdev->dev, "%s failed: id=%u\n", __func__, id); nvhost_debug_dump(syncpt_to_dev(sp)); BUG(); } return live; }
/** * Main entrypoint for syncpoint value waits. */ int nvhost_syncpt_wait_timeout(struct nvhost_syncpt *sp, u32 id, u32 thresh, u32 timeout, u32 *value) { DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); void *ref; void *waiter; int err = 0, check_count = 0, low_timeout = 0; u32 val; if (value) *value = 0; /* first check cache */ if (nvhost_syncpt_is_expired(sp, id, thresh)) { if (value) *value = nvhost_syncpt_read_min(sp, id); return 0; } /* keep host alive */ nvhost_module_busy(syncpt_to_dev(sp)->dev); /* try to read from register */ val = syncpt_op().update_min(sp, id); if (nvhost_syncpt_is_expired(sp, id, thresh)) { if (value) *value = val; goto done; } if (!timeout) { err = -EAGAIN; goto done; } /* schedule a wakeup when the syncpoint value is reached */ waiter = nvhost_intr_alloc_waiter(); if (!waiter) { err = -ENOMEM; goto done; } err = nvhost_intr_add_action(&(syncpt_to_dev(sp)->intr), id, thresh, NVHOST_INTR_ACTION_WAKEUP_INTERRUPTIBLE, &wq, waiter, &ref); if (err) goto done; err = -EAGAIN; /* Caller-specified timeout may be impractically low */ if (timeout < SYNCPT_CHECK_PERIOD) low_timeout = timeout; /* wait for the syncpoint, or timeout, or signal */ while (timeout) { u32 check = min_t(u32, SYNCPT_CHECK_PERIOD, timeout); int remain = wait_event_interruptible_timeout(wq, syncpt_update_min_is_expired(sp, id, thresh), check); if (remain > 0 || nvhost_syncpt_is_expired(sp, id, thresh)) { if (value) *value = nvhost_syncpt_read_min(sp, id); err = 0; break; } if (remain < 0) { err = remain; break; } if (timeout != NVHOST_NO_TIMEOUT) timeout -= check; if (timeout && check_count <= MAX_STUCK_CHECK_COUNT) { dev_warn(&syncpt_to_dev(sp)->dev->dev, "%s: syncpoint id %d (%s) stuck waiting %d, timeout=%d\n", current->comm, id, syncpt_op().name(sp, id), thresh, timeout); syncpt_op().debug(sp); if (check_count == MAX_STUCK_CHECK_COUNT) { if (low_timeout) { dev_warn(&syncpt_to_dev(sp)->dev->dev, "is timeout %d too low?\n", low_timeout); } nvhost_debug_dump(syncpt_to_dev(sp)); } check_count++; } } nvhost_intr_put_ref(&(syncpt_to_dev(sp)->intr), id, ref); done: nvhost_module_idle(syncpt_to_dev(sp)->dev); return err; }
/** * Main entrypoint for syncpoint value waits. */ int nvhost_syncpt_wait_timeout(struct nvhost_syncpt *sp, u32 id, u32 thresh, u32 timeout) { DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); void *ref; int err = 0; unsigned int debug_done = 0 ; BUG_ON(!check_max(sp, id, thresh)); /* first check cache */ if (nvhost_syncpt_min_cmp(sp, id, thresh)) return 0; /* keep host alive */ nvhost_module_busy(&syncpt_to_dev(sp)->mod); if (client_managed(id) || !nvhost_syncpt_min_eq_max(sp, id)) { /* try to read from register */ u32 val = nvhost_syncpt_update_min(sp, id); if ((s32)(val - thresh) >= 0) goto done; } if (!timeout) { err = -EAGAIN; goto done; } /* schedule a wakeup when the syncpoint value is reached */ err = nvhost_intr_add_action(&(syncpt_to_dev(sp)->intr), id, thresh, NVHOST_INTR_ACTION_WAKEUP_INTERRUPTIBLE, &wq, &ref); if (err) goto done; err = -EAGAIN; /* wait for the syncpoint, or timeout, or signal */ while (timeout) { u32 check = min_t(u32, SYNCPT_CHECK_PERIOD, timeout); int remain = wait_event_interruptible_timeout(wq, nvhost_syncpt_min_cmp(sp, id, thresh), check); if (remain > 0 || nvhost_syncpt_min_cmp(sp, id, thresh)) { err = 0; break; } if (remain < 0) { err = remain; break; } if (timeout != NVHOST_NO_TIMEOUT) timeout -= check; if (timeout) { dev_warn(&syncpt_to_dev(sp)->pdev->dev, "syncpoint id %d (%s) stuck waiting %d\n", id, nvhost_syncpt_name(id), thresh); nvhost_syncpt_debug(sp); if (debug_done > 15) { nvhost_debug_dump(); BUG_ON(1); } debug_done++; } }; nvhost_intr_put_ref(&(syncpt_to_dev(sp)->intr), ref); done: nvhost_module_idle(&syncpt_to_dev(sp)->mod); return err; }
/** * Main entrypoint for syncpoint value waits. */ int nvhost_syncpt_wait_timeout(struct nvhost_syncpt *sp, u32 id, u32 thresh, u32 timeout, u32 *value) { DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); void *ref; void *waiter; int err = 0, check_count = 0, low_timeout = 0; static int print_once = 0; if (value) *value = 0; BUG_ON(!syncpt_op(sp).update_min); if (!nvhost_syncpt_check_max(sp, id, thresh)) { dev_warn(&syncpt_to_dev(sp)->pdev->dev, "wait %d (%s) for (%d) wouldn't be met (max %d)\n", id, syncpt_op(sp).name(sp, id), thresh, nvhost_syncpt_read_max(sp, id)); nvhost_debug_dump(syncpt_to_dev(sp)); return -EINVAL; } /* first check cache */ if (nvhost_syncpt_min_cmp(sp, id, thresh)) { if (value) *value = nvhost_syncpt_read_min(sp, id); return 0; } /* keep host alive */ nvhost_module_busy(syncpt_to_dev(sp)->dev); if (client_managed(id) || !nvhost_syncpt_min_eq_max(sp, id)) { /* try to read from register */ u32 val = syncpt_op(sp).update_min(sp, id); if ((s32)(val - thresh) >= 0) { if (value) *value = val; goto done; } } if (!timeout) { err = -EAGAIN; goto done; } /* schedule a wakeup when the syncpoint value is reached */ waiter = nvhost_intr_alloc_waiter(); if (!waiter) { err = -ENOMEM; goto done; } err = nvhost_intr_add_action(&(syncpt_to_dev(sp)->intr), id, thresh, NVHOST_INTR_ACTION_WAKEUP_INTERRUPTIBLE, &wq, waiter, &ref); if (err) goto done; err = -EAGAIN; /* wait for the syncpoint, or timeout, or signal */ while (timeout) { u32 check = min_t(u32, SYNCPT_CHECK_PERIOD, timeout); int remain = wait_event_interruptible_timeout(wq, nvhost_syncpt_min_cmp(sp, id, thresh), check); if (remain > 0 || nvhost_syncpt_min_cmp(sp, id, thresh)) { if (value) *value = nvhost_syncpt_read_min(sp, id); err = 0; break; } if (remain < 0) { err = remain; break; } if (timeout != NVHOST_NO_TIMEOUT) { if (timeout < SYNCPT_CHECK_PERIOD) { /* Caller-specified timeout may be impractically low */ low_timeout = timeout; } timeout -= check; } if (timeout) { dev_warn(&syncpt_to_dev(sp)->pdev->dev, "%s: syncpoint id %d (%s) stuck waiting %d, timeout=%d\n", current->comm, id, syncpt_op(sp).name(sp, id), thresh, timeout); syncpt_op(sp).debug(sp); print_once++; if (print_once == 1) { nvhost_debug_dump(syncpt_to_dev(sp)); debug_stuck_syncpoint(); } if (check_count > MAX_STUCK_CHECK_COUNT) { if (low_timeout) { dev_warn(&syncpt_to_dev(sp)->pdev->dev, "is timeout %d too low?\n", low_timeout); } nvhost_debug_dump(syncpt_to_dev(sp)); BUG(); } check_count++; } } nvhost_intr_put_ref(&(syncpt_to_dev(sp)->intr), ref); done: nvhost_module_idle(syncpt_to_dev(sp)->dev); return err; }