avila_gpio.c revision 214946
1214946Sthompsa/*-
2214946Sthompsa * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org>
3214946Sthompsa * Copyright (c) 2009, Luiz Otavio O Souza.
4214946Sthompsa * Copyright (c) 2010, Andrew Thompson <thompsa@FreeBSD.org>
5214946Sthompsa * All rights reserved.
6214946Sthompsa *
7214946Sthompsa * Redistribution and use in source and binary forms, with or without
8214946Sthompsa * modification, are permitted provided that the following conditions
9214946Sthompsa * are met:
10214946Sthompsa * 1. Redistributions of source code must retain the above copyright
11214946Sthompsa *    notice unmodified, this list of conditions, and the following
12214946Sthompsa *    disclaimer.
13214946Sthompsa * 2. Redistributions in binary form must reproduce the above copyright
14214946Sthompsa *    notice, this list of conditions and the following disclaimer in the
15214946Sthompsa *    documentation and/or other materials provided with the distribution.
16214946Sthompsa *
17214946Sthompsa * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18214946Sthompsa * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19214946Sthompsa * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20214946Sthompsa * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21214946Sthompsa * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22214946Sthompsa * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23214946Sthompsa * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24214946Sthompsa * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25214946Sthompsa * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26214946Sthompsa * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27214946Sthompsa * SUCH DAMAGE.
28214946Sthompsa */
29214946Sthompsa
30214946Sthompsa/*
31214946Sthompsa * GPIO driver for Gateworks Avilia
32214946Sthompsa */
33214946Sthompsa
34214946Sthompsa#include <sys/cdefs.h>
35214946Sthompsa__FBSDID("$FreeBSD: head/sys/arm/xscale/ixp425/avila_gpio.c 214946 2010-11-07 20:33:39Z thompsa $");
36214946Sthompsa
37214946Sthompsa#include <sys/param.h>
38214946Sthompsa#include <sys/systm.h>
39214946Sthompsa#include <sys/bus.h>
40214946Sthompsa
41214946Sthompsa#include <sys/kernel.h>
42214946Sthompsa#include <sys/module.h>
43214946Sthompsa#include <sys/rman.h>
44214946Sthompsa#include <sys/lock.h>
45214946Sthompsa#include <sys/mutex.h>
46214946Sthompsa#include <sys/gpio.h>
47214946Sthompsa
48214946Sthompsa#include <machine/bus.h>
49214946Sthompsa#include <machine/resource.h>
50214946Sthompsa#include <arm/xscale/ixp425/ixp425reg.h>
51214946Sthompsa#include <arm/xscale/ixp425/ixp425var.h>
52214946Sthompsa
53214946Sthompsa#include "gpio_if.h"
54214946Sthompsa
55214946Sthompsa#define GPIO_SET_BITS(sc, reg, bits)	\
56214946Sthompsa	GPIO_CONF_WRITE_4(sc, reg, GPIO_CONF_READ_4(sc, (reg)) | (bits))
57214946Sthompsa
58214946Sthompsa#define GPIO_CLEAR_BITS(sc, reg, bits)	\
59214946Sthompsa	GPIO_CONF_WRITE_4(sc, reg, GPIO_CONF_READ_4(sc, (reg)) & ~(bits))
60214946Sthompsa
61214946Sthompsa#define GPIO_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
62214946Sthompsa#define GPIO_UNLOCK(_sc)	mtx_unlock(&(_sc)->sc_mtx)
63214946Sthompsa#define GPIO_LOCK_ASSERT(_sc)	mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
64214946Sthompsa
65214946Sthompsastruct avila_gpio_softc {
66214946Sthompsa	device_t		sc_dev;
67214946Sthompsa        struct mtx		sc_mtx;
68214946Sthompsa	bus_space_tag_t		sc_iot;
69214946Sthompsa	bus_space_handle_t	sc_gpio_ioh;
70214946Sthompsa	uint32_t		sc_valid;
71214946Sthompsa	struct gpio_pin		sc_pins[IXP4XX_GPIO_PINS];
72214946Sthompsa};
73214946Sthompsa
74214946Sthompsastruct avila_gpio_pin {
75214946Sthompsa	const char *name;
76214946Sthompsa	int pin;
77214946Sthompsa	int caps;
78214946Sthompsa};
79214946Sthompsa
80214946Sthompsa#define	GPIO_PIN_IO	(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)
81214946Sthompsastatic struct avila_gpio_pin avila_gpio_pins[] = {
82214946Sthompsa	{ "GPIO0", 0, GPIO_PIN_IO },
83214946Sthompsa	{ "GPIO1", 1, GPIO_PIN_IO },
84214946Sthompsa	{ "GPIO2", 2, GPIO_PIN_IO },
85214946Sthompsa	{ "GPIO3", 3, GPIO_PIN_IO },
86214946Sthompsa	{ "GPIO4", 4, GPIO_PIN_IO },
87214946Sthompsa	/*
88214946Sthompsa	 * The following pins are connected to system devices and should not
89214946Sthompsa	 * really be frobbed.
90214946Sthompsa	 */
91214946Sthompsa#if 0
92214946Sthompsa	{ "SER_ENA", 5, GPIO_PIN_IO },
93214946Sthompsa	{ "I2C_SCL", 6, GPIO_PIN_IO },
94214946Sthompsa	{ "I2C_SDA", 7, GPIO_PIN_IO },
95214946Sthompsa	{ "PCI_INTD", 8, GPIO_PIN_IO },
96214946Sthompsa	{ "PCI_INTC", 9, GPIO_PIN_IO },
97214946Sthompsa	{ "PCI_INTB", 10, GPIO_PIN_IO },
98214946Sthompsa	{ "PCI_INTA", 11, GPIO_PIN_IO },
99214946Sthompsa	{ "ATA_INT", 12, GPIO_PIN_IO },
100214946Sthompsa	{ "PCI_RST", 13, GPIO_PIN_IO },
101214946Sthompsa	{ "PCI_CLK", 14, GPIO_PIN_OUTPUT },
102214946Sthompsa	{ "EX_CLK", 15, GPIO_PIN_OUTPUT },
103214946Sthompsa#endif
104214946Sthompsa};
105214946Sthompsa#undef GPIO_PIN_IO
106214946Sthompsa
107214946Sthompsa/*
108214946Sthompsa * Helpers
109214946Sthompsa */
110214946Sthompsastatic void avila_gpio_pin_configure(struct avila_gpio_softc *sc,
111214946Sthompsa    struct gpio_pin *pin, uint32_t flags);
112214946Sthompsastatic int  avila_gpio_pin_flags(struct avila_gpio_softc *sc, uint32_t pin);
113214946Sthompsa
114214946Sthompsa/*
115214946Sthompsa * Driver stuff
116214946Sthompsa */
117214946Sthompsastatic int avila_gpio_probe(device_t dev);
118214946Sthompsastatic int avila_gpio_attach(device_t dev);
119214946Sthompsastatic int avila_gpio_detach(device_t dev);
120214946Sthompsa
121214946Sthompsa/*
122214946Sthompsa * GPIO interface
123214946Sthompsa */
124214946Sthompsastatic int avila_gpio_pin_max(device_t dev, int *maxpin);
125214946Sthompsastatic int avila_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps);
126214946Sthompsastatic int avila_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t
127214946Sthompsa    *flags);
128214946Sthompsastatic int avila_gpio_pin_getname(device_t dev, uint32_t pin, char *name);
129214946Sthompsastatic int avila_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags);
130214946Sthompsastatic int avila_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value);
131214946Sthompsastatic int avila_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val);
132214946Sthompsastatic int avila_gpio_pin_toggle(device_t dev, uint32_t pin);
133214946Sthompsa
134214946Sthompsastatic int
135214946Sthompsaavila_gpio_pin_flags(struct avila_gpio_softc *sc, uint32_t pin)
136214946Sthompsa{
137214946Sthompsa	uint32_t v;
138214946Sthompsa
139214946Sthompsa	v = GPIO_CONF_READ_4(sc, IXP425_GPIO_GPINR) & (1 << pin);
140214946Sthompsa
141214946Sthompsa	return (v ? GPIO_PIN_INPUT : GPIO_PIN_OUTPUT);
142214946Sthompsa}
143214946Sthompsa
144214946Sthompsastatic void
145214946Sthompsaavila_gpio_pin_configure(struct avila_gpio_softc *sc, struct gpio_pin *pin,
146214946Sthompsa    unsigned int flags)
147214946Sthompsa{
148214946Sthompsa	uint32_t mask;
149214946Sthompsa
150214946Sthompsa	mask = 1 << pin->gp_pin;
151214946Sthompsa	GPIO_LOCK(sc);
152214946Sthompsa
153214946Sthompsa	/*
154214946Sthompsa	 * Manage input/output
155214946Sthompsa	 */
156214946Sthompsa	if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) {
157214946Sthompsa		pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT);
158214946Sthompsa		if (flags & GPIO_PIN_OUTPUT) {
159214946Sthompsa			pin->gp_flags |= GPIO_PIN_OUTPUT;
160214946Sthompsa			GPIO_CLEAR_BITS(sc, IXP425_GPIO_GPOER, mask);
161214946Sthompsa		}
162214946Sthompsa		else {
163214946Sthompsa			pin->gp_flags |= GPIO_PIN_INPUT;
164214946Sthompsa			GPIO_SET_BITS(sc, IXP425_GPIO_GPOER, mask);
165214946Sthompsa		}
166214946Sthompsa	}
167214946Sthompsa
168214946Sthompsa	GPIO_UNLOCK(sc);
169214946Sthompsa}
170214946Sthompsa
171214946Sthompsastatic int
172214946Sthompsaavila_gpio_pin_max(device_t dev, int *maxpin)
173214946Sthompsa{
174214946Sthompsa
175214946Sthompsa	*maxpin = IXP4XX_GPIO_PINS - 1;
176214946Sthompsa	return (0);
177214946Sthompsa}
178214946Sthompsa
179214946Sthompsastatic int
180214946Sthompsaavila_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
181214946Sthompsa{
182214946Sthompsa	struct avila_gpio_softc *sc = device_get_softc(dev);
183214946Sthompsa
184214946Sthompsa	if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & (1 << pin)))
185214946Sthompsa		return (EINVAL);
186214946Sthompsa
187214946Sthompsa	GPIO_LOCK(sc);
188214946Sthompsa	*caps = sc->sc_pins[pin].gp_caps;
189214946Sthompsa	GPIO_UNLOCK(sc);
190214946Sthompsa
191214946Sthompsa	return (0);
192214946Sthompsa}
193214946Sthompsa
194214946Sthompsastatic int
195214946Sthompsaavila_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
196214946Sthompsa{
197214946Sthompsa	struct avila_gpio_softc *sc = device_get_softc(dev);
198214946Sthompsa
199214946Sthompsa	if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & (1 << pin)))
200214946Sthompsa		return (EINVAL);
201214946Sthompsa
202214946Sthompsa	GPIO_LOCK(sc);
203214946Sthompsa	/* refresh since we do not own all the pins */
204214946Sthompsa	sc->sc_pins[pin].gp_flags = avila_gpio_pin_flags(sc, pin);
205214946Sthompsa	*flags = sc->sc_pins[pin].gp_flags;
206214946Sthompsa	GPIO_UNLOCK(sc);
207214946Sthompsa
208214946Sthompsa	return (0);
209214946Sthompsa}
210214946Sthompsa
211214946Sthompsastatic int
212214946Sthompsaavila_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
213214946Sthompsa{
214214946Sthompsa	struct avila_gpio_softc *sc = device_get_softc(dev);
215214946Sthompsa
216214946Sthompsa	if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & (1 << pin)))
217214946Sthompsa		return (EINVAL);
218214946Sthompsa
219214946Sthompsa	GPIO_LOCK(sc);
220214946Sthompsa	memcpy(name, sc->sc_pins[pin].gp_name, GPIOMAXNAME);
221214946Sthompsa	GPIO_UNLOCK(sc);
222214946Sthompsa
223214946Sthompsa	return (0);
224214946Sthompsa}
225214946Sthompsa
226214946Sthompsastatic int
227214946Sthompsaavila_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
228214946Sthompsa{
229214946Sthompsa	struct avila_gpio_softc *sc = device_get_softc(dev);
230214946Sthompsa	uint32_t mask = 1 << pin;
231214946Sthompsa
232214946Sthompsa	if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & mask))
233214946Sthompsa		return (EINVAL);
234214946Sthompsa
235214946Sthompsa	/* Filter out unwanted flags */
236214946Sthompsa	if ((flags &= sc->sc_pins[pin].gp_caps) != flags)
237214946Sthompsa		return (EINVAL);
238214946Sthompsa
239214946Sthompsa	/* Can't mix input/output together */
240214946Sthompsa	if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) ==
241214946Sthompsa	    (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT))
242214946Sthompsa		return (EINVAL);
243214946Sthompsa
244214946Sthompsa	avila_gpio_pin_configure(sc, &sc->sc_pins[pin], flags);
245214946Sthompsa	return (0);
246214946Sthompsa}
247214946Sthompsa
248214946Sthompsastatic int
249214946Sthompsaavila_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
250214946Sthompsa{
251214946Sthompsa	struct avila_gpio_softc *sc = device_get_softc(dev);
252214946Sthompsa	uint32_t mask = 1 << pin;
253214946Sthompsa
254214946Sthompsa	if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & mask))
255214946Sthompsa		return (EINVAL);
256214946Sthompsa
257214946Sthompsa	GPIO_LOCK(sc);
258214946Sthompsa	if (value)
259214946Sthompsa		GPIO_SET_BITS(sc, IXP425_GPIO_GPOUTR, mask);
260214946Sthompsa	else
261214946Sthompsa		GPIO_CLEAR_BITS(sc, IXP425_GPIO_GPOUTR, mask);
262214946Sthompsa	GPIO_UNLOCK(sc);
263214946Sthompsa
264214946Sthompsa	return (0);
265214946Sthompsa}
266214946Sthompsa
267214946Sthompsastatic int
268214946Sthompsaavila_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
269214946Sthompsa{
270214946Sthompsa	struct avila_gpio_softc *sc = device_get_softc(dev);
271214946Sthompsa
272214946Sthompsa	if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & (1 << pin)))
273214946Sthompsa		return (EINVAL);
274214946Sthompsa
275214946Sthompsa	GPIO_LOCK(sc);
276214946Sthompsa	*val = (GPIO_CONF_READ_4(sc, IXP425_GPIO_GPINR) & (1 << pin)) ? 1 : 0;
277214946Sthompsa	GPIO_UNLOCK(sc);
278214946Sthompsa
279214946Sthompsa	return (0);
280214946Sthompsa}
281214946Sthompsa
282214946Sthompsastatic int
283214946Sthompsaavila_gpio_pin_toggle(device_t dev, uint32_t pin)
284214946Sthompsa{
285214946Sthompsa	struct avila_gpio_softc *sc = device_get_softc(dev);
286214946Sthompsa	uint32_t mask = 1 << pin;
287214946Sthompsa	int res;
288214946Sthompsa
289214946Sthompsa	if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & mask))
290214946Sthompsa		return (EINVAL);
291214946Sthompsa
292214946Sthompsa	GPIO_LOCK(sc);
293214946Sthompsa	res = (GPIO_CONF_READ_4(sc, IXP425_GPIO_GPINR) & mask) ? 1 : 0;
294214946Sthompsa	if (res)
295214946Sthompsa		GPIO_CLEAR_BITS(sc, IXP425_GPIO_GPOUTR, mask);
296214946Sthompsa	else
297214946Sthompsa		GPIO_SET_BITS(sc, IXP425_GPIO_GPOUTR, mask);
298214946Sthompsa	GPIO_UNLOCK(sc);
299214946Sthompsa
300214946Sthompsa	return (0);
301214946Sthompsa}
302214946Sthompsa
303214946Sthompsastatic int
304214946Sthompsaavila_gpio_probe(device_t dev)
305214946Sthompsa{
306214946Sthompsa
307214946Sthompsa	device_set_desc(dev, "Gateworks Avila GPIO driver");
308214946Sthompsa	return (0);
309214946Sthompsa}
310214946Sthompsa
311214946Sthompsastatic int
312214946Sthompsaavila_gpio_attach(device_t dev)
313214946Sthompsa{
314214946Sthompsa#define	N(a)	(sizeof(a) / sizeof(a[0]))
315214946Sthompsa	struct avila_gpio_softc *sc = device_get_softc(dev);
316214946Sthompsa	struct ixp425_softc *sa = device_get_softc(device_get_parent(dev));
317214946Sthompsa	int i;
318214946Sthompsa
319214946Sthompsa	sc->sc_dev = dev;
320214946Sthompsa	sc->sc_iot = sa->sc_iot;
321214946Sthompsa	sc->sc_gpio_ioh = sa->sc_gpio_ioh;
322214946Sthompsa
323214946Sthompsa	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
324214946Sthompsa	    MTX_DEF);
325214946Sthompsa
326214946Sthompsa	for (i = 0; i < N(avila_gpio_pins); i++) {
327214946Sthompsa		struct avila_gpio_pin *p = &avila_gpio_pins[i];
328214946Sthompsa
329214946Sthompsa		strncpy(sc->sc_pins[p->pin].gp_name, p->name, GPIOMAXNAME);
330214946Sthompsa		sc->sc_pins[p->pin].gp_pin = p->pin;
331214946Sthompsa		sc->sc_pins[p->pin].gp_caps = p->caps;
332214946Sthompsa		sc->sc_pins[p->pin].gp_flags = avila_gpio_pin_flags(sc, p->pin);
333214946Sthompsa		sc->sc_valid |= 1 << p->pin;
334214946Sthompsa	}
335214946Sthompsa
336214946Sthompsa	device_add_child(dev, "gpioc", device_get_unit(dev));
337214946Sthompsa	device_add_child(dev, "gpiobus", device_get_unit(dev));
338214946Sthompsa	return (bus_generic_attach(dev));
339214946Sthompsa#undef N
340214946Sthompsa}
341214946Sthompsa
342214946Sthompsastatic int
343214946Sthompsaavila_gpio_detach(device_t dev)
344214946Sthompsa{
345214946Sthompsa	struct avila_gpio_softc *sc = device_get_softc(dev);
346214946Sthompsa
347214946Sthompsa	KASSERT(mtx_initialized(&sc->sc_mtx), ("gpio mutex not initialized"));
348214946Sthompsa
349214946Sthompsa	bus_generic_detach(dev);
350214946Sthompsa
351214946Sthompsa	mtx_destroy(&sc->sc_mtx);
352214946Sthompsa
353214946Sthompsa	return(0);
354214946Sthompsa}
355214946Sthompsa
356214946Sthompsastatic device_method_t gpio_avila_methods[] = {
357214946Sthompsa	DEVMETHOD(device_probe, avila_gpio_probe),
358214946Sthompsa	DEVMETHOD(device_attach, avila_gpio_attach),
359214946Sthompsa	DEVMETHOD(device_detach, avila_gpio_detach),
360214946Sthompsa
361214946Sthompsa	/* GPIO protocol */
362214946Sthompsa	DEVMETHOD(gpio_pin_max, avila_gpio_pin_max),
363214946Sthompsa	DEVMETHOD(gpio_pin_getname, avila_gpio_pin_getname),
364214946Sthompsa	DEVMETHOD(gpio_pin_getflags, avila_gpio_pin_getflags),
365214946Sthompsa	DEVMETHOD(gpio_pin_getcaps, avila_gpio_pin_getcaps),
366214946Sthompsa	DEVMETHOD(gpio_pin_setflags, avila_gpio_pin_setflags),
367214946Sthompsa	DEVMETHOD(gpio_pin_get, avila_gpio_pin_get),
368214946Sthompsa	DEVMETHOD(gpio_pin_set, avila_gpio_pin_set),
369214946Sthompsa	DEVMETHOD(gpio_pin_toggle, avila_gpio_pin_toggle),
370214946Sthompsa	{0, 0},
371214946Sthompsa};
372214946Sthompsa
373214946Sthompsastatic driver_t gpio_avila_driver = {
374214946Sthompsa	"gpio_avila",
375214946Sthompsa	gpio_avila_methods,
376214946Sthompsa	sizeof(struct avila_gpio_softc),
377214946Sthompsa};
378214946Sthompsastatic devclass_t gpio_avila_devclass;
379214946Sthompsa
380214946SthompsaDRIVER_MODULE(gpio_avila, ixp, gpio_avila_driver, gpio_avila_devclass, 0, 0);
381