diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-09-25 16:18:45 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-09-25 16:18:45 -0400 |
commit | eeb1aa5f78caf8d5ec1ef651c09bde34370321d8 (patch) | |
tree | cc641b7dad3bff3bf1527a99c5d88d1f23d0a563 /drivers/cpuidle | |
parent | 9133664097dd4aca0bed4882a86f0bfccbf07e53 (diff) | |
parent | d2e5c871ed8a250f7ee1fe34dd52ed5757363fba (diff) |
Merge branch 'cpuidle/3.18' of https://git.linaro.org/people/daniel.lezcano/linux into pm-cpuidle
Pull ARM cpuidle changes for v3.18 from Daniel Lezcano:
"this pull request contains the following changes:
* Lorenzo Pieralisi implemented a framework to initialize the ARM
cpuidle drivers with the DT. As an example, it provided a couple of
drivers using it: arm64 and big little. The former one is a new
driver while the latter is a change. There was also a patch for
Exynos allowing to use this framework but as it depends on a change
in Samsung's tree, I postponed this patch until the change is visible
after the merge. The set of changes depends on some other changes
made in the ARM64 tree, for this reason a shared branch is used. This
is why there is a merge from arm64 in my pull request. I believe we
already used this procedure.
* Kevin Hilman added the compatible string for the exynos 5800 in the DT"
* 'cpuidle/3.18' of https://git.linaro.org/people/daniel.lezcano/linux:
drivers: cpuidle: initialize big.LITTLE driver through DT
drivers: cpuidle: CPU idle ARM64 driver
drivers: cpuidle: implement DT based idle states infrastructure
cpuidle: big.LITTLE: add Exynos5800 compatible string
arm64: add PSCI CPU_SUSPEND based cpu_suspend support
arm64: kernel: introduce cpu_init_idle CPU operation
arm64: kernel: refactor the CPU suspend API for retention states
Documentation: arm: define DT idle states bindings
Diffstat (limited to 'drivers/cpuidle')
-rw-r--r-- | drivers/cpuidle/Kconfig | 8 | ||||
-rw-r--r-- | drivers/cpuidle/Kconfig.arm | 1 | ||||
-rw-r--r-- | drivers/cpuidle/Kconfig.arm64 | 14 | ||||
-rw-r--r-- | drivers/cpuidle/Makefile | 5 | ||||
-rw-r--r-- | drivers/cpuidle/cpuidle-arm64.c | 133 | ||||
-rw-r--r-- | drivers/cpuidle/cpuidle-big_little.c | 20 | ||||
-rw-r--r-- | drivers/cpuidle/dt_idle_states.c | 213 | ||||
-rw-r--r-- | drivers/cpuidle/dt_idle_states.h | 7 |
8 files changed, 401 insertions, 0 deletions
diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig index 32748c36c477..c5029c1209b4 100644 --- a/drivers/cpuidle/Kconfig +++ b/drivers/cpuidle/Kconfig | |||
@@ -25,11 +25,19 @@ config CPU_IDLE_GOV_MENU | |||
25 | bool "Menu governor (for tickless system)" | 25 | bool "Menu governor (for tickless system)" |
26 | default y | 26 | default y |
27 | 27 | ||
28 | config DT_IDLE_STATES | ||
29 | bool | ||
30 | |||
28 | menu "ARM CPU Idle Drivers" | 31 | menu "ARM CPU Idle Drivers" |
29 | depends on ARM | 32 | depends on ARM |
30 | source "drivers/cpuidle/Kconfig.arm" | 33 | source "drivers/cpuidle/Kconfig.arm" |
31 | endmenu | 34 | endmenu |
32 | 35 | ||
36 | menu "ARM64 CPU Idle Drivers" | ||
37 | depends on ARM64 | ||
38 | source "drivers/cpuidle/Kconfig.arm64" | ||
39 | endmenu | ||
40 | |||
33 | menu "MIPS CPU Idle Drivers" | 41 | menu "MIPS CPU Idle Drivers" |
34 | depends on MIPS | 42 | depends on MIPS |
35 | source "drivers/cpuidle/Kconfig.mips" | 43 | source "drivers/cpuidle/Kconfig.mips" |
diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm index 38cff69ffe06..e339c7f2c2b7 100644 --- a/drivers/cpuidle/Kconfig.arm +++ b/drivers/cpuidle/Kconfig.arm | |||
@@ -7,6 +7,7 @@ config ARM_BIG_LITTLE_CPUIDLE | |||
7 | depends on MCPM | 7 | depends on MCPM |
8 | select ARM_CPU_SUSPEND | 8 | select ARM_CPU_SUSPEND |
9 | select CPU_IDLE_MULTIPLE_DRIVERS | 9 | select CPU_IDLE_MULTIPLE_DRIVERS |
10 | select DT_IDLE_STATES | ||
10 | help | 11 | help |
11 | Select this option to enable CPU idle driver for big.LITTLE based | 12 | Select this option to enable CPU idle driver for big.LITTLE based |
12 | ARM systems. Driver manages CPUs coordination through MCPM and | 13 | ARM systems. Driver manages CPUs coordination through MCPM and |
diff --git a/drivers/cpuidle/Kconfig.arm64 b/drivers/cpuidle/Kconfig.arm64 new file mode 100644 index 000000000000..d0a08ed1b2ee --- /dev/null +++ b/drivers/cpuidle/Kconfig.arm64 | |||
@@ -0,0 +1,14 @@ | |||
1 | # | ||
2 | # ARM64 CPU Idle drivers | ||
3 | # | ||
4 | |||
5 | config ARM64_CPUIDLE | ||
6 | bool "Generic ARM64 CPU idle Driver" | ||
7 | select ARM64_CPU_SUSPEND | ||
8 | select DT_IDLE_STATES | ||
9 | help | ||
10 | Select this to enable generic cpuidle driver for ARM64. | ||
11 | It provides a generic idle driver whose idle states are configured | ||
12 | at run-time through DT nodes. The CPUidle suspend backend is | ||
13 | initialized by calling the CPU operations init idle hook | ||
14 | provided by architecture code. | ||
diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index 11edb31c55e9..4d177b916f75 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile | |||
@@ -4,6 +4,7 @@ | |||
4 | 4 | ||
5 | obj-y += cpuidle.o driver.o governor.o sysfs.o governors/ | 5 | obj-y += cpuidle.o driver.o governor.o sysfs.o governors/ |
6 | obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o | 6 | obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o |
7 | obj-$(CONFIG_DT_IDLE_STATES) += dt_idle_states.o | ||
7 | 8 | ||
8 | ################################################################################## | 9 | ################################################################################## |
9 | # ARM SoC drivers | 10 | # ARM SoC drivers |
@@ -22,6 +23,10 @@ obj-$(CONFIG_ARM_EXYNOS_CPUIDLE) += cpuidle-exynos.o | |||
22 | obj-$(CONFIG_MIPS_CPS_CPUIDLE) += cpuidle-cps.o | 23 | obj-$(CONFIG_MIPS_CPS_CPUIDLE) += cpuidle-cps.o |
23 | 24 | ||
24 | ############################################################################### | 25 | ############################################################################### |
26 | # ARM64 drivers | ||
27 | obj-$(CONFIG_ARM64_CPUIDLE) += cpuidle-arm64.o | ||
28 | |||
29 | ############################################################################### | ||
25 | # POWERPC drivers | 30 | # POWERPC drivers |
26 | obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o | 31 | obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o |
27 | obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o | 32 | obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o |
diff --git a/drivers/cpuidle/cpuidle-arm64.c b/drivers/cpuidle/cpuidle-arm64.c new file mode 100644 index 000000000000..50997ea942fc --- /dev/null +++ b/drivers/cpuidle/cpuidle-arm64.c | |||
@@ -0,0 +1,133 @@ | |||
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 | #define pr_fmt(fmt) "CPUidle arm64: " fmt | ||
13 | |||
14 | #include <linux/cpuidle.h> | ||
15 | #include <linux/cpumask.h> | ||
16 | #include <linux/cpu_pm.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/of.h> | ||
20 | |||
21 | #include <asm/cpuidle.h> | ||
22 | #include <asm/suspend.h> | ||
23 | |||
24 | #include "dt_idle_states.h" | ||
25 | |||
26 | /* | ||
27 | * arm64_enter_idle_state - Programs CPU to enter the specified state | ||
28 | * | ||
29 | * dev: cpuidle device | ||
30 | * drv: cpuidle driver | ||
31 | * idx: state index | ||
32 | * | ||
33 | * Called from the CPUidle framework to program the device to the | ||
34 | * specified target state selected by the governor. | ||
35 | */ | ||
36 | static int arm64_enter_idle_state(struct cpuidle_device *dev, | ||
37 | struct cpuidle_driver *drv, int idx) | ||
38 | { | ||
39 | int ret; | ||
40 | |||
41 | if (!idx) { | ||
42 | cpu_do_idle(); | ||
43 | return idx; | ||
44 | } | ||
45 | |||
46 | ret = cpu_pm_enter(); | ||
47 | if (!ret) { | ||
48 | /* | ||
49 | * Pass idle state index to cpu_suspend which in turn will | ||
50 | * call the CPU ops suspend protocol with idle index as a | ||
51 | * parameter. | ||
52 | */ | ||
53 | ret = cpu_suspend(idx); | ||
54 | |||
55 | cpu_pm_exit(); | ||
56 | } | ||
57 | |||
58 | return ret ? -1 : idx; | ||
59 | } | ||
60 | |||
61 | static struct cpuidle_driver arm64_idle_driver = { | ||
62 | .name = "arm64_idle", | ||
63 | .owner = THIS_MODULE, | ||
64 | /* | ||
65 | * State at index 0 is standby wfi and considered standard | ||
66 | * on all ARM platforms. If in some platforms simple wfi | ||
67 | * can't be used as "state 0", DT bindings must be implemented | ||
68 | * to work around this issue and allow installing a special | ||
69 | * handler for idle state index 0. | ||
70 | */ | ||
71 | .states[0] = { | ||
72 | .enter = arm64_enter_idle_state, | ||
73 | .exit_latency = 1, | ||
74 | .target_residency = 1, | ||
75 | .power_usage = UINT_MAX, | ||
76 | .flags = CPUIDLE_FLAG_TIME_VALID, | ||
77 | .name = "WFI", | ||
78 | .desc = "ARM64 WFI", | ||
79 | } | ||
80 | }; | ||
81 | |||
82 | static const struct of_device_id arm64_idle_state_match[] __initconst = { | ||
83 | { .compatible = "arm,idle-state", | ||
84 | .data = arm64_enter_idle_state }, | ||
85 | { }, | ||
86 | }; | ||
87 | |||
88 | /* | ||
89 | * arm64_idle_init | ||
90 | * | ||
91 | * Registers the arm64 specific cpuidle driver with the cpuidle | ||
92 | * framework. It relies on core code to parse the idle states | ||
93 | * and initialize them using driver data structures accordingly. | ||
94 | */ | ||
95 | static int __init arm64_idle_init(void) | ||
96 | { | ||
97 | int cpu, ret; | ||
98 | struct cpuidle_driver *drv = &arm64_idle_driver; | ||
99 | |||
100 | /* | ||
101 | * Initialize idle states data, starting at index 1. | ||
102 | * This driver is DT only, if no DT idle states are detected (ret == 0) | ||
103 | * let the driver initialization fail accordingly since there is no | ||
104 | * reason to initialize the idle driver if only wfi is supported. | ||
105 | */ | ||
106 | ret = dt_init_idle_driver(drv, arm64_idle_state_match, 1); | ||
107 | if (ret <= 0) { | ||
108 | if (ret) | ||
109 | pr_err("failed to initialize idle states\n"); | ||
110 | return ret ? : -ENODEV; | ||
111 | } | ||
112 | |||
113 | /* | ||
114 | * Call arch CPU operations in order to initialize | ||
115 | * idle states suspend back-end specific data | ||
116 | */ | ||
117 | for_each_possible_cpu(cpu) { | ||
118 | ret = cpu_init_idle(cpu); | ||
119 | if (ret) { | ||
120 | pr_err("CPU %d failed to init idle CPU ops\n", cpu); | ||
121 | return ret; | ||
122 | } | ||
123 | } | ||
124 | |||
125 | ret = cpuidle_register(drv, NULL); | ||
126 | if (ret) { | ||
127 | pr_err("failed to register cpuidle driver\n"); | ||
128 | return ret; | ||
129 | } | ||
130 | |||
131 | return 0; | ||
132 | } | ||
133 | device_initcall(arm64_idle_init); | ||
diff --git a/drivers/cpuidle/cpuidle-big_little.c b/drivers/cpuidle/cpuidle-big_little.c index ef94c3b81f18..fbc00a1d3c48 100644 --- a/drivers/cpuidle/cpuidle-big_little.c +++ b/drivers/cpuidle/cpuidle-big_little.c | |||
@@ -24,6 +24,8 @@ | |||
24 | #include <asm/smp_plat.h> | 24 | #include <asm/smp_plat.h> |
25 | #include <asm/suspend.h> | 25 | #include <asm/suspend.h> |
26 | 26 | ||
27 | #include "dt_idle_states.h" | ||
28 | |||
27 | static int bl_enter_powerdown(struct cpuidle_device *dev, | 29 | static int bl_enter_powerdown(struct cpuidle_device *dev, |
28 | struct cpuidle_driver *drv, int idx); | 30 | struct cpuidle_driver *drv, int idx); |
29 | 31 | ||
@@ -73,6 +75,12 @@ static struct cpuidle_driver bl_idle_little_driver = { | |||
73 | .state_count = 2, | 75 | .state_count = 2, |
74 | }; | 76 | }; |
75 | 77 | ||
78 | static const struct of_device_id bl_idle_state_match[] __initconst = { | ||
79 | { .compatible = "arm,idle-state", | ||
80 | .data = bl_enter_powerdown }, | ||
81 | { }, | ||
82 | }; | ||
83 | |||
76 | static struct cpuidle_driver bl_idle_big_driver = { | 84 | static struct cpuidle_driver bl_idle_big_driver = { |
77 | .name = "big_idle", | 85 | .name = "big_idle", |
78 | .owner = THIS_MODULE, | 86 | .owner = THIS_MODULE, |
@@ -159,6 +167,7 @@ static int __init bl_idle_driver_init(struct cpuidle_driver *drv, int part_id) | |||
159 | static const struct of_device_id compatible_machine_match[] = { | 167 | static const struct of_device_id compatible_machine_match[] = { |
160 | { .compatible = "arm,vexpress,v2p-ca15_a7" }, | 168 | { .compatible = "arm,vexpress,v2p-ca15_a7" }, |
161 | { .compatible = "samsung,exynos5420" }, | 169 | { .compatible = "samsung,exynos5420" }, |
170 | { .compatible = "samsung,exynos5800" }, | ||
162 | {}, | 171 | {}, |
163 | }; | 172 | }; |
164 | 173 | ||
@@ -190,6 +199,17 @@ static int __init bl_idle_init(void) | |||
190 | if (ret) | 199 | if (ret) |
191 | goto out_uninit_little; | 200 | goto out_uninit_little; |
192 | 201 | ||
202 | /* Start at index 1, index 0 standard WFI */ | ||
203 | ret = dt_init_idle_driver(&bl_idle_big_driver, bl_idle_state_match, 1); | ||
204 | if (ret < 0) | ||
205 | goto out_uninit_big; | ||
206 | |||
207 | /* Start at index 1, index 0 standard WFI */ | ||
208 | ret = dt_init_idle_driver(&bl_idle_little_driver, | ||
209 | bl_idle_state_match, 1); | ||
210 | if (ret < 0) | ||
211 | goto out_uninit_big; | ||
212 | |||
193 | ret = cpuidle_register(&bl_idle_little_driver, NULL); | 213 | ret = cpuidle_register(&bl_idle_little_driver, NULL); |
194 | if (ret) | 214 | if (ret) |
195 | goto out_uninit_big; | 215 | goto out_uninit_big; |
diff --git a/drivers/cpuidle/dt_idle_states.c b/drivers/cpuidle/dt_idle_states.c new file mode 100644 index 000000000000..52f4d11bbf3f --- /dev/null +++ b/drivers/cpuidle/dt_idle_states.c | |||
@@ -0,0 +1,213 @@ | |||
1 | /* | ||
2 | * DT idle states parsing code. | ||
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 | #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 | |||
24 | static 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 | |||
31 | match_id = of_match_node(matches, state_node); | ||
32 | if (!match_id) | ||
33 | return -ENODEV; | ||
34 | /* | ||
35 | * CPUidle drivers are expected to initialize the const void *data | ||
36 | * pointer of the passed in struct of_device_id array to the idle | ||
37 | * state enter function. | ||
38 | */ | ||
39 | idle_state->enter = match_id->data; | ||
40 | |||
41 | err = of_property_read_u32(state_node, "wakeup-latency-us", | ||
42 | &idle_state->exit_latency); | ||
43 | if (err) { | ||
44 | u32 entry_latency, exit_latency; | ||
45 | |||
46 | err = of_property_read_u32(state_node, "entry-latency-us", | ||
47 | &entry_latency); | ||
48 | if (err) { | ||
49 | pr_debug(" * %s missing entry-latency-us property\n", | ||
50 | state_node->full_name); | ||
51 | return -EINVAL; | ||
52 | } | ||
53 | |||
54 | err = of_property_read_u32(state_node, "exit-latency-us", | ||
55 | &exit_latency); | ||
56 | if (err) { | ||
57 | pr_debug(" * %s missing exit-latency-us property\n", | ||
58 | state_node->full_name); | ||
59 | return -EINVAL; | ||
60 | } | ||
61 | /* | ||
62 | * If wakeup-latency-us is missing, default to entry+exit | ||
63 | * latencies as defined in idle states bindings | ||
64 | */ | ||
65 | idle_state->exit_latency = entry_latency + exit_latency; | ||
66 | } | ||
67 | |||
68 | err = of_property_read_u32(state_node, "min-residency-us", | ||
69 | &idle_state->target_residency); | ||
70 | if (err) { | ||
71 | pr_debug(" * %s missing min-residency-us property\n", | ||
72 | state_node->full_name); | ||
73 | return -EINVAL; | ||
74 | } | ||
75 | |||
76 | idle_state->flags = CPUIDLE_FLAG_TIME_VALID; | ||
77 | if (of_property_read_bool(state_node, "local-timer-stop")) | ||
78 | idle_state->flags |= CPUIDLE_FLAG_TIMER_STOP; | ||
79 | /* | ||
80 | * TODO: | ||
81 | * replace with kstrdup and pointer assignment when name | ||
82 | * and desc become string pointers | ||
83 | */ | ||
84 | strncpy(idle_state->name, state_node->name, CPUIDLE_NAME_LEN - 1); | ||
85 | strncpy(idle_state->desc, state_node->name, CPUIDLE_DESC_LEN - 1); | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | /* | ||
90 | * Check that the idle state is uniform across all CPUs in the CPUidle driver | ||
91 | * cpumask | ||
92 | */ | ||
93 | static bool idle_state_valid(struct device_node *state_node, unsigned int idx, | ||
94 | const cpumask_t *cpumask) | ||
95 | { | ||
96 | int cpu; | ||
97 | struct device_node *cpu_node, *curr_state_node; | ||
98 | bool valid = true; | ||
99 | |||
100 | /* | ||
101 | * Compare idle state phandles for index idx on all CPUs in the | ||
102 | * CPUidle driver cpumask. Start from next logical cpu following | ||
103 | * cpumask_first(cpumask) since that's the CPU state_node was | ||
104 | * retrieved from. If a mismatch is found bail out straight | ||
105 | * away since we certainly hit a firmware misconfiguration. | ||
106 | */ | ||
107 | for (cpu = cpumask_next(cpumask_first(cpumask), cpumask); | ||
108 | cpu < nr_cpu_ids; cpu = cpumask_next(cpu, cpumask)) { | ||
109 | cpu_node = of_cpu_device_node_get(cpu); | ||
110 | curr_state_node = of_parse_phandle(cpu_node, "cpu-idle-states", | ||
111 | idx); | ||
112 | if (state_node != curr_state_node) | ||
113 | valid = false; | ||
114 | |||
115 | of_node_put(curr_state_node); | ||
116 | of_node_put(cpu_node); | ||
117 | if (!valid) | ||
118 | break; | ||
119 | } | ||
120 | |||
121 | return valid; | ||
122 | } | ||
123 | |||
124 | /** | ||
125 | * dt_init_idle_driver() - Parse the DT idle states and initialize the | ||
126 | * idle driver states array | ||
127 | * @drv: Pointer to CPU idle driver to be initialized | ||
128 | * @matches: Array of of_device_id match structures to search in for | ||
129 | * compatible idle state nodes. The data pointer for each valid | ||
130 | * struct of_device_id entry in the matches array must point to | ||
131 | * a function with the following signature, that corresponds to | ||
132 | * the CPUidle state enter function signature: | ||
133 | * | ||
134 | * int (*)(struct cpuidle_device *dev, | ||
135 | * struct cpuidle_driver *drv, | ||
136 | * int index); | ||
137 | * | ||
138 | * @start_idx: First idle state index to be initialized | ||
139 | * | ||
140 | * If DT idle states are detected and are valid the state count and states | ||
141 | * array entries in the cpuidle driver are initialized accordingly starting | ||
142 | * from index start_idx. | ||
143 | * | ||
144 | * Return: number of valid DT idle states parsed, <0 on failure | ||
145 | */ | ||
146 | int dt_init_idle_driver(struct cpuidle_driver *drv, | ||
147 | const struct of_device_id *matches, | ||
148 | unsigned int start_idx) | ||
149 | { | ||
150 | struct cpuidle_state *idle_state; | ||
151 | struct device_node *state_node, *cpu_node; | ||
152 | int i, err = 0; | ||
153 | const cpumask_t *cpumask; | ||
154 | unsigned int state_idx = start_idx; | ||
155 | |||
156 | if (state_idx >= CPUIDLE_STATE_MAX) | ||
157 | return -EINVAL; | ||
158 | /* | ||
159 | * We get the idle states for the first logical cpu in the | ||
160 | * driver mask (or cpu_possible_mask if the driver cpumask is not set) | ||
161 | * and we check through idle_state_valid() if they are uniform | ||
162 | * across CPUs, otherwise we hit a firmware misconfiguration. | ||
163 | */ | ||
164 | cpumask = drv->cpumask ? : cpu_possible_mask; | ||
165 | cpu_node = of_cpu_device_node_get(cpumask_first(cpumask)); | ||
166 | |||
167 | for (i = 0; ; i++) { | ||
168 | state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i); | ||
169 | if (!state_node) | ||
170 | break; | ||
171 | |||
172 | if (!idle_state_valid(state_node, i, cpumask)) { | ||
173 | pr_warn("%s idle state not valid, bailing out\n", | ||
174 | state_node->full_name); | ||
175 | err = -EINVAL; | ||
176 | break; | ||
177 | } | ||
178 | |||
179 | if (state_idx == CPUIDLE_STATE_MAX) { | ||
180 | pr_warn("State index reached static CPU idle driver states array size\n"); | ||
181 | break; | ||
182 | } | ||
183 | |||
184 | idle_state = &drv->states[state_idx++]; | ||
185 | err = init_state_node(idle_state, matches, state_node); | ||
186 | if (err) { | ||
187 | pr_err("Parsing idle state node %s failed with err %d\n", | ||
188 | state_node->full_name, err); | ||
189 | err = -EINVAL; | ||
190 | break; | ||
191 | } | ||
192 | of_node_put(state_node); | ||
193 | } | ||
194 | |||
195 | of_node_put(state_node); | ||
196 | of_node_put(cpu_node); | ||
197 | if (err) | ||
198 | return err; | ||
199 | /* | ||
200 | * Update the driver state count only if some valid DT idle states | ||
201 | * were detected | ||
202 | */ | ||
203 | if (i) | ||
204 | drv->state_count = state_idx; | ||
205 | |||
206 | /* | ||
207 | * Return the number of present and valid DT idle states, which can | ||
208 | * also be 0 on platforms with missing DT idle states or legacy DT | ||
209 | * configuration predating the DT idle states bindings. | ||
210 | */ | ||
211 | return i; | ||
212 | } | ||
213 | 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 index 000000000000..4818134bc65b --- /dev/null +++ b/drivers/cpuidle/dt_idle_states.h | |||
@@ -0,0 +1,7 @@ | |||
1 | #ifndef __DT_IDLE_STATES | ||
2 | #define __DT_IDLE_STATES | ||
3 | |||
4 | int dt_init_idle_driver(struct cpuidle_driver *drv, | ||
5 | const struct of_device_id *matches, | ||
6 | unsigned int start_idx); | ||
7 | #endif | ||