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