1308428Sgonzo/*- 2308428Sgonzo * Copyright (c) 2009 Oleksandr Tymoshenko <gonzo@freebsd.org> 3308428Sgonzo * All rights reserved. 4308428Sgonzo * 5308428Sgonzo * Redistribution and use in source and binary forms, with or without 6308428Sgonzo * modification, are permitted provided that the following conditions 7308428Sgonzo * are met: 8308428Sgonzo * 1. Redistributions of source code must retain the above copyright 9308428Sgonzo * notice, this list of conditions and the following disclaimer. 10308428Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 11308428Sgonzo * notice, this list of conditions and the following disclaimer in the 12308428Sgonzo * documentation and/or other materials provided with the distribution. 13308428Sgonzo * 14308428Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15308428Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16308428Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17308428Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18308428Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19308428Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20308428Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21308428Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22308428Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23308428Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24308428Sgonzo * SUCH DAMAGE. 25308428Sgonzo */ 26308428Sgonzo 27308428Sgonzo#include <sys/cdefs.h> 28308428Sgonzo__FBSDID("$FreeBSD: stable/11/sys/dev/gpio/gpioled_fdt.c 308658 2016-11-15 00:28:07Z gonzo $"); 29308428Sgonzo 30308428Sgonzo#include "opt_platform.h" 31308428Sgonzo 32308428Sgonzo#include <sys/param.h> 33308428Sgonzo#include <sys/systm.h> 34308428Sgonzo#include <sys/bus.h> 35308428Sgonzo#include <sys/gpio.h> 36308428Sgonzo#include <sys/kernel.h> 37308428Sgonzo#include <sys/lock.h> 38308428Sgonzo#include <sys/malloc.h> 39308428Sgonzo#include <sys/module.h> 40308428Sgonzo#include <sys/mutex.h> 41308428Sgonzo 42308428Sgonzo#include <dev/fdt/fdt_common.h> 43308428Sgonzo#include <dev/ofw/ofw_bus.h> 44308428Sgonzo 45308428Sgonzo#include <dev/gpio/gpiobusvar.h> 46308428Sgonzo#include <dev/led/led.h> 47308428Sgonzo 48308428Sgonzo#include "gpiobus_if.h" 49308428Sgonzo 50308428Sgonzostruct gpioled 51308428Sgonzo{ 52308428Sgonzo struct gpioleds_softc *parent_sc; 53308428Sgonzo gpio_pin_t pin; 54308428Sgonzo struct cdev *leddev; 55308428Sgonzo}; 56308428Sgonzo 57308428Sgonzostruct gpioleds_softc 58308428Sgonzo{ 59308428Sgonzo device_t sc_dev; 60308428Sgonzo device_t sc_busdev; 61308428Sgonzo struct gpioled *sc_leds; 62308428Sgonzo int sc_total_leds; 63308428Sgonzo}; 64308428Sgonzo 65308428Sgonzostatic void gpioled_control(void *, int); 66308428Sgonzostatic int gpioled_probe(device_t); 67308428Sgonzostatic int gpioled_attach(device_t); 68308428Sgonzostatic int gpioled_detach(device_t); 69308428Sgonzo 70308428Sgonzostatic void 71308428Sgonzogpioled_control(void *priv, int onoff) 72308428Sgonzo{ 73308428Sgonzo struct gpioled *led; 74308428Sgonzo 75308428Sgonzo led = (struct gpioled *)priv; 76308428Sgonzo if (led->pin) 77308428Sgonzo gpio_pin_set_active(led->pin, onoff); 78308428Sgonzo} 79308428Sgonzo 80308428Sgonzostatic void 81308428Sgonzogpioleds_attach_led(struct gpioleds_softc *sc, phandle_t node, 82308428Sgonzo struct gpioled *led) 83308428Sgonzo{ 84308428Sgonzo char *name; 85308428Sgonzo int state, err; 86308428Sgonzo char *default_state; 87308428Sgonzo 88308428Sgonzo led->parent_sc = sc; 89308428Sgonzo 90308428Sgonzo state = 0; 91308428Sgonzo if (OF_getprop_alloc(node, "default-state", 92308428Sgonzo sizeof(char), (void **)&default_state) != -1) { 93308428Sgonzo if (strcasecmp(default_state, "on") == 0) 94308428Sgonzo state = 1; 95308428Sgonzo else if (strcasecmp(default_state, "off") == 0) 96308428Sgonzo state = 0; 97308428Sgonzo else if (strcasecmp(default_state, "keep") == 0) 98308428Sgonzo state = -1; 99308428Sgonzo else { 100308428Sgonzo state = -1; 101308428Sgonzo device_printf(sc->sc_dev, 102308428Sgonzo "unknown value for default-state in FDT\n"); 103308428Sgonzo } 104308428Sgonzo OF_prop_free(default_state); 105308428Sgonzo } 106308428Sgonzo 107308428Sgonzo name = NULL; 108308428Sgonzo if (OF_getprop_alloc(node, "label", 1, (void **)&name) == -1) 109308428Sgonzo OF_getprop_alloc(node, "name", 1, (void **)&name); 110308428Sgonzo 111308428Sgonzo if (name == NULL) { 112308428Sgonzo device_printf(sc->sc_dev, 113308428Sgonzo "no name provided for gpio LED, skipping\n"); 114308428Sgonzo return; 115308428Sgonzo } 116308428Sgonzo 117308428Sgonzo err = gpio_pin_get_by_ofw_idx(sc->sc_dev, node, 0, &led->pin); 118308428Sgonzo if (err) { 119308428Sgonzo device_printf(sc->sc_dev, "<%s> failed to map pin\n", name); 120308428Sgonzo if (name) 121308428Sgonzo OF_prop_free(name); 122308428Sgonzo return; 123308428Sgonzo } 124308428Sgonzo gpio_pin_setflags(led->pin, GPIO_PIN_OUTPUT); 125308428Sgonzo 126308428Sgonzo led->leddev = led_create_state(gpioled_control, led, name, 127308428Sgonzo state); 128308428Sgonzo 129308428Sgonzo if (name != NULL) 130308428Sgonzo OF_prop_free(name); 131308428Sgonzo} 132308428Sgonzo 133308428Sgonzostatic void 134308428Sgonzogpioleds_detach_led(struct gpioled *led) 135308428Sgonzo{ 136308428Sgonzo 137308428Sgonzo if (led->leddev != NULL) 138308428Sgonzo led_destroy(led->leddev); 139308428Sgonzo 140308428Sgonzo if (led->pin) 141308428Sgonzo gpio_pin_release(led->pin); 142308428Sgonzo} 143308428Sgonzo 144308428Sgonzostatic int 145308428Sgonzogpioled_probe(device_t dev) 146308428Sgonzo{ 147308428Sgonzo if (!ofw_bus_is_compatible(dev, "gpio-leds")) 148308428Sgonzo return (ENXIO); 149308428Sgonzo 150308428Sgonzo device_set_desc(dev, "GPIO LEDs"); 151308428Sgonzo 152308428Sgonzo return (BUS_PROBE_DEFAULT); 153308428Sgonzo} 154308428Sgonzo 155308428Sgonzostatic int 156308428Sgonzogpioled_attach(device_t dev) 157308428Sgonzo{ 158308428Sgonzo struct gpioleds_softc *sc; 159308428Sgonzo phandle_t child, leds; 160308428Sgonzo int total_leds; 161308428Sgonzo 162308428Sgonzo if ((leds = ofw_bus_get_node(dev)) == -1) 163308428Sgonzo return (ENXIO); 164308428Sgonzo 165308428Sgonzo sc = device_get_softc(dev); 166308428Sgonzo sc->sc_dev = dev; 167308428Sgonzo sc->sc_busdev = device_get_parent(dev); 168308428Sgonzo 169308428Sgonzo /* Traverse the 'gpio-leds' node and count leds */ 170308428Sgonzo total_leds = 0; 171308428Sgonzo for (child = OF_child(leds); child != 0; child = OF_peer(child)) { 172308428Sgonzo if (!OF_hasprop(child, "gpios")) 173308428Sgonzo continue; 174308428Sgonzo total_leds++; 175308428Sgonzo } 176308428Sgonzo 177308428Sgonzo if (total_leds) { 178308428Sgonzo sc->sc_leds = malloc(sizeof(struct gpioled) * total_leds, 179308428Sgonzo M_DEVBUF, M_WAITOK | M_ZERO); 180308428Sgonzo 181308428Sgonzo sc->sc_total_leds = 0; 182308428Sgonzo /* Traverse the 'gpio-leds' node and count leds */ 183308428Sgonzo for (child = OF_child(leds); child != 0; child = OF_peer(child)) { 184308428Sgonzo if (!OF_hasprop(child, "gpios")) 185308428Sgonzo continue; 186308428Sgonzo gpioleds_attach_led(sc, child, &sc->sc_leds[sc->sc_total_leds]); 187308428Sgonzo sc->sc_total_leds++; 188308428Sgonzo } 189308428Sgonzo } 190308428Sgonzo 191308428Sgonzo return (0); 192308428Sgonzo} 193308428Sgonzo 194308428Sgonzostatic int 195308428Sgonzogpioled_detach(device_t dev) 196308428Sgonzo{ 197308428Sgonzo struct gpioleds_softc *sc; 198308428Sgonzo int i; 199308428Sgonzo 200308428Sgonzo sc = device_get_softc(dev); 201308428Sgonzo 202308428Sgonzo for (i = 0; i < sc->sc_total_leds; i++) 203308428Sgonzo gpioleds_detach_led(&sc->sc_leds[i]); 204308428Sgonzo 205308428Sgonzo if (sc->sc_leds) 206308428Sgonzo free(sc->sc_leds, M_DEVBUF); 207308428Sgonzo 208308428Sgonzo return (0); 209308428Sgonzo} 210308428Sgonzo 211308428Sgonzostatic devclass_t gpioled_devclass; 212308428Sgonzo 213308428Sgonzostatic device_method_t gpioled_methods[] = { 214308428Sgonzo /* Device interface */ 215308428Sgonzo DEVMETHOD(device_probe, gpioled_probe), 216308428Sgonzo DEVMETHOD(device_attach, gpioled_attach), 217308428Sgonzo DEVMETHOD(device_detach, gpioled_detach), 218308428Sgonzo 219308428Sgonzo DEVMETHOD_END 220308428Sgonzo}; 221308428Sgonzo 222308428Sgonzostatic driver_t gpioled_driver = { 223308428Sgonzo "gpioled", 224308428Sgonzo gpioled_methods, 225308428Sgonzo sizeof(struct gpioleds_softc), 226308428Sgonzo}; 227308428Sgonzo 228308428SgonzoDRIVER_MODULE(gpioled, ofwbus, gpioled_driver, gpioled_devclass, 0, 0); 229308428SgonzoDRIVER_MODULE(gpioled, simplebus, gpioled_driver, gpioled_devclass, 0, 0); 230308428SgonzoMODULE_DEPEND(gpioled, gpiobus, 1, 1, 1); 231