xref: /freebsd/sys/arm/broadcom/bcm2835/bcm2835_gpio.c (revision 5508fc88ba535747904ab8d1d6ea7f4c8efde862)
14063f925SOleksandr Tymoshenko /*-
2*5508fc88SLuiz Otavio O Souza  * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
3*5508fc88SLuiz Otavio O Souza  * Copyright (c) 2012-2015 Luiz Otavio O Souza <loos@FreeBSD.org>
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 #include <sys/gpio.h>
3512471cecSLuiz Otavio O Souza #include <sys/interrupt.h>
36e74d6e2aSLuiz Otavio O Souza #include <sys/kernel.h>
37e74d6e2aSLuiz Otavio O Souza #include <sys/lock.h>
38e74d6e2aSLuiz Otavio O Souza #include <sys/module.h>
39e74d6e2aSLuiz Otavio O Souza #include <sys/mutex.h>
40e74d6e2aSLuiz Otavio O Souza #include <sys/rman.h>
4190576f54SOleksandr Tymoshenko #include <sys/sysctl.h>
424063f925SOleksandr Tymoshenko 
434063f925SOleksandr Tymoshenko #include <machine/bus.h>
444063f925SOleksandr Tymoshenko 
457836352bSLuiz Otavio O Souza #include <dev/gpio/gpiobusvar.h>
464063f925SOleksandr Tymoshenko #include <dev/ofw/ofw_bus.h>
474063f925SOleksandr Tymoshenko 
4844d06d8dSLuiz Otavio O Souza #include <arm/broadcom/bcm2835/bcm2835_gpio.h>
4944d06d8dSLuiz Otavio O Souza 
504063f925SOleksandr Tymoshenko #include "gpio_if.h"
514063f925SOleksandr Tymoshenko 
524063f925SOleksandr Tymoshenko #ifdef DEBUG
534063f925SOleksandr Tymoshenko #define dprintf(fmt, args...) do { printf("%s(): ", __func__);   \
544063f925SOleksandr Tymoshenko     printf(fmt,##args); } while (0)
554063f925SOleksandr Tymoshenko #else
564063f925SOleksandr Tymoshenko #define dprintf(fmt, args...)
574063f925SOleksandr Tymoshenko #endif
584063f925SOleksandr Tymoshenko 
59c2136589SLuiz Otavio O Souza #define	BCM_GPIO_IRQS		4
604063f925SOleksandr Tymoshenko #define	BCM_GPIO_PINS		54
6112471cecSLuiz Otavio O Souza #define	BCM_GPIO_PINS_PER_BANK	32
624063f925SOleksandr Tymoshenko #define	BCM_GPIO_DEFAULT_CAPS	(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |	\
634063f925SOleksandr Tymoshenko     GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN)
644063f925SOleksandr Tymoshenko 
65c2136589SLuiz Otavio O Souza static struct resource_spec bcm_gpio_res_spec[] = {
66c2136589SLuiz Otavio O Souza 	{ SYS_RES_MEMORY, 0, RF_ACTIVE },
67c2136589SLuiz Otavio O Souza 	{ SYS_RES_IRQ, 0, RF_ACTIVE },
68c2136589SLuiz Otavio O Souza 	{ SYS_RES_IRQ, 1, RF_ACTIVE },
69c2136589SLuiz Otavio O Souza 	{ SYS_RES_IRQ, 2, RF_ACTIVE },
70c2136589SLuiz Otavio O Souza 	{ SYS_RES_IRQ, 3, RF_ACTIVE },
71c2136589SLuiz Otavio O Souza 	{ -1, 0, 0 }
72c2136589SLuiz Otavio O Souza };
73c2136589SLuiz Otavio O Souza 
7490576f54SOleksandr Tymoshenko struct bcm_gpio_sysctl {
7590576f54SOleksandr Tymoshenko 	struct bcm_gpio_softc	*sc;
7690576f54SOleksandr Tymoshenko 	uint32_t		pin;
7790576f54SOleksandr Tymoshenko };
7890576f54SOleksandr Tymoshenko 
794063f925SOleksandr Tymoshenko struct bcm_gpio_softc {
804063f925SOleksandr Tymoshenko 	device_t		sc_dev;
817836352bSLuiz Otavio O Souza 	device_t		sc_busdev;
824063f925SOleksandr Tymoshenko 	struct mtx		sc_mtx;
83c2136589SLuiz Otavio O Souza 	struct resource *	sc_res[BCM_GPIO_IRQS + 1];
844063f925SOleksandr Tymoshenko 	bus_space_tag_t		sc_bst;
854063f925SOleksandr Tymoshenko 	bus_space_handle_t	sc_bsh;
8612471cecSLuiz Otavio O Souza 	void *			sc_intrhand[BCM_GPIO_IRQS];
874063f925SOleksandr Tymoshenko 	int			sc_gpio_npins;
884063f925SOleksandr Tymoshenko 	int			sc_ro_npins;
894063f925SOleksandr Tymoshenko 	int			sc_ro_pins[BCM_GPIO_PINS];
904063f925SOleksandr Tymoshenko 	struct gpio_pin		sc_gpio_pins[BCM_GPIO_PINS];
9112471cecSLuiz Otavio O Souza 	struct intr_event *	sc_events[BCM_GPIO_PINS];
9290576f54SOleksandr Tymoshenko 	struct bcm_gpio_sysctl	sc_sysctl[BCM_GPIO_PINS];
9312471cecSLuiz Otavio O Souza 	enum intr_trigger	sc_irq_trigger[BCM_GPIO_PINS];
9412471cecSLuiz Otavio O Souza 	enum intr_polarity	sc_irq_polarity[BCM_GPIO_PINS];
954063f925SOleksandr Tymoshenko };
964063f925SOleksandr Tymoshenko 
974063f925SOleksandr Tymoshenko enum bcm_gpio_pud {
984063f925SOleksandr Tymoshenko 	BCM_GPIO_NONE,
994063f925SOleksandr Tymoshenko 	BCM_GPIO_PULLDOWN,
1004063f925SOleksandr Tymoshenko 	BCM_GPIO_PULLUP,
1014063f925SOleksandr Tymoshenko };
1024063f925SOleksandr Tymoshenko 
10312471cecSLuiz Otavio O Souza #define	BCM_GPIO_LOCK(_sc)	mtx_lock_spin(&(_sc)->sc_mtx)
10412471cecSLuiz Otavio O Souza #define	BCM_GPIO_UNLOCK(_sc)	mtx_unlock_spin(&(_sc)->sc_mtx)
10512471cecSLuiz Otavio O Souza #define	BCM_GPIO_LOCK_ASSERT(_sc)	mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
1064063f925SOleksandr Tymoshenko #define	BCM_GPIO_WRITE(_sc, _off, _val)		\
10712471cecSLuiz Otavio O Souza     bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, _off, _val)
1084063f925SOleksandr Tymoshenko #define	BCM_GPIO_READ(_sc, _off)		\
10912471cecSLuiz Otavio O Souza     bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, _off)
11012471cecSLuiz Otavio O Souza #define	BCM_GPIO_CLEAR_BITS(_sc, _off, _bits)	\
11112471cecSLuiz Otavio O Souza     BCM_GPIO_WRITE(_sc, _off, BCM_GPIO_READ(_sc, _off) & ~(_bits))
11212471cecSLuiz Otavio O Souza #define	BCM_GPIO_SET_BITS(_sc, _off, _bits)	\
11312471cecSLuiz Otavio O Souza     BCM_GPIO_WRITE(_sc, _off, BCM_GPIO_READ(_sc, _off) | _bits)
11412471cecSLuiz Otavio O Souza #define	BCM_GPIO_BANK(a)	(a / BCM_GPIO_PINS_PER_BANK)
11512471cecSLuiz Otavio O Souza #define	BCM_GPIO_MASK(a)	(1U << (a % BCM_GPIO_PINS_PER_BANK))
11612471cecSLuiz Otavio O Souza 
11712471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPFSEL(_bank)	(0x00 + _bank * 4)	/* Function Select */
11812471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPSET(_bank)	(0x1c + _bank * 4)	/* Pin Out Set */
11912471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPCLR(_bank)	(0x28 + _bank * 4)	/* Pin Out Clear */
12012471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPLEV(_bank)	(0x34 + _bank * 4)	/* Pin Level */
12112471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPEDS(_bank)	(0x40 + _bank * 4)	/* Event Status */
12212471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPREN(_bank)	(0x4c + _bank * 4)	/* Rising Edge irq */
12312471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPFEN(_bank)	(0x58 + _bank * 4)	/* Falling Edge irq */
12412471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPHEN(_bank)	(0x64 + _bank * 4)	/* High Level irq */
12512471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPLEN(_bank)	(0x70 + _bank * 4)	/* Low Level irq */
12612471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPAREN(_bank)	(0x7c + _bank * 4)	/* Async Rising Edge */
12712471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPAFEN(_bank)	(0x88 + _bank * 4)	/* Async Falling Egde */
12812471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPPUD(_bank)	(0x94)			/* Pin Pull up/down */
12912471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPPUDCLK(_bank) (0x98 + _bank * 4)	/* Pin Pull up clock */
13012471cecSLuiz Otavio O Souza 
13112471cecSLuiz Otavio O Souza static struct bcm_gpio_softc *bcm_gpio_sc = NULL;
1324063f925SOleksandr Tymoshenko 
1334063f925SOleksandr Tymoshenko static int
1344063f925SOleksandr Tymoshenko bcm_gpio_pin_is_ro(struct bcm_gpio_softc *sc, int pin)
1354063f925SOleksandr Tymoshenko {
1364063f925SOleksandr Tymoshenko 	int i;
1374063f925SOleksandr Tymoshenko 
1384063f925SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_ro_npins; i++)
1394063f925SOleksandr Tymoshenko 		if (pin == sc->sc_ro_pins[i])
1404063f925SOleksandr Tymoshenko 			return (1);
1414063f925SOleksandr Tymoshenko 	return (0);
1424063f925SOleksandr Tymoshenko }
1434063f925SOleksandr Tymoshenko 
1444063f925SOleksandr Tymoshenko static uint32_t
1454063f925SOleksandr Tymoshenko bcm_gpio_get_function(struct bcm_gpio_softc *sc, uint32_t pin)
1464063f925SOleksandr Tymoshenko {
14790576f54SOleksandr Tymoshenko 	uint32_t bank, func, offset;
1484063f925SOleksandr Tymoshenko 
1494063f925SOleksandr Tymoshenko 	/* Five banks, 10 pins per bank, 3 bits per pin. */
1504063f925SOleksandr Tymoshenko 	bank = pin / 10;
1514063f925SOleksandr Tymoshenko 	offset = (pin - bank * 10) * 3;
1524063f925SOleksandr Tymoshenko 
1534063f925SOleksandr Tymoshenko 	BCM_GPIO_LOCK(sc);
15490576f54SOleksandr Tymoshenko 	func = (BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank)) >> offset) & 7;
1554063f925SOleksandr Tymoshenko 	BCM_GPIO_UNLOCK(sc);
1564063f925SOleksandr Tymoshenko 
15790576f54SOleksandr Tymoshenko 	return (func);
15890576f54SOleksandr Tymoshenko }
15990576f54SOleksandr Tymoshenko 
16090576f54SOleksandr Tymoshenko static void
16190576f54SOleksandr Tymoshenko bcm_gpio_func_str(uint32_t nfunc, char *buf, int bufsize)
16290576f54SOleksandr Tymoshenko {
16390576f54SOleksandr Tymoshenko 
16490576f54SOleksandr Tymoshenko 	switch (nfunc) {
1654063f925SOleksandr Tymoshenko 	case BCM_GPIO_INPUT:
16690576f54SOleksandr Tymoshenko 		strncpy(buf, "input", bufsize);
1674063f925SOleksandr Tymoshenko 		break;
1684063f925SOleksandr Tymoshenko 	case BCM_GPIO_OUTPUT:
16990576f54SOleksandr Tymoshenko 		strncpy(buf, "output", bufsize);
1704063f925SOleksandr Tymoshenko 		break;
1714063f925SOleksandr Tymoshenko 	case BCM_GPIO_ALT0:
17290576f54SOleksandr Tymoshenko 		strncpy(buf, "alt0", bufsize);
1734063f925SOleksandr Tymoshenko 		break;
1744063f925SOleksandr Tymoshenko 	case BCM_GPIO_ALT1:
17590576f54SOleksandr Tymoshenko 		strncpy(buf, "alt1", bufsize);
1764063f925SOleksandr Tymoshenko 		break;
1774063f925SOleksandr Tymoshenko 	case BCM_GPIO_ALT2:
17890576f54SOleksandr Tymoshenko 		strncpy(buf, "alt2", bufsize);
1794063f925SOleksandr Tymoshenko 		break;
1804063f925SOleksandr Tymoshenko 	case BCM_GPIO_ALT3:
18190576f54SOleksandr Tymoshenko 		strncpy(buf, "alt3", bufsize);
1824063f925SOleksandr Tymoshenko 		break;
1834063f925SOleksandr Tymoshenko 	case BCM_GPIO_ALT4:
18490576f54SOleksandr Tymoshenko 		strncpy(buf, "alt4", bufsize);
1854063f925SOleksandr Tymoshenko 		break;
1864063f925SOleksandr Tymoshenko 	case BCM_GPIO_ALT5:
18790576f54SOleksandr Tymoshenko 		strncpy(buf, "alt5", bufsize);
1884063f925SOleksandr Tymoshenko 		break;
18990576f54SOleksandr Tymoshenko 	default:
19090576f54SOleksandr Tymoshenko 		strncpy(buf, "invalid", bufsize);
1914063f925SOleksandr Tymoshenko 	}
19290576f54SOleksandr Tymoshenko }
1934063f925SOleksandr Tymoshenko 
19490576f54SOleksandr Tymoshenko static int
19590576f54SOleksandr Tymoshenko bcm_gpio_str_func(char *func, uint32_t *nfunc)
19690576f54SOleksandr Tymoshenko {
19790576f54SOleksandr Tymoshenko 
19890576f54SOleksandr Tymoshenko 	if (strcasecmp(func, "input") == 0)
19990576f54SOleksandr Tymoshenko 		*nfunc = BCM_GPIO_INPUT;
20090576f54SOleksandr Tymoshenko 	else if (strcasecmp(func, "output") == 0)
20190576f54SOleksandr Tymoshenko 		*nfunc = BCM_GPIO_OUTPUT;
20290576f54SOleksandr Tymoshenko 	else if (strcasecmp(func, "alt0") == 0)
20390576f54SOleksandr Tymoshenko 		*nfunc = BCM_GPIO_ALT0;
20490576f54SOleksandr Tymoshenko 	else if (strcasecmp(func, "alt1") == 0)
20590576f54SOleksandr Tymoshenko 		*nfunc = BCM_GPIO_ALT1;
20690576f54SOleksandr Tymoshenko 	else if (strcasecmp(func, "alt2") == 0)
20790576f54SOleksandr Tymoshenko 		*nfunc = BCM_GPIO_ALT2;
20890576f54SOleksandr Tymoshenko 	else if (strcasecmp(func, "alt3") == 0)
20990576f54SOleksandr Tymoshenko 		*nfunc = BCM_GPIO_ALT3;
21090576f54SOleksandr Tymoshenko 	else if (strcasecmp(func, "alt4") == 0)
21190576f54SOleksandr Tymoshenko 		*nfunc = BCM_GPIO_ALT4;
21290576f54SOleksandr Tymoshenko 	else if (strcasecmp(func, "alt5") == 0)
21390576f54SOleksandr Tymoshenko 		*nfunc = BCM_GPIO_ALT5;
21490576f54SOleksandr Tymoshenko 	else
21590576f54SOleksandr Tymoshenko 		return (-1);
21690576f54SOleksandr Tymoshenko 
21790576f54SOleksandr Tymoshenko 	return (0);
21890576f54SOleksandr Tymoshenko }
21990576f54SOleksandr Tymoshenko 
22090576f54SOleksandr Tymoshenko static uint32_t
22190576f54SOleksandr Tymoshenko bcm_gpio_func_flag(uint32_t nfunc)
22290576f54SOleksandr Tymoshenko {
22390576f54SOleksandr Tymoshenko 
22490576f54SOleksandr Tymoshenko 	switch (nfunc) {
2254063f925SOleksandr Tymoshenko 	case BCM_GPIO_INPUT:
2264063f925SOleksandr Tymoshenko 		return (GPIO_PIN_INPUT);
2274063f925SOleksandr Tymoshenko 	case BCM_GPIO_OUTPUT:
2284063f925SOleksandr Tymoshenko 		return (GPIO_PIN_OUTPUT);
2294063f925SOleksandr Tymoshenko 	}
2304063f925SOleksandr Tymoshenko 	return (0);
2314063f925SOleksandr Tymoshenko }
2324063f925SOleksandr Tymoshenko 
2334063f925SOleksandr Tymoshenko static void
2344063f925SOleksandr Tymoshenko bcm_gpio_set_function(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t f)
2354063f925SOleksandr Tymoshenko {
2364063f925SOleksandr Tymoshenko 	uint32_t bank, data, offset;
2374063f925SOleksandr Tymoshenko 
23890576f54SOleksandr Tymoshenko 	/* Must be called with lock held. */
23990576f54SOleksandr Tymoshenko 	BCM_GPIO_LOCK_ASSERT(sc);
24090576f54SOleksandr Tymoshenko 
2414063f925SOleksandr Tymoshenko 	/* Five banks, 10 pins per bank, 3 bits per pin. */
2424063f925SOleksandr Tymoshenko 	bank = pin / 10;
2434063f925SOleksandr Tymoshenko 	offset = (pin - bank * 10) * 3;
2444063f925SOleksandr Tymoshenko 
2454063f925SOleksandr Tymoshenko 	data = BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank));
2464063f925SOleksandr Tymoshenko 	data &= ~(7 << offset);
2474063f925SOleksandr Tymoshenko 	data |= (f << offset);
2484063f925SOleksandr Tymoshenko 	BCM_GPIO_WRITE(sc, BCM_GPIO_GPFSEL(bank), data);
2494063f925SOleksandr Tymoshenko }
2504063f925SOleksandr Tymoshenko 
2514063f925SOleksandr Tymoshenko static void
2524063f925SOleksandr Tymoshenko bcm_gpio_set_pud(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t state)
2534063f925SOleksandr Tymoshenko {
254*5508fc88SLuiz Otavio O Souza 	uint32_t bank;
2554063f925SOleksandr Tymoshenko 
25690576f54SOleksandr Tymoshenko 	/* Must be called with lock held. */
25790576f54SOleksandr Tymoshenko 	BCM_GPIO_LOCK_ASSERT(sc);
25890576f54SOleksandr Tymoshenko 
259*5508fc88SLuiz Otavio O Souza 	bank = BCM_GPIO_BANK(pin);
2604063f925SOleksandr Tymoshenko 	BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), state);
261*5508fc88SLuiz Otavio O Souza 	BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), BCM_GPIO_MASK(pin));
2624063f925SOleksandr Tymoshenko 	BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), 0);
2634063f925SOleksandr Tymoshenko 	BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), 0);
2644063f925SOleksandr Tymoshenko }
2654063f925SOleksandr Tymoshenko 
26644d06d8dSLuiz Otavio O Souza void
26744d06d8dSLuiz Otavio O Souza bcm_gpio_set_alternate(device_t dev, uint32_t pin, uint32_t nfunc)
26844d06d8dSLuiz Otavio O Souza {
26944d06d8dSLuiz Otavio O Souza 	struct bcm_gpio_softc *sc;
27044d06d8dSLuiz Otavio O Souza 	int i;
27144d06d8dSLuiz Otavio O Souza 
27244d06d8dSLuiz Otavio O Souza 	sc = device_get_softc(dev);
27344d06d8dSLuiz Otavio O Souza 	BCM_GPIO_LOCK(sc);
27444d06d8dSLuiz Otavio O Souza 
27544d06d8dSLuiz Otavio O Souza 	/* Disable pull-up or pull-down on pin. */
27644d06d8dSLuiz Otavio O Souza 	bcm_gpio_set_pud(sc, pin, BCM_GPIO_NONE);
27744d06d8dSLuiz Otavio O Souza 
27844d06d8dSLuiz Otavio O Souza 	/* And now set the pin function. */
27944d06d8dSLuiz Otavio O Souza 	bcm_gpio_set_function(sc, pin, nfunc);
28044d06d8dSLuiz Otavio O Souza 
28144d06d8dSLuiz Otavio O Souza 	/* Update the pin flags. */
28244d06d8dSLuiz Otavio O Souza 	for (i = 0; i < sc->sc_gpio_npins; i++) {
28344d06d8dSLuiz Otavio O Souza 		if (sc->sc_gpio_pins[i].gp_pin == pin)
28444d06d8dSLuiz Otavio O Souza 			break;
28544d06d8dSLuiz Otavio O Souza 	}
28644d06d8dSLuiz Otavio O Souza 	if (i < sc->sc_gpio_npins)
28744d06d8dSLuiz Otavio O Souza 		sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(nfunc);
28844d06d8dSLuiz Otavio O Souza 
28944d06d8dSLuiz Otavio O Souza         BCM_GPIO_UNLOCK(sc);
29044d06d8dSLuiz Otavio O Souza }
29144d06d8dSLuiz Otavio O Souza 
2924063f925SOleksandr Tymoshenko static void
2934063f925SOleksandr Tymoshenko bcm_gpio_pin_configure(struct bcm_gpio_softc *sc, struct gpio_pin *pin,
2944063f925SOleksandr Tymoshenko     unsigned int flags)
2954063f925SOleksandr Tymoshenko {
2964063f925SOleksandr Tymoshenko 
29790576f54SOleksandr Tymoshenko 	BCM_GPIO_LOCK(sc);
29890576f54SOleksandr Tymoshenko 
2994063f925SOleksandr Tymoshenko 	/*
3004063f925SOleksandr Tymoshenko 	 * Manage input/output.
3014063f925SOleksandr Tymoshenko 	 */
3024063f925SOleksandr Tymoshenko 	if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) {
3034063f925SOleksandr Tymoshenko 		pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT);
3044063f925SOleksandr Tymoshenko 		if (flags & GPIO_PIN_OUTPUT) {
3054063f925SOleksandr Tymoshenko 			pin->gp_flags |= GPIO_PIN_OUTPUT;
3064063f925SOleksandr Tymoshenko 			bcm_gpio_set_function(sc, pin->gp_pin,
3074063f925SOleksandr Tymoshenko 			    BCM_GPIO_OUTPUT);
3084063f925SOleksandr Tymoshenko 		} else {
3094063f925SOleksandr Tymoshenko 			pin->gp_flags |= GPIO_PIN_INPUT;
3104063f925SOleksandr Tymoshenko 			bcm_gpio_set_function(sc, pin->gp_pin,
3114063f925SOleksandr Tymoshenko 			    BCM_GPIO_INPUT);
3124063f925SOleksandr Tymoshenko 		}
3134063f925SOleksandr Tymoshenko 	}
3144063f925SOleksandr Tymoshenko 
3154063f925SOleksandr Tymoshenko 	/* Manage Pull-up/pull-down. */
3164063f925SOleksandr Tymoshenko 	pin->gp_flags &= ~(GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN);
3174063f925SOleksandr Tymoshenko 	if (flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) {
3184063f925SOleksandr Tymoshenko 		if (flags & GPIO_PIN_PULLUP) {
3194063f925SOleksandr Tymoshenko 			pin->gp_flags |= GPIO_PIN_PULLUP;
3204063f925SOleksandr Tymoshenko 			bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLUP);
3214063f925SOleksandr Tymoshenko 		} else {
3224063f925SOleksandr Tymoshenko 			pin->gp_flags |= GPIO_PIN_PULLDOWN;
3234063f925SOleksandr Tymoshenko 			bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLDOWN);
3244063f925SOleksandr Tymoshenko 		}
3254063f925SOleksandr Tymoshenko 	} else
3264063f925SOleksandr Tymoshenko 		bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_NONE);
32790576f54SOleksandr Tymoshenko 
32890576f54SOleksandr Tymoshenko 	BCM_GPIO_UNLOCK(sc);
3294063f925SOleksandr Tymoshenko }
3304063f925SOleksandr Tymoshenko 
3317836352bSLuiz Otavio O Souza static device_t
3327836352bSLuiz Otavio O Souza bcm_gpio_get_bus(device_t dev)
3337836352bSLuiz Otavio O Souza {
3347836352bSLuiz Otavio O Souza 	struct bcm_gpio_softc *sc;
3357836352bSLuiz Otavio O Souza 
3367836352bSLuiz Otavio O Souza 	sc = device_get_softc(dev);
3377836352bSLuiz Otavio O Souza 
3387836352bSLuiz Otavio O Souza 	return (sc->sc_busdev);
3397836352bSLuiz Otavio O Souza }
3407836352bSLuiz Otavio O Souza 
3414063f925SOleksandr Tymoshenko static int
3424063f925SOleksandr Tymoshenko bcm_gpio_pin_max(device_t dev, int *maxpin)
3434063f925SOleksandr Tymoshenko {
3444063f925SOleksandr Tymoshenko 
3454063f925SOleksandr Tymoshenko 	*maxpin = BCM_GPIO_PINS - 1;
3464063f925SOleksandr Tymoshenko 	return (0);
3474063f925SOleksandr Tymoshenko }
3484063f925SOleksandr Tymoshenko 
3494063f925SOleksandr Tymoshenko static int
3504063f925SOleksandr Tymoshenko bcm_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
3514063f925SOleksandr Tymoshenko {
3524063f925SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc = device_get_softc(dev);
3534063f925SOleksandr Tymoshenko 	int i;
3544063f925SOleksandr Tymoshenko 
3554063f925SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_gpio_npins; i++) {
3564063f925SOleksandr Tymoshenko 		if (sc->sc_gpio_pins[i].gp_pin == pin)
3574063f925SOleksandr Tymoshenko 			break;
3584063f925SOleksandr Tymoshenko 	}
3594063f925SOleksandr Tymoshenko 
3604063f925SOleksandr Tymoshenko 	if (i >= sc->sc_gpio_npins)
3614063f925SOleksandr Tymoshenko 		return (EINVAL);
3624063f925SOleksandr Tymoshenko 
3634063f925SOleksandr Tymoshenko 	BCM_GPIO_LOCK(sc);
3644063f925SOleksandr Tymoshenko 	*caps = sc->sc_gpio_pins[i].gp_caps;
3654063f925SOleksandr Tymoshenko 	BCM_GPIO_UNLOCK(sc);
3664063f925SOleksandr Tymoshenko 
3674063f925SOleksandr Tymoshenko 	return (0);
3684063f925SOleksandr Tymoshenko }
3694063f925SOleksandr Tymoshenko 
3704063f925SOleksandr Tymoshenko static int
3714063f925SOleksandr Tymoshenko bcm_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
3724063f925SOleksandr Tymoshenko {
3734063f925SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc = device_get_softc(dev);
3744063f925SOleksandr Tymoshenko 	int i;
3754063f925SOleksandr Tymoshenko 
3764063f925SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_gpio_npins; i++) {
3774063f925SOleksandr Tymoshenko 		if (sc->sc_gpio_pins[i].gp_pin == pin)
3784063f925SOleksandr Tymoshenko 			break;
3794063f925SOleksandr Tymoshenko 	}
3804063f925SOleksandr Tymoshenko 
3814063f925SOleksandr Tymoshenko 	if (i >= sc->sc_gpio_npins)
3824063f925SOleksandr Tymoshenko 		return (EINVAL);
3834063f925SOleksandr Tymoshenko 
3844063f925SOleksandr Tymoshenko 	BCM_GPIO_LOCK(sc);
3854063f925SOleksandr Tymoshenko 	*flags = sc->sc_gpio_pins[i].gp_flags;
3864063f925SOleksandr Tymoshenko 	BCM_GPIO_UNLOCK(sc);
3874063f925SOleksandr Tymoshenko 
3884063f925SOleksandr Tymoshenko 	return (0);
3894063f925SOleksandr Tymoshenko }
3904063f925SOleksandr Tymoshenko 
3914063f925SOleksandr Tymoshenko static int
3924063f925SOleksandr Tymoshenko bcm_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
3934063f925SOleksandr Tymoshenko {
3944063f925SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc = device_get_softc(dev);
3954063f925SOleksandr Tymoshenko 	int i;
3964063f925SOleksandr Tymoshenko 
3974063f925SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_gpio_npins; i++) {
3984063f925SOleksandr Tymoshenko 		if (sc->sc_gpio_pins[i].gp_pin == pin)
3994063f925SOleksandr Tymoshenko 			break;
4004063f925SOleksandr Tymoshenko 	}
4014063f925SOleksandr Tymoshenko 
4024063f925SOleksandr Tymoshenko 	if (i >= sc->sc_gpio_npins)
4034063f925SOleksandr Tymoshenko 		return (EINVAL);
4044063f925SOleksandr Tymoshenko 
4054063f925SOleksandr Tymoshenko 	BCM_GPIO_LOCK(sc);
4064063f925SOleksandr Tymoshenko 	memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME);
4074063f925SOleksandr Tymoshenko 	BCM_GPIO_UNLOCK(sc);
4084063f925SOleksandr Tymoshenko 
4094063f925SOleksandr Tymoshenko 	return (0);
4104063f925SOleksandr Tymoshenko }
4114063f925SOleksandr Tymoshenko 
4124063f925SOleksandr Tymoshenko static int
4134063f925SOleksandr Tymoshenko bcm_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
4144063f925SOleksandr Tymoshenko {
4154063f925SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc = device_get_softc(dev);
4164063f925SOleksandr Tymoshenko 	int i;
4174063f925SOleksandr Tymoshenko 
4184063f925SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_gpio_npins; i++) {
4194063f925SOleksandr Tymoshenko 		if (sc->sc_gpio_pins[i].gp_pin == pin)
4204063f925SOleksandr Tymoshenko 			break;
4214063f925SOleksandr Tymoshenko 	}
4224063f925SOleksandr Tymoshenko 
4234063f925SOleksandr Tymoshenko 	if (i >= sc->sc_gpio_npins)
4244063f925SOleksandr Tymoshenko 		return (EINVAL);
4254063f925SOleksandr Tymoshenko 
4264063f925SOleksandr Tymoshenko 	/* We never touch on read-only/reserved pins. */
4274063f925SOleksandr Tymoshenko 	if (bcm_gpio_pin_is_ro(sc, pin))
4284063f925SOleksandr Tymoshenko 		return (EINVAL);
4294063f925SOleksandr Tymoshenko 
4304063f925SOleksandr Tymoshenko 	bcm_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags);
4314063f925SOleksandr Tymoshenko 
4324063f925SOleksandr Tymoshenko 	return (0);
4334063f925SOleksandr Tymoshenko }
4344063f925SOleksandr Tymoshenko 
4354063f925SOleksandr Tymoshenko static int
4364063f925SOleksandr Tymoshenko bcm_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
4374063f925SOleksandr Tymoshenko {
4384063f925SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc = device_get_softc(dev);
439*5508fc88SLuiz Otavio O Souza 	uint32_t bank, reg;
4404063f925SOleksandr Tymoshenko 	int i;
4414063f925SOleksandr Tymoshenko 
4424063f925SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_gpio_npins; i++) {
4434063f925SOleksandr Tymoshenko 		if (sc->sc_gpio_pins[i].gp_pin == pin)
4444063f925SOleksandr Tymoshenko 			break;
4454063f925SOleksandr Tymoshenko 	}
4464063f925SOleksandr Tymoshenko 	if (i >= sc->sc_gpio_npins)
4474063f925SOleksandr Tymoshenko 		return (EINVAL);
4484063f925SOleksandr Tymoshenko 	/* We never write to read-only/reserved pins. */
4494063f925SOleksandr Tymoshenko 	if (bcm_gpio_pin_is_ro(sc, pin))
4504063f925SOleksandr Tymoshenko 		return (EINVAL);
4514063f925SOleksandr Tymoshenko 	BCM_GPIO_LOCK(sc);
452*5508fc88SLuiz Otavio O Souza 	bank = BCM_GPIO_BANK(pin);
4534063f925SOleksandr Tymoshenko 	if (value)
454*5508fc88SLuiz Otavio O Souza 		reg = BCM_GPIO_GPSET(bank);
4554063f925SOleksandr Tymoshenko 	else
456*5508fc88SLuiz Otavio O Souza 		reg = BCM_GPIO_GPCLR(bank);
457*5508fc88SLuiz Otavio O Souza 	BCM_GPIO_WRITE(sc, reg, BCM_GPIO_MASK(pin));
4584063f925SOleksandr Tymoshenko 	BCM_GPIO_UNLOCK(sc);
4594063f925SOleksandr Tymoshenko 
4604063f925SOleksandr Tymoshenko 	return (0);
4614063f925SOleksandr Tymoshenko }
4624063f925SOleksandr Tymoshenko 
4634063f925SOleksandr Tymoshenko static int
4644063f925SOleksandr Tymoshenko bcm_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
4654063f925SOleksandr Tymoshenko {
4664063f925SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc = device_get_softc(dev);
467*5508fc88SLuiz Otavio O Souza 	uint32_t bank, reg_data;
4684063f925SOleksandr Tymoshenko 	int i;
4694063f925SOleksandr Tymoshenko 
4704063f925SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_gpio_npins; i++) {
4714063f925SOleksandr Tymoshenko 		if (sc->sc_gpio_pins[i].gp_pin == pin)
4724063f925SOleksandr Tymoshenko 			break;
4734063f925SOleksandr Tymoshenko 	}
4744063f925SOleksandr Tymoshenko 	if (i >= sc->sc_gpio_npins)
4754063f925SOleksandr Tymoshenko 		return (EINVAL);
476*5508fc88SLuiz Otavio O Souza 	bank = BCM_GPIO_BANK(pin);
4774063f925SOleksandr Tymoshenko 	BCM_GPIO_LOCK(sc);
4784063f925SOleksandr Tymoshenko 	reg_data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank));
4794063f925SOleksandr Tymoshenko 	BCM_GPIO_UNLOCK(sc);
480*5508fc88SLuiz Otavio O Souza 	*val = (reg_data & BCM_GPIO_MASK(pin)) ? 1 : 0;
4814063f925SOleksandr Tymoshenko 
4824063f925SOleksandr Tymoshenko 	return (0);
4834063f925SOleksandr Tymoshenko }
4844063f925SOleksandr Tymoshenko 
4854063f925SOleksandr Tymoshenko static int
4864063f925SOleksandr Tymoshenko bcm_gpio_pin_toggle(device_t dev, uint32_t pin)
4874063f925SOleksandr Tymoshenko {
4884063f925SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc = device_get_softc(dev);
489*5508fc88SLuiz Otavio O Souza 	uint32_t bank, data, reg;
4904063f925SOleksandr Tymoshenko 	int i;
4914063f925SOleksandr Tymoshenko 
4924063f925SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_gpio_npins; i++) {
4934063f925SOleksandr Tymoshenko 		if (sc->sc_gpio_pins[i].gp_pin == pin)
4944063f925SOleksandr Tymoshenko 			break;
4954063f925SOleksandr Tymoshenko 	}
4964063f925SOleksandr Tymoshenko 	if (i >= sc->sc_gpio_npins)
4974063f925SOleksandr Tymoshenko 		return (EINVAL);
4984063f925SOleksandr Tymoshenko 	/* We never write to read-only/reserved pins. */
4994063f925SOleksandr Tymoshenko 	if (bcm_gpio_pin_is_ro(sc, pin))
5004063f925SOleksandr Tymoshenko 		return (EINVAL);
5014063f925SOleksandr Tymoshenko 	BCM_GPIO_LOCK(sc);
502*5508fc88SLuiz Otavio O Souza 	bank = BCM_GPIO_BANK(pin);
5034063f925SOleksandr Tymoshenko 	data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank));
504*5508fc88SLuiz Otavio O Souza 	if (data & BCM_GPIO_MASK(pin))
505*5508fc88SLuiz Otavio O Souza 		reg = BCM_GPIO_GPCLR(bank);
5064063f925SOleksandr Tymoshenko 	else
507*5508fc88SLuiz Otavio O Souza 		reg = BCM_GPIO_GPSET(bank);
508*5508fc88SLuiz Otavio O Souza 	BCM_GPIO_WRITE(sc, reg, BCM_GPIO_MASK(pin));
5094063f925SOleksandr Tymoshenko 	BCM_GPIO_UNLOCK(sc);
5104063f925SOleksandr Tymoshenko 
5114063f925SOleksandr Tymoshenko 	return (0);
5124063f925SOleksandr Tymoshenko }
5134063f925SOleksandr Tymoshenko 
5144063f925SOleksandr Tymoshenko static int
51590576f54SOleksandr Tymoshenko bcm_gpio_func_proc(SYSCTL_HANDLER_ARGS)
51690576f54SOleksandr Tymoshenko {
51790576f54SOleksandr Tymoshenko 	char buf[16];
51890576f54SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc;
51990576f54SOleksandr Tymoshenko 	struct bcm_gpio_sysctl *sc_sysctl;
52090576f54SOleksandr Tymoshenko 	uint32_t nfunc;
52144d06d8dSLuiz Otavio O Souza 	int error;
52290576f54SOleksandr Tymoshenko 
52390576f54SOleksandr Tymoshenko 	sc_sysctl = arg1;
52490576f54SOleksandr Tymoshenko 	sc = sc_sysctl->sc;
52590576f54SOleksandr Tymoshenko 
52690576f54SOleksandr Tymoshenko 	/* Get the current pin function. */
52790576f54SOleksandr Tymoshenko 	nfunc = bcm_gpio_get_function(sc, sc_sysctl->pin);
52890576f54SOleksandr Tymoshenko 	bcm_gpio_func_str(nfunc, buf, sizeof(buf));
52990576f54SOleksandr Tymoshenko 
53090576f54SOleksandr Tymoshenko 	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
53190576f54SOleksandr Tymoshenko 	if (error != 0 || req->newptr == NULL)
53290576f54SOleksandr Tymoshenko 		return (error);
53399a20111SLuiz Otavio O Souza 	/* Ignore changes on read-only pins. */
53499a20111SLuiz Otavio O Souza 	if (bcm_gpio_pin_is_ro(sc, sc_sysctl->pin))
53599a20111SLuiz Otavio O Souza 		return (0);
53690576f54SOleksandr Tymoshenko 	/* Parse the user supplied string and check for a valid pin function. */
53790576f54SOleksandr Tymoshenko 	if (bcm_gpio_str_func(buf, &nfunc) != 0)
53890576f54SOleksandr Tymoshenko 		return (EINVAL);
53990576f54SOleksandr Tymoshenko 
54044d06d8dSLuiz Otavio O Souza 	/* Update the pin alternate function. */
54144d06d8dSLuiz Otavio O Souza 	bcm_gpio_set_alternate(sc->sc_dev, sc_sysctl->pin, nfunc);
54290576f54SOleksandr Tymoshenko 
54390576f54SOleksandr Tymoshenko 	return (0);
54490576f54SOleksandr Tymoshenko }
54590576f54SOleksandr Tymoshenko 
54690576f54SOleksandr Tymoshenko static void
54790576f54SOleksandr Tymoshenko bcm_gpio_sysctl_init(struct bcm_gpio_softc *sc)
54890576f54SOleksandr Tymoshenko {
54990576f54SOleksandr Tymoshenko 	char pinbuf[3];
55090576f54SOleksandr Tymoshenko 	struct bcm_gpio_sysctl *sc_sysctl;
55190576f54SOleksandr Tymoshenko 	struct sysctl_ctx_list *ctx;
55290576f54SOleksandr Tymoshenko 	struct sysctl_oid *tree_node, *pin_node, *pinN_node;
55390576f54SOleksandr Tymoshenko 	struct sysctl_oid_list *tree, *pin_tree, *pinN_tree;
55490576f54SOleksandr Tymoshenko 	int i;
55590576f54SOleksandr Tymoshenko 
55690576f54SOleksandr Tymoshenko 	/*
55790576f54SOleksandr Tymoshenko 	 * Add per-pin sysctl tree/handlers.
55890576f54SOleksandr Tymoshenko 	 */
55990576f54SOleksandr Tymoshenko 	ctx = device_get_sysctl_ctx(sc->sc_dev);
56090576f54SOleksandr Tymoshenko  	tree_node = device_get_sysctl_tree(sc->sc_dev);
56190576f54SOleksandr Tymoshenko  	tree = SYSCTL_CHILDREN(tree_node);
56290576f54SOleksandr Tymoshenko 	pin_node = SYSCTL_ADD_NODE(ctx, tree, OID_AUTO, "pin",
5631fbabb48SLuiz Otavio O Souza 	    CTLFLAG_RD, NULL, "GPIO Pins");
56490576f54SOleksandr Tymoshenko 	pin_tree = SYSCTL_CHILDREN(pin_node);
56590576f54SOleksandr Tymoshenko 
56690576f54SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_gpio_npins; i++) {
56790576f54SOleksandr Tymoshenko 
56890576f54SOleksandr Tymoshenko 		snprintf(pinbuf, sizeof(pinbuf), "%d", i);
56990576f54SOleksandr Tymoshenko 		pinN_node = SYSCTL_ADD_NODE(ctx, pin_tree, OID_AUTO, pinbuf,
57090576f54SOleksandr Tymoshenko 		    CTLFLAG_RD, NULL, "GPIO Pin");
57190576f54SOleksandr Tymoshenko 		pinN_tree = SYSCTL_CHILDREN(pinN_node);
57290576f54SOleksandr Tymoshenko 
57390576f54SOleksandr Tymoshenko 		sc->sc_sysctl[i].sc = sc;
57490576f54SOleksandr Tymoshenko 		sc_sysctl = &sc->sc_sysctl[i];
57590576f54SOleksandr Tymoshenko 		sc_sysctl->sc = sc;
57690576f54SOleksandr Tymoshenko 		sc_sysctl->pin = sc->sc_gpio_pins[i].gp_pin;
57790576f54SOleksandr Tymoshenko 		SYSCTL_ADD_PROC(ctx, pinN_tree, OID_AUTO, "function",
57890576f54SOleksandr Tymoshenko 		    CTLFLAG_RW | CTLTYPE_STRING, sc_sysctl,
57990576f54SOleksandr Tymoshenko 		    sizeof(struct bcm_gpio_sysctl), bcm_gpio_func_proc,
58090576f54SOleksandr Tymoshenko 		    "A", "Pin Function");
58190576f54SOleksandr Tymoshenko 	}
58290576f54SOleksandr Tymoshenko }
58390576f54SOleksandr Tymoshenko 
58490576f54SOleksandr Tymoshenko static int
58599a20111SLuiz Otavio O Souza bcm_gpio_get_ro_pins(struct bcm_gpio_softc *sc, phandle_t node,
58699a20111SLuiz Otavio O Souza 	const char *propname, const char *label)
58799a20111SLuiz Otavio O Souza {
58899a20111SLuiz Otavio O Souza 	int i, need_comma, npins, range_start, range_stop;
58999a20111SLuiz Otavio O Souza 	pcell_t *pins;
59099a20111SLuiz Otavio O Souza 
59199a20111SLuiz Otavio O Souza 	/* Get the property data. */
59299a20111SLuiz Otavio O Souza 	npins = OF_getencprop_alloc(node, propname, sizeof(*pins),
59399a20111SLuiz Otavio O Souza 	    (void **)&pins);
59499a20111SLuiz Otavio O Souza 	if (npins < 0)
59599a20111SLuiz Otavio O Souza 		return (-1);
59699a20111SLuiz Otavio O Souza 	if (npins == 0) {
59799a20111SLuiz Otavio O Souza 		free(pins, M_OFWPROP);
59899a20111SLuiz Otavio O Souza 		return (0);
59999a20111SLuiz Otavio O Souza 	}
60099a20111SLuiz Otavio O Souza 	for (i = 0; i < npins; i++)
60199a20111SLuiz Otavio O Souza 		sc->sc_ro_pins[i + sc->sc_ro_npins] = pins[i];
60299a20111SLuiz Otavio O Souza 	sc->sc_ro_npins += npins;
60399a20111SLuiz Otavio O Souza 	need_comma = 0;
60499a20111SLuiz Otavio O Souza 	device_printf(sc->sc_dev, "%s pins: ", label);
60599a20111SLuiz Otavio O Souza 	range_start = range_stop = pins[0];
60699a20111SLuiz Otavio O Souza 	for (i = 1; i < npins; i++) {
60799a20111SLuiz Otavio O Souza 		if (pins[i] != range_stop + 1) {
60899a20111SLuiz Otavio O Souza 			if (need_comma)
60999a20111SLuiz Otavio O Souza 				printf(",");
61099a20111SLuiz Otavio O Souza 			if (range_start != range_stop)
61199a20111SLuiz Otavio O Souza 				printf("%d-%d", range_start, range_stop);
61299a20111SLuiz Otavio O Souza 			else
61399a20111SLuiz Otavio O Souza 				printf("%d", range_start);
61499a20111SLuiz Otavio O Souza 			range_start = range_stop = pins[i];
61599a20111SLuiz Otavio O Souza 			need_comma = 1;
61699a20111SLuiz Otavio O Souza 		} else
61799a20111SLuiz Otavio O Souza 			range_stop++;
61899a20111SLuiz Otavio O Souza 	}
61999a20111SLuiz Otavio O Souza 	if (need_comma)
62099a20111SLuiz Otavio O Souza 		printf(",");
62199a20111SLuiz Otavio O Souza 	if (range_start != range_stop)
62299a20111SLuiz Otavio O Souza 		printf("%d-%d.\n", range_start, range_stop);
62399a20111SLuiz Otavio O Souza 	else
62499a20111SLuiz Otavio O Souza 		printf("%d.\n", range_start);
62599a20111SLuiz Otavio O Souza 	free(pins, M_OFWPROP);
62699a20111SLuiz Otavio O Souza 
62799a20111SLuiz Otavio O Souza 	return (0);
62899a20111SLuiz Otavio O Souza }
62999a20111SLuiz Otavio O Souza 
63099a20111SLuiz Otavio O Souza static int
6314063f925SOleksandr Tymoshenko bcm_gpio_get_reserved_pins(struct bcm_gpio_softc *sc)
6324063f925SOleksandr Tymoshenko {
63399a20111SLuiz Otavio O Souza 	char *name;
6344063f925SOleksandr Tymoshenko 	phandle_t gpio, node, reserved;
63599a20111SLuiz Otavio O Souza 	ssize_t len;
6364063f925SOleksandr Tymoshenko 
6374063f925SOleksandr Tymoshenko 	/* Get read-only pins. */
6384063f925SOleksandr Tymoshenko 	gpio = ofw_bus_get_node(sc->sc_dev);
63999a20111SLuiz Otavio O Souza 	if (bcm_gpio_get_ro_pins(sc, gpio, "broadcom,read-only",
64099a20111SLuiz Otavio O Souza 	    "read-only") != 0)
64199a20111SLuiz Otavio O Souza 		return (-1);
64299a20111SLuiz Otavio O Souza 	/* Traverse the GPIO subnodes to find the reserved pins node. */
6434063f925SOleksandr Tymoshenko 	reserved = 0;
64499a20111SLuiz Otavio O Souza 	node = OF_child(gpio);
6454063f925SOleksandr Tymoshenko 	while ((node != 0) && (reserved == 0)) {
64699a20111SLuiz Otavio O Souza 		len = OF_getprop_alloc(node, "name", 1, (void **)&name);
64799a20111SLuiz Otavio O Souza 		if (len == -1)
64899a20111SLuiz Otavio O Souza 			return (-1);
6494063f925SOleksandr Tymoshenko 		if (strcmp(name, "reserved") == 0)
6504063f925SOleksandr Tymoshenko 			reserved = node;
65199a20111SLuiz Otavio O Souza 		free(name, M_OFWPROP);
6524063f925SOleksandr Tymoshenko 		node = OF_peer(node);
6534063f925SOleksandr Tymoshenko 	}
6544063f925SOleksandr Tymoshenko 	if (reserved == 0)
6554063f925SOleksandr Tymoshenko 		return (-1);
6564063f925SOleksandr Tymoshenko 	/* Get the reserved pins. */
65799a20111SLuiz Otavio O Souza 	if (bcm_gpio_get_ro_pins(sc, reserved, "broadcom,pins",
65899a20111SLuiz Otavio O Souza 	    "reserved") != 0)
6594063f925SOleksandr Tymoshenko 		return (-1);
6604063f925SOleksandr Tymoshenko 
6614063f925SOleksandr Tymoshenko 	return (0);
6624063f925SOleksandr Tymoshenko }
6634063f925SOleksandr Tymoshenko 
6644063f925SOleksandr Tymoshenko static int
66512471cecSLuiz Otavio O Souza bcm_gpio_intr(void *arg)
66612471cecSLuiz Otavio O Souza {
66712471cecSLuiz Otavio O Souza 	int bank_last, irq;
66812471cecSLuiz Otavio O Souza 	struct bcm_gpio_softc *sc;
66912471cecSLuiz Otavio O Souza 	struct intr_event *event;
67012471cecSLuiz Otavio O Souza 	uint32_t bank, mask, reg;
67112471cecSLuiz Otavio O Souza 
67212471cecSLuiz Otavio O Souza 	sc = (struct bcm_gpio_softc *)arg;
67312471cecSLuiz Otavio O Souza 	reg = 0;
67412471cecSLuiz Otavio O Souza 	bank_last = -1;
67512471cecSLuiz Otavio O Souza 	for (irq = 0; irq < BCM_GPIO_PINS; irq++) {
67612471cecSLuiz Otavio O Souza 		bank = BCM_GPIO_BANK(irq);
67712471cecSLuiz Otavio O Souza 		mask = BCM_GPIO_MASK(irq);
67812471cecSLuiz Otavio O Souza 		if (bank != bank_last) {
67912471cecSLuiz Otavio O Souza 			reg = BCM_GPIO_READ(sc, BCM_GPIO_GPEDS(bank));
68012471cecSLuiz Otavio O Souza 			bank_last = bank;
68112471cecSLuiz Otavio O Souza 		}
68212471cecSLuiz Otavio O Souza 		if (reg & mask) {
68312471cecSLuiz Otavio O Souza 			event = sc->sc_events[irq];
68412471cecSLuiz Otavio O Souza 			if (event != NULL && !TAILQ_EMPTY(&event->ie_handlers))
68512471cecSLuiz Otavio O Souza 				intr_event_handle(event, NULL);
68612471cecSLuiz Otavio O Souza 			else {
68712471cecSLuiz Otavio O Souza 				device_printf(sc->sc_dev, "Stray IRQ %d\n",
68812471cecSLuiz Otavio O Souza 				    irq);
68912471cecSLuiz Otavio O Souza 			}
69012471cecSLuiz Otavio O Souza 			/* Clear the Status bit by writing '1' to it. */
69112471cecSLuiz Otavio O Souza 			BCM_GPIO_WRITE(sc, BCM_GPIO_GPEDS(bank), mask);
69212471cecSLuiz Otavio O Souza 		}
69312471cecSLuiz Otavio O Souza 	}
69412471cecSLuiz Otavio O Souza 
69512471cecSLuiz Otavio O Souza 	return (FILTER_HANDLED);
69612471cecSLuiz Otavio O Souza }
69712471cecSLuiz Otavio O Souza 
69812471cecSLuiz Otavio O Souza static int
6994063f925SOleksandr Tymoshenko bcm_gpio_probe(device_t dev)
7004063f925SOleksandr Tymoshenko {
701add35ed5SIan Lepore 
702add35ed5SIan Lepore 	if (!ofw_bus_status_okay(dev))
703add35ed5SIan Lepore 		return (ENXIO);
704add35ed5SIan Lepore 
7054063f925SOleksandr Tymoshenko 	if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-gpio"))
7064063f925SOleksandr Tymoshenko 		return (ENXIO);
7074063f925SOleksandr Tymoshenko 
7084063f925SOleksandr Tymoshenko 	device_set_desc(dev, "BCM2708/2835 GPIO controller");
7094063f925SOleksandr Tymoshenko 	return (BUS_PROBE_DEFAULT);
7104063f925SOleksandr Tymoshenko }
7114063f925SOleksandr Tymoshenko 
7124063f925SOleksandr Tymoshenko static int
71312471cecSLuiz Otavio O Souza bcm_gpio_intr_attach(device_t dev)
71412471cecSLuiz Otavio O Souza {
71512471cecSLuiz Otavio O Souza 	struct bcm_gpio_softc *sc;
71612471cecSLuiz Otavio O Souza 	int i;
71712471cecSLuiz Otavio O Souza 
71812471cecSLuiz Otavio O Souza 	sc = device_get_softc(dev);
71912471cecSLuiz Otavio O Souza 	for (i = 0; i < BCM_GPIO_IRQS; i++) {
72012471cecSLuiz Otavio O Souza 		if (bus_setup_intr(dev, sc->sc_res[i + 1],
72112471cecSLuiz Otavio O Souza 		    INTR_TYPE_MISC | INTR_MPSAFE, bcm_gpio_intr,
72212471cecSLuiz Otavio O Souza 		    NULL, sc, &sc->sc_intrhand[i]) != 0) {
72312471cecSLuiz Otavio O Souza 			return (-1);
72412471cecSLuiz Otavio O Souza 		}
72512471cecSLuiz Otavio O Souza 	}
72612471cecSLuiz Otavio O Souza 
72712471cecSLuiz Otavio O Souza 	return (0);
72812471cecSLuiz Otavio O Souza }
72912471cecSLuiz Otavio O Souza 
73012471cecSLuiz Otavio O Souza static void
73112471cecSLuiz Otavio O Souza bcm_gpio_intr_detach(device_t dev)
73212471cecSLuiz Otavio O Souza {
73312471cecSLuiz Otavio O Souza 	struct bcm_gpio_softc *sc;
73412471cecSLuiz Otavio O Souza 	int i;
73512471cecSLuiz Otavio O Souza 
73612471cecSLuiz Otavio O Souza 	sc = device_get_softc(dev);
73712471cecSLuiz Otavio O Souza 	for (i = 0; i < BCM_GPIO_IRQS; i++) {
73812471cecSLuiz Otavio O Souza 		if (sc->sc_intrhand[i]) {
73912471cecSLuiz Otavio O Souza 			bus_teardown_intr(dev, sc->sc_res[i + 1],
74012471cecSLuiz Otavio O Souza 			    sc->sc_intrhand[i]);
74112471cecSLuiz Otavio O Souza 		}
74212471cecSLuiz Otavio O Souza 	}
74312471cecSLuiz Otavio O Souza }
74412471cecSLuiz Otavio O Souza 
74512471cecSLuiz Otavio O Souza static int
7464063f925SOleksandr Tymoshenko bcm_gpio_attach(device_t dev)
7474063f925SOleksandr Tymoshenko {
748c2136589SLuiz Otavio O Souza 	int i, j;
7494063f925SOleksandr Tymoshenko 	phandle_t gpio;
750c2136589SLuiz Otavio O Souza 	struct bcm_gpio_softc *sc;
751c2136589SLuiz Otavio O Souza 	uint32_t func;
7524063f925SOleksandr Tymoshenko 
75312471cecSLuiz Otavio O Souza 	if (bcm_gpio_sc != NULL)
75412471cecSLuiz Otavio O Souza 		return (ENXIO);
75512471cecSLuiz Otavio O Souza 
75612471cecSLuiz Otavio O Souza 	bcm_gpio_sc = sc = device_get_softc(dev);
7574063f925SOleksandr Tymoshenko  	sc->sc_dev = dev;
75812471cecSLuiz Otavio O Souza 	mtx_init(&sc->sc_mtx, "bcm gpio", "gpio", MTX_SPIN);
759c2136589SLuiz Otavio O Souza 	if (bus_alloc_resources(dev, bcm_gpio_res_spec, sc->sc_res) != 0) {
760c2136589SLuiz Otavio O Souza 		device_printf(dev, "cannot allocate resources\n");
761c2136589SLuiz Otavio O Souza 		goto fail;
7624063f925SOleksandr Tymoshenko 	}
763c2136589SLuiz Otavio O Souza 	sc->sc_bst = rman_get_bustag(sc->sc_res[0]);
764c2136589SLuiz Otavio O Souza 	sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]);
76512471cecSLuiz Otavio O Souza 	/* Setup the GPIO interrupt handler. */
76612471cecSLuiz Otavio O Souza 	if (bcm_gpio_intr_attach(dev)) {
76712471cecSLuiz Otavio O Souza 		device_printf(dev, "unable to setup the gpio irq handler\n");
76812471cecSLuiz Otavio O Souza 		goto fail;
76912471cecSLuiz Otavio O Souza 	}
7704063f925SOleksandr Tymoshenko 	/* Find our node. */
7714063f925SOleksandr Tymoshenko 	gpio = ofw_bus_get_node(sc->sc_dev);
7724063f925SOleksandr Tymoshenko 	if (!OF_hasprop(gpio, "gpio-controller"))
7734063f925SOleksandr Tymoshenko 		/* Node is not a GPIO controller. */
7744063f925SOleksandr Tymoshenko 		goto fail;
7754063f925SOleksandr Tymoshenko 	/*
7764063f925SOleksandr Tymoshenko 	 * Find the read-only pins.  These are pins we never touch or bad
7774063f925SOleksandr Tymoshenko 	 * things could happen.
7784063f925SOleksandr Tymoshenko 	 */
7794063f925SOleksandr Tymoshenko 	if (bcm_gpio_get_reserved_pins(sc) == -1)
7804063f925SOleksandr Tymoshenko 		goto fail;
7814063f925SOleksandr Tymoshenko 	/* Initialize the software controlled pins. */
7828d900240SLuiz Otavio O Souza 	for (i = 0, j = 0; j < BCM_GPIO_PINS; j++) {
7834063f925SOleksandr Tymoshenko 		snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
7844063f925SOleksandr Tymoshenko 		    "pin %d", j);
78590576f54SOleksandr Tymoshenko 		func = bcm_gpio_get_function(sc, j);
7864063f925SOleksandr Tymoshenko 		sc->sc_gpio_pins[i].gp_pin = j;
7874063f925SOleksandr Tymoshenko 		sc->sc_gpio_pins[i].gp_caps = BCM_GPIO_DEFAULT_CAPS;
78890576f54SOleksandr Tymoshenko 		sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(func);
78912471cecSLuiz Otavio O Souza 		/* The default is active-low interrupts. */
79012471cecSLuiz Otavio O Souza 		sc->sc_irq_trigger[i] = INTR_TRIGGER_LEVEL;
79112471cecSLuiz Otavio O Souza 		sc->sc_irq_polarity[i] = INTR_POLARITY_LOW;
7924063f925SOleksandr Tymoshenko 		i++;
7934063f925SOleksandr Tymoshenko 	}
7944063f925SOleksandr Tymoshenko 	sc->sc_gpio_npins = i;
79590576f54SOleksandr Tymoshenko 	bcm_gpio_sysctl_init(sc);
7967836352bSLuiz Otavio O Souza 	sc->sc_busdev = gpiobus_attach_bus(dev);
7977836352bSLuiz Otavio O Souza 	if (sc->sc_busdev == NULL)
7987836352bSLuiz Otavio O Souza 		goto fail;
79990576f54SOleksandr Tymoshenko 
8007836352bSLuiz Otavio O Souza 	return (0);
8014063f925SOleksandr Tymoshenko 
8024063f925SOleksandr Tymoshenko fail:
80312471cecSLuiz Otavio O Souza 	bcm_gpio_intr_detach(dev);
804c2136589SLuiz Otavio O Souza 	bus_release_resources(dev, bcm_gpio_res_spec, sc->sc_res);
805c2136589SLuiz Otavio O Souza 	mtx_destroy(&sc->sc_mtx);
806c2136589SLuiz Otavio O Souza 
8074063f925SOleksandr Tymoshenko 	return (ENXIO);
8084063f925SOleksandr Tymoshenko }
8094063f925SOleksandr Tymoshenko 
8104063f925SOleksandr Tymoshenko static int
8114063f925SOleksandr Tymoshenko bcm_gpio_detach(device_t dev)
8124063f925SOleksandr Tymoshenko {
8134063f925SOleksandr Tymoshenko 
8144063f925SOleksandr Tymoshenko 	return (EBUSY);
8154063f925SOleksandr Tymoshenko }
8164063f925SOleksandr Tymoshenko 
81712471cecSLuiz Otavio O Souza static uint32_t
81812471cecSLuiz Otavio O Souza bcm_gpio_intr_reg(struct bcm_gpio_softc *sc, unsigned int irq, uint32_t bank)
81912471cecSLuiz Otavio O Souza {
82012471cecSLuiz Otavio O Souza 
82112471cecSLuiz Otavio O Souza 	if (irq > BCM_GPIO_PINS)
82212471cecSLuiz Otavio O Souza 		return (0);
82312471cecSLuiz Otavio O Souza 	if (sc->sc_irq_trigger[irq] == INTR_TRIGGER_LEVEL) {
82412471cecSLuiz Otavio O Souza 		if (sc->sc_irq_polarity[irq] == INTR_POLARITY_LOW)
82512471cecSLuiz Otavio O Souza 			return (BCM_GPIO_GPLEN(bank));
82612471cecSLuiz Otavio O Souza 		else if (sc->sc_irq_polarity[irq] == INTR_POLARITY_HIGH)
82712471cecSLuiz Otavio O Souza 			return (BCM_GPIO_GPHEN(bank));
82812471cecSLuiz Otavio O Souza 	} else if (sc->sc_irq_trigger[irq] == INTR_TRIGGER_EDGE) {
82912471cecSLuiz Otavio O Souza 		if (sc->sc_irq_polarity[irq] == INTR_POLARITY_LOW)
83012471cecSLuiz Otavio O Souza 			return (BCM_GPIO_GPFEN(bank));
83112471cecSLuiz Otavio O Souza 		else if (sc->sc_irq_polarity[irq] == INTR_POLARITY_HIGH)
83212471cecSLuiz Otavio O Souza 			return (BCM_GPIO_GPREN(bank));
83312471cecSLuiz Otavio O Souza 	}
83412471cecSLuiz Otavio O Souza 
83512471cecSLuiz Otavio O Souza 	return (0);
83612471cecSLuiz Otavio O Souza }
83712471cecSLuiz Otavio O Souza 
83812471cecSLuiz Otavio O Souza static void
83912471cecSLuiz Otavio O Souza bcm_gpio_mask_irq(void *source)
84012471cecSLuiz Otavio O Souza {
84112471cecSLuiz Otavio O Souza 	uint32_t bank, mask, reg;
84212471cecSLuiz Otavio O Souza 	unsigned int irq;
84312471cecSLuiz Otavio O Souza 
84412471cecSLuiz Otavio O Souza 	irq = (unsigned int)source;
84512471cecSLuiz Otavio O Souza 	if (irq > BCM_GPIO_PINS)
84612471cecSLuiz Otavio O Souza 		return;
84712471cecSLuiz Otavio O Souza 	if (bcm_gpio_pin_is_ro(bcm_gpio_sc, irq))
84812471cecSLuiz Otavio O Souza 		return;
84912471cecSLuiz Otavio O Souza 	bank = BCM_GPIO_BANK(irq);
85012471cecSLuiz Otavio O Souza 	mask = BCM_GPIO_MASK(irq);
85112471cecSLuiz Otavio O Souza 	BCM_GPIO_LOCK(bcm_gpio_sc);
85212471cecSLuiz Otavio O Souza 	reg = bcm_gpio_intr_reg(bcm_gpio_sc, irq, bank);
85312471cecSLuiz Otavio O Souza 	if (reg != 0)
85412471cecSLuiz Otavio O Souza 		BCM_GPIO_CLEAR_BITS(bcm_gpio_sc, reg, mask);
85512471cecSLuiz Otavio O Souza 	BCM_GPIO_UNLOCK(bcm_gpio_sc);
85612471cecSLuiz Otavio O Souza }
85712471cecSLuiz Otavio O Souza 
85812471cecSLuiz Otavio O Souza static void
85912471cecSLuiz Otavio O Souza bcm_gpio_unmask_irq(void *source)
86012471cecSLuiz Otavio O Souza {
86112471cecSLuiz Otavio O Souza 	uint32_t bank, mask, reg;
86212471cecSLuiz Otavio O Souza 	unsigned int irq;
86312471cecSLuiz Otavio O Souza 
86412471cecSLuiz Otavio O Souza 	irq = (unsigned int)source;
86512471cecSLuiz Otavio O Souza 	if (irq > BCM_GPIO_PINS)
86612471cecSLuiz Otavio O Souza 		return;
86712471cecSLuiz Otavio O Souza 	if (bcm_gpio_pin_is_ro(bcm_gpio_sc, irq))
86812471cecSLuiz Otavio O Souza 		return;
86912471cecSLuiz Otavio O Souza 	bank = BCM_GPIO_BANK(irq);
87012471cecSLuiz Otavio O Souza 	mask = BCM_GPIO_MASK(irq);
87112471cecSLuiz Otavio O Souza 	BCM_GPIO_LOCK(bcm_gpio_sc);
87212471cecSLuiz Otavio O Souza 	reg = bcm_gpio_intr_reg(bcm_gpio_sc, irq, bank);
87312471cecSLuiz Otavio O Souza 	if (reg != 0)
87412471cecSLuiz Otavio O Souza 		BCM_GPIO_SET_BITS(bcm_gpio_sc, reg, mask);
87512471cecSLuiz Otavio O Souza 	BCM_GPIO_UNLOCK(bcm_gpio_sc);
87612471cecSLuiz Otavio O Souza }
87712471cecSLuiz Otavio O Souza 
87812471cecSLuiz Otavio O Souza static int
87912471cecSLuiz Otavio O Souza bcm_gpio_activate_resource(device_t bus, device_t child, int type, int rid,
88012471cecSLuiz Otavio O Souza 	struct resource *res)
88112471cecSLuiz Otavio O Souza {
88212471cecSLuiz Otavio O Souza 	int pin;
88312471cecSLuiz Otavio O Souza 
88412471cecSLuiz Otavio O Souza 	if (type != SYS_RES_IRQ)
88512471cecSLuiz Otavio O Souza 		return (ENXIO);
88612471cecSLuiz Otavio O Souza 	/* Unmask the interrupt. */
88712471cecSLuiz Otavio O Souza 	pin = rman_get_start(res);
88812471cecSLuiz Otavio O Souza 	bcm_gpio_unmask_irq((void *)pin);
88912471cecSLuiz Otavio O Souza 
89012471cecSLuiz Otavio O Souza 	return (0);
89112471cecSLuiz Otavio O Souza }
89212471cecSLuiz Otavio O Souza 
89312471cecSLuiz Otavio O Souza static int
89412471cecSLuiz Otavio O Souza bcm_gpio_deactivate_resource(device_t bus, device_t child, int type, int rid,
89512471cecSLuiz Otavio O Souza 	struct resource *res)
89612471cecSLuiz Otavio O Souza {
89712471cecSLuiz Otavio O Souza 	int pin;
89812471cecSLuiz Otavio O Souza 
89912471cecSLuiz Otavio O Souza 	if (type != SYS_RES_IRQ)
90012471cecSLuiz Otavio O Souza 		return (ENXIO);
90112471cecSLuiz Otavio O Souza 	/* Mask the interrupt. */
90212471cecSLuiz Otavio O Souza 	pin = rman_get_start(res);
90312471cecSLuiz Otavio O Souza 	bcm_gpio_mask_irq((void *)pin);
90412471cecSLuiz Otavio O Souza 
90512471cecSLuiz Otavio O Souza 	return (0);
90612471cecSLuiz Otavio O Souza }
90712471cecSLuiz Otavio O Souza 
90812471cecSLuiz Otavio O Souza static int
90912471cecSLuiz Otavio O Souza bcm_gpio_config_intr(device_t dev, int irq, enum intr_trigger trig,
91012471cecSLuiz Otavio O Souza 	enum intr_polarity pol)
91112471cecSLuiz Otavio O Souza {
91212471cecSLuiz Otavio O Souza 	int bank;
91312471cecSLuiz Otavio O Souza 	struct bcm_gpio_softc *sc;
91412471cecSLuiz Otavio O Souza 	uint32_t mask, oldreg, reg;
91512471cecSLuiz Otavio O Souza 
91612471cecSLuiz Otavio O Souza 	if (irq > BCM_GPIO_PINS)
91712471cecSLuiz Otavio O Souza 		return (EINVAL);
91812471cecSLuiz Otavio O Souza 	/* There is no standard trigger or polarity. */
91912471cecSLuiz Otavio O Souza 	if (trig == INTR_TRIGGER_CONFORM || pol == INTR_POLARITY_CONFORM)
92012471cecSLuiz Otavio O Souza 		return (EINVAL);
92112471cecSLuiz Otavio O Souza 	sc = device_get_softc(dev);
92212471cecSLuiz Otavio O Souza 	if (bcm_gpio_pin_is_ro(sc, irq))
92312471cecSLuiz Otavio O Souza 		return (EINVAL);
92412471cecSLuiz Otavio O Souza 	bank = BCM_GPIO_BANK(irq);
92512471cecSLuiz Otavio O Souza 	mask = BCM_GPIO_MASK(irq);
92612471cecSLuiz Otavio O Souza 	BCM_GPIO_LOCK(sc);
92712471cecSLuiz Otavio O Souza 	oldreg = bcm_gpio_intr_reg(sc, irq, bank);
92812471cecSLuiz Otavio O Souza 	sc->sc_irq_trigger[irq] = trig;
92912471cecSLuiz Otavio O Souza 	sc->sc_irq_polarity[irq] = pol;
93012471cecSLuiz Otavio O Souza 	reg = bcm_gpio_intr_reg(sc, irq, bank);
93112471cecSLuiz Otavio O Souza 	if (reg != 0)
93212471cecSLuiz Otavio O Souza 		BCM_GPIO_SET_BITS(sc, reg, mask);
93312471cecSLuiz Otavio O Souza 	if (reg != oldreg && oldreg != 0)
93412471cecSLuiz Otavio O Souza 		BCM_GPIO_CLEAR_BITS(sc, oldreg, mask);
93512471cecSLuiz Otavio O Souza 	BCM_GPIO_UNLOCK(sc);
93612471cecSLuiz Otavio O Souza 
93712471cecSLuiz Otavio O Souza 	return (0);
93812471cecSLuiz Otavio O Souza }
93912471cecSLuiz Otavio O Souza 
94012471cecSLuiz Otavio O Souza static int
94112471cecSLuiz Otavio O Souza bcm_gpio_setup_intr(device_t bus, device_t child, struct resource *ires,
94212471cecSLuiz Otavio O Souza 	int flags, driver_filter_t *filt, driver_intr_t *handler,
94312471cecSLuiz Otavio O Souza 	void *arg, void **cookiep)
94412471cecSLuiz Otavio O Souza {
94512471cecSLuiz Otavio O Souza 	struct bcm_gpio_softc *sc;
94612471cecSLuiz Otavio O Souza 	struct intr_event *event;
94712471cecSLuiz Otavio O Souza 	int pin, error;
94812471cecSLuiz Otavio O Souza 
94912471cecSLuiz Otavio O Souza 	sc = device_get_softc(bus);
95012471cecSLuiz Otavio O Souza 	pin = rman_get_start(ires);
95112471cecSLuiz Otavio O Souza 	if (pin > BCM_GPIO_PINS)
95212471cecSLuiz Otavio O Souza 		panic("%s: bad pin %d", __func__, pin);
95312471cecSLuiz Otavio O Souza 	event = sc->sc_events[pin];
95412471cecSLuiz Otavio O Souza 	if (event == NULL) {
95512471cecSLuiz Otavio O Souza 		error = intr_event_create(&event, (void *)pin, 0, pin,
95612471cecSLuiz Otavio O Souza 		    bcm_gpio_mask_irq, bcm_gpio_unmask_irq, NULL, NULL,
95712471cecSLuiz Otavio O Souza 		    "gpio%d pin%d:", device_get_unit(bus), pin);
95812471cecSLuiz Otavio O Souza 		if (error != 0)
95912471cecSLuiz Otavio O Souza 			return (error);
96012471cecSLuiz Otavio O Souza 		sc->sc_events[pin] = event;
96112471cecSLuiz Otavio O Souza 	}
96212471cecSLuiz Otavio O Souza 	intr_event_add_handler(event, device_get_nameunit(child), filt,
96312471cecSLuiz Otavio O Souza 	    handler, arg, intr_priority(flags), flags, cookiep);
96412471cecSLuiz Otavio O Souza 
96512471cecSLuiz Otavio O Souza 	return (0);
96612471cecSLuiz Otavio O Souza }
96712471cecSLuiz Otavio O Souza 
96812471cecSLuiz Otavio O Souza static int
96912471cecSLuiz Otavio O Souza bcm_gpio_teardown_intr(device_t dev, device_t child, struct resource *ires,
97012471cecSLuiz Otavio O Souza 	void *cookie)
97112471cecSLuiz Otavio O Souza {
97212471cecSLuiz Otavio O Souza 	struct bcm_gpio_softc *sc;
97312471cecSLuiz Otavio O Souza 	int pin, err;
97412471cecSLuiz Otavio O Souza 
97512471cecSLuiz Otavio O Souza 	sc = device_get_softc(dev);
97612471cecSLuiz Otavio O Souza 	pin = rman_get_start(ires);
97712471cecSLuiz Otavio O Souza 	if (pin > BCM_GPIO_PINS)
97812471cecSLuiz Otavio O Souza 		panic("%s: bad pin %d", __func__, pin);
97912471cecSLuiz Otavio O Souza 	if (sc->sc_events[pin] == NULL)
98012471cecSLuiz Otavio O Souza 		panic("Trying to teardown unoccupied IRQ");
98112471cecSLuiz Otavio O Souza 	err = intr_event_remove_handler(cookie);
98212471cecSLuiz Otavio O Souza 	if (!err)
98312471cecSLuiz Otavio O Souza 		sc->sc_events[pin] = NULL;
98412471cecSLuiz Otavio O Souza 
98512471cecSLuiz Otavio O Souza 	return (err);
98612471cecSLuiz Otavio O Souza }
98712471cecSLuiz Otavio O Souza 
9888c705c2cSLuiz Otavio O Souza static phandle_t
9898c705c2cSLuiz Otavio O Souza bcm_gpio_get_node(device_t bus, device_t dev)
9908c705c2cSLuiz Otavio O Souza {
9918c705c2cSLuiz Otavio O Souza 
9928c705c2cSLuiz Otavio O Souza 	/* We only have one child, the GPIO bus, which needs our own node. */
9938c705c2cSLuiz Otavio O Souza 	return (ofw_bus_get_node(bus));
9948c705c2cSLuiz Otavio O Souza }
9958c705c2cSLuiz Otavio O Souza 
9964063f925SOleksandr Tymoshenko static device_method_t bcm_gpio_methods[] = {
9974063f925SOleksandr Tymoshenko 	/* Device interface */
9984063f925SOleksandr Tymoshenko 	DEVMETHOD(device_probe,		bcm_gpio_probe),
9994063f925SOleksandr Tymoshenko 	DEVMETHOD(device_attach,	bcm_gpio_attach),
10004063f925SOleksandr Tymoshenko 	DEVMETHOD(device_detach,	bcm_gpio_detach),
10014063f925SOleksandr Tymoshenko 
10024063f925SOleksandr Tymoshenko 	/* GPIO protocol */
10037836352bSLuiz Otavio O Souza 	DEVMETHOD(gpio_get_bus,		bcm_gpio_get_bus),
10044063f925SOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_max,		bcm_gpio_pin_max),
10054063f925SOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_getname,	bcm_gpio_pin_getname),
10064063f925SOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_getflags,	bcm_gpio_pin_getflags),
10074063f925SOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_getcaps,	bcm_gpio_pin_getcaps),
10084063f925SOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_setflags,	bcm_gpio_pin_setflags),
10094063f925SOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_get,		bcm_gpio_pin_get),
10104063f925SOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_set,		bcm_gpio_pin_set),
10114063f925SOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_toggle,	bcm_gpio_pin_toggle),
10124063f925SOleksandr Tymoshenko 
101312471cecSLuiz Otavio O Souza 	/* Bus interface */
101412471cecSLuiz Otavio O Souza 	DEVMETHOD(bus_activate_resource,	bcm_gpio_activate_resource),
101512471cecSLuiz Otavio O Souza 	DEVMETHOD(bus_deactivate_resource,	bcm_gpio_deactivate_resource),
101612471cecSLuiz Otavio O Souza 	DEVMETHOD(bus_config_intr,	bcm_gpio_config_intr),
101712471cecSLuiz Otavio O Souza 	DEVMETHOD(bus_setup_intr,	bcm_gpio_setup_intr),
101812471cecSLuiz Otavio O Souza 	DEVMETHOD(bus_teardown_intr,	bcm_gpio_teardown_intr),
101912471cecSLuiz Otavio O Souza 
10208c705c2cSLuiz Otavio O Souza 	/* ofw_bus interface */
10218c705c2cSLuiz Otavio O Souza 	DEVMETHOD(ofw_bus_get_node,	bcm_gpio_get_node),
10228c705c2cSLuiz Otavio O Souza 
10234063f925SOleksandr Tymoshenko 	DEVMETHOD_END
10244063f925SOleksandr Tymoshenko };
10254063f925SOleksandr Tymoshenko 
10264063f925SOleksandr Tymoshenko static devclass_t bcm_gpio_devclass;
10274063f925SOleksandr Tymoshenko 
10284063f925SOleksandr Tymoshenko static driver_t bcm_gpio_driver = {
10294063f925SOleksandr Tymoshenko 	"gpio",
10304063f925SOleksandr Tymoshenko 	bcm_gpio_methods,
10314063f925SOleksandr Tymoshenko 	sizeof(struct bcm_gpio_softc),
10324063f925SOleksandr Tymoshenko };
10334063f925SOleksandr Tymoshenko 
10344063f925SOleksandr Tymoshenko DRIVER_MODULE(bcm_gpio, simplebus, bcm_gpio_driver, bcm_gpio_devclass, 0, 0);
1035