diff options
| author | Nathan Fontenot <nfont@austin.ibm.com> | 2008-07-02 23:22:39 -0400 |
|---|---|---|
| committer | Paul Mackerras <paulus@samba.org> | 2008-07-03 02:58:16 -0400 |
| commit | 3c3f67eafad12d4ccabe491c6c8a50bf6e75b89a (patch) | |
| tree | be4081be92d8a7c165281242384cb79cb8c2d06d | |
| parent | 92ecd1790b10e12015070e33a0f70493d51aca50 (diff) | |
powerpc/pseries: Update the device tree correctly for drconf memory add/remove
This updates the device tree manipulation routines so that memory
add/remove of lmbs represented under the
ibm,dynamic-reconfiguration-memory node of the device tree invokes the
hotplug notifier chain.
This change is needed because of the change in the way memory is
represented under the ibm,dynamic-reconfiguration-memory node. All lmbs
are described in the ibm,dynamic-memory property instead of having a
separate node for each lmb as in previous device tree layouts. This
requires the update_node() routine to check for updates to the
ibm,dynamic-memory property and invoke the hotplug notifier chain.
This also updates the pseries hotplug notifier to be able to gather information
for lmbs represented under the ibm,dynamic-reconfiguration-memory node and
have the lmbs added/removed.
Signed-off-by: Nathan Fontenot <nfont@austin.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
| -rw-r--r-- | arch/powerpc/platforms/pseries/hotplug-memory.c | 95 | ||||
| -rw-r--r-- | arch/powerpc/platforms/pseries/reconfig.c | 36 | ||||
| -rw-r--r-- | include/asm-powerpc/pSeries_reconfig.h | 6 |
3 files changed, 104 insertions, 33 deletions
diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c index 18a8138ef99f..a1a368dd2d99 100644 --- a/arch/powerpc/platforms/pseries/hotplug-memory.c +++ b/arch/powerpc/platforms/pseries/hotplug-memory.c | |||
| @@ -15,32 +15,11 @@ | |||
| 15 | #include <asm/machdep.h> | 15 | #include <asm/machdep.h> |
| 16 | #include <asm/pSeries_reconfig.h> | 16 | #include <asm/pSeries_reconfig.h> |
| 17 | 17 | ||
| 18 | static int pseries_remove_memory(struct device_node *np) | 18 | static int pseries_remove_lmb(unsigned long base, unsigned int lmb_size) |
| 19 | { | 19 | { |
| 20 | const char *type; | 20 | unsigned long start, start_pfn; |
| 21 | const unsigned int *regs; | ||
| 22 | unsigned long base; | ||
| 23 | unsigned int lmb_size; | ||
| 24 | u64 start_pfn, start; | ||
| 25 | struct zone *zone; | 21 | struct zone *zone; |
| 26 | int ret = -EINVAL; | 22 | int ret; |
| 27 | |||
| 28 | /* | ||
| 29 | * Check to see if we are actually removing memory | ||
| 30 | */ | ||
| 31 | type = of_get_property(np, "device_type", NULL); | ||
| 32 | if (type == NULL || strcmp(type, "memory") != 0) | ||
| 33 | return 0; | ||
| 34 | |||
| 35 | /* | ||
| 36 | * Find the bae address and size of the lmb | ||
| 37 | */ | ||
| 38 | regs = of_get_property(np, "reg", NULL); | ||
| 39 | if (!regs) | ||
| 40 | return ret; | ||
| 41 | |||
| 42 | base = *(unsigned long *)regs; | ||
| 43 | lmb_size = regs[3]; | ||
| 44 | 23 | ||
| 45 | start_pfn = base >> PFN_SECTION_SHIFT; | 24 | start_pfn = base >> PFN_SECTION_SHIFT; |
| 46 | zone = page_zone(pfn_to_page(start_pfn)); | 25 | zone = page_zone(pfn_to_page(start_pfn)); |
| @@ -71,13 +50,41 @@ static int pseries_remove_memory(struct device_node *np) | |||
| 71 | return ret; | 50 | return ret; |
| 72 | } | 51 | } |
| 73 | 52 | ||
| 53 | static int pseries_remove_memory(struct device_node *np) | ||
| 54 | { | ||
| 55 | const char *type; | ||
| 56 | const unsigned int *regs; | ||
| 57 | unsigned long base; | ||
| 58 | unsigned int lmb_size; | ||
| 59 | int ret = -EINVAL; | ||
| 60 | |||
| 61 | /* | ||
| 62 | * Check to see if we are actually removing memory | ||
| 63 | */ | ||
| 64 | type = of_get_property(np, "device_type", NULL); | ||
| 65 | if (type == NULL || strcmp(type, "memory") != 0) | ||
| 66 | return 0; | ||
| 67 | |||
| 68 | /* | ||
| 69 | * Find the bae address and size of the lmb | ||
| 70 | */ | ||
| 71 | regs = of_get_property(np, "reg", NULL); | ||
| 72 | if (!regs) | ||
| 73 | return ret; | ||
| 74 | |||
| 75 | base = *(unsigned long *)regs; | ||
| 76 | lmb_size = regs[3]; | ||
| 77 | |||
| 78 | ret = pseries_remove_lmb(base, lmb_size); | ||
| 79 | return ret; | ||
| 80 | } | ||
| 81 | |||
| 74 | static int pseries_add_memory(struct device_node *np) | 82 | static int pseries_add_memory(struct device_node *np) |
| 75 | { | 83 | { |
| 76 | const char *type; | 84 | const char *type; |
| 77 | const unsigned int *regs; | 85 | const unsigned int *regs; |
| 78 | unsigned long base; | 86 | unsigned long base; |
| 79 | unsigned int lmb_size; | 87 | unsigned int lmb_size; |
| 80 | u64 start_pfn; | ||
| 81 | int ret = -EINVAL; | 88 | int ret = -EINVAL; |
| 82 | 89 | ||
| 83 | /* | 90 | /* |
| @@ -100,8 +107,37 @@ static int pseries_add_memory(struct device_node *np) | |||
| 100 | /* | 107 | /* |
| 101 | * Update memory region to represent the memory add | 108 | * Update memory region to represent the memory add |
| 102 | */ | 109 | */ |
| 103 | lmb_add(base, lmb_size); | 110 | ret = lmb_add(base, lmb_size); |
| 104 | return 0; | 111 | return (ret < 0) ? -EINVAL : 0; |
| 112 | } | ||
| 113 | |||
| 114 | static int pseries_drconf_memory(unsigned long *base, unsigned int action) | ||
| 115 | { | ||
| 116 | struct device_node *np; | ||
| 117 | const unsigned long *lmb_size; | ||
| 118 | int rc; | ||
| 119 | |||
| 120 | np = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory"); | ||
| 121 | if (!np) | ||
| 122 | return -EINVAL; | ||
| 123 | |||
| 124 | lmb_size = of_get_property(np, "ibm,lmb-size", NULL); | ||
| 125 | if (!lmb_size) { | ||
| 126 | of_node_put(np); | ||
| 127 | return -EINVAL; | ||
| 128 | } | ||
| 129 | |||
| 130 | if (action == PSERIES_DRCONF_MEM_ADD) { | ||
| 131 | rc = lmb_add(*base, *lmb_size); | ||
| 132 | rc = (rc < 0) ? -EINVAL : 0; | ||
| 133 | } else if (action == PSERIES_DRCONF_MEM_REMOVE) { | ||
| 134 | rc = pseries_remove_lmb(*base, *lmb_size); | ||
| 135 | } else { | ||
| 136 | rc = -EINVAL; | ||
| 137 | } | ||
| 138 | |||
| 139 | of_node_put(np); | ||
| 140 | return rc; | ||
| 105 | } | 141 | } |
| 106 | 142 | ||
| 107 | static int pseries_memory_notifier(struct notifier_block *nb, | 143 | static int pseries_memory_notifier(struct notifier_block *nb, |
| @@ -118,6 +154,11 @@ static int pseries_memory_notifier(struct notifier_block *nb, | |||
| 118 | if (pseries_remove_memory(node)) | 154 | if (pseries_remove_memory(node)) |
| 119 | err = NOTIFY_BAD; | 155 | err = NOTIFY_BAD; |
| 120 | break; | 156 | break; |
| 157 | case PSERIES_DRCONF_MEM_ADD: | ||
| 158 | case PSERIES_DRCONF_MEM_REMOVE: | ||
| 159 | if (pseries_drconf_memory(node, action)) | ||
| 160 | err = NOTIFY_BAD; | ||
| 161 | break; | ||
| 121 | default: | 162 | default: |
| 122 | err = NOTIFY_DONE; | 163 | err = NOTIFY_DONE; |
| 123 | break; | 164 | break; |
diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c index dfa2ebd2deb5..7637bd38c795 100644 --- a/arch/powerpc/platforms/pseries/reconfig.c +++ b/arch/powerpc/platforms/pseries/reconfig.c | |||
| @@ -422,8 +422,8 @@ static int do_update_property(char *buf, size_t bufsize) | |||
| 422 | { | 422 | { |
| 423 | struct device_node *np; | 423 | struct device_node *np; |
| 424 | unsigned char *value; | 424 | unsigned char *value; |
| 425 | char *name, *end; | 425 | char *name, *end, *next_prop; |
| 426 | int length; | 426 | int rc, length; |
| 427 | struct property *newprop, *oldprop; | 427 | struct property *newprop, *oldprop; |
| 428 | buf = parse_node(buf, bufsize, &np); | 428 | buf = parse_node(buf, bufsize, &np); |
| 429 | end = buf + bufsize; | 429 | end = buf + bufsize; |
| @@ -431,7 +431,8 @@ static int do_update_property(char *buf, size_t bufsize) | |||
| 431 | if (!np) | 431 | if (!np) |
| 432 | return -ENODEV; | 432 | return -ENODEV; |
| 433 | 433 | ||
| 434 | if (parse_next_property(buf, end, &name, &length, &value) == NULL) | 434 | next_prop = parse_next_property(buf, end, &name, &length, &value); |
| 435 | if (!next_prop) | ||
| 435 | return -EINVAL; | 436 | return -EINVAL; |
| 436 | 437 | ||
| 437 | newprop = new_property(name, length, value, NULL); | 438 | newprop = new_property(name, length, value, NULL); |
| @@ -442,7 +443,34 @@ static int do_update_property(char *buf, size_t bufsize) | |||
| 442 | if (!oldprop) | 443 | if (!oldprop) |
| 443 | return -ENODEV; | 444 | return -ENODEV; |
| 444 | 445 | ||
| 445 | return prom_update_property(np, newprop, oldprop); | 446 | rc = prom_update_property(np, newprop, oldprop); |
| 447 | if (rc) | ||
| 448 | return rc; | ||
| 449 | |||
| 450 | /* For memory under the ibm,dynamic-reconfiguration-memory node | ||
| 451 | * of the device tree, adding and removing memory is just an update | ||
| 452 | * to the ibm,dynamic-memory property instead of adding/removing a | ||
| 453 | * memory node in the device tree. For these cases we still need to | ||
| 454 | * involve the notifier chain. | ||
| 455 | */ | ||
| 456 | if (!strcmp(name, "ibm,dynamic-memory")) { | ||
| 457 | int action; | ||
| 458 | |||
| 459 | next_prop = parse_next_property(next_prop, end, &name, | ||
| 460 | &length, &value); | ||
| 461 | if (!next_prop) | ||
| 462 | return -EINVAL; | ||
| 463 | |||
| 464 | if (!strcmp(name, "add")) | ||
| 465 | action = PSERIES_DRCONF_MEM_ADD; | ||
| 466 | else | ||
| 467 | action = PSERIES_DRCONF_MEM_REMOVE; | ||
| 468 | |||
| 469 | blocking_notifier_call_chain(&pSeries_reconfig_chain, | ||
| 470 | action, value); | ||
| 471 | } | ||
| 472 | |||
| 473 | return 0; | ||
| 446 | } | 474 | } |
| 447 | 475 | ||
| 448 | /** | 476 | /** |
diff --git a/include/asm-powerpc/pSeries_reconfig.h b/include/asm-powerpc/pSeries_reconfig.h index ea6cfb8efb84..e482e5352e69 100644 --- a/include/asm-powerpc/pSeries_reconfig.h +++ b/include/asm-powerpc/pSeries_reconfig.h | |||
| @@ -9,8 +9,10 @@ | |||
| 9 | * added or removed on pSeries systems. | 9 | * added or removed on pSeries systems. |
| 10 | */ | 10 | */ |
| 11 | 11 | ||
| 12 | #define PSERIES_RECONFIG_ADD 0x0001 | 12 | #define PSERIES_RECONFIG_ADD 0x0001 |
| 13 | #define PSERIES_RECONFIG_REMOVE 0x0002 | 13 | #define PSERIES_RECONFIG_REMOVE 0x0002 |
| 14 | #define PSERIES_DRCONF_MEM_ADD 0x0003 | ||
| 15 | #define PSERIES_DRCONF_MEM_REMOVE 0x0004 | ||
| 14 | 16 | ||
| 15 | #ifdef CONFIG_PPC_PSERIES | 17 | #ifdef CONFIG_PPC_PSERIES |
| 16 | extern int pSeries_reconfig_notifier_register(struct notifier_block *); | 18 | extern int pSeries_reconfig_notifier_register(struct notifier_block *); |
