1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3   * Copyright (C) 2010 Brian King IBM Corporation
4   */
5 
6 #include <linux/cpu.h>
7 #include <linux/delay.h>
8 #include <linux/suspend.h>
9 #include <linux/stat.h>
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>
15 #include <asm/topology.h>
16 #include "../../kernel/cacheinfo.h"
17 
18 static u64 stream_id;
19 static struct device suspend_dev;
20 static DECLARE_COMPLETION(suspend_work);
21 static struct rtas_suspend_me_data suspend_data;
22 static 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  **/
32 static 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  **/
62 static int pseries_suspend_cpu(void)
63 {
64 	if (atomic_read(&suspending))
65 		return rtas_suspend_cpu(&suspend_data);
66 	return 0;
67 }
68 
69 /**
70  * pseries_suspend_enable_irqs
71  *
72  * Post suspend configuration updates
73  *
74  **/
75 static 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 
86 /**
87  * pseries_suspend_enter - Final phase of hibernation
88  *
89  * Return value:
90  * 	0 on success / other on failure
91  **/
92 static 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  **/
107 static 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;
114 	reinit_completion(&suspend_work);
115 	return 0;
116 }
117 
118 /**
119  * store_hibernate - Initiate partition hibernation
120  * @dev:		subsys root device
121  * @attr:		device attribute struct
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  **/
131 static ssize_t store_hibernate(struct device *dev,
132 			       struct device_attribute *attr,
133 			       const char *buf, size_t count)
134 {
135 	int rc;
136 
137 	if (!capable(CAP_SYS_ADMIN))
138 		return -EPERM;
139 
140 	stream_id = simple_strtoul(buf, NULL, 16);
141 
142 	do {
143 		rc = pseries_suspend_begin(PM_SUSPEND_MEM);
144 		if (rc == -EAGAIN)
145 			ssleep(1);
146 	} while (rc == -EAGAIN);
147 
148 	if (!rc)
149 		rc = pm_suspend(PM_SUSPEND_MEM);
150 
151 	stream_id = 0;
152 
153 	if (!rc)
154 		rc = count;
155 
156 	return rc;
157 }
158 
159 #define USER_DT_UPDATE	0
160 #define KERN_DT_UPDATE	1
161 
162 /**
163  * show_hibernate - Report device tree update responsibilty
164  * @dev:		subsys root device
165  * @attr:		device attribute struct
166  * @buf:		buffer
167  *
168  * Report whether a device tree update is performed by the kernel after a
169  * resume, or if drmgr must coordinate the update from user space.
170  *
171  * Return value:
172  *	0 if drmgr is to initiate update, and 1 otherwise
173  **/
174 static ssize_t show_hibernate(struct device *dev,
175 			      struct device_attribute *attr,
176 			      char *buf)
177 {
178 	return sprintf(buf, "%d\n", KERN_DT_UPDATE);
179 }
180 
181 static DEVICE_ATTR(hibernate, 0644, show_hibernate, store_hibernate);
182 
183 static struct bus_type suspend_subsys = {
184 	.name = "power",
185 	.dev_name = "power",
186 };
187 
188 static const struct platform_suspend_ops pseries_suspend_ops = {
189 	.valid		= suspend_valid_only_mem,
190 	.begin		= pseries_suspend_begin,
191 	.prepare_late	= pseries_prepare_late,
192 	.enter		= pseries_suspend_enter,
193 };
194 
195 /**
196  * pseries_suspend_sysfs_register - Register with sysfs
197  *
198  * Return value:
199  * 	0 on success / other on failure
200  **/
201 static int pseries_suspend_sysfs_register(struct device *dev)
202 {
203 	int rc;
204 
205 	if ((rc = subsys_system_register(&suspend_subsys, NULL)))
206 		return rc;
207 
208 	dev->id = 0;
209 	dev->bus = &suspend_subsys;
210 
211 	if ((rc = device_create_file(suspend_subsys.dev_root, &dev_attr_hibernate)))
212 		goto subsys_unregister;
213 
214 	return 0;
215 
216 subsys_unregister:
217 	bus_unregister(&suspend_subsys);
218 	return rc;
219 }
220 
221 /**
222  * pseries_suspend_init - initcall for pSeries suspend
223  *
224  * Return value:
225  * 	0 on success / other on failure
226  **/
227 static int __init pseries_suspend_init(void)
228 {
229 	int rc;
230 
231 	if (!firmware_has_feature(FW_FEATURE_LPAR))
232 		return 0;
233 
234 	suspend_data.token = rtas_token("ibm,suspend-me");
235 	if (suspend_data.token == RTAS_UNKNOWN_SERVICE)
236 		return 0;
237 
238 	if ((rc = pseries_suspend_sysfs_register(&suspend_dev)))
239 		return rc;
240 
241 	ppc_md.suspend_disable_cpu = pseries_suspend_cpu;
242 	ppc_md.suspend_enable_irqs = pseries_suspend_enable_irqs;
243 	suspend_set_ops(&pseries_suspend_ops);
244 	return 0;
245 }
246 machine_device_initcall(pseries, pseries_suspend_init);
247