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
54struct 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
65static struct ofw_compat_data compat_data[] = {
66	{"raspberrypi,firmware-gpio",	1},
67	{NULL,				0}
68};
69
70static int
71rpi_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
128fail:
129	RPI_FW_GPIO_UNLOCK(sc);
130
131	return (rv);
132}
133
134static device_t
135rpi_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
144static int
145rpi_fw_gpio_pin_max(device_t dev, int *maxpin)
146{
147
148	*maxpin = RPI_FW_GPIO_PINS - 1;
149	return (0);
150}
151
152static int
153rpi_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
171static int
172rpi_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
192static int
193rpi_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
214static int
215rpi_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
232static int
233rpi_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
269static int
270rpi_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
301static int
302rpi_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
341static int
342rpi_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
355static int
356rpi_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
413fail:
414	sx_destroy(&sc->sc_sx);
415
416	return (ENXIO);
417}
418
419static int
420rpi_fw_gpio_detach(device_t dev)
421{
422
423	return (EBUSY);
424}
425
426static 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
446static driver_t rpi_fw_gpio_driver = {
447	"gpio",
448	rpi_fw_gpio_methods,
449	sizeof(struct rpi_fw_gpio_softc),
450};
451
452EARLY_DRIVER_MODULE(rpi_fw_gpio, bcm2835_firmware, rpi_fw_gpio_driver, 0, 0,
453    BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
454