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 *); |