|
@@ -1816,20 +1816,55 @@ EXPORT_SYMBOL(cpufreq_unregister_notifier);
|
|
* GOVERNORS *
|
|
* GOVERNORS *
|
|
*********************************************************************/
|
|
*********************************************************************/
|
|
|
|
|
|
|
|
+/* Must set freqs->new to intermediate frequency */
|
|
|
|
+static int __target_intermediate(struct cpufreq_policy *policy,
|
|
|
|
+ struct cpufreq_freqs *freqs, int index)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ freqs->new = cpufreq_driver->get_intermediate(policy, index);
|
|
|
|
+
|
|
|
|
+ /* We don't need to switch to intermediate freq */
|
|
|
|
+ if (!freqs->new)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ pr_debug("%s: cpu: %d, switching to intermediate freq: oldfreq: %u, intermediate freq: %u\n",
|
|
|
|
+ __func__, policy->cpu, freqs->old, freqs->new);
|
|
|
|
+
|
|
|
|
+ cpufreq_freq_transition_begin(policy, freqs);
|
|
|
|
+ ret = cpufreq_driver->target_intermediate(policy, index);
|
|
|
|
+ cpufreq_freq_transition_end(policy, freqs, ret);
|
|
|
|
+
|
|
|
|
+ if (ret)
|
|
|
|
+ pr_err("%s: Failed to change to intermediate frequency: %d\n",
|
|
|
|
+ __func__, ret);
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
static int __target_index(struct cpufreq_policy *policy,
|
|
static int __target_index(struct cpufreq_policy *policy,
|
|
struct cpufreq_frequency_table *freq_table, int index)
|
|
struct cpufreq_frequency_table *freq_table, int index)
|
|
{
|
|
{
|
|
- struct cpufreq_freqs freqs;
|
|
|
|
|
|
+ struct cpufreq_freqs freqs = {.old = policy->cur, .flags = 0};
|
|
|
|
+ unsigned int intermediate_freq = 0;
|
|
int retval = -EINVAL;
|
|
int retval = -EINVAL;
|
|
bool notify;
|
|
bool notify;
|
|
|
|
|
|
notify = !(cpufreq_driver->flags & CPUFREQ_ASYNC_NOTIFICATION);
|
|
notify = !(cpufreq_driver->flags & CPUFREQ_ASYNC_NOTIFICATION);
|
|
-
|
|
|
|
if (notify) {
|
|
if (notify) {
|
|
- freqs.old = policy->cur;
|
|
|
|
- freqs.new = freq_table[index].frequency;
|
|
|
|
- freqs.flags = 0;
|
|
|
|
|
|
+ /* Handle switching to intermediate frequency */
|
|
|
|
+ if (cpufreq_driver->get_intermediate) {
|
|
|
|
+ retval = __target_intermediate(policy, &freqs, index);
|
|
|
|
+ if (retval)
|
|
|
|
+ return retval;
|
|
|
|
+
|
|
|
|
+ intermediate_freq = freqs.new;
|
|
|
|
+ /* Set old freq to intermediate */
|
|
|
|
+ if (intermediate_freq)
|
|
|
|
+ freqs.old = freqs.new;
|
|
|
|
+ }
|
|
|
|
|
|
|
|
+ freqs.new = freq_table[index].frequency;
|
|
pr_debug("%s: cpu: %d, oldfreq: %u, new freq: %u\n",
|
|
pr_debug("%s: cpu: %d, oldfreq: %u, new freq: %u\n",
|
|
__func__, policy->cpu, freqs.old, freqs.new);
|
|
__func__, policy->cpu, freqs.old, freqs.new);
|
|
|
|
|
|
@@ -1841,9 +1876,23 @@ static int __target_index(struct cpufreq_policy *policy,
|
|
pr_err("%s: Failed to change cpu frequency: %d\n", __func__,
|
|
pr_err("%s: Failed to change cpu frequency: %d\n", __func__,
|
|
retval);
|
|
retval);
|
|
|
|
|
|
- if (notify)
|
|
|
|
|
|
+ if (notify) {
|
|
cpufreq_freq_transition_end(policy, &freqs, retval);
|
|
cpufreq_freq_transition_end(policy, &freqs, retval);
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * Failed after setting to intermediate freq? Driver should have
|
|
|
|
+ * reverted back to initial frequency and so should we. Check
|
|
|
|
+ * here for intermediate_freq instead of get_intermediate, in
|
|
|
|
+ * case we have't switched to intermediate freq at all.
|
|
|
|
+ */
|
|
|
|
+ if (unlikely(retval && intermediate_freq)) {
|
|
|
|
+ freqs.old = intermediate_freq;
|
|
|
|
+ freqs.new = policy->restore_freq;
|
|
|
|
+ cpufreq_freq_transition_begin(policy, &freqs);
|
|
|
|
+ cpufreq_freq_transition_end(policy, &freqs, 0);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
return retval;
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1875,6 +1924,9 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
|
|
if (target_freq == policy->cur)
|
|
if (target_freq == policy->cur)
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
|
|
+ /* Save last value to restore later on errors */
|
|
|
|
+ policy->restore_freq = policy->cur;
|
|
|
|
+
|
|
if (cpufreq_driver->target)
|
|
if (cpufreq_driver->target)
|
|
retval = cpufreq_driver->target(policy, target_freq, relation);
|
|
retval = cpufreq_driver->target(policy, target_freq, relation);
|
|
else if (cpufreq_driver->target_index) {
|
|
else if (cpufreq_driver->target_index) {
|
|
@@ -2361,7 +2413,8 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
|
|
!(driver_data->setpolicy || driver_data->target_index ||
|
|
!(driver_data->setpolicy || driver_data->target_index ||
|
|
driver_data->target) ||
|
|
driver_data->target) ||
|
|
(driver_data->setpolicy && (driver_data->target_index ||
|
|
(driver_data->setpolicy && (driver_data->target_index ||
|
|
- driver_data->target)))
|
|
|
|
|
|
+ driver_data->target)) ||
|
|
|
|
+ (!!driver_data->get_intermediate != !!driver_data->target_intermediate))
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
pr_debug("trying to register driver %s\n", driver_data->name);
|
|
pr_debug("trying to register driver %s\n", driver_data->name);
|