pl061gpio_fdt.c revision 1.2
1/* $NetBSD: pl061gpio_fdt.c,v 1.2 2018/09/03 23:19:01 jmcneill Exp $ */ 2 3/* 4 * Copyright (c) 2018 Jonathan A. Kollasch 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 26 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__KERNEL_RCSID(0, "$NetBSD: pl061gpio_fdt.c,v 1.2 2018/09/03 23:19:01 jmcneill Exp $"); 31 32#include <sys/param.h> 33#include <sys/bus.h> 34#include <sys/device.h> 35#include <sys/intr.h> 36#include <sys/systm.h> 37#include <sys/kernel.h> 38#include <sys/kmem.h> 39#include <sys/gpio.h> 40 41#include <dev/gpio/gpiovar.h> 42#include "gpio.h" 43 44#include <dev/ic/pl061reg.h> 45 46#include <dev/fdt/fdtvar.h> 47 48static int pl061_gpio_match(device_t, cfdata_t, void *); 49static void pl061_gpio_attach(device_t, device_t, void *); 50 51static void * pl061_gpio_fdt_acquire(device_t, const void *, 52 size_t, int); 53static void pl061_gpio_fdt_release(device_t, void *); 54static int pl061_gpio_fdt_read(device_t, void *, bool); 55static void pl061_gpio_fdt_write(device_t, void *, int, bool); 56 57struct fdtbus_gpio_controller_func pl061_gpio_funcs = { 58 .acquire = pl061_gpio_fdt_acquire, 59 .release = pl061_gpio_fdt_release, 60 .read = pl061_gpio_fdt_read, 61 .write = pl061_gpio_fdt_write 62}; 63 64struct pl061_gpio_softc { 65 device_t sc_dev; 66 bus_space_tag_t sc_bst; 67 bus_space_handle_t sc_bsh; 68 69 struct gpio_chipset_tag sc_gc; 70 gpio_pin_t sc_pins[8]; 71}; 72 73struct pl061_gpio_pin { 74 struct pl061_gpio_softc *pin_sc; 75 int pin_no; 76 u_int pin_flags; 77 bool pin_actlo; 78}; 79 80static int pl061_gpio_pin_read(void *, int); 81static void pl061_gpio_pin_write(void *, int, int); 82static void pl061_gpio_pin_ctl(void *, int, int); 83 84CFATTACH_DECL_NEW(pl061gpio_fdt, sizeof(struct pl061_gpio_softc), 85 pl061_gpio_match, pl061_gpio_attach, NULL, NULL); 86 87#define PL061_WRITE(sc, reg, val) \ 88 bus_space_write_1((sc)->sc_bst, (sc)->sc_bsh, (reg)/4, (val)) 89#define PL061_READ(sc, reg) \ 90 bus_space_read_1((sc)->sc_bst, (sc)->sc_bsh, (reg)/4) 91 92static int 93pl061_gpio_match(device_t parent, cfdata_t cf, void *aux) 94{ 95 const char * const compatible[] = { 96 "arm,pl061", 97 NULL 98 }; 99 struct fdt_attach_args * const faa = aux; 100 101 return of_match_compatible(faa->faa_phandle, compatible); 102} 103 104static void 105pl061_gpio_attach(device_t parent, device_t self, void *aux) 106{ 107 struct pl061_gpio_softc * const sc = device_private(self); 108 struct fdt_attach_args * const faa = aux; 109 bus_addr_t addr; 110 bus_size_t size; 111 int error; 112 struct gpiobus_attach_args gba; 113 u_int pin; 114 115 116 if (fdtbus_get_reg(faa->faa_phandle, 0, &addr, &size) != 0) { 117 aprint_error(": couldn't get registers\n"); 118 return; 119 } 120 121 sc->sc_dev = self; 122 sc->sc_bst = faa->faa_a4x_bst; 123 error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh); 124 if (error) { 125 aprint_error(": couldn't map %#"PRIx64": %d", (uint64_t)addr, error); 126 return; 127 } 128 129 aprint_naive("\n"); 130 aprint_normal(": GPIO\n"); 131 132 sc->sc_gc.gp_cookie = sc; 133 sc->sc_gc.gp_pin_read = pl061_gpio_pin_read; 134 sc->sc_gc.gp_pin_write = pl061_gpio_pin_write; 135 sc->sc_gc.gp_pin_ctl = pl061_gpio_pin_ctl; 136 137 const uint32_t cnf = PL061_READ(sc, PL061_GPIOAFSEL_REG); 138 139 for (pin = 0; pin < 8; pin++) { 140 sc->sc_pins[pin].pin_num = pin; 141 /* skip pins in hardware control mode */ 142 if ((cnf & __BIT(pin)) != 0) 143 continue; 144 sc->sc_pins[pin].pin_caps = 145 GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | 146 GPIO_PIN_TRISTATE; 147 sc->sc_pins[pin].pin_state = 148 pl061_gpio_pin_read(sc, pin); 149 } 150 151 memset(&gba, 0, sizeof(gba)); 152 gba.gba_gc = &sc->sc_gc; 153 gba.gba_pins = sc->sc_pins; 154 gba.gba_npins = 8; 155 156#if NGPIO > 0 157 (void)config_found_ia(sc->sc_dev, "gpiobus", &gba, 158 gpiobus_print); 159#endif 160 161 fdtbus_register_gpio_controller(self, faa->faa_phandle, 162 &pl061_gpio_funcs); 163 164} 165 166static int 167pl061_gpio_pin_read(void *priv, int pin) 168{ 169 struct pl061_gpio_softc * const sc = priv; 170 171 const uint32_t v = PL061_READ(sc, PL061_GPIODATA_REG(1<<pin)); 172 173 return (v >> pin) & 1; 174} 175 176static void 177pl061_gpio_pin_write(void *priv, int pin, int val) 178{ 179 struct pl061_gpio_softc * const sc = priv; 180 181 PL061_WRITE(sc, PL061_GPIODATA_REG(1 << pin), val << pin); 182} 183 184static void 185pl061_gpio_pin_ctl(void *priv, int pin, int flags) 186{ 187 struct pl061_gpio_softc * const sc = priv; 188 uint32_t v; 189 190 if (flags & GPIO_PIN_INPUT) { 191 v = PL061_READ(sc, PL061_GPIODIR_REG); 192 v &= ~(1 << pin); 193 PL061_WRITE(sc, PL061_GPIODIR_REG, v); 194 } else if (flags & GPIO_PIN_OUTPUT) { 195 v = PL061_READ(sc, PL061_GPIODIR_REG); 196 v |= (1 << pin); 197 PL061_WRITE(sc, PL061_GPIODIR_REG, v); 198 } 199} 200 201static void * 202pl061_gpio_fdt_acquire(device_t dev, const void *data, size_t len, int flags) 203{ 204 struct pl061_gpio_softc * const sc = device_private(dev); 205 struct pl061_gpio_pin *gpin; 206 const u_int *gpio = data; 207 208 if (len != 12) 209 return NULL; 210 211 const u_int pin = be32toh(gpio[1]); 212 const bool actlo = be32toh(gpio[2]) & 1; 213 214 if (pin > 8) 215 return NULL; 216 217 const uint32_t cnf = PL061_READ(sc, PL061_GPIOAFSEL_REG); 218 if ((cnf & __BIT(pin)) != 0) 219 PL061_WRITE(sc, PL061_GPIOAFSEL_REG, cnf & ~__BIT(pin)); 220 221 gpin = kmem_zalloc(sizeof(*gpin), KM_SLEEP); 222 gpin->pin_sc = sc; 223 gpin->pin_no = pin; 224 gpin->pin_flags = flags; 225 gpin->pin_actlo = actlo; 226 227 pl061_gpio_pin_ctl(gpin->pin_sc, gpin->pin_no, gpin->pin_flags); 228 229 return gpin; 230} 231 232static void 233pl061_gpio_fdt_release(device_t dev, void *priv) 234{ 235 struct pl061_gpio_pin * const gpin = priv; 236 237 pl061_gpio_pin_ctl(gpin->pin_sc, gpin->pin_no, GPIO_PIN_INPUT); 238 kmem_free(gpin, sizeof(*gpin)); 239} 240 241static int 242pl061_gpio_fdt_read(device_t dev, void *priv, bool raw) 243{ 244 struct pl061_gpio_pin * const gpin = priv; 245 int val; 246 247 val = pl061_gpio_pin_read(gpin->pin_sc, gpin->pin_no); 248 249 if (!raw && gpin->pin_actlo) 250 val = !val; 251 252 return val; 253} 254 255static void 256pl061_gpio_fdt_write(device_t dev, void *priv, int val, bool raw) 257{ 258 struct pl061_gpio_pin * const gpin = priv; 259 260 if (!raw && gpin->pin_actlo) 261 val = !val; 262 263 pl061_gpio_pin_write(gpin->pin_sc, gpin->pin_no, val); 264} 265