diff options
| author | Kornel Duleba <mindal@semihalf.com> | 2021-09-28 15:09:41 +0000 |
|---|---|---|
| committer | Wojciech Macek <wma@FreeBSD.org> | 2021-10-29 08:08:26 +0000 |
| commit | 027a58aab2cee5589a3a639afb77ecbb607f8fee (patch) | |
| tree | 16d0233533d857b3300097bd06c46a38721a0fbe /sys/dev | |
| parent | d88aecce69c82e9312c4e5e4e9eab4c56284925f (diff) | |
Diffstat (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/gpio/qoriq_gpio.c | 352 |
1 files changed, 339 insertions, 13 deletions
diff --git a/sys/dev/gpio/qoriq_gpio.c b/sys/dev/gpio/qoriq_gpio.c index dc4813e07b8e..0a78adbecb0f 100644 --- a/sys/dev/gpio/qoriq_gpio.c +++ b/sys/dev/gpio/qoriq_gpio.c @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$"); #include <sys/kernel.h> #include <sys/module.h> #include <sys/mutex.h> +#include <sys/proc.h> #include <sys/rman.h> #include <sys/gpio.h> @@ -49,19 +50,25 @@ __FBSDID("$FreeBSD$"); #include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> +#include <dt-bindings/interrupt-controller/irq.h> + #include "gpio_if.h" +#include "pic_if.h" +#define BIT(x) (1 << (x)) #define MAXPIN (31) #define VALID_PIN(u) ((u) >= 0 && (u) <= MAXPIN) #define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ - GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL) + GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | \ + GPIO_INTR_EDGE_FALLING | GPIO_INTR_EDGE_BOTH | \ + GPIO_PIN_PULLUP) -#define GPIO_LOCK(sc) mtx_lock(&(sc)->sc_mtx) -#define GPIO_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) +#define GPIO_LOCK(sc) mtx_lock_spin(&(sc)->sc_mtx) +#define GPIO_UNLOCK(sc) mtx_unlock_spin(&(sc)->sc_mtx) #define GPIO_LOCK_INIT(sc) \ mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), \ - "gpio", MTX_DEF) + "gpio", MTX_SPIN) #define GPIO_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); #define GPIO_GPDIR 0x0 @@ -72,12 +79,21 @@ __FBSDID("$FreeBSD$"); #define GPIO_GPICR 0x14 #define GPIO_GPIBE 0x18 +struct qoriq_gpio_irqsrc { + struct intr_irqsrc isrc; + int pin; +}; + struct qoriq_gpio_softc { device_t dev; device_t busdev; struct mtx sc_mtx; struct resource *sc_mem; /* Memory resource */ + struct resource *sc_intr; + void *intr_cookie; struct gpio_pin sc_pins[MAXPIN + 1]; + struct qoriq_gpio_irqsrc sc_isrcs[MAXPIN + 1]; + struct intr_map_data_gpio gdata; }; static device_t @@ -260,6 +276,254 @@ qoriq_gpio_pin_toggle(device_t dev, uint32_t pin) return (0); } +static void +qoriq_gpio_set_intr(struct qoriq_gpio_softc *sc, int pin, bool enable) +{ + uint32_t reg; + + reg = bus_read_4(sc->sc_mem, GPIO_GPIMR); + if (enable) + reg |= BIT(31 - pin); + else + reg &= ~BIT(31 - pin); + bus_write_4(sc->sc_mem, GPIO_GPIMR, reg); +} + +static void +qoriq_gpio_ack_intr(struct qoriq_gpio_softc *sc, int pin) +{ + uint32_t reg; + + reg = BIT(31 - pin); + bus_write_4(sc->sc_mem, GPIO_GPIER, reg); +} + +static int +qoriq_gpio_intr(void *arg) +{ + struct qoriq_gpio_softc *sc; + struct trapframe *tf; + uint32_t status; + int pin; + + sc = (struct qoriq_gpio_softc *)arg; + tf = curthread->td_intr_frame; + + status = bus_read_4(sc->sc_mem, GPIO_GPIER); + status &= bus_read_4(sc->sc_mem, GPIO_GPIMR); + while (status != 0) { + pin = ffs(status) - 1; + status &= ~BIT(pin); + pin = 31 - pin; + + if (intr_isrc_dispatch(&sc->sc_isrcs[pin].isrc, tf) != 0) { + GPIO_LOCK(sc); + qoriq_gpio_set_intr(sc, pin, false); + qoriq_gpio_ack_intr(sc, pin); + GPIO_UNLOCK(sc); + device_printf(sc->dev, + "Masking spurious pin interrupt %d\n", + pin); + } + } + + return (FILTER_HANDLED); +} + +static void +qoriq_gpio_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct qoriq_gpio_softc *sc; + struct qoriq_gpio_irqsrc *qisrc; + + sc = device_get_softc(dev); + qisrc = (struct qoriq_gpio_irqsrc *)isrc; + + GPIO_LOCK(sc); + qoriq_gpio_set_intr(sc, qisrc->pin, false); + GPIO_UNLOCK(sc); +} + +static void +qoriq_gpio_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct qoriq_gpio_softc *sc; + struct qoriq_gpio_irqsrc *qisrc; + + sc = device_get_softc(dev); + qisrc = (struct qoriq_gpio_irqsrc *)isrc; + + GPIO_LOCK(sc); + qoriq_gpio_set_intr(sc, qisrc->pin, true); + GPIO_UNLOCK(sc); +} + +static struct intr_map_data_gpio* +qoriq_gpio_convert_map_data(struct qoriq_gpio_softc *sc, struct intr_map_data *data) +{ + struct intr_map_data_gpio *gdata; + struct intr_map_data_fdt *daf; + + switch (data->type) { + case INTR_MAP_DATA_GPIO: + gdata = (struct intr_map_data_gpio *)data; + break; + case INTR_MAP_DATA_FDT: + daf = (struct intr_map_data_fdt *)data; + if (daf->ncells != 2) + return (NULL); + + gdata = &sc->gdata; + gdata->gpio_pin_num = daf->cells[0]; + switch (daf->cells[1]) { + case IRQ_TYPE_LEVEL_LOW: + gdata->gpio_intr_mode = GPIO_INTR_LEVEL_LOW; + break; + case IRQ_TYPE_LEVEL_HIGH: + gdata->gpio_intr_mode = GPIO_INTR_LEVEL_HIGH; + break; + case IRQ_TYPE_EDGE_RISING: + gdata->gpio_intr_mode = GPIO_INTR_EDGE_RISING; + break; + case IRQ_TYPE_EDGE_FALLING: + gdata->gpio_intr_mode = GPIO_INTR_EDGE_FALLING; + break; + case IRQ_TYPE_EDGE_BOTH: + gdata->gpio_intr_mode = GPIO_INTR_EDGE_BOTH; + break; + default: + return (NULL); + } + break; + default: + return (NULL); + } + + return (gdata); +} + + +static int +qoriq_gpio_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + struct qoriq_gpio_softc *sc; + struct intr_map_data_gpio *gdata; + int pin; + + sc = device_get_softc(dev); + + gdata = qoriq_gpio_convert_map_data(sc, data); + if (gdata == NULL) + return (EINVAL); + + pin = gdata->gpio_pin_num; + if (pin > MAXPIN) + return (EINVAL); + + *isrcp = &sc->sc_isrcs[pin].isrc; + return (0); +} + +static int +qoriq_gpio_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct qoriq_gpio_softc *sc; + struct intr_map_data_gpio *gdata; + struct qoriq_gpio_irqsrc *qisrc; + bool falling; + uint32_t reg; + + sc = device_get_softc(dev); + qisrc = (struct qoriq_gpio_irqsrc *)isrc; + + gdata = qoriq_gpio_convert_map_data(sc, data); + if (gdata == NULL) + return (EINVAL); + + if (gdata->gpio_intr_mode & GPIO_INTR_EDGE_BOTH) + falling = false; + else if (gdata->gpio_intr_mode & GPIO_INTR_EDGE_FALLING) + falling = true; + else + return (EOPNOTSUPP); + + GPIO_LOCK(sc); + reg = bus_read_4(sc->sc_mem, GPIO_GPICR); + if (falling) + reg |= BIT(31 - qisrc->pin); + else + reg &= ~BIT(31 - qisrc->pin); + bus_write_4(sc->sc_mem, GPIO_GPICR, reg); + GPIO_UNLOCK(sc); + + return (0); +} + +static int +qoriq_gpio_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct qoriq_gpio_softc *sc; + struct qoriq_gpio_irqsrc *qisrc; + + sc = device_get_softc(dev); + qisrc = (struct qoriq_gpio_irqsrc *)isrc; + + if (isrc->isrc_handlers > 0) + return (0); + + GPIO_LOCK(sc); + qoriq_gpio_set_intr(sc, qisrc->pin, false); + GPIO_UNLOCK(sc); + return (0); +} + +static void +qoriq_gpio_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ + struct qoriq_gpio_softc *sc; + struct qoriq_gpio_irqsrc *qisrc; + + sc = device_get_softc(dev); + qisrc = (struct qoriq_gpio_irqsrc *)isrc; + + GPIO_LOCK(sc); + qoriq_gpio_ack_intr(sc, qisrc->pin); + GPIO_UNLOCK(sc); +} + + +static void +qoriq_gpio_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct qoriq_gpio_softc *sc; + struct qoriq_gpio_irqsrc *qisrc; + + sc = device_get_softc(dev); + qisrc = (struct qoriq_gpio_irqsrc *)isrc; + + GPIO_LOCK(sc); + qoriq_gpio_ack_intr(sc, qisrc->pin); + qoriq_gpio_set_intr(sc, qisrc->pin, true); + GPIO_UNLOCK(sc); +} + +static void +qoriq_gpio_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct qoriq_gpio_softc *sc; + struct qoriq_gpio_irqsrc *qisrc; + + sc = device_get_softc(dev); + qisrc = (struct qoriq_gpio_irqsrc *)isrc; + + GPIO_LOCK(sc); + qoriq_gpio_set_intr(sc, qisrc->pin, false); + GPIO_UNLOCK(sc); +} + static struct ofw_compat_data gpio_matches[] = { {"fsl,qoriq-gpio", 1}, {"fsl,pq3-gpio", 1}, @@ -385,7 +649,9 @@ static int qoriq_gpio_attach(device_t dev) { struct qoriq_gpio_softc *sc = device_get_softc(dev); - int i, rid; + int i, rid, error; + const char *name; + intptr_t xref; sc->dev = dev; @@ -397,17 +663,46 @@ qoriq_gpio_attach(device_t dev) SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_mem == NULL) { device_printf(dev, "Can't allocate memory for device output port"); - qoriq_gpio_detach(dev); - return (ENOMEM); + error = ENOMEM; + goto fail; + } + + rid = 0; + sc->sc_intr = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE | RF_SHAREABLE); + if (sc->sc_intr == NULL) { + device_printf(dev, "Can't allocate interrupt resource.\n"); + error = ENOMEM; + goto fail; + } + + error = bus_setup_intr(dev, sc->sc_intr, INTR_TYPE_MISC | INTR_MPSAFE, + qoriq_gpio_intr, NULL, sc, &sc->intr_cookie); + if (error != 0) { + device_printf(dev, "Failed to setup interrupt.\n"); + goto fail; } - for (i = 0; i <= MAXPIN; i++) + name = device_get_nameunit(dev); + for (i = 0; i <= MAXPIN; i++) { sc->sc_pins[i].gp_caps = DEFAULT_CAPS; + sc->sc_isrcs[i].pin = i; + error = intr_isrc_register(&sc->sc_isrcs[i].isrc, + dev, 0, "%s,%u", name, i); + if (error != 0) + goto fail; + } + + xref = OF_xref_from_node(ofw_bus_get_node(dev)); + if (intr_pic_register(dev, xref) == NULL) { + error = ENXIO; + goto fail; + } sc->busdev = gpiobus_attach_bus(dev); if (sc->busdev == NULL) { - qoriq_gpio_detach(dev); - return (ENOMEM); + error = ENXIO; + goto fail; } /* * Enable the GPIO Input Buffer for all GPIOs. @@ -419,7 +714,13 @@ qoriq_gpio_attach(device_t dev) OF_device_register_xref(OF_xref_from_node(ofw_bus_get_node(dev)), dev); + bus_write_4(sc->sc_mem, GPIO_GPIER, 0xffffffff); + bus_write_4(sc->sc_mem, GPIO_GPIMR, 0); + return (0); +fail: + qoriq_gpio_detach(dev); + return (error); } static int @@ -435,6 +736,13 @@ qoriq_gpio_detach(device_t dev) rman_get_rid(sc->sc_mem), sc->sc_mem); } + if (sc->intr_cookie != NULL) + bus_teardown_intr(dev, sc->sc_intr, sc->intr_cookie); + + if (sc->sc_intr != NULL) + bus_release_resource(dev, SYS_RES_IRQ, + rman_get_rid(sc->sc_intr), sc->sc_intr); + GPIO_LOCK_DESTROY(sc); return (0); @@ -446,6 +754,11 @@ static device_method_t qoriq_gpio_methods[] = { DEVMETHOD(device_attach, qoriq_gpio_attach), DEVMETHOD(device_detach, qoriq_gpio_detach), + /* Bus interface */ + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + /* GPIO protocol */ DEVMETHOD(gpio_get_bus, qoriq_gpio_get_bus), DEVMETHOD(gpio_pin_max, qoriq_gpio_pin_max), @@ -461,6 +774,16 @@ static device_method_t qoriq_gpio_methods[] = { DEVMETHOD(gpio_pin_access_32, qoriq_gpio_pin_access_32), DEVMETHOD(gpio_pin_config_32, qoriq_gpio_pin_config_32), + /* Interrupt controller */ + DEVMETHOD(pic_disable_intr, qoriq_gpio_disable_intr), + DEVMETHOD(pic_enable_intr, qoriq_gpio_enable_intr), + DEVMETHOD(pic_map_intr, qoriq_gpio_map_intr), + DEVMETHOD(pic_setup_intr, qoriq_gpio_setup_intr), + DEVMETHOD(pic_teardown_intr, qoriq_gpio_teardown_intr), + DEVMETHOD(pic_post_filter, qoriq_gpio_post_filter), + DEVMETHOD(pic_post_ithread, qoriq_gpio_post_ithread), + DEVMETHOD(pic_pre_ithread, qoriq_gpio_pre_ithread), + DEVMETHOD_END }; @@ -471,6 +794,9 @@ static driver_t qoriq_gpio_driver = { }; static devclass_t qoriq_gpio_devclass; -EARLY_DRIVER_MODULE(qoriq_gpio, simplebus, qoriq_gpio_driver, - qoriq_gpio_devclass, NULL, NULL, - BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE); +/* + * This needs to be loaded after interrupts are available and + * before consumers need it. + */ +EARLY_DRIVER_MODULE(qoriq_gpio, simplebus, qoriq_gpio_driver, qoriq_gpio_devclass, + NULL, NULL, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); |
