xref: /openbsd-src/sys/dev/acpi/qcgpio.c (revision fc405d53b73a2d73393cb97f684863d17b583e38)
1 /*	$OpenBSD: qcgpio.c,v 1.9 2023/04/11 04:45:11 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_intr(void *);
108 
109 int
110 qcgpio_acpi_match(struct device *parent, void *match, void *aux)
111 {
112 	struct acpi_attach_args *aaa = aux;
113 	struct cfdata *cf = match;
114 
115 	if (aaa->aaa_naddr < 1 || aaa->aaa_nirq < 1)
116 		return 0;
117 	return acpi_matchhids(aaa, qcgpio_hids, cf->cf_driver->cd_name);
118 }
119 
120 void
121 qcgpio_acpi_attach(struct device *parent, struct device *self, void *aux)
122 {
123 	struct acpi_attach_args *aaa = aux;
124 	struct qcgpio_softc *sc = (struct qcgpio_softc *)self;
125 
126 	sc->sc_acpi = (struct acpi_softc *)parent;
127 	sc->sc_node = aaa->aaa_node;
128 	printf(" %s", sc->sc_node->name);
129 
130 	printf(" addr 0x%llx/0x%llx", aaa->aaa_addr[0], aaa->aaa_size[0]);
131 
132 	sc->sc_iot = aaa->aaa_bst[0];
133 	if (bus_space_map(sc->sc_iot, aaa->aaa_addr[0], aaa->aaa_size[0],
134 	    0, &sc->sc_ioh)) {
135 		printf(": can't map registers\n");
136 		return;
137 	}
138 
139 	if (strcmp(aaa->aaa_dev, "QCOM080D") == 0) {
140 		sc->sc_npins = 119;
141 		sc->sc_pin_map = qcgpio_sc7180_pin_map;
142 	} else if (strcmp(aaa->aaa_dev, "QCOM060C") == 0) {
143 		sc->sc_npins = 228;
144 		sc->sc_pin_map = qcgpio_sc8280xp_pin_map;
145 	}
146 	KASSERT(sc->sc_npins != 0);
147 
148 	sc->sc_pin_ih = mallocarray(sc->sc_npins, sizeof(*sc->sc_pin_ih),
149 	    M_DEVBUF, M_WAITOK | M_ZERO);
150 
151 	printf(" irq %d", aaa->aaa_irq[0]);
152 
153 	sc->sc_ih = acpi_intr_establish(aaa->aaa_irq[0],
154 	    aaa->aaa_irq_flags[0], IPL_BIO, qcgpio_intr,
155 	    sc, sc->sc_dev.dv_xname);
156 	if (sc->sc_ih == NULL) {
157 		printf(": can't establish interrupt\n");
158 		goto unmap;
159 	}
160 
161 	sc->sc_gpio.cookie = sc;
162 	sc->sc_gpio.read_pin = qcgpio_read_pin;
163 	sc->sc_gpio.write_pin = qcgpio_write_pin;
164 	sc->sc_gpio.intr_establish = qcgpio_intr_establish;
165 	sc->sc_gpio.intr_enable = qcgpio_intr_enable;
166 	sc->sc_gpio.intr_disable = qcgpio_intr_disable;
167 	sc->sc_node->gpio = &sc->sc_gpio;
168 
169 	printf("\n");
170 
171 	acpi_register_gpio(sc->sc_acpi, sc->sc_node);
172 	return;
173 
174 unmap:
175 	if (sc->sc_ih)
176 		acpi_intr_disestablish(sc->sc_ih);
177 	free(sc->sc_pin_ih, M_DEVBUF, sc->sc_npins * sizeof(*sc->sc_pin_ih));
178 	bus_space_unmap(sc->sc_iot, sc->sc_ioh, aaa->aaa_size[0]);
179 }
180 
181 int
182 qcgpio_sc7180_pin_map(int pin, bus_size_t *off)
183 {
184 	switch (pin) {
185 	case 30:
186 		*off = QCGPIO_SC7180_SOUTH;
187 		return 30;
188 #if 0
189 	/* XXX: Disable until we can fix the interrupt storm. */
190 	case 32:
191 	case 0x140:
192 		*off = QCGPIO_SC7180_NORTH;
193 		return 32;
194 #endif
195 	case 33:
196 	case 0x180:
197 		*off = QCGPIO_SC7180_NORTH;
198 		return 33;
199 	case 94:
200 	case 0x1c0:
201 		*off = QCGPIO_SC7180_SOUTH;
202 		return 94;
203 	default:
204 		return -1;
205 	}
206 }
207 
208 int
209 qcgpio_sc8280xp_pin_map(int pin, bus_size_t *off)
210 {
211 	switch (pin) {
212 	case 107:
213 	case 175:
214 		return pin;
215 	case 0x2c0:
216 		return 107;
217 	case 0x340:
218 		return 104;
219 	case 0x380:
220 		return 182;
221 	default:
222 		return -1;
223 	}
224 }
225 
226 int
227 qcgpio_read_pin(void *cookie, int pin)
228 {
229 	struct qcgpio_softc *sc = cookie;
230 	bus_size_t off = 0;
231 	uint32_t reg;
232 
233 	pin = sc->sc_pin_map(pin, &off);
234 	if (pin < 0 || pin >= sc->sc_npins)
235 		return 0;
236 
237 	reg = HREAD4(sc, off + TLMM_GPIO_IN_OUT(pin));
238 	return !!(reg & TLMM_GPIO_IN_OUT_GPIO_IN);
239 }
240 
241 void
242 qcgpio_write_pin(void *cookie, int pin, int val)
243 {
244 	struct qcgpio_softc *sc = cookie;
245 	bus_size_t off = 0;
246 
247 	pin = sc->sc_pin_map(pin, &off);
248 	if (pin < 0 || pin >= sc->sc_npins)
249 		return;
250 
251 	if (val) {
252 		HSET4(sc, off + TLMM_GPIO_IN_OUT(pin),
253 		    TLMM_GPIO_IN_OUT_GPIO_OUT);
254 	} else {
255 		HCLR4(sc, off + TLMM_GPIO_IN_OUT(pin),
256 		    TLMM_GPIO_IN_OUT_GPIO_OUT);
257 	}
258 }
259 
260 void
261 qcgpio_intr_establish(void *cookie, int pin, int flags,
262     int (*func)(void *), void *arg)
263 {
264 	struct qcgpio_softc *sc = cookie;
265 	bus_size_t off = 0;
266 	uint32_t reg;
267 
268 	pin = sc->sc_pin_map(pin, &off);
269 	if (pin < 0 || pin >= sc->sc_npins)
270 		return;
271 
272 	sc->sc_pin_ih[pin].ih_func = func;
273 	sc->sc_pin_ih[pin].ih_arg = arg;
274 
275 	reg = HREAD4(sc, off + TLMM_GPIO_INTR_CFG(pin));
276 	reg &= ~TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_MASK;
277 	reg &= ~TLMM_GPIO_INTR_CFG_INTR_POL_CTL;
278 	switch (flags & (LR_GPIO_MODE | LR_GPIO_POLARITY)) {
279 	case LR_GPIO_LEVEL | LR_GPIO_ACTLO:
280 		reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_LEVEL;
281 		break;
282 	case LR_GPIO_LEVEL | LR_GPIO_ACTHI:
283 		reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_LEVEL |
284 		    TLMM_GPIO_INTR_CFG_INTR_POL_CTL;
285 		break;
286 	case LR_GPIO_EDGE | LR_GPIO_ACTLO:
287 		reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_EDGE_NEG |
288 		    TLMM_GPIO_INTR_CFG_INTR_POL_CTL;
289 		break;
290 	case LR_GPIO_EDGE | LR_GPIO_ACTHI:
291 		reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_EDGE_POS |
292 		    TLMM_GPIO_INTR_CFG_INTR_POL_CTL;
293 		break;
294 	case LR_GPIO_EDGE | LR_GPIO_ACTBOTH:
295 		reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_EDGE_BOTH;
296 		break;
297 	default:
298 		printf("%s: unsupported interrupt mode/polarity\n",
299 		    sc->sc_dev.dv_xname);
300 		break;
301 	}
302 	reg &= ~TLMM_GPIO_INTR_CFG_TARGET_PROC_MASK;
303 	reg |= TLMM_GPIO_INTR_CFG_TARGET_PROC_RPM;
304 	reg |= TLMM_GPIO_INTR_CFG_INTR_RAW_STATUS_EN;
305 	reg |= TLMM_GPIO_INTR_CFG_INTR_ENABLE;
306 	HWRITE4(sc, off + TLMM_GPIO_INTR_CFG(pin), reg);
307 }
308 
309 void
310 qcgpio_intr_enable(void *cookie, int pin)
311 {
312 	struct qcgpio_softc *sc = cookie;
313 	bus_size_t off = 0;
314 
315 	pin = sc->sc_pin_map(pin, &off);
316 	if (pin < 0 || pin >= sc->sc_npins)
317 		return;
318 
319 	HSET4(sc, off + TLMM_GPIO_INTR_CFG(pin),
320 	    TLMM_GPIO_INTR_CFG_INTR_ENABLE);
321 }
322 
323 void
324 qcgpio_intr_disable(void *cookie, int pin)
325 {
326 	struct qcgpio_softc *sc = cookie;
327 	bus_size_t off = 0;
328 
329 	pin = sc->sc_pin_map(pin, &off);
330 	if (pin < 0 || pin >= sc->sc_npins)
331 		return;
332 
333 	HCLR4(sc, off + TLMM_GPIO_INTR_CFG(pin),
334 	    TLMM_GPIO_INTR_CFG_INTR_ENABLE);
335 }
336 
337 int
338 qcgpio_intr(void *arg)
339 {
340 	struct qcgpio_softc *sc = arg;
341 	int pin, handled = 0;
342 	bus_size_t off = 0;
343 	uint32_t stat;
344 
345 	for (pin = 0; pin < sc->sc_npins; pin++) {
346 		if (sc->sc_pin_ih[pin].ih_func == NULL)
347 			continue;
348 
349 		sc->sc_pin_map(pin, &off);
350 
351 		stat = HREAD4(sc, off + TLMM_GPIO_INTR_STATUS(pin));
352 		if (stat & TLMM_GPIO_INTR_STATUS_INTR_STATUS) {
353 			sc->sc_pin_ih[pin].ih_func(sc->sc_pin_ih[pin].ih_arg);
354 			HWRITE4(sc, off + TLMM_GPIO_INTR_STATUS(pin),
355 			    stat & ~TLMM_GPIO_INTR_STATUS_INTR_STATUS);
356 			handled = 1;
357 		}
358 	}
359 
360 	return handled;
361 }
362