From c8529834348e812863f03e17434b53c75ee50141 Mon Sep 17 00:00:00 2001 From: Subhash Jadavani Date: Fri, 9 Jan 2015 12:54:11 -0800 Subject: [PATCH] phy: qcom-ufs-qmp-14nm: add svs mode workaround UFS PHY power up calibration sequence on UFS controller revision 2.0.0 can't have SVS mode configuration otherwise calibration result cannot be used in HS-G3. So there are additional register writes must be done after the PHY is initialized but before the controller requests hibernate exit. Also as this issue is not present on UFS controller revision 2.1.0, SVS mode configuration registers are written as part of the power up calibration sequence itself. This change takes care of these issues related to SVS mode. Change-Id: Ib431d98345224db13f1d68197e948bb077c95080 Signed-off-by: Subhash Jadavani [venkatg@codeaurora.org: resolved trivial merge conflicts, drop include/linux/phy/phy-qcom-ufs.h] Signed-off-by: Venkat Gopalakrishnan --- drivers/phy/phy-qcom-ufs-qmp-14nm.c | 57 +++++++++++++++++--- drivers/phy/phy-qcom-ufs-qmp-14nm.h | 81 ++++++++++++++++++++++++++++- 2 files changed, 130 insertions(+), 8 deletions(-) diff --git a/drivers/phy/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/phy-qcom-ufs-qmp-14nm.c index 79756ec074c5..9299cf190b80 100644 --- a/drivers/phy/phy-qcom-ufs-qmp-14nm.c +++ b/drivers/phy/phy-qcom-ufs-qmp-14nm.c @@ -21,13 +21,35 @@ static int ufs_qcom_phy_qmp_14nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, bool is_rate_B) { - int tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A); - int tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); int err; + int tbl_size_A, tbl_size_B; + struct ufs_qcom_phy_calibration *tbl_A, *tbl_B; + u8 major = ufs_qcom_phy->host_ctrl_rev_major; + u16 minor = ufs_qcom_phy->host_ctrl_rev_minor; + u16 step = ufs_qcom_phy->host_ctrl_rev_step; - err = ufs_qcom_phy_calibrate(ufs_qcom_phy, phy_cal_table_rate_A, - tbl_size_A, phy_cal_table_rate_B, tbl_size_B, is_rate_B); + tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); + tbl_B = phy_cal_table_rate_B; + if ((major == 0x2) && (minor == 0x000) && (step == 0x0000)) { + tbl_A = phy_cal_table_rate_A_2_0_0; + tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_2_0_0); + } else if ((major == 0x2) && (minor == 0x001) && (step == 0x0000)) { + tbl_A = phy_cal_table_rate_A_2_1_0; + tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_2_1_0); + } else { + dev_err(ufs_qcom_phy->dev, + "%s: Unknown UFS-PHY version (major 0x%x minor 0x%x step 0x%x), no calibration values\n", + __func__, major, minor, step); + err = -ENODEV; + goto out; + } + + err = ufs_qcom_phy_calibrate(ufs_qcom_phy, + tbl_A, tbl_size_A, + tbl_B, tbl_size_B, + rate); +out: if (err) dev_err(ufs_qcom_phy->dev, "%s: ufs_qcom_phy_calibrate() failed %d\n", @@ -38,8 +60,14 @@ int ufs_qcom_phy_qmp_14nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, static void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy *phy_common) { - phy_common->quirks = - UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; + u8 major = phy_common->host_ctrl_rev_major; + u16 minor = phy_common->host_ctrl_rev_minor; + u16 step = phy_common->host_ctrl_rev_step; + + if ((major == 0x2) && (minor == 0x000) && (step == 0x0000)) + phy_common->quirks = + UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE | + UFS_QCOM_PHY_QUIRK_SVS_MODE; } static int ufs_qcom_phy_qmp_14nm_init(struct phy *generic_phy) @@ -126,9 +154,24 @@ static int ufs_qcom_phy_qmp_14nm_is_pcs_ready(struct ufs_qcom_phy *phy_common) err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS, val, (val & MASK_PCS_READY), 10, 1000000); - if (err) + if (err) { dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n", __func__, err); + goto out; + } + + if (phy_common->quirks & UFS_QCOM_PHY_QUIRK_SVS_MODE) { + int i; + + for (i = 0; i < ARRAY_SIZE(phy_svs_mode_config_2_0_0); i++) + writel_relaxed(phy_svs_mode_config_2_0_0[i].cfg_value, + (phy_common->mmio + + phy_svs_mode_config_2_0_0[i].reg_offset)); + /* apply above configuration immediately */ + mb(); + } + +out: return err; } diff --git a/drivers/phy/phy-qcom-ufs-qmp-14nm.h b/drivers/phy/phy-qcom-ufs-qmp-14nm.h index e3e2f3a10b92..e2f55aae8ad3 100644 --- a/drivers/phy/phy-qcom-ufs-qmp-14nm.h +++ b/drivers/phy/phy-qcom-ufs-qmp-14nm.h @@ -109,7 +109,7 @@ struct ufs_qcom_phy_qmp_14nm { struct ufs_qcom_phy common_cfg; }; -static struct ufs_qcom_phy_calibration phy_cal_table_rate_A[] = { +static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_2_0_0[] = { UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_CONFIG, 0x0e), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0xd7), @@ -174,8 +174,87 @@ static struct ufs_qcom_phy_calibration phy_cal_table_rate_A[] = { UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0E), }; +/* + * For 2.1.0 revision, SVS mode configuration can be part of PHY power + * up sequence itself. + */ +static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_2_1_0[] = { + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_PWM_GEAR_BAND, 0x15), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_CONFIG, 0x0e), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0xd7), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CLK_SELECT, 0x30), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x06), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TIMER, 0x0a), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_HSCLK_SEL, 0x05), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV, 0x0a), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV_MODE1, 0x0a), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_EN, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_CTRL, 0x10), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x20), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORE_CLK_EN, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_CFG, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x14), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE0, 0x82), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE0, 0x0b), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE0, 0x28), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE0, 0x02), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE0, 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE0, 0x0c), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE1, 0x98), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE1, 0x0b), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE1, 0x16), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE1, 0x28), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x80), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE1, 0xd6), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE1, 0x32), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE1, 0x0f), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE1, 0x00), + + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN, 0x45), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE, 0x02), + + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_LVL, 0x24), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL, 0x02), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_INTERFACE_MODE, 0x40), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x18), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_TERM_BW, 0x5B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB, 0xFF), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB, 0x3F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB, 0xFF), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0E), +}; + static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = { UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x54), }; +/* + * For 2.0.0 revision, apply this SVS mode configuration after PHY power + * up sequence is completed. + */ +static struct ufs_qcom_phy_calibration phy_svs_mode_config_2_0_0[] = { + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_INTERFACE_MODE, 0x40), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_PWM_GEAR_BAND, 0x15), +}; + #endif