Author: mmel
Date: Sat Oct 31 16:03:35 2020
New Revision: 367216
URL: https://svnweb.freebsd.org/changeset/base/367216

Log:
  MFC r362384,r362385,r362386,r362392:
  
    r362384:
      Add DTB files for ARMADA 8040 based boards.
    r362385:
      Add specialized gpio driver for ARMADA 8k SoC.  Older marvell gpio blocks
      are to different for reusing/enhancing existing frivers.
    r362386:
      Add specific stub for ARMADA 8k SoC to Marvell RTC driver.  The AXI bridge
      is different between ARMADA 38x and 8K, and both platforms needs specific
      setup to mitigate HW issues with accessing RTC registers.
    r362392:
      Adapt ARMADA8k PCIe driver to newly imported 5.7 DT.  - temporarily 
disable
      handling with phy, we don't have driver for it yet - always clear cause 
for
      administartive interrupt.  While I'm in, fix style(9) (mainly whitespace).

Added:
  stable/12/sys/arm/mv/mvebu_gpio.c
     - copied unchanged from r362385, head/sys/arm/mv/mvebu_gpio.c
Modified:
  stable/12/sys/arm/mv/armada38x/armada38x_rtc.c
  stable/12/sys/arm/mv/gpio.c
  stable/12/sys/conf/files.arm64
  stable/12/sys/dev/pci/pci_dw_mv.c
  stable/12/sys/modules/dtb/mv/Makefile
Directory Properties:
  stable/12/   (props changed)

Modified: stable/12/sys/arm/mv/armada38x/armada38x_rtc.c
==============================================================================
--- stable/12/sys/arm/mv/armada38x/armada38x_rtc.c      Sat Oct 31 15:58:05 
2020        (r367215)
+++ stable/12/sys/arm/mv/armada38x/armada38x_rtc.c      Sat Oct 31 16:03:35 
2020        (r367216)
@@ -72,14 +72,27 @@ __FBSDID("$FreeBSD$");
 #define        MV_RTC_LOCK(sc)         mtx_lock_spin(&(sc)->mutex)
 #define        MV_RTC_UNLOCK(sc)       mtx_unlock_spin(&(sc)->mutex)
 
-#define        RTC_BRIDGE_TIMING_CTRL          0x0
-#define        RTC_WRCLK_PERIOD_SHIFT                  0
-#define        RTC_WRCLK_PERIOD_MASK                   0x00000003FF
-#define        RTC_WRCLK_PERIOD_MAX                    0x3FF
-#define        RTC_READ_OUTPUT_DELAY_SHIFT             26
-#define        RTC_READ_OUTPUT_DELAY_MASK              0x007C000000
-#define        RTC_READ_OUTPUT_DELAY_MAX               0x1F
+#define        A38X_RTC_BRIDGE_TIMING_CTRL             0x0
+#define        A38X_RTC_WRCLK_PERIOD_SHIFT             0
+#define        A38X_RTC_WRCLK_PERIOD_MASK              0x00000003FF
+#define        A38X_RTC_WRCLK_PERIOD_MAX               0x3FF
+#define        A38X_RTC_READ_OUTPUT_DELAY_SHIFT        26
+#define        A38X_RTC_READ_OUTPUT_DELAY_MASK         0x007C000000
+#define        A38X_RTC_READ_OUTPUT_DELAY_MAX          0x1F
 
+#define        A8K_RTC_BRIDGE_TIMING_CTRL0             0x0
+#define        A8K_RTC_WRCLK_PERIOD_SHIFT              0
+#define        A8K_RTC_WRCLK_PERIOD_MASK               0x000000FFFF
+#define        A8K_RTC_WRCLK_PERIOD_VAL                0x3FF
+#define        A8K_RTC_WRCLK_SETUP_SHIFT               16
+#define        A8K_RTC_WRCLK_SETUP_MASK                0x00FFFF0000
+#define        A8K_RTC_WRCLK_SETUP_VAL                 29
+#define        A8K_RTC_BRIDGE_TIMING_CTRL1             0x4
+#define        A8K_RTC_READ_OUTPUT_DELAY_SHIFT         0
+#define        A8K_RTC_READ_OUTPUT_DELAY_MASK          0x000000FFFF
+#define        A8K_RTC_READ_OUTPUT_DELAY_VAL           0x3F
+
+
 #define        RTC_RES         0
 #define        RTC_SOC_RES     1
 
@@ -94,6 +107,7 @@ struct mv_rtc_softc {
        device_t        dev;
        struct resource *res[2];
        struct mtx      mutex;
+       int             rtc_type;
 };
 
 static int mv_rtc_probe(device_t dev);
@@ -107,7 +121,8 @@ static inline uint32_t mv_rtc_reg_read(struct mv_rtc_s
     bus_size_t off);
 static inline int mv_rtc_reg_write(struct mv_rtc_softc *sc, bus_size_t off,
     uint32_t val);
