xref: /openbsd-src/sys/dev/fdt/qcspmi.c (revision 1ad61ae0a79a724d2d3ec69e69c8e1d1ff6b53a0)
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, &reg, 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, &reg, 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(&reg, 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(&reg, 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, &reg, 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, &reg, 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, &reg, 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 		    &reg, 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, &reg, 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 		    &reg, sizeof(reg));
601 		if (error)
602 			printf("%s: cannot clear irq\n", sc->sc_dev.dv_xname);
603 	}
604 
605 	return handled;
606 }
607