xref: /openbsd/sys/dev/fdt/imxgpio.c (revision 671f311c)
1 /* $OpenBSD: imxgpio.c,v 1.7 2023/03/05 14:45:07 patrick Exp $ */
2 /*
3  * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org>
4  * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/queue.h>
22 #include <sys/device.h>
23 #include <sys/malloc.h>
24 #include <sys/evcount.h>
25 
26 #include <machine/bus.h>
27 #include <machine/fdt.h>
28 #include <machine/intr.h>
29 
30 #include <dev/ofw/openfirm.h>
31 #include <dev/ofw/ofw_gpio.h>
32 #include <dev/ofw/fdt.h>
33 
34 /* iMX6 registers */
35 #define GPIO_DR			0x00
36 #define GPIO_GDIR		0x04
37 #define GPIO_PSR		0x08
38 #define GPIO_ICR1		0x0C
39 #define GPIO_ICR2		0x10
40 #define GPIO_IMR		0x14
41 #define GPIO_ISR		0x18
42 #define GPIO_EDGE_SEL		0x1C
43 
44 #define GPIO_NUM_PINS		32
45 
46 struct intrhand {
47 	int (*ih_func)(void *);		/* handler */
48 	void *ih_arg;			/* arg for handler */
49 	int ih_ipl;			/* IPL_* */
50 	int ih_irq;			/* IRQ number */
51 	int ih_level;			/* GPIO level */
52 	struct evcount	ih_count;
53 	char *ih_name;
54 	void *ih_sc;
55 };
56 
57 struct imxgpio_softc {
58 	struct device		sc_dev;
59 	bus_space_tag_t		sc_iot;
60 	bus_space_handle_t	sc_ioh;
61 	int			sc_node;
62 
63 	void			*sc_ih_h;
64 	void			*sc_ih_l;
65 	int			sc_ipl;
66 	int			sc_irq;
67 	struct intrhand		*sc_handlers[GPIO_NUM_PINS];
68 	struct interrupt_controller sc_ic;
69 
70 	struct gpio_controller sc_gc;
71 };
72 
73 int imxgpio_match(struct device *, void *, void *);
74 void imxgpio_attach(struct device *, struct device *, void *);
75 
76 void imxgpio_config_pin(void *, uint32_t *, int);
77 int imxgpio_get_pin(void *, uint32_t *);
78 void imxgpio_set_pin(void *, uint32_t *, int);
79 
80 int imxgpio_intr(void *);
81 void *imxgpio_intr_establish(void *, int *, int, struct cpu_info *,
82     int (*)(void *), void *, char *);
83 void imxgpio_intr_disestablish(void *);
84 void imxgpio_recalc_ipl(struct imxgpio_softc *);
85 void imxgpio_intr_enable(void *);
86 void imxgpio_intr_disable(void *);
87 void imxgpio_intr_barrier(void *);
88 
89 
90 const struct cfattach	imxgpio_ca = {
91 	sizeof (struct imxgpio_softc), imxgpio_match, imxgpio_attach
92 };
93 
94 struct cfdriver imxgpio_cd = {
95 	NULL, "imxgpio", DV_DULL
96 };
97 
98 int
imxgpio_match(struct device * parent,void * match,void * aux)99 imxgpio_match(struct device *parent, void *match, void *aux)
100 {
101 	struct fdt_attach_args *faa = aux;
102 
103 	return OF_is_compatible(faa->fa_node, "fsl,imx35-gpio");
104 }
105 
106 void
imxgpio_attach(struct device * parent,struct device * self,void * aux)107 imxgpio_attach(struct device *parent, struct device *self, void *aux)
108 {
109 	struct imxgpio_softc *sc = (struct imxgpio_softc *)self;
110 	struct fdt_attach_args *faa = aux;
111 
112 	if (faa->fa_nreg < 1)
113 		return;
114 
115 	sc->sc_node = faa->fa_node;
116 	sc->sc_iot = faa->fa_iot;
117 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
118 	    faa->fa_reg[0].size, 0, &sc->sc_ioh))
119 		panic("imxgpio_attach: bus_space_map failed!");
120 
121 	sc->sc_gc.gc_node = faa->fa_node;
122 	sc->sc_gc.gc_cookie = sc;
123 	sc->sc_gc.gc_config_pin = imxgpio_config_pin;
124 	sc->sc_gc.gc_get_pin = imxgpio_get_pin;
125 	sc->sc_gc.gc_set_pin = imxgpio_set_pin;
126 	gpio_controller_register(&sc->sc_gc);
127 
128 	sc->sc_ipl = IPL_NONE;
129 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR, 0);
130 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_ISR, ~0);
131 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_EDGE_SEL, 0);
132 
133 	sc->sc_ic.ic_node = faa->fa_node;
134 	sc->sc_ic.ic_cookie = sc;
135 	sc->sc_ic.ic_establish = imxgpio_intr_establish;
136 	sc->sc_ic.ic_disestablish = imxgpio_intr_disestablish;
137 	sc->sc_ic.ic_enable = imxgpio_intr_enable;
138 	sc->sc_ic.ic_disable = imxgpio_intr_disable;
139 	sc->sc_ic.ic_barrier = imxgpio_intr_barrier;
140 	fdt_intr_register(&sc->sc_ic);
141 
142 	printf("\n");
143 
144 	/* XXX - SYSCONFIG */
145 	/* XXX - CTRL */
146 	/* XXX - DEBOUNCE */
147 }
148 
149 void
imxgpio_config_pin(void * cookie,uint32_t * cells,int config)150 imxgpio_config_pin(void *cookie, uint32_t *cells, int config)
151 {
152 	struct imxgpio_softc *sc = cookie;
153 	uint32_t pin = cells[0];
154 	uint32_t val;
155 
156 	if (pin >= GPIO_NUM_PINS)
157 		return;
158 
159 	val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_GDIR);
160 	if (config & GPIO_CONFIG_OUTPUT)
161 		val |= 1 << pin;
162 	else
163 		val &= ~(1 << pin);
164 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_GDIR, val);
165 }
166 
167 int
imxgpio_get_pin(void * cookie,uint32_t * cells)168 imxgpio_get_pin(void *cookie, uint32_t *cells)
169 {
170 	struct imxgpio_softc *sc = cookie;
171 	uint32_t pin = cells[0];
172 	uint32_t flags = cells[1];
173 	uint32_t reg;
174 	int val;
175 
176 	reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_DR);
177 	reg &= (1 << pin);
178 	val = (reg >> pin) & 1;
179 	if (flags & GPIO_ACTIVE_LOW)
180 		val = !val;
181 	return val;
182 }
183 
184 void
imxgpio_set_pin(void * cookie,uint32_t * cells,int val)185 imxgpio_set_pin(void *cookie, uint32_t *cells, int val)
186 {
187 	struct imxgpio_softc *sc = cookie;
188 	uint32_t pin = cells[0];
189 	uint32_t flags = cells[1];
190 	uint32_t reg;
191 
192 	reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_DR);
193 	if (flags & GPIO_ACTIVE_LOW)
194 		val = !val;
195 	if (val)
196 		reg |= (1 << pin);
197 	else
198 		reg &= ~(1 << pin);
199 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_DR, reg);
200 }
201 
202 int
imxgpio_intr(void * cookie)203 imxgpio_intr(void *cookie)
204 {
205 	struct imxgpio_softc	*sc = (struct imxgpio_softc *)cookie;
206 	struct intrhand		*ih;
207 	uint32_t		 status, pending, mask;
208 	int			 pin, s;
209 
210 	status = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_ISR);
211 	mask = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR);
212 
213 	status &= mask;
214 	pending = status;
215 
216 	while (pending) {
217 		pin = ffs(pending) - 1;
218 
219 		if ((ih = sc->sc_handlers[pin]) != NULL) {
220 			s = splraise(ih->ih_ipl);
221 			if (ih->ih_func(ih->ih_arg))
222 				ih->ih_count.ec_count++;
223 			splx(s);
224 		}
225 
226 		pending &= ~(1 << pin);
227 	}
228 
229 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_ISR, status);
230 
231 	return 1;
232 }
233 
234 void *
imxgpio_intr_establish(void * cookie,int * cells,int ipl,struct cpu_info * ci,int (* func)(void *),void * arg,char * name)235 imxgpio_intr_establish(void *cookie, int *cells, int ipl,
236     struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
237 {
238 	struct imxgpio_softc	*sc = (struct imxgpio_softc *)cookie;
239 	struct intrhand		*ih;
240 	int			 s, val, reg, shift;
241 	int			 irqno = cells[0];
242 	int			 level = cells[1];
243 
244 	if (irqno < 0 || irqno >= GPIO_NUM_PINS)
245 		panic("%s: bogus irqnumber %d: %s", __func__,
246 		     irqno, name);
247 
248 	if (sc->sc_handlers[irqno] != NULL)
249 		panic("%s: irqnumber %d reused: %s", __func__,
250 		     irqno, name);
251 
252 	if (ci != NULL && !CPU_IS_PRIMARY(ci))
253 		return NULL;
254 
255 	ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
256 	ih->ih_func = func;
257 	ih->ih_arg = arg;
258 	ih->ih_ipl = ipl & IPL_IRQMASK;
259 	ih->ih_irq = irqno;
260 	ih->ih_name = name;
261 	ih->ih_level = level;
262 	ih->ih_sc = sc;
263 
264 	s = splhigh();
265 
266 	sc->sc_handlers[irqno] = ih;
267 
268 	if (name != NULL)
269 		evcount_attach(&ih->ih_count, name, &ih->ih_irq);
270 
271 #ifdef DEBUG_INTC
272 	printf("%s: irq %d ipl %d [%s]\n", __func__, ih->ih_irq, ih->ih_ipl,
273 	    ih->ih_name);
274 #endif
275 
276 	imxgpio_recalc_ipl(sc);
277 
278 	switch (level) {
279 		case 1: /* rising */
280 			val = 2;
281 			break;
282 		case 2: /* falling */
283 			val = 3;
284 			break;
285 		case 4: /* high */
286 			val = 1;
287 			break;
288 		case 8: /* low */
289 			val = 0;
290 			break;
291 		default:
292 			panic("%s: unsupported trigger type", __func__);
293 	}
294 
295 	if (irqno < 16) {
296 		reg = GPIO_ICR1;
297 		shift = irqno << 1;
298 	} else {
299 		reg = GPIO_ICR2;
300 		shift = (irqno - 16) << 1;
301 	}
302 
303 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, reg,
304 	    bus_space_read_4(sc->sc_iot, sc->sc_ioh, reg) & ~(0x3 << shift));
305 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, reg,
306 	    bus_space_read_4(sc->sc_iot, sc->sc_ioh, reg) | val << shift);
307 
308 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR,
309 	    bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR) | 1 << irqno);
310 
311 	splx(s);
312 	return (ih);
313 }
314 
315 void
imxgpio_intr_disestablish(void * cookie)316 imxgpio_intr_disestablish(void *cookie)
317 {
318 	struct intrhand		*ih = cookie;
319 	struct imxgpio_softc	*sc = ih->ih_sc;
320 	uint32_t		 mask;
321 	int			 s;
322 
323 	s = splhigh();
324 
325 #ifdef DEBUG_INTC
326 	printf("%s: irq %d ipl %d [%s]\n", __func__, ih->ih_irq, ih->ih_ipl,
327 	    ih->ih_name);
328 #endif
329 
330 	mask = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR);
331 	mask &= ~(1 << ih->ih_irq);
332 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR, mask);
333 
334 	sc->sc_handlers[ih->ih_irq] = NULL;
335 	if (ih->ih_name != NULL)
336 		evcount_detach(&ih->ih_count);
337 	free(ih, M_DEVBUF, sizeof(*ih));
338 
339 	imxgpio_recalc_ipl(sc);
340 
341 	splx(s);
342 }
343 
344 void
imxgpio_recalc_ipl(struct imxgpio_softc * sc)345 imxgpio_recalc_ipl(struct imxgpio_softc *sc)
346 {
347 	struct intrhand		*ih;
348 	int			 pin;
349 	int			 max = IPL_NONE;
350 	int			 min = IPL_HIGH;
351 
352 	for (pin = 0; pin < GPIO_NUM_PINS; pin++) {
353 		ih = sc->sc_handlers[pin];
354 		if (ih == NULL)
355 			continue;
356 
357 		if (ih->ih_ipl > max)
358 			max = ih->ih_ipl;
359 
360 		if (ih->ih_ipl < min)
361 			min = ih->ih_ipl;
362 	}
363 
364 	if (max == IPL_NONE)
365 		min = IPL_NONE;
366 
367 	if (sc->sc_ipl != max) {
368 		sc->sc_ipl = max;
369 
370 		if (sc->sc_ih_l != NULL)
371 			fdt_intr_disestablish(sc->sc_ih_l);
372 
373 		if (sc->sc_ih_h != NULL)
374 			fdt_intr_disestablish(sc->sc_ih_h);
375 
376 		if (sc->sc_ipl != IPL_NONE) {
377 			sc->sc_ih_l = fdt_intr_establish_idx(sc->sc_node, 0,
378 			    sc->sc_ipl, imxgpio_intr, sc, sc->sc_dev.dv_xname);
379 			sc->sc_ih_h = fdt_intr_establish_idx(sc->sc_node, 1,
380 			    sc->sc_ipl, imxgpio_intr, sc, sc->sc_dev.dv_xname);
381 		}
382 	}
383 }
384 
385 void
imxgpio_intr_enable(void * cookie)386 imxgpio_intr_enable(void *cookie)
387 {
388 	struct intrhand		*ih = cookie;
389 	struct imxgpio_softc	*sc = ih->ih_sc;
390 	uint32_t		 mask;
391 	int			 s;
392 
393 	s = splhigh();
394 	mask = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR);
395 	mask |= (1 << ih->ih_irq);
396 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR, mask);
397 	splx(s);
398 }
399 
400 void
imxgpio_intr_disable(void * cookie)401 imxgpio_intr_disable(void *cookie)
402 {
403 	struct intrhand		*ih = cookie;
404 	struct imxgpio_softc	*sc = ih->ih_sc;
405 	uint32_t		 mask;
406 	int			 s;
407 
408 	s = splhigh();
409 	mask = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR);
410 	mask &= ~(1 << ih->ih_irq);
411 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR, mask);
412 	splx(s);
413 }
414 
415 void
imxgpio_intr_barrier(void * cookie)416 imxgpio_intr_barrier(void *cookie)
417 {
418 	struct intrhand		*ih = cookie;
419 	struct imxgpio_softc	*sc = ih->ih_sc;
420 
421 	if (sc->sc_ih_h && ih->ih_irq >= 16)
422 		intr_barrier(sc->sc_ih_h);
423 	else if (sc->sc_ih_l)
424 		intr_barrier(sc->sc_ih_l);
425 }
426