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$");
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
61214946Sthompsastruct avila_gpio_softc {
62214946Sthompsa	device_t		sc_dev;
63214946Sthompsa	bus_space_tag_t		sc_iot;
64214946Sthompsa	bus_space_handle_t	sc_gpio_ioh;
65214946Sthompsa	uint32_t		sc_valid;
66214946Sthompsa	struct gpio_pin		sc_pins[IXP4XX_GPIO_PINS];
67214946Sthompsa};
68214946Sthompsa
69214946Sthompsastruct avila_gpio_pin {
70214946Sthompsa	const char *name;
71214946Sthompsa	int pin;
72214946Sthompsa	int caps;
73214946Sthompsa};
74214946Sthompsa
75214946Sthompsa#define	GPIO_PIN_IO	(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)
76214946Sthompsastatic struct avila_gpio_pin avila_gpio_pins[] = {
77214946Sthompsa	{ "GPIO0", 0, GPIO_PIN_IO },
78214946Sthompsa	{ "GPIO1", 1, GPIO_PIN_IO },
79214946Sthompsa	{ "GPIO2", 2, GPIO_PIN_IO },
80214946Sthompsa	{ "GPIO3", 3, GPIO_PIN_IO },
81214946Sthompsa	{ "GPIO4", 4, GPIO_PIN_IO },
82214946Sthompsa	/*
83214946Sthompsa	 * The following pins are connected to system devices and should not
84214946Sthompsa	 * really be frobbed.
85214946Sthompsa	 */
86214946Sthompsa#if 0
87214946Sthompsa	{ "SER_ENA", 5, GPIO_PIN_IO },
88214946Sthompsa	{ "I2C_SCL", 6, GPIO_PIN_IO },
89214946Sthompsa	{ "I2C_SDA", 7, GPIO_PIN_IO },
90214946Sthompsa	{ "PCI_INTD", 8, GPIO_PIN_IO },
91214946Sthompsa	{ "PCI_INTC", 9, GPIO_PIN_IO },
92214946Sthompsa	{ "PCI_INTB", 10, GPIO_PIN_IO },
93214946Sthompsa	{ "PCI_INTA", 11, GPIO_PIN_IO },
94214946Sthompsa	{ "ATA_INT", 12, GPIO_PIN_IO },
95214946Sthompsa	{ "PCI_RST", 13, GPIO_PIN_IO },
96214946Sthompsa	{ "PCI_CLK", 14, GPIO_PIN_OUTPUT },
97214946Sthompsa	{ "EX_CLK", 15, GPIO_PIN_OUTPUT },
98214946Sthompsa#endif
99214946Sthompsa};
100214946Sthompsa#undef GPIO_PIN_IO
101214946Sthompsa
102214946Sthompsa/*
103214946Sthompsa * Helpers
104214946Sthompsa */
105214946Sthompsastatic void avila_gpio_pin_configure(struct avila_gpio_softc *sc,
106214946Sthompsa    struct gpio_pin *pin, uint32_t flags);
107214946Sthompsastatic int  avila_gpio_pin_flags(struct avila_gpio_softc *sc, uint32_t pin);
108214946Sthompsa
109214946Sthompsa/*
110214946Sthompsa * Driver stuff
111214946Sthompsa */
112214946Sthompsastatic int avila_gpio_probe(device_t dev);
113214946Sthompsastatic int avila_gpio_attach(device_t dev);
114214946Sthompsastatic int avila_gpio_detach(device_t dev);
115214946Sthompsa
116214946Sthompsa/*
117214946Sthompsa * GPIO interface
118214946Sthompsa */
119214946Sthompsastatic int avila_gpio_pin_max(device_t dev, int *maxpin);
120214946Sthompsastatic int avila_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps);
121214946Sthompsastatic int avila_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t
122214946Sthompsa    *flags);
123214946Sthompsastatic int avila_gpio_pin_getname(device_t dev, uint32_t pin, char *name);
124214946Sthompsastatic int avila_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags);
125214946Sthompsastatic int avila_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value);
126214946Sthompsastatic int avila_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val);
127214946Sthompsastatic int avila_gpio_pin_toggle(device_t dev, uint32_t pin);
128214946Sthompsa
129214946Sthompsastatic int
130214946Sthompsaavila_gpio_pin_flags(struct avila_gpio_softc *sc, uint32_t pin)
131214946Sthompsa{
132214946Sthompsa	uint32_t v;
133214946Sthompsa
134214946Sthompsa	v = GPIO_CONF_READ_4(sc, IXP425_GPIO_GPINR) & (1 << pin);
135214946Sthompsa
136214946Sthompsa	return (v ? GPIO_PIN_INPUT : GPIO_PIN_OUTPUT);
137214946Sthompsa}
138214946Sthompsa
139214946Sthompsastatic void
140214946Sthompsaavila_gpio_pin_configure(struct avila_gpio_softc *sc, struct gpio_pin *pin,
141214946Sthompsa    unsigned int flags)
142214946Sthompsa{
143214946Sthompsa	uint32_t mask;
144214946Sthompsa
145214946Sthompsa	mask = 1 << pin->gp_pin;
146214946Sthompsa
147214946Sthompsa	/*
148214946Sthompsa	 * Manage input/output
149214946Sthompsa	 */
150214946Sthompsa	if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) {
151216681Simp		IXP4XX_GPIO_LOCK();
152214946Sthompsa		pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT);
153214946Sthompsa		if (flags & GPIO_PIN_OUTPUT) {
154214946Sthompsa			pin->gp_flags |= GPIO_PIN_OUTPUT;
155214946Sthompsa			GPIO_CLEAR_BITS(sc, IXP425_GPIO_GPOER, mask);
156214946Sthompsa		}
157214946Sthompsa		else {
158214946Sthompsa			pin->gp_flags |= GPIO_PIN_INPUT;
159214946Sthompsa			GPIO_SET_BITS(sc, IXP425_GPIO_GPOER, mask);
160214946Sthompsa		}
161216681Simp		IXP4XX_GPIO_UNLOCK();
162214946Sthompsa	}
163214946Sthompsa}
164214946Sthompsa
165214946Sthompsastatic int
166214946Sthompsaavila_gpio_pin_max(device_t dev, int *maxpin)
167214946Sthompsa{
168214946Sthompsa
169214946Sthompsa	*maxpin = IXP4XX_GPIO_PINS - 1;
170214946Sthompsa	return (0);
171214946Sthompsa}
172214946Sthompsa
173214946Sthompsastatic int
174214946Sthompsaavila_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
175214946Sthompsa{
176214946Sthompsa	struct avila_gpio_softc *sc = device_get_softc(dev);
177214946Sthompsa
178214946Sthompsa	if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & (1 << pin)))
179214946Sthompsa		return (EINVAL);
180214946Sthompsa
181214946Sthompsa	*caps = sc->sc_pins[pin].gp_caps;
182214946Sthompsa	return (0);
183214946Sthompsa}
184214946Sthompsa
185214946Sthompsastatic int
186214946Sthompsaavila_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
187214946Sthompsa{
188214946Sthompsa	struct avila_gpio_softc *sc = device_get_softc(dev);
189214946Sthompsa
190214946Sthompsa	if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & (1 << pin)))
191214946Sthompsa		return (EINVAL);
192214946Sthompsa
193216681Simp	IXP4XX_GPIO_LOCK();
194214946Sthompsa	/* refresh since we do not own all the pins */
195214946Sthompsa	sc->sc_pins[pin].gp_flags = avila_gpio_pin_flags(sc, pin);
196214946Sthompsa	*flags = sc->sc_pins[pin].gp_flags;
197216681Simp	IXP4XX_GPIO_UNLOCK();
198214946Sthompsa
199214946Sthompsa	return (0);
200214946Sthompsa}
201214946Sthompsa
202214946Sthompsastatic int
203214946Sthompsaavila_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
204214946Sthompsa{
205214946Sthompsa	struct avila_gpio_softc *sc = device_get_softc(dev);
206214946Sthompsa
207214946Sthompsa	if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & (1 << pin)))
208214946Sthompsa		return (EINVAL);
209214946Sthompsa
210214946Sthompsa	memcpy(name, sc->sc_pins[pin].gp_name, GPIOMAXNAME);
211214946Sthompsa	return (0);
212214946Sthompsa}
213214946Sthompsa
214214946Sthompsastatic int
215214946Sthompsaavila_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
216214946Sthompsa{
217214946Sthompsa	struct avila_gpio_softc *sc = device_get_softc(dev);
218214946Sthompsa	uint32_t mask = 1 << pin;
219214946Sthompsa
220214946Sthompsa	if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & mask))
221214946Sthompsa		return (EINVAL);
222214946Sthompsa
223249550Sdim	/* Check for unwanted flags. */
224249550Sdim	if ((flags & sc->sc_pins[pin].gp_caps) != flags)
225214946Sthompsa		return (EINVAL);
226214946Sthompsa
227214946Sthompsa	/* Can't mix input/output together */
228214946Sthompsa	if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) ==
229214946Sthompsa	    (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT))
230214946Sthompsa		return (EINVAL);
231214946Sthompsa
232214946Sthompsa	avila_gpio_pin_configure(sc, &sc->sc_pins[pin], flags);
233214946Sthompsa	return (0);
234214946Sthompsa}
235214946Sthompsa
236214946Sthompsastatic int
237214946Sthompsaavila_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
238214946Sthompsa{
239214946Sthompsa	struct avila_gpio_softc *sc = device_get_softc(dev);
240214946Sthompsa	uint32_t mask = 1 << pin;
241214946Sthompsa
242214946Sthompsa	if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & mask))
243214946Sthompsa		return (EINVAL);
244214946Sthompsa
245216681Simp	IXP4XX_GPIO_LOCK();
246214946Sthompsa	if (value)
247214946Sthompsa		GPIO_SET_BITS(sc, IXP425_GPIO_GPOUTR, mask);
248214946Sthompsa	else
249214946Sthompsa		GPIO_CLEAR_BITS(sc, IXP425_GPIO_GPOUTR, mask);
250216681Simp	IXP4XX_GPIO_UNLOCK();
251214946Sthompsa
252214946Sthompsa	return (0);
253214946Sthompsa}
254214946Sthompsa
255214946Sthompsastatic int
256214946Sthompsaavila_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
257214946Sthompsa{
258214946Sthompsa	struct avila_gpio_softc *sc = device_get_softc(dev);
259214946Sthompsa
260214946Sthompsa	if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & (1 << pin)))
261214946Sthompsa		return (EINVAL);
262214946Sthompsa
263216681Simp	IXP4XX_GPIO_LOCK();
264214946Sthompsa	*val = (GPIO_CONF_READ_4(sc, IXP425_GPIO_GPINR) & (1 << pin)) ? 1 : 0;
265216681Simp	IXP4XX_GPIO_UNLOCK();
266214946Sthompsa
267214946Sthompsa	return (0);
268214946Sthompsa}
269214946Sthompsa
270214946Sthompsastatic int
271214946Sthompsaavila_gpio_pin_toggle(device_t dev, uint32_t pin)
272214946Sthompsa{
273214946Sthompsa	struct avila_gpio_softc *sc = device_get_softc(dev);
274214946Sthompsa	uint32_t mask = 1 << pin;
275214946Sthompsa	int res;
276214946Sthompsa
277214946Sthompsa	if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & mask))
278214946Sthompsa		return (EINVAL);
279214946Sthompsa
280216681Simp	IXP4XX_GPIO_LOCK();
281215319Sthompsa	res = GPIO_CONF_READ_4(sc, IXP425_GPIO_GPINR) & mask;
282214946Sthompsa	if (res)
283214946Sthompsa		GPIO_CLEAR_BITS(sc, IXP425_GPIO_GPOUTR, mask);
284214946Sthompsa	else
285214946Sthompsa		GPIO_SET_BITS(sc, IXP425_GPIO_GPOUTR, mask);
286216681Simp	IXP4XX_GPIO_UNLOCK();
287214946Sthompsa
288214946Sthompsa	return (0);
289214946Sthompsa}
290214946Sthompsa
291214946Sthompsastatic int
292214946Sthompsaavila_gpio_probe(device_t dev)
293214946Sthompsa{
294214946Sthompsa
295214946Sthompsa	device_set_desc(dev, "Gateworks Avila GPIO driver");
296214946Sthompsa	return (0);
297214946Sthompsa}
298214946Sthompsa
299214946Sthompsastatic int
300214946Sthompsaavila_gpio_attach(device_t dev)
301214946Sthompsa{
302214946Sthompsa#define	N(a)	(sizeof(a) / sizeof(a[0]))
303214946Sthompsa	struct avila_gpio_softc *sc = device_get_softc(dev);
304214946Sthompsa	struct ixp425_softc *sa = device_get_softc(device_get_parent(dev));
305214946Sthompsa	int i;
306214946Sthompsa
307214946Sthompsa	sc->sc_dev = dev;
308214946Sthompsa	sc->sc_iot = sa->sc_iot;
309214946Sthompsa	sc->sc_gpio_ioh = sa->sc_gpio_ioh;
310214946Sthompsa
311214946Sthompsa	for (i = 0; i < N(avila_gpio_pins); i++) {
312214946Sthompsa		struct avila_gpio_pin *p = &avila_gpio_pins[i];
313214946Sthompsa
314214946Sthompsa		strncpy(sc->sc_pins[p->pin].gp_name, p->name, GPIOMAXNAME);
315214946Sthompsa		sc->sc_pins[p->pin].gp_pin = p->pin;
316214946Sthompsa		sc->sc_pins[p->pin].gp_caps = p->caps;
317214946Sthompsa		sc->sc_pins[p->pin].gp_flags = avila_gpio_pin_flags(sc, p->pin);
318214946Sthompsa		sc->sc_valid |= 1 << p->pin;
319214946Sthompsa	}
320214946Sthompsa
321214946Sthompsa	device_add_child(dev, "gpioc", device_get_unit(dev));
322214946Sthompsa	device_add_child(dev, "gpiobus", device_get_unit(dev));
323214946Sthompsa	return (bus_generic_attach(dev));
324214946Sthompsa#undef N
325214946Sthompsa}
326214946Sthompsa
327214946Sthompsastatic int
328214946Sthompsaavila_gpio_detach(device_t dev)
329214946Sthompsa{
330214946Sthompsa
331214946Sthompsa	bus_generic_detach(dev);
332214946Sthompsa
333214946Sthompsa	return(0);
334214946Sthompsa}
335214946Sthompsa
336214946Sthompsastatic device_method_t gpio_avila_methods[] = {
337214946Sthompsa	DEVMETHOD(device_probe, avila_gpio_probe),
338214946Sthompsa	DEVMETHOD(device_attach, avila_gpio_attach),
339214946Sthompsa	DEVMETHOD(device_detach, avila_gpio_detach),
340214946Sthompsa
341214946Sthompsa	/* GPIO protocol */
342214946Sthompsa	DEVMETHOD(gpio_pin_max, avila_gpio_pin_max),
343214946Sthompsa	DEVMETHOD(gpio_pin_getname, avila_gpio_pin_getname),
344214946Sthompsa	DEVMETHOD(gpio_pin_getflags, avila_gpio_pin_getflags),
345214946Sthompsa	DEVMETHOD(gpio_pin_getcaps, avila_gpio_pin_getcaps),
346214946Sthompsa	DEVMETHOD(gpio_pin_setflags, avila_gpio_pin_setflags),
347214946Sthompsa	DEVMETHOD(gpio_pin_get, avila_gpio_pin_get),
348214946Sthompsa	DEVMETHOD(gpio_pin_set, avila_gpio_pin_set),
349214946Sthompsa	DEVMETHOD(gpio_pin_toggle, avila_gpio_pin_toggle),
350214946Sthompsa	{0, 0},
351214946Sthompsa};
352214946Sthompsa
353214946Sthompsastatic driver_t gpio_avila_driver = {
354214946Sthompsa	"gpio_avila",
355214946Sthompsa	gpio_avila_methods,
356214946Sthompsa	sizeof(struct avila_gpio_softc),
357214946Sthompsa};
358214946Sthompsastatic devclass_t gpio_avila_devclass;
359229883Sthompsaextern devclass_t gpiobus_devclass, gpioc_devclass;
360229883Sthompsaextern driver_t gpiobus_driver, gpioc_driver;
361214946Sthompsa
362214946SthompsaDRIVER_MODULE(gpio_avila, ixp, gpio_avila_driver, gpio_avila_devclass, 0, 0);
363229883SthompsaDRIVER_MODULE(gpiobus, gpio_avila, gpiobus_driver, gpiobus_devclass, 0, 0);
364229883SthompsaDRIVER_MODULE(gpioc, gpio_avila, gpioc_driver, gpioc_devclass, 0, 0);
365229883SthompsaMODULE_VERSION(gpio_avila, 1);
366