1 /* $OpenBSD: qcgpio_fdt.c,v 1.5 2025/01/09 21:52:25 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2022 Mark Kettenis <kettenis@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/malloc.h> 20 #include <sys/systm.h> 21 22 #include <machine/fdt.h> 23 24 #include <dev/ofw/openfirm.h> 25 #include <dev/ofw/ofw_gpio.h> 26 #include <dev/ofw/fdt.h> 27 28 /* Registers. */ 29 #define TLMM_GPIO_CFG(pin) (0x0000 + 0x1000 * (pin)) 30 #define TLMM_GPIO_CFG_OUT_EN (1 << 9) 31 #define TLMM_GPIO_IN_OUT(pin) (0x0004 + 0x1000 * (pin)) 32 #define TLMM_GPIO_IN_OUT_GPIO_IN (1 << 0) 33 #define TLMM_GPIO_IN_OUT_GPIO_OUT (1 << 1) 34 #define TLMM_GPIO_INTR_CFG(pin) (0x0008 + 0x1000 * (pin)) 35 #define TLMM_GPIO_INTR_CFG_TARGET_PROC_MASK (0x7 << 5) 36 #define TLMM_GPIO_INTR_CFG_TARGET_PROC_RPM (0x3 << 5) 37 #define TLMM_GPIO_INTR_CFG_INTR_RAW_STATUS_EN (1 << 4) 38 #define TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_MASK (0x3 << 2) 39 #define TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_LEVEL (0x0 << 2) 40 #define TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_EDGE_POS (0x1 << 2) 41 #define TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_EDGE_NEG (0x2 << 2) 42 #define TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_EDGE_BOTH (0x3 << 2) 43 #define TLMM_GPIO_INTR_CFG_INTR_POL_CTL (1 << 1) 44 #define TLMM_GPIO_INTR_CFG_INTR_ENABLE (1 << 0) 45 #define TLMM_GPIO_INTR_STATUS(pin) (0x000c + 0x1000 * (pin)) 46 #define TLMM_GPIO_INTR_STATUS_INTR_STATUS (1 << 0) 47 48 #define HREAD4(sc, reg) \ 49 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 50 #define HWRITE4(sc, reg, val) \ 51 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 52 #define HSET4(sc, reg, bits) \ 53 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) 54 #define HCLR4(sc, reg, bits) \ 55 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) 56 57 struct qcgpio_intrhand { 58 int (*ih_func)(void *); 59 void *ih_arg; 60 void *ih_sc; 61 int ih_pin; 62 }; 63 64 struct qcgpio_softc { 65 struct device sc_dev; 66 67 bus_space_tag_t sc_iot; 68 bus_space_handle_t sc_ioh; 69 70 void *sc_ih; 71 72 uint32_t sc_npins; 73 struct qcgpio_intrhand *sc_pin_ih; 74 75 struct gpio_controller sc_gc; 76 struct interrupt_controller sc_ic; 77 }; 78 79 int qcgpio_fdt_match(struct device *, void *, void *); 80 void qcgpio_fdt_attach(struct device *, struct device *, void *); 81 82 const struct cfattach qcgpio_fdt_ca = { 83 sizeof(struct qcgpio_softc), qcgpio_fdt_match, qcgpio_fdt_attach 84 }; 85 86 void qcgpio_fdt_config_pin(void *, uint32_t *, int); 87 int qcgpio_fdt_get_pin(void *, uint32_t *); 88 void qcgpio_fdt_set_pin(void *, uint32_t *, int); 89 void *qcgpio_fdt_intr_establish_pin(void *, uint32_t *, int, 90 struct cpu_info *, int (*)(void *), void *, char *); 91 92 void *qcgpio_fdt_intr_establish(void *, int *, int, struct cpu_info *, 93 int (*)(void *), void *, char *); 94 void qcgpio_fdt_intr_disestablish(void *); 95 void qcgpio_fdt_intr_enable(void *); 96 void qcgpio_fdt_intr_disable(void *); 97 void qcgpio_fdt_intr_barrier(void *); 98 int qcgpio_fdt_intr(void *); 99 100 int 101 qcgpio_fdt_match(struct device *parent, void *match, void *aux) 102 { 103 struct fdt_attach_args *faa = aux; 104 105 return (OF_is_compatible(faa->fa_node, "qcom,sc8280xp-tlmm") || 106 OF_is_compatible(faa->fa_node, "qcom,x1e80100-tlmm")); 107 } 108 109 void 110 qcgpio_fdt_attach(struct device *parent, struct device *self, void *aux) 111 { 112 struct fdt_attach_args *faa = aux; 113 struct qcgpio_softc *sc = (struct qcgpio_softc *)self; 114 115 sc->sc_iot = faa->fa_iot; 116 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size, 117 0, &sc->sc_ioh)) { 118 printf(": can't map registers\n"); 119 return; 120 } 121 122 if (OF_is_compatible(faa->fa_node, "qcom,sc8280xp-tlmm")) 123 sc->sc_npins = 230; 124 else 125 sc->sc_npins = 239; 126 sc->sc_pin_ih = mallocarray(sc->sc_npins, sizeof(*sc->sc_pin_ih), 127 M_DEVBUF, M_WAITOK | M_ZERO); 128 129 sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_BIO, qcgpio_fdt_intr, 130 sc, sc->sc_dev.dv_xname); 131 if (sc->sc_ih == NULL) { 132 printf(": can't establish interrupt\n"); 133 goto unmap; 134 } 135 136 sc->sc_gc.gc_node = faa->fa_node; 137 sc->sc_gc.gc_cookie = sc; 138 sc->sc_gc.gc_config_pin = qcgpio_fdt_config_pin; 139 sc->sc_gc.gc_get_pin = qcgpio_fdt_get_pin; 140 sc->sc_gc.gc_set_pin = qcgpio_fdt_set_pin; 141 sc->sc_gc.gc_intr_establish = qcgpio_fdt_intr_establish_pin; 142 gpio_controller_register(&sc->sc_gc); 143 144 sc->sc_ic.ic_node = faa->fa_node; 145 sc->sc_ic.ic_cookie = sc; 146 sc->sc_ic.ic_establish = qcgpio_fdt_intr_establish; 147 sc->sc_ic.ic_disestablish = qcgpio_fdt_intr_disestablish; 148 sc->sc_ic.ic_enable = qcgpio_fdt_intr_enable; 149 sc->sc_ic.ic_disable = qcgpio_fdt_intr_disable; 150 sc->sc_ic.ic_barrier = qcgpio_fdt_intr_barrier; 151 fdt_intr_register(&sc->sc_ic); 152 153 printf("\n"); 154 return; 155 156 unmap: 157 if (sc->sc_ih) 158 fdt_intr_disestablish(sc->sc_ih); 159 free(sc->sc_pin_ih, M_DEVBUF, sc->sc_npins * sizeof(*sc->sc_pin_ih)); 160 bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size); 161 } 162 163 void 164 qcgpio_fdt_config_pin(void *cookie, uint32_t *cells, int config) 165 { 166 struct qcgpio_softc *sc = cookie; 167 uint32_t pin = cells[0]; 168 169 if (pin >= sc->sc_npins) 170 return; 171 172 if (config & GPIO_CONFIG_OUTPUT) 173 HSET4(sc, TLMM_GPIO_CFG(pin), TLMM_GPIO_CFG_OUT_EN); 174 else 175 HCLR4(sc, TLMM_GPIO_CFG(pin), TLMM_GPIO_CFG_OUT_EN); 176 } 177 178 int 179 qcgpio_fdt_get_pin(void *cookie, uint32_t *cells) 180 { 181 struct qcgpio_softc *sc = cookie; 182 uint32_t pin = cells[0]; 183 uint32_t flags = cells[1]; 184 uint32_t reg; 185 int val; 186 187 if (pin >= sc->sc_npins) 188 return 0; 189 190 reg = HREAD4(sc, TLMM_GPIO_IN_OUT(pin)); 191 val = !!(reg & TLMM_GPIO_IN_OUT_GPIO_IN); 192 if (flags & GPIO_ACTIVE_LOW) 193 val = !val; 194 return val; 195 } 196 197 void 198 qcgpio_fdt_set_pin(void *cookie, uint32_t *cells, int val) 199 { 200 struct qcgpio_softc *sc = cookie; 201 uint32_t pin = cells[0]; 202 uint32_t flags = cells[1]; 203 204 if (pin >= sc->sc_npins) 205 return; 206 207 if (flags & GPIO_ACTIVE_LOW) 208 val = !val; 209 210 if (val) { 211 HSET4(sc, TLMM_GPIO_IN_OUT(pin), 212 TLMM_GPIO_IN_OUT_GPIO_OUT); 213 } else { 214 HCLR4(sc, TLMM_GPIO_IN_OUT(pin), 215 TLMM_GPIO_IN_OUT_GPIO_OUT); 216 } 217 } 218 219 void * 220 qcgpio_fdt_intr_establish_pin(void *cookie, uint32_t *cells, int ipl, 221 struct cpu_info *ci, int (*func)(void *), void *arg, char *name) 222 { 223 struct qcgpio_softc *sc = cookie; 224 uint32_t icells[2]; 225 226 icells[0] = cells[0]; 227 icells[1] = 3; /* both edges */ 228 229 return qcgpio_fdt_intr_establish(sc, icells, ipl, ci, func, arg, name); 230 } 231 232 void * 233 qcgpio_fdt_intr_establish(void *cookie, int *cells, int ipl, 234 struct cpu_info *ci, int (*func)(void *), void *arg, char *name) 235 { 236 struct qcgpio_softc *sc = cookie; 237 uint32_t reg; 238 int pin = cells[0]; 239 int level = cells[1]; 240 241 if (pin < 0 || pin >= sc->sc_npins) 242 return NULL; 243 244 sc->sc_pin_ih[pin].ih_func = func; 245 sc->sc_pin_ih[pin].ih_arg = arg; 246 sc->sc_pin_ih[pin].ih_pin = pin; 247 sc->sc_pin_ih[pin].ih_sc = sc; 248 249 reg = HREAD4(sc, TLMM_GPIO_INTR_CFG(pin)); 250 reg &= ~TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_MASK; 251 reg &= ~TLMM_GPIO_INTR_CFG_INTR_POL_CTL; 252 switch (level) { 253 case 1: 254 reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_EDGE_POS | 255 TLMM_GPIO_INTR_CFG_INTR_POL_CTL; 256 break; 257 case 2: 258 reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_EDGE_NEG | 259 TLMM_GPIO_INTR_CFG_INTR_POL_CTL; 260 break; 261 case 3: 262 reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_EDGE_BOTH; 263 break; 264 case 4: 265 reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_LEVEL | 266 TLMM_GPIO_INTR_CFG_INTR_POL_CTL; 267 break; 268 case 8: 269 reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_LEVEL; 270 break; 271 default: 272 printf("%s: unsupported interrupt mode/polarity\n", 273 sc->sc_dev.dv_xname); 274 break; 275 } 276 reg &= ~TLMM_GPIO_INTR_CFG_TARGET_PROC_MASK; 277 reg |= TLMM_GPIO_INTR_CFG_TARGET_PROC_RPM; 278 reg |= TLMM_GPIO_INTR_CFG_INTR_RAW_STATUS_EN; 279 reg |= TLMM_GPIO_INTR_CFG_INTR_ENABLE; 280 HWRITE4(sc, TLMM_GPIO_INTR_CFG(pin), reg); 281 282 return &sc->sc_pin_ih[pin]; 283 } 284 285 void 286 qcgpio_fdt_intr_disestablish(void *cookie) 287 { 288 struct qcgpio_intrhand *ih = cookie; 289 290 qcgpio_fdt_intr_disable(cookie); 291 ih->ih_func = NULL; 292 } 293 294 void 295 qcgpio_fdt_intr_enable(void *cookie) 296 { 297 struct qcgpio_intrhand *ih = cookie; 298 struct qcgpio_softc *sc = ih->ih_sc; 299 int pin = ih->ih_pin; 300 301 if (pin < 0 || pin >= sc->sc_npins) 302 return; 303 304 HSET4(sc, TLMM_GPIO_INTR_CFG(pin), 305 TLMM_GPIO_INTR_CFG_INTR_ENABLE); 306 } 307 308 void 309 qcgpio_fdt_intr_disable(void *cookie) 310 { 311 struct qcgpio_intrhand *ih = cookie; 312 struct qcgpio_softc *sc = ih->ih_sc; 313 int pin = ih->ih_pin; 314 315 if (pin < 0 || pin >= sc->sc_npins) 316 return; 317 318 HCLR4(sc, TLMM_GPIO_INTR_CFG(pin), 319 TLMM_GPIO_INTR_CFG_INTR_ENABLE); 320 } 321 322 void 323 qcgpio_fdt_intr_barrier(void *cookie) 324 { 325 struct qcgpio_intrhand *ih = cookie; 326 struct qcgpio_softc *sc = ih->ih_sc; 327 328 intr_barrier(sc->sc_ih); 329 } 330 331 int 332 qcgpio_fdt_intr(void *arg) 333 { 334 struct qcgpio_softc *sc = arg; 335 int pin, handled = 0; 336 uint32_t stat; 337 338 for (pin = 0; pin < sc->sc_npins; pin++) { 339 if (sc->sc_pin_ih[pin].ih_func == NULL) 340 continue; 341 342 stat = HREAD4(sc, TLMM_GPIO_INTR_STATUS(pin)); 343 if (stat & TLMM_GPIO_INTR_STATUS_INTR_STATUS) { 344 sc->sc_pin_ih[pin].ih_func(sc->sc_pin_ih[pin].ih_arg); 345 handled = 1; 346 } 347 HWRITE4(sc, TLMM_GPIO_INTR_STATUS(pin), 348 stat & ~TLMM_GPIO_INTR_STATUS_INTR_STATUS); 349 } 350 351 return handled; 352 } 353