xref: /linux/arch/powerpc/platforms/pseries/suspend.c (revision bba2c3615bd6cfee7456d1130f2e6b01b3f4e9ba)
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 <linux/sysfs.h>
11 #include <asm/firmware.h>
12 #include <asm/hvcall.h>
13 #include <asm/machdep.h>
14 #include <asm/mmu.h>
15 #include <asm/rtas.h>
16 #include <asm/topology.h>
17 #include "pseries.h"
18 
19 static struct device suspend_dev;
20 
21 /**
22  * pseries_suspend_begin - First phase of hibernation
23  *
24  * Check to ensure we are in a valid state to hibernate
25  *
26  * Return value:
27  * 	0 on success / other on failure
28  **/
29 static int pseries_suspend_begin(u64 stream_id)
30 {
31 	long vasi_state, rc;
32 	unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
33 
34 	/* Make sure the state is valid */
35 	rc = plpar_hcall(H_VASI_STATE, retbuf, stream_id);
36 
37 	vasi_state = retbuf[0];
38 
39 	if (rc) {
40 		pr_err("pseries_suspend_begin: vasi_state returned %ld\n",rc);
41 		return rc;
42 	} else if (vasi_state == H_VASI_ENABLED) {
43 		return -EAGAIN;
44 	} else if (vasi_state != H_VASI_SUSPENDING) {
45 		pr_err("pseries_suspend_begin: vasi_state returned state %ld\n",
46 		       vasi_state);
47 		return -EIO;
48 	}
49 	return 0;
50 }
51 
52 /**
53  * pseries_suspend_enter - Final phase of hibernation
54  *
55  * Return value:
56  * 	0 on success / other on failure
57  **/
58 static int pseries_suspend_enter(suspend_state_t state)
59 {
60 	return rtas_ibm_suspend_me(NULL);
61 }
62 
63 /**
64  * store_hibernate - Initiate partition hibernation
65  * @dev:		subsys root device
66  * @attr:		device attribute struct
67  * @buf:		buffer
68  * @count:		buffer size
69  *
70  * Write the stream ID received from the HMC to this file
71  * to trigger hibernating the partition
72  *
73  * Return value:
74  * 	number of bytes printed to buffer / other on failure
75  **/
76 static ssize_t store_hibernate(struct device *dev,
77 			       struct device_attribute *attr,
78 			       const char *buf, size_t count)
79 {
80 	u64 stream_id;
81 	int rc;
82 
83 	if (!capable(CAP_SYS_ADMIN))
84 		return -EPERM;
85 
86 	stream_id = simple_strtoul(buf, NULL, 16);
87 
88 	do {
89 		rc = pseries_suspend_begin(stream_id);
90 		if (rc == -EAGAIN)
91 			ssleep(1);
92 	} while (rc == -EAGAIN);
93 
94 	if (!rc)
95 		rc = pm_suspend(PM_SUSPEND_MEM);
96 
97 	if (!rc) {
98 		rc = count;
99 		post_mobility_fixup();
100 	}
101 
102 
103 	return rc;
104 }
105 
106 #define USER_DT_UPDATE	0
107 #define KERN_DT_UPDATE	1
108 
109 /**
110  * show_hibernate - Report device tree update responsibilty
111  * @dev:		subsys root device
112  * @attr:		device attribute struct
113  * @buf:		buffer
114  *
115  * Report whether a device tree update is performed by the kernel after a
116  * resume, or if drmgr must coordinate the update from user space.
117  *
118  * Return value:
119  *	0 if drmgr is to initiate update, and 1 otherwise
120  **/
121 static ssize_t show_hibernate(struct device *dev,
122 			      struct device_attribute *attr,
123 			      char *buf)
124 {
125 	return sysfs_emit(buf, "%d\n", KERN_DT_UPDATE);
126 }
127 
128 static DEVICE_ATTR(hibernate, 0644, show_hibernate, store_hibernate);
129 
130 static const struct bus_type suspend_subsys = {
131 	.name = "power",
132 	.dev_name = "power",
133 };
134 
135 static const struct platform_suspend_ops pseries_suspend_ops = {
136 	.valid		= suspend_valid_only_mem,
137 	.enter		= pseries_suspend_enter,
138 };
139 
140 /**
141  * pseries_suspend_sysfs_register - Register with sysfs
142  *
143  * Return value:
144  * 	0 on success / other on failure
145  **/
146 static int pseries_suspend_sysfs_register(struct device *dev)
147 {
148 	struct device *dev_root;
149 	int rc;
150 
151 	if ((rc = subsys_system_register(&suspend_subsys, NULL)))
152 		return rc;
153 
154 	dev->id = 0;
155 	dev->bus = &suspend_subsys;
156 
157 	dev_root = bus_get_dev_root(&suspend_subsys);
158 	if (dev_root) {
159 		rc = device_create_file(dev_root, &dev_attr_hibernate);
160 		put_device(dev_root);
161 		if (rc)
162 			goto subsys_unregister;
163 	}
164 
165 	return 0;
166 
167 subsys_unregister:
168 	bus_unregister(&suspend_subsys);
169 	return rc;
170 }
171 
172 /**
173  * pseries_suspend_init - initcall for pSeries suspend
174  *
175  * Return value:
176  * 	0 on success / other on failure
177  **/
178 static int __init pseries_suspend_init(void)
179 {
180 	int rc;
181 
182 	if (!firmware_has_feature(FW_FEATURE_LPAR))
183 		return 0;
184 
185 	if ((rc = pseries_suspend_sysfs_register(&suspend_dev)))
186 		return rc;
187 
188 	suspend_set_ops(&pseries_suspend_ops);
189 	return 0;
190 }
191 machine_device_initcall(pseries, pseries_suspend_init);
192