source: rtems/c/src/lib/libbsp/sparc/shared/irq/genirq.c @ 8670c464

5
Last change on this file since 8670c464 was 8670c464, checked in by Daniel Hellstrom <daniel@…>, on 05/05/17 at 13:42:45

leon, genirq: SMP support for PCI peripherals

The common interrupt layer for GRLIB PCI perihperals is prepared for SMP
support by this patch. The existing locking (interrupt disabling) is
replaced by a new requirement on the user to implement locking before
calling the genirq API. This approach avoids taking more locks than
necessary.

The split up of the locks also introduces that the user must allocate
memory to describe ISR handlers, to avoid calling malloc()/free() while
possessing a spin-lock and interrupts are globally disabled.

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