1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19#include <linux/cpu.h>
20#include <linux/delay.h>
21#include <linux/suspend.h>
22#include <linux/stat.h>
23#include <asm/firmware.h>
24#include <asm/hvcall.h>
25#include <asm/machdep.h>
26#include <asm/mmu.h>
27#include <asm/rtas.h>
28#include <asm/topology.h>
29#include "../../kernel/cacheinfo.h"
30
31static u64 stream_id;
32static struct device suspend_dev;
33static DECLARE_COMPLETION(suspend_work);
34static struct rtas_suspend_me_data suspend_data;
35static atomic_t suspending;
36
37
38
39
40
41
42
43
44
45static int pseries_suspend_begin(suspend_state_t state)
46{
47 long vasi_state, rc;
48 unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
49
50
51 rc = plpar_hcall(H_VASI_STATE, retbuf, stream_id);
52
53 vasi_state = retbuf[0];
54
55 if (rc) {
56 pr_err("pseries_suspend_begin: vasi_state returned %ld\n",rc);
57 return rc;
58 } else if (vasi_state == H_VASI_ENABLED) {
59 return -EAGAIN;
60 } else if (vasi_state != H_VASI_SUSPENDING) {
61 pr_err("pseries_suspend_begin: vasi_state returned state %ld\n",
62 vasi_state);
63 return -EIO;
64 }
65
66 return 0;
67}
68
69
70
71
72
73
74
75static int pseries_suspend_cpu(void)
76{
77 if (atomic_read(&suspending))
78 return rtas_suspend_cpu(&suspend_data);
79 return 0;
80}
81
82
83
84
85
86
87
88static void pseries_suspend_enable_irqs(void)
89{
90
91
92
93
94 cacheinfo_cpu_offline(smp_processor_id());
95 post_mobility_fixup();
96 cacheinfo_cpu_online(smp_processor_id());
97}
98
99
100
101
102
103
104
105static int pseries_suspend_enter(suspend_state_t state)
106{
107 int rc = rtas_suspend_last_cpu(&suspend_data);
108
109 atomic_set(&suspending, 0);
110 atomic_set(&suspend_data.done, 1);
111 return rc;
112}
113
114
115
116
117
118
119
120static int pseries_prepare_late(void)
121{
122 atomic_set(&suspending, 1);
123 atomic_set(&suspend_data.working, 0);
124 atomic_set(&suspend_data.done, 0);
125 atomic_set(&suspend_data.error, 0);
126 suspend_data.complete = &suspend_work;
127 reinit_completion(&suspend_work);
128 return 0;
129}
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144static ssize_t store_hibernate(struct device *dev,
145 struct device_attribute *attr,
146 const char *buf, size_t count)
147{
148 cpumask_var_t offline_mask;
149 int rc;
150
151 if (!capable(CAP_SYS_ADMIN))
152 return -EPERM;
153
154 if (!alloc_cpumask_var(&offline_mask, GFP_KERNEL))
155 return -ENOMEM;
156
157 stream_id = simple_strtoul(buf, NULL, 16);
158
159 do {
160 rc = pseries_suspend_begin(PM_SUSPEND_MEM);
161 if (rc == -EAGAIN)
162 ssleep(1);
163 } while (rc == -EAGAIN);
164
165 if (!rc) {
166
167 cpumask_andnot(offline_mask, cpu_present_mask,
168 cpu_online_mask);
169 rc = rtas_online_cpus_mask(offline_mask);
170 if (rc) {
171 pr_err("%s: Could not bring present CPUs online.\n",
172 __func__);
173 goto out;
174 }
175
176 stop_topology_update();
177 rc = pm_suspend(PM_SUSPEND_MEM);
178 start_topology_update();
179
180
181 if (!rtas_offline_cpus_mask(offline_mask))
182 pr_warn("%s: Could not restore CPUs to offline "
183 "state.\n", __func__);
184 }
185
186 stream_id = 0;
187
188 if (!rc)
189 rc = count;
190out:
191 free_cpumask_var(offline_mask);
192 return rc;
193}
194
195#define USER_DT_UPDATE 0
196#define KERN_DT_UPDATE 1
197
198
199
200
201
202
203
204
205
206
207
208
209
210static ssize_t show_hibernate(struct device *dev,
211 struct device_attribute *attr,
212 char *buf)
213{
214 return sprintf(buf, "%d\n", KERN_DT_UPDATE);
215}
216
217static DEVICE_ATTR(hibernate, 0644, show_hibernate, store_hibernate);
218
219static struct bus_type suspend_subsys = {
220 .name = "power",
221 .dev_name = "power",
222};
223
224static const struct platform_suspend_ops pseries_suspend_ops = {
225 .valid = suspend_valid_only_mem,
226 .begin = pseries_suspend_begin,
227 .prepare_late = pseries_prepare_late,
228 .enter = pseries_suspend_enter,
229};
230
231
232
233
234
235
236
237static int pseries_suspend_sysfs_register(struct device *dev)
238{
239 int rc;
240
241 if ((rc = subsys_system_register(&suspend_subsys, NULL)))
242 return rc;
243
244 dev->id = 0;
245 dev->bus = &suspend_subsys;
246
247 if ((rc = device_create_file(suspend_subsys.dev_root, &dev_attr_hibernate)))
248 goto subsys_unregister;
249
250 return 0;
251
252subsys_unregister:
253 bus_unregister(&suspend_subsys);
254 return rc;
255}
256
257
258
259
260
261
262
263static int __init pseries_suspend_init(void)
264{
265 int rc;
266
267 if (!firmware_has_feature(FW_FEATURE_LPAR))
268 return 0;
269
270 suspend_data.token = rtas_token("ibm,suspend-me");
271 if (suspend_data.token == RTAS_UNKNOWN_SERVICE)
272 return 0;
273
274 if ((rc = pseries_suspend_sysfs_register(&suspend_dev)))
275 return rc;
276
277 ppc_md.suspend_disable_cpu = pseries_suspend_cpu;
278 ppc_md.suspend_enable_irqs = pseries_suspend_enable_irqs;
279 suspend_set_ops(&pseries_suspend_ops);
280 return 0;
281}
282machine_device_initcall(pseries, pseries_suspend_init);
283