From 3b300e1ab79c891a47fcafddeedfa13877fcd1f4 Mon Sep 17 00:00:00 2001 From: patrick Date: Sat, 6 Aug 2016 18:12:13 +0000 Subject: [PATCH] Implement interrupt controller functionality in the i.MX6 GPIO driver. This allows us to use to hook up the Ethernet interrupt on the Nitrogen6x, SabreLite and WandBoard like it's described in the device tree. ok kettenis@ --- sys/arch/armv7/imx/imxgpio.c | 211 +++++++++++++++++++++++++++++++- sys/arch/armv7/imx/imxgpiovar.h | 11 +- 2 files changed, 207 insertions(+), 15 deletions(-) diff --git a/sys/arch/armv7/imx/imxgpio.c b/sys/arch/armv7/imx/imxgpio.c index 7ace8f50680..995a4e2f542 100644 --- a/sys/arch/armv7/imx/imxgpio.c +++ b/sys/arch/armv7/imx/imxgpio.c @@ -1,4 +1,4 @@ -/* $OpenBSD: imxgpio.c,v 1.10 2016/07/27 11:45:02 patrick Exp $ */ +/* $OpenBSD: imxgpio.c,v 1.11 2016/08/06 18:12:13 patrick Exp $ */ /* * Copyright (c) 2007,2009 Dale Rahn * Copyright (c) 2012-2013 Patrick Wildt @@ -53,21 +53,25 @@ struct intrhand { void *ih_arg; /* arg for handler */ int ih_ipl; /* IPL_* */ int ih_irq; /* IRQ number */ - int ih_gpio; /* gpio pin */ + int ih_level; /* GPIO level */ struct evcount ih_count; char *ih_name; + void *ih_sc; }; struct imxgpio_softc { struct device sc_dev; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; + int sc_node; + void *sc_ih_h; void *sc_ih_l; - int sc_max_il; - int sc_min_il; + int sc_ipl; int sc_irq; struct intrhand *sc_handlers[GPIO_NUM_PINS]; + struct interrupt_controller sc_ic; + unsigned int (*sc_get_bit)(struct imxgpio_softc *sc, unsigned int gpio); void (*sc_set_bit)(struct imxgpio_softc *sc, @@ -89,6 +93,12 @@ void imxgpio_config_pin(void *, uint32_t *, int); int imxgpio_get_pin(void *, uint32_t *); void imxgpio_set_pin(void *, uint32_t *, int); +int imxgpio_intr(void *); +void *imxgpio_intr_establish(void *, int *, int, int (*)(void *), + void *, char *); +void imxgpio_intr_disestablish(void *); +void imxgpio_recalc_ipl(struct imxgpio_softc *); + unsigned int imxgpio_v6_get_bit(struct imxgpio_softc *, unsigned int); void imxgpio_v6_set_bit(struct imxgpio_softc *, unsigned int); void imxgpio_v6_clear_bit(struct imxgpio_softc *, unsigned int); @@ -121,6 +131,7 @@ imxgpio_attach(struct device *parent, struct device *self, void *aux) if (faa->fa_nreg < 1) return; + sc->sc_node = faa->fa_node; sc->sc_iot = faa->fa_iot; if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size, 0, &sc->sc_ioh)) @@ -138,9 +149,19 @@ imxgpio_attach(struct device *parent, struct device *self, void *aux) sc->sc_clear_bit = imxgpio_v6_clear_bit; sc->sc_set_dir = imxgpio_v6_set_dir; + sc->sc_ipl = IPL_NONE; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR, 0); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_ISR, ~0); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_EDGE_SEL, 0); + + sc->sc_ic.ic_node = faa->fa_node; + sc->sc_ic.ic_cookie = sc; + sc->sc_ic.ic_establish = imxgpio_intr_establish; + sc->sc_ic.ic_disestablish = imxgpio_intr_disestablish; + arm_intr_register_fdt(&sc->sc_ic); + printf("\n"); - /* XXX - IRQ */ /* XXX - SYSCONFIG */ /* XXX - CTRL */ /* XXX - DEBOUNCE */ @@ -298,3 +319,183 @@ imxgpio_v6_get_dir(struct imxgpio_softc *sc, unsigned int gpio) splx(s); return val; } + +int +imxgpio_intr(void *cookie) +{ + struct imxgpio_softc *sc = (struct imxgpio_softc *)cookie; + struct intrhand *ih; + uint32_t status, pending, mask; + int pin, s; + + status = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_ISR); + mask = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR); + + status &= mask; + pending = status; + + while (pending) { + pin = ffs(pending) - 1; + + if ((ih = sc->sc_handlers[pin]) != NULL) { + s = splraise(ih->ih_ipl); + if (ih->ih_func(ih->ih_arg)) + ih->ih_count.ec_count++; + splx(s); + } + + pending &= ~(1 << pin); + } + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_ISR, status); + + return 1; +} + +void * +imxgpio_intr_establish(void *cookie, int *cells, int ipl, + int (*func)(void *), void *arg, char *name) +{ + struct imxgpio_softc *sc = (struct imxgpio_softc *)cookie; + struct intrhand *ih; + int psw, val, reg, shift; + int irqno = cells[0]; + int level = cells[1]; + + if (irqno < 0 || irqno >= GPIO_NUM_PINS) + panic("%s: bogus irqnumber %d: %s", __func__, + irqno, name); + + if (sc->sc_handlers[irqno] != NULL) + panic("%s: irqnumber %d reused: %s", __func__, + irqno, name); + + ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK); + ih->ih_func = func; + ih->ih_arg = arg; + ih->ih_ipl = ipl; + ih->ih_irq = irqno; + ih->ih_name = name; + ih->ih_level = level; + ih->ih_sc = sc; + + psw = disable_interrupts(PSR_I); + + sc->sc_handlers[irqno] = ih; + + if (name != NULL) + evcount_attach(&ih->ih_count, name, &ih->ih_irq); + +#ifdef DEBUG_INTC + printf("%s: irq %d ipl %d [%s]\n", __func__, ih->ih_irq, ih->ih_ipl, + ih->ih_name); +#endif + + imxgpio_recalc_ipl(sc); + + switch (level) { + case 1: /* rising */ + val = 2; + break; + case 2: /* falling */ + val = 3; + break; + case 4: /* high */ + val = 1; + break; + case 8: /* low */ + val = 0; + break; + default: + panic("%s: unsupported trigger type", __func__); + } + + if (irqno < 16) { + reg = GPIO_ICR1; + shift = irqno << 1; + } else { + reg = GPIO_ICR2; + shift = (irqno - 16) << 1; + } + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, reg, + bus_space_read_4(sc->sc_iot, sc->sc_ioh, reg) & ~(0x3 << shift)); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, reg, + bus_space_read_4(sc->sc_iot, sc->sc_ioh, reg) | val << shift); + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR, + bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR) | 1 << irqno); + + restore_interrupts(psw); + return (ih); +} + +void +imxgpio_intr_disestablish(void *cookie) +{ + struct intrhand *ih = cookie; + struct imxgpio_softc *sc = ih->ih_sc; + uint32_t mask; + int psw; + + psw = disable_interrupts(PSR_I); + +#ifdef DEBUG_INTC + printf("%s: irq %d ipl %d [%s]\n", __func__, ih->ih_irq, ih->ih_ipl, + ih->ih_name); +#endif + + mask = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR); + mask &= ~(1 << ih->ih_irq); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR, mask); + + sc->sc_handlers[ih->ih_irq] = NULL; + if (ih->ih_name != NULL) + evcount_detach(&ih->ih_count); + free(ih, M_DEVBUF, sizeof(*ih)); + + imxgpio_recalc_ipl(sc); + + restore_interrupts(psw); +} + +void +imxgpio_recalc_ipl(struct imxgpio_softc *sc) +{ + struct intrhand *ih; + int pin; + int max = IPL_NONE; + int min = IPL_HIGH; + + for (pin = 0; pin < GPIO_NUM_PINS; pin++) { + ih = sc->sc_handlers[pin]; + if (ih == NULL) + continue; + + if (ih->ih_ipl > max) + max = ih->ih_ipl; + + if (ih->ih_ipl < min) + min = ih->ih_ipl; + } + + if (max == IPL_NONE) + min = IPL_NONE; + + if (sc->sc_ipl != max) { + sc->sc_ipl = max; + + if (sc->sc_ih_l != NULL) + arm_intr_disestablish_fdt(sc->sc_ih_l); + + if (sc->sc_ih_h != NULL) + arm_intr_disestablish_fdt(sc->sc_ih_h); + + if (sc->sc_ipl != IPL_NONE) { + sc->sc_ih_l = arm_intr_establish_fdt_idx(sc->sc_node, 0, + sc->sc_ipl, imxgpio_intr, sc, sc->sc_dev.dv_xname); + sc->sc_ih_h = arm_intr_establish_fdt_idx(sc->sc_node, 1, + sc->sc_ipl, imxgpio_intr, sc, sc->sc_dev.dv_xname); + } + } +} diff --git a/sys/arch/armv7/imx/imxgpiovar.h b/sys/arch/armv7/imx/imxgpiovar.h index 1451624c58c..b9b59aab56c 100644 --- a/sys/arch/armv7/imx/imxgpiovar.h +++ b/sys/arch/armv7/imx/imxgpiovar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: imxgpiovar.h,v 1.1 2013/09/06 20:45:53 patrick Exp $ */ +/* $OpenBSD: imxgpiovar.h,v 1.2 2016/08/06 18:12:13 patrick Exp $ */ /* * Copyright (c) 2007,2009 Dale Rahn * Copyright (c) 2012-2013 Patrick Wildt @@ -29,13 +29,4 @@ void imxgpio_set_bit(unsigned int gpio); void imxgpio_clear_bit(unsigned int gpio); void imxgpio_set_dir(unsigned int gpio, unsigned int dir); -/* interrupts */ -void imxgpio_clear_intr(unsigned int gpio); -void imxgpio_intr_mask(unsigned int gpio); -void imxgpio_intr_unmask(unsigned int gpio); -void imxgpio_intr_level(unsigned int gpio, unsigned int level); -void *imxgpio_intr_establish(unsigned int gpio, int level, int spl, - int (*func)(void *), void *arg, char *name); -void imxgpio_intr_disestablish(void *cookie); - #endif /* IMXGPIOVAR_H */ -- 2.20.1