Re: git: ec556724d7ad - main - Add interrupt handling to rk_gpio driver.
- In reply to: Ganbold Tsagaankhuu : "git: ec556724d7ad - main - Add interrupt handling to rk_gpio driver."
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sat, 20 Aug 2022 11:38:11 UTC
On Sat, Aug 20, 2022 at 7:32 PM Ganbold Tsagaankhuu <ganbold@freebsd.org>
wrote:
> The branch main has been updated by ganbold:
>
> URL:
> https://cgit.FreeBSD.org/src/commit/?id=ec556724d7ad1ee117fe595728e73dc9ddf78048
>
> commit ec556724d7ad1ee117fe595728e73dc9ddf78048
> Author: Søren Schmidt <sos@FreeBSD.org>
> AuthorDate: 2022-08-20 06:09:49 +0000
> Commit: Ganbold Tsagaankhuu <ganbold@FreeBSD.org>
> CommitDate: 2022-08-20 11:30:54 +0000
>
> Add interrupt handling to rk_gpio driver.
>
Sorry, it was reviewed by manu and differential revision is
https://reviews.freebsd.org/D36273
Probably I missed git arc ... command before pushing.
Ganbold
> ---
> sys/arm64/rockchip/rk_gpio.c | 227
> ++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 226 insertions(+), 1 deletion(-)
>
> diff --git a/sys/arm64/rockchip/rk_gpio.c b/sys/arm64/rockchip/rk_gpio.c
> index c9ad1c9ea1df..c3b1044df2f7 100644
> --- a/sys/arm64/rockchip/rk_gpio.c
> +++ b/sys/arm64/rockchip/rk_gpio.c
> @@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$");
>
> #include <sys/kernel.h>
> #include <sys/module.h>
> +#include <sys/proc.h>
> #include <sys/rman.h>
> #include <sys/lock.h>
> #include <sys/mutex.h>
> @@ -51,6 +52,7 @@ __FBSDID("$FreeBSD$");
> #include <dev/extres/clk/clk.h>
>
> #include "gpio_if.h"
> +#include "pic_if.h"
>
> #include "fdt_pinctrl_if.h"
>
> @@ -73,7 +75,9 @@ enum gpio_regs {
> #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_PIN_PULLUP | GPIO_PIN_PULLDOWN | GPIO_INTR_EDGE_BOTH | \
> + GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING | \
> + GPIO_INTR_LEVEL_HIGH | GPIO_INTR_LEVEL_LOW)
>
> #define GPIO_FLAGS_PINCTRL GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN
> #define RK_GPIO_MAX_PINS 32
> @@ -83,6 +87,12 @@ struct pin_cached {
> uint32_t flags;
> };
>
> +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;
> @@ -97,6 +107,8 @@ struct rk_gpio_softc {
> uint32_t version;
> struct pin_cached pin_cached[RK_GPIO_MAX_PINS];
> uint8_t regs[RK_GPIO_REGNUM];
> + void *ihandle;
> + struct rk_pin_irqsrc isrcs[RK_GPIO_MAX_PINS];
> };
>
> static struct ofw_compat_data compat_data[] = {
> @@ -113,6 +125,7 @@ static struct resource_spec rk_gpio_spec[] = {
> #define RK_GPIO_VERSION 0x78
> #define RK_GPIO_TYPE_V1 0x00000000
> #define RK_GPIO_TYPE_V2 0x01000c2b
> +#define RK_GPIO_ISRC(sc, irq) (&(sc->isrcs[irq].isrc))
>
> static int rk_gpio_detach(device_t dev);
>
> @@ -141,6 +154,29 @@ rk_gpio_read_bit(struct rk_gpio_softc *sc, int reg,
> int bit)
> return (value & 1);
> }
>
> +static void
> +rk_gpio_write_bit(struct rk_gpio_softc *sc, int reg, int bit, int data)
> +{
> + int offset = sc->regs[reg];
> + uint32_t value;
> +
> + if (sc->version == RK_GPIO_TYPE_V1) {
> + value = RK_GPIO_READ(sc, offset);
> + if (data)
> + value |= (1 << bit);
> + else
> + value &= ~(1 << bit);
> + RK_GPIO_WRITE(sc, offset, value);
> + } else {
> + if (data)
> + value = (1 << (bit % 16));
> + else
> + value = 0;
> + value |= (1 << ((bit % 16) + 16));
> + RK_GPIO_WRITE(sc, bit > 15 ? offset + 4 : offset, value);
> + }
> +}
> +
> static uint32_t
> rk_gpio_read_4(struct rk_gpio_softc *sc, int reg)
> {
> @@ -168,6 +204,43 @@ rk_gpio_write_4(struct rk_gpio_softc *sc, int reg,
> uint32_t value)
> }
> }
>
> +static int
> +rk_gpio_intr(void *arg)
> +{
> + struct rk_gpio_softc *sc = (struct rk_gpio_softc *)arg;;
> + struct trapframe *tf = curthread->td_intr_frame;
> + uint32_t status;
> +
> + RK_GPIO_LOCK(sc);
> + status = rk_gpio_read_4(sc, RK_GPIO_INT_STATUS);
> + rk_gpio_write_4(sc, RK_GPIO_PORTA_EOI, status);
> + RK_GPIO_UNLOCK(sc);
> +
> + while (status) {
> + int pin = ffs(status) - 1;
> +
> + status &= ~(1 << pin);
> + if (intr_isrc_dispatch(RK_GPIO_ISRC(sc, pin), tf)) {
> + device_printf(sc->sc_dev, "Interrupt pin=%d
> unhandled\n",
> + pin);
> + continue;
> + }
> +
> + if ((sc->version == RK_GPIO_TYPE_V1) &&
> + (sc->isrcs[pin].mode & GPIO_INTR_EDGE_BOTH)) {
> + RK_GPIO_LOCK(sc);
> + if (rk_gpio_read_bit(sc, RK_GPIO_EXT_PORTA, pin))
> + rk_gpio_write_bit(sc, RK_GPIO_INT_POLARITY,
> + (1 << pin), 0);
> + else
> + rk_gpio_write_bit(sc, RK_GPIO_INT_POLARITY,
> + (1 << pin), 1);
> + RK_GPIO_UNLOCK(sc);
> + }
> + }
> + return (FILTER_HANDLED);
> +}
> +
> static int
> rk_gpio_probe(device_t dev)
> {
> @@ -221,6 +294,15 @@ rk_gpio_attach(device_t dev)
> rk_gpio_detach(dev);
> return (ENXIO);
> }
> +
> + if ((err = bus_setup_intr(dev, sc->sc_res[1],
> + INTR_TYPE_MISC | INTR_MPSAFE, rk_gpio_intr, NULL,
> + sc, &sc->ihandle))) {
> + device_printf(dev, "Can not setup IRQ\n");
> + rk_gpio_detach(dev);
> + return (ENXIO);
> + }
> +
> RK_GPIO_LOCK(sc);
> sc->version = rk_gpio_read_4(sc, RK_GPIO_VERSION);
> RK_GPIO_UNLOCK(sc);
> @@ -259,6 +341,23 @@ rk_gpio_attach(device_t dev)
> return (ENXIO);
> }
>
> + for (i = 0; i < RK_GPIO_MAX_PINS; i++) {
> + sc->isrcs[i].irq = i;
> + sc->isrcs[i].mode = GPIO_INTR_CONFORM;
> + if ((err = intr_isrc_register(RK_GPIO_ISRC(sc, i),
> + dev, 0, "%s", device_get_nameunit(dev)))) {
> + device_printf(dev, "Can not register isrc %d\n",
> err);
> + rk_gpio_detach(dev);
> + return (ENXIO);
> + }
> + }
> +
> + if (intr_pic_register(dev, OF_xref_from_node(node)) == NULL) {
> + device_printf(dev, "Can not register pic\n");
> + rk_gpio_detach(dev);
> + return (ENXIO);
> + }
> +
> sc->sc_busdev = gpiobus_attach_bus(dev);
> if (sc->sc_busdev == NULL) {
> rk_gpio_detach(dev);
> @@ -549,6 +648,127 @@ rk_gpio_get_node(device_t bus, device_t dev)
> return (ofw_bus_get_node(bus));
> }
>
> +static int
> +rk_pic_map_intr(device_t dev, struct intr_map_data *data,
> + struct intr_irqsrc **isrcp)
> +{
> + struct rk_gpio_softc *sc = device_get_softc(dev);
> + struct intr_map_data_gpio *gdata;
> + uint32_t irq;
> +
> + if (data->type != INTR_MAP_DATA_GPIO) {
> + device_printf(dev, "Wrong type\n");
> + return (ENOTSUP);
> + }
> + gdata = (struct intr_map_data_gpio *)data;
> + irq = gdata->gpio_pin_num;
> + if (irq >= RK_GPIO_MAX_PINS) {
> + device_printf(dev, "Invalid interrupt %u\n", irq);
> + return (EINVAL);
> + }
> + *isrcp = RK_GPIO_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 = device_get_softc(dev);
> + struct rk_pin_irqsrc *rkisrc = (struct rk_pin_irqsrc *)isrc;
> + struct intr_map_data_gpio *gdata;
> + uint32_t mode;
> + uint8_t pin;
> +
> + if (!data) {
> + device_printf(dev, "No map data\n");
> + return (ENOTSUP);
> + }
> + gdata = (struct intr_map_data_gpio *)data;
> + mode = gdata->gpio_intr_mode;
> + pin = gdata->gpio_pin_num;
> +
> + if (rkisrc->irq != gdata->gpio_pin_num) {
> + device_printf(dev, "Interrupts don't match\n");
> + return (EINVAL);
> + }
> +
> + if (isrc->isrc_handlers != 0) {
> + device_printf(dev, "Handler already attached\n");
> + 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_write_bit(sc, RK_GPIO_SWPORTA_DDR, pin, 0);
> + rk_gpio_write_bit(sc, RK_GPIO_INTTYPE_LEVEL, pin, 1);
> + rk_gpio_write_bit(sc, RK_GPIO_INT_POLARITY, pin, 1);
> + break;
> + case GPIO_INTR_EDGE_FALLING:
> + rk_gpio_write_bit(sc, RK_GPIO_SWPORTA_DDR, pin, 0);
> + rk_gpio_write_bit(sc, RK_GPIO_INTTYPE_LEVEL, pin, 1);
> + rk_gpio_write_bit(sc, RK_GPIO_INT_POLARITY, pin, 0);
> + break;
> + case GPIO_INTR_EDGE_BOTH:
> + rk_gpio_write_bit(sc, RK_GPIO_SWPORTA_DDR, pin, 0);
> + rk_gpio_write_bit(sc, RK_GPIO_INTTYPE_LEVEL, pin, 1);
> + if (sc->version == RK_GPIO_TYPE_V1) {
> + if (rk_gpio_read_bit(sc, RK_GPIO_EXT_PORTA, pin))
> + rk_gpio_write_bit(sc, RK_GPIO_INT_POLARITY,
> + pin, 0);
> + else
> + rk_gpio_write_bit(sc, RK_GPIO_INT_POLARITY,
> + pin, 1);
> + } else
> + rk_gpio_write_bit(sc, RK_GPIO_INTTYPE_BOTH, pin,
> 1);
> + break;
> + case GPIO_INTR_LEVEL_HIGH:
> + rk_gpio_write_bit(sc, RK_GPIO_SWPORTA_DDR, pin, 0);
> + rk_gpio_write_bit(sc, RK_GPIO_INTTYPE_LEVEL, pin, 0);
> + rk_gpio_write_bit(sc, RK_GPIO_INT_POLARITY, pin, 1);
> + break;
> + case GPIO_INTR_LEVEL_LOW:
> + rk_gpio_write_bit(sc, RK_GPIO_SWPORTA_DDR, pin, 0);
> + rk_gpio_write_bit(sc, RK_GPIO_INTTYPE_LEVEL, pin, 0);
> + rk_gpio_write_bit(sc, RK_GPIO_INT_POLARITY, pin, 0);
> + break;
> + default:
> + rk_gpio_write_bit(sc, RK_GPIO_INTMASK, pin, 1);
> + rk_gpio_write_bit(sc, RK_GPIO_INTEN, pin, 0);
> + RK_GPIO_UNLOCK(sc);
> + return (EINVAL);
> + }
> + rk_gpio_write_bit(sc, RK_GPIO_DEBOUNCE, pin, 1);
> + rk_gpio_write_bit(sc, RK_GPIO_INTMASK, pin, 0);
> + rk_gpio_write_bit(sc, RK_GPIO_INTEN, pin, 1);
> + 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 = device_get_softc(dev);
> + struct rk_pin_irqsrc *irqsrc;
> +
> + irqsrc = (struct rk_pin_irqsrc *)isrc;
> +
> + if (isrc->isrc_handlers == 0) {
> + irqsrc->mode = GPIO_INTR_CONFORM;
> + RK_GPIO_LOCK(sc);
> + rk_gpio_write_bit(sc, RK_GPIO_INTEN, irqsrc->irq, 0);
> + rk_gpio_write_bit(sc, RK_GPIO_INTMASK, irqsrc->irq, 0);
> + rk_gpio_write_bit(sc, RK_GPIO_DEBOUNCE, irqsrc->irq, 0);
> + RK_GPIO_UNLOCK(sc);
> + }
> + return (0);
> +}
> +
> static device_method_t rk_gpio_methods[] = {
> /* Device interface */
> DEVMETHOD(device_probe, rk_gpio_probe),
> @@ -569,6 +789,11 @@ static device_method_t rk_gpio_methods[] = {
> DEVMETHOD(gpio_pin_config_32, rk_gpio_pin_config_32),
> DEVMETHOD(gpio_map_gpios, rk_gpio_map_gpios),
>
> + /* 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),
> +
> /* ofw_bus interface */
> DEVMETHOD(ofw_bus_get_node, rk_gpio_get_node),
>
>
>