[PATCH 16/19] usb: ehci: add Marvell EHCI host controller driver
Luca Lauro via B4 Relay <[email protected]> Thu, 23 Jul 2026 15:57:54 +0200
| Newsgroups | org.infradead.lists.barebox,org.kernel.feeds.b4-sent |
|---|---|
| Message-ID | <[email protected]> |
From: Luca Lauro <[email protected]> --- drivers/usb/host/Kconfig | 7 ++ drivers/usb/host/Makefile | 13 +-- drivers/usb/host/ehci-marvell.c | 229 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 243 insertions(+), 6 deletions(-) diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 58f276cdb4..21bf876d69 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..932581d691 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..8accfd697d --- /dev/null +++ b/drivers/usb/host/ehci-marvell.c @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Marvell Orion / Kirkwood / Armada EHCI host for barebox + * + * Porting basato su ehci-marvell.c di U-Boot moderno, + * adattato al modello ehci_register() di barebox. + */ +/*#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 (copiato da 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 /* non oltrepassare lo spazio sorgente */ + +/* + * Stato privato per questa istanza EHCI Marvell. + * Non serve conoscere struct ehci_host: basta il puntatore. + */ +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); +} + +/* + * Programma le decoding windows USB -> DRAM usando le info MBUS. + * Per AC5 replica lo speciale mapping 0x0..USB_DRAM_SIZE. + */ +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")) { + /* mappa DRAM vista da USB a 0x0 (come 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 { + /* caso generico mvebu: una window per ciascun CS DRAM */ + + 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); + } +} + +/* + * Hook di init chiamato da ehci-hcd.c prima di avviare l'host. + * Qui facciamo solo la parte “bridge address decoding”. + */ +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); + + /* + * La programmazione di SBUSCFG (BAWR/BARD/AHBBRST) in U-Boot + * serve solo per SoC senza hlock (Armada3700, ecc.). + * Per ora la lasciamo fuori: se in futuro servirà, possiamo + * aggiungere un post_init o un hook dedicato nel core EHCI. + */ + + 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; + + memset(&data, 0, sizeof(data)); + + /* + * Sui controller Marvell il blocco EHCI inizia a offset 0x100 + * rispetto alla base USB. + */ + 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