avila_gpio.c revision 214946
1/*-
2 * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org>
3 * Copyright (c) 2009, Luiz Otavio O Souza.
4 * Copyright (c) 2010, Andrew Thompson <thompsa@FreeBSD.org>
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 unmodified, this list of conditions, and the following
12 *    disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30/*
31 * GPIO driver for Gateworks Avilia
32 */
33
34#include <sys/cdefs.h>
35__FBSDID("$FreeBSD: head/sys/arm/xscale/ixp425/avila_gpio.c 214946 2010-11-07 20:33:39Z thompsa $");
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/bus.h>
40
41#include <sys/kernel.h>
42#include <sys/module.h>
43#include <sys/rman.h>
44#include <sys/lock.h>
45#include <sys/mutex.h>
46#include <sys/gpio.h>
47
48#include <machine/bus.h>
49#include <machine/resource.h>
50#include <arm/xscale/ixp425/ixp425reg.h>
51#include <arm/xscale/ixp425/ixp425var.h>
52
53#include "gpio_if.h"
54
55#define GPIO_SET_BITS(sc, reg, bits)	\
56	GPIO_CONF_WRITE_4(sc, reg, GPIO_CONF_READ_4(sc, (reg)) | (bits))
57
58#define GPIO_CLEAR_BITS(sc, reg, bits)	\
59	GPIO_CONF_WRITE_4(sc, reg, GPIO_CONF_READ_4(sc, (reg)) & ~(bits))
60
61#define GPIO_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
62#define GPIO_UNLOCK(_sc)	mtx_unlock(&(_sc)->sc_mtx)
63#define GPIO_LOCK_ASSERT(_sc)	mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
64
65struct avila_gpio_softc {
66	device_t		sc_dev;
67        struct mtx		sc_mtx;
68	bus_space_tag_t		sc_iot;
69	bus_space_handle_t	sc_gpio_ioh;
70	uint32_t		sc_valid;
71	struct gpio_pin		sc_pins[IXP4XX_GPIO_PINS];
72};
73
74struct avila_gpio_pin {
75	const char *name;
76	int pin;
77	int caps;
78};
79
80#define	GPIO_PIN_IO	(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)
81static struct avila_gpio_pin avila_gpio_pins[] = {
82	{ "GPIO0", 0, GPIO_PIN_IO },
83	{ "GPIO1", 1, GPIO_PIN_IO },
84	{ "GPIO2", 2, GPIO_PIN_IO },
85	{ "GPIO3", 3, GPIO_PIN_IO },
86	{ "GPIO4", 4, GPIO_PIN_IO },
87	/*
88	 * The following pins are connected to system devices and should not
89	 * really be frobbed.
90	 */
91#if 0
92	{ "SER_ENA", 5, GPIO_PIN_IO },
93	{ "I2C_SCL", 6, GPIO_PIN_IO },
94	{ "I2C_SDA", 7, GPIO_PIN_IO },
95	{ "PCI_INTD", 8, GPIO_PIN_IO },
96	{ "PCI_INTC", 9, GPIO_PIN_IO },
97	{ "PCI_INTB", 10, GPIO_PIN_IO },
98	{ "PCI_INTA", 11, GPIO_PIN_IO },
99	{ "ATA_INT", 12, GPIO_PIN_IO },
100	{ "PCI_RST", 13, GPIO_PIN_IO },
101	{ "PCI_CLK", 14, GPIO_PIN_OUTPUT },
102	{ "EX_CLK", 15, GPIO_PIN_OUTPUT },
103#endif
104};
105#undef GPIO_PIN_IO
106
107/*
108 * Helpers
109 */
110static void avila_gpio_pin_configure(struct avila_gpio_softc *sc,
111    struct gpio_pin *pin, uint32_t flags);
112static int  avila_gpio_pin_flags(struct avila_gpio_softc *sc, uint32_t pin);
113
114/*
115 * Driver stuff
116 */
117static int avila_gpio_probe(device_t dev);
118static int avila_gpio_attach(device_t dev);
119static int avila_gpio_detach(device_t dev);
120
121/*
122 * GPIO interface
123 */
124static int avila_gpio_pin_max(device_t dev, int *maxpin);
125static int avila_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps);
126static int avila_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t
127    *flags);
128static int avila_gpio_pin_getname(device_t dev, uint32_t pin, char *name);
129static int avila_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags);
130static int avila_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value);
131static int avila_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val);
132static int avila_gpio_pin_toggle(device_t dev, uint32_t pin);
133
134static int
135avila_gpio_pin_flags(struct avila_gpio_softc *sc, uint32_t pin)
136{
137	uint32_t v;
138
139	v = GPIO_CONF_READ_4(sc, IXP425_GPIO_GPINR) & (1 << pin);
140
141	return (v ? GPIO_PIN_INPUT : GPIO_PIN_OUTPUT);
142}
143
144static void
145avila_gpio_pin_configure(struct avila_gpio_softc *sc, struct gpio_pin *pin,
146    unsigned int flags)
147{
148	uint32_t mask;
149
150	mask = 1 << pin->gp_pin;
151	GPIO_LOCK(sc);
152
153	/*
154	 * Manage input/output
155	 */
156	if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) {
157		pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT);
158		if (flags & GPIO_PIN_OUTPUT) {
159			pin->gp_flags |= GPIO_PIN_OUTPUT;
160			GPIO_CLEAR_BITS(sc, IXP425_GPIO_GPOER, mask);
161		}
162		else {
163			pin->gp_flags |= GPIO_PIN_INPUT;
164			GPIO_SET_BITS(sc, IXP425_GPIO_GPOER, mask);
165		}
166	}
167
168	GPIO_UNLOCK(sc);
169}
170
171static int
172avila_gpio_pin_max(device_t dev, int *maxpin)
173{
174
175	*maxpin = IXP4XX_GPIO_PINS - 1;
176	return (0);
177}
178
179static int
180avila_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
181{
182	struct avila_gpio_softc *sc = device_get_softc(dev);
183
184	if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & (1 << pin)))
185		return (EINVAL);
186
187	GPIO_LOCK(sc);
188	*caps = sc->sc_pins[pin].gp_caps;
189	GPIO_UNLOCK(sc);
190
191	return (0);
192}
193
194static int
195avila_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
196{
197	struct avila_gpio_softc *sc = device_get_softc(dev);
198
199	if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & (1 << pin)))
200		return (EINVAL);
201
202	GPIO_LOCK(sc);
203	/* refresh since we do not own all the pins */
204	sc->sc_pins[pin].gp_flags = avila_gpio_pin_flags(sc, pin);
205	*flags = sc->sc_pins[pin].gp_flags;
206	GPIO_UNLOCK(sc);
207
208	return (0);
209}
210
211static int
212avila_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
213{
214	struct avila_gpio_softc *sc = device_get_softc(dev);
215
216	if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & (1 << pin)))
217		return (EINVAL);
218
219	GPIO_LOCK(sc);
220	memcpy(name, sc->sc_pins[pin].gp_name, GPIOMAXNAME);
221	GPIO_UNLOCK(sc);
222
223	return (0);
224}
225
226static int
227avila_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
228{
229	struct avila_gpio_softc *sc = device_get_softc(dev);
230	uint32_t mask = 1 << pin;
231
232	if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & mask))
233		return (EINVAL);
234
235	/* Filter out unwanted flags */
236	if ((flags &= sc->sc_pins[pin].gp_caps) != flags)
237		return (EINVAL);
238
239	/* Can't mix input/output together */
240	if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) ==
241	    (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT))
242		return (EINVAL);
243
244	avila_gpio_pin_configure(sc, &sc->sc_pins[pin], flags);
245	return (0);
246}
247
248static int
249avila_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
250{
251	struct avila_gpio_softc *sc = device_get_softc(dev);
252	uint32_t mask = 1 << pin;
253
254	if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & mask))
255		return (EINVAL);
256
257	GPIO_LOCK(sc);
258	if (value)
259		GPIO_SET_BITS(sc, IXP425_GPIO_GPOUTR, mask);
260	else
261		GPIO_CLEAR_BITS(sc, IXP425_GPIO_GPOUTR, mask);
262	GPIO_UNLOCK(sc);
263
264	return (0);
265}
266
267static int
268avila_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
269{
270	struct avila_gpio_softc *sc = device_get_softc(dev);
271
272	if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & (1 << pin)))
273		return (EINVAL);
274
275	GPIO_LOCK(sc);
276	*val = (GPIO_CONF_READ_4(sc, IXP425_GPIO_GPINR) & (1 << pin)) ? 1 : 0;
277	GPIO_UNLOCK(sc);
278
279	return (0);
280}
281
282static int
283avila_gpio_pin_toggle(device_t dev, uint32_t pin)
284{
285	struct avila_gpio_softc *sc = device_get_softc(dev);
286	uint32_t mask = 1 << pin;
287	int res;
288
289	if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & mask))
290		return (EINVAL);
291
292	GPIO_LOCK(sc);
293	res = (GPIO_CONF_READ_4(sc, IXP425_GPIO_GPINR) & mask) ? 1 : 0;
294	if (res)
295		GPIO_CLEAR_BITS(sc, IXP425_GPIO_GPOUTR, mask);
296	else
297		GPIO_SET_BITS(sc, IXP425_GPIO_GPOUTR, mask);
298	GPIO_UNLOCK(sc);
299
300	return (0);
301}
302
303static int
304avila_gpio_probe(device_t dev)
305{
306
307	device_set_desc(dev, "Gateworks Avila GPIO driver");
308	return (0);
309}
310
311static int
312avila_gpio_attach(device_t dev)
313{
314#define	N(a)	(sizeof(a) / sizeof(a[0]))
315	struct avila_gpio_softc *sc = device_get_softc(dev);
316	struct ixp425_softc *sa = device_get_softc(device_get_parent(dev));
317	int i;
318
319	sc->sc_dev = dev;
320	sc->sc_iot = sa->sc_iot;
321	sc->sc_gpio_ioh = sa->sc_gpio_ioh;
322
323	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
324	    MTX_DEF);
325
326	for (i = 0; i < N(avila_gpio_pins); i++) {
327		struct avila_gpio_pin *p = &avila_gpio_pins[i];
328
329		strncpy(sc->sc_pins[p->pin].gp_name, p->name, GPIOMAXNAME);
330		sc->sc_pins[p->pin].gp_pin = p->pin;
331		sc->sc_pins[p->pin].gp_caps = p->caps;
332		sc->sc_pins[p->pin].gp_flags = avila_gpio_pin_flags(sc, p->pin);
333		sc->sc_valid |= 1 << p->pin;
334	}
335
336	device_add_child(dev, "gpioc", device_get_unit(dev));
337	device_add_child(dev, "gpiobus", device_get_unit(dev));
338	return (bus_generic_attach(dev));
339#undef N
340}
341
342static int
343avila_gpio_detach(device_t dev)
344{
345	struct avila_gpio_softc *sc = device_get_softc(dev);
346
347	KASSERT(mtx_initialized(&sc->sc_mtx), ("gpio mutex not initialized"));
348
349	bus_generic_detach(dev);
350
351	mtx_destroy(&sc->sc_mtx);
352
353	return(0);
354}
355
356static device_method_t gpio_avila_methods[] = {
357	DEVMETHOD(device_probe, avila_gpio_probe),
358	DEVMETHOD(device_attach, avila_gpio_attach),
359	DEVMETHOD(device_detach, avila_gpio_detach),
360
361	/* GPIO protocol */
362	DEVMETHOD(gpio_pin_max, avila_gpio_pin_max),
363	DEVMETHOD(gpio_pin_getname, avila_gpio_pin_getname),
364	DEVMETHOD(gpio_pin_getflags, avila_gpio_pin_getflags),
365	DEVMETHOD(gpio_pin_getcaps, avila_gpio_pin_getcaps),
366	DEVMETHOD(gpio_pin_setflags, avila_gpio_pin_setflags),
367	DEVMETHOD(gpio_pin_get, avila_gpio_pin_get),
368	DEVMETHOD(gpio_pin_set, avila_gpio_pin_set),
369	DEVMETHOD(gpio_pin_toggle, avila_gpio_pin_toggle),
370	{0, 0},
371};
372
373static driver_t gpio_avila_driver = {
374	"gpio_avila",
375	gpio_avila_methods,
376	sizeof(struct avila_gpio_softc),
377};
378static devclass_t gpio_avila_devclass;
379
380DRIVER_MODULE(gpio_avila, ixp, gpio_avila_driver, gpio_avila_devclass, 0, 0);
381