vf_gpio.c revision 278782
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: stable/10/sys/arm/freescale/vybrid/vf_gpio.c 278782 2015-02-14 20:37:33Z 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		return (ENXIO);
129	}
130
131	/* Memory interface */
132	sc->bst = rman_get_bustag(sc->res[0]);
133	sc->bsh = rman_get_bushandle(sc->res[0]);
134
135	gpio_sc = sc;
136
137	sc->gpio_npins = NGPIO;
138
139	for (i = 0; i < sc->gpio_npins; i++) {
140		sc->gpio_pins[i].gp_pin = i;
141		sc->gpio_pins[i].gp_caps = DEFAULT_CAPS;
142		sc->gpio_pins[i].gp_flags =
143		    (READ4(sc, GPIO_PDOR(i)) & (1 << (i % 32))) ?
144		    GPIO_PIN_OUTPUT: GPIO_PIN_INPUT;
145		snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME,
146		    "vf_gpio%d.%d", device_get_unit(dev), i);
147	}
148
149	device_add_child(dev, "gpioc", -1);
150	device_add_child(dev, "gpiobus", -1);
151
152	return (bus_generic_attach(dev));
153}
154
155static int
156vf_gpio_pin_max(device_t dev, int *maxpin)
157{
158
159	*maxpin = NGPIO - 1;
160	return (0);
161}
162
163static int
164vf_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
165{
166	struct vf_gpio_softc *sc;
167	int i;
168
169	sc = device_get_softc(dev);
170	for (i = 0; i < sc->gpio_npins; i++) {
171		if (sc->gpio_pins[i].gp_pin == pin)
172			break;
173	}
174
175	if (i >= sc->gpio_npins)
176		return (EINVAL);
177
178	GPIO_LOCK(sc);
179	memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME);
180	GPIO_UNLOCK(sc);
181
182	return (0);
183}
184
185static int
186vf_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
187{
188	struct vf_gpio_softc *sc;
189	int i;
190
191	sc = device_get_softc(dev);
192	for (i = 0; i < sc->gpio_npins; i++) {
193		if (sc->gpio_pins[i].gp_pin == pin)
194			break;
195	}
196
197	if (i >= sc->gpio_npins)
198		return (EINVAL);
199
200	GPIO_LOCK(sc);
201	*caps = sc->gpio_pins[i].gp_caps;
202	GPIO_UNLOCK(sc);
203
204	return (0);
205}
206
207static int
208vf_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
209{
210	struct vf_gpio_softc *sc;
211	int i;
212
213	sc = device_get_softc(dev);
214	for (i = 0; i < sc->gpio_npins; i++) {
215		if (sc->gpio_pins[i].gp_pin == pin)
216			break;
217	}
218
219	if (i >= sc->gpio_npins)
220		return (EINVAL);
221
222	GPIO_LOCK(sc);
223	*flags = sc->gpio_pins[i].gp_flags;
224	GPIO_UNLOCK(sc);
225
226	return (0);
227}
228
229static int
230vf_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
231{
232	struct vf_gpio_softc *sc;
233	int i;
234
235	sc = device_get_softc(dev);
236	for (i = 0; i < sc->gpio_npins; i++) {
237		if (sc->gpio_pins[i].gp_pin == pin)
238			break;
239	}
240
241	if (i >= sc->gpio_npins)
242		return (EINVAL);
243
244	GPIO_LOCK(sc);
245	*val = (READ4(sc, GPIO_PDOR(i)) & (1 << (i % 32)));
246	GPIO_UNLOCK(sc);
247
248	return (0);
249}
250
251static int
252vf_gpio_pin_toggle(device_t dev, uint32_t pin)
253{
254	struct vf_gpio_softc *sc;
255	int i;
256
257	sc = device_get_softc(dev);
258	for (i = 0; i < sc->gpio_npins; i++) {
259		if (sc->gpio_pins[i].gp_pin == pin)
260			break;
261	}
262
263	if (i >= sc->gpio_npins)
264		return (EINVAL);
265
266	GPIO_LOCK(sc);
267	WRITE4(sc, GPIO_PTOR(i), (1 << (i % 32)));
268	GPIO_UNLOCK(sc);
269
270	return (0);
271}
272
273
274static void
275vf_gpio_pin_configure(struct vf_gpio_softc *sc, struct gpio_pin *pin,
276    unsigned int flags)
277{
278
279	GPIO_LOCK(sc);
280
281	/*
282	 * Manage input/output
283	 */
284	if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) {
285		pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT);
286		if (flags & GPIO_PIN_OUTPUT) {
287			pin->gp_flags |= GPIO_PIN_OUTPUT;
288
289		} else {
290			pin->gp_flags |= GPIO_PIN_INPUT;
291			WRITE4(sc, GPIO_PCOR(pin->gp_pin),
292			    (1 << (pin->gp_pin % 32)));
293		}
294	}
295
296	GPIO_UNLOCK(sc);
297}
298
299
300static int
301vf_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
302{
303	struct vf_gpio_softc *sc;
304	int i;
305
306	sc = device_get_softc(dev);
307	for (i = 0; i < sc->gpio_npins; i++) {
308		if (sc->gpio_pins[i].gp_pin == pin)
309			break;
310	}
311
312	if (i >= sc->gpio_npins)
313		return (EINVAL);
314
315	/* Check for unwanted flags. */
316	if ((flags & sc->gpio_pins[i].gp_caps) != flags)
317		return (EINVAL);
318
319	/* Can't mix input/output together */
320	if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) ==
321	    (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT))
322		return (EINVAL);
323
324	vf_gpio_pin_configure(sc, &sc->gpio_pins[i], flags);
325
326	return (0);
327}
328
329static int
330vf_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
331{
332	struct vf_gpio_softc *sc;
333	int i;
334
335	sc = device_get_softc(dev);
336	for (i = 0; i < sc->gpio_npins; i++) {
337		if (sc->gpio_pins[i].gp_pin == pin)
338			break;
339	}
340
341	if (i >= sc->gpio_npins)
342		return (EINVAL);
343
344	GPIO_LOCK(sc);
345	if (value)
346		WRITE4(sc, GPIO_PSOR(i), (1 << (i % 32)));
347	else
348		WRITE4(sc, GPIO_PCOR(i), (1 << (i % 32)));
349	GPIO_UNLOCK(sc);
350
351	return (0);
352}
353
354static device_method_t vf_gpio_methods[] = {
355	DEVMETHOD(device_probe,		vf_gpio_probe),
356	DEVMETHOD(device_attach,	vf_gpio_attach),
357
358	/* GPIO protocol */
359	DEVMETHOD(gpio_pin_max,		vf_gpio_pin_max),
360	DEVMETHOD(gpio_pin_getname,	vf_gpio_pin_getname),
361	DEVMETHOD(gpio_pin_getcaps,	vf_gpio_pin_getcaps),
362	DEVMETHOD(gpio_pin_getflags,	vf_gpio_pin_getflags),
363	DEVMETHOD(gpio_pin_get,		vf_gpio_pin_get),
364	DEVMETHOD(gpio_pin_toggle,	vf_gpio_pin_toggle),
365	DEVMETHOD(gpio_pin_setflags,	vf_gpio_pin_setflags),
366	DEVMETHOD(gpio_pin_set,		vf_gpio_pin_set),
367	{ 0, 0 }
368};
369
370static driver_t vf_gpio_driver = {
371	"gpio",
372	vf_gpio_methods,
373	sizeof(struct vf_gpio_softc),
374};
375
376static devclass_t vf_gpio_devclass;
377
378DRIVER_MODULE(vf_gpio, simplebus, vf_gpio_driver, vf_gpio_devclass, 0, 0);
379