]> rtime.felk.cvut.cz Git - mcf548x/linux.git/blob - arch/powerpc/platforms/pseries/mobility.c
Initial 2.6.37
[mcf548x/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/completion.h>
16 #include <linux/device.h>
17 #include <linux/delay.h>
18 #include <linux/slab.h>
19
20 #include <asm/rtas.h>
21 #include "pseries.h"
22
23 static struct kobject *mobility_kobj;
24
25 struct update_props_workarea {
26         u32 phandle;
27         u32 state;
28         u64 reserved;
29         u32 nprops;
30 };
31
32 #define NODE_ACTION_MASK        0xff000000
33 #define NODE_COUNT_MASK         0x00ffffff
34
35 #define DELETE_DT_NODE  0x01000000
36 #define UPDATE_DT_NODE  0x02000000
37 #define ADD_DT_NODE     0x03000000
38
39 static int mobility_rtas_call(int token, char *buf)
40 {
41         int rc;
42
43         spin_lock(&rtas_data_buf_lock);
44
45         memcpy(rtas_data_buf, buf, RTAS_DATA_BUF_SIZE);
46         rc = rtas_call(token, 2, 1, NULL, rtas_data_buf, 1);
47         memcpy(buf, rtas_data_buf, RTAS_DATA_BUF_SIZE);
48
49         spin_unlock(&rtas_data_buf_lock);
50         return rc;
51 }
52
53 static int delete_dt_node(u32 phandle)
54 {
55         struct device_node *dn;
56
57         dn = of_find_node_by_phandle(phandle);
58         if (!dn)
59                 return -ENOENT;
60
61         dlpar_detach_node(dn);
62         return 0;
63 }
64
65 static int update_dt_property(struct device_node *dn, struct property **prop,
66                               const char *name, u32 vd, char *value)
67 {
68         struct property *new_prop = *prop;
69         struct property *old_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                 old_prop = of_find_property(dn, new_prop->name, NULL);
120                 if (old_prop)
121                         prom_update_property(dn, new_prop, old_prop);
122                 else
123                         prom_add_property(dn, new_prop);
124
125                 new_prop = NULL;
126         }
127
128         return 0;
129 }
130
131 static int update_dt_node(u32 phandle)
132 {
133         struct update_props_workarea *upwa;
134         struct device_node *dn;
135         struct property *prop = NULL;
136         int i, rc;
137         char *prop_data;
138         char *rtas_buf;
139         int update_properties_token;
140
141         update_properties_token = rtas_token("ibm,update-properties");
142         if (update_properties_token == RTAS_UNKNOWN_SERVICE)
143                 return -EINVAL;
144
145         rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
146         if (!rtas_buf)
147                 return -ENOMEM;
148
149         dn = of_find_node_by_phandle(phandle);
150         if (!dn) {
151                 kfree(rtas_buf);
152                 return -ENOENT;
153         }
154
155         upwa = (struct update_props_workarea *)&rtas_buf[0];
156         upwa->phandle = phandle;
157
158         do {
159                 rc = mobility_rtas_call(update_properties_token, rtas_buf);
160                 if (rc < 0)
161                         break;
162
163                 prop_data = rtas_buf + sizeof(*upwa);
164
165                 for (i = 0; i < upwa->nprops; i++) {
166                         char *prop_name;
167                         u32 vd;
168
169                         prop_name = prop_data + 1;
170                         prop_data += strlen(prop_name) + 1;
171                         vd = *prop_data++;
172
173                         switch (vd) {
174                         case 0x00000000:
175                                 /* name only property, nothing to do */
176                                 break;
177
178                         case 0x80000000:
179                                 prop = of_find_property(dn, prop_name, NULL);
180                                 prom_remove_property(dn, prop);
181                                 prop = NULL;
182                                 break;
183
184                         default:
185                                 rc = update_dt_property(dn, &prop, prop_name,
186                                                         vd, prop_data);
187                                 if (rc) {
188                                         printk(KERN_ERR "Could not update %s"
189                                                " property\n", prop_name);
190                                 }
191
192                                 prop_data += vd;
193                         }
194                 }
195         } while (rc == 1);
196
197         of_node_put(dn);
198         kfree(rtas_buf);
199         return 0;
200 }
201
202 static int add_dt_node(u32 parent_phandle, u32 drc_index)
203 {
204         struct device_node *dn;
205         struct device_node *parent_dn;
206         int rc;
207
208         dn = dlpar_configure_connector(drc_index);
209         if (!dn)
210                 return -ENOENT;
211
212         parent_dn = of_find_node_by_phandle(parent_phandle);
213         if (!parent_dn) {
214                 dlpar_free_cc_nodes(dn);
215                 return -ENOENT;
216         }
217
218         dn->parent = parent_dn;
219         rc = dlpar_attach_node(dn);
220         if (rc)
221                 dlpar_free_cc_nodes(dn);
222
223         of_node_put(parent_dn);
224         return rc;
225 }
226
227 static int pseries_devicetree_update(void)
228 {
229         char *rtas_buf;
230         u32 *data;
231         int update_nodes_token;
232         int rc;
233
234         update_nodes_token = rtas_token("ibm,update-nodes");
235         if (update_nodes_token == RTAS_UNKNOWN_SERVICE)
236                 return -EINVAL;
237
238         rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
239         if (!rtas_buf)
240                 return -ENOMEM;
241
242         do {
243                 rc = mobility_rtas_call(update_nodes_token, rtas_buf);
244                 if (rc && rc != 1)
245                         break;
246
247                 data = (u32 *)rtas_buf + 4;
248                 while (*data & NODE_ACTION_MASK) {
249                         int i;
250                         u32 action = *data & NODE_ACTION_MASK;
251                         int node_count = *data & NODE_COUNT_MASK;
252
253                         data++;
254
255                         for (i = 0; i < node_count; i++) {
256                                 u32 phandle = *data++;
257                                 u32 drc_index;
258
259                                 switch (action) {
260                                 case DELETE_DT_NODE:
261                                         delete_dt_node(phandle);
262                                         break;
263                                 case UPDATE_DT_NODE:
264                                         update_dt_node(phandle);
265                                         break;
266                                 case ADD_DT_NODE:
267                                         drc_index = *data++;
268                                         add_dt_node(phandle, drc_index);
269                                         break;
270                                 }
271                         }
272                 }
273         } while (rc == 1);
274
275         kfree(rtas_buf);
276         return rc;
277 }
278
279 void post_mobility_fixup(void)
280 {
281         int rc;
282         int activate_fw_token;
283
284         rc = pseries_devicetree_update();
285         if (rc) {
286                 printk(KERN_ERR "Initial post-mobility device tree update "
287                        "failed: %d\n", rc);
288                 return;
289         }
290
291         activate_fw_token = rtas_token("ibm,activate-firmware");
292         if (activate_fw_token == RTAS_UNKNOWN_SERVICE) {
293                 printk(KERN_ERR "Could not make post-mobility "
294                        "activate-fw call.\n");
295                 return;
296         }
297
298         rc = rtas_call(activate_fw_token, 0, 1, NULL);
299         if (!rc) {
300                 rc = pseries_devicetree_update();
301                 if (rc)
302                         printk(KERN_ERR "Secondary post-mobility device tree "
303                                "update failed: %d\n", rc);
304         } else {
305                 printk(KERN_ERR "Post-mobility activate-fw failed: %d\n", rc);
306                 return;
307         }
308
309         return;
310 }
311
312 static ssize_t migrate_store(struct class *class, struct class_attribute *attr,
313                              const char *buf, size_t count)
314 {
315         struct rtas_args args;
316         u64 streamid;
317         int rc;
318
319         rc = strict_strtoull(buf, 0, &streamid);
320         if (rc)
321                 return rc;
322
323         memset(&args, 0, sizeof(args));
324         args.token = rtas_token("ibm,suspend-me");
325         args.nargs = 2;
326         args.nret = 1;
327
328         args.args[0] = streamid >> 32 ;
329         args.args[1] = streamid & 0xffffffff;
330         args.rets = &args.args[args.nargs];
331
332         do {
333                 args.rets[0] = 0;
334                 rc = rtas_ibm_suspend_me(&args);
335                 if (!rc && args.rets[0] == RTAS_NOT_SUSPENDABLE)
336                         ssleep(1);
337         } while (!rc && args.rets[0] == RTAS_NOT_SUSPENDABLE);
338
339         if (rc)
340                 return rc;
341         else if (args.rets[0])
342                 return args.rets[0];
343
344         post_mobility_fixup();
345         return count;
346 }
347
348 static CLASS_ATTR(migration, S_IWUSR, NULL, migrate_store);
349
350 static int __init mobility_sysfs_init(void)
351 {
352         int rc;
353
354         mobility_kobj = kobject_create_and_add("mobility", kernel_kobj);
355         if (!mobility_kobj)
356                 return -ENOMEM;
357
358         rc = sysfs_create_file(mobility_kobj, &class_attr_migration.attr);
359
360         return rc;
361 }
362 device_initcall(mobility_sysfs_init);