diff options
Diffstat (limited to 'arch/powerpc/platforms/pseries/reconfig.c')
| -rw-r--r-- | arch/powerpc/platforms/pseries/reconfig.c | 122 |
1 files changed, 7 insertions, 115 deletions
diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c index 39f71fba9b38..d6491bd481d0 100644 --- a/arch/powerpc/platforms/pseries/reconfig.c +++ b/arch/powerpc/platforms/pseries/reconfig.c | |||
| @@ -16,55 +16,13 @@ | |||
| 16 | #include <linux/notifier.h> | 16 | #include <linux/notifier.h> |
| 17 | #include <linux/proc_fs.h> | 17 | #include <linux/proc_fs.h> |
| 18 | #include <linux/slab.h> | 18 | #include <linux/slab.h> |
| 19 | #include <linux/of.h> | ||
| 19 | 20 | ||
| 20 | #include <asm/prom.h> | 21 | #include <asm/prom.h> |
| 21 | #include <asm/machdep.h> | 22 | #include <asm/machdep.h> |
| 22 | #include <asm/uaccess.h> | 23 | #include <asm/uaccess.h> |
| 23 | #include <asm/pSeries_reconfig.h> | ||
| 24 | #include <asm/mmu.h> | 24 | #include <asm/mmu.h> |
| 25 | 25 | ||
| 26 | |||
| 27 | |||
| 28 | /* | ||
| 29 | * Routines for "runtime" addition and removal of device tree nodes. | ||
| 30 | */ | ||
| 31 | #ifdef CONFIG_PROC_DEVICETREE | ||
| 32 | /* | ||
| 33 | * Add a node to /proc/device-tree. | ||
| 34 | */ | ||
| 35 | static void add_node_proc_entries(struct device_node *np) | ||
| 36 | { | ||
| 37 | struct proc_dir_entry *ent; | ||
| 38 | |||
| 39 | ent = proc_mkdir(strrchr(np->full_name, '/') + 1, np->parent->pde); | ||
| 40 | if (ent) | ||
| 41 | proc_device_tree_add_node(np, ent); | ||
| 42 | } | ||
| 43 | |||
| 44 | static void remove_node_proc_entries(struct device_node *np) | ||
| 45 | { | ||
| 46 | struct property *pp = np->properties; | ||
| 47 | struct device_node *parent = np->parent; | ||
| 48 | |||
| 49 | while (pp) { | ||
| 50 | remove_proc_entry(pp->name, np->pde); | ||
| 51 | pp = pp->next; | ||
| 52 | } | ||
| 53 | if (np->pde) | ||
| 54 | remove_proc_entry(np->pde->name, parent->pde); | ||
| 55 | } | ||
| 56 | #else /* !CONFIG_PROC_DEVICETREE */ | ||
| 57 | static void add_node_proc_entries(struct device_node *np) | ||
| 58 | { | ||
| 59 | return; | ||
| 60 | } | ||
| 61 | |||
| 62 | static void remove_node_proc_entries(struct device_node *np) | ||
| 63 | { | ||
| 64 | return; | ||
| 65 | } | ||
| 66 | #endif /* CONFIG_PROC_DEVICETREE */ | ||
| 67 | |||
| 68 | /** | 26 | /** |
| 69 | * derive_parent - basically like dirname(1) | 27 | * derive_parent - basically like dirname(1) |
| 70 | * @path: the full_name of a node to be added to the tree | 28 | * @path: the full_name of a node to be added to the tree |
| @@ -97,28 +55,6 @@ static struct device_node *derive_parent(const char *path) | |||
| 97 | return parent; | 55 | return parent; |
| 98 | } | 56 | } |
| 99 | 57 | ||
| 100 | static BLOCKING_NOTIFIER_HEAD(pSeries_reconfig_chain); | ||
| 101 | |||
| 102 | int pSeries_reconfig_notifier_register(struct notifier_block *nb) | ||
| 103 | { | ||
| 104 | return blocking_notifier_chain_register(&pSeries_reconfig_chain, nb); | ||
| 105 | } | ||
| 106 | EXPORT_SYMBOL_GPL(pSeries_reconfig_notifier_register); | ||
| 107 | |||
| 108 | void pSeries_reconfig_notifier_unregister(struct notifier_block *nb) | ||
| 109 | { | ||
| 110 | blocking_notifier_chain_unregister(&pSeries_reconfig_chain, nb); | ||
| 111 | } | ||
| 112 | EXPORT_SYMBOL_GPL(pSeries_reconfig_notifier_unregister); | ||
| 113 | |||
| 114 | int pSeries_reconfig_notify(unsigned long action, void *p) | ||
| 115 | { | ||
| 116 | int err = blocking_notifier_call_chain(&pSeries_reconfig_chain, | ||
| 117 | action, p); | ||
| 118 | |||
| 119 | return notifier_to_errno(err); | ||
| 120 | } | ||
| 121 | |||
| 122 | static int pSeries_reconfig_add_node(const char *path, struct property *proplist) | 58 | static int pSeries_reconfig_add_node(const char *path, struct property *proplist) |
| 123 | { | 59 | { |
| 124 | struct device_node *np; | 60 | struct device_node *np; |
| @@ -142,16 +78,12 @@ static int pSeries_reconfig_add_node(const char *path, struct property *proplist | |||
| 142 | goto out_err; | 78 | goto out_err; |
| 143 | } | 79 | } |
| 144 | 80 | ||
| 145 | err = pSeries_reconfig_notify(PSERIES_RECONFIG_ADD, np); | 81 | err = of_attach_node(np); |
| 146 | if (err) { | 82 | if (err) { |
| 147 | printk(KERN_ERR "Failed to add device node %s\n", path); | 83 | printk(KERN_ERR "Failed to add device node %s\n", path); |
| 148 | goto out_err; | 84 | goto out_err; |
| 149 | } | 85 | } |
| 150 | 86 | ||
| 151 | of_attach_node(np); | ||
| 152 | |||
| 153 | add_node_proc_entries(np); | ||
| 154 | |||
| 155 | of_node_put(np->parent); | 87 | of_node_put(np->parent); |
| 156 | 88 | ||
| 157 | return 0; | 89 | return 0; |
| @@ -179,11 +111,7 @@ static int pSeries_reconfig_remove_node(struct device_node *np) | |||
| 179 | return -EBUSY; | 111 | return -EBUSY; |
| 180 | } | 112 | } |
| 181 | 113 | ||
| 182 | remove_node_proc_entries(np); | ||
| 183 | |||
| 184 | pSeries_reconfig_notify(PSERIES_RECONFIG_REMOVE, np); | ||
| 185 | of_detach_node(np); | 114 | of_detach_node(np); |
| 186 | |||
| 187 | of_node_put(parent); | 115 | of_node_put(parent); |
| 188 | of_node_put(np); /* Must decrement the refcount */ | 116 | of_node_put(np); /* Must decrement the refcount */ |
| 189 | return 0; | 117 | return 0; |
| @@ -281,12 +209,11 @@ static struct property *new_property(const char *name, const int length, | |||
| 281 | if (!new) | 209 | if (!new) |
| 282 | return NULL; | 210 | return NULL; |
| 283 | 211 | ||
| 284 | if (!(new->name = kmalloc(strlen(name) + 1, GFP_KERNEL))) | 212 | if (!(new->name = kstrdup(name, GFP_KERNEL))) |
| 285 | goto cleanup; | 213 | goto cleanup; |
| 286 | if (!(new->value = kmalloc(length + 1, GFP_KERNEL))) | 214 | if (!(new->value = kmalloc(length + 1, GFP_KERNEL))) |
| 287 | goto cleanup; | 215 | goto cleanup; |
| 288 | 216 | ||
| 289 | strcpy(new->name, name); | ||
| 290 | memcpy(new->value, value, length); | 217 | memcpy(new->value, value, length); |
| 291 | *(((char *)new->value) + length) = 0; | 218 | *(((char *)new->value) + length) = 0; |
| 292 | new->length = length; | 219 | new->length = length; |
| @@ -398,7 +325,7 @@ static int do_add_property(char *buf, size_t bufsize) | |||
| 398 | if (!prop) | 325 | if (!prop) |
| 399 | return -ENOMEM; | 326 | return -ENOMEM; |
| 400 | 327 | ||
| 401 | prom_add_property(np, prop); | 328 | of_add_property(np, prop); |
| 402 | 329 | ||
| 403 | return 0; | 330 | return 0; |
| 404 | } | 331 | } |
| @@ -422,16 +349,15 @@ static int do_remove_property(char *buf, size_t bufsize) | |||
| 422 | 349 | ||
| 423 | prop = of_find_property(np, buf, NULL); | 350 | prop = of_find_property(np, buf, NULL); |
| 424 | 351 | ||
| 425 | return prom_remove_property(np, prop); | 352 | return of_remove_property(np, prop); |
| 426 | } | 353 | } |
| 427 | 354 | ||
| 428 | static int do_update_property(char *buf, size_t bufsize) | 355 | static int do_update_property(char *buf, size_t bufsize) |
| 429 | { | 356 | { |
| 430 | struct device_node *np; | 357 | struct device_node *np; |
| 431 | struct pSeries_reconfig_prop_update upd_value; | ||
| 432 | unsigned char *value; | 358 | unsigned char *value; |
| 433 | char *name, *end, *next_prop; | 359 | char *name, *end, *next_prop; |
| 434 | int rc, length; | 360 | int length; |
| 435 | struct property *newprop; | 361 | struct property *newprop; |
| 436 | buf = parse_node(buf, bufsize, &np); | 362 | buf = parse_node(buf, bufsize, &np); |
| 437 | end = buf + bufsize; | 363 | end = buf + bufsize; |
| @@ -453,41 +379,7 @@ static int do_update_property(char *buf, size_t bufsize) | |||
| 453 | if (!strcmp(name, "slb-size") || !strcmp(name, "ibm,slb-size")) | 379 | if (!strcmp(name, "slb-size") || !strcmp(name, "ibm,slb-size")) |
| 454 | slb_set_size(*(int *)value); | 380 | slb_set_size(*(int *)value); |
| 455 | 381 | ||
| 456 | upd_value.node = np; | 382 | return of_update_property(np, newprop); |
| 457 | upd_value.property = newprop; | ||
| 458 | pSeries_reconfig_notify(PSERIES_UPDATE_PROPERTY, &upd_value); | ||
| 459 | |||
| 460 | rc = prom_update_property(np, newprop); | ||
| 461 | if (rc) | ||
| 462 | return rc; | ||
| 463 | |||
| 464 | /* For memory under the ibm,dynamic-reconfiguration-memory node | ||
| 465 | * of the device tree, adding and removing memory is just an update | ||
| 466 | * to the ibm,dynamic-memory property instead of adding/removing a | ||
| 467 | * memory node in the device tree. For these cases we still need to | ||
| 468 | * involve the notifier chain. | ||
| 469 | */ | ||
| 470 | if (!strcmp(name, "ibm,dynamic-memory")) { | ||
| 471 | int action; | ||
| 472 | |||
| 473 | next_prop = parse_next_property(next_prop, end, &name, | ||
| 474 | &length, &value); | ||
| 475 | if (!next_prop) | ||
| 476 | return -EINVAL; | ||
| 477 | |||
| 478 | if (!strcmp(name, "add")) | ||
| 479 | action = PSERIES_DRCONF_MEM_ADD; | ||
| 480 | else | ||
| 481 | action = PSERIES_DRCONF_MEM_REMOVE; | ||
| 482 | |||
| 483 | rc = pSeries_reconfig_notify(action, value); | ||
| 484 | if (rc) { | ||
| 485 | prom_update_property(np, newprop); | ||
| 486 | return rc; | ||
| 487 | } | ||
| 488 | } | ||
| 489 | |||
| 490 | return 0; | ||
| 491 | } | 383 | } |
| 492 | 384 | ||
| 493 | /** | 385 | /** |
