diff --git a/drivers/scsi/ufs/debugfs.c b/drivers/scsi/ufs/debugfs.c index 9328ea6cb41f..9cfb12cdbcfe 100644 --- a/drivers/scsi/ufs/debugfs.c +++ b/drivers/scsi/ufs/debugfs.c @@ -753,6 +753,23 @@ out: return ret; } +static int ufsdbg_config_pwr_mode(struct ufs_hba *hba, + struct ufs_pa_layer_attr *desired_pwr_mode) +{ + #define DOORBELL_CLR_TOUT_US (1000 * 1000) /* 1 sec */ + int ret; + + pm_runtime_get_sync(hba->dev); + scsi_block_requests(hba->host); + ret = ufshcd_wait_for_doorbell_clr(hba, DOORBELL_CLR_TOUT_US); + if (!ret) + ret = ufshcd_change_power_mode(hba, desired_pwr_mode); + scsi_unblock_requests(hba->host); + pm_runtime_put_sync(hba->dev); + + return ret; +} + static ssize_t ufsdbg_power_mode_write(struct file *file, const char __user *ubuf, size_t cnt, loff_t *ppos) @@ -764,7 +781,6 @@ static ssize_t ufsdbg_power_mode_write(struct file *file, loff_t buff_pos = 0; int ret; int idx = 0; - #define DOORBELL_CLR_TOUT_US (1000 * 1000) /* 1 sec */ ret = simple_write_to_buffer(pwr_mode_str, BUFF_LINE_CAPACITY, &buff_pos, ubuf, cnt); @@ -800,13 +816,7 @@ static ssize_t ufsdbg_power_mode_write(struct file *file, return cnt; } - pm_runtime_get_sync(hba->dev); - scsi_block_requests(hba->host); - ret = ufshcd_wait_for_doorbell_clr(hba, DOORBELL_CLR_TOUT_US); - if (!ret) - ret = ufshcd_change_power_mode(hba, &final_pwr_mode); - scsi_unblock_requests(hba->host); - pm_runtime_put_sync(hba->dev); + ret = ufsdbg_config_pwr_mode(hba, &final_pwr_mode); if (ret == -EBUSY) dev_err(hba->dev, "%s: ufshcd_config_pwr_mode failed: system is busy, try again\n", @@ -830,19 +840,21 @@ static const struct file_operations ufsdbg_power_mode_desc = { .write = ufsdbg_power_mode_write, }; -static int ufsdbg_dme_local_read(void *data, u64 *attr_val) +static int ufsdbg_dme_read(void *data, u64 *attr_val, bool peer) { int ret; struct ufs_hba *hba = data; - u32 read_val = 0; + u32 attr_id, read_val = 0; + int (*read_func) (struct ufs_hba *, u32, u32 *); if (!hba) return -EINVAL; + read_func = peer ? ufshcd_dme_peer_get : ufshcd_dme_get; + attr_id = peer ? hba->debugfs_files.dme_peer_attr_id : + hba->debugfs_files.dme_local_attr_id; pm_runtime_get_sync(hba->dev); - ret = ufshcd_dme_get(hba, - UIC_ARG_MIB(hba->debugfs_files.dme_local_attr_id), - &read_val); + ret = ufshcd_dme_get(hba, UIC_ARG_MIB(attr_id), &read_val); pm_runtime_put_sync(hba->dev); if (!ret) @@ -863,11 +875,70 @@ static int ufsdbg_dme_local_set_attr_id(void *data, u64 attr_id) return 0; } +static int ufsdbg_dme_local_read(void *data, u64 *attr_val) +{ + return ufsdbg_dme_read(data, attr_val, false); +} + DEFINE_SIMPLE_ATTRIBUTE(ufsdbg_dme_local_read_ops, ufsdbg_dme_local_read, ufsdbg_dme_local_set_attr_id, "%llu\n"); +static int ufsdbg_dme_peer_read(void *data, u64 *attr_val) +{ + int ret; + struct ufs_hba *hba = data; + struct ufs_pa_layer_attr orig_pwr_info; + struct ufs_pa_layer_attr temp_pwr_info; + bool restore_pwr_mode = false; + + if (!hba) + return -EINVAL; + + if (hba->quirks & UFSHCD_QUIRK_DME_PEER_GET_FAST_MODE) { + orig_pwr_info = hba->pwr_info; + temp_pwr_info = orig_pwr_info; + if (orig_pwr_info.pwr_tx == FAST_MODE || + orig_pwr_info.pwr_rx == FAST_MODE) { + temp_pwr_info.pwr_tx = FASTAUTO_MODE; + temp_pwr_info.pwr_rx = FASTAUTO_MODE; + ret = ufsdbg_config_pwr_mode(hba, &temp_pwr_info); + if (ret) + goto out; + else + restore_pwr_mode = true; + } + } + + ret = ufsdbg_dme_read(data, attr_val, true); + + if (hba->quirks & UFSHCD_QUIRK_DME_PEER_GET_FAST_MODE) { + if (restore_pwr_mode) + ufsdbg_config_pwr_mode(hba, &orig_pwr_info); + } + +out: + return ret; +} + +static int ufsdbg_dme_peer_set_attr_id(void *data, u64 attr_id) +{ + struct ufs_hba *hba = data; + + if (!hba) + return -EINVAL; + + hba->debugfs_files.dme_peer_attr_id = (u32)attr_id; + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(ufsdbg_dme_peer_read_ops, + ufsdbg_dme_peer_read, + ufsdbg_dme_peer_set_attr_id, + "%llu\n"); + void ufsdbg_add_debugfs(struct ufs_hba *hba) { if (!hba) { @@ -962,6 +1033,17 @@ void ufsdbg_add_debugfs(struct ufs_hba *hba) goto err; } + hba->debugfs_files.dme_peer_read = + debugfs_create_file("dme_peer_read", S_IRUSR | S_IWUSR, + hba->debugfs_files.debugfs_root, hba, + &ufsdbg_dme_peer_read_ops); + if (!hba->debugfs_files.dme_peer_read) { + dev_err(hba->dev, + "%s: failed create dme_peer_read debugfs entry\n", + __func__); + goto err; + } + ufsdbg_setup_fault_injection(hba); return; diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index e1960dbb46d2..f74031146933 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -754,6 +754,8 @@ static void ufs_qcom_advertise_quirks(struct ufs_hba *hba) hba->quirks |= (UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS | UFSHCD_QUIRK_BROKEN_PA_RXHSUNTERMCAP | UFSHCD_QUIRK_BROKEN_LCC); + + hba->quirks |= UFSHCD_QUIRK_DME_PEER_GET_FAST_MODE; } static int ufs_qcom_get_bus_vote(struct ufs_qcom_host *host, diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 85ec9dceced6..ceffb2f4591d 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -2777,6 +2777,7 @@ int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba, u64 wait_timeout_us) bool timeout = false; ktime_t start = ktime_get(); + ufshcd_hold(hba, false); spin_lock_irqsave(hba->host->host_lock, flags); if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL) { ret = -EBUSY; @@ -2811,6 +2812,7 @@ int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba, u64 wait_timeout_us) } out: spin_unlock_irqrestore(hba->host->host_lock, flags); + ufshcd_release(hba); return ret; } diff --git a/include/linux/scsi/ufs/ufshcd.h b/include/linux/scsi/ufs/ufshcd.h index dd27d41320d7..7109f59f9e80 100644 --- a/include/linux/scsi/ufs/ufshcd.h +++ b/include/linux/scsi/ufs/ufshcd.h @@ -378,7 +378,9 @@ struct debugfs_files { struct dentry *dump_dev_desc; struct dentry *power_mode; struct dentry *dme_local_read; + struct dentry *dme_peer_read; u32 dme_local_attr_id; + u32 dme_peer_attr_id; #ifdef CONFIG_UFS_FAULT_INJECTION struct fault_attr fail_attr; #endif