diff options
author | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2012-02-19 06:14:24 +0000 |
---|---|---|
committer | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2012-02-19 06:14:24 +0000 |
commit | 6fafeb146e9efd59140ea58cebd7dd38ae9a6379 (patch) | |
tree | 7db89ba6a28932514b105d620bba4884ec332ec3 /libdde_linux26/lib/src/arch/l4/irq.c | |
parent | 38c2c2458e3f4ecb329ff35621806252aac209b9 (diff) | |
parent | 8df772b3c665e663f6f9d2a70f9c691590bd3f91 (diff) |
Merge branch 'dde' into upstream-merged
Diffstat (limited to 'libdde_linux26/lib/src/arch/l4/irq.c')
-rw-r--r-- | libdde_linux26/lib/src/arch/l4/irq.c | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/libdde_linux26/lib/src/arch/l4/irq.c b/libdde_linux26/lib/src/arch/l4/irq.c new file mode 100644 index 00000000..20bd57f0 --- /dev/null +++ b/libdde_linux26/lib/src/arch/l4/irq.c @@ -0,0 +1,244 @@ +/* + * \brief Hardware-interrupt support + * \author Christian Helmuth <ch12@os.inf.tu-dresden.de> + * \date 2007-02-12 + * + * + * + * XXX Consider support for IRQ_HANDLED and friends (linux/irqreturn.h) + */ + +/* Linux */ +#include <linux/interrupt.h> +#include <linux/string.h> /* memset() */ + +/* local */ +#include "dde26.h" +#include "local.h" + +/* dummy */ +irq_cpustat_t irq_stat[CONFIG_NR_CPUS]; + +/** + * IRQ handling data + */ +static struct dde_irq +{ + unsigned irq; /* IRQ number */ + unsigned count; /* usage count */ + int shared; /* shared IRQ */ + struct ddekit_thread *thread; /* DDEKit interrupt thread */ + struct irqaction *action; /* Linux IRQ action */ + + struct dde_irq *next; /* next DDE IRQ */ +} *used_irqs; + + +static void irq_thread_init(void *p) { + l4dde26_process_add_worker(); } + + +extern ddekit_sem_t *dde_softirq_sem; +static void irq_handler(void *arg) +{ + struct dde_irq *irq = arg; + struct irqaction *action; + +#if 0 + DEBUG_MSG("irq 0x%x", irq->irq); +#endif + /* interrupt occurred - call all handlers */ + for (action = irq->action; action; action = action->next) { + irqreturn_t r = action->handler(action->irq, action->dev_id); +#if 0 + DEBUG_MSG("return: %s", r == IRQ_HANDLED ? "IRQ_HANDLED" : r == IRQ_NONE ? "IRQ_NONE" : "??"); +#endif + } + + /* upon return we check for pending soft irqs */ + if (local_softirq_pending()) + ddekit_sem_up(dde_softirq_sem); +} + + +/***************************** + ** IRQ handler bookkeeping ** + *****************************/ + +/** + * Claim IRQ + * + * \return usage counter or negative error code + * + * FIXME list locking + * FIXME are there more races? + */ +static int claim_irq(struct irqaction *action) +{ + int shared = action->flags & IRQF_SHARED ? 1 : 0; + struct dde_irq *irq; + + /* check if IRQ already used */ + for (irq = used_irqs; irq; irq = irq->next) + if (irq->irq == action->irq) break; + + /* we have to setup IRQ handling */ + if (!irq) { + /* allocate and initalize new descriptor */ + irq = ddekit_simple_malloc(sizeof(*irq)); + if (!irq) return -ENOMEM; + memset(irq, 0, sizeof(*irq)); + + irq->irq = action->irq; + irq->shared = shared; + irq->next = used_irqs; + used_irqs = irq; + + /* attach to interrupt */ + irq->thread = ddekit_interrupt_attach(irq->irq, + irq->shared, + irq_thread_init, + irq_handler, + (void *)irq); + if (!irq->thread) { + used_irqs = irq->next; + ddekit_simple_free(irq); + return -EBUSY; + } + } + + /* does desciptor allow our new handler? */ + if ((!irq->shared || !shared) && irq->action) return -EBUSY; + + /* add handler */ + irq->count++; + action->next = irq->action; + irq->action = action; + + return irq->count; +} + + +/** + * Free previously claimed IRQ + * + * \return usage counter or negative error code + */ +static struct irqaction *release_irq(unsigned irq_num, void *dev_id) +{ + struct dde_irq *prev_irq, *irq; + + /* check if IRQ already used */ + for (prev_irq = 0, irq = used_irqs; irq; + prev_irq = irq, irq = irq->next) + if (irq->irq == irq_num) break; + + if (!irq) return 0; + + struct irqaction *prev_action, *action; + + for (prev_action = 0, action = irq->action; action; + prev_action = action, action = action->next) + if (action->dev_id == dev_id) break; + + if (!action) return 0; + + /* dequeue action from irq */ + if (prev_action) + prev_action->next = action->next; + else + irq->action = action->next; + + /* dequeue irq from used_irqs list and free structure, + if no more actions available */ + if (!irq->action) { + if (prev_irq) + prev_irq->next = irq->next; + else + used_irqs = irq->next; + + /* detach from interrupt */ + ddekit_interrupt_detach(irq->irq); + + ddekit_simple_free(irq); + } + + return action; +} + + +/*************** + ** Linux API ** + ***************/ + +/** + * Request interrupt + * + * \param irq interrupt number + * \param handler interrupt handler -> top half + * \param flags interrupt handling flags (SA_SHIRQ, ...) + * \param dev_name device name + * \param dev_id cookie passed back to handler + * + * \return 0 on success; error code otherwise + * + * \todo FIXME consider locking! + */ +int request_irq(unsigned int irq, irq_handler_t handler, + unsigned long flags, const char *dev_name, void *dev_id) +{ + if (!handler) return -EINVAL; + + /* facilitate Linux irqaction for this handler */ + struct irqaction *irq_action = ddekit_simple_malloc(sizeof(*irq_action)); + if (!irq_action) return -ENOMEM; + memset(irq_action, 0, sizeof(*irq_action)); + + irq_action->handler = handler; + irq_action->flags = flags; + irq_action->name = dev_name; + irq_action->dev_id = dev_id; + irq_action->irq = irq; + + /* attach to IRQ */ + int err = claim_irq(irq_action); + if (err < 0) return err; + + return 0; +} + +/** Release Interrupt + * \ingroup mod_irq + * + * \param irq interrupt number + * \param dev_id cookie passed back to handler + * + */ +void free_irq(unsigned int irq, void *dev_id) +{ + struct irqaction *irq_action = release_irq(irq, dev_id); + + if (irq_action) + ddekit_simple_free(irq_action); +} + +void disable_irq(unsigned int irq) +{ + ddekit_interrupt_disable(irq); +} + +void disable_irq_nosync(unsigned int irq) +{ + /* + * Note: + * In contrast to the _nosync semantics, DDEKit's + * disable definitely waits until a currently executed + * IRQ handler terminates. + */ + ddekit_interrupt_disable(irq); +} + +void enable_irq(unsigned int irq) +{ + ddekit_interrupt_enable(irq); +} |