Index: head/lib/libgpio/gpio.3 =================================================================== --- head/lib/libgpio/gpio.3 (revision 279760) +++ head/lib/libgpio/gpio.3 (revision 279761) @@ -1,192 +1,198 @@ .\" .\" Copyright (c) 2014 Rui Paulo .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ .\" -.Dd November 17, 2014 +.Dd March 7, 2015 .Dt GPIO 3 .Os .Sh NAME .Nm gpio_open , .Nm gpio_close .Nd "library to handle GPIO pins" .Sh LIBRARY .Lb libgpio .Sh SYNOPSIS .In libgpio.h .Ft "gpio_handle_t" .Fn gpio_open "unsigned int unit" .Ft "gpio_handle_t" .Fn gpio_open_device "const char *device" .Ft void .Fn gpio_close "gpio_handle_t handle" .Ft int .Fn gpio_pin_list "gpio_handle_t handle, gpio_config_t **pcfgs" .Ft int -.Fn gpio_pin_config "gpio_handle_t handle, gpio_config *cfg" +.Fn gpio_pin_config "gpio_handle_t handle, gpio_config_t *cfg" .Ft int +.Fn gpio_pin_set_name "gpio_handle_t handle, gpio_pin_t pin, char *name" +.Ft int .Fn gpio_pin_set_flags "gpio_handle_t handle, gpio_config_t *cfg" .Ft gpio_value_t .Fn gpio_pin_get "gpio_handle_t handle, gpio_pin_t pin" .Ft int .Fn gpio_pin_set "gpio_handle_t handle, gpio_pin_t pin, gpio_value_t value" .Ft int .Fn gpio_pin_toggle "gpio_handle_t handle, gpio_pin_t pin" .Ft int .Fn gpio_pin_low "gpio_handle_t handle, gpio_pin_t pin" .Ft int .Fn gpio_pin_high "gpio_handle_t handle, gpio_pin_t pin" .Ft int .Fn gpio_pin_input "gpio_handle_t handle, gpio_pin_t pin" .Ft int .Fn gpio_pin_output "gpio_handle_t handle, gpio_pin_t pin" .Ft int .Fn gpio_pin_opendrain "gpio_handle_t handle, gpio_pin_t pin" .Ft int .Fn gpio_pin_pushpull "gpio_handle_t handle, gpio_pin_t pin" .Ft int .Fn gpio_pin_tristate "gpio_handle_t handle, gpio_pin_t pin" .Ft int .Fn gpio_pin_pullup "gpio_handle_t handle, gpio_pin_t pin" .Ft int .Fn gpio_pin_pulldown "gpio_handle_t handle, gpio_pin_t pin" .Ft int .Fn gpio_pin_invin "gpio_handle_t handle, gpio_pin_t pin" .Ft int .Fn gpio_pin_invout "gpio_handle_t handle, gpio_pin_t pin" .Ft int .Fn gpio_pin_pulsate "gpio_handle_t handle, gpio_pin_t pin" .Sh DESCRIPTION The .Nm libgpio library provides an interface to configure GPIO pins. The library operates with a .Ft gpio_handle_t opaque type which can be created with .Fn gpio_open or .Fn gpio_open_device . When no more GPIO operations are needed, this handle can be destroyed with .Fn gpio_close . .Pp To get a list of all available pins, one can call .Fn gpio_pin_list . This function takes a pointer to a .Ft gpio_config_t which is dynamically allocated. This pointer should be freed with .Xr free 3 when it's no longer necessary. .Pp The function .Fn gpio_pin_config retrieves the current configuration of a pin. The pin number should be passed in via the .Ft g_pin variable which is part of the .Ft gpio_config_t structure. +.Pp +The function +.Fn gpio_pin_set_name +sets the name used to describe a pin. .Pp The function .Fn gpio_pin_set_flags configures a pin with the flags passed in by the .Ft gpio_config_t structure. The pin number should also be passed in through the .Ft g_pin variable. All other structure members will be ignored by this function. The list of flags can be found in .Pa /usr/include/sys/gpio.h . .Pp The get or set the state of a GPIO pin, the functions .Fn gpio_pin_get and .Fn gpio_pin_set are available, respectively. To toggle the state, use .Fn gpio_pin_toggle . .Pp The functions .Fn gpio_pin_low and .Fn gpio_pin_high are wrappers around .Fn gpio_pin_set . .Pp The functions .Fn gpio_pin_input , .Fn gpio_pin_output , .Fn gpio_pin_opendrain , .Fn gpio_pin_pushpull , .Fn gpio_pin_tristate , .Fn gpio_pin_pullup , .Fn gpio_pin_pulldown , .Fn gpio_pin_invin , .Fn gpio_pin_invout and .Fn gpio_pin_pulsate are wrappers around .Fn gpio_pin_set_flags . .Sh EXAMPLES The following example shows how to configure pin 16 as output and then drive it high: .Bd -literal #include #include gpio_handle_t handle; handle = gpio_open(0); if (handle == GPIO_HANDLE_INVALID) err(1, "gpio_open failed"); gpio_pin_output(handle, 16); gpio_pin_high(handle, 16); gpio_close(handle); .Ed .Pp The following example shows how to get a configuration of a pin: .Bd -literal gpio_config_t cfg; cfg.g_pin = 32; gpio_pin_config(handle, &cfg); .Ed .Pp The structure will contain the name of the pin and its flags. .Sh SEE ALSO .Xr gpiobus 4 , .Xr gpioctl 8 .Sh HISTORY The .Nm libgpio library first appeared in .Fx 11.0 . .Sh AUTHORS The .Nm libgpio library was implemented by .An Rui Paulo Aq Mt rpaulo@FreeBSD.org . Index: head/lib/libgpio/gpio.c =================================================================== --- head/lib/libgpio/gpio.c (revision 279760) +++ head/lib/libgpio/gpio.c (revision 279761) @@ -1,262 +1,278 @@ /*- * Copyright (c) 2013-2014 Rui Paulo * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include gpio_handle_t gpio_open(unsigned int unit) { char device[16]; snprintf(device, sizeof(device), "/dev/gpioc%u", unit); return (gpio_open_device(device)); } gpio_handle_t gpio_open_device(const char *device) { int fd, maxpins; int serr; fd = open(device, O_RDONLY); if (fd < 0) return (GPIO_INVALID_HANDLE); /* * Check whether a simple ioctl works. */ if (ioctl(fd, GPIOMAXPIN, &maxpins) < 0) { serr = errno; close(fd); errno = serr; return (GPIO_INVALID_HANDLE); } return (fd); } void gpio_close(gpio_handle_t handle) { close(handle); } int gpio_pin_list(gpio_handle_t handle, gpio_config_t **pcfgs) { int maxpins, i; gpio_config_t *cfgs; *pcfgs = NULL; if (ioctl(handle, GPIOMAXPIN, &maxpins) < 0) return (-1); /* Reasonable values. */ if (maxpins < 0 || maxpins > 4096) { errno = EINVAL; return (-1); } cfgs = calloc(maxpins + 1, sizeof(*cfgs)); if (cfgs == NULL) return (-1); for (i = 0; i <= maxpins; i++) { cfgs[i].g_pin = i; gpio_pin_config(handle, &cfgs[i]); } *pcfgs = cfgs; return (maxpins); } int gpio_pin_config(gpio_handle_t handle, gpio_config_t *cfg) { struct gpio_pin gppin; if (cfg == NULL) return (-1); gppin.gp_pin = cfg->g_pin; if (ioctl(handle, GPIOGETCONFIG, &gppin) < 0) return (-1); strlcpy(cfg->g_name, gppin.gp_name, GPIOMAXNAME); cfg->g_caps = gppin.gp_caps; cfg->g_flags = gppin.gp_flags; return (0); } int +gpio_pin_set_name(gpio_handle_t handle, gpio_pin_t pin, char *name) +{ + struct gpio_pin gppin; + + if (name == NULL) + return (-1); + bzero(&gppin, sizeof(gppin)); + gppin.gp_pin = pin; + strlcpy(gppin.gp_name, name, GPIOMAXNAME); + if (ioctl(handle, GPIOSETNAME, &gppin) < 0) + return (-1); + + return (0); +} + +int gpio_pin_set_flags(gpio_handle_t handle, gpio_config_t *cfg) { struct gpio_pin gppin; if (cfg == NULL) return (-1); gppin.gp_pin = cfg->g_pin; gppin.gp_flags = cfg->g_flags; if (ioctl(handle, GPIOSETCONFIG, &gppin) < 0) return (-1); return (0); } gpio_value_t gpio_pin_get(gpio_handle_t handle, gpio_pin_t pin) { struct gpio_req gpreq; bzero(&gpreq, sizeof(gpreq)); gpreq.gp_pin = pin; if (ioctl(handle, GPIOGET, &gpreq) < 0) return (GPIO_VALUE_INVALID); return (gpreq.gp_value); } int gpio_pin_set(gpio_handle_t handle, gpio_pin_t pin, gpio_value_t value) { struct gpio_req gpreq; if (value == GPIO_VALUE_INVALID) return (-1); bzero(&gpreq, sizeof(gpreq)); gpreq.gp_pin = pin; gpreq.gp_value = value; if (ioctl(handle, GPIOSET, &gpreq) < 0) return (-1); return (0); } int gpio_pin_toggle(gpio_handle_t handle, gpio_pin_t pin) { gpio_value_t value; value = gpio_pin_get(handle, pin); if (value == GPIO_VALUE_INVALID) return (-1); value = !value; return (gpio_pin_set(handle, pin, value)); } int gpio_pin_low(gpio_handle_t handle, gpio_pin_t pin) { return (gpio_pin_set(handle, pin, GPIO_VALUE_LOW)); } int gpio_pin_high(gpio_handle_t handle, gpio_pin_t pin) { return (gpio_pin_set(handle, pin, GPIO_VALUE_HIGH)); } static int gpio_pin_set_flag(gpio_handle_t handle, gpio_pin_t pin, uint32_t flag) { gpio_config_t cfg; bzero(&cfg, sizeof(cfg)); cfg.g_pin = pin; if (gpio_pin_config(handle, &cfg) < 0) return (-1); cfg.g_flags = flag; return (gpio_pin_set_flags(handle, &cfg)); } int gpio_pin_input(gpio_handle_t handle, gpio_pin_t pin) { return (gpio_pin_set_flag(handle, pin, GPIO_PIN_INPUT)); } int gpio_pin_output(gpio_handle_t handle, gpio_pin_t pin) { return (gpio_pin_set_flag(handle, pin, GPIO_PIN_OUTPUT)); } int gpio_pin_opendrain(gpio_handle_t handle, gpio_pin_t pin) { return (gpio_pin_set_flag(handle, pin, GPIO_PIN_OPENDRAIN)); } int gpio_pin_pushpull(gpio_handle_t handle, gpio_pin_t pin) { return (gpio_pin_set_flag(handle, pin, GPIO_PIN_PUSHPULL)); } int gpio_pin_tristate(gpio_handle_t handle, gpio_pin_t pin) { return (gpio_pin_set_flag(handle, pin, GPIO_PIN_TRISTATE)); } int gpio_pin_pullup(gpio_handle_t handle, gpio_pin_t pin) { return (gpio_pin_set_flag(handle, pin, GPIO_PIN_PULLUP)); } int gpio_pin_pulldown(gpio_handle_t handle, gpio_pin_t pin) { return (gpio_pin_set_flag(handle, pin, GPIO_PIN_PULLDOWN)); } int gpio_pin_invin(gpio_handle_t handle, gpio_pin_t pin) { return (gpio_pin_set_flag(handle, pin, GPIO_PIN_INVIN)); } int gpio_pin_invout(gpio_handle_t handle, gpio_pin_t pin) { return (gpio_pin_set_flag(handle, pin, GPIO_PIN_INVOUT)); } int gpio_pin_pulsate(gpio_handle_t handle, gpio_pin_t pin) { return (gpio_pin_set_flag(handle, pin, GPIO_PIN_PULSATE)); } Index: head/lib/libgpio/libgpio.h =================================================================== --- head/lib/libgpio/libgpio.h (revision 279760) +++ head/lib/libgpio/libgpio.h (revision 279761) @@ -1,105 +1,110 @@ /*- * Copyright (c) 2013-2014 Rui Paulo * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _LIBGPIO_H_ #define _LIBGPIO_H_ #include __BEGIN_DECLS #define GPIO_INVALID_HANDLE -1 typedef int gpio_handle_t; typedef uint32_t gpio_pin_t; /* * Structure describing a GPIO pin configuration. */ typedef struct { gpio_pin_t g_pin; char g_name[GPIOMAXNAME]; uint32_t g_caps; uint32_t g_flags; } gpio_config_t; typedef enum { GPIO_VALUE_INVALID = -1, GPIO_VALUE_LOW = GPIO_PIN_LOW, GPIO_VALUE_HIGH = GPIO_PIN_HIGH } gpio_value_t; /* * Open /dev/gpiocN or a specific device. */ gpio_handle_t gpio_open(unsigned int); gpio_handle_t gpio_open_device(const char *); void gpio_close(gpio_handle_t); /* * Get a list of all the GPIO pins. */ int gpio_pin_list(gpio_handle_t, gpio_config_t **); /* * GPIO pin configuration. * * Retrieve the configuration of a specific GPIO pin. The pin number is * passed through the gpio_config_t structure. */ int gpio_pin_config(gpio_handle_t, gpio_config_t *); /* + * Sets the GPIO pin name. The pin number and pin name to be set are passed + * as parameters. + */ +int gpio_pin_set_name(gpio_handle_t, gpio_pin_t, char *); +/* * Sets the GPIO flags on a specific GPIO pin. The pin number and the flags * to be set are passed through the gpio_config_t structure. */ int gpio_pin_set_flags(gpio_handle_t, gpio_config_t *); /* * GPIO pin values. */ int gpio_pin_get(gpio_handle_t, gpio_pin_t); int gpio_pin_set(gpio_handle_t, gpio_pin_t, int); int gpio_pin_toggle(gpio_handle_t, gpio_pin_t); /* * Helper functions to set pin states. */ int gpio_pin_low(gpio_handle_t, gpio_pin_t); int gpio_pin_high(gpio_handle_t, gpio_pin_t); /* * Helper functions to configure pins. */ int gpio_pin_input(gpio_handle_t, gpio_pin_t); int gpio_pin_output(gpio_handle_t, gpio_pin_t); int gpio_pin_opendrain(gpio_handle_t, gpio_pin_t); int gpio_pin_pushpull(gpio_handle_t, gpio_pin_t); int gpio_pin_tristate(gpio_handle_t, gpio_pin_t); int gpio_pin_pullup(gpio_handle_t, gpio_pin_t); int gpio_pin_pulldown(gpio_handle_t, gpio_pin_t); int gpio_pin_invin(gpio_handle_t, gpio_pin_t); int gpio_pin_invout(gpio_handle_t, gpio_pin_t); int gpio_pin_pulsate(gpio_handle_t, gpio_pin_t); __END_DECLS #endif /* _LIBGPIO_H_ */ Index: head/sys/dev/gpio/gpiobus.c =================================================================== --- head/sys/dev/gpio/gpiobus.c (revision 279760) +++ head/sys/dev/gpio/gpiobus.c (revision 279761) @@ -1,715 +1,761 @@ /*- * Copyright (c) 2009 Oleksandr Tymoshenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "gpiobus_if.h" #undef GPIOBUS_DEBUG #ifdef GPIOBUS_DEBUG #define dprintf printf #else #define dprintf(x, arg...) #endif static void gpiobus_print_pins(struct gpiobus_ivar *, char *, size_t); static int gpiobus_parse_pins(struct gpiobus_softc *, device_t, int); static int gpiobus_probe(device_t); static int gpiobus_attach(device_t); static int gpiobus_detach(device_t); static int gpiobus_suspend(device_t); static int gpiobus_resume(device_t); static void gpiobus_probe_nomatch(device_t, device_t); static int gpiobus_print_child(device_t, device_t); static int gpiobus_child_location_str(device_t, device_t, char *, size_t); static int gpiobus_child_pnpinfo_str(device_t, device_t, char *, size_t); static device_t gpiobus_add_child(device_t, u_int, const char *, int); static void gpiobus_hinted_child(device_t, const char *, int); /* * GPIOBUS interface */ static int gpiobus_acquire_bus(device_t, device_t, int); static void gpiobus_release_bus(device_t, device_t); static int gpiobus_pin_setflags(device_t, device_t, uint32_t, uint32_t); static int gpiobus_pin_getflags(device_t, device_t, uint32_t, uint32_t*); static int gpiobus_pin_getcaps(device_t, device_t, uint32_t, uint32_t*); static int gpiobus_pin_set(device_t, device_t, uint32_t, unsigned int); static int gpiobus_pin_get(device_t, device_t, uint32_t, unsigned int*); static int gpiobus_pin_toggle(device_t, device_t, uint32_t); int gpio_check_flags(uint32_t caps, uint32_t flags) { /* Check for unwanted flags. */ if ((flags & caps) == 0 || (flags & caps) != flags) return (EINVAL); /* Cannot mix input/output together. */ if (flags & GPIO_PIN_INPUT && flags & GPIO_PIN_OUTPUT) return (EINVAL); /* Cannot mix pull-up/pull-down together. */ if (flags & GPIO_PIN_PULLUP && flags & GPIO_PIN_PULLDOWN) return (EINVAL); return (0); } static void gpiobus_print_pins(struct gpiobus_ivar *devi, char *buf, size_t buflen) { char tmp[128]; int i, range_start, range_stop, need_coma; if (devi->npins == 0) return; need_coma = 0; range_start = range_stop = devi->pins[0]; for (i = 1; i < devi->npins; i++) { if (devi->pins[i] != (range_stop + 1)) { if (need_coma) strlcat(buf, ",", buflen); memset(tmp, 0, sizeof(tmp)); if (range_start != range_stop) snprintf(tmp, sizeof(tmp) - 1, "%d-%d", range_start, range_stop); else snprintf(tmp, sizeof(tmp) - 1, "%d", range_start); strlcat(buf, tmp, buflen); range_start = range_stop = devi->pins[i]; need_coma = 1; } else range_stop++; } if (need_coma) strlcat(buf, ",", buflen); memset(tmp, 0, sizeof(tmp)); if (range_start != range_stop) snprintf(tmp, sizeof(tmp) - 1, "%d-%d", range_start, range_stop); else snprintf(tmp, sizeof(tmp) - 1, "%d", range_start); strlcat(buf, tmp, buflen); } device_t gpiobus_attach_bus(device_t dev) { device_t busdev; busdev = device_add_child(dev, "gpiobus", -1); if (busdev == NULL) return (NULL); if (device_add_child(dev, "gpioc", -1) == NULL) { device_delete_child(dev, busdev); return (NULL); } #ifdef FDT ofw_gpiobus_register_provider(dev); #endif bus_generic_attach(dev); return (busdev); } int gpiobus_detach_bus(device_t dev) { #ifdef FDT ofw_gpiobus_unregister_provider(dev); #endif return (bus_generic_detach(dev)); } int gpiobus_init_softc(device_t dev) { struct gpiobus_softc *sc; sc = GPIOBUS_SOFTC(dev); sc->sc_busdev = dev; sc->sc_dev = device_get_parent(dev); sc->sc_intr_rman.rm_type = RMAN_ARRAY; sc->sc_intr_rman.rm_descr = "GPIO Interrupts"; if (rman_init(&sc->sc_intr_rman) != 0 || rman_manage_region(&sc->sc_intr_rman, 0, ~0) != 0) panic("%s: failed to set up rman.", __func__); if (GPIO_PIN_MAX(sc->sc_dev, &sc->sc_npins) != 0) return (ENXIO); KASSERT(sc->sc_npins != 0, ("GPIO device with no pins")); /* Pins = GPIO_PIN_MAX() + 1 */ sc->sc_npins++; - sc->sc_pins_mapped = malloc(sizeof(int) * sc->sc_npins, M_DEVBUF, + sc->sc_pins = malloc(sizeof(*sc->sc_pins) * sc->sc_npins, M_DEVBUF, M_NOWAIT | M_ZERO); - if (sc->sc_pins_mapped == NULL) + if (sc->sc_pins == NULL) return (ENOMEM); /* Initialize the bus lock. */ GPIOBUS_LOCK_INIT(sc); return (0); } int gpiobus_alloc_ivars(struct gpiobus_ivar *devi) { /* Allocate pins and flags memory. */ devi->pins = malloc(sizeof(uint32_t) * devi->npins, M_DEVBUF, 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); } 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; } } int gpiobus_map_pin(device_t bus, uint32_t pin) { struct gpiobus_softc *sc; sc = device_get_softc(bus); /* Consistency check. */ if (pin >= sc->sc_npins) { device_printf(bus, "invalid pin %d, max: %d\n", pin, sc->sc_npins - 1); return (-1); } /* Mark pin as mapped and give warning if it's already mapped. */ - if (sc->sc_pins_mapped[pin]) { + if (sc->sc_pins[pin].mapped) { device_printf(bus, "warning: pin %d is already mapped\n", pin); return (-1); } - sc->sc_pins_mapped[pin] = 1; + sc->sc_pins[pin].mapped = 1; return (0); } static int gpiobus_parse_pins(struct gpiobus_softc *sc, device_t child, int mask) { struct gpiobus_ivar *devi = GPIOBUS_IVAR(child); int i, npins; npins = 0; for (i = 0; i < 32; i++) { if (mask & (1 << i)) npins++; } if (npins == 0) { device_printf(child, "empty pin mask\n"); return (EINVAL); } devi->npins = npins; if (gpiobus_alloc_ivars(devi) != 0) { device_printf(child, "cannot allocate device ivars\n"); return (EINVAL); } npins = 0; for (i = 0; i < 32; i++) { if ((mask & (1 << i)) == 0) continue; /* Reserve the GPIO pin. */ if (gpiobus_map_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)); } return (0); } static int gpiobus_probe(device_t dev) { device_set_desc(dev, "GPIO bus"); return (BUS_PROBE_GENERIC); } static int gpiobus_attach(device_t dev) { int err; err = gpiobus_init_softc(dev); if (err != 0) return (err); /* * Get parent's pins and mark them as unmapped */ bus_generic_probe(dev); bus_enumerate_hinted_children(dev); return (bus_generic_attach(dev)); } /* * Since this is not a self-enumerating bus, and since we always add * children in attach, we have to always delete children here. */ static int gpiobus_detach(device_t dev) { struct gpiobus_softc *sc; struct gpiobus_ivar *devi; device_t *devlist; int i, err, ndevs; sc = GPIOBUS_SOFTC(dev); KASSERT(mtx_initialized(&sc->sc_mtx), ("gpiobus mutex not initialized")); GPIOBUS_LOCK_DESTROY(sc); if ((err = bus_generic_detach(dev)) != 0) return (err); if ((err = device_get_children(dev, &devlist, &ndevs)) != 0) return (err); for (i = 0; i < ndevs; i++) { device_delete_child(dev, devlist[i]); devi = GPIOBUS_IVAR(devlist[i]); gpiobus_free_ivars(devi); } free(devlist, M_TEMP); - - if (sc->sc_pins_mapped) { - free(sc->sc_pins_mapped, M_DEVBUF); - sc->sc_pins_mapped = NULL; + if (sc->sc_pins) { + for (i = 0; i < sc->sc_npins; i++) { + if (sc->sc_pins[i].name != NULL) + free(sc->sc_pins[i].name, M_DEVBUF); + sc->sc_pins[i].name = NULL; + } + free(sc->sc_pins, M_DEVBUF); + sc->sc_pins = NULL; } return (0); } static int gpiobus_suspend(device_t dev) { return (bus_generic_suspend(dev)); } static int gpiobus_resume(device_t dev) { return (bus_generic_resume(dev)); } static void gpiobus_probe_nomatch(device_t dev, device_t child) { char pins[128]; struct gpiobus_ivar *devi; devi = GPIOBUS_IVAR(child); memset(pins, 0, sizeof(pins)); gpiobus_print_pins(devi, pins, sizeof(pins)); device_printf(dev, " at pin(s) %s", pins); resource_list_print_type(&devi->rl, "irq", SYS_RES_IRQ, "%ld"); printf("\n"); } static int gpiobus_print_child(device_t dev, device_t child) { char pins[128]; int retval = 0; struct gpiobus_ivar *devi; devi = GPIOBUS_IVAR(child); memset(pins, 0, sizeof(pins)); retval += bus_print_child_header(dev, child); retval += printf(" at pin(s) "); gpiobus_print_pins(devi, pins, sizeof(pins)); retval += printf("%s", pins); resource_list_print_type(&devi->rl, "irq", SYS_RES_IRQ, "%ld"); retval += bus_print_child_footer(dev, child); return (retval); } static int gpiobus_child_location_str(device_t bus, device_t child, char *buf, size_t buflen) { struct gpiobus_ivar *devi; devi = GPIOBUS_IVAR(child); strlcpy(buf, "pin(s)=", buflen); gpiobus_print_pins(devi, buf, buflen); return (0); } static int gpiobus_child_pnpinfo_str(device_t bus, device_t child, char *buf, size_t buflen) { *buf = '\0'; return (0); } static device_t gpiobus_add_child(device_t dev, u_int order, const char *name, int unit) { device_t child; struct gpiobus_ivar *devi; child = device_add_child_ordered(dev, order, name, unit); if (child == NULL) return (child); devi = malloc(sizeof(struct gpiobus_ivar), M_DEVBUF, M_NOWAIT | M_ZERO); if (devi == NULL) { device_delete_child(dev, child); return (0); } resource_list_init(&devi->rl); device_set_ivars(child, devi); return (child); } static void 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; 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)) device_delete_child(bus, child); if (resource_int_value(dname, dunit, "irq", &irq) == 0) { if (bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1) != 0) device_printf(bus, "warning: bus_set_resource() failed\n"); } } static int gpiobus_set_resource(device_t dev, device_t child, int type, int rid, u_long start, u_long count) { struct gpiobus_ivar *devi; struct resource_list_entry *rle; dprintf("%s: entry (%p, %p, %d, %d, %p, %ld)\n", __func__, dev, child, type, rid, (void *)(intptr_t)start, count); devi = GPIOBUS_IVAR(child); rle = resource_list_add(&devi->rl, type, rid, start, start + count - 1, count); if (rle == NULL) return (ENXIO); return (0); } static struct resource * gpiobus_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct gpiobus_softc *sc; struct resource *rv; struct resource_list *rl; struct resource_list_entry *rle; int isdefault; if (type != SYS_RES_IRQ) return (NULL); isdefault = (start == 0UL && end == ~0UL && count == 1); rle = NULL; if (isdefault) { rl = BUS_GET_RESOURCE_LIST(bus, child); if (rl == NULL) return (NULL); rle = resource_list_find(rl, type, *rid); if (rle == NULL) return (NULL); if (rle->res != NULL) panic("%s: resource entry is busy", __func__); start = rle->start; count = rle->count; end = rle->end; } sc = device_get_softc(bus); rv = rman_reserve_resource(&sc->sc_intr_rman, start, end, count, flags, child); if (rv == NULL) return (NULL); rman_set_rid(rv, *rid); if ((flags & RF_ACTIVE) != 0 && bus_activate_resource(child, type, *rid, rv) != 0) { rman_release_resource(rv); return (NULL); } return (rv); } static int gpiobus_release_resource(device_t bus __unused, device_t child, int type, int rid, struct resource *r) { int error; if (rman_get_flags(r) & RF_ACTIVE) { error = bus_deactivate_resource(child, type, rid, r); if (error) return (error); } return (rman_release_resource(r)); } static struct resource_list * gpiobus_get_resource_list(device_t bus __unused, device_t child) { struct gpiobus_ivar *ivar; ivar = GPIOBUS_IVAR(child); return (&ivar->rl); } static int gpiobus_acquire_bus(device_t busdev, device_t child, int how) { struct gpiobus_softc *sc; sc = device_get_softc(busdev); GPIOBUS_ASSERT_UNLOCKED(sc); GPIOBUS_LOCK(sc); if (sc->sc_owner != NULL) { if (how == GPIOBUS_DONTWAIT) { GPIOBUS_UNLOCK(sc); return (EWOULDBLOCK); } while (sc->sc_owner != NULL) mtx_sleep(sc, &sc->sc_mtx, 0, "gpiobuswait", 0); } sc->sc_owner = child; GPIOBUS_UNLOCK(sc); return (0); } static void gpiobus_release_bus(device_t busdev, device_t child) { struct gpiobus_softc *sc; sc = device_get_softc(busdev); GPIOBUS_ASSERT_UNLOCKED(sc); GPIOBUS_LOCK(sc); if (sc->sc_owner == NULL) panic("gpiobus: releasing unowned bus."); if (sc->sc_owner != child) panic("gpiobus: you don't own the bus. game over."); sc->sc_owner = NULL; wakeup(sc); GPIOBUS_UNLOCK(sc); } static int gpiobus_pin_setflags(device_t dev, device_t child, uint32_t pin, uint32_t flags) { struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev); struct gpiobus_ivar *devi = GPIOBUS_IVAR(child); uint32_t caps; if (pin >= devi->npins) return (EINVAL); if (GPIO_PIN_GETCAPS(sc->sc_dev, devi->pins[pin], &caps) != 0) return (EINVAL); if (gpio_check_flags(caps, flags) != 0) return (EINVAL); return (GPIO_PIN_SETFLAGS(sc->sc_dev, devi->pins[pin], flags)); } static int gpiobus_pin_getflags(device_t dev, device_t child, uint32_t pin, uint32_t *flags) { struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev); struct gpiobus_ivar *devi = GPIOBUS_IVAR(child); if (pin >= devi->npins) return (EINVAL); return GPIO_PIN_GETFLAGS(sc->sc_dev, devi->pins[pin], flags); } static int gpiobus_pin_getcaps(device_t dev, device_t child, uint32_t pin, uint32_t *caps) { struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev); struct gpiobus_ivar *devi = GPIOBUS_IVAR(child); if (pin >= devi->npins) return (EINVAL); return GPIO_PIN_GETCAPS(sc->sc_dev, devi->pins[pin], caps); } static int gpiobus_pin_set(device_t dev, device_t child, uint32_t pin, unsigned int value) { struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev); struct gpiobus_ivar *devi = GPIOBUS_IVAR(child); if (pin >= devi->npins) return (EINVAL); return GPIO_PIN_SET(sc->sc_dev, devi->pins[pin], value); } static int gpiobus_pin_get(device_t dev, device_t child, uint32_t pin, unsigned int *value) { struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev); struct gpiobus_ivar *devi = GPIOBUS_IVAR(child); if (pin >= devi->npins) return (EINVAL); return GPIO_PIN_GET(sc->sc_dev, devi->pins[pin], value); } static int gpiobus_pin_toggle(device_t dev, device_t child, uint32_t pin) { struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev); struct gpiobus_ivar *devi = GPIOBUS_IVAR(child); if (pin >= devi->npins) return (EINVAL); return GPIO_PIN_TOGGLE(sc->sc_dev, devi->pins[pin]); } +static int +gpiobus_pin_getname(device_t dev, uint32_t pin, char *name) +{ + struct gpiobus_softc *sc; + + sc = GPIOBUS_SOFTC(dev); + if (pin > sc->sc_npins) + return (EINVAL); + /* Did we have a name for this pin ? */ + if (sc->sc_pins[pin].name != NULL) { + memcpy(name, sc->sc_pins[pin].name, GPIOMAXNAME); + return (0); + } + + /* Return the default pin name. */ + return (GPIO_PIN_GETNAME(device_get_parent(dev), pin, name)); +} + +static int +gpiobus_pin_setname(device_t dev, uint32_t pin, const char *name) +{ + struct gpiobus_softc *sc; + + sc = GPIOBUS_SOFTC(dev); + if (pin > sc->sc_npins) + return (EINVAL); + if (name == NULL) + return (EINVAL); + /* Save the pin name. */ + if (sc->sc_pins[pin].name == NULL) + sc->sc_pins[pin].name = malloc(GPIOMAXNAME, M_DEVBUF, + M_WAITOK | M_ZERO); + strlcpy(sc->sc_pins[pin].name, name, GPIOMAXNAME); + + return (0); +} + static device_method_t gpiobus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, gpiobus_probe), DEVMETHOD(device_attach, gpiobus_attach), DEVMETHOD(device_detach, gpiobus_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, gpiobus_suspend), DEVMETHOD(device_resume, gpiobus_resume), /* Bus interface */ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_config_intr, bus_generic_config_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_set_resource, gpiobus_set_resource), DEVMETHOD(bus_alloc_resource, gpiobus_alloc_resource), DEVMETHOD(bus_release_resource, gpiobus_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_get_resource_list, gpiobus_get_resource_list), DEVMETHOD(bus_add_child, gpiobus_add_child), DEVMETHOD(bus_probe_nomatch, gpiobus_probe_nomatch), DEVMETHOD(bus_print_child, gpiobus_print_child), 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), /* GPIO protocol */ DEVMETHOD(gpiobus_acquire_bus, gpiobus_acquire_bus), DEVMETHOD(gpiobus_release_bus, gpiobus_release_bus), DEVMETHOD(gpiobus_pin_getflags, gpiobus_pin_getflags), DEVMETHOD(gpiobus_pin_getcaps, gpiobus_pin_getcaps), DEVMETHOD(gpiobus_pin_setflags, gpiobus_pin_setflags), DEVMETHOD(gpiobus_pin_get, gpiobus_pin_get), DEVMETHOD(gpiobus_pin_set, gpiobus_pin_set), DEVMETHOD(gpiobus_pin_toggle, gpiobus_pin_toggle), + DEVMETHOD(gpiobus_pin_getname, gpiobus_pin_getname), + DEVMETHOD(gpiobus_pin_setname, gpiobus_pin_setname), DEVMETHOD_END }; driver_t gpiobus_driver = { "gpiobus", gpiobus_methods, sizeof(struct gpiobus_softc) }; devclass_t gpiobus_devclass; DRIVER_MODULE(gpiobus, gpio, gpiobus_driver, gpiobus_devclass, 0, 0); MODULE_VERSION(gpiobus, 1); Index: head/sys/dev/gpio/gpiobus_if.m =================================================================== --- head/sys/dev/gpio/gpiobus_if.m (revision 279760) +++ head/sys/dev/gpio/gpiobus_if.m (revision 279761) @@ -1,108 +1,126 @@ #- # Copyright (c) 2009 Oleksandr Tymoshenko # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $FreeBSD$ # #include #include INTERFACE gpiobus; # # Dedicate the gpio bus control for a child # METHOD int acquire_bus { device_t busdev; device_t dev; int how; }; # # Release the bus # METHOD void release_bus { device_t busdev; device_t dev; }; # # Set value of pin specifed by pin_num # METHOD int pin_set { device_t dev; device_t child; uint32_t pin_num; uint32_t pin_value; }; # # Get value of pin specifed by pin_num # METHOD int pin_get { device_t dev; device_t child; uint32_t pin_num; uint32_t *pin_value; }; # # Toggle value of pin specifed by pin_num # METHOD int pin_toggle { device_t dev; device_t child; uint32_t pin_num; }; # # Get pin capabilities # METHOD int pin_getcaps { device_t dev; device_t child; uint32_t pin_num; uint32_t *caps; }; # # Get pin flags # METHOD int pin_getflags { device_t dev; device_t child; uint32_t pin_num; uint32_t *flags; }; # # Set current configuration and capabilities # METHOD int pin_setflags { device_t dev; device_t child; uint32_t pin_num; uint32_t flags; }; + +# +# Get the pin name +# +METHOD int pin_getname { + device_t dev; + uint32_t pin_num; + char *name; +}; + +# +# Set the pin name +# +METHOD int pin_setname { + device_t dev; + uint32_t pin_num; + const char *name; +}; Index: head/sys/dev/gpio/gpiobusvar.h =================================================================== --- head/sys/dev/gpio/gpiobusvar.h (revision 279760) +++ head/sys/dev/gpio/gpiobusvar.h (revision 279761) @@ -1,117 +1,123 @@ /*- * Copyright (c) 2009 Oleksandr Tymoshenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ * */ #ifndef __GPIOBUS_H__ #define __GPIOBUS_H__ #include "opt_platform.h" #include #include #include #ifdef FDT #include #endif #include "gpio_if.h" #ifdef FDT #define GPIOBUS_IVAR(d) (struct gpiobus_ivar *) \ &((struct ofw_gpiobus_devinfo *)device_get_ivars(d))->opd_dinfo #else #define GPIOBUS_IVAR(d) (struct gpiobus_ivar *) device_get_ivars(d) #endif #define GPIOBUS_SOFTC(d) (struct gpiobus_softc *) device_get_softc(d) #define GPIOBUS_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define GPIOBUS_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define GPIOBUS_LOCK_INIT(_sc) mtx_init(&_sc->sc_mtx, \ device_get_nameunit(_sc->sc_dev), "gpiobus", MTX_DEF) #define GPIOBUS_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx) #define GPIOBUS_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED) #define GPIOBUS_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED) #define GPIOBUS_WAIT 1 #define GPIOBUS_DONTWAIT 2 +struct gpiobus_pin_data +{ + int mapped; /* pin is mapped/reserved. */ + char *name; /* pin name. */ +}; + struct gpiobus_softc { struct mtx sc_mtx; /* bus mutex */ struct rman sc_intr_rman; /* isr resources */ device_t sc_busdev; /* bus device */ device_t sc_owner; /* bus owner */ device_t sc_dev; /* driver device */ int sc_npins; /* total pins on bus */ - int *sc_pins_mapped; /* mark mapped pins */ + struct gpiobus_pin_data *sc_pins; /* pin data */ }; struct gpiobus_pin { device_t dev; /* gpio device */ uint32_t flags; /* pin flags */ uint32_t pin; /* pin number */ }; 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 */ }; #ifdef FDT struct ofw_gpiobus_devinfo { struct gpiobus_ivar opd_dinfo; struct ofw_bus_devinfo opd_obdinfo; }; static __inline int gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags) { return (GPIO_MAP_GPIOS(bus, dev, gparent, gcells, gpios, pin, flags)); } device_t ofw_gpiobus_add_fdt_child(device_t, const char *, phandle_t); int ofw_gpiobus_parse_gpios(device_t, char *, struct gpiobus_pin **); void ofw_gpiobus_register_provider(device_t); void ofw_gpiobus_unregister_provider(device_t); #endif int gpio_check_flags(uint32_t, uint32_t); device_t gpiobus_attach_bus(device_t); int gpiobus_detach_bus(device_t); int gpiobus_init_softc(device_t); int gpiobus_alloc_ivars(struct gpiobus_ivar *); void gpiobus_free_ivars(struct gpiobus_ivar *); int gpiobus_map_pin(device_t, uint32_t); extern driver_t gpiobus_driver; #endif /* __GPIOBUS_H__ */ Index: head/sys/dev/gpio/gpioc.c =================================================================== --- head/sys/dev/gpio/gpioc.c (revision 279760) +++ head/sys/dev/gpio/gpioc.c (revision 279761) @@ -1,199 +1,210 @@ /*- * Copyright (c) 2009 Oleksandr Tymoshenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" +#include "gpiobus_if.h" #undef GPIOC_DEBUG #ifdef GPIOC_DEBUG #define dprintf printf #else #define dprintf(x, arg...) #endif static int gpioc_probe(device_t dev); static int gpioc_attach(device_t dev); static int gpioc_detach(device_t dev); static d_ioctl_t gpioc_ioctl; static struct cdevsw gpioc_cdevsw = { .d_version = D_VERSION, .d_ioctl = gpioc_ioctl, .d_name = "gpioc", }; struct gpioc_softc { device_t sc_dev; /* gpiocX dev */ device_t sc_pdev; /* gpioX dev */ struct cdev *sc_ctl_dev; /* controller device */ int sc_unit; }; static int gpioc_probe(device_t dev) { device_set_desc(dev, "GPIO controller"); return (0); } static int gpioc_attach(device_t dev) { struct gpioc_softc *sc = device_get_softc(dev); sc->sc_dev = dev; sc->sc_pdev = device_get_parent(dev); sc->sc_unit = device_get_unit(dev); sc->sc_ctl_dev = make_dev(&gpioc_cdevsw, sc->sc_unit, UID_ROOT, GID_WHEEL, 0600, "gpioc%d", sc->sc_unit); if (!sc->sc_ctl_dev) { printf("Failed to create gpioc%d", sc->sc_unit); return (ENXIO); } sc->sc_ctl_dev->si_drv1 = sc; return (0); } static int gpioc_detach(device_t dev) { struct gpioc_softc *sc = device_get_softc(dev); int err; if (sc->sc_ctl_dev) destroy_dev(sc->sc_ctl_dev); if ((err = bus_generic_detach(dev)) != 0) return (err); return (0); } static int gpioc_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag, struct thread *td) { + device_t bus; int max_pin, res; struct gpioc_softc *sc = cdev->si_drv1; struct gpio_pin pin; struct gpio_req req; uint32_t caps; + bus = GPIO_GET_BUS(sc->sc_pdev); + if (bus == NULL) + return (EINVAL); switch (cmd) { case GPIOMAXPIN: max_pin = -1; res = GPIO_PIN_MAX(sc->sc_pdev, &max_pin); bcopy(&max_pin, arg, sizeof(max_pin)); break; case GPIOGETCONFIG: bcopy(arg, &pin, sizeof(pin)); dprintf("get config pin %d\n", pin.gp_pin); res = GPIO_PIN_GETFLAGS(sc->sc_pdev, pin.gp_pin, &pin.gp_flags); /* Fail early */ if (res) break; GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, &pin.gp_caps); - GPIO_PIN_GETNAME(sc->sc_pdev, pin.gp_pin, pin.gp_name); + GPIOBUS_PIN_GETNAME(bus, pin.gp_pin, pin.gp_name); bcopy(&pin, arg, sizeof(pin)); break; case GPIOSETCONFIG: bcopy(arg, &pin, sizeof(pin)); dprintf("set config pin %d\n", pin.gp_pin); res = GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, &caps); if (res == 0) res = gpio_check_flags(caps, pin.gp_flags); if (res == 0) res = GPIO_PIN_SETFLAGS(sc->sc_pdev, pin.gp_pin, pin.gp_flags); break; case GPIOGET: bcopy(arg, &req, sizeof(req)); res = GPIO_PIN_GET(sc->sc_pdev, req.gp_pin, &req.gp_value); dprintf("read pin %d -> %d\n", req.gp_pin, req.gp_value); bcopy(&req, arg, sizeof(req)); break; case GPIOSET: bcopy(arg, &req, sizeof(req)); res = GPIO_PIN_SET(sc->sc_pdev, req.gp_pin, req.gp_value); dprintf("write pin %d -> %d\n", req.gp_pin, req.gp_value); break; case GPIOTOGGLE: bcopy(arg, &req, sizeof(req)); dprintf("toggle pin %d\n", req.gp_pin); res = GPIO_PIN_TOGGLE(sc->sc_pdev, req.gp_pin); + break; + case GPIOSETNAME: + bcopy(arg, &pin, sizeof(pin)); + dprintf("set name on pin %d\n", pin.gp_pin); + res = GPIOBUS_PIN_SETNAME(bus, pin.gp_pin, + pin.gp_name); break; default: return (ENOTTY); break; } return (res); } static device_method_t gpioc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, gpioc_probe), DEVMETHOD(device_attach, gpioc_attach), DEVMETHOD(device_detach, gpioc_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), { 0, 0 } }; driver_t gpioc_driver = { "gpioc", gpioc_methods, sizeof(struct gpioc_softc) }; devclass_t gpioc_devclass; DRIVER_MODULE(gpioc, gpio, gpioc_driver, gpioc_devclass, 0, 0); MODULE_VERSION(gpioc, 1); Index: head/sys/dev/gpio/ofw_gpiobus.c =================================================================== --- head/sys/dev/gpio/ofw_gpiobus.c (revision 279760) +++ head/sys/dev/gpio/ofw_gpiobus.c (revision 279761) @@ -1,403 +1,412 @@ /*- * Copyright (c) 2009, Nathan Whitehorn * Copyright (c) 2013, Luiz Otavio O Souza * Copyright (c) 2013 The FreeBSD Foundation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include +#include "gpiobus_if.h" + static struct ofw_gpiobus_devinfo *ofw_gpiobus_setup_devinfo(device_t, device_t, phandle_t); static void ofw_gpiobus_destroy_devinfo(device_t, struct ofw_gpiobus_devinfo *); static int ofw_gpiobus_parse_gpios_impl(device_t, phandle_t, char *, struct gpiobus_softc *, struct gpiobus_pin **); device_t ofw_gpiobus_add_fdt_child(device_t bus, const char *drvname, phandle_t child) { device_t childdev; + int i; + struct gpiobus_ivar *devi; struct ofw_gpiobus_devinfo *dinfo; /* * Set up the GPIO child and OFW bus layer devinfo and add it to bus. */ childdev = device_add_child(bus, drvname, -1); if (childdev == NULL) return (NULL); dinfo = ofw_gpiobus_setup_devinfo(bus, childdev, child); if (dinfo == NULL) { device_delete_child(bus, childdev); return (NULL); } if (device_probe_and_attach(childdev) != 0) { ofw_gpiobus_destroy_devinfo(bus, dinfo); device_delete_child(bus, childdev); return (NULL); } + /* Use the child name as pin name. */ + devi = &dinfo->opd_dinfo; + for (i = 0; i < devi->npins; i++) + GPIOBUS_PIN_SETNAME(bus, devi->pins[i], + device_get_nameunit(childdev)); return (childdev); } int ofw_gpiobus_parse_gpios(device_t consumer, char *pname, struct gpiobus_pin **pins) { return (ofw_gpiobus_parse_gpios_impl(consumer, ofw_bus_get_node(consumer), pname, NULL, pins)); } void ofw_gpiobus_register_provider(device_t provider) { phandle_t node; node = ofw_bus_get_node(provider); OF_device_register_xref(OF_xref_from_node(node), provider); } void ofw_gpiobus_unregister_provider(device_t provider) { phandle_t node; node = ofw_bus_get_node(provider); OF_device_register_xref(OF_xref_from_node(node), NULL); } static struct ofw_gpiobus_devinfo * ofw_gpiobus_setup_devinfo(device_t bus, device_t child, phandle_t node) { int i, npins; struct gpiobus_ivar *devi; struct gpiobus_pin *pins; struct gpiobus_softc *sc; struct ofw_gpiobus_devinfo *dinfo; sc = device_get_softc(bus); dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_NOWAIT | M_ZERO); if (dinfo == NULL) return (NULL); if (ofw_bus_gen_setup_devinfo(&dinfo->opd_obdinfo, node) != 0) { free(dinfo, M_DEVBUF); return (NULL); } /* Parse the gpios property for the child. */ npins = ofw_gpiobus_parse_gpios_impl(child, node, "gpios", sc, &pins); if (npins <= 0) { ofw_bus_gen_destroy_devinfo(&dinfo->opd_obdinfo); free(dinfo, M_DEVBUF); return (NULL); } /* Initialize the irq resource list. */ resource_list_init(&dinfo->opd_dinfo.rl); /* Allocate the child ivars and copy the parsed pin data. */ devi = &dinfo->opd_dinfo; devi->npins = (uint32_t)npins; if (gpiobus_alloc_ivars(devi) != 0) { free(pins, M_DEVBUF); ofw_gpiobus_destroy_devinfo(bus, dinfo); return (NULL); } for (i = 0; i < devi->npins; i++) { devi->flags[i] = pins[i].flags; devi->pins[i] = pins[i].pin; } free(pins, M_DEVBUF); /* Parse the interrupt resources. */ if (ofw_bus_intr_to_rl(bus, node, &dinfo->opd_dinfo.rl) != 0) { ofw_gpiobus_destroy_devinfo(bus, dinfo); return (NULL); } device_set_ivars(child, dinfo); return (dinfo); } static void ofw_gpiobus_destroy_devinfo(device_t bus, struct ofw_gpiobus_devinfo *dinfo) { int i; struct gpiobus_ivar *devi; struct gpiobus_softc *sc; sc = device_get_softc(bus); devi = &dinfo->opd_dinfo; for (i = 0; i < devi->npins; i++) { if (devi->pins[i] > sc->sc_npins) continue; - sc->sc_pins_mapped[devi->pins[i]] = 0; + sc->sc_pins[devi->pins[i]].mapped = 0; } gpiobus_free_ivars(devi); resource_list_free(&dinfo->opd_dinfo.rl); ofw_bus_gen_destroy_devinfo(&dinfo->opd_obdinfo); free(dinfo, M_DEVBUF); } static int ofw_gpiobus_parse_gpios_impl(device_t consumer, phandle_t cnode, char *pname, struct gpiobus_softc *bussc, struct gpiobus_pin **pins) { int gpiocells, i, j, ncells, npins; pcell_t *gpios; phandle_t gpio; ncells = OF_getencprop_alloc(cnode, pname, sizeof(*gpios), (void **)&gpios); if (ncells == -1) { device_printf(consumer, "Warning: No %s specified in fdt data; " "device may not function.\n", pname); return (-1); } /* * The gpio-specifier is controller independent, the first pcell has * the reference to the GPIO controller phandler. * Count the number of encoded gpio-specifiers on the first pass. */ i = 0; npins = 0; while (i < ncells) { /* Allow NULL specifiers. */ if (gpios[i] == 0) { npins++; i++; continue; } gpio = OF_node_from_xref(gpios[i]); /* If we have bussc, ignore devices from other gpios. */ if (bussc != NULL) if (ofw_bus_get_node(bussc->sc_dev) != gpio) return (0); /* * Check for gpio-controller property and read the #gpio-cells * for this GPIO controller. */ if (!OF_hasprop(gpio, "gpio-controller") || OF_getencprop(gpio, "#gpio-cells", &gpiocells, sizeof(gpiocells)) < 0) { device_printf(consumer, "gpio reference is not a gpio-controller.\n"); free(gpios, M_OFWPROP); return (-1); } if (ncells - i < gpiocells + 1) { device_printf(consumer, "%s cells doesn't match #gpio-cells.\n", pname); return (-1); } npins++; i += gpiocells + 1; } if (npins == 0 || pins == NULL) { if (npins == 0) device_printf(consumer, "no pin specified in %s.\n", pname); free(gpios, M_OFWPROP); return (npins); } *pins = malloc(sizeof(struct gpiobus_pin) * npins, M_DEVBUF, M_NOWAIT | M_ZERO); if (*pins == NULL) { free(gpios, M_OFWPROP); return (-1); } /* Decode the gpio specifier on the second pass. */ i = 0; j = 0; while (i < ncells) { /* Allow NULL specifiers. */ if (gpios[i] == 0) { j++; i++; continue; } gpio = OF_node_from_xref(gpios[i]); /* Read gpio-cells property for this GPIO controller. */ if (OF_getencprop(gpio, "#gpio-cells", &gpiocells, sizeof(gpiocells)) < 0) { device_printf(consumer, "gpio does not have the #gpio-cells property.\n"); goto fail; } /* Return the device reference for the GPIO controller. */ (*pins)[j].dev = OF_device_from_xref(gpios[i]); if ((*pins)[j].dev == NULL) { device_printf(consumer, "no device registered for the gpio controller.\n"); goto fail; } /* * If the gpiobus softc is NULL we use the GPIO_GET_BUS() to * retrieve it. The GPIO_GET_BUS() method is only valid after * the child is probed and attached. */ if (bussc == NULL) { if (GPIO_GET_BUS((*pins)[j].dev) == NULL) { device_printf(consumer, "no gpiobus reference for %s.\n", device_get_nameunit((*pins)[j].dev)); goto fail; } bussc = device_get_softc(GPIO_GET_BUS((*pins)[j].dev)); } /* Get the GPIO pin number and flags. */ if (gpio_map_gpios((*pins)[j].dev, cnode, gpio, gpiocells, &gpios[i + 1], &(*pins)[j].pin, &(*pins)[j].flags) != 0) { device_printf(consumer, "cannot map the gpios specifier.\n"); goto fail; } /* Reserve the GPIO pin. */ if (gpiobus_map_pin(bussc->sc_busdev, (*pins)[j].pin) != 0) goto fail; j++; i += gpiocells + 1; } free(gpios, M_OFWPROP); return (npins); fail: free(gpios, M_OFWPROP); free(*pins, M_DEVBUF); return (-1); } static int ofw_gpiobus_probe(device_t dev) { if (ofw_bus_get_node(dev) == -1) return (ENXIO); device_set_desc(dev, "OFW GPIO bus"); return (0); } static int ofw_gpiobus_attach(device_t dev) { int err; phandle_t child; err = gpiobus_init_softc(dev); if (err != 0) return (err); bus_generic_probe(dev); bus_enumerate_hinted_children(dev); /* * Attach the children represented in the device tree. */ for (child = OF_child(ofw_bus_get_node(dev)); child != 0; child = OF_peer(child)) { if (!OF_hasprop(child, "gpios")) continue; if (ofw_gpiobus_add_fdt_child(dev, NULL, child) == NULL) continue; } return (bus_generic_attach(dev)); } static device_t ofw_gpiobus_add_child(device_t dev, u_int order, const char *name, int unit) { device_t child; struct ofw_gpiobus_devinfo *devi; child = device_add_child_ordered(dev, order, name, unit); if (child == NULL) return (child); devi = malloc(sizeof(struct ofw_gpiobus_devinfo), M_DEVBUF, M_NOWAIT | M_ZERO); if (devi == NULL) { device_delete_child(dev, child); return (0); } /* * NULL all the OFW-related parts of the ivars for non-OFW * children. */ devi->opd_obdinfo.obd_node = -1; devi->opd_obdinfo.obd_name = NULL; devi->opd_obdinfo.obd_compat = NULL; devi->opd_obdinfo.obd_type = NULL; devi->opd_obdinfo.obd_model = NULL; device_set_ivars(child, devi); return (child); } static const struct ofw_bus_devinfo * ofw_gpiobus_get_devinfo(device_t bus, device_t dev) { struct ofw_gpiobus_devinfo *dinfo; dinfo = device_get_ivars(dev); return (&dinfo->opd_obdinfo); } static device_method_t ofw_gpiobus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ofw_gpiobus_probe), DEVMETHOD(device_attach, ofw_gpiobus_attach), /* Bus interface */ DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str), DEVMETHOD(bus_add_child, ofw_gpiobus_add_child), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_devinfo, ofw_gpiobus_get_devinfo), DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), DEVMETHOD_END }; static devclass_t ofwgpiobus_devclass; DEFINE_CLASS_1(gpiobus, ofw_gpiobus_driver, ofw_gpiobus_methods, sizeof(struct gpiobus_softc), gpiobus_driver); DRIVER_MODULE(ofw_gpiobus, gpio, ofw_gpiobus_driver, ofwgpiobus_devclass, 0, 0); MODULE_VERSION(ofw_gpiobus, 1); MODULE_DEPEND(ofw_gpiobus, gpiobus, 1, 1, 1); Index: head/sys/sys/gpio.h =================================================================== --- head/sys/sys/gpio.h (revision 279760) +++ head/sys/sys/gpio.h (revision 279761) @@ -1,97 +1,98 @@ /* $NetBSD: gpio.h,v 1.7 2009/09/25 20:27:50 mbalmer Exp $ */ /* $OpenBSD: gpio.h,v 1.7 2008/11/26 14:51:20 mbalmer Exp $ */ /*- * Copyright (c) 2009, Oleksandr Tymoshenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ * */ /* * Copyright (c) 2009 Marc Balmer * Copyright (c) 2004 Alexander Yurchenko * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef __GPIO_H__ #define __GPIO_H__ #include /* GPIO pin states */ #define GPIO_PIN_LOW 0x00 /* low level (logical 0) */ #define GPIO_PIN_HIGH 0x01 /* high level (logical 1) */ /* Max name length of a pin */ #define GPIOMAXNAME 64 /* GPIO pin configuration flags */ #define GPIO_PIN_INPUT 0x0001 /* input direction */ #define GPIO_PIN_OUTPUT 0x0002 /* output direction */ #define GPIO_PIN_OPENDRAIN 0x0004 /* open-drain output */ #define GPIO_PIN_PUSHPULL 0x0008 /* push-pull output */ #define GPIO_PIN_TRISTATE 0x0010 /* output disabled */ #define GPIO_PIN_PULLUP 0x0020 /* internal pull-up enabled */ #define GPIO_PIN_PULLDOWN 0x0040 /* internal pull-down enabled */ #define GPIO_PIN_INVIN 0x0080 /* invert input */ #define GPIO_PIN_INVOUT 0x0100 /* invert output */ #define GPIO_PIN_PULSATE 0x0200 /* pulsate in hardware */ struct gpio_pin { uint32_t gp_pin; /* pin number */ char gp_name[GPIOMAXNAME]; /* human-readable name */ uint32_t gp_caps; /* capabilities */ uint32_t gp_flags; /* current flags */ }; /* GPIO pin request (read/write/toggle) */ struct gpio_req { uint32_t gp_pin; /* pin number */ uint32_t gp_value; /* value */ }; /* * ioctls */ #define GPIOMAXPIN _IOR('G', 0, int) #define GPIOGETCONFIG _IOWR('G', 1, struct gpio_pin) #define GPIOSETCONFIG _IOW('G', 2, struct gpio_pin) #define GPIOGET _IOWR('G', 3, struct gpio_req) #define GPIOSET _IOW('G', 4, struct gpio_req) #define GPIOTOGGLE _IOWR('G', 5, struct gpio_req) +#define GPIOSETNAME _IOW('G', 6, struct gpio_pin) #endif /* __GPIO_H__ */ Index: head/usr.sbin/gpioctl/gpioctl.8 =================================================================== --- head/usr.sbin/gpioctl/gpioctl.8 (revision 279760) +++ head/usr.sbin/gpioctl/gpioctl.8 (revision 279761) @@ -1,124 +1,131 @@ .\" Copyright (c) 1980, 1991, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ .\" -.Dd November 7, 2013 +.Dd March 7, 2015 .Dt GPIOCTL 1 .Os .Sh NAME .Nm gpioctl .Nd GPIO control utility .Sh SYNOPSIS .Nm -.Cm -l .Op Fl f Ar ctldev +.Cm -l .Op Fl v .Nm -.Cm -t .Op Fl f Ar ctldev +.Cm -t .Ar pin .Nm -.Cm -c .Op Fl f Ar ctldev +.Cm -c .Ar pin .Ar flag .Op flag ... .Nm +.Op Fl f Ar ctldev +.Cm -n +.Ar pin +.Ar pin-name +.Nm .Op Cm -f Ar ctldev .Ar pin .Ar [0|1] .Sh DESCRIPTION The .Nm utility could be used to manage GPIO pins from userland and list available pins. .Pp The options are as follows: .Bl -tag -width ".Fl f Ar ctldev" .It Fl c Ar pin Ar flag Op flag ... Configure pin by setting provided flags. The following flags are currently defined: .Bl -tag -offset indent -width ".Cm PULSE" .It Cm IN Input pin .It Cm OUT Output pin .It Cm OD Open drain pin .It Cm PP Push pull pin .It Cm TS Tristate pin .It Cm PU Pull-up pin .It Cm PD Pull-down pin .It Cm II Inverted input pin .It Cm IO Inverted output pin .El .It Fl f Ar ctldev GPIO controller device to use If not specified, defaults to .Pa /dev/gpioc0 .It Fl l list available pins +.It Fl n Ar pin Ar pin-name +set the name used to describe the pin .It Fl t Ar pin toggle value of provided pin number .It Fl v be verbose: for each listed pin print current configuration .El .Sh EXAMPLES .Bl -bullet .It List pins available on GPIO controller defined by device /dev/gpioc0 .Pp gpioctl -f /dev/gpioc0 -l .It Set the value of pin 12 to 1 .Pp gpioctl -f /dev/gpioc0 12 1 .It Configure pin 12 to be input pin .Pp gpioctl -f /dev/gpioc0 -c 12 IN .El .Sh SEE ALSO .Xr gpio 4 , .Xr gpioiic 4 , .Xr gpioled 4 .Sh HISTORY The .Nm utility appeared in .Fx 9.0 . .Sh AUTHORS .An -nosplit The .Nm utility and this manual page were written by .An Oleksandr Tymoshenko Aq Mt gonzo@freebsd.org . Index: head/usr.sbin/gpioctl/gpioctl.c =================================================================== --- head/usr.sbin/gpioctl/gpioctl.c (revision 279760) +++ head/usr.sbin/gpioctl/gpioctl.c (revision 279761) @@ -1,307 +1,327 @@ /*- * Copyright (c) 2009, Oleksandr Tymoshenko * Copyright (c) 2014, Rui Paulo * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include struct flag_desc { const char *name; uint32_t flag; }; static struct flag_desc gpio_flags[] = { { "IN", GPIO_PIN_INPUT }, { "OUT", GPIO_PIN_OUTPUT }, { "OD", GPIO_PIN_OPENDRAIN }, { "PP", GPIO_PIN_PUSHPULL }, { "TS", GPIO_PIN_TRISTATE }, { "PU", GPIO_PIN_PULLUP }, { "PD", GPIO_PIN_PULLDOWN }, { "II", GPIO_PIN_INVIN }, { "IO", GPIO_PIN_INVOUT }, { "PULSE", GPIO_PIN_PULSATE }, { NULL, 0 }, }; int str2cap(const char *str); static void usage(void) { fprintf(stderr, "Usage:\n"); fprintf(stderr, "\tgpioctl [-f ctldev] -l [-v]\n"); fprintf(stderr, "\tgpioctl [-f ctldev] -t pin\n"); fprintf(stderr, "\tgpioctl [-f ctldev] -c pin flag ...\n"); + fprintf(stderr, "\tgpioctl [-f ctldev] -n pin pin-name\n"); fprintf(stderr, "\tgpioctl [-f ctldev] pin [0|1]\n"); exit(1); } static const char * cap2str(uint32_t cap) { struct flag_desc * pdesc = gpio_flags; while (pdesc->name) { if (pdesc->flag == cap) return pdesc->name; pdesc++; } return "UNKNOWN"; } int str2cap(const char *str) { struct flag_desc * pdesc = gpio_flags; while (pdesc->name) { if (strcasecmp(str, pdesc->name) == 0) return pdesc->flag; pdesc++; } return (-1); } /* * Our handmade function for converting string to number */ static int str2int(const char *s, int *ok) { char *endptr; int res = strtod(s, &endptr); if (endptr != s + strlen(s) ) *ok = 0; else *ok = 1; return res; } static void print_caps(int caps) { int i, need_coma; need_coma = 0; printf("<"); for (i = 0; i < 32; i++) { if (caps & (1 << i)) { if (need_coma) printf(","); printf("%s", cap2str(1 << i)); need_coma = 1; } } printf(">"); } static void dump_pins(gpio_handle_t handle, int verbose) { int i, maxpin, pinv; gpio_config_t *cfgs; gpio_config_t *pin; maxpin = gpio_pin_list(handle, &cfgs); if (maxpin < 0) { perror("gpio_pin_list"); exit(1); } for (i = 0; i <= maxpin; i++) { pin = cfgs + i; pinv = gpio_pin_get(handle, pin->g_pin); printf("pin %02d:\t%d\t%s", pin->g_pin, pinv, pin->g_name); print_caps(pin->g_flags); if (verbose) { printf(", caps:"); print_caps(pin->g_caps); } printf("\n"); } free(cfgs); } static void fail(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); exit(1); } int main(int argc, char **argv) { int i; gpio_config_t pin; gpio_handle_t handle; char *ctlfile = NULL; int pinn, pinv, ch; int flags, flag, ok; - int config, toggle, verbose, list; + int config, list, name, toggle, verbose; - config = toggle = verbose = list = pinn = 0; + config = toggle = verbose = list = name = pinn = 0; - while ((ch = getopt(argc, argv, "c:f:lt:v")) != -1) { + while ((ch = getopt(argc, argv, "c:f:ln:t:v")) != -1) { switch (ch) { case 'c': config = 1; pinn = str2int(optarg, &ok); if (!ok) fail("Invalid pin number: %s\n", optarg); break; case 'f': ctlfile = optarg; break; case 'l': list = 1; break; + case 'n': + name = 1; + pinn = str2int(optarg, &ok); + if (!ok) + fail("Invalid pin number: %s\n", optarg); + break; case 't': toggle = 1; pinn = str2int(optarg, &ok); if (!ok) fail("Invalid pin number: %s\n", optarg); break; case 'v': verbose = 1; break; default: usage(); break; } } argv += optind; argc -= optind; if (ctlfile == NULL) handle = gpio_open(0); else handle = gpio_open_device(ctlfile); if (handle == GPIO_INVALID_HANDLE) { perror("gpio_open"); exit(1); + } + + /* Set the pin name. */ + if (name) { + if (argc == 0) { + usage(); + exit(1); + } + if (gpio_pin_set_name(handle, pinn, argv[0]) < 0) { + perror("gpio_pin_set_name"); + exit(1); + } + exit(0); } if (list) { dump_pins(handle, verbose); gpio_close(handle); exit(0); } if (toggle) { /* * -t pin assumes no additional arguments */ if (argc > 0) { usage(); exit(1); } if (gpio_pin_toggle(handle, pinn) < 0) { perror("gpio_pin_toggle"); exit(1); } gpio_close(handle); exit(0); } if (config) { flags = 0; for (i = 0; i < argc; i++) { flag = str2cap(argv[i]); if (flag < 0) fail("Invalid flag: %s\n", argv[i]); flags |= flag; } pin.g_pin = pinn; pin.g_flags = flags; if (gpio_pin_set_flags(handle, &pin) < 0) { perror("gpio_pin_set_flags"); exit(1); } exit(0); } /* * Last two cases - set value or print value */ if ((argc == 0) || (argc > 2)) { usage(); exit(1); } pinn = str2int(argv[0], &ok); if (!ok) fail("Invalid pin number: %s\n", argv[0]); /* * Read pin value */ if (argc == 1) { pinv = gpio_pin_get(handle, pinn); if (pinv < 0) { perror("gpio_pin_get"); exit(1); } printf("%d\n", pinv); exit(0); } /* Is it valid number (0 or 1) ? */ pinv = str2int(argv[1], &ok); if (!ok || ((pinv != 0) && (pinv != 1))) fail("Invalid pin value: %s\n", argv[1]); /* * Set pin value */ if (gpio_pin_set(handle, pinn, pinv) < 0) { perror("gpio_pin_set"); exit(1); } gpio_close(handle); exit(0); }