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