Index: head/sys/arm/broadcom/bcm2835/bcm2835_gpio.c =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_gpio.c +++ head/sys/arm/broadcom/bcm2835/bcm2835_gpio.c @@ -28,6 +28,8 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_platform.h" + #include #include #include @@ -37,10 +39,12 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -49,6 +53,10 @@ #include "gpio_if.h" +#ifdef ARM_INTRNG +#include "pic_if.h" +#endif + #ifdef DEBUG #define dprintf(fmt, args...) do { printf("%s(): ", __func__); \ printf(fmt,##args); } while (0) @@ -64,10 +72,10 @@ static struct resource_spec bcm_gpio_res_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, - { SYS_RES_IRQ, 0, RF_ACTIVE }, - { SYS_RES_IRQ, 1, RF_ACTIVE }, - { SYS_RES_IRQ, 2, RF_ACTIVE }, - { SYS_RES_IRQ, 3, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, /* bank 0 interrupt */ + { SYS_RES_IRQ, 1, RF_ACTIVE }, /* bank 1 interrupt */ + { SYS_RES_IRQ, 2, RF_ACTIVE }, /* bank 1 interrupt (mirrored) */ + { SYS_RES_IRQ, 3, RF_ACTIVE }, /* bank 0-1 interrupt (united) */ { -1, 0, 0 } }; @@ -76,6 +84,15 @@ uint32_t pin; }; +#ifdef ARM_INTRNG +struct bcm_gpio_irqsrc { + struct intr_irqsrc bgi_isrc; + uint32_t bgi_irq; + uint32_t bgi_reg; + uint32_t bgi_mask; +}; +#endif + struct bcm_gpio_softc { device_t sc_dev; device_t sc_busdev; @@ -88,10 +105,16 @@ int sc_ro_npins; int sc_ro_pins[BCM_GPIO_PINS]; struct gpio_pin sc_gpio_pins[BCM_GPIO_PINS]; +#ifndef ARM_INTRNG struct intr_event * sc_events[BCM_GPIO_PINS]; +#endif struct bcm_gpio_sysctl sc_sysctl[BCM_GPIO_PINS]; +#ifdef ARM_INTRNG + struct bcm_gpio_irqsrc sc_isrcs[BCM_GPIO_PINS]; +#else enum intr_trigger sc_irq_trigger[BCM_GPIO_PINS]; enum intr_polarity sc_irq_polarity[BCM_GPIO_PINS]; +#endif }; enum bcm_gpio_pud { @@ -130,6 +153,13 @@ static struct bcm_gpio_softc *bcm_gpio_sc = NULL; +#ifdef ARM_INTRNG +static int bcm_gpio_intr_bank0(void *arg); +static int bcm_gpio_intr_bank1(void *arg); +static int bcm_gpio_pic_attach(struct bcm_gpio_softc *sc); +static int bcm_gpio_pic_detach(struct bcm_gpio_softc *sc); +#endif + static int bcm_gpio_pin_is_ro(struct bcm_gpio_softc *sc, int pin) { @@ -661,6 +691,7 @@ return (0); } +#ifndef ARM_INTRNG static int bcm_gpio_intr(void *arg) { @@ -694,6 +725,7 @@ return (FILTER_HANDLED); } +#endif static int bcm_gpio_probe(device_t dev) @@ -709,6 +741,49 @@ return (BUS_PROBE_DEFAULT); } +#ifdef ARM_INTRNG +static int +bcm_gpio_intr_attach(device_t dev) +{ + struct bcm_gpio_softc *sc; + + /* + * Only first two interrupt lines are used. Third line is + * mirrored second line and forth line is common for all banks. + */ + sc = device_get_softc(dev); + if (sc->sc_res[1] == NULL || sc->sc_res[2] == NULL) + return (-1); + + if (bcm_gpio_pic_attach(sc) != 0) { + device_printf(dev, "unable to attach PIC\n"); + return (-1); + } + if (bus_setup_intr(dev, sc->sc_res[1], INTR_TYPE_MISC | INTR_MPSAFE, + bcm_gpio_intr_bank0, NULL, sc, &sc->sc_intrhand[0]) != 0) + return (-1); + if (bus_setup_intr(dev, sc->sc_res[2], INTR_TYPE_MISC | INTR_MPSAFE, + bcm_gpio_intr_bank1, NULL, sc, &sc->sc_intrhand[1]) != 0) + return (-1); + + return (0); +} + +static void +bcm_gpio_intr_detach(device_t dev) +{ + struct bcm_gpio_softc *sc; + + sc = device_get_softc(dev); + if (sc->sc_intrhand[0] != NULL) + bus_teardown_intr(dev, sc->sc_res[1], sc->sc_intrhand[0]); + if (sc->sc_intrhand[1] != NULL) + bus_teardown_intr(dev, sc->sc_res[2], sc->sc_intrhand[1]); + + bcm_gpio_pic_detach(sc); +} + +#else static int bcm_gpio_intr_attach(device_t dev) { @@ -741,6 +816,7 @@ } } } +#endif static int bcm_gpio_attach(device_t dev) @@ -786,9 +862,11 @@ sc->sc_gpio_pins[i].gp_pin = j; sc->sc_gpio_pins[i].gp_caps = BCM_GPIO_DEFAULT_CAPS; sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(func); +#ifndef ARM_INTRNG /* The default is active-low interrupts. */ sc->sc_irq_trigger[i] = INTR_TRIGGER_LEVEL; sc->sc_irq_polarity[i] = INTR_POLARITY_LOW; +#endif i++; } sc->sc_gpio_npins = i; @@ -814,6 +892,289 @@ return (EBUSY); } +#ifdef ARM_INTRNG +static inline void +bcm_gpio_isrc_eoi(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi) +{ + uint32_t bank; + + /* Write 1 to clear. */ + bank = BCM_GPIO_BANK(bgi->bgi_irq); + BCM_GPIO_WRITE(sc, BCM_GPIO_GPEDS(bank), bgi->bgi_mask); +} + +static inline bool +bcm_gpio_isrc_is_level(struct bcm_gpio_irqsrc *bgi) +{ + uint32_t bank; + + bank = BCM_GPIO_BANK(bgi->bgi_irq); + return (bgi->bgi_reg == BCM_GPIO_GPHEN(bank) || + bgi->bgi_reg == BCM_GPIO_GPLEN(bank)); +} + +static inline void +bcm_gpio_isrc_mask(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi) +{ + + BCM_GPIO_LOCK(sc); + BCM_GPIO_CLEAR_BITS(sc, bgi->bgi_reg, bgi->bgi_mask); + BCM_GPIO_UNLOCK(bcm_gpio_sc); +} + +static inline void +bcm_gpio_isrc_unmask(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi) +{ + + BCM_GPIO_LOCK(sc); + BCM_GPIO_SET_BITS(sc, bgi->bgi_reg, bgi->bgi_mask); + BCM_GPIO_UNLOCK(sc); +} + +static int +bcm_gpio_intr_internal(struct bcm_gpio_softc *sc, uint32_t bank) +{ + u_int irq; + struct bcm_gpio_irqsrc *bgi; + uint32_t reg; + + /* Do not care of spurious interrupt on GPIO. */ + reg = BCM_GPIO_READ(sc, BCM_GPIO_GPEDS(bank)); + while (reg != 0) { + irq = BCM_GPIO_PINS_PER_BANK * bank + ffs(reg) - 1; + bgi = sc->sc_isrcs + irq; + if (!bcm_gpio_isrc_is_level(bgi)) + bcm_gpio_isrc_eoi(sc, bgi); + if (intr_isrc_dispatch(&bgi->bgi_isrc, + curthread->td_intr_frame) != 0) { + bcm_gpio_isrc_mask(sc, bgi); + if (bcm_gpio_isrc_is_level(bgi)) + bcm_gpio_isrc_eoi(sc, bgi); + device_printf(sc->sc_dev, "Stray irq %u disabled\n", + irq); + } + reg &= ~bgi->bgi_mask; + } + return (FILTER_HANDLED); +} + +static int +bcm_gpio_intr_bank0(void *arg) +{ + + return (bcm_gpio_intr_internal(arg, 0)); +} + +static int +bcm_gpio_intr_bank1(void *arg) +{ + + return (bcm_gpio_intr_internal(arg, 1)); +} + +static int +bcm_gpio_pic_attach(struct bcm_gpio_softc *sc) +{ + int error; + uint32_t irq; + const char *name; + + name = device_get_nameunit(sc->sc_dev); + for (irq = 0; irq < BCM_GPIO_PINS; irq++) { + sc->sc_isrcs[irq].bgi_irq = irq; + sc->sc_isrcs[irq].bgi_mask = BCM_GPIO_MASK(irq); + sc->sc_isrcs[irq].bgi_reg = 0; + + error = intr_isrc_register(&sc->sc_isrcs[irq].bgi_isrc, + sc->sc_dev, 0, "%s,%u", name, irq); + if (error != 0) + return (error); /* XXX deregister ISRCs */ + } + return (intr_pic_register(sc->sc_dev, + OF_xref_from_node(ofw_bus_get_node(sc->sc_dev)))); +} + +static int +bcm_gpio_pic_detach(struct bcm_gpio_softc *sc) +{ + + /* + * There has not been established any procedure yet + * how to detach PIC from living system correctly. + */ + device_printf(sc->sc_dev, "%s: not implemented yet\n", __func__); + return (EBUSY); +} + +static void +bcm_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct bcm_gpio_softc *sc = device_get_softc(dev); + struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; + + bcm_gpio_isrc_mask(sc, bgi); +} + +static void +bcm_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct bcm_gpio_softc *sc = device_get_softc(dev); + struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; + + arm_irq_memory_barrier(bgi->bgi_irq); + bcm_gpio_isrc_unmask(sc, bgi); +} + +static int +bcm_gpio_pic_map_fdt(struct bcm_gpio_softc *sc, u_int ncells, pcell_t *cells, + u_int *irqp, uint32_t *regp) +{ + u_int irq; + uint32_t reg, bank; + + /* + * The first cell is the interrupt number. + * The second cell is used to specify flags: + * bits[3:0] trigger type and level flags: + * 1 = low-to-high edge triggered. + * 2 = high-to-low edge triggered. + * 4 = active high level-sensitive. + * 8 = active low level-sensitive. + */ + if (ncells != 2) + return (EINVAL); + + irq = cells[0]; + if (irq >= BCM_GPIO_PINS || bcm_gpio_pin_is_ro(sc, irq)) + return (EINVAL); + + /* + * All interrupt types could be set for an interrupt at one moment. + * At least, the combination of 'low-to-high' and 'high-to-low' edge + * triggered interrupt types can make a sense. However, no combo is + * supported now. + */ + bank = BCM_GPIO_BANK(irq); + if (cells[1] == 1) + reg = BCM_GPIO_GPREN(bank); + else if (cells[1] == 2) + reg = BCM_GPIO_GPFEN(bank); + else if (cells[1] == 4) + reg = BCM_GPIO_GPHEN(bank); + else if (cells[1] == 8) + reg = BCM_GPIO_GPLEN(bank); + else + return (EINVAL); + + *irqp = irq; + if (regp != NULL) + *regp = reg; + return (0); +} + +static int +bcm_gpio_pic_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + int error; + u_int irq; + struct bcm_gpio_softc *sc; + + if (data->type != INTR_MAP_DATA_FDT) + return (ENOTSUP); + + sc = device_get_softc(dev); + error = bcm_gpio_pic_map_fdt(sc, data->fdt.ncells, data->fdt.cells, + &irq, NULL); + if (error == 0) + *isrcp = &sc->sc_isrcs[irq].bgi_isrc; + return (error); +} + +static void +bcm_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ + struct bcm_gpio_softc *sc = device_get_softc(dev); + struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; + + if (bcm_gpio_isrc_is_level(bgi)) + bcm_gpio_isrc_eoi(sc, bgi); +} + +static void +bcm_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + + bcm_gpio_pic_enable_intr(dev, isrc); +} + +static void +bcm_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct bcm_gpio_softc *sc = device_get_softc(dev); + struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; + + bcm_gpio_isrc_mask(sc, bgi); + if (bcm_gpio_isrc_is_level(bgi)) + bcm_gpio_isrc_eoi(sc, bgi); +} + +static int +bcm_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + u_int irq; + uint32_t bank, reg; + struct bcm_gpio_softc *sc; + struct bcm_gpio_irqsrc *bgi; + + if (data == NULL || data->type != INTR_MAP_DATA_FDT) + return (ENOTSUP); + + sc = device_get_softc(dev); + bgi = (struct bcm_gpio_irqsrc *)isrc; + + /* Get and check config for an interrupt. */ + if (bcm_gpio_pic_map_fdt(sc, data->fdt.ncells, data->fdt.cells, &irq, + ®) != 0 || bgi->bgi_irq != irq) + return (EINVAL); + + /* + * If this is a setup for another handler, + * only check that its configuration match. + */ + if (isrc->isrc_handlers != 0) + return (bgi->bgi_reg == reg ? 0 : EINVAL); + + bank = BCM_GPIO_BANK(irq); + BCM_GPIO_LOCK(sc); + BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask); + BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask); + BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPHEN(bank), bgi->bgi_mask); + BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPLEN(bank), bgi->bgi_mask); + bgi->bgi_reg = reg; + BCM_GPIO_SET_BITS(sc, reg, bgi->bgi_mask); + BCM_GPIO_UNLOCK(sc); + return (0); +} + +static int +bcm_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct bcm_gpio_softc *sc = device_get_softc(dev); + struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; + + if (isrc->isrc_handlers == 0) { + BCM_GPIO_LOCK(sc); + BCM_GPIO_CLEAR_BITS(sc, bgi->bgi_reg, bgi->bgi_mask); + bgi->bgi_reg = 0; + BCM_GPIO_UNLOCK(sc); + } + return (0); +} + +#else static uint32_t bcm_gpio_intr_reg(struct bcm_gpio_softc *sc, unsigned int irq, uint32_t bank) { @@ -984,6 +1345,7 @@ return (err); } +#endif static phandle_t bcm_gpio_get_node(device_t bus, device_t dev) @@ -1010,13 +1372,24 @@ DEVMETHOD(gpio_pin_set, bcm_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, bcm_gpio_pin_toggle), +#ifdef ARM_INTRNG + /* Interrupt controller interface */ + DEVMETHOD(pic_disable_intr, bcm_gpio_pic_disable_intr), + DEVMETHOD(pic_enable_intr, bcm_gpio_pic_enable_intr), + DEVMETHOD(pic_map_intr, bcm_gpio_pic_map_intr), + DEVMETHOD(pic_post_filter, bcm_gpio_pic_post_filter), + DEVMETHOD(pic_post_ithread, bcm_gpio_pic_post_ithread), + DEVMETHOD(pic_pre_ithread, bcm_gpio_pic_pre_ithread), + DEVMETHOD(pic_setup_intr, bcm_gpio_pic_setup_intr), + DEVMETHOD(pic_teardown_intr, bcm_gpio_pic_teardown_intr), +#else /* Bus interface */ DEVMETHOD(bus_activate_resource, bcm_gpio_activate_resource), DEVMETHOD(bus_deactivate_resource, bcm_gpio_deactivate_resource), DEVMETHOD(bus_config_intr, bcm_gpio_config_intr), DEVMETHOD(bus_setup_intr, bcm_gpio_setup_intr), DEVMETHOD(bus_teardown_intr, bcm_gpio_teardown_intr), - +#endif /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, bcm_gpio_get_node), Index: head/sys/boot/fdt/dts/arm/bcm2835.dtsi =================================================================== --- head/sys/boot/fdt/dts/arm/bcm2835.dtsi +++ head/sys/boot/fdt/dts/arm/bcm2835.dtsi @@ -144,7 +144,7 @@ #gpio-cells = <2>; interrupt-controller; - #interrupt-cells = <1>; + #interrupt-cells = <2>; pinctrl-names = "default"; pinctrl-0 = <&pins_reserved>; Index: head/sys/boot/fdt/dts/arm/bcm2836.dtsi =================================================================== --- head/sys/boot/fdt/dts/arm/bcm2836.dtsi +++ head/sys/boot/fdt/dts/arm/bcm2836.dtsi @@ -137,7 +137,7 @@ #gpio-cells = <2>; interrupt-controller; - #interrupt-cells = <1>; + #interrupt-cells = <2>; pinctrl-names = "default"; pinctrl-0 = <&pins_reserved>;