Skip to content

Commit 2c2e6ec

Browse files
Deepthi Dharwarozbenh
authored andcommitted
powerpc/powernv/cpuidle: Back-end cpuidle driver for powernv platform.
Following patch ports the cpuidle framework for powernv platform and also implements a cpuidle back-end powernv idle driver calling on to power7_nap and snooze idle states. Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
1 parent 3fa8cad commit 2c2e6ec

File tree

4 files changed

+191
-1
lines changed

4 files changed

+191
-1
lines changed

arch/powerpc/platforms/powernv/setup.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <linux/of_fdt.h>
2727
#include <linux/interrupt.h>
2828
#include <linux/bug.h>
29+
#include <linux/cpuidle.h>
2930

3031
#include <asm/machdep.h>
3132
#include <asm/firmware.h>
@@ -216,6 +217,16 @@ static int __init pnv_probe(void)
216217
return 1;
217218
}
218219

220+
void powernv_idle(void)
221+
{
222+
/* Hook to cpuidle framework if available, else
223+
* call on default platform idle code
224+
*/
225+
if (cpuidle_idle_call()) {
226+
power7_idle();
227+
}
228+
}
229+
219230
define_machine(powernv) {
220231
.name = "PowerNV",
221232
.probe = pnv_probe,
@@ -225,7 +236,7 @@ define_machine(powernv) {
225236
.show_cpuinfo = pnv_show_cpuinfo,
226237
.progress = pnv_progress,
227238
.machine_shutdown = pnv_shutdown,
228-
.power_save = power7_idle,
239+
.power_save = powernv_idle,
229240
.calibrate_decr = generic_calibrate_decr,
230241
#ifdef CONFIG_KEXEC
231242
.kexec_cpu_down = pnv_kexec_cpu_down,

drivers/cpuidle/Kconfig.powerpc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,12 @@ config PSERIES_CPUIDLE
99
help
1010
Select this option to enable processor idle state management
1111
through cpuidle subsystem.
12+
13+
config POWERNV_CPUIDLE
14+
bool "Cpuidle driver for powernv platforms"
15+
depends on CPU_IDLE
16+
depends on PPC_POWERNV
17+
default y
18+
help
19+
Select this option to enable processor idle state management
20+
through cpuidle subsystem.

drivers/cpuidle/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ obj-$(CONFIG_ARM_AT91_CPUIDLE) += cpuidle-at91.o
1717
###############################################################################
1818
# POWERPC drivers
1919
obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o
20+
obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o

drivers/cpuidle/cpuidle-powernv.c

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/*
2+
* cpuidle-powernv - idle state cpuidle driver.
3+
* Adapted from drivers/cpuidle/cpuidle-pseries
4+
*
5+
*/
6+
7+
#include <linux/kernel.h>
8+
#include <linux/module.h>
9+
#include <linux/init.h>
10+
#include <linux/moduleparam.h>
11+
#include <linux/cpuidle.h>
12+
#include <linux/cpu.h>
13+
#include <linux/notifier.h>
14+
15+
#include <asm/machdep.h>
16+
#include <asm/firmware.h>
17+
18+
struct cpuidle_driver powernv_idle_driver = {
19+
.name = "powernv_idle",
20+
.owner = THIS_MODULE,
21+
};
22+
23+
static int max_idle_state;
24+
static struct cpuidle_state *cpuidle_state_table;
25+
26+
static int snooze_loop(struct cpuidle_device *dev,
27+
struct cpuidle_driver *drv,
28+
int index)
29+
{
30+
local_irq_enable();
31+
set_thread_flag(TIF_POLLING_NRFLAG);
32+
33+
while (!need_resched()) {
34+
HMT_low();
35+
HMT_very_low();
36+
}
37+
38+
HMT_medium();
39+
clear_thread_flag(TIF_POLLING_NRFLAG);
40+
smp_mb();
41+
return index;
42+
}
43+
44+
static int nap_loop(struct cpuidle_device *dev,
45+
struct cpuidle_driver *drv,
46+
int index)
47+
{
48+
power7_idle();
49+
return index;
50+
}
51+
52+
/*
53+
* States for dedicated partition case.
54+
*/
55+
static struct cpuidle_state powernv_states[] = {
56+
{ /* Snooze */
57+
.name = "snooze",
58+
.desc = "snooze",
59+
.flags = CPUIDLE_FLAG_TIME_VALID,
60+
.exit_latency = 0,
61+
.target_residency = 0,
62+
.enter = &snooze_loop },
63+
{ /* NAP */
64+
.name = "NAP",
65+
.desc = "NAP",
66+
.flags = CPUIDLE_FLAG_TIME_VALID,
67+
.exit_latency = 10,
68+
.target_residency = 100,
69+
.enter = &nap_loop },
70+
};
71+
72+
static int powernv_cpuidle_add_cpu_notifier(struct notifier_block *n,
73+
unsigned long action, void *hcpu)
74+
{
75+
int hotcpu = (unsigned long)hcpu;
76+
struct cpuidle_device *dev =
77+
per_cpu(cpuidle_devices, hotcpu);
78+
79+
if (dev && cpuidle_get_driver()) {
80+
switch (action) {
81+
case CPU_ONLINE:
82+
case CPU_ONLINE_FROZEN:
83+
cpuidle_pause_and_lock();
84+
cpuidle_enable_device(dev);
85+
cpuidle_resume_and_unlock();
86+
break;
87+
88+
case CPU_DEAD:
89+
case CPU_DEAD_FROZEN:
90+
cpuidle_pause_and_lock();
91+
cpuidle_disable_device(dev);
92+
cpuidle_resume_and_unlock();
93+
break;
94+
95+
default:
96+
return NOTIFY_DONE;
97+
}
98+
}
99+
return NOTIFY_OK;
100+
}
101+
102+
static struct notifier_block setup_hotplug_notifier = {
103+
.notifier_call = powernv_cpuidle_add_cpu_notifier,
104+
};
105+
106+
/*
107+
* powernv_cpuidle_driver_init()
108+
*/
109+
static int powernv_cpuidle_driver_init(void)
110+
{
111+
int idle_state;
112+
struct cpuidle_driver *drv = &powernv_idle_driver;
113+
114+
drv->state_count = 0;
115+
116+
for (idle_state = 0; idle_state < max_idle_state; ++idle_state) {
117+
/* Is the state not enabled? */
118+
if (cpuidle_state_table[idle_state].enter == NULL)
119+
continue;
120+
121+
drv->states[drv->state_count] = /* structure copy */
122+
cpuidle_state_table[idle_state];
123+
124+
drv->state_count += 1;
125+
}
126+
127+
return 0;
128+
}
129+
130+
/*
131+
* powernv_idle_probe()
132+
* Choose state table for shared versus dedicated partition
133+
*/
134+
static int powernv_idle_probe(void)
135+
{
136+
137+
if (cpuidle_disable != IDLE_NO_OVERRIDE)
138+
return -ENODEV;
139+
140+
if (firmware_has_feature(FW_FEATURE_OPALv3)) {
141+
cpuidle_state_table = powernv_states;
142+
max_idle_state = ARRAY_SIZE(powernv_states);
143+
} else
144+
return -ENODEV;
145+
146+
return 0;
147+
}
148+
149+
static int __init powernv_processor_idle_init(void)
150+
{
151+
int retval;
152+
153+
retval = powernv_idle_probe();
154+
if (retval)
155+
return retval;
156+
157+
powernv_cpuidle_driver_init();
158+
retval = cpuidle_register(&powernv_idle_driver, NULL);
159+
if (retval) {
160+
printk(KERN_DEBUG "Registration of powernv driver failed.\n");
161+
return retval;
162+
}
163+
164+
register_cpu_notifier(&setup_hotplug_notifier);
165+
printk(KERN_DEBUG "powernv_idle_driver registered\n");
166+
return 0;
167+
}
168+
169+
device_initcall(powernv_processor_idle_init);

0 commit comments

Comments
 (0)