1 /* $OpenBSD: qcgpio_fdt.c,v 1.2 2022/11/08 11:51:34 patrick 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 90 void *qcgpio_fdt_intr_establish(void *, int *, int, struct cpu_info *, 91 int (*)(void *), void *, char *); 92 void qcgpio_fdt_intr_disestablish(void *); 93 void qcgpio_fdt_intr_enable(void *); 94 void qcgpio_fdt_intr_disable(void *); 95 void qcgpio_fdt_intr_barrier(void *); 96 int qcgpio_fdt_pin_intr(struct qcgpio_softc *, int); 97 int qcgpio_fdt_intr(void *); 98 99 int 100 qcgpio_fdt_match(struct device *parent, void *match, void *aux) 101 { 102 struct fdt_attach_args *faa = aux; 103 104 return OF_is_compatible(faa->fa_node, "qcom,sc8280xp-tlmm"); 105 } 106 107 void 108 qcgpio_fdt_attach(struct device *parent, struct device *self, void *aux) 109 { 110 struct fdt_attach_args *faa = aux; 111 struct qcgpio_softc *sc = (struct qcgpio_softc *)self; 112 113 sc->sc_iot = faa->fa_iot; 114 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size, 115 0, &sc->sc_ioh)) { 116 printf(": can't map registers\n"); 117 return; 118 } 119 120 sc->sc_npins = 230; 121 sc->sc_pin_ih = mallocarray(sc->sc_npins, sizeof(*sc->sc_pin_ih), 122 M_DEVBUF, M_WAITOK | M_ZERO); 123 124 sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_BIO, qcgpio_fdt_intr, 125 sc, sc->sc_dev.dv_xname); 126 if (sc->sc_ih == NULL) { 127 printf(": can't establish interrupt\n"); 128 goto unmap; 129 } 130 131 sc->sc_gc.gc_node = faa->fa_node; 132 sc->sc_gc.gc_cookie = sc; 133 sc->sc_gc.gc_config_pin = qcgpio_fdt_config_pin; 134 sc->sc_gc.gc_get_pin = qcgpio_fdt_get_pin; 135 sc->sc_gc.gc_set_pin = qcgpio_fdt_set_pin; 136 gpio_controller_register(&sc->sc_gc); 137 138 sc->sc_ic.ic_node = faa->fa_node; 139 sc->sc_ic.ic_cookie = sc; 140 sc->sc_ic.ic_establish = qcgpio_fdt_intr_establish; 141 sc->sc_ic.ic_disestablish = qcgpio_fdt_intr_disestablish; 142 sc->sc_ic.ic_enable = qcgpio_fdt_intr_enable; 143 sc->sc_ic.ic_disable = qcgpio_fdt_intr_disable; 144 sc->sc_ic.ic_barrier = qcgpio_fdt_intr_barrier; 145 fdt_intr_register(&sc->sc_ic); 146 147 printf("\n"); 148 return; 149 150 unmap: 151 if (sc->sc_ih) 152 fdt_intr_disestablish(sc->sc_ih); 153 free(sc->sc_pin_ih, M_DEVBUF, sc->sc_npins * sizeof(*sc->sc_pin_ih)); 154 bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size); 155 } 156 157 void 158 qcgpio_fdt_config_pin(void *cookie, uint32_t *cells, int config) 159 { 160 struct qcgpio_softc *sc = cookie; 161 uint32_t pin = cells[0]; 162 163 if (pin >= sc->sc_npins) 164 return; 165 166 if (config & GPIO_CONFIG_OUTPUT) 167 HSET4(sc, TLMM_GPIO_CFG(pin), TLMM_GPIO_CFG_OUT_EN); 168 else 169 HCLR4(sc, TLMM_GPIO_CFG(pin), TLMM_GPIO_CFG_OUT_EN); 170 } 171 172 int 173 qcgpio_fdt_get_pin(void *cookie, uint32_t *cells) 174 { 175 struct qcgpio_softc *sc = cookie; 176 uint32_t pin = cells[0]; 177 uint32_t flags = cells[1]; 178 uint32_t reg; 179 int val; 180 181 if (pin >= sc->sc_npins) 182 return 0; 183 184 reg = HREAD4(sc, TLMM_GPIO_IN_OUT(pin)); 185 val = !!(reg & TLMM_GPIO_IN_OUT_GPIO_IN); 186 if (flags & GPIO_ACTIVE_LOW) 187 val = !val; 188 return val; 189 } 190 191 void 192 qcgpio_fdt_set_pin(void *cookie, uint32_t *cells, int val) 193 { 194 struct qcgpio_softc *sc = cookie; 195 uint32_t pin = cells[0]; 196 uint32_t flags = cells[1]; 197 198 if (pin >= sc->sc_npins) 199 return; 200 201 if (flags & GPIO_ACTIVE_LOW) 202 val = !val; 203 204 if (val) { 205 HSET4(sc, TLMM_GPIO_IN_OUT(pin), 206 TLMM_GPIO_IN_OUT_GPIO_OUT); 207 } else { 208 HCLR4(sc, TLMM_GPIO_IN_OUT(pin), 209 TLMM_GPIO_IN_OUT_GPIO_OUT); 210 } 211 } 212 213 void * 214 qcgpio_fdt_intr_establish(void *cookie, int *cells, int ipl, 215 struct cpu_info *ci, int (*func)(void *), void *arg, char *name) 216 { 217 struct qcgpio_softc *sc = cookie; 218 uint32_t reg; 219 int pin = cells[0]; 220 int level = cells[1]; 221 222 if (pin < 0 || pin >= sc->sc_npins) 223 return NULL; 224 225 sc->sc_pin_ih[pin].ih_func = func; 226 sc->sc_pin_ih[pin].ih_arg = arg; 227 sc->sc_pin_ih[pin].ih_pin = pin; 228 sc->sc_pin_ih[pin].ih_sc = sc; 229 230 reg = HREAD4(sc, TLMM_GPIO_INTR_CFG(pin)); 231 reg &= ~TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_MASK; 232 reg &= ~TLMM_GPIO_INTR_CFG_INTR_POL_CTL; 233 switch (level) { 234 case 1: 235 reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_EDGE_POS | 236 TLMM_GPIO_INTR_CFG_INTR_POL_CTL; 237 break; 238 case 2: 239 reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_EDGE_NEG | 240 TLMM_GPIO_INTR_CFG_INTR_POL_CTL; 241 break; 242 case 3: 243 reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_EDGE_BOTH; 244 break; 245 case 4: 246 reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_LEVEL | 247 TLMM_GPIO_INTR_CFG_INTR_POL_CTL; 248 break; 249 case 8: 250 reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_LEVEL; 251 break; 252 default: 253 printf("%s: unsupported interrupt mode/polarity\n", 254 sc->sc_dev.dv_xname); 255 break; 256 } 257 reg &= ~TLMM_GPIO_INTR_CFG_TARGET_PROC_MASK; 258 reg |= TLMM_GPIO_INTR_CFG_TARGET_PROC_RPM; 259 reg |= TLMM_GPIO_INTR_CFG_INTR_RAW_STATUS_EN; 260 reg |= TLMM_GPIO_INTR_CFG_INTR_ENABLE; 261 HWRITE4(sc, TLMM_GPIO_INTR_CFG(pin), reg); 262 263 return &sc->sc_pin_ih[pin]; 264 } 265 266 void 267 qcgpio_fdt_intr_disestablish(void *cookie) 268 { 269 struct qcgpio_intrhand *ih = cookie; 270 271 qcgpio_fdt_intr_disable(cookie); 272 ih->ih_func = NULL; 273 } 274 275 void 276 qcgpio_fdt_intr_enable(void *cookie) 277 { 278 struct qcgpio_intrhand *ih = cookie; 279 struct qcgpio_softc *sc = ih->ih_sc; 280 int pin = ih->ih_pin; 281 282 if (pin < 0 || pin >= sc->sc_npins) 283 return; 284 285 HSET4(sc, TLMM_GPIO_INTR_CFG(pin), 286 TLMM_GPIO_INTR_CFG_INTR_ENABLE); 287 } 288 289 void 290 qcgpio_fdt_intr_disable(void *cookie) 291 { 292 struct qcgpio_intrhand *ih = cookie; 293 struct qcgpio_softc *sc = ih->ih_sc; 294 int pin = ih->ih_pin; 295 296 if (pin < 0 || pin >= sc->sc_npins) 297 return; 298 299 HCLR4(sc, TLMM_GPIO_INTR_CFG(pin), 300 TLMM_GPIO_INTR_CFG_INTR_ENABLE); 301 } 302 303 void 304 qcgpio_fdt_intr_barrier(void *cookie) 305 { 306 struct qcgpio_intrhand *ih = cookie; 307 struct qcgpio_softc *sc = ih->ih_sc; 308 309 intr_barrier(sc->sc_ih); 310 } 311 312 int 313 qcgpio_fdt_intr(void *arg) 314 { 315 struct qcgpio_softc *sc = arg; 316 int pin, handled = 0; 317 uint32_t stat; 318 319 for (pin = 0; pin < sc->sc_npins; pin++) { 320 if (sc->sc_pin_ih[pin].ih_func == NULL) 321 continue; 322 323 stat = HREAD4(sc, TLMM_GPIO_INTR_STATUS(pin)); 324 if (stat & TLMM_GPIO_INTR_STATUS_INTR_STATUS) { 325 sc->sc_pin_ih[pin].ih_func(sc->sc_pin_ih[pin].ih_arg); 326 handled = 1; 327 } 328 HWRITE4(sc, TLMM_GPIO_INTR_STATUS(pin), 329 stat & ~TLMM_GPIO_INTR_STATUS_INTR_STATUS); 330 } 331 332 return handled; 333 } 334