diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 17:40:19 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 17:40:19 +0000 |
commit | 9f0fc191371843c4fc000a226b0a26b6c059aacd (patch) | |
tree | 35f8be3ef04506ac891ad001e8c41e535ae8d01d /drivers/usb/dwc3 | |
parent | Releasing progress-linux version 6.6.15-2~progress7.99u1. (diff) | |
download | linux-9f0fc191371843c4fc000a226b0a26b6c059aacd.tar.xz linux-9f0fc191371843c4fc000a226b0a26b6c059aacd.zip |
Merging upstream version 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/Kconfig | 11 | ||||
-rw-r--r-- | drivers/usb/dwc3/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/dwc3/core.c | 28 | ||||
-rw-r--r-- | drivers/usb/dwc3/core.h | 6 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-pci.c | 4 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-qcom.c | 12 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-rtk.c | 481 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-xilinx.c | 14 | ||||
-rw-r--r-- | drivers/usb/dwc3/gadget.c | 11 | ||||
-rw-r--r-- | drivers/usb/dwc3/host.c | 4 |
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"); |