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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3a2f8aa was 3a2f8aa, checked in by Jiri Svoboda <jiri@…>, 15 years ago

ADB driver for userspace, ADB keyboard support.

  • Property mode set to 100644
File size: 12.1 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>
[3a2f8aa]76#include <interrupt.h>
[cecb0789]77#include <memstr.h>
[0d107f31]78#include <arch.h>
79
[e3890b3f]80#define KEY_INR 0
81#define KEY_DEVNO 1
82
[0d107f31]83/**
[cecb0789]84 * Spinlock protecting the kernel IRQ hash table.
[0d107f31]85 * This lock must be taken only when interrupts are disabled.
86 */
[00eace3]87SPINLOCK_INITIALIZE(irq_kernel_hash_table_lock);
[cecb0789]88/** The kernel IRQ hash table. */
89static hash_table_t irq_kernel_hash_table;
90
91/**
92 * Spinlock protecting the uspace IRQ hash table.
93 * This lock must be taken only when interrupts are disabled.
94 */
95SPINLOCK_INITIALIZE(irq_uspace_hash_table_lock);
96/** The uspace IRQ hash table. */
97hash_table_t irq_uspace_hash_table;
[0d107f31]98
99/**
100 * Hash table operations for cases when we know that
101 * there will be collisions between different keys.
102 */
[98000fb]103static size_t irq_ht_hash(unative_t *key);
104static bool irq_ht_compare(unative_t *key, size_t keys, link_t *item);
[2845930]105static void irq_ht_remove(link_t *item);
[0d107f31]106
107static hash_table_operations_t irq_ht_ops = {
108 .hash = irq_ht_hash,
109 .compare = irq_ht_compare,
[2845930]110 .remove_callback = irq_ht_remove,
[0d107f31]111};
112
113/**
114 * Hash table operations for cases when we know that
115 * there will be no collisions between different keys.
116 * However, there might be still collisions among
117 * elements with single key (sharing of one IRQ).
118 */
[98000fb]119static size_t irq_lin_hash(unative_t *key);
120static bool irq_lin_compare(unative_t *key, size_t keys, link_t *item);
[2845930]121static void irq_lin_remove(link_t *item);
[0d107f31]122
123static hash_table_operations_t irq_lin_ops = {
124 .hash = irq_lin_hash,
125 .compare = irq_lin_compare,
[2845930]126 .remove_callback = irq_lin_remove,
[0d107f31]127};
128
[cecb0789]129/** Number of buckets in either of the hash tables. */
[98000fb]130static size_t buckets;
[cecb0789]131
[0d107f31]132/** Initialize IRQ subsystem.
133 *
134 * @param inrs Numbers of unique IRQ numbers or INRs.
135 * @param chains Number of chains in the hash table.
136 */
[98000fb]137void irq_init(size_t inrs, size_t chains)
[0d107f31]138{
[cecb0789]139 buckets = chains;
[0d107f31]140 /*
141 * Be smart about the choice of the hash table operations.
142 * In cases in which inrs equals the requested number of
143 * chains (i.e. where there is no collision between
144 * different keys), we can use optimized set of operations.
145 */
[cecb0789]146 if (inrs == chains) {
147 hash_table_create(&irq_uspace_hash_table, chains, 2,
148 &irq_lin_ops);
149 hash_table_create(&irq_kernel_hash_table, chains, 2,
150 &irq_lin_ops);
151 } else {
152 hash_table_create(&irq_uspace_hash_table, chains, 2,
153 &irq_ht_ops);
154 hash_table_create(&irq_kernel_hash_table, chains, 2,
155 &irq_ht_ops);
156 }
[0d107f31]157}
158
159/** Initialize one IRQ structure.
160 *
161 * @param irq Pointer to the IRQ structure to be initialized.
162 *
163 */
164void irq_initialize(irq_t *irq)
165{
[f542825]166 memsetb(irq, sizeof(irq_t), 0);
[0d107f31]167 link_initialize(&irq->link);
[63530c62]168 spinlock_initialize(&irq->lock, "irq.lock");
[cecb0789]169 link_initialize(&irq->notif_cfg.link);
[0d107f31]170 irq->inr = -1;
171 irq->devno = -1;
[3a2f8aa]172
173 irq_initialize_arch(irq);
[0d107f31]174}
175
176/** Register IRQ for device.
177 *
178 * The irq structure must be filled with information
179 * about the interrupt source and with the claim()
[cecb0789]180 * function pointer and handler() function pointer.
[0d107f31]181 *
[cecb0789]182 * @param irq IRQ structure belonging to a device.
183 * @return True on success, false on failure.
[0d107f31]184 */
185void irq_register(irq_t *irq)
186{
187 ipl_t ipl;
[e3890b3f]188 unative_t key[] = {
189 (unative_t) irq->inr,
190 (unative_t) irq->devno
191 };
[0d107f31]192
193 ipl = interrupts_disable();
[00eace3]194 spinlock_lock(&irq_kernel_hash_table_lock);
[cecb0789]195 spinlock_lock(&irq->lock);
[00eace3]196 hash_table_insert(&irq_kernel_hash_table, key, &irq->link);
[cecb0789]197 spinlock_unlock(&irq->lock);
[00eace3]198 spinlock_unlock(&irq_kernel_hash_table_lock);
[0d107f31]199 interrupts_restore(ipl);
200}
201
[691eb52]202/** Search and lock the uspace IRQ hash table.
[e3890b3f]203 *
[0d107f31]204 */
[691eb52]205static irq_t *irq_dispatch_and_lock_uspace(inr_t inr)
[0d107f31]206{
207 link_t *lnk;
[e3890b3f]208 unative_t key[] = {
209 (unative_t) inr,
[691eb52]210 (unative_t) -1 /* search will use claim() instead of devno */
[e3890b3f]211 };
[0d107f31]212
[cecb0789]213 spinlock_lock(&irq_uspace_hash_table_lock);
214 lnk = hash_table_find(&irq_uspace_hash_table, key);
[e3890b3f]215 if (lnk) {
216 irq_t *irq;
217
218 irq = hash_table_get_instance(lnk, irq_t, link);
[cecb0789]219 spinlock_unlock(&irq_uspace_hash_table_lock);
[e3890b3f]220 return irq;
221 }
[cecb0789]222 spinlock_unlock(&irq_uspace_hash_table_lock);
[691eb52]223
224 return NULL;
225}
[e3890b3f]226
[691eb52]227/** Search and lock the kernel IRQ hash table.
228 *
229 */
230static irq_t *irq_dispatch_and_lock_kernel(inr_t inr)
231{
232 link_t *lnk;
233 unative_t key[] = {
234 (unative_t) inr,
235 (unative_t) -1 /* search will use claim() instead of devno */
236 };
237
[cecb0789]238 spinlock_lock(&irq_kernel_hash_table_lock);
239 lnk = hash_table_find(&irq_kernel_hash_table, key);
[0d107f31]240 if (lnk) {
241 irq_t *irq;
242
243 irq = hash_table_get_instance(lnk, irq_t, link);
[cecb0789]244 spinlock_unlock(&irq_kernel_hash_table_lock);
[0d107f31]245 return irq;
246 }
[cecb0789]247 spinlock_unlock(&irq_kernel_hash_table_lock);
[691eb52]248
249 return NULL;
250}
[0d107f31]251
[691eb52]252/** Dispatch the IRQ.
253 *
254 * We assume this function is only called from interrupt
255 * context (i.e. that interrupts are disabled prior to
256 * this call).
257 *
258 * This function attempts to lookup a fitting IRQ
259 * structure. In case of success, return with interrupts
260 * disabled and holding the respective structure.
261 *
262 * @param inr Interrupt number (aka inr or irq).
263 *
264 * @return IRQ structure of the respective device or NULL.
265 */
266irq_t *irq_dispatch_and_lock(inr_t inr)
267{
268 irq_t *irq;
269
270 /*
271 * If the kernel console is silenced,
272 * then try first the uspace handlers,
273 * eventually fall back to kernel handlers.
274 *
275 * If the kernel console is active,
276 * then do it the other way around.
277 */
278 if (silent) {
279 irq = irq_dispatch_and_lock_uspace(inr);
280 if (irq)
281 return irq;
282 return irq_dispatch_and_lock_kernel(inr);
283 }
284
285 irq = irq_dispatch_and_lock_kernel(inr);
286 if (irq)
287 return irq;
288 return irq_dispatch_and_lock_uspace(inr);
[0d107f31]289}
290
291/** Compute hash index for the key.
292 *
293 * This function computes hash index into
294 * the IRQ hash table for which there
295 * can be collisions between different
296 * INRs.
297 *
[e3890b3f]298 * The devno is not used to compute the hash.
299 *
300 * @param key The first of the keys is inr and the second is devno or -1.
[0d107f31]301 *
302 * @return Index into the hash table.
303 */
[98000fb]304size_t irq_ht_hash(unative_t key[])
[0d107f31]305{
[e3890b3f]306 inr_t inr = (inr_t) key[KEY_INR];
[cecb0789]307 return inr % buckets;
[0d107f31]308}
309
310/** Compare hash table element with a key.
311 *
[e3890b3f]312 * There are two things to note about this function.
313 * First, it is used for the more complex architecture setup
314 * in which there are way too many interrupt numbers (i.e. inr's)
315 * to arrange the hash table so that collisions occur only
316 * among same inrs of different devnos. So the explicit check
317 * for inr match must be done.
318 * Second, if devno is -1, the second key (i.e. devno) is not
319 * used for the match and the result of the claim() function
320 * is used instead.
[0d107f31]321 *
[e3890b3f]322 * This function assumes interrupts are already disabled.
323 *
324 * @param key Keys (i.e. inr and devno).
325 * @param keys This is 2.
[0d107f31]326 * @param item The item to compare the key with.
327 *
328 * @return True on match or false otherwise.
329 */
[98000fb]330bool irq_ht_compare(unative_t key[], size_t keys, link_t *item)
[0d107f31]331{
332 irq_t *irq = hash_table_get_instance(item, irq_t, link);
[e3890b3f]333 inr_t inr = (inr_t) key[KEY_INR];
334 devno_t devno = (devno_t) key[KEY_DEVNO];
335
[63530c62]336 bool rv;
[0d107f31]337
[63530c62]338 spinlock_lock(&irq->lock);
[e3890b3f]339 if (devno == -1) {
[f619ec11]340 /* Invoked by irq_dispatch_and_lock(). */
[6cd9aa6]341 rv = ((irq->inr == inr) &&
[cecb0789]342 (irq->claim(irq) == IRQ_ACCEPT));
[e3890b3f]343 } else {
[f619ec11]344 /* Invoked by irq_find_and_lock(). */
[e3890b3f]345 rv = ((irq->inr == inr) && (irq->devno == devno));
346 }
347
348 /* unlock only on non-match */
349 if (!rv)
350 spinlock_unlock(&irq->lock);
[63530c62]351
352 return rv;
[0d107f31]353}
354
[2845930]355/** Unlock IRQ structure after hash_table_remove().
356 *
[c822026]357 * @param lnk Link in the removed and locked IRQ structure.
[2845930]358 */
359void irq_ht_remove(link_t *lnk)
360{
[c822026]361 irq_t *irq __attribute__((unused))
362 = hash_table_get_instance(lnk, irq_t, link);
[2845930]363 spinlock_unlock(&irq->lock);
364}
365
[0d107f31]366/** Compute hash index for the key.
367 *
368 * This function computes hash index into
369 * the IRQ hash table for which there
370 * are no collisions between different
371 * INRs.
372 *
[e3890b3f]373 * @param key The first of the keys is inr and the second is devno or -1.
[0d107f31]374 *
375 * @return Index into the hash table.
376 */
[98000fb]377size_t irq_lin_hash(unative_t key[])
[0d107f31]378{
[e3890b3f]379 inr_t inr = (inr_t) key[KEY_INR];
380 return inr;
[0d107f31]381}
382
383/** Compare hash table element with a key.
384 *
[e3890b3f]385 * There are two things to note about this function.
386 * First, it is used for the less complex architecture setup
387 * in which there are not too many interrupt numbers (i.e. inr's)
388 * to arrange the hash table so that collisions occur only
389 * among same inrs of different devnos. So the explicit check
390 * for inr match is not done.
391 * Second, if devno is -1, the second key (i.e. devno) is not
392 * used for the match and the result of the claim() function
393 * is used instead.
394 *
395 * This function assumes interrupts are already disabled.
[0d107f31]396 *
[e3890b3f]397 * @param key Keys (i.e. inr and devno).
398 * @param keys This is 2.
[0d107f31]399 * @param item The item to compare the key with.
400 *
401 * @return True on match or false otherwise.
402 */
[98000fb]403bool irq_lin_compare(unative_t key[], size_t keys, link_t *item)
[0d107f31]404{
405 irq_t *irq = list_get_instance(item, irq_t, link);
[e3890b3f]406 devno_t devno = (devno_t) key[KEY_DEVNO];
[63530c62]407 bool rv;
408
409 spinlock_lock(&irq->lock);
[e3890b3f]410 if (devno == -1) {
[f619ec11]411 /* Invoked by irq_dispatch_and_lock() */
[cecb0789]412 rv = (irq->claim(irq) == IRQ_ACCEPT);
[e3890b3f]413 } else {
[f619ec11]414 /* Invoked by irq_find_and_lock() */
[e3890b3f]415 rv = (irq->devno == devno);
416 }
417
418 /* unlock only on non-match */
419 if (!rv)
420 spinlock_unlock(&irq->lock);
[0d107f31]421
[63530c62]422 return rv;
[0d107f31]423}
424
[2845930]425/** Unlock IRQ structure after hash_table_remove().
426 *
427 * @param lnk Link in the removed and locked IRQ structure.
428 */
429void irq_lin_remove(link_t *lnk)
430{
[c822026]431 irq_t *irq __attribute__((unused))
432 = hash_table_get_instance(lnk, irq_t, link);
[2845930]433 spinlock_unlock(&irq->lock);
434}
435
[0d107f31]436/** @}
437 */
Note: See TracBrowser for help on using the repository browser.