xref: /freebsd/sys/arm/broadcom/bcm2835/raspberrypi_gpio.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
19ca3eaf0SAndrew Turner /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
39ca3eaf0SAndrew Turner  *
49ca3eaf0SAndrew Turner  * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
59ca3eaf0SAndrew Turner  * Copyright (c) 2012-2015 Luiz Otavio O Souza <loos@FreeBSD.org>
69ca3eaf0SAndrew Turner  * All rights reserved.
79ca3eaf0SAndrew Turner  *
89ca3eaf0SAndrew Turner  * Redistribution and use in source and binary forms, with or without
99ca3eaf0SAndrew Turner  * modification, are permitted provided that the following conditions
109ca3eaf0SAndrew Turner  * are met:
119ca3eaf0SAndrew Turner  * 1. Redistributions of source code must retain the above copyright
129ca3eaf0SAndrew Turner  *    notice, this list of conditions and the following disclaimer.
139ca3eaf0SAndrew Turner  * 2. Redistributions in binary form must reproduce the above copyright
149ca3eaf0SAndrew Turner  *    notice, this list of conditions and the following disclaimer in the
159ca3eaf0SAndrew Turner  *    documentation and/or other materials provided with the distribution.
169ca3eaf0SAndrew Turner  *
179ca3eaf0SAndrew Turner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
189ca3eaf0SAndrew Turner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
199ca3eaf0SAndrew Turner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
209ca3eaf0SAndrew Turner  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
219ca3eaf0SAndrew Turner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
229ca3eaf0SAndrew Turner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
239ca3eaf0SAndrew Turner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
249ca3eaf0SAndrew Turner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
259ca3eaf0SAndrew Turner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
269ca3eaf0SAndrew Turner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
279ca3eaf0SAndrew Turner  * SUCH DAMAGE.
289ca3eaf0SAndrew Turner  *
299ca3eaf0SAndrew Turner  */
309ca3eaf0SAndrew Turner #include <sys/cdefs.h>
319ca3eaf0SAndrew Turner #include "opt_platform.h"
329ca3eaf0SAndrew Turner 
339ca3eaf0SAndrew Turner #include <sys/param.h>
349ca3eaf0SAndrew Turner #include <sys/systm.h>
359ca3eaf0SAndrew Turner #include <sys/bus.h>
369ca3eaf0SAndrew Turner #include <sys/gpio.h>
379ca3eaf0SAndrew Turner #include <sys/kernel.h>
389ca3eaf0SAndrew Turner #include <sys/lock.h>
399ca3eaf0SAndrew Turner #include <sys/module.h>
409ca3eaf0SAndrew Turner #include <sys/sx.h>
419ca3eaf0SAndrew Turner #include <sys/proc.h>
429ca3eaf0SAndrew Turner 
439ca3eaf0SAndrew Turner #include <dev/gpio/gpiobusvar.h>
449ca3eaf0SAndrew Turner #include <dev/ofw/ofw_bus.h>
459ca3eaf0SAndrew Turner 
469ca3eaf0SAndrew Turner #include <arm/broadcom/bcm2835/bcm2835_firmware.h>
479ca3eaf0SAndrew Turner 
489ca3eaf0SAndrew Turner #include "gpio_if.h"
499ca3eaf0SAndrew Turner 
509ca3eaf0SAndrew Turner #define	RPI_FW_GPIO_PINS		8
519ca3eaf0SAndrew Turner #define	RPI_FW_GPIO_BASE		128
529ca3eaf0SAndrew Turner #define	RPI_FW_GPIO_DEFAULT_CAPS	(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)
539ca3eaf0SAndrew Turner 
549ca3eaf0SAndrew Turner struct rpi_fw_gpio_softc {
559ca3eaf0SAndrew Turner 	device_t		sc_busdev;
569ca3eaf0SAndrew Turner 	device_t		sc_firmware;
579ca3eaf0SAndrew Turner 	struct sx		sc_sx;
589ca3eaf0SAndrew Turner 	struct gpio_pin		sc_gpio_pins[RPI_FW_GPIO_PINS];
599ca3eaf0SAndrew Turner 	uint8_t			sc_gpio_state;
609ca3eaf0SAndrew Turner };
619ca3eaf0SAndrew Turner 
629ca3eaf0SAndrew Turner #define	RPI_FW_GPIO_LOCK(_sc)	sx_xlock(&(_sc)->sc_sx)
639ca3eaf0SAndrew Turner #define	RPI_FW_GPIO_UNLOCK(_sc)	sx_xunlock(&(_sc)->sc_sx)
649ca3eaf0SAndrew Turner 
659ca3eaf0SAndrew Turner static struct ofw_compat_data compat_data[] = {
669ca3eaf0SAndrew Turner 	{"raspberrypi,firmware-gpio",	1},
679ca3eaf0SAndrew Turner 	{NULL,				0}
689ca3eaf0SAndrew Turner };
699ca3eaf0SAndrew Turner 
709ca3eaf0SAndrew Turner static int
rpi_fw_gpio_pin_configure(struct rpi_fw_gpio_softc * sc,struct gpio_pin * pin,unsigned int flags)719ca3eaf0SAndrew Turner rpi_fw_gpio_pin_configure(struct rpi_fw_gpio_softc *sc, struct gpio_pin *pin,
729ca3eaf0SAndrew Turner     unsigned int flags)
739ca3eaf0SAndrew Turner {
749ca3eaf0SAndrew Turner 	union msg_get_gpio_config old_cfg;
759ca3eaf0SAndrew Turner 	union msg_set_gpio_config new_cfg;
769ca3eaf0SAndrew Turner 	int rv;
779ca3eaf0SAndrew Turner 
789ca3eaf0SAndrew Turner 	bzero(&old_cfg, sizeof(old_cfg));
799ca3eaf0SAndrew Turner 	bzero(&new_cfg, sizeof(new_cfg));
809ca3eaf0SAndrew Turner 	old_cfg.req.gpio = RPI_FW_GPIO_BASE + pin->gp_pin;
819ca3eaf0SAndrew Turner 
829ca3eaf0SAndrew Turner 	RPI_FW_GPIO_LOCK(sc);
839ca3eaf0SAndrew Turner 	rv = bcm2835_firmware_property(sc->sc_firmware,
849ca3eaf0SAndrew Turner 	    BCM2835_FIRMWARE_TAG_GET_GPIO_CONFIG, &old_cfg, sizeof(old_cfg));
859ca3eaf0SAndrew Turner 	if (rv == 0 && old_cfg.resp.gpio != 0)
869ca3eaf0SAndrew Turner 		rv = EIO;
879ca3eaf0SAndrew Turner 	if (rv != 0)
889ca3eaf0SAndrew Turner 		goto fail;
899ca3eaf0SAndrew Turner 
909ca3eaf0SAndrew Turner 	new_cfg.req.gpio = RPI_FW_GPIO_BASE + pin->gp_pin;
919ca3eaf0SAndrew Turner 	if (flags & GPIO_PIN_INPUT) {
929ca3eaf0SAndrew Turner 		new_cfg.req.dir = BCM2835_FIRMWARE_GPIO_IN;
939ca3eaf0SAndrew Turner 		new_cfg.req.state = 0;
949ca3eaf0SAndrew Turner 		pin->gp_flags = GPIO_PIN_INPUT;
959ca3eaf0SAndrew Turner 	} else if (flags & GPIO_PIN_OUTPUT) {
969ca3eaf0SAndrew Turner 		new_cfg.req.dir = BCM2835_FIRMWARE_GPIO_OUT;
979ca3eaf0SAndrew Turner 		if (flags & (GPIO_PIN_PRESET_HIGH | GPIO_PIN_PRESET_LOW)) {
989ca3eaf0SAndrew Turner 			if (flags & GPIO_PIN_PRESET_HIGH) {
999ca3eaf0SAndrew Turner 				new_cfg.req.state = 1;
1009ca3eaf0SAndrew Turner 				sc->sc_gpio_state |= (1 << pin->gp_pin);
1019ca3eaf0SAndrew Turner 			} else {
1029ca3eaf0SAndrew Turner 				new_cfg.req.state = 0;
1039ca3eaf0SAndrew Turner 				sc->sc_gpio_state &= ~(1 << pin->gp_pin);
1049ca3eaf0SAndrew Turner 			}
1059ca3eaf0SAndrew Turner 		} else {
1069ca3eaf0SAndrew Turner 			if ((sc->sc_gpio_state & (1 << pin->gp_pin)) != 0) {
1079ca3eaf0SAndrew Turner 				new_cfg.req.state = 1;
1089ca3eaf0SAndrew Turner 			} else {
1099ca3eaf0SAndrew Turner 				new_cfg.req.state = 0;
1109ca3eaf0SAndrew Turner 			}
1119ca3eaf0SAndrew Turner 		}
1129ca3eaf0SAndrew Turner 		pin->gp_flags = GPIO_PIN_OUTPUT;
1139ca3eaf0SAndrew Turner 	} else {
1149ca3eaf0SAndrew Turner 		new_cfg.req.dir = old_cfg.resp.dir;
1159ca3eaf0SAndrew Turner 		/* Use the old state to decide high/low */
1169ca3eaf0SAndrew Turner 		if ((sc->sc_gpio_state & (1 << pin->gp_pin)) != 0)
1179ca3eaf0SAndrew Turner 			new_cfg.req.state = 1;
1189ca3eaf0SAndrew Turner 		else
1199ca3eaf0SAndrew Turner 			new_cfg.req.state = 0;
1209ca3eaf0SAndrew Turner 	}
1219ca3eaf0SAndrew Turner 	new_cfg.req.pol = old_cfg.resp.pol;
1229ca3eaf0SAndrew Turner 	new_cfg.req.term_en = 0;
1239ca3eaf0SAndrew Turner 	new_cfg.req.term_pull_up = 0;
1249ca3eaf0SAndrew Turner 
1259ca3eaf0SAndrew Turner 	rv = bcm2835_firmware_property(sc->sc_firmware,
1269ca3eaf0SAndrew Turner 	    BCM2835_FIRMWARE_TAG_SET_GPIO_CONFIG, &new_cfg, sizeof(new_cfg));
1279ca3eaf0SAndrew Turner 
1289ca3eaf0SAndrew Turner fail:
1299ca3eaf0SAndrew Turner 	RPI_FW_GPIO_UNLOCK(sc);
1309ca3eaf0SAndrew Turner 
1319ca3eaf0SAndrew Turner 	return (rv);
1329ca3eaf0SAndrew Turner }
1339ca3eaf0SAndrew Turner 
1349ca3eaf0SAndrew Turner static device_t
rpi_fw_gpio_get_bus(device_t dev)1359ca3eaf0SAndrew Turner rpi_fw_gpio_get_bus(device_t dev)
1369ca3eaf0SAndrew Turner {
1379ca3eaf0SAndrew Turner 	struct rpi_fw_gpio_softc *sc;
1389ca3eaf0SAndrew Turner 
1399ca3eaf0SAndrew Turner 	sc = device_get_softc(dev);
1409ca3eaf0SAndrew Turner 
1419ca3eaf0SAndrew Turner 	return (sc->sc_busdev);
1429ca3eaf0SAndrew Turner }
1439ca3eaf0SAndrew Turner 
1449ca3eaf0SAndrew Turner static int
rpi_fw_gpio_pin_max(device_t dev,int * maxpin)1459ca3eaf0SAndrew Turner rpi_fw_gpio_pin_max(device_t dev, int *maxpin)
1469ca3eaf0SAndrew Turner {
1479ca3eaf0SAndrew Turner 
1489ca3eaf0SAndrew Turner 	*maxpin = RPI_FW_GPIO_PINS - 1;
1499ca3eaf0SAndrew Turner 	return (0);
1509ca3eaf0SAndrew Turner }
1519ca3eaf0SAndrew Turner 
1529ca3eaf0SAndrew Turner static int
rpi_fw_gpio_pin_getcaps(device_t dev,uint32_t pin,uint32_t * caps)1539ca3eaf0SAndrew Turner rpi_fw_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
1549ca3eaf0SAndrew Turner {
1559ca3eaf0SAndrew Turner 	struct rpi_fw_gpio_softc *sc;
1569ca3eaf0SAndrew Turner 	int i;
1579ca3eaf0SAndrew Turner 
1589ca3eaf0SAndrew Turner 	sc = device_get_softc(dev);
1599ca3eaf0SAndrew Turner 	for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
1609ca3eaf0SAndrew Turner 		if (sc->sc_gpio_pins[i].gp_pin == pin)
1619ca3eaf0SAndrew Turner 			break;
1629ca3eaf0SAndrew Turner 	}
1639ca3eaf0SAndrew Turner 
1649ca3eaf0SAndrew Turner 	if (i >= RPI_FW_GPIO_PINS)
1659ca3eaf0SAndrew Turner 		return (EINVAL);
1669ca3eaf0SAndrew Turner 
1679ca3eaf0SAndrew Turner 	*caps = RPI_FW_GPIO_DEFAULT_CAPS;
1689ca3eaf0SAndrew Turner 	return (0);
1699ca3eaf0SAndrew Turner }
1709ca3eaf0SAndrew Turner 
1719ca3eaf0SAndrew Turner static int
rpi_fw_gpio_pin_getflags(device_t dev,uint32_t pin,uint32_t * flags)1729ca3eaf0SAndrew Turner rpi_fw_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
1739ca3eaf0SAndrew Turner {
1749ca3eaf0SAndrew Turner 	struct rpi_fw_gpio_softc *sc = device_get_softc(dev);
1759ca3eaf0SAndrew Turner 	int i;
1769ca3eaf0SAndrew Turner 
1779ca3eaf0SAndrew Turner 	for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
1789ca3eaf0SAndrew Turner 		if (sc->sc_gpio_pins[i].gp_pin == pin)
1799ca3eaf0SAndrew Turner 			break;
1809ca3eaf0SAndrew Turner 	}
1819ca3eaf0SAndrew Turner 
1829ca3eaf0SAndrew Turner 	if (i >= RPI_FW_GPIO_PINS)
1839ca3eaf0SAndrew Turner 		return (EINVAL);
1849ca3eaf0SAndrew Turner 
1859ca3eaf0SAndrew Turner 	RPI_FW_GPIO_LOCK(sc);
1869ca3eaf0SAndrew Turner 	*flags = sc->sc_gpio_pins[i].gp_flags;
1879ca3eaf0SAndrew Turner 	RPI_FW_GPIO_UNLOCK(sc);
1889ca3eaf0SAndrew Turner 
1899ca3eaf0SAndrew Turner 	return (0);
1909ca3eaf0SAndrew Turner }
1919ca3eaf0SAndrew Turner 
1929ca3eaf0SAndrew Turner static int
rpi_fw_gpio_pin_getname(device_t dev,uint32_t pin,char * name)1939ca3eaf0SAndrew Turner rpi_fw_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
1949ca3eaf0SAndrew Turner {
1959ca3eaf0SAndrew Turner 	struct rpi_fw_gpio_softc *sc;
1969ca3eaf0SAndrew Turner 	int i;
1979ca3eaf0SAndrew Turner 
1989ca3eaf0SAndrew Turner 	sc = device_get_softc(dev);
1999ca3eaf0SAndrew Turner 	for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
2009ca3eaf0SAndrew Turner 		if (sc->sc_gpio_pins[i].gp_pin == pin)
2019ca3eaf0SAndrew Turner 			break;
2029ca3eaf0SAndrew Turner 	}
2039ca3eaf0SAndrew Turner 
2049ca3eaf0SAndrew Turner 	if (i >= RPI_FW_GPIO_PINS)
2059ca3eaf0SAndrew Turner 		return (EINVAL);
2069ca3eaf0SAndrew Turner 
2079ca3eaf0SAndrew Turner 	RPI_FW_GPIO_LOCK(sc);
2089ca3eaf0SAndrew Turner 	memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME);
2099ca3eaf0SAndrew Turner 	RPI_FW_GPIO_UNLOCK(sc);
2109ca3eaf0SAndrew Turner 
2119ca3eaf0SAndrew Turner 	return (0);
2129ca3eaf0SAndrew Turner }
2139ca3eaf0SAndrew Turner 
2149ca3eaf0SAndrew Turner static int
rpi_fw_gpio_pin_setflags(device_t dev,uint32_t pin,uint32_t flags)2159ca3eaf0SAndrew Turner rpi_fw_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
2169ca3eaf0SAndrew Turner {
2179ca3eaf0SAndrew Turner 	struct rpi_fw_gpio_softc *sc;
2189ca3eaf0SAndrew Turner 	int i;
2199ca3eaf0SAndrew Turner 
2209ca3eaf0SAndrew Turner 	sc = device_get_softc(dev);
2219ca3eaf0SAndrew Turner 	for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
2229ca3eaf0SAndrew Turner 		if (sc->sc_gpio_pins[i].gp_pin == pin)
2239ca3eaf0SAndrew Turner 			break;
2249ca3eaf0SAndrew Turner 	}
2259ca3eaf0SAndrew Turner 
2269ca3eaf0SAndrew Turner 	if (i >= RPI_FW_GPIO_PINS)
2279ca3eaf0SAndrew Turner 		return (EINVAL);
2289ca3eaf0SAndrew Turner 
2299ca3eaf0SAndrew Turner 	return (rpi_fw_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags));
2309ca3eaf0SAndrew Turner }
2319ca3eaf0SAndrew Turner 
2329ca3eaf0SAndrew Turner static int
rpi_fw_gpio_pin_set(device_t dev,uint32_t pin,unsigned int value)2339ca3eaf0SAndrew Turner rpi_fw_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
2349ca3eaf0SAndrew Turner {
2359ca3eaf0SAndrew Turner 	struct rpi_fw_gpio_softc *sc;
2369ca3eaf0SAndrew Turner 	union msg_set_gpio_state state;
2379ca3eaf0SAndrew Turner 	int i, rv;
2389ca3eaf0SAndrew Turner 
2399ca3eaf0SAndrew Turner 	sc = device_get_softc(dev);
2409ca3eaf0SAndrew Turner 	for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
2419ca3eaf0SAndrew Turner 		if (sc->sc_gpio_pins[i].gp_pin == pin)
2429ca3eaf0SAndrew Turner 			break;
2439ca3eaf0SAndrew Turner 	}
2449ca3eaf0SAndrew Turner 	if (i >= RPI_FW_GPIO_PINS)
2459ca3eaf0SAndrew Turner 		return (EINVAL);
2469ca3eaf0SAndrew Turner 
2479ca3eaf0SAndrew Turner 	state.req.gpio = RPI_FW_GPIO_BASE + pin;
2489ca3eaf0SAndrew Turner 	state.req.state = value;
2499ca3eaf0SAndrew Turner 
2509ca3eaf0SAndrew Turner 	RPI_FW_GPIO_LOCK(sc);
2519ca3eaf0SAndrew Turner 	rv = bcm2835_firmware_property(sc->sc_firmware,
2529ca3eaf0SAndrew Turner 	    BCM2835_FIRMWARE_TAG_SET_GPIO_STATE, &state, sizeof(state));
2539ca3eaf0SAndrew Turner 	/* The firmware sets gpio to 0 on success */
2549ca3eaf0SAndrew Turner 	if (rv == 0 && state.resp.gpio != 0)
2559ca3eaf0SAndrew Turner 		rv = EINVAL;
2569ca3eaf0SAndrew Turner 	if (rv == 0) {
2579ca3eaf0SAndrew Turner 		sc->sc_gpio_pins[i].gp_flags &= ~(GPIO_PIN_PRESET_HIGH |
2589ca3eaf0SAndrew Turner 		    GPIO_PIN_PRESET_LOW);
2599ca3eaf0SAndrew Turner 		if (value)
2609ca3eaf0SAndrew Turner 			sc->sc_gpio_state |= (1 << i);
2619ca3eaf0SAndrew Turner 		else
2629ca3eaf0SAndrew Turner 			sc->sc_gpio_state &= ~(1 << i);
2639ca3eaf0SAndrew Turner 	}
2649ca3eaf0SAndrew Turner 	RPI_FW_GPIO_UNLOCK(sc);
2659ca3eaf0SAndrew Turner 
2669ca3eaf0SAndrew Turner 	return (rv);
2679ca3eaf0SAndrew Turner }
2689ca3eaf0SAndrew Turner 
2699ca3eaf0SAndrew Turner static int
rpi_fw_gpio_pin_get(device_t dev,uint32_t pin,unsigned int * val)2709ca3eaf0SAndrew Turner rpi_fw_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
2719ca3eaf0SAndrew Turner {
2729ca3eaf0SAndrew Turner 	struct rpi_fw_gpio_softc *sc;
2739ca3eaf0SAndrew Turner 	union msg_get_gpio_state state;
2749ca3eaf0SAndrew Turner 	int i, rv;
2759ca3eaf0SAndrew Turner 
2769ca3eaf0SAndrew Turner 	sc = device_get_softc(dev);
2779ca3eaf0SAndrew Turner 	for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
2789ca3eaf0SAndrew Turner 		if (sc->sc_gpio_pins[i].gp_pin == pin)
2799ca3eaf0SAndrew Turner 			break;
2809ca3eaf0SAndrew Turner 	}
2819ca3eaf0SAndrew Turner 	if (i >= RPI_FW_GPIO_PINS)
2829ca3eaf0SAndrew Turner 		return (EINVAL);
2839ca3eaf0SAndrew Turner 
2849ca3eaf0SAndrew Turner 	bzero(&state, sizeof(state));
2859ca3eaf0SAndrew Turner 	state.req.gpio = RPI_FW_GPIO_BASE + pin;
2869ca3eaf0SAndrew Turner 
2879ca3eaf0SAndrew Turner 	RPI_FW_GPIO_LOCK(sc);
2889ca3eaf0SAndrew Turner 	rv = bcm2835_firmware_property(sc->sc_firmware,
2899ca3eaf0SAndrew Turner 	    BCM2835_FIRMWARE_TAG_GET_GPIO_STATE, &state, sizeof(state));
2909ca3eaf0SAndrew Turner 	RPI_FW_GPIO_UNLOCK(sc);
2919ca3eaf0SAndrew Turner 
2929ca3eaf0SAndrew Turner 	/* The firmware sets gpio to 0 on success */
2939ca3eaf0SAndrew Turner 	if (rv == 0 && state.resp.gpio != 0)
2949ca3eaf0SAndrew Turner 		rv = EINVAL;
2959ca3eaf0SAndrew Turner 	if (rv == 0)
2969ca3eaf0SAndrew Turner 		*val = !state.resp.state;
2979ca3eaf0SAndrew Turner 
2989ca3eaf0SAndrew Turner 	return (rv);
2999ca3eaf0SAndrew Turner }
3009ca3eaf0SAndrew Turner 
3019ca3eaf0SAndrew Turner static int
rpi_fw_gpio_pin_toggle(device_t dev,uint32_t pin)3029ca3eaf0SAndrew Turner rpi_fw_gpio_pin_toggle(device_t dev, uint32_t pin)
3039ca3eaf0SAndrew Turner {
3049ca3eaf0SAndrew Turner 	struct rpi_fw_gpio_softc *sc;
3059ca3eaf0SAndrew Turner 	union msg_get_gpio_state old_state;
3069ca3eaf0SAndrew Turner 	union msg_set_gpio_state new_state;
3079ca3eaf0SAndrew Turner 	int i, rv;
3089ca3eaf0SAndrew Turner 
3099ca3eaf0SAndrew Turner 	sc = device_get_softc(dev);
3109ca3eaf0SAndrew Turner 	for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
3119ca3eaf0SAndrew Turner 		if (sc->sc_gpio_pins[i].gp_pin == pin)
3129ca3eaf0SAndrew Turner 			break;
3139ca3eaf0SAndrew Turner 	}
3149ca3eaf0SAndrew Turner 	if (i >= RPI_FW_GPIO_PINS)
3159ca3eaf0SAndrew Turner 		return (EINVAL);
3169ca3eaf0SAndrew Turner 
3179ca3eaf0SAndrew Turner 	bzero(&old_state, sizeof(old_state));
3189ca3eaf0SAndrew Turner 	bzero(&new_state, sizeof(new_state));
3199ca3eaf0SAndrew Turner 
3209ca3eaf0SAndrew Turner 	old_state.req.gpio = RPI_FW_GPIO_BASE + pin;
3219ca3eaf0SAndrew Turner 	new_state.req.gpio = RPI_FW_GPIO_BASE + pin;
3229ca3eaf0SAndrew Turner 
3239ca3eaf0SAndrew Turner 	RPI_FW_GPIO_LOCK(sc);
3249ca3eaf0SAndrew Turner 	rv = bcm2835_firmware_property(sc->sc_firmware,
3259ca3eaf0SAndrew Turner 	    BCM2835_FIRMWARE_TAG_GET_GPIO_STATE, &old_state, sizeof(old_state));
3269ca3eaf0SAndrew Turner 	/* The firmware sets gpio to 0 on success */
3279ca3eaf0SAndrew Turner 	if (rv == 0 && old_state.resp.gpio == 0) {
3289ca3eaf0SAndrew Turner 		/* Set the new state to invert the GPIO */
3299ca3eaf0SAndrew Turner 		new_state.req.state = !old_state.resp.state;
3309ca3eaf0SAndrew Turner 		rv = bcm2835_firmware_property(sc->sc_firmware,
3319ca3eaf0SAndrew Turner 		    BCM2835_FIRMWARE_TAG_SET_GPIO_STATE, &new_state,
3329ca3eaf0SAndrew Turner 		    sizeof(new_state));
3339ca3eaf0SAndrew Turner 	}
3349ca3eaf0SAndrew Turner 	if (rv == 0 && (old_state.resp.gpio != 0 || new_state.resp.gpio != 0))
3359ca3eaf0SAndrew Turner 		rv = EINVAL;
3369ca3eaf0SAndrew Turner 	RPI_FW_GPIO_UNLOCK(sc);
3379ca3eaf0SAndrew Turner 
3389ca3eaf0SAndrew Turner 	return (rv);
3399ca3eaf0SAndrew Turner }
3409ca3eaf0SAndrew Turner 
3419ca3eaf0SAndrew Turner static int
rpi_fw_gpio_probe(device_t dev)3429ca3eaf0SAndrew Turner rpi_fw_gpio_probe(device_t dev)
3439ca3eaf0SAndrew Turner {
3449ca3eaf0SAndrew Turner 
3459ca3eaf0SAndrew Turner 	if (!ofw_bus_status_okay(dev))
3469ca3eaf0SAndrew Turner 		return (ENXIO);
3479ca3eaf0SAndrew Turner 
3489ca3eaf0SAndrew Turner 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
3499ca3eaf0SAndrew Turner 		return (ENXIO);
3509ca3eaf0SAndrew Turner 
3519ca3eaf0SAndrew Turner 	device_set_desc(dev, "Raspberry Pi Firmware GPIO controller");
3529ca3eaf0SAndrew Turner 	return (BUS_PROBE_DEFAULT);
3539ca3eaf0SAndrew Turner }
3549ca3eaf0SAndrew Turner 
3559ca3eaf0SAndrew Turner static int
rpi_fw_gpio_attach(device_t dev)3569ca3eaf0SAndrew Turner rpi_fw_gpio_attach(device_t dev)
3579ca3eaf0SAndrew Turner {
3589ca3eaf0SAndrew Turner 	union msg_get_gpio_config cfg;
3599ca3eaf0SAndrew Turner 	struct rpi_fw_gpio_softc *sc;
3609ca3eaf0SAndrew Turner 	char *names;
3619ca3eaf0SAndrew Turner 	phandle_t gpio;
3629ca3eaf0SAndrew Turner 	int i, nelems, elm_pos, rv;
3639ca3eaf0SAndrew Turner 
3649ca3eaf0SAndrew Turner 	sc = device_get_softc(dev);
3659ca3eaf0SAndrew Turner 	sc->sc_firmware = device_get_parent(dev);
3669ca3eaf0SAndrew Turner 	sx_init(&sc->sc_sx, "Raspberry Pi firmware gpio");
3679ca3eaf0SAndrew Turner 	/* Find our node. */
3689ca3eaf0SAndrew Turner 	gpio = ofw_bus_get_node(dev);
3699ca3eaf0SAndrew Turner 	if (!OF_hasprop(gpio, "gpio-controller"))
3709ca3eaf0SAndrew Turner 		/* This is not a GPIO controller. */
3719ca3eaf0SAndrew Turner 		goto fail;
3729ca3eaf0SAndrew Turner 
3739ca3eaf0SAndrew Turner 	nelems = OF_getprop_alloc(gpio, "gpio-line-names", (void **)&names);
3749ca3eaf0SAndrew Turner 	if (nelems <= 0)
3759ca3eaf0SAndrew Turner 		names = NULL;
3769ca3eaf0SAndrew Turner 	elm_pos = 0;
3779ca3eaf0SAndrew Turner 	for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
3789ca3eaf0SAndrew Turner 		/* Set the current pin name */
3799ca3eaf0SAndrew Turner 		if (names != NULL && elm_pos < nelems &&
3809ca3eaf0SAndrew Turner 		    names[elm_pos] != '\0') {
3819ca3eaf0SAndrew Turner 			snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
3829ca3eaf0SAndrew Turner 			    "%s", names + elm_pos);
3839ca3eaf0SAndrew Turner 			/* Find the next pin name */
3849ca3eaf0SAndrew Turner 			elm_pos += strlen(names + elm_pos) + 1;
3859ca3eaf0SAndrew Turner 		} else {
3869ca3eaf0SAndrew Turner 			snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
3879ca3eaf0SAndrew Turner 			    "pin %d", i);
3889ca3eaf0SAndrew Turner 		}
3899ca3eaf0SAndrew Turner 
3909ca3eaf0SAndrew Turner 		sc->sc_gpio_pins[i].gp_pin = i;
3919ca3eaf0SAndrew Turner 		sc->sc_gpio_pins[i].gp_caps = RPI_FW_GPIO_DEFAULT_CAPS;
3929ca3eaf0SAndrew Turner 
3939ca3eaf0SAndrew Turner 		bzero(&cfg, sizeof(cfg));
3949ca3eaf0SAndrew Turner 		cfg.req.gpio = RPI_FW_GPIO_BASE + i;
3959ca3eaf0SAndrew Turner 		rv = bcm2835_firmware_property(sc->sc_firmware,
3969ca3eaf0SAndrew Turner 		    BCM2835_FIRMWARE_TAG_GET_GPIO_CONFIG, &cfg, sizeof(cfg));
3979ca3eaf0SAndrew Turner 		if (rv == 0 && cfg.resp.gpio == 0) {
3989ca3eaf0SAndrew Turner 			if (cfg.resp.dir == BCM2835_FIRMWARE_GPIO_IN)
3999ca3eaf0SAndrew Turner 				sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_INPUT;
4009ca3eaf0SAndrew Turner 			else
4019ca3eaf0SAndrew Turner 				sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_OUTPUT;
4029ca3eaf0SAndrew Turner 		} else {
4039ca3eaf0SAndrew Turner 			sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_INPUT;
4049ca3eaf0SAndrew Turner 		}
4059ca3eaf0SAndrew Turner 	}
4069ca3eaf0SAndrew Turner 	free(names, M_OFWPROP);
4079ca3eaf0SAndrew Turner 	sc->sc_busdev = gpiobus_attach_bus(dev);
4089ca3eaf0SAndrew Turner 	if (sc->sc_busdev == NULL)
4099ca3eaf0SAndrew Turner 		goto fail;
4109ca3eaf0SAndrew Turner 
4119ca3eaf0SAndrew Turner 	return (0);
4129ca3eaf0SAndrew Turner 
4139ca3eaf0SAndrew Turner fail:
4149ca3eaf0SAndrew Turner 	sx_destroy(&sc->sc_sx);
4159ca3eaf0SAndrew Turner 
4169ca3eaf0SAndrew Turner 	return (ENXIO);
4179ca3eaf0SAndrew Turner }
4189ca3eaf0SAndrew Turner 
4199ca3eaf0SAndrew Turner static int
rpi_fw_gpio_detach(device_t dev)4209ca3eaf0SAndrew Turner rpi_fw_gpio_detach(device_t dev)
4219ca3eaf0SAndrew Turner {
4229ca3eaf0SAndrew Turner 
4239ca3eaf0SAndrew Turner 	return (EBUSY);
4249ca3eaf0SAndrew Turner }
4259ca3eaf0SAndrew Turner 
4269ca3eaf0SAndrew Turner static device_method_t rpi_fw_gpio_methods[] = {
4279ca3eaf0SAndrew Turner 	/* Device interface */
4289ca3eaf0SAndrew Turner 	DEVMETHOD(device_probe,		rpi_fw_gpio_probe),
4299ca3eaf0SAndrew Turner 	DEVMETHOD(device_attach,	rpi_fw_gpio_attach),
4309ca3eaf0SAndrew Turner 	DEVMETHOD(device_detach,	rpi_fw_gpio_detach),
4319ca3eaf0SAndrew Turner 
4329ca3eaf0SAndrew Turner 	/* GPIO protocol */
4339ca3eaf0SAndrew Turner 	DEVMETHOD(gpio_get_bus,		rpi_fw_gpio_get_bus),
4349ca3eaf0SAndrew Turner 	DEVMETHOD(gpio_pin_max,		rpi_fw_gpio_pin_max),
4359ca3eaf0SAndrew Turner 	DEVMETHOD(gpio_pin_getname,	rpi_fw_gpio_pin_getname),
4369ca3eaf0SAndrew Turner 	DEVMETHOD(gpio_pin_getflags,	rpi_fw_gpio_pin_getflags),
4379ca3eaf0SAndrew Turner 	DEVMETHOD(gpio_pin_getcaps,	rpi_fw_gpio_pin_getcaps),
4389ca3eaf0SAndrew Turner 	DEVMETHOD(gpio_pin_setflags,	rpi_fw_gpio_pin_setflags),
4399ca3eaf0SAndrew Turner 	DEVMETHOD(gpio_pin_get,		rpi_fw_gpio_pin_get),
4409ca3eaf0SAndrew Turner 	DEVMETHOD(gpio_pin_set,		rpi_fw_gpio_pin_set),
4419ca3eaf0SAndrew Turner 	DEVMETHOD(gpio_pin_toggle,	rpi_fw_gpio_pin_toggle),
4429ca3eaf0SAndrew Turner 
4439ca3eaf0SAndrew Turner 	DEVMETHOD_END
4449ca3eaf0SAndrew Turner };
4459ca3eaf0SAndrew Turner 
4469ca3eaf0SAndrew Turner static driver_t rpi_fw_gpio_driver = {
4479ca3eaf0SAndrew Turner 	"gpio",
4489ca3eaf0SAndrew Turner 	rpi_fw_gpio_methods,
4499ca3eaf0SAndrew Turner 	sizeof(struct rpi_fw_gpio_softc),
4509ca3eaf0SAndrew Turner };
4519ca3eaf0SAndrew Turner 
45282d4dc06SJohn Baldwin EARLY_DRIVER_MODULE(rpi_fw_gpio, bcm2835_firmware, rpi_fw_gpio_driver, 0, 0,
45382d4dc06SJohn Baldwin     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
454