diff options
Diffstat (limited to 'drivers/usb/dwc3')
-rw-r--r-- | drivers/usb/dwc3/Kconfig | 9 | ||||
-rw-r--r-- | drivers/usb/dwc3/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/dwc3/core.c | 16 | ||||
-rw-r--r-- | drivers/usb/dwc3/core.h | 38 | ||||
-rw-r--r-- | drivers/usb/dwc3/debug.h | 2 | ||||
-rw-r--r-- | drivers/usb/dwc3/debugfs.c | 14 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-exynos.c | 2 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-imx8mp.c | 2 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-keystone.c | 2 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-of-simple.c | 1 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-pci.c | 2 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-qcom.c | 2 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-st.c | 2 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-xilinx.c | 339 | ||||
-rw-r--r-- | drivers/usb/dwc3/gadget.c | 72 | ||||
-rw-r--r-- | drivers/usb/dwc3/gadget.h | 6 | ||||
-rw-r--r-- | drivers/usb/dwc3/io.h | 2 | ||||
-rw-r--r-- | drivers/usb/dwc3/trace.c | 2 | ||||
-rw-r--r-- | drivers/usb/dwc3/trace.h | 8 |
19 files changed, 464 insertions, 58 deletions
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 2133acf8ee69..66b1454c4db2 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -149,4 +149,13 @@ config USB_DWC3_IMX8MP functionality. Say 'Y' or 'M' if you have one such device. +config USB_DWC3_XILINX + tristate "Xilinx Platforms" + depends on (ARCH_ZYNQMP || ARCH_VERSAL) && OF + default USB_DWC3 + help + Support Xilinx SoCs with DesignWare Core USB3 IP. + This driver handles both ZynqMP and Versal SoC operations. + Say 'Y' or 'M' if you have one such device. + endif diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 2259f8876fb2..2d499de6f66a 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -52,3 +52,4 @@ 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_IMX8MP) += dwc3-imx8mp.o +obj-$(CONFIG_USB_DWC3_XILINX) += dwc3-xilinx.o diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index f2448d0a9d39..05e2e54cbbdc 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1238,6 +1238,8 @@ static void dwc3_get_properties(struct dwc3 *dwc) u8 rx_max_burst_prd; u8 tx_thr_num_pkt_prd; u8 tx_max_burst_prd; + const char *usb_psy_name; + int ret; /* default to highest possible threshold */ lpm_nyet_threshold = 0xf; @@ -1263,6 +1265,13 @@ static void dwc3_get_properties(struct dwc3 *dwc) else dwc->sysdev = dwc->dev; + ret = device_property_read_string(dev, "usb-psy-name", &usb_psy_name); + if (ret >= 0) { + dwc->usb_psy = power_supply_get_by_name(usb_psy_name); + if (!dwc->usb_psy) + dev_err(dev, "couldn't get usb power supply\n"); + } + dwc->has_lpm_erratum = device_property_read_bool(dev, "snps,has-lpm-erratum"); device_property_read_u8(dev, "snps,lpm-nyet-threshold", @@ -1385,7 +1394,6 @@ static void dwc3_check_params(struct dwc3 *dwc) /* Check the maximum_speed parameter */ switch (dwc->maximum_speed) { - case USB_SPEED_LOW: case USB_SPEED_FULL: case USB_SPEED_HIGH: break; @@ -1619,6 +1627,9 @@ disable_clks: assert_reset: reset_control_assert(dwc->reset); + if (dwc->usb_psy) + power_supply_put(dwc->usb_psy); + return ret; } @@ -1641,6 +1652,9 @@ static int dwc3_remove(struct platform_device *pdev) dwc3_free_event_buffers(dwc); dwc3_free_scratch_buffers(dwc); + if (dwc->usb_psy) + power_supply_put(dwc->usb_psy); + return 0; } diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 052b20d52651..6e9abfbccaa6 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -30,6 +30,8 @@ #include <linux/phy/phy.h> +#include <linux/power_supply.h> + #define DWC3_MSG_MAX 500 /* Global constants */ @@ -396,7 +398,6 @@ #define DWC3_DCFG_SUPERSPEED (4 << 0) #define DWC3_DCFG_HIGHSPEED (0 << 0) #define DWC3_DCFG_FULLSPEED BIT(0) -#define DWC3_DCFG_LOWSPEED (2 << 0) #define DWC3_DCFG_NUMP_SHIFT 17 #define DWC3_DCFG_NUMP(n) (((n) >> DWC3_DCFG_NUMP_SHIFT) & 0x1f) @@ -490,7 +491,6 @@ #define DWC3_DSTS_SUPERSPEED (4 << 0) #define DWC3_DSTS_HIGHSPEED (0 << 0) #define DWC3_DSTS_FULLSPEED BIT(0) -#define DWC3_DSTS_LOWSPEED (2 << 0) /* Device Generic Command Register */ #define DWC3_DGCMD_SET_LMP 0x01 @@ -860,8 +860,6 @@ struct dwc3_hwparams { /* HWPARAMS0 */ #define DWC3_MODE(n) ((n) & 0x7) -#define DWC3_MDWIDTH(n) (((n) & 0xff00) >> 8) - /* HWPARAMS1 */ #define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15) @@ -908,11 +906,13 @@ struct dwc3_request { unsigned int remaining; unsigned int status; -#define DWC3_REQUEST_STATUS_QUEUED 0 -#define DWC3_REQUEST_STATUS_STARTED 1 -#define DWC3_REQUEST_STATUS_CANCELLED 2 -#define DWC3_REQUEST_STATUS_COMPLETED 3 -#define DWC3_REQUEST_STATUS_UNKNOWN -1 +#define DWC3_REQUEST_STATUS_QUEUED 0 +#define DWC3_REQUEST_STATUS_STARTED 1 +#define DWC3_REQUEST_STATUS_DISCONNECTED 2 +#define DWC3_REQUEST_STATUS_DEQUEUED 3 +#define DWC3_REQUEST_STATUS_STALLED 4 +#define DWC3_REQUEST_STATUS_COMPLETED 5 +#define DWC3_REQUEST_STATUS_UNKNOWN -1 u8 epnum; struct dwc3_trb *trb; @@ -986,6 +986,7 @@ struct dwc3_scratchpad_array { * @role_sw: usb_role_switch handle * @role_switch_default_mode: default operation mode of controller while * usb role is USB_ROLE_NONE. + * @usb_psy: pointer to power supply interface. * @usb2_phy: pointer to USB2 PHY * @usb3_phy: pointer to USB3 PHY * @usb2_generic_phy: pointer to USB2 PHY @@ -1125,6 +1126,8 @@ struct dwc3 { struct usb_role_switch *role_sw; enum usb_dr_mode role_switch_default_mode; + struct power_supply *usb_psy; + u32 fladj; u32 irq_gadget; u32 otg_irq; @@ -1455,6 +1458,23 @@ u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type); (!(_ip##_VERSIONTYPE_##_to) || \ dwc->version_type <= _ip##_VERSIONTYPE_##_to)) +/** + * dwc3_mdwidth - get MDWIDTH value in bits + * @dwc: pointer to our context structure + * + * Return MDWIDTH configuration value in bits. + */ +static inline u32 dwc3_mdwidth(struct dwc3 *dwc) +{ + u32 mdwidth; + + mdwidth = DWC3_GHWPARAMS0_MDWIDTH(dwc->hwparams.hwparams0); + if (DWC3_IP_IS(DWC32)) + mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6); + + return mdwidth; +} + bool dwc3_has_imod(struct dwc3 *dwc); int dwc3_event_buffers_setup(struct dwc3 *dwc); diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index 8ab394942360..db231de46bb3 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/** +/* * debug.h - DesignWare USB3 DRD Controller Debug Header * * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 5da4f6082d93..7146ee2ac057 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * debugfs.c - DesignWare USB3 DRD Controller DebugFS file * * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com @@ -638,16 +638,14 @@ static int dwc3_tx_fifo_size_show(struct seq_file *s, void *unused) struct dwc3_ep *dep = s->private; struct dwc3 *dwc = dep->dwc; unsigned long flags; - int mdwidth; + u32 mdwidth; u32 val; spin_lock_irqsave(&dwc->lock, flags); val = dwc3_core_fifo_space(dep, DWC3_TXFIFO); /* Convert to bytes */ - mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0); - if (DWC3_IP_IS(DWC32)) - mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6); + mdwidth = dwc3_mdwidth(dwc); val *= mdwidth; val >>= 3; @@ -662,16 +660,14 @@ static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused) struct dwc3_ep *dep = s->private; struct dwc3 *dwc = dep->dwc; unsigned long flags; - int mdwidth; + u32 mdwidth; u32 val; spin_lock_irqsave(&dwc->lock, flags); val = dwc3_core_fifo_space(dep, DWC3_RXFIFO); /* Convert to bytes */ - mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0); - if (DWC3_IP_IS(DWC32)) - mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6); + mdwidth = dwc3_mdwidth(dwc); val *= mdwidth; val >>= 3; diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index 90bb022737da..0ecf20eeceee 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * dwc3-exynos.c - Samsung Exynos DWC3 Specific Glue layer * * Copyright (c) 2012 Samsung Electronics Co., Ltd. diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c index 75f0042b998b..b13cfab89d53 100644 --- a/drivers/usb/dwc3/dwc3-imx8mp.c +++ b/drivers/usb/dwc3/dwc3-imx8mp.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * dwc3-imx8mp.c - NXP imx8mp Specific Glue layer * * Copyright (c) 2020 NXP. diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c index 057056c0975e..1317959294e6 100644 --- a/drivers/usb/dwc3/dwc3-keystone.c +++ b/drivers/usb/dwc3/dwc3-keystone.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * dwc3-keystone.c - Keystone Specific Glue layer * * Copyright (C) 2010-2013 Texas Instruments Incorporated - https://www.ti.com diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index e62ecd22b3ed..71fd620c5161 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -172,7 +172,6 @@ static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = { static const struct of_device_id of_dwc3_simple_match[] = { { .compatible = "rockchip,rk3399-dwc3" }, - { .compatible = "xlnx,zynqmp-dwc3" }, { .compatible = "cavium,octeon-7130-usb-uctl" }, { .compatible = "sprd,sc9860-dwc3" }, { .compatible = "allwinner,sun50i-h6-dwc3" }, diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 4c5c6972124a..4698c43af5ae 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * dwc3-pci.c - PCI Specific glue layer * * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index 3de291ab951a..e37cc58dfa55 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -235,7 +235,7 @@ static int dwc3_qcom_interconnect_disable(struct dwc3_qcom *qcom) /** * dwc3_qcom_interconnect_init() - Get interconnect path handles - * and set bandwidhth. + * and set bandwidth. * @qcom: Pointer to the concerned usb core. * */ diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c index b06b7092b1a2..166b5bde45cb 100644 --- a/drivers/usb/dwc3/dwc3-st.c +++ b/drivers/usb/dwc3/dwc3-st.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0+ -/** +/* * dwc3-st.c Support for dwc3 platform devices on ST Microelectronics platforms * * This is a small driver for the dwc3 to provide the glue logic diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c new file mode 100644 index 000000000000..a59e1494b1a0 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-xilinx.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * dwc3-xilinx.c - Xilinx DWC3 controller specific glue driver + * + * Authors: Manish Narani <manish.narani@xilinx.com> + * Anurag Kumar Vulisha <anurag.kumar.vulisha@xilinx.com> + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/of_platform.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> +#include <linux/of_address.h> +#include <linux/delay.h> +#include <linux/firmware/xlnx-zynqmp.h> +#include <linux/io.h> + +#include <linux/phy/phy.h> + +/* USB phy reset mask register */ +#define XLNX_USB_PHY_RST_EN 0x001C +#define XLNX_PHY_RST_MASK 0x1 + +/* Xilinx USB 3.0 IP Register */ +#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 +#define XLNX_USB_FPD_POWER_PRSNT 0x80 +#define FPD_POWER_PRSNT_OPTION BIT(0) + +struct dwc3_xlnx { + int num_clocks; + struct clk_bulk_data *clks; + struct device *dev; + void __iomem *regs; + int (*pltfm_init)(struct dwc3_xlnx *data); +}; + +static void dwc3_xlnx_mask_phy_rst(struct dwc3_xlnx *priv_data, bool mask) +{ + u32 reg; + + /* + * Enable or disable ULPI PHY reset from USB Controller. + * This does not actually reset the phy, but just controls + * whether USB controller can or cannot reset ULPI PHY. + */ + reg = readl(priv_data->regs + XLNX_USB_PHY_RST_EN); + + if (mask) + reg &= ~XLNX_PHY_RST_MASK; + else + reg |= XLNX_PHY_RST_MASK; + + writel(reg, priv_data->regs + XLNX_USB_PHY_RST_EN); +} + +static int dwc3_xlnx_init_versal(struct dwc3_xlnx *priv_data) +{ + struct device *dev = priv_data->dev; + int ret; + + 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); + 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); + if (ret < 0) { + dev_err_probe(dev, ret, "failed to De-assert Reset\n"); + return ret; + } + + dwc3_xlnx_mask_phy_rst(priv_data, true); + + return 0; +} + +static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) +{ + struct device *dev = priv_data->dev; + struct reset_control *crst, *hibrst, *apbrst; + struct phy *usb3_phy; + int ret; + u32 reg; + + usb3_phy = devm_phy_get(dev, "usb3-phy"); + if (PTR_ERR(usb3_phy) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err; + } else if (IS_ERR(usb3_phy)) { + usb3_phy = NULL; + } + + crst = devm_reset_control_get_exclusive(dev, "usb_crst"); + if (IS_ERR(crst)) { + ret = PTR_ERR(crst); + dev_err_probe(dev, ret, + "failed to get core reset signal\n"); + goto err; + } + + hibrst = devm_reset_control_get_exclusive(dev, "usb_hibrst"); + if (IS_ERR(hibrst)) { + ret = PTR_ERR(hibrst); + dev_err_probe(dev, ret, + "failed to get hibernation reset signal\n"); + goto err; + } + + apbrst = devm_reset_control_get_exclusive(dev, "usb_apbrst"); + if (IS_ERR(apbrst)) { + ret = PTR_ERR(apbrst); + dev_err_probe(dev, ret, + "failed to get APB reset signal\n"); + goto err; + } + + ret = reset_control_assert(crst); + if (ret < 0) { + dev_err(dev, "Failed to assert core reset\n"); + goto err; + } + + ret = reset_control_assert(hibrst); + if (ret < 0) { + dev_err(dev, "Failed to assert hibernation reset\n"); + goto err; + } + + ret = reset_control_assert(apbrst); + if (ret < 0) { + dev_err(dev, "Failed to assert APB reset\n"); + goto err; + } + + ret = phy_init(usb3_phy); + if (ret < 0) { + phy_exit(usb3_phy); + goto err; + } + + ret = reset_control_deassert(apbrst); + if (ret < 0) { + dev_err(dev, "Failed to release APB reset\n"); + goto err; + } + + /* Set PIPE Power Present signal in FPD Power Present Register*/ + writel(FPD_POWER_PRSNT_OPTION, priv_data->regs + XLNX_USB_FPD_POWER_PRSNT); + + /* Set the PIPE Clock Select bit in FPD PIPE Clock register */ + writel(PIPE_CLK_SELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK); + + ret = reset_control_deassert(crst); + if (ret < 0) { + dev_err(dev, "Failed to release core reset\n"); + goto err; + } + + ret = reset_control_deassert(hibrst); + if (ret < 0) { + dev_err(dev, "Failed to release hibernation reset\n"); + goto err; + } + + ret = phy_power_on(usb3_phy); + if (ret < 0) { + phy_exit(usb3_phy); + goto err; + } + + /* + * This routes the USB DMA traffic to go through FPD path instead + * of reaching DDR directly. This traffic routing is needed to + * make SMMU and CCI work with USB DMA. + */ + if (of_dma_is_coherent(dev->of_node) || device_iommu_mapped(dev)) { + reg = readl(priv_data->regs + XLNX_USB_TRAFFIC_ROUTE_CONFIG); + reg |= XLNX_USB_TRAFFIC_ROUTE_FPD; + writel(reg, priv_data->regs + XLNX_USB_TRAFFIC_ROUTE_CONFIG); + } + +err: + return ret; +} + +static const struct of_device_id dwc3_xlnx_of_match[] = { + { + .compatible = "xlnx,zynqmp-dwc3", + .data = &dwc3_xlnx_init_zynqmp, + }, + { + .compatible = "xlnx,versal-dwc3", + .data = &dwc3_xlnx_init_versal, + }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dwc3_xlnx_of_match); + +static int dwc3_xlnx_probe(struct platform_device *pdev) +{ + struct dwc3_xlnx *priv_data; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + const struct of_device_id *match; + void __iomem *regs; + int ret; + + priv_data = devm_kzalloc(dev, sizeof(*priv_data), GFP_KERNEL); + if (!priv_data) + return -ENOMEM; + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) { + ret = PTR_ERR(regs); + dev_err_probe(dev, ret, "failed to map registers\n"); + return ret; + } + + match = of_match_node(dwc3_xlnx_of_match, pdev->dev.of_node); + + priv_data->pltfm_init = match->data; + priv_data->regs = regs; + priv_data->dev = dev; + + platform_set_drvdata(pdev, priv_data); + + ret = devm_clk_bulk_get_all(priv_data->dev, &priv_data->clks); + if (ret < 0) + return ret; + + priv_data->num_clocks = ret; + + ret = clk_bulk_prepare_enable(priv_data->num_clocks, priv_data->clks); + if (ret) + return ret; + + ret = priv_data->pltfm_init(priv_data); + if (ret) + goto err_clk_put; + + ret = of_platform_populate(np, NULL, NULL, dev); + if (ret) + goto err_clk_put; + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_suspend_ignore_children(dev, false); + pm_runtime_get_sync(dev); + + return 0; + +err_clk_put: + clk_bulk_disable_unprepare(priv_data->num_clocks, priv_data->clks); + clk_bulk_put_all(priv_data->num_clocks, priv_data->clks); + + return ret; +} + +static int dwc3_xlnx_remove(struct platform_device *pdev) +{ + struct dwc3_xlnx *priv_data = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + of_platform_depopulate(dev); + + clk_bulk_disable_unprepare(priv_data->num_clocks, priv_data->clks); + clk_bulk_put_all(priv_data->num_clocks, priv_data->clks); + priv_data->num_clocks = 0; + + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + pm_runtime_set_suspended(dev); + + return 0; +} + +static int __maybe_unused dwc3_xlnx_suspend_common(struct device *dev) +{ + struct dwc3_xlnx *priv_data = dev_get_drvdata(dev); + + clk_bulk_disable(priv_data->num_clocks, priv_data->clks); + + return 0; +} + +static int __maybe_unused dwc3_xlnx_resume_common(struct device *dev) +{ + struct dwc3_xlnx *priv_data = dev_get_drvdata(dev); + + return clk_bulk_enable(priv_data->num_clocks, priv_data->clks); +} + +static int __maybe_unused dwc3_xlnx_runtime_idle(struct device *dev) +{ + pm_runtime_mark_last_busy(dev); + pm_runtime_autosuspend(dev); + + return 0; +} + +static UNIVERSAL_DEV_PM_OPS(dwc3_xlnx_dev_pm_ops, dwc3_xlnx_suspend_common, + dwc3_xlnx_resume_common, dwc3_xlnx_runtime_idle); + +static struct platform_driver dwc3_xlnx_driver = { + .probe = dwc3_xlnx_probe, + .remove = dwc3_xlnx_remove, + .driver = { + .name = "dwc3-xilinx", + .of_match_table = dwc3_xlnx_of_match, + .pm = &dwc3_xlnx_dev_pm_ops, + }, +}; + +module_platform_driver(dwc3_xlnx_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Xilinx DWC3 controller specific glue driver"); +MODULE_AUTHOR("Manish Narani <manish.narani@xilinx.com>"); +MODULE_AUTHOR("Anurag Kumar Vulisha <anurag.kumar.vulisha@xilinx.com>"); diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index c7ef218e7a8c..e1b04c976da5 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1402,7 +1402,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep) dwc3_stop_active_transfer(dep, true, true); list_for_each_entry_safe(req, tmp, &dep->started_list, list) - dwc3_gadget_move_cancelled_request(req); + dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_DEQUEUED); /* If ep isn't started, then there's no end transfer pending */ if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING)) @@ -1729,10 +1729,25 @@ static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep) { struct dwc3_request *req; struct dwc3_request *tmp; + struct dwc3 *dwc = dep->dwc; list_for_each_entry_safe(req, tmp, &dep->cancelled_list, list) { dwc3_gadget_ep_skip_trbs(dep, req); - dwc3_gadget_giveback(dep, req, -ECONNRESET); + switch (req->status) { + case DWC3_REQUEST_STATUS_DISCONNECTED: + dwc3_gadget_giveback(dep, req, -ESHUTDOWN); + break; + case DWC3_REQUEST_STATUS_DEQUEUED: + dwc3_gadget_giveback(dep, req, -ECONNRESET); + break; + case DWC3_REQUEST_STATUS_STALLED: + dwc3_gadget_giveback(dep, req, -EPIPE); + break; + default: + dev_err(dwc->dev, "request cancelled with wrong reason:%d\n", req->status); + dwc3_gadget_giveback(dep, req, -ECONNRESET); + break; + } } } @@ -1776,7 +1791,8 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, * cancelled. */ list_for_each_entry_safe(r, t, &dep->started_list, list) - dwc3_gadget_move_cancelled_request(r); + dwc3_gadget_move_cancelled_request(r, + DWC3_REQUEST_STATUS_DEQUEUED); dep->flags &= ~DWC3_EP_WAIT_TRANSFER_COMPLETE; @@ -1848,7 +1864,7 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) dwc3_stop_active_transfer(dep, true, true); list_for_each_entry_safe(req, tmp, &dep->started_list, list) - dwc3_gadget_move_cancelled_request(req); + dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_STALLED); if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) { dep->flags |= DWC3_EP_PENDING_CLEAR_STALL; @@ -2113,9 +2129,6 @@ static void __dwc3_gadget_set_speed(struct dwc3 *dwc) reg |= DWC3_DCFG_SUPERSPEED; } else { switch (speed) { - case USB_SPEED_LOW: - reg |= DWC3_DCFG_LOWSPEED; - break; case USB_SPEED_FULL: reg |= DWC3_DCFG_FULLSPEED; break; @@ -2340,9 +2353,7 @@ static void dwc3_gadget_setup_nump(struct dwc3 *dwc) u32 reg; ram2_depth = DWC3_GHWPARAMS7_RAM2_DEPTH(dwc->hwparams.hwparams7); - mdwidth = DWC3_GHWPARAMS0_MDWIDTH(dwc->hwparams.hwparams0); - if (DWC3_IP_IS(DWC32)) - mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6); + mdwidth = dwc3_mdwidth(dwc); nump = ((ram2_depth * mdwidth / 8) - 24 - 16) / 1024; nump = min_t(u32, nump, 16); @@ -2531,11 +2542,19 @@ static void dwc3_gadget_set_ssp_rate(struct usb_gadget *g, static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA) { struct dwc3 *dwc = gadget_to_dwc(g); + union power_supply_propval val = {0}; + int ret; if (dwc->usb2_phy) return usb_phy_set_power(dwc->usb2_phy, mA); - return 0; + if (!dwc->usb_psy) + return -EOPNOTSUPP; + + val.intval = 1000 * mA; + ret = power_supply_set_property(dwc->usb_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val); + + return ret; } static const struct usb_gadget_ops dwc3_gadget_ops = { @@ -2571,12 +2590,10 @@ static int dwc3_gadget_init_control_endpoint(struct dwc3_ep *dep) static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep) { struct dwc3 *dwc = dep->dwc; - int mdwidth; + u32 mdwidth; int size; - mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0); - if (DWC3_IP_IS(DWC32)) - mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6); + mdwidth = dwc3_mdwidth(dwc); /* MDWIDTH is represented in bits, we need it in bytes */ mdwidth /= 8; @@ -2618,12 +2635,10 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep) static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep) { struct dwc3 *dwc = dep->dwc; - int mdwidth; + u32 mdwidth; int size; - mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0); - if (DWC3_IP_IS(DWC32)) - mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6); + mdwidth = dwc3_mdwidth(dwc); /* MDWIDTH is represented in bits, convert to bytes */ mdwidth /= 8; @@ -2913,6 +2928,11 @@ static void dwc3_gadget_ep_cleanup_completed_requests(struct dwc3_ep *dep, static bool dwc3_gadget_ep_should_continue(struct dwc3_ep *dep) { struct dwc3_request *req; + struct dwc3 *dwc = dep->dwc; + + if (!dep->endpoint.desc || !dwc->pullups_connected || + !dwc->connected) + return false; if (!list_empty(&dep->pending_list)) return true; @@ -3323,6 +3343,15 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) u32 reg; /* + * Ideally, dwc3_reset_gadget() would trigger the function + * drivers to stop any active transfers through ep disable. + * However, for functions which defer ep disable, such as mass + * storage, we will need to rely on the call to stop active + * transfers here, and avoid allowing of request queuing. + */ + dwc->connected = false; + + /* * WORKAROUND: DWC3 revisions <1.88a have an issue which * would cause a missing Disconnect Event if there's a * pending Setup Packet in the FIFO. @@ -3448,11 +3477,6 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) dwc->gadget->ep0->maxpacket = 64; dwc->gadget->speed = USB_SPEED_FULL; break; - case DWC3_DSTS_LOWSPEED: - dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8); - dwc->gadget->ep0->maxpacket = 8; - dwc->gadget->speed = USB_SPEED_LOW; - break; } dwc->eps[1]->endpoint.maxpacket = dwc->gadget->ep0->maxpacket; diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 0cd281949970..77df4b6d6c13 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -90,15 +90,17 @@ static inline void dwc3_gadget_move_started_request(struct dwc3_request *req) /** * dwc3_gadget_move_cancelled_request - move @req to the cancelled_list * @req: the request to be moved + * @reason: cancelled reason for the dwc3 request * * Caller should take care of locking. This function will move @req from its * current list to the endpoint's cancelled_list. */ -static inline void dwc3_gadget_move_cancelled_request(struct dwc3_request *req) +static inline void dwc3_gadget_move_cancelled_request(struct dwc3_request *req, + unsigned int reason) { struct dwc3_ep *dep = req->dep; - req->status = DWC3_REQUEST_STATUS_CANCELLED; + req->status = reason; list_move_tail(&req->list, &dep->cancelled_list); } diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h index 76b73b116862..1e96ea339d48 100644 --- a/drivers/usb/dwc3/io.h +++ b/drivers/usb/dwc3/io.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/** +/* * io.h - DesignWare USB3 DRD IO Header * * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com diff --git a/drivers/usb/dwc3/trace.c b/drivers/usb/dwc3/trace.c index 1b45a9723eeb..088995885678 100644 --- a/drivers/usb/dwc3/trace.c +++ b/drivers/usb/dwc3/trace.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * trace.c - DesignWare USB3 DRD Controller Trace Support * * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h index 97f4f1125a41..51d18e8d1602 100644 --- a/drivers/usb/dwc3/trace.h +++ b/drivers/usb/dwc3/trace.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/** +/* * trace.h - DesignWare USB3 DRD Controller Trace Support * * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com @@ -32,8 +32,10 @@ DECLARE_EVENT_CLASS(dwc3_log_io, __entry->offset = offset; __entry->value = value; ), - TP_printk("addr %p value %08x", __entry->base + __entry->offset, - __entry->value) + TP_printk("addr %p offset %04x value %08x", + __entry->base + __entry->offset, + __entry->offset, + __entry->value) ); DEFINE_EVENT(dwc3_log_io, dwc3_readl, |