xref: /linux/drivers/phy/qualcomm/phy-qcom-usb-hs-28nm.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
167b27dbeSShawn Guo // SPDX-License-Identifier: GPL-2.0
267b27dbeSShawn Guo /*
367b27dbeSShawn Guo  * Copyright (c) 2009-2018, Linux Foundation. All rights reserved.
467b27dbeSShawn Guo  * Copyright (c) 2018-2020, Linaro Limited
567b27dbeSShawn Guo  */
667b27dbeSShawn Guo 
767b27dbeSShawn Guo #include <linux/clk.h>
867b27dbeSShawn Guo #include <linux/delay.h>
967b27dbeSShawn Guo #include <linux/io.h>
1067b27dbeSShawn Guo #include <linux/kernel.h>
1167b27dbeSShawn Guo #include <linux/module.h>
1267b27dbeSShawn Guo #include <linux/of.h>
1367b27dbeSShawn Guo #include <linux/of_graph.h>
1467b27dbeSShawn Guo #include <linux/phy/phy.h>
1567b27dbeSShawn Guo #include <linux/platform_device.h>
1667b27dbeSShawn Guo #include <linux/regulator/consumer.h>
1767b27dbeSShawn Guo #include <linux/reset.h>
1867b27dbeSShawn Guo #include <linux/slab.h>
1967b27dbeSShawn Guo 
2067b27dbeSShawn Guo /* PHY register and bit definitions */
2167b27dbeSShawn Guo #define PHY_CTRL_COMMON0		0x078
2267b27dbeSShawn Guo #define SIDDQ				BIT(2)
2367b27dbeSShawn Guo #define PHY_IRQ_CMD			0x0d0
2467b27dbeSShawn Guo #define PHY_INTR_MASK0			0x0d4
2567b27dbeSShawn Guo #define PHY_INTR_CLEAR0			0x0dc
2667b27dbeSShawn Guo #define DPDM_MASK			0x1e
2767b27dbeSShawn Guo #define DP_1_0				BIT(4)
2867b27dbeSShawn Guo #define DP_0_1				BIT(3)
2967b27dbeSShawn Guo #define DM_1_0				BIT(2)
3067b27dbeSShawn Guo #define DM_0_1				BIT(1)
3167b27dbeSShawn Guo 
3267b27dbeSShawn Guo enum hsphy_voltage {
3367b27dbeSShawn Guo 	VOL_NONE,
3467b27dbeSShawn Guo 	VOL_MIN,
3567b27dbeSShawn Guo 	VOL_MAX,
3667b27dbeSShawn Guo 	VOL_NUM,
3767b27dbeSShawn Guo };
3867b27dbeSShawn Guo 
3967b27dbeSShawn Guo enum hsphy_vreg {
4067b27dbeSShawn Guo 	VDD,
4167b27dbeSShawn Guo 	VDDA_1P8,
4267b27dbeSShawn Guo 	VDDA_3P3,
4367b27dbeSShawn Guo 	VREG_NUM,
4467b27dbeSShawn Guo };
4567b27dbeSShawn Guo 
4667b27dbeSShawn Guo struct hsphy_init_seq {
4767b27dbeSShawn Guo 	int offset;
4867b27dbeSShawn Guo 	int val;
4967b27dbeSShawn Guo 	int delay;
5067b27dbeSShawn Guo };
5167b27dbeSShawn Guo 
5267b27dbeSShawn Guo struct hsphy_data {
5367b27dbeSShawn Guo 	const struct hsphy_init_seq *init_seq;
5467b27dbeSShawn Guo 	unsigned int init_seq_num;
5567b27dbeSShawn Guo };
5667b27dbeSShawn Guo 
5767b27dbeSShawn Guo struct hsphy_priv {
5867b27dbeSShawn Guo 	void __iomem *base;
5967b27dbeSShawn Guo 	struct clk_bulk_data *clks;
6067b27dbeSShawn Guo 	int num_clks;
6167b27dbeSShawn Guo 	struct reset_control *phy_reset;
6267b27dbeSShawn Guo 	struct reset_control *por_reset;
6367b27dbeSShawn Guo 	struct regulator_bulk_data vregs[VREG_NUM];
6467b27dbeSShawn Guo 	const struct hsphy_data *data;
6567b27dbeSShawn Guo 	enum phy_mode mode;
6667b27dbeSShawn Guo };
6767b27dbeSShawn Guo 
qcom_snps_hsphy_set_mode(struct phy * phy,enum phy_mode mode,int submode)6867b27dbeSShawn Guo static int qcom_snps_hsphy_set_mode(struct phy *phy, enum phy_mode mode,
6967b27dbeSShawn Guo 				    int submode)
7067b27dbeSShawn Guo {
7167b27dbeSShawn Guo 	struct hsphy_priv *priv = phy_get_drvdata(phy);
7267b27dbeSShawn Guo 
7367b27dbeSShawn Guo 	priv->mode = PHY_MODE_INVALID;
7467b27dbeSShawn Guo 
7567b27dbeSShawn Guo 	if (mode > 0)
7667b27dbeSShawn Guo 		priv->mode = mode;
7767b27dbeSShawn Guo 
7867b27dbeSShawn Guo 	return 0;
7967b27dbeSShawn Guo }
8067b27dbeSShawn Guo 
qcom_snps_hsphy_enable_hv_interrupts(struct hsphy_priv * priv)8167b27dbeSShawn Guo static void qcom_snps_hsphy_enable_hv_interrupts(struct hsphy_priv *priv)
8267b27dbeSShawn Guo {
8367b27dbeSShawn Guo 	u32 val;
8467b27dbeSShawn Guo 
8567b27dbeSShawn Guo 	/* Clear any existing interrupts before enabling the interrupts */
8667b27dbeSShawn Guo 	val = readb(priv->base + PHY_INTR_CLEAR0);
8767b27dbeSShawn Guo 	val |= DPDM_MASK;
8867b27dbeSShawn Guo 	writeb(val, priv->base + PHY_INTR_CLEAR0);
8967b27dbeSShawn Guo 
9067b27dbeSShawn Guo 	writeb(0x0, priv->base + PHY_IRQ_CMD);
9167b27dbeSShawn Guo 	usleep_range(200, 220);
9267b27dbeSShawn Guo 	writeb(0x1, priv->base + PHY_IRQ_CMD);
9367b27dbeSShawn Guo 
9467b27dbeSShawn Guo 	/* Make sure the interrupts are cleared */
9567b27dbeSShawn Guo 	usleep_range(200, 220);
9667b27dbeSShawn Guo 
9767b27dbeSShawn Guo 	val = readb(priv->base + PHY_INTR_MASK0);
9867b27dbeSShawn Guo 	switch (priv->mode) {
9967b27dbeSShawn Guo 	case PHY_MODE_USB_HOST_HS:
10067b27dbeSShawn Guo 	case PHY_MODE_USB_HOST_FS:
10167b27dbeSShawn Guo 	case PHY_MODE_USB_DEVICE_HS:
10267b27dbeSShawn Guo 	case PHY_MODE_USB_DEVICE_FS:
10367b27dbeSShawn Guo 		val |= DP_1_0 | DM_0_1;
10467b27dbeSShawn Guo 		break;
10567b27dbeSShawn Guo 	case PHY_MODE_USB_HOST_LS:
10667b27dbeSShawn Guo 	case PHY_MODE_USB_DEVICE_LS:
10767b27dbeSShawn Guo 		val |= DP_0_1 | DM_1_0;
10867b27dbeSShawn Guo 		break;
10967b27dbeSShawn Guo 	default:
11067b27dbeSShawn Guo 		/* No device connected */
11167b27dbeSShawn Guo 		val |= DP_0_1 | DM_0_1;
11267b27dbeSShawn Guo 		break;
11367b27dbeSShawn Guo 	}
11467b27dbeSShawn Guo 	writeb(val, priv->base + PHY_INTR_MASK0);
11567b27dbeSShawn Guo }
11667b27dbeSShawn Guo 
qcom_snps_hsphy_disable_hv_interrupts(struct hsphy_priv * priv)11767b27dbeSShawn Guo static void qcom_snps_hsphy_disable_hv_interrupts(struct hsphy_priv *priv)
11867b27dbeSShawn Guo {
11967b27dbeSShawn Guo 	u32 val;
12067b27dbeSShawn Guo 
12167b27dbeSShawn Guo 	val = readb(priv->base + PHY_INTR_MASK0);
12267b27dbeSShawn Guo 	val &= ~DPDM_MASK;
12367b27dbeSShawn Guo 	writeb(val, priv->base + PHY_INTR_MASK0);
12467b27dbeSShawn Guo 
12567b27dbeSShawn Guo 	/* Clear any pending interrupts */
12667b27dbeSShawn Guo 	val = readb(priv->base + PHY_INTR_CLEAR0);
12767b27dbeSShawn Guo 	val |= DPDM_MASK;
12867b27dbeSShawn Guo 	writeb(val, priv->base + PHY_INTR_CLEAR0);
12967b27dbeSShawn Guo 
13067b27dbeSShawn Guo 	writeb(0x0, priv->base + PHY_IRQ_CMD);
13167b27dbeSShawn Guo 	usleep_range(200, 220);
13267b27dbeSShawn Guo 
13367b27dbeSShawn Guo 	writeb(0x1, priv->base + PHY_IRQ_CMD);
13467b27dbeSShawn Guo 	usleep_range(200, 220);
13567b27dbeSShawn Guo }
13667b27dbeSShawn Guo 
qcom_snps_hsphy_enter_retention(struct hsphy_priv * priv)13767b27dbeSShawn Guo static void qcom_snps_hsphy_enter_retention(struct hsphy_priv *priv)
13867b27dbeSShawn Guo {
13967b27dbeSShawn Guo 	u32 val;
14067b27dbeSShawn Guo 
14167b27dbeSShawn Guo 	val = readb(priv->base + PHY_CTRL_COMMON0);
14267b27dbeSShawn Guo 	val |= SIDDQ;
14367b27dbeSShawn Guo 	writeb(val, priv->base + PHY_CTRL_COMMON0);
14467b27dbeSShawn Guo }
14567b27dbeSShawn Guo 
qcom_snps_hsphy_exit_retention(struct hsphy_priv * priv)14667b27dbeSShawn Guo static void qcom_snps_hsphy_exit_retention(struct hsphy_priv *priv)
14767b27dbeSShawn Guo {
14867b27dbeSShawn Guo 	u32 val;
14967b27dbeSShawn Guo 
15067b27dbeSShawn Guo 	val = readb(priv->base + PHY_CTRL_COMMON0);
15167b27dbeSShawn Guo 	val &= ~SIDDQ;
15267b27dbeSShawn Guo 	writeb(val, priv->base + PHY_CTRL_COMMON0);
15367b27dbeSShawn Guo }
15467b27dbeSShawn Guo 
qcom_snps_hsphy_power_on(struct phy * phy)15567b27dbeSShawn Guo static int qcom_snps_hsphy_power_on(struct phy *phy)
15667b27dbeSShawn Guo {
15767b27dbeSShawn Guo 	struct hsphy_priv *priv = phy_get_drvdata(phy);
15867b27dbeSShawn Guo 	int ret;
15967b27dbeSShawn Guo 
16067b27dbeSShawn Guo 	ret = regulator_bulk_enable(VREG_NUM, priv->vregs);
16167b27dbeSShawn Guo 	if (ret)
16267b27dbeSShawn Guo 		return ret;
163*820eeb9dSBjorn Andersson 
16467b27dbeSShawn Guo 	qcom_snps_hsphy_disable_hv_interrupts(priv);
16567b27dbeSShawn Guo 	qcom_snps_hsphy_exit_retention(priv);
16667b27dbeSShawn Guo 
16767b27dbeSShawn Guo 	return 0;
16867b27dbeSShawn Guo }
16967b27dbeSShawn Guo 
qcom_snps_hsphy_power_off(struct phy * phy)17067b27dbeSShawn Guo static int qcom_snps_hsphy_power_off(struct phy *phy)
17167b27dbeSShawn Guo {
17267b27dbeSShawn Guo 	struct hsphy_priv *priv = phy_get_drvdata(phy);
17367b27dbeSShawn Guo 
17467b27dbeSShawn Guo 	qcom_snps_hsphy_enter_retention(priv);
17567b27dbeSShawn Guo 	qcom_snps_hsphy_enable_hv_interrupts(priv);
17667b27dbeSShawn Guo 	regulator_bulk_disable(VREG_NUM, priv->vregs);
17767b27dbeSShawn Guo 
17867b27dbeSShawn Guo 	return 0;
17967b27dbeSShawn Guo }
18067b27dbeSShawn Guo 
qcom_snps_hsphy_reset(struct hsphy_priv * priv)18167b27dbeSShawn Guo static int qcom_snps_hsphy_reset(struct hsphy_priv *priv)
18267b27dbeSShawn Guo {
18367b27dbeSShawn Guo 	int ret;
18467b27dbeSShawn Guo 
18567b27dbeSShawn Guo 	ret = reset_control_assert(priv->phy_reset);
18667b27dbeSShawn Guo 	if (ret)
18767b27dbeSShawn Guo 		return ret;
18867b27dbeSShawn Guo 
18967b27dbeSShawn Guo 	usleep_range(10, 15);
19067b27dbeSShawn Guo 
19167b27dbeSShawn Guo 	ret = reset_control_deassert(priv->phy_reset);
19267b27dbeSShawn Guo 	if (ret)
19367b27dbeSShawn Guo 		return ret;
19467b27dbeSShawn Guo 
19567b27dbeSShawn Guo 	usleep_range(80, 100);
19667b27dbeSShawn Guo 
19767b27dbeSShawn Guo 	return 0;
19867b27dbeSShawn Guo }
19967b27dbeSShawn Guo 
qcom_snps_hsphy_init_sequence(struct hsphy_priv * priv)20067b27dbeSShawn Guo static void qcom_snps_hsphy_init_sequence(struct hsphy_priv *priv)
20167b27dbeSShawn Guo {
20267b27dbeSShawn Guo 	const struct hsphy_data *data = priv->data;
20367b27dbeSShawn Guo 	const struct hsphy_init_seq *seq;
20467b27dbeSShawn Guo 	int i;
20567b27dbeSShawn Guo 
20667b27dbeSShawn Guo 	/* Device match data is optional. */
20767b27dbeSShawn Guo 	if (!data)
20867b27dbeSShawn Guo 		return;
20967b27dbeSShawn Guo 
21067b27dbeSShawn Guo 	seq = data->init_seq;
21167b27dbeSShawn Guo 
21267b27dbeSShawn Guo 	for (i = 0; i < data->init_seq_num; i++, seq++) {
21367b27dbeSShawn Guo 		writeb(seq->val, priv->base + seq->offset);
21467b27dbeSShawn Guo 		if (seq->delay)
21567b27dbeSShawn Guo 			usleep_range(seq->delay, seq->delay + 10);
21667b27dbeSShawn Guo 	}
21767b27dbeSShawn Guo }
21867b27dbeSShawn Guo 
qcom_snps_hsphy_por_reset(struct hsphy_priv * priv)21967b27dbeSShawn Guo static int qcom_snps_hsphy_por_reset(struct hsphy_priv *priv)
22067b27dbeSShawn Guo {
22167b27dbeSShawn Guo 	int ret;
22267b27dbeSShawn Guo 
22367b27dbeSShawn Guo 	ret = reset_control_assert(priv->por_reset);
22467b27dbeSShawn Guo 	if (ret)
22567b27dbeSShawn Guo 		return ret;
22667b27dbeSShawn Guo 
22767b27dbeSShawn Guo 	/*
22867b27dbeSShawn Guo 	 * The Femto PHY is POR reset in the following scenarios.
22967b27dbeSShawn Guo 	 *
23067b27dbeSShawn Guo 	 * 1. After overriding the parameter registers.
23167b27dbeSShawn Guo 	 * 2. Low power mode exit from PHY retention.
23267b27dbeSShawn Guo 	 *
23367b27dbeSShawn Guo 	 * Ensure that SIDDQ is cleared before bringing the PHY
23467b27dbeSShawn Guo 	 * out of reset.
23567b27dbeSShawn Guo 	 */
23667b27dbeSShawn Guo 	qcom_snps_hsphy_exit_retention(priv);
23767b27dbeSShawn Guo 
23867b27dbeSShawn Guo 	/*
23967b27dbeSShawn Guo 	 * As per databook, 10 usec delay is required between
24067b27dbeSShawn Guo 	 * PHY POR assert and de-assert.
24167b27dbeSShawn Guo 	 */
24267b27dbeSShawn Guo 	usleep_range(10, 20);
24367b27dbeSShawn Guo 	ret = reset_control_deassert(priv->por_reset);
24467b27dbeSShawn Guo 	if (ret)
24567b27dbeSShawn Guo 		return ret;
24667b27dbeSShawn Guo 
24767b27dbeSShawn Guo 	/*
24867b27dbeSShawn Guo 	 * As per databook, it takes 75 usec for PHY to stabilize
24967b27dbeSShawn Guo 	 * after the reset.
25067b27dbeSShawn Guo 	 */
25167b27dbeSShawn Guo 	usleep_range(80, 100);
25267b27dbeSShawn Guo 
25367b27dbeSShawn Guo 	return 0;
25467b27dbeSShawn Guo }
25567b27dbeSShawn Guo 
qcom_snps_hsphy_init(struct phy * phy)25667b27dbeSShawn Guo static int qcom_snps_hsphy_init(struct phy *phy)
25767b27dbeSShawn Guo {
25867b27dbeSShawn Guo 	struct hsphy_priv *priv = phy_get_drvdata(phy);
25967b27dbeSShawn Guo 	int ret;
26067b27dbeSShawn Guo 
261*820eeb9dSBjorn Andersson 	ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks);
26267b27dbeSShawn Guo 	if (ret)
26367b27dbeSShawn Guo 		return ret;
26467b27dbeSShawn Guo 
265*820eeb9dSBjorn Andersson 	ret = qcom_snps_hsphy_reset(priv);
266*820eeb9dSBjorn Andersson 	if (ret)
267*820eeb9dSBjorn Andersson 		goto disable_clocks;
268*820eeb9dSBjorn Andersson 
26967b27dbeSShawn Guo 	qcom_snps_hsphy_init_sequence(priv);
27067b27dbeSShawn Guo 
27167b27dbeSShawn Guo 	ret = qcom_snps_hsphy_por_reset(priv);
27267b27dbeSShawn Guo 	if (ret)
273*820eeb9dSBjorn Andersson 		goto disable_clocks;
274*820eeb9dSBjorn Andersson 
275*820eeb9dSBjorn Andersson 	return 0;
276*820eeb9dSBjorn Andersson 
277*820eeb9dSBjorn Andersson disable_clocks:
278*820eeb9dSBjorn Andersson 	clk_bulk_disable_unprepare(priv->num_clks, priv->clks);
27967b27dbeSShawn Guo 	return ret;
280*820eeb9dSBjorn Andersson }
281*820eeb9dSBjorn Andersson 
qcom_snps_hsphy_exit(struct phy * phy)282*820eeb9dSBjorn Andersson static int qcom_snps_hsphy_exit(struct phy *phy)
283*820eeb9dSBjorn Andersson {
284*820eeb9dSBjorn Andersson 	struct hsphy_priv *priv = phy_get_drvdata(phy);
285*820eeb9dSBjorn Andersson 
286*820eeb9dSBjorn Andersson 	clk_bulk_disable_unprepare(priv->num_clks, priv->clks);
28767b27dbeSShawn Guo 
28867b27dbeSShawn Guo 	return 0;
28967b27dbeSShawn Guo }
29067b27dbeSShawn Guo 
29167b27dbeSShawn Guo static const struct phy_ops qcom_snps_hsphy_ops = {
29267b27dbeSShawn Guo 	.init = qcom_snps_hsphy_init,
293*820eeb9dSBjorn Andersson 	.exit = qcom_snps_hsphy_exit,
29467b27dbeSShawn Guo 	.power_on = qcom_snps_hsphy_power_on,
29567b27dbeSShawn Guo 	.power_off = qcom_snps_hsphy_power_off,
29667b27dbeSShawn Guo 	.set_mode = qcom_snps_hsphy_set_mode,
29767b27dbeSShawn Guo 	.owner = THIS_MODULE,
29867b27dbeSShawn Guo };
29967b27dbeSShawn Guo 
30067b27dbeSShawn Guo static const char * const qcom_snps_hsphy_clks[] = {
30167b27dbeSShawn Guo 	"ref",
30267b27dbeSShawn Guo 	"ahb",
30367b27dbeSShawn Guo 	"sleep",
30467b27dbeSShawn Guo };
30567b27dbeSShawn Guo 
qcom_snps_hsphy_probe(struct platform_device * pdev)30667b27dbeSShawn Guo static int qcom_snps_hsphy_probe(struct platform_device *pdev)
30767b27dbeSShawn Guo {
30867b27dbeSShawn Guo 	struct device *dev = &pdev->dev;
30967b27dbeSShawn Guo 	struct phy_provider *provider;
31067b27dbeSShawn Guo 	struct hsphy_priv *priv;
31167b27dbeSShawn Guo 	struct phy *phy;
31267b27dbeSShawn Guo 	int ret;
31367b27dbeSShawn Guo 	int i;
31467b27dbeSShawn Guo 
31567b27dbeSShawn Guo 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
31667b27dbeSShawn Guo 	if (!priv)
31767b27dbeSShawn Guo 		return -ENOMEM;
31867b27dbeSShawn Guo 
31967b27dbeSShawn Guo 	priv->base = devm_platform_ioremap_resource(pdev, 0);
32067b27dbeSShawn Guo 	if (IS_ERR(priv->base))
32167b27dbeSShawn Guo 		return PTR_ERR(priv->base);
32267b27dbeSShawn Guo 
32367b27dbeSShawn Guo 	priv->num_clks = ARRAY_SIZE(qcom_snps_hsphy_clks);
32467b27dbeSShawn Guo 	priv->clks = devm_kcalloc(dev, priv->num_clks, sizeof(*priv->clks),
32567b27dbeSShawn Guo 				  GFP_KERNEL);
32667b27dbeSShawn Guo 	if (!priv->clks)
32767b27dbeSShawn Guo 		return -ENOMEM;
32867b27dbeSShawn Guo 
32967b27dbeSShawn Guo 	for (i = 0; i < priv->num_clks; i++)
33067b27dbeSShawn Guo 		priv->clks[i].id = qcom_snps_hsphy_clks[i];
33167b27dbeSShawn Guo 
33267b27dbeSShawn Guo 	ret = devm_clk_bulk_get(dev, priv->num_clks, priv->clks);
33367b27dbeSShawn Guo 	if (ret)
33467b27dbeSShawn Guo 		return ret;
33567b27dbeSShawn Guo 
33667b27dbeSShawn Guo 	priv->phy_reset = devm_reset_control_get_exclusive(dev, "phy");
33767b27dbeSShawn Guo 	if (IS_ERR(priv->phy_reset))
33867b27dbeSShawn Guo 		return PTR_ERR(priv->phy_reset);
33967b27dbeSShawn Guo 
34067b27dbeSShawn Guo 	priv->por_reset = devm_reset_control_get_exclusive(dev, "por");
34167b27dbeSShawn Guo 	if (IS_ERR(priv->por_reset))
34267b27dbeSShawn Guo 		return PTR_ERR(priv->por_reset);
34367b27dbeSShawn Guo 
34467b27dbeSShawn Guo 	priv->vregs[VDD].supply = "vdd";
34567b27dbeSShawn Guo 	priv->vregs[VDDA_1P8].supply = "vdda1p8";
34667b27dbeSShawn Guo 	priv->vregs[VDDA_3P3].supply = "vdda3p3";
34767b27dbeSShawn Guo 
34867b27dbeSShawn Guo 	ret = devm_regulator_bulk_get(dev, VREG_NUM, priv->vregs);
34967b27dbeSShawn Guo 	if (ret)
35067b27dbeSShawn Guo 		return ret;
35167b27dbeSShawn Guo 
35267b27dbeSShawn Guo 	/* Get device match data */
35367b27dbeSShawn Guo 	priv->data = device_get_match_data(dev);
35467b27dbeSShawn Guo 
35567b27dbeSShawn Guo 	phy = devm_phy_create(dev, dev->of_node, &qcom_snps_hsphy_ops);
35667b27dbeSShawn Guo 	if (IS_ERR(phy))
35767b27dbeSShawn Guo 		return PTR_ERR(phy);
35867b27dbeSShawn Guo 
35967b27dbeSShawn Guo 	phy_set_drvdata(phy, priv);
36067b27dbeSShawn Guo 
36167b27dbeSShawn Guo 	provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
36267b27dbeSShawn Guo 	if (IS_ERR(provider))
36367b27dbeSShawn Guo 		return PTR_ERR(provider);
36467b27dbeSShawn Guo 
36567b27dbeSShawn Guo 	ret = regulator_set_load(priv->vregs[VDDA_1P8].consumer, 19000);
36667b27dbeSShawn Guo 	if (ret < 0)
36767b27dbeSShawn Guo 		return ret;
36867b27dbeSShawn Guo 
36967b27dbeSShawn Guo 	ret = regulator_set_load(priv->vregs[VDDA_3P3].consumer, 16000);
37067b27dbeSShawn Guo 	if (ret < 0)
37167b27dbeSShawn Guo 		goto unset_1p8_load;
37267b27dbeSShawn Guo 
37367b27dbeSShawn Guo 	return 0;
37467b27dbeSShawn Guo 
37567b27dbeSShawn Guo unset_1p8_load:
37667b27dbeSShawn Guo 	regulator_set_load(priv->vregs[VDDA_1P8].consumer, 0);
37767b27dbeSShawn Guo 
37867b27dbeSShawn Guo 	return ret;
37967b27dbeSShawn Guo }
38067b27dbeSShawn Guo 
38167b27dbeSShawn Guo /*
38267b27dbeSShawn Guo  * The macro is used to define an initialization sequence.  Each tuple
38367b27dbeSShawn Guo  * is meant to program 'value' into phy register at 'offset' with 'delay'
38467b27dbeSShawn Guo  * in us followed.
38567b27dbeSShawn Guo  */
38667b27dbeSShawn Guo #define HSPHY_INIT_CFG(o, v, d)	{ .offset = o, .val = v, .delay = d, }
38767b27dbeSShawn Guo 
38867b27dbeSShawn Guo static const struct hsphy_init_seq init_seq_femtophy[] = {
38967b27dbeSShawn Guo 	HSPHY_INIT_CFG(0xc0, 0x01, 0),
39067b27dbeSShawn Guo 	HSPHY_INIT_CFG(0xe8, 0x0d, 0),
39167b27dbeSShawn Guo 	HSPHY_INIT_CFG(0x74, 0x12, 0),
39267b27dbeSShawn Guo 	HSPHY_INIT_CFG(0x98, 0x63, 0),
39367b27dbeSShawn Guo 	HSPHY_INIT_CFG(0x9c, 0x03, 0),
39467b27dbeSShawn Guo 	HSPHY_INIT_CFG(0xa0, 0x1d, 0),
39567b27dbeSShawn Guo 	HSPHY_INIT_CFG(0xa4, 0x03, 0),
39667b27dbeSShawn Guo 	HSPHY_INIT_CFG(0x8c, 0x23, 0),
39767b27dbeSShawn Guo 	HSPHY_INIT_CFG(0x78, 0x08, 0),
39867b27dbeSShawn Guo 	HSPHY_INIT_CFG(0x7c, 0xdc, 0),
39967b27dbeSShawn Guo 	HSPHY_INIT_CFG(0x90, 0xe0, 20),
40067b27dbeSShawn Guo 	HSPHY_INIT_CFG(0x74, 0x10, 0),
40167b27dbeSShawn Guo 	HSPHY_INIT_CFG(0x90, 0x60, 0),
40267b27dbeSShawn Guo };
40367b27dbeSShawn Guo 
40467b27dbeSShawn Guo static const struct hsphy_data hsphy_data_femtophy = {
40567b27dbeSShawn Guo 	.init_seq = init_seq_femtophy,
40667b27dbeSShawn Guo 	.init_seq_num = ARRAY_SIZE(init_seq_femtophy),
40767b27dbeSShawn Guo };
40867b27dbeSShawn Guo 
40967b27dbeSShawn Guo static const struct of_device_id qcom_snps_hsphy_match[] = {
41067b27dbeSShawn Guo 	{ .compatible = "qcom,usb-hs-28nm-femtophy", .data = &hsphy_data_femtophy, },
41167b27dbeSShawn Guo 	{ },
41267b27dbeSShawn Guo };
41367b27dbeSShawn Guo MODULE_DEVICE_TABLE(of, qcom_snps_hsphy_match);
41467b27dbeSShawn Guo 
41567b27dbeSShawn Guo static struct platform_driver qcom_snps_hsphy_driver = {
41667b27dbeSShawn Guo 	.probe = qcom_snps_hsphy_probe,
41767b27dbeSShawn Guo 	.driver	= {
41867b27dbeSShawn Guo 		.name = "qcom,usb-hs-28nm-phy",
41967b27dbeSShawn Guo 		.of_match_table = qcom_snps_hsphy_match,
42067b27dbeSShawn Guo 	},
42167b27dbeSShawn Guo };
42267b27dbeSShawn Guo module_platform_driver(qcom_snps_hsphy_driver);
42367b27dbeSShawn Guo 
42467b27dbeSShawn Guo MODULE_DESCRIPTION("Qualcomm 28nm Hi-Speed USB PHY driver");
42567b27dbeSShawn Guo MODULE_LICENSE("GPL v2");
426