xref: /OK3568_Linux_fs/kernel/arch/powerpc/platforms/pseries/mobility.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Support for Partition Mobility/Migration
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2010 Nathan Fontenot
6*4882a593Smuzhiyun  * Copyright (C) 2010 IBM Corporation
7*4882a593Smuzhiyun  */
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #define pr_fmt(fmt) "mobility: " fmt
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <linux/cpu.h>
13*4882a593Smuzhiyun #include <linux/kernel.h>
14*4882a593Smuzhiyun #include <linux/kobject.h>
15*4882a593Smuzhiyun #include <linux/sched.h>
16*4882a593Smuzhiyun #include <linux/smp.h>
17*4882a593Smuzhiyun #include <linux/stat.h>
18*4882a593Smuzhiyun #include <linux/completion.h>
19*4882a593Smuzhiyun #include <linux/device.h>
20*4882a593Smuzhiyun #include <linux/delay.h>
21*4882a593Smuzhiyun #include <linux/slab.h>
22*4882a593Smuzhiyun #include <linux/stringify.h>
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun #include <asm/machdep.h>
25*4882a593Smuzhiyun #include <asm/rtas.h>
26*4882a593Smuzhiyun #include "pseries.h"
27*4882a593Smuzhiyun #include "../../kernel/cacheinfo.h"
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun static struct kobject *mobility_kobj;
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun struct update_props_workarea {
32*4882a593Smuzhiyun 	__be32 phandle;
33*4882a593Smuzhiyun 	__be32 state;
34*4882a593Smuzhiyun 	__be64 reserved;
35*4882a593Smuzhiyun 	__be32 nprops;
36*4882a593Smuzhiyun } __packed;
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun #define NODE_ACTION_MASK	0xff000000
39*4882a593Smuzhiyun #define NODE_COUNT_MASK		0x00ffffff
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun #define DELETE_DT_NODE	0x01000000
42*4882a593Smuzhiyun #define UPDATE_DT_NODE	0x02000000
43*4882a593Smuzhiyun #define ADD_DT_NODE	0x03000000
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun #define MIGRATION_SCOPE	(1)
46*4882a593Smuzhiyun #define PRRN_SCOPE -2
47*4882a593Smuzhiyun 
mobility_rtas_call(int token,char * buf,s32 scope)48*4882a593Smuzhiyun static int mobility_rtas_call(int token, char *buf, s32 scope)
49*4882a593Smuzhiyun {
50*4882a593Smuzhiyun 	int rc;
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun 	spin_lock(&rtas_data_buf_lock);
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun 	memcpy(rtas_data_buf, buf, RTAS_DATA_BUF_SIZE);
55*4882a593Smuzhiyun 	rc = rtas_call(token, 2, 1, NULL, rtas_data_buf, scope);
56*4882a593Smuzhiyun 	memcpy(buf, rtas_data_buf, RTAS_DATA_BUF_SIZE);
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun 	spin_unlock(&rtas_data_buf_lock);
59*4882a593Smuzhiyun 	return rc;
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun 
delete_dt_node(struct device_node * dn)62*4882a593Smuzhiyun static int delete_dt_node(struct device_node *dn)
63*4882a593Smuzhiyun {
64*4882a593Smuzhiyun 	struct device_node *pdn;
65*4882a593Smuzhiyun 	bool is_platfac;
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	pdn = of_get_parent(dn);
68*4882a593Smuzhiyun 	is_platfac = of_node_is_type(dn, "ibm,platform-facilities") ||
69*4882a593Smuzhiyun 		     of_node_is_type(pdn, "ibm,platform-facilities");
70*4882a593Smuzhiyun 	of_node_put(pdn);
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun 	/*
73*4882a593Smuzhiyun 	 * The drivers that bind to nodes in the platform-facilities
74*4882a593Smuzhiyun 	 * hierarchy don't support node removal, and the removal directive
75*4882a593Smuzhiyun 	 * from firmware is always followed by an add of an equivalent
76*4882a593Smuzhiyun 	 * node. The capability (e.g. RNG, encryption, compression)
77*4882a593Smuzhiyun 	 * represented by the node is never interrupted by the migration.
78*4882a593Smuzhiyun 	 * So ignore changes to this part of the tree.
79*4882a593Smuzhiyun 	 */
80*4882a593Smuzhiyun 	if (is_platfac) {
81*4882a593Smuzhiyun 		pr_notice("ignoring remove operation for %pOFfp\n", dn);
82*4882a593Smuzhiyun 		return 0;
83*4882a593Smuzhiyun 	}
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	pr_debug("removing node %pOFfp\n", dn);
86*4882a593Smuzhiyun 	dlpar_detach_node(dn);
87*4882a593Smuzhiyun 	return 0;
88*4882a593Smuzhiyun }
89*4882a593Smuzhiyun 
update_dt_property(struct device_node * dn,struct property ** prop,const char * name,u32 vd,char * value)90*4882a593Smuzhiyun static int update_dt_property(struct device_node *dn, struct property **prop,
91*4882a593Smuzhiyun 			      const char *name, u32 vd, char *value)
92*4882a593Smuzhiyun {
93*4882a593Smuzhiyun 	struct property *new_prop = *prop;
94*4882a593Smuzhiyun 	int more = 0;
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 	/* A negative 'vd' value indicates that only part of the new property
97*4882a593Smuzhiyun 	 * value is contained in the buffer and we need to call
98*4882a593Smuzhiyun 	 * ibm,update-properties again to get the rest of the value.
99*4882a593Smuzhiyun 	 *
100*4882a593Smuzhiyun 	 * A negative value is also the two's compliment of the actual value.
101*4882a593Smuzhiyun 	 */
102*4882a593Smuzhiyun 	if (vd & 0x80000000) {
103*4882a593Smuzhiyun 		vd = ~vd + 1;
104*4882a593Smuzhiyun 		more = 1;
105*4882a593Smuzhiyun 	}
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun 	if (new_prop) {
108*4882a593Smuzhiyun 		/* partial property fixup */
109*4882a593Smuzhiyun 		char *new_data = kzalloc(new_prop->length + vd, GFP_KERNEL);
110*4882a593Smuzhiyun 		if (!new_data)
111*4882a593Smuzhiyun 			return -ENOMEM;
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun 		memcpy(new_data, new_prop->value, new_prop->length);
114*4882a593Smuzhiyun 		memcpy(new_data + new_prop->length, value, vd);
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 		kfree(new_prop->value);
117*4882a593Smuzhiyun 		new_prop->value = new_data;
118*4882a593Smuzhiyun 		new_prop->length += vd;
119*4882a593Smuzhiyun 	} else {
120*4882a593Smuzhiyun 		new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
121*4882a593Smuzhiyun 		if (!new_prop)
122*4882a593Smuzhiyun 			return -ENOMEM;
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun 		new_prop->name = kstrdup(name, GFP_KERNEL);
125*4882a593Smuzhiyun 		if (!new_prop->name) {
126*4882a593Smuzhiyun 			kfree(new_prop);
127*4882a593Smuzhiyun 			return -ENOMEM;
128*4882a593Smuzhiyun 		}
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 		new_prop->length = vd;
131*4882a593Smuzhiyun 		new_prop->value = kzalloc(new_prop->length, GFP_KERNEL);
132*4882a593Smuzhiyun 		if (!new_prop->value) {
133*4882a593Smuzhiyun 			kfree(new_prop->name);
134*4882a593Smuzhiyun 			kfree(new_prop);
135*4882a593Smuzhiyun 			return -ENOMEM;
136*4882a593Smuzhiyun 		}
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 		memcpy(new_prop->value, value, vd);
139*4882a593Smuzhiyun 		*prop = new_prop;
140*4882a593Smuzhiyun 	}
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	if (!more) {
143*4882a593Smuzhiyun 		pr_debug("updating node %pOF property %s\n", dn, name);
144*4882a593Smuzhiyun 		of_update_property(dn, new_prop);
145*4882a593Smuzhiyun 		*prop = NULL;
146*4882a593Smuzhiyun 	}
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	return 0;
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun 
update_dt_node(struct device_node * dn,s32 scope)151*4882a593Smuzhiyun static int update_dt_node(struct device_node *dn, s32 scope)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun 	struct update_props_workarea *upwa;
154*4882a593Smuzhiyun 	struct property *prop = NULL;
155*4882a593Smuzhiyun 	int i, rc, rtas_rc;
156*4882a593Smuzhiyun 	char *prop_data;
157*4882a593Smuzhiyun 	char *rtas_buf;
158*4882a593Smuzhiyun 	int update_properties_token;
159*4882a593Smuzhiyun 	u32 nprops;
160*4882a593Smuzhiyun 	u32 vd;
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	update_properties_token = rtas_token("ibm,update-properties");
163*4882a593Smuzhiyun 	if (update_properties_token == RTAS_UNKNOWN_SERVICE)
164*4882a593Smuzhiyun 		return -EINVAL;
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
167*4882a593Smuzhiyun 	if (!rtas_buf)
168*4882a593Smuzhiyun 		return -ENOMEM;
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	upwa = (struct update_props_workarea *)&rtas_buf[0];
171*4882a593Smuzhiyun 	upwa->phandle = cpu_to_be32(dn->phandle);
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	do {
174*4882a593Smuzhiyun 		rtas_rc = mobility_rtas_call(update_properties_token, rtas_buf,
175*4882a593Smuzhiyun 					scope);
176*4882a593Smuzhiyun 		if (rtas_rc < 0)
177*4882a593Smuzhiyun 			break;
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 		prop_data = rtas_buf + sizeof(*upwa);
180*4882a593Smuzhiyun 		nprops = be32_to_cpu(upwa->nprops);
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 		/* On the first call to ibm,update-properties for a node the
183*4882a593Smuzhiyun 		 * the first property value descriptor contains an empty
184*4882a593Smuzhiyun 		 * property name, the property value length encoded as u32,
185*4882a593Smuzhiyun 		 * and the property value is the node path being updated.
186*4882a593Smuzhiyun 		 */
187*4882a593Smuzhiyun 		if (*prop_data == 0) {
188*4882a593Smuzhiyun 			prop_data++;
189*4882a593Smuzhiyun 			vd = be32_to_cpu(*(__be32 *)prop_data);
190*4882a593Smuzhiyun 			prop_data += vd + sizeof(vd);
191*4882a593Smuzhiyun 			nprops--;
192*4882a593Smuzhiyun 		}
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 		for (i = 0; i < nprops; i++) {
195*4882a593Smuzhiyun 			char *prop_name;
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 			prop_name = prop_data;
198*4882a593Smuzhiyun 			prop_data += strlen(prop_name) + 1;
199*4882a593Smuzhiyun 			vd = be32_to_cpu(*(__be32 *)prop_data);
200*4882a593Smuzhiyun 			prop_data += sizeof(vd);
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 			switch (vd) {
203*4882a593Smuzhiyun 			case 0x00000000:
204*4882a593Smuzhiyun 				/* name only property, nothing to do */
205*4882a593Smuzhiyun 				break;
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun 			case 0x80000000:
208*4882a593Smuzhiyun 				of_remove_property(dn, of_find_property(dn,
209*4882a593Smuzhiyun 							prop_name, NULL));
210*4882a593Smuzhiyun 				prop = NULL;
211*4882a593Smuzhiyun 				break;
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 			default:
214*4882a593Smuzhiyun 				rc = update_dt_property(dn, &prop, prop_name,
215*4882a593Smuzhiyun 							vd, prop_data);
216*4882a593Smuzhiyun 				if (rc) {
217*4882a593Smuzhiyun 					printk(KERN_ERR "Could not update %s"
218*4882a593Smuzhiyun 					       " property\n", prop_name);
219*4882a593Smuzhiyun 				}
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 				prop_data += vd;
222*4882a593Smuzhiyun 			}
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 			cond_resched();
225*4882a593Smuzhiyun 		}
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 		cond_resched();
228*4882a593Smuzhiyun 	} while (rtas_rc == 1);
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 	kfree(rtas_buf);
231*4882a593Smuzhiyun 	return 0;
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun 
add_dt_node(struct device_node * parent_dn,__be32 drc_index)234*4882a593Smuzhiyun static int add_dt_node(struct device_node *parent_dn, __be32 drc_index)
235*4882a593Smuzhiyun {
236*4882a593Smuzhiyun 	struct device_node *dn;
237*4882a593Smuzhiyun 	int rc;
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 	dn = dlpar_configure_connector(drc_index, parent_dn);
240*4882a593Smuzhiyun 	if (!dn)
241*4882a593Smuzhiyun 		return -ENOENT;
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 	/*
244*4882a593Smuzhiyun 	 * Since delete_dt_node() ignores this node type, this is the
245*4882a593Smuzhiyun 	 * necessary counterpart. We also know that a platform-facilities
246*4882a593Smuzhiyun 	 * node returned from dlpar_configure_connector() has children
247*4882a593Smuzhiyun 	 * attached, and dlpar_attach_node() only adds the parent, leaking
248*4882a593Smuzhiyun 	 * the children. So ignore these on the add side for now.
249*4882a593Smuzhiyun 	 */
250*4882a593Smuzhiyun 	if (of_node_is_type(dn, "ibm,platform-facilities")) {
251*4882a593Smuzhiyun 		pr_notice("ignoring add operation for %pOF\n", dn);
252*4882a593Smuzhiyun 		dlpar_free_cc_nodes(dn);
253*4882a593Smuzhiyun 		return 0;
254*4882a593Smuzhiyun 	}
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun 	rc = dlpar_attach_node(dn, parent_dn);
257*4882a593Smuzhiyun 	if (rc)
258*4882a593Smuzhiyun 		dlpar_free_cc_nodes(dn);
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun 	pr_debug("added node %pOFfp\n", dn);
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 	return rc;
263*4882a593Smuzhiyun }
264*4882a593Smuzhiyun 
pseries_devicetree_update(s32 scope)265*4882a593Smuzhiyun int pseries_devicetree_update(s32 scope)
266*4882a593Smuzhiyun {
267*4882a593Smuzhiyun 	char *rtas_buf;
268*4882a593Smuzhiyun 	__be32 *data;
269*4882a593Smuzhiyun 	int update_nodes_token;
270*4882a593Smuzhiyun 	int rc;
271*4882a593Smuzhiyun 
272*4882a593Smuzhiyun 	update_nodes_token = rtas_token("ibm,update-nodes");
273*4882a593Smuzhiyun 	if (update_nodes_token == RTAS_UNKNOWN_SERVICE)
274*4882a593Smuzhiyun 		return -EINVAL;
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun 	rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
277*4882a593Smuzhiyun 	if (!rtas_buf)
278*4882a593Smuzhiyun 		return -ENOMEM;
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun 	do {
281*4882a593Smuzhiyun 		rc = mobility_rtas_call(update_nodes_token, rtas_buf, scope);
282*4882a593Smuzhiyun 		if (rc && rc != 1)
283*4882a593Smuzhiyun 			break;
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun 		data = (__be32 *)rtas_buf + 4;
286*4882a593Smuzhiyun 		while (be32_to_cpu(*data) & NODE_ACTION_MASK) {
287*4882a593Smuzhiyun 			int i;
288*4882a593Smuzhiyun 			u32 action = be32_to_cpu(*data) & NODE_ACTION_MASK;
289*4882a593Smuzhiyun 			u32 node_count = be32_to_cpu(*data) & NODE_COUNT_MASK;
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 			data++;
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 			for (i = 0; i < node_count; i++) {
294*4882a593Smuzhiyun 				struct device_node *np;
295*4882a593Smuzhiyun 				__be32 phandle = *data++;
296*4882a593Smuzhiyun 				__be32 drc_index;
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun 				np = of_find_node_by_phandle(be32_to_cpu(phandle));
299*4882a593Smuzhiyun 				if (!np) {
300*4882a593Smuzhiyun 					pr_warn("Failed lookup: phandle 0x%x for action 0x%x\n",
301*4882a593Smuzhiyun 						be32_to_cpu(phandle), action);
302*4882a593Smuzhiyun 					continue;
303*4882a593Smuzhiyun 				}
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 				switch (action) {
306*4882a593Smuzhiyun 				case DELETE_DT_NODE:
307*4882a593Smuzhiyun 					delete_dt_node(np);
308*4882a593Smuzhiyun 					break;
309*4882a593Smuzhiyun 				case UPDATE_DT_NODE:
310*4882a593Smuzhiyun 					update_dt_node(np, scope);
311*4882a593Smuzhiyun 					break;
312*4882a593Smuzhiyun 				case ADD_DT_NODE:
313*4882a593Smuzhiyun 					drc_index = *data++;
314*4882a593Smuzhiyun 					add_dt_node(np, drc_index);
315*4882a593Smuzhiyun 					break;
316*4882a593Smuzhiyun 				}
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 				of_node_put(np);
319*4882a593Smuzhiyun 				cond_resched();
320*4882a593Smuzhiyun 			}
321*4882a593Smuzhiyun 		}
322*4882a593Smuzhiyun 
323*4882a593Smuzhiyun 		cond_resched();
324*4882a593Smuzhiyun 	} while (rc == 1);
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 	kfree(rtas_buf);
327*4882a593Smuzhiyun 	return rc;
328*4882a593Smuzhiyun }
329*4882a593Smuzhiyun 
post_mobility_fixup(void)330*4882a593Smuzhiyun void post_mobility_fixup(void)
331*4882a593Smuzhiyun {
332*4882a593Smuzhiyun 	int rc;
333*4882a593Smuzhiyun 	int activate_fw_token;
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 	activate_fw_token = rtas_token("ibm,activate-firmware");
336*4882a593Smuzhiyun 	if (activate_fw_token == RTAS_UNKNOWN_SERVICE) {
337*4882a593Smuzhiyun 		printk(KERN_ERR "Could not make post-mobility "
338*4882a593Smuzhiyun 		       "activate-fw call.\n");
339*4882a593Smuzhiyun 		return;
340*4882a593Smuzhiyun 	}
341*4882a593Smuzhiyun 
342*4882a593Smuzhiyun 	do {
343*4882a593Smuzhiyun 		rc = rtas_call(activate_fw_token, 0, 1, NULL);
344*4882a593Smuzhiyun 	} while (rtas_busy_delay(rc));
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun 	if (rc)
347*4882a593Smuzhiyun 		printk(KERN_ERR "Post-mobility activate-fw failed: %d\n", rc);
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	/*
350*4882a593Smuzhiyun 	 * We don't want CPUs to go online/offline while the device
351*4882a593Smuzhiyun 	 * tree is being updated.
352*4882a593Smuzhiyun 	 */
353*4882a593Smuzhiyun 	cpus_read_lock();
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun 	/*
356*4882a593Smuzhiyun 	 * It's common for the destination firmware to replace cache
357*4882a593Smuzhiyun 	 * nodes.  Release all of the cacheinfo hierarchy's references
358*4882a593Smuzhiyun 	 * before updating the device tree.
359*4882a593Smuzhiyun 	 */
360*4882a593Smuzhiyun 	cacheinfo_teardown();
361*4882a593Smuzhiyun 
362*4882a593Smuzhiyun 	rc = pseries_devicetree_update(MIGRATION_SCOPE);
363*4882a593Smuzhiyun 	if (rc)
364*4882a593Smuzhiyun 		printk(KERN_ERR "Post-mobility device tree update "
365*4882a593Smuzhiyun 			"failed: %d\n", rc);
366*4882a593Smuzhiyun 
367*4882a593Smuzhiyun 	cacheinfo_rebuild();
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 	cpus_read_unlock();
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 	/* Possibly switch to a new L1 flush type */
372*4882a593Smuzhiyun 	pseries_setup_security_mitigations();
373*4882a593Smuzhiyun 
374*4882a593Smuzhiyun 	/* Reinitialise system information for hv-24x7 */
375*4882a593Smuzhiyun 	read_24x7_sys_info();
376*4882a593Smuzhiyun 
377*4882a593Smuzhiyun 	return;
378*4882a593Smuzhiyun }
379*4882a593Smuzhiyun 
migration_store(struct class * class,struct class_attribute * attr,const char * buf,size_t count)380*4882a593Smuzhiyun static ssize_t migration_store(struct class *class,
381*4882a593Smuzhiyun 			       struct class_attribute *attr, const char *buf,
382*4882a593Smuzhiyun 			       size_t count)
383*4882a593Smuzhiyun {
384*4882a593Smuzhiyun 	u64 streamid;
385*4882a593Smuzhiyun 	int rc;
386*4882a593Smuzhiyun 
387*4882a593Smuzhiyun 	rc = kstrtou64(buf, 0, &streamid);
388*4882a593Smuzhiyun 	if (rc)
389*4882a593Smuzhiyun 		return rc;
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun 	do {
392*4882a593Smuzhiyun 		rc = rtas_ibm_suspend_me(streamid);
393*4882a593Smuzhiyun 		if (rc == -EAGAIN)
394*4882a593Smuzhiyun 			ssleep(1);
395*4882a593Smuzhiyun 	} while (rc == -EAGAIN);
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 	if (rc)
398*4882a593Smuzhiyun 		return rc;
399*4882a593Smuzhiyun 
400*4882a593Smuzhiyun 	post_mobility_fixup();
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 	return count;
403*4882a593Smuzhiyun }
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun /*
406*4882a593Smuzhiyun  * Used by drmgr to determine the kernel behavior of the migration interface.
407*4882a593Smuzhiyun  *
408*4882a593Smuzhiyun  * Version 1: Performs all PAPR requirements for migration including
409*4882a593Smuzhiyun  *	firmware activation and device tree update.
410*4882a593Smuzhiyun  */
411*4882a593Smuzhiyun #define MIGRATION_API_VERSION	1
412*4882a593Smuzhiyun 
413*4882a593Smuzhiyun static CLASS_ATTR_WO(migration);
414*4882a593Smuzhiyun static CLASS_ATTR_STRING(api_version, 0444, __stringify(MIGRATION_API_VERSION));
415*4882a593Smuzhiyun 
mobility_sysfs_init(void)416*4882a593Smuzhiyun static int __init mobility_sysfs_init(void)
417*4882a593Smuzhiyun {
418*4882a593Smuzhiyun 	int rc;
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	mobility_kobj = kobject_create_and_add("mobility", kernel_kobj);
421*4882a593Smuzhiyun 	if (!mobility_kobj)
422*4882a593Smuzhiyun 		return -ENOMEM;
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 	rc = sysfs_create_file(mobility_kobj, &class_attr_migration.attr);
425*4882a593Smuzhiyun 	if (rc)
426*4882a593Smuzhiyun 		pr_err("unable to create migration sysfs file (%d)\n", rc);
427*4882a593Smuzhiyun 
428*4882a593Smuzhiyun 	rc = sysfs_create_file(mobility_kobj, &class_attr_api_version.attr.attr);
429*4882a593Smuzhiyun 	if (rc)
430*4882a593Smuzhiyun 		pr_err("unable to create api_version sysfs file (%d)\n", rc);
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun 	return 0;
433*4882a593Smuzhiyun }
434*4882a593Smuzhiyun machine_device_initcall(pseries, mobility_sysfs_init);
435