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