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

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

Create a slab cache for allocating irq_t structures

  • Property mode set to 100644
File size: 7.8 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
[4d76cfc]34 * @brief IRQ dispatcher
[0d107f31]35 *
[4d76cfc]36 * This file provides means of connecting IRQs with respective device drivers
37 * and logic for dispatching interrupts to IRQ handlers defined by those
38 * drivers.
[0d107f31]39 */
40
[7dcf22a]41#include <ddi/irq.h>
[0d107f31]42#include <adt/hash_table.h>
[cecb0789]43#include <mm/slab.h>
[d99c1d2]44#include <typedefs.h>
[0d107f31]45#include <synch/spinlock.h>
[691eb52]46#include <console/console.h>
[3a2f8aa]47#include <interrupt.h>
[44a7ee5]48#include <mem.h>
[0d107f31]49#include <arch.h>
50
[431c402]51slab_cache_t *irq_slab = NULL;
52
[4d76cfc]53/** Spinlock protecting the kernel IRQ hash table
[da1bafb]54 *
[0d107f31]55 * This lock must be taken only when interrupts are disabled.
[da1bafb]56 *
[0d107f31]57 */
[da1bafb]58IRQ_SPINLOCK_STATIC_INITIALIZE(irq_kernel_hash_table_lock);
59
[cecb0789]60/** The kernel IRQ hash table. */
61static hash_table_t irq_kernel_hash_table;
62
[4d76cfc]63/** Spinlock protecting the uspace IRQ hash table
[da1bafb]64 *
[cecb0789]65 * This lock must be taken only when interrupts are disabled.
[da1bafb]66 *
[cecb0789]67 */
[da1bafb]68IRQ_SPINLOCK_INITIALIZE(irq_uspace_hash_table_lock);
69
[4d76cfc]70/** The uspace IRQ hash table */
[cecb0789]71hash_table_t irq_uspace_hash_table;
[0d107f31]72
[96b02eb9]73static size_t irq_ht_hash(sysarg_t *key);
74static bool irq_ht_compare(sysarg_t *key, size_t keys, link_t *item);
[2845930]75static void irq_ht_remove(link_t *item);
[0d107f31]76
77static hash_table_operations_t irq_ht_ops = {
78 .hash = irq_ht_hash,
79 .compare = irq_ht_compare,
[2845930]80 .remove_callback = irq_ht_remove,
[0d107f31]81};
82
[4d76cfc]83/** Number of buckets in either of the hash tables */
[98000fb]84static size_t buckets;
[cecb0789]85
[4d76cfc]86/** Last valid INR */
[78ffb70]87inr_t last_inr = 0;
88
[4d76cfc]89/** Initialize IRQ subsystem
[0d107f31]90 *
[4d76cfc]91 * @param inrs Numbers of unique IRQ numbers or INRs.
92 * @param chains Number of buckets in the hash table.
[da1bafb]93 *
[0d107f31]94 */
[98000fb]95void irq_init(size_t inrs, size_t chains)
[0d107f31]96{
[cecb0789]97 buckets = chains;
[78ffb70]98 last_inr = inrs - 1;
99
[431c402]100 irq_slab = slab_cache_create("irq_t", sizeof(irq_t), 0, NULL, NULL,
101 FRAME_ATOMIC);
102 assert(irq_slab);
103
[4d76cfc]104 hash_table_create(&irq_uspace_hash_table, chains, 2, &irq_ht_ops);
105 hash_table_create(&irq_kernel_hash_table, chains, 2, &irq_ht_ops);
[0d107f31]106}
107
[4d76cfc]108/** Initialize one IRQ structure
[0d107f31]109 *
[4d76cfc]110 * @param irq Pointer to the IRQ structure to be initialized.
[0d107f31]111 *
112 */
113void irq_initialize(irq_t *irq)
114{
[f542825]115 memsetb(irq, sizeof(irq_t), 0);
[0d107f31]116 link_initialize(&irq->link);
[da1bafb]117 irq_spinlock_initialize(&irq->lock, "irq.lock");
[0d107f31]118 irq->inr = -1;
[da1bafb]119
[3a2f8aa]120 irq_initialize_arch(irq);
[0d107f31]121}
122
[4d76cfc]123/** Register IRQ for device
[0d107f31]124 *
[ee50130]125 * The irq structure must be filled with information about the interrupt source
126 * and with the claim() function pointer and handler() function pointer.
[0d107f31]127 *
[4d76cfc]128 * @param irq IRQ structure belonging to a device.
[da1bafb]129 *
[0d107f31]130 */
131void irq_register(irq_t *irq)
132{
[96b02eb9]133 sysarg_t key[] = {
[24abb85d]134 [IRQ_HT_KEY_INR] = (sysarg_t) irq->inr,
135 [IRQ_HT_KEY_MODE] = (sysarg_t) IRQ_HT_MODE_NO_CLAIM
[e3890b3f]136 };
[0d107f31]137
[da1bafb]138 irq_spinlock_lock(&irq_kernel_hash_table_lock, true);
139 irq_spinlock_lock(&irq->lock, false);
[00eace3]140 hash_table_insert(&irq_kernel_hash_table, key, &irq->link);
[da1bafb]141 irq_spinlock_unlock(&irq->lock, false);
142 irq_spinlock_unlock(&irq_kernel_hash_table_lock, true);
[0d107f31]143}
144
[4d76cfc]145/** Search and lock the uspace IRQ hash table */
[691eb52]146static irq_t *irq_dispatch_and_lock_uspace(inr_t inr)
[0d107f31]147{
148 link_t *lnk;
[96b02eb9]149 sysarg_t key[] = {
[24abb85d]150 [IRQ_HT_KEY_INR] = (sysarg_t) inr,
151 [IRQ_HT_KEY_MODE] = (sysarg_t) IRQ_HT_MODE_CLAIM
[e3890b3f]152 };
[0d107f31]153
[da1bafb]154 irq_spinlock_lock(&irq_uspace_hash_table_lock, false);
[cecb0789]155 lnk = hash_table_find(&irq_uspace_hash_table, key);
[e3890b3f]156 if (lnk) {
[da1bafb]157 irq_t *irq = hash_table_get_instance(lnk, irq_t, link);
158 irq_spinlock_unlock(&irq_uspace_hash_table_lock, false);
[e3890b3f]159 return irq;
160 }
[da1bafb]161 irq_spinlock_unlock(&irq_uspace_hash_table_lock, false);
[691eb52]162
163 return NULL;
164}
[e3890b3f]165
[4d76cfc]166/** Search and lock the kernel IRQ hash table */
[691eb52]167static irq_t *irq_dispatch_and_lock_kernel(inr_t inr)
168{
169 link_t *lnk;
[96b02eb9]170 sysarg_t key[] = {
[24abb85d]171 [IRQ_HT_KEY_INR] = (sysarg_t) inr,
172 [IRQ_HT_KEY_MODE] = (sysarg_t) IRQ_HT_MODE_CLAIM
[691eb52]173 };
174
[da1bafb]175 irq_spinlock_lock(&irq_kernel_hash_table_lock, false);
[cecb0789]176 lnk = hash_table_find(&irq_kernel_hash_table, key);
[0d107f31]177 if (lnk) {
[da1bafb]178 irq_t *irq = hash_table_get_instance(lnk, irq_t, link);
179 irq_spinlock_unlock(&irq_kernel_hash_table_lock, false);
[0d107f31]180 return irq;
181 }
[da1bafb]182 irq_spinlock_unlock(&irq_kernel_hash_table_lock, false);
[691eb52]183
184 return NULL;
185}
[0d107f31]186
[4d76cfc]187/** Dispatch the IRQ
[691eb52]188 *
[ee50130]189 * We assume this function is only called from interrupt context (i.e. that
190 * interrupts are disabled prior to this call).
[691eb52]191 *
[ee50130]192 * This function attempts to lookup a fitting IRQ structure. In case of success,
193 * return with interrupts disabled and holding the respective structure.
[691eb52]194 *
[4d76cfc]195 * @param inr Interrupt number (aka inr or irq).
[691eb52]196 *
[ee50130]197 * @return IRQ structure of the respective device
198 * @return NULL if no IRQ structure found
[da1bafb]199 *
[691eb52]200 */
201irq_t *irq_dispatch_and_lock(inr_t inr)
202{
203 /*
[ee50130]204 * If the kernel console override is on, then try first the kernel
205 * handlers and eventually fall back to uspace handlers.
[691eb52]206 *
[ee50130]207 * In the usual case the uspace handlers have precedence.
[691eb52]208 */
[b366a6f4]209
210 if (console_override) {
211 irq_t *irq = irq_dispatch_and_lock_kernel(inr);
[691eb52]212 if (irq)
213 return irq;
[da1bafb]214
[b366a6f4]215 return irq_dispatch_and_lock_uspace(inr);
[691eb52]216 }
217
[b366a6f4]218 irq_t *irq = irq_dispatch_and_lock_uspace(inr);
[691eb52]219 if (irq)
220 return irq;
[da1bafb]221
[b366a6f4]222 return irq_dispatch_and_lock_kernel(inr);
[0d107f31]223}
224
[4d76cfc]225/** Compute hash index for the key
[0d107f31]226 *
[4d76cfc]227 * @param key The first of the keys is inr and the second is mode. Only inr is
228 * used to compute the hash.
[0d107f31]229 *
230 * @return Index into the hash table.
[da1bafb]231 *
[0d107f31]232 */
[96b02eb9]233size_t irq_ht_hash(sysarg_t key[])
[0d107f31]234{
[24abb85d]235 inr_t inr = (inr_t) key[IRQ_HT_KEY_INR];
[cecb0789]236 return inr % buckets;
[0d107f31]237}
238
[4d76cfc]239/** Compare hash table element with a key
[0d107f31]240 *
[4d76cfc]241 * If mode is IRQ_HT_MODE_CLAIM, the result of the claim() function is used for
242 * the match. Otherwise the key does not match.
[0d107f31]243 *
[e3890b3f]244 * This function assumes interrupts are already disabled.
245 *
[4d76cfc]246 * @param key Keys (i.e. inr and mode).
247 * @param keys This is 2.
248 * @param item The item to compare the key with.
[0d107f31]249 *
[4d76cfc]250 * @return True on match
251 * @return False on no match
[da1bafb]252 *
[0d107f31]253 */
[96b02eb9]254bool irq_ht_compare(sysarg_t key[], size_t keys, link_t *item)
[0d107f31]255{
256 irq_t *irq = hash_table_get_instance(item, irq_t, link);
[24abb85d]257 inr_t inr = (inr_t) key[IRQ_HT_KEY_INR];
258 irq_ht_mode_t mode = (irq_ht_mode_t) key[IRQ_HT_KEY_MODE];
[da1bafb]259
[63530c62]260 bool rv;
[0d107f31]261
[da1bafb]262 irq_spinlock_lock(&irq->lock, false);
[24abb85d]263 if (mode == IRQ_HT_MODE_CLAIM) {
[f619ec11]264 /* Invoked by irq_dispatch_and_lock(). */
[24abb85d]265 rv = ((irq->inr == inr) && (irq->claim(irq) == IRQ_ACCEPT));
[e3890b3f]266 } else {
[f619ec11]267 /* Invoked by irq_find_and_lock(). */
[24abb85d]268 rv = false;
[e3890b3f]269 }
270
271 /* unlock only on non-match */
272 if (!rv)
[da1bafb]273 irq_spinlock_unlock(&irq->lock, false);
274
[63530c62]275 return rv;
[0d107f31]276}
277
[4d76cfc]278/** Unlock IRQ structure after hash_table_remove()
[2845930]279 *
[4d76cfc]280 * @param lnk Link in the removed and locked IRQ structure.
[2845930]281 */
282void irq_ht_remove(link_t *lnk)
283{
[c822026]284 irq_t *irq __attribute__((unused))
285 = hash_table_get_instance(lnk, irq_t, link);
[da1bafb]286 irq_spinlock_unlock(&irq->lock, false);
[2845930]287}
288
[0d107f31]289/** @}
290 */
Note: See TracBrowser for help on using the repository browser.