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

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

Add additional members to the irq_t structure so that an interrupt-driven driver
does not need to know how to clear the level interrupt. The z8530 was modified
in this way and is much more generic. The ns16550 driver has also been modified,
but awaits testing. The sparc64 interrupt mapping and dispatch code is now using
the new info and calls the clear-interrupt-routine itself.

  • Property mode set to 100644
File size: 10.6 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 signle device
43 *
44 *
45 * Note about architectures.
46 *
47 * Some architectures has the term IRQ well defined. Examples
48 * of such architectures include amd64, ia32 and mips32. Some
49 * other architectures, such as sparc64, don't use the term
50 * at all. In those cases, we boldly step forward and define what
51 * an IRQ is.
52 *
53 * The implementation is generic enough and still allows the
54 * architectures to use the hardware layout effectively.
55 * For instance, on amd64 and ia32, where there is only 16
56 * IRQs, the irq_hash_table can be optimized to a one-dimensional
57 * array. Next, when it is known that the IRQ numbers (aka INR's)
58 * are unique, the claim functions can always return IRQ_ACCEPT.
59 *
60 *
61 * Note about the irq_hash_table.
62 *
63 * The hash table is configured to use two keys: inr and devno.
64 * However, the hash index is computed only from inr. Moreover,
65 * if devno is -1, the match is based on the return value of
66 * the claim() function instead of on devno.
67 */
68
69#include <ddi/irq.h>
70#include <adt/hash_table.h>
71#include <arch/types.h>
72#include <synch/spinlock.h>
73#include <arch.h>
74
75#define KEY_INR 0
76#define KEY_DEVNO 1
77
78/**
79 * Spinlock protecting the hash table.
80 * This lock must be taken only when interrupts are disabled.
81 */
82SPINLOCK_INITIALIZE(irq_hash_table_lock);
83static hash_table_t irq_hash_table;
84
85/**
86 * Hash table operations for cases when we know that
87 * there will be collisions between different keys.
88 */
89static index_t irq_ht_hash(unative_t *key);
90static bool irq_ht_compare(unative_t *key, count_t keys, link_t *item);
91
92static hash_table_operations_t irq_ht_ops = {
93 .hash = irq_ht_hash,
94 .compare = irq_ht_compare,
95 .remove_callback = NULL /* not used */
96};
97
98/**
99 * Hash table operations for cases when we know that
100 * there will be no collisions between different keys.
101 * However, there might be still collisions among
102 * elements with single key (sharing of one IRQ).
103 */
104static index_t irq_lin_hash(unative_t *key);
105static bool irq_lin_compare(unative_t *key, count_t keys, link_t *item);
106
107static hash_table_operations_t irq_lin_ops = {
108 .hash = irq_lin_hash,
109 .compare = irq_lin_compare,
110 .remove_callback = NULL /* not used */
111};
112
113/** Initialize IRQ subsystem.
114 *
115 * @param inrs Numbers of unique IRQ numbers or INRs.
116 * @param chains Number of chains in the hash table.
117 */
118void irq_init(count_t inrs, count_t chains)
119{
120 /*
121 * Be smart about the choice of the hash table operations.
122 * In cases in which inrs equals the requested number of
123 * chains (i.e. where there is no collision between
124 * different keys), we can use optimized set of operations.
125 */
126 if (inrs == chains)
127 hash_table_create(&irq_hash_table, chains, 2, &irq_lin_ops);
128 else
129 hash_table_create(&irq_hash_table, chains, 2, &irq_ht_ops);
130}
131
132/** Initialize one IRQ structure.
133 *
134 * @param irq Pointer to the IRQ structure to be initialized.
135 *
136 */
137void irq_initialize(irq_t *irq)
138{
139 link_initialize(&irq->link);
140 spinlock_initialize(&irq->lock, "irq.lock");
141 irq->preack = false;
142 irq->inr = -1;
143 irq->devno = -1;
144 irq->trigger = (irq_trigger_t) 0;
145 irq->claim = NULL;
146 irq->handler = NULL;
147 irq->arg = NULL;
148 irq->cir = NULL;
149 irq->cir_arg = NULL;
150 irq->notif_cfg.notify = false;
151 irq->notif_cfg.answerbox = NULL;
152 irq->notif_cfg.code = NULL;
153 irq->notif_cfg.method = 0;
154 irq->notif_cfg.counter = 0;
155 link_initialize(&irq->notif_cfg.link);
156}
157
158/** Register IRQ for device.
159 *
160 * The irq structure must be filled with information
161 * about the interrupt source and with the claim()
162 * function pointer and irq_handler() function pointer.
163 *
164 * @param irq IRQ structure belonging to a device.
165 */
166void irq_register(irq_t *irq)
167{
168 ipl_t ipl;
169 unative_t key[] = {
170 (unative_t) irq->inr,
171 (unative_t) irq->devno
172 };
173
174 ipl = interrupts_disable();
175 spinlock_lock(&irq_hash_table_lock);
176 hash_table_insert(&irq_hash_table, key, &irq->link);
177 spinlock_unlock(&irq_hash_table_lock);
178 interrupts_restore(ipl);
179}
180
181/** Dispatch the IRQ.
182 *
183 * We assume this function is only called from interrupt
184 * context (i.e. that interrupts are disabled prior to
185 * this call).
186 *
187 * This function attempts to lookup a fitting IRQ
188 * structure. In case of success, return with interrupts
189 * disabled and holding the respective structure.
190 *
191 * @param inr Interrupt number (aka inr or irq).
192 *
193 * @return IRQ structure of the respective device or NULL.
194 */
195irq_t *irq_dispatch_and_lock(inr_t inr)
196{
197 link_t *lnk;
198 unative_t key[] = {
199 (unative_t) inr,
200 (unative_t) -1 /* search will use claim() instead of devno */
201 };
202
203 spinlock_lock(&irq_hash_table_lock);
204
205 lnk = hash_table_find(&irq_hash_table, key);
206 if (lnk) {
207 irq_t *irq;
208
209 irq = hash_table_get_instance(lnk, irq_t, link);
210
211 spinlock_unlock(&irq_hash_table_lock);
212 return irq;
213 }
214
215 spinlock_unlock(&irq_hash_table_lock);
216
217 return NULL;
218}
219
220/** Find the IRQ structure corresponding to inr and devno.
221 *
222 * This functions attempts to lookup the IRQ structure
223 * corresponding to its arguments. On success, this
224 * function returns with interrups disabled, holding
225 * the lock of the respective IRQ structure.
226 *
227 * This function assumes interrupts are already disabled.
228 *
229 * @param inr INR being looked up.
230 * @param devno Devno being looked up.
231 *
232 * @return Locked IRQ structure on success or NULL on failure.
233 */
234irq_t *irq_find_and_lock(inr_t inr, devno_t devno)
235{
236 link_t *lnk;
237 unative_t keys[] = {
238 (unative_t) inr,
239 (unative_t) devno
240 };
241
242 spinlock_lock(&irq_hash_table_lock);
243
244 lnk = hash_table_find(&irq_hash_table, keys);
245 if (lnk) {
246 irq_t *irq;
247
248 irq = hash_table_get_instance(lnk, irq_t, link);
249
250 spinlock_unlock(&irq_hash_table_lock);
251 return irq;
252 }
253
254 spinlock_unlock(&irq_hash_table_lock);
255
256 return NULL;
257}
258
259/** Compute hash index for the key.
260 *
261 * This function computes hash index into
262 * the IRQ hash table for which there
263 * can be collisions between different
264 * INRs.
265 *
266 * The devno is not used to compute the hash.
267 *
268 * @param key The first of the keys is inr and the second is devno or -1.
269 *
270 * @return Index into the hash table.
271 */
272index_t irq_ht_hash(unative_t key[])
273{
274 inr_t inr = (inr_t) key[KEY_INR];
275 return inr % irq_hash_table.entries;
276}
277
278/** Compare hash table element with a key.
279 *
280 * There are two things to note about this function.
281 * First, it is used for the more complex architecture setup
282 * in which there are way too many interrupt numbers (i.e. inr's)
283 * to arrange the hash table so that collisions occur only
284 * among same inrs of different devnos. So the explicit check
285 * for inr match must be done.
286 * Second, if devno is -1, the second key (i.e. devno) is not
287 * used for the match and the result of the claim() function
288 * is used instead.
289 *
290 * This function assumes interrupts are already disabled.
291 *
292 * @param key Keys (i.e. inr and devno).
293 * @param keys This is 2.
294 * @param item The item to compare the key with.
295 *
296 * @return True on match or false otherwise.
297 */
298bool irq_ht_compare(unative_t key[], count_t keys, link_t *item)
299{
300 irq_t *irq = hash_table_get_instance(item, irq_t, link);
301 inr_t inr = (inr_t) key[KEY_INR];
302 devno_t devno = (devno_t) key[KEY_DEVNO];
303
304 bool rv;
305
306 spinlock_lock(&irq->lock);
307 if (devno == -1) {
308 /* Invoked by irq_dispatch_and_lock(). */
309 rv = ((irq->inr == inr) && (irq->claim() == IRQ_ACCEPT));
310 } else {
311 /* Invoked by irq_find_and_lock(). */
312 rv = ((irq->inr == inr) && (irq->devno == devno));
313 }
314
315 /* unlock only on non-match */
316 if (!rv)
317 spinlock_unlock(&irq->lock);
318
319 return rv;
320}
321
322/** Compute hash index for the key.
323 *
324 * This function computes hash index into
325 * the IRQ hash table for which there
326 * are no collisions between different
327 * INRs.
328 *
329 * @param key The first of the keys is inr and the second is devno or -1.
330 *
331 * @return Index into the hash table.
332 */
333index_t irq_lin_hash(unative_t key[])
334{
335 inr_t inr = (inr_t) key[KEY_INR];
336 return inr;
337}
338
339/** Compare hash table element with a key.
340 *
341 * There are two things to note about this function.
342 * First, it is used for the less complex architecture setup
343 * in which there are not too many interrupt numbers (i.e. inr's)
344 * to arrange the hash table so that collisions occur only
345 * among same inrs of different devnos. So the explicit check
346 * for inr match is not done.
347 * Second, if devno is -1, the second key (i.e. devno) is not
348 * used for the match and the result of the claim() function
349 * is used instead.
350 *
351 * This function assumes interrupts are already disabled.
352 *
353 * @param key Keys (i.e. inr and devno).
354 * @param keys This is 2.
355 * @param item The item to compare the key with.
356 *
357 * @return True on match or false otherwise.
358 */
359bool irq_lin_compare(unative_t key[], count_t keys, link_t *item)
360{
361 irq_t *irq = list_get_instance(item, irq_t, link);
362 devno_t devno = (devno_t) key[KEY_DEVNO];
363 bool rv;
364
365 spinlock_lock(&irq->lock);
366 if (devno == -1) {
367 /* Invoked by irq_dispatch_and_lock() */
368 rv = (irq->claim() == IRQ_ACCEPT);
369 } else {
370 /* Invoked by irq_find_and_lock() */
371 rv = (irq->devno == devno);
372 }
373
374 /* unlock only on non-match */
375 if (!rv)
376 spinlock_unlock(&irq->lock);
377
378 return rv;
379}
380
381/** @}
382 */
Note: See TracBrowser for help on using the repository browser.