xref: /openbsd/sys/dev/fdt/bcm2835_gpio.c (revision 905646f0)
1 /*	$OpenBSD: bcm2835_gpio.c,v 1.4 2020/06/12 13:21:55 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2020 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/device.h>
20 #include <sys/gpio.h>
21 #include <sys/malloc.h>
22 #include <sys/systm.h>
23 
24 #include <machine/bus.h>
25 #include <machine/fdt.h>
26 
27 #include <dev/gpio/gpiovar.h>
28 #include <dev/ofw/openfirm.h>
29 #include <dev/ofw/ofw_gpio.h>
30 #include <dev/ofw/ofw_pinctrl.h>
31 #include <dev/ofw/fdt.h>
32 
33 #include "gpio.h"
34 
35 /* Registers */
36 #define GPFSEL(n)		(0x00 + ((n) * 4))
37 #define  GPFSEL_MASK		0x7
38 #define  GPFSEL_GPIO_IN		0x0
39 #define  GPFSEL_GPIO_OUT	0x1
40 #define  GPFSEL_ALT0		0x4
41 #define  GPFSEL_ALT1		0x5
42 #define  GPFSEL_ALT2		0x6
43 #define  GPFSEL_ALT3		0x7
44 #define  GPFSEL_ALT4		0x3
45 #define  GPFSEL_ALT5		0x2
46 #define GPSET(n)		(0x1c + ((n) * 4))
47 #define GPCLR(n)		(0x28 + ((n) * 4))
48 #define GPLEV(n)		(0x34 + ((n) * 4))
49 #define GPPUD			0x94
50 #define  GPPUD_PUD		0x3
51 #define  GPPUD_PUD_OFF		0x0
52 #define  GPPUD_PUD_DOWN		0x1
53 #define  GPPUD_PUD_UP		0x2
54 #define GPPUDCLK(n)		(0x98 + ((n) * 4))
55 #define GPPULL(n)		(0xe4 + ((n) * 4))
56 #define  GPPULL_MASK		0x3
57 
58 #define BCMGPIO_MAX_PINS	58
59 
60 #define HREAD4(sc, reg)							\
61 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
62 #define HWRITE4(sc, reg, val)						\
63 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
64 
65 struct bcmgpio_softc {
66 	struct device		sc_dev;
67 	bus_space_tag_t		sc_iot;
68 	bus_space_handle_t	sc_ioh;
69 
70 	void	(*sc_config_pull)(struct bcmgpio_softc *, int, int);
71 	int			sc_num_pins;
72 
73 	struct gpio_controller	sc_gc;
74 
75 	struct gpio_chipset_tag	sc_gpio_tag;
76 	gpio_pin_t		sc_gpio_pins[BCMGPIO_MAX_PINS];
77 	int			sc_gpio_claimed[BCMGPIO_MAX_PINS];
78 };
79 
80 int	bcmgpio_match(struct device *, void *, void *);
81 void	bcmgpio_attach(struct device *, struct device *, void *);
82 
83 struct cfattach	bcmgpio_ca = {
84 	sizeof (struct bcmgpio_softc), bcmgpio_match, bcmgpio_attach
85 };
86 
87 struct cfdriver bcmgpio_cd = {
88 	NULL, "bcmgpio", DV_DULL
89 };
90 
91 void	bcm2711_config_pull(struct bcmgpio_softc *, int, int);
92 void	bcm2835_config_pull(struct bcmgpio_softc *, int, int);
93 int	bcmgpio_pinctrl(uint32_t, void *);
94 void	bcmgpio_config_pin(void *, uint32_t *, int);
95 int	bcmgpio_get_pin(void *, uint32_t *);
96 void	bcmgpio_set_pin(void *, uint32_t *, int);
97 void	bcmgpio_attach_gpio(struct device *);
98 
99 int
100 bcmgpio_match(struct device *parent, void *match, void *aux)
101 {
102 	struct fdt_attach_args *faa = aux;
103 
104 	return (OF_is_compatible(faa->fa_node, "brcm,bcm2711-gpio") ||
105 	    OF_is_compatible(faa->fa_node, "brcm,bcm2835-gpio"));
106 }
107 
108 void
109 bcmgpio_attach(struct device *parent, struct device *self, void *aux)
110 {
111 	struct bcmgpio_softc *sc = (struct bcmgpio_softc *)self;
112 	struct fdt_attach_args *faa = aux;
113 
114 	if (faa->fa_nreg < 1) {
115 		printf(": no registers\n");
116 		return;
117 	}
118 
119 	sc->sc_iot = faa->fa_iot;
120 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
121 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
122 		printf(": can't map registers\n");
123 		return;
124 	}
125 
126 	printf("\n");
127 
128 	if (OF_is_compatible(faa->fa_node, "brcm,bcm2711-gpio")) {
129 	    sc->sc_config_pull = bcm2711_config_pull;
130 	    sc->sc_num_pins = 58;
131 	} else {
132 	    sc->sc_config_pull = bcm2835_config_pull;
133 	    sc->sc_num_pins = 54;
134 	}
135 
136 	pinctrl_register(faa->fa_node, bcmgpio_pinctrl, sc);
137 
138 	sc->sc_gc.gc_node = faa->fa_node;
139 	sc->sc_gc.gc_cookie = sc;
140 	sc->sc_gc.gc_config_pin = bcmgpio_config_pin;
141 	sc->sc_gc.gc_get_pin = bcmgpio_get_pin;
142 	sc->sc_gc.gc_set_pin = bcmgpio_set_pin;
143 	gpio_controller_register(&sc->sc_gc);
144 
145 	config_mountroot(self, bcmgpio_attach_gpio);
146 }
147 
148 void
149 bcmgpio_config_func(struct bcmgpio_softc *sc, int pin, int func)
150 {
151 	int reg = (pin / 10);
152 	int shift = (pin % 10) * 3;
153 	uint32_t val;
154 
155 	val = HREAD4(sc, GPFSEL(reg));
156 	val &= ~(GPFSEL_MASK << shift);
157 	HWRITE4(sc, GPFSEL(reg), val);
158 	val |= ((func & GPFSEL_MASK) << shift);
159 	HWRITE4(sc, GPFSEL(reg), val);
160 }
161 
162 void
163 bcm2711_config_pull(struct bcmgpio_softc *sc, int pin, int pull)
164 {
165 	int reg = (pin / 16);
166 	int shift = (pin % 16) * 2;
167 	uint32_t val;
168 
169 	val = HREAD4(sc, GPPULL(reg));
170 	val &= ~(GPPULL_MASK << shift);
171 	pull = ((pull & 1) << 1) | ((pull & 2) >> 1);
172 	val |= (pull << shift);
173 	HWRITE4(sc, GPPULL(reg), val);
174 }
175 
176 void
177 bcm2835_config_pull(struct bcmgpio_softc *sc, int pin, int pull)
178 {
179 	int reg = (pin / 32);
180 	int shift = (pin % 32);
181 
182 	HWRITE4(sc, GPPUD, pull & GPPUD_PUD);
183 	delay(1);
184 	HWRITE4(sc, GPPUDCLK(reg), 1 << shift);
185 	delay(1);
186 	HWRITE4(sc, GPPUDCLK(reg), 0);
187 }
188 
189 int
190 bcmgpio_pinctrl(uint32_t phandle, void *cookie)
191 {
192 	struct bcmgpio_softc *sc = cookie;
193 	uint32_t *pins, *pull = NULL;
194 	int len, plen = 0;
195 	int node, i;
196 	int func;
197 
198 	node = OF_getnodebyphandle(phandle);
199 	if (node == 0)
200 		return -1;
201 
202 	len = OF_getproplen(node, "brcm,pins");
203 	if (len <= 0)
204 		return -1;
205 
206 	pins = malloc(len, M_TEMP, M_WAITOK);
207 	if (OF_getpropintarray(node, "brcm,pins", pins, len) != len)
208 		goto fail;
209 	func = OF_getpropint(node, "brcm,function", -1);
210 
211 	plen = OF_getproplen(node, "brcm,pull");
212 	if (plen > 0) {
213 		pull = malloc(len, M_TEMP, M_WAITOK);
214 		if (OF_getpropintarray(node, "brcm,pull", pull, plen) != plen)
215 			goto fail;
216 	}
217 
218 	for (i = 0; i < len / sizeof(uint32_t); i++) {
219 		bcmgpio_config_func(sc, pins[i], func);
220 		if (plen > 0 && i < plen / sizeof(uint32_t))
221 			sc->sc_config_pull(sc, pins[i], pull[i]);
222 		sc->sc_gpio_claimed[pins[i]] = 1;
223 	}
224 
225 	free(pull, M_TEMP, plen);
226 	free(pins, M_TEMP, len);
227 	return 0;
228 
229 fail:
230 	free(pull, M_TEMP, plen);
231 	free(pins, M_TEMP, len);
232 	return -1;
233 }
234 
235 void
236 bcmgpio_config_pin(void *cookie, uint32_t *cells, int config)
237 {
238 	struct bcmgpio_softc *sc = cookie;
239 	uint32_t pin = cells[0];
240 
241 	if (pin >= sc->sc_num_pins)
242 		return;
243 
244 	if (config & GPIO_CONFIG_OUTPUT)
245 		bcmgpio_config_func(sc, pin, GPFSEL_GPIO_OUT);
246 	else
247 		bcmgpio_config_func(sc, pin, GPFSEL_GPIO_IN);
248 	if (config & GPIO_CONFIG_PULL_UP)
249 		sc->sc_config_pull(sc, pin, GPPUD_PUD_UP);
250 	else if (config & GPIO_CONFIG_PULL_DOWN)
251 		sc->sc_config_pull(sc, pin, GPPUD_PUD_DOWN);
252 	else
253 		sc->sc_config_pull(sc, pin, GPPUD_PUD_OFF);
254 }
255 
256 int
257 bcmgpio_get_pin(void *cookie, uint32_t *cells)
258 {
259 	struct bcmgpio_softc *sc = cookie;
260 	uint32_t pin = cells[0];
261 	uint32_t flags = cells[1];
262 	uint32_t reg;
263 	int val;
264 
265 	if (pin >= sc->sc_num_pins)
266 		return 0;
267 
268 	reg = HREAD4(sc, GPLEV(pin / 32));
269 	val = (reg >> (pin % 32)) & 1;
270 	if (flags & GPIO_ACTIVE_LOW)
271 		val = !val;
272 	return val;
273 }
274 
275 void
276 bcmgpio_set_pin(void *cookie, uint32_t *cells, int val)
277 {
278 	struct bcmgpio_softc *sc = cookie;
279 	uint32_t pin = cells[0];
280 	uint32_t flags = cells[1];
281 
282 	if (pin >= sc->sc_num_pins)
283 		return;
284 
285 	if (flags & GPIO_ACTIVE_LOW)
286 		val = !val;
287 	if (val)
288 		HWRITE4(sc, GPSET(pin / 32), (1 << (pin % 32)));
289 	else
290 		HWRITE4(sc, GPCLR(pin / 32), (1 << (pin % 32)));
291 }
292 
293 /*
294  * GPIO support code
295  */
296 
297 int	bcmgpio_pin_read(void *, int);
298 void	bcmgpio_pin_write(void *, int, int);
299 void	bcmgpio_pin_ctl(void *, int, int);
300 
301 static const struct gpio_chipset_tag bcmgpio_gpio_tag = {
302 	.gp_pin_read = bcmgpio_pin_read,
303 	.gp_pin_write = bcmgpio_pin_write,
304 	.gp_pin_ctl = bcmgpio_pin_ctl
305 };
306 
307 int
308 bcmgpio_pin_read(void *cookie, int pin)
309 {
310 	struct bcmgpio_softc *sc = cookie;
311 	uint32_t cells[2];
312 
313 	cells[0] = pin;
314 	cells[1] = 0;
315 
316 	return bcmgpio_get_pin(sc, cells) ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
317 }
318 
319 void
320 bcmgpio_pin_write(void *cookie, int pin, int val)
321 {
322 	struct bcmgpio_softc *sc = cookie;
323 	uint32_t cells[2];
324 
325 	cells[0] = pin;
326 	cells[1] = 0;
327 
328 	bcmgpio_set_pin(sc, cells, val);
329 }
330 
331 void
332 bcmgpio_pin_ctl(void *cookie, int pin, int flags)
333 {
334 	struct bcmgpio_softc *sc = cookie;
335 	uint32_t cells[2];
336 	uint32_t config;
337 
338 	cells[0] = pin;
339 	cells[1] = 0;
340 
341 	config = 0;
342 	if (ISSET(flags, GPIO_PIN_OUTPUT))
343 		config |= GPIO_CONFIG_OUTPUT;
344 	if (ISSET(flags, GPIO_PIN_PULLUP))
345 		config |= GPIO_CONFIG_PULL_UP;
346 	if (ISSET(flags, GPIO_PIN_PULLDOWN))
347 		config |= GPIO_CONFIG_PULL_DOWN;
348 
349 	bcmgpio_config_pin(sc, cells, config);
350 }
351 
352 void
353 bcmgpio_attach_gpio(struct device *parent)
354 {
355 	struct bcmgpio_softc *sc = (struct bcmgpio_softc *)parent;
356 	struct gpiobus_attach_args gba;
357 	uint32_t reg;
358 	int func, state, flags;
359 	int pin;
360 
361 	for (pin = 0; pin < sc->sc_num_pins; pin++) {
362 		/* Skip pins claimed by other devices. */
363 		if (sc->sc_gpio_claimed[pin])
364 			continue;
365 
366 		/* Get pin configuration. */
367 		reg = HREAD4(sc, GPFSEL(pin / 10));
368 		func = (reg >> ((pin % 10) * 3)) & GPFSEL_MASK;
369 
370 		switch (func) {
371 		case GPFSEL_GPIO_IN:
372 			flags = GPIO_PIN_SET | GPIO_PIN_INPUT;
373 			break;
374 		case GPFSEL_GPIO_OUT:
375 			flags = GPIO_PIN_SET | GPIO_PIN_OUTPUT;
376 			break;
377 		default:
378 			/* Ignore pins with an assigned function. */
379 			continue;
380 		}
381 
382 		/* Get pin state. */
383 		reg = HREAD4(sc, GPLEV(pin / 32));
384 		state = (reg >> (pin % 32)) & 1;
385 
386 		sc->sc_gpio_pins[pin].pin_caps =
387 		    GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
388 		    GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN;
389 		sc->sc_gpio_pins[pin].pin_flags = flags;
390 		sc->sc_gpio_pins[pin].pin_state = state;
391 		sc->sc_gpio_pins[pin].pin_num = pin;
392 	}
393 
394 	memcpy(&sc->sc_gpio_tag, &bcmgpio_gpio_tag, sizeof(bcmgpio_gpio_tag));
395 	sc->sc_gpio_tag.gp_cookie = sc;
396 
397 	gba.gba_name = "gpio";
398 	gba.gba_gc = &sc->sc_gpio_tag;
399 	gba.gba_pins = &sc->sc_gpio_pins[0];
400 	gba.gba_npins = sc->sc_num_pins;
401 
402 #if NGPIO > 0
403 	config_found(&sc->sc_dev, &gba, gpiobus_print);
404 #endif
405 }
406