xref: /freebsd/sys/arm/xilinx/zy7_gpio.c (revision b07fed8180a6b97f44c7d900d17b94fa3f1974cf)
1a9caca6aSWojciech A. Koszek /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3af3dc4a7SPedro F. Giffuni  *
440713190SWojciech A. Koszek  * Copyright (c) 2013 Thomas Skibo
5a9caca6aSWojciech A. Koszek  * All rights reserved.
6a9caca6aSWojciech A. Koszek  *
7a9caca6aSWojciech A. Koszek  * Redistribution and use in source and binary forms, with or without
840713190SWojciech A. Koszek  * modification, are permitted provided that the following conditions
940713190SWojciech A. Koszek  * are met:
1040713190SWojciech A. Koszek  * 1. Redistributions of source code must retain the above copyright
11a9caca6aSWojciech A. Koszek  *    notice, this list of conditions and the following disclaimer.
1240713190SWojciech A. Koszek  * 2. Redistributions in binary form must reproduce the above copyright
13a9caca6aSWojciech A. Koszek  *    notice, this list of conditions and the following disclaimer in the
14a9caca6aSWojciech A. Koszek  *    documentation and/or other materials provided with the distribution.
15a9caca6aSWojciech A. Koszek  *
1640713190SWojciech A. Koszek  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1740713190SWojciech A. Koszek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18a9caca6aSWojciech A. Koszek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1940713190SWojciech A. Koszek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2040713190SWojciech A. Koszek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2140713190SWojciech A. Koszek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2240713190SWojciech A. Koszek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2340713190SWojciech A. Koszek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24a9caca6aSWojciech A. Koszek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2540713190SWojciech A. Koszek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2640713190SWojciech A. Koszek  * SUCH DAMAGE.
27a9caca6aSWojciech A. Koszek  *
2840713190SWojciech A. Koszek  * $FreeBSD$
29a9caca6aSWojciech A. Koszek  */
30a9caca6aSWojciech A. Koszek 
3140713190SWojciech A. Koszek /*
3240713190SWojciech A. Koszek  * A GPIO driver for Xilinx Zynq-7000.
33a9caca6aSWojciech A. Koszek  *
34a9caca6aSWojciech A. Koszek  * The GPIO peripheral on Zynq allows controlling 114 general purpose I/Os.
35a9caca6aSWojciech A. Koszek  *
36a9caca6aSWojciech A. Koszek  * Pins 53-0 are sent to the MIO.  Any MIO pins not used by a PS peripheral are
37a9caca6aSWojciech A. Koszek  * available as a GPIO pin.  Pins 64-127 are sent to the PL (FPGA) section of
38a9caca6aSWojciech A. Koszek  * Zynq as EMIO signals.
39a9caca6aSWojciech A. Koszek  *
40a9caca6aSWojciech A. Koszek  * The hardware provides a way to use IOs as interrupt sources but the
41a9caca6aSWojciech A. Koszek  * gpio framework doesn't seem to have hooks for this.
42a9caca6aSWojciech A. Koszek  *
43a9caca6aSWojciech A. Koszek  * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual.
44a9caca6aSWojciech A. Koszek  * (v1.4) November 16, 2012.  Xilinx doc UG585.  GPIO is covered in
45a9caca6aSWojciech A. Koszek  * chater 14.  Register definitions are in appendix B.19.
46a9caca6aSWojciech A. Koszek  */
47a9caca6aSWojciech A. Koszek 
48a9caca6aSWojciech A. Koszek #include <sys/cdefs.h>
49a9caca6aSWojciech A. Koszek __FBSDID("$FreeBSD$");
50a9caca6aSWojciech A. Koszek 
51a9caca6aSWojciech A. Koszek #include <sys/param.h>
52a9caca6aSWojciech A. Koszek #include <sys/systm.h>
53a9caca6aSWojciech A. Koszek #include <sys/conf.h>
54a9caca6aSWojciech A. Koszek #include <sys/bus.h>
55a9caca6aSWojciech A. Koszek #include <sys/kernel.h>
56a9caca6aSWojciech A. Koszek #include <sys/module.h>
57a9caca6aSWojciech A. Koszek #include <sys/lock.h>
58a9caca6aSWojciech A. Koszek #include <sys/mutex.h>
59a9caca6aSWojciech A. Koszek #include <sys/resource.h>
60a9caca6aSWojciech A. Koszek #include <sys/rman.h>
61a9caca6aSWojciech A. Koszek #include <sys/gpio.h>
62a9caca6aSWojciech A. Koszek 
63a9caca6aSWojciech A. Koszek #include <machine/bus.h>
64a9caca6aSWojciech A. Koszek #include <machine/resource.h>
65a9caca6aSWojciech A. Koszek #include <machine/stdarg.h>
66a9caca6aSWojciech A. Koszek 
677836352bSLuiz Otavio O Souza #include <dev/gpio/gpiobusvar.h>
68a9caca6aSWojciech A. Koszek #include <dev/ofw/ofw_bus.h>
69a9caca6aSWojciech A. Koszek #include <dev/ofw/ofw_bus_subr.h>
70a9caca6aSWojciech A. Koszek 
71a9caca6aSWojciech A. Koszek #include "gpio_if.h"
72a9caca6aSWojciech A. Koszek 
73*b07fed81SEmmanuel Vadot #define	ZYNQ_MAX_BANK		4
74a9caca6aSWojciech A. Koszek 
75*b07fed81SEmmanuel Vadot /* Zynq 7000 */
76*b07fed81SEmmanuel Vadot #define	ZYNQ_BANK0_PIN_MIN	0
77*b07fed81SEmmanuel Vadot #define	ZYNQ_BANK0_NPIN		32
78*b07fed81SEmmanuel Vadot #define	ZYNQ_BANK1_PIN_MIN	32
79*b07fed81SEmmanuel Vadot #define	ZYNQ_BANK1_NPIN		22
80*b07fed81SEmmanuel Vadot #define	ZYNQ_BANK2_PIN_MIN	64
81*b07fed81SEmmanuel Vadot #define	ZYNQ_BANK2_NPIN		32
82*b07fed81SEmmanuel Vadot #define	ZYNQ_BANK3_PIN_MIN	96
83*b07fed81SEmmanuel Vadot #define	ZYNQ_BANK3_NPIN		32
84*b07fed81SEmmanuel Vadot #define	ZYNQ_PIN_MIO_MIN	0
85*b07fed81SEmmanuel Vadot #define	ZYNQ_PIN_MIO_MAX	54
86*b07fed81SEmmanuel Vadot #define	ZYNQ_PIN_EMIO_MIN	64
87*b07fed81SEmmanuel Vadot #define	ZYNQ_PIN_EMIO_MAX	118
88a9caca6aSWojciech A. Koszek 
89*b07fed81SEmmanuel Vadot #define	ZYNQ_BANK_NPIN(bank)	(ZYNQ_BANK##bank##_NPIN)
90*b07fed81SEmmanuel Vadot #define	ZYNQ_BANK_PIN_MIN(bank)	(ZYNQ_BANK##bank##_PIN_MIN)
91*b07fed81SEmmanuel Vadot #define	ZYNQ_BANK_PIN_MAX(bank)	(ZYNQ_BANK##bank##_PIN_MIN + ZYNQ_BANK##bank##_NPIN - 1)
92*b07fed81SEmmanuel Vadot 
93*b07fed81SEmmanuel Vadot #define	ZYNQ_PIN_IS_MIO(pin)	(pin >= ZYNQ_PIN_MIO_MIN && \
94*b07fed81SEmmanuel Vadot 	  pin <= ZYNQ_PIN_MIO_MAX)
95*b07fed81SEmmanuel Vadot #define	ZYNQ_PIN_IS_EMIO(pin)	(pin >= ZYNQ_PIN_EMIO_MIN && \
96*b07fed81SEmmanuel Vadot 	  pin <= ZYNQ_PIN_EMIO_MAX)
97a9caca6aSWojciech A. Koszek 
98a9caca6aSWojciech A. Koszek #define ZGPIO_LOCK(sc)			mtx_lock(&(sc)->sc_mtx)
99a9caca6aSWojciech A. Koszek #define	ZGPIO_UNLOCK(sc)		mtx_unlock(&(sc)->sc_mtx)
100a9caca6aSWojciech A. Koszek #define ZGPIO_LOCK_INIT(sc) \
101a9caca6aSWojciech A. Koszek 	mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev),	\
102a9caca6aSWojciech A. Koszek 	    "gpio", MTX_DEF)
103a9caca6aSWojciech A. Koszek #define ZGPIO_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
104a9caca6aSWojciech A. Koszek 
105*b07fed81SEmmanuel Vadot struct zynq_gpio_conf {
106*b07fed81SEmmanuel Vadot 	char		*name;
107*b07fed81SEmmanuel Vadot 	uint32_t	nbanks;
108*b07fed81SEmmanuel Vadot 	uint32_t	maxpin;
109*b07fed81SEmmanuel Vadot 	uint32_t	bank_min[ZYNQ_MAX_BANK];
110*b07fed81SEmmanuel Vadot 	uint32_t	bank_max[ZYNQ_MAX_BANK];
111*b07fed81SEmmanuel Vadot };
112*b07fed81SEmmanuel Vadot 
113a9caca6aSWojciech A. Koszek struct zy7_gpio_softc {
114a9caca6aSWojciech A. Koszek 	device_t		dev;
1157836352bSLuiz Otavio O Souza 	device_t		busdev;
116a9caca6aSWojciech A. Koszek 	struct mtx		sc_mtx;
117a9caca6aSWojciech A. Koszek 	struct resource		*mem_res;	/* Memory resource */
118*b07fed81SEmmanuel Vadot 	struct zynq_gpio_conf	*conf;
119*b07fed81SEmmanuel Vadot };
120*b07fed81SEmmanuel Vadot 
121*b07fed81SEmmanuel Vadot static struct zynq_gpio_conf z7_gpio_conf = {
122*b07fed81SEmmanuel Vadot 	.name = "Zynq-7000 GPIO Controller",
123*b07fed81SEmmanuel Vadot 	.nbanks = ZYNQ_MAX_BANK,
124*b07fed81SEmmanuel Vadot 	.maxpin = ZYNQ_PIN_EMIO_MAX,
125*b07fed81SEmmanuel Vadot 	.bank_min[0] = ZYNQ_BANK_PIN_MIN(0),
126*b07fed81SEmmanuel Vadot 	.bank_max[0] = ZYNQ_BANK_PIN_MAX(0),
127*b07fed81SEmmanuel Vadot 	.bank_min[1] = ZYNQ_BANK_PIN_MIN(1),
128*b07fed81SEmmanuel Vadot 	.bank_max[1] = ZYNQ_BANK_PIN_MAX(1),
129*b07fed81SEmmanuel Vadot 	.bank_min[2] = ZYNQ_BANK_PIN_MIN(2),
130*b07fed81SEmmanuel Vadot 	.bank_max[2] = ZYNQ_BANK_PIN_MAX(2),
131*b07fed81SEmmanuel Vadot 	.bank_min[3] = ZYNQ_BANK_PIN_MIN(3),
132*b07fed81SEmmanuel Vadot 	.bank_max[3] = ZYNQ_BANK_PIN_MAX(3),
133*b07fed81SEmmanuel Vadot };
134*b07fed81SEmmanuel Vadot 
135*b07fed81SEmmanuel Vadot static struct ofw_compat_data compat_data[] = {
136*b07fed81SEmmanuel Vadot 	{"xlnx,zy7_gpio",		(uintptr_t)&z7_gpio_conf},
137*b07fed81SEmmanuel Vadot 	{NULL, 0},
138a9caca6aSWojciech A. Koszek };
139a9caca6aSWojciech A. Koszek 
140a9caca6aSWojciech A. Koszek #define WR4(sc, off, val)	bus_write_4((sc)->mem_res, (off), (val))
141a9caca6aSWojciech A. Koszek #define RD4(sc, off)		bus_read_4((sc)->mem_res, (off))
142a9caca6aSWojciech A. Koszek 
143a9caca6aSWojciech A. Koszek /* Xilinx Zynq-7000 GPIO register definitions:
144a9caca6aSWojciech A. Koszek  */
145a9caca6aSWojciech A. Koszek #define ZY7_GPIO_MASK_DATA_LSW(b)	(0x0000+8*(b))	/* maskable wr lo */
146a9caca6aSWojciech A. Koszek #define ZY7_GPIO_MASK_DATA_MSW(b)	(0x0004+8*(b))	/* maskable wr hi */
147a9caca6aSWojciech A. Koszek #define ZY7_GPIO_DATA(b)		(0x0040+4*(b))	/* in/out data */
148a9caca6aSWojciech A. Koszek #define ZY7_GPIO_DATA_RO(b)		(0x0060+4*(b))	/* input data */
149a9caca6aSWojciech A. Koszek 
150a9caca6aSWojciech A. Koszek #define ZY7_GPIO_DIRM(b)		(0x0204+0x40*(b)) /* direction mode */
151a9caca6aSWojciech A. Koszek #define ZY7_GPIO_OEN(b)			(0x0208+0x40*(b)) /* output enable */
152a9caca6aSWojciech A. Koszek #define ZY7_GPIO_INT_MASK(b)		(0x020c+0x40*(b)) /* int mask */
153a9caca6aSWojciech A. Koszek #define ZY7_GPIO_INT_EN(b)		(0x0210+0x40*(b)) /* int enable */
154a9caca6aSWojciech A. Koszek #define ZY7_GPIO_INT_DIS(b)		(0x0214+0x40*(b)) /* int disable */
155a9caca6aSWojciech A. Koszek #define ZY7_GPIO_INT_STAT(b)		(0x0218+0x40*(b)) /* int status */
156a9caca6aSWojciech A. Koszek #define ZY7_GPIO_INT_TYPE(b)		(0x021c+0x40*(b)) /* int type */
157a9caca6aSWojciech A. Koszek #define ZY7_GPIO_INT_POLARITY(b)	(0x0220+0x40*(b)) /* int polarity */
158a9caca6aSWojciech A. Koszek #define ZY7_GPIO_INT_ANY(b)		(0x0224+0x40*(b)) /* any edge */
159a9caca6aSWojciech A. Koszek 
1607836352bSLuiz Otavio O Souza static device_t
1617836352bSLuiz Otavio O Souza zy7_gpio_get_bus(device_t dev)
1627836352bSLuiz Otavio O Souza {
1637836352bSLuiz Otavio O Souza 	struct zy7_gpio_softc *sc;
1647836352bSLuiz Otavio O Souza 
1657836352bSLuiz Otavio O Souza 	sc = device_get_softc(dev);
1667836352bSLuiz Otavio O Souza 
1677836352bSLuiz Otavio O Souza 	return (sc->busdev);
1687836352bSLuiz Otavio O Souza }
169a9caca6aSWojciech A. Koszek 
170a9caca6aSWojciech A. Koszek static int
171a9caca6aSWojciech A. Koszek zy7_gpio_pin_max(device_t dev, int *maxpin)
172a9caca6aSWojciech A. Koszek {
173*b07fed81SEmmanuel Vadot 	struct zy7_gpio_softc *sc;
174a9caca6aSWojciech A. Koszek 
175*b07fed81SEmmanuel Vadot 	sc = device_get_softc(dev);
176*b07fed81SEmmanuel Vadot 	*maxpin = sc->conf->maxpin;
177a9caca6aSWojciech A. Koszek 	return (0);
178a9caca6aSWojciech A. Koszek }
179a9caca6aSWojciech A. Koszek 
180*b07fed81SEmmanuel Vadot static inline bool
181*b07fed81SEmmanuel Vadot zy7_pin_valid(device_t dev, uint32_t pin)
182*b07fed81SEmmanuel Vadot {
183*b07fed81SEmmanuel Vadot 	struct zy7_gpio_softc *sc;
184*b07fed81SEmmanuel Vadot 	int i;
185*b07fed81SEmmanuel Vadot 	bool found = false;
186*b07fed81SEmmanuel Vadot 
187*b07fed81SEmmanuel Vadot 	sc = device_get_softc(dev);
188*b07fed81SEmmanuel Vadot 	for (i = 0; i < sc->conf->nbanks; i++) {
189*b07fed81SEmmanuel Vadot 		if (pin >= sc->conf->bank_min[i] && pin <= sc->conf->bank_max[i]) {
190*b07fed81SEmmanuel Vadot 			found = true;
191*b07fed81SEmmanuel Vadot 			break;
192*b07fed81SEmmanuel Vadot 		}
193*b07fed81SEmmanuel Vadot 	}
194*b07fed81SEmmanuel Vadot 
195*b07fed81SEmmanuel Vadot 	return (found);
196*b07fed81SEmmanuel Vadot }
197*b07fed81SEmmanuel Vadot 
198a9caca6aSWojciech A. Koszek /* Get a specific pin's capabilities. */
199a9caca6aSWojciech A. Koszek static int
200a9caca6aSWojciech A. Koszek zy7_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
201a9caca6aSWojciech A. Koszek {
202a9caca6aSWojciech A. Koszek 
203*b07fed81SEmmanuel Vadot 	if (!zy7_pin_valid(dev, pin))
204a9caca6aSWojciech A. Koszek 		return (EINVAL);
205a9caca6aSWojciech A. Koszek 
206a9caca6aSWojciech A. Koszek 	*caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_TRISTATE);
207a9caca6aSWojciech A. Koszek 
208a9caca6aSWojciech A. Koszek 	return (0);
209a9caca6aSWojciech A. Koszek }
210a9caca6aSWojciech A. Koszek 
211a9caca6aSWojciech A. Koszek /* Get a specific pin's name. */
212a9caca6aSWojciech A. Koszek static int
213a9caca6aSWojciech A. Koszek zy7_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
214a9caca6aSWojciech A. Koszek {
215a9caca6aSWojciech A. Koszek 
216*b07fed81SEmmanuel Vadot 	if (!zy7_pin_valid(dev, pin))
217a9caca6aSWojciech A. Koszek 		return (EINVAL);
218a9caca6aSWojciech A. Koszek 
219*b07fed81SEmmanuel Vadot 	if (ZYNQ_PIN_IS_MIO(pin)) {
220a9caca6aSWojciech A. Koszek 		snprintf(name, GPIOMAXNAME, "MIO_%d", pin);
221a9caca6aSWojciech A. Koszek 		name[GPIOMAXNAME - 1] = '\0';
222a9caca6aSWojciech A. Koszek 	} else {
223*b07fed81SEmmanuel Vadot 		snprintf(name, GPIOMAXNAME, "EMIO_%d", pin - ZYNQ_PIN_EMIO_MIN);
224a9caca6aSWojciech A. Koszek 		name[GPIOMAXNAME - 1] = '\0';
225a9caca6aSWojciech A. Koszek 	}
226a9caca6aSWojciech A. Koszek 
227a9caca6aSWojciech A. Koszek 	return (0);
228a9caca6aSWojciech A. Koszek }
229a9caca6aSWojciech A. Koszek 
230a9caca6aSWojciech A. Koszek /* Get a specific pin's current in/out/tri state. */
231a9caca6aSWojciech A. Koszek static int
232a9caca6aSWojciech A. Koszek zy7_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
233a9caca6aSWojciech A. Koszek {
234a9caca6aSWojciech A. Koszek 	struct zy7_gpio_softc *sc = device_get_softc(dev);
235a9caca6aSWojciech A. Koszek 
236*b07fed81SEmmanuel Vadot 	if (!zy7_pin_valid(dev, pin))
237a9caca6aSWojciech A. Koszek 		return (EINVAL);
238a9caca6aSWojciech A. Koszek 
239a9caca6aSWojciech A. Koszek 	ZGPIO_LOCK(sc);
240a9caca6aSWojciech A. Koszek 
241a9caca6aSWojciech A. Koszek 	if ((RD4(sc, ZY7_GPIO_DIRM(pin >> 5)) & (1 << (pin & 31))) != 0) {
242a9caca6aSWojciech A. Koszek 		/* output */
243a9caca6aSWojciech A. Koszek 		if ((RD4(sc, ZY7_GPIO_OEN(pin >> 5)) & (1 << (pin & 31))) == 0)
244a9caca6aSWojciech A. Koszek 			*flags = (GPIO_PIN_OUTPUT | GPIO_PIN_TRISTATE);
245a9caca6aSWojciech A. Koszek 		else
246a9caca6aSWojciech A. Koszek 			*flags = GPIO_PIN_OUTPUT;
247a9caca6aSWojciech A. Koszek 	} else
248a9caca6aSWojciech A. Koszek 		/* input */
249a9caca6aSWojciech A. Koszek 		*flags = GPIO_PIN_INPUT;
250a9caca6aSWojciech A. Koszek 
251a9caca6aSWojciech A. Koszek 	ZGPIO_UNLOCK(sc);
252a9caca6aSWojciech A. Koszek 
253a9caca6aSWojciech A. Koszek 	return (0);
254a9caca6aSWojciech A. Koszek }
255a9caca6aSWojciech A. Koszek 
256a9caca6aSWojciech A. Koszek /* Set a specific pin's in/out/tri state. */
257a9caca6aSWojciech A. Koszek static int
258a9caca6aSWojciech A. Koszek zy7_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
259a9caca6aSWojciech A. Koszek {
260a9caca6aSWojciech A. Koszek 	struct zy7_gpio_softc *sc = device_get_softc(dev);
261a9caca6aSWojciech A. Koszek 
262*b07fed81SEmmanuel Vadot 	if (!zy7_pin_valid(dev, pin))
263a9caca6aSWojciech A. Koszek 		return (EINVAL);
264a9caca6aSWojciech A. Koszek 
265a9caca6aSWojciech A. Koszek 	ZGPIO_LOCK(sc);
266a9caca6aSWojciech A. Koszek 
267a9caca6aSWojciech A. Koszek 	if ((flags & GPIO_PIN_OUTPUT) != 0) {
268a9caca6aSWojciech A. Koszek 		/* Output.  Set or reset OEN too. */
269a9caca6aSWojciech A. Koszek 		WR4(sc, ZY7_GPIO_DIRM(pin >> 5),
270a9caca6aSWojciech A. Koszek 		    RD4(sc, ZY7_GPIO_DIRM(pin >> 5)) | (1 << (pin & 31)));
271a9caca6aSWojciech A. Koszek 
272a9caca6aSWojciech A. Koszek 		if ((flags & GPIO_PIN_TRISTATE) != 0)
273a9caca6aSWojciech A. Koszek 			WR4(sc, ZY7_GPIO_OEN(pin >> 5),
274a9caca6aSWojciech A. Koszek 			    RD4(sc, ZY7_GPIO_OEN(pin >> 5)) &
275a9caca6aSWojciech A. Koszek 			    ~(1 << (pin & 31)));
276a9caca6aSWojciech A. Koszek 		else
277a9caca6aSWojciech A. Koszek 			WR4(sc, ZY7_GPIO_OEN(pin >> 5),
278a9caca6aSWojciech A. Koszek 			    RD4(sc, ZY7_GPIO_OEN(pin >> 5)) |
279a9caca6aSWojciech A. Koszek 			    (1 << (pin & 31)));
280a9caca6aSWojciech A. Koszek 	} else {
281a9caca6aSWojciech A. Koszek 		/* Input.  Turn off OEN. */
282a9caca6aSWojciech A. Koszek 		WR4(sc, ZY7_GPIO_DIRM(pin >> 5),
283a9caca6aSWojciech A. Koszek 		    RD4(sc, ZY7_GPIO_DIRM(pin >> 5)) & ~(1 << (pin & 31)));
284a9caca6aSWojciech A. Koszek 		WR4(sc, ZY7_GPIO_OEN(pin >> 5),
285a9caca6aSWojciech A. Koszek 		    RD4(sc, ZY7_GPIO_OEN(pin >> 5)) & ~(1 << (pin & 31)));
286a9caca6aSWojciech A. Koszek 	}
287a9caca6aSWojciech A. Koszek 
288a9caca6aSWojciech A. Koszek 	ZGPIO_UNLOCK(sc);
289a9caca6aSWojciech A. Koszek 
290a9caca6aSWojciech A. Koszek 	return (0);
291a9caca6aSWojciech A. Koszek }
292a9caca6aSWojciech A. Koszek 
293a9caca6aSWojciech A. Koszek /* Set a specific output pin's value. */
294a9caca6aSWojciech A. Koszek static int
295a9caca6aSWojciech A. Koszek zy7_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
296a9caca6aSWojciech A. Koszek {
297a9caca6aSWojciech A. Koszek 	struct zy7_gpio_softc *sc = device_get_softc(dev);
298a9caca6aSWojciech A. Koszek 
299*b07fed81SEmmanuel Vadot 	if (!zy7_pin_valid(dev, pin) || value > 1)
300a9caca6aSWojciech A. Koszek 		return (EINVAL);
301a9caca6aSWojciech A. Koszek 
302a9caca6aSWojciech A. Koszek 	/* Fancy register tricks allow atomic set or reset. */
303a9caca6aSWojciech A. Koszek 	if ((pin & 16) != 0)
304a9caca6aSWojciech A. Koszek 		WR4(sc, ZY7_GPIO_MASK_DATA_MSW(pin >> 5),
305a9caca6aSWojciech A. Koszek 		    (0xffff0000 ^ (0x10000 << (pin & 15))) |
306a9caca6aSWojciech A. Koszek 		    (value << (pin & 15)));
307a9caca6aSWojciech A. Koszek 	else
308a9caca6aSWojciech A. Koszek 		WR4(sc, ZY7_GPIO_MASK_DATA_LSW(pin >> 5),
309a9caca6aSWojciech A. Koszek 		    (0xffff0000 ^ (0x10000 << (pin & 15))) |
310a9caca6aSWojciech A. Koszek 		    (value << (pin & 15)));
311a9caca6aSWojciech A. Koszek 
312a9caca6aSWojciech A. Koszek 	return (0);
313a9caca6aSWojciech A. Koszek }
314a9caca6aSWojciech A. Koszek 
315a9caca6aSWojciech A. Koszek /* Get a specific pin's input value. */
316a9caca6aSWojciech A. Koszek static int
317a9caca6aSWojciech A. Koszek zy7_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value)
318a9caca6aSWojciech A. Koszek {
319a9caca6aSWojciech A. Koszek 	struct zy7_gpio_softc *sc = device_get_softc(dev);
320a9caca6aSWojciech A. Koszek 
321*b07fed81SEmmanuel Vadot 	if (!zy7_pin_valid(dev, pin))
322a9caca6aSWojciech A. Koszek 		return (EINVAL);
323a9caca6aSWojciech A. Koszek 
324a9caca6aSWojciech A. Koszek 	*value = (RD4(sc, ZY7_GPIO_DATA_RO(pin >> 5)) >> (pin & 31)) & 1;
325a9caca6aSWojciech A. Koszek 
326a9caca6aSWojciech A. Koszek 	return (0);
327a9caca6aSWojciech A. Koszek }
328a9caca6aSWojciech A. Koszek 
329a9caca6aSWojciech A. Koszek /* Toggle a pin's output value. */
330a9caca6aSWojciech A. Koszek static int
331a9caca6aSWojciech A. Koszek zy7_gpio_pin_toggle(device_t dev, uint32_t pin)
332a9caca6aSWojciech A. Koszek {
333a9caca6aSWojciech A. Koszek 	struct zy7_gpio_softc *sc = device_get_softc(dev);
334a9caca6aSWojciech A. Koszek 
335*b07fed81SEmmanuel Vadot 	if (!zy7_pin_valid(dev, pin))
336a9caca6aSWojciech A. Koszek 		return (EINVAL);
337a9caca6aSWojciech A. Koszek 
338a9caca6aSWojciech A. Koszek 	ZGPIO_LOCK(sc);
339a9caca6aSWojciech A. Koszek 
340a9caca6aSWojciech A. Koszek 	WR4(sc, ZY7_GPIO_DATA(pin >> 5),
341a9caca6aSWojciech A. Koszek 	    RD4(sc, ZY7_GPIO_DATA(pin >> 5)) ^ (1 << (pin & 31)));
342a9caca6aSWojciech A. Koszek 
343a9caca6aSWojciech A. Koszek 	ZGPIO_UNLOCK(sc);
344a9caca6aSWojciech A. Koszek 
345a9caca6aSWojciech A. Koszek 	return (0);
346a9caca6aSWojciech A. Koszek }
347a9caca6aSWojciech A. Koszek 
348a9caca6aSWojciech A. Koszek static int
349a9caca6aSWojciech A. Koszek zy7_gpio_probe(device_t dev)
350a9caca6aSWojciech A. Koszek {
351*b07fed81SEmmanuel Vadot 	struct zynq_gpio_conf *conf;
352a9caca6aSWojciech A. Koszek 
353add35ed5SIan Lepore 	if (!ofw_bus_status_okay(dev))
354add35ed5SIan Lepore 		return (ENXIO);
355add35ed5SIan Lepore 
356*b07fed81SEmmanuel Vadot 	conf = (struct zynq_gpio_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
357*b07fed81SEmmanuel Vadot 	if (conf == 0)
358a9caca6aSWojciech A. Koszek 		return (ENXIO);
359a9caca6aSWojciech A. Koszek 
360*b07fed81SEmmanuel Vadot 	device_set_desc(dev, conf->name);
361a9caca6aSWojciech A. Koszek 	return (0);
362a9caca6aSWojciech A. Koszek }
363a9caca6aSWojciech A. Koszek 
364a9caca6aSWojciech A. Koszek static int zy7_gpio_detach(device_t dev);
365a9caca6aSWojciech A. Koszek 
366a9caca6aSWojciech A. Koszek static int
367a9caca6aSWojciech A. Koszek zy7_gpio_attach(device_t dev)
368a9caca6aSWojciech A. Koszek {
369a9caca6aSWojciech A. Koszek 	struct zy7_gpio_softc *sc = device_get_softc(dev);
370a9caca6aSWojciech A. Koszek 	int rid;
371a9caca6aSWojciech A. Koszek 
372a9caca6aSWojciech A. Koszek 	sc->dev = dev;
373*b07fed81SEmmanuel Vadot 	sc->conf = (struct zynq_gpio_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
374a9caca6aSWojciech A. Koszek 
375a9caca6aSWojciech A. Koszek 	ZGPIO_LOCK_INIT(sc);
376a9caca6aSWojciech A. Koszek 
377a9caca6aSWojciech A. Koszek 	/* Allocate memory. */
378a9caca6aSWojciech A. Koszek 	rid = 0;
379a9caca6aSWojciech A. Koszek 	sc->mem_res = bus_alloc_resource_any(dev,
380a9caca6aSWojciech A. Koszek 		     SYS_RES_MEMORY, &rid, RF_ACTIVE);
381a9caca6aSWojciech A. Koszek 	if (sc->mem_res == NULL) {
382a9caca6aSWojciech A. Koszek 		device_printf(dev, "Can't allocate memory for device");
383a9caca6aSWojciech A. Koszek 		zy7_gpio_detach(dev);
384a9caca6aSWojciech A. Koszek 		return (ENOMEM);
385a9caca6aSWojciech A. Koszek 	}
386a9caca6aSWojciech A. Koszek 
3877836352bSLuiz Otavio O Souza 	sc->busdev = gpiobus_attach_bus(dev);
3887836352bSLuiz Otavio O Souza 	if (sc->busdev == NULL) {
3897836352bSLuiz Otavio O Souza 		zy7_gpio_detach(dev);
3907836352bSLuiz Otavio O Souza 		return (ENOMEM);
3917836352bSLuiz Otavio O Souza 	}
392a9caca6aSWojciech A. Koszek 
3937836352bSLuiz Otavio O Souza 	return (0);
394a9caca6aSWojciech A. Koszek }
395a9caca6aSWojciech A. Koszek 
396a9caca6aSWojciech A. Koszek static int
397a9caca6aSWojciech A. Koszek zy7_gpio_detach(device_t dev)
398a9caca6aSWojciech A. Koszek {
399a9caca6aSWojciech A. Koszek 	struct zy7_gpio_softc *sc = device_get_softc(dev);
400a9caca6aSWojciech A. Koszek 
4017836352bSLuiz Otavio O Souza 	gpiobus_detach_bus(dev);
402a9caca6aSWojciech A. Koszek 
403a9caca6aSWojciech A. Koszek 	if (sc->mem_res != NULL) {
404a9caca6aSWojciech A. Koszek 		/* Release memory resource. */
405a9caca6aSWojciech A. Koszek 		bus_release_resource(dev, SYS_RES_MEMORY,
406a9caca6aSWojciech A. Koszek 				     rman_get_rid(sc->mem_res), sc->mem_res);
407a9caca6aSWojciech A. Koszek 	}
408a9caca6aSWojciech A. Koszek 
409a9caca6aSWojciech A. Koszek 	ZGPIO_LOCK_DESTROY(sc);
410a9caca6aSWojciech A. Koszek 
411a9caca6aSWojciech A. Koszek 	return (0);
412a9caca6aSWojciech A. Koszek }
413a9caca6aSWojciech A. Koszek 
414a9caca6aSWojciech A. Koszek static device_method_t zy7_gpio_methods[] = {
415a9caca6aSWojciech A. Koszek 	/* device_if */
416a9caca6aSWojciech A. Koszek 	DEVMETHOD(device_probe, 	zy7_gpio_probe),
417a9caca6aSWojciech A. Koszek 	DEVMETHOD(device_attach, 	zy7_gpio_attach),
418a9caca6aSWojciech A. Koszek 	DEVMETHOD(device_detach, 	zy7_gpio_detach),
419a9caca6aSWojciech A. Koszek 
420a9caca6aSWojciech A. Koszek 	/* GPIO protocol */
4217836352bSLuiz Otavio O Souza 	DEVMETHOD(gpio_get_bus, 	zy7_gpio_get_bus),
422a9caca6aSWojciech A. Koszek 	DEVMETHOD(gpio_pin_max, 	zy7_gpio_pin_max),
423a9caca6aSWojciech A. Koszek 	DEVMETHOD(gpio_pin_getname, 	zy7_gpio_pin_getname),
424a9caca6aSWojciech A. Koszek 	DEVMETHOD(gpio_pin_getflags, 	zy7_gpio_pin_getflags),
425a9caca6aSWojciech A. Koszek 	DEVMETHOD(gpio_pin_getcaps, 	zy7_gpio_pin_getcaps),
426a9caca6aSWojciech A. Koszek 	DEVMETHOD(gpio_pin_setflags, 	zy7_gpio_pin_setflags),
427a9caca6aSWojciech A. Koszek 	DEVMETHOD(gpio_pin_get, 	zy7_gpio_pin_get),
428a9caca6aSWojciech A. Koszek 	DEVMETHOD(gpio_pin_set, 	zy7_gpio_pin_set),
429a9caca6aSWojciech A. Koszek 	DEVMETHOD(gpio_pin_toggle, 	zy7_gpio_pin_toggle),
430a9caca6aSWojciech A. Koszek 
431a9caca6aSWojciech A. Koszek 	DEVMETHOD_END
432a9caca6aSWojciech A. Koszek };
433a9caca6aSWojciech A. Koszek 
434a9caca6aSWojciech A. Koszek static driver_t zy7_gpio_driver = {
435c67d5d66SLuiz Otavio O Souza 	"gpio",
436a9caca6aSWojciech A. Koszek 	zy7_gpio_methods,
437a9caca6aSWojciech A. Koszek 	sizeof(struct zy7_gpio_softc),
438a9caca6aSWojciech A. Koszek };
439a9caca6aSWojciech A. Koszek 
440*b07fed81SEmmanuel Vadot EARLY_DRIVER_MODULE(zy7_gpio, simplebus, zy7_gpio_driver, 0, 0,
441*b07fed81SEmmanuel Vadot     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
442