/* $OpenBSD: sa11x0_gpio.c,v 1.18 2005/06/16 21:57:29 drahn Exp $ */ /* $NetBSD: sa11x0_gpio.c,v 1.2 2003/07/15 00:24:55 lukem Exp $ */ /* * Copyright 2003 Wasabi Systems, Inc. * All rights reserved. * * Written by Steve C. Woodford for Wasabi Systems, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed for the NetBSD Project by * Wasabi Systems, Inc. * 4. The name of Wasabi Systems, Inc. may not be used to endorse * or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * General Purpose Input-Output found on SA11[01]0. * It features 28 GPIO lines but some are used for "alternate" functions. * Pins 0-10 are independent interrupt sources in SA-11x0 Interrupt Controller. * Remaining lines 11-27 are OR'ed together and act like one interrupt source. */ #include #include #include #include #include #include #include #include #include #include #include #include #include struct gpio_irq_handler { // struct gpio_irq_handler *gh_next; int (*gh_func)(void *); void *gh_arg; int gh_spl; u_int gh_gpio; int gh_level; int gh_irq; /* intrno in an INTC */ struct evcount gh_count; }; struct sagpio_softc { struct device sc_dev; bus_space_tag_t sc_bust; bus_space_handle_t sc_bush; // void *sc_irqcookie[4]; // u_int32_t sc_mask[3]; struct gpio_irq_handler *sc_handlers[SAGPIO_NPINS]; int sc_minipl; int sc_maxipl; int sc_npins; }; int sagpio_match(struct device *, void *, void *); void sagpio_attach(struct device *, struct device *, void *); struct cfattach sagpio_ca = { sizeof (struct sagpio_softc), sagpio_match, sagpio_attach }; struct cfdriver sagpio_cd = { NULL, "sagpio", DV_DULL }; static struct sagpio_softc *sagpio_softc; static vaddr_t sagpio_regs; #define GPIO_BOOTSTRAP_REG(reg) \ (*((volatile u_int32_t *)(sagpio_regs + (reg)))) void sa11x0_gpio_set_intr_level(u_int, int); int sagpio_dispatch(void *arg); int sagpio_irq_handler(void *arg); u_int32_t sagpio_reg_read(struct sagpio_softc *sc, int reg); void sagpio_reg_write(struct sagpio_softc *sc, int reg, u_int32_t val); uint32_t sagpio_reg_read(struct sagpio_softc *sc, int reg) { if (sc != NULL) return (bus_space_read_4(sc->sc_bust, sc->sc_bush, reg)); else if (sagpio_regs) return (GPIO_BOOTSTRAP_REG(reg)); panic("sagpio_reg_read: not bootstrapped"); } void sagpio_reg_write(struct sagpio_softc *sc, int reg, u_int32_t val) { if (sc != NULL) bus_space_write_4(sc->sc_bust, sc->sc_bush, reg, val); else if (sagpio_regs) GPIO_BOOTSTRAP_REG(reg) = val; else panic("sagpio_reg_write: not bootstrapped"); return; } int sagpio_match(struct device *parent, void *cf, void *aux) { struct saip_attach_args *saa = aux; if (sagpio_softc != NULL && saa->sai_addr != SAGPIO_BASE) return (0); saa->sai_size = SAGPIO_NPORTS; return (1); } void sagpio_attach(struct device *parent, struct device *self, void *aux) { struct sagpio_softc *sc = (struct sagpio_softc *)self; struct saip_attach_args *saa = aux; sc->sc_bust = saa->sai_iot; printf(": SA-11x0 GPIO Controller\n"); if (cputype == CPU_ID_SA1110) { sc->sc_npins = SAGPIO_NPINS; } else { /* XXX */ printf("%s: Attaching to non-SA1110 cputype\n", sc->sc_dev.dv_xname); return; } if (bus_space_map(sc->sc_bust, saa->sai_addr, saa->sai_size, 0, &sc->sc_bush)) { /* XXX panic here? */ printf("%s: Can't map registers!\n", sc->sc_dev.dv_xname); return; } /* NULLify */ memset(sc->sc_handlers, 0, sizeof(sc->sc_handlers)); /* disable all Rising/Falling Edge detects */ // sagpio_reg_write(sc, SAGPIO_RER, 0); // sagpio_reg_write(sc, SAGPIO_FER, 0); /* clear all previous Edge Detects */ sagpio_reg_write(sc, SAGPIO_EDR, 0); /* XXX do not touch pin directions? */ sc->sc_minipl = IPL_NONE; sc->sc_maxipl = IPL_NONE; // sc->sc_irqcookie[0] = sc->sc_irqcookie[1] = NULL; sagpio_softc = sc; } /* * This is called during bootstrap to inform us SAGPIO virtual address. */ void sa11x0_gpio_bootstrap(vaddr_t gpio_regs) { sagpio_regs = gpio_regs; } const char * sa11x0_gpio_intr_string(void *cookie) { static char irqstr[32]; struct gpio_irq_handler *gh = cookie; if (gh == NULL) snprintf(irqstr, sizeof irqstr, "couldn't establish interrupt"); else snprintf(irqstr, sizeof irqstr, "irq %ld", gh->gh_irq); return(irqstr); } int sagpio_dispatch(void *arg) { struct sagpio_softc *sc = sagpio_softc; uint32_t ip, ipbit; /* multiplexed interrupt; see what pin(s) caused it */ ip = sagpio_reg_read(sc, SAGPIO_EDR); /* check if activity has been detected on pins[11:27] */ if ((ip & 0x0ffff800) == 0) { printf("%s: dispatch: stray interrupt (GEDR=0x%8.x)\n", sc->sc_dev.dv_xname, ip); return(1); } for (ipbit = GPIO_PIN(11); ipbit < GPIO_PIN(28); ipbit <<= 1) if (ip & ipbit) { /* clear status */ sa11x0_gpio_clear_intr(ipbit); if (sc->sc_handlers[ipbit] != NULL) /* run! */ sc->sc_handlers[ipbit]->gh_func(sc->sc_handlers[ipbit]->gh_arg); else /* edge detect occured on this pin but handler is missing */ printf("%s: no registered intr handler for pin %d\n", sc->sc_dev.dv_xname, ip); } return(0); } int sagpio_irq_handler(void *arg) { struct sagpio_softc *sc = sagpio_softc; struct gpio_irq_handler *gih = arg; uint32_t ip; /* * All we need to do is to clear status bit and call real handler. */ ip = sagpio_reg_read(sc, SAGPIO_EDR); if ((ip & GPIO_PIN(gih->gh_gpio)) == 0) { printf("%s: irq_handler: stray interrupt (GEDR=0x%8.x)\n", sc->sc_dev.dv_xname, ip); return(1); } sa11x0_gpio_clear_intr(gih->gh_gpio); gih->gh_func(gih->gh_arg); return(0); } u_int sa11x0_gpio_get_function(u_int gpio) { struct sagpio_softc *sc = sagpio_softc; u_int32_t rv; if (sc != NULL) KDASSERT(gpio < sc->sc_npins); rv = sagpio_reg_read(sc, SAGPIO_AFR) & GPIO_PIN(gpio); /* 0 == GPIO, 1 == Alt. func. */ return (rv); } u_int sa11x0_gpio_set_function(u_int gpio, u_int fn) { struct sagpio_softc *sc = sagpio_softc; u_int oldfn; if (sc != NULL) KDASSERT(gpio < sc->sc_npins); oldfn = sa11x0_gpio_get_function(gpio); switch (fn) { case SAGPIO_FUNC_GPIO: sagpio_reg_write(sc, SAGPIO_AFR, oldfn & ~(GPIO_PIN(gpio))); break; case SAGPIO_FUNC_ALT: sagpio_reg_write(sc, SAGPIO_AFR, oldfn | GPIO_PIN(gpio)); break; default: panic("%s: bogus gpio function %d\n", sc->sc_dev.dv_xname, fn); /* NOTREACHED */ } return (oldfn); } /* * Quick function to read pin value */ int sa11x0_gpio_get_bit(u_int gpio) { struct sagpio_softc *sc = sagpio_softc; int bit; if (sc != NULL) KDASSERT(gpio < sc->sc_npins); bit = sagpio_reg_read(sc, SAGPIO_PLR) & GPIO_PIN(gpio); if (bit == 0) return(SAGPIO_LEVEL_LOW); else return(SAGPIO_LEVEL_HIGH); } /* * Quick function to set pin to 1 */ void sa11x0_gpio_set_bit(u_int gpio) { struct sagpio_softc *sc = sagpio_softc; sagpio_reg_write(sc, SAGPIO_PSR, GPIO_PIN(gpio)); } /* * Quick function to set pin to 0 */ void sa11x0_gpio_clear_bit(u_int gpio) { struct sagpio_softc *sc = sagpio_softc; sagpio_reg_write(sc, SAGPIO_PCR, GPIO_PIN(gpio)); } /* * Quick function to change pin direction */ void sa11x0_gpio_set_dir(u_int gpio, int dir) { struct sagpio_softc *sc = sagpio_softc; uint32_t reg; reg = sagpio_reg_read(sc, SAGPIO_PDR); switch (dir) { case SAGPIO_DIR_INPUT: reg &= ~(GPIO_PIN(gpio)); break; case SAGPIO_DIR_OUTPUT: reg |= GPIO_PIN(gpio); break; default: panic("%s: bogus pin direction %d", sc->sc_dev.dv_xname, dir); /* NOTREACHED */ } sagpio_reg_write(sc, SAGPIO_PDR, reg); } /* * Quick function to clear interrupt status on a pin * GPIO pins may be toggle in an interrupt and we dont want * extra spurious interrupts to occur. * Suppose this causes a slight race if a key is pressed while * the interrupt handler is running. (yes this is for the keyboard driver) */ void sa11x0_gpio_clear_intr(u_int gpio) { struct sagpio_softc *sc = sagpio_softc; sagpio_reg_write(sc, SAGPIO_EDR, GPIO_PIN(gpio)); } /* * Quick function to mask (disable) a GPIO interrupt */ void sa11x0_gpio_intr_mask(void *v) { struct gpio_irq_handler *gh = v; sa11x0_gpio_set_intr_level(gh->gh_gpio, IST_NONE); } /* * Quick function to unmask (enable) a GPIO interrupt */ void sa11x0_gpio_intr_unmask(void *v) { struct gpio_irq_handler *gh = v; sa11x0_gpio_set_intr_level(gh->gh_gpio, gh->gh_level); } /* * Configure the edge sensitivity of interrupt pins */ void sa11x0_gpio_set_intr_level(u_int gpio, int level) { struct sagpio_softc *sc = sagpio_softc; u_int32_t bit; u_int32_t gfer; /* falling edge */ u_int32_t grer; /* rising edge */ int s; s = splhigh(); bit = GPIO_PIN(gpio); gfer = sagpio_reg_read(sc, SAGPIO_FER); grer = sagpio_reg_read(sc, SAGPIO_RER); switch (level) { case IST_NONE: gfer &= ~bit; grer &= ~bit; break; case IST_EDGE_FALLING: gfer |= bit; grer &= ~bit; break; case IST_EDGE_RISING: gfer &= ~bit; grer |= bit; break; case IST_EDGE_BOTH: gfer |= bit; grer |= bit; break; default: panic("%s: bad level: %d", sc->sc_dev.dv_xname, level); break; } sagpio_reg_write(sc, SAGPIO_FER, gfer); sagpio_reg_write(sc, SAGPIO_RER, grer); splx(s); } void * sa11x0_gpio_intr_establish(u_int gpio, int level, int spl, int (*func)(void *), void *arg, char *name) { /* * Establish an interrupt with given gpio intr level. * XXX We do not support multiple handlers on one pin. */ struct sagpio_softc *sc = sagpio_softc; struct gpio_irq_handler *gih; void *ih; if (gpio > SAGPIO_NPINS - 1) panic("%s: intr_establish: pin %d out of range", sc->sc_dev.dv_xname, gpio); MALLOC(gih, struct gpio_irq_handler *, sizeof(struct gpio_irq_handler), M_DEVBUF, M_NOWAIT); gih->gh_func = func; gih->gh_arg = arg; gih->gh_spl = spl; gih->gh_level = level; gih->gh_gpio = gpio; gih->gh_irq = gpio < 11 ? gpio : 11; evcount_attach(&gih->gh_count, name, (void *)&gih->gh_irq, &evcount_intr); sc->sc_handlers[gpio] = gih; /* * SA11x0 INTC has 11 (intno 0-10) interrupts reserved for GPIO[0:10] respectively. * Pins [11:27] are OR'ed together and form intno 11 in an INTC. * Here we just emulate all 28 gpio interrupts (multiplexing them in 12 intnos really). */ sa11x0_gpio_set_intr_level(gpio, level); if (gih->gh_irq < 11) /* individual (intc irq = gpio pin), register common gpio handler */ ih = sa11x0_intr_establish(NULL, gih->gh_irq, 0, level, sagpio_irq_handler, gih, NULL); else /* multiplexed, register dispatcher */ ih = sa11x0_intr_establish(NULL, gih->gh_irq, 0, level, sagpio_dispatch, NULL, NULL); return(ih); }