summaryrefslogtreecommitdiff
path: root/drivers/ufs
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ufs')
-rw-r--r--drivers/ufs/core/ufs-sysfs.c2
-rw-r--r--drivers/ufs/core/ufs-sysfs.h1
-rw-r--r--drivers/ufs/core/ufshcd.c17
-rw-r--r--drivers/ufs/host/ufs-qcom.c15
-rw-r--r--drivers/ufs/host/ufshcd-pci.c70
5 files changed, 88 insertions, 17 deletions
diff --git a/drivers/ufs/core/ufs-sysfs.c b/drivers/ufs/core/ufs-sysfs.c
index c040afc6668e..0086816b27cd 100644
--- a/drivers/ufs/core/ufs-sysfs.c
+++ b/drivers/ufs/core/ufs-sysfs.c
@@ -1949,7 +1949,7 @@ static umode_t ufs_sysfs_hid_is_visible(struct kobject *kobj,
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",
.attrs = ufs_sysfs_hid,
.is_visible = ufs_sysfs_hid_is_visible,
diff --git a/drivers/ufs/core/ufs-sysfs.h b/drivers/ufs/core/ufs-sysfs.h
index 6efb82a082fd..8d94af3b8077 100644
--- a/drivers/ufs/core/ufs-sysfs.h
+++ b/drivers/ufs/core/ufs-sysfs.h
@@ -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_lun_attributes_group;
-extern const struct attribute_group ufs_sysfs_hid_group;
#endif
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index 9ca27de4767a..d6a060a72461 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -5066,7 +5066,8 @@ static int ufshcd_link_startup(struct ufs_hba *hba)
* 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.
*/
- 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:
@@ -5131,12 +5132,8 @@ link_startup:
ufshcd_readl(hba, REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER);
ret = ufshcd_make_hba_operational(hba);
out:
- if (ret) {
+ if (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;
}
@@ -8503,8 +8500,6 @@ static int ufs_get_device_desc(struct ufs_hba *hba)
DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP) &
UFS_DEV_HID_SUPPORT;
- sysfs_update_group(&hba->dev->kobj, &ufs_sysfs_hid_group);
-
model_index = desc_buf[DEVICE_DESC_PARAM_PRDCT_NAME];
err = ufshcd_read_string_desc(hba, model_index,
@@ -10661,7 +10656,7 @@ remove_scsi_host:
* @mmio_base: base register address
* @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)
{
@@ -10891,8 +10886,8 @@ initialized:
if (err)
goto out_disable;
- async_schedule(ufshcd_async_scan, hba);
ufs_sysfs_add_nodes(hba->dev);
+ async_schedule(ufshcd_async_scan, hba);
device_enable_async_suspend(dev);
ufshcd_pm_qos_init(hba);
@@ -10902,7 +10897,7 @@ out_disable:
hba->is_irq_enabled = false;
ufshcd_hba_exit(hba);
out_error:
- return err;
+ return err > 0 ? -EIO : err;
}
EXPORT_SYMBOL_GPL(ufshcd_init);
diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
index 3e83dc51d538..eba0e6617483 100644
--- a/drivers/ufs/host/ufs-qcom.c
+++ b/drivers/ufs/host/ufs-qcom.c
@@ -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 */
- 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);
+ /*
+ * 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);
}
diff --git a/drivers/ufs/host/ufshcd-pci.c b/drivers/ufs/host/ufshcd-pci.c
index b87e03777395..5f65dfad1a71 100644
--- a/drivers/ufs/host/ufshcd-pci.c
+++ b/drivers/ufs/host/ufshcd-pci.c
@@ -15,6 +15,7 @@
#include <linux/pci.h>
#include <linux/pm_runtime.h>
#include <linux/pm_qos.h>
+#include <linux/suspend.h>
#include <linux/debugfs.h>
#include <linux/uuid.h>
#include <linux/acpi.h>
@@ -31,6 +32,7 @@ struct intel_host {
u32 dsm_fns;
u32 active_ltr;
u32 idle_ltr;
+ int saved_spm_lvl;
struct dentry *debugfs_root;
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);
if (!host)
return -ENOMEM;
+ host->saved_spm_lvl = -1;
ufshcd_set_variant(hba, host);
intel_dsm_init(host, hba->dev);
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)
{
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;
return ufs_intel_common_init(hba);
}
@@ -538,6 +542,66 @@ static int ufshcd_pci_restore(struct device *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
/**
@@ -611,8 +675,8 @@ static const struct dev_pm_ops ufshcd_pci_pm_ops = {
.thaw = ufshcd_system_resume,
.poweroff = ufshcd_system_suspend,
.restore = ufshcd_pci_restore,
- .prepare = ufshcd_suspend_prepare,
- .complete = ufshcd_resume_complete,
+ .prepare = ufshcd_pci_suspend_prepare,
+ .complete = ufshcd_pci_resume_complete,
#endif
};