2 * Tegra host1x Interrupt Management
4 * Copyright (c) 2010-2013, NVIDIA Corporation.
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include <linux/clk.h>
20 #include <linux/interrupt.h>
21 #include <linux/slab.h>
22 #include <linux/irq.h>
24 #include <trace/events/host1x.h>
29 /* Wait list management */
38 static void waiter_release(struct kref
*kref
)
40 kfree(container_of(kref
, struct host1x_waitlist
, refcount
));
44 * add a waiter to a waiter queue, sorted by threshold
45 * returns true if it was added at the head of the queue
47 static bool add_waiter_to_queue(struct host1x_waitlist
*waiter
,
48 struct list_head
*queue
)
50 struct host1x_waitlist
*pos
;
51 u32 thresh
= waiter
->thresh
;
53 list_for_each_entry_reverse(pos
, queue
, list
)
54 if ((s32
)(pos
->thresh
- thresh
) <= 0) {
55 list_add(&waiter
->list
, &pos
->list
);
59 list_add(&waiter
->list
, queue
);
64 * run through a waiter queue for a single sync point ID
65 * and gather all completed waiters into lists by actions
67 static void remove_completed_waiters(struct list_head
*head
, u32 sync
,
68 struct list_head completed
[HOST1X_INTR_ACTION_COUNT
])
70 struct list_head
*dest
;
71 struct host1x_waitlist
*waiter
, *next
, *prev
;
73 list_for_each_entry_safe(waiter
, next
, head
, list
) {
74 if ((s32
)(waiter
->thresh
- sync
) > 0)
77 dest
= completed
+ waiter
->action
;
79 /* consolidate submit cleanups */
80 if (waiter
->action
== HOST1X_INTR_ACTION_SUBMIT_COMPLETE
&&
82 prev
= list_entry(dest
->prev
,
83 struct host1x_waitlist
, list
);
84 if (prev
->data
== waiter
->data
) {
90 /* PENDING->REMOVED or CANCELLED->HANDLED */
91 if (atomic_inc_return(&waiter
->state
) == WLS_HANDLED
|| !dest
) {
92 list_del(&waiter
->list
);
93 kref_put(&waiter
->refcount
, waiter_release
);
95 list_move_tail(&waiter
->list
, dest
);
99 static void reset_threshold_interrupt(struct host1x
*host
,
100 struct list_head
*head
,
104 list_first_entry(head
, struct host1x_waitlist
, list
)->thresh
;
106 host1x_hw_intr_set_syncpt_threshold(host
, id
, thresh
);
107 host1x_hw_intr_enable_syncpt_intr(host
, id
);
110 static void action_submit_complete(struct host1x_waitlist
*waiter
)
112 struct host1x_channel
*channel
= waiter
->data
;
114 host1x_cdma_update(&channel
->cdma
);
116 /* Add nr_completed to trace */
117 trace_host1x_channel_submit_complete(dev_name(channel
->dev
),
118 waiter
->count
, waiter
->thresh
);
122 static void action_wakeup(struct host1x_waitlist
*waiter
)
124 wait_queue_head_t
*wq
= waiter
->data
;
129 static void action_wakeup_interruptible(struct host1x_waitlist
*waiter
)
131 wait_queue_head_t
*wq
= waiter
->data
;
133 wake_up_interruptible(wq
);
136 typedef void (*action_handler
)(struct host1x_waitlist
*waiter
);
138 static const action_handler action_handlers
[HOST1X_INTR_ACTION_COUNT
] = {
139 action_submit_complete
,
141 action_wakeup_interruptible
,
144 static void run_handlers(struct list_head completed
[HOST1X_INTR_ACTION_COUNT
])
146 struct list_head
*head
= completed
;
149 for (i
= 0; i
< HOST1X_INTR_ACTION_COUNT
; ++i
, ++head
) {
150 action_handler handler
= action_handlers
[i
];
151 struct host1x_waitlist
*waiter
, *next
;
153 list_for_each_entry_safe(waiter
, next
, head
, list
) {
154 list_del(&waiter
->list
);
156 WARN_ON(atomic_xchg(&waiter
->state
, WLS_HANDLED
) !=
158 kref_put(&waiter
->refcount
, waiter_release
);
164 * Remove & handle all waiters that have completed for the given syncpt
166 static int process_wait_list(struct host1x
*host
,
167 struct host1x_syncpt
*syncpt
,
170 struct list_head completed
[HOST1X_INTR_ACTION_COUNT
];
174 for (i
= 0; i
< HOST1X_INTR_ACTION_COUNT
; ++i
)
175 INIT_LIST_HEAD(completed
+ i
);
177 spin_lock(&syncpt
->intr
.lock
);
179 remove_completed_waiters(&syncpt
->intr
.wait_head
, threshold
,
182 empty
= list_empty(&syncpt
->intr
.wait_head
);
184 host1x_hw_intr_disable_syncpt_intr(host
, syncpt
->id
);
186 reset_threshold_interrupt(host
, &syncpt
->intr
.wait_head
,
189 spin_unlock(&syncpt
->intr
.lock
);
191 run_handlers(completed
);
197 * Sync point threshold interrupt service thread function
198 * Handles sync point threshold triggers, in thread context
201 static void syncpt_thresh_work(struct work_struct
*work
)
203 struct host1x_syncpt_intr
*syncpt_intr
=
204 container_of(work
, struct host1x_syncpt_intr
, work
);
205 struct host1x_syncpt
*syncpt
=
206 container_of(syncpt_intr
, struct host1x_syncpt
, intr
);
207 unsigned int id
= syncpt
->id
;
208 struct host1x
*host
= syncpt
->host
;
210 (void)process_wait_list(host
, syncpt
,
211 host1x_syncpt_load(host
->syncpt
+ id
));
214 int host1x_intr_add_action(struct host1x
*host
, unsigned int id
, u32 thresh
,
215 enum host1x_intr_action action
, void *data
,
216 struct host1x_waitlist
*waiter
, void **ref
)
218 struct host1x_syncpt
*syncpt
;
221 if (waiter
== NULL
) {
222 pr_warn("%s: NULL waiter\n", __func__
);
226 /* initialize a new waiter */
227 INIT_LIST_HEAD(&waiter
->list
);
228 kref_init(&waiter
->refcount
);
230 kref_get(&waiter
->refcount
);
231 waiter
->thresh
= thresh
;
232 waiter
->action
= action
;
233 atomic_set(&waiter
->state
, WLS_PENDING
);
237 syncpt
= host
->syncpt
+ id
;
239 spin_lock(&syncpt
->intr
.lock
);
241 queue_was_empty
= list_empty(&syncpt
->intr
.wait_head
);
243 if (add_waiter_to_queue(waiter
, &syncpt
->intr
.wait_head
)) {
244 /* added at head of list - new threshold value */
245 host1x_hw_intr_set_syncpt_threshold(host
, id
, thresh
);
247 /* added as first waiter - enable interrupt */
249 host1x_hw_intr_enable_syncpt_intr(host
, id
);
252 spin_unlock(&syncpt
->intr
.lock
);
259 void host1x_intr_put_ref(struct host1x
*host
, unsigned int id
, void *ref
)
261 struct host1x_waitlist
*waiter
= ref
;
262 struct host1x_syncpt
*syncpt
;
264 while (atomic_cmpxchg(&waiter
->state
, WLS_PENDING
, WLS_CANCELLED
) ==
268 syncpt
= host
->syncpt
+ id
;
269 (void)process_wait_list(host
, syncpt
,
270 host1x_syncpt_load(host
->syncpt
+ id
));
272 kref_put(&waiter
->refcount
, waiter_release
);
275 int host1x_intr_init(struct host1x
*host
, unsigned int irq_sync
)
278 u32 nb_pts
= host1x_syncpt_nb_pts(host
);
280 mutex_init(&host
->intr_mutex
);
281 host
->intr_syncpt_irq
= irq_sync
;
283 for (id
= 0; id
< nb_pts
; ++id
) {
284 struct host1x_syncpt
*syncpt
= host
->syncpt
+ id
;
286 spin_lock_init(&syncpt
->intr
.lock
);
287 INIT_LIST_HEAD(&syncpt
->intr
.wait_head
);
288 snprintf(syncpt
->intr
.thresh_irq_name
,
289 sizeof(syncpt
->intr
.thresh_irq_name
),
290 "host1x_sp_%02u", id
);
293 host1x_intr_start(host
);
298 void host1x_intr_deinit(struct host1x
*host
)
300 host1x_intr_stop(host
);
303 void host1x_intr_start(struct host1x
*host
)
305 u32 hz
= clk_get_rate(host
->clk
);
308 mutex_lock(&host
->intr_mutex
);
309 err
= host1x_hw_intr_init_host_sync(host
, DIV_ROUND_UP(hz
, 1000000),
312 mutex_unlock(&host
->intr_mutex
);
315 mutex_unlock(&host
->intr_mutex
);
318 void host1x_intr_stop(struct host1x
*host
)
321 struct host1x_syncpt
*syncpt
= host
->syncpt
;
322 u32 nb_pts
= host1x_syncpt_nb_pts(host
);
324 mutex_lock(&host
->intr_mutex
);
326 host1x_hw_intr_disable_all_syncpt_intrs(host
);
328 for (id
= 0; id
< nb_pts
; ++id
) {
329 struct host1x_waitlist
*waiter
, *next
;
331 list_for_each_entry_safe(waiter
, next
,
332 &syncpt
[id
].intr
.wait_head
, list
) {
333 if (atomic_cmpxchg(&waiter
->state
,
334 WLS_CANCELLED
, WLS_HANDLED
) == WLS_CANCELLED
) {
335 list_del(&waiter
->list
);
336 kref_put(&waiter
->refcount
, waiter_release
);
340 if (!list_empty(&syncpt
[id
].intr
.wait_head
)) {
341 /* output diagnostics */
342 mutex_unlock(&host
->intr_mutex
);
343 pr_warn("%s cannot stop syncpt intr id=%u\n",
349 host1x_hw_intr_free_syncpt_irq(host
);
351 mutex_unlock(&host
->intr_mutex
);