1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * PS3 Platform spu routines. |
4 | * |
5 | * Copyright (C) 2006 Sony Computer Entertainment Inc. |
6 | * Copyright 2006 Sony Corp. |
7 | */ |
8 | |
9 | #include <linux/kernel.h> |
10 | #include <linux/init.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/mmzone.h> |
13 | #include <linux/export.h> |
14 | #include <linux/io.h> |
15 | #include <linux/mm.h> |
16 | |
17 | #include <asm/spu.h> |
18 | #include <asm/spu_priv1.h> |
19 | #include <asm/lv1call.h> |
20 | #include <asm/ps3.h> |
21 | |
22 | #include "../cell/spufs/spufs.h" |
23 | #include "platform.h" |
24 | |
25 | /* spu_management_ops */ |
26 | |
27 | /** |
28 | * enum spe_type - Type of spe to create. |
29 | * @spe_type_logical: Standard logical spe. |
30 | * |
31 | * For use with lv1_construct_logical_spe(). The current HV does not support |
32 | * any types other than those listed. |
33 | */ |
34 | |
35 | enum spe_type { |
36 | SPE_TYPE_LOGICAL = 0, |
37 | }; |
38 | |
39 | /** |
40 | * struct spe_shadow - logical spe shadow register area. |
41 | * |
42 | * Read-only shadow of spe registers. |
43 | */ |
44 | |
45 | struct spe_shadow { |
46 | u8 padding_0140[0x0140]; |
47 | u64 int_status_class0_RW; /* 0x0140 */ |
48 | u64 int_status_class1_RW; /* 0x0148 */ |
49 | u64 int_status_class2_RW; /* 0x0150 */ |
50 | u8 padding_0158[0x0610-0x0158]; |
51 | u64 mfc_dsisr_RW; /* 0x0610 */ |
52 | u8 padding_0618[0x0620-0x0618]; |
53 | u64 mfc_dar_RW; /* 0x0620 */ |
54 | u8 padding_0628[0x0800-0x0628]; |
55 | u64 mfc_dsipr_R; /* 0x0800 */ |
56 | u8 padding_0808[0x0810-0x0808]; |
57 | u64 mfc_lscrr_R; /* 0x0810 */ |
58 | u8 padding_0818[0x0c00-0x0818]; |
59 | u64 mfc_cer_R; /* 0x0c00 */ |
60 | u8 padding_0c08[0x0f00-0x0c08]; |
61 | u64 spe_execution_status; /* 0x0f00 */ |
62 | u8 padding_0f08[0x1000-0x0f08]; |
63 | }; |
64 | |
65 | /** |
66 | * enum spe_ex_state - Logical spe execution state. |
67 | * @spe_ex_state_unexecutable: Uninitialized. |
68 | * @spe_ex_state_executable: Enabled, not ready. |
69 | * @spe_ex_state_executed: Ready for use. |
70 | * |
71 | * The execution state (status) of the logical spe as reported in |
72 | * struct spe_shadow:spe_execution_status. |
73 | */ |
74 | |
75 | enum spe_ex_state { |
76 | SPE_EX_STATE_UNEXECUTABLE = 0, |
77 | SPE_EX_STATE_EXECUTABLE = 2, |
78 | SPE_EX_STATE_EXECUTED = 3, |
79 | }; |
80 | |
81 | /** |
82 | * struct priv1_cache - Cached values of priv1 registers. |
83 | * @masks[]: Array of cached spe interrupt masks, indexed by class. |
84 | * @sr1: Cached mfc_sr1 register. |
85 | * @tclass_id: Cached mfc_tclass_id register. |
86 | */ |
87 | |
88 | struct priv1_cache { |
89 | u64 masks[3]; |
90 | u64 sr1; |
91 | u64 tclass_id; |
92 | }; |
93 | |
94 | /** |
95 | * struct spu_pdata - Platform state variables. |
96 | * @spe_id: HV spe id returned by lv1_construct_logical_spe(). |
97 | * @resource_id: HV spe resource id returned by |
98 | * ps3_repository_read_spe_resource_id(). |
99 | * @priv2_addr: lpar address of spe priv2 area returned by |
100 | * lv1_construct_logical_spe(). |
101 | * @shadow_addr: lpar address of spe register shadow area returned by |
102 | * lv1_construct_logical_spe(). |
103 | * @shadow: Virtual (ioremap) address of spe register shadow area. |
104 | * @cache: Cached values of priv1 registers. |
105 | */ |
106 | |
107 | struct spu_pdata { |
108 | u64 spe_id; |
109 | u64 resource_id; |
110 | u64 priv2_addr; |
111 | u64 shadow_addr; |
112 | struct spe_shadow __iomem *shadow; |
113 | struct priv1_cache cache; |
114 | }; |
115 | |
116 | static struct spu_pdata *spu_pdata(struct spu *spu) |
117 | { |
118 | return spu->pdata; |
119 | } |
120 | |
121 | #define dump_areas(_a, _b, _c, _d, _e) \ |
122 | _dump_areas(_a, _b, _c, _d, _e, __func__, __LINE__) |
123 | static void _dump_areas(unsigned int spe_id, unsigned long priv2, |
124 | unsigned long problem, unsigned long ls, unsigned long shadow, |
125 | const char* func, int line) |
126 | { |
127 | pr_debug("%s:%d: spe_id: %xh (%u)\n" , func, line, spe_id, spe_id); |
128 | pr_debug("%s:%d: priv2: %lxh\n" , func, line, priv2); |
129 | pr_debug("%s:%d: problem: %lxh\n" , func, line, problem); |
130 | pr_debug("%s:%d: ls: %lxh\n" , func, line, ls); |
131 | pr_debug("%s:%d: shadow: %lxh\n" , func, line, shadow); |
132 | } |
133 | |
134 | u64 ps3_get_spe_id(void *arg) |
135 | { |
136 | return spu_pdata(spu: arg)->spe_id; |
137 | } |
138 | EXPORT_SYMBOL_GPL(ps3_get_spe_id); |
139 | |
140 | static unsigned long __init get_vas_id(void) |
141 | { |
142 | u64 id; |
143 | |
144 | lv1_get_logical_ppe_id(&id); |
145 | lv1_get_virtual_address_space_id_of_ppe(&id); |
146 | |
147 | return id; |
148 | } |
149 | |
150 | static int __init construct_spu(struct spu *spu) |
151 | { |
152 | int result; |
153 | u64 unused; |
154 | u64 problem_phys; |
155 | u64 local_store_phys; |
156 | |
157 | result = lv1_construct_logical_spe(PAGE_SHIFT, PAGE_SHIFT, PAGE_SHIFT, |
158 | PAGE_SHIFT, PAGE_SHIFT, get_vas_id(), SPE_TYPE_LOGICAL, |
159 | &spu_pdata(spu)->priv2_addr, &problem_phys, |
160 | &local_store_phys, &unused, |
161 | &spu_pdata(spu)->shadow_addr, |
162 | &spu_pdata(spu)->spe_id); |
163 | spu->problem_phys = problem_phys; |
164 | spu->local_store_phys = local_store_phys; |
165 | |
166 | if (result) { |
167 | pr_debug("%s:%d: lv1_construct_logical_spe failed: %s\n" , |
168 | __func__, __LINE__, ps3_result(result)); |
169 | return result; |
170 | } |
171 | |
172 | return result; |
173 | } |
174 | |
175 | static void spu_unmap(struct spu *spu) |
176 | { |
177 | iounmap(addr: spu->priv2); |
178 | iounmap(addr: spu->problem); |
179 | iounmap(addr: (__force u8 __iomem *)spu->local_store); |
180 | iounmap(addr: spu_pdata(spu)->shadow); |
181 | } |
182 | |
183 | /** |
184 | * setup_areas - Map the spu regions into the address space. |
185 | * |
186 | * The current HV requires the spu shadow regs to be mapped with the |
187 | * PTE page protection bits set as read-only. |
188 | */ |
189 | |
190 | static int __init setup_areas(struct spu *spu) |
191 | { |
192 | struct table {char* name; unsigned long addr; unsigned long size;}; |
193 | unsigned long shadow_flags = pgprot_val(pgprot_noncached_wc(PAGE_KERNEL_RO)); |
194 | |
195 | spu_pdata(spu)->shadow = ioremap_prot(offset: spu_pdata(spu)->shadow_addr, |
196 | size: sizeof(struct spe_shadow), prot_val: shadow_flags); |
197 | if (!spu_pdata(spu)->shadow) { |
198 | pr_debug("%s:%d: ioremap shadow failed\n" , __func__, __LINE__); |
199 | goto fail_ioremap; |
200 | } |
201 | |
202 | spu->local_store = (__force void *)ioremap_wc(spu->local_store_phys, LS_SIZE); |
203 | |
204 | if (!spu->local_store) { |
205 | pr_debug("%s:%d: ioremap local_store failed\n" , |
206 | __func__, __LINE__); |
207 | goto fail_ioremap; |
208 | } |
209 | |
210 | spu->problem = ioremap(spu->problem_phys, |
211 | sizeof(struct spu_problem)); |
212 | |
213 | if (!spu->problem) { |
214 | pr_debug("%s:%d: ioremap problem failed\n" , __func__, __LINE__); |
215 | goto fail_ioremap; |
216 | } |
217 | |
218 | spu->priv2 = ioremap(spu_pdata(spu)->priv2_addr, |
219 | sizeof(struct spu_priv2)); |
220 | |
221 | if (!spu->priv2) { |
222 | pr_debug("%s:%d: ioremap priv2 failed\n" , __func__, __LINE__); |
223 | goto fail_ioremap; |
224 | } |
225 | |
226 | dump_areas(spu_pdata(spu)->spe_id, spu_pdata(spu)->priv2_addr, |
227 | spu->problem_phys, spu->local_store_phys, |
228 | spu_pdata(spu)->shadow_addr); |
229 | dump_areas(spu_pdata(spu)->spe_id, (unsigned long)spu->priv2, |
230 | (unsigned long)spu->problem, (unsigned long)spu->local_store, |
231 | (unsigned long)spu_pdata(spu)->shadow); |
232 | |
233 | return 0; |
234 | |
235 | fail_ioremap: |
236 | spu_unmap(spu); |
237 | |
238 | return -ENOMEM; |
239 | } |
240 | |
241 | static int __init setup_interrupts(struct spu *spu) |
242 | { |
243 | int result; |
244 | |
245 | result = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id, |
246 | 0, &spu->irqs[0]); |
247 | |
248 | if (result) |
249 | goto fail_alloc_0; |
250 | |
251 | result = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id, |
252 | 1, &spu->irqs[1]); |
253 | |
254 | if (result) |
255 | goto fail_alloc_1; |
256 | |
257 | result = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id, |
258 | 2, &spu->irqs[2]); |
259 | |
260 | if (result) |
261 | goto fail_alloc_2; |
262 | |
263 | return result; |
264 | |
265 | fail_alloc_2: |
266 | ps3_spe_irq_destroy(spu->irqs[1]); |
267 | fail_alloc_1: |
268 | ps3_spe_irq_destroy(spu->irqs[0]); |
269 | fail_alloc_0: |
270 | spu->irqs[0] = spu->irqs[1] = spu->irqs[2] = 0; |
271 | return result; |
272 | } |
273 | |
274 | static int __init enable_spu(struct spu *spu) |
275 | { |
276 | int result; |
277 | |
278 | result = lv1_enable_logical_spe(spu_pdata(spu)->spe_id, |
279 | spu_pdata(spu)->resource_id); |
280 | |
281 | if (result) { |
282 | pr_debug("%s:%d: lv1_enable_logical_spe failed: %s\n" , |
283 | __func__, __LINE__, ps3_result(result)); |
284 | goto fail_enable; |
285 | } |
286 | |
287 | result = setup_areas(spu); |
288 | |
289 | if (result) |
290 | goto fail_areas; |
291 | |
292 | result = setup_interrupts(spu); |
293 | |
294 | if (result) |
295 | goto fail_interrupts; |
296 | |
297 | return 0; |
298 | |
299 | fail_interrupts: |
300 | spu_unmap(spu); |
301 | fail_areas: |
302 | lv1_disable_logical_spe(spu_pdata(spu)->spe_id, 0); |
303 | fail_enable: |
304 | return result; |
305 | } |
306 | |
307 | static int ps3_destroy_spu(struct spu *spu) |
308 | { |
309 | int result; |
310 | |
311 | pr_debug("%s:%d spu_%d\n" , __func__, __LINE__, spu->number); |
312 | |
313 | result = lv1_disable_logical_spe(spu_pdata(spu)->spe_id, 0); |
314 | BUG_ON(result); |
315 | |
316 | ps3_spe_irq_destroy(spu->irqs[2]); |
317 | ps3_spe_irq_destroy(spu->irqs[1]); |
318 | ps3_spe_irq_destroy(spu->irqs[0]); |
319 | |
320 | spu->irqs[0] = spu->irqs[1] = spu->irqs[2] = 0; |
321 | |
322 | spu_unmap(spu); |
323 | |
324 | result = lv1_destruct_logical_spe(spu_pdata(spu)->spe_id); |
325 | BUG_ON(result); |
326 | |
327 | kfree(objp: spu->pdata); |
328 | spu->pdata = NULL; |
329 | |
330 | return 0; |
331 | } |
332 | |
333 | static int __init ps3_create_spu(struct spu *spu, void *data) |
334 | { |
335 | int result; |
336 | |
337 | pr_debug("%s:%d spu_%d\n" , __func__, __LINE__, spu->number); |
338 | |
339 | spu->pdata = kzalloc(size: sizeof(struct spu_pdata), |
340 | GFP_KERNEL); |
341 | |
342 | if (!spu->pdata) { |
343 | result = -ENOMEM; |
344 | goto fail_malloc; |
345 | } |
346 | |
347 | spu_pdata(spu)->resource_id = (unsigned long)data; |
348 | |
349 | /* Init cached reg values to HV defaults. */ |
350 | |
351 | spu_pdata(spu)->cache.sr1 = 0x33; |
352 | |
353 | result = construct_spu(spu); |
354 | |
355 | if (result) |
356 | goto fail_construct; |
357 | |
358 | /* For now, just go ahead and enable it. */ |
359 | |
360 | result = enable_spu(spu); |
361 | |
362 | if (result) |
363 | goto fail_enable; |
364 | |
365 | /* Make sure the spu is in SPE_EX_STATE_EXECUTED. */ |
366 | |
367 | /* need something better here!!! */ |
368 | while (in_be64(&spu_pdata(spu)->shadow->spe_execution_status) |
369 | != SPE_EX_STATE_EXECUTED) |
370 | (void)0; |
371 | |
372 | return result; |
373 | |
374 | fail_enable: |
375 | fail_construct: |
376 | ps3_destroy_spu(spu); |
377 | fail_malloc: |
378 | return result; |
379 | } |
380 | |
381 | static int __init ps3_enumerate_spus(int (*fn)(void *data)) |
382 | { |
383 | int result; |
384 | unsigned int num_resource_id; |
385 | unsigned int i; |
386 | |
387 | result = ps3_repository_read_num_spu_resource_id(num_resource_id: &num_resource_id); |
388 | |
389 | pr_debug("%s:%d: num_resource_id %u\n" , __func__, __LINE__, |
390 | num_resource_id); |
391 | |
392 | /* |
393 | * For now, just create logical spus equal to the number |
394 | * of physical spus reserved for the partition. |
395 | */ |
396 | |
397 | for (i = 0; i < num_resource_id; i++) { |
398 | enum ps3_spu_resource_type resource_type; |
399 | unsigned int resource_id; |
400 | |
401 | result = ps3_repository_read_spu_resource_id(res_index: i, |
402 | resource_type: &resource_type, resource_id: &resource_id); |
403 | |
404 | if (result) |
405 | break; |
406 | |
407 | if (resource_type == PS3_SPU_RESOURCE_TYPE_EXCLUSIVE) { |
408 | result = fn((void*)(unsigned long)resource_id); |
409 | |
410 | if (result) |
411 | break; |
412 | } |
413 | } |
414 | |
415 | if (result) { |
416 | printk(KERN_WARNING "%s:%d: Error initializing spus\n" , |
417 | __func__, __LINE__); |
418 | return result; |
419 | } |
420 | |
421 | return num_resource_id; |
422 | } |
423 | |
424 | static int ps3_init_affinity(void) |
425 | { |
426 | return 0; |
427 | } |
428 | |
429 | /** |
430 | * ps3_enable_spu - Enable SPU run control. |
431 | * |
432 | * An outstanding enhancement for the PS3 would be to add a guard to check |
433 | * for incorrect access to the spu problem state when the spu context is |
434 | * disabled. This check could be implemented with a flag added to the spu |
435 | * context that would inhibit mapping problem state pages, and a routine |
436 | * to unmap spu problem state pages. When the spu is enabled with |
437 | * ps3_enable_spu() the flag would be set allowing pages to be mapped, |
438 | * and when the spu is disabled with ps3_disable_spu() the flag would be |
439 | * cleared and the mapped problem state pages would be unmapped. |
440 | */ |
441 | |
442 | static void ps3_enable_spu(struct spu_context *ctx) |
443 | { |
444 | } |
445 | |
446 | static void ps3_disable_spu(struct spu_context *ctx) |
447 | { |
448 | ctx->ops->runcntl_stop(ctx); |
449 | } |
450 | |
451 | static const struct spu_management_ops spu_management_ps3_ops = { |
452 | .enumerate_spus = ps3_enumerate_spus, |
453 | .create_spu = ps3_create_spu, |
454 | .destroy_spu = ps3_destroy_spu, |
455 | .enable_spu = ps3_enable_spu, |
456 | .disable_spu = ps3_disable_spu, |
457 | .init_affinity = ps3_init_affinity, |
458 | }; |
459 | |
460 | /* spu_priv1_ops */ |
461 | |
462 | static void int_mask_and(struct spu *spu, int class, u64 mask) |
463 | { |
464 | u64 old_mask; |
465 | |
466 | /* are these serialized by caller??? */ |
467 | old_mask = spu_int_mask_get(spu, class); |
468 | spu_int_mask_set(spu, class, old_mask & mask); |
469 | } |
470 | |
471 | static void int_mask_or(struct spu *spu, int class, u64 mask) |
472 | { |
473 | u64 old_mask; |
474 | |
475 | old_mask = spu_int_mask_get(spu, class); |
476 | spu_int_mask_set(spu, class, old_mask | mask); |
477 | } |
478 | |
479 | static void int_mask_set(struct spu *spu, int class, u64 mask) |
480 | { |
481 | spu_pdata(spu)->cache.masks[class] = mask; |
482 | lv1_set_spe_interrupt_mask(spu_pdata(spu)->spe_id, class, |
483 | spu_pdata(spu)->cache.masks[class]); |
484 | } |
485 | |
486 | static u64 int_mask_get(struct spu *spu, int class) |
487 | { |
488 | return spu_pdata(spu)->cache.masks[class]; |
489 | } |
490 | |
491 | static void int_stat_clear(struct spu *spu, int class, u64 stat) |
492 | { |
493 | /* Note that MFC_DSISR will be cleared when class1[MF] is set. */ |
494 | |
495 | lv1_clear_spe_interrupt_status(spu_pdata(spu)->spe_id, class, |
496 | stat, 0); |
497 | } |
498 | |
499 | static u64 int_stat_get(struct spu *spu, int class) |
500 | { |
501 | u64 stat; |
502 | |
503 | lv1_get_spe_interrupt_status(spu_pdata(spu)->spe_id, class, &stat); |
504 | return stat; |
505 | } |
506 | |
507 | static void cpu_affinity_set(struct spu *spu, int cpu) |
508 | { |
509 | /* No support. */ |
510 | } |
511 | |
512 | static u64 mfc_dar_get(struct spu *spu) |
513 | { |
514 | return in_be64(&spu_pdata(spu)->shadow->mfc_dar_RW); |
515 | } |
516 | |
517 | static void mfc_dsisr_set(struct spu *spu, u64 dsisr) |
518 | { |
519 | /* Nothing to do, cleared in int_stat_clear(). */ |
520 | } |
521 | |
522 | static u64 mfc_dsisr_get(struct spu *spu) |
523 | { |
524 | return in_be64(&spu_pdata(spu)->shadow->mfc_dsisr_RW); |
525 | } |
526 | |
527 | static void mfc_sdr_setup(struct spu *spu) |
528 | { |
529 | /* Nothing to do. */ |
530 | } |
531 | |
532 | static void mfc_sr1_set(struct spu *spu, u64 sr1) |
533 | { |
534 | /* Check bits allowed by HV. */ |
535 | |
536 | static const u64 allowed = ~(MFC_STATE1_LOCAL_STORAGE_DECODE_MASK |
537 | | MFC_STATE1_PROBLEM_STATE_MASK); |
538 | |
539 | BUG_ON((sr1 & allowed) != (spu_pdata(spu)->cache.sr1 & allowed)); |
540 | |
541 | spu_pdata(spu)->cache.sr1 = sr1; |
542 | lv1_set_spe_privilege_state_area_1_register( |
543 | spu_pdata(spu)->spe_id, |
544 | offsetof(struct spu_priv1, mfc_sr1_RW), |
545 | spu_pdata(spu)->cache.sr1); |
546 | } |
547 | |
548 | static u64 mfc_sr1_get(struct spu *spu) |
549 | { |
550 | return spu_pdata(spu)->cache.sr1; |
551 | } |
552 | |
553 | static void mfc_tclass_id_set(struct spu *spu, u64 tclass_id) |
554 | { |
555 | spu_pdata(spu)->cache.tclass_id = tclass_id; |
556 | lv1_set_spe_privilege_state_area_1_register( |
557 | spu_pdata(spu)->spe_id, |
558 | offsetof(struct spu_priv1, mfc_tclass_id_RW), |
559 | spu_pdata(spu)->cache.tclass_id); |
560 | } |
561 | |
562 | static u64 mfc_tclass_id_get(struct spu *spu) |
563 | { |
564 | return spu_pdata(spu)->cache.tclass_id; |
565 | } |
566 | |
567 | static void tlb_invalidate(struct spu *spu) |
568 | { |
569 | /* Nothing to do. */ |
570 | } |
571 | |
572 | static void resource_allocation_groupID_set(struct spu *spu, u64 id) |
573 | { |
574 | /* No support. */ |
575 | } |
576 | |
577 | static u64 resource_allocation_groupID_get(struct spu *spu) |
578 | { |
579 | return 0; /* No support. */ |
580 | } |
581 | |
582 | static void resource_allocation_enable_set(struct spu *spu, u64 enable) |
583 | { |
584 | /* No support. */ |
585 | } |
586 | |
587 | static u64 resource_allocation_enable_get(struct spu *spu) |
588 | { |
589 | return 0; /* No support. */ |
590 | } |
591 | |
592 | static const struct spu_priv1_ops spu_priv1_ps3_ops = { |
593 | .int_mask_and = int_mask_and, |
594 | .int_mask_or = int_mask_or, |
595 | .int_mask_set = int_mask_set, |
596 | .int_mask_get = int_mask_get, |
597 | .int_stat_clear = int_stat_clear, |
598 | .int_stat_get = int_stat_get, |
599 | .cpu_affinity_set = cpu_affinity_set, |
600 | .mfc_dar_get = mfc_dar_get, |
601 | .mfc_dsisr_set = mfc_dsisr_set, |
602 | .mfc_dsisr_get = mfc_dsisr_get, |
603 | .mfc_sdr_setup = mfc_sdr_setup, |
604 | .mfc_sr1_set = mfc_sr1_set, |
605 | .mfc_sr1_get = mfc_sr1_get, |
606 | .mfc_tclass_id_set = mfc_tclass_id_set, |
607 | .mfc_tclass_id_get = mfc_tclass_id_get, |
608 | .tlb_invalidate = tlb_invalidate, |
609 | .resource_allocation_groupID_set = resource_allocation_groupID_set, |
610 | .resource_allocation_groupID_get = resource_allocation_groupID_get, |
611 | .resource_allocation_enable_set = resource_allocation_enable_set, |
612 | .resource_allocation_enable_get = resource_allocation_enable_get, |
613 | }; |
614 | |
615 | void ps3_spu_set_platform(void) |
616 | { |
617 | spu_priv1_ops = &spu_priv1_ps3_ops; |
618 | spu_management_ops = &spu_management_ps3_ops; |
619 | } |
620 | |