1243465Sgonzo/*-
2243465Sgonzo * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
3243465Sgonzo * Copyright (c) 2012 Luiz Otavio O Souza.
4243465Sgonzo * All rights reserved.
5243465Sgonzo *
6243465Sgonzo * Redistribution and use in source and binary forms, with or without
7243465Sgonzo * modification, are permitted provided that the following conditions
8243465Sgonzo * are met:
9243465Sgonzo * 1. Redistributions of source code must retain the above copyright
10243465Sgonzo *    notice, this list of conditions and the following disclaimer.
11243465Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
12243465Sgonzo *    notice, this list of conditions and the following disclaimer in the
13243465Sgonzo *    documentation and/or other materials provided with the distribution.
14243465Sgonzo *
15243465Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16243465Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17243465Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18243465Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19243465Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20243465Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21243465Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22243465Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23243465Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24243465Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25243465Sgonzo * SUCH DAMAGE.
26243465Sgonzo *
27243465Sgonzo */
28243465Sgonzo#include <sys/cdefs.h>
29243465Sgonzo__FBSDID("$FreeBSD$");
30243465Sgonzo
31243465Sgonzo#include <sys/param.h>
32243465Sgonzo#include <sys/systm.h>
33243465Sgonzo#include <sys/bus.h>
34243465Sgonzo
35243465Sgonzo#include <sys/kernel.h>
36243465Sgonzo#include <sys/module.h>
37243465Sgonzo#include <sys/rman.h>
38243465Sgonzo#include <sys/lock.h>
39243465Sgonzo#include <sys/mutex.h>
40243465Sgonzo#include <sys/gpio.h>
41244412Sgonzo#include <sys/sysctl.h>
42243465Sgonzo
43243465Sgonzo#include <machine/bus.h>
44243465Sgonzo#include <machine/cpu.h>
45243465Sgonzo#include <machine/cpufunc.h>
46243465Sgonzo#include <machine/resource.h>
47243465Sgonzo#include <machine/fdt.h>
48243465Sgonzo#include <machine/frame.h>
49243465Sgonzo#include <machine/intr.h>
50243465Sgonzo
51243465Sgonzo#include <dev/fdt/fdt_common.h>
52243465Sgonzo#include <dev/ofw/ofw_bus.h>
53243465Sgonzo#include <dev/ofw/ofw_bus_subr.h>
54243465Sgonzo
55255370Sloos#include <arm/broadcom/bcm2835/bcm2835_gpio.h>
56255370Sloos
57243465Sgonzo#include "gpio_if.h"
58243465Sgonzo
59243465Sgonzo#undef	DEBUG
60243465Sgonzo
61243465Sgonzo#ifdef DEBUG
62243465Sgonzo#define dprintf(fmt, args...) do { printf("%s(): ", __func__);   \
63243465Sgonzo    printf(fmt,##args); } while (0)
64243465Sgonzo#else
65243465Sgonzo#define dprintf(fmt, args...)
66243465Sgonzo#endif
67243465Sgonzo
68243465Sgonzo#define	BCM_GPIO_PINS		54
69243465Sgonzo#define	BCM_GPIO_DEFAULT_CAPS	(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |	\
70243465Sgonzo    GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN)
71243465Sgonzo
72244412Sgonzostruct bcm_gpio_sysctl {
73244412Sgonzo	struct bcm_gpio_softc	*sc;
74244412Sgonzo	uint32_t		pin;
75244412Sgonzo};
76244412Sgonzo
77243465Sgonzostruct bcm_gpio_softc {
78243465Sgonzo	device_t		sc_dev;
79243465Sgonzo	struct mtx		sc_mtx;
80243465Sgonzo	struct resource *	sc_mem_res;
81243465Sgonzo	struct resource *	sc_irq_res;
82243465Sgonzo	bus_space_tag_t		sc_bst;
83243465Sgonzo	bus_space_handle_t	sc_bsh;
84243465Sgonzo	void *			sc_intrhand;
85243465Sgonzo	int			sc_gpio_npins;
86243465Sgonzo	int			sc_ro_npins;
87243465Sgonzo	int			sc_ro_pins[BCM_GPIO_PINS];
88243465Sgonzo	struct gpio_pin		sc_gpio_pins[BCM_GPIO_PINS];
89244412Sgonzo	struct bcm_gpio_sysctl	sc_sysctl[BCM_GPIO_PINS];
90243465Sgonzo};
91243465Sgonzo
92243465Sgonzoenum bcm_gpio_pud {
93243465Sgonzo	BCM_GPIO_NONE,
94243465Sgonzo	BCM_GPIO_PULLDOWN,
95243465Sgonzo	BCM_GPIO_PULLUP,
96243465Sgonzo};
97243465Sgonzo
98243465Sgonzo#define	BCM_GPIO_LOCK(_sc)	mtx_lock(&_sc->sc_mtx)
99243465Sgonzo#define	BCM_GPIO_UNLOCK(_sc)	mtx_unlock(&_sc->sc_mtx)
100244412Sgonzo#define	BCM_GPIO_LOCK_ASSERT(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED)
101243465Sgonzo
102243465Sgonzo#define	BCM_GPIO_GPFSEL(_bank)	0x00 + _bank * 4
103243465Sgonzo#define	BCM_GPIO_GPSET(_bank)	0x1c + _bank * 4
104243465Sgonzo#define	BCM_GPIO_GPCLR(_bank)	0x28 + _bank * 4
105243465Sgonzo#define	BCM_GPIO_GPLEV(_bank)	0x34 + _bank * 4
106243465Sgonzo#define	BCM_GPIO_GPPUD(_bank)	0x94
107243465Sgonzo#define	BCM_GPIO_GPPUDCLK(_bank)	0x98 + _bank * 4
108243465Sgonzo
109243465Sgonzo#define	BCM_GPIO_WRITE(_sc, _off, _val)		\
110243465Sgonzo    bus_space_write_4(_sc->sc_bst, _sc->sc_bsh, _off, _val)
111243465Sgonzo#define	BCM_GPIO_READ(_sc, _off)		\
112243465Sgonzo    bus_space_read_4(_sc->sc_bst, _sc->sc_bsh, _off)
113243465Sgonzo
114243465Sgonzostatic int
115243465Sgonzobcm_gpio_pin_is_ro(struct bcm_gpio_softc *sc, int pin)
116243465Sgonzo{
117243465Sgonzo	int i;
118243465Sgonzo
119243465Sgonzo	for (i = 0; i < sc->sc_ro_npins; i++)
120243465Sgonzo		if (pin == sc->sc_ro_pins[i])
121243465Sgonzo			return (1);
122243465Sgonzo	return (0);
123243465Sgonzo}
124243465Sgonzo
125243465Sgonzostatic uint32_t
126243465Sgonzobcm_gpio_get_function(struct bcm_gpio_softc *sc, uint32_t pin)
127243465Sgonzo{
128244412Sgonzo	uint32_t bank, func, offset;
129243465Sgonzo
130243465Sgonzo	/* Five banks, 10 pins per bank, 3 bits per pin. */
131243465Sgonzo	bank = pin / 10;
132243465Sgonzo	offset = (pin - bank * 10) * 3;
133243465Sgonzo
134243465Sgonzo	BCM_GPIO_LOCK(sc);
135244412Sgonzo	func = (BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank)) >> offset) & 7;
136243465Sgonzo	BCM_GPIO_UNLOCK(sc);
137243465Sgonzo
138244412Sgonzo	return (func);
139244412Sgonzo}
140244412Sgonzo
141244412Sgonzostatic void
142244412Sgonzobcm_gpio_func_str(uint32_t nfunc, char *buf, int bufsize)
143244412Sgonzo{
144244412Sgonzo
145244412Sgonzo	switch (nfunc) {
146243465Sgonzo	case BCM_GPIO_INPUT:
147244412Sgonzo		strncpy(buf, "input", bufsize);
148243465Sgonzo		break;
149243465Sgonzo	case BCM_GPIO_OUTPUT:
150244412Sgonzo		strncpy(buf, "output", bufsize);
151243465Sgonzo		break;
152243465Sgonzo	case BCM_GPIO_ALT0:
153244412Sgonzo		strncpy(buf, "alt0", bufsize);
154243465Sgonzo		break;
155243465Sgonzo	case BCM_GPIO_ALT1:
156244412Sgonzo		strncpy(buf, "alt1", bufsize);
157243465Sgonzo		break;
158243465Sgonzo	case BCM_GPIO_ALT2:
159244412Sgonzo		strncpy(buf, "alt2", bufsize);
160243465Sgonzo		break;
161243465Sgonzo	case BCM_GPIO_ALT3:
162244412Sgonzo		strncpy(buf, "alt3", bufsize);
163243465Sgonzo		break;
164243465Sgonzo	case BCM_GPIO_ALT4:
165244412Sgonzo		strncpy(buf, "alt4", bufsize);
166243465Sgonzo		break;
167243465Sgonzo	case BCM_GPIO_ALT5:
168244412Sgonzo		strncpy(buf, "alt5", bufsize);
169243465Sgonzo		break;
170244412Sgonzo	default:
171244412Sgonzo		strncpy(buf, "invalid", bufsize);
172243465Sgonzo	}
173244412Sgonzo}
174243465Sgonzo
175244412Sgonzostatic int
176244412Sgonzobcm_gpio_str_func(char *func, uint32_t *nfunc)
177244412Sgonzo{
178244412Sgonzo
179244412Sgonzo	if (strcasecmp(func, "input") == 0)
180244412Sgonzo		*nfunc = BCM_GPIO_INPUT;
181244412Sgonzo	else if (strcasecmp(func, "output") == 0)
182244412Sgonzo		*nfunc = BCM_GPIO_OUTPUT;
183244412Sgonzo	else if (strcasecmp(func, "alt0") == 0)
184244412Sgonzo		*nfunc = BCM_GPIO_ALT0;
185244412Sgonzo	else if (strcasecmp(func, "alt1") == 0)
186244412Sgonzo		*nfunc = BCM_GPIO_ALT1;
187244412Sgonzo	else if (strcasecmp(func, "alt2") == 0)
188244412Sgonzo		*nfunc = BCM_GPIO_ALT2;
189244412Sgonzo	else if (strcasecmp(func, "alt3") == 0)
190244412Sgonzo		*nfunc = BCM_GPIO_ALT3;
191244412Sgonzo	else if (strcasecmp(func, "alt4") == 0)
192244412Sgonzo		*nfunc = BCM_GPIO_ALT4;
193244412Sgonzo	else if (strcasecmp(func, "alt5") == 0)
194244412Sgonzo		*nfunc = BCM_GPIO_ALT5;
195244412Sgonzo	else
196244412Sgonzo		return (-1);
197244412Sgonzo
198244412Sgonzo	return (0);
199244412Sgonzo}
200244412Sgonzo
201244412Sgonzostatic uint32_t
202244412Sgonzobcm_gpio_func_flag(uint32_t nfunc)
203244412Sgonzo{
204244412Sgonzo
205244412Sgonzo	switch (nfunc) {
206243465Sgonzo	case BCM_GPIO_INPUT:
207243465Sgonzo		return (GPIO_PIN_INPUT);
208243465Sgonzo	case BCM_GPIO_OUTPUT:
209243465Sgonzo		return (GPIO_PIN_OUTPUT);
210243465Sgonzo	}
211243465Sgonzo	return (0);
212243465Sgonzo}
213243465Sgonzo
214243465Sgonzostatic void
215243465Sgonzobcm_gpio_set_function(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t f)
216243465Sgonzo{
217243465Sgonzo	uint32_t bank, data, offset;
218243465Sgonzo
219244412Sgonzo	/* Must be called with lock held. */
220244412Sgonzo	BCM_GPIO_LOCK_ASSERT(sc);
221244412Sgonzo
222243465Sgonzo	/* Five banks, 10 pins per bank, 3 bits per pin. */
223243465Sgonzo	bank = pin / 10;
224243465Sgonzo	offset = (pin - bank * 10) * 3;
225243465Sgonzo
226243465Sgonzo	data = BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank));
227243465Sgonzo	data &= ~(7 << offset);
228243465Sgonzo	data |= (f << offset);
229243465Sgonzo	BCM_GPIO_WRITE(sc, BCM_GPIO_GPFSEL(bank), data);
230243465Sgonzo}
231243465Sgonzo
232243465Sgonzostatic void
233243465Sgonzobcm_gpio_set_pud(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t state)
234243465Sgonzo{
235243465Sgonzo	uint32_t bank, offset;
236243465Sgonzo
237244412Sgonzo	/* Must be called with lock held. */
238244412Sgonzo	BCM_GPIO_LOCK_ASSERT(sc);
239244412Sgonzo
240243465Sgonzo	bank = pin / 32;
241243465Sgonzo	offset = pin - 32 * bank;
242243465Sgonzo
243243465Sgonzo	BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), state);
244243465Sgonzo	DELAY(10);
245243465Sgonzo	BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), (1 << offset));
246243465Sgonzo	DELAY(10);
247243465Sgonzo	BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), 0);
248243465Sgonzo	BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), 0);
249243465Sgonzo}
250243465Sgonzo
251255370Sloosvoid
252255370Sloosbcm_gpio_set_alternate(device_t dev, uint32_t pin, uint32_t nfunc)
253255370Sloos{
254255370Sloos	struct bcm_gpio_softc *sc;
255255370Sloos	int i;
256255370Sloos
257255370Sloos	sc = device_get_softc(dev);
258255370Sloos	BCM_GPIO_LOCK(sc);
259255370Sloos
260255370Sloos	/* Disable pull-up or pull-down on pin. */
261255370Sloos	bcm_gpio_set_pud(sc, pin, BCM_GPIO_NONE);
262255370Sloos
263255370Sloos	/* And now set the pin function. */
264255370Sloos	bcm_gpio_set_function(sc, pin, nfunc);
265255370Sloos
266255370Sloos	/* Update the pin flags. */
267255370Sloos	for (i = 0; i < sc->sc_gpio_npins; i++) {
268255370Sloos		if (sc->sc_gpio_pins[i].gp_pin == pin)
269255370Sloos			break;
270255370Sloos	}
271255370Sloos	if (i < sc->sc_gpio_npins)
272255370Sloos		sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(nfunc);
273255370Sloos
274255370Sloos        BCM_GPIO_UNLOCK(sc);
275255370Sloos}
276255370Sloos
277243465Sgonzostatic void
278243465Sgonzobcm_gpio_pin_configure(struct bcm_gpio_softc *sc, struct gpio_pin *pin,
279243465Sgonzo    unsigned int flags)
280243465Sgonzo{
281243465Sgonzo
282244412Sgonzo	BCM_GPIO_LOCK(sc);
283244412Sgonzo
284243465Sgonzo	/*
285243465Sgonzo	 * Manage input/output.
286243465Sgonzo	 */
287243465Sgonzo	if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) {
288243465Sgonzo		pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT);
289243465Sgonzo		if (flags & GPIO_PIN_OUTPUT) {
290243465Sgonzo			pin->gp_flags |= GPIO_PIN_OUTPUT;
291243465Sgonzo			bcm_gpio_set_function(sc, pin->gp_pin,
292243465Sgonzo			    BCM_GPIO_OUTPUT);
293243465Sgonzo		} else {
294243465Sgonzo			pin->gp_flags |= GPIO_PIN_INPUT;
295243465Sgonzo			bcm_gpio_set_function(sc, pin->gp_pin,
296243465Sgonzo			    BCM_GPIO_INPUT);
297243465Sgonzo		}
298243465Sgonzo	}
299243465Sgonzo
300243465Sgonzo	/* Manage Pull-up/pull-down. */
301243465Sgonzo	pin->gp_flags &= ~(GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN);
302243465Sgonzo	if (flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) {
303243465Sgonzo		if (flags & GPIO_PIN_PULLUP) {
304243465Sgonzo			pin->gp_flags |= GPIO_PIN_PULLUP;
305243465Sgonzo			bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLUP);
306243465Sgonzo		} else {
307243465Sgonzo			pin->gp_flags |= GPIO_PIN_PULLDOWN;
308243465Sgonzo			bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLDOWN);
309243465Sgonzo		}
310243465Sgonzo	} else
311243465Sgonzo		bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_NONE);
312244412Sgonzo
313244412Sgonzo	BCM_GPIO_UNLOCK(sc);
314243465Sgonzo}
315243465Sgonzo
316243465Sgonzostatic int
317243465Sgonzobcm_gpio_pin_max(device_t dev, int *maxpin)
318243465Sgonzo{
319243465Sgonzo
320243465Sgonzo	*maxpin = BCM_GPIO_PINS - 1;
321243465Sgonzo	return (0);
322243465Sgonzo}
323243465Sgonzo
324243465Sgonzostatic int
325243465Sgonzobcm_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
326243465Sgonzo{
327243465Sgonzo	struct bcm_gpio_softc *sc = device_get_softc(dev);
328243465Sgonzo	int i;
329243465Sgonzo
330243465Sgonzo	for (i = 0; i < sc->sc_gpio_npins; i++) {
331243465Sgonzo		if (sc->sc_gpio_pins[i].gp_pin == pin)
332243465Sgonzo			break;
333243465Sgonzo	}
334243465Sgonzo
335243465Sgonzo	if (i >= sc->sc_gpio_npins)
336243465Sgonzo		return (EINVAL);
337243465Sgonzo
338243465Sgonzo	BCM_GPIO_LOCK(sc);
339243465Sgonzo	*caps = sc->sc_gpio_pins[i].gp_caps;
340243465Sgonzo	BCM_GPIO_UNLOCK(sc);
341243465Sgonzo
342243465Sgonzo	return (0);
343243465Sgonzo}
344243465Sgonzo
345243465Sgonzostatic int
346243465Sgonzobcm_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
347243465Sgonzo{
348243465Sgonzo	struct bcm_gpio_softc *sc = device_get_softc(dev);
349243465Sgonzo	int i;
350243465Sgonzo
351243465Sgonzo	for (i = 0; i < sc->sc_gpio_npins; i++) {
352243465Sgonzo		if (sc->sc_gpio_pins[i].gp_pin == pin)
353243465Sgonzo			break;
354243465Sgonzo	}
355243465Sgonzo
356243465Sgonzo	if (i >= sc->sc_gpio_npins)
357243465Sgonzo		return (EINVAL);
358243465Sgonzo
359243465Sgonzo	BCM_GPIO_LOCK(sc);
360243465Sgonzo	*flags = sc->sc_gpio_pins[i].gp_flags;
361243465Sgonzo	BCM_GPIO_UNLOCK(sc);
362243465Sgonzo
363243465Sgonzo	return (0);
364243465Sgonzo}
365243465Sgonzo
366243465Sgonzostatic int
367243465Sgonzobcm_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
368243465Sgonzo{
369243465Sgonzo	struct bcm_gpio_softc *sc = device_get_softc(dev);
370243465Sgonzo	int i;
371243465Sgonzo
372243465Sgonzo	for (i = 0; i < sc->sc_gpio_npins; i++) {
373243465Sgonzo		if (sc->sc_gpio_pins[i].gp_pin == pin)
374243465Sgonzo			break;
375243465Sgonzo	}
376243465Sgonzo
377243465Sgonzo	if (i >= sc->sc_gpio_npins)
378243465Sgonzo		return (EINVAL);
379243465Sgonzo
380243465Sgonzo	BCM_GPIO_LOCK(sc);
381243465Sgonzo	memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME);
382243465Sgonzo	BCM_GPIO_UNLOCK(sc);
383243465Sgonzo
384243465Sgonzo	return (0);
385243465Sgonzo}
386243465Sgonzo
387243465Sgonzostatic int
388243465Sgonzobcm_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
389243465Sgonzo{
390243465Sgonzo	struct bcm_gpio_softc *sc = device_get_softc(dev);
391243465Sgonzo	int i;
392243465Sgonzo
393243465Sgonzo	for (i = 0; i < sc->sc_gpio_npins; i++) {
394243465Sgonzo		if (sc->sc_gpio_pins[i].gp_pin == pin)
395243465Sgonzo			break;
396243465Sgonzo	}
397243465Sgonzo
398243465Sgonzo	if (i >= sc->sc_gpio_npins)
399243465Sgonzo		return (EINVAL);
400243465Sgonzo
401243465Sgonzo	/* We never touch on read-only/reserved pins. */
402243465Sgonzo	if (bcm_gpio_pin_is_ro(sc, pin))
403243465Sgonzo		return (EINVAL);
404243465Sgonzo
405249449Sdim	/* Check for unwanted flags. */
406249449Sdim	if ((flags & sc->sc_gpio_pins[i].gp_caps) != flags)
407243465Sgonzo		return (EINVAL);
408243465Sgonzo
409243465Sgonzo	/* Can't mix input/output together. */
410243465Sgonzo	if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) ==
411243465Sgonzo	    (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT))
412243465Sgonzo		return (EINVAL);
413243465Sgonzo
414243465Sgonzo	/* Can't mix pull-up/pull-down together. */
415243465Sgonzo	if ((flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) ==
416243465Sgonzo	    (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN))
417243465Sgonzo		return (EINVAL);
418243465Sgonzo
419243465Sgonzo	bcm_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags);
420243465Sgonzo
421243465Sgonzo	return (0);
422243465Sgonzo}
423243465Sgonzo
424243465Sgonzostatic int
425243465Sgonzobcm_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
426243465Sgonzo{
427243465Sgonzo	struct bcm_gpio_softc *sc = device_get_softc(dev);
428243465Sgonzo	uint32_t bank, offset;
429243465Sgonzo	int i;
430243465Sgonzo
431243465Sgonzo	for (i = 0; i < sc->sc_gpio_npins; i++) {
432243465Sgonzo		if (sc->sc_gpio_pins[i].gp_pin == pin)
433243465Sgonzo			break;
434243465Sgonzo	}
435243465Sgonzo
436243465Sgonzo	if (i >= sc->sc_gpio_npins)
437243465Sgonzo		return (EINVAL);
438243465Sgonzo
439243465Sgonzo	/* We never write to read-only/reserved pins. */
440243465Sgonzo	if (bcm_gpio_pin_is_ro(sc, pin))
441243465Sgonzo		return (EINVAL);
442243465Sgonzo
443243465Sgonzo	bank = pin / 32;
444243465Sgonzo	offset = pin - 32 * bank;
445243465Sgonzo
446243465Sgonzo	BCM_GPIO_LOCK(sc);
447243465Sgonzo	if (value)
448243465Sgonzo		BCM_GPIO_WRITE(sc, BCM_GPIO_GPSET(bank), (1 << offset));
449243465Sgonzo	else
450243465Sgonzo		BCM_GPIO_WRITE(sc, BCM_GPIO_GPCLR(bank), (1 << offset));
451243465Sgonzo	BCM_GPIO_UNLOCK(sc);
452243465Sgonzo
453243465Sgonzo	return (0);
454243465Sgonzo}
455243465Sgonzo
456243465Sgonzostatic int
457243465Sgonzobcm_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
458243465Sgonzo{
459243465Sgonzo	struct bcm_gpio_softc *sc = device_get_softc(dev);
460243465Sgonzo	uint32_t bank, offset, reg_data;
461243465Sgonzo	int i;
462243465Sgonzo
463243465Sgonzo	for (i = 0; i < sc->sc_gpio_npins; i++) {
464243465Sgonzo		if (sc->sc_gpio_pins[i].gp_pin == pin)
465243465Sgonzo			break;
466243465Sgonzo	}
467243465Sgonzo
468243465Sgonzo	if (i >= sc->sc_gpio_npins)
469243465Sgonzo		return (EINVAL);
470243465Sgonzo
471243465Sgonzo	bank = pin / 32;
472243465Sgonzo	offset = pin - 32 * bank;
473243465Sgonzo
474243465Sgonzo	BCM_GPIO_LOCK(sc);
475243465Sgonzo	reg_data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank));
476243465Sgonzo	BCM_GPIO_UNLOCK(sc);
477243465Sgonzo	*val = (reg_data & (1 << offset)) ? 1 : 0;
478243465Sgonzo
479243465Sgonzo	return (0);
480243465Sgonzo}
481243465Sgonzo
482243465Sgonzostatic int
483243465Sgonzobcm_gpio_pin_toggle(device_t dev, uint32_t pin)
484243465Sgonzo{
485243465Sgonzo	struct bcm_gpio_softc *sc = device_get_softc(dev);
486243465Sgonzo	uint32_t bank, data, offset;
487243465Sgonzo	int i;
488243465Sgonzo
489243465Sgonzo	for (i = 0; i < sc->sc_gpio_npins; i++) {
490243465Sgonzo		if (sc->sc_gpio_pins[i].gp_pin == pin)
491243465Sgonzo			break;
492243465Sgonzo	}
493243465Sgonzo
494243465Sgonzo	if (i >= sc->sc_gpio_npins)
495243465Sgonzo		return (EINVAL);
496243465Sgonzo
497243465Sgonzo	/* We never write to read-only/reserved pins. */
498243465Sgonzo	if (bcm_gpio_pin_is_ro(sc, pin))
499243465Sgonzo		return (EINVAL);
500243465Sgonzo
501243465Sgonzo	bank = pin / 32;
502243465Sgonzo	offset = pin - 32 * bank;
503243465Sgonzo
504243465Sgonzo	BCM_GPIO_LOCK(sc);
505243465Sgonzo	data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank));
506243465Sgonzo	if (data & (1 << offset))
507243465Sgonzo		BCM_GPIO_WRITE(sc, BCM_GPIO_GPCLR(bank), (1 << offset));
508243465Sgonzo	else
509243465Sgonzo		BCM_GPIO_WRITE(sc, BCM_GPIO_GPSET(bank), (1 << offset));
510243465Sgonzo	BCM_GPIO_UNLOCK(sc);
511243465Sgonzo
512243465Sgonzo	return (0);
513243465Sgonzo}
514243465Sgonzo
515243465Sgonzostatic int
516243465Sgonzobcm_gpio_get_ro_pins(struct bcm_gpio_softc *sc)
517243465Sgonzo{
518243465Sgonzo	int i, len;
519243465Sgonzo	pcell_t pins[BCM_GPIO_PINS];
520243465Sgonzo	phandle_t gpio;
521243465Sgonzo
522243465Sgonzo	/* Find the gpio node to start. */
523243465Sgonzo	gpio = ofw_bus_get_node(sc->sc_dev);
524243465Sgonzo
525243465Sgonzo	len = OF_getproplen(gpio, "broadcom,read-only");
526243465Sgonzo	if (len < 0 || len > sizeof(pins))
527243465Sgonzo		return (-1);
528243465Sgonzo
529243465Sgonzo	if (OF_getprop(gpio, "broadcom,read-only", &pins, len) < 0)
530243465Sgonzo		return (-1);
531243465Sgonzo
532243465Sgonzo	sc->sc_ro_npins = len / sizeof(pcell_t);
533243465Sgonzo
534243465Sgonzo	device_printf(sc->sc_dev, "read-only pins: ");
535243465Sgonzo	for (i = 0; i < sc->sc_ro_npins; i++) {
536243465Sgonzo		sc->sc_ro_pins[i] = fdt32_to_cpu(pins[i]);
537243465Sgonzo		if (i > 0)
538243465Sgonzo			printf(",");
539243465Sgonzo		printf("%d", sc->sc_ro_pins[i]);
540243465Sgonzo	}
541244412Sgonzo	if (i > 0)
542243465Sgonzo		printf(".");
543243465Sgonzo	printf("\n");
544243465Sgonzo
545243465Sgonzo	return (0);
546243465Sgonzo}
547243465Sgonzo
548243465Sgonzostatic int
549244412Sgonzobcm_gpio_func_proc(SYSCTL_HANDLER_ARGS)
550244412Sgonzo{
551244412Sgonzo	char buf[16];
552244412Sgonzo	struct bcm_gpio_softc *sc;
553244412Sgonzo	struct bcm_gpio_sysctl *sc_sysctl;
554244412Sgonzo	uint32_t nfunc;
555255370Sloos	int error;
556244412Sgonzo
557244412Sgonzo	sc_sysctl = arg1;
558244412Sgonzo	sc = sc_sysctl->sc;
559244412Sgonzo
560244412Sgonzo	/* Get the current pin function. */
561244412Sgonzo	nfunc = bcm_gpio_get_function(sc, sc_sysctl->pin);
562244412Sgonzo	bcm_gpio_func_str(nfunc, buf, sizeof(buf));
563244412Sgonzo
564244412Sgonzo	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
565244412Sgonzo	if (error != 0 || req->newptr == NULL)
566244412Sgonzo		return (error);
567244412Sgonzo
568244412Sgonzo	/* Parse the user supplied string and check for a valid pin function. */
569244412Sgonzo	if (bcm_gpio_str_func(buf, &nfunc) != 0)
570244412Sgonzo		return (EINVAL);
571244412Sgonzo
572255370Sloos	/* Update the pin alternate function. */
573255370Sloos	bcm_gpio_set_alternate(sc->sc_dev, sc_sysctl->pin, nfunc);
574244412Sgonzo
575244412Sgonzo	return (0);
576244412Sgonzo}
577244412Sgonzo
578244412Sgonzostatic void
579244412Sgonzobcm_gpio_sysctl_init(struct bcm_gpio_softc *sc)
580244412Sgonzo{
581244412Sgonzo	char pinbuf[3];
582244412Sgonzo	struct bcm_gpio_sysctl *sc_sysctl;
583244412Sgonzo	struct sysctl_ctx_list *ctx;
584244412Sgonzo	struct sysctl_oid *tree_node, *pin_node, *pinN_node;
585244412Sgonzo	struct sysctl_oid_list *tree, *pin_tree, *pinN_tree;
586244412Sgonzo	int i;
587244412Sgonzo
588244412Sgonzo	/*
589244412Sgonzo	 * Add per-pin sysctl tree/handlers.
590244412Sgonzo	 */
591244412Sgonzo	ctx = device_get_sysctl_ctx(sc->sc_dev);
592244412Sgonzo 	tree_node = device_get_sysctl_tree(sc->sc_dev);
593244412Sgonzo 	tree = SYSCTL_CHILDREN(tree_node);
594244412Sgonzo	pin_node = SYSCTL_ADD_NODE(ctx, tree, OID_AUTO, "pin",
595244412Sgonzo	    CTLFLAG_RW, NULL, "GPIO Pins");
596244412Sgonzo	pin_tree = SYSCTL_CHILDREN(pin_node);
597244412Sgonzo
598244412Sgonzo	for (i = 0; i < sc->sc_gpio_npins; i++) {
599244412Sgonzo
600244412Sgonzo		snprintf(pinbuf, sizeof(pinbuf), "%d", i);
601244412Sgonzo		pinN_node = SYSCTL_ADD_NODE(ctx, pin_tree, OID_AUTO, pinbuf,
602244412Sgonzo		    CTLFLAG_RD, NULL, "GPIO Pin");
603244412Sgonzo		pinN_tree = SYSCTL_CHILDREN(pinN_node);
604244412Sgonzo
605244412Sgonzo		sc->sc_sysctl[i].sc = sc;
606244412Sgonzo		sc_sysctl = &sc->sc_sysctl[i];
607244412Sgonzo		sc_sysctl->sc = sc;
608244412Sgonzo		sc_sysctl->pin = sc->sc_gpio_pins[i].gp_pin;
609244412Sgonzo		SYSCTL_ADD_PROC(ctx, pinN_tree, OID_AUTO, "function",
610244412Sgonzo		    CTLFLAG_RW | CTLTYPE_STRING, sc_sysctl,
611244412Sgonzo		    sizeof(struct bcm_gpio_sysctl), bcm_gpio_func_proc,
612244412Sgonzo		    "A", "Pin Function");
613244412Sgonzo	}
614244412Sgonzo}
615244412Sgonzo
616244412Sgonzostatic int
617243465Sgonzobcm_gpio_get_reserved_pins(struct bcm_gpio_softc *sc)
618243465Sgonzo{
619243465Sgonzo	int i, j, len, npins;
620243465Sgonzo	pcell_t pins[BCM_GPIO_PINS];
621243465Sgonzo	phandle_t gpio, node, reserved;
622243465Sgonzo	char name[32];
623243465Sgonzo
624243465Sgonzo	/* Get read-only pins. */
625243465Sgonzo	if (bcm_gpio_get_ro_pins(sc) != 0)
626243465Sgonzo		return (-1);
627243465Sgonzo
628243465Sgonzo	/* Find the gpio/reserved pins node to start. */
629243465Sgonzo	gpio = ofw_bus_get_node(sc->sc_dev);
630243465Sgonzo	node = OF_child(gpio);
631243465Sgonzo
632243465Sgonzo	/*
633243465Sgonzo	 * Find reserved node
634243465Sgonzo	 */
635243465Sgonzo	reserved = 0;
636243465Sgonzo	while ((node != 0) && (reserved == 0)) {
637244412Sgonzo		len = OF_getprop(node, "name", name,
638243465Sgonzo		    sizeof(name) - 1);
639243465Sgonzo		name[len] = 0;
640243465Sgonzo		if (strcmp(name, "reserved") == 0)
641243465Sgonzo			reserved = node;
642243465Sgonzo		node = OF_peer(node);
643243465Sgonzo	}
644243465Sgonzo
645243465Sgonzo	if (reserved == 0)
646243465Sgonzo		return (-1);
647243465Sgonzo
648243465Sgonzo	/* Get the reserved pins. */
649243465Sgonzo	len = OF_getproplen(reserved, "broadcom,pins");
650243465Sgonzo	if (len < 0 || len > sizeof(pins))
651243465Sgonzo		return (-1);
652243465Sgonzo
653243465Sgonzo	if (OF_getprop(reserved, "broadcom,pins", &pins, len) < 0)
654243465Sgonzo		return (-1);
655243465Sgonzo
656243465Sgonzo	npins = len / sizeof(pcell_t);
657243465Sgonzo
658243465Sgonzo	j = 0;
659243465Sgonzo	device_printf(sc->sc_dev, "reserved pins: ");
660243465Sgonzo	for (i = 0; i < npins; i++) {
661243465Sgonzo		if (i > 0)
662243465Sgonzo			printf(",");
663243465Sgonzo		printf("%d", fdt32_to_cpu(pins[i]));
664243465Sgonzo		/* Some pins maybe already on the list of read-only pins. */
665243465Sgonzo		if (bcm_gpio_pin_is_ro(sc, fdt32_to_cpu(pins[i])))
666243465Sgonzo			continue;
667243465Sgonzo		sc->sc_ro_pins[j++ + sc->sc_ro_npins] = fdt32_to_cpu(pins[i]);
668243465Sgonzo	}
669243465Sgonzo	sc->sc_ro_npins += j;
670244412Sgonzo	if (i > 0)
671243465Sgonzo		printf(".");
672243465Sgonzo	printf("\n");
673243465Sgonzo
674243465Sgonzo	return (0);
675243465Sgonzo}
676243465Sgonzo
677243465Sgonzostatic int
678243465Sgonzobcm_gpio_probe(device_t dev)
679243465Sgonzo{
680243465Sgonzo	if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-gpio"))
681243465Sgonzo		return (ENXIO);
682243465Sgonzo
683243465Sgonzo	device_set_desc(dev, "BCM2708/2835 GPIO controller");
684243465Sgonzo	return (BUS_PROBE_DEFAULT);
685243465Sgonzo}
686243465Sgonzo
687243465Sgonzostatic int
688243465Sgonzobcm_gpio_attach(device_t dev)
689243465Sgonzo{
690243465Sgonzo	struct bcm_gpio_softc *sc = device_get_softc(dev);
691244412Sgonzo	uint32_t func;
692243465Sgonzo	int i, j, rid;
693243465Sgonzo	phandle_t gpio;
694243465Sgonzo
695243465Sgonzo	sc->sc_dev = dev;
696243465Sgonzo
697243465Sgonzo	mtx_init(&sc->sc_mtx, "bcm gpio", "gpio", MTX_DEF);
698243465Sgonzo
699243465Sgonzo	rid = 0;
700243465Sgonzo	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
701243465Sgonzo	    RF_ACTIVE);
702243465Sgonzo	if (!sc->sc_mem_res) {
703243465Sgonzo		device_printf(dev, "cannot allocate memory window\n");
704243465Sgonzo		return (ENXIO);
705243465Sgonzo	}
706243465Sgonzo
707243465Sgonzo	sc->sc_bst = rman_get_bustag(sc->sc_mem_res);
708243465Sgonzo	sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res);
709243465Sgonzo
710243465Sgonzo	rid = 0;
711243465Sgonzo	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
712243465Sgonzo	    RF_ACTIVE);
713243465Sgonzo	if (!sc->sc_irq_res) {
714243465Sgonzo		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
715243465Sgonzo		device_printf(dev, "cannot allocate interrupt\n");
716243465Sgonzo		return (ENXIO);
717243465Sgonzo	}
718243465Sgonzo
719243465Sgonzo	/* Find our node. */
720243465Sgonzo	gpio = ofw_bus_get_node(sc->sc_dev);
721243465Sgonzo
722243465Sgonzo	if (!OF_hasprop(gpio, "gpio-controller"))
723243465Sgonzo		/* Node is not a GPIO controller. */
724243465Sgonzo		goto fail;
725243465Sgonzo
726243465Sgonzo	/*
727243465Sgonzo	 * Find the read-only pins.  These are pins we never touch or bad
728243465Sgonzo	 * things could happen.
729243465Sgonzo	 */
730243465Sgonzo	if (bcm_gpio_get_reserved_pins(sc) == -1)
731243465Sgonzo		goto fail;
732243465Sgonzo
733243465Sgonzo	/* Initialize the software controlled pins. */
734255334Sloos	for (i = 0, j = 0; j < BCM_GPIO_PINS; j++) {
735243465Sgonzo		if (bcm_gpio_pin_is_ro(sc, j))
736243465Sgonzo			continue;
737243465Sgonzo		snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
738243465Sgonzo		    "pin %d", j);
739244412Sgonzo		func = bcm_gpio_get_function(sc, j);
740243465Sgonzo		sc->sc_gpio_pins[i].gp_pin = j;
741243465Sgonzo		sc->sc_gpio_pins[i].gp_caps = BCM_GPIO_DEFAULT_CAPS;
742244412Sgonzo		sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(func);
743243465Sgonzo		i++;
744243465Sgonzo	}
745243465Sgonzo	sc->sc_gpio_npins = i;
746243465Sgonzo
747244412Sgonzo	bcm_gpio_sysctl_init(sc);
748244412Sgonzo
749244412Sgonzo	device_add_child(dev, "gpioc", device_get_unit(dev));
750244412Sgonzo	device_add_child(dev, "gpiobus", device_get_unit(dev));
751243465Sgonzo	return (bus_generic_attach(dev));
752243465Sgonzo
753243465Sgonzofail:
754243465Sgonzo	if (sc->sc_irq_res)
755243465Sgonzo		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
756243465Sgonzo	if (sc->sc_mem_res)
757243465Sgonzo		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
758243465Sgonzo	return (ENXIO);
759243465Sgonzo}
760243465Sgonzo
761243465Sgonzostatic int
762243465Sgonzobcm_gpio_detach(device_t dev)
763243465Sgonzo{
764243465Sgonzo
765243465Sgonzo	return (EBUSY);
766243465Sgonzo}
767243465Sgonzo
768243465Sgonzostatic device_method_t bcm_gpio_methods[] = {
769243465Sgonzo	/* Device interface */
770243465Sgonzo	DEVMETHOD(device_probe,		bcm_gpio_probe),
771243465Sgonzo	DEVMETHOD(device_attach,	bcm_gpio_attach),
772243465Sgonzo	DEVMETHOD(device_detach,	bcm_gpio_detach),
773243465Sgonzo
774243465Sgonzo	/* GPIO protocol */
775243465Sgonzo	DEVMETHOD(gpio_pin_max,		bcm_gpio_pin_max),
776243465Sgonzo	DEVMETHOD(gpio_pin_getname,	bcm_gpio_pin_getname),
777243465Sgonzo	DEVMETHOD(gpio_pin_getflags,	bcm_gpio_pin_getflags),
778243465Sgonzo	DEVMETHOD(gpio_pin_getcaps,	bcm_gpio_pin_getcaps),
779243465Sgonzo	DEVMETHOD(gpio_pin_setflags,	bcm_gpio_pin_setflags),
780243465Sgonzo	DEVMETHOD(gpio_pin_get,		bcm_gpio_pin_get),
781243465Sgonzo	DEVMETHOD(gpio_pin_set,		bcm_gpio_pin_set),
782243465Sgonzo	DEVMETHOD(gpio_pin_toggle,	bcm_gpio_pin_toggle),
783243465Sgonzo
784243465Sgonzo	DEVMETHOD_END
785243465Sgonzo};
786243465Sgonzo
787243465Sgonzostatic devclass_t bcm_gpio_devclass;
788243465Sgonzo
789243465Sgonzostatic driver_t bcm_gpio_driver = {
790243465Sgonzo	"gpio",
791243465Sgonzo	bcm_gpio_methods,
792243465Sgonzo	sizeof(struct bcm_gpio_softc),
793243465Sgonzo};
794243465Sgonzo
795243465SgonzoDRIVER_MODULE(bcm_gpio, simplebus, bcm_gpio_driver, bcm_gpio_devclass, 0, 0);
796