Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/arch/arm/sunxi Add support for interrupt pins.
details: https://anonhg.NetBSD.org/src/rev/1956b6683adc
branches: trunk
changeset: 355983:1956b6683adc
user: jmcneill <jmcneill%NetBSD.org@localhost>
date: Sat Aug 26 17:59:24 2017 +0000
description:
Add support for interrupt pins.
diffstat:
sys/arch/arm/sunxi/sunxi_gpio.c | 215 +++++++++++++++++++++++++++++++++++++++-
1 files changed, 213 insertions(+), 2 deletions(-)
diffs (282 lines):
diff -r e2d2baba0fa2 -r 1956b6683adc sys/arch/arm/sunxi/sunxi_gpio.c
--- a/sys/arch/arm/sunxi/sunxi_gpio.c Sat Aug 26 15:06:53 2017 +0000
+++ b/sys/arch/arm/sunxi/sunxi_gpio.c Sat Aug 26 17:59:24 2017 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: sunxi_gpio.c,v 1.11 2017/08/25 00:07:03 jmcneill Exp $ */
+/* $NetBSD: sunxi_gpio.c,v 1.12 2017/08/26 17:59:24 jmcneill Exp $ */
/*-
* Copyright (c) 2017 Jared McNeill <jmcneill%invisible.ca@localhost>
@@ -29,7 +29,7 @@
#include "opt_soc.h"
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sunxi_gpio.c,v 1.11 2017/08/25 00:07:03 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sunxi_gpio.c,v 1.12 2017/08/26 17:59:24 jmcneill Exp $");
#include <sys/param.h>
#include <sys/bus.h>
@@ -39,12 +39,15 @@
#include <sys/mutex.h>
#include <sys/kmem.h>
#include <sys/gpio.h>
+#include <sys/bitops.h>
#include <dev/fdt/fdtvar.h>
#include <dev/gpio/gpiovar.h>
#include <arm/sunxi/sunxi_gpio.h>
+#define SUNXI_GPIO_MAX_EINT 32
+
#define SUNXI_GPIO_PORT(port) (0x24 * (port))
#define SUNXI_GPIO_CFG(port, pin) (SUNXI_GPIO_PORT(port) + 0x00 + (0x4 * ((pin) / 8)))
#define SUNXI_GPIO_CFG_PINMASK(pin) (0x7 << (((pin) % 8) * 4))
@@ -56,6 +59,15 @@
#define SUNXI_GPIO_PULL_UP 1
#define SUNXI_GPIO_PULL_DOWN 2
#define SUNXI_GPIO_PULL_PINMASK(pin) (0x3 << (((pin) % 16) * 2))
+#define SUNXI_GPIO_INT_CFG(eint) (0x200 + (0x4 * ((eint) / 8)))
+#define SUNXI_GPIO_INT_MODEMASK(eint) (0xf << (((eint) % 8) * 4))
+#define SUNXI_GPIO_INT_MODE_POS_EDGE 0x0
+#define SUNXI_GPIO_INT_MODE_NEG_EDGE 0x1
+#define SUNXI_GPIO_INT_MODE_HIGH_LEVEL 0x2
+#define SUNXI_GPIO_INT_MODE_LOW_LEVEL 0x3
+#define SUNXI_GPIO_INT_MODE_DOUBLE_EDGE 0x4
+#define SUNXI_GPIO_INT_CTL 0x210
+#define SUNXI_GPIO_INT_STATUS 0x214
static const struct of_compat_data compat_data[] = {
#ifdef SOC_SUN5I_A13
@@ -80,6 +92,13 @@
{ NULL }
};
+struct sunxi_gpio_eint {
+ int (*eint_func)(void *);
+ void *eint_arg;
+ int eint_flags;
+ int eint_num;
+};
+
struct sunxi_gpio_softc {
device_t sc_dev;
bus_space_tag_t sc_bst;
@@ -90,6 +109,9 @@
struct gpio_chipset_tag sc_gp;
gpio_pin_t *sc_pins;
device_t sc_gpiodev;
+
+ void *sc_ih;
+ struct sunxi_gpio_eint sc_eint[SUNXI_GPIO_MAX_EINT];
};
struct sunxi_gpio_pin {
@@ -353,6 +375,175 @@
.write = sunxi_gpio_write,
};
+static int
+sunxi_gpio_intr(void *priv)
+{
+ struct sunxi_gpio_softc * const sc = priv;
+ struct sunxi_gpio_eint *eint;
+ uint32_t status, bit;
+ int ret = 0;
+
+ status = GPIO_READ(sc, SUNXI_GPIO_INT_STATUS);
+ GPIO_WRITE(sc, SUNXI_GPIO_INT_STATUS, status);
+
+ while ((bit = ffs32(status)) != 0) {
+ status &= ~__BIT(bit - 1);
+ eint = &sc->sc_eint[bit - 1];
+ if (eint->eint_func == NULL)
+ continue;
+ const bool mpsafe = (eint->eint_flags & FDT_INTR_MPSAFE) != 0;
+ if (!mpsafe)
+ KERNEL_LOCK(1, curlwp);
+ ret |= eint->eint_func(eint->eint_arg);
+ if (!mpsafe)
+ KERNEL_UNLOCK_ONE(curlwp);
+ }
+
+ return ret;
+}
+
+static void *
+sunxi_gpio_establish(device_t dev, u_int *specifier, int ipl, int flags,
+ int (*func)(void *), void *arg)
+{
+ struct sunxi_gpio_softc * const sc = device_private(dev);
+ const struct sunxi_gpio_pins *pin_def;
+ struct sunxi_gpio_eint *eint;
+ uint32_t val;
+ u_int mode;
+
+ if (ipl != IPL_VM) {
+ aprint_error_dev(dev, "%s: wrong IPL %d (expected %d)\n",
+ __func__, ipl, IPL_VM);
+ return NULL;
+ }
+
+ /* 1st cell is the bank */
+ /* 2nd cell is the pin */
+ /* 3rd cell is flags */
+ const u_int port = be32toh(specifier[0]);
+ const u_int pin = be32toh(specifier[1]);
+ const u_int type = be32toh(specifier[2]) & 0xf;
+
+ switch (type) {
+ case 0x1:
+ mode = SUNXI_GPIO_INT_MODE_POS_EDGE;
+ break;
+ case 0x2:
+ mode = SUNXI_GPIO_INT_MODE_NEG_EDGE;
+ break;
+ case 0x3:
+ mode = SUNXI_GPIO_INT_MODE_DOUBLE_EDGE;
+ break;
+ case 0x4:
+ mode = SUNXI_GPIO_INT_MODE_HIGH_LEVEL;
+ break;
+ case 0x8:
+ mode = SUNXI_GPIO_INT_MODE_LOW_LEVEL;
+ break;
+ default:
+ aprint_error_dev(dev, "%s: unsupported irq type 0x%x\n",
+ __func__, type);
+ return NULL;
+ }
+
+ pin_def = sunxi_gpio_lookup(sc, port, pin);
+ if (pin_def == NULL)
+ return NULL;
+ if (pin_def->functions[pin_def->eint_func] == NULL ||
+ strcmp(pin_def->functions[pin_def->eint_func], "eint") != 0)
+ return NULL;
+
+ KASSERT(pin_def->eint_num < SUNXI_GPIO_MAX_EINT);
+
+ mutex_enter(&sc->sc_lock);
+
+ eint = &sc->sc_eint[pin_def->eint_num];
+ if (eint->eint_func != NULL) {
+ mutex_exit(&sc->sc_lock);
+ return NULL; /* in use */
+ }
+
+ /* Set function */
+ if (sunxi_gpio_setfunc(sc, pin_def, "eint") != 0) {
+ mutex_exit(&sc->sc_lock);
+ return NULL;
+ }
+
+ eint->eint_func = func;
+ eint->eint_arg = arg;
+ eint->eint_flags = flags;
+ eint->eint_num = pin_def->eint_num;
+
+ /* Configure eint mode */
+ val = GPIO_READ(sc, SUNXI_GPIO_INT_CFG(eint->eint_num));
+ val &= ~SUNXI_GPIO_INT_MODEMASK(eint->eint_num);
+ val |= __SHIFTIN(mode, SUNXI_GPIO_INT_MODEMASK(eint->eint_num));
+ GPIO_WRITE(sc, SUNXI_GPIO_INT_CFG(eint->eint_num), val);
+
+ /* Enable eint */
+ val = GPIO_READ(sc, SUNXI_GPIO_INT_CTL);
+ val |= __BIT(eint->eint_num);
+ GPIO_WRITE(sc, SUNXI_GPIO_INT_CTL, val);
+
+ mutex_exit(&sc->sc_lock);
+
+ return eint;
+}
+
+static void
+sunxi_gpio_disestablish(device_t dev, void *ih)
+{
+ struct sunxi_gpio_softc * const sc = device_private(dev);
+ struct sunxi_gpio_eint * const eint = ih;
+ uint32_t val;
+
+ KASSERT(eint->eint_func != NULL);
+
+ mutex_enter(&sc->sc_lock);
+
+ /* Disable eint */
+ val = GPIO_READ(sc, SUNXI_GPIO_INT_CTL);
+ val &= ~__BIT(eint->eint_num);
+ GPIO_WRITE(sc, SUNXI_GPIO_INT_CTL, val);
+ GPIO_WRITE(sc, SUNXI_GPIO_INT_STATUS, __BIT(eint->eint_num));
+
+ eint->eint_func = NULL;
+ eint->eint_arg = NULL;
+ eint->eint_flags = 0;
+
+ mutex_exit(&sc->sc_lock);
+}
+
+static bool
+sunxi_gpio_intrstr(device_t dev, u_int *specifier, char *buf, size_t buflen)
+{
+ struct sunxi_gpio_softc * const sc = device_private(dev);
+ const struct sunxi_gpio_pins *pin_def;
+
+ /* 1st cell is the bank */
+ /* 2nd cell is the pin */
+ /* 3rd cell is flags */
+ if (!specifier)
+ return false;
+ const u_int port = be32toh(specifier[0]);
+ const u_int pin = be32toh(specifier[1]);
+
+ pin_def = sunxi_gpio_lookup(sc, port, pin);
+ if (pin_def == NULL)
+ return false;
+
+ snprintf(buf, buflen, "GPIO %s", pin_def->name);
+
+ return true;
+}
+
+static struct fdtbus_interrupt_controller_func sunxi_gpio_intrfuncs = {
+ .establish = sunxi_gpio_establish,
+ .disestablish = sunxi_gpio_disestablish,
+ .intrstr = sunxi_gpio_intrstr,
+};
+
static const char *
sunxi_pinctrl_parse_function(int phandle)
{
@@ -586,6 +777,7 @@
struct sunxi_gpio_softc * const sc = device_private(self);
struct fdt_attach_args * const faa = aux;
const int phandle = faa->faa_phandle;
+ char intrstr[128];
struct fdtbus_reset *rst;
struct clk *clk;
bus_addr_t addr;
@@ -632,4 +824,23 @@
fdtbus_pinctrl_configure();
sunxi_gpio_attach_ports(sc);
+
+ /* Disable all external interrupts */
+ GPIO_WRITE(sc, SUNXI_GPIO_INT_CTL, 0);
+ GPIO_WRITE(sc, SUNXI_GPIO_INT_STATUS, GPIO_READ(sc, SUNXI_GPIO_INT_STATUS));
+
+ if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
+ aprint_error_dev(self, "failed to decode interrupt\n");
+ return;
+ }
+ sc->sc_ih = fdtbus_intr_establish(phandle, 0, IPL_VM, FDT_INTR_MPSAFE,
+ sunxi_gpio_intr, sc);
+ if (sc->sc_ih == NULL) {
+ aprint_error_dev(self, "failed to establish interrupt on %s\n",
+ intrstr);
+ return;
+ }
+ aprint_normal_dev(self, "interrupting on %s\n", intrstr);
+ fdtbus_register_interrupt_controller(self, phandle,
+ &sunxi_gpio_intrfuncs);
}
Home |
Main Index |
Thread Index |
Old Index