diff options
| author | Subhash Jadavani <subhashj@codeaurora.org> | 2015-01-09 14:49:31 -0800 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-22 11:01:12 -0700 |
| commit | 087e7bfd341fdecf51a74f657140be155505e7cd (patch) | |
| tree | 1caaf4c60de9351a2177618f27489846665c7a89 /drivers/phy | |
| parent | c8529834348e812863f03e17434b53c75ee50141 (diff) | |
phy: qcom-ufs-qmp-14nm: add hibern8 workaround
Due to missing reset in the UFS PHY logic, the pll may not lock following
analog power collapse. As a result the common block of the PHY must be put
into reset during hibernate entry and taken out of reset during hibernate
exit. SW needs to save ave the calibrated VCO codes after the PHY power up
sequence is completed, saving these codes will save substantial time on
hibernate exit (<50us vs. 1.7ms).
Change-Id: Id5f5eab04f1a1f93179cf9e5cdd3c7c8be4b17af
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
Diffstat (limited to 'drivers/phy')
| -rw-r--r-- | drivers/phy/phy-qcom-ufs-qmp-14nm.c | 112 | ||||
| -rw-r--r-- | drivers/phy/phy-qcom-ufs-qmp-14nm.h | 6 |
2 files changed, 111 insertions, 7 deletions
diff --git a/drivers/phy/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/phy-qcom-ufs-qmp-14nm.c index 9299cf190b80..c6cf41964d8d 100644 --- a/drivers/phy/phy-qcom-ufs-qmp-14nm.c +++ b/drivers/phy/phy-qcom-ufs-qmp-14nm.c @@ -99,14 +99,50 @@ out: } static -void ufs_qcom_phy_qmp_14nm_power_control(struct ufs_qcom_phy *phy, bool val) +void ufs_qcom_phy_qmp_14nm_power_control(struct ufs_qcom_phy *phy, + bool is_pwr_collapse) { - writel_relaxed(val ? 0x1 : 0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); - /* - * Before any transactions involving PHY, ensure PHY knows - * that it's analog rail is powered ON (or OFF). - */ - mb(); + bool is_workaround_req = false; + + if (phy->quirks & + UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE) + is_workaround_req = true; + + if (is_pwr_collapse) { + if (is_workaround_req) { + /* assert common reset before analog power collapse */ + writel_relaxed(0x1, phy->mmio + QSERDES_COM_SW_RESET); + /* + * make sure that reset is propogated before analog + * power collapse + */ + mb(); + } + /* apply analog power collapse */ + writel_relaxed(0x1, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); + /* + * Make sure that PHY knows its analog rail is going to be + * powered OFF. + */ + mb(); + } else { + /* bring PHY out of analog power collapse */ + writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); + /* + * Before any transactions involving PHY, ensure PHY knows + * that it's analog rail is powered ON. + */ + mb(); + if (is_workaround_req) { + /* + * de-assert common reset after coming out of analog + * power collapse + */ + writel_relaxed(0x0, phy->mmio + QSERDES_COM_SW_RESET); + /* make common reset is de-asserted before proceeding */ + mb(); + } + } } static inline @@ -146,6 +182,64 @@ static inline void ufs_qcom_phy_qmp_14nm_start_serdes(struct ufs_qcom_phy *phy) /* Ensure register value is committed */ mb(); } +/* + * This additional sequence is required as a workaround for following bug: + * Due to missing reset in the UFS PHY logic, the pll may not lock following + * analog power collapse. As a result the common block of the PHY must be put + * into reset during hibernate entry and taken out of reset during hibernate + * exit. The following sequence is required to save the calibrated VCO codes. + * Saving the codes will save substantial time on hibernate exit + * (<50us vs. 1.7ms). + */ +static void +ufs_qcom_phy_qmp_14nm_save_calibrated_vco_code(struct ufs_qcom_phy *phy) +{ + u32 vco_tune_mode0, vco_tune_mode1; + u32 temp; + + /* set common debug bus select */ + writel_relaxed(0x02, phy->mmio + QSERDES_COM_DEBUG_BUS_SEL); + /* apply debug bus select before reading the debug bus registers */ + mb(); + + /* vco_tune_mode0[7:0]: Read VCO tuning code for Series A */ + vco_tune_mode0 = readl_relaxed(phy->mmio + QSERDES_COM_DEBUG_BUS0) + & 0xFF; + + temp = readl_relaxed(phy->mmio + QSERDES_COM_DEBUG_BUS1); + /* vco_tune_mode0[9:8]: Read VCO tuning code for Series A */ + vco_tune_mode0 |= (temp & 0x300); + /* vco_tune_mode1[5:0]: Read VCO tuning code for Series B */ + vco_tune_mode1 = temp & 0x3F; + + temp = readl_relaxed(phy->mmio + QSERDES_COM_DEBUG_BUS2); + /* vco_tune_mode1[9:6]: Read VCO tuning code for Series B */ + vco_tune_mode1 |= (temp & 0x3C0); + + /* vco_tune_mode0[7:0] */ + writel_relaxed((vco_tune_mode0 & 0xff), + phy->mmio + QSERDES_COM_VCO_TUNE1_MODE0); + /* vco_tune_mode0[9:8] */ + writel_relaxed((vco_tune_mode0 & 0x300) >> 8, + phy->mmio + QSERDES_COM_VCO_TUNE2_MODE0); + /* vco_tune_mode1[7:0] */ + writel_relaxed((vco_tune_mode1 & 0xff), + phy->mmio + QSERDES_COM_VCO_TUNE1_MODE1); + /* vco_tune_mode1[9:8] */ + writel_relaxed((vco_tune_mode1 & 0x300) >> 8, + phy->mmio + QSERDES_COM_VCO_TUNE2_MODE1); + /* apply vco tuning codes before enabling the vco bypass mode */ + mb(); + + temp = readl_relaxed(phy->mmio + QSERDES_COM_VCO_TUNE_CTRL); + /* + * Bypass the calibrated code and use the stored code for + * both A and B series + */ + writel_relaxed((temp | 0xC), phy->mmio + QSERDES_COM_VCO_TUNE_CTRL); + /* apply this configuration before return */ + mb(); +} static int ufs_qcom_phy_qmp_14nm_is_pcs_ready(struct ufs_qcom_phy *phy_common) { @@ -171,6 +265,10 @@ static int ufs_qcom_phy_qmp_14nm_is_pcs_ready(struct ufs_qcom_phy *phy_common) mb(); } + if (phy_common->quirks & + UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE) + ufs_qcom_phy_qmp_14nm_save_calibrated_vco_code(phy_common); + out: return err; } diff --git a/drivers/phy/phy-qcom-ufs-qmp-14nm.h b/drivers/phy/phy-qcom-ufs-qmp-14nm.h index e2f55aae8ad3..d4704cc28b6f 100644 --- a/drivers/phy/phy-qcom-ufs-qmp-14nm.h +++ b/drivers/phy/phy-qcom-ufs-qmp-14nm.h @@ -66,9 +66,15 @@ #define QSERDES_COM_CLK_SELECT COM_OFF(0x174) #define QSERDES_COM_HSCLK_SEL COM_OFF(0x178) #define QSERDES_COM_CORECLK_DIV COM_OFF(0x184) +#define QSERDES_COM_SW_RESET COM_OFF(0x188) #define QSERDES_COM_CORE_CLK_EN COM_OFF(0x18C) #define QSERDES_COM_CMN_CONFIG COM_OFF(0x194) #define QSERDES_COM_SVS_MODE_CLK_SEL COM_OFF(0x19C) +#define QSERDES_COM_DEBUG_BUS0 COM_OFF(0x1A0) +#define QSERDES_COM_DEBUG_BUS1 COM_OFF(0x1A4) +#define QSERDES_COM_DEBUG_BUS2 COM_OFF(0x1A8) +#define QSERDES_COM_DEBUG_BUS3 COM_OFF(0x1AC) +#define QSERDES_COM_DEBUG_BUS_SEL COM_OFF(0x1B0) #define QSERDES_COM_CORECLK_DIV_MODE1 COM_OFF(0x1BC) /* UFS PHY registers */ |
