Merge remote-tracking branch 'lsk/v3.10/topic/arm64-perf' into linux-linaro-lsk
[firefly-linux-kernel-4.4.55.git] / drivers / cpuidle / cpuidle-arm64.c
index 2cfde6ce3086389e2b3091b3e4b70257b9ec8592..50997ea942fce58975db9f7d933183671f6e740f 100644 (file)
@@ -9,6 +9,8 @@
  * published by the Free Software Foundation.
  */
 
+#define pr_fmt(fmt) "CPUidle arm64: " fmt
+
 #include <linux/cpuidle.h>
 #include <linux/cpumask.h>
 #include <linux/cpu_pm.h>
 #include <linux/module.h>
 #include <linux/of.h>
 
-#include <asm/psci.h>
+#include <asm/cpuidle.h>
 #include <asm/suspend.h>
 
-#include "of_idle_states.h"
-
-typedef int (*suspend_init_fn)(struct cpuidle_driver *,
-                              struct device_node *[]);
-
-struct cpu_suspend_ops {
-       const char *id;
-       suspend_init_fn init_fn;
-};
-
-static const struct cpu_suspend_ops suspend_operations[] __initconst = {
-       {"arm,psci", psci_dt_register_idle_states},
-       {}
-};
-
-static __init const struct cpu_suspend_ops *get_suspend_ops(const char *str)
-{
-       int i;
-
-       if (!str)
-               return NULL;
-
-       for (i = 0; suspend_operations[i].id; i++)
-               if (!strcmp(suspend_operations[i].id, str))
-                       return &suspend_operations[i];
-
-       return NULL;
-}
+#include "dt_idle_states.h"
 
 /*
- * arm_enter_idle_state - Programs CPU to enter the specified state
+ * arm64_enter_idle_state - Programs CPU to enter the specified state
  *
- * @dev: cpuidle device
- * @drv: cpuidle driver
- * @idx: state index
+ * dev: cpuidle device
+ * drv: cpuidle driver
+ * idx: state index
  *
  * Called from the CPUidle framework to program the device to the
  * specified target state selected by the governor.
  */
-static int arm_enter_idle_state(struct cpuidle_device *dev,
-                               struct cpuidle_driver *drv, int idx)
+static int arm64_enter_idle_state(struct cpuidle_device *dev,
+                                 struct cpuidle_driver *drv, int idx)
 {
        int ret;
 
@@ -68,30 +43,47 @@ static int arm_enter_idle_state(struct cpuidle_device *dev,
                return idx;
        }
 
-       cpu_pm_enter();
-       /*
-        * Pass idle state index to cpu_suspend which in turn will call
-        * the CPU ops suspend protocol with idle index as a parameter.
-        *
-        * Some states would not require context to be saved and flushed
-        * to DRAM, so calling cpu_suspend would not be stricly necessary.
-        * When power domains specifications for ARM CPUs are finalized then
-        * this code can be optimized to prevent saving registers if not
-        * needed.
-        */
-       ret = cpu_suspend(idx);
+       ret = cpu_pm_enter();
+       if (!ret) {
+               /*
+                * Pass idle state index to cpu_suspend which in turn will
+                * call the CPU ops suspend protocol with idle index as a
+                * parameter.
+                */
+               ret = cpu_suspend(idx);
 
-       cpu_pm_exit();
+               cpu_pm_exit();
+       }
 
        return ret ? -1 : idx;
 }
 
