]> git.karo-electronics.de Git - karo-tx-linux.git/blob - arch/powerpc/platforms/pseries/mobility.c
Merge tag 'ep93xx-fixes-for-3.8' of git://github.com/RyanMallon/linux-ep93xx into...
[karo-tx-linux.git] / arch / powerpc / platforms / pseries / mobility.c
1 /*
2  * Support for Partition Mobility/Migration
3  *
4  * Copyright (C) 2010 Nathan Fontenot
5  * Copyright (C) 2010 IBM Corporation
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License version
9  * 2 as published by the Free Software Foundation.
10  */
11
12 #include <linux/kernel.h>
13 #include <linux/kobject.h>
14 #include <linux/smp.h>
15 #include <linux/stat.h>
16 #include <linux/completion.h>
17 #include <linux/device.h>
18 #include <linux/delay.h>
19 #include <linux/slab.h>
20
21 #include <asm/rtas.h>
22 #include "pseries.h"
23
24 static struct kobject *mobility_kobj;
25
26 struct update_props_workarea {
27         u32 phandle;
28         u32 state;
29         u64 reserved;
30         u32 nprops;
31 };
32
33 #define NODE_ACTION_MASK        0xff000000
34 #define NODE_COUNT_MASK         0x00ffffff
35
36 #define DELETE_DT_NODE  0x01000000
37 #define UPDATE_DT_NODE  0x02000000
38 #define ADD_DT_NODE     0x03000000
39
40 static int mobility_rtas_call(int token, char *buf)
41 {
42         int rc;
43
44         spin_lock(&rtas_data_buf_lock);
45
46         memcpy(rtas_data_buf, buf, RTAS_DATA_BUF_SIZE);
47         rc = rtas_call(token, 2, 1, NULL, rtas_data_buf, 1);
48         memcpy(buf, rtas_data_buf, RTAS_DATA_BUF_SIZE);
49
50         spin_unlock(&rtas_data_buf_lock);
51         return rc;
52 }
53
54 static int delete_dt_node(u32 phandle)
55 {
56         struct device_node *dn;
57
58         dn = of_find_node_by_phandle(phandle);
59         if (!dn)
60                 return -ENOENT;
61
62         dlpar_detach_node(dn);
63         return 0;
64 }
65
66 static int update_dt_property(struct device_node *dn, struct property **prop,
67                               const char *name, u32 vd, char *value)
68 {
69         struct property *new_prop = *prop;
70         int more = 0;
71
72         /* A negative 'vd' value indicates that only part of the new property
73          * value is contained in the buffer and we need to call
74          * ibm,update-properties again to get the rest of the value.
75          *
76          * A negative value is also the two's compliment of the actual value.
77          */
78         if (vd & 0x80000000) {
79                 vd = ~vd + 1;
80                 more = 1;
81         }
82
83         if (new_prop) {
84                 /* partial property fixup */
85                 char *new_data = kzalloc(new_prop->length + vd, GFP_KERNEL);
86                 if (!new_data)
87                         return -ENOMEM;
88
89                 memcpy(new_data, new_prop->value, new_prop->length);
90                 memcpy(new_data + new_prop->length, value, vd);
91
92                 kfree(new_prop->value);
93                 new_prop->value = new_data;
94                 new_prop->length += vd;
95         } else {
96                 new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
97                 if (!new_prop)
98                         return -ENOMEM;
99
100                 new_prop->name = kstrdup(name, GFP_KERNEL);
101                 if (!new_prop->name) {
102                         kfree(new_prop);
103                         return -ENOMEM;
104                 }
105
106                 new_prop->length = vd;
107                 new_prop->value = kzalloc(new_prop->length, GFP_KERNEL);
108                 if (!new_prop->value) {
109                         kfree(new_prop->name);
110                         kfree(new_prop);
111                         return -ENOMEM;
112                 }
113
114                 memcpy(new_prop->value, value, vd);
115                 *prop = new_prop;
116         }
117
118         if (!more) {
119                 prom_update_property(dn, new_prop);
120                 new_prop = NULL;
121         }
122
123         return 0;
124 }
125
126 static int update_dt_node(u32 phandle)
127 {
128         struct update_props_workarea *upwa;
129         struct device_node *dn;
130         struct property *prop = NULL;
131         int i, rc;
132         char *prop_data;
133         char *rtas_buf;
134         int update_properties_token;
135
136         update_properties_token = rtas_token("ibm,update-properties");
137         if (update_properties_token == RTAS_UNKNOWN_SERVICE)
138                 return -EINVAL;
139
140         rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
141         if (!rtas_buf)
142                 return -ENOMEM;
143
144         dn = of_find_node_by_phandle(phandle);
145         if (!dn) {
146                 kfree(rtas_buf);
147                 return -ENOENT;
148         }
149
150         upwa = (struct update_props_workarea *)&rtas_buf[0];
151         upwa->phandle = phandle;
152
153         do {
154                 rc = mobility_rtas_call(update_properties_token, rtas_buf);
155                 if (rc < 0)
156                         break;
157
158                 prop_data = rtas_buf + sizeof(*upwa);
159
160                 for (i = 0; i < upwa->nprops; i++) {
161                         char *prop_name;
162                         u32 vd;
163
164                         prop_name = prop_data + 1;
165                         prop_data += strlen(prop_name) + 1;
166                         vd = *prop_data++;
167
168                         switch (vd) {
169                         case 0x00000000:
170                                 /* name only property, nothing to do */
171                                 break;
172
173                         case 0x80000000:
174                                 prop = of_find_property(dn, prop_name, NULL);
175                                 prom_remove_property(dn, prop);
176                                 prop = NULL;
177                                 break;
178
179                         default:
180                                 rc = update_dt_property(dn, &prop, prop_name,
181                                                         vd, prop_data);
182                                 if (rc) {
183                                         printk(KERN_ERR "Could not update %s"
184                                                " property\n", prop_name);
185                                 }
186
187                                 prop_data += vd;
188                         }
189                 }
190         } while (rc == 1);
191
192         of_node_put(dn);
193         kfree(rtas_buf);
194         return 0;
195 }
196
197 static int add_dt_node(u32 parent_phandle, u32 drc_index)
198 {
199         struct device_node *dn;
200         struct device_node *parent_dn;
201         int rc;
202
203         dn = dlpar_configure_connector(drc_index);
204         if (!dn)
205                 return -ENOENT;
206
207         parent_dn = of_find_node_by_phandle(parent_phandle);
208         if (!parent_dn) {
209                 dlpar_free_cc_nodes(dn);
210                 return -ENOENT;
211         }
212
213         dn->parent = parent_dn;
214         rc = dlpar_attach_node(dn);
215         if (rc)
216                 dlpar_free_cc_nodes(dn);
217
218         of_node_put(parent_dn);
219         return rc;
220 }
221
222 static int pseries_devicetree_update(void)
223 {
224         char *rtas_buf;
225         u32 *data;
226         int update_nodes_token;
227         int rc;
228
229         update_nodes_token = rtas_token("ibm,update-nodes");
230         if (update_nodes_token == RTAS_UNKNOWN_SERVICE)
231                 return -EINVAL;
232
233         rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
234         if (!rtas_buf)
235                 return -ENOMEM;
236
237         do {
238                 rc = mobility_rtas_call(update_nodes_token, rtas_buf);
239                 if (rc && rc != 1)
240                         break;
241
242                 data = (u32 *)rtas_buf + 4;
243                 while (*data & NODE_ACTION_MASK) {
244                         int i;
245                         u32 action = *data & NODE_ACTION_MASK;
246                         int node_count = *data & NODE_COUNT_MASK;
247
248                         data++;
249
250                         for (i = 0; i < node_count; i++) {
251                                 u32 phandle = *data++;
252                                 u32 drc_index;
253
254                                 switch (action) {
255                                 case DELETE_DT_NODE:
256                                         delete_dt_node(phandle);
257                                         break;
258                                 case UPDATE_DT_NODE:
259                                         update_dt_node(phandle);
260                                         break;
261                                 case ADD_DT_NODE:
262                                         drc_index = *data++;
263                                         add_dt_node(phandle, drc_index);
264                                         break;
265                                 }
266                         }
267                 }
268         } while (rc == 1);
269
270         kfree(rtas_buf);
271         return rc;
272 }
273
274 void post_mobility_fixup(void)
275 {
276         int rc;
277         int activate_fw_token;
278
279         rc = pseries_devicetree_update();
280         if (rc) {
281                 printk(KERN_ERR "Initial post-mobility device tree update "
282                        "failed: %d\n", rc);
283                 return;
284         }
285
286         activate_fw_token = rtas_token("ibm,activate-firmware");
287         if (activate_fw_token == RTAS_UNKNOWN_SERVICE) {
288                 printk(KERN_ERR "Could not make post-mobility "
289                        "activate-fw call.\n");
290                 return;
291         }
292
293         rc = rtas_call(activate_fw_token, 0, 1, NULL);
294         if (!rc) {
295                 rc = pseries_devicetree_update();
296                 if (rc)
297                         printk(KERN_ERR "Secondary post-mobility device tree "
298                                "update failed: %d\n", rc);
299         } else {
300                 printk(KERN_ERR "Post-mobility activate-fw failed: %d\n", rc);
301                 return;
302         }
303
304         return;
305 }
306
307 static ssize_t migrate_store(struct class *class, struct class_attribute *attr,
308                              const char *buf, size_t count)
309 {
310         struct rtas_args args;
311         u64 streamid;
312         int rc;
313
314         rc = strict_strtoull(buf, 0, &streamid);
315         if (rc)
316                 return rc;
317
318         memset(&args, 0, sizeof(args));
319         args.token = rtas_token("ibm,suspend-me");
320         args.nargs = 2;
321         args.nret = 1;
322
323         args.args[0] = streamid >> 32 ;
324         args.args[1] = streamid & 0xffffffff;
325         args.rets = &args.args[args.nargs];
326
327         do {
328                 args.rets[0] = 0;
329                 rc = rtas_ibm_suspend_me(&args);
330                 if (!rc && args.rets[0] == RTAS_NOT_SUSPENDABLE)
331                         ssleep(1);
332         } while (!rc && args.rets[0] == RTAS_NOT_SUSPENDABLE);
333
334         if (rc)
335                 return rc;
336         else if (args.rets[0])
337                 return args.rets[0];
338
339         post_mobility_fixup();
340         return count;
341 }
342
343 static CLASS_ATTR(migration, S_IWUSR, NULL, migrate_store);
344
345 static int __init mobility_sysfs_init(void)
346 {
347         int rc;
348
349         mobility_kobj = kobject_create_and_add("mobility", kernel_kobj);
350         if (!mobility_kobj)
351                 return -ENOMEM;
352
353         rc = sysfs_create_file(mobility_kobj, &class_attr_migration.attr);
354
355         return rc;
356 }
357 device_initcall(mobility_sysfs_init);