2 * drivers/video/tegra/host/nvhost_intr.c
4 * Tegra Graphics Host Interrupt Management
6 * Copyright (c) 2010, NVIDIA Corporation.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #include "nvhost_intr.h"
25 #include <linux/interrupt.h>
26 #include <linux/slab.h>
27 #include <linux/irq.h>
29 #define intr_to_dev(x) container_of(x, struct nvhost_master, intr)
32 /*** HW sync point threshold interrupt management ***/
34 static void set_syncpt_threshold(void __iomem *sync_regs, u32 id, u32 thresh)
37 writel(thresh, sync_regs + (HOST1X_SYNC_SYNCPT_INT_THRESH_0 + id * 4));
40 static void enable_syncpt_interrupt(void __iomem *sync_regs, u32 id)
42 writel(BIT(id), sync_regs + HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0);
46 /*** Wait list management ***/
48 struct nvhost_waitlist {
49 struct list_head list;
52 enum nvhost_intr_action action;
66 static void waiter_release(struct kref *kref)
68 kfree(container_of(kref, struct nvhost_waitlist, refcount));
72 * add a waiter to a waiter queue, sorted by threshold
73 * returns true if it was added at the head of the queue
75 static bool add_waiter_to_queue(struct nvhost_waitlist *waiter,
76 struct list_head *queue)
78 struct nvhost_waitlist *pos;
79 u32 thresh = waiter->thresh;
81 list_for_each_entry_reverse(pos, queue, list)
82 if ((s32)(pos->thresh - thresh) <= 0) {
83 list_add(&waiter->list, &pos->list);
87 list_add(&waiter->list, queue);
92 * run through a waiter queue for a single sync point ID
93 * and gather all completed waiters into lists by actions
95 static void remove_completed_waiters(struct list_head *head, u32 sync,
96 struct list_head completed[NVHOST_INTR_ACTION_COUNT])
98 struct list_head *dest;
99 struct nvhost_waitlist *waiter, *next, *prev;
101 list_for_each_entry_safe(waiter, next, head, list) {
102 if ((s32)(waiter->thresh - sync) > 0)
105 dest = completed + waiter->action;
107 /* consolidate submit cleanups */
108 if (waiter->action == NVHOST_INTR_ACTION_SUBMIT_COMPLETE
109 && !list_empty(dest)) {
110 prev = list_entry(dest->prev,
111 struct nvhost_waitlist, list);
112 if (prev->data == waiter->data) {
118 /* PENDING->REMOVED or CANCELLED->HANDLED */
119 if (atomic_inc_return(&waiter->state) == WLS_HANDLED || !dest) {
120 list_del(&waiter->list);
121 kref_put(&waiter->refcount, waiter_release);
123 list_move_tail(&waiter->list, dest);
128 static void action_submit_complete(struct nvhost_waitlist *waiter)
130 struct nvhost_channel *channel = waiter->data;
131 int nr_completed = waiter->count;
133 nvhost_cdma_update(&channel->cdma);
134 nvhost_module_idle_mult(&channel->mod, nr_completed);
137 static void action_ctxsave(struct nvhost_waitlist *waiter)
139 struct nvhost_hwctx *hwctx = waiter->data;
140 struct nvhost_channel *channel = hwctx->channel;
142 channel->ctxhandler.save_service(hwctx);
143 channel->ctxhandler.put(hwctx);
146 static void action_wakeup(struct nvhost_waitlist *waiter)
148 wait_queue_head_t *wq = waiter->data;
153 static void action_wakeup_interruptible(struct nvhost_waitlist *waiter)
155 wait_queue_head_t *wq = waiter->data;
157 wake_up_interruptible(wq);
160 typedef void (*action_handler)(struct nvhost_waitlist *waiter);
162 static action_handler action_handlers[NVHOST_INTR_ACTION_COUNT] = {
163 action_submit_complete,
166 action_wakeup_interruptible,
169 static void run_handlers(struct list_head completed[NVHOST_INTR_ACTION_COUNT])
171 struct list_head *head = completed;
174 for (i = 0; i < NVHOST_INTR_ACTION_COUNT; ++i, ++head) {
175 action_handler handler = action_handlers[i];
176 struct nvhost_waitlist *waiter, *next;
178 list_for_each_entry_safe(waiter, next, head, list) {
179 list_del(&waiter->list);
181 WARN_ON(atomic_xchg(&waiter->state, WLS_HANDLED) != WLS_REMOVED);
182 kref_put(&waiter->refcount, waiter_release);
188 /*** Interrupt service functions ***/
191 * Host1x intterrupt service function
192 * Handles read / write failures
194 static irqreturn_t host1x_isr(int irq, void *dev_id)
196 struct nvhost_intr *intr = dev_id;
197 void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture;
202 stat = readl(sync_regs + HOST1X_SYNC_HINTSTATUS);
203 ext_stat = readl(sync_regs + HOST1X_SYNC_HINTSTATUS_EXT);
205 if (nvhost_sync_hintstatus_ext_ip_read_int(ext_stat)) {
206 addr = readl(sync_regs + HOST1X_SYNC_IP_READ_TIMEOUT_ADDR);
207 pr_err("Host read timeout at address %x\n", addr);
210 if (nvhost_sync_hintstatus_ext_ip_write_int(ext_stat)) {
211 addr = readl(sync_regs + HOST1X_SYNC_IP_WRITE_TIMEOUT_ADDR);
212 pr_err("Host write timeout at address %x\n", addr);
215 writel(ext_stat, sync_regs + HOST1X_SYNC_HINTSTATUS_EXT);
216 writel(stat, sync_regs + HOST1X_SYNC_HINTSTATUS);
222 * Sync point threshold interrupt service function
223 * Handles sync point threshold triggers, in interrupt context
225 static irqreturn_t syncpt_thresh_isr(int irq, void *dev_id)
227 struct nvhost_intr_syncpt *syncpt = dev_id;
228 unsigned int id = syncpt->id;
229 struct nvhost_intr *intr = container_of(syncpt, struct nvhost_intr,
231 void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture;
234 sync_regs + HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE);
236 sync_regs + HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS);
238 return IRQ_WAKE_THREAD;
243 * Sync point threshold interrupt service thread function
244 * Handles sync point threshold triggers, in thread context
246 static irqreturn_t syncpt_thresh_fn(int irq, void *dev_id)
248 struct nvhost_intr_syncpt *syncpt = dev_id;
249 unsigned int id = syncpt->id;
250 struct nvhost_intr *intr = container_of(syncpt, struct nvhost_intr,
252 struct nvhost_master *dev = intr_to_dev(intr);
253 void __iomem *sync_regs = dev->sync_aperture;
255 struct list_head completed[NVHOST_INTR_ACTION_COUNT];
259 for (i = 0; i < NVHOST_INTR_ACTION_COUNT; ++i)
260 INIT_LIST_HEAD(completed + i);
262 sync = nvhost_syncpt_update_min(&dev->syncpt, id);
264 spin_lock(&syncpt->lock);
266 remove_completed_waiters(&syncpt->wait_head, sync, completed);
268 if (!list_empty(&syncpt->wait_head)) {
269 u32 thresh = list_first_entry(&syncpt->wait_head,
270 struct nvhost_waitlist, list)->thresh;
272 set_syncpt_threshold(sync_regs, id, thresh);
273 enable_syncpt_interrupt(sync_regs, id);
276 spin_unlock(&syncpt->lock);
278 run_handlers(completed);
284 * lazily request a syncpt's irq
286 static int request_syncpt_irq(struct nvhost_intr_syncpt *syncpt)
288 static DEFINE_MUTEX(mutex);
292 if (!syncpt->irq_requested) {
293 err = request_threaded_irq(syncpt->irq,
294 syncpt_thresh_isr, syncpt_thresh_fn,
295 0, syncpt->thresh_irq_name, syncpt);
297 syncpt->irq_requested = 1;
299 mutex_unlock(&mutex);
306 int nvhost_intr_add_action(struct nvhost_intr *intr, u32 id, u32 thresh,
307 enum nvhost_intr_action action, void *data,
310 struct nvhost_waitlist *waiter;
311 struct nvhost_intr_syncpt *syncpt;
312 void __iomem *sync_regs;
316 /* create and initialize a new waiter */
317 waiter = kmalloc(sizeof(*waiter), GFP_KERNEL);
320 INIT_LIST_HEAD(&waiter->list);
321 kref_init(&waiter->refcount);
323 kref_get(&waiter->refcount);
324 waiter->thresh = thresh;
325 waiter->action = action;
326 atomic_set(&waiter->state, WLS_PENDING);
330 BUG_ON(id >= NV_HOST1X_SYNCPT_NB_PTS);
331 syncpt = intr->syncpt + id;
332 sync_regs = intr_to_dev(intr)->sync_aperture;
334 spin_lock(&syncpt->lock);
336 /* lazily request irq for this sync point */
337 if (!syncpt->irq_requested) {
338 spin_unlock(&syncpt->lock);
340 err = request_syncpt_irq(syncpt);
346 spin_lock(&syncpt->lock);
349 queue_was_empty = list_empty(&syncpt->wait_head);
351 if (add_waiter_to_queue(waiter, &syncpt->wait_head)) {
352 /* added at head of list - new threshold value */
353 set_syncpt_threshold(sync_regs, id, thresh);
355 /* added as first waiter - enable interrupt */
357 enable_syncpt_interrupt(sync_regs, id);
360 spin_unlock(&syncpt->lock);
367 void nvhost_intr_put_ref(struct nvhost_intr *intr, void *ref)
369 struct nvhost_waitlist *waiter = ref;
371 while (atomic_cmpxchg(&waiter->state,
372 WLS_PENDING, WLS_CANCELLED) == WLS_REMOVED)
375 kref_put(&waiter->refcount, waiter_release);
379 /*** Init & shutdown ***/
381 int nvhost_intr_init(struct nvhost_intr *intr, u32 irq_gen, u32 irq_sync)
384 struct nvhost_intr_syncpt *syncpt;
387 err = request_irq(irq_gen, host1x_isr, 0, "host_status", intr);
390 intr->host1x_irq = irq_gen;
391 intr->host1x_isr_started = true;
393 for (id = 0, syncpt = intr->syncpt;
394 id < NV_HOST1X_SYNCPT_NB_PTS;
397 syncpt->irq = irq_sync + id;
398 syncpt->irq_requested = 0;
399 spin_lock_init(&syncpt->lock);
400 INIT_LIST_HEAD(&syncpt->wait_head);
401 snprintf(syncpt->thresh_irq_name,
402 sizeof(syncpt->thresh_irq_name),
403 "%s", nvhost_syncpt_name(id));
409 nvhost_intr_deinit(intr);
413 void nvhost_intr_deinit(struct nvhost_intr *intr)
416 struct nvhost_intr_syncpt *syncpt;
418 for (id = 0, syncpt = intr->syncpt;
419 id < NV_HOST1X_SYNCPT_NB_PTS;
421 struct nvhost_waitlist *waiter, *next;
422 list_for_each_entry_safe(waiter, next, &syncpt->wait_head, list) {
423 if (atomic_cmpxchg(&waiter->state, WLS_CANCELLED, WLS_HANDLED)
425 list_del(&waiter->list);
426 kref_put(&waiter->refcount, waiter_release);
430 if(!list_empty(&syncpt->wait_head)) { // output diagnostics
431 printk("%s id=%d\n",__func__,id);
435 if (syncpt->irq_requested)
436 free_irq(syncpt->irq, syncpt);
439 if (intr->host1x_isr_started) {
440 free_irq(intr->host1x_irq, intr);
441 intr->host1x_isr_started = false;
445 void nvhost_intr_configure (struct nvhost_intr *intr, u32 hz)
447 void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture;
449 // write microsecond clock register
450 writel((hz + 1000000 - 1)/1000000, sync_regs + HOST1X_SYNC_USEC_CLK);
452 /* disable the ip_busy_timeout. this prevents write drops, etc.
453 * there's no real way to recover from a hung client anyway.
455 writel(0, sync_regs + HOST1X_SYNC_IP_BUSY_TIMEOUT);
457 /* increase the auto-ack timout to the maximum value. 2d will hang
460 writel(0xff, sync_regs + HOST1X_SYNC_CTXSW_TIMEOUT_CFG);
462 /* disable interrupts for both cpu's */
463 writel(0, sync_regs + HOST1X_SYNC_SYNCPT_THRESH_INT_MASK_0);
464 writel(0, sync_regs + HOST1X_SYNC_SYNCPT_THRESH_INT_MASK_1);
466 /* masking all of the interrupts actually means "enable" */
467 writel(BIT(0), sync_regs + HOST1X_SYNC_INTMASK);
469 /* enable HOST_INT_C0MASK */
470 writel(BIT(0), sync_regs + HOST1X_SYNC_INTC0MASK);
472 /* enable HINTMASK_EXT */
473 writel(BIT(31), sync_regs + HOST1X_SYNC_HINTMASK);
475 /* enable IP_READ_INT and IP_WRITE_INT */
476 writel(BIT(30) | BIT(31), sync_regs + HOST1X_SYNC_HINTMASK_EXT);