Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:06

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /*
0004  * Generic interrupt helpers mainly for GRLIB PCI peripherals
0005  *
0006  * COPYRIGHT (c) 2008.
0007  * Cobham Gaisler AB.
0008  *
0009  * Redistribution and use in source and binary forms, with or without
0010  * modification, are permitted provided that the following conditions
0011  * are met:
0012  * 1. Redistributions of source code must retain the above copyright
0013  *    notice, this list of conditions and the following disclaimer.
0014  * 2. Redistributions in binary form must reproduce the above copyright
0015  *    notice, this list of conditions and the following disclaimer in the
0016  *    documentation and/or other materials provided with the distribution.
0017  *
0018  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0019  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0020  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0021  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0022  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0023  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0024  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0025  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0026  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0027  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0028  * POSSIBILITY OF SUCH DAMAGE.
0029  */
0030 
0031 #include <rtems.h>
0032 #include <rtems/bspIo.h>
0033 #include <stdlib.h>
0034 #include <string.h>
0035 #include <grlib/genirq.h>
0036 
0037 #include <grlib/grlib_impl.h>
0038 
0039 struct genirq_handler_entry {
0040     struct genirq_handler_entry *next;      /* Next ISR entry for this IRQ number */
0041     genirq_handler          isr;        /* ISR function called upon IRQ */
0042     void                *arg;       /* custom argument to ISR */
0043     int             enabled;    /* Inidicates if IRQ is enabled */
0044 };
0045 
0046 struct genirq_irq_entry {
0047     struct genirq_handler_entry *head;
0048     struct genirq_stats     stats;
0049 };
0050 
0051 struct genirq_priv {
0052     /* Maximum number of interrupt */
0053     int             genirq_max;
0054     /* IRQ Table index N reflect IRQ number N */
0055     struct genirq_irq_entry     genirq_table[0]; /* Length depends on */
0056 };
0057 
0058 genirq_t genirq_init(int number_of_irqs)
0059 {
0060     size_t size;
0061     struct genirq_priv *priv;
0062 
0063     size = sizeof(*priv) +
0064            number_of_irqs * sizeof(priv->genirq_table[0]);
0065 
0066     priv = grlib_calloc(1, size);
0067     if ( !priv )
0068         return NULL;
0069     priv->genirq_max = number_of_irqs - 1;
0070 
0071     return priv;
0072 }
0073 
0074 void genirq_destroy(genirq_t d)
0075 {
0076     struct genirq_priv *priv = d;
0077     struct genirq_irq_entry *irqentry;
0078     struct genirq_handler_entry *isrentry, *tmp;
0079     int i;
0080 
0081     /* Free all registered interrupts */
0082     for ( i=0; i<priv->genirq_max; i++) {
0083         irqentry = &priv->genirq_table[i];
0084         isrentry = irqentry->head;
0085         while ( isrentry ) {
0086             tmp = isrentry;
0087             isrentry = isrentry->next;
0088             genirq_free_handler(tmp);
0089         }
0090     }
0091 
0092     free(priv);
0093 }
0094 
0095 int genirq_check(genirq_t d, int irq)
0096 {
0097     struct genirq_priv *priv = d;
0098 
0099     if ( (irq <= 0) || (irq > priv->genirq_max) )
0100         return -1;
0101     else
0102         return 0;
0103 }
0104 
0105 void *genirq_alloc_handler(genirq_handler isr, void *arg)
0106 {
0107     struct genirq_handler_entry *newentry;
0108 
0109     newentry = grlib_malloc(sizeof(*newentry));
0110     if ( newentry ) {
0111         /* Initialize ISR entry */
0112         newentry->isr     = isr;
0113         newentry->arg     = arg;
0114         newentry->enabled = 0;
0115     }
0116     return newentry;
0117 }
0118 
0119 int genirq_register(genirq_t d, int irq, void *handler)
0120 {
0121     struct genirq_priv *priv = d;
0122     struct genirq_irq_entry *irqentry;
0123     struct genirq_handler_entry *isrentry, *newentry = handler;
0124 
0125     if ( genirq_check(d, irq) )
0126         return -1;
0127 
0128     /* Insert new ISR entry first into table */
0129     irqentry = &priv->genirq_table[irq];
0130     isrentry = irqentry->head;
0131     irqentry->head = newentry;
0132     newentry->next = isrentry;
0133 
0134     if ( isrentry )
0135         return 1; /* This is the first handler on this IRQ */
0136     return 0;
0137 }
0138 
0139 void *genirq_unregister(genirq_t d, int irq, genirq_handler isr, void *arg)
0140 {
0141     struct genirq_priv *priv = d;
0142     struct genirq_irq_entry *irqentry;
0143     struct genirq_handler_entry *isrentry, **prev;
0144     void *ret;
0145 
0146     if ( genirq_check(d, irq) )
0147         return NULL;
0148 
0149     /* Remove isr[arg] from ISR list */
0150     irqentry = &priv->genirq_table[irq];
0151     ret = NULL;
0152 
0153     prev = &irqentry->head;
0154     isrentry = irqentry->head;
0155     while ( isrentry ) {
0156         if ( (isrentry->arg == arg) && (isrentry->isr == isr) ) {
0157             /* Found ISR, remove it from list */
0158             if ( isrentry->enabled ) {
0159                 /* Can not remove enabled ISRs, disable first */
0160                 ret = NULL;
0161                 break;
0162             }
0163             *prev = isrentry->next;
0164             ret = isrentry;
0165             break;
0166         }
0167         prev = &isrentry->next;
0168         isrentry = isrentry->next;
0169     }
0170 
0171     return ret;
0172 }
0173 
0174 /* Enables or Disables ISR handler. Internal function to reduce footprint
0175  * of enable/disable functions.
0176  *
0177  * \param action 1=enable, 0=disable ISR
0178  */
0179 static int genirq_set_active(
0180     struct genirq_priv *priv,
0181     int irq,
0182     genirq_handler isr,
0183     void *arg,
0184     int action)
0185 {
0186     struct genirq_irq_entry *irqentry;
0187     struct genirq_handler_entry *isrentry, *e = NULL;
0188     int enabled;
0189 
0190     if ( genirq_check(priv, irq) )
0191         return -1;
0192 
0193     /* Find isr[arg] in ISR list */
0194     irqentry = &priv->genirq_table[irq];
0195     enabled = 0;
0196 
0197     isrentry = irqentry->head;
0198     while ( isrentry ) {
0199         if ( (isrentry->arg == arg) && (isrentry->isr == isr) ) {
0200             /* Found ISR */
0201             if ( isrentry->enabled == action ) {
0202                 /* The ISR is already enabled or disabled 
0203                  * depending on request, neccessary actions
0204                  * were taken last time the same action was
0205                  * requested.
0206                  */
0207                 return 1;
0208             }
0209             e = isrentry;
0210         } else if ( isrentry->enabled ) {
0211             enabled = 1;
0212         }
0213         isrentry = isrentry->next;
0214     }
0215 
0216     if ( !e )
0217         return -1;
0218 
0219     e->enabled = action;
0220 
0221     return enabled;
0222 }
0223 
0224 int genirq_enable(genirq_t d, int irq, genirq_handler isr, void *arg)
0225 {
0226     struct genirq_priv *priv = d;
0227     return genirq_set_active(priv, irq, isr, arg, 1);
0228 }
0229 
0230 int genirq_disable(genirq_t d, int irq, genirq_handler isr, void *arg)
0231 {
0232     struct genirq_priv *priv = d;
0233     return genirq_set_active(priv, irq, isr, arg, 0);
0234 }
0235 
0236 void genirq_doirq(genirq_t d, int irq)
0237 {
0238     struct genirq_priv *priv = d;
0239     struct genirq_irq_entry *irqentry;
0240     struct genirq_handler_entry *isrentry;
0241     int enabled;
0242 
0243     irqentry = &priv->genirq_table[irq];
0244     irqentry->stats.irq_cnt++;
0245 
0246     enabled = 0;
0247 
0248     isrentry = irqentry->head;
0249     while ( isrentry ) {
0250         if ( isrentry->enabled ) {
0251             enabled = 1;
0252             /* Call the ISR */
0253             isrentry->isr(isrentry->arg);
0254         }
0255         isrentry = isrentry->next;
0256     }
0257 
0258     /* Was the IRQ an IRQ without source? */
0259     if ( enabled == 0 ) {
0260         /* This should not happen */
0261         printk("Spurious IRQ happened on IRQ %d\n", irq);
0262     }
0263 }