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>