[PATCH v2 08/11] scsi: ufs: ufs-qcom: Implement vops tx_eqtr_notify()

Can Guo posted 11 patches 1 month, 1 week ago
Only 10 patches received!
There is a newer version of this series
[PATCH v2 08/11] scsi: ufs: ufs-qcom: Implement vops tx_eqtr_notify()
Posted by Can Guo 1 month, 1 week ago
On some platforms, HW does not support triggering TX EQTR from the most
reliable High-Speed (HS) Gear (HS Gear1), but only allows to trigger TX
EQTR for the target HS Gear from the same HS Gear. To work around the HW
limitation, implement vops tx_eqtr_notify() to change Power Mode to the
target TX EQTR HS Gear prior to TX EQTR procedure and change Power Mode
back to HS Gear1 (the most reliable gear) post TX EQTR procedure.

Signed-off-by: Can Guo <can.guo@oss.qualcomm.com>
---
 drivers/ufs/host/ufs-qcom.c | 63 +++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
index 3a9279066192..1e074058f23d 100644
--- a/drivers/ufs/host/ufs-qcom.c
+++ b/drivers/ufs/host/ufs-qcom.c
@@ -2512,6 +2512,68 @@ static u32 ufs_qcom_freq_to_gear_speed(struct ufs_hba *hba, unsigned long freq)
 	return min_t(u32, gear, hba->max_pwr_info.info.gear_rx);
 }
 
+static int ufs_qcom_change_power_mode(struct ufs_hba *hba,
+				      struct ufs_pa_layer_attr *pwr_mode,
+				      enum ufshcd_pmc_policy pmc_policy)
+{
+	int ret;
+
+	ret = ufs_qcom_pwr_change_notify(hba, PRE_CHANGE, pwr_mode);
+	if (ret) {
+		dev_err(hba->dev, "Power change notify (PRE_CHANGE) failed: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = ufshcd_change_power_mode(hba, pwr_mode, pmc_policy);
+	if (ret)
+		return ret;
+
+	ufs_qcom_pwr_change_notify(hba, POST_CHANGE, pwr_mode);
+
+	return ret;
+}
+
+static int ufs_qcom_tx_eqtr_notify(struct ufs_hba *hba,
+				   enum ufs_notify_change_status status,
+				   struct ufs_pa_layer_attr *pwr_mode)
+{
+	struct ufs_qcom_host *host = ufshcd_get_variant(hba);
+	struct ufs_pa_layer_attr pwr_mode_hs_g1 = {
+		.gear_rx = UFS_HS_G1,
+		.gear_tx = UFS_HS_G1,
+		.lane_rx = pwr_mode->lane_rx,
+		.lane_tx = pwr_mode->lane_tx,
+		.pwr_rx = FAST_MODE,
+		.pwr_tx = FAST_MODE,
+		.hs_rate = pwr_mode->hs_rate,
+	};
+	u32 gear = pwr_mode->gear_tx;
+	u32 rate = pwr_mode->hs_rate;
+	int ret;
+
+	if (host->hw_ver.major != 0x7 || host->hw_ver.minor > 0x1)
+		return 0;
+
+	if (status == PRE_CHANGE) {
+		/* PMC to target HS Gear. */
+		ret = ufs_qcom_change_power_mode(hba, pwr_mode,
+						 UFSHCD_PMC_POLICY_DONT_FORCE);
+		if (ret)
+			dev_err(hba->dev, "%s: Failed to change power mode to target HS-G%u, Rate-%s: %d\n",
+				__func__, gear, UFS_HS_RATE_STRING(rate), ret);
+	} else {
+		/* PMC back to HS-G1. */
+		ret = ufs_qcom_change_power_mode(hba, &pwr_mode_hs_g1,
+						 UFSHCD_PMC_POLICY_DONT_FORCE);
+		if (ret)
+			dev_err(hba->dev, "%s: Failed to change power mode to HS-G1, Rate-%s: %d\n",
+				__func__, UFS_HS_RATE_STRING(rate), ret);
+	}
+
+	return ret;
+}
+
 /*
  * struct ufs_hba_qcom_vops - UFS QCOM specific variant operations
  *
@@ -2542,6 +2604,7 @@ static const struct ufs_hba_variant_ops ufs_hba_qcom_vops = {
 	.get_outstanding_cqs	= ufs_qcom_get_outstanding_cqs,
 	.config_esi		= ufs_qcom_config_esi,
 	.freq_to_gear_speed	= ufs_qcom_freq_to_gear_speed,
+	.tx_eqtr_notify		= ufs_qcom_tx_eqtr_notify,
 };
 
 static const struct ufs_hba_variant_ops ufs_hba_qcom_sa8255p_vops = {
-- 
2.34.1
Re: [PATCH v2 08/11] scsi: ufs: ufs-qcom: Implement vops tx_eqtr_notify()
Posted by Bean Huo 1 month, 1 week ago
On Wed, 2026-03-04 at 05:53 -0800, Can Guo wrote:
> freq)
>         return min_t(u32, gear, hba->max_pwr_info.info.gear_rx);
>  }
>  
> +static int ufs_qcom_change_power_mode(struct ufs_hba *hba,
> +                                     struct ufs_pa_layer_attr *pwr_mode,
> +                                     enum ufshcd_pmc_policy pmc_policy)
> +{
> +       int ret;
> +
> +       ret = ufs_qcom_pwr_change_notify(hba, PRE_CHANGE, pwr_mode);
> +       if (ret) {
> +               dev_err(hba->dev, "Power change notify (PRE_CHANGE) failed:
> %d\n",
> +                       ret);
> +               return ret;
> +       }
> +
> +       ret = ufshcd_change_power_mode(hba, pwr_mode, pmc_policy);
> +       if (ret)
> +               return ret;
> +
> +       ufs_qcom_pwr_change_notify(hba, POST_CHANGE, pwr_mode);
> +
> +       return ret;
> +}

seems Qcom UFS driver does duplicate notify now, above
ufs_qcom_change_power_mode() does PRE/POST itself, then calls core
ufshcd_change_power_mode() which already does PRE/POST, double side effects? or
I am wrong?

Kind regards, 
Bean

Re: [PATCH v2 08/11] scsi: ufs: ufs-qcom: Implement vops tx_eqtr_notify()
Posted by Can Guo 1 month, 1 week ago
Hi Bean,

On 3/6/2026 5:17 AM, Bean Huo wrote:
> On Wed, 2026-03-04 at 05:53 -0800, Can Guo wrote:
>> freq)
>>          return min_t(u32, gear, hba->max_pwr_info.info.gear_rx);
>>   }
>>   
>> +static int ufs_qcom_change_power_mode(struct ufs_hba *hba,
>> +                                     struct ufs_pa_layer_attr *pwr_mode,
>> +                                     enum ufshcd_pmc_policy pmc_policy)
>> +{
>> +       int ret;
>> +
>> +       ret = ufs_qcom_pwr_change_notify(hba, PRE_CHANGE, pwr_mode);
>> +       if (ret) {
>> +               dev_err(hba->dev, "Power change notify (PRE_CHANGE) failed:
>> %d\n",
>> +                       ret);
>> +               return ret;
>> +       }
>> +
>> +       ret = ufshcd_change_power_mode(hba, pwr_mode, pmc_policy);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ufs_qcom_pwr_change_notify(hba, POST_CHANGE, pwr_mode);
>> +
>> +       return ret;
>> +}
> seems Qcom UFS driver does duplicate notify now, above
> ufs_qcom_change_power_mode() does PRE/POST itself, then calls core
> ufshcd_change_power_mode() which already does PRE/POST, double side effects? or
> I am wrong?
You are right... I made a mistake when I cleaned up the code. Thanks for 
catching it.

Best Regards,
Can Guo.
>
> Kind regards,
> Bean
>

Re: [PATCH v2 08/11] scsi: ufs: ufs-qcom: Implement vops tx_eqtr_notify()
Posted by Manivannan Sadhasivam 1 month, 1 week ago
On Wed, Mar 04, 2026 at 05:53:10AM -0800, Can Guo wrote:
> On some platforms, HW does not support triggering TX EQTR from the most
> reliable High-Speed (HS) Gear (HS Gear1), but only allows to trigger TX
> EQTR for the target HS Gear from the same HS Gear. To work around the HW
> limitation, implement vops tx_eqtr_notify() to change Power Mode to the
> target TX EQTR HS Gear prior to TX EQTR procedure and change Power Mode
> back to HS Gear1 (the most reliable gear) post TX EQTR procedure.
> 
> Signed-off-by: Can Guo <can.guo@oss.qualcomm.com>
> ---
>  drivers/ufs/host/ufs-qcom.c | 63 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 63 insertions(+)
> 
> diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
> index 3a9279066192..1e074058f23d 100644
> --- a/drivers/ufs/host/ufs-qcom.c
> +++ b/drivers/ufs/host/ufs-qcom.c
> @@ -2512,6 +2512,68 @@ static u32 ufs_qcom_freq_to_gear_speed(struct ufs_hba *hba, unsigned long freq)
>  	return min_t(u32, gear, hba->max_pwr_info.info.gear_rx);
>  }
>  
> +static int ufs_qcom_change_power_mode(struct ufs_hba *hba,
> +				      struct ufs_pa_layer_attr *pwr_mode,
> +				      enum ufshcd_pmc_policy pmc_policy)
> +{
> +	int ret;
> +
> +	ret = ufs_qcom_pwr_change_notify(hba, PRE_CHANGE, pwr_mode);
> +	if (ret) {
> +		dev_err(hba->dev, "Power change notify (PRE_CHANGE) failed: %d\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	ret = ufshcd_change_power_mode(hba, pwr_mode, pmc_policy);
> +	if (ret)
> +		return ret;
> +
> +	ufs_qcom_pwr_change_notify(hba, POST_CHANGE, pwr_mode);
> +
> +	return ret;

return 0;

> +}
> +
> +static int ufs_qcom_tx_eqtr_notify(struct ufs_hba *hba,
> +				   enum ufs_notify_change_status status,
> +				   struct ufs_pa_layer_attr *pwr_mode)
> +{
> +	struct ufs_qcom_host *host = ufshcd_get_variant(hba);
> +	struct ufs_pa_layer_attr pwr_mode_hs_g1 = {
> +		.gear_rx = UFS_HS_G1,
> +		.gear_tx = UFS_HS_G1,
> +		.lane_rx = pwr_mode->lane_rx,
> +		.lane_tx = pwr_mode->lane_tx,
> +		.pwr_rx = FAST_MODE,
> +		.pwr_tx = FAST_MODE,
> +		.hs_rate = pwr_mode->hs_rate,
> +	};
> +	u32 gear = pwr_mode->gear_tx;
> +	u32 rate = pwr_mode->hs_rate;
> +	int ret;
> +
> +	if (host->hw_ver.major != 0x7 || host->hw_ver.minor > 0x1)
> +		return 0;
> +
> +	if (status == PRE_CHANGE) {
> +		/* PMC to target HS Gear. */
> +		ret = ufs_qcom_change_power_mode(hba, pwr_mode,
> +						 UFSHCD_PMC_POLICY_DONT_FORCE);
> +		if (ret)
> +			dev_err(hba->dev, "%s: Failed to change power mode to target HS-G%u, Rate-%s: %d\n",

Same comment as other patch. Goes over 100 column.

> +				__func__, gear, UFS_HS_RATE_STRING(rate), ret);

Can we please not specify the function name in error log?

- Mani

-- 
மணிவண்ணன் சதாசிவம்
Re: [PATCH v2 08/11] scsi: ufs: ufs-qcom: Implement vops tx_eqtr_notify()
Posted by Can Guo 1 month, 1 week ago

On 3/4/2026 11:20 PM, Manivannan Sadhasivam wrote:
> On Wed, Mar 04, 2026 at 05:53:10AM -0800, Can Guo wrote:
>> On some platforms, HW does not support triggering TX EQTR from the most
>> reliable High-Speed (HS) Gear (HS Gear1), but only allows to trigger TX
>> EQTR for the target HS Gear from the same HS Gear. To work around the HW
>> limitation, implement vops tx_eqtr_notify() to change Power Mode to the
>> target TX EQTR HS Gear prior to TX EQTR procedure and change Power Mode
>> back to HS Gear1 (the most reliable gear) post TX EQTR procedure.
>>
>> Signed-off-by: Can Guo <can.guo@oss.qualcomm.com>
>> ---
>>   drivers/ufs/host/ufs-qcom.c | 63 +++++++++++++++++++++++++++++++++++++
>>   1 file changed, 63 insertions(+)
>>
>> diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
>> index 3a9279066192..1e074058f23d 100644
>> --- a/drivers/ufs/host/ufs-qcom.c
>> +++ b/drivers/ufs/host/ufs-qcom.c
>> @@ -2512,6 +2512,68 @@ static u32 ufs_qcom_freq_to_gear_speed(struct ufs_hba *hba, unsigned long freq)
>>   	return min_t(u32, gear, hba->max_pwr_info.info.gear_rx);
>>   }
>>   
>> +static int ufs_qcom_change_power_mode(struct ufs_hba *hba,
>> +				      struct ufs_pa_layer_attr *pwr_mode,
>> +				      enum ufshcd_pmc_policy pmc_policy)
>> +{
>> +	int ret;
>> +
>> +	ret = ufs_qcom_pwr_change_notify(hba, PRE_CHANGE, pwr_mode);
>> +	if (ret) {
>> +		dev_err(hba->dev, "Power change notify (PRE_CHANGE) failed: %d\n",
>> +			ret);
>> +		return ret;
>> +	}
>> +
>> +	ret = ufshcd_change_power_mode(hba, pwr_mode, pmc_policy);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ufs_qcom_pwr_change_notify(hba, POST_CHANGE, pwr_mode);
>> +
>> +	return ret;
> return 0;
This function should not exist, I will remove this function in next 
version.
>
>> +}
>> +
>> +static int ufs_qcom_tx_eqtr_notify(struct ufs_hba *hba,
>> +				   enum ufs_notify_change_status status,
>> +				   struct ufs_pa_layer_attr *pwr_mode)
>> +{
>> +	struct ufs_qcom_host *host = ufshcd_get_variant(hba);
>> +	struct ufs_pa_layer_attr pwr_mode_hs_g1 = {
>> +		.gear_rx = UFS_HS_G1,
>> +		.gear_tx = UFS_HS_G1,
>> +		.lane_rx = pwr_mode->lane_rx,
>> +		.lane_tx = pwr_mode->lane_tx,
>> +		.pwr_rx = FAST_MODE,
>> +		.pwr_tx = FAST_MODE,
>> +		.hs_rate = pwr_mode->hs_rate,
>> +	};
>> +	u32 gear = pwr_mode->gear_tx;
>> +	u32 rate = pwr_mode->hs_rate;
>> +	int ret;
>> +
>> +	if (host->hw_ver.major != 0x7 || host->hw_ver.minor > 0x1)
>> +		return 0;
>> +
>> +	if (status == PRE_CHANGE) {
>> +		/* PMC to target HS Gear. */
>> +		ret = ufs_qcom_change_power_mode(hba, pwr_mode,
>> +						 UFSHCD_PMC_POLICY_DONT_FORCE);
>> +		if (ret)
>> +			dev_err(hba->dev, "%s: Failed to change power mode to target HS-G%u, Rate-%s: %d\n",
> Same comment as other patch. Goes over 100 column.
Will fix in next version.
>
>> +				__func__, gear, UFS_HS_RATE_STRING(rate), ret);
> Can we please not specify the function name in error log?
There are too many Power Mode changes in Qualcomm's TX EQTR procedure, I 
want to print
some hints to help pinpoint issues easier...

Thanks,
Can Guo.
>
> - Mani
>