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. Reviewed by: mizhka MFC after: 2 weeks Differential Revision: https://reviews.freebsd.org/D20459
This commit is contained in:
parent
d55fcc487e
commit
061b38cdcc
@ -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 drivers for devices
|
||||
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
|
||||
|
@ -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
|
||||
@ -325,6 +315,34 @@ gpiobus_release_pin(device_t bus, uint32_t pin)
|
||||
return (0);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@ -349,17 +367,66 @@ gpiobus_parse_pins(struct gpiobus_softc *sc, device_t child, int mask)
|
||||
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);
|
||||
}
|
||||
|
||||
@ -539,15 +606,26 @@ gpiobus_hinted_child(device_t bus, const char *dname, int dunit)
|
||||
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)
|
||||
@ -574,6 +652,61 @@ gpiobus_set_resource(device_t dev, device_t child, int type, int rid,
|
||||
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),
|
||||
|
@ -107,10 +107,22 @@ 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 {
|
||||
struct gpiobus_ivar opd_dinfo;
|
||||
|
Loading…
Reference in New Issue
Block a user