Merge remote-tracking branch 'lsk/v3.10/topic/arm64-cpuidle' into linux-linaro-lsk
authorMark Brown <broonie@kernel.org>
Sun, 18 Jan 2015 13:12:48 +0000 (13:12 +0000)
committerMark Brown <broonie@kernel.org>
Sun, 18 Jan 2015 13:12:48 +0000 (13:12 +0000)
Conflicts:
arch/arm64/kernel/Makefile
drivers/cpuidle/Makefile

13 files changed:
arch/arm/kernel/Makefile
arch/arm64/include/asm/cpu_ops.h
arch/arm64/include/asm/cpuidle.h [new file with mode: 0644]
arch/arm64/kernel/Makefile
arch/arm64/kernel/cpuidle.c [new file with mode: 0644]
drivers/cpuidle/Kconfig
drivers/cpuidle/Kconfig.arm64
drivers/cpuidle/Makefile
drivers/cpuidle/cpuidle-arm64.c
drivers/cpuidle/dt_idle_states.c [new file with mode: 0644]
drivers/cpuidle/dt_idle_states.h [new file with mode: 0644]
drivers/cpuidle/of_idle_states.c [deleted file]
drivers/cpuidle/of_idle_states.h [deleted file]

index 65160d5fe850c7507cdd98a756482b7e52fa1dfa..39754851a3b1e9c1f111166ba78c0c37f1d13591 100644 (file)
@@ -32,6 +32,7 @@ obj-$(CONFIG_ARTHUR)          += arthur.o
 obj-$(CONFIG_ISA_DMA)          += dma-isa.o
 obj-$(CONFIG_PCI)              += bios32.o isa.o
 obj-$(CONFIG_ARM_CPU_SUSPEND)  += sleep.o suspend.o
+obj-$(CONFIG_ARM_CPU_IDLE)     += cpuidle.o
 obj-$(CONFIG_SMP)              += smp.o smp_tlb.o
 obj-$(CONFIG_HAVE_ARM_SCU)     += smp_scu.o
 obj-$(CONFIG_HAVE_ARM_TWD)     += smp_twd.o
index d7b4b38a8e8625f25131bfc5d72c79aecfbbc4f8..47dfa31ad71a531e3bfed91c9ea0c519a44fee58 100644 (file)
@@ -28,6 +28,8 @@ struct device_node;
  *             enable-method property.
  * @cpu_init:  Reads any data necessary for a specific enable-method from the
  *             devicetree, for a given cpu node and proposed logical id.
+ * @cpu_init_idle: Reads any data necessary to initialize CPU idle states from
+ *             devicetree, for a given cpu node and proposed logical id.
  * @cpu_prepare: Early one-time preparation step for a cpu. If there is a
  *             mechanism for doing so, tests whether it is possible to boot
  *             the given CPU.
@@ -47,6 +49,7 @@ struct device_node;
 struct cpu_operations {
        const char      *name;
        int             (*cpu_init)(struct device_node *, unsigned int);
+       int             (*cpu_init_idle)(struct device_node *, unsigned int);
        int             (*cpu_prepare)(unsigned int);
        int             (*cpu_boot)(unsigned int);
        void            (*cpu_postboot)(void);
diff --git a/arch/arm64/include/asm/cpuidle.h b/arch/arm64/include/asm/cpuidle.h
new file mode 100644 (file)
index 0000000..b52a993
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __ASM_CPUIDLE_H
+#define __ASM_CPUIDLE_H
+
+#ifdef CONFIG_CPU_IDLE
+extern int cpu_init_idle(unsigned int cpu);
+#else
+static inline int cpu_init_idle(unsigned int cpu)
+{
+       return -EOPNOTSUPP;
+}
+#endif
+
+#endif
index ac389d32ccde1c626f5f0de5ed02cf6bb03c6dfc..a8ad571c4758fd06593b6b2ac0dff8307379c9e9 100644 (file)
@@ -30,6 +30,7 @@ arm64-obj-$(CONFIG_EARLY_PRINTK)      += early_printk.o
 arm64-obj-$(CONFIG_ARM_CPU_TOPOLOGY)  += topology.o
 arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND)  += sleep.o suspend.o
 arm64-obj-$(CONFIG_JUMP_LABEL)         += jump_label.o
