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

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

fix compilation in non-SMP configurations

  • Property mode set to 100644
File size: 12.0 KB
RevLine 
[0d107f31]1/*
[df4ed85]2 * Copyright (c) 2006 Jakub Jermar
[0d107f31]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
[7dcf22a]29/** @addtogroup genericddi
[0d107f31]30 * @{
31 */
32/**
33 * @file
[e3890b3f]34 * @brief IRQ dispatcher.
[0d107f31]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
[6cd9aa6]42 * - multiple IRQs per single device
43 * - multiple instances of the same device
[0d107f31]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.
[e3890b3f]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.
[0d107f31]68 */
69
[7dcf22a]70#include <ddi/irq.h>
[0d107f31]71#include <adt/hash_table.h>
[cecb0789]72#include <mm/slab.h>
[0d107f31]73#include <arch/types.h>
74#include <synch/spinlock.h>
[691eb52]75#include <console/console.h>
[cecb0789]76#include <memstr.h>
[0d107f31]77#include <arch.h>
78
[e3890b3f]79#define KEY_INR 0
80#define KEY_DEVNO 1
81
[0d107f31]82/**
[cecb0789]83 * Spinlock protecting the kernel IRQ hash table.
[0d107f31]84 * This lock must be taken only when interrupts are disabled.
85 */
[00eace3]86SPINLOCK_INITIALIZE(irq_kernel_hash_table_lock);
[cecb0789]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;
[0d107f31]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);
[2845930]104static void irq_ht_remove(link_t *item);
[0d107f31]105
106static hash_table_operations_t irq_ht_ops = {
107 .hash = irq_ht_hash,
108 .compare = irq_ht_compare,
[2845930]109 .remove_callback = irq_ht_remove,
[0d107f31]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);
[2845930]120static void irq_lin_remove(link_t *item);
[0d107f31]121
122static hash_table_operations_t irq_lin_ops = {
123 .hash = irq_lin_hash,
124 .compare = irq_lin_compare,
[2845930]125 .remove_callback = irq_lin_remove,
[0d107f31]126};
127
[cecb0789]128/** Number of buckets in either of the hash tables. */
129static count_t buckets;
130
[0d107f31]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{
[cecb0789]138 buckets = chains;
[0d107f31]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 */
[cecb0789]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 }
[0d107f31]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{
[f542825]165 memsetb(irq, sizeof(irq_t), 0);
[0d107f31]166 link_initialize(&irq->link);
[63530c62]167 spinlock_initialize(&irq->lock, "irq.lock");
[cecb0789]168 link_initialize(&irq->notif_cfg.link);
[0d107f31]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()
[cecb0789]177 * function pointer and handler() function pointer.
[0d107f31]178 *
[cecb0789]179 * @param irq IRQ structure belonging to a device.
180 * @return True on success, false on failure.
[0d107f31]181 */
182void irq_register(irq_t *irq)
183{
184 ipl_t ipl;
[e3890b3f]185 unative_t key[] = {
186 (unative_t) irq->inr,
187 (unative_t) irq->devno
188 };
[0d107f31]189
190 ipl = interrupts_disable();
[00eace3]191 spinlock_lock(&irq_kernel_hash_table_lock);
[cecb0789]192 spinlock_lock(&irq->lock);
[00eace3]193 hash_table_insert(&irq_kernel_hash_table, key, &irq->link);
[cecb0789]194 spinlock_unlock(&irq->lock);
[00eace3]195 spinlock_unlock(&irq_kernel_hash_table_lock);
[0d107f31]196 interrupts_restore(ipl);
197}
198
[691eb52]199/** Search and lock the uspace IRQ hash table.
[e3890b3f]200 *
[0d107f31]201 */
[691eb52]202static irq_t *irq_dispatch_and_lock_uspace(inr_t inr)
[0d107f31]203{
204 link_t *lnk;
[e3890b3f]205 unative_t key[] = {
206 (unative_t) inr,
[691eb52]207 (unative_t) -1 /* search will use claim() instead of devno */
[e3890b3f]208 };
[0d107f31]209
[cecb0789]210 spinlock_lock(&irq_uspace_hash_table_lock);
211 lnk = hash_table_find(&irq_uspace_hash_table, key);
[e3890b3f]212 if (lnk) {
213 irq_t *irq;
214
215 irq = hash_table_get_instance(lnk, irq_t, link);
[cecb0789]216 spinlock_unlock(&irq_uspace_hash_table_lock);
[e3890b3f]217 return irq;
218 }
[cecb0789]219 spinlock_unlock(&irq_uspace_hash_table_lock);
[691eb52]220
221 return NULL;
222}
[e3890b3f]223
[691eb52]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
[cecb0789]235 spinlock_lock(&irq_kernel_hash_table_lock);
236 lnk = hash_table_find(&irq_kernel_hash_table, key);
[0d107f31]237 if (lnk) {
238 irq_t *irq;
239
240 irq = hash_table_get_instance(lnk, irq_t, link);
[cecb0789]241 spinlock_unlock(&irq_kernel_hash_table_lock);
[0d107f31]242 return irq;
243 }
[cecb0789]244 spinlock_unlock(&irq_kernel_hash_table_lock);
[691eb52]245
246 return NULL;
247}
[0d107f31]248
[691eb52]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);
[0d107f31]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 *
[e3890b3f]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.
[0d107f31]298 *
299 * @return Index into the hash table.
300 */
[e3890b3f]301index_t irq_ht_hash(unative_t key[])
[0d107f31]302{
[e3890b3f]303 inr_t inr = (inr_t) key[KEY_INR];
[cecb0789]304 return inr % buckets;
[0d107f31]305}
306
307/** Compare hash table element with a key.
308 *
[e3890b3f]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.
[0d107f31]318 *
[e3890b3f]319 * This function assumes interrupts are already disabled.
320 *
321 * @param key Keys (i.e. inr and devno).
322 * @param keys This is 2.
[0d107f31]323 * @param item The item to compare the key with.
324 *
325 * @return True on match or false otherwise.
326 */
[e3890b3f]327bool irq_ht_compare(unative_t key[], count_t keys, link_t *item)
[0d107f31]328{
329 irq_t *irq = hash_table_get_instance(item, irq_t, link);
[e3890b3f]330 inr_t inr = (inr_t) key[KEY_INR];
331 devno_t devno = (devno_t) key[KEY_DEVNO];
332
[63530c62]333 bool rv;
[0d107f31]334
[63530c62]335 spinlock_lock(&irq->lock);
[e3890b3f]336 if (devno == -1) {
[f619ec11]337 /* Invoked by irq_dispatch_and_lock(). */
[6cd9aa6]338 rv = ((irq->inr == inr) &&
[cecb0789]339 (irq->claim(irq) == IRQ_ACCEPT));
[e3890b3f]340 } else {
[f619ec11]341 /* Invoked by irq_find_and_lock(). */
[e3890b3f]342 rv = ((irq->inr == inr) && (irq->devno == devno));
343 }
344
345 /* unlock only on non-match */
346 if (!rv)
347 spinlock_unlock(&irq->lock);
[63530c62]348
349 return rv;
[0d107f31]350}
351
[2845930]352/** Unlock IRQ structure after hash_table_remove().
353 *
[c822026]354 * @param lnk Link in the removed and locked IRQ structure.
[2845930]355 */
356void irq_ht_remove(link_t *lnk)
357{
[c822026]358 irq_t *irq __attribute__((unused))
359 = hash_table_get_instance(lnk, irq_t, link);
[2845930]360 spinlock_unlock(&irq->lock);
361}
362
[0d107f31]363/** Compute hash index for the key.
364 *
365 * This function computes hash index into
366 * the IRQ hash table for which there
367 * are no collisions between different
368 * INRs.
369 *
[e3890b3f]370 * @param key The first of the keys is inr and the second is devno or -1.
[0d107f31]371 *
372 * @return Index into the hash table.
373 */
[e3890b3f]374index_t irq_lin_hash(unative_t key[])
[0d107f31]375{
[e3890b3f]376 inr_t inr = (inr_t) key[KEY_INR];
377 return inr;
[0d107f31]378}
379
380/** Compare hash table element with a key.
381 *
[e3890b3f]382 * There are two things to note about this function.
383 * First, it is used for the less complex architecture setup
384 * in which there are not too many interrupt numbers (i.e. inr's)
385 * to arrange the hash table so that collisions occur only
386 * among same inrs of different devnos. So the explicit check
387 * for inr match is not done.
388 * Second, if devno is -1, the second key (i.e. devno) is not
389 * used for the match and the result of the claim() function
390 * is used instead.
391 *
392 * This function assumes interrupts are already disabled.
[0d107f31]393 *
[e3890b3f]394 * @param key Keys (i.e. inr and devno).
395 * @param keys This is 2.
[0d107f31]396 * @param item The item to compare the key with.
397 *
398 * @return True on match or false otherwise.
399 */
[e3890b3f]400bool irq_lin_compare(unative_t key[], count_t keys, link_t *item)
[0d107f31]401{
402 irq_t *irq = list_get_instance(item, irq_t, link);
[e3890b3f]403 devno_t devno = (devno_t) key[KEY_DEVNO];
[63530c62]404 bool rv;
405
406 spinlock_lock(&irq->lock);
[e3890b3f]407 if (devno == -1) {
[f619ec11]408 /* Invoked by irq_dispatch_and_lock() */
[cecb0789]409 rv = (irq->claim(irq) == IRQ_ACCEPT);
[e3890b3f]410 } else {
[f619ec11]411 /* Invoked by irq_find_and_lock() */
[e3890b3f]412 rv = (irq->devno == devno);
413 }
414
415 /* unlock only on non-match */
416 if (!rv)
417 spinlock_unlock(&irq->lock);
[0d107f31]418
[63530c62]419 return rv;
[0d107f31]420}
421
[2845930]422/** Unlock IRQ structure after hash_table_remove().
423 *
424 * @param lnk Link in the removed and locked IRQ structure.
425 */
426void irq_lin_remove(link_t *lnk)
427{
[c822026]428 irq_t *irq __attribute__((unused))
429 = hash_table_get_instance(lnk, irq_t, link);
[2845930]430 spinlock_unlock(&irq->lock);
431}
432
[0d107f31]433/** @}
434 */
Note: See TracBrowser for help on using the repository browser.