xref: /openbsd/sys/dev/fdt/qcgpio_fdt.c (revision 73664677)
1 /*	$OpenBSD: qcgpio_fdt.c,v 1.5 2025/01/09 21:52:25 kettenis 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 <machine/fdt.h>
23 
24 #include <dev/ofw/openfirm.h>
25 #include <dev/ofw/ofw_gpio.h>
26 #include <dev/ofw/fdt.h>
27 
28 /* Registers. */
29 #define TLMM_GPIO_CFG(pin)		(0x0000 + 0x1000 * (pin))
30 #define  TLMM_GPIO_CFG_OUT_EN				(1 << 9)
31 #define TLMM_GPIO_IN_OUT(pin)		(0x0004 + 0x1000 * (pin))
32 #define  TLMM_GPIO_IN_OUT_GPIO_IN			(1 << 0)
33 #define  TLMM_GPIO_IN_OUT_GPIO_OUT			(1 << 1)
34 #define TLMM_GPIO_INTR_CFG(pin)		(0x0008 + 0x1000 * (pin))
35 #define  TLMM_GPIO_INTR_CFG_TARGET_PROC_MASK		(0x7 << 5)
36 #define  TLMM_GPIO_INTR_CFG_TARGET_PROC_RPM		(0x3 << 5)
37 #define  TLMM_GPIO_INTR_CFG_INTR_RAW_STATUS_EN		(1 << 4)
38 #define  TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_MASK		(0x3 << 2)
39 #define  TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_LEVEL		(0x0 << 2)
40 #define  TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_EDGE_POS	(0x1 << 2)
41 #define  TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_EDGE_NEG	(0x2 << 2)
42 #define  TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_EDGE_BOTH	(0x3 << 2)
43 #define  TLMM_GPIO_INTR_CFG_INTR_POL_CTL		(1 << 1)
44 #define  TLMM_GPIO_INTR_CFG_INTR_ENABLE			(1 << 0)
45 #define TLMM_GPIO_INTR_STATUS(pin)	(0x000c + 0x1000 * (pin))
46 #define  TLMM_GPIO_INTR_STATUS_INTR_STATUS		(1 << 0)
47 
48 #define HREAD4(sc, reg)							\
49 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
50 #define HWRITE4(sc, reg, val)						\
51 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
52 #define HSET4(sc, reg, bits)						\
53 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
54 #define HCLR4(sc, reg, bits)						\
55 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
56 
57 struct qcgpio_intrhand {
58 	int (*ih_func)(void *);
59 	void *ih_arg;
60 	void *ih_sc;
61 	int ih_pin;
62 };
63 
64 struct qcgpio_softc {
65 	struct device		sc_dev;
66 
67 	bus_space_tag_t		sc_iot;
68 	bus_space_handle_t	sc_ioh;
69 
70 	void			*sc_ih;
71 
72 	uint32_t		sc_npins;
73 	struct qcgpio_intrhand	*sc_pin_ih;
74 
75 	struct gpio_controller	sc_gc;
76 	struct interrupt_controller sc_ic;
77 };
78 
79 int	qcgpio_fdt_match(struct device *, void *, void *);
80 void	qcgpio_fdt_attach(struct device *, struct device *, void *);
81 
82 const struct cfattach qcgpio_fdt_ca = {
83 	sizeof(struct qcgpio_softc), qcgpio_fdt_match, qcgpio_fdt_attach
84 };
85 
86 void	qcgpio_fdt_config_pin(void *, uint32_t *, int);
87 int	qcgpio_fdt_get_pin(void *, uint32_t *);
88 void	qcgpio_fdt_set_pin(void *, uint32_t *, int);
89 void	*qcgpio_fdt_intr_establish_pin(void *, uint32_t *, int,
90 	    struct cpu_info *, int (*)(void *), void *, char *);
91 
92 void	*qcgpio_fdt_intr_establish(void *, int *, int, struct cpu_info *,
93 	    int (*)(void *), void *, char *);
94 void	qcgpio_fdt_intr_disestablish(void *);
95 void	qcgpio_fdt_intr_enable(void *);
96 void	qcgpio_fdt_intr_disable(void *);
97 void	qcgpio_fdt_intr_barrier(void *);
98 int	qcgpio_fdt_intr(void *);
99 
100 int
qcgpio_fdt_match(struct device * parent,void * match,void * aux)101 qcgpio_fdt_match(struct device *parent, void *match, void *aux)
102 {
103 	struct fdt_attach_args *faa = aux;
104 
105 	return (OF_is_compatible(faa->fa_node, "qcom,sc8280xp-tlmm") ||
106 	    OF_is_compatible(faa->fa_node, "qcom,x1e80100-tlmm"));
107 }
108 
109 void
qcgpio_fdt_attach(struct device * parent,struct device * self,void * aux)110 qcgpio_fdt_attach(struct device *parent, struct device *self, void *aux)
111 {
112 	struct fdt_attach_args *faa = aux;
113 	struct qcgpio_softc *sc = (struct qcgpio_softc *)self;
114 
115 	sc->sc_iot = faa->fa_iot;
116 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size,
117 	    0, &sc->sc_ioh)) {
118 		printf(": can't map registers\n");
119 		return;
120 	}
121 
122 	if (OF_is_compatible(faa->fa_node, "qcom,sc8280xp-tlmm"))
123 		sc->sc_npins = 230;
124 	else
125 		sc->sc_npins = 239;
126 	sc->sc_pin_ih = mallocarray(sc->sc_npins, sizeof(*sc->sc_pin_ih),
127 	    M_DEVBUF, M_WAITOK | M_ZERO);
128 
129 	sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_BIO, qcgpio_fdt_intr,
130 	    sc, sc->sc_dev.dv_xname);
131 	if (sc->sc_ih == NULL) {
132 		printf(": can't establish interrupt\n");
133 		goto unmap;
134 	}
135 
136 	sc->sc_gc.gc_node = faa->fa_node;
137 	sc->sc_gc.gc_cookie = sc;
138 	sc->sc_gc.gc_config_pin = qcgpio_fdt_config_pin;
139 	sc->sc_gc.gc_get_pin = qcgpio_fdt_get_pin;
140 	sc->sc_gc.gc_set_pin = qcgpio_fdt_set_pin;
141 	sc->sc_gc.gc_intr_establish = qcgpio_fdt_intr_establish_pin;
142 	gpio_controller_register(&sc->sc_gc);
143 
144 	sc->sc_ic.ic_node = faa->fa_node;
145 	sc->sc_ic.ic_cookie = sc;
146 	sc->sc_ic.ic_establish = qcgpio_fdt_intr_establish;
147 	sc->sc_ic.ic_disestablish = qcgpio_fdt_intr_disestablish;
148 	sc->sc_ic.ic_enable = qcgpio_fdt_intr_enable;
149 	sc->sc_ic.ic_disable = qcgpio_fdt_intr_disable;
150 	sc->sc_ic.ic_barrier = qcgpio_fdt_intr_barrier;
151 	fdt_intr_register(&sc->sc_ic);
152 
153 	printf("\n");
154 	return;
155 
156 unmap:
157 	if (sc->sc_ih)
158 		fdt_intr_disestablish(sc->sc_ih);
159 	free(sc->sc_pin_ih, M_DEVBUF, sc->sc_npins * sizeof(*sc->sc_pin_ih));
160 	bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size);
161 }
162 
163 void
qcgpio_fdt_config_pin(void * cookie,uint32_t * cells,int config)164 qcgpio_fdt_config_pin(void *cookie, uint32_t *cells, int config)
165 {
166 	struct qcgpio_softc *sc = cookie;
167 	uint32_t pin = cells[0];
168 
169 	if (pin >= sc->sc_npins)
170 		return;
171 
172 	if (config & GPIO_CONFIG_OUTPUT)
173 		HSET4(sc, TLMM_GPIO_CFG(pin), TLMM_GPIO_CFG_OUT_EN);
174 	else
175 		HCLR4(sc, TLMM_GPIO_CFG(pin), TLMM_GPIO_CFG_OUT_EN);
176 }
177 
178 int
qcgpio_fdt_get_pin(void * cookie,uint32_t * cells)179 qcgpio_fdt_get_pin(void *cookie, uint32_t *cells)
180 {
181 	struct qcgpio_softc *sc = cookie;
182 	uint32_t pin = cells[0];
183 	uint32_t flags = cells[1];
184 	uint32_t reg;
185 	int val;
186 
187 	if (pin >= sc->sc_npins)
188 		return 0;
189 
190 	reg = HREAD4(sc, TLMM_GPIO_IN_OUT(pin));
191 	val = !!(reg & TLMM_GPIO_IN_OUT_GPIO_IN);
192 	if (flags & GPIO_ACTIVE_LOW)
193 		val = !val;
194 	return val;
195 }
196 
197 void
qcgpio_fdt_set_pin(void * cookie,uint32_t * cells,int val)198 qcgpio_fdt_set_pin(void *cookie, uint32_t *cells, int val)
199 {
200 	struct qcgpio_softc *sc = cookie;
201 	uint32_t pin = cells[0];
202 	uint32_t flags = cells[1];
203 
204 	if (pin >= sc->sc_npins)
205 		return;
206 
207 	if (flags & GPIO_ACTIVE_LOW)
208 		val = !val;
209 
210 	if (val) {
211 		HSET4(sc, TLMM_GPIO_IN_OUT(pin),
212 		    TLMM_GPIO_IN_OUT_GPIO_OUT);
213 	} else {
214 		HCLR4(sc, TLMM_GPIO_IN_OUT(pin),
215 		    TLMM_GPIO_IN_OUT_GPIO_OUT);
216 	}
217 }
218 
219 void *
qcgpio_fdt_intr_establish_pin(void * cookie,uint32_t * cells,int ipl,struct cpu_info * ci,int (* func)(void *),void * arg,char * name)220 qcgpio_fdt_intr_establish_pin(void *cookie, uint32_t *cells, int ipl,
221     struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
222 {
223 	struct qcgpio_softc *sc = cookie;
224 	uint32_t icells[2];
225 
226 	icells[0] = cells[0];
227 	icells[1] = 3; /* both edges */
228 
229 	return qcgpio_fdt_intr_establish(sc, icells, ipl, ci, func, arg, name);
230 }
231 
232 void *
qcgpio_fdt_intr_establish(void * cookie,int * cells,int ipl,struct cpu_info * ci,int (* func)(void *),void * arg,char * name)233 qcgpio_fdt_intr_establish(void *cookie, int *cells, int ipl,
234     struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
235 {
236 	struct qcgpio_softc *sc = cookie;
237 	uint32_t reg;
238 	int pin = cells[0];
239 	int level = cells[1];
240 
241 	if (pin < 0 || pin >= sc->sc_npins)
242 		return NULL;
243 
244 	sc->sc_pin_ih[pin].ih_func = func;
245 	sc->sc_pin_ih[pin].ih_arg = arg;
246 	sc->sc_pin_ih[pin].ih_pin = pin;
247 	sc->sc_pin_ih[pin].ih_sc = sc;
248 
249 	reg = HREAD4(sc, TLMM_GPIO_INTR_CFG(pin));
250 	reg &= ~TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_MASK;
251 	reg &= ~TLMM_GPIO_INTR_CFG_INTR_POL_CTL;
252 	switch (level) {
253 	case 1:
254 		reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_EDGE_POS |
255 		    TLMM_GPIO_INTR_CFG_INTR_POL_CTL;
256 		break;
257 	case 2:
258 		reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_EDGE_NEG |
259 		    TLMM_GPIO_INTR_CFG_INTR_POL_CTL;
260 		break;
261 	case 3:
262 		reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_EDGE_BOTH;
263 		break;
264 	case 4:
265 		reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_LEVEL |
266 		    TLMM_GPIO_INTR_CFG_INTR_POL_CTL;
267 		break;
268 	case 8:
269 		reg |= TLMM_GPIO_INTR_CFG_INTR_DECT_CTL_LEVEL;
270 		break;
271 	default:
272 		printf("%s: unsupported interrupt mode/polarity\n",
273 		    sc->sc_dev.dv_xname);
274 		break;
275 	}
276 	reg &= ~TLMM_GPIO_INTR_CFG_TARGET_PROC_MASK;
277 	reg |= TLMM_GPIO_INTR_CFG_TARGET_PROC_RPM;
278 	reg |= TLMM_GPIO_INTR_CFG_INTR_RAW_STATUS_EN;
279 	reg |= TLMM_GPIO_INTR_CFG_INTR_ENABLE;
280 	HWRITE4(sc, TLMM_GPIO_INTR_CFG(pin), reg);
281 
282 	return &sc->sc_pin_ih[pin];
283 }
284 
285 void
qcgpio_fdt_intr_disestablish(void * cookie)286 qcgpio_fdt_intr_disestablish(void *cookie)
287 {
288 	struct qcgpio_intrhand *ih = cookie;
289 
290 	qcgpio_fdt_intr_disable(cookie);
291 	ih->ih_func = NULL;
292 }
293 
294 void
qcgpio_fdt_intr_enable(void * cookie)295 qcgpio_fdt_intr_enable(void *cookie)
296 {
297 	struct qcgpio_intrhand *ih = cookie;
298 	struct qcgpio_softc *sc = ih->ih_sc;
299 	int pin = ih->ih_pin;
300 
301 	if (pin < 0 || pin >= sc->sc_npins)
302 		return;
303 
304 	HSET4(sc, TLMM_GPIO_INTR_CFG(pin),
305 	    TLMM_GPIO_INTR_CFG_INTR_ENABLE);
306 }
307 
308 void
qcgpio_fdt_intr_disable(void * cookie)309 qcgpio_fdt_intr_disable(void *cookie)
310 {
311 	struct qcgpio_intrhand *ih = cookie;
312 	struct qcgpio_softc *sc = ih->ih_sc;
313 	int pin = ih->ih_pin;
314 
315 	if (pin < 0 || pin >= sc->sc_npins)
316 		return;
317 
318 	HCLR4(sc, TLMM_GPIO_INTR_CFG(pin),
319 	    TLMM_GPIO_INTR_CFG_INTR_ENABLE);
320 }
321 
322 void
qcgpio_fdt_intr_barrier(void * cookie)323 qcgpio_fdt_intr_barrier(void *cookie)
324 {
325 	struct qcgpio_intrhand *ih = cookie;
326 	struct qcgpio_softc *sc = ih->ih_sc;
327 
328 	intr_barrier(sc->sc_ih);
329 }
330 
331 int
qcgpio_fdt_intr(void * arg)332 qcgpio_fdt_intr(void *arg)
333 {
334 	struct qcgpio_softc *sc = arg;
335 	int pin, handled = 0;
336 	uint32_t stat;
337 
338 	for (pin = 0; pin < sc->sc_npins; pin++) {
339 		if (sc->sc_pin_ih[pin].ih_func == NULL)
340 			continue;
341 
342 		stat = HREAD4(sc, TLMM_GPIO_INTR_STATUS(pin));
343 		if (stat & TLMM_GPIO_INTR_STATUS_INTR_STATUS) {
344 			sc->sc_pin_ih[pin].ih_func(sc->sc_pin_ih[pin].ih_arg);
345 			handled = 1;
346 		}
347 		HWRITE4(sc, TLMM_GPIO_INTR_STATUS(pin),
348 		    stat & ~TLMM_GPIO_INTR_STATUS_INTR_STATUS);
349 	}
350 
351 	return handled;
352 }
353