1 /* $OpenBSD: mvpinctrl.c,v 1.12 2023/08/15 08:27:30 miod Exp $ */ 2 /* 3 * Copyright (c) 2013,2016 Patrick Wildt <patrick@blueri.se> 4 * Copyright (c) 2016 Mark Kettenis <kettenis@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/device.h> 22 #include <sys/malloc.h> 23 24 #include <machine/intr.h> 25 #include <machine/bus.h> 26 #include <machine/fdt.h> 27 28 #include <dev/ofw/openfirm.h> 29 #include <dev/ofw/ofw_clock.h> 30 #include <dev/ofw/ofw_gpio.h> 31 #include <dev/ofw/ofw_misc.h> 32 #include <dev/ofw/ofw_pinctrl.h> 33 #include <dev/ofw/fdt.h> 34 35 /* Armada 3700 Registers */ 36 #define GPIO_DIRECTION 0x00 37 #define GPIO_INPUT 0x10 38 #define GPIO_OUTPUT 0x18 39 40 #define HREAD4(sc, reg) \ 41 (regmap_read_4((sc)->sc_rm, (reg))) 42 #define HWRITE4(sc, reg, val) \ 43 regmap_write_4((sc)->sc_rm, (reg), (val)) 44 #define HSET4(sc, reg, bits) \ 45 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) 46 #define HCLR4(sc, reg, bits) \ 47 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) 48 49 struct mvpinctrl_pin { 50 const char *pin; 51 const char *function; 52 int value; 53 int pid; 54 }; 55 56 struct mvpinctrl_softc { 57 struct device sc_dev; 58 bus_space_tag_t sc_iot; 59 bus_space_handle_t sc_ioh; 60 struct regmap *sc_rm; 61 const struct mvpinctrl_pin *sc_pins; 62 int sc_npins; 63 struct gpio_controller sc_gc; 64 struct clock_device sc_cd_xtal; 65 }; 66 67 int mvpinctrl_match(struct device *, void *, void *); 68 void mvpinctrl_attach(struct device *, struct device *, void *); 69 int mvpinctrl_pinctrl(uint32_t, void *); 70 71 void mvpinctrl_config_pin(void *, uint32_t *, int); 72 int mvpinctrl_get_pin(void *, uint32_t *); 73 void mvpinctrl_set_pin(void *, uint32_t *, int); 74 75 uint32_t a3700_xtal_get_frequency(void *, uint32_t *); 76 77 const struct cfattach mvpinctrl_ca = { 78 sizeof (struct mvpinctrl_softc), mvpinctrl_match, mvpinctrl_attach 79 }; 80 81 struct cfdriver mvpinctrl_cd = { 82 NULL, "mvpinctrl", DV_DULL 83 }; 84 85 #define STR_HELPER(x) #x 86 #define STR(x) STR_HELPER(x) 87 #define MPP(id, func, val) { STR(mpp ## id), func, val, id } 88 89 #include "mvpinctrl_pins.h" 90 91 struct mvpinctrl_pins { 92 const char *compat; 93 const struct mvpinctrl_pin *pins; 94 int npins; 95 }; 96 97 const struct mvpinctrl_pins mvpinctrl_pins[] = { 98 { 99 "marvell,mv88f6810-pinctrl", 100 armada_38x_pins, nitems(armada_38x_pins) 101 }, 102 { 103 "marvell,mv88f6820-pinctrl", 104 armada_38x_pins, nitems(armada_38x_pins) 105 }, 106 { 107 "marvell,mv88f6828-pinctrl", 108 armada_38x_pins, nitems(armada_38x_pins) 109 }, 110 { 111 "marvell,ap806-pinctrl", 112 armada_ap806_pins, nitems(armada_ap806_pins) 113 }, 114 { 115 "marvell,cp110-pinctrl", 116 armada_cp110_pins, nitems(armada_cp110_pins) 117 }, 118 { 119 "marvell,cp115-standalone-pinctrl", 120 armada_cp110_pins, nitems(armada_cp110_pins) 121 }, 122 { 123 "marvell,armada-7k-pinctrl", 124 armada_cp110_pins, nitems(armada_cp110_pins) 125 }, 126 { 127 "marvell,armada-8k-cpm-pinctrl", 128 armada_cp110_pins, nitems(armada_cp110_pins) 129 }, 130 { 131 "marvell,armada-8k-cps-pinctrl", 132 armada_cp110_pins, nitems(armada_cp110_pins) 133 }, 134 }; 135 136 int 137 mvpinctrl_match(struct device *parent, void *match, void *aux) 138 { 139 struct fdt_attach_args *faa = aux; 140 int i; 141 142 for (i = 0; i < nitems(mvpinctrl_pins); i++) { 143 if (OF_is_compatible(faa->fa_node, mvpinctrl_pins[i].compat)) 144 return 10; 145 } 146 147 if (OF_is_compatible(faa->fa_node, "marvell,armada3710-nb-pinctrl") || 148 OF_is_compatible(faa->fa_node, "marvell,armada3710-sb-pinctrl")) 149 return 10; 150 151 return 0; 152 } 153 154 void 155 mvpinctrl_attach(struct device *parent, struct device *self, void *aux) 156 { 157 struct mvpinctrl_softc *sc = (struct mvpinctrl_softc *)self; 158 struct fdt_attach_args *faa = aux; 159 int i, node; 160 161 if (faa->fa_nreg > 0) { 162 sc->sc_iot = faa->fa_iot; 163 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 164 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 165 printf(": can't map registers\n"); 166 return; 167 } 168 169 regmap_register(faa->fa_node, sc->sc_iot, sc->sc_ioh, 170 faa->fa_reg[0].size); 171 sc->sc_rm = regmap_bynode(faa->fa_node); 172 } else { 173 /* No registers; use regmap provided by parent. */ 174 sc->sc_rm = regmap_bynode(OF_parent(faa->fa_node)); 175 } 176 177 if (sc->sc_rm == NULL) { 178 printf(": no registers\n"); 179 return; 180 } 181 182 printf("\n"); 183 184 if (OF_is_compatible(faa->fa_node, "marvell,armada3710-nb-pinctrl")) { 185 for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) { 186 if (OF_is_compatible(node, "marvell,armada-3700-xtal-clock")) 187 break; 188 } 189 KASSERT(node != 0); 190 sc->sc_cd_xtal.cd_node = node; 191 sc->sc_cd_xtal.cd_cookie = sc; 192 sc->sc_cd_xtal.cd_get_frequency = a3700_xtal_get_frequency; 193 clock_register(&sc->sc_cd_xtal); 194 } 195 196 if (OF_is_compatible(faa->fa_node, "marvell,armada3710-nb-pinctrl") || 197 OF_is_compatible(faa->fa_node, "marvell,armada3710-sb-pinctrl")) { 198 for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) { 199 if (OF_getproplen(node, "gpio-controller") == 0) 200 break; 201 } 202 KASSERT(node != 0); 203 sc->sc_gc.gc_node = node; 204 sc->sc_gc.gc_cookie = sc; 205 sc->sc_gc.gc_config_pin = mvpinctrl_config_pin; 206 sc->sc_gc.gc_get_pin = mvpinctrl_get_pin; 207 sc->sc_gc.gc_set_pin = mvpinctrl_set_pin; 208 gpio_controller_register(&sc->sc_gc); 209 return; 210 } 211 212 for (i = 0; i < nitems(mvpinctrl_pins); i++) { 213 if (OF_is_compatible(faa->fa_node, mvpinctrl_pins[i].compat)) { 214 sc->sc_pins = mvpinctrl_pins[i].pins; 215 sc->sc_npins = mvpinctrl_pins[i].npins; 216 break; 217 } 218 } 219 220 KASSERT(sc->sc_pins); 221 pinctrl_register(faa->fa_node, mvpinctrl_pinctrl, sc); 222 } 223 224 int 225 mvpinctrl_pinctrl(uint32_t phandle, void *cookie) 226 { 227 struct mvpinctrl_softc *sc = cookie; 228 char *pins, *pin, *func; 229 int i, flen, plen, node; 230 231 node = OF_getnodebyphandle(phandle); 232 if (node == 0) 233 return -1; 234 235 flen = OF_getproplen(node, "marvell,function"); 236 if (flen <= 0) 237 return -1; 238 239 func = malloc(flen, M_TEMP, M_WAITOK); 240 OF_getprop(node, "marvell,function", func, flen); 241 242 plen = OF_getproplen(node, "marvell,pins"); 243 if (plen <= 0) 244 return -1; 245 246 pin = pins = malloc(plen, M_TEMP, M_WAITOK); 247 OF_getprop(node, "marvell,pins", pins, plen); 248 249 while (plen > 0) { 250 for (i = 0; i < sc->sc_npins; i++) { 251 uint32_t off, shift; 252 253 if (strcmp(sc->sc_pins[i].pin, pin)) 254 continue; 255 if (strcmp(sc->sc_pins[i].function, func)) 256 continue; 257 258 off = (sc->sc_pins[i].pid / 8) * sizeof(uint32_t); 259 shift = (sc->sc_pins[i].pid % 8) * 4; 260 261 HWRITE4(sc, off, (HREAD4(sc, off) & ~(0xf << shift)) | 262 (sc->sc_pins[i].value << shift)); 263 break; 264 } 265 266 if (i == sc->sc_npins) 267 printf("%s: unsupported pin %s function %s\n", 268 sc->sc_dev.dv_xname, pin, func); 269 270 plen -= strlen(pin) + 1; 271 pin += strlen(pin) + 1; 272 } 273 274 free(func, M_TEMP, flen); 275 free(pins, M_TEMP, plen); 276 return 0; 277 } 278 279 void 280 mvpinctrl_config_pin(void *cookie, uint32_t *cells, int config) 281 { 282 struct mvpinctrl_softc *sc = cookie; 283 uint32_t pin = cells[0]; 284 285 if (pin >= 32) 286 return; 287 288 if (config & GPIO_CONFIG_OUTPUT) 289 HSET4(sc, GPIO_DIRECTION, (1 << pin)); 290 else 291 HCLR4(sc, GPIO_DIRECTION, (1 << pin)); 292 } 293 294 int 295 mvpinctrl_get_pin(void *cookie, uint32_t *cells) 296 { 297 struct mvpinctrl_softc *sc = cookie; 298 uint32_t pin = cells[0]; 299 uint32_t flags = cells[1]; 300 uint32_t reg; 301 int val; 302 303 if (pin >= 32) 304 return 0; 305 306 reg = HREAD4(sc, GPIO_INPUT); 307 reg &= (1 << pin); 308 val = (reg >> pin) & 1; 309 if (flags & GPIO_ACTIVE_LOW) 310 val = !val; 311 return val; 312 } 313 314 void 315 mvpinctrl_set_pin(void *cookie, uint32_t *cells, int val) 316 { 317 struct mvpinctrl_softc *sc = cookie; 318 uint32_t pin = cells[0]; 319 uint32_t flags = cells[1]; 320 321 if (pin >= 32) 322 return; 323 324 if (flags & GPIO_ACTIVE_LOW) 325 val = !val; 326 if (val) 327 HSET4(sc, GPIO_OUTPUT, (1 << pin)); 328 else 329 HCLR4(sc, GPIO_OUTPUT, (1 << pin)); 330 } 331 332 /* Armada 3700 XTAL block */ 333 334 #define XTAL 0xc 335 #define XTAL_MODE (1U << 31) 336 337 uint32_t 338 a3700_xtal_get_frequency(void *cookie, uint32_t *cells) 339 { 340 struct mvpinctrl_softc *sc = cookie; 341 342 if (regmap_read_4(sc->sc_rm, XTAL) & XTAL_MODE) 343 return 40000000; 344 else 345 return 25000000; 346 } 347