+arm64-obj-$(CONFIG_CPU_IDLE)           += cpuidle.o
 arm64-obj-$(CONFIG_KGDB)               += kgdb.o
 arm64-obj-$(CONFIG_EFI)                        += efi.o efi-stub.o efi-entry.o
 
diff --git a/arch/arm64/kernel/cpuidle.c b/arch/arm64/kernel/cpuidle.c
new file mode 100644 (file)
index 0000000..19d17f5
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * ARM64 CPU idle arch support
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <asm/cpuidle.h>
+#include <asm/cpu_ops.h>
+
+int cpu_init_idle(unsigned int cpu)
+{
+       int ret = -EOPNOTSUPP;
+       struct device_node *cpu_node = of_cpu_device_node_get(cpu);
+
+       if (!cpu_node)
+               return -ENODEV;
+
+       if (cpu_ops[cpu] && cpu_ops[cpu]->cpu_init_idle)
+               ret = cpu_ops[cpu]->cpu_init_idle(cpu_node, cpu);
+
+       of_node_put(cpu_node);
+       return ret;
+}
index 842d7ba83101b224fc62bae095a8065307f00112..9625ce7ed5f8dc3c2a5bf46cc70a39b2a88fedd8 100644 (file)
@@ -31,26 +31,20 @@ config CPU_IDLE_GOV_MENU
 config ARCH_NEEDS_CPU_IDLE_COUPLED
        def_bool n
 
-config OF_IDLE_STATES
-        bool "Idle states DT support"
-       depends on ARM || ARM64
-       default n
-       help
-        Allows the CPU idle framework to initialize CPU idle drivers
-        state data by using DT provided nodes compliant with idle states
-        device tree bindings.
+config DT_IDLE_STATES
+       bool
 
 if CPU_IDLE
 
+menu "ARM64 CPU Idle Drivers"
+depends on ARM64
+source "drivers/cpuidle/Kconfig.arm64"
+endmenu
+
 config CPU_IDLE_CALXEDA
        bool "CPU Idle Driver for Calxeda processors"
        depends on ARCH_HIGHBANK
        help
          Select this to enable cpuidle on Calxeda processors.
 
-menu "ARM64 CPU Idle Drivers"
-depends on ARM64
-source "drivers/cpuidle/Kconfig.arm64"
-endmenu
-
 endif
index b83612c67e6de58cc5e10937c1a0445818ed839b..d0a08ed1b2ee62823e3f457a1ebf4905a41332f4 100644 (file)
@@ -4,10 +4,11 @@
 
 config ARM64_CPUIDLE
        bool "Generic ARM64 CPU idle Driver"
-       select OF_IDLE_STATES
+       select ARM64_CPU_SUSPEND
+       select DT_IDLE_STATES
        help
-         Select this to enable generic cpuidle driver for ARM v8.
+         Select this to enable generic cpuidle driver for ARM64.
          It provides a generic idle driver whose idle states are configured
          at run-time through DT nodes. The CPUidle suspend backend is
-         initialized by the device tree parsing code on matching the entry
-         method to the respective CPU operations.
+         initialized by calling the CPU operations init idle hook
+         provided by architecture code.
index 2d97bcfecd00cfa02f4239960abb5f37e5a6639c..0bd32cd03f0af843d2917dbce105419a7cdae0eb 100644 (file)
@@ -5,7 +5,7 @@
 obj-y += cpuidle.o driver.o governor.o sysfs.o governors/
 obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
 obj-$(CONFIG_BIG_LITTLE) += arm_big_little.o
