summaryrefslogtreecommitdiffstats
path: root/drivers/cpuidle
diff options
context:
space:
mode:
authorPaul Burton <paul.burton@imgtec.com>2014-04-14 11:25:29 -0400
committerPaul Burton <paul.burton@imgtec.com>2014-05-28 11:20:36 -0400
commitd050894435cdc78807e714a0148527542a583e87 (patch)
tree39f95ee57dbe42e78945365e2b166161a24cc804 /drivers/cpuidle
parentf08dbf8a61462aa122b9b5077849a3f4bd84702a (diff)
cpuidle: cpuidle-cps: add MIPS CPS cpuidle driver
This patch adds a cpuidle driver for systems based around the MIPS Coherent Processing System (CPS) architecture. It supports four idle states: - The standard MIPS wait instruction. - The non-coherent wait, clock gated & power gated states exposed by the recently added pm-cps layer. The pm-cps layer is used to enter all the deep idle states. Since cores in the clock or power gated states cannot service interrupts, the gic_send_ipi_single function is modified to send a power up command for the appropriate core to the CPC in cases where the target CPU has marked itself potentially incoherent. Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Diffstat (limited to 'drivers/cpuidle')
-rw-r--r--drivers/cpuidle/Kconfig5
-rw-r--r--drivers/cpuidle/Kconfig.mips17
-rw-r--r--drivers/cpuidle/Makefile4
-rw-r--r--drivers/cpuidle/cpuidle-cps.c186
4 files changed, 212 insertions, 0 deletions
diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig
index f04e25f6c98d..1b96fb91d32c 100644
--- a/drivers/cpuidle/Kconfig
+++ b/drivers/cpuidle/Kconfig
@@ -35,6 +35,11 @@ depends on ARM
35source "drivers/cpuidle/Kconfig.arm" 35source "drivers/cpuidle/Kconfig.arm"
36endmenu 36endmenu
37 37
38menu "MIPS CPU Idle Drivers"
39depends on MIPS
40source "drivers/cpuidle/Kconfig.mips"
41endmenu
42
38menu "POWERPC CPU Idle Drivers" 43menu "POWERPC CPU Idle Drivers"
39depends on PPC 44depends on PPC
40source "drivers/cpuidle/Kconfig.powerpc" 45source "drivers/cpuidle/Kconfig.powerpc"
diff --git a/drivers/cpuidle/Kconfig.mips b/drivers/cpuidle/Kconfig.mips
new file mode 100644
index 000000000000..0e70ee28a5ca
--- /dev/null
+++ b/drivers/cpuidle/Kconfig.mips
@@ -0,0 +1,17 @@
1#
2# MIPS CPU Idle Drivers
3#
4config MIPS_CPS_CPUIDLE
5 bool "CPU Idle driver for MIPS CPS platforms"
6 depends on CPU_IDLE
7 depends on SYS_SUPPORTS_MIPS_CPS
8 select ARCH_NEEDS_CPU_IDLE_COUPLED if MIPS_MT
9 select GENERIC_CLOCKEVENTS_BROADCAST if SMP
10 select MIPS_CPS_PM
11 default y
12 help
13 Select this option to enable processor idle state management
14 through cpuidle for systems built around the MIPS Coherent
15 Processing System (CPS) architecture. In order to make use of
16 the deepest idle states you will need to ensure that you are
17 also using the CONFIG_MIPS_CPS SMP implementation.
diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile
index f71ae1b373c5..a7fc96bcf319 100644
--- a/drivers/cpuidle/Makefile
+++ b/drivers/cpuidle/Makefile
@@ -15,6 +15,10 @@ obj-$(CONFIG_ARM_U8500_CPUIDLE) += cpuidle-ux500.o
15obj-$(CONFIG_ARM_AT91_CPUIDLE) += cpuidle-at91.o 15obj-$(CONFIG_ARM_AT91_CPUIDLE) += cpuidle-at91.o
16 16
17############################################################################### 17###############################################################################
18# MIPS drivers
19obj-$(CONFIG_MIPS_CPS_CPUIDLE) += cpuidle-cps.o
20
21###############################################################################
18# POWERPC drivers 22# POWERPC drivers
19obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o 23obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o
20obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o 24obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o
diff --git a/drivers/cpuidle/cpuidle-cps.c b/drivers/cpuidle/cpuidle-cps.c
new file mode 100644
index 000000000000..fc7b62720deb
--- /dev/null
+++ b/drivers/cpuidle/cpuidle-cps.c
@@ -0,0 +1,186 @@
1/*
2 * Copyright (C) 2014 Imagination Technologies
3 * Author: Paul Burton <paul.burton@imgtec.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
9 */
10
11#include <linux/cpu_pm.h>
12#include <linux/cpuidle.h>
13#include <linux/init.h>
14
15#include <asm/idle.h>
16#include <asm/pm-cps.h>
17
18/* Enumeration of the various idle states this driver may enter */
19enum cps_idle_state {
20 STATE_WAIT = 0, /* MIPS wait instruction, coherent */
21 STATE_NC_WAIT, /* MIPS wait instruction, non-coherent */
22 STATE_CLOCK_GATED, /* Core clock gated */
23 STATE_POWER_GATED, /* Core power gated */
24 STATE_COUNT
25};
26
27static int cps_nc_enter(struct cpuidle_device *dev,
28 struct cpuidle_driver *drv, int index)
29{
30 enum cps_pm_state pm_state;
31 int err;
32
33 /*
34 * At least one core must remain powered up & clocked in order for the
35 * system to have any hope of functioning.
36 *
37 * TODO: don't treat core 0 specially, just prevent the final core
38 * TODO: remap interrupt affinity temporarily
39 */
40 if (!cpu_data[dev->cpu].core && (index > STATE_NC_WAIT))
41 index = STATE_NC_WAIT;
42
43 /* Select the appropriate cps_pm_state */
44 switch (index) {
45 case STATE_NC_WAIT:
46 pm_state = CPS_PM_NC_WAIT;
47 break;
48 case STATE_CLOCK_GATED:
49 pm_state = CPS_PM_CLOCK_GATED;
50 break;
51 case STATE_POWER_GATED:
52 pm_state = CPS_PM_POWER_GATED;
53 break;
54 default:
55 BUG();
56 return -EINVAL;
57 }
58
59 /* Notify listeners the CPU is about to power down */
60 if ((pm_state == CPS_PM_POWER_GATED) && cpu_pm_enter())
61 return -EINTR;
62
63 /* Enter that state */
64 err = cps_pm_enter_state(pm_state);
65
66 /* Notify listeners the CPU is back up */
67 if (pm_state == CPS_PM_POWER_GATED)
68 cpu_pm_exit();
69
70 return err ?: index;
71}
72
73static struct cpuidle_driver cps_driver = {
74 .name = "cpc_cpuidle",
75 .owner = THIS_MODULE,
76 .states = {
77 [STATE_WAIT] = MIPS_CPUIDLE_WAIT_STATE,
78 [STATE_NC_WAIT] = {
79 .enter = cps_nc_enter,
80 .exit_latency = 200,
81 .target_residency = 450,
82 .flags = CPUIDLE_FLAG_TIME_VALID,
83 .name = "nc-wait",
84 .desc = "non-coherent MIPS wait",
85 },
86 [STATE_CLOCK_GATED] = {
87 .enter = cps_nc_enter,
88 .exit_latency = 300,
89 .target_residency = 700,
90 .flags = CPUIDLE_FLAG_TIME_VALID |
91 CPUIDLE_FLAG_TIMER_STOP,
92 .name = "clock-gated",
93 .desc = "core clock gated",
94 },
95 [STATE_POWER_GATED] = {
96 .enter = cps_nc_enter,
97 .exit_latency = 600,
98 .target_residency = 1000,
99 .flags = CPUIDLE_FLAG_TIME_VALID |
100 CPUIDLE_FLAG_TIMER_STOP,
101 .name = "power-gated",
102 .desc = "core power gated",
103 },
104 },
105 .state_count = STATE_COUNT,
106 .safe_state_index = 0,
107};
108
109static void __init cps_cpuidle_unregister(void)
110{
111 int cpu;
112 struct cpuidle_device *device;
113
114 for_each_possible_cpu(cpu) {
115 device = &per_cpu(cpuidle_dev, cpu);
116 cpuidle_unregister_device(device);
117 }
118
119 cpuidle_unregister_driver(&cps_driver);
120}
121
122static int __init cps_cpuidle_init(void)
123{
124 int err, cpu, core, i;
125 struct cpuidle_device *device;
126
127 /* Detect supported states */
128 if (!cps_pm_support_state(CPS_PM_POWER_GATED))
129 cps_driver.state_count = STATE_CLOCK_GATED + 1;
130 if (!cps_pm_support_state(CPS_PM_CLOCK_GATED))
131 cps_driver.state_count = STATE_NC_WAIT + 1;
132 if (!cps_pm_support_state(CPS_PM_NC_WAIT))
133 cps_driver.state_count = STATE_WAIT + 1;
134
135 /* Inform the user if some states are unavailable */
136 if (cps_driver.state_count < STATE_COUNT) {
137 pr_info("cpuidle-cps: limited to ");
138 switch (cps_driver.state_count - 1) {
139 case STATE_WAIT:
140 pr_cont("coherent wait\n");
141 break;
142 case STATE_NC_WAIT:
143 pr_cont("non-coherent wait\n");
144 break;
145 case STATE_CLOCK_GATED:
146 pr_cont("clock gating\n");
147 break;
148 }
149 }
150
151 /*
152 * Set the coupled flag on the appropriate states if this system
153 * requires it.
154 */
155 if (coupled_coherence)
156 for (i = STATE_NC_WAIT; i < cps_driver.state_count; i++)
157 cps_driver.states[i].flags |= CPUIDLE_FLAG_COUPLED;
158
159 err = cpuidle_register_driver(&cps_driver);
160 if (err) {
161 pr_err("Failed to register CPS cpuidle driver\n");
162 return err;
163 }
164
165 for_each_possible_cpu(cpu) {
166 core = cpu_data[cpu].core;
167 device = &per_cpu(cpuidle_dev, cpu);
168 device->cpu = cpu;
169#ifdef CONFIG_MIPS_MT
170 cpumask_copy(&device->coupled_cpus, &cpu_sibling_map[cpu]);
171#endif
172
173 err = cpuidle_register_device(device);
174 if (err) {
175 pr_err("Failed to register CPU%d cpuidle device\n",
176 cpu);
177 goto err_out;
178 }
179 }
180
181 return 0;
182err_out:
183 cps_cpuidle_unregister();
184 return err;
185}
186device_initcall(cps_cpuidle_init);