Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/gpio/gpiobus.c
Show First 20 Lines • Show All 249 Lines • ▼ Show 20 Lines | |||||
gpiobus_alloc_ivars(struct gpiobus_ivar *devi) | gpiobus_alloc_ivars(struct gpiobus_ivar *devi) | ||||
{ | { | ||||
/* Allocate pins and flags memory. */ | /* Allocate pins and flags memory. */ | ||||
devi->pins = malloc(sizeof(uint32_t) * devi->npins, M_DEVBUF, | devi->pins = malloc(sizeof(uint32_t) * devi->npins, M_DEVBUF, | ||||
M_NOWAIT | M_ZERO); | M_NOWAIT | M_ZERO); | ||||
if (devi->pins == NULL) | if (devi->pins == NULL) | ||||
return (ENOMEM); | 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); | return (0); | ||||
} | } | ||||
void | void | ||||
gpiobus_free_ivars(struct gpiobus_ivar *devi) | gpiobus_free_ivars(struct gpiobus_ivar *devi) | ||||
{ | { | ||||
if (devi->flags) { | |||||
free(devi->flags, M_DEVBUF); | |||||
devi->flags = NULL; | |||||
} | |||||
if (devi->pins) { | if (devi->pins) { | ||||
free(devi->pins, M_DEVBUF); | free(devi->pins, M_DEVBUF); | ||||
devi->pins = NULL; | devi->pins = NULL; | ||||
} | } | ||||
devi->npins = 0; | |||||
} | } | ||||
int | int | ||||
gpiobus_acquire_pin(device_t bus, uint32_t pin) | gpiobus_acquire_pin(device_t bus, uint32_t pin) | ||||
{ | { | ||||
struct gpiobus_softc *sc; | struct gpiobus_softc *sc; | ||||
sc = device_get_softc(bus); | sc = device_get_softc(bus); | ||||
Show All 33 Lines | if (!sc->sc_pins[pin].mapped) { | ||||
return (-1); | return (-1); | ||||
} | } | ||||
sc->sc_pins[pin].mapped = 0; | sc->sc_pins[pin].mapped = 0; | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | 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) | gpiobus_parse_pins(struct gpiobus_softc *sc, device_t child, int mask) | ||||
{ | { | ||||
struct gpiobus_ivar *devi = GPIOBUS_IVAR(child); | struct gpiobus_ivar *devi = GPIOBUS_IVAR(child); | ||||
int i, npins; | int i, npins; | ||||
npins = 0; | npins = 0; | ||||
for (i = 0; i < 32; i++) { | for (i = 0; i < 32; i++) { | ||||
if (mask & (1 << i)) | if (mask & (1 << i)) | ||||
npins++; | npins++; | ||||
} | } | ||||
if (npins == 0) { | if (npins == 0) { | ||||
device_printf(child, "empty pin mask\n"); | device_printf(child, "empty pin mask\n"); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
devi->npins = npins; | devi->npins = npins; | ||||
if (gpiobus_alloc_ivars(devi) != 0) { | if (gpiobus_alloc_ivars(devi) != 0) { | ||||
device_printf(child, "cannot allocate device ivars\n"); | device_printf(child, "cannot allocate device ivars\n"); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
npins = 0; | npins = 0; | ||||
for (i = 0; i < 32; i++) { | for (i = 0; i < 32; i++) { | ||||
if ((mask & (1 << i)) == 0) | if ((mask & (1 << i)) == 0) | ||||
continue; | continue; | ||||
/* Reserve the GPIO pin. */ | devi->pins[npins++] = i; | ||||
if (gpiobus_acquire_pin(sc->sc_busdev, i) != 0) { | } | ||||
gpiobus_free_ivars(devi); | |||||
if (gpiobus_acquire_child_pins(sc->sc_busdev, child) != 0) | |||||
return (EINVAL); | return (EINVAL); | ||||
return (0); | |||||
} | } | ||||
devi->pins[npins++] = i; | |||||
/* Use the child name as pin name. */ | static int | ||||
GPIOBUS_PIN_SETNAME(sc->sc_busdev, i, | gpiobus_parse_pin_list(struct gpiobus_softc *sc, device_t child, | ||||
device_get_nameunit(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); | return (0); | ||||
} | } | ||||
static int | static int | ||||
gpiobus_probe(device_t dev) | gpiobus_probe(device_t dev) | ||||
{ | { | ||||
device_set_desc(dev, "GPIO bus"); | device_set_desc(dev, "GPIO bus"); | ||||
▲ Show 20 Lines • Show All 163 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
static void | static void | ||||
gpiobus_hinted_child(device_t bus, const char *dname, int dunit) | gpiobus_hinted_child(device_t bus, const char *dname, int dunit) | ||||
{ | { | ||||
struct gpiobus_softc *sc = GPIOBUS_SOFTC(bus); | struct gpiobus_softc *sc = GPIOBUS_SOFTC(bus); | ||||
struct gpiobus_ivar *devi; | struct gpiobus_ivar *devi; | ||||
device_t child; | device_t child; | ||||
int irq, pins; | const char *pins; | ||||
int irq, pinmask; | |||||
child = BUS_ADD_CHILD(bus, 0, dname, dunit); | child = BUS_ADD_CHILD(bus, 0, dname, dunit); | ||||
devi = GPIOBUS_IVAR(child); | devi = GPIOBUS_IVAR(child); | ||||
resource_int_value(dname, dunit, "pins", &pins); | if (resource_int_value(dname, dunit, "pins", &pinmask) == 0) { | ||||
if (gpiobus_parse_pins(sc, child, pins)) { | if (gpiobus_parse_pins(sc, child, pinmask)) { | ||||
resource_list_free(&devi->rl); | resource_list_free(&devi->rl); | ||||
free(devi, M_DEVBUF); | free(devi, M_DEVBUF); | ||||
device_delete_child(bus, child); | 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 (resource_int_value(dname, dunit, "irq", &irq) == 0) { | ||||
if (bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1) != 0) | if (bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1) != 0) | ||||
device_printf(bus, | device_printf(bus, | ||||
"warning: bus_set_resource() failed\n"); | "warning: bus_set_resource() failed\n"); | ||||
} | } | ||||
} | } | ||||
static int | static int | ||||
Show All 9 Lines | gpiobus_set_resource(device_t dev, device_t child, int type, int rid, | ||||
rle = resource_list_add(&devi->rl, type, rid, start, | rle = resource_list_add(&devi->rl, type, rid, start, | ||||
start + count - 1, count); | start + count - 1, count); | ||||
if (rle == NULL) | if (rle == NULL) | ||||
return (ENXIO); | return (ENXIO); | ||||
return (0); | 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 * | static struct resource * | ||||
gpiobus_alloc_resource(device_t bus, device_t child, int type, int *rid, | 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) | rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) | ||||
{ | { | ||||
struct gpiobus_softc *sc; | struct gpiobus_softc *sc; | ||||
struct resource *rv; | struct resource *rv; | ||||
struct resource_list *rl; | struct resource_list *rl; | ||||
struct resource_list_entry *rle; | struct resource_list_entry *rle; | ||||
▲ Show 20 Lines • Show All 243 Lines • ▼ Show 20 Lines | static device_method_t gpiobus_methods[] = { | ||||
DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), | DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), | ||||
DEVMETHOD(bus_get_resource_list, gpiobus_get_resource_list), | DEVMETHOD(bus_get_resource_list, gpiobus_get_resource_list), | ||||
DEVMETHOD(bus_add_child, gpiobus_add_child), | DEVMETHOD(bus_add_child, gpiobus_add_child), | ||||
DEVMETHOD(bus_probe_nomatch, gpiobus_probe_nomatch), | DEVMETHOD(bus_probe_nomatch, gpiobus_probe_nomatch), | ||||
DEVMETHOD(bus_print_child, gpiobus_print_child), | DEVMETHOD(bus_print_child, gpiobus_print_child), | ||||
DEVMETHOD(bus_child_pnpinfo_str, gpiobus_child_pnpinfo_str), | DEVMETHOD(bus_child_pnpinfo_str, gpiobus_child_pnpinfo_str), | ||||
DEVMETHOD(bus_child_location_str, gpiobus_child_location_str), | DEVMETHOD(bus_child_location_str, gpiobus_child_location_str), | ||||
DEVMETHOD(bus_hinted_child, gpiobus_hinted_child), | DEVMETHOD(bus_hinted_child, gpiobus_hinted_child), | ||||
DEVMETHOD(bus_read_ivar, gpiobus_read_ivar), | |||||
DEVMETHOD(bus_write_ivar, gpiobus_write_ivar), | |||||
/* GPIO protocol */ | /* GPIO protocol */ | ||||
DEVMETHOD(gpiobus_acquire_bus, gpiobus_acquire_bus), | DEVMETHOD(gpiobus_acquire_bus, gpiobus_acquire_bus), | ||||
DEVMETHOD(gpiobus_release_bus, gpiobus_release_bus), | DEVMETHOD(gpiobus_release_bus, gpiobus_release_bus), | ||||
DEVMETHOD(gpiobus_pin_getflags, gpiobus_pin_getflags), | DEVMETHOD(gpiobus_pin_getflags, gpiobus_pin_getflags), | ||||
DEVMETHOD(gpiobus_pin_getcaps, gpiobus_pin_getcaps), | DEVMETHOD(gpiobus_pin_getcaps, gpiobus_pin_getcaps), | ||||
DEVMETHOD(gpiobus_pin_setflags, gpiobus_pin_setflags), | DEVMETHOD(gpiobus_pin_setflags, gpiobus_pin_setflags), | ||||
DEVMETHOD(gpiobus_pin_get, gpiobus_pin_get), | DEVMETHOD(gpiobus_pin_get, gpiobus_pin_get), | ||||
Show All 19 Lines |