Subversion Repositories HelenOS

Rev

Rev 1920 | Rev 1922 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1919 jermar 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
 
1920 jermar 29
/** @addtogroup genericddi
1919 jermar 30
 * @{
31
 */
32
/**
33
 * @file
34
 * @brief   IRQ redirector.
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
 
1920 jermar 61
#include <ddi/irq.h>
1919 jermar 62
#include <adt/hash_table.h>
63
#include <arch/types.h>
64
#include <typedefs.h>
65
#include <synch/spinlock.h>
1921 jermar 66
#include <atomic.h>
1919 jermar 67
#include <arch.h>
68
 
69
/**
70
 * Spinlock protecting the hash table.
71
 * This lock must be taken only when interrupts are disabled.
72
 */
73
SPINLOCK_INITIALIZE(irq_hash_table_lock);
74
static hash_table_t irq_hash_table;
75
 
76
/**
77
 * Hash table operations for cases when we know that
78
 * there will be collisions between different keys.
79
 */
80
static index_t irq_ht_hash(unative_t *key);
81
static bool irq_ht_compare(unative_t *key, count_t keys, link_t *item);
82
 
83
static hash_table_operations_t irq_ht_ops = {
84
    .hash = irq_ht_hash,
85
    .compare = irq_ht_compare,
86
    .remove_callback = NULL     /* not used */
87
};
88
 
89
/**
90
 * Hash table operations for cases when we know that
91
 * there will be no collisions between different keys.
92
 * However, there might be still collisions among
93
 * elements with single key (sharing of one IRQ).
94
 */
95
static index_t irq_lin_hash(unative_t *key);
96
static bool irq_lin_compare(unative_t *key, count_t keys, link_t *item);
97
 
98
static hash_table_operations_t irq_lin_ops = {
99
    .hash = irq_lin_hash,
100
    .compare = irq_lin_compare,
101
    .remove_callback = NULL     /* not used */
102
};
103
 
104
/** Initialize IRQ subsystem.
105
 *
106
 * @param inrs Numbers of unique IRQ numbers or INRs.
107
 * @param chains Number of chains in the hash table.
108
 */
109
void irq_init(count_t inrs, count_t chains)
110
{
111
    /*
112
     * Be smart about the choice of the hash table operations.
113
     * In cases in which inrs equals the requested number of
114
     * chains (i.e. where there is no collision between
115
     * different keys), we can use optimized set of operations.
116
     */
117
    if (inrs == chains)
118
        hash_table_create(&irq_hash_table, chains, 1, &irq_lin_ops);
119
    else
120
        hash_table_create(&irq_hash_table, chains, 1, &irq_ht_ops);
121
}
122
 
123
/** Initialize one IRQ structure.
124
 *
125
 * @param irq Pointer to the IRQ structure to be initialized.
126
 *
127
 */
128
void irq_initialize(irq_t *irq)
129
{
130
    link_initialize(&irq->link);
1921 jermar 131
    spinlock_initialize(&irq->lock, "irq.lock");
1919 jermar 132
    irq->inr = -1;
133
    irq->devno = -1;
134
    irq->trigger = 0;
135
    irq->claim = NULL;
136
    irq->handler = NULL;
137
    irq->arg = NULL;
1921 jermar 138
    irq->notif_answerbox = NULL;
139
    irq->code = NULL;
140
    atomic_set(&irq->counter, 0);
1919 jermar 141
}
142
 
143
/** Register IRQ for device.
144
 *
145
 * The irq structure must be filled with information
146
 * about the interrupt source and with the claim()
147
 * function pointer and irq_handler() function pointer.
148
 *
149
 * @param irq IRQ structure belonging to a device.
150
 */
151
void irq_register(irq_t *irq)
152
{
153
    ipl_t ipl;
154
 
155
    ipl = interrupts_disable();
156
    spinlock_lock(&irq_hash_table_lock);
157
    hash_table_insert(&irq_hash_table, (void *) &irq->inr, &irq->link);
158
    spinlock_unlock(&irq_hash_table_lock);
159
    interrupts_restore(ipl);
160
}
161
 
