xref: /openbsd-src/sys/dev/fdt/imxgpio.c (revision 99fd087599a8791921855f21bd7e36130f39aadc)
1 /* $OpenBSD: imxgpio.c,v 1.3 2018/08/08 11:06:47 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, int (*)(void *),
82     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 
88 
89 struct cfattach	imxgpio_ca = {
90 	sizeof (struct imxgpio_softc), imxgpio_match, imxgpio_attach
91 };
92 
93 struct cfdriver imxgpio_cd = {
94 	NULL, "imxgpio", DV_DULL
95 };
96 
97 int
98 imxgpio_match(struct device *parent, void *match, void *aux)
99 {
100 	struct fdt_attach_args *faa = aux;
101 
102 	return OF_is_compatible(faa->fa_node, "fsl,imx35-gpio");
103 }
104 
105 void
106 imxgpio_attach(struct device *parent, struct device *self, void *aux)
107 {
108 	struct imxgpio_softc *sc = (struct imxgpio_softc *)self;
109 	struct fdt_attach_args *faa = aux;
110 
111 	if (faa->fa_nreg < 1)
112 		return;
113 
114 	sc->sc_node = faa->fa_node;
115 	sc->sc_iot = faa->fa_iot;
116 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
117 	    faa->fa_reg[0].size, 0, &sc->sc_ioh))
118 		panic("imxgpio_attach: bus_space_map failed!");
119 
120 	sc->sc_gc.gc_node = faa->fa_node;
121 	sc->sc_gc.gc_cookie = sc;
122 	sc->sc_gc.gc_config_pin = imxgpio_config_pin;
123 	sc->sc_gc.gc_get_pin = imxgpio_get_pin;
124 	sc->sc_gc.gc_set_pin = imxgpio_set_pin;
125 	gpio_controller_register(&sc->sc_gc);
126 
127 	sc->sc_ipl = IPL_NONE;
128 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR, 0);
129 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_ISR, ~0);
130 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_EDGE_SEL, 0);
131 
132 	sc->sc_ic.ic_node = faa->fa_node;
133 	sc->sc_ic.ic_cookie = sc;
134 	sc->sc_ic.ic_establish = imxgpio_intr_establish;
135 	sc->sc_ic.ic_disestablish = imxgpio_intr_disestablish;
136 	sc->sc_ic.ic_enable = imxgpio_intr_enable;
137 	sc->sc_ic.ic_disable = imxgpio_intr_disable;
138 	fdt_intr_register(&sc->sc_ic);
139 
140 	printf("\n");
141 
142 	/* XXX - SYSCONFIG */
143 	/* XXX - CTRL */
144 	/* XXX - DEBOUNCE */
145 }
146 
147 void
148 imxgpio_config_pin(void *cookie, uint32_t *cells, int config)
149 {
150 	struct imxgpio_softc *sc = cookie;
151 	uint32_t pin = cells[0];
152 	uint32_t val;
153 
154 	if (pin >= GPIO_NUM_PINS)
155 		return;
156 
157 	val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_GDIR);
158 	if (config & GPIO_CONFIG_OUTPUT)
159 		val |= 1 << pin;
160 	else
161 		val &= ~(1 << pin);
162 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_GDIR, val);
163 }
164 
165 int
166 imxgpio_get_pin(void *cookie, uint32_t *cells)
167 {
168 	struct imxgpio_softc *sc = cookie;
169 	uint32_t pin = cells[0];
170 	uint32_t flags = cells[1];
171 	uint32_t reg;
172 	int val;
173 
174 	reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_DR);
175 	reg &= (1 << pin);
176 	val = (reg >> pin) & 1;
177 	if (flags & GPIO_ACTIVE_LOW)
178 		val = !val;
179 	return val;
180 }
181 
182 void
183 imxgpio_set_pin(void *cookie, uint32_t *cells, int val)
184 {
185 	struct imxgpio_softc *sc = cookie;
186 	uint32_t pin = cells[0];
187 	uint32_t flags = cells[1];
188 	uint32_t reg;
189 
190 	reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_DR);
191 	if (flags & GPIO_ACTIVE_LOW)
192 		val = !val;
193 	if (val)
194 		reg |= (1 << pin);
195 	else
196 		reg &= ~(1 << pin);
197 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_DR, reg);
198 }
199 
200 int
201 imxgpio_intr(void *cookie)
202 {
203 	struct imxgpio_softc	*sc = (struct imxgpio_softc *)cookie;
204 	struct intrhand		*ih;
205 	uint32_t		 status, pending, mask;
206 	int			 pin, s;
207 
208 	status = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_ISR);
209 	mask = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR);
210 
211 	status &= mask;
212 	pending = status;
213 
214 	while (pending) {
215 		pin = ffs(pending) - 1;
216 
217 		if ((ih = sc->sc_handlers[pin]) != NULL) {
218 			s = splraise(ih->ih_ipl);
219 			if (ih->ih_func(ih->ih_arg))
220 				ih->ih_count.ec_count++;
221 			splx(s);
222 		}
223 
224 		pending &= ~(1 << pin);
225 	}
226 
227 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_ISR, status);
228 
229 	return 1;
230 }
231 
232 void *
233 imxgpio_intr_establish(void *cookie, int *cells, int ipl,
234     int (*func)(void *), void *arg, char *name)
235 {
236 	struct imxgpio_softc	*sc = (struct imxgpio_softc *)cookie;
237 	struct intrhand		*ih;
238 	int			 s, val, reg, shift;
239 	int			 irqno = cells[0];
240 	int			 level = cells[1];
241 
242 	if (irqno < 0 || irqno >= GPIO_NUM_PINS)
243 		panic("%s: bogus irqnumber %d: %s", __func__,
244 		     irqno, name);
245 
246 	if (sc->sc_handlers[irqno] != NULL)
247 		panic("%s: irqnumber %d reused: %s", __func__,
248 		     irqno, name);
249 
250 	ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
251 	ih->ih_func = func;
252 	ih->ih_arg = arg;
253 	ih->ih_ipl = ipl;
254 	ih->ih_irq = irqno;
255 	ih->ih_name = name;
256 	ih->ih_level = level;
257 	ih->ih_sc = sc;
258 
259 	s = splhigh();
260 
261 	sc->sc_handlers[irqno] = ih;
262 
263 	if (name != NULL)
264 		evcount_attach(&ih->ih_count, name, &ih->ih_irq);
265 
266 #ifdef DEBUG_INTC
267 	printf("%s: irq %d ipl %d [%s]\n", __func__, ih->ih_irq, ih->ih_ipl,
268 	    ih->ih_name);
269 #endif
270 
271 	imxgpio_recalc_ipl(sc);
272 
273 	switch (level) {
274 		case 1: /* rising */
275 			val = 2;
276 			break;
277 		case 2: /* falling */
278 			val = 3;
279 			break;
280 		case 4: /* high */
281 			val = 1;
282 			break;
283 		case 8: /* low */
284 			val = 0;
285 			break;
286 		default:
287 			panic("%s: unsupported trigger type", __func__);
288 	}
289 
290 	if (irqno < 16) {
291 		reg = GPIO_ICR1;
292 		shift = irqno << 1;
293 	} else {
294 		reg = GPIO_ICR2;
295 		shift = (irqno - 16) << 1;
296 	}
297 
298 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, reg,
299 	    bus_space_read_4(sc->sc_iot, sc->sc_ioh, reg) & ~(0x3 << shift));
300 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, reg,
301 	    bus_space_read_4(sc->sc_iot, sc->sc_ioh, reg) | val << shift);
302 
303 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR,
304 	    bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR) | 1 << irqno);
305 
306 	splx(s);
307 	return (ih);
308 }
309 
310 void
311 imxgpio_intr_disestablish(void *cookie)
312 {
313 	struct intrhand		*ih = cookie;
314 	struct imxgpio_softc	*sc = ih->ih_sc;
315 	uint32_t		 mask;
316 	int			 s;
317 
318 	s = splhigh();
319 
320 #ifdef DEBUG_INTC
321 	printf("%s: irq %d ipl %d [%s]\n", __func__, ih->ih_irq, ih->ih_ipl,
322 	    ih->ih_name);
323 #endif
324 
325 	mask = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR);
326 	mask &= ~(1 << ih->ih_irq);
327 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR, mask);
328 
329 	sc->sc_handlers[ih->ih_irq] = NULL;
330 	if (ih->ih_name != NULL)
331 		evcount_detach(&ih->ih_count);
332 	free(ih, M_DEVBUF, sizeof(*ih));
333 
334 	imxgpio_recalc_ipl(sc);
335 
336 	splx(s);
337 }
338 
339 void
340 imxgpio_recalc_ipl(struct imxgpio_softc *sc)
341 {
342 	struct intrhand		*ih;
343 	int			 pin;
344 	int			 max = IPL_NONE;
345 	int			 min = IPL_HIGH;
346 
347 	for (pin = 0; pin < GPIO_NUM_PINS; pin++) {
348 		ih = sc->sc_handlers[pin];
349 		if (ih == NULL)
350 			continue;
351 
352 		if (ih->ih_ipl > max)
353 			max = ih->ih_ipl;
354 
355 		if (ih->ih_ipl < min)
356 			min = ih->ih_ipl;
357 	}
358 
359 	if (max == IPL_NONE)
360 		min = IPL_NONE;
361 
362 	if (sc->sc_ipl != max) {
363 		sc->sc_ipl = max;
364 
365 		if (sc->sc_ih_l != NULL)
366 			fdt_intr_disestablish(sc->sc_ih_l);
367 
368 		if (sc->sc_ih_h != NULL)
369 			fdt_intr_disestablish(sc->sc_ih_h);
370 
371 		if (sc->sc_ipl != IPL_NONE) {
372 			sc->sc_ih_l = fdt_intr_establish_idx(sc->sc_node, 0,
373 			    sc->sc_ipl, imxgpio_intr, sc, sc->sc_dev.dv_xname);
374 			sc->sc_ih_h = fdt_intr_establish_idx(sc->sc_node, 1,
375 			    sc->sc_ipl, imxgpio_intr, sc, sc->sc_dev.dv_xname);
376 		}
377 	}
378 }
379 
380 void
381 imxgpio_intr_enable(void *cookie)
382 {
383 	struct intrhand		*ih = cookie;
384 	struct imxgpio_softc	*sc = ih->ih_sc;
385 	uint32_t		 mask;
386 	int			 s;
387 
388 	s = splhigh();
389 	mask = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR);
390 	mask |= (1 << ih->ih_irq);
391 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR, mask);
392 	splx(s);
393 }
394 
395 void
396 imxgpio_intr_disable(void *cookie)
397 {
398 	struct intrhand		*ih = cookie;
399 	struct imxgpio_softc	*sc = ih->ih_sc;
400 	uint32_t		 mask;
401 	int			 s;
402 
403 	s = splhigh();
404 	mask = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR);
405 	mask &= ~(1 << ih->ih_irq);
406 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_IMR, mask);
407 	splx(s);
408 }
409