diff options
-rw-r--r-- | share/man/man4/gpioiic.4 | 68 | ||||
-rw-r--r-- | share/man/man4/gpioled.4 | 70 | ||||
-rw-r--r-- | sys/arm/broadcom/bcm2835/bcm2835_gpio.c | 11 | ||||
-rw-r--r-- | sys/arm/conf/BEAGLEBONE | 1 | ||||
-rw-r--r-- | sys/arm/ti/ti_gpio.c | 16 | ||||
-rw-r--r-- | sys/boot/fdt/dts/beaglebone-black.dts | 24 | ||||
-rw-r--r-- | sys/boot/fdt/dts/bindings-gpio.txt | 2 | ||||
-rw-r--r-- | sys/conf/files | 1 | ||||
-rw-r--r-- | sys/dev/gpio/gpiobus.c | 22 | ||||
-rw-r--r-- | sys/dev/gpio/gpiobusvar.h | 24 | ||||
-rw-r--r-- | sys/dev/gpio/gpioiic.c | 49 | ||||
-rw-r--r-- | sys/dev/gpio/gpioled.c | 88 | ||||
-rw-r--r-- | sys/dev/gpio/ofw_gpiobus.c | 337 | ||||
-rw-r--r-- | sys/dev/iicbus/iicbb.c | 25 | ||||
-rw-r--r-- | sys/dev/ofw/ofw_bus_if.m | 2 | ||||
-rw-r--r-- | sys/dev/ofw/ofw_iicbus.c | 1 | ||||
-rw-r--r-- | sys/modules/gpio/gpiobus/Makefile | 2 | ||||
-rw-r--r-- | sys/modules/gpio/gpioiic/Makefile | 1 | ||||
-rw-r--r-- | sys/modules/gpio/gpioled/Makefile | 2 | ||||
-rw-r--r-- | sys/modules/i2c/iicbb/Makefile | 2 |
20 files changed, 711 insertions, 37 deletions
diff --git a/share/man/man4/gpioiic.4 b/share/man/man4/gpioiic.4 index cbcaec9..e3f9814 100644 --- a/share/man/man4/gpioiic.4 +++ b/share/man/man4/gpioiic.4 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd November 5, 2013 +.Dd February 13, 2014 .Dt GPIOIIC 4 .Os .Sh NAME @@ -65,7 +65,7 @@ This is a bitmask of the pins on the that are to be used for SCLOCK and SDATA from the GPIO IIC bit-banging bus. To configure pin 0 and 7, use the bitmask of -10000001 and convert it to a hexadecimal value of 0x0081. +0b10000001 and convert it to a hexadecimal value of 0x0081. Please note that this mask should only ever have two bits set (any others bits - i.e., pins - will be ignored). .It Va hint.gpioiic.%d.scl @@ -73,13 +73,77 @@ Indicates which bit in the .Va hint.gpioiic.%d.pins should be used as the SCLOCK source. +Optional, defaults to 0. .It Va hint.gpioiic.%d.sda Indicates which bit in the .Va hint.gpioiic.%d.pins should be used as the SDATA source. +Optional, defaults to 1. +.El +.Pp +On a +.Xr FDT 4 +based system, like +.Li ARM , the dts part for a +.Nm gpioiic +device usually looks like: +.Bd -literal +gpio: gpio { + + gpio-controller; + ... + + gpioiic0 { + compatible = "gpioiic"; + /* + * Attach to GPIO pins 21 and 22. Set them + * initially as inputs. + */ + gpios = <&gpio 21 1 0 + &gpio 22 1 0>; + scl = <0>; /* GPIO pin 21 - optional */ + sda = <1>; /* GPIO pin 22 - optional */ + + /* This is an example of a gpioiic child. */ + gpioiic-child0 { + compatible = "lm75"; + i2c-address = <0x4f>; + }; + }; +}; +.Ed +.Pp +Where: +.Bl -tag -width ".Va compatible" +.It Va compatible +Should always be set to "gpioiic". +.It Va gpios +The +.Va gpios +property indicates which GPIO pins should be used for SCLOCK and SDATA +on the GPIO IIC bit-banging bus. +For more details about the +.Va gpios +property, please consult +.Pa /usr/src/sys/boot/fdt/dts/bindings-gpio.txt . +.It Va scl +The +.Va scl +option indicates which bit in the +.Va gpios +should be used as the SCLOCK source. +Optional, defaults to 0. +.It Va sda +The +.Va sda +option indicates which bit in the +.Va gpios +should be used as the SDATA source. +Optional, defaults to 1. .El .Sh SEE ALSO +.Xr fdt 4 , .Xr gpio 4 , .Xr gpioled 4 , .Xr iic 4 , diff --git a/share/man/man4/gpioled.4 b/share/man/man4/gpioled.4 index 76f59fa..74c28e6 100644 --- a/share/man/man4/gpioled.4 +++ b/share/man/man4/gpioled.4 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd November 5, 2013 +.Dd February 13, 2014 .Dt GPIOLED 4 .Os .Sh NAME @@ -68,7 +68,75 @@ Which pin on the GPIO interface to map to this instance. Please note that this mask should only ever have one bit set (any others bits - i.e., pins - will be ignored). .El +.Pp +On a +.Xr FDT 4 +based system, like +.Li ARM , the dts part for a +.Nm gpioled +device usually looks like: +.Bd -literal +gpio: gpio { + + gpio-controller; + ... + + led0 { + compatible = "gpioled"; + gpios = <&gpio 16 2 0>; /* GPIO pin 16. */ + name = "ok"; + }; + + led1 { + compatible = "gpioled"; + gpios = <&gpio 17 2 0>; /* GPIO pin 17. */ + name = "user-led1"; + }; +}; +.Ed +.Pp +And optionally, you can choose combine all the leds under a single +.Dq gpio-leds +compatible node: +.Bd -literal +simplebus0 { + + ... + + leds { + compatible = "gpio-leds"; + + led0 { + gpios = <&gpio 16 2 0>; + name = "ok" + }; + + led1 { + gpios = <&gpio 17 2 0>; + name = "user-led1" + }; + }; +}; +.Ed +.Pp +Both methods are equally supported and it is possible to have the leds +defined with any sort of mix between the methods. +The only restriction is that a GPIO pin cannot be mapped by two different +(gpio)leds. +.Pp +For more details about the +.Va gpios +property, please consult +.Pa /usr/src/sys/boot/fdt/dts/bindings-gpio.txt . +.Pp +The property +.Va name +is the arbitrary name of device in +.Pa /dev/led/ +to create for +.Xr led 4 . .Sh SEE ALSO +.Xr fdt 4 , .Xr gpio 4 , .Xr led 4 , .Xr gpioiic 4 diff --git a/sys/arm/broadcom/bcm2835/bcm2835_gpio.c b/sys/arm/broadcom/bcm2835/bcm2835_gpio.c index 9c5f5cd..7e8ead7 100644 --- a/sys/arm/broadcom/bcm2835/bcm2835_gpio.c +++ b/sys/arm/broadcom/bcm2835/bcm2835_gpio.c @@ -762,6 +762,14 @@ bcm_gpio_detach(device_t dev) return (EBUSY); } +static phandle_t +bcm_gpio_get_node(device_t bus, device_t dev) +{ + + /* We only have one child, the GPIO bus, which needs our own node. */ + return (ofw_bus_get_node(bus)); +} + static device_method_t bcm_gpio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bcm_gpio_probe), @@ -778,6 +786,9 @@ static device_method_t bcm_gpio_methods[] = { DEVMETHOD(gpio_pin_set, bcm_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, bcm_gpio_pin_toggle), + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_node, bcm_gpio_get_node), + DEVMETHOD_END }; diff --git a/sys/arm/conf/BEAGLEBONE b/sys/arm/conf/BEAGLEBONE index 3c6bebb..5fc02c1 100644 --- a/sys/arm/conf/BEAGLEBONE +++ b/sys/arm/conf/BEAGLEBONE @@ -101,6 +101,7 @@ device am335x_pmic # AM335x Power Management IC (TPC65217) # GPIO device gpio +device gpioled # USB support device usb diff --git a/sys/arm/ti/ti_gpio.c b/sys/arm/ti/ti_gpio.c index 4edb10e..fdbf2c4 100644 --- a/sys/arm/ti/ti_gpio.c +++ b/sys/arm/ti/ti_gpio.c @@ -543,9 +543,9 @@ ti_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value) /* Read the value on the pin */ if (val & mask) - *value = (ti_gpio_read_4(sc, bank, TI_GPIO_DATAOUT) & mask) ? 1 : 0; - else *value = (ti_gpio_read_4(sc, bank, TI_GPIO_DATAIN) & mask) ? 1 : 0; + else + *value = (ti_gpio_read_4(sc, bank, TI_GPIO_DATAOUT) & mask) ? 1 : 0; TI_GPIO_UNLOCK(sc); @@ -788,6 +788,14 @@ ti_gpio_detach(device_t dev) return(0); } +static phandle_t +ti_gpio_get_node(device_t bus, device_t dev) +{ + + /* We only have one child, the GPIO bus, which needs our own node. */ + return (ofw_bus_get_node(bus)); +} + static device_method_t ti_gpio_methods[] = { DEVMETHOD(device_probe, ti_gpio_probe), DEVMETHOD(device_attach, ti_gpio_attach), @@ -802,6 +810,10 @@ static device_method_t ti_gpio_methods[] = { DEVMETHOD(gpio_pin_get, ti_gpio_pin_get), DEVMETHOD(gpio_pin_set, ti_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, ti_gpio_pin_toggle), + + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_node, ti_gpio_get_node), + {0, 0}, }; diff --git a/sys/boot/fdt/dts/beaglebone-black.dts b/sys/boot/fdt/dts/beaglebone-black.dts index 2ab5066..5da633f 100644 --- a/sys/boot/fdt/dts/beaglebone-black.dts +++ b/sys/boot/fdt/dts/beaglebone-black.dts @@ -153,6 +153,30 @@ } }; + leds { + compatible = "gpio-leds"; + + led1 { + gpios = <&GPIO 53 2 0>; + name = "led1"; + }; + + led2 { + gpios = <&GPIO 54 2 0>; + name = "led2"; + }; + + led3 { + gpios = <&GPIO 55 2 0>; + name = "led3"; + }; + + led4 { + gpios = <&GPIO 56 2 0>; + name = "led4"; + }; + }; + chosen { stdin = "uart0"; stdout = "uart0"; diff --git a/sys/boot/fdt/dts/bindings-gpio.txt b/sys/boot/fdt/dts/bindings-gpio.txt index 492616e..b13078a 100644 --- a/sys/boot/fdt/dts/bindings-gpio.txt +++ b/sys/boot/fdt/dts/bindings-gpio.txt @@ -82,7 +82,7 @@ dir: flags: 0x0000---- IN_NONE - 0x0001---- IN_POL_LOW Polarity low (inverted input value. + 0x0001---- IN_POL_LOW Polarity low (active-low). 0x0002---- IN_IRQ_EDGE Interrupt, edge triggered. 0x0004---- IN_IRQ_LEVEL Interrupt, level triggered. diff --git a/sys/conf/files b/sys/conf/files index 5d78804..ed2dd01 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1439,6 +1439,7 @@ dev/gpio/gpioiic.c optional gpioiic dev/gpio/gpioled.c optional gpioled dev/gpio/gpio_if.m optional gpio dev/gpio/gpiobus_if.m optional gpio +dev/gpio/ofw_gpiobus.c optional fdt 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/gpiobus.c b/sys/dev/gpio/gpiobus.c index 6abb10c..d8eacc6 100644 --- a/sys/dev/gpio/gpiobus.c +++ b/sys/dev/gpio/gpiobus.c @@ -46,7 +46,6 @@ __FBSDID("$FreeBSD$"); #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); @@ -73,17 +72,7 @@ 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); -#define GPIOBUS_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) -#define GPIOBUS_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) -#define GPIOBUS_LOCK_INIT(_sc) \ - mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ - "gpiobus", MTX_DEF) -#define GPIOBUS_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); -#define GPIOBUS_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); -#define GPIOBUS_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); - - -static void +void gpiobus_print_pins(struct gpiobus_ivar *devi) { int range_start, range_stop, need_coma; @@ -175,7 +164,8 @@ static int gpiobus_probe(device_t dev) { device_set_desc(dev, "GPIO bus"); - return (0); + + return (BUS_PROBE_GENERIC); } static int @@ -190,13 +180,13 @@ gpiobus_attach(device_t dev) if (res) return (ENXIO); + KASSERT(sc->sc_npins != 0, ("GPIO device with no pins")); + /* * Increase to get number of pins */ sc->sc_npins++; - KASSERT(sc->sc_npins != 0, ("GPIO device with no pins")); - sc->sc_pins_mapped = malloc(sizeof(int) * sc->sc_npins, M_DEVBUF, M_NOWAIT | M_ZERO); @@ -209,7 +199,9 @@ gpiobus_attach(device_t dev) /* * Get parent's pins and mark them as unmapped */ + bus_generic_probe(dev); bus_enumerate_hinted_children(dev); + return (bus_generic_attach(dev)); } diff --git a/sys/dev/gpio/gpiobusvar.h b/sys/dev/gpio/gpiobusvar.h index 7a79aed..94ae1ad 100644 --- a/sys/dev/gpio/gpiobusvar.h +++ b/sys/dev/gpio/gpiobusvar.h @@ -30,12 +30,25 @@ #ifndef __GPIOBUS_H__ #define __GPIOBUS_H__ +#include "opt_platform.h" + #include <sys/param.h> #include <sys/lock.h> #include <sys/mutex.h> -#define GPIOBUS_IVAR(d) (struct gpiobus_ivar *) device_get_ivars(d) -#define GPIOBUS_SOFTC(d) (struct gpiobus_softc *) device_get_softc(d) +#ifdef FDT +#include <dev/ofw/ofw_bus_subr.h> +#endif + +#define GPIOBUS_IVAR(d) (struct gpiobus_ivar *) device_get_ivars(d) +#define GPIOBUS_SOFTC(d) (struct gpiobus_softc *) device_get_softc(d) +#define GPIOBUS_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define GPIOBUS_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define GPIOBUS_LOCK_INIT(_sc) mtx_init(&_sc->sc_mtx, \ + device_get_nameunit(_sc->sc_dev), "gpiobus", MTX_DEF) +#define GPIOBUS_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx) +#define GPIOBUS_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED) +#define GPIOBUS_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED) struct gpiobus_softc { @@ -54,4 +67,11 @@ struct gpiobus_ivar uint32_t *pins; /* pins map */ }; +void gpiobus_print_pins(struct gpiobus_ivar *); +#ifdef FDT +device_t ofw_gpiobus_add_fdt_child(device_t, phandle_t); +#endif + +extern driver_t gpiobus_driver; + #endif /* __GPIOBUS_H__ */ diff --git a/sys/dev/gpio/gpioiic.c b/sys/dev/gpio/gpioiic.c index 2a6d093..d7772b6 100644 --- a/sys/dev/gpio/gpioiic.c +++ b/sys/dev/gpio/gpioiic.c @@ -28,21 +28,24 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "opt_platform.h" + #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 <sys/gpio.h> #include "gpiobus_if.h" +#ifdef FDT +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/fdt/fdt_common.h> +#endif + #include <dev/iicbus/iiconf.h> #include <dev/iicbus/iicbus.h> @@ -55,7 +58,6 @@ struct gpioiic_softc { device_t sc_dev; device_t sc_busdev; - struct cdev *sc_leddev; int scl_pin; int sda_pin; }; @@ -77,7 +79,12 @@ static int gpioiic_probe(device_t dev) { +#ifdef FDT + if (!ofw_bus_is_compatible(dev, "gpioiic")) + return (ENXIO); +#endif device_set_desc(dev, "GPIO I2C bit-banging driver"); + return (0); } @@ -86,6 +93,10 @@ gpioiic_attach(device_t dev) { struct gpioiic_softc *sc = device_get_softc(dev); device_t bitbang; +#ifdef FDT + phandle_t node; + pcell_t pin; +#endif sc->sc_dev = dev; sc->sc_busdev = device_get_parent(dev); @@ -96,6 +107,15 @@ gpioiic_attach(device_t dev) device_get_unit(dev), "sda", &sc->sda_pin)) sc->sda_pin = SDA_PIN_DEFAULT; +#ifdef FDT + if ((node = ofw_bus_get_node(dev)) == -1) + return (ENXIO); + if (OF_getencprop(node, "scl", &pin, sizeof(pin)) > 0) + sc->scl_pin = (int)pin; + if (OF_getencprop(node, "sda", &pin, sizeof(pin)) > 0) + sc->sda_pin = (int)pin; +#endif + /* add generic bit-banging code */ bitbang = device_add_child(dev, "iicbb", -1); device_probe_and_attach(bitbang); @@ -139,7 +159,7 @@ gpioiic_callback(device_t dev, int index, caddr_t data) error = EINVAL; } - return(error); + return (error); } static void @@ -214,6 +234,16 @@ gpioiic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) return (IIC_ENOADDR); } +#ifdef FDT +static phandle_t +gpioiic_get_node(device_t bus, device_t dev) +{ + + /* We only have one child, the iicbb, which needs our own node. */ + return (ofw_bus_get_node(bus)); +} +#endif + static devclass_t gpioiic_devclass; static device_method_t gpioiic_methods[] = { @@ -230,6 +260,11 @@ static device_method_t gpioiic_methods[] = { DEVMETHOD(iicbb_getscl, gpioiic_getscl), DEVMETHOD(iicbb_reset, gpioiic_reset), +#ifdef FDT + /* OFW bus interface */ + DEVMETHOD(ofw_bus_get_node, gpioiic_get_node), +#endif + { 0, 0 } }; diff --git a/sys/dev/gpio/gpioled.c b/sys/dev/gpio/gpioled.c index a03c172..1377a14 100644 --- a/sys/dev/gpio/gpioled.c +++ b/sys/dev/gpio/gpioled.c @@ -27,6 +27,8 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "opt_platform.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/bio.h> @@ -39,6 +41,12 @@ __FBSDID("$FreeBSD$"); #include <sys/module.h> #include <sys/mutex.h> +#ifdef FDT +#include <dev/fdt/fdt_common.h> +#include <dev/gpio/gpiobusvar.h> +#include <dev/ofw/ofw_bus.h> +#endif + #include <dev/led/led.h> #include <sys/gpio.h> #include "gpiobus_if.h" @@ -75,6 +83,8 @@ gpioled_control(void *priv, int onoff) GPIOLED_LOCK(sc); GPIOBUS_LOCK_BUS(sc->sc_busdev); GPIOBUS_ACQUIRE_BUS(sc->sc_busdev, sc->sc_dev); + GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, GPIOLED_PIN, + GPIO_PIN_OUTPUT); GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, GPIOLED_PIN, onoff ? GPIO_PIN_HIGH : GPIO_PIN_LOW); GPIOBUS_RELEASE_BUS(sc->sc_busdev, sc->sc_dev); @@ -82,10 +92,65 @@ gpioled_control(void *priv, int onoff) GPIOLED_UNLOCK(sc); } +#ifdef FDT +static void +gpioled_identify(driver_t *driver, device_t bus) +{ + phandle_t child, leds, root; + + root = OF_finddevice("/"); + if (root == 0) + return; + leds = fdt_find_compatible(root, "gpio-leds", 1); + if (leds == 0) + return; + + /* Traverse the 'gpio-leds' node and add its children. */ + for (child = OF_child(leds); child != 0; child = OF_peer(child)) + if (ofw_gpiobus_add_fdt_child(bus, child) == NULL) + continue; +} +#endif + static int gpioled_probe(device_t dev) { +#ifdef FDT + int match; + phandle_t node; + char *compat; + + /* + * We can match against our own node compatible string and also against + * our parent node compatible string. The first is normally used to + * describe leds on a gpiobus and the later when there is a common node + * compatible with 'gpio-leds' which is used to concentrate all the + * leds nodes on the dts. + */ + match = 0; + if (ofw_bus_is_compatible(dev, "gpioled")) + match = 1; + + if (match == 0) { + if ((node = ofw_bus_get_node(dev)) == -1) + return (ENXIO); + if ((node = OF_parent(node)) == -1) + return (ENXIO); + if (OF_getprop_alloc(node, "compatible", 1, + (void **)&compat) == -1) + return (ENXIO); + + if (strcasecmp(compat, "gpio-leds") == 0) + match = 1; + + free(compat, M_OFWPROP); + } + + if (match == 0) + return (ENXIO); +#endif device_set_desc(dev, "GPIO led"); + return (0); } @@ -93,21 +158,35 @@ static int gpioled_attach(device_t dev) { struct gpioled_softc *sc; +#ifdef FDT + phandle_t node; + char *name; +#else const char *name; +#endif sc = device_get_softc(dev); sc->sc_dev = dev; sc->sc_busdev = device_get_parent(dev); GPIOLED_LOCK_INIT(sc); +#ifdef FDT + name = NULL; + if ((node = ofw_bus_get_node(dev)) == -1) + return (ENXIO); + if (OF_getprop_alloc(node, "label", 1, (void **)&name) == -1) + OF_getprop_alloc(node, "name", 1, (void **)&name); +#else if (resource_string_value(device_get_name(dev), device_get_unit(dev), "name", &name)) name = NULL; - - GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, GPIOLED_PIN, - GPIO_PIN_OUTPUT); +#endif sc->sc_leddev = led_create(gpioled_control, sc, name ? name : device_get_nameunit(dev)); +#ifdef FDT + if (name != NULL) + free(name, M_OFWPROP); +#endif return (0); } @@ -130,6 +209,9 @@ static devclass_t gpioled_devclass; static device_method_t gpioled_methods[] = { /* Device interface */ +#ifdef FDT + DEVMETHOD(device_identify, gpioled_identify), +#endif DEVMETHOD(device_probe, gpioled_probe), DEVMETHOD(device_attach, gpioled_attach), DEVMETHOD(device_detach, gpioled_detach), diff --git a/sys/dev/gpio/ofw_gpiobus.c b/sys/dev/gpio/ofw_gpiobus.c new file mode 100644 index 0000000..7641b56 --- /dev/null +++ b/sys/dev/gpio/ofw_gpiobus.c @@ -0,0 +1,337 @@ +/*- + * Copyright (c) 2009, Nathan Whitehorn <nwhitehorn@FreeBSD.org> + * Copyright (c) 2013, Luiz Otavio O Souza <loos@FreeBSD.org> + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/gpio.h> +#include <sys/kernel.h> +#include <sys/libkern.h> +#include <sys/lock.h> +#include <sys/module.h> +#include <sys/mutex.h> + +#include <dev/gpio/gpiobusvar.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/openfirm.h> + +#include <machine/resource.h> + +#include "gpio_if.h" +#include "gpiobus_if.h" + +struct ofw_gpiobus_devinfo { + struct gpiobus_ivar opd_dinfo; + struct ofw_bus_devinfo opd_obdinfo; +}; + +static int ofw_gpiobus_parse_gpios(struct gpiobus_softc *, + struct gpiobus_ivar *, phandle_t); +static struct ofw_gpiobus_devinfo *ofw_gpiobus_setup_devinfo(device_t, + phandle_t); +static void ofw_gpiobus_destroy_devinfo(struct ofw_gpiobus_devinfo *); + +device_t +ofw_gpiobus_add_fdt_child(device_t bus, phandle_t child) +{ + struct ofw_gpiobus_devinfo *dinfo; + device_t childdev; + + /* + * Set up the GPIO child and OFW bus layer devinfo and add it to bus. + */ + dinfo = ofw_gpiobus_setup_devinfo(bus, child); + if (dinfo == NULL) + return (NULL); + childdev = device_add_child(bus, NULL, -1); + if (childdev == NULL) { + device_printf(bus, "could not add child: %s\n", + dinfo->opd_obdinfo.obd_name); + ofw_gpiobus_destroy_devinfo(dinfo); + return (NULL); + } + device_set_ivars(childdev, dinfo); + + return (childdev); +} + +static int +ofw_gpiobus_parse_gpios(struct gpiobus_softc *sc, struct gpiobus_ivar *dinfo, + phandle_t child) +{ + int i, len; + pcell_t *gpios; + phandle_t gpio; + + /* Retrieve the gpios property. */ + if ((len = OF_getproplen(child, "gpios")) < 0) + return (EINVAL); + gpios = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO); + if (gpios == NULL) + return (ENOMEM); + if (OF_getencprop(child, "gpios", gpios, len) < 0) { + free(gpios, M_DEVBUF); + return (EINVAL); + } + + /* + * Each 'gpios' entry must contain 4 pcells. + * The first one is the GPIO controller phandler. + * Then the last three are the GPIO pin, the GPIO pin direction and + * the GPIO pin flags. + */ + if ((len / sizeof(pcell_t)) % 4) { + free(gpios, M_DEVBUF); + return (EINVAL); + } + dinfo->npins = len / (sizeof(pcell_t) * 4); + dinfo->pins = malloc(sizeof(uint32_t) * dinfo->npins, M_DEVBUF, + M_NOWAIT | M_ZERO); + if (dinfo->pins == NULL) { + free(gpios, M_DEVBUF); + return (ENOMEM); + } + + for (i = 0; i < dinfo->npins; i++) { + + /* Verify if we're attaching to the correct gpio controller. */ + gpio = OF_xref_phandle(gpios[i * 4 + 0]); + if (!OF_hasprop(gpio, "gpio-controller") || + gpio != ofw_bus_get_node(sc->sc_dev)) { + free(dinfo->pins, M_DEVBUF); + free(gpios, M_DEVBUF); + return (EINVAL); + } + + /* Get the GPIO pin number. */ + dinfo->pins[i] = gpios[i * 4 + 1]; + /* gpios[i * 4 + 2] - GPIO pin direction */ + /* gpios[i * 4 + 3] - GPIO pin flags */ + + if (dinfo->pins[i] > sc->sc_npins) { + device_printf(sc->sc_busdev, + "invalid pin %d, max: %d\n", + dinfo->pins[i], sc->sc_npins - 1); + free(dinfo->pins, M_DEVBUF); + free(gpios, M_DEVBUF); + return (EINVAL); + } + + /* + * Mark pin as mapped and give warning if it's already mapped. + */ + if (sc->sc_pins_mapped[dinfo->pins[i]]) { + device_printf(sc->sc_busdev, + "warning: pin %d is already mapped\n", + dinfo->pins[i]); + free(dinfo->pins, M_DEVBUF); + free(gpios, M_DEVBUF); + return (EINVAL); + } + sc->sc_pins_mapped[dinfo->pins[i]] = 1; + } + + free(gpios, M_DEVBUF); + + return (0); +} + +static struct ofw_gpiobus_devinfo * +ofw_gpiobus_setup_devinfo(device_t dev, phandle_t node) +{ + struct gpiobus_softc *sc; + struct ofw_gpiobus_devinfo *dinfo; + + sc = device_get_softc(dev); + dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_NOWAIT | M_ZERO); + if (dinfo == NULL) + return (NULL); + if (ofw_bus_gen_setup_devinfo(&dinfo->opd_obdinfo, node) != 0) { + free(dinfo, M_DEVBUF); + return (NULL); + } + + /* Parse the gpios property for the child. */ + if (ofw_gpiobus_parse_gpios(sc, &dinfo->opd_dinfo, node) != 0) { + ofw_bus_gen_destroy_devinfo(&dinfo->opd_obdinfo); + free(dinfo, M_DEVBUF); + return (NULL); + } + + return (dinfo); +} + +static void +ofw_gpiobus_destroy_devinfo(struct ofw_gpiobus_devinfo *dinfo) +{ + + ofw_bus_gen_destroy_devinfo(&dinfo->opd_obdinfo); + free(dinfo, M_DEVBUF); +} + +static int +ofw_gpiobus_probe(device_t dev) +{ + + if (ofw_bus_get_node(dev) == -1) + return (ENXIO); + device_set_desc(dev, "OFW GPIO bus"); + + return (0); +} + +static int +ofw_gpiobus_attach(device_t dev) +{ + struct gpiobus_softc *sc; + phandle_t child; + + sc = GPIOBUS_SOFTC(dev); + sc->sc_busdev = dev; + sc->sc_dev = device_get_parent(dev); + + /* Read the pin max. value */ + if (GPIO_PIN_MAX(sc->sc_dev, &sc->sc_npins) != 0) + return (ENXIO); + + KASSERT(sc->sc_npins != 0, ("GPIO device with no pins")); + + /* + * Increase to get number of pins. + */ + sc->sc_npins++; + + sc->sc_pins_mapped = malloc(sizeof(int) * sc->sc_npins, M_DEVBUF, + M_NOWAIT | M_ZERO); + + if (!sc->sc_pins_mapped) + return (ENOMEM); + + /* Init the bus lock. */ + GPIOBUS_LOCK_INIT(sc); + + bus_generic_probe(dev); + bus_enumerate_hinted_children(dev); + + /* + * Attach the children represented in the device tree. + */ + for (child = OF_child(ofw_bus_get_node(dev)); child != 0; + child = OF_peer(child)) + if (ofw_gpiobus_add_fdt_child(dev, child) == NULL) + continue; + + return (bus_generic_attach(dev)); +} + +static device_t +ofw_gpiobus_add_child(device_t dev, u_int order, const char *name, int unit) +{ + device_t child; + struct ofw_gpiobus_devinfo *devi; + + child = device_add_child_ordered(dev, order, name, unit); + if (child == NULL) + return (child); + devi = malloc(sizeof(struct ofw_gpiobus_devinfo), M_DEVBUF, + M_NOWAIT | M_ZERO); + if (devi == NULL) { + device_delete_child(dev, child); + return (0); + } + + /* + * NULL all the OFW-related parts of the ivars for non-OFW + * children. + */ + devi->opd_obdinfo.obd_node = -1; + devi->opd_obdinfo.obd_name = NULL; + devi->opd_obdinfo.obd_compat = NULL; + devi->opd_obdinfo.obd_type = NULL; + devi->opd_obdinfo.obd_model = NULL; + + device_set_ivars(child, devi); + + return (child); +} + +static int +ofw_gpiobus_print_child(device_t dev, device_t child) +{ + struct ofw_gpiobus_devinfo *devi; + int retval = 0; + + devi = device_get_ivars(child); + retval += bus_print_child_header(dev, child); + retval += printf(" at pin(s) "); + gpiobus_print_pins(&devi->opd_dinfo); + retval += bus_print_child_footer(dev, child); + + return (retval); +} + +static const struct ofw_bus_devinfo * +ofw_gpiobus_get_devinfo(device_t bus, device_t dev) +{ + struct ofw_gpiobus_devinfo *dinfo; + + dinfo = device_get_ivars(dev); + + return (&dinfo->opd_obdinfo); +} + +static device_method_t ofw_gpiobus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ofw_gpiobus_probe), + DEVMETHOD(device_attach, ofw_gpiobus_attach), + + /* Bus interface */ + DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str), + DEVMETHOD(bus_print_child, ofw_gpiobus_print_child), + DEVMETHOD(bus_add_child, ofw_gpiobus_add_child), + + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_devinfo, ofw_gpiobus_get_devinfo), + DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), + DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), + DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), + DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), + DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), + + DEVMETHOD_END +}; + +static devclass_t ofwgpiobus_devclass; + +DEFINE_CLASS_1(gpiobus, ofw_gpiobus_driver, ofw_gpiobus_methods, + sizeof(struct gpiobus_softc), gpiobus_driver); +DRIVER_MODULE(ofw_gpiobus, gpio, ofw_gpiobus_driver, ofwgpiobus_devclass, 0, 0); +MODULE_VERSION(ofw_gpiobus, 1); +MODULE_DEPEND(ofw_gpiobus, gpiobus, 1, 1, 1); diff --git a/sys/dev/iicbus/iicbb.c b/sys/dev/iicbus/iicbb.c index 44a069f..977d52a 100644 --- a/sys/dev/iicbus/iicbb.c +++ b/sys/dev/iicbus/iicbb.c @@ -43,6 +43,8 @@ __FBSDID("$FreeBSD$"); * */ +#include "opt_platform.h" + #include <sys/param.h> #include <sys/kernel.h> #include <sys/systm.h> @@ -50,6 +52,11 @@ __FBSDID("$FreeBSD$"); #include <sys/bus.h> #include <sys/uio.h> +#ifdef FDT +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/fdt/fdt_common.h> +#endif #include <dev/iicbus/iiconf.h> #include <dev/iicbus/iicbus.h> @@ -77,6 +84,9 @@ static int iicbb_write(device_t, const char *, int, int *, int); static int iicbb_read(device_t, char *, int, int *, int, int); static int iicbb_reset(device_t, u_char, u_char, u_char *); static int iicbb_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs); +#ifdef FDT +static phandle_t iicbb_get_node(device_t, device_t); +#endif static device_method_t iicbb_methods[] = { /* device interface */ @@ -98,6 +108,11 @@ static device_method_t iicbb_methods[] = { DEVMETHOD(iicbus_reset, iicbb_reset), DEVMETHOD(iicbus_transfer, iicbb_transfer), +#ifdef FDT + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_node, iicbb_get_node), +#endif + { 0, 0 } }; @@ -154,6 +169,16 @@ iicbb_detach(device_t dev) return (0); } +#ifdef FDT +static phandle_t +iicbb_get_node(device_t bus, device_t dev) +{ + + /* We only have one child, the I2C bus, which needs our own node. */ + return (ofw_bus_get_node(bus)); +} +#endif + static void iicbb_child_detached( device_t dev, device_t child ) { diff --git a/sys/dev/ofw/ofw_bus_if.m b/sys/dev/ofw/ofw_bus_if.m index e2466b4..0034a15 100644 --- a/sys/dev/ofw/ofw_bus_if.m +++ b/sys/dev/ofw/ofw_bus_if.m @@ -158,7 +158,7 @@ METHOD const char * get_name { } DEFAULT ofw_bus_default_get_name; # Get the firmware node for the device dev on the bus. The default method will -# return 0, which signals that there is no such node. +# return -1, which signals that there is no such node. METHOD phandle_t get_node { device_t bus; device_t dev; diff --git a/sys/dev/ofw/ofw_iicbus.c b/sys/dev/ofw/ofw_iicbus.c index 965721a..1574f6e 100644 --- a/sys/dev/ofw/ofw_iicbus.c +++ b/sys/dev/ofw/ofw_iicbus.c @@ -80,6 +80,7 @@ static devclass_t ofwiicbus_devclass; DEFINE_CLASS_1(iicbus, ofw_iicbus_driver, ofw_iicbus_methods, sizeof(struct iicbus_softc), iicbus_driver); +DRIVER_MODULE(ofw_iicbus, iicbb, ofw_iicbus_driver, ofwiicbus_devclass, 0, 0); DRIVER_MODULE(ofw_iicbus, iichb, ofw_iicbus_driver, ofwiicbus_devclass, 0, 0); MODULE_VERSION(ofw_iicbus, 1); MODULE_DEPEND(ofw_iicbus, iicbus, 1, 1, 1); diff --git a/sys/modules/gpio/gpiobus/Makefile b/sys/modules/gpio/gpiobus/Makefile index 1b7afe8..e868cba 100644 --- a/sys/modules/gpio/gpiobus/Makefile +++ b/sys/modules/gpio/gpiobus/Makefile @@ -33,7 +33,7 @@ KMOD= gpiobus SRCS= gpiobus.c -SRCS+= device_if.h bus_if.h gpio_if.h gpiobus_if.h +SRCS+= device_if.h bus_if.h gpio_if.h gpiobus_if.h opt_platform.h CFLAGS+= -I. -I${.CURDIR}/../../../dev/gpio/ diff --git a/sys/modules/gpio/gpioiic/Makefile b/sys/modules/gpio/gpioiic/Makefile index be59861..abd835e 100644 --- a/sys/modules/gpio/gpioiic/Makefile +++ b/sys/modules/gpio/gpioiic/Makefile @@ -34,6 +34,7 @@ KMOD= gpioiic SRCS= gpioiic.c SRCS+= device_if.h bus_if.h gpio_if.h gpiobus_if.h iicbus_if.h iicbb_if.h +SRCS+= opt_platform.h CFLAGS+= -I. -I${.CURDIR}/../../../dev/gpio/ diff --git a/sys/modules/gpio/gpioled/Makefile b/sys/modules/gpio/gpioled/Makefile index 0420b1b..cc5f860 100644 --- a/sys/modules/gpio/gpioled/Makefile +++ b/sys/modules/gpio/gpioled/Makefile @@ -33,7 +33,7 @@ KMOD= gpioled SRCS= gpioled.c -SRCS+= device_if.h bus_if.h gpio_if.h gpiobus_if.h +SRCS+= device_if.h bus_if.h gpio_if.h gpiobus_if.h opt_platform.h CFLAGS+= -I. -I${.CURDIR}/../../../dev/gpio/ diff --git a/sys/modules/i2c/iicbb/Makefile b/sys/modules/i2c/iicbb/Makefile index ea991e0..75d1c83 100644 --- a/sys/modules/i2c/iicbb/Makefile +++ b/sys/modules/i2c/iicbb/Makefile @@ -3,6 +3,6 @@ .PATH: ${.CURDIR}/../../../dev/iicbus KMOD = iicbb SRCS = device_if.h bus_if.h iicbus_if.h \ - iicbb_if.h iicbb_if.c iicbb.c + iicbb_if.h iicbb_if.c iicbb.c opt_platform.h .include <bsd.kmod.mk> |