1
2
3
4
5
6
7
8
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
19enum cps_idle_state {
20 STATE_WAIT = 0,
21 STATE_NC_WAIT,
22 STATE_CLOCK_GATED,
23 STATE_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
35
36
37
38
39
40 if (!cpu_data[dev->cpu].core && (index > STATE_NC_WAIT))
41 index = STATE_NC_WAIT;
42
43
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
60 if ((pm_state == CPS_PM_POWER_GATED) && cpu_pm_enter())
61 return -EINTR;
62
63
64 err = cps_pm_enter_state(pm_state);
65
66
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
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
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
153
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);
187