summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSayali Lokhande <sayalil@codeaurora.org>2017-07-06 12:03:10 +0530
committerSayali Lokhande <sayalil@codeaurora.org>2017-07-06 16:14:44 +0530
commit01d3b1fce14c8768892d45308679ad73f288773d (patch)
tree3ad8a0421cca965603be8f94edab9cd103af2fe8
parent0f6cf457b2bdcd335b710a5c5f27f234da076696 (diff)
scsi: ufs: Serialise ufs clock gating and ungating
On ufs based targets we observed several unclocked register access issues due to race conditions between clock gating and ungating. Sequence of events causing race looks like: [1]ungate_work_scheduled = (tv64 = 27288.795244106),REQ_CLKS_ON [2]gating_work_execute_end = (tv64 = 27288.795948169),REQ_CLKS_ON [3]last intr status = (tv64 = 27288.801560512) [4]ungate_work_execute_start=(tv64 = 27288.801725304),REQ_CLKS_ON [5]clk_rel ctx=XFR_REQ_COMPL)(tv64 = 27288.801950460),REQ_CLKS_OFF [6]gating_work_scheduled = (tv64 = 27288.812817231),REQ_CLKS_OFF [7]gating_work_execute_start=(tv64 = 27288.813704106),REQ_CLKS_OFF [8]clk_hold ctx = QUEUE_CMD) (tv64 = 27288.821010200) [9](WRITE)issue_time_stamp = (tv64 = 27288.821059366),gating in progress! [10]ungate_work_execute_end =(tv64 = 27288.821251866),CLKS_ON [11]Gladiator Error Detected = 27288.830788 Here clock gating work[6] is scheduled while ungating[4] is in progress thus causing unclocked register access when request is issued[9]. This change is to avoid such race condition by using single threaded workqueue for both gate and ungate work. Change-Id: I710ff0dbe59df0c1eb903b18555b6184cb298fa3 Signed-off-by: Sayali Lokhande <sayalil@codeaurora.org>
-rw-r--r--drivers/scsi/ufs/ufshcd.c14
-rw-r--r--drivers/scsi/ufs/ufshcd.h2
2 files changed, 9 insertions, 7 deletions
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index a2f2cc0c2c51..c23023f43d30 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -1483,7 +1483,7 @@ start:
hba->clk_gating.state = REQ_CLKS_ON;
trace_ufshcd_clk_gating(dev_name(hba->dev),
hba->clk_gating.state);
- queue_work(hba->clk_gating.ungating_workq,
+ queue_work(hba->clk_gating.clk_gating_workq,
&hba->clk_gating.ungate_work);
/*
* fall through to check if we should wait for this
@@ -1751,7 +1751,8 @@ static enum hrtimer_restart ufshcd_clkgate_hrtimer_handler(
struct ufs_hba *hba = container_of(timer, struct ufs_hba,
clk_gating.gate_hrtimer);
- schedule_work(&hba->clk_gating.gate_work);
+ queue_work(hba->clk_gating.clk_gating_workq,
+ &hba->clk_gating.gate_work);
return HRTIMER_NORESTART;
}
@@ -1759,7 +1760,7 @@ static enum hrtimer_restart ufshcd_clkgate_hrtimer_handler(
static void ufshcd_init_clk_gating(struct ufs_hba *hba)
{
struct ufs_clk_gating *gating = &hba->clk_gating;
- char wq_name[sizeof("ufs_clk_ungating_00")];
+ char wq_name[sizeof("ufs_clk_gating_00")];
hba->clk_gating.state = CLKS_ON;
@@ -1788,9 +1789,10 @@ static void ufshcd_init_clk_gating(struct ufs_hba *hba)
hrtimer_init(&gating->gate_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
gating->gate_hrtimer.function = ufshcd_clkgate_hrtimer_handler;
- snprintf(wq_name, ARRAY_SIZE(wq_name), "ufs_clk_ungating_%d",
+ snprintf(wq_name, ARRAY_SIZE(wq_name), "ufs_clk_gating_%d",
hba->host->host_no);
- hba->clk_gating.ungating_workq = create_singlethread_workqueue(wq_name);
+ hba->clk_gating.clk_gating_workq =
+ create_singlethread_workqueue(wq_name);
gating->is_enabled = true;
@@ -1854,7 +1856,7 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba)
device_remove_file(hba->dev, &hba->clk_gating.enable_attr);
ufshcd_cancel_gate_work(hba);
cancel_work_sync(&hba->clk_gating.ungate_work);
- destroy_workqueue(hba->clk_gating.ungating_workq);
+ destroy_workqueue(hba->clk_gating.clk_gating_workq);
}
static void ufshcd_set_auto_hibern8_timer(struct ufs_hba *hba, u32 delay)
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index d66205ff9f5d..da3ad78d3405 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -431,7 +431,7 @@ struct ufs_clk_gating {
struct device_attribute enable_attr;
bool is_enabled;
int active_reqs;
- struct workqueue_struct *ungating_workq;
+ struct workqueue_struct *clk_gating_workq;
};
/* Hibern8 state */