diff options
Diffstat (limited to 'bsps/sparc/shared/irq/genirq.c')
-rw-r--r-- | bsps/sparc/shared/irq/genirq.c | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/bsps/sparc/shared/irq/genirq.c b/bsps/sparc/shared/irq/genirq.c new file mode 100644 index 0000000000..bba642e701 --- /dev/null +++ b/bsps/sparc/shared/irq/genirq.c @@ -0,0 +1,243 @@ +/* + * Generic interrupt helpers mainly for GRLIB PCI peripherals + * + * COPYRIGHT (c) 2008. + * Cobham Gaisler AB. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <rtems.h> +#include <rtems/bspIo.h> +#include <stdlib.h> +#include <string.h> +#include <bsp/genirq.h> + +struct genirq_handler_entry { + struct genirq_handler_entry *next; /* Next ISR entry for this IRQ number */ + genirq_handler isr; /* ISR function called upon IRQ */ + void *arg; /* custom argument to ISR */ + int enabled; /* Inidicates if IRQ is enabled */ +}; + +struct genirq_irq_entry { + struct genirq_handler_entry *head; + struct genirq_stats stats; +}; + +struct genirq_priv { + /* Maximum number of interrupt */ + int genirq_max; + /* IRQ Table index N reflect IRQ number N */ + struct genirq_irq_entry genirq_table[1]; /* Length depends on */ +}; + +genirq_t genirq_init(int number_of_irqs) +{ + int size; + struct genirq_priv *priv; + + size = sizeof(int) + + number_of_irqs * sizeof(struct genirq_irq_entry); + + priv = (struct genirq_priv *)malloc(size); + if ( !priv ) + return NULL; + memset(priv, 0, size); + priv->genirq_max = number_of_irqs - 1; + + return priv; +} + +void genirq_destroy(genirq_t d) +{ + struct genirq_priv *priv = d; + struct genirq_irq_entry *irqentry; + struct genirq_handler_entry *isrentry, *tmp; + int i; + + /* Free all registered interrupts */ + for ( i=0; i<priv->genirq_max; i++) { + irqentry = &priv->genirq_table[i]; + isrentry = irqentry->head; + while ( isrentry ) { + tmp = isrentry; + isrentry = isrentry->next; + genirq_free_handler(tmp); + } + } + + free(priv); +} + +int genirq_check(genirq_t d, int irq) +{ + struct genirq_priv *priv = d; + + if ( (irq <= 0) || (irq > priv->genirq_max) ) + return -1; + else + return 0; +} + +void *genirq_alloc_handler(genirq_handler isr, void *arg) +{ + struct genirq_handler_entry *newentry; + + newentry = malloc(sizeof(struct genirq_handler_entry)); + if ( newentry ) { + /* Initialize ISR entry */ + newentry->isr = isr; + newentry->arg = arg; + newentry->enabled = 0; + } + return newentry; +} + +int genirq_register(genirq_t d, int irq, void *handler) +{ + struct genirq_priv *priv = d; + struct genirq_irq_entry *irqentry; + struct genirq_handler_entry *isrentry, *newentry = handler; + + if ( genirq_check(d, irq) ) + return -1; + + /* Insert new ISR entry first into table */ + irqentry = &priv->genirq_table[irq]; + isrentry = irqentry->head; + irqentry->head = newentry; + newentry->next = isrentry; + + if ( isrentry ) + return 1; /* This is the first handler on this IRQ */ + return 0; +} + +void *genirq_unregister(genirq_t d, int irq, genirq_handler isr, void *arg) +{ + struct genirq_priv *priv = d; + struct genirq_irq_entry *irqentry; + struct genirq_handler_entry *isrentry, **prev; + void *ret; + + if ( genirq_check(d, irq) ) + return NULL; + + /* Remove isr[arg] from ISR list */ + irqentry = &priv->genirq_table[irq]; + ret = NULL; + + prev = &irqentry->head; + isrentry = irqentry->head; + while ( isrentry ) { + if ( (isrentry->arg == arg) && (isrentry->isr == isr) ) { + /* Found ISR, remove it from list */ + if ( isrentry->enabled ) { + /* Can not remove enabled ISRs, disable first */ + ret = NULL; + break; + } + *prev = isrentry->next; + ret = isrentry; + break; + } + prev = &isrentry->next; + isrentry = isrentry->next; + } + + return ret; +} + +/* Enables or Disables ISR handler. Internal function to reduce footprint + * of enable/disable functions. + * + * \param action 1=enable, 0=disable ISR + */ +static int genirq_set_active( + struct genirq_priv *priv, + int irq, + genirq_handler isr, + void *arg, + int action) +{ + struct genirq_irq_entry *irqentry; + struct genirq_handler_entry *isrentry, *e = NULL; + int enabled; + + if ( genirq_check(priv, irq) ) + return -1; + + /* Find isr[arg] in ISR list */ + irqentry = &priv->genirq_table[irq]; + enabled = 0; + + isrentry = irqentry->head; + while ( isrentry ) { + if ( (isrentry->arg == arg) && (isrentry->isr == isr) ) { + /* Found ISR */ + if ( isrentry->enabled == action ) { + /* The ISR is already enabled or disabled + * depending on request, neccessary actions + * were taken last time the same action was + * requested. + */ + return 1; + } + e = isrentry; + } else { + enabled += isrentry->enabled; + } + isrentry = isrentry->next; + } + + if ( !e ) + return -1; + + e->enabled = action; + + return enabled; +} + +int genirq_enable(genirq_t d, int irq, genirq_handler isr, void *arg) +{ + struct genirq_priv *priv = d; + return genirq_set_active(priv, irq, isr, arg, 1); +} + +int genirq_disable(genirq_t d, int irq, genirq_handler isr, void *arg) +{ + struct genirq_priv *priv = d; + return genirq_set_active(priv, irq, isr, arg, 0); +} + +void genirq_doirq(genirq_t d, int irq) +{ + struct genirq_priv *priv = d; + struct genirq_irq_entry *irqentry; + struct genirq_handler_entry *isrentry; + int enabled; + + irqentry = &priv->genirq_table[irq]; + irqentry->stats.irq_cnt++; + + enabled = 0; + + isrentry = irqentry->head; + while ( isrentry ) { + if ( isrentry->enabled ) { + enabled = 1; + /* Call the ISR */ + isrentry->isr(isrentry->arg); + } + isrentry = isrentry->next; + } + + /* Was the IRQ an IRQ without source? */ + if ( enabled == 0 ) { + /* This should not happen */ + printk("Spurious IRQ happened on IRQ %d\n", irq); + } +} |