source: rtems/c/src/lib/libbsp/sparc/shared/irq/genirq.c @ 5c93fb3b

5
Last change on this file since 5c93fb3b was 4a7d1026, checked in by Daniel Hellstrom <daniel@…>, on 04/13/15 at 08:25:52

sparc bsps: updated license to rtems.org

  • Property mode set to 100644
File size: 5.2 KB
Line 
1/*
2 * Generic interrupt helpers mainly for GRLIB PCI peripherals
3 *
4 * COPYRIGHT (c) 2008.
5 * Cobham Gaisler AB.
6 *
7 * The license and distribution terms for this file may be
8 * found in the file LICENSE in this distribution or at
9 * http://www.rtems.org/license/LICENSE.
10 */
11
12#include <rtems.h>
13#include <stdlib.h>
14#include <string.h>
15#include <bsp/genirq.h>
16
17struct genirq_handler_entry {
18        struct genirq_handler_entry     *next;          /* Next ISR entry for this IRQ number */
19        genirq_handler                  isr;            /* ISR function called upon IRQ */
20        void                            *arg;           /* custom argument to ISR */
21        int                             enabled;        /* Inidicates if IRQ is enabled */
22};
23
24struct genirq_irq_entry {
25        struct genirq_handler_entry     *head;
26        struct genirq_stats             stats;
27};
28
29struct genirq_priv {
30        /* Maximum number of interrupt */
31        int                             genirq_max;
32        /* IRQ Table index N reflect IRQ number N */
33        struct genirq_irq_entry         genirq_table[1]; /* Length depends on */
34};
35
36genirq_t genirq_init(int number_of_irqs)
37{
38        int size;
39        struct genirq_priv *priv;
40
41        size = sizeof(int) +
42               number_of_irqs * sizeof(struct genirq_irq_entry);
43
44        priv = (struct genirq_priv *)malloc(size);
45        if ( !priv )
46                return NULL;
47        memset(priv, 0, size);
48        priv->genirq_max = number_of_irqs - 1;
49        return priv;
50}
51
52void genirq_destroy(genirq_t d)
53{
54        struct genirq_priv *priv = d;
55        struct genirq_irq_entry *irqentry;
56        struct genirq_handler_entry *isrentry, *tmp;
57        int i;
58
59        /* Free all registered interrupts */
60        for ( i=0; i<priv->genirq_max; i++) {
61                irqentry = &priv->genirq_table[i];
62                isrentry = irqentry->head;
63                while ( isrentry ) {
64                        tmp = isrentry;
65                        isrentry = isrentry->next;
66                        free(tmp);
67                }
68        }
69
70        free(priv);
71}
72
73int genirq_check(genirq_t d, int irq)
74{
75        struct genirq_priv *priv = d;
76
77        if ( (irq <= 0) || (irq > priv->genirq_max) )
78                return -1;
79        else
80                return 0;
81}
82
83int genirq_register(genirq_t d, int irq, genirq_handler isr, void *arg)
84{
85        struct genirq_priv *priv = d;
86        struct genirq_irq_entry *irqentry;
87        struct genirq_handler_entry *isrentry, *newentry;
88        rtems_interrupt_level level;
89       
90        if ( genirq_check(d, irq) )
91                return -1;
92
93        newentry = malloc(sizeof(*newentry));
94        if ( !newentry )
95                return -1;
96
97        /* Initialize ISR entry */
98        newentry->isr     = isr;
99        newentry->arg     = arg;
100        newentry->enabled = 0;
101
102        rtems_interrupt_disable(level);
103
104        /* Insert new ISR entry first into table */
105        irqentry = &priv->genirq_table[irq];
106        isrentry = irqentry->head;
107        irqentry->head = newentry;
108        newentry->next = isrentry;
109
110        rtems_interrupt_enable(level);
111
112        if ( isrentry )
113                return 1; /* This is the first handler on this IRQ */
114        return 0;
115}
116
117int genirq_unregister(genirq_t d, int irq, genirq_handler isr, void *arg)
118{
119        struct genirq_priv *priv = d;
120        struct genirq_irq_entry *irqentry;
121        struct genirq_handler_entry *isrentry, **prev;
122        rtems_interrupt_level level;
123        int ret;
124
125        if ( genirq_check(d, irq) )
126                return -1;
127
128        /* Remove isr[arg] from ISR list */
129        irqentry = &priv->genirq_table[irq];
130        ret = -1;
131
132        rtems_interrupt_disable(level);
133
134        prev = &irqentry->head;
135        isrentry = irqentry->head;
136        while ( isrentry ) {
137                if ( (isrentry->arg == arg) && (isrentry->isr == isr) ) {
138                        /* Found ISR, remove it from list */
139                        if ( isrentry->enabled ) {
140                                /* Can not remove enabled ISRs, disable first */
141                                ret = 1;
142                                break;
143                        }
144                        *prev = isrentry->next;
145                        ret = 0;
146                        break;
147                }
148                prev = &isrentry->next;
149                isrentry = isrentry->next;
150        }
151
152        rtems_interrupt_enable(level);
153
154        return ret;
155}
156
157/* Enables or Disables ISR handler. Internal function to reduce footprint
158 * of enable/disable functions.
159 *
160 * \param action 1=enable, 0=disable ISR
161 */
162static int genirq_set_active(
163        struct genirq_priv *priv,
164        int irq,
165        genirq_handler isr,
166        void *arg,
167        int action)
168{
169        struct genirq_irq_entry *irqentry;
170        struct genirq_handler_entry *isrentry, *e = NULL;
171        int enabled;
172
173        if ( genirq_check(priv, irq) )
174                return -1;
175
176        /* Find isr[arg] in ISR list */
177        irqentry = &priv->genirq_table[irq];
178        enabled = 0;
179
180        isrentry = irqentry->head;
181        while ( isrentry ) {
182                if ( (isrentry->arg == arg) && (isrentry->isr == isr) ) {
183                        /* Found ISR */
184                        if ( isrentry->enabled == action ) {
185                                /* The ISR is already enabled or disabled
186                                 * depending on request, neccessary actions
187                                 * were taken last time the same action was
188                                 * requested.
189                                 */
190                                return 1;
191                        }
192                        e = isrentry;
193                } else {
194                        enabled += isrentry->enabled;
195                }
196                isrentry = isrentry->next;
197        }
198
199        if ( !e )
200                return -1;
201
202        e->enabled = action;
203
204        return enabled;
205}
206
207int genirq_enable(genirq_t d, int irq, genirq_handler isr, void *arg)
208{
209        struct genirq_priv *priv = d;
210        return genirq_set_active(priv, irq, isr, arg, 1);
211}
212
213int genirq_disable(genirq_t d, int irq, genirq_handler isr, void *arg)
214{
215        struct genirq_priv *priv = d;
216        return genirq_set_active(priv, irq, isr, arg, 0);
217}
218
219void genirq_doirq(genirq_t d, int irq)
220{
221        struct genirq_priv *priv = d;
222        struct genirq_irq_entry *irqentry;
223        struct genirq_handler_entry *isrentry;
224        int enabled;
225
226        irqentry = &priv->genirq_table[irq];
227        irqentry->stats.irq_cnt++;
228       
229        enabled = 0;
230
231        isrentry = irqentry->head;
232        while ( isrentry ) {
233                if ( isrentry->enabled ) {
234                        enabled = 1;
235                        /* Call the ISR */
236                        isrentry->isr(isrentry->arg);
237                }
238                isrentry = isrentry->next;
239        }
240
241        /* Was the IRQ an IRQ without source? */
242        if ( enabled == 0 ) {
243                /* This should not happen */
244                printk("Spurious IRQ happened on IRQ %d\n", irq);
245        }
246}
Note: See TracBrowser for help on using the repository browser.