Merge branch 'v3.10/topic/misc' into linux-linaro-lsk
[firefly-linux-kernel-4.4.55.git] / drivers / cpufreq / cpufreq.c
index 648554742a998aec1f2c0fdfea26adb597a8d7db..ce94c323fa13dc88f147abd935fba80497b426d6 100644 (file)
@@ -31,6 +31,8 @@
 #include <linux/completion.h>
 #include <linux/mutex.h>
 #include <linux/syscore_ops.h>
+#include <linux/suspend.h>
+#include <linux/tick.h>
 
 #include <trace/events/power.h>
 
@@ -46,6 +48,15 @@ static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data);
 static DEFINE_PER_CPU(char[CPUFREQ_NAME_LEN], cpufreq_cpu_governor);
 #endif
 static DEFINE_RWLOCK(cpufreq_driver_lock);
+static DEFINE_MUTEX(cpufreq_governor_lock);
+
+/* Flag to suspend/resume CPUFreq governors */
+static bool cpufreq_suspended;
+
+static inline bool has_target(void)
+{
+       return cpufreq_driver->target;
+}
 
 /*
  * cpu_policy_rwsem is a per CPU reader-writer semaphore designed to cure
@@ -1274,82 +1285,89 @@ static struct subsys_interface cpufreq_interface = {
 
 
 /**
- * cpufreq_bp_suspend - Prepare the boot CPU for system suspend.
+ * cpufreq_suspend() - Suspend CPUFreq governors
  *
- * This function is only executed for the boot processor.  The other CPUs
- * have been put offline by means of CPU hotplug.
+ * Called during system wide Suspend/Hibernate cycles for suspending governors
+ * as some platforms can't change frequency after this point in suspend cycle.
+ * Because some of the devices (like: i2c, regulators, etc) they use for
+ * changing frequency are suspended quickly after this point.
  */
