Skip site navigation (1)Skip section navigation (2)
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>