1296936Smmel/*-
2296936Smmel * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
3296936Smmel * All rights reserved.
4296936Smmel *
5296936Smmel * Redistribution and use in source and binary forms, with or without
6296936Smmel * modification, are permitted provided that the following conditions
7296936Smmel * are met:
8296936Smmel * 1. Redistributions of source code must retain the above copyright
9296936Smmel *    notice, this list of conditions and the following disclaimer.
10296936Smmel * 2. Redistributions in binary form must reproduce the above copyright
11296936Smmel *    notice, this list of conditions and the following disclaimer in the
12296936Smmel *    documentation and/or other materials provided with the distribution.
13296936Smmel *
14296936Smmel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15296936Smmel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16296936Smmel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17296936Smmel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18296936Smmel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19296936Smmel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20296936Smmel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21296936Smmel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22296936Smmel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23296936Smmel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24296936Smmel * SUCH DAMAGE.
25296936Smmel */
26296936Smmel
27296936Smmel#include <sys/cdefs.h>
28296936Smmel__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_gpio.c 308335 2016-11-05 10:56:32Z mmel $");
29296936Smmel
30296936Smmel/*
31296936Smmel * Tegra GPIO driver.
32296936Smmel */
33298742Smmel#include "opt_platform.h"
34296936Smmel#include <sys/param.h>
35296936Smmel#include <sys/systm.h>
36296936Smmel#include <sys/bus.h>
37298742Smmel#include <sys/gpio.h>
38296936Smmel#include <sys/kernel.h>
39298742Smmel#include <sys/proc.h>
40296936Smmel#include <sys/rman.h>
41296936Smmel#include <sys/lock.h>
42298742Smmel#include <sys/module.h>
43296936Smmel#include <sys/mutex.h>
44296936Smmel
45296936Smmel#include <machine/bus.h>
46298742Smmel#include <machine/intr.h>
47296936Smmel#include <machine/resource.h>
48296936Smmel
49296936Smmel#include <dev/fdt/fdt_common.h>
50296936Smmel#include <dev/gpio/gpiobusvar.h>
51296936Smmel#include <dev/ofw/openfirm.h>
52296936Smmel#include <dev/ofw/ofw_bus.h>
53296936Smmel#include <dev/ofw/ofw_bus_subr.h>
54296936Smmel
55298742Smmel#include "pic_if.h"
56296936Smmel
57298742Smmel#define	GPIO_LOCK(_sc)		mtx_lock(&(_sc)->mtx)
58298742Smmel#define	GPIO_UNLOCK(_sc)	mtx_unlock(&(_sc)->mtx)
59298742Smmel#define	GPIO_LOCK_INIT(_sc)	mtx_init(&_sc->mtx, 			\
60298742Smmel	    device_get_nameunit(_sc->dev), "tegra_gpio", MTX_DEF)
61298742Smmel#define	GPIO_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->mtx);
62298742Smmel#define	GPIO_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->mtx, MA_OWNED);
63298742Smmel#define	GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED);
64296936Smmel
65296936Smmel#define	WR4(_sc, _r, _v)	bus_write_4((_sc)->mem_res, (_r), (_v))
66296936Smmel#define	RD4(_sc, _r)		bus_read_4((_sc)->mem_res, (_r))
67296936Smmel
68296936Smmel#define	GPIO_BANK_OFFS		0x100	/* Bank offset */
69296936Smmel#define	GPIO_NUM_BANKS		8	/* Total number per bank */
70296936Smmel#define	GPIO_REGS_IN_BANK	4	/* Total registers in bank */
71296936Smmel#define	GPIO_PINS_IN_REG	8	/* Total pin in register */
72296936Smmel
73296936Smmel#define	GPIO_BANKNUM(n)		((n) / (GPIO_REGS_IN_BANK * GPIO_PINS_IN_REG))
74296936Smmel#define	GPIO_PORTNUM(n)		(((n) / GPIO_PINS_IN_REG) % GPIO_REGS_IN_BANK)
75296936Smmel#define	GPIO_BIT(n)		((n) % GPIO_PINS_IN_REG)
76296936Smmel
77296936Smmel#define	GPIO_REGNUM(n)		(GPIO_BANKNUM(n) * GPIO_BANK_OFFS + \
78296936Smmel				    GPIO_PORTNUM(n) * 4)
79296936Smmel
80296936Smmel#define	NGPIO	((GPIO_NUM_BANKS * GPIO_REGS_IN_BANK * GPIO_PINS_IN_REG) - 8)
81296936Smmel
82296936Smmel/* Register offsets */
83296936Smmel#define	GPIO_CNF		0x00
84296936Smmel#define	GPIO_OE			0x10
85296936Smmel#define	GPIO_OUT		0x20
86296936Smmel#define	GPIO_IN			0x30
87296936Smmel#define	GPIO_INT_STA		0x40
88296936Smmel#define	GPIO_INT_ENB		0x50
89296936Smmel#define	GPIO_INT_LVL		0x60
90298742Smmel#define  GPIO_INT_LVL_DELTA		(1 << 16)
91298742Smmel#define  GPIO_INT_LVL_EDGE		(1 << 8)
92298742Smmel#define  GPIO_INT_LVL_HIGH		(1 << 0)
93298742Smmel#define  GPIO_INT_LVL_MASK		(GPIO_INT_LVL_DELTA |		\
94298742Smmel					 GPIO_INT_LVL_EDGE | GPIO_INT_LVL_HIGH)
95296936Smmel#define	GPIO_INT_CLR		0x70
96296936Smmel#define	GPIO_MSK_CNF		0x80
97296936Smmel#define	GPIO_MSK_OE		0x90
98296936Smmel#define	GPIO_MSK_OUT		0xA0
99296936Smmel#define	GPIO_MSK_INT_STA	0xC0
100296936Smmel#define	GPIO_MSK_INT_ENB	0xD0
101296936Smmel#define	GPIO_MSK_INT_LVL	0xE0
102296936Smmel
103296936Smmelchar *tegra_gpio_port_names[] = {
104296936Smmel	 "A",  "B",  "C",  "D", /* Bank 0 */
105296936Smmel	 "E",  "F",  "G",  "H", /* Bank 1 */
106296936Smmel	 "I",  "J",  "K",  "L", /* Bank 2 */
107296936Smmel	 "M",  "N",  "O",  "P", /* Bank 3 */
108296936Smmel	 "Q",  "R",  "S",  "T", /* Bank 4 */
109296936Smmel	 "U",  "V",  "W",  "X", /* Bank 5 */
110298742Smmel	 "Y",  "Z", "AA", "BB", /* Bank 6 */
111298742Smmel	"CC", "DD", "EE"	/* Bank 7 */
112296936Smmel};
113296936Smmel
114298742Smmelstruct tegra_gpio_irqsrc {
115298742Smmel	struct intr_irqsrc	isrc;
116298742Smmel	u_int			irq;
117298742Smmel	uint32_t		cfgreg;
118298742Smmel};
119298742Smmel
120298742Smmelstruct tegra_gpio_softc;
121298742Smmelstruct tegra_gpio_irq_cookie {
122298742Smmel	struct tegra_gpio_softc	*sc;
123298742Smmel	int			bank_num;
124298742Smmel};
125298742Smmel
126296936Smmelstruct tegra_gpio_softc {
127296936Smmel	device_t		dev;
128298742Smmel	device_t		busdev;
129298742Smmel	struct mtx		mtx;
130296936Smmel	struct resource		*mem_res;
131298742Smmel	struct resource		*irq_res[GPIO_NUM_BANKS];
132298742Smmel	void			*irq_ih[GPIO_NUM_BANKS];
133298742Smmel	struct tegra_gpio_irq_cookie irq_cookies[GPIO_NUM_BANKS];
134296936Smmel	int			gpio_npins;
135296936Smmel	struct gpio_pin		gpio_pins[NGPIO];
136298742Smmel	struct tegra_gpio_irqsrc *isrcs;
137296936Smmel};
138296936Smmel
139296936Smmelstatic struct ofw_compat_data compat_data[] = {
140296936Smmel	{"nvidia,tegra124-gpio", 1},
141296936Smmel	{NULL,			0}
142296936Smmel};
143296936Smmel
144298742Smmel/* --------------------------------------------------------------------------
145298742Smmel *
146298742Smmel * GPIO
147298742Smmel *
148298742Smmel */
149296936Smmelstatic inline void
150296936Smmelgpio_write_masked(struct tegra_gpio_softc *sc, bus_size_t reg,
151296936Smmel    struct gpio_pin *pin, uint32_t val)
152296936Smmel{
153296936Smmel	uint32_t tmp;
154296936Smmel	int bit;
155296936Smmel
156296936Smmel	bit = GPIO_BIT(pin->gp_pin);
157296936Smmel	tmp = 0x100 << bit;		/* mask */
158296936Smmel	tmp |= (val & 1) << bit;	/* value */
159296936Smmel	bus_write_4(sc->mem_res, reg + GPIO_REGNUM(pin->gp_pin), tmp);
160296936Smmel}
161298742Smmel
162296936Smmelstatic inline uint32_t
163296936Smmelgpio_read(struct tegra_gpio_softc *sc, bus_size_t reg, struct gpio_pin *pin)
164296936Smmel{
165296936Smmel	int bit;
166296936Smmel	uint32_t val;
167296936Smmel
168296936Smmel	bit = GPIO_BIT(pin->gp_pin);
169296936Smmel	val = bus_read_4(sc->mem_res, reg + GPIO_REGNUM(pin->gp_pin));
170296936Smmel	return (val >> bit) & 1;
171296936Smmel}
172296936Smmel
173296936Smmelstatic void
174296936Smmeltegra_gpio_pin_configure(struct tegra_gpio_softc *sc, struct gpio_pin *pin,
175296936Smmel    unsigned int flags)
176296936Smmel{
177296936Smmel
178296936Smmel	if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) == 0)
179296936Smmel		return;
180296936Smmel
181296936Smmel	/* Manage input/output */
182296936Smmel	pin->gp_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
183296936Smmel	if (flags & GPIO_PIN_OUTPUT) {
184296936Smmel		pin->gp_flags |= GPIO_PIN_OUTPUT;
185296936Smmel		gpio_write_masked(sc, GPIO_MSK_OE, pin, 1);
186296936Smmel	} else {
187296936Smmel		pin->gp_flags |= GPIO_PIN_INPUT;
188296936Smmel		gpio_write_masked(sc, GPIO_MSK_OE, pin, 0);
189296936Smmel	}
190296936Smmel}
191296936Smmel
192296936Smmelstatic device_t
193296936Smmeltegra_gpio_get_bus(device_t dev)
194296936Smmel{
195296936Smmel	struct tegra_gpio_softc *sc;
196296936Smmel
197296936Smmel	sc = device_get_softc(dev);
198298742Smmel	return (sc->busdev);
199296936Smmel}
200296936Smmel
201296936Smmelstatic int
202296936Smmeltegra_gpio_pin_max(device_t dev, int *maxpin)
203296936Smmel{
204296936Smmel
205296936Smmel	*maxpin = NGPIO - 1;
206296936Smmel	return (0);
207296936Smmel}
208296936Smmel
209296936Smmelstatic int
210296936Smmeltegra_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
211296936Smmel{
212296936Smmel	struct tegra_gpio_softc *sc;
213296936Smmel
214296936Smmel	sc = device_get_softc(dev);
215296936Smmel	if (pin >= sc->gpio_npins)
216296936Smmel		return (EINVAL);
217296936Smmel
218296936Smmel	GPIO_LOCK(sc);
219296936Smmel	*caps = sc->gpio_pins[pin].gp_caps;
220296936Smmel	GPIO_UNLOCK(sc);
221296936Smmel
222296936Smmel	return (0);
223296936Smmel}
224296936Smmel
225296936Smmelstatic int
226296936Smmeltegra_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
227296936Smmel{
228296936Smmel	struct tegra_gpio_softc *sc;
229296936Smmel	int cnf;
230296936Smmel
231296936Smmel	sc = device_get_softc(dev);
232296936Smmel	if (pin >= sc->gpio_npins)
233296936Smmel		return (EINVAL);
234296936Smmel
235296936Smmel	GPIO_LOCK(sc);
236296936Smmel	cnf = gpio_read(sc, GPIO_CNF, &sc->gpio_pins[pin]);
237296936Smmel	if (cnf == 0) {
238296936Smmel		GPIO_UNLOCK(sc);
239296936Smmel		return (ENXIO);
240296936Smmel	}
241296936Smmel	*flags = sc->gpio_pins[pin].gp_flags;
242296936Smmel	GPIO_UNLOCK(sc);
243296936Smmel
244296936Smmel	return (0);
245296936Smmel}
246296936Smmel
247296936Smmelstatic int
248296936Smmeltegra_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
249296936Smmel{
250296936Smmel	struct tegra_gpio_softc *sc;
251296936Smmel
252296936Smmel	sc = device_get_softc(dev);
253296936Smmel	if (pin >= sc->gpio_npins)
254296936Smmel		return (EINVAL);
255296936Smmel
256296936Smmel	GPIO_LOCK(sc);
257296936Smmel	memcpy(name, sc->gpio_pins[pin].gp_name, GPIOMAXNAME);
258296936Smmel	GPIO_UNLOCK(sc);
259296936Smmel
260296936Smmel	return (0);
261296936Smmel}
262296936Smmel
263296936Smmelstatic int
264296936Smmeltegra_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
265296936Smmel{
266296936Smmel	struct tegra_gpio_softc *sc;
267296936Smmel	int cnf;
268296936Smmel
269296936Smmel	sc = device_get_softc(dev);
270296936Smmel	if (pin >= sc->gpio_npins)
271296936Smmel		return (EINVAL);
272296936Smmel
273296936Smmel	GPIO_LOCK(sc);
274296936Smmel	cnf = gpio_read(sc, GPIO_CNF,  &sc->gpio_pins[pin]);
275296936Smmel	if (cnf == 0) {
276296936Smmel		/* XXX - allow this for while ....
277296936Smmel		GPIO_UNLOCK(sc);
278296936Smmel		return (ENXIO);
279296936Smmel		*/
280296936Smmel		gpio_write_masked(sc, GPIO_MSK_CNF,  &sc->gpio_pins[pin], 1);
281296936Smmel	}
282296936Smmel	tegra_gpio_pin_configure(sc, &sc->gpio_pins[pin], flags);
283296936Smmel	GPIO_UNLOCK(sc);
284296936Smmel
285296936Smmel	return (0);
286296936Smmel}
287296936Smmel
288296936Smmelstatic int
289296936Smmeltegra_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
290296936Smmel{
291296936Smmel	struct tegra_gpio_softc *sc;
292296936Smmel
293296936Smmel	sc = device_get_softc(dev);
294296936Smmel	if (pin >= sc->gpio_npins)
295296936Smmel		return (EINVAL);
296296936Smmel	GPIO_LOCK(sc);
297296936Smmel	gpio_write_masked(sc, GPIO_MSK_OUT, &sc->gpio_pins[pin], value);
298296936Smmel	GPIO_UNLOCK(sc);
299296936Smmel
300296936Smmel	return (0);
301296936Smmel}
302296936Smmel
303296936Smmelstatic int
304296936Smmeltegra_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
305296936Smmel{
306296936Smmel	struct tegra_gpio_softc *sc;
307296936Smmel
308296936Smmel	sc = device_get_softc(dev);
309296936Smmel	if (pin >= sc->gpio_npins)
310296936Smmel		return (EINVAL);
311296936Smmel
312296936Smmel	GPIO_LOCK(sc);
313296936Smmel	*val = gpio_read(sc, GPIO_IN, &sc->gpio_pins[pin]);
314296936Smmel	GPIO_UNLOCK(sc);
315296936Smmel
316296936Smmel	return (0);
317296936Smmel}
318296936Smmel
319296936Smmelstatic int
320296936Smmeltegra_gpio_pin_toggle(device_t dev, uint32_t pin)
321296936Smmel{
322296936Smmel	struct tegra_gpio_softc *sc;
323296936Smmel
324296936Smmel	sc = device_get_softc(dev);
325296936Smmel	if (pin >= sc->gpio_npins)
326296936Smmel		return (EINVAL);
327296936Smmel
328296936Smmel	GPIO_LOCK(sc);
329296936Smmel	gpio_write_masked(sc, GPIO_MSK_OE, &sc->gpio_pins[pin],
330296936Smmel	     gpio_read(sc, GPIO_IN, &sc->gpio_pins[pin]) ^ 1);
331296936Smmel	GPIO_UNLOCK(sc);
332296936Smmel
333296936Smmel	return (0);
334296936Smmel}
335296936Smmel
336298742Smmel/* --------------------------------------------------------------------------
337298742Smmel *
338298742Smmel * Interrupts
339298742Smmel *
340298742Smmel */
341298742Smmelstatic inline void
342298742Smmelintr_write_masked(struct tegra_gpio_softc *sc, bus_addr_t reg,
343298742Smmel    struct tegra_gpio_irqsrc *tgi, uint32_t val)
344298742Smmel{
345298742Smmel	uint32_t tmp;
346298742Smmel	int bit;
347298742Smmel
348298742Smmel	bit = GPIO_BIT(tgi->irq);
349298742Smmel	tmp = 0x100 << bit;		/* mask */
350298742Smmel	tmp |= (val & 1) << bit;	/* value */
351298742Smmel	bus_write_4(sc->mem_res, reg + GPIO_REGNUM(tgi->irq), tmp);
352298742Smmel}
353298742Smmel
354298742Smmelstatic inline void
355298742Smmelintr_write_modify(struct tegra_gpio_softc *sc, bus_addr_t reg,
356298742Smmel    struct tegra_gpio_irqsrc *tgi, uint32_t val, uint32_t mask)
357298742Smmel{
358298742Smmel	uint32_t tmp;
359298742Smmel	int bit;
360298742Smmel
361298742Smmel	bit = GPIO_BIT(tgi->irq);
362298742Smmel	GPIO_LOCK(sc);
363298742Smmel	tmp = bus_read_4(sc->mem_res, reg + GPIO_REGNUM(tgi->irq));
364298742Smmel	tmp &= ~(mask << bit);
365298742Smmel	tmp |= val << bit;
366298742Smmel	bus_write_4(sc->mem_res, reg + GPIO_REGNUM(tgi->irq), tmp);
367298742Smmel	GPIO_UNLOCK(sc);
368298742Smmel}
369298742Smmel
370298742Smmelstatic inline void
371298742Smmeltegra_gpio_isrc_mask(struct tegra_gpio_softc *sc,
372298742Smmel     struct tegra_gpio_irqsrc *tgi, uint32_t val)
373298742Smmel{
374298742Smmel
375298742Smmel	intr_write_masked(sc, GPIO_MSK_INT_ENB, tgi, val);
376298742Smmel}
377298742Smmel
378298742Smmelstatic inline void
379298742Smmeltegra_gpio_isrc_eoi(struct tegra_gpio_softc *sc,
380298742Smmel     struct tegra_gpio_irqsrc *tgi)
381298742Smmel{
382298742Smmel
383298742Smmel	intr_write_masked(sc, GPIO_INT_CLR, tgi, 1);
384298742Smmel}
385298742Smmel
386298742Smmelstatic inline bool
387298742Smmeltegra_gpio_isrc_is_level(struct tegra_gpio_irqsrc *tgi)
388298742Smmel{
389298742Smmel
390298742Smmel	return (tgi->cfgreg & GPIO_INT_LVL_EDGE);
391298742Smmel}
392298742Smmel
393296936Smmelstatic int
394296936Smmeltegra_gpio_intr(void *arg)
395296936Smmel{
396298742Smmel	u_int irq, i, j, val, basepin;
397296936Smmel	struct tegra_gpio_softc *sc;
398298742Smmel	struct trapframe *tf;
399298742Smmel	struct tegra_gpio_irqsrc *tgi;
400298742Smmel	struct tegra_gpio_irq_cookie *cookie;
401296936Smmel
402298742Smmel	cookie = (struct tegra_gpio_irq_cookie *)arg;
403298742Smmel	sc = cookie->sc;
404298742Smmel	tf = curthread->td_intr_frame;
405298742Smmel
406298742Smmel	for (i = 0; i < GPIO_REGS_IN_BANK; i++) {
407298742Smmel		basepin  = cookie->bank_num * GPIO_REGS_IN_BANK *
408298742Smmel		    GPIO_PINS_IN_REG + i * GPIO_PINS_IN_REG;
409298742Smmel
410298742Smmel		val = bus_read_4(sc->mem_res, GPIO_INT_STA +
411298742Smmel		    GPIO_REGNUM(basepin));
412298742Smmel		val &= bus_read_4(sc->mem_res, GPIO_INT_ENB +
413298742Smmel		    GPIO_REGNUM(basepin));
414296936Smmel		/* Interrupt handling */
415296936Smmel		for (j = 0; j < GPIO_PINS_IN_REG; j++) {
416298742Smmel			if ((val & (1 << j)) == 0)
417298742Smmel				continue;
418298742Smmel			irq = basepin + j;
419298742Smmel			tgi = &sc->isrcs[irq];
420298742Smmel			if (!tegra_gpio_isrc_is_level(tgi))
421298742Smmel				tegra_gpio_isrc_eoi(sc, tgi);
422298742Smmel			if (intr_isrc_dispatch(&tgi->isrc, tf) != 0) {
423298742Smmel				tegra_gpio_isrc_mask(sc, tgi, 0);
424298742Smmel				if (tegra_gpio_isrc_is_level(tgi))
425298742Smmel					tegra_gpio_isrc_eoi(sc, tgi);
426298742Smmel				device_printf(sc->dev,
427298742Smmel				    "Stray irq %u disabled\n", irq);
428298742Smmel			}
429298742Smmel
430296936Smmel		}
431296936Smmel	}
432298742Smmel
433296936Smmel	return (FILTER_HANDLED);
434296936Smmel}
435296936Smmel
436296936Smmelstatic int
437298742Smmeltegra_gpio_pic_attach(struct tegra_gpio_softc *sc)
438298742Smmel{
439298742Smmel	int error;
440298742Smmel	uint32_t irq;
441298742Smmel	const char *name;
442298742Smmel
443298742Smmel	sc->isrcs = malloc(sizeof(*sc->isrcs) * sc->gpio_npins, M_DEVBUF,
444298742Smmel	    M_WAITOK | M_ZERO);
445298742Smmel
446298742Smmel	name = device_get_nameunit(sc->dev);
447298742Smmel	for (irq = 0; irq < sc->gpio_npins; irq++) {
448298742Smmel		sc->isrcs[irq].irq = irq;
449298742Smmel		sc->isrcs[irq].cfgreg = 0;
450298742Smmel		error = intr_isrc_register(&sc->isrcs[irq].isrc,
451298742Smmel		    sc->dev, 0, "%s,%u", name, irq);
452298742Smmel		if (error != 0)
453298742Smmel			return (error); /* XXX deregister ISRCs */
454298742Smmel	}
455300149Sandrew	if (intr_pic_register(sc->dev,
456300149Sandrew	    OF_xref_from_node(ofw_bus_get_node(sc->dev))) == NULL)
457300149Sandrew		return (ENXIO);
458300149Sandrew
459300149Sandrew	return (0);
460298742Smmel}
461298742Smmel
462298742Smmelstatic int
463298742Smmeltegra_gpio_pic_detach(struct tegra_gpio_softc *sc)
464298742Smmel{
465298742Smmel
466298742Smmel	/*
467298742Smmel	 *  There has not been established any procedure yet
468298742Smmel	 *  how to detach PIC from living system correctly.
469298742Smmel	 */
470298742Smmel	device_printf(sc->dev, "%s: not implemented yet\n", __func__);
471298742Smmel	return (EBUSY);
472298742Smmel}
473298742Smmel
474298742Smmel
475298742Smmelstatic void
476298742Smmeltegra_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
477298742Smmel{
478298742Smmel	struct tegra_gpio_softc *sc;
479298742Smmel	struct tegra_gpio_irqsrc *tgi;
480298742Smmel
481298742Smmel	sc = device_get_softc(dev);
482298742Smmel	tgi = (struct tegra_gpio_irqsrc *)isrc;
483298742Smmel	tegra_gpio_isrc_mask(sc, tgi, 0);
484298742Smmel}
485298742Smmel
486298742Smmelstatic void
487298742Smmeltegra_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
488298742Smmel{
489298742Smmel	struct tegra_gpio_softc *sc;
490298742Smmel	struct tegra_gpio_irqsrc *tgi;
491298742Smmel
492298742Smmel	sc = device_get_softc(dev);
493298742Smmel	tgi = (struct tegra_gpio_irqsrc *)isrc;
494298742Smmel	tegra_gpio_isrc_mask(sc, tgi, 1);
495298742Smmel}
496298742Smmel
497298742Smmelstatic int
498298742Smmeltegra_gpio_pic_map_fdt(struct tegra_gpio_softc *sc, u_int ncells,
499298742Smmel    pcell_t *cells, u_int *irqp, uint32_t *regp)
500298742Smmel{
501298742Smmel	uint32_t reg;
502298742Smmel
503298742Smmel	/*
504298742Smmel	 * The first cell is the interrupt number.
505298742Smmel	 * The second cell is used to specify flags:
506298742Smmel	 *	bits[3:0] trigger type and level flags:
507298742Smmel	 *		1 = low-to-high edge triggered.
508298742Smmel	 *		2 = high-to-low edge triggered.
509298742Smmel	 *		4 = active high level-sensitive.
510298742Smmel	 *		8 = active low level-sensitive.
511298742Smmel	 */
512298742Smmel	if (ncells != 2 || cells[0] >= sc->gpio_npins)
513298742Smmel		return (EINVAL);
514298742Smmel
515298742Smmel	/*
516298742Smmel	 * All interrupt types could be set for an interrupt at one moment.
517298742Smmel	 * At least, the combination of 'low-to-high' and 'high-to-low' edge
518298742Smmel	 * triggered interrupt types can make a sense.
519298742Smmel	 */
520298742Smmel	if (cells[1] == 1)
521298742Smmel		reg = GPIO_INT_LVL_EDGE | GPIO_INT_LVL_HIGH;
522298742Smmel	else if (cells[1] == 2)
523298742Smmel		reg = GPIO_INT_LVL_EDGE;
524298742Smmel	else if (cells[1] == 3)
525298742Smmel		reg = GPIO_INT_LVL_EDGE | GPIO_INT_LVL_DELTA;
526298742Smmel	else if (cells[1] == 4)
527298742Smmel		reg = GPIO_INT_LVL_HIGH;
528298742Smmel	else if (cells[1] == 8)
529298742Smmel		reg = 0;
530298742Smmel	else
531298742Smmel		return (EINVAL);
532298742Smmel
533298742Smmel	*irqp = cells[0];
534298742Smmel	if (regp != NULL)
535298742Smmel		*regp = reg;
536298742Smmel	return (0);
537298742Smmel}
538298742Smmel
539298742Smmel
540298742Smmelstatic int
541298742Smmeltegra_gpio_pic_map_gpio(struct tegra_gpio_softc *sc, u_int gpio_pin_num,
542298742Smmel    u_int gpio_pin_flags, u_int intr_mode, u_int *irqp, uint32_t *regp)
543298742Smmel{
544298742Smmel
545298742Smmel	uint32_t reg;
546298742Smmel
547298742Smmel	if (gpio_pin_num >= sc->gpio_npins)
548298742Smmel		return (EINVAL);
549298742Smmel	switch (intr_mode) {
550298742Smmel	case GPIO_INTR_CONFORM:
551298742Smmel	case GPIO_INTR_LEVEL_LOW:
552298742Smmel		reg = 0;
553298742Smmel		break;
554298742Smmel	case GPIO_INTR_LEVEL_HIGH:
555298742Smmel		reg = GPIO_INT_LVL_HIGH;
556298742Smmel		break;
557298742Smmel	case GPIO_INTR_EDGE_RISING:
558298742Smmel		reg = GPIO_INT_LVL_EDGE | GPIO_INT_LVL_HIGH;
559298742Smmel		break;
560298742Smmel	case GPIO_INTR_EDGE_FALLING:
561298742Smmel		reg = GPIO_INT_LVL_EDGE;
562298742Smmel		break;
563298742Smmel	case GPIO_INTR_EDGE_BOTH:
564298742Smmel		reg = GPIO_INT_LVL_EDGE | GPIO_INT_LVL_DELTA;
565298742Smmel		break;
566298742Smmel	default:
567298742Smmel		return (EINVAL);
568298742Smmel	}
569298742Smmel	*irqp = gpio_pin_num;
570298742Smmel	if (regp != NULL)
571298742Smmel		*regp = reg;
572298742Smmel	return (0);
573298742Smmel}
574298742Smmel
575298742Smmelstatic int
576298742Smmeltegra_gpio_pic_map_intr(device_t dev, struct intr_map_data *data,
577298742Smmel    struct intr_irqsrc **isrcp)
578298742Smmel{
579298742Smmel	int rv;
580298742Smmel	u_int irq;
581298742Smmel	struct tegra_gpio_softc *sc;
582298742Smmel
583298742Smmel	sc = device_get_softc(dev);
584298742Smmel
585299117Sskra	if (data->type == INTR_MAP_DATA_FDT) {
586299117Sskra		struct intr_map_data_fdt *daf;
587299117Sskra
588299117Sskra		daf = (struct intr_map_data_fdt *)data;
589299117Sskra		rv = tegra_gpio_pic_map_fdt(sc, daf->ncells, daf->cells, &irq,
590299117Sskra		    NULL);
591299117Sskra	} else if (data->type == INTR_MAP_DATA_GPIO) {
592299117Sskra		struct intr_map_data_gpio *dag;
593299117Sskra
594299117Sskra		dag = (struct intr_map_data_gpio *)data;
595299117Sskra		rv = tegra_gpio_pic_map_gpio(sc, dag->gpio_pin_num,
596299117Sskra		   dag->gpio_pin_flags, dag->gpio_intr_mode, &irq, NULL);
597299117Sskra	} else
598298742Smmel		return (ENOTSUP);
599298742Smmel
600298742Smmel	if (rv == 0)
601298742Smmel		*isrcp = &sc->isrcs[irq].isrc;
602298742Smmel	return (rv);
603298742Smmel}
604298742Smmel
605298742Smmelstatic void
606298742Smmeltegra_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
607298742Smmel{
608298742Smmel	struct tegra_gpio_softc *sc;
609298742Smmel	struct tegra_gpio_irqsrc *tgi;
610298742Smmel
611298742Smmel	sc = device_get_softc(dev);
612298742Smmel	tgi = (struct tegra_gpio_irqsrc *)isrc;
613298742Smmel	if (tegra_gpio_isrc_is_level(tgi))
614298742Smmel		tegra_gpio_isrc_eoi(sc, tgi);
615298742Smmel}
616298742Smmel
617298742Smmelstatic void
618298742Smmeltegra_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
619298742Smmel{
620298742Smmel	struct tegra_gpio_softc *sc;
621298742Smmel	struct tegra_gpio_irqsrc *tgi;
622298742Smmel
623298742Smmel	sc = device_get_softc(dev);
624298742Smmel	tgi = (struct tegra_gpio_irqsrc *)isrc;
625298742Smmel	tegra_gpio_isrc_mask(sc, tgi, 1);
626298742Smmel}
627298742Smmel
628298742Smmelstatic void
629298742Smmeltegra_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
630298742Smmel{
631298742Smmel	struct tegra_gpio_softc *sc;
632298742Smmel	struct tegra_gpio_irqsrc *tgi;
633298742Smmel
634298742Smmel	sc = device_get_softc(dev);
635298742Smmel	tgi = (struct tegra_gpio_irqsrc *)isrc;
636298742Smmel
637298742Smmel	tegra_gpio_isrc_mask(sc, tgi, 0);
638298742Smmel	if (tegra_gpio_isrc_is_level(tgi))
639298742Smmel		tegra_gpio_isrc_eoi(sc, tgi);
640298742Smmel}
641298742Smmel
642298742Smmelstatic int
643298742Smmeltegra_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
644298742Smmel    struct resource *res, struct intr_map_data *data)
645298742Smmel{
646298742Smmel	u_int irq;
647298742Smmel	uint32_t cfgreg;
648298742Smmel	int rv;
649298742Smmel	struct tegra_gpio_softc *sc;
650298742Smmel	struct tegra_gpio_irqsrc *tgi;
651298742Smmel
652298742Smmel	sc = device_get_softc(dev);
653298742Smmel	tgi = (struct tegra_gpio_irqsrc *)isrc;
654298742Smmel
655298742Smmel	if (data == NULL)
656298742Smmel		return (ENOTSUP);
657298742Smmel
658298742Smmel	/* Get and check config for an interrupt. */
659299117Sskra	if (data->type == INTR_MAP_DATA_FDT) {
660299117Sskra		struct intr_map_data_fdt *daf;
661299117Sskra
662299117Sskra		daf = (struct intr_map_data_fdt *)data;
663299117Sskra		rv = tegra_gpio_pic_map_fdt(sc, daf->ncells, daf->cells, &irq,
664299117Sskra		    &cfgreg);
665299117Sskra	} else if (data->type == INTR_MAP_DATA_GPIO) {
666299117Sskra		struct intr_map_data_gpio *dag;
667299117Sskra
668299117Sskra		dag = (struct intr_map_data_gpio *)data;
669299117Sskra		rv = tegra_gpio_pic_map_gpio(sc, dag->gpio_pin_num,
670299117Sskra		   dag->gpio_pin_flags, dag->gpio_intr_mode, &irq, &cfgreg);
671299117Sskra	} else
672298742Smmel		return (ENOTSUP);
673298742Smmel	if (rv != 0)
674298742Smmel		return (EINVAL);
675298742Smmel
676298742Smmel	/*
677298742Smmel	 * If this is a setup for another handler,
678298742Smmel	 * only check that its configuration match.
679298742Smmel	 */
680298742Smmel	if (isrc->isrc_handlers != 0)
681298742Smmel		return (tgi->cfgreg == cfgreg ? 0 : EINVAL);
682298742Smmel
683298742Smmel	tgi->cfgreg = cfgreg;
684298742Smmel	intr_write_modify(sc, GPIO_INT_LVL, tgi, cfgreg, GPIO_INT_LVL_MASK);
685298742Smmel	tegra_gpio_pic_enable_intr(dev, isrc);
686298742Smmel
687298742Smmel	return (0);
688298742Smmel}
689298742Smmel
690298742Smmelstatic int
691298742Smmeltegra_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
692298742Smmel    struct resource *res, struct intr_map_data *data)
693298742Smmel{
694298742Smmel	struct tegra_gpio_softc *sc;
695298742Smmel	struct tegra_gpio_irqsrc *tgi;
696298742Smmel
697298742Smmel	sc = device_get_softc(dev);
698298742Smmel	tgi = (struct tegra_gpio_irqsrc *)isrc;
699298742Smmel
700298742Smmel	if (isrc->isrc_handlers == 0)
701298742Smmel		tegra_gpio_isrc_mask(sc, tgi, 0);
702298742Smmel	return (0);
703298742Smmel}
704298742Smmel
705298742Smmelstatic int
706296936Smmeltegra_gpio_probe(device_t dev)
707296936Smmel{
708296936Smmel
709296936Smmel	if (!ofw_bus_status_okay(dev))
710296936Smmel		return (ENXIO);
711296936Smmel	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
712296936Smmel		device_set_desc(dev, "Tegra GPIO Controller");
713296936Smmel		return (BUS_PROBE_DEFAULT);
714296936Smmel	}
715296936Smmel
716296936Smmel	return (ENXIO);
717296936Smmel}
718296936Smmel
719298742Smmel/* --------------------------------------------------------------------------
720298742Smmel *
721298742Smmel * Bus
722298742Smmel *
723298742Smmel */
724296936Smmelstatic int
725296936Smmeltegra_gpio_detach(device_t dev)
726296936Smmel{
727296936Smmel	struct tegra_gpio_softc *sc;
728298742Smmel	int i;
729296936Smmel
730296936Smmel	sc = device_get_softc(dev);
731296936Smmel
732298742Smmel	KASSERT(mtx_initialized(&sc->mtx), ("gpio mutex not initialized"));
733296936Smmel
734298742Smmel	for (i = 0; i < GPIO_NUM_BANKS; i++) {
735298742Smmel		if (sc->irq_ih[i] != NULL)
736298742Smmel			bus_teardown_intr(dev, sc->irq_res[i], sc->irq_ih[i]);
737298742Smmel	}
738298742Smmel
739298742Smmel	if (sc->isrcs != NULL)
740298742Smmel		tegra_gpio_pic_detach(sc);
741298742Smmel
742296936Smmel	gpiobus_detach_bus(dev);
743298742Smmel
744298742Smmel	for (i = 0; i < GPIO_NUM_BANKS; i++) {
745298742Smmel		if (sc->irq_res[i] != NULL)
746298742Smmel			bus_release_resource(dev, SYS_RES_IRQ, 0,
747298742Smmel			    sc->irq_res[i]);
748298742Smmel	}
749296936Smmel	if (sc->mem_res != NULL)
750296936Smmel		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
751298742Smmel	GPIO_LOCK_DESTROY(sc);
752296936Smmel
753296936Smmel	return(0);
754296936Smmel}
755296936Smmel
756296936Smmelstatic int
757296936Smmeltegra_gpio_attach(device_t dev)
758296936Smmel{
759296936Smmel	struct tegra_gpio_softc *sc;
760296936Smmel	int i, rid;
761296936Smmel
762296936Smmel	sc = device_get_softc(dev);
763298742Smmel	sc->dev = dev;
764298742Smmel	GPIO_LOCK_INIT(sc);
765296936Smmel
766296936Smmel	/* Allocate bus_space resources. */
767296936Smmel	rid = 0;
768296936Smmel	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
769296936Smmel	    RF_ACTIVE);
770296936Smmel	if (sc->mem_res == NULL) {
771296936Smmel		device_printf(dev, "Cannot allocate memory resources\n");
772296936Smmel		tegra_gpio_detach(dev);
773296936Smmel		return (ENXIO);
774296936Smmel	}
775296936Smmel
776296936Smmel	sc->gpio_npins = NGPIO;
777296936Smmel	for (i = 0; i < sc->gpio_npins; i++) {
778296936Smmel		sc->gpio_pins[i].gp_pin = i;
779298742Smmel		sc->gpio_pins[i].gp_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
780298742Smmel		    GPIO_INTR_LEVEL_LOW | GPIO_INTR_LEVEL_HIGH |
781298742Smmel		    GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING |
782298742Smmel		    GPIO_INTR_EDGE_BOTH;
783296936Smmel		snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, "gpio_%s.%d",
784296936Smmel		    tegra_gpio_port_names[ i / GPIO_PINS_IN_REG],
785296936Smmel		    i % GPIO_PINS_IN_REG);
786296936Smmel		sc->gpio_pins[i].gp_flags =
787296936Smmel		    gpio_read(sc, GPIO_OE, &sc->gpio_pins[i]) != 0 ?
788296936Smmel		    GPIO_PIN_OUTPUT : GPIO_PIN_INPUT;
789296936Smmel	}
790296936Smmel
791298742Smmel	/* Init interrupt related registes. */
792298742Smmel	for (i = 0; i < sc->gpio_npins; i += GPIO_PINS_IN_REG) {
793298742Smmel		bus_write_4(sc->mem_res, GPIO_INT_ENB + GPIO_REGNUM(i), 0);
794298742Smmel		bus_write_4(sc->mem_res, GPIO_INT_STA + GPIO_REGNUM(i), 0xFF);
795298742Smmel		bus_write_4(sc->mem_res, GPIO_INT_CLR + GPIO_REGNUM(i), 0xFF);
796298742Smmel	}
797298742Smmel
798298742Smmel	/* Allocate interrupts. */
799298742Smmel	for (i = 0; i < GPIO_NUM_BANKS; i++) {
800298742Smmel		sc->irq_cookies[i].sc = sc;
801298742Smmel		sc->irq_cookies[i].bank_num = i;
802298742Smmel		rid = i;
803298742Smmel		sc->irq_res[i] = bus_alloc_resource_any(dev, SYS_RES_IRQ,
804298742Smmel		    &rid, RF_ACTIVE);
805298742Smmel		if (sc->irq_res[i] == NULL) {
806298742Smmel			device_printf(dev, "Cannot allocate IRQ resources\n");
807298742Smmel			tegra_gpio_detach(dev);
808298742Smmel			return (ENXIO);
809298742Smmel		}
810298742Smmel		if ((bus_setup_intr(dev, sc->irq_res[i],
811298742Smmel		    INTR_TYPE_MISC | INTR_MPSAFE, tegra_gpio_intr, NULL,
812298742Smmel		    &sc->irq_cookies[i], &sc->irq_ih[i]))) {
813298742Smmel			device_printf(dev,
814298742Smmel			    "WARNING: unable to register interrupt handler\n");
815298742Smmel			tegra_gpio_detach(dev);
816298742Smmel			return (ENXIO);
817298742Smmel		}
818298742Smmel	}
819298742Smmel
820298742Smmel	if (tegra_gpio_pic_attach(sc) != 0) {
821298742Smmel		device_printf(dev, "WARNING: unable to attach PIC\n");
822296936Smmel		tegra_gpio_detach(dev);
823296936Smmel		return (ENXIO);
824296936Smmel	}
825296936Smmel
826298742Smmel	sc->busdev = gpiobus_attach_bus(dev);
827298742Smmel	if (sc->busdev == NULL) {
828298742Smmel		tegra_gpio_detach(dev);
829298742Smmel		return (ENXIO);
830298742Smmel	}
831298742Smmel
832296936Smmel	return (bus_generic_attach(dev));
833296936Smmel}
834296936Smmel
835296936Smmelstatic int
836296936Smmeltegra_map_gpios(device_t dev, phandle_t pdev, phandle_t gparent,
837296936Smmel    int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags)
838296936Smmel{
839296936Smmel
840296936Smmel	if (gcells != 2)
841296936Smmel		return (ERANGE);
842296936Smmel	*pin = gpios[0];
843296936Smmel	*flags= gpios[1];
844296936Smmel	return (0);
845296936Smmel}
846296936Smmel
847296936Smmelstatic phandle_t
848296936Smmeltegra_gpio_get_node(device_t bus, device_t dev)
849296936Smmel{
850296936Smmel
851296936Smmel	/* We only have one child, the GPIO bus, which needs our own node. */
852296936Smmel	return (ofw_bus_get_node(bus));
853296936Smmel}
854296936Smmel
855296936Smmelstatic device_method_t tegra_gpio_methods[] = {
856296936Smmel	DEVMETHOD(device_probe,		tegra_gpio_probe),
857296936Smmel	DEVMETHOD(device_attach,	tegra_gpio_attach),
858296936Smmel	DEVMETHOD(device_detach,	tegra_gpio_detach),
859296936Smmel
860298742Smmel	/* Interrupt controller interface */
861298742Smmel	DEVMETHOD(pic_disable_intr,	tegra_gpio_pic_disable_intr),
862298742Smmel	DEVMETHOD(pic_enable_intr,	tegra_gpio_pic_enable_intr),
863298742Smmel	DEVMETHOD(pic_map_intr,		tegra_gpio_pic_map_intr),
864298742Smmel	DEVMETHOD(pic_setup_intr,	tegra_gpio_pic_setup_intr),
865298742Smmel	DEVMETHOD(pic_teardown_intr,	tegra_gpio_pic_teardown_intr),
866298742Smmel	DEVMETHOD(pic_post_filter,	tegra_gpio_pic_post_filter),
867298742Smmel	DEVMETHOD(pic_post_ithread,	tegra_gpio_pic_post_ithread),
868298742Smmel	DEVMETHOD(pic_pre_ithread,	tegra_gpio_pic_pre_ithread),
869298742Smmel
870296936Smmel	/* GPIO protocol */
871296936Smmel	DEVMETHOD(gpio_get_bus,		tegra_gpio_get_bus),
872296936Smmel	DEVMETHOD(gpio_pin_max,		tegra_gpio_pin_max),
873296936Smmel	DEVMETHOD(gpio_pin_getname,	tegra_gpio_pin_getname),
874296936Smmel	DEVMETHOD(gpio_pin_getflags,	tegra_gpio_pin_getflags),
875296936Smmel	DEVMETHOD(gpio_pin_getcaps,	tegra_gpio_pin_getcaps),
876296936Smmel	DEVMETHOD(gpio_pin_setflags,	tegra_gpio_pin_setflags),
877296936Smmel	DEVMETHOD(gpio_pin_get,		tegra_gpio_pin_get),
878296936Smmel	DEVMETHOD(gpio_pin_set,		tegra_gpio_pin_set),
879296936Smmel	DEVMETHOD(gpio_pin_toggle,	tegra_gpio_pin_toggle),
880296936Smmel	DEVMETHOD(gpio_map_gpios,	tegra_map_gpios),
881296936Smmel
882296936Smmel	/* ofw_bus interface */
883296936Smmel	DEVMETHOD(ofw_bus_get_node,	tegra_gpio_get_node),
884296936Smmel
885296936Smmel	DEVMETHOD_END
886296936Smmel};
887296936Smmel
888296936Smmelstatic devclass_t tegra_gpio_devclass;
889308335Smmelstatic DEFINE_CLASS_0(gpio, tegra_gpio_driver, tegra_gpio_methods,
890308335Smmel    sizeof(struct tegra_gpio_softc));
891296936SmmelEARLY_DRIVER_MODULE(tegra_gpio, simplebus, tegra_gpio_driver,
892308335Smmel    tegra_gpio_devclass, NULL, NULL, 70);
893