summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/phy/phy-msm-qusb.c86
1 files changed, 74 insertions, 12 deletions
diff --git a/drivers/usb/phy/phy-msm-qusb.c b/drivers/usb/phy/phy-msm-qusb.c
index 97a15355c146..839d701ec9b5 100644
--- a/drivers/usb/phy/phy-msm-qusb.c
+++ b/drivers/usb/phy/phy-msm-qusb.c
@@ -109,6 +109,7 @@ struct qusb_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;
@@ -147,6 +148,7 @@ 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;
};
static void qusb_phy_enable_clocks(struct qusb_phy *qphy, bool on)
@@ -329,6 +331,47 @@ static int qusb_phy_update_dpdm(struct usb_phy *phy, int value)
dev_dbg(phy->dev, "DP_DM_F: rm_pulldown:%d\n",
qphy->rm_pulldown);
}
+
+ if (qphy->put_into_high_z_state) {
+ if (qphy->tcsr_clamp_dig_n)
+ writel_relaxed(0x1,
+ qphy->tcsr_clamp_dig_n);
+
+ qusb_phy_enable_clocks(qphy, true);
+
+ dev_dbg(phy->dev, "RESET QUSB PHY\n");
+ ret = reset_control_assert(qphy->phy_reset);
+ if (ret)
+ dev_err(phy->dev, "phyassert failed\n");
+ usleep_range(100, 150);
+ ret = reset_control_deassert(qphy->phy_reset);
+ if (ret)
+ dev_err(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);
+ }
}
break;
@@ -337,6 +380,9 @@ static int qusb_phy_update_dpdm(struct usb_phy *phy, int value)
dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DPR_DMR\n");
if (qphy->rm_pulldown) {
if (!qphy->cable_connected) {
+ if (qphy->tcsr_clamp_dig_n)
+ writel_relaxed(0x0,
+ qphy->tcsr_clamp_dig_n);
dev_dbg(phy->dev, "turn off for HVDCP case\n");
ret = qusb_phy_enable_power(qphy, false);
}
@@ -647,22 +693,23 @@ 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);
+ /* 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);
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 {
@@ -675,6 +722,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;
@@ -845,6 +895,18 @@ 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");