aboutsummaryrefslogtreecommitdiff
path: root/sys/arm64/rockchip/rk_gpio.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arm64/rockchip/rk_gpio.c')
-rw-r--r--sys/arm64/rockchip/rk_gpio.c478
1 files changed, 411 insertions, 67 deletions
diff --git a/sys/arm64/rockchip/rk_gpio.c b/sys/arm64/rockchip/rk_gpio.c
index b695797f153e..6a7ae95d4e8a 100644
--- a/sys/arm64/rockchip/rk_gpio.c
+++ b/sys/arm64/rockchip/rk_gpio.c
@@ -1,7 +1,8 @@
/*-
- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ * SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2018 Emmanuel Vadot <manu@FreeBSD.org>
+ * Copyright (c) 2021 Soren Schmidt <sos@deepcore.dk>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -26,15 +27,13 @@
*
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
+#include <sys/proc.h>
#include <sys/rman.h>
#include <sys/lock.h>
#include <sys/mutex.h>
@@ -47,31 +46,49 @@ __FBSDID("$FreeBSD$");
#include <dev/gpio/gpiobusvar.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
-#include <dev/extres/clk/clk.h>
+#include <dev/clk/clk.h>
#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 */
+enum gpio_regs {
+ RK_GPIO_SWPORTA_DR = 1, /* Data register */
+ RK_GPIO_SWPORTA_DDR, /* Data direction register */
+ RK_GPIO_INTEN, /* Interrupt enable register */
+ RK_GPIO_INTMASK, /* Interrupt mask register */
+ RK_GPIO_INTTYPE_LEVEL, /* Interrupt level register */
+ RK_GPIO_INTTYPE_BOTH, /* Both rise and falling edge */
+ RK_GPIO_INT_POLARITY, /* Interrupt polarity register */
+ RK_GPIO_INT_STATUS, /* Interrupt status register */
+ RK_GPIO_INT_RAWSTATUS, /* Raw Interrupt status register */
+ RK_GPIO_DEBOUNCE, /* Debounce enable register */
+ RK_GPIO_PORTA_EOI, /* Clear interrupt register */
+ RK_GPIO_EXT_PORTA, /* External port register */
+ RK_GPIO_REGNUM
+};
-#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_LS_SYNC 0x60 /* Level sensitive syncronization enable register */
-#define RK_GPIO_DEBOUNCE 0x48 /* Debounce enable register */
+#define RK_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \
+ 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 RK_GPIO_PORTA_EOI 0x4C /* Clear interrupt register */
-#define RK_GPIO_EXT_PORTA 0x50 /* External port register */
+#define GPIO_FLAGS_PINCTRL GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN
+#define RK_GPIO_MAX_PINS 32
-#define RK_GPIO_LS_SYNC 0x60 /* Level sensitive syncronization enable register */
+struct pin_cached {
+ uint8_t is_gpio;
+ uint32_t flags;
+};
-#define RK_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \
- GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN)
+struct rk_pin_irqsrc {
+ struct intr_irqsrc isrc;
+ uint32_t irq;
+ uint32_t mode;
+};
struct rk_gpio_softc {
device_t sc_dev;
@@ -82,6 +99,13 @@ struct rk_gpio_softc {
bus_space_handle_t sc_bsh;
clk_t clk;
device_t pinctrl;
+ uint32_t swporta;
+ uint32_t swporta_ddr;
+ 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[] = {
@@ -95,6 +119,11 @@ static struct resource_spec rk_gpio_spec[] = {
{ -1, 0 }
};
+#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);
#define RK_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx)
@@ -107,6 +136,109 @@ static int rk_gpio_detach(device_t dev);
bus_space_read_4(_sc->sc_bst, _sc->sc_bsh, _off)
static int
+rk_gpio_read_bit(struct rk_gpio_softc *sc, int reg, int bit)
+{
+ int offset = sc->regs[reg];
+ uint32_t value;
+
+ if (sc->version == RK_GPIO_TYPE_V1) {
+ value = RK_GPIO_READ(sc, offset);
+ value >>= bit;
+ } else {
+ value = RK_GPIO_READ(sc, bit > 15 ? offset + 4 : offset);
+ value >>= (bit % 16);
+ }
+ 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)
+{
+ int offset = sc->regs[reg];
+ uint32_t value;
+
+ if (sc->version == RK_GPIO_TYPE_V1)
+ value = RK_GPIO_READ(sc, offset);
+ else
+ value = (RK_GPIO_READ(sc, offset) & 0xffff) |
+ (RK_GPIO_READ(sc, offset + 4) << 16);
+ return (value);
+}
+
+static void
+rk_gpio_write_4(struct rk_gpio_softc *sc, int reg, uint32_t value)
+{
+ int offset = sc->regs[reg];
+
+ if (sc->version == RK_GPIO_TYPE_V1)
+ RK_GPIO_WRITE(sc, offset, value);
+ else {
+ RK_GPIO_WRITE(sc, offset, (value & 0xffff) | 0xffff0000);
+ RK_GPIO_WRITE(sc, offset + 4, (value >> 16) | 0xffff0000);
+ }
+}
+
+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)
{
@@ -124,12 +256,13 @@ static int
rk_gpio_attach(device_t dev)
{
struct rk_gpio_softc *sc;
- phandle_t node;
- int err;
+ phandle_t parent_node, node;
+ int err, i;
sc = device_get_softc(dev);
sc->sc_dev = dev;
sc->pinctrl = device_get_parent(dev);
+ parent_node = ofw_bus_get_node(sc->pinctrl);
node = ofw_bus_get_node(sc->sc_dev);
if (!OF_hasprop(node, "gpio-controller"))
@@ -160,12 +293,90 @@ rk_gpio_attach(device_t 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);
+ }
+
+ /*
+ * RK3568 has GPIO_VER_ID register, however both
+ * RK3328 and RK3399 doesn't have. So choose the
+ * version based on parent's compat string.
+ */
+ if (ofw_bus_node_is_compatible(parent_node, "rockchip,rk3568-pinctrl"))
+ sc->version = RK_GPIO_TYPE_V2;
+ else
+ sc->version = RK_GPIO_TYPE_V1;
+
+ switch (sc->version) {
+ case RK_GPIO_TYPE_V1:
+ sc->regs[RK_GPIO_SWPORTA_DR] = 0x00;
+ sc->regs[RK_GPIO_SWPORTA_DDR] = 0x04;
+ sc->regs[RK_GPIO_INTEN] = 0x30;
+ sc->regs[RK_GPIO_INTMASK] = 0x34;
+ sc->regs[RK_GPIO_INTTYPE_LEVEL] = 0x38;
+ sc->regs[RK_GPIO_INT_POLARITY] = 0x3c;
+ sc->regs[RK_GPIO_INT_STATUS] = 0x40;
+ sc->regs[RK_GPIO_INT_RAWSTATUS] = 0x44;
+ sc->regs[RK_GPIO_DEBOUNCE] = 0x48;
+ sc->regs[RK_GPIO_PORTA_EOI] = 0x4c;
+ sc->regs[RK_GPIO_EXT_PORTA] = 0x50;
+ break;
+ case RK_GPIO_TYPE_V2:
+ sc->regs[RK_GPIO_SWPORTA_DR] = 0x00;
+ sc->regs[RK_GPIO_SWPORTA_DDR] = 0x08;
+ sc->regs[RK_GPIO_INTEN] = 0x10;
+ sc->regs[RK_GPIO_INTMASK] = 0x18;
+ sc->regs[RK_GPIO_INTTYPE_LEVEL] = 0x20;
+ sc->regs[RK_GPIO_INTTYPE_BOTH] = 0x30;
+ sc->regs[RK_GPIO_INT_POLARITY] = 0x28;
+ sc->regs[RK_GPIO_INT_STATUS] = 0x50;
+ sc->regs[RK_GPIO_INT_RAWSTATUS] = 0x58;
+ sc->regs[RK_GPIO_DEBOUNCE] = 0x38;
+ sc->regs[RK_GPIO_PORTA_EOI] = 0x60;
+ sc->regs[RK_GPIO_EXT_PORTA] = 0x70;
+ break;
+ default:
+ device_printf(dev, "Unknown gpio version %08x\n", sc->version);
+ rk_gpio_detach(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);
return (ENXIO);
}
+ /* Set the cached value to unknown */
+ for (i = 0; i < RK_GPIO_MAX_PINS; i++)
+ sc->pin_cached[i].is_gpio = 2;
+
+ RK_GPIO_LOCK(sc);
+ sc->swporta = rk_gpio_read_4(sc, RK_GPIO_SWPORTA_DR);
+ sc->swporta_ddr = rk_gpio_read_4(sc, RK_GPIO_SWPORTA_DDR);
+ RK_GPIO_UNLOCK(sc);
+
return (0);
}
@@ -229,28 +440,24 @@ static int
rk_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
{
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)
- return (EINVAL);
-
+ if (__predict_false(sc->pin_cached[pin].is_gpio != 1)) {
+ rv = FDT_PINCTRL_IS_GPIO(sc->pinctrl, dev, pin, (bool *)&sc->pin_cached[pin].is_gpio);
+ if (rv != 0)
+ return (rv);
+ if (sc->pin_cached[pin].is_gpio == 0)
+ return (EINVAL);
+ }
*flags = 0;
rv = FDT_PINCTRL_GET_FLAGS(sc->pinctrl, dev, pin, flags);
if (rv != 0)
return (rv);
+ sc->pin_cached[pin].flags = *flags;
- RK_GPIO_LOCK(sc);
- reg = RK_GPIO_READ(sc, RK_GPIO_SWPORTA_DDR);
- RK_GPIO_UNLOCK(sc);
-
- if (reg & (1 << pin))
+ if (sc->swporta_ddr & (1 << pin))
*flags |= GPIO_PIN_OUTPUT;
else
*flags |= GPIO_PIN_INPUT;
@@ -262,6 +469,9 @@ static int
rk_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
{
+ if (pin >= RK_GPIO_MAX_PINS)
+ return EINVAL;
+
*caps = RK_GPIO_DEFAULT_CAPS;
return (0);
}
@@ -270,31 +480,35 @@ static int
rk_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
{
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_GPIO_MAX_PINS)
return (EINVAL);
- rv = FDT_PINCTRL_SET_FLAGS(sc->pinctrl, dev, pin, flags);
- if (rv != 0)
- return (rv);
+ if (__predict_false(sc->pin_cached[pin].is_gpio != 1)) {
+ rv = FDT_PINCTRL_IS_GPIO(sc->pinctrl, dev, pin, (bool *)&sc->pin_cached[pin].is_gpio);
+ if (rv != 0)
+ return (rv);
+ if (sc->pin_cached[pin].is_gpio == 0)
+ return (EINVAL);
+ }
- RK_GPIO_LOCK(sc);
+ if (__predict_false((flags & GPIO_PIN_INPUT) && ((flags & GPIO_FLAGS_PINCTRL) != sc->pin_cached[pin].flags))) {
+ rv = FDT_PINCTRL_SET_FLAGS(sc->pinctrl, dev, pin, flags);
+ sc->pin_cached[pin].flags = flags & GPIO_FLAGS_PINCTRL;
+ if (rv != 0)
+ return (rv);
+ }
- reg = RK_GPIO_READ(sc, RK_GPIO_SWPORTA_DDR);
+ RK_GPIO_LOCK(sc);
if (flags & GPIO_PIN_INPUT)
- reg &= ~(1 << pin);
+ sc->swporta_ddr &= ~(1 << pin);
else if (flags & GPIO_PIN_OUTPUT)
- reg |= (1 << pin);
+ sc->swporta_ddr |= (1 << pin);
- RK_GPIO_WRITE(sc, RK_GPIO_SWPORTA_DDR, reg);
+ rk_gpio_write_4(sc, RK_GPIO_SWPORTA_DDR, sc->swporta_ddr);
RK_GPIO_UNLOCK(sc);
return (0);
@@ -304,16 +518,16 @@ static int
rk_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
{
struct rk_gpio_softc *sc;
- uint32_t reg;
sc = device_get_softc(dev);
+ if (pin >= RK_GPIO_MAX_PINS)
+ return (EINVAL);
+
RK_GPIO_LOCK(sc);
- reg = RK_GPIO_READ(sc, RK_GPIO_EXT_PORTA);
+ *val = rk_gpio_read_bit(sc, RK_GPIO_EXT_PORTA, pin);
RK_GPIO_UNLOCK(sc);
- *val = reg & (1 << pin) ? 1 : 0;
-
return (0);
}
@@ -321,17 +535,18 @@ static int
rk_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
{
struct rk_gpio_softc *sc;
- uint32_t reg;
sc = device_get_softc(dev);
+ if (pin >= RK_GPIO_MAX_PINS)
+ return (EINVAL);
+
RK_GPIO_LOCK(sc);
- reg = RK_GPIO_READ(sc, RK_GPIO_SWPORTA_DR);
if (value)
- reg |= (1 << pin);
+ sc->swporta |= (1 << pin);
else
- reg &= ~(1 << pin);
- RK_GPIO_WRITE(sc, RK_GPIO_SWPORTA_DR, reg);
+ sc->swporta &= ~(1 << pin);
+ rk_gpio_write_4(sc, RK_GPIO_SWPORTA_DR, sc->swporta);
RK_GPIO_UNLOCK(sc);
return (0);
@@ -341,17 +556,18 @@ static int
rk_gpio_pin_toggle(device_t dev, uint32_t pin)
{
struct rk_gpio_softc *sc;
- uint32_t reg;
sc = device_get_softc(dev);
+ if (pin >= RK_GPIO_MAX_PINS)
+ return (EINVAL);
+
RK_GPIO_LOCK(sc);
- reg = RK_GPIO_READ(sc, RK_GPIO_SWPORTA_DR);
- if (reg & (1 << pin))
- reg &= ~(1 << pin);
+ if (sc->swporta & (1 << pin))
+ sc->swporta &= ~(1 << pin);
else
- reg |= (1 << pin);
- RK_GPIO_WRITE(sc, RK_GPIO_SWPORTA_DR, reg);
+ sc->swporta |= (1 << pin);
+ rk_gpio_write_4(sc, RK_GPIO_SWPORTA_DR, sc->swporta);
RK_GPIO_UNLOCK(sc);
return (0);
@@ -367,13 +583,14 @@ rk_gpio_pin_access_32(device_t dev, uint32_t first_pin, uint32_t clear_pins,
sc = device_get_softc(dev);
RK_GPIO_LOCK(sc);
- reg = RK_GPIO_READ(sc, RK_GPIO_SWPORTA_DR);
+ reg = rk_gpio_read_4(sc, RK_GPIO_SWPORTA_DR);
if (orig_pins)
*orig_pins = reg;
+ sc->swporta = reg;
if ((clear_pins | change_pins) != 0) {
reg = (reg & ~clear_pins) ^ change_pins;
- RK_GPIO_WRITE(sc, RK_GPIO_SWPORTA_DR, reg);
+ rk_gpio_write_4(sc, RK_GPIO_SWPORTA_DR, reg);
}
RK_GPIO_UNLOCK(sc);
@@ -406,10 +623,11 @@ rk_gpio_pin_config_32(device_t dev, uint32_t first_pin, uint32_t num_pins,
}
RK_GPIO_LOCK(sc);
- reg = RK_GPIO_READ(sc, RK_GPIO_SWPORTA_DDR);
+ reg = rk_gpio_read_4(sc, RK_GPIO_SWPORTA_DDR);
reg &= ~mask;
reg |= set;
- RK_GPIO_WRITE(sc, RK_GPIO_SWPORTA_DDR, reg);
+ rk_gpio_write_4(sc, RK_GPIO_SWPORTA_DDR, reg);
+ sc->swporta_ddr = reg;
RK_GPIO_UNLOCK(sc);
return (0);
@@ -434,6 +652,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),
@@ -454,6 +793,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),