[PATCH v4 07/14] usb: ehci: add Marvell EHCI host controller driver

Luca Lauro <[email protected]>
Newsgroups org.kernel.feeds.b4-sent,org.infradead.lists.barebox
Message-ID <[email protected]>
Signed-off-by: Luca Lauro <[email protected]>
---
 drivers/usb/host/Kconfig        |   7 ++
 drivers/usb/host/Makefile       |  13 +--
 drivers/usb/host/ehci-marvell.c | 228 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 242 insertions(+), 6 deletions(-)

diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 58f276cdb4..8e46eca8a3 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -22,6 +22,13 @@ config USB_EHCI_ZYNQ
 	help
 	  Enable support for Zynq on-chip EHCI USB controller
 
+config USB_EHCI_MARVELL
+	bool "Marvell USB EHCI driver"
+	depends on ARCH_MVEBU
+	depends on USB_EHCI
+	help
+	  Enable support for Marvell USB EHCI controller
+
 config USB_OHCI
 	bool "OHCI driver"
 	depends on !MMU && HAS_DMA
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index cbddfbe923..5442d44e60 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -1,8 +1,9 @@
 # SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_USB_EHCI)		+= ehci-hcd.o
-obj-$(CONFIG_USB_EHCI_OMAP)	+= ehci-omap.o
+obj-$(CONFIG_USB_EHCI)			+= ehci-hcd.o
+obj-$(CONFIG_USB_EHCI_OMAP)		+= ehci-omap.o
 obj-$(CONFIG_USB_EHCI_ATMEL)	+= ehci-atmel.o
