xref: /freebsd/sys/arm/broadcom/bcm2835/bcm2835_gpio.c (revision add35ed5b81bd56b4e0f1f9db0636b785cc0c8d1)
14063f925SOleksandr Tymoshenko /*-
24063f925SOleksandr Tymoshenko  * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
34063f925SOleksandr Tymoshenko  * Copyright (c) 2012 Luiz Otavio O Souza.
44063f925SOleksandr Tymoshenko  * All rights reserved.
54063f925SOleksandr Tymoshenko  *
64063f925SOleksandr Tymoshenko  * Redistribution and use in source and binary forms, with or without
74063f925SOleksandr Tymoshenko  * modification, are permitted provided that the following conditions
84063f925SOleksandr Tymoshenko  * are met:
94063f925SOleksandr Tymoshenko  * 1. Redistributions of source code must retain the above copyright
104063f925SOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer.
114063f925SOleksandr Tymoshenko  * 2. Redistributions in binary form must reproduce the above copyright
124063f925SOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer in the
134063f925SOleksandr Tymoshenko  *    documentation and/or other materials provided with the distribution.
144063f925SOleksandr Tymoshenko  *
154063f925SOleksandr Tymoshenko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
164063f925SOleksandr Tymoshenko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
174063f925SOleksandr Tymoshenko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
184063f925SOleksandr Tymoshenko  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
194063f925SOleksandr Tymoshenko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
204063f925SOleksandr Tymoshenko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
214063f925SOleksandr Tymoshenko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
224063f925SOleksandr Tymoshenko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
234063f925SOleksandr Tymoshenko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
244063f925SOleksandr Tymoshenko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
254063f925SOleksandr Tymoshenko  * SUCH DAMAGE.
264063f925SOleksandr Tymoshenko  *
274063f925SOleksandr Tymoshenko  */
284063f925SOleksandr Tymoshenko #include <sys/cdefs.h>
294063f925SOleksandr Tymoshenko __FBSDID("$FreeBSD$");
304063f925SOleksandr Tymoshenko 
314063f925SOleksandr Tymoshenko #include <sys/param.h>
324063f925SOleksandr Tymoshenko #include <sys/systm.h>
334063f925SOleksandr Tymoshenko #include <sys/bus.h>
344063f925SOleksandr Tymoshenko 
354063f925SOleksandr Tymoshenko #include <sys/kernel.h>
364063f925SOleksandr Tymoshenko #include <sys/module.h>
374063f925SOleksandr Tymoshenko #include <sys/rman.h>
384063f925SOleksandr Tymoshenko #include <sys/lock.h>
394063f925SOleksandr Tymoshenko #include <sys/mutex.h>
404063f925SOleksandr Tymoshenko #include <sys/gpio.h>
4190576f54SOleksandr Tymoshenko #include <sys/sysctl.h>
424063f925SOleksandr Tymoshenko 
434063f925SOleksandr Tymoshenko #include <machine/bus.h>
444063f925SOleksandr Tymoshenko #include <machine/cpu.h>
454063f925SOleksandr Tymoshenko #include <machine/cpufunc.h>
464063f925SOleksandr Tymoshenko #include <machine/resource.h>
474063f925SOleksandr Tymoshenko #include <machine/fdt.h>
484063f925SOleksandr Tymoshenko #include <machine/intr.h>
494063f925SOleksandr Tymoshenko 
504063f925SOleksandr Tymoshenko #include <dev/fdt/fdt_common.h>
514063f925SOleksandr Tymoshenko #include <dev/ofw/ofw_bus.h>
524063f925SOleksandr Tymoshenko #include <dev/ofw/ofw_bus_subr.h>
534063f925SOleksandr Tymoshenko 
5444d06d8dSLuiz Otavio O Souza #include <arm/broadcom/bcm2835/bcm2835_gpio.h>
5544d06d8dSLuiz Otavio O Souza 
564063f925SOleksandr Tymoshenko #include "gpio_if.h"
574063f925SOleksandr Tymoshenko 
584063f925SOleksandr Tymoshenko #ifdef DEBUG
594063f925SOleksandr Tymoshenko #define dprintf(fmt, args...) do { printf("%s(): ", __func__);   \
604063f925SOleksandr Tymoshenko     printf(fmt,##args); } while (0)
614063f925SOleksandr Tymoshenko #else
624063f925SOleksandr Tymoshenko #define dprintf(fmt, args...)
634063f925SOleksandr Tymoshenko #endif
644063f925SOleksandr Tymoshenko 
654063f925SOleksandr Tymoshenko #define	BCM_GPIO_PINS		54
664063f925SOleksandr Tymoshenko #define	BCM_GPIO_DEFAULT_CAPS	(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |	\
674063f925SOleksandr Tymoshenko     GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN)
684063f925SOleksandr Tymoshenko 
6990576f54SOleksandr Tymoshenko struct bcm_gpio_sysctl {
7090576f54SOleksandr Tymoshenko 	struct bcm_gpio_softc	*sc;
7190576f54SOleksandr Tymoshenko 	uint32_t		pin;
7290576f54SOleksandr Tymoshenko };
7390576f54SOleksandr Tymoshenko 
744063f925SOleksandr Tymoshenko struct bcm_gpio_softc {
754063f925SOleksandr Tymoshenko 	device_t		sc_dev;
764063f925SOleksandr Tymoshenko 	struct mtx		sc_mtx;
774063f925SOleksandr Tymoshenko 	struct resource *	sc_mem_res;
784063f925SOleksandr Tymoshenko 	struct resource *	sc_irq_res;
794063f925SOleksandr Tymoshenko 	bus_space_tag_t		sc_bst;
804063f925SOleksandr Tymoshenko 	bus_space_handle_t	sc_bsh;
814063f925SOleksandr Tymoshenko 	void *			sc_intrhand;
824063f925SOleksandr Tymoshenko 	int			sc_gpio_npins;
834063f925SOleksandr Tymoshenko 	int			sc_ro_npins;
844063f925SOleksandr Tymoshenko 	int			sc_ro_pins[BCM_GPIO_PINS];
854063f925SOleksandr Tymoshenko 	struct gpio_pin		sc_gpio_pins[BCM_GPIO_PINS];
8690576f54SOleksandr Tymoshenko 	struct bcm_gpio_sysctl	sc_sysctl[BCM_GPIO_PINS];
874063f925SOleksandr Tymoshenko };
884063f925SOleksandr Tymoshenko 
894063f925SOleksandr Tymoshenko enum bcm_gpio_pud {
904063f925SOleksandr Tymoshenko 	BCM_GPIO_NONE,
914063f925SOleksandr Tymoshenko 	BCM_GPIO_PULLDOWN,
924063f925SOleksandr Tymoshenko 	BCM_GPIO_PULLUP,
934063f925SOleksandr Tymoshenko };
944063f925SOleksandr Tymoshenko 
954063f925SOleksandr Tymoshenko #define	BCM_GPIO_LOCK(_sc)	mtx_lock(&_sc->sc_mtx)
964063f925SOleksandr Tymoshenko #define	BCM_GPIO_UNLOCK(_sc)	mtx_unlock(&_sc->sc_mtx)
9790576f54SOleksandr Tymoshenko #define	BCM_GPIO_LOCK_ASSERT(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED)
984063f925SOleksandr Tymoshenko 
994063f925SOleksandr Tymoshenko #define	BCM_GPIO_GPFSEL(_bank)	0x00 + _bank * 4
1004063f925SOleksandr Tymoshenko #define	BCM_GPIO_GPSET(_bank)	0x1c + _bank * 4
1014063f925SOleksandr Tymoshenko #define	BCM_GPIO_GPCLR(_bank)	0x28 + _bank * 4
1024063f925SOleksandr Tymoshenko #define	BCM_GPIO_GPLEV(_bank)	0x34 + _bank * 4
1034063f925SOleksandr Tymoshenko #define	BCM_GPIO_GPPUD(_bank)	0x94
1044063f925SOleksandr Tymoshenko #define	BCM_GPIO_GPPUDCLK(_bank)	0x98 + _bank * 4
1054063f925SOleksandr Tymoshenko 
1064063f925SOleksandr Tymoshenko #define	BCM_GPIO_WRITE(_sc, _off, _val)		\
1074063f925SOleksandr Tymoshenko     bus_space_write_4(_sc->sc_bst, _sc->sc_bsh, _off, _val)
1084063f925SOleksandr Tymoshenko #define	BCM_GPIO_READ(_sc, _off)		\
1094063f925SOleksandr Tymoshenko     bus_space_read_4(_sc->sc_bst, _sc->sc_bsh, _off)
1104063f925SOleksandr Tymoshenko 
1114063f925SOleksandr Tymoshenko static int
1124063f925SOleksandr Tymoshenko bcm_gpio_pin_is_ro(struct bcm_gpio_softc *sc, int pin)
1134063f925SOleksandr Tymoshenko {
1144063f925SOleksandr Tymoshenko 	int i;
1154063f925SOleksandr Tymoshenko 
1164063f925SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_ro_npins; i++)
1174063f925SOleksandr Tymoshenko 		if (pin == sc->sc_ro_pins[i])
1184063f925SOleksandr Tymoshenko 			return (1);
1194063f925SOleksandr Tymoshenko 	return (0);
1204063f925SOleksandr Tymoshenko }
1214063f925SOleksandr Tymoshenko 
1224063f925SOleksandr Tymoshenko static uint32_t
1234063f925SOleksandr Tymoshenko bcm_gpio_get_function(struct bcm_gpio_softc *sc, uint32_t pin)
1244063f925SOleksandr Tymoshenko {
12590576f54SOleksandr Tymoshenko 	uint32_t bank, func, offset;
1264063f925SOleksandr Tymoshenko 
1274063f925SOleksandr Tymoshenko 	/* Five banks, 10 pins per bank, 3 bits per pin. */
1284063f925SOleksandr Tymoshenko 	bank = pin / 10;
1294063f925SOleksandr Tymoshenko 	offset = (pin - bank * 10) * 3;
1304063f925SOleksandr Tymoshenko 
1314063f925SOleksandr Tymoshenko 	BCM_GPIO_LOCK(sc);
13290576f54SOleksandr Tymoshenko 	func = (BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank)) >> offset) & 7;
1334063f925SOleksandr Tymoshenko 	BCM_GPIO_UNLOCK(sc);
1344063f925SOleksandr Tymoshenko 
13590576f54SOleksandr Tymoshenko 	return (func);
13690576f54SOleksandr Tymoshenko }
13790576f54SOleksandr Tymoshenko 
13890576f54SOleksandr Tymoshenko static void
13990576f54SOleksandr Tymoshenko bcm_gpio_func_str(uint32_t nfunc, char *buf, int bufsize)
14090576f54SOleksandr Tymoshenko {
14190576f54SOleksandr Tymoshenko 
14290576f54SOleksandr Tymoshenko 	switch (nfunc) {
1434063f925SOleksandr Tymoshenko 	case BCM_GPIO_INPUT:
14490576f54SOleksandr Tymoshenko 		strncpy(buf, "input", bufsize);
1454063f925SOleksandr Tymoshenko 		break;
1464063f925SOleksandr Tymoshenko 	case BCM_GPIO_OUTPUT:
14790576f54SOleksandr Tymoshenko 		strncpy(buf, "output", bufsize);
1484063f925SOleksandr Tymoshenko 		break;
1494063f925SOleksandr Tymoshenko 	case BCM_GPIO_ALT0:
15090576f54SOleksandr Tymoshenko 		strncpy(buf, "alt0", bufsize);
1514063f925SOleksandr Tymoshenko 		break;
1524063f925SOleksandr Tymoshenko 	case BCM_GPIO_ALT1:
15390576f54SOleksandr Tymoshenko 		strncpy(buf, "alt1", bufsize);
1544063f925SOleksandr Tymoshenko 		break;
1554063f925SOleksandr Tymoshenko 	case BCM_GPIO_ALT2:
15690576f54SOleksandr Tymoshenko 		strncpy(buf, "alt2", bufsize);
1574063f925SOleksandr Tymoshenko 		break;
1584063f925SOleksandr Tymoshenko 	case BCM_GPIO_ALT3:
15990576f54SOleksandr Tymoshenko 		strncpy(buf, "alt3", bufsize);
1604063f925SOleksandr Tymoshenko 		break;
1614063f925SOleksandr Tymoshenko 	case BCM_GPIO_ALT4:
16290576f54SOleksandr Tymoshenko 		strncpy(buf, "alt4", bufsize);
1634063f925SOleksandr Tymoshenko 		break;
1644063f925SOleksandr Tymoshenko 	case BCM_GPIO_ALT5:
16590576f54SOleksandr Tymoshenko 		strncpy(buf, "alt5", bufsize);
1664063f925SOleksandr Tymoshenko 		break;
16790576f54SOleksandr Tymoshenko 	default:
16890576f54SOleksandr Tymoshenko 		strncpy(buf, "invalid", bufsize);
1694063f925SOleksandr Tymoshenko 	}
17090576f54SOleksandr Tymoshenko }
1714063f925SOleksandr Tymoshenko 
17290576f54SOleksandr Tymoshenko static int
17390576f54SOleksandr Tymoshenko bcm_gpio_str_func(char *func, uint32_t *nfunc)
17490576f54SOleksandr Tymoshenko {
17590576f54SOleksandr Tymoshenko 
17690576f54SOleksandr Tymoshenko 	if (strcasecmp(func, "input") == 0)
17790576f54SOleksandr Tymoshenko 		*nfunc = BCM_GPIO_INPUT;
17890576f54SOleksandr Tymoshenko 	else if (strcasecmp(func, "output") == 0)
17990576f54SOleksandr Tymoshenko 		*nfunc = BCM_GPIO_OUTPUT;
18090576f54SOleksandr Tymoshenko 	else if (strcasecmp(func, "alt0") == 0)
18190576f54SOleksandr Tymoshenko 		*nfunc = BCM_GPIO_ALT0;
18290576f54SOleksandr Tymoshenko 	else if (strcasecmp(func, "alt1") == 0)
18390576f54SOleksandr Tymoshenko 		*nfunc = BCM_GPIO_ALT1;
18490576f54SOleksandr Tymoshenko 	else if (strcasecmp(func, "alt2") == 0)
18590576f54SOleksandr Tymoshenko 		*nfunc = BCM_GPIO_ALT2;
18690576f54SOleksandr Tymoshenko 	else if (strcasecmp(func, "alt3") == 0)
18790576f54SOleksandr Tymoshenko 		*nfunc = BCM_GPIO_ALT3;
18890576f54SOleksandr Tymoshenko 	else if (strcasecmp(func, "alt4") == 0)
18990576f54SOleksandr Tymoshenko 		*nfunc = BCM_GPIO_ALT4;
19090576f54SOleksandr Tymoshenko 	else if (strcasecmp(func, "alt5") == 0)
19190576f54SOleksandr Tymoshenko 		*nfunc = BCM_GPIO_ALT5;
19290576f54SOleksandr Tymoshenko 	else
19390576f54SOleksandr Tymoshenko 		return (-1);
19490576f54SOleksandr Tymoshenko 
19590576f54SOleksandr Tymoshenko 	return (0);
19690576f54SOleksandr Tymoshenko }
19790576f54SOleksandr Tymoshenko 
19890576f54SOleksandr Tymoshenko static uint32_t
19990576f54SOleksandr Tymoshenko bcm_gpio_func_flag(uint32_t nfunc)
20090576f54SOleksandr Tymoshenko {
20190576f54SOleksandr Tymoshenko 
20290576f54SOleksandr Tymoshenko 	switch (nfunc) {
2034063f925SOleksandr Tymoshenko 	case BCM_GPIO_INPUT:
2044063f925SOleksandr Tymoshenko 		return (GPIO_PIN_INPUT);
2054063f925SOleksandr Tymoshenko 	case BCM_GPIO_OUTPUT:
2064063f925SOleksandr Tymoshenko 		return (GPIO_PIN_OUTPUT);
2074063f925SOleksandr Tymoshenko 	}
2084063f925SOleksandr Tymoshenko 	return (0);
2094063f925SOleksandr Tymoshenko }
2104063f925SOleksandr Tymoshenko 
2114063f925SOleksandr Tymoshenko static void
2124063f925SOleksandr Tymoshenko bcm_gpio_set_function(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t f)
2134063f925SOleksandr Tymoshenko {
2144063f925SOleksandr Tymoshenko 	uint32_t bank, data, offset;
2154063f925SOleksandr Tymoshenko 
21690576f54SOleksandr Tymoshenko 	/* Must be called with lock held. */
21790576f54SOleksandr Tymoshenko 	BCM_GPIO_LOCK_ASSERT(sc);
21890576f54SOleksandr Tymoshenko 
2194063f925SOleksandr Tymoshenko 	/* Five banks, 10 pins per bank, 3 bits per pin. */
2204063f925SOleksandr Tymoshenko 	bank = pin / 10;
2214063f925SOleksandr Tymoshenko 	offset = (pin - bank * 10) * 3;
2224063f925SOleksandr Tymoshenko 
2234063f925SOleksandr Tymoshenko 	data = BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank));
2244063f925SOleksandr Tymoshenko 	data &= ~(7 << offset);
2254063f925SOleksandr Tymoshenko 	data |= (f << offset);
2264063f925SOleksandr Tymoshenko 	BCM_GPIO_WRITE(sc, BCM_GPIO_GPFSEL(bank), data);
2274063f925SOleksandr Tymoshenko }
2284063f925SOleksandr Tymoshenko 
2294063f925SOleksandr Tymoshenko static void
2304063f925SOleksandr Tymoshenko bcm_gpio_set_pud(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t state)
2314063f925SOleksandr Tymoshenko {
2324063f925SOleksandr Tymoshenko 	uint32_t bank, offset;
2334063f925SOleksandr Tymoshenko 
23490576f54SOleksandr Tymoshenko 	/* Must be called with lock held. */
23590576f54SOleksandr Tymoshenko 	BCM_GPIO_LOCK_ASSERT(sc);
23690576f54SOleksandr Tymoshenko 
2374063f925SOleksandr Tymoshenko 	bank = pin / 32;
2384063f925SOleksandr Tymoshenko 	offset = pin - 32 * bank;
2394063f925SOleksandr Tymoshenko 
2404063f925SOleksandr Tymoshenko 	BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), state);
2414063f925SOleksandr Tymoshenko 	DELAY(10);
2424063f925SOleksandr Tymoshenko 	BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), (1 << offset));
2434063f925SOleksandr Tymoshenko 	DELAY(10);
2444063f925SOleksandr Tymoshenko 	BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), 0);
2454063f925SOleksandr Tymoshenko 	BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), 0);
2464063f925SOleksandr Tymoshenko }
2474063f925SOleksandr Tymoshenko 
24844d06d8dSLuiz Otavio O Souza void
24944d06d8dSLuiz Otavio O Souza bcm_gpio_set_alternate(device_t dev, uint32_t pin, uint32_t nfunc)
25044d06d8dSLuiz Otavio O Souza {
25144d06d8dSLuiz Otavio O Souza 	struct bcm_gpio_softc *sc;
25244d06d8dSLuiz Otavio O Souza 	int i;
25344d06d8dSLuiz Otavio O Souza 
25444d06d8dSLuiz Otavio O Souza 	sc = device_get_softc(dev);
25544d06d8dSLuiz Otavio O Souza 	BCM_GPIO_LOCK(sc);
25644d06d8dSLuiz Otavio O Souza 
25744d06d8dSLuiz Otavio O Souza 	/* Disable pull-up or pull-down on pin. */
25844d06d8dSLuiz Otavio O Souza 	bcm_gpio_set_pud(sc, pin, BCM_GPIO_NONE);
25944d06d8dSLuiz Otavio O Souza 
26044d06d8dSLuiz Otavio O Souza 	/* And now set the pin function. */
26144d06d8dSLuiz Otavio O Souza 	bcm_gpio_set_function(sc, pin, nfunc);
26244d06d8dSLuiz Otavio O Souza 
26344d06d8dSLuiz Otavio O Souza 	/* Update the pin flags. */
26444d06d8dSLuiz Otavio O Souza 	for (i = 0; i < sc->sc_gpio_npins; i++) {
26544d06d8dSLuiz Otavio O Souza 		if (sc->sc_gpio_pins[i].gp_pin == pin)
26644d06d8dSLuiz Otavio O Souza 			break;
26744d06d8dSLuiz Otavio O Souza 	}
26844d06d8dSLuiz Otavio O Souza 	if (i < sc->sc_gpio_npins)
26944d06d8dSLuiz Otavio O Souza 		sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(nfunc);
27044d06d8dSLuiz Otavio O Souza 
27144d06d8dSLuiz Otavio O Souza         BCM_GPIO_UNLOCK(sc);
27244d06d8dSLuiz Otavio O Souza }
27344d06d8dSLuiz Otavio O Souza 
2744063f925SOleksandr Tymoshenko static void
2754063f925SOleksandr Tymoshenko bcm_gpio_pin_configure(struct bcm_gpio_softc *sc, struct gpio_pin *pin,
2764063f925SOleksandr Tymoshenko     unsigned int flags)
2774063f925SOleksandr Tymoshenko {
2784063f925SOleksandr Tymoshenko 
27990576f54SOleksandr Tymoshenko 	BCM_GPIO_LOCK(sc);
28090576f54SOleksandr Tymoshenko 
2814063f925SOleksandr Tymoshenko 	/*
2824063f925SOleksandr Tymoshenko 	 * Manage input/output.
2834063f925SOleksandr Tymoshenko 	 */
2844063f925SOleksandr Tymoshenko 	if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) {
2854063f925SOleksandr Tymoshenko 		pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT);
2864063f925SOleksandr Tymoshenko 		if (flags & GPIO_PIN_OUTPUT) {
2874063f925SOleksandr Tymoshenko 			pin->gp_flags |= GPIO_PIN_OUTPUT;
2884063f925SOleksandr Tymoshenko 			bcm_gpio_set_function(sc, pin->gp_pin,
2894063f925SOleksandr Tymoshenko 			    BCM_GPIO_OUTPUT);
2904063f925SOleksandr Tymoshenko 		} else {
2914063f925SOleksandr Tymoshenko 			pin->gp_flags |= GPIO_PIN_INPUT;
2924063f925SOleksandr Tymoshenko 			bcm_gpio_set_function(sc, pin->gp_pin,
2934063f925SOleksandr Tymoshenko 			    BCM_GPIO_INPUT);
2944063f925SOleksandr Tymoshenko 		}
2954063f925SOleksandr Tymoshenko 	}
2964063f925SOleksandr Tymoshenko 
2974063f925SOleksandr Tymoshenko 	/* Manage Pull-up/pull-down. */
2984063f925SOleksandr Tymoshenko 	pin->gp_flags &= ~(GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN);
2994063f925SOleksandr Tymoshenko 	if (flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) {
3004063f925SOleksandr Tymoshenko 		if (flags & GPIO_PIN_PULLUP) {
3014063f925SOleksandr Tymoshenko 			pin->gp_flags |= GPIO_PIN_PULLUP;
3024063f925SOleksandr Tymoshenko 			bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLUP);
3034063f925SOleksandr Tymoshenko 		} else {
3044063f925SOleksandr Tymoshenko 			pin->gp_flags |= GPIO_PIN_PULLDOWN;
3054063f925SOleksandr Tymoshenko 			bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLDOWN);
3064063f925SOleksandr Tymoshenko 		}
3074063f925SOleksandr Tymoshenko 	} else
3084063f925SOleksandr Tymoshenko 		bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_NONE);
30990576f54SOleksandr Tymoshenko 
31090576f54SOleksandr Tymoshenko 	BCM_GPIO_UNLOCK(sc);
3114063f925SOleksandr Tymoshenko }
3124063f925SOleksandr Tymoshenko 
3134063f925SOleksandr Tymoshenko static int
3144063f925SOleksandr Tymoshenko bcm_gpio_pin_max(device_t dev, int *maxpin)
3154063f925SOleksandr Tymoshenko {
3164063f925SOleksandr Tymoshenko 
3174063f925SOleksandr Tymoshenko 	*maxpin = BCM_GPIO_PINS - 1;
3184063f925SOleksandr Tymoshenko 	return (0);
3194063f925SOleksandr Tymoshenko }
3204063f925SOleksandr Tymoshenko 
3214063f925SOleksandr Tymoshenko static int
3224063f925SOleksandr Tymoshenko bcm_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
3234063f925SOleksandr Tymoshenko {
3244063f925SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc = device_get_softc(dev);
3254063f925SOleksandr Tymoshenko 	int i;
3264063f925SOleksandr Tymoshenko 
3274063f925SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_gpio_npins; i++) {
3284063f925SOleksandr Tymoshenko 		if (sc->sc_gpio_pins[i].gp_pin == pin)
3294063f925SOleksandr Tymoshenko 			break;
3304063f925SOleksandr Tymoshenko 	}
3314063f925SOleksandr Tymoshenko 
3324063f925SOleksandr Tymoshenko 	if (i >= sc->sc_gpio_npins)
3334063f925SOleksandr Tymoshenko 		return (EINVAL);
3344063f925SOleksandr Tymoshenko 
3354063f925SOleksandr Tymoshenko 	BCM_GPIO_LOCK(sc);
3364063f925SOleksandr Tymoshenko 	*caps = sc->sc_gpio_pins[i].gp_caps;
3374063f925SOleksandr Tymoshenko 	BCM_GPIO_UNLOCK(sc);
3384063f925SOleksandr Tymoshenko 
3394063f925SOleksandr Tymoshenko 	return (0);
3404063f925SOleksandr Tymoshenko }
3414063f925SOleksandr Tymoshenko 
3424063f925SOleksandr Tymoshenko static int
3434063f925SOleksandr Tymoshenko bcm_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
3444063f925SOleksandr Tymoshenko {
3454063f925SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc = device_get_softc(dev);
3464063f925SOleksandr Tymoshenko 	int i;
3474063f925SOleksandr Tymoshenko 
3484063f925SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_gpio_npins; i++) {
3494063f925SOleksandr Tymoshenko 		if (sc->sc_gpio_pins[i].gp_pin == pin)
3504063f925SOleksandr Tymoshenko 			break;
3514063f925SOleksandr Tymoshenko 	}
3524063f925SOleksandr Tymoshenko 
3534063f925SOleksandr Tymoshenko 	if (i >= sc->sc_gpio_npins)
3544063f925SOleksandr Tymoshenko 		return (EINVAL);
3554063f925SOleksandr Tymoshenko 
3564063f925SOleksandr Tymoshenko 	BCM_GPIO_LOCK(sc);
3574063f925SOleksandr Tymoshenko 	*flags = sc->sc_gpio_pins[i].gp_flags;
3584063f925SOleksandr Tymoshenko 	BCM_GPIO_UNLOCK(sc);
3594063f925SOleksandr Tymoshenko 
3604063f925SOleksandr Tymoshenko 	return (0);
3614063f925SOleksandr Tymoshenko }
3624063f925SOleksandr Tymoshenko 
3634063f925SOleksandr Tymoshenko static int
3644063f925SOleksandr Tymoshenko bcm_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
3654063f925SOleksandr Tymoshenko {
3664063f925SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc = device_get_softc(dev);
3674063f925SOleksandr Tymoshenko 	int i;
3684063f925SOleksandr Tymoshenko 
3694063f925SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_gpio_npins; i++) {
3704063f925SOleksandr Tymoshenko 		if (sc->sc_gpio_pins[i].gp_pin == pin)
3714063f925SOleksandr Tymoshenko 			break;
3724063f925SOleksandr Tymoshenko 	}
3734063f925SOleksandr Tymoshenko 
3744063f925SOleksandr Tymoshenko 	if (i >= sc->sc_gpio_npins)
3754063f925SOleksandr Tymoshenko 		return (EINVAL);
3764063f925SOleksandr Tymoshenko 
3774063f925SOleksandr Tymoshenko 	BCM_GPIO_LOCK(sc);
3784063f925SOleksandr Tymoshenko 	memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME);
3794063f925SOleksandr Tymoshenko 	BCM_GPIO_UNLOCK(sc);
3804063f925SOleksandr Tymoshenko 
3814063f925SOleksandr Tymoshenko 	return (0);
3824063f925SOleksandr Tymoshenko }
3834063f925SOleksandr Tymoshenko 
3844063f925SOleksandr Tymoshenko static int
3854063f925SOleksandr Tymoshenko bcm_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
3864063f925SOleksandr Tymoshenko {
3874063f925SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc = device_get_softc(dev);
3884063f925SOleksandr Tymoshenko 	int i;
3894063f925SOleksandr Tymoshenko 
3904063f925SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_gpio_npins; i++) {
3914063f925SOleksandr Tymoshenko 		if (sc->sc_gpio_pins[i].gp_pin == pin)
3924063f925SOleksandr Tymoshenko 			break;
3934063f925SOleksandr Tymoshenko 	}
3944063f925SOleksandr Tymoshenko 
3954063f925SOleksandr Tymoshenko 	if (i >= sc->sc_gpio_npins)
3964063f925SOleksandr Tymoshenko 		return (EINVAL);
3974063f925SOleksandr Tymoshenko 
3984063f925SOleksandr Tymoshenko 	/* We never touch on read-only/reserved pins. */
3994063f925SOleksandr Tymoshenko 	if (bcm_gpio_pin_is_ro(sc, pin))
4004063f925SOleksandr Tymoshenko 		return (EINVAL);
4014063f925SOleksandr Tymoshenko 
40227e644a8SDimitry Andric 	/* Check for unwanted flags. */
40327e644a8SDimitry Andric 	if ((flags & sc->sc_gpio_pins[i].gp_caps) != flags)
4044063f925SOleksandr Tymoshenko 		return (EINVAL);
4054063f925SOleksandr Tymoshenko 
4064063f925SOleksandr Tymoshenko 	/* Can't mix input/output together. */
4074063f925SOleksandr Tymoshenko 	if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) ==
4084063f925SOleksandr Tymoshenko 	    (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT))
4094063f925SOleksandr Tymoshenko 		return (EINVAL);
4104063f925SOleksandr Tymoshenko 
4114063f925SOleksandr Tymoshenko 	/* Can't mix pull-up/pull-down together. */
4124063f925SOleksandr Tymoshenko 	if ((flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) ==
4134063f925SOleksandr Tymoshenko 	    (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN))
4144063f925SOleksandr Tymoshenko 		return (EINVAL);
4154063f925SOleksandr Tymoshenko 
4164063f925SOleksandr Tymoshenko 	bcm_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags);
4174063f925SOleksandr Tymoshenko 
4184063f925SOleksandr Tymoshenko 	return (0);
4194063f925SOleksandr Tymoshenko }
4204063f925SOleksandr Tymoshenko 
4214063f925SOleksandr Tymoshenko static int
4224063f925SOleksandr Tymoshenko bcm_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
4234063f925SOleksandr Tymoshenko {
4244063f925SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc = device_get_softc(dev);
4254063f925SOleksandr Tymoshenko 	uint32_t bank, offset;
4264063f925SOleksandr Tymoshenko 	int i;
4274063f925SOleksandr Tymoshenko 
4284063f925SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_gpio_npins; i++) {
4294063f925SOleksandr Tymoshenko 		if (sc->sc_gpio_pins[i].gp_pin == pin)
4304063f925SOleksandr Tymoshenko 			break;
4314063f925SOleksandr Tymoshenko 	}
4324063f925SOleksandr Tymoshenko 
4334063f925SOleksandr Tymoshenko 	if (i >= sc->sc_gpio_npins)
4344063f925SOleksandr Tymoshenko 		return (EINVAL);
4354063f925SOleksandr Tymoshenko 
4364063f925SOleksandr Tymoshenko 	/* We never write to read-only/reserved pins. */
4374063f925SOleksandr Tymoshenko 	if (bcm_gpio_pin_is_ro(sc, pin))
4384063f925SOleksandr Tymoshenko 		return (EINVAL);
4394063f925SOleksandr Tymoshenko 
4404063f925SOleksandr Tymoshenko 	bank = pin / 32;
4414063f925SOleksandr Tymoshenko 	offset = pin - 32 * bank;
4424063f925SOleksandr Tymoshenko 
4434063f925SOleksandr Tymoshenko 	BCM_GPIO_LOCK(sc);
4444063f925SOleksandr Tymoshenko 	if (value)
4454063f925SOleksandr Tymoshenko 		BCM_GPIO_WRITE(sc, BCM_GPIO_GPSET(bank), (1 << offset));
4464063f925SOleksandr Tymoshenko 	else
4474063f925SOleksandr Tymoshenko 		BCM_GPIO_WRITE(sc, BCM_GPIO_GPCLR(bank), (1 << offset));
4484063f925SOleksandr Tymoshenko 	BCM_GPIO_UNLOCK(sc);
4494063f925SOleksandr Tymoshenko 
4504063f925SOleksandr Tymoshenko 	return (0);
4514063f925SOleksandr Tymoshenko }
4524063f925SOleksandr Tymoshenko 
4534063f925SOleksandr Tymoshenko static int
4544063f925SOleksandr Tymoshenko bcm_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
4554063f925SOleksandr Tymoshenko {
4564063f925SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc = device_get_softc(dev);
4574063f925SOleksandr Tymoshenko 	uint32_t bank, offset, reg_data;
4584063f925SOleksandr Tymoshenko 	int i;
4594063f925SOleksandr Tymoshenko 
4604063f925SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_gpio_npins; i++) {
4614063f925SOleksandr Tymoshenko 		if (sc->sc_gpio_pins[i].gp_pin == pin)
4624063f925SOleksandr Tymoshenko 			break;
4634063f925SOleksandr Tymoshenko 	}
4644063f925SOleksandr Tymoshenko 
4654063f925SOleksandr Tymoshenko 	if (i >= sc->sc_gpio_npins)
4664063f925SOleksandr Tymoshenko 		return (EINVAL);
4674063f925SOleksandr Tymoshenko 
4684063f925SOleksandr Tymoshenko 	bank = pin / 32;
4694063f925SOleksandr Tymoshenko 	offset = pin - 32 * bank;
4704063f925SOleksandr Tymoshenko 
4714063f925SOleksandr Tymoshenko 	BCM_GPIO_LOCK(sc);
4724063f925SOleksandr Tymoshenko 	reg_data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank));
4734063f925SOleksandr Tymoshenko 	BCM_GPIO_UNLOCK(sc);
4744063f925SOleksandr Tymoshenko 	*val = (reg_data & (1 << offset)) ? 1 : 0;
4754063f925SOleksandr Tymoshenko 
4764063f925SOleksandr Tymoshenko 	return (0);
4774063f925SOleksandr Tymoshenko }
4784063f925SOleksandr Tymoshenko 
4794063f925SOleksandr Tymoshenko static int
4804063f925SOleksandr Tymoshenko bcm_gpio_pin_toggle(device_t dev, uint32_t pin)
4814063f925SOleksandr Tymoshenko {
4824063f925SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc = device_get_softc(dev);
4834063f925SOleksandr Tymoshenko 	uint32_t bank, data, offset;
4844063f925SOleksandr Tymoshenko 	int i;
4854063f925SOleksandr Tymoshenko 
4864063f925SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_gpio_npins; i++) {
4874063f925SOleksandr Tymoshenko 		if (sc->sc_gpio_pins[i].gp_pin == pin)
4884063f925SOleksandr Tymoshenko 			break;
4894063f925SOleksandr Tymoshenko 	}
4904063f925SOleksandr Tymoshenko 
4914063f925SOleksandr Tymoshenko 	if (i >= sc->sc_gpio_npins)
4924063f925SOleksandr Tymoshenko 		return (EINVAL);
4934063f925SOleksandr Tymoshenko 
4944063f925SOleksandr Tymoshenko 	/* We never write to read-only/reserved pins. */
4954063f925SOleksandr Tymoshenko 	if (bcm_gpio_pin_is_ro(sc, pin))
4964063f925SOleksandr Tymoshenko 		return (EINVAL);
4974063f925SOleksandr Tymoshenko 
4984063f925SOleksandr Tymoshenko 	bank = pin / 32;
4994063f925SOleksandr Tymoshenko 	offset = pin - 32 * bank;
5004063f925SOleksandr Tymoshenko 
5014063f925SOleksandr Tymoshenko 	BCM_GPIO_LOCK(sc);
5024063f925SOleksandr Tymoshenko 	data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank));
5034063f925SOleksandr Tymoshenko 	if (data & (1 << offset))
5044063f925SOleksandr Tymoshenko 		BCM_GPIO_WRITE(sc, BCM_GPIO_GPCLR(bank), (1 << offset));
5054063f925SOleksandr Tymoshenko 	else
5064063f925SOleksandr Tymoshenko 		BCM_GPIO_WRITE(sc, BCM_GPIO_GPSET(bank), (1 << offset));
5074063f925SOleksandr Tymoshenko 	BCM_GPIO_UNLOCK(sc);
5084063f925SOleksandr Tymoshenko 
5094063f925SOleksandr Tymoshenko 	return (0);
5104063f925SOleksandr Tymoshenko }
5114063f925SOleksandr Tymoshenko 
5124063f925SOleksandr Tymoshenko static int
5134063f925SOleksandr Tymoshenko bcm_gpio_get_ro_pins(struct bcm_gpio_softc *sc)
5144063f925SOleksandr Tymoshenko {
5154063f925SOleksandr Tymoshenko 	int i, len;
5164063f925SOleksandr Tymoshenko 	pcell_t pins[BCM_GPIO_PINS];
5174063f925SOleksandr Tymoshenko 	phandle_t gpio;
5184063f925SOleksandr Tymoshenko 
5194063f925SOleksandr Tymoshenko 	/* Find the gpio node to start. */
5204063f925SOleksandr Tymoshenko 	gpio = ofw_bus_get_node(sc->sc_dev);
5214063f925SOleksandr Tymoshenko 
5224063f925SOleksandr Tymoshenko 	len = OF_getproplen(gpio, "broadcom,read-only");
5234063f925SOleksandr Tymoshenko 	if (len < 0 || len > sizeof(pins))
5244063f925SOleksandr Tymoshenko 		return (-1);
5254063f925SOleksandr Tymoshenko 
5264063f925SOleksandr Tymoshenko 	if (OF_getprop(gpio, "broadcom,read-only", &pins, len) < 0)
5274063f925SOleksandr Tymoshenko 		return (-1);
5284063f925SOleksandr Tymoshenko 
5294063f925SOleksandr Tymoshenko 	sc->sc_ro_npins = len / sizeof(pcell_t);
5304063f925SOleksandr Tymoshenko 
5314063f925SOleksandr Tymoshenko 	device_printf(sc->sc_dev, "read-only pins: ");
5324063f925SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_ro_npins; i++) {
5334063f925SOleksandr Tymoshenko 		sc->sc_ro_pins[i] = fdt32_to_cpu(pins[i]);
5344063f925SOleksandr Tymoshenko 		if (i > 0)
5354063f925SOleksandr Tymoshenko 			printf(",");
5364063f925SOleksandr Tymoshenko 		printf("%d", sc->sc_ro_pins[i]);
5374063f925SOleksandr Tymoshenko 	}
5384063f925SOleksandr Tymoshenko 	if (i > 0)
5394063f925SOleksandr Tymoshenko 		printf(".");
5404063f925SOleksandr Tymoshenko 	printf("\n");
5414063f925SOleksandr Tymoshenko 
5424063f925SOleksandr Tymoshenko 	return (0);
5434063f925SOleksandr Tymoshenko }
5444063f925SOleksandr Tymoshenko 
5454063f925SOleksandr Tymoshenko static int
54690576f54SOleksandr Tymoshenko bcm_gpio_func_proc(SYSCTL_HANDLER_ARGS)
54790576f54SOleksandr Tymoshenko {
54890576f54SOleksandr Tymoshenko 	char buf[16];
54990576f54SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc;
55090576f54SOleksandr Tymoshenko 	struct bcm_gpio_sysctl *sc_sysctl;
55190576f54SOleksandr Tymoshenko 	uint32_t nfunc;
55244d06d8dSLuiz Otavio O Souza 	int error;
55390576f54SOleksandr Tymoshenko 
55490576f54SOleksandr Tymoshenko 	sc_sysctl = arg1;
55590576f54SOleksandr Tymoshenko 	sc = sc_sysctl->sc;
55690576f54SOleksandr Tymoshenko 
55790576f54SOleksandr Tymoshenko 	/* Get the current pin function. */
55890576f54SOleksandr Tymoshenko 	nfunc = bcm_gpio_get_function(sc, sc_sysctl->pin);
55990576f54SOleksandr Tymoshenko 	bcm_gpio_func_str(nfunc, buf, sizeof(buf));
56090576f54SOleksandr Tymoshenko 
56190576f54SOleksandr Tymoshenko 	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
56290576f54SOleksandr Tymoshenko 	if (error != 0 || req->newptr == NULL)
56390576f54SOleksandr Tymoshenko 		return (error);
56490576f54SOleksandr Tymoshenko 
56590576f54SOleksandr Tymoshenko 	/* Parse the user supplied string and check for a valid pin function. */
56690576f54SOleksandr Tymoshenko 	if (bcm_gpio_str_func(buf, &nfunc) != 0)
56790576f54SOleksandr Tymoshenko 		return (EINVAL);
56890576f54SOleksandr Tymoshenko 
56944d06d8dSLuiz Otavio O Souza 	/* Update the pin alternate function. */
57044d06d8dSLuiz Otavio O Souza 	bcm_gpio_set_alternate(sc->sc_dev, sc_sysctl->pin, nfunc);
57190576f54SOleksandr Tymoshenko 
57290576f54SOleksandr Tymoshenko 	return (0);
57390576f54SOleksandr Tymoshenko }
57490576f54SOleksandr Tymoshenko 
57590576f54SOleksandr Tymoshenko static void
57690576f54SOleksandr Tymoshenko bcm_gpio_sysctl_init(struct bcm_gpio_softc *sc)
57790576f54SOleksandr Tymoshenko {
57890576f54SOleksandr Tymoshenko 	char pinbuf[3];
57990576f54SOleksandr Tymoshenko 	struct bcm_gpio_sysctl *sc_sysctl;
58090576f54SOleksandr Tymoshenko 	struct sysctl_ctx_list *ctx;
58190576f54SOleksandr Tymoshenko 	struct sysctl_oid *tree_node, *pin_node, *pinN_node;
58290576f54SOleksandr Tymoshenko 	struct sysctl_oid_list *tree, *pin_tree, *pinN_tree;
58390576f54SOleksandr Tymoshenko 	int i;
58490576f54SOleksandr Tymoshenko 
58590576f54SOleksandr Tymoshenko 	/*
58690576f54SOleksandr Tymoshenko 	 * Add per-pin sysctl tree/handlers.
58790576f54SOleksandr Tymoshenko 	 */
58890576f54SOleksandr Tymoshenko 	ctx = device_get_sysctl_ctx(sc->sc_dev);
58990576f54SOleksandr Tymoshenko  	tree_node = device_get_sysctl_tree(sc->sc_dev);
59090576f54SOleksandr Tymoshenko  	tree = SYSCTL_CHILDREN(tree_node);
59190576f54SOleksandr Tymoshenko 	pin_node = SYSCTL_ADD_NODE(ctx, tree, OID_AUTO, "pin",
5921fbabb48SLuiz Otavio O Souza 	    CTLFLAG_RD, NULL, "GPIO Pins");
59390576f54SOleksandr Tymoshenko 	pin_tree = SYSCTL_CHILDREN(pin_node);
59490576f54SOleksandr Tymoshenko 
59590576f54SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_gpio_npins; i++) {
59690576f54SOleksandr Tymoshenko 
59790576f54SOleksandr Tymoshenko 		snprintf(pinbuf, sizeof(pinbuf), "%d", i);
59890576f54SOleksandr Tymoshenko 		pinN_node = SYSCTL_ADD_NODE(ctx, pin_tree, OID_AUTO, pinbuf,
59990576f54SOleksandr Tymoshenko 		    CTLFLAG_RD, NULL, "GPIO Pin");
60090576f54SOleksandr Tymoshenko 		pinN_tree = SYSCTL_CHILDREN(pinN_node);
60190576f54SOleksandr Tymoshenko 
60290576f54SOleksandr Tymoshenko 		sc->sc_sysctl[i].sc = sc;
60390576f54SOleksandr Tymoshenko 		sc_sysctl = &sc->sc_sysctl[i];
60490576f54SOleksandr Tymoshenko 		sc_sysctl->sc = sc;
60590576f54SOleksandr Tymoshenko 		sc_sysctl->pin = sc->sc_gpio_pins[i].gp_pin;
60690576f54SOleksandr Tymoshenko 		SYSCTL_ADD_PROC(ctx, pinN_tree, OID_AUTO, "function",
60790576f54SOleksandr Tymoshenko 		    CTLFLAG_RW | CTLTYPE_STRING, sc_sysctl,
60890576f54SOleksandr Tymoshenko 		    sizeof(struct bcm_gpio_sysctl), bcm_gpio_func_proc,
60990576f54SOleksandr Tymoshenko 		    "A", "Pin Function");
61090576f54SOleksandr Tymoshenko 	}
61190576f54SOleksandr Tymoshenko }
61290576f54SOleksandr Tymoshenko 
61390576f54SOleksandr Tymoshenko static int
6144063f925SOleksandr Tymoshenko bcm_gpio_get_reserved_pins(struct bcm_gpio_softc *sc)
6154063f925SOleksandr Tymoshenko {
6164063f925SOleksandr Tymoshenko 	int i, j, len, npins;
6174063f925SOleksandr Tymoshenko 	pcell_t pins[BCM_GPIO_PINS];
6184063f925SOleksandr Tymoshenko 	phandle_t gpio, node, reserved;
6194063f925SOleksandr Tymoshenko 	char name[32];
6204063f925SOleksandr Tymoshenko 
6214063f925SOleksandr Tymoshenko 	/* Get read-only pins. */
6224063f925SOleksandr Tymoshenko 	if (bcm_gpio_get_ro_pins(sc) != 0)
6234063f925SOleksandr Tymoshenko 		return (-1);
6244063f925SOleksandr Tymoshenko 
6254063f925SOleksandr Tymoshenko 	/* Find the gpio/reserved pins node to start. */
6264063f925SOleksandr Tymoshenko 	gpio = ofw_bus_get_node(sc->sc_dev);
6274063f925SOleksandr Tymoshenko 	node = OF_child(gpio);
6284063f925SOleksandr Tymoshenko 
6294063f925SOleksandr Tymoshenko 	/*
6304063f925SOleksandr Tymoshenko 	 * Find reserved node
6314063f925SOleksandr Tymoshenko 	 */
6324063f925SOleksandr Tymoshenko 	reserved = 0;
6334063f925SOleksandr Tymoshenko 	while ((node != 0) && (reserved == 0)) {
6344063f925SOleksandr Tymoshenko 		len = OF_getprop(node, "name", name,
6354063f925SOleksandr Tymoshenko 		    sizeof(name) - 1);
6364063f925SOleksandr Tymoshenko 		name[len] = 0;
6374063f925SOleksandr Tymoshenko 		if (strcmp(name, "reserved") == 0)
6384063f925SOleksandr Tymoshenko 			reserved = node;
6394063f925SOleksandr Tymoshenko 		node = OF_peer(node);
6404063f925SOleksandr Tymoshenko 	}
6414063f925SOleksandr Tymoshenko 
6424063f925SOleksandr Tymoshenko 	if (reserved == 0)
6434063f925SOleksandr Tymoshenko 		return (-1);
6444063f925SOleksandr Tymoshenko 
6454063f925SOleksandr Tymoshenko 	/* Get the reserved pins. */
6464063f925SOleksandr Tymoshenko 	len = OF_getproplen(reserved, "broadcom,pins");
6474063f925SOleksandr Tymoshenko 	if (len < 0 || len > sizeof(pins))
6484063f925SOleksandr Tymoshenko 		return (-1);
6494063f925SOleksandr Tymoshenko 
6504063f925SOleksandr Tymoshenko 	if (OF_getprop(reserved, "broadcom,pins", &pins, len) < 0)
6514063f925SOleksandr Tymoshenko 		return (-1);
6524063f925SOleksandr Tymoshenko 
6534063f925SOleksandr Tymoshenko 	npins = len / sizeof(pcell_t);
6544063f925SOleksandr Tymoshenko 
6554063f925SOleksandr Tymoshenko 	j = 0;
6564063f925SOleksandr Tymoshenko 	device_printf(sc->sc_dev, "reserved pins: ");
6574063f925SOleksandr Tymoshenko 	for (i = 0; i < npins; i++) {
6584063f925SOleksandr Tymoshenko 		if (i > 0)
6594063f925SOleksandr Tymoshenko 			printf(",");
6604063f925SOleksandr Tymoshenko 		printf("%d", fdt32_to_cpu(pins[i]));
6614063f925SOleksandr Tymoshenko 		/* Some pins maybe already on the list of read-only pins. */
6624063f925SOleksandr Tymoshenko 		if (bcm_gpio_pin_is_ro(sc, fdt32_to_cpu(pins[i])))
6634063f925SOleksandr Tymoshenko 			continue;
6644063f925SOleksandr Tymoshenko 		sc->sc_ro_pins[j++ + sc->sc_ro_npins] = fdt32_to_cpu(pins[i]);
6654063f925SOleksandr Tymoshenko 	}
6664063f925SOleksandr Tymoshenko 	sc->sc_ro_npins += j;
6674063f925SOleksandr Tymoshenko 	if (i > 0)
6684063f925SOleksandr Tymoshenko 		printf(".");
6694063f925SOleksandr Tymoshenko 	printf("\n");
6704063f925SOleksandr Tymoshenko 
6714063f925SOleksandr Tymoshenko 	return (0);
6724063f925SOleksandr Tymoshenko }
6734063f925SOleksandr Tymoshenko 
6744063f925SOleksandr Tymoshenko static int
6754063f925SOleksandr Tymoshenko bcm_gpio_probe(device_t dev)
6764063f925SOleksandr Tymoshenko {
677*add35ed5SIan Lepore 
678*add35ed5SIan Lepore 	if (!ofw_bus_status_okay(dev))
679*add35ed5SIan Lepore 		return (ENXIO);
680*add35ed5SIan Lepore 
6814063f925SOleksandr Tymoshenko 	if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-gpio"))
6824063f925SOleksandr Tymoshenko 		return (ENXIO);
6834063f925SOleksandr Tymoshenko 
6844063f925SOleksandr Tymoshenko 	device_set_desc(dev, "BCM2708/2835 GPIO controller");
6854063f925SOleksandr Tymoshenko 	return (BUS_PROBE_DEFAULT);
6864063f925SOleksandr Tymoshenko }
6874063f925SOleksandr Tymoshenko 
6884063f925SOleksandr Tymoshenko static int
6894063f925SOleksandr Tymoshenko bcm_gpio_attach(device_t dev)
6904063f925SOleksandr Tymoshenko {
6914063f925SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc = device_get_softc(dev);
69290576f54SOleksandr Tymoshenko 	uint32_t func;
6934063f925SOleksandr Tymoshenko 	int i, j, rid;
6944063f925SOleksandr Tymoshenko 	phandle_t gpio;
6954063f925SOleksandr Tymoshenko 
6964063f925SOleksandr Tymoshenko 	sc->sc_dev = dev;
6974063f925SOleksandr Tymoshenko 
6984063f925SOleksandr Tymoshenko 	mtx_init(&sc->sc_mtx, "bcm gpio", "gpio", MTX_DEF);
6994063f925SOleksandr Tymoshenko 
7004063f925SOleksandr Tymoshenko 	rid = 0;
7014063f925SOleksandr Tymoshenko 	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
7024063f925SOleksandr Tymoshenko 	    RF_ACTIVE);
7034063f925SOleksandr Tymoshenko 	if (!sc->sc_mem_res) {
7044063f925SOleksandr Tymoshenko 		device_printf(dev, "cannot allocate memory window\n");
7054063f925SOleksandr Tymoshenko 		return (ENXIO);
7064063f925SOleksandr Tymoshenko 	}
7074063f925SOleksandr Tymoshenko 
7084063f925SOleksandr Tymoshenko 	sc->sc_bst = rman_get_bustag(sc->sc_mem_res);
7094063f925SOleksandr Tymoshenko 	sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res);
7104063f925SOleksandr Tymoshenko 
7114063f925SOleksandr Tymoshenko 	rid = 0;
7124063f925SOleksandr Tymoshenko 	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
7134063f925SOleksandr Tymoshenko 	    RF_ACTIVE);
7144063f925SOleksandr Tymoshenko 	if (!sc->sc_irq_res) {
7154063f925SOleksandr Tymoshenko 		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
7164063f925SOleksandr Tymoshenko 		device_printf(dev, "cannot allocate interrupt\n");
7174063f925SOleksandr Tymoshenko 		return (ENXIO);
7184063f925SOleksandr Tymoshenko 	}
7194063f925SOleksandr Tymoshenko 
7204063f925SOleksandr Tymoshenko 	/* Find our node. */
7214063f925SOleksandr Tymoshenko 	gpio = ofw_bus_get_node(sc->sc_dev);
7224063f925SOleksandr Tymoshenko 
7234063f925SOleksandr Tymoshenko 	if (!OF_hasprop(gpio, "gpio-controller"))
7244063f925SOleksandr Tymoshenko 		/* Node is not a GPIO controller. */
7254063f925SOleksandr Tymoshenko 		goto fail;
7264063f925SOleksandr Tymoshenko 
7274063f925SOleksandr Tymoshenko 	/*
7284063f925SOleksandr Tymoshenko 	 * Find the read-only pins.  These are pins we never touch or bad
7294063f925SOleksandr Tymoshenko 	 * things could happen.
7304063f925SOleksandr Tymoshenko 	 */
7314063f925SOleksandr Tymoshenko 	if (bcm_gpio_get_reserved_pins(sc) == -1)
7324063f925SOleksandr Tymoshenko 		goto fail;
7334063f925SOleksandr Tymoshenko 
7344063f925SOleksandr Tymoshenko 	/* Initialize the software controlled pins. */
7358d900240SLuiz Otavio O Souza 	for (i = 0, j = 0; j < BCM_GPIO_PINS; j++) {
7364063f925SOleksandr Tymoshenko 		if (bcm_gpio_pin_is_ro(sc, j))
7374063f925SOleksandr Tymoshenko 			continue;
7384063f925SOleksandr Tymoshenko 		snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
7394063f925SOleksandr Tymoshenko 		    "pin %d", j);
74090576f54SOleksandr Tymoshenko 		func = bcm_gpio_get_function(sc, j);
7414063f925SOleksandr Tymoshenko 		sc->sc_gpio_pins[i].gp_pin = j;
7424063f925SOleksandr Tymoshenko 		sc->sc_gpio_pins[i].gp_caps = BCM_GPIO_DEFAULT_CAPS;
74390576f54SOleksandr Tymoshenko 		sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(func);
7444063f925SOleksandr Tymoshenko 		i++;
7454063f925SOleksandr Tymoshenko 	}
7464063f925SOleksandr Tymoshenko 	sc->sc_gpio_npins = i;
7474063f925SOleksandr Tymoshenko 
74890576f54SOleksandr Tymoshenko 	bcm_gpio_sysctl_init(sc);
74990576f54SOleksandr Tymoshenko 
7504063f925SOleksandr Tymoshenko 	device_add_child(dev, "gpioc", device_get_unit(dev));
7514063f925SOleksandr Tymoshenko 	device_add_child(dev, "gpiobus", device_get_unit(dev));
7524063f925SOleksandr Tymoshenko 	return (bus_generic_attach(dev));
7534063f925SOleksandr Tymoshenko 
7544063f925SOleksandr Tymoshenko fail:
7554063f925SOleksandr Tymoshenko 	if (sc->sc_irq_res)
7564063f925SOleksandr Tymoshenko 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
7574063f925SOleksandr Tymoshenko 	if (sc->sc_mem_res)
7584063f925SOleksandr Tymoshenko 		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
7594063f925SOleksandr Tymoshenko 	return (ENXIO);
7604063f925SOleksandr Tymoshenko }
7614063f925SOleksandr Tymoshenko 
7624063f925SOleksandr Tymoshenko static int
7634063f925SOleksandr Tymoshenko bcm_gpio_detach(device_t dev)
7644063f925SOleksandr Tymoshenko {
7654063f925SOleksandr Tymoshenko 
7664063f925SOleksandr Tymoshenko 	return (EBUSY);
7674063f925SOleksandr Tymoshenko }
7684063f925SOleksandr Tymoshenko 
7694063f925SOleksandr Tymoshenko static device_method_t bcm_gpio_methods[] = {
7704063f925SOleksandr Tymoshenko 	/* Device interface */
7714063f925SOleksandr Tymoshenko 	DEVMETHOD(device_probe,		bcm_gpio_probe),
7724063f925SOleksandr Tymoshenko 	DEVMETHOD(device_attach,	bcm_gpio_attach),
7734063f925SOleksandr Tymoshenko 	DEVMETHOD(device_detach,	bcm_gpio_detach),
7744063f925SOleksandr Tymoshenko 
7754063f925SOleksandr Tymoshenko 	/* GPIO protocol */
7764063f925SOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_max,		bcm_gpio_pin_max),
7774063f925SOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_getname,	bcm_gpio_pin_getname),
7784063f925SOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_getflags,	bcm_gpio_pin_getflags),
7794063f925SOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_getcaps,	bcm_gpio_pin_getcaps),
7804063f925SOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_setflags,	bcm_gpio_pin_setflags),
7814063f925SOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_get,		bcm_gpio_pin_get),
7824063f925SOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_set,		bcm_gpio_pin_set),
7834063f925SOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_toggle,	bcm_gpio_pin_toggle),
7844063f925SOleksandr Tymoshenko 
7854063f925SOleksandr Tymoshenko 	DEVMETHOD_END
7864063f925SOleksandr Tymoshenko };
7874063f925SOleksandr Tymoshenko 
7884063f925SOleksandr Tymoshenko static devclass_t bcm_gpio_devclass;
7894063f925SOleksandr Tymoshenko 
7904063f925SOleksandr Tymoshenko static driver_t bcm_gpio_driver = {
7914063f925SOleksandr Tymoshenko 	"gpio",
7924063f925SOleksandr Tymoshenko 	bcm_gpio_methods,
7934063f925SOleksandr Tymoshenko 	sizeof(struct bcm_gpio_softc),
7944063f925SOleksandr Tymoshenko };
7954063f925SOleksandr Tymoshenko 
7964063f925SOleksandr Tymoshenko DRIVER_MODULE(bcm_gpio, simplebus, bcm_gpio_driver, bcm_gpio_devclass, 0, 0);
797