-obj-$(CONFIG_OF_IDLE_STATES)             += of_idle_states.o
+obj-$(CONFIG_DT_IDLE_STATES)             += dt_idle_states.o
 
 obj-$(CONFIG_CPU_IDLE_CALXEDA) += cpuidle-calxeda.o
 obj-$(CONFIG_ARCH_KIRKWOOD) += cpuidle-kirkwood.o
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);
diff --git a/drivers/cpuidle/dt_idle_states.c b/drivers/cpuidle/dt_idle_states.c
new file mode 100644 (file)
index 0000000..52f4d11
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * DT idle states parsing code.
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) "DT idle-states: " fmt
+
+#include <linux/cpuidle.h>
+#include <linux/cpumask.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include "dt_idle_states.h"
+
+static int init_state_node(struct cpuidle_state *idle_state,
+                          const struct of_device_id *matches,
+                          struct device_node *state_node)
+{
+       int err;
+       const struct of_device_id *match_id;
+
+       match_id = of_match_node(matches, state_node);
+       if (!match_id)
+               return -ENODEV;
+       /*
+        * CPUidle drivers are expected to initialize the const void *data
+        * pointer of the passed in struct of_device_id array to the idle
+        * state enter function.
+        */
+       idle_state->enter = match_id->data;
+
+       err = of_property_read_u32(state_node, "wakeup-latency-us",
+                                  &idle_state->exit_latency);
+       if (err) {
+               u32 entry_latency, exit_latency;
+
+               err = of_property_read_u32(state_node, "entry-latency-us",
+                                          &entry_latency);
+               if (err) {
+                       pr_debug(" * %s missing entry-latency-us property\n",
+                                state_node->full_name);
+                       return -EINVAL;
+               }
+
+               err = of_property_read_u32(state_node, "exit-latency-us",
+                                          &exit_latency);
+               if (err) {
+                       pr_debug(" * %s missing exit-latency-us property\n",
+                                state_node->full_name);
+                       return -EINVAL;
+               }
+               /*
+                * If wakeup-latency-us is missing, default to entry+exit
+                * latencies as defined in idle states bindings
+                */
+               idle_state->exit_latency = entry_latency + exit_latency;
+       }
+
+       err = of_property_read_u32(state_node, "min-residency-us",
+                                  &idle_state->target_residency);
+       if (err) {
+               pr_debug(" * %s missing min-residency-us property\n",
+                            state_node->full_name);
+               return -EINVAL;
+       }
+
+       idle_state->flags = CPUIDLE_FLAG_TIME_VALID;
+       if (of_property_read_bool(state_node, "local-timer-stop"))
+               idle_state->flags |= CPUIDLE_FLAG_TIMER_STOP;
+       /*
+        * TODO:
+        *      replace with kstrdup and pointer assignment when name
+        *      and desc become string pointers
+        */
+       strncpy(idle_state->name, state_node->name, CPUIDLE_NAME_LEN - 1);
+       strncpy(idle_state->desc, state_node->name, CPUIDLE_DESC_LEN - 1);
+       return 0;
+}
+
+/*
+ * Check that the idle state is uniform across all CPUs in the CPUidle driver
+ * cpumask
+ */
+static bool idle_state_valid(struct device_node *state_node, unsigned int idx,
+                            const cpumask_t *cpumask)
+{
+       int cpu;
+       struct device_node *cpu_node, *curr_state_node;
+       bool valid = true;
+
+       /*
+        * Compare idle state phandles for index idx on all CPUs in the
+        * CPUidle driver cpumask. Start from next logical cpu following
+        * cpumask_first(cpumask) since that's the CPU state_node was
+        * retrieved from. If a mismatch is found bail out straight
+        * away since we certainly hit a firmware misconfiguration.
+        */
+       for (cpu = cpumask_next(cpumask_first(cpumask), cpumask);
+            cpu < nr_cpu_ids; cpu = cpumask_next(cpu, cpumask)) {
+               cpu_node = of_cpu_device_node_get(cpu);
+               curr_state_node = of_parse_phandle(cpu_node, "cpu-idle-states",
+                                                  idx);
+               if (state_node != curr_state_node)
+                       valid = false;
+
+               of_node_put(curr_state_node);
+               of_node_put(cpu_node);
+               if (!valid)
+                       break;
+       }
+
+       return valid;
+}
+
+/**
+ * dt_init_idle_driver() - Parse the DT idle states and initialize the
+ *                        idle driver states array
+ * @drv:         Pointer to CPU idle driver to be initialized
+ * @matches:     Array of of_device_id match structures to search in for
+ *               compatible idle state nodes. The data pointer for each valid
+ *               struct of_device_id entry in the matches array must point to
+ *               a function with the following signature, that corresponds to
+ *               the CPUidle state enter function signature:
+ *
+ *               int (*)(struct cpuidle_device *dev,
+ *                       struct cpuidle_driver *drv,
+ *                       int index);
+ *
+ * @start_idx:    First idle state index to be initialized
+ *
+ * If DT idle states are detected and are valid the state count and states
+ * array entries in the cpuidle driver are initialized accordingly starting
+ * from index start_idx.
+ *
+ * Return: number of valid DT idle states parsed, <0 on failure
+ */
+int dt_init_idle_driver(struct cpuidle_driver *drv,
+                       const struct of_device_id *matches,
+                       unsigned int start_idx)
+{
+       struct cpuidle_state *idle_state;
+       struct device_node *state_node, *cpu_node;
+       int i, err = 0;
+       const cpumask_t *cpumask;
+       unsigned int state_idx = start_idx;
+
+       if (state_idx >= CPUIDLE_STATE_MAX)
+               return -EINVAL;
+       /*
+        * We get the idle states for the first logical cpu in the
+        * driver mask (or cpu_possible_mask if the driver cpumask is not set)
+        * and we check through idle_state_valid() if they are uniform
+        * across CPUs, otherwise we hit a firmware misconfiguration.
+        */
+       cpumask = drv->cpumask ? : cpu_possible_mask;
+       cpu_node = of_cpu_device_node_get(cpumask_first(cpumask));
+
+       for (i = 0; ; i++) {
+               state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
+               if (!state_node)
+                       break;
+
+               if (!idle_state_valid(state_node, i, cpumask)) {
+                       pr_warn("%s idle state not valid, bailing out\n",
+                               state_node->full_name);
+                       err = -EINVAL;
+                       break;
+               }
+
+               if (state_idx == CPUIDLE_STATE_MAX) {
+                       pr_warn("State index reached static CPU idle driver states array size\n");
+                       break;
+               }
+
+               idle_state = &drv->states[state_idx++];
+               err = init_state_node(idle_state, matches, state_node);
+               if (err) {
+                       pr_err("Parsing idle state node %s failed with err %d\n",
+                              state_node->full_name, err);
+                       err = -EINVAL;
+                       break;
+               }
+               of_node_put(state_node);
+       }
+
+       of_node_put(state_node);
+       of_node_put(cpu_node);
+       if (err)
+               return err;
+       /*
+        * Update the driver state count only if some valid DT idle states
+        * were detected
+        */
+       if (i)
+               drv->state_count = state_idx;
+
+       /*
+        * Return the number of present and valid DT idle states, which can
+        * also be 0 on platforms with missing DT idle states or legacy DT
+        * configuration predating the DT idle states bindings.
+        */
+       return i;
+}
+EXPORT_SYMBOL_GPL(dt_init_idle_driver);
diff --git a/drivers/cpuidle/dt_idle_states.h b/drivers/cpuidle/dt_idle_states.h
new file mode 100644 (file)
index 0000000..4818134
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef __DT_IDLE_STATES
+#define __DT_IDLE_STATES
+
+int dt_init_idle_driver(struct cpuidle_driver *drv,
+                       const struct of_device_id *matches,
+                       unsigned int start_idx);
+#endif
diff --git a/drivers/cpuidle/of_idle_states.c b/drivers/cpuidle/of_idle_states.c
deleted file mode 100644 (file)
index eceb1b4..0000000
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * OF idle states parsing code.
- *
- * Copyright (C) 2014 ARM Ltd.
- * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/cpuidle.h>
-#include <linux/cpumask.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/list_sort.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/slab.h>
-
-#include "of_idle_states.h"
-
-struct state_elem {
-       struct list_head list;
-       struct device_node *node;
-       int val;
-};
-
-static struct list_head head __initdata = LIST_HEAD_INIT(head);
-
-static bool __init state_cpu_valid(struct device_node *state_node,
-                                  struct device_node *cpu_node)
-{
-       int i = 0;
-       struct device_node *cpu_state;
-
-       while ((cpu_state = of_parse_phandle(cpu_node,
-                                            "cpu-idle-states", i++))) {
-               if (cpu_state && state_node == cpu_state) {
-                       of_node_put(cpu_state);
-                       return true;
-               }
-               of_node_put(cpu_state);
-       }
-       return false;
-}
-
-static bool __init state_cpus_valid(const cpumask_t *cpus,
-                                   struct device_node *state_node)
-{
-       int cpu;
-       struct device_node *cpu_node;
-
-       /*
-        * Check if state is valid on driver cpumask cpus
-        */
-       for_each_cpu(cpu, cpus) {
-               cpu_node = of_get_cpu_node(cpu, NULL);
-
-               if (!cpu_node) {
-                       pr_err("Missing device node for CPU %d\n", cpu);
-                       return false;
-               }
-
-               if (!state_cpu_valid(state_node, cpu_node))
-                       return false;
-       }
-
-       return true;
-}
-
-static int __init state_cmp(void *priv, struct list_head *a,
-                           struct list_head *b)
-{
-       struct state_elem *ela, *elb;
-
-       ela = container_of(a, struct state_elem, list);
-       elb = container_of(b, struct state_elem, list);
-
-       return ela->val - elb->val;
-}
-
-static int __init add_state_node(cpumask_t *cpumask,
-                                struct device_node *state_node)
-{
-       struct state_elem *el;
-       u32 val;
-
-       pr_debug(" * %s...\n", state_node->full_name);
-
-       if (!state_cpus_valid(cpumask, state_node))
-               return -EINVAL;
-       /*
-        * Parse just the value required to sort the states.
-        */
-       if (of_property_read_u32(state_node, "min-residency-us",
-                                &val)) {
-               pr_debug(" * %s missing min-residency-us property\n",
-                        state_node->full_name);
-               return -EINVAL;
-       }
-
-       el = kmalloc(sizeof(*el), GFP_KERNEL);
-       if (!el) {
-               pr_err("%s failed to allocate memory\n", __func__);
-               return -ENOMEM;
-       }
-
-       el->node = state_node;
-       el->val = val;
-       list_add_tail(&el->list, &head);
-
-       return 0;
-}
-
-static void __init init_state_node(struct cpuidle_driver *drv,
-                                  struct device_node *state_node,
-                                  int *cnt)
-{
-       struct cpuidle_state *idle_state;
-
-       pr_debug(" * %s...\n", state_node->full_name);
-
-       idle_state = &drv->states[*cnt];
-
-       if (of_property_read_u32(state_node, "exit-latency-us",
-                                &idle_state->exit_latency)) {
-               pr_debug(" * %s missing exit-latency-us property\n",
-                            state_node->full_name);
-               return;
-       }
-
-       if (of_property_read_u32(state_node, "min-residency-us",
-                                &idle_state->target_residency)) {
-               pr_debug(" * %s missing min-residency-us property\n",
-                            state_node->full_name);
-               return;
-       }
-       /*
-        * It is unknown to the idle driver if and when the tick_device
-        * loses context when the CPU enters the idle states. To solve
-        * this issue the tick device must be linked to a power domain
-        * so that the idle driver can check on which states the device
-        * loses its context. Current code takes the conservative choice
-        * of defining the idle state as one where the tick device always
-        * loses its context. On platforms where tick device never loses
-        * its context (ie it is not a C3STOP device) this turns into
-        * a nop. On platforms where the tick device does lose context in some
-        * states, this code can be optimized, when power domain specifications
-        * for ARM CPUs are finalized.
-        */
-       idle_state->flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TIMER_STOP;
-
-       strncpy(idle_state->name, state_node->name, CPUIDLE_NAME_LEN);
-       strncpy(idle_state->desc, state_node->name, CPUIDLE_NAME_LEN);
-
-       (*cnt)++;
-}
-
-static int __init init_idle_states(struct cpuidle_driver *drv,
-                                  struct device_node *state_nodes[],
-                                  unsigned int start_idx, bool init_nodes)
-{
-       struct state_elem *el;
-       struct list_head *curr, *tmp;
-       unsigned int cnt = start_idx;
-
-       list_for_each_entry(el, &head, list) {
-               /*
-                * Check if the init function has to fill the
-                * state_nodes array on behalf of the CPUidle driver.
-                */
-               if (init_nodes)
-                       state_nodes[cnt] = el->node;
-               /*
-                * cnt is updated on return if a state was added.
-                */
-               init_state_node(drv, el->node, &cnt);
-
-               if (cnt == CPUIDLE_STATE_MAX) {
-                       pr_warn("State index reached static CPU idle state limit\n");
-                       break;
-               }
-       }
-
-       drv->state_count = cnt;
-
-       list_for_each_safe(curr, tmp, &head) {
-               list_del(curr);
-               kfree(container_of(curr, struct state_elem, list));
-       }
-
-       /*
-        * If no idle states are detected, return an error and let the idle
-        * driver initialization fail accordingly.
-        */
-       return (cnt > start_idx) ? 0 : -ENODATA;
-}
-
-static void __init add_idle_states(struct cpuidle_driver *drv,
-                                  struct device_node *idle_states)
-{
-       struct device_node *state_node;
-
-       for_each_child_of_node(idle_states, state_node) {
-               if ((!of_device_is_compatible(state_node, "arm,idle-state"))) {
-                       pr_warn(" * %s: children of /cpus/idle-states must be \"arm,idle-state\" compatible\n",
-                                    state_node->full_name);
-                       continue;
-               }
-               /*
-                * If memory allocation fails, better bail out.
-                * Initialized nodes are freed at initialization
-                * completion in of_init_idle_driver().
-                */
-               if ((add_state_node(drv->cpumask, state_node) == -ENOMEM))
-                       break;
-       }
-       /*
-        * Sort the states list before initializing the CPUidle driver
-        * states array.
-        */
-       list_sort(NULL, &head, state_cmp);
-}
-
-/*
- * of_init_idle_driver - Parse the DT idle states and initialize the
- *                      idle driver states array
- *
- * @drv:         Pointer to CPU idle driver to be initialized
- * @state_nodes:  Array of struct device_nodes to be initialized if
- *               init_nodes == true. Must be sized CPUIDLE_STATE_MAX
- * @start_idx:    First idle state index to be initialized
- * @init_nodes:   Boolean to request device nodes initialization
- *
- * Returns:
- *     0 on success
- *     <0 on failure
- *
- *     On success the states array in the cpuidle driver contains
- *     initialized entries in the states array, starting from index start_idx.
- *     If init_nodes == true, on success the state_nodes array is initialized
- *     with idle state DT node pointers, starting from index start_idx,
- *     in a 1:1 relation with the idle driver states array.
- */
-int __init of_init_idle_driver(struct cpuidle_driver *drv,
-                              struct device_node *state_nodes[],
-                              unsigned int start_idx, bool init_nodes)
-{
-       struct device_node *idle_states_node;
-       int ret;
-
-       if (start_idx >= CPUIDLE_STATE_MAX) {
-               pr_warn("State index exceeds static CPU idle driver states array size\n");
-               return -EINVAL;
-       }
-
-       if (WARN(init_nodes && !state_nodes,
-               "Requested nodes stashing in an invalid nodes container\n"))
-               return -EINVAL;
-
-       idle_states_node = of_find_node_by_path("/cpus/idle-states");
-       if (!idle_states_node)
-               return -ENOENT;
-
-       add_idle_states(drv, idle_states_node);
-
-       ret = init_idle_states(drv, state_nodes, start_idx, init_nodes);
-
-       of_node_put(idle_states_node);
-
-       return ret;
-}
diff --git a/drivers/cpuidle/of_idle_states.h b/drivers/cpuidle/of_idle_states.h
deleted file mode 100644 (file)
index 049f94f..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef __OF_IDLE_STATES
-#define __OF_IDLE_STATES
-
-int __init of_init_idle_driver(struct cpuidle_driver *drv,
-                              struct device_node *state_nodes[],
-                              unsigned int start_idx,
-                              bool init_nodes);
-#endif