brintos

brintos / linux-shallow public Read only

0
0
Text · 15.5 KiB · 06392ed Raw
567 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2 3#include <linux/clk.h>4#include <linux/err.h>5#include <linux/io.h>6#include <linux/module.h>7#include <linux/of.h>8#include <linux/phy/phy.h>9#include <linux/platform_device.h>10#include <linux/delay.h>11#include <linux/regmap.h>12#include <linux/mfd/syscon.h>13#include <linux/bitfield.h>14 15/* USB QSCRATCH Hardware registers */16#define QSCRATCH_GENERAL_CFG		(0x08)17#define HSUSB_PHY_CTRL_REG		(0x10)18 19/* PHY_CTRL_REG */20#define HSUSB_CTRL_DMSEHV_CLAMP		BIT(24)21#define HSUSB_CTRL_USB2_SUSPEND		BIT(23)22#define HSUSB_CTRL_UTMI_CLK_EN		BIT(21)23#define HSUSB_CTRL_UTMI_OTG_VBUS_VALID	BIT(20)24#define HSUSB_CTRL_USE_CLKCORE		BIT(18)25#define HSUSB_CTRL_DPSEHV_CLAMP		BIT(17)26#define HSUSB_CTRL_COMMONONN		BIT(11)27#define HSUSB_CTRL_ID_HV_CLAMP		BIT(9)28#define HSUSB_CTRL_OTGSESSVLD_CLAMP	BIT(8)29#define HSUSB_CTRL_CLAMP_EN		BIT(7)30#define HSUSB_CTRL_RETENABLEN		BIT(1)31#define HSUSB_CTRL_POR			BIT(0)32 33/* QSCRATCH_GENERAL_CFG */34#define HSUSB_GCFG_XHCI_REV		BIT(2)35 36/* USB QSCRATCH Hardware registers */37#define SSUSB_PHY_CTRL_REG		(0x00)38#define SSUSB_PHY_PARAM_CTRL_1		(0x04)39#define SSUSB_PHY_PARAM_CTRL_2		(0x08)40#define CR_PROTOCOL_DATA_IN_REG		(0x0c)41#define CR_PROTOCOL_DATA_OUT_REG	(0x10)42#define CR_PROTOCOL_CAP_ADDR_REG	(0x14)43#define CR_PROTOCOL_CAP_DATA_REG	(0x18)44#define CR_PROTOCOL_READ_REG		(0x1c)45#define CR_PROTOCOL_WRITE_REG		(0x20)46 47/* PHY_CTRL_REG */48#define SSUSB_CTRL_REF_USE_PAD		BIT(28)49#define SSUSB_CTRL_TEST_POWERDOWN	BIT(27)50#define SSUSB_CTRL_LANE0_PWR_PRESENT	BIT(24)51#define SSUSB_CTRL_SS_PHY_EN		BIT(8)52#define SSUSB_CTRL_SS_PHY_RESET		BIT(7)53 54/* SSPHY control registers - Does this need 0x30? */55#define SSPHY_CTRL_RX_OVRD_IN_HI(lane)	(0x1006 + 0x100 * (lane))56#define SSPHY_CTRL_TX_OVRD_DRV_LO(lane)	(0x1002 + 0x100 * (lane))57 58/* SSPHY SoC version specific values */59#define SSPHY_RX_EQ_VALUE		4 /* Override value for rx_eq */60/* Override value for transmit preemphasis */61#define SSPHY_TX_DEEMPH_3_5DB		2362/* Override value for mpll */63#define SSPHY_MPLL_VALUE		064 65/* QSCRATCH PHY_PARAM_CTRL1 fields */66#define PHY_PARAM_CTRL1_TX_FULL_SWING_MASK	GENMASK(26, 19)67#define PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK	GENMASK(19, 13)68#define PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK	GENMASK(13, 7)69#define PHY_PARAM_CTRL1_LOS_BIAS_MASK		GENMASK(7, 2)70 71#define PHY_PARAM_CTRL1_MASK				\72		(PHY_PARAM_CTRL1_TX_FULL_SWING_MASK |	\73		 PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK |	\74		 PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK |	\75		 PHY_PARAM_CTRL1_LOS_BIAS_MASK)76 77#define PHY_PARAM_CTRL1_TX_FULL_SWING(x)	\78		FIELD_PREP(PHY_PARAM_CTRL1_TX_FULL_SWING_MASK, (x))79#define PHY_PARAM_CTRL1_TX_DEEMPH_6DB(x)	\80		FIELD_PREP(PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK, (x))81#define PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB(x)	\82		FIELD_PREP(PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK, x)83#define PHY_PARAM_CTRL1_LOS_BIAS(x)	\84		FIELD_PREP(PHY_PARAM_CTRL1_LOS_BIAS_MASK, (x))85 86/* RX OVRD IN HI bits */87#define RX_OVRD_IN_HI_RX_RESET_OVRD		BIT(13)88#define RX_OVRD_IN_HI_RX_RX_RESET		BIT(12)89#define RX_OVRD_IN_HI_RX_EQ_OVRD		BIT(11)90#define RX_OVRD_IN_HI_RX_EQ_MASK		GENMASK(10, 7)91#define RX_OVRD_IN_HI_RX_EQ(x)			FIELD_PREP(RX_OVRD_IN_HI_RX_EQ_MASK, (x))92#define RX_OVRD_IN_HI_RX_EQ_EN_OVRD		BIT(7)93#define RX_OVRD_IN_HI_RX_EQ_EN			BIT(6)94#define RX_OVRD_IN_HI_RX_LOS_FILTER_OVRD	BIT(5)95#define RX_OVRD_IN_HI_RX_LOS_FILTER_MASK	GENMASK(4, 2)96#define RX_OVRD_IN_HI_RX_RATE_OVRD		BIT(2)97#define RX_OVRD_IN_HI_RX_RATE_MASK		GENMASK(2, 0)98 99/* TX OVRD DRV LO register bits */100#define TX_OVRD_DRV_LO_AMPLITUDE_MASK		GENMASK(6, 0)101#define TX_OVRD_DRV_LO_PREEMPH_MASK		GENMASK(13, 6)102#define TX_OVRD_DRV_LO_PREEMPH(x)		((x) << 7)103#define TX_OVRD_DRV_LO_EN			BIT(14)104 105/* MPLL bits */106#define SSPHY_MPLL_MASK				GENMASK(8, 5)107#define SSPHY_MPLL(x)				((x) << 5)108 109/* SS CAP register bits */110#define SS_CR_CAP_ADDR_REG			BIT(0)111#define SS_CR_CAP_DATA_REG			BIT(0)112#define SS_CR_READ_REG				BIT(0)113#define SS_CR_WRITE_REG				BIT(0)114 115#define LATCH_SLEEP				40116#define LATCH_TIMEOUT				100117 118struct usb_phy {119	void __iomem		*base;120	struct device		*dev;121	struct clk		*xo_clk;122	struct clk		*ref_clk;123	u32			rx_eq;124	u32			tx_deamp_3_5db;125	u32			mpll;126};127 128struct phy_drvdata {129	struct phy_ops	ops;130	u32		clk_rate;131};132 133/**134 * usb_phy_write_readback() - Write register and read back masked value to135 * confirm it is written136 *137 * @phy_dwc3: QCOM DWC3 phy context138 * @offset: register offset.139 * @mask: register bitmask specifying what should be updated140 * @val: value to write.141 */142static inline void usb_phy_write_readback(struct usb_phy *phy_dwc3,143					  u32 offset,144					  const u32 mask, u32 val)145{146	u32 write_val, tmp = readl(phy_dwc3->base + offset);147 148	tmp &= ~mask;		/* retain other bits */149	write_val = tmp | val;150 151	writel(write_val, phy_dwc3->base + offset);152 153	/* Read back to see if val was written */154	tmp = readl(phy_dwc3->base + offset);155	tmp &= mask;		/* clear other bits */156 157	if (tmp != val)158		dev_err(phy_dwc3->dev, "write: %x to QSCRATCH: %x FAILED\n", val, offset);159}160 161static int wait_for_latch(void __iomem *addr)162{163	u32 val;164 165	return readl_poll_timeout(addr, val, !val, LATCH_SLEEP, LATCH_TIMEOUT);166}167 168/**169 * usb_ss_write_phycreg() - Write SSPHY register170 *171 * @phy_dwc3: QCOM DWC3 phy context172 * @addr: SSPHY address to write.173 * @val: value to write.174 */175static int usb_ss_write_phycreg(struct usb_phy *phy_dwc3,176				u32 addr, u32 val)177{178	int ret;179 180	writel(addr, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG);181	writel(SS_CR_CAP_ADDR_REG,182	       phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG);183 184	ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG);185	if (ret)186		goto err_wait;187 188	writel(val, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG);189	writel(SS_CR_CAP_DATA_REG,190	       phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG);191 192	ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG);193	if (ret)194		goto err_wait;195 196	writel(SS_CR_WRITE_REG, phy_dwc3->base + CR_PROTOCOL_WRITE_REG);197 198	ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_WRITE_REG);199 200err_wait:201	if (ret)202		dev_err(phy_dwc3->dev, "timeout waiting for latch\n");203	return ret;204}205 206/**207 * usb_ss_read_phycreg() - Read SSPHY register.208 *209 * @phy_dwc3: QCOM DWC3 phy context210 * @addr: SSPHY address to read.211 * @val: pointer in which read is store.212 */213static int usb_ss_read_phycreg(struct usb_phy *phy_dwc3,214			       u32 addr, u32 *val)215{216	int ret;217 218	writel(addr, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG);219	writel(SS_CR_CAP_ADDR_REG,220	       phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG);221 222	ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG);223	if (ret)224		goto err_wait;225 226	/*227	 * Due to hardware bug, first read of SSPHY register might be228	 * incorrect. Hence as workaround, SW should perform SSPHY register229	 * read twice, but use only second read and ignore first read.230	 */231	writel(SS_CR_READ_REG, phy_dwc3->base + CR_PROTOCOL_READ_REG);232 233	ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_READ_REG);234	if (ret)235		goto err_wait;236 237	/* throwaway read */238	readl(phy_dwc3->base + CR_PROTOCOL_DATA_OUT_REG);239 240	writel(SS_CR_READ_REG, phy_dwc3->base + CR_PROTOCOL_READ_REG);241 242	ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_READ_REG);243	if (ret)244		goto err_wait;245 246	*val = readl(phy_dwc3->base + CR_PROTOCOL_DATA_OUT_REG);247 248err_wait:249	return ret;250}251 252static int qcom_ipq806x_usb_hs_phy_init(struct phy *phy)253{254	struct usb_phy *phy_dwc3 = phy_get_drvdata(phy);255	int ret;256	u32 val;257 258	ret = clk_prepare_enable(phy_dwc3->xo_clk);259	if (ret)260		return ret;261 262	ret = clk_prepare_enable(phy_dwc3->ref_clk);263	if (ret) {264		clk_disable_unprepare(phy_dwc3->xo_clk);265		return ret;266	}267 268	/*269	 * HSPHY Initialization: Enable UTMI clock, select 19.2MHz fsel270	 * enable clamping, and disable RETENTION (power-on default is ENABLED)271	 */272	val = HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_DMSEHV_CLAMP |273		HSUSB_CTRL_RETENABLEN  | HSUSB_CTRL_COMMONONN |274		HSUSB_CTRL_OTGSESSVLD_CLAMP | HSUSB_CTRL_ID_HV_CLAMP |275		HSUSB_CTRL_UTMI_OTG_VBUS_VALID | HSUSB_CTRL_UTMI_CLK_EN |276		HSUSB_CTRL_CLAMP_EN | 0x70;277 278	/* use core clock if external reference is not present */279	if (!phy_dwc3->xo_clk)280		val |= HSUSB_CTRL_USE_CLKCORE;281 282	writel(val, phy_dwc3->base + HSUSB_PHY_CTRL_REG);283	usleep_range(2000, 2200);284 285	/* Disable (bypass) VBUS and ID filters */286	writel(HSUSB_GCFG_XHCI_REV, phy_dwc3->base + QSCRATCH_GENERAL_CFG);287 288	return 0;289}290 291static int qcom_ipq806x_usb_hs_phy_exit(struct phy *phy)292{293	struct usb_phy *phy_dwc3 = phy_get_drvdata(phy);294 295	clk_disable_unprepare(phy_dwc3->ref_clk);296	clk_disable_unprepare(phy_dwc3->xo_clk);297 298	return 0;299}300 301static int qcom_ipq806x_usb_ss_phy_init(struct phy *phy)302{303	struct usb_phy *phy_dwc3 = phy_get_drvdata(phy);304	int ret;305	u32 data;306 307	ret = clk_prepare_enable(phy_dwc3->xo_clk);308	if (ret)309		return ret;310 311	ret = clk_prepare_enable(phy_dwc3->ref_clk);312	if (ret) {313		clk_disable_unprepare(phy_dwc3->xo_clk);314		return ret;315	}316 317	/* reset phy */318	data = readl(phy_dwc3->base + SSUSB_PHY_CTRL_REG);319	writel(data | SSUSB_CTRL_SS_PHY_RESET,320	       phy_dwc3->base + SSUSB_PHY_CTRL_REG);321	usleep_range(2000, 2200);322	writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);323 324	/* clear REF_PAD if we don't have XO clk */325	if (!phy_dwc3->xo_clk)326		data &= ~SSUSB_CTRL_REF_USE_PAD;327	else328		data |= SSUSB_CTRL_REF_USE_PAD;329 330	writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);331 332	/* wait for ref clk to become stable, this can take up to 30ms */333	msleep(30);334 335	data |= SSUSB_CTRL_SS_PHY_EN | SSUSB_CTRL_LANE0_PWR_PRESENT;336	writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);337 338	/*339	 * WORKAROUND: There is SSPHY suspend bug due to which USB enumerates340	 * in HS mode instead of SS mode. Workaround it by asserting341	 * LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use alt bus mode342	 */343	ret = usb_ss_read_phycreg(phy_dwc3, 0x102D, &data);344	if (ret)345		goto err_phy_trans;346 347	data |= (1 << 7);348	ret = usb_ss_write_phycreg(phy_dwc3, 0x102D, data);349	if (ret)350		goto err_phy_trans;351 352	ret = usb_ss_read_phycreg(phy_dwc3, 0x1010, &data);353	if (ret)354		goto err_phy_trans;355 356	data &= ~0xff0;357	data |= 0x20;358	ret = usb_ss_write_phycreg(phy_dwc3, 0x1010, data);359	if (ret)360		goto err_phy_trans;361 362	/*363	 * Fix RX Equalization setting as follows364	 * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0365	 * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1366	 * LANE0.RX_OVRD_IN_HI.RX_EQ set based on SoC version367	 * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1368	 */369	ret = usb_ss_read_phycreg(phy_dwc3, SSPHY_CTRL_RX_OVRD_IN_HI(0), &data);370	if (ret)371		goto err_phy_trans;372 373	data &= ~RX_OVRD_IN_HI_RX_EQ_EN;374	data |= RX_OVRD_IN_HI_RX_EQ_EN_OVRD;375	data &= ~RX_OVRD_IN_HI_RX_EQ_MASK;376	data |= RX_OVRD_IN_HI_RX_EQ(phy_dwc3->rx_eq);377	data |= RX_OVRD_IN_HI_RX_EQ_OVRD;378	ret = usb_ss_write_phycreg(phy_dwc3,379				   SSPHY_CTRL_RX_OVRD_IN_HI(0), data);380	if (ret)381		goto err_phy_trans;382 383	/*384	 * Set EQ and TX launch amplitudes as follows385	 * LANE0.TX_OVRD_DRV_LO.PREEMPH set based on SoC version386	 * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 110387	 * LANE0.TX_OVRD_DRV_LO.EN set to 1.388	 */389	ret = usb_ss_read_phycreg(phy_dwc3,390				  SSPHY_CTRL_TX_OVRD_DRV_LO(0), &data);391	if (ret)392		goto err_phy_trans;393 394	data &= ~TX_OVRD_DRV_LO_PREEMPH_MASK;395	data |= TX_OVRD_DRV_LO_PREEMPH(phy_dwc3->tx_deamp_3_5db);396	data &= ~TX_OVRD_DRV_LO_AMPLITUDE_MASK;397	data |= 0x6E;398	data |= TX_OVRD_DRV_LO_EN;399	ret = usb_ss_write_phycreg(phy_dwc3,400				   SSPHY_CTRL_TX_OVRD_DRV_LO(0), data);401	if (ret)402		goto err_phy_trans;403 404	data = 0;405	data &= ~SSPHY_MPLL_MASK;406	data |= SSPHY_MPLL(phy_dwc3->mpll);407	usb_ss_write_phycreg(phy_dwc3, 0x30, data);408 409	/*410	 * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows411	 * TX_FULL_SWING [26:20] amplitude to 110412	 * TX_DEEMPH_6DB [19:14] to 32413	 * TX_DEEMPH_3_5DB [13:8] set based on SoC version414	 * LOS_BIAS [7:3] to 9415	 */416	data = readl(phy_dwc3->base + SSUSB_PHY_PARAM_CTRL_1);417 418	data &= ~PHY_PARAM_CTRL1_MASK;419 420	data |= PHY_PARAM_CTRL1_TX_FULL_SWING(0x6e) |421		PHY_PARAM_CTRL1_TX_DEEMPH_6DB(0x20) |422		PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB(phy_dwc3->tx_deamp_3_5db) |423		PHY_PARAM_CTRL1_LOS_BIAS(0x9);424 425	usb_phy_write_readback(phy_dwc3, SSUSB_PHY_PARAM_CTRL_1,426			       PHY_PARAM_CTRL1_MASK, data);427 428err_phy_trans:429	return ret;430}431 432static int qcom_ipq806x_usb_ss_phy_exit(struct phy *phy)433{434	struct usb_phy *phy_dwc3 = phy_get_drvdata(phy);435 436	/* Sequence to put SSPHY in low power state:437	 * 1. Clear REF_PHY_EN in PHY_CTRL_REG438	 * 2. Clear REF_USE_PAD in PHY_CTRL_REG439	 * 3. Set TEST_POWERED_DOWN in PHY_CTRL_REG to enable PHY retention440	 */441	usb_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,442			       SSUSB_CTRL_SS_PHY_EN, 0x0);443	usb_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,444			       SSUSB_CTRL_REF_USE_PAD, 0x0);445	usb_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,446			       SSUSB_CTRL_TEST_POWERDOWN, 0x0);447 448	clk_disable_unprepare(phy_dwc3->ref_clk);449	clk_disable_unprepare(phy_dwc3->xo_clk);450 451	return 0;452}453 454static const struct phy_drvdata qcom_ipq806x_usb_hs_drvdata = {455	.ops = {456		.init		= qcom_ipq806x_usb_hs_phy_init,457		.exit		= qcom_ipq806x_usb_hs_phy_exit,458		.owner		= THIS_MODULE,459	},460	.clk_rate = 60000000,461};462 463static const struct phy_drvdata qcom_ipq806x_usb_ss_drvdata = {464	.ops = {465		.init		= qcom_ipq806x_usb_ss_phy_init,466		.exit		= qcom_ipq806x_usb_ss_phy_exit,467		.owner		= THIS_MODULE,468	},469	.clk_rate = 125000000,470};471 472static const struct of_device_id qcom_ipq806x_usb_phy_table[] = {473	{ .compatible = "qcom,ipq806x-usb-phy-hs",474	  .data = &qcom_ipq806x_usb_hs_drvdata },475	{ .compatible = "qcom,ipq806x-usb-phy-ss",476	  .data = &qcom_ipq806x_usb_ss_drvdata },477	{ /* Sentinel */ }478};479MODULE_DEVICE_TABLE(of, qcom_ipq806x_usb_phy_table);480 481static int qcom_ipq806x_usb_phy_probe(struct platform_device *pdev)482{483	struct resource *res;484	resource_size_t size;485	struct phy *generic_phy;486	struct usb_phy *phy_dwc3;487	const struct phy_drvdata *data;488	struct phy_provider *phy_provider;489 490	phy_dwc3 = devm_kzalloc(&pdev->dev, sizeof(*phy_dwc3), GFP_KERNEL);491	if (!phy_dwc3)492		return -ENOMEM;493 494	data = of_device_get_match_data(&pdev->dev);495 496	phy_dwc3->dev = &pdev->dev;497 498	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);499	if (!res)500		return -EINVAL;501	size = resource_size(res);502	phy_dwc3->base = devm_ioremap(phy_dwc3->dev, res->start, size);503 504	if (!phy_dwc3->base) {505		dev_err(phy_dwc3->dev, "failed to map reg\n");506		return -ENOMEM;507	}508 509	phy_dwc3->ref_clk = devm_clk_get(phy_dwc3->dev, "ref");510	if (IS_ERR(phy_dwc3->ref_clk)) {511		dev_dbg(phy_dwc3->dev, "cannot get reference clock\n");512		return PTR_ERR(phy_dwc3->ref_clk);513	}514 515	clk_set_rate(phy_dwc3->ref_clk, data->clk_rate);516 517	phy_dwc3->xo_clk = devm_clk_get(phy_dwc3->dev, "xo");518	if (IS_ERR(phy_dwc3->xo_clk)) {519		dev_dbg(phy_dwc3->dev, "cannot get TCXO clock\n");520		phy_dwc3->xo_clk = NULL;521	}522 523	/* Parse device node to probe HSIO settings */524	if (device_property_read_u32(&pdev->dev, "qcom,rx-eq",525				     &phy_dwc3->rx_eq))526		phy_dwc3->rx_eq = SSPHY_RX_EQ_VALUE;527 528	if (device_property_read_u32(&pdev->dev, "qcom,tx-deamp_3_5db",529				     &phy_dwc3->tx_deamp_3_5db))530		phy_dwc3->tx_deamp_3_5db = SSPHY_TX_DEEMPH_3_5DB;531 532	if (device_property_read_u32(&pdev->dev, "qcom,mpll", &phy_dwc3->mpll))533		phy_dwc3->mpll = SSPHY_MPLL_VALUE;534 535	generic_phy = devm_phy_create(phy_dwc3->dev, pdev->dev.of_node, &data->ops);536 537	if (IS_ERR(generic_phy))538		return PTR_ERR(generic_phy);539 540	phy_set_drvdata(generic_phy, phy_dwc3);541	platform_set_drvdata(pdev, phy_dwc3);542 543	phy_provider = devm_of_phy_provider_register(phy_dwc3->dev,544						     of_phy_simple_xlate);545 546	if (IS_ERR(phy_provider))547		return PTR_ERR(phy_provider);548 549	return 0;550}551 552static struct platform_driver qcom_ipq806x_usb_phy_driver = {553	.probe		= qcom_ipq806x_usb_phy_probe,554	.driver		= {555		.name	= "qcom-ipq806x-usb-phy",556		.of_match_table = qcom_ipq806x_usb_phy_table,557	},558};559 560module_platform_driver(qcom_ipq806x_usb_phy_driver);561 562MODULE_ALIAS("platform:phy-qcom-ipq806x-usb");563MODULE_LICENSE("GPL v2");564MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");565MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");566MODULE_DESCRIPTION("DesignWare USB3 QCOM PHY driver");567