diff mbox series

[v2,04/11] usb: musb-new: Add support for Analog Devices SC5xx SoCs

Message ID 20240925-sc5xx-driver-series-v2-4-ad9287c25c08@analog.com
State Changes Requested
Delegated to: Tom Rini
Headers show
Series drivers: Driver support for ADI SC5xx SoCs | expand

Commit Message

Oliver Gaskell via B4 Relay Sept. 25, 2024, 12:25 p.m. UTC
From: Nathan Barrett-Morrison <nathan.morrison@timesys.com>

This adds support for the MUSB-based USB controller found in the
Analog Devices SC57x and SC58x SoCs.

Co-developed-by: Greg Malysa <greg.malysa@timesys.com>
Signed-off-by: Greg Malysa <greg.malysa@timesys.com>
Co-developed-by: Ian Roberts <ian.roberts@timesys.com>
Signed-off-by: Ian Roberts <ian.roberts@timesys.com>
Signed-off-by: Vasileios Bimpikas <vasileios.bimpikas@analog.com>
Signed-off-by: Utsav Agarwal <utsav.agarwal@analog.com>
Signed-off-by: Arturs Artamonovs <arturs.artamonovs@analog.com>
Signed-off-by: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
---
 MAINTAINERS                   |   1 +
 drivers/usb/musb-new/Kconfig  |   7 ++
 drivers/usb/musb-new/Makefile |   1 +
 drivers/usb/musb-new/sc5xx.c  | 202 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 211 insertions(+)
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 225012025c..6b3b0bb9b3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -617,6 +617,7 @@  F:	drivers/gpio/gpio-adi-adsp.c
 F:	drivers/pinctrl/pinctrl-adi-adsp.c
 F:	drivers/serial/serial_adi_uart4.c
 F:	drivers/timer/adi_sc5xx_timer.c
+F:	drivers/usb/musb-new/sc5xx.c
 F:	include/configs/sc5*
 F:	include/env/adi/
 
diff --git a/drivers/usb/musb-new/Kconfig b/drivers/usb/musb-new/Kconfig
index c52afd41a7..ad9072a532 100644
--- a/drivers/usb/musb-new/Kconfig
+++ b/drivers/usb/musb-new/Kconfig
@@ -22,6 +22,13 @@  config USB_MUSB_GADGET
 	  Enables the MUSB USB dual-role controller in gadget mode.
 
 if USB_MUSB_HOST || USB_MUSB_GADGET
+config USB_MUSB_SC5XX
+        bool "Analog Devices MUSB support"
+        depends on (SC57X || SC58X)
+	help
+	 Say y here to enable support for the USB controller on
+	 ADI SC57X/SC58X processors.
+
 config USB_MUSB_DA8XX
 	bool "Enable DA8xx MUSB Controller"
 	depends on ARCH_DAVINCI
diff --git a/drivers/usb/musb-new/Makefile b/drivers/usb/musb-new/Makefile
index 396ff02654..6638772dac 100644
--- a/drivers/usb/musb-new/Makefile
+++ b/drivers/usb/musb-new/Makefile
@@ -14,6 +14,7 @@  obj-$(CONFIG_USB_MUSB_PIC32) += pic32.o
 obj-$(CONFIG_USB_MUSB_SUNXI) += sunxi.o
 obj-$(CONFIG_USB_MUSB_TI) += ti-musb.o
 obj-$(CONFIG_USB_MUSB_UX500) += ux500.o
+obj-$(CONFIG_USB_MUSB_SC5XX) += sc5xx.o
 
 ccflags-y := $(call cc-option,-Wno-unused-variable) \
 		$(call cc-option,-Wno-unused-but-set-variable) \
diff --git a/drivers/usb/musb-new/sc5xx.c b/drivers/usb/musb-new/sc5xx.c
new file mode 100644
index 0000000000..16201480b4
--- /dev/null
+++ b/drivers/usb/musb-new/sc5xx.c
@@ -0,0 +1,202 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * ADI SC5XX MUSB "glue layer"
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Loosely ported from Linux driver:
+ * Author: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ *
+ */
+
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/usb/musb.h>
+#include "linux-compat.h"
+#include "musb_core.h"
+#include "musb_uboot.h"
+
+#define MUSB_SOFTRST		0x7f
+#define  MUSB_SOFTRST_NRST	BIT(0)
+#define  MUSB_SOFTRST_NRSTX	BIT(1)
+
+#define REG_USB_VBUS_CTL	0x380
+#define REG_USB_ID_CTL		0x382
+#define REG_USB_PHY_CTL		0x394
+#define REG_USB_PLL_OSC		0x398
+#define REG_USB_UTMI_CTL	0x39c
+
+/* controller data */
+struct sc5xx_musb_data {
+	struct musb_host_data mdata;
+	struct device dev;
+};
+
+#define to_sc5xx_musb_data(d)	\
+	container_of(d, struct sc5xx_musb_data, dev)
+
+static void sc5xx_musb_disable(struct musb *musb)
+{
+	/* no way to shut the controller */
+}
+
+static int sc5xx_musb_enable(struct musb *musb)
+{
+	/* soft reset by NRSTx */
+	musb_writeb(musb->mregs, MUSB_SOFTRST, MUSB_SOFTRST_NRSTX);
+	/* set mode */
+	musb_platform_set_mode(musb, musb->board_mode);
+
+	return 0;
+}
+
+static irqreturn_t sc5xx_interrupt(int irq, void *hci)
+{
+	struct musb  *musb = hci;
+	irqreturn_t ret = IRQ_NONE;
+	u8 devctl;
+
+	musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
+	musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
+	musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
+
+	if (musb->int_usb & MUSB_INTR_VBUSERROR) {
+		musb->int_usb &= ~MUSB_INTR_VBUSERROR;
+		devctl = musb_readw(musb->mregs, MUSB_DEVCTL);
+		devctl |= MUSB_DEVCTL_SESSION;
+		musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+	}
+	if (musb->int_usb || musb->int_tx || musb->int_rx) {
+		musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb);
+		musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx);
+		musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx);
+		ret = musb_interrupt(musb);
+	}
+
+	if (musb->int_usb & MUSB_INTR_DISCONNECT && is_host_active(musb))
+		musb_writeb(musb->mregs, REG_USB_VBUS_CTL, 0x0);
+
+	return ret;
+}
+
+static int sc5xx_musb_set_mode(struct musb *musb, u8 mode)
+{
+	struct device *dev = musb->controller;
+	struct sc5xx_musb_data *pdata = to_sc5xx_musb_data(dev);
+
+	switch (mode) {
+	case MUSB_HOST:
+		musb_writeb(musb->mregs, REG_USB_ID_CTL, 0x1);
+		break;
+	case MUSB_PERIPHERAL:
+		musb_writeb(musb->mregs, REG_USB_ID_CTL, 0x3);
+		break;
+	case MUSB_OTG:
+		musb_writeb(musb->mregs, REG_USB_ID_CTL, 0x0);
+		break;
+	default:
+		dev_err(dev, "unsupported mode %d\n", mode);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int sc5xx_musb_init(struct musb *musb)
+{
+	struct sc5xx_musb_data *pdata = to_sc5xx_musb_data(musb->controller);
+
+	musb->isr = sc5xx_interrupt;
+
+	musb_writel(musb->mregs, REG_USB_PLL_OSC, 20 << 1);
+	musb_writeb(musb->mregs, REG_USB_VBUS_CTL, 0x0);
+	musb_writeb(musb->mregs, REG_USB_PHY_CTL, 0x80);
+	musb_writel(musb->mregs, REG_USB_UTMI_CTL,
+		    0x40 | musb_readl(musb->mregs, REG_USB_UTMI_CTL));
+
+	return 0;
+}
+
+const struct musb_platform_ops sc5xx_musb_ops = {
+	.init		= sc5xx_musb_init,
+	.set_mode	= sc5xx_musb_set_mode,
+	.disable	= sc5xx_musb_disable,
+	.enable		= sc5xx_musb_enable,
+};
+
+static struct musb_hdrc_config sc5xx_musb_config = {
+	.multipoint     = 1,
+	.dyn_fifo       = 1,
+	.num_eps        = 16,
+	.ram_bits       = 12,
+};
+
+/* has one MUSB controller which can be host or gadget */
+static struct musb_hdrc_platform_data sc5xx_musb_plat = {
+	.mode           = MUSB_HOST,
+	.config         = &sc5xx_musb_config,
+	.power          = 100,
+	.platform_ops	= &sc5xx_musb_ops,
+};
+
+static int musb_usb_probe(struct udevice *dev)
+{
+	struct usb_bus_priv *priv = dev_get_uclass_priv(dev);
+	struct sc5xx_musb_data *pdata = dev_get_priv(dev);
+	struct musb_host_data *mdata = &pdata->mdata;
+	void __iomem *mregs;
+	int ret;
+
+	priv->desc_before_addr = true;
+
+	mregs = dev_remap_addr(dev);
+	if (!mregs)
+		return -EINVAL;
+
+	/* init controller */
+	if (IS_ENABLED(CONFIG_USB_MUSB_HOST)) {
+		mdata->host = musb_init_controller(&sc5xx_musb_plat,
+						   &pdata->dev, mregs);
+		if (!mdata->host)
+			return -EIO;
+
+		ret = musb_lowlevel_init(mdata);
+	} else {
+		sc5xx_musb_plat.mode = MUSB_PERIPHERAL;
+		mdata->host = musb_register(&sc5xx_musb_plat, &pdata->dev, mregs);
+		if (!mdata->host)
+			return -EIO;
+	}
+	return ret;
+}
+
+static int musb_usb_remove(struct udevice *dev)
+{
+	struct sc5xx_musb_data *pdata = dev_get_priv(dev);
+
+	musb_stop(pdata->mdata.host);
+
+	return 0;
+}
+
+static const struct udevice_id sc5xx_musb_ids[] = {
+	{ .compatible = "adi,sc5xx-musb" },
+	{ }
+};
+
+U_BOOT_DRIVER(usb_musb) = {
+	.name		= "sc5xx-musb",
+	.id		= UCLASS_USB,
+	.of_match	= sc5xx_musb_ids,
+	.probe		= musb_usb_probe,
+	.remove		= musb_usb_remove,
+#ifdef CONFIG_USB_MUSB_HOST
+	.ops		= &musb_usb_ops,
+#endif
+	.plat_auto	= sizeof(struct usb_plat),
+	.priv_auto	= sizeof(struct sc5xx_musb_data),
+};