Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 31 Oct 2020 16:03:36 +0000 (UTC)
From:      Michal Meloun <mmel@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-12@freebsd.org
Subject:   svn commit: r367216 - in stable/12/sys: arm/mv arm/mv/armada38x conf dev/pci modules/dtb/mv
Message-ID:  <202010311603.09VG3as1023623@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
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 <mmel@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 ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202010311603.09VG3as1023623>