-static inline void mv_rtc_configure_bus(struct mv_rtc_softc *sc);
+static inline void mv_rtc_configure_bus_a38x(struct mv_rtc_softc *sc);
+static inline void mv_rtc_configure_bus_a8k(struct mv_rtc_softc *sc);
 
 static device_method_t mv_rtc_methods[] = {
        DEVMETHOD(device_probe,         mv_rtc_probe),
@@ -126,10 +141,13 @@ static driver_t mv_rtc_driver = {
        sizeof(struct mv_rtc_softc),
 };
 
+#define  RTC_A38X      1
+#define  RTC_A8K       2
+
 static struct ofw_compat_data mv_rtc_compat[] = {
-       {"marvell,armada-380-rtc",      true},
-       {"marvell,armada-8k-rtc",       true},
-       {NULL,                          false},
+       {"marvell,armada-380-rtc",      RTC_A38X},
+       {"marvell,armada-8k-rtc",       RTC_A8K},
+       {NULL,                          0},
 };
 
 static devclass_t mv_rtc_devclass;
@@ -198,20 +216,29 @@ mv_rtc_attach(device_t dev)
 
        sc = device_get_softc(dev);
        sc->dev = dev;
+       sc->rtc_type = ofw_bus_search_compatible(dev, mv_rtc_compat)->ocd_data;
 
-       clock_register(dev, RTC_RES_US);
-
        mtx_init(&sc->mutex, device_get_nameunit(dev), NULL, MTX_SPIN);
 
        ret = bus_alloc_resources(dev, res_spec, sc->res);
-
        if (ret != 0) {
                device_printf(dev, "could not allocate resources\n");
                mtx_destroy(&sc->mutex);
                return (ENXIO);
        }
-       mv_rtc_configure_bus(sc);
 
+       switch (sc->rtc_type) {
+       case RTC_A38X:
+               mv_rtc_configure_bus_a38x(sc);
+               break;
+       case RTC_A8K:
+               mv_rtc_configure_bus_a8k(sc);
+               break;
+       default:
+               panic("Unknown RTC type: %d", sc->rtc_type);
+       }
+       clock_register(dev, RTC_RES_US);
+
        return (0);
 }
 
@@ -239,13 +266,15 @@ mv_rtc_gettime(device_t dev, struct timespec *ts)
 
        MV_RTC_LOCK(sc);
        /*
-        * According to HW Errata if more than one second between
-        * two time reads is detected, then read once again
+        * According to HW Errata, if more than one second is detected
+        * between two time reads, then at least one of the reads gave
+        * an invalid value.
         */
-       val = mv_rtc_reg_read(sc, RTC_TIME);
-       val_check = mv_rtc_reg_read(sc, RTC_TIME);
-       if (val_check - val > 1)
+       do {
+               val = mv_rtc_reg_read(sc, RTC_TIME);
+               DELAY(100);
                val_check = mv_rtc_reg_read(sc, RTC_TIME);
+       } while ((val_check - val) > 1);
 
        MV_RTC_UNLOCK(sc);
 
@@ -284,7 +313,6 @@ mv_rtc_settime(device_t dev, struct timespec *ts)
        mv_rtc_reg_write(sc, RTC_STATUS, 0x0);
        mv_rtc_reg_write(sc, RTC_STATUS, 0x0);
        mv_rtc_reg_write(sc, RTC_TIME, ts->tv_sec);
-
        MV_RTC_UNLOCK(sc);
 
        return (0);
@@ -313,13 +341,30 @@ mv_rtc_reg_write(struct mv_rtc_softc *sc, bus_size_t o
 }
 
 static inline void
