usb: phy: qusb: Make sure QUSB PHY is into proper state

On some platforms, QUSB PHY's DVDD related power supply (LDO) is
not always ON. Hence when this power supply is switched off, QUSB
PHY's register configuration is not retained. QUSB PHY state
is unknown when required LDOs are turned ON with USB cable connect
case and may interfere charger detection. Hence use suggested sequence
which involves resetting QUSB PHY and performing few set of QUSB PHY
register configuration to bring QUSB PHY into non-driving mode.

Also it is required to disable QUSB2PHY level shifter by writing to
TCSR_QUSB2PHY_CLAMP_DIG_N_1P8 register during disconnect to avoid
leakage current. Hence add support for the same.

Change-Id: I30b8488a1c19815601e6a1c5bcbdeed53715f8fa
Signed-off-by: Vijayavardhan Vennapusa <vvreddy@codeaurora.org>
This commit is contained in:
Vijayavardhan Vennapusa 2016-11-10 12:49:54 +05:30
parent 0cc66e3892
commit b833212e44

View file

@ -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");