xref: /freebsd/sys/arm/xilinx/zy7_gpio.c (revision af3dc4a7ca7fdfbe1790f34b83024557a35d11f2)
1a9caca6aSWojciech A. Koszek /*-
2*af3dc4a7SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*af3dc4a7SPedro 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 
73a9caca6aSWojciech A. Koszek #define NUMBANKS	4
74a9caca6aSWojciech A. Koszek #define MAXPIN		(32*NUMBANKS)
75a9caca6aSWojciech A. Koszek 
76a9caca6aSWojciech A. Koszek #define MIO_PIN		0	/* pins 0-53 go to MIO */
77a9caca6aSWojciech A. Koszek #define NUM_MIO_PINS	54
78a9caca6aSWojciech A. Koszek #define EMIO_PIN	64	/* pins 64-127 go to PL */
79a9caca6aSWojciech A. Koszek #define NUM_EMIO_PINS	64
80a9caca6aSWojciech A. Koszek 
81a9caca6aSWojciech A. Koszek #define VALID_PIN(u)	(((u) >= MIO_PIN && (u) < MIO_PIN + NUM_MIO_PINS) || \
82a9caca6aSWojciech A. Koszek 			 ((u) >= EMIO_PIN && (u) < EMIO_PIN + NUM_EMIO_PINS))
83a9caca6aSWojciech A. Koszek 
84a9caca6aSWojciech A. Koszek #define ZGPIO_LOCK(sc)			mtx_lock(&(sc)->sc_mtx)
85a9caca6aSWojciech A. Koszek #define	ZGPIO_UNLOCK(sc)		mtx_unlock(&(sc)->sc_mtx)
86a9caca6aSWojciech A. Koszek #define ZGPIO_LOCK_INIT(sc) \
87a9caca6aSWojciech A. Koszek 	mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev),	\
88a9caca6aSWojciech A. Koszek 	    "gpio", MTX_DEF)
89a9caca6aSWojciech A. Koszek #define ZGPIO_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
90a9caca6aSWojciech A. Koszek 
91a9caca6aSWojciech A. Koszek struct zy7_gpio_softc {
92a9caca6aSWojciech A. Koszek 	device_t	dev;
937836352bSLuiz Otavio O Souza 	device_t	busdev;
94a9caca6aSWojciech A. Koszek 	struct mtx	sc_mtx;
95a9caca6aSWojciech A. Koszek 	struct resource *mem_res;	/* Memory resource */
96a9caca6aSWojciech A. Koszek };
97a9caca6aSWojciech A. Koszek 
98a9caca6aSWojciech A. Koszek #define WR4(sc, off, val)	bus_write_4((sc)->mem_res, (off), (val))
99a9caca6aSWojciech A. Koszek #define RD4(sc, off)		bus_read_4((sc)->mem_res, (off))
100a9caca6aSWojciech A. Koszek 
101a9caca6aSWojciech A. Koszek 
102a9caca6aSWojciech A. Koszek /* Xilinx Zynq-7000 GPIO register definitions:
103a9caca6aSWojciech A. Koszek  */
104a9caca6aSWojciech A. Koszek #define ZY7_GPIO_MASK_DATA_LSW(b)	(0x0000+8*(b))	/* maskable wr lo */
105a9caca6aSWojciech A. Koszek #define ZY7_GPIO_MASK_DATA_MSW(b)	(0x0004+8*(b))	/* maskable wr hi */
106a9caca6aSWojciech A. Koszek #define ZY7_GPIO_DATA(b)		(0x0040+4*(b))	/* in/out data */
107a9caca6aSWojciech A. Koszek #define ZY7_GPIO_DATA_RO(b)		(0x0060+4*(b))	/* input data */
108a9caca6aSWojciech A. Koszek 
109a9caca6aSWojciech A. Koszek #define ZY7_GPIO_DIRM(b)		(0x0204+0x40*(b)) /* direction mode */
110a9caca6aSWojciech A. Koszek #define ZY7_GPIO_OEN(b)			(0x0208+0x40*(b)) /* output enable */
111a9caca6aSWojciech A. Koszek #define ZY7_GPIO_INT_MASK(b)		(0x020c+0x40*(b)) /* int mask */
112a9caca6aSWojciech A. Koszek #define ZY7_GPIO_INT_EN(b)		(0x0210+0x40*(b)) /* int enable */
113a9caca6aSWojciech A. Koszek #define ZY7_GPIO_INT_DIS(b)		(0x0214+0x40*(b)) /* int disable */
114a9caca6aSWojciech A. Koszek #define ZY7_GPIO_INT_STAT(b)		(0x0218+0x40*(b)) /* int status */
115a9caca6aSWojciech A. Koszek #define ZY7_GPIO_INT_TYPE(b)		(0x021c+0x40*(b)) /* int type */
116a9caca6aSWojciech A. Koszek #define ZY7_GPIO_INT_POLARITY(b)	(0x0220+0x40*(b)) /* int polarity */
117a9caca6aSWojciech A. Koszek #define ZY7_GPIO_INT_ANY(b)		(0x0224+0x40*(b)) /* any edge */
118a9caca6aSWojciech A. Koszek 
1197836352bSLuiz Otavio O Souza static device_t
1207836352bSLuiz Otavio O Souza zy7_gpio_get_bus(device_t dev)
1217836352bSLuiz Otavio O Souza {
1227836352bSLuiz Otavio O Souza 	struct zy7_gpio_softc *sc;
1237836352bSLuiz Otavio O Souza 
1247836352bSLuiz Otavio O Souza 	sc = device_get_softc(dev);
1257836352bSLuiz Otavio O Souza 
1267836352bSLuiz Otavio O Souza 	return (sc->busdev);
1277836352bSLuiz Otavio O Souza }
128a9caca6aSWojciech A. Koszek 
129a9caca6aSWojciech A. Koszek static int
130a9caca6aSWojciech A. Koszek zy7_gpio_pin_max(device_t dev, int *maxpin)
131a9caca6aSWojciech A. Koszek {
132a9caca6aSWojciech A. Koszek 
133a9caca6aSWojciech A. Koszek 	*maxpin = MAXPIN;
134a9caca6aSWojciech A. Koszek 	return (0);
135a9caca6aSWojciech A. Koszek }
136a9caca6aSWojciech A. Koszek 
137a9caca6aSWojciech A. Koszek /* Get a specific pin's capabilities. */
138a9caca6aSWojciech A. Koszek static int
139a9caca6aSWojciech A. Koszek zy7_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
140a9caca6aSWojciech A. Koszek {
141a9caca6aSWojciech A. Koszek 
142a9caca6aSWojciech A. Koszek 	if (!VALID_PIN(pin))
143a9caca6aSWojciech A. Koszek 		return (EINVAL);
144a9caca6aSWojciech A. Koszek 
145a9caca6aSWojciech A. Koszek 	*caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_TRISTATE);
146a9caca6aSWojciech A. Koszek 
147a9caca6aSWojciech A. Koszek 	return (0);
148a9caca6aSWojciech A. Koszek }
149a9caca6aSWojciech A. Koszek 
150a9caca6aSWojciech A. Koszek /* Get a specific pin's name. */
151a9caca6aSWojciech A. Koszek static int
152a9caca6aSWojciech A. Koszek zy7_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
153a9caca6aSWojciech A. Koszek {
154a9caca6aSWojciech A. Koszek 
155a9caca6aSWojciech A. Koszek 	if (!VALID_PIN(pin))
156a9caca6aSWojciech A. Koszek 		return (EINVAL);
157a9caca6aSWojciech A. Koszek 
158a9caca6aSWojciech A. Koszek 	if (pin < NUM_MIO_PINS) {
159a9caca6aSWojciech A. Koszek 		snprintf(name, GPIOMAXNAME, "MIO_%d", pin);
160a9caca6aSWojciech A. Koszek 		name[GPIOMAXNAME - 1] = '\0';
161a9caca6aSWojciech A. Koszek 	} else {
162a9caca6aSWojciech A. Koszek 		snprintf(name, GPIOMAXNAME, "EMIO_%d", pin - EMIO_PIN);
163a9caca6aSWojciech A. Koszek 		name[GPIOMAXNAME - 1] = '\0';
164a9caca6aSWojciech A. Koszek 	}
165a9caca6aSWojciech A. Koszek 
166a9caca6aSWojciech A. Koszek 	return (0);
167a9caca6aSWojciech A. Koszek }
168a9caca6aSWojciech A. Koszek 
169a9caca6aSWojciech A. Koszek /* Get a specific pin's current in/out/tri state. */
170a9caca6aSWojciech A. Koszek static int
171a9caca6aSWojciech A. Koszek zy7_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
172a9caca6aSWojciech A. Koszek {
173a9caca6aSWojciech A. Koszek 	struct zy7_gpio_softc *sc = device_get_softc(dev);
174a9caca6aSWojciech A. Koszek 
175a9caca6aSWojciech A. Koszek 	if (!VALID_PIN(pin))
176a9caca6aSWojciech A. Koszek 		return (EINVAL);
177a9caca6aSWojciech A. Koszek 
178a9caca6aSWojciech A. Koszek 	ZGPIO_LOCK(sc);
179a9caca6aSWojciech A. Koszek 
180a9caca6aSWojciech A. Koszek 	if ((RD4(sc, ZY7_GPIO_DIRM(pin >> 5)) & (1 << (pin & 31))) != 0) {
181a9caca6aSWojciech A. Koszek 		/* output */
182a9caca6aSWojciech A. Koszek 		if ((RD4(sc, ZY7_GPIO_OEN(pin >> 5)) & (1 << (pin & 31))) == 0)
183a9caca6aSWojciech A. Koszek 			*flags = (GPIO_PIN_OUTPUT | GPIO_PIN_TRISTATE);
184a9caca6aSWojciech A. Koszek 		else
185a9caca6aSWojciech A. Koszek 			*flags = GPIO_PIN_OUTPUT;
186a9caca6aSWojciech A. Koszek 	} else
187a9caca6aSWojciech A. Koszek 		/* input */
188a9caca6aSWojciech A. Koszek 		*flags = GPIO_PIN_INPUT;
189a9caca6aSWojciech A. Koszek 
190a9caca6aSWojciech A. Koszek 	ZGPIO_UNLOCK(sc);
191a9caca6aSWojciech A. Koszek 
192a9caca6aSWojciech A. Koszek 	return (0);
193a9caca6aSWojciech A. Koszek }
194a9caca6aSWojciech A. Koszek 
195a9caca6aSWojciech A. Koszek /* Set a specific pin's in/out/tri state. */
196a9caca6aSWojciech A. Koszek static int
197a9caca6aSWojciech A. Koszek zy7_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
198a9caca6aSWojciech A. Koszek {
199a9caca6aSWojciech A. Koszek 	struct zy7_gpio_softc *sc = device_get_softc(dev);
200a9caca6aSWojciech A. Koszek 
201a9caca6aSWojciech A. Koszek 	if (!VALID_PIN(pin))
202a9caca6aSWojciech A. Koszek 		return (EINVAL);
203a9caca6aSWojciech A. Koszek 
204a9caca6aSWojciech A. Koszek 	ZGPIO_LOCK(sc);
205a9caca6aSWojciech A. Koszek 
206a9caca6aSWojciech A. Koszek 	if ((flags & GPIO_PIN_OUTPUT) != 0) {
207a9caca6aSWojciech A. Koszek 		/* Output.  Set or reset OEN too. */
208a9caca6aSWojciech A. Koszek 		WR4(sc, ZY7_GPIO_DIRM(pin >> 5),
209a9caca6aSWojciech A. Koszek 		    RD4(sc, ZY7_GPIO_DIRM(pin >> 5)) | (1 << (pin & 31)));
210a9caca6aSWojciech A. Koszek 
211a9caca6aSWojciech A. Koszek 		if ((flags & GPIO_PIN_TRISTATE) != 0)
212a9caca6aSWojciech A. Koszek 			WR4(sc, ZY7_GPIO_OEN(pin >> 5),
213a9caca6aSWojciech A. Koszek 			    RD4(sc, ZY7_GPIO_OEN(pin >> 5)) &
214a9caca6aSWojciech A. Koszek 			    ~(1 << (pin & 31)));
215a9caca6aSWojciech A. Koszek 		else
216a9caca6aSWojciech A. Koszek 			WR4(sc, ZY7_GPIO_OEN(pin >> 5),
217a9caca6aSWojciech A. Koszek 			    RD4(sc, ZY7_GPIO_OEN(pin >> 5)) |
218a9caca6aSWojciech A. Koszek 			    (1 << (pin & 31)));
219a9caca6aSWojciech A. Koszek 	} else {
220a9caca6aSWojciech A. Koszek 		/* Input.  Turn off OEN. */
221a9caca6aSWojciech A. Koszek 		WR4(sc, ZY7_GPIO_DIRM(pin >> 5),
222a9caca6aSWojciech A. Koszek 		    RD4(sc, ZY7_GPIO_DIRM(pin >> 5)) & ~(1 << (pin & 31)));
223a9caca6aSWojciech A. Koszek 		WR4(sc, ZY7_GPIO_OEN(pin >> 5),
224a9caca6aSWojciech A. Koszek 		    RD4(sc, ZY7_GPIO_OEN(pin >> 5)) & ~(1 << (pin & 31)));
225a9caca6aSWojciech A. Koszek 	}
226a9caca6aSWojciech A. Koszek 
227a9caca6aSWojciech A. Koszek 	ZGPIO_UNLOCK(sc);
228a9caca6aSWojciech A. Koszek 
229a9caca6aSWojciech A. Koszek 	return (0);
230a9caca6aSWojciech A. Koszek }
231a9caca6aSWojciech A. Koszek 
232a9caca6aSWojciech A. Koszek /* Set a specific output pin's value. */
233a9caca6aSWojciech A. Koszek static int
234a9caca6aSWojciech A. Koszek zy7_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
235a9caca6aSWojciech A. Koszek {
236a9caca6aSWojciech A. Koszek 	struct zy7_gpio_softc *sc = device_get_softc(dev);
237a9caca6aSWojciech A. Koszek 
238a9caca6aSWojciech A. Koszek 	if (!VALID_PIN(pin) || value > 1)
239a9caca6aSWojciech A. Koszek 		return (EINVAL);
240a9caca6aSWojciech A. Koszek 
241a9caca6aSWojciech A. Koszek 	/* Fancy register tricks allow atomic set or reset. */
242a9caca6aSWojciech A. Koszek 	if ((pin & 16) != 0)
243a9caca6aSWojciech A. Koszek 		WR4(sc, ZY7_GPIO_MASK_DATA_MSW(pin >> 5),
244a9caca6aSWojciech A. Koszek 		    (0xffff0000 ^ (0x10000 << (pin & 15))) |
245a9caca6aSWojciech A. Koszek 		    (value << (pin & 15)));
246a9caca6aSWojciech A. Koszek 	else
247a9caca6aSWojciech A. Koszek 		WR4(sc, ZY7_GPIO_MASK_DATA_LSW(pin >> 5),
248a9caca6aSWojciech A. Koszek 		    (0xffff0000 ^ (0x10000 << (pin & 15))) |
249a9caca6aSWojciech A. Koszek 		    (value << (pin & 15)));
250a9caca6aSWojciech A. Koszek 
251a9caca6aSWojciech A. Koszek 	return (0);
252a9caca6aSWojciech A. Koszek }
253a9caca6aSWojciech A. Koszek 
254a9caca6aSWojciech A. Koszek /* Get a specific pin's input value. */
255a9caca6aSWojciech A. Koszek static int
256a9caca6aSWojciech A. Koszek zy7_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value)
257a9caca6aSWojciech A. Koszek {
258a9caca6aSWojciech A. Koszek 	struct zy7_gpio_softc *sc = device_get_softc(dev);
259a9caca6aSWojciech A. Koszek 
260a9caca6aSWojciech A. Koszek 	if (!VALID_PIN(pin))
261a9caca6aSWojciech A. Koszek 		return (EINVAL);
262a9caca6aSWojciech A. Koszek 
263a9caca6aSWojciech A. Koszek 	*value = (RD4(sc, ZY7_GPIO_DATA_RO(pin >> 5)) >> (pin & 31)) & 1;
264a9caca6aSWojciech A. Koszek 
265a9caca6aSWojciech A. Koszek 	return (0);
266a9caca6aSWojciech A. Koszek }
267a9caca6aSWojciech A. Koszek 
268a9caca6aSWojciech A. Koszek /* Toggle a pin's output value. */
269a9caca6aSWojciech A. Koszek static int
270a9caca6aSWojciech A. Koszek zy7_gpio_pin_toggle(device_t dev, uint32_t pin)
271a9caca6aSWojciech A. Koszek {
272a9caca6aSWojciech A. Koszek 	struct zy7_gpio_softc *sc = device_get_softc(dev);
273a9caca6aSWojciech A. Koszek 
274a9caca6aSWojciech A. Koszek 	if (!VALID_PIN(pin))
275a9caca6aSWojciech A. Koszek 		return (EINVAL);
276a9caca6aSWojciech A. Koszek 
277a9caca6aSWojciech A. Koszek 	ZGPIO_LOCK(sc);
278a9caca6aSWojciech A. Koszek 
279a9caca6aSWojciech A. Koszek 	WR4(sc, ZY7_GPIO_DATA(pin >> 5),
280a9caca6aSWojciech A. Koszek 	    RD4(sc, ZY7_GPIO_DATA(pin >> 5)) ^ (1 << (pin & 31)));
281a9caca6aSWojciech A. Koszek 
282a9caca6aSWojciech A. Koszek 	ZGPIO_UNLOCK(sc);
283a9caca6aSWojciech A. Koszek 
284a9caca6aSWojciech A. Koszek 	return (0);
285a9caca6aSWojciech A. Koszek }
286a9caca6aSWojciech A. Koszek 
287a9caca6aSWojciech A. Koszek static int
288a9caca6aSWojciech A. Koszek zy7_gpio_probe(device_t dev)
289a9caca6aSWojciech A. Koszek {
290a9caca6aSWojciech A. Koszek 
291add35ed5SIan Lepore 	if (!ofw_bus_status_okay(dev))
292add35ed5SIan Lepore 		return (ENXIO);
293add35ed5SIan Lepore 
294a9caca6aSWojciech A. Koszek 	if (!ofw_bus_is_compatible(dev, "xlnx,zy7_gpio"))
295a9caca6aSWojciech A. Koszek 		return (ENXIO);
296a9caca6aSWojciech A. Koszek 
297a9caca6aSWojciech A. Koszek 	device_set_desc(dev, "Zynq-7000 GPIO driver");
298a9caca6aSWojciech A. Koszek 	return (0);
299a9caca6aSWojciech A. Koszek }
300a9caca6aSWojciech A. Koszek 
301a9caca6aSWojciech A. Koszek static void
302a9caca6aSWojciech A. Koszek zy7_gpio_hw_reset(struct zy7_gpio_softc *sc)
303a9caca6aSWojciech A. Koszek {
304a9caca6aSWojciech A. Koszek 	int i;
305a9caca6aSWojciech A. Koszek 
306a9caca6aSWojciech A. Koszek 	for (i = 0; i < NUMBANKS; i++) {
307a9caca6aSWojciech A. Koszek 		WR4(sc, ZY7_GPIO_DATA(i), 0);
308a9caca6aSWojciech A. Koszek 		WR4(sc, ZY7_GPIO_DIRM(i), 0);
309a9caca6aSWojciech A. Koszek 		WR4(sc, ZY7_GPIO_OEN(i), 0);
310a9caca6aSWojciech A. Koszek 		WR4(sc, ZY7_GPIO_INT_DIS(i), 0xffffffff);
311a9caca6aSWojciech A. Koszek 		WR4(sc, ZY7_GPIO_INT_POLARITY(i), 0);
312a9caca6aSWojciech A. Koszek 		WR4(sc, ZY7_GPIO_INT_TYPE(i),
313a9caca6aSWojciech A. Koszek 		    i == 1 ? 0x003fffff : 0xffffffff);
314a9caca6aSWojciech A. Koszek 		WR4(sc, ZY7_GPIO_INT_ANY(i), 0);
315a9caca6aSWojciech A. Koszek 		WR4(sc, ZY7_GPIO_INT_STAT(i), 0xffffffff);
316a9caca6aSWojciech A. Koszek 	}
317a9caca6aSWojciech A. Koszek }
318a9caca6aSWojciech A. Koszek 
319a9caca6aSWojciech A. Koszek static int zy7_gpio_detach(device_t dev);
320a9caca6aSWojciech A. Koszek 
321a9caca6aSWojciech A. Koszek static int
322a9caca6aSWojciech A. Koszek zy7_gpio_attach(device_t dev)
323a9caca6aSWojciech A. Koszek {
324a9caca6aSWojciech A. Koszek 	struct zy7_gpio_softc *sc = device_get_softc(dev);
325a9caca6aSWojciech A. Koszek 	int rid;
326a9caca6aSWojciech A. Koszek 
327a9caca6aSWojciech A. Koszek 	sc->dev = dev;
328a9caca6aSWojciech A. Koszek 
329a9caca6aSWojciech A. Koszek 	ZGPIO_LOCK_INIT(sc);
330a9caca6aSWojciech A. Koszek 
331a9caca6aSWojciech A. Koszek 	/* Allocate memory. */
332a9caca6aSWojciech A. Koszek 	rid = 0;
333a9caca6aSWojciech A. Koszek 	sc->mem_res = bus_alloc_resource_any(dev,
334a9caca6aSWojciech A. Koszek 		     SYS_RES_MEMORY, &rid, RF_ACTIVE);
335a9caca6aSWojciech A. Koszek 	if (sc->mem_res == NULL) {
336a9caca6aSWojciech A. Koszek 		device_printf(dev, "Can't allocate memory for device");
337a9caca6aSWojciech A. Koszek 		zy7_gpio_detach(dev);
338a9caca6aSWojciech A. Koszek 		return (ENOMEM);
339a9caca6aSWojciech A. Koszek 	}
340a9caca6aSWojciech A. Koszek 
341a9caca6aSWojciech A. Koszek 	/* Completely reset. */
342a9caca6aSWojciech A. Koszek 	zy7_gpio_hw_reset(sc);
343a9caca6aSWojciech A. Koszek 
3447836352bSLuiz Otavio O Souza 	sc->busdev = gpiobus_attach_bus(dev);
3457836352bSLuiz Otavio O Souza 	if (sc->busdev == NULL) {
3467836352bSLuiz Otavio O Souza 		zy7_gpio_detach(dev);
3477836352bSLuiz Otavio O Souza 		return (ENOMEM);
3487836352bSLuiz Otavio O Souza 	}
349a9caca6aSWojciech A. Koszek 
3507836352bSLuiz Otavio O Souza 	return (0);
351a9caca6aSWojciech A. Koszek }
352a9caca6aSWojciech A. Koszek 
353a9caca6aSWojciech A. Koszek static int
354a9caca6aSWojciech A. Koszek zy7_gpio_detach(device_t dev)
355a9caca6aSWojciech A. Koszek {
356a9caca6aSWojciech A. Koszek 	struct zy7_gpio_softc *sc = device_get_softc(dev);
357a9caca6aSWojciech A. Koszek 
3587836352bSLuiz Otavio O Souza 	gpiobus_detach_bus(dev);
359a9caca6aSWojciech A. Koszek 
360a9caca6aSWojciech A. Koszek 	if (sc->mem_res != NULL) {
361a9caca6aSWojciech A. Koszek 		/* Release memory resource. */
362a9caca6aSWojciech A. Koszek 		bus_release_resource(dev, SYS_RES_MEMORY,
363a9caca6aSWojciech A. Koszek 				     rman_get_rid(sc->mem_res), sc->mem_res);
364a9caca6aSWojciech A. Koszek 	}
365a9caca6aSWojciech A. Koszek 
366a9caca6aSWojciech A. Koszek 	ZGPIO_LOCK_DESTROY(sc);
367a9caca6aSWojciech A. Koszek 
368a9caca6aSWojciech A. Koszek 	return (0);
369a9caca6aSWojciech A. Koszek }
370a9caca6aSWojciech A. Koszek 
371a9caca6aSWojciech A. Koszek static device_method_t zy7_gpio_methods[] = {
372a9caca6aSWojciech A. Koszek 	/* device_if */
373a9caca6aSWojciech A. Koszek 	DEVMETHOD(device_probe, 	zy7_gpio_probe),
374a9caca6aSWojciech A. Koszek 	DEVMETHOD(device_attach, 	zy7_gpio_attach),
375a9caca6aSWojciech A. Koszek 	DEVMETHOD(device_detach, 	zy7_gpio_detach),
376a9caca6aSWojciech A. Koszek 
377a9caca6aSWojciech A. Koszek 	/* GPIO protocol */
3787836352bSLuiz Otavio O Souza 	DEVMETHOD(gpio_get_bus, 	zy7_gpio_get_bus),
379a9caca6aSWojciech A. Koszek 	DEVMETHOD(gpio_pin_max, 	zy7_gpio_pin_max),
380a9caca6aSWojciech A. Koszek 	DEVMETHOD(gpio_pin_getname, 	zy7_gpio_pin_getname),
381a9caca6aSWojciech A. Koszek 	DEVMETHOD(gpio_pin_getflags, 	zy7_gpio_pin_getflags),
382a9caca6aSWojciech A. Koszek 	DEVMETHOD(gpio_pin_getcaps, 	zy7_gpio_pin_getcaps),
383a9caca6aSWojciech A. Koszek 	DEVMETHOD(gpio_pin_setflags, 	zy7_gpio_pin_setflags),
384a9caca6aSWojciech A. Koszek 	DEVMETHOD(gpio_pin_get, 	zy7_gpio_pin_get),
385a9caca6aSWojciech A. Koszek 	DEVMETHOD(gpio_pin_set, 	zy7_gpio_pin_set),
386a9caca6aSWojciech A. Koszek 	DEVMETHOD(gpio_pin_toggle, 	zy7_gpio_pin_toggle),
387a9caca6aSWojciech A. Koszek 
388a9caca6aSWojciech A. Koszek 	DEVMETHOD_END
389a9caca6aSWojciech A. Koszek };
390a9caca6aSWojciech A. Koszek 
391a9caca6aSWojciech A. Koszek static driver_t zy7_gpio_driver = {
392c67d5d66SLuiz Otavio O Souza 	"gpio",
393a9caca6aSWojciech A. Koszek 	zy7_gpio_methods,
394a9caca6aSWojciech A. Koszek 	sizeof(struct zy7_gpio_softc),
395a9caca6aSWojciech A. Koszek };
396a9caca6aSWojciech A. Koszek static devclass_t zy7_gpio_devclass;
397a9caca6aSWojciech A. Koszek 
398a9caca6aSWojciech A. Koszek DRIVER_MODULE(zy7_gpio, simplebus, zy7_gpio_driver, zy7_gpio_devclass, \
399a9caca6aSWojciech A. Koszek 	      NULL, NULL);
400