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