/* * Generic kernel irq handling. * * Copyright (C) 2007 - 2009 Bahadir Balban */ #include #include #include #include #include #include #include #include #include #include INC_PLAT(irq.h) #include INC_ARCH(exception.h) /* * Registers a userspace thread as an irq handler. * * A userspace irq thread should have a low-level, device-specific * irq handler as an in-kernel counterpart. This and its irq chip * must have been set up at compile-time. These handlers should * also know how to notify their userspace threads. * * If the irq does not have these set up, we cannot allow * the irq registry. */ int irq_register(struct ktcb *task, int notify_slot, l4id_t irq_index) { struct irq_desc *this_desc = irq_desc_array + irq_index; /* Kernel counterpart not set up, don't allow */ if (!this_desc->handler || !this_desc->chip) return -ENOIRQ; /* Index must be valid */ if (irq_index > IRQS_MAX || irq_index < 0) return -ENOIRQ; /* Setup the task and notify slot */ this_desc->task = task; this_desc->task_notify_slot = notify_slot; /* Setup irq desc waitqueue */ waitqueue_head_init(&this_desc->wqh_irq); /* Enable the irq */ irq_enable(irq_index); return 0; } /* If there is cascading, enable it. */ static inline void cascade_irq_chip(struct irq_chip *this_chip) { if (this_chip->cascade >= 0) { BUG_ON(IRQ_CHIPS_MAX == 1); this_chip->ops.unmask(this_chip->cascade); } } void irq_controllers_init(void) { struct irq_chip *this_chip; for (int i = 0; i < IRQ_CHIPS_MAX; i++) { this_chip = irq_chip_array + i; /* Initialise the irq chip (e.g. reset all registers) */ this_chip->ops.init(); /* Enable cascaded irq on this chip if it exists */ cascade_irq_chip(this_chip); } } /* * Finds the global irq number by looping over irq chips. * * Global irq number = * Unique irq chip_offset defined by us + irq number local to chip */ l4id_t global_irq_index(void) { struct irq_chip *this_chip; l4id_t irq_index = 0; /* * Loop over all chips, starting from the top * (i.e. nearest to the cpu) */ for (int i = 0; i < IRQ_CHIPS_MAX; i++) { /* Get the chip */ this_chip = irq_chip_array + i; /* Find local irq that is triggered on this chip */ BUG_ON((irq_index = this_chip->ops.read_irq()) == IRQ_NIL); /* See if this irq is a cascaded irq */ if (irq_index == this_chip->cascade) continue; /* Yes, continue to next chip */ /* * Irq was initiated from this chip. Add this chip's * global irq offset and return it */ irq_index += this_chip->start; return irq_index; } /* * Cascaded irq detected, but no lower chips * left to process. This should not happen */ BUG(); return IRQ_NIL; } void do_irq(void) { l4id_t irq_index = global_irq_index(); struct irq_desc *this_irq = irq_desc_array + irq_index; /* * Note, this can be easily done a few instructions * quicker by some immediate read/disable/enable_all(). * * We currently stick with it as it is clearer. */ irq_disable(irq_index); /* Re-enable all irqs */ enable_irqs(); /* Handle the irq */ BUG_ON(!this_irq->handler); if (this_irq->handler(this_irq) != IRQ_HANDLED) { printk("Spurious or broken irq\n"); BUG(); } irq_enable(irq_index); }