xref: /freebsd/sys/riscv/sifive/sifive_gpio.c (revision bb32809b7429262a5fec4defe4deb8d1c603dd8a)
1b47e5c5dSJessica Clarke /*-
2b47e5c5dSJessica Clarke  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3b47e5c5dSJessica Clarke  *
4b47e5c5dSJessica Clarke  * Copyright (c) 2021 Jessica Clarke <jrtc27@FreeBSD.org>
5b47e5c5dSJessica Clarke  *
6b47e5c5dSJessica Clarke  * Redistribution and use in source and binary forms, with or without
7b47e5c5dSJessica Clarke  * modification, are permitted provided that the following conditions
8b47e5c5dSJessica Clarke  * are met:
9b47e5c5dSJessica Clarke  * 1. Redistributions of source code must retain the above copyright
10b47e5c5dSJessica Clarke  *    notice, this list of conditions and the following disclaimer.
11b47e5c5dSJessica Clarke  * 2. Redistributions in binary form must reproduce the above copyright
12b47e5c5dSJessica Clarke  *    notice, this list of conditions and the following disclaimer in the
13b47e5c5dSJessica Clarke  *    documentation and/or other materials provided with the distribution.
14b47e5c5dSJessica Clarke  *
15b47e5c5dSJessica Clarke  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16b47e5c5dSJessica Clarke  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17b47e5c5dSJessica Clarke  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18b47e5c5dSJessica Clarke  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19b47e5c5dSJessica Clarke  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20b47e5c5dSJessica Clarke  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21b47e5c5dSJessica Clarke  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22b47e5c5dSJessica Clarke  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23b47e5c5dSJessica Clarke  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24b47e5c5dSJessica Clarke  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25b47e5c5dSJessica Clarke  * SUCH DAMAGE.
26b47e5c5dSJessica Clarke  *
27b47e5c5dSJessica Clarke  */
28b47e5c5dSJessica Clarke 
29b47e5c5dSJessica Clarke /* TODO: Provide interrupt controller interface */
30b47e5c5dSJessica Clarke 
31b47e5c5dSJessica Clarke #include <sys/cdefs.h>
32b47e5c5dSJessica Clarke __FBSDID("$FreeBSD$");
33b47e5c5dSJessica Clarke 
34b47e5c5dSJessica Clarke #include <sys/param.h>
35b47e5c5dSJessica Clarke #include <sys/systm.h>
36b47e5c5dSJessica Clarke #include <sys/bus.h>
37b47e5c5dSJessica Clarke #include <sys/kernel.h>
38b47e5c5dSJessica Clarke #include <sys/module.h>
39b47e5c5dSJessica Clarke #include <sys/rman.h>
40b47e5c5dSJessica Clarke #include <sys/lock.h>
41b47e5c5dSJessica Clarke #include <sys/mutex.h>
42b47e5c5dSJessica Clarke #include <sys/gpio.h>
43b47e5c5dSJessica Clarke 
44b47e5c5dSJessica Clarke #include <dev/gpio/gpiobusvar.h>
45b47e5c5dSJessica Clarke #include <dev/ofw/ofw_bus.h>
46b47e5c5dSJessica Clarke #include <dev/ofw/ofw_bus_subr.h>
47b47e5c5dSJessica Clarke 
48b47e5c5dSJessica Clarke #include <machine/bus.h>
49b47e5c5dSJessica Clarke 
50b47e5c5dSJessica Clarke /* Registers are 32-bit so can only fit 32 pins */
51b47e5c5dSJessica Clarke #define	SFGPIO_MAX_PINS		32
52b47e5c5dSJessica Clarke 
53b47e5c5dSJessica Clarke #define	SFGPIO_DEFAULT_CAPS	(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)
54b47e5c5dSJessica Clarke 
55b47e5c5dSJessica Clarke #define	SFGPIO_INPUT_VAL	0x0
56b47e5c5dSJessica Clarke #define	SFGPIO_INPUT_EN		0x4
57b47e5c5dSJessica Clarke #define	SFGPIO_OUTPUT_EN	0x8
58b47e5c5dSJessica Clarke #define	SFGPIO_OUTPUT_VAL	0xc
59b47e5c5dSJessica Clarke #define	SFGPIO_RISE_IE		0x18
60b47e5c5dSJessica Clarke #define	SFGPIO_RISE_IP		0x1c
61b47e5c5dSJessica Clarke #define	SFGPIO_FALL_IE		0x20
62b47e5c5dSJessica Clarke #define	SFGPIO_FALL_IP		0x24
63b47e5c5dSJessica Clarke #define	SFGPIO_HIGH_IE		0x28
64b47e5c5dSJessica Clarke #define	SFGPIO_HIGH_IP		0x2c
65b47e5c5dSJessica Clarke #define	SFGPIO_LOW_IE		0x30
66b47e5c5dSJessica Clarke #define	SFGPIO_LOW_IP		0x34
67b47e5c5dSJessica Clarke 
68b47e5c5dSJessica Clarke struct sfgpio_softc {
69b47e5c5dSJessica Clarke 	device_t	dev;
70b47e5c5dSJessica Clarke 	device_t	busdev;
71b47e5c5dSJessica Clarke 	struct mtx	mtx;
72b47e5c5dSJessica Clarke 	struct resource	*mem_res;
73b47e5c5dSJessica Clarke 	int		mem_rid;
74b47e5c5dSJessica Clarke 	struct resource	*irq_res;
75b47e5c5dSJessica Clarke 	int		irq_rid;
76b47e5c5dSJessica Clarke 	int		npins;
77b47e5c5dSJessica Clarke 	struct gpio_pin	gpio_pins[SFGPIO_MAX_PINS];
78b47e5c5dSJessica Clarke };
79b47e5c5dSJessica Clarke 
80b47e5c5dSJessica Clarke #define	SFGPIO_LOCK(_sc)	mtx_lock(&(_sc)->mtx)
81b47e5c5dSJessica Clarke #define	SFGPIO_UNLOCK(_sc)	mtx_unlock(&(_sc)->mtx)
82b47e5c5dSJessica Clarke 
83b47e5c5dSJessica Clarke #define	SFGPIO_READ(_sc, _off)		\
84b47e5c5dSJessica Clarke     bus_read_4((_sc)->mem_res, (_off))
85b47e5c5dSJessica Clarke #define	SFGPIO_WRITE(_sc, _off, _val)	\
86b47e5c5dSJessica Clarke     bus_write_4((_sc)->mem_res, (_off), (_val))
87b47e5c5dSJessica Clarke 
88b47e5c5dSJessica Clarke static struct ofw_compat_data compat_data[] = {
89b47e5c5dSJessica Clarke 	{ "sifive,gpio0",	1 },
90b47e5c5dSJessica Clarke 	{ NULL,			0 },
91b47e5c5dSJessica Clarke };
92b47e5c5dSJessica Clarke 
93b47e5c5dSJessica Clarke static int
94b47e5c5dSJessica Clarke sfgpio_probe(device_t dev)
95b47e5c5dSJessica Clarke {
96b47e5c5dSJessica Clarke 	if (!ofw_bus_status_okay(dev))
97b47e5c5dSJessica Clarke 		return (ENXIO);
98b47e5c5dSJessica Clarke 
99b47e5c5dSJessica Clarke 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
100b47e5c5dSJessica Clarke 		return (ENXIO);
101b47e5c5dSJessica Clarke 
102b47e5c5dSJessica Clarke 	device_set_desc(dev, "SiFive GPIO Controller");
103b47e5c5dSJessica Clarke 
104b47e5c5dSJessica Clarke 	return (BUS_PROBE_DEFAULT);
105b47e5c5dSJessica Clarke }
106b47e5c5dSJessica Clarke 
107b47e5c5dSJessica Clarke static int
108b47e5c5dSJessica Clarke sfgpio_attach(device_t dev)
109b47e5c5dSJessica Clarke {
110b47e5c5dSJessica Clarke 	struct sfgpio_softc *sc;
111b47e5c5dSJessica Clarke 	phandle_t node;
112b47e5c5dSJessica Clarke 	int error, i;
113b47e5c5dSJessica Clarke 	pcell_t npins;
114b47e5c5dSJessica Clarke 	uint32_t input_en, output_en;
115b47e5c5dSJessica Clarke 
116b47e5c5dSJessica Clarke 	sc = device_get_softc(dev);
117b47e5c5dSJessica Clarke 	sc->dev = dev;
118b47e5c5dSJessica Clarke 
119b47e5c5dSJessica Clarke 	node = ofw_bus_get_node(dev);
120b47e5c5dSJessica Clarke 
121b47e5c5dSJessica Clarke 	mtx_init(&sc->mtx, device_get_nameunit(sc->dev), NULL, MTX_DEF);
122b47e5c5dSJessica Clarke 
123b47e5c5dSJessica Clarke 	sc->mem_rid = 0;
124b47e5c5dSJessica Clarke 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
125b47e5c5dSJessica Clarke 	    &sc->mem_rid, RF_ACTIVE);
126b47e5c5dSJessica Clarke 	if (sc->mem_res == NULL) {
127b47e5c5dSJessica Clarke 		device_printf(dev, "Cannot allocate memory resource\n");
128b47e5c5dSJessica Clarke 		error = ENXIO;
129b47e5c5dSJessica Clarke 		goto fail;
130b47e5c5dSJessica Clarke 	}
131b47e5c5dSJessica Clarke 
132b47e5c5dSJessica Clarke 	if (OF_getencprop(node, "ngpios", &npins, sizeof(npins)) <= 0) {
133b47e5c5dSJessica Clarke 		/* Optional; defaults to 16 */
134b47e5c5dSJessica Clarke 		npins = 16;
135b47e5c5dSJessica Clarke 	} else if (npins > SFGPIO_MAX_PINS) {
136b47e5c5dSJessica Clarke 		device_printf(dev, "Too many pins: %d\n", npins);
137b47e5c5dSJessica Clarke 		error = ENXIO;
138b47e5c5dSJessica Clarke 		goto fail;
139b47e5c5dSJessica Clarke 	}
140b47e5c5dSJessica Clarke 	sc->npins = npins;
141b47e5c5dSJessica Clarke 
142b47e5c5dSJessica Clarke 	sc->irq_rid = 0;
143b47e5c5dSJessica Clarke 	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid,
144b47e5c5dSJessica Clarke 	    RF_ACTIVE);
145b47e5c5dSJessica Clarke 	if (sc->irq_res == NULL) {
146b47e5c5dSJessica Clarke 		device_printf(dev, "Cannot allocate IRQ resource\n");
147b47e5c5dSJessica Clarke 		error = ENXIO;
148b47e5c5dSJessica Clarke 		goto fail;
149b47e5c5dSJessica Clarke 	}
150b47e5c5dSJessica Clarke 
151b47e5c5dSJessica Clarke 	input_en = SFGPIO_READ(sc, SFGPIO_INPUT_EN);
152b47e5c5dSJessica Clarke 	output_en = SFGPIO_READ(sc, SFGPIO_OUTPUT_EN);
153b47e5c5dSJessica Clarke 	for (i = 0; i < sc->npins; ++i) {
154b47e5c5dSJessica Clarke 		sc->gpio_pins[i].gp_pin = i;
155b47e5c5dSJessica Clarke 		sc->gpio_pins[i].gp_caps = SFGPIO_DEFAULT_CAPS;
156b47e5c5dSJessica Clarke 		sc->gpio_pins[i].gp_flags =
157b47e5c5dSJessica Clarke 		    ((input_en & (1u << i)) ? GPIO_PIN_INPUT : 0) |
158b47e5c5dSJessica Clarke 		    ((output_en & (1u << i)) ? GPIO_PIN_OUTPUT : 0);
159b47e5c5dSJessica Clarke 		snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, "GPIO%d", i);
160b47e5c5dSJessica Clarke 		sc->gpio_pins[i].gp_name[GPIOMAXNAME - 1] = '\0';
161b47e5c5dSJessica Clarke 	}
162b47e5c5dSJessica Clarke 
163b47e5c5dSJessica Clarke 	sc->busdev = gpiobus_attach_bus(dev);
164b47e5c5dSJessica Clarke 	if (sc->busdev == NULL) {
165b47e5c5dSJessica Clarke 		device_printf(dev, "Cannot attach gpiobus\n");
166b47e5c5dSJessica Clarke 		error = ENXIO;
167b47e5c5dSJessica Clarke 		goto fail;
168b47e5c5dSJessica Clarke 	}
169b47e5c5dSJessica Clarke 
170b47e5c5dSJessica Clarke 	return (0);
171b47e5c5dSJessica Clarke 
172b47e5c5dSJessica Clarke fail:
173b47e5c5dSJessica Clarke 	if (sc->busdev != NULL)
174b47e5c5dSJessica Clarke 		gpiobus_detach_bus(dev);
175b47e5c5dSJessica Clarke 	if (sc->irq_res != NULL)
176b47e5c5dSJessica Clarke 		bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid,
177b47e5c5dSJessica Clarke 		    sc->irq_res);
178b47e5c5dSJessica Clarke 	if (sc->mem_res != NULL)
179b47e5c5dSJessica Clarke 		bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid,
180b47e5c5dSJessica Clarke 		    sc->mem_res);
181b47e5c5dSJessica Clarke 	mtx_destroy(&sc->mtx);
182b47e5c5dSJessica Clarke 	return (error);
183b47e5c5dSJessica Clarke }
184b47e5c5dSJessica Clarke 
185b47e5c5dSJessica Clarke static device_t
186b47e5c5dSJessica Clarke sfgpio_get_bus(device_t dev)
187b47e5c5dSJessica Clarke {
188b47e5c5dSJessica Clarke 	struct sfgpio_softc *sc;
189b47e5c5dSJessica Clarke 
190b47e5c5dSJessica Clarke 	sc = device_get_softc(dev);
191b47e5c5dSJessica Clarke 
192b47e5c5dSJessica Clarke 	return (sc->busdev);
193b47e5c5dSJessica Clarke }
194b47e5c5dSJessica Clarke 
195b47e5c5dSJessica Clarke static int
196b47e5c5dSJessica Clarke sfgpio_pin_max(device_t dev, int *maxpin)
197b47e5c5dSJessica Clarke {
198b47e5c5dSJessica Clarke 	struct sfgpio_softc *sc;
199b47e5c5dSJessica Clarke 
200b47e5c5dSJessica Clarke 	sc = device_get_softc(dev);
201b47e5c5dSJessica Clarke 
202b47e5c5dSJessica Clarke 	*maxpin = sc->npins - 1;
203b47e5c5dSJessica Clarke 
204b47e5c5dSJessica Clarke 	return (0);
205b47e5c5dSJessica Clarke }
206b47e5c5dSJessica Clarke 
207b47e5c5dSJessica Clarke static int
208b47e5c5dSJessica Clarke sfgpio_pin_set(device_t dev, uint32_t pin, unsigned int val)
209b47e5c5dSJessica Clarke {
210b47e5c5dSJessica Clarke 	struct sfgpio_softc *sc;
211b47e5c5dSJessica Clarke 	uint32_t reg;
212b47e5c5dSJessica Clarke 
213b47e5c5dSJessica Clarke 	sc = device_get_softc(dev);
214b47e5c5dSJessica Clarke 
215b47e5c5dSJessica Clarke 	if (pin >= sc->npins)
216b47e5c5dSJessica Clarke 		return (EINVAL);
217b47e5c5dSJessica Clarke 
218b47e5c5dSJessica Clarke 	SFGPIO_LOCK(sc);
219b47e5c5dSJessica Clarke 	reg = SFGPIO_READ(sc, SFGPIO_OUTPUT_VAL);
220b47e5c5dSJessica Clarke 	if (val)
221b47e5c5dSJessica Clarke 		reg |= (1u << pin);
222b47e5c5dSJessica Clarke 	else
223b47e5c5dSJessica Clarke 		reg &= ~(1u << pin);
224b47e5c5dSJessica Clarke 	SFGPIO_WRITE(sc, SFGPIO_OUTPUT_VAL, reg);
225b47e5c5dSJessica Clarke 	SFGPIO_UNLOCK(sc);
226b47e5c5dSJessica Clarke 
227b47e5c5dSJessica Clarke 	return (0);
228b47e5c5dSJessica Clarke }
229b47e5c5dSJessica Clarke 
230b47e5c5dSJessica Clarke static int
231b47e5c5dSJessica Clarke sfgpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
232b47e5c5dSJessica Clarke {
233b47e5c5dSJessica Clarke 	struct sfgpio_softc *sc;
234b47e5c5dSJessica Clarke 	uint32_t reg;
235b47e5c5dSJessica Clarke 
236b47e5c5dSJessica Clarke 	sc = device_get_softc(dev);
237b47e5c5dSJessica Clarke 
238b47e5c5dSJessica Clarke 	if (pin >= sc->npins)
239b47e5c5dSJessica Clarke 		return (EINVAL);
240b47e5c5dSJessica Clarke 
241b47e5c5dSJessica Clarke 	SFGPIO_LOCK(sc);
242b47e5c5dSJessica Clarke 	if (sc->gpio_pins[pin].gp_flags & GPIO_PIN_OUTPUT)
243b47e5c5dSJessica Clarke 		reg = SFGPIO_READ(sc, SFGPIO_OUTPUT_VAL);
244b47e5c5dSJessica Clarke 	else
245b47e5c5dSJessica Clarke 		reg = SFGPIO_READ(sc, SFGPIO_INPUT_VAL);
246b47e5c5dSJessica Clarke 	*val = (reg & (1u << pin)) ? 1 : 0;
247b47e5c5dSJessica Clarke 	SFGPIO_UNLOCK(sc);
248b47e5c5dSJessica Clarke 
249b47e5c5dSJessica Clarke 	return (0);
250b47e5c5dSJessica Clarke }
251b47e5c5dSJessica Clarke 
252b47e5c5dSJessica Clarke static int
253b47e5c5dSJessica Clarke sfgpio_pin_toggle(device_t dev, uint32_t pin)
254b47e5c5dSJessica Clarke {
255b47e5c5dSJessica Clarke 	struct sfgpio_softc *sc;
256b47e5c5dSJessica Clarke 	uint32_t reg;
257b47e5c5dSJessica Clarke 
258b47e5c5dSJessica Clarke 	sc = device_get_softc(dev);
259b47e5c5dSJessica Clarke 
260b47e5c5dSJessica Clarke 	if (pin >= sc->npins)
261b47e5c5dSJessica Clarke 		return (EINVAL);
262b47e5c5dSJessica Clarke 
263b47e5c5dSJessica Clarke 	SFGPIO_LOCK(sc);
264b47e5c5dSJessica Clarke 	reg = SFGPIO_READ(sc, SFGPIO_OUTPUT_VAL);
265b47e5c5dSJessica Clarke 	reg ^= (1u << pin);
266b47e5c5dSJessica Clarke 	SFGPIO_WRITE(sc, SFGPIO_OUTPUT_VAL, reg);
267b47e5c5dSJessica Clarke 	SFGPIO_UNLOCK(sc);
268b47e5c5dSJessica Clarke 
269b47e5c5dSJessica Clarke 	return (0);
270b47e5c5dSJessica Clarke }
271b47e5c5dSJessica Clarke 
272b47e5c5dSJessica Clarke static int
273b47e5c5dSJessica Clarke sfgpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
274b47e5c5dSJessica Clarke {
275b47e5c5dSJessica Clarke 	struct sfgpio_softc *sc;
276b47e5c5dSJessica Clarke 
277b47e5c5dSJessica Clarke 	sc = device_get_softc(dev);
278b47e5c5dSJessica Clarke 
279b47e5c5dSJessica Clarke 	if (pin >= sc->npins)
280b47e5c5dSJessica Clarke 		return (EINVAL);
281b47e5c5dSJessica Clarke 
282b47e5c5dSJessica Clarke 	SFGPIO_LOCK(sc);
283b47e5c5dSJessica Clarke 	*caps = sc->gpio_pins[pin].gp_caps;
284b47e5c5dSJessica Clarke 	SFGPIO_UNLOCK(sc);
285b47e5c5dSJessica Clarke 
286b47e5c5dSJessica Clarke 	return (0);
287b47e5c5dSJessica Clarke }
288b47e5c5dSJessica Clarke 
289b47e5c5dSJessica Clarke static int
290b47e5c5dSJessica Clarke sfgpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
291b47e5c5dSJessica Clarke {
292b47e5c5dSJessica Clarke 	struct sfgpio_softc *sc;
293b47e5c5dSJessica Clarke 
294b47e5c5dSJessica Clarke 	sc = device_get_softc(dev);
295b47e5c5dSJessica Clarke 
296b47e5c5dSJessica Clarke 	if (pin >= sc->npins)
297b47e5c5dSJessica Clarke 		return (EINVAL);
298b47e5c5dSJessica Clarke 
299b47e5c5dSJessica Clarke 	SFGPIO_LOCK(sc);
300b47e5c5dSJessica Clarke 	*flags = sc->gpio_pins[pin].gp_flags;
301b47e5c5dSJessica Clarke 	SFGPIO_UNLOCK(sc);
302b47e5c5dSJessica Clarke 
303b47e5c5dSJessica Clarke 	return (0);
304b47e5c5dSJessica Clarke }
305b47e5c5dSJessica Clarke 
306b47e5c5dSJessica Clarke static int
307b47e5c5dSJessica Clarke sfgpio_pin_getname(device_t dev, uint32_t pin, char *name)
308b47e5c5dSJessica Clarke {
309b47e5c5dSJessica Clarke 	struct sfgpio_softc *sc;
310b47e5c5dSJessica Clarke 
311b47e5c5dSJessica Clarke 	sc = device_get_softc(dev);
312b47e5c5dSJessica Clarke 
313b47e5c5dSJessica Clarke 	if (pin >= sc->npins)
314b47e5c5dSJessica Clarke 		return (EINVAL);
315b47e5c5dSJessica Clarke 
316b47e5c5dSJessica Clarke 	SFGPIO_LOCK(sc);
317b47e5c5dSJessica Clarke 	memcpy(name, sc->gpio_pins[pin].gp_name, GPIOMAXNAME);
318b47e5c5dSJessica Clarke 	SFGPIO_UNLOCK(sc);
319b47e5c5dSJessica Clarke 
320b47e5c5dSJessica Clarke 	return (0);
321b47e5c5dSJessica Clarke }
322b47e5c5dSJessica Clarke 
323b47e5c5dSJessica Clarke static int
324b47e5c5dSJessica Clarke sfgpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
325b47e5c5dSJessica Clarke {
326b47e5c5dSJessica Clarke 	struct sfgpio_softc *sc;
327b47e5c5dSJessica Clarke 	uint32_t reg;
328b47e5c5dSJessica Clarke 
329b47e5c5dSJessica Clarke 	sc = device_get_softc(dev);
330b47e5c5dSJessica Clarke 
331b47e5c5dSJessica Clarke 	if (pin >= sc->npins)
332b47e5c5dSJessica Clarke 		return (EINVAL);
333b47e5c5dSJessica Clarke 
334b47e5c5dSJessica Clarke 	SFGPIO_LOCK(sc);
335b47e5c5dSJessica Clarke 
336b47e5c5dSJessica Clarke 	reg = SFGPIO_READ(sc, SFGPIO_INPUT_EN);
337b47e5c5dSJessica Clarke 	if (flags & GPIO_PIN_INPUT) {
338b47e5c5dSJessica Clarke 		reg |= (1u << pin);
339b47e5c5dSJessica Clarke 		sc->gpio_pins[pin].gp_flags |= GPIO_PIN_INPUT;
340b47e5c5dSJessica Clarke 	} else {
341b47e5c5dSJessica Clarke 		reg &= ~(1u << pin);
342b47e5c5dSJessica Clarke 		sc->gpio_pins[pin].gp_flags &= ~GPIO_PIN_INPUT;
343b47e5c5dSJessica Clarke 	}
344b47e5c5dSJessica Clarke 	SFGPIO_WRITE(sc, SFGPIO_INPUT_EN, reg);
345b47e5c5dSJessica Clarke 
346b47e5c5dSJessica Clarke 	reg = SFGPIO_READ(sc, SFGPIO_OUTPUT_EN);
347b47e5c5dSJessica Clarke 	if (flags & GPIO_PIN_OUTPUT) {
348b47e5c5dSJessica Clarke 		reg |= (1u << pin);
349b47e5c5dSJessica Clarke 		sc->gpio_pins[pin].gp_flags |= GPIO_PIN_OUTPUT;
350b47e5c5dSJessica Clarke 	} else {
351b47e5c5dSJessica Clarke 		reg &= ~(1u << pin);
352b47e5c5dSJessica Clarke 		sc->gpio_pins[pin].gp_flags &= ~GPIO_PIN_OUTPUT;
353b47e5c5dSJessica Clarke 	}
354b47e5c5dSJessica Clarke 	SFGPIO_WRITE(sc, SFGPIO_OUTPUT_EN, reg);
355b47e5c5dSJessica Clarke 
356b47e5c5dSJessica Clarke 	SFGPIO_UNLOCK(sc);
357b47e5c5dSJessica Clarke 
358b47e5c5dSJessica Clarke 	return (0);
359b47e5c5dSJessica Clarke }
360b47e5c5dSJessica Clarke 
361b47e5c5dSJessica Clarke static int
362b47e5c5dSJessica Clarke sfgpio_pin_access_32(device_t dev, uint32_t first_pin, uint32_t clear_pins,
363b47e5c5dSJessica Clarke     uint32_t change_pins, uint32_t *orig_pins)
364b47e5c5dSJessica Clarke {
365b47e5c5dSJessica Clarke 	struct sfgpio_softc *sc;
366b47e5c5dSJessica Clarke 	uint32_t reg;
367b47e5c5dSJessica Clarke 
368b47e5c5dSJessica Clarke 	if (first_pin != 0)
369b47e5c5dSJessica Clarke 		return (EINVAL);
370b47e5c5dSJessica Clarke 
371b47e5c5dSJessica Clarke 	sc = device_get_softc(dev);
372b47e5c5dSJessica Clarke 
373b47e5c5dSJessica Clarke 	SFGPIO_LOCK(sc);
374b47e5c5dSJessica Clarke 
375b47e5c5dSJessica Clarke 	reg = SFGPIO_READ(sc, SFGPIO_OUTPUT_VAL);
376b47e5c5dSJessica Clarke 
377b47e5c5dSJessica Clarke 	if (orig_pins != NULL)
378b47e5c5dSJessica Clarke 		/* Only input_val is implicitly masked by input_en */
379b47e5c5dSJessica Clarke 		*orig_pins = SFGPIO_READ(sc, SFGPIO_INPUT_VAL) |
380b47e5c5dSJessica Clarke 		     (reg & SFGPIO_READ(sc, SFGPIO_OUTPUT_EN));
381b47e5c5dSJessica Clarke 
382b47e5c5dSJessica Clarke 	if ((clear_pins | change_pins) != 0)
383b47e5c5dSJessica Clarke 		SFGPIO_WRITE(sc, SFGPIO_OUTPUT_VAL,
384b47e5c5dSJessica Clarke 		    (reg & ~clear_pins) ^ change_pins);
385b47e5c5dSJessica Clarke 
386b47e5c5dSJessica Clarke 	SFGPIO_UNLOCK(sc);
387b47e5c5dSJessica Clarke 
388b47e5c5dSJessica Clarke 	return (0);
389b47e5c5dSJessica Clarke }
390b47e5c5dSJessica Clarke 
391b47e5c5dSJessica Clarke static int
392b47e5c5dSJessica Clarke sfgpio_pin_config_32(device_t dev, uint32_t first_pin, uint32_t num_pins,
393b47e5c5dSJessica Clarke     uint32_t *pin_flags)
394b47e5c5dSJessica Clarke {
395b47e5c5dSJessica Clarke 	struct sfgpio_softc *sc;
396b47e5c5dSJessica Clarke 	uint32_t ireg, oreg;
397b47e5c5dSJessica Clarke 	int i;
398b47e5c5dSJessica Clarke 
399b47e5c5dSJessica Clarke 	sc = device_get_softc(dev);
400b47e5c5dSJessica Clarke 
401b47e5c5dSJessica Clarke 	if (first_pin != 0 || num_pins > sc->npins)
402b47e5c5dSJessica Clarke 		return (EINVAL);
403b47e5c5dSJessica Clarke 
404b47e5c5dSJessica Clarke 	SFGPIO_LOCK(sc);
405b47e5c5dSJessica Clarke 
406b47e5c5dSJessica Clarke 	ireg = SFGPIO_READ(sc, SFGPIO_INPUT_EN);
407b47e5c5dSJessica Clarke 	oreg = SFGPIO_READ(sc, SFGPIO_OUTPUT_EN);
408b47e5c5dSJessica Clarke 	for (i = 0; i < num_pins; ++i) {
409b47e5c5dSJessica Clarke 		if (pin_flags[i] & GPIO_PIN_INPUT) {
410b47e5c5dSJessica Clarke 			ireg |= (1u << i);
411b47e5c5dSJessica Clarke 			oreg &= ~(1u << i);
412b47e5c5dSJessica Clarke 			sc->gpio_pins[i].gp_flags |= GPIO_PIN_INPUT;
413b47e5c5dSJessica Clarke 			sc->gpio_pins[i].gp_flags &= ~GPIO_PIN_OUTPUT;
414b47e5c5dSJessica Clarke 		} else if (pin_flags[i] & GPIO_PIN_OUTPUT) {
415b47e5c5dSJessica Clarke 			ireg &= ~(1u << i);
416b47e5c5dSJessica Clarke 			oreg |= (1u << i);
417b47e5c5dSJessica Clarke 			sc->gpio_pins[i].gp_flags &= ~GPIO_PIN_INPUT;
418b47e5c5dSJessica Clarke 			sc->gpio_pins[i].gp_flags |= GPIO_PIN_OUTPUT;
419b47e5c5dSJessica Clarke 		}
420b47e5c5dSJessica Clarke 	}
421b47e5c5dSJessica Clarke 	SFGPIO_WRITE(sc, SFGPIO_INPUT_EN, ireg);
422b47e5c5dSJessica Clarke 	SFGPIO_WRITE(sc, SFGPIO_OUTPUT_EN, oreg);
423b47e5c5dSJessica Clarke 
424b47e5c5dSJessica Clarke 	SFGPIO_UNLOCK(sc);
425b47e5c5dSJessica Clarke 
426b47e5c5dSJessica Clarke 	return (0);
427b47e5c5dSJessica Clarke }
428b47e5c5dSJessica Clarke 
429b47e5c5dSJessica Clarke static phandle_t
430b47e5c5dSJessica Clarke sfgpio_get_node(device_t bus, device_t dev)
431b47e5c5dSJessica Clarke {
432b47e5c5dSJessica Clarke 	return (ofw_bus_get_node(bus));
433b47e5c5dSJessica Clarke }
434b47e5c5dSJessica Clarke 
435b47e5c5dSJessica Clarke static device_method_t sfgpio_methods[] = {
436b47e5c5dSJessica Clarke 	/* Device interface */
437b47e5c5dSJessica Clarke 	DEVMETHOD(device_probe,		sfgpio_probe),
438b47e5c5dSJessica Clarke 	DEVMETHOD(device_attach,	sfgpio_attach),
439b47e5c5dSJessica Clarke 
440b47e5c5dSJessica Clarke 	/* GPIO protocol */
441b47e5c5dSJessica Clarke 	DEVMETHOD(gpio_get_bus,		sfgpio_get_bus),
442b47e5c5dSJessica Clarke 	DEVMETHOD(gpio_pin_max,		sfgpio_pin_max),
443b47e5c5dSJessica Clarke 	DEVMETHOD(gpio_pin_set,		sfgpio_pin_set),
444b47e5c5dSJessica Clarke 	DEVMETHOD(gpio_pin_get,		sfgpio_pin_get),
445b47e5c5dSJessica Clarke 	DEVMETHOD(gpio_pin_toggle,	sfgpio_pin_toggle),
446b47e5c5dSJessica Clarke 	DEVMETHOD(gpio_pin_getcaps,	sfgpio_pin_getcaps),
447b47e5c5dSJessica Clarke 	DEVMETHOD(gpio_pin_getflags,	sfgpio_pin_getflags),
448b47e5c5dSJessica Clarke 	DEVMETHOD(gpio_pin_getname,	sfgpio_pin_getname),
449b47e5c5dSJessica Clarke 	DEVMETHOD(gpio_pin_setflags,	sfgpio_pin_setflags),
450b47e5c5dSJessica Clarke 	DEVMETHOD(gpio_pin_access_32,	sfgpio_pin_access_32),
451b47e5c5dSJessica Clarke 	DEVMETHOD(gpio_pin_config_32,	sfgpio_pin_config_32),
452b47e5c5dSJessica Clarke 
453b47e5c5dSJessica Clarke 	/* ofw_bus interface */
454b47e5c5dSJessica Clarke 	DEVMETHOD(ofw_bus_get_node,	sfgpio_get_node),
455b47e5c5dSJessica Clarke 
456b47e5c5dSJessica Clarke 	DEVMETHOD_END
457b47e5c5dSJessica Clarke };
458b47e5c5dSJessica Clarke 
459b47e5c5dSJessica Clarke DEFINE_CLASS_0(gpio, sfgpio_driver, sfgpio_methods,
460b47e5c5dSJessica Clarke     sizeof(struct sfgpio_softc));
461*bb32809bSJohn Baldwin EARLY_DRIVER_MODULE(gpio, simplebus, sfgpio_driver, 0, 0,
462b47e5c5dSJessica Clarke     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
463b47e5c5dSJessica Clarke MODULE_DEPEND(sfgpio, gpiobus, 1, 1, 1);
464