-obj-$(CONFIG_USB_EHCI_ZYNQ)	+= ehci-zynq.o
-obj-$(CONFIG_USB_OHCI)		+= ohci-hcd.o
-obj-$(CONFIG_USB_OHCI_AT91)	+= ohci-at91.o
-obj-$(CONFIG_USB_XHCI)		+= xhci.o xhci-mem.o xhci-ring.o
+obj-$(CONFIG_USB_EHCI_ZYNQ)		+= ehci-zynq.o
+obj-$(CONFIG_USB_EHCI_MARVELL)	+= ehci-marvell.o
+obj-$(CONFIG_USB_OHCI)			+= ohci-hcd.o
+obj-$(CONFIG_USB_OHCI_AT91)		+= ohci-at91.o
+obj-$(CONFIG_USB_XHCI)			+= xhci.o xhci-mem.o xhci-ring.o
diff --git a/drivers/usb/host/ehci-marvell.c b/drivers/usb/host/ehci-marvell.c
new file mode 100644
index 0000000000..97123558d1
--- /dev/null
+++ b/drivers/usb/host/ehci-marvell.c
@@ -0,0 +1,228 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Marvell Orion / Kirkwood / Armada EHCI host controller for barebox
+ *
+ * Ported from the modern U-Boot ehci-marvell.c implementation and
+ * adapted to the barebox ehci_register() model.
+ */
+
+/*#define DEBUG*/
+
+#include <common.h>
+#include <driver.h>
+#include <init.h>
+#include <io.h>
+#include <of.h>
+#include <linux/usb/usb.h>
+#include <linux/usb/ehci.h>
+#include <linux/mbus.h>
+
+#include "ehci.h"
+
+#define USB_WINDOW_CTRL(i)			(0x320 + ((i) << 4))
+#define USB_WINDOW_BASE(i)			(0x324 + ((i) << 4))
+#define USB_TARGET_DRAM				0x0
+
+#define USB2_SBUSCFG_OFF			0x90
+
+#define USB_SBUSCFG_BAWR_OFF		0x6
+#define USB_SBUSCFG_BARD_OFF		0x3
+#define USB_SBUSCFG_AHBBRST_OFF		0x0
+
+#define USB_SBUSCFG_BAWR_ALIGN_64B	0x4
+#define USB_SBUSCFG_BARD_ALIGN_64B	0x4
+#define USB_SBUSCFG_AHBBRST_INCR16	0x7
+
+/* AC5 special window mapping (taken from U-Boot) */
+#define USB_TO_DRAM_TARGET_ID		0x2
+#define USB_TO_DRAM_ATTR_ID			0x0
+#define USB_DRAM_BASE				0x00000000
+#define USB_DRAM_SIZE				0xfff	/* do not exceed the source address space */
+
+/*
+ * Private state for this Marvell EHCI instance.
+ * The driver only needs the pointer to struct ehci_host.
+ */
+struct marvell_ehci {
+	struct ehci_host *ehci;
+	void __iomem *base;
+	struct device *dev;
+	u32 dram_bus_base;
+};
+
+static void marvell_ehci_sbuscfg_fixup(void __iomem *base)
+{
+	/*
+	 * SBUSCFG: Control for the AMBA system bus interface:
+	 * BAWR = BARD = 4 : Align rd/wr bursts packets larger than 64 bytes
+	 * AHBBRST = 7	 : Align AHB burst for packets larger than 64 bytes
+	 */
+	writel((USB_SBUSCFG_BAWR_ALIGN_64B << USB_SBUSCFG_BAWR_OFF) |
+			(USB_SBUSCFG_BARD_ALIGN_64B << USB_SBUSCFG_BARD_OFF) |
+			(USB_SBUSCFG_AHBBRST_INCR16 << USB_SBUSCFG_AHBBRST_OFF),
+			base + USB2_SBUSCFG_OFF);
+}
+
+/*
+ * Program the USB → DRAM decoding windows using MBUS information.
+ * For AC5, replicate the special 0x0..USB_DRAM_SIZE mapping.
+ */
+static void marvell_usb_brg_adrdec_setup(struct marvell_ehci *priv)
+{
+	const struct mbus_dram_target_info *dram;
+	void __iomem *base = priv->base;
+	int i;
+
+	dev_info(priv->dev, "USB adrdec setup: ENTER\n");
+
+	dram = mvebu_mbus_dram_info();
+	if (!dram) {
+		dev_err(priv->dev, "no MBUS DRAM info\n");
+		return;
+	}
+
+	for (i = 0; i < 4; i++) {
+		writel(0, base + USB_WINDOW_CTRL(i));
+		writel(0, base + USB_WINDOW_BASE(i));
+	}
+
+	if (priv->dev->of_node &&
+		of_device_is_compatible(priv->dev->of_node, "marvell,ac5-ehci")) {
+		/* map DRAM as seen by USB at address 0x0 (as done in U-Boot) */
+		writel((USB_DRAM_SIZE << 16) |
+				(USB_TO_DRAM_ATTR_ID << 8) |
+				(USB_TO_DRAM_TARGET_ID << 4) | 1,
+				base + USB_WINDOW_CTRL(0));
+		writel(USB_DRAM_BASE, base + USB_WINDOW_BASE(0));
+
+		dev_dbg(priv->dev,
+			"AC5 decoding windows: ctrl=0x%08x base=0x%08x\n",
+			readl(base + USB_WINDOW_CTRL(0)),
+			readl(base + USB_WINDOW_BASE(0)));
+	} else {
+		/* generic mvebu case: one window per each DRAM chip-select */
+
+		for (i = 0; i < dram->num_cs; i++) {
+			const struct mbus_dram_window *cs = dram->cs + i;
+
+			/* Write size, attributes and target id to control register */
+			writel(((cs->size - 1) & 0xffff0000) |
+					(cs->mbus_attr << 8) |
+					(dram->mbus_dram_target_id << 4) | 1,
+					base + USB_WINDOW_CTRL(i));
+
+			/* Write base address to base register */
+			writel(cs->base, base + USB_WINDOW_BASE(i));
+		}
+	}
+
+	for (i = 0; i < 4; i++) {
+		u32 ctrl = readl(base + USB_WINDOW_CTRL(i));
+		u32 winb = readl(base + USB_WINDOW_BASE(i));
+		dev_info(priv->dev, "WIN%d: CTRL=0x%08x BASE=0x%08x\n", i, ctrl, winb);
+	}
+}
+
+/*
+ * Initialization hook called by ehci-hcd.c before starting the host.
+ * Only the bridge address decoding is performed here.
+ */
+static int marvell_ehci_init(void *drvdata)
+{
+	struct marvell_ehci *priv = drvdata;
+
+	marvell_usb_brg_adrdec_setup(priv);
+
+	writel((USB_SBUSCFG_BAWR_ALIGN_64B << USB_SBUSCFG_BAWR_OFF) |
+		(USB_SBUSCFG_BARD_ALIGN_64B << USB_SBUSCFG_BARD_OFF) |
+		(USB_SBUSCFG_AHBBRST_INCR16 << USB_SBUSCFG_AHBBRST_OFF),
+		priv->base + 0x100 + 0x90);
+	mdelay(50);
+
+	/*
+	* The SBUSCFG programming (BAWR/BARD/AHBBRST) in U-Boot is only
+	* required for SoCs without hlock (e.g. Armada3700).
+	* It is currently omitted; if needed in the future, a dedicated
+	* post_init or EHCI core hook can be added.
+	*/
+
+	dev_dbg(priv->dev, "marvell_ehci_init done\n");
+
+	return 0;
+}
+
+static int marvell_ehci_probe(struct device *dev)
+{
+	struct marvell_ehci *priv;
+	struct resource *iores;
+	struct ehci_data data = {};
+	void __iomem *base;
+	struct ehci_host *ehci;
+
+	priv = xzalloc(sizeof(*priv));
+	priv->dev = dev;
+	dev->priv = priv;
+
+	iores = dev_request_mem_resource(dev, 0);
+	if (IS_ERR(iores))
+		return PTR_ERR(iores);
+
+	base = IOMEM(iores->start);
+	priv->base = base;
+
+	/*
+	* On Marvell controllers the EHCI block starts at offset 0x100
+	* from the USB base address.
+	*/
+	data.hccr = base + 0x100;
+
+	u32 capbase = readl(data.hccr);			/* HC_CAPBASE */
+	u8 caplength = capbase & 0xff;			/* CAPLENGTH */
+
+	data.hcor = (void __iomem *)((u8 *)data.hccr + caplength);
+
+	dev_info(dev, "EHCI probe: base=%p hccr=%p hcor=%p\n",
+			base, data.hccr, data.hcor);
+
+	dev_info(dev, "EHCI CAPBASE=0x%08x CAPLENGTH=0x%02x\n",
+			capbase, caplength);
+
+	data.init = marvell_ehci_init;
+	data.drvdata = priv;
+
+	if (of_device_is_compatible(dev->of_node, "marvell,armada-3700-ehci"))
+		marvell_ehci_sbuscfg_fixup(base);
+
+	dev_dbg(dev, "marvell_ehci_probe: done\n");
+
+	ehci = ehci_register(dev, &data);
+	if (IS_ERR(ehci))
+		return PTR_ERR(ehci);
+
+	priv->ehci = ehci;
+
+	return 0;
+}
+
+static void marvell_ehci_remove(struct device *dev)
+{
+	struct marvell_ehci *priv = dev->priv;
+
+	if (priv && priv->ehci)
+		ehci_unregister(priv->ehci);
+}
+
+static const struct of_device_id marvell_ehci_dt_ids[] = {
+	{ .compatible = "marvell,orion-ehci" },
+	{ .compatible = "marvell,armada-3700-ehci" },
+	{ .compatible = "marvell,ac5-ehci" },
+	{ /* sentinel */ }
+};
+
+static struct driver marvell_ehci_driver = {
+	.name			= "marvell-ehci",
+	.probe			= marvell_ehci_probe,
+	.remove			= marvell_ehci_remove,
+	.of_compatible	= DRV_OF_COMPAT(marvell_ehci_dt_ids),
+};
+device_platform_driver(marvell_ehci_driver);

-- 
2.47.3
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.