From nobody Fri Oct 29 08:31:44 2021 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 560CD1826A79; Fri, 29 Oct 2021 08:31:46 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4HgbJ13VqGz4Ssf; Fri, 29 Oct 2021 08:31:45 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 152F913F9D; Fri, 29 Oct 2021 08:31:45 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 19T8ViFt073873; Fri, 29 Oct 2021 08:31:44 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 19T8Vi7W073872; Fri, 29 Oct 2021 08:31:44 GMT (envelope-from git) Date: Fri, 29 Oct 2021 08:31:44 GMT Message-Id: <202110290831.19T8Vi7W073872@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Wojciech Macek Subject: git: 027a58aab2ce - main - qoriq_gpio: Implement interrupt controller functionality List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-all@freebsd.org X-BeenThere: dev-commits-src-all@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: wma X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 027a58aab2cee5589a3a639afb77ecbb607f8fee Auto-Submitted: auto-generated X-ThisMailContainsUnwantedMimeParts: N The branch main has been updated by wma: URL: https://cgit.FreeBSD.org/src/commit/?id=027a58aab2cee5589a3a639afb77ecbb607f8fee commit 027a58aab2cee5589a3a639afb77ecbb607f8fee Author: Kornel Duleba AuthorDate: 2021-09-28 15:09:41 +0000 Commit: Wojciech Macek CommitDate: 2021-10-29 08:08:26 +0000 qoriq_gpio: Implement interrupt controller functionality The pic_* interface was used. Only edge interrupts are supported by this controller. Driver mutex had to be converted to a spin lock so that it can be used in the interrupt filter context. 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 Differential revision: https://reviews.freebsd.org/D32587 --- sys/dev/gpio/qoriq_gpio.c | 352 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file 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 #include #include +#include #include #include @@ -49,19 +50,25 @@ __FBSDID("$FreeBSD$"); #include #include +#include + #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);