/* $Id: sa1111_intr.c,v 1.3 2008/03/05 20:27:08 nbrk Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * 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); }