Merge branch 6.18/scsi-fixes into 6.19/scsi-staging
Pull in fixes branch to resolve UFS merge conflict. Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
@@ -24,6 +24,10 @@ properties:
|
|||||||
- enum:
|
- enum:
|
||||||
- qcom,qcs8300-qmp-ufs-phy
|
- qcom,qcs8300-qmp-ufs-phy
|
||||||
- const: qcom,sa8775p-qmp-ufs-phy
|
- const: qcom,sa8775p-qmp-ufs-phy
|
||||||
|
- items:
|
||||||
|
- enum:
|
||||||
|
- qcom,kaanapali-qmp-ufs-phy
|
||||||
|
- const: qcom,sm8750-qmp-ufs-phy
|
||||||
- enum:
|
- enum:
|
||||||
- qcom,msm8996-qmp-ufs-phy
|
- qcom,msm8996-qmp-ufs-phy
|
||||||
- qcom,msm8998-qmp-ufs-phy
|
- qcom,msm8998-qmp-ufs-phy
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ select:
|
|||||||
compatible:
|
compatible:
|
||||||
contains:
|
contains:
|
||||||
enum:
|
enum:
|
||||||
|
- qcom,kaanapali-ufshc
|
||||||
- qcom,sm8650-ufshc
|
- qcom,sm8650-ufshc
|
||||||
- qcom,sm8750-ufshc
|
- qcom,sm8750-ufshc
|
||||||
required:
|
required:
|
||||||
@@ -24,6 +25,7 @@ properties:
|
|||||||
compatible:
|
compatible:
|
||||||
items:
|
items:
|
||||||
- enum:
|
- enum:
|
||||||
|
- qcom,kaanapali-ufshc
|
||||||
- qcom,sm8650-ufshc
|
- qcom,sm8650-ufshc
|
||||||
- qcom,sm8750-ufshc
|
- qcom,sm8750-ufshc
|
||||||
- const: qcom,ufshc
|
- const: qcom,ufshc
|
||||||
|
|||||||
@@ -626,8 +626,9 @@ int scsi_host_busy(struct Scsi_Host *shost)
|
|||||||
{
|
{
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
|
|
||||||
blk_mq_tagset_busy_iter(&shost->tag_set,
|
if (shost->tag_set.ops)
|
||||||
scsi_host_check_in_flight, &cnt);
|
blk_mq_tagset_busy_iter(&shost->tag_set,
|
||||||
|
scsi_host_check_in_flight, &cnt);
|
||||||
return cnt;
|
return cnt;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(scsi_host_busy);
|
EXPORT_SYMBOL(scsi_host_busy);
|
||||||
|
|||||||
@@ -503,7 +503,7 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
|
|||||||
host_bcode = FC_ERROR;
|
host_bcode = FC_ERROR;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
if (offset + len > fsp->data_len) {
|
if (size_add(offset, len) > fsp->data_len) {
|
||||||
/* this should never happen */
|
/* this should never happen */
|
||||||
if ((fr_flags(fp) & FCPHF_CRC_UNCHECKED) &&
|
if ((fr_flags(fp) & FCPHF_CRC_UNCHECKED) &&
|
||||||
fc_frame_crc_check(fp))
|
fc_frame_crc_check(fp))
|
||||||
|
|||||||
@@ -4104,7 +4104,7 @@ void qla4xxx_srb_compl(struct kref *ref)
|
|||||||
* The mid-level driver tries to ensure that queuecommand never gets
|
* The mid-level driver tries to ensure that queuecommand never gets
|
||||||
* invoked concurrently with itself or the interrupt handler (although
|
* invoked concurrently with itself or the interrupt handler (although
|
||||||
* the interrupt handler may call this routine as part of request-
|
* the interrupt handler may call this routine as part of request-
|
||||||
* completion handling). Unfortunely, it sometimes calls the scheduler
|
* completion handling). Unfortunately, it sometimes calls the scheduler
|
||||||
* in interrupt context which is a big NO! NO!.
|
* in interrupt context which is a big NO! NO!.
|
||||||
**/
|
**/
|
||||||
static int qla4xxx_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
|
static int qla4xxx_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
|
||||||
@@ -4647,7 +4647,7 @@ static int qla4xxx_cmd_wait(struct scsi_qla_host *ha)
|
|||||||
cmd = scsi_host_find_tag(ha->host, index);
|
cmd = scsi_host_find_tag(ha->host, index);
|
||||||
/*
|
/*
|
||||||
* We cannot just check if the index is valid,
|
* We cannot just check if the index is valid,
|
||||||
* becase if we are run from the scsi eh, then
|
* because if we are run from the scsi eh, then
|
||||||
* the scsi/block layer is going to prevent
|
* the scsi/block layer is going to prevent
|
||||||
* the tag from being released.
|
* the tag from being released.
|
||||||
*/
|
*/
|
||||||
@@ -4952,7 +4952,7 @@ recover_ha_init_adapter:
|
|||||||
/* Upon successful firmware/chip reset, re-initialize the adapter */
|
/* Upon successful firmware/chip reset, re-initialize the adapter */
|
||||||
if (status == QLA_SUCCESS) {
|
if (status == QLA_SUCCESS) {
|
||||||
/* For ISP-4xxx, force function 1 to always initialize
|
/* For ISP-4xxx, force function 1 to always initialize
|
||||||
* before function 3 to prevent both funcions from
|
* before function 3 to prevent both functions from
|
||||||
* stepping on top of the other */
|
* stepping on top of the other */
|
||||||
if (is_qla40XX(ha) && (ha->mac_index == 3))
|
if (is_qla40XX(ha) && (ha->mac_index == 3))
|
||||||
ssleep(6);
|
ssleep(6);
|
||||||
@@ -6914,7 +6914,7 @@ static int qla4xxx_sess_conn_setup(struct scsi_qla_host *ha,
|
|||||||
struct ddb_entry *ddb_entry = NULL;
|
struct ddb_entry *ddb_entry = NULL;
|
||||||
|
|
||||||
/* Create session object, with INVALID_ENTRY,
|
/* Create session object, with INVALID_ENTRY,
|
||||||
* the targer_id would get set when we issue the login
|
* the target_id would get set when we issue the login
|
||||||
*/
|
*/
|
||||||
cls_sess = iscsi_session_setup(&qla4xxx_iscsi_transport, ha->host,
|
cls_sess = iscsi_session_setup(&qla4xxx_iscsi_transport, ha->host,
|
||||||
cmds_max, sizeof(struct ddb_entry),
|
cmds_max, sizeof(struct ddb_entry),
|
||||||
|
|||||||
@@ -554,9 +554,9 @@ enum scsi_disposition scsi_check_sense(struct scsi_cmnd *scmd)
|
|||||||
* happened, even if someone else gets the sense data.
|
* happened, even if someone else gets the sense data.
|
||||||
*/
|
*/
|
||||||
if (sshdr.asc == 0x28)
|
if (sshdr.asc == 0x28)
|
||||||
scmd->device->ua_new_media_ctr++;
|
atomic_inc(&sdev->ua_new_media_ctr);
|
||||||
else if (sshdr.asc == 0x29)
|
else if (sshdr.asc == 0x29)
|
||||||
scmd->device->ua_por_ctr++;
|
atomic_inc(&sdev->ua_por_ctr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scsi_sense_is_deferred(&sshdr))
|
if (scsi_sense_is_deferred(&sshdr))
|
||||||
|
|||||||
@@ -1406,14 +1406,19 @@ static struct vmbus_channel *get_og_chn(struct storvsc_device *stor_device,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Our channel array is sparsley populated and we
|
* Our channel array could be sparsley populated and we
|
||||||
* initiated I/O on a processor/hw-q that does not
|
* initiated I/O on a processor/hw-q that does not
|
||||||
* currently have a designated channel. Fix this.
|
* currently have a designated channel. Fix this.
|
||||||
* The strategy is simple:
|
* The strategy is simple:
|
||||||
* I. Ensure NUMA locality
|
* I. Prefer the channel associated with the current CPU
|
||||||
* II. Distribute evenly (best effort)
|
* II. Ensure NUMA locality
|
||||||
|
* III. Distribute evenly (best effort)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* Prefer the channel on the I/O issuing processor/hw-q */
|
||||||
|
if (cpumask_test_cpu(q_num, &stor_device->alloced_cpus))
|
||||||
|
return stor_device->stor_chns[q_num];
|
||||||
|
|
||||||
node_mask = cpumask_of_node(cpu_to_node(q_num));
|
node_mask = cpumask_of_node(cpu_to_node(q_num));
|
||||||
|
|
||||||
num_channels = 0;
|
num_channels = 0;
|
||||||
@@ -1469,59 +1474,48 @@ static int storvsc_do_io(struct hv_device *device,
|
|||||||
/* See storvsc_change_target_cpu(). */
|
/* See storvsc_change_target_cpu(). */
|
||||||
outgoing_channel = READ_ONCE(stor_device->stor_chns[q_num]);
|
outgoing_channel = READ_ONCE(stor_device->stor_chns[q_num]);
|
||||||
if (outgoing_channel != NULL) {
|
if (outgoing_channel != NULL) {
|
||||||
if (outgoing_channel->target_cpu == q_num) {
|
if (hv_get_avail_to_write_percent(&outgoing_channel->outbound)
|
||||||
/*
|
> ring_avail_percent_lowater)
|
||||||
* Ideally, we want to pick a different channel if
|
goto found_channel;
|
||||||
* available on the same NUMA node.
|
|
||||||
*/
|
|
||||||
node_mask = cpumask_of_node(cpu_to_node(q_num));
|
|
||||||
for_each_cpu_wrap(tgt_cpu,
|
|
||||||
&stor_device->alloced_cpus, q_num + 1) {
|
|
||||||
if (!cpumask_test_cpu(tgt_cpu, node_mask))
|
|
||||||
continue;
|
|
||||||
if (tgt_cpu == q_num)
|
|
||||||
continue;
|
|
||||||
channel = READ_ONCE(
|
|
||||||
stor_device->stor_chns[tgt_cpu]);
|
|
||||||
if (channel == NULL)
|
|
||||||
continue;
|
|
||||||
if (hv_get_avail_to_write_percent(
|
|
||||||
&channel->outbound)
|
|
||||||
> ring_avail_percent_lowater) {
|
|
||||||
outgoing_channel = channel;
|
|
||||||
goto found_channel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* All the other channels on the same NUMA node are
|
* Channel is busy, try to find a channel on the same NUMA node
|
||||||
* busy. Try to use the channel on the current CPU
|
*/
|
||||||
*/
|
node_mask = cpumask_of_node(cpu_to_node(q_num));
|
||||||
if (hv_get_avail_to_write_percent(
|
for_each_cpu_wrap(tgt_cpu, &stor_device->alloced_cpus,
|
||||||
&outgoing_channel->outbound)
|
q_num + 1) {
|
||||||
> ring_avail_percent_lowater)
|
if (!cpumask_test_cpu(tgt_cpu, node_mask))
|
||||||
|
continue;
|
||||||
|
channel = READ_ONCE(stor_device->stor_chns[tgt_cpu]);
|
||||||
|
if (!channel)
|
||||||
|
continue;
|
||||||
|
if (hv_get_avail_to_write_percent(&channel->outbound)
|
||||||
|
> ring_avail_percent_lowater) {
|
||||||
|
outgoing_channel = channel;
|
||||||
goto found_channel;
|
goto found_channel;
|
||||||
|
|
||||||
/*
|
|
||||||
* If we reach here, all the channels on the current
|
|
||||||
* NUMA node are busy. Try to find a channel in
|
|
||||||
* other NUMA nodes
|
|
||||||
*/
|
|
||||||
for_each_cpu(tgt_cpu, &stor_device->alloced_cpus) {
|
|
||||||
if (cpumask_test_cpu(tgt_cpu, node_mask))
|
|
||||||
continue;
|
|
||||||
channel = READ_ONCE(
|
|
||||||
stor_device->stor_chns[tgt_cpu]);
|
|
||||||
if (channel == NULL)
|
|
||||||
continue;
|
|
||||||
if (hv_get_avail_to_write_percent(
|
|
||||||
&channel->outbound)
|
|
||||||
> ring_avail_percent_lowater) {
|
|
||||||
outgoing_channel = channel;
|
|
||||||
goto found_channel;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we reach here, all the channels on the current
|
||||||
|
* NUMA node are busy. Try to find a channel in
|
||||||
|
* all NUMA nodes
|
||||||
|
*/
|
||||||
|
for_each_cpu_wrap(tgt_cpu, &stor_device->alloced_cpus,
|
||||||
|
q_num + 1) {
|
||||||
|
channel = READ_ONCE(stor_device->stor_chns[tgt_cpu]);
|
||||||
|
if (!channel)
|
||||||
|
continue;
|
||||||
|
if (hv_get_avail_to_write_percent(&channel->outbound)
|
||||||
|
> ring_avail_percent_lowater) {
|
||||||
|
outgoing_channel = channel;
|
||||||
|
goto found_channel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* If we reach here, all the channels are busy. Use the
|
||||||
|
* original channel found.
|
||||||
|
*/
|
||||||
} else {
|
} else {
|
||||||
spin_lock_irqsave(&stor_device->lock, flags);
|
spin_lock_irqsave(&stor_device->lock, flags);
|
||||||
outgoing_channel = stor_device->stor_chns[q_num];
|
outgoing_channel = stor_device->stor_chns[q_num];
|
||||||
|
|||||||
@@ -1950,7 +1950,7 @@ static umode_t ufs_sysfs_hid_is_visible(struct kobject *kobj,
|
|||||||
return hba->dev_info.hid_sup ? attr->mode : 0;
|
return hba->dev_info.hid_sup ? attr->mode : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct attribute_group ufs_sysfs_hid_group = {
|
static const struct attribute_group ufs_sysfs_hid_group = {
|
||||||
.name = "hid",
|
.name = "hid",
|
||||||
.attrs = ufs_sysfs_hid,
|
.attrs = ufs_sysfs_hid,
|
||||||
.is_visible = ufs_sysfs_hid_is_visible,
|
.is_visible = ufs_sysfs_hid_is_visible,
|
||||||
|
|||||||
@@ -14,6 +14,5 @@ void ufs_sysfs_remove_nodes(struct device *dev);
|
|||||||
|
|
||||||
extern const struct attribute_group ufs_sysfs_unit_descriptor_group;
|
extern const struct attribute_group ufs_sysfs_unit_descriptor_group;
|
||||||
extern const struct attribute_group ufs_sysfs_lun_attributes_group;
|
extern const struct attribute_group ufs_sysfs_lun_attributes_group;
|
||||||
extern const struct attribute_group ufs_sysfs_hid_group;
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -4269,8 +4269,8 @@ int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
|
|||||||
get, UIC_GET_ATTR_ID(attr_sel),
|
get, UIC_GET_ATTR_ID(attr_sel),
|
||||||
UFS_UIC_COMMAND_RETRIES - retries);
|
UFS_UIC_COMMAND_RETRIES - retries);
|
||||||
|
|
||||||
if (mib_val && !ret)
|
if (mib_val)
|
||||||
*mib_val = uic_cmd.argument3;
|
*mib_val = ret == 0 ? uic_cmd.argument3 : 0;
|
||||||
|
|
||||||
if (peer && (hba->quirks & UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE)
|
if (peer && (hba->quirks & UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE)
|
||||||
&& pwr_mode_change)
|
&& pwr_mode_change)
|
||||||
@@ -4986,7 +4986,7 @@ EXPORT_SYMBOL_GPL(ufshcd_hba_enable);
|
|||||||
|
|
||||||
static int ufshcd_disable_tx_lcc(struct ufs_hba *hba, bool peer)
|
static int ufshcd_disable_tx_lcc(struct ufs_hba *hba, bool peer)
|
||||||
{
|
{
|
||||||
int tx_lanes = 0, i, err = 0;
|
int tx_lanes, i, err = 0;
|
||||||
|
|
||||||
if (!peer)
|
if (!peer)
|
||||||
ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES),
|
ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES),
|
||||||
@@ -5053,7 +5053,8 @@ static int ufshcd_link_startup(struct ufs_hba *hba)
|
|||||||
* If UFS device isn't active then we will have to issue link startup
|
* If UFS device isn't active then we will have to issue link startup
|
||||||
* 2 times to make sure the device state move to active.
|
* 2 times to make sure the device state move to active.
|
||||||
*/
|
*/
|
||||||
if (!ufshcd_is_ufs_dev_active(hba))
|
if (!(hba->quirks & UFSHCD_QUIRK_PERFORM_LINK_STARTUP_ONCE) &&
|
||||||
|
!ufshcd_is_ufs_dev_active(hba))
|
||||||
link_startup_again = true;
|
link_startup_again = true;
|
||||||
|
|
||||||
link_startup:
|
link_startup:
|
||||||
@@ -5118,12 +5119,8 @@ link_startup:
|
|||||||
ufshcd_readl(hba, REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER);
|
ufshcd_readl(hba, REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER);
|
||||||
ret = ufshcd_make_hba_operational(hba);
|
ret = ufshcd_make_hba_operational(hba);
|
||||||
out:
|
out:
|
||||||
if (ret) {
|
if (ret)
|
||||||
dev_err(hba->dev, "link startup failed %d\n", ret);
|
dev_err(hba->dev, "link startup failed %d\n", ret);
|
||||||
ufshcd_print_host_state(hba);
|
|
||||||
ufshcd_print_pwr_info(hba);
|
|
||||||
ufshcd_print_evt_hist(hba);
|
|
||||||
}
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6679,6 +6676,20 @@ static void ufshcd_err_handler(struct work_struct *work)
|
|||||||
hba->saved_uic_err, hba->force_reset,
|
hba->saved_uic_err, hba->force_reset,
|
||||||
ufshcd_is_link_broken(hba) ? "; link is broken" : "");
|
ufshcd_is_link_broken(hba) ? "; link is broken" : "");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use ufshcd_rpm_get_noresume() here to safely perform link recovery
|
||||||
|
* even if an error occurs during runtime suspend or runtime resume.
|
||||||
|
* This avoids potential deadlocks that could happen if we tried to
|
||||||
|
* resume the device while a PM operation is already in progress.
|
||||||
|
*/
|
||||||
|
ufshcd_rpm_get_noresume(hba);
|
||||||
|
if (hba->pm_op_in_progress) {
|
||||||
|
ufshcd_link_recovery(hba);
|
||||||
|
ufshcd_rpm_put(hba);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ufshcd_rpm_put(hba);
|
||||||
|
|
||||||
down(&hba->host_sem);
|
down(&hba->host_sem);
|
||||||
spin_lock_irqsave(hba->host->host_lock, flags);
|
spin_lock_irqsave(hba->host->host_lock, flags);
|
||||||
if (ufshcd_err_handling_should_stop(hba)) {
|
if (ufshcd_err_handling_should_stop(hba)) {
|
||||||
@@ -6690,14 +6701,6 @@ static void ufshcd_err_handler(struct work_struct *work)
|
|||||||
}
|
}
|
||||||
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
||||||
|
|
||||||
ufshcd_rpm_get_noresume(hba);
|
|
||||||
if (hba->pm_op_in_progress) {
|
|
||||||
ufshcd_link_recovery(hba);
|
|
||||||
ufshcd_rpm_put(hba);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ufshcd_rpm_put(hba);
|
|
||||||
|
|
||||||
ufshcd_err_handling_prepare(hba);
|
ufshcd_err_handling_prepare(hba);
|
||||||
|
|
||||||
spin_lock_irqsave(hba->host->host_lock, flags);
|
spin_lock_irqsave(hba->host->host_lock, flags);
|
||||||
@@ -8538,8 +8541,6 @@ static int ufs_get_device_desc(struct ufs_hba *hba)
|
|||||||
DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP) &
|
DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP) &
|
||||||
UFS_DEV_HID_SUPPORT;
|
UFS_DEV_HID_SUPPORT;
|
||||||
|
|
||||||
sysfs_update_group(&hba->dev->kobj, &ufs_sysfs_hid_group);
|
|
||||||
|
|
||||||
model_index = desc_buf[DEVICE_DESC_PARAM_PRDCT_NAME];
|
model_index = desc_buf[DEVICE_DESC_PARAM_PRDCT_NAME];
|
||||||
|
|
||||||
err = ufshcd_read_string_desc(hba, model_index,
|
err = ufshcd_read_string_desc(hba, model_index,
|
||||||
@@ -10700,7 +10701,7 @@ remove_scsi_host:
|
|||||||
* @mmio_base: base register address
|
* @mmio_base: base register address
|
||||||
* @irq: Interrupt line of device
|
* @irq: Interrupt line of device
|
||||||
*
|
*
|
||||||
* Return: 0 on success, non-zero value on failure.
|
* Return: 0 on success; < 0 on failure.
|
||||||
*/
|
*/
|
||||||
int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
|
int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
|
||||||
{
|
{
|
||||||
@@ -10939,8 +10940,8 @@ initialized:
|
|||||||
if (err)
|
if (err)
|
||||||
goto out_disable;
|
goto out_disable;
|
||||||
|
|
||||||
async_schedule(ufshcd_async_scan, hba);
|
|
||||||
ufs_sysfs_add_nodes(hba->dev);
|
ufs_sysfs_add_nodes(hba->dev);
|
||||||
|
async_schedule(ufshcd_async_scan, hba);
|
||||||
|
|
||||||
device_enable_async_suspend(dev);
|
device_enable_async_suspend(dev);
|
||||||
ufshcd_pm_qos_init(hba);
|
ufshcd_pm_qos_init(hba);
|
||||||
@@ -10950,7 +10951,7 @@ out_disable:
|
|||||||
hba->is_irq_enabled = false;
|
hba->is_irq_enabled = false;
|
||||||
ufshcd_hba_exit(hba);
|
ufshcd_hba_exit(hba);
|
||||||
out_error:
|
out_error:
|
||||||
return err;
|
return err > 0 ? -EIO : err;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(ufshcd_init);
|
EXPORT_SYMBOL_GPL(ufshcd_init);
|
||||||
|
|
||||||
|
|||||||
@@ -740,8 +740,21 @@ static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op,
|
|||||||
|
|
||||||
|
|
||||||
/* reset the connected UFS device during power down */
|
/* reset the connected UFS device during power down */
|
||||||
if (ufs_qcom_is_link_off(hba) && host->device_reset)
|
if (ufs_qcom_is_link_off(hba) && host->device_reset) {
|
||||||
ufs_qcom_device_reset_ctrl(hba, true);
|
ufs_qcom_device_reset_ctrl(hba, true);
|
||||||
|
/*
|
||||||
|
* After sending the SSU command, asserting the rst_n
|
||||||
|
* line causes the device firmware to wake up and
|
||||||
|
* execute its reset routine.
|
||||||
|
*
|
||||||
|
* During this process, the device may draw current
|
||||||
|
* beyond the permissible limit for low-power mode (LPM).
|
||||||
|
* A 10ms delay, based on experimental observations,
|
||||||
|
* allows the UFS device to complete its hardware reset
|
||||||
|
* before transitioning the power rail to LPM.
|
||||||
|
*/
|
||||||
|
usleep_range(10000, 11000);
|
||||||
|
}
|
||||||
|
|
||||||
return ufs_qcom_ice_suspend(host);
|
return ufs_qcom_ice_suspend(host);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/pm_qos.h>
|
#include <linux/pm_qos.h>
|
||||||
|
#include <linux/suspend.h>
|
||||||
#include <linux/debugfs.h>
|
#include <linux/debugfs.h>
|
||||||
#include <linux/uuid.h>
|
#include <linux/uuid.h>
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
@@ -31,6 +32,7 @@ struct intel_host {
|
|||||||
u32 dsm_fns;
|
u32 dsm_fns;
|
||||||
u32 active_ltr;
|
u32 active_ltr;
|
||||||
u32 idle_ltr;
|
u32 idle_ltr;
|
||||||
|
int saved_spm_lvl;
|
||||||
struct dentry *debugfs_root;
|
struct dentry *debugfs_root;
|
||||||
struct gpio_desc *reset_gpio;
|
struct gpio_desc *reset_gpio;
|
||||||
};
|
};
|
||||||
@@ -347,6 +349,7 @@ static int ufs_intel_common_init(struct ufs_hba *hba)
|
|||||||
host = devm_kzalloc(hba->dev, sizeof(*host), GFP_KERNEL);
|
host = devm_kzalloc(hba->dev, sizeof(*host), GFP_KERNEL);
|
||||||
if (!host)
|
if (!host)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
host->saved_spm_lvl = -1;
|
||||||
ufshcd_set_variant(hba, host);
|
ufshcd_set_variant(hba, host);
|
||||||
intel_dsm_init(host, hba->dev);
|
intel_dsm_init(host, hba->dev);
|
||||||
if (INTEL_DSM_SUPPORTED(host, RESET)) {
|
if (INTEL_DSM_SUPPORTED(host, RESET)) {
|
||||||
@@ -425,7 +428,8 @@ static int ufs_intel_lkf_init(struct ufs_hba *hba)
|
|||||||
static int ufs_intel_adl_init(struct ufs_hba *hba)
|
static int ufs_intel_adl_init(struct ufs_hba *hba)
|
||||||
{
|
{
|
||||||
hba->nop_out_timeout = 200;
|
hba->nop_out_timeout = 200;
|
||||||
hba->quirks |= UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8;
|
hba->quirks |= UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8 |
|
||||||
|
UFSHCD_QUIRK_PERFORM_LINK_STARTUP_ONCE;
|
||||||
hba->caps |= UFSHCD_CAP_WB_EN;
|
hba->caps |= UFSHCD_CAP_WB_EN;
|
||||||
return ufs_intel_common_init(hba);
|
return ufs_intel_common_init(hba);
|
||||||
}
|
}
|
||||||
@@ -538,6 +542,66 @@ static int ufshcd_pci_restore(struct device *dev)
|
|||||||
|
|
||||||
return ufshcd_system_resume(dev);
|
return ufshcd_system_resume(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ufs_intel_suspend_prepare(struct device *dev)
|
||||||
|
{
|
||||||
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
||||||
|
struct intel_host *host = ufshcd_get_variant(hba);
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only s2idle (S0ix) retains link state. Force power-off
|
||||||
|
* (UFS_PM_LVL_5) for any other case.
|
||||||
|
*/
|
||||||
|
if (pm_suspend_target_state != PM_SUSPEND_TO_IDLE && hba->spm_lvl < UFS_PM_LVL_5) {
|
||||||
|
host->saved_spm_lvl = hba->spm_lvl;
|
||||||
|
hba->spm_lvl = UFS_PM_LVL_5;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ufshcd_suspend_prepare(dev);
|
||||||
|
|
||||||
|
if (err < 0 && host->saved_spm_lvl != -1) {
|
||||||
|
hba->spm_lvl = host->saved_spm_lvl;
|
||||||
|
host->saved_spm_lvl = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ufs_intel_resume_complete(struct device *dev)
|
||||||
|
{
|
||||||
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
||||||
|
struct intel_host *host = ufshcd_get_variant(hba);
|
||||||
|
|
||||||
|
ufshcd_resume_complete(dev);
|
||||||
|
|
||||||
|
if (host->saved_spm_lvl != -1) {
|
||||||
|
hba->spm_lvl = host->saved_spm_lvl;
|
||||||
|
host->saved_spm_lvl = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ufshcd_pci_suspend_prepare(struct device *dev)
|
||||||
|
{
|
||||||
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
if (!strcmp(hba->vops->name, "intel-pci"))
|
||||||
|
return ufs_intel_suspend_prepare(dev);
|
||||||
|
|
||||||
|
return ufshcd_suspend_prepare(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ufshcd_pci_resume_complete(struct device *dev)
|
||||||
|
{
|
||||||
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
if (!strcmp(hba->vops->name, "intel-pci")) {
|
||||||
|
ufs_intel_resume_complete(dev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ufshcd_resume_complete(dev);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -611,8 +675,8 @@ static const struct dev_pm_ops ufshcd_pci_pm_ops = {
|
|||||||
.thaw = ufshcd_system_resume,
|
.thaw = ufshcd_system_resume,
|
||||||
.poweroff = ufshcd_system_suspend,
|
.poweroff = ufshcd_system_suspend,
|
||||||
.restore = ufshcd_pci_restore,
|
.restore = ufshcd_pci_restore,
|
||||||
.prepare = ufshcd_suspend_prepare,
|
.prepare = ufshcd_pci_suspend_prepare,
|
||||||
.complete = ufshcd_resume_complete,
|
.complete = ufshcd_pci_resume_complete,
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -258,8 +258,8 @@ struct scsi_device {
|
|||||||
unsigned int queue_stopped; /* request queue is quiesced */
|
unsigned int queue_stopped; /* request queue is quiesced */
|
||||||
bool offline_already; /* Device offline message logged */
|
bool offline_already; /* Device offline message logged */
|
||||||
|
|
||||||
unsigned int ua_new_media_ctr; /* Counter for New Media UNIT ATTENTIONs */
|
atomic_t ua_new_media_ctr; /* Counter for New Media UNIT ATTENTIONs */
|
||||||
unsigned int ua_por_ctr; /* Counter for Power On / Reset UAs */
|
atomic_t ua_por_ctr; /* Counter for Power On / Reset UAs */
|
||||||
|
|
||||||
atomic_t disk_events_disable_depth; /* disable depth for disk events */
|
atomic_t disk_events_disable_depth; /* disable depth for disk events */
|
||||||
|
|
||||||
@@ -719,10 +719,8 @@ static inline int scsi_device_busy(struct scsi_device *sdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Macros to access the UNIT ATTENTION counters */
|
/* Macros to access the UNIT ATTENTION counters */
|
||||||
#define scsi_get_ua_new_media_ctr(sdev) \
|
#define scsi_get_ua_new_media_ctr(sdev) atomic_read(&sdev->ua_new_media_ctr)
|
||||||
((const unsigned int)(sdev->ua_new_media_ctr))
|
#define scsi_get_ua_por_ctr(sdev) atomic_read(&sdev->ua_por_ctr)
|
||||||
#define scsi_get_ua_por_ctr(sdev) \
|
|
||||||
((const unsigned int)(sdev->ua_por_ctr))
|
|
||||||
|
|
||||||
#define MODULE_ALIAS_SCSI_DEVICE(type) \
|
#define MODULE_ALIAS_SCSI_DEVICE(type) \
|
||||||
MODULE_ALIAS("scsi:t-" __stringify(type) "*")
|
MODULE_ALIAS("scsi:t-" __stringify(type) "*")
|
||||||
|
|||||||
@@ -683,6 +683,13 @@ enum ufshcd_quirks {
|
|||||||
* single doorbell mode.
|
* single doorbell mode.
|
||||||
*/
|
*/
|
||||||
UFSHCD_QUIRK_BROKEN_LSDBS_CAP = 1 << 25,
|
UFSHCD_QUIRK_BROKEN_LSDBS_CAP = 1 << 25,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This quirk indicates that DME_LINKSTARTUP should not be issued a 2nd
|
||||||
|
* time (refer link_startup_again) after the 1st time was successful,
|
||||||
|
* because it causes link startup to become unreliable.
|
||||||
|
*/
|
||||||
|
UFSHCD_QUIRK_PERFORM_LINK_STARTUP_ONCE = 1 << 26,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ufshcd_caps {
|
enum ufshcd_caps {
|
||||||
|
|||||||
Reference in New Issue
Block a user