diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 75abd3d5b974..bb37686f3901 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -81,7 +81,7 @@ static int ufs_qcom_init_sysfs(struct ufs_hba *hba); static int ufs_qcom_update_qos_constraints(struct qos_cpu_group *qcg, enum constraint type); static int ufs_qcom_unvote_qos_all(struct ufs_hba *hba); - +static void ufs_qcom_parse_g4_workaround_flag(struct ufs_qcom_host *host); static int ufs_qcom_get_pwr_dev_param(struct ufs_qcom_dev_params *qcom_param, struct ufs_pa_layer_attr *dev_max, struct ufs_pa_layer_attr *agreed_pwr) @@ -813,6 +813,61 @@ static int ufs_qcom_set_dme_vs_core_clk_ctrl_max_freq_mode(struct ufs_hba *hba) return err; } +/** + * ufs_qcom_bypass_cfgready_signal - Tunes PA_VS_CONFIG_REG1 and + * PA_VS_CONFIG_REG2 vendor specific attributes of local unipro + * to bypass CFGREADY signal on Config interface between UFS + * controller and PHY. + * + * The issue is related to config signals sampling from PHY + * to controller. The PHY signals which are driven by 150MHz + * clock and sampled by 300MHz instead of 150MHZ. + * + * The issue will be seen when only one of tx_cfg_rdyn_0 + * and tx_cfg_rdyn_1 is 0 around sampling clock edge and + * if timing is not met as timing margin for some devices is + * very less in one of the corner. + * + * To workaround this issue, controller should bypass the Cfgready + * signal(TX_CFGREADY and RX_CFGREDY) because controller still wait + * for another signal tx_savestatusn which will serve same purpose. + * + * The corresponding HW CR: 'QCTDD06985523' UFS HSG4 test fails + * in SDF MAX GLS is linked to this issue. + */ +static int ufs_qcom_bypass_cfgready_signal(struct ufs_hba *hba) +{ + int err = 0; + u32 pa_vs_config_reg1; + u32 pa_vs_config_reg2; + u32 mask; + + err = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_VS_CONFIG_REG1), + &pa_vs_config_reg1); + if (err) + goto out; + + err = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_VS_CONFIG_REG1), + (pa_vs_config_reg1 | BIT_TX_EOB_COND)); + if (err) + goto out; + + err = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_VS_CONFIG_REG2), + &pa_vs_config_reg2); + if (err) + goto out; + + mask = (BIT_RX_EOB_COND | BIT_LINKCFG_WAIT_LL1_RX_CFG_RDY | + H8_ENTER_COND_MASK); + pa_vs_config_reg2 = (pa_vs_config_reg2 & ~mask) | + (0x2 << H8_ENTER_COND_OFFSET); + + err = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_VS_CONFIG_REG2), + (pa_vs_config_reg2)); +out: + return err; +} + static int ufs_qcom_link_startup_notify(struct ufs_hba *hba, enum ufs_notify_change_status status) { @@ -853,6 +908,9 @@ static int ufs_qcom_link_startup_notify(struct ufs_hba *hba, err = ufshcd_disable_host_tx_lcc(hba); if (err) goto out; + + if (host->bypass_g4_cfgready) + err = ufs_qcom_bypass_cfgready_signal(hba); break; case POST_CHANGE: ufs_qcom_link_startup_post_change(hba); @@ -2602,6 +2660,7 @@ static int ufs_qcom_init(struct ufs_hba *hba) ufs_qcom_parse_pm_level(hba); ufs_qcom_parse_limits(host); + ufs_qcom_parse_g4_workaround_flag(host); ufs_qcom_parse_lpm(host); if (host->disable_lpm) pm_runtime_forbid(host->hba->dev); @@ -3180,6 +3239,20 @@ static void ufs_qcom_parse_limits(struct ufs_qcom_host *host) of_property_read_u32(np, "limit-phy-submode", &host->limit_phy_submode); } +/* + * ufs_qcom_parse_g4_workaround_flag - read bypass-g4-cfgready entry from DT + */ +static void ufs_qcom_parse_g4_workaround_flag(struct ufs_qcom_host *host) +{ + struct device_node *np = host->hba->dev->of_node; + const char *str = "bypass-g4-cfgready"; + + if (!np) + return; + + host->bypass_g4_cfgready = of_property_read_bool(np, str); +} + /* * ufs_qcom_parse_lpm - read from DTS whether LPM modes should be disabled. */ diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h index d7aa08fd9200..ee4b18e16226 100644 --- a/drivers/scsi/ufs/ufs-qcom.h +++ b/drivers/scsi/ufs/ufs-qcom.h @@ -168,8 +168,16 @@ enum ufs_qcom_phy_init_type { /* QUniPro Vendor specific attributes */ #define PA_VS_CONFIG_REG1 0x9000 -#define SAVECONFIGTIME_MODE_MASK 0x6000 -#define DME_VS_CORE_CLK_CTRL 0xD002 +#define BIT_TX_EOB_COND BIT(23) +#define PA_VS_CONFIG_REG2 0x9005 +#define H8_ENTER_COND_OFFSET 0x6 +#define H8_ENTER_COND_MASK GENMASK(6, 7) +#define BIT_RX_EOB_COND BIT(5) +#define BIT_LINKCFG_WAIT_LL1_RX_CFG_RDY BIT(26) +#define SAVECONFIGTIME_MODE_MASK 0x6000 +#define DME_VS_CORE_CLK_CTRL 0xD002 + + /* bit and mask definitions for DME_VS_CORE_CLK_CTRL attribute */ #define DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT BIT(8) #define DME_VS_CORE_CLK_CTRL_MAX_CORE_CLK_1US_CYCLES_MASK 0xFF @@ -367,6 +375,7 @@ struct ufs_qcom_host { atomic_t scale_up; atomic_t clks_on; struct ufs_qcom_qos_req *ufs_qos; + bool bypass_g4_cfgready; }; static inline u32