• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/arch/powerpc/platforms/pseries/
1/*
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>
13#include <linux/memblock.h>
14#include <linux/vmalloc.h>
15#include <asm/firmware.h>
16#include <asm/machdep.h>
17#include <asm/pSeries_reconfig.h>
18#include <asm/sparsemem.h>
19
20static int pseries_remove_memblock(unsigned long base, unsigned int memblock_size)
21{
22	unsigned long start, start_pfn;
23	struct zone *zone;
24	int ret;
25
26	start_pfn = base >> PAGE_SHIFT;
27
28	if (!pfn_valid(start_pfn)) {
29		memblock_remove(base, memblock_size);
30		return 0;
31	}
32
33	zone = page_zone(pfn_to_page(start_pfn));
34
35	/*
36	 * Remove section mappings and sysfs entries for the
37	 * section of the memory we are removing.
38	 *
39	 * NOTE: Ideally, this should be done in generic code like
40	 * remove_memory(). But remove_memory() gets called by writing
41	 * to sysfs "state" file and we can't remove sysfs entries
42	 * while writing to it. So we have to defer it to here.
43	 */
44	ret = __remove_pages(zone, start_pfn, memblock_size >> PAGE_SHIFT);
45	if (ret)
46		return ret;
47
48	/*
49	 * Update memory regions for memory remove
50	 */
51	memblock_remove(base, memblock_size);
52
53	/*
54	 * Remove htab bolted mappings for this section of memory
55	 */
56	start = (unsigned long)__va(base);
57	ret = remove_section_mapping(start, start + memblock_size);
58
59	/* Ensure all vmalloc mappings are flushed in case they also
60	 * hit that section of memory
61	 */
62	vm_unmap_aliases();
63
64	return ret;
65}
66
67static int pseries_remove_memory(struct device_node *np)
68{
69	const char *type;
70	const unsigned int *regs;
71	unsigned long base;
72	unsigned int lmb_size;
73	int ret = -EINVAL;
74
75	/*
76	 * Check to see if we are actually removing memory
77	 */
78	type = of_get_property(np, "device_type", NULL);
79	if (type == NULL || strcmp(type, "memory") != 0)
80		return 0;
81
82	/*
83	 * Find the bae address and size of the memblock
84	 */
85	regs = of_get_property(np, "reg", NULL);
86	if (!regs)
87		return ret;
88
89	base = *(unsigned long *)regs;
90	lmb_size = regs[3];
91
92	ret = pseries_remove_memblock(base, lmb_size);
93	return ret;
94}
95
96static int pseries_add_memory(struct device_node *np)
97{
98	const char *type;
99	const unsigned int *regs;
100	unsigned long base;
101	unsigned int lmb_size;
102	int ret = -EINVAL;
103
104	/*
105	 * Check to see if we are actually adding memory
106	 */
107	type = of_get_property(np, "device_type", NULL);
108	if (type == NULL || strcmp(type, "memory") != 0)
109		return 0;
110
111	/*
112	 * Find the base and size of the memblock
113	 */
114	regs = of_get_property(np, "reg", NULL);
115	if (!regs)
116		return ret;
117
118	base = *(unsigned long *)regs;
119	lmb_size = regs[3];
120
121	/*
122	 * Update memory region to represent the memory add
123	 */
124	ret = memblock_add(base, lmb_size);
125	return (ret < 0) ? -EINVAL : 0;
126}
127
128static int pseries_drconf_memory(unsigned long *base, unsigned int action)
129{
130	struct device_node *np;
131	const unsigned long *lmb_size;
132	int rc;
133
134	np = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
135	if (!np)
136		return -EINVAL;
137
138	lmb_size = of_get_property(np, "ibm,lmb-size", NULL);
139	if (!lmb_size) {
140		of_node_put(np);
141		return -EINVAL;
142	}
143
144	if (action == PSERIES_DRCONF_MEM_ADD) {
145		rc = memblock_add(*base, *lmb_size);
146		rc = (rc < 0) ? -EINVAL : 0;
147	} else if (action == PSERIES_DRCONF_MEM_REMOVE) {
148		rc = pseries_remove_memblock(*base, *lmb_size);
149	} else {
150		rc = -EINVAL;
151	}
152
153	of_node_put(np);
154	return rc;
155}
156
157static int pseries_memory_notifier(struct notifier_block *nb,
158				unsigned long action, void *node)
159{
160	int err = NOTIFY_OK;
161
162	switch (action) {
163	case PSERIES_RECONFIG_ADD:
164		if (pseries_add_memory(node))
165			err = NOTIFY_BAD;
166		break;
167	case PSERIES_RECONFIG_REMOVE:
168		if (pseries_remove_memory(node))
169			err = NOTIFY_BAD;
170		break;
171	case PSERIES_DRCONF_MEM_ADD:
172	case PSERIES_DRCONF_MEM_REMOVE:
173		if (pseries_drconf_memory(node, action))
174			err = NOTIFY_BAD;
175		break;
176	default:
177		err = NOTIFY_DONE;
178		break;
179	}
180	return err;
181}
182
183static struct notifier_block pseries_mem_nb = {
184	.notifier_call = pseries_memory_notifier,
185};
186
187static int __init pseries_memory_hotplug_init(void)
188{
189	if (firmware_has_feature(FW_FEATURE_LPAR))
190		pSeries_reconfig_notifier_register(&pseries_mem_nb);
191
192	return 0;
193}
194machine_device_initcall(pseries, pseries_memory_hotplug_init);
195