/*
* $Id: saaic.c,v 1.1 2007/11/24 20:49:44 nbrk Exp $
*/
#include <sys/types.h>
#include <sys/device.h>
#include <sys/bus.h>
#include <sys/kern_irq.h>
#include <dev/cpuvar.h>
#include <arch/sam7s64/dev/saaicvar.h>
#include <arch/sam7s64/dev/at91sam7.h>
#include <libkern/printf.h>
#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);
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;
/* 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);
__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());
}
uint32_t
saaic_intrstatus(void)
{
return( bus_read_4(irqcdd->sa_bhp, (uint32_t)AT91C_AIC_IMR));
}
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 (status=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);
}
}