-mv_rtc_configure_bus(struct mv_rtc_softc *sc)
+mv_rtc_configure_bus_a38x(struct mv_rtc_softc *sc)
 {
        int val;
 
-       val = bus_read_4(sc->res[RTC_SOC_RES], RTC_BRIDGE_TIMING_CTRL);
-       val &= ~(RTC_WRCLK_PERIOD_MASK | RTC_READ_OUTPUT_DELAY_MASK);
-       val |= RTC_WRCLK_PERIOD_MAX << RTC_WRCLK_PERIOD_SHIFT;
-       val |= RTC_READ_OUTPUT_DELAY_MAX << RTC_READ_OUTPUT_DELAY_SHIFT;
-       bus_write_4(sc->res[RTC_SOC_RES], RTC_BRIDGE_TIMING_CTRL, val);
+       val = bus_read_4(sc->res[RTC_SOC_RES], A38X_RTC_BRIDGE_TIMING_CTRL);
+       val &= ~(A38X_RTC_WRCLK_PERIOD_MASK | A38X_RTC_READ_OUTPUT_DELAY_MASK);
+       val |= A38X_RTC_WRCLK_PERIOD_MAX << A38X_RTC_WRCLK_PERIOD_SHIFT;
+       val |= A38X_RTC_READ_OUTPUT_DELAY_MAX << 
A38X_RTC_READ_OUTPUT_DELAY_SHIFT;
+       bus_write_4(sc->res[RTC_SOC_RES], A38X_RTC_BRIDGE_TIMING_CTRL, val);
+}
+
+static inline void
+mv_rtc_configure_bus_a8k(struct mv_rtc_softc *sc)
+{
+       int val;
+
+       val = bus_read_4(sc->res[RTC_SOC_RES], A8K_RTC_BRIDGE_TIMING_CTRL0);
+       val &= ~(A8K_RTC_WRCLK_PERIOD_MASK | A8K_RTC_WRCLK_SETUP_MASK);
+       val |= A8K_RTC_WRCLK_PERIOD_VAL << A8K_RTC_WRCLK_PERIOD_SHIFT;
+       val |= A8K_RTC_WRCLK_SETUP_VAL << A8K_RTC_WRCLK_SETUP_SHIFT;
+       bus_write_4(sc->res[RTC_SOC_RES], A8K_RTC_BRIDGE_TIMING_CTRL1, val);
+
+       val = bus_read_4(sc->res[RTC_SOC_RES], A8K_RTC_BRIDGE_TIMING_CTRL0);
+       val &= ~A8K_RTC_READ_OUTPUT_DELAY_MASK;
+       val |= A8K_RTC_READ_OUTPUT_DELAY_VAL << A8K_RTC_READ_OUTPUT_DELAY_SHIFT;
+       bus_write_4(sc->res[RTC_SOC_RES], A8K_RTC_BRIDGE_TIMING_CTRL1, val);
 }

Modified: stable/12/sys/arm/mv/gpio.c
==============================================================================
--- stable/12/sys/arm/mv/gpio.c Sat Oct 31 15:58:05 2020        (r367215)
+++ stable/12/sys/arm/mv/gpio.c Sat Oct 31 16:03:35 2020        (r367216)
@@ -60,9 +60,6 @@ __FBSDID("$FreeBSD$");
 
 #include "gpio_if.h"
 
-#ifdef __aarch64__
-#include "opt_soc.h"
-#endif
 
 #define GPIO_MAX_INTR_COUNT    8
 #define GPIO_PINS_PER_REG      32
