Merge remote-tracking branch 'lsk/v3.10/topic/juno' into linux-linaro-lsk
[firefly-linux-kernel-4.4.55.git] / drivers / cpuidle / cpuidle-arm64.c
1 /*
2  * ARM64 generic CPU idle driver.
3  *
4  * Copyright (C) 2014 ARM Ltd.
5  * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11
12 #include <linux/cpuidle.h>
13 #include <linux/cpumask.h>
14 #include <linux/cpu_pm.h>
15 #include <linux/kernel.h>
16 #include <linux/module.h>
17 #include <linux/of.h>
18
19 #include <asm/psci.h>
20 #include <asm/suspend.h>
21
22 #include "of_idle_states.h"
23
24 typedef int (*suspend_init_fn)(struct cpuidle_driver *,
25                                struct device_node *[]);
26
27 struct cpu_suspend_ops {
28         const char *id;
29         suspend_init_fn init_fn;
30 };
31
32 static const struct cpu_suspend_ops suspend_operations[] __initconst = {
33         {"arm,psci", psci_dt_register_idle_states},
34         {}
35 };
36
37 static __init const struct cpu_suspend_ops *get_suspend_ops(const char *str)
38 {
39         int i;
40
41         if (!str)
42                 return NULL;
43
44         for (i = 0; suspend_operations[i].id; i++)
45                 if (!strcmp(suspend_operations[i].id, str))
46                         return &suspend_operations[i];
47
48         return NULL;
49 }
50
51 /*
52  * arm_enter_idle_state - Programs CPU to enter the specified state
53  *
54  * @dev: cpuidle device
55  * @drv: cpuidle driver
56  * @idx: state index
57  *
58  * Called from the CPUidle framework to program the device to the
59  * specified target state selected by the governor.
60  */
61 static int arm_enter_idle_state(struct cpuidle_device *dev,
62                                 struct cpuidle_driver *drv, int idx)
63 {
64         int ret;
65
66         if (!idx) {
67                 cpu_do_idle();
68                 return idx;
69         }
70
71         cpu_pm_enter();
72         /*
73          * Pass idle state index to cpu_suspend which in turn will call
74          * the CPU ops suspend protocol with idle index as a parameter.
75          *
76          * Some states would not require context to be saved and flushed
77          * to DRAM, so calling cpu_suspend would not be stricly necessary.
78          * When power domains specifications for ARM CPUs are finalized then
79          * this code can be optimized to prevent saving registers if not
80          * needed.
81          */
82         ret = cpu_suspend(idx);
83
84         cpu_pm_exit();
85
86         return ret ? -1 : idx;
87 }
88
89 struct cpuidle_driver arm64_idle_driver = {
90         .name = "arm64_idle",
91         .owner = THIS_MODULE,
92 };
93
94 static struct device_node *state_nodes[CPUIDLE_STATE_MAX] __initdata;
95
96 /*
97  * arm64_idle_init
98  *
99  * Registers the arm64 specific cpuidle driver with the cpuidle
100  * framework. It relies on core code to parse the idle states
101  * and initialize them using driver data structures accordingly.
102  */
103 static int __init arm64_idle_init(void)
104 {
105         int i, ret;
106         const char *entry_method;
107         struct device_node *idle_states_node;
108         const struct cpu_suspend_ops *suspend_init;
109         struct cpuidle_driver *drv = &arm64_idle_driver;
110
111         idle_states_node = of_find_node_by_path("/cpus/idle-states");
112         if (!idle_states_node)
113                 return -ENOENT;
114
115         if (of_property_read_string(idle_states_node, "entry-method",
116                                     &entry_method)) {
117                 pr_warn(" * %s missing entry-method property\n",
118                             idle_states_node->full_name);
119                 of_node_put(idle_states_node);
120                 return -EOPNOTSUPP;
121         }
122
123         suspend_init = get_suspend_ops(entry_method);
124         if (!suspend_init) {
125                 pr_warn("Missing suspend initializer\n");
126                 of_node_put(idle_states_node);
127                 return -EOPNOTSUPP;
128         }
129
130         /*
131          * State at index 0 is standby wfi and considered standard
132          * on all ARM platforms. If in some platforms simple wfi
133          * can't be used as "state 0", DT bindings must be implemented
134          * to work around this issue and allow installing a special
135          * handler for idle state index 0.
136          */
137         drv->states[0].exit_latency = 1;
138         drv->states[0].target_residency = 1;
139         drv->states[0].flags = CPUIDLE_FLAG_TIME_VALID;
140         strncpy(drv->states[0].name, "ARM WFI", CPUIDLE_NAME_LEN);
141         strncpy(drv->states[0].desc, "ARM WFI", CPUIDLE_DESC_LEN);
142
143         drv->cpumask = (struct cpumask *) cpu_possible_mask;
144         /*
145          * Start at index 1, request idle state nodes to be filled
146          */
147         ret = of_init_idle_driver(drv, state_nodes, 1, true);
148         if (ret)
149                 return ret;
150
151         if (suspend_init->init_fn(drv, state_nodes))
152                 return -EOPNOTSUPP;
153
154         for (i = 0; i < drv->state_count; i++)
155                 drv->states[i].enter = arm_enter_idle_state;
156
157         return cpuidle_register(drv, NULL);
158 }
159 device_initcall(arm64_idle_init);