blob: 140d02a5232af273b617a464fa08052438266262 [file] [log] [blame]
Badari Pulavarty57b53922008-04-18 13:33:50 -07001/*
2 * pseries Memory Hotplug infrastructure.
3 *
4 * Copyright (C) 2008 Badari Pulavarty, IBM Corporation
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12#include <linux/of.h>
Badari Pulavarty98d5c212008-04-18 13:33:52 -070013#include <linux/lmb.h>
Badari Pulavarty57b53922008-04-18 13:33:50 -070014#include <asm/firmware.h>
15#include <asm/machdep.h>
16#include <asm/pSeries_reconfig.h>
17
Nathan Fontenot3c3f67e2008-07-03 13:22:39 +100018static int pseries_remove_lmb(unsigned long base, unsigned int lmb_size)
Badari Pulavarty57b53922008-04-18 13:33:50 -070019{
Nathan Fontenot3c3f67e2008-07-03 13:22:39 +100020 unsigned long start, start_pfn;
Badari Pulavarty57b53922008-04-18 13:33:50 -070021 struct zone *zone;
Nathan Fontenot3c3f67e2008-07-03 13:22:39 +100022 int ret;
Nathan Fontenot92ecd172008-07-03 13:20:58 +100023
Nathan Fontenot9fd3f882008-10-01 09:44:02 +000024 start_pfn = base >> PAGE_SHIFT;
Badari Pulavarty57b53922008-04-18 13:33:50 -070025 zone = page_zone(pfn_to_page(start_pfn));
26
27 /*
28 * Remove section mappings and sysfs entries for the
29 * section of the memory we are removing.
30 *
31 * NOTE: Ideally, this should be done in generic code like
32 * remove_memory(). But remove_memory() gets called by writing
33 * to sysfs "state" file and we can't remove sysfs entries
34 * while writing to it. So we have to defer it to here.
35 */
Nathan Fontenot92ecd172008-07-03 13:20:58 +100036 ret = __remove_pages(zone, start_pfn, lmb_size >> PAGE_SHIFT);
Badari Pulavarty57b53922008-04-18 13:33:50 -070037 if (ret)
38 return ret;
39
40 /*
Badari Pulavarty98d5c212008-04-18 13:33:52 -070041 * Update memory regions for memory remove
42 */
Nathan Fontenot92ecd172008-07-03 13:20:58 +100043 lmb_remove(base, lmb_size);
Badari Pulavarty98d5c212008-04-18 13:33:52 -070044
45 /*
Badari Pulavarty57b53922008-04-18 13:33:50 -070046 * Remove htab bolted mappings for this section of memory
47 */
Nathan Fontenot92ecd172008-07-03 13:20:58 +100048 start = (unsigned long)__va(base);
49 ret = remove_section_mapping(start, start + lmb_size);
Badari Pulavarty57b53922008-04-18 13:33:50 -070050 return ret;
51}
52
Nathan Fontenot3c3f67e2008-07-03 13:22:39 +100053static 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
Badari Pulavarty98d5c212008-04-18 13:33:52 -070082static int pseries_add_memory(struct device_node *np)
83{
84 const char *type;
Badari Pulavarty98d5c212008-04-18 13:33:52 -070085 const unsigned int *regs;
Nathan Fontenot92ecd172008-07-03 13:20:58 +100086 unsigned long base;
87 unsigned int lmb_size;
Badari Pulavarty98d5c212008-04-18 13:33:52 -070088 int ret = -EINVAL;
89
90 /*
91 * Check to see if we are actually adding memory
92 */
93 type = of_get_property(np, "device_type", NULL);
94 if (type == NULL || strcmp(type, "memory") != 0)
95 return 0;
96
97 /*
Nathan Fontenot92ecd172008-07-03 13:20:58 +100098 * Find the base and size of the lmb
Badari Pulavarty98d5c212008-04-18 13:33:52 -070099 */
Badari Pulavarty98d5c212008-04-18 13:33:52 -0700100 regs = of_get_property(np, "reg", NULL);
101 if (!regs)
102 return ret;
103
Nathan Fontenot92ecd172008-07-03 13:20:58 +1000104 base = *(unsigned long *)regs;
105 lmb_size = regs[3];
Badari Pulavarty98d5c212008-04-18 13:33:52 -0700106
107 /*
108 * Update memory region to represent the memory add
109 */
Nathan Fontenot3c3f67e2008-07-03 13:22:39 +1000110 ret = lmb_add(base, lmb_size);
111 return (ret < 0) ? -EINVAL : 0;
112}
113
114static 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;
Badari Pulavarty98d5c212008-04-18 13:33:52 -0700141}
142
Badari Pulavarty57b53922008-04-18 13:33:50 -0700143static int pseries_memory_notifier(struct notifier_block *nb,
144 unsigned long action, void *node)
145{
146 int err = NOTIFY_OK;
147
148 switch (action) {
149 case PSERIES_RECONFIG_ADD:
Badari Pulavarty98d5c212008-04-18 13:33:52 -0700150 if (pseries_add_memory(node))
151 err = NOTIFY_BAD;
Badari Pulavarty57b53922008-04-18 13:33:50 -0700152 break;
153 case PSERIES_RECONFIG_REMOVE:
154 if (pseries_remove_memory(node))
155 err = NOTIFY_BAD;
156 break;
Nathan Fontenot3c3f67e2008-07-03 13:22:39 +1000157 case PSERIES_DRCONF_MEM_ADD:
158 case PSERIES_DRCONF_MEM_REMOVE:
159 if (pseries_drconf_memory(node, action))
160 err = NOTIFY_BAD;
161 break;
Badari Pulavarty57b53922008-04-18 13:33:50 -0700162 default:
163 err = NOTIFY_DONE;
164 break;
165 }
166 return err;
167}
168
169static struct notifier_block pseries_mem_nb = {
170 .notifier_call = pseries_memory_notifier,
171};
172
173static int __init pseries_memory_hotplug_init(void)
174{
175 if (firmware_has_feature(FW_FEATURE_LPAR))
176 pSeries_reconfig_notifier_register(&pseries_mem_nb);
177
178 return 0;
179}
180machine_device_initcall(pseries, pseries_memory_hotplug_init);