vf_gpio.c revision 277968
1/*-
2 * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27/*
28 * Vybrid Family General-Purpose Input/Output (GPIO)
29 * Chapter 7, Vybrid Reference Manual, Rev. 5, 07/2013
30 */
31
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD: head/sys/arm/freescale/vybrid/vf_gpio.c 277968 2015-01-31 12:17:07Z loos $");
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/bus.h>
38#include <sys/kernel.h>
39#include <sys/module.h>
40#include <sys/malloc.h>
41#include <sys/rman.h>
42#include <sys/timeet.h>
43#include <sys/timetc.h>
44#include <sys/watchdog.h>
45#include <sys/mutex.h>
46#include <sys/gpio.h>
47
48#include <dev/fdt/fdt_common.h>
49#include <dev/ofw/openfirm.h>
50#include <dev/ofw/ofw_bus.h>
51#include <dev/ofw/ofw_bus_subr.h>
52
53#include <machine/bus.h>
54#include <machine/fdt.h>
55#include <machine/cpu.h>
56#include <machine/intr.h>
57
58#include "gpio_if.h"
59
60#include <arm/freescale/vybrid/vf_common.h>
61#include <arm/freescale/vybrid/vf_port.h>
62
63#define	GPIO_PDOR(n)	(0x00 + 0x40 * (n >> 5))
64#define	GPIO_PSOR(n)	(0x04 + 0x40 * (n >> 5))
65#define	GPIO_PCOR(n)	(0x08 + 0x40 * (n >> 5))
66#define	GPIO_PTOR(n)	(0x0C + 0x40 * (n >> 5))
67#define	GPIO_PDIR(n)	(0x10 + 0x40 * (n >> 5))
68
69#define	GPIO_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
70#define	GPIO_UNLOCK(_sc)	mtx_unlock(&(_sc)->sc_mtx)
71
72#define	DEFAULT_CAPS	(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)
73
74/*
75 * GPIO interface
76 */
77static int vf_gpio_pin_max(device_t, int *);
78static int vf_gpio_pin_getcaps(device_t, uint32_t, uint32_t *);
79static int vf_gpio_pin_getname(device_t, uint32_t, char *);
80static int vf_gpio_pin_getflags(device_t, uint32_t, uint32_t *);
81static int vf_gpio_pin_setflags(device_t, uint32_t, uint32_t);
82static int vf_gpio_pin_set(device_t, uint32_t, unsigned int);
83static int vf_gpio_pin_get(device_t, uint32_t, unsigned int *);
84static int vf_gpio_pin_toggle(device_t, uint32_t pin);
85
86struct vf_gpio_softc {
87	struct resource		*res[1];
88	bus_space_tag_t		bst;
89	bus_space_handle_t	bsh;
90
91	struct mtx		sc_mtx;
92	int			gpio_npins;
93	struct gpio_pin		gpio_pins[NGPIO];
94};
95
96struct vf_gpio_softc *gpio_sc;
97
98static struct resource_spec vf_gpio_spec[] = {
99	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
100	{ -1, 0 }
101};
102
103static int
104vf_gpio_probe(device_t dev)
105{
106
107	if (!ofw_bus_status_okay(dev))
108		return (ENXIO);
109
110	if (!ofw_bus_is_compatible(dev, "fsl,mvf600-gpio"))
111		return (ENXIO);
112
113	device_set_desc(dev, "Vybrid Family GPIO Unit");
114	return (BUS_PROBE_DEFAULT);
115}
116
117static int
118vf_gpio_attach(device_t dev)
119{
120	struct vf_gpio_softc *sc;
121	int i;
122
123	sc = device_get_softc(dev);
124	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
125
126	if (bus_alloc_resources(dev, vf_gpio_spec, sc->res)) {
127		device_printf(dev, "could not allocate resources\n");
128		mtx_destroy(&sc->sc_mtx);
129		return (ENXIO);
130	}
131
132	/* Memory interface */
133	sc->bst = rman_get_bustag(sc->res[0]);
134	sc->bsh = rman_get_bushandle(sc->res[0]);
135
136	gpio_sc = sc;
137
138	sc->gpio_npins = NGPIO;
139
140	for (i = 0; i < sc->gpio_npins; i++) {
141		sc->gpio_pins[i].gp_pin = i;
142		sc->gpio_pins[i].gp_caps = DEFAULT_CAPS;
143		sc->gpio_pins[i].gp_flags =
144		    (READ4(sc, GPIO_PDOR(i)) & (1 << (i % 32))) ?
145		    GPIO_PIN_OUTPUT: GPIO_PIN_INPUT;
146		snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME,
147		    "vf_gpio%d.%d", device_get_unit(dev), i);
148	}
149
150	device_add_child(dev, "gpioc", -1);
151	device_add_child(dev, "gpiobus", -1);
152
153	return (bus_generic_attach(dev));
154}
155
156static int
157vf_gpio_pin_max(device_t dev, int *maxpin)
158{
159
160	*maxpin = NGPIO - 1;
161	return (0);
162}
163
164static int
165vf_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
166{
167	struct vf_gpio_softc *sc;
168	int i;
169
170	sc = device_get_softc(dev);
171	for (i = 0; i < sc->gpio_npins; i++) {
172		if (sc->gpio_pins[i].gp_pin == pin)
173			break;
174	}
175
176	if (i >= sc->gpio_npins)
177		return (EINVAL);
178
179	GPIO_LOCK(sc);
180	memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME);
181	GPIO_UNLOCK(sc);
182
183	return (0);
184}
185
186static int
187vf_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
188{
189	struct vf_gpio_softc *sc;
190	int i;
191
192	sc = device_get_softc(dev);
193	for (i = 0; i < sc->gpio_npins; i++) {
194		if (sc->gpio_pins[i].gp_pin == pin)
195			break;
196	}
197
198	if (i >= sc->gpio_npins)
199		return (EINVAL);
200
201	GPIO_LOCK(sc);
202	*caps = sc->gpio_pins[i].gp_caps;
203	GPIO_UNLOCK(sc);
204
205	return (0);
206}
207
208static int
209vf_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
210{
211	struct vf_gpio_softc *sc;
212	int i;
213
214	sc = device_get_softc(dev);
215	for (i = 0; i < sc->gpio_npins; i++) {
216		if (sc->gpio_pins[i].gp_pin == pin)
217			break;
218	}
219
220	if (i >= sc->gpio_npins)
221		return (EINVAL);
222
223	GPIO_LOCK(sc);
224	*flags = sc->gpio_pins[i].gp_flags;
225	GPIO_UNLOCK(sc);
226
227	return (0);
228}
229
230static int
231vf_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
232{
233	struct vf_gpio_softc *sc;
234	int i;
235
236	sc = device_get_softc(dev);
237	for (i = 0; i < sc->gpio_npins; i++) {
238		if (sc->gpio_pins[i].gp_pin == pin)
239			break;
240	}
241
242	if (i >= sc->gpio_npins)
243		return (EINVAL);
244
245	GPIO_LOCK(sc);
246	*val = (READ4(sc, GPIO_PDIR(i)) & (1 << (i % 32))) ? 1 : 0;
247	GPIO_UNLOCK(sc);
248
249	return (0);
250}
251
252static int
253vf_gpio_pin_toggle(device_t dev, uint32_t pin)
254{
255	struct vf_gpio_softc *sc;
256	int i;
257
258	sc = device_get_softc(dev);
259	for (i = 0; i < sc->gpio_npins; i++) {
260		if (sc->gpio_pins[i].gp_pin == pin)
261			break;
262	}
263
264	if (i >= sc->gpio_npins)
265		return (EINVAL);
266
267	GPIO_LOCK(sc);
268	WRITE4(sc, GPIO_PTOR(i), (1 << (i % 32)));
269	GPIO_UNLOCK(sc);
270
271	return (0);
272}
273
274
275static void
276vf_gpio_pin_configure(struct vf_gpio_softc *sc, struct gpio_pin *pin,
277    unsigned int flags)
278{
279
280	GPIO_LOCK(sc);
281
282	/*
283	 * Manage input/output
284	 */
285	if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) {
286		pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT);
287		if (flags & GPIO_PIN_OUTPUT) {
288			pin->gp_flags |= GPIO_PIN_OUTPUT;
289
290		} else {
291			pin->gp_flags |= GPIO_PIN_INPUT;
292			WRITE4(sc, GPIO_PCOR(pin->gp_pin),
293			    (1 << (pin->gp_pin % 32)));
294		}
295	}
296
297	GPIO_UNLOCK(sc);
298}
299
300
301static int
302vf_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
303{
304	struct vf_gpio_softc *sc;
305	int i;
306
307	sc = device_get_softc(dev);
308	for (i = 0; i < sc->gpio_npins; i++) {
309		if (sc->gpio_pins[i].gp_pin == pin)
310			break;
311	}
312
313	if (i >= sc->gpio_npins)
314		return (EINVAL);
315
316	vf_gpio_pin_configure(sc, &sc->gpio_pins[i], flags);
317
318	return (0);
319}
320
321static int
322vf_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
323{
324	struct vf_gpio_softc *sc;
325	int i;
326
327	sc = device_get_softc(dev);
328	for (i = 0; i < sc->gpio_npins; i++) {
329		if (sc->gpio_pins[i].gp_pin == pin)
330			break;
331	}
332
333	if (i >= sc->gpio_npins)
334		return (EINVAL);
335
336	GPIO_LOCK(sc);
337	if (value)
338		WRITE4(sc, GPIO_PSOR(i), (1 << (i % 32)));
339	else
340		WRITE4(sc, GPIO_PCOR(i), (1 << (i % 32)));
341	GPIO_UNLOCK(sc);
342
343	return (0);
344}
345
346static device_method_t vf_gpio_methods[] = {
347	DEVMETHOD(device_probe,		vf_gpio_probe),
348	DEVMETHOD(device_attach,	vf_gpio_attach),
349
350	/* GPIO protocol */
351	DEVMETHOD(gpio_pin_max,		vf_gpio_pin_max),
352	DEVMETHOD(gpio_pin_getname,	vf_gpio_pin_getname),
353	DEVMETHOD(gpio_pin_getcaps,	vf_gpio_pin_getcaps),
354	DEVMETHOD(gpio_pin_getflags,	vf_gpio_pin_getflags),
355	DEVMETHOD(gpio_pin_get,		vf_gpio_pin_get),
356	DEVMETHOD(gpio_pin_toggle,	vf_gpio_pin_toggle),
357	DEVMETHOD(gpio_pin_setflags,	vf_gpio_pin_setflags),
358	DEVMETHOD(gpio_pin_set,		vf_gpio_pin_set),
359	{ 0, 0 }
360};
361
362static driver_t vf_gpio_driver = {
363	"gpio",
364	vf_gpio_methods,
365	sizeof(struct vf_gpio_softc),
366};
367
368static devclass_t vf_gpio_devclass;
369
370DRIVER_MODULE(vf_gpio, simplebus, vf_gpio_driver, vf_gpio_devclass, 0, 0);
371