xref: /freebsd/sys/arm/broadcom/bcm2835/bcm2835_gpio.c (revision 12471cec7c87ab3c7debbbb45aa72afc99bff8e1)
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 #include <sys/gpio.h>
35*12471cecSLuiz 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
61*12471cecSLuiz 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;
86*12471cecSLuiz 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];
91*12471cecSLuiz Otavio O Souza 	struct intr_event *	sc_events[BCM_GPIO_PINS];
9290576f54SOleksandr Tymoshenko 	struct bcm_gpio_sysctl	sc_sysctl[BCM_GPIO_PINS];
93*12471cecSLuiz Otavio O Souza 	enum intr_trigger	sc_irq_trigger[BCM_GPIO_PINS];
94*12471cecSLuiz 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 
103*12471cecSLuiz Otavio O Souza #define	BCM_GPIO_LOCK(_sc)	mtx_lock_spin(&(_sc)->sc_mtx)
104*12471cecSLuiz Otavio O Souza #define	BCM_GPIO_UNLOCK(_sc)	mtx_unlock_spin(&(_sc)->sc_mtx)
105*12471cecSLuiz 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)		\
107*12471cecSLuiz Otavio O Souza     bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, _off, _val)
1084063f925SOleksandr Tymoshenko #define	BCM_GPIO_READ(_sc, _off)		\
109*12471cecSLuiz Otavio O Souza     bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, _off)
110*12471cecSLuiz Otavio O Souza #define	BCM_GPIO_CLEAR_BITS(_sc, _off, _bits)	\
111*12471cecSLuiz Otavio O Souza     BCM_GPIO_WRITE(_sc, _off, BCM_GPIO_READ(_sc, _off) & ~(_bits))
112*12471cecSLuiz Otavio O Souza #define	BCM_GPIO_SET_BITS(_sc, _off, _bits)	\
113*12471cecSLuiz Otavio O Souza     BCM_GPIO_WRITE(_sc, _off, BCM_GPIO_READ(_sc, _off) | _bits)
114*12471cecSLuiz Otavio O Souza #define	BCM_GPIO_BANK(a)	(a / BCM_GPIO_PINS_PER_BANK)
115*12471cecSLuiz Otavio O Souza #define	BCM_GPIO_MASK(a)	(1U << (a % BCM_GPIO_PINS_PER_BANK))
116*12471cecSLuiz Otavio O Souza 
117*12471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPFSEL(_bank)	(0x00 + _bank * 4)	/* Function Select */
118*12471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPSET(_bank)	(0x1c + _bank * 4)	/* Pin Out Set */
119*12471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPCLR(_bank)	(0x28 + _bank * 4)	/* Pin Out Clear */
120*12471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPLEV(_bank)	(0x34 + _bank * 4)	/* Pin Level */
121*12471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPEDS(_bank)	(0x40 + _bank * 4)	/* Event Status */
122*12471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPREN(_bank)	(0x4c + _bank * 4)	/* Rising Edge irq */
123*12471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPFEN(_bank)	(0x58 + _bank * 4)	/* Falling Edge irq */
124*12471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPHEN(_bank)	(0x64 + _bank * 4)	/* High Level irq */
125*12471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPLEN(_bank)	(0x70 + _bank * 4)	/* Low Level irq */
126*12471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPAREN(_bank)	(0x7c + _bank * 4)	/* Async Rising Edge */
127*12471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPAFEN(_bank)	(0x88 + _bank * 4)	/* Async Falling Egde */
128*12471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPPUD(_bank)	(0x94)			/* Pin Pull up/down */
129*12471cecSLuiz Otavio O Souza #define	BCM_GPIO_GPPUDCLK(_bank) (0x98 + _bank * 4)	/* Pin Pull up clock */
130*12471cecSLuiz Otavio O Souza 
131*12471cecSLuiz 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 {
2544063f925SOleksandr Tymoshenko 	uint32_t bank, offset;
2554063f925SOleksandr Tymoshenko 
25690576f54SOleksandr Tymoshenko 	/* Must be called with lock held. */
25790576f54SOleksandr Tymoshenko 	BCM_GPIO_LOCK_ASSERT(sc);
25890576f54SOleksandr Tymoshenko 
2594063f925SOleksandr Tymoshenko 	bank = pin / 32;
2604063f925SOleksandr Tymoshenko 	offset = pin - 32 * bank;
2614063f925SOleksandr Tymoshenko 
2624063f925SOleksandr Tymoshenko 	BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), state);
2634063f925SOleksandr Tymoshenko 	BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), (1 << offset));
2644063f925SOleksandr Tymoshenko 	BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), 0);
2654063f925SOleksandr Tymoshenko 	BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), 0);
2664063f925SOleksandr Tymoshenko }
2674063f925SOleksandr Tymoshenko 
26844d06d8dSLuiz Otavio O Souza void
26944d06d8dSLuiz Otavio O Souza bcm_gpio_set_alternate(device_t dev, uint32_t pin, uint32_t nfunc)
27044d06d8dSLuiz Otavio O Souza {
27144d06d8dSLuiz Otavio O Souza 	struct bcm_gpio_softc *sc;
27244d06d8dSLuiz Otavio O Souza 	int i;
27344d06d8dSLuiz Otavio O Souza 
27444d06d8dSLuiz Otavio O Souza 	sc = device_get_softc(dev);
27544d06d8dSLuiz Otavio O Souza 	BCM_GPIO_LOCK(sc);
27644d06d8dSLuiz Otavio O Souza 
27744d06d8dSLuiz Otavio O Souza 	/* Disable pull-up or pull-down on pin. */
27844d06d8dSLuiz Otavio O Souza 	bcm_gpio_set_pud(sc, pin, BCM_GPIO_NONE);
27944d06d8dSLuiz Otavio O Souza 
28044d06d8dSLuiz Otavio O Souza 	/* And now set the pin function. */
28144d06d8dSLuiz Otavio O Souza 	bcm_gpio_set_function(sc, pin, nfunc);
28244d06d8dSLuiz Otavio O Souza 
28344d06d8dSLuiz Otavio O Souza 	/* Update the pin flags. */
28444d06d8dSLuiz Otavio O Souza 	for (i = 0; i < sc->sc_gpio_npins; i++) {
28544d06d8dSLuiz Otavio O Souza 		if (sc->sc_gpio_pins[i].gp_pin == pin)
28644d06d8dSLuiz Otavio O Souza 			break;
28744d06d8dSLuiz Otavio O Souza 	}
28844d06d8dSLuiz Otavio O Souza 	if (i < sc->sc_gpio_npins)
28944d06d8dSLuiz Otavio O Souza 		sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(nfunc);
29044d06d8dSLuiz Otavio O Souza 
29144d06d8dSLuiz Otavio O Souza         BCM_GPIO_UNLOCK(sc);
29244d06d8dSLuiz Otavio O Souza }
29344d06d8dSLuiz Otavio O Souza 
2944063f925SOleksandr Tymoshenko static void
2954063f925SOleksandr Tymoshenko bcm_gpio_pin_configure(struct bcm_gpio_softc *sc, struct gpio_pin *pin,
2964063f925SOleksandr Tymoshenko     unsigned int flags)
2974063f925SOleksandr Tymoshenko {
2984063f925SOleksandr Tymoshenko 
29990576f54SOleksandr Tymoshenko 	BCM_GPIO_LOCK(sc);
30090576f54SOleksandr Tymoshenko 
3014063f925SOleksandr Tymoshenko 	/*
3024063f925SOleksandr Tymoshenko 	 * Manage input/output.
3034063f925SOleksandr Tymoshenko 	 */
3044063f925SOleksandr Tymoshenko 	if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) {
3054063f925SOleksandr Tymoshenko 		pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT);
3064063f925SOleksandr Tymoshenko 		if (flags & GPIO_PIN_OUTPUT) {
3074063f925SOleksandr Tymoshenko 			pin->gp_flags |= GPIO_PIN_OUTPUT;
3084063f925SOleksandr Tymoshenko 			bcm_gpio_set_function(sc, pin->gp_pin,
3094063f925SOleksandr Tymoshenko 			    BCM_GPIO_OUTPUT);
3104063f925SOleksandr Tymoshenko 		} else {
3114063f925SOleksandr Tymoshenko 			pin->gp_flags |= GPIO_PIN_INPUT;
3124063f925SOleksandr Tymoshenko 			bcm_gpio_set_function(sc, pin->gp_pin,
3134063f925SOleksandr Tymoshenko 			    BCM_GPIO_INPUT);
3144063f925SOleksandr Tymoshenko 		}
3154063f925SOleksandr Tymoshenko 	}
3164063f925SOleksandr Tymoshenko 
3174063f925SOleksandr Tymoshenko 	/* Manage Pull-up/pull-down. */
3184063f925SOleksandr Tymoshenko 	pin->gp_flags &= ~(GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN);
3194063f925SOleksandr Tymoshenko 	if (flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) {
3204063f925SOleksandr Tymoshenko 		if (flags & GPIO_PIN_PULLUP) {
3214063f925SOleksandr Tymoshenko 			pin->gp_flags |= GPIO_PIN_PULLUP;
3224063f925SOleksandr Tymoshenko 			bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLUP);
3234063f925SOleksandr Tymoshenko 		} else {
3244063f925SOleksandr Tymoshenko 			pin->gp_flags |= GPIO_PIN_PULLDOWN;
3254063f925SOleksandr Tymoshenko 			bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLDOWN);
3264063f925SOleksandr Tymoshenko 		}
3274063f925SOleksandr Tymoshenko 	} else
3284063f925SOleksandr Tymoshenko 		bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_NONE);
32990576f54SOleksandr Tymoshenko 
33090576f54SOleksandr Tymoshenko 	BCM_GPIO_UNLOCK(sc);
3314063f925SOleksandr Tymoshenko }
3324063f925SOleksandr Tymoshenko 
3337836352bSLuiz Otavio O Souza static device_t
3347836352bSLuiz Otavio O Souza bcm_gpio_get_bus(device_t dev)
3357836352bSLuiz Otavio O Souza {
3367836352bSLuiz Otavio O Souza 	struct bcm_gpio_softc *sc;
3377836352bSLuiz Otavio O Souza 
3387836352bSLuiz Otavio O Souza 	sc = device_get_softc(dev);
3397836352bSLuiz Otavio O Souza 
3407836352bSLuiz Otavio O Souza 	return (sc->sc_busdev);
3417836352bSLuiz Otavio O Souza }
3427836352bSLuiz Otavio O Souza 
3434063f925SOleksandr Tymoshenko static int
3444063f925SOleksandr Tymoshenko bcm_gpio_pin_max(device_t dev, int *maxpin)
3454063f925SOleksandr Tymoshenko {
3464063f925SOleksandr Tymoshenko 
3474063f925SOleksandr Tymoshenko 	*maxpin = BCM_GPIO_PINS - 1;
3484063f925SOleksandr Tymoshenko 	return (0);
3494063f925SOleksandr Tymoshenko }
3504063f925SOleksandr Tymoshenko 
3514063f925SOleksandr Tymoshenko static int
3524063f925SOleksandr Tymoshenko bcm_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
3534063f925SOleksandr Tymoshenko {
3544063f925SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc = device_get_softc(dev);
3554063f925SOleksandr Tymoshenko 	int i;
3564063f925SOleksandr Tymoshenko 
3574063f925SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_gpio_npins; i++) {
3584063f925SOleksandr Tymoshenko 		if (sc->sc_gpio_pins[i].gp_pin == pin)
3594063f925SOleksandr Tymoshenko 			break;
3604063f925SOleksandr Tymoshenko 	}
3614063f925SOleksandr Tymoshenko 
3624063f925SOleksandr Tymoshenko 	if (i >= sc->sc_gpio_npins)
3634063f925SOleksandr Tymoshenko 		return (EINVAL);
3644063f925SOleksandr Tymoshenko 
3654063f925SOleksandr Tymoshenko 	BCM_GPIO_LOCK(sc);
3664063f925SOleksandr Tymoshenko 	*caps = sc->sc_gpio_pins[i].gp_caps;
3674063f925SOleksandr Tymoshenko 	BCM_GPIO_UNLOCK(sc);
3684063f925SOleksandr Tymoshenko 
3694063f925SOleksandr Tymoshenko 	return (0);
3704063f925SOleksandr Tymoshenko }
3714063f925SOleksandr Tymoshenko 
3724063f925SOleksandr Tymoshenko static int
3734063f925SOleksandr Tymoshenko bcm_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
3744063f925SOleksandr Tymoshenko {
3754063f925SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc = device_get_softc(dev);
3764063f925SOleksandr Tymoshenko 	int i;
3774063f925SOleksandr Tymoshenko 
3784063f925SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_gpio_npins; i++) {
3794063f925SOleksandr Tymoshenko 		if (sc->sc_gpio_pins[i].gp_pin == pin)
3804063f925SOleksandr Tymoshenko 			break;
3814063f925SOleksandr Tymoshenko 	}
3824063f925SOleksandr Tymoshenko 
3834063f925SOleksandr Tymoshenko 	if (i >= sc->sc_gpio_npins)
3844063f925SOleksandr Tymoshenko 		return (EINVAL);
3854063f925SOleksandr Tymoshenko 
3864063f925SOleksandr Tymoshenko 	BCM_GPIO_LOCK(sc);
3874063f925SOleksandr Tymoshenko 	*flags = sc->sc_gpio_pins[i].gp_flags;
3884063f925SOleksandr Tymoshenko 	BCM_GPIO_UNLOCK(sc);
3894063f925SOleksandr Tymoshenko 
3904063f925SOleksandr Tymoshenko 	return (0);
3914063f925SOleksandr Tymoshenko }
3924063f925SOleksandr Tymoshenko 
3934063f925SOleksandr Tymoshenko static int
3944063f925SOleksandr Tymoshenko bcm_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
3954063f925SOleksandr Tymoshenko {
3964063f925SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc = device_get_softc(dev);
3974063f925SOleksandr Tymoshenko 	int i;
3984063f925SOleksandr Tymoshenko 
3994063f925SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_gpio_npins; i++) {
4004063f925SOleksandr Tymoshenko 		if (sc->sc_gpio_pins[i].gp_pin == pin)
4014063f925SOleksandr Tymoshenko 			break;
4024063f925SOleksandr Tymoshenko 	}
4034063f925SOleksandr Tymoshenko 
4044063f925SOleksandr Tymoshenko 	if (i >= sc->sc_gpio_npins)
4054063f925SOleksandr Tymoshenko 		return (EINVAL);
4064063f925SOleksandr Tymoshenko 
4074063f925SOleksandr Tymoshenko 	BCM_GPIO_LOCK(sc);
4084063f925SOleksandr Tymoshenko 	memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME);
4094063f925SOleksandr Tymoshenko 	BCM_GPIO_UNLOCK(sc);
4104063f925SOleksandr Tymoshenko 
4114063f925SOleksandr Tymoshenko 	return (0);
4124063f925SOleksandr Tymoshenko }
4134063f925SOleksandr Tymoshenko 
4144063f925SOleksandr Tymoshenko static int
4154063f925SOleksandr Tymoshenko bcm_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
4164063f925SOleksandr Tymoshenko {
4174063f925SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc = device_get_softc(dev);
4184063f925SOleksandr Tymoshenko 	int i;
4194063f925SOleksandr Tymoshenko 
4204063f925SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_gpio_npins; i++) {
4214063f925SOleksandr Tymoshenko 		if (sc->sc_gpio_pins[i].gp_pin == pin)
4224063f925SOleksandr Tymoshenko 			break;
4234063f925SOleksandr Tymoshenko 	}
4244063f925SOleksandr Tymoshenko 
4254063f925SOleksandr Tymoshenko 	if (i >= sc->sc_gpio_npins)
4264063f925SOleksandr Tymoshenko 		return (EINVAL);
4274063f925SOleksandr Tymoshenko 
4284063f925SOleksandr Tymoshenko 	/* We never touch on read-only/reserved pins. */
4294063f925SOleksandr Tymoshenko 	if (bcm_gpio_pin_is_ro(sc, pin))
4304063f925SOleksandr Tymoshenko 		return (EINVAL);
4314063f925SOleksandr Tymoshenko 
4324063f925SOleksandr Tymoshenko 	bcm_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags);
4334063f925SOleksandr Tymoshenko 
4344063f925SOleksandr Tymoshenko 	return (0);
4354063f925SOleksandr Tymoshenko }
4364063f925SOleksandr Tymoshenko 
4374063f925SOleksandr Tymoshenko static int
4384063f925SOleksandr Tymoshenko bcm_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
4394063f925SOleksandr Tymoshenko {
4404063f925SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc = device_get_softc(dev);
4414063f925SOleksandr Tymoshenko 	uint32_t bank, offset;
4424063f925SOleksandr Tymoshenko 	int i;
4434063f925SOleksandr Tymoshenko 
4444063f925SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_gpio_npins; i++) {
4454063f925SOleksandr Tymoshenko 		if (sc->sc_gpio_pins[i].gp_pin == pin)
4464063f925SOleksandr Tymoshenko 			break;
4474063f925SOleksandr Tymoshenko 	}
4484063f925SOleksandr Tymoshenko 
4494063f925SOleksandr Tymoshenko 	if (i >= sc->sc_gpio_npins)
4504063f925SOleksandr Tymoshenko 		return (EINVAL);
4514063f925SOleksandr Tymoshenko 
4524063f925SOleksandr Tymoshenko 	/* We never write to read-only/reserved pins. */
4534063f925SOleksandr Tymoshenko 	if (bcm_gpio_pin_is_ro(sc, pin))
4544063f925SOleksandr Tymoshenko 		return (EINVAL);
4554063f925SOleksandr Tymoshenko 
4564063f925SOleksandr Tymoshenko 	bank = pin / 32;
4574063f925SOleksandr Tymoshenko 	offset = pin - 32 * bank;
4584063f925SOleksandr Tymoshenko 
4594063f925SOleksandr Tymoshenko 	BCM_GPIO_LOCK(sc);
4604063f925SOleksandr Tymoshenko 	if (value)
4614063f925SOleksandr Tymoshenko 		BCM_GPIO_WRITE(sc, BCM_GPIO_GPSET(bank), (1 << offset));
4624063f925SOleksandr Tymoshenko 	else
4634063f925SOleksandr Tymoshenko 		BCM_GPIO_WRITE(sc, BCM_GPIO_GPCLR(bank), (1 << offset));
4644063f925SOleksandr Tymoshenko 	BCM_GPIO_UNLOCK(sc);
4654063f925SOleksandr Tymoshenko 
4664063f925SOleksandr Tymoshenko 	return (0);
4674063f925SOleksandr Tymoshenko }
4684063f925SOleksandr Tymoshenko 
4694063f925SOleksandr Tymoshenko static int
4704063f925SOleksandr Tymoshenko bcm_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
4714063f925SOleksandr Tymoshenko {
4724063f925SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc = device_get_softc(dev);
4734063f925SOleksandr Tymoshenko 	uint32_t bank, offset, reg_data;
4744063f925SOleksandr Tymoshenko 	int i;
4754063f925SOleksandr Tymoshenko 
4764063f925SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_gpio_npins; i++) {
4774063f925SOleksandr Tymoshenko 		if (sc->sc_gpio_pins[i].gp_pin == pin)
4784063f925SOleksandr Tymoshenko 			break;
4794063f925SOleksandr Tymoshenko 	}
4804063f925SOleksandr Tymoshenko 
4814063f925SOleksandr Tymoshenko 	if (i >= sc->sc_gpio_npins)
4824063f925SOleksandr Tymoshenko 		return (EINVAL);
4834063f925SOleksandr Tymoshenko 
4844063f925SOleksandr Tymoshenko 	bank = pin / 32;
4854063f925SOleksandr Tymoshenko 	offset = pin - 32 * bank;
4864063f925SOleksandr Tymoshenko 
4874063f925SOleksandr Tymoshenko 	BCM_GPIO_LOCK(sc);
4884063f925SOleksandr Tymoshenko 	reg_data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank));
4894063f925SOleksandr Tymoshenko 	BCM_GPIO_UNLOCK(sc);
4904063f925SOleksandr Tymoshenko 	*val = (reg_data & (1 << offset)) ? 1 : 0;
4914063f925SOleksandr Tymoshenko 
4924063f925SOleksandr Tymoshenko 	return (0);
4934063f925SOleksandr Tymoshenko }
4944063f925SOleksandr Tymoshenko 
4954063f925SOleksandr Tymoshenko static int
4964063f925SOleksandr Tymoshenko bcm_gpio_pin_toggle(device_t dev, uint32_t pin)
4974063f925SOleksandr Tymoshenko {
4984063f925SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc = device_get_softc(dev);
4994063f925SOleksandr Tymoshenko 	uint32_t bank, data, offset;
5004063f925SOleksandr Tymoshenko 	int i;
5014063f925SOleksandr Tymoshenko 
5024063f925SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_gpio_npins; i++) {
5034063f925SOleksandr Tymoshenko 		if (sc->sc_gpio_pins[i].gp_pin == pin)
5044063f925SOleksandr Tymoshenko 			break;
5054063f925SOleksandr Tymoshenko 	}
5064063f925SOleksandr Tymoshenko 
5074063f925SOleksandr Tymoshenko 	if (i >= sc->sc_gpio_npins)
5084063f925SOleksandr Tymoshenko 		return (EINVAL);
5094063f925SOleksandr Tymoshenko 
5104063f925SOleksandr Tymoshenko 	/* We never write to read-only/reserved pins. */
5114063f925SOleksandr Tymoshenko 	if (bcm_gpio_pin_is_ro(sc, pin))
5124063f925SOleksandr Tymoshenko 		return (EINVAL);
5134063f925SOleksandr Tymoshenko 
5144063f925SOleksandr Tymoshenko 	bank = pin / 32;
5154063f925SOleksandr Tymoshenko 	offset = pin - 32 * bank;
5164063f925SOleksandr Tymoshenko 
5174063f925SOleksandr Tymoshenko 	BCM_GPIO_LOCK(sc);
5184063f925SOleksandr Tymoshenko 	data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank));
5194063f925SOleksandr Tymoshenko 	if (data & (1 << offset))
5204063f925SOleksandr Tymoshenko 		BCM_GPIO_WRITE(sc, BCM_GPIO_GPCLR(bank), (1 << offset));
5214063f925SOleksandr Tymoshenko 	else
5224063f925SOleksandr Tymoshenko 		BCM_GPIO_WRITE(sc, BCM_GPIO_GPSET(bank), (1 << offset));
5234063f925SOleksandr Tymoshenko 	BCM_GPIO_UNLOCK(sc);
5244063f925SOleksandr Tymoshenko 
5254063f925SOleksandr Tymoshenko 	return (0);
5264063f925SOleksandr Tymoshenko }
5274063f925SOleksandr Tymoshenko 
5284063f925SOleksandr Tymoshenko static int
52990576f54SOleksandr Tymoshenko bcm_gpio_func_proc(SYSCTL_HANDLER_ARGS)
53090576f54SOleksandr Tymoshenko {
53190576f54SOleksandr Tymoshenko 	char buf[16];
53290576f54SOleksandr Tymoshenko 	struct bcm_gpio_softc *sc;
53390576f54SOleksandr Tymoshenko 	struct bcm_gpio_sysctl *sc_sysctl;
53490576f54SOleksandr Tymoshenko 	uint32_t nfunc;
53544d06d8dSLuiz Otavio O Souza 	int error;
53690576f54SOleksandr Tymoshenko 
53790576f54SOleksandr Tymoshenko 	sc_sysctl = arg1;
53890576f54SOleksandr Tymoshenko 	sc = sc_sysctl->sc;
53990576f54SOleksandr Tymoshenko 
54090576f54SOleksandr Tymoshenko 	/* Get the current pin function. */
54190576f54SOleksandr Tymoshenko 	nfunc = bcm_gpio_get_function(sc, sc_sysctl->pin);
54290576f54SOleksandr Tymoshenko 	bcm_gpio_func_str(nfunc, buf, sizeof(buf));
54390576f54SOleksandr Tymoshenko 
54490576f54SOleksandr Tymoshenko 	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
54590576f54SOleksandr Tymoshenko 	if (error != 0 || req->newptr == NULL)
54690576f54SOleksandr Tymoshenko 		return (error);
54799a20111SLuiz Otavio O Souza 	/* Ignore changes on read-only pins. */
54899a20111SLuiz Otavio O Souza 	if (bcm_gpio_pin_is_ro(sc, sc_sysctl->pin))
54999a20111SLuiz Otavio O Souza 		return (0);
55090576f54SOleksandr Tymoshenko 	/* Parse the user supplied string and check for a valid pin function. */
55190576f54SOleksandr Tymoshenko 	if (bcm_gpio_str_func(buf, &nfunc) != 0)
55290576f54SOleksandr Tymoshenko 		return (EINVAL);
55390576f54SOleksandr Tymoshenko 
55444d06d8dSLuiz Otavio O Souza 	/* Update the pin alternate function. */
55544d06d8dSLuiz Otavio O Souza 	bcm_gpio_set_alternate(sc->sc_dev, sc_sysctl->pin, nfunc);
55690576f54SOleksandr Tymoshenko 
55790576f54SOleksandr Tymoshenko 	return (0);
55890576f54SOleksandr Tymoshenko }
55990576f54SOleksandr Tymoshenko 
56090576f54SOleksandr Tymoshenko static void
56190576f54SOleksandr Tymoshenko bcm_gpio_sysctl_init(struct bcm_gpio_softc *sc)
56290576f54SOleksandr Tymoshenko {
56390576f54SOleksandr Tymoshenko 	char pinbuf[3];
56490576f54SOleksandr Tymoshenko 	struct bcm_gpio_sysctl *sc_sysctl;
56590576f54SOleksandr Tymoshenko 	struct sysctl_ctx_list *ctx;
56690576f54SOleksandr Tymoshenko 	struct sysctl_oid *tree_node, *pin_node, *pinN_node;
56790576f54SOleksandr Tymoshenko 	struct sysctl_oid_list *tree, *pin_tree, *pinN_tree;
56890576f54SOleksandr Tymoshenko 	int i;
56990576f54SOleksandr Tymoshenko 
57090576f54SOleksandr Tymoshenko 	/*
57190576f54SOleksandr Tymoshenko 	 * Add per-pin sysctl tree/handlers.
57290576f54SOleksandr Tymoshenko 	 */
57390576f54SOleksandr Tymoshenko 	ctx = device_get_sysctl_ctx(sc->sc_dev);
57490576f54SOleksandr Tymoshenko  	tree_node = device_get_sysctl_tree(sc->sc_dev);
57590576f54SOleksandr Tymoshenko  	tree = SYSCTL_CHILDREN(tree_node);
57690576f54SOleksandr Tymoshenko 	pin_node = SYSCTL_ADD_NODE(ctx, tree, OID_AUTO, "pin",
5771fbabb48SLuiz Otavio O Souza 	    CTLFLAG_RD, NULL, "GPIO Pins");
57890576f54SOleksandr Tymoshenko 	pin_tree = SYSCTL_CHILDREN(pin_node);
57990576f54SOleksandr Tymoshenko 
58090576f54SOleksandr Tymoshenko 	for (i = 0; i < sc->sc_gpio_npins; i++) {
58190576f54SOleksandr Tymoshenko 
58290576f54SOleksandr Tymoshenko 		snprintf(pinbuf, sizeof(pinbuf), "%d", i);
58390576f54SOleksandr Tymoshenko 		pinN_node = SYSCTL_ADD_NODE(ctx, pin_tree, OID_AUTO, pinbuf,
58490576f54SOleksandr Tymoshenko 		    CTLFLAG_RD, NULL, "GPIO Pin");
58590576f54SOleksandr Tymoshenko 		pinN_tree = SYSCTL_CHILDREN(pinN_node);
58690576f54SOleksandr Tymoshenko 
58790576f54SOleksandr Tymoshenko 		sc->sc_sysctl[i].sc = sc;
58890576f54SOleksandr Tymoshenko 		sc_sysctl = &sc->sc_sysctl[i];
58990576f54SOleksandr Tymoshenko 		sc_sysctl->sc = sc;
59090576f54SOleksandr Tymoshenko 		sc_sysctl->pin = sc->sc_gpio_pins[i].gp_pin;
59190576f54SOleksandr Tymoshenko 		SYSCTL_ADD_PROC(ctx, pinN_tree, OID_AUTO, "function",
59290576f54SOleksandr Tymoshenko 		    CTLFLAG_RW | CTLTYPE_STRING, sc_sysctl,
59390576f54SOleksandr Tymoshenko 		    sizeof(struct bcm_gpio_sysctl), bcm_gpio_func_proc,
59490576f54SOleksandr Tymoshenko 		    "A", "Pin Function");
59590576f54SOleksandr Tymoshenko 	}
59690576f54SOleksandr Tymoshenko }
59790576f54SOleksandr Tymoshenko 
59890576f54SOleksandr Tymoshenko static int
59999a20111SLuiz Otavio O Souza bcm_gpio_get_ro_pins(struct bcm_gpio_softc *sc, phandle_t node,
60099a20111SLuiz Otavio O Souza 	const char *propname, const char *label)
60199a20111SLuiz Otavio O Souza {
60299a20111SLuiz Otavio O Souza 	int i, need_comma, npins, range_start, range_stop;
60399a20111SLuiz Otavio O Souza 	pcell_t *pins;
60499a20111SLuiz Otavio O Souza 
60599a20111SLuiz Otavio O Souza 	/* Get the property data. */
60699a20111SLuiz Otavio O Souza 	npins = OF_getencprop_alloc(node, propname, sizeof(*pins),
60799a20111SLuiz Otavio O Souza 	    (void **)&pins);
60899a20111SLuiz Otavio O Souza 	if (npins < 0)
60999a20111SLuiz Otavio O Souza 		return (-1);
61099a20111SLuiz Otavio O Souza 	if (npins == 0) {
61199a20111SLuiz Otavio O Souza 		free(pins, M_OFWPROP);
61299a20111SLuiz Otavio O Souza 		return (0);
61399a20111SLuiz Otavio O Souza 	}
61499a20111SLuiz Otavio O Souza 	for (i = 0; i < npins; i++)
61599a20111SLuiz Otavio O Souza 		sc->sc_ro_pins[i + sc->sc_ro_npins] = pins[i];
61699a20111SLuiz Otavio O Souza 	sc->sc_ro_npins += npins;
61799a20111SLuiz Otavio O Souza 	need_comma = 0;
61899a20111SLuiz Otavio O Souza 	device_printf(sc->sc_dev, "%s pins: ", label);
61999a20111SLuiz Otavio O Souza 	range_start = range_stop = pins[0];
62099a20111SLuiz Otavio O Souza 	for (i = 1; i < npins; i++) {
62199a20111SLuiz Otavio O Souza 		if (pins[i] != range_stop + 1) {
62299a20111SLuiz Otavio O Souza 			if (need_comma)
62399a20111SLuiz Otavio O Souza 				printf(",");
62499a20111SLuiz Otavio O Souza 			if (range_start != range_stop)
62599a20111SLuiz Otavio O Souza 				printf("%d-%d", range_start, range_stop);
62699a20111SLuiz Otavio O Souza 			else
62799a20111SLuiz Otavio O Souza 				printf("%d", range_start);
62899a20111SLuiz Otavio O Souza 			range_start = range_stop = pins[i];
62999a20111SLuiz Otavio O Souza 			need_comma = 1;
63099a20111SLuiz Otavio O Souza 		} else
63199a20111SLuiz Otavio O Souza 			range_stop++;
63299a20111SLuiz Otavio O Souza 	}
63399a20111SLuiz Otavio O Souza 	if (need_comma)
63499a20111SLuiz Otavio O Souza 		printf(",");
63599a20111SLuiz Otavio O Souza 	if (range_start != range_stop)
63699a20111SLuiz Otavio O Souza 		printf("%d-%d.\n", range_start, range_stop);
63799a20111SLuiz Otavio O Souza 	else
63899a20111SLuiz Otavio O Souza 		printf("%d.\n", range_start);
63999a20111SLuiz Otavio O Souza 	free(pins, M_OFWPROP);
64099a20111SLuiz Otavio O Souza 
64199a20111SLuiz Otavio O Souza 	return (0);
64299a20111SLuiz Otavio O Souza }
64399a20111SLuiz Otavio O Souza 
64499a20111SLuiz Otavio O Souza static int
6454063f925SOleksandr Tymoshenko bcm_gpio_get_reserved_pins(struct bcm_gpio_softc *sc)
6464063f925SOleksandr Tymoshenko {
64799a20111SLuiz Otavio O Souza 	char *name;
6484063f925SOleksandr Tymoshenko 	phandle_t gpio, node, reserved;
64999a20111SLuiz Otavio O Souza 	ssize_t len;
6504063f925SOleksandr Tymoshenko 
6514063f925SOleksandr Tymoshenko 	/* Get read-only pins. */
6524063f925SOleksandr Tymoshenko 	gpio = ofw_bus_get_node(sc->sc_dev);
65399a20111SLuiz Otavio O Souza 	if (bcm_gpio_get_ro_pins(sc, gpio, "broadcom,read-only",
65499a20111SLuiz Otavio O Souza 	    "read-only") != 0)
65599a20111SLuiz Otavio O Souza 		return (-1);
65699a20111SLuiz Otavio O Souza 	/* Traverse the GPIO subnodes to find the reserved pins node. */
6574063f925SOleksandr Tymoshenko 	reserved = 0;
65899a20111SLuiz Otavio O Souza 	node = OF_child(gpio);
6594063f925SOleksandr Tymoshenko 	while ((node != 0) && (reserved == 0)) {
66099a20111SLuiz Otavio O Souza 		len = OF_getprop_alloc(node, "name", 1, (void **)&name);
66199a20111SLuiz Otavio O Souza 		if (len == -1)
66299a20111SLuiz Otavio O Souza 			return (-1);
6634063f925SOleksandr Tymoshenko 		if (strcmp(name, "reserved") == 0)
6644063f925SOleksandr Tymoshenko 			reserved = node;
66599a20111SLuiz Otavio O Souza 		free(name, M_OFWPROP);
6664063f925SOleksandr Tymoshenko 		node = OF_peer(node);
6674063f925SOleksandr Tymoshenko 	}
6684063f925SOleksandr Tymoshenko 	if (reserved == 0)
6694063f925SOleksandr Tymoshenko 		return (-1);
6704063f925SOleksandr Tymoshenko 	/* Get the reserved pins. */
67199a20111SLuiz Otavio O Souza 	if (bcm_gpio_get_ro_pins(sc, reserved, "broadcom,pins",
67299a20111SLuiz Otavio O Souza 	    "reserved") != 0)
6734063f925SOleksandr Tymoshenko 		return (-1);
6744063f925SOleksandr Tymoshenko 
6754063f925SOleksandr Tymoshenko 	return (0);
6764063f925SOleksandr Tymoshenko }
6774063f925SOleksandr Tymoshenko 
6784063f925SOleksandr Tymoshenko static int
679*12471cecSLuiz Otavio O Souza bcm_gpio_intr(void *arg)
680*12471cecSLuiz Otavio O Souza {
681*12471cecSLuiz Otavio O Souza 	int bank_last, irq;
682*12471cecSLuiz Otavio O Souza 	struct bcm_gpio_softc *sc;
683*12471cecSLuiz Otavio O Souza 	struct intr_event *event;
684*12471cecSLuiz Otavio O Souza 	uint32_t bank, mask, reg;
685*12471cecSLuiz Otavio O Souza 
686*12471cecSLuiz Otavio O Souza 	sc = (struct bcm_gpio_softc *)arg;
687*12471cecSLuiz Otavio O Souza 	reg = 0;
688*12471cecSLuiz Otavio O Souza 	bank_last = -1;
689*12471cecSLuiz Otavio O Souza 	for (irq = 0; irq < BCM_GPIO_PINS; irq++) {
690*12471cecSLuiz Otavio O Souza 		bank = BCM_GPIO_BANK(irq);
691*12471cecSLuiz Otavio O Souza 		mask = BCM_GPIO_MASK(irq);
692*12471cecSLuiz Otavio O Souza 		if (bank != bank_last) {
693*12471cecSLuiz Otavio O Souza 			reg = BCM_GPIO_READ(sc, BCM_GPIO_GPEDS(bank));
694*12471cecSLuiz Otavio O Souza 			bank_last = bank;
695*12471cecSLuiz Otavio O Souza 		}
696*12471cecSLuiz Otavio O Souza 		if (reg & mask) {
697*12471cecSLuiz Otavio O Souza 			event = sc->sc_events[irq];
698*12471cecSLuiz Otavio O Souza 			if (event != NULL && !TAILQ_EMPTY(&event->ie_handlers))
699*12471cecSLuiz Otavio O Souza 				intr_event_handle(event, NULL);
700*12471cecSLuiz Otavio O Souza 			else {
701*12471cecSLuiz Otavio O Souza 				device_printf(sc->sc_dev, "Stray IRQ %d\n",
702*12471cecSLuiz Otavio O Souza 				    irq);
703*12471cecSLuiz Otavio O Souza 			}
704*12471cecSLuiz Otavio O Souza 			/* Clear the Status bit by writing '1' to it. */
705*12471cecSLuiz Otavio O Souza 			BCM_GPIO_WRITE(sc, BCM_GPIO_GPEDS(bank), mask);
706*12471cecSLuiz Otavio O Souza 		}
707*12471cecSLuiz Otavio O Souza 	}
708*12471cecSLuiz Otavio O Souza 
709*12471cecSLuiz Otavio O Souza 	return (FILTER_HANDLED);
710*12471cecSLuiz Otavio O Souza }
711*12471cecSLuiz Otavio O Souza 
712*12471cecSLuiz Otavio O Souza static int
7134063f925SOleksandr Tymoshenko bcm_gpio_probe(device_t dev)
7144063f925SOleksandr Tymoshenko {
715add35ed5SIan Lepore 
716add35ed5SIan Lepore 	if (!ofw_bus_status_okay(dev))
717add35ed5SIan Lepore 		return (ENXIO);
718add35ed5SIan Lepore 
7194063f925SOleksandr Tymoshenko 	if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-gpio"))
7204063f925SOleksandr Tymoshenko 		return (ENXIO);
7214063f925SOleksandr Tymoshenko 
7224063f925SOleksandr Tymoshenko 	device_set_desc(dev, "BCM2708/2835 GPIO controller");
7234063f925SOleksandr Tymoshenko 	return (BUS_PROBE_DEFAULT);
7244063f925SOleksandr Tymoshenko }
7254063f925SOleksandr Tymoshenko 
7264063f925SOleksandr Tymoshenko static int
727*12471cecSLuiz Otavio O Souza bcm_gpio_intr_attach(device_t dev)
728*12471cecSLuiz Otavio O Souza {
729*12471cecSLuiz Otavio O Souza 	struct bcm_gpio_softc *sc;
730*12471cecSLuiz Otavio O Souza 	int i;
731*12471cecSLuiz Otavio O Souza 
732*12471cecSLuiz Otavio O Souza 	sc = device_get_softc(dev);
733*12471cecSLuiz Otavio O Souza 	for (i = 0; i < BCM_GPIO_IRQS; i++) {
734*12471cecSLuiz Otavio O Souza 		if (bus_setup_intr(dev, sc->sc_res[i + 1],
735*12471cecSLuiz Otavio O Souza 		    INTR_TYPE_MISC | INTR_MPSAFE, bcm_gpio_intr,
736*12471cecSLuiz Otavio O Souza 		    NULL, sc, &sc->sc_intrhand[i]) != 0) {
737*12471cecSLuiz Otavio O Souza 			return (-1);
738*12471cecSLuiz Otavio O Souza 		}
739*12471cecSLuiz Otavio O Souza 	}
740*12471cecSLuiz Otavio O Souza 
741*12471cecSLuiz Otavio O Souza 	return (0);
742*12471cecSLuiz Otavio O Souza }
743*12471cecSLuiz Otavio O Souza 
744*12471cecSLuiz Otavio O Souza static void
745*12471cecSLuiz Otavio O Souza bcm_gpio_intr_detach(device_t dev)
746*12471cecSLuiz Otavio O Souza {
747*12471cecSLuiz Otavio O Souza 	struct bcm_gpio_softc *sc;
748*12471cecSLuiz Otavio O Souza 	int i;
749*12471cecSLuiz Otavio O Souza 
750*12471cecSLuiz Otavio O Souza 	sc = device_get_softc(dev);
751*12471cecSLuiz Otavio O Souza 	for (i = 0; i < BCM_GPIO_IRQS; i++) {
752*12471cecSLuiz Otavio O Souza 		if (sc->sc_intrhand[i]) {
753*12471cecSLuiz Otavio O Souza 			bus_teardown_intr(dev, sc->sc_res[i + 1],
754*12471cecSLuiz Otavio O Souza 			    sc->sc_intrhand[i]);
755*12471cecSLuiz Otavio O Souza 		}
756*12471cecSLuiz Otavio O Souza 	}
757*12471cecSLuiz Otavio O Souza }
758*12471cecSLuiz Otavio O Souza 
759*12471cecSLuiz Otavio O Souza static int
7604063f925SOleksandr Tymoshenko bcm_gpio_attach(device_t dev)
7614063f925SOleksandr Tymoshenko {
762c2136589SLuiz Otavio O Souza 	int i, j;
7634063f925SOleksandr Tymoshenko 	phandle_t gpio;
764c2136589SLuiz Otavio O Souza 	struct bcm_gpio_softc *sc;
765c2136589SLuiz Otavio O Souza 	uint32_t func;
7664063f925SOleksandr Tymoshenko 
767*12471cecSLuiz Otavio O Souza 	if (bcm_gpio_sc != NULL)
768*12471cecSLuiz Otavio O Souza 		return (ENXIO);
769*12471cecSLuiz Otavio O Souza 
770*12471cecSLuiz Otavio O Souza 	bcm_gpio_sc = sc = device_get_softc(dev);
7714063f925SOleksandr Tymoshenko  	sc->sc_dev = dev;
772*12471cecSLuiz Otavio O Souza 	mtx_init(&sc->sc_mtx, "bcm gpio", "gpio", MTX_SPIN);
773c2136589SLuiz Otavio O Souza 	if (bus_alloc_resources(dev, bcm_gpio_res_spec, sc->sc_res) != 0) {
774c2136589SLuiz Otavio O Souza 		device_printf(dev, "cannot allocate resources\n");
775c2136589SLuiz Otavio O Souza 		goto fail;
7764063f925SOleksandr Tymoshenko 	}
777c2136589SLuiz Otavio O Souza 	sc->sc_bst = rman_get_bustag(sc->sc_res[0]);
778c2136589SLuiz Otavio O Souza 	sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]);
779*12471cecSLuiz Otavio O Souza 	/* Setup the GPIO interrupt handler. */
780*12471cecSLuiz Otavio O Souza 	if (bcm_gpio_intr_attach(dev)) {
781*12471cecSLuiz Otavio O Souza 		device_printf(dev, "unable to setup the gpio irq handler\n");
782*12471cecSLuiz Otavio O Souza 		goto fail;
783*12471cecSLuiz Otavio O Souza 	}
7844063f925SOleksandr Tymoshenko 	/* Find our node. */
7854063f925SOleksandr Tymoshenko 	gpio = ofw_bus_get_node(sc->sc_dev);
7864063f925SOleksandr Tymoshenko 	if (!OF_hasprop(gpio, "gpio-controller"))
7874063f925SOleksandr Tymoshenko 		/* Node is not a GPIO controller. */
7884063f925SOleksandr Tymoshenko 		goto fail;
7894063f925SOleksandr Tymoshenko 	/*
7904063f925SOleksandr Tymoshenko 	 * Find the read-only pins.  These are pins we never touch or bad
7914063f925SOleksandr Tymoshenko 	 * things could happen.
7924063f925SOleksandr Tymoshenko 	 */
7934063f925SOleksandr Tymoshenko 	if (bcm_gpio_get_reserved_pins(sc) == -1)
7944063f925SOleksandr Tymoshenko 		goto fail;
7954063f925SOleksandr Tymoshenko 	/* Initialize the software controlled pins. */
7968d900240SLuiz Otavio O Souza 	for (i = 0, j = 0; j < BCM_GPIO_PINS; j++) {
7974063f925SOleksandr Tymoshenko 		snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
7984063f925SOleksandr Tymoshenko 		    "pin %d", j);
79990576f54SOleksandr Tymoshenko 		func = bcm_gpio_get_function(sc, j);
8004063f925SOleksandr Tymoshenko 		sc->sc_gpio_pins[i].gp_pin = j;
8014063f925SOleksandr Tymoshenko 		sc->sc_gpio_pins[i].gp_caps = BCM_GPIO_DEFAULT_CAPS;
80290576f54SOleksandr Tymoshenko 		sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(func);
803*12471cecSLuiz Otavio O Souza 		/* The default is active-low interrupts. */
804*12471cecSLuiz Otavio O Souza 		sc->sc_irq_trigger[i] = INTR_TRIGGER_LEVEL;
805*12471cecSLuiz Otavio O Souza 		sc->sc_irq_polarity[i] = INTR_POLARITY_LOW;
8064063f925SOleksandr Tymoshenko 		i++;
8074063f925SOleksandr Tymoshenko 	}
8084063f925SOleksandr Tymoshenko 	sc->sc_gpio_npins = i;
80990576f54SOleksandr Tymoshenko 	bcm_gpio_sysctl_init(sc);
8107836352bSLuiz Otavio O Souza 	sc->sc_busdev = gpiobus_attach_bus(dev);
8117836352bSLuiz Otavio O Souza 	if (sc->sc_busdev == NULL)
8127836352bSLuiz Otavio O Souza 		goto fail;
81390576f54SOleksandr Tymoshenko 
8147836352bSLuiz Otavio O Souza 	return (0);
8154063f925SOleksandr Tymoshenko 
8164063f925SOleksandr Tymoshenko fail:
817*12471cecSLuiz Otavio O Souza 	bcm_gpio_intr_detach(dev);
818c2136589SLuiz Otavio O Souza 	bus_release_resources(dev, bcm_gpio_res_spec, sc->sc_res);
819c2136589SLuiz Otavio O Souza 	mtx_destroy(&sc->sc_mtx);
820c2136589SLuiz Otavio O Souza 
8214063f925SOleksandr Tymoshenko 	return (ENXIO);
8224063f925SOleksandr Tymoshenko }
8234063f925SOleksandr Tymoshenko 
8244063f925SOleksandr Tymoshenko static int
8254063f925SOleksandr Tymoshenko bcm_gpio_detach(device_t dev)
8264063f925SOleksandr Tymoshenko {
8274063f925SOleksandr Tymoshenko 
8284063f925SOleksandr Tymoshenko 	return (EBUSY);
8294063f925SOleksandr Tymoshenko }
8304063f925SOleksandr Tymoshenko 
831*12471cecSLuiz Otavio O Souza static uint32_t
832*12471cecSLuiz Otavio O Souza bcm_gpio_intr_reg(struct bcm_gpio_softc *sc, unsigned int irq, uint32_t bank)
833*12471cecSLuiz Otavio O Souza {
834*12471cecSLuiz Otavio O Souza 
835*12471cecSLuiz Otavio O Souza 	if (irq > BCM_GPIO_PINS)
836*12471cecSLuiz Otavio O Souza 		return (0);
837*12471cecSLuiz Otavio O Souza 	if (sc->sc_irq_trigger[irq] == INTR_TRIGGER_LEVEL) {
838*12471cecSLuiz Otavio O Souza 		if (sc->sc_irq_polarity[irq] == INTR_POLARITY_LOW)
839*12471cecSLuiz Otavio O Souza 			return (BCM_GPIO_GPLEN(bank));
840*12471cecSLuiz Otavio O Souza 		else if (sc->sc_irq_polarity[irq] == INTR_POLARITY_HIGH)
841*12471cecSLuiz Otavio O Souza 			return (BCM_GPIO_GPHEN(bank));
842*12471cecSLuiz Otavio O Souza 	} else if (sc->sc_irq_trigger[irq] == INTR_TRIGGER_EDGE) {
843*12471cecSLuiz Otavio O Souza 		if (sc->sc_irq_polarity[irq] == INTR_POLARITY_LOW)
844*12471cecSLuiz Otavio O Souza 			return (BCM_GPIO_GPFEN(bank));
845*12471cecSLuiz Otavio O Souza 		else if (sc->sc_irq_polarity[irq] == INTR_POLARITY_HIGH)
846*12471cecSLuiz Otavio O Souza 			return (BCM_GPIO_GPREN(bank));
847*12471cecSLuiz Otavio O Souza 	}
848*12471cecSLuiz Otavio O Souza 
849*12471cecSLuiz Otavio O Souza 	return (0);
850*12471cecSLuiz Otavio O Souza }
851*12471cecSLuiz Otavio O Souza 
852*12471cecSLuiz Otavio O Souza static void
853*12471cecSLuiz Otavio O Souza bcm_gpio_mask_irq(void *source)
854*12471cecSLuiz Otavio O Souza {
855*12471cecSLuiz Otavio O Souza 	uint32_t bank, mask, reg;
856*12471cecSLuiz Otavio O Souza 	unsigned int irq;
857*12471cecSLuiz Otavio O Souza 
858*12471cecSLuiz Otavio O Souza 	irq = (unsigned int)source;
859*12471cecSLuiz Otavio O Souza 	if (irq > BCM_GPIO_PINS)
860*12471cecSLuiz Otavio O Souza 		return;
861*12471cecSLuiz Otavio O Souza 	if (bcm_gpio_pin_is_ro(bcm_gpio_sc, irq))
862*12471cecSLuiz Otavio O Souza 		return;
863*12471cecSLuiz Otavio O Souza 	bank = BCM_GPIO_BANK(irq);
864*12471cecSLuiz Otavio O Souza 	mask = BCM_GPIO_MASK(irq);
865*12471cecSLuiz Otavio O Souza 	BCM_GPIO_LOCK(bcm_gpio_sc);
866*12471cecSLuiz Otavio O Souza 	reg = bcm_gpio_intr_reg(bcm_gpio_sc, irq, bank);
867*12471cecSLuiz Otavio O Souza 	if (reg != 0)
868*12471cecSLuiz Otavio O Souza 		BCM_GPIO_CLEAR_BITS(bcm_gpio_sc, reg, mask);
869*12471cecSLuiz Otavio O Souza 	BCM_GPIO_UNLOCK(bcm_gpio_sc);
870*12471cecSLuiz Otavio O Souza }
871*12471cecSLuiz Otavio O Souza 
872*12471cecSLuiz Otavio O Souza static void
873*12471cecSLuiz Otavio O Souza bcm_gpio_unmask_irq(void *source)
874*12471cecSLuiz Otavio O Souza {
875*12471cecSLuiz Otavio O Souza 	uint32_t bank, mask, reg;
876*12471cecSLuiz Otavio O Souza 	unsigned int irq;
877*12471cecSLuiz Otavio O Souza 
878*12471cecSLuiz Otavio O Souza 	irq = (unsigned int)source;
879*12471cecSLuiz Otavio O Souza 	if (irq > BCM_GPIO_PINS)
880*12471cecSLuiz Otavio O Souza 		return;
881*12471cecSLuiz Otavio O Souza 	if (bcm_gpio_pin_is_ro(bcm_gpio_sc, irq))
882*12471cecSLuiz Otavio O Souza 		return;
883*12471cecSLuiz Otavio O Souza 	bank = BCM_GPIO_BANK(irq);
884*12471cecSLuiz Otavio O Souza 	mask = BCM_GPIO_MASK(irq);
885*12471cecSLuiz Otavio O Souza 	BCM_GPIO_LOCK(bcm_gpio_sc);
886*12471cecSLuiz Otavio O Souza 	reg = bcm_gpio_intr_reg(bcm_gpio_sc, irq, bank);
887*12471cecSLuiz Otavio O Souza 	if (reg != 0)
888*12471cecSLuiz Otavio O Souza 		BCM_GPIO_SET_BITS(bcm_gpio_sc, reg, mask);
889*12471cecSLuiz Otavio O Souza 	BCM_GPIO_UNLOCK(bcm_gpio_sc);
890*12471cecSLuiz Otavio O Souza }
891*12471cecSLuiz Otavio O Souza 
892*12471cecSLuiz Otavio O Souza static int
893*12471cecSLuiz Otavio O Souza bcm_gpio_activate_resource(device_t bus, device_t child, int type, int rid,
894*12471cecSLuiz Otavio O Souza 	struct resource *res)
895*12471cecSLuiz Otavio O Souza {
896*12471cecSLuiz Otavio O Souza 	int pin;
897*12471cecSLuiz Otavio O Souza 
898*12471cecSLuiz Otavio O Souza 	if (type != SYS_RES_IRQ)
899*12471cecSLuiz Otavio O Souza 		return (ENXIO);
900*12471cecSLuiz Otavio O Souza 	/* Unmask the interrupt. */
901*12471cecSLuiz Otavio O Souza 	pin = rman_get_start(res);
902*12471cecSLuiz Otavio O Souza 	bcm_gpio_unmask_irq((void *)pin);
903*12471cecSLuiz Otavio O Souza 
904*12471cecSLuiz Otavio O Souza 	return (0);
905*12471cecSLuiz Otavio O Souza }
906*12471cecSLuiz Otavio O Souza 
907*12471cecSLuiz Otavio O Souza static int
908*12471cecSLuiz Otavio O Souza bcm_gpio_deactivate_resource(device_t bus, device_t child, int type, int rid,
909*12471cecSLuiz Otavio O Souza 	struct resource *res)
910*12471cecSLuiz Otavio O Souza {
911*12471cecSLuiz Otavio O Souza 	int pin;
912*12471cecSLuiz Otavio O Souza 
913*12471cecSLuiz Otavio O Souza 	if (type != SYS_RES_IRQ)
914*12471cecSLuiz Otavio O Souza 		return (ENXIO);
915*12471cecSLuiz Otavio O Souza 	/* Mask the interrupt. */
916*12471cecSLuiz Otavio O Souza 	pin = rman_get_start(res);
917*12471cecSLuiz Otavio O Souza 	bcm_gpio_mask_irq((void *)pin);
918*12471cecSLuiz Otavio O Souza 
919*12471cecSLuiz Otavio O Souza 	return (0);
920*12471cecSLuiz Otavio O Souza }
921*12471cecSLuiz Otavio O Souza 
922*12471cecSLuiz Otavio O Souza static int
923*12471cecSLuiz Otavio O Souza bcm_gpio_config_intr(device_t dev, int irq, enum intr_trigger trig,
924*12471cecSLuiz Otavio O Souza 	enum intr_polarity pol)
925*12471cecSLuiz Otavio O Souza {
926*12471cecSLuiz Otavio O Souza 	int bank;
927*12471cecSLuiz Otavio O Souza 	struct bcm_gpio_softc *sc;
928*12471cecSLuiz Otavio O Souza 	uint32_t mask, oldreg, reg;
929*12471cecSLuiz Otavio O Souza 
930*12471cecSLuiz Otavio O Souza 	if (irq > BCM_GPIO_PINS)
931*12471cecSLuiz Otavio O Souza 		return (EINVAL);
932*12471cecSLuiz Otavio O Souza 	/* There is no standard trigger or polarity. */
933*12471cecSLuiz Otavio O Souza 	if (trig == INTR_TRIGGER_CONFORM || pol == INTR_POLARITY_CONFORM)
934*12471cecSLuiz Otavio O Souza 		return (EINVAL);
935*12471cecSLuiz Otavio O Souza 	sc = device_get_softc(dev);
936*12471cecSLuiz Otavio O Souza 	if (bcm_gpio_pin_is_ro(sc, irq))
937*12471cecSLuiz Otavio O Souza 		return (EINVAL);
938*12471cecSLuiz Otavio O Souza 	bank = BCM_GPIO_BANK(irq);
939*12471cecSLuiz Otavio O Souza 	mask = BCM_GPIO_MASK(irq);
940*12471cecSLuiz Otavio O Souza 	BCM_GPIO_LOCK(sc);
941*12471cecSLuiz Otavio O Souza 	oldreg = bcm_gpio_intr_reg(sc, irq, bank);
942*12471cecSLuiz Otavio O Souza 	sc->sc_irq_trigger[irq] = trig;
943*12471cecSLuiz Otavio O Souza 	sc->sc_irq_polarity[irq] = pol;
944*12471cecSLuiz Otavio O Souza 	reg = bcm_gpio_intr_reg(sc, irq, bank);
945*12471cecSLuiz Otavio O Souza 	if (reg != 0)
946*12471cecSLuiz Otavio O Souza 		BCM_GPIO_SET_BITS(sc, reg, mask);
947*12471cecSLuiz Otavio O Souza 	if (reg != oldreg && oldreg != 0)
948*12471cecSLuiz Otavio O Souza 		BCM_GPIO_CLEAR_BITS(sc, oldreg, mask);
949*12471cecSLuiz Otavio O Souza 	BCM_GPIO_UNLOCK(sc);
950*12471cecSLuiz Otavio O Souza 
951*12471cecSLuiz Otavio O Souza 	return (0);
952*12471cecSLuiz Otavio O Souza }
953*12471cecSLuiz Otavio O Souza 
954*12471cecSLuiz Otavio O Souza static int
955*12471cecSLuiz Otavio O Souza bcm_gpio_setup_intr(device_t bus, device_t child, struct resource *ires,
956*12471cecSLuiz Otavio O Souza 	int flags, driver_filter_t *filt, driver_intr_t *handler,
957*12471cecSLuiz Otavio O Souza 	void *arg, void **cookiep)
958*12471cecSLuiz Otavio O Souza {
959*12471cecSLuiz Otavio O Souza 	struct bcm_gpio_softc *sc;
960*12471cecSLuiz Otavio O Souza 	struct intr_event *event;
961*12471cecSLuiz Otavio O Souza 	int pin, error;
962*12471cecSLuiz Otavio O Souza 
963*12471cecSLuiz Otavio O Souza 	sc = device_get_softc(bus);
964*12471cecSLuiz Otavio O Souza 	pin = rman_get_start(ires);
965*12471cecSLuiz Otavio O Souza 	if (pin > BCM_GPIO_PINS)
966*12471cecSLuiz Otavio O Souza 		panic("%s: bad pin %d", __func__, pin);
967*12471cecSLuiz Otavio O Souza 	event = sc->sc_events[pin];
968*12471cecSLuiz Otavio O Souza 	if (event == NULL) {
969*12471cecSLuiz Otavio O Souza 		error = intr_event_create(&event, (void *)pin, 0, pin,
970*12471cecSLuiz Otavio O Souza 		    bcm_gpio_mask_irq, bcm_gpio_unmask_irq, NULL, NULL,
971*12471cecSLuiz Otavio O Souza 		    "gpio%d pin%d:", device_get_unit(bus), pin);
972*12471cecSLuiz Otavio O Souza 		if (error != 0)
973*12471cecSLuiz Otavio O Souza 			return (error);
974*12471cecSLuiz Otavio O Souza 		sc->sc_events[pin] = event;
975*12471cecSLuiz Otavio O Souza 	}
976*12471cecSLuiz Otavio O Souza 	intr_event_add_handler(event, device_get_nameunit(child), filt,
977*12471cecSLuiz Otavio O Souza 	    handler, arg, intr_priority(flags), flags, cookiep);
978*12471cecSLuiz Otavio O Souza 
979*12471cecSLuiz Otavio O Souza 	return (0);
980*12471cecSLuiz Otavio O Souza }
981*12471cecSLuiz Otavio O Souza 
982*12471cecSLuiz Otavio O Souza static int
983*12471cecSLuiz Otavio O Souza bcm_gpio_teardown_intr(device_t dev, device_t child, struct resource *ires,
984*12471cecSLuiz Otavio O Souza 	void *cookie)
985*12471cecSLuiz Otavio O Souza {
986*12471cecSLuiz Otavio O Souza 	struct bcm_gpio_softc *sc;
987*12471cecSLuiz Otavio O Souza 	int pin, err;
988*12471cecSLuiz Otavio O Souza 
989*12471cecSLuiz Otavio O Souza 	sc = device_get_softc(dev);
990*12471cecSLuiz Otavio O Souza 	pin = rman_get_start(ires);
991*12471cecSLuiz Otavio O Souza 	if (pin > BCM_GPIO_PINS)
992*12471cecSLuiz Otavio O Souza 		panic("%s: bad pin %d", __func__, pin);
993*12471cecSLuiz Otavio O Souza 	if (sc->sc_events[pin] == NULL)
994*12471cecSLuiz Otavio O Souza 		panic("Trying to teardown unoccupied IRQ");
995*12471cecSLuiz Otavio O Souza 	err = intr_event_remove_handler(cookie);
996*12471cecSLuiz Otavio O Souza 	if (!err)
997*12471cecSLuiz Otavio O Souza 		sc->sc_events[pin] = NULL;
998*12471cecSLuiz Otavio O Souza 
999*12471cecSLuiz Otavio O Souza 	return (err);
1000*12471cecSLuiz Otavio O Souza }
1001*12471cecSLuiz Otavio O Souza 
10028c705c2cSLuiz Otavio O Souza static phandle_t
10038c705c2cSLuiz Otavio O Souza bcm_gpio_get_node(device_t bus, device_t dev)
10048c705c2cSLuiz Otavio O Souza {
10058c705c2cSLuiz Otavio O Souza 
10068c705c2cSLuiz Otavio O Souza 	/* We only have one child, the GPIO bus, which needs our own node. */
10078c705c2cSLuiz Otavio O Souza 	return (ofw_bus_get_node(bus));
10088c705c2cSLuiz Otavio O Souza }
10098c705c2cSLuiz Otavio O Souza 
10104063f925SOleksandr Tymoshenko static device_method_t bcm_gpio_methods[] = {
10114063f925SOleksandr Tymoshenko 	/* Device interface */
10124063f925SOleksandr Tymoshenko 	DEVMETHOD(device_probe,		bcm_gpio_probe),
10134063f925SOleksandr Tymoshenko 	DEVMETHOD(device_attach,	bcm_gpio_attach),
10144063f925SOleksandr Tymoshenko 	DEVMETHOD(device_detach,	bcm_gpio_detach),
10154063f925SOleksandr Tymoshenko 
10164063f925SOleksandr Tymoshenko 	/* GPIO protocol */
10177836352bSLuiz Otavio O Souza 	DEVMETHOD(gpio_get_bus,		bcm_gpio_get_bus),
10184063f925SOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_max,		bcm_gpio_pin_max),
10194063f925SOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_getname,	bcm_gpio_pin_getname),
10204063f925SOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_getflags,	bcm_gpio_pin_getflags),
10214063f925SOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_getcaps,	bcm_gpio_pin_getcaps),
10224063f925SOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_setflags,	bcm_gpio_pin_setflags),
10234063f925SOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_get,		bcm_gpio_pin_get),
10244063f925SOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_set,		bcm_gpio_pin_set),
10254063f925SOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_toggle,	bcm_gpio_pin_toggle),
10264063f925SOleksandr Tymoshenko 
1027*12471cecSLuiz Otavio O Souza 	/* Bus interface */
1028*12471cecSLuiz Otavio O Souza 	DEVMETHOD(bus_activate_resource,	bcm_gpio_activate_resource),
1029*12471cecSLuiz Otavio O Souza 	DEVMETHOD(bus_deactivate_resource,	bcm_gpio_deactivate_resource),
1030*12471cecSLuiz Otavio O Souza 	DEVMETHOD(bus_config_intr,	bcm_gpio_config_intr),
1031*12471cecSLuiz Otavio O Souza 	DEVMETHOD(bus_setup_intr,	bcm_gpio_setup_intr),
1032*12471cecSLuiz Otavio O Souza 	DEVMETHOD(bus_teardown_intr,	bcm_gpio_teardown_intr),
1033*12471cecSLuiz Otavio O Souza 
10348c705c2cSLuiz Otavio O Souza 	/* ofw_bus interface */
10358c705c2cSLuiz Otavio O Souza 	DEVMETHOD(ofw_bus_get_node,	bcm_gpio_get_node),
10368c705c2cSLuiz Otavio O Souza 
10374063f925SOleksandr Tymoshenko 	DEVMETHOD_END
10384063f925SOleksandr Tymoshenko };
10394063f925SOleksandr Tymoshenko 
10404063f925SOleksandr Tymoshenko static devclass_t bcm_gpio_devclass;
10414063f925SOleksandr Tymoshenko 
10424063f925SOleksandr Tymoshenko static driver_t bcm_gpio_driver = {
10434063f925SOleksandr Tymoshenko 	"gpio",
10444063f925SOleksandr Tymoshenko 	bcm_gpio_methods,
10454063f925SOleksandr Tymoshenko 	sizeof(struct bcm_gpio_softc),
10464063f925SOleksandr Tymoshenko };
10474063f925SOleksandr Tymoshenko 
10484063f925SOleksandr Tymoshenko DRIVER_MODULE(bcm_gpio, simplebus, bcm_gpio_driver, bcm_gpio_devclass, 0, 0);
1049