File: [local] / sys / arch / arm / sa11x0 / sa1111_intr.c (download)
Revision 1.1, Wed Mar 5 11:19:00 2008 UTC (16 years, 3 months ago) by nbrk
Branch: MAIN
Add sacic, initial driver for SA-1111 Interrupt Controller.
This icu has many sources (55) and signals to main SA-1110 by generating
rising edge on INT line. SA-1111's INT line is routed to GPIO 0 on the SA-1110
GPIO controller (so we establish our dispatcher on host irq 0, rising edge)
|
/* $Id: sa1111_intr.c,v 1.1 2008/03/05 11:19:00 nbrk Exp $ */
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/evcount.h>
#include <sys/queue.h>
#include <uvm/uvm_extern.h>
#include <machine/bus.h>
#include <machine/intr.h>
#include <machine/lock.h>
#include <arm/sa11x0/sa11x0_intr.h>
#include <arm/sa11x0/sa11x0_gpiovar.h>
#include <arm/sa11x0/sa11x1_var.h>
#include <arm/sa11x0/sa1111_reg.h>
#include <arm/sa11x0/sa1111_intr.h>
/*
* TODO:
* - SPL
*/
/*
* Autoconf glue.
*/
int sacic_match(struct device *, void *, void *);
void sacic_attach(struct device *, struct device *, void *);
int sacic_dispatcher(void *arg);
void sacic_intr_mask(int intrno);
void sacic_intr_unmask(int intrno);
void sacic_intr_clear(int intrno);
void sacic_set_intr_level(int intrno, int level);
struct cfattach sacic_ca = {
sizeof(struct sacic_softc), sacic_match, sacic_attach
};
struct cfdriver sacic_cd = {
NULL, "sacic", DV_DULL
};
struct sacic_softc *sacic_sc;
int
sacic_match(struct device *parent, void *cf, void *aux)
{
struct sacc_attach_args *saa = aux;
if (sacic_sc != NULL || saa->sac_typecookie != SACC_TYPE_INTC)
return(0);
return(1);
}
void
sacic_attach(struct device *parent, struct device *self, void *aux)
{
/*
* Initialize ourselfes and establish our dispatcher on host irq xintr.
*/
struct sacic_softc *sc = (struct sacic_softc *)self;
struct sacc_attach_args *saa = aux;
sc->sc_bust = saa->sac_iot;
sc->sc_bush = saa->sac_bush;
/* NULLify handlers */
memset(sc->sc_handlers, 0, sizeof(sc->sc_handlers));
/* mask all interrupts.. */
bus_space_write_4(sc->sc_bust, sc->sc_bush, SACCIC_INTEN0, 0);
bus_space_write_4(sc->sc_bust, sc->sc_bush, SACCIC_INTEN1, 0);
/* ..and kill all pending ones */
bus_space_write_4(sc->sc_bust, sc->sc_bush, SACCIC_INTSTATCLR0, 0xffffffff);
bus_space_write_4(sc->sc_bust, sc->sc_bush, SACCIC_INTSTATCLR1, 0xffffffff);
/* XXX IPL_AUDIO ? */
sc->sc_hostih = sa11x0_gpio_intr_establish(saa->sac_xintr, IST_EDGE_RISING, IPL_AUDIO,
sacic_dispatcher, NULL, "sacic");
sacic_sc = sc;
}
void
sacic_intr_mask(int intrno)
{
struct sacic_softc *sc = sacic_sc;
bus_addr_t reg;
uint32_t oldmask;
if (sc == NULL)
panic("%s: sacic_intr_mask: sacic not configured", sc->sc_dev.dv_xname);
/* select apropriate register bank */
if (intrno >= 0 && intrno < 32)
reg = SACCIC_INTEN0;
else {
if (intrno >= 32 && intrno < SACCIC_LEN) {
reg = SACCIC_INTEN1;
/* adjust to be 0..31 */
intrno -= 32;
}
else
/* out of range */
panic("%s: sacic_intr_mask: bogus intrno %d", sc->sc_dev.dv_xname, intrno);
}
oldmask = bus_space_read_4(sc->sc_bust, sc->sc_bush, reg);
bus_space_write_4(sc->sc_bust, sc->sc_bush, reg, oldmask & ~(1 << intrno));
}
void
sacic_intr_unmask(int intrno)
{
struct sacic_softc *sc = sacic_sc;
bus_addr_t reg;
if (sc == NULL)
panic("sacic_intr_mask: sacic not configured");
/* select apropriate register bank */
if (intrno >= 0 && intrno < 32)
reg = SACCIC_INTEN0;
else {
if (intrno >= 32 && intrno < SACCIC_LEN) {
reg = SACCIC_INTEN1;
/* adjust to be 0..31 */
intrno -= 32;
}
else
/* out of range */
panic("%s: sacic_intr_mask: bogus intrno %d", sc->sc_dev.dv_xname, intrno);
}
bus_space_write_4(sc->sc_bust, sc->sc_bush, reg, 1 << intrno);
}
void
sacic_intr_clear(int intrno)
{
struct sacic_softc *sc = sacic_sc;
bus_addr_t reg;
/* select apropriate register bank */
if (intrno >= 0 && intrno < 32)
reg = SACCIC_INTSTATCLR0;
else {
if (intrno >= 32 && intrno < SACCIC_LEN) {
reg = SACCIC_INTSTATCLR1;
/* adjust to be 0..31 */
intrno -= 32;
}
else
/* out of range */
panic("%s: sacic_intr_clear: bogus intrno %d", sc->sc_dev.dv_xname, intrno);
}
bus_space_write_4(sc->sc_bust, sc->sc_bush, reg, 1 << intrno);
}
void
sacic_set_intr_level(int intrno, int level)
{
/*
* Set interrupt polarity to either falling or rising edge.
*/
struct sacic_softc *sc = sacic_sc;
bus_addr_t reg;
uint32_t oldpol;
if (sc == NULL)
panic("sacic_set_intr_level: sacic not configured");
/* select apropriate register bank */
if (intrno >= 0 && intrno < 32)
reg = SACCIC_INTPOL0;
else {
if (intrno >= 32 && intrno < SACCIC_LEN) {
reg = SACCIC_INTPOL1;
/* adjust to be 0..31 */
intrno -= 32;
}
else
/* out of range */
panic("%s: sacic_set_intr_level: bogus intrno %d", sc->sc_dev.dv_xname, intrno);
}
oldpol = bus_space_read_4(sc->sc_bust, sc->sc_bush, reg);
switch (level) {
case IST_EDGE_FALLING:
oldpol |= 1 << intrno;
break;
case IST_EDGE_RISING:
oldpol &= ~(1 << intrno);
break;
default:
panic("%s: bad level: %d", sc->sc_dev.dv_xname, level);
break;
}
bus_space_write_4(sc->sc_bust, sc->sc_bush, reg, oldpol);
}
int
sacic_dispatcher(void *arg)
{
/*
* Process pending interrupt(s).
*/
struct sacic_softc *sc = sacic_sc;
uint32_t intstat, intbit;
int intrno;
/* check first bank */
intstat = bus_space_read_4(sc->sc_bust, sc->sc_bush, SACCIC_INTSTATCLR0);
for (intbit = 1, intrno = 0; intbit > 0; intbit <<= 1, intrno++) {
if (intstat & intbit) {
/* interrupt set */
if (sc->sc_handlers[intrno] != NULL)
/* jump! */
sc->sc_handlers[intrno]->ih_func(sc->sc_handlers[intrno]->ih_arg);
else
/* no handler */
printf("%s: sacic_dispatcher: warning, no handler for pending intrno %d\n",
sc->sc_dev.dv_xname, intrno);
/* XXX acknowledge intr anyway */
sacic_intr_clear(intrno);
}
}
/* check second bank */
intstat = bus_space_read_4(sc->sc_bust, sc->sc_bush, SACCIC_INTSTATCLR1);
for (intbit = 1, intrno = 32; intbit > 0; intbit <<= 1, intrno++) {
if (intstat & intbit) {
/* interrupt set */
if (sc->sc_handlers[intrno] != NULL)
/* jump! */
sc->sc_handlers[intrno]->ih_func(sc->sc_handlers[intrno]->ih_arg);
else
/* no handler */
printf("%s: sacic_dispatcher: warning, no handler for pending intrno %d\n",
sc->sc_dev.dv_xname, intrno);
/* XXX acknowledge intr anyway */
sacic_intr_clear(intrno);
}
}
return(0);
}
void
*sa11x1_intr_establish(int irq, int level, int (*func)(void *),
void *arg, char *name)
{
struct sacic_softc *sc = sacic_sc;
struct sacic_intrhandler *sih;
if (sc == NULL)
panic("sa11x1_intr_establish: sacic not configured");
if (irq < 0 || irq > 31)
panic("sa11x1_intr_establish: bogus intrno %d", irq);
MALLOC(sih, struct sacic_intrhandler *, sizeof(struct sacic_intrhandler),
M_DEVBUF, M_NOWAIT);
sih->ih_xintr = irq;
sih->ih_level = level;
sih->ih_func = func;
sih->ih_arg = arg;
sih->ih_name = name;
/* TODO evcount? */
sacic_set_intr_level(irq, level);
/* XXX silently drops previous handler */
sc->sc_handlers[irq] = sih;
sacic_intr_unmask(irq);
return(sih);
}
void sa11x1_intr_disestablish(void *cookie)
{
/* TODO */
}