vf_gpio.c revision 258057
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 258057 2013-11-12 18:02:56Z br $");
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
62#define	GPIO_PDOR(n)	(0x00 + 0x40 * (n >> 5))
63#define	GPIO_PSOR(n)	(0x04 + 0x40 * (n >> 5))
64#define	GPIO_PCOR(n)	(0x08 + 0x40 * (n >> 5))
65#define	GPIO_PTOR(n)	(0x0C + 0x40 * (n >> 5))
66#define	GPIO_PDIR(n)	(0x10 + 0x40 * (n >> 5))
67
68#define	GPIO_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
69#define	GPIO_UNLOCK(_sc)	mtx_unlock(&(_sc)->sc_mtx)
70
71#define	NPORTS		5
72#define	NGPIO		(NPORTS * 32)
73#define	DEFAULT_CAPS	(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)
74
75/*
76 * GPIO interface
77 */
78static int vf_gpio_pin_max(device_t, int *);
79static int vf_gpio_pin_getcaps(device_t, uint32_t, uint32_t *);
80static int vf_gpio_pin_getname(device_t, uint32_t, char *);
81static int vf_gpio_pin_getflags(device_t, uint32_t, uint32_t *);
82static int vf_gpio_pin_setflags(device_t, uint32_t, uint32_t);
83static int vf_gpio_pin_set(device_t, uint32_t, unsigned int);
84static int vf_gpio_pin_get(device_t, uint32_t, unsigned int *);
85static int vf_gpio_pin_toggle(device_t, uint32_t pin);
86
87struct vf_gpio_softc {
88	struct resource		*res[6];
89	bus_space_tag_t		bst;
90	bus_space_handle_t	bsh;
91
92	struct mtx		sc_mtx;
93	int			gpio_npins;
94	struct gpio_pin		gpio_pins[NGPIO];
95	void			*gpio_ih[NPORTS];
96};
97
98struct vf_gpio_softc *gpio_sc;
99
100static struct resource_spec vf_gpio_spec[] = {
101	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
102	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
103	{ SYS_RES_IRQ,		1,	RF_ACTIVE },
104	{ SYS_RES_IRQ,		2,	RF_ACTIVE },
105	{ SYS_RES_IRQ,		3,	RF_ACTIVE },
106	{ SYS_RES_IRQ,		4,	RF_ACTIVE },
107	{ -1, 0 }
108};
109
110static int
111vf_gpio_intr(void *arg)
112{
113	struct vf_gpio_softc *sc;
114	sc = arg;
115
116	/* TODO: interrupt handling */
117
118	return (FILTER_HANDLED);
119}
120
121
122static int
123vf_gpio_probe(device_t dev)
124{
125
126	if (!ofw_bus_is_compatible(dev, "fsl,mvf600-gpio"))
127		return (ENXIO);
128
129	device_set_desc(dev, "Vybrid Family GPIO Unit");
130	return (BUS_PROBE_DEFAULT);
131}
132
133static int
134vf_gpio_attach(device_t dev)
135{
136	struct vf_gpio_softc *sc;
137	int irq, i;
138
139	sc = device_get_softc(dev);
140	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
141
142	if (bus_alloc_resources(dev, vf_gpio_spec, sc->res)) {
143		device_printf(dev, "could not allocate resources\n");
144		return (ENXIO);
145	}
146
147	gpio_sc = sc;
148
149	/* Memory interface */
150	sc->bst = rman_get_bustag(sc->res[0]);
151	sc->bsh = rman_get_bushandle(sc->res[0]);
152
153	sc->gpio_npins = NGPIO;
154
155	for (irq = 0; irq < NPORTS; irq ++) {
156		if ((bus_setup_intr(dev, sc->res[1 + irq], INTR_TYPE_MISC,
157		    vf_gpio_intr, NULL, sc, &sc->gpio_ih[irq]))) {
158			device_printf(dev,
159			    "WARNING: unable to register interrupt handler\n");
160			return (ENXIO);
161		}
162	}
163
164	for (i = 0; i < sc->gpio_npins; i++) {
165		sc->gpio_pins[i].gp_pin = i;
166		sc->gpio_pins[i].gp_caps = DEFAULT_CAPS;
167		sc->gpio_pins[i].gp_flags =
168		    (READ4(sc, GPIO_PDOR(i)) & (1 << (i % 32))) ?
169		    GPIO_PIN_OUTPUT: GPIO_PIN_INPUT;
170		snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME,
171		    "vf_gpio%d.%d", device_get_unit(dev), i);
172	}
173
174	device_add_child(dev, "gpioc", device_get_unit(dev));
175	device_add_child(dev, "gpiobus", device_get_unit(dev));
176
177	return (bus_generic_attach(dev));
178}
179
180static int
181vf_gpio_pin_max(device_t dev, int *maxpin)
182{
183
184	*maxpin = NGPIO - 1;
185	return (0);
186}
187
188static int
189vf_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
190{
191	struct vf_gpio_softc *sc;
192	int i;
193
194	sc = device_get_softc(dev);
195	for (i = 0; i < sc->gpio_npins; i++) {
196		if (sc->gpio_pins[i].gp_pin == pin)
197			break;
198	}
199
200	if (i >= sc->gpio_npins)
201		return (EINVAL);
202
203	GPIO_LOCK(sc);
204	memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME);
205	GPIO_UNLOCK(sc);
206
207	return (0);
208}
209
210static int
211vf_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
212{
213	struct vf_gpio_softc *sc;
214	int i;
215
216	sc = device_get_softc(dev);
217	for (i = 0; i < sc->gpio_npins; i++) {
218		if (sc->gpio_pins[i].gp_pin == pin)
219			break;
220	}
221
222	if (i >= sc->gpio_npins)
223		return (EINVAL);
224
225	GPIO_LOCK(sc);
226	*caps = sc->gpio_pins[i].gp_caps;
227	GPIO_UNLOCK(sc);
228
229	return (0);
230}
231
232static int
233vf_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
234{
235	struct vf_gpio_softc *sc;
236	int i;
237
238	sc = device_get_softc(dev);
239	for (i = 0; i < sc->gpio_npins; i++) {
240		if (sc->gpio_pins[i].gp_pin == pin)
241			break;
242	}
243
244	if (i >= sc->gpio_npins)
245		return (EINVAL);
246
247	GPIO_LOCK(sc);
248	*flags = sc->gpio_pins[i].gp_flags;
249	GPIO_UNLOCK(sc);
250
251	return (0);
252}
253
254static int
255vf_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
256{
257	struct vf_gpio_softc *sc;
258	int i;
259
260	sc = device_get_softc(dev);
261	for (i = 0; i < sc->gpio_npins; i++) {
262		if (sc->gpio_pins[i].gp_pin == pin)
263			break;
264	}
265
266	if (i >= sc->gpio_npins)
267		return (EINVAL);
268
269	GPIO_LOCK(sc);
270	*val = (READ4(sc, GPIO_PDOR(i)) & (1 << (i % 32)));
271	GPIO_UNLOCK(sc);
272
273	return (0);
274}
275
276static int
277vf_gpio_pin_toggle(device_t dev, uint32_t pin)
278{
279	struct vf_gpio_softc *sc;
280	int i;
281
282	sc = device_get_softc(dev);
283	for (i = 0; i < sc->gpio_npins; i++) {
284		if (sc->gpio_pins[i].gp_pin == pin)
285			break;
286	}
287
288	if (i >= sc->gpio_npins)
289		return (EINVAL);
290
291	GPIO_LOCK(sc);
292	WRITE4(sc, GPIO_PTOR(i), (1 << (i % 32)));
293	GPIO_UNLOCK(sc);
294
295	return (0);
296}
297
298
299static void
300vf_gpio_pin_configure(struct vf_gpio_softc *sc, struct gpio_pin *pin,
301    unsigned int flags)
302{
303
304	GPIO_LOCK(sc);
305
306	/*
307	 * Manage input/output
308	 */
309	if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) {
310		pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT);
311		if (flags & GPIO_PIN_OUTPUT) {
312			pin->gp_flags |= GPIO_PIN_OUTPUT;
313
314		} else {
315			pin->gp_flags |= GPIO_PIN_INPUT;
316			WRITE4(sc, GPIO_PCOR(pin->gp_pin),
317			    (1 << (pin->gp_pin % 32)));
318		}
319	}
320
321	GPIO_UNLOCK(sc);
322}
323
324
325static int
326vf_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
327{
328	struct vf_gpio_softc *sc;
329	int i;
330
331	sc = device_get_softc(dev);
332	for (i = 0; i < sc->gpio_npins; i++) {
333		if (sc->gpio_pins[i].gp_pin == pin)
334			break;
335	}
336
337	if (i >= sc->gpio_npins)
338		return (EINVAL);
339
340	/* Check for unwanted flags. */
341	if ((flags & sc->gpio_pins[i].gp_caps) != flags)
342		return (EINVAL);
343
344	/* Can't mix input/output together */
345	if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) ==
346	    (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT))
347		return (EINVAL);
348
349	vf_gpio_pin_configure(sc, &sc->gpio_pins[i], flags);
350
351	return (0);
352}
353
354static int
355vf_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
356{
357	struct vf_gpio_softc *sc;
358	int i;
359
360	sc = device_get_softc(dev);
361	for (i = 0; i < sc->gpio_npins; i++) {
362		if (sc->gpio_pins[i].gp_pin == pin)
363			break;
364	}
365
366	if (i >= sc->gpio_npins)
367		return (EINVAL);
368
369	GPIO_LOCK(sc);
370	if (value)
371		WRITE4(sc, GPIO_PSOR(i), (1 << (i % 32)));
372	else
373		WRITE4(sc, GPIO_PCOR(i), (1 << (i % 32)));
374	GPIO_UNLOCK(sc);
375
376	return (0);
377}
378
379static device_method_t vf_gpio_methods[] = {
380	DEVMETHOD(device_probe,		vf_gpio_probe),
381	DEVMETHOD(device_attach,	vf_gpio_attach),
382
383	/* GPIO protocol */
384	DEVMETHOD(gpio_pin_max,		vf_gpio_pin_max),
385	DEVMETHOD(gpio_pin_getname,	vf_gpio_pin_getname),
386	DEVMETHOD(gpio_pin_getcaps,	vf_gpio_pin_getcaps),
387	DEVMETHOD(gpio_pin_getflags,	vf_gpio_pin_getflags),
388	DEVMETHOD(gpio_pin_get,		vf_gpio_pin_get),
389	DEVMETHOD(gpio_pin_toggle,	vf_gpio_pin_toggle),
390	DEVMETHOD(gpio_pin_setflags,	vf_gpio_pin_setflags),
391	DEVMETHOD(gpio_pin_set,		vf_gpio_pin_set),
392	{ 0, 0 }
393};
394
395static driver_t vf_gpio_driver = {
396	"gpio",
397	vf_gpio_methods,
398	sizeof(struct vf_gpio_softc),
399};
400
401static devclass_t vf_gpio_devclass;
402
403DRIVER_MODULE(vf_gpio, simplebus, vf_gpio_driver, vf_gpio_devclass, 0, 0);
404