[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.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 */
}