aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/cpuidle
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-10-09 16:07:43 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-10-09 16:07:43 -0400
commitb528392669415dc1e53a047215e5ad6c2de879fc (patch)
treed19aa6e1464ef7c7d9f399ac8ec9a7707e5ba6b4 /drivers/cpuidle
parent80213c03c4151d900cf293ef0fc51f8d88495e14 (diff)
parent9f1a053296953c69d7f23511db9441290cb89e2c (diff)
Merge tag 'pm+acpi-3.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull ACPI and power management updates from Rafael Wysocki: "Features-wise, to me the most important this time is a rework of wakeup interrupts handling in the core that makes them work consistently across all of the available sleep states, including suspend-to-idle. Many thanks to Thomas Gleixner for his help with this work. Second is an update of the generic PM domains code that has been in need of some care for quite a while. Unused code is being removed, DT support is being added and domains are now going to be attached to devices in bus type code in analogy with the ACPI PM domain. The majority of work here was done by Ulf Hansson who also has been the most active developer this time. Apart from this we have a traditional ACPICA update, this time to upstream version 20140828 and a few ACPI wakeup interrupts handling patches on top of the general rework mentioned above. There also are several cpufreq commits including renaming the cpufreq-cpu0 driver to cpufreq-dt, as this is what implements generic DT-based cpufreq support, and a new DT-based idle states infrastructure for cpuidle. In addition to that, the ACPI LPSS driver is updated, ACPI support for Apple machines is improved, a few bugs are fixed and a few cleanups are made all over. Finally, the Adaptive Voltage Scaling (AVS) subsystem now has a tree maintained by Kevin Hilman that will be merged through the PM tree. Numbers-wise, the generic PM domains update takes the lead this time with 32 non-merge commits, second is cpufreq (15 commits) and the 3rd place goes to the wakeup interrupts handling rework (13 commits). Specifics: - Rework the handling of wakeup IRQs by the IRQ core such that all of them will be switched over to "wakeup" mode in suspend_device_irqs() and in that mode the first interrupt will abort system suspend in progress or wake up the system if already in suspend-to-idle (or equivalent) without executing any interrupt handlers. Among other things that eliminates the wakeup-related motivation to use the IRQF_NO_SUSPEND interrupt flag with interrupts which don't really need it and should not use it (Thomas Gleixner and Rafael Wysocki) - Switch over ACPI to handling wakeup interrupts with the help of the new mechanism introduced by the above IRQ core rework (Rafael Wysocki) - Rework the core generic PM domains code to eliminate code that's not used, add DT support and add a generic mechanism by which devices can be added to PM domains automatically during enumeration (Ulf Hansson, Geert Uytterhoeven and Tomasz Figa). - Add debugfs-based mechanics for debugging generic PM domains (Maciej Matraszek). - ACPICA update to upstream version 20140828. Included are updates related to the SRAT and GTDT tables and the _PSx methods are in the METHOD_NAME list now (Bob Moore and Hanjun Guo). - Add _OSI("Darwin") support to the ACPI core (unfortunately, that can't really be done in a straightforward way) to prevent Thunderbolt from being turned off on Apple systems after boot (or after resume from system suspend) and rework the ACPI Smart Battery Subsystem (SBS) driver to work correctly with Apple platforms (Matthew Garrett and Andreas Noever). - ACPI LPSS (Low-Power Subsystem) driver update cleaning up the code, adding support for 133MHz I2C source clock on Intel Baytrail to it and making it avoid using UART RTS override with Auto Flow Control (Heikki Krogerus). - ACPI backlight updates removing the video_set_use_native_backlight quirk which is not necessary any more, making the code check the list of output devices returned by the _DOD method to avoid creating acpi_video interfaces that won't work and adding a quirk for Lenovo Ideapad Z570 (Hans de Goede, Aaron Lu and Stepan Bujnak) - New Win8 ACPI OSI quirks for some Dell laptops (Edward Lin) - Assorted ACPI code cleanups (Fabian Frederick, Rasmus Villemoes, Sudip Mukherjee, Yijing Wang, and Zhang Rui) - cpufreq core updates and cleanups (Viresh Kumar, Preeti U Murthy, Rasmus Villemoes) - cpufreq driver updates: cpufreq-cpu0/cpufreq-dt (driver name change among other things), ppc-corenet, powernv (Viresh Kumar, Preeti U Murthy, Shilpasri G Bhat, Lucas Stach) - cpuidle support for DT-based idle states infrastructure, new ARM64 cpuidle driver, cpuidle core cleanups (Lorenzo Pieralisi, Rasmus Villemoes) - ARM big.LITTLE cpuidle driver updates: support for DT-based initialization and Exynos5800 compatible string (Lorenzo Pieralisi, Kevin Hilman) - Rework of the test_suspend kernel command line argument and a new trace event for console resume (Srinivas Pandruvada, Todd E Brandt) - Second attempt to optimize swsusp_free() (hibernation core) to make it avoid going through all PFNs which may be way too slow on some systems (Joerg Roedel) - devfreq updates (Paul Bolle, Punit Agrawal, Ãrjan Eide). - rockchip-io Adaptive Voltage Scaling (AVS) driver and AVS entry update in MAINTAINERS (Heiko Stübner, Kevin Hilman) - PM core fix related to clock management (Geert Uytterhoeven) - PM core's sysfs code cleanup (Johannes Berg)" * tag 'pm+acpi-3.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (105 commits) ACPI / fan: printk replacement PM / clk: Fix crash in clocks management code if !CONFIG_PM_RUNTIME PM / Domains: Rename cpu_data to cpuidle_data cpufreq: cpufreq-dt: fix potential double put of cpu OF node cpufreq: cpu0: rename driver and internals to 'cpufreq_dt' PM / hibernate: Iterate over set bits instead of PFNs in swsusp_free() cpufreq: ppc-corenet: remove duplicate update of cpu_data ACPI / sleep: Rework the handling of ACPI GPE wakeup from suspend-to-idle PM / sleep: Rename platform suspend/resume functions in suspend.c PM / sleep: Export dpm_suspend_late/noirq() and dpm_resume_early/noirq() ACPICA: Introduce acpi_enable_all_wakeup_gpes() ACPICA: Clear all non-wakeup GPEs in acpi_hw_enable_wakeup_gpe_block() ACPI / video: check _DOD list when creating backlight devices PM / Domains: Move dev_pm_domain_attach|detach() to pm_domain.h cpufreq: Replace strnicmp with strncasecmp cpufreq: powernv: Set the cpus to nominal frequency during reboot/kexec cpufreq: powernv: Set the pstate of the last hotplugged out cpu in policy->cpus to minimum cpufreq: Allow stop CPU callback to be used by all cpufreq drivers PM / devfreq: exynos: Enable building exynos PPMU as module PM / devfreq: Export helper functions for drivers ...
Diffstat (limited to 'drivers/cpuidle')
-rw-r--r--drivers/cpuidle/Kconfig8
-rw-r--r--drivers/cpuidle/Kconfig.arm1
-rw-r--r--drivers/cpuidle/Kconfig.arm6414
-rw-r--r--drivers/cpuidle/Makefile5
-rw-r--r--drivers/cpuidle/cpuidle-arm64.c133
-rw-r--r--drivers/cpuidle/cpuidle-big_little.c20
-rw-r--r--drivers/cpuidle/dt_idle_states.c213
-rw-r--r--drivers/cpuidle/dt_idle_states.h7
-rw-r--r--drivers/cpuidle/governor.c2
9 files changed, 402 insertions, 1 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
28config DT_IDLE_STATES
29 bool
30
28menu "ARM CPU Idle Drivers" 31menu "ARM CPU Idle Drivers"
29depends on ARM 32depends on ARM
30source "drivers/cpuidle/Kconfig.arm" 33source "drivers/cpuidle/Kconfig.arm"
31endmenu 34endmenu
32 35
36menu "ARM64 CPU Idle Drivers"
37depends on ARM64
38source "drivers/cpuidle/Kconfig.arm64"
39endmenu
40
33menu "MIPS CPU Idle Drivers" 41menu "MIPS CPU Idle Drivers"
34depends on MIPS 42depends on MIPS
35source "drivers/cpuidle/Kconfig.mips" 43source "drivers/cpuidle/Kconfig.mips"
diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm
index 58bcd0d166ec..8c16ab20fb15 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
5config 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
5obj-y += cpuidle.o driver.o governor.o sysfs.o governors/ 5obj-y += cpuidle.o driver.o governor.o sysfs.o governors/
6obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o 6obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
7obj-$(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
22obj-$(CONFIG_MIPS_CPS_CPUIDLE) += cpuidle-cps.o 23obj-$(CONFIG_MIPS_CPS_CPUIDLE) += cpuidle-cps.o
23 24
24############################################################################### 25###############################################################################
26# ARM64 drivers
27obj-$(CONFIG_ARM64_CPUIDLE) += cpuidle-arm64.o
28
29###############################################################################
25# POWERPC drivers 30# POWERPC drivers
26obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o 31obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o
27obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o 32obj-$(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 */
36static 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
61static 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
82static 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 */
95static 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}
133device_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
27static int bl_enter_powerdown(struct cpuidle_device *dev, 29static 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
78static const struct of_device_id bl_idle_state_match[] __initconst = {
79 { .compatible = "arm,idle-state",
80 .data = bl_enter_powerdown },
81 { },
82};
83
76static struct cpuidle_driver bl_idle_big_driver = { 84static 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)
159static const struct of_device_id compatible_machine_match[] = { 167static 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
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
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 */
93static 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 */
146int 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}
213EXPORT_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
4int dt_init_idle_driver(struct cpuidle_driver *drv,
5 const struct of_device_id *matches,
6 unsigned int start_idx);
7#endif
diff --git a/drivers/cpuidle/governor.c b/drivers/cpuidle/governor.c
index ca89412f5122..fb9f511cca23 100644
--- a/drivers/cpuidle/governor.c
+++ b/drivers/cpuidle/governor.c
@@ -28,7 +28,7 @@ static struct cpuidle_governor * __cpuidle_find_governor(const char *str)
28 struct cpuidle_governor *gov; 28 struct cpuidle_governor *gov;
29 29
30 list_for_each_entry(gov, &cpuidle_governors, governor_list) 30 list_for_each_entry(gov, &cpuidle_governors, governor_list)
31 if (!strnicmp(str, gov->name, CPUIDLE_NAME_LEN)) 31 if (!strncasecmp(str, gov->name, CPUIDLE_NAME_LEN))
32 return gov; 32 return gov;
33 33
34 return NULL; 34 return NULL;