diff --git a/sys/conf/files b/sys/conf/files index 7793ec7..b302879 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -974,6 +974,13 @@ dev/fxp/if_fxp.c optional fxp inet dev/gem/if_gem.c optional gem dev/gem/if_gem_pci.c optional gem pci dev/gem/if_gem_sbus.c optional gem sbus +dev/gpio/gpiobus.c optional gpio \ + dependency "gpiobus_if.h" +dev/gpio/gpioc.c optional gpio \ + dependency "gpio_if.h" +dev/gpio/gpioled.c optional gpioled +dev/gpio/gpio_if.m optional gpio +dev/gpio/gpiobus_if.m optional gpio dev/hatm/if_hatm.c optional hatm pci dev/hatm/if_hatm_intr.c optional hatm pci dev/hatm/if_hatm_ioctl.c optional hatm pci diff --git a/sys/dev/gpio/gpio_if.m b/sys/dev/gpio/gpio_if.m new file mode 100644 index 0000000..8e29607 --- /dev/null +++ b/sys/dev/gpio/gpio_if.m @@ -0,0 +1,102 @@ +#- +# 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 gpio; + +# +# Get total number of pins +# +METHOD int pin_max { + device_t dev; + int *npins; +}; + +# +# Set value of pin specifed by pin_num +# +METHOD int pin_set { + device_t dev; + uint32_t pin_num; + uint32_t pin_value; +}; + +# +# Get value of pin specifed by pin_num +# +METHOD int pin_get { + device_t dev; + uint32_t pin_num; + uint32_t *pin_value; +}; + +# +# Toggle value of pin specifed by pin_num +# +METHOD int pin_toggle { + device_t dev; + uint32_t pin_num; +}; + +# +# Get pin capabilities +# +METHOD int pin_getcaps { + device_t dev; + uint32_t pin_num; + uint32_t *caps; +}; + +# +# Get pin flags +# +METHOD int pin_getflags { + device_t dev; + uint32_t pin_num; + uint32_t *flags; +}; + +# +# Get pin name +# +METHOD int pin_getname { + device_t dev; + uint32_t pin_num; + char *name; +}; + +# +# Set current configuration and capabilities +# +METHOD int pin_setflags { + device_t dev; + uint32_t pin_num; + uint32_t flags; +}; diff --git a/sys/dev/gpio/gpiobus.c b/sys/dev/gpio/gpiobus.c new file mode 100644 index 0000000..05c558f --- /dev/null +++ b/sys/dev/gpio/gpiobus.c @@ -0,0 +1,390 @@ +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include "gpio_if.h" +#include "gpiobus_if.h" + +static void gpiobus_print_pins(struct gpiobus_ivar *); +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 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, int, const char *, int); +static void gpiobus_hinted_child(device_t, const char *, int); + +/* + * GPIOBUS interface + */ +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); + +static void +gpiobus_print_pins(struct gpiobus_ivar *devi) +{ + int range_start, range_stop, need_coma; + int i; + + if (devi->npins == 0) + return; + + need_coma = 0; + range_start = range_stop = devi->pins[0]; + for(i = 1; i < devi->npins-1; i++) { + if (devi->pins[i] != (range_stop + 1)) { + if (need_coma) + printf(","); + if (range_start != range_stop) + printf("%d-%d", range_start, range_stop); + else + printf("%d", range_start); + + range_start = range_stop = devi->pins[i]; + need_coma = 1; + } + else + range_stop++; + } + + if (need_coma) + printf(","); + if (range_start != range_stop) + printf("%d-%d", range_start, range_stop); + else + printf("%d", range_start); +} + +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"); + return (EINVAL); + } + + devi->npins = npins; + devi->pins = malloc(sizeof(uint32_t) * devi->npins, M_DEVBUF, + M_NOWAIT | M_ZERO); + + if (!devi->pins) + return (ENOMEM); + + npins = 0; + for (i = 0; i < 32; i++) { + + if ((mask & (1 << i)) == 0) + continue; + + if (i >= sc->npins) { + device_printf(child, + "invalid pin %d, max: %d\n", i, sc->npins - 1); + return (EINVAL); + } + + devi->pins[npins++] = i; + /* + * Mark pin as mapped and give warning if it's already mapped + */ + if (sc->pins_mapped[i]) { + device_printf(child, + "warning: pin %d is already mapped\n", i); + return (EINVAL); + } + sc->pins_mapped[i] = 1; + } + + return (0); +} + +static int +gpiobus_probe(device_t dev) +{ + device_set_desc(dev, "GPIO bus"); + return (0); +} + +static int +gpiobus_attach(device_t dev) +{ + struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev); + int res; + + sc->dev = dev; + sc->pdev = device_get_parent(dev); + res = GPIO_PIN_MAX(sc->pdev, &sc->npins); + if (res) + return (ENXIO); + + /* + * Increase to get number of pins + */ + sc->npins++; + + KASSERT(sc->npins != 0, ("GPIO device with no pins")); + + sc->pins_mapped = malloc(sizeof(int) * sc->npins, M_DEVBUF, + M_NOWAIT | M_ZERO); + + if (!sc->pins_mapped) + return (ENOMEM); + + /* + * Get parent's pins and mark them as unmapped + */ + 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 = GPIOBUS_SOFTC(dev); + int err, ndevs, i; + device_t *devlist; + + 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]); + + if (sc->pins_mapped) { + free(sc->pins_mapped, M_DEVBUF); + sc->pins_mapped = NULL; + } + free(devlist, M_TEMP); + + 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 int +gpiobus_print_child(device_t dev, device_t child) +{ + struct gpiobus_ivar *devi = GPIOBUS_IVAR(child); + int retval = 0; + + retval += bus_print_child_header(dev, child); + retval += printf(" at pin(s) "); + gpiobus_print_pins(devi); + 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 = GPIOBUS_IVAR(child); + + snprintf(buf, buflen, "pins=?"); + 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, 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); + } + 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 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); +} + +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); + + if (pin >= devi->npins) + return (EINVAL); + + return GPIO_PIN_SETFLAGS(sc->pdev, 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->pdev, 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->pdev, 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->pdev, 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->pdev, 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->pdev, devi->pins[pin]); +} + +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_add_child, gpiobus_add_child), + DEVMETHOD(bus_print_child, gpiobus_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + 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_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), + + { 0, 0 } +}; + +static 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); diff --git a/sys/dev/gpio/gpiobus_if.m b/sys/dev/gpio/gpiobus_if.m new file mode 100644 index 0000000..a406e01 --- /dev/null +++ b/sys/dev/gpio/gpiobus_if.m @@ -0,0 +1,91 @@ +#- +# 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; + +# +# 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; +}; diff --git a/sys/dev/gpio/gpiobusvar.h b/sys/dev/gpio/gpiobusvar.h new file mode 100644 index 0000000..f1a290f --- /dev/null +++ b/sys/dev/gpio/gpiobusvar.h @@ -0,0 +1,22 @@ +#ifndef __GPIOBUS_H__ +#define __GPIOBUS_H__ + +#define GPIOBUS_IVAR(d) (struct gpiobus_ivar *) device_get_ivars(d) +#define GPIOBUS_SOFTC(d) (struct gpiobus_softc *) device_get_softc(d) + +struct gpiobus_softc +{ + device_t pdev; /* bus device */ + device_t dev; + int npins; /* total pins on bus */ + int *pins_mapped; /* mark mapped pins */ +}; + + +struct gpiobus_ivar +{ + uint32_t npins; /* pins total */ + uint32_t *pins; /* pins map */ +}; + +#endif /* __GPIOBUS_H__ */ diff --git a/sys/dev/gpio/gpioc.c b/sys/dev/gpio/gpioc.c new file mode 100644 index 0000000..e507a73 --- /dev/null +++ b/sys/dev/gpio/gpioc.c @@ -0,0 +1,174 @@ +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "gpio_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", +#if __FreeBSD_version >= 800039 + .d_flags = D_PSEUDO | D_NEEDMINOR +#endif +}; + +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) +{ + int max_pin, res; + struct gpioc_softc *sc = cdev->si_drv1; + struct gpio_pin pin; + struct gpio_req req; + + 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); + 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_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; + 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 } +}; + +static 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); diff --git a/sys/dev/gpio/gpioled.c b/sys/dev/gpio/gpioled.c new file mode 100644 index 0000000..082d5e8 --- /dev/null +++ b/sys/dev/gpio/gpioled.c @@ -0,0 +1,137 @@ +/*- + * 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 ``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 +#include +#include + +#include +#include +#include "gpiobus_if.h" + +/* + * Only one pin for led + */ +#define GPIOLED_PIN 0 + +#define GPIOLED_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define GPIOLED_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define GPIOLED_LOCK_INIT(_sc) \ + mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ + "gpioled", MTX_DEF) +#define GPIOLED_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); + +struct gpioled_softc +{ + device_t sc_dev; + device_t sc_busdev; + struct mtx sc_mtx; + struct cdev *sc_leddev; +}; + +static void gpioled_control(void *, int); +static int gpioled_probe(device_t); +static int gpioled_attach(device_t); +static int gpioled_detach(device_t); + +static void +gpioled_control(void *priv, int onoff) +{ + struct gpioled_softc *sc = priv; + GPIOLED_LOCK(sc); + GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, GPIOLED_PIN, + onoff ? GPIO_PIN_HIGH : GPIO_PIN_LOW); + GPIOLED_UNLOCK(sc); +} + +static int +gpioled_probe(device_t dev) +{ + device_set_desc(dev, "GPIO led"); + return (0); +} + +static int +gpioled_attach(device_t dev) +{ + struct gpioled_softc *sc; + const char *name; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + sc->sc_busdev = device_get_parent(dev); + GPIOLED_LOCK_INIT(sc); + if (resource_string_value(device_get_name(dev), + device_get_unit(dev), "name", &name)) + name = NULL; + + sc->sc_leddev = led_create(gpioled_control, sc, name ? name : + device_get_nameunit(dev)); + + return (0); +} + +static int +gpioled_detach(device_t dev) +{ + struct gpioled_softc *sc; + + sc = device_get_softc(dev); + if (sc->sc_leddev) { + led_destroy(sc->sc_leddev); + sc->sc_leddev = NULL; + } + GPIOLED_LOCK_DESTROY(sc); + return (0); +} + +static devclass_t gpioled_devclass; + +static device_method_t gpioled_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, gpioled_probe), + DEVMETHOD(device_attach, gpioled_attach), + DEVMETHOD(device_detach, gpioled_detach), + + { 0, 0 } +}; + +static driver_t gpioled_driver = { + "gpioled", + gpioled_methods, + sizeof(struct gpioled_softc), +}; + +DRIVER_MODULE(gpioled, gpiobus, gpioled_driver, gpioled_devclass, 0, 0); diff --git a/sys/mips/atheros/ar71xx_gpio.c b/sys/mips/atheros/ar71xx_gpio.c new file mode 100644 index 0000000..c66266e --- /dev/null +++ b/sys/mips/atheros/ar71xx_gpio.c @@ -0,0 +1,446 @@ +/*- + * Copyright (c) 2009, Oleksandr Tymoshenko + * Copyright (c) 2009, Luiz Otavio O Souza. + * 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. + */ + +/* + * GPIO driver for AR71xx + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "gpio_if.h" + +#define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT) + +struct ar71xx_gpio_pin { + const char *name; + int pin; + int flags; +}; + +static struct ar71xx_gpio_pin ar71xx_gpio_pins[] = { + { "RFled", 2, GPIO_PIN_OUTPUT}, + { "SW4", 8, GPIO_PIN_INPUT}, + { NULL, 0, 0}, +}; + +/* + * Helpers + */ +static void ar71xx_gpio_function_enable(struct ar71xx_gpio_softc *sc, + uint32_t mask); +static void ar71xx_gpio_function_disable(struct ar71xx_gpio_softc *sc, + uint32_t mask); +static void ar71xx_gpio_pin_configure(struct ar71xx_gpio_softc *sc, + struct gpio_pin *pin, uint32_t flags); + +/* + * Driver stuff + */ +static int ar71xx_gpio_probe(device_t dev); +static int ar71xx_gpio_attach(device_t dev); +static int ar71xx_gpio_detach(device_t dev); +static int ar71xx_gpio_filter(void *arg); +static void ar71xx_gpio_intr(void *arg); + +/* + * GPIO interface + */ +static int ar71xx_gpio_pin_max(device_t dev, int *maxpin); +static int ar71xx_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps); +static int ar71xx_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t + *flags); +static int ar71xx_gpio_pin_getname(device_t dev, uint32_t pin, char *name); +static int ar71xx_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags); +static int ar71xx_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value); +static int ar71xx_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val); +static int ar71xx_gpio_pin_toggle(device_t dev, uint32_t pin); + +static void +ar71xx_gpio_function_enable(struct ar71xx_gpio_softc *sc, uint32_t mask) +{ + GPIO_LOCK(sc); + GPIO_SET_BITS(sc, AR71XX_GPIO_FUNCTION, mask); + GPIO_UNLOCK(sc); +} + +static void +ar71xx_gpio_function_disable(struct ar71xx_gpio_softc *sc, uint32_t mask) +{ + GPIO_LOCK(sc); + GPIO_CLEAR_BITS(sc, AR71XX_GPIO_FUNCTION, mask); + GPIO_UNLOCK(sc); +} + +static void +ar71xx_gpio_pin_configure(struct ar71xx_gpio_softc *sc, struct gpio_pin *pin, + unsigned int flags) +{ + uint32_t mask; + + mask = 1 << pin->gp_pin; + GPIO_LOCK(sc); + + /* + * Manage input/output + */ + if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { + pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); + if (flags & GPIO_PIN_OUTPUT) { + pin->gp_flags |= GPIO_PIN_OUTPUT; + GPIO_SET_BITS(sc, AR71XX_GPIO_OE, mask); + } + else { + pin->gp_flags |= GPIO_PIN_INPUT; + GPIO_CLEAR_BITS(sc, AR71XX_GPIO_OE, mask); + } + } + + GPIO_UNLOCK(sc); +} + +static int +ar71xx_gpio_pin_max(device_t dev, int *maxpin) +{ + + *maxpin = AR71XX_GPIO_PINS - 1; + return (0); +} + +static int +ar71xx_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) +{ + struct ar71xx_gpio_softc *sc = device_get_softc(dev); + int i; + + for (i = 0; i < sc->gpio_npins; i++) { + if (sc->gpio_pins[i].gp_pin == pin) + break; + } + + if (i >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + *caps = sc->gpio_pins[i].gp_caps; + GPIO_UNLOCK(sc); + + return (0); +} + +static int +ar71xx_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) +{ + struct ar71xx_gpio_softc *sc = device_get_softc(dev); + int i; + + for (i = 0; i < sc->gpio_npins; i++) { + if (sc->gpio_pins[i].gp_pin == pin) + break; + } + + if (i >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + *flags = sc->gpio_pins[i].gp_flags; + GPIO_UNLOCK(sc); + + return (0); +} + +static int +ar71xx_gpio_pin_getname(device_t dev, uint32_t pin, char *name) +{ + struct ar71xx_gpio_softc *sc = device_get_softc(dev); + int i; + + for (i = 0; i < sc->gpio_npins; i++) { + if (sc->gpio_pins[i].gp_pin == pin) + break; + } + + if (i >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME); + GPIO_UNLOCK(sc); + + return (0); +} + +static int +ar71xx_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) +{ + int i; + struct ar71xx_gpio_softc *sc = device_get_softc(dev); + + for (i = 0; i < sc->gpio_npins; i++) { + if (sc->gpio_pins[i].gp_pin == pin) + break; + } + + if (i >= sc->gpio_npins) + return (EINVAL); + + /* Filter out unwanted flags */ + if ((flags &= sc->gpio_pins[i].gp_caps) != flags) + return (EINVAL); + + /* Can't mix input/output together */ + if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) == + (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) + return (EINVAL); + + ar71xx_gpio_pin_configure(sc, &sc->gpio_pins[i], flags); + return (0); +} + +static int +ar71xx_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) +{ + struct ar71xx_gpio_softc *sc = device_get_softc(dev); + int i; + + for (i = 0; i < sc->gpio_npins; i++) { + if (sc->gpio_pins[i].gp_pin == pin) + break; + } + + if (i >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + if (value) + GPIO_WRITE(sc, AR71XX_GPIO_SET, (1 << pin)); + else + GPIO_WRITE(sc, AR71XX_GPIO_CLEAR, (1 << pin)); + GPIO_UNLOCK(sc); + + return (0); +} + +static int +ar71xx_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) +{ + struct ar71xx_gpio_softc *sc = device_get_softc(dev); + int i; + + for (i = 0; i < sc->gpio_npins; i++) { + if (sc->gpio_pins[i].gp_pin == pin) + break; + } + + if (i >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + *val = (GPIO_READ(sc, AR71XX_GPIO_IN) & (1 << pin)) ? 1 : 0; + GPIO_UNLOCK(sc); + + return (0); +} + +static int +ar71xx_gpio_pin_toggle(device_t dev, uint32_t pin) +{ + int res, i; + struct ar71xx_gpio_softc *sc = device_get_softc(dev); + + for (i = 0; i < sc->gpio_npins; i++) { + if (sc->gpio_pins[i].gp_pin == pin) + break; + } + + if (i >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + res = (GPIO_READ(sc, AR71XX_GPIO_IN) & (1 << pin)) ? 1 : 0; + if (res) + GPIO_WRITE(sc, AR71XX_GPIO_CLEAR, (1 << pin)); + else + GPIO_WRITE(sc, AR71XX_GPIO_SET, (1 << pin)); + GPIO_UNLOCK(sc); + + return (0); +} + +static int +ar71xx_gpio_filter(void *arg) +{ + + /* TODO: something useful */ + return (FILTER_STRAY); +} + + + +static void +ar71xx_gpio_intr(void *arg) +{ + struct ar71xx_gpio_softc *sc = arg; + GPIO_LOCK(sc); + /* TODO: something useful */ + GPIO_UNLOCK(sc); +} + +static int +ar71xx_gpio_probe(device_t dev) +{ + + device_set_desc(dev, "Atheros AR71XX GPIO driver"); + return (0); +} + +static int +ar71xx_gpio_attach(device_t dev) +{ + struct ar71xx_gpio_softc *sc = device_get_softc(dev); + int error = 0; + struct ar71xx_gpio_pin *pinp; + int i; + + KASSERT((device_get_unit(dev) == 0), + ("ar71xx_gpio: Only one gpio module supported")); + + mtx_init(&sc->gpio_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF); + + /* Map control/status registers. */ + sc->gpio_mem_rid = 0; + sc->gpio_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->gpio_mem_rid, RF_ACTIVE); + + if (sc->gpio_mem_res == NULL) { + device_printf(dev, "couldn't map memory\n"); + error = ENXIO; + ar71xx_gpio_detach(dev); + return(error); + } + + if ((sc->gpio_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &sc->gpio_irq_rid, RF_SHAREABLE | RF_ACTIVE)) == NULL) { + device_printf(dev, "unable to allocate IRQ resource\n"); + return (ENXIO); + } + + if ((bus_setup_intr(dev, sc->gpio_irq_res, INTR_TYPE_MISC, + ar71xx_gpio_filter, ar71xx_gpio_intr, sc, &sc->gpio_ih))) { + device_printf(dev, + "WARNING: unable to register interrupt handler\n"); + return (ENXIO); + } + + sc->dev = dev; + ar71xx_gpio_function_enable(sc, GPIO_SPI_CS1_EN); + ar71xx_gpio_function_enable(sc, GPIO_SPI_CS2_EN); + /* Configure all pins as input */ + /* disable interrupts for all pins */ + GPIO_WRITE(sc, AR71XX_GPIO_INT_MASK, 0); + pinp = ar71xx_gpio_pins; + i = 0; + while (pinp->name) { + strncpy(sc->gpio_pins[i].gp_name, pinp->name, GPIOMAXNAME); + sc->gpio_pins[i].gp_pin = pinp->pin; + sc->gpio_pins[i].gp_caps = DEFAULT_CAPS; + sc->gpio_pins[i].gp_flags = 0; + ar71xx_gpio_pin_configure(sc, &sc->gpio_pins[i], pinp->flags); + pinp++; + i++; + } + + sc->gpio_npins = i; + + device_add_child(dev, "gpioc", device_get_unit(dev)); + device_add_child(dev, "gpiobus", device_get_unit(dev)); + return (bus_generic_attach(dev)); +} + +static int +ar71xx_gpio_detach(device_t dev) +{ + struct ar71xx_gpio_softc *sc = device_get_softc(dev); + + KASSERT(mtx_initialized(&sc->gpio_mtx), ("gpio mutex not initialized")); + + ar71xx_gpio_function_disable(sc, GPIO_SPI_CS1_EN); + ar71xx_gpio_function_disable(sc, GPIO_SPI_CS2_EN); + bus_generic_detach(dev); + + if (sc->gpio_mem_res) + bus_release_resource(dev, SYS_RES_MEMORY, sc->gpio_mem_rid, + sc->gpio_mem_res); + + mtx_destroy(&sc->gpio_mtx); + + return(0); +} + +static device_method_t ar71xx_gpio_methods[] = { + DEVMETHOD(device_probe, ar71xx_gpio_probe), + DEVMETHOD(device_attach, ar71xx_gpio_attach), + DEVMETHOD(device_detach, ar71xx_gpio_detach), + + /* GPIO protocol */ + DEVMETHOD(gpio_pin_max, ar71xx_gpio_pin_max), + DEVMETHOD(gpio_pin_getname, ar71xx_gpio_pin_getname), + DEVMETHOD(gpio_pin_getflags, ar71xx_gpio_pin_getflags), + DEVMETHOD(gpio_pin_getcaps, ar71xx_gpio_pin_getcaps), + DEVMETHOD(gpio_pin_setflags, ar71xx_gpio_pin_setflags), + DEVMETHOD(gpio_pin_get, ar71xx_gpio_pin_get), + DEVMETHOD(gpio_pin_set, ar71xx_gpio_pin_set), + DEVMETHOD(gpio_pin_toggle, ar71xx_gpio_pin_toggle), + {0, 0}, +}; + +static driver_t ar71xx_gpio_driver = { + "gpio", + ar71xx_gpio_methods, + sizeof(struct ar71xx_gpio_softc), +}; +static devclass_t ar71xx_gpio_devclass; + +DRIVER_MODULE(ar71xx_gpio, apb, ar71xx_gpio_driver, ar71xx_gpio_devclass, 0, 0); diff --git a/sys/mips/atheros/ar71xx_gpiovar.h b/sys/mips/atheros/ar71xx_gpiovar.h new file mode 100644 index 0000000..9cbea65 --- /dev/null +++ b/sys/mips/atheros/ar71xx_gpiovar.h @@ -0,0 +1,65 @@ +/*- + * Copyright (c) 2009, Oleksandr Tymoshenko + * Copyright (c) 2009, Luiz Otavio O Souza. + * 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. + */ + +#ifndef __AR71XX_GPIOVAR_H__ +#define __AR71XX_GPIOVAR_H__ + +#define GPIO_LOCK(_sc) mtx_lock(&(_sc)->gpio_mtx) +#define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->gpio_mtx) +#define GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->gpio_mtx, MA_OWNED) + +/* + * register space access macros + */ +#define GPIO_WRITE(sc, reg, val) do { \ + bus_write_4(sc->gpio_mem_res, (reg), (val)); \ + } while (0) + +#define GPIO_READ(sc, reg) bus_read_4(sc->gpio_mem_res, (reg)) + +#define GPIO_SET_BITS(sc, reg, bits) \ + GPIO_WRITE(sc, reg, GPIO_READ(sc, (reg)) | (bits)) + +#define GPIO_CLEAR_BITS(sc, reg, bits) \ + GPIO_WRITE(sc, reg, GPIO_READ(sc, (reg)) & ~(bits)) + +#define AR71XX_GPIO_PINS 12 + +struct ar71xx_gpio_softc { + device_t dev; + struct mtx gpio_mtx; + struct resource *gpio_mem_res; + int gpio_mem_rid; + struct resource *gpio_irq_res; + int gpio_irq_rid; + void *gpio_ih; + int gpio_npins; + struct gpio_pin gpio_pins[AR71XX_GPIO_PINS]; +}; + +#endif /* __AR71XX_GPIOVAR_H__ */ diff --git a/sys/mips/atheros/ar71xxreg.h b/sys/mips/atheros/ar71xxreg.h index 0983966..7f6800a 100644 --- a/sys/mips/atheros/ar71xxreg.h +++ b/sys/mips/atheros/ar71xxreg.h @@ -137,6 +137,21 @@ #define USB_CTRL_CONFIG_RESUME_UTMI_PLS_DIS (1 << 1) #define USB_CTRL_CONFIG_UTMI_BACKWARD_ENB (1 << 0) +#define AR71XX_GPIO_BASE 0x18040000 +#define AR71XX_GPIO_OE 0x00 +#define AR71XX_GPIO_IN 0x04 +#define AR71XX_GPIO_OUT 0x08 +#define AR71XX_GPIO_SET 0x0c +#define AR71XX_GPIO_CLEAR 0x10 +#define AR71XX_GPIO_INT 0x14 +#define AR71XX_GPIO_INT_TYPE 0x18 +#define AR71XX_GPIO_INT_POLARITY 0x1c +#define AR71XX_GPIO_INT_PENDING 0x20 +#define AR71XX_GPIO_INT_MASK 0x24 +#define AR71XX_GPIO_FUNCTION 0x28 +#define GPIO_SPI_CS2_EN (1 << 13) +#define GPIO_SPI_CS1_EN (1 << 12) + #define AR71XX_BASE_FREQ 40000000 #define AR71XX_PLL_CPU_CONFIG 0x18050000 #define PLL_SW_UPDATE (1 << 31) diff --git a/sys/mips/atheros/files.ar71xx b/sys/mips/atheros/files.ar71xx index fed21af..40bc706 100644 --- a/sys/mips/atheros/files.ar71xx +++ b/sys/mips/atheros/files.ar71xx @@ -1,6 +1,7 @@ # $FreeBSD$ mips/atheros/apb.c standard +mips/atheros/ar71xx_gpio.c optional gpio mips/atheros/ar71xx_machdep.c standard mips/atheros/ar71xx_ehci.c optional ehci mips/atheros/ar71xx_ohci.c optional ohci diff --git a/sys/mips/conf/AR71XX b/sys/mips/conf/AR71XX index 30126bf..982545f 100644 --- a/sys/mips/conf/AR71XX +++ b/sys/mips/conf/AR71XX @@ -72,6 +72,9 @@ device arge # options USB_DEBUG # device ehci +device gpio +device gpioled + device spibus device ar71xx_spi device mx25l diff --git a/sys/mips/conf/AR71XX.hints b/sys/mips/conf/AR71XX.hints index c68d6bc..6719d16 100644 --- a/sys/mips/conf/AR71XX.hints +++ b/sys/mips/conf/AR71XX.hints @@ -47,6 +47,18 @@ hint.arge.1.fduplex=1 # Uncomment this hint for RS (not PRO) # hint.arge.0.phymask=70000 +# GPIO +hint.gpio.0.at="apb0" +hint.gpio.0.maddr=0x18040000 +hint.gpio.0.msize=0x1000 +hint.gpio.0.irq=2 + +# RF led +hint.gpioled.0.at="gpiobus0" +hint.gpioled.0.name="rf" +# pin 2 +hint.gpioled.0.pins=0x0004 + # SPI flash hint.spi.0.at="nexus0" hint.spi.0.maddr=0x1f000000 diff --git a/sys/sys/gpio.h b/sys/sys/gpio.h new file mode 100644 index 0000000..3222aed --- /dev/null +++ b/sys/sys/gpio.h @@ -0,0 +1,94 @@ +/* $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. + */ +/* + * 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) + +#endif /* __GPIO_H__ */ diff --git a/usr.bin/Makefile b/usr.bin/Makefile index 00bbb3b..720d151 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -77,6 +77,7 @@ SUBDIR= alias \ getconf \ getent \ getopt \ + gpioctl \ ${_gprof} \ gzip \ head \ diff --git a/usr.bin/gpioctl/Makefile b/usr.bin/gpioctl/Makefile new file mode 100644 index 0000000..1daf641 --- /dev/null +++ b/usr.bin/gpioctl/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/9/93 +# $FreeBSD$ + +PROG= gpioctl + +.include diff --git a/usr.bin/gpioctl/gpioctl.1 b/usr.bin/gpioctl/gpioctl.1 new file mode 100644 index 0000000..116e471 --- /dev/null +++ b/usr.bin/gpioctl/gpioctl.1 @@ -0,0 +1,47 @@ +.\" 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 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 18, 2009 +.Dt GPIOCTL 1 +.Os +.Sh NAME +.Nm gpioctl +.Nd manage GPIO devices +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +The +.Nm +utility manages GPIO devices +.Sh HISTORY +None diff --git a/usr.bin/gpioctl/gpioctl.c b/usr.bin/gpioctl/gpioctl.c new file mode 100644 index 0000000..f67f519 --- /dev/null +++ b/usr.bin/gpioctl/gpioctl.c @@ -0,0 +1,320 @@ +/*- + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +struct flag_desc { + const char *name; + uint32_t flag; +}; + +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 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(int fd, int verbose) +{ + int i, maxpin; + struct gpio_pin pin; + struct gpio_req req; + + if (ioctl(fd, GPIOMAXPIN, &maxpin) < 0) { + perror("ioctl(GPIOMAXPIN)"); + exit(1); + } + + for (i = 0; i <= maxpin; i++) { + pin.gp_pin = i; + if (ioctl(fd, GPIOGETCONFIG, &pin) < 0) + /* For some reason this pin is inaccessible */ + continue; + + req.gp_pin = i; + if (ioctl(fd, GPIOGET, &req) < 0) { + /* Now, that's wrong */ + perror("ioctl(GPIOGET)"); + exit(1); + } + + printf("pin %02d:\t%d\t%s", pin.gp_pin, req.gp_value, + pin.gp_name); + + print_caps(pin.gp_flags); + + if (verbose) { + printf(", caps:"); + print_caps(pin.gp_caps); + } + printf("\n"); + } +} + +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; + struct gpio_pin pin; + struct gpio_req req; + char *ctlfile = NULL; + int pinn, pinv, fd, ch; + int flags, flag, ok; + int config, toggle, verbose, list; + + config = toggle = verbose = list = pinn = 0; + + while ((ch = getopt(argc, argv, "c:f:lt: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 '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; + for (i = 0; i < argc; i++) + printf("%d/%s\n", i, argv[i]); + + if (ctlfile == NULL) + fail("No gpioctl device provided\n"); + + fd = open(ctlfile, O_RDONLY); + if (fd < 0) { + perror("open"); + exit(1); + } + + if (list) { + dump_pins(fd, verbose); + close(fd); + exit(0); + } + + if (toggle) { + /* + * -t pin assumes no additional arguments + */ + if(argc > 0) { + usage(); + exit(1); + } + + req.gp_pin = pinn; + if (ioctl(fd, GPIOTOGGLE, &req) < 0) { + perror("ioctl(GPIOTOGGLE)"); + exit(1); + } + + close(fd); + 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.gp_pin = pinn; + pin.gp_flags = flags; + if (ioctl(fd, GPIOSETCONFIG, &pin) < 0) { + perror("ioctl(GPIOSETCONFIG)"); + 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) { + req.gp_pin = pinn; + if (ioctl(fd, GPIOGET, &req) < 0) { + perror("ioctl(GPIOGET)"); + exit(1); + } + printf("%d\n", req.gp_value); + 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 + */ + req.gp_pin = pinn; + req.gp_value = pinv; + if (ioctl(fd, GPIOSET, &req) < 0) { + perror("ioctl(GPIOSET)"); + exit(1); + } + + close(fd); + exit(0); +}