xref: /linux/arch/powerpc/platforms/pseries/mobility.c (revision 6f428096a4d1e1809b162ca40dec5f7d09f3f1d1)
1410bccf9SNathan Fontenot /*
2410bccf9SNathan Fontenot  * Support for Partition Mobility/Migration
3410bccf9SNathan Fontenot  *
4410bccf9SNathan Fontenot  * Copyright (C) 2010 Nathan Fontenot
5410bccf9SNathan Fontenot  * Copyright (C) 2010 IBM Corporation
6410bccf9SNathan Fontenot  *
7410bccf9SNathan Fontenot  * This program is free software; you can redistribute it and/or
8410bccf9SNathan Fontenot  * modify it under the terms of the GNU General Public License version
9410bccf9SNathan Fontenot  * 2 as published by the Free Software Foundation.
10410bccf9SNathan Fontenot  */
11410bccf9SNathan Fontenot 
12410bccf9SNathan Fontenot #include <linux/kernel.h>
13410bccf9SNathan Fontenot #include <linux/kobject.h>
14410bccf9SNathan Fontenot #include <linux/smp.h>
15b56eade5SPaul Gortmaker #include <linux/stat.h>
16410bccf9SNathan Fontenot #include <linux/completion.h>
17410bccf9SNathan Fontenot #include <linux/device.h>
18410bccf9SNathan Fontenot #include <linux/delay.h>
19410bccf9SNathan Fontenot #include <linux/slab.h>
20410bccf9SNathan Fontenot 
218e83e905SMichael Ellerman #include <asm/machdep.h>
22410bccf9SNathan Fontenot #include <asm/rtas.h>
23410bccf9SNathan Fontenot #include "pseries.h"
24410bccf9SNathan Fontenot 
25410bccf9SNathan Fontenot static struct kobject *mobility_kobj;
26410bccf9SNathan Fontenot 
27410bccf9SNathan Fontenot struct update_props_workarea {
28f6ff0414STyrel Datwyler 	__be32 phandle;
29f6ff0414STyrel Datwyler 	__be32 state;
30f6ff0414STyrel Datwyler 	__be64 reserved;
31f6ff0414STyrel Datwyler 	__be32 nprops;
32d0ef4403STyrel Datwyler } __packed;
33410bccf9SNathan Fontenot 
34410bccf9SNathan Fontenot #define NODE_ACTION_MASK	0xff000000
35410bccf9SNathan Fontenot #define NODE_COUNT_MASK		0x00ffffff
36410bccf9SNathan Fontenot 
37410bccf9SNathan Fontenot #define DELETE_DT_NODE	0x01000000
38410bccf9SNathan Fontenot #define UPDATE_DT_NODE	0x02000000
39410bccf9SNathan Fontenot #define ADD_DT_NODE	0x03000000
40410bccf9SNathan Fontenot 
41762ec157SNathan Fontenot #define MIGRATION_SCOPE	(1)
42675d8ee6SJohn Allen #define PRRN_SCOPE -2
43762ec157SNathan Fontenot 
44762ec157SNathan Fontenot static int mobility_rtas_call(int token, char *buf, s32 scope)
45410bccf9SNathan Fontenot {
46410bccf9SNathan Fontenot 	int rc;
47410bccf9SNathan Fontenot 
48410bccf9SNathan Fontenot 	spin_lock(&rtas_data_buf_lock);
49410bccf9SNathan Fontenot 
50410bccf9SNathan Fontenot 	memcpy(rtas_data_buf, buf, RTAS_DATA_BUF_SIZE);
51762ec157SNathan Fontenot 	rc = rtas_call(token, 2, 1, NULL, rtas_data_buf, scope);
52410bccf9SNathan Fontenot 	memcpy(buf, rtas_data_buf, RTAS_DATA_BUF_SIZE);
53410bccf9SNathan Fontenot 
54410bccf9SNathan Fontenot 	spin_unlock(&rtas_data_buf_lock);
55410bccf9SNathan Fontenot 	return rc;
56410bccf9SNathan Fontenot }
57410bccf9SNathan Fontenot 
58f6ff0414STyrel Datwyler static int delete_dt_node(__be32 phandle)
59410bccf9SNathan Fontenot {
60410bccf9SNathan Fontenot 	struct device_node *dn;
61410bccf9SNathan Fontenot 
62f6ff0414STyrel Datwyler 	dn = of_find_node_by_phandle(be32_to_cpu(phandle));
63410bccf9SNathan Fontenot 	if (!dn)
64410bccf9SNathan Fontenot 		return -ENOENT;
65410bccf9SNathan Fontenot 
66410bccf9SNathan Fontenot 	dlpar_detach_node(dn);
6714cd820aSTyrel Datwyler 	of_node_put(dn);
68410bccf9SNathan Fontenot 	return 0;
69410bccf9SNathan Fontenot }
70410bccf9SNathan Fontenot 
71410bccf9SNathan Fontenot static int update_dt_property(struct device_node *dn, struct property **prop,
72410bccf9SNathan Fontenot 			      const char *name, u32 vd, char *value)
73410bccf9SNathan Fontenot {
74410bccf9SNathan Fontenot 	struct property *new_prop = *prop;
75410bccf9SNathan Fontenot 	int more = 0;
76410bccf9SNathan Fontenot 
77410bccf9SNathan Fontenot 	/* A negative 'vd' value indicates that only part of the new property
78410bccf9SNathan Fontenot 	 * value is contained in the buffer and we need to call
79410bccf9SNathan Fontenot 	 * ibm,update-properties again to get the rest of the value.
80410bccf9SNathan Fontenot 	 *
81410bccf9SNathan Fontenot 	 * A negative value is also the two's compliment of the actual value.
82410bccf9SNathan Fontenot 	 */
83410bccf9SNathan Fontenot 	if (vd & 0x80000000) {
84410bccf9SNathan Fontenot 		vd = ~vd + 1;
85410bccf9SNathan Fontenot 		more = 1;
86410bccf9SNathan Fontenot 	}
87410bccf9SNathan Fontenot 
88410bccf9SNathan Fontenot 	if (new_prop) {
89410bccf9SNathan Fontenot 		/* partial property fixup */
90410bccf9SNathan Fontenot 		char *new_data = kzalloc(new_prop->length + vd, GFP_KERNEL);
91410bccf9SNathan Fontenot 		if (!new_data)
92410bccf9SNathan Fontenot 			return -ENOMEM;
93410bccf9SNathan Fontenot 
94410bccf9SNathan Fontenot 		memcpy(new_data, new_prop->value, new_prop->length);
95410bccf9SNathan Fontenot 		memcpy(new_data + new_prop->length, value, vd);
96410bccf9SNathan Fontenot 
97410bccf9SNathan Fontenot 		kfree(new_prop->value);
98410bccf9SNathan Fontenot 		new_prop->value = new_data;
99410bccf9SNathan Fontenot 		new_prop->length += vd;
100410bccf9SNathan Fontenot 	} else {
101410bccf9SNathan Fontenot 		new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
102410bccf9SNathan Fontenot 		if (!new_prop)
103410bccf9SNathan Fontenot 			return -ENOMEM;
104410bccf9SNathan Fontenot 
105410bccf9SNathan Fontenot 		new_prop->name = kstrdup(name, GFP_KERNEL);
106410bccf9SNathan Fontenot 		if (!new_prop->name) {
107410bccf9SNathan Fontenot 			kfree(new_prop);
108410bccf9SNathan Fontenot 			return -ENOMEM;
109410bccf9SNathan Fontenot 		}
110410bccf9SNathan Fontenot 
111410bccf9SNathan Fontenot 		new_prop->length = vd;
112410bccf9SNathan Fontenot 		new_prop->value = kzalloc(new_prop->length, GFP_KERNEL);
113410bccf9SNathan Fontenot 		if (!new_prop->value) {
114410bccf9SNathan Fontenot 			kfree(new_prop->name);
115410bccf9SNathan Fontenot 			kfree(new_prop);
116410bccf9SNathan Fontenot 			return -ENOMEM;
117410bccf9SNathan Fontenot 		}
118410bccf9SNathan Fontenot 
119410bccf9SNathan Fontenot 		memcpy(new_prop->value, value, vd);
120410bccf9SNathan Fontenot 		*prop = new_prop;
121410bccf9SNathan Fontenot 	}
122410bccf9SNathan Fontenot 
123410bccf9SNathan Fontenot 	if (!more) {
12479d1c712SNathan Fontenot 		of_update_property(dn, new_prop);
125d8e533b4STyrel Datwyler 		*prop = NULL;
126410bccf9SNathan Fontenot 	}
127410bccf9SNathan Fontenot 
128410bccf9SNathan Fontenot 	return 0;
129410bccf9SNathan Fontenot }
130410bccf9SNathan Fontenot 
131f6ff0414STyrel Datwyler static int update_dt_node(__be32 phandle, s32 scope)
132410bccf9SNathan Fontenot {
133410bccf9SNathan Fontenot 	struct update_props_workarea *upwa;
134410bccf9SNathan Fontenot 	struct device_node *dn;
135410bccf9SNathan Fontenot 	struct property *prop = NULL;
136638a405fSTyrel Datwyler 	int i, rc, rtas_rc;
137410bccf9SNathan Fontenot 	char *prop_data;
138410bccf9SNathan Fontenot 	char *rtas_buf;
139410bccf9SNathan Fontenot 	int update_properties_token;
140f6ff0414STyrel Datwyler 	u32 nprops;
1412e9b7b02SNathan Fontenot 	u32 vd;
142410bccf9SNathan Fontenot 
143410bccf9SNathan Fontenot 	update_properties_token = rtas_token("ibm,update-properties");
144410bccf9SNathan Fontenot 	if (update_properties_token == RTAS_UNKNOWN_SERVICE)
145410bccf9SNathan Fontenot 		return -EINVAL;
146410bccf9SNathan Fontenot 
147410bccf9SNathan Fontenot 	rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
148410bccf9SNathan Fontenot 	if (!rtas_buf)
149410bccf9SNathan Fontenot 		return -ENOMEM;
150410bccf9SNathan Fontenot 
151f6ff0414STyrel Datwyler 	dn = of_find_node_by_phandle(be32_to_cpu(phandle));
152410bccf9SNathan Fontenot 	if (!dn) {
153410bccf9SNathan Fontenot 		kfree(rtas_buf);
154410bccf9SNathan Fontenot 		return -ENOENT;
155410bccf9SNathan Fontenot 	}
156410bccf9SNathan Fontenot 
157410bccf9SNathan Fontenot 	upwa = (struct update_props_workarea *)&rtas_buf[0];
158410bccf9SNathan Fontenot 	upwa->phandle = phandle;
159410bccf9SNathan Fontenot 
160410bccf9SNathan Fontenot 	do {
161638a405fSTyrel Datwyler 		rtas_rc = mobility_rtas_call(update_properties_token, rtas_buf,
162762ec157SNathan Fontenot 					scope);
163638a405fSTyrel Datwyler 		if (rtas_rc < 0)
164410bccf9SNathan Fontenot 			break;
165410bccf9SNathan Fontenot 
166410bccf9SNathan Fontenot 		prop_data = rtas_buf + sizeof(*upwa);
167f6ff0414STyrel Datwyler 		nprops = be32_to_cpu(upwa->nprops);
168410bccf9SNathan Fontenot 
169c8f5a57cSTyrel Datwyler 		/* On the first call to ibm,update-properties for a node the
170c8f5a57cSTyrel Datwyler 		 * the first property value descriptor contains an empty
171c8f5a57cSTyrel Datwyler 		 * property name, the property value length encoded as u32,
172c8f5a57cSTyrel Datwyler 		 * and the property value is the node path being updated.
1732e9b7b02SNathan Fontenot 		 */
174c8f5a57cSTyrel Datwyler 		if (*prop_data == 0) {
175c8f5a57cSTyrel Datwyler 			prop_data++;
176f6ff0414STyrel Datwyler 			vd = be32_to_cpu(*(__be32 *)prop_data);
177c8f5a57cSTyrel Datwyler 			prop_data += vd + sizeof(vd);
178f6ff0414STyrel Datwyler 			nprops--;
179c8f5a57cSTyrel Datwyler 		}
1802e9b7b02SNathan Fontenot 
181f6ff0414STyrel Datwyler 		for (i = 0; i < nprops; i++) {
1822e9b7b02SNathan Fontenot 			char *prop_name;
1832e9b7b02SNathan Fontenot 
1842e9b7b02SNathan Fontenot 			prop_name = prop_data;
1852e9b7b02SNathan Fontenot 			prop_data += strlen(prop_name) + 1;
186f6ff0414STyrel Datwyler 			vd = be32_to_cpu(*(__be32 *)prop_data);
1872e9b7b02SNathan Fontenot 			prop_data += sizeof(vd);
188410bccf9SNathan Fontenot 
189410bccf9SNathan Fontenot 			switch (vd) {
190410bccf9SNathan Fontenot 			case 0x00000000:
191410bccf9SNathan Fontenot 				/* name only property, nothing to do */
192410bccf9SNathan Fontenot 				break;
193410bccf9SNathan Fontenot 
194410bccf9SNathan Fontenot 			case 0x80000000:
195925e2d1dSSuraj Jitindar Singh 				of_remove_property(dn, of_find_property(dn,
196925e2d1dSSuraj Jitindar Singh 							prop_name, NULL));
197410bccf9SNathan Fontenot 				prop = NULL;
198410bccf9SNathan Fontenot 				break;
199410bccf9SNathan Fontenot 
200410bccf9SNathan Fontenot 			default:
201410bccf9SNathan Fontenot 				rc = update_dt_property(dn, &prop, prop_name,
202410bccf9SNathan Fontenot 							vd, prop_data);
203410bccf9SNathan Fontenot 				if (rc) {
204410bccf9SNathan Fontenot 					printk(KERN_ERR "Could not update %s"
205410bccf9SNathan Fontenot 					       " property\n", prop_name);
206410bccf9SNathan Fontenot 				}
207410bccf9SNathan Fontenot 
208410bccf9SNathan Fontenot 				prop_data += vd;
209410bccf9SNathan Fontenot 			}
210410bccf9SNathan Fontenot 		}
211638a405fSTyrel Datwyler 	} while (rtas_rc == 1);
212410bccf9SNathan Fontenot 
213410bccf9SNathan Fontenot 	of_node_put(dn);
214410bccf9SNathan Fontenot 	kfree(rtas_buf);
215410bccf9SNathan Fontenot 	return 0;
216410bccf9SNathan Fontenot }
217410bccf9SNathan Fontenot 
218f6ff0414STyrel Datwyler static int add_dt_node(__be32 parent_phandle, __be32 drc_index)
219410bccf9SNathan Fontenot {
220410bccf9SNathan Fontenot 	struct device_node *dn;
221410bccf9SNathan Fontenot 	struct device_node *parent_dn;
222410bccf9SNathan Fontenot 	int rc;
223410bccf9SNathan Fontenot 
224f6ff0414STyrel Datwyler 	parent_dn = of_find_node_by_phandle(be32_to_cpu(parent_phandle));
2258d5ff320STyrel Datwyler 	if (!parent_dn)
2268d5ff320STyrel Datwyler 		return -ENOENT;
2278d5ff320STyrel Datwyler 
2288d5ff320STyrel Datwyler 	dn = dlpar_configure_connector(drc_index, parent_dn);
229410bccf9SNathan Fontenot 	if (!dn)
230410bccf9SNathan Fontenot 		return -ENOENT;
231410bccf9SNathan Fontenot 
232410bccf9SNathan Fontenot 	rc = dlpar_attach_node(dn);
233410bccf9SNathan Fontenot 	if (rc)
234410bccf9SNathan Fontenot 		dlpar_free_cc_nodes(dn);
235410bccf9SNathan Fontenot 
236410bccf9SNathan Fontenot 	of_node_put(parent_dn);
237410bccf9SNathan Fontenot 	return rc;
238410bccf9SNathan Fontenot }
239410bccf9SNathan Fontenot 
240675d8ee6SJohn Allen static void prrn_update_node(__be32 phandle)
241675d8ee6SJohn Allen {
242675d8ee6SJohn Allen 	struct pseries_hp_errorlog *hp_elog;
243675d8ee6SJohn Allen 	struct device_node *dn;
244675d8ee6SJohn Allen 
245675d8ee6SJohn Allen 	/*
246675d8ee6SJohn Allen 	 * If a node is found from a the given phandle, the phandle does not
247675d8ee6SJohn Allen 	 * represent the drc index of an LMB and we can ignore.
248675d8ee6SJohn Allen 	 */
249675d8ee6SJohn Allen 	dn = of_find_node_by_phandle(be32_to_cpu(phandle));
250675d8ee6SJohn Allen 	if (dn) {
251675d8ee6SJohn Allen 		of_node_put(dn);
252675d8ee6SJohn Allen 		return;
253675d8ee6SJohn Allen 	}
254675d8ee6SJohn Allen 
255675d8ee6SJohn Allen 	hp_elog = kzalloc(sizeof(*hp_elog), GFP_KERNEL);
256675d8ee6SJohn Allen 	if(!hp_elog)
257675d8ee6SJohn Allen 		return;
258675d8ee6SJohn Allen 
259675d8ee6SJohn Allen 	hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_MEM;
260675d8ee6SJohn Allen 	hp_elog->action = PSERIES_HP_ELOG_ACTION_READD;
261675d8ee6SJohn Allen 	hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_INDEX;
262675d8ee6SJohn Allen 	hp_elog->_drc_u.drc_index = phandle;
263675d8ee6SJohn Allen 
264675d8ee6SJohn Allen 	queue_hotplug_event(hp_elog, NULL, NULL);
265675d8ee6SJohn Allen 
266675d8ee6SJohn Allen 	kfree(hp_elog);
267675d8ee6SJohn Allen }
268675d8ee6SJohn Allen 
269762ec157SNathan Fontenot int pseries_devicetree_update(s32 scope)
270410bccf9SNathan Fontenot {
271410bccf9SNathan Fontenot 	char *rtas_buf;
272f6ff0414STyrel Datwyler 	__be32 *data;
273410bccf9SNathan Fontenot 	int update_nodes_token;
274410bccf9SNathan Fontenot 	int rc;
275410bccf9SNathan Fontenot 
276410bccf9SNathan Fontenot 	update_nodes_token = rtas_token("ibm,update-nodes");
277410bccf9SNathan Fontenot 	if (update_nodes_token == RTAS_UNKNOWN_SERVICE)
278410bccf9SNathan Fontenot 		return -EINVAL;
279410bccf9SNathan Fontenot 
280410bccf9SNathan Fontenot 	rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
281410bccf9SNathan Fontenot 	if (!rtas_buf)
282410bccf9SNathan Fontenot 		return -ENOMEM;
283410bccf9SNathan Fontenot 
284410bccf9SNathan Fontenot 	do {
285762ec157SNathan Fontenot 		rc = mobility_rtas_call(update_nodes_token, rtas_buf, scope);
286410bccf9SNathan Fontenot 		if (rc && rc != 1)
287410bccf9SNathan Fontenot 			break;
288410bccf9SNathan Fontenot 
289f6ff0414STyrel Datwyler 		data = (__be32 *)rtas_buf + 4;
290f6ff0414STyrel Datwyler 		while (be32_to_cpu(*data) & NODE_ACTION_MASK) {
291410bccf9SNathan Fontenot 			int i;
292f6ff0414STyrel Datwyler 			u32 action = be32_to_cpu(*data) & NODE_ACTION_MASK;
293f6ff0414STyrel Datwyler 			u32 node_count = be32_to_cpu(*data) & NODE_COUNT_MASK;
294410bccf9SNathan Fontenot 
295410bccf9SNathan Fontenot 			data++;
296410bccf9SNathan Fontenot 
297410bccf9SNathan Fontenot 			for (i = 0; i < node_count; i++) {
298f6ff0414STyrel Datwyler 				__be32 phandle = *data++;
299f6ff0414STyrel Datwyler 				__be32 drc_index;
300410bccf9SNathan Fontenot 
301410bccf9SNathan Fontenot 				switch (action) {
302410bccf9SNathan Fontenot 				case DELETE_DT_NODE:
303410bccf9SNathan Fontenot 					delete_dt_node(phandle);
304410bccf9SNathan Fontenot 					break;
305410bccf9SNathan Fontenot 				case UPDATE_DT_NODE:
306762ec157SNathan Fontenot 					update_dt_node(phandle, scope);
307675d8ee6SJohn Allen 
308675d8ee6SJohn Allen 					if (scope == PRRN_SCOPE)
309675d8ee6SJohn Allen 						prrn_update_node(phandle);
310675d8ee6SJohn Allen 
311410bccf9SNathan Fontenot 					break;
312410bccf9SNathan Fontenot 				case ADD_DT_NODE:
313410bccf9SNathan Fontenot 					drc_index = *data++;
314410bccf9SNathan Fontenot 					add_dt_node(phandle, drc_index);
315410bccf9SNathan Fontenot 					break;
316410bccf9SNathan Fontenot 				}
317410bccf9SNathan Fontenot 			}
318410bccf9SNathan Fontenot 		}
319410bccf9SNathan Fontenot 	} while (rc == 1);
320410bccf9SNathan Fontenot 
321410bccf9SNathan Fontenot 	kfree(rtas_buf);
322410bccf9SNathan Fontenot 	return rc;
323410bccf9SNathan Fontenot }
324410bccf9SNathan Fontenot 
325410bccf9SNathan Fontenot void post_mobility_fixup(void)
326410bccf9SNathan Fontenot {
327410bccf9SNathan Fontenot 	int rc;
328410bccf9SNathan Fontenot 	int activate_fw_token;
329410bccf9SNathan Fontenot 
330410bccf9SNathan Fontenot 	activate_fw_token = rtas_token("ibm,activate-firmware");
331410bccf9SNathan Fontenot 	if (activate_fw_token == RTAS_UNKNOWN_SERVICE) {
332410bccf9SNathan Fontenot 		printk(KERN_ERR "Could not make post-mobility "
333410bccf9SNathan Fontenot 		       "activate-fw call.\n");
334410bccf9SNathan Fontenot 		return;
335410bccf9SNathan Fontenot 	}
336410bccf9SNathan Fontenot 
33739a33b59SHaren Myneni 	do {
338410bccf9SNathan Fontenot 		rc = rtas_call(activate_fw_token, 0, 1, NULL);
33939a33b59SHaren Myneni 	} while (rtas_busy_delay(rc));
34039a33b59SHaren Myneni 
34139a33b59SHaren Myneni 	if (rc)
34239a33b59SHaren Myneni 		printk(KERN_ERR "Post-mobility activate-fw failed: %d\n", rc);
34339a33b59SHaren Myneni 
344762ec157SNathan Fontenot 	rc = pseries_devicetree_update(MIGRATION_SCOPE);
345410bccf9SNathan Fontenot 	if (rc)
34639a33b59SHaren Myneni 		printk(KERN_ERR "Post-mobility device tree update "
34739a33b59SHaren Myneni 			"failed: %d\n", rc);
348410bccf9SNathan Fontenot 
349410bccf9SNathan Fontenot 	return;
350410bccf9SNathan Fontenot }
351410bccf9SNathan Fontenot 
352*6f428096SGreg Kroah-Hartman static ssize_t migration_store(struct class *class,
353*6f428096SGreg Kroah-Hartman 			       struct class_attribute *attr, const char *buf,
354*6f428096SGreg Kroah-Hartman 			       size_t count)
355410bccf9SNathan Fontenot {
356410bccf9SNathan Fontenot 	u64 streamid;
357410bccf9SNathan Fontenot 	int rc;
358410bccf9SNathan Fontenot 
3591618bd53SDaniel Walter 	rc = kstrtou64(buf, 0, &streamid);
360410bccf9SNathan Fontenot 	if (rc)
361410bccf9SNathan Fontenot 		return rc;
362410bccf9SNathan Fontenot 
363410bccf9SNathan Fontenot 	do {
364c03e7374STyrel Datwyler 		rc = rtas_ibm_suspend_me(streamid);
365c03e7374STyrel Datwyler 		if (rc == -EAGAIN)
366410bccf9SNathan Fontenot 			ssleep(1);
367c03e7374STyrel Datwyler 	} while (rc == -EAGAIN);
368410bccf9SNathan Fontenot 
369410bccf9SNathan Fontenot 	if (rc)
370410bccf9SNathan Fontenot 		return rc;
371410bccf9SNathan Fontenot 
372410bccf9SNathan Fontenot 	post_mobility_fixup();
373410bccf9SNathan Fontenot 	return count;
374410bccf9SNathan Fontenot }
375410bccf9SNathan Fontenot 
376288a298cSTyrel Datwyler /*
377288a298cSTyrel Datwyler  * Used by drmgr to determine the kernel behavior of the migration interface.
378288a298cSTyrel Datwyler  *
379288a298cSTyrel Datwyler  * Version 1: Performs all PAPR requirements for migration including
380288a298cSTyrel Datwyler  *	firmware activation and device tree update.
381288a298cSTyrel Datwyler  */
382288a298cSTyrel Datwyler #define MIGRATION_API_VERSION	1
383288a298cSTyrel Datwyler 
384*6f428096SGreg Kroah-Hartman static CLASS_ATTR_WO(migration);
385288a298cSTyrel Datwyler static CLASS_ATTR_STRING(api_version, S_IRUGO, __stringify(MIGRATION_API_VERSION));
386410bccf9SNathan Fontenot 
387410bccf9SNathan Fontenot static int __init mobility_sysfs_init(void)
388410bccf9SNathan Fontenot {
389410bccf9SNathan Fontenot 	int rc;
390410bccf9SNathan Fontenot 
391410bccf9SNathan Fontenot 	mobility_kobj = kobject_create_and_add("mobility", kernel_kobj);
392410bccf9SNathan Fontenot 	if (!mobility_kobj)
393410bccf9SNathan Fontenot 		return -ENOMEM;
394410bccf9SNathan Fontenot 
395410bccf9SNathan Fontenot 	rc = sysfs_create_file(mobility_kobj, &class_attr_migration.attr);
396288a298cSTyrel Datwyler 	if (rc)
397288a298cSTyrel Datwyler 		pr_err("mobility: unable to create migration sysfs file (%d)\n", rc);
398410bccf9SNathan Fontenot 
399288a298cSTyrel Datwyler 	rc = sysfs_create_file(mobility_kobj, &class_attr_api_version.attr.attr);
400288a298cSTyrel Datwyler 	if (rc)
401288a298cSTyrel Datwyler 		pr_err("mobility: unable to create api_version sysfs file (%d)\n", rc);
402288a298cSTyrel Datwyler 
403288a298cSTyrel Datwyler 	return 0;
404410bccf9SNathan Fontenot }
4058e83e905SMichael Ellerman machine_device_initcall(pseries, mobility_sysfs_init);
406