1
2
3
4
5
6
7
8
9
10
11
12#define pr_fmt(fmt) "DT idle-states: " fmt
13
14#include <linux/cpuidle.h>
15#include <linux/cpumask.h>
16#include <linux/errno.h>
17#include <linux/kernel.h>
18#include <linux/module.h>
19#include <linux/of.h>
20#include <linux/of_device.h>
21
22#include "dt_idle_states.h"
23
24static int init_state_node(struct cpuidle_state *idle_state,
25 const struct of_device_id *matches,
26 struct device_node *state_node)
27{
28 int err;
29 const struct of_device_id *match_id;
30 const char *desc;
31
32 match_id = of_match_node(matches, state_node);
33 if (!match_id)
34 return -ENODEV;
35
36
37
38
39
40 idle_state->enter = match_id->data;
41
42 err = of_property_read_u32(state_node, "wakeup-latency-us",
43 &idle_state->exit_latency);
44 if (err) {
45 u32 entry_latency, exit_latency;
46
47 err = of_property_read_u32(state_node, "entry-latency-us",
48 &entry_latency);
49 if (err) {
50 pr_debug(" * %s missing entry-latency-us property\n",
51 state_node->full_name);
52 return -EINVAL;
53 }
54
55 err = of_property_read_u32(state_node, "exit-latency-us",
56 &exit_latency);
57 if (err) {
58 pr_debug(" * %s missing exit-latency-us property\n",
59 state_node->full_name);
60 return -EINVAL;
61 }
62
63
64
65
66 idle_state->exit_latency = entry_latency + exit_latency;
67 }
68
69 err = of_property_read_u32(state_node, "min-residency-us",
70 &idle_state->target_residency);
71 if (err) {
72 pr_debug(" * %s missing min-residency-us property\n",
73 state_node->full_name);
74 return -EINVAL;
75 }
76
77 err = of_property_read_string(state_node, "idle-state-name", &desc);
78 if (err)
79 desc = state_node->name;
80
81 idle_state->flags = 0;
82 if (of_property_read_bool(state_node, "local-timer-stop"))
83 idle_state->flags |= CPUIDLE_FLAG_TIMER_STOP;
84
85
86
87
88
89 strncpy(idle_state->name, state_node->name, CPUIDLE_NAME_LEN - 1);
90 strncpy(idle_state->desc, desc, CPUIDLE_DESC_LEN - 1);
91 return 0;
92}
93
94
95
96
97
98static bool idle_state_valid(struct device_node *state_node, unsigned int idx,
99 const cpumask_t *cpumask)
100{
101 int cpu;
102 struct device_node *cpu_node, *curr_state_node;
103 bool valid = true;
104
105
106
107
108
109
110
111
112 for (cpu = cpumask_next(cpumask_first(cpumask), cpumask);
113 cpu < nr_cpu_ids; cpu = cpumask_next(cpu, cpumask)) {
114 cpu_node = of_cpu_device_node_get(cpu);
115 curr_state_node = of_parse_phandle(cpu_node, "cpu-idle-states",
116 idx);
117 if (state_node != curr_state_node)
118 valid = false;
119
120 of_node_put(curr_state_node);
121 of_node_put(cpu_node);
122 if (!valid)
123 break;
124 }
125
126 return valid;
127}
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151int dt_init_idle_driver(struct cpuidle_driver *drv,
152 const struct of_device_id *matches,
153 unsigned int start_idx)
154{
155 struct cpuidle_state *idle_state;
156 struct device_node *state_node, *cpu_node;
157 int i, err = 0;
158 const cpumask_t *cpumask;
159 unsigned int state_idx = start_idx;
160
161 if (state_idx >= CPUIDLE_STATE_MAX)
162 return -EINVAL;
163
164
165
166
167
168
169 cpumask = drv->cpumask ? : cpu_possible_mask;
170 cpu_node = of_cpu_device_node_get(cpumask_first(cpumask));
171
172 for (i = 0; ; i++) {
173 state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
174 if (!state_node)
175 break;
176
177 if (!of_device_is_available(state_node))
178 continue;
179
180 if (!idle_state_valid(state_node, i, cpumask)) {
181 pr_warn("%s idle state not valid, bailing out\n",
182 state_node->full_name);
183 err = -EINVAL;
184 break;
185 }
186
187 if (state_idx == CPUIDLE_STATE_MAX) {
188 pr_warn("State index reached static CPU idle driver states array size\n");
189 break;
190 }
191
192 idle_state = &drv->states[state_idx++];
193 err = init_state_node(idle_state, matches, state_node);
194 if (err) {
195 pr_err("Parsing idle state node %s failed with err %d\n",
196 state_node->full_name, err);
197 err = -EINVAL;
198 break;
199 }
200 of_node_put(state_node);
201 }
202
203 of_node_put(state_node);
204 of_node_put(cpu_node);
205 if (err)
206 return err;
207
208
209
210
211 if (i)
212 drv->state_count = state_idx;
213
214
215
216
217
218
219 return i;
220}
221EXPORT_SYMBOL_GPL(dt_init_idle_driver);
222