diff options
author | Jeremy Kerr <jk@ozlabs.org> | 2007-04-23 15:35:47 -0400 |
---|---|---|
committer | Arnd Bergmann <arnd@klappe.arndb.de> | 2007-04-23 15:44:41 -0400 |
commit | 150f7e3cfec42a7d96ffda6f83881a7cee101c87 (patch) | |
tree | 9611ed3ebeb7203264ac50054f0dc948c5a7a8e4 /arch | |
parent | 9dd855a729abb4670522d5eae6854db48d624498 (diff) |
[POWERPC] cell: enable RTAS-based PTCAL for Cell XDR memory
Enable Periodic Recalibration (PTCAL) support for Cell XDR memory,
using the new ibm,cbe-start-ptcal and ibm,cbe-stop-ptcal RTAS calls.
Tested on QS20 and QS21 (by Thomas Huth). It seems that SLOF has
problems disabling, at least on QS20; this patch should only be
used once these problems have been addressed.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Arnd Bergmann <arnd.bergmann@de.ibm.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/powerpc/platforms/cell/ras.c | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/cell/ras.c b/arch/powerpc/platforms/cell/ras.c index 0984c7071695..b5ebc916388b 100644 --- a/arch/powerpc/platforms/cell/ras.c +++ b/arch/powerpc/platforms/cell/ras.c | |||
@@ -3,11 +3,13 @@ | |||
3 | #include <linux/types.h> | 3 | #include <linux/types.h> |
4 | #include <linux/kernel.h> | 4 | #include <linux/kernel.h> |
5 | #include <linux/smp.h> | 5 | #include <linux/smp.h> |
6 | #include <linux/reboot.h> | ||
6 | 7 | ||
7 | #include <asm/reg.h> | 8 | #include <asm/reg.h> |
8 | #include <asm/io.h> | 9 | #include <asm/io.h> |
9 | #include <asm/prom.h> | 10 | #include <asm/prom.h> |
10 | #include <asm/machdep.h> | 11 | #include <asm/machdep.h> |
12 | #include <asm/rtas.h> | ||
11 | 13 | ||
12 | #include "ras.h" | 14 | #include "ras.h" |
13 | #include "cbe_regs.h" | 15 | #include "cbe_regs.h" |
@@ -82,6 +84,164 @@ static int cbe_machine_check_handler(struct pt_regs *regs) | |||
82 | return 0; | 84 | return 0; |
83 | } | 85 | } |
84 | 86 | ||
87 | struct ptcal_area { | ||
88 | struct list_head list; | ||
89 | int nid; | ||
90 | int order; | ||
91 | struct page *pages; | ||
92 | }; | ||
93 | |||
94 | static LIST_HEAD(ptcal_list); | ||
95 | |||
96 | static int ptcal_start_tok, ptcal_stop_tok; | ||
97 | |||
98 | static int __init cbe_ptcal_enable_on_node(int nid, int order) | ||
99 | { | ||
100 | struct ptcal_area *area; | ||
101 | int ret = -ENOMEM; | ||
102 | unsigned long addr; | ||
103 | |||
104 | #ifdef CONFIG_CRASH_DUMP | ||
105 | rtas_call(ptcal_stop_tok, 1, 1, NULL, nid); | ||
106 | #endif | ||
107 | |||
108 | area = kmalloc(sizeof(*area), GFP_KERNEL); | ||
109 | if (!area) | ||
110 | goto out_err; | ||
111 | |||
112 | area->nid = nid; | ||
113 | area->order = order; | ||
114 | area->pages = alloc_pages_node(area->nid, GFP_KERNEL, area->order); | ||
115 | |||
116 | if (!area->pages) | ||
117 | goto out_free_area; | ||
118 | |||
119 | addr = __pa(page_address(area->pages)); | ||
120 | |||
121 | ret = -EIO; | ||
122 | if (rtas_call(ptcal_start_tok, 3, 1, NULL, area->nid, | ||
123 | (unsigned int)(addr >> 32), | ||
124 | (unsigned int)(addr & 0xffffffff))) { | ||
125 | printk(KERN_ERR "%s: error enabling PTCAL on node %d!\n", | ||
126 | __FUNCTION__, nid); | ||
127 | goto out_free_pages; | ||
128 | } | ||
129 | |||
130 | list_add(&area->list, &ptcal_list); | ||
131 | |||
132 | return 0; | ||
133 | |||
134 | out_free_pages: | ||
135 | __free_pages(area->pages, area->order); | ||
136 | out_free_area: | ||
137 | kfree(area); | ||
138 | out_err: | ||
139 | return ret; | ||
140 | } | ||
141 | |||
142 | static int __init cbe_ptcal_enable(void) | ||
143 | { | ||
144 | const u32 *size; | ||
145 | struct device_node *np; | ||
146 | int order, found_mic = 0; | ||
147 | |||
148 | np = of_find_node_by_path("/rtas"); | ||
149 | if (!np) | ||
150 | return -ENODEV; | ||
151 | |||
152 | size = get_property(np, "ibm,cbe-ptcal-size", NULL); | ||
153 | if (!size) | ||
154 | return -ENODEV; | ||
155 | |||
156 | pr_debug("%s: enabling PTCAL, size = 0x%x\n", __FUNCTION__, *size); | ||
157 | order = get_order(*size); | ||
158 | of_node_put(np); | ||
159 | |||
160 | /* support for malta device trees, with be@/mic@ nodes */ | ||
161 | for_each_node_by_type(np, "mic-tm") { | ||
162 | cbe_ptcal_enable_on_node(of_node_to_nid(np), order); | ||
163 | found_mic = 1; | ||
164 | } | ||
165 | |||
166 | if (found_mic) | ||
167 | return 0; | ||
168 | |||
169 | /* support for older device tree - use cpu nodes */ | ||
170 | for_each_node_by_type(np, "cpu") { | ||
171 | const u32 *nid = get_property(np, "node-id", NULL); | ||
172 | if (!nid) { | ||
173 | printk(KERN_ERR "%s: node %s is missing node-id?\n", | ||
174 | __FUNCTION__, np->full_name); | ||
175 | continue; | ||
176 | } | ||
177 | cbe_ptcal_enable_on_node(*nid, order); | ||
178 | found_mic = 1; | ||
179 | } | ||
180 | |||
181 | return found_mic ? 0 : -ENODEV; | ||
182 | } | ||
183 | |||
184 | static int cbe_ptcal_disable(void) | ||
185 | { | ||
186 | struct ptcal_area *area, *tmp; | ||
187 | int ret = 0; | ||
188 | |||
189 | pr_debug("%s: disabling PTCAL\n", __FUNCTION__); | ||
190 | |||
191 | list_for_each_entry_safe(area, tmp, &ptcal_list, list) { | ||
192 | /* disable ptcal on this node */ | ||
193 | if (rtas_call(ptcal_stop_tok, 1, 1, NULL, area->nid)) { | ||
194 | printk(KERN_ERR "%s: error disabling PTCAL " | ||
195 | "on node %d!\n", __FUNCTION__, | ||
196 | area->nid); | ||
197 | ret = -EIO; | ||
198 | continue; | ||
199 | } | ||
200 | |||
201 | /* ensure we can access the PTCAL area */ | ||
202 | memset(page_address(area->pages), 0, | ||
203 | 1 << (area->order + PAGE_SHIFT)); | ||
204 | |||
205 | /* clean up */ | ||
206 | list_del(&area->list); | ||
207 | __free_pages(area->pages, area->order); | ||
208 | kfree(area); | ||
209 | } | ||
210 | |||
211 | return ret; | ||
212 | } | ||
213 | |||
214 | static int cbe_ptcal_notify_reboot(struct notifier_block *nb, | ||
215 | unsigned long code, void *data) | ||
216 | { | ||
217 | return cbe_ptcal_disable(); | ||
218 | } | ||
219 | |||
220 | static struct notifier_block cbe_ptcal_reboot_notifier = { | ||
221 | .notifier_call = cbe_ptcal_notify_reboot | ||
222 | }; | ||
223 | |||
224 | int __init cbe_ptcal_init(void) | ||
225 | { | ||
226 | int ret; | ||
227 | ptcal_start_tok = rtas_token("ibm,cbe-start-ptcal"); | ||
228 | ptcal_stop_tok = rtas_token("ibm,cbe-stop-ptcal"); | ||
229 | |||
230 | if (ptcal_start_tok == RTAS_UNKNOWN_SERVICE | ||
231 | || ptcal_stop_tok == RTAS_UNKNOWN_SERVICE) | ||
232 | return -ENODEV; | ||
233 | |||
234 | ret = register_reboot_notifier(&cbe_ptcal_reboot_notifier); | ||
235 | if (ret) { | ||
236 | printk(KERN_ERR "Can't disable PTCAL, so not enabling\n"); | ||
237 | return ret; | ||
238 | } | ||
239 | |||
240 | return cbe_ptcal_enable(); | ||
241 | } | ||
242 | |||
243 | arch_initcall(cbe_ptcal_init); | ||
244 | |||
85 | void __init cbe_ras_init(void) | 245 | void __init cbe_ras_init(void) |
86 | { | 246 | { |
87 | unsigned long hid0; | 247 | unsigned long hid0; |