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