/* * $Id: saaic.c,v 1.2 2007/12/16 23:20:31 nbrk Exp $ */ #include #include #include #include #include #include #include #include #define SAAIC_DEBUG #ifdef SAAIC_DEBUG #define DPRINTF(x...) do { printf(x); } while (0) #else #define DPRINTF(x...) { } #endif /* * Advanced Interrupt Controller. */ extern void (*irq_trampoline_func)(void); extern struct intr_vector *irqtable; struct saaic_dd *irqcdd; /* current irqc device */ struct driver saaic_dr = { sizeof(struct saaic_dd), saaic_attach, NULL, NULL }; int saaic_attach(struct device *self, uint32_t loc, uint8_t flags) { struct saaic_dd *ddp = self->dv_devdata; uint8_t intrno; struct intr_vector *ivp = irqtable; /* leap to parent's bus_handle */ ddp->sa_bhp = self->dv_parent->dv_aux; /* make this controller default */ irqcdd = ddp; printf("SAM7 Advanced Interrupt Controller (32 sources, 8 pri_levels)\n"); /* override trampoline so core irq jumps to us */ irq_trampoline_func = saaic_irq; /* mask all interrupts */ for (intrno = 0; intrno < 32; intrno++) saaic_unmask_intr(intrno); /* clear all interrupts */ for (intrno = 0; intrno < 32; intrno++) saaic_clear_intr(intrno); /* look in system irqtable for claimed interrupts and unmask them */ while(ivp != NULL) { saaic_unmask_intr(ivp->iv_intrno); ivp = ivp->iv_next; } __cpu_enable_irq(); return(0); } void saaic_mask_intr(uint8_t intrno) { bus_write_4(irqcdd->sa_bhp, (uint32_t)AT91C_AIC_IDCR, 1 << intrno); DPRINTF("saaic_mask_intr: masked interrupt no. %d (status=0x%x)\n", intrno, saaic_intrstatus()); } void saaic_unmask_intr(uint8_t intrno) { bus_write_1(irqcdd->sa_bhp, (uint32_t)AT91C_AIC_IECR, 1 << intrno); DPRINTF("saaic_unmask_intr: unmasked interrupt no. %d (status=0x%x)\n", intrno, saaic_intrstatus()); } void saaic_clear_intr(uint8_t intrno) { bus_write_1(irqcdd->sa_bhp, (uint32_t)AT91C_AIC_ICCR, 1 << intrno); DPRINTF("saaic_clear_intr: cleared interrupt no. %d (status=0x%x)\n", intrno, saaic_intrstatus()); } void saaic_ack_intr(void) { bus_write_1(irqcdd->sa_bhp, (uint32_t)AT91C_AIC_EOICR, 1); DPRINTF("saaic_ack_intr: acknowledged current interrupt (status=0x%x)\n", saaic_intrstatus()); } uint32_t saaic_intrstatus(void) { return( bus_read_4(irqcdd->sa_bhp, (uint32_t)AT91C_AIC_IPR)); } void saaic_irq(void) { /* * Process an IRQ. * Check interrupt status of the irqc and execute corresponding intr handlers. */ uint32_t irqstatus; uint8_t intrno; /* read intr status; one bit per intr source */ irqstatus = saaic_intrstatus(); DPRINTF("saaic_irq: got interrupt (pending mask=0x%x)\n", irqstatus); /* exit if no interrupts; should not happen */ // if (irqstatus == 0) // return; /* let kern_irq throw us to the right place */ for (intrno = 0; intrno < 32; intrno++) if (irqstatus & (1 << intrno)) { /* interrupt line is active */ intr_execute(intrno); /* XXX */ saaic_clear_intr(intrno); /* XXX acknowledge this interrupt */ saaic_ack_intr(); } }