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;
}