pxa_gpio.c revision 331722
1/*-
2 * Copyright (c) 2006 Benno Rice.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include <sys/cdefs.h>
26__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/pxa/pxa_gpio.c 331722 2018-03-29 02:50:57Z eadler $");
27
28#include <sys/param.h>
29#include <sys/systm.h>
30#include <sys/bus.h>
31#include <sys/kernel.h>
32#include <sys/lock.h>
33#include <sys/interrupt.h>
34#include <sys/module.h>
35#include <sys/malloc.h>
36#include <sys/mutex.h>
37#include <sys/rman.h>
38#include <sys/queue.h>
39#include <sys/taskqueue.h>
40#include <sys/timetc.h>
41#include <machine/bus.h>
42#include <machine/intr.h>
43
44#include <arm/xscale/pxa/pxavar.h>
45#include <arm/xscale/pxa/pxareg.h>
46
47struct pxa_gpio_softc {
48	struct resource *	pg_res[4];
49	bus_space_tag_t		pg_bst;
50	bus_space_handle_t	pg_bsh;
51	struct mtx		pg_mtx;
52
53	uint32_t		pg_intr[3];
54};
55
56static struct resource_spec pxa_gpio_spec[] = {
57	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
58	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
59	{ SYS_RES_IRQ,		1,	RF_ACTIVE },
60	{ SYS_RES_IRQ,		2,	RF_ACTIVE },
61	{ -1, 0 }
62};
63
64static struct pxa_gpio_softc *pxa_gpio_softc = NULL;
65
66static int	pxa_gpio_probe(device_t);
67static int	pxa_gpio_attach(device_t);
68
69static driver_filter_t	pxa_gpio_intr0;
70static driver_filter_t	pxa_gpio_intr1;
71static driver_filter_t	pxa_gpio_intrN;
72
73static int
74pxa_gpio_probe(device_t dev)
75{
76
77	device_set_desc(dev, "GPIO Controller");
78	return (0);
79}
80
81static int
82pxa_gpio_attach(device_t dev)
83{
84	int	error;
85	void	*ihl;
86	struct	pxa_gpio_softc *sc;
87
88	sc = (struct pxa_gpio_softc *)device_get_softc(dev);
89
90	if (pxa_gpio_softc != NULL)
91		return (ENXIO);
92	pxa_gpio_softc = sc;
93
94	error = bus_alloc_resources(dev, pxa_gpio_spec, sc->pg_res);
95	if (error) {
96		device_printf(dev, "could not allocate resources\n");
97		return (ENXIO);
98	}
99
100	sc->pg_bst = rman_get_bustag(sc->pg_res[0]);
101	sc->pg_bsh = rman_get_bushandle(sc->pg_res[0]);
102
103	/* Disable and clear all interrupts. */
104	bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GRER0, 0);
105	bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GRER1, 0);
106	bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GRER2, 0);
107	bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GFER0, 0);
108	bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GFER1, 0);
109	bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GFER2, 0);
110	bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GEDR0, ~0);
111	bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GEDR1, ~0);
112	bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GEDR2, ~0);
113
114	mtx_init(&sc->pg_mtx, "GPIO mutex", NULL, MTX_SPIN);
115
116	if (bus_setup_intr(dev, sc->pg_res[1], INTR_TYPE_MISC|INTR_MPSAFE,
117	    pxa_gpio_intr0, NULL, sc, &ihl) != 0) {
118		bus_release_resources(dev, pxa_gpio_spec, sc->pg_res);
119		device_printf(dev, "could not set up intr0\n");
120		return (ENXIO);
121	}
122
123	if (bus_setup_intr(dev, sc->pg_res[2], INTR_TYPE_MISC|INTR_MPSAFE,
124	    pxa_gpio_intr1, NULL, sc, &ihl) != 0) {
125		bus_release_resources(dev, pxa_gpio_spec, sc->pg_res);
126		device_printf(dev, "could not set up intr1\n");
127		return (ENXIO);
128	}
129
130	if (bus_setup_intr(dev, sc->pg_res[3], INTR_TYPE_MISC|INTR_MPSAFE,
131	    pxa_gpio_intrN, NULL, sc, &ihl) != 0) {
132		bus_release_resources(dev, pxa_gpio_spec, sc->pg_res);
133		device_printf(dev, "could not set up intrN\n");
134		return (ENXIO);
135	}
136
137	return (0);
138}
139
140static int
141pxa_gpio_intr0(void *arg)
142{
143	struct	pxa_gpio_softc *sc;
144
145	sc = (struct pxa_gpio_softc *)arg;
146
147	bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GEDR0, 0x1);
148	sc->pg_intr[0] |= 1;
149
150	return (FILTER_HANDLED);
151}
152
153static int
154pxa_gpio_intr1(void *arg)
155{
156	struct	pxa_gpio_softc *sc;
157
158	sc = (struct pxa_gpio_softc *)arg;
159
160	bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GEDR0, 0x2);
161	sc->pg_intr[1] |= 2;
162
163	return (FILTER_HANDLED);
164}
165
166static int
167pxa_gpio_intrN(void *arg)
168{
169	uint32_t	gedr0, gedr1, gedr2;
170	struct		pxa_gpio_softc *sc;
171
172	sc = (struct pxa_gpio_softc *)arg;
173
174	gedr0 = bus_space_read_4(sc->pg_bst, sc->pg_bsh, GPIO_GEDR0);
175	gedr0 &= 0xfffffffc;
176	bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GEDR0, gedr0);
177
178	gedr1 = bus_space_read_4(sc->pg_bst, sc->pg_bsh, GPIO_GEDR1);
179	bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GEDR1, gedr1);
180
181	gedr2 = bus_space_read_4(sc->pg_bst, sc->pg_bsh, GPIO_GEDR2);
182	gedr2 &= 0x001fffff;
183	bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GEDR2, gedr2);
184
185	sc->pg_intr[0] |= gedr0;
186	sc->pg_intr[1] |= gedr1;
187	sc->pg_intr[2] |= gedr2;
188
189	return (FILTER_HANDLED);
190}
191
192static device_method_t pxa_gpio_methods[] = {
193	DEVMETHOD(device_probe, pxa_gpio_probe),
194	DEVMETHOD(device_attach, pxa_gpio_attach),
195
196	{0, 0}
197};
198
199static driver_t pxa_gpio_driver = {
200	"gpio",
201	pxa_gpio_methods,
202	sizeof(struct pxa_gpio_softc),
203};
204
205static devclass_t pxa_gpio_devclass;
206
207DRIVER_MODULE(pxagpio, pxa, pxa_gpio_driver, pxa_gpio_devclass, 0, 0);
208
209#define	pxagpio_reg_read(softc, reg)		\
210	bus_space_read_4(sc->pg_bst, sc->pg_bsh, reg)
211#define	pxagpio_reg_write(softc, reg, val)	\
212	bus_space_write_4(sc->pg_bst, sc->pg_bsh, reg, val)
213
214uint32_t
215pxa_gpio_get_function(int gpio)
216{
217	struct		pxa_gpio_softc *sc;
218	uint32_t	rv, io;
219
220	sc = pxa_gpio_softc;
221
222	rv = pxagpio_reg_read(sc, GPIO_FN_REG(gpio)) >> GPIO_FN_SHIFT(gpio);
223	rv = GPIO_FN(rv);
224
225	io = pxagpio_reg_read(sc, PXA250_GPIO_REG(GPIO_GPDR0, gpio));
226	if (io & GPIO_BIT(gpio))
227		rv |= GPIO_OUT;
228
229	io = pxagpio_reg_read(sc, PXA250_GPIO_REG(GPIO_GPLR0, gpio));
230	if (io & GPIO_BIT(gpio))
231		rv |= GPIO_SET;
232
233	return (rv);
234}
235
236uint32_t
237pxa_gpio_set_function(int gpio, uint32_t fn)
238{
239	struct		pxa_gpio_softc *sc;
240	uint32_t	rv, bit, oldfn;
241
242	sc = pxa_gpio_softc;
243
244	oldfn = pxa_gpio_get_function(gpio);
245
246	if (GPIO_FN(fn) == GPIO_FN(oldfn) &&
247	    GPIO_FN_IS_OUT(fn) == GPIO_FN_IS_OUT(oldfn)) {
248		/*
249		 * The pin's function is not changing.
250		 * For Alternate Functions and GPIO input, we can just
251		 * return now.
252		 * For GPIO output pins, check the initial state is
253		 * the same.
254		 *
255		 * Return 'fn' instead of 'oldfn' so the caller can
256		 * reliably detect that we didn't change anything.
257		 * (The initial state might be different for non-
258		 * GPIO output pins).
259		 */
260		if (!GPIO_IS_GPIO_OUT(fn) ||
261		    GPIO_FN_IS_SET(fn) == GPIO_FN_IS_SET(oldfn))
262			return (fn);
263	}
264
265	/*
266	 * See section 4.1.3.7 of the PXA2x0 Developer's Manual for
267	 * the correct procedure for changing GPIO pin functions.
268	 */
269
270	bit = GPIO_BIT(gpio);
271
272	/*
273	 * 1. Configure the correct set/clear state of the pin
274	 */
275	if (GPIO_FN_IS_SET(fn))
276		pxagpio_reg_write(sc, PXA250_GPIO_REG(GPIO_GPSR0, gpio), bit);
277	else
278		pxagpio_reg_write(sc, PXA250_GPIO_REG(GPIO_GPCR0, gpio), bit);
279
280	/*
281	 * 2. Configure the pin as an input or output as appropriate
282	 */
283	rv = pxagpio_reg_read(sc, PXA250_GPIO_REG(GPIO_GPDR0, gpio)) & ~bit;
284	if (GPIO_FN_IS_OUT(fn))
285		rv |= bit;
286	pxagpio_reg_write(sc, PXA250_GPIO_REG(GPIO_GPDR0, gpio), rv);
287
288	/*
289	 * 3. Configure the pin's function
290	 */
291	bit = GPIO_FN_MASK << GPIO_FN_SHIFT(gpio);
292	fn = GPIO_FN(fn) << GPIO_FN_SHIFT(gpio);
293	rv = pxagpio_reg_read(sc, GPIO_FN_REG(gpio)) & ~bit;
294	pxagpio_reg_write(sc, GPIO_FN_REG(gpio), rv | fn);
295
296	return (oldfn);
297}
298
299/*
300 * GPIO "interrupt" handling.
301 */
302
303void
304pxa_gpio_mask_irq(int irq)
305{
306	uint32_t	val;
307	struct		pxa_gpio_softc *sc;
308	int		gpio;
309
310	sc = pxa_gpio_softc;
311	gpio = IRQ_TO_GPIO(irq);
312
313	val = pxagpio_reg_read(sc, PXA250_GPIO_REG(GPIO_GRER0, gpio));
314	val &= ~GPIO_BIT(gpio);
315	pxagpio_reg_write(sc, PXA250_GPIO_REG(GPIO_GRER0, gpio), val);
316}
317
318void
319pxa_gpio_unmask_irq(int irq)
320{
321	uint32_t	val;
322	struct		pxa_gpio_softc *sc;
323	int		gpio;
324
325	sc = pxa_gpio_softc;
326	gpio = IRQ_TO_GPIO(irq);
327
328	val = pxagpio_reg_read(sc, PXA250_GPIO_REG(GPIO_GRER0, gpio));
329	val |= GPIO_BIT(gpio);
330	pxagpio_reg_write(sc, PXA250_GPIO_REG(GPIO_GRER0, gpio), val);
331}
332
333int
334pxa_gpio_get_next_irq(void)
335{
336	struct  pxa_gpio_softc *sc;
337	int     gpio;
338
339	sc = pxa_gpio_softc;
340
341	if (sc->pg_intr[0] != 0) {
342		gpio = ffs(sc->pg_intr[0]) - 1;
343		sc->pg_intr[0] &= ~(1 << gpio);
344		return (GPIO_TO_IRQ(gpio));
345	}
346	if (sc->pg_intr[1] != 0) {
347		gpio = ffs(sc->pg_intr[1]) - 1;
348		sc->pg_intr[1] &= ~(1 << gpio);
349		return (GPIO_TO_IRQ(gpio + 32));
350	}
351	if (sc->pg_intr[2] != 0) {
352		gpio = ffs(sc->pg_intr[2]) - 1;
353		sc->pg_intr[2] &= ~(1 << gpio);
354		return (GPIO_TO_IRQ(gpio + 64));
355	}
356
357	return (-1);
358}
359