162
/** Dispatch the IRQ.
163
 *
164
 * @param inr Interrupt number (aka inr or irq).
165
 *
166
 * @return IRQ structure of the respective device or NULL.
167
 */
168
irq_t *irq_dispatch(inr_t inr)
169
{
170
    ipl_t ipl;
171
    link_t *lnk;
172
 
173
    ipl = interrupts_disable();
174
    spinlock_lock(&irq_hash_table_lock);
175
 
176
    lnk = hash_table_find(&irq_hash_table, (void *) &inr);
177
    if (lnk) {
178
        irq_t *irq;
179
 
180
        irq = hash_table_get_instance(lnk, irq_t, link);
181
 
182
        spinlock_unlock(&irq_hash_table_lock);
183
        interrupts_restore(ipl);
184
        return irq;
185
    }
186
 
187
    spinlock_unlock(&irq_hash_table_lock);
188
    interrupts_restore(ipl);
189
 
190
    return NULL;   
191
}
192
 
193
/** Compute hash index for the key.
194
 *
195
 * This function computes hash index into
196
 * the IRQ hash table for which there
197
 * can be collisions between different
198
 * INRs.
199
 *
200
 * @param key Pointer to INR.
201
 *
202
 * @return Index into the hash table.
203
 */
204
index_t irq_ht_hash(unative_t *key)
205
{
206
    inr_t *inr = (inr_t *) key;
207
    return *inr % irq_hash_table.entries;
208
}
209
 
210
/** Compare hash table element with a key.
211
 *
212
 * As usually, we do sort of a hack here.
213
 * Even when the key matches the inr member,
214
 * we ask the device to either accept
215
 * or decline to service the interrupt.
216
 *
217
 * @param key Pointer to key (i.e. inr).
218
 * @param keys This is 1.
219
 * @param item The item to compare the key with.
220
 *
221
 * @return True on match or false otherwise.
222
 */
223
bool irq_ht_compare(unative_t *key, count_t keys, link_t *item)
224
{
225
    irq_t *irq = hash_table_get_instance(item, irq_t, link);
226
    inr_t *inr = (inr_t *) key;
1921 jermar 227
    bool rv;
1919 jermar 228
 
1921 jermar 229
    spinlock_lock(&irq->lock);
230
    rv = ((irq->inr == *inr) && (irq->claim() == IRQ_ACCEPT));
231
    spinlock_unlock(&irq->lock);
232
 
233
    return rv;
1919 jermar 234
}
235
 
236
/** Compute hash index for the key.
237
 *
238
 * This function computes hash index into
239
 * the IRQ hash table for which there
240
 * are no collisions between different
241
 * INRs.
242
 *
243
 * @param key INR.
244
 *
245
 * @return Index into the hash table.
246
 */
247
index_t irq_lin_hash(unative_t *key)
248
{
249
    inr_t *inr = (inr_t *) key;
250
    return *inr;
251
}
252
 
253
/** Compare hash table element with a key.
254
 *
255
 * As usually, we do sort of a hack here.
256
 * We don't compare the inr member with
257
 * the key because we know that there are
258
 * no collision between different keys.
259
 * We only ask the device to either accept
260
 * or decline to service the interrupt.
261
 *
262
 * @param key Pointer to key (i.e. inr).
263
 * @param keys This is 1.
264
 * @param item The item to compare the key with.
265
 *
266
 * @return True on match or false otherwise.
267
 */
268
bool irq_lin_compare(unative_t *key, count_t keys, link_t *item)
269
{
270
    irq_t *irq = list_get_instance(item, irq_t, link);
1921 jermar 271
    bool rv;
1919 jermar 272
 
1921 jermar 273
    spinlock_lock(&irq->lock);
274
    rv = (irq->claim() == IRQ_ACCEPT);
275
    spinlock_unlock(&irq->lock);
276
 
277
    return rv;
1919 jermar 278
}
279
 
280
/** @}
281
 */