@@ -1245,11 +1245,13 @@ static bool ufshcd_is_devfreq_scaling_required(struct ufs_hba *hba,
static u32 ufshcd_pending_cmds(struct ufs_hba *hba)
{
const struct scsi_device *sdev;
+ unsigned long flags;
u32 pending = 0;
- lockdep_assert_held(hba->host->host_lock);
+ spin_lock_irqsave(hba->host->host_lock, flags);
__shost_for_each_device(sdev, hba->host)
pending += sbitmap_weight(&sdev->budget_map);
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
return pending;
}
@@ -1271,7 +1273,6 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba,
ktime_t start;
ufshcd_hold(hba);
- spin_lock_irqsave(hba->host->host_lock, flags);
/*
* Wait for all the outstanding tasks/transfer requests.
* Verify by checking the doorbell registers are clear.
@@ -1283,7 +1284,10 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba,
goto out;
}
+ spin_lock_irqsave(&hba->reg_lock, flags);
tm_doorbell = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL);
+ spin_unlock_irqrestore(&hba->reg_lock, flags);
+
tr_pending = ufshcd_pending_cmds(hba);
if (!tm_doorbell && !tr_pending) {
timeout = false;
@@ -1292,7 +1296,6 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba,
break;
}
- spin_unlock_irqrestore(hba->host->host_lock, flags);
io_schedule_timeout(msecs_to_jiffies(20));
if (ktime_to_us(ktime_sub(ktime_get(), start)) >
wait_timeout_us) {
@@ -1304,7 +1307,6 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba,
*/
do_last_check = true;
}
- spin_lock_irqsave(hba->host->host_lock, flags);
} while (tm_doorbell || tr_pending);
if (timeout) {
@@ -1314,7 +1316,6 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba,
ret = -EBUSY;
}
out:
- spin_unlock_irqrestore(hba->host->host_lock, flags);
ufshcd_release(hba);
return ret;
}
@@ -6881,9 +6882,13 @@ static irqreturn_t ufshcd_tmc_handler(struct ufs_hba *hba)
irqreturn_t ret = IRQ_NONE;
int tag;
- spin_lock_irqsave(hba->host->host_lock, flags);
+ spin_lock_irqsave(&hba->reg_lock, flags);
pending = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL);
+ spin_unlock_irqrestore(&hba->reg_lock, flags);
+
issued = hba->outstanding_tasks & ~pending;
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
for_each_set_bit(tag, &issued, hba->nutmrs) {
struct request *req = hba->tmf_rqs[tag];
struct completion *c = req->end_io_data;
@@ -7065,11 +7070,13 @@ static int __ufshcd_issue_tm_cmd(struct ufs_hba *hba,
memcpy(hba->utmrdl_base_addr + task_tag, treq, sizeof(*treq));
ufshcd_vops_setup_task_mgmt(hba, task_tag, tm_function);
+ spin_unlock_irqrestore(host->host_lock, flags);
+
+ spin_lock_irqsave(&hba->reg_lock, flags);
/* send command to the controller */
__set_bit(task_tag, &hba->outstanding_tasks);
ufshcd_writel(hba, 1 << task_tag, REG_UTP_TASK_REQ_DOOR_BELL);
-
- spin_unlock_irqrestore(host->host_lock, flags);
+ spin_unlock_irqrestore(&hba->reg_lock, flags);
ufshcd_add_tm_upiu_trace(hba, task_tag, UFS_TM_SEND);
@@ -10469,6 +10476,9 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
sema_init(&hba->host_sem, 1);
+ /* Initialize host register read/write spinlock */
+ spin_lock_init(&hba->reg_lock);
+
/* Initialize UIC command mutex */
mutex_init(&hba->uic_cmd_mutex);
@@ -847,6 +847,7 @@ enum ufshcd_mcq_opr {
* @host: Scsi_Host instance of the driver
* @dev: device handle
* @ufs_device_wlun: WLUN that controls the entire UFS device.
+ * @reg_lock: protect register reads/writes
* @hwmon_device: device instance registered with the hwmon core.
* @curr_dev_pwr_mode: active UFS device power mode.
* @uic_link_state: active state of the link to the UFS device.
@@ -978,6 +979,8 @@ struct ufs_hba {
struct device *dev;
struct scsi_device *ufs_device_wlun;
+ spinlock_t reg_lock;
+
#ifdef CONFIG_SCSI_UFS_HWMON
struct device *hwmon_device;
#endif
Introduce a new host register read/write lock. Use it to protect access to the task management doorbell register: UTMRLDBR. This is not the UTRLDBR which is already protected by its own outstanding_lock. Signed-off-by: Avri Altman <avri.altman@wdc.com> --- drivers/ufs/core/ufshcd.c | 26 ++++++++++++++++++-------- include/ufs/ufshcd.h | 3 +++ 2 files changed, 21 insertions(+), 8 deletions(-)