1 /*- 2 * Copyright (c) 2009 Oleksandr Tymoshenko <gonzo@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 #include "opt_platform.h" 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/bus.h> 33 #include <sys/gpio.h> 34 #include <sys/kernel.h> 35 #include <sys/lock.h> 36 #include <sys/malloc.h> 37 #include <sys/module.h> 38 #include <sys/mutex.h> 39 40 #include <dev/fdt/fdt_common.h> 41 #include <dev/ofw/ofw_bus.h> 42 43 #include <dev/gpio/gpiobusvar.h> 44 #include <dev/led/led.h> 45 46 #include "gpiobus_if.h" 47 48 struct gpioled 49 { 50 struct gpioleds_softc *parent_sc; 51 gpio_pin_t pin; 52 struct cdev *leddev; 53 }; 54 55 struct gpioleds_softc 56 { 57 device_t sc_dev; 58 device_t sc_busdev; 59 struct gpioled *sc_leds; 60 int sc_total_leds; 61 }; 62 63 static void gpioled_control(void *, int); 64 static int gpioled_probe(device_t); 65 static int gpioled_attach(device_t); 66 static int gpioled_detach(device_t); 67 68 static void 69 gpioled_control(void *priv, int onoff) 70 { 71 struct gpioled *led; 72 73 led = (struct gpioled *)priv; 74 if (led->pin) 75 gpio_pin_set_active(led->pin, onoff); 76 } 77 78 static void 79 gpioleds_attach_led(struct gpioleds_softc *sc, phandle_t node, 80 struct gpioled *led) 81 { 82 char *name; 83 int state, err; 84 char *default_state; 85 86 led->parent_sc = sc; 87 88 state = 0; 89 if (OF_getprop_alloc(node, "default-state", 90 (void **)&default_state) != -1) { 91 if (strcasecmp(default_state, "on") == 0) 92 state = 1; 93 else if (strcasecmp(default_state, "off") == 0) 94 state = 0; 95 else if (strcasecmp(default_state, "keep") == 0) 96 state = -1; 97 else { 98 state = -1; 99 device_printf(sc->sc_dev, 100 "unknown value for default-state in FDT\n"); 101 } 102 OF_prop_free(default_state); 103 } 104 105 name = NULL; 106 if (OF_getprop_alloc(node, "label", (void **)&name) == -1) 107 OF_getprop_alloc(node, "name", (void **)&name); 108 109 if (name == NULL) { 110 device_printf(sc->sc_dev, 111 "no name provided for gpio LED, skipping\n"); 112 return; 113 } 114 115 err = gpio_pin_get_by_ofw_idx(sc->sc_dev, node, 0, &led->pin); 116 if (err) { 117 device_printf(sc->sc_dev, "<%s> failed to map pin\n", name); 118 if (name) 119 OF_prop_free(name); 120 return; 121 } 122 gpio_pin_setflags(led->pin, GPIO_PIN_OUTPUT); 123 124 led->leddev = led_create_state(gpioled_control, led, name, 125 state); 126 127 if (name != NULL) 128 OF_prop_free(name); 129 } 130 131 static void 132 gpioleds_detach_led(struct gpioled *led) 133 { 134 135 if (led->leddev != NULL) 136 led_destroy(led->leddev); 137 138 if (led->pin) 139 gpio_pin_release(led->pin); 140 } 141 142 static int 143 gpioled_probe(device_t dev) 144 { 145 if (!ofw_bus_status_okay(dev)) 146 return (ENXIO); 147 if (!ofw_bus_is_compatible(dev, "gpio-leds")) 148 return (ENXIO); 149 150 device_set_desc(dev, "GPIO LEDs"); 151 152 return (BUS_PROBE_DEFAULT); 153 } 154 155 static int 156 gpioled_attach(device_t dev) 157 { 158 struct gpioleds_softc *sc; 159 phandle_t child, leds; 160 int total_leds; 161 162 if ((leds = ofw_bus_get_node(dev)) == -1) 163 return (ENXIO); 164 165 sc = device_get_softc(dev); 166 sc->sc_dev = dev; 167 sc->sc_busdev = device_get_parent(dev); 168 169 /* Traverse the 'gpio-leds' node and count leds */ 170 total_leds = 0; 171 for (child = OF_child(leds); child != 0; child = OF_peer(child)) { 172 if (!OF_hasprop(child, "gpios")) 173 continue; 174 total_leds++; 175 } 176 177 if (total_leds) { 178 sc->sc_leds = malloc(sizeof(struct gpioled) * total_leds, 179 M_DEVBUF, M_WAITOK | M_ZERO); 180 181 sc->sc_total_leds = 0; 182 /* Traverse the 'gpio-leds' node and count leds */ 183 for (child = OF_child(leds); child != 0; child = OF_peer(child)) { 184 if (!OF_hasprop(child, "gpios")) 185 continue; 186 gpioleds_attach_led(sc, child, &sc->sc_leds[sc->sc_total_leds]); 187 sc->sc_total_leds++; 188 } 189 } 190 191 return (0); 192 } 193 194 static int 195 gpioled_detach(device_t dev) 196 { 197 struct gpioleds_softc *sc; 198 int i; 199 200 sc = device_get_softc(dev); 201 202 for (i = 0; i < sc->sc_total_leds; i++) 203 gpioleds_detach_led(&sc->sc_leds[i]); 204 205 if (sc->sc_leds) 206 free(sc->sc_leds, M_DEVBUF); 207 208 return (0); 209 } 210 211 static device_method_t gpioled_methods[] = { 212 /* Device interface */ 213 DEVMETHOD(device_probe, gpioled_probe), 214 DEVMETHOD(device_attach, gpioled_attach), 215 DEVMETHOD(device_detach, gpioled_detach), 216 217 DEVMETHOD_END 218 }; 219 220 static driver_t gpioled_driver = { 221 "gpioled", 222 gpioled_methods, 223 sizeof(struct gpioleds_softc), 224 }; 225 226 DRIVER_MODULE(gpioled, ofwbus, gpioled_driver, 0, 0); 227 DRIVER_MODULE(gpioled, simplebus, gpioled_driver, 0, 0); 228 MODULE_DEPEND(gpioled, gpiobus, 1, 1, 1); 229