Loading Documentation/devicetree/bindings/usb/msm-phy.txt +5 −0 Original line number Diff line number Diff line Loading @@ -160,6 +160,9 @@ Optional properties: "efuse_addr": EFUSE address to read and update analog tune parameter. "emu_phy_base" : phy base address used for programming emulation target phy. "ref_clk_addr" : ref_clk bcr address used for on/off ref_clk before reset. "tcsr_clamp_dig_n" : To enable/disable digital clamp to the phy. When de-asserted, it will prevent random leakage from qusb2 phy resulting from out of sequence turn on/off of 1p8, 3p3 and DVDD regulators. "refgen_north_bg_reg" : address used to read REFGEN status for overriding QUSB PHY register. - clocks: a list of phandles to the PHY clocks. Use as per Documentation/devicetree/bindings/clock/clock-bindings.txt Loading @@ -179,6 +182,8 @@ Optional properties: - qcom,major-rev: provide major revision number to differentiate power up sequence. default is 2.0 - pinctrl-names/pinctrl-0/1: The GPIOs configured as output function. Names represents "active" state when attached in host mode and "suspend" state when detached. - qcom,tune2-efuse-correction: The value to be adjusted from fused value for improved rise/fall times. Example: qusb_phy: qusb@f9b39000 { Loading drivers/usb/phy/phy-msm-qusb.c +249 −34 Original line number Diff line number Diff line Loading @@ -45,6 +45,8 @@ #define FREEZIO_N BIT(1) #define POWER_DOWN BIT(0) #define QUSB2PHY_PORT_TEST_CTRL 0xB8 #define QUSB2PHY_PWR_CTRL1 0x210 #define PWR_CTRL1_CLAMP_N_EN BIT(1) #define PWR_CTRL1_POWR_DOWN BIT(0) Loading @@ -68,10 +70,7 @@ #define QUSB2PHY_PORT_TUNE2 0x84 #define QUSB2PHY_PORT_TUNE3 0x88 #define QUSB2PHY_PORT_TUNE4 0x8C /* In case Efuse register shows zero, use this value */ #define TUNE2_DEFAULT_HIGH_NIBBLE 0xB #define TUNE2_DEFAULT_LOW_NIBBLE 0x3 #define QUSB2PHY_PORT_TUNE5 0x90 /* Get TUNE2's high nibble value read from efuse */ #define TUNE2_HIGH_NIBBLE_VAL(val, pos, mask) ((val >> pos) & mask) Loading @@ -98,21 +97,42 @@ #define QUSB2PHY_REFCLK_ENABLE BIT(0) unsigned int tune2; module_param(tune2, uint, S_IRUGO | S_IWUSR); static unsigned int tune1; module_param(tune1, uint, 0644); MODULE_PARM_DESC(tune1, "QUSB PHY TUNE1"); static unsigned int tune2; module_param(tune2, uint, 0644); MODULE_PARM_DESC(tune2, "QUSB PHY TUNE2"); static unsigned int tune3; module_param(tune3, uint, 0644); MODULE_PARM_DESC(tune3, "QUSB PHY TUNE3"); static unsigned int tune4; module_param(tune4, uint, 0644); MODULE_PARM_DESC(tune4, "QUSB PHY TUNE4"); static unsigned int tune5; module_param(tune5, uint, 0644); MODULE_PARM_DESC(tune5, "QUSB PHY TUNE5"); struct qusb_phy { struct usb_phy phy; void __iomem *base; void __iomem *tune2_efuse_reg; void __iomem *ref_clk_base; void __iomem *tcsr_clamp_dig_n; struct clk *ref_clk_src; struct clk *ref_clk; struct clk *cfg_ahb_clk; struct reset_control *phy_reset; struct clk *iface_clk; struct clk *core_clk; struct regulator *gdsc; struct regulator *vdd; struct regulator *vdda33; struct regulator *vdda18; Loading @@ -124,6 +144,7 @@ struct qusb_phy { u32 tune2_val; int tune2_efuse_bit_pos; int tune2_efuse_num_of_bits; int tune2_efuse_correction; bool power_enabled; bool clocks_enabled; Loading @@ -145,6 +166,8 @@ struct qusb_phy { int phy_pll_reset_seq_len; int *emu_dcm_reset_seq; int emu_dcm_reset_seq_len; bool put_into_high_z_state; struct mutex phy_lock; }; static void qusb_phy_enable_clocks(struct qusb_phy *qphy, bool on) Loading @@ -155,14 +178,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 @@ -170,6 +201,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 Loading @@ -313,6 +370,7 @@ static void qusb_phy_get_tune2_param(struct qusb_phy *qphy) { u8 num_of_bits; u32 bit_mask = 1; u8 reg_val; pr_debug("%s(): num_of_bits:%d bit_pos:%d\n", __func__, qphy->tune2_efuse_num_of_bits, Loading @@ -326,9 +384,8 @@ static void qusb_phy_get_tune2_param(struct qusb_phy *qphy) /* * Read EFUSE register having TUNE2 parameter's high nibble. * If efuse register shows value as 0x0, then use default value * as 0xB as high nibble. Otherwise use efuse register based * value for this purpose. * If efuse register shows value as 0x0, then use previous value * as it is. Otherwise use efuse register based value for this purpose. */ qphy->tune2_val = readl_relaxed(qphy->tune2_efuse_reg); pr_debug("%s(): bit_mask:%d efuse based tune2 value:%d\n", Loading @@ -337,12 +394,24 @@ static void qusb_phy_get_tune2_param(struct qusb_phy *qphy) qphy->tune2_val = TUNE2_HIGH_NIBBLE_VAL(qphy->tune2_val, qphy->tune2_efuse_bit_pos, bit_mask); if (!qphy->tune2_val) qphy->tune2_val = TUNE2_DEFAULT_HIGH_NIBBLE; /* Update higher nibble of TUNE2 value for better rise/fall times */ if (qphy->tune2_efuse_correction && qphy->tune2_val) { if (qphy->tune2_efuse_correction > 5 || qphy->tune2_efuse_correction < -10) pr_warn("Correction value is out of range : %d\n", qphy->tune2_efuse_correction); else qphy->tune2_val = qphy->tune2_val + qphy->tune2_efuse_correction; } reg_val = readb_relaxed(qphy->base + QUSB2PHY_PORT_TUNE2); if (qphy->tune2_val) { reg_val &= 0x0f; reg_val |= (qphy->tune2_val << 4); } /* Get TUNE2 byte value using high and low nibble value */ qphy->tune2_val = ((qphy->tune2_val << 0x4) | TUNE2_DEFAULT_LOW_NIBBLE); qphy->tune2_val = reg_val; } static void qusb_phy_write_seq(void __iomem *base, u32 *seq, int cnt, Loading Loading @@ -450,7 +519,7 @@ static int qusb_phy_init(struct usb_phy *phy) * and try to read EFUSE value only once i.e. not every USB * cable connect case. */ if (qphy->tune2_efuse_reg) { if (qphy->tune2_efuse_reg && !tune2) { if (!qphy->tune2_val) qusb_phy_get_tune2_param(qphy); Loading @@ -460,13 +529,29 @@ static int qusb_phy_init(struct usb_phy *phy) qphy->base + QUSB2PHY_PORT_TUNE2); } /* If tune2 modparam set, override tune2 value */ if (tune2) { pr_debug("%s(): (modparam) TUNE2 val:0x%02x\n", __func__, tune2); /* If tune modparam set, override tune value */ pr_debug("%s():userspecified modparams TUNEX val:0x%x %x %x %x %x\n", __func__, tune1, tune2, tune3, tune4, tune5); if (tune1) writel_relaxed(tune1, qphy->base + QUSB2PHY_PORT_TUNE1); if (tune2) writel_relaxed(tune2, qphy->base + QUSB2PHY_PORT_TUNE2); } if (tune3) writel_relaxed(tune3, qphy->base + QUSB2PHY_PORT_TUNE3); if (tune4) writel_relaxed(tune4, qphy->base + QUSB2PHY_PORT_TUNE4); if (tune5) writel_relaxed(tune5, qphy->base + QUSB2PHY_PORT_TUNE5); /* ensure above writes are completed before re-enabling PHY */ wmb(); Loading Loading @@ -596,27 +681,55 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend) writel_relaxed(intr_mask, qphy->base + QUSB2PHY_PORT_INTR_CTRL); if (linestate & (LINESTATE_DP | LINESTATE_DM)) { /* enable phy auto-resume */ writel_relaxed(0x0C, qphy->base + QUSB2PHY_PORT_TEST_CTRL); /* flush the previous write before next write */ wmb(); writel_relaxed(0x04, qphy->base + QUSB2PHY_PORT_TEST_CTRL); } dev_dbg(phy->dev, "%s: intr_mask = %x\n", __func__, intr_mask); /* Makes sure that above write goes through */ wmb(); qusb_phy_enable_clocks(qphy, false); } else { /* Disconnect case */ mutex_lock(&qphy->phy_lock); /* 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(POWER_DOWN, qphy->base + QUSB2PHY_PORT_POWERDOWN); /* Make sure that above write is completed */ wmb(); qusb_phy_enable_clocks(qphy, false); if (qphy->tcsr_clamp_dig_n) writel_relaxed(0x0, qphy->tcsr_clamp_dig_n); /* Do not disable power rails if there is vote for it */ if (!qphy->dpdm_enable) qusb_phy_enable_power(qphy, false); else dev_dbg(phy->dev, "race with rm_pulldown. Keep ldo ON\n"); mutex_unlock(&qphy->phy_lock); /* * 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 @@ -629,6 +742,9 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend) qphy->base + QUSB2PHY_PORT_INTR_CTRL); } else { qusb_phy_enable_power(qphy, true); if (qphy->tcsr_clamp_dig_n) writel_relaxed(0x1, qphy->tcsr_clamp_dig_n); qusb_phy_enable_clocks(qphy, true); } qphy->suspended = false; Loading Loading @@ -669,15 +785,61 @@ static int qusb_phy_dpdm_regulator_enable(struct regulator_dev *rdev) dev_dbg(qphy->phy.dev, "%s dpdm_enable:%d\n", __func__, qphy->dpdm_enable); mutex_lock(&qphy->phy_lock); if (!qphy->dpdm_enable) { ret = qusb_phy_enable_power(qphy, true); if (ret < 0) { dev_dbg(qphy->phy.dev, "dpdm regulator enable failed:%d\n", ret); mutex_unlock(&qphy->phy_lock); return ret; } qphy->dpdm_enable = true; if (qphy->put_into_high_z_state) { if (qphy->tcsr_clamp_dig_n) writel_relaxed(0x1, qphy->tcsr_clamp_dig_n); qusb_phy_gdsc(qphy, true); qusb_phy_enable_clocks(qphy, true); dev_dbg(qphy->phy.dev, "RESET QUSB PHY\n"); ret = reset_control_assert(qphy->phy_reset); if (ret) dev_err(qphy->phy.dev, "phyassert failed\n"); usleep_range(100, 150); ret = reset_control_deassert(qphy->phy_reset); if (ret) dev_err(qphy->phy.dev, "deassert failed\n"); /* * 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_gdsc(qphy, false); } } mutex_unlock(&qphy->phy_lock); return ret; } Loading @@ -690,19 +852,25 @@ static int qusb_phy_dpdm_regulator_disable(struct regulator_dev *rdev) dev_dbg(qphy->phy.dev, "%s dpdm_enable:%d\n", __func__, qphy->dpdm_enable); mutex_lock(&qphy->phy_lock); if (qphy->dpdm_enable) { if (!qphy->cable_connected) { if (qphy->tcsr_clamp_dig_n) writel_relaxed(0x0, qphy->tcsr_clamp_dig_n); dev_dbg(qphy->phy.dev, "turn off for HVDCP case\n"); ret = qusb_phy_enable_power(qphy, false); if (ret < 0) { dev_dbg(qphy->phy.dev, "dpdm regulator disable failed:%d\n", ret); mutex_unlock(&qphy->phy_lock); return ret; } } qphy->dpdm_enable = false; } mutex_unlock(&qphy->phy_lock); return ret; } Loading Loading @@ -794,6 +962,9 @@ static int qusb_phy_probe(struct platform_device *pdev) "qcom,tune2-efuse-num-bits", &qphy->tune2_efuse_num_of_bits); } of_property_read_u32(dev->of_node, "qcom,tune2-efuse-correction", &qphy->tune2_efuse_correction); if (ret) { dev_err(dev, "DT Value for tune2 efuse is invalid.\n"); Loading Loading @@ -829,6 +1000,17 @@ static int qusb_phy_probe(struct platform_device *pdev) } } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tcsr_clamp_dig_n_1p8"); if (res) { qphy->tcsr_clamp_dig_n = devm_ioremap_nocache(dev, res->start, resource_size(res)); if (IS_ERR(qphy->tcsr_clamp_dig_n)) { dev_err(dev, "err reading tcsr_clamp_dig_n\n"); qphy->tcsr_clamp_dig_n = NULL; } } qphy->ref_clk_src = devm_clk_get(dev, "ref_clk_src"); if (IS_ERR(qphy->ref_clk_src)) dev_dbg(dev, "clk get failed for ref_clk_src\n"); Loading @@ -847,6 +1029,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 @@ -981,6 +1191,7 @@ static int qusb_phy_probe(struct platform_device *pdev) return PTR_ERR(qphy->vdda18); } mutex_init(&qphy->phy_lock); platform_set_drvdata(pdev, qphy); qphy->phy.label = "msm-qusb-phy"; Loading Loading @@ -1010,6 +1221,10 @@ static int qusb_phy_probe(struct platform_device *pdev) if (ret) usb_remove_phy(&qphy->phy); /* de-assert clamp dig n to reduce leakage on 1p8 upon boot up */ if (qphy->tcsr_clamp_dig_n) writel_relaxed(0x0, qphy->tcsr_clamp_dig_n); return ret; } Loading Loading
Documentation/devicetree/bindings/usb/msm-phy.txt +5 −0 Original line number Diff line number Diff line Loading @@ -160,6 +160,9 @@ Optional properties: "efuse_addr": EFUSE address to read and update analog tune parameter. "emu_phy_base" : phy base address used for programming emulation target phy. "ref_clk_addr" : ref_clk bcr address used for on/off ref_clk before reset. "tcsr_clamp_dig_n" : To enable/disable digital clamp to the phy. When de-asserted, it will prevent random leakage from qusb2 phy resulting from out of sequence turn on/off of 1p8, 3p3 and DVDD regulators. "refgen_north_bg_reg" : address used to read REFGEN status for overriding QUSB PHY register. - clocks: a list of phandles to the PHY clocks. Use as per Documentation/devicetree/bindings/clock/clock-bindings.txt Loading @@ -179,6 +182,8 @@ Optional properties: - qcom,major-rev: provide major revision number to differentiate power up sequence. default is 2.0 - pinctrl-names/pinctrl-0/1: The GPIOs configured as output function. Names represents "active" state when attached in host mode and "suspend" state when detached. - qcom,tune2-efuse-correction: The value to be adjusted from fused value for improved rise/fall times. Example: qusb_phy: qusb@f9b39000 { Loading
drivers/usb/phy/phy-msm-qusb.c +249 −34 Original line number Diff line number Diff line Loading @@ -45,6 +45,8 @@ #define FREEZIO_N BIT(1) #define POWER_DOWN BIT(0) #define QUSB2PHY_PORT_TEST_CTRL 0xB8 #define QUSB2PHY_PWR_CTRL1 0x210 #define PWR_CTRL1_CLAMP_N_EN BIT(1) #define PWR_CTRL1_POWR_DOWN BIT(0) Loading @@ -68,10 +70,7 @@ #define QUSB2PHY_PORT_TUNE2 0x84 #define QUSB2PHY_PORT_TUNE3 0x88 #define QUSB2PHY_PORT_TUNE4 0x8C /* In case Efuse register shows zero, use this value */ #define TUNE2_DEFAULT_HIGH_NIBBLE 0xB #define TUNE2_DEFAULT_LOW_NIBBLE 0x3 #define QUSB2PHY_PORT_TUNE5 0x90 /* Get TUNE2's high nibble value read from efuse */ #define TUNE2_HIGH_NIBBLE_VAL(val, pos, mask) ((val >> pos) & mask) Loading @@ -98,21 +97,42 @@ #define QUSB2PHY_REFCLK_ENABLE BIT(0) unsigned int tune2; module_param(tune2, uint, S_IRUGO | S_IWUSR); static unsigned int tune1; module_param(tune1, uint, 0644); MODULE_PARM_DESC(tune1, "QUSB PHY TUNE1"); static unsigned int tune2; module_param(tune2, uint, 0644); MODULE_PARM_DESC(tune2, "QUSB PHY TUNE2"); static unsigned int tune3; module_param(tune3, uint, 0644); MODULE_PARM_DESC(tune3, "QUSB PHY TUNE3"); static unsigned int tune4; module_param(tune4, uint, 0644); MODULE_PARM_DESC(tune4, "QUSB PHY TUNE4"); static unsigned int tune5; module_param(tune5, uint, 0644); MODULE_PARM_DESC(tune5, "QUSB PHY TUNE5"); struct qusb_phy { struct usb_phy phy; void __iomem *base; void __iomem *tune2_efuse_reg; void __iomem *ref_clk_base; void __iomem *tcsr_clamp_dig_n; struct clk *ref_clk_src; struct clk *ref_clk; struct clk *cfg_ahb_clk; struct reset_control *phy_reset; struct clk *iface_clk; struct clk *core_clk; struct regulator *gdsc; struct regulator *vdd; struct regulator *vdda33; struct regulator *vdda18; Loading @@ -124,6 +144,7 @@ struct qusb_phy { u32 tune2_val; int tune2_efuse_bit_pos; int tune2_efuse_num_of_bits; int tune2_efuse_correction; bool power_enabled; bool clocks_enabled; Loading @@ -145,6 +166,8 @@ struct qusb_phy { int phy_pll_reset_seq_len; int *emu_dcm_reset_seq; int emu_dcm_reset_seq_len; bool put_into_high_z_state; struct mutex phy_lock; }; static void qusb_phy_enable_clocks(struct qusb_phy *qphy, bool on) Loading @@ -155,14 +178,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 @@ -170,6 +201,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 Loading @@ -313,6 +370,7 @@ static void qusb_phy_get_tune2_param(struct qusb_phy *qphy) { u8 num_of_bits; u32 bit_mask = 1; u8 reg_val; pr_debug("%s(): num_of_bits:%d bit_pos:%d\n", __func__, qphy->tune2_efuse_num_of_bits, Loading @@ -326,9 +384,8 @@ static void qusb_phy_get_tune2_param(struct qusb_phy *qphy) /* * Read EFUSE register having TUNE2 parameter's high nibble. * If efuse register shows value as 0x0, then use default value * as 0xB as high nibble. Otherwise use efuse register based * value for this purpose. * If efuse register shows value as 0x0, then use previous value * as it is. Otherwise use efuse register based value for this purpose. */ qphy->tune2_val = readl_relaxed(qphy->tune2_efuse_reg); pr_debug("%s(): bit_mask:%d efuse based tune2 value:%d\n", Loading @@ -337,12 +394,24 @@ static void qusb_phy_get_tune2_param(struct qusb_phy *qphy) qphy->tune2_val = TUNE2_HIGH_NIBBLE_VAL(qphy->tune2_val, qphy->tune2_efuse_bit_pos, bit_mask); if (!qphy->tune2_val) qphy->tune2_val = TUNE2_DEFAULT_HIGH_NIBBLE; /* Update higher nibble of TUNE2 value for better rise/fall times */ if (qphy->tune2_efuse_correction && qphy->tune2_val) { if (qphy->tune2_efuse_correction > 5 || qphy->tune2_efuse_correction < -10) pr_warn("Correction value is out of range : %d\n", qphy->tune2_efuse_correction); else qphy->tune2_val = qphy->tune2_val + qphy->tune2_efuse_correction; } reg_val = readb_relaxed(qphy->base + QUSB2PHY_PORT_TUNE2); if (qphy->tune2_val) { reg_val &= 0x0f; reg_val |= (qphy->tune2_val << 4); } /* Get TUNE2 byte value using high and low nibble value */ qphy->tune2_val = ((qphy->tune2_val << 0x4) | TUNE2_DEFAULT_LOW_NIBBLE); qphy->tune2_val = reg_val; } static void qusb_phy_write_seq(void __iomem *base, u32 *seq, int cnt, Loading Loading @@ -450,7 +519,7 @@ static int qusb_phy_init(struct usb_phy *phy) * and try to read EFUSE value only once i.e. not every USB * cable connect case. */ if (qphy->tune2_efuse_reg) { if (qphy->tune2_efuse_reg && !tune2) { if (!qphy->tune2_val) qusb_phy_get_tune2_param(qphy); Loading @@ -460,13 +529,29 @@ static int qusb_phy_init(struct usb_phy *phy) qphy->base + QUSB2PHY_PORT_TUNE2); } /* If tune2 modparam set, override tune2 value */ if (tune2) { pr_debug("%s(): (modparam) TUNE2 val:0x%02x\n", __func__, tune2); /* If tune modparam set, override tune value */ pr_debug("%s():userspecified modparams TUNEX val:0x%x %x %x %x %x\n", __func__, tune1, tune2, tune3, tune4, tune5); if (tune1) writel_relaxed(tune1, qphy->base + QUSB2PHY_PORT_TUNE1); if (tune2) writel_relaxed(tune2, qphy->base + QUSB2PHY_PORT_TUNE2); } if (tune3) writel_relaxed(tune3, qphy->base + QUSB2PHY_PORT_TUNE3); if (tune4) writel_relaxed(tune4, qphy->base + QUSB2PHY_PORT_TUNE4); if (tune5) writel_relaxed(tune5, qphy->base + QUSB2PHY_PORT_TUNE5); /* ensure above writes are completed before re-enabling PHY */ wmb(); Loading Loading @@ -596,27 +681,55 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend) writel_relaxed(intr_mask, qphy->base + QUSB2PHY_PORT_INTR_CTRL); if (linestate & (LINESTATE_DP | LINESTATE_DM)) { /* enable phy auto-resume */ writel_relaxed(0x0C, qphy->base + QUSB2PHY_PORT_TEST_CTRL); /* flush the previous write before next write */ wmb(); writel_relaxed(0x04, qphy->base + QUSB2PHY_PORT_TEST_CTRL); } dev_dbg(phy->dev, "%s: intr_mask = %x\n", __func__, intr_mask); /* Makes sure that above write goes through */ wmb(); qusb_phy_enable_clocks(qphy, false); } else { /* Disconnect case */ mutex_lock(&qphy->phy_lock); /* 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(POWER_DOWN, qphy->base + QUSB2PHY_PORT_POWERDOWN); /* Make sure that above write is completed */ wmb(); qusb_phy_enable_clocks(qphy, false); if (qphy->tcsr_clamp_dig_n) writel_relaxed(0x0, qphy->tcsr_clamp_dig_n); /* Do not disable power rails if there is vote for it */ if (!qphy->dpdm_enable) qusb_phy_enable_power(qphy, false); else dev_dbg(phy->dev, "race with rm_pulldown. Keep ldo ON\n"); mutex_unlock(&qphy->phy_lock); /* * 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 @@ -629,6 +742,9 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend) qphy->base + QUSB2PHY_PORT_INTR_CTRL); } else { qusb_phy_enable_power(qphy, true); if (qphy->tcsr_clamp_dig_n) writel_relaxed(0x1, qphy->tcsr_clamp_dig_n); qusb_phy_enable_clocks(qphy, true); } qphy->suspended = false; Loading Loading @@ -669,15 +785,61 @@ static int qusb_phy_dpdm_regulator_enable(struct regulator_dev *rdev) dev_dbg(qphy->phy.dev, "%s dpdm_enable:%d\n", __func__, qphy->dpdm_enable); mutex_lock(&qphy->phy_lock); if (!qphy->dpdm_enable) { ret = qusb_phy_enable_power(qphy, true); if (ret < 0) { dev_dbg(qphy->phy.dev, "dpdm regulator enable failed:%d\n", ret); mutex_unlock(&qphy->phy_lock); return ret; } qphy->dpdm_enable = true; if (qphy->put_into_high_z_state) { if (qphy->tcsr_clamp_dig_n) writel_relaxed(0x1, qphy->tcsr_clamp_dig_n); qusb_phy_gdsc(qphy, true); qusb_phy_enable_clocks(qphy, true); dev_dbg(qphy->phy.dev, "RESET QUSB PHY\n"); ret = reset_control_assert(qphy->phy_reset); if (ret) dev_err(qphy->phy.dev, "phyassert failed\n"); usleep_range(100, 150); ret = reset_control_deassert(qphy->phy_reset); if (ret) dev_err(qphy->phy.dev, "deassert failed\n"); /* * 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_gdsc(qphy, false); } } mutex_unlock(&qphy->phy_lock); return ret; } Loading @@ -690,19 +852,25 @@ static int qusb_phy_dpdm_regulator_disable(struct regulator_dev *rdev) dev_dbg(qphy->phy.dev, "%s dpdm_enable:%d\n", __func__, qphy->dpdm_enable); mutex_lock(&qphy->phy_lock); if (qphy->dpdm_enable) { if (!qphy->cable_connected) { if (qphy->tcsr_clamp_dig_n) writel_relaxed(0x0, qphy->tcsr_clamp_dig_n); dev_dbg(qphy->phy.dev, "turn off for HVDCP case\n"); ret = qusb_phy_enable_power(qphy, false); if (ret < 0) { dev_dbg(qphy->phy.dev, "dpdm regulator disable failed:%d\n", ret); mutex_unlock(&qphy->phy_lock); return ret; } } qphy->dpdm_enable = false; } mutex_unlock(&qphy->phy_lock); return ret; } Loading Loading @@ -794,6 +962,9 @@ static int qusb_phy_probe(struct platform_device *pdev) "qcom,tune2-efuse-num-bits", &qphy->tune2_efuse_num_of_bits); } of_property_read_u32(dev->of_node, "qcom,tune2-efuse-correction", &qphy->tune2_efuse_correction); if (ret) { dev_err(dev, "DT Value for tune2 efuse is invalid.\n"); Loading Loading @@ -829,6 +1000,17 @@ static int qusb_phy_probe(struct platform_device *pdev) } } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tcsr_clamp_dig_n_1p8"); if (res) { qphy->tcsr_clamp_dig_n = devm_ioremap_nocache(dev, res->start, resource_size(res)); if (IS_ERR(qphy->tcsr_clamp_dig_n)) { dev_err(dev, "err reading tcsr_clamp_dig_n\n"); qphy->tcsr_clamp_dig_n = NULL; } } qphy->ref_clk_src = devm_clk_get(dev, "ref_clk_src"); if (IS_ERR(qphy->ref_clk_src)) dev_dbg(dev, "clk get failed for ref_clk_src\n"); Loading @@ -847,6 +1029,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 @@ -981,6 +1191,7 @@ static int qusb_phy_probe(struct platform_device *pdev) return PTR_ERR(qphy->vdda18); } mutex_init(&qphy->phy_lock); platform_set_drvdata(pdev, qphy); qphy->phy.label = "msm-qusb-phy"; Loading Loading @@ -1010,6 +1221,10 @@ static int qusb_phy_probe(struct platform_device *pdev) if (ret) usb_remove_phy(&qphy->phy); /* de-assert clamp dig n to reduce leakage on 1p8 upon boot up */ if (qphy->tcsr_clamp_dig_n) writel_relaxed(0x0, qphy->tcsr_clamp_dig_n); return ret; } Loading