-static int cpufreq_bp_suspend(void)
+void cpufreq_suspend(void)
 {
-       int ret = 0;
+       struct cpufreq_policy *policy;
+       int cpu;
 
-       int cpu = smp_processor_id();
-       struct cpufreq_policy *cpu_policy;
+       if (!cpufreq_driver)
+               return;
 
-       pr_debug("suspending cpu %u\n", cpu);
+       if (!has_target())
+               return;
 
-       /* If there's no policy for the boot CPU, we have nothing to do. */
-       cpu_policy = cpufreq_cpu_get(cpu);
-       if (!cpu_policy)
-               return 0;
+       pr_debug("%s: Suspending Governors\n", __func__);
 
-       if (cpufreq_driver->suspend) {
-               ret = cpufreq_driver->suspend(cpu_policy);
-               if (ret)
-                       printk(KERN_ERR "cpufreq: suspend failed in ->suspend "
-                                       "step on CPU %u\n", cpu_policy->cpu);
+       for_each_possible_cpu(cpu) {
+               if (!cpu_online(cpu))
+                       continue;
+
+               policy = cpufreq_cpu_get(cpu);
+
+               if (__cpufreq_governor(policy, CPUFREQ_GOV_STOP))
+                       pr_err("%s: Failed to stop governor for policy: %p\n",
+                               __func__, policy);
+               else if (cpufreq_driver->suspend
+                   && cpufreq_driver->suspend(policy))
+                       pr_err("%s: Failed to suspend driver: %p\n", __func__,
+                               policy);
        }
 
-       cpufreq_cpu_put(cpu_policy);
-       return ret;
+       cpufreq_suspended = true;
 }
 
 /**
- * cpufreq_bp_resume - Restore proper frequency handling of the boot CPU.
+ * cpufreq_resume() - Resume CPUFreq governors
  *
- *     1.) resume CPUfreq hardware support (cpufreq_driver->resume())
- *     2.) schedule call cpufreq_update_policy() ASAP as interrupts are
- *         restored. It will verify that the current freq is in sync with
- *         what we believe it to be. This is a bit later than when it
- *         should be, but nonethteless it's better than calling
- *         cpufreq_driver->get() here which might re-enable interrupts...
- *
- * This function is only executed for the boot CPU.  The other CPUs have not
- * been turned on yet.
+ * Called during system wide Suspend/Hibernate cycle for resuming governors that
+ * are suspended with cpufreq_suspend().
  */
-static void cpufreq_bp_resume(void)
+void cpufreq_resume(void)
 {
-       int ret = 0;
-
-       int cpu = smp_processor_id();
-       struct cpufreq_policy *cpu_policy;
+       struct cpufreq_policy *policy;
+       int cpu;
 
-       pr_debug("resuming cpu %u\n", cpu);
+       if (!cpufreq_driver)
+               return;
 
-       /* If there's no policy for the boot CPU, we have nothing to do. */
-       cpu_policy = cpufreq_cpu_get(cpu);
-       if (!cpu_policy)
+       if (!has_target())
                return;
 
-       if (cpufreq_driver->resume) {
-               ret = cpufreq_driver->resume(cpu_policy);
-               if (ret) {
-                       printk(KERN_ERR "cpufreq: resume failed in ->resume "
-                                       "step on CPU %u\n", cpu_policy->cpu);
-                       goto fail;
-               }
-       }
+       pr_debug("%s: Resuming Governors\n", __func__);
 
-       schedule_work(&cpu_policy->update);
+       cpufreq_suspended = false;
 
-fail:
-       cpufreq_cpu_put(cpu_policy);
-}
+       for_each_possible_cpu(cpu) {
+               if (!cpu_online(cpu))
+                       continue;
 
-static struct syscore_ops cpufreq_syscore_ops = {
-       .suspend        = cpufreq_bp_suspend,
-       .resume         = cpufreq_bp_resume,
-};
+               policy = cpufreq_cpu_get(cpu);
+
+               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
+                * policy in list. It will verify that the current freq is in
+                * sync with what we believe it to be.
+                */
+               if (cpu == 0)
+                       schedule_work(&policy->update);
+       }
+}
 
 /**
  *     cpufreq_get_current_driver - return current driver's name
@@ -1543,6 +1561,10 @@ static int __cpufreq_governor(struct cpufreq_policy *policy,
        struct cpufreq_governor *gov = NULL;
 #endif
 
+       /* Don't start any governor operations if we are entering suspend */
+       if (cpufreq_suspended)
+               return 0;
+
        if (policy->governor->max_transition_latency &&
            policy->cpuinfo.transition_latency >
            policy->governor->max_transition_latency) {
@@ -1563,6 +1585,21 @@ static int __cpufreq_governor(struct cpufreq_policy *policy,
 
        pr_debug("__cpufreq_governor for CPU %u, event %u\n",
                                                policy->cpu, event);
+
+       mutex_lock(&cpufreq_governor_lock);
+       if ((!policy->governor_enabled && (event == CPUFREQ_GOV_STOP)) ||
+           (policy->governor_enabled && (event == CPUFREQ_GOV_START))) {
+               mutex_unlock(&cpufreq_governor_lock);
+               return -EBUSY;
+       }
+
+       if (event == CPUFREQ_GOV_STOP)
+               policy->governor_enabled = false;
+       else if (event == CPUFREQ_GOV_START)
+               policy->governor_enabled = true;
+
+       mutex_unlock(&cpufreq_governor_lock);
+
        ret = policy->governor->governor(policy, event);
 
        if (!ret) {
@@ -1570,6 +1607,14 @@ static int __cpufreq_governor(struct cpufreq_policy *policy,
                        policy->governor->initialized++;
                else if (event == CPUFREQ_GOV_POLICY_EXIT)
                        policy->governor->initialized--;
+       } else {
+               /* Restore original values */
+               mutex_lock(&cpufreq_governor_lock);
+               if (event == CPUFREQ_GOV_STOP)
+                       policy->governor_enabled = true;
+               else if (event == CPUFREQ_GOV_START)
+                       policy->governor_enabled = false;
+               mutex_unlock(&cpufreq_governor_lock);
        }
 
        /* we keep one module reference alive for
@@ -1977,7 +2022,6 @@ static int __init cpufreq_core_init(void)
 
        cpufreq_global_kobject = kobject_create_and_add("cpufreq", &cpu_subsys.dev_root->kobj);
        BUG_ON(!cpufreq_global_kobject);
-       register_syscore_ops(&cpufreq_syscore_ops);
 
        return 0;
 }