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