Date: Tue, 15 Jun 2010 14:22:33 +0300 From: Alexander Mogilny <sg@sg.org.ua> To: Luiz Otavio O Souza <lists.br@gmail.com> Cc: freebsd-mips@freebsd.org Subject: Re: RouterBOARD RB450G Message-ID: <0EFED95F-1B59-4A34-B518-E49A6ACD7D64@sg.org.ua> In-Reply-To: <EDEDDC71-8C3E-4EDC-949B-AD4EBE6D14DD@gmail.com> References: <9C6B899F-0361-4E20-A9C4-20C002A3CA1D@sg.org.ua> <2D2B1168-4E11-4353-8856-FD2728413F1B@bluezbox.com> <EDEDDC71-8C3E-4EDC-949B-AD4EBE6D14DD@gmail.com>
index | next in thread | previous in thread | raw e-mail
[-- Attachment #1 --] On Jun 12, 2010, at 2:36 PM, Luiz Otavio O Souza wrote: > Gonzo, > > I 've most of the trivial fixes for routerboards, just give me a few days to extract the current patches. > > My old stuff can be found on: http://loos.no-ip.org:280/routerboard/ > > The missing bits are: nand (the driver is ported, but i need more -free- time to sort out the FS layer), sd/mmc card slot (connected to spi bus) and the ethernet switch (useful on RG450(g) which have 4 ports connected to switch). > Hi again! I confirm that this "old stuff" :) works on RB450G. Web document should be corrected to use port 280 for downloading patches from http://loos.no-ip.org. I also made some corrections to gpio.diff as world failed to build because of warnings in gpioctl.c . You may see corrected diff attached. I will now make some tests on the board and give you feedback if everything is fine. Here is my dmesg.boot: RouterBOOT booter 2.23 RouterBoard 450G CPU frequency: 680 MHz Memory size: 256 MB Press any key within 2 seconds to enter setup.. Please, check ethernet cable... trying bootp protocol... OK Got IP address: 172.16.0.40 resolved mac address 00:E0:81:49:87:F7 Gateway: 172.16.0.1 transfer started .................................. transfer ok, time=2.75s setting up elf image... OK jumping to kernel code platform frequency: 680000000 arguments: a0 = 00000008 a1 = a0861c00 a2 = 00000000 a3 = 00000000 Cmd line: console=ttyS0,115200 gpio=1983 HZ=340000000 mem=256M kmac=00:0C:42:59:30:FF board=450G boot=1 mlc=2 Environment: envp is invalid Cache info: picache_stride = 4096 picache_loopcount = 16 pdcache_stride = 4096 pdcache_loopcount = 8 cpu0: MIPS Technologies processor v116.147 MMU: Standard TLB, 16 entries L1 i-cache: 4 ways of 512 sets, 32 bytes per line L1 d-cache: 4 ways of 256 sets, 32 bytes per line Config1=0x9ee3519e<PerfCount,WatchRegs,MIPS16,EJTAG> Config3=0x20 Copyright (c) 1992-2010 The FreeBSD Project. Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994 The Regents of the University of California. All rights reserved. FreeBSD is a registered trademark of The FreeBSD Foundation. FreeBSD 9.0-CURRENT #3: Tue Jun 15 14:00:59 EEST 2010 root@tbilisi.intra:/usr/obj/mips/mips/usr/src.mips/sys/RB4XX mips real memory = 268435456 (262144K bytes) avail memory = 257089536 (245MB) nexus0: <MIPS32 root nexus> clock0: <Generic MIPS32 ticker> on nexus0 clock0: [FILTER] apb0 at irq 4 on nexus0 apb0: [FILTER] uart0: <16550 or compatible> on apb0 uart0: [FILTER] uart0: console (115200,n,8,1) gpio0: <Atheros AR71XX GPIO driver> on apb0 gpio0: [GIANT-LOCKED] gpio0: [FILTER+ITHREAD] gpioc0: <GPIO controller> on gpio0 gpiobus0: <GPIO bus> on gpio0 gpioled0: <GPIO led> at pin(s) 4 on gpiobus0 ehci0: <AR71XX Integrated USB 2.0 controller> at mem 0x1b000000-0x1bffffff irq 1 on nexus0 ehci0: [ITHREAD] usbus0: set host controller mode usbus0: EHCI version 1.0 usbus0: set host controller mode usbus0: <AR71XX Integrated USB 2.0 controller> on ehci0 arge0: <Atheros AR71xx built-in ethernet interface> at mem 0x19000000-0x19000fff irq 2 on nexus0 arge0: Ethernet address: 00:0c:42:59:30:00 arge0: [FILTER+ITHREAD] arge1: <Atheros AR71xx built-in ethernet interface> at mem 0x1a000000-0x1a000fff irq 3 on nexus0 miibus0: <MII bus> on arge1 ukphy0: <Generic IEEE 802.3u media interface> PHY 4 on miibus0 ukphy0: 10baseT, 10baseT-FDX, 100baseTX, 100baseTX-FDX, 1000baseT-FDX, auto arge1: Ethernet address: 00:0c:42:59:30:ff arge1: [FILTER+ITHREAD] ar71xx_wdog0: <Atheros AR71XX watchdog timer> on nexus0 spi0: <AR71XX SPI> at mem 0x1f000000-0x1f00000f on nexus0 spibus0: <spibus bus> on spi0 Timecounter "MIPS32" frequency 340000000 Hz quality 800 Timecounters tick every 1.000 msec bootpc_init: wired to interface 'arge1' Sending DHCP Discover packet from interface arge1 (00:0c:42:59:30:ff) arge1: link state changed to DOWN usbus0: 480Mbps High Speed USB v2.0 ugen0.1: <Atheros> at usbus0 uhub0: <Atheros EHCI root HUB, class 9/0, rev 2.00/1.00, addr 1> on usbus0 uhub0: 2 ports with 2 removable, self powered DHCP/BOOTP timeout for server 255.255.255.255 arge1: link state changed to UP DHCP/BOOTP timeout for server 255.255.255.255 Received DHCP Offer packet on arge1 from 172.16.0.100 (accepted) (no root path) Received DHCP Offer packet on arge1 from 172.16.0.100 (ignored) (no root path) Received DHCP Offer packet on arge1 from 172.16.0.100 (ignored) (no root path) Sending DHCP Request packet from interface arge1 (00:0c:42:59:30:ff) DHCP/BOOTP timeout for server 255.255.255.255 Received DHCP Ack packet on arge1 from 172.16.0.100 (accepted) (got root path) Received DHCP Ack packet on arge1 from 172.16.0.100 via 172.16.0.1 (accepted) (got root path) arge1 at 172.16.0.40 server 172.16.0.100 via gateway 172.16.0.1 boot file boot/kernel/kernel subnet mask 255.255.255.0 router 172.16.0.1 rootfs 172.16.0.100:/home/nfs hostname mk-40 Adjusted interface arge1 Trying to mount root from nfs: NFS ROOT: 172.16.0.100:/home/nfs <...> Now, tell me how can I help you with writing some code for ethernet switch etc? As I need to get it working :). [-- Attachment #2 --] diff --git a/sys/conf/files b/sys/conf/files index b5a6f8c..7afc80b 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -935,6 +935,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 <sys/bus.h> +#include <sys/gpio.h> + +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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/kernel.h> +#include <sys/queue.h> +#include <sys/sysctl.h> +#include <sys/types.h> + +#include <sys/bus.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> + +#include <sys/gpio.h> +#include <dev/gpio/gpiobusvar.h> +#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 <sys/bus.h> +#include <sys/gpio.h> + +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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/types.h> + +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/ioccom.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/queue.h> +#include <machine/bus.h> +#include <machine/resource.h> + +#include <sys/gpio.h> +#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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bio.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/kthread.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/mutex.h> + +#include <dev/led/led.h> +#include <sys/gpio.h> +#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 <gonzo@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> + +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/rman.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/gpio.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <mips/atheros/ar71xxreg.h> +#include <mips/atheros/ar71xx_gpiovar.h> + +#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 <gonzo@FreeBSD.org> + * 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 5677a4e..5d36a67 100644 --- a/sys/mips/atheros/ar71xxreg.h +++ b/sys/mips/atheros/ar71xxreg.h @@ -134,6 +134,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 29add2e..ee2ad87 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 cfdd82d..4cb3f23 100644 --- a/sys/mips/conf/AR71XX +++ b/sys/mips/conf/AR71XX @@ -69,6 +69,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 0593dc0..2b7bac0 100644 --- a/sys/mips/conf/AR71XX.hints +++ b/sys/mips/conf/AR71XX.hints @@ -48,5 +48,17 @@ hint.spi.0.msize=0x10 hint.mx25l.0.at="spibus0" hint.mx25l.0.cs=0 +# 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 + # Watchdog hint.ar71xx_wdog.0.at="nexus0" 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 <gonzo@FreeBSD.org> + * 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 <marc@msys.ch> + * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org> + * + * 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 <sys/ioccom.h> + +/* 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 6497743..37581b8 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -75,6 +75,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 <bsd.prog.mk> 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..abbe95d --- /dev/null +++ b/usr.bin/gpioctl/gpioctl.c @@ -0,0 +1,327 @@ +/*- + * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> + * 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 <fcntl.h> +#include <getopt.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/gpio.h> + +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 }, +}; + +static void usage(void); +static const char* cap2str(uint32_t); +int str2cap(const char*); +static int str2int(const char*, int*); +static void print_caps(int); +static void dump_pins(int, int); +static void fail(const char*, ...); + +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; + + pinn = config = toggle = verbose = list = 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); + } + + /* + * Set pin value + */ + + /* Is it valid number and 0 or 1? */ + pinv = str2int(argv[0], &ok); + if (!ok || ((pinv != 0) && (pinv != 1))) + fail("Invalid pin value: %s\n", argv[0]); + + req.gp_pin = pinn; + req.gp_pin = pinv; + if (ioctl(fd, GPIOSET, &req) < 0) { + perror("ioctl(GPIOSET)"); + exit(1); + } + + close(fd); + exit(0); +} + +static void +usage() +{ + 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)) + 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); +} [-- Attachment #3 --] [-- Attachment #4 --] /*- * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> * 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 <fcntl.h> #include <getopt.h> #include <stdio.h> #include <stdarg.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/gpio.h> 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 }, }; static void usage(void); static const char* cap2str(uint32_t); int str2cap(const char*); static int str2int(const char*, int*); static void print_caps(int); static void dump_pins(int, int); static void fail(const char*, ...); 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; pinn = config = toggle = verbose = list = 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); } /* * Set pin value */ /* Is it valid number and 0 or 1? */ pinv = str2int(argv[0], &ok); if (!ok || ((pinv != 0) && (pinv != 1))) fail("Invalid pin value: %s\n", argv[0]); req.gp_pin = pinn; req.gp_pin = pinv; if (ioctl(fd, GPIOSET, &req) < 0) { perror("ioctl(GPIOSET)"); exit(1); } close(fd); exit(0); } 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)) 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); } [-- Attachment #5 --] [-- Attachment #6 --] # # $FreeBSD$ # ident RB4XX cpu CPU_MIPS4KC options ISA_MIPS32 makeoptions TARGET_BIG_ENDIAN makeoptions KERNLOADADDR=0x80050000 options HZ=1000 files "../atheros/files.ar71xx" hints "RB450.hints" #makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols makeoptions MODULES_OVERRIDE="" #options DDB #options KDB options SCHED_4BSD #4BSD scheduler options INET #InterNETworking options NFSCLIENT #Network Filesystem Client options NFS_ROOT #NFS usable as /, requires NFSCLIENT options PSEUDOFS #Pseudo-filesystem framework options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions # options NFS_LEGACYRPC # Debugging for use in -current # options INVARIANTS # options INVARIANT_SUPPORT # options WITNESS # options WITNESS_SKIPSPIN options FFS #Berkeley Fast Filesystem options SOFTUPDATES #Enable FFS soft updates support options UFS_ACL #Support for access control lists options UFS_DIRHASH #Improve performance on big directories options BOOTP options BOOTP_NFSROOT options BOOTP_NFSV3 options BOOTP_WIRED_TO=arge1 options BOOTP_COMPAT options ROOTDEVNAME=\"nfs:192.168.10.1:/mnt/bsd\" device pci #device ath # Atheros pci/cardbus NIC's #options ATH_DEBUG #device ath_hal #option AH_SUPPORT_AR5416 #device ath_rate_sample device mii device arge device usb options USB_EHCI_BIG_ENDIAN_DESC # handle big-endian byte order options USB_DEBUG device ehci device gpio device gpioled device spibus device ar71xx_spi #device nand # Generic NAND disk support #device rb_nand # RouterBoard NAND Support #device geom_nand # NAND slices support #options YAFFS device ar71xx_wdog device uart device loop device ether device md device bpf device random device if_bridge device pf device pfsync device pflog options RB_GPIO_PINS options FIX_RB_MAC_ADDRESS [-- Attachment #7 --] [-- Attachment #8 --] # $FreeBSD$ hint.apb.0.at="nexus0" hint.apb.0.irq=4 # uart0 hint.uart.0.at="apb0" # see atheros/uart_cpu_ar71xx.c why +3 hint.uart.0.maddr=0x18020003 hint.uart.0.msize=0x18 hint.uart.0.irq=3 #ohci #hint.ohci.0.at="apb0" #hint.ohci.0.maddr=0x1c000000 #hint.ohci.0.msize=0x01000000 #hint.ohci.0.irq=6 # ehci hint.ehci.0.at="nexus0" hint.ehci.0.maddr=0x1b000000 hint.ehci.0.msize=0x01000000 hint.ehci.0.irq=1 # pci #hint.pcib.0.at="nexus0" #hint.pcib.0.irq=0 hint.arge.0.at="nexus0" hint.arge.0.maddr=0x19000000 hint.arge.0.msize=0x1000 hint.arge.0.irq=2 # PHY0, PHY1, PHY2, PHY3 hint.arge.0.phymask=0x0f hint.arge.1.at="nexus0" hint.arge.1.maddr=0x1A000000 hint.arge.1.msize=0x1000 hint.arge.1.irq=3 # PHY4 hint.arge.1.phymask=0x10 # Watchdog hint.ar71xx_wdog.0.at="nexus0" # GPIO hint.gpio.0.at="apb0" hint.gpio.0.maddr=0x18040000 hint.gpio.0.msize=0x1000 hint.gpio.0.irq=2 # User led hint.gpioled.0.at="gpiobus0" hint.gpioled.0.name="user" # pin 4 hint.gpioled.0.pins=0x0010 # SPI flash hint.spi.0.at="nexus0" hint.spi.0.maddr=0x1f000000 hint.spi.0.msize=0x10 # RB Flash #hint.rb_nand.0.at="spibus0" #hint.rb_nand.0.cs=0 # NAND slices for RouterBoard hint.nand_slice.0.size="0x40000" hint.nand_slice.0.offset="0" hint.nand_slice.0.name="bootloader" hint.nand_slice.1.size="0x3c0000" hint.nand_slice.1.offset="0x40000" hint.nand_slice.1.name="kernelfs" hint.nand_slice.2.size="0" hint.nand_slice.2.offset="0x400000" hint.nand_slice.2.name="rootfs"help
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?0EFED95F-1B59-4A34-B518-E49A6ACD7D64>
