summaryrefslogtreecommitdiffstats
path: root/drivers/usb/dwc3
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 17:35:05 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 17:39:31 +0000
commit85c675d0d09a45a135bddd15d7b385f8758c32fb (patch)
tree76267dbc9b9a130337be3640948fe397b04ac629 /drivers/usb/dwc3
parentAdding upstream version 6.6.15. (diff)
downloadlinux-85c675d0d09a45a135bddd15d7b385f8758c32fb.tar.xz
linux-85c675d0d09a45a135bddd15d7b385f8758c32fb.zip
Adding upstream version 6.7.7.upstream/6.7.7
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/usb/dwc3')
-rw-r--r--drivers/usb/dwc3/Kconfig11
-rw-r--r--drivers/usb/dwc3/Makefile1
-rw-r--r--drivers/usb/dwc3/core.c28
-rw-r--r--drivers/usb/dwc3/core.h6
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c4
-rw-r--r--drivers/usb/dwc3/dwc3-qcom.c12
-rw-r--r--drivers/usb/dwc3/dwc3-rtk.c481
-rw-r--r--drivers/usb/dwc3/dwc3-xilinx.c14
-rw-r--r--drivers/usb/dwc3/gadget.c11
-rw-r--r--drivers/usb/dwc3/host.c4
10 files changed, 552 insertions, 20 deletions
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index 98efcbb76c..5fc27b20df 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -178,4 +178,15 @@ config USB_DWC3_OCTEON
Only the host mode is currently supported.
Say 'Y' or 'M' here if you have one such device.
+config USB_DWC3_RTK
+ tristate "Realtek DWC3 Platform Driver"
+ depends on OF && ARCH_REALTEK
+ default USB_DWC3
+ select USB_ROLE_SWITCH
+ help
+ RTK DHC RTD SoCs with DesignWare Core USB3 IP inside,
+ and IP Core configured for USB 2.0 and USB 3.0 in host
+ or dual-role mode.
+ Say 'Y' or 'M' if you have such device.
+
endif
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index fe1493d4bb..124eda2522 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -55,3 +55,4 @@ obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o
obj-$(CONFIG_USB_DWC3_IMX8MP) += dwc3-imx8mp.o
obj-$(CONFIG_USB_DWC3_XILINX) += dwc3-xilinx.o
obj-$(CONFIG_USB_DWC3_OCTEON) += dwc3-octeon.o
+obj-$(CONFIG_USB_DWC3_RTK) += dwc3-rtk.o
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index b73b79fea2..f50b5575d5 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -817,8 +817,20 @@ static int dwc3_clk_enable(struct dwc3 *dwc)
if (ret)
goto disable_ref_clk;
+ ret = clk_prepare_enable(dwc->utmi_clk);
+ if (ret)
+ goto disable_susp_clk;
+
+ ret = clk_prepare_enable(dwc->pipe_clk);
+ if (ret)
+ goto disable_utmi_clk;
+
return 0;
+disable_utmi_clk:
+ clk_disable_unprepare(dwc->utmi_clk);
+disable_susp_clk:
+ clk_disable_unprepare(dwc->susp_clk);
disable_ref_clk:
clk_disable_unprepare(dwc->ref_clk);
disable_bus_clk:
@@ -828,6 +840,8 @@ disable_bus_clk:
static void dwc3_clk_disable(struct dwc3 *dwc)
{
+ clk_disable_unprepare(dwc->pipe_clk);
+ clk_disable_unprepare(dwc->utmi_clk);
clk_disable_unprepare(dwc->susp_clk);
clk_disable_unprepare(dwc->ref_clk);
clk_disable_unprepare(dwc->bus_clk);
@@ -1836,6 +1850,20 @@ static int dwc3_get_clocks(struct dwc3 *dwc)
}
}
+ /* specific to Rockchip RK3588 */
+ dwc->utmi_clk = devm_clk_get_optional(dev, "utmi");
+ if (IS_ERR(dwc->utmi_clk)) {
+ return dev_err_probe(dev, PTR_ERR(dwc->utmi_clk),
+ "could not get utmi clock\n");
+ }
+
+ /* specific to Rockchip RK3588 */
+ dwc->pipe_clk = devm_clk_get_optional(dev, "pipe");
+ if (IS_ERR(dwc->pipe_clk)) {
+ return dev_err_probe(dev, PTR_ERR(dwc->pipe_clk),
+ "could not get pipe clock\n");
+ }
+
return 0;
}
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 6782ec8bfd..efe6caf4d0 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -996,6 +996,8 @@ struct dwc3_scratchpad_array {
* @bus_clk: clock for accessing the registers
* @ref_clk: reference clock
* @susp_clk: clock used when the SS phy is in low power (S3) state
+ * @utmi_clk: clock used for USB2 PHY communication
+ * @pipe_clk: clock used for USB3 PHY communication
* @reset: reset control
* @regs: base address for our registers
* @regs_size: address space size
@@ -1115,6 +1117,8 @@ struct dwc3_scratchpad_array {
* instances in park mode.
* @parkmode_disable_hs_quirk: set if we need to disable all HishSpeed
* instances in park mode.
+ * @gfladj_refclk_lpm_sel: set if we need to enable SOF/ITP counter
+ * running based on ref_clk
* @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
* @tx_de_emphasis: Tx de-emphasis value
* 0 - -6dB de-emphasis
@@ -1165,6 +1169,8 @@ struct dwc3 {
struct clk *bus_clk;
struct clk *ref_clk;
struct clk *susp_clk;
+ struct clk *utmi_clk;
+ struct clk *pipe_clk;
struct reset_control *reset;
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index 6604845c39..39564e17f3 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -51,6 +51,8 @@
#define PCI_DEVICE_ID_INTEL_MTLP 0x7ec1
#define PCI_DEVICE_ID_INTEL_MTLS 0x7f6f
#define PCI_DEVICE_ID_INTEL_MTL 0x7e7e
+#define PCI_DEVICE_ID_INTEL_ARLH 0x7ec1
+#define PCI_DEVICE_ID_INTEL_ARLH_PCH 0x777e
#define PCI_DEVICE_ID_INTEL_TGL 0x9a15
#define PCI_DEVICE_ID_AMD_MR 0x163a
@@ -421,6 +423,8 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
{ PCI_DEVICE_DATA(INTEL, MTLP, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, MTL, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, MTLS, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, ARLH, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, ARLH_PCH, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, TGL, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(AMD, NL_USB, &dwc3_pci_amd_swnode) },
diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
index 8254437411..fdf6d5d3c2 100644
--- a/drivers/usb/dwc3/dwc3-qcom.c
+++ b/drivers/usb/dwc3/dwc3-qcom.c
@@ -546,10 +546,9 @@ static int dwc3_qcom_setup_irq(struct platform_device *pdev)
pdata ? pdata->hs_phy_irq_index : -1);
if (irq > 0) {
/* Keep wakeup interrupts disabled until suspend */
- irq_set_status_flags(irq, IRQ_NOAUTOEN);
ret = devm_request_threaded_irq(qcom->dev, irq, NULL,
qcom_dwc3_resume_irq,
- IRQF_ONESHOT,
+ IRQF_ONESHOT | IRQF_NO_AUTOEN,
"qcom_dwc3 HS", qcom);
if (ret) {
dev_err(qcom->dev, "hs_phy_irq failed: %d\n", ret);
@@ -561,10 +560,9 @@ static int dwc3_qcom_setup_irq(struct platform_device *pdev)
irq = dwc3_qcom_get_irq(pdev, "dp_hs_phy_irq",
pdata ? pdata->dp_hs_phy_irq_index : -1);
if (irq > 0) {
- irq_set_status_flags(irq, IRQ_NOAUTOEN);
ret = devm_request_threaded_irq(qcom->dev, irq, NULL,
qcom_dwc3_resume_irq,
- IRQF_ONESHOT,
+ IRQF_ONESHOT | IRQF_NO_AUTOEN,
"qcom_dwc3 DP_HS", qcom);
if (ret) {
dev_err(qcom->dev, "dp_hs_phy_irq failed: %d\n", ret);
@@ -576,10 +574,9 @@ static int dwc3_qcom_setup_irq(struct platform_device *pdev)
irq = dwc3_qcom_get_irq(pdev, "dm_hs_phy_irq",
pdata ? pdata->dm_hs_phy_irq_index : -1);
if (irq > 0) {
- irq_set_status_flags(irq, IRQ_NOAUTOEN);
ret = devm_request_threaded_irq(qcom->dev, irq, NULL,
qcom_dwc3_resume_irq,
- IRQF_ONESHOT,
+ IRQF_ONESHOT | IRQF_NO_AUTOEN,
"qcom_dwc3 DM_HS", qcom);
if (ret) {
dev_err(qcom->dev, "dm_hs_phy_irq failed: %d\n", ret);
@@ -591,10 +588,9 @@ static int dwc3_qcom_setup_irq(struct platform_device *pdev)
irq = dwc3_qcom_get_irq(pdev, "ss_phy_irq",
pdata ? pdata->ss_phy_irq_index : -1);
if (irq > 0) {
- irq_set_status_flags(irq, IRQ_NOAUTOEN);
ret = devm_request_threaded_irq(qcom->dev, irq, NULL,
qcom_dwc3_resume_irq,
- IRQF_ONESHOT,
+ IRQF_ONESHOT | IRQF_NO_AUTOEN,
"qcom_dwc3 SS", qcom);
if (ret) {
dev_err(qcom->dev, "ss_phy_irq failed: %d\n", ret);
diff --git a/drivers/usb/dwc3/dwc3-rtk.c b/drivers/usb/dwc3/dwc3-rtk.c
new file mode 100644
index 0000000000..3cd6b18455
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-rtk.c
@@ -0,0 +1,481 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dwc3-rtk.c - Realtek DWC3 Specific Glue layer
+ *
+ * Copyright (C) 2023 Realtek Semiconductor Corporation
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/suspend.h>
+#include <linux/sys_soc.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/of.h>
+#include <linux/usb/role.h>
+
+#include "core.h"
+
+#define WRAP_CTR_REG 0x0
+#define DISABLE_MULTI_REQ BIT(1)
+#define DESC_R2W_MULTI_DISABLE BIT(9)
+#define FORCE_PIPE3_PHY_STATUS_TO_0 BIT(13)
+
+#define WRAP_USB2_PHY_UTMI_REG 0x8
+#define TXHSVM_EN BIT(3)
+
+#define WRAP_PHY_PIPE_REG 0xC
+#define RESET_DISABLE_PIPE3_P0 BIT(0)
+#define CLOCK_ENABLE_FOR_PIPE3_PCLK 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
+
+#define WRAP_APHY_REG 0x128
+#define USB3_MBIAS_ENABLE BIT(1)
+
+/* pm control */
+#define WRAP_USB_DBUS_PWR_CTRL_REG 0x160
+#define USB_DBUS_PWR_CTRL_REG 0x0
+#define DBUS_PWR_CTRL_EN BIT(0)
+
+struct dwc3_rtk {
+ struct device *dev;
+ void __iomem *regs;
+ size_t regs_size;
+ void __iomem *pm_base;
+
+ struct dwc3 *dwc;
+
+ enum usb_role cur_role;
+ struct usb_role_switch *role_switch;
+};
+
+static void switch_usb2_role(struct dwc3_rtk *rtk, enum usb_role role)
+{
+ void __iomem *reg;
+ int val;
+
+ reg = rtk->regs + WRAP_USB2_PHY_REG;
+ val = ~USB2_PHY_SWITCH_MASK & readl(reg);
+
+ switch (role) {
+ case USB_ROLE_DEVICE:
+ writel(USB2_PHY_SWITCH_DEVICE | val, reg);
+ break;
+ case USB_ROLE_HOST:
+ writel(USB2_PHY_SWITCH_HOST | val, reg);
+ break;
+ default:
+ dev_dbg(rtk->dev, "%s: role=%d\n", __func__, role);
+ break;
+ }
+}
+
+static void switch_dwc3_role(struct dwc3_rtk *rtk, enum usb_role role)
+{
+ if (!rtk->dwc->role_sw)
+ return;
+
+ usb_role_switch_set_role(rtk->dwc->role_sw, role);
+}
+
+static enum usb_role dwc3_rtk_get_role(struct dwc3_rtk *rtk)
+{
+ enum usb_role role;
+
+ role = rtk->cur_role;
+
+ if (rtk->dwc && rtk->dwc->role_sw)
+ role = usb_role_switch_get_role(rtk->dwc->role_sw);
+ else
+ dev_dbg(rtk->dev, "%s not usb_role_switch role=%d\n", __func__, role);
+
+ return role;
+}
+
+static void dwc3_rtk_set_role(struct dwc3_rtk *rtk, enum usb_role role)
+{
+ rtk->cur_role = role;
+
+ switch_dwc3_role(rtk, role);
+ mdelay(10);
+ switch_usb2_role(rtk, role);
+}
+
+#if IS_ENABLED(CONFIG_USB_ROLE_SWITCH)
+static int dwc3_usb_role_switch_set(struct usb_role_switch *sw, enum usb_role role)
+{
+ struct dwc3_rtk *rtk = usb_role_switch_get_drvdata(sw);
+
+ dwc3_rtk_set_role(rtk, role);
+
+ return 0;
+}
+
+static enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw)
+{
+ struct dwc3_rtk *rtk = usb_role_switch_get_drvdata(sw);
+
+ return dwc3_rtk_get_role(rtk);
+}
+
+static int dwc3_rtk_setup_role_switch(struct dwc3_rtk *rtk)
+{
+ struct usb_role_switch_desc dwc3_role_switch = {NULL};
+
+ dwc3_role_switch.name = dev_name(rtk->dev);
+ dwc3_role_switch.driver_data = rtk;
+ dwc3_role_switch.allow_userspace_control = true;
+ dwc3_role_switch.fwnode = dev_fwnode(rtk->dev);
+ dwc3_role_switch.set = dwc3_usb_role_switch_set;
+ dwc3_role_switch.get = dwc3_usb_role_switch_get;
+ rtk->role_switch = usb_role_switch_register(rtk->dev, &dwc3_role_switch);
+ if (IS_ERR(rtk->role_switch))
+ return PTR_ERR(rtk->role_switch);
+
+ return 0;
+}
+
+static int dwc3_rtk_remove_role_switch(struct dwc3_rtk *rtk)
+{
+ if (rtk->role_switch)
+ usb_role_switch_unregister(rtk->role_switch);
+
+ rtk->role_switch = NULL;
+
+ return 0;
+}
+#else
+#define dwc3_rtk_setup_role_switch(x) 0
+#define dwc3_rtk_remove_role_switch(x) 0
+#endif
+
+static const char *const speed_names[] = {
+ [USB_SPEED_UNKNOWN] = "UNKNOWN",
+ [USB_SPEED_LOW] = "low-speed",
+ [USB_SPEED_FULL] = "full-speed",
+ [USB_SPEED_HIGH] = "high-speed",
+ [USB_SPEED_WIRELESS] = "wireless",
+ [USB_SPEED_SUPER] = "super-speed",
+ [USB_SPEED_SUPER_PLUS] = "super-speed-plus",
+};
+
+static enum usb_device_speed __get_dwc3_maximum_speed(struct device_node *np)
+{
+ struct device_node *dwc3_np;
+ const char *maximum_speed;
+ int ret;
+
+ dwc3_np = of_get_compatible_child(np, "snps,dwc3");
+ if (!dwc3_np)
+ return USB_SPEED_UNKNOWN;
+
+ ret = of_property_read_string(dwc3_np, "maximum-speed", &maximum_speed);
+ if (ret < 0)
+ goto out;
+
+ ret = match_string(speed_names, ARRAY_SIZE(speed_names), maximum_speed);
+
+out:
+ of_node_put(dwc3_np);
+
+ return (ret < 0) ? USB_SPEED_UNKNOWN : ret;
+}
+
+static int dwc3_rtk_init(struct dwc3_rtk *rtk)
+{
+ struct device *dev = rtk->dev;
+ void __iomem *reg;
+ int val;
+ enum usb_device_speed maximum_speed;
+ const struct soc_device_attribute rtk_soc_kylin_a00[] = {
+ { .family = "Realtek Kylin", .revision = "A00", },
+ { /* empty */ } };
+ const struct soc_device_attribute rtk_soc_hercules[] = {
+ { .family = "Realtek Hercules", }, { /* empty */ } };
+ const struct soc_device_attribute rtk_soc_thor[] = {
+ { .family = "Realtek Thor", }, { /* empty */ } };
+
+ if (soc_device_match(rtk_soc_kylin_a00)) {
+ reg = rtk->regs + WRAP_CTR_REG;
+ val = readl(reg);
+ writel(DISABLE_MULTI_REQ | val, 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)) {
+ reg = rtk->regs + WRAP_USB2_PHY_REG;
+ val = readl(reg);
+ writel(USB2_PHY_EN_PHY_PLL_PORT1 | val, reg);
+ dev_info(dev, "[bug fixed] 1395 add workaround to disable usb2 port 2 suspend!");
+ }
+
+ reg = rtk->regs + WRAP_USB2_PHY_UTMI_REG;
+ val = readl(reg);
+ writel(TXHSVM_EN | val, reg);
+
+ maximum_speed = __get_dwc3_maximum_speed(dev->of_node);
+ if (maximum_speed != USB_SPEED_UNKNOWN && maximum_speed <= USB_SPEED_HIGH) {
+ if (soc_device_match(rtk_soc_thor)) {
+ reg = rtk->regs + WRAP_USB_HMAC_CTR0_REG;
+ val = readl(reg);
+ writel(U3PORT_DIS | val, reg);
+ } else {
+ reg = rtk->regs + WRAP_CTR_REG;
+ val = readl(reg);
+ writel(FORCE_PIPE3_PHY_STATUS_TO_0 | val, reg);
+
+ reg = rtk->regs + WRAP_PHY_PIPE_REG;
+ val = ~CLOCK_ENABLE_FOR_PIPE3_PCLK & readl(reg);
+ writel(RESET_DISABLE_PIPE3_P0 | val, reg);
+
+ reg = rtk->regs + WRAP_USB_HMAC_CTR0_REG;
+ val = readl(reg);
+ writel(U3PORT_DIS | val, reg);
+
+ reg = rtk->regs + WRAP_APHY_REG;
+ val = readl(reg);
+ writel(~USB3_MBIAS_ENABLE & val, reg);
+
+ dev_dbg(rtk->dev, "%s: disable usb 3.0 phy\n", __func__);
+ }
+ }
+
+ reg = rtk->regs + WRAP_CTR_REG;
+ val = readl(reg);
+ writel(DESC_R2W_MULTI_DISABLE | val, reg);
+
+ /* Set phy Dp/Dm initial state to host mode to avoid the Dp glitch */
+ reg = rtk->regs + WRAP_USB2_PHY_REG;
+ val = ~USB2_PHY_SWITCH_MASK & readl(reg);
+ writel(USB2_PHY_SWITCH_HOST | val, reg);
+
+ if (rtk->pm_base) {
+ reg = rtk->pm_base + USB_DBUS_PWR_CTRL_REG;
+ val = DBUS_PWR_CTRL_EN | readl(reg);
+ writel(val, reg);
+ }
+
+ return 0;
+}
+
+static int dwc3_rtk_probe_dwc3_core(struct dwc3_rtk *rtk)
+{
+ struct device *dev = rtk->dev;
+ struct device_node *node = dev->of_node;
+ struct platform_device *dwc3_pdev;
+ struct device *dwc3_dev;
+ struct device_node *dwc3_node;
+ enum usb_dr_mode dr_mode;
+ int ret = 0;
+
+ ret = dwc3_rtk_init(rtk);
+ if (ret)
+ return -EINVAL;
+
+ 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_compatible_child(node, "snps,dwc3");
+ if (!dwc3_node) {
+ dev_err(dev, "failed to find dwc3 core node\n");
+ ret = -ENODEV;
+ goto depopulate;
+ }
+
+ dwc3_pdev = of_find_device_by_node(dwc3_node);
+ if (!dwc3_pdev) {
+ dev_err(dev, "failed to find dwc3 core platform_device\n");
+ ret = -ENODEV;
+ goto err_node_put;
+ }
+
+ dwc3_dev = &dwc3_pdev->dev;
+ rtk->dwc = platform_get_drvdata(dwc3_pdev);
+ if (!rtk->dwc) {
+ dev_err(dev, "failed to find dwc3 core\n");
+ ret = -ENODEV;
+ goto err_pdev_put;
+ }
+
+ dr_mode = usb_get_dr_mode(dwc3_dev);
+ if (dr_mode != rtk->dwc->dr_mode) {
+ dev_info(dev, "dts set dr_mode=%d, but dwc3 set dr_mode=%d\n",
+ dr_mode, rtk->dwc->dr_mode);
+ dr_mode = rtk->dwc->dr_mode;
+ }
+
+ switch (dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ rtk->cur_role = USB_ROLE_DEVICE;
+ break;
+ case USB_DR_MODE_HOST:
+ rtk->cur_role = USB_ROLE_HOST;
+ break;
+ default:
+ dev_dbg(rtk->dev, "%s: dr_mode=%d\n", __func__, dr_mode);
+ break;
+ }
+
+ if (device_property_read_bool(dwc3_dev, "usb-role-switch")) {
+ ret = dwc3_rtk_setup_role_switch(rtk);
+ if (ret) {
+ dev_err(dev, "dwc3_rtk_setup_role_switch fail=%d\n", ret);
+ goto err_pdev_put;
+ }
+ rtk->cur_role = dwc3_rtk_get_role(rtk);
+ }
+
+ switch_usb2_role(rtk, rtk->cur_role);
+
+ platform_device_put(dwc3_pdev);
+ of_node_put(dwc3_node);
+
+ return 0;
+
+err_pdev_put:
+ platform_device_put(dwc3_pdev);
+err_node_put:
+ of_node_put(dwc3_node);
+depopulate:
+ of_platform_depopulate(dev);
+
+ return ret;
+}
+
+static int dwc3_rtk_probe(struct platform_device *pdev)
+{
+ struct dwc3_rtk *rtk;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ void __iomem *regs;
+ int ret = 0;
+
+ rtk = devm_kzalloc(dev, sizeof(*rtk), GFP_KERNEL);
+ if (!rtk) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ 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 out;
+ }
+
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs)) {
+ ret = PTR_ERR(regs);
+ goto out;
+ }
+
+ rtk->regs = regs;
+ rtk->regs_size = resource_size(res);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res) {
+ rtk->pm_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(rtk->pm_base)) {
+ ret = PTR_ERR(rtk->pm_base);
+ goto out;
+ }
+ }
+
+ ret = dwc3_rtk_probe_dwc3_core(rtk);
+
+out:
+ return ret;
+}
+
+static void dwc3_rtk_remove(struct platform_device *pdev)
+{
+ struct dwc3_rtk *rtk = platform_get_drvdata(pdev);
+
+ rtk->dwc = NULL;
+
+ dwc3_rtk_remove_role_switch(rtk);
+
+ of_platform_depopulate(rtk->dev);
+}
+
+static void dwc3_rtk_shutdown(struct platform_device *pdev)
+{
+ struct dwc3_rtk *rtk = platform_get_drvdata(pdev);
+
+ of_platform_depopulate(rtk->dev);
+}
+
+static const struct of_device_id rtk_dwc3_match[] = {
+ { .compatible = "realtek,rtd-dwc3" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rtk_dwc3_match);
+
+#ifdef CONFIG_PM_SLEEP
+static int dwc3_rtk_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int dwc3_rtk_resume(struct device *dev)
+{
+ struct dwc3_rtk *rtk = dev_get_drvdata(dev);
+
+ dwc3_rtk_init(rtk);
+
+ switch_usb2_role(rtk, rtk->cur_role);
+
+ /* runtime set active to reflect active state. */
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ 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_new = dwc3_rtk_remove,
+ .driver = {
+ .name = "rtk-dwc3",
+ .of_match_table = rtk_dwc3_match,
+ .pm = DEV_PM_OPS,
+ },
+ .shutdown = dwc3_rtk_shutdown,
+};
+
+module_platform_driver(dwc3_rtk_driver);
+
+MODULE_AUTHOR("Stanley Chang <stanley_chang@realtek.com>");
+MODULE_DESCRIPTION("DesignWare USB3 Realtek Glue Layer");
+MODULE_ALIAS("platform:rtk-dwc3");
+MODULE_LICENSE("GPL");
+MODULE_SOFTDEP("pre: phy_rtk_usb2 phy_rtk_usb3");
diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c
index 19307d24f3..5b7e92f476 100644
--- a/drivers/usb/dwc3/dwc3-xilinx.c
+++ b/drivers/usb/dwc3/dwc3-xilinx.c
@@ -32,9 +32,6 @@
#define XLNX_USB_TRAFFIC_ROUTE_CONFIG 0x005C
#define XLNX_USB_TRAFFIC_ROUTE_FPD 0x1
-/* Versal USB Reset ID */
-#define VERSAL_USB_RESET_ID 0xC104036
-
#define XLNX_USB_FPD_PIPE_CLK 0x7c
#define PIPE_CLK_DESELECT 1
#define PIPE_CLK_SELECT 0
@@ -72,20 +69,23 @@ static void dwc3_xlnx_mask_phy_rst(struct dwc3_xlnx *priv_data, bool mask)
static int dwc3_xlnx_init_versal(struct dwc3_xlnx *priv_data)
{
struct device *dev = priv_data->dev;
+ struct reset_control *crst;
int ret;
+ crst = devm_reset_control_get_exclusive(dev, NULL);
+ if (IS_ERR(crst))
+ return dev_err_probe(dev, PTR_ERR(crst), "failed to get reset signal\n");
+
dwc3_xlnx_mask_phy_rst(priv_data, false);
/* Assert and De-assert reset */
- ret = zynqmp_pm_reset_assert(VERSAL_USB_RESET_ID,
- PM_RESET_ACTION_ASSERT);
+ ret = reset_control_assert(crst);
if (ret < 0) {
dev_err_probe(dev, ret, "failed to assert Reset\n");
return ret;
}
- ret = zynqmp_pm_reset_assert(VERSAL_USB_RESET_ID,
- PM_RESET_ACTION_RELEASE);
+ ret = reset_control_deassert(crst);
if (ret < 0) {
dev_err_probe(dev, ret, "failed to De-assert Reset\n");
return ret;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 89de363ecf..28f49400f3 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -2650,6 +2650,11 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
int ret;
spin_lock_irqsave(&dwc->lock, flags);
+ if (!dwc->pullups_connected) {
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return 0;
+ }
+
dwc->connected = false;
/*
@@ -4703,15 +4708,13 @@ int dwc3_gadget_suspend(struct dwc3 *dwc)
unsigned long flags;
int ret;
- if (!dwc->gadget_driver)
- return 0;
-
ret = dwc3_gadget_soft_disconnect(dwc);
if (ret)
goto err;
spin_lock_irqsave(&dwc->lock, flags);
- dwc3_disconnect_gadget(dwc);
+ if (dwc->gadget_driver)
+ dwc3_disconnect_gadget(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index 61f57fe5bb..4323091532 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -61,7 +61,7 @@ out:
int dwc3_host_init(struct dwc3 *dwc)
{
- struct property_entry props[4];
+ struct property_entry props[5];
struct platform_device *xhci;
int ret, irq;
int prop_idx = 0;
@@ -89,6 +89,8 @@ int dwc3_host_init(struct dwc3 *dwc)
memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props));
+ props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-sg-trb-cache-size-quirk");
+
if (dwc->usb3_lpm_capable)
props[prop_idx++] = PROPERTY_ENTRY_BOOL("usb3-lpm-capable");