git: 463b6ba05a69 - main - Introduce qoriq_gpio_pic driver
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sat, 06 Nov 2021 08:11:31 UTC
The branch main has been updated by wma: URL: https://cgit.FreeBSD.org/src/commit/?id=463b6ba05a69edbdaaf79425ba9fdc9dc64eae70 commit 463b6ba05a69edbdaaf79425ba9fdc9dc64eae70 Author: Kornel Duleba <mindal@semihalf.com> AuthorDate: 2021-11-02 10:30:01 +0000 Commit: Wojciech Macek <wma@FreeBSD.org> CommitDate: 2021-11-06 08:08:45 +0000 Introduce qoriq_gpio_pic driver It adds the PIC functionality on top of qoriq_gpio driver. We need a separate module since the powerpc PIC API is completely different than on other architectures. Two types of intr_map_data are supported: INTR_MAP_DATA_GPIO and INTR_MAP_DATA_FDT. This way interrupts can be allocated using the userspace gpio interrupt allocation method, as well as directly from simplebus. The latter can be used by devices that have its irq routed to a GPIO pin. Obtained from: Semihalf Sponsored by: Alstom Group --- sys/arm64/qoriq/qoriq_gpio_pic.c | 439 +++++++++++++++++++++++++++++++++++++++ sys/conf/files.arm64 | 1 + sys/dev/gpio/qoriq_gpio.c | 1 - 3 files changed, 440 insertions(+), 1 deletion(-) diff --git a/sys/arm64/qoriq/qoriq_gpio_pic.c b/sys/arm64/qoriq/qoriq_gpio_pic.c new file mode 100644 index 000000000000..c2f462bf7787 --- /dev/null +++ b/sys/arm64/qoriq/qoriq_gpio_pic.c @@ -0,0 +1,439 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Alstom Group. + * Copyright (c) 2021 Semihalf. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/proc.h> +#include <sys/rman.h> +#include <sys/gpio.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <machine/stdarg.h> + +#include <dev/gpio/gpiobusvar.h> +#include <dev/gpio/qoriq_gpio.h> +#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" + +struct qoriq_gpio_pic_irqsrc { + struct intr_irqsrc isrc; + int pin; +}; + +struct qoriq_gpio_pic_softc { + struct qoriq_gpio_softc base; + + struct resource *res_irq; + void *irq_cookie; + struct qoriq_gpio_pic_irqsrc isrcs[MAXPIN + 1]; + struct intr_map_data_gpio gdata; +}; + +#define RD4(sc, off) bus_read_4((sc)->base.sc_mem, (off)) +#define WR4(sc, off, data) bus_write_4((sc)->base.sc_mem, (off), (data)) + +static device_probe_t qoriq_gpio_pic_probe; +static device_attach_t qoriq_gpio_pic_attach; +static device_detach_t qoriq_gpio_pic_detach; + +static void +qoriq_gpio_pic_set_intr(struct qoriq_gpio_pic_softc *sc, int pin, bool enable) +{ + uint32_t reg; + + reg = RD4(sc, GPIO_GPIMR); + if (enable) + reg |= BIT(31 - pin); + else + reg &= ~BIT(31 - pin); + WR4(sc, GPIO_GPIMR, reg); +} + +static void +qoriq_gpio_pic_ack_intr(struct qoriq_gpio_pic_softc *sc, int pin) +{ + uint32_t reg; + + reg = BIT(31 - pin); + WR4(sc, GPIO_GPIER, reg); +} + +static int +qoriq_gpio_pic_intr(void *arg) +{ + struct qoriq_gpio_pic_softc *sc; + struct trapframe *tf; + uint32_t status; + int pin; + + sc = (struct qoriq_gpio_pic_softc *)arg; + tf = curthread->td_intr_frame; + + status = RD4(sc, GPIO_GPIER); + status &= RD4(sc, GPIO_GPIMR); + while (status != 0) { + pin = ffs(status) - 1; + status &= ~BIT(pin); + pin = 31 - pin; + + if (intr_isrc_dispatch(&sc->isrcs[pin].isrc, tf) != 0) { + GPIO_LOCK(&sc->base); + qoriq_gpio_pic_set_intr(sc, pin, false); + qoriq_gpio_pic_ack_intr(sc, pin); + GPIO_UNLOCK(&sc->base); + device_printf(sc->base.dev, + "Masking spurious pin interrupt %d\n", + pin); + } + } + + return (FILTER_HANDLED); +} + +static void +qoriq_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct qoriq_gpio_pic_softc *sc; + struct qoriq_gpio_pic_irqsrc *qisrc; + + sc = device_get_softc(dev); + qisrc = (struct qoriq_gpio_pic_irqsrc *)isrc; + + GPIO_LOCK(&sc->base); + qoriq_gpio_pic_set_intr(sc, qisrc->pin, false); + GPIO_UNLOCK(&sc->base); +} + +static void +qoriq_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct qoriq_gpio_pic_softc *sc; + struct qoriq_gpio_pic_irqsrc *qisrc; + + sc = device_get_softc(dev); + qisrc = (struct qoriq_gpio_pic_irqsrc *)isrc; + + GPIO_LOCK(&sc->base); + qoriq_gpio_pic_set_intr(sc, qisrc->pin, true); + GPIO_UNLOCK(&sc->base); +} + +static struct intr_map_data_gpio* +qoriq_gpio_pic_convert_map_data(struct qoriq_gpio_pic_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_pic_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + struct qoriq_gpio_pic_softc *sc; + struct intr_map_data_gpio *gdata; + int pin; + + sc = device_get_softc(dev); + + gdata = qoriq_gpio_pic_convert_map_data(sc, data); + if (gdata == NULL) + return (EINVAL); + + pin = gdata->gpio_pin_num; + if (pin > MAXPIN) + return (EINVAL); + + *isrcp = &sc->isrcs[pin].isrc; + return (0); +} + +static int +qoriq_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct qoriq_gpio_pic_softc *sc; + struct intr_map_data_gpio *gdata; + struct qoriq_gpio_pic_irqsrc *qisrc; + bool falling; + uint32_t reg; + + sc = device_get_softc(dev); + qisrc = (struct qoriq_gpio_pic_irqsrc *)isrc; + + gdata = qoriq_gpio_pic_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->base); + reg = RD4(sc, GPIO_GPICR); + if (falling) + reg |= BIT(31 - qisrc->pin); + else + reg &= ~BIT(31 - qisrc->pin); + WR4(sc, GPIO_GPICR, reg); + GPIO_UNLOCK(&sc->base); + + return (0); +} + +static int +qoriq_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct qoriq_gpio_pic_softc *sc; + struct qoriq_gpio_pic_irqsrc *qisrc; + + sc = device_get_softc(dev); + qisrc = (struct qoriq_gpio_pic_irqsrc *)isrc; + + if (isrc->isrc_handlers > 0) + return (0); + + GPIO_LOCK(&sc->base); + qoriq_gpio_pic_set_intr(sc, qisrc->pin, false); + GPIO_UNLOCK(&sc->base); + return (0); +} + +static void +qoriq_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ + struct qoriq_gpio_pic_softc *sc; + struct qoriq_gpio_pic_irqsrc *qisrc; + + sc = device_get_softc(dev); + qisrc = (struct qoriq_gpio_pic_irqsrc *)isrc; + + GPIO_LOCK(&sc->base); + qoriq_gpio_pic_ack_intr(sc, qisrc->pin); + GPIO_UNLOCK(&sc->base); +} + + +static void +qoriq_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct qoriq_gpio_pic_softc *sc; + struct qoriq_gpio_pic_irqsrc *qisrc; + + sc = device_get_softc(dev); + qisrc = (struct qoriq_gpio_pic_irqsrc *)isrc; + + GPIO_LOCK(&sc->base); + qoriq_gpio_pic_ack_intr(sc, qisrc->pin); + qoriq_gpio_pic_set_intr(sc, qisrc->pin, true); + GPIO_UNLOCK(&sc->base); +} + +static void +qoriq_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct qoriq_gpio_pic_softc *sc; + struct qoriq_gpio_pic_irqsrc *qisrc; + + sc = device_get_softc(dev); + qisrc = (struct qoriq_gpio_pic_irqsrc *)isrc; + + GPIO_LOCK(&sc->base); + qoriq_gpio_pic_set_intr(sc, qisrc->pin, false); + GPIO_UNLOCK(&sc->base); + +} +static int +qoriq_gpio_pic_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "fsl,qoriq-gpio")) + return (ENXIO); + + device_set_desc(dev, "Freescale QorIQ GPIO PIC driver"); + + return (BUS_PROBE_DEFAULT); +} + +static int +qoriq_gpio_pic_attach(device_t dev) +{ + struct qoriq_gpio_pic_softc *sc; + int error, rid, i; + const char *name; + intptr_t xref; + + sc = device_get_softc(dev); + + error = qoriq_gpio_attach(dev); + if (error != 0) + return (error); + + rid = 0; + sc->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE | RF_SHAREABLE); + if (sc->res_irq == NULL) { + device_printf(dev, "Can't allocate interrupt resource.\n"); + error = ENOMEM; + goto fail; + } + + error = bus_setup_intr(dev, sc->res_irq, INTR_TYPE_MISC | INTR_MPSAFE, + qoriq_gpio_pic_intr, NULL, sc, &sc->irq_cookie); + if (error != 0) { + device_printf(dev, "Failed to setup interrupt.\n"); + goto fail; + } + + name = device_get_nameunit(dev); + for (i = 0; i <= MAXPIN; i++) { + sc->isrcs[i].pin = i; + error = intr_isrc_register(&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; + } + + /* ACK and mask all interrupts. */ + WR4(sc, GPIO_GPIER, 0xffffffff); + WR4(sc, GPIO_GPIMR, 0); + + return (0); +fail: + qoriq_gpio_pic_detach(dev); + return (error); +} + +static int +qoriq_gpio_pic_detach(device_t dev) +{ + struct qoriq_gpio_pic_softc *sc; + + sc = device_get_softc(dev); + + if (sc->irq_cookie != NULL) + bus_teardown_intr(dev, sc->res_irq, sc->irq_cookie); + + if (sc->res_irq != NULL) + bus_release_resource(dev, SYS_RES_IRQ, + rman_get_rid(sc->res_irq), sc->res_irq); + + return (qoriq_gpio_detach(dev)); +} + + +static device_method_t qoriq_gpio_pic_methods[] = { + DEVMETHOD(device_probe, qoriq_gpio_pic_probe), + DEVMETHOD(device_attach, qoriq_gpio_pic_attach), + DEVMETHOD(device_detach, qoriq_gpio_pic_detach), + + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + + DEVMETHOD(pic_disable_intr, qoriq_gpio_pic_disable_intr), + DEVMETHOD(pic_enable_intr, qoriq_gpio_pic_enable_intr), + DEVMETHOD(pic_map_intr, qoriq_gpio_pic_map_intr), + DEVMETHOD(pic_setup_intr, qoriq_gpio_pic_setup_intr), + DEVMETHOD(pic_teardown_intr, qoriq_gpio_pic_teardown_intr), + DEVMETHOD(pic_post_filter, qoriq_gpio_pic_post_filter), + DEVMETHOD(pic_post_ithread, qoriq_gpio_pic_post_ithread), + DEVMETHOD(pic_pre_ithread, qoriq_gpio_pic_pre_ithread), + + DEVMETHOD_END +}; + +static devclass_t qoriq_gpio_pic_devclass; + +DEFINE_CLASS_1(gpio, qoriq_gpio_pic_driver, qoriq_gpio_pic_methods, + sizeof(struct qoriq_gpio_pic_softc), qoriq_gpio_driver); +EARLY_DRIVER_MODULE(qoriq_gpio_pic, simplebus, qoriq_gpio_pic_driver, + qoriq_gpio_pic_devclass, NULL, NULL, + BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64 index c1f99382863a..e718e9b93847 100644 --- a/sys/conf/files.arm64 +++ b/sys/conf/files.arm64 @@ -522,6 +522,7 @@ tegra210_xusb.fw optional tegra210_xusb_fw \ # NXP arm/freescale/vybrid/vf_i2c.c optional vf_i2c iicbus SOC_NXP_LS arm64/qoriq/qoriq_dw_pci.c optional pci fdt SOC_NXP_LS +arm64/qoriq/qoriq_gpio_pic.c optional gpio fdt SOC_NXP_LS arm64/qoriq/qoriq_therm.c optional pci fdt SOC_NXP_LS arm64/qoriq/qoriq_therm_if.m optional pci fdt SOC_NXP_LS arm64/qoriq/clk/ls1028a_clkgen.c optional clk SOC_NXP_LS diff --git a/sys/dev/gpio/qoriq_gpio.c b/sys/dev/gpio/qoriq_gpio.c index eb790eb50d29..8300a45aa249 100644 --- a/sys/dev/gpio/qoriq_gpio.c +++ b/sys/dev/gpio/qoriq_gpio.c @@ -233,7 +233,6 @@ qoriq_gpio_pin_toggle(device_t dev, uint32_t pin) } static struct ofw_compat_data gpio_matches[] = { - {"fsl,qoriq-gpio", 1}, {"fsl,pq3-gpio", 1}, {"fsl,mpc8572-gpio", 1}, {0, 0}