1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#include <linux/cpuidle.h>
16#include <linux/cpu_pm.h>
17#include <linux/slab.h>
18#include <linux/of.h>
19
20#include <asm/cpu.h>
21#include <asm/cputype.h>
22#include <asm/cpuidle.h>
23#include <asm/mcpm.h>
24#include <asm/smp_plat.h>
25#include <asm/suspend.h>
26
27static int bl_enter_powerdown(struct cpuidle_device *dev,
28 struct cpuidle_driver *drv, int idx);
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60static struct cpuidle_driver bl_idle_little_driver = {
61 .name = "little_idle",
62 .owner = THIS_MODULE,
63 .states[0] = ARM_CPUIDLE_WFI_STATE,
64 .states[1] = {
65 .enter = bl_enter_powerdown,
66 .exit_latency = 700,
67 .target_residency = 2500,
68 .flags = CPUIDLE_FLAG_TIME_VALID |
69 CPUIDLE_FLAG_TIMER_STOP,
70 .name = "C1",
71 .desc = "ARM little-cluster power down",
72 },
73 .state_count = 2,
74};
75
76static struct cpuidle_driver bl_idle_big_driver = {
77 .name = "big_idle",
78 .owner = THIS_MODULE,
79 .states[0] = ARM_CPUIDLE_WFI_STATE,
80 .states[1] = {
81 .enter = bl_enter_powerdown,
82 .exit_latency = 500,
83 .target_residency = 2000,
84 .flags = CPUIDLE_FLAG_TIME_VALID |
85 CPUIDLE_FLAG_TIMER_STOP,
86 .name = "C1",
87 .desc = "ARM big-cluster power down",
88 },
89 .state_count = 2,
90};
91
92
93
94
95
96
97static int notrace bl_powerdown_finisher(unsigned long arg)
98{
99
100 unsigned int mpidr = read_cpuid_mpidr();
101 unsigned int cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
102 unsigned int cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
103
104 mcpm_set_entry_vector(cpu, cluster, cpu_resume);
105
106
107
108
109
110
111 mcpm_cpu_suspend(0);
112
113
114 return 1;
115}
116
117
118
119
120
121
122
123
124
125
126static int bl_enter_powerdown(struct cpuidle_device *dev,
127 struct cpuidle_driver *drv, int idx)
128{
129 cpu_pm_enter();
130
131 cpu_suspend(0, bl_powerdown_finisher);
132
133
134 mcpm_cpu_powered_up();
135
136 cpu_pm_exit();
137
138 return idx;
139}
140
141static int __init bl_idle_driver_init(struct cpuidle_driver *drv, int cpu_id)
142{
143 struct cpuinfo_arm *cpu_info;
144 struct cpumask *cpumask;
145 unsigned long cpuid;
146 int cpu;
147
148 cpumask = kzalloc(cpumask_size(), GFP_KERNEL);
149 if (!cpumask)
150 return -ENOMEM;
151
152 for_each_possible_cpu(cpu) {
153 cpu_info = &per_cpu(cpu_data, cpu);
154 cpuid = is_smp() ? cpu_info->cpuid : read_cpuid_id();
155
156
157 if ((cpuid & 0xFFF0) == cpu_id)
158 cpumask_set_cpu(cpu, cpumask);
159 }
160
161 drv->cpumask = cpumask;
162
163 return 0;
164}
165
166static int __init bl_idle_init(void)
167{
168 int ret;
169
170
171
172
173 if (!of_machine_is_compatible("arm,vexpress,v2p-ca15_a7"))
174 return -ENODEV;
175
176
177
178
179
180
181 ret = bl_idle_driver_init(&bl_idle_little_driver,
182 ARM_CPU_PART_CORTEX_A7);
183 if (ret)
184 return ret;
185
186 ret = bl_idle_driver_init(&bl_idle_big_driver, ARM_CPU_PART_CORTEX_A15);
187 if (ret)
188 goto out_uninit_little;
189
190 ret = cpuidle_register(&bl_idle_little_driver, NULL);
191 if (ret)
192 goto out_uninit_big;
193
194 ret = cpuidle_register(&bl_idle_big_driver, NULL);
195 if (ret)
196 goto out_unregister_little;
197
198 return 0;
199
200out_unregister_little:
201 cpuidle_unregister(&bl_idle_little_driver);
202out_uninit_big:
203 kfree(bl_idle_big_driver.cpumask);
204out_uninit_little:
205 kfree(bl_idle_little_driver.cpumask);
206
207 return ret;
208}
209device_initcall(bl_idle_init);
210