diff options
author | Viresh Kumar <viresh.kumar@linaro.org> | 2013-12-02 00:34:13 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-01-05 19:43:44 -0500 |
commit | ab1b1c4e8223f9ee66aa93aaf64c36e77cadffac (patch) | |
tree | b592025459590a4564fd8e7760707c7d77a159e1 /drivers/cpufreq | |
parent | f7ba3b41e27129575201f0f9656e83fb67e86c3b (diff) |
cpufreq: send new set of notification for transition failures
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>
Diffstat (limited to 'drivers/cpufreq')
-rw-r--r-- | drivers/cpufreq/cpufreq.c | 13 | ||||
-rw-r--r-- | drivers/cpufreq/pcc-cpufreq.c | 18 | ||||
-rw-r--r-- | drivers/cpufreq/powernow-k8.c | 7 | ||||
-rw-r--r-- | drivers/cpufreq/unicore2-cpufreq.c | 16 |
4 files changed, 17 insertions, 37 deletions
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index a123a731ec4e..d533c205eea4 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c | |||
@@ -1739,17 +1739,8 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, | |||
1739 | pr_err("%s: Failed to change cpu frequency: %d\n", | 1739 | pr_err("%s: Failed to change cpu frequency: %d\n", |
1740 | __func__, retval); | 1740 | __func__, retval); |
1741 | 1741 | ||
1742 | if (notify) { | 1742 | if (notify) |
1743 | /* | 1743 | cpufreq_notify_post_transition(policy, &freqs, retval); |
1744 | * Notify with old freq in case we failed to change | ||
1745 | * frequency | ||
1746 | */ | ||
1747 | if (retval) | ||
1748 | freqs.new = freqs.old; | ||
1749 | |||
1750 | cpufreq_notify_transition(policy, &freqs, | ||
1751 | CPUFREQ_POSTCHANGE); | ||
1752 | } | ||
1753 | } | 1744 | } |
1754 | 1745 | ||
1755 | out: | 1746 | out: |
diff --git a/drivers/cpufreq/pcc-cpufreq.c b/drivers/cpufreq/pcc-cpufreq.c index e2b4f40ff69a..1c0f1067af73 100644 --- a/drivers/cpufreq/pcc-cpufreq.c +++ b/drivers/cpufreq/pcc-cpufreq.c | |||
@@ -213,6 +213,7 @@ static int pcc_cpufreq_target(struct cpufreq_policy *policy, | |||
213 | cpu, target_freq, | 213 | cpu, target_freq, |
214 | (pcch_virt_addr + pcc_cpu_data->input_offset)); | 214 | (pcch_virt_addr + pcc_cpu_data->input_offset)); |
215 | 215 | ||
216 | freqs.old = policy->cur; | ||
216 | freqs.new = target_freq; | 217 | freqs.new = target_freq; |
217 | cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); | 218 | cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); |
218 | 219 | ||
@@ -228,25 +229,20 @@ static int pcc_cpufreq_target(struct cpufreq_policy *policy, | |||
228 | memset_io((pcch_virt_addr + pcc_cpu_data->input_offset), 0, BUF_SZ); | 229 | memset_io((pcch_virt_addr + pcc_cpu_data->input_offset), 0, BUF_SZ); |
229 | 230 | ||
230 | status = ioread16(&pcch_hdr->status); | 231 | status = ioread16(&pcch_hdr->status); |
232 | iowrite16(0, &pcch_hdr->status); | ||
233 | |||
234 | cpufreq_notify_post_transition(policy, &freqs, status != CMD_COMPLETE); | ||
235 | spin_unlock(&pcc_lock); | ||
236 | |||
231 | if (status != CMD_COMPLETE) { | 237 | if (status != CMD_COMPLETE) { |
232 | pr_debug("target: FAILED for cpu %d, with status: 0x%x\n", | 238 | pr_debug("target: FAILED for cpu %d, with status: 0x%x\n", |
233 | cpu, status); | 239 | cpu, status); |
234 | goto cmd_incomplete; | 240 | return -EINVAL; |
235 | } | 241 | } |
236 | iowrite16(0, &pcch_hdr->status); | ||
237 | 242 | ||
238 | cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); | ||
239 | pr_debug("target: was SUCCESSFUL for cpu %d\n", cpu); | 243 | pr_debug("target: was SUCCESSFUL for cpu %d\n", cpu); |
240 | spin_unlock(&pcc_lock); | ||
241 | 244 | ||
242 | return 0; | 245 | return 0; |
243 | |||
244 | cmd_incomplete: | ||
245 | freqs.new = freqs.old; | ||
246 | cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); | ||
247 | iowrite16(0, &pcch_hdr->status); | ||
248 | spin_unlock(&pcc_lock); | ||
249 | return -EINVAL; | ||
250 | } | 246 | } |
251 | 247 | ||
252 | static int pcc_get_offset(int cpu) | 248 | static int pcc_get_offset(int cpu) |
diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c index 0023c7d40a51..e10b646634d7 100644 --- a/drivers/cpufreq/powernow-k8.c +++ b/drivers/cpufreq/powernow-k8.c | |||
@@ -964,14 +964,9 @@ static int transition_frequency_fidvid(struct powernow_k8_data *data, | |||
964 | cpufreq_cpu_put(policy); | 964 | cpufreq_cpu_put(policy); |
965 | 965 | ||
966 | cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); | 966 | cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); |
967 | |||
968 | res = transition_fid_vid(data, fid, vid); | 967 | res = transition_fid_vid(data, fid, vid); |
969 | if (res) | 968 | cpufreq_notify_post_transition(policy, &freqs, res); |
970 | freqs.new = freqs.old; | ||
971 | else | ||
972 | freqs.new = find_khz_freq_from_fid(data->currfid); | ||
973 | 969 | ||
974 | cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); | ||
975 | return res; | 970 | return res; |
976 | } | 971 | } |
977 | 972 | ||
diff --git a/drivers/cpufreq/unicore2-cpufreq.c b/drivers/cpufreq/unicore2-cpufreq.c index 653ae2955b55..86f6cfec2e09 100644 --- a/drivers/cpufreq/unicore2-cpufreq.c +++ b/drivers/cpufreq/unicore2-cpufreq.c | |||
@@ -46,20 +46,18 @@ static int ucv2_target(struct cpufreq_policy *policy, | |||
46 | unsigned int target_freq, | 46 | unsigned int target_freq, |
47 | unsigned int relation) | 47 | unsigned int relation) |
48 | { | 48 | { |
49 | unsigned int cur = ucv2_getspeed(0); | ||
50 | struct cpufreq_freqs freqs; | 49 | struct cpufreq_freqs freqs; |
51 | struct clk *mclk = clk_get(NULL, "MAIN_CLK"); | 50 | struct clk *mclk = clk_get(NULL, "MAIN_CLK"); |
51 | int ret; | ||
52 | 52 | ||
53 | cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); | 53 | freqs.old = policy->cur; |
54 | 54 | freqs.new = target_freq; | |
55 | if (!clk_set_rate(mclk, target_freq * 1000)) { | ||
56 | freqs.old = cur; | ||
57 | freqs.new = target_freq; | ||
58 | } | ||
59 | 55 | ||
60 | cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); | 56 | cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); |
57 | ret = clk_set_rate(mclk, target_freq * 1000); | ||
58 | cpufreq_notify_post_transition(policy, &freqs, ret); | ||
61 | 59 | ||
62 | return 0; | 60 | return ret; |
63 | } | 61 | } |
64 | 62 | ||
65 | static int __init ucv2_cpu_init(struct cpufreq_policy *policy) | 63 | static int __init ucv2_cpu_init(struct cpufreq_policy *policy) |