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

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
...@@ -128,6 +128,81 @@ static int ufs_intel_link_startup_notify(struct ufs_hba *hba, ...@@ -128,6 +128,81 @@ static int ufs_intel_link_startup_notify(struct ufs_hba *hba,
return err; 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_ACTIVELTR 0x804
#define INTEL_IDLELTR 0x808 #define INTEL_IDLELTR 0x808
...@@ -351,6 +426,7 @@ static int ufs_intel_lkf_init(struct ufs_hba *hba) ...@@ -351,6 +426,7 @@ static int ufs_intel_lkf_init(struct ufs_hba *hba)
struct ufs_host *ufs_host; struct ufs_host *ufs_host;
int err; int err;
hba->nop_out_timeout = 200;
hba->quirks |= UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8; hba->quirks |= UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8;
hba->caps |= UFSHCD_CAP_CRYPTO; hba->caps |= UFSHCD_CAP_CRYPTO;
err = ufs_intel_common_init(hba); err = ufs_intel_common_init(hba);
...@@ -381,6 +457,8 @@ static struct ufs_hba_variant_ops ufs_intel_lkf_hba_vops = { ...@@ -381,6 +457,8 @@ static struct ufs_hba_variant_ops ufs_intel_lkf_hba_vops = {
.exit = ufs_intel_common_exit, .exit = ufs_intel_common_exit,
.hce_enable_notify = ufs_intel_hce_enable_notify, .hce_enable_notify = ufs_intel_hce_enable_notify,
.link_startup_notify = ufs_intel_link_startup_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, .resume = ufs_intel_resume,
.device_reset = ufs_intel_device_reset, .device_reset = ufs_intel_device_reset,
}; };
......
...@@ -4776,7 +4776,7 @@ static int ufshcd_verify_dev_init(struct ufs_hba *hba) ...@@ -4776,7 +4776,7 @@ static int ufshcd_verify_dev_init(struct ufs_hba *hba)
mutex_lock(&hba->dev_cmd.lock); mutex_lock(&hba->dev_cmd.lock);
for (retries = NOP_OUT_RETRIES; retries > 0; retries--) { for (retries = NOP_OUT_RETRIES; retries > 0; retries--) {
err = ufshcd_exec_dev_cmd(hba, DEV_CMD_TYPE_NOP, err = ufshcd_exec_dev_cmd(hba, DEV_CMD_TYPE_NOP,
NOP_OUT_TIMEOUT); hba->nop_out_timeout);
if (!err || err == -ETIMEDOUT) if (!err || err == -ETIMEDOUT)
break; break;
...@@ -9483,6 +9483,7 @@ int ufshcd_alloc_host(struct device *dev, struct ufs_hba **hba_handle) ...@@ -9483,6 +9483,7 @@ int ufshcd_alloc_host(struct device *dev, struct ufs_hba **hba_handle)
hba->host = host; hba->host = host;
hba->dev = dev; hba->dev = dev;
hba->dev_ref_clk_freq = REF_CLK_FREQ_INVAL; hba->dev_ref_clk_freq = REF_CLK_FREQ_INVAL;
hba->nop_out_timeout = NOP_OUT_TIMEOUT;
INIT_LIST_HEAD(&hba->clk_list_head); INIT_LIST_HEAD(&hba->clk_list_head);
spin_lock_init(&hba->outstanding_lock); spin_lock_init(&hba->outstanding_lock);
......
...@@ -858,6 +858,7 @@ struct ufs_hba { ...@@ -858,6 +858,7 @@ struct ufs_hba {
/* Device management request data */ /* Device management request data */
struct ufs_dev_cmd dev_cmd; struct ufs_dev_cmd dev_cmd;
ktime_t last_dme_cmd_tstamp; ktime_t last_dme_cmd_tstamp;
int nop_out_timeout;
/* Keeps information of the UFS device connected to this host */ /* Keeps information of the UFS device connected to this host */
struct ufs_dev_info dev_info; struct ufs_dev_info dev_info;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment