Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 16 Jul 2019 15:52:47 +0000 (UTC)
From:      Andriy Gapon <avg@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-12@freebsd.org
Subject:   svn commit: r350044 - in stable/12: share/man/man4 sys/dev/gpio
Message-ID:  <201907161552.x6GFqlU5013936@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: avg
Date: Tue Jul 16 15:52:47 2019
New Revision: 350044
URL: https://svnweb.freebsd.org/changeset/base/350044

Log:
  MFC r349460: gpiobus: provide a new hint, pin_list
  
  "pin_list" allows to specify child pins as a list of pin numbers.
  Existing hint "pins" serves the same purpose but with a 32-bit wide bit
  mask.  One problem with that is that a controller can have more than 32
  pins.  One example is amdgpio.  Also, a list of numbers is a little bit
  more human friendly than a matching bit mask.  As a side note, it seems
  that in FDT pins are typically specified by their numbers as well.
  
  This commit also adds accessors for instance variables (IVARs) that
  define the child pins.  My primary goal is to allow a child to be
  configured programmatically rather than via hints (assuming that FDT is
  not supported on a platform).  Also, while a child should not care about
  specific pin numbers that are allocated to it, it could be interested in
  how many were actually assigned to it.
  
  While there, I removed "flags" instance variable.  It was unused.

Modified:
  stable/12/share/man/man4/gpio.4
  stable/12/sys/dev/gpio/gpiobus.c
  stable/12/sys/dev/gpio/gpiobusvar.h
Directory Properties:
  stable/12/   (props changed)

Modified: stable/12/share/man/man4/gpio.4
==============================================================================
--- stable/12/share/man/man4/gpio.4	Tue Jul 16 15:51:09 2019	(r350043)
+++ stable/12/share/man/man4/gpio.4	Tue Jul 16 15:52:47 2019	(r350044)
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd June 26, 2019
+.Dd June 27, 2019
 .Dt GPIO 4
 .Os
 .Sh NAME
@@ -109,7 +109,7 @@ based system these hints can be used to configure driv
 attached to
 .Nm
 pins:
-.Bl -tag -width ".Va hint.driver.unit.pins"
+.Bl -tag -width ".Va hint.driver.unit.pin_list"
 .It Va hint.driver.unit.at
 The
 .Nm gpiobus
@@ -125,6 +125,20 @@ This is a bitmask of the pins on the
 .Nm gpiobus
 that are connected to the device.
 The pins will be allocated to the specified driver instance.
+Only pins with numbers from 0 to 31 can be specified using this hint.
+.It Va hint.driver.unit.pin_list
+This is a list of pin numbers of pins on the
+.Nm gpiobus
+that are connected to the device.
+The pins will be allocated to the specified driver instance.
+This is a more user friendly alternative to the
+.Ar pins
+hint.
+Additionally, this hint allows specifying pin numbers greater than 31.
+The numbers can be decimal or hexadecimal with 0x prefix.
+Any non-digit character can be used as a separator.
+For example, it can be a comma, a slash or a space.
+The separator can be followed by any number of space characters.
 .El
 .Pp
 The following

Modified: stable/12/sys/dev/gpio/gpiobus.c
==============================================================================
--- stable/12/sys/dev/gpio/gpiobus.c	Tue Jul 16 15:51:09 2019	(r350043)
+++ stable/12/sys/dev/gpio/gpiobus.c	Tue Jul 16 15:52:47 2019	(r350044)
@@ -255,13 +255,6 @@ gpiobus_alloc_ivars(struct gpiobus_ivar *devi)
 	    M_NOWAIT | M_ZERO);
 	if (devi->pins == NULL)
 		return (ENOMEM);
-	devi->flags = malloc(sizeof(uint32_t) * devi->npins, M_DEVBUF,
-	    M_NOWAIT | M_ZERO);
-	if (devi->flags == NULL) {
-		free(devi->pins, M_DEVBUF);
-		return (ENOMEM);
-	}
-
 	return (0);
 }
 
@@ -269,14 +262,11 @@ void
 gpiobus_free_ivars(struct gpiobus_ivar *devi)
 {
 
-	if (devi->flags) {
-		free(devi->flags, M_DEVBUF);
-		devi->flags = NULL;
-	}
 	if (devi->pins) {
 		free(devi->pins, M_DEVBUF);
 		devi->pins = NULL;
 	}
+	devi->npins = 0;
 }
 
 int
