1 /* $OpenBSD: qcspmi.c,v 1.3 2022/12/21 23:26:54 patrick Exp $ */ 2 /* 3 * Copyright (c) 2022 Patrick Wildt <patrick@blueri.se> 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/bus.h> 23 #include <machine/fdt.h> 24 25 #include <dev/fdt/spmivar.h> 26 27 #include <dev/ofw/openfirm.h> 28 #include <dev/ofw/ofw_clock.h> 29 #include <dev/ofw/ofw_power.h> 30 #include <dev/ofw/fdt.h> 31 32 /* Core registers. */ 33 #define SPMI_VERSION 0x00 34 #define SPMI_VERSION_V2_MIN 0x20010000 35 #define SPMI_VERSION_V3_MIN 0x30000000 36 #define SPMI_VERSION_V5_MIN 0x50000000 37 #define SPMI_ARB_APID_MAP(x) (0x900 + (x) * 0x4) 38 #define SPMI_ARB_APID_MAP_PPID_MASK 0xfff 39 #define SPMI_ARB_APID_MAP_PPID_SHIFT 8 40 #define SPMI_ARB_APID_MAP_IRQ_OWNER (1 << 14) 41 42 /* Channel registers. */ 43 #define SPMI_CHAN_OFF(x) (0x10000 * (x)) 44 #define SPMI_OBSV_OFF(x, y) (0x10000 * (x) + 0x80 * (y)) 45 #define SPMI_COMMAND 0x00 46 #define SPMI_COMMAND_OP_EXT_WRITEL (0 << 27) 47 #define SPMI_COMMAND_OP_EXT_READL (1 << 27) 48 #define SPMI_COMMAND_OP_EXT_WRITE (2 << 27) 49 #define SPMI_COMMAND_OP_RESET (3 << 27) 50 #define SPMI_COMMAND_OP_SLEEP (4 << 27) 51 #define SPMI_COMMAND_OP_SHUTDOWN (5 << 27) 52 #define SPMI_COMMAND_OP_WAKEUP (6 << 27) 53 #define SPMI_COMMAND_OP_AUTHENTICATE (7 << 27) 54 #define SPMI_COMMAND_OP_MSTR_READ (8 << 27) 55 #define SPMI_COMMAND_OP_MSTR_WRITE (9 << 27) 56 #define SPMI_COMMAND_OP_EXT_READ (13 << 27) 57 #define SPMI_COMMAND_OP_WRITE (14 << 27) 58 #define SPMI_COMMAND_OP_READ (15 << 27) 59 #define SPMI_COMMAND_OP_ZERO_WRITE (16 << 27) 60 #define SPMI_COMMAND_ADDR(x) (((x) & 0xff) << 4) 61 #define SPMI_COMMAND_LEN(x) (((x) & 0x7) << 0) 62 #define SPMI_CONFIG 0x04 63 #define SPMI_STATUS 0x08 64 #define SPMI_STATUS_DONE (1 << 0) 65 #define SPMI_STATUS_FAILURE (1 << 1) 66 #define SPMI_STATUS_DENIED (1 << 2) 67 #define SPMI_STATUS_DROPPED (1 << 3) 68 #define SPMI_WDATA0 0x10 69 #define SPMI_WDATA1 0x14 70 #define SPMI_RDATA0 0x18 71 #define SPMI_RDATA1 0x1c 72 #define SPMI_ACC_ENABLE 0x100 73 #define SPMI_ACC_ENABLE_BIT (1 << 0) 74 #define SPMI_IRQ_STATUS 0x104 75 #define SPMI_IRQ_CLEAR 0x108 76 77 /* Intr registers */ 78 #define SPMI_OWNER_ACC_STATUS(x, y) (0x10000 * (x) + 0x4 * (y)) 79 80 /* Config registers */ 81 #define SPMI_OWNERSHIP_TABLE(x) (0x700 + (x) * 0x4) 82 #define SPMI_OWNERSHIP_TABLE_OWNER(x) ((x) & 0x7) 83 84 /* Misc */ 85 #define SPMI_MAX_PERIPH 512 86 #define SPMI_MAX_PPID 4096 87 #define SPMI_PPID_TO_APID_VALID (1U << 15) 88 #define SPMI_PPID_TO_APID_MASK (0x7fff) 89 90 /* Intr commands */ 91 #define INTR_RT_STS 0x10 92 #define INTR_SET_TYPE 0x11 93 #define INTR_POLARITY_HIGH 0x12 94 #define INTR_POLARITY_LOW 0x13 95 #define INTR_LATCHED_CLR 0x14 96 #define INTR_EN_SET 0x15 97 #define INTR_EN_CLR 0x16 98 #define INTR_LATCHED_STS 0x18 99 100 #define HREAD4(sc, obj, reg) \ 101 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh[obj], (reg))) 102 #define HWRITE4(sc, obj, reg, val) \ 103 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh[obj], (reg), (val)) 104 #define HSET4(sc, obj, reg, bits) \ 105 HWRITE4((sc), (obj), (reg), HREAD4((sc), (reg)) | (bits)) 106 #define HCLR4(sc, obj, reg, bits) \ 107 HWRITE4((sc), (obj), (reg), HREAD4((sc), (reg)) & ~(bits)) 108 109 #define QCSPMI_REG_CORE 0 110 #define QCSPMI_REG_CHNLS 1 111 #define QCSPMI_REG_OBSRVR 2 112 #define QCSPMI_REG_INTR 3 113 #define QCSPMI_REG_CNFG 4 114 #define QCSPMI_REG_MAX 5 115 116 char *qcspmi_regs[] = { "core", "chnls", "obsrvr", "intr", "cnfg" }; 117 118 struct qcspmi_apid { 119 uint16_t ppid; 120 uint8_t write_ee; 121 uint8_t irq_ee; 122 }; 123 124 struct qcspmi_intrhand { 125 TAILQ_ENTRY(qcspmi_intrhand) ih_q; 126 int (*ih_func)(void *); 127 void *ih_arg; 128 void *ih_sc; 129 uint16_t ih_per; 130 uint8_t ih_pin; 131 uint8_t ih_sid; 132 uint32_t ih_apid; 133 }; 134 135 struct qcspmi_softc { 136 struct device sc_dev; 137 int sc_node; 138 139 bus_space_tag_t sc_iot; 140 bus_space_handle_t sc_ioh[QCSPMI_REG_MAX]; 141 void *sc_ih; 142 143 int sc_ee; 144 145 struct qcspmi_apid sc_apid[SPMI_MAX_PERIPH]; 146 uint16_t sc_ppid_to_apid[SPMI_MAX_PPID]; 147 148 struct spmi_controller sc_tag; 149 struct interrupt_controller sc_ic; 150 151 TAILQ_HEAD(,qcspmi_intrhand) sc_intrq; 152 }; 153 154 int qcspmi_match(struct device *, void *, void *); 155 void qcspmi_attach(struct device *, struct device *, void *); 156 int qcspmi_print(void *, const char *); 157 158 int qcspmi_cmd_read(void *, uint8_t, uint8_t, uint16_t, void *, size_t); 159 int qcspmi_cmd_write(void *, uint8_t, uint8_t, uint16_t, 160 const void *, size_t); 161 162 void *qcspmi_intr_establish(void *, int *, int, struct cpu_info *, 163 int (*)(void *), void *, char *); 164 void qcspmi_intr_disestablish(void *); 165 void qcspmi_intr_enable(void *); 166 void qcspmi_intr_disable(void *); 167 void qcspmi_intr_barrier(void *); 168 int qcspmi_pin_intr(struct qcspmi_softc *, int); 169 int qcspmi_intr(void *); 170 171 const struct cfattach qcspmi_ca = { 172 sizeof(struct qcspmi_softc), qcspmi_match, qcspmi_attach 173 }; 174 175 struct cfdriver qcspmi_cd = { 176 NULL, "qcspmi", DV_DULL 177 }; 178 179 int 180 qcspmi_match(struct device *parent, void *match, void *aux) 181 { 182 struct fdt_attach_args *faa = aux; 183 184 return OF_is_compatible(faa->fa_node, "qcom,spmi-pmic-arb"); 185 } 186 187 void 188 qcspmi_attach(struct device *parent, struct device *self, void *aux) 189 { 190 struct fdt_attach_args *faa = aux; 191 struct qcspmi_softc *sc = (struct qcspmi_softc *)self; 192 struct qcspmi_apid *apid, *last_apid; 193 uint32_t val, ppid, irq_own; 194 struct spmi_attach_args sa; 195 char name[32]; 196 uint32_t reg[2]; 197 int i, j, node; 198 199 sc->sc_node = faa->fa_node; 200 sc->sc_iot = faa->fa_iot; 201 202 for (i = 0; i < nitems(qcspmi_regs); i++) { 203 j = OF_getindex(faa->fa_node, qcspmi_regs[i], "reg-names"); 204 if (j < 0 || j >= faa->fa_nreg) { 205 printf(": no %s registers\n", qcspmi_regs[i]); 206 return; 207 } 208 209 if (bus_space_map(sc->sc_iot, faa->fa_reg[j].addr, 210 faa->fa_reg[j].size, 0, &sc->sc_ioh[i])) { 211 printf(": can't map %s registers\n", qcspmi_regs[i]); 212 return; 213 } 214 } 215 216 /* Support only version 5 for now */ 217 val = HREAD4(sc, QCSPMI_REG_CORE, SPMI_VERSION); 218 if (val < SPMI_VERSION_V5_MIN) { 219 printf(": unsupported version 0x%08x\n", val); 220 return; 221 } 222 223 sc->sc_ee = OF_getpropint(sc->sc_node, "qcom,ee", 0); 224 if (sc->sc_ee > 5) { 225 printf(": unsupported EE\n"); 226 return; 227 } 228 229 TAILQ_INIT(&sc->sc_intrq); 230 231 sc->sc_ih = fdt_intr_establish(sc->sc_node, IPL_BIO, qcspmi_intr, 232 sc, sc->sc_dev.dv_xname); 233 if (sc->sc_ih == NULL) { 234 printf(": can't establish interrupt\n"); 235 return; 236 } 237 238 printf("\n"); 239 240 for (i = 0; i < SPMI_MAX_PERIPH; i++) { 241 val = HREAD4(sc, QCSPMI_REG_CORE, SPMI_ARB_APID_MAP(i)); 242 if (!val) 243 continue; 244 ppid = (val >> SPMI_ARB_APID_MAP_PPID_SHIFT) & 245 SPMI_ARB_APID_MAP_PPID_MASK; 246 irq_own = val & SPMI_ARB_APID_MAP_IRQ_OWNER; 247 val = HREAD4(sc, QCSPMI_REG_CNFG, SPMI_OWNERSHIP_TABLE(i)); 248 apid = &sc->sc_apid[i]; 249 apid->write_ee = SPMI_OWNERSHIP_TABLE_OWNER(val); 250 apid->irq_ee = 0xff; 251 if (irq_own) 252 apid->irq_ee = apid->write_ee; 253 last_apid = &sc->sc_apid[sc->sc_ppid_to_apid[ppid] & 254 SPMI_PPID_TO_APID_MASK]; 255 if (!(sc->sc_ppid_to_apid[ppid] & SPMI_PPID_TO_APID_VALID) || 256 apid->write_ee == sc->sc_ee) { 257 sc->sc_ppid_to_apid[ppid] = SPMI_PPID_TO_APID_VALID | i; 258 } else if ((sc->sc_ppid_to_apid[ppid] & 259 SPMI_PPID_TO_APID_VALID) && irq_own && 260 last_apid->write_ee == sc->sc_ee) { 261 last_apid->irq_ee = apid->irq_ee; 262 } 263 } 264 265 sc->sc_tag.sc_cookie = sc; 266 sc->sc_tag.sc_cmd_read = qcspmi_cmd_read; 267 sc->sc_tag.sc_cmd_write = qcspmi_cmd_write; 268 269 sc->sc_ic.ic_node = faa->fa_node; 270 sc->sc_ic.ic_cookie = sc; 271 sc->sc_ic.ic_establish = qcspmi_intr_establish; 272 sc->sc_ic.ic_disestablish = qcspmi_intr_disestablish; 273 sc->sc_ic.ic_enable = qcspmi_intr_enable; 274 sc->sc_ic.ic_disable = qcspmi_intr_disable; 275 sc->sc_ic.ic_barrier = qcspmi_intr_barrier; 276 fdt_intr_register(&sc->sc_ic); 277 278 for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) { 279 if (OF_getpropintarray(node, "reg", reg, 280 sizeof(reg)) != sizeof(reg)) 281 continue; 282 283 memset(name, 0, sizeof(name)); 284 if (OF_getprop(node, "compatible", name, sizeof(name)) == -1) 285 continue; 286 if (name[0] == '\0') 287 continue; 288 289 memset(&sa, 0, sizeof(sa)); 290 sa.sa_tag = &sc->sc_tag; 291 sa.sa_sid = reg[0]; 292 sa.sa_name = name; 293 sa.sa_node = node; 294 config_found(self, &sa, qcspmi_print); 295 } 296 } 297 298 int 299 qcspmi_print(void *aux, const char *pnp) 300 { 301 struct spmi_attach_args *sa = aux; 302 303 if (pnp != NULL) 304 printf("\"%s\" at %s", sa->sa_name, pnp); 305 printf(" sid 0x%x", sa->sa_sid); 306 307 return UNCONF; 308 } 309 310 int 311 qcspmi_cmd_read(void *cookie, uint8_t sid, uint8_t cmd, uint16_t addr, 312 void *buf, size_t len) 313 { 314 struct qcspmi_softc *sc = cookie; 315 uint8_t *cbuf = buf; 316 uint32_t reg; 317 uint16_t apid, ppid; 318 int bc = len - 1; 319 int i; 320 321 if (len == 0 || len > 8) 322 return EINVAL; 323 324 /* TODO: support more types */ 325 if (cmd != SPMI_CMD_EXT_READL) 326 return EINVAL; 327 328 ppid = (sid << 8) | (addr >> 8); 329 if (!(sc->sc_ppid_to_apid[ppid] & SPMI_PPID_TO_APID_VALID)) 330 return ENXIO; 331 apid = sc->sc_ppid_to_apid[ppid] & SPMI_PPID_TO_APID_MASK; 332 333 HWRITE4(sc, QCSPMI_REG_OBSRVR, 334 SPMI_OBSV_OFF(sc->sc_ee, apid) + SPMI_COMMAND, 335 SPMI_COMMAND_OP_EXT_READL | SPMI_COMMAND_ADDR(addr) | 336 SPMI_COMMAND_LEN(bc)); 337 338 for (i = 1000; i > 0; i--) { 339 reg = HREAD4(sc, QCSPMI_REG_OBSRVR, 340 SPMI_OBSV_OFF(sc->sc_ee, apid) + SPMI_STATUS); 341 if (reg & SPMI_STATUS_DONE) 342 break; 343 } 344 if (i == 0) 345 return ETIMEDOUT; 346 347 if (reg & SPMI_STATUS_FAILURE || 348 reg & SPMI_STATUS_DENIED || 349 reg & SPMI_STATUS_DROPPED) 350 return EIO; 351 352 if (len > 0) { 353 reg = HREAD4(sc, QCSPMI_REG_OBSRVR, 354 SPMI_OBSV_OFF(sc->sc_ee, apid) + SPMI_RDATA0); 355 memcpy(cbuf, ®, MIN(len, 4)); 356 cbuf += MIN(len, 4); 357 len -= MIN(len, 4); 358 } 359 if (len > 0) { 360 reg = HREAD4(sc, QCSPMI_REG_OBSRVR, 361 SPMI_OBSV_OFF(sc->sc_ee, apid) + SPMI_RDATA1); 362 memcpy(cbuf, ®, MIN(len, 4)); 363 cbuf += MIN(len, 4); 364 len -= MIN(len, 4); 365 } 366 367 return 0; 368 } 369 370 int 371 qcspmi_cmd_write(void *cookie, uint8_t sid, uint8_t cmd, uint16_t addr, 372 const void *buf, size_t len) 373 { 374 struct qcspmi_softc *sc = cookie; 375 const uint8_t *cbuf = buf; 376 uint32_t reg; 377 uint16_t apid, ppid; 378 int bc = len - 1; 379 int i; 380 381 if (len == 0 || len > 8) 382 return EINVAL; 383 384 /* TODO: support more types */ 385 if (cmd != SPMI_CMD_EXT_WRITEL) 386 return EINVAL; 387 388 ppid = (sid << 8) | (addr >> 8); 389 if (!(sc->sc_ppid_to_apid[ppid] & SPMI_PPID_TO_APID_VALID)) 390 return ENXIO; 391 apid = sc->sc_ppid_to_apid[ppid] & SPMI_PPID_TO_APID_MASK; 392 393 if (sc->sc_apid[apid].write_ee != sc->sc_ee) 394 return EPERM; 395 396 if (len > 0) { 397 memcpy(®, cbuf, MIN(len, 4)); 398 HWRITE4(sc, QCSPMI_REG_CHNLS, SPMI_CHAN_OFF(apid) + 399 SPMI_WDATA0, reg); 400 cbuf += MIN(len, 4); 401 len -= MIN(len, 4); 402 } 403 if (len > 0) { 404 memcpy(®, cbuf, MIN(len, 4)); 405 HWRITE4(sc, QCSPMI_REG_CHNLS, SPMI_CHAN_OFF(apid) + 406 SPMI_WDATA1, reg); 407 cbuf += MIN(len, 4); 408 len -= MIN(len, 4); 409 } 410 411 HWRITE4(sc, QCSPMI_REG_CHNLS, SPMI_CHAN_OFF(apid) + SPMI_COMMAND, 412 SPMI_COMMAND_OP_EXT_WRITEL | SPMI_COMMAND_ADDR(addr) | 413 SPMI_COMMAND_LEN(bc)); 414 415 for (i = 1000; i > 0; i--) { 416 reg = HREAD4(sc, QCSPMI_REG_CHNLS, SPMI_CHAN_OFF(apid) + 417 SPMI_STATUS); 418 if (reg & SPMI_STATUS_DONE) 419 break; 420 } 421 if (i == 0) 422 return ETIMEDOUT; 423 424 if (reg & SPMI_STATUS_FAILURE || 425 reg & SPMI_STATUS_DENIED || 426 reg & SPMI_STATUS_DROPPED) 427 return EIO; 428 429 return 0; 430 } 431 432 void * 433 qcspmi_intr_establish(void *cookie, int *cells, int ipl, 434 struct cpu_info *ci, int (*func)(void *), void *arg, char *name) 435 { 436 struct qcspmi_softc *sc = cookie; 437 struct qcspmi_intrhand *ih; 438 uint16_t ppid; 439 uint8_t reg[3]; 440 int error; 441 442 ppid = cells[0] << 8 | cells[1]; 443 if (!(sc->sc_ppid_to_apid[ppid] & SPMI_PPID_TO_APID_VALID)) 444 return NULL; 445 446 ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK | M_ZERO); 447 ih->ih_func = func; 448 ih->ih_arg = arg; 449 ih->ih_sc = sc; 450 ih->ih_sid = cells[0]; 451 ih->ih_per = cells[1]; 452 ih->ih_pin = cells[2]; 453 ih->ih_apid = sc->sc_ppid_to_apid[ppid] & SPMI_PPID_TO_APID_MASK; 454 TAILQ_INSERT_TAIL(&sc->sc_intrq, ih, ih_q); 455 456 error = spmi_cmd_read(&sc->sc_tag, ih->ih_sid, SPMI_CMD_EXT_READL, 457 (ih->ih_per << 8) | INTR_SET_TYPE, ®, sizeof(reg)); 458 if (error) 459 printf("%s: cannot read irq setting\n", sc->sc_dev.dv_xname); 460 461 reg[0] &= ~(1U << ih->ih_pin); 462 reg[1] &= ~(1U << ih->ih_pin); 463 reg[2] &= ~(1U << ih->ih_pin); 464 465 switch (cells[3]) { 466 case 1: 467 reg[0] |= (1U << ih->ih_pin); /* edge */ 468 reg[1] |= (1U << ih->ih_pin); /* rising */ 469 break; 470 case 2: 471 reg[0] |= (1U << ih->ih_pin); /* edge */ 472 reg[2] |= (1U << ih->ih_pin); /* falling */ 473 break; 474 case 3: 475 reg[0] |= (1U << ih->ih_pin); /* edge */ 476 reg[1] |= (1U << ih->ih_pin); /* rising */ 477 reg[2] |= (1U << ih->ih_pin); /* falling */ 478 break; 479 case 4: 480 reg[1] |= (1U << ih->ih_pin); /* high */ 481 break; 482 case 8: 483 reg[2] |= (1U << ih->ih_pin); /* low */ 484 break; 485 default: 486 printf("%s: unsupported interrupt mode/polarity\n", 487 sc->sc_dev.dv_xname); 488 break; 489 } 490 491 error = spmi_cmd_write(&sc->sc_tag, ih->ih_sid, SPMI_CMD_EXT_WRITEL, 492 (ih->ih_per << 8) | INTR_SET_TYPE, ®, sizeof(reg)); 493 if (error) 494 printf("%s: cannot write irq setting\n", sc->sc_dev.dv_xname); 495 496 HWRITE4(sc, QCSPMI_REG_CHNLS, SPMI_CHAN_OFF(ih->ih_apid) + 497 SPMI_IRQ_CLEAR, (1U << ih->ih_pin)); 498 qcspmi_intr_enable(ih); 499 500 if (ipl & IPL_WAKEUP) 501 intr_set_wakeup(sc->sc_ih); 502 503 return ih; 504 } 505 506 void 507 qcspmi_intr_disestablish(void *cookie) 508 { 509 struct qcspmi_intrhand *ih = cookie; 510 struct qcspmi_softc *sc = ih->ih_sc; 511 512 qcspmi_intr_disable(cookie); 513 514 TAILQ_REMOVE(&sc->sc_intrq, ih, ih_q); 515 free(ih, M_DEVBUF, sizeof(*ih)); 516 } 517 518 void 519 qcspmi_intr_enable(void *cookie) 520 { 521 struct qcspmi_intrhand *ih = cookie; 522 struct qcspmi_softc *sc = ih->ih_sc; 523 uint8_t reg[2]; 524 int error; 525 526 HWRITE4(sc, QCSPMI_REG_CHNLS, SPMI_CHAN_OFF(ih->ih_apid) + 527 SPMI_ACC_ENABLE, SPMI_ACC_ENABLE_BIT); 528 529 error = spmi_cmd_read(&sc->sc_tag, ih->ih_sid, SPMI_CMD_EXT_READL, 530 (ih->ih_per << 8) | INTR_EN_SET, ®, 1); 531 if (error) 532 printf("%s: cannot read irq setting\n", sc->sc_dev.dv_xname); 533 534 if (!(reg[0] & (1U << ih->ih_pin))) { 535 reg[0] = (1U << ih->ih_pin); 536 reg[1] = (1U << ih->ih_pin); 537 error = spmi_cmd_write(&sc->sc_tag, ih->ih_sid, 538 SPMI_CMD_EXT_WRITEL, (ih->ih_per << 8) | INTR_LATCHED_CLR, 539 ®, 2); 540 if (error) 541 printf("%s: cannot enable irq\n", sc->sc_dev.dv_xname); 542 } 543 } 544 545 void 546 qcspmi_intr_disable(void *cookie) 547 { 548 struct qcspmi_intrhand *ih = cookie; 549 struct qcspmi_softc *sc = ih->ih_sc; 550 uint8_t reg = (1U << ih->ih_pin); 551 int error; 552 553 error = spmi_cmd_write(&sc->sc_tag, ih->ih_sid, SPMI_CMD_EXT_WRITEL, 554 (ih->ih_per << 8) | INTR_EN_CLR, ®, sizeof(reg)); 555 if (error) 556 printf("%s: cannot disable irq\n", sc->sc_dev.dv_xname); 557 } 558 559 void 560 qcspmi_intr_barrier(void *cookie) 561 { 562 struct qcspmi_intrhand *ih = cookie; 563 struct qcspmi_softc *sc = ih->ih_sc; 564 565 intr_barrier(sc->sc_ih); 566 } 567 568 int 569 qcspmi_intr(void *arg) 570 { 571 struct qcspmi_softc *sc = arg; 572 struct qcspmi_intrhand *ih; 573 uint32_t status; 574 uint8_t reg; 575 int error; 576 int handled = 0; 577 578 TAILQ_FOREACH(ih, &sc->sc_intrq, ih_q) { 579 status = HREAD4(sc, QCSPMI_REG_INTR, 580 SPMI_OWNER_ACC_STATUS(sc->sc_ee, ih->ih_apid / 32)); 581 if (!(status & (1U << (ih->ih_apid % 32)))) 582 continue; 583 status = HREAD4(sc, QCSPMI_REG_CHNLS, 584 SPMI_CHAN_OFF(ih->ih_apid) + SPMI_ACC_ENABLE); 585 if (!(status & SPMI_ACC_ENABLE_BIT)) 586 continue; 587 status = HREAD4(sc, QCSPMI_REG_CHNLS, 588 SPMI_CHAN_OFF(ih->ih_apid) + SPMI_IRQ_STATUS); 589 if (!(status & (1U << ih->ih_pin))) 590 continue; 591 592 ih->ih_func(ih->ih_arg); 593 handled = 1; 594 595 HWRITE4(sc, QCSPMI_REG_CHNLS, SPMI_CHAN_OFF(ih->ih_apid) + 596 SPMI_IRQ_CLEAR, (1U << ih->ih_pin)); 597 reg = 1U << ih->ih_pin; 598 error = spmi_cmd_write(&sc->sc_tag, ih->ih_sid, 599 SPMI_CMD_EXT_WRITEL, (ih->ih_per << 8) | INTR_LATCHED_CLR, 600 ®, sizeof(reg)); 601 if (error) 602 printf("%s: cannot clear irq\n", sc->sc_dev.dv_xname); 603 } 604 605 return handled; 606 } 607