-struct cpuidle_driver arm64_idle_driver = {
+static struct cpuidle_driver arm64_idle_driver = {
        .name = "arm64_idle",
        .owner = THIS_MODULE,
+       /*
+        * State at index 0 is standby wfi and considered standard
+        * on all ARM platforms. If in some platforms simple wfi
+        * can't be used as "state 0", DT bindings must be implemented
+        * to work around this issue and allow installing a special
+        * handler for idle state index 0.
+        */
+       .states[0] = {
+               .enter                  = arm64_enter_idle_state,
+               .exit_latency           = 1,
+               .target_residency       = 1,
+               .power_usage            = UINT_MAX,
+               .flags                  = CPUIDLE_FLAG_TIME_VALID,
+               .name                   = "WFI",
+               .desc                   = "ARM64 WFI",
+       }
 };
 
-static struct device_node *state_nodes[CPUIDLE_STATE_MAX] __initdata;
+static const struct of_device_id arm64_idle_state_match[] __initconst = {
+       { .compatible = "arm,idle-state",
+         .data = arm64_enter_idle_state },
+       { },
+};
 
 /*
  * arm64_idle_init
@@ -102,58 +94,40 @@ static struct device_node *state_nodes[CPUIDLE_STATE_MAX] __initdata;
  */
 static int __init arm64_idle_init(void)
 {
-       int i, ret;
-       const char *entry_method;
-       struct device_node *idle_states_node;
-       const struct cpu_suspend_ops *suspend_init;
+       int cpu, ret;
        struct cpuidle_driver *drv = &arm64_idle_driver;
 
-       idle_states_node = of_find_node_by_path("/cpus/idle-states");
-       if (!idle_states_node)
-               return -ENOENT;
-
-       if (of_property_read_string(idle_states_node, "entry-method",
-                                   &entry_method)) {
-               pr_warn(" * %s missing entry-method property\n",
-                           idle_states_node->full_name);
-               of_node_put(idle_states_node);
-               return -EOPNOTSUPP;
-       }
-
-       suspend_init = get_suspend_ops(entry_method);
-       if (!suspend_init) {
-               pr_warn("Missing suspend initializer\n");
-               of_node_put(idle_states_node);
-               return -EOPNOTSUPP;
-       }
-
        /*
-        * State at index 0 is standby wfi and considered standard
-        * on all ARM platforms. If in some platforms simple wfi
-        * can't be used as "state 0", DT bindings must be implemented
-        * to work around this issue and allow installing a special
-        * handler for idle state index 0.
+        * Initialize idle states data, starting at index 1.
+        * This driver is DT only, if no DT idle states are detected (ret == 0)
+        * let the driver initialization fail accordingly since there is no
+        * reason to initialize the idle driver if only wfi is supported.
         */
-       drv->states[0].exit_latency = 1;
-       drv->states[0].target_residency = 1;
-       drv->states[0].flags = CPUIDLE_FLAG_TIME_VALID;
-       strncpy(drv->states[0].name, "ARM WFI", CPUIDLE_NAME_LEN);
-       strncpy(drv->states[0].desc, "ARM WFI", CPUIDLE_DESC_LEN);
+       ret = dt_init_idle_driver(drv, arm64_idle_state_match, 1);
+       if (ret <= 0) {
+               if (ret)
+                       pr_err("failed to initialize idle states\n");
+               return ret ? : -ENODEV;
+       }
 
-       drv->cpumask = (struct cpumask *) cpu_possible_mask;
        /*
-        * Start at index 1, request idle state nodes to be filled
+        * Call arch CPU operations in order to initialize
+        * idle states suspend back-end specific data
         */
-       ret = of_init_idle_driver(drv, state_nodes, 1, true);
-       if (ret)
-               return ret;
-
-       if (suspend_init->init_fn(drv, state_nodes))
-               return -EOPNOTSUPP;
+       for_each_possible_cpu(cpu) {
+               ret = cpu_init_idle(cpu);
+               if (ret) {
+                       pr_err("CPU %d failed to init idle CPU ops\n", cpu);
+                       return ret;
+               }
+       }
 
-       for (i = 0; i < drv->state_count; i++)
-               drv->states[i].enter = arm_enter_idle_state;
+       ret = cpuidle_register(drv, NULL);
+       if (ret) {
+               pr_err("failed to register cpuidle driver\n");
+               return ret;
+       }
 
-       return cpuidle_register(drv, NULL);
+       return 0;
 }
 device_initcall(arm64_idle_init);