@@ -326,6 +316,34 @@ gpiobus_release_pin(device_t bus, uint32_t pin)
 }
 
 static int
+gpiobus_acquire_child_pins(device_t dev, device_t child)
+{
+	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
+	int i;
+
+	for (i = 0; i < devi->npins; i++) {
+		/* Reserve the GPIO pin. */
+		if (gpiobus_acquire_pin(dev, devi->pins[i]) != 0) {
+			device_printf(child, "cannot acquire pin %d\n",
+			    devi->pins[i]);
+			while (--i >= 0) {
+				(void)gpiobus_release_pin(dev,
+				    devi->pins[i]);
+			}
+			gpiobus_free_ivars(devi);
+			return (EBUSY);
+		}
+	}
+	for (i = 0; i < devi->npins; i++) {
+		/* Use the child name as pin name. */
+		GPIOBUS_PIN_SETNAME(dev, devi->pins[i],
+		    device_get_nameunit(child));
+
+	}
+	return (0);
+}
+
+static int
 gpiobus_parse_pins(struct gpiobus_softc *sc, device_t child, int mask)
 {
 	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
@@ -349,21 +367,70 @@ gpiobus_parse_pins(struct gpiobus_softc *sc, device_t 
 	for (i = 0; i < 32; i++) {
 		if ((mask & (1 << i)) == 0)
 			continue;
-		/* Reserve the GPIO pin. */
-		if (gpiobus_acquire_pin(sc->sc_busdev, i) != 0) {
-			gpiobus_free_ivars(devi);
-			return (EINVAL);
-		}
 		devi->pins[npins++] = i;
-		/* Use the child name as pin name. */
-		GPIOBUS_PIN_SETNAME(sc->sc_busdev, i,
-		    device_get_nameunit(child));
 	}
 
+	if (gpiobus_acquire_child_pins(sc->sc_busdev, child) != 0)
+		return (EINVAL);
 	return (0);
 }
 
 static int
+gpiobus_parse_pin_list(struct gpiobus_softc *sc, device_t child,
+    const char *pins)
+{
+	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
+	const char *p;
+	char *endp;
+	unsigned long pin;
+	int i, npins;
+
+	npins = 0;
+	p = pins;
+	for (;;) {
+		pin = strtoul(p, &endp, 0);
+		if (endp == p)
+			break;
+		npins++;
+		if (*endp == '\0')
+			break;
+		p = endp + 1;
+	}
+
+	if (*endp != '\0') {
+		device_printf(child, "garbage in the pin list: %s\n", endp);
+		return (EINVAL);
+	}
+	if (npins == 0) {
+		device_printf(child, "empty pin list\n");
+		return (EINVAL);
+	}
+
+	devi->npins = npins;
+	if (gpiobus_alloc_ivars(devi) != 0) {
+		device_printf(child, "cannot allocate device ivars\n");
+		return (EINVAL);
+	}
+
+	i = 0;
+	p = pins;
+	for (;;) {
+		pin = strtoul(p, &endp, 0);
+
+		devi->pins[i] = pin;
+
+		if (*endp == '\0')
+			break;
+		i++;
+		p = endp + 1;
+	}
+
+	if (gpiobus_acquire_child_pins(sc->sc_busdev, child) != 0)
+		return (EINVAL);
+	return (0);
+}
+
+static int
 gpiobus_probe(device_t dev)
 {
 	device_set_desc(dev, "GPIO bus");
@@ -539,16 +606,27 @@ gpiobus_hinted_child(device_t bus, const char *dname, 
 	struct gpiobus_softc *sc = GPIOBUS_SOFTC(bus);
 	struct gpiobus_ivar *devi;
 	device_t child;
-	int irq, pins;
+	const char *pins;
+	int irq, pinmask;
 
 	child = BUS_ADD_CHILD(bus, 0, dname, dunit);
 	devi = GPIOBUS_IVAR(child);
-	resource_int_value(dname, dunit, "pins", &pins);
-	if (gpiobus_parse_pins(sc, child, pins)) {
-		resource_list_free(&devi->rl);
-		free(devi, M_DEVBUF);
-		device_delete_child(bus, child);
+	if (resource_int_value(dname, dunit, "pins", &pinmask) == 0) {
+		if (gpiobus_parse_pins(sc, child, pinmask)) {
+			resource_list_free(&devi->rl);
+			free(devi, M_DEVBUF);
+			device_delete_child(bus, child);
+			return;
+		}
 	}
+	else if (resource_string_value(dname, dunit, "pin_list", &pins) == 0) {
+		if (gpiobus_parse_pin_list(sc, child, pins)) {
+			resource_list_free(&devi->rl);
+			free(devi, M_DEVBUF);
+			device_delete_child(bus, child);
+			return;
+		}
+	}
 	if (resource_int_value(dname, dunit, "irq", &irq) == 0) {
 		if (bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1) != 0)
 			device_printf(bus,
@@ -574,6 +652,61 @@ gpiobus_set_resource(device_t dev, device_t child, int
 	return (0);
 }
 
+static int
+gpiobus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
+{
+	struct gpiobus_ivar *devi;
+
+	devi = GPIOBUS_IVAR(child);
+        switch (which) {
+	case GPIOBUS_IVAR_NPINS:
+		*result = devi->npins;
+		break;
+	case GPIOBUS_IVAR_PINS:
+		/* Children do not ever need to directly examine this. */
+		return (ENOTSUP);
+        default:
+                return (ENOENT);
+        }
+
+	return (0);
+}
+
+static int
+gpiobus_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
+{
+	struct gpiobus_ivar *devi;
+	const uint32_t *ptr;
+	int i;
+
+	devi = GPIOBUS_IVAR(child);
+        switch (which) {
+	case GPIOBUS_IVAR_NPINS:
+		/* GPIO ivars are set once. */
+		if (devi->npins != 0) {
+			return (EBUSY);
+		}
+		devi->npins = value;
+		if (gpiobus_alloc_ivars(devi) != 0) {
+			device_printf(child, "cannot allocate device ivars\n");
+			devi->npins = 0;
+			return (ENOMEM);
+		}
+		break;
+	case GPIOBUS_IVAR_PINS:
+		ptr = (const uint32_t *)value;
+		for (i = 0; i < devi->npins; i++)
+			devi->pins[i] = ptr[i];
+		if (gpiobus_acquire_child_pins(dev, child) != 0)
+			return (EBUSY);
+		break;
+        default:
+                return (ENOENT);
+        }
+
+        return (0);
+}
+
 static struct resource *
 gpiobus_alloc_resource(device_t bus, device_t child, int type, int *rid,
     rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
@@ -833,6 +966,8 @@ static device_method_t gpiobus_methods[] = {
 	DEVMETHOD(bus_child_pnpinfo_str, gpiobus_child_pnpinfo_str),
 	DEVMETHOD(bus_child_location_str, gpiobus_child_location_str),
 	DEVMETHOD(bus_hinted_child,	gpiobus_hinted_child),
+	DEVMETHOD(bus_read_ivar,        gpiobus_read_ivar),
+	DEVMETHOD(bus_write_ivar,       gpiobus_write_ivar),
 
 	/* GPIO protocol */
 	DEVMETHOD(gpiobus_acquire_bus,	gpiobus_acquire_bus),

Modified: stable/12/sys/dev/gpio/gpiobusvar.h
==============================================================================
--- stable/12/sys/dev/gpio/gpiobusvar.h	Tue Jul 16 15:51:09 2019	(r350043)
+++ stable/12/sys/dev/gpio/gpiobusvar.h	Tue Jul 16 15:52:47 2019	(r350044)
@@ -107,9 +107,21 @@ struct gpiobus_ivar
 {
 	struct resource_list	rl;	/* isr resource list */
 	uint32_t	npins;	/* pins total */
-	uint32_t	*flags;	/* pins flags */
 	uint32_t	*pins;	/* pins map */
 };
+
+enum gpiobus_ivars {
+	GPIOBUS_IVAR_NPINS	= 10500,
+	GPIOBUS_IVAR_PINS,
+};
+
+#define GPIOBUS_ACCESSOR(var, ivar, type)                                 \
+        __BUS_ACCESSOR(gpiobus, var, GPIOBUS, ivar, type)
+
+GPIOBUS_ACCESSOR(npins,		NPINS,		uint32_t)
+GPIOBUS_ACCESSOR(pins,		PINS,		const uint32_t *)
+
+#undef GPIOBUS_ACCESSOR
 
 #ifdef FDT
 struct ofw_gpiobus_devinfo {



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