msm_dcvs: Update interface to get updated timer on IDLE_EXIT

TZ DCVS algorithm is updated to give a new timer value on idle_exit
in order to catch smaller CPU bursts correctly. Also protect
against a timer race condition between IDLE_EXIT and CLOCK_FREQ_CHANGE
events.

Change-Id: I1ab414e9cd582c0eb8f86f0d87fa132d709da100
Signed-off-by: Eugene Seah <eseah@codeaurora.org>
diff --git a/arch/arm/mach-msm/msm_dcvs.c b/arch/arm/mach-msm/msm_dcvs.c
index c547d8c..0c158de 100644
--- a/arch/arm/mach-msm/msm_dcvs.c
+++ b/arch/arm/mach-msm/msm_dcvs.c
@@ -81,8 +81,9 @@
 	uint32_t group_id;
 	uint32_t freq_pending;
 	struct hrtimer timer;
-	int32_t core_slack_time_us;
 	int32_t timer_disabled;
+	/* track if kthread for change_freq is active */
+	int32_t change_freq_activated;
 };
 
 static int msm_dcvs_debug;
@@ -153,19 +154,12 @@
 	if (ret <= 0) {
 		__err("Core %s failed to set freq %u\n",
 				core->core_name, requested_freq);
-		/* Restart the timers at the current slack time */
-		core->timer_disabled = 0;
-		ret = hrtimer_start(&core->timer,
-			ktime_set(0, core->core_slack_time_us * 1000),
-				HRTIMER_MODE_REL_PINNED);
-			if (ret)
-				__err("Failed to register timer for core %s\n",
-						core->core_name);
-		return -EFAULT;
+		/* continue to call TZ to get updated slack timer */
+	} else {
+		prev_freq = core->actual_freq;
+		core->actual_freq = ret;
 	}
 
-	prev_freq = core->actual_freq;
-	core->actual_freq = ret;
 	time_end = ktime_to_ns(ktime_get());
 	if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
 		__info("Core %s Time end %llu Time start: %llu\n",
@@ -201,7 +195,6 @@
 		core->actual_freq, (uint32_t)time_end, &slack_us, &ret1);
 	if (!ret) {
 		/* Reset the slack timer */
-		core->core_slack_time_us = slack_us;
 		if (slack_us) {
 			core->timer_disabled = 0;
 			ret = hrtimer_start(&core->timer,
@@ -221,7 +214,7 @@
 			"change time %u us slack time %u us\n",
 			requested_freq, core->core_name,
 			core->actual_freq, prev_freq,
-			core->freq_change_us, core->core_slack_time_us);
+			core->freq_change_us, slack_us);
 
 	/**
 	 * By the time we are done with freq changes, we could be asked to
@@ -230,6 +223,7 @@
 	if (core->freq_pending)
 		goto repeat;
 
+	core->change_freq_activated = 0;
 	return ret;
 }
 
@@ -260,17 +254,16 @@
 }
 
 static int msm_dcvs_update_freq(struct dcvs_core *core,
-		enum msm_dcvs_scm_event event, uint32_t param0)
+		enum msm_dcvs_scm_event event, uint32_t param0,
+		uint32_t *ret1, int *freq_changed)
 {
 	int ret = 0;
 	unsigned long flags = 0;
 	uint32_t new_freq = 0;
-	uint32_t ret1 = 0;
 
 	spin_lock_irqsave(&core->cpu_lock, flags);
 	ret = msm_dcvs_scm_event(core->handle, event, param0,
-				core->actual_freq, &new_freq, &ret1);
-
+				core->actual_freq, &new_freq, ret1);
 	if (ret) {
 		__err("Error (%d) sending SCM event %d for core %s\n",
 				ret, event, core->core_name);
@@ -288,8 +281,15 @@
 		if (!core->task)
 			__err("Uninitialized task for core %s\n",
 					core->core_name);
-		else
+		else {
+			if (freq_changed)
+				*freq_changed = 1;
+			core->change_freq_activated = 1;
 			wake_up_process(core->task);
+		}
+	} else {
+		if (freq_changed)
+			*freq_changed = 0;
 	}
 freq_done:
 	spin_unlock_irqrestore(&core->cpu_lock, flags);
@@ -301,6 +301,8 @@
 {
 	int ret = 0;
 	struct dcvs_core *core = container_of(timer, struct dcvs_core, timer);
+	uint32_t ret1;
+	uint32_t ret2;
 
 	if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
 		__info("Slack timer fired for core %s\n", core->core_name);
@@ -309,7 +311,8 @@
 	 * Timer expired, notify TZ
 	 * Dont care about the third arg.
 	 */
-	ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0);
+	ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0,
+				   &ret1, &ret2);
 	if (ret)
 		__err("Timer expired for core %s but failed to notify.\n",
 				core->core_name);
@@ -551,6 +554,8 @@
 {
 	int ret = -EINVAL;
 	struct dcvs_core *core = NULL;
+	uint32_t ret1;
+	uint32_t ret2;
 
 	if (!drv || !drv->core_name)
 		return ret;
@@ -577,7 +582,8 @@
 	if (core->idle_driver) {
 		core->actual_freq = core->freq_driver->get_frequency(drv);
 		/* Notify TZ to start receiving idle info for the core */
-		ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_ENABLE_CORE, 1);
+		ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_ENABLE_CORE, 1,
+					   &ret1, &ret2);
 		core->idle_driver->enable(core->idle_driver,
 				MSM_DCVS_ENABLE_IDLE_PULSE);
 	}
@@ -592,6 +598,8 @@
 {
 	int ret = -EINVAL;
 	struct dcvs_core *core = NULL;
+	uint32_t ret1;
+	uint32_t ret2;
 
 	if (!drv || !drv->core_name)
 		return ret;
@@ -607,7 +615,8 @@
 		core->idle_driver->enable(core->idle_driver,
 				MSM_DCVS_DISABLE_IDLE_PULSE);
 		/* Notify TZ to stop receiving idle info for the core */
-		ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_ENABLE_CORE, 0);
+		ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_ENABLE_CORE, 0,
+					   &ret1, &ret2);
 		hrtimer_cancel(&core->timer);
 		core->idle_driver->enable(core->idle_driver,
 				MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
@@ -670,8 +679,9 @@
 {
 	int ret = 0;
 	struct dcvs_core *core = NULL;
-	int64_t timer_interval_us = 0;
+	uint32_t timer_interval_us = 0;
 	uint32_t r0, r1;
+	uint32_t freq_changed = 0;
 
 	if (handle >= CORE_HANDLE_OFFSET &&
 			(handle - CORE_HANDLE_OFFSET) < CORES_MAX)
@@ -695,18 +705,21 @@
 	case MSM_DCVS_IDLE_EXIT:
 		hrtimer_cancel(&core->timer);
 		ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT,
-				iowaited);
+				iowaited, &timer_interval_us, &freq_changed);
 		if (ret)
 			__err("Error (%d) sending idle exit for %s\n",
 					ret, core->core_name);
-		timer_interval_us = core->core_slack_time_us;
+		/* only start slack timer if change_freq won't */
+		if (freq_changed || core->change_freq_activated)
+			break;
 		if (timer_interval_us && !core->timer_disabled) {
 			ret = hrtimer_start(&core->timer,
 				ktime_set(0, timer_interval_us * 1000),
 				HRTIMER_MODE_REL_PINNED);
+
 			if (ret)
 				__err("Failed to register timer for core %s\n",
-						core->core_name);
+				      core->core_name);
 		}
 		break;
 	}