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
46*62e8ccc3SEmmanuel 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