cpufreq: send new set of notification for transition failures
authorViresh Kumar <viresh.kumar@linaro.org>
Mon, 2 Dec 2013 05:34:13 +0000 (11:04 +0530)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 6 Jan 2014 00:43:44 +0000 (01:43 +0100)
In the current code, if we fail during a frequency transition, we
simply send the POSTCHANGE notification with the old frequency. This
isn't enough.

One of the core users of these notifications is the code responsible
for keeping loops_per_jiffy aligned with frequency changes. And mostly
it is written as:

if ((val == CPUFREQ_PRECHANGE  && freq->old < freq->new) ||
    (val == CPUFREQ_POSTCHANGE && freq->old > freq->new)) {
update-loops-per-jiffy...
}

So, suppose we are changing to a higher frequency and failed during
transition, then following will happen:
- CPUFREQ_PRECHANGE notification with freq-new > freq-old
- CPUFREQ_POSTCHANGE notification with freq-new == freq-old

The first one will update loops_per_jiffy and second one will do
nothing. Even if we send the 2nd notification by exchanging values of
freq-new and old, some users of these notifications might get
unstable.

This can be fixed by simply calling cpufreq_notify_post_transition()
with error code and this routine will take care of sending
notifications in the correct order.

Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
[rjw: Folded 3 patches into one, rebased unicore2 changes]
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/cpufreq/cpufreq.c
drivers/cpufreq/pcc-cpufreq.c
drivers/cpufreq/powernow-k8.c
drivers/cpufreq/unicore2-cpufreq.c

index a123a731ec4ed9641751e914df46996222b5d8ac..d533c205eea4a0298186c3f07f3ed6df368b69e8 100644 (file)
@@ -1739,17 +1739,8 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
                        pr_err("%s: Failed to change cpu frequency: %d\n",
                                        __func__, retval);
 
-               if (notify) {
-                       /*
-                        * Notify with old freq in case we failed to change
-                        * frequency
-                        */
-                       if (retval)
-                               freqs.new = freqs.old;
-
-                       cpufreq_notify_transition(policy, &freqs,
-                                       CPUFREQ_POSTCHANGE);
-               }
+               if (notify)
+                       cpufreq_notify_post_transition(policy, &freqs, retval);
        }
 
 out:
index e2b4f40ff69acaaff51f6461caafd4c8a23bdd16..1c0f1067af73318387d3d3e133e3d67a990eac61 100644 (file)
@@ -213,6 +213,7 @@ static int pcc_cpufreq_target(struct cpufreq_policy *policy,
                cpu, target_freq,
                (pcch_virt_addr + pcc_cpu_data->input_offset));
 
+       freqs.old = policy->cur;
        freqs.new = target_freq;
        cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
 
@@ -228,25 +229,20 @@ static int pcc_cpufreq_target(struct cpufreq_policy *policy,
        memset_io((pcch_virt_addr + pcc_cpu_data->input_offset), 0, BUF_SZ);
 
        status = ioread16(&pcch_hdr->status);
+       iowrite16(0, &pcch_hdr->status);
+
+       cpufreq_notify_post_transition(policy, &freqs, status != CMD_COMPLETE);
+       spin_unlock(&pcc_lock);
+
        if (status != CMD_COMPLETE) {
                pr_debug("target: FAILED for cpu %d, with status: 0x%x\n",
                        cpu, status);
-               goto cmd_incomplete;
+               return -EINVAL;
        }
-       iowrite16(0, &pcch_hdr->status);
 
-       cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
        pr_debug("target: was SUCCESSFUL for cpu %d\n", cpu);
-       spin_unlock(&pcc_lock);
 
        return 0;
-
-cmd_incomplete:
-       freqs.new = freqs.old;
-       cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
-       iowrite16(0, &pcch_hdr->status);
-       spin_unlock(&pcc_lock);
-       return -EINVAL;
 }
 
 static int pcc_get_offset(int cpu)
index 0023c7d40a51a21994620907e75520e5512c5d52..e10b646634d77ff3a4bcf0c54d154a91c7c35805 100644 (file)
@@ -964,14 +964,9 @@ static int transition_frequency_fidvid(struct powernow_k8_data *data,
        cpufreq_cpu_put(policy);
 
        cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
-
        res = transition_fid_vid(data, fid, vid);
-       if (res)
-               freqs.new = freqs.old;
-       else
-               freqs.new = find_khz_freq_from_fid(data->currfid);
+       cpufreq_notify_post_transition(policy, &freqs, res);
 
-       cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
        return res;
 }
 
index 653ae2955b555ad63607d84a4ebeb27be205e644..86f6cfec2e09fe7595379b6f64dba144a0a1c224 100644 (file)
@@ -46,20 +46,18 @@ static int ucv2_target(struct cpufreq_policy *policy,
                         unsigned int target_freq,
                         unsigned int relation)
 {
-       unsigned int cur = ucv2_getspeed(0);
        struct cpufreq_freqs freqs;
        struct clk *mclk = clk_get(NULL, "MAIN_CLK");
+       int ret;
 
-       cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
-
-       if (!clk_set_rate(mclk, target_freq * 1000)) {
-               freqs.old = cur;
-               freqs.new = target_freq;
-       }
+       freqs.old = policy->cur;
+       freqs.new = target_freq;
 
-       cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
+       cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
+       ret = clk_set_rate(mclk, target_freq * 1000);
+       cpufreq_notify_post_transition(policy, &freqs, ret);
 
-       return 0;
+       return ret;
 }
 
 static int __init ucv2_cpu_init(struct cpufreq_policy *policy)