File: [local] / funnyos / arch / sam7s64 / dev / saaic.c (download)
Revision 1.2, Sun Dec 16 23:20:31 2007 UTC (16 years, 5 months ago) by nbrk
Branch: MAIN
CVS Tags: HEAD Changes since 1.1: +43 -7 lines
add saaic_clear_intr() and saaic_ack_intr(); shuffle code a bit;
still half functional (and SAAIC_DEBUG is on by default)
|
/*
* $Id: saaic.c,v 1.2 2007/12/16 23:20:31 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);
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();
}
}