xref: /freebsd/sys/arm/mv/mvebu_gpio.c (revision 62e8ccc3)
15e2e692cSMichal Meloun /*-
25e2e692cSMichal Meloun  * Copyright (c) 2020 Michal Meloun <mmel@FreeBSD.org>
35e2e692cSMichal Meloun  *
45e2e692cSMichal Meloun  * Redistribution and use in source and binary forms, with or without
55e2e692cSMichal Meloun  * modification, are permitted provided that the following conditions
65e2e692cSMichal Meloun  * are met:
75e2e692cSMichal Meloun  * 1. Redistributions of source code must retain the above copyright
85e2e692cSMichal Meloun  *    notice, this list of conditions and the following disclaimer.
95e2e692cSMichal Meloun  * 2. Redistributions in binary form must reproduce the above copyright
105e2e692cSMichal Meloun  *    notice, this list of conditions and the following disclaimer in the
115e2e692cSMichal Meloun  *    documentation and/or other materials provided with the distribution.
125e2e692cSMichal Meloun  *
135e2e692cSMichal Meloun  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
145e2e692cSMichal Meloun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
155e2e692cSMichal Meloun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
165e2e692cSMichal Meloun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
175e2e692cSMichal Meloun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
185e2e692cSMichal Meloun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
195e2e692cSMichal Meloun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
205e2e692cSMichal Meloun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
215e2e692cSMichal Meloun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
225e2e692cSMichal Meloun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
235e2e692cSMichal Meloun  * SUCH DAMAGE.
245e2e692cSMichal Meloun  */
255e2e692cSMichal Meloun 
265e2e692cSMichal Meloun #include <sys/cdefs.h>
275e2e692cSMichal Meloun /*
285e2e692cSMichal Meloun  * ARMADA 8040 GPIO driver.
295e2e692cSMichal Meloun  */
305e2e692cSMichal Meloun #include "opt_platform.h"
315e2e692cSMichal Meloun #include <sys/param.h>
325e2e692cSMichal Meloun #include <sys/systm.h>
335e2e692cSMichal Meloun #include <sys/bus.h>
345e2e692cSMichal Meloun #include <sys/gpio.h>
355e2e692cSMichal Meloun #include <sys/kernel.h>
365e2e692cSMichal Meloun #include <sys/proc.h>
375e2e692cSMichal Meloun #include <sys/rman.h>
385e2e692cSMichal Meloun #include <sys/lock.h>
395e2e692cSMichal Meloun #include <sys/module.h>
405e2e692cSMichal Meloun #include <sys/mutex.h>
415e2e692cSMichal Meloun 
425e2e692cSMichal Meloun #include <machine/bus.h>
435e2e692cSMichal Meloun #include <machine/intr.h>
445e2e692cSMichal Meloun #include <machine/resource.h>
455e2e692cSMichal Meloun 
4662e8ccc3SEmmanuel Vadot #include <dev/syscon/syscon.h>
475e2e692cSMichal Meloun 
485e2e692cSMichal Meloun #include <dev/gpio/gpiobusvar.h>
495e2e692cSMichal Meloun 
505e2e692cSMichal Meloun #include <dev/ofw/openfirm.h>
515e2e692cSMichal Meloun #include <dev/ofw/ofw_bus.h>
525e2e692cSMichal Meloun #include <dev/ofw/ofw_bus_subr.h>
535e2e692cSMichal Meloun 
545e2e692cSMichal Meloun #include "pic_if.h"
555e2e692cSMichal Meloun #include "syscon_if.h"
565e2e692cSMichal Meloun 
575e2e692cSMichal Meloun #define	GPIO_LOCK(_sc)		mtx_lock(&(_sc)->mtx)
585e2e692cSMichal Meloun #define	GPIO_UNLOCK(_sc)	mtx_unlock(&(_sc)->mtx)
595e2e692cSMichal Meloun #define	GPIO_LOCK_INIT(_sc)	mtx_init(&_sc->mtx, 			\
605e2e692cSMichal Meloun 	    device_get_nameunit(_sc->dev), "mvebu_gpio", MTX_DEF)
615e2e692cSMichal Meloun #define	GPIO_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->mtx);
625e2e692cSMichal Meloun #define	GPIO_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->mtx, MA_OWNED);
635e2e692cSMichal Meloun #define	GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED);
645e2e692cSMichal Meloun 
655e2e692cSMichal Meloun #define	GPIO_DATA_OUT		0x00
665e2e692cSMichal Meloun #define	GPIO_CONTROL		0x04
675e2e692cSMichal Meloun #define	GPIO_BLINK_ENA		0x08
685e2e692cSMichal Meloun #define	GPIO_DATA_IN_POL	0x0C
695e2e692cSMichal Meloun #define	GPIO_DATA_IN		0x10
705e2e692cSMichal Meloun #define	GPIO_INT_CAUSE		0x14
715e2e692cSMichal Meloun #define	GPIO_INT_MASK		0x18
725e2e692cSMichal Meloun #define	GPIO_INT_LEVEL_MASK	0x1C
735e2e692cSMichal Meloun #define	GPIO_CONTROL_SET	0x28
745e2e692cSMichal Meloun #define	GPIO_CONTROL_CLR	0x2C
755e2e692cSMichal Meloun #define	GPIO_DATA_SET		0x30
765e2e692cSMichal Meloun #define	GPIO_DATA_CLR		0x34
775e2e692cSMichal Meloun 
785e2e692cSMichal Meloun #define	GPIO_BIT(_p)		((_p) % 32)
795e2e692cSMichal Meloun #define	GPIO_REGNUM(_p)		((_p) / 32)
805e2e692cSMichal Meloun 
815e2e692cSMichal Meloun #define	MV_GPIO_MAX_NIRQS	4
825e2e692cSMichal Meloun #define	MV_GPIO_MAX_NPINS	32
835e2e692cSMichal Meloun 
845e2e692cSMichal Meloun struct mvebu_gpio_irqsrc {
855e2e692cSMichal Meloun 	struct intr_irqsrc	isrc;
865e2e692cSMichal Meloun 	u_int			irq;
875e2e692cSMichal Meloun 	bool			is_level;
885e2e692cSMichal Meloun 	bool			is_inverted;
895e2e692cSMichal Meloun };
905e2e692cSMichal Meloun 
915e2e692cSMichal Meloun struct mvebu_gpio_softc;
925e2e692cSMichal Meloun struct mvebu_gpio_irq_cookie {
935e2e692cSMichal Meloun 	struct mvebu_gpio_softc	*sc;
945e2e692cSMichal Meloun 	int			bank_num;
955e2e692cSMichal Meloun };
965e2e692cSMichal Meloun 
975e2e692cSMichal Meloun struct mvebu_gpio_softc {
985e2e692cSMichal Meloun 	device_t		dev;
995e2e692cSMichal Meloun 	device_t		busdev;
1005e2e692cSMichal Meloun 	struct mtx		mtx;
1015e2e692cSMichal Meloun 	struct syscon		*syscon;
1025e2e692cSMichal Meloun 	uint32_t		offset;
1035e2e692cSMichal Meloun 	struct resource		*irq_res[MV_GPIO_MAX_NIRQS];
1045e2e692cSMichal Meloun 	void			*irq_ih[MV_GPIO_MAX_NIRQS];
1055e2e692cSMichal Meloun 	struct mvebu_gpio_irq_cookie irq_cookies[MV_GPIO_MAX_NIRQS];
1065e2e692cSMichal Meloun 	int			gpio_npins;
1075e2e692cSMichal Meloun 	struct gpio_pin		gpio_pins[MV_GPIO_MAX_NPINS];
1085e2e692cSMichal Meloun 	struct mvebu_gpio_irqsrc *isrcs;
1095e2e692cSMichal Meloun };
1105e2e692cSMichal Meloun 
1115e2e692cSMichal Meloun static struct ofw_compat_data compat_data[] = {
1125e2e692cSMichal Meloun 	{"marvell,armada-8k-gpio", 1},
1135e2e692cSMichal Meloun 	{NULL, 0}
1145e2e692cSMichal Meloun };
1155e2e692cSMichal Meloun 
1165e2e692cSMichal Meloun /* --------------------------------------------------------------------------
1175e2e692cSMichal Meloun  *
1185e2e692cSMichal Meloun  * GPIO
1195e2e692cSMichal Meloun  *
1205e2e692cSMichal Meloun  */
1215e2e692cSMichal Meloun static inline void
gpio_write(struct mvebu_gpio_softc * sc,bus_size_t reg,struct gpio_pin * pin,uint32_t val)1225e2e692cSMichal Meloun gpio_write(struct mvebu_gpio_softc *sc, bus_size_t reg,
1235e2e692cSMichal Meloun     struct gpio_pin *pin, uint32_t val)
1245e2e692cSMichal Meloun {
1255e2e692cSMichal Meloun 	int bit;
1265e2e692cSMichal Meloun 
1275e2e692cSMichal Meloun 	bit = GPIO_BIT(pin->gp_pin);
1285e2e692cSMichal Meloun 	SYSCON_WRITE_4(sc->syscon, sc->offset + GPIO_REGNUM(pin->gp_pin) + reg,
129a5dce53bSMichal Meloun 	    (val & 1) << bit);
1305e2e692cSMichal Meloun }
1315e2e692cSMichal Meloun 
1325e2e692cSMichal Meloun static inline uint32_t
gpio_read(struct mvebu_gpio_softc * sc,bus_size_t reg,struct gpio_pin * pin)1335e2e692cSMichal Meloun gpio_read(struct mvebu_gpio_softc *sc, bus_size_t reg, struct gpio_pin *pin)
1345e2e692cSMichal Meloun {
1355e2e692cSMichal Meloun 	int bit;
1365e2e692cSMichal Meloun 	uint32_t val;
1375e2e692cSMichal Meloun 
1385e2e692cSMichal Meloun 	bit = GPIO_BIT(pin->gp_pin);
1395e2e692cSMichal Meloun 	val = SYSCON_READ_4(sc->syscon,
1405e2e692cSMichal Meloun 	    sc->offset + GPIO_REGNUM(pin->gp_pin) + reg);
141a5dce53bSMichal Meloun 
1425e2e692cSMichal Meloun 	return (val >> bit) & 1;
1435e2e692cSMichal Meloun }
1445e2e692cSMichal Meloun 
145a5dce53bSMichal Meloun static inline void
gpio_modify(struct mvebu_gpio_softc * sc,bus_size_t reg,struct gpio_pin * pin,uint32_t val)146a5dce53bSMichal Meloun gpio_modify(struct mvebu_gpio_softc *sc, bus_size_t reg,
147a5dce53bSMichal Meloun     struct gpio_pin *pin, uint32_t val)
148a5dce53bSMichal Meloun {
149a5dce53bSMichal Meloun 	int bit;
150a5dce53bSMichal Meloun 
151a5dce53bSMichal Meloun 	bit = GPIO_BIT(pin->gp_pin);
152a5dce53bSMichal Meloun 	SYSCON_MODIFY_4(sc->syscon, sc->offset + GPIO_REGNUM(pin->gp_pin) + reg,
153a5dce53bSMichal Meloun 	    1 << bit, (val & 1) << bit);
154a5dce53bSMichal Meloun }
155a5dce53bSMichal Meloun 
1565e2e692cSMichal Meloun static void
mvebu_gpio_pin_configure(struct mvebu_gpio_softc * sc,struct gpio_pin * pin,unsigned int flags)1575e2e692cSMichal Meloun mvebu_gpio_pin_configure(struct mvebu_gpio_softc *sc, struct gpio_pin *pin,
1585e2e692cSMichal Meloun     unsigned int flags)
1595e2e692cSMichal Meloun {
1605e2e692cSMichal Meloun 
1615e2e692cSMichal Meloun 	if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) == 0)
1625e2e692cSMichal Meloun 		return;
1635e2e692cSMichal Meloun 
1645e2e692cSMichal Meloun 	/* Manage input/output */
1655e2e692cSMichal Meloun 	pin->gp_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
1665e2e692cSMichal Meloun 	if (flags & GPIO_PIN_OUTPUT) {
1675e2e692cSMichal Meloun 		pin->gp_flags |= GPIO_PIN_OUTPUT;
16801c6d791SMichal Meloun 		gpio_write(sc, GPIO_CONTROL_CLR, pin, 1);
1695e2e692cSMichal Meloun 	} else {
1705e2e692cSMichal Meloun 		pin->gp_flags |= GPIO_PIN_INPUT;
17101c6d791SMichal Meloun 		gpio_write(sc, GPIO_CONTROL_SET, pin, 1);
1725e2e692cSMichal Meloun 	}
1735e2e692cSMichal Meloun }
1745e2e692cSMichal Meloun 
1755e2e692cSMichal Meloun static device_t
mvebu_gpio_get_bus(device_t dev)1765e2e692cSMichal Meloun mvebu_gpio_get_bus(device_t dev)
1775e2e692cSMichal Meloun {
1785e2e692cSMichal Meloun 	struct mvebu_gpio_softc *sc;
1795e2e692cSMichal Meloun 
1805e2e692cSMichal Meloun 	sc = device_get_softc(dev);
1815e2e692cSMichal Meloun 	return (sc->busdev);
1825e2e692cSMichal Meloun }
1835e2e692cSMichal Meloun 
1845e2e692cSMichal Meloun static int
mvebu_gpio_pin_max(device_t dev,int * maxpin)1855e2e692cSMichal Meloun mvebu_gpio_pin_max(device_t dev, int *maxpin)
1865e2e692cSMichal Meloun {
1875e2e692cSMichal Meloun 	struct mvebu_gpio_softc *sc;
1885e2e692cSMichal Meloun 
1895e2e692cSMichal Meloun 	sc = device_get_softc(dev);
1905e2e692cSMichal Meloun 	*maxpin = sc->gpio_npins - 1;
1915e2e692cSMichal Meloun 	return (0);
1925e2e692cSMichal Meloun }
1935e2e692cSMichal Meloun 
1945e2e692cSMichal Meloun static int
mvebu_gpio_pin_getcaps(device_t dev,uint32_t pin,uint32_t * caps)1955e2e692cSMichal Meloun mvebu_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
1965e2e692cSMichal Meloun {
1975e2e692cSMichal Meloun 	struct mvebu_gpio_softc *sc;
1985e2e692cSMichal Meloun 
1995e2e692cSMichal Meloun 	sc = device_get_softc(dev);
2005e2e692cSMichal Meloun 	if (pin >= sc->gpio_npins)
2015e2e692cSMichal Meloun 		return (EINVAL);
2025e2e692cSMichal Meloun 
2035e2e692cSMichal Meloun 	*caps = sc->gpio_pins[pin].gp_caps;
2045e2e692cSMichal Meloun 
2055e2e692cSMichal Meloun 	return (0);
2065e2e692cSMichal Meloun }
2075e2e692cSMichal Meloun 
2085e2e692cSMichal Meloun static int
mvebu_gpio_pin_getflags(device_t dev,uint32_t pin,uint32_t * flags)2095e2e692cSMichal Meloun mvebu_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
2105e2e692cSMichal Meloun {
2115e2e692cSMichal Meloun 	struct mvebu_gpio_softc *sc;
2125e2e692cSMichal Meloun 
2135e2e692cSMichal Meloun 	sc = device_get_softc(dev);
2145e2e692cSMichal Meloun 	if (pin >= sc->gpio_npins)
2155e2e692cSMichal Meloun 		return (EINVAL);
2165e2e692cSMichal Meloun 
2175e2e692cSMichal Meloun 	*flags = sc->gpio_pins[pin].gp_flags;
2185e2e692cSMichal Meloun 
2195e2e692cSMichal Meloun 	return (0);
2205e2e692cSMichal Meloun }
2215e2e692cSMichal Meloun 
2225e2e692cSMichal Meloun static int
mvebu_gpio_pin_getname(device_t dev,uint32_t pin,char * name)2235e2e692cSMichal Meloun mvebu_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
2245e2e692cSMichal Meloun {
2255e2e692cSMichal Meloun 	struct mvebu_gpio_softc *sc;
2265e2e692cSMichal Meloun 
2275e2e692cSMichal Meloun 	sc = device_get_softc(dev);
2285e2e692cSMichal Meloun 	if (pin >= sc->gpio_npins)
2295e2e692cSMichal Meloun 		return (EINVAL);
2305e2e692cSMichal Meloun 
2315e2e692cSMichal Meloun 	memcpy(name, sc->gpio_pins[pin].gp_name, GPIOMAXNAME);
2325e2e692cSMichal Meloun 
2335e2e692cSMichal Meloun 	return (0);
2345e2e692cSMichal Meloun }
2355e2e692cSMichal Meloun 
2365e2e692cSMichal Meloun static int
mvebu_gpio_pin_setflags(device_t dev,uint32_t pin,uint32_t flags)2375e2e692cSMichal Meloun mvebu_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
2385e2e692cSMichal Meloun {
2395e2e692cSMichal Meloun 	struct mvebu_gpio_softc *sc;
2405e2e692cSMichal Meloun 
2415e2e692cSMichal Meloun 	sc = device_get_softc(dev);
2425e2e692cSMichal Meloun 	if (pin >= sc->gpio_npins)
2435e2e692cSMichal Meloun 		return (EINVAL);
2445e2e692cSMichal Meloun 
2455e2e692cSMichal Meloun 	mvebu_gpio_pin_configure(sc, &sc->gpio_pins[pin], flags);
2465e2e692cSMichal Meloun 
2475e2e692cSMichal Meloun 	return (0);
2485e2e692cSMichal Meloun }
2495e2e692cSMichal Meloun 
2505e2e692cSMichal Meloun static int
mvebu_gpio_pin_set(device_t dev,uint32_t pin,unsigned int value)2515e2e692cSMichal Meloun mvebu_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
2525e2e692cSMichal Meloun {
2535e2e692cSMichal Meloun 	struct mvebu_gpio_softc *sc;
2545e2e692cSMichal Meloun 
2555e2e692cSMichal Meloun 	sc = device_get_softc(dev);
2565e2e692cSMichal Meloun 	if (pin >= sc->gpio_npins)
2575e2e692cSMichal Meloun 		return (EINVAL);
2585e2e692cSMichal Meloun 
2595e2e692cSMichal Meloun 	if (value != 0)
2605e2e692cSMichal Meloun 		gpio_write(sc, GPIO_DATA_SET, &sc->gpio_pins[pin], 1);
2615e2e692cSMichal Meloun 	else
2625e2e692cSMichal Meloun 		gpio_write(sc, GPIO_DATA_CLR, &sc->gpio_pins[pin], 1);
2635e2e692cSMichal Meloun 
2645e2e692cSMichal Meloun 	return (0);
2655e2e692cSMichal Meloun }
2665e2e692cSMichal Meloun 
2675e2e692cSMichal Meloun static int
mvebu_gpio_pin_get(device_t dev,uint32_t pin,unsigned int * val)2685e2e692cSMichal Meloun mvebu_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
2695e2e692cSMichal Meloun {
2705e2e692cSMichal Meloun 	struct mvebu_gpio_softc *sc;
2715e2e692cSMichal Meloun 
2725e2e692cSMichal Meloun 	sc = device_get_softc(dev);
2735e2e692cSMichal Meloun 	if (pin >= sc->gpio_npins)
2745e2e692cSMichal Meloun 		return (EINVAL);
2755e2e692cSMichal Meloun 
2765e2e692cSMichal Meloun 	GPIO_LOCK(sc);
2775e2e692cSMichal Meloun 	*val = gpio_read(sc, GPIO_DATA_IN, &sc->gpio_pins[pin]);
2785e2e692cSMichal Meloun 	*val ^= gpio_read(sc, GPIO_DATA_IN_POL, &sc->gpio_pins[pin]);
2795e2e692cSMichal Meloun 	GPIO_UNLOCK(sc);
2805e2e692cSMichal Meloun 
2815e2e692cSMichal Meloun 	return (0);
2825e2e692cSMichal Meloun }
2835e2e692cSMichal Meloun 
2845e2e692cSMichal Meloun static int
mvebu_gpio_pin_toggle(device_t dev,uint32_t pin)2855e2e692cSMichal Meloun mvebu_gpio_pin_toggle(device_t dev, uint32_t pin)
2865e2e692cSMichal Meloun {
2875e2e692cSMichal Meloun 	struct mvebu_gpio_softc *sc;
2885e2e692cSMichal Meloun 	uint32_t val;
2895e2e692cSMichal Meloun 
2905e2e692cSMichal Meloun 	sc = device_get_softc(dev);
2915e2e692cSMichal Meloun 	if (pin >= sc->gpio_npins)
2925e2e692cSMichal Meloun 		return (EINVAL);
2935e2e692cSMichal Meloun 
2945e2e692cSMichal Meloun 	GPIO_LOCK(sc);
2955e2e692cSMichal Meloun 	mvebu_gpio_pin_get(sc->dev, pin, &val);
2965e2e692cSMichal Meloun 	if (val != 0)
2975e2e692cSMichal Meloun 		gpio_write(sc, GPIO_DATA_CLR, &sc->gpio_pins[pin], 1);
2985e2e692cSMichal Meloun 	else
2995e2e692cSMichal Meloun 		gpio_write(sc, GPIO_DATA_SET, &sc->gpio_pins[pin], 1);
3005e2e692cSMichal Meloun 	GPIO_UNLOCK(sc);
3015e2e692cSMichal Meloun 
3025e2e692cSMichal Meloun 	return (0);
3035e2e692cSMichal Meloun }
3045e2e692cSMichal Meloun 
3055e2e692cSMichal Meloun /* --------------------------------------------------------------------------
3065e2e692cSMichal Meloun  *
3075e2e692cSMichal Meloun  * Interrupts
3085e2e692cSMichal Meloun  *
3095e2e692cSMichal Meloun  */
3105e2e692cSMichal Meloun static inline void
intr_modify(struct mvebu_gpio_softc * sc,bus_addr_t reg,struct mvebu_gpio_irqsrc * mgi,uint32_t val)3115e2e692cSMichal Meloun intr_modify(struct mvebu_gpio_softc *sc, bus_addr_t reg,
312a5dce53bSMichal Meloun     struct mvebu_gpio_irqsrc *mgi, uint32_t val)
3135e2e692cSMichal Meloun {
3145e2e692cSMichal Meloun 	int bit;
3155e2e692cSMichal Meloun 
3165e2e692cSMichal Meloun 	bit = GPIO_BIT(mgi->irq);
317a5dce53bSMichal Meloun 	SYSCON_MODIFY_4(sc->syscon,
318a5dce53bSMichal Meloun 	    sc->offset + GPIO_REGNUM(mgi->irq) + reg, 1 << bit,
319a5dce53bSMichal Meloun 	    (val & 1) << bit);
3205e2e692cSMichal Meloun }
3215e2e692cSMichal Meloun 
3225e2e692cSMichal Meloun static inline void
mvebu_gpio_isrc_mask(struct mvebu_gpio_softc * sc,struct mvebu_gpio_irqsrc * mgi,uint32_t val)3235e2e692cSMichal Meloun mvebu_gpio_isrc_mask(struct mvebu_gpio_softc *sc,
3245e2e692cSMichal Meloun      struct mvebu_gpio_irqsrc *mgi, uint32_t val)
3255e2e692cSMichal Meloun {
3265e2e692cSMichal Meloun 
3275e2e692cSMichal Meloun 	if (mgi->is_level)
328a5dce53bSMichal Meloun 		intr_modify(sc, GPIO_INT_LEVEL_MASK, mgi, val);
3295e2e692cSMichal Meloun 	else
330a5dce53bSMichal Meloun 		intr_modify(sc, GPIO_INT_MASK, mgi, val);
3315e2e692cSMichal Meloun }
3325e2e692cSMichal Meloun 
3335e2e692cSMichal Meloun static inline void
mvebu_gpio_isrc_eoi(struct mvebu_gpio_softc * sc,struct mvebu_gpio_irqsrc * mgi)3345e2e692cSMichal Meloun mvebu_gpio_isrc_eoi(struct mvebu_gpio_softc *sc,
3355e2e692cSMichal Meloun      struct mvebu_gpio_irqsrc *mgi)
3365e2e692cSMichal Meloun {
337a5dce53bSMichal Meloun 	int bit;
3385e2e692cSMichal Meloun 
339a5dce53bSMichal Meloun 	if (!mgi->is_level) {
340a5dce53bSMichal Meloun 		bit = GPIO_BIT(mgi->irq);
341a5dce53bSMichal Meloun 		SYSCON_WRITE_4(sc->syscon,
342a5dce53bSMichal Meloun 		    sc->offset + GPIO_REGNUM(mgi->irq) + GPIO_INT_CAUSE,
343a5dce53bSMichal Meloun 		    ~(1 << bit));
344a5dce53bSMichal Meloun 	}
3455e2e692cSMichal Meloun }
3465e2e692cSMichal Meloun 
3475e2e692cSMichal Meloun static int
mvebu_gpio_pic_attach(struct mvebu_gpio_softc * sc)3485e2e692cSMichal Meloun mvebu_gpio_pic_attach(struct mvebu_gpio_softc *sc)
3495e2e692cSMichal Meloun {
3505e2e692cSMichal Meloun 	int rv;
3515e2e692cSMichal Meloun 	uint32_t irq;
3525e2e692cSMichal Meloun 	const char *name;
3535e2e692cSMichal Meloun 
3545e2e692cSMichal Meloun 	sc->isrcs = malloc(sizeof(*sc->isrcs) * sc->gpio_npins, M_DEVBUF,
3555e2e692cSMichal Meloun 	    M_WAITOK | M_ZERO);
3565e2e692cSMichal Meloun 
3575e2e692cSMichal Meloun 	name = device_get_nameunit(sc->dev);
3585e2e692cSMichal Meloun 	for (irq = 0; irq < sc->gpio_npins; irq++) {
3595e2e692cSMichal Meloun 		sc->isrcs[irq].irq = irq;
3605e2e692cSMichal Meloun 		sc->isrcs[irq].is_level = false;
3615e2e692cSMichal Meloun 		sc->isrcs[irq].is_inverted = false;
3625e2e692cSMichal Meloun 		rv = intr_isrc_register(&sc->isrcs[irq].isrc,
3635e2e692cSMichal Meloun 		    sc->dev, 0, "%s,%u", name, irq);
3645e2e692cSMichal Meloun 		if (rv != 0)
3655e2e692cSMichal Meloun 			return (rv); /* XXX deregister ISRCs */
3665e2e692cSMichal Meloun 	}
3675e2e692cSMichal Meloun 	if (intr_pic_register(sc->dev,
3685e2e692cSMichal Meloun 	    OF_xref_from_node(ofw_bus_get_node(sc->dev))) == NULL)
3695e2e692cSMichal Meloun 		return (ENXIO);
3705e2e692cSMichal Meloun 
3715e2e692cSMichal Meloun 	return (0);
3725e2e692cSMichal Meloun }
3735e2e692cSMichal Meloun 
3745e2e692cSMichal Meloun static int
mvebu_gpio_pic_detach(struct mvebu_gpio_softc * sc)3755e2e692cSMichal Meloun mvebu_gpio_pic_detach(struct mvebu_gpio_softc *sc)
3765e2e692cSMichal Meloun {
3775e2e692cSMichal Meloun 
3785e2e692cSMichal Meloun 	/*
3795e2e692cSMichal Meloun 	 *  There has not been established any procedure yet
3805e2e692cSMichal Meloun 	 *  how to detach PIC from living system correctly.
3815e2e692cSMichal Meloun 	 */
3825e2e692cSMichal Meloun 	device_printf(sc->dev, "%s: not implemented yet\n", __func__);
3835e2e692cSMichal Meloun 	return (EBUSY);
3845e2e692cSMichal Meloun }
3855e2e692cSMichal Meloun 
3865e2e692cSMichal Meloun static void
mvebu_gpio_pic_disable_intr(device_t dev,struct intr_irqsrc * isrc)3875e2e692cSMichal Meloun mvebu_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
3885e2e692cSMichal Meloun {
3895e2e692cSMichal Meloun 	struct mvebu_gpio_softc *sc;
3905e2e692cSMichal Meloun 	struct mvebu_gpio_irqsrc *mgi;
3915e2e692cSMichal Meloun 
3925e2e692cSMichal Meloun 	sc = device_get_softc(dev);
3935e2e692cSMichal Meloun 	mgi = (struct mvebu_gpio_irqsrc *)isrc;
3945e2e692cSMichal Meloun 	mvebu_gpio_isrc_mask(sc, mgi, 0);
3955e2e692cSMichal Meloun }
3965e2e692cSMichal Meloun 
3975e2e692cSMichal Meloun static void
mvebu_gpio_pic_enable_intr(device_t dev,struct intr_irqsrc * isrc)3985e2e692cSMichal Meloun mvebu_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
3995e2e692cSMichal Meloun {
4005e2e692cSMichal Meloun 	struct mvebu_gpio_softc *sc;
4015e2e692cSMichal Meloun 	struct mvebu_gpio_irqsrc *mgi;
4025e2e692cSMichal Meloun 
4035e2e692cSMichal Meloun 	sc = device_get_softc(dev);
4045e2e692cSMichal Meloun 	mgi = (struct mvebu_gpio_irqsrc *)isrc;
4055e2e692cSMichal Meloun 	mvebu_gpio_isrc_mask(sc, mgi, 1);
4065e2e692cSMichal Meloun }
4075e2e692cSMichal Meloun 
4085e2e692cSMichal Meloun static int
mvebu_gpio_pic_map_fdt(struct mvebu_gpio_softc * sc,u_int ncells,pcell_t * cells,u_int * irqp,bool * invertedp,bool * levelp)4095e2e692cSMichal Meloun mvebu_gpio_pic_map_fdt(struct mvebu_gpio_softc *sc, u_int ncells,
4105e2e692cSMichal Meloun     pcell_t *cells, u_int *irqp, bool *invertedp, bool *levelp)
4115e2e692cSMichal Meloun {
4125e2e692cSMichal Meloun 	bool inverted, level;
4135e2e692cSMichal Meloun 
4145e2e692cSMichal Meloun 	/*
4155e2e692cSMichal Meloun 	 * The first cell is the interrupt number.
4165e2e692cSMichal Meloun 	 * The second cell is used to specify flags:
4175e2e692cSMichal Meloun 	 *	bits[3:0] trigger type and level flags:
4185e2e692cSMichal Meloun 	 *		1 = low-to-high edge triggered.
4195e2e692cSMichal Meloun 	 *		2 = high-to-low edge triggered.
4205e2e692cSMichal Meloun 	 *		4 = active high level-sensitive.
4215e2e692cSMichal Meloun 	 *		8 = active low level-sensitive.
4225e2e692cSMichal Meloun 	 */
4235e2e692cSMichal Meloun 	if (ncells != 2 || cells[0] >= sc->gpio_npins)
4245e2e692cSMichal Meloun 		return (EINVAL);
4255e2e692cSMichal Meloun 
4265e2e692cSMichal Meloun 	switch (cells[1]) {
4275e2e692cSMichal Meloun 	case 1:
4285e2e692cSMichal Meloun 		inverted  = false;
4295e2e692cSMichal Meloun 		level  = false;
4305e2e692cSMichal Meloun 		break;
4315e2e692cSMichal Meloun 	case 2:
4325e2e692cSMichal Meloun 		inverted  = true;
4335e2e692cSMichal Meloun 		level  = false;
4345e2e692cSMichal Meloun 		break;
4355e2e692cSMichal Meloun 	case 4:
4365e2e692cSMichal Meloun 		inverted  = false;
4375e2e692cSMichal Meloun 		level  = true;
4385e2e692cSMichal Meloun 		break;
4395e2e692cSMichal Meloun 	case 8:
4405e2e692cSMichal Meloun 		inverted  = true;
4415e2e692cSMichal Meloun 		level  = true;
4425e2e692cSMichal Meloun 		break;
4435e2e692cSMichal Meloun 	default:
4445e2e692cSMichal Meloun 		return (EINVAL);
4455e2e692cSMichal Meloun 	}
4465e2e692cSMichal Meloun 	*irqp = cells[0];
4475e2e692cSMichal Meloun 	if (invertedp != NULL)
4485e2e692cSMichal Meloun 		*invertedp = inverted;
4495e2e692cSMichal Meloun 	if (levelp != NULL)
4505e2e692cSMichal Meloun 		*levelp = level;
4515e2e692cSMichal Meloun 	return (0);
4525e2e692cSMichal Meloun }
4535e2e692cSMichal Meloun 
4545e2e692cSMichal Meloun static int
mvebu_gpio_pic_map_gpio(struct mvebu_gpio_softc * sc,u_int gpio_pin_num,u_int gpio_pin_flags,u_int intr_mode,u_int * irqp,bool * invertedp,bool * levelp)4555e2e692cSMichal Meloun mvebu_gpio_pic_map_gpio(struct mvebu_gpio_softc *sc, u_int gpio_pin_num,
4565e2e692cSMichal Meloun     u_int gpio_pin_flags, u_int intr_mode, u_int *irqp, bool *invertedp,
4575e2e692cSMichal Meloun     bool *levelp)
4585e2e692cSMichal Meloun {
4595e2e692cSMichal Meloun 	bool inverted, level;
4605e2e692cSMichal Meloun 
4615e2e692cSMichal Meloun 	if (gpio_pin_num >= sc->gpio_npins)
4625e2e692cSMichal Meloun 		return (EINVAL);
4635e2e692cSMichal Meloun 
4645e2e692cSMichal Meloun 	switch (intr_mode) {
4655e2e692cSMichal Meloun 	case GPIO_INTR_LEVEL_LOW:
4665e2e692cSMichal Meloun 		inverted  = true;
4675e2e692cSMichal Meloun 		level = true;
4685e2e692cSMichal Meloun 		break;
4695e2e692cSMichal Meloun 	case GPIO_INTR_LEVEL_HIGH:
4705e2e692cSMichal Meloun 		inverted  = false;
4715e2e692cSMichal Meloun 		level = true;
4725e2e692cSMichal Meloun 		break;
4735e2e692cSMichal Meloun 	case GPIO_INTR_CONFORM:
4745e2e692cSMichal Meloun 	case GPIO_INTR_EDGE_RISING:
4755e2e692cSMichal Meloun 		inverted  = false;
4765e2e692cSMichal Meloun 		level = false;
4775e2e692cSMichal Meloun 		break;
4785e2e692cSMichal Meloun 	case GPIO_INTR_EDGE_FALLING:
4795e2e692cSMichal Meloun 		inverted  = true;
4805e2e692cSMichal Meloun 		level = false;
4815e2e692cSMichal Meloun 		break;
4825e2e692cSMichal Meloun 	default:
4835e2e692cSMichal Meloun 		return (EINVAL);
4845e2e692cSMichal Meloun 	}
4855e2e692cSMichal Meloun 	*irqp = gpio_pin_num;
4865e2e692cSMichal Meloun 	if (invertedp != NULL)
4875e2e692cSMichal Meloun 		*invertedp = inverted;
4885e2e692cSMichal Meloun 	if (levelp != NULL)
4895e2e692cSMichal Meloun 		*levelp = level;
4905e2e692cSMichal Meloun 	return (0);
4915e2e692cSMichal Meloun }
4925e2e692cSMichal Meloun 
4935e2e692cSMichal Meloun static int
mvebu_gpio_pic_map_intr(device_t dev,struct intr_map_data * data,struct intr_irqsrc ** isrcp)4945e2e692cSMichal Meloun mvebu_gpio_pic_map_intr(device_t dev, struct intr_map_data *data,
4955e2e692cSMichal Meloun     struct intr_irqsrc **isrcp)
4965e2e692cSMichal Meloun {
4975e2e692cSMichal Meloun 	int rv;
4985e2e692cSMichal Meloun 	u_int irq;
4995e2e692cSMichal Meloun 	struct mvebu_gpio_softc *sc;
5005e2e692cSMichal Meloun 
5015e2e692cSMichal Meloun 	sc = device_get_softc(dev);
5025e2e692cSMichal Meloun 
5035e2e692cSMichal Meloun 	if (data->type == INTR_MAP_DATA_FDT) {
5045e2e692cSMichal Meloun 		struct intr_map_data_fdt *daf;
5055e2e692cSMichal Meloun 
5065e2e692cSMichal Meloun 		daf = (struct intr_map_data_fdt *)data;
5075e2e692cSMichal Meloun 		rv = mvebu_gpio_pic_map_fdt(sc, daf->ncells, daf->cells, &irq,
5085e2e692cSMichal Meloun 		    NULL, NULL);
5095e2e692cSMichal Meloun 	} else if (data->type == INTR_MAP_DATA_GPIO) {
5105e2e692cSMichal Meloun 		struct intr_map_data_gpio *dag;
5115e2e692cSMichal Meloun 
5125e2e692cSMichal Meloun 		dag = (struct intr_map_data_gpio *)data;
5135e2e692cSMichal Meloun 		rv = mvebu_gpio_pic_map_gpio(sc, dag->gpio_pin_num,
5145e2e692cSMichal Meloun 		   dag->gpio_pin_flags, dag->gpio_intr_mode, &irq, NULL, NULL);
5155e2e692cSMichal Meloun 	} else
5165e2e692cSMichal Meloun 		return (ENOTSUP);
5175e2e692cSMichal Meloun 
5185e2e692cSMichal Meloun 	if (rv == 0)
5195e2e692cSMichal Meloun 		*isrcp = &sc->isrcs[irq].isrc;
5205e2e692cSMichal Meloun 	return (rv);
5215e2e692cSMichal Meloun }
5225e2e692cSMichal Meloun 
5235e2e692cSMichal Meloun static void
mvebu_gpio_pic_post_filter(device_t dev,struct intr_irqsrc * isrc)5245e2e692cSMichal Meloun mvebu_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
5255e2e692cSMichal Meloun {
5265e2e692cSMichal Meloun 	struct mvebu_gpio_softc *sc;
5275e2e692cSMichal Meloun 	struct mvebu_gpio_irqsrc *mgi;
5285e2e692cSMichal Meloun 
5295e2e692cSMichal Meloun 	sc = device_get_softc(dev);
5305e2e692cSMichal Meloun 	mgi = (struct mvebu_gpio_irqsrc *)isrc;
5315e2e692cSMichal Meloun 	if (mgi->is_level)
5325e2e692cSMichal Meloun 		mvebu_gpio_isrc_eoi(sc, mgi);
5335e2e692cSMichal Meloun }
5345e2e692cSMichal Meloun 
5355e2e692cSMichal Meloun static void
mvebu_gpio_pic_post_ithread(device_t dev,struct intr_irqsrc * isrc)5365e2e692cSMichal Meloun mvebu_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
5375e2e692cSMichal Meloun {
5385e2e692cSMichal Meloun 	struct mvebu_gpio_softc *sc;
5395e2e692cSMichal Meloun 	struct mvebu_gpio_irqsrc *mgi;
5405e2e692cSMichal Meloun 
5415e2e692cSMichal Meloun 	sc = device_get_softc(dev);
5425e2e692cSMichal Meloun 	mgi = (struct mvebu_gpio_irqsrc *)isrc;
5435e2e692cSMichal Meloun 	mvebu_gpio_isrc_mask(sc, mgi, 1);
5445e2e692cSMichal Meloun }
5455e2e692cSMichal Meloun 
5465e2e692cSMichal Meloun static void
mvebu_gpio_pic_pre_ithread(device_t dev,struct intr_irqsrc * isrc)5475e2e692cSMichal Meloun mvebu_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
5485e2e692cSMichal Meloun {
5495e2e692cSMichal Meloun 	struct mvebu_gpio_softc *sc;
5505e2e692cSMichal Meloun 	struct mvebu_gpio_irqsrc *mgi;
5515e2e692cSMichal Meloun 
5525e2e692cSMichal Meloun 	sc = device_get_softc(dev);
5535e2e692cSMichal Meloun 	mgi = (struct mvebu_gpio_irqsrc *)isrc;
5545e2e692cSMichal Meloun 
5555e2e692cSMichal Meloun 	mvebu_gpio_isrc_mask(sc, mgi, 0);
5565e2e692cSMichal Meloun 	if (mgi->is_level)
5575e2e692cSMichal Meloun 		mvebu_gpio_isrc_eoi(sc, mgi);
5585e2e692cSMichal Meloun }
5595e2e692cSMichal Meloun 
5605e2e692cSMichal Meloun static int
mvebu_gpio_pic_setup_intr(device_t dev,struct intr_irqsrc * isrc,struct resource * res,struct intr_map_data * data)5615e2e692cSMichal Meloun mvebu_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
5625e2e692cSMichal Meloun     struct resource *res, struct intr_map_data *data)
5635e2e692cSMichal Meloun {
5645e2e692cSMichal Meloun 	u_int irq;
5655e2e692cSMichal Meloun 	bool inverted, level;
5665e2e692cSMichal Meloun 	int rv;
5675e2e692cSMichal Meloun 	struct mvebu_gpio_softc *sc;
5685e2e692cSMichal Meloun 	struct mvebu_gpio_irqsrc *mgi;
5695e2e692cSMichal Meloun 
5705e2e692cSMichal Meloun 	sc = device_get_softc(dev);
5715e2e692cSMichal Meloun 	mgi = (struct mvebu_gpio_irqsrc *)isrc;
5725e2e692cSMichal Meloun 
5735e2e692cSMichal Meloun 	if (data == NULL)
5745e2e692cSMichal Meloun 		return (ENOTSUP);
5755e2e692cSMichal Meloun 
5765e2e692cSMichal Meloun 	/* Get and check config for an interrupt. */
5775e2e692cSMichal Meloun 	if (data->type == INTR_MAP_DATA_FDT) {
5785e2e692cSMichal Meloun 		struct intr_map_data_fdt *daf;
5795e2e692cSMichal Meloun 
5805e2e692cSMichal Meloun 		daf = (struct intr_map_data_fdt *)data;
5815e2e692cSMichal Meloun 		rv = mvebu_gpio_pic_map_fdt(sc, daf->ncells, daf->cells, &irq,
5825e2e692cSMichal Meloun 		    &inverted, &level);
5835e2e692cSMichal Meloun 	} else if (data->type == INTR_MAP_DATA_GPIO) {
5845e2e692cSMichal Meloun 		struct intr_map_data_gpio *dag;
5855e2e692cSMichal Meloun 
5865e2e692cSMichal Meloun 		dag = (struct intr_map_data_gpio *)data;
5875e2e692cSMichal Meloun 		rv = mvebu_gpio_pic_map_gpio(sc, dag->gpio_pin_num,
5885e2e692cSMichal Meloun 		   dag->gpio_pin_flags, dag->gpio_intr_mode, &irq,
5895e2e692cSMichal Meloun 		   &inverted, &level);
5905e2e692cSMichal Meloun 	} else
5915e2e692cSMichal Meloun 		return (ENOTSUP);
5925e2e692cSMichal Meloun 
5935e2e692cSMichal Meloun 	if (rv != 0)
5945e2e692cSMichal Meloun 		return (EINVAL);
5955e2e692cSMichal Meloun 
5965e2e692cSMichal Meloun 	/*
5975e2e692cSMichal Meloun 	 * If this is a setup for another handler,
5985e2e692cSMichal Meloun 	 * only check that its configuration match.
5995e2e692cSMichal Meloun 	 */
6005e2e692cSMichal Meloun 	if (isrc->isrc_handlers != 0)
6015e2e692cSMichal Meloun 		return (
6025e2e692cSMichal Meloun 		    mgi->is_level == level && mgi->is_inverted == inverted ?
6035e2e692cSMichal Meloun 		    0 : EINVAL);
6045e2e692cSMichal Meloun 
6055e2e692cSMichal Meloun 	mgi->is_level = level;
6065e2e692cSMichal Meloun 	mgi->is_inverted = inverted;
607a5dce53bSMichal Meloun 
608a5dce53bSMichal Meloun 	GPIO_LOCK(sc);
609a5dce53bSMichal Meloun 	intr_modify(sc, GPIO_DATA_IN_POL, mgi, inverted ? 1 : 0);
6105e2e692cSMichal Meloun 	mvebu_gpio_pic_enable_intr(dev, isrc);
611a5dce53bSMichal Meloun 	GPIO_UNLOCK(sc);
6125e2e692cSMichal Meloun 
6135e2e692cSMichal Meloun 	return (0);
6145e2e692cSMichal Meloun }
6155e2e692cSMichal Meloun 
6165e2e692cSMichal Meloun static int
mvebu_gpio_pic_teardown_intr(device_t dev,struct intr_irqsrc * isrc,struct resource * res,struct intr_map_data * data)6175e2e692cSMichal Meloun mvebu_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
6185e2e692cSMichal Meloun     struct resource *res, struct intr_map_data *data)
6195e2e692cSMichal Meloun {
6205e2e692cSMichal Meloun 	struct mvebu_gpio_softc *sc;
6215e2e692cSMichal Meloun 	struct mvebu_gpio_irqsrc *mgi;
6225e2e692cSMichal Meloun 
6235e2e692cSMichal Meloun 	sc = device_get_softc(dev);
6245e2e692cSMichal Meloun 	mgi = (struct mvebu_gpio_irqsrc *)isrc;
6255e2e692cSMichal Meloun 
6265e2e692cSMichal Meloun 	if (isrc->isrc_handlers == 0)
6275e2e692cSMichal Meloun 		mvebu_gpio_isrc_mask(sc, mgi, 0);
6285e2e692cSMichal Meloun 	return (0);
6295e2e692cSMichal Meloun }
6305e2e692cSMichal Meloun 
6315e2e692cSMichal Meloun /* --------------------------------------------------------------------------
6325e2e692cSMichal Meloun  *
6335e2e692cSMichal Meloun  * Bus
6345e2e692cSMichal Meloun  *
6355e2e692cSMichal Meloun  */
6365e2e692cSMichal Meloun 
6375e2e692cSMichal Meloun static int
mvebu_gpio_intr(void * arg)6385e2e692cSMichal Meloun mvebu_gpio_intr(void *arg)
6395e2e692cSMichal Meloun {
6405e2e692cSMichal Meloun 	u_int i, lvl, edge;
6415e2e692cSMichal Meloun 	struct mvebu_gpio_softc *sc;
6425e2e692cSMichal Meloun 	struct trapframe *tf;
6435e2e692cSMichal Meloun 	struct mvebu_gpio_irqsrc *mgi;
6445e2e692cSMichal Meloun 	struct mvebu_gpio_irq_cookie *cookie;
6455e2e692cSMichal Meloun 
6465e2e692cSMichal Meloun 	cookie = (struct mvebu_gpio_irq_cookie *)arg;
6475e2e692cSMichal Meloun 	sc = cookie->sc;
6485e2e692cSMichal Meloun 	tf = curthread->td_intr_frame;
6495e2e692cSMichal Meloun 
6505e2e692cSMichal Meloun 	for (i = 0; i < sc->gpio_npins; i++) {
6515e2e692cSMichal Meloun 		lvl = gpio_read(sc, GPIO_DATA_IN, &sc->gpio_pins[i]);
6525e2e692cSMichal Meloun 		lvl &= gpio_read(sc, GPIO_INT_LEVEL_MASK, &sc->gpio_pins[i]);
6535e2e692cSMichal Meloun 		edge = gpio_read(sc, GPIO_DATA_IN, &sc->gpio_pins[i]);
6545e2e692cSMichal Meloun 		edge &= gpio_read(sc, GPIO_INT_LEVEL_MASK, &sc->gpio_pins[i]);
655a5dce53bSMichal Meloun 		if (edge == 0  && lvl == 0)
6565e2e692cSMichal Meloun 			continue;
6575e2e692cSMichal Meloun 
6585e2e692cSMichal Meloun 		mgi = &sc->isrcs[i];
6595e2e692cSMichal Meloun 		if (!mgi->is_level)
6605e2e692cSMichal Meloun 			mvebu_gpio_isrc_eoi(sc, mgi);
661a5dce53bSMichal Meloun 
6625e2e692cSMichal Meloun 		if (intr_isrc_dispatch(&mgi->isrc, tf) != 0) {
6635e2e692cSMichal Meloun 			mvebu_gpio_isrc_mask(sc, mgi, 0);
6645e2e692cSMichal Meloun 			if (mgi->is_level)
6655e2e692cSMichal Meloun 				mvebu_gpio_isrc_eoi(sc, mgi);
6665e2e692cSMichal Meloun 			device_printf(sc->dev,
6675e2e692cSMichal Meloun 			    "Stray irq %u disabled\n", mgi->irq);
6685e2e692cSMichal Meloun 		}
6695e2e692cSMichal Meloun 	}
6705e2e692cSMichal Meloun 	return (FILTER_HANDLED);
6715e2e692cSMichal Meloun }
6725e2e692cSMichal Meloun 
6735e2e692cSMichal Meloun static int
mvebu_gpio_probe(device_t dev)6745e2e692cSMichal Meloun mvebu_gpio_probe(device_t dev)
6755e2e692cSMichal Meloun {
6765e2e692cSMichal Meloun 
6775e2e692cSMichal Meloun 	if (!ofw_bus_status_okay(dev))
6785e2e692cSMichal Meloun 		return (ENXIO);
6795e2e692cSMichal Meloun 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
6805e2e692cSMichal Meloun 		return (ENXIO);
6815e2e692cSMichal Meloun 
6825e2e692cSMichal Meloun 	device_set_desc(dev, "Marvell Integrated GPIO Controller");
6835e2e692cSMichal Meloun 	return (0);
6845e2e692cSMichal Meloun }
6855e2e692cSMichal Meloun 
6865e2e692cSMichal Meloun static int
mvebu_gpio_detach(device_t dev)6875e2e692cSMichal Meloun mvebu_gpio_detach(device_t dev)
6885e2e692cSMichal Meloun {
6895e2e692cSMichal Meloun 	struct mvebu_gpio_softc *sc;
6905e2e692cSMichal Meloun 	int i;
6915e2e692cSMichal Meloun 
6925e2e692cSMichal Meloun 	sc = device_get_softc(dev);
6935e2e692cSMichal Meloun 
6945e2e692cSMichal Meloun 	KASSERT(mtx_initialized(&sc->mtx), ("gpio mutex not initialized"));
6955e2e692cSMichal Meloun 
6965e2e692cSMichal Meloun 	for (i = 0; i < MV_GPIO_MAX_NIRQS; i++) {
6975e2e692cSMichal Meloun 		if (sc->irq_ih[i] != NULL)
6985e2e692cSMichal Meloun 			bus_teardown_intr(dev, sc->irq_res[i], sc->irq_ih[i]);
6995e2e692cSMichal Meloun 	}
7005e2e692cSMichal Meloun 
7015e2e692cSMichal Meloun 	if (sc->isrcs != NULL)
7025e2e692cSMichal Meloun 		mvebu_gpio_pic_detach(sc);
7035e2e692cSMichal Meloun 
7045e2e692cSMichal Meloun 	gpiobus_detach_bus(dev);
7055e2e692cSMichal Meloun 
7065e2e692cSMichal Meloun 	for (i = 0; i < MV_GPIO_MAX_NIRQS; i++) {
7075e2e692cSMichal Meloun 		if (sc->irq_res[i] != NULL)
7085e2e692cSMichal Meloun 			bus_release_resource(dev, SYS_RES_IRQ, 0,
7095e2e692cSMichal Meloun 			     sc->irq_res[i]);
7105e2e692cSMichal Meloun 	}
7115e2e692cSMichal Meloun 	GPIO_LOCK_DESTROY(sc);
7125e2e692cSMichal Meloun 
7135e2e692cSMichal Meloun 	return(0);
7145e2e692cSMichal Meloun }
7155e2e692cSMichal Meloun 
7165e2e692cSMichal Meloun static int
mvebu_gpio_attach(device_t dev)7175e2e692cSMichal Meloun mvebu_gpio_attach(device_t dev)
7185e2e692cSMichal Meloun {
7195e2e692cSMichal Meloun 	struct mvebu_gpio_softc *sc;
7205e2e692cSMichal Meloun 	phandle_t node;
7215e2e692cSMichal Meloun 	struct gpio_pin *pin;
7225e2e692cSMichal Meloun 	pcell_t pincnt;
7235e2e692cSMichal Meloun 	int i, rv, rid;
7245e2e692cSMichal Meloun 
7255e2e692cSMichal Meloun 	sc = device_get_softc(dev);
7265e2e692cSMichal Meloun 	sc->dev = dev;
7275e2e692cSMichal Meloun 	node = ofw_bus_get_node(dev);
7285e2e692cSMichal Meloun 
7295e2e692cSMichal Meloun 	GPIO_LOCK_INIT(sc);
7305e2e692cSMichal Meloun 
7315e2e692cSMichal Meloun 	pincnt = 0;
7325e2e692cSMichal Meloun 	rv = OF_getencprop(node, "ngpios", &pincnt, sizeof(pcell_t));
7335e2e692cSMichal Meloun 	if (rv < 0) {
7345e2e692cSMichal Meloun 		device_printf(dev,
7355e2e692cSMichal Meloun 		    "ERROR: no pin-count or ngpios entry found!\n");
7365e2e692cSMichal Meloun 		return (ENXIO);
7375e2e692cSMichal Meloun 	}
7385e2e692cSMichal Meloun 
7395e2e692cSMichal Meloun 	sc->gpio_npins = MIN(pincnt, MV_GPIO_MAX_NPINS);
7405e2e692cSMichal Meloun 	if (bootverbose)
7415e2e692cSMichal Meloun 		device_printf(dev,
7425e2e692cSMichal Meloun 		    "%d pins available\n", sc->gpio_npins);
7435e2e692cSMichal Meloun 
7445e2e692cSMichal Meloun 	rv = OF_getencprop(node, "offset", &sc->offset, sizeof(sc->offset));
7455e2e692cSMichal Meloun 	if (rv == -1) {
7465e2e692cSMichal Meloun 		device_printf(dev, "ERROR: no 'offset' property found!\n");
7475e2e692cSMichal Meloun 		return (ENXIO);
7485e2e692cSMichal Meloun 	}
7495e2e692cSMichal Meloun 
7505e2e692cSMichal Meloun 	if (SYSCON_GET_HANDLE(sc->dev, &sc->syscon) != 0 ||
7515e2e692cSMichal Meloun 	    sc->syscon == NULL) {
7525e2e692cSMichal Meloun 		device_printf(dev, "ERROR: cannot get syscon handle!\n");
7535e2e692cSMichal Meloun 		return (ENXIO);
7545e2e692cSMichal Meloun 	}
7555e2e692cSMichal Meloun 
7565e2e692cSMichal Meloun 	/* Allocate interrupts. */
7575e2e692cSMichal Meloun 	for (i = 0; i < MV_GPIO_MAX_NIRQS; i++) {
7585e2e692cSMichal Meloun 		sc->irq_cookies[i].sc = sc;
7595e2e692cSMichal Meloun 		sc->irq_cookies[i].bank_num = i;
7605e2e692cSMichal Meloun 		rid = i;
7615e2e692cSMichal Meloun 		sc->irq_res[i] = bus_alloc_resource_any(dev, SYS_RES_IRQ,
7625e2e692cSMichal Meloun 		    &rid, RF_ACTIVE);
7635e2e692cSMichal Meloun 		if (sc->irq_res[i] == NULL)
7645e2e692cSMichal Meloun 			break;
7655e2e692cSMichal Meloun 		if ((bus_setup_intr(dev, sc->irq_res[i],
7665e2e692cSMichal Meloun 		    INTR_TYPE_MISC | INTR_MPSAFE, mvebu_gpio_intr, NULL,
7675e2e692cSMichal Meloun 		    &sc->irq_cookies[i], &sc->irq_ih[i]))) {
7685e2e692cSMichal Meloun 			device_printf(dev,
7695e2e692cSMichal Meloun 			    "WARNING: unable to register interrupt handler\n");
7705e2e692cSMichal Meloun 			mvebu_gpio_detach(dev);
7715e2e692cSMichal Meloun 			return (ENXIO);
7725e2e692cSMichal Meloun 		}
7735e2e692cSMichal Meloun 	}
7745e2e692cSMichal Meloun 
7755e2e692cSMichal Meloun 	/* Init GPIO pins */
7765e2e692cSMichal Meloun 	for (i = 0; i < sc->gpio_npins; i++) {
7775e2e692cSMichal Meloun 		pin = sc->gpio_pins + i;
7785e2e692cSMichal Meloun 		pin->gp_pin = i;
7795e2e692cSMichal Meloun 		if (sc->irq_res[0] != NULL)
7805e2e692cSMichal Meloun 			pin->gp_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
7815e2e692cSMichal Meloun 			    GPIO_INTR_LEVEL_LOW | GPIO_INTR_LEVEL_HIGH |
7825e2e692cSMichal Meloun 			    GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING;
7835e2e692cSMichal Meloun 		else
7845e2e692cSMichal Meloun 			pin->gp_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
7855e2e692cSMichal Meloun 		pin->gp_flags =
78601c6d791SMichal Meloun 		    gpio_read(sc, GPIO_CONTROL, &sc->gpio_pins[i]) == 0 ?
7875e2e692cSMichal Meloun 		    GPIO_PIN_OUTPUT : GPIO_PIN_INPUT;
7885e2e692cSMichal Meloun 		snprintf(pin->gp_name, GPIOMAXNAME, "gpio%d", i);
7895e2e692cSMichal Meloun 
7905e2e692cSMichal Meloun 		/* Init HW */
791a5dce53bSMichal Meloun 		gpio_modify(sc, GPIO_INT_MASK, pin, 0);
792a5dce53bSMichal Meloun 		gpio_modify(sc, GPIO_INT_LEVEL_MASK, pin, 0);
793a5dce53bSMichal Meloun 		gpio_modify(sc, GPIO_INT_CAUSE, pin, 0);
794a5dce53bSMichal Meloun 		gpio_modify(sc, GPIO_DATA_IN_POL, pin, 0);
795a5dce53bSMichal Meloun 		gpio_modify(sc, GPIO_BLINK_ENA, pin, 0);
7965e2e692cSMichal Meloun 	}
7975e2e692cSMichal Meloun 
7985e2e692cSMichal Meloun 	if (sc->irq_res[0] != NULL) {
7995e2e692cSMichal Meloun 		rv = mvebu_gpio_pic_attach(sc);
8005e2e692cSMichal Meloun 		if (rv != 0) {
8015e2e692cSMichal Meloun 			device_printf(dev, "WARNING: unable to attach PIC\n");
8025e2e692cSMichal Meloun 			mvebu_gpio_detach(dev);
8035e2e692cSMichal Meloun 			return (rv);
8045e2e692cSMichal Meloun 		}
8055e2e692cSMichal Meloun 	}
8065e2e692cSMichal Meloun 
8075e2e692cSMichal Meloun 	sc->busdev = gpiobus_attach_bus(dev);
8085e2e692cSMichal Meloun 	if (sc->busdev == NULL) {
8095e2e692cSMichal Meloun 		mvebu_gpio_detach(dev);
8105e2e692cSMichal Meloun 		return (ENXIO);
8115e2e692cSMichal Meloun 	}
8125e2e692cSMichal Meloun 
8135e2e692cSMichal Meloun 	return (bus_generic_attach(dev));
8145e2e692cSMichal Meloun }
8155e2e692cSMichal Meloun 
8165e2e692cSMichal Meloun static int
mvebu_gpio_map_gpios(device_t dev,phandle_t pdev,phandle_t gparent,int gcells,pcell_t * gpios,uint32_t * pin,uint32_t * flags)8175e2e692cSMichal Meloun mvebu_gpio_map_gpios(device_t dev, phandle_t pdev, phandle_t gparent,
8185e2e692cSMichal Meloun     int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags)
8195e2e692cSMichal Meloun {
8205e2e692cSMichal Meloun 
8215e2e692cSMichal Meloun 	if (gcells != 2)
8225e2e692cSMichal Meloun 		return (ERANGE);
8235e2e692cSMichal Meloun 	*pin = gpios[0];
8245e2e692cSMichal Meloun 	*flags= gpios[1];
8255e2e692cSMichal Meloun 	return (0);
8265e2e692cSMichal Meloun }
8275e2e692cSMichal Meloun 
8285e2e692cSMichal Meloun static phandle_t
mvebu_gpio_get_node(device_t bus,device_t dev)8295e2e692cSMichal Meloun mvebu_gpio_get_node(device_t bus, device_t dev)
8305e2e692cSMichal Meloun {
8315e2e692cSMichal Meloun 
8325e2e692cSMichal Meloun 	/* We only have one child, the GPIO bus, which needs our own node. */
8335e2e692cSMichal Meloun 	return (ofw_bus_get_node(bus));
8345e2e692cSMichal Meloun }
8355e2e692cSMichal Meloun 
8365e2e692cSMichal Meloun static device_method_t mvebu_gpio_methods[] = {
8375e2e692cSMichal Meloun 	DEVMETHOD(device_probe,		mvebu_gpio_probe),
8385e2e692cSMichal Meloun 	DEVMETHOD(device_attach,	mvebu_gpio_attach),
8395e2e692cSMichal Meloun 	DEVMETHOD(device_detach,	mvebu_gpio_detach),
8405e2e692cSMichal Meloun 
8415e2e692cSMichal Meloun 	/* Interrupt controller interface */
8425e2e692cSMichal Meloun 	DEVMETHOD(pic_disable_intr,	mvebu_gpio_pic_disable_intr),
8435e2e692cSMichal Meloun 	DEVMETHOD(pic_enable_intr,	mvebu_gpio_pic_enable_intr),
8445e2e692cSMichal Meloun 	DEVMETHOD(pic_map_intr,		mvebu_gpio_pic_map_intr),
8455e2e692cSMichal Meloun 	DEVMETHOD(pic_setup_intr,	mvebu_gpio_pic_setup_intr),
8465e2e692cSMichal Meloun 	DEVMETHOD(pic_teardown_intr,	mvebu_gpio_pic_teardown_intr),
8475e2e692cSMichal Meloun 	DEVMETHOD(pic_post_filter,	mvebu_gpio_pic_post_filter),
8485e2e692cSMichal Meloun 	DEVMETHOD(pic_post_ithread,	mvebu_gpio_pic_post_ithread),
8495e2e692cSMichal Meloun 	DEVMETHOD(pic_pre_ithread,	mvebu_gpio_pic_pre_ithread),
8505e2e692cSMichal Meloun 
8515e2e692cSMichal Meloun 	/* GPIO protocol */
8525e2e692cSMichal Meloun 	DEVMETHOD(gpio_get_bus,		mvebu_gpio_get_bus),
8535e2e692cSMichal Meloun 	DEVMETHOD(gpio_pin_max,		mvebu_gpio_pin_max),
8545e2e692cSMichal Meloun 	DEVMETHOD(gpio_pin_getname,	mvebu_gpio_pin_getname),
8555e2e692cSMichal Meloun 	DEVMETHOD(gpio_pin_getflags,	mvebu_gpio_pin_getflags),
8565e2e692cSMichal Meloun 	DEVMETHOD(gpio_pin_getcaps,	mvebu_gpio_pin_getcaps),
8575e2e692cSMichal Meloun 	DEVMETHOD(gpio_pin_setflags,	mvebu_gpio_pin_setflags),
8585e2e692cSMichal Meloun 	DEVMETHOD(gpio_pin_get,		mvebu_gpio_pin_get),
8595e2e692cSMichal Meloun 	DEVMETHOD(gpio_pin_set,		mvebu_gpio_pin_set),
8605e2e692cSMichal Meloun 	DEVMETHOD(gpio_pin_toggle,	mvebu_gpio_pin_toggle),
8615e2e692cSMichal Meloun 	DEVMETHOD(gpio_map_gpios,	mvebu_gpio_map_gpios),
8625e2e692cSMichal Meloun 
8635e2e692cSMichal Meloun 	/* ofw_bus interface */
8645e2e692cSMichal Meloun 	DEVMETHOD(ofw_bus_get_node,	mvebu_gpio_get_node),
8655e2e692cSMichal Meloun 
8665e2e692cSMichal Meloun 	DEVMETHOD_END
8675e2e692cSMichal Meloun };
8685e2e692cSMichal Meloun 
8695e2e692cSMichal Meloun static DEFINE_CLASS_0(gpio, mvebu_gpio_driver, mvebu_gpio_methods,
8705e2e692cSMichal Meloun     sizeof(struct mvebu_gpio_softc));
871a3b866cbSJohn Baldwin EARLY_DRIVER_MODULE(mvebu_gpio, simplebus, mvebu_gpio_driver, NULL, NULL,
8725e2e692cSMichal Meloun      BUS_PASS_TIMER + BUS_PASS_ORDER_LAST);
873