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

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

more unification of basic types

  • use sysarg_t and native_t (unsigned and signed variant) in both kernel and uspace
  • remove ipcarg_t in favour of sysarg_t

(no change in functionality)

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