xref: /freebsd/sys/arm/ti/ti_gpio.c (revision aa0d25b7a1595721908d0be91e40f54e4aa62b43)
1e53470feSOleksandr Tymoshenko /*-
23681c8d7SLuiz Otavio O Souza  * Copyright (c) 2011 Ben Gray <ben.r.gray@gmail.com>.
33681c8d7SLuiz Otavio O Souza  * Copyright (c) 2014 Luiz Otavio O Souza <loos@FreeBSD.org>.
4e53470feSOleksandr Tymoshenko  * All rights reserved.
5e53470feSOleksandr Tymoshenko  *
6e53470feSOleksandr Tymoshenko  * Redistribution and use in source and binary forms, with or without
7e53470feSOleksandr Tymoshenko  * modification, are permitted provided that the following conditions
8e53470feSOleksandr Tymoshenko  * are met:
9e53470feSOleksandr Tymoshenko  * 1. Redistributions of source code must retain the above copyright
10e53470feSOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer.
11e53470feSOleksandr Tymoshenko  * 2. Redistributions in binary form must reproduce the above copyright
12e53470feSOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer in the
13e53470feSOleksandr Tymoshenko  *    documentation and/or other materials provided with the distribution.
14e53470feSOleksandr Tymoshenko  *
15e53470feSOleksandr Tymoshenko  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16e53470feSOleksandr Tymoshenko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17e53470feSOleksandr Tymoshenko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18e53470feSOleksandr Tymoshenko  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
19e53470feSOleksandr Tymoshenko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20e53470feSOleksandr Tymoshenko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21e53470feSOleksandr Tymoshenko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22e53470feSOleksandr Tymoshenko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23e53470feSOleksandr Tymoshenko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24e53470feSOleksandr Tymoshenko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25e53470feSOleksandr Tymoshenko  * SUCH DAMAGE.
26e53470feSOleksandr Tymoshenko  */
27e53470feSOleksandr Tymoshenko 
28e53470feSOleksandr Tymoshenko /**
293681c8d7SLuiz Otavio O Souza  * Beware that the OMAP4 datasheet(s) lists GPIO banks 1-6, whereas the code
303681c8d7SLuiz Otavio O Souza  * here uses 0-5.
31e53470feSOleksandr Tymoshenko  */
32e53470feSOleksandr Tymoshenko 
33e53470feSOleksandr Tymoshenko #include <sys/cdefs.h>
34e53470feSOleksandr Tymoshenko __FBSDID("$FreeBSD$");
35e53470feSOleksandr Tymoshenko 
362df5562dSSvatopluk Kraus #include "opt_platform.h"
372df5562dSSvatopluk Kraus 
38e53470feSOleksandr Tymoshenko #include <sys/param.h>
39e53470feSOleksandr Tymoshenko #include <sys/systm.h>
40e53470feSOleksandr Tymoshenko #include <sys/bus.h>
41e53470feSOleksandr Tymoshenko 
42e53470feSOleksandr Tymoshenko #include <sys/kernel.h>
43e53470feSOleksandr Tymoshenko #include <sys/module.h>
442df5562dSSvatopluk Kraus #include <sys/proc.h>
45e53470feSOleksandr Tymoshenko #include <sys/rman.h>
46e53470feSOleksandr Tymoshenko #include <sys/lock.h>
47e53470feSOleksandr Tymoshenko #include <sys/mutex.h>
48e53470feSOleksandr Tymoshenko #include <sys/gpio.h>
493681c8d7SLuiz Otavio O Souza #include <sys/interrupt.h>
50e53470feSOleksandr Tymoshenko 
51e53470feSOleksandr Tymoshenko #include <machine/bus.h>
522df5562dSSvatopluk Kraus #include <machine/intr.h>
53e53470feSOleksandr Tymoshenko #include <machine/resource.h>
54e53470feSOleksandr Tymoshenko 
554eb12144SAndrew Turner #include <arm/ti/ti_cpuid.h>
56b6c7dacfSAndrew Turner #include <arm/ti/ti_gpio.h>
57e53470feSOleksandr Tymoshenko #include <arm/ti/ti_scm.h>
58e53470feSOleksandr Tymoshenko #include <arm/ti/ti_prcm.h>
595b03aba6SOleksandr Tymoshenko #include <arm/ti/ti_hwmods.h>
60e53470feSOleksandr Tymoshenko 
61e53470feSOleksandr Tymoshenko #include <dev/fdt/fdt_common.h>
627836352bSLuiz Otavio O Souza #include <dev/gpio/gpiobusvar.h>
63e53470feSOleksandr Tymoshenko #include <dev/ofw/openfirm.h>
64e53470feSOleksandr Tymoshenko #include <dev/ofw/ofw_bus.h>
65e53470feSOleksandr Tymoshenko #include <dev/ofw/ofw_bus_subr.h>
66e53470feSOleksandr Tymoshenko 
67e53470feSOleksandr Tymoshenko #include "gpio_if.h"
68b6c7dacfSAndrew Turner #include "ti_gpio_if.h"
6959c3cb81SAndrew Turner #ifdef INTRNG
702df5562dSSvatopluk Kraus #include "pic_if.h"
712df5562dSSvatopluk Kraus #endif
72e53470feSOleksandr Tymoshenko 
73a59806b2SLuiz Otavio O Souza #if !defined(SOC_OMAP4) && !defined(SOC_TI_AM335X)
74a59806b2SLuiz Otavio O Souza #error "Unknown SoC"
75a59806b2SLuiz Otavio O Souza #endif
76a59806b2SLuiz Otavio O Souza 
77e53470feSOleksandr Tymoshenko /* Register definitions */
78e53470feSOleksandr Tymoshenko #define	TI_GPIO_REVISION		0x0000
79e53470feSOleksandr Tymoshenko #define	TI_GPIO_SYSCONFIG		0x0010
80e53470feSOleksandr Tymoshenko #define	TI_GPIO_IRQSTATUS_RAW_0		0x0024
81e53470feSOleksandr Tymoshenko #define	TI_GPIO_IRQSTATUS_RAW_1		0x0028
822df5562dSSvatopluk Kraus #define	TI_GPIO_IRQSTATUS_0		0x002C	/* writing a 0 has no effect */
832df5562dSSvatopluk Kraus #define	TI_GPIO_IRQSTATUS_1		0x0030	/* writing a 0 has no effect */
842df5562dSSvatopluk Kraus #define	TI_GPIO_IRQSTATUS_SET_0		0x0034	/* writing a 0 has no effect */
852df5562dSSvatopluk Kraus #define	TI_GPIO_IRQSTATUS_SET_1		0x0038	/* writing a 0 has no effect */
862df5562dSSvatopluk Kraus #define	TI_GPIO_IRQSTATUS_CLR_0		0x003C	/* writing a 0 has no effect */
872df5562dSSvatopluk Kraus #define	TI_GPIO_IRQSTATUS_CLR_1		0x0040	/* writing a 0 has no effect */
88e53470feSOleksandr Tymoshenko #define	TI_GPIO_IRQWAKEN_0		0x0044
89e53470feSOleksandr Tymoshenko #define	TI_GPIO_IRQWAKEN_1		0x0048
90e53470feSOleksandr Tymoshenko #define	TI_GPIO_SYSSTATUS		0x0114
91e53470feSOleksandr Tymoshenko #define	TI_GPIO_IRQSTATUS1		0x0118
92e53470feSOleksandr Tymoshenko #define	TI_GPIO_IRQENABLE1		0x011C
93e53470feSOleksandr Tymoshenko #define	TI_GPIO_WAKEUPENABLE		0x0120
94e53470feSOleksandr Tymoshenko #define	TI_GPIO_IRQSTATUS2		0x0128
95e53470feSOleksandr Tymoshenko #define	TI_GPIO_IRQENABLE2		0x012C
96e53470feSOleksandr Tymoshenko #define	TI_GPIO_CTRL			0x0130
97e53470feSOleksandr Tymoshenko #define	TI_GPIO_OE			0x0134
98e53470feSOleksandr Tymoshenko #define	TI_GPIO_DATAIN			0x0138
99e53470feSOleksandr Tymoshenko #define	TI_GPIO_DATAOUT			0x013C
1002df5562dSSvatopluk Kraus #define	TI_GPIO_LEVELDETECT0		0x0140	/* RW register */
1012df5562dSSvatopluk Kraus #define	TI_GPIO_LEVELDETECT1		0x0144	/* RW register */
1022df5562dSSvatopluk Kraus #define	TI_GPIO_RISINGDETECT		0x0148	/* RW register */
1032df5562dSSvatopluk Kraus #define	TI_GPIO_FALLINGDETECT		0x014C	/* RW register */
104e53470feSOleksandr Tymoshenko #define	TI_GPIO_DEBOUNCENABLE		0x0150
105e53470feSOleksandr Tymoshenko #define	TI_GPIO_DEBOUNCINGTIME		0x0154
106e53470feSOleksandr Tymoshenko #define	TI_GPIO_CLEARWKUPENA		0x0180
107e53470feSOleksandr Tymoshenko #define	TI_GPIO_SETWKUENA		0x0184
108e53470feSOleksandr Tymoshenko #define	TI_GPIO_CLEARDATAOUT		0x0190
109e53470feSOleksandr Tymoshenko #define	TI_GPIO_SETDATAOUT		0x0194
110e53470feSOleksandr Tymoshenko 
111e53470feSOleksandr Tymoshenko /* Other SoC Specific definitions */
1124eb12144SAndrew Turner #define	OMAP4_FIRST_GPIO_BANK		1
1134eb12144SAndrew Turner #define	OMAP4_INTR_PER_BANK		1
1144eb12144SAndrew Turner #define	OMAP4_GPIO_REV			0x50600801
1154eb12144SAndrew Turner #define	AM335X_FIRST_GPIO_BANK		0
1164eb12144SAndrew Turner #define	AM335X_INTR_PER_BANK		2
1174eb12144SAndrew Turner #define	AM335X_GPIO_REV			0x50600801
118ff5823beSLuiz Otavio O Souza #define	PINS_PER_BANK			32
119e350f76cSLuiz Otavio O Souza #define	TI_GPIO_MASK(p)			(1U << ((p) % PINS_PER_BANK))
1204eb12144SAndrew Turner 
1212df5562dSSvatopluk Kraus static int ti_gpio_intr(void *arg);
122876c1bd8SLuiz Otavio O Souza static int ti_gpio_detach(device_t);
1233681c8d7SLuiz Otavio O Souza 
12459c3cb81SAndrew Turner #ifdef INTRNG
1252df5562dSSvatopluk Kraus static int ti_gpio_pic_attach(struct ti_gpio_softc *sc);
1262df5562dSSvatopluk Kraus static int ti_gpio_pic_detach(struct ti_gpio_softc *sc);
1272df5562dSSvatopluk Kraus #endif
1282df5562dSSvatopluk Kraus 
1294eb12144SAndrew Turner static u_int
1304eb12144SAndrew Turner ti_first_gpio_bank(void)
1314eb12144SAndrew Turner {
1324eb12144SAndrew Turner 	switch(ti_chip()) {
1334eb12144SAndrew Turner #ifdef SOC_OMAP4
1344eb12144SAndrew Turner 	case CHIP_OMAP_4:
1354eb12144SAndrew Turner 		return (OMAP4_FIRST_GPIO_BANK);
1364eb12144SAndrew Turner #endif
1374eb12144SAndrew Turner #ifdef SOC_TI_AM335X
1384eb12144SAndrew Turner 	case CHIP_AM335X:
1394eb12144SAndrew Turner 		return (AM335X_FIRST_GPIO_BANK);
1404eb12144SAndrew Turner #endif
1414eb12144SAndrew Turner 	}
1424eb12144SAndrew Turner 	return (0);
1434eb12144SAndrew Turner }
1444eb12144SAndrew Turner 
1454eb12144SAndrew Turner static uint32_t
1464eb12144SAndrew Turner ti_gpio_rev(void)
1474eb12144SAndrew Turner {
1484eb12144SAndrew Turner 	switch(ti_chip()) {
1494eb12144SAndrew Turner #ifdef SOC_OMAP4
1504eb12144SAndrew Turner 	case CHIP_OMAP_4:
1514eb12144SAndrew Turner 		return (OMAP4_GPIO_REV);
1524eb12144SAndrew Turner #endif
1534eb12144SAndrew Turner #ifdef SOC_TI_AM335X
1544eb12144SAndrew Turner 	case CHIP_AM335X:
1554eb12144SAndrew Turner 		return (AM335X_GPIO_REV);
1564eb12144SAndrew Turner #endif
1574eb12144SAndrew Turner 	}
1584eb12144SAndrew Turner 	return (0);
1594eb12144SAndrew Turner }
160e53470feSOleksandr Tymoshenko 
161e53470feSOleksandr Tymoshenko /**
162e53470feSOleksandr Tymoshenko  *	Macros for driver mutex locking
163e53470feSOleksandr Tymoshenko  */
1643681c8d7SLuiz Otavio O Souza #define	TI_GPIO_LOCK(_sc)		mtx_lock_spin(&(_sc)->sc_mtx)
1653681c8d7SLuiz Otavio O Souza #define	TI_GPIO_UNLOCK(_sc)		mtx_unlock_spin(&(_sc)->sc_mtx)
166e53470feSOleksandr Tymoshenko #define	TI_GPIO_LOCK_INIT(_sc)		\
1673681c8d7SLuiz Otavio O Souza 	mtx_init(&_sc->sc_mtx, device_get_nameunit((_sc)->sc_dev), \
1683681c8d7SLuiz Otavio O Souza 	    "ti_gpio", MTX_SPIN)
1693681c8d7SLuiz Otavio O Souza #define	TI_GPIO_LOCK_DESTROY(_sc)	mtx_destroy(&(_sc)->sc_mtx)
1703681c8d7SLuiz Otavio O Souza #define	TI_GPIO_ASSERT_LOCKED(_sc)	mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
1713681c8d7SLuiz Otavio O Souza #define	TI_GPIO_ASSERT_UNLOCKED(_sc)	mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED)
172e53470feSOleksandr Tymoshenko 
173e53470feSOleksandr Tymoshenko /**
174e350f76cSLuiz Otavio O Souza  *	ti_gpio_read_4 - reads a 32-bit value from one of the GPIO registers
175e53470feSOleksandr Tymoshenko  *	@sc: GPIO device context
176e53470feSOleksandr Tymoshenko  *	@bank: The bank to read from
177e53470feSOleksandr Tymoshenko  *	@off: The offset of a register from the GPIO register address range
178e53470feSOleksandr Tymoshenko  *
179e53470feSOleksandr Tymoshenko  *
180e53470feSOleksandr Tymoshenko  *	RETURNS:
181e53470feSOleksandr Tymoshenko  *	32-bit value read from the register.
182e53470feSOleksandr Tymoshenko  */
183e53470feSOleksandr Tymoshenko static inline uint32_t
1845b03aba6SOleksandr Tymoshenko ti_gpio_read_4(struct ti_gpio_softc *sc, bus_size_t off)
185e53470feSOleksandr Tymoshenko {
1865b03aba6SOleksandr Tymoshenko 	return (bus_read_4(sc->sc_mem_res, off));
187e53470feSOleksandr Tymoshenko }
188e53470feSOleksandr Tymoshenko 
189e53470feSOleksandr Tymoshenko /**
190e350f76cSLuiz Otavio O Souza  *	ti_gpio_write_4 - writes a 32-bit value to one of the GPIO registers
191e53470feSOleksandr Tymoshenko  *	@sc: GPIO device context
192e53470feSOleksandr Tymoshenko  *	@bank: The bank to write to
193e53470feSOleksandr Tymoshenko  *	@off: The offset of a register from the GPIO register address range
194e53470feSOleksandr Tymoshenko  *	@val: The value to write into the register
195e53470feSOleksandr Tymoshenko  *
196e53470feSOleksandr Tymoshenko  *	RETURNS:
197e53470feSOleksandr Tymoshenko  *	nothing
198e53470feSOleksandr Tymoshenko  */
199e53470feSOleksandr Tymoshenko static inline void
2005b03aba6SOleksandr Tymoshenko ti_gpio_write_4(struct ti_gpio_softc *sc, bus_size_t off,
201e53470feSOleksandr Tymoshenko                  uint32_t val)
202e53470feSOleksandr Tymoshenko {
2035b03aba6SOleksandr Tymoshenko 	bus_write_4(sc->sc_mem_res, off, val);
204e53470feSOleksandr Tymoshenko }
205e53470feSOleksandr Tymoshenko 
206db8a14ecSLuiz Otavio O Souza static inline void
2075b03aba6SOleksandr Tymoshenko ti_gpio_intr_clr(struct ti_gpio_softc *sc, uint32_t mask)
208db8a14ecSLuiz Otavio O Souza {
209db8a14ecSLuiz Otavio O Souza 
210db8a14ecSLuiz Otavio O Souza 	/* We clear both set of registers. */
2115b03aba6SOleksandr Tymoshenko 	ti_gpio_write_4(sc, TI_GPIO_IRQSTATUS_CLR_0, mask);
2125b03aba6SOleksandr Tymoshenko 	ti_gpio_write_4(sc, TI_GPIO_IRQSTATUS_CLR_1, mask);
213db8a14ecSLuiz Otavio O Souza }
214db8a14ecSLuiz Otavio O Souza 
2153681c8d7SLuiz Otavio O Souza static inline void
2165b03aba6SOleksandr Tymoshenko ti_gpio_intr_set(struct ti_gpio_softc *sc, uint32_t mask)
2173681c8d7SLuiz Otavio O Souza {
2183681c8d7SLuiz Otavio O Souza 
2193681c8d7SLuiz Otavio O Souza 	/*
2203681c8d7SLuiz Otavio O Souza 	 * On OMAP4 we unmask only the MPU interrupt and on AM335x we
2213681c8d7SLuiz Otavio O Souza 	 * also activate only the first interrupt.
2223681c8d7SLuiz Otavio O Souza 	 */
2235b03aba6SOleksandr Tymoshenko 	ti_gpio_write_4(sc, TI_GPIO_IRQSTATUS_SET_0, mask);
2243681c8d7SLuiz Otavio O Souza }
2253681c8d7SLuiz Otavio O Souza 
2263681c8d7SLuiz Otavio O Souza static inline void
2275b03aba6SOleksandr Tymoshenko ti_gpio_intr_ack(struct ti_gpio_softc *sc, uint32_t mask)
2283681c8d7SLuiz Otavio O Souza {
2293681c8d7SLuiz Otavio O Souza 
2303681c8d7SLuiz Otavio O Souza 	/*
2313681c8d7SLuiz Otavio O Souza 	 * Acknowledge the interrupt on both registers even if we use only
2323681c8d7SLuiz Otavio O Souza 	 * the first one.
2333681c8d7SLuiz Otavio O Souza 	 */
2345b03aba6SOleksandr Tymoshenko 	ti_gpio_write_4(sc, TI_GPIO_IRQSTATUS_0, mask);
2355b03aba6SOleksandr Tymoshenko 	ti_gpio_write_4(sc, TI_GPIO_IRQSTATUS_1, mask);
2363681c8d7SLuiz Otavio O Souza }
2373681c8d7SLuiz Otavio O Souza 
2383681c8d7SLuiz Otavio O Souza static inline uint32_t
2395b03aba6SOleksandr Tymoshenko ti_gpio_intr_status(struct ti_gpio_softc *sc)
2403681c8d7SLuiz Otavio O Souza {
2413681c8d7SLuiz Otavio O Souza 	uint32_t reg;
2423681c8d7SLuiz Otavio O Souza 
2433681c8d7SLuiz Otavio O Souza 	/* Get the status from both registers. */
2445b03aba6SOleksandr Tymoshenko 	reg = ti_gpio_read_4(sc, TI_GPIO_IRQSTATUS_0);
2455b03aba6SOleksandr Tymoshenko 	reg |= ti_gpio_read_4(sc, TI_GPIO_IRQSTATUS_1);
2463681c8d7SLuiz Otavio O Souza 
2473681c8d7SLuiz Otavio O Souza 	return (reg);
2483681c8d7SLuiz Otavio O Souza }
2493681c8d7SLuiz Otavio O Souza 
2507836352bSLuiz Otavio O Souza static device_t
2517836352bSLuiz Otavio O Souza ti_gpio_get_bus(device_t dev)
2527836352bSLuiz Otavio O Souza {
2537836352bSLuiz Otavio O Souza 	struct ti_gpio_softc *sc;
2547836352bSLuiz Otavio O Souza 
2557836352bSLuiz Otavio O Souza 	sc = device_get_softc(dev);
2567836352bSLuiz Otavio O Souza 
2577836352bSLuiz Otavio O Souza 	return (sc->sc_busdev);
2587836352bSLuiz Otavio O Souza }
2597836352bSLuiz Otavio O Souza 
260e53470feSOleksandr Tymoshenko /**
261e53470feSOleksandr Tymoshenko  *	ti_gpio_pin_max - Returns the maximum number of GPIO pins
262e53470feSOleksandr Tymoshenko  *	@dev: gpio device handle
263e53470feSOleksandr Tymoshenko  *	@maxpin: pointer to a value that upon return will contain the maximum number
264e53470feSOleksandr Tymoshenko  *	         of pins in the device.
265e53470feSOleksandr Tymoshenko  *
266e53470feSOleksandr Tymoshenko  *
267e53470feSOleksandr Tymoshenko  *	LOCKING:
268f9de33d4SLuiz Otavio O Souza  *	No locking required, returns static data.
269e53470feSOleksandr Tymoshenko  *
270e53470feSOleksandr Tymoshenko  *	RETURNS:
271e53470feSOleksandr Tymoshenko  *	Returns 0 on success otherwise an error code
272e53470feSOleksandr Tymoshenko  */
273e53470feSOleksandr Tymoshenko static int
274e53470feSOleksandr Tymoshenko ti_gpio_pin_max(device_t dev, int *maxpin)
275e53470feSOleksandr Tymoshenko {
276e53470feSOleksandr Tymoshenko 
2775b03aba6SOleksandr Tymoshenko 	*maxpin = PINS_PER_BANK - 1;
278e53470feSOleksandr Tymoshenko 
279e53470feSOleksandr Tymoshenko 	return (0);
280e53470feSOleksandr Tymoshenko }
281e53470feSOleksandr Tymoshenko 
282e350f76cSLuiz Otavio O Souza static int
283e350f76cSLuiz Otavio O Souza ti_gpio_valid_pin(struct ti_gpio_softc *sc, int pin)
284e350f76cSLuiz Otavio O Souza {
285e350f76cSLuiz Otavio O Souza 
2865b03aba6SOleksandr Tymoshenko 	if (pin >= sc->sc_maxpin || sc->sc_mem_res == NULL)
287e350f76cSLuiz Otavio O Souza 		return (EINVAL);
288e350f76cSLuiz Otavio O Souza 
289e350f76cSLuiz Otavio O Souza 	return (0);
290e350f76cSLuiz Otavio O Souza }
291e350f76cSLuiz Otavio O Souza 
292e53470feSOleksandr Tymoshenko /**
293*aa0d25b7SSvatopluk Kraus  *	ti_gpio_pin_getcaps - Gets the capabilities of a given pin
294e53470feSOleksandr Tymoshenko  *	@dev: gpio device handle
295e53470feSOleksandr Tymoshenko  *	@pin: the number of the pin
296e53470feSOleksandr Tymoshenko  *	@caps: pointer to a value that upon return will contain the capabilities
297e53470feSOleksandr Tymoshenko  *
298e53470feSOleksandr Tymoshenko  *	Currently all pins have the same capability, notably:
299e53470feSOleksandr Tymoshenko  *	  - GPIO_PIN_INPUT
300e53470feSOleksandr Tymoshenko  *	  - GPIO_PIN_OUTPUT
301e53470feSOleksandr Tymoshenko  *	  - GPIO_PIN_PULLUP
302e53470feSOleksandr Tymoshenko  *	  - GPIO_PIN_PULLDOWN
303*aa0d25b7SSvatopluk Kraus  *	  - GPIO_INTR_LEVEL_LOW
304*aa0d25b7SSvatopluk Kraus  *	  - GPIO_INTR_LEVEL_HIGH
305*aa0d25b7SSvatopluk Kraus  *	  - GPIO_INTR_EDGE_RISING
306*aa0d25b7SSvatopluk Kraus  *	  - GPIO_INTR_EDGE_FALLING
307*aa0d25b7SSvatopluk Kraus  *	  - GPIO_INTR_EDGE_BOTH
308e53470feSOleksandr Tymoshenko  *
309e53470feSOleksandr Tymoshenko  *	LOCKING:
310f9de33d4SLuiz Otavio O Souza  *	No locking required, returns static data.
311e53470feSOleksandr Tymoshenko  *
312e53470feSOleksandr Tymoshenko  *	RETURNS:
313e53470feSOleksandr Tymoshenko  *	Returns 0 on success otherwise an error code
314e53470feSOleksandr Tymoshenko  */
315e53470feSOleksandr Tymoshenko static int
316e53470feSOleksandr Tymoshenko ti_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
317e53470feSOleksandr Tymoshenko {
318e350f76cSLuiz Otavio O Souza 	struct ti_gpio_softc *sc;
319e53470feSOleksandr Tymoshenko 
320e350f76cSLuiz Otavio O Souza 	sc = device_get_softc(dev);
321e350f76cSLuiz Otavio O Souza 	if (ti_gpio_valid_pin(sc, pin) != 0)
322e53470feSOleksandr Tymoshenko 		return (EINVAL);
323e53470feSOleksandr Tymoshenko 
324*aa0d25b7SSvatopluk Kraus #ifdef INTRNG
325*aa0d25b7SSvatopluk Kraus 	*caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_PULLUP |
326*aa0d25b7SSvatopluk Kraus 	    GPIO_PIN_PULLDOWN | GPIO_INTR_LEVEL_LOW | GPIO_INTR_LEVEL_HIGH |
327*aa0d25b7SSvatopluk Kraus 	    GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING |
328*aa0d25b7SSvatopluk Kraus 	    GPIO_INTR_EDGE_BOTH);
329*aa0d25b7SSvatopluk Kraus #else
330e53470feSOleksandr Tymoshenko 	*caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_PULLUP |
331e53470feSOleksandr Tymoshenko 	    GPIO_PIN_PULLDOWN);
332*aa0d25b7SSvatopluk Kraus #endif
333e53470feSOleksandr Tymoshenko 
334e53470feSOleksandr Tymoshenko 	return (0);
335e53470feSOleksandr Tymoshenko }
336e53470feSOleksandr Tymoshenko 
337e53470feSOleksandr Tymoshenko /**
338e53470feSOleksandr Tymoshenko  *	ti_gpio_pin_getflags - Gets the current flags of a given pin
339e53470feSOleksandr Tymoshenko  *	@dev: gpio device handle
340e53470feSOleksandr Tymoshenko  *	@pin: the number of the pin
341e53470feSOleksandr Tymoshenko  *	@flags: upon return will contain the current flags of the pin
342e53470feSOleksandr Tymoshenko  *
343e53470feSOleksandr Tymoshenko  *	Reads the current flags of a given pin, here we actually read the H/W
344e53470feSOleksandr Tymoshenko  *	registers to determine the flags, rather than storing the value in the
345e53470feSOleksandr Tymoshenko  *	setflags call.
346e53470feSOleksandr Tymoshenko  *
347e53470feSOleksandr Tymoshenko  *	LOCKING:
348e53470feSOleksandr Tymoshenko  *	Internally locks the context
349e53470feSOleksandr Tymoshenko  *
350e53470feSOleksandr Tymoshenko  *	RETURNS:
351e53470feSOleksandr Tymoshenko  *	Returns 0 on success otherwise an error code
352e53470feSOleksandr Tymoshenko  */
353e53470feSOleksandr Tymoshenko static int
354e53470feSOleksandr Tymoshenko ti_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
355e53470feSOleksandr Tymoshenko {
356e350f76cSLuiz Otavio O Souza 	struct ti_gpio_softc *sc;
357e53470feSOleksandr Tymoshenko 
358e350f76cSLuiz Otavio O Souza 	sc = device_get_softc(dev);
359e350f76cSLuiz Otavio O Souza 	if (ti_gpio_valid_pin(sc, pin) != 0)
360e53470feSOleksandr Tymoshenko 		return (EINVAL);
361e53470feSOleksandr Tymoshenko 
362e53470feSOleksandr Tymoshenko 	/* Get the current pin state */
363f9de33d4SLuiz Otavio O Souza 	TI_GPIO_LOCK(sc);
364b6c7dacfSAndrew Turner 	TI_GPIO_GET_FLAGS(dev, pin, flags);
365e53470feSOleksandr Tymoshenko 	TI_GPIO_UNLOCK(sc);
366e53470feSOleksandr Tymoshenko 
367e53470feSOleksandr Tymoshenko 	return (0);
368e53470feSOleksandr Tymoshenko }
369e53470feSOleksandr Tymoshenko 
370e53470feSOleksandr Tymoshenko /**
371e53470feSOleksandr Tymoshenko  *	ti_gpio_pin_getname - Gets the name of a given pin
372e53470feSOleksandr Tymoshenko  *	@dev: gpio device handle
373e53470feSOleksandr Tymoshenko  *	@pin: the number of the pin
374e53470feSOleksandr Tymoshenko  *	@name: buffer to put the name in
375e53470feSOleksandr Tymoshenko  *
376e53470feSOleksandr Tymoshenko  *	The driver simply calls the pins gpio_n, where 'n' is obviously the number
377e53470feSOleksandr Tymoshenko  *	of the pin.
378e53470feSOleksandr Tymoshenko  *
379e53470feSOleksandr Tymoshenko  *	LOCKING:
380f9de33d4SLuiz Otavio O Souza  *	No locking required, returns static data.
381e53470feSOleksandr Tymoshenko  *
382e53470feSOleksandr Tymoshenko  *	RETURNS:
383e53470feSOleksandr Tymoshenko  *	Returns 0 on success otherwise an error code
384e53470feSOleksandr Tymoshenko  */
385e53470feSOleksandr Tymoshenko static int
386e53470feSOleksandr Tymoshenko ti_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
387e53470feSOleksandr Tymoshenko {
388e350f76cSLuiz Otavio O Souza 	struct ti_gpio_softc *sc;
389e53470feSOleksandr Tymoshenko 
390e350f76cSLuiz Otavio O Souza 	sc = device_get_softc(dev);
391e350f76cSLuiz Otavio O Souza 	if (ti_gpio_valid_pin(sc, pin) != 0)
392e53470feSOleksandr Tymoshenko 		return (EINVAL);
393e53470feSOleksandr Tymoshenko 
394e53470feSOleksandr Tymoshenko 	/* Set a very simple name */
395e53470feSOleksandr Tymoshenko 	snprintf(name, GPIOMAXNAME, "gpio_%u", pin);
396e53470feSOleksandr Tymoshenko 	name[GPIOMAXNAME - 1] = '\0';
397e53470feSOleksandr Tymoshenko 
398e53470feSOleksandr Tymoshenko 	return (0);
399e53470feSOleksandr Tymoshenko }
400e53470feSOleksandr Tymoshenko 
401e53470feSOleksandr Tymoshenko /**
402e53470feSOleksandr Tymoshenko  *	ti_gpio_pin_setflags - Sets the flags for a given pin
403e53470feSOleksandr Tymoshenko  *	@dev: gpio device handle
404e53470feSOleksandr Tymoshenko  *	@pin: the number of the pin
405e53470feSOleksandr Tymoshenko  *	@flags: the flags to set
406e53470feSOleksandr Tymoshenko  *
407e53470feSOleksandr Tymoshenko  *	The flags of the pin correspond to things like input/output mode, pull-ups,
408e53470feSOleksandr Tymoshenko  *	pull-downs, etc.  This driver doesn't support all flags, only the following:
409e53470feSOleksandr Tymoshenko  *	  - GPIO_PIN_INPUT
410e53470feSOleksandr Tymoshenko  *	  - GPIO_PIN_OUTPUT
411e53470feSOleksandr Tymoshenko  *	  - GPIO_PIN_PULLUP
412e53470feSOleksandr Tymoshenko  *	  - GPIO_PIN_PULLDOWN
413e53470feSOleksandr Tymoshenko  *
414e53470feSOleksandr Tymoshenko  *	LOCKING:
415e53470feSOleksandr Tymoshenko  *	Internally locks the context
416e53470feSOleksandr Tymoshenko  *
417e53470feSOleksandr Tymoshenko  *	RETURNS:
418e53470feSOleksandr Tymoshenko  *	Returns 0 on success otherwise an error code
419e53470feSOleksandr Tymoshenko  */
420e53470feSOleksandr Tymoshenko static int
421e53470feSOleksandr Tymoshenko ti_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
422e53470feSOleksandr Tymoshenko {
423e350f76cSLuiz Otavio O Souza 	struct ti_gpio_softc *sc;
424f9de33d4SLuiz Otavio O Souza 	uint32_t oe;
425e53470feSOleksandr Tymoshenko 
426e350f76cSLuiz Otavio O Souza 	sc = device_get_softc(dev);
427e350f76cSLuiz Otavio O Souza 	if (ti_gpio_valid_pin(sc, pin) != 0)
428e53470feSOleksandr Tymoshenko 		return (EINVAL);
429e53470feSOleksandr Tymoshenko 
430e53470feSOleksandr Tymoshenko 	/* Set the GPIO mode and state */
431f9de33d4SLuiz Otavio O Souza 	TI_GPIO_LOCK(sc);
432b6c7dacfSAndrew Turner 	if (TI_GPIO_SET_FLAGS(dev, pin, flags) != 0) {
433e53470feSOleksandr Tymoshenko 		TI_GPIO_UNLOCK(sc);
434e53470feSOleksandr Tymoshenko 		return (EINVAL);
435e53470feSOleksandr Tymoshenko 	}
436e53470feSOleksandr Tymoshenko 
437e53470feSOleksandr Tymoshenko 	/* If configuring as an output set the "output enable" bit */
4385b03aba6SOleksandr Tymoshenko 	oe = ti_gpio_read_4(sc, TI_GPIO_OE);
439e53470feSOleksandr Tymoshenko 	if (flags & GPIO_PIN_INPUT)
440e350f76cSLuiz Otavio O Souza 		oe |= TI_GPIO_MASK(pin);
441e53470feSOleksandr Tymoshenko 	else
442e350f76cSLuiz Otavio O Souza 		oe &= ~TI_GPIO_MASK(pin);
4435b03aba6SOleksandr Tymoshenko 	ti_gpio_write_4(sc, TI_GPIO_OE, oe);
444e53470feSOleksandr Tymoshenko 	TI_GPIO_UNLOCK(sc);
445e53470feSOleksandr Tymoshenko 
446e53470feSOleksandr Tymoshenko 	return (0);
447e53470feSOleksandr Tymoshenko }
448e53470feSOleksandr Tymoshenko 
449e53470feSOleksandr Tymoshenko /**
450e53470feSOleksandr Tymoshenko  *	ti_gpio_pin_set - Sets the current level on a GPIO pin
451e53470feSOleksandr Tymoshenko  *	@dev: gpio device handle
452e53470feSOleksandr Tymoshenko  *	@pin: the number of the pin
453e53470feSOleksandr Tymoshenko  *	@value: non-zero value will drive the pin high, otherwise the pin is
454e53470feSOleksandr Tymoshenko  *	        driven low.
455e53470feSOleksandr Tymoshenko  *
456e53470feSOleksandr Tymoshenko  *
457e53470feSOleksandr Tymoshenko  *	LOCKING:
458e53470feSOleksandr Tymoshenko  *	Internally locks the context
459e53470feSOleksandr Tymoshenko  *
460e53470feSOleksandr Tymoshenko  *	RETURNS:
461e53470feSOleksandr Tymoshenko  *	Returns 0 on success otherwise a error code
462e53470feSOleksandr Tymoshenko  */
463e53470feSOleksandr Tymoshenko static int
464e53470feSOleksandr Tymoshenko ti_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
465e53470feSOleksandr Tymoshenko {
466e350f76cSLuiz Otavio O Souza 	struct ti_gpio_softc *sc;
467f9de33d4SLuiz Otavio O Souza 	uint32_t reg;
468e53470feSOleksandr Tymoshenko 
469e350f76cSLuiz Otavio O Souza 	sc = device_get_softc(dev);
470e350f76cSLuiz Otavio O Souza 	if (ti_gpio_valid_pin(sc, pin) != 0)
471e53470feSOleksandr Tymoshenko 		return (EINVAL);
472e53470feSOleksandr Tymoshenko 
473f9de33d4SLuiz Otavio O Souza 	TI_GPIO_LOCK(sc);
474f9de33d4SLuiz Otavio O Souza 	if (value == GPIO_PIN_LOW)
475f9de33d4SLuiz Otavio O Souza 		reg = TI_GPIO_CLEARDATAOUT;
476f9de33d4SLuiz Otavio O Souza 	else
477f9de33d4SLuiz Otavio O Souza 		reg = TI_GPIO_SETDATAOUT;
4785b03aba6SOleksandr Tymoshenko 	ti_gpio_write_4(sc, reg, TI_GPIO_MASK(pin));
479e53470feSOleksandr Tymoshenko 	TI_GPIO_UNLOCK(sc);
480e53470feSOleksandr Tymoshenko 
481e53470feSOleksandr Tymoshenko 	return (0);
482e53470feSOleksandr Tymoshenko }
483e53470feSOleksandr Tymoshenko 
484e53470feSOleksandr Tymoshenko /**
485e53470feSOleksandr Tymoshenko  *	ti_gpio_pin_get - Gets the current level on a GPIO pin
486e53470feSOleksandr Tymoshenko  *	@dev: gpio device handle
487e53470feSOleksandr Tymoshenko  *	@pin: the number of the pin
488e53470feSOleksandr Tymoshenko  *	@value: pointer to a value that upond return will contain the pin value
489e53470feSOleksandr Tymoshenko  *
490e53470feSOleksandr Tymoshenko  *	The pin must be configured as an input pin beforehand, otherwise this
491e53470feSOleksandr Tymoshenko  *	function will fail.
492e53470feSOleksandr Tymoshenko  *
493e53470feSOleksandr Tymoshenko  *	LOCKING:
494e53470feSOleksandr Tymoshenko  *	Internally locks the context
495e53470feSOleksandr Tymoshenko  *
496e53470feSOleksandr Tymoshenko  *	RETURNS:
497e53470feSOleksandr Tymoshenko  *	Returns 0 on success otherwise a error code
498e53470feSOleksandr Tymoshenko  */
499e53470feSOleksandr Tymoshenko static int
500e53470feSOleksandr Tymoshenko ti_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value)
501e53470feSOleksandr Tymoshenko {
502e350f76cSLuiz Otavio O Souza 	struct ti_gpio_softc *sc;
503e350f76cSLuiz Otavio O Souza 	uint32_t oe, reg, val;
504e53470feSOleksandr Tymoshenko 
505e350f76cSLuiz Otavio O Souza 	sc = device_get_softc(dev);
506e350f76cSLuiz Otavio O Souza 	if (ti_gpio_valid_pin(sc, pin) != 0)
507e53470feSOleksandr Tymoshenko 		return (EINVAL);
508e53470feSOleksandr Tymoshenko 
509f9de33d4SLuiz Otavio O Souza 	/*
510f9de33d4SLuiz Otavio O Souza 	 * Return data from output latch when set as output and from the
511f9de33d4SLuiz Otavio O Souza 	 * input register otherwise.
512f9de33d4SLuiz Otavio O Souza 	 */
513f9de33d4SLuiz Otavio O Souza 	TI_GPIO_LOCK(sc);
5145b03aba6SOleksandr Tymoshenko 	oe = ti_gpio_read_4(sc, TI_GPIO_OE);
515e350f76cSLuiz Otavio O Souza 	if (oe & TI_GPIO_MASK(pin))
516f9de33d4SLuiz Otavio O Souza 		reg = TI_GPIO_DATAIN;
5179f165184SLuiz Otavio O Souza 	else
518f9de33d4SLuiz Otavio O Souza 		reg = TI_GPIO_DATAOUT;
5195b03aba6SOleksandr Tymoshenko 	val = ti_gpio_read_4(sc, reg);
520e350f76cSLuiz Otavio O Souza 	*value = (val & TI_GPIO_MASK(pin)) ? 1 : 0;
521e53470feSOleksandr Tymoshenko 	TI_GPIO_UNLOCK(sc);
522e53470feSOleksandr Tymoshenko 
523e53470feSOleksandr Tymoshenko 	return (0);
524e53470feSOleksandr Tymoshenko }
525e53470feSOleksandr Tymoshenko 
526e53470feSOleksandr Tymoshenko /**
527e53470feSOleksandr Tymoshenko  *	ti_gpio_pin_toggle - Toggles a given GPIO pin
528e53470feSOleksandr Tymoshenko  *	@dev: gpio device handle
529e53470feSOleksandr Tymoshenko  *	@pin: the number of the pin
530e53470feSOleksandr Tymoshenko  *
531e53470feSOleksandr Tymoshenko  *
532e53470feSOleksandr Tymoshenko  *	LOCKING:
533e53470feSOleksandr Tymoshenko  *	Internally locks the context
534e53470feSOleksandr Tymoshenko  *
535e53470feSOleksandr Tymoshenko  *	RETURNS:
536e53470feSOleksandr Tymoshenko  *	Returns 0 on success otherwise a error code
537e53470feSOleksandr Tymoshenko  */
538e53470feSOleksandr Tymoshenko static int
539e53470feSOleksandr Tymoshenko ti_gpio_pin_toggle(device_t dev, uint32_t pin)
540e53470feSOleksandr Tymoshenko {
541e350f76cSLuiz Otavio O Souza 	struct ti_gpio_softc *sc;
542f9de33d4SLuiz Otavio O Souza 	uint32_t reg, val;
543e53470feSOleksandr Tymoshenko 
544e350f76cSLuiz Otavio O Souza 	sc = device_get_softc(dev);
545e350f76cSLuiz Otavio O Souza 	if (ti_gpio_valid_pin(sc, pin) != 0)
546e53470feSOleksandr Tymoshenko 		return (EINVAL);
547e53470feSOleksandr Tymoshenko 
548e53470feSOleksandr Tymoshenko 	/* Toggle the pin */
549f9de33d4SLuiz Otavio O Souza 	TI_GPIO_LOCK(sc);
5505b03aba6SOleksandr Tymoshenko 	val = ti_gpio_read_4(sc, TI_GPIO_DATAOUT);
551e350f76cSLuiz Otavio O Souza 	if (val & TI_GPIO_MASK(pin))
552f9de33d4SLuiz Otavio O Souza 		reg = TI_GPIO_CLEARDATAOUT;
553e53470feSOleksandr Tymoshenko 	else
554f9de33d4SLuiz Otavio O Souza 		reg = TI_GPIO_SETDATAOUT;
5555b03aba6SOleksandr Tymoshenko 	ti_gpio_write_4(sc, reg, TI_GPIO_MASK(pin));
556e53470feSOleksandr Tymoshenko 	TI_GPIO_UNLOCK(sc);
557e53470feSOleksandr Tymoshenko 
558e53470feSOleksandr Tymoshenko 	return (0);
559e53470feSOleksandr Tymoshenko }
560e53470feSOleksandr Tymoshenko 
56159c3cb81SAndrew Turner #ifndef INTRNG
562e53470feSOleksandr Tymoshenko /**
563e53470feSOleksandr Tymoshenko  *	ti_gpio_intr - ISR for all GPIO modules
564e53470feSOleksandr Tymoshenko  *	@arg: the soft context pointer
565e53470feSOleksandr Tymoshenko  *
566e53470feSOleksandr Tymoshenko  *	LOCKING:
567e53470feSOleksandr Tymoshenko  *	Internally locks the context
568e53470feSOleksandr Tymoshenko  *
569e53470feSOleksandr Tymoshenko  */
5703681c8d7SLuiz Otavio O Souza static int
571e53470feSOleksandr Tymoshenko ti_gpio_intr(void *arg)
572e53470feSOleksandr Tymoshenko {
5733681c8d7SLuiz Otavio O Souza 	int bank_last, irq;
5743681c8d7SLuiz Otavio O Souza 	struct intr_event *event;
5753681c8d7SLuiz Otavio O Souza 	struct ti_gpio_softc *sc;
5763681c8d7SLuiz Otavio O Souza 	uint32_t reg;
577e53470feSOleksandr Tymoshenko 
5783681c8d7SLuiz Otavio O Souza 	sc = (struct ti_gpio_softc *)arg;
5793681c8d7SLuiz Otavio O Souza 	bank_last = -1;
5802ed434b4SIan Lepore 	reg = 0; /* squelch bogus gcc warning */
5815b03aba6SOleksandr Tymoshenko 	reg = ti_gpio_intr_status(sc);
5823681c8d7SLuiz Otavio O Souza 	for (irq = 0; irq < sc->sc_maxpin; irq++) {
5833681c8d7SLuiz Otavio O Souza 		if ((reg & TI_GPIO_MASK(irq)) == 0)
5843681c8d7SLuiz Otavio O Souza 			continue;
5853681c8d7SLuiz Otavio O Souza 		event = sc->sc_events[irq];
5863681c8d7SLuiz Otavio O Souza 		if (event != NULL && !TAILQ_EMPTY(&event->ie_handlers))
5873681c8d7SLuiz Otavio O Souza 			intr_event_handle(event, NULL);
5883681c8d7SLuiz Otavio O Souza 		else
5893681c8d7SLuiz Otavio O Souza 			device_printf(sc->sc_dev, "Stray IRQ %d\n", irq);
5903681c8d7SLuiz Otavio O Souza 		/* Ack the IRQ Status bit. */
5915b03aba6SOleksandr Tymoshenko 		ti_gpio_intr_ack(sc, TI_GPIO_MASK(irq));
5923681c8d7SLuiz Otavio O Souza 	}
5933681c8d7SLuiz Otavio O Souza 
5943681c8d7SLuiz Otavio O Souza 	return (FILTER_HANDLED);
595e53470feSOleksandr Tymoshenko }
5962df5562dSSvatopluk Kraus #endif
597e53470feSOleksandr Tymoshenko 
5989b1cba84SLuiz Otavio O Souza static int
5995b03aba6SOleksandr Tymoshenko ti_gpio_bank_init(device_t dev)
6009b1cba84SLuiz Otavio O Souza {
6011f1e8f16SLuiz Otavio O Souza 	int pin;
6029b1cba84SLuiz Otavio O Souza 	struct ti_gpio_softc *sc;
603c3321180SOleksandr Tymoshenko 	uint32_t flags, reg_oe, reg_set, rev;
6045b03aba6SOleksandr Tymoshenko 	clk_ident_t clk;
6059b1cba84SLuiz Otavio O Souza 
6069b1cba84SLuiz Otavio O Souza 	sc = device_get_softc(dev);
6079b1cba84SLuiz Otavio O Souza 
6089b1cba84SLuiz Otavio O Souza 	/* Enable the interface and functional clocks for the module. */
6095b03aba6SOleksandr Tymoshenko 	clk = ti_hwmods_get_clock(dev);
6105b03aba6SOleksandr Tymoshenko 	if (clk == INVALID_CLK_IDENT) {
6115b03aba6SOleksandr Tymoshenko 		device_printf(dev, "failed to get device id based on ti,hwmods\n");
6125b03aba6SOleksandr Tymoshenko 		return (EINVAL);
6135b03aba6SOleksandr Tymoshenko 	}
6145b03aba6SOleksandr Tymoshenko 
6155b03aba6SOleksandr Tymoshenko 	sc->sc_bank = clk - GPIO1_CLK + ti_first_gpio_bank();
6165b03aba6SOleksandr Tymoshenko 	ti_prcm_clk_enable(clk);
6179b1cba84SLuiz Otavio O Souza 
6189b1cba84SLuiz Otavio O Souza 	/*
6199b1cba84SLuiz Otavio O Souza 	 * Read the revision number of the module.  TI don't publish the
6209b1cba84SLuiz Otavio O Souza 	 * actual revision numbers, so instead the values have been
6219b1cba84SLuiz Otavio O Souza 	 * determined by experimentation.
6229b1cba84SLuiz Otavio O Souza 	 */
6235b03aba6SOleksandr Tymoshenko 	rev = ti_gpio_read_4(sc, TI_GPIO_REVISION);
6249b1cba84SLuiz Otavio O Souza 
6259b1cba84SLuiz Otavio O Souza 	/* Check the revision. */
626e350f76cSLuiz Otavio O Souza 	if (rev != ti_gpio_rev()) {
6279b1cba84SLuiz Otavio O Souza 		device_printf(dev, "Warning: could not determine the revision "
6285b03aba6SOleksandr Tymoshenko 		    "of GPIO module (revision:0x%08x)\n", rev);
6299b1cba84SLuiz Otavio O Souza 		return (EINVAL);
6309b1cba84SLuiz Otavio O Souza 	}
6319b1cba84SLuiz Otavio O Souza 
6329b1cba84SLuiz Otavio O Souza 	/* Disable interrupts for all pins. */
6335b03aba6SOleksandr Tymoshenko 	ti_gpio_intr_clr(sc, 0xffffffff);
6349b1cba84SLuiz Otavio O Souza 
6359b1cba84SLuiz Otavio O Souza 	/* Init OE register based on pads configuration. */
6369b1cba84SLuiz Otavio O Souza 	reg_oe = 0xffffffff;
637c3321180SOleksandr Tymoshenko 	reg_set = 0;
6389b1cba84SLuiz Otavio O Souza 	for (pin = 0; pin < PINS_PER_BANK; pin++) {
6395b03aba6SOleksandr Tymoshenko 		TI_GPIO_GET_FLAGS(dev, pin, &flags);
640c3321180SOleksandr Tymoshenko 		if (flags & GPIO_PIN_OUTPUT) {
6419b1cba84SLuiz Otavio O Souza 			reg_oe &= ~(1UL << pin);
642c3321180SOleksandr Tymoshenko 			if (flags & GPIO_PIN_PULLUP)
643c3321180SOleksandr Tymoshenko 				reg_set |= (1UL << pin);
644c3321180SOleksandr Tymoshenko 		}
6459b1cba84SLuiz Otavio O Souza 	}
6465b03aba6SOleksandr Tymoshenko 	ti_gpio_write_4(sc, TI_GPIO_OE, reg_oe);
647c3321180SOleksandr Tymoshenko 	if (reg_set)
648c3321180SOleksandr Tymoshenko 		ti_gpio_write_4(sc, TI_GPIO_SETDATAOUT, reg_set);
6499b1cba84SLuiz Otavio O Souza 
6509b1cba84SLuiz Otavio O Souza 	return (0);
6519b1cba84SLuiz Otavio O Souza }
6529b1cba84SLuiz Otavio O Souza 
653e53470feSOleksandr Tymoshenko /**
654e53470feSOleksandr Tymoshenko  *	ti_gpio_attach - attach function for the driver
655e53470feSOleksandr Tymoshenko  *	@dev: gpio device handle
656e53470feSOleksandr Tymoshenko  *
657e53470feSOleksandr Tymoshenko  *	Allocates and sets up the driver context for all GPIO banks.  This function
658e53470feSOleksandr Tymoshenko  *	expects the memory ranges and IRQs to already be allocated to the driver.
659e53470feSOleksandr Tymoshenko  *
660e53470feSOleksandr Tymoshenko  *	LOCKING:
661e53470feSOleksandr Tymoshenko  *	None
662e53470feSOleksandr Tymoshenko  *
663e53470feSOleksandr Tymoshenko  *	RETURNS:
664e53470feSOleksandr Tymoshenko  *	Always returns 0
665e53470feSOleksandr Tymoshenko  */
666e53470feSOleksandr Tymoshenko static int
667e53470feSOleksandr Tymoshenko ti_gpio_attach(device_t dev)
668e53470feSOleksandr Tymoshenko {
6699b1cba84SLuiz Otavio O Souza 	struct ti_gpio_softc *sc;
67059c3cb81SAndrew Turner #ifndef INTRNG
671e53470feSOleksandr Tymoshenko 	unsigned int i;
6722df5562dSSvatopluk Kraus #endif
6739b1cba84SLuiz Otavio O Souza 	int err;
674e53470feSOleksandr Tymoshenko 
6755b03aba6SOleksandr Tymoshenko 	sc = device_get_softc(dev);
676e53470feSOleksandr Tymoshenko 	sc->sc_dev = dev;
677e53470feSOleksandr Tymoshenko 	TI_GPIO_LOCK_INIT(sc);
678e350f76cSLuiz Otavio O Souza 	ti_gpio_pin_max(dev, &sc->sc_maxpin);
679f7f77280SLuiz Otavio O Souza 	sc->sc_maxpin++;
680e53470feSOleksandr Tymoshenko 
6815b03aba6SOleksandr Tymoshenko 	sc->sc_mem_rid = 0;
6825b03aba6SOleksandr Tymoshenko 	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
6835b03aba6SOleksandr Tymoshenko 	    &sc->sc_mem_rid, RF_ACTIVE);
6845b03aba6SOleksandr Tymoshenko 	if (!sc->sc_mem_res) {
685e53470feSOleksandr Tymoshenko 		device_printf(dev, "Error: could not allocate mem resources\n");
686876c1bd8SLuiz Otavio O Souza 		ti_gpio_detach(dev);
687e53470feSOleksandr Tymoshenko 		return (ENXIO);
688e53470feSOleksandr Tymoshenko 	}
689e53470feSOleksandr Tymoshenko 
6905b03aba6SOleksandr Tymoshenko 	sc->sc_irq_rid = 0;
6915b03aba6SOleksandr Tymoshenko 	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
6925b03aba6SOleksandr Tymoshenko 	    &sc->sc_irq_rid, RF_ACTIVE);
6935b03aba6SOleksandr Tymoshenko 	if (!sc->sc_irq_res) {
694e53470feSOleksandr Tymoshenko 		device_printf(dev, "Error: could not allocate irq resources\n");
695876c1bd8SLuiz Otavio O Souza 		ti_gpio_detach(dev);
696e53470feSOleksandr Tymoshenko 		return (ENXIO);
697e53470feSOleksandr Tymoshenko 	}
698e53470feSOleksandr Tymoshenko 
6995b03aba6SOleksandr Tymoshenko 	/*
7005b03aba6SOleksandr Tymoshenko 	 * Register our interrupt filter for each of the IRQ resources.
7015b03aba6SOleksandr Tymoshenko 	 */
7025b03aba6SOleksandr Tymoshenko 	if (bus_setup_intr(dev, sc->sc_irq_res,
7035b03aba6SOleksandr Tymoshenko 	    INTR_TYPE_MISC | INTR_MPSAFE, ti_gpio_intr, NULL, sc,
7045b03aba6SOleksandr Tymoshenko 	    &sc->sc_irq_hdl) != 0) {
7055b03aba6SOleksandr Tymoshenko 		device_printf(dev,
7065b03aba6SOleksandr Tymoshenko 		    "WARNING: unable to register interrupt filter\n");
707876c1bd8SLuiz Otavio O Souza 		ti_gpio_detach(dev);
708e53470feSOleksandr Tymoshenko 		return (ENXIO);
709e53470feSOleksandr Tymoshenko 	}
710e53470feSOleksandr Tymoshenko 
71159c3cb81SAndrew Turner #ifdef INTRNG
7122df5562dSSvatopluk Kraus 	if (ti_gpio_pic_attach(sc) != 0) {
7132df5562dSSvatopluk Kraus 		device_printf(dev, "WARNING: unable to attach PIC\n");
7142df5562dSSvatopluk Kraus 		ti_gpio_detach(dev);
7152df5562dSSvatopluk Kraus 		return (ENXIO);
7162df5562dSSvatopluk Kraus 	}
7172df5562dSSvatopluk Kraus #else
7183681c8d7SLuiz Otavio O Souza 	/*
7193681c8d7SLuiz Otavio O Souza 	 * Initialize the interrupt settings.  The default is active-low
7203681c8d7SLuiz Otavio O Souza 	 * interrupts.
7213681c8d7SLuiz Otavio O Souza 	 */
7223681c8d7SLuiz Otavio O Souza 	sc->sc_irq_trigger = malloc(
7233681c8d7SLuiz Otavio O Souza 	    sizeof(*sc->sc_irq_trigger) * sc->sc_maxpin,
7243681c8d7SLuiz Otavio O Souza 	    M_DEVBUF, M_WAITOK | M_ZERO);
7253681c8d7SLuiz Otavio O Souza 	sc->sc_irq_polarity = malloc(
7263681c8d7SLuiz Otavio O Souza 	    sizeof(*sc->sc_irq_polarity) * sc->sc_maxpin,
7273681c8d7SLuiz Otavio O Souza 	    M_DEVBUF, M_WAITOK | M_ZERO);
7283681c8d7SLuiz Otavio O Souza 	for (i = 0; i < sc->sc_maxpin; i++) {
7293681c8d7SLuiz Otavio O Souza 		sc->sc_irq_trigger[i] = INTR_TRIGGER_LEVEL;
7303681c8d7SLuiz Otavio O Souza 		sc->sc_irq_polarity[i] = INTR_POLARITY_LOW;
7313681c8d7SLuiz Otavio O Souza 	}
7323681c8d7SLuiz Otavio O Souza 
7333681c8d7SLuiz Otavio O Souza 	sc->sc_events = malloc(sizeof(struct intr_event *) * sc->sc_maxpin,
7343681c8d7SLuiz Otavio O Souza 	    M_DEVBUF, M_WAITOK | M_ZERO);
7353681c8d7SLuiz Otavio O Souza 
7365b03aba6SOleksandr Tymoshenko 	sc->sc_mask_args = malloc(sizeof(struct ti_gpio_mask_arg) * sc->sc_maxpin,
7375b03aba6SOleksandr Tymoshenko 	    M_DEVBUF, M_WAITOK | M_ZERO);
7382df5562dSSvatopluk Kraus #endif
739e53470feSOleksandr Tymoshenko 	/* We need to go through each block and ensure the clocks are running and
740e53470feSOleksandr Tymoshenko 	 * the module is enabled.  It might be better to do this only when the
741e53470feSOleksandr Tymoshenko 	 * pins are configured which would result in less power used if the GPIO
742e53470feSOleksandr Tymoshenko 	 * pins weren't used ...
743e53470feSOleksandr Tymoshenko 	 */
7445b03aba6SOleksandr Tymoshenko 	if (sc->sc_mem_res != NULL) {
7451f1e8f16SLuiz Otavio O Souza 		/* Initialize the GPIO module. */
7465b03aba6SOleksandr Tymoshenko 		err = ti_gpio_bank_init(dev);
7479b1cba84SLuiz Otavio O Souza 		if (err != 0) {
748876c1bd8SLuiz Otavio O Souza 			ti_gpio_detach(dev);
7499b1cba84SLuiz Otavio O Souza 			return (err);
750e53470feSOleksandr Tymoshenko 		}
751e53470feSOleksandr Tymoshenko 	}
7525b03aba6SOleksandr Tymoshenko 
7537836352bSLuiz Otavio O Souza 	sc->sc_busdev = gpiobus_attach_bus(dev);
7547836352bSLuiz Otavio O Souza 	if (sc->sc_busdev == NULL) {
7557836352bSLuiz Otavio O Souza 		ti_gpio_detach(dev);
7567836352bSLuiz Otavio O Souza 		return (ENXIO);
7577836352bSLuiz Otavio O Souza 	}
758e53470feSOleksandr Tymoshenko 
7597836352bSLuiz Otavio O Souza 	return (0);
760e53470feSOleksandr Tymoshenko }
761e53470feSOleksandr Tymoshenko 
762e53470feSOleksandr Tymoshenko /**
763e53470feSOleksandr Tymoshenko  *	ti_gpio_detach - detach function for the driver
764e53470feSOleksandr Tymoshenko  *	@dev: scm device handle
765e53470feSOleksandr Tymoshenko  *
766e53470feSOleksandr Tymoshenko  *	Allocates and sets up the driver context, this simply entails creating a
767e53470feSOleksandr Tymoshenko  *	bus mappings for the SCM register set.
768e53470feSOleksandr Tymoshenko  *
769e53470feSOleksandr Tymoshenko  *	LOCKING:
770e53470feSOleksandr Tymoshenko  *	None
771e53470feSOleksandr Tymoshenko  *
772e53470feSOleksandr Tymoshenko  *	RETURNS:
773e53470feSOleksandr Tymoshenko  *	Always returns 0
774e53470feSOleksandr Tymoshenko  */
775e53470feSOleksandr Tymoshenko static int
776e53470feSOleksandr Tymoshenko ti_gpio_detach(device_t dev)
777e53470feSOleksandr Tymoshenko {
778e53470feSOleksandr Tymoshenko 	struct ti_gpio_softc *sc = device_get_softc(dev);
779e53470feSOleksandr Tymoshenko 
780e53470feSOleksandr Tymoshenko 	KASSERT(mtx_initialized(&sc->sc_mtx), ("gpio mutex not initialized"));
781e53470feSOleksandr Tymoshenko 
782e53470feSOleksandr Tymoshenko 	/* Disable all interrupts */
7835b03aba6SOleksandr Tymoshenko 	if (sc->sc_mem_res != NULL)
7845b03aba6SOleksandr Tymoshenko 		ti_gpio_intr_clr(sc, 0xffffffff);
7857836352bSLuiz Otavio O Souza 	gpiobus_detach_bus(dev);
78659c3cb81SAndrew Turner #ifdef	INTRNG
7872df5562dSSvatopluk Kraus 	if (sc->sc_isrcs != NULL)
7882df5562dSSvatopluk Kraus 		ti_gpio_pic_detach(sc);
7892df5562dSSvatopluk Kraus #else
790876c1bd8SLuiz Otavio O Souza 	if (sc->sc_events)
7913681c8d7SLuiz Otavio O Souza 		free(sc->sc_events, M_DEVBUF);
7925b03aba6SOleksandr Tymoshenko 	if (sc->sc_mask_args)
7935b03aba6SOleksandr Tymoshenko 		free(sc->sc_mask_args, M_DEVBUF);
794876c1bd8SLuiz Otavio O Souza 	if (sc->sc_irq_polarity)
7953681c8d7SLuiz Otavio O Souza 		free(sc->sc_irq_polarity, M_DEVBUF);
796876c1bd8SLuiz Otavio O Souza 	if (sc->sc_irq_trigger)
7973681c8d7SLuiz Otavio O Souza 		free(sc->sc_irq_trigger, M_DEVBUF);
7982df5562dSSvatopluk Kraus #endif
7999b1cba84SLuiz Otavio O Souza 	/* Release the memory and IRQ resources. */
8005b03aba6SOleksandr Tymoshenko 	if (sc->sc_irq_hdl) {
8015b03aba6SOleksandr Tymoshenko 		bus_teardown_intr(dev, sc->sc_irq_res,
8025b03aba6SOleksandr Tymoshenko 		    sc->sc_irq_hdl);
8035b03aba6SOleksandr Tymoshenko 	}
8045b03aba6SOleksandr Tymoshenko 	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid,
8055b03aba6SOleksandr Tymoshenko 	    sc->sc_irq_res);
8065b03aba6SOleksandr Tymoshenko 	bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
8075b03aba6SOleksandr Tymoshenko 	    sc->sc_mem_res);
808e53470feSOleksandr Tymoshenko 	TI_GPIO_LOCK_DESTROY(sc);
809e53470feSOleksandr Tymoshenko 
810e53470feSOleksandr Tymoshenko 	return (0);
811e53470feSOleksandr Tymoshenko }
812e53470feSOleksandr Tymoshenko 
81359c3cb81SAndrew Turner #ifdef INTRNG
8142df5562dSSvatopluk Kraus static inline void
8152202c379SSvatopluk Kraus ti_gpio_rwreg_modify(struct ti_gpio_softc *sc, uint32_t reg, uint32_t mask,
8162202c379SSvatopluk Kraus     bool set_bits)
8172df5562dSSvatopluk Kraus {
8182202c379SSvatopluk Kraus 	uint32_t value;
8192df5562dSSvatopluk Kraus 
8202202c379SSvatopluk Kraus 	value = ti_gpio_read_4(sc, reg);
8212202c379SSvatopluk Kraus 	ti_gpio_write_4(sc, reg, set_bits ? value | mask : value & ~mask);
8222df5562dSSvatopluk Kraus }
8232df5562dSSvatopluk Kraus 
8242df5562dSSvatopluk Kraus static inline void
8252df5562dSSvatopluk Kraus ti_gpio_isrc_mask(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi)
8262df5562dSSvatopluk Kraus {
8272df5562dSSvatopluk Kraus 
8282df5562dSSvatopluk Kraus 	/* Writing a 0 has no effect. */
8292df5562dSSvatopluk Kraus 	ti_gpio_intr_clr(sc, tgi->tgi_mask);
8302df5562dSSvatopluk Kraus }
8312df5562dSSvatopluk Kraus 
8322df5562dSSvatopluk Kraus static inline void
8332df5562dSSvatopluk Kraus ti_gpio_isrc_unmask(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi)
8342df5562dSSvatopluk Kraus {
8352df5562dSSvatopluk Kraus 
8362df5562dSSvatopluk Kraus 	/* Writing a 0 has no effect. */
8372df5562dSSvatopluk Kraus 	ti_gpio_intr_set(sc, tgi->tgi_mask);
8382df5562dSSvatopluk Kraus }
8392df5562dSSvatopluk Kraus 
8402df5562dSSvatopluk Kraus static inline void
8412df5562dSSvatopluk Kraus ti_gpio_isrc_eoi(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi)
8422df5562dSSvatopluk Kraus {
8432df5562dSSvatopluk Kraus 
8442df5562dSSvatopluk Kraus 	/* Writing a 0 has no effect. */
8452df5562dSSvatopluk Kraus 	ti_gpio_intr_ack(sc, tgi->tgi_mask);
8462df5562dSSvatopluk Kraus }
8472df5562dSSvatopluk Kraus 
8482df5562dSSvatopluk Kraus static inline bool
8492df5562dSSvatopluk Kraus ti_gpio_isrc_is_level(struct ti_gpio_irqsrc *tgi)
8502df5562dSSvatopluk Kraus {
8512df5562dSSvatopluk Kraus 
8522202c379SSvatopluk Kraus 	return (tgi->tgi_mode == GPIO_INTR_LEVEL_LOW ||
8532202c379SSvatopluk Kraus 	    tgi->tgi_mode == GPIO_INTR_LEVEL_HIGH);
8542df5562dSSvatopluk Kraus }
8552df5562dSSvatopluk Kraus 
8562df5562dSSvatopluk Kraus static int
8572df5562dSSvatopluk Kraus ti_gpio_intr(void *arg)
8582df5562dSSvatopluk Kraus {
8592df5562dSSvatopluk Kraus 	u_int irq;
8602df5562dSSvatopluk Kraus 	uint32_t reg;
8612df5562dSSvatopluk Kraus 	struct ti_gpio_softc *sc;
8622df5562dSSvatopluk Kraus 	struct trapframe *tf;
8632df5562dSSvatopluk Kraus 	struct ti_gpio_irqsrc *tgi;
8642df5562dSSvatopluk Kraus 
8652df5562dSSvatopluk Kraus 	sc = (struct ti_gpio_softc *)arg;
8662df5562dSSvatopluk Kraus 	tf = curthread->td_intr_frame;
8672df5562dSSvatopluk Kraus 
8682df5562dSSvatopluk Kraus 	reg = ti_gpio_intr_status(sc);
8692df5562dSSvatopluk Kraus 	for (irq = 0; irq < sc->sc_maxpin; irq++) {
8702df5562dSSvatopluk Kraus 		tgi = &sc->sc_isrcs[irq];
8712df5562dSSvatopluk Kraus 		if ((reg & tgi->tgi_mask) == 0)
8722df5562dSSvatopluk Kraus 			continue;
8732df5562dSSvatopluk Kraus 		if (!ti_gpio_isrc_is_level(tgi))
8742df5562dSSvatopluk Kraus 			ti_gpio_isrc_eoi(sc, tgi);
8752df5562dSSvatopluk Kraus 		if (intr_isrc_dispatch(&tgi->tgi_isrc, tf) != 0) {
8762df5562dSSvatopluk Kraus 			ti_gpio_isrc_mask(sc, tgi);
8772df5562dSSvatopluk Kraus 			if (ti_gpio_isrc_is_level(tgi))
8782df5562dSSvatopluk Kraus 				ti_gpio_isrc_eoi(sc, tgi);
8792df5562dSSvatopluk Kraus 			device_printf(sc->sc_dev, "Stray irq %u disabled\n",
8802df5562dSSvatopluk Kraus 			    irq);
8812df5562dSSvatopluk Kraus 		}
8822df5562dSSvatopluk Kraus 	}
8832df5562dSSvatopluk Kraus 	return (FILTER_HANDLED);
8842df5562dSSvatopluk Kraus }
8852df5562dSSvatopluk Kraus 
8862df5562dSSvatopluk Kraus static int
8872df5562dSSvatopluk Kraus ti_gpio_pic_attach(struct ti_gpio_softc *sc)
8882df5562dSSvatopluk Kraus {
8892df5562dSSvatopluk Kraus 	int error;
8902df5562dSSvatopluk Kraus 	uint32_t irq;
8912df5562dSSvatopluk Kraus 	const char *name;
8922df5562dSSvatopluk Kraus 
8932df5562dSSvatopluk Kraus 	sc->sc_isrcs = malloc(sizeof(*sc->sc_isrcs) * sc->sc_maxpin, M_DEVBUF,
8942df5562dSSvatopluk Kraus 	    M_WAITOK | M_ZERO);
8952df5562dSSvatopluk Kraus 
8962df5562dSSvatopluk Kraus 	name = device_get_nameunit(sc->sc_dev);
8972df5562dSSvatopluk Kraus 	for (irq = 0; irq < sc->sc_maxpin; irq++) {
8982df5562dSSvatopluk Kraus 		sc->sc_isrcs[irq].tgi_irq = irq;
8992df5562dSSvatopluk Kraus 		sc->sc_isrcs[irq].tgi_mask = TI_GPIO_MASK(irq);
9002202c379SSvatopluk Kraus 		sc->sc_isrcs[irq].tgi_mode = GPIO_INTR_CONFORM;
9012df5562dSSvatopluk Kraus 
9022df5562dSSvatopluk Kraus 		error = intr_isrc_register(&sc->sc_isrcs[irq].tgi_isrc,
9032df5562dSSvatopluk Kraus 		    sc->sc_dev, 0, "%s,%u", name, irq);
9042df5562dSSvatopluk Kraus 		if (error != 0)
9052df5562dSSvatopluk Kraus 			return (error); /* XXX deregister ISRCs */
9062df5562dSSvatopluk Kraus 	}
9072df5562dSSvatopluk Kraus 	return (intr_pic_register(sc->sc_dev,
9082df5562dSSvatopluk Kraus 	    OF_xref_from_node(ofw_bus_get_node(sc->sc_dev))));
9092df5562dSSvatopluk Kraus }
9102df5562dSSvatopluk Kraus 
9112df5562dSSvatopluk Kraus static int
9122df5562dSSvatopluk Kraus ti_gpio_pic_detach(struct ti_gpio_softc *sc)
9132df5562dSSvatopluk Kraus {
9142df5562dSSvatopluk Kraus 
9152df5562dSSvatopluk Kraus 	/*
9162df5562dSSvatopluk Kraus 	 *  There has not been established any procedure yet
9172df5562dSSvatopluk Kraus 	 *  how to detach PIC from living system correctly.
9182df5562dSSvatopluk Kraus 	 */
9192df5562dSSvatopluk Kraus 	device_printf(sc->sc_dev, "%s: not implemented yet\n", __func__);
9202df5562dSSvatopluk Kraus 	return (EBUSY);
9212df5562dSSvatopluk Kraus }
9222df5562dSSvatopluk Kraus 
9232df5562dSSvatopluk Kraus static void
9242202c379SSvatopluk Kraus ti_gpio_pic_config_intr(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi,
9252202c379SSvatopluk Kraus     uint32_t mode)
9262202c379SSvatopluk Kraus {
9272202c379SSvatopluk Kraus 
9282202c379SSvatopluk Kraus 	TI_GPIO_LOCK(sc);
9292202c379SSvatopluk Kraus 	ti_gpio_rwreg_modify(sc, TI_GPIO_RISINGDETECT, tgi->tgi_mask,
9302202c379SSvatopluk Kraus 	    mode == GPIO_INTR_EDGE_RISING || mode == GPIO_INTR_EDGE_BOTH);
9312202c379SSvatopluk Kraus 	ti_gpio_rwreg_modify(sc, TI_GPIO_FALLINGDETECT, tgi->tgi_mask,
9322202c379SSvatopluk Kraus 	    mode == GPIO_INTR_EDGE_FALLING || mode == GPIO_INTR_EDGE_BOTH);
9332202c379SSvatopluk Kraus 	ti_gpio_rwreg_modify(sc, TI_GPIO_LEVELDETECT1, tgi->tgi_mask,
9342202c379SSvatopluk Kraus 	    mode == GPIO_INTR_LEVEL_HIGH);
9352202c379SSvatopluk Kraus 	ti_gpio_rwreg_modify(sc, TI_GPIO_LEVELDETECT0, tgi->tgi_mask,
9362202c379SSvatopluk Kraus 	    mode == GPIO_INTR_LEVEL_LOW);
9372202c379SSvatopluk Kraus 	tgi->tgi_mode = mode;
9382202c379SSvatopluk Kraus 	TI_GPIO_UNLOCK(sc);
9392202c379SSvatopluk Kraus }
9402202c379SSvatopluk Kraus 
9412202c379SSvatopluk Kraus static void
9422df5562dSSvatopluk Kraus ti_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
9432df5562dSSvatopluk Kraus {
9442df5562dSSvatopluk Kraus 	struct ti_gpio_softc *sc = device_get_softc(dev);
9452df5562dSSvatopluk Kraus 	struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc;
9462df5562dSSvatopluk Kraus 
9472df5562dSSvatopluk Kraus 	ti_gpio_isrc_mask(sc, tgi);
9482df5562dSSvatopluk Kraus }
9492df5562dSSvatopluk Kraus 
9502df5562dSSvatopluk Kraus static void
9512df5562dSSvatopluk Kraus ti_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
9522df5562dSSvatopluk Kraus {
9532df5562dSSvatopluk Kraus 	struct ti_gpio_softc *sc = device_get_softc(dev);
9542df5562dSSvatopluk Kraus 	struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc;
9552df5562dSSvatopluk Kraus 
9562df5562dSSvatopluk Kraus 	arm_irq_memory_barrier(tgi->tgi_irq);
9572df5562dSSvatopluk Kraus 	ti_gpio_isrc_unmask(sc, tgi);
9582df5562dSSvatopluk Kraus }
9592df5562dSSvatopluk Kraus 
9602df5562dSSvatopluk Kraus static int
9611a251c53SSvatopluk Kraus ti_gpio_pic_map_fdt(struct ti_gpio_softc *sc, struct intr_map_data_fdt *daf,
9622202c379SSvatopluk Kraus     u_int *irqp, uint32_t *modep)
9632df5562dSSvatopluk Kraus {
9642202c379SSvatopluk Kraus 	uint32_t mode;
9652df5562dSSvatopluk Kraus 
9662df5562dSSvatopluk Kraus 	/*
9672df5562dSSvatopluk Kraus 	 * The first cell is the interrupt number.
9682df5562dSSvatopluk Kraus 	 * The second cell is used to specify flags:
9692df5562dSSvatopluk Kraus 	 *	bits[3:0] trigger type and level flags:
9702df5562dSSvatopluk Kraus 	 *		1 = low-to-high edge triggered.
9712df5562dSSvatopluk Kraus 	 *		2 = high-to-low edge triggered.
9722df5562dSSvatopluk Kraus 	 *		4 = active high level-sensitive.
9732df5562dSSvatopluk Kraus 	 *		8 = active low level-sensitive.
9742df5562dSSvatopluk Kraus 	 */
9751a251c53SSvatopluk Kraus 	if (daf->ncells != 2 || daf->cells[0] >= sc->sc_maxpin)
9762df5562dSSvatopluk Kraus 		return (EINVAL);
9772df5562dSSvatopluk Kraus 
9782202c379SSvatopluk Kraus 	/* Only reasonable modes are supported. */
9791a251c53SSvatopluk Kraus 	if (daf->cells[1] == 1)
9802202c379SSvatopluk Kraus 		mode = GPIO_INTR_EDGE_RISING;
9811a251c53SSvatopluk Kraus 	else if (daf->cells[1] == 2)
9822202c379SSvatopluk Kraus 		mode = GPIO_INTR_EDGE_FALLING;
9831a251c53SSvatopluk Kraus 	else if (daf->cells[1] == 3)
9842202c379SSvatopluk Kraus 		mode = GPIO_INTR_EDGE_BOTH;
9851a251c53SSvatopluk Kraus 	else if (daf->cells[1] == 4)
9862202c379SSvatopluk Kraus 		mode = GPIO_INTR_LEVEL_HIGH;
9871a251c53SSvatopluk Kraus 	else if (daf->cells[1] == 8)
9882202c379SSvatopluk Kraus 		mode = GPIO_INTR_LEVEL_LOW;
9892df5562dSSvatopluk Kraus 	else
9902df5562dSSvatopluk Kraus 		return (EINVAL);
9912df5562dSSvatopluk Kraus 
9921a251c53SSvatopluk Kraus 	*irqp = daf->cells[0];
9932202c379SSvatopluk Kraus 	if (modep != NULL)
9942202c379SSvatopluk Kraus 		*modep = mode;
9952df5562dSSvatopluk Kraus 	return (0);
9962df5562dSSvatopluk Kraus }
9972df5562dSSvatopluk Kraus 
9982df5562dSSvatopluk Kraus static int
9991a251c53SSvatopluk Kraus ti_gpio_pic_map_gpio(struct ti_gpio_softc *sc, struct intr_map_data_gpio *dag,
10001a251c53SSvatopluk Kraus     u_int *irqp, uint32_t *modep)
10011a251c53SSvatopluk Kraus {
10021a251c53SSvatopluk Kraus 	uint32_t mode;
10031a251c53SSvatopluk Kraus 
10041a251c53SSvatopluk Kraus 	if (dag->gpio_pin_num >= sc->sc_maxpin)
10051a251c53SSvatopluk Kraus 		return (EINVAL);
10061a251c53SSvatopluk Kraus 
10071a251c53SSvatopluk Kraus 	mode = dag->gpio_intr_mode;
10081a251c53SSvatopluk Kraus 	if (mode != GPIO_INTR_LEVEL_LOW && mode != GPIO_INTR_LEVEL_HIGH &&
10091a251c53SSvatopluk Kraus 	    mode != GPIO_INTR_EDGE_RISING && mode != GPIO_INTR_EDGE_FALLING &&
10101a251c53SSvatopluk Kraus 	    mode != GPIO_INTR_EDGE_BOTH)
10111a251c53SSvatopluk Kraus 		return (EINVAL);
10121a251c53SSvatopluk Kraus 
10131a251c53SSvatopluk Kraus 	*irqp = dag->gpio_pin_num;
10141a251c53SSvatopluk Kraus 	if (modep != NULL)
10151a251c53SSvatopluk Kraus 		*modep = mode;
10161a251c53SSvatopluk Kraus 	return (0);
10171a251c53SSvatopluk Kraus }
10181a251c53SSvatopluk Kraus 
10191a251c53SSvatopluk Kraus static int
10201a251c53SSvatopluk Kraus ti_gpio_pic_map(struct ti_gpio_softc *sc, struct intr_map_data *data,
10211a251c53SSvatopluk Kraus     u_int *irqp, uint32_t *modep)
10221a251c53SSvatopluk Kraus {
10231a251c53SSvatopluk Kraus 
10241a251c53SSvatopluk Kraus 	switch (data->type) {
10251a251c53SSvatopluk Kraus 	case INTR_MAP_DATA_FDT:
10261a251c53SSvatopluk Kraus 		return (ti_gpio_pic_map_fdt(sc,
10271a251c53SSvatopluk Kraus 		    (struct intr_map_data_fdt *)data, irqp, modep));
10281a251c53SSvatopluk Kraus 	case INTR_MAP_DATA_GPIO:
10291a251c53SSvatopluk Kraus 		return (ti_gpio_pic_map_gpio(sc,
10301a251c53SSvatopluk Kraus 		    (struct intr_map_data_gpio *)data, irqp, modep));
10311a251c53SSvatopluk Kraus 	default:
10321a251c53SSvatopluk Kraus 		return (ENOTSUP);
10331a251c53SSvatopluk Kraus 	}
10341a251c53SSvatopluk Kraus }
10351a251c53SSvatopluk Kraus 
10361a251c53SSvatopluk Kraus static int
10372df5562dSSvatopluk Kraus ti_gpio_pic_map_intr(device_t dev, struct intr_map_data *data,
10382df5562dSSvatopluk Kraus     struct intr_irqsrc **isrcp)
10392df5562dSSvatopluk Kraus {
10402df5562dSSvatopluk Kraus 	int error;
10412df5562dSSvatopluk Kraus 	u_int irq;
10421a251c53SSvatopluk Kraus 	struct ti_gpio_softc *sc = device_get_softc(dev);
10432df5562dSSvatopluk Kraus 
10441a251c53SSvatopluk Kraus 	error = ti_gpio_pic_map(sc, data, &irq, NULL);
10452df5562dSSvatopluk Kraus 	if (error == 0)
10462df5562dSSvatopluk Kraus 		*isrcp = &sc->sc_isrcs[irq].tgi_isrc;
10472df5562dSSvatopluk Kraus 	return (error);
10482df5562dSSvatopluk Kraus }
10492df5562dSSvatopluk Kraus 
10502df5562dSSvatopluk Kraus static void
10512df5562dSSvatopluk Kraus ti_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
10522df5562dSSvatopluk Kraus {
10532df5562dSSvatopluk Kraus 	struct ti_gpio_softc *sc = device_get_softc(dev);
10542df5562dSSvatopluk Kraus 	struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc;
10552df5562dSSvatopluk Kraus 
10562df5562dSSvatopluk Kraus 	if (ti_gpio_isrc_is_level(tgi))
10572df5562dSSvatopluk Kraus 		ti_gpio_isrc_eoi(sc, tgi);
10582df5562dSSvatopluk Kraus }
10592df5562dSSvatopluk Kraus 
10602df5562dSSvatopluk Kraus static void
10612df5562dSSvatopluk Kraus ti_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
10622df5562dSSvatopluk Kraus {
10632df5562dSSvatopluk Kraus 
10642df5562dSSvatopluk Kraus 	ti_gpio_pic_enable_intr(dev, isrc);
10652df5562dSSvatopluk Kraus }
10662df5562dSSvatopluk Kraus 
10672df5562dSSvatopluk Kraus static void
10682df5562dSSvatopluk Kraus ti_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
10692df5562dSSvatopluk Kraus {
10702df5562dSSvatopluk Kraus 	struct ti_gpio_softc *sc = device_get_softc(dev);
10712df5562dSSvatopluk Kraus 	struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc;
10722df5562dSSvatopluk Kraus 
10732df5562dSSvatopluk Kraus 	ti_gpio_isrc_mask(sc, tgi);
10742df5562dSSvatopluk Kraus 	if (ti_gpio_isrc_is_level(tgi))
10752df5562dSSvatopluk Kraus 		ti_gpio_isrc_eoi(sc, tgi);
10762df5562dSSvatopluk Kraus }
10772df5562dSSvatopluk Kraus 
10782df5562dSSvatopluk Kraus static int
10792df5562dSSvatopluk Kraus ti_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
10802df5562dSSvatopluk Kraus     struct resource *res, struct intr_map_data *data)
10812df5562dSSvatopluk Kraus {
10822df5562dSSvatopluk Kraus 	u_int irq;
10832202c379SSvatopluk Kraus 	uint32_t mode;
10842df5562dSSvatopluk Kraus 	struct ti_gpio_softc *sc;
10852df5562dSSvatopluk Kraus 	struct ti_gpio_irqsrc *tgi;
10862df5562dSSvatopluk Kraus 
10871a251c53SSvatopluk Kraus 	if (data == NULL)
10882df5562dSSvatopluk Kraus 		return (ENOTSUP);
10892df5562dSSvatopluk Kraus 
10902df5562dSSvatopluk Kraus 	sc = device_get_softc(dev);
10912df5562dSSvatopluk Kraus 	tgi = (struct ti_gpio_irqsrc *)isrc;
10922df5562dSSvatopluk Kraus 
10932df5562dSSvatopluk Kraus 	/* Get and check config for an interrupt. */
10941a251c53SSvatopluk Kraus 	if (ti_gpio_pic_map(sc, data, &irq, &mode) != 0 || tgi->tgi_irq != irq)
10952df5562dSSvatopluk Kraus 		return (EINVAL);
10962df5562dSSvatopluk Kraus 
10972df5562dSSvatopluk Kraus 	/*
10982df5562dSSvatopluk Kraus 	 * If this is a setup for another handler,
10992df5562dSSvatopluk Kraus 	 * only check that its configuration match.
11002df5562dSSvatopluk Kraus 	 */
11012df5562dSSvatopluk Kraus 	if (isrc->isrc_handlers != 0)
11022202c379SSvatopluk Kraus 		return (tgi->tgi_mode == mode ? 0 : EINVAL);
11032df5562dSSvatopluk Kraus 
11042202c379SSvatopluk Kraus 	ti_gpio_pic_config_intr(sc, tgi, mode);
11052df5562dSSvatopluk Kraus 	return (0);
11062df5562dSSvatopluk Kraus }
11072df5562dSSvatopluk Kraus 
11082df5562dSSvatopluk Kraus static int
11092df5562dSSvatopluk Kraus ti_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
11102df5562dSSvatopluk Kraus     struct resource *res, struct intr_map_data *data)
11112df5562dSSvatopluk Kraus {
11122df5562dSSvatopluk Kraus 	struct ti_gpio_softc *sc = device_get_softc(dev);
11132df5562dSSvatopluk Kraus 	struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc;
11142df5562dSSvatopluk Kraus 
11152202c379SSvatopluk Kraus 	if (isrc->isrc_handlers == 0)
11162202c379SSvatopluk Kraus 		ti_gpio_pic_config_intr(sc, tgi, GPIO_INTR_CONFORM);
11172df5562dSSvatopluk Kraus 	return (0);
11182df5562dSSvatopluk Kraus }
11192df5562dSSvatopluk Kraus 
11202df5562dSSvatopluk Kraus #else
11213681c8d7SLuiz Otavio O Souza static uint32_t
11223681c8d7SLuiz Otavio O Souza ti_gpio_intr_reg(struct ti_gpio_softc *sc, int irq)
11233681c8d7SLuiz Otavio O Souza {
11243681c8d7SLuiz Otavio O Souza 
11253681c8d7SLuiz Otavio O Souza 	if (ti_gpio_valid_pin(sc, irq) != 0)
11263681c8d7SLuiz Otavio O Souza 		return (0);
11273681c8d7SLuiz Otavio O Souza 
11283681c8d7SLuiz Otavio O Souza 	if (sc->sc_irq_trigger[irq] == INTR_TRIGGER_LEVEL) {
11293681c8d7SLuiz Otavio O Souza 		if (sc->sc_irq_polarity[irq] == INTR_POLARITY_LOW)
11303681c8d7SLuiz Otavio O Souza 			return (TI_GPIO_LEVELDETECT0);
11313681c8d7SLuiz Otavio O Souza 		else if (sc->sc_irq_polarity[irq] == INTR_POLARITY_HIGH)
11323681c8d7SLuiz Otavio O Souza 			return (TI_GPIO_LEVELDETECT1);
11333681c8d7SLuiz Otavio O Souza 	} else if (sc->sc_irq_trigger[irq] == INTR_TRIGGER_EDGE) {
11343681c8d7SLuiz Otavio O Souza 		if (sc->sc_irq_polarity[irq] == INTR_POLARITY_LOW)
11353681c8d7SLuiz Otavio O Souza 			return (TI_GPIO_FALLINGDETECT);
11363681c8d7SLuiz Otavio O Souza 		else if (sc->sc_irq_polarity[irq] == INTR_POLARITY_HIGH)
11373681c8d7SLuiz Otavio O Souza 			return (TI_GPIO_RISINGDETECT);
11383681c8d7SLuiz Otavio O Souza 	}
11393681c8d7SLuiz Otavio O Souza 
11403681c8d7SLuiz Otavio O Souza 	return (0);
11413681c8d7SLuiz Otavio O Souza }
11423681c8d7SLuiz Otavio O Souza 
11433681c8d7SLuiz Otavio O Souza static void
11445b03aba6SOleksandr Tymoshenko ti_gpio_mask_irq_internal(struct ti_gpio_softc *sc, int irq)
11453681c8d7SLuiz Otavio O Souza {
11463681c8d7SLuiz Otavio O Souza 	uint32_t reg, val;
11473681c8d7SLuiz Otavio O Souza 
11485b03aba6SOleksandr Tymoshenko 	if (ti_gpio_valid_pin(sc, irq) != 0)
11493681c8d7SLuiz Otavio O Souza 		return;
11503681c8d7SLuiz Otavio O Souza 
11515b03aba6SOleksandr Tymoshenko 	TI_GPIO_LOCK(sc);
11525b03aba6SOleksandr Tymoshenko 	ti_gpio_intr_clr(sc, TI_GPIO_MASK(irq));
11535b03aba6SOleksandr Tymoshenko 	reg = ti_gpio_intr_reg(sc, irq);
11543681c8d7SLuiz Otavio O Souza 	if (reg != 0) {
11555b03aba6SOleksandr Tymoshenko 		val = ti_gpio_read_4(sc, reg);
11563681c8d7SLuiz Otavio O Souza 		val &= ~TI_GPIO_MASK(irq);
11575b03aba6SOleksandr Tymoshenko 		ti_gpio_write_4(sc, reg, val);
11583681c8d7SLuiz Otavio O Souza 	}
11595b03aba6SOleksandr Tymoshenko 	TI_GPIO_UNLOCK(sc);
11605b03aba6SOleksandr Tymoshenko }
11615b03aba6SOleksandr Tymoshenko 
11625b03aba6SOleksandr Tymoshenko static void
11635b03aba6SOleksandr Tymoshenko ti_gpio_unmask_irq_internal(struct ti_gpio_softc *sc, int irq)
11645b03aba6SOleksandr Tymoshenko {
11655b03aba6SOleksandr Tymoshenko 	uint32_t reg, val;
11665b03aba6SOleksandr Tymoshenko 
11675b03aba6SOleksandr Tymoshenko 	if (ti_gpio_valid_pin(sc, irq) != 0)
11685b03aba6SOleksandr Tymoshenko 		return;
11695b03aba6SOleksandr Tymoshenko 
11705b03aba6SOleksandr Tymoshenko 	TI_GPIO_LOCK(sc);
11715b03aba6SOleksandr Tymoshenko 	reg = ti_gpio_intr_reg(sc, irq);
11725b03aba6SOleksandr Tymoshenko 	if (reg != 0) {
11735b03aba6SOleksandr Tymoshenko 		val = ti_gpio_read_4(sc, reg);
11745b03aba6SOleksandr Tymoshenko 		val |= TI_GPIO_MASK(irq);
11755b03aba6SOleksandr Tymoshenko 		ti_gpio_write_4(sc, reg, val);
11765b03aba6SOleksandr Tymoshenko 		ti_gpio_intr_set(sc, TI_GPIO_MASK(irq));
11775b03aba6SOleksandr Tymoshenko 	}
11785b03aba6SOleksandr Tymoshenko 	TI_GPIO_UNLOCK(sc);
11795b03aba6SOleksandr Tymoshenko }
11805b03aba6SOleksandr Tymoshenko 
11815b03aba6SOleksandr Tymoshenko static void
11825b03aba6SOleksandr Tymoshenko ti_gpio_mask_irq(void *source)
11835b03aba6SOleksandr Tymoshenko {
11845b03aba6SOleksandr Tymoshenko 	struct ti_gpio_mask_arg *arg = source;
11855b03aba6SOleksandr Tymoshenko 
11865b03aba6SOleksandr Tymoshenko 	ti_gpio_mask_irq_internal(arg->softc, arg->pin);
11873681c8d7SLuiz Otavio O Souza }
11883681c8d7SLuiz Otavio O Souza 
11893681c8d7SLuiz Otavio O Souza static void
11903681c8d7SLuiz Otavio O Souza ti_gpio_unmask_irq(void *source)
11913681c8d7SLuiz Otavio O Souza {
11925b03aba6SOleksandr Tymoshenko 	struct ti_gpio_mask_arg *arg = source;
11933681c8d7SLuiz Otavio O Souza 
11945b03aba6SOleksandr Tymoshenko 	ti_gpio_unmask_irq_internal(arg->softc, arg->pin);
11953681c8d7SLuiz Otavio O Souza }
11963681c8d7SLuiz Otavio O Souza 
11973681c8d7SLuiz Otavio O Souza static int
11983681c8d7SLuiz Otavio O Souza ti_gpio_activate_resource(device_t dev, device_t child, int type, int rid,
11993681c8d7SLuiz Otavio O Souza 	struct resource *res)
12003681c8d7SLuiz Otavio O Souza {
12017b25d1d6SOleksandr Tymoshenko 	struct ti_gpio_mask_arg mask_arg;
12023681c8d7SLuiz Otavio O Souza 
12033681c8d7SLuiz Otavio O Souza 	if (type != SYS_RES_IRQ)
12043681c8d7SLuiz Otavio O Souza 		return (ENXIO);
12053681c8d7SLuiz Otavio O Souza 
12063681c8d7SLuiz Otavio O Souza 	/* Unmask the interrupt. */
12077b25d1d6SOleksandr Tymoshenko 	mask_arg.pin = rman_get_start(res);
12087b25d1d6SOleksandr Tymoshenko 	mask_arg.softc = device_get_softc(dev);
12097b25d1d6SOleksandr Tymoshenko 
12107b25d1d6SOleksandr Tymoshenko 	ti_gpio_unmask_irq((void *)&mask_arg);
12113681c8d7SLuiz Otavio O Souza 
12123681c8d7SLuiz Otavio O Souza 	return (0);
12133681c8d7SLuiz Otavio O Souza }
12143681c8d7SLuiz Otavio O Souza 
12153681c8d7SLuiz Otavio O Souza static int
12163681c8d7SLuiz Otavio O Souza ti_gpio_deactivate_resource(device_t dev, device_t child, int type, int rid,
12173681c8d7SLuiz Otavio O Souza 	struct resource *res)
12183681c8d7SLuiz Otavio O Souza {
12193681c8d7SLuiz Otavio O Souza 	int pin;
12203681c8d7SLuiz Otavio O Souza 
12213681c8d7SLuiz Otavio O Souza 	if (type != SYS_RES_IRQ)
12223681c8d7SLuiz Otavio O Souza 		return (ENXIO);
12233681c8d7SLuiz Otavio O Souza 
12243681c8d7SLuiz Otavio O Souza 	/* Mask the interrupt. */
12253681c8d7SLuiz Otavio O Souza 	pin = rman_get_start(res);
12263681c8d7SLuiz Otavio O Souza 	ti_gpio_mask_irq((void *)(uintptr_t)pin);
12273681c8d7SLuiz Otavio O Souza 
12283681c8d7SLuiz Otavio O Souza 	return (0);
12293681c8d7SLuiz Otavio O Souza }
12303681c8d7SLuiz Otavio O Souza 
12313681c8d7SLuiz Otavio O Souza static int
12323681c8d7SLuiz Otavio O Souza ti_gpio_config_intr(device_t dev, int irq, enum intr_trigger trig,
12333681c8d7SLuiz Otavio O Souza 	enum intr_polarity pol)
12343681c8d7SLuiz Otavio O Souza {
12353681c8d7SLuiz Otavio O Souza 	struct ti_gpio_softc *sc;
12363681c8d7SLuiz Otavio O Souza 	uint32_t oldreg, reg, val;
12373681c8d7SLuiz Otavio O Souza 
12383681c8d7SLuiz Otavio O Souza 	sc = device_get_softc(dev);
12393681c8d7SLuiz Otavio O Souza 	if (ti_gpio_valid_pin(sc, irq) != 0)
12403681c8d7SLuiz Otavio O Souza 		return (EINVAL);
12413681c8d7SLuiz Otavio O Souza 
12423681c8d7SLuiz Otavio O Souza 	/* There is no standard trigger or polarity. */
12433681c8d7SLuiz Otavio O Souza 	if (trig == INTR_TRIGGER_CONFORM || pol == INTR_POLARITY_CONFORM)
12443681c8d7SLuiz Otavio O Souza 		return (EINVAL);
12453681c8d7SLuiz Otavio O Souza 
12463681c8d7SLuiz Otavio O Souza 	TI_GPIO_LOCK(sc);
12473681c8d7SLuiz Otavio O Souza 	/*
12483681c8d7SLuiz Otavio O Souza 	 * TRM recommends add the new event before remove the old one to
12493681c8d7SLuiz Otavio O Souza 	 * avoid losing interrupts.
12503681c8d7SLuiz Otavio O Souza 	 */
12513681c8d7SLuiz Otavio O Souza 	oldreg = ti_gpio_intr_reg(sc, irq);
12523681c8d7SLuiz Otavio O Souza 	sc->sc_irq_trigger[irq] = trig;
12533681c8d7SLuiz Otavio O Souza 	sc->sc_irq_polarity[irq] = pol;
12543681c8d7SLuiz Otavio O Souza 	reg = ti_gpio_intr_reg(sc, irq);
12553681c8d7SLuiz Otavio O Souza 	if (reg != 0) {
12563681c8d7SLuiz Otavio O Souza 		/* Apply the new settings. */
12575b03aba6SOleksandr Tymoshenko 		val = ti_gpio_read_4(sc, reg);
12583681c8d7SLuiz Otavio O Souza 		val |= TI_GPIO_MASK(irq);
12595b03aba6SOleksandr Tymoshenko 		ti_gpio_write_4(sc, reg, val);
12603681c8d7SLuiz Otavio O Souza 	}
12617d732ceaSLuiz Otavio O Souza 	if (reg != oldreg && oldreg != 0) {
12623681c8d7SLuiz Otavio O Souza 		/* Remove the old settings. */
12635b03aba6SOleksandr Tymoshenko 		val = ti_gpio_read_4(sc, oldreg);
12643681c8d7SLuiz Otavio O Souza 		val &= ~TI_GPIO_MASK(irq);
12655b03aba6SOleksandr Tymoshenko 		ti_gpio_write_4(sc, oldreg, val);
12663681c8d7SLuiz Otavio O Souza 	}
12673681c8d7SLuiz Otavio O Souza 	TI_GPIO_UNLOCK(sc);
12683681c8d7SLuiz Otavio O Souza 
12693681c8d7SLuiz Otavio O Souza 	return (0);
12703681c8d7SLuiz Otavio O Souza }
12713681c8d7SLuiz Otavio O Souza 
12723681c8d7SLuiz Otavio O Souza static int
12733681c8d7SLuiz Otavio O Souza ti_gpio_setup_intr(device_t dev, device_t child, struct resource *ires,
12743681c8d7SLuiz Otavio O Souza 	int flags, driver_filter_t *filt, driver_intr_t *handler,
12753681c8d7SLuiz Otavio O Souza 	void *arg, void **cookiep)
12763681c8d7SLuiz Otavio O Souza {
12773681c8d7SLuiz Otavio O Souza 	struct ti_gpio_softc *sc;
12783681c8d7SLuiz Otavio O Souza 	struct intr_event *event;
12793681c8d7SLuiz Otavio O Souza 	int pin, error;
12803681c8d7SLuiz Otavio O Souza 
12813681c8d7SLuiz Otavio O Souza 	sc = device_get_softc(dev);
12823681c8d7SLuiz Otavio O Souza 	pin = rman_get_start(ires);
12833681c8d7SLuiz Otavio O Souza 	if (ti_gpio_valid_pin(sc, pin) != 0)
12843681c8d7SLuiz Otavio O Souza 		panic("%s: bad pin %d", __func__, pin);
12853681c8d7SLuiz Otavio O Souza 
12863681c8d7SLuiz Otavio O Souza 	event = sc->sc_events[pin];
12873681c8d7SLuiz Otavio O Souza 	if (event == NULL) {
12885b03aba6SOleksandr Tymoshenko 		sc->sc_mask_args[pin].softc = sc;
12895b03aba6SOleksandr Tymoshenko 		sc->sc_mask_args[pin].pin = pin;
12905b03aba6SOleksandr Tymoshenko 		error = intr_event_create(&event, (void *)&sc->sc_mask_args[pin], 0,
12913681c8d7SLuiz Otavio O Souza 		    pin, ti_gpio_mask_irq, ti_gpio_unmask_irq, NULL, NULL,
12923681c8d7SLuiz Otavio O Souza 		    "gpio%d pin%d:", device_get_unit(dev), pin);
12933681c8d7SLuiz Otavio O Souza 		if (error != 0)
12943681c8d7SLuiz Otavio O Souza 			return (error);
12953681c8d7SLuiz Otavio O Souza 		sc->sc_events[pin] = event;
12963681c8d7SLuiz Otavio O Souza 	}
12973681c8d7SLuiz Otavio O Souza 	intr_event_add_handler(event, device_get_nameunit(child), filt,
12983681c8d7SLuiz Otavio O Souza 	    handler, arg, intr_priority(flags), flags, cookiep);
12993681c8d7SLuiz Otavio O Souza 
13003681c8d7SLuiz Otavio O Souza 	return (0);
13013681c8d7SLuiz Otavio O Souza }
13023681c8d7SLuiz Otavio O Souza 
13033681c8d7SLuiz Otavio O Souza static int
13043681c8d7SLuiz Otavio O Souza ti_gpio_teardown_intr(device_t dev, device_t child, struct resource *ires,
13053681c8d7SLuiz Otavio O Souza 	void *cookie)
13063681c8d7SLuiz Otavio O Souza {
13073681c8d7SLuiz Otavio O Souza 	struct ti_gpio_softc *sc;
13083681c8d7SLuiz Otavio O Souza 	int pin, err;
13093681c8d7SLuiz Otavio O Souza 
13103681c8d7SLuiz Otavio O Souza 	sc = device_get_softc(dev);
13113681c8d7SLuiz Otavio O Souza 	pin = rman_get_start(ires);
13123681c8d7SLuiz Otavio O Souza 	if (ti_gpio_valid_pin(sc, pin) != 0)
13133681c8d7SLuiz Otavio O Souza 		panic("%s: bad pin %d", __func__, pin);
13143681c8d7SLuiz Otavio O Souza 	if (sc->sc_events[pin] == NULL)
13153681c8d7SLuiz Otavio O Souza 		panic("Trying to teardown unoccupied IRQ");
13163681c8d7SLuiz Otavio O Souza 	err = intr_event_remove_handler(cookie);
13173681c8d7SLuiz Otavio O Souza 	if (!err)
13183681c8d7SLuiz Otavio O Souza 		sc->sc_events[pin] = NULL;
13193681c8d7SLuiz Otavio O Souza 
13203681c8d7SLuiz Otavio O Souza 	return (err);
13213681c8d7SLuiz Otavio O Souza }
13222df5562dSSvatopluk Kraus #endif
13233681c8d7SLuiz Otavio O Souza 
13248c705c2cSLuiz Otavio O Souza static phandle_t
13258c705c2cSLuiz Otavio O Souza ti_gpio_get_node(device_t bus, device_t dev)
13268c705c2cSLuiz Otavio O Souza {
13278c705c2cSLuiz Otavio O Souza 
13288c705c2cSLuiz Otavio O Souza 	/* We only have one child, the GPIO bus, which needs our own node. */
13298c705c2cSLuiz Otavio O Souza 	return (ofw_bus_get_node(bus));
13308c705c2cSLuiz Otavio O Souza }
13318c705c2cSLuiz Otavio O Souza 
1332e53470feSOleksandr Tymoshenko static device_method_t ti_gpio_methods[] = {
1333e53470feSOleksandr Tymoshenko 	DEVMETHOD(device_attach, ti_gpio_attach),
1334e53470feSOleksandr Tymoshenko 	DEVMETHOD(device_detach, ti_gpio_detach),
1335e53470feSOleksandr Tymoshenko 
1336e53470feSOleksandr Tymoshenko 	/* GPIO protocol */
13377836352bSLuiz Otavio O Souza 	DEVMETHOD(gpio_get_bus, ti_gpio_get_bus),
1338e53470feSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_max, ti_gpio_pin_max),
1339e53470feSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_getname, ti_gpio_pin_getname),
1340e53470feSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_getflags, ti_gpio_pin_getflags),
1341e53470feSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_getcaps, ti_gpio_pin_getcaps),
1342e53470feSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_setflags, ti_gpio_pin_setflags),
1343e53470feSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_get, ti_gpio_pin_get),
1344e53470feSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_set, ti_gpio_pin_set),
1345e53470feSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_toggle, ti_gpio_pin_toggle),
13468c705c2cSLuiz Otavio O Souza 
134759c3cb81SAndrew Turner #ifdef INTRNG
13482df5562dSSvatopluk Kraus 	/* Interrupt controller interface */
13492df5562dSSvatopluk Kraus 	DEVMETHOD(pic_disable_intr,	ti_gpio_pic_disable_intr),
13502df5562dSSvatopluk Kraus 	DEVMETHOD(pic_enable_intr,	ti_gpio_pic_enable_intr),
13512df5562dSSvatopluk Kraus 	DEVMETHOD(pic_map_intr,		ti_gpio_pic_map_intr),
13522df5562dSSvatopluk Kraus 	DEVMETHOD(pic_setup_intr,	ti_gpio_pic_setup_intr),
13532df5562dSSvatopluk Kraus 	DEVMETHOD(pic_teardown_intr,	ti_gpio_pic_teardown_intr),
13542df5562dSSvatopluk Kraus 	DEVMETHOD(pic_post_filter,	ti_gpio_pic_post_filter),
13552df5562dSSvatopluk Kraus 	DEVMETHOD(pic_post_ithread,	ti_gpio_pic_post_ithread),
13562df5562dSSvatopluk Kraus 	DEVMETHOD(pic_pre_ithread,	ti_gpio_pic_pre_ithread),
13572df5562dSSvatopluk Kraus #else
13583681c8d7SLuiz Otavio O Souza 	/* Bus interface */
13593681c8d7SLuiz Otavio O Souza 	DEVMETHOD(bus_activate_resource, ti_gpio_activate_resource),
13603681c8d7SLuiz Otavio O Souza 	DEVMETHOD(bus_deactivate_resource, ti_gpio_deactivate_resource),
13613681c8d7SLuiz Otavio O Souza 	DEVMETHOD(bus_config_intr, ti_gpio_config_intr),
13623681c8d7SLuiz Otavio O Souza 	DEVMETHOD(bus_setup_intr, ti_gpio_setup_intr),
13633681c8d7SLuiz Otavio O Souza 	DEVMETHOD(bus_teardown_intr, ti_gpio_teardown_intr),
13642df5562dSSvatopluk Kraus #endif
13653681c8d7SLuiz Otavio O Souza 
13668c705c2cSLuiz Otavio O Souza 	/* ofw_bus interface */
13678c705c2cSLuiz Otavio O Souza 	DEVMETHOD(ofw_bus_get_node, ti_gpio_get_node),
13688c705c2cSLuiz Otavio O Souza 
1369e53470feSOleksandr Tymoshenko 	{0, 0},
1370e53470feSOleksandr Tymoshenko };
1371e53470feSOleksandr Tymoshenko 
1372b6c7dacfSAndrew Turner driver_t ti_gpio_driver = {
1373e53470feSOleksandr Tymoshenko 	"gpio",
1374e53470feSOleksandr Tymoshenko 	ti_gpio_methods,
1375e53470feSOleksandr Tymoshenko 	sizeof(struct ti_gpio_softc),
1376e53470feSOleksandr Tymoshenko };
1377