@@ -199,9 +196,6 @@ EARLY_DRIVER_MODULE(mv_gpio, simplebus, mv_gpio_driver
 struct ofw_compat_data compat_data[] = {
        { "mrvl,gpio", 1 },
        { "marvell,orion-gpio", 1 },
-#ifdef SOC_MARVELL_8K
-       { "marvell,armada-8k-gpio", 1 },
-#endif
        { NULL, 0 }
 };
 

Copied: stable/12/sys/arm/mv/mvebu_gpio.c (from r362385, 
head/sys/arm/mv/mvebu_gpio.c)
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ stable/12/sys/arm/mv/mvebu_gpio.c   Sat Oct 31 16:03:35 2020        
(r367216, copy of r362385, head/sys/arm/mv/mvebu_gpio.c)
@@ -0,0 +1,869 @@
+/*-
+ * Copyright (c) 2020 Michal Meloun <m...@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * ARMADA 8040 GPIO driver.
+ */
+#include "opt_platform.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/resource.h>
+
+#include <dev/extres/syscon/syscon.h>
+
+#include <dev/gpio/gpiobusvar.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "pic_if.h"
+#include "syscon_if.h"
+
+#define        GPIO_LOCK(_sc)          mtx_lock(&(_sc)->mtx)
+#define        GPIO_UNLOCK(_sc)        mtx_unlock(&(_sc)->mtx)
+#define        GPIO_LOCK_INIT(_sc)     mtx_init(&_sc->mtx,                     
\
+           device_get_nameunit(_sc->dev), "mvebu_gpio", MTX_DEF)
+#define        GPIO_LOCK_DESTROY(_sc)  mtx_destroy(&_sc->mtx);
+#define        GPIO_ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED);
+#define        GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED);
+
+#define        GPIO_DATA_OUT           0x00
+#define        GPIO_CONTROL            0x04
+#define        GPIO_BLINK_ENA          0x08
+#define        GPIO_DATA_IN_POL        0x0C
+#define        GPIO_DATA_IN            0x10
+#define        GPIO_INT_CAUSE          0x14
+#define        GPIO_INT_MASK           0x18
+#define        GPIO_INT_LEVEL_MASK     0x1C
+#define        GPIO_CONTROL_SET        0x28
+#define        GPIO_CONTROL_CLR        0x2C
+#define        GPIO_DATA_SET           0x30
+#define        GPIO_DATA_CLR           0x34
+
+#define        GPIO_BIT(_p)            ((_p) % 32)
+#define        GPIO_REGNUM(_p)         ((_p) / 32)
+
+#define        MV_GPIO_MAX_NIRQS       4
+#define        MV_GPIO_MAX_NPINS       32
+
+#define        RD4(sc, reg)            SYSCON_READ_4((sc)->syscon, (reg))
+#define        WR4(sc, reg, val)       SYSCON_WRITE_4((sc)->syscon, (reg), 
(val))
+
+struct mvebu_gpio_irqsrc {
+       struct intr_irqsrc      isrc;
+       u_int                   irq;
+       bool                    is_level;
+       bool                    is_inverted;
+};
+
+struct mvebu_gpio_softc;
+struct mvebu_gpio_irq_cookie {
+       struct mvebu_gpio_softc *sc;
+       int                     bank_num;
+};
+
+struct mvebu_gpio_softc {
+       device_t                dev;
+       device_t                busdev;
+       struct mtx              mtx;
+       struct syscon           *syscon;
+       uint32_t                offset;
+       struct resource         *irq_res[MV_GPIO_MAX_NIRQS];
+       void                    *irq_ih[MV_GPIO_MAX_NIRQS];
+       struct mvebu_gpio_irq_cookie irq_cookies[MV_GPIO_MAX_NIRQS];
+       int                     gpio_npins;
+       struct gpio_pin         gpio_pins[MV_GPIO_MAX_NPINS];
+       struct mvebu_gpio_irqsrc *isrcs;
+};
+
+static struct ofw_compat_data compat_data[] = {
+       {"marvell,armada-8k-gpio", 1},
+       {NULL, 0}
+};
+
+/* --------------------------------------------------------------------------
+ *
+ * GPIO
+ *
+ */
+static inline void
+gpio_write(struct mvebu_gpio_softc *sc, bus_size_t reg,
+    struct gpio_pin *pin, uint32_t val)
+{
+       uint32_t tmp;
+       int bit;
+
+       bit = GPIO_BIT(pin->gp_pin);
+       tmp = 0x100 << bit;             /* mask */
+       tmp |= (val & 1) << bit;        /* value */
+       SYSCON_WRITE_4(sc->syscon, sc->offset + GPIO_REGNUM(pin->gp_pin) + reg,
+           tmp);
+}
+
+static inline uint32_t
+gpio_read(struct mvebu_gpio_softc *sc, bus_size_t reg, struct gpio_pin *pin)
+{
+       int bit;
+       uint32_t val;
+
+       bit = GPIO_BIT(pin->gp_pin);
+       val = SYSCON_READ_4(sc->syscon,
+           sc->offset + GPIO_REGNUM(pin->gp_pin) + reg);
+       return (val >> bit) & 1;
+}
+
+static void
+mvebu_gpio_pin_configure(struct mvebu_gpio_softc *sc, struct gpio_pin *pin,
+    unsigned int flags)
+{
+
+       if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) == 0)
+               return;
+
+       /* Manage input/output */
+       pin->gp_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
+       if (flags & GPIO_PIN_OUTPUT) {
+               pin->gp_flags |= GPIO_PIN_OUTPUT;
+               gpio_write(sc, GPIO_CONTROL_SET, pin, 1);
+       } else {
+               pin->gp_flags |= GPIO_PIN_INPUT;
+               gpio_write(sc, GPIO_CONTROL_CLR, pin, 1);
+       }
+}
+
+static device_t
+mvebu_gpio_get_bus(device_t dev)
+{
+       struct mvebu_gpio_softc *sc;
+
+       sc = device_get_softc(dev);
+       return (sc->busdev);
+}
+
+static int
+mvebu_gpio_pin_max(device_t dev, int *maxpin)
+{
+       struct mvebu_gpio_softc *sc;
+
+       sc = device_get_softc(dev);
+       *maxpin = sc->gpio_npins - 1;
+       return (0);
+}
+
+static int
+mvebu_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+       struct mvebu_gpio_softc *sc;
+
+       sc = device_get_softc(dev);
+       if (pin >= sc->gpio_npins)
+               return (EINVAL);
+
+       *caps = sc->gpio_pins[pin].gp_caps;
+
+       return (0);
+}
+
+static int
+mvebu_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
+{
+       struct mvebu_gpio_softc *sc;
+
+       sc = device_get_softc(dev);
+       if (pin >= sc->gpio_npins)
+               return (EINVAL);
+
+       *flags = sc->gpio_pins[pin].gp_flags;
+
+       return (0);
+}
+
+static int
+mvebu_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+       struct mvebu_gpio_softc *sc;
+
+       sc = device_get_softc(dev);
+       if (pin >= sc->gpio_npins)
+               return (EINVAL);
+
+       memcpy(name, sc->gpio_pins[pin].gp_name, GPIOMAXNAME);
+
+       return (0);
+}
+
+static int
+mvebu_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+       struct mvebu_gpio_softc *sc;
+
+       sc = device_get_softc(dev);
+       if (pin >= sc->gpio_npins)
+               return (EINVAL);
+
+
+       mvebu_gpio_pin_configure(sc, &sc->gpio_pins[pin], flags);
+
+       return (0);
+}
+
+static int
+mvebu_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
+{
+       struct mvebu_gpio_softc *sc;
+
+       sc = device_get_softc(dev);
+       if (pin >= sc->gpio_npins)
+               return (EINVAL);
+
+       if (value != 0)
+               gpio_write(sc, GPIO_DATA_SET, &sc->gpio_pins[pin], 1);
+       else
+               gpio_write(sc, GPIO_DATA_CLR, &sc->gpio_pins[pin], 1);
+
+       return (0);
+}
+
+static int
+mvebu_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
+{
+       struct mvebu_gpio_softc *sc;
+
+       sc = device_get_softc(dev);
+       if (pin >= sc->gpio_npins)
+               return (EINVAL);
+
+       GPIO_LOCK(sc);
+       *val = gpio_read(sc, GPIO_DATA_IN, &sc->gpio_pins[pin]);
+       *val ^= gpio_read(sc, GPIO_DATA_IN_POL, &sc->gpio_pins[pin]);
+       GPIO_UNLOCK(sc);
+
+       return (0);
+}
+
+static int
+mvebu_gpio_pin_toggle(device_t dev, uint32_t pin)
+{
+       struct mvebu_gpio_softc *sc;
+       uint32_t val;
+
+       sc = device_get_softc(dev);
+       if (pin >= sc->gpio_npins)
+               return (EINVAL);
+
+       GPIO_LOCK(sc);
+       mvebu_gpio_pin_get(sc->dev, pin, &val);
+       if (val != 0)
+               gpio_write(sc, GPIO_DATA_CLR, &sc->gpio_pins[pin], 1);
+       else
+               gpio_write(sc, GPIO_DATA_SET, &sc->gpio_pins[pin], 1);
+       GPIO_UNLOCK(sc);
+
+       return (0);
+}
+
+
+/* --------------------------------------------------------------------------
+ *
+ * Interrupts
+ *
+ */
+static inline void
+intr_modify(struct mvebu_gpio_softc *sc, bus_addr_t reg,
+    struct mvebu_gpio_irqsrc *mgi, uint32_t val, uint32_t mask)
+{
+       int bit;
+
+       bit = GPIO_BIT(mgi->irq);
+       GPIO_LOCK(sc);
+       val = SYSCON_MODIFY_4(sc->syscon,
+           sc->offset + GPIO_REGNUM(mgi->irq) + reg, val, mask);
+       GPIO_UNLOCK(sc);
+}
+
+static inline void
+mvebu_gpio_isrc_mask(struct mvebu_gpio_softc *sc,
+     struct mvebu_gpio_irqsrc *mgi, uint32_t val)
+{
+
+       if (mgi->is_level)
+               intr_modify(sc, GPIO_INT_LEVEL_MASK, mgi, val, 1);
+       else
+               intr_modify(sc, GPIO_INT_MASK, mgi, val, 1);
+}
+
+static inline void
+mvebu_gpio_isrc_eoi(struct mvebu_gpio_softc *sc,
+     struct mvebu_gpio_irqsrc *mgi)
+{
+
+       if (!mgi->is_level)
+               intr_modify(sc, GPIO_INT_CAUSE, mgi, 1, 1);
+}
+
+
+static int
+mvebu_gpio_pic_attach(struct mvebu_gpio_softc *sc)
+{
+       int rv;
+       uint32_t irq;
+       const char *name;
+
+       sc->isrcs = malloc(sizeof(*sc->isrcs) * sc->gpio_npins, M_DEVBUF,
+           M_WAITOK | M_ZERO);
+
+       name = device_get_nameunit(sc->dev);
+       for (irq = 0; irq < sc->gpio_npins; irq++) {
+               sc->isrcs[irq].irq = irq;
+               sc->isrcs[irq].is_level = false;
+               sc->isrcs[irq].is_inverted = false;
+               rv = intr_isrc_register(&sc->isrcs[irq].isrc,
+                   sc->dev, 0, "%s,%u", name, irq);
+               if (rv != 0)
+                       return (rv); /* XXX deregister ISRCs */
+       }
+       if (intr_pic_register(sc->dev,
+           OF_xref_from_node(ofw_bus_get_node(sc->dev))) == NULL)
+               return (ENXIO);
+
+       return (0);
+}
+
+static int
+mvebu_gpio_pic_detach(struct mvebu_gpio_softc *sc)
+{
+
+       /*
+        *  There has not been established any procedure yet
+        *  how to detach PIC from living system correctly.
+        */
+       device_printf(sc->dev, "%s: not implemented yet\n", __func__);
+       return (EBUSY);
+}
+
+
+static void
+mvebu_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+       struct mvebu_gpio_softc *sc;
+       struct mvebu_gpio_irqsrc *mgi;
+
+       sc = device_get_softc(dev);
+       mgi = (struct mvebu_gpio_irqsrc *)isrc;
+       mvebu_gpio_isrc_mask(sc, mgi, 0);
+}
+
+static void
+mvebu_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+       struct mvebu_gpio_softc *sc;
+       struct mvebu_gpio_irqsrc *mgi;
+
+       sc = device_get_softc(dev);
+       mgi = (struct mvebu_gpio_irqsrc *)isrc;
+       mvebu_gpio_isrc_mask(sc, mgi, 1);
+}
+
+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)
+{
+       bool inverted, level;
+
+       /*
+        * The first cell is the interrupt number.
+        * The second cell is used to specify flags:
+        *      bits[3:0] trigger type and level flags:
+        *              1 = low-to-high edge triggered.
+        *              2 = high-to-low edge triggered.
+        *              4 = active high level-sensitive.
+        *              8 = active low level-sensitive.
+        */
+       if (ncells != 2 || cells[0] >= sc->gpio_npins)
+               return (EINVAL);
+
+
+       switch (cells[1]) {
+       case 1:
+               inverted  = false;
+               level  = false;
+               break;
+       case 2:
+               inverted  = true;
+               level  = false;
+               break;
+       case 4:
+               inverted  = false;
+               level  = true;
+               break;
+       case 8:
+               inverted  = true;
+               level  = true;
+               break;
+       default:
+               return (EINVAL);
+       }
+       *irqp = cells[0];
+       if (invertedp != NULL)
+               *invertedp = inverted;
+       if (levelp != NULL)
+               *levelp = level;
+       return (0);
+}
+
+
+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)
+{
+       bool inverted, level;
+
+       if (gpio_pin_num >= sc->gpio_npins)
+               return (EINVAL);
+
+       switch (intr_mode) {
+       case GPIO_INTR_LEVEL_LOW:
+               inverted  = true;
+               level = true;
+               break;
+       case GPIO_INTR_LEVEL_HIGH:
+               inverted  = false;
+               level = true;
+               break;
+       case GPIO_INTR_CONFORM:
+       case GPIO_INTR_EDGE_RISING:
+               inverted  = false;
+               level = false;
+               break;
+       case GPIO_INTR_EDGE_FALLING:
+               inverted  = true;
+               level = false;
+               break;
+       default:
+               return (EINVAL);
+       }
+       *irqp = gpio_pin_num;
+       if (invertedp != NULL)
+               *invertedp = inverted;
+       if (levelp != NULL)
+               *levelp = level;
+       return (0);
+}
+
+static int
+mvebu_gpio_pic_map_intr(device_t dev, struct intr_map_data *data,
+    struct intr_irqsrc **isrcp)
+{
+       int rv;
+       u_int irq;
+       struct mvebu_gpio_softc *sc;
+
+       sc = device_get_softc(dev);
+
+       if (data->type == INTR_MAP_DATA_FDT) {
+               struct intr_map_data_fdt *daf;
+
+               daf = (struct intr_map_data_fdt *)data;
+               rv = mvebu_gpio_pic_map_fdt(sc, daf->ncells, daf->cells, &irq,
+                   NULL, NULL);
+       } else if (data->type == INTR_MAP_DATA_GPIO) {
+               struct intr_map_data_gpio *dag;
+
+               dag = (struct intr_map_data_gpio *)data;
+               rv = mvebu_gpio_pic_map_gpio(sc, dag->gpio_pin_num,
+                  dag->gpio_pin_flags, dag->gpio_intr_mode, &irq, NULL, NULL);
+       } else
+               return (ENOTSUP);
+
+       if (rv == 0)
+               *isrcp = &sc->isrcs[irq].isrc;
+       return (rv);
+}
+
+static void
+mvebu_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+       struct mvebu_gpio_softc *sc;
+       struct mvebu_gpio_irqsrc *mgi;
+
+       sc = device_get_softc(dev);
+       mgi = (struct mvebu_gpio_irqsrc *)isrc;
+       if (mgi->is_level)
+               mvebu_gpio_isrc_eoi(sc, mgi);
+}
+
+static void
+mvebu_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+       struct mvebu_gpio_softc *sc;
+       struct mvebu_gpio_irqsrc *mgi;
+
+       sc = device_get_softc(dev);
+       mgi = (struct mvebu_gpio_irqsrc *)isrc;
+       mvebu_gpio_isrc_mask(sc, mgi, 1);
+}
+
+static void
+mvebu_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+       struct mvebu_gpio_softc *sc;
+       struct mvebu_gpio_irqsrc *mgi;
+
+       sc = device_get_softc(dev);
+       mgi = (struct mvebu_gpio_irqsrc *)isrc;
+
+       mvebu_gpio_isrc_mask(sc, mgi, 0);
+       if (mgi->is_level)
+               mvebu_gpio_isrc_eoi(sc, mgi);
+}
+
+static int
+mvebu_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+    struct resource *res, struct intr_map_data *data)
+{
+       u_int irq;
+       bool inverted, level;
+       int rv;
+       struct mvebu_gpio_softc *sc;
+       struct mvebu_gpio_irqsrc *mgi;
+
+       sc = device_get_softc(dev);
+       mgi = (struct mvebu_gpio_irqsrc *)isrc;
+
+       if (data == NULL)
+               return (ENOTSUP);
+
+       /* Get and check config for an interrupt. */
+       if (data->type == INTR_MAP_DATA_FDT) {
+               struct intr_map_data_fdt *daf;
+
+               daf = (struct intr_map_data_fdt *)data;
+               rv = mvebu_gpio_pic_map_fdt(sc, daf->ncells, daf->cells, &irq,
+                   &inverted, &level);
+       } else if (data->type == INTR_MAP_DATA_GPIO) {
+               struct intr_map_data_gpio *dag;
+
+               dag = (struct intr_map_data_gpio *)data;
+               rv = mvebu_gpio_pic_map_gpio(sc, dag->gpio_pin_num,
+                  dag->gpio_pin_flags, dag->gpio_intr_mode, &irq,
+                  &inverted, &level);
+       } else
+               return (ENOTSUP);
+
+       if (rv != 0)
+               return (EINVAL);
+
+       /*
+        * If this is a setup for another handler,
+        * only check that its configuration match.
+        */
+       if (isrc->isrc_handlers != 0)
+               return (
+                   mgi->is_level == level && mgi->is_inverted == inverted ?
+                   0 : EINVAL);
+
+       mgi->is_level = level;
+       mgi->is_inverted = inverted;
+       intr_modify(sc, GPIO_DATA_IN_POL, mgi, inverted ? 1 : 0, 1);
+       mvebu_gpio_pic_enable_intr(dev, isrc);
+
+       return (0);
+}
+
+static int
+mvebu_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
+    struct resource *res, struct intr_map_data *data)
+{
+       struct mvebu_gpio_softc *sc;
+       struct mvebu_gpio_irqsrc *mgi;
+
+       sc = device_get_softc(dev);
+       mgi = (struct mvebu_gpio_irqsrc *)isrc;
+
+       if (isrc->isrc_handlers == 0)
+               mvebu_gpio_isrc_mask(sc, mgi, 0);
+       return (0);
+}
+
+/* --------------------------------------------------------------------------
+ *
+ * Bus
+ *
+ */
+
+static int
+mvebu_gpio_intr(void *arg)
+{
+       u_int i, lvl, edge;
+       struct mvebu_gpio_softc *sc;
+       struct trapframe *tf;
+       struct mvebu_gpio_irqsrc *mgi;
+       struct mvebu_gpio_irq_cookie *cookie;
+
+
+       cookie = (struct mvebu_gpio_irq_cookie *)arg;
+       sc = cookie->sc;
+       tf = curthread->td_intr_frame;
+
+       for (i = 0; i < sc->gpio_npins; i++) {
+               lvl = gpio_read(sc, GPIO_DATA_IN, &sc->gpio_pins[i]);
+               lvl &= gpio_read(sc, GPIO_INT_LEVEL_MASK, &sc->gpio_pins[i]);
+               edge = gpio_read(sc, GPIO_DATA_IN, &sc->gpio_pins[i]);
+               edge &= gpio_read(sc, GPIO_INT_LEVEL_MASK, &sc->gpio_pins[i]);
+               if (edge == 0  || lvl == 0)
+                       continue;
+
+               mgi = &sc->isrcs[i];
+               if (!mgi->is_level)
+                       mvebu_gpio_isrc_eoi(sc, mgi);
+               if (intr_isrc_dispatch(&mgi->isrc, tf) != 0) {
+                       mvebu_gpio_isrc_mask(sc, mgi, 0);
+                       if (mgi->is_level)
+                               mvebu_gpio_isrc_eoi(sc, mgi);
+                       device_printf(sc->dev,
+                           "Stray irq %u disabled\n", mgi->irq);
+               }
+       }
+       return (FILTER_HANDLED);
+}
+
+static int
+mvebu_gpio_probe(device_t dev)
+{
+
+       if (!ofw_bus_status_okay(dev))
+               return (ENXIO);
+       if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+               return (ENXIO);
+
+       device_set_desc(dev, "Marvell Integrated GPIO Controller");
+       return (0);
+}
+
+static int
+mvebu_gpio_detach(device_t dev)
+{
+       struct mvebu_gpio_softc *sc;
+       int i;
+
+       sc = device_get_softc(dev);
+
+       KASSERT(mtx_initialized(&sc->mtx), ("gpio mutex not initialized"));
+
+       for (i = 0; i < MV_GPIO_MAX_NIRQS; i++) {
+               if (sc->irq_ih[i] != NULL)
+                       bus_teardown_intr(dev, sc->irq_res[i], sc->irq_ih[i]);
+       }
+
+       if (sc->isrcs != NULL)
+               mvebu_gpio_pic_detach(sc);
+
+       gpiobus_detach_bus(dev);
+
+       for (i = 0; i < MV_GPIO_MAX_NIRQS; i++) {
+               if (sc->irq_res[i] != NULL)
+                       bus_release_resource(dev, SYS_RES_IRQ, 0,
+                            sc->irq_res[i]);
+       }
+       GPIO_LOCK_DESTROY(sc);
+
+       return(0);
+}
+
+static int
+mvebu_gpio_attach(device_t dev)
+{
+       struct mvebu_gpio_softc *sc;
+       phandle_t node;
+       struct gpio_pin *pin;
+       pcell_t pincnt;
+       int i, rv, rid;
+
+       sc = device_get_softc(dev);
+       sc->dev = dev;
+       node = ofw_bus_get_node(dev);
+
+       GPIO_LOCK_INIT(sc);
+
+       pincnt = 0;
+       rv = OF_getencprop(node, "ngpios", &pincnt, sizeof(pcell_t));
+       if (rv < 0) {
+               device_printf(dev,
+                   "ERROR: no pin-count or ngpios entry found!\n");
+               return (ENXIO);
+       }
+
+       sc->gpio_npins = MIN(pincnt, MV_GPIO_MAX_NPINS);
+       if (bootverbose)
+               device_printf(dev,
+                   "%d pins available\n", sc->gpio_npins);
+
+       rv = OF_getencprop(node, "offset", &sc->offset, sizeof(sc->offset));
+       if (rv == -1) {
+               device_printf(dev, "ERROR: no 'offset' property found!\n");
+               return (ENXIO);
+       }
+
+       if (SYSCON_GET_HANDLE(sc->dev, &sc->syscon) != 0 ||
+           sc->syscon == NULL) {
+               device_printf(dev, "ERROR: cannot get syscon handle!\n");
+               return (ENXIO);
+       }
+
+       /* Allocate interrupts. */
+       for (i = 0; i < MV_GPIO_MAX_NIRQS; i++) {
+               sc->irq_cookies[i].sc = sc;
+               sc->irq_cookies[i].bank_num = i;
+               rid = i;
+               sc->irq_res[i] = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+                   &rid, RF_ACTIVE);
+               if (sc->irq_res[i] == NULL)
+                       break;
+               if ((bus_setup_intr(dev, sc->irq_res[i],
+                   INTR_TYPE_MISC | INTR_MPSAFE, mvebu_gpio_intr, NULL,
+                   &sc->irq_cookies[i], &sc->irq_ih[i]))) {
+                       device_printf(dev,
+                           "WARNING: unable to register interrupt handler\n");
+                       mvebu_gpio_detach(dev);
+                       return (ENXIO);
+               }
+       }
+
+       /* Init GPIO pins */
+       for (i = 0; i < sc->gpio_npins; i++) {
+               pin = sc->gpio_pins + i;
+               pin->gp_pin = i;
+               if (sc->irq_res[0] != NULL)
+                       pin->gp_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
+                           GPIO_INTR_LEVEL_LOW | GPIO_INTR_LEVEL_HIGH |
+                           GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING;
+               else
+                       pin->gp_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
+               pin->gp_flags =
+                   gpio_read(sc, GPIO_CONTROL, &sc->gpio_pins[i]) != 0 ?
+                   GPIO_PIN_OUTPUT : GPIO_PIN_INPUT;
+               snprintf(pin->gp_name, GPIOMAXNAME, "gpio%d", i);
+
+               /* Init HW */
+               gpio_write(sc, GPIO_INT_MASK, pin, 0);
+               gpio_write(sc, GPIO_INT_LEVEL_MASK, pin, 0);
+               gpio_write(sc, GPIO_INT_CAUSE, pin, 1);
+               gpio_write(sc, GPIO_DATA_IN_POL, pin, 1);
+               gpio_write(sc, GPIO_BLINK_ENA, pin, 0);
+       }
+
+       if (sc->irq_res[0] != NULL) {
+               rv = mvebu_gpio_pic_attach(sc);
+               if (rv != 0) {
+                       device_printf(dev, "WARNING: unable to attach PIC\n");
+                       mvebu_gpio_detach(dev);
+                       return (rv);
+               }
+       }
+
+       sc->busdev = gpiobus_attach_bus(dev);
+       if (sc->busdev == NULL) {

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to