source: mainline/kernel/generic/src/ddi/irq.c@ 2845930

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 2845930 was 2845930, checked in by Jakub Jermar <jakub@…>, 16 years ago

Address issues with IRQ notifications.

  • Property mode set to 100644
File size: 12.0 KB
Line 
1/*
2 * Copyright (c) 2006 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup genericddi
30 * @{
31 */
32/**
33 * @file
34 * @brief IRQ dispatcher.
35 *
36 * This file provides means of connecting IRQs with particular
37 * devices and logic for dispatching interrupts to IRQ handlers
38 * defined by those devices.
39 *
40 * This code is designed to support:
41 * - multiple devices sharing single IRQ
42 * - multiple IRQs per single device
43 * - multiple instances of the same device
44 *
45 *
46 * Note about architectures.
47 *
48 * Some architectures has the term IRQ well defined. Examples
49 * of such architectures include amd64, ia32 and mips32. Some
50 * other architectures, such as sparc64, don't use the term
51 * at all. In those cases, we boldly step forward and define what
52 * an IRQ is.
53 *
54 * The implementation is generic enough and still allows the
55 * architectures to use the hardware layout effectively.
56 * For instance, on amd64 and ia32, where there is only 16
57 * IRQs, the irq_hash_table can be optimized to a one-dimensional
58 * array. Next, when it is known that the IRQ numbers (aka INR's)
59 * are unique, the claim functions can always return IRQ_ACCEPT.
60 *
61 *
62 * Note about the irq_hash_table.
63 *
64 * The hash table is configured to use two keys: inr and devno.
65 * However, the hash index is computed only from inr. Moreover,
66 * if devno is -1, the match is based on the return value of
67 * the claim() function instead of on devno.
68 */
69
70#include <ddi/irq.h>
71#include <adt/hash_table.h>
72#include <mm/slab.h>
73#include <arch/types.h>
74#include <synch/spinlock.h>
75#include <console/console.h>
76#include <memstr.h>
77#include <arch.h>
78
79#define KEY_INR 0
80#define KEY_DEVNO 1
81
82/**
83 * Spinlock protecting the kernel IRQ hash table.
84 * This lock must be taken only when interrupts are disabled.
85 */
86SPINLOCK_INITIALIZE(irq_kernel_hash_table_lock);
87/** The kernel IRQ hash table. */
88static hash_table_t irq_kernel_hash_table;
89
90/**
91 * Spinlock protecting the uspace IRQ hash table.
92 * This lock must be taken only when interrupts are disabled.
93 */
94SPINLOCK_INITIALIZE(irq_uspace_hash_table_lock);
95/** The uspace IRQ hash table. */
96hash_table_t irq_uspace_hash_table;
97
98/**
99 * Hash table operations for cases when we know that
100 * there will be collisions between different keys.
101 */
102static index_t irq_ht_hash(unative_t *key);
103static bool irq_ht_compare(unative_t *key, count_t keys, link_t *item);
104static void irq_ht_remove(link_t *item);
105
106static hash_table_operations_t irq_ht_ops = {
107 .hash = irq_ht_hash,
108 .compare = irq_ht_compare,
109 .remove_callback = irq_ht_remove,
110};
111
112/**
113 * Hash table operations for cases when we know that
114 * there will be no collisions between different keys.
115 * However, there might be still collisions among
116 * elements with single key (sharing of one IRQ).
117 */
118static index_t irq_lin_hash(unative_t *key);
119static bool irq_lin_compare(unative_t *key, count_t keys, link_t *item);
120static void irq_lin_remove(link_t *item);
121
122static hash_table_operations_t irq_lin_ops = {
123 .hash = irq_lin_hash,
124 .compare = irq_lin_compare,
125 .remove_callback = irq_lin_remove,
126};
127
128/** Number of buckets in either of the hash tables. */
129static count_t buckets;
130
131/** Initialize IRQ subsystem.
132 *
133 * @param inrs Numbers of unique IRQ numbers or INRs.
134 * @param chains Number of chains in the hash table.
135 */
136void irq_init(count_t inrs, count_t chains)
137{
138 buckets = chains;
139 /*
140 * Be smart about the choice of the hash table operations.
141 * In cases in which inrs equals the requested number of
142 * chains (i.e. where there is no collision between
143 * different keys), we can use optimized set of operations.
144 */
145 if (inrs == chains) {
146 hash_table_create(&irq_uspace_hash_table, chains, 2,
147 &irq_lin_ops);
148 hash_table_create(&irq_kernel_hash_table, chains, 2,
149 &irq_lin_ops);
150 } else {
151 hash_table_create(&irq_uspace_hash_table, chains, 2,
152 &irq_ht_ops);
153 hash_table_create(&irq_kernel_hash_table, chains, 2,
154 &irq_ht_ops);
155 }
156}
157
158/** Initialize one IRQ structure.
159 *
160 * @param irq Pointer to the IRQ structure to be initialized.
161 *
162 */
163void irq_initialize(irq_t *irq)
164{
165 memsetb(irq, sizeof(irq_t), 0);
166 link_initialize(&irq->link);
167 spinlock_initialize(&irq->lock, "irq.lock");
168 link_initialize(&irq->notif_cfg.link);
169 irq->inr = -1;
170 irq->devno = -1;
171}
172
173/** Register IRQ for device.
174 *
175 * The irq structure must be filled with information
176 * about the interrupt source and with the claim()
177 * function pointer and handler() function pointer.
178 *
179 * @param irq IRQ structure belonging to a device.
180 * @return True on success, false on failure.
181 */
182void irq_register(irq_t *irq)
183{
184 ipl_t ipl;
185 unative_t key[] = {
186 (unative_t) irq->inr,
187 (unative_t) irq->devno
188 };
189
190 ipl = interrupts_disable();
191 spinlock_lock(&irq_kernel_hash_table_lock);
192 spinlock_lock(&irq->lock);
193 hash_table_insert(&irq_kernel_hash_table, key, &irq->link);
194 spinlock_unlock(&irq->lock);
195 spinlock_unlock(&irq_kernel_hash_table_lock);
196 interrupts_restore(ipl);
197}
198
199/** Search and lock the uspace IRQ hash table.
200 *
201 */
202static irq_t *irq_dispatch_and_lock_uspace(inr_t inr)
203{
204 link_t *lnk;
205 unative_t key[] = {
206 (unative_t) inr,
207 (unative_t) -1 /* search will use claim() instead of devno */
208 };
209
210 spinlock_lock(&irq_uspace_hash_table_lock);
211 lnk = hash_table_find(&irq_uspace_hash_table, key);
212 if (lnk) {
213 irq_t *irq;
214
215 irq = hash_table_get_instance(lnk, irq_t, link);
216 spinlock_unlock(&irq_uspace_hash_table_lock);
217 return irq;
218 }
219 spinlock_unlock(&irq_uspace_hash_table_lock);
220
221 return NULL;
222}
223
224/** Search and lock the kernel IRQ hash table.
225 *
226 */
227static irq_t *irq_dispatch_and_lock_kernel(inr_t inr)
228{
229 link_t *lnk;
230 unative_t key[] = {
231 (unative_t) inr,
232 (unative_t) -1 /* search will use claim() instead of devno */
233 };
234
235 spinlock_lock(&irq_kernel_hash_table_lock);
236 lnk = hash_table_find(&irq_kernel_hash_table, key);
237 if (lnk) {
238 irq_t *irq;
239
240 irq = hash_table_get_instance(lnk, irq_t, link);
241 spinlock_unlock(&irq_kernel_hash_table_lock);
242 return irq;
243 }
244 spinlock_unlock(&irq_kernel_hash_table_lock);
245
246 return NULL;
247}
248
249/** Dispatch the IRQ.
250 *
251 * We assume this function is only called from interrupt
252 * context (i.e. that interrupts are disabled prior to
253 * this call).
254 *
255 * This function attempts to lookup a fitting IRQ
256 * structure. In case of success, return with interrupts
257 * disabled and holding the respective structure.
258 *
259 * @param inr Interrupt number (aka inr or irq).
260 *
261 * @return IRQ structure of the respective device or NULL.
262 */
263irq_t *irq_dispatch_and_lock(inr_t inr)
264{
265 irq_t *irq;
266
267 /*
268 * If the kernel console is silenced,
269 * then try first the uspace handlers,
270 * eventually fall back to kernel handlers.
271 *
272 * If the kernel console is active,
273 * then do it the other way around.
274 */
275 if (silent) {
276 irq = irq_dispatch_and_lock_uspace(inr);
277 if (irq)
278 return irq;
279 return irq_dispatch_and_lock_kernel(inr);
280 }
281
282 irq = irq_dispatch_and_lock_kernel(inr);
283 if (irq)
284 return irq;
285 return irq_dispatch_and_lock_uspace(inr);
286}
287
288/** Compute hash index for the key.
289 *
290 * This function computes hash index into
291 * the IRQ hash table for which there
292 * can be collisions between different
293 * INRs.
294 *
295 * The devno is not used to compute the hash.
296 *
297 * @param key The first of the keys is inr and the second is devno or -1.
298 *
299 * @return Index into the hash table.
300 */
301index_t irq_ht_hash(unative_t key[])
302{
303 inr_t inr = (inr_t) key[KEY_INR];
304 return inr % buckets;
305}
306
307/** Compare hash table element with a key.
308 *
309 * There are two things to note about this function.
310 * First, it is used for the more complex architecture setup
311 * in which there are way too many interrupt numbers (i.e. inr's)
312 * to arrange the hash table so that collisions occur only
313 * among same inrs of different devnos. So the explicit check
314 * for inr match must be done.
315 * Second, if devno is -1, the second key (i.e. devno) is not
316 * used for the match and the result of the claim() function
317 * is used instead.
318 *
319 * This function assumes interrupts are already disabled.
320 *
321 * @param key Keys (i.e. inr and devno).
322 * @param keys This is 2.
323 * @param item The item to compare the key with.
324 *
325 * @return True on match or false otherwise.
326 */
327bool irq_ht_compare(unative_t key[], count_t keys, link_t *item)
328{
329 irq_t *irq = hash_table_get_instance(item, irq_t, link);
330 inr_t inr = (inr_t) key[KEY_INR];
331 devno_t devno = (devno_t) key[KEY_DEVNO];
332
333 bool rv;
334
335 spinlock_lock(&irq->lock);
336 if (devno == -1) {
337 /* Invoked by irq_dispatch_and_lock(). */
338 rv = ((irq->inr == inr) &&
339 (irq->claim(irq) == IRQ_ACCEPT));
340 } else {
341 /* Invoked by irq_find_and_lock(). */
342 rv = ((irq->inr == inr) && (irq->devno == devno));
343 }
344
345 /* unlock only on non-match */
346 if (!rv)
347 spinlock_unlock(&irq->lock);
348
349 return rv;
350}
351
352/** Unlock IRQ structure after hash_table_remove().
353 *
354 * @param lnk Link in the removed and locked IRQ structure.
355 */
356void irq_ht_remove(link_t *lnk)
357{
358 irq_t *irq = hash_table_get_instance(lnk, irq_t, link);
359 spinlock_unlock(&irq->lock);
360}
361
362/** Compute hash index for the key.
363 *
364 * This function computes hash index into
365 * the IRQ hash table for which there
366 * are no collisions between different
367 * INRs.
368 *
369 * @param key The first of the keys is inr and the second is devno or -1.
370 *
371 * @return Index into the hash table.
372 */
373index_t irq_lin_hash(unative_t key[])
374{
375 inr_t inr = (inr_t) key[KEY_INR];
376 return inr;
377}
378
379/** Compare hash table element with a key.
380 *
381 * There are two things to note about this function.
382 * First, it is used for the less complex architecture setup
383 * in which there are not too many interrupt numbers (i.e. inr's)
384 * to arrange the hash table so that collisions occur only
385 * among same inrs of different devnos. So the explicit check
386 * for inr match is not done.
387 * Second, if devno is -1, the second key (i.e. devno) is not
388 * used for the match and the result of the claim() function
389 * is used instead.
390 *
391 * This function assumes interrupts are already disabled.
392 *
393 * @param key Keys (i.e. inr and devno).
394 * @param keys This is 2.
395 * @param item The item to compare the key with.
396 *
397 * @return True on match or false otherwise.
398 */
399bool irq_lin_compare(unative_t key[], count_t keys, link_t *item)
400{
401 irq_t *irq = list_get_instance(item, irq_t, link);
402 devno_t devno = (devno_t) key[KEY_DEVNO];
403 bool rv;
404
405 spinlock_lock(&irq->lock);
406 if (devno == -1) {
407 /* Invoked by irq_dispatch_and_lock() */
408 rv = (irq->claim(irq) == IRQ_ACCEPT);
409 } else {
410 /* Invoked by irq_find_and_lock() */
411 rv = (irq->devno == devno);
412 }
413
414 /* unlock only on non-match */
415 if (!rv)
416 spinlock_unlock(&irq->lock);
417
418 return rv;
419}
420
421/** Unlock IRQ structure after hash_table_remove().
422 *
423 * @param lnk Link in the removed and locked IRQ structure.
424 */
425void irq_lin_remove(link_t *lnk)
426{
427 irq_t *irq = hash_table_get_instance(lnk, irq_t, link);
428 spinlock_unlock(&irq->lock);
429}
430
431/** @}
432 */
Note: See TracBrowser for help on using the repository browser.