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