Enable power management for ufs wlun
Subsystems (9)
v12 cover Enable power management for ufs wlun
This patch attempts to fix a deadlock in ufs while sending SSU. Recently, blk_queue_enter() added a check to not process requests if the queue is suspended. That leads to a resume of the associated device which is suspended. In ufs, that device is ufs device wlun and it's parent is ufs_hba. This resume tries to resume ufs device wlun which in turn tries to resume ufs_hba, which is already in the process of suspending, thus causing a deadlock.
This patch takes care of:
* Suspending the ufs device lun only after all other luns are suspended
* Sending SSU during ufs device wlun suspend
* Clearing uac for rpmb and ufs device wlun
* Not sending commands to the device during host suspend
v11 -> v12:
- Addressed Adrian's comments
* Fixed ahit for Mediatek driver
* Fixed error handling in ufshcd_core_init()
* Tested this patch and the issue is still seen.
v10 -> v11:
- Fixed supplier suspending before consumer race
- Addressed Adrian's comments
* Added proper resume/suspend cb to ufshcd_auto_hibern8_update()
* Cosmetic changes to ufshcd-pci.c
* Cleaned up ufshcd_system_suspend()
* Added ufshcd_debugfs_eh_exit to ufshcd_core_init()
v9 -> v10:
- Addressed Adrian's comments
* Moved suspend/resume vops to __ufshcd_wl_[suspend/resume]()
* Added correct resume in ufs_bsg
v8 -> v9:
- Addressed Adrian's comments
* Moved link transition to __ufshcd_wl_[suspend/resume]()
* Fixed the other minor comments
v7 -> v8:
- Addressed Adrian's comments
* Removed separate autosuspend delay for ufs-device lun
* Fixed the ee handler getting scheduled during pm
* Always runtime resume in suspend_prepare()
* Added CONFIG_PM_SLEEP where needed
v6 -> v7:
* Resume the ufs device before shutting it down
v5 -> v6:
- Addressed Adrian's comments
* Added complete() cb
* Added suspend_prepare() and complete() to all drivers
* Moved suspend_prepare() and complete() to ufshcd
* .poweroff() uses ufhcd_wl_poweroff()
* Removed several forward declarations
* Moved scsi_register_driver() to ufshcd_core_init()
v4 -> v5:
- Addressed Adrian's comments
* Used the rpmb driver contributed by Adrian
* Runtime-resume the ufs device during suspend to honor spm-lvl
* Unregister the scsi_driver in ufshcd_remove()
* Currently shutdown() puts the ufs device to power-down mode so, just removed ufshcd_pci_poweroff()
* Quiesce the scsi device during shutdown instead of remove
v3 RFC -> v4:
- Addressed Bart's comments
* Except that I didn't get any checkpatch failures
- Addressed Avri's comments
- Addressed Adrian's comments
* Added a check for deepsleep power mode
* Removed a couple of forward declarations
* Didn't separate the scsi drivers because in rpmb case it just sends uac in resume and it seemed pretty neat to me.
- Added sysfs changes to resume the devices before accessing
Asutosh Das (2):
scsi: ufs: Enable power management for wlun
ufs: sysfs: Resume the proper scsi device
drivers/scsi/ufs/cdns-pltfrm.c | 2 + drivers/scsi/ufs/tc-dwc-g210-pci.c | 2 + drivers/scsi/ufs/ufs-debugfs.c | 2 +- drivers/scsi/ufs/ufs-debugfs.h | 2 +- drivers/scsi/ufs/ufs-exynos.c | 2 + drivers/scsi/ufs/ufs-hisi.c | 2 + drivers/scsi/ufs/ufs-mediatek.c | 12 +- drivers/scsi/ufs/ufs-qcom.c | 2 + drivers/scsi/ufs/ufs-sysfs.c | 30 +- drivers/scsi/ufs/ufs_bsg.c | 6 +- drivers/scsi/ufs/ufshcd-pci.c | 36 +-- drivers/scsi/ufs/ufshcd.c | 622 ++++++++++++++++++++++++++----------- drivers/scsi/ufs/ufshcd.h | 6 + include/trace/events/ufs.h | 20 ++ 14 files changed, 508 insertions(+), 238 deletions(-)
Signature · 7 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project. _______________________________________________ Linux-mediatek mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-mediatek
PATCH v12 1/2 scsi: ufs: Enable power management for wlun
During runtime-suspend of ufs host, the scsi devices are already suspended and so are the queues associated with them. But the ufs host sends SSU to wlun during its runtime-suspend. During the process blk_queue_enter checks if the queue is not in suspended state. If so, it waits for the queue to resume, and never comes out of it.
The commit
(d55d15a33: scsi: block: Do not accept any requests while suspended) adds the check if the queue is in suspended state in blk_queue_enter().
Call trace: __switch_to+0x174/0x2c4 __schedule+0x478/0x764 schedule+0x9c/0xe0 blk_queue_enter+0x158/0x228 blk_mq_alloc_request+0x40/0xa4 blk_get_request+0x2c/0x70 __scsi_execute+0x60/0x1c4 ufshcd_set_dev_pwr_mode+0x124/0x1e4 ufshcd_suspend+0x208/0x83c ufshcd_runtime_suspend+0x40/0x154 ufshcd_pltfrm_runtime_suspend+0x14/0x20 pm_generic_runtime_suspend+0x28/0x3c __rpm_callback+0x80/0x2a4 rpm_suspend+0x308/0x614 rpm_idle+0x158/0x228 pm_runtime_work+0x84/0xac process_one_work+0x1f0/0x470 worker_thread+0x26c/0x4c8 kthread+0x13c/0x320 ret_from_fork+0x10/0x18
Fix this by registering ufs device wlun as a scsi driver and registering it for block runtime-pm. Also make this as a supplier for all other luns. That way, this device wlun suspends after all the consumers and resumes after hba resumes.
--- drivers/scsi/ufs/cdns-pltfrm.c | 2 + drivers/scsi/ufs/tc-dwc-g210-pci.c | 2 + drivers/scsi/ufs/ufs-debugfs.c | 2 +- drivers/scsi/ufs/ufs-debugfs.h | 2 +- drivers/scsi/ufs/ufs-exynos.c | 2 + drivers/scsi/ufs/ufs-hisi.c | 2 + drivers/scsi/ufs/ufs-mediatek.c | 12 +- drivers/scsi/ufs/ufs-qcom.c | 2 + drivers/scsi/ufs/ufs_bsg.c | 6 +- drivers/scsi/ufs/ufshcd-pci.c | 36 +-- drivers/scsi/ufs/ufshcd.c | 622 ++++++++++++++++++++++++++----------- drivers/scsi/ufs/ufshcd.h | 6 + include/trace/events/ufs.h | 20 ++ 13 files changed, 491 insertions(+), 225 deletions(-)
drivers/scsi/ufs/cdns-pltfrm.c +2 -0
@@ -319,6 +319,8 @@ static const struct dev_pm_ops cdns_ufs_dev_pm_ops = { .runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_idle = ufshcd_pltfrm_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, };static struct platform_driver cdns_ufs_pltfrm_driver = {
drivers/scsi/ufs/tc-dwc-g210-pci.c +2 -0
@@ -148,6 +148,8 @@ static const struct dev_pm_ops tc_dwc_g210_pci_pm_ops = { .runtime_suspend = tc_dwc_g210_pci_runtime_suspend, .runtime_resume = tc_dwc_g210_pci_runtime_resume, .runtime_idle = tc_dwc_g210_pci_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, };static const struct pci_device_id tc_dwc_g210_pci_tbl[] = {
drivers/scsi/ufs/ufs-debugfs.c +1 -1
@@ -13,7 +13,7 @@ void __init ufs_debugfs_init(void) ufs_debugfs_root =debugfs_create_dir ("ufshcd" ,NULL ); } -void __exit ufs_debugfs_exit(void) +void ufs_debugfs_exit (void ) {debugfs_remove_recursive (ufs_debugfs_root); }
drivers/scsi/ufs/ufs-debugfs.h +1 -1
@@ -9,7 +9,7 @@ struct ufs_hba;#ifdef CONFIG_DEBUG_FS void __init ufs_debugfs_init (void ); -void __exit ufs_debugfs_exit(void); +void ufs_debugfs_exit (void );void ufs_debugfs_hba_init (struct ufs_hba *hba);void ufs_debugfs_hba_exit (struct ufs_hba *hba);#else
drivers/scsi/ufs/ufs-exynos.c +2 -0
@@ -1268,6 +1268,8 @@ static const struct dev_pm_ops exynos_ufs_pm_ops = { .runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_idle = ufshcd_pltfrm_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, };static struct platform_driver exynos_ufs_pltform = {
drivers/scsi/ufs/ufs-hisi.c +2 -0
@@ -574,6 +574,8 @@ static const struct dev_pm_ops ufs_hisi_pm_ops = { .runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_idle = ufshcd_pltfrm_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, };static struct platform_driver ufs_hisi_pltform = {
drivers/scsi/ufs/ufs-mediatek.c +6 -6
@@ -810,12 +810,10 @@ static int ufs_mtk_post_link(struct ufs_hba *hba)/* enable unipro clock gating feature */ ufs_mtk_cfg_unipro_cg (hba, true); -/* configure auto-hibern8 timer to 10ms */ -if (ufshcd_is_auto_hibern8_supported (hba)) { -ufshcd_auto_hibern8_update (hba, -FIELD_PREP (UFSHCI_AHIBERN8_TIMER_MASK ,10 ) | -FIELD_PREP (UFSHCI_AHIBERN8_SCALE_MASK ,3 )); - } +/* will be configured during probe hba */ +if (ufshcd_is_auto_hibern8_supported (hba)) + hba->ahit =FIELD_PREP (UFSHCI_AHIBERN8_TIMER_MASK ,10 ) | +FIELD_PREP (UFSHCI_AHIBERN8_SCALE_MASK ,3 );ufs_mtk_setup_clk_gating (hba);
@@ -1097,6 +1095,8 @@ static const struct dev_pm_ops ufs_mtk_pm_ops = { .runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_idle = ufshcd_pltfrm_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, };static struct platform_driver ufs_mtk_pltform = {
drivers/scsi/ufs/ufs-qcom.c +2 -0
@@ -1546,6 +1546,8 @@ static const struct dev_pm_ops ufs_qcom_pm_ops = { .runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_idle = ufshcd_pltfrm_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, };static struct platform_driver ufs_qcom_pltform = {
drivers/scsi/ufs/ufs_bsg.c +3 -3
@@ -97,7 +97,7 @@ static int ufs_bsg_request(struct bsg_job *job) bsg_reply->reply_payload_rcv_len =0 ; -pm_runtime_get_sync (hba->dev); +scsi_autopm_get_device (hba->sdev_ufs_device); msgcode = bsg_request->msgcode;switch (msgcode) {
@@ -106,7 +106,7 @@ static int ufs_bsg_request(struct bsg_job *job) ret =ufs_bsg_alloc_desc_buffer (hba, job, &desc_buff, &desc_len, desc_op);if (ret) { -pm_runtime_put_sync (hba->dev); +scsi_autopm_put_device (hba->sdev_ufs_device); goto out; }
@@ -138,7 +138,7 @@ static int ufs_bsg_request(struct bsg_job *job)break ; } -pm_runtime_put_sync (hba->dev); +scsi_autopm_put_device (hba->sdev_ufs_device);if (!desc_buff) goto out;
drivers/scsi/ufs/ufshcd-pci.c +5 -31
@@ -247,29 +247,6 @@ static int ufshcd_pci_resume(struct device *dev) return ufshcd_system_resume(dev_get_drvdata(dev)); } -/** -* ufshcd_pci_poweroff - suspend-to-disk poweroff function -* @dev: pointer to PCI device handle -* -* Returns 0 if successful -* Returns non-zero otherwise -*/ -static int ufshcd_pci_poweroff (struct device *dev) -{ -struct ufs_hba *hba =dev_get_drvdata (dev); -int spm_lvl = hba->spm_lvl; -int ret; - -/* -* For poweroff we need to set the UFS device to PowerDown mode. -* Force spm_lvl to ensure that. -*/ - hba->spm_lvl =5 ; - ret =ufshcd_system_suspend (hba); - hba->spm_lvl = spm_lvl; -return ret; -} -#endif /* !CONFIG_PM_SLEEP */ #ifdef CONFIG_PM
@@ -365,17 +342,14 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) }static const struct dev_pm_ops ufshcd_pci_pm_ops = { -#ifdef CONFIG_PM_SLEEP - .suspend = ufshcd_pci_suspend, - .resume = ufshcd_pci_resume, - .freeze = ufshcd_pci_suspend, - .thaw = ufshcd_pci_resume, - .poweroff = ufshcd_pci_poweroff, - .restore = ufshcd_pci_resume, -#endif SET_RUNTIME_PM_OPS(ufshcd_pci_runtime_suspend, ufshcd_pci_runtime_resume, ufshcd_pci_runtime_idle) + SET_SYSTEM_SLEEP_PM_OPS(ufshcd_pci_suspend, ufshcd_pci_resume) +#ifdef CONFIG_PM_SLEEP + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, +#endif };static const struct pci_device_id ufshcd_pci_tbl[] = {
drivers/scsi/ufs/ufshcd.c +439 -183
969 lines not on this page · raw message
drivers/scsi/ufs/ufshcd.h +6 -0
@@ -72,6 +72,8 @@ enum ufs_event_type {UFS_EVT_LINK_STARTUP_FAIL ,UFS_EVT_RESUME_ERR ,UFS_EVT_SUSPEND_ERR , +UFS_EVT_WL_SUSP_ERR , +UFS_EVT_WL_RES_ERR ,/* abnormal events */ UFS_EVT_DEV_RESET,
@@ -804,6 +806,7 @@ struct ufs_hba {struct list_head clk_list_head;bool wlun_dev_clr_ua; +bool wlun_rpmb_clr_ua;/* Number of requests aborts */ int req_abort_count;
@@ -841,6 +844,7 @@ struct ufs_hba {#ifdef CONFIG_DEBUG_FS struct dentry *debugfs_root;#endif +u32 luns_avail; };/* Returns true if clocks can be gated. Otherwise false */
@@ -1100,6 +1104,8 @@ int ufshcd_exec_raw_upiu_cmd(struct ufs_hba *hba, enum query_opcode desc_op);int ufshcd_wb_ctrl (struct ufs_hba *hba,bool enable); +int ufshcd_suspend_prepare (struct device *dev); +void ufshcd_resume_complete (struct device *dev);/* Wrapper functions for safely calling variant operations */ static inline const char *ufshcd_get_var_name (struct ufs_hba *hba)
include/trace/events/ufs.h +20 -0
@@ -246,6 +246,26 @@ DEFINE_EVENT(ufshcd_template, ufshcd_init, int dev_state, int link_state), TP_ARGS(dev_name, err, usecs, dev_state, link_state)); +DEFINE_EVENT (ufshcd_template, ufshcd_wl_suspend, + TP_PROTO(const char *dev_name, int err, s64 usecs, + int dev_state, int link_state), +TP_ARGS (dev_name, err, usecs, dev_state, link_state)); + +DEFINE_EVENT (ufshcd_template, ufshcd_wl_resume, + TP_PROTO(const char *dev_name, int err, s64 usecs, + int dev_state, int link_state), +TP_ARGS (dev_name, err, usecs, dev_state, link_state)); + +DEFINE_EVENT (ufshcd_template, ufshcd_wl_runtime_suspend, + TP_PROTO(const char *dev_name, int err, s64 usecs, + int dev_state, int link_state), +TP_ARGS (dev_name, err, usecs, dev_state, link_state)); + +DEFINE_EVENT (ufshcd_template, ufshcd_wl_runtime_resume, + TP_PROTO(const char *dev_name, int err, s64 usecs, + int dev_state, int link_state), +TP_ARGS (dev_name, err, usecs, dev_state, link_state)); + TRACE_EVENT(ufshcd_command, TP_PROTO(const char *dev_name, enum ufs_trace_str_t str_t, unsigned int tag, u32 doorbell, int transfer_len, u32 intr,
Signature · 7 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project. _______________________________________________ Linux-mediatek mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-mediatek
PATCH v12 2/2 ufs: sysfs: Resume the proper scsi device
In reply to Asutosh DasResumes the actual scsi device the unit descriptor of which is being accessed instead of the hba alone.
--- drivers/scsi/ufs/ufs-sysfs.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-)
drivers/scsi/ufs/ufs-sysfs.c +17 -13
@@ -245,9 +245,9 @@ static ssize_t wb_on_store(struct device *dev, struct device_attribute *attr, goto out; } -pm_runtime_get_sync (hba->dev); +scsi_autopm_get_device (hba->sdev_ufs_device); res =ufshcd_wb_ctrl (hba, wb_enable); -pm_runtime_put_sync (hba->dev); +scsi_autopm_put_device (hba->sdev_ufs_device); out:up (&hba->host_sem);return res <0 ? res : count;
@@ -297,10 +297,10 @@ static ssize_t ufs_sysfs_read_desc_param(struct ufs_hba *hba, goto out; } -pm_runtime_get_sync (hba->dev); +scsi_autopm_get_device (hba->sdev_ufs_device); ret =ufshcd_read_desc_param (hba, desc_id, desc_index, param_offset, desc_buf, param_size); -pm_runtime_put_sync (hba->dev); +scsi_autopm_put_device (hba->sdev_ufs_device);if (ret) { ret = -EINVAL ; goto out;
@@ -678,7 +678,7 @@ static ssize_t _name##_show(struct device *dev, \up (&hba->host_sem); \return -ENOMEM ; \ } \ -pm_runtime_get_sync (hba->dev); \ +scsi_autopm_get_device (hba->sdev_ufs_device); \ ret =ufshcd_query_descriptor_retry (hba, \UPIU_QUERY_OPCODE_READ_DESC ,QUERY_DESC_IDN_DEVICE , \0 ,0 , desc_buf, &desc_len); \
@@ -695,7 +695,7 @@ static ssize_t _name##_show(struct device *dev, \ goto out; \ ret =sysfs_emit (buf,"%s\n" , desc_buf); \ out: \ -pm_runtime_put_sync (hba->dev); \ +scsi_autopm_put_device (hba->sdev_ufs_device); \kfree (desc_buf); \up (&hba->host_sem); \return ret; \
@@ -744,10 +744,10 @@ static ssize_t _name##_show(struct device *dev, \ } \ if (ufshcd_is_wb_flags(QUERY_FLAG_IDN##_uname)) \ index =ufshcd_wb_get_query_index (hba); \ -pm_runtime_get_sync (hba->dev); \ +scsi_autopm_get_device (hba->sdev_ufs_device); \ ret =ufshcd_query_flag (hba,UPIU_QUERY_OPCODE_READ_FLAG , \ QUERY_FLAG_IDN##_uname, index, &flag); \ -pm_runtime_put_sync (hba->dev); \ +scsi_autopm_put_device (hba->sdev_ufs_device); \if (ret) { \ ret = -EINVAL ; \ goto out; \
@@ -813,10 +813,10 @@ static ssize_t _name##_show(struct device *dev, \ } \ if (ufshcd_is_wb_attrs(QUERY_ATTR_IDN##_uname)) \ index =ufshcd_wb_get_query_index (hba); \ -pm_runtime_get_sync (hba->dev); \ +scsi_autopm_get_device (hba->sdev_ufs_device); \ ret =ufshcd_query_attr (hba,UPIU_QUERY_OPCODE_READ_ATTR , \ QUERY_ATTR_IDN##_uname, index, 0, &value); \ -pm_runtime_put_sync (hba->dev); \ +scsi_autopm_put_device (hba->sdev_ufs_device); \if (ret) { \ ret = -EINVAL ; \ goto out; \
@@ -899,11 +899,15 @@ static ssize_t _pname##_show(struct device *dev, \struct scsi_device *sdev =to_scsi_device (dev); \struct ufs_hba *hba =shost_priv (sdev->host); \u8 lun =ufshcd_scsi_to_upiu_lun (sdev->lun); \ +int ret; \if (!ufs_is_valid_unit_desc_lun (&hba->dev_info, lun, \ _duname##_DESC_PARAM##_puname)) \return -EINVAL ; \ - return ufs_sysfs_read_desc_param(hba, QUERY_DESC_IDN_##_duname, \ +scsi_autopm_get_device (sdev); \ + ret = ufs_sysfs_read_desc_param(hba, QUERY_DESC_IDN_##_duname, \ lun, _duname##_DESC_PARAM##_puname, buf, _size); \ +scsi_autopm_put_device (sdev); \ +return ret; \ } \static DEVICE_ATTR_RO (_pname)
@@ -964,10 +968,10 @@ static ssize_t dyn_cap_needed_attribute_show(struct device *dev, goto out; } -pm_runtime_get_sync (hba->dev); +scsi_autopm_get_device (hba->sdev_ufs_device); ret =ufshcd_query_attr (hba,UPIU_QUERY_OPCODE_READ_ATTR ,QUERY_ATTR_IDN_DYN_CAP_NEEDED , lun,0 , &value); -pm_runtime_put_sync (hba->dev); +scsi_autopm_put_device (hba->sdev_ufs_device);if (ret) { ret = -EINVAL ; goto out;
Signature · 1 line
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
Re: [PATCH v12 1/2] scsi: ufs: Enable power management for wlun
In reply to Asutosh DasQuoted 9 lines · Asutosh Das
During runtime-suspend of ufs host, the scsi devices are already suspended and so are the queues associated with them. But the ufs host sends SSU to wlun during its runtime-suspend. During the process
blk_queue_enterchecks if the queue is not in suspended state. If so, it waits for the queue to resume, and never comes out of it.
The commit
(d55d15a33: scsi: block: Do not accept any requests while suspended) adds the check if the queue is in suspended state inblk_queue_enter().
What is the role of the WLUN during runtime suspend and why does a command need to be sent to the WLUN during runtime suspend? Although it is possible to derive this from the source code, please explain this in the patch description.
What does the acronym SSU stand for? This doesn't seem like a commonly used kernel acronym to me so please expand that acronym.
Quoted 5 lines
Fix this by registering ufs device wlun as a scsi driver and registering it for block runtime-pm. Also make this as a supplier for all other luns. That way, this device wlun suspends after all the consumers and resumes after hba resumes.
That's an interesting solution.
drivers/scsi/ufs/ufs-debugfs.c void __init ufs_debugfs_init(void) · line 16
Quoted 2 lines
drivers/scsi/ufs/ufs-debugfs.c +1
-1-void __exit ufs_debugfs_exit(void) +void ufs_debugfs_exit (void )
Is the above change related to the rest of this patch?
drivers/scsi/ufs/ufs_bsg.c static int ufs_bsg_request(struct bsg_job *job) · line 100
Quoted 11 lines
static struct
platform_driverufs_qcom_pltform= {drivers/scsi/ufs/ufs_bsg.c +1
-1@@ -97,7 +97,7 @@ static int ufs_bsg_request(struct bsg_job *job) bsg_reply->reply_payload_rcv_len =0 ; -pm_runtime_get_sync (hba->dev); +scsi_autopm_get_device (hba->sdev_ufs_device);
Can the pm_runtime_get_sync() to scsi_autopm_get_device() changes be moved into a separate patch?
drivers/scsi/ufs/ufs_bsg.c static int ufs_bsg_request(struct bsg_job *job)
Quoted 4 lines
drivers/scsi/ufs/ufs_bsg.c +4
-0+static inline bool is_rpmb_wlun (struct scsi_device *sdev) +{ +return (sdev->lun ==ufshcd_upiu_wlun_to_scsi_wlun (UFS_UPIU_RPMB_WLUN )); +}
Has this patch been verified with checkpatch? Checkpatch should have reported the following for the above code:
return is not a function, parentheses are not required
drivers/scsi/ufs/ufs_bsg.c
Quoted 5 lines
drivers/scsi/ufs/ufs_bsg.c +5
-0+static inline bool is_device_wlun (struct scsi_device *sdev) +{ +return (sdev->lun == +ufshcd_upiu_wlun_to_scsi_wlun (UFS_UPIU_UFS_DEVICE_WLUN )); +}
Same comment here.
drivers/scsi/ufs/ufs_bsg.c
Quoted 6 lines
drivers/scsi/ufs/ufs_bsg.c +1
-1/* -* Don't assume anything of pm_runtime_get_sync(), if +* Don't assume anything of resume, if * resume fails, irq and clocks can be OFF, and powers * can be OFF or in LPM. */
Please make better use of the horizontal space in the above comment by making comment lines longer.
Thanks,
Bart.
_______________________________________________
Linux-mediatek mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-mediatek
Re: [PATCH v12 1/2] scsi: ufs: Enable power management for wlun
Quoted 11 lines · Bart Van Assche
Quoted 9 lines · Asutosh Das
During runtime-suspend of ufs host, the scsi devices are already suspended and so are the queues associated with them. But the ufs host sends SSU to wlun during its runtime-suspend. During the process
blk_queue_enterchecks if the queue is not in suspended state. If so, it waits for the queue to resume, and never comes out of it.
The commit
(d55d15a33: scsi: block: Do not accept any requests while suspended) adds the check if the queue is in suspended state inblk_queue_enter().
Hi Bart,
Thanks for the review comments.
Quoted 5 lines
What is the role of the WLUN during runtime suspend and why does a command need to be sent to the WLUN during runtime suspend? Although it is possible to derive this from the source code, please explain this in the patch description.
Ok. Will explain it in the next version.
Quoted 3 lines
What does the acronym SSU stand for? This doesn't seem like a commonly used kernel acronym to me so please expand that acronym.
START STOP UNIT.
Anyway, I'll expand it in the next version.
Quoted 6 lines
Quoted 5 lines
Fix this by registering ufs device wlun as a scsi driver and registering it for block runtime-pm. Also make this as a supplier for all other luns. That way, this device wlun suspends after all the consumers and resumes after hba resumes.
Quoted 7 lines
That's an interesting solution.
drivers/scsi/ufs/ufs-debugfs.c void __init ufs_debugfs_init(void) · line 16
Quoted 2 lines
drivers/scsi/ufs/ufs-debugfs.c +1
-1-void __exit ufs_debugfs_exit(void) +void ufs_debugfs_exit (void )Is the above change related to the rest of this patch?
Yes, it's used to handle an error in ufshcd_core_init() function.
Quoted 7 lines
Quoted 7 lines
static struct platform_driver ufs_qcom_pltform = {drivers/scsi/ufs/ufs_bsg.c +0
-0@@ -97,7 +97,7 @@ static int ufs_bsg_request(struct bsg_job *job)
Quoted 8 lines
drivers/scsi/ufs/ufs_bsg.c static int ufs_bsg_request(struct bsg_job *job) · line 100
Quoted 4 lines
drivers/scsi/ufs/ufs_bsg.c +1
-1bsg_reply->reply_payload_rcv_len =0 ; -pm_runtime_get_sync (hba->dev); +scsi_autopm_get_device (hba->sdev_ufs_device);Can the
pm_runtime_get_sync()toscsi_autopm_get_device()changes be moved into a separate patch?
I guess so. But then this patch would have issues when used independently.
Quoted 10 lines
drivers/scsi/ufs/ufs_bsg.c static int ufs_bsg_request(struct bsg_job *job)
Quoted 4 lines
drivers/scsi/ufs/ufs_bsg.c +4
-0+static inline bool is_rpmb_wlun (struct scsi_device *sdev) +{ +return (sdev->lun ==ufshcd_upiu_wlun_to_scsi_wlun (UFS_UPIU_RPMB_WLUN )); +}Has this patch been verified with checkpatch? Checkpatch should have reported the following for the above code:
return is not a function, parentheses are not required
Yes, it has been verified. But I didn't see any error reports. Below is the o/p of checkpatch:
$ ./scripts/checkpatch.pl /tmp/up/ufs-pm-v12/* ------------------------------------------ /tmp/up/ufs-pm-v12/0000-cover-letter.patch ------------------------------------------ WARNING: Possible unwrapped commit description (prefer a maximum 75 chars per line) #107: Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
total: 0 errors, 1 warnings, 0 lines checked
NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or
--fix-inplace.
/tmp/up/ufs-pm-v12/0000-cover-letter.patch has style problems, please review.
-----------------------------------------------------------------------/tmp/up/ufs-pm-v12/0001-scsi-ufs-Enable-power-management-for-wlun.patch
-----------------------------------------------------------------------
total: 0 errors, 0 warnings, 1180 lines checked
/tmp/up/ufs-pm-v12/0001-scsi-ufs-Enable-power-management-for-wlun.patch has no obvious style problems and is ready for submission.
---------------------------------------------------------------------/tmp/up/ufs-pm-v12/0002-ufs-sysfs-Resume-the-proper-scsi-device.patch
---------------------------------------------------------------------
total: 0 errors, 0 warnings, 91 lines checked
/tmp/up/ufs-pm-v12/0002-ufs-sysfs-Resume-the-proper-scsi-device.patch has no obvious style problems and is ready for submission.
NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.
Quoted 8 lines
drivers/scsi/ufs/ufs_bsg.c
Quoted 5 lines
drivers/scsi/ufs/ufs_bsg.c +5
-0+static inline bool is_device_wlun (struct scsi_device *sdev) +{ +return (sdev->lun == +ufshcd_upiu_wlun_to_scsi_wlun (UFS_UPIU_UFS_DEVICE_WLUN )); +}Same comment here.
Quoted 10 lines
drivers/scsi/ufs/ufs_bsg.c
Quoted 6 lines
/*
drivers/scsi/ufs/ufs_bsg.c +1
-1-* Don't assume anything of pm_runtime_get_sync(), if +* Don't assume anything of resume, if * resume fails, irq and clocks can be OFF, and powers * can be OFF or in LPM. */ Please make better use of the horizontal space in the above comment by making comment lines longer.
Ok Sure.
Quoted 4 lines
Thanks,
Bart.
-asd
Signature · 7 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, Linux Foundation Collaborative Project _______________________________________________ Linux-mediatek mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-mediatek
Re: [PATCH v12 1/2] scsi: ufs: Enable power management for wlun
In reply to Asutosh DasQuoted 38 lines · Asutosh Das
During runtime-suspend of ufs host, the scsi devices are already suspended and so are the queues associated with them. But the ufs host sends SSU to wlun during its runtime-suspend. During the process
blk_queue_enterchecks if the queue is not in suspended state. If so, it waits for the queue to resume, and never comes out of it.
The commit
(d55d15a33: scsi: block: Do not accept any requests while suspended) adds the check if the queue is in suspended state inblk_queue_enter().Call trace: __switch_to+0x174/0x2c4 __schedule+0x478/0x764 schedule+0x9c/0xe0 blk_queue_enter+0x158/0x228 blk_mq_alloc_request+0x40/0xa4 blk_get_request+0x2c/0x70 __scsi_execute+0x60/0x1c4 ufshcd_set_dev_pwr_mode+0x124/0x1e4 ufshcd_suspend+0x208/0x83c ufshcd_runtime_suspend+0x40/0x154 ufshcd_pltfrm_runtime_suspend+0x14/0x20 pm_generic_runtime_suspend+0x28/0x3c __rpm_callback+0x80/0x2a4 rpm_suspend+0x308/0x614 rpm_idle+0x158/0x228 pm_runtime_work+0x84/0xac process_one_work+0x1f0/0x470 worker_thread+0x26c/0x4c8 kthread+0x13c/0x320 ret_from_fork+0x10/0x18Fix this by registering ufs device wlun as a scsi driver and registering it for block runtime-pm. Also make this as a supplier for all other luns. That way, this device wlun suspends after all the consumers and resumes after hba resumes.
Quoted 3 lines
Co-developed-by Can Guo <[email protected]>Signed-off-by Can Guo <[email protected]>Signed-off-by Asutosh Das <[email protected]>
I have some more comments that may help straighten things out.
Also please look at ufs_debugfs_get_user_access() and ufs_debugfs_put_user_access() that now need to scsi_autopm_get/put_device sdev_ufs_device.
It would also be good if you could re-base on linux-next.
Quoted 314 lines
--- drivers/scsi/ufs/cdns-pltfrm.c | 2 + drivers/scsi/ufs/tc-dwc-g210-pci.c | 2 + drivers/scsi/ufs/ufs-debugfs.c | 2 +- drivers/scsi/ufs/ufs-debugfs.h | 2 +- drivers/scsi/ufs/ufs-exynos.c | 2 + drivers/scsi/ufs/ufs-hisi.c | 2 + drivers/scsi/ufs/ufs-mediatek.c | 12 +- drivers/scsi/ufs/ufs-qcom.c | 2 + drivers/scsi/ufs/ufs_bsg.c | 6 +- drivers/scsi/ufs/ufshcd-pci.c | 36 +-- drivers/scsi/ufs/ufshcd.c | 622 ++++++++++++++++++++++++++----------- drivers/scsi/ufs/ufshcd.h | 6 + include/trace/events/ufs.h | 20 ++ 13 files changed, 491 insertions(+), 225 deletions(-)drivers/scsi/ufs/cdns-pltfrm.c +2
-0@@ -319,6 +319,8 @@ static const struct dev_pm_ops cdns_ufs_dev_pm_ops = { .runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_idle = ufshcd_pltfrm_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, };static struct platform_driver cdns_ufs_pltfrm_driver = {drivers/scsi/ufs/tc-dwc-g210-pci.c +2
-0@@ -148,6 +148,8 @@ static const struct dev_pm_ops tc_dwc_g210_pci_pm_ops = { .runtime_suspend = tc_dwc_g210_pci_runtime_suspend, .runtime_resume = tc_dwc_g210_pci_runtime_resume, .runtime_idle = tc_dwc_g210_pci_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, };static const struct pci_device_id tc_dwc_g210_pci_tbl[] = {drivers/scsi/ufs/ufs-debugfs.c +1
-1@@ -13,7 +13,7 @@ void __init ufs_debugfs_init(void) ufs_debugfs_root =debugfs_create_dir ("ufshcd" ,NULL ); } -void __exit ufs_debugfs_exit(void) +void ufs_debugfs_exit (void ) {debugfs_remove_recursive (ufs_debugfs_root); }drivers/scsi/ufs/ufs-debugfs.h +1
-1@@ -9,7 +9,7 @@ struct ufs_hba;#ifdef CONFIG_DEBUG_FS void __init ufs_debugfs_init (void ); -void __exit ufs_debugfs_exit(void); +void ufs_debugfs_exit (void );void ufs_debugfs_hba_init (struct ufs_hba *hba);void ufs_debugfs_hba_exit (struct ufs_hba *hba);#else drivers/scsi/ufs/ufs-exynos.c +2
-0@@ -1268,6 +1268,8 @@ static const struct dev_pm_ops exynos_ufs_pm_ops = { .runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_idle = ufshcd_pltfrm_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, };static struct platform_driver exynos_ufs_pltform = {drivers/scsi/ufs/ufs-hisi.c +2
-0@@ -574,6 +574,8 @@ static const struct dev_pm_ops ufs_hisi_pm_ops = { .runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_idle = ufshcd_pltfrm_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, };static struct platform_driver ufs_hisi_pltform = {drivers/scsi/ufs/ufs-mediatek.c +6
-6@@ -810,12 +810,10 @@ static int ufs_mtk_post_link(struct ufs_hba *hba)/* enable unipro clock gating feature */ ufs_mtk_cfg_unipro_cg (hba, true); -/* configure auto-hibern8 timer to 10ms */ -if (ufshcd_is_auto_hibern8_supported (hba)) { -ufshcd_auto_hibern8_update (hba, -FIELD_PREP (UFSHCI_AHIBERN8_TIMER_MASK ,10 ) | -FIELD_PREP (UFSHCI_AHIBERN8_SCALE_MASK ,3 )); - } +/* will be configured during probe hba */ +if (ufshcd_is_auto_hibern8_supported (hba)) + hba->ahit =FIELD_PREP (UFSHCI_AHIBERN8_TIMER_MASK ,10 ) | +FIELD_PREP (UFSHCI_AHIBERN8_SCALE_MASK ,3 );ufs_mtk_setup_clk_gating (hba);@@ -1097,6 +1095,8 @@ static const struct dev_pm_ops ufs_mtk_pm_ops = { .runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_idle = ufshcd_pltfrm_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, };static struct platform_driver ufs_mtk_pltform = {drivers/scsi/ufs/ufs-qcom.c +2
-0@@ -1546,6 +1546,8 @@ static const struct dev_pm_ops ufs_qcom_pm_ops = { .runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_idle = ufshcd_pltfrm_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, };static struct platform_driver ufs_qcom_pltform = {drivers/scsi/ufs/ufs_bsg.c +3
-3@@ -97,7 +97,7 @@ static int ufs_bsg_request(struct bsg_job *job) bsg_reply->reply_payload_rcv_len =0 ; -pm_runtime_get_sync (hba->dev); +scsi_autopm_get_device (hba->sdev_ufs_device); msgcode = bsg_request->msgcode;switch (msgcode) {@@ -106,7 +106,7 @@ static int ufs_bsg_request(struct bsg_job *job) ret =ufs_bsg_alloc_desc_buffer (hba, job, &desc_buff, &desc_len, desc_op);if (ret) { -pm_runtime_put_sync (hba->dev); +scsi_autopm_put_device (hba->sdev_ufs_device); goto out; }@@ -138,7 +138,7 @@ static int ufs_bsg_request(struct bsg_job *job)break ; } -pm_runtime_put_sync (hba->dev); +scsi_autopm_put_device (hba->sdev_ufs_device);if (!desc_buff) goto out;drivers/scsi/ufs/ufshcd-pci.c +5
-31@@ -247,29 +247,6 @@ static int ufshcd_pci_resume(struct device *dev) return ufshcd_system_resume(dev_get_drvdata(dev)); } -/** -* ufshcd_pci_poweroff - suspend-to-disk poweroff function -* @dev: pointer to PCI device handle -* -* Returns 0 if successful -* Returns non-zero otherwise -*/ -static int ufshcd_pci_poweroff (struct device *dev) -{ -struct ufs_hba *hba =dev_get_drvdata (dev); -int spm_lvl = hba->spm_lvl; -int ret; - -/* -* For poweroff we need to set the UFS device to PowerDown mode. -* Force spm_lvl to ensure that. -*/ - hba->spm_lvl =5 ; - ret =ufshcd_system_suspend (hba); - hba->spm_lvl = spm_lvl; -return ret; -} -#endif /* !CONFIG_PM_SLEEP */ #ifdef CONFIG_PM @@ -365,17 +342,14 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) }static const struct dev_pm_ops ufshcd_pci_pm_ops = { -#ifdef CONFIG_PM_SLEEP - .suspend = ufshcd_pci_suspend, - .resume = ufshcd_pci_resume, - .freeze = ufshcd_pci_suspend, - .thaw = ufshcd_pci_resume, - .poweroff = ufshcd_pci_poweroff, - .restore = ufshcd_pci_resume, -#endif SET_RUNTIME_PM_OPS(ufshcd_pci_runtime_suspend, ufshcd_pci_runtime_resume, ufshcd_pci_runtime_idle) + SET_SYSTEM_SLEEP_PM_OPS(ufshcd_pci_suspend, ufshcd_pci_resume) +#ifdef CONFIG_PM_SLEEP + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, +#endif };static const struct pci_device_id ufshcd_pci_tbl[] = {drivers/scsi/ufs/ufshcd.c +32
-4@@ -16,6 +16,7 @@#include <linux/bitfield.h> #include <linux/blk-pm.h> #include <linux/blkdev.h> +#include <scsi/scsi_driver.h> #include "ufshcd.h" #include "ufs_quirks.h" #include "unipro.h" @@ -78,6 +79,8 @@/* Polling time to wait for fDeviceInit */ #define FDEVICEINIT_COMPL_TIMEOUT 1500/* millisecs */ +#define wlun_dev_to_hba (dv) shost_priv(to_scsi_device(dv)->host) +#define ufshcd_toggle_vreg (_dev, _vreg, _on) \ ({ \ int _ret; \@@ -1556,7 +1559,7 @@ static ssize_t ufshcd_clkscale_enable_store(struct device *dev,if (value == hba->clk_scaling.is_enabled) goto out; -pm_runtime_get_sync (hba->dev); +scsi_autopm_get_device (hba->sdev_ufs_device);ufshcd_hold (hba, false); hba->clk_scaling.is_enabled = value;@@ -1572,7 +1575,7 @@ static ssize_t ufshcd_clkscale_enable_store(struct device *dev, }ufshcd_release (hba); -pm_runtime_put_sync (hba->dev); +scsi_autopm_put_device (hba->sdev_ufs_device); out:up (&hba->host_sem);return err ? err : count;@@ -2572,6 +2575,17 @@ static inline u16 ufshcd_upiu_wlun_to_scsi_wlun(u8 upiu_wlun_id)return (upiu_wlun_id & ~UFS_UPIU_WLUN_ID ) |SCSI_W_LUN_BASE ; } +static inline bool is_rpmb_wlun (struct scsi_device *sdev) +{ +return (sdev->lun ==ufshcd_upiu_wlun_to_scsi_wlun (UFS_UPIU_RPMB_WLUN )); +} + +static inline bool is_device_wlun (struct scsi_device *sdev) +{ +return (sdev->lun == +ufshcd_upiu_wlun_to_scsi_wlun (UFS_UPIU_UFS_DEVICE_WLUN )); +} +static void ufshcd_init_lrb (struct ufs_hba *hba,struct ufshcd_lrb *lrb,int i) {struct utp_transfer_cmd_desc *cmd_descp = hba->ucdl_base_addr;@@ -4106,11 +4120,11 @@ void ufshcd_auto_hibern8_update(struct ufs_hba *hba, u32 ahit)spin_unlock_irqrestore (hba->host->host_lock, flags);if (update && !pm_runtime_suspended (hba->dev)) { -pm_runtime_get_sync (hba->dev); +scsi_autopm_get_device (hba->sdev_ufs_device);ufshcd_hold (hba, false);ufshcd_auto_hibern8_enable (hba);ufshcd_release (hba); -pm_runtime_put (hba->dev); +scsi_autopm_put_device (hba->sdev_ufs_device); } }EXPORT_SYMBOL_GPL (ufshcd_auto_hibern8_update);@@ -4808,6 +4822,41 @@ static inline void ufshcd_get_lu_power_on_wp_status(struct ufs_hba *hba, }/** +* ufshcd_setup_links - associate link b/w device wlun and other luns +* @sdev: pointer to SCSI device +* @hba: pointer to ufs hba +*/ +static void ufshcd_setup_links (struct ufs_hba *hba,struct scsi_device *sdev) +{ +struct device_link *link; + +/* +* device wlun is the supplier & rest of the luns are consumers +* This ensures that device wlun suspends after all other luns. +*/ +if (hba->sdev_ufs_device) { + link = device_link_add(&sdev->sdev_gendev,
drivers/scsi/ufs/ufshcd.c static inline void ufshcd_get_lu_power_on_wp_status(struct ufs_hba *hba, · line 4848
Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10
-0+ &hba->sdev_ufs_device->sdev_gendev, + DL_FLAG_PM_RUNTIME|DL_FLAG_RPM_ACTIVE); +if (!link) { +dev_err (&sdev->sdev_gendev,"Failed establishing link - %s\n" , +dev_name (&hba->sdev_ufs_device->sdev_gendev)); +return ; + } + hba->luns_avail--; +/* Ignore REPORT_LUN wlun probing */ +if (hba->luns_avail !=1 ) {
The condition here is the wrong way around. i.e. should be
if (hba->luns_avail == 1) {drivers/scsi/ufs/ufshcd.c static inline void ufshcd_get_lu_power_on_wp_status(struct ufs_hba *hba, · line 4849
Quoted 1 line
drivers/scsi/ufs/ufshcd.c +1
-0+pm_runtime_put_noidle (&hba->sdev_ufs_device->sdev_gendev);
If the usage_count is zero here, then we should let it suspend i.e. make this:
pm_runtime_put(&hba->sdev_ufs_device->sdev_gendev);
Note this pairs with the pm_runtime_get_noresume() that needs to be added to ufshcd_slave_configure. Please see comments for ufshcd_slave_configure().
drivers/scsi/ufs/ufshcd.c static inline void ufshcd_get_lu_power_on_wp_status(struct ufs_hba *hba, · line 4850
Quoted 1 line
drivers/scsi/ufs/ufshcd.c +1
-0+pm_runtime_allow (&hba->sdev_ufs_device->sdev_gendev);
This pm_runtime_allow should not be here. It should be in ufshcd_wl_probe(). Please see comments for ufshcd_wl_probe().
Quoted 23 lines
drivers/scsi/ufs/ufshcd.c +11
-0+return ; + } + }else { +/* device wlun is probed */ + hba->luns_avail--; + } +} + +/** * ufshcd_slave_alloc - handle initial SCSI device configurations * @sdev: pointer to SCSI device * @@ -4838,6 +4887,8 @@ static int ufshcd_slave_alloc(struct scsi_device *sdev)ufshcd_get_lu_power_on_wp_status (hba, sdev); +ufshcd_setup_links (hba, sdev); +return 0 ; }@@ -4869,8 +4920,13 @@ static int ufshcd_slave_configure(struct scsi_device *sdev)blk_queue_update_dma_pad (q,PRDT_DATA_BYTE_COUNT_PAD -1 );
drivers/scsi/ufs/ufshcd.c static int ufshcd_slave_configure(struct scsi_device *sdev) · line 4928
Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +6
-2if (hba->quirks &UFSHCD_QUIRK_ALIGN_SG_WITH_PAGE_SIZE )blk_queue_update_dma_alignment (q,PAGE_SIZE -1 ); - -if (ufshcd_is_rpm_autosuspend_allowed (hba)) +/* +* Block runtime-pm until all consumers are added. +* Refer ufshcd_setup_links(). +*/ +if (is_device_wlun (sdev)) +pm_runtime_forbid (&sdev->sdev_gendev);
Since rpm_autosuspend is not being set for sdev_ufs_device, scsi_sysfs_add_sdev() will pm_runtime_forbid() it anyway. We should increase the usage_count instead i.e.
pm_runtime_get_noresume(&sdev->sdev_gendev);
Quoted 119 lines
drivers/scsi/ufs/ufshcd.c +19
-31+else if (ufshcd_is_rpm_autosuspend_allowed (hba)) sdev->rpm_autosuspend =1 ;ufshcd_crypto_setup_rq_keyslot_manager (hba, q);@@ -4985,15 +5041,9 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)* UFS device needs urgent BKOPs. */ if (!hba->pm_op_in_progress && -ufshcd_is_exception_event (lrbp->ucd_rsp_ptr) && -schedule_work (&hba->eeh_work)) { -/* -* Prevent suspend once eeh_work is scheduled -* to avoid deadlock between ufshcd_suspend -* and exception event handler. -*/ -pm_runtime_get_noresume (hba->dev); - } +ufshcd_is_exception_event (lrbp->ucd_rsp_ptr)) +/* Flushed in suspend */ +schedule_work (&hba->eeh_work);break ;case UPIU_TRANSACTION_REJECT_UPIU :/* TODO: handle Reject UPIU Response */ @@ -5589,8 +5639,8 @@ static void ufshcd_rpm_dev_flush_recheck_work(struct work_struct *work)* after a certain delay to recheck the threshold by next runtime * suspend. */ -pm_runtime_get_sync (hba->dev); -pm_runtime_put_sync (hba->dev); +scsi_autopm_get_device (hba->sdev_ufs_device); +scsi_autopm_put_device (hba->sdev_ufs_device); }/** @@ -5607,7 +5657,6 @@ static void ufshcd_exception_event_handler(struct work_struct *work)u32 status =0 ; hba = container_of(work, struct ufs_hba, eeh_work); -pm_runtime_get_sync (hba->dev);ufshcd_scsi_block_requests (hba); err =ufshcd_get_ee_status (hba, &status);if (err) {@@ -5623,14 +5672,6 @@ static void ufshcd_exception_event_handler(struct work_struct *work) out:ufshcd_scsi_unblock_requests (hba); -/* -* pm_runtime_get_noresume is called while scheduling -* eeh_work to avoid suspend racing with exception work. -* Hence decrement usage counter using pm_runtime_put_noidle -* to allow suspend on completion of exception event handler. -*/ -pm_runtime_put_noidle (hba->dev); -pm_runtime_put (hba->dev);return ; }@@ -5755,12 +5796,13 @@ static void ufshcd_clk_scaling_suspend(struct ufs_hba *hba, bool suspend)static void ufshcd_err_handling_prepare (struct ufs_hba *hba) { -pm_runtime_get_sync (hba->dev); -if (pm_runtime_status_suspended (hba->dev) || hba->is_sys_suspended) { +scsi_autopm_get_device (hba->sdev_ufs_device); +if (pm_runtime_status_suspended (&hba->sdev_ufs_device->sdev_gendev) || + hba->is_sys_suspended) {enum ufs_pm_op pm_op;/* -* Don't assume anything of pm_runtime_get_sync(), if +* Don't assume anything of resume, if * resume fails, irq and clocks can be OFF, and powers * can be OFF or in LPM. */ @@ -5789,7 +5831,7 @@ static void ufshcd_err_handling_unprepare(struct ufs_hba *hba)ufshcd_release (hba);if (ufshcd_is_clkscaling_supported (hba))ufshcd_clk_scaling_suspend (hba, false); -pm_runtime_put (hba->dev); +pm_runtime_put (&hba->sdev_ufs_device->sdev_gendev); }static inline bool ufshcd_err_handling_should_stop (struct ufs_hba *hba)@@ -5810,14 +5852,14 @@ static void ufshcd_recover_pm_error(struct ufs_hba *hba) hba->is_sys_suspended = false;/* -* Set RPM status of hba device to RPM_ACTIVE, +* Set RPM status of wlun device to RPM_ACTIVE, * this also clears its runtime error. */ - ret =pm_runtime_set_active (hba->dev); + ret =pm_runtime_set_active (&hba->sdev_ufs_device->sdev_gendev);/* -* If hba device had runtime error, we also need to resume those -* scsi devices under hba in case any of them has failed to be -* resumed due to hba runtime resume failure. This is to unblock +* If wlun device had runtime error, we also need to resume those +* consumer scsi devices in case any of them has failed to be +* resumed due to supplier runtime resume failure. This is to unblock * blk_queue_enter in case there are bios waiting inside it. */ if (!ret) {@@ -7207,11 +7249,12 @@ static void ufshcd_set_active_icc_lvl(struct ufs_hba *hba)static inline void ufshcd_blk_pm_runtime_init (struct scsi_device *sdev) { +int dly =is_device_wlun (sdev) ?0 :RPM_AUTOSUSPEND_DELAY_MS ; +scsi_autopm_get_device (sdev);blk_pm_runtime_init (sdev->request_queue, &sdev->sdev_gendev);if (sdev->rpm_autosuspend) -pm_runtime_set_autosuspend_delay (&sdev->sdev_gendev, -RPM_AUTOSUSPEND_DELAY_MS ); +pm_runtime_set_autosuspend_delay (&sdev->sdev_gendev, dly);scsi_autopm_put_device (sdev); }
drivers/scsi/ufs/ufshcd.c static int ufshcd_scsi_add_wlus(struct ufs_hba *hba) · line 7300
Quoted 5 lines
drivers/scsi/ufs/ufshcd.c +1
-0@@ -7253,6 +7296,7 @@ static int ufshcd_scsi_add_wlus(struct ufs_hba *hba) hba->sdev_ufs_device =NULL ; goto out; } +scsi_autopm_get_device (hba->sdev_ufs_device);ufshcd_blk_pm_runtime_init (hba->sdev_ufs_device);
Let's drop both scsi_autopm_get_device() and ufshcd_blk_pm_runtime_init() here and instead do things in ufshcd_wl_probe() - see comments for ufshcd_wl_probe()
Quoted 626 lines
drivers/scsi/ufs/ufshcd.c +238
-146scsi_device_put (hba->sdev_ufs_device);@@ -7417,6 +7461,9 @@ static int ufs_get_device_desc(struct ufs_hba *hba) goto out; } + hba->luns_avail = desc_buf[DEVICE_DESC_PARAM_NUM_LU ] + + desc_buf[DEVICE_DESC_PARAM_NUM_WLU ]; +ufs_fixup_device_setup (hba);ufshcd_wb_probe (hba, desc_buf);@@ -7892,6 +7939,7 @@ static int ufshcd_probe_hba(struct ufs_hba *hba, bool async)ufshcd_set_ufs_dev_active (hba);ufshcd_force_reset_auto_bkops (hba); hba->wlun_dev_clr_ua = true; + hba->wlun_rpmb_clr_ua = true;/* Gear up to HS gear if supported */ if (hba->max_pwr_info.is_valid) {@@ -8475,7 +8523,8 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba,* handling context. */ hba->host->eh_noresume =1 ; -ufshcd_clear_ua_wluns (hba); +if (hba->wlun_dev_clr_ua) +ufshcd_clear_ua_wlun (hba,UFS_UPIU_UFS_DEVICE_WLUN ); cmd[4 ] = pwr_mode <<4 ;@@ -8650,23 +8699,7 @@ static void ufshcd_hba_vreg_set_hpm(struct ufs_hba *hba)ufshcd_setup_hba_vreg (hba, true); } -/** -* ufshcd_suspend - helper function for suspend operations -* @hba: per adapter instance -* @pm_op: desired low power operation type -* -* This function will try to put the UFS device and link into low power -* mode based on the "rpm_lvl" (Runtime PM level) or "spm_lvl" -* (System PM level). -* -* If this function is called during shutdown, it will make sure that -* both UFS device and UFS link is powered off. -* -* NOTE: UFS device & link must be active before we enter in this function. -* -* Returns 0 for success and non-zero for failure -*/ -static int ufshcd_suspend (struct ufs_hba *hba,enum ufs_pm_op pm_op) +static int __ufshcd_wl_suspend (struct ufs_hba *hba,enum ufs_pm_op pm_op) {int ret =0 ;int check_for_bkops;@@ -8674,7 +8707,7 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)enum ufs_dev_pwr_mode req_dev_pwr_mode;enum uic_link_state req_link_state; - hba->pm_op_in_progress =1 ; + hba->pm_op_in_progress = true;if (!ufshcd_is_shutdown_pm (pm_op)) { pm_lvl =ufshcd_is_runtime_pm (pm_op) ? hba->rpm_lvl : hba->spm_lvl;@@ -8697,17 +8730,17 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)if (req_dev_pwr_mode ==UFS_ACTIVE_PWR_MODE && req_link_state ==UIC_LINK_ACTIVE_STATE ) { - goto disable_clks; + goto enable_scaling; }if ((req_dev_pwr_mode == hba->curr_dev_pwr_mode) && (req_link_state == hba->uic_link_state)) - goto enable_gating; + goto enable_scaling;/* UFS device & link must be active before we enter in this function */ if (!ufshcd_is_ufs_dev_active (hba) || !ufshcd_is_link_active (hba)) { ret = -EINVAL ; - goto enable_gating; + goto enable_scaling; }if (ufshcd_is_runtime_pm (pm_op)) {@@ -8719,7 +8752,7 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)*/ ret =ufshcd_urgent_bkops (hba);if (ret) - goto enable_gating; + goto enable_scaling; }else {/* make sure that auto bkops is disabled */ ufshcd_disable_auto_bkops (hba);@@ -8747,7 +8780,7 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)if (!hba->dev_info.b_rpm_dev_flush_capable) { ret =ufshcd_set_dev_pwr_mode (hba, req_dev_pwr_mode);if (ret) - goto enable_gating; + goto enable_scaling; } }@@ -8760,7 +8793,6 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)if (ret) goto set_dev_active; -disable_clks:/* * Call vendor specific suspend callback. As these callbacks may access * vendor specific host controller register space call them before the @@ -8769,28 +8801,9 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) ret =ufshcd_vops_suspend (hba, pm_op);if (ret) goto set_link_active; -/* -* Disable the host irq as host controller as there won't be any -* host controller transaction expected till resume. -*/ -ufshcd_disable_irq (hba); - -ufshcd_setup_clocks (hba, false); - -if (ufshcd_is_clkgating_allowed (hba)) { - hba->clk_gating.state =CLKS_OFF ; -trace_ufshcd_clk_gating (dev_name (hba->dev), - hba->clk_gating.state); - } - -ufshcd_vreg_set_lpm (hba); - -/* Put the host controller in low power mode if possible */ -ufshcd_hba_vreg_set_lpm (hba); goto out; set_link_active: -ufshcd_vreg_set_hpm (hba);/* * Device hardware reset is required to exit DeepSleep. Also, for * DeepSleep, the link is off so host reset and restore will be done @@ -8812,57 +8825,32 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) }if (!ufshcd_set_dev_pwr_mode (hba,UFS_ACTIVE_PWR_MODE ))ufshcd_disable_auto_bkops (hba); -enable_gating: +enable_scaling:if (ufshcd_is_clkscaling_supported (hba))ufshcd_clk_scaling_suspend (hba, false); - hba->clk_gating.is_suspended = false; hba->dev_info.b_rpm_dev_flush_capable = false; -ufshcd_clear_ua_wluns (hba); -ufshcd_release (hba); out:if (hba->dev_info.b_rpm_dev_flush_capable) {schedule_delayed_work (&hba->rpm_dev_flush_recheck_work,msecs_to_jiffies (RPM_DEV_FLUSH_RECHECK_WORK_DELAY_MS )); } - hba->pm_op_in_progress =0 ; - -if (ret) -ufshcd_update_evt_hist (hba,UFS_EVT_SUSPEND_ERR , (u32 )ret); +if (ret) { +ufshcd_update_evt_hist (hba,UFS_EVT_WL_SUSP_ERR , (u32 )ret); + hba->clk_gating.is_suspended = false; +ufshcd_release (hba); + } + hba->pm_op_in_progress = false;return ret; } -/** -* ufshcd_resume - helper function for resume operations -* @hba: per adapter instance -* @pm_op: runtime PM or system PM -* -* This function basically brings the UFS device, UniPro link and controller -* to active state. -* -* Returns 0 for success and non-zero for failure -*/ -static int ufshcd_resume (struct ufs_hba *hba,enum ufs_pm_op pm_op) +static int __ufshcd_wl_resume (struct ufs_hba *hba,enum ufs_pm_op pm_op) {int ret; -enum uic_link_state old_link_state; +enum uic_link_state old_link_state = hba->uic_link_state; - hba->pm_op_in_progress =1 ; - old_link_state = hba->uic_link_state; - -ufshcd_hba_vreg_set_hpm (hba); - ret =ufshcd_vreg_set_hpm (hba); -if (ret) - goto out; - -/* Make sure clocks are enabled before accessing controller */ - ret =ufshcd_setup_clocks (hba, true); -if (ret) - goto disable_vreg; - -/* enable the host irq as host controller would be active soon */ -ufshcd_enable_irq (hba); + hba->pm_op_in_progress = true;/* * Call vendor specific resume callback. As these callbacks may access @@ -8871,7 +8859,7 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)*/ ret =ufshcd_vops_resume (hba, pm_op);if (ret) - goto disable_irq_and_vops_clks; + goto out;/* For DeepSleep, the only supported option is to have the link off */ WARN_ON (ufshcd_is_ufs_dev_deepsleep (hba) && !ufshcd_is_link_off (hba));@@ -8916,31 +8904,147 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)*/ ufshcd_urgent_bkops (hba); - hba->clk_gating.is_suspended = false; - -if (ufshcd_is_clkscaling_supported (hba)) -ufshcd_clk_scaling_suspend (hba, false); - -/* Enable Auto-Hibernate if configured */ -ufshcd_auto_hibern8_enable (hba); +if (hba->clk_scaling.is_allowed) +ufshcd_resume_clkscaling (hba);if (hba->dev_info.b_rpm_dev_flush_capable) { hba->dev_info.b_rpm_dev_flush_capable = false;cancel_delayed_work (&hba->rpm_dev_flush_recheck_work); } -ufshcd_clear_ua_wluns (hba); - -/* Schedule clock gating in case of no access to UFS device yet */ -ufshcd_release (hba); - +/* Enable Auto-Hibernate if configured */ +ufshcd_auto_hibern8_enable (hba); goto out; set_old_link_state:ufshcd_link_state_transition (hba, old_link_state,0 ); vendor_suspend:ufshcd_vops_suspend (hba, pm_op); -disable_irq_and_vops_clks: +out: +if (ret) +ufshcd_update_evt_hist (hba,UFS_EVT_WL_RES_ERR , (u32 )ret); + hba->clk_gating.is_suspended = false; +ufshcd_release (hba); + hba->pm_op_in_progress = false; +return ret; +} + +static int ufshcd_wl_runtime_suspend (struct device *dev) +{ +struct scsi_device *sdev =to_scsi_device (dev); +struct ufs_hba *hba; +int ret; +ktime_t start =ktime_get (); + + hba =shost_priv (sdev->host); + + ret =__ufshcd_wl_suspend (hba,UFS_RUNTIME_PM ); +if (ret) +dev_err (&sdev->sdev_gendev,"%s failed: %d\n" , __func__, ret); + +trace_ufshcd_wl_runtime_suspend (dev_name (dev), ret, +ktime_to_us (ktime_sub (ktime_get (), start)), + hba->curr_dev_pwr_mode, hba->uic_link_state); + +return ret; +} + +static int ufshcd_wl_runtime_resume (struct device *dev) +{ +struct scsi_device *sdev =to_scsi_device (dev); +struct ufs_hba *hba; +int ret =0 ; +ktime_t start =ktime_get (); + + hba =shost_priv (sdev->host); + + ret =__ufshcd_wl_resume (hba,UFS_RUNTIME_PM ); +if (ret) +dev_err (&sdev->sdev_gendev,"%s failed: %d\n" , __func__, ret); + +trace_ufshcd_wl_runtime_resume (dev_name (dev), ret, +ktime_to_us (ktime_sub (ktime_get (), start)), + hba->curr_dev_pwr_mode, hba->uic_link_state); + +return ret; +} + +#ifdef CONFIG_PM_SLEEP +static int ufshcd_wl_suspend (struct device *dev) +{ +struct scsi_device *sdev =to_scsi_device (dev); +struct ufs_hba *hba; +int ret; +ktime_t start =ktime_get (); + + hba =shost_priv (sdev->host); + ret =__ufshcd_wl_suspend (hba,UFS_SYSTEM_PM ); +if (ret) +dev_err (&sdev->sdev_gendev,"%s failed: %d\n" , __func__, ret); + +trace_ufshcd_wl_suspend (dev_name (dev), ret, +ktime_to_us (ktime_sub (ktime_get (), start)), + hba->curr_dev_pwr_mode, hba->uic_link_state); + +return ret; +} + +static int ufshcd_wl_resume (struct device *dev) +{ +struct scsi_device *sdev =to_scsi_device (dev); +struct ufs_hba *hba; +int ret =0 ; +ktime_t start =ktime_get (); + +if (pm_runtime_suspended (dev)) +return 0 ; + hba =shost_priv (sdev->host); + + ret =__ufshcd_wl_resume (hba,UFS_SYSTEM_PM ); +if (ret) +dev_err (&sdev->sdev_gendev,"%s failed: %d\n" , __func__, ret); + +trace_ufshcd_wl_resume (dev_name (dev), ret, +ktime_to_us (ktime_sub (ktime_get (), start)), + hba->curr_dev_pwr_mode, hba->uic_link_state); + +return ret; +} +#endif + +static void ufshcd_wl_shutdown (struct device *dev) +{ +struct scsi_device *sdev =to_scsi_device (dev); +struct ufs_hba *hba; + + hba =shost_priv (sdev->host); +/* Turn on everything while shutting down */ +scsi_autopm_get_device (sdev); +scsi_device_quiesce (sdev); +shost_for_each_device (sdev, hba->host) { +if (sdev == hba->sdev_ufs_device) +continue ; +scsi_device_quiesce (sdev); + } +__ufshcd_wl_suspend (hba,UFS_SHUTDOWN_PM ); +} + +/** +* ufshcd_suspend - helper function for suspend operations +* @hba: per adapter instance +* +* This function will put disable irqs, turn off clocks +* and set vreg and hba-vreg in lpm mode. +* Also check the description of __ufshcd_wl_suspend(). +*/ +static void ufshcd_suspend (struct ufs_hba *hba) +{ + hba->pm_op_in_progress =1 ; + +/* +* Disable the host irq as host controller as there won't be any +* host controller transaction expected till resume. +*/ ufshcd_disable_irq (hba);ufshcd_setup_clocks (hba, false);if (ufshcd_is_clkgating_allowed (hba)) {@@ -8948,6 +9052,43 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)trace_ufshcd_clk_gating (dev_name (hba->dev), hba->clk_gating.state); } + +ufshcd_vreg_set_lpm (hba); +/* Put the host controller in low power mode if possible */ +ufshcd_hba_vreg_set_lpm (hba); + hba->pm_op_in_progress =0 ; +} + +/** +* ufshcd_resume - helper function for resume operations +* @hba: per adapter instance +* +* This function basically turns on the regulators, clocks and +* irqs of the hba. +* Also check the description of __ufshcd_wl_resume(). +* +* Returns 0 for success and non-zero for failure +*/ +static int ufshcd_resume (struct ufs_hba *hba) +{ +int ret; + + hba->pm_op_in_progress =1 ; + +ufshcd_hba_vreg_set_hpm (hba); + ret =ufshcd_vreg_set_hpm (hba); +if (ret) + goto out; + +/* Make sure clocks are enabled before accessing controller */ + ret =ufshcd_setup_clocks (hba, true); +if (ret) + goto disable_vreg; + +/* enable the host irq as host controller would be active soon */ +ufshcd_enable_irq (hba); + goto out; + disable_vreg:ufshcd_vreg_set_lpm (hba); out:@@ -8962,6 +9103,7 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)* @hba: per adapter instance * * Check the description of ufshcd_suspend() function for more details. +* Also check the description of __ufshcd_wl_suspend(). * * Returns 0 for success and non-zero for failure */ @@ -8987,21 +9129,7 @@ int ufshcd_system_suspend(struct ufs_hba *hba) !hba->dev_info.b_rpm_dev_flush_capable) goto out; -if (pm_runtime_suspended (hba->dev)) { -/* -* UFS device and/or UFS link low power states during runtime -* suspend seems to be different than what is expected during -* system suspend. Hence runtime resume the devic & link and -* let the system suspend low power states to take effect. -* TODO: If resume takes longer time, we might have optimize -* it in future by not resuming everything if possible. -*/ - ret =ufshcd_runtime_resume (hba); -if (ret) - goto out; - } - - ret =ufshcd_suspend (hba,UFS_SYSTEM_PM ); +ufshcd_suspend (hba); out: trace_ufshcd_system_suspend(dev_name(hba->dev), ret, ktime_to_us(ktime_sub(ktime_get(), start)),@@ -9023,7 +9151,6 @@ EXPORT_SYMBOL(ufshcd_system_suspend);int ufshcd_system_resume (struct ufs_hba *hba) { -int ret =0 ;ktime_t start =ktime_get ();if (!hba)@@ -9034,22 +9161,18 @@ int ufshcd_system_resume(struct ufs_hba *hba)down (&hba->host_sem); } -if (!hba->is_powered ||pm_runtime_suspended (hba->dev)) -/* -* Let the runtime resume take care of resuming -* if runtime suspended. -*/ +if (!hba->is_powered) goto out;else - ret =ufshcd_resume (hba,UFS_SYSTEM_PM ); +ufshcd_resume (hba); out: -trace_ufshcd_system_resume (dev_name (hba->dev), ret, +trace_ufshcd_system_resume (dev_name (hba->dev),0 ,ktime_to_us (ktime_sub (ktime_get (), start)), hba->curr_dev_pwr_mode, hba->uic_link_state); -if (!ret) - hba->is_sys_suspended = false; + + hba->is_sys_suspended = false;up (&hba->host_sem); -return ret; +return 0 ; }EXPORT_SYMBOL (ufshcd_system_resume);@@ -9058,12 +9181,12 @@ EXPORT_SYMBOL(ufshcd_system_resume);* @hba: per adapter instance * * Check the description of ufshcd_suspend() function for more details. +* Also check the description of __ufshcd_wl_suspend(). * * Returns 0 for success and non-zero for failure */ int ufshcd_runtime_suspend (struct ufs_hba *hba) { -int ret =0 ;ktime_t start =ktime_get ();if (!hba)@@ -9072,12 +9195,12 @@ int ufshcd_runtime_suspend(struct ufs_hba *hba)if (!hba->is_powered) goto out;else - ret =ufshcd_suspend (hba,UFS_RUNTIME_PM ); +ufshcd_suspend (hba); out: -trace_ufshcd_runtime_suspend (dev_name (hba->dev), ret, +trace_ufshcd_runtime_suspend (dev_name (hba->dev),0 ,ktime_to_us (ktime_sub (ktime_get (), start)), hba->curr_dev_pwr_mode, hba->uic_link_state); -return ret; +return 0 ; }EXPORT_SYMBOL (ufshcd_runtime_suspend);@@ -9085,26 +9208,14 @@ EXPORT_SYMBOL(ufshcd_runtime_suspend);* ufshcd_runtime_resume - runtime resume routine * @hba: per adapter instance * -* This function basically brings the UFS device, UniPro link and controller +* This function basically brings controller * to active state. Following operations are done in this function: * * 1. Turn on all the controller related clocks -* 2. Bring the UniPro link out of Hibernate state -* 3. If UFS device is in sleep state, turn ON VCC rail and bring the UFS device -* to active state. -* 4. If auto-bkops is enabled on the device, disable it. -* -* So following would be the possible power state after this function return -* successfully: -* S1: UFS device in Active state with VCC rail ON -* UniPro link in Active state -* All the UFS/UniPro controller clocks are ON -* -* Returns 0 for success and non-zero for failure +* 2. Turn ON VCC rail */ int ufshcd_runtime_resume (struct ufs_hba *hba) { -int ret =0 ;ktime_t start =ktime_get ();if (!hba)@@ -9113,12 +9224,12 @@ int ufshcd_runtime_resume(struct ufs_hba *hba)if (!hba->is_powered) goto out;else - ret =ufshcd_resume (hba,UFS_RUNTIME_PM ); +ufshcd_resume (hba); out: -trace_ufshcd_runtime_resume (dev_name (hba->dev), ret, +trace_ufshcd_runtime_resume (dev_name (hba->dev),0 ,ktime_to_us (ktime_sub (ktime_get (), start)), hba->curr_dev_pwr_mode, hba->uic_link_state); -return ret; +return 0 ; }EXPORT_SYMBOL (ufshcd_runtime_resume);@@ -9132,14 +9243,13 @@ EXPORT_SYMBOL(ufshcd_runtime_idle);* ufshcd_shutdown - shutdown routine * @hba: per adapter instance * -* This function would power off both UFS device and UFS link. +* This function would turn off both UFS device and UFS hba +* regulators. It would also disable clocks. * * Returns 0 always to allow force shutdown even in case of errors. */ int ufshcd_shutdown (struct ufs_hba *hba) { -int ret =0 ; -down (&hba->host_sem); hba->shutting_down = true;up (&hba->host_sem);@@ -9152,10 +9262,8 @@ int ufshcd_shutdown(struct ufs_hba *hba)pm_runtime_get_sync (hba->dev); - ret =ufshcd_suspend (hba,UFS_SHUTDOWN_PM ); +ufshcd_suspend (hba); out: -if (ret) -dev_err (hba->dev,"%s failed, err %d\n" , __func__, ret); hba->is_powered = false;/* allow force shutdown even in case of errors */ return 0 ;@@ -9477,15 +9585,163 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) }EXPORT_SYMBOL_GPL (ufshcd_init); +void ufshcd_resume_complete (struct device *dev) +{ +struct ufs_hba *hba =dev_get_drvdata (dev); + +pm_runtime_put_noidle (&hba->sdev_ufs_device->sdev_gendev); +} +EXPORT_SYMBOL_GPL (ufshcd_resume_complete); + +int ufshcd_suspend_prepare (struct device *dev) +{ +struct ufs_hba *hba =dev_get_drvdata (dev); + +/* +* SCSI assumes that runtime-pm and system-pm for scsi drivers +* are same. And it doesn't wake up the device for system-suspend +* if it's runtime suspended. But ufs doesn't follow that. +* The rpm-lvl and spm-lvl can be different in ufs. +* Force it to honor system-suspend. +*/ +scsi_autopm_get_device (hba->sdev_ufs_device); +/* Refer ufshcd_resume_complete() */ +pm_runtime_get_noresume (&hba->sdev_ufs_device->sdev_gendev); +scsi_autopm_put_device (hba->sdev_ufs_device); +return 0 ; +} +EXPORT_SYMBOL_GPL (ufshcd_suspend_prepare); + +#ifdef CONFIG_PM_SLEEP
drivers/scsi/ufs/ufshcd.c int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) · line 9625
Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10
-0+static int ufshcd_wl_poweroff (struct device *dev) +{ +ufshcd_wl_shutdown (dev); +return 0 ; +} +#endif + +static int ufshcd_wl_probe (struct device *dev) +{ +return is_device_wlun (to_scsi_device (dev)) ?0 : -ENODEV ;
The "ufs_device_wlun" driver owns PM for sdev_ufs_device because it provides the dev_pm_ops. This is where the pm_runtime_allow() belongs and it is symmetrical with the pm_runtime_forbid() in ufshcd_wl_remove().
if (!is_device_wlun(to_scsi_device(dev))) return -ENODEV; blk_pm_runtime_init(dev); pm_runtime_set_autosuspend_delay(dev, 0); pm_runtime_allow(dev);
Note sdev_ufs_device will not runtime suspend here unless the device links have been added and all consumers have suspended. That is handled separately by the pm_runtime_get_noresume() in ufshcd_slave_configure() and the pm_runtime_put() when luns_avail == 1 in ufshcd_setup_links().
Quoted 182 lines
drivers/scsi/ufs/ufshcd.c +110
-0+} + +static int ufshcd_wl_remove (struct device *dev) +{ +pm_runtime_forbid (dev); +return 0 ; +} + +static const struct dev_pm_ops ufshcd_wl_pm_ops = { +#ifdef CONFIG_PM_SLEEP + .suspend = ufshcd_wl_suspend, + .resume = ufshcd_wl_resume, + .freeze = ufshcd_wl_suspend, + .thaw = ufshcd_wl_resume, + .poweroff = ufshcd_wl_poweroff, + .restore = ufshcd_wl_resume, +#endif +SET_RUNTIME_PM_OPS (ufshcd_wl_runtime_suspend, ufshcd_wl_runtime_resume,NULL ) +}; + +/** +* ufs_dev_wlun_template - describes ufs device wlun +* ufs-device wlun - used to send pm commands +* All luns are consumers of ufs-device wlun. +* +* Currently, no sd driver is present for wluns. +* Hence the no specific pm operations are performed. +* With ufs design, SSU should be sent to ufs-device wlun. +* Hence register a scsi driver for ufs wluns only. +*/ +static struct scsi_driver ufs_dev_wlun_template = { + .gendrv = { + .name ="ufs_device_wlun" , + .owner =THIS_MODULE , + .probe = ufshcd_wl_probe, + .remove = ufshcd_wl_remove, + .pm = &ufshcd_wl_pm_ops, + .shutdown = ufshcd_wl_shutdown, + }, +}; + +static int ufshcd_rpmb_probe (struct device *dev) +{ +return is_rpmb_wlun (to_scsi_device (dev)) ?0 : -ENODEV ; +} + +static inline int ufshcd_clear_rpmb_uac (struct ufs_hba *hba) +{ +int ret =0 ; + +if (!hba->wlun_rpmb_clr_ua) +return 0 ; + ret =ufshcd_clear_ua_wlun (hba,UFS_UPIU_RPMB_WLUN ); +if (!ret) + hba->wlun_rpmb_clr_ua =0 ; +return ret; +} + +static int ufshcd_rpmb_runtime_resume (struct device *dev) +{ +struct ufs_hba *hba =wlun_dev_to_hba (dev); + +if (hba->sdev_rpmb) +return ufshcd_clear_rpmb_uac (hba); +return 0 ; +} + +static int ufshcd_rpmb_resume (struct device *dev) +{ +struct ufs_hba *hba =wlun_dev_to_hba (dev); + +if (hba->sdev_rpmb && !pm_runtime_suspended (dev)) +return ufshcd_clear_rpmb_uac (hba); +return 0 ; +} + +static const struct dev_pm_ops ufs_rpmb_pm_ops = { + SET_RUNTIME_PM_OPS(NULL, ufshcd_rpmb_runtime_resume, NULL) +SET_SYSTEM_SLEEP_PM_OPS (NULL , ufshcd_rpmb_resume) +}; + +/** +* Describes the ufs rpmb wlun. +* Used only to send uac. +*/ +static struct scsi_driver ufs_rpmb_wlun_template = { + .gendrv = { + .name ="ufs_rpmb_wlun" , + .owner =THIS_MODULE , + .probe = ufshcd_rpmb_probe, + .pm = &ufs_rpmb_pm_ops, + }, +}; +static int __init ufshcd_core_init (void ) { +int ret; +ufs_debugfs_init (); + + ret =scsi_register_driver (&ufs_dev_wlun_template.gendrv); +if (ret) { +ufs_debugfs_exit (); +return ret; + } + ret =scsi_register_driver (&ufs_rpmb_wlun_template.gendrv); +if (ret) { +ufs_debugfs_exit (); +scsi_unregister_driver (&ufs_dev_wlun_template.gendrv); +return ret; + }return 0 ; } static void __exit ufshcd_core_exit(void) {ufs_debugfs_exit (); +scsi_unregister_driver (&ufs_dev_wlun_template.gendrv); +scsi_unregister_driver (&ufs_rpmb_wlun_template.gendrv); }module_init (ufshcd_core_init);drivers/scsi/ufs/ufshcd.h +6
-0@@ -72,6 +72,8 @@ enum ufs_event_type {UFS_EVT_LINK_STARTUP_FAIL ,UFS_EVT_RESUME_ERR ,UFS_EVT_SUSPEND_ERR , +UFS_EVT_WL_SUSP_ERR , +UFS_EVT_WL_RES_ERR ,/* abnormal events */ UFS_EVT_DEV_RESET,@@ -804,6 +806,7 @@ struct ufs_hba {struct list_head clk_list_head;bool wlun_dev_clr_ua; +bool wlun_rpmb_clr_ua;/* Number of requests aborts */ int req_abort_count;@@ -841,6 +844,7 @@ struct ufs_hba {#ifdef CONFIG_DEBUG_FS struct dentry *debugfs_root;#endif +u32 luns_avail; };/* Returns true if clocks can be gated. Otherwise false */ @@ -1100,6 +1104,8 @@ int ufshcd_exec_raw_upiu_cmd(struct ufs_hba *hba, enum query_opcode desc_op);int ufshcd_wb_ctrl (struct ufs_hba *hba,bool enable); +int ufshcd_suspend_prepare (struct device *dev); +void ufshcd_resume_complete (struct device *dev);/* Wrapper functions for safely calling variant operations */ static inline const char *ufshcd_get_var_name (struct ufs_hba *hba)include/trace/events/ufs.h +13
-0@@ -246,6 +246,26 @@ DEFINE_EVENT(ufshcd_template, ufshcd_init, int dev_state, int link_state), TP_ARGS(dev_name, err, usecs, dev_state, link_state)); +DEFINE_EVENT (ufshcd_template, ufshcd_wl_suspend, + TP_PROTO(const char *dev_name, int err, s64 usecs, + int dev_state, int link_state), +TP_ARGS (dev_name, err, usecs, dev_state, link_state)); + +DEFINE_EVENT (ufshcd_template, ufshcd_wl_resume, + TP_PROTO(const char *dev_name, int err, s64 usecs, + int dev_state, int link_state), +TP_ARGS (dev_name, err, usecs, dev_state, link_state)); + +DEFINE_EVENT(ufshcd_template, ufshcd_wl_runtime_suspend, + TP_PROTO(const char *dev_name, int err, s64 usecs, + int dev_state, int link_state),
include/trace/events/ufs.h DEFINE_EVENT(ufshcd_template, ufshcd_init, · line 271
Quoted 10 lines
include/trace/events/ufs.h +7
-0+ TP_ARGS(dev_name, err, usecs, dev_state, link_state)); + +DEFINE_EVENT (ufshcd_template, ufshcd_wl_runtime_resume, + TP_PROTO(const char *dev_name, int err, s64 usecs, + int dev_state, int link_state), +TP_ARGS (dev_name, err, usecs, dev_state, link_state)); + TRACE_EVENT(ufshcd_command, TP_PROTO(const char *dev_name, enum ufs_trace_str_t str_t, unsigned int tag, u32 doorbell, int transfer_len, u32 intr,
_______________________________________________
Linux-mediatek mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-mediatek
Re: [PATCH v12 1/2] scsi: ufs: Enable power management for wlun
Quoted 1 line · Adrian Hunter
It would also be good if you could re-base on linux-next.
Hmm ... my understanding is that patches should be prepared on top of the for-next branch of the maintainer a patch is sent to, in this case the for-next branch of git://git.kernel.org/pub/scm/linux/kernel/git/mkp/scsi.git.
Thanks,
Bart.
_______________________________________________
Linux-mediatek mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-mediatek
Re: [PATCH v12 1/2] scsi: ufs: Enable power management for wlun
In reply to Adrian HunterQuoted 43 lines · Adrian Hunter
Quoted 38 lines · Asutosh Das
During runtime-suspend of ufs host, the scsi devices are already suspended and so are the queues associated with them. But the ufs host sends SSU to wlun during its runtime-suspend. During the process
blk_queue_enterchecks if the queue is not in suspended state. If so, it waits for the queue to resume, and never comes out of it.
The commit
(d55d15a33: scsi: block: Do not accept any requests while suspended) adds the check if the queue is in suspended state inblk_queue_enter().Call trace: __switch_to+0x174/0x2c4 __schedule+0x478/0x764 schedule+0x9c/0xe0 blk_queue_enter+0x158/0x228 blk_mq_alloc_request+0x40/0xa4 blk_get_request+0x2c/0x70 __scsi_execute+0x60/0x1c4 ufshcd_set_dev_pwr_mode+0x124/0x1e4 ufshcd_suspend+0x208/0x83c ufshcd_runtime_suspend+0x40/0x154 ufshcd_pltfrm_runtime_suspend+0x14/0x20 pm_generic_runtime_suspend+0x28/0x3c __rpm_callback+0x80/0x2a4 rpm_suspend+0x308/0x614 rpm_idle+0x158/0x228 pm_runtime_work+0x84/0xac process_one_work+0x1f0/0x470 worker_thread+0x26c/0x4c8 kthread+0x13c/0x320 ret_from_fork+0x10/0x18Fix this by registering ufs device wlun as a scsi driver and registering it for block runtime-pm. Also make this as a supplier for all other luns. That way, this device wlun suspends after all the consumers and resumes after hba resumes.
Quoted 3 lines
Co-developed-by Can Guo <[email protected]>Signed-off-by Can Guo <[email protected]>Signed-off-by Asutosh Das <[email protected]>
Quoted 8 lines
I have some more comments that may help straighten things out.
Also please look at
ufs_debugfs_get_user_access()andufs_debugfs_put_user_access()that now need to scsi_autopm_get/put_devicesdev_ufs_device.It would also be good if you could re-base on linux-next.
Hi Adrian
Thanks for the comments.
I agree moving the code to wlun probe and other changes. But it looks to me that it may not fully solve the issue.
Please let me explain my understanding on this:
(Please refer to the logs in v10)
scsi_autopm_*() are invoked on a sdev. pm_runtime_get_suppliers()/rpm_put_suppliers() are on the supplier device.
For the device wlun:
slave_configure():
- doesn't set the rpm_autosuspend
- pm_runtime_getnoresume()
scsi_sysfs_add_sdev():
- pm_runtime_forbid()
- scsi_autopm_get_device()
- device_add()
- ufshcd_wl_probe()
- scsi_autopm_put_device()
For all other scsi devices:
slave_alloc(): - ufshcd_setup_links()
Say all link_add: pm_runtime_put(&hba->sdev_ufs_device->sdev_gendev);
slave_configure():
- set rpm_autosuspend
scsi_sysfs_add_sdev():
- scsi_autopm_get_device()
- device_add() -> schedules an async probe()
- scsi_autopm_put_device() - (1)
Now the rpm_put_suppliers() can be invoked *after* pm_runtime_get_suppliers() of the async probe(), since both are running in different contexts. In that case, the usage_count of supplier would be decremented until rpm_active of this link becomes 1.
Now the pm_runtime_get_suppliers() expects the link_active to be more than 1.
Now then, there comes a time, that when sd_probe() schedules a suspend, the supplier usage_count becomes 0 and the link_active becomes 1. And the supplier suspends before the consumer.
So I was wondering, what'd make sure that the pm_runtime_get_suppliers() from driver_probe_device() is invoked after scsi_autopm_put_device() (1) finishes the rpm_put_suppliers().
Am not sure if I'm missing something in this.
Do you think, the current changes alone can fix the above issue?
-asd
Quoted 1,358 lines
Quoted 314 lines
--- drivers/scsi/ufs/cdns-pltfrm.c | 2 + drivers/scsi/ufs/tc-dwc-g210-pci.c | 2 + drivers/scsi/ufs/ufs-debugfs.c | 2 +- drivers/scsi/ufs/ufs-debugfs.h | 2 +- drivers/scsi/ufs/ufs-exynos.c | 2 + drivers/scsi/ufs/ufs-hisi.c | 2 + drivers/scsi/ufs/ufs-mediatek.c | 12 +- drivers/scsi/ufs/ufs-qcom.c | 2 + drivers/scsi/ufs/ufs_bsg.c | 6 +- drivers/scsi/ufs/ufshcd-pci.c | 36 +-- drivers/scsi/ufs/ufshcd.c | 622 ++++++++++++++++++++++++++----------- drivers/scsi/ufs/ufshcd.h | 6 + include/trace/events/ufs.h | 20 ++ 13 files changed, 491 insertions(+), 225 deletions(-)drivers/scsi/ufs/cdns-pltfrm.c +2
-0@@ -319,6 +319,8 @@ static const struct dev_pm_ops cdns_ufs_dev_pm_ops = { .runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_idle = ufshcd_pltfrm_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, };static struct platform_driver cdns_ufs_pltfrm_driver = {drivers/scsi/ufs/tc-dwc-g210-pci.c +2
-0@@ -148,6 +148,8 @@ static const struct dev_pm_ops tc_dwc_g210_pci_pm_ops = { .runtime_suspend = tc_dwc_g210_pci_runtime_suspend, .runtime_resume = tc_dwc_g210_pci_runtime_resume, .runtime_idle = tc_dwc_g210_pci_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, };static const struct pci_device_id tc_dwc_g210_pci_tbl[] = {drivers/scsi/ufs/ufs-debugfs.c +1
-1@@ -13,7 +13,7 @@ void __init ufs_debugfs_init(void) ufs_debugfs_root =debugfs_create_dir ("ufshcd" ,NULL ); } -void __exit ufs_debugfs_exit(void) +void ufs_debugfs_exit (void ) {debugfs_remove_recursive (ufs_debugfs_root); }drivers/scsi/ufs/ufs-debugfs.h +1
-1@@ -9,7 +9,7 @@ struct ufs_hba;#ifdef CONFIG_DEBUG_FS void __init ufs_debugfs_init (void ); -void __exit ufs_debugfs_exit(void); +void ufs_debugfs_exit (void );void ufs_debugfs_hba_init (struct ufs_hba *hba);void ufs_debugfs_hba_exit (struct ufs_hba *hba);#else drivers/scsi/ufs/ufs-exynos.c +2
-0@@ -1268,6 +1268,8 @@ static const struct dev_pm_ops exynos_ufs_pm_ops = { .runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_idle = ufshcd_pltfrm_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, };static struct platform_driver exynos_ufs_pltform = {drivers/scsi/ufs/ufs-hisi.c +2
-0@@ -574,6 +574,8 @@ static const struct dev_pm_ops ufs_hisi_pm_ops = { .runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_idle = ufshcd_pltfrm_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, };static struct platform_driver ufs_hisi_pltform = {drivers/scsi/ufs/ufs-mediatek.c +6
-6@@ -810,12 +810,10 @@ static int ufs_mtk_post_link(struct ufs_hba *hba)/* enable unipro clock gating feature */ ufs_mtk_cfg_unipro_cg (hba, true); -/* configure auto-hibern8 timer to 10ms */ -if (ufshcd_is_auto_hibern8_supported (hba)) { -ufshcd_auto_hibern8_update (hba, -FIELD_PREP (UFSHCI_AHIBERN8_TIMER_MASK ,10 ) | -FIELD_PREP (UFSHCI_AHIBERN8_SCALE_MASK ,3 )); - } +/* will be configured during probe hba */ +if (ufshcd_is_auto_hibern8_supported (hba)) + hba->ahit =FIELD_PREP (UFSHCI_AHIBERN8_TIMER_MASK ,10 ) | +FIELD_PREP (UFSHCI_AHIBERN8_SCALE_MASK ,3 );ufs_mtk_setup_clk_gating (hba);@@ -1097,6 +1095,8 @@ static const struct dev_pm_ops ufs_mtk_pm_ops = { .runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_idle = ufshcd_pltfrm_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, };static struct platform_driver ufs_mtk_pltform = {drivers/scsi/ufs/ufs-qcom.c +2
-0@@ -1546,6 +1546,8 @@ static const struct dev_pm_ops ufs_qcom_pm_ops = { .runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_idle = ufshcd_pltfrm_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, };static struct platform_driver ufs_qcom_pltform = {drivers/scsi/ufs/ufs_bsg.c +3
-3@@ -97,7 +97,7 @@ static int ufs_bsg_request(struct bsg_job *job) bsg_reply->reply_payload_rcv_len =0 ; -pm_runtime_get_sync (hba->dev); +scsi_autopm_get_device (hba->sdev_ufs_device); msgcode = bsg_request->msgcode;switch (msgcode) {@@ -106,7 +106,7 @@ static int ufs_bsg_request(struct bsg_job *job) ret =ufs_bsg_alloc_desc_buffer (hba, job, &desc_buff, &desc_len, desc_op);if (ret) { -pm_runtime_put_sync (hba->dev); +scsi_autopm_put_device (hba->sdev_ufs_device); goto out; }@@ -138,7 +138,7 @@ static int ufs_bsg_request(struct bsg_job *job)break ; } -pm_runtime_put_sync (hba->dev); +scsi_autopm_put_device (hba->sdev_ufs_device);if (!desc_buff) goto out;drivers/scsi/ufs/ufshcd-pci.c +5
-31@@ -247,29 +247,6 @@ static int ufshcd_pci_resume(struct device *dev) return ufshcd_system_resume(dev_get_drvdata(dev)); } -/** -* ufshcd_pci_poweroff - suspend-to-disk poweroff function -* @dev: pointer to PCI device handle -* -* Returns 0 if successful -* Returns non-zero otherwise -*/ -static int ufshcd_pci_poweroff (struct device *dev) -{ -struct ufs_hba *hba =dev_get_drvdata (dev); -int spm_lvl = hba->spm_lvl; -int ret; - -/* -* For poweroff we need to set the UFS device to PowerDown mode. -* Force spm_lvl to ensure that. -*/ - hba->spm_lvl =5 ; - ret =ufshcd_system_suspend (hba); - hba->spm_lvl = spm_lvl; -return ret; -} -#endif /* !CONFIG_PM_SLEEP */ #ifdef CONFIG_PM @@ -365,17 +342,14 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) }static const struct dev_pm_ops ufshcd_pci_pm_ops = { -#ifdef CONFIG_PM_SLEEP - .suspend = ufshcd_pci_suspend, - .resume = ufshcd_pci_resume, - .freeze = ufshcd_pci_suspend, - .thaw = ufshcd_pci_resume, - .poweroff = ufshcd_pci_poweroff, - .restore = ufshcd_pci_resume, -#endif SET_RUNTIME_PM_OPS(ufshcd_pci_runtime_suspend, ufshcd_pci_runtime_resume, ufshcd_pci_runtime_idle) + SET_SYSTEM_SLEEP_PM_OPS(ufshcd_pci_suspend, ufshcd_pci_resume) +#ifdef CONFIG_PM_SLEEP + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, +#endif };static const struct pci_device_id ufshcd_pci_tbl[] = {drivers/scsi/ufs/ufshcd.c +32
-4@@ -16,6 +16,7 @@#include <linux/bitfield.h> #include <linux/blk-pm.h> #include <linux/blkdev.h> +#include <scsi/scsi_driver.h> #include "ufshcd.h" #include "ufs_quirks.h" #include "unipro.h" @@ -78,6 +79,8 @@/* Polling time to wait for fDeviceInit */ #define FDEVICEINIT_COMPL_TIMEOUT 1500/* millisecs */ +#define wlun_dev_to_hba (dv) shost_priv(to_scsi_device(dv)->host) +#define ufshcd_toggle_vreg (_dev, _vreg, _on) \ ({ \ int _ret; \@@ -1556,7 +1559,7 @@ static ssize_t ufshcd_clkscale_enable_store(struct device *dev,if (value == hba->clk_scaling.is_enabled) goto out; -pm_runtime_get_sync (hba->dev); +scsi_autopm_get_device (hba->sdev_ufs_device);ufshcd_hold (hba, false); hba->clk_scaling.is_enabled = value;@@ -1572,7 +1575,7 @@ static ssize_t ufshcd_clkscale_enable_store(struct device *dev, }ufshcd_release (hba); -pm_runtime_put_sync (hba->dev); +scsi_autopm_put_device (hba->sdev_ufs_device); out:up (&hba->host_sem);return err ? err : count;@@ -2572,6 +2575,17 @@ static inline u16 ufshcd_upiu_wlun_to_scsi_wlun(u8 upiu_wlun_id)return (upiu_wlun_id & ~UFS_UPIU_WLUN_ID ) |SCSI_W_LUN_BASE ; } +static inline bool is_rpmb_wlun (struct scsi_device *sdev) +{ +return (sdev->lun ==ufshcd_upiu_wlun_to_scsi_wlun (UFS_UPIU_RPMB_WLUN )); +} + +static inline bool is_device_wlun (struct scsi_device *sdev) +{ +return (sdev->lun == +ufshcd_upiu_wlun_to_scsi_wlun (UFS_UPIU_UFS_DEVICE_WLUN )); +} +static void ufshcd_init_lrb (struct ufs_hba *hba,struct ufshcd_lrb *lrb,int i) {struct utp_transfer_cmd_desc *cmd_descp = hba->ucdl_base_addr;@@ -4106,11 +4120,11 @@ void ufshcd_auto_hibern8_update(struct ufs_hba *hba, u32 ahit)spin_unlock_irqrestore (hba->host->host_lock, flags);if (update && !pm_runtime_suspended (hba->dev)) { -pm_runtime_get_sync (hba->dev); +scsi_autopm_get_device (hba->sdev_ufs_device);ufshcd_hold (hba, false);ufshcd_auto_hibern8_enable (hba);ufshcd_release (hba); -pm_runtime_put (hba->dev); +scsi_autopm_put_device (hba->sdev_ufs_device); } }EXPORT_SYMBOL_GPL (ufshcd_auto_hibern8_update);@@ -4808,6 +4822,41 @@ static inline void ufshcd_get_lu_power_on_wp_status(struct ufs_hba *hba, }/** +* ufshcd_setup_links - associate link b/w device wlun and other luns +* @sdev: pointer to SCSI device +* @hba: pointer to ufs hba +*/ +static void ufshcd_setup_links (struct ufs_hba *hba,struct scsi_device *sdev) +{ +struct device_link *link; + +/* +* device wlun is the supplier & rest of the luns are consumers +* This ensures that device wlun suspends after all other luns. +*/ +if (hba->sdev_ufs_device) { + link = device_link_add(&sdev->sdev_gendev,drivers/scsi/ufs/ufshcd.c static inline void ufshcd_get_lu_power_on_wp_status(struct ufs_hba *hba, · line 4848
Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10
-0+ &hba->sdev_ufs_device->sdev_gendev, + DL_FLAG_PM_RUNTIME|DL_FLAG_RPM_ACTIVE); +if (!link) { +dev_err (&sdev->sdev_gendev,"Failed establishing link - %s\n" , +dev_name (&hba->sdev_ufs_device->sdev_gendev)); +return ; + } + hba->luns_avail--; +/* Ignore REPORT_LUN wlun probing */ +if (hba->luns_avail !=1 ) {The condition here is the wrong way around. i.e. should be
if (hba->luns_avail == 1) {drivers/scsi/ufs/ufshcd.c static inline void ufshcd_get_lu_power_on_wp_status(struct ufs_hba *hba, · line 4849
Quoted 1 line
drivers/scsi/ufs/ufshcd.c +1
-0+pm_runtime_put_noidle (&hba->sdev_ufs_device->sdev_gendev);If the
usage_countis zero here, then we should let it suspend i.e. make this:pm_runtime_put(&hba->sdev_ufs_device->sdev_gendev);Note this pairs with the
pm_runtime_get_noresume()that needs to be added toufshcd_slave_configure. Please see comments forufshcd_slave_configure().drivers/scsi/ufs/ufshcd.c static inline void ufshcd_get_lu_power_on_wp_status(struct ufs_hba *hba, · line 4850
Quoted 1 line
drivers/scsi/ufs/ufshcd.c +1
-0+pm_runtime_allow (&hba->sdev_ufs_device->sdev_gendev);This
pm_runtime_allowshould not be here. It should be inufshcd_wl_probe(). Please see comments forufshcd_wl_probe().Quoted 23 lines
drivers/scsi/ufs/ufshcd.c +11
-0+return ; + } + }else { +/* device wlun is probed */ + hba->luns_avail--; + } +} + +/** * ufshcd_slave_alloc - handle initial SCSI device configurations * @sdev: pointer to SCSI device * @@ -4838,6 +4887,8 @@ static int ufshcd_slave_alloc(struct scsi_device *sdev)ufshcd_get_lu_power_on_wp_status (hba, sdev); +ufshcd_setup_links (hba, sdev); +return 0 ; }@@ -4869,8 +4920,13 @@ static int ufshcd_slave_configure(struct scsi_device *sdev)blk_queue_update_dma_pad (q,PRDT_DATA_BYTE_COUNT_PAD -1 );drivers/scsi/ufs/ufshcd.c static int ufshcd_slave_configure(struct scsi_device *sdev) · line 4928
Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +6
-2if (hba->quirks &UFSHCD_QUIRK_ALIGN_SG_WITH_PAGE_SIZE )blk_queue_update_dma_alignment (q,PAGE_SIZE -1 ); - -if (ufshcd_is_rpm_autosuspend_allowed (hba)) +/* +* Block runtime-pm until all consumers are added. +* Refer ufshcd_setup_links(). +*/ +if (is_device_wlun (sdev)) +pm_runtime_forbid (&sdev->sdev_gendev);Since
rpm_autosuspendis not being set forsdev_ufs_device,scsi_sysfs_add_sdev()willpm_runtime_forbid()it anyway. We should increase theusage_countinstead i.e.pm_runtime_get_noresume(&sdev->sdev_gendev);Quoted 119 lines
drivers/scsi/ufs/ufshcd.c +19
-31+else if (ufshcd_is_rpm_autosuspend_allowed (hba)) sdev->rpm_autosuspend =1 ;ufshcd_crypto_setup_rq_keyslot_manager (hba, q);@@ -4985,15 +5041,9 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)* UFS device needs urgent BKOPs. */ if (!hba->pm_op_in_progress && -ufshcd_is_exception_event (lrbp->ucd_rsp_ptr) && -schedule_work (&hba->eeh_work)) { -/* -* Prevent suspend once eeh_work is scheduled -* to avoid deadlock between ufshcd_suspend -* and exception event handler. -*/ -pm_runtime_get_noresume (hba->dev); - } +ufshcd_is_exception_event (lrbp->ucd_rsp_ptr)) +/* Flushed in suspend */ +schedule_work (&hba->eeh_work);break ;case UPIU_TRANSACTION_REJECT_UPIU :/* TODO: handle Reject UPIU Response */ @@ -5589,8 +5639,8 @@ static void ufshcd_rpm_dev_flush_recheck_work(struct work_struct *work)* after a certain delay to recheck the threshold by next runtime * suspend. */ -pm_runtime_get_sync (hba->dev); -pm_runtime_put_sync (hba->dev); +scsi_autopm_get_device (hba->sdev_ufs_device); +scsi_autopm_put_device (hba->sdev_ufs_device); }/** @@ -5607,7 +5657,6 @@ static void ufshcd_exception_event_handler(struct work_struct *work)u32 status =0 ; hba = container_of(work, struct ufs_hba, eeh_work); -pm_runtime_get_sync (hba->dev);ufshcd_scsi_block_requests (hba); err =ufshcd_get_ee_status (hba, &status);if (err) {@@ -5623,14 +5672,6 @@ static void ufshcd_exception_event_handler(struct work_struct *work) out:ufshcd_scsi_unblock_requests (hba); -/* -* pm_runtime_get_noresume is called while scheduling -* eeh_work to avoid suspend racing with exception work. -* Hence decrement usage counter using pm_runtime_put_noidle -* to allow suspend on completion of exception event handler. -*/ -pm_runtime_put_noidle (hba->dev); -pm_runtime_put (hba->dev);return ; }@@ -5755,12 +5796,13 @@ static void ufshcd_clk_scaling_suspend(struct ufs_hba *hba, bool suspend)static void ufshcd_err_handling_prepare (struct ufs_hba *hba) { -pm_runtime_get_sync (hba->dev); -if (pm_runtime_status_suspended (hba->dev) || hba->is_sys_suspended) { +scsi_autopm_get_device (hba->sdev_ufs_device); +if (pm_runtime_status_suspended (&hba->sdev_ufs_device->sdev_gendev) || + hba->is_sys_suspended) {enum ufs_pm_op pm_op;/* -* Don't assume anything of pm_runtime_get_sync(), if +* Don't assume anything of resume, if * resume fails, irq and clocks can be OFF, and powers * can be OFF or in LPM. */ @@ -5789,7 +5831,7 @@ static void ufshcd_err_handling_unprepare(struct ufs_hba *hba)ufshcd_release (hba);if (ufshcd_is_clkscaling_supported (hba))ufshcd_clk_scaling_suspend (hba, false); -pm_runtime_put (hba->dev); +pm_runtime_put (&hba->sdev_ufs_device->sdev_gendev); }static inline bool ufshcd_err_handling_should_stop (struct ufs_hba *hba)@@ -5810,14 +5852,14 @@ static void ufshcd_recover_pm_error(struct ufs_hba *hba) hba->is_sys_suspended = false;/* -* Set RPM status of hba device to RPM_ACTIVE, +* Set RPM status of wlun device to RPM_ACTIVE, * this also clears its runtime error. */ - ret =pm_runtime_set_active (hba->dev); + ret =pm_runtime_set_active (&hba->sdev_ufs_device->sdev_gendev);/* -* If hba device had runtime error, we also need to resume those -* scsi devices under hba in case any of them has failed to be -* resumed due to hba runtime resume failure. This is to unblock +* If wlun device had runtime error, we also need to resume those +* consumer scsi devices in case any of them has failed to be +* resumed due to supplier runtime resume failure. This is to unblock * blk_queue_enter in case there are bios waiting inside it. */ if (!ret) {@@ -7207,11 +7249,12 @@ static void ufshcd_set_active_icc_lvl(struct ufs_hba *hba)static inline void ufshcd_blk_pm_runtime_init (struct scsi_device *sdev) { +int dly =is_device_wlun (sdev) ?0 :RPM_AUTOSUSPEND_DELAY_MS ; +scsi_autopm_get_device (sdev);blk_pm_runtime_init (sdev->request_queue, &sdev->sdev_gendev);if (sdev->rpm_autosuspend) -pm_runtime_set_autosuspend_delay (&sdev->sdev_gendev, -RPM_AUTOSUSPEND_DELAY_MS ); +pm_runtime_set_autosuspend_delay (&sdev->sdev_gendev, dly);scsi_autopm_put_device (sdev); }drivers/scsi/ufs/ufshcd.c static int ufshcd_scsi_add_wlus(struct ufs_hba *hba) · line 7300
Quoted 5 lines
drivers/scsi/ufs/ufshcd.c +1
-0@@ -7253,6 +7296,7 @@ static int ufshcd_scsi_add_wlus(struct ufs_hba *hba) hba->sdev_ufs_device =NULL ; goto out; } +scsi_autopm_get_device (hba->sdev_ufs_device);ufshcd_blk_pm_runtime_init (hba->sdev_ufs_device);Let's drop both
scsi_autopm_get_device()andufshcd_blk_pm_runtime_init()here and instead do things inufshcd_wl_probe()- see comments forufshcd_wl_probe()Quoted 626 lines
drivers/scsi/ufs/ufshcd.c +238
-146scsi_device_put (hba->sdev_ufs_device);@@ -7417,6 +7461,9 @@ static int ufs_get_device_desc(struct ufs_hba *hba) goto out; } + hba->luns_avail = desc_buf[DEVICE_DESC_PARAM_NUM_LU ] + + desc_buf[DEVICE_DESC_PARAM_NUM_WLU ]; +ufs_fixup_device_setup (hba);ufshcd_wb_probe (hba, desc_buf);@@ -7892,6 +7939,7 @@ static int ufshcd_probe_hba(struct ufs_hba *hba, bool async)ufshcd_set_ufs_dev_active (hba);ufshcd_force_reset_auto_bkops (hba); hba->wlun_dev_clr_ua = true; + hba->wlun_rpmb_clr_ua = true;/* Gear up to HS gear if supported */ if (hba->max_pwr_info.is_valid) {@@ -8475,7 +8523,8 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba,* handling context. */ hba->host->eh_noresume =1 ; -ufshcd_clear_ua_wluns (hba); +if (hba->wlun_dev_clr_ua) +ufshcd_clear_ua_wlun (hba,UFS_UPIU_UFS_DEVICE_WLUN ); cmd[4 ] = pwr_mode <<4 ;@@ -8650,23 +8699,7 @@ static void ufshcd_hba_vreg_set_hpm(struct ufs_hba *hba)ufshcd_setup_hba_vreg (hba, true); } -/** -* ufshcd_suspend - helper function for suspend operations -* @hba: per adapter instance -* @pm_op: desired low power operation type -* -* This function will try to put the UFS device and link into low power -* mode based on the "rpm_lvl" (Runtime PM level) or "spm_lvl" -* (System PM level). -* -* If this function is called during shutdown, it will make sure that -* both UFS device and UFS link is powered off. -* -* NOTE: UFS device & link must be active before we enter in this function. -* -* Returns 0 for success and non-zero for failure -*/ -static int ufshcd_suspend (struct ufs_hba *hba,enum ufs_pm_op pm_op) +static int __ufshcd_wl_suspend (struct ufs_hba *hba,enum ufs_pm_op pm_op) {int ret =0 ;int check_for_bkops;@@ -8674,7 +8707,7 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)enum ufs_dev_pwr_mode req_dev_pwr_mode;enum uic_link_state req_link_state; - hba->pm_op_in_progress =1 ; + hba->pm_op_in_progress = true;if (!ufshcd_is_shutdown_pm (pm_op)) { pm_lvl =ufshcd_is_runtime_pm (pm_op) ? hba->rpm_lvl : hba->spm_lvl;@@ -8697,17 +8730,17 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)if (req_dev_pwr_mode ==UFS_ACTIVE_PWR_MODE && req_link_state ==UIC_LINK_ACTIVE_STATE ) { - goto disable_clks; + goto enable_scaling; }if ((req_dev_pwr_mode == hba->curr_dev_pwr_mode) && (req_link_state == hba->uic_link_state)) - goto enable_gating; + goto enable_scaling;/* UFS device & link must be active before we enter in this function */ if (!ufshcd_is_ufs_dev_active (hba) || !ufshcd_is_link_active (hba)) { ret = -EINVAL ; - goto enable_gating; + goto enable_scaling; }if (ufshcd_is_runtime_pm (pm_op)) {@@ -8719,7 +8752,7 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)*/ ret =ufshcd_urgent_bkops (hba);if (ret) - goto enable_gating; + goto enable_scaling; }else {/* make sure that auto bkops is disabled */ ufshcd_disable_auto_bkops (hba);@@ -8747,7 +8780,7 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)if (!hba->dev_info.b_rpm_dev_flush_capable) { ret =ufshcd_set_dev_pwr_mode (hba, req_dev_pwr_mode);if (ret) - goto enable_gating; + goto enable_scaling; } }@@ -8760,7 +8793,6 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)if (ret) goto set_dev_active; -disable_clks:/* * Call vendor specific suspend callback. As these callbacks may access * vendor specific host controller register space call them before the @@ -8769,28 +8801,9 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) ret =ufshcd_vops_suspend (hba, pm_op);if (ret) goto set_link_active; -/* -* Disable the host irq as host controller as there won't be any -* host controller transaction expected till resume. -*/ -ufshcd_disable_irq (hba); - -ufshcd_setup_clocks (hba, false); - -if (ufshcd_is_clkgating_allowed (hba)) { - hba->clk_gating.state =CLKS_OFF ; -trace_ufshcd_clk_gating (dev_name (hba->dev), - hba->clk_gating.state); - } - -ufshcd_vreg_set_lpm (hba); - -/* Put the host controller in low power mode if possible */ -ufshcd_hba_vreg_set_lpm (hba); goto out; set_link_active: -ufshcd_vreg_set_hpm (hba);/* * Device hardware reset is required to exit DeepSleep. Also, for * DeepSleep, the link is off so host reset and restore will be done @@ -8812,57 +8825,32 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) }if (!ufshcd_set_dev_pwr_mode (hba,UFS_ACTIVE_PWR_MODE ))ufshcd_disable_auto_bkops (hba); -enable_gating: +enable_scaling:if (ufshcd_is_clkscaling_supported (hba))ufshcd_clk_scaling_suspend (hba, false); - hba->clk_gating.is_suspended = false; hba->dev_info.b_rpm_dev_flush_capable = false; -ufshcd_clear_ua_wluns (hba); -ufshcd_release (hba); out:if (hba->dev_info.b_rpm_dev_flush_capable) {schedule_delayed_work (&hba->rpm_dev_flush_recheck_work,msecs_to_jiffies (RPM_DEV_FLUSH_RECHECK_WORK_DELAY_MS )); } - hba->pm_op_in_progress =0 ; - -if (ret) -ufshcd_update_evt_hist (hba,UFS_EVT_SUSPEND_ERR , (u32 )ret); +if (ret) { +ufshcd_update_evt_hist (hba,UFS_EVT_WL_SUSP_ERR , (u32 )ret); + hba->clk_gating.is_suspended = false; +ufshcd_release (hba); + } + hba->pm_op_in_progress = false;return ret; } -/** -* ufshcd_resume - helper function for resume operations -* @hba: per adapter instance -* @pm_op: runtime PM or system PM -* -* This function basically brings the UFS device, UniPro link and controller -* to active state. -* -* Returns 0 for success and non-zero for failure -*/ -static int ufshcd_resume (struct ufs_hba *hba,enum ufs_pm_op pm_op) +static int __ufshcd_wl_resume (struct ufs_hba *hba,enum ufs_pm_op pm_op) {int ret; -enum uic_link_state old_link_state; +enum uic_link_state old_link_state = hba->uic_link_state; - hba->pm_op_in_progress =1 ; - old_link_state = hba->uic_link_state; - -ufshcd_hba_vreg_set_hpm (hba); - ret =ufshcd_vreg_set_hpm (hba); -if (ret) - goto out; - -/* Make sure clocks are enabled before accessing controller */ - ret =ufshcd_setup_clocks (hba, true); -if (ret) - goto disable_vreg; - -/* enable the host irq as host controller would be active soon */ -ufshcd_enable_irq (hba); + hba->pm_op_in_progress = true;/* * Call vendor specific resume callback. As these callbacks may access @@ -8871,7 +8859,7 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)*/ ret =ufshcd_vops_resume (hba, pm_op);if (ret) - goto disable_irq_and_vops_clks; + goto out;/* For DeepSleep, the only supported option is to have the link off */ WARN_ON (ufshcd_is_ufs_dev_deepsleep (hba) && !ufshcd_is_link_off (hba));@@ -8916,31 +8904,147 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)*/ ufshcd_urgent_bkops (hba); - hba->clk_gating.is_suspended = false; - -if (ufshcd_is_clkscaling_supported (hba)) -ufshcd_clk_scaling_suspend (hba, false); - -/* Enable Auto-Hibernate if configured */ -ufshcd_auto_hibern8_enable (hba); +if (hba->clk_scaling.is_allowed) +ufshcd_resume_clkscaling (hba);if (hba->dev_info.b_rpm_dev_flush_capable) { hba->dev_info.b_rpm_dev_flush_capable = false;cancel_delayed_work (&hba->rpm_dev_flush_recheck_work); } -ufshcd_clear_ua_wluns (hba); - -/* Schedule clock gating in case of no access to UFS device yet */ -ufshcd_release (hba); - +/* Enable Auto-Hibernate if configured */ +ufshcd_auto_hibern8_enable (hba); goto out; set_old_link_state:ufshcd_link_state_transition (hba, old_link_state,0 ); vendor_suspend:ufshcd_vops_suspend (hba, pm_op); -disable_irq_and_vops_clks: +out: +if (ret) +ufshcd_update_evt_hist (hba,UFS_EVT_WL_RES_ERR , (u32 )ret); + hba->clk_gating.is_suspended = false; +ufshcd_release (hba); + hba->pm_op_in_progress = false; +return ret; +} + +static int ufshcd_wl_runtime_suspend (struct device *dev) +{ +struct scsi_device *sdev =to_scsi_device (dev); +struct ufs_hba *hba; +int ret; +ktime_t start =ktime_get (); + + hba =shost_priv (sdev->host); + + ret =__ufshcd_wl_suspend (hba,UFS_RUNTIME_PM ); +if (ret) +dev_err (&sdev->sdev_gendev,"%s failed: %d\n" , __func__, ret); + +trace_ufshcd_wl_runtime_suspend (dev_name (dev), ret, +ktime_to_us (ktime_sub (ktime_get (), start)), + hba->curr_dev_pwr_mode, hba->uic_link_state); + +return ret; +} + +static int ufshcd_wl_runtime_resume (struct device *dev) +{ +struct scsi_device *sdev =to_scsi_device (dev); +struct ufs_hba *hba; +int ret =0 ; +ktime_t start =ktime_get (); + + hba =shost_priv (sdev->host); + + ret =__ufshcd_wl_resume (hba,UFS_RUNTIME_PM ); +if (ret) +dev_err (&sdev->sdev_gendev,"%s failed: %d\n" , __func__, ret); + +trace_ufshcd_wl_runtime_resume (dev_name (dev), ret, +ktime_to_us (ktime_sub (ktime_get (), start)), + hba->curr_dev_pwr_mode, hba->uic_link_state); + +return ret; +} + +#ifdef CONFIG_PM_SLEEP +static int ufshcd_wl_suspend (struct device *dev) +{ +struct scsi_device *sdev =to_scsi_device (dev); +struct ufs_hba *hba; +int ret; +ktime_t start =ktime_get (); + + hba =shost_priv (sdev->host); + ret =__ufshcd_wl_suspend (hba,UFS_SYSTEM_PM ); +if (ret) +dev_err (&sdev->sdev_gendev,"%s failed: %d\n" , __func__, ret); + +trace_ufshcd_wl_suspend (dev_name (dev), ret, +ktime_to_us (ktime_sub (ktime_get (), start)), + hba->curr_dev_pwr_mode, hba->uic_link_state); + +return ret; +} + +static int ufshcd_wl_resume (struct device *dev) +{ +struct scsi_device *sdev =to_scsi_device (dev); +struct ufs_hba *hba; +int ret =0 ; +ktime_t start =ktime_get (); + +if (pm_runtime_suspended (dev)) +return 0 ; + hba =shost_priv (sdev->host); + + ret =__ufshcd_wl_resume (hba,UFS_SYSTEM_PM ); +if (ret) +dev_err (&sdev->sdev_gendev,"%s failed: %d\n" , __func__, ret); + +trace_ufshcd_wl_resume (dev_name (dev), ret, +ktime_to_us (ktime_sub (ktime_get (), start)), + hba->curr_dev_pwr_mode, hba->uic_link_state); + +return ret; +} +#endif + +static void ufshcd_wl_shutdown (struct device *dev) +{ +struct scsi_device *sdev =to_scsi_device (dev); +struct ufs_hba *hba; + + hba =shost_priv (sdev->host); +/* Turn on everything while shutting down */ +scsi_autopm_get_device (sdev); +scsi_device_quiesce (sdev); +shost_for_each_device (sdev, hba->host) { +if (sdev == hba->sdev_ufs_device) +continue ; +scsi_device_quiesce (sdev); + } +__ufshcd_wl_suspend (hba,UFS_SHUTDOWN_PM ); +} + +/** +* ufshcd_suspend - helper function for suspend operations +* @hba: per adapter instance +* +* This function will put disable irqs, turn off clocks +* and set vreg and hba-vreg in lpm mode. +* Also check the description of __ufshcd_wl_suspend(). +*/ +static void ufshcd_suspend (struct ufs_hba *hba) +{ + hba->pm_op_in_progress =1 ; + +/* +* Disable the host irq as host controller as there won't be any +* host controller transaction expected till resume. +*/ ufshcd_disable_irq (hba);ufshcd_setup_clocks (hba, false);if (ufshcd_is_clkgating_allowed (hba)) {@@ -8948,6 +9052,43 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)trace_ufshcd_clk_gating (dev_name (hba->dev), hba->clk_gating.state); } + +ufshcd_vreg_set_lpm (hba); +/* Put the host controller in low power mode if possible */ +ufshcd_hba_vreg_set_lpm (hba); + hba->pm_op_in_progress =0 ; +} + +/** +* ufshcd_resume - helper function for resume operations +* @hba: per adapter instance +* +* This function basically turns on the regulators, clocks and +* irqs of the hba. +* Also check the description of __ufshcd_wl_resume(). +* +* Returns 0 for success and non-zero for failure +*/ +static int ufshcd_resume (struct ufs_hba *hba) +{ +int ret; + + hba->pm_op_in_progress =1 ; + +ufshcd_hba_vreg_set_hpm (hba); + ret =ufshcd_vreg_set_hpm (hba); +if (ret) + goto out; + +/* Make sure clocks are enabled before accessing controller */ + ret =ufshcd_setup_clocks (hba, true); +if (ret) + goto disable_vreg; + +/* enable the host irq as host controller would be active soon */ +ufshcd_enable_irq (hba); + goto out; + disable_vreg:ufshcd_vreg_set_lpm (hba); out:@@ -8962,6 +9103,7 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)* @hba: per adapter instance * * Check the description of ufshcd_suspend() function for more details. +* Also check the description of __ufshcd_wl_suspend(). * * Returns 0 for success and non-zero for failure */ @@ -8987,21 +9129,7 @@ int ufshcd_system_suspend(struct ufs_hba *hba) !hba->dev_info.b_rpm_dev_flush_capable) goto out; -if (pm_runtime_suspended (hba->dev)) { -/* -* UFS device and/or UFS link low power states during runtime -* suspend seems to be different than what is expected during -* system suspend. Hence runtime resume the devic & link and -* let the system suspend low power states to take effect. -* TODO: If resume takes longer time, we might have optimize -* it in future by not resuming everything if possible. -*/ - ret =ufshcd_runtime_resume (hba); -if (ret) - goto out; - } - - ret =ufshcd_suspend (hba,UFS_SYSTEM_PM ); +ufshcd_suspend (hba); out: trace_ufshcd_system_suspend(dev_name(hba->dev), ret, ktime_to_us(ktime_sub(ktime_get(), start)),@@ -9023,7 +9151,6 @@ EXPORT_SYMBOL(ufshcd_system_suspend);int ufshcd_system_resume (struct ufs_hba *hba) { -int ret =0 ;ktime_t start =ktime_get ();if (!hba)@@ -9034,22 +9161,18 @@ int ufshcd_system_resume(struct ufs_hba *hba)down (&hba->host_sem); } -if (!hba->is_powered ||pm_runtime_suspended (hba->dev)) -/* -* Let the runtime resume take care of resuming -* if runtime suspended. -*/ +if (!hba->is_powered) goto out;else - ret =ufshcd_resume (hba,UFS_SYSTEM_PM ); +ufshcd_resume (hba); out: -trace_ufshcd_system_resume (dev_name (hba->dev), ret, +trace_ufshcd_system_resume (dev_name (hba->dev),0 ,ktime_to_us (ktime_sub (ktime_get (), start)), hba->curr_dev_pwr_mode, hba->uic_link_state); -if (!ret) - hba->is_sys_suspended = false; + + hba->is_sys_suspended = false;up (&hba->host_sem); -return ret; +return 0 ; }EXPORT_SYMBOL (ufshcd_system_resume);@@ -9058,12 +9181,12 @@ EXPORT_SYMBOL(ufshcd_system_resume);* @hba: per adapter instance * * Check the description of ufshcd_suspend() function for more details. +* Also check the description of __ufshcd_wl_suspend(). * * Returns 0 for success and non-zero for failure */ int ufshcd_runtime_suspend (struct ufs_hba *hba) { -int ret =0 ;ktime_t start =ktime_get ();if (!hba)@@ -9072,12 +9195,12 @@ int ufshcd_runtime_suspend(struct ufs_hba *hba)if (!hba->is_powered) goto out;else - ret =ufshcd_suspend (hba,UFS_RUNTIME_PM ); +ufshcd_suspend (hba); out: -trace_ufshcd_runtime_suspend (dev_name (hba->dev), ret, +trace_ufshcd_runtime_suspend (dev_name (hba->dev),0 ,ktime_to_us (ktime_sub (ktime_get (), start)), hba->curr_dev_pwr_mode, hba->uic_link_state); -return ret; +return 0 ; }EXPORT_SYMBOL (ufshcd_runtime_suspend);@@ -9085,26 +9208,14 @@ EXPORT_SYMBOL(ufshcd_runtime_suspend);* ufshcd_runtime_resume - runtime resume routine * @hba: per adapter instance * -* This function basically brings the UFS device, UniPro link and controller +* This function basically brings controller * to active state. Following operations are done in this function: * * 1. Turn on all the controller related clocks -* 2. Bring the UniPro link out of Hibernate state -* 3. If UFS device is in sleep state, turn ON VCC rail and bring the UFS device -* to active state. -* 4. If auto-bkops is enabled on the device, disable it. -* -* So following would be the possible power state after this function return -* successfully: -* S1: UFS device in Active state with VCC rail ON -* UniPro link in Active state -* All the UFS/UniPro controller clocks are ON -* -* Returns 0 for success and non-zero for failure +* 2. Turn ON VCC rail */ int ufshcd_runtime_resume (struct ufs_hba *hba) { -int ret =0 ;ktime_t start =ktime_get ();if (!hba)@@ -9113,12 +9224,12 @@ int ufshcd_runtime_resume(struct ufs_hba *hba)if (!hba->is_powered) goto out;else - ret =ufshcd_resume (hba,UFS_RUNTIME_PM ); +ufshcd_resume (hba); out: -trace_ufshcd_runtime_resume (dev_name (hba->dev), ret, +trace_ufshcd_runtime_resume (dev_name (hba->dev),0 ,ktime_to_us (ktime_sub (ktime_get (), start)), hba->curr_dev_pwr_mode, hba->uic_link_state); -return ret; +return 0 ; }EXPORT_SYMBOL (ufshcd_runtime_resume);@@ -9132,14 +9243,13 @@ EXPORT_SYMBOL(ufshcd_runtime_idle);* ufshcd_shutdown - shutdown routine * @hba: per adapter instance * -* This function would power off both UFS device and UFS link. +* This function would turn off both UFS device and UFS hba +* regulators. It would also disable clocks. * * Returns 0 always to allow force shutdown even in case of errors. */ int ufshcd_shutdown (struct ufs_hba *hba) { -int ret =0 ; -down (&hba->host_sem); hba->shutting_down = true;up (&hba->host_sem);@@ -9152,10 +9262,8 @@ int ufshcd_shutdown(struct ufs_hba *hba)pm_runtime_get_sync (hba->dev); - ret =ufshcd_suspend (hba,UFS_SHUTDOWN_PM ); +ufshcd_suspend (hba); out: -if (ret) -dev_err (hba->dev,"%s failed, err %d\n" , __func__, ret); hba->is_powered = false;/* allow force shutdown even in case of errors */ return 0 ;@@ -9477,15 +9585,163 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) }EXPORT_SYMBOL_GPL (ufshcd_init); +void ufshcd_resume_complete (struct device *dev) +{ +struct ufs_hba *hba =dev_get_drvdata (dev); + +pm_runtime_put_noidle (&hba->sdev_ufs_device->sdev_gendev); +} +EXPORT_SYMBOL_GPL (ufshcd_resume_complete); + +int ufshcd_suspend_prepare (struct device *dev) +{ +struct ufs_hba *hba =dev_get_drvdata (dev); + +/* +* SCSI assumes that runtime-pm and system-pm for scsi drivers +* are same. And it doesn't wake up the device for system-suspend +* if it's runtime suspended. But ufs doesn't follow that. +* The rpm-lvl and spm-lvl can be different in ufs. +* Force it to honor system-suspend. +*/ +scsi_autopm_get_device (hba->sdev_ufs_device); +/* Refer ufshcd_resume_complete() */ +pm_runtime_get_noresume (&hba->sdev_ufs_device->sdev_gendev); +scsi_autopm_put_device (hba->sdev_ufs_device); +return 0 ; +} +EXPORT_SYMBOL_GPL (ufshcd_suspend_prepare); + +#ifdef CONFIG_PM_SLEEP drivers/scsi/ufs/ufshcd.c int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) · line 9625
Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10
-0+static int ufshcd_wl_poweroff (struct device *dev) +{ +ufshcd_wl_shutdown (dev); +return 0 ; +} +#endif + +static int ufshcd_wl_probe (struct device *dev) +{ +return is_device_wlun (to_scsi_device (dev)) ?0 : -ENODEV ;The "
ufs_device_wlun" driver owns PM forsdev_ufs_devicebecause it provides thedev_pm_ops. This is where thepm_runtime_allow()belongs and it is symmetrical with thepm_runtime_forbid()inufshcd_wl_remove().if (!is_device_wlun(to_scsi_device(dev))) return -ENODEV; blk_pm_runtime_init(dev); pm_runtime_set_autosuspend_delay(dev, 0); pm_runtime_allow(dev);Note
sdev_ufs_devicewill not runtime suspend here unless the device links have been added and all consumers have suspended. That is handled separately by thepm_runtime_get_noresume()inufshcd_slave_configure()and thepm_runtime_put()whenluns_avail== 1 inufshcd_setup_links().Quoted 192 lines
drivers/scsi/ufs/ufshcd.c +110
-0+} + +static int ufshcd_wl_remove (struct device *dev) +{ +pm_runtime_forbid (dev); +return 0 ; +} + +static const struct dev_pm_ops ufshcd_wl_pm_ops = { +#ifdef CONFIG_PM_SLEEP + .suspend = ufshcd_wl_suspend, + .resume = ufshcd_wl_resume, + .freeze = ufshcd_wl_suspend, + .thaw = ufshcd_wl_resume, + .poweroff = ufshcd_wl_poweroff, + .restore = ufshcd_wl_resume, +#endif +SET_RUNTIME_PM_OPS (ufshcd_wl_runtime_suspend, ufshcd_wl_runtime_resume,NULL ) +}; + +/** +* ufs_dev_wlun_template - describes ufs device wlun +* ufs-device wlun - used to send pm commands +* All luns are consumers of ufs-device wlun. +* +* Currently, no sd driver is present for wluns. +* Hence the no specific pm operations are performed. +* With ufs design, SSU should be sent to ufs-device wlun. +* Hence register a scsi driver for ufs wluns only. +*/ +static struct scsi_driver ufs_dev_wlun_template = { + .gendrv = { + .name ="ufs_device_wlun" , + .owner =THIS_MODULE , + .probe = ufshcd_wl_probe, + .remove = ufshcd_wl_remove, + .pm = &ufshcd_wl_pm_ops, + .shutdown = ufshcd_wl_shutdown, + }, +}; + +static int ufshcd_rpmb_probe (struct device *dev) +{ +return is_rpmb_wlun (to_scsi_device (dev)) ?0 : -ENODEV ; +} + +static inline int ufshcd_clear_rpmb_uac (struct ufs_hba *hba) +{ +int ret =0 ; + +if (!hba->wlun_rpmb_clr_ua) +return 0 ; + ret =ufshcd_clear_ua_wlun (hba,UFS_UPIU_RPMB_WLUN ); +if (!ret) + hba->wlun_rpmb_clr_ua =0 ; +return ret; +} + +static int ufshcd_rpmb_runtime_resume (struct device *dev) +{ +struct ufs_hba *hba =wlun_dev_to_hba (dev); + +if (hba->sdev_rpmb) +return ufshcd_clear_rpmb_uac (hba); +return 0 ; +} + +static int ufshcd_rpmb_resume (struct device *dev) +{ +struct ufs_hba *hba =wlun_dev_to_hba (dev); + +if (hba->sdev_rpmb && !pm_runtime_suspended (dev)) +return ufshcd_clear_rpmb_uac (hba); +return 0 ; +} + +static const struct dev_pm_ops ufs_rpmb_pm_ops = { + SET_RUNTIME_PM_OPS(NULL, ufshcd_rpmb_runtime_resume, NULL) +SET_SYSTEM_SLEEP_PM_OPS (NULL , ufshcd_rpmb_resume) +}; + +/** +* Describes the ufs rpmb wlun. +* Used only to send uac. +*/ +static struct scsi_driver ufs_rpmb_wlun_template = { + .gendrv = { + .name ="ufs_rpmb_wlun" , + .owner =THIS_MODULE , + .probe = ufshcd_rpmb_probe, + .pm = &ufs_rpmb_pm_ops, + }, +}; +static int __init ufshcd_core_init (void ) { +int ret; +ufs_debugfs_init (); + + ret =scsi_register_driver (&ufs_dev_wlun_template.gendrv); +if (ret) { +ufs_debugfs_exit (); +return ret; + } + ret =scsi_register_driver (&ufs_rpmb_wlun_template.gendrv); +if (ret) { +ufs_debugfs_exit (); +scsi_unregister_driver (&ufs_dev_wlun_template.gendrv); +return ret; + }return 0 ; } static void __exit ufshcd_core_exit(void) {ufs_debugfs_exit (); +scsi_unregister_driver (&ufs_dev_wlun_template.gendrv); +scsi_unregister_driver (&ufs_rpmb_wlun_template.gendrv); }module_init (ufshcd_core_init);drivers/scsi/ufs/ufshcd.h +6
-0@@ -72,6 +72,8 @@ enum ufs_event_type {UFS_EVT_LINK_STARTUP_FAIL ,UFS_EVT_RESUME_ERR ,UFS_EVT_SUSPEND_ERR , +UFS_EVT_WL_SUSP_ERR , +UFS_EVT_WL_RES_ERR ,/* abnormal events */ UFS_EVT_DEV_RESET,@@ -804,6 +806,7 @@ struct ufs_hba {struct list_head clk_list_head;bool wlun_dev_clr_ua; +bool wlun_rpmb_clr_ua;/* Number of requests aborts */ int req_abort_count;@@ -841,6 +844,7 @@ struct ufs_hba {#ifdef CONFIG_DEBUG_FS struct dentry *debugfs_root;#endif +u32 luns_avail; };/* Returns true if clocks can be gated. Otherwise false */ @@ -1100,6 +1104,8 @@ int ufshcd_exec_raw_upiu_cmd(struct ufs_hba *hba, enum query_opcode desc_op);int ufshcd_wb_ctrl (struct ufs_hba *hba,bool enable); +int ufshcd_suspend_prepare (struct device *dev); +void ufshcd_resume_complete (struct device *dev);/* Wrapper functions for safely calling variant operations */ static inline const char *ufshcd_get_var_name (struct ufs_hba *hba)include/trace/events/ufs.h +20
-0@@ -246,6 +246,26 @@ DEFINE_EVENT(ufshcd_template, ufshcd_init, int dev_state, int link_state), TP_ARGS(dev_name, err, usecs, dev_state, link_state)); +DEFINE_EVENT (ufshcd_template, ufshcd_wl_suspend, + TP_PROTO(const char *dev_name, int err, s64 usecs, + int dev_state, int link_state), +TP_ARGS (dev_name, err, usecs, dev_state, link_state)); + +DEFINE_EVENT (ufshcd_template, ufshcd_wl_resume, + TP_PROTO(const char *dev_name, int err, s64 usecs, + int dev_state, int link_state), +TP_ARGS (dev_name, err, usecs, dev_state, link_state)); + +DEFINE_EVENT (ufshcd_template, ufshcd_wl_runtime_suspend, + TP_PROTO(const char *dev_name, int err, s64 usecs, + int dev_state, int link_state), +TP_ARGS (dev_name, err, usecs, dev_state, link_state)); + +DEFINE_EVENT (ufshcd_template, ufshcd_wl_runtime_resume, + TP_PROTO(const char *dev_name, int err, s64 usecs, + int dev_state, int link_state), +TP_ARGS (dev_name, err, usecs, dev_state, link_state)); + TRACE_EVENT(ufshcd_command, TP_PROTO(const char *dev_name, enum ufs_trace_str_t str_t, unsigned int tag, u32 doorbell, int transfer_len, u32 intr,
Signature · 7 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, Linux Foundation Collaborative Project _______________________________________________ Linux-mediatek mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-mediatek
Re: [PATCH v12 1/2] scsi: ufs: Enable power management for wlun
Quoted 76 lines · Asutosh Das (asd)
Quoted 43 lines · Adrian Hunter
Quoted 38 lines · Asutosh Das
During runtime-suspend of ufs host, the scsi devices are already suspended and so are the queues associated with them. But the ufs host sends SSU to wlun during its runtime-suspend. During the process
blk_queue_enterchecks if the queue is not in suspended state. If so, it waits for the queue to resume, and never comes out of it.
The commit
(d55d15a33: scsi: block: Do not accept any requests while suspended) adds the check if the queue is in suspended state inblk_queue_enter().Call trace: __switch_to+0x174/0x2c4 __schedule+0x478/0x764 schedule+0x9c/0xe0 blk_queue_enter+0x158/0x228 blk_mq_alloc_request+0x40/0xa4 blk_get_request+0x2c/0x70 __scsi_execute+0x60/0x1c4 ufshcd_set_dev_pwr_mode+0x124/0x1e4 ufshcd_suspend+0x208/0x83c ufshcd_runtime_suspend+0x40/0x154 ufshcd_pltfrm_runtime_suspend+0x14/0x20 pm_generic_runtime_suspend+0x28/0x3c __rpm_callback+0x80/0x2a4 rpm_suspend+0x308/0x614 rpm_idle+0x158/0x228 pm_runtime_work+0x84/0xac process_one_work+0x1f0/0x470 worker_thread+0x26c/0x4c8 kthread+0x13c/0x320 ret_from_fork+0x10/0x18Fix this by registering ufs device wlun as a scsi driver and registering it for block runtime-pm. Also make this as a supplier for all other luns. That way, this device wlun suspends after all the consumers and resumes after hba resumes.
Quoted 3 lines
Co-developed-by Can Guo <[email protected]>Signed-off-by Can Guo <[email protected]>Signed-off-by Asutosh Das <[email protected]>Quoted 8 lines
I have some more comments that may help straighten things out.
Also please look at
ufs_debugfs_get_user_access()andufs_debugfs_put_user_access()that now need to scsi_autopm_get/put_devicesdev_ufs_device.It would also be good if you could re-base on linux-next.
Hi Adrian
Thanks for the comments.I agree moving the code to wlun probe and other changes. But it looks to me that it may not fully solve the issue.
Please let me explain my understanding on this:
(Please refer to the logs in v10)
scsi_autopm_*() are invoked on a sdev. pm_runtime_get_suppliers()/rpm_put_suppliers() are on the supplier device.For the device wlun:
slave_configure():
- doesn't set therpm_autosuspend
-pm_runtime_getnoresume()
scsi_sysfs_add_sdev():
-pm_runtime_forbid()
-scsi_autopm_get_device()
-device_add()
-ufshcd_wl_probe()
-scsi_autopm_put_device()
Quoted 4 lines
For all other scsi devices:
slave_alloc():
-ufshcd_setup_links()
Say alllink_add:pm_runtime_put(&hba->sdev_ufs_device->sdev_gendev);
With DL_FLAG_RPM_ACTIVE, links will 'get' not 'put'
Quoted 8 lines
slave_configure():
- setrpm_autosuspendscsi_sysfs_add_sdev():
-scsi_autopm_get_device()
-device_add()-> schedules an asyncprobe()
-scsi_autopm_put_device()- (1)Now the
rpm_put_suppliers()can be invoked *after*pm_runtime_get_suppliers()of the asyncprobe(), since both are running in different contexts.
Only if the sd device suspends.
Quoted 1 line
In that case, the
usage_countof supplier would be decremented untilrpm_activeof this link becomes 1.
Right, because the sd device suspended.
Quoted 1 line
Now the
pm_runtime_get_suppliers()expects thelink_activeto be more than 1.
Not sure what you mean here. pm_runtime_*put*_suppliers() won't do anything if the link count is 1.
Quoted 2 lines
Now then, there comes a time, that when
sd_probe()schedules a suspend, the supplierusage_countbecomes 0 and thelink_activebecomes 1. And the supplier suspends before the consumer.
sd probe first resumes the sd device which will resume the supplier.
Quoted 5 lines
So I was wondering, what'd make sure that the
pm_runtime_get_suppliers()fromdriver_probe_device()is invoked afterscsi_autopm_put_device()(1) finishes therpm_put_suppliers().Am not sure if I'm missing something in this.
Do you think, the current changes alone can fix the above issue?
Yes, but let's see.
Quoted 1,275 lines
-asd
Quoted 1,270 lines
Quoted 286 lines
---
drivers/scsi/ufs/cdns-pltfrm.c | 2 +drivers/scsi/ufs/tc-dwc-g210-pci.c| 2 + drivers/scsi/ufs/ufs-debugfs.c | 2 +- drivers/scsi/ufs/ufs-debugfs.h | 2 +- drivers/scsi/ufs/ufs-exynos.c | 2 + drivers/scsi/ufs/ufs-hisi.c | 2 + drivers/scsi/ufs/ufs-mediatek.c | 12 +- drivers/scsi/ufs/ufs-qcom.c | 2 + drivers/scsi/ufs/ufs_bsg.c | 6 +- drivers/scsi/ufs/ufshcd-pci.c | 36 +-- drivers/scsi/ufs/ufshcd.c | 622 ++++++++++++++++++++++++++----------- drivers/scsi/ufs/ufshcd.h | 6 + include/trace/events/ufs.h | 20 ++13 files changed, 491 insertions(+), 225 deletions(-)drivers/scsi/ufs/cdns-pltfrm.c +2
-0@@ -319,6 +319,8 @@ static const struct dev_pm_ops cdns_ufs_dev_pm_ops = { .runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_idle = ufshcd_pltfrm_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, }; static struct platform_driver cdns_ufs_pltfrm_driver = {drivers/scsi/ufs/tc-dwc-g210-pci.c +2
-0@@ -148,6 +148,8 @@ static const struct dev_pm_ops tc_dwc_g210_pci_pm_ops = { .runtime_suspend = tc_dwc_g210_pci_runtime_suspend, .runtime_resume = tc_dwc_g210_pci_runtime_resume, .runtime_idle = tc_dwc_g210_pci_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, }; static const struct pci_device_id tc_dwc_g210_pci_tbl[] = {drivers/scsi/ufs/ufs-debugfs.c +1
-0@@ -13,7 +13,7 @@ void __init ufs_debugfs_init(void) ufs_debugfs_root = debugfs_create_dir("ufshcd", NULL); } -void __exit ufs_debugfs_exit(void) +voidufs_debugfs_exit (void) { debugfs_remove_recursive(ufs_debugfs_root); }drivers/scsi/ufs/ufs-debugfs.h +1
-1@@ -9,7 +9,7 @@ struct ufs_hba; #ifdef CONFIG_DEBUG_FS void __init ufs_debugfs_init(void); -void __exit ufs_debugfs_exit(void); +void ufs_debugfs_exit (void ); void ufs_debugfs_hba_init(struct ufs_hba *hba); void ufs_debugfs_hba_exit(struct ufs_hba *hba); #elsedrivers/scsi/ufs/ufs-exynos.c +2
-0@@ -1268,6 +1268,8 @@ static const struct dev_pm_ops exynos_ufs_pm_ops = { .runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_idle = ufshcd_pltfrm_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, }; static struct platform_driver exynos_ufs_pltform = {drivers/scsi/ufs/ufs-hisi.c +2
-0@@ -574,6 +574,8 @@ static const struct dev_pm_ops ufs_hisi_pm_ops = { .runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_idle = ufshcd_pltfrm_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, }; static struct platform_driver ufs_hisi_pltform = {drivers/scsi/ufs/ufs-mediatek.c +0
-0@@ -810,12 +810,10 @@ static int ufs_mtk_post_link(struct ufs_hba *hba) /* enable unipro clock gating feature */ ufs_mtk_cfg_unipro_cg(hba, true);- /* configure auto-hibern8 timer to 10ms */
- if (ufshcd_is_auto_hibern8_supported(hba)) {
- ufshcd_auto_hibern8_update(hba,
-FIELD_PREP(UFSHCI_AHIBERN8_TIMER_MASK, 10)|
-FIELD_PREP(UFSHCI_AHIBERN8_SCALE_MASK, 3));
- }
+ /* will be configured during probe hba */
+ if (ufshcd_is_auto_hibern8_supported(hba))
+hba->ahit=FIELD_PREP(UFSHCI_AHIBERN8_TIMER_MASK, 10)| +FIELD_PREP(UFSHCI_AHIBERN8_SCALE_MASK, 3);ufs_mtk_setup_clk_gating(hba);
@@ -1097,6 +1095,8 @@ static const structdev_pm_opsufs_mtk_pm_ops= { .runtime_suspend =ufshcd_pltfrm_runtime_suspend, .runtime_resume =ufshcd_pltfrm_runtime_resume, .runtime_idle =ufshcd_pltfrm_runtime_idle,
+ .prepare =ufshcd_suspend_prepare,
+ .complete =ufshcd_resume_complete,
};
static structplatform_driverufs_mtk_pltform= {drivers/scsi/ufs/ufs-qcom.c +2
-0@@ -1546,6 +1546,8 @@ static const struct dev_pm_ops ufs_qcom_pm_ops = { .runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_idle = ufshcd_pltfrm_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, }; static struct platform_driver ufs_qcom_pltform = {drivers/scsi/ufs/ufs_bsg.c +2
-1@@ -97,7 +97,7 @@ static int ufs_bsg_request(struct bsg_job *job) bsg_reply->reply_payload_rcv_len = 0; - pm_runtime_get_sync(hba->dev); + scsi_autopm_get_device(hba->sdev_ufs_device); msgcode = bsg_request->msgcode; switch (msgcode) {@@ -106,7 +106,7 @@ static int ufs_bsg_request(struct bsg_job *job) ret = ufs_bsg_alloc_desc_buffer(hba, job, &desc_buff, &desc_len, desc_op); if (ret) { - pm_runtime_put_sync(hba->dev); + scsi_autopm_put_device(hba->sdev_ufs_device); goto out; } @@ -138,7 +138,7 @@ static int ufs_bsg_request(struct bsg_job *job)break;
}
-pm_runtime_put_sync(hba->dev);
+scsi_autopm_put_device(hba->sdev_ufs_device); if (!desc_buff)
goto out;drivers/scsi/ufs/ufshcd-pci.c +0
-0@@ -247,29 +247,6 @@ static int ufshcd_pci_resume(struct device *dev) return ufshcd_system_resume(dev_get_drvdata(dev)); }-/**
- *ufshcd_pci_poweroff- suspend-to-disk poweroff function
- * @dev: pointer to PCI device handle
- *
- * Returns 0 if successful
- * Returns non-zero otherwise
- */drivers/scsi/ufs/ufshcd-pci.c +0
-16-static int ufshcd_pci_poweroff (struct device *dev) -{ - struct ufs_hba *hba = dev_get_drvdata(dev); - int spm_lvl = hba->spm_lvl; - int ret; - - /* - * For poweroff we need to set the UFS device to PowerDown mode. - * Force spm_lvl to ensure that. - */ - hba->spm_lvl = 5; - ret = ufshcd_system_suspend(hba); - hba->spm_lvl = spm_lvl; - return ret; -} -#endif /* !CONFIG_PM_SLEEP */
#ifdefCONFIG_PMdrivers/scsi/ufs/ufshcd-pci.c +5
-8@@ -365,17 +342,14 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) } static const struct dev_pm_ops ufshcd_pci_pm_ops = { -#ifdef CONFIG_PM_SLEEP - .suspend = ufshcd_pci_suspend, - .resume = ufshcd_pci_resume, - .freeze = ufshcd_pci_suspend, - .thaw = ufshcd_pci_resume, - .poweroff = ufshcd_pci_poweroff, - .restore = ufshcd_pci_resume, -#endif SET_RUNTIME_PM_OPS(ufshcd_pci_runtime_suspend, ufshcd_pci_runtime_resume, ufshcd_pci_runtime_idle) + SET_SYSTEM_SLEEP_PM_OPS(ufshcd_pci_suspend, ufshcd_pci_resume) +#ifdef CONFIG_PM_SLEEP + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, +#endif }; static const struct pci_device_id ufshcd_pci_tbl[] = {drivers/scsi/ufs/ufshcd.c +4
-1@@ -16,6 +16,7 @@ #include <linux/bitfield.h> #include <linux/blk-pm.h> #include <linux/blkdev.h> +#include <scsi/scsi_driver.h> #include "ufshcd.h" #include "ufs_quirks.h" #include "unipro.h"@@ -78,6 +79,8 @@ /* Polling time to wait for fDeviceInit */ #define FDEVICEINIT_COMPL_TIMEOUT 1500 /* millisecs */ +#define wlun_dev_to_hba(dv) shost_priv(to_scsi_device(dv)->host) + #define ufshcd_toggle_vreg(_dev, _vreg, _on) \ ({ \ int _ret; \@@ -1556,7 +1559,7 @@ static ssize_t ufshcd_clkscale_enable_store(struct device *dev, if (value == hba->clk_scaling.is_enabled) goto out; - pm_runtime_get_sync(hba->dev); + scsi_autopm_get_device(hba->sdev_ufs_device); ufshcd_hold(hba, false); hba->clk_scaling.is_enabled = value;@@ -1572,7 +1575,7 @@ static ssize_t ufshcd_clkscale_enable_store(struct device *dev, } ufshcd_release(hba); - pm_runtime_put_sync(hba->dev); + scsi_autopm_put_device(hba->sdev_ufs_device);out:
up(&hba->host_sem);
return err ? err : count;drivers/scsi/ufs/ufshcd.c +26
-2@@ -2572,6 +2575,17 @@ static inline u16 ufshcd_upiu_wlun_to_scsi_wlun(u8 upiu_wlun_id) return (upiu_wlun_id & ~UFS_UPIU_WLUN_ID) | SCSI_W_LUN_BASE; } +static inline bool is_rpmb_wlun(struct scsi_device *sdev) +{ + return (sdev->lun == ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_RPMB_WLUN)); +} + +static inline bool is_device_wlun (struct scsi_device *sdev) +{ + return (sdev->lun == + ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_UFS_DEVICE_WLUN)); +} + static void ufshcd_init_lrb(struct ufs_hba *hba, struct ufshcd_lrb *lrb, int i) { struct utp_transfer_cmd_desc *cmd_descp = hba->ucdl_base_addr;@@ -4106,11 +4120,11 @@ void ufshcd_auto_hibern8_update(struct ufs_hba *hba, u32 ahit) spin_unlock_irqrestore(hba->host->host_lock, flags); if (update && !pm_runtime_suspended(hba->dev)) { - pm_runtime_get_sync(hba->dev); + scsi_autopm_get_device(hba->sdev_ufs_device); ufshcd_hold(hba, false); ufshcd_auto_hibern8_enable(hba); ufshcd_release(hba); - pm_runtime_put(hba->dev); + scsi_autopm_put_device(hba->sdev_ufs_device); } } EXPORT_SYMBOL_GPL(ufshcd_auto_hibern8_update);@@ -4808,6 +4822,41 @@ static inline void ufshcd_get_lu_power_on_wp_status(struct ufs_hba *hba, } /** +* ufshcd_setup_links - associate link b/w device wlun and other luns +* @sdev: pointer to SCSI device +* @hba: pointer to ufs hba +*/ +static void ufshcd_setup_links (struct ufs_hba *hba,struct scsi_device *sdev) +{ + struct device_link *link; + + /* + * device wlun is the supplier & rest of the luns are consumers + * This ensures that device wlun suspends after all other luns. + */ + if (hba->sdev_ufs_device) { + link = device_link_add(&sdev->sdev_gendev,drivers/scsi/ufs/ufshcd.c static inline void ufshcd_get_lu_power_on_wp_status(struct ufs_hba *hba, · line 4847
Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10
-0+ &hba->sdev_ufs_device->sdev_gendev, + DL_FLAG_PM_RUNTIME|DL_FLAG_RPM_ACTIVE); + if (!link) { + dev_err(&sdev->sdev_gendev, "Failed establishing link - %s\n", + dev_name(&hba->sdev_ufs_device->sdev_gendev)); + return; + } + hba->luns_avail--; + /* Ignore REPORT_LUN wlun probing */ + if (hba->luns_avail != 1) {The condition here is the wrong way around. i.e. should be
if (
hba->luns_avail== 1) {drivers/scsi/ufs/ufshcd.c static inline void ufshcd_get_lu_power_on_wp_status(struct ufs_hba *hba, · line 4848
Quoted 1 line
drivers/scsi/ufs/ufshcd.c +1
-0+ pm_runtime_put_noidle(&hba->sdev_ufs_device->sdev_gendev);If the
usage_countis zero here, then we should let it suspend i.e. make this:
pm_runtime_put(&hba->sdev_ufs_device->sdev_gendev);Note this pairs with the
pm_runtime_get_noresume()that needs to be added to ufshcd_slave_configure. Please see comments forufshcd_slave_configure().drivers/scsi/ufs/ufshcd.c static inline void ufshcd_get_lu_power_on_wp_status(struct ufs_hba *hba, · line 4850
Quoted 1 line
drivers/scsi/ufs/ufshcd.c +1
-0+ pm_runtime_allow(&hba->sdev_ufs_device->sdev_gendev);This
pm_runtime_allowshould not be here. It should be inufshcd_wl_probe(). Please see comments forufshcd_wl_probe().Quoted 22 lines
drivers/scsi/ufs/ufshcd.c +10
-0+ return; + } + } else { + /* device wlun is probed */ + hba->luns_avail--; + } +} + +/** * ufshcd_slave_alloc - handle initial SCSI device configurations * @sdev: pointer to SCSI device * @@ -4838,6 +4887,8 @@ static int ufshcd_slave_alloc(struct scsi_device *sdev) ufshcd_get_lu_power_on_wp_status(hba, sdev); + ufshcd_setup_links(hba, sdev); + return 0; } @@ -4869,8 +4920,13 @@ static int ufshcd_slave_configure(struct scsi_device *sdev) blk_queue_update_dma_pad(q, PRDT_DATA_BYTE_COUNT_PAD - 1);if (
hba->quirks&UFSHCD_QUIRK_ALIGN_SG_WITH_PAGE_SIZE)blk_queue_update_dma_alignment(q, PAGE_SIZE - 1);Quoted 8 lines
-
- if (ufshcd_is_rpm_autosuspend_allowed(hba)) + /*
+ * Block runtime-pm until all consumers are added. + * Referufshcd_setup_links().
+ */
+ if (is_device_wlun(sdev))
+pm_runtime_forbid(&sdev->sdev_gendev);Since
rpm_autosuspendis not being set forsdev_ufs_device,scsi_sysfs_add_sdev()willpm_runtime_forbid()it anyway. We should increase theusage_countinstead i.e.
pm_runtime_get_noresume(&sdev->sdev_gendev);Quoted 109 lines
+ else if (
ufshcd_is_rpm_autosuspend_allowed(hba))sdev->rpm_autosuspend= 1;ufshcd_crypto_setup_rq_keyslot_manager(hba, q);drivers/scsi/ufs/ufshcd.c +0
-0@@ -4985,15 +5041,9 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)* UFS device needs urgent BKOPs. */ if (!hba->pm_op_in_progress &&
-ufshcd_is_exception_event(lrbp->ucd_rsp_ptr)&& -schedule_work(&hba->eeh_work)) {
- /*
- * Prevent suspend onceeeh_workis scheduled - * to avoid deadlock betweenufshcd_suspend- * and exception event handler.
- */
-pm_runtime_get_noresume(hba->dev);
- }
+ufshcd_is_exception_event(lrbp->ucd_rsp_ptr)) + /* Flushed in suspend */
+schedule_work(&hba->eeh_work);
break;
caseUPIU_TRANSACTION_REJECT_UPIU:
/* TODO: handle Reject UPIU Response */drivers/scsi/ufs/ufshcd.c +2
-2@@ -5589,8 +5639,8 @@ static void ufshcd_rpm_dev_flush_recheck_work(struct work_struct *work)* after a certain delay to recheck the threshold by next runtime * suspend. */ - pm_runtime_get_sync(hba->dev); - pm_runtime_put_sync(hba->dev); + scsi_autopm_get_device(hba->sdev_ufs_device); + scsi_autopm_put_device(hba->sdev_ufs_device); } /**@@ -5607,7 +5657,6 @@ static void ufshcd_exception_event_handler(struct work_struct *work) u32 status = 0; hba = container_of(work, struct ufs_hba, eeh_work); - pm_runtime_get_sync(hba->dev); ufshcd_scsi_block_requests(hba); err = ufshcd_get_ee_status(hba, &status); if (err) {@@ -5623,14 +5672,6 @@ static void ufshcd_exception_event_handler(struct work_struct *work)out:
ufshcd_scsi_unblock_requests(hba);
- /*
- *pm_runtime_get_noresumeis called while scheduling
- *eeh_workto avoid suspend racing with exception work.
- * Hence decrement usage counter usingpm_runtime_put_noidle
- * to allow suspend on completion of exception event handler.
- */
-pm_runtime_put_noidle(hba->dev);
-pm_runtime_put(hba->dev);
return;
}
@@ -5755,12 +5796,13 @@ static voidufshcd_clk_scaling_suspend(struct ufs_hba *hba, bool suspend)static voidufshcd_err_handling_prepare(struct ufs_hba *hba)
{
-pm_runtime_get_sync(hba->dev);
- if (pm_runtime_status_suspended(hba->dev)||hba->is_sys_suspended) {
+scsi_autopm_get_device(hba->sdev_ufs_device);
+ if (pm_runtime_status_suspended(&hba->sdev_ufs_device->sdev_gendev)||
+hba->is_sys_suspended) {
enumufs_pm_oppm_op;
/*
- * Don't assume anything ofpm_runtime_get_sync(), if
+ * Don't assume anything of resume, if
* resume fails, irq and clocks can be OFF, and powers
* can be OFF or in LPM.
*/drivers/scsi/ufs/ufshcd.c +9
-8@@ -5789,7 +5831,7 @@ static void ufshcd_err_handling_unprepare(struct ufs_hba *hba) ufshcd_release(hba); if (ufshcd_is_clkscaling_supported(hba)) ufshcd_clk_scaling_suspend(hba, false); - pm_runtime_put(hba->dev); + pm_runtime_put(&hba->sdev_ufs_device->sdev_gendev); } static inline bool ufshcd_err_handling_should_stop(struct ufs_hba *hba)@@ -5810,14 +5852,14 @@ static void ufshcd_recover_pm_error(struct ufs_hba *hba) hba->is_sys_suspended = false; /* - * Set RPM status of hba device to RPM_ACTIVE, + * Set RPM status of wlun device to RPM_ACTIVE, * this also clears its runtime error. */ - ret = pm_runtime_set_active(hba->dev); + ret = pm_runtime_set_active(&hba->sdev_ufs_device->sdev_gendev); /* - * If hba device had runtime error, we also need to resume those - * scsi devices under hba in case any of them has failed to be - * resumed due to hba runtime resume failure. This is to unblock + * If wlun device had runtime error, we also need to resume those + * consumer scsi devices in case any of them has failed to be + * resumed due to supplier runtime resume failure. This is to unblock * blk_queue_enter in case there are bios waiting inside it. */ if (!ret) {@@ -7207,11 +7249,12 @@ static void ufshcd_set_active_icc_lvl(struct ufs_hba *hba) static inline void ufshcd_blk_pm_runtime_init(struct scsi_device *sdev) { + int dly = is_device_wlun(sdev) ? 0:RPM_AUTOSUSPEND_DELAY_MS; + scsi_autopm_get_device(sdev); blk_pm_runtime_init(sdev->request_queue, &sdev->sdev_gendev); if (sdev->rpm_autosuspend) - pm_runtime_set_autosuspend_delay(&sdev->sdev_gendev, - RPM_AUTOSUSPEND_DELAY_MS); + pm_runtime_set_autosuspend_delay(&sdev->sdev_gendev, dly); scsi_autopm_put_device(sdev); } @@ -7253,6 +7296,7 @@ static int ufshcd_scsi_add_wlus(struct ufs_hba *hba) hba->sdev_ufs_device = NULL;Quoted 4 lines
goto out;
}
+scsi_autopm_get_device(hba->sdev_ufs_device);ufshcd_blk_pm_runtime_init(hba->sdev_ufs_device);Let's drop both
scsi_autopm_get_device()andufshcd_blk_pm_runtime_init()here and instead do things inufshcd_wl_probe()- see comments forufshcd_wl_probe()Quoted 589 lines
scsi_device_put(hba->sdev_ufs_device);
@@ -7417,6 +7461,9 @@ static intufs_get_device_desc(struct ufs_hba *hba)goto out;
}
+hba->luns_avail= desc_buf[DEVICE_DESC_PARAM_NUM_LU] +
+ desc_buf[DEVICE_DESC_PARAM_NUM_WLU];
+
ufs_fixup_device_setup(hba);
ufshcd_wb_probe(hba, desc_buf);drivers/scsi/ufs/ufshcd.c +3
-1@@ -7892,6 +7939,7 @@ static int ufshcd_probe_hba(struct ufs_hba *hba, bool async) ufshcd_set_ufs_dev_active(hba); ufshcd_force_reset_auto_bkops(hba); hba->wlun_dev_clr_ua = true; + hba->wlun_rpmb_clr_ua = true; /* Gear up to HS gear if supported */ if (hba->max_pwr_info.is_valid) {@@ -8475,7 +8523,8 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba,* handling context. */ hba->host->eh_noresume = 1; - ufshcd_clear_ua_wluns(hba); + if (hba->wlun_dev_clr_ua) + ufshcd_clear_ua_wlun(hba, UFS_UPIU_UFS_DEVICE_WLUN); cmd[4] = pwr_mode << 4; @@ -8650,23 +8699,7 @@ static void ufshcd_hba_vreg_set_hpm(struct ufs_hba *hba) ufshcd_setup_hba_vreg(hba, true);}
-/**
- *ufshcd_suspend- helper function for suspend operations
- * @hba: per adapter instance
- * @pm_op: desired low power operation type
- *
- * This function will try to put the UFS device and link into low power
- * mode based on the "rpm_lvl" (Runtime PM level) or "spm_lvl"
- * (System PM level).
- *
- * If this function is called during shutdown, it will make sure that
- * both UFS device and UFS link is powered off.
- *
- * NOTE: UFS device & link must be active before we enter in this function.
- *
- * Returns 0 for success and non-zero for failure
- */drivers/scsi/ufs/ufshcd.c +1
-1-static int ufshcd_suspend (struct ufs_hba *hba,enum ufs_pm_op pm_op) +static int __ufshcd_wl_suspend (struct ufs_hba *hba,enum ufs_pm_op pm_op){
int ret = 0;
intcheck_for_bkops;drivers/scsi/ufs/ufshcd.c +1
-0@@ -8674,7 +8707,7 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) enum ufs_dev_pwr_mode req_dev_pwr_mode; enum uic_link_state req_link_state; - hba->pm_op_in_progress = 1; + hba->pm_op_in_progress = true; if (!ufshcd_is_shutdown_pm(pm_op)) {
pm_lvl=ufshcd_is_runtime_pm(pm_op)?
hba->rpm_lvl:hba->spm_lvl;drivers/scsi/ufs/ufshcd.c +0
-0@@ -8697,17 +8730,17 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)if (
req_dev_pwr_mode==UFS_ACTIVE_PWR_MODE&&req_link_state==UIC_LINK_ACTIVE_STATE) {
- gotodisable_clks;
+ gotoenable_scaling;
}
if ((req_dev_pwr_mode==hba->curr_dev_pwr_mode) && (req_link_state==hba->uic_link_state))
- gotoenable_gating;
+ gotoenable_scaling;
/* UFS device & link must be active before we enter in this function */ if (!ufshcd_is_ufs_dev_active(hba) || !ufshcd_is_link_active(hba)) { ret =-EINVAL;
- gotoenable_gating;
+ gotoenable_scaling;
}
if (ufshcd_is_runtime_pm(pm_op)) {drivers/scsi/ufs/ufshcd.c +2
-2@@ -8719,7 +8752,7 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)*/ ret = ufshcd_urgent_bkops(hba); if (ret) - goto enable_gating; + goto enable_scaling; } else { /* make sure that auto bkops is disabled */ ufshcd_disable_auto_bkops(hba);@@ -8747,7 +8780,7 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) if (!hba->dev_info.b_rpm_dev_flush_capable) { ret = ufshcd_set_dev_pwr_mode(hba, req_dev_pwr_mode); if (ret) - goto enable_gating; + goto enable_scaling; } } @@ -8760,7 +8793,6 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)if (ret)
gotoset_dev_active;
-disable_clks:
/*
* Call vendor specific suspend callback. As these callbacks may access
* vendor specific host controller register space call them before thedrivers/scsi/ufs/ufshcd.c +0
-18@@ -8769,28 +8801,9 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) ret = ufshcd_vops_suspend(hba, pm_op); if (ret) goto set_link_active; - /* - * Disable the host irq as host controller as there won't be any - * host controller transaction expected till resume. - */ - ufshcd_disable_irq(hba); - - ufshcd_setup_clocks(hba, false); - - if (ufshcd_is_clkgating_allowed(hba)) { - hba->clk_gating.state = CLKS_OFF; - trace_ufshcd_clk_gating(dev_name(hba->dev), - hba->clk_gating.state); - } - - ufshcd_vreg_set_lpm(hba); - - /* Put the host controller in low power mode if possible */ - ufshcd_hba_vreg_set_lpm(hba); goto out;
set_link_active:
-ufshcd_vreg_set_hpm(hba);
/*
* Device hardware reset is required to exit DeepSleep. Also, for
* DeepSleep, the link is off so host reset and restore will be donedrivers/scsi/ufs/ufshcd.c +1
-3@@ -8812,57 +8825,32 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) } if (!ufshcd_set_dev_pwr_mode(hba, UFS_ACTIVE_PWR_MODE)) ufshcd_disable_auto_bkops(hba); -enable_gating: +enable_scaling: if (ufshcd_is_clkscaling_supported(hba)) ufshcd_clk_scaling_suspend(hba, false); - hba->clk_gating.is_suspended = false; hba->dev_info.b_rpm_dev_flush_capable = false; - ufshcd_clear_ua_wluns(hba); - ufshcd_release(hba);out:
if (hba->dev_info.b_rpm_dev_flush_capable) {schedule_delayed_work(&hba->rpm_dev_flush_recheck_work, msecs_to_jiffies(RPM_DEV_FLUSH_RECHECK_WORK_DELAY_MS)); }
-hba->pm_op_in_progress= 0;
-
- if (ret)
-ufshcd_update_evt_hist(hba, UFS_EVT_SUSPEND_ERR, (u32)ret); + if (ret) {
+ufshcd_update_evt_hist(hba, UFS_EVT_WL_SUSP_ERR, (u32)ret); +hba->clk_gating.is_suspended= false;
+ufshcd_release(hba);
+ }
+hba->pm_op_in_progress= false;
return ret;
}
-/**
- *ufshcd_resume- helper function for resume operations
- * @hba: per adapter instance
- * @pm_op: runtime PM or system PM
- *
- * This function basically brings the UFS device, UniPro link and controller
- * to active state.
- *
- * Returns 0 for success and non-zero for failure
- */drivers/scsi/ufs/ufshcd.c +1
-1-static int ufshcd_resume (struct ufs_hba *hba,enum ufs_pm_op pm_op) +static int __ufshcd_wl_resume (struct ufs_hba *hba,enum ufs_pm_op pm_op){
int ret;
- enumuic_link_stateold_link_state;
+ enumuic_link_stateold_link_state=hba->uic_link_state; -hba->pm_op_in_progress= 1;
-old_link_state=hba->uic_link_state;drivers/scsi/ufs/ufshcd.c +1
-13- - ufshcd_hba_vreg_set_hpm(hba); - ret = ufshcd_vreg_set_hpm(hba); - if (ret) - goto out; - - /* Make sure clocks are enabled before accessing controller */ - ret = ufshcd_setup_clocks(hba, true); - if (ret) - goto disable_vreg; - - /* enable the host irq as host controller would be active soon */ - ufshcd_enable_irq(hba); + hba->pm_op_in_progress = true;/*
* Call vendor specific resume callback. As these callbacks may accessdrivers/scsi/ufs/ufshcd.c +5
-11@@ -8871,7 +8859,7 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)*/ ret = ufshcd_vops_resume(hba, pm_op); if (ret) - goto disable_irq_and_vops_clks; + goto out; /* For DeepSleep, the only supported option is to have the link off */ WARN_ON(ufshcd_is_ufs_dev_deepsleep(hba) && !ufshcd_is_link_off(hba));@@ -8916,31 +8904,147 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)*/ ufshcd_urgent_bkops(hba); - hba->clk_gating.is_suspended = false; - - if (ufshcd_is_clkscaling_supported(hba)) - ufshcd_clk_scaling_suspend(hba, false); - - /* Enable Auto-Hibernate if configured */ - ufshcd_auto_hibern8_enable(hba); + if (hba->clk_scaling.is_allowed) + ufshcd_resume_clkscaling(hba); if (hba->dev_info.b_rpm_dev_flush_capable) { hba->dev_info.b_rpm_dev_flush_capable = false; cancel_delayed_work(&hba->rpm_dev_flush_recheck_work); } - ufshcd_clear_ua_wluns(hba); - - /* Schedule clock gating in case of no access to UFS device yet */ - ufshcd_release(hba); - + /* Enable Auto-Hibernate if configured */ + ufshcd_auto_hibern8_enable(hba); goto out;
set_old_link_state:
ufshcd_link_state_transition(hba, old_link_state, 0);vendor_suspend:
ufshcd_vops_suspend(hba, pm_op);drivers/scsi/ufs/ufshcd.c +125
-1-disable_irq_and_vops_clks: +out: + if (ret) + ufshcd_update_evt_hist(hba, UFS_EVT_WL_RES_ERR, (u32)ret); + hba->clk_gating.is_suspended = false; + ufshcd_release(hba); + hba->pm_op_in_progress = false; + return ret; +} + +static int ufshcd_wl_runtime_suspend (struct device *dev) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct ufs_hba *hba; + int ret; + ktime_t start = ktime_get(); + + hba = shost_priv(sdev->host); + + ret = __ufshcd_wl_suspend(hba, UFS_RUNTIME_PM); + if (ret) + dev_err(&sdev->sdev_gendev, "%s failed: %d\n", __func__, ret); + + trace_ufshcd_wl_runtime_suspend(dev_name(dev), ret, + ktime_to_us(ktime_sub(ktime_get(), start)), + hba->curr_dev_pwr_mode, hba->uic_link_state); + + return ret; +} + +static int ufshcd_wl_runtime_resume (struct device *dev) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct ufs_hba *hba; + int ret = 0; + ktime_t start = ktime_get(); + + hba = shost_priv(sdev->host); + + ret = __ufshcd_wl_resume(hba, UFS_RUNTIME_PM); + if (ret) + dev_err(&sdev->sdev_gendev, "%s failed: %d\n", __func__, ret); + + trace_ufshcd_wl_runtime_resume(dev_name(dev), ret, + ktime_to_us(ktime_sub(ktime_get(), start)), + hba->curr_dev_pwr_mode, hba->uic_link_state); + + return ret; +} + +#ifdef CONFIG_PM_SLEEP +static int ufshcd_wl_suspend (struct device *dev) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct ufs_hba *hba; + int ret; + ktime_t start = ktime_get(); + + hba = shost_priv(sdev->host); + ret = __ufshcd_wl_suspend(hba, UFS_SYSTEM_PM); + if (ret) + dev_err(&sdev->sdev_gendev, "%s failed: %d\n", __func__, ret); + + trace_ufshcd_wl_suspend(dev_name(dev), ret, + ktime_to_us(ktime_sub(ktime_get(), start)), + hba->curr_dev_pwr_mode, hba->uic_link_state); + + return ret; +} + +static int ufshcd_wl_resume (struct device *dev) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct ufs_hba *hba; + int ret = 0; + ktime_t start = ktime_get(); + + if (pm_runtime_suspended(dev)) + return 0; + hba = shost_priv(sdev->host); + + ret = __ufshcd_wl_resume(hba, UFS_SYSTEM_PM); + if (ret) + dev_err(&sdev->sdev_gendev, "%s failed: %d\n", __func__, ret); + + trace_ufshcd_wl_resume(dev_name(dev), ret, + ktime_to_us(ktime_sub(ktime_get(), start)), + hba->curr_dev_pwr_mode, hba->uic_link_state); + + return ret; +} +#endif + +static void ufshcd_wl_shutdown (struct device *dev) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct ufs_hba *hba; + + hba = shost_priv(sdev->host); + /* Turn on everything while shutting down */ + scsi_autopm_get_device(sdev); + scsi_device_quiesce(sdev); + shost_for_each_device(sdev, hba->host) { + if (sdev == hba->sdev_ufs_device) + continue; + scsi_device_quiesce(sdev); + } + __ufshcd_wl_suspend(hba, UFS_SHUTDOWN_PM); +} + +/** +* ufshcd_suspend - helper function for suspend operations +* @hba: per adapter instance +* +* This function will put disable irqs, turn off clocks +* and set vreg and hba-vreg in lpm mode. +* Also check the description of __ufshcd_wl_suspend(). +*/ +static void ufshcd_suspend (struct ufs_hba *hba) +{ + hba->pm_op_in_progress = 1; + + /* +* Disable the host irq as host controller as there won't be any +* host controller transaction expected till resume. +*/
ufshcd_disable_irq(hba);
ufshcd_setup_clocks(hba, false);
if (ufshcd_is_clkgating_allowed(hba)) {drivers/scsi/ufs/ufshcd.c +37
-0@@ -8948,6 +9052,43 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) trace_ufshcd_clk_gating(dev_name(hba->dev), hba->clk_gating.state); } + + ufshcd_vreg_set_lpm(hba); + /* Put the host controller in low power mode if possible */ + ufshcd_hba_vreg_set_lpm(hba); + hba->pm_op_in_progress = 0; +} + +/** +* ufshcd_resume - helper function for resume operations +* @hba: per adapter instance +* +* This function basically turns on the regulators, clocks and +* irqs of the hba. +* Also check the description of __ufshcd_wl_resume(). +* +* Returns 0 for success and non-zero for failure +*/ +static int ufshcd_resume (struct ufs_hba *hba) +{ + int ret; + + hba->pm_op_in_progress = 1; + + ufshcd_hba_vreg_set_hpm(hba); + ret = ufshcd_vreg_set_hpm(hba); + if (ret) + goto out; + + /* Make sure clocks are enabled before accessing controller */ + ret = ufshcd_setup_clocks(hba, true); + if (ret) + goto disable_vreg; + + /* enable the host irq as host controller would be active soon */ + ufshcd_enable_irq(hba); + goto out; +
disable_vreg:
ufshcd_vreg_set_lpm(hba);
out:drivers/scsi/ufs/ufshcd.c +2
-14@@ -8962,6 +9103,7 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)* @hba: per adapter instance * * Check the description of ufshcd_suspend() function for more details. +* Also check the description of __ufshcd_wl_suspend(). * * Returns 0 for success and non-zero for failure */ @@ -8987,21 +9129,7 @@ int ufshcd_system_suspend(struct ufs_hba *hba) !hba->dev_info.b_rpm_dev_flush_capable) goto out; - if (pm_runtime_suspended(hba->dev)) { - /* - * UFS device and/or UFS link low power states during runtime - * suspend seems to be different than what is expected during - * system suspend. Hence runtime resume the devic & link and - * let the system suspend low power states to take effect. - * TODO: If resume takes longer time, we might have optimize - * it in future by not resuming everything if possible. - */ - ret = ufshcd_runtime_resume(hba); - if (ret) - goto out; - } - - ret = ufshcd_suspend(hba, UFS_SYSTEM_PM); + ufshcd_suspend(hba);out:
trace_ufshcd_system_suspend(dev_name(hba->dev), ret,ktime_to_us(ktime_sub(ktime_get(), start)),drivers/scsi/ufs/ufshcd.c +1
-5@@ -9023,7 +9151,6 @@ EXPORT_SYMBOL(ufshcd_system_suspend); int ufshcd_system_resume(struct ufs_hba *hba) { - int ret = 0; ktime_t start = ktime_get(); if (!hba)@@ -9034,22 +9161,18 @@ int ufshcd_system_resume(struct ufs_hba *hba) down(&hba->host_sem); } - if (!hba->is_powered || pm_runtime_suspended(hba->dev)) - /* - * Let the runtime resume take care of resuming - * if runtime suspended. - */ + if (!hba->is_powered) goto out;else
- ret =ufshcd_resume(hba, UFS_SYSTEM_PM);
+ufshcd_resume(hba);
out:
- trace_ufshcd_system_resume(dev_name(hba->dev), ret,
+ trace_ufshcd_system_resume(dev_name(hba->dev), 0,ktime_to_us(ktime_sub(ktime_get(), start)),hba->curr_dev_pwr_mode,hba->uic_link_state);
- if (!ret)
-hba->is_sys_suspended= false;
+
+hba->is_sys_suspended= false;
up(&hba->host_sem);
- return ret;
+ return 0;
}
EXPORT_SYMBOL(ufshcd_system_resume);
@@ -9058,12 +9181,12 @@EXPORT_SYMBOL(ufshcd_system_resume);
* @hba: per adapter instance
*
* Check the description ofufshcd_suspend()function for more details.
+ * Also check the description of__ufshcd_wl_suspend().
*
* Returns 0 for success and non-zero for failure
*/
intufshcd_runtime_suspend(struct ufs_hba *hba)
{
- int ret = 0;
ktime_tstart =ktime_get();
if (!hba)drivers/scsi/ufs/ufshcd.c +0
-0@@ -9072,12 +9195,12 @@ int ufshcd_runtime_suspend(struct ufs_hba *hba) if (!hba->is_powered) goto out;else
- ret =ufshcd_suspend(hba, UFS_RUNTIME_PM);
+ufshcd_suspend(hba);
out:
- trace_ufshcd_runtime_suspend(dev_name(hba->dev), ret,
+ trace_ufshcd_runtime_suspend(dev_name(hba->dev), 0,ktime_to_us(ktime_sub(ktime_get(), start)),hba->curr_dev_pwr_mode,hba->uic_link_state);
- return ret;
+ return 0;
}
EXPORT_SYMBOL(ufshcd_runtime_suspend);
@@ -9085,26 +9208,14 @@EXPORT_SYMBOL(ufshcd_runtime_suspend);
*ufshcd_runtime_resume- runtime resume routine
* @hba: per adapter instance
*
- * This function basically brings the UFS device, UniPro link and controller
+ * This function basically brings controller
* to active state. Following operations are done in this function:
*
* 1. Turn on all the controller related clocks
- * 2. Bring the UniPro link out of Hibernate state
- * 3. If UFS device is in sleep state, turn ON VCC rail and bring the UFS device
- * to active state.
- * 4. If auto-bkops is enabled on the device, disable it.
- *
- * So following would be the possible power state after this function return
- * successfully:
- * S1: UFS device in Active state with VCC rail ON
- * UniPro link in Active state
- * All the UFS/UniPro controller clocks are ON
- *
- * Returns 0 for success and non-zero for failure
+ * 2. Turn ON VCC rail
*/
intufshcd_runtime_resume(struct ufs_hba *hba)
{
- int ret = 0;
ktime_tstart =ktime_get();
if (!hba)drivers/scsi/ufs/ufshcd.c +0
-0@@ -9113,12 +9224,12 @@ int ufshcd_runtime_resume(struct ufs_hba *hba) if (!hba->is_powered) goto out;else
- ret =ufshcd_resume(hba, UFS_RUNTIME_PM);
+ufshcd_resume(hba);
out:
- trace_ufshcd_runtime_resume(dev_name(hba->dev), ret, + trace_ufshcd_runtime_resume(dev_name(hba->dev), 0,ktime_to_us(ktime_sub(ktime_get(), start)),hba->curr_dev_pwr_mode,hba->uic_link_state);
- return ret;
+ return 0;
}
EXPORT_SYMBOL(ufshcd_runtime_resume);
@@ -9132,14 +9243,13 @@EXPORT_SYMBOL(ufshcd_runtime_idle);
*ufshcd_shutdown- shutdown routine
* @hba: per adapter instance
*
- * This function would power off both UFS device and UFS link.
+ * This function would turn off both UFS device and UFS hba
+ * regulators. It would also disable clocks.
*
* Returns 0 always to allow force shutdown even in case of errors. */
intufshcd_shutdown(struct ufs_hba *hba)
{
- int ret = 0;
-
down(&hba->host_sem);
hba->shutting_down= true;
up(&hba->host_sem);drivers/scsi/ufs/ufshcd.c +1
-0@@ -9152,10 +9262,8 @@ int ufshcd_shutdown(struct ufs_hba *hba) pm_runtime_get_sync(hba->dev); - ret = ufshcd_suspend(hba, UFS_SHUTDOWN_PM); + ufshcd_suspend(hba);out:
- if (ret)
-dev_err(hba->dev, "%s failed, err %d\n", __func__, ret);hba->is_powered= false;
/* allow force shutdown even in case of errors */ return 0;drivers/scsi/ufs/ufshcd.c +27
-0@@ -9477,15 +9585,163 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) } EXPORT_SYMBOL_GPL(ufshcd_init); +void ufshcd_resume_complete(struct device *dev) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + + pm_runtime_put_noidle(&hba->sdev_ufs_device->sdev_gendev); +} +EXPORT_SYMBOL_GPL (ufshcd_resume_complete); + +int ufshcd_suspend_prepare (struct device *dev) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + + /* + * SCSI assumes that runtime-pm and system-pm for scsi drivers + * are same. And it doesn't wake up the device for system-suspend + * if it's runtime suspended. But ufs doesn't follow that. + * The rpm-lvl and spm-lvl can be different in ufs. + * Force it to honor system-suspend. + */ + scsi_autopm_get_device(hba->sdev_ufs_device); + /* Refer ufshcd_resume_complete() */ + pm_runtime_get_noresume(&hba->sdev_ufs_device->sdev_gendev); + scsi_autopm_put_device(hba->sdev_ufs_device); + return 0; +} +EXPORT_SYMBOL_GPL (ufshcd_suspend_prepare); + +#ifdef CONFIG_PM_SLEEP drivers/scsi/ufs/ufshcd.c int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) · line 9624
Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10
-0+static int ufshcd_wl_poweroff (struct device *dev) +{ + ufshcd_wl_shutdown(dev); + return 0; +} +#endif + +static int ufshcd_wl_probe (struct device *dev) +{ + return is_device_wlun(to_scsi_device(dev)) ? 0 : -ENODEV;The "
ufs_device_wlun" driver owns PM forsdev_ufs_devicebecause it provides the dev_pm_ops. This is where thepm_runtime_allow()belongs and it is symmetrical with thepm_runtime_forbid()inufshcd_wl_remove().if (!is_device_wlun(to_scsi_device(dev)))
return-ENODEV;
blk_pm_runtime_init(dev);
pm_runtime_set_autosuspend_delay(dev, 0);
pm_runtime_allow(dev);Note
sdev_ufs_devicewill not runtime suspend here unless the device links have been added and all consumers have suspended. That is handled separately by thepm_runtime_get_noresume()inufshcd_slave_configure()and thepm_runtime_put()whenluns_avail== 1 inufshcd_setup_links().Quoted 183 lines
drivers/scsi/ufs/ufshcd.c +110
-0+} + +static int ufshcd_wl_remove (struct device *dev) +{ + pm_runtime_forbid(dev); + return 0; +} + +static const struct dev_pm_ops ufshcd_wl_pm_ops = { +#ifdef CONFIG_PM_SLEEP + .suspend = ufshcd_wl_suspend, + .resume = ufshcd_wl_resume, + .freeze = ufshcd_wl_suspend, + .thaw = ufshcd_wl_resume, + .poweroff = ufshcd_wl_poweroff, + .restore = ufshcd_wl_resume, +#endif + SET_RUNTIME_PM_OPS(ufshcd_wl_runtime_suspend, ufshcd_wl_runtime_resume, NULL) +}; + +/** +* ufs_dev_wlun_template - describes ufs device wlun +* ufs-device wlun - used to send pm commands +* All luns are consumers of ufs-device wlun. +* +* Currently, no sd driver is present for wluns. +* Hence the no specific pm operations are performed. +* With ufs design, SSU should be sent to ufs-device wlun. +* Hence register a scsi driver for ufs wluns only. +*/ +static struct scsi_driver ufs_dev_wlun_template = { + .gendrv = { + .name = "ufs_device_wlun", + .owner = THIS_MODULE, + .probe = ufshcd_wl_probe, + .remove = ufshcd_wl_remove, + .pm = &ufshcd_wl_pm_ops, + .shutdown = ufshcd_wl_shutdown, + }, +}; + +static int ufshcd_rpmb_probe (struct device *dev) +{ + return is_rpmb_wlun(to_scsi_device(dev)) ? 0 : -ENODEV; +} + +static inline int ufshcd_clear_rpmb_uac (struct ufs_hba *hba) +{ + int ret = 0; + + if (!hba->wlun_rpmb_clr_ua) + return 0; + ret = ufshcd_clear_ua_wlun(hba, UFS_UPIU_RPMB_WLUN); + if (!ret) + hba->wlun_rpmb_clr_ua = 0; + return ret; +} + +static int ufshcd_rpmb_runtime_resume (struct device *dev) +{ + struct ufs_hba *hba = wlun_dev_to_hba(dev); + + if (hba->sdev_rpmb) + return ufshcd_clear_rpmb_uac(hba); + return 0; +} + +static int ufshcd_rpmb_resume (struct device *dev) +{ + struct ufs_hba *hba = wlun_dev_to_hba(dev); + + if (hba->sdev_rpmb && !pm_runtime_suspended(dev)) + return ufshcd_clear_rpmb_uac(hba); + return 0; +} + +static const struct dev_pm_ops ufs_rpmb_pm_ops = { + SET_RUNTIME_PM_OPS(NULL, ufshcd_rpmb_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(NULL, ufshcd_rpmb_resume) +}; + +/** +* Describes the ufs rpmb wlun. +* Used only to send uac. +*/ +static struct scsi_driver ufs_rpmb_wlun_template = { + .gendrv = { + .name = "ufs_rpmb_wlun", + .owner = THIS_MODULE, + .probe = ufshcd_rpmb_probe, + .pm = &ufs_rpmb_pm_ops, + }, +}; + static int __init ufshcd_core_init(void) { + int ret; + ufs_debugfs_init(); + + ret = scsi_register_driver(&ufs_dev_wlun_template.gendrv); + if (ret) { + ufs_debugfs_exit(); + return ret; + } + ret = scsi_register_driver(&ufs_rpmb_wlun_template.gendrv); + if (ret) { + ufs_debugfs_exit(); + scsi_unregister_driver(&ufs_dev_wlun_template.gendrv); + return ret; + } return 0; } static void __exit ufshcd_core_exit(void) { ufs_debugfs_exit(); + scsi_unregister_driver(&ufs_dev_wlun_template.gendrv); + scsi_unregister_driver(&ufs_rpmb_wlun_template.gendrv); } module_init(ufshcd_core_init);drivers/scsi/ufs/ufshcd.h +6
-0@@ -72,6 +72,8 @@ enum ufs_event_type { UFS_EVT_LINK_STARTUP_FAIL, UFS_EVT_RESUME_ERR, UFS_EVT_SUSPEND_ERR, + UFS_EVT_WL_SUSP_ERR, + UFS_EVT_WL_RES_ERR, /* abnormal events */ UFS_EVT_DEV_RESET,@@ -804,6 +806,7 @@ struct ufs_hba { struct list_head clk_list_head; bool wlun_dev_clr_ua; + bool wlun_rpmb_clr_ua; /* Number of requests aborts */ int req_abort_count;@@ -841,6 +844,7 @@ struct ufs_hba { #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_root; #endif + u32 luns_avail; }; /* Returns true if clocks can be gated. Otherwise false */@@ -1100,6 +1104,8 @@ int ufshcd_exec_raw_upiu_cmd(struct ufs_hba *hba, enum query_opcode desc_op); int ufshcd_wb_ctrl(struct ufs_hba *hba, bool enable); +int ufshcd_suspend_prepare (struct device *dev); +void ufshcd_resume_complete (struct device *dev); /* Wrapper functions for safely calling variant operations */ static inline const char *ufshcd_get_var_name(struct ufs_hba *hba)include/trace/events/ufs.h +19
-0@@ -246,6 +246,26 @@ DEFINE_EVENT(ufshcd_template, ufshcd_init, int dev_state, int link_state), TP_ARGS(dev_name, err, usecs, dev_state, link_state)); +DEFINE_EVENT(ufshcd_template, ufshcd_wl_suspend, + TP_PROTO(const char *dev_name, int err, s64 usecs, + int dev_state, int link_state), + TP_ARGS(dev_name, err, usecs, dev_state, link_state)); + +DEFINE_EVENT (ufshcd_template, ufshcd_wl_resume, + TP_PROTO(const char *dev_name, int err, s64 usecs, + int dev_state, int link_state), + TP_ARGS(dev_name, err, usecs, dev_state, link_state)); + +DEFINE_EVENT (ufshcd_template, ufshcd_wl_runtime_suspend, + TP_PROTO(const char *dev_name, int err, s64 usecs, + int dev_state, int link_state), + TP_ARGS(dev_name, err, usecs, dev_state, link_state)); + +DEFINE_EVENT (ufshcd_template, ufshcd_wl_runtime_resume, + TP_PROTO(const char *dev_name, int err, s64 usecs, + int dev_state, int link_state), + TP_ARGS(dev_name, err, usecs, dev_state, link_state)); + TRACE_EVENT(ufshcd_command, TP_PROTO(const char *dev_name, enum ufs_trace_str_t str_t, unsigned int tag, u32 doorbell, int transfer_len, u32 intr,
_______________________________________________
Linux-mediatek mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-mediatek
Re: [PATCH v12 1/2] scsi: ufs: Enable power management for wlun
Quoted 77 lines · Adrian Hunter
Quoted 76 lines · Asutosh Das (asd)
Quoted 43 lines · Adrian Hunter
Quoted 38 lines · Asutosh Das
During runtime-suspend of ufs host, the scsi devices are already suspended and so are the queues associated with them. But the ufs host sends SSU to wlun during its runtime-suspend. During the process
blk_queue_enterchecks if the queue is not in suspended state. If so, it waits for the queue to resume, and never comes out of it.
The commit
(d55d15a33: scsi: block: Do not accept any requests while suspended) adds the check if the queue is in suspended state inblk_queue_enter().Call trace: __switch_to+0x174/0x2c4 __schedule+0x478/0x764 schedule+0x9c/0xe0 blk_queue_enter+0x158/0x228 blk_mq_alloc_request+0x40/0xa4 blk_get_request+0x2c/0x70 __scsi_execute+0x60/0x1c4 ufshcd_set_dev_pwr_mode+0x124/0x1e4 ufshcd_suspend+0x208/0x83c ufshcd_runtime_suspend+0x40/0x154 ufshcd_pltfrm_runtime_suspend+0x14/0x20 pm_generic_runtime_suspend+0x28/0x3c __rpm_callback+0x80/0x2a4 rpm_suspend+0x308/0x614 rpm_idle+0x158/0x228 pm_runtime_work+0x84/0xac process_one_work+0x1f0/0x470 worker_thread+0x26c/0x4c8 kthread+0x13c/0x320 ret_from_fork+0x10/0x18Fix this by registering ufs device wlun as a scsi driver and registering it for block runtime-pm. Also make this as a supplier for all other luns. That way, this device wlun suspends after all the consumers and resumes after hba resumes.
Quoted 3 lines
Co-developed-by Can Guo <[email protected]>Signed-off-by Can Guo <[email protected]>Signed-off-by Asutosh Das <[email protected]>Quoted 8 lines
I have some more comments that may help straighten things out.
Also please look at
ufs_debugfs_get_user_access()andufs_debugfs_put_user_access()that now need to scsi_autopm_get/put_devicesdev_ufs_device.It would also be good if you could re-base on linux-next.
Hi Adrian
Thanks for the comments.I agree moving the code to wlun probe and other changes. But it looks to me that it may not fully solve the issue.
Please let me explain my understanding on this:
(Please refer to the logs in v10)
scsi_autopm_*() are invoked on a sdev. pm_runtime_get_suppliers()/rpm_put_suppliers() are on the supplier device.For the device wlun:
slave_configure():
- doesn't set therpm_autosuspend
-pm_runtime_getnoresume()
scsi_sysfs_add_sdev():
-pm_runtime_forbid()
-scsi_autopm_get_device()
-device_add()
-ufshcd_wl_probe()
-scsi_autopm_put_device()
Quoted 7 lines
Quoted 4 lines
For all other scsi devices:
slave_alloc():
-ufshcd_setup_links()
Say alllink_add:pm_runtime_put(&hba->sdev_ufs_device->sdev_gendev);With
DL_FLAG_RPM_ACTIVE, links will 'get' not 'put'
I'm referring to the pm_runtime_put(sdev_ufs_device) after all the links are setup, that you suggested to add.
Quoted 11 lines
Quoted 8 lines
slave_configure():
- setrpm_autosuspendscsi_sysfs_add_sdev():
-scsi_autopm_get_device()
-device_add()-> schedules an asyncprobe()
-scsi_autopm_put_device()- (1)Now the
rpm_put_suppliers()can be invoked *after*pm_runtime_get_suppliers()of the asyncprobe(), since both are running in different contexts.Only if the sd device suspends.
Correct. What'd stop the sd device from suspending?
We should be stopping the sd device from suspending here - imho.
Quoted 7 lines
Quoted 1 line
In that case, the
usage_countof supplier would be decremented untilrpm_activeof this link becomes 1.Right, because the sd device suspended.
Quoted 1 line
Now the
pm_runtime_get_suppliers()expects thelink_activeto be more than 1.Not sure what you mean here. pm_runtime_*put*_suppliers() won't do anything if the link count is 1.
I'm referring to the logs that I pasted before:
[ 6.941267][ T7] scsi 0:0:0:4: rpm_put_suppliers: [BEF] Supp (0:0:0:49488) usage_count: 4 rpm_active: 3
------ T196 Context comes in while T7 is running ----------
[ 6.941466][ T196] scsi 0:0:0:4: pm_runtime_get_suppliers: (0:0:0:49488): supp: usage_count: 5 rpm_active: 4
--------------------------------------------------------------
[ 7.788397][ T7] scsi 0:0:0:4: rpm_put_suppliers: [AFT] Supp (0:0:0:49488) usage_count: 2 rpm_active: 1
I meant to say that, if the rpm_put_suppliers() is invoked after the pm_runtime_get_suppliers() as is seen above then the link_active may become 1 even *after* pm_runtime_get_suppliers() is invoked.
I'm referring to the pm_runtime_get_suppliers() invoked from:driver_probe_device() - say for, sd 0:0:0:x
|- pm_runtime_get_suppliers() - for sd 0:0:0:49488
Quoted 6 lines
Quoted 3 lines
Now then, there comes a time, that when
sd_probe()schedules a suspend, the supplierusage_countbecomes 0 and thelink_activebecomes 1. And the supplier suspends before the consumer.sd probe first resumes the sd device which will resume the supplier.
Correct, but it'd again schedule a suspend (since autosuspend is enabled now) at the end of the sd_probe(). Thereafter, pm_runtime_put_suppliers()(sd 0:0:0:49488) is invoked from driver_probe_device() which had actually invoked sd_probe(). That'd make the link_active to 1 even when sd 0:0:0:x is active.
Quoted 8 lines
Quoted 5 lines
So I was wondering, what'd make sure that the
pm_runtime_get_suppliers()fromdriver_probe_device()is invoked afterscsi_autopm_put_device()(1) finishes therpm_put_suppliers().Am not sure if I'm missing something in this.
Do you think, the current changes alone can fix the above issue?Yes, but let's see.
Essentially, we should stop the sd device from runtime suspending until it's probe is done. Then allow the same. Does it make sense? Please let me know what you think.
-asd
Quoted 1,276 lines
Quoted 1,275 lines
-asd
Quoted 1,270 lines
Quoted 286 lines
--- drivers/scsi/ufs/cdns-pltfrm.c | 2 + drivers/scsi/ufs/tc-dwc-g210-pci.c | 2 + drivers/scsi/ufs/ufs-debugfs.c | 2 +- drivers/scsi/ufs/ufs-debugfs.h | 2 +- drivers/scsi/ufs/ufs-exynos.c | 2 + drivers/scsi/ufs/ufs-hisi.c | 2 + drivers/scsi/ufs/ufs-mediatek.c | 12 +- drivers/scsi/ufs/ufs-qcom.c | 2 + drivers/scsi/ufs/ufs_bsg.c | 6 +- drivers/scsi/ufs/ufshcd-pci.c | 36 +-- drivers/scsi/ufs/ufshcd.c | 622 ++++++++++++++++++++++++++----------- drivers/scsi/ufs/ufshcd.h | 6 + include/trace/events/ufs.h | 20 ++ 13 files changed, 491 insertions(+), 225 deletions(-)drivers/scsi/ufs/cdns-pltfrm.c +2
-0@@ -319,6 +319,8 @@ static const struct dev_pm_ops cdns_ufs_dev_pm_ops = { .runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_idle = ufshcd_pltfrm_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, }; static struct platform_driver cdns_ufs_pltfrm_driver = {drivers/scsi/ufs/tc-dwc-g210-pci.c +2
-0@@ -148,6 +148,8 @@ static const struct dev_pm_ops tc_dwc_g210_pci_pm_ops = { .runtime_suspend = tc_dwc_g210_pci_runtime_suspend, .runtime_resume = tc_dwc_g210_pci_runtime_resume, .runtime_idle = tc_dwc_g210_pci_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, }; static const struct pci_device_id tc_dwc_g210_pci_tbl[] = {drivers/scsi/ufs/ufs-debugfs.c +1
-0@@ -13,7 +13,7 @@ void __init ufs_debugfs_init(void) ufs_debugfs_root = debugfs_create_dir("ufshcd", NULL); } -void __exit ufs_debugfs_exit(void) +voidufs_debugfs_exit (void) { debugfs_remove_recursive(ufs_debugfs_root); }drivers/scsi/ufs/ufs-debugfs.h +1
-1@@ -9,7 +9,7 @@ struct ufs_hba; #ifdef CONFIG_DEBUG_FS void __init ufs_debugfs_init(void); -void __exit ufs_debugfs_exit(void); +void ufs_debugfs_exit (void ); void ufs_debugfs_hba_init(struct ufs_hba *hba); void ufs_debugfs_hba_exit(struct ufs_hba *hba); #elsedrivers/scsi/ufs/ufs-exynos.c +2
-0@@ -1268,6 +1268,8 @@ static const struct dev_pm_ops exynos_ufs_pm_ops = { .runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_idle = ufshcd_pltfrm_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, }; static struct platform_driver exynos_ufs_pltform = {drivers/scsi/ufs/ufs-hisi.c +2
-0@@ -574,6 +574,8 @@ static const struct dev_pm_ops ufs_hisi_pm_ops = { .runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_idle = ufshcd_pltfrm_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, }; static struct platform_driver ufs_hisi_pltform = {drivers/scsi/ufs/ufs-mediatek.c +6
-5@@ -810,12 +810,10 @@ static int ufs_mtk_post_link(struct ufs_hba *hba) /* enable unipro clock gating feature */ ufs_mtk_cfg_unipro_cg(hba, true); - /* configure auto-hibern8 timer to 10ms */ - if (ufshcd_is_auto_hibern8_supported(hba)) { - ufshcd_auto_hibern8_update(hba, - FIELD_PREP(UFSHCI_AHIBERN8_TIMER_MASK, 10) | - FIELD_PREP(UFSHCI_AHIBERN8_SCALE_MASK, 3)); - } + /* will be configured during probe hba */ + if (ufshcd_is_auto_hibern8_supported(hba)) + hba->ahit = FIELD_PREP(UFSHCI_AHIBERN8_TIMER_MASK, 10) | + FIELD_PREP(UFSHCI_AHIBERN8_SCALE_MASK, 3); ufs_mtk_setup_clk_gating(hba); @@ -1097,6 +1095,8 @@ static const struct dev_pm_ops ufs_mtk_pm_ops = { .runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_idle = ufshcd_pltfrm_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, }; static struct platform_driver ufs_mtk_pltform = {drivers/scsi/ufs/ufs-qcom.c +2
-0@@ -1546,6 +1546,8 @@ static const struct dev_pm_ops ufs_qcom_pm_ops = { .runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_idle = ufshcd_pltfrm_runtime_idle, + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, }; static struct platform_driver ufs_qcom_pltform = {drivers/scsi/ufs/ufs_bsg.c +2
-1@@ -97,7 +97,7 @@ static int ufs_bsg_request(struct bsg_job *job) bsg_reply->reply_payload_rcv_len = 0; - pm_runtime_get_sync(hba->dev); + scsi_autopm_get_device(hba->sdev_ufs_device); msgcode = bsg_request->msgcode; switch (msgcode) {@@ -106,7 +106,7 @@ static int ufs_bsg_request(struct bsg_job *job) ret = ufs_bsg_alloc_desc_buffer(hba, job, &desc_buff, &desc_len, desc_op); if (ret) { - pm_runtime_put_sync(hba->dev); + scsi_autopm_put_device(hba->sdev_ufs_device); goto out; } @@ -138,7 +138,7 @@ static int ufs_bsg_request(struct bsg_job *job)break;
}
-pm_runtime_put_sync(hba->dev);
+scsi_autopm_put_device(hba->sdev_ufs_device);
if (!desc_buff)
goto out;drivers/scsi/ufs/ufshcd-pci.c +5
-30@@ -247,29 +247,6 @@ static int ufshcd_pci_resume(struct device *dev) return ufshcd_system_resume(dev_get_drvdata(dev)); } -/** -* ufshcd_pci_poweroff - suspend-to-disk poweroff function -* @dev: pointer to PCI device handle -* -* Returns 0 if successful -* Returns non-zero otherwise -*/ -static int ufshcd_pci_poweroff(struct device *dev) -{ - struct ufs_hba *hba = dev_get_drvdata(dev); - int spm_lvl = hba->spm_lvl; - int ret; - - /* - * For poweroff we need to set the UFS device to PowerDown mode. - * Force spm_lvl to ensure that. - */ - hba->spm_lvl = 5; - ret = ufshcd_system_suspend(hba); - hba->spm_lvl = spm_lvl; - return ret; -} - #endif /* !CONFIG_PM_SLEEP */ #ifdef CONFIG_PM@@ -365,17 +342,14 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) } static const struct dev_pm_ops ufshcd_pci_pm_ops = { -#ifdef CONFIG_PM_SLEEP - .suspend = ufshcd_pci_suspend, - .resume = ufshcd_pci_resume, - .freeze = ufshcd_pci_suspend, - .thaw = ufshcd_pci_resume, - .poweroff = ufshcd_pci_poweroff, - .restore = ufshcd_pci_resume, -#endif SET_RUNTIME_PM_OPS(ufshcd_pci_runtime_suspend, ufshcd_pci_runtime_resume, ufshcd_pci_runtime_idle) + SET_SYSTEM_SLEEP_PM_OPS(ufshcd_pci_suspend, ufshcd_pci_resume) +#ifdef CONFIG_PM_SLEEP + .prepare = ufshcd_suspend_prepare, + .complete = ufshcd_resume_complete, +#endif }; static const struct pci_device_id ufshcd_pci_tbl[] = {drivers/scsi/ufs/ufshcd.c +30
-3@@ -16,6 +16,7 @@ #include <linux/bitfield.h> #include <linux/blk-pm.h> #include <linux/blkdev.h> +#include <scsi/scsi_driver.h> #include "ufshcd.h" #include "ufs_quirks.h" #include "unipro.h"@@ -78,6 +79,8 @@ /* Polling time to wait for fDeviceInit */ #define FDEVICEINIT_COMPL_TIMEOUT 1500 /* millisecs */ +#define wlun_dev_to_hba(dv) shost_priv(to_scsi_device(dv)->host) + #define ufshcd_toggle_vreg(_dev, _vreg, _on) \ ({ \ int _ret; \@@ -1556,7 +1559,7 @@ static ssize_t ufshcd_clkscale_enable_store(struct device *dev, if (value == hba->clk_scaling.is_enabled) goto out; - pm_runtime_get_sync(hba->dev); + scsi_autopm_get_device(hba->sdev_ufs_device); ufshcd_hold(hba, false); hba->clk_scaling.is_enabled = value;@@ -1572,7 +1575,7 @@ static ssize_t ufshcd_clkscale_enable_store(struct device *dev, } ufshcd_release(hba); - pm_runtime_put_sync(hba->dev); + scsi_autopm_put_device(hba->sdev_ufs_device); out: up(&hba->host_sem); return err ? err : count;@@ -2572,6 +2575,17 @@ static inline u16 ufshcd_upiu_wlun_to_scsi_wlun(u8 upiu_wlun_id) return (upiu_wlun_id & ~UFS_UPIU_WLUN_ID) | SCSI_W_LUN_BASE; } +static inline bool is_rpmb_wlun(struct scsi_device *sdev) +{ + return (sdev->lun == ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_RPMB_WLUN)); +} + +static inline bool is_device_wlun (struct scsi_device *sdev) +{ + return (sdev->lun == + ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_UFS_DEVICE_WLUN)); +} + static void ufshcd_init_lrb(struct ufs_hba *hba, struct ufshcd_lrb *lrb, int i) { struct utp_transfer_cmd_desc *cmd_descp = hba->ucdl_base_addr;@@ -4106,11 +4120,11 @@ void ufshcd_auto_hibern8_update(struct ufs_hba *hba, u32 ahit) spin_unlock_irqrestore(hba->host->host_lock, flags); if (update && !pm_runtime_suspended(hba->dev)) { - pm_runtime_get_sync(hba->dev); + scsi_autopm_get_device(hba->sdev_ufs_device); ufshcd_hold(hba, false); ufshcd_auto_hibern8_enable(hba); ufshcd_release(hba); - pm_runtime_put(hba->dev); + scsi_autopm_put_device(hba->sdev_ufs_device); } } EXPORT_SYMBOL_GPL(ufshcd_auto_hibern8_update);@@ -4808,6 +4822,41 @@ static inline void ufshcd_get_lu_power_on_wp_status(struct ufs_hba *hba, } /** +* ufshcd_setup_links - associate link b/w device wlun and other luns +* @sdev: pointer to SCSI device +* @hba: pointer to ufs hba +*/ +static void ufshcd_setup_links (struct ufs_hba *hba,struct scsi_device *sdev) +{ + struct device_link *link; + + /* + * device wlun is the supplier & rest of the luns are consumers + * This ensures that device wlun suspends after all other luns. + */ + if (hba->sdev_ufs_device) { + link = device_link_add(&sdev->sdev_gendev,drivers/scsi/ufs/ufshcd.c static inline void ufshcd_get_lu_power_on_wp_status(struct ufs_hba *hba, · line 4847
Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10
-0+ &hba->sdev_ufs_device->sdev_gendev, + DL_FLAG_PM_RUNTIME|DL_FLAG_RPM_ACTIVE); + if (!link) { + dev_err(&sdev->sdev_gendev, "Failed establishing link - %s\n", + dev_name(&hba->sdev_ufs_device->sdev_gendev)); + return; + } + hba->luns_avail--; + /* Ignore REPORT_LUN wlun probing */ + if (hba->luns_avail != 1) {The condition here is the wrong way around. i.e. should be
if (
hba->luns_avail== 1) {drivers/scsi/ufs/ufshcd.c static inline void ufshcd_get_lu_power_on_wp_status(struct ufs_hba *hba, · line 4848
Quoted 1 line
drivers/scsi/ufs/ufshcd.c +1
-0+ pm_runtime_put_noidle(&hba->sdev_ufs_device->sdev_gendev);If the
usage_countis zero here, then we should let it suspend i.e. make this:
pm_runtime_put(&hba->sdev_ufs_device->sdev_gendev);Note this pairs with the
pm_runtime_get_noresume()that needs to be added to ufshcd_slave_configure. Please see comments forufshcd_slave_configure().drivers/scsi/ufs/ufshcd.c static inline void ufshcd_get_lu_power_on_wp_status(struct ufs_hba *hba, · line 4850
Quoted 1 line
drivers/scsi/ufs/ufshcd.c +1
-0+ pm_runtime_allow(&hba->sdev_ufs_device->sdev_gendev);This
pm_runtime_allowshould not be here. It should be inufshcd_wl_probe(). Please see comments forufshcd_wl_probe().Quoted 20 lines
drivers/scsi/ufs/ufshcd.c +10
-0+ return; + } + } else { + /* device wlun is probed */ + hba->luns_avail--; + } +} + +/** * ufshcd_slave_alloc - handle initial SCSI device configurations * @sdev: pointer to SCSI device * @@ -4838,6 +4887,8 @@ static int ufshcd_slave_alloc(struct scsi_device *sdev) ufshcd_get_lu_power_on_wp_status(hba, sdev); + ufshcd_setup_links(hba, sdev); + return 0; } @@ -4869,8 +4920,13 @@ static int ufshcd_slave_configure(struct scsi_device *sdev) blk_queue_update_dma_pad(q, PRDT_DATA_BYTE_COUNT_PAD - 1);drivers/scsi/ufs/ufshcd.c static int ufshcd_slave_alloc(struct scsi_device *sdev) · line 4928
Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +6
-2if (hba->quirks & UFSHCD_QUIRK_ALIGN_SG_WITH_PAGE_SIZE) blk_queue_update_dma_alignment(q, PAGE_SIZE - 1); - - if (ufshcd_is_rpm_autosuspend_allowed(hba)) + /* + * Block runtime-pm until all consumers are added. + * Refer ufshcd_setup_links(). + */ + if (is_device_wlun(sdev)) + pm_runtime_forbid(&sdev->sdev_gendev);Since
rpm_autosuspendis not being set forsdev_ufs_device,scsi_sysfs_add_sdev()willpm_runtime_forbid()it anyway. We should increase theusage_countinstead i.e.
pm_runtime_get_noresume(&sdev->sdev_gendev);Quoted 109 lines
+ else if (
ufshcd_is_rpm_autosuspend_allowed(hba))
sdev->rpm_autosuspend= 1;ufshcd_crypto_setup_rq_keyslot_manager(hba, q);drivers/scsi/ufs/ufshcd.c +5
-19@@ -4985,15 +5041,9 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)* UFS device needs urgent BKOPs. */ if (!hba->pm_op_in_progress && - ufshcd_is_exception_event(lrbp->ucd_rsp_ptr) && - schedule_work(&hba->eeh_work)) { - /* - * Prevent suspend once eeh_work is scheduled - * to avoid deadlock between ufshcd_suspend - * and exception event handler. - */ - pm_runtime_get_noresume(hba->dev); - } + ufshcd_is_exception_event(lrbp->ucd_rsp_ptr)) + /* Flushed in suspend */ + schedule_work(&hba->eeh_work); break; case UPIU_TRANSACTION_REJECT_UPIU: /* TODO: handle Reject UPIU Response */@@ -5589,8 +5639,8 @@ static void ufshcd_rpm_dev_flush_recheck_work(struct work_struct *work)* after a certain delay to recheck the threshold by next runtime * suspend. */ - pm_runtime_get_sync(hba->dev); - pm_runtime_put_sync(hba->dev); + scsi_autopm_get_device(hba->sdev_ufs_device); + scsi_autopm_put_device(hba->sdev_ufs_device); } /**@@ -5607,7 +5657,6 @@ static void ufshcd_exception_event_handler(struct work_struct *work) u32 status = 0; hba = container_of(work, struct ufs_hba, eeh_work); - pm_runtime_get_sync(hba->dev); ufshcd_scsi_block_requests(hba); err = ufshcd_get_ee_status(hba, &status); if (err) {@@ -5623,14 +5672,6 @@ static void ufshcd_exception_event_handler(struct work_struct *work) out: ufshcd_scsi_unblock_requests(hba); - /* - * pm_runtime_get_noresume is called while scheduling - * eeh_work to avoid suspend racing with exception work. - * Hence decrement usage counter using pm_runtime_put_noidle - * to allow suspend on completion of exception event handler. - */ - pm_runtime_put_noidle(hba->dev); - pm_runtime_put(hba->dev); return; } @@ -5755,12 +5796,13 @@ static void ufshcd_clk_scaling_suspend(struct ufs_hba *hba, bool suspend) static void ufshcd_err_handling_prepare(struct ufs_hba *hba){
-pm_runtime_get_sync(hba->dev);
- if (pm_runtime_status_suspended(hba->dev)||hba->is_sys_suspended) { +scsi_autopm_get_device(hba->sdev_ufs_device);
+ if (pm_runtime_status_suspended(&hba->sdev_ufs_device->sdev_gendev)|| +hba->is_sys_suspended) {
enumufs_pm_oppm_op;
/*
- * Don't assume anything ofpm_runtime_get_sync(), if + * Don't assume anything of resume, if
* resume fails, irq and clocks can be OFF, and powers
* can be OFF or in LPM.
*/drivers/scsi/ufs/ufshcd.c +9
-8@@ -5789,7 +5831,7 @@ static void ufshcd_err_handling_unprepare(struct ufs_hba *hba) ufshcd_release(hba); if (ufshcd_is_clkscaling_supported(hba)) ufshcd_clk_scaling_suspend(hba, false); - pm_runtime_put(hba->dev); + pm_runtime_put(&hba->sdev_ufs_device->sdev_gendev); } static inline bool ufshcd_err_handling_should_stop(struct ufs_hba *hba)@@ -5810,14 +5852,14 @@ static void ufshcd_recover_pm_error(struct ufs_hba *hba) hba->is_sys_suspended = false; /* - * Set RPM status of hba device to RPM_ACTIVE, + * Set RPM status of wlun device to RPM_ACTIVE, * this also clears its runtime error. */ - ret = pm_runtime_set_active(hba->dev); + ret = pm_runtime_set_active(&hba->sdev_ufs_device->sdev_gendev); /* - * If hba device had runtime error, we also need to resume those - * scsi devices under hba in case any of them has failed to be - * resumed due to hba runtime resume failure. This is to unblock + * If wlun device had runtime error, we also need to resume those + * consumer scsi devices in case any of them has failed to be + * resumed due to supplier runtime resume failure. This is to unblock * blk_queue_enter in case there are bios waiting inside it. */ if (!ret) {@@ -7207,11 +7249,12 @@ static void ufshcd_set_active_icc_lvl(struct ufs_hba *hba) static inline void ufshcd_blk_pm_runtime_init(struct scsi_device *sdev) { + int dly = is_device_wlun(sdev) ? 0:RPM_AUTOSUSPEND_DELAY_MS; + scsi_autopm_get_device(sdev); blk_pm_runtime_init(sdev->request_queue, &sdev->sdev_gendev); if (sdev->rpm_autosuspend) - pm_runtime_set_autosuspend_delay(&sdev->sdev_gendev, - RPM_AUTOSUSPEND_DELAY_MS); + pm_runtime_set_autosuspend_delay(&sdev->sdev_gendev, dly); scsi_autopm_put_device(sdev); } @@ -7253,6 +7296,7 @@ static int ufshcd_scsi_add_wlus(struct ufs_hba *hba) hba->sdev_ufs_device = NULL;Quoted 4 lines
goto out;
}
+scsi_autopm_get_device(hba->sdev_ufs_device);
ufshcd_blk_pm_runtime_init(hba->sdev_ufs_device);Let's drop both
scsi_autopm_get_device()andufshcd_blk_pm_runtime_init()here and instead do things inufshcd_wl_probe()- see comments forufshcd_wl_probe()Quoted 589 lines
scsi_device_put(hba->sdev_ufs_device);
@@ -7417,6 +7461,9 @@ static intufs_get_device_desc(struct ufs_hba *hba)goto out;
}
+hba->luns_avail= desc_buf[DEVICE_DESC_PARAM_NUM_LU] +
+ desc_buf[DEVICE_DESC_PARAM_NUM_WLU];drivers/scsi/ufs/ufshcd.c +4
-1+ ufs_fixup_device_setup(hba); ufshcd_wb_probe(hba, desc_buf);@@ -7892,6 +7939,7 @@ static int ufshcd_probe_hba(struct ufs_hba *hba, bool async) ufshcd_set_ufs_dev_active(hba); ufshcd_force_reset_auto_bkops(hba); hba->wlun_dev_clr_ua = true; + hba->wlun_rpmb_clr_ua = true; /* Gear up to HS gear if supported */ if (hba->max_pwr_info.is_valid) {@@ -8475,7 +8523,8 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba,* handling context. */ hba->host->eh_noresume = 1; - ufshcd_clear_ua_wluns(hba); + if (hba->wlun_dev_clr_ua) + ufshcd_clear_ua_wlun(hba, UFS_UPIU_UFS_DEVICE_WLUN); cmd[4] = pwr_mode << 4; @@ -8650,23 +8699,7 @@ static void ufshcd_hba_vreg_set_hpm(struct ufs_hba *hba) ufshcd_setup_hba_vreg(hba, true);}
-/**
- *ufshcd_suspend- helper function for suspend operations
- * @hba: per adapter instance
- * @pm_op: desired low power operation type
- *
- * This function will try to put the UFS device and link into low power
- * mode based on the "rpm_lvl" (Runtime PM level) or "spm_lvl"
- * (System PM level).
- *
- * If this function is called during shutdown, it will make sure that
- * both UFS device and UFS link is powered off.
- *
- * NOTE: UFS device & link must be active before we enter in this function.
- *
- * Returns 0 for success and non-zero for failure
- */drivers/scsi/ufs/ufshcd.c +7
-6-static int ufshcd_suspend (struct ufs_hba *hba,enum ufs_pm_op pm_op) +static int __ufshcd_wl_suspend (struct ufs_hba *hba,enum ufs_pm_op pm_op) { int ret = 0; int check_for_bkops;@@ -8674,7 +8707,7 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) enum ufs_dev_pwr_mode req_dev_pwr_mode; enum uic_link_state req_link_state; - hba->pm_op_in_progress = 1; + hba->pm_op_in_progress = true; if (!ufshcd_is_shutdown_pm(pm_op)) { pm_lvl = ufshcd_is_runtime_pm(pm_op) ? hba->rpm_lvl : hba->spm_lvl;@@ -8697,17 +8730,17 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) if (req_dev_pwr_mode == UFS_ACTIVE_PWR_MODE && req_link_state == UIC_LINK_ACTIVE_STATE) { - goto disable_clks; + goto enable_scaling; } if ((req_dev_pwr_mode == hba->curr_dev_pwr_mode) && (req_link_state == hba->uic_link_state)) - goto enable_gating; + goto enable_scaling; /* UFS device & link must be active before we enter in this function */ if (!ufshcd_is_ufs_dev_active(hba) || !ufshcd_is_link_active(hba)) { ret = -EINVAL; - goto enable_gating; + goto enable_scaling; } if (ufshcd_is_runtime_pm(pm_op)) {@@ -8719,7 +8752,7 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)*/ ret = ufshcd_urgent_bkops(hba); if (ret) - goto enable_gating; + goto enable_scaling; } else { /* make sure that auto bkops is disabled */ ufshcd_disable_auto_bkops(hba);@@ -8747,7 +8780,7 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) if (!hba->dev_info.b_rpm_dev_flush_capable) { ret = ufshcd_set_dev_pwr_mode(hba, req_dev_pwr_mode); if (ret) - goto enable_gating; + goto enable_scaling; } } @@ -8760,7 +8793,6 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)if (ret)
gotoset_dev_active;
-disable_clks:
/*
* Call vendor specific suspend callback. As these callbacks may access
* vendor specific host controller register space call them before thedrivers/scsi/ufs/ufshcd.c +189
-90@@ -8769,28 +8801,9 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) ret = ufshcd_vops_suspend(hba, pm_op); if (ret) goto set_link_active; - /* - * Disable the host irq as host controller as there won't be any - * host controller transaction expected till resume. - */ - ufshcd_disable_irq(hba); - - ufshcd_setup_clocks(hba, false); - - if (ufshcd_is_clkgating_allowed(hba)) { - hba->clk_gating.state = CLKS_OFF; - trace_ufshcd_clk_gating(dev_name(hba->dev), - hba->clk_gating.state); - } - - ufshcd_vreg_set_lpm(hba); - - /* Put the host controller in low power mode if possible */ - ufshcd_hba_vreg_set_lpm(hba); goto out; set_link_active: - ufshcd_vreg_set_hpm(hba); /** Device hardware reset is required to exit DeepSleep. Also, for * DeepSleep, the link is off so host reset and restore will be done @@ -8812,57 +8825,32 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) } if (!ufshcd_set_dev_pwr_mode(hba, UFS_ACTIVE_PWR_MODE)) ufshcd_disable_auto_bkops(hba); -enable_gating: +enable_scaling: if (ufshcd_is_clkscaling_supported(hba)) ufshcd_clk_scaling_suspend(hba, false); - hba->clk_gating.is_suspended = false; hba->dev_info.b_rpm_dev_flush_capable = false; - ufshcd_clear_ua_wluns(hba); - ufshcd_release(hba); out: if (hba->dev_info.b_rpm_dev_flush_capable) { schedule_delayed_work(&hba->rpm_dev_flush_recheck_work, msecs_to_jiffies(RPM_DEV_FLUSH_RECHECK_WORK_DELAY_MS)); } - hba->pm_op_in_progress = 0; - - if (ret) - ufshcd_update_evt_hist(hba, UFS_EVT_SUSPEND_ERR, (u32)ret); + if (ret) { + ufshcd_update_evt_hist(hba, UFS_EVT_WL_SUSP_ERR, (u32)ret); + hba->clk_gating.is_suspended = false; + ufshcd_release(hba); + } + hba->pm_op_in_progress = false; return ret; } -/** -* ufshcd_resume - helper function for resume operations -* @hba: per adapter instance -* @pm_op: runtime PM or system PM -* -* This function basically brings the UFS device, UniPro link and controller -* to active state. -* -* Returns 0 for success and non-zero for failure -*/ -static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) +static int __ufshcd_wl_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) { int ret; - enum uic_link_state old_link_state; +enum uic_link_state old_link_state = hba->uic_link_state; - hba->pm_op_in_progress = 1; - old_link_state = hba->uic_link_state; - - ufshcd_hba_vreg_set_hpm(hba); - ret = ufshcd_vreg_set_hpm(hba); - if (ret) - goto out; - - /* Make sure clocks are enabled before accessing controller */ - ret = ufshcd_setup_clocks(hba, true); - if (ret) - goto disable_vreg; - - /* enable the host irq as host controller would be active soon */ - ufshcd_enable_irq(hba); +hba->pm_op_in_progress = true; /* * Call vendor specific resume callback. As these callbacks may access @@ -8871,7 +8859,7 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)*/ ret = ufshcd_vops_resume(hba, pm_op); if (ret) - goto disable_irq_and_vops_clks; + goto out; /* For DeepSleep, the only supported option is to have the link off */ WARN_ON(ufshcd_is_ufs_dev_deepsleep(hba) && !ufshcd_is_link_off(hba));@@ -8916,31 +8904,147 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)*/ ufshcd_urgent_bkops(hba); - hba->clk_gating.is_suspended = false; - - if (ufshcd_is_clkscaling_supported(hba)) - ufshcd_clk_scaling_suspend(hba, false); - - /* Enable Auto-Hibernate if configured */ - ufshcd_auto_hibern8_enable(hba); + if (hba->clk_scaling.is_allowed) + ufshcd_resume_clkscaling(hba); if (hba->dev_info.b_rpm_dev_flush_capable) { hba->dev_info.b_rpm_dev_flush_capable = false; cancel_delayed_work(&hba->rpm_dev_flush_recheck_work); } - ufshcd_clear_ua_wluns(hba); - - /* Schedule clock gating in case of no access to UFS device yet */ - ufshcd_release(hba); - + /* Enable Auto-Hibernate if configured */ + ufshcd_auto_hibern8_enable(hba); goto out; set_old_link_state: ufshcd_link_state_transition(hba, old_link_state, 0); vendor_suspend: ufshcd_vops_suspend(hba, pm_op); -disable_irq_and_vops_clks: +out: + if (ret) + ufshcd_update_evt_hist(hba, UFS_EVT_WL_RES_ERR, (u32)ret); + hba->clk_gating.is_suspended = false; + ufshcd_release(hba); + hba->pm_op_in_progress = false; + return ret; +} + +static int ufshcd_wl_runtime_suspend (struct device *dev) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct ufs_hba *hba; + int ret; + ktime_t start = ktime_get(); + + hba = shost_priv(sdev->host); + + ret = __ufshcd_wl_suspend(hba, UFS_RUNTIME_PM); + if (ret) + dev_err(&sdev->sdev_gendev, "%s failed: %d\n", __func__, ret); + + trace_ufshcd_wl_runtime_suspend(dev_name(dev), ret, + ktime_to_us(ktime_sub(ktime_get(), start)), + hba->curr_dev_pwr_mode, hba->uic_link_state); + + return ret; +} + +static int ufshcd_wl_runtime_resume (struct device *dev) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct ufs_hba *hba; + int ret = 0; + ktime_t start = ktime_get(); + + hba = shost_priv(sdev->host); + + ret = __ufshcd_wl_resume(hba, UFS_RUNTIME_PM); + if (ret) + dev_err(&sdev->sdev_gendev, "%s failed: %d\n", __func__, ret); + + trace_ufshcd_wl_runtime_resume(dev_name(dev), ret, + ktime_to_us(ktime_sub(ktime_get(), start)), + hba->curr_dev_pwr_mode, hba->uic_link_state); + + return ret; +} + +#ifdef CONFIG_PM_SLEEP +static int ufshcd_wl_suspend (struct device *dev) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct ufs_hba *hba; + int ret; + ktime_t start = ktime_get(); + + hba = shost_priv(sdev->host); + ret = __ufshcd_wl_suspend(hba, UFS_SYSTEM_PM); + if (ret) + dev_err(&sdev->sdev_gendev, "%s failed: %d\n", __func__, ret); + + trace_ufshcd_wl_suspend(dev_name(dev), ret, + ktime_to_us(ktime_sub(ktime_get(), start)), + hba->curr_dev_pwr_mode, hba->uic_link_state); + + return ret; +} + +static int ufshcd_wl_resume (struct device *dev) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct ufs_hba *hba; + int ret = 0; + ktime_t start = ktime_get(); + + if (pm_runtime_suspended(dev)) + return 0; + hba = shost_priv(sdev->host); + + ret = __ufshcd_wl_resume(hba, UFS_SYSTEM_PM); + if (ret) + dev_err(&sdev->sdev_gendev, "%s failed: %d\n", __func__, ret); + + trace_ufshcd_wl_resume(dev_name(dev), ret, + ktime_to_us(ktime_sub(ktime_get(), start)), + hba->curr_dev_pwr_mode, hba->uic_link_state); + + return ret; +} +#endif + +static void ufshcd_wl_shutdown (struct device *dev) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct ufs_hba *hba; + + hba = shost_priv(sdev->host); + /* Turn on everything while shutting down */ + scsi_autopm_get_device(sdev); + scsi_device_quiesce(sdev); + shost_for_each_device(sdev, hba->host) { + if (sdev == hba->sdev_ufs_device) + continue; + scsi_device_quiesce(sdev); + } + __ufshcd_wl_suspend(hba, UFS_SHUTDOWN_PM); +} + +/** +* ufshcd_suspend - helper function for suspend operations +* @hba: per adapter instance +* +* This function will put disable irqs, turn off clocks +* and set vreg and hba-vreg in lpm mode. +* Also check the description of __ufshcd_wl_suspend(). +*/ +static void ufshcd_suspend (struct ufs_hba *hba) +{ + hba->pm_op_in_progress = 1; + + /* + * Disable the host irq as host controller as there won't be any + * host controller transaction expected till resume. + */ ufshcd_disable_irq(hba); ufshcd_setup_clocks(hba, false); if (ufshcd_is_clkgating_allowed(hba)) {@@ -8948,6 +9052,43 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) trace_ufshcd_clk_gating(dev_name(hba->dev), hba->clk_gating.state); } + + ufshcd_vreg_set_lpm(hba); + /* Put the host controller in low power mode if possible */ + ufshcd_hba_vreg_set_lpm(hba); + hba->pm_op_in_progress = 0; +} + +/** +* ufshcd_resume - helper function for resume operations +* @hba: per adapter instance +* +* This function basically turns on the regulators, clocks and +* irqs of the hba. +* Also check the description of __ufshcd_wl_resume(). +* +* Returns 0 for success and non-zero for failure +*/ +static int ufshcd_resume (struct ufs_hba *hba) +{ + int ret; + + hba->pm_op_in_progress = 1; + + ufshcd_hba_vreg_set_hpm(hba); + ret = ufshcd_vreg_set_hpm(hba); + if (ret) + goto out; + + /* Make sure clocks are enabled before accessing controller */ + ret = ufshcd_setup_clocks(hba, true); + if (ret) + goto disable_vreg; + + /* enable the host irq as host controller would be active soon */ + ufshcd_enable_irq(hba); + goto out; + disable_vreg: ufshcd_vreg_set_lpm(hba); out:@@ -8962,6 +9103,7 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)* @hba: per adapter instance * * Check the description of ufshcd_suspend() function for more details. +* Also check the description of __ufshcd_wl_suspend(). * * Returns 0 for success and non-zero for failure */ @@ -8987,21 +9129,7 @@ int ufshcd_system_suspend(struct ufs_hba *hba) !hba->dev_info.b_rpm_dev_flush_capable) goto out; - if (pm_runtime_suspended(hba->dev)) { - /* - * UFS device and/or UFS link low power states during runtime - * suspend seems to be different than what is expected during - * system suspend. Hence runtime resume the devic & link and - * let the system suspend low power states to take effect. - * TODO: If resume takes longer time, we might have optimize - * it in future by not resuming everything if possible. - */ - ret = ufshcd_runtime_resume(hba); - if (ret) - goto out; - } - - ret = ufshcd_suspend(hba, UFS_SYSTEM_PM); + ufshcd_suspend(hba); out: trace_ufshcd_system_suspend(dev_name(hba->dev), ret, ktime_to_us(ktime_sub(ktime_get(), start)),@@ -9023,7 +9151,6 @@ EXPORT_SYMBOL(ufshcd_system_suspend); int ufshcd_system_resume(struct ufs_hba *hba) { - int ret = 0; ktime_t start = ktime_get(); if (!hba)@@ -9034,22 +9161,18 @@ int ufshcd_system_resume(struct ufs_hba *hba)down(&hba->host_sem); } - if (!hba->is_powered || pm_runtime_suspended(hba->dev)) -/* -* Let the runtime resume take care of resuming -* if runtime suspended. -*/ +if (!hba->is_powered) goto out; else - ret = ufshcd_resume(hba, UFS_SYSTEM_PM); +ufshcd_resume(hba); out: - trace_ufshcd_system_resume(dev_name(hba->dev), ret, +trace_ufshcd_system_resume(dev_name(hba->dev), 0, ktime_to_us(ktime_sub(ktime_get(), start)), hba->curr_dev_pwr_mode, hba->uic_link_state); - if (!ret) - hba->is_sys_suspended = false; + +hba->is_sys_suspended = false; up(&hba->host_sem); - return ret; +return 0; } EXPORT_SYMBOL(ufshcd_system_resume); @@ -9058,12 +9181,12 @@ EXPORT_SYMBOL(ufshcd_system_resume); * @hba: per adapter instance * * Check the description of ufshcd_suspend() function for more details. +* Also check the description of __ufshcd_wl_suspend(). * * Returns 0 for success and non-zero for failure */ int ufshcd_runtime_suspend(struct ufs_hba *hba) { - int ret = 0; ktime_t start = ktime_get(); if (!hba)@@ -9072,12 +9195,12 @@ int ufshcd_runtime_suspend(struct ufs_hba *hba) if (!hba->is_powered) goto out; else - ret = ufshcd_suspend(hba, UFS_RUNTIME_PM); + ufshcd_suspend(hba); out: - trace_ufshcd_runtime_suspend(dev_name(hba->dev), ret, + trace_ufshcd_runtime_suspend(dev_name(hba->dev), 0, ktime_to_us(ktime_sub(ktime_get(), start)), hba->curr_dev_pwr_mode, hba->uic_link_state); - return ret; + return 0; } EXPORT_SYMBOL(ufshcd_runtime_suspend); @@ -9085,26 +9208,14 @@ EXPORT_SYMBOL(ufshcd_runtime_suspend);*
ufshcd_runtime_resume- runtime resume routine
* @hba: per adapter instance
*
- * This function basically brings the UFS device, UniPro link and controller
+ * This function basically brings controller
* to active state. Following operations are done in this function:
*
* 1. Turn on all the controller related clocks
- * 2. Bring the UniPro link out of Hibernate state
- * 3. If UFS device is in sleep state, turn ON VCC rail and bring the UFS device
- * to active state.
- * 4. If auto-bkops is enabled on the device, disable it.
- *
- * So following would be the possible power state after this function return
- * successfully:
- * S1: UFS device in Active state with VCC rail ON
- * UniPro link in Active state
- * All the UFS/UniPro controller clocks are ON
- *
- * Returns 0 for success and non-zero for failure
+ * 2. Turn ON VCC rail
*/
intufshcd_runtime_resume(struct ufs_hba *hba)
{
- int ret = 0;
ktime_tstart =ktime_get();
if (!hba)drivers/scsi/ufs/ufshcd.c +3
-3@@ -9113,12 +9224,12 @@ int ufshcd_runtime_resume(struct ufs_hba *hba) if (!hba->is_powered) goto out; else - ret = ufshcd_resume(hba, UFS_RUNTIME_PM); + ufshcd_resume(hba); out: - trace_ufshcd_runtime_resume(dev_name(hba->dev), ret, + trace_ufshcd_runtime_resume(dev_name(hba->dev), 0, ktime_to_us(ktime_sub(ktime_get(), start)), hba->curr_dev_pwr_mode, hba->uic_link_state); - return ret; + return 0; } EXPORT_SYMBOL(ufshcd_runtime_resume); @@ -9132,14 +9243,13 @@ EXPORT_SYMBOL(ufshcd_runtime_idle);*
ufshcd_shutdown- shutdown routine
* @hba: per adapter instance
*
- * This function would power off both UFS device and UFS link.
+ * This function would turn off both UFS device and UFS hba
+ * regulators. It would also disable clocks.
*
* Returns 0 always to allow force shutdown even in case of errors. */
intufshcd_shutdown(struct ufs_hba *hba)
{
- int ret = 0;drivers/scsi/ufs/ufshcd.c +28
-3- down(&hba->host_sem); hba->shutting_down = true; up(&hba->host_sem);@@ -9152,10 +9262,8 @@ int ufshcd_shutdown(struct ufs_hba *hba) pm_runtime_get_sync(hba->dev); - ret = ufshcd_suspend(hba, UFS_SHUTDOWN_PM); + ufshcd_suspend(hba); out: - if (ret) - dev_err(hba->dev, "%s failed, err %d\n", __func__, ret); hba->is_powered = false; /* allow force shutdown even in case of errors */ return 0;@@ -9477,15 +9585,163 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) } EXPORT_SYMBOL_GPL(ufshcd_init); +void ufshcd_resume_complete(struct device *dev) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + + pm_runtime_put_noidle(&hba->sdev_ufs_device->sdev_gendev); +} +EXPORT_SYMBOL_GPL (ufshcd_resume_complete); + +int ufshcd_suspend_prepare (struct device *dev) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + + /* + * SCSI assumes that runtime-pm and system-pm for scsi drivers + * are same. And it doesn't wake up the device for system-suspend + * if it's runtime suspended. But ufs doesn't follow that. + * The rpm-lvl and spm-lvl can be different in ufs. + * Force it to honor system-suspend. + */ + scsi_autopm_get_device(hba->sdev_ufs_device); + /* Refer ufshcd_resume_complete() */ + pm_runtime_get_noresume(&hba->sdev_ufs_device->sdev_gendev); + scsi_autopm_put_device(hba->sdev_ufs_device); + return 0; +} +EXPORT_SYMBOL_GPL (ufshcd_suspend_prepare); + +#ifdef CONFIG_PM_SLEEP drivers/scsi/ufs/ufshcd.c int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) · line 9624
Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10
-0+static int ufshcd_wl_poweroff (struct device *dev) +{ + ufshcd_wl_shutdown(dev); + return 0; +} +#endif + +static int ufshcd_wl_probe (struct device *dev) +{ + return is_device_wlun(to_scsi_device(dev)) ? 0 : -ENODEV;The "
ufs_device_wlun" driver owns PM forsdev_ufs_devicebecause it provides the dev_pm_ops. This is where thepm_runtime_allow()belongs and it is symmetrical with thepm_runtime_forbid()inufshcd_wl_remove().if (!is_device_wlun(to_scsi_device(dev)))
return-ENODEV;
blk_pm_runtime_init(dev);
pm_runtime_set_autosuspend_delay(dev, 0);
pm_runtime_allow(dev);Note
sdev_ufs_devicewill not runtime suspend here unless the device links have been added and all consumers have suspended. That is handled separately by thepm_runtime_get_noresume()inufshcd_slave_configure()and thepm_runtime_put()whenluns_avail== 1 inufshcd_setup_links().Quoted 183 lines
drivers/scsi/ufs/ufshcd.c +110
-0+} + +static int ufshcd_wl_remove (struct device *dev) +{ + pm_runtime_forbid(dev); + return 0; +} + +static const struct dev_pm_ops ufshcd_wl_pm_ops = { +#ifdef CONFIG_PM_SLEEP + .suspend = ufshcd_wl_suspend, + .resume = ufshcd_wl_resume, + .freeze = ufshcd_wl_suspend, + .thaw = ufshcd_wl_resume, + .poweroff = ufshcd_wl_poweroff, + .restore = ufshcd_wl_resume, +#endif + SET_RUNTIME_PM_OPS(ufshcd_wl_runtime_suspend, ufshcd_wl_runtime_resume, NULL) +}; + +/** +* ufs_dev_wlun_template - describes ufs device wlun +* ufs-device wlun - used to send pm commands +* All luns are consumers of ufs-device wlun. +* +* Currently, no sd driver is present for wluns. +* Hence the no specific pm operations are performed. +* With ufs design, SSU should be sent to ufs-device wlun. +* Hence register a scsi driver for ufs wluns only. +*/ +static struct scsi_driver ufs_dev_wlun_template = { + .gendrv = { + .name = "ufs_device_wlun", + .owner = THIS_MODULE, + .probe = ufshcd_wl_probe, + .remove = ufshcd_wl_remove, + .pm = &ufshcd_wl_pm_ops, + .shutdown = ufshcd_wl_shutdown, + }, +}; + +static int ufshcd_rpmb_probe (struct device *dev) +{ + return is_rpmb_wlun(to_scsi_device(dev)) ? 0 : -ENODEV; +} + +static inline int ufshcd_clear_rpmb_uac (struct ufs_hba *hba) +{ + int ret = 0; + + if (!hba->wlun_rpmb_clr_ua) + return 0; + ret = ufshcd_clear_ua_wlun(hba, UFS_UPIU_RPMB_WLUN); + if (!ret) + hba->wlun_rpmb_clr_ua = 0; + return ret; +} + +static int ufshcd_rpmb_runtime_resume (struct device *dev) +{ + struct ufs_hba *hba = wlun_dev_to_hba(dev); + + if (hba->sdev_rpmb) + return ufshcd_clear_rpmb_uac(hba); + return 0; +} + +static int ufshcd_rpmb_resume (struct device *dev) +{ + struct ufs_hba *hba = wlun_dev_to_hba(dev); + + if (hba->sdev_rpmb && !pm_runtime_suspended(dev)) + return ufshcd_clear_rpmb_uac(hba); + return 0; +} + +static const struct dev_pm_ops ufs_rpmb_pm_ops = { + SET_RUNTIME_PM_OPS(NULL, ufshcd_rpmb_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(NULL, ufshcd_rpmb_resume) +}; + +/** +* Describes the ufs rpmb wlun. +* Used only to send uac. +*/ +static struct scsi_driver ufs_rpmb_wlun_template = { + .gendrv = { + .name = "ufs_rpmb_wlun", + .owner = THIS_MODULE, + .probe = ufshcd_rpmb_probe, + .pm = &ufs_rpmb_pm_ops, + }, +}; + static int __init ufshcd_core_init(void) { + int ret; + ufs_debugfs_init(); + + ret = scsi_register_driver(&ufs_dev_wlun_template.gendrv); + if (ret) { + ufs_debugfs_exit(); + return ret; + } + ret = scsi_register_driver(&ufs_rpmb_wlun_template.gendrv); + if (ret) { + ufs_debugfs_exit(); + scsi_unregister_driver(&ufs_dev_wlun_template.gendrv); + return ret; + } return 0; } static void __exit ufshcd_core_exit(void) { ufs_debugfs_exit(); + scsi_unregister_driver(&ufs_dev_wlun_template.gendrv); + scsi_unregister_driver(&ufs_rpmb_wlun_template.gendrv); } module_init(ufshcd_core_init);drivers/scsi/ufs/ufshcd.h +6
-0@@ -72,6 +72,8 @@ enum ufs_event_type { UFS_EVT_LINK_STARTUP_FAIL, UFS_EVT_RESUME_ERR, UFS_EVT_SUSPEND_ERR, + UFS_EVT_WL_SUSP_ERR, + UFS_EVT_WL_RES_ERR, /* abnormal events */ UFS_EVT_DEV_RESET,@@ -804,6 +806,7 @@ struct ufs_hba { struct list_head clk_list_head; bool wlun_dev_clr_ua; + bool wlun_rpmb_clr_ua; /* Number of requests aborts */ int req_abort_count;@@ -841,6 +844,7 @@ struct ufs_hba { #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_root; #endif + u32 luns_avail; }; /* Returns true if clocks can be gated. Otherwise false */@@ -1100,6 +1104,8 @@ int ufshcd_exec_raw_upiu_cmd(struct ufs_hba *hba, enum query_opcode desc_op); int ufshcd_wb_ctrl(struct ufs_hba *hba, bool enable); +int ufshcd_suspend_prepare (struct device *dev); +void ufshcd_resume_complete (struct device *dev); /* Wrapper functions for safely calling variant operations */ static inline const char *ufshcd_get_var_name(struct ufs_hba *hba)include/trace/events/ufs.h +19
-0@@ -246,6 +246,26 @@ DEFINE_EVENT(ufshcd_template, ufshcd_init, int dev_state, int link_state), TP_ARGS(dev_name, err, usecs, dev_state, link_state)); +DEFINE_EVENT(ufshcd_template, ufshcd_wl_suspend, + TP_PROTO(const char *dev_name, int err, s64 usecs, + int dev_state, int link_state), + TP_ARGS(dev_name, err, usecs, dev_state, link_state)); + +DEFINE_EVENT (ufshcd_template, ufshcd_wl_resume, + TP_PROTO(const char *dev_name, int err, s64 usecs, + int dev_state, int link_state), + TP_ARGS(dev_name, err, usecs, dev_state, link_state)); + +DEFINE_EVENT (ufshcd_template, ufshcd_wl_runtime_suspend, + TP_PROTO(const char *dev_name, int err, s64 usecs, + int dev_state, int link_state), + TP_ARGS(dev_name, err, usecs, dev_state, link_state)); + +DEFINE_EVENT (ufshcd_template, ufshcd_wl_runtime_resume, + TP_PROTO(const char *dev_name, int err, s64 usecs, + int dev_state, int link_state), + TP_ARGS(dev_name, err, usecs, dev_state, link_state)); + TRACE_EVENT(ufshcd_command, TP_PROTO(const char *dev_name, enum ufs_trace_str_t str_t, unsigned int tag, u32 doorbell, int transfer_len, u32 intr,
Signature · 7 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, Linux Foundation Collaborative Project _______________________________________________ Linux-mediatek mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-mediatek
Re: [PATCH v12 1/2] scsi: ufs: Enable power management for wlun
Quoted 78 lines · Asutosh Das (asd)
Quoted 77 lines · Adrian Hunter
Quoted 76 lines · Asutosh Das (asd)
Quoted 43 lines · Adrian Hunter
Quoted 38 lines · Asutosh Das
During runtime-suspend of ufs host, the scsi devices are already suspended and so are the queues associated with them. But the ufs host sends SSU to wlun during its runtime-suspend. During the process
blk_queue_enterchecks if the queue is not in suspended state. If so, it waits for the queue to resume, and never comes out of it.
The commit
(d55d15a33: scsi: block: Do not accept any requests while suspended) adds the check if the queue is in suspended state inblk_queue_enter().Call trace: __switch_to+0x174/0x2c4 __schedule+0x478/0x764 schedule+0x9c/0xe0 blk_queue_enter+0x158/0x228 blk_mq_alloc_request+0x40/0xa4 blk_get_request+0x2c/0x70 __scsi_execute+0x60/0x1c4 ufshcd_set_dev_pwr_mode+0x124/0x1e4 ufshcd_suspend+0x208/0x83c ufshcd_runtime_suspend+0x40/0x154 ufshcd_pltfrm_runtime_suspend+0x14/0x20 pm_generic_runtime_suspend+0x28/0x3c __rpm_callback+0x80/0x2a4 rpm_suspend+0x308/0x614 rpm_idle+0x158/0x228 pm_runtime_work+0x84/0xac process_one_work+0x1f0/0x470 worker_thread+0x26c/0x4c8 kthread+0x13c/0x320 ret_from_fork+0x10/0x18Fix this by registering ufs device wlun as a scsi driver and registering it for block runtime-pm. Also make this as a supplier for all other luns. That way, this device wlun suspends after all the consumers and resumes after hba resumes.
Quoted 3 lines
Co-developed-by Can Guo <[email protected]>Signed-off-by Can Guo <[email protected]>Signed-off-by Asutosh Das <[email protected]>Quoted 8 lines
I have some more comments that may help straighten things out.
Also please look at
ufs_debugfs_get_user_access()andufs_debugfs_put_user_access()that now need to scsi_autopm_get/put_devicesdev_ufs_device.It would also be good if you could re-base on linux-next.
Hi Adrian
Thanks for the comments.I agree moving the code to wlun probe and other changes. But it looks to me that it may not fully solve the issue.
Please let me explain my understanding on this:
(Please refer to the logs in v10)
scsi_autopm_*() are invoked on a sdev. pm_runtime_get_suppliers()/rpm_put_suppliers() are on the supplier device.For the device wlun:
slave_configure():
- doesn't set therpm_autosuspend
-pm_runtime_getnoresume()
scsi_sysfs_add_sdev():
-pm_runtime_forbid()
-scsi_autopm_get_device()
-device_add()
-ufshcd_wl_probe()
-scsi_autopm_put_device()
Quoted 8 lines
Quoted 7 lines
Quoted 4 lines
For all other scsi devices:
slave_alloc():
-ufshcd_setup_links()
Say alllink_add:pm_runtime_put(&hba->sdev_ufs_device->sdev_gendev);With
DL_FLAG_RPM_ACTIVE, links will 'get' not 'put'I'm referring to the
pm_runtime_put(sdev_ufs_device)after all the links are setup, that you suggested to add.
Ok
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
slave_configure():
- setrpm_autosuspendscsi_sysfs_add_sdev():
-scsi_autopm_get_device()
-device_add()-> schedules an asyncprobe()
-scsi_autopm_put_device()- (1)
Quoted 6 lines
Quoted 4 lines
Quoted 1 line
Now the
rpm_put_suppliers()can be invoked *after*pm_runtime_get_suppliers()of the asyncprobe(), since both are running in different contexts.Only if the sd device suspends.
Correct. What'd stop the sd device from suspending?
We should be stopping the sd device from suspending here - imho.
You mean for performance reasons. That is something we can look at, but let's get it working first.
Quoted 15 lines
Quoted 8 lines
Quoted 2 lines
In that case, the
usage_countof supplier would be decremented untilrpm_activeof this link becomes 1.Right, because the sd device suspended.
Quoted 1 line
Now the
pm_runtime_get_suppliers()expects thelink_activeto be more than 1.Not sure what you mean here. pm_runtime_*put*_suppliers() won't do anything if the link count is 1.
I'm referring to the logs that I pasted before:
[ 6.941267][ T7] scsi 0:0:0:4:rpm_put_suppliers: [BEF] Supp (0:0:0:49488)usage_count: 4rpm_active: 3------ T196 Context comes in while T7 is running ----------
[ 6.941466][ T196] scsi 0:0:0:4:pm_runtime_get_suppliers: (0:0:0:49488): supp:usage_count: 5rpm_active: 4
--------------------------------------------------------------
Quoted 7 lines
[ 7.788397][ T7] scsi 0:0:0:4:
rpm_put_suppliers: [AFT] Supp (0:0:0:49488)usage_count: 2rpm_active: 1I meant to say that, if the
rpm_put_suppliers()is invoked after thepm_runtime_get_suppliers()as is seen above then thelink_activemay become 1 even *after*pm_runtime_get_suppliers()is invoked.I'm referring to the
pm_runtime_get_suppliers()invoked from:driver_probe_device()- say for, sd 0:0:0:x
|-pm_runtime_get_suppliers()- for sd 0:0:0:49488
I am hoping that was the problem that Rafael's revert dealt with.
Quoted 9 lines
Quoted 6 lines
Quoted 3 lines
Now then, there comes a time, that when
sd_probe()schedules a suspend, the supplierusage_countbecomes 0 and thelink_activebecomes 1. And the supplier suspends before the consumer.sd probe first resumes the sd device which will resume the supplier.
Correct, but it'd again schedule a suspend (since autosuspend is enabled now) at the end of the
sd_probe(). Thereafter, pm_runtime_put_suppliers()(sd 0:0:0:49488) is invoked fromdriver_probe_device()which had actually invokedsd_probe(). That'd make thelink_activeto 1 even when sd 0:0:0:x is active.
If sd 0:0:0:x is active then rpm_get_suppliers() still has +1 rpm_active. pm_runtime_get_suppliers() also has +1 rpm_active.
i.e. rpm_active is 3. If rpm_put_suppliers() is called, it means sd 0:0:0:x has really runtime suspended (not just waiting for autosuspend. Otherwise when the probe ends pm_runtime_put_suppliers() will drop rpm_active from 3 to 2.
But it is a bit theoretical. Let's try it and see.
Quoted 11 lines
Quoted 9 lines
Quoted 6 lines
So I was wondering, what'd make sure that the
pm_runtime_get_suppliers()fromdriver_probe_device()is invoked afterscsi_autopm_put_device()(1) finishes therpm_put_suppliers().Am not sure if I'm missing something in this.
Do you think, the current changes alone can fix the above issue?Yes, but let's see.
Essentially, we should stop the sd device from runtime suspending until it's probe is done. Then allow the same. Does it make sense? Please let me know what you think.
I really think it would be good to try the changes that have been identified and see how it behaves.
Then go from there.
_______________________________________________
Linux-mediatek mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-mediatek
Re: [PATCH v12 1/2] scsi: ufs: Enable power management for wlun
Quoted 84 lines · Adrian Hunter
Quoted 83 lines · Asutosh Das (asd)
Quoted 82 lines · Adrian Hunter
Quoted 80 lines · Asutosh Das (asd)
Quoted 43 lines · Adrian Hunter
Quoted 38 lines · Asutosh Das
During runtime-suspend of ufs host, the scsi devices are already suspended and so are the queues associated with them. But the ufs host sends SSU to wlun during its runtime-suspend. During the process
blk_queue_enterchecks if the queue is not in suspended state. If so, it waits for the queue to resume, and never comes out of it.
The commit
(d55d15a33: scsi: block: Do not accept any requests while suspended) adds the check if the queue is in suspended state inblk_queue_enter().Call trace: __switch_to+0x174/0x2c4 __schedule+0x478/0x764 schedule+0x9c/0xe0 blk_queue_enter+0x158/0x228 blk_mq_alloc_request+0x40/0xa4 blk_get_request+0x2c/0x70 __scsi_execute+0x60/0x1c4 ufshcd_set_dev_pwr_mode+0x124/0x1e4 ufshcd_suspend+0x208/0x83c ufshcd_runtime_suspend+0x40/0x154 ufshcd_pltfrm_runtime_suspend+0x14/0x20 pm_generic_runtime_suspend+0x28/0x3c __rpm_callback+0x80/0x2a4 rpm_suspend+0x308/0x614 rpm_idle+0x158/0x228 pm_runtime_work+0x84/0xac process_one_work+0x1f0/0x470 worker_thread+0x26c/0x4c8 kthread+0x13c/0x320 ret_from_fork+0x10/0x18Fix this by registering ufs device wlun as a scsi driver and registering it for block runtime-pm. Also make this as a supplier for all other luns. That way, this device wlun suspends after all the consumers and resumes after hba resumes.
Quoted 3 lines
Co-developed-by Can Guo <[email protected]>Signed-off-by Can Guo <[email protected]>Signed-off-by Asutosh Das <[email protected]>Quoted 8 lines
I have some more comments that may help straighten things out.
Also please look at
ufs_debugfs_get_user_access()andufs_debugfs_put_user_access()that now need to scsi_autopm_get/put_devicesdev_ufs_device.It would also be good if you could re-base on linux-next.
Hi Adrian
Thanks for the comments.I agree moving the code to wlun probe and other changes. But it looks to me that it may not fully solve the issue.
Please let me explain my understanding on this:
(Please refer to the logs in v10)
scsi_autopm_*() are invoked on a sdev. pm_runtime_get_suppliers()/rpm_put_suppliers() are on the supplier device.For the device wlun:
slave_configure():
- doesn't set therpm_autosuspend
-pm_runtime_getnoresume()
scsi_sysfs_add_sdev():
-pm_runtime_forbid()
-scsi_autopm_get_device()
-device_add()
-ufshcd_wl_probe()
-scsi_autopm_put_device()For all other scsi devices:
slave_alloc():
-ufshcd_setup_links()
Say alllink_add:pm_runtime_put(&hba->sdev_ufs_device->sdev_gendev);
Quoted 20 lines
Quoted 3 lines
Quoted 2 lines
With
DL_FLAG_RPM_ACTIVE, links will 'get' not 'put'I'm referring to the
pm_runtime_put(sdev_ufs_device)after all the links are setup, that you suggested to add.Ok
Quoted 13 lines
Quoted 11 lines
Quoted 8 lines
slave_configure():
- setrpm_autosuspendscsi_sysfs_add_sdev():
-scsi_autopm_get_device()
-device_add()-> schedules an asyncprobe()
-scsi_autopm_put_device()- (1)Now the
rpm_put_suppliers()can be invoked *after*pm_runtime_get_suppliers()of the asyncprobe(), since both are running in different contexts.Only if the sd device suspends.
Correct. What'd stop the sd device from suspending?
We should be stopping the sd device from suspending here - imho.
Hi Adrian,
Thanks for the comments.
Quoted 3 lines
You mean for performance reasons. That is something we can look at, but let's get it working first.
Not for performance reasons. I meant to say that this issue can be fixed if we stop the sd devices from suspending until the sd_probe() is completed.
Quoted 17 lines
Quoted 17 lines
Quoted 8 lines
Quoted 2 lines
In that case, the
usage_countof supplier would be decremented untilrpm_activeof this link becomes 1.Right, because the sd device suspended.
Quoted 1 line
Now the
pm_runtime_get_suppliers()expects thelink_activeto be more than 1.Not sure what you mean here. pm_runtime_*put*_suppliers() won't do anything if the link count is 1.
I'm referring to the logs that I pasted before:
[ 6.941267][ T7] scsi 0:0:0:4:rpm_put_suppliers: [BEF] Supp (0:0:0:49488)usage_count: 4rpm_active: 3------ T196 Context comes in while T7 is running ----------
[ 6.941466][ T196] scsi 0:0:0:4:pm_runtime_get_suppliers: (0:0:0:49488): supp:usage_count: 5rpm_active: 4
--------------------------------------------------------------[ 7.788397][ T7] scsi 0:0:0:4:
rpm_put_suppliers: [AFT] Supp (0:0:0:49488)usage_count: 2rpm_active: 1
Quoted 8 lines
Quoted 5 lines
I meant to say that, if the
rpm_put_suppliers()is invoked after thepm_runtime_get_suppliers()as is seen above then thelink_activemay become 1 even *after*pm_runtime_get_suppliers()is invoked.I'm referring to the
pm_runtime_get_suppliers()invoked from:driver_probe_device()- say for, sd 0:0:0:x
|-pm_runtime_get_suppliers()- for sd 0:0:0:49488I am hoping that was the problem that Rafael's revert dealt with.
I think the issue is in the sequence of events.
If rpm_put_suppliers() runs after pm_runtime_get_suppliers() this issue can occur.
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 3 lines
Now then, there comes a time, that when
sd_probe()schedules a suspend, the supplierusage_countbecomes 0 and thelink_activebecomes 1. And the supplier suspends before the consumer.
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
sd probe first resumes the sd device which will resume the supplier.
Correct, but it'd again schedule a suspend (since autosuspend is enabled now) at the end of the
sd_probe(). Thereafter, pm_runtime_put_suppliers()(sd 0:0:0:49488) is invoked fromdriver_probe_device()which had actually invokedsd_probe(). That'd make thelink_activeto 1 even when sd 0:0:0:x is active.If sd 0:0:0:x is active then
rpm_get_suppliers()still has +1rpm_active.pm_runtime_get_suppliers()also has +1rpm_active.
i.e.rpm_activeis 3. Ifrpm_put_suppliers()is called, it means sd 0:0:0:x has really runtime suspended (not just waiting for autosuspend. Otherwise when the probe endspm_runtime_put_suppliers()will droprpm_activefrom 3 to 2.
In the good case it'd drop from 3 to 2. But in the bad case, I see that it drops to 1. That's when the supplier suspends before the consumer.
That would happen when rpm_put_suppliers() runs after the pm_runtime_get_suppliers() is completed and decrements the usage_count of supplier until link_active is 1. At that point yes, sd 0:0:0:x has really runtime-suspended. sd_probe() would resume it and schedule a suspend at the end of probe.
IIUC, below is the sequence of events that can lead to this issue:
1. sd 0:0:0:x schedules an async probe
2. sd 0:0:0:x invokes scsi_autopm_put_device()
3. async probe completes pm_runtime_get_suppliers() increments the rpm_active.
4. suspend of sd 0:0:0:x is invoked and rpm_put_suppliers() is invoked which decrements the link_active (this was incremented in 3 above)
5. sd_probe() is invoked which resumes it and schedules a suspend
6. pm_runtime_put_suppliers() is invoked which decreases the link_active to 1 and supplier suspends before the consumer.
So my solution was to stop sd 0:0:0:x from runtime suspending until the sd_probe() is done.
Quoted 10 lines
But it is a bit theoretical. Let's try it and see.
Quoted 7 lines
Quoted 7 lines
Quoted 6 lines
So I was wondering, what'd make sure that the
pm_runtime_get_suppliers()fromdriver_probe_device()is invoked afterscsi_autopm_put_device()(1) finishes therpm_put_suppliers().Am not sure if I'm missing something in this.
Do you think, the current changes alone can fix the above issue?
Quoted 9 lines
Quoted 4 lines
Quoted 2 lines
Yes, but let's see.
Essentially, we should stop the sd device from runtime suspending until it's probe is done. Then allow the same. Does it make sense? Please let me know what you think.
I really think it would be good to try the changes that have been identified and see how it behaves.
Then go from there.
Sure, I've pushed the changes v13 today.
I will test it after the changes are finalized.
Signature · 7 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, Linux Foundation Collaborative Project _______________________________________________ Linux-mediatek mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-mediatek
Re: [PATCH v12 1/2] scsi: ufs: Enable power management for wlun
Quoted 106 lines · Asutosh Das (asd)
Quoted 84 lines · Adrian Hunter
Quoted 83 lines · Asutosh Das (asd)
Quoted 82 lines · Adrian Hunter
Quoted 80 lines · Asutosh Das (asd)
Quoted 43 lines · Adrian Hunter
Quoted 38 lines · Asutosh Das
During runtime-suspend of ufs host, the scsi devices are already suspended and so are the queues associated with them. But the ufs host sends SSU to wlun during its runtime-suspend. During the process
blk_queue_enterchecks if the queue is not in suspended state. If so, it waits for the queue to resume, and never comes out of it.
The commit
(d55d15a33: scsi: block: Do not accept any requests while suspended) adds the check if the queue is in suspended state inblk_queue_enter().Call trace: __switch_to+0x174/0x2c4 __schedule+0x478/0x764 schedule+0x9c/0xe0 blk_queue_enter+0x158/0x228 blk_mq_alloc_request+0x40/0xa4 blk_get_request+0x2c/0x70 __scsi_execute+0x60/0x1c4 ufshcd_set_dev_pwr_mode+0x124/0x1e4 ufshcd_suspend+0x208/0x83c ufshcd_runtime_suspend+0x40/0x154 ufshcd_pltfrm_runtime_suspend+0x14/0x20 pm_generic_runtime_suspend+0x28/0x3c __rpm_callback+0x80/0x2a4 rpm_suspend+0x308/0x614 rpm_idle+0x158/0x228 pm_runtime_work+0x84/0xac process_one_work+0x1f0/0x470 worker_thread+0x26c/0x4c8 kthread+0x13c/0x320 ret_from_fork+0x10/0x18Fix this by registering ufs device wlun as a scsi driver and registering it for block runtime-pm. Also make this as a supplier for all other luns. That way, this device wlun suspends after all the consumers and resumes after hba resumes.
Quoted 3 lines
Co-developed-by Can Guo <[email protected]>Signed-off-by Can Guo <[email protected]>Signed-off-by Asutosh Das <[email protected]>Quoted 8 lines
I have some more comments that may help straighten things out.
Also please look at
ufs_debugfs_get_user_access()andufs_debugfs_put_user_access()that now need to scsi_autopm_get/put_devicesdev_ufs_device.It would also be good if you could re-base on linux-next.
Hi Adrian
Thanks for the comments.I agree moving the code to wlun probe and other changes. But it looks to me that it may not fully solve the issue.
Please let me explain my understanding on this:
(Please refer to the logs in v10)
scsi_autopm_*() are invoked on a sdev. pm_runtime_get_suppliers()/rpm_put_suppliers() are on the supplier device.For the device wlun:
slave_configure():
- doesn't set therpm_autosuspend
-pm_runtime_getnoresume()
scsi_sysfs_add_sdev():
-pm_runtime_forbid()
-scsi_autopm_get_device()
-device_add()
-ufshcd_wl_probe()
-scsi_autopm_put_device()For all other scsi devices:
slave_alloc():
-ufshcd_setup_links()
Say alllink_add:pm_runtime_put(&hba->sdev_ufs_device->sdev_gendev);Quoted 20 lines
Quoted 3 lines
Quoted 2 lines
With
DL_FLAG_RPM_ACTIVE, links will 'get' not 'put'I'm referring to the
pm_runtime_put(sdev_ufs_device)after all the links are setup, that you suggested to add.Ok
Quoted 13 lines
Quoted 11 lines
Quoted 8 lines
slave_configure():
- setrpm_autosuspendscsi_sysfs_add_sdev():
-scsi_autopm_get_device()
-device_add()-> schedules an asyncprobe()
-scsi_autopm_put_device()- (1)Now the
rpm_put_suppliers()can be invoked *after*pm_runtime_get_suppliers()of the asyncprobe(), since both are running in different contexts.Only if the sd device suspends.
Correct. What'd stop the sd device from suspending?
We should be stopping the sd device from suspending here - imho.
Quoted 7 lines
Hi Adrian,
Thanks for the comments.Quoted 3 lines
You mean for performance reasons. That is something we can look at, but let's get it working first.
Not for performance reasons. I meant to say that this issue can be fixed if we stop the sd devices from suspending until the
sd_probe()is completed.
To me that looks like hiding the problem rather than fixing it.
For example, Rafael's revert was a real issue that we uncovered.
From a maintenance point of view, hiding problems rather than fixing them, creates an unsustainable technical debt for the future.
Quoted 19 lines
Quoted 19 lines
Quoted 19 lines
Quoted 8 lines
Quoted 2 lines
In that case, the
usage_countof supplier would be decremented untilrpm_activeof this link becomes 1.Right, because the sd device suspended.
Quoted 1 line
Now the
pm_runtime_get_suppliers()expects thelink_activeto be more than 1.Not sure what you mean here. pm_runtime_*put*_suppliers() won't do anything if the link count is 1.
I'm referring to the logs that I pasted before:
[ 6.941267][ T7] scsi 0:0:0:4:rpm_put_suppliers: [BEF] Supp (0:0:0:49488)usage_count: 4rpm_active: 3------ T196 Context comes in while T7 is running ----------
[ 6.941466][ T196] scsi 0:0:0:4:pm_runtime_get_suppliers: (0:0:0:49488): supp:usage_count: 5rpm_active: 4
--------------------------------------------------------------[ 7.788397][ T7] scsi 0:0:0:4:
rpm_put_suppliers: [AFT] Supp (0:0:0:49488)usage_count: 2rpm_active: 1I meant to say that, if the
rpm_put_suppliers()is invoked after thepm_runtime_get_suppliers()as is seen above then thelink_activemay become 1 even *after*pm_runtime_get_suppliers()is invoked.
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines
I'm referring to the
pm_runtime_get_suppliers()invoked from:driver_probe_device()- say for, sd 0:0:0:x
|-pm_runtime_get_suppliers()- for sd 0:0:0:49488I am hoping that was the problem that Rafael's revert dealt with.
I think the issue is in the sequence of events.
Ifrpm_put_suppliers()runs afterpm_runtime_get_suppliers()this issue can occur.
Then it would be a core PM issue not a UFS issue.
Quoted 24 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
Now then, there comes a time, that when
sd_probe()schedules a suspend, the supplierusage_countbecomes 0 and thelink_activebecomes 1. And the supplier suspends before the consumer.Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
sd probe first resumes the sd device which will resume the supplier.
Correct, but it'd again schedule a suspend (since autosuspend is enabled now) at the end of the
sd_probe(). Thereafter, pm_runtime_put_suppliers()(sd 0:0:0:49488) is invoked fromdriver_probe_device()which had actually invokedsd_probe(). That'd make thelink_activeto 1 even when sd 0:0:0:x is active.If sd 0:0:0:x is active then
rpm_get_suppliers()still has +1rpm_active.pm_runtime_get_suppliers()also has +1rpm_active.
i.e.rpm_activeis 3. Ifrpm_put_suppliers()is called, it means sd 0:0:0:x has really runtime suspended (not just waiting for autosuspend. Otherwise when the probe endspm_runtime_put_suppliers()will droprpm_activefrom 3 to 2.In the good case it'd drop from 3 to 2. But in the bad case, I see that it drops to 1. That's when the supplier suspends before the consumer.
That would happen whenrpm_put_suppliers()runs after thepm_runtime_get_suppliers()is completed and decrements theusage_countof supplier untillink_activeis 1. At that point yes, sd 0:0:0:x has really runtime-suspended.sd_probe()would resume it and schedule a suspend at the end of probe.IIUC, below is the sequence of events that can lead to this issue:
1. sd 0:0:0:x schedules an async probe
2. sd 0:0:0:x invokesscsi_autopm_put_device()
3. async probe completespm_runtime_get_suppliers()increments therpm_active.
4. suspend of sd 0:0:0:x is invoked andrpm_put_suppliers()is invoked which decrements thelink_active(this was incremented in 3 above)
5.sd_probe()is invoked which resumes it and schedules a suspend
6.pm_runtime_put_suppliers()is invoked which decreases thelink_activeto 1 and supplier suspends before the consumer.
Quoted 1 line
So my solution was to stop sd 0:0:0:x from runtime suspending until the
sd_probe()is done.
I'll look into it, but maybe the following would help:
drivers/base/power/runtime.c +1 -0
@@ -312,6 +312,7 @@ static void rpm_put_suppliers(struct device *dev)list_for_each_entry_rcu (link, &dev->links.suppliers, c_node,device_links_read_lock_held ()) { + link->supplier_preactivated = false;while (refcount_dec_not_one (&link->rpm_active))pm_runtime_put (link->supplier); }
Quoted 16 lines
Quoted 16 lines
But it is a bit theoretical. Let's try it and see.
Quoted 11 lines
Quoted 9 lines
Quoted 6 lines
So I was wondering, what'd make sure that the
pm_runtime_get_suppliers()fromdriver_probe_device()is invoked afterscsi_autopm_put_device()(1) finishes therpm_put_suppliers().Am not sure if I'm missing something in this.
Do you think, the current changes alone can fix the above issue?Yes, but let's see.
Essentially, we should stop the sd device from runtime suspending until it's probe is done. Then allow the same. Does it make sense? Please let me know what you think.
Quoted 7 lines
Quoted 4 lines
I really think it would be good to try the changes that have been identified and see how it behaves.
Then go from there.
Sure, I've pushed the changes v13 today.
I will test it after the changes are finalized.
Thank you! :-)
_______________________________________________
Linux-mediatek mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-mediatek