diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/powerpc/platforms/cell/Makefile | 7 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spu_manage.c | 420 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spu_priv1_mmio.c | 422 |
3 files changed, 448 insertions, 401 deletions
diff --git a/arch/powerpc/platforms/cell/Makefile b/arch/powerpc/platforms/cell/Makefile index f90e8337796c..869af89df6ff 100644 --- a/arch/powerpc/platforms/cell/Makefile +++ b/arch/powerpc/platforms/cell/Makefile | |||
@@ -14,7 +14,12 @@ endif | |||
14 | spufs-modular-$(CONFIG_SPU_FS) += spu_syscalls.o | 14 | spufs-modular-$(CONFIG_SPU_FS) += spu_syscalls.o |
15 | spu-priv1-$(CONFIG_PPC_CELL_NATIVE) += spu_priv1_mmio.o | 15 | spu-priv1-$(CONFIG_PPC_CELL_NATIVE) += spu_priv1_mmio.o |
16 | 16 | ||
17 | spu-manage-$(CONFIG_PPC_CELLEB) += spu_manage.o | ||
18 | spu-manage-$(CONFIG_PPC_CELL_NATIVE) += spu_manage.o | ||
19 | |||
17 | obj-$(CONFIG_SPU_BASE) += spu_callbacks.o spu_base.o \ | 20 | obj-$(CONFIG_SPU_BASE) += spu_callbacks.o spu_base.o \ |
18 | spu_coredump.o \ | 21 | spu_coredump.o \ |
19 | $(spufs-modular-m) \ | 22 | $(spufs-modular-m) \ |
20 | $(spu-priv1-y) spufs/ | 23 | $(spu-priv1-y) \ |
24 | $(spu-manage-y) \ | ||
25 | spufs/ | ||
diff --git a/arch/powerpc/platforms/cell/spu_manage.c b/arch/powerpc/platforms/cell/spu_manage.c new file mode 100644 index 000000000000..d8b39fe39cdd --- /dev/null +++ b/arch/powerpc/platforms/cell/spu_manage.c | |||
@@ -0,0 +1,420 @@ | |||
1 | /* | ||
2 | * spu management operations for of based platforms | ||
3 | * | ||
4 | * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 | ||
5 | * Copyright 2006 Sony Corp. | ||
6 | * (C) Copyright 2007 TOSHIBA CORPORATION | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; version 2 of the License. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License along | ||
18 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
20 | */ | ||
21 | |||
22 | #include <linux/interrupt.h> | ||
23 | #include <linux/list.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/ptrace.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/wait.h> | ||
28 | #include <linux/mm.h> | ||
29 | #include <linux/io.h> | ||
30 | #include <linux/mutex.h> | ||
31 | #include <linux/device.h> | ||
32 | |||
33 | #include <asm/spu.h> | ||
34 | #include <asm/spu_priv1.h> | ||
35 | #include <asm/firmware.h> | ||
36 | #include <asm/prom.h> | ||
37 | |||
38 | #include "interrupt.h" | ||
39 | |||
40 | struct device_node *spu_devnode(struct spu *spu) | ||
41 | { | ||
42 | return spu->devnode; | ||
43 | } | ||
44 | |||
45 | EXPORT_SYMBOL_GPL(spu_devnode); | ||
46 | |||
47 | static u64 __init find_spu_unit_number(struct device_node *spe) | ||
48 | { | ||
49 | const unsigned int *prop; | ||
50 | int proplen; | ||
51 | prop = get_property(spe, "unit-id", &proplen); | ||
52 | if (proplen == 4) | ||
53 | return (u64)*prop; | ||
54 | |||
55 | prop = get_property(spe, "reg", &proplen); | ||
56 | if (proplen == 4) | ||
57 | return (u64)*prop; | ||
58 | |||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | static int __init cell_spuprop_present(struct spu *spu, struct device_node *spe, | ||
63 | const char *prop) | ||
64 | { | ||
65 | const struct address_prop { | ||
66 | unsigned long address; | ||
67 | unsigned int len; | ||
68 | } __attribute__((packed)) *p; | ||
69 | int proplen; | ||
70 | |||
71 | unsigned long start_pfn, nr_pages; | ||
72 | struct pglist_data *pgdata; | ||
73 | struct zone *zone; | ||
74 | int ret; | ||
75 | |||
76 | p = get_property(spe, prop, &proplen); | ||
77 | WARN_ON(proplen != sizeof (*p)); | ||
78 | |||
79 | start_pfn = p->address >> PAGE_SHIFT; | ||
80 | nr_pages = ((unsigned long)p->len + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
81 | |||
82 | pgdata = NODE_DATA(spu->node); | ||
83 | zone = pgdata->node_zones; | ||
84 | |||
85 | ret = __add_pages(zone, start_pfn, nr_pages); | ||
86 | |||
87 | return ret; | ||
88 | } | ||
89 | |||
90 | static void __iomem * __init map_spe_prop(struct spu *spu, | ||
91 | struct device_node *n, const char *name) | ||
92 | { | ||
93 | const struct address_prop { | ||
94 | unsigned long address; | ||
95 | unsigned int len; | ||
96 | } __attribute__((packed)) *prop; | ||
97 | |||
98 | const void *p; | ||
99 | int proplen; | ||
100 | void __iomem *ret = NULL; | ||
101 | int err = 0; | ||
102 | |||
103 | p = get_property(n, name, &proplen); | ||
104 | if (proplen != sizeof (struct address_prop)) | ||
105 | return NULL; | ||
106 | |||
107 | prop = p; | ||
108 | |||
109 | err = cell_spuprop_present(spu, n, name); | ||
110 | if (err && (err != -EEXIST)) | ||
111 | goto out; | ||
112 | |||
113 | ret = ioremap(prop->address, prop->len); | ||
114 | |||
115 | out: | ||
116 | return ret; | ||
117 | } | ||
118 | |||
119 | static void spu_unmap(struct spu *spu) | ||
120 | { | ||
121 | if (!firmware_has_feature(FW_FEATURE_LPAR)) | ||
122 | iounmap(spu->priv1); | ||
123 | iounmap(spu->priv2); | ||
124 | iounmap(spu->problem); | ||
125 | iounmap((__force u8 __iomem *)spu->local_store); | ||
126 | } | ||
127 | |||
128 | static int __init spu_map_interrupts_old(struct spu *spu, | ||
129 | struct device_node *np) | ||
130 | { | ||
131 | unsigned int isrc; | ||
132 | const u32 *tmp; | ||
133 | int nid; | ||
134 | |||
135 | /* Get the interrupt source unit from the device-tree */ | ||
136 | tmp = get_property(np, "isrc", NULL); | ||
137 | if (!tmp) | ||
138 | return -ENODEV; | ||
139 | isrc = tmp[0]; | ||
140 | |||
141 | tmp = get_property(np->parent->parent, "node-id", NULL); | ||
142 | if (!tmp) { | ||
143 | printk(KERN_WARNING "%s: can't find node-id\n", __FUNCTION__); | ||
144 | nid = spu->node; | ||
145 | } else | ||
146 | nid = tmp[0]; | ||
147 | |||
148 | /* Add the node number */ | ||
149 | isrc |= nid << IIC_IRQ_NODE_SHIFT; | ||
150 | |||
151 | /* Now map interrupts of all 3 classes */ | ||
152 | spu->irqs[0] = irq_create_mapping(NULL, IIC_IRQ_CLASS_0 | isrc); | ||
153 | spu->irqs[1] = irq_create_mapping(NULL, IIC_IRQ_CLASS_1 | isrc); | ||
154 | spu->irqs[2] = irq_create_mapping(NULL, IIC_IRQ_CLASS_2 | isrc); | ||
155 | |||
156 | /* Right now, we only fail if class 2 failed */ | ||
157 | return spu->irqs[2] == NO_IRQ ? -EINVAL : 0; | ||
158 | } | ||
159 | |||
160 | static int __init spu_map_device_old(struct spu *spu) | ||
161 | { | ||
162 | struct device_node *node = spu->devnode; | ||
163 | const char *prop; | ||
164 | int ret; | ||
165 | |||
166 | ret = -ENODEV; | ||
167 | spu->name = get_property(node, "name", NULL); | ||
168 | if (!spu->name) | ||
169 | goto out; | ||
170 | |||
171 | prop = get_property(node, "local-store", NULL); | ||
172 | if (!prop) | ||
173 | goto out; | ||
174 | spu->local_store_phys = *(unsigned long *)prop; | ||
175 | |||
176 | /* we use local store as ram, not io memory */ | ||
177 | spu->local_store = (void __force *) | ||
178 | map_spe_prop(spu, node, "local-store"); | ||
179 | if (!spu->local_store) | ||
180 | goto out; | ||
181 | |||
182 | prop = get_property(node, "problem", NULL); | ||
183 | if (!prop) | ||
184 | goto out_unmap; | ||
185 | spu->problem_phys = *(unsigned long *)prop; | ||
186 | |||
187 | spu->problem = map_spe_prop(spu, node, "problem"); | ||
188 | if (!spu->problem) | ||
189 | goto out_unmap; | ||
190 | |||
191 | spu->priv2 = map_spe_prop(spu, node, "priv2"); | ||
192 | if (!spu->priv2) | ||
193 | goto out_unmap; | ||
194 | |||
195 | if (!firmware_has_feature(FW_FEATURE_LPAR)) { | ||
196 | spu->priv1 = map_spe_prop(spu, node, "priv1"); | ||
197 | if (!spu->priv1) | ||
198 | goto out_unmap; | ||
199 | } | ||
200 | |||
201 | ret = 0; | ||
202 | goto out; | ||
203 | |||
204 | out_unmap: | ||
205 | spu_unmap(spu); | ||
206 | out: | ||
207 | return ret; | ||
208 | } | ||
209 | |||
210 | static int __init spu_map_interrupts(struct spu *spu, struct device_node *np) | ||
211 | { | ||
212 | struct of_irq oirq; | ||
213 | int ret; | ||
214 | int i; | ||
215 | |||
216 | for (i=0; i < 3; i++) { | ||
217 | ret = of_irq_map_one(np, i, &oirq); | ||
218 | if (ret) { | ||
219 | pr_debug("spu_new: failed to get irq %d\n", i); | ||
220 | goto err; | ||
221 | } | ||
222 | ret = -EINVAL; | ||
223 | pr_debug(" irq %d no 0x%x on %s\n", i, oirq.specifier[0], | ||
224 | oirq.controller->full_name); | ||
225 | spu->irqs[i] = irq_create_of_mapping(oirq.controller, | ||
226 | oirq.specifier, oirq.size); | ||
227 | if (spu->irqs[i] == NO_IRQ) { | ||
228 | pr_debug("spu_new: failed to map it !\n"); | ||
229 | goto err; | ||
230 | } | ||
231 | } | ||
232 | return 0; | ||
233 | |||
234 | err: | ||
235 | pr_debug("failed to map irq %x for spu %s\n", *oirq.specifier, | ||
236 | spu->name); | ||
237 | for (; i >= 0; i--) { | ||
238 | if (spu->irqs[i] != NO_IRQ) | ||
239 | irq_dispose_mapping(spu->irqs[i]); | ||
240 | } | ||
241 | return ret; | ||
242 | } | ||
243 | |||
244 | static int spu_map_resource(struct spu *spu, int nr, | ||
245 | void __iomem** virt, unsigned long *phys) | ||
246 | { | ||
247 | struct device_node *np = spu->devnode; | ||
248 | unsigned long start_pfn, nr_pages; | ||
249 | struct pglist_data *pgdata; | ||
250 | struct zone *zone; | ||
251 | struct resource resource = { }; | ||
252 | unsigned long len; | ||
253 | int ret; | ||
254 | |||
255 | ret = of_address_to_resource(np, nr, &resource); | ||
256 | if (ret) | ||
257 | goto out; | ||
258 | |||
259 | if (phys) | ||
260 | *phys = resource.start; | ||
261 | len = resource.end - resource.start + 1; | ||
262 | *virt = ioremap(resource.start, len); | ||
263 | if (!*virt) | ||
264 | ret = -EINVAL; | ||
265 | |||
266 | start_pfn = resource.start >> PAGE_SHIFT; | ||
267 | nr_pages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
268 | |||
269 | pgdata = NODE_DATA(spu->node); | ||
270 | zone = pgdata->node_zones; | ||
271 | |||
272 | ret = __add_pages(zone, start_pfn, nr_pages); | ||
273 | |||
274 | out: | ||
275 | return ret; | ||
276 | } | ||
277 | |||
278 | static int __init spu_map_device(struct spu *spu) | ||
279 | { | ||
280 | struct device_node *np = spu->devnode; | ||
281 | int ret = -ENODEV; | ||
282 | |||
283 | spu->name = get_property(np, "name", NULL); | ||
284 | if (!spu->name) | ||
285 | goto out; | ||
286 | |||
287 | ret = spu_map_resource(spu, 0, (void __iomem**)&spu->local_store, | ||
288 | &spu->local_store_phys); | ||
289 | if (ret) { | ||
290 | pr_debug("spu_new: failed to map %s resource 0\n", | ||
291 | np->full_name); | ||
292 | goto out; | ||
293 | } | ||
294 | ret = spu_map_resource(spu, 1, (void __iomem**)&spu->problem, | ||
295 | &spu->problem_phys); | ||
296 | if (ret) { | ||
297 | pr_debug("spu_new: failed to map %s resource 1\n", | ||
298 | np->full_name); | ||
299 | goto out_unmap; | ||
300 | } | ||
301 | ret = spu_map_resource(spu, 2, (void __iomem**)&spu->priv2, NULL); | ||
302 | if (ret) { | ||
303 | pr_debug("spu_new: failed to map %s resource 2\n", | ||
304 | np->full_name); | ||
305 | goto out_unmap; | ||
306 | } | ||
307 | if (!firmware_has_feature(FW_FEATURE_LPAR)) | ||
308 | ret = spu_map_resource(spu, 3, | ||
309 | (void __iomem**)&spu->priv1, NULL); | ||
310 | if (ret) { | ||
311 | pr_debug("spu_new: failed to map %s resource 3\n", | ||
312 | np->full_name); | ||
313 | goto out_unmap; | ||
314 | } | ||
315 | pr_debug("spu_new: %s maps:\n", np->full_name); | ||
316 | pr_debug(" local store : 0x%016lx -> 0x%p\n", | ||
317 | spu->local_store_phys, spu->local_store); | ||
318 | pr_debug(" problem state : 0x%016lx -> 0x%p\n", | ||
319 | spu->problem_phys, spu->problem); | ||
320 | pr_debug(" priv2 : 0x%p\n", spu->priv2); | ||
321 | pr_debug(" priv1 : 0x%p\n", spu->priv1); | ||
322 | |||
323 | return 0; | ||
324 | |||
325 | out_unmap: | ||
326 | spu_unmap(spu); | ||
327 | out: | ||
328 | pr_debug("failed to map spe %s: %d\n", spu->name, ret); | ||
329 | return ret; | ||
330 | } | ||
331 | |||
332 | static int __init of_enumerate_spus(int (*fn)(void *data)) | ||
333 | { | ||
334 | int ret; | ||
335 | struct device_node *node; | ||
336 | |||
337 | ret = -ENODEV; | ||
338 | for (node = of_find_node_by_type(NULL, "spe"); | ||
339 | node; node = of_find_node_by_type(node, "spe")) { | ||
340 | ret = fn(node); | ||
341 | if (ret) { | ||
342 | printk(KERN_WARNING "%s: Error initializing %s\n", | ||
343 | __FUNCTION__, node->name); | ||
344 | break; | ||
345 | } | ||
346 | } | ||
347 | return ret; | ||
348 | } | ||
349 | |||
350 | static int __init of_create_spu(struct spu *spu, void *data) | ||
351 | { | ||
352 | int ret; | ||
353 | struct device_node *spe = (struct device_node *)data; | ||
354 | static int legacy_map = 0, legacy_irq = 0; | ||
355 | |||
356 | spu->devnode = of_node_get(spe); | ||
357 | spu->spe_id = find_spu_unit_number(spe); | ||
358 | |||
359 | spu->node = of_node_to_nid(spe); | ||
360 | if (spu->node >= MAX_NUMNODES) { | ||
361 | printk(KERN_WARNING "SPE %s on node %d ignored," | ||
362 | " node number too big\n", spe->full_name, spu->node); | ||
363 | printk(KERN_WARNING "Check if CONFIG_NUMA is enabled.\n"); | ||
364 | ret = -ENODEV; | ||
365 | goto out; | ||
366 | } | ||
367 | |||
368 | ret = spu_map_device(spu); | ||
369 | if (ret) { | ||
370 | if (!legacy_map) { | ||
371 | legacy_map = 1; | ||
372 | printk(KERN_WARNING "%s: Legacy device tree found, " | ||
373 | "trying to map old style\n", __FUNCTION__); | ||
374 | } | ||
375 | ret = spu_map_device_old(spu); | ||
376 | if (ret) { | ||
377 | printk(KERN_ERR "Unable to map %s\n", | ||
378 | spu->name); | ||
379 | goto out; | ||
380 | } | ||
381 | } | ||
382 | |||
383 | ret = spu_map_interrupts(spu, spe); | ||
384 | if (ret) { | ||
385 | if (!legacy_irq) { | ||
386 | legacy_irq = 1; | ||
387 | printk(KERN_WARNING "%s: Legacy device tree found, " | ||
388 | "trying old style irq\n", __FUNCTION__); | ||
389 | } | ||
390 | ret = spu_map_interrupts_old(spu, spe); | ||
391 | if (ret) { | ||
392 | printk(KERN_ERR "%s: could not map interrupts", | ||
393 | spu->name); | ||
394 | goto out_unmap; | ||
395 | } | ||
396 | } | ||
397 | |||
398 | pr_debug("Using SPE %s %p %p %p %p %d\n", spu->name, | ||
399 | spu->local_store, spu->problem, spu->priv1, | ||
400 | spu->priv2, spu->number); | ||
401 | goto out; | ||
402 | |||
403 | out_unmap: | ||
404 | spu_unmap(spu); | ||
405 | out: | ||
406 | return ret; | ||
407 | } | ||
408 | |||
409 | static int of_destroy_spu(struct spu *spu) | ||
410 | { | ||
411 | spu_unmap(spu); | ||
412 | of_node_put(spu->devnode); | ||
413 | return 0; | ||
414 | } | ||
415 | |||
416 | const struct spu_management_ops spu_management_of_ops = { | ||
417 | .enumerate_spus = of_enumerate_spus, | ||
418 | .create_spu = of_create_spu, | ||
419 | .destroy_spu = of_destroy_spu, | ||
420 | }; | ||
diff --git a/arch/powerpc/platforms/cell/spu_priv1_mmio.c b/arch/powerpc/platforms/cell/spu_priv1_mmio.c index 910a926b61a2..67fa7247b80a 100644 --- a/arch/powerpc/platforms/cell/spu_priv1_mmio.c +++ b/arch/powerpc/platforms/cell/spu_priv1_mmio.c | |||
@@ -37,490 +37,112 @@ | |||
37 | #include "interrupt.h" | 37 | #include "interrupt.h" |
38 | #include "spu_priv1_mmio.h" | 38 | #include "spu_priv1_mmio.h" |
39 | 39 | ||
40 | static DEFINE_MUTEX(add_spumem_mutex); | ||
41 | |||
42 | struct spu_pdata { | ||
43 | struct device_node *devnode; | ||
44 | struct spu_priv1 __iomem *priv1; | ||
45 | }; | ||
46 | |||
47 | static struct spu_pdata *spu_get_pdata(struct spu *spu) | ||
48 | { | ||
49 | BUG_ON(!spu->pdata); | ||
50 | return spu->pdata; | ||
51 | } | ||
52 | |||
53 | struct device_node *spu_devnode(struct spu *spu) | ||
54 | { | ||
55 | return spu_get_pdata(spu)->devnode; | ||
56 | } | ||
57 | |||
58 | EXPORT_SYMBOL_GPL(spu_devnode); | ||
59 | |||
60 | static int __init cell_spuprop_present(struct spu *spu, struct device_node *spe, | ||
61 | const char *prop) | ||
62 | { | ||
63 | const struct address_prop { | ||
64 | unsigned long address; | ||
65 | unsigned int len; | ||
66 | } __attribute__((packed)) *p; | ||
67 | int proplen; | ||
68 | |||
69 | unsigned long start_pfn, nr_pages; | ||
70 | struct pglist_data *pgdata; | ||
71 | struct zone *zone; | ||
72 | int ret; | ||
73 | |||
74 | p = get_property(spe, prop, &proplen); | ||
75 | WARN_ON(proplen != sizeof (*p)); | ||
76 | |||
77 | start_pfn = p->address >> PAGE_SHIFT; | ||
78 | nr_pages = ((unsigned long)p->len + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
79 | |||
80 | pgdata = NODE_DATA(spu->node); | ||
81 | zone = pgdata->node_zones; | ||
82 | |||
83 | /* XXX rethink locking here */ | ||
84 | mutex_lock(&add_spumem_mutex); | ||
85 | ret = __add_pages(zone, start_pfn, nr_pages); | ||
86 | mutex_unlock(&add_spumem_mutex); | ||
87 | |||
88 | return ret; | ||
89 | } | ||
90 | |||
91 | static void __iomem * __init map_spe_prop(struct spu *spu, | ||
92 | struct device_node *n, const char *name) | ||
93 | { | ||
94 | const struct address_prop { | ||
95 | unsigned long address; | ||
96 | unsigned int len; | ||
97 | } __attribute__((packed)) *prop; | ||
98 | |||
99 | const void *p; | ||
100 | int proplen; | ||
101 | void __iomem *ret = NULL; | ||
102 | int err = 0; | ||
103 | |||
104 | p = get_property(n, name, &proplen); | ||
105 | if (proplen != sizeof (struct address_prop)) | ||
106 | return NULL; | ||
107 | |||
108 | prop = p; | ||
109 | |||
110 | err = cell_spuprop_present(spu, n, name); | ||
111 | if (err && (err != -EEXIST)) | ||
112 | goto out; | ||
113 | |||
114 | ret = ioremap(prop->address, prop->len); | ||
115 | |||
116 | out: | ||
117 | return ret; | ||
118 | } | ||
119 | |||
120 | static void spu_unmap(struct spu *spu) | ||
121 | { | ||
122 | iounmap(spu->priv2); | ||
123 | iounmap(spu_get_pdata(spu)->priv1); | ||
124 | iounmap(spu->problem); | ||
125 | iounmap((__force u8 __iomem *)spu->local_store); | ||
126 | } | ||
127 | |||
128 | static int __init spu_map_interrupts_old(struct spu *spu, | ||
129 | struct device_node *np) | ||
130 | { | ||
131 | unsigned int isrc; | ||
132 | const u32 *tmp; | ||
133 | int nid; | ||
134 | |||
135 | /* Get the interrupt source unit from the device-tree */ | ||
136 | tmp = get_property(np, "isrc", NULL); | ||
137 | if (!tmp) | ||
138 | return -ENODEV; | ||
139 | isrc = tmp[0]; | ||
140 | |||
141 | tmp = get_property(np->parent->parent, "node-id", NULL); | ||
142 | if (!tmp) { | ||
143 | printk(KERN_WARNING "%s: can't find node-id\n", __FUNCTION__); | ||
144 | nid = spu->node; | ||
145 | } else | ||
146 | nid = tmp[0]; | ||
147 | |||
148 | /* Add the node number */ | ||
149 | isrc |= nid << IIC_IRQ_NODE_SHIFT; | ||
150 | |||
151 | /* Now map interrupts of all 3 classes */ | ||
152 | spu->irqs[0] = irq_create_mapping(NULL, IIC_IRQ_CLASS_0 | isrc); | ||
153 | spu->irqs[1] = irq_create_mapping(NULL, IIC_IRQ_CLASS_1 | isrc); | ||
154 | spu->irqs[2] = irq_create_mapping(NULL, IIC_IRQ_CLASS_2 | isrc); | ||
155 | |||
156 | /* Right now, we only fail if class 2 failed */ | ||
157 | return spu->irqs[2] == NO_IRQ ? -EINVAL : 0; | ||
158 | } | ||
159 | |||
160 | static int __init spu_map_device_old(struct spu *spu, struct device_node *node) | ||
161 | { | ||
162 | const char *prop; | ||
163 | int ret; | ||
164 | |||
165 | ret = -ENODEV; | ||
166 | spu->name = get_property(node, "name", NULL); | ||
167 | if (!spu->name) | ||
168 | goto out; | ||
169 | |||
170 | prop = get_property(node, "local-store", NULL); | ||
171 | if (!prop) | ||
172 | goto out; | ||
173 | spu->local_store_phys = *(unsigned long *)prop; | ||
174 | |||
175 | /* we use local store as ram, not io memory */ | ||
176 | spu->local_store = (void __force *) | ||
177 | map_spe_prop(spu, node, "local-store"); | ||
178 | if (!spu->local_store) | ||
179 | goto out; | ||
180 | |||
181 | prop = get_property(node, "problem", NULL); | ||
182 | if (!prop) | ||
183 | goto out_unmap; | ||
184 | spu->problem_phys = *(unsigned long *)prop; | ||
185 | |||
186 | spu->problem= map_spe_prop(spu, node, "problem"); | ||
187 | if (!spu->problem) | ||
188 | goto out_unmap; | ||
189 | |||
190 | spu_get_pdata(spu)->priv1= map_spe_prop(spu, node, "priv1"); | ||
191 | |||
192 | spu->priv2= map_spe_prop(spu, node, "priv2"); | ||
193 | if (!spu->priv2) | ||
194 | goto out_unmap; | ||
195 | ret = 0; | ||
196 | goto out; | ||
197 | |||
198 | out_unmap: | ||
199 | spu_unmap(spu); | ||
200 | out: | ||
201 | return ret; | ||
202 | } | ||
203 | |||
204 | static int __init spu_map_interrupts(struct spu *spu, struct device_node *np) | ||
205 | { | ||
206 | struct of_irq oirq; | ||
207 | int ret; | ||
208 | int i; | ||
209 | |||
210 | for (i=0; i < 3; i++) { | ||
211 | ret = of_irq_map_one(np, i, &oirq); | ||
212 | if (ret) { | ||
213 | pr_debug("spu_new: failed to get irq %d\n", i); | ||
214 | goto err; | ||
215 | } | ||
216 | ret = -EINVAL; | ||
217 | pr_debug(" irq %d no 0x%x on %s\n", i, oirq.specifier[0], | ||
218 | oirq.controller->full_name); | ||
219 | spu->irqs[i] = irq_create_of_mapping(oirq.controller, | ||
220 | oirq.specifier, oirq.size); | ||
221 | if (spu->irqs[i] == NO_IRQ) { | ||
222 | pr_debug("spu_new: failed to map it !\n"); | ||
223 | goto err; | ||
224 | } | ||
225 | } | ||
226 | return 0; | ||
227 | |||
228 | err: | ||
229 | pr_debug("failed to map irq %x for spu %s\n", *oirq.specifier, | ||
230 | spu->name); | ||
231 | for (; i >= 0; i--) { | ||
232 | if (spu->irqs[i] != NO_IRQ) | ||
233 | irq_dispose_mapping(spu->irqs[i]); | ||
234 | } | ||
235 | return ret; | ||
236 | } | ||
237 | |||
238 | static int spu_map_resource(struct spu *spu, int nr, | ||
239 | void __iomem** virt, unsigned long *phys) | ||
240 | { | ||
241 | struct device_node *np = spu_get_pdata(spu)->devnode; | ||
242 | unsigned long start_pfn, nr_pages; | ||
243 | struct pglist_data *pgdata; | ||
244 | struct zone *zone; | ||
245 | struct resource resource = { }; | ||
246 | unsigned long len; | ||
247 | int ret; | ||
248 | |||
249 | ret = of_address_to_resource(np, nr, &resource); | ||
250 | if (ret) | ||
251 | goto out; | ||
252 | |||
253 | if (phys) | ||
254 | *phys = resource.start; | ||
255 | len = resource.end - resource.start + 1; | ||
256 | *virt = ioremap(resource.start, len); | ||
257 | if (!*virt) | ||
258 | ret = -EINVAL; | ||
259 | |||
260 | start_pfn = resource.start >> PAGE_SHIFT; | ||
261 | nr_pages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
262 | |||
263 | pgdata = NODE_DATA(spu->node); | ||
264 | zone = pgdata->node_zones; | ||
265 | |||
266 | /* XXX rethink locking here */ | ||
267 | mutex_lock(&add_spumem_mutex); | ||
268 | ret = __add_pages(zone, start_pfn, nr_pages); | ||
269 | mutex_unlock(&add_spumem_mutex); | ||
270 | |||
271 | out: | ||
272 | return ret; | ||
273 | } | ||
274 | |||
275 | static int __init spu_map_device(struct spu *spu) | ||
276 | { | ||
277 | struct device_node *np = spu_get_pdata(spu)->devnode; | ||
278 | int ret = -ENODEV; | ||
279 | |||
280 | spu->name = get_property(np, "name", NULL); | ||
281 | if (!spu->name) | ||
282 | goto out; | ||
283 | |||
284 | ret = spu_map_resource(spu, 0, (void __iomem**)&spu->local_store, | ||
285 | &spu->local_store_phys); | ||
286 | if (ret) { | ||
287 | pr_debug("spu_new: failed to map %s resource 0\n", | ||
288 | np->full_name); | ||
289 | goto out; | ||
290 | } | ||
291 | ret = spu_map_resource(spu, 1, (void __iomem**)&spu->problem, | ||
292 | &spu->problem_phys); | ||
293 | if (ret) { | ||
294 | pr_debug("spu_new: failed to map %s resource 1\n", | ||
295 | np->full_name); | ||
296 | goto out_unmap; | ||
297 | } | ||
298 | ret = spu_map_resource(spu, 2, (void __iomem**)&spu->priv2, NULL); | ||
299 | if (ret) { | ||
300 | pr_debug("spu_new: failed to map %s resource 2\n", | ||
301 | np->full_name); | ||
302 | goto out_unmap; | ||
303 | } | ||
304 | if (!firmware_has_feature(FW_FEATURE_LPAR)) | ||
305 | ret = spu_map_resource(spu, 3, | ||
306 | (void __iomem**)&spu_get_pdata(spu)->priv1, NULL); | ||
307 | if (ret) { | ||
308 | pr_debug("spu_new: failed to map %s resource 3\n", | ||
309 | np->full_name); | ||
310 | goto out_unmap; | ||
311 | } | ||
312 | pr_debug("spu_new: %s maps:\n", np->full_name); | ||
313 | pr_debug(" local store : 0x%016lx -> 0x%p\n", | ||
314 | spu->local_store_phys, spu->local_store); | ||
315 | pr_debug(" problem state : 0x%016lx -> 0x%p\n", | ||
316 | spu->problem_phys, spu->problem); | ||
317 | pr_debug(" priv2 : 0x%p\n", spu->priv2); | ||
318 | pr_debug(" priv1 : 0x%p\n", | ||
319 | spu_get_pdata(spu)->priv1); | ||
320 | |||
321 | return 0; | ||
322 | |||
323 | out_unmap: | ||
324 | spu_unmap(spu); | ||
325 | out: | ||
326 | pr_debug("failed to map spe %s: %d\n", spu->name, ret); | ||
327 | return ret; | ||
328 | } | ||
329 | |||
330 | static int __init of_enumerate_spus(int (*fn)(void *data)) | ||
331 | { | ||
332 | int ret; | ||
333 | struct device_node *node; | ||
334 | |||
335 | ret = -ENODEV; | ||
336 | for (node = of_find_node_by_type(NULL, "spe"); | ||
337 | node; node = of_find_node_by_type(node, "spe")) { | ||
338 | ret = fn(node); | ||
339 | if (ret) { | ||
340 | printk(KERN_WARNING "%s: Error initializing %s\n", | ||
341 | __FUNCTION__, node->name); | ||
342 | break; | ||
343 | } | ||
344 | } | ||
345 | return ret; | ||
346 | } | ||
347 | |||
348 | static int __init of_create_spu(struct spu *spu, void *data) | ||
349 | { | ||
350 | int ret; | ||
351 | struct device_node *spe = (struct device_node *)data; | ||
352 | |||
353 | spu->pdata = kzalloc(sizeof(struct spu_pdata), | ||
354 | GFP_KERNEL); | ||
355 | if (!spu->pdata) { | ||
356 | ret = -ENOMEM; | ||
357 | goto out; | ||
358 | } | ||
359 | spu_get_pdata(spu)->devnode = of_node_get(spe); | ||
360 | |||
361 | spu->node = of_node_to_nid(spe); | ||
362 | if (spu->node >= MAX_NUMNODES) { | ||
363 | printk(KERN_WARNING "SPE %s on node %d ignored," | ||
364 | " node number too big\n", spe->full_name, spu->node); | ||
365 | printk(KERN_WARNING "Check if CONFIG_NUMA is enabled.\n"); | ||
366 | ret = -ENODEV; | ||
367 | goto out_free; | ||
368 | } | ||
369 | |||
370 | ret = spu_map_device(spu); | ||
371 | /* try old method */ | ||
372 | if (ret) | ||
373 | ret = spu_map_device_old(spu, spe); | ||
374 | if (ret) | ||
375 | goto out_free; | ||
376 | |||
377 | ret = spu_map_interrupts(spu, spe); | ||
378 | if (ret) | ||
379 | ret = spu_map_interrupts_old(spu, spe); | ||
380 | if (ret) | ||
381 | goto out_unmap; | ||
382 | |||
383 | pr_debug(KERN_DEBUG "Using SPE %s %p %p %p %p %d\n", spu->name, | ||
384 | spu->local_store, spu->problem, spu_get_pdata(spu)->priv1, | ||
385 | spu->priv2, spu->number); | ||
386 | goto out; | ||
387 | |||
388 | out_unmap: | ||
389 | spu_unmap(spu); | ||
390 | out_free: | ||
391 | kfree(spu->pdata); | ||
392 | spu->pdata = NULL; | ||
393 | out: | ||
394 | return ret; | ||
395 | } | ||
396 | |||
397 | static int of_destroy_spu(struct spu *spu) | ||
398 | { | ||
399 | spu_unmap(spu); | ||
400 | of_node_put(spu_get_pdata(spu)->devnode); | ||
401 | kfree(spu->pdata); | ||
402 | spu->pdata = NULL; | ||
403 | return 0; | ||
404 | } | ||
405 | |||
406 | const struct spu_management_ops spu_management_of_ops = { | ||
407 | .enumerate_spus = of_enumerate_spus, | ||
408 | .create_spu = of_create_spu, | ||
409 | .destroy_spu = of_destroy_spu, | ||
410 | }; | ||
411 | |||
412 | static void int_mask_and(struct spu *spu, int class, u64 mask) | 40 | static void int_mask_and(struct spu *spu, int class, u64 mask) |
413 | { | 41 | { |
414 | u64 old_mask; | 42 | u64 old_mask; |
415 | 43 | ||
416 | old_mask = in_be64(&spu_get_pdata(spu)->priv1->int_mask_RW[class]); | 44 | old_mask = in_be64(&spu->priv1->int_mask_RW[class]); |
417 | out_be64(&spu_get_pdata(spu)->priv1->int_mask_RW[class], | 45 | out_be64(&spu->priv1->int_mask_RW[class], old_mask & mask); |
418 | old_mask & mask); | ||
419 | } | 46 | } |
420 | 47 | ||
421 | static void int_mask_or(struct spu *spu, int class, u64 mask) | 48 | static void int_mask_or(struct spu *spu, int class, u64 mask) |
422 | { | 49 | { |
423 | u64 old_mask; | 50 | u64 old_mask; |
424 | 51 | ||
425 | old_mask = in_be64(&spu_get_pdata(spu)->priv1->int_mask_RW[class]); | 52 | old_mask = in_be64(&spu->priv1->int_mask_RW[class]); |
426 | out_be64(&spu_get_pdata(spu)->priv1->int_mask_RW[class], | 53 | out_be64(&spu->priv1->int_mask_RW[class], old_mask | mask); |
427 | old_mask | mask); | ||
428 | } | 54 | } |
429 | 55 | ||
430 | static void int_mask_set(struct spu *spu, int class, u64 mask) | 56 | static void int_mask_set(struct spu *spu, int class, u64 mask) |
431 | { | 57 | { |
432 | out_be64(&spu_get_pdata(spu)->priv1->int_mask_RW[class], mask); | 58 | out_be64(&spu->priv1->int_mask_RW[class], mask); |
433 | } | 59 | } |
434 | 60 | ||
435 | static u64 int_mask_get(struct spu *spu, int class) | 61 | static u64 int_mask_get(struct spu *spu, int class) |
436 | { | 62 | { |
437 | return in_be64(&spu_get_pdata(spu)->priv1->int_mask_RW[class]); | 63 | return in_be64(&spu->priv1->int_mask_RW[class]); |
438 | } | 64 | } |
439 | 65 | ||
440 | static void int_stat_clear(struct spu *spu, int class, u64 stat) | 66 | static void int_stat_clear(struct spu *spu, int class, u64 stat) |
441 | { | 67 | { |
442 | out_be64(&spu_get_pdata(spu)->priv1->int_stat_RW[class], stat); | 68 | out_be64(&spu->priv1->int_stat_RW[class], stat); |
443 | } | 69 | } |
444 | 70 | ||
445 | static u64 int_stat_get(struct spu *spu, int class) | 71 | static u64 int_stat_get(struct spu *spu, int class) |
446 | { | 72 | { |
447 | return in_be64(&spu_get_pdata(spu)->priv1->int_stat_RW[class]); | 73 | return in_be64(&spu->priv1->int_stat_RW[class]); |
448 | } | 74 | } |
449 | 75 | ||
450 | static void cpu_affinity_set(struct spu *spu, int cpu) | 76 | static void cpu_affinity_set(struct spu *spu, int cpu) |
451 | { | 77 | { |
452 | u64 target = iic_get_target_id(cpu); | 78 | u64 target = iic_get_target_id(cpu); |
453 | u64 route = target << 48 | target << 32 | target << 16; | 79 | u64 route = target << 48 | target << 32 | target << 16; |
454 | out_be64(&spu_get_pdata(spu)->priv1->int_route_RW, route); | 80 | out_be64(&spu->priv1->int_route_RW, route); |
455 | } | 81 | } |
456 | 82 | ||
457 | static u64 mfc_dar_get(struct spu *spu) | 83 | static u64 mfc_dar_get(struct spu *spu) |
458 | { | 84 | { |
459 | return in_be64(&spu_get_pdata(spu)->priv1->mfc_dar_RW); | 85 | return in_be64(&spu->priv1->mfc_dar_RW); |
460 | } | 86 | } |
461 | 87 | ||
462 | static u64 mfc_dsisr_get(struct spu *spu) | 88 | static u64 mfc_dsisr_get(struct spu *spu) |
463 | { | 89 | { |
464 | return in_be64(&spu_get_pdata(spu)->priv1->mfc_dsisr_RW); | 90 | return in_be64(&spu->priv1->mfc_dsisr_RW); |
465 | } | 91 | } |
466 | 92 | ||
467 | static void mfc_dsisr_set(struct spu *spu, u64 dsisr) | 93 | static void mfc_dsisr_set(struct spu *spu, u64 dsisr) |
468 | { | 94 | { |
469 | out_be64(&spu_get_pdata(spu)->priv1->mfc_dsisr_RW, dsisr); | 95 | out_be64(&spu->priv1->mfc_dsisr_RW, dsisr); |
470 | } | 96 | } |
471 | 97 | ||
472 | static void mfc_sdr_setup(struct spu *spu) | 98 | static void mfc_sdr_setup(struct spu *spu) |
473 | { | 99 | { |
474 | out_be64(&spu_get_pdata(spu)->priv1->mfc_sdr_RW, mfspr(SPRN_SDR1)); | 100 | out_be64(&spu->priv1->mfc_sdr_RW, mfspr(SPRN_SDR1)); |
475 | } | 101 | } |
476 | 102 | ||
477 | static void mfc_sr1_set(struct spu *spu, u64 sr1) | 103 | static void mfc_sr1_set(struct spu *spu, u64 sr1) |
478 | { | 104 | { |
479 | out_be64(&spu_get_pdata(spu)->priv1->mfc_sr1_RW, sr1); | 105 | out_be64(&spu->priv1->mfc_sr1_RW, sr1); |
480 | } | 106 | } |
481 | 107 | ||
482 | static u64 mfc_sr1_get(struct spu *spu) | 108 | static u64 mfc_sr1_get(struct spu *spu) |
483 | { | 109 | { |
484 | return in_be64(&spu_get_pdata(spu)->priv1->mfc_sr1_RW); | 110 | return in_be64(&spu->priv1->mfc_sr1_RW); |
485 | } | 111 | } |
486 | 112 | ||
487 | static void mfc_tclass_id_set(struct spu *spu, u64 tclass_id) | 113 | static void mfc_tclass_id_set(struct spu *spu, u64 tclass_id) |
488 | { | 114 | { |
489 | out_be64(&spu_get_pdata(spu)->priv1->mfc_tclass_id_RW, tclass_id); | 115 | out_be64(&spu->priv1->mfc_tclass_id_RW, tclass_id); |
490 | } | 116 | } |
491 | 117 | ||
492 | static u64 mfc_tclass_id_get(struct spu *spu) | 118 | static u64 mfc_tclass_id_get(struct spu *spu) |
493 | { | 119 | { |
494 | return in_be64(&spu_get_pdata(spu)->priv1->mfc_tclass_id_RW); | 120 | return in_be64(&spu->priv1->mfc_tclass_id_RW); |
495 | } | 121 | } |
496 | 122 | ||
497 | static void tlb_invalidate(struct spu *spu) | 123 | static void tlb_invalidate(struct spu *spu) |
498 | { | 124 | { |
499 | out_be64(&spu_get_pdata(spu)->priv1->tlb_invalidate_entry_W, 0ul); | 125 | out_be64(&spu->priv1->tlb_invalidate_entry_W, 0ul); |
500 | } | 126 | } |
501 | 127 | ||
502 | static void resource_allocation_groupID_set(struct spu *spu, u64 id) | 128 | static void resource_allocation_groupID_set(struct spu *spu, u64 id) |
503 | { | 129 | { |
504 | out_be64(&spu_get_pdata(spu)->priv1->resource_allocation_groupID_RW, | 130 | out_be64(&spu->priv1->resource_allocation_groupID_RW, id); |
505 | id); | ||
506 | } | 131 | } |
507 | 132 | ||
508 | static u64 resource_allocation_groupID_get(struct spu *spu) | 133 | static u64 resource_allocation_groupID_get(struct spu *spu) |
509 | { | 134 | { |
510 | return in_be64( | 135 | return in_be64(&spu->priv1->resource_allocation_groupID_RW); |
511 | &spu_get_pdata(spu)->priv1->resource_allocation_groupID_RW); | ||
512 | } | 136 | } |
513 | 137 | ||
514 | static void resource_allocation_enable_set(struct spu *spu, u64 enable) | 138 | static void resource_allocation_enable_set(struct spu *spu, u64 enable) |
515 | { | 139 | { |
516 | out_be64(&spu_get_pdata(spu)->priv1->resource_allocation_enable_RW, | 140 | out_be64(&spu->priv1->resource_allocation_enable_RW, enable); |
517 | enable); | ||
518 | } | 141 | } |
519 | 142 | ||
520 | static u64 resource_allocation_enable_get(struct spu *spu) | 143 | static u64 resource_allocation_enable_get(struct spu *spu) |
521 | { | 144 | { |
522 | return in_be64( | 145 | return in_be64(&spu->priv1->resource_allocation_enable_RW); |
523 | &spu_get_pdata(spu)->priv1->resource_allocation_enable_RW); | ||
524 | } | 146 | } |
525 | 147 | ||
526 | const struct spu_priv1_ops spu_priv1_mmio_ops = | 148 | const struct spu_priv1_ops spu_priv1_mmio_ops = |