gpio.c revision 186901
1/*-
2 * Copyright (c) 2006 Benno Rice.
3 * Copyright (C) 2008 MARVELL INTERNATIONAL LTD.
4 * All rights reserved.
5 *
6 * Adapted and extended for Marvell SoCs by Semihalf.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following 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 ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * from: FreeBSD: //depot/projects/arm/src/sys/arm/xscale/pxa2x0/pxa2x0_gpio.c, rev 1
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD: head/sys/arm/mv/gpio.c 186901 2009-01-08 13:25:22Z raj $");
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/bus.h>
37#include <sys/kernel.h>
38#include <sys/lock.h>
39#include <sys/interrupt.h>
40#include <sys/module.h>
41#include <sys/malloc.h>
42#include <sys/mutex.h>
43#include <sys/rman.h>
44#include <sys/queue.h>
45#include <sys/timetc.h>
46#include <machine/bus.h>
47#include <machine/intr.h>
48
49#include <arm/mv/mvvar.h>
50#include <arm/mv/mvreg.h>
51
52#define GPIO_MAX_INTR_COUNT	8
53#define GPIO_PINS_PER_REG	32
54
55struct mv_gpio_softc {
56	struct resource *	res[GPIO_MAX_INTR_COUNT + 1];
57	void			*ih_cookie[GPIO_MAX_INTR_COUNT];
58	bus_space_tag_t		bst;
59	bus_space_handle_t	bsh;
60	uint8_t			pin_num;	/* number of GPIO pins */
61	uint8_t			irq_num;	/* number of real IRQs occupied by GPIO controller */
62	uint8_t			use_high;
63};
64
65extern struct resource_spec mv_gpio_spec[];
66
67static struct mv_gpio_softc *mv_gpio_softc = NULL;
68static uint32_t	gpio_setup[MV_GPIO_MAX_NPINS];
69
70static int	mv_gpio_probe(device_t);
71static int	mv_gpio_attach(device_t);
72static void	mv_gpio_intr(void *);
73
74static void	mv_gpio_intr_handler(int pin);
75static uint32_t	mv_gpio_reg_read(uint32_t reg);
76static void	mv_gpio_reg_write(uint32_t reg, uint32_t val);
77static void	mv_gpio_reg_set(uint32_t reg, uint32_t val);
78static void	mv_gpio_reg_clear(uint32_t reg, uint32_t val);
79
80static void	mv_gpio_blink(uint32_t pin, uint8_t enable);
81static void	mv_gpio_polarity(uint32_t pin, uint8_t enable);
82static void	mv_gpio_level(uint32_t pin, uint8_t enable);
83static void	mv_gpio_edge(uint32_t pin, uint8_t enable);
84static void	mv_gpio_out_en(uint32_t pin, uint8_t enable);
85static void	mv_gpio_int_ack(uint32_t pin);
86static void	mv_gpio_value_set(uint32_t pin, uint8_t val);
87static uint32_t	mv_gpio_value_get(uint32_t pin);
88
89static device_method_t mv_gpio_methods[] = {
90	DEVMETHOD(device_probe,		mv_gpio_probe),
91	DEVMETHOD(device_attach,	mv_gpio_attach),
92	{ 0, 0 }
93};
94
95static driver_t mv_gpio_driver = {
96	"gpio",
97	mv_gpio_methods,
98	sizeof(struct mv_gpio_softc),
99};
100
101static devclass_t mv_gpio_devclass;
102
103DRIVER_MODULE(gpio, mbus, mv_gpio_driver, mv_gpio_devclass, 0, 0);
104
105static int
106mv_gpio_probe(device_t dev)
107{
108
109	device_set_desc(dev, "Marvell Integrated GPIO Controller");
110	return (0);
111}
112
113static int
114mv_gpio_attach(device_t dev)
115{
116	int error, i;
117	struct mv_gpio_softc *sc;
118	uint32_t dev_id, rev_id;
119
120	sc = (struct mv_gpio_softc *)device_get_softc(dev);
121
122	if (mv_gpio_softc != NULL)
123		return (ENXIO);
124	mv_gpio_softc = sc;
125
126	/* Get board id and revision */
127	soc_id(&dev_id, &rev_id);
128
129	if (dev_id == MV_DEV_88F5182 ||
130	    dev_id == MV_DEV_88F5281 ||
131	    dev_id == MV_DEV_MV78100) {
132		sc->pin_num = 32;
133		sc->irq_num = 4;
134		sc->use_high = 0;
135
136	} else if (dev_id == MV_DEV_88F6281) {
137		sc->pin_num = 50;
138		sc->irq_num = 7;
139		sc->use_high = 1;
140
141	} else {
142		device_printf(dev, "unknown board id=0x%x\n", dev_id);
143		return (ENXIO);
144	}
145
146	error = bus_alloc_resources(dev, mv_gpio_spec, sc->res);
147	if (error) {
148		device_printf(dev, "could not allocate resources\n");
149		return (ENXIO);
150	}
151
152	sc->bst = rman_get_bustag(sc->res[0]);
153	sc->bsh = rman_get_bushandle(sc->res[0]);
154
155	/* Disable and clear all interrupts */
156	bus_space_write_4(sc->bst, sc->bsh, GPIO_INT_EDGE_MASK, 0);
157	bus_space_write_4(sc->bst, sc->bsh, GPIO_INT_LEV_MASK, 0);
158	bus_space_write_4(sc->bst, sc->bsh, GPIO_INT_CAUSE, 0);
159
160	if (sc->use_high) {
161		bus_space_write_4(sc->bst, sc->bsh,
162		    GPIO_HI_INT_EDGE_MASK, 0);
163		bus_space_write_4(sc->bst, sc->bsh,
164		    GPIO_HI_INT_LEV_MASK, 0);
165		bus_space_write_4(sc->bst, sc->bsh,
166		    GPIO_HI_INT_CAUSE, 0);
167	}
168
169	for (i = 0; i < sc->irq_num; i++) {
170		if (bus_setup_intr(dev, sc->res[1 + i],
171		    INTR_TYPE_MISC | INTR_FAST,
172		    (driver_filter_t *)mv_gpio_intr, NULL,
173		    sc, &sc->ih_cookie[i]) != 0) {
174			bus_release_resources(dev, mv_gpio_spec, sc->res);
175			device_printf(dev, "could not set up intr %d\n", i);
176			return (ENXIO);
177		}
178	}
179
180	return (0);
181}
182
183static void
184mv_gpio_intr(void *arg)
185{
186	uint32_t	int_cause, gpio_val;
187	uint32_t	int_cause_hi, gpio_val_hi = 0;
188	int		i;
189
190	int_cause = mv_gpio_reg_read(GPIO_INT_CAUSE);
191	gpio_val = mv_gpio_reg_read(GPIO_DATA_IN);
192	gpio_val &= int_cause;
193	if (mv_gpio_softc->use_high) {
194		int_cause_hi = mv_gpio_reg_read(GPIO_HI_INT_CAUSE);
195		gpio_val_hi = mv_gpio_reg_read(GPIO_HI_DATA_IN);
196		gpio_val_hi &= int_cause_hi;
197	}
198
199	i = 0;
200	while (gpio_val != 0) {
201		if (gpio_val & 1)
202			mv_gpio_intr_handler(i);
203		gpio_val >>= 1;
204		i++;
205	}
206
207	if (mv_gpio_softc->use_high) {
208		i = 0;
209		while (gpio_val_hi != 0) {
210			if (gpio_val_hi & 1)
211				mv_gpio_intr_handler(i + GPIO_PINS_PER_REG);
212			gpio_val_hi >>= 1;
213			i++;
214		}
215	}
216}
217
218/*
219 * GPIO interrupt handling
220 */
221
222static struct intr_event *gpio_events[MV_GPIO_MAX_NPINS];
223
224int
225mv_gpio_setup_intrhandler(const char *name, driver_filter_t *filt,
226    void (*hand)(void *), void *arg, int pin, int flags, void **cookiep)
227{
228	struct	intr_event *event;
229	int	error;
230
231	if (pin < 0 || pin >= mv_gpio_softc->pin_num)
232		return (ENXIO);
233	event = gpio_events[pin];
234	if (event == NULL) {
235		error = intr_event_create(&event, (void *)pin, 0, pin,
236		    (void (*)(void *))mv_gpio_intr_mask,
237		    (void (*)(void *))mv_gpio_intr_unmask,
238		    (void (*)(void *))mv_gpio_int_ack,
239		    NULL,
240		    "gpio%d:", pin);
241		if (error != 0)
242			return (error);
243		gpio_events[pin] = event;
244	}
245
246	intr_event_add_handler(event, name, filt, hand, arg,
247	    intr_priority(flags), flags, cookiep);
248	return (0);
249}
250
251void
252mv_gpio_intr_mask(int pin)
253{
254
255	if (pin >= mv_gpio_softc->pin_num)
256		return;
257
258	if (gpio_setup[pin] & MV_GPIO_EDGE)
259		mv_gpio_edge(pin, 0);
260	else
261		mv_gpio_level(pin, 0);
262}
263
264void
265mv_gpio_intr_unmask(int pin)
266{
267
268	if (pin >= mv_gpio_softc->pin_num)
269		return;
270
271	if (gpio_setup[pin] & MV_GPIO_EDGE)
272		mv_gpio_edge(pin, 1);
273	else
274		mv_gpio_level(pin, 1);
275}
276
277static void
278mv_gpio_intr_handler(int pin)
279{
280	struct intr_event *event;
281
282	event = gpio_events[pin];
283	if (event == NULL || TAILQ_EMPTY(&event->ie_handlers))
284		return;
285
286	intr_event_handle(event, NULL);
287}
288
289int
290mv_gpio_configure(uint32_t pin, uint32_t flags, uint32_t mask)
291{
292
293	if (pin >= mv_gpio_softc->pin_num)
294		return (EINVAL);
295
296	if (mask & MV_GPIO_BLINK)
297		mv_gpio_blink(pin, flags & MV_GPIO_BLINK);
298	if (mask & MV_GPIO_POLARITY)
299		mv_gpio_polarity(pin, flags & MV_GPIO_POLARITY);
300	if (mask & MV_GPIO_EDGE)
301		mv_gpio_edge(pin, flags & MV_GPIO_EDGE);
302	if (mask & MV_GPIO_LEVEL)
303		mv_gpio_level(pin, flags & MV_GPIO_LEVEL);
304
305	gpio_setup[pin] &= ~(mask);
306	gpio_setup[pin] |= (flags & mask);
307
308	return (0);
309}
310
311void
312mv_gpio_out(uint32_t pin, uint8_t val, uint8_t enable)
313{
314
315	mv_gpio_value_set(pin, val);
316	mv_gpio_out_en(pin, enable);
317}
318
319uint8_t
320mv_gpio_in(uint32_t pin)
321{
322
323	return (mv_gpio_value_get(pin));
324}
325
326static uint32_t
327mv_gpio_reg_read(uint32_t reg)
328{
329
330	return (bus_space_read_4(mv_gpio_softc->bst,
331	    mv_gpio_softc->bsh, reg));
332}
333
334static void
335mv_gpio_reg_write(uint32_t reg, uint32_t val)
336{
337
338	bus_space_write_4(mv_gpio_softc->bst,
339	    mv_gpio_softc->bsh, reg, val);
340}
341
342static void
343mv_gpio_reg_set(uint32_t reg, uint32_t pin)
344{
345	uint32_t reg_val;
346
347	reg_val = mv_gpio_reg_read(reg);
348	reg_val |= GPIO(pin);
349	mv_gpio_reg_write(reg, reg_val);
350}
351
352static void
353mv_gpio_reg_clear(uint32_t reg, uint32_t pin)
354{
355	uint32_t reg_val;
356
357	reg_val = mv_gpio_reg_read(reg);
358	reg_val &= ~(GPIO(pin));
359	mv_gpio_reg_write(reg, reg_val);
360}
361
362static void
363mv_gpio_out_en(uint32_t pin, uint8_t enable)
364{
365	uint32_t reg;
366
367	if (pin >= mv_gpio_softc->pin_num)
368		return;
369
370	if (pin >= GPIO_PINS_PER_REG) {
371		reg = GPIO_HI_DATA_OUT_EN_CTRL;
372		pin -= GPIO_PINS_PER_REG;
373	} else
374		reg = GPIO_DATA_OUT_EN_CTRL;
375
376	if (enable)
377		mv_gpio_reg_clear(reg, pin);
378	else
379		mv_gpio_reg_set(reg, pin);
380}
381
382static void
383mv_gpio_blink(uint32_t pin, uint8_t enable)
384{
385	uint32_t reg;
386
387	if (pin >= mv_gpio_softc->pin_num)
388		return;
389
390	if (pin >= GPIO_PINS_PER_REG) {
391		reg = GPIO_HI_BLINK_EN;
392		pin -= GPIO_PINS_PER_REG;
393	} else
394		reg = GPIO_BLINK_EN;
395
396	if (enable)
397		mv_gpio_reg_set(reg, pin);
398	else
399		mv_gpio_reg_clear(reg, pin);
400}
401
402static void
403mv_gpio_polarity(uint32_t pin, uint8_t enable)
404{
405	uint32_t reg;
406
407	if (pin >= mv_gpio_softc->pin_num)
408		return;
409
410	if (pin >= GPIO_PINS_PER_REG) {
411		reg = GPIO_HI_DATA_IN_POLAR;
412		pin -= GPIO_PINS_PER_REG;
413	} else
414		reg = GPIO_DATA_IN_POLAR;
415
416	if (enable)
417		mv_gpio_reg_set(reg, pin);
418	else
419		mv_gpio_reg_clear(reg, pin);
420}
421
422static void
423mv_gpio_level(uint32_t pin, uint8_t enable)
424{
425	uint32_t reg;
426
427	if (pin >= mv_gpio_softc->pin_num)
428		return;
429
430	if (pin >= GPIO_PINS_PER_REG) {
431		reg = GPIO_HI_INT_LEV_MASK;
432		pin -= GPIO_PINS_PER_REG;
433	} else
434		reg = GPIO_INT_LEV_MASK;
435
436	if (enable)
437		mv_gpio_reg_set(reg, pin);
438	else
439		mv_gpio_reg_clear(reg, pin);
440}
441
442static void
443mv_gpio_edge(uint32_t pin, uint8_t enable)
444{
445	uint32_t reg;
446
447	if (pin >= mv_gpio_softc->pin_num)
448		return;
449
450	if (pin >= GPIO_PINS_PER_REG) {
451		reg = GPIO_HI_INT_EDGE_MASK;
452		pin -= GPIO_PINS_PER_REG;
453	} else
454		reg = GPIO_INT_EDGE_MASK;
455
456	if (enable)
457		mv_gpio_reg_set(reg, pin);
458	else
459		mv_gpio_reg_clear(reg, pin);
460}
461
462static void
463mv_gpio_int_ack(uint32_t pin)
464{
465	uint32_t reg;
466
467	if (pin >= mv_gpio_softc->pin_num)
468		return;
469
470	if (pin >= GPIO_PINS_PER_REG) {
471		reg = GPIO_HI_INT_CAUSE;
472		pin -= GPIO_PINS_PER_REG;
473	} else
474		reg = GPIO_INT_CAUSE;
475
476	mv_gpio_reg_clear(reg, pin);
477}
478
479static uint32_t
480mv_gpio_value_get(uint32_t pin)
481{
482	uint32_t reg, reg_val;
483
484	if (pin >= mv_gpio_softc->pin_num)
485		return (0);
486
487	if (pin >= GPIO_PINS_PER_REG) {
488		reg = GPIO_HI_DATA_IN;
489		pin -= GPIO_PINS_PER_REG;
490	} else
491		reg = GPIO_DATA_IN;
492
493	reg_val = mv_gpio_reg_read(reg);
494
495	return (reg_val & GPIO(pin));
496}
497
498static void
499mv_gpio_value_set(uint32_t pin, uint8_t val)
500{
501	uint32_t reg;
502
503	if (pin >= mv_gpio_softc->pin_num)
504		return;
505
506	if (pin >= GPIO_PINS_PER_REG) {
507		reg = GPIO_HI_DATA_OUT;
508		pin -= GPIO_PINS_PER_REG;
509	} else
510		reg = GPIO_DATA_OUT;
511
512	if (val)
513		mv_gpio_reg_set(reg, pin);
514	else
515		mv_gpio_reg_clear(reg, pin);
516}
517