[BACK]Return to sa1111_intr.c CVS log [TXT][DIR] Up to [local] / sys / arch / arm / sa11x0

File: [local] / sys / arch / arm / sa11x0 / sa1111_intr.c (download)

Revision 1.3, Wed Mar 5 20:27:08 2008 UTC (16 years, 2 months ago) by nbrk
Branch: MAIN
CVS Tags: HEAD
Changes since 1.2: +5 -11 lines

register dispatcher at IPL_TTY (instead of IPL_AUDIO);
do not mask and ack pending interrupts on attachemnt until we figure out
magic that causes data abort here..
do not print 'no handler for pending intrno' because they are (until we fix
data abort in attachment).

/*	$Id: sa1111_intr.c,v 1.3 2008/03/05 20:27:08 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));
#if 0
	/* 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);
#endif
	printf(": SA-1111 Interrupt Controller\n");
	/* XXX IPL_AUDIO ? */
	sc->sc_hostih = sa11x0_gpio_intr_establish(saa->sac_xintr, IST_EDGE_RISING, IPL_TTY,
						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);

				/* 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);

				/* 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);
	memset(sih, 0, sizeof(struct sacic_intrhandler));
	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)
{
	/*
	 * Unlink interrupt handler and free its data structures.
	 */
	struct sacic_softc *sc = sacic_sc;
	struct sacic_intrhandler *sih = cookie;

	sc->sc_handlers[sih->ih_xintr] = NULL;

	FREE(sih, M_DEVBUF);
}