1 /* $OpenBSD: exgpio.c,v 1.6 2017/03/11 17:06:27 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org> 4 * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> 5 * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/param.h> 21 #include <sys/systm.h> 22 #include <sys/device.h> 23 #include <sys/malloc.h> 24 25 #include <machine/bus.h> 26 #include <machine/fdt.h> 27 #include <machine/intr.h> 28 29 #include <dev/ofw/openfirm.h> 30 #include <dev/ofw/ofw_gpio.h> 31 #include <dev/ofw/ofw_pinctrl.h> 32 #include <dev/ofw/fdt.h> 33 34 #define GPXCON(x) ((x) + 0x0000) 35 #define GPXCON_INPUT 0 36 #define GPXCON_OUTPUT 1 37 #define GPXDAT(x) ((x) + 0x0004) 38 #define GPXPUD(x) ((x) + 0x0008) 39 #define GPXDRV(x) ((x) + 0x000c) 40 41 #define GPX_NUM_PINS 8 42 43 #define HREAD4(sc, reg) \ 44 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 45 #define HWRITE4(sc, reg, val) \ 46 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 47 48 struct exgpio_bank { 49 const char name[8]; 50 bus_addr_t addr; 51 }; 52 53 struct exgpio_controller { 54 struct gpio_controller ec_gc; 55 struct exgpio_bank *ec_bank; 56 struct exgpio_softc *ec_sc; 57 }; 58 59 struct exgpio_softc { 60 struct device sc_dev; 61 bus_space_tag_t sc_iot; 62 bus_space_handle_t sc_ioh; 63 64 struct exgpio_bank *sc_banks; 65 int sc_nbanks; 66 }; 67 68 int exgpio_match(struct device *, void *, void *); 69 void exgpio_attach(struct device *, struct device *, void *); 70 71 struct cfattach exgpio_ca = { 72 sizeof (struct exgpio_softc), exgpio_match, exgpio_attach 73 }; 74 75 struct cfdriver exgpio_cd = { 76 NULL, "exgpio", DV_DULL 77 }; 78 79 /* Exynos 5420/5422 */ 80 struct exgpio_bank exynos5420_banks[] = { 81 /* Controller 0 */ 82 { "gpy7", 0x0000 }, 83 { "gpx0", 0x0c00 }, 84 { "gpx1", 0x0c20 }, 85 { "gpx2", 0x0c40 }, 86 { "gpx3", 0x0c60 }, 87 88 /* Controller 1 */ 89 { "gpc0", 0x0000 }, 90 { "gpc1", 0x0020 }, 91 { "gpc2", 0x0040 }, 92 { "gpc3", 0x0060 }, 93 { "gpc4", 0x0080 }, 94 { "gpd1", 0x00a0 }, 95 { "gpy0", 0x00c0 }, 96 { "gpy1", 0x00e0 }, 97 { "gpy2", 0x0100 }, 98 { "gpy3", 0x0120 }, 99 { "gpy4", 0x0140 }, 100 { "gpy5", 0x0160 }, 101 { "gpy6", 0x0180 }, 102 103 /* Controller 2 */ 104 { "gpe0", 0x0000 }, 105 { "gpe1", 0x0020 }, 106 { "gpf0", 0x0040 }, 107 { "gpf1", 0x0060 }, 108 { "gpg0", 0x0080 }, 109 { "gpg1", 0x00a0 }, 110 { "gpg2", 0x00c0 }, 111 { "gpj4", 0x00e0 }, 112 113 /* Controller 3 */ 114 { "gpa0", 0x0000 }, 115 { "gpa1", 0x0020 }, 116 { "gpa2", 0x0040 }, 117 { "gpb0", 0x0060 }, 118 { "gpb1", 0x0080 }, 119 { "gpb2", 0x00a0 }, 120 { "gpb3", 0x00c0 }, 121 { "gpb4", 0x00e0 }, 122 { "gph0", 0x0100 }, 123 124 /* Controller 4 */ 125 { "gpz", 0x0000 }, 126 }; 127 128 struct exgpio_bank *exgpio_bank(struct exgpio_softc *, const char *); 129 int exgpio_pinctrl(uint32_t, void *); 130 void exgpio_config_pin(void *, uint32_t *, int); 131 int exgpio_get_pin(void *, uint32_t *); 132 void exgpio_set_pin(void *, uint32_t *, int); 133 134 int 135 exgpio_match(struct device *parent, void *match, void *aux) 136 { 137 struct fdt_attach_args *faa = aux; 138 139 return OF_is_compatible(faa->fa_node, "samsung,exynos5420-pinctrl"); 140 } 141 142 void 143 exgpio_attach(struct device *parent, struct device *self, void *aux) 144 { 145 struct exgpio_softc *sc = (struct exgpio_softc *)self; 146 struct fdt_attach_args *faa = aux; 147 struct exgpio_controller *ec; 148 struct exgpio_bank *bank; 149 char name[8]; 150 int node; 151 int len; 152 153 sc->sc_iot = faa->fa_iot; 154 155 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 156 faa->fa_reg[0].size, 0, &sc->sc_ioh)) 157 panic("%s: bus_space_map failed!", __func__); 158 159 if (OF_is_compatible(faa->fa_node, "samsung,exynos5420-pinctrl")) { 160 sc->sc_banks = exynos5420_banks; 161 sc->sc_nbanks = nitems(exynos5420_banks); 162 } 163 164 KASSERT(sc->sc_banks); 165 pinctrl_register(faa->fa_node, exgpio_pinctrl, sc); 166 167 for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) { 168 if (OF_getproplen(node, "gpio-controller") < 0) 169 continue; 170 171 len = OF_getprop(node, "name", &name, sizeof(name)); 172 if (len <= 0 || len >= sizeof(name)) 173 continue; 174 175 bank = exgpio_bank(sc, name); 176 if (bank == NULL) 177 continue; 178 179 ec = malloc(sizeof(*ec), M_DEVBUF, M_WAITOK); 180 ec->ec_bank = exgpio_bank(sc, name); 181 ec->ec_sc = sc; 182 ec->ec_gc.gc_node = node; 183 ec->ec_gc.gc_cookie = ec; 184 ec->ec_gc.gc_config_pin = exgpio_config_pin; 185 ec->ec_gc.gc_get_pin = exgpio_get_pin; 186 ec->ec_gc.gc_set_pin = exgpio_set_pin; 187 gpio_controller_register(&ec->ec_gc); 188 } 189 190 printf("\n"); 191 } 192 193 struct exgpio_bank * 194 exgpio_bank(struct exgpio_softc *sc, const char *name) 195 { 196 int i; 197 198 for (i = 0; i < sc->sc_nbanks; i++) { 199 if (strcmp(name, sc->sc_banks[i].name) == 0) 200 return &sc->sc_banks[i]; 201 } 202 203 return NULL; 204 } 205 206 int 207 exgpio_pinctrl(uint32_t phandle, void *cookie) 208 { 209 struct exgpio_softc *sc = cookie; 210 char *pins, *bank_name, *pin_name; 211 struct exgpio_bank *bank; 212 uint32_t func, val, pud, drv; 213 uint32_t reg; 214 int node; 215 int len; 216 int pin; 217 218 node = OF_getnodebyphandle(phandle); 219 if (node == 0) 220 return -1; 221 222 len = OF_getproplen(node, "samsung,pins"); 223 if (len <= 0) 224 return -1; 225 226 pins = malloc(len, M_TEMP, M_WAITOK); 227 OF_getprop(node, "samsung,pins", pins, len); 228 229 func = OF_getpropint(node, "samsung,pin-function", 0); 230 val = OF_getpropint(node, "samsung,pin-val", 0); 231 pud = OF_getpropint(node, "samsung,pin-pud", 1); 232 drv = OF_getpropint(node, "samsung,pin-drv", 0); 233 234 bank_name = pins; 235 while (bank_name < pins + len) { 236 pin_name = strchr(bank_name, '-'); 237 if (pin_name == NULL) 238 goto fail; 239 *pin_name++ = 0; 240 pin = *pin_name - '0'; 241 if (pin < 0 || pin >= GPX_NUM_PINS) 242 goto fail; 243 244 bank = exgpio_bank(sc, bank_name); 245 if (bank == NULL) 246 goto fail; 247 248 reg = HREAD4(sc, GPXCON(bank->addr)); 249 reg &= ~(0xf << (pin * 4)); 250 reg |= (func << (pin * 4)); 251 HWRITE4(sc, GPXCON(bank->addr), reg); 252 253 reg = HREAD4(sc, GPXDAT(bank->addr)); 254 if (val) 255 reg |= (1 << pin); 256 else 257 reg &= ~(1 << pin); 258 HWRITE4(sc, GPXDAT(bank->addr), reg); 259 260 reg = HREAD4(sc, GPXPUD(bank->addr)); 261 reg &= ~(0x3 << (pin * 2)); 262 reg |= (pud << (pin * 2)); 263 HWRITE4(sc, GPXPUD(bank->addr), reg); 264 265 reg = HREAD4(sc, GPXDRV(bank->addr)); 266 reg &= ~(0x3 << (pin * 2)); 267 reg |= (drv << (pin * 2)); 268 HWRITE4(sc, GPXDRV(bank->addr), reg); 269 270 bank_name = pin_name + 2; 271 } 272 273 free(pins, M_TEMP, len); 274 return 0; 275 276 fail: 277 free(pins, M_TEMP, len); 278 return -1; 279 } 280 281 void 282 exgpio_config_pin(void *cookie, uint32_t *cells, int config) 283 { 284 struct exgpio_controller *ec = cookie; 285 uint32_t pin = cells[0]; 286 uint32_t val; 287 int func; 288 289 if (pin >= GPX_NUM_PINS) 290 return; 291 292 func = (config & GPIO_CONFIG_OUTPUT) ? GPXCON_OUTPUT : GPXCON_INPUT; 293 val = HREAD4(ec->ec_sc, GPXCON(ec->ec_bank->addr)); 294 val &= ~(0xf << (pin * 4)); 295 val |= (func << (pin * 4)); 296 HWRITE4(ec->ec_sc, GPXCON(ec->ec_bank->addr), val); 297 } 298 299 int 300 exgpio_get_pin(void *cookie, uint32_t *cells) 301 { 302 struct exgpio_controller *ec = cookie; 303 uint32_t pin = cells[0]; 304 uint32_t flags = cells[1]; 305 uint32_t reg; 306 int val; 307 308 if (pin >= GPX_NUM_PINS) 309 return 0; 310 311 reg = HREAD4(ec->ec_sc, GPXDAT(ec->ec_bank->addr)); 312 reg &= (1 << pin); 313 val = (reg >> pin) & 1; 314 if (flags & GPIO_ACTIVE_LOW) 315 val = !val; 316 return val; 317 } 318 319 void 320 exgpio_set_pin(void *cookie, uint32_t *cells, int val) 321 { 322 struct exgpio_controller *ec = cookie; 323 uint32_t pin = cells[0]; 324 uint32_t flags = cells[1]; 325 uint32_t reg; 326 327 if (pin >= GPX_NUM_PINS) 328 return; 329 330 reg = HREAD4(ec->ec_sc, GPXDAT(ec->ec_bank->addr)); 331 if (flags & GPIO_ACTIVE_LOW) 332 val = !val; 333 if (val) 334 reg |= (1 << pin); 335 else 336 reg &= ~(1 << pin); 337 HWRITE4(ec->ec_sc, GPXDAT(ec->ec_bank->addr), reg); 338 } 339