1 /* $OpenBSD: qcgpio.c,v 1.8 2022/12/18 10:00:53 mglocker 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 <dev/acpi/acpireg.h> 23 #include <dev/acpi/acpivar.h> 24 #include <dev/acpi/acpidev.h> 25 #include <dev/acpi/amltypes.h> 26 #include <dev/acpi/dsdt.h> 27 28 /* Registers. */ 29 #define TLMM_GPIO_IN_OUT(pin) (0x0004 + 0x1000 * (pin)) 30 #define TLMM_GPIO_IN_OUT_GPIO_IN (1 << 0) 31 #define TLMM_GPIO_IN_OUT_GPIO_OUT (1 << 1) 32 #define TLMM_GPIO_INTR_CFG(pin) (0x0008 + 0x1000 * (pin)) 33 #define TLMM_GPIO_INTR_CFG_TARGET_PROC_MASK (0x7 << 5) 34 #define TLMM_GPIO_INTR_CFG_TARGET_PROC_RPM (0x3 << 5) 35 #define TLMM_GPIO_INTR_CFG_INTR_RAW_STATUS_EN (1 << 4) 36 #define TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_MASK (0x3 << 2) 37 #define TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_LEVEL (0x0 << 2) 38 #define TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_EDGE_POS (0x1 << 2) 39 #define TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_EDGE_NEG (0x2 << 2) 40 #define TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_EDGE_BOTH (0x3 << 2) 41 #define TLMM_GPIO_INTR_CFG_INTR_POL_CTL (1 << 1) 42 #define TLMM_GPIO_INTR_CFG_INTR_ENABLE (1 << 0) 43 #define TLMM_GPIO_INTR_STATUS(pin) (0x000c + 0x1000 * (pin)) 44 #define TLMM_GPIO_INTR_STATUS_INTR_STATUS (1 << 0) 45 46 /* SC7180 has multiple tiles */ 47 #define QCGPIO_SC7180_WEST 0x00100000 48 #define QCGPIO_SC7180_NORTH 0x00500000 49 #define QCGPIO_SC7180_SOUTH 0x00900000 50 51 #define HREAD4(sc, reg) \ 52 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 53 #define HWRITE4(sc, reg, val) \ 54 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 55 #define HSET4(sc, reg, bits) \ 56 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) 57 #define HCLR4(sc, reg, bits) \ 58 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) 59 60 struct qcgpio_intrhand { 61 int (*ih_func)(void *); 62 void *ih_arg; 63 }; 64 65 struct qcgpio_softc { 66 struct device sc_dev; 67 struct acpi_softc *sc_acpi; 68 struct aml_node *sc_node; 69 70 bus_space_tag_t sc_iot; 71 bus_space_handle_t sc_ioh; 72 73 void *sc_ih; 74 75 uint32_t sc_npins; 76 int (*sc_pin_map)(int, bus_size_t *); 77 struct qcgpio_intrhand *sc_pin_ih; 78 79 struct acpi_gpio sc_gpio; 80 }; 81 82 int qcgpio_acpi_match(struct device *, void *, void *); 83 void qcgpio_acpi_attach(struct device *, struct device *, void *); 84 85 const struct cfattach qcgpio_acpi_ca = { 86 sizeof(struct qcgpio_softc), qcgpio_acpi_match, qcgpio_acpi_attach 87 }; 88 89 struct cfdriver qcgpio_cd = { 90 NULL, "qcgpio", DV_DULL 91 }; 92 93 const char *qcgpio_hids[] = { 94 "QCOM060C", 95 "QCOM080D", 96 NULL 97 }; 98 99 int qcgpio_sc7180_pin_map(int, bus_size_t *); 100 int qcgpio_sc8280xp_pin_map(int, bus_size_t *); 101 102 int qcgpio_read_pin(void *, int); 103 void qcgpio_write_pin(void *, int, int); 104 void qcgpio_intr_establish(void *, int, int, int (*)(void *), void *); 105 void qcgpio_intr_enable(void *, int); 106 void qcgpio_intr_disable(void *, int); 107 int qcgpio_pin_intr(struct qcgpio_softc *, int); 108 int qcgpio_intr(void *); 109 110 int 111 qcgpio_acpi_match(struct device *parent, void *match, void *aux) 112 { 113 struct acpi_attach_args *aaa = aux; 114 struct cfdata *cf = match; 115 116 if (aaa->aaa_naddr < 1 || aaa->aaa_nirq < 1) 117 return 0; 118 return acpi_matchhids(aaa, qcgpio_hids, cf->cf_driver->cd_name); 119 } 120 121 void 122 qcgpio_acpi_attach(struct device *parent, struct device *self, void *aux) 123 { 124 struct acpi_attach_args *aaa = aux; 125 struct qcgpio_softc *sc = (struct qcgpio_softc *)self; 126 127 sc->sc_acpi = (struct acpi_softc *)parent; 128 sc->sc_node = aaa->aaa_node; 129 printf(" %s", sc->sc_node->name); 130 131 printf(" addr 0x%llx/0x%llx", aaa->aaa_addr[0], aaa->aaa_size[0]); 132 133 sc->sc_iot = aaa->aaa_bst[0]; 134 if (bus_space_map(sc->sc_iot, aaa->aaa_addr[0], aaa->aaa_size[0], 135 0, &sc->sc_ioh)) { 136 printf(": can't map registers\n"); 137 return; 138 } 139 140 if (strcmp(aaa->aaa_dev, "QCOM080D") == 0) { 141 sc->sc_npins = 119; 142 sc->sc_pin_map = qcgpio_sc7180_pin_map; 143 } else if (strcmp(aaa->aaa_dev, "QCOM060C") == 0) { 144 sc->sc_npins = 228; 145 sc->sc_pin_map = qcgpio_sc8280xp_pin_map; 146 } 147 KASSERT(sc->sc_npins != 0); 148 149 sc->sc_pin_ih = mallocarray(sc->sc_npins, sizeof(*sc->sc_pin_ih), 150 M_DEVBUF, M_WAITOK | M_ZERO); 151 152 printf(" irq %d", aaa->aaa_irq[0]); 153 154 sc->sc_ih = acpi_intr_establish(aaa->aaa_irq[0], 155 aaa->aaa_irq_flags[0], IPL_BIO, qcgpio_intr, 156 sc, sc->sc_dev.dv_xname); 157 if (sc->sc_ih == NULL) { 158 printf(": can't establish interrupt\n"); 159 goto unmap; 160 } 161 162 sc->sc_gpio.cookie = sc; 163 sc->sc_gpio.read_pin = qcgpio_read_pin; 164 sc->sc_gpio.write_pin = qcgpio_write_pin; 165 sc->sc_gpio.intr_establish = qcgpio_intr_establish; 166 sc->sc_gpio.intr_enable = qcgpio_intr_enable; 167 sc->sc_gpio.intr_disable = qcgpio_intr_disable; 168 sc->sc_node->gpio = &sc->sc_gpio; 169 170 printf("\n"); 171 172 acpi_register_gpio(sc->sc_acpi, sc->sc_node); 173 return; 174 175 unmap: 176 if (sc->sc_ih) 177 acpi_intr_disestablish(sc->sc_ih); 178 free(sc->sc_pin_ih, M_DEVBUF, sc->sc_npins * sizeof(*sc->sc_pin_ih)); 179 bus_space_unmap(sc->sc_iot, sc->sc_ioh, aaa->aaa_size[0]); 180 } 181 182 int 183 qcgpio_sc7180_pin_map(int pin, bus_size_t *off) 184 { 185 switch (pin) { 186 case 30: 187 *off = QCGPIO_SC7180_SOUTH; 188 return 30; 189 #if 0 190 /* XXX: Disable until we can fix the interrupt storm. */ 191 case 32: 192 case 0x140: 193 *off = QCGPIO_SC7180_NORTH; 194 return 32; 195 #endif 196 case 33: 197 case 0x180: 198 *off = QCGPIO_SC7180_NORTH; 199 return 33; 200 case 94: 201 case 0x1c0: 202 *off = QCGPIO_SC7180_SOUTH; 203 return 94; 204 default: 205 return -1; 206 } 207 } 208 209 int 210 qcgpio_sc8280xp_pin_map(int pin, bus_size_t *off) 211 { 212 switch (pin) { 213 case 107: 214 case 175: 215 return pin; 216 case 0x2c0: 217 return 107; 218 case 0x340: 219 return 104; 220 case 0x380: 221 return 182; 222 default: 223 return -1; 224 } 225 } 226 227 int 228 qcgpio_read_pin(void *cookie, int pin) 229 { 230 struct qcgpio_softc *sc = cookie; 231 bus_size_t off = 0; 232 uint32_t reg; 233 234 pin = sc->sc_pin_map(pin, &off); 235 if (pin < 0 || pin >= sc->sc_npins) 236 return 0; 237 238 reg = HREAD4(sc, off + TLMM_GPIO_IN_OUT(pin)); 239 return !!(reg & TLMM_GPIO_IN_OUT_GPIO_IN); 240 } 241 242 void 243 qcgpio_write_pin(void *cookie, int pin, int val) 244 { 245 struct qcgpio_softc *sc = cookie; 246 bus_size_t off = 0; 247 248 pin = sc->sc_pin_map(pin, &off); 249 if (pin < 0 || pin >= sc->sc_npins) 250 return; 251 252 if (val) { 253 HSET4(sc, off + TLMM_GPIO_IN_OUT(pin), 254 TLMM_GPIO_IN_OUT_GPIO_OUT); 255 } else { 256 HCLR4(sc, off + TLMM_GPIO_IN_OUT(pin), 257 TLMM_GPIO_IN_OUT_GPIO_OUT); 258 } 259 } 260 261 void 262 qcgpio_intr_establish(void *cookie, int pin, int flags, 263 int (*func)(void *), void *arg) 264 { 265 struct qcgpio_softc *sc = cookie; 266 bus_size_t off = 0; 267 uint32_t reg; 268 269 pin = sc->sc_pin_map(pin, &off); 270 if (pin < 0 || pin >= sc->sc_npins) 271 return; 272 273 sc->sc_pin_ih[pin].ih_func = func; 274 sc->sc_pin_ih[pin].ih_arg = arg; 275 276 reg = HREAD4(sc, off + TLMM_GPIO_INTR_CFG(pin)); 277 reg &= ~TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_MASK; 278 reg &= ~TLMM_GPIO_INTR_CFG_INTR_POL_CTL; 279 switch (flags & (LR_GPIO_MODE | LR_GPIO_POLARITY)) { 280 case LR_GPIO_LEVEL | LR_GPIO_ACTLO: 281 reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_LEVEL; 282 break; 283 case LR_GPIO_LEVEL | LR_GPIO_ACTHI: 284 reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_LEVEL | 285 TLMM_GPIO_INTR_CFG_INTR_POL_CTL; 286 break; 287 case LR_GPIO_EDGE | LR_GPIO_ACTLO: 288 reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_EDGE_NEG | 289 TLMM_GPIO_INTR_CFG_INTR_POL_CTL; 290 break; 291 case LR_GPIO_EDGE | LR_GPIO_ACTHI: 292 reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_EDGE_POS | 293 TLMM_GPIO_INTR_CFG_INTR_POL_CTL; 294 break; 295 case LR_GPIO_EDGE | LR_GPIO_ACTBOTH: 296 reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_EDGE_BOTH; 297 break; 298 default: 299 printf("%s: unsupported interrupt mode/polarity\n", 300 sc->sc_dev.dv_xname); 301 break; 302 } 303 reg &= ~TLMM_GPIO_INTR_CFG_TARGET_PROC_MASK; 304 reg |= TLMM_GPIO_INTR_CFG_TARGET_PROC_RPM; 305 reg |= TLMM_GPIO_INTR_CFG_INTR_RAW_STATUS_EN; 306 reg |= TLMM_GPIO_INTR_CFG_INTR_ENABLE; 307 HWRITE4(sc, off + TLMM_GPIO_INTR_CFG(pin), reg); 308 } 309 310 void 311 qcgpio_intr_enable(void *cookie, int pin) 312 { 313 struct qcgpio_softc *sc = cookie; 314 bus_size_t off = 0; 315 316 pin = sc->sc_pin_map(pin, &off); 317 if (pin < 0 || pin >= sc->sc_npins) 318 return; 319 320 HSET4(sc, off + TLMM_GPIO_INTR_CFG(pin), 321 TLMM_GPIO_INTR_CFG_INTR_ENABLE); 322 } 323 324 void 325 qcgpio_intr_disable(void *cookie, int pin) 326 { 327 struct qcgpio_softc *sc = cookie; 328 bus_size_t off = 0; 329 330 pin = sc->sc_pin_map(pin, &off); 331 if (pin < 0 || pin >= sc->sc_npins) 332 return; 333 334 HCLR4(sc, off + TLMM_GPIO_INTR_CFG(pin), 335 TLMM_GPIO_INTR_CFG_INTR_ENABLE); 336 } 337 338 int 339 qcgpio_intr(void *arg) 340 { 341 struct qcgpio_softc *sc = arg; 342 int pin, handled = 0; 343 bus_size_t off = 0; 344 uint32_t stat; 345 346 for (pin = 0; pin < sc->sc_npins; pin++) { 347 if (sc->sc_pin_ih[pin].ih_func == NULL) 348 continue; 349 350 sc->sc_pin_map(pin, &off); 351 352 stat = HREAD4(sc, off + TLMM_GPIO_INTR_STATUS(pin)); 353 if (stat & TLMM_GPIO_INTR_STATUS_INTR_STATUS) { 354 sc->sc_pin_ih[pin].ih_func(sc->sc_pin_ih[pin].ih_arg); 355 handled = 1; 356 } 357 HWRITE4(sc, off + TLMM_GPIO_INTR_STATUS(pin), 358 stat & ~TLMM_GPIO_INTR_STATUS_INTR_STATUS); 359 } 360 361 return handled; 362 } 363