--- rk_gpio.c.original 2021-03-19 09:42:31.334213000 +0100 +++ rk_gpio.c 2021-02-21 18:56:47.274703000 +0100 @@ -2,6 +2,7 @@ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2018 Emmanuel Vadot + * Copyright (c) 2021 Soren Schmidt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -35,6 +36,7 @@ #include #include +#include #include #include #include @@ -50,38 +52,43 @@ #include #include "gpio_if.h" +#include "pic_if.h" -#include "fdt_pinctrl_if.h" - #define RK_GPIO_SWPORTA_DR 0x00 /* Data register */ #define RK_GPIO_SWPORTA_DDR 0x04 /* Data direction register */ - #define RK_GPIO_INTEN 0x30 /* Interrupt enable register */ #define RK_GPIO_INTMASK 0x34 /* Interrupt mask register */ #define RK_GPIO_INTTYPE_LEVEL 0x38 /* Interrupt level register */ #define RK_GPIO_INT_POLARITY 0x3C /* Interrupt polarity register */ #define RK_GPIO_INT_STATUS 0x40 /* Interrupt status register */ #define RK_GPIO_INT_RAWSTATUS 0x44 /* Raw Interrupt status register */ - #define RK_GPIO_DEBOUNCE 0x48 /* Debounce enable register */ - #define RK_GPIO_PORTA_EOI 0x4C /* Clear interrupt register */ #define RK_GPIO_EXT_PORTA 0x50 /* External port register */ +#define RK_GPIO_LS_SYNC 0x60 /* Level sensitive syncronization */ -#define RK_GPIO_LS_SYNC 0x60 /* Level sensitive syncronization enable register */ +#define RK_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ + GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | \ + GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING|\ + GPIO_INTR_LEVEL_HIGH | GPIO_INTR_LEVEL_LOW) -#define RK_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ - GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN) +#define RK_NUM_GPIO 32 +struct rk_pin_irqsrc { + struct intr_irqsrc isrc; + uint32_t irq; + uint32_t mode; +}; + struct rk_gpio_softc { - device_t sc_dev; - device_t sc_busdev; - struct mtx sc_mtx; - struct resource *sc_res[2]; - bus_space_tag_t sc_bst; - bus_space_handle_t sc_bsh; + device_t dev; + device_t busdev; + struct mtx mtx; + struct resource *res[2]; + void *ihandle; clk_t clk; device_t pinctrl; + struct rk_pin_irqsrc sc_isrcs[RK_NUM_GPIO]; }; static struct ofw_compat_data compat_data[] = { @@ -97,15 +104,19 @@ static int rk_gpio_detach(device_t dev); -#define RK_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx) -#define RK_GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx) -#define RK_GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) +#define RK_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->mtx) +#define RK_GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->mtx) +#define RK_GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->mtx, MA_OWNED) -#define RK_GPIO_WRITE(_sc, _off, _val) \ - bus_space_write_4(_sc->sc_bst, _sc->sc_bsh, _off, _val) -#define RK_GPIO_READ(_sc, _off) \ - bus_space_read_4(_sc->sc_bst, _sc->sc_bsh, _off) +#define RK_GPIO_READ(_sc, _o) bus_read_4(sc->res[0], _o) +#define RK_GPIO_WRITE(_sc, _o, _v) bus_write_4(sc->res[0], _o, _v) +#define RK_GPIO_MASK_SET(_sc, _o, _m, _s) \ + bus_write_4(sc->res[0], _o, \ + (bus_read_4(sc->res[0], _o) & ~_m) | _s) +#define RK_PIC_INTR_ISRC(sc, irq) (&(sc->sc_isrcs[irq].isrc)) + + static int rk_gpio_probe(device_t dev) { @@ -121,47 +132,99 @@ } static int +rk_intr(void *arg) +{ + struct rk_gpio_softc *sc; + struct trapframe *tf; + uint32_t status; + int pin; + + sc = (struct rk_gpio_softc *)arg; + tf = curthread->td_intr_frame; + + /* read and clear interrupt */ + status = RK_GPIO_READ(sc, RK_GPIO_INT_STATUS); + RK_GPIO_WRITE(sc, RK_GPIO_PORTA_EOI, status); + + while (status != 0) { + pin = ffs(status) - 1; + status &= ~(1 << pin); + + if (intr_isrc_dispatch(RK_PIC_INTR_ISRC(sc, pin), tf) != 0) + device_printf(sc->dev, "spurious interrupt %d\n", pin); + } + return (FILTER_HANDLED); +} + +static int rk_gpio_attach(device_t dev) { struct rk_gpio_softc *sc; phandle_t node; + const char *name; + int irq; int err; sc = device_get_softc(dev); - sc->sc_dev = dev; + sc->dev = dev; sc->pinctrl = device_get_parent(dev); - node = ofw_bus_get_node(sc->sc_dev); + node = ofw_bus_get_node(sc->dev); if (!OF_hasprop(node, "gpio-controller")) return (ENXIO); - mtx_init(&sc->sc_mtx, "rk gpio", "gpio", MTX_SPIN); + mtx_init(&sc->mtx, "rk gpio", "gpio", MTX_SPIN); - if (bus_alloc_resources(dev, rk_gpio_spec, sc->sc_res)) { + if (bus_alloc_resources(dev, rk_gpio_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); - bus_release_resources(dev, rk_gpio_spec, sc->sc_res); - mtx_destroy(&sc->sc_mtx); + bus_release_resources(dev, rk_gpio_spec, sc->res); + mtx_destroy(&sc->mtx); return (ENXIO); } - sc->sc_bst = rman_get_bustag(sc->sc_res[0]); - sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]); - if (clk_get_by_ofw_index(dev, 0, 0, &sc->clk) != 0) { device_printf(dev, "Cannot get clock\n"); rk_gpio_detach(dev); return (ENXIO); } err = clk_enable(sc->clk); - if (err != 0) { + if (err) { device_printf(dev, "Could not enable clock %s\n", clk_get_name(sc->clk)); rk_gpio_detach(dev); return (ENXIO); } - sc->sc_busdev = gpiobus_attach_bus(dev); - if (sc->sc_busdev == NULL) { + err = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, + rk_intr, NULL, sc, &sc->ihandle); + if (err) { + device_printf(dev, "%s: can't setup IRQ\n", __func__); + rk_gpio_detach(dev); + return (ENXIO); + } + + name = device_get_nameunit(dev); + + for (irq = 0; irq < RK_NUM_GPIO; irq++) { + if (bootverbose) { + device_printf(dev, + "trying to register pin %d name %s\n", irq, name); + } + sc->sc_isrcs[irq].irq = irq; + sc->sc_isrcs[irq].mode = GPIO_INTR_CONFORM; + err = intr_isrc_register(RK_PIC_INTR_ISRC(sc, irq), dev, 0, + "%s", name); + if (err) { + device_printf(dev, "can't register isrc %d\n", err); + rk_gpio_detach(dev); + return (ENXIO); + } + } + if (intr_pic_register(dev, OF_xref_from_node(node)) == NULL) + return (ENXIO); + + sc->busdev = gpiobus_attach_bus(dev); + if (sc->busdev == NULL) { rk_gpio_detach(dev); return (ENXIO); } @@ -176,10 +239,10 @@ sc = device_get_softc(dev); - if (sc->sc_busdev) + if (sc->busdev) gpiobus_detach_bus(dev); - bus_release_resources(dev, rk_gpio_spec, sc->sc_res); - mtx_destroy(&sc->sc_mtx); + bus_release_resources(dev, rk_gpio_spec, sc->res); + mtx_destroy(&sc->mtx); clk_disable(sc->clk); return(0); @@ -192,16 +255,14 @@ sc = device_get_softc(dev); - return (sc->sc_busdev); + return (sc->busdev); } static int rk_gpio_pin_max(device_t dev, int *maxpin) { + *maxpin = RK_NUM_GPIO - 1; - /* Each bank have always 32 pins */ - /* XXX not true*/ - *maxpin = 31; return (0); } @@ -210,11 +271,11 @@ { struct rk_gpio_softc *sc; - sc = device_get_softc(dev); - - if (pin >= 32) + if (pin >= RK_NUM_GPIO) return (EINVAL); + sc = device_get_softc(dev); + RK_GPIO_LOCK(sc); snprintf(name, GPIOMAXNAME, "gpio%d", pin); RK_GPIO_UNLOCK(sc); @@ -227,21 +288,13 @@ { struct rk_gpio_softc *sc; uint32_t reg; - int rv; - bool is_gpio; - sc = device_get_softc(dev); - - rv = FDT_PINCTRL_IS_GPIO(sc->pinctrl, dev, pin, &is_gpio); - if (rv != 0) - return (rv); - if (!is_gpio) + if (pin >= RK_NUM_GPIO) return (EINVAL); + sc = device_get_softc(dev); + *flags = 0; - rv = FDT_PINCTRL_GET_FLAGS(sc->pinctrl, dev, pin, flags); - if (rv != 0) - return (rv); RK_GPIO_LOCK(sc); reg = RK_GPIO_READ(sc, RK_GPIO_SWPORTA_DDR); @@ -258,8 +311,11 @@ static int rk_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { + if (pin >= RK_NUM_GPIO) + return (EINVAL); *caps = RK_GPIO_DEFAULT_CAPS; + return (0); } @@ -268,24 +324,16 @@ { struct rk_gpio_softc *sc; uint32_t reg; - int rv; - bool is_gpio; - sc = device_get_softc(dev); - - rv = FDT_PINCTRL_IS_GPIO(sc->pinctrl, dev, pin, &is_gpio); - if (rv != 0) - return (rv); - if (!is_gpio) + if (pin >= RK_NUM_GPIO) return (EINVAL); - rv = FDT_PINCTRL_SET_FLAGS(sc->pinctrl, dev, pin, flags); - if (rv != 0) - return (rv); + sc = device_get_softc(dev); RK_GPIO_LOCK(sc); reg = RK_GPIO_READ(sc, RK_GPIO_SWPORTA_DDR); + if (flags & GPIO_PIN_INPUT) reg &= ~(1 << pin); else if (flags & GPIO_PIN_OUTPUT) @@ -385,11 +433,11 @@ uint32_t reg, set, mask, flags; int i; - sc = device_get_softc(dev); - - if (first_pin != 0 || num_pins > 32) + if (first_pin != 0 || num_pins > RK_NUM_GPIO) return (EINVAL); + sc = device_get_softc(dev); + set = 0; mask = 0; for (i = 0; i < num_pins; i++) { @@ -420,17 +468,173 @@ /* The gpios are mapped as */ *pin = gpios[0]; *flags = gpios[1]; + return (0); } static phandle_t rk_gpio_get_node(device_t bus, device_t dev) { - /* We only have one child, the GPIO bus, which needs our own node. */ return (ofw_bus_get_node(bus)); } +#if 0 // unused +static void +rk_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct rk_gpio_softc *sc; + uint32_t mask; + + sc = device_get_softc(dev); + + mask = 1 << ((struct rk_pin_irqsrc *)isrc)->irq; + RK_GPIO_LOCK(sc); + RK_GPIO_MASK_SET(sc, RK_GPIO_INTEN, mask, 0); + RK_GPIO_MASK_SET(sc, RK_GPIO_INTMASK, mask, 0); + RK_GPIO_MASK_SET(sc, RK_GPIO_DEBOUNCE, mask, 0); + RK_GPIO_UNLOCK(sc); +} + +static void +rk_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct rk_gpio_softc *sc; + uint32_t mask; + + sc = device_get_softc(dev); + + mask = 1 << ((struct rk_pin_irqsrc *)isrc)->irq; + + RK_GPIO_LOCK(sc); + RK_GPIO_MASK_SET(sc, RK_GPIO_DEBOUNCE, mask, mask); + RK_GPIO_MASK_SET(sc, RK_GPIO_INTMASK, mask, 0); + RK_GPIO_MASK_SET(sc, RK_GPIO_INTEN, mask, mask); + RK_GPIO_UNLOCK(sc); +} +#endif + +static int +rk_pic_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + struct rk_gpio_softc *sc; + struct intr_map_data_gpio *gdata; + uint32_t irq; + + sc = device_get_softc(dev); + if (data->type != INTR_MAP_DATA_GPIO) { + device_printf(dev, "%s: wrong type\n", __func__); + return (ENOTSUP); + } + + gdata = (struct intr_map_data_gpio *)data; + irq = gdata->gpio_pin_num; + if (irq >= RK_NUM_GPIO) { + device_printf(dev, "%s: invalid interrupt %u\n", __func__, irq); + return (EINVAL); + } + + *isrcp = RK_PIC_INTR_ISRC(sc, irq); + + return 0; +} + +static int +rk_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct rk_gpio_softc *sc; + struct rk_pin_irqsrc *rkisrc; + struct intr_map_data_gpio *gdata; + uint32_t mode; + uint8_t mask; + + if (!data) { + device_printf(dev, "%s: no map data\n", __func__); + return (ENOTSUP); + } + + sc = device_get_softc(dev); + gdata = (struct intr_map_data_gpio *)data; + rkisrc = (struct rk_pin_irqsrc *)isrc; + + mode = gdata->gpio_intr_mode; + mask = 1 << gdata->gpio_pin_num; + + if (rkisrc->irq != gdata->gpio_pin_num) { + device_printf(dev, "%s: interrupts don't match\n", __func__); + return (EINVAL); + } + + if (isrc->isrc_handlers != 0) { + device_printf(dev, "%s: handler already attached\n", __func__); + return (rkisrc->mode == mode ? 0 : EINVAL); + } + rkisrc->mode = mode; + + RK_GPIO_LOCK(sc); + + switch (mode & GPIO_INTR_MASK) { + case GPIO_INTR_EDGE_RISING: + RK_GPIO_MASK_SET(sc, RK_GPIO_SWPORTA_DDR, mask, 0); + RK_GPIO_MASK_SET(sc, RK_GPIO_INTTYPE_LEVEL, mask, mask); + RK_GPIO_MASK_SET(sc, RK_GPIO_INT_POLARITY, mask, 1); + break; + case GPIO_INTR_EDGE_FALLING: + RK_GPIO_MASK_SET(sc, RK_GPIO_SWPORTA_DDR, mask, 0); + RK_GPIO_MASK_SET(sc, RK_GPIO_INTTYPE_LEVEL, mask, mask); + RK_GPIO_MASK_SET(sc, RK_GPIO_INT_POLARITY, mask, 0); + break; + case GPIO_INTR_LEVEL_HIGH: + RK_GPIO_MASK_SET(sc, RK_GPIO_SWPORTA_DDR, mask, 0); + RK_GPIO_MASK_SET(sc, RK_GPIO_INTTYPE_LEVEL, mask, 0); + RK_GPIO_MASK_SET(sc, RK_GPIO_INT_POLARITY, mask, 1); + break; + case GPIO_INTR_LEVEL_LOW: + RK_GPIO_MASK_SET(sc, RK_GPIO_SWPORTA_DDR, mask, 0); + RK_GPIO_MASK_SET(sc, RK_GPIO_INTTYPE_LEVEL, mask, 0); + RK_GPIO_MASK_SET(sc, RK_GPIO_INT_POLARITY, mask, 0); + break; + default: + RK_GPIO_MASK_SET(sc, RK_GPIO_INTMASK, mask, mask); + RK_GPIO_MASK_SET(sc, RK_GPIO_INTEN, mask, 0); + RK_GPIO_UNLOCK(sc); + return (EINVAL); + } + RK_GPIO_MASK_SET(sc, RK_GPIO_DEBOUNCE, mask, mask); + RK_GPIO_MASK_SET(sc, RK_GPIO_INTMASK, mask, 0); + RK_GPIO_MASK_SET(sc, RK_GPIO_INTEN, mask, mask); + + RK_GPIO_UNLOCK(sc); + + return (0); +} + +static int +rk_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct rk_gpio_softc *sc; + struct rk_pin_irqsrc *irqsrc; + uint8_t mask; + + irqsrc = (struct rk_pin_irqsrc *)isrc; + mask = 1 << irqsrc->irq; + + sc = device_get_softc(dev); + if (isrc->isrc_handlers == 0) { + irqsrc->mode = GPIO_INTR_CONFORM; + RK_GPIO_LOCK(sc); + RK_GPIO_MASK_SET(sc, RK_GPIO_INTEN, mask, 0); + RK_GPIO_MASK_SET(sc, RK_GPIO_INTMASK, mask, 0); + RK_GPIO_MASK_SET(sc, RK_GPIO_DEBOUNCE, mask, 0); + RK_GPIO_UNLOCK(sc); + } + return (0); +} + + static device_method_t rk_gpio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rk_gpio_probe), @@ -451,7 +655,17 @@ DEVMETHOD(gpio_pin_config_32, rk_gpio_pin_config_32), DEVMETHOD(gpio_map_gpios, rk_gpio_map_gpios), - /* ofw_bus interface */ + /* Interrupt controller interface */ + DEVMETHOD(pic_map_intr, rk_pic_map_intr), + DEVMETHOD(pic_setup_intr, rk_pic_setup_intr), + DEVMETHOD(pic_teardown_intr, rk_pic_teardown_intr), + //DEVMETHOD(pic_disable_intr, rk_pic_disable_intr), + //DEVMETHOD(pic_enable_intr, rk_pic_enable_intr), + //DEVMETHOD(pic_post_filter, rk_pic_post_filter), + //DEVMETHOD(pic_post_ithread, rk_pic_post_ithread), + //DEVMETHOD(pic_pre_ithread, rk_pic_pre_ithread), + + /* OFW bus interface */ DEVMETHOD(ofw_bus_get_node, rk_gpio_get_node), DEVMETHOD_END