mirror of
https://gitlab.rtems.org/rtems/rtos/rtems.git
synced 2025-12-05 15:15:44 +00:00
245 lines
5.2 KiB
C
245 lines
5.2 KiB
C
/*
|
|
* 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 <grlib/genirq.h>
|
|
|
|
#include <grlib/grlib_impl.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[0]; /* Length depends on */
|
|
};
|
|
|
|
genirq_t genirq_init(int number_of_irqs)
|
|
{
|
|
size_t size;
|
|
struct genirq_priv *priv;
|
|
|
|
size = sizeof(*priv) +
|
|
number_of_irqs * sizeof(priv->genirq_table[0]);
|
|
|
|
priv = grlib_calloc(1, size);
|
|
if ( !priv )
|
|
return NULL;
|
|
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 = grlib_malloc(sizeof(*newentry));
|
|
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 if ( isrentry->enabled ) {
|
|
enabled = 1;
|
|
}
|
|
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);
|
|
}
|
|
}
|