xref: /freebsd/sys/arm/broadcom/bcm2835/raspberrypi_gpio.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
5  * Copyright (c) 2012-2015 Luiz Otavio O Souza <loos@FreeBSD.org>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  */
30 #include <sys/cdefs.h>
31 #include "opt_platform.h"
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/bus.h>
36 #include <sys/gpio.h>
37 #include <sys/kernel.h>
38 #include <sys/lock.h>
39 #include <sys/module.h>
40 #include <sys/sx.h>
41 #include <sys/proc.h>
42 
43 #include <dev/gpio/gpiobusvar.h>
44 #include <dev/ofw/ofw_bus.h>
45 
46 #include <arm/broadcom/bcm2835/bcm2835_firmware.h>
47 
48 #include "gpio_if.h"
49 
50 #define	RPI_FW_GPIO_PINS		8
51 #define	RPI_FW_GPIO_BASE		128
52 #define	RPI_FW_GPIO_DEFAULT_CAPS	(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)
53 
54 struct rpi_fw_gpio_softc {
55 	device_t		sc_busdev;
56 	device_t		sc_firmware;
57 	struct sx		sc_sx;
58 	struct gpio_pin		sc_gpio_pins[RPI_FW_GPIO_PINS];
59 	uint8_t			sc_gpio_state;
60 };
61 
62 #define	RPI_FW_GPIO_LOCK(_sc)	sx_xlock(&(_sc)->sc_sx)
63 #define	RPI_FW_GPIO_UNLOCK(_sc)	sx_xunlock(&(_sc)->sc_sx)
64 
65 static struct ofw_compat_data compat_data[] = {
66 	{"raspberrypi,firmware-gpio",	1},
67 	{NULL,				0}
68 };
69 
70 static int
rpi_fw_gpio_pin_configure(struct rpi_fw_gpio_softc * sc,struct gpio_pin * pin,unsigned int flags)71 rpi_fw_gpio_pin_configure(struct rpi_fw_gpio_softc *sc, struct gpio_pin *pin,
72     unsigned int flags)
73 {
74 	union msg_get_gpio_config old_cfg;
75 	union msg_set_gpio_config new_cfg;
76 	int rv;
77 
78 	bzero(&old_cfg, sizeof(old_cfg));
79 	bzero(&new_cfg, sizeof(new_cfg));
80 	old_cfg.req.gpio = RPI_FW_GPIO_BASE + pin->gp_pin;
81 
82 	RPI_FW_GPIO_LOCK(sc);
83 	rv = bcm2835_firmware_property(sc->sc_firmware,
84 	    BCM2835_FIRMWARE_TAG_GET_GPIO_CONFIG, &old_cfg, sizeof(old_cfg));
85 	if (rv == 0 && old_cfg.resp.gpio != 0)
86 		rv = EIO;
87 	if (rv != 0)
88 		goto fail;
89 
90 	new_cfg.req.gpio = RPI_FW_GPIO_BASE + pin->gp_pin;
91 	if (flags & GPIO_PIN_INPUT) {
92 		new_cfg.req.dir = BCM2835_FIRMWARE_GPIO_IN;
93 		new_cfg.req.state = 0;
94 		pin->gp_flags = GPIO_PIN_INPUT;
95 	} else if (flags & GPIO_PIN_OUTPUT) {
96 		new_cfg.req.dir = BCM2835_FIRMWARE_GPIO_OUT;
97 		if (flags & (GPIO_PIN_PRESET_HIGH | GPIO_PIN_PRESET_LOW)) {
98 			if (flags & GPIO_PIN_PRESET_HIGH) {
99 				new_cfg.req.state = 1;
100 				sc->sc_gpio_state |= (1 << pin->gp_pin);
101 			} else {
102 				new_cfg.req.state = 0;
103 				sc->sc_gpio_state &= ~(1 << pin->gp_pin);
104 			}
105 		} else {
106 			if ((sc->sc_gpio_state & (1 << pin->gp_pin)) != 0) {
107 				new_cfg.req.state = 1;
108 			} else {
109 				new_cfg.req.state = 0;
110 			}
111 		}
112 		pin->gp_flags = GPIO_PIN_OUTPUT;
113 	} else {
114 		new_cfg.req.dir = old_cfg.resp.dir;
115 		/* Use the old state to decide high/low */
116 		if ((sc->sc_gpio_state & (1 << pin->gp_pin)) != 0)
117 			new_cfg.req.state = 1;
118 		else
119 			new_cfg.req.state = 0;
120 	}
121 	new_cfg.req.pol = old_cfg.resp.pol;
122 	new_cfg.req.term_en = 0;
123 	new_cfg.req.term_pull_up = 0;
124 
125 	rv = bcm2835_firmware_property(sc->sc_firmware,
126 	    BCM2835_FIRMWARE_TAG_SET_GPIO_CONFIG, &new_cfg, sizeof(new_cfg));
127 
128 fail:
129 	RPI_FW_GPIO_UNLOCK(sc);
130 
131 	return (rv);
132 }
133 
134 static device_t
rpi_fw_gpio_get_bus(device_t dev)135 rpi_fw_gpio_get_bus(device_t dev)
136 {
137 	struct rpi_fw_gpio_softc *sc;
138 
139 	sc = device_get_softc(dev);
140 
141 	return (sc->sc_busdev);
142 }
143 
144 static int
rpi_fw_gpio_pin_max(device_t dev,int * maxpin)145 rpi_fw_gpio_pin_max(device_t dev, int *maxpin)
146 {
147 
148 	*maxpin = RPI_FW_GPIO_PINS - 1;
149 	return (0);
150 }
151 
152 static int
rpi_fw_gpio_pin_getcaps(device_t dev,uint32_t pin,uint32_t * caps)153 rpi_fw_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
154 {
155 	struct rpi_fw_gpio_softc *sc;
156 	int i;
157 
158 	sc = device_get_softc(dev);
159 	for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
160 		if (sc->sc_gpio_pins[i].gp_pin == pin)
161 			break;
162 	}
163 
164 	if (i >= RPI_FW_GPIO_PINS)
165 		return (EINVAL);
166 
167 	*caps = RPI_FW_GPIO_DEFAULT_CAPS;
168 	return (0);
169 }
170 
171 static int
rpi_fw_gpio_pin_getflags(device_t dev,uint32_t pin,uint32_t * flags)172 rpi_fw_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
173 {
174 	struct rpi_fw_gpio_softc *sc = device_get_softc(dev);
175 	int i;
176 
177 	for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
178 		if (sc->sc_gpio_pins[i].gp_pin == pin)
179 			break;
180 	}
181 
182 	if (i >= RPI_FW_GPIO_PINS)
183 		return (EINVAL);
184 
185 	RPI_FW_GPIO_LOCK(sc);
186 	*flags = sc->sc_gpio_pins[i].gp_flags;
187 	RPI_FW_GPIO_UNLOCK(sc);
188 
189 	return (0);
190 }
191 
192 static int
rpi_fw_gpio_pin_getname(device_t dev,uint32_t pin,char * name)193 rpi_fw_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
194 {
195 	struct rpi_fw_gpio_softc *sc;
196 	int i;
197 
198 	sc = device_get_softc(dev);
199 	for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
200 		if (sc->sc_gpio_pins[i].gp_pin == pin)
201 			break;
202 	}
203 
204 	if (i >= RPI_FW_GPIO_PINS)
205 		return (EINVAL);
206 
207 	RPI_FW_GPIO_LOCK(sc);
208 	memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME);
209 	RPI_FW_GPIO_UNLOCK(sc);
210 
211 	return (0);
212 }
213 
214 static int
rpi_fw_gpio_pin_setflags(device_t dev,uint32_t pin,uint32_t flags)215 rpi_fw_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
216 {
217 	struct rpi_fw_gpio_softc *sc;
218 	int i;
219 
220 	sc = device_get_softc(dev);
221 	for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
222 		if (sc->sc_gpio_pins[i].gp_pin == pin)
223 			break;
224 	}
225 
226 	if (i >= RPI_FW_GPIO_PINS)
227 		return (EINVAL);
228 
229 	return (rpi_fw_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags));
230 }
231 
232 static int
rpi_fw_gpio_pin_set(device_t dev,uint32_t pin,unsigned int value)233 rpi_fw_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
234 {
235 	struct rpi_fw_gpio_softc *sc;
236 	union msg_set_gpio_state state;
237 	int i, rv;
238 
239 	sc = device_get_softc(dev);
240 	for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
241 		if (sc->sc_gpio_pins[i].gp_pin == pin)
242 			break;
243 	}
244 	if (i >= RPI_FW_GPIO_PINS)
245 		return (EINVAL);
246 
247 	state.req.gpio = RPI_FW_GPIO_BASE + pin;
248 	state.req.state = value;
249 
250 	RPI_FW_GPIO_LOCK(sc);
251 	rv = bcm2835_firmware_property(sc->sc_firmware,
252 	    BCM2835_FIRMWARE_TAG_SET_GPIO_STATE, &state, sizeof(state));
253 	/* The firmware sets gpio to 0 on success */
254 	if (rv == 0 && state.resp.gpio != 0)
255 		rv = EINVAL;
256 	if (rv == 0) {
257 		sc->sc_gpio_pins[i].gp_flags &= ~(GPIO_PIN_PRESET_HIGH |
258 		    GPIO_PIN_PRESET_LOW);
259 		if (value)
260 			sc->sc_gpio_state |= (1 << i);
261 		else
262 			sc->sc_gpio_state &= ~(1 << i);
263 	}
264 	RPI_FW_GPIO_UNLOCK(sc);
265 
266 	return (rv);
267 }
268 
269 static int
rpi_fw_gpio_pin_get(device_t dev,uint32_t pin,unsigned int * val)270 rpi_fw_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
271 {
272 	struct rpi_fw_gpio_softc *sc;
273 	union msg_get_gpio_state state;
274 	int i, rv;
275 
276 	sc = device_get_softc(dev);
277 	for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
278 		if (sc->sc_gpio_pins[i].gp_pin == pin)
279 			break;
280 	}
281 	if (i >= RPI_FW_GPIO_PINS)
282 		return (EINVAL);
283 
284 	bzero(&state, sizeof(state));
285 	state.req.gpio = RPI_FW_GPIO_BASE + pin;
286 
287 	RPI_FW_GPIO_LOCK(sc);
288 	rv = bcm2835_firmware_property(sc->sc_firmware,
289 	    BCM2835_FIRMWARE_TAG_GET_GPIO_STATE, &state, sizeof(state));
290 	RPI_FW_GPIO_UNLOCK(sc);
291 
292 	/* The firmware sets gpio to 0 on success */
293 	if (rv == 0 && state.resp.gpio != 0)
294 		rv = EINVAL;
295 	if (rv == 0)
296 		*val = !state.resp.state;
297 
298 	return (rv);
299 }
300 
301 static int
rpi_fw_gpio_pin_toggle(device_t dev,uint32_t pin)302 rpi_fw_gpio_pin_toggle(device_t dev, uint32_t pin)
303 {
304 	struct rpi_fw_gpio_softc *sc;
305 	union msg_get_gpio_state old_state;
306 	union msg_set_gpio_state new_state;
307 	int i, rv;
308 
309 	sc = device_get_softc(dev);
310 	for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
311 		if (sc->sc_gpio_pins[i].gp_pin == pin)
312 			break;
313 	}
314 	if (i >= RPI_FW_GPIO_PINS)
315 		return (EINVAL);
316 
317 	bzero(&old_state, sizeof(old_state));
318 	bzero(&new_state, sizeof(new_state));
319 
320 	old_state.req.gpio = RPI_FW_GPIO_BASE + pin;
321 	new_state.req.gpio = RPI_FW_GPIO_BASE + pin;
322 
323 	RPI_FW_GPIO_LOCK(sc);
324 	rv = bcm2835_firmware_property(sc->sc_firmware,
325 	    BCM2835_FIRMWARE_TAG_GET_GPIO_STATE, &old_state, sizeof(old_state));
326 	/* The firmware sets gpio to 0 on success */
327 	if (rv == 0 && old_state.resp.gpio == 0) {
328 		/* Set the new state to invert the GPIO */
329 		new_state.req.state = !old_state.resp.state;
330 		rv = bcm2835_firmware_property(sc->sc_firmware,
331 		    BCM2835_FIRMWARE_TAG_SET_GPIO_STATE, &new_state,
332 		    sizeof(new_state));
333 	}
334 	if (rv == 0 && (old_state.resp.gpio != 0 || new_state.resp.gpio != 0))
335 		rv = EINVAL;
336 	RPI_FW_GPIO_UNLOCK(sc);
337 
338 	return (rv);
339 }
340 
341 static int
rpi_fw_gpio_probe(device_t dev)342 rpi_fw_gpio_probe(device_t dev)
343 {
344 
345 	if (!ofw_bus_status_okay(dev))
346 		return (ENXIO);
347 
348 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
349 		return (ENXIO);
350 
351 	device_set_desc(dev, "Raspberry Pi Firmware GPIO controller");
352 	return (BUS_PROBE_DEFAULT);
353 }
354 
355 static int
rpi_fw_gpio_attach(device_t dev)356 rpi_fw_gpio_attach(device_t dev)
357 {
358 	union msg_get_gpio_config cfg;
359 	struct rpi_fw_gpio_softc *sc;
360 	char *names;
361 	phandle_t gpio;
362 	int i, nelems, elm_pos, rv;
363 
364 	sc = device_get_softc(dev);
365 	sc->sc_firmware = device_get_parent(dev);
366 	sx_init(&sc->sc_sx, "Raspberry Pi firmware gpio");
367 	/* Find our node. */
368 	gpio = ofw_bus_get_node(dev);
369 	if (!OF_hasprop(gpio, "gpio-controller"))
370 		/* This is not a GPIO controller. */
371 		goto fail;
372 
373 	nelems = OF_getprop_alloc(gpio, "gpio-line-names", (void **)&names);
374 	if (nelems <= 0)
375 		names = NULL;
376 	elm_pos = 0;
377 	for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
378 		/* Set the current pin name */
379 		if (names != NULL && elm_pos < nelems &&
380 		    names[elm_pos] != '\0') {
381 			snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
382 			    "%s", names + elm_pos);
383 			/* Find the next pin name */
384 			elm_pos += strlen(names + elm_pos) + 1;
385 		} else {
386 			snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
387 			    "pin %d", i);
388 		}
389 
390 		sc->sc_gpio_pins[i].gp_pin = i;
391 		sc->sc_gpio_pins[i].gp_caps = RPI_FW_GPIO_DEFAULT_CAPS;
392 
393 		bzero(&cfg, sizeof(cfg));
394 		cfg.req.gpio = RPI_FW_GPIO_BASE + i;
395 		rv = bcm2835_firmware_property(sc->sc_firmware,
396 		    BCM2835_FIRMWARE_TAG_GET_GPIO_CONFIG, &cfg, sizeof(cfg));
397 		if (rv == 0 && cfg.resp.gpio == 0) {
398 			if (cfg.resp.dir == BCM2835_FIRMWARE_GPIO_IN)
399 				sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_INPUT;
400 			else
401 				sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_OUTPUT;
402 		} else {
403 			sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_INPUT;
404 		}
405 	}
406 	free(names, M_OFWPROP);
407 	sc->sc_busdev = gpiobus_attach_bus(dev);
408 	if (sc->sc_busdev == NULL)
409 		goto fail;
410 
411 	return (0);
412 
413 fail:
414 	sx_destroy(&sc->sc_sx);
415 
416 	return (ENXIO);
417 }
418 
419 static int
rpi_fw_gpio_detach(device_t dev)420 rpi_fw_gpio_detach(device_t dev)
421 {
422 
423 	return (EBUSY);
424 }
425 
426 static device_method_t rpi_fw_gpio_methods[] = {
427 	/* Device interface */
428 	DEVMETHOD(device_probe,		rpi_fw_gpio_probe),
429 	DEVMETHOD(device_attach,	rpi_fw_gpio_attach),
430 	DEVMETHOD(device_detach,	rpi_fw_gpio_detach),
431 
432 	/* GPIO protocol */
433 	DEVMETHOD(gpio_get_bus,		rpi_fw_gpio_get_bus),
434 	DEVMETHOD(gpio_pin_max,		rpi_fw_gpio_pin_max),
435 	DEVMETHOD(gpio_pin_getname,	rpi_fw_gpio_pin_getname),
436 	DEVMETHOD(gpio_pin_getflags,	rpi_fw_gpio_pin_getflags),
437 	DEVMETHOD(gpio_pin_getcaps,	rpi_fw_gpio_pin_getcaps),
438 	DEVMETHOD(gpio_pin_setflags,	rpi_fw_gpio_pin_setflags),
439 	DEVMETHOD(gpio_pin_get,		rpi_fw_gpio_pin_get),
440 	DEVMETHOD(gpio_pin_set,		rpi_fw_gpio_pin_set),
441 	DEVMETHOD(gpio_pin_toggle,	rpi_fw_gpio_pin_toggle),
442 
443 	DEVMETHOD_END
444 };
445 
446 static driver_t rpi_fw_gpio_driver = {
447 	"gpio",
448 	rpi_fw_gpio_methods,
449 	sizeof(struct rpi_fw_gpio_softc),
450 };
451 
452 EARLY_DRIVER_MODULE(rpi_fw_gpio, bcm2835_firmware, rpi_fw_gpio_driver, 0, 0,
453     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
454