treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 156
[linux-block.git] / arch / powerpc / platforms / pseries / suspend.c
CommitLineData
1a59d1b8 1// SPDX-License-Identifier: GPL-2.0-or-later
32d8ad4e
BK
2/*
3 * Copyright (C) 2010 Brian King IBM Corporation
32d8ad4e
BK
4 */
5
120496ac 6#include <linux/cpu.h>
32d8ad4e
BK
7#include <linux/delay.h>
8#include <linux/suspend.h>
b56eade5 9#include <linux/stat.h>
32d8ad4e
BK
10#include <asm/firmware.h>
11#include <asm/hvcall.h>
12#include <asm/machdep.h>
13#include <asm/mmu.h>
14#include <asm/rtas.h>
444080d1 15#include <asm/topology.h>
6b36ba84 16#include "../../kernel/cacheinfo.h"
32d8ad4e
BK
17
18static u64 stream_id;
86ba41d0 19static struct device suspend_dev;
32d8ad4e
BK
20static DECLARE_COMPLETION(suspend_work);
21static struct rtas_suspend_me_data suspend_data;
22static atomic_t suspending;
23
24/**
25 * pseries_suspend_begin - First phase of hibernation
26 *
27 * Check to ensure we are in a valid state to hibernate
28 *
29 * Return value:
30 * 0 on success / other on failure
31 **/
32static int pseries_suspend_begin(suspend_state_t state)
33{
34 long vasi_state, rc;
35 unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
36
37 /* Make sure the state is valid */
38 rc = plpar_hcall(H_VASI_STATE, retbuf, stream_id);
39
40 vasi_state = retbuf[0];
41
42 if (rc) {
43 pr_err("pseries_suspend_begin: vasi_state returned %ld\n",rc);
44 return rc;
45 } else if (vasi_state == H_VASI_ENABLED) {
46 return -EAGAIN;
47 } else if (vasi_state != H_VASI_SUSPENDING) {
48 pr_err("pseries_suspend_begin: vasi_state returned state %ld\n",
49 vasi_state);
50 return -EIO;
51 }
52
53 return 0;
54}
55
56/**
57 * pseries_suspend_cpu - Suspend a single CPU
58 *
59 * Makes the H_JOIN call to suspend the CPU
60 *
61 **/
62static int pseries_suspend_cpu(void)
63{
64 if (atomic_read(&suspending))
65 return rtas_suspend_cpu(&suspend_data);
66 return 0;
67}
68
6b36ba84
HM
69/**
70 * pseries_suspend_enable_irqs
71 *
72 * Post suspend configuration updates
73 *
74 **/
75static void pseries_suspend_enable_irqs(void)
76{
77 /*
78 * Update configuration which can be modified based on device tree
79 * changes during resume.
80 */
81 cacheinfo_cpu_offline(smp_processor_id());
82 post_mobility_fixup();
83 cacheinfo_cpu_online(smp_processor_id());
84}
85
32d8ad4e
BK
86/**
87 * pseries_suspend_enter - Final phase of hibernation
88 *
89 * Return value:
90 * 0 on success / other on failure
91 **/
92static int pseries_suspend_enter(suspend_state_t state)
93{
94 int rc = rtas_suspend_last_cpu(&suspend_data);
95
96 atomic_set(&suspending, 0);
97 atomic_set(&suspend_data.done, 1);
98 return rc;
99}
100
101/**
102 * pseries_prepare_late - Prepare to suspend all other CPUs
103 *
104 * Return value:
105 * 0 on success / other on failure
106 **/
107static int pseries_prepare_late(void)
108{
109 atomic_set(&suspending, 1);
110 atomic_set(&suspend_data.working, 0);
111 atomic_set(&suspend_data.done, 0);
112 atomic_set(&suspend_data.error, 0);
113 suspend_data.complete = &suspend_work;
16735d02 114 reinit_completion(&suspend_work);
32d8ad4e
BK
115 return 0;
116}
117
118/**
119 * store_hibernate - Initiate partition hibernation
86ba41d0
KS
120 * @dev: subsys root device
121 * @attr: device attribute struct
32d8ad4e
BK
122 * @buf: buffer
123 * @count: buffer size
124 *
125 * Write the stream ID received from the HMC to this file
126 * to trigger hibernating the partition
127 *
128 * Return value:
129 * number of bytes printed to buffer / other on failure
130 **/
86ba41d0
KS
131static ssize_t store_hibernate(struct device *dev,
132 struct device_attribute *attr,
32d8ad4e
BK
133 const char *buf, size_t count)
134{
120496ac 135 cpumask_var_t offline_mask;
32d8ad4e
BK
136 int rc;
137
138 if (!capable(CAP_SYS_ADMIN))
139 return -EPERM;
140
0ee931c4 141 if (!alloc_cpumask_var(&offline_mask, GFP_KERNEL))
120496ac
RJ
142 return -ENOMEM;
143
32d8ad4e
BK
144 stream_id = simple_strtoul(buf, NULL, 16);
145
146 do {
147 rc = pseries_suspend_begin(PM_SUSPEND_MEM);
148 if (rc == -EAGAIN)
149 ssleep(1);
150 } while (rc == -EAGAIN);
151
444080d1 152 if (!rc) {
120496ac
RJ
153 /* All present CPUs must be online */
154 cpumask_andnot(offline_mask, cpu_present_mask,
155 cpu_online_mask);
156 rc = rtas_online_cpus_mask(offline_mask);
157 if (rc) {
158 pr_err("%s: Could not bring present CPUs online.\n",
159 __func__);
160 goto out;
161 }
162
444080d1 163 stop_topology_update();
32d8ad4e 164 rc = pm_suspend(PM_SUSPEND_MEM);
444080d1 165 start_topology_update();
120496ac
RJ
166
167 /* Take down CPUs not online prior to suspend */
168 if (!rtas_offline_cpus_mask(offline_mask))
169 pr_warn("%s: Could not restore CPUs to offline "
170 "state.\n", __func__);
444080d1 171 }
32d8ad4e
BK
172
173 stream_id = 0;
174
175 if (!rc)
176 rc = count;
120496ac
RJ
177out:
178 free_cpumask_var(offline_mask);
32d8ad4e
BK
179 return rc;
180}
181
9da34892
TD
182#define USER_DT_UPDATE 0
183#define KERN_DT_UPDATE 1
184
185/**
186 * show_hibernate - Report device tree update responsibilty
187 * @dev: subsys root device
188 * @attr: device attribute struct
189 * @buf: buffer
190 *
191 * Report whether a device tree update is performed by the kernel after a
192 * resume, or if drmgr must coordinate the update from user space.
193 *
194 * Return value:
195 * 0 if drmgr is to initiate update, and 1 otherwise
196 **/
197static ssize_t show_hibernate(struct device *dev,
198 struct device_attribute *attr,
199 char *buf)
200{
201 return sprintf(buf, "%d\n", KERN_DT_UPDATE);
202}
203
57ad583f 204static DEVICE_ATTR(hibernate, 0644, show_hibernate, store_hibernate);
32d8ad4e 205
86ba41d0 206static struct bus_type suspend_subsys = {
32d8ad4e 207 .name = "power",
86ba41d0 208 .dev_name = "power",
32d8ad4e
BK
209};
210
2f55ac07 211static const struct platform_suspend_ops pseries_suspend_ops = {
32d8ad4e
BK
212 .valid = suspend_valid_only_mem,
213 .begin = pseries_suspend_begin,
214 .prepare_late = pseries_prepare_late,
215 .enter = pseries_suspend_enter,
216};
217
218/**
219 * pseries_suspend_sysfs_register - Register with sysfs
220 *
221 * Return value:
222 * 0 on success / other on failure
223 **/
86ba41d0 224static int pseries_suspend_sysfs_register(struct device *dev)
32d8ad4e
BK
225{
226 int rc;
227
86ba41d0 228 if ((rc = subsys_system_register(&suspend_subsys, NULL)))
32d8ad4e
BK
229 return rc;
230
86ba41d0
KS
231 dev->id = 0;
232 dev->bus = &suspend_subsys;
32d8ad4e 233
86ba41d0
KS
234 if ((rc = device_create_file(suspend_subsys.dev_root, &dev_attr_hibernate)))
235 goto subsys_unregister;
32d8ad4e
BK
236
237 return 0;
238
86ba41d0
KS
239subsys_unregister:
240 bus_unregister(&suspend_subsys);
32d8ad4e
BK
241 return rc;
242}
243
244/**
245 * pseries_suspend_init - initcall for pSeries suspend
246 *
247 * Return value:
248 * 0 on success / other on failure
249 **/
250static int __init pseries_suspend_init(void)
251{
252 int rc;
253
8e83e905 254 if (!firmware_has_feature(FW_FEATURE_LPAR))
32d8ad4e
BK
255 return 0;
256
257 suspend_data.token = rtas_token("ibm,suspend-me");
258 if (suspend_data.token == RTAS_UNKNOWN_SERVICE)
259 return 0;
260
86ba41d0 261 if ((rc = pseries_suspend_sysfs_register(&suspend_dev)))
32d8ad4e
BK
262 return rc;
263
264 ppc_md.suspend_disable_cpu = pseries_suspend_cpu;
6b36ba84 265 ppc_md.suspend_enable_irqs = pseries_suspend_enable_irqs;
32d8ad4e
BK
266 suspend_set_ops(&pseries_suspend_ops);
267 return 0;
268}
8e83e905 269machine_device_initcall(pseries, pseries_suspend_init);