xref: /openbmc/linux/drivers/usb/dwc3/dwc3-xilinx.c (revision 16f6ccde74a6f8538c62f127f17207c75f4dba7a)
184770f02SManish Narani // SPDX-License-Identifier: GPL-2.0
23a2a68ecSManish Narani /*
384770f02SManish Narani  * dwc3-xilinx.c - Xilinx DWC3 controller specific glue driver
484770f02SManish Narani  *
584770f02SManish Narani  * Authors: Manish Narani <manish.narani@xilinx.com>
684770f02SManish Narani  *          Anurag Kumar Vulisha <anurag.kumar.vulisha@xilinx.com>
784770f02SManish Narani  */
884770f02SManish Narani 
984770f02SManish Narani #include <linux/module.h>
1084770f02SManish Narani #include <linux/kernel.h>
1184770f02SManish Narani #include <linux/slab.h>
1284770f02SManish Narani #include <linux/clk.h>
1384770f02SManish Narani #include <linux/of.h>
1484770f02SManish Narani #include <linux/platform_device.h>
1584770f02SManish Narani #include <linux/dma-mapping.h>
16e498a044SArnd Bergmann #include <linux/gpio/consumer.h>
1784770f02SManish Narani #include <linux/of_platform.h>
1884770f02SManish Narani #include <linux/pm_runtime.h>
1984770f02SManish Narani #include <linux/reset.h>
2084770f02SManish Narani #include <linux/of_address.h>
2184770f02SManish Narani #include <linux/delay.h>
2284770f02SManish Narani #include <linux/firmware/xlnx-zynqmp.h>
2384770f02SManish Narani #include <linux/io.h>
2484770f02SManish Narani 
2584770f02SManish Narani #include <linux/phy/phy.h>
2684770f02SManish Narani 
2784770f02SManish Narani /* USB phy reset mask register */
2884770f02SManish Narani #define XLNX_USB_PHY_RST_EN			0x001C
2984770f02SManish Narani #define XLNX_PHY_RST_MASK			0x1
3084770f02SManish Narani 
3184770f02SManish Narani /* Xilinx USB 3.0 IP Register */
3284770f02SManish Narani #define XLNX_USB_TRAFFIC_ROUTE_CONFIG		0x005C
3384770f02SManish Narani #define XLNX_USB_TRAFFIC_ROUTE_FPD		0x1
3484770f02SManish Narani 
3584770f02SManish Narani /* Versal USB Reset ID */
3684770f02SManish Narani #define VERSAL_USB_RESET_ID			0xC104036
3784770f02SManish Narani 
3884770f02SManish Narani #define XLNX_USB_FPD_PIPE_CLK			0x7c
3984770f02SManish Narani #define PIPE_CLK_DESELECT			1
4084770f02SManish Narani #define PIPE_CLK_SELECT				0
4184770f02SManish Narani #define XLNX_USB_FPD_POWER_PRSNT		0x80
4284770f02SManish Narani #define FPD_POWER_PRSNT_OPTION			BIT(0)
4384770f02SManish Narani 
4484770f02SManish Narani struct dwc3_xlnx {
4584770f02SManish Narani 	int				num_clocks;
4684770f02SManish Narani 	struct clk_bulk_data		*clks;
4784770f02SManish Narani 	struct device			*dev;
4884770f02SManish Narani 	void __iomem			*regs;
4984770f02SManish Narani 	int				(*pltfm_init)(struct dwc3_xlnx *data);
50d6edcdc1SPiyush Mehta 	struct phy			*usb3_phy;
5184770f02SManish Narani };
5284770f02SManish Narani 
dwc3_xlnx_mask_phy_rst(struct dwc3_xlnx * priv_data,bool mask)5384770f02SManish Narani static void dwc3_xlnx_mask_phy_rst(struct dwc3_xlnx *priv_data, bool mask)
5484770f02SManish Narani {
5584770f02SManish Narani 	u32 reg;
5684770f02SManish Narani 
5784770f02SManish Narani 	/*
5884770f02SManish Narani 	 * Enable or disable ULPI PHY reset from USB Controller.
5984770f02SManish Narani 	 * This does not actually reset the phy, but just controls
6084770f02SManish Narani 	 * whether USB controller can or cannot reset ULPI PHY.
6184770f02SManish Narani 	 */
6284770f02SManish Narani 	reg = readl(priv_data->regs + XLNX_USB_PHY_RST_EN);
6384770f02SManish Narani 
6484770f02SManish Narani 	if (mask)
6584770f02SManish Narani 		reg &= ~XLNX_PHY_RST_MASK;
6684770f02SManish Narani 	else
6784770f02SManish Narani 		reg |= XLNX_PHY_RST_MASK;
6884770f02SManish Narani 
6984770f02SManish Narani 	writel(reg, priv_data->regs + XLNX_USB_PHY_RST_EN);
7084770f02SManish Narani }
7184770f02SManish Narani 
dwc3_xlnx_init_versal(struct dwc3_xlnx * priv_data)7284770f02SManish Narani static int dwc3_xlnx_init_versal(struct dwc3_xlnx *priv_data)
7384770f02SManish Narani {
7484770f02SManish Narani 	struct device		*dev = priv_data->dev;
7584770f02SManish Narani 	int			ret;
7684770f02SManish Narani 
7784770f02SManish Narani 	dwc3_xlnx_mask_phy_rst(priv_data, false);
7884770f02SManish Narani 
7984770f02SManish Narani 	/* Assert and De-assert reset */
8084770f02SManish Narani 	ret = zynqmp_pm_reset_assert(VERSAL_USB_RESET_ID,
8184770f02SManish Narani 				     PM_RESET_ACTION_ASSERT);
8284770f02SManish Narani 	if (ret < 0) {
8384770f02SManish Narani 		dev_err_probe(dev, ret, "failed to assert Reset\n");
8484770f02SManish Narani 		return ret;
8584770f02SManish Narani 	}
8684770f02SManish Narani 
8784770f02SManish Narani 	ret = zynqmp_pm_reset_assert(VERSAL_USB_RESET_ID,
8884770f02SManish Narani 				     PM_RESET_ACTION_RELEASE);
8984770f02SManish Narani 	if (ret < 0) {
9084770f02SManish Narani 		dev_err_probe(dev, ret, "failed to De-assert Reset\n");
9184770f02SManish Narani 		return ret;
9284770f02SManish Narani 	}
9384770f02SManish Narani 
9484770f02SManish Narani 	dwc3_xlnx_mask_phy_rst(priv_data, true);
9584770f02SManish Narani 
9684770f02SManish Narani 	return 0;
9784770f02SManish Narani }
9884770f02SManish Narani 
dwc3_xlnx_init_zynqmp(struct dwc3_xlnx * priv_data)9984770f02SManish Narani static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
10084770f02SManish Narani {
10184770f02SManish Narani 	struct device		*dev = priv_data->dev;
10284770f02SManish Narani 	struct reset_control	*crst, *hibrst, *apbrst;
103ca05b382SPiyush Mehta 	struct gpio_desc	*reset_gpio;
104b470947cSRobert Hancock 	int			ret = 0;
10584770f02SManish Narani 	u32			reg;
10684770f02SManish Narani 
107d6edcdc1SPiyush Mehta 	priv_data->usb3_phy = devm_phy_optional_get(dev, "usb3-phy");
108d6edcdc1SPiyush Mehta 	if (IS_ERR(priv_data->usb3_phy)) {
109d6edcdc1SPiyush Mehta 		ret = PTR_ERR(priv_data->usb3_phy);
1102cc9b1c9SRobert Hancock 		dev_err_probe(dev, ret,
1112cc9b1c9SRobert Hancock 			      "failed to get USB3 PHY\n");
11284770f02SManish Narani 		goto err;
11384770f02SManish Narani 	}
11484770f02SManish Narani 
1159678f336SRobert Hancock 	/*
1169678f336SRobert Hancock 	 * The following core resets are not required unless a USB3 PHY
1179678f336SRobert Hancock 	 * is used, and the subsequent register settings are not required
1189678f336SRobert Hancock 	 * unless a core reset is performed (they should be set properly
1199678f336SRobert Hancock 	 * by the first-stage boot loader, but may be reverted by a core
1209678f336SRobert Hancock 	 * reset). They may also break the configuration if USB3 is actually
1219678f336SRobert Hancock 	 * in use but the usb3-phy entry is missing from the device tree.
1229678f336SRobert Hancock 	 * Therefore, skip these operations in this case.
1239678f336SRobert Hancock 	 */
124*c0484aa1SNeal Frager 	if (!priv_data->usb3_phy) {
125*c0484aa1SNeal Frager 		/* Deselect the PIPE Clock Select bit in FPD PIPE Clock register */
126*c0484aa1SNeal Frager 		writel(PIPE_CLK_DESELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK);
1279678f336SRobert Hancock 		goto skip_usb3_phy;
128*c0484aa1SNeal Frager 	}
1299678f336SRobert Hancock 
13084770f02SManish Narani 	crst = devm_reset_control_get_exclusive(dev, "usb_crst");
13184770f02SManish Narani 	if (IS_ERR(crst)) {
13284770f02SManish Narani 		ret = PTR_ERR(crst);
13384770f02SManish Narani 		dev_err_probe(dev, ret,
13484770f02SManish Narani 			      "failed to get core reset signal\n");
13584770f02SManish Narani 		goto err;
13684770f02SManish Narani 	}
13784770f02SManish Narani 
13884770f02SManish Narani 	hibrst = devm_reset_control_get_exclusive(dev, "usb_hibrst");
13984770f02SManish Narani 	if (IS_ERR(hibrst)) {
14084770f02SManish Narani 		ret = PTR_ERR(hibrst);
14184770f02SManish Narani 		dev_err_probe(dev, ret,
14284770f02SManish Narani 			      "failed to get hibernation reset signal\n");
14384770f02SManish Narani 		goto err;
14484770f02SManish Narani 	}
14584770f02SManish Narani 
14684770f02SManish Narani 	apbrst = devm_reset_control_get_exclusive(dev, "usb_apbrst");
14784770f02SManish Narani 	if (IS_ERR(apbrst)) {
14884770f02SManish Narani 		ret = PTR_ERR(apbrst);
14984770f02SManish Narani 		dev_err_probe(dev, ret,
15084770f02SManish Narani 			      "failed to get APB reset signal\n");
15184770f02SManish Narani 		goto err;
15284770f02SManish Narani 	}
15384770f02SManish Narani 
15484770f02SManish Narani 	ret = reset_control_assert(crst);
15584770f02SManish Narani 	if (ret < 0) {
15684770f02SManish Narani 		dev_err(dev, "Failed to assert core reset\n");
15784770f02SManish Narani 		goto err;
15884770f02SManish Narani 	}
15984770f02SManish Narani 
16084770f02SManish Narani 	ret = reset_control_assert(hibrst);
16184770f02SManish Narani 	if (ret < 0) {
16284770f02SManish Narani 		dev_err(dev, "Failed to assert hibernation reset\n");
16384770f02SManish Narani 		goto err;
16484770f02SManish Narani 	}
16584770f02SManish Narani 
16684770f02SManish Narani 	ret = reset_control_assert(apbrst);
16784770f02SManish Narani 	if (ret < 0) {
16884770f02SManish Narani 		dev_err(dev, "Failed to assert APB reset\n");
16984770f02SManish Narani 		goto err;
17084770f02SManish Narani 	}
17184770f02SManish Narani 
172d6edcdc1SPiyush Mehta 	ret = phy_init(priv_data->usb3_phy);
17384770f02SManish Narani 	if (ret < 0) {
174d6edcdc1SPiyush Mehta 		phy_exit(priv_data->usb3_phy);
17584770f02SManish Narani 		goto err;
17684770f02SManish Narani 	}
17784770f02SManish Narani 
17884770f02SManish Narani 	ret = reset_control_deassert(apbrst);
17984770f02SManish Narani 	if (ret < 0) {
18084770f02SManish Narani 		dev_err(dev, "Failed to release APB reset\n");
18184770f02SManish Narani 		goto err;
18284770f02SManish Narani 	}
18384770f02SManish Narani 
18484770f02SManish Narani 	/* Set PIPE Power Present signal in FPD Power Present Register*/
18584770f02SManish Narani 	writel(FPD_POWER_PRSNT_OPTION, priv_data->regs + XLNX_USB_FPD_POWER_PRSNT);
18684770f02SManish Narani 
18784770f02SManish Narani 	/* Set the PIPE Clock Select bit in FPD PIPE Clock register */
18884770f02SManish Narani 	writel(PIPE_CLK_SELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK);
18984770f02SManish Narani 
19084770f02SManish Narani 	ret = reset_control_deassert(crst);
19184770f02SManish Narani 	if (ret < 0) {
19284770f02SManish Narani 		dev_err(dev, "Failed to release core reset\n");
19384770f02SManish Narani 		goto err;
19484770f02SManish Narani 	}
19584770f02SManish Narani 
19684770f02SManish Narani 	ret = reset_control_deassert(hibrst);
19784770f02SManish Narani 	if (ret < 0) {
19884770f02SManish Narani 		dev_err(dev, "Failed to release hibernation reset\n");
19984770f02SManish Narani 		goto err;
20084770f02SManish Narani 	}
20184770f02SManish Narani 
202d6edcdc1SPiyush Mehta 	ret = phy_power_on(priv_data->usb3_phy);
20384770f02SManish Narani 	if (ret < 0) {
204d6edcdc1SPiyush Mehta 		phy_exit(priv_data->usb3_phy);
20584770f02SManish Narani 		goto err;
20684770f02SManish Narani 	}
20784770f02SManish Narani 
2089678f336SRobert Hancock skip_usb3_phy:
209ca05b382SPiyush Mehta 	/* ulpi reset via gpio-modepin or gpio-framework driver */
210ca05b382SPiyush Mehta 	reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
211ca05b382SPiyush Mehta 	if (IS_ERR(reset_gpio)) {
212ca05b382SPiyush Mehta 		return dev_err_probe(dev, PTR_ERR(reset_gpio),
213ca05b382SPiyush Mehta 				     "Failed to request reset GPIO\n");
214ca05b382SPiyush Mehta 	}
215ca05b382SPiyush Mehta 
216ca05b382SPiyush Mehta 	if (reset_gpio) {
217ca05b382SPiyush Mehta 		/* Toggle ulpi to reset the phy. */
218ca05b382SPiyush Mehta 		gpiod_set_value_cansleep(reset_gpio, 1);
219ca05b382SPiyush Mehta 		usleep_range(5000, 10000);
220ca05b382SPiyush Mehta 		gpiod_set_value_cansleep(reset_gpio, 0);
221ca05b382SPiyush Mehta 		usleep_range(5000, 10000);
222ca05b382SPiyush Mehta 	}
223ca05b382SPiyush Mehta 
22484770f02SManish Narani 	/*
22584770f02SManish Narani 	 * This routes the USB DMA traffic to go through FPD path instead
22684770f02SManish Narani 	 * of reaching DDR directly. This traffic routing is needed to
22784770f02SManish Narani 	 * make SMMU and CCI work with USB DMA.
22884770f02SManish Narani 	 */
22984770f02SManish Narani 	if (of_dma_is_coherent(dev->of_node) || device_iommu_mapped(dev)) {
23084770f02SManish Narani 		reg = readl(priv_data->regs + XLNX_USB_TRAFFIC_ROUTE_CONFIG);
23184770f02SManish Narani 		reg |= XLNX_USB_TRAFFIC_ROUTE_FPD;
23284770f02SManish Narani 		writel(reg, priv_data->regs + XLNX_USB_TRAFFIC_ROUTE_CONFIG);
23384770f02SManish Narani 	}
23484770f02SManish Narani 
23584770f02SManish Narani err:
23684770f02SManish Narani 	return ret;
23784770f02SManish Narani }
23884770f02SManish Narani 
23984770f02SManish Narani static const struct of_device_id dwc3_xlnx_of_match[] = {
24084770f02SManish Narani 	{
24184770f02SManish Narani 		.compatible = "xlnx,zynqmp-dwc3",
24284770f02SManish Narani 		.data = &dwc3_xlnx_init_zynqmp,
24384770f02SManish Narani 	},
24484770f02SManish Narani 	{
24584770f02SManish Narani 		.compatible = "xlnx,versal-dwc3",
24684770f02SManish Narani 		.data = &dwc3_xlnx_init_versal,
24784770f02SManish Narani 	},
24884770f02SManish Narani 	{ /* Sentinel */ }
24984770f02SManish Narani };
25084770f02SManish Narani MODULE_DEVICE_TABLE(of, dwc3_xlnx_of_match);
25184770f02SManish Narani 
dwc3_xlnx_probe(struct platform_device * pdev)25284770f02SManish Narani static int dwc3_xlnx_probe(struct platform_device *pdev)
25384770f02SManish Narani {
25484770f02SManish Narani 	struct dwc3_xlnx		*priv_data;
25584770f02SManish Narani 	struct device			*dev = &pdev->dev;
25684770f02SManish Narani 	struct device_node		*np = dev->of_node;
25784770f02SManish Narani 	const struct of_device_id	*match;
25884770f02SManish Narani 	void __iomem			*regs;
25984770f02SManish Narani 	int				ret;
26084770f02SManish Narani 
26184770f02SManish Narani 	priv_data = devm_kzalloc(dev, sizeof(*priv_data), GFP_KERNEL);
26284770f02SManish Narani 	if (!priv_data)
26384770f02SManish Narani 		return -ENOMEM;
26484770f02SManish Narani 
26584770f02SManish Narani 	regs = devm_platform_ioremap_resource(pdev, 0);
26684770f02SManish Narani 	if (IS_ERR(regs)) {
26784770f02SManish Narani 		ret = PTR_ERR(regs);
26884770f02SManish Narani 		dev_err_probe(dev, ret, "failed to map registers\n");
26984770f02SManish Narani 		return ret;
27084770f02SManish Narani 	}
27184770f02SManish Narani 
27284770f02SManish Narani 	match = of_match_node(dwc3_xlnx_of_match, pdev->dev.of_node);
27384770f02SManish Narani 
27484770f02SManish Narani 	priv_data->pltfm_init = match->data;
27584770f02SManish Narani 	priv_data->regs = regs;
27684770f02SManish Narani 	priv_data->dev = dev;
27784770f02SManish Narani 
27884770f02SManish Narani 	platform_set_drvdata(pdev, priv_data);
27984770f02SManish Narani 
28084770f02SManish Narani 	ret = devm_clk_bulk_get_all(priv_data->dev, &priv_data->clks);
28184770f02SManish Narani 	if (ret < 0)
28284770f02SManish Narani 		return ret;
28384770f02SManish Narani 
28484770f02SManish Narani 	priv_data->num_clocks = ret;
28584770f02SManish Narani 
28684770f02SManish Narani 	ret = clk_bulk_prepare_enable(priv_data->num_clocks, priv_data->clks);
28784770f02SManish Narani 	if (ret)
28884770f02SManish Narani 		return ret;
28984770f02SManish Narani 
29084770f02SManish Narani 	ret = priv_data->pltfm_init(priv_data);
29184770f02SManish Narani 	if (ret)
29284770f02SManish Narani 		goto err_clk_put;
29384770f02SManish Narani 
29484770f02SManish Narani 	ret = of_platform_populate(np, NULL, NULL, dev);
29584770f02SManish Narani 	if (ret)
29684770f02SManish Narani 		goto err_clk_put;
29784770f02SManish Narani 
29884770f02SManish Narani 	pm_runtime_set_active(dev);
29984770f02SManish Narani 	pm_runtime_enable(dev);
30084770f02SManish Narani 	pm_suspend_ignore_children(dev, false);
30184770f02SManish Narani 	pm_runtime_get_sync(dev);
30284770f02SManish Narani 
30384770f02SManish Narani 	return 0;
30484770f02SManish Narani 
30584770f02SManish Narani err_clk_put:
30684770f02SManish Narani 	clk_bulk_disable_unprepare(priv_data->num_clocks, priv_data->clks);
30784770f02SManish Narani 
30884770f02SManish Narani 	return ret;
30984770f02SManish Narani }
31084770f02SManish Narani 
dwc3_xlnx_remove(struct platform_device * pdev)3115b3eb973SUwe Kleine-König static void dwc3_xlnx_remove(struct platform_device *pdev)
31284770f02SManish Narani {
31384770f02SManish Narani 	struct dwc3_xlnx	*priv_data = platform_get_drvdata(pdev);
31484770f02SManish Narani 	struct device		*dev = &pdev->dev;
31584770f02SManish Narani 
31684770f02SManish Narani 	of_platform_depopulate(dev);
31784770f02SManish Narani 
31884770f02SManish Narani 	clk_bulk_disable_unprepare(priv_data->num_clocks, priv_data->clks);
31984770f02SManish Narani 	priv_data->num_clocks = 0;
32084770f02SManish Narani 
32184770f02SManish Narani 	pm_runtime_disable(dev);
32284770f02SManish Narani 	pm_runtime_put_noidle(dev);
32384770f02SManish Narani 	pm_runtime_set_suspended(dev);
32484770f02SManish Narani }
32584770f02SManish Narani 
dwc3_xlnx_runtime_suspend(struct device * dev)326ec50e114SPiyush Mehta static int __maybe_unused dwc3_xlnx_runtime_suspend(struct device *dev)
32784770f02SManish Narani {
32884770f02SManish Narani 	struct dwc3_xlnx *priv_data = dev_get_drvdata(dev);
32984770f02SManish Narani 
33084770f02SManish Narani 	clk_bulk_disable(priv_data->num_clocks, priv_data->clks);
33184770f02SManish Narani 
33284770f02SManish Narani 	return 0;
33384770f02SManish Narani }
33484770f02SManish Narani 
dwc3_xlnx_runtime_resume(struct device * dev)335ec50e114SPiyush Mehta static int __maybe_unused dwc3_xlnx_runtime_resume(struct device *dev)
33684770f02SManish Narani {
33784770f02SManish Narani 	struct dwc3_xlnx *priv_data = dev_get_drvdata(dev);
33884770f02SManish Narani 
33984770f02SManish Narani 	return clk_bulk_enable(priv_data->num_clocks, priv_data->clks);
34084770f02SManish Narani }
34184770f02SManish Narani 
dwc3_xlnx_runtime_idle(struct device * dev)34284770f02SManish Narani static int __maybe_unused dwc3_xlnx_runtime_idle(struct device *dev)
34384770f02SManish Narani {
34484770f02SManish Narani 	pm_runtime_mark_last_busy(dev);
34584770f02SManish Narani 	pm_runtime_autosuspend(dev);
34684770f02SManish Narani 
34784770f02SManish Narani 	return 0;
34884770f02SManish Narani }
34984770f02SManish Narani 
dwc3_xlnx_suspend(struct device * dev)350ec50e114SPiyush Mehta static int __maybe_unused dwc3_xlnx_suspend(struct device *dev)
351ec50e114SPiyush Mehta {
352ec50e114SPiyush Mehta 	struct dwc3_xlnx *priv_data = dev_get_drvdata(dev);
353ec50e114SPiyush Mehta 
354d6edcdc1SPiyush Mehta 	phy_exit(priv_data->usb3_phy);
355d6edcdc1SPiyush Mehta 
356ec50e114SPiyush Mehta 	/* Disable the clocks */
357ec50e114SPiyush Mehta 	clk_bulk_disable(priv_data->num_clocks, priv_data->clks);
358ec50e114SPiyush Mehta 
359ec50e114SPiyush Mehta 	return 0;
360ec50e114SPiyush Mehta }
361ec50e114SPiyush Mehta 
dwc3_xlnx_resume(struct device * dev)362ec50e114SPiyush Mehta static int __maybe_unused dwc3_xlnx_resume(struct device *dev)
363ec50e114SPiyush Mehta {
364ec50e114SPiyush Mehta 	struct dwc3_xlnx *priv_data = dev_get_drvdata(dev);
365ec50e114SPiyush Mehta 	int ret;
366ec50e114SPiyush Mehta 
367ec50e114SPiyush Mehta 	ret = clk_bulk_enable(priv_data->num_clocks, priv_data->clks);
368ec50e114SPiyush Mehta 	if (ret)
369ec50e114SPiyush Mehta 		return ret;
370ec50e114SPiyush Mehta 
371d6edcdc1SPiyush Mehta 	ret = phy_init(priv_data->usb3_phy);
372d6edcdc1SPiyush Mehta 	if (ret < 0)
373d6edcdc1SPiyush Mehta 		return ret;
374d6edcdc1SPiyush Mehta 
375d6edcdc1SPiyush Mehta 	ret = phy_power_on(priv_data->usb3_phy);
376d6edcdc1SPiyush Mehta 	if (ret < 0) {
377d6edcdc1SPiyush Mehta 		phy_exit(priv_data->usb3_phy);
378d6edcdc1SPiyush Mehta 		return ret;
379d6edcdc1SPiyush Mehta 	}
380d6edcdc1SPiyush Mehta 
381ec50e114SPiyush Mehta 	return 0;
382ec50e114SPiyush Mehta }
383ec50e114SPiyush Mehta 
384ec50e114SPiyush Mehta static const struct dev_pm_ops dwc3_xlnx_dev_pm_ops = {
385ec50e114SPiyush Mehta 	SET_SYSTEM_SLEEP_PM_OPS(dwc3_xlnx_suspend, dwc3_xlnx_resume)
386ec50e114SPiyush Mehta 	SET_RUNTIME_PM_OPS(dwc3_xlnx_runtime_suspend,
387ec50e114SPiyush Mehta 			   dwc3_xlnx_runtime_resume, dwc3_xlnx_runtime_idle)
388ec50e114SPiyush Mehta };
38984770f02SManish Narani 
39084770f02SManish Narani static struct platform_driver dwc3_xlnx_driver = {
39184770f02SManish Narani 	.probe		= dwc3_xlnx_probe,
3925b3eb973SUwe Kleine-König 	.remove_new	= dwc3_xlnx_remove,
39384770f02SManish Narani 	.driver		= {
39484770f02SManish Narani 		.name		= "dwc3-xilinx",
39584770f02SManish Narani 		.of_match_table	= dwc3_xlnx_of_match,
39684770f02SManish Narani 		.pm		= &dwc3_xlnx_dev_pm_ops,
39784770f02SManish Narani 	},
39884770f02SManish Narani };
39984770f02SManish Narani 
40084770f02SManish Narani module_platform_driver(dwc3_xlnx_driver);
40184770f02SManish Narani 
40284770f02SManish Narani MODULE_LICENSE("GPL v2");
40384770f02SManish Narani MODULE_DESCRIPTION("Xilinx DWC3 controller specific glue driver");
40484770f02SManish Narani MODULE_AUTHOR("Manish Narani <manish.narani@xilinx.com>");
40584770f02SManish Narani MODULE_AUTHOR("Anurag Kumar Vulisha <anurag.kumar.vulisha@xilinx.com>");
406