Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ab6db44c authored by Sayali Lokhande's avatar Sayali Lokhande
Browse files

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: default avatarSayali Lokhande <sayalil@codeaurora.org>
parent 814e5c69
Loading
Loading
Loading
Loading
+8 −6
Original line number Diff line number Diff line
@@ -1487,7 +1487,7 @@ 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);
		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
@@ -1755,7 +1755,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;
}
@@ -1763,7 +1764,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;

@@ -1792,9 +1793,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;

@@ -1858,7 +1860,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)
+1 −1
Original line number Diff line number Diff line
@@ -446,7 +446,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  */