qoriq_gpio.c revision 291462
1/*- 2 * Copyright (c) 2015 Justin Hibbits 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 * $FreeBSD: head/sys/powerpc/mpc85xx/qoriq_gpio.c 291462 2015-11-30 02:23:56Z jhibbits $ 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD: head/sys/powerpc/mpc85xx/qoriq_gpio.c 291462 2015-11-30 02:23:56Z jhibbits $"); 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/conf.h> 35#include <sys/bus.h> 36#include <sys/kernel.h> 37#include <sys/module.h> 38#include <sys/mutex.h> 39#include <sys/rman.h> 40#include <sys/gpio.h> 41 42#include <machine/bus.h> 43#include <machine/resource.h> 44#include <machine/stdarg.h> 45 46#include <dev/gpio/gpiobusvar.h> 47#include <dev/ofw/ofw_bus.h> 48#include <dev/ofw/ofw_bus_subr.h> 49 50#include "gpio_if.h" 51 52#define MAXPIN (31) 53 54#define VALID_PIN(u) ((u) >= 0 && (u) <= MAXPIN) 55 56#define GPIO_LOCK(sc) mtx_lock(&(sc)->sc_mtx) 57#define GPIO_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) 58#define GPIO_LOCK_INIT(sc) \ 59 mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), \ 60 "gpio", MTX_DEF) 61#define GPIO_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); 62 63#define GPIO_GPDIR 0x0 64#define GPIO_GPODR 0x4 65#define GPIO_GPDAT 0x8 66#define GPIO_GPIER 0xc 67#define GPIO_GPIMR 0x10 68#define GPIO_GPICR 0x14 69 70 71struct qoriq_gpio_softc { 72 device_t dev; 73 device_t busdev; 74 struct mtx sc_mtx; 75 struct resource *sc_mem; /* Memory resource */ 76}; 77 78static device_t 79qoriq_gpio_get_bus(device_t dev) 80{ 81 struct qoriq_gpio_softc *sc; 82 83 sc = device_get_softc(dev); 84 85 return (sc->busdev); 86} 87 88static int 89qoriq_gpio_pin_max(device_t dev, int *maxpin) 90{ 91 92 *maxpin = MAXPIN; 93 return (0); 94} 95 96/* Get a specific pin's capabilities. */ 97static int 98qoriq_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) 99{ 100 101 if (!VALID_PIN(pin)) 102 return (EINVAL); 103 104 *caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN); 105 106 return (0); 107} 108 109/* Get a specific pin's name. */ 110static int 111qoriq_gpio_pin_getname(device_t dev, uint32_t pin, char *name) 112{ 113 114 if (!VALID_PIN(pin)) 115 return (EINVAL); 116 117 snprintf(name, GPIOMAXNAME, "qoriq_gpio%d.%d", 118 device_get_unit(dev), pin); 119 name[GPIOMAXNAME-1] = '\0'; 120 121 return (0); 122} 123 124/* Set flags for the pin. */ 125static int 126qoriq_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) 127{ 128 struct qoriq_gpio_softc *sc = device_get_softc(dev); 129 uint32_t reg; 130 131 if (!VALID_PIN(pin)) 132 return (EINVAL); 133 134 if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) == 135 (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) 136 return (EINVAL); 137 138 GPIO_LOCK(sc); 139 if (flags & GPIO_PIN_INPUT) { 140 reg = bus_read_4(sc->sc_mem, GPIO_GPDIR); 141 reg &= ~(1 << (31 - pin)); 142 bus_write_4(sc->sc_mem, GPIO_GPDIR, reg); 143 } 144 else if (flags & GPIO_PIN_OUTPUT) { 145 reg = bus_read_4(sc->sc_mem, GPIO_GPDIR); 146 reg |= (1 << (31 - pin)); 147 bus_write_4(sc->sc_mem, GPIO_GPDIR, reg); 148 reg = bus_read_4(sc->sc_mem, GPIO_GPODR); 149 if (flags & GPIO_PIN_OPENDRAIN) 150 reg |= (1 << (31 - pin)); 151 else 152 reg &= ~(1 << (31 - pin)); 153 bus_write_4(sc->sc_mem, GPIO_GPODR, reg); 154 } 155 GPIO_UNLOCK(sc); 156 return (0); 157} 158 159/* Set a specific output pin's value. */ 160static int 161qoriq_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) 162{ 163 struct qoriq_gpio_softc *sc = device_get_softc(dev); 164 uint32_t outvals; 165 uint8_t pinbit; 166 167 if (!VALID_PIN(pin) || value > 1) 168 return (EINVAL); 169 170 GPIO_LOCK(sc); 171 pinbit = 31 - pin; 172 173 outvals = bus_read_4(sc->sc_mem, GPIO_GPDAT); 174 outvals &= ~(1 << pinbit); 175 outvals |= (value << pinbit); 176 bus_write_4(sc->sc_mem, 0, outvals); 177 178 GPIO_UNLOCK(sc); 179 180 return (0); 181} 182 183/* Get a specific pin's input value. */ 184static int 185qoriq_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value) 186{ 187 struct qoriq_gpio_softc *sc = device_get_softc(dev); 188 189 if (!VALID_PIN(pin)) 190 return (EINVAL); 191 192 *value = (bus_read_4(sc->sc_mem, GPIO_GPDAT) >> (31 - pin)) & 1; 193 194 return (0); 195} 196 197/* Toggle a pin's output value. */ 198static int 199qoriq_gpio_pin_toggle(device_t dev, uint32_t pin) 200{ 201 struct qoriq_gpio_softc *sc = device_get_softc(dev); 202 uint32_t val; 203 204 if (!VALID_PIN(pin)) 205 return (EINVAL); 206 207 GPIO_LOCK(sc); 208 209 val = bus_read_4(sc->sc_mem, GPIO_GPDAT); 210 val ^= (1 << (31 - pin)); 211 bus_write_4(sc->sc_mem, 0, val); 212 213 GPIO_UNLOCK(sc); 214 215 return (0); 216} 217 218static int 219qoriq_gpio_probe(device_t dev) 220{ 221 222 if (!ofw_bus_is_compatible(dev, "fsl,qoriq-gpio")) 223 return (ENXIO); 224 225 device_set_desc(dev, "Freescale QorIQ GPIO driver"); 226 227 return (0); 228} 229 230static int qoriq_gpio_detach(device_t dev); 231 232static int 233qoriq_gpio_attach(device_t dev) 234{ 235 struct qoriq_gpio_softc *sc = device_get_softc(dev); 236 int rid; 237 238 sc->dev = dev; 239 240 GPIO_LOCK_INIT(sc); 241 242 /* Allocate memory. */ 243 rid = 0; 244 sc->sc_mem = bus_alloc_resource_any(dev, 245 SYS_RES_MEMORY, &rid, RF_ACTIVE); 246 if (sc->sc_mem == NULL) { 247 device_printf(dev, "Can't allocate memory for device output port"); 248 qoriq_gpio_detach(dev); 249 return (ENOMEM); 250 } 251 252 sc->busdev = gpiobus_attach_bus(dev); 253 if (sc->busdev == NULL) { 254 qoriq_gpio_detach(dev); 255 return (ENOMEM); 256 } 257 258 OF_device_register_xref(OF_xref_from_node(ofw_bus_get_node(dev)), dev); 259 260 return (0); 261} 262 263static int 264qoriq_gpio_detach(device_t dev) 265{ 266 struct qoriq_gpio_softc *sc = device_get_softc(dev); 267 268 gpiobus_detach_bus(dev); 269 270 if (sc->sc_mem != NULL) { 271 /* Release output port resource. */ 272 bus_release_resource(dev, SYS_RES_MEMORY, 273 rman_get_rid(sc->sc_mem), sc->sc_mem); 274 } 275 276 GPIO_LOCK_DESTROY(sc); 277 278 return (0); 279} 280 281static device_method_t qoriq_gpio_methods[] = { 282 /* device_if */ 283 DEVMETHOD(device_probe, qoriq_gpio_probe), 284 DEVMETHOD(device_attach, qoriq_gpio_attach), 285 DEVMETHOD(device_detach, qoriq_gpio_detach), 286 287 /* GPIO protocol */ 288 DEVMETHOD(gpio_get_bus, qoriq_gpio_get_bus), 289 DEVMETHOD(gpio_pin_max, qoriq_gpio_pin_max), 290 DEVMETHOD(gpio_pin_getname, qoriq_gpio_pin_getname), 291 DEVMETHOD(gpio_pin_getcaps, qoriq_gpio_pin_getcaps), 292 DEVMETHOD(gpio_pin_get, qoriq_gpio_pin_get), 293 DEVMETHOD(gpio_pin_set, qoriq_gpio_pin_set), 294 DEVMETHOD(gpio_pin_setflags, qoriq_gpio_pin_setflags), 295 DEVMETHOD(gpio_pin_toggle, qoriq_gpio_pin_toggle), 296 297 DEVMETHOD_END 298}; 299 300static driver_t qoriq_gpio_driver = { 301 "gpio", 302 qoriq_gpio_methods, 303 sizeof(struct qoriq_gpio_softc), 304}; 305static devclass_t qoriq_gpio_devclass; 306 307EARLY_DRIVER_MODULE(qoriq_gpio, simplebus, qoriq_gpio_driver, 308 qoriq_gpio_devclass, NULL, NULL, 309 BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE); 310