[PATCH 03/10] usb: dwc3: rtk: Add new realtek soc dwc3 controller glue driver
stanley_chang
stanley_chang at realtek.com
Thu Sep 17 04:38:28 EDT 2020
Realtek DHC SoCs embeds a DWC3 USB IP Core. This USB module can be drive
by dwc3-rtk glue driver
Signed-off-by: stanley_chang <stanley_chang at realtek.com>
---
drivers/usb/dwc3/Kconfig | 10 +
drivers/usb/dwc3/Makefile | 1 +
drivers/usb/dwc3/dwc3-rtk-debugfs.c | 501 ++++++++++++++++++++++++++++
drivers/usb/dwc3/dwc3-rtk.c | 336 +++++++++++++++++++
drivers/usb/dwc3/dwc3-rtk.h | 45 +++
5 files changed, 893 insertions(+)
create mode 100644 drivers/usb/dwc3/dwc3-rtk-debugfs.c
create mode 100644 drivers/usb/dwc3/dwc3-rtk.c
create mode 100644 drivers/usb/dwc3/dwc3-rtk.h
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index 206caa0ea1c6..5353d05d9f6c 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -52,6 +52,16 @@ endchoice
comment "Platform Glue Driver Support"
+config USB_DWC3_RTK
+ tristate "Realtek DWC3 Platform Driver"
+ default USB_DWC3
+ select RTK_USB2PHY
+ select RTK_USB3PHY
+ help
+ RTK SoCs with DesignWare Core USB3 IP inside,
+ say 'Y' or 'M' if you have such device.
+
+
config USB_DWC3_OMAP
tristate "Texas Instruments OMAP5 and similar Platforms"
depends on ARCH_OMAP2PLUS || COMPILE_TEST
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index ae86da0dc5bd..d57cb12d000a 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -51,3 +51,4 @@ obj-$(CONFIG_USB_DWC3_MESON_G12A) += dwc3-meson-g12a.o
obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o
obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o
obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o
+obj-$(CONFIG_USB_DWC3_RTK) += dwc3-rtk.o dwc3-rtk-debugfs.o
diff --git a/drivers/usb/dwc3/dwc3-rtk-debugfs.c b/drivers/usb/dwc3/dwc3-rtk-debugfs.c
new file mode 100644
index 000000000000..367f194c4e8e
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-rtk-debugfs.c
@@ -0,0 +1,501 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * dwc3-rtk-debugfs.c - Reltek DesignWare USB3 DRD Controller DebugFS file
+ *
+ * Copyright (C) 2020 Realtek Semiconductor Corporation
+ *
+ */
+
+#include <linux/spinlock.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/of_address.h>
+
+#include "dwc3-rtk.h"
+#include "core.h"
+#include "io.h"
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+
+struct rtk_debugfs_reg32 {
+ char *name;
+ unsigned long offset;
+ int default_val;
+};
+
+#define dump_register(nm) \
+{ \
+ .name = __stringify(nm) " [" __stringify(DWC3_ ##nm) "]", \
+ .offset = DWC3_ ##nm, \
+}
+
+#define dump_ep_register_set_DEPCMDPAR2(n) \
+ { \
+ .name = "DEPCMDPAR2("__stringify(n)") [" \
+ __stringify(DWC3_DEP_BASE(n)) " + " \
+ __stringify(DWC3_DEPCMDPAR2) "]", \
+ .offset = DWC3_DEP_BASE(n) + \
+ DWC3_DEPCMDPAR2, \
+ },
+#define dump_ep_register_set_DEPCMDPAR1(n) \
+ { \
+ .name = "DEPCMDPAR1("__stringify(n)" [" \
+ __stringify(DWC3_DEP_BASE(n)) " + " \
+ __stringify(DWC3_DEPCMDPAR1) "]", \
+ .offset = DWC3_DEP_BASE(n) + \
+ DWC3_DEPCMDPAR1, \
+ },
+#define dump_ep_register_set_DEPCMDPAR0(n) \
+ { \
+ .name = "DEPCMDPAR0("__stringify(n)") [" \
+ __stringify(DWC3_DEP_BASE(n)) " + " \
+ __stringify(DWC3_DEPCMDPAR0) "]", \
+ .offset = DWC3_DEP_BASE(n) + \
+ DWC3_DEPCMDPAR0, \
+ },
+#define dump_ep_register_set_CMD(n) \
+ { \
+ .name = "DEPCMD("__stringify(n)") [" \
+ __stringify(DWC3_DEP_BASE(n)) " + " \
+ __stringify(DWC3_DEPCMD) "]", \
+ .offset = DWC3_DEP_BASE(n) + \
+ DWC3_DEPCMD, \
+ }
+
+#define dump_ep_register_set(n) \
+ dump_ep_register_set_DEPCMDPAR2(n) \
+ dump_ep_register_set_DEPCMDPAR1(n) \
+ dump_ep_register_set_DEPCMDPAR0(n) \
+ dump_ep_register_set_CMD(n) \
+
+static const struct rtk_debugfs_reg32 dwc3_regs[] = {
+ dump_register(GSBUSCFG0),
+ dump_register(GSBUSCFG1),
+ dump_register(GTXTHRCFG),
+ dump_register(GRXTHRCFG),
+ dump_register(GCTL),
+ dump_register(GEVTEN),
+ dump_register(GSTS),
+ dump_register(GUCTL1),
+ dump_register(GSNPSID),
+ dump_register(GGPIO),
+ dump_register(GUID),
+ dump_register(GUCTL),
+ dump_register(GBUSERRADDR0),
+ dump_register(GBUSERRADDR1),
+ dump_register(GPRTBIMAP0),
+ dump_register(GPRTBIMAP1),
+ dump_register(GHWPARAMS0),
+ dump_register(GHWPARAMS1),
+ dump_register(GHWPARAMS2),
+ dump_register(GHWPARAMS3),
+ dump_register(GHWPARAMS4),
+ dump_register(GHWPARAMS5),
+ dump_register(GHWPARAMS6),
+ dump_register(GHWPARAMS7),
+ dump_register(GDBGFIFOSPACE),
+ dump_register(GDBGLTSSM),
+ dump_register(GPRTBIMAP_HS0),
+ dump_register(GPRTBIMAP_HS1),
+ dump_register(GPRTBIMAP_FS0),
+ dump_register(GPRTBIMAP_FS1),
+
+ dump_register(GUSB2PHYCFG(0)),
+ dump_register(GUSB2PHYCFG(1)),
+ dump_register(GUSB2PHYCFG(2)),
+ dump_register(GUSB2PHYCFG(3)),
+ dump_register(GUSB2PHYCFG(4)),
+ dump_register(GUSB2PHYCFG(5)),
+ dump_register(GUSB2PHYCFG(6)),
+ dump_register(GUSB2PHYCFG(7)),
+ dump_register(GUSB2PHYCFG(8)),
+ dump_register(GUSB2PHYCFG(9)),
+ dump_register(GUSB2PHYCFG(10)),
+ dump_register(GUSB2PHYCFG(11)),
+ dump_register(GUSB2PHYCFG(12)),
+ dump_register(GUSB2PHYCFG(13)),
+ dump_register(GUSB2PHYCFG(14)),
+ dump_register(GUSB2PHYCFG(15)),
+
+ dump_register(GUSB2I2CCTL(0)),
+ dump_register(GUSB2I2CCTL(1)),
+ dump_register(GUSB2I2CCTL(2)),
+ dump_register(GUSB2I2CCTL(3)),
+ dump_register(GUSB2I2CCTL(4)),
+ dump_register(GUSB2I2CCTL(5)),
+ dump_register(GUSB2I2CCTL(6)),
+ dump_register(GUSB2I2CCTL(7)),
+ dump_register(GUSB2I2CCTL(8)),
+ dump_register(GUSB2I2CCTL(9)),
+ dump_register(GUSB2I2CCTL(10)),
+ dump_register(GUSB2I2CCTL(11)),
+ dump_register(GUSB2I2CCTL(12)),
+ dump_register(GUSB2I2CCTL(13)),
+ dump_register(GUSB2I2CCTL(14)),
+ dump_register(GUSB2I2CCTL(15)),
+
+ dump_register(GUSB2PHYACC(0)),
+ dump_register(GUSB2PHYACC(1)),
+ dump_register(GUSB2PHYACC(2)),
+ dump_register(GUSB2PHYACC(3)),
+ dump_register(GUSB2PHYACC(4)),
+ dump_register(GUSB2PHYACC(5)),
+ dump_register(GUSB2PHYACC(6)),
+ dump_register(GUSB2PHYACC(7)),
+ dump_register(GUSB2PHYACC(8)),
+ dump_register(GUSB2PHYACC(9)),
+ dump_register(GUSB2PHYACC(10)),
+ dump_register(GUSB2PHYACC(11)),
+ dump_register(GUSB2PHYACC(12)),
+ dump_register(GUSB2PHYACC(13)),
+ dump_register(GUSB2PHYACC(14)),
+ dump_register(GUSB2PHYACC(15)),
+
+ dump_register(GUSB3PIPECTL(0)),
+ dump_register(GUSB3PIPECTL(1)),
+ dump_register(GUSB3PIPECTL(2)),
+ dump_register(GUSB3PIPECTL(3)),
+ dump_register(GUSB3PIPECTL(4)),
+ dump_register(GUSB3PIPECTL(5)),
+ dump_register(GUSB3PIPECTL(6)),
+ dump_register(GUSB3PIPECTL(7)),
+ dump_register(GUSB3PIPECTL(8)),
+ dump_register(GUSB3PIPECTL(9)),
+ dump_register(GUSB3PIPECTL(10)),
+ dump_register(GUSB3PIPECTL(11)),
+ dump_register(GUSB3PIPECTL(12)),
+ dump_register(GUSB3PIPECTL(13)),
+ dump_register(GUSB3PIPECTL(14)),
+ dump_register(GUSB3PIPECTL(15)),
+
+ dump_register(GTXFIFOSIZ(0)),
+ dump_register(GTXFIFOSIZ(1)),
+ dump_register(GTXFIFOSIZ(2)),
+ dump_register(GTXFIFOSIZ(3)),
+ dump_register(GTXFIFOSIZ(4)),
+ dump_register(GTXFIFOSIZ(5)),
+ dump_register(GTXFIFOSIZ(6)),
+ dump_register(GTXFIFOSIZ(7)),
+ dump_register(GTXFIFOSIZ(8)),
+ dump_register(GTXFIFOSIZ(9)),
+ dump_register(GTXFIFOSIZ(10)),
+ dump_register(GTXFIFOSIZ(11)),
+ dump_register(GTXFIFOSIZ(12)),
+ dump_register(GTXFIFOSIZ(13)),
+ dump_register(GTXFIFOSIZ(14)),
+ dump_register(GTXFIFOSIZ(15)),
+ dump_register(GTXFIFOSIZ(16)),
+ dump_register(GTXFIFOSIZ(17)),
+ dump_register(GTXFIFOSIZ(18)),
+ dump_register(GTXFIFOSIZ(19)),
+ dump_register(GTXFIFOSIZ(20)),
+ dump_register(GTXFIFOSIZ(21)),
+ dump_register(GTXFIFOSIZ(22)),
+ dump_register(GTXFIFOSIZ(23)),
+ dump_register(GTXFIFOSIZ(24)),
+ dump_register(GTXFIFOSIZ(25)),
+ dump_register(GTXFIFOSIZ(26)),
+ dump_register(GTXFIFOSIZ(27)),
+ dump_register(GTXFIFOSIZ(28)),
+ dump_register(GTXFIFOSIZ(29)),
+ dump_register(GTXFIFOSIZ(30)),
+ dump_register(GTXFIFOSIZ(31)),
+
+ dump_register(GRXFIFOSIZ(0)),
+ dump_register(GRXFIFOSIZ(1)),
+ dump_register(GRXFIFOSIZ(2)),
+ dump_register(GRXFIFOSIZ(3)),
+ dump_register(GRXFIFOSIZ(4)),
+ dump_register(GRXFIFOSIZ(5)),
+ dump_register(GRXFIFOSIZ(6)),
+ dump_register(GRXFIFOSIZ(7)),
+ dump_register(GRXFIFOSIZ(8)),
+ dump_register(GRXFIFOSIZ(9)),
+ dump_register(GRXFIFOSIZ(10)),
+ dump_register(GRXFIFOSIZ(11)),
+ dump_register(GRXFIFOSIZ(12)),
+ dump_register(GRXFIFOSIZ(13)),
+ dump_register(GRXFIFOSIZ(14)),
+ dump_register(GRXFIFOSIZ(15)),
+ dump_register(GRXFIFOSIZ(16)),
+ dump_register(GRXFIFOSIZ(17)),
+ dump_register(GRXFIFOSIZ(18)),
+ dump_register(GRXFIFOSIZ(19)),
+ dump_register(GRXFIFOSIZ(20)),
+ dump_register(GRXFIFOSIZ(21)),
+ dump_register(GRXFIFOSIZ(22)),
+ dump_register(GRXFIFOSIZ(23)),
+ dump_register(GRXFIFOSIZ(24)),
+ dump_register(GRXFIFOSIZ(25)),
+ dump_register(GRXFIFOSIZ(26)),
+ dump_register(GRXFIFOSIZ(27)),
+ dump_register(GRXFIFOSIZ(28)),
+ dump_register(GRXFIFOSIZ(29)),
+ dump_register(GRXFIFOSIZ(30)),
+ dump_register(GRXFIFOSIZ(31)),
+
+ dump_register(GEVNTADRLO(0)),
+ dump_register(GEVNTADRHI(0)),
+ dump_register(GEVNTSIZ(0)),
+ dump_register(GEVNTCOUNT(0)),
+
+ dump_register(GHWPARAMS8),
+ dump_register(DCFG),
+ dump_register(DCTL),
+ dump_register(DEVTEN),
+ dump_register(DSTS),
+ dump_register(DGCMDPAR),
+ dump_register(DGCMD),
+ dump_register(DALEPENA),
+
+ dump_ep_register_set(0),
+ dump_ep_register_set(1),
+ dump_ep_register_set(2),
+ dump_ep_register_set(3),
+ dump_ep_register_set(4),
+ dump_ep_register_set(5),
+ dump_ep_register_set(6),
+ dump_ep_register_set(7),
+ dump_ep_register_set(8),
+ dump_ep_register_set(9),
+ dump_ep_register_set(10),
+ dump_ep_register_set(11),
+ dump_ep_register_set(12),
+ dump_ep_register_set(13),
+ dump_ep_register_set(14),
+ dump_ep_register_set(15),
+ dump_ep_register_set(16),
+ dump_ep_register_set(17),
+ dump_ep_register_set(18),
+ dump_ep_register_set(19),
+ dump_ep_register_set(20),
+ dump_ep_register_set(21),
+ dump_ep_register_set(22),
+ dump_ep_register_set(23),
+ dump_ep_register_set(24),
+ dump_ep_register_set(25),
+ dump_ep_register_set(26),
+ dump_ep_register_set(27),
+ dump_ep_register_set(28),
+ dump_ep_register_set(29),
+ dump_ep_register_set(30),
+ dump_ep_register_set(31),
+
+ dump_register(OCFG),
+ dump_register(OCTL),
+ dump_register(OEVT),
+ dump_register(OEVTEN),
+ dump_register(OSTS),
+};
+
+#define dump_wrap_register(nm) \
+{ \
+ .name = __stringify(nm) " [" __stringify(WRAP_ ##nm) "]", \
+ .offset = WRAP_ ##nm, \
+}
+
+static const struct rtk_debugfs_reg32 wrap_regs[] = {
+ dump_wrap_register(CTR_reg),
+ dump_wrap_register(USB_HMAC_CTR0_reg),
+ dump_wrap_register(USB2_PHY_reg),
+};
+
+static int dwc3_rtk_dwc_regdump_show(struct seq_file *s, void *unused)
+{
+ struct dwc3_rtk *dwc3_rtk = s->private;
+ struct dwc3 *dwc;
+ unsigned long flags;
+ struct rtk_debugfs_reg32 *regs = (struct rtk_debugfs_reg32 *)dwc3_regs;
+ u32 nregs = ARRAY_SIZE(dwc3_regs);
+ int i;
+
+ dwc = dwc3_rtk->dwc;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ for (i = 0; i < nregs; i++, regs++) {
+ u32 val = dwc3_readl(dwc->regs, regs->offset);
+
+ seq_printf(s, "%s = 0x%08x -> 0x%08x (diff=0x%08x)\n",
+ regs->name,
+ regs->default_val, val, regs->default_val ^ val);
+ if (seq_has_overflowed(s))
+ break;
+ }
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return 0;
+}
+
+static int dwc3_rtk_dwc_regdump_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dwc3_rtk_dwc_regdump_show, inode->i_private);
+}
+
+static const struct file_operations dwc3_rtk_dwc_regdump_fops = {
+ .open = dwc3_rtk_dwc_regdump_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int dwc3_rtk_wrap_regdump_show(struct seq_file *s, void *unused)
+{
+ struct dwc3_rtk *dwc3_rtk = s->private;
+ struct rtk_debugfs_reg32 *regs = (struct rtk_debugfs_reg32 *)wrap_regs;
+ u32 nregs = ARRAY_SIZE(wrap_regs);
+ int i;
+
+ for (i = 0; i < nregs; i++, regs++) {
+ u32 val = readl(dwc3_rtk->regs + regs->offset);
+
+ seq_printf(s, "%s = 0x%08x -> 0x%08x (diff=0x%08x)\n",
+ regs->name,
+ regs->default_val, val, regs->default_val ^ val);
+ if (seq_has_overflowed(s))
+ break;
+ }
+
+ return 0;
+}
+
+static int dwc3_rtk_wrap_regdump_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dwc3_rtk_wrap_regdump_show, inode->i_private);
+}
+
+static const struct file_operations dwc3_rtk_wrap_regdump_fops = {
+ .open = dwc3_rtk_wrap_regdump_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+void dwc3_rtk_regdump_init(struct dwc3_rtk *dwc3_rtk)
+{
+ struct device *dev;
+ struct device_node *node;
+ struct device_node *dwc3_node;
+
+ if (!dwc3_rtk) {
+ pr_err("%s: No device for dwc3_rtk!\n", __func__);
+ return;
+ }
+
+ dev = dwc3_rtk->dev;
+ if (!dev) {
+ pr_err("%s: No device for dwc3_rtk!\n", __func__);
+ return;
+ }
+
+ node = dev->of_node;
+ if (!node) {
+ pr_err("%s: No device node for dwc3_rtk!\n", __func__);
+ return;
+ }
+
+ dwc3_node = of_get_next_child(node, NULL);
+ if (dwc3_node) {
+ const __be32 *addrp;
+ u64 addr;
+ u64 size;
+ unsigned int flags;
+ u32 fixed_dwc3_globals_regs_start;
+ struct rtk_debugfs_reg32 *regs;
+ void __iomem *base;
+ int i;
+
+ regs = (struct rtk_debugfs_reg32 *)dwc3_regs;
+
+ addrp = of_get_address(dwc3_node, 0, &size, &flags);
+ if (addrp == NULL) {
+ dev_err(dev, "%s: Error! Can't get dwc3 reg address!\n",
+ __func__);
+ return;
+ }
+ addr = of_translate_address(dwc3_node, addrp);
+
+ of_property_read_u32(dwc3_node,
+ "snps,fixed_dwc3_globals_regs_start",
+ &fixed_dwc3_globals_regs_start);
+ if (fixed_dwc3_globals_regs_start) {
+ addr += fixed_dwc3_globals_regs_start;
+ size -= fixed_dwc3_globals_regs_start;
+ }
+ dev_dbg(dev, "dwc3 register start address to 0x%x (size=%x)\n",
+ (u32)addr, (u32)size);
+
+ base = ioremap(addr, size);
+ for (i = 0; i < ARRAY_SIZE(dwc3_regs); i++, regs++)
+ regs->default_val = dwc3_readl(base, regs->offset);
+
+ iounmap(base);
+ }
+
+ if (dwc3_rtk) {
+ struct rtk_debugfs_reg32 *regs;
+ int i;
+
+ regs = (struct rtk_debugfs_reg32 *)wrap_regs;
+
+ for (i = 0; i < ARRAY_SIZE(wrap_regs); i++, regs++) {
+ regs->default_val = readl(dwc3_rtk->regs +
+ regs->offset);
+ }
+ }
+}
+
+void dwc3_rtk_debugfs_init(struct dwc3_rtk *dwc3_rtk)
+{
+ struct dentry *debug_dir;
+ struct dentry *file;
+ struct device *dev;
+
+ if (!dwc3_rtk) {
+ pr_err("%s: No device for dwc3_rtk!\n", __func__);
+ return;
+ }
+
+ dev = dwc3_rtk->dev;
+ if (!dev) {
+ pr_err("%s: No device for dwc3_rtk!\n", __func__);
+ return;
+ }
+
+ dwc3_rtk_regdump_init(dwc3_rtk);
+
+ debug_dir = debugfs_create_dir(dev_name(dev), usb_debug_root);
+ if (!debug_dir) {
+ dev_err(dev, "%s: Can't create debugfs dir\n", __func__);
+ return;
+ }
+ dwc3_rtk->debug_dir = debug_dir;
+
+ file = debugfs_create_file("dwc3_regdump", S_IRUGO,
+ debug_dir, dwc3_rtk, &dwc3_rtk_dwc_regdump_fops);
+ if (!file)
+ dev_err(dev, "%s: Can't create debugfs regdump\n", __func__);
+
+ file = debugfs_create_file("wrap_regdump", S_IRUGO,
+ debug_dir, dwc3_rtk, &dwc3_rtk_wrap_regdump_fops);
+ if (!file)
+ dev_err(dev, "%s: Can't create debugfs regdump\n", __func__);
+}
+
+void dwc3_rtk_debugfs_exit(struct dwc3_rtk *dwc3_rtk)
+{
+ debugfs_remove_recursive(dwc3_rtk->debug_dir);
+}
+
+#else
+void dwc3_rtk_debugfs_init(struct dwc3_rtk *dwc3_rtk)
+{
+
+}
+
+void dwc3_rtk_debugfs_exit(struct dwc3_rtk *dwc3_rtk)
+{
+
+}
+#endif /* CONFIG_DYNAMIC_DEBUG */
diff --git a/drivers/usb/dwc3/dwc3-rtk.c b/drivers/usb/dwc3/dwc3-rtk.c
new file mode 100644
index 000000000000..3291ee401a64
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-rtk.c
@@ -0,0 +1,336 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * dwc3-rtk.c - Realtek DWC3 Specific Glue layer
+ *
+ * Copyright (C) 2017 Realtek Semiconductor Corporation
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/of.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/suspend.h>
+#include <linux/sys_soc.h>
+
+#include "dwc3-rtk.h"
+#include "core.h"
+#include "io.h"
+
+static const struct soc_device_attribute rtk_soc_kylin_a00[] = {
+ {
+ .family = "Realtek Kylin",
+ .revision = "A00",
+ },
+ {
+ /* empty */
+ }
+};
+
+static const struct soc_device_attribute rtk_soc_hercules[] = {
+ {
+ .family = "Realtek Hercules",
+ },
+ {
+ /* empty */
+ }
+};
+
+static void switch_u2_dr_mode(struct dwc3_rtk *rtk, int dr_mode)
+{
+ switch (dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ writel(USB2_PHY_SWITCH_DEVICE |
+ (~USB2_PHY_SWITCH_MASK &
+ readl(rtk->regs + WRAP_USB2_PHY_reg)),
+ rtk->regs + WRAP_USB2_PHY_reg);
+ break;
+ case USB_DR_MODE_HOST:
+ writel(USB2_PHY_SWITCH_HOST |
+ (~USB2_PHY_SWITCH_MASK &
+ readl(rtk->regs + WRAP_USB2_PHY_reg)),
+ rtk->regs + WRAP_USB2_PHY_reg);
+ break;
+ case USB_DR_MODE_OTG:
+ //writel(BIT(11) , rtk->regs + WRAP_USB2_PHY_reg);
+ dev_info(rtk->dev, "%s: USB_DR_MODE_OTG\n", __func__);
+ break;
+ }
+}
+
+static int dwc3_rtk_init(struct dwc3_rtk *rtk)
+{
+ struct device *dev = rtk->dev;
+ void __iomem *regs = rtk->regs;
+
+ if (soc_device_match(rtk_soc_kylin_a00)) {
+ writel(DISABLE_MULTI_REQ | readl(regs + WRAP_CTR_reg),
+ regs + WRAP_CTR_reg);
+ dev_info(dev, "[bug fixed] 1295/1296 A00: add workaround to disable multiple request for D-Bus");
+ }
+
+ if (soc_device_match(rtk_soc_hercules)) {
+ writel(USB2_PHY_EN_PHY_PLL_PORT1 |
+ readl(regs + WRAP_USB2_PHY_reg),
+ regs + WRAP_USB2_PHY_reg);
+ dev_info(dev, "[bug fixed] 1395 add workaround to disable usb2 port 2 suspend!");
+
+ }
+ return 0;
+}
+
+static int dwc3_rtk_probe_dwc3core(struct dwc3_rtk *rtk)
+{
+ struct device *dev = rtk->dev;
+ struct device_node *node = dev->of_node;
+ struct device_node *dwc3_node;
+ int ret = 0;
+
+ dwc3_rtk_init(rtk);
+
+ if (node) {
+ ret = of_platform_populate(node, NULL, NULL, dev);
+ if (ret) {
+ dev_err(dev, "failed to add dwc3 core\n");
+ return ret;
+ }
+
+ dwc3_node = of_get_next_child(node, NULL);
+ if (dwc3_node != NULL) {
+ struct device *dwc3_dev;
+ struct platform_device *dwc3_pdev;
+ int dr_mode;
+
+ dwc3_pdev = of_find_device_by_node(dwc3_node);
+ dwc3_dev = &dwc3_pdev->dev;
+ rtk->dwc = platform_get_drvdata(dwc3_pdev);
+
+ dr_mode = usb_get_dr_mode(dwc3_dev);
+ rtk->default_dwc3_dr_mode = dr_mode;
+ rtk->cur_dr_mode = dr_mode;
+
+ switch_u2_dr_mode(rtk, dr_mode);
+
+ } else {
+ dev_err(dev, "dwc3 node is NULL\n");
+ }
+ } else {
+ dev_err(dev, "dwc3_rtk node is NULL\n");
+ }
+
+ return ret;
+}
+
+static void dwc3_rtk_probe_work(struct work_struct *work)
+{
+ struct dwc3_rtk *rtk = container_of(work, struct dwc3_rtk, work);
+ struct device *dev = rtk->dev;
+ int ret = 0;
+
+ unsigned long probe_time = jiffies;
+
+ dev_info(dev, "%s Start ...\n", __func__);
+
+ ret = dwc3_rtk_probe_dwc3core(rtk);
+
+ if (ret)
+ dev_err(dev, "%s failed to add dwc3 core\n", __func__);
+
+ dev_info(dev, "%s End ... ok! (take %d ms)\n", __func__,
+ jiffies_to_msecs(jiffies - probe_time));
+}
+
+static int dwc3_rtk_probe(struct platform_device *pdev)
+{
+ struct dwc3_rtk *rtk;
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+
+ struct resource *res;
+ void __iomem *regs;
+
+ int ret = -ENOMEM;
+ unsigned long probe_time = jiffies;
+
+ dev_info(&pdev->dev, "Probe Realtek-SoC USB DWC3 Host Controller\n");
+
+ rtk = devm_kzalloc(dev, sizeof(*rtk), GFP_KERNEL);
+ if (!rtk)
+ goto err1;
+
+ /*
+ * Right now device-tree probed devices don't get dma_mask set.
+ * Since shared usb code relies on it, set it here for now.
+ * Once we move to full device tree support this will vanish off.
+ */
+ if (!dev->dma_mask)
+ dev->dma_mask = &dev->coherent_dma_mask;
+ if (!dev->coherent_dma_mask)
+ dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ platform_set_drvdata(pdev, rtk);
+
+ rtk->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "missing memory resource\n");
+ ret = -ENODEV;
+ goto err1;
+ }
+
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs)) {
+ ret = PTR_ERR(regs);
+ goto err1;
+ }
+
+ rtk->regs = regs;
+ rtk->regs_size = resource_size(res);
+
+ dwc3_rtk_debugfs_init(rtk);
+
+ if (node) {
+ if (of_property_read_bool(node, "dis_u3_port")) {
+ void __iomem *usb_hmac_ctr0 = rtk->regs +
+ WRAP_USB_HMAC_CTR0_reg;
+ int val_u3port_dis = U3PORT_DIS | readl(usb_hmac_ctr0);
+
+ writel(val_u3port_dis, usb_hmac_ctr0);
+
+ dev_info(rtk->dev, "%s: disable usb 3.0 port (usb_hmac_ctr0=0x%x)\n",
+ __func__, readl(usb_hmac_ctr0));
+ }
+
+ }
+
+ if (node) {
+ if (of_property_read_bool(node, "delay_probe_work")) {
+ INIT_WORK(&rtk->work, dwc3_rtk_probe_work);
+ schedule_work(&rtk->work);
+ } else {
+ ret = dwc3_rtk_probe_dwc3core(rtk);
+ if (ret) {
+ dev_err(dev, "%s failed to add dwc3 core\n",
+ __func__);
+ goto err1;
+ }
+ }
+ } else {
+ dev_err(dev, "no device node, failed to add dwc3 core\n");
+ ret = -ENODEV;
+ goto err1;
+ }
+
+ dev_info(dev, "%s ok! (take %d ms)\n", __func__,
+ jiffies_to_msecs(jiffies - probe_time));
+
+ return 0;
+
+err1:
+ return ret;
+}
+
+static int dwc3_rtk_remove(struct platform_device *pdev)
+{
+ struct dwc3_rtk *rtk = platform_get_drvdata(pdev);
+
+ dwc3_rtk_debugfs_exit(rtk);
+
+ of_platform_depopulate(rtk->dev);
+
+ return 0;
+}
+
+static void dwc3_rtk_shutdown(struct platform_device *pdev)
+{
+ struct dwc3_rtk *rtk = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+
+ dev_info(dev, "%s start ...\n", __func__);
+
+ of_platform_depopulate(rtk->dev);
+
+ dev_info(dev, "%s ok!\n", __func__);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rtk_dwc3_match[] = {
+ { .compatible = "realtek,dwc3" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rtk_dwc3_match);
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int dwc3_rtk_suspend(struct device *dev)
+{
+ struct dwc3_rtk *rtk = dev_get_drvdata(dev);
+
+ dev_info(dev, "[USB] Enter %s\n", __func__);
+
+ if (!rtk) {
+ dev_err(dev, "[USB] %s dwc3_rtk is NULL\n", __func__);
+ goto out;
+ }
+
+out:
+ dev_info(dev, "[USB] Exit %s", __func__);
+ return 0;
+}
+
+static int dwc3_rtk_resume(struct device *dev)
+{
+ struct dwc3_rtk *rtk = dev_get_drvdata(dev);
+ struct dwc3 *dwc = rtk->dwc;
+
+ dev_info(dev, "[USB] Enter %s", __func__);
+
+ if (!rtk) {
+ dev_err(dev, "[USB] %s dwc3_rtk is NULL\n", __func__);
+ goto out;
+ }
+
+ dwc3_rtk_init(rtk);
+
+ switch_u2_dr_mode(rtk, dwc->dr_mode);
+
+ /* runtime set active to reflect active state. */
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+out:
+ dev_info(dev, "[USB] Exit %s\n", __func__);
+ return 0;
+}
+
+static const struct dev_pm_ops dwc3_rtk_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(dwc3_rtk_suspend, dwc3_rtk_resume)
+};
+
+#define DEV_PM_OPS (&dwc3_rtk_dev_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static struct platform_driver dwc3_rtk_driver = {
+ .probe = dwc3_rtk_probe,
+ .remove = dwc3_rtk_remove,
+ .driver = {
+ .name = "rtk-dwc3",
+ .of_match_table = of_match_ptr(rtk_dwc3_match),
+ .pm = DEV_PM_OPS,
+ },
+ .shutdown = dwc3_rtk_shutdown,
+};
+
+module_platform_driver(dwc3_rtk_driver);
+
+MODULE_ALIAS("platform:rtk-dwc3");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/dwc3/dwc3-rtk.h b/drivers/usb/dwc3/dwc3-rtk.h
new file mode 100644
index 000000000000..9b4e7683def7
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-rtk.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/**
+ * dwc3-rtk.h - Realtek DWC3 Specific Glue layer
+ *
+ * Copyright (C) 2020 Realtek Semiconductor Corporation
+ *
+ */
+
+#ifndef __DRIVERS_USB_DWC3_RTK_H
+#define __DRIVERS_USB_DWC3_RTK_H
+
+#define WRAP_CTR_reg 0x0
+#define DISABLE_MULTI_REQ BIT(1)
+
+#define WRAP_USB_HMAC_CTR0_reg 0x60
+#define U3PORT_DIS BIT(8)
+
+#define WRAP_USB2_PHY_reg 0x70
+#define USB2_PHY_EN_PHY_PLL_PORT0 BIT(12)
+#define USB2_PHY_EN_PHY_PLL_PORT1 BIT(13)
+#define USB2_PHY_SWITCH_MASK 0x707
+#define USB2_PHY_SWITCH_DEVICE 0x0
+#define USB2_PHY_SWITCH_HOST 0x606
+
+struct dwc3_rtk {
+ struct device *dev;
+
+ void __iomem *regs;
+ size_t regs_size;
+
+ struct dwc3 *dwc;
+
+ struct work_struct work;
+
+ int default_dwc3_dr_mode; /* define by dwc3 driver, and it is fixed */
+ int cur_dr_mode; /* current dr mode */
+
+ /* For debugfs */
+ struct dentry *debug_dir;
+};
+
+void dwc3_rtk_debugfs_init(struct dwc3_rtk *dwc3_rtk);
+void dwc3_rtk_debugfs_exit(struct dwc3_rtk *dwc3_rtk);
+
+#endif /* __DRIVERS_USB_DWC3_RTK_H */
--
2.28.0
More information about the linux-realtek-soc
mailing list