1213237Sgonzo/*- 2213237Sgonzo * Copyright (c) 2009 Oleksandr Tymoshenko <gonzo@freebsd.org> 3213237Sgonzo * All rights reserved. 4213237Sgonzo * 5213237Sgonzo * Redistribution and use in source and binary forms, with or without 6213237Sgonzo * modification, are permitted provided that the following conditions 7213237Sgonzo * are met: 8213237Sgonzo * 1. Redistributions of source code must retain the above copyright 9213237Sgonzo * notice, this list of conditions and the following disclaimer. 10213237Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 11213237Sgonzo * notice, this list of conditions and the following disclaimer in the 12213237Sgonzo * documentation and/or other materials provided with the distribution. 13213237Sgonzo * 14213277Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15213277Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16213277Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17213277Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18213277Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19213277Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20213277Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21213277Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22213277Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23213277Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24213277Sgonzo * SUCH DAMAGE. 25213237Sgonzo */ 26213237Sgonzo 27213237Sgonzo#include <sys/cdefs.h> 28213237Sgonzo__FBSDID("$FreeBSD$"); 29213237Sgonzo 30266105Sloos#include "opt_platform.h" 31266105Sloos 32213237Sgonzo#include <sys/param.h> 33213237Sgonzo#include <sys/systm.h> 34213237Sgonzo#include <sys/bus.h> 35278783Sloos#include <sys/gpio.h> 36213237Sgonzo#include <sys/kernel.h> 37213237Sgonzo#include <sys/lock.h> 38213237Sgonzo#include <sys/malloc.h> 39213237Sgonzo#include <sys/module.h> 40213237Sgonzo#include <sys/mutex.h> 41213237Sgonzo 42266105Sloos#ifdef FDT 43266105Sloos#include <dev/fdt/fdt_common.h> 44266105Sloos#include <dev/ofw/ofw_bus.h> 45266105Sloos#endif 46266105Sloos 47278783Sloos#include <dev/gpio/gpiobusvar.h> 48213237Sgonzo#include <dev/led/led.h> 49278783Sloos 50213237Sgonzo#include "gpiobus_if.h" 51213237Sgonzo 52213237Sgonzo/* 53213237Sgonzo * Only one pin for led 54213237Sgonzo */ 55213237Sgonzo#define GPIOLED_PIN 0 56213237Sgonzo 57213237Sgonzo#define GPIOLED_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 58213237Sgonzo#define GPIOLED_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 59213237Sgonzo#define GPIOLED_LOCK_INIT(_sc) \ 60213237Sgonzo mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ 61213237Sgonzo "gpioled", MTX_DEF) 62213237Sgonzo#define GPIOLED_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); 63213237Sgonzo 64213237Sgonzostruct gpioled_softc 65213237Sgonzo{ 66213237Sgonzo device_t sc_dev; 67213237Sgonzo device_t sc_busdev; 68213237Sgonzo struct mtx sc_mtx; 69213237Sgonzo struct cdev *sc_leddev; 70213237Sgonzo}; 71213237Sgonzo 72213237Sgonzostatic void gpioled_control(void *, int); 73213237Sgonzostatic int gpioled_probe(device_t); 74213237Sgonzostatic int gpioled_attach(device_t); 75213237Sgonzostatic int gpioled_detach(device_t); 76213237Sgonzo 77213237Sgonzostatic void 78213237Sgonzogpioled_control(void *priv, int onoff) 79213237Sgonzo{ 80278783Sloos int error; 81278783Sloos struct gpioled_softc *sc; 82278783Sloos 83278783Sloos sc = (struct gpioled_softc *)priv; 84213237Sgonzo GPIOLED_LOCK(sc); 85278783Sloos error = GPIOBUS_ACQUIRE_BUS(sc->sc_busdev, sc->sc_dev, 86278783Sloos GPIOBUS_DONTWAIT); 87278783Sloos if (error != 0) { 88278783Sloos GPIOLED_UNLOCK(sc); 89278783Sloos return; 90278783Sloos } 91278783Sloos error = GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, 92278783Sloos GPIOLED_PIN, GPIO_PIN_OUTPUT); 93278783Sloos if (error == 0) 94278783Sloos GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, GPIOLED_PIN, 95278783Sloos onoff ? GPIO_PIN_HIGH : GPIO_PIN_LOW); 96213237Sgonzo GPIOBUS_RELEASE_BUS(sc->sc_busdev, sc->sc_dev); 97213237Sgonzo GPIOLED_UNLOCK(sc); 98213237Sgonzo} 99213237Sgonzo 100266105Sloos#ifdef FDT 101266105Sloosstatic void 102266105Sloosgpioled_identify(driver_t *driver, device_t bus) 103266105Sloos{ 104266105Sloos phandle_t child, leds, root; 105266105Sloos 106266105Sloos root = OF_finddevice("/"); 107266105Sloos if (root == 0) 108266105Sloos return; 109266105Sloos leds = fdt_find_compatible(root, "gpio-leds", 1); 110266105Sloos if (leds == 0) 111266105Sloos return; 112266105Sloos 113266105Sloos /* Traverse the 'gpio-leds' node and add its children. */ 114266105Sloos for (child = OF_child(leds); child != 0; child = OF_peer(child)) 115266105Sloos if (ofw_gpiobus_add_fdt_child(bus, child) == NULL) 116266105Sloos continue; 117266105Sloos} 118266105Sloos#endif 119266105Sloos 120213237Sgonzostatic int 121213237Sgonzogpioled_probe(device_t dev) 122213237Sgonzo{ 123266105Sloos#ifdef FDT 124266105Sloos int match; 125266105Sloos phandle_t node; 126266105Sloos char *compat; 127266105Sloos 128266105Sloos /* 129266105Sloos * We can match against our own node compatible string and also against 130266105Sloos * our parent node compatible string. The first is normally used to 131266105Sloos * describe leds on a gpiobus and the later when there is a common node 132266105Sloos * compatible with 'gpio-leds' which is used to concentrate all the 133266105Sloos * leds nodes on the dts. 134266105Sloos */ 135266105Sloos match = 0; 136266105Sloos if (ofw_bus_is_compatible(dev, "gpioled")) 137266105Sloos match = 1; 138266105Sloos 139266105Sloos if (match == 0) { 140266105Sloos if ((node = ofw_bus_get_node(dev)) == -1) 141266105Sloos return (ENXIO); 142266105Sloos if ((node = OF_parent(node)) == -1) 143266105Sloos return (ENXIO); 144266105Sloos if (OF_getprop_alloc(node, "compatible", 1, 145266105Sloos (void **)&compat) == -1) 146266105Sloos return (ENXIO); 147266105Sloos 148266105Sloos if (strcasecmp(compat, "gpio-leds") == 0) 149266105Sloos match = 1; 150266105Sloos 151266105Sloos free(compat, M_OFWPROP); 152266105Sloos } 153266105Sloos 154266105Sloos if (match == 0) 155266105Sloos return (ENXIO); 156266105Sloos#endif 157213237Sgonzo device_set_desc(dev, "GPIO led"); 158266105Sloos 159213237Sgonzo return (0); 160213237Sgonzo} 161213237Sgonzo 162213237Sgonzostatic int 163213237Sgonzogpioled_attach(device_t dev) 164213237Sgonzo{ 165213237Sgonzo struct gpioled_softc *sc; 166266105Sloos#ifdef FDT 167266105Sloos phandle_t node; 168266105Sloos char *name; 169266105Sloos#else 170213237Sgonzo const char *name; 171266105Sloos#endif 172213237Sgonzo 173213237Sgonzo sc = device_get_softc(dev); 174213237Sgonzo sc->sc_dev = dev; 175213237Sgonzo sc->sc_busdev = device_get_parent(dev); 176213237Sgonzo GPIOLED_LOCK_INIT(sc); 177266105Sloos#ifdef FDT 178266105Sloos name = NULL; 179266105Sloos if ((node = ofw_bus_get_node(dev)) == -1) 180266105Sloos return (ENXIO); 181266105Sloos if (OF_getprop_alloc(node, "label", 1, (void **)&name) == -1) 182266105Sloos OF_getprop_alloc(node, "name", 1, (void **)&name); 183266105Sloos#else 184213237Sgonzo if (resource_string_value(device_get_name(dev), 185213237Sgonzo device_get_unit(dev), "name", &name)) 186213237Sgonzo name = NULL; 187266105Sloos#endif 188213237Sgonzo 189213237Sgonzo sc->sc_leddev = led_create(gpioled_control, sc, name ? name : 190213237Sgonzo device_get_nameunit(dev)); 191266105Sloos#ifdef FDT 192266105Sloos if (name != NULL) 193266105Sloos free(name, M_OFWPROP); 194266105Sloos#endif 195213237Sgonzo 196213237Sgonzo return (0); 197213237Sgonzo} 198213237Sgonzo 199213237Sgonzostatic int 200213237Sgonzogpioled_detach(device_t dev) 201213237Sgonzo{ 202213237Sgonzo struct gpioled_softc *sc; 203213237Sgonzo 204213237Sgonzo sc = device_get_softc(dev); 205213237Sgonzo if (sc->sc_leddev) { 206213237Sgonzo led_destroy(sc->sc_leddev); 207213237Sgonzo sc->sc_leddev = NULL; 208213237Sgonzo } 209213237Sgonzo GPIOLED_LOCK_DESTROY(sc); 210213237Sgonzo return (0); 211213237Sgonzo} 212213237Sgonzo 213213237Sgonzostatic devclass_t gpioled_devclass; 214213237Sgonzo 215213237Sgonzostatic device_method_t gpioled_methods[] = { 216213237Sgonzo /* Device interface */ 217266105Sloos#ifdef FDT 218266105Sloos DEVMETHOD(device_identify, gpioled_identify), 219266105Sloos#endif 220213237Sgonzo DEVMETHOD(device_probe, gpioled_probe), 221213237Sgonzo DEVMETHOD(device_attach, gpioled_attach), 222213237Sgonzo DEVMETHOD(device_detach, gpioled_detach), 223213237Sgonzo 224213237Sgonzo { 0, 0 } 225213237Sgonzo}; 226213237Sgonzo 227213237Sgonzostatic driver_t gpioled_driver = { 228213237Sgonzo "gpioled", 229213237Sgonzo gpioled_methods, 230213237Sgonzo sizeof(struct gpioled_softc), 231213237Sgonzo}; 232213237Sgonzo 233213237SgonzoDRIVER_MODULE(gpioled, gpiobus, gpioled_driver, gpioled_devclass, 0, 0); 234