Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/arch/arm/ti Add support for GPIO interrupts and fix read...
details: https://anonhg.NetBSD.org/src/rev/a7eb2c6ed245
branches: trunk
changeset: 966544:a7eb2c6ed245
user: jmcneill <jmcneill%NetBSD.org@localhost>
date: Sun Nov 03 11:34:40 2019 +0000
description:
Add support for GPIO interrupts and fix reading the state of output pins.
diffstat:
sys/arch/arm/ti/ti_gpio.c | 267 ++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 247 insertions(+), 20 deletions(-)
diffs (truncated from 363 to 300 lines):
diff -r 76b862f49655 -r a7eb2c6ed245 sys/arch/arm/ti/ti_gpio.c
--- a/sys/arch/arm/ti/ti_gpio.c Sun Nov 03 11:13:45 2019 +0000
+++ b/sys/arch/arm/ti/ti_gpio.c Sun Nov 03 11:34:40 2019 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: ti_gpio.c,v 1.2 2019/10/29 22:19:13 jmcneill Exp $ */
+/* $NetBSD: ti_gpio.c,v 1.3 2019/11/03 11:34:40 jmcneill Exp $ */
/*-
* Copyright (c) 2019 Jared McNeill <jmcneill%invisible.ca@localhost>
@@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ti_gpio.c,v 1.2 2019/10/29 22:19:13 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ti_gpio.c,v 1.3 2019/11/03 11:34:40 jmcneill Exp $");
#include <sys/param.h>
#include <sys/bus.h>
@@ -44,27 +44,87 @@
#include <arm/ti/ti_prcm.h>
-#define GPIO_OE 0x34
-#define GPIO_DATAIN 0x38
-#define GPIO_CLEARDATAOUT 0x90
-#define GPIO_SETDATAOUT 0x94
+#define TI_GPIO_NPINS 32
+
+enum ti_gpio_type {
+ TI_GPIO_OMAP3,
+ TI_GPIO_OMAP4,
+ TI_NGPIO
+};
+
+enum {
+ GPIO_IRQSTATUS1,
+ GPIO_IRQENABLE1, /* OMAP3 */
+ GPIO_IRQENABLE1_SET, /* OMAP4 */
+ GPIO_IRQENABLE1_CLR, /* OMAP4 */
+ GPIO_OE,
+ GPIO_DATAIN,
+ GPIO_DATAOUT,
+ GPIO_LEVELDETECT0,
+ GPIO_LEVELDETECT1,
+ GPIO_RISINGDETECT,
+ GPIO_FALLINGDETECT,
+ GPIO_CLEARDATAOUT,
+ GPIO_SETDATAOUT,
+ GPIO_NREG
+};
+
+static const u_int ti_gpio_regmap[TI_NGPIO][GPIO_NREG] = {
+ [TI_GPIO_OMAP3] = {
+ [GPIO_IRQSTATUS1] = 0x18,
+ [GPIO_IRQENABLE1] = 0x1c,
+ [GPIO_OE] = 0x34,
+ [GPIO_DATAIN] = 0x38,
+ [GPIO_DATAOUT] = 0x3c,
+ [GPIO_LEVELDETECT0] = 0x40,
+ [GPIO_LEVELDETECT1] = 0x44,
+ [GPIO_RISINGDETECT] = 0x48,
+ [GPIO_FALLINGDETECT] = 0x4c,
+ [GPIO_CLEARDATAOUT] = 0x90,
+ [GPIO_SETDATAOUT] = 0x94,
+ },
+ [TI_GPIO_OMAP4] = {
+ [GPIO_IRQSTATUS1] = 0x2c,
+ [GPIO_IRQENABLE1_SET] = 0x34,
+ [GPIO_IRQENABLE1_CLR] = 0x38,
+ [GPIO_OE] = 0x134,
+ [GPIO_DATAIN] = 0x138,
+ [GPIO_DATAOUT] = 0x13c,
+ [GPIO_LEVELDETECT0] = 0x140,
+ [GPIO_LEVELDETECT1] = 0x144,
+ [GPIO_RISINGDETECT] = 0x148,
+ [GPIO_FALLINGDETECT] = 0x14c,
+ [GPIO_CLEARDATAOUT] = 0x190,
+ [GPIO_SETDATAOUT] = 0x194,
+ },
+};
static const struct of_compat_data compat_data[] = {
- /* compatible reg offset */
- { "ti,omap3-gpio", 0x0 },
- { "ti,omap4-gpio", 0x100 },
+ { "ti,omap3-gpio", TI_GPIO_OMAP3 },
+ { "ti,omap4-gpio", TI_GPIO_OMAP4 },
{ NULL }
};
+struct ti_gpio_intr {
+ u_int intr_pin;
+ int (*intr_func)(void *);
+ void *intr_arg;
+ bool intr_mpsafe;
+};
+
struct ti_gpio_softc {
device_t sc_dev;
bus_space_tag_t sc_bst;
bus_space_handle_t sc_bsh;
kmutex_t sc_lock;
- bus_size_t sc_regoff;
+ enum ti_gpio_type sc_type;
+ const char *sc_modname;
+ void *sc_ih;
struct gpio_chipset_tag sc_gp;
- gpio_pin_t sc_pins[32];
+ gpio_pin_t sc_pins[TI_GPIO_NPINS];
+ bool sc_pinout[TI_GPIO_NPINS];
+ struct ti_gpio_intr sc_intr[TI_GPIO_NPINS];
device_t sc_gpiodev;
};
@@ -76,9 +136,9 @@
};
#define RD4(sc, reg) \
- bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg) + (sc)->sc_regoff)
+ bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, ti_gpio_regmap[(sc)->sc_type][(reg)])
#define WR4(sc, reg, val) \
- bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg) + (sc)->sc_regoff, (val))
+ bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, ti_gpio_regmap[(sc)->sc_type][(reg)], (val))
static int ti_gpio_match(device_t, cfdata_t, void *);
static void ti_gpio_attach(device_t, device_t, void *);
@@ -100,6 +160,8 @@
oe &= ~__BIT(pin);
WR4(sc, GPIO_OE, oe);
+ sc->sc_pinout[pin] = (flags & GPIO_PIN_OUTPUT) != 0;
+
return 0;
}
@@ -162,7 +224,10 @@
const uint32_t data_mask = __BIT(pin->pin_nr);
/* No lock required for reads */
- data = RD4(sc, GPIO_DATAIN);
+ if (sc->sc_pinout[pin->pin_nr])
+ data = RD4(sc, GPIO_DATAOUT);
+ else
+ data = RD4(sc, GPIO_DATAIN);
val = __SHIFTOUT(data, data_mask);
if (!raw && pin->pin_actlo)
val = !val;
@@ -195,6 +260,126 @@
.write = ti_gpio_write,
};
+static void
+ti_gpio_intr_disestablish(device_t dev, void *ih)
+{
+ struct ti_gpio_softc * const sc = device_private(dev);
+ struct ti_gpio_intr *intr = ih;
+ const u_int pin = intr->intr_pin;
+ const uint32_t pin_mask = __BIT(pin);
+ uint32_t val;
+
+ /* Disable interrupts */
+ if (sc->sc_type == TI_GPIO_OMAP3) {
+ val = RD4(sc, GPIO_IRQENABLE1);
+ WR4(sc, GPIO_IRQENABLE1, val & ~pin_mask);
+ } else {
+ WR4(sc, GPIO_IRQENABLE1_CLR, pin_mask);
+ }
+
+ intr->intr_func = NULL;
+ intr->intr_arg = NULL;
+}
+
+static void *
+ti_gpio_intr_establish(device_t dev, u_int *specifier, int ipl, int flags,
+ int (*func)(void *), void *arg)
+{
+ struct ti_gpio_softc * const sc = device_private(dev);
+ uint32_t val;
+
+ /* 1st cell is the pin */
+ /* 2nd cell is flags */
+ const u_int pin = be32toh(specifier[0]);
+ const u_int type = be32toh(specifier[2]) & 0xf;
+
+ if (ipl != IPL_VM || pin >= __arraycount(sc->sc_pins))
+ return NULL;
+
+ /*
+ * Enabling both high and low level triggers will cause the GPIO
+ * controller to always assert the interrupt.
+ */
+ if ((type & (FDT_INTR_TYPE_LOW_LEVEL|FDT_INTR_TYPE_HIGH_LEVEL)) ==
+ (FDT_INTR_TYPE_LOW_LEVEL|FDT_INTR_TYPE_HIGH_LEVEL))
+ return NULL;
+
+ if (sc->sc_intr[pin].intr_func != NULL)
+ return NULL;
+
+ /* Set pin as input */
+ if (ti_gpio_ctl(sc, pin, GPIO_PIN_INPUT) != 0)
+ return NULL;
+
+ sc->sc_intr[pin].intr_pin = pin;
+ sc->sc_intr[pin].intr_func = func;
+ sc->sc_intr[pin].intr_arg = arg;
+ sc->sc_intr[pin].intr_mpsafe = (flags & FDT_INTR_MPSAFE) != 0;
+
+ const uint32_t pin_mask = __BIT(pin);
+
+ /* Configure triggers */
+ val = RD4(sc, GPIO_LEVELDETECT0);
+ if ((type & FDT_INTR_TYPE_LOW_LEVEL) != 0)
+ val |= pin_mask;
+ else
+ val &= ~pin_mask;
+ WR4(sc, GPIO_LEVELDETECT0, val);
+
+ val = RD4(sc, GPIO_LEVELDETECT1);
+ if ((type & FDT_INTR_TYPE_HIGH_LEVEL) != 0)
+ val |= pin_mask;
+ else
+ val &= ~pin_mask;
+ WR4(sc, GPIO_LEVELDETECT1, val);
+
+ val = RD4(sc, GPIO_RISINGDETECT);
+ if ((type & FDT_INTR_TYPE_POS_EDGE) != 0)
+ val |= pin_mask;
+ else
+ val &= ~pin_mask;
+ WR4(sc, GPIO_RISINGDETECT, val);
+
+ val = RD4(sc, GPIO_FALLINGDETECT);
+ if ((type & FDT_INTR_TYPE_NEG_EDGE) != 0)
+ val |= pin_mask;
+ else
+ val &= ~pin_mask;
+ WR4(sc, GPIO_FALLINGDETECT, val);
+
+ /* Enable interrupts */
+ if (sc->sc_type == TI_GPIO_OMAP3) {
+ val = RD4(sc, GPIO_IRQENABLE1);
+ WR4(sc, GPIO_IRQENABLE1, val | pin_mask);
+ } else {
+ WR4(sc, GPIO_IRQENABLE1_SET, pin_mask);
+ }
+
+ return &sc->sc_intr[pin];
+}
+
+static bool
+ti_gpio_intrstr(device_t dev, u_int *specifier, char *buf, size_t buflen)
+{
+ struct ti_gpio_softc * const sc = device_private(dev);
+
+ /* 1st cell is the pin */
+ /* 2nd cell is flags */
+ const u_int pin = be32toh(specifier[0]);
+
+ if (pin >= __arraycount(sc->sc_pins))
+ return false;
+
+ snprintf(buf, buflen, "%s pin %d", sc->sc_modname, pin);
+ return true;
+}
+
+static struct fdtbus_interrupt_controller_func ti_gpio_intrfuncs = {
+ .establish = ti_gpio_intr_establish,
+ .disestablish = ti_gpio_intr_disestablish,
+ .intrstr = ti_gpio_intrstr,
+};
+
static int
ti_gpio_pin_read(void *priv, int pin)
{
@@ -263,6 +448,34 @@
}
static int
+ti_gpio_intr(void *priv)
+{
+ struct ti_gpio_softc * const sc = priv;
+ uint32_t status;
+ u_int bit;
+ int rv = 0;
+
+ status = RD4(sc, GPIO_IRQSTATUS1);
+ WR4(sc, GPIO_IRQSTATUS1, status);
+
+ while ((bit = ffs32(status)) != 0) {
+ const u_int pin = bit - 1;
+ const uint32_t pin_mask = __BIT(pin);
+ struct ti_gpio_intr *intr = &sc->sc_intr[pin];
+ status &= ~pin_mask;
+ if (intr->intr_func == NULL)
+ continue;
+ if (!intr->intr_mpsafe)
+ KERNEL_LOCK(1, curlwp);
+ rv |= intr->intr_func(intr->intr_arg);
+ if (!intr->intr_mpsafe)
Home |
Main Index |
Thread Index |
Old Index