Commit 1cbc9ad3 authored by Adrian Hunter's avatar Adrian Hunter Committed by Martin K. Petersen
Browse files

scsi: ufs: ufs-pci: Fix Intel LKF link stability

Intel LKF can experience link errors. Make fixes to increase link
stability, especially when switching to high speed modes.

Link: https://lore.kernel.org/r/20210831145317.26306-1-adrian.hunter@intel.com


Fixes: b2c57925 ("scsi: ufs: ufs-pci: Add support for Intel LKF")
Cc: stable@vger.kernel.org
Signed-off-by: default avatarAdrian Hunter <adrian.hunter@intel.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 04c260bd
Loading
Loading
Loading
Loading
+78 −0
Original line number Diff line number Diff line
@@ -128,6 +128,81 @@ static int ufs_intel_link_startup_notify(struct ufs_hba *hba,
	return err;
}

static int ufs_intel_set_lanes(struct ufs_hba *hba, u32 lanes)
{
	struct ufs_pa_layer_attr pwr_info = hba->pwr_info;
	int ret;

	pwr_info.lane_rx = lanes;
	pwr_info.lane_tx = lanes;
	ret = ufshcd_config_pwr_mode(hba, &pwr_info);
	if (ret)
		dev_err(hba->dev, "%s: Setting %u lanes, err = %d\n",
			__func__, lanes, ret);
	return ret;
}

static int ufs_intel_lkf_pwr_change_notify(struct ufs_hba *hba,
				enum ufs_notify_change_status status,
				struct ufs_pa_layer_attr *dev_max_params,
				struct ufs_pa_layer_attr *dev_req_params)
{
	int err = 0;

	switch (status) {
	case PRE_CHANGE:
		if (ufshcd_is_hs_mode(dev_max_params) &&
		    (hba->pwr_info.lane_rx != 2 || hba->pwr_info.lane_tx != 2))
			ufs_intel_set_lanes(hba, 2);
		memcpy(dev_req_params, dev_max_params, sizeof(*dev_req_params));
		break;
	case POST_CHANGE:
		if (ufshcd_is_hs_mode(dev_req_params)) {
			u32 peer_granularity;

			usleep_range(1000, 1250);
			err = ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_GRANULARITY),
						  &peer_granularity);
		}
		break;
	default:
		break;
	}

	return err;
}

static int ufs_intel_lkf_apply_dev_quirks(struct ufs_hba *hba)
{
	u32 granularity, peer_granularity;
	u32 pa_tactivate, peer_pa_tactivate;
	int ret;

	ret = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_GRANULARITY), &granularity);
	if (ret)
		goto out;

	ret = ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_GRANULARITY), &peer_granularity);
	if (ret)
		goto out;

	ret = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_TACTIVATE), &pa_tactivate);
	if (ret)
		goto out;

	ret = ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_TACTIVATE), &peer_pa_tactivate);
	if (ret)
		goto out;

	if (granularity == peer_granularity) {
		u32 new_peer_pa_tactivate = pa_tactivate + 2;

		ret = ufshcd_dme_peer_set(hba, UIC_ARG_MIB(PA_TACTIVATE), new_peer_pa_tactivate);
	}
out:
	return ret;
}

#define INTEL_ACTIVELTR		0x804
#define INTEL_IDLELTR		0x808

@@ -351,6 +426,7 @@ static int ufs_intel_lkf_init(struct ufs_hba *hba)
	struct ufs_host *ufs_host;
	int err;

	hba->nop_out_timeout = 200;
	hba->quirks |= UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8;
	hba->caps |= UFSHCD_CAP_CRYPTO;
	err = ufs_intel_common_init(hba);
@@ -381,6 +457,8 @@ static struct ufs_hba_variant_ops ufs_intel_lkf_hba_vops = {
	.exit			= ufs_intel_common_exit,
	.hce_enable_notify	= ufs_intel_hce_enable_notify,
	.link_startup_notify	= ufs_intel_link_startup_notify,
	.pwr_change_notify	= ufs_intel_lkf_pwr_change_notify,
	.apply_dev_quirks	= ufs_intel_lkf_apply_dev_quirks,
	.resume			= ufs_intel_resume,
	.device_reset		= ufs_intel_device_reset,
};
+2 −1
Original line number Diff line number Diff line
@@ -4776,7 +4776,7 @@ static int ufshcd_verify_dev_init(struct ufs_hba *hba)
	mutex_lock(&hba->dev_cmd.lock);
	for (retries = NOP_OUT_RETRIES; retries > 0; retries--) {
		err = ufshcd_exec_dev_cmd(hba, DEV_CMD_TYPE_NOP,
					       NOP_OUT_TIMEOUT);
					  hba->nop_out_timeout);

		if (!err || err == -ETIMEDOUT)
			break;
@@ -9483,6 +9483,7 @@ int ufshcd_alloc_host(struct device *dev, struct ufs_hba **hba_handle)
	hba->host = host;
	hba->dev = dev;
	hba->dev_ref_clk_freq = REF_CLK_FREQ_INVAL;
	hba->nop_out_timeout = NOP_OUT_TIMEOUT;
	INIT_LIST_HEAD(&hba->clk_list_head);
	spin_lock_init(&hba->outstanding_lock);

+1 −0
Original line number Diff line number Diff line
@@ -858,6 +858,7 @@ struct ufs_hba {
	/* Device management request data */
	struct ufs_dev_cmd dev_cmd;
	ktime_t last_dme_cmd_tstamp;
	int nop_out_timeout;

	/* Keeps information of the UFS device connected to this host */
	struct ufs_dev_info dev_info;