Module Name:    src
Committed By:   tnn
Date:           Thu May 30 18:19:36 UTC 2019

Modified Files:
        src/sys/arch/arm/sunxi: sunxi_gpio.c

Log Message:
sunxi_gpio: implement the interrupt API in gpio_chipset_tag

- advertise GPIO pins as interrupt capable via pin_intrcaps
- split establish/disestablish code into fdt attach specific,
  gpio attach specific and shared parts, similar to the BCM2835 GPIO driver


To generate a diff of this commit:
cvs rdiff -u -r1.24 -r1.25 src/sys/arch/arm/sunxi/sunxi_gpio.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/arm/sunxi/sunxi_gpio.c
diff -u src/sys/arch/arm/sunxi/sunxi_gpio.c:1.24 src/sys/arch/arm/sunxi/sunxi_gpio.c:1.25
--- src/sys/arch/arm/sunxi/sunxi_gpio.c:1.24	Mon May 27 23:26:20 2019
+++ src/sys/arch/arm/sunxi/sunxi_gpio.c	Thu May 30 18:19:36 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: sunxi_gpio.c,v 1.24 2019/05/27 23:26:20 jmcneill Exp $ */
+/* $NetBSD: sunxi_gpio.c,v 1.25 2019/05/30 18:19:36 tnn Exp $ */
 
 /*-
  * Copyright (c) 2017 Jared McNeill <jmcne...@invisible.ca>
@@ -29,7 +29,7 @@
 #include "opt_soc.h"
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sunxi_gpio.c,v 1.24 2019/05/27 23:26:20 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sunxi_gpio.c,v 1.25 2019/05/30 18:19:36 tnn Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -120,7 +120,7 @@ static const struct of_compat_data compa
 struct sunxi_gpio_eint {
 	int (*eint_func)(void *);
 	void *eint_arg;
-	int eint_flags;
+	bool eint_mpsafe;
 	int eint_bank;
 	int eint_num;
 };
@@ -429,11 +429,10 @@ sunxi_gpio_intr(void *priv)
 			eint = &sc->sc_eint[bank][bit - 1];
 			if (eint->eint_func == NULL)
 				continue;
-			const bool mpsafe = (eint->eint_flags & FDT_INTR_MPSAFE) != 0;
-			if (!mpsafe)
+			if (!eint->eint_mpsafe)
 				KERNEL_LOCK(1, curlwp);
 			ret |= eint->eint_func(eint->eint_arg);
-			if (!mpsafe)
+			if (!eint->eint_mpsafe)
 				KERNEL_UNLOCK_ONE(curlwp);
 		}
 	}
@@ -442,53 +441,13 @@ sunxi_gpio_intr(void *priv)
 }
 
 static void *
-sunxi_gpio_establish(device_t dev, u_int *specifier, int ipl, int flags,
+sunxi_intr_enable(struct sunxi_gpio_softc *sc,
+    const struct sunxi_gpio_pins *pin_def, u_int mode, bool mpsafe,
     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 FDT_INTR_TYPE_POS_EDGE:
-		mode = SUNXI_GPIO_INT_MODE_POS_EDGE;
-		break;
-	case FDT_INTR_TYPE_NEG_EDGE:
-		mode = SUNXI_GPIO_INT_MODE_NEG_EDGE;
-		break;
-	case FDT_INTR_TYPE_DOUBLE_EDGE:
-		mode = SUNXI_GPIO_INT_MODE_DOUBLE_EDGE;
-		break;
-	case FDT_INTR_TYPE_HIGH_LEVEL:
-		mode = SUNXI_GPIO_INT_MODE_HIGH_LEVEL;
-		break;
-	case FDT_INTR_TYPE_LOW_LEVEL:
-		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;
+	struct sunxi_gpio_eint *eint;
+	
 	if (pin_def->functions[pin_def->eint_func] == NULL ||
 	    strcmp(pin_def->functions[pin_def->eint_func], "irq") != 0)
 		return NULL;
@@ -511,7 +470,7 @@ sunxi_gpio_establish(device_t dev, u_int
 
 	eint->eint_func = func;
 	eint->eint_arg = arg;
-	eint->eint_flags = flags;
+	eint->eint_mpsafe = mpsafe;
 	eint->eint_bank = pin_def->eint_bank;
 	eint->eint_num = pin_def->eint_num;
 
@@ -532,10 +491,8 @@ sunxi_gpio_establish(device_t dev, u_int
 }
 
 static void
-sunxi_gpio_disestablish(device_t dev, void *ih)
+sunxi_intr_disable(struct sunxi_gpio_softc *sc, struct sunxi_gpio_eint *eint)
 {
-	struct sunxi_gpio_softc * const sc = device_private(dev);
-	struct sunxi_gpio_eint * const eint = ih;
 	uint32_t val;
 
 	KASSERT(eint->eint_func != NULL);
@@ -550,13 +507,73 @@ sunxi_gpio_disestablish(device_t dev, vo
 
 	eint->eint_func = NULL;
 	eint->eint_arg = NULL;
-	eint->eint_flags = 0;
+	eint->eint_mpsafe = false;
 
 	mutex_exit(&sc->sc_lock);
 }
 
+static void *
+sunxi_fdt_intr_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);
+	bool mpsafe = (flags & FDT_INTR_MPSAFE) != 0;
+	const struct sunxi_gpio_pins *pin_def;
+	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 FDT_INTR_TYPE_POS_EDGE:
+		mode = SUNXI_GPIO_INT_MODE_POS_EDGE;
+		break;
+	case FDT_INTR_TYPE_NEG_EDGE:
+		mode = SUNXI_GPIO_INT_MODE_NEG_EDGE;
+		break;
+	case FDT_INTR_TYPE_DOUBLE_EDGE:
+		mode = SUNXI_GPIO_INT_MODE_DOUBLE_EDGE;
+		break;
+	case FDT_INTR_TYPE_HIGH_LEVEL:
+		mode = SUNXI_GPIO_INT_MODE_HIGH_LEVEL;
+		break;
+	case FDT_INTR_TYPE_LOW_LEVEL:
+		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;
+
+	return sunxi_intr_enable(sc, pin_def, mode, mpsafe, func, arg);
+}
+
+static void
+sunxi_fdt_intr_disestablish(device_t dev, void *ih)
+{
+	struct sunxi_gpio_softc * const sc = device_private(dev);
+	struct sunxi_gpio_eint * const eint = ih;
+
+	sunxi_intr_disable(sc, eint);
+}
+
 static bool
-sunxi_gpio_intrstr(device_t dev, u_int *specifier, char *buf, size_t buflen)
+sunxi_fdt_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;
@@ -579,11 +596,74 @@ sunxi_gpio_intrstr(device_t dev, u_int *
 }
 
 static struct fdtbus_interrupt_controller_func sunxi_gpio_intrfuncs = {
-	.establish = sunxi_gpio_establish,
-	.disestablish = sunxi_gpio_disestablish,
-	.intrstr = sunxi_gpio_intrstr,
+	.establish = sunxi_fdt_intr_establish,
+	.disestablish = sunxi_fdt_intr_disestablish,
+	.intrstr = sunxi_fdt_intrstr,
 };
 
+static void *
+sunxi_gpio_intr_establish(void *vsc, int pin, int ipl, int irqmode,
+    int (*func)(void *), void *arg)
+{
+	struct sunxi_gpio_softc * const sc = vsc;
+	bool mpsafe = (irqmode & GPIO_INTR_MPSAFE) != 0;
+	int type = irqmode & GPIO_INTR_MODE_MASK;
+	const struct sunxi_gpio_pins *pin_def;
+	u_int mode;
+
+	switch (type) {
+	case GPIO_INTR_POS_EDGE:
+		mode = SUNXI_GPIO_INT_MODE_POS_EDGE;
+		break;
+	case GPIO_INTR_NEG_EDGE:
+		mode = SUNXI_GPIO_INT_MODE_NEG_EDGE;
+		break;
+	case GPIO_INTR_DOUBLE_EDGE:
+		mode = SUNXI_GPIO_INT_MODE_DOUBLE_EDGE;
+		break;
+	case GPIO_INTR_HIGH_LEVEL:
+		mode = SUNXI_GPIO_INT_MODE_HIGH_LEVEL;
+		break;
+	case GPIO_INTR_LOW_LEVEL:
+		mode = SUNXI_GPIO_INT_MODE_LOW_LEVEL;
+		break;
+	default:
+		aprint_error_dev(sc->sc_dev, "%s: unsupported irq type 0x%x\n",
+				 __func__, type);
+		return NULL;
+	}
+
+	if (pin < 0 || pin >= sc->sc_padconf->npins)
+		return NULL;
+	pin_def = &sc->sc_padconf->pins[pin];
+
+	return sunxi_intr_enable(sc, pin_def, mode, mpsafe, func, arg);
+}
+
+static void
+sunxi_gpio_intr_disestablish(void *vsc, void *ih)
+{
+	struct sunxi_gpio_softc * const sc = vsc;
+	struct sunxi_gpio_eint * const eint = ih;
+
+	sunxi_intr_disable(sc, eint);
+}
+
+static bool
+sunxi_gpio_intrstr(void *vsc, int pin, int irqmode, char *buf, size_t buflen)
+{
+	struct sunxi_gpio_softc * const sc = vsc;
+	const struct sunxi_gpio_pins *pin_def;
+
+	if (pin < 0 || pin >= sc->sc_padconf->npins)
+		return NULL;
+	pin_def = &sc->sc_padconf->pins[pin];
+
+	snprintf(buf, buflen, "GPIO %s", pin_def->name);
+
+	return true;
+}
+
 static const char *
 sunxi_pinctrl_parse_function(int phandle)
 {
@@ -840,6 +920,9 @@ sunxi_gpio_attach_ports(struct sunxi_gpi
 	gp->gp_pin_read = sunxi_gpio_pin_read;
 	gp->gp_pin_write = sunxi_gpio_pin_write;
 	gp->gp_pin_ctl = sunxi_gpio_pin_ctl;
+	gp->gp_intr_establish = sunxi_gpio_intr_establish;
+	gp->gp_intr_disestablish = sunxi_gpio_intr_disestablish;
+	gp->gp_intr_str = sunxi_gpio_intrstr;
 
 	const u_int npins = sc->sc_padconf->npins;
 	sc->sc_pins = kmem_zalloc(sizeof(*sc->sc_pins) * npins, KM_SLEEP);
@@ -849,6 +932,13 @@ sunxi_gpio_attach_ports(struct sunxi_gpi
 		sc->sc_pins[pin].pin_num = pin;
 		sc->sc_pins[pin].pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
 		    GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN;
+		if (pin_def->functions[pin_def->eint_func] != NULL &&
+		    strcmp(pin_def->functions[pin_def->eint_func], "irq") == 0) {
+			sc->sc_pins[pin].pin_intrcaps =
+			    GPIO_INTR_POS_EDGE | GPIO_INTR_NEG_EDGE |
+			    GPIO_INTR_HIGH_LEVEL | GPIO_INTR_LOW_LEVEL |
+			    GPIO_INTR_DOUBLE_EDGE | GPIO_INTR_MPSAFE;
+		}
 		sc->sc_pins[pin].pin_state = sunxi_gpio_pin_read(sc, pin);
 		strlcpy(sc->sc_pins[pin].pin_defname, pin_def->name,
 		    sizeof(sc->sc_pins[pin].pin_defname));

Reply via email to