[PATCH v1 9/9] scsi: ufs: enable FASTAUTO mode during low load condition
From: Subhash Jadavani We are currently running UFS link in HS-G3 FAST mode during high load condition for best possible performance and in HS-G2 FAST mode during low load condition to save power. As we are anyway scaling down from HS-G3 to HS-G2, we can also change the mode from FAST to FASTAUTO. So we looked at the performance numbers with HS-G2 FASTAUTO mode and they are good enough for most of the low bandwidth usecases. But Samsung UFS memory devices are exception which has really low sequential read throughput in FAST AUTO mode hence we will only be enabling FAST AUTO mode for other UFS device vendors. Signed-off-by: Subhash Jadavani Signed-off-by: Can Guo Signed-off-by: Asutosh Das --- drivers/scsi/ufs/ufshcd.c | 4 1 file changed, 4 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 50588cf..a6e43f9 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -1088,6 +1088,10 @@ static int ufshcd_scale_gear(struct ufs_hba *hba, bool scale_up) /* scale down gear */ new_pwr_info.gear_tx = UFS_MIN_GEAR_TO_SCALE_DOWN; new_pwr_info.gear_rx = UFS_MIN_GEAR_TO_SCALE_DOWN; + if (!(hba->dev_quirks & UFS_DEVICE_NO_FASTAUTO)) { + new_pwr_info.pwr_tx = FASTAUTO_MODE; + new_pwr_info.pwr_rx = FASTAUTO_MODE; + } } } -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH v1 4/9] scsi: ufs: add option to change default UFS power management level
From: Subhash Jadavani UFS device and link can be put in multiple different low power modes hence UFS driver supports multiple different low power modes. By default UFS driver selects the default (optimal) low power mode (which gives moderate power savings and have relatively less enter and exit latencies) but we might have to tune this default power mode for different chipset platforms to meet the low power requirements/goals. Hence this patch adds option to change default UFS low power mode (level). Signed-off-by: Subhash Jadavani Signed-off-by: Venkat Gopalakrishnan Signed-off-by: Can Guo Signed-off-by: Asutosh Das --- .../devicetree/bindings/ufs/ufshcd-pltfrm.txt | 11 drivers/scsi/ufs/ufshcd-pltfrm.c | 14 +++ drivers/scsi/ufs/ufshcd.c | 29 +++--- drivers/scsi/ufs/ufshcd.h | 4 +-- 4 files changed, 47 insertions(+), 11 deletions(-) diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt index c39dfef..f564d9a 100644 --- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt +++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt @@ -38,6 +38,15 @@ Optional properties: defined or a value in the array is "0" then it is assumed that the frequency is set by the parent clock or a fixed rate clock source. +- rpm-level: UFS Runtime power management level. Following PM levels are supported: + 0 - Both UFS device and Link in active state (Highest power consumption) + 1 - UFS device in active state but Link in Hibern8 state + 2 - UFS device in Sleep state but Link in active state + 3 - UFS device in Sleep state and Link in hibern8 state (default PM level) + 4 - UFS device in Power-down state and Link in Hibern8 state + 5 - UFS device in Power-down state and Link in OFF state (Lowest power consumption) +- spm-level: UFS System power management level. Allowed PM levels are same as rpm-level. + -lanes-per-direction : number of lanes available per direction - either 1 or 2. Note that it is assume same number of lanes is used both directions at once. If not specified, default is 2 lanes per direction. @@ -66,4 +75,6 @@ Example: freq-table-hz = <1 2>, <0 0>, <0 0>; phys = <>; phy-names = "ufsphy"; + rpm-level = <3>; + spm-level = <5>; }; diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index e82bde0..7dba799 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -221,6 +221,19 @@ static int ufshcd_parse_regulator_info(struct ufs_hba *hba) return err; } +static void ufshcd_parse_pm_levels(struct ufs_hba *hba) +{ + struct device *dev = hba->dev; + struct device_node *np = dev->of_node; + + if (np) { + if (of_property_read_u32(np, "rpm-level", >rpm_lvl)) + hba->rpm_lvl = -1; + if (of_property_read_u32(np, "spm-level", >spm_lvl)) + hba->spm_lvl = -1; + } +} + #ifdef CONFIG_PM /** * ufshcd_pltfrm_suspend - suspend power management function @@ -340,6 +353,7 @@ int ufshcd_pltfrm_init(struct platform_device *pdev, goto dealloc_host; } + ufshcd_parse_pm_levels(hba); pm_runtime_set_active(>dev); pm_runtime_enable(>dev); diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index b03f3ea..e950204 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -192,6 +192,14 @@ struct ufs_pm_lvl_states ufs_pm_lvl_states[] = { return UFS_PM_LVL_0; } +static inline bool ufshcd_is_valid_pm_lvl(int lvl) +{ + if (lvl >= 0 && lvl < ARRAY_SIZE(ufs_pm_lvl_states)) + return true; + else + return false; +} + static struct ufs_dev_fix ufs_fixups[] = { /* UFS cards deviations table */ UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL, @@ -8051,16 +8059,19 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) } /* -* Set the default power management level for runtime and system PM. -* Default power saving mode is to keep UFS link in Hibern8 state -* and UFS device in sleep state. +* If rpm_lvl and and spm_lvl are not already set to valid levels, +* set the default power management level for UFS runtime and system +* suspend
[PATCH v1 6/9] scsi: ufs: optimize clock, pm_qos, hibern8 handling in queuecommand
From: Subhash Jadavani ufshcd_queuecommand() vote for the resources in this order: clocks, pm_qos latency, hibern8 exit. If any of these votes are not already applied, each one has to be applied asynchronously and in that case we are releasing all the previously applied resource votes (for example, if hibern8 exit has to be completed asynchronously, we release the votes for pm_qos and clocks as well). This is not a optimal solution instead we should skip scheduling the unvoting work for already voted resources. Signed-off-by: Subhash Jadavani Signed-off-by: Can Guo Signed-off-by: Asutosh Das --- drivers/scsi/ufs/ufs-qcom.c | 2 +- drivers/scsi/ufs/ufshcd.c | 33 + drivers/scsi/ufs/ufshcd.h | 2 +- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 221820a..fa01924 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -1605,7 +1605,7 @@ int ufs_qcom_testbus_config(struct ufs_qcom_host *host) * committed before returning. */ mb(); - ufshcd_release(host->hba); + ufshcd_release(host->hba, false); pm_runtime_put_sync(host->hba->dev); return 0; diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 8a56ef6..40d9c35 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -1175,7 +1175,7 @@ static int ufshcd_devfreq_scale(struct ufs_hba *hba, bool scale_up) out: ufshcd_clock_scaling_unprepare(hba); - ufshcd_release(hba); + ufshcd_release_all(hba); return ret; } @@ -1447,7 +1447,7 @@ static ssize_t ufshcd_clkscale_enable_store(struct device *dev, __func__, err); } - ufshcd_release(hba); + ufshcd_release(hba, false); pm_runtime_put_sync(hba->dev); out: return count; @@ -1662,7 +1662,7 @@ static void ufshcd_gate_work(struct work_struct *work) } /* host lock must be held before calling this variant */ -static void __ufshcd_release(struct ufs_hba *hba) +static void __ufshcd_release(struct ufs_hba *hba, bool no_sched) { if (!ufshcd_is_clkgating_allowed(hba)) return; @@ -1673,7 +1673,7 @@ static void __ufshcd_release(struct ufs_hba *hba) || hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL || hba->lrb_in_use || hba->outstanding_tasks || hba->active_uic_cmd || hba->uic_async_done - || ufshcd_eh_in_progress(hba)) + || ufshcd_eh_in_progress(hba) || no_sched) return; hba->clk_gating.state = REQ_CLKS_OFF; @@ -1682,12 +1682,12 @@ static void __ufshcd_release(struct ufs_hba *hba) msecs_to_jiffies(hba->clk_gating.delay_ms)); } -void ufshcd_release(struct ufs_hba *hba) +void ufshcd_release(struct ufs_hba *hba, bool no_sched) { unsigned long flags; spin_lock_irqsave(hba->host->host_lock, flags); - __ufshcd_release(hba); + __ufshcd_release(hba, no_sched); spin_unlock_irqrestore(hba->host->host_lock, flags); } EXPORT_SYMBOL_GPL(ufshcd_release); @@ -1738,7 +1738,7 @@ static ssize_t ufshcd_clkgate_enable_store(struct device *dev, goto out; if (value) { - ufshcd_release(hba); + ufshcd_release(hba, false); } else { spin_lock_irqsave(hba->host->host_lock, flags); hba->clk_gating.active_reqs++; @@ -1870,7 +1870,7 @@ int ufshcd_hibern8_hold(struct ufs_hba *hba, bool async) } /* host lock must be held before calling this variant */ -static void __ufshcd_hibern8_release(struct ufs_hba *hba) +static void __ufshcd_hibern8_release(struct ufs_hba *hba, bool no_sched) { unsigned long delay_in_jiffies; @@ -1884,7 +1884,8 @@ static void __ufshcd_hibern8_release(struct ufs_hba *hba) || hba->hibern8_on_idle.is_suspended || hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL || hba->lrb_in_use || hba->outstanding_tasks - || hba->active_uic_cmd || hba->uic_async_done) + || hba->active_uic_cmd || hba->uic_async_done + || ufshcd_eh_in_progress(hba) || no_sched) return; hba->hibern8_on_idle.state = REQ_HIBERN8_ENTER; @@ -1907,12 +1908,12 @@ static void __ufshcd_hibern8_release(struct ufs_hba *hba) delay_in_jiffies); } -void ufshcd_hibern8_release(struct ufs_hba *hba) +void ufshcd_hibern8_release(struct ufs_hba *hba, bool no_sched) { unsigned long flags; spin_lock_irqsave(hba->host->host_lock, flags); - __ufshcd_hibern8_release(hba); + __ufshcd_hibern8_release(hba, no_sched); spin_unlock_irqrestore(hba->host->host_lock, fl
[PATCH v1 2/9] scsi: Allow auto suspend override by low-level driver
From: Sujit Reddy Thumma Until now the scsi mid-layer forbids runtime suspend till userspace enables it. This is mainly to quarantine some disks with broken runtime power management or have high latencies executing suspend resume callbacks. If the userspace doesn't enable the runtime suspend the underlying hardware will be always on even when it is not doing any useful work and thus wasting power. Some low-level drivers for the controllers can efficiently use runtime power management to reduce power consumption and improve battery life. Allow runtime suspend parameters override within the LLD itself instead of waiting for userspace to control the power management. Signed-off-by: Sujit Reddy Thumma Signed-off-by: Subhash Jadavani Signed-off-by: Asutosh Das --- drivers/scsi/scsi_scan.c | 4 drivers/scsi/scsi_sysfs.c | 3 ++- drivers/scsi/sd.c | 2 ++ include/scsi/scsi_device.h | 3 +++ 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 0880d97..5b7232a 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -969,6 +969,10 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, transport_configure_device(>sdev_gendev); + /* The LLD can override auto suspend tunables in ->slave_configure() */ + sdev->use_rpm_auto = 0; + sdev->autosuspend_delay = SCSI_DEFAULT_AUTOSUSPEND_DELAY; + if (sdev->host->hostt->slave_configure) { ret = sdev->host->hostt->slave_configure(sdev); if (ret) { diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 7943b76..706e778 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -1266,7 +1266,8 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) device_enable_async_suspend(>sdev_gendev); scsi_autopm_get_target(starget); pm_runtime_set_active(>sdev_gendev); - pm_runtime_forbid(>sdev_gendev); + if (!sdev->use_rpm_auto) + pm_runtime_forbid(>sdev_gendev); pm_runtime_enable(>sdev_gendev); scsi_autopm_put_target(starget); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index a6201e6..205624f 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -3269,6 +3269,8 @@ static void sd_probe_async(void *data, async_cookie_t cookie) } blk_pm_runtime_init(sdp->request_queue, dev); + if (sdp->autosuspend_delay >= 0) + pm_runtime_set_autosuspend_delay(dev, sdp->autosuspend_delay); device_add_disk(dev, gd); if (sdkp->capacity) sd_dif_config_host(sdkp); diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 4c36af6..1d5ae90 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -197,7 +197,10 @@ struct scsi_device { unsigned broken_fua:1; /* Don't set FUA bit */ unsigned lun_in_cdb:1; /* Store LUN bits in CDB[1] */ unsigned unmap_limit_for_ws:1; /* Use the UNMAP limit for WRITE SAME */ + unsigned use_rpm_auto:1; /* Enable runtime PM auto suspend */ +#define SCSI_DEFAULT_AUTOSUSPEND_DELAY -1 + int autosuspend_delay; atomic_t disk_events_disable_depth; /* disable depth for disk events */ DECLARE_BITMAP(supported_events, SDEV_EVT_MAXBITS); /* supported events */ -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH v1 8/9] scsi: ufs: enable runtime pm only after ufshcd init
From: Gilad Broner Previous code enables runtime pm before ufshcd_init() is completed, and before the hba struct is stored in the platform device private data. This means that pm runtime calls will have null hba pointer as well as partially initialized driver. Instead, enable pm runtime only after ufshcd_init() is done and after hba struct is stored in the platform device private data. Signed-off-by: Gilad Broner Signed-off-by: Can Guo Signed-off-by: Asutosh Das --- drivers/scsi/ufs/ufshcd-pltfrm.c | 11 --- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index 7dba799..8332d99 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -354,24 +354,21 @@ int ufshcd_pltfrm_init(struct platform_device *pdev, } ufshcd_parse_pm_levels(hba); - pm_runtime_set_active(>dev); - pm_runtime_enable(>dev); ufshcd_init_lanes_per_dir(hba); err = ufshcd_init(hba, mmio_base, irq); if (err) { dev_err(dev, "Initialization failed\n"); - goto out_disable_rpm; + goto dealloc_host; } platform_set_drvdata(pdev, hba); - return 0; + pm_runtime_set_active(>dev); + pm_runtime_enable(>dev); -out_disable_rpm: - pm_runtime_disable(>dev); - pm_runtime_set_suspended(>dev); + return 0; dealloc_host: ufshcd_dealloc_host(hba); out: -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH v1 5/9] scsi: ufs: add support for hibern8 on idle
From: Subhash Jadavani In order to save power we should put the UFS link into hibern8 as soon as UFS link is idle and power measurement of active usecases (like audio/video playback/recording) show that putting UFS link in hibern8 @ 10ms of idle (if not earlier) would save significant power. Our current available solution is to do hibern8 with clock gating @idle timeout of 150ms. As clock gating has huge latencies (7ms each in enter and exit), we cannot bring down the idle timeout to <=10ms without degrading UFS throughput. Hence this change has added support to enter into hibern8 with another idle timer. Signed-off-by: Subhash Jadavani Signed-off-by: Can Guo Signed-off-by: Asutosh Das --- drivers/scsi/ufs/ufshcd.c | 372 - drivers/scsi/ufs/ufshcd.h | 39 + include/trace/events/ufs.h | 20 +++ 3 files changed, 397 insertions(+), 34 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index e950204..8a56ef6 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -246,6 +246,8 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on, static irqreturn_t ufshcd_intr(int irq, void *__hba); static int ufshcd_change_power_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *pwr_mode); +static void ufshcd_hold_all(struct ufs_hba *hba); +static void ufshcd_release_all(struct ufs_hba *hba); static inline bool ufshcd_valid_tag(struct ufs_hba *hba, int tag) { return tag >= 0 && tag < hba->nutrs; @@ -853,6 +855,18 @@ static inline bool ufshcd_is_hba_active(struct ufs_hba *hba) ? false : true; } +static const char *ufshcd_hibern8_on_idle_state_to_string( + enum ufshcd_hibern8_on_idle_state state) +{ + switch (state) { + case HIBERN8_ENTERED: return "HIBERN8_ENTERED"; + case HIBERN8_EXITED:return "HIBERN8_EXITED"; + case REQ_HIBERN8_ENTER: return "REQ_HIBERN8_ENTER"; + case REQ_HIBERN8_EXIT: return "REQ_HIBERN8_EXIT"; + default:return "UNKNOWN_STATE"; + } +} + u32 ufshcd_get_local_unipro_ver(struct ufs_hba *hba) { /* HCI version 1.0 and 1.1 supports UniPro 1.41 */ @@ -993,7 +1007,7 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba, bool timeout = false, do_last_check = false; ktime_t start; - ufshcd_hold(hba, false); + ufshcd_hold_all(hba); spin_lock_irqsave(hba->host->host_lock, flags); /* * Wait for all the outstanding tasks/transfer requests. @@ -1038,7 +1052,7 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba, } out: spin_unlock_irqrestore(hba->host->host_lock, flags); - ufshcd_release(hba); + ufshcd_release_all(hba); return ret; } @@ -1601,8 +1615,16 @@ static void ufshcd_gate_work(struct work_struct *work) spin_unlock_irqrestore(hba->host->host_lock, flags); + if (ufshcd_is_hibern8_on_idle_allowed(hba)) + /* +* Hibern8 enter work (on Idle) needs clocks to be ON hence +* make sure that it is flushed before turning off the clocks. +*/ + flush_delayed_work(>hibern8_on_idle.enter_work); + /* put the link into hibern8 mode before turning off clocks */ - if (ufshcd_can_hibern8_during_gating(hba)) { + if (ufshcd_can_hibern8_during_gating(hba) && + ufshcd_is_link_active(hba)) { if (ufshcd_uic_hibern8_enter(hba)) { hba->clk_gating.state = CLKS_ON; trace_ufshcd_clk_gating(dev_name(hba->dev), @@ -1732,6 +1754,8 @@ static void ufshcd_init_clk_gating(struct ufs_hba *hba) { char wq_name[sizeof("ufs_clk_gating_00")]; + hba->clk_gating.state = CLKS_ON; + if (!ufshcd_is_clkgating_allowed(hba)) return; @@ -1774,6 +1798,246 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba) destroy_workqueue(hba->clk_gating.clk_gating_workq); } +/** + * ufshcd_hibern8_hold - Make sure that link is not in hibern8. + * + * @hba: per adapter instance + * @async: This indicates whether caller wants to exit hibern8 asynchronously. + * + * Exit from hibern8 mode and set the link as active. + * + * Return 0 on success, non-zero on failure. + */ +int ufshcd_hibern8_hold(struct ufs_hba *hba, bool async) +{ + int rc = 0; + unsigned long flags; + + if (!ufshcd_is_hibern8_on_idle_allowed(hba)) + goto out; + + spin_lock_irqsave(hba->host->host_lock, flags); + hba->hibern8_on_idle.active_reqs++; + +start: + switch (hba->hibern8_on_idle.state) { + case HIBERN8_EXITED: + break
[PATCH v1 3/9] scsi: ufs: Override auto suspend tunables for ufs
From: Sujit Reddy Thumma Override auto suspend tunables for UFS device LUNs during initialization so as to efficiently manage background operations and the power consumption. Signed-off-by: Sujit Reddy Thumma Signed-off-by: Gilad Broner Signed-off-by: Asutosh Das --- drivers/scsi/ufs/ufshcd.c | 6 ++ 1 file changed, 6 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 77e2b3e..b03f3ea 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -89,6 +89,9 @@ /* Interrupt aggregation default timeout, unit: 40us */ #define INT_AGGR_DEF_TO0x02 +/* default value of auto suspend is 3 seconds */ +#define UFSHCD_AUTO_SUSPEND_DELAY_MS 3000 /* millisecs */ + #define ufshcd_toggle_vreg(_dev, _vreg, _on) \ ({ \ int _ret; \ @@ -4528,6 +4531,9 @@ static int ufshcd_slave_configure(struct scsi_device *sdev) blk_queue_update_dma_pad(q, PRDT_DATA_BYTE_COUNT_PAD - 1); blk_queue_max_segment_size(q, PRDT_DATA_BYTE_COUNT_MAX); + sdev->autosuspend_delay = UFSHCD_AUTO_SUSPEND_DELAY_MS; + sdev->use_rpm_auto = 1; + return 0; } -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH v1 0/9] UFS: Fixes and enhancements
This patch series adds some enhancements and fixes to UFS driver. Gilad Broner (1): scsi: ufs: enable runtime pm only after ufshcd init Subhash Jadavani (6): scsi: ufs: add support to allow non standard behaviours (quirks) scsi: ufs: add option to change default UFS power management level scsi: ufs: add support for hibern8 on idle scsi: ufs: optimize clock, pm_qos, hibern8 handling in queuecommand scsi: ufs: add UFS power collapse support during hibern8 scsi: ufs: enable FASTAUTO mode during low load condition Sujit Reddy Thumma (2): scsi: Allow auto suspend override by low-level driver scsi: ufs: Override auto suspend tunables for ufs .../devicetree/bindings/ufs/ufshcd-pltfrm.txt | 11 + drivers/scsi/scsi_scan.c | 4 + drivers/scsi/scsi_sysfs.c | 3 +- drivers/scsi/sd.c | 2 + drivers/scsi/ufs/ufs-qcom.c| 2 +- drivers/scsi/ufs/ufshcd-pltfrm.c | 25 +- drivers/scsi/ufs/ufshcd.c | 436 ++--- drivers/scsi/ufs/ufshcd.h | 61 ++- include/scsi/scsi_device.h | 3 + include/trace/events/ufs.h | 20 + 10 files changed, 501 insertions(+), 66 deletions(-) -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH v1 1/9] scsi: ufs: add support to allow non standard behaviours (quirks)
From: Subhash Jadavani Some implementation of UFS host controller HW might have some non-standard behaviours (quirks) when compared to behaviour specified by UFSHCI specification. This patch adds a quirk for broken hibernate support. Signed-off-by: Subhash Jadavani Signed-off-by: Can Guo Signed-off-by: Asutosh Das --- drivers/scsi/ufs/ufshcd.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index f51758f..e996a08 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -613,6 +613,9 @@ struct ufs_hba { * enabled via HCE register. */ #define UFSHCI_QUIRK_BROKEN_HCE 0x400 + + /* HIBERN8 support is broken */ + #define UFSHCD_QUIRK_BROKEN_HIBERN8 0x800 unsigned int quirks;/* Deviations from standard UFSHCI spec. */ /* Device deviations from standard UFS device spec. */ -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH v1 7/9] scsi: ufs: add UFS power collapse support during hibern8
From: Subhash Jadavani UFS host controller hardware may allow the host controller to be power collapsed when UFS link is hibern8 state, this change allows the UFS host controller to be power collapsed during hibern8. Signed-off-by: Subhash Jadavani Signed-off-by: Venkat Gopalakrishnan Signed-off-by: Can Guo Signed-off-by: Asutosh Das --- drivers/scsi/ufs/ufshcd.c | 8 ++-- drivers/scsi/ufs/ufshcd.h | 13 - 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 40d9c35..50588cf 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -7673,13 +7673,17 @@ static int ufshcd_vreg_set_hpm(struct ufs_hba *hba) static void ufshcd_hba_vreg_set_lpm(struct ufs_hba *hba) { - if (ufshcd_is_link_off(hba)) + if (ufshcd_is_link_off(hba) || + (ufshcd_is_link_hibern8(hba) +&& ufshcd_is_power_collapse_during_hibern8_allowed(hba))) ufshcd_setup_hba_vreg(hba, false); } static void ufshcd_hba_vreg_set_hpm(struct ufs_hba *hba) { - if (ufshcd_is_link_off(hba)) + if (ufshcd_is_link_off(hba) || + (ufshcd_is_link_hibern8(hba) +&& ufshcd_is_power_collapse_during_hibern8_allowed(hba))) ufshcd_setup_hba_vreg(hba, true); } diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index f79a639..8c5f987 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -728,7 +728,12 @@ struct ufs_hba { * to do background operation when it's active but it might degrade * the performance of ongoing read/write operations. */ -#define UFSHCD_CAP_KEEP_AUTO_BKOPS_ENABLED_EXCEPT_SUSPEND (1 << 5) +#define UFSHCD_CAP_KEEP_AUTO_BKOPS_ENABLED_EXCEPT_SUSPEND (1 << 6) + /* +* If host controller hardware can be power collapsed when UFS link is +* in hibern8 then enable this cap. +*/ +#define UFSHCD_CAP_POWER_COLLAPSE_DURING_HIBERN8 (1 << 7) struct devfreq *devfreq; struct ufs_clk_scaling clk_scaling; @@ -764,6 +769,12 @@ static inline bool ufshcd_is_hibern8_on_idle_allowed(struct ufs_hba *hba) return hba->caps & UFSHCD_CAP_HIBERN8_ENTER_ON_IDLE; } +static inline bool ufshcd_is_power_collapse_during_hibern8_allowed( + struct ufs_hba *hba) +{ + return !!(hba->caps & UFSHCD_CAP_POWER_COLLAPSE_DURING_HIBERN8); +} + static inline bool ufshcd_is_intr_aggr_allowed(struct ufs_hba *hba) { /* DWC UFS Core has the Interrupt aggregation feature but is not detectable*/ -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
Re: [PATCH 1/4] scsi: ufs: add quirk to fix mishandling utrlclr/utmrlclr
On 5/6/2018 3:44 PM, Alim Akhtar wrote: In the right behavior, setting the bit to '0' indicates clear and '1' indicates no change. If host controller handles this the other way, UFSHCI_QUIRK_BROKEN_REQ_LIST_CLR can be used. Signed-off-by: Seungwon JeonSigned-off-by: Alim Akhtar --- drivers/scsi/ufs/ufshcd.c | 21 +++-- drivers/scsi/ufs/ufshcd.h | 5 + 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 00e7905..9898ce5 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -675,7 +675,24 @@ static inline void ufshcd_put_tm_slot(struct ufs_hba *hba, int slot) */ static inline void ufshcd_utrl_clear(struct ufs_hba *hba, u32 pos) { - ufshcd_writel(hba, ~(1 << pos), REG_UTP_TRANSFER_REQ_LIST_CLEAR); + if (hba->quirks & UFSHCI_QUIRK_BROKEN_REQ_LIST_CLR) + ufshcd_writel(hba, (1 << pos), REG_UTP_TRANSFER_REQ_LIST_CLEAR); + else + ufshcd_writel(hba, ~(1 << pos), + REG_UTP_TRANSFER_REQ_LIST_CLEAR); +} + +/** + * ufshcd_utmrl_clear - Clear a bit in UTRMLCLR register + * @hba: per adapter instance + * @pos: position of the bit to be cleared + */ +static inline void ufshcd_utmrl_clear(struct ufs_hba *hba, u32 pos) +{ + if (hba->quirks & UFSHCI_QUIRK_BROKEN_REQ_LIST_CLR) + ufshcd_writel(hba, (1 << pos), REG_UTP_TASK_REQ_LIST_CLEAR); + else + ufshcd_writel(hba, ~(1 << pos), REG_UTP_TASK_REQ_LIST_CLEAR); } /** @@ -5398,7 +5415,7 @@ static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag) goto out; spin_lock_irqsave(hba->host->host_lock, flags); - ufshcd_writel(hba, ~(1 << tag), REG_UTP_TASK_REQ_LIST_CLEAR); + ufshcd_utmrl_clear(hba, tag); spin_unlock_irqrestore(hba->host->host_lock, flags); /* poll for max. 1 sec to clear door bell register by h/w */ diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 8110dcd..43035f8 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -595,6 +595,11 @@ struct ufs_hba { */ #define UFSHCD_QUIRK_PRDT_BYTE_GRAN 0x80 + /* +* Cleaer handling for transfer/task request list is just opposite. +*/ Spell check - should be 'Clear' + #define UFSHCI_QUIRK_BROKEN_REQ_LIST_CLR0x100 + unsigned int quirks;/* Deviations from standard UFSHCI spec. */ /* Device deviations from standard UFS device spec. */ Looks good to me, except the spell-check. -- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH v2 10/10] scsi: ufs: Add clock ungating to a separate workqueue
From: Vijay Viswanath <vvisw...@codeaurora.org> UFS driver can receive a request during memory reclaim by kswapd. So when ufs driver puts the ungate work in queue, and if there are no idle workers, kthreadd is invoked to create a new kworker. Since kswapd task holds a mutex which kthreadd also needs, this can cause a deadlock situation. So ungate work must be done in a separate work queue with WQ_MEM_RECLAIM flag enabled. Such a workqueue will have a rescue thread which will be called when the above deadlock condition is possible. Signed-off-by: Vijay Viswanath <vvisw...@codeaurora.org> Signed-off-by: Can Guo <c...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 11 ++- drivers/scsi/ufs/ufshcd.h | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 557d538..3be61b7 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -1483,7 +1483,8 @@ int ufshcd_hold(struct ufs_hba *hba, bool async) hba->clk_gating.state = REQ_CLKS_ON; trace_ufshcd_clk_gating(dev_name(hba->dev), hba->clk_gating.state); - schedule_work(>clk_gating.ungate_work); + queue_work(hba->clk_gating.clk_gating_workq, + >clk_gating.ungate_work); /* * fall through to check if we should wait for this * work to be done or not. @@ -1669,6 +1670,8 @@ static ssize_t ufshcd_clkgate_enable_store(struct device *dev, static void ufshcd_init_clk_gating(struct ufs_hba *hba) { + char wq_name[sizeof("ufs_clk_gating_00")]; + if (!ufshcd_is_clkgating_allowed(hba)) return; @@ -1676,6 +1679,11 @@ static void ufshcd_init_clk_gating(struct ufs_hba *hba) INIT_DELAYED_WORK(>clk_gating.gate_work, ufshcd_gate_work); INIT_WORK(>clk_gating.ungate_work, ufshcd_ungate_work); + snprintf(wq_name, ARRAY_SIZE(wq_name), "ufs_clk_gating_%d", +hba->host->host_no); + hba->clk_gating.clk_gating_workq = alloc_ordered_workqueue(wq_name, + WQ_MEM_RECLAIM); + hba->clk_gating.is_enabled = true; hba->clk_gating.delay_attr.show = ufshcd_clkgate_delay_show; @@ -1703,6 +1711,7 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba) device_remove_file(hba->dev, >clk_gating.enable_attr); cancel_work_sync(>clk_gating.ungate_work); cancel_delayed_work_sync(>clk_gating.gate_work); + destroy_workqueue(hba->clk_gating.clk_gating_workq); } /* Must be called with host lock acquired */ diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 76c31d5..2e6bdc0 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -361,6 +361,7 @@ struct ufs_clk_gating { struct device_attribute enable_attr; bool is_enabled; int active_reqs; + struct workqueue_struct *clk_gating_workq; }; struct ufs_saved_pwr_info { -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH v2 02/10] scsi: ufs-qcom: Enable UFSHCD_QUIRK_BROKEN_PWR_MODE_CHANGE quirk
Enable UFSHCD_QUIRK_BROKEN_PWR_MODE_CHANGE quirk to avoid failures in seen on some UFS devices. Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufs-qcom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 4563d2e..d9edef8 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -1105,7 +1105,7 @@ static void ufs_qcom_advertise_quirks(struct ufs_hba *hba) if (host->hw_ver.major >= 0x2) { hba->quirks |= UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION; - + hba->quirks |= UFSHCD_QUIRK_BROKEN_PWR_MODE_CHANGE; if (!ufs_qcom_cap_qunipro(host)) /* Legacy UniPro mode still need following quirks */ hba->quirks |= (UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH v2 01/10] scsi: ufs: Allowing power mode change
From: Yaniv Gardi <yga...@codeaurora.org> Due to M-PHY issues, moving from HS to any other mode or gear or even Hibern8 may cause some un-predicted behavior of the device. This patch adds provides a quirk to address that. Signed-off-by: Yaniv Gardi <yga...@codeaurora.org> Signed-off-by: Subhash Jadavani <subha...@codeaurora.org> Signed-off-by: Can Guo <c...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 8 +++- drivers/scsi/ufs/ufshcd.h | 7 +++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 5bc9dc1..f3083fe 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4162,9 +4162,15 @@ static int ufshcd_link_startup(struct ufs_hba *hba) goto out; } while (ret && retries--); - if (ret) + if (ret) { /* failed to get the link up... retire */ goto out; + } + + if (hba->quirks & UFSHCD_QUIRK_BROKEN_PWR_MODE_CHANGE) { + ufshcd_dme_set(hba, UIC_ARG_MIB(TX_LCC_ENABLE), 0); + ufshcd_dme_set(hba, UIC_ARG_MIB(TX_LCC_ENABLE), 1); + } if (link_startup_again) { link_startup_again = false; diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index cbe46f6..bb4ecfb 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -591,6 +591,13 @@ struct ufs_hba { */ #define UFSHCD_QUIRK_PRDT_BYTE_GRAN UFS_BIT(7) + /* +* Needs to be enabled if moving from HS to any other gear/mode or even +* hibern8 causes unpredicted behavior of device. +* If this quirk is enabled, standard UFS driver will disable/enable +* TX_LCC. +*/ + #define UFSHCD_QUIRK_BROKEN_PWR_MODE_CHANGE UFS_BIT(8) unsigned int quirks;/* Deviations from standard UFSHCI spec. */ /* Device deviations from standard UFS device spec. */ -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH v2 06/10] scsi: ufs: add reference counting for scsi block requests
From: Subhash Jadavani <subha...@codeaurora.org> Currently we call the scsi_block_requests()/scsi_unblock_requests() whenever we want to block/unblock scsi requests but as there is no reference counting, nesting of these calls could leave us in undesired state sometime. Consider following call flow sequence: 1. func1() calls scsi_block_requests() but calls func2() before calling scsi_unblock_requests() 2. func2() calls scsi_block_requests() 3. func2() calls scsi_unblock_requests() 4. func1() calls scsi_unblock_requests() As there is no reference counting, we will have scsi requests unblocked after #3 instead of it to be unblocked only after #4. Though we may not have failures seen with this, we might run into some failures in future. Better solution would be to fix this by adding reference counting. Signed-off-by: Subhash Jadavani <subha...@codeaurora.org> Signed-off-by: Can Guo <c...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 28 drivers/scsi/ufs/ufshcd.h | 2 ++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index dfeb194..c35a076 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -264,6 +264,18 @@ static inline void ufshcd_disable_irq(struct ufs_hba *hba) } } +static void ufshcd_scsi_unblock_requests(struct ufs_hba *hba) +{ + if (atomic_dec_and_test(>scsi_block_reqs_cnt)) + scsi_unblock_requests(hba->host); +} + +static void ufshcd_scsi_block_requests(struct ufs_hba *hba) +{ + if (atomic_inc_return(>scsi_block_reqs_cnt) == 1) + scsi_block_requests(hba->host); +} + /* replace non-printable or non-ASCII characters with spaces */ static inline void ufshcd_remove_non_printable(char *val) { @@ -1077,12 +1089,12 @@ static int ufshcd_clock_scaling_prepare(struct ufs_hba *hba) * make sure that there are no outstanding requests when * clock scaling is in progress */ - scsi_block_requests(hba->host); + ufshcd_scsi_block_requests(hba); down_write(>clk_scaling_lock); if (ufshcd_wait_for_doorbell_clr(hba, DOORBELL_CLR_TOUT_US)) { ret = -EBUSY; up_write(>clk_scaling_lock); - scsi_unblock_requests(hba->host); + ufshcd_scsi_unblock_requests(hba); } return ret; @@ -1091,7 +1103,7 @@ static int ufshcd_clock_scaling_prepare(struct ufs_hba *hba) static void ufshcd_clock_scaling_unprepare(struct ufs_hba *hba) { up_write(>clk_scaling_lock); - scsi_unblock_requests(hba->host); + ufshcd_scsi_unblock_requests(hba); } /** @@ -1411,7 +1423,7 @@ static void ufshcd_ungate_work(struct work_struct *work) hba->clk_gating.is_suspended = false; } unblock_reqs: - scsi_unblock_requests(hba->host); + ufshcd_scsi_unblock_requests(hba); } /** @@ -1467,7 +1479,7 @@ int ufshcd_hold(struct ufs_hba *hba, bool async) * work and to enable clocks. */ case CLKS_OFF: - scsi_block_requests(hba->host); + ufshcd_scsi_block_requests(hba); hba->clk_gating.state = REQ_CLKS_ON; trace_ufshcd_clk_gating(dev_name(hba->dev), hba->clk_gating.state); @@ -5192,7 +5204,7 @@ static void ufshcd_err_handler(struct work_struct *work) out: spin_unlock_irqrestore(hba->host->host_lock, flags); - scsi_unblock_requests(hba->host); + ufshcd_scsi_unblock_requests(hba); ufshcd_release(hba); pm_runtime_put_sync(hba->dev); } @@ -5294,7 +5306,7 @@ static void ufshcd_check_errors(struct ufs_hba *hba) /* handle fatal errors only when link is functional */ if (hba->ufshcd_state == UFSHCD_STATE_OPERATIONAL) { /* block commands from scsi mid-layer */ - scsi_block_requests(hba->host); + ufshcd_scsi_block_requests(hba); hba->ufshcd_state = UFSHCD_STATE_EH_SCHEDULED; @@ -8017,7 +8029,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) /* Hold auto suspend until async scan completes */ pm_runtime_get_sync(dev); - + atomic_set(>scsi_block_reqs_cnt, 0); /* * We are assuming that device wasn't put in sleep/power-down * state exclusively during the boot stage before kernel. diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 0417c42..76c31d5 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -498,6 +498,7 @@ struct ufs_stats { * @urgent_bkops_lvl: keeps track of urgent bkops level for device * @is_urgent_bkops_lvl_checked:
[PATCH v2 04/10] scsi: ufs: fix exception event handling
From: Maya Erez <me...@codeaurora.org> The device can set the exception event bit in one of the response UPIU, for example to notify the need for urgent BKOPs operation. In such a case the host driver calls ufshcd_exception_event_handler to handle this notification. When trying to check the exception event status (for finding the cause for the exception event), the device may be busy with additional SCSI commands handling and may not respond within the 100ms timeout. To prevent that, we need to block SCSI commands during handling of exception events and allow retransmissions of the query requests, in case of timeout. Signed-off-by: Subhash Jadavani <subha...@codeaurora.org> Signed-off-by: Maya Erez <me...@codeaurora.org> Signed-off-by: Can Guo <c...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 6dabce8..838ba8f0 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4967,6 +4967,7 @@ static void ufshcd_exception_event_handler(struct work_struct *work) hba = container_of(work, struct ufs_hba, eeh_work); pm_runtime_get_sync(hba->dev); + scsi_block_requests(hba->host); err = ufshcd_get_ee_status(hba, ); if (err) { dev_err(hba->dev, "%s: failed to get exception status %d\n", @@ -4980,6 +4981,7 @@ static void ufshcd_exception_event_handler(struct work_struct *work) ufshcd_bkops_exception_event_handler(hba); out: + scsi_unblock_requests(hba->host); pm_runtime_put_sync(hba->dev); return; } -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH v2 08/10] scsi: ufs: make sure all interrupts are processed
From: Venkat Gopalakrishnan <venk...@codeaurora.org> As multiple requests are submitted to the ufs host controller in parallel there could be instances where the command completion interrupt arrives later for a request that is already processed earlier as the corresponding doorbell was cleared when handling the previous interrupt. Read the interrupt status in a loop after processing the received interrupt to catch such interrupts and handle it. Signed-off-by: Venkat Gopalakrishnan <venk...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 27 +++ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index c35a076..09b7a3f 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -5383,19 +5383,30 @@ static irqreturn_t ufshcd_intr(int irq, void *__hba) u32 intr_status, enabled_intr_status; irqreturn_t retval = IRQ_NONE; struct ufs_hba *hba = __hba; + int retries = hba->nutrs; spin_lock(hba->host->host_lock); intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); - enabled_intr_status = - intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE); - if (intr_status) - ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS); + /* +* There could be max of hba->nutrs reqs in flight and in worst case +* if the reqs get finished 1 by 1 after the interrupt status is +* read, make sure we handle them by checking the interrupt status +* again in a loop until we process all of the reqs before returning. +*/ + do { + enabled_intr_status = + intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE); + if (intr_status) + ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS); + if (enabled_intr_status) { + ufshcd_sl_intr(hba, enabled_intr_status); + retval = IRQ_HANDLED; + } + + intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); + } while (intr_status && --retries); - if (enabled_intr_status) { - ufshcd_sl_intr(hba, enabled_intr_status); - retval = IRQ_HANDLED; - } spin_unlock(hba->host->host_lock); return retval; } -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH v2 09/10] scsi: ufs: fix irq return code
From: Venkat Gopalakrishnan <venk...@codeaurora.org> Return IRQ_HANDLED only if the irq is really handled, this will help in catching spurious interrupts that go unhandled. Signed-off-by: Venkat Gopalakrishnan <venk...@codeaurora.org> Signed-off-by: Can Guo <c...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 137 ++ 1 file changed, 101 insertions(+), 36 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 09b7a3f..557d538 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -211,7 +211,7 @@ enum { END_FIX }; -static void ufshcd_tmc_handler(struct ufs_hba *hba); +static irqreturn_t ufshcd_tmc_handler(struct ufs_hba *hba); static void ufshcd_async_scan(void *data, async_cookie_t cookie); static int ufshcd_reset_and_restore(struct ufs_hba *hba); static int ufshcd_eh_host_reset_handler(struct scsi_cmnd *cmd); @@ -4609,19 +4609,29 @@ static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index, u8 *resp) * ufshcd_uic_cmd_compl - handle completion of uic command * @hba: per adapter instance * @intr_status: interrupt status generated by the controller + * + * Returns + * IRQ_HANDLED - If interrupt is valid + * IRQ_NONE- If invalid interrupt */ -static void ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status) +static irqreturn_t ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status) { + irqreturn_t retval = IRQ_NONE; + if ((intr_status & UIC_COMMAND_COMPL) && hba->active_uic_cmd) { hba->active_uic_cmd->argument2 |= ufshcd_get_uic_cmd_result(hba); hba->active_uic_cmd->argument3 = ufshcd_get_dme_attr_val(hba); complete(>active_uic_cmd->done); + retval = IRQ_HANDLED; } - if ((intr_status & UFSHCD_UIC_PWR_MASK) && hba->uic_async_done) + if ((intr_status & UFSHCD_UIC_PWR_MASK) && hba->uic_async_done) { complete(hba->uic_async_done); + retval = IRQ_HANDLED; + } + return retval; } /** @@ -4675,8 +4685,12 @@ static void __ufshcd_transfer_req_compl(struct ufs_hba *hba, /** * ufshcd_transfer_req_compl - handle SCSI and query command completion * @hba: per adapter instance + * + * Returns + * IRQ_HANDLED - If interrupt is valid + * IRQ_NONE- If invalid interrupt */ -static void ufshcd_transfer_req_compl(struct ufs_hba *hba) +static irqreturn_t ufshcd_transfer_req_compl(struct ufs_hba *hba) { unsigned long completed_reqs; u32 tr_doorbell; @@ -4694,7 +4708,12 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba) tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); completed_reqs = tr_doorbell ^ hba->outstanding_reqs; - __ufshcd_transfer_req_compl(hba, completed_reqs); + if (completed_reqs) { + __ufshcd_transfer_req_compl(hba, completed_reqs); + return IRQ_HANDLED; + } else { + return IRQ_NONE; + } } /** @@ -5220,16 +5239,21 @@ static void ufshcd_update_uic_reg_hist(struct ufs_uic_err_reg_hist *reg_hist, /** * ufshcd_update_uic_error - check and set fatal UIC error flags. * @hba: per-adapter instance + * + * Returns + * IRQ_HANDLED - If interrupt is valid + * IRQ_NONE- If invalid interrupt */ -static void ufshcd_update_uic_error(struct ufs_hba *hba) +static irqreturn_t ufshcd_update_uic_error(struct ufs_hba *hba) { u32 reg; + irqreturn_t retval = IRQ_NONE; /* PHY layer lane error */ reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER); /* Ignore LINERESET indication, as this is not an error */ if ((reg & UIC_PHY_ADAPTER_LAYER_ERROR) && - (reg & UIC_PHY_ADAPTER_LAYER_LANE_ERR_MASK)) { + (reg & UIC_PHY_ADAPTER_LAYER_ERROR_CODE_MASK)) { /* * To know whether this error is fatal or not, DB timeout * must be checked but this error is handled separately. @@ -5240,57 +5264,73 @@ static void ufshcd_update_uic_error(struct ufs_hba *hba) /* PA_INIT_ERROR is fatal and needs UIC reset */ reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_DATA_LINK_LAYER); - if (reg) + if ((reg & UIC_DATA_LINK_LAYER_ERROR) && + (reg & UIC_DATA_LINK_LAYER_ERROR_CODE_MASK)) { ufshcd_update_uic_reg_hist(>ufs_stats.dl_err, reg); - - if (reg & UIC_DATA_LINK_LAYER_ERROR_PA_INIT) - hba->uic_error |= UFSHCD_UIC_DL_PA_INIT_ERROR; - else if (hba->dev_quirks & - UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS) { - if (reg &a
[PATCH v2 05/10] scsi: ufshcd: fix possible unclocked register access
From: Subhash Jadavani <subha...@codeaurora.org> vendor specific setup_clocks ops may depend on clocks managed by ufshcd driver so if the vendor specific setup_clocks callback is called when the required clocks are turned off, it results into unclocked register access. This change make sure that required clocks are enabled before vendor specific setup_clocks callback is called. Signed-off-by: Subhash Jadavani <subha...@codeaurora.org> Signed-off-by: Venkat Gopalakrishnan <venk...@codeaurora.org> Signed-off-by: Can Guo <c...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 26 -- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 838ba8f0..dfeb194 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -6780,9 +6780,16 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on, if (list_empty(head)) goto out; - ret = ufshcd_vops_setup_clocks(hba, on, PRE_CHANGE); - if (ret) - return ret; + /* +* vendor specific setup_clocks ops may depend on clocks managed by +* this standard driver hence call the vendor specific setup_clocks +* before disabling the clocks managed here. +*/ + if (!on) { + ret = ufshcd_vops_setup_clocks(hba, on, PRE_CHANGE); + if (ret) + return ret; + } list_for_each_entry(clki, head, list) { if (!IS_ERR_OR_NULL(clki->clk)) { @@ -6806,9 +6813,16 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on, } } - ret = ufshcd_vops_setup_clocks(hba, on, POST_CHANGE); - if (ret) - return ret; + /* +* vendor specific setup_clocks ops may depend on clocks managed by +* this standard driver hence call the vendor specific setup_clocks +* after enabling the clocks managed here. +*/ + if (on) { + ret = ufshcd_vops_setup_clocks(hba, on, POST_CHANGE); + if (ret) + return ret; + } out: if (ret) { -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH v2 07/10] scsi: ufs-qcom: remove broken hci version quirk
From: Subhash Jadavani <subha...@codeaurora.org> UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION is only applicable for QCOM UFS host controller version 2.x.y and this has been fixed from version 3.x.y onwards, hence this change removes this quirk for version 3.x.y onwards. Signed-off-by: Subhash Jadavani <subha...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufs-qcom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index d9edef8..27be327 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -1103,7 +1103,7 @@ static void ufs_qcom_advertise_quirks(struct ufs_hba *hba) hba->quirks |= UFSHCD_QUIRK_BROKEN_LCC; } - if (host->hw_ver.major >= 0x2) { + if (host->hw_ver.major == 0x2) { hba->quirks |= UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION; hba->quirks |= UFSHCD_QUIRK_BROKEN_PWR_MODE_CHANGE; if (!ufs_qcom_cap_qunipro(host)) -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH v2 03/10] scsi: ufs: Add LCC quirk for host and device
LCC (Line Control Command) is being used for communication between UFS host and UFS device. But some hosts might have the issue with issuing the LCC commands to UFS device and in this case LCC could be explicitly disabled. But there could be a need where we don't want to disable the LCC on both host & device; hence this change splits the quirk in 2 parts one for host and one for device. Signed-off-by: Subhash Jadavani <subha...@codeaurora.org> Signed-off-by: Venkat Gopalakrishnan <venk...@codeaurora.org> Signed-off-by: Can Guo <c...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 16 drivers/scsi/ufs/ufshcd.h | 12 2 files changed, 28 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index f3083fe..6dabce8 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4116,6 +4116,11 @@ static int ufshcd_disable_tx_lcc(struct ufs_hba *hba, bool peer) return err; } +static inline int ufshcd_disable_host_tx_lcc(struct ufs_hba *hba) +{ + return ufshcd_disable_tx_lcc(hba, false); +} + static inline int ufshcd_disable_device_tx_lcc(struct ufs_hba *hba) { return ufshcd_disable_tx_lcc(hba, true); @@ -4172,6 +4177,17 @@ static int ufshcd_link_startup(struct ufs_hba *hba) ufshcd_dme_set(hba, UIC_ARG_MIB(TX_LCC_ENABLE), 1); } + if (hba->quirks & UFSHCD_BROKEN_LCC_PROCESSING_ON_HOST) { + ret = ufshcd_disable_device_tx_lcc(hba); + if (ret) + goto out; + } + + if (hba->quirks & UFSHCD_BROKEN_LCC_PROCESSING_ON_DEVICE) { + ret = ufshcd_disable_host_tx_lcc(hba); + if (ret) + goto out; + } if (link_startup_again) { link_startup_again = false; retries = DME_LINKSTARTUP_RETRIES; diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index bb4ecfb..0417c42 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -598,6 +598,18 @@ struct ufs_hba { * TX_LCC. */ #define UFSHCD_QUIRK_BROKEN_PWR_MODE_CHANGE UFS_BIT(8) + + /* +* If UFS device is having issue in processing LCC (Line Control +* Command) coming from UFS host controller then enable this quirk. +* When this quirk is enabled, host controller driver should disable +* the LCC transmission on UFS host controller (by clearing +* TX_LCC_ENABLE attribute of host to 0). +*/ + #define UFSHCD_BROKEN_LCC_PROCESSING_ON_DEVICE 0x100 + + #define UFSHCD_BROKEN_LCC_PROCESSING_ON_HOST0x200 + unsigned int quirks;/* Deviations from standard UFSHCI spec. */ /* Device deviations from standard UFS device spec. */ -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH v2 00/10] ufshcd optimizations and fixes
This patch set has a bunch of optimizations for UFS HCI. Changes since v1: Addressed the review comments Asutosh Das (2): scsi: ufs-qcom: Enable UFSHCD_QUIRK_BROKEN_PWR_MODE_CHANGE quirk scsi: ufs: Add LCC quirk for host and device Maya Erez (1): scsi: ufs: fix exception event handling Subhash Jadavani (3): scsi: ufshcd: fix possible unclocked register access scsi: ufs: add reference counting for scsi block requests scsi: ufs-qcom: remove broken hci version quirk Venkat Gopalakrishnan (2): scsi: ufs: make sure all interrupts are processed scsi: ufs: fix irq return code Vijay Viswanath (1): scsi: ufs: Add clock ungating to a separate workqueue Yaniv Gardi (1): scsi: ufs: Allowing power mode change drivers/scsi/ufs/ufs-qcom.c | 4 +- drivers/scsi/ufs/ufshcd.c | 245 ++-- drivers/scsi/ufs/ufshcd.h | 22 3 files changed, 214 insertions(+), 57 deletions(-) -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
Re: [PATCH 9/9] scsi: ufs: Add clock ungating to a separate workqueue
On 2/24/2018 5:27 AM, Miguel Ojeda wrote: On Wed, Feb 21, 2018 at 5:56 AM, Asutosh Das <asuto...@codeaurora.org> wrote: From: Vijay Viswanath <vvisw...@codeaurora.org> UFS driver can receive a request during memory reclaim by kswapd. So when ufs driver puts the ungate work in queue, and if there are no idle workers, kthreadd is invoked to create a new kworker. Since kswapd task holds a mutex which kthreadd also needs, this can cause a deadlock situation. So ungate work must be done in a separate work queue with WQ__RECLAIM flag enabled.Such a workqueue will have WQ_MEM_RECLAIM here. Also space after dot. a rescue thread which will be called when the above deadlock condition is possible. Signed-off-by: Vijay Viswanath <vvisw...@codeaurora.org> Signed-off-by: Can Guo <c...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 10 +- drivers/scsi/ufs/ufshcd.h | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 6541e1d..bb3382a 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -1503,7 +1503,8 @@ int ufshcd_hold(struct ufs_hba *hba, bool async) hba->clk_gating.state = REQ_CLKS_ON; trace_ufshcd_clk_gating(dev_name(hba->dev), hba->clk_gating.state); - schedule_work(>clk_gating.ungate_work); + queue_work(hba->clk_gating.clk_gating_workq, + >clk_gating.ungate_work); /* * fall through to check if we should wait for this * work to be done or not. @@ -1689,6 +1690,8 @@ static ssize_t ufshcd_clkgate_enable_store(struct device *dev, static void ufshcd_init_clk_gating(struct ufs_hba *hba) { + char wq_name[sizeof("ufs_clk_gating_00")]; + if (!ufshcd_is_clkgating_allowed(hba)) return; @@ -1696,6 +1699,10 @@ static void ufshcd_init_clk_gating(struct ufs_hba *hba) INIT_DELAYED_WORK(>clk_gating.gate_work, ufshcd_gate_work); INIT_WORK(>clk_gating.ungate_work, ufshcd_ungate_work); + snprintf(wq_name, ARRAY_SIZE(wq_name), "ufs_clk_gating_%d", +hba->host->host_no); + hba->clk_gating.clk_gating_workq = create_singlethread_workqueue(wq_name); Shouldn't this be alloc_ordered_workqueue() with WQ_MEM_RECLAIM then? (create_singlethread_workqueue() and friends are deprecated). Cheers, Miguel + hba->clk_gating.is_enabled = true; hba->clk_gating.delay_attr.show = ufshcd_clkgate_delay_show; @@ -1723,6 +1730,7 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba) device_remove_file(hba->dev, >clk_gating.enable_attr); cancel_work_sync(>clk_gating.ungate_work); cancel_delayed_work_sync(>clk_gating.gate_work); + destroy_workqueue(hba->clk_gating.clk_gating_workq); } /* Must be called with host lock acquired */ diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 4385741..570c33e 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -361,6 +361,7 @@ struct ufs_clk_gating { struct device_attribute enable_attr; bool is_enabled; int active_reqs; + struct workqueue_struct *clk_gating_workq; }; struct ufs_saved_pwr_info { -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project. Hi Miguel Thanks for the review. I'll check this and put up the changes in v2. -asd -- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Re: [PATCH 1/9] scsi: ufs: Allowing power mode change
On 2/23/2018 10:40 AM, Kyuho Choi wrote: Hi Asutosh, I've simple question in below. On 2/21/18, Asutosh Das <asuto...@codeaurora.org> wrote: From: Yaniv Gardi <yga...@codeaurora.org> Due to M-PHY issues, moving from HS to any other mode or gear or even Hibern8 causes some un-predicted behavior of the device. This patch fixes this issues. Signed-off-by: Yaniv Gardi <yga...@codeaurora.org> Signed-off-by: Subhash Jadavani <subha...@codeaurora.org> Signed-off-by: Can Guo <c...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 6 +- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 011c336..d74d529 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4167,9 +4167,13 @@ static int ufshcd_link_startup(struct ufs_hba *hba) goto out; } while (ret && retries--); - if (ret) + if (ret) { /* failed to get the link up... retire */ goto out; + } else { + ufshcd_dme_set(hba, UIC_ARG_MIB(TX_LCC_ENABLE), 0); + ufshcd_dme_set(hba, UIC_ARG_MIB(TX_LCC_ENABLE), 1); + } Every ufs host has same issue and affected?. if (link_startup_again) { link_startup_again = false; -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project. Hi Choi Thanks for the review. No - I can't say if every host has the same issue. However, I get your point. It could be done with a quirk. I'll fix this in v2 after collating all the comments from the rest of the patches. -asd -- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Re: [PATCH 5/9] scsi: ufs: add reference counting for scsi block requests
On 2/21/2018 6:48 PM, Stanislav Nijnikov wrote: -Original Message- From: linux-scsi-ow...@vger.kernel.org [mailto:linux-scsi- ow...@vger.kernel.org] On Behalf Of Asutosh Das Sent: Wednesday, February 21, 2018 6:57 AM To: subha...@codeaurora.org; c...@codeaurora.org; vivek.gau...@codeaurora.org; rna...@codeaurora.org; vinholika...@gmail.com; j...@linux.vnet.ibm.com; martin.peter...@oracle.com Cc: linux-scsi@vger.kernel.org; Asutosh Das <asuto...@codeaurora.org>; open list <linux-ker...@vger.kernel.org> Subject: [PATCH 5/9] scsi: ufs: add reference counting for scsi block requests From: Subhash Jadavani <subha...@codeaurora.org> Currently we call the scsi_block_requests()/scsi_unblock_requests() whenever we want to block/unblock scsi requests but as there is no reference counting, nesting of these calls could leave us in undesired state sometime. Consider following call flow sequence: 1. func1() calls scsi_block_requests() but calls func2() before calling scsi_unblock_requests() 2. func2() calls scsi_block_requests() 3. func2() calls scsi_unblock_requests() 4. func1() calls scsi_unblock_requests() As there is no reference counting, we will have scsi requests unblocked after #3 instead of it to be unblocked only after #4. Though we may not have failures seen with this, we might run into some failures in future. Better solution would be to fix this by adding reference counting. Signed-off-by: Subhash Jadavani <subha...@codeaurora.org> Signed-off-by: Can Guo <c...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 44 +--- drivers/scsi/ufs/ufshcd.h | 5 + 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 7a4df95..987b81b 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -264,6 +264,36 @@ static inline void ufshcd_disable_irq(struct ufs_hba *hba) } } +void ufshcd_scsi_unblock_requests(struct ufs_hba *hba) { + unsigned long flags; + bool unblock = false; + + spin_lock_irqsave(hba->host->host_lock, flags); + hba->scsi_block_reqs_cnt--; + unblock = !hba->scsi_block_reqs_cnt; + spin_unlock_irqrestore(hba->host->host_lock, flags); + if (unblock) + scsi_unblock_requests(hba->host); +} +EXPORT_SYMBOL(ufshcd_scsi_unblock_requests); + +static inline void __ufshcd_scsi_block_requests(struct ufs_hba *hba) { + if (!hba->scsi_block_reqs_cnt++) + scsi_block_requests(hba->host); +} + +void ufshcd_scsi_block_requests(struct ufs_hba *hba) { + unsigned long flags; + + spin_lock_irqsave(hba->host->host_lock, flags); + __ufshcd_scsi_block_requests(hba); + spin_unlock_irqrestore(hba->host->host_lock, flags); } +EXPORT_SYMBOL(ufshcd_scsi_block_requests); + /* replace non-printable or non-ASCII characters with spaces */ static inline void ufshcd_remove_non_printable(char *val) { @@ -1079,12 +1109,12 @@ static int ufshcd_clock_scaling_prepare(struct ufs_hba *hba) * make sure that there are no outstanding requests when * clock scaling is in progress */ - scsi_block_requests(hba->host); + ufshcd_scsi_block_requests(hba); down_write(>clk_scaling_lock); if (ufshcd_wait_for_doorbell_clr(hba, DOORBELL_CLR_TOUT_US)) { ret = -EBUSY; up_write(>clk_scaling_lock); - scsi_unblock_requests(hba->host); + ufshcd_scsi_unblock_requests(hba); } return ret; @@ -1093,7 +1123,7 @@ static int ufshcd_clock_scaling_prepare(struct ufs_hba *hba) static void ufshcd_clock_scaling_unprepare(struct ufs_hba *hba) { up_write(>clk_scaling_lock); - scsi_unblock_requests(hba->host); + ufshcd_scsi_unblock_requests(hba); } /** @@ -1413,7 +1443,7 @@ static void ufshcd_ungate_work(struct work_struct *work) hba->clk_gating.is_suspended = false; } unblock_reqs: - scsi_unblock_requests(hba->host); + ufshcd_scsi_unblock_requests(hba); } /** @@ -1469,7 +1499,7 @@ int ufshcd_hold(struct ufs_hba *hba, bool async) * work and to enable clocks. */ case CLKS_OFF: - scsi_block_requests(hba->host); + __ufshcd_scsi_block_requests(hba); hba->clk_gating.state = REQ_CLKS_ON; trace_ufshcd_clk_gating(dev_name(hba->dev), hba->clk_gating.state); @@ -5197,7 +5227,7 @@ static void ufshcd_err_handler(struct work_struct *work) out: spin_unlock_irqrestore(hba->host->host_lock, flags); - scsi_unblock_requests(hba->host); + ufshcd_scsi_unblock_requests(hba); ufshcd_release(hb
[PATCH 1/9] scsi: ufs: Allowing power mode change
From: Yaniv Gardi <yga...@codeaurora.org> Due to M-PHY issues, moving from HS to any other mode or gear or even Hibern8 causes some un-predicted behavior of the device. This patch fixes this issues. Signed-off-by: Yaniv Gardi <yga...@codeaurora.org> Signed-off-by: Subhash Jadavani <subha...@codeaurora.org> Signed-off-by: Can Guo <c...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 6 +- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 011c336..d74d529 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4167,9 +4167,13 @@ static int ufshcd_link_startup(struct ufs_hba *hba) goto out; } while (ret && retries--); - if (ret) + if (ret) { /* failed to get the link up... retire */ goto out; + } else { + ufshcd_dme_set(hba, UIC_ARG_MIB(TX_LCC_ENABLE), 0); + ufshcd_dme_set(hba, UIC_ARG_MIB(TX_LCC_ENABLE), 1); + } if (link_startup_again) { link_startup_again = false; -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH 8/9] scsi: ufs: fix irq return code
From: Venkat Gopalakrishnan <venk...@codeaurora.org> Return IRQ_HANDLED only if the irq is really handled, this will help in catching spurious interrupts that go unhandled. Signed-off-by: Venkat Gopalakrishnan <venk...@codeaurora.org> Signed-off-by: Can Guo <c...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 137 ++ 1 file changed, 101 insertions(+), 36 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 4d4c7d6..6541e1d 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -211,7 +211,7 @@ enum { END_FIX }; -static void ufshcd_tmc_handler(struct ufs_hba *hba); +static irqreturn_t ufshcd_tmc_handler(struct ufs_hba *hba); static void ufshcd_async_scan(void *data, async_cookie_t cookie); static int ufshcd_reset_and_restore(struct ufs_hba *hba); static int ufshcd_eh_host_reset_handler(struct scsi_cmnd *cmd); @@ -4630,19 +4630,29 @@ static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index, u8 *resp) * ufshcd_uic_cmd_compl - handle completion of uic command * @hba: per adapter instance * @intr_status: interrupt status generated by the controller + * + * Returns + * IRQ_HANDLED - If interrupt is valid + * IRQ_NONE- If invalid interrupt */ -static void ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status) +static irqreturn_t ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status) { + irqreturn_t retval = IRQ_NONE; + if ((intr_status & UIC_COMMAND_COMPL) && hba->active_uic_cmd) { hba->active_uic_cmd->argument2 |= ufshcd_get_uic_cmd_result(hba); hba->active_uic_cmd->argument3 = ufshcd_get_dme_attr_val(hba); complete(>active_uic_cmd->done); + retval = IRQ_HANDLED; } - if ((intr_status & UFSHCD_UIC_PWR_MASK) && hba->uic_async_done) + if ((intr_status & UFSHCD_UIC_PWR_MASK) && hba->uic_async_done) { complete(hba->uic_async_done); + retval = IRQ_HANDLED; + } + return retval; } /** @@ -4698,8 +4708,12 @@ static void __ufshcd_transfer_req_compl(struct ufs_hba *hba, /** * ufshcd_transfer_req_compl - handle SCSI and query command completion * @hba: per adapter instance + * + * Returns + * IRQ_HANDLED - If interrupt is valid + * IRQ_NONE- If invalid interrupt */ -static void ufshcd_transfer_req_compl(struct ufs_hba *hba) +static irqreturn_t ufshcd_transfer_req_compl(struct ufs_hba *hba) { unsigned long completed_reqs; u32 tr_doorbell; @@ -4717,7 +4731,12 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba) tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); completed_reqs = tr_doorbell ^ hba->outstanding_reqs; - __ufshcd_transfer_req_compl(hba, completed_reqs); + if (completed_reqs) { + __ufshcd_transfer_req_compl(hba, completed_reqs); + return IRQ_HANDLED; + } else { + return IRQ_NONE; + } } /** @@ -5243,16 +5262,21 @@ static void ufshcd_update_uic_reg_hist(struct ufs_uic_err_reg_hist *reg_hist, /** * ufshcd_update_uic_error - check and set fatal UIC error flags. * @hba: per-adapter instance + * + * Returns + * IRQ_HANDLED - If interrupt is valid + * IRQ_NONE- If invalid interrupt */ -static void ufshcd_update_uic_error(struct ufs_hba *hba) +static irqreturn_t ufshcd_update_uic_error(struct ufs_hba *hba) { u32 reg; + irqreturn_t retval = IRQ_NONE; /* PHY layer lane error */ reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER); /* Ignore LINERESET indication, as this is not an error */ if ((reg & UIC_PHY_ADAPTER_LAYER_ERROR) && - (reg & UIC_PHY_ADAPTER_LAYER_LANE_ERR_MASK)) { + (reg & UIC_PHY_ADAPTER_LAYER_ERROR_CODE_MASK)) { /* * To know whether this error is fatal or not, DB timeout * must be checked but this error is handled separately. @@ -5263,57 +5287,73 @@ static void ufshcd_update_uic_error(struct ufs_hba *hba) /* PA_INIT_ERROR is fatal and needs UIC reset */ reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_DATA_LINK_LAYER); - if (reg) + if ((reg & UIC_DATA_LINK_LAYER_ERROR) && + (reg & UIC_DATA_LINK_LAYER_ERROR_CODE_MASK)) { ufshcd_update_uic_reg_hist(>ufs_stats.dl_err, reg); - - if (reg & UIC_DATA_LINK_LAYER_ERROR_PA_INIT) - hba->uic_error |= UFSHCD_UIC_DL_PA_INIT_ERROR; - else if (hba->dev_quirks & - UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS) { - if (reg &a
[PATCH 3/9] scsi: ufs: fix exception event handling
From: Maya Erez <me...@codeaurora.org> The device can set the exception event bit in one of the response UPIU, for example to notify the need for urgent BKOPs operation. In such a case the host driver calls ufshcd_exception_event_handler to handle this notification. When trying to check the exception event status (for finding the cause for the exception event), the device may be busy with additional SCSI commands handling and may not respond within the 100ms timeout. To prevent that, we need to block SCSI commands during handling of exception events and allow retransmissions of the query requests, in case of timeout. Signed-off-by: Subhash Jadavani <subha...@codeaurora.org> Signed-off-by: Maya Erez <me...@codeaurora.org> Signed-off-by: Can Guo <c...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index cc7eb1e..8d3f8ce 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4972,6 +4972,7 @@ static void ufshcd_exception_event_handler(struct work_struct *work) hba = container_of(work, struct ufs_hba, eeh_work); pm_runtime_get_sync(hba->dev); + scsi_block_requests(hba->host); err = ufshcd_get_ee_status(hba, ); if (err) { dev_err(hba->dev, "%s: failed to get exception status %d\n", @@ -4985,6 +4986,7 @@ static void ufshcd_exception_event_handler(struct work_struct *work) ufshcd_bkops_exception_event_handler(hba); out: + scsi_unblock_requests(hba->host); pm_runtime_put_sync(hba->dev); return; } -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH 2/9] scsi: ufs: Add LCC quirk for host and device
From: Subhash Jadavani <subha...@codeaurora.org> LCC (Line Control Command) is being used for communication between UFS host and UFS device. But some hosts might have the issue with issuing the LCC commands to UFS device and in this case LCC could be explicitly disabled. But there could be a need where we don't want to disable the LCC on both host & device; hence this change splits the quirk in 2 parts one for host and one for device. Signed-off-by: Subhash Jadavani <subha...@codeaurora.org> Signed-off-by: Venkat Gopalakrishnan <venk...@codeaurora.org> Signed-off-by: Can Guo <c...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 16 drivers/scsi/ufs/ufshcd.h | 11 +++ 2 files changed, 27 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index d74d529..cc7eb1e 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4121,6 +4121,11 @@ static int ufshcd_disable_tx_lcc(struct ufs_hba *hba, bool peer) return err; } +static inline int ufshcd_disable_host_tx_lcc(struct ufs_hba *hba) +{ + return ufshcd_disable_tx_lcc(hba, false); +} + static inline int ufshcd_disable_device_tx_lcc(struct ufs_hba *hba) { return ufshcd_disable_tx_lcc(hba, true); @@ -4175,6 +4180,17 @@ static int ufshcd_link_startup(struct ufs_hba *hba) ufshcd_dme_set(hba, UIC_ARG_MIB(TX_LCC_ENABLE), 1); } + if (hba->quirks & UFSHCD_BROKEN_LCC_PROCESSING_ON_HOST) { + ret = ufshcd_disable_device_tx_lcc(hba); + if (ret) + goto out; + } + + if (hba->quirks & UFSHCD_BROKEN_LCC_PROCESSING_ON_DEVICE) { + ret = ufshcd_disable_host_tx_lcc(hba); + if (ret) + goto out; + } if (link_startup_again) { link_startup_again = false; retries = DME_LINKSTARTUP_RETRIES; diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 1332e54..7a2dad3 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -591,6 +591,17 @@ struct ufs_hba { */ #define UFSHCD_QUIRK_PRDT_BYTE_GRAN 0x80 + /* +* If UFS device is having issue in processing LCC (Line Control +* Command) coming from UFS host controller then enable this quirk. +* When this quirk is enabled, host controller driver should disable +* the LCC transmission on UFS host controller (by clearing +* TX_LCC_ENABLE attribute of host to 0). +*/ + #define UFSHCD_BROKEN_LCC_PROCESSING_ON_DEVICE 0x100 + + #define UFSHCD_BROKEN_LCC_PROCESSING_ON_HOST0x200 + unsigned int quirks;/* Deviations from standard UFSHCI spec. */ /* Device deviations from standard UFS device spec. */ -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH 5/9] scsi: ufs: add reference counting for scsi block requests
From: Subhash Jadavani <subha...@codeaurora.org> Currently we call the scsi_block_requests()/scsi_unblock_requests() whenever we want to block/unblock scsi requests but as there is no reference counting, nesting of these calls could leave us in undesired state sometime. Consider following call flow sequence: 1. func1() calls scsi_block_requests() but calls func2() before calling scsi_unblock_requests() 2. func2() calls scsi_block_requests() 3. func2() calls scsi_unblock_requests() 4. func1() calls scsi_unblock_requests() As there is no reference counting, we will have scsi requests unblocked after #3 instead of it to be unblocked only after #4. Though we may not have failures seen with this, we might run into some failures in future. Better solution would be to fix this by adding reference counting. Signed-off-by: Subhash Jadavani <subha...@codeaurora.org> Signed-off-by: Can Guo <c...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 44 +--- drivers/scsi/ufs/ufshcd.h | 5 + 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 7a4df95..987b81b 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -264,6 +264,36 @@ static inline void ufshcd_disable_irq(struct ufs_hba *hba) } } +void ufshcd_scsi_unblock_requests(struct ufs_hba *hba) +{ + unsigned long flags; + bool unblock = false; + + spin_lock_irqsave(hba->host->host_lock, flags); + hba->scsi_block_reqs_cnt--; + unblock = !hba->scsi_block_reqs_cnt; + spin_unlock_irqrestore(hba->host->host_lock, flags); + if (unblock) + scsi_unblock_requests(hba->host); +} +EXPORT_SYMBOL(ufshcd_scsi_unblock_requests); + +static inline void __ufshcd_scsi_block_requests(struct ufs_hba *hba) +{ + if (!hba->scsi_block_reqs_cnt++) + scsi_block_requests(hba->host); +} + +void ufshcd_scsi_block_requests(struct ufs_hba *hba) +{ + unsigned long flags; + + spin_lock_irqsave(hba->host->host_lock, flags); + __ufshcd_scsi_block_requests(hba); + spin_unlock_irqrestore(hba->host->host_lock, flags); +} +EXPORT_SYMBOL(ufshcd_scsi_block_requests); + /* replace non-printable or non-ASCII characters with spaces */ static inline void ufshcd_remove_non_printable(char *val) { @@ -1079,12 +1109,12 @@ static int ufshcd_clock_scaling_prepare(struct ufs_hba *hba) * make sure that there are no outstanding requests when * clock scaling is in progress */ - scsi_block_requests(hba->host); + ufshcd_scsi_block_requests(hba); down_write(>clk_scaling_lock); if (ufshcd_wait_for_doorbell_clr(hba, DOORBELL_CLR_TOUT_US)) { ret = -EBUSY; up_write(>clk_scaling_lock); - scsi_unblock_requests(hba->host); + ufshcd_scsi_unblock_requests(hba); } return ret; @@ -1093,7 +1123,7 @@ static int ufshcd_clock_scaling_prepare(struct ufs_hba *hba) static void ufshcd_clock_scaling_unprepare(struct ufs_hba *hba) { up_write(>clk_scaling_lock); - scsi_unblock_requests(hba->host); + ufshcd_scsi_unblock_requests(hba); } /** @@ -1413,7 +1443,7 @@ static void ufshcd_ungate_work(struct work_struct *work) hba->clk_gating.is_suspended = false; } unblock_reqs: - scsi_unblock_requests(hba->host); + ufshcd_scsi_unblock_requests(hba); } /** @@ -1469,7 +1499,7 @@ int ufshcd_hold(struct ufs_hba *hba, bool async) * work and to enable clocks. */ case CLKS_OFF: - scsi_block_requests(hba->host); + __ufshcd_scsi_block_requests(hba); hba->clk_gating.state = REQ_CLKS_ON; trace_ufshcd_clk_gating(dev_name(hba->dev), hba->clk_gating.state); @@ -5197,7 +5227,7 @@ static void ufshcd_err_handler(struct work_struct *work) out: spin_unlock_irqrestore(hba->host->host_lock, flags); - scsi_unblock_requests(hba->host); + ufshcd_scsi_unblock_requests(hba); ufshcd_release(hba); pm_runtime_put_sync(hba->dev); } @@ -5299,7 +5329,7 @@ static void ufshcd_check_errors(struct ufs_hba *hba) /* handle fatal errors only when link is functional */ if (hba->ufshcd_state == UFSHCD_STATE_OPERATIONAL) { /* block commands from scsi mid-layer */ - scsi_block_requests(hba->host); + __ufshcd_scsi_block_requests(hba); hba->ufshcd_state = UFSHCD_STATE_EH_SCHEDULED; diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 7a2d
[PATCH 7/9] scsi: ufs: make sure all interrupts are processed
From: Venkat Gopalakrishnan <venk...@codeaurora.org> As multiple requests are submitted to the ufs host controller in parallel there could be instances where the command completion interrupt arrives later for a request that is already processed earlier as the corresponding doorbell was cleared when handling the previous interrupt. Read the interrupt status in a loop after processing the received interrupt to catch such interrupts and handle it. Signed-off-by: Venkat Gopalakrishnan <venk...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 27 +++ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 987b81b..4d4c7d6 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -5406,19 +5406,30 @@ static irqreturn_t ufshcd_intr(int irq, void *__hba) u32 intr_status, enabled_intr_status; irqreturn_t retval = IRQ_NONE; struct ufs_hba *hba = __hba; + int retries = hba->nutrs; spin_lock(hba->host->host_lock); intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); - enabled_intr_status = - intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE); - if (intr_status) - ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS); + /* +* There could be max of hba->nutrs reqs in flight and in worst case +* if the reqs get finished 1 by 1 after the interrupt status is +* read, make sure we handle them by checking the interrupt status +* again in a loop until we process all of the reqs before returning. +*/ + do { + enabled_intr_status = + intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE); + if (intr_status) + ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS); + if (enabled_intr_status) { + ufshcd_sl_intr(hba, enabled_intr_status); + retval = IRQ_HANDLED; + } + + intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); + } while (intr_status && --retries); - if (enabled_intr_status) { - ufshcd_sl_intr(hba, enabled_intr_status); - retval = IRQ_HANDLED; - } spin_unlock(hba->host->host_lock); return retval; } -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH 4/9] scsi: ufshcd: fix possible unclocked register access
From: Subhash Jadavani <subha...@codeaurora.org> vendor specific setup_clocks ops may depend on clocks managed by ufshcd driver so if the vendor specific setup_clocks callback is called when the required clocks are turned off, it results into unclocked register access. This change make sure that required clocks are enabled before vendor specific setup_clocks callback is called. Signed-off-by: Subhash Jadavani <subha...@codeaurora.org> Signed-off-by: Venkat Gopalakrishnan <venk...@codeaurora.org> Signed-off-by: Can Guo <c...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 26 -- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 8d3f8ce..7a4df95 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -6782,9 +6782,16 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on, if (list_empty(head)) goto out; - ret = ufshcd_vops_setup_clocks(hba, on, PRE_CHANGE); - if (ret) - return ret; + /* +* vendor specific setup_clocks ops may depend on clocks managed by +* this standard driver hence call the vendor specific setup_clocks +* before disabling the clocks managed here. +*/ + if (!on) { + ret = ufshcd_vops_setup_clocks(hba, on, PRE_CHANGE); + if (ret) + return ret; + } list_for_each_entry(clki, head, list) { if (!IS_ERR_OR_NULL(clki->clk)) { @@ -6808,9 +6815,16 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on, } } - ret = ufshcd_vops_setup_clocks(hba, on, POST_CHANGE); - if (ret) - return ret; + /* +* vendor specific setup_clocks ops may depend on clocks managed by +* this standard driver hence call the vendor specific setup_clocks +* after enabling the clocks managed here. +*/ + if (on) { + ret = ufshcd_vops_setup_clocks(hba, on, POST_CHANGE); + if (ret) + return ret; + } out: if (ret) { -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH 9/9] scsi: ufs: Add clock ungating to a separate workqueue
From: Vijay Viswanath <vvisw...@codeaurora.org> UFS driver can receive a request during memory reclaim by kswapd. So when ufs driver puts the ungate work in queue, and if there are no idle workers, kthreadd is invoked to create a new kworker. Since kswapd task holds a mutex which kthreadd also needs, this can cause a deadlock situation. So ungate work must be done in a separate work queue with WQ__RECLAIM flag enabled.Such a workqueue will have a rescue thread which will be called when the above deadlock condition is possible. Signed-off-by: Vijay Viswanath <vvisw...@codeaurora.org> Signed-off-by: Can Guo <c...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 10 +- drivers/scsi/ufs/ufshcd.h | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 6541e1d..bb3382a 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -1503,7 +1503,8 @@ int ufshcd_hold(struct ufs_hba *hba, bool async) hba->clk_gating.state = REQ_CLKS_ON; trace_ufshcd_clk_gating(dev_name(hba->dev), hba->clk_gating.state); - schedule_work(>clk_gating.ungate_work); + queue_work(hba->clk_gating.clk_gating_workq, + >clk_gating.ungate_work); /* * fall through to check if we should wait for this * work to be done or not. @@ -1689,6 +1690,8 @@ static ssize_t ufshcd_clkgate_enable_store(struct device *dev, static void ufshcd_init_clk_gating(struct ufs_hba *hba) { + char wq_name[sizeof("ufs_clk_gating_00")]; + if (!ufshcd_is_clkgating_allowed(hba)) return; @@ -1696,6 +1699,10 @@ static void ufshcd_init_clk_gating(struct ufs_hba *hba) INIT_DELAYED_WORK(>clk_gating.gate_work, ufshcd_gate_work); INIT_WORK(>clk_gating.ungate_work, ufshcd_ungate_work); + snprintf(wq_name, ARRAY_SIZE(wq_name), "ufs_clk_gating_%d", +hba->host->host_no); + hba->clk_gating.clk_gating_workq = create_singlethread_workqueue(wq_name); + hba->clk_gating.is_enabled = true; hba->clk_gating.delay_attr.show = ufshcd_clkgate_delay_show; @@ -1723,6 +1730,7 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba) device_remove_file(hba->dev, >clk_gating.enable_attr); cancel_work_sync(>clk_gating.ungate_work); cancel_delayed_work_sync(>clk_gating.gate_work); + destroy_workqueue(hba->clk_gating.clk_gating_workq); } /* Must be called with host lock acquired */ diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 4385741..570c33e 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -361,6 +361,7 @@ struct ufs_clk_gating { struct device_attribute enable_attr; bool is_enabled; int active_reqs; + struct workqueue_struct *clk_gating_workq; }; struct ufs_saved_pwr_info { -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH 6/9] scsi: ufs-qcom: remove broken hci version quirk
From: Subhash Jadavani <subha...@codeaurora.org> UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION is only applicable for QCOM UFS host controller version 2.x.y and this has been fixed from version 3.x.y onwards, hence this change removes this quirk for version 3.x.y onwards. Signed-off-by: Subhash Jadavani <subha...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufs-qcom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 2b38db2..221820a 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -1098,7 +1098,7 @@ static void ufs_qcom_advertise_quirks(struct ufs_hba *hba) hba->quirks |= UFSHCD_QUIRK_BROKEN_LCC; } - if (host->hw_ver.major >= 0x2) { + if (host->hw_ver.major == 0x2) { hba->quirks |= UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION; if (!ufs_qcom_cap_qunipro(host)) -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH 0/9] ufshcd optimizations and fixes
This patch set has a bunch of optimizations for UFS HCI. Maya Erez (1): scsi: ufs: fix exception event handling Subhash Jadavani (4): scsi: ufs: Add LCC quirk for host and device scsi: ufshcd: fix possible unclocked register access scsi: ufs: add reference counting for scsi block requests scsi: ufs-qcom: remove broken hci version quirk Venkat Gopalakrishnan (2): scsi: ufs: make sure all interrupts are processed scsi: ufs: fix irq return code Vijay Viswanath (1): scsi: ufs: Add clock ungating to a separate workqueue Yaniv Gardi (1): scsi: ufs: Allowing power mode change drivers/scsi/ufs/ufs-qcom.c | 2 +- drivers/scsi/ufs/ufshcd.c | 258 ++-- drivers/scsi/ufs/ufshcd.h | 17 +++ 3 files changed, 222 insertions(+), 55 deletions(-) -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
Re: [PATCH 1/1] scsi: ufs: make sure all interrupts are processed
On 2/2/2018 8:53 AM, Asutosh Das (asd) wrote: On 1/31/2018 1:09 PM, Avri Altman wrote: Hi, Can you elaborate how this can even happen? Isn't the interrupt aggregation capability should attend for those cases? Thanks, Avri -Original Message- From: linux-scsi-ow...@vger.kernel.org [mailto:linux-scsi- ow...@vger.kernel.org] On Behalf Of Asutosh Das Sent: Tuesday, January 30, 2018 6:54 AM To: subha...@codeaurora.org; c...@codeaurora.org; vivek.gau...@codeaurora.org; rna...@codeaurora.org; vinholika...@gmail.com; j...@linux.vnet.ibm.com; martin.peter...@oracle.com Cc: linux-scsi@vger.kernel.org; Venkat Gopalakrishnan <venk...@codeaurora.org>; Asutosh Das <asuto...@codeaurora.org>; open list <linux-ker...@vger.kernel.org> Subject: [PATCH 1/1] scsi: ufs: make sure all interrupts are processed From: Venkat Gopalakrishnan <venk...@codeaurora.org> As multiple requests are submitted to the ufs host controller in parallel there could be instances where the command completion interrupt arrives later for a request that is already processed earlier as the corresponding doorbell was cleared when handling the previous interrupt. Read the interrupt status in a loop after processing the received interrupt to catch such interrupts and handle it. Signed-off-by: Venkat Gopalakrishnan <venk...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 27 +++ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 8af2af3..58d81de 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -5357,19 +5357,30 @@ static irqreturn_t ufshcd_intr(int irq, void *__hba) u32 intr_status, enabled_intr_status; irqreturn_t retval = IRQ_NONE; struct ufs_hba *hba = __hba; + int retries = hba->nutrs; spin_lock(hba->host->host_lock); intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); - enabled_intr_status = - intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE); - if (intr_status) - ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS); + /* + * There could be max of hba->nutrs reqs in flight and in worst case + * if the reqs get finished 1 by 1 after the interrupt status is + * read, make sure we handle them by checking the interrupt status + * again in a loop until we process all of the reqs before returning. + */ + do { + enabled_intr_status = + intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE); + if (intr_status) + ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS); + if (enabled_intr_status) { + ufshcd_sl_intr(hba, enabled_intr_status); + retval = IRQ_HANDLED; + } + + intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); + } while (intr_status && --retries); - if (enabled_intr_status) { - ufshcd_sl_intr(hba, enabled_intr_status); - retval = IRQ_HANDLED; - } spin_unlock(hba->host->host_lock); return retval; } -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project. Hi yes - interrupt aggregation makes sense here. But there were some performance concerns with it; well, I don't have the data to back that up now though. However, I can code it up and check it. Will post it in some time. -asd Hi Avri, I went through the UFS HCI - v2.1 spec. Specifically, in sec 7.2.3 it explicitly mentions that the software should determine if new TRs were completed since the interrupt status was last read/cleared. This step is independent of aggregation. So I think the above implementation makes sense. Please let me know if I understood your concern correctly. -asd -- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Re: [PATCH 1/1] scsi: ufs: make sure all interrupts are processed
On 1/31/2018 1:09 PM, Avri Altman wrote: Hi, Can you elaborate how this can even happen? Isn't the interrupt aggregation capability should attend for those cases? Thanks, Avri -Original Message- From: linux-scsi-ow...@vger.kernel.org [mailto:linux-scsi- ow...@vger.kernel.org] On Behalf Of Asutosh Das Sent: Tuesday, January 30, 2018 6:54 AM To: subha...@codeaurora.org; c...@codeaurora.org; vivek.gau...@codeaurora.org; rna...@codeaurora.org; vinholika...@gmail.com; j...@linux.vnet.ibm.com; martin.peter...@oracle.com Cc: linux-scsi@vger.kernel.org; Venkat Gopalakrishnan <venk...@codeaurora.org>; Asutosh Das <asuto...@codeaurora.org>; open list <linux-ker...@vger.kernel.org> Subject: [PATCH 1/1] scsi: ufs: make sure all interrupts are processed From: Venkat Gopalakrishnan <venk...@codeaurora.org> As multiple requests are submitted to the ufs host controller in parallel there could be instances where the command completion interrupt arrives later for a request that is already processed earlier as the corresponding doorbell was cleared when handling the previous interrupt. Read the interrupt status in a loop after processing the received interrupt to catch such interrupts and handle it. Signed-off-by: Venkat Gopalakrishnan <venk...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 27 +++ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 8af2af3..58d81de 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -5357,19 +5357,30 @@ static irqreturn_t ufshcd_intr(int irq, void *__hba) u32 intr_status, enabled_intr_status; irqreturn_t retval = IRQ_NONE; struct ufs_hba *hba = __hba; + int retries = hba->nutrs; spin_lock(hba->host->host_lock); intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); - enabled_intr_status = - intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE); - if (intr_status) - ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS); + /* +* There could be max of hba->nutrs reqs in flight and in worst case +* if the reqs get finished 1 by 1 after the interrupt status is +* read, make sure we handle them by checking the interrupt status +* again in a loop until we process all of the reqs before returning. +*/ + do { + enabled_intr_status = + intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE); + if (intr_status) + ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS); + if (enabled_intr_status) { + ufshcd_sl_intr(hba, enabled_intr_status); + retval = IRQ_HANDLED; + } + + intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); + } while (intr_status && --retries); - if (enabled_intr_status) { - ufshcd_sl_intr(hba, enabled_intr_status); - retval = IRQ_HANDLED; - } spin_unlock(hba->host->host_lock); return retval; } -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project. Hi yes - interrupt aggregation makes sense here. But there were some performance concerns with it; well, I don't have the data to back that up now though. However, I can code it up and check it. Will post it in some time. -asd -- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Re: [PATCH 1/1] scsi: ufs-qcom: remove broken hci version quirk
On 1/30/2018 11:33 AM, Vivek Gautam wrote: Hi Asutosh, On 1/30/2018 10:11 AM, Asutosh Das wrote: From: Subhash Jadavani <subha...@codeaurora.org> UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION is only applicable for QCOM UFS host controller version 2.x.y and this has been fixed from version 3.x.y onwards, hence this change removes this quirk for version 3.x.y onwards. Signed-off-by: Subhash Jadavani <subha...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- This patch and all other ufs patches that you have posted recently, do they all fall under one 'ufs-qcom fixes' patch series for fixes that we would want to do? If it is so, then please club them together in a series, so that it's easy for reviewers and maintainers to review, and keep track of all the patches that can get-in after the reviews. If they belong to two or more separate patch-series then please create such patch series. It's difficult to read through a lot of [PATCH 1/1] ... patch. Sure Vivek - Makes sense. Will resend it. Regards Vivek drivers/scsi/ufs/ufs-qcom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 2b38db2..221820a 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -1098,7 +1098,7 @@ static void ufs_qcom_advertise_quirks(struct ufs_hba *hba) hba->quirks |= UFSHCD_QUIRK_BROKEN_LCC; } - if (host->hw_ver.major >= 0x2) { + if (host->hw_ver.major == 0x2) { hba->quirks |= UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION; if (!ufs_qcom_cap_qunipro(host)) -- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH 1/1] scsi: ufs: make sure all interrupts are processed
From: Venkat Gopalakrishnan <venk...@codeaurora.org> As multiple requests are submitted to the ufs host controller in parallel there could be instances where the command completion interrupt arrives later for a request that is already processed earlier as the corresponding doorbell was cleared when handling the previous interrupt. Read the interrupt status in a loop after processing the received interrupt to catch such interrupts and handle it. Signed-off-by: Venkat Gopalakrishnan <venk...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 27 +++ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 8af2af3..58d81de 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -5357,19 +5357,30 @@ static irqreturn_t ufshcd_intr(int irq, void *__hba) u32 intr_status, enabled_intr_status; irqreturn_t retval = IRQ_NONE; struct ufs_hba *hba = __hba; + int retries = hba->nutrs; spin_lock(hba->host->host_lock); intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); - enabled_intr_status = - intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE); - if (intr_status) - ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS); + /* +* There could be max of hba->nutrs reqs in flight and in worst case +* if the reqs get finished 1 by 1 after the interrupt status is +* read, make sure we handle them by checking the interrupt status +* again in a loop until we process all of the reqs before returning. +*/ + do { + enabled_intr_status = + intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE); + if (intr_status) + ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS); + if (enabled_intr_status) { + ufshcd_sl_intr(hba, enabled_intr_status); + retval = IRQ_HANDLED; + } + + intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); + } while (intr_status && --retries); - if (enabled_intr_status) { - ufshcd_sl_intr(hba, enabled_intr_status); - retval = IRQ_HANDLED; - } spin_unlock(hba->host->host_lock); return retval; } -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH 1/1] scsi: ufs-qcom: remove broken hci version quirk
From: Subhash Jadavani <subha...@codeaurora.org> UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION is only applicable for QCOM UFS host controller version 2.x.y and this has been fixed from version 3.x.y onwards, hence this change removes this quirk for version 3.x.y onwards. Signed-off-by: Subhash Jadavani <subha...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufs-qcom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 2b38db2..221820a 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -1098,7 +1098,7 @@ static void ufs_qcom_advertise_quirks(struct ufs_hba *hba) hba->quirks |= UFSHCD_QUIRK_BROKEN_LCC; } - if (host->hw_ver.major >= 0x2) { + if (host->hw_ver.major == 0x2) { hba->quirks |= UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION; if (!ufs_qcom_cap_qunipro(host)) -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH 1/1] scsi: ufs: add reference counting for scsi block requests
From: Subhash Jadavani <subha...@codeaurora.org> Currently we call the scsi_block_requests()/scsi_unblock_requests() whenever we want to block/unblock scsi requests but as there is no reference counting, nesting of these calls could leave us in undesired state sometime. Consider following call flow sequence: 1. func1() calls scsi_block_requests() but calls func2() before calling scsi_unblock_requests() 2. func2() calls scsi_block_requests() 3. func2() calls scsi_unblock_requests() 4. func1() calls scsi_unblock_requests() As there is no reference counting, we will have scsi requests unblocked after #3 instead of it to be unblocked only after #4. Though we may not have failures seen with this, we might run into some failures in future. Better solution would be to fix this by adding reference counting. Signed-off-by: Subhash Jadavani <subha...@codeaurora.org> Signed-off-by: Can Guo <c...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 44 +--- drivers/scsi/ufs/ufshcd.h | 5 + 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 8af2af3..a9ebc8d 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -264,6 +264,36 @@ static inline void ufshcd_disable_irq(struct ufs_hba *hba) } } +void ufshcd_scsi_unblock_requests(struct ufs_hba *hba) +{ + unsigned long flags; + bool unblock = false; + + spin_lock_irqsave(hba->host->host_lock, flags); + hba->scsi_block_reqs_cnt--; + unblock = !hba->scsi_block_reqs_cnt; + spin_unlock_irqrestore(hba->host->host_lock, flags); + if (unblock) + scsi_unblock_requests(hba->host); +} +EXPORT_SYMBOL(ufshcd_scsi_unblock_requests); + +static inline void __ufshcd_scsi_block_requests(struct ufs_hba *hba) +{ + if (!hba->scsi_block_reqs_cnt++) + scsi_block_requests(hba->host); +} + +void ufshcd_scsi_block_requests(struct ufs_hba *hba) +{ + unsigned long flags; + + spin_lock_irqsave(hba->host->host_lock, flags); + __ufshcd_scsi_block_requests(hba); + spin_unlock_irqrestore(hba->host->host_lock, flags); +} +EXPORT_SYMBOL(ufshcd_scsi_block_requests); + /* replace non-printable or non-ASCII characters with spaces */ static inline void ufshcd_remove_non_printable(char *val) { @@ -1079,12 +1109,12 @@ static int ufshcd_clock_scaling_prepare(struct ufs_hba *hba) * make sure that there are no outstanding requests when * clock scaling is in progress */ - scsi_block_requests(hba->host); + ufshcd_scsi_block_requests(hba); down_write(>clk_scaling_lock); if (ufshcd_wait_for_doorbell_clr(hba, DOORBELL_CLR_TOUT_US)) { ret = -EBUSY; up_write(>clk_scaling_lock); - scsi_unblock_requests(hba->host); + ufshcd_scsi_unblock_requests(hba); } return ret; @@ -1093,7 +1123,7 @@ static int ufshcd_clock_scaling_prepare(struct ufs_hba *hba) static void ufshcd_clock_scaling_unprepare(struct ufs_hba *hba) { up_write(>clk_scaling_lock); - scsi_unblock_requests(hba->host); + ufshcd_scsi_unblock_requests(hba); } /** @@ -1413,7 +1443,7 @@ static void ufshcd_ungate_work(struct work_struct *work) hba->clk_gating.is_suspended = false; } unblock_reqs: - scsi_unblock_requests(hba->host); + ufshcd_scsi_unblock_requests(hba); } /** @@ -1469,7 +1499,7 @@ int ufshcd_hold(struct ufs_hba *hba, bool async) * work and to enable clocks. */ case CLKS_OFF: - scsi_block_requests(hba->host); + __ufshcd_scsi_block_requests(hba); hba->clk_gating.state = REQ_CLKS_ON; trace_ufshcd_clk_gating(dev_name(hba->dev), hba->clk_gating.state); @@ -5178,7 +5208,7 @@ static void ufshcd_err_handler(struct work_struct *work) out: spin_unlock_irqrestore(hba->host->host_lock, flags); - scsi_unblock_requests(hba->host); + ufshcd_scsi_unblock_requests(hba); ufshcd_release(hba); pm_runtime_put_sync(hba->dev); } @@ -5280,7 +5310,7 @@ static void ufshcd_check_errors(struct ufs_hba *hba) /* handle fatal errors only when link is functional */ if (hba->ufshcd_state == UFSHCD_STATE_OPERATIONAL) { /* block commands from scsi mid-layer */ - scsi_block_requests(hba->host); + __ufshcd_scsi_block_requests(hba); hba->ufshcd_state = UFSHCD_STATE_EH_SCHEDULED; diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 1332
[PATCH 1/1] scsi: ufshcd: fix possible unclocked register access
From: Subhash Jadavani <subha...@codeaurora.org> vendor specific setup_clocks ops may depend on clocks managed by ufshcd driver so if the vendor specific setup_clocks callback is called when the required clocks are turned off, it results into unclocked register access. This change make sure that required clocks are enabled before vendor specific setup_clocks callback is called. Signed-off-by: Subhash Jadavani <subha...@codeaurora.org> Signed-off-by: Venkat Gopalakrishnan <venk...@codeaurora.org> Signed-off-by: Can Guo <c...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 26 -- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 8af2af3..c5ee686 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -6763,9 +6763,16 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on, if (list_empty(head)) goto out; - ret = ufshcd_vops_setup_clocks(hba, on, PRE_CHANGE); - if (ret) - return ret; + /* +* vendor specific setup_clocks ops may depend on clocks managed by +* this standard driver hence call the vendor specific setup_clocks +* before disabling the clocks managed here. +*/ + if (!on) { + ret = ufshcd_vops_setup_clocks(hba, on, PRE_CHANGE); + if (ret) + return ret; + } list_for_each_entry(clki, head, list) { if (!IS_ERR_OR_NULL(clki->clk)) { @@ -6789,9 +6796,16 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on, } } - ret = ufshcd_vops_setup_clocks(hba, on, POST_CHANGE); - if (ret) - return ret; + /* +* vendor specific setup_clocks ops may depend on clocks managed by +* this standard driver hence call the vendor specific setup_clocks +* after enabling the clocks managed here. +*/ + if (on) { + ret = ufshcd_vops_setup_clocks(hba, on, POST_CHANGE); + if (ret) + return ret; + } out: if (ret) { -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH 1/1] scsi: ufs: fix exception event handling
From: Maya Erez <me...@codeaurora.org> The device can set the exception event bit in one of the response UPIU, for example to notify the need for urgent BKOPs operation. In such a case the host driver calls ufshcd_exception_event_handler to handle this notification. When trying to check the exception event status (for finding the cause for the exception event), the device may be busy with additional SCSI commands handling and may not respond within the 100ms timeout. To prevent that, we need to block SCSI commands during handling of exception events and allow retransmissions of the query requests, in case of timeout. Signed-off-by: Subhash Jadavani <subha...@codeaurora.org> Signed-off-by: Maya Erez <me...@codeaurora.org> Signed-off-by: Can Guo <c...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 8af2af3..2dd488f 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4955,6 +4955,7 @@ static void ufshcd_exception_event_handler(struct work_struct *work) hba = container_of(work, struct ufs_hba, eeh_work); pm_runtime_get_sync(hba->dev); + scsi_block_requests(hba->host); err = ufshcd_get_ee_status(hba, ); if (err) { dev_err(hba->dev, "%s: failed to get exception status %d\n", @@ -4968,6 +4969,7 @@ static void ufshcd_exception_event_handler(struct work_struct *work) ufshcd_bkops_exception_event_handler(hba); out: + scsi_unblock_requests(hba->host); pm_runtime_put_sync(hba->dev); return; } -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH 1/1] scsi: ufs: Add LCC quirk for host and device
From: Subhash Jadavani <subha...@codeaurora.org> LCC (Line Control Command) is being used for communication between UFS host and UFS device. But some hosts might have the issue with issuing the LCC commands to UFS device and in this case LCC could be explicitly disabled. But there could be a need where we don't want to disable the LCC on both host & device; hence this change splits the quirk in 2 parts one for host and one for device. Signed-off-by: Subhash Jadavani <subha...@codeaurora.org> Signed-off-by: Venkat Gopalakrishnan <venk...@codeaurora.org> Signed-off-by: Can Guo <c...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 16 drivers/scsi/ufs/ufshcd.h | 11 +++ 2 files changed, 27 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 8af2af3..d6fc88d 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4121,6 +4121,11 @@ static int ufshcd_disable_tx_lcc(struct ufs_hba *hba, bool peer) return err; } +static inline int ufshcd_disable_host_tx_lcc(struct ufs_hba *hba) +{ + return ufshcd_disable_tx_lcc(hba, false); +} + static inline int ufshcd_disable_device_tx_lcc(struct ufs_hba *hba) { return ufshcd_disable_tx_lcc(hba, true); @@ -4171,6 +4176,17 @@ static int ufshcd_link_startup(struct ufs_hba *hba) /* failed to get the link up... retire */ goto out; + if (hba->quirks & UFSHCD_BROKEN_LCC_PROCESSING_ON_HOST) { + ret = ufshcd_disable_device_tx_lcc(hba); + if (ret) + goto out; + } + + if (hba->quirks & UFSHCD_BROKEN_LCC_PROCESSING_ON_DEVICE) { + ret = ufshcd_disable_host_tx_lcc(hba); + if (ret) + goto out; + } if (link_startup_again) { link_startup_again = false; retries = DME_LINKSTARTUP_RETRIES; diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 1332e54..7a2dad3 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -591,6 +591,17 @@ struct ufs_hba { */ #define UFSHCD_QUIRK_PRDT_BYTE_GRAN 0x80 + /* +* If UFS device is having issue in processing LCC (Line Control +* Command) coming from UFS host controller then enable this quirk. +* When this quirk is enabled, host controller driver should disable +* the LCC transmission on UFS host controller (by clearing +* TX_LCC_ENABLE attribute of host to 0). +*/ + #define UFSHCD_BROKEN_LCC_PROCESSING_ON_DEVICE 0x100 + + #define UFSHCD_BROKEN_LCC_PROCESSING_ON_HOST0x200 + unsigned int quirks;/* Deviations from standard UFSHCI spec. */ /* Device deviations from standard UFS device spec. */ -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH 1/1] scsi: ufs: Enable quirk to ignore sending WRITE_SAME command
From: Sujit Reddy Thumma <sthu...@codeaurora.org> WRITE_SAME command is not supported by UFS. Enable a quirk for the upper level drivers to not send WRITE SAME command. Signed-off-by: Sujit Reddy Thumma <sthu...@codeaurora.org> Signed-off-by: Subhash Jadavani <subha...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 8af2af3..5a8dc3b 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4356,6 +4356,9 @@ static int ufshcd_slave_alloc(struct scsi_device *sdev) /* WRITE_SAME command is not supported*/ sdev->no_write_same = 1; + /* WRITE_SAME command is not supported*/ + sdev->no_write_same = 1; + ufshcd_set_queue_depth(sdev); ufshcd_get_lu_power_on_wp_status(hba, sdev); -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH 1/1] scsi: ufs: Allowing power mode change
From: Yaniv Gardi <yga...@codeaurora.org> Due to M-PHY issues, moving from HS to any other mode or gear or even Hibern8 causes some un-predicted behavior of the device. This patch fixes this issues. Signed-off-by: Yaniv Gardi <yga...@codeaurora.org> Signed-off-by: Subhash Jadavani <subha...@codeaurora.org> Signed-off-by: Can Guo <c...@codeaurora.org> Signed-off-by: Asutosh Das <asuto...@codeaurora.org> --- drivers/scsi/ufs/ufshcd.c | 6 +- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 011c336..d74d529 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4167,9 +4167,13 @@ static int ufshcd_link_startup(struct ufs_hba *hba) goto out; } while (ret && retries--); - if (ret) + if (ret) { /* failed to get the link up... retire */ goto out; + } else { + ufshcd_dme_set(hba, UIC_ARG_MIB(TX_LCC_ENABLE), 0); + ufshcd_dme_set(hba, UIC_ARG_MIB(TX_LCC_ENABLE), 1); + } if (link_startup_again) { link_startup_again = false; -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
Re: [PATCH 0/5] qcom-ufs: phy/hcd: Refactor phy initialization code
Vivek, On 8/11/2017 12:42 PM, Vivek Gautam wrote: On Fri, Aug 11, 2017 at 5:33 AM, Martin K. Petersen <martin.peter...@oracle.com> wrote: Vivek, Can you kindly review this patch series (for UFS controller changes) and consider giving your Ack so that Kishon can pull in the series through phy tree. SCSI piece looks OK. Thank you Martin for your review. Would still like Subhash to review the rest. Subhash is on vacation with limited access to emails. I will ask his team to take a look. Looks good to me. regards Vivek -- Martin K. Petersen Oracle Linux Engineering -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html -- Asutosh Das (asd) Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Re: [ufs]: [scsi]: BUG: spinlock recursion on CPU#4
On 6/1/2017 7:32 PM, Bart Van Assche wrote: On Thu, 2017-06-01 at 12:28 +0530, Asutosh Das (asd) wrote: Please can you check if this is actually a bug and my understanding is correct. Hello Asutosh, Spinlock recursion is always a bug. With what kernel version did you encounter this? Was it with a kernel from kernel.org, a distro kernel or an Android kernel? Have you already tried to reproduce this with kernel debugging enabled? Enabling CONFIG_DEBUG_SPINLOCK and CONFIG_PROVE_LOCKING should provide more detailed information. Bart. Hello Bart, Thanks. It's on 4.4 and its an Android kernel. No - I haven't tried it out yet. I could get some clues from the call-stack itself, like I explained before. I can try these configs though. While I do that, I'd like to know your thoughts on my analysis. Do you think with the current data, it makes sense? -- Asutosh. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
[ufs]: [scsi]: BUG: spinlock recursion on CPU#4
Hi All, Recently, I came across an issue with the below call stack. -000|arch_counter_get_cntvct(inline) -000|__delay() -001|__const_udelay(?) -002|msm_trigger_wdog_bite() -003|spin_dump(inline) -003|spin_bug(lock = ?, ?) -004|current_thread_info(inline) -004|debug_spin_lock_before(inline) -004|do_raw_spin_lock() -005|raw_spin_lock_irqsave(lock = ?) -006|blk_end_bidi_request(inline) -006|blk_end_request_all(rq = ?, error = 0) <-- this tries to acquire the lock acquired by blk_delay_work (-024) and spinbug recursion occurs -007|dm_end_request(clone = ?, error = 0) -008|dm_done(inline) -008|dm_softirq_done() -009|blk_done_softirq() -010|__read_once_size(inline) -010|static_key_count(inline) -010|static_key_false(inline) -010|trace_softirq_exit(inline) -010|__do_softirq() -011|do_softirq_own_stack(inline) -011|invoke_softirq(inline) <-- softirq is triggered because scsi_request_fn (-016) enabled interrupts on this cpu -011|irq_exit() -012|handle_IPI() -013|gic_handle_irq() -014|el1_irq(asm) -->|exception -015|__raw_spin_unlock_irq(inline) -015|raw_spin_unlock_irq(lock = ?) -016|scsi_request_fn() <-- Unlocks the queue using spin_unlock, doesn't restore the flags, thus enabling the interrupts -017|__blk_run_queue_uncond(inline) -017|__blk_run_queue(q = ?) -018|__elv_add_request() -019|blk_insert_cloned_request() <-- acquires the queue lock & saves the flags -020|dm_dispatch_clone_request(clone = ?, rq = ?) -021|map_request() -022|dm_request_fn() -023|__blk_run_queue_uncond(inline) -023|__blk_run_queue -024|spin_unlock_irq(inline) -024|blk_delay_work(?) <-- also acquires a queue lock, but this is a different queue, blk_end_request_all will reference this queue -025|__read_once_size(inline) -025|static_key_count(inline) -025|static_key_false(inline) -025|trace_workqueue_execute_end(inline) -025|process_one_work() -026|worker_thread() -027|kthread() -028|ret_from_fork(asm) ---|end of frame Please can you check if this is actually a bug and my understanding is correct. If so, I can put up a patch for the same. -- Asutosh Das (asd) Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project