Merge branch 'pm-cpufreq'
[firefly-linux-kernel-4.4.55.git] / drivers / cpufreq / cpufreq.c
index 3aa7a7a226b3f23f2977975651fc0fc2869ffd7b..abda6609d3e79f670c7143a01bbec53078e8bce1 100644 (file)
@@ -331,16 +331,15 @@ static void __cpufreq_notify_transition(struct cpufreq_policy *policy,
  * function. It is called twice on all CPU frequency changes that have
  * external effects.
  */
-void cpufreq_notify_transition(struct cpufreq_policy *policy,
+static void cpufreq_notify_transition(struct cpufreq_policy *policy,
                struct cpufreq_freqs *freqs, unsigned int state)
 {
        for_each_cpu(freqs->cpu, policy->cpus)
                __cpufreq_notify_transition(policy, freqs, state);
 }
-EXPORT_SYMBOL_GPL(cpufreq_notify_transition);
 
 /* Do post notifications when there are chances that transition has failed */
-void cpufreq_notify_post_transition(struct cpufreq_policy *policy,
+static void cpufreq_notify_post_transition(struct cpufreq_policy *policy,
                struct cpufreq_freqs *freqs, int transition_failed)
 {
        cpufreq_notify_transition(policy, freqs, CPUFREQ_POSTCHANGE);
@@ -351,7 +350,41 @@ void cpufreq_notify_post_transition(struct cpufreq_policy *policy,
        cpufreq_notify_transition(policy, freqs, CPUFREQ_PRECHANGE);
        cpufreq_notify_transition(policy, freqs, CPUFREQ_POSTCHANGE);
 }
-EXPORT_SYMBOL_GPL(cpufreq_notify_post_transition);
+
+void cpufreq_freq_transition_begin(struct cpufreq_policy *policy,
+               struct cpufreq_freqs *freqs)
+{
+wait:
+       wait_event(policy->transition_wait, !policy->transition_ongoing);
+
+       spin_lock(&policy->transition_lock);
+
+       if (unlikely(policy->transition_ongoing)) {
+               spin_unlock(&policy->transition_lock);
+               goto wait;
+       }
+
+       policy->transition_ongoing = true;
+
+       spin_unlock(&policy->transition_lock);
+
+       cpufreq_notify_transition(policy, freqs, CPUFREQ_PRECHANGE);
+}
+EXPORT_SYMBOL_GPL(cpufreq_freq_transition_begin);
+
+void cpufreq_freq_transition_end(struct cpufreq_policy *policy,
+               struct cpufreq_freqs *freqs, int transition_failed)
+{
+       if (unlikely(WARN_ON(!policy->transition_ongoing)))
+               return;
+
+       cpufreq_notify_post_transition(policy, freqs, transition_failed);
+
+       policy->transition_ongoing = false;
+
+       wake_up(&policy->transition_wait);
+}
+EXPORT_SYMBOL_GPL(cpufreq_freq_transition_end);
 
 
 /*********************************************************************
@@ -985,6 +1018,8 @@ static struct cpufreq_policy *cpufreq_policy_alloc(void)
 
        INIT_LIST_HEAD(&policy->policy_list);
        init_rwsem(&policy->rwsem);
+       spin_lock_init(&policy->transition_lock);
+       init_waitqueue_head(&policy->transition_wait);
 
        return policy;
 
@@ -1470,8 +1505,8 @@ static void cpufreq_out_of_sync(unsigned int cpu, unsigned int old_freq,
        policy = per_cpu(cpufreq_cpu_data, cpu);
        read_unlock_irqrestore(&cpufreq_driver_lock, flags);
 
-       cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
-       cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
+       cpufreq_freq_transition_begin(policy, &freqs);
+       cpufreq_freq_transition_end(policy, &freqs, 0);
 }
 
 /**
@@ -1652,14 +1687,13 @@ void cpufreq_resume(void)
        cpufreq_suspended = false;
 
        list_for_each_entry(policy, &cpufreq_policy_list, policy_list) {
-               if (__cpufreq_governor(policy, CPUFREQ_GOV_START)
+               if (cpufreq_driver->resume && cpufreq_driver->resume(policy))
+                       pr_err("%s: Failed to resume driver: %p\n", __func__,
+                               policy);
+               else if (__cpufreq_governor(policy, CPUFREQ_GOV_START)
                    || __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS))
                        pr_err("%s: Failed to start governor for policy: %p\n",
                                __func__, policy);
-               else if (cpufreq_driver->resume
-                   && cpufreq_driver->resume(policy))
-                       pr_err("%s: Failed to resume driver: %p\n", __func__,
-                               policy);
 
                /*
                 * schedule call cpufreq_update_policy() for boot CPU, i.e. last
@@ -1832,8 +1866,7 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
                        pr_debug("%s: cpu: %d, oldfreq: %u, new freq: %u\n",
                                 __func__, policy->cpu, freqs.old, freqs.new);
 
-                       cpufreq_notify_transition(policy, &freqs,
-                                       CPUFREQ_PRECHANGE);
+                       cpufreq_freq_transition_begin(policy, &freqs);
                }
 
                retval = cpufreq_driver->target_index(policy, index);
@@ -1842,7 +1875,7 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
                               __func__, retval);
 
                if (notify)
-                       cpufreq_notify_post_transition(policy, &freqs, retval);
+                       cpufreq_freq_transition_end(policy, &freqs, retval);
        }
 
 out: