Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 18 Dec 2012 22:18:54 +0000 (UTC)
From:      Oleksandr Tymoshenko <gonzo@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r244412 - head/sys/arm/broadcom/bcm2835
Message-ID:  <201212182218.qBIMIt01041209@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: gonzo
Date: Tue Dec 18 22:18:54 2012
New Revision: 244412
URL: http://svnweb.freebsd.org/changeset/base/244412

Log:
  Add sysctls for changing GPIO pins function
  
  Submitted by:	Luiz Otavio O Souza

Modified:
  head/sys/arm/broadcom/bcm2835/bcm2835_gpio.c

Modified: head/sys/arm/broadcom/bcm2835/bcm2835_gpio.c
==============================================================================
--- head/sys/arm/broadcom/bcm2835/bcm2835_gpio.c	Tue Dec 18 22:10:40 2012	(r244411)
+++ head/sys/arm/broadcom/bcm2835/bcm2835_gpio.c	Tue Dec 18 22:18:54 2012	(r244412)
@@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/lock.h>
 #include <sys/mutex.h>
 #include <sys/gpio.h>
+#include <sys/sysctl.h>
 
 #include <machine/bus.h>
 #include <machine/cpu.h>
@@ -66,6 +67,11 @@ __FBSDID("$FreeBSD$");
 #define	BCM_GPIO_DEFAULT_CAPS	(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |	\
     GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN)
 
+struct bcm_gpio_sysctl {
+	struct bcm_gpio_softc	*sc;
+	uint32_t		pin;
+};
+
 struct bcm_gpio_softc {
 	device_t		sc_dev;
 	struct mtx		sc_mtx;
@@ -78,6 +84,7 @@ struct bcm_gpio_softc {
 	int			sc_ro_npins;
 	int			sc_ro_pins[BCM_GPIO_PINS];
 	struct gpio_pin		sc_gpio_pins[BCM_GPIO_PINS];
+	struct bcm_gpio_sysctl	sc_sysctl[BCM_GPIO_PINS];
 };
 
 enum bcm_gpio_fsel {
@@ -99,6 +106,7 @@ enum bcm_gpio_pud {
 
 #define	BCM_GPIO_LOCK(_sc)	mtx_lock(&_sc->sc_mtx)
 #define	BCM_GPIO_UNLOCK(_sc)	mtx_unlock(&_sc->sc_mtx)
+#define	BCM_GPIO_LOCK_ASSERT(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED)
 
 #define	BCM_GPIO_GPFSEL(_bank)	0x00 + _bank * 4
 #define	BCM_GPIO_GPSET(_bank)	0x1c + _bank * 4
@@ -126,53 +134,89 @@ bcm_gpio_pin_is_ro(struct bcm_gpio_softc
 static uint32_t
 bcm_gpio_get_function(struct bcm_gpio_softc *sc, uint32_t pin)
 {
-	uint32_t bank, data, offset;
+	uint32_t bank, func, offset;
 
 	/* Five banks, 10 pins per bank, 3 bits per pin. */
 	bank = pin / 10;
 	offset = (pin - bank * 10) * 3;
 
 	BCM_GPIO_LOCK(sc);
-	data = (BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank)) >> offset) & 7;
+	func = (BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank)) >> offset) & 7;
 	BCM_GPIO_UNLOCK(sc);
 
-#ifdef	DEBUG
-	device_printf(sc->sc_dev, "pin %d function: ", pin);
-	switch (data) {
+	return (func);
+}
+
+static void
+bcm_gpio_func_str(uint32_t nfunc, char *buf, int bufsize)
+{
+
+	switch (nfunc) {
 	case BCM_GPIO_INPUT:
-		printf("input\n");
+		strncpy(buf, "input", bufsize);
 		break;
 	case BCM_GPIO_OUTPUT:
-		printf("output\n");
+		strncpy(buf, "output", bufsize);
 		break;
 	case BCM_GPIO_ALT0:
-		printf("alt0\n");
+		strncpy(buf, "alt0", bufsize);
 		break;
 	case BCM_GPIO_ALT1:
-		printf("alt1\n");
+		strncpy(buf, "alt1", bufsize);
 		break;
 	case BCM_GPIO_ALT2:
-		printf("alt2\n");
+		strncpy(buf, "alt2", bufsize);
 		break;
 	case BCM_GPIO_ALT3:
-		printf("alt3\n");
+		strncpy(buf, "alt3", bufsize);
 		break;
 	case BCM_GPIO_ALT4:
-		printf("alt4\n");
+		strncpy(buf, "alt4", bufsize);
 		break;
 	case BCM_GPIO_ALT5:
-		printf("alt5\n");
+		strncpy(buf, "alt5", bufsize);
 		break;
+	default:
+		strncpy(buf, "invalid", bufsize);
 	}
-#endif
+}
+
+static int
+bcm_gpio_str_func(char *func, uint32_t *nfunc)
+{
+
+	if (strcasecmp(func, "input") == 0)
+		*nfunc = BCM_GPIO_INPUT;
+	else if (strcasecmp(func, "output") == 0)
+		*nfunc = BCM_GPIO_OUTPUT;
+	else if (strcasecmp(func, "alt0") == 0)
+		*nfunc = BCM_GPIO_ALT0;
+	else if (strcasecmp(func, "alt1") == 0)
+		*nfunc = BCM_GPIO_ALT1;
+	else if (strcasecmp(func, "alt2") == 0)
+		*nfunc = BCM_GPIO_ALT2;
+	else if (strcasecmp(func, "alt3") == 0)
+		*nfunc = BCM_GPIO_ALT3;
+	else if (strcasecmp(func, "alt4") == 0)
+		*nfunc = BCM_GPIO_ALT4;
+	else if (strcasecmp(func, "alt5") == 0)
+		*nfunc = BCM_GPIO_ALT5;
+	else
+		return (-1);
 
-	switch (data) {
+	return (0);
+}
+
+static uint32_t
+bcm_gpio_func_flag(uint32_t nfunc)
+{
+
+	switch (nfunc) {
 	case BCM_GPIO_INPUT:
 		return (GPIO_PIN_INPUT);
 	case BCM_GPIO_OUTPUT:
 		return (GPIO_PIN_OUTPUT);
 	}
-
 	return (0);
 }
 
@@ -181,16 +225,17 @@ bcm_gpio_set_function(struct bcm_gpio_so
 {
 	uint32_t bank, data, offset;
 
+	/* Must be called with lock held. */
+	BCM_GPIO_LOCK_ASSERT(sc);
+
 	/* Five banks, 10 pins per bank, 3 bits per pin. */
 	bank = pin / 10;
 	offset = (pin - bank * 10) * 3;
 
-	BCM_GPIO_LOCK(sc);
 	data = BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank));
 	data &= ~(7 << offset);
 	data |= (f << offset);
 	BCM_GPIO_WRITE(sc, BCM_GPIO_GPFSEL(bank), data);
-	BCM_GPIO_UNLOCK(sc);
 }
 
 static void
@@ -198,17 +243,18 @@ bcm_gpio_set_pud(struct bcm_gpio_softc *
 {
 	uint32_t bank, offset;
 
+	/* Must be called with lock held. */
+	BCM_GPIO_LOCK_ASSERT(sc);
+
 	bank = pin / 32;
 	offset = pin - 32 * bank;
 
-	BCM_GPIO_LOCK(sc);
 	BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), state);
 	DELAY(10);
 	BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), (1 << offset));
 	DELAY(10);
 	BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), 0);
 	BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), 0);
-	BCM_GPIO_UNLOCK(sc);
 }
 
 static void
@@ -216,6 +262,8 @@ bcm_gpio_pin_configure(struct bcm_gpio_s
     unsigned int flags)
 {
 
+	BCM_GPIO_LOCK(sc);
+
 	/*
 	 * Manage input/output.
 	 */
@@ -244,6 +292,8 @@ bcm_gpio_pin_configure(struct bcm_gpio_s
 		}
 	} else 
 		bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_NONE);
+
+	BCM_GPIO_UNLOCK(sc);
 }
 
 static int
@@ -471,7 +521,7 @@ bcm_gpio_get_ro_pins(struct bcm_gpio_sof
 			printf(",");
 		printf("%d", sc->sc_ro_pins[i]);
 	}
-        if (i > 0)
+	if (i > 0)
 		printf(".");
 	printf("\n");
 
@@ -479,6 +529,89 @@ bcm_gpio_get_ro_pins(struct bcm_gpio_sof
 }
 
 static int
+bcm_gpio_func_proc(SYSCTL_HANDLER_ARGS)
+{
+	char buf[16];
+	struct bcm_gpio_softc *sc;
+	struct bcm_gpio_sysctl *sc_sysctl;
+	uint32_t nfunc;
+	int i, error;
+
+	sc_sysctl = arg1;
+	sc = sc_sysctl->sc;
+
+	/* Get the current pin function. */
+	nfunc = bcm_gpio_get_function(sc, sc_sysctl->pin);
+	bcm_gpio_func_str(nfunc, buf, sizeof(buf));
+
+	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+	if (error != 0 || req->newptr == NULL)
+		return (error);
+
+	/* Parse the user supplied string and check for a valid pin function. */
+	if (bcm_gpio_str_func(buf, &nfunc) != 0)
+		return (EINVAL);
+
+	BCM_GPIO_LOCK(sc);
+
+	/* Disable pull-up or pull-down on pin. */
+	bcm_gpio_set_pud(sc, sc_sysctl->pin, BCM_GPIO_NONE);
+
+	/* And now set the pin function. */
+	bcm_gpio_set_function(sc, sc_sysctl->pin, nfunc);
+
+	/* Update the pin flags. */
+	for (i = 0; i < sc->sc_gpio_npins; i++) {
+		if (sc->sc_gpio_pins[i].gp_pin == sc_sysctl->pin)
+			break;
+	}
+	if (i < sc->sc_gpio_npins)
+		sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(nfunc);
+
+	BCM_GPIO_UNLOCK(sc);
+
+	return (0);
+}
+
+static void
+bcm_gpio_sysctl_init(struct bcm_gpio_softc *sc)
+{
+	char pinbuf[3];
+	struct bcm_gpio_sysctl *sc_sysctl;
+	struct sysctl_ctx_list *ctx;
+	struct sysctl_oid *tree_node, *pin_node, *pinN_node;
+	struct sysctl_oid_list *tree, *pin_tree, *pinN_tree;
+	int i;
+
+	/*
+	 * Add per-pin sysctl tree/handlers.
+	 */
+	ctx = device_get_sysctl_ctx(sc->sc_dev);
+ 	tree_node = device_get_sysctl_tree(sc->sc_dev);
+ 	tree = SYSCTL_CHILDREN(tree_node);
+	pin_node = SYSCTL_ADD_NODE(ctx, tree, OID_AUTO, "pin",
+	    CTLFLAG_RW, NULL, "GPIO Pins");
+	pin_tree = SYSCTL_CHILDREN(pin_node);
+
+	for (i = 0; i < sc->sc_gpio_npins; i++) {
+
+		snprintf(pinbuf, sizeof(pinbuf), "%d", i);
+		pinN_node = SYSCTL_ADD_NODE(ctx, pin_tree, OID_AUTO, pinbuf,
+		    CTLFLAG_RD, NULL, "GPIO Pin");
+		pinN_tree = SYSCTL_CHILDREN(pinN_node);
+
+		sc->sc_sysctl[i].sc = sc;
+		sc_sysctl = &sc->sc_sysctl[i];
+		sc_sysctl->sc = sc;
+		sc_sysctl->pin = sc->sc_gpio_pins[i].gp_pin;
+		SYSCTL_ADD_PROC(ctx, pinN_tree, OID_AUTO, "function",
+		    CTLFLAG_RW | CTLTYPE_STRING, sc_sysctl,
+		    sizeof(struct bcm_gpio_sysctl), bcm_gpio_func_proc,
+		    "A", "Pin Function");
+	}
+}
+
+static int
 bcm_gpio_get_reserved_pins(struct bcm_gpio_softc *sc)
 {
 	int i, j, len, npins;
@@ -499,7 +632,7 @@ bcm_gpio_get_reserved_pins(struct bcm_gp
 	 */
 	reserved = 0;
 	while ((node != 0) && (reserved == 0)) {
-        	len = OF_getprop(node, "name", name,
+		len = OF_getprop(node, "name", name,
 		    sizeof(name) - 1);
 		name[len] = 0;
 		if (strcmp(name, "reserved") == 0)
@@ -532,7 +665,7 @@ bcm_gpio_get_reserved_pins(struct bcm_gp
 		sc->sc_ro_pins[j++ + sc->sc_ro_npins] = fdt32_to_cpu(pins[i]);
 	}
 	sc->sc_ro_npins += j;
-        if (i > 0)
+	if (i > 0)
 		printf(".");
 	printf("\n");
 
@@ -553,6 +686,7 @@ static int
 bcm_gpio_attach(device_t dev)
 {
 	struct bcm_gpio_softc *sc = device_get_softc(dev);
+	uint32_t func;
 	int i, j, rid;
 	phandle_t gpio;
 
@@ -600,15 +734,18 @@ bcm_gpio_attach(device_t dev)
 			continue;
 		snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
 		    "pin %d", j);
+		func = bcm_gpio_get_function(sc, j);
 		sc->sc_gpio_pins[i].gp_pin = j;
 		sc->sc_gpio_pins[i].gp_caps = BCM_GPIO_DEFAULT_CAPS;
-		sc->sc_gpio_pins[i].gp_flags = bcm_gpio_get_function(sc, j);
+		sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(func);
 		i++;
 	}
 	sc->sc_gpio_npins = i;
 
-        device_add_child(dev, "gpioc", device_get_unit(dev));
-        device_add_child(dev, "gpiobus", device_get_unit(dev));
+	bcm_gpio_sysctl_init(sc);
+
+	device_add_child(dev, "gpioc", device_get_unit(dev));
+	device_add_child(dev, "gpiobus", device_get_unit(dev));
 	return (bus_generic_attach(dev));
 
 fail:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201212182218.qBIMIt01041209>