Loading drivers/usb/phy/phy-msm-qusb.c +163 −38 Original line number Diff line number Diff line Loading @@ -129,7 +129,10 @@ struct qusb_phy { struct clk *ref_clk; struct clk *cfg_ahb_clk; struct clk *phy_reset; struct clk *iface_clk; struct clk *core_clk; struct regulator *gdsc; struct regulator *vdd; struct regulator *vdda33; struct regulator *vdda18; Loading @@ -142,6 +145,7 @@ struct qusb_phy { int tune2_efuse_num_of_bits; int tune2_efuse_correction; bool vdd_enabled; bool power_enabled; bool clocks_enabled; bool cable_connected; Loading @@ -160,6 +164,7 @@ struct qusb_phy { int *emu_dcm_reset_seq; int emu_dcm_reset_seq_len; spinlock_t pulse_lock; bool put_into_high_z_state; }; static void qusb_phy_enable_clocks(struct qusb_phy *qphy, bool on) Loading @@ -170,14 +175,22 @@ static void qusb_phy_enable_clocks(struct qusb_phy *qphy, bool on) if (!qphy->clocks_enabled && on) { clk_prepare_enable(qphy->ref_clk_src); clk_prepare_enable(qphy->ref_clk); clk_prepare_enable(qphy->iface_clk); clk_prepare_enable(qphy->core_clk); clk_prepare_enable(qphy->cfg_ahb_clk); qphy->clocks_enabled = true; } if (qphy->clocks_enabled && !on) { clk_disable_unprepare(qphy->cfg_ahb_clk); /* * FSM depedency beween iface_clk and core_clk. * Hence turned off core_clk before iface_clk. */ clk_disable_unprepare(qphy->core_clk); clk_disable_unprepare(qphy->iface_clk); clk_disable_unprepare(qphy->ref_clk); clk_disable_unprepare(qphy->ref_clk_src); clk_disable_unprepare(qphy->cfg_ahb_clk); qphy->clocks_enabled = false; } Loading @@ -185,6 +198,32 @@ static void qusb_phy_enable_clocks(struct qusb_phy *qphy, bool on) qphy->clocks_enabled); } static int qusb_phy_gdsc(struct qusb_phy *qphy, bool on) { int ret; if (IS_ERR_OR_NULL(qphy->gdsc)) return -EPERM; if (on) { dev_dbg(qphy->phy.dev, "TURNING ON GDSC\n"); ret = regulator_enable(qphy->gdsc); if (ret) { dev_err(qphy->phy.dev, "unable to enable gdsc\n"); return ret; } } else { dev_dbg(qphy->phy.dev, "TURNING OFF GDSC\n"); ret = regulator_disable(qphy->gdsc); if (ret) { dev_err(qphy->phy.dev, "unable to disable gdsc\n"); return ret; } } return ret; } static int qusb_phy_config_vdd(struct qusb_phy *qphy, int high) { int min, ret; Loading @@ -202,6 +241,47 @@ static int qusb_phy_config_vdd(struct qusb_phy *qphy, int high) return ret; } static int qusb_phy_vdd(struct qusb_phy *qphy, bool on) { int ret = 0; if (!qphy->vdd_enabled && on) { dev_dbg(qphy->phy.dev, "TURNING ON VDD\n"); ret = qusb_phy_config_vdd(qphy, true); if (ret) { dev_err(qphy->phy.dev, "Unable to config VDD:%d\n", ret); goto err; } ret = regulator_enable(qphy->vdd); if (ret) { dev_err(qphy->phy.dev, "Unable to enable VDD\n"); goto err; } qphy->vdd_enabled = true; } if (qphy->vdd_enabled && !on) { dev_dbg(qphy->phy.dev, "TURNING OFF VDD\n"); ret = regulator_disable(qphy->vdd); if (ret) { dev_err(qphy->phy.dev, "Unable to disable vdd:%d\n", ret); goto err; } ret = qusb_phy_config_vdd(qphy, false); if (ret) { dev_err(qphy->phy.dev, "Unable unconfig VDD:%d\n", ret); goto err; } qphy->vdd_enabled = false; } err: return ret; } static int qusb_phy_enable_power(struct qusb_phy *qphy, bool on) { int ret = 0; Loading @@ -217,18 +297,9 @@ static int qusb_phy_enable_power(struct qusb_phy *qphy, bool on) if (!on) goto disable_vdda33; ret = qusb_phy_config_vdd(qphy, true); if (ret) { dev_err(qphy->phy.dev, "Unable to config VDD:%d\n", ret); ret = qusb_phy_vdd(qphy, true); if (ret < 0) goto err_vdd; } ret = regulator_enable(qphy->vdd); if (ret) { dev_err(qphy->phy.dev, "Unable to enable VDD\n"); goto unconfig_vdd; } ret = regulator_set_optimum_mode(qphy->vdda18, QUSB2PHY_1P8_HPM_LOAD); if (ret < 0) { Loading Loading @@ -308,16 +379,7 @@ put_vdda18_lpm: dev_err(qphy->phy.dev, "Unable to set LPM of vdda18\n"); disable_vdd: ret = regulator_disable(qphy->vdd); if (ret) dev_err(qphy->phy.dev, "Unable to disable vdd:%d\n", ret); unconfig_vdd: ret = qusb_phy_config_vdd(qphy, false); if (ret) dev_err(qphy->phy.dev, "Unable unconfig VDD:%d\n", ret); ret = qusb_phy_vdd(qphy, false); err_vdd: qphy->power_enabled = false; dev_dbg(qphy->phy.dev, "QUSB PHY's regulators are turned OFF.\n"); Loading @@ -340,12 +402,55 @@ static int qusb_phy_update_dpdm(struct usb_phy *phy, int value) case POWER_SUPPLY_DP_DM_DPF_DMF: dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DPF_DMF\n"); if (!qphy->rm_pulldown) { if (qphy->put_into_high_z_state) { /* Bring up DVDD */ ret = qusb_phy_vdd(qphy, true); if (ret < 0) goto clk_error; qusb_phy_gdsc(qphy, true); qusb_phy_enable_clocks(qphy, true); dev_dbg(phy->dev, "RESET QUSB PHY\n"); clk_reset(qphy->phy_reset, CLK_RESET_ASSERT); usleep_range(100, 150); clk_reset(qphy->phy_reset, CLK_RESET_DEASSERT); /* * Phy in non-driving mode leaves Dp and Dm * lines in high-Z state. Controller power * collapse is not switching phy to non-driving * mode causing charger detection failure. Bring * phy to non-driving mode by overriding * controller output via UTMI interface. */ writel_relaxed(TERM_SELECT | XCVR_SELECT_FS | OP_MODE_NON_DRIVE, qphy->base + QUSB2PHY_PORT_UTMI_CTRL1); writel_relaxed(UTMI_ULPI_SEL | UTMI_TEST_MUX_SEL, qphy->base + QUSB2PHY_PORT_UTMI_CTRL2); /* Disable PHY */ writel_relaxed(CLAMP_N_EN | FREEZIO_N | POWER_DOWN, qphy->base + QUSB2PHY_PORT_POWERDOWN); /* Make sure that above write is completed */ wmb(); } ret = qusb_phy_enable_power(qphy, true); if (ret >= 0) { qphy->rm_pulldown = true; dev_dbg(phy->dev, "DP_DM_F: rm_pulldown:%d\n", qphy->rm_pulldown); } if (qphy->put_into_high_z_state) { qusb_phy_enable_clocks(qphy, false); qusb_phy_gdsc(qphy, false); } } /* Clear QC1 and QC2 registers when rm_pulldown = 1 */ Loading Loading @@ -892,28 +997,20 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend) /* Disable all interrupts */ writel_relaxed(0x00, qphy->base + QUSB2PHY_PORT_INTR_CTRL); /* * Phy in non-driving mode leaves Dp and Dm lines in * high-Z state. Controller power collapse is not * switching phy to non-driving mode causing charger * detection failure. Bring phy to non-driving mode by * overriding controller output via UTMI interface. */ writel_relaxed(TERM_SELECT | XCVR_SELECT_FS | OP_MODE_NON_DRIVE, qphy->base + QUSB2PHY_PORT_UTMI_CTRL1); writel_relaxed(UTMI_ULPI_SEL | UTMI_TEST_MUX_SEL, qphy->base + QUSB2PHY_PORT_UTMI_CTRL2); /* Disable PHY */ writel_relaxed(CLAMP_N_EN | FREEZIO_N | POWER_DOWN, qphy->base + QUSB2PHY_PORT_POWERDOWN); /* Make sure that above write is completed */ wmb(); qusb_phy_enable_clocks(qphy, false); qusb_phy_enable_power(qphy, false); /* * Set put_into_high_z_state to true so next USB * cable connect, DPF_DMF request performs PHY * reset and put it into high-z state. For bootup * with or without USB cable, it doesn't require * to put QUSB PHY into high-z state. */ qphy->put_into_high_z_state = true; } qphy->suspended = true; } else { Loading Loading @@ -1101,6 +1198,34 @@ static int qusb_phy_probe(struct platform_device *pdev) if (IS_ERR(qphy->phy_reset)) return PTR_ERR(qphy->phy_reset); if (of_property_match_string(dev->of_node, "clock-names", "iface_clk") >= 0) { qphy->iface_clk = devm_clk_get(dev, "iface_clk"); if (IS_ERR(qphy->iface_clk)) { ret = PTR_ERR(qphy->iface_clk); qphy->iface_clk = NULL; if (ret == -EPROBE_DEFER) return ret; dev_err(dev, "couldn't get iface_clk(%d)\n", ret); } } if (of_property_match_string(dev->of_node, "clock-names", "core_clk") >= 0) { qphy->core_clk = devm_clk_get(dev, "core_clk"); if (IS_ERR(qphy->core_clk)) { ret = PTR_ERR(qphy->core_clk); qphy->core_clk = NULL; if (ret == -EPROBE_DEFER) return ret; dev_err(dev, "couldn't get core_clk(%d)\n", ret); } } qphy->gdsc = devm_regulator_get(dev, "USB3_GDSC"); if (IS_ERR(qphy->gdsc)) qphy->gdsc = NULL; qphy->emulation = of_property_read_bool(dev->of_node, "qcom,emulation"); Loading Loading
drivers/usb/phy/phy-msm-qusb.c +163 −38 Original line number Diff line number Diff line Loading @@ -129,7 +129,10 @@ struct qusb_phy { struct clk *ref_clk; struct clk *cfg_ahb_clk; struct clk *phy_reset; struct clk *iface_clk; struct clk *core_clk; struct regulator *gdsc; struct regulator *vdd; struct regulator *vdda33; struct regulator *vdda18; Loading @@ -142,6 +145,7 @@ struct qusb_phy { int tune2_efuse_num_of_bits; int tune2_efuse_correction; bool vdd_enabled; bool power_enabled; bool clocks_enabled; bool cable_connected; Loading @@ -160,6 +164,7 @@ struct qusb_phy { int *emu_dcm_reset_seq; int emu_dcm_reset_seq_len; spinlock_t pulse_lock; bool put_into_high_z_state; }; static void qusb_phy_enable_clocks(struct qusb_phy *qphy, bool on) Loading @@ -170,14 +175,22 @@ static void qusb_phy_enable_clocks(struct qusb_phy *qphy, bool on) if (!qphy->clocks_enabled && on) { clk_prepare_enable(qphy->ref_clk_src); clk_prepare_enable(qphy->ref_clk); clk_prepare_enable(qphy->iface_clk); clk_prepare_enable(qphy->core_clk); clk_prepare_enable(qphy->cfg_ahb_clk); qphy->clocks_enabled = true; } if (qphy->clocks_enabled && !on) { clk_disable_unprepare(qphy->cfg_ahb_clk); /* * FSM depedency beween iface_clk and core_clk. * Hence turned off core_clk before iface_clk. */ clk_disable_unprepare(qphy->core_clk); clk_disable_unprepare(qphy->iface_clk); clk_disable_unprepare(qphy->ref_clk); clk_disable_unprepare(qphy->ref_clk_src); clk_disable_unprepare(qphy->cfg_ahb_clk); qphy->clocks_enabled = false; } Loading @@ -185,6 +198,32 @@ static void qusb_phy_enable_clocks(struct qusb_phy *qphy, bool on) qphy->clocks_enabled); } static int qusb_phy_gdsc(struct qusb_phy *qphy, bool on) { int ret; if (IS_ERR_OR_NULL(qphy->gdsc)) return -EPERM; if (on) { dev_dbg(qphy->phy.dev, "TURNING ON GDSC\n"); ret = regulator_enable(qphy->gdsc); if (ret) { dev_err(qphy->phy.dev, "unable to enable gdsc\n"); return ret; } } else { dev_dbg(qphy->phy.dev, "TURNING OFF GDSC\n"); ret = regulator_disable(qphy->gdsc); if (ret) { dev_err(qphy->phy.dev, "unable to disable gdsc\n"); return ret; } } return ret; } static int qusb_phy_config_vdd(struct qusb_phy *qphy, int high) { int min, ret; Loading @@ -202,6 +241,47 @@ static int qusb_phy_config_vdd(struct qusb_phy *qphy, int high) return ret; } static int qusb_phy_vdd(struct qusb_phy *qphy, bool on) { int ret = 0; if (!qphy->vdd_enabled && on) { dev_dbg(qphy->phy.dev, "TURNING ON VDD\n"); ret = qusb_phy_config_vdd(qphy, true); if (ret) { dev_err(qphy->phy.dev, "Unable to config VDD:%d\n", ret); goto err; } ret = regulator_enable(qphy->vdd); if (ret) { dev_err(qphy->phy.dev, "Unable to enable VDD\n"); goto err; } qphy->vdd_enabled = true; } if (qphy->vdd_enabled && !on) { dev_dbg(qphy->phy.dev, "TURNING OFF VDD\n"); ret = regulator_disable(qphy->vdd); if (ret) { dev_err(qphy->phy.dev, "Unable to disable vdd:%d\n", ret); goto err; } ret = qusb_phy_config_vdd(qphy, false); if (ret) { dev_err(qphy->phy.dev, "Unable unconfig VDD:%d\n", ret); goto err; } qphy->vdd_enabled = false; } err: return ret; } static int qusb_phy_enable_power(struct qusb_phy *qphy, bool on) { int ret = 0; Loading @@ -217,18 +297,9 @@ static int qusb_phy_enable_power(struct qusb_phy *qphy, bool on) if (!on) goto disable_vdda33; ret = qusb_phy_config_vdd(qphy, true); if (ret) { dev_err(qphy->phy.dev, "Unable to config VDD:%d\n", ret); ret = qusb_phy_vdd(qphy, true); if (ret < 0) goto err_vdd; } ret = regulator_enable(qphy->vdd); if (ret) { dev_err(qphy->phy.dev, "Unable to enable VDD\n"); goto unconfig_vdd; } ret = regulator_set_optimum_mode(qphy->vdda18, QUSB2PHY_1P8_HPM_LOAD); if (ret < 0) { Loading Loading @@ -308,16 +379,7 @@ put_vdda18_lpm: dev_err(qphy->phy.dev, "Unable to set LPM of vdda18\n"); disable_vdd: ret = regulator_disable(qphy->vdd); if (ret) dev_err(qphy->phy.dev, "Unable to disable vdd:%d\n", ret); unconfig_vdd: ret = qusb_phy_config_vdd(qphy, false); if (ret) dev_err(qphy->phy.dev, "Unable unconfig VDD:%d\n", ret); ret = qusb_phy_vdd(qphy, false); err_vdd: qphy->power_enabled = false; dev_dbg(qphy->phy.dev, "QUSB PHY's regulators are turned OFF.\n"); Loading @@ -340,12 +402,55 @@ static int qusb_phy_update_dpdm(struct usb_phy *phy, int value) case POWER_SUPPLY_DP_DM_DPF_DMF: dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DPF_DMF\n"); if (!qphy->rm_pulldown) { if (qphy->put_into_high_z_state) { /* Bring up DVDD */ ret = qusb_phy_vdd(qphy, true); if (ret < 0) goto clk_error; qusb_phy_gdsc(qphy, true); qusb_phy_enable_clocks(qphy, true); dev_dbg(phy->dev, "RESET QUSB PHY\n"); clk_reset(qphy->phy_reset, CLK_RESET_ASSERT); usleep_range(100, 150); clk_reset(qphy->phy_reset, CLK_RESET_DEASSERT); /* * Phy in non-driving mode leaves Dp and Dm * lines in high-Z state. Controller power * collapse is not switching phy to non-driving * mode causing charger detection failure. Bring * phy to non-driving mode by overriding * controller output via UTMI interface. */ writel_relaxed(TERM_SELECT | XCVR_SELECT_FS | OP_MODE_NON_DRIVE, qphy->base + QUSB2PHY_PORT_UTMI_CTRL1); writel_relaxed(UTMI_ULPI_SEL | UTMI_TEST_MUX_SEL, qphy->base + QUSB2PHY_PORT_UTMI_CTRL2); /* Disable PHY */ writel_relaxed(CLAMP_N_EN | FREEZIO_N | POWER_DOWN, qphy->base + QUSB2PHY_PORT_POWERDOWN); /* Make sure that above write is completed */ wmb(); } ret = qusb_phy_enable_power(qphy, true); if (ret >= 0) { qphy->rm_pulldown = true; dev_dbg(phy->dev, "DP_DM_F: rm_pulldown:%d\n", qphy->rm_pulldown); } if (qphy->put_into_high_z_state) { qusb_phy_enable_clocks(qphy, false); qusb_phy_gdsc(qphy, false); } } /* Clear QC1 and QC2 registers when rm_pulldown = 1 */ Loading Loading @@ -892,28 +997,20 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend) /* Disable all interrupts */ writel_relaxed(0x00, qphy->base + QUSB2PHY_PORT_INTR_CTRL); /* * Phy in non-driving mode leaves Dp and Dm lines in * high-Z state. Controller power collapse is not * switching phy to non-driving mode causing charger * detection failure. Bring phy to non-driving mode by * overriding controller output via UTMI interface. */ writel_relaxed(TERM_SELECT | XCVR_SELECT_FS | OP_MODE_NON_DRIVE, qphy->base + QUSB2PHY_PORT_UTMI_CTRL1); writel_relaxed(UTMI_ULPI_SEL | UTMI_TEST_MUX_SEL, qphy->base + QUSB2PHY_PORT_UTMI_CTRL2); /* Disable PHY */ writel_relaxed(CLAMP_N_EN | FREEZIO_N | POWER_DOWN, qphy->base + QUSB2PHY_PORT_POWERDOWN); /* Make sure that above write is completed */ wmb(); qusb_phy_enable_clocks(qphy, false); qusb_phy_enable_power(qphy, false); /* * Set put_into_high_z_state to true so next USB * cable connect, DPF_DMF request performs PHY * reset and put it into high-z state. For bootup * with or without USB cable, it doesn't require * to put QUSB PHY into high-z state. */ qphy->put_into_high_z_state = true; } qphy->suspended = true; } else { Loading Loading @@ -1101,6 +1198,34 @@ static int qusb_phy_probe(struct platform_device *pdev) if (IS_ERR(qphy->phy_reset)) return PTR_ERR(qphy->phy_reset); if (of_property_match_string(dev->of_node, "clock-names", "iface_clk") >= 0) { qphy->iface_clk = devm_clk_get(dev, "iface_clk"); if (IS_ERR(qphy->iface_clk)) { ret = PTR_ERR(qphy->iface_clk); qphy->iface_clk = NULL; if (ret == -EPROBE_DEFER) return ret; dev_err(dev, "couldn't get iface_clk(%d)\n", ret); } } if (of_property_match_string(dev->of_node, "clock-names", "core_clk") >= 0) { qphy->core_clk = devm_clk_get(dev, "core_clk"); if (IS_ERR(qphy->core_clk)) { ret = PTR_ERR(qphy->core_clk); qphy->core_clk = NULL; if (ret == -EPROBE_DEFER) return ret; dev_err(dev, "couldn't get core_clk(%d)\n", ret); } } qphy->gdsc = devm_regulator_get(dev, "USB3_GDSC"); if (IS_ERR(qphy->gdsc)) qphy->gdsc = NULL; qphy->emulation = of_property_read_bool(dev->of_node, "qcom,emulation"); Loading