diff options
Diffstat (limited to 'arch/sh/kernel/cpu/sh4')
-rw-r--r-- | arch/sh/kernel/cpu/sh4/Makefile | 10 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4/ex.S | 176 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4/probe.c | 138 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4/setup-sh4-202.c | 43 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4/setup-sh73180.c | 43 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4/setup-sh7343.c | 43 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4/setup-sh7750.c | 48 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4/setup-sh7760.c | 53 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4/setup-sh7770.c | 53 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4/setup-sh7780.c | 79 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4/sq.c | 543 |
11 files changed, 912 insertions, 317 deletions
diff --git a/arch/sh/kernel/cpu/sh4/Makefile b/arch/sh/kernel/cpu/sh4/Makefile index 3d5cafc71ae3..8dbf3895ece7 100644 --- a/arch/sh/kernel/cpu/sh4/Makefile +++ b/arch/sh/kernel/cpu/sh4/Makefile | |||
@@ -7,6 +7,16 @@ obj-y := ex.o probe.o | |||
7 | obj-$(CONFIG_SH_FPU) += fpu.o | 7 | obj-$(CONFIG_SH_FPU) += fpu.o |
8 | obj-$(CONFIG_SH_STORE_QUEUES) += sq.o | 8 | obj-$(CONFIG_SH_STORE_QUEUES) += sq.o |
9 | 9 | ||
10 | # CPU subtype setup | ||
11 | obj-$(CONFIG_CPU_SUBTYPE_SH7750) += setup-sh7750.o | ||
12 | obj-$(CONFIG_CPU_SUBTYPE_SH7751) += setup-sh7750.o | ||
13 | obj-$(CONFIG_CPU_SUBTYPE_SH7760) += setup-sh7760.o | ||
14 | obj-$(CONFIG_CPU_SUBTYPE_SH7770) += setup-sh7770.o | ||
15 | obj-$(CONFIG_CPU_SUBTYPE_SH7780) += setup-sh7780.o | ||
16 | obj-$(CONFIG_CPU_SUBTYPE_SH73180) += setup-sh73180.o | ||
17 | obj-$(CONFIG_CPU_SUBTYPE_SH7343) += setup-sh7343.o | ||
18 | obj-$(CONFIG_CPU_SUBTYPE_SH4_202) += setup-sh4-202.o | ||
19 | |||
10 | # Primary on-chip clocks (common) | 20 | # Primary on-chip clocks (common) |
11 | clock-$(CONFIG_CPU_SH4) := clock-sh4.o | 21 | clock-$(CONFIG_CPU_SH4) := clock-sh4.o |
12 | clock-$(CONFIG_CPU_SUBTYPE_SH73180) := clock-sh73180.o | 22 | clock-$(CONFIG_CPU_SUBTYPE_SH73180) := clock-sh73180.o |
diff --git a/arch/sh/kernel/cpu/sh4/ex.S b/arch/sh/kernel/cpu/sh4/ex.S index 26a27df06505..7146893a6cca 100644 --- a/arch/sh/kernel/cpu/sh4/ex.S +++ b/arch/sh/kernel/cpu/sh4/ex.S | |||
@@ -72,6 +72,7 @@ ENTRY(interrupt_table) | |||
72 | .long do_IRQ ! 1110 | 72 | .long do_IRQ ! 1110 |
73 | .long exception_error | 73 | .long exception_error |
74 | ! Internal hardware | 74 | ! Internal hardware |
75 | #ifndef CONFIG_CPU_SUBTYPE_SH7780 | ||
75 | .long do_IRQ ! TMU0 tuni0 /* 400 */ | 76 | .long do_IRQ ! TMU0 tuni0 /* 400 */ |
76 | .long do_IRQ ! TMU1 tuni1 | 77 | .long do_IRQ ! TMU1 tuni1 |
77 | .long do_IRQ ! TMU2 tuni2 | 78 | .long do_IRQ ! TMU2 tuni2 |
@@ -122,6 +123,13 @@ ENTRY(interrupt_table) | |||
122 | .long do_IRQ ! 45 dmte5 | 123 | .long do_IRQ ! 45 dmte5 |
123 | .long do_IRQ ! 46 dmte6 | 124 | .long do_IRQ ! 46 dmte6 |
124 | .long do_IRQ ! 47 dmte7 /* 7E0 */ | 125 | .long do_IRQ ! 47 dmte7 /* 7E0 */ |
126 | #elif defined(CONFIG_CPU_SUBTYPE_SH7343) | ||
127 | .long do_IRQ ! 44 IIC1 ali /* 780 */ | ||
128 | .long do_IRQ ! 45 tacki | ||
129 | .long do_IRQ ! 46 waiti | ||
130 | .long do_IRQ ! 47 dtei /* 7E0 */ | ||
131 | .long do_IRQ ! 48 DMAC dei0 /* 800 */ | ||
132 | .long do_IRQ ! 49 dei1 /* 820 */ | ||
125 | #else | 133 | #else |
126 | .long exception_error ! 44 /* 780 */ | 134 | .long exception_error ! 44 /* 780 */ |
127 | .long exception_error ! 45 | 135 | .long exception_error ! 45 |
@@ -131,7 +139,8 @@ ENTRY(interrupt_table) | |||
131 | #if defined(CONFIG_SH_FPU) | 139 | #if defined(CONFIG_SH_FPU) |
132 | .long do_fpu_state_restore ! 48 /* 800 */ | 140 | .long do_fpu_state_restore ! 48 /* 800 */ |
133 | .long do_fpu_state_restore ! 49 /* 820 */ | 141 | .long do_fpu_state_restore ! 49 /* 820 */ |
134 | #else | 142 | #elif !defined(CONFIG_CPU_SUBTYPE_SH7343) && \ |
143 | !defined(CONFIG_CPU_SUBTYPE_SH73180) | ||
135 | .long exception_error | 144 | .long exception_error |
136 | .long exception_error | 145 | .long exception_error |
137 | #endif | 146 | #endif |
@@ -224,7 +233,7 @@ ENTRY(interrupt_table) | |||
224 | .long exception_error | 233 | .long exception_error |
225 | .long do_IRQ ! ADC adi | 234 | .long do_IRQ ! ADC adi |
226 | .long do_IRQ ! CMT cmti /* FA0 */ | 235 | .long do_IRQ ! CMT cmti /* FA0 */ |
227 | #elif defined(CONFIG_CPU_SUBTYPE_SH73180) | 236 | #elif defined(CONFIG_CPU_SUBTYPE_SH73180) || defined(CONFIG_CPU_SUBTYPE_SH7343) |
228 | .long do_IRQ ! 50 0x840 | 237 | .long do_IRQ ! 50 0x840 |
229 | .long do_IRQ ! 51 0x860 | 238 | .long do_IRQ ! 51 0x860 |
230 | .long do_IRQ ! 52 0x880 | 239 | .long do_IRQ ! 52 0x880 |
@@ -379,5 +388,168 @@ ENTRY(interrupt_table) | |||
379 | .long exception_error ! 141 0x13a0 | 388 | .long exception_error ! 141 0x13a0 |
380 | .long exception_error ! 142 0x13c0 | 389 | .long exception_error ! 142 0x13c0 |
381 | .long exception_error ! 143 0x13e0 | 390 | .long exception_error ! 143 0x13e0 |
391 | #elif defined(CONFIG_CPU_SUBTYPE_SH7770) | ||
392 | .long do_IRQ ! 50 0x840 | ||
393 | .long do_IRQ ! 51 0x860 | ||
394 | .long do_IRQ ! 52 0x880 | ||
395 | .long do_IRQ ! 53 0x8a0 | ||
396 | .long do_IRQ ! 54 0x8c0 | ||
397 | .long do_IRQ ! 55 0x8e0 | ||
398 | .long do_IRQ ! 56 0x900 | ||
399 | .long do_IRQ ! 57 0x920 | ||
400 | .long do_IRQ ! 58 0x940 | ||
401 | .long do_IRQ ! 59 0x960 | ||
402 | .long do_IRQ ! 60 0x980 | ||
403 | .long do_IRQ ! 61 0x9a0 | ||
404 | .long do_IRQ ! 62 0x9c0 | ||
405 | .long do_IRQ ! 63 0x9e0 | ||
406 | .long do_IRQ ! 64 0xa00 | ||
407 | .long do_IRQ ! 65 0xa20 | ||
408 | .long do_IRQ ! 66 0xa4d | ||
409 | .long do_IRQ ! 67 0xa60 | ||
410 | .long do_IRQ ! 68 0xa80 | ||
411 | .long do_IRQ ! 69 0xaa0 | ||
412 | .long do_IRQ ! 70 0xac0 | ||
413 | .long do_IRQ ! 71 0xae0 | ||
414 | .long do_IRQ ! 72 0xb00 | ||
415 | .long do_IRQ ! 73 0xb20 | ||
416 | .long do_IRQ ! 74 0xb40 | ||
417 | .long do_IRQ ! 75 0xb60 | ||
418 | .long do_IRQ ! 76 0xb80 | ||
419 | .long do_IRQ ! 77 0xba0 | ||
420 | .long do_IRQ ! 78 0xbc0 | ||
421 | .long do_IRQ ! 79 0xbe0 | ||
422 | .long do_IRQ ! 80 0xc00 | ||
423 | .long do_IRQ ! 81 0xc20 | ||
424 | .long do_IRQ ! 82 0xc40 | ||
425 | .long do_IRQ ! 83 0xc60 | ||
426 | .long do_IRQ ! 84 0xc80 | ||
427 | .long do_IRQ ! 85 0xca0 | ||
428 | .long do_IRQ ! 86 0xcc0 | ||
429 | .long do_IRQ ! 87 0xce0 | ||
430 | .long do_IRQ ! 88 0xd00 | ||
431 | .long do_IRQ ! 89 0xd20 | ||
432 | .long do_IRQ ! 90 0xd40 | ||
433 | .long do_IRQ ! 91 0xd60 | ||
434 | .long do_IRQ ! 92 0xd80 | ||
435 | .long do_IRQ ! 93 0xda0 | ||
436 | .long do_IRQ ! 94 0xdc0 | ||
437 | .long do_IRQ ! 95 0xde0 | ||
438 | .long do_IRQ ! 96 0xe00 | ||
439 | .long do_IRQ ! 97 0xe20 | ||
440 | .long do_IRQ ! 98 0xe40 | ||
441 | .long do_IRQ ! 99 0xe60 | ||
442 | .long do_IRQ ! 100 0xe80 | ||
443 | .long do_IRQ ! 101 0xea0 | ||
444 | .long do_IRQ ! 102 0xec0 | ||
445 | .long do_IRQ ! 103 0xee0 | ||
446 | .long do_IRQ ! 104 0xf00 | ||
447 | .long do_IRQ ! 105 0xf20 | ||
448 | .long do_IRQ ! 106 0xf40 | ||
449 | .long do_IRQ ! 107 0xf60 | ||
450 | .long do_IRQ ! 108 0xf80 | ||
451 | #endif | ||
452 | #else | ||
453 | .long exception_error /* 400 */ | ||
454 | .long exception_error | ||
455 | .long exception_error | ||
456 | .long exception_error | ||
457 | .long do_IRQ ! RTC ati | ||
458 | .long do_IRQ ! pri | ||
459 | .long do_IRQ ! cui | ||
460 | .long exception_error | ||
461 | .long exception_error /* 500 */ | ||
462 | .long exception_error | ||
463 | .long exception_error | ||
464 | .long do_IRQ ! WDT iti /* 560 */ | ||
465 | .long do_IRQ ! TMU-ch0 | ||
466 | .long do_IRQ ! TMU-ch1 | ||
467 | .long do_IRQ ! TMU-ch2 | ||
468 | .long do_IRQ ! ticpi2 /* 5E0 */ | ||
469 | .long do_IRQ ! 32 Hitachi UDI /* 600 */ | ||
470 | .long exception_error | ||
471 | .long do_IRQ ! 34 DMAC dmte0 | ||
472 | .long do_IRQ ! 35 dmte1 | ||
473 | .long do_IRQ ! 36 dmte2 | ||
474 | .long do_IRQ ! 37 dmte3 | ||
475 | .long do_IRQ ! 38 dmae | ||
476 | .long exception_error ! 39 /* 6E0 */ | ||
477 | .long do_IRQ ! 40 SCIF-ch0 eri /* 700 */ | ||
478 | .long do_IRQ ! 41 rxi | ||
479 | .long do_IRQ ! 42 bri | ||
480 | .long do_IRQ ! 43 txi | ||
481 | .long do_IRQ ! 44 DMAC dmte4 /* 780 */ | ||
482 | .long do_IRQ ! 45 dmte5 | ||
483 | .long do_IRQ ! 46 dmte6 | ||
484 | .long do_IRQ ! 47 dmte7 /* 7E0 */ | ||
485 | #if defined(CONFIG_SH_FPU) | ||
486 | .long do_fpu_state_restore ! 48 /* 800 */ | ||
487 | .long do_fpu_state_restore ! 49 /* 820 */ | ||
488 | #else | ||
489 | .long exception_error | ||
490 | .long exception_error | ||
491 | #endif | ||
492 | .long exception_error /* 840 */ | ||
493 | .long exception_error | ||
494 | .long exception_error | ||
495 | .long exception_error | ||
496 | .long exception_error | ||
497 | .long exception_error | ||
498 | .long do_IRQ ! 56 CMT /* 900 */ | ||
499 | .long exception_error | ||
500 | .long exception_error | ||
501 | .long exception_error | ||
502 | .long do_IRQ ! 60 HAC | ||
503 | .long exception_error | ||
504 | .long exception_error | ||
505 | .long exception_error | ||
506 | .long do_IRQ ! PCI serr /* A00 */ | ||
507 | .long do_IRQ ! INTA | ||
508 | .long do_IRQ ! INTB | ||
509 | .long do_IRQ ! INTC | ||
510 | .long do_IRQ ! INTD | ||
511 | .long do_IRQ ! err | ||
512 | .long do_IRQ ! pwd3 | ||
513 | .long do_IRQ ! pwd2 | ||
514 | .long do_IRQ ! pwd1 /* B00 */ | ||
515 | .long do_IRQ ! pwd0 | ||
516 | .long exception_error | ||
517 | .long exception_error | ||
518 | .long do_IRQ ! SCIF-ch1 eri /* B80 */ | ||
519 | .long do_IRQ ! rxi | ||
520 | .long do_IRQ ! bri | ||
521 | .long do_IRQ ! txi | ||
522 | .long do_IRQ ! SIOF /* C00 */ | ||
523 | .long exception_error | ||
524 | .long exception_error | ||
525 | .long exception_error | ||
526 | .long do_IRQ ! HSPI /* C80 */ | ||
527 | .long exception_error | ||
528 | .long exception_error | ||
529 | .long exception_error | ||
530 | .long do_IRQ ! MMCIF fatat /* D00 */ | ||
531 | .long do_IRQ ! tran | ||
532 | .long do_IRQ ! err | ||
533 | .long do_IRQ ! frdy | ||
534 | .long do_IRQ ! DMAC dmint8 /* D80 */ | ||
535 | .long do_IRQ ! dmint9 | ||
536 | .long do_IRQ ! dmint10 | ||
537 | .long do_IRQ ! dmint11 | ||
538 | .long do_IRQ ! TMU-ch3 /* E00 */ | ||
539 | .long do_IRQ ! TMU-ch4 | ||
540 | .long do_IRQ ! TMU-ch5 | ||
541 | .long exception_error | ||
542 | .long do_IRQ ! SSI | ||
543 | .long exception_error | ||
544 | .long exception_error | ||
545 | .long exception_error | ||
546 | .long do_IRQ ! FLCTL flste /* F00 */ | ||
547 | .long do_IRQ ! fltend | ||
548 | .long do_IRQ ! fltrq0 | ||
549 | .long do_IRQ ! fltrq1 | ||
550 | .long do_IRQ ! GPIO gpioi0 /* F80 */ | ||
551 | .long do_IRQ ! gpioi1 | ||
552 | .long do_IRQ ! gpioi2 | ||
553 | .long do_IRQ ! gpioi3 | ||
382 | #endif | 554 | #endif |
383 | 555 | ||
diff --git a/arch/sh/kernel/cpu/sh4/probe.c b/arch/sh/kernel/cpu/sh4/probe.c index 42427b79697b..c294de1e14a3 100644 --- a/arch/sh/kernel/cpu/sh4/probe.c +++ b/arch/sh/kernel/cpu/sh4/probe.c | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * CPU Subtype Probing for SH-4. | 4 | * CPU Subtype Probing for SH-4. |
5 | * | 5 | * |
6 | * Copyright (C) 2001, 2002, 2003, 2004 Paul Mundt | 6 | * Copyright (C) 2001 - 2006 Paul Mundt |
7 | * Copyright (C) 2003 Richard Curnow | 7 | * Copyright (C) 2003 Richard Curnow |
8 | * | 8 | * |
9 | * This file is subject to the terms and conditions of the GNU General Public | 9 | * This file is subject to the terms and conditions of the GNU General Public |
@@ -29,7 +29,7 @@ int __init detect_cpu_and_cache_system(void) | |||
29 | [9] = (1 << 16) | 29 | [9] = (1 << 16) |
30 | }; | 30 | }; |
31 | 31 | ||
32 | pvr = (ctrl_inl(CCN_PVR) >> 8) & 0xffff; | 32 | pvr = (ctrl_inl(CCN_PVR) >> 8) & 0xffffff; |
33 | prr = (ctrl_inl(CCN_PRR) >> 4) & 0xff; | 33 | prr = (ctrl_inl(CCN_PRR) >> 4) & 0xff; |
34 | cvr = (ctrl_inl(CCN_CVR)); | 34 | cvr = (ctrl_inl(CCN_CVR)); |
35 | 35 | ||
@@ -38,7 +38,6 @@ int __init detect_cpu_and_cache_system(void) | |||
38 | */ | 38 | */ |
39 | cpu_data->icache.way_incr = (1 << 13); | 39 | cpu_data->icache.way_incr = (1 << 13); |
40 | cpu_data->icache.entry_shift = 5; | 40 | cpu_data->icache.entry_shift = 5; |
41 | cpu_data->icache.entry_mask = 0x1fe0; | ||
42 | cpu_data->icache.sets = 256; | 41 | cpu_data->icache.sets = 256; |
43 | cpu_data->icache.ways = 1; | 42 | cpu_data->icache.ways = 1; |
44 | cpu_data->icache.linesz = L1_CACHE_BYTES; | 43 | cpu_data->icache.linesz = L1_CACHE_BYTES; |
@@ -48,13 +47,29 @@ int __init detect_cpu_and_cache_system(void) | |||
48 | */ | 47 | */ |
49 | cpu_data->dcache.way_incr = (1 << 14); | 48 | cpu_data->dcache.way_incr = (1 << 14); |
50 | cpu_data->dcache.entry_shift = 5; | 49 | cpu_data->dcache.entry_shift = 5; |
51 | cpu_data->dcache.entry_mask = 0x3fe0; | ||
52 | cpu_data->dcache.sets = 512; | 50 | cpu_data->dcache.sets = 512; |
53 | cpu_data->dcache.ways = 1; | 51 | cpu_data->dcache.ways = 1; |
54 | cpu_data->dcache.linesz = L1_CACHE_BYTES; | 52 | cpu_data->dcache.linesz = L1_CACHE_BYTES; |
55 | 53 | ||
56 | /* Set the FPU flag, virtually all SH-4's have one */ | 54 | /* |
57 | cpu_data->flags |= CPU_HAS_FPU; | 55 | * Setup some generic flags we can probe |
56 | * (L2 and DSP detection only work on SH-4A) | ||
57 | */ | ||
58 | if (((pvr >> 16) & 0xff) == 0x10) { | ||
59 | if ((cvr & 0x02000000) == 0) | ||
60 | cpu_data->flags |= CPU_HAS_L2_CACHE; | ||
61 | if ((cvr & 0x10000000) == 0) | ||
62 | cpu_data->flags |= CPU_HAS_DSP; | ||
63 | |||
64 | cpu_data->flags |= CPU_HAS_LLSC; | ||
65 | } | ||
66 | |||
67 | /* FPU detection works for everyone */ | ||
68 | if ((cvr & 0x20000000) == 1) | ||
69 | cpu_data->flags |= CPU_HAS_FPU; | ||
70 | |||
71 | /* Mask off the upper chip ID */ | ||
72 | pvr &= 0xffff; | ||
58 | 73 | ||
59 | /* | 74 | /* |
60 | * Probe the underlying processor version/revision and | 75 | * Probe the underlying processor version/revision and |
@@ -63,56 +78,101 @@ int __init detect_cpu_and_cache_system(void) | |||
63 | switch (pvr) { | 78 | switch (pvr) { |
64 | case 0x205: | 79 | case 0x205: |
65 | cpu_data->type = CPU_SH7750; | 80 | cpu_data->type = CPU_SH7750; |
66 | cpu_data->flags |= CPU_HAS_P2_FLUSH_BUG | CPU_HAS_PERF_COUNTER; | 81 | cpu_data->flags |= CPU_HAS_P2_FLUSH_BUG | CPU_HAS_FPU | |
82 | CPU_HAS_PERF_COUNTER | CPU_HAS_PTEA; | ||
67 | break; | 83 | break; |
68 | case 0x206: | 84 | case 0x206: |
69 | cpu_data->type = CPU_SH7750S; | 85 | cpu_data->type = CPU_SH7750S; |
70 | cpu_data->flags |= CPU_HAS_P2_FLUSH_BUG | CPU_HAS_PERF_COUNTER; | 86 | cpu_data->flags |= CPU_HAS_P2_FLUSH_BUG | CPU_HAS_FPU | |
87 | CPU_HAS_PERF_COUNTER | CPU_HAS_PTEA; | ||
71 | break; | 88 | break; |
72 | case 0x1100: | 89 | case 0x1100: |
73 | cpu_data->type = CPU_SH7751; | 90 | cpu_data->type = CPU_SH7751; |
91 | cpu_data->flags |= CPU_HAS_FPU | CPU_HAS_PTEA; | ||
74 | break; | 92 | break; |
75 | case 0x2000: | 93 | case 0x2000: |
76 | cpu_data->type = CPU_SH73180; | 94 | cpu_data->type = CPU_SH73180; |
77 | cpu_data->icache.ways = 4; | 95 | cpu_data->icache.ways = 4; |
78 | cpu_data->dcache.ways = 4; | 96 | cpu_data->dcache.ways = 4; |
79 | cpu_data->flags &= ~CPU_HAS_FPU; | 97 | cpu_data->flags |= CPU_HAS_LLSC; |
98 | break; | ||
99 | case 0x2001: | ||
100 | case 0x2004: | ||
101 | cpu_data->type = CPU_SH7770; | ||
102 | cpu_data->icache.ways = 4; | ||
103 | cpu_data->dcache.ways = 4; | ||
104 | |||
105 | cpu_data->flags |= CPU_HAS_FPU | CPU_HAS_LLSC; | ||
106 | break; | ||
107 | case 0x2006: | ||
108 | case 0x200A: | ||
109 | if (prr == 0x61) | ||
110 | cpu_data->type = CPU_SH7781; | ||
111 | else | ||
112 | cpu_data->type = CPU_SH7780; | ||
113 | |||
114 | cpu_data->icache.ways = 4; | ||
115 | cpu_data->dcache.ways = 4; | ||
116 | |||
117 | cpu_data->flags |= CPU_HAS_FPU | CPU_HAS_PERF_COUNTER | | ||
118 | CPU_HAS_LLSC; | ||
119 | break; | ||
120 | case 0x3000: | ||
121 | case 0x3003: | ||
122 | cpu_data->type = CPU_SH7343; | ||
123 | cpu_data->icache.ways = 4; | ||
124 | cpu_data->dcache.ways = 4; | ||
125 | cpu_data->flags |= CPU_HAS_LLSC; | ||
80 | break; | 126 | break; |
81 | case 0x8000: | 127 | case 0x8000: |
82 | cpu_data->type = CPU_ST40RA; | 128 | cpu_data->type = CPU_ST40RA; |
129 | cpu_data->flags |= CPU_HAS_FPU | CPU_HAS_PTEA; | ||
83 | break; | 130 | break; |
84 | case 0x8100: | 131 | case 0x8100: |
85 | cpu_data->type = CPU_ST40GX1; | 132 | cpu_data->type = CPU_ST40GX1; |
133 | cpu_data->flags |= CPU_HAS_FPU | CPU_HAS_PTEA; | ||
86 | break; | 134 | break; |
87 | case 0x700: | 135 | case 0x700: |
88 | cpu_data->type = CPU_SH4_501; | 136 | cpu_data->type = CPU_SH4_501; |
89 | cpu_data->icache.ways = 2; | 137 | cpu_data->icache.ways = 2; |
90 | cpu_data->dcache.ways = 2; | 138 | cpu_data->dcache.ways = 2; |
91 | 139 | cpu_data->flags |= CPU_HAS_PTEA; | |
92 | /* No FPU on the SH4-500 series.. */ | ||
93 | cpu_data->flags &= ~CPU_HAS_FPU; | ||
94 | break; | 140 | break; |
95 | case 0x600: | 141 | case 0x600: |
96 | cpu_data->type = CPU_SH4_202; | 142 | cpu_data->type = CPU_SH4_202; |
97 | cpu_data->icache.ways = 2; | 143 | cpu_data->icache.ways = 2; |
98 | cpu_data->dcache.ways = 2; | 144 | cpu_data->dcache.ways = 2; |
145 | cpu_data->flags |= CPU_HAS_FPU | CPU_HAS_PTEA; | ||
99 | break; | 146 | break; |
100 | case 0x500 ... 0x501: | 147 | case 0x500 ... 0x501: |
101 | switch (prr) { | 148 | switch (prr) { |
102 | case 0x10: cpu_data->type = CPU_SH7750R; break; | 149 | case 0x10: |
103 | case 0x11: cpu_data->type = CPU_SH7751R; break; | 150 | cpu_data->type = CPU_SH7750R; |
104 | case 0x50: cpu_data->type = CPU_SH7760; break; | 151 | break; |
152 | case 0x11: | ||
153 | cpu_data->type = CPU_SH7751R; | ||
154 | break; | ||
155 | case 0x50 ... 0x5f: | ||
156 | cpu_data->type = CPU_SH7760; | ||
157 | break; | ||
105 | } | 158 | } |
106 | 159 | ||
107 | cpu_data->icache.ways = 2; | 160 | cpu_data->icache.ways = 2; |
108 | cpu_data->dcache.ways = 2; | 161 | cpu_data->dcache.ways = 2; |
109 | 162 | ||
163 | cpu_data->flags |= CPU_HAS_FPU | CPU_HAS_PTEA; | ||
164 | |||
110 | break; | 165 | break; |
111 | default: | 166 | default: |
112 | cpu_data->type = CPU_SH_NONE; | 167 | cpu_data->type = CPU_SH_NONE; |
113 | break; | 168 | break; |
114 | } | 169 | } |
115 | 170 | ||
171 | #ifdef CONFIG_SH_DIRECT_MAPPED | ||
172 | cpu_data->icache.ways = 1; | ||
173 | cpu_data->dcache.ways = 1; | ||
174 | #endif | ||
175 | |||
116 | /* | 176 | /* |
117 | * On anything that's not a direct-mapped cache, look to the CVR | 177 | * On anything that's not a direct-mapped cache, look to the CVR |
118 | * for I/D-cache specifics. | 178 | * for I/D-cache specifics. |
@@ -121,18 +181,56 @@ int __init detect_cpu_and_cache_system(void) | |||
121 | size = sizes[(cvr >> 20) & 0xf]; | 181 | size = sizes[(cvr >> 20) & 0xf]; |
122 | cpu_data->icache.way_incr = (size >> 1); | 182 | cpu_data->icache.way_incr = (size >> 1); |
123 | cpu_data->icache.sets = (size >> 6); | 183 | cpu_data->icache.sets = (size >> 6); |
124 | cpu_data->icache.entry_mask = | 184 | |
125 | (cpu_data->icache.way_incr - (1 << 5)); | ||
126 | } | 185 | } |
127 | 186 | ||
187 | /* Setup the rest of the I-cache info */ | ||
188 | cpu_data->icache.entry_mask = cpu_data->icache.way_incr - | ||
189 | cpu_data->icache.linesz; | ||
190 | |||
191 | cpu_data->icache.way_size = cpu_data->icache.sets * | ||
192 | cpu_data->icache.linesz; | ||
193 | |||
194 | /* And the rest of the D-cache */ | ||
128 | if (cpu_data->dcache.ways > 1) { | 195 | if (cpu_data->dcache.ways > 1) { |
129 | size = sizes[(cvr >> 16) & 0xf]; | 196 | size = sizes[(cvr >> 16) & 0xf]; |
130 | cpu_data->dcache.way_incr = (size >> 1); | 197 | cpu_data->dcache.way_incr = (size >> 1); |
131 | cpu_data->dcache.sets = (size >> 6); | 198 | cpu_data->dcache.sets = (size >> 6); |
132 | cpu_data->dcache.entry_mask = | 199 | } |
133 | (cpu_data->dcache.way_incr - (1 << 5)); | 200 | |
201 | cpu_data->dcache.entry_mask = cpu_data->dcache.way_incr - | ||
202 | cpu_data->dcache.linesz; | ||
203 | |||
204 | cpu_data->dcache.way_size = cpu_data->dcache.sets * | ||
205 | cpu_data->dcache.linesz; | ||
206 | |||
207 | /* | ||
208 | * Setup the L2 cache desc | ||
209 | * | ||
210 | * SH-4A's have an optional PIPT L2. | ||
211 | */ | ||
212 | if (cpu_data->flags & CPU_HAS_L2_CACHE) { | ||
213 | /* | ||
214 | * Size calculation is much more sensible | ||
215 | * than it is for the L1. | ||
216 | * | ||
217 | * Sizes are 128KB, 258KB, 512KB, and 1MB. | ||
218 | */ | ||
219 | size = (cvr & 0xf) << 17; | ||
220 | |||
221 | BUG_ON(!size); | ||
222 | |||
223 | cpu_data->scache.way_incr = (1 << 16); | ||
224 | cpu_data->scache.entry_shift = 5; | ||
225 | cpu_data->scache.ways = 4; | ||
226 | cpu_data->scache.linesz = L1_CACHE_BYTES; | ||
227 | cpu_data->scache.entry_mask = | ||
228 | (cpu_data->scache.way_incr - cpu_data->scache.linesz); | ||
229 | cpu_data->scache.sets = size / | ||
230 | (cpu_data->scache.linesz * cpu_data->scache.ways); | ||
231 | cpu_data->scache.way_size = | ||
232 | (cpu_data->scache.sets * cpu_data->scache.linesz); | ||
134 | } | 233 | } |
135 | 234 | ||
136 | return 0; | 235 | return 0; |
137 | } | 236 | } |
138 | |||
diff --git a/arch/sh/kernel/cpu/sh4/setup-sh4-202.c b/arch/sh/kernel/cpu/sh4/setup-sh4-202.c new file mode 100644 index 000000000000..6e4e96541358 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/setup-sh4-202.c | |||
@@ -0,0 +1,43 @@ | |||
1 | /* | ||
2 | * SH4-202 Setup | ||
3 | * | ||
4 | * Copyright (C) 2006 Paul Mundt | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License. See the file "COPYING" in the main directory of this archive | ||
8 | * for more details. | ||
9 | */ | ||
10 | #include <linux/platform_device.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/serial.h> | ||
13 | #include <asm/sci.h> | ||
14 | |||
15 | static struct plat_sci_port sci_platform_data[] = { | ||
16 | { | ||
17 | .mapbase = 0xffe80000, | ||
18 | .flags = UPF_BOOT_AUTOCONF, | ||
19 | .type = PORT_SCIF, | ||
20 | .irqs = { 40, 41, 43, 42 }, | ||
21 | }, { | ||
22 | .flags = 0, | ||
23 | } | ||
24 | }; | ||
25 | |||
26 | static struct platform_device sci_device = { | ||
27 | .name = "sh-sci", | ||
28 | .id = -1, | ||
29 | .dev = { | ||
30 | .platform_data = sci_platform_data, | ||
31 | }, | ||
32 | }; | ||
33 | |||
34 | static struct platform_device *sh4202_devices[] __initdata = { | ||
35 | &sci_device, | ||
36 | }; | ||
37 | |||
38 | static int __init sh4202_devices_setup(void) | ||
39 | { | ||
40 | return platform_add_devices(sh4202_devices, | ||
41 | ARRAY_SIZE(sh4202_devices)); | ||
42 | } | ||
43 | __initcall(sh4202_devices_setup); | ||
diff --git a/arch/sh/kernel/cpu/sh4/setup-sh73180.c b/arch/sh/kernel/cpu/sh4/setup-sh73180.c new file mode 100644 index 000000000000..cc9ea1e2e5df --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/setup-sh73180.c | |||
@@ -0,0 +1,43 @@ | |||
1 | /* | ||
2 | * SH73180 Setup | ||
3 | * | ||
4 | * Copyright (C) 2006 Paul Mundt | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License. See the file "COPYING" in the main directory of this archive | ||
8 | * for more details. | ||
9 | */ | ||
10 | #include <linux/platform_device.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/serial.h> | ||
13 | #include <asm/sci.h> | ||
14 | |||
15 | static struct plat_sci_port sci_platform_data[] = { | ||
16 | { | ||
17 | .mapbase = 0xffe80000, | ||
18 | .flags = UPF_BOOT_AUTOCONF, | ||
19 | .type = PORT_SCIF, | ||
20 | .irqs = { 80, 81, 83, 82 }, | ||
21 | }, { | ||
22 | .flags = 0, | ||
23 | } | ||
24 | }; | ||
25 | |||
26 | static struct platform_device sci_device = { | ||
27 | .name = "sh-sci", | ||
28 | .id = -1, | ||
29 | .dev = { | ||
30 | .platform_data = sci_platform_data, | ||
31 | }, | ||
32 | }; | ||
33 | |||
34 | static struct platform_device *sh73180_devices[] __initdata = { | ||
35 | &sci_device, | ||
36 | }; | ||
37 | |||
38 | static int __init sh73180_devices_setup(void) | ||
39 | { | ||
40 | return platform_add_devices(sh73180_devices, | ||
41 | ARRAY_SIZE(sh73180_devices)); | ||
42 | } | ||
43 | __initcall(sh73180_devices_setup); | ||
diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7343.c b/arch/sh/kernel/cpu/sh4/setup-sh7343.c new file mode 100644 index 000000000000..91d61cf91ba1 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/setup-sh7343.c | |||
@@ -0,0 +1,43 @@ | |||
1 | /* | ||
2 | * SH7343 Setup | ||
3 | * | ||
4 | * Copyright (C) 2006 Paul Mundt | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License. See the file "COPYING" in the main directory of this archive | ||
8 | * for more details. | ||
9 | */ | ||
10 | #include <linux/platform_device.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/serial.h> | ||
13 | #include <asm/sci.h> | ||
14 | |||
15 | static struct plat_sci_port sci_platform_data[] = { | ||
16 | { | ||
17 | .mapbase = 0xffe00000, | ||
18 | .flags = UPF_BOOT_AUTOCONF, | ||
19 | .type = PORT_SCIF, | ||
20 | .irqs = { 80, 81, 83, 82 }, | ||
21 | }, { | ||
22 | .flags = 0, | ||
23 | } | ||
24 | }; | ||
25 | |||
26 | static struct platform_device sci_device = { | ||
27 | .name = "sh-sci", | ||
28 | .id = -1, | ||
29 | .dev = { | ||
30 | .platform_data = sci_platform_data, | ||
31 | }, | ||
32 | }; | ||
33 | |||
34 | static struct platform_device *sh7343_devices[] __initdata = { | ||
35 | &sci_device, | ||
36 | }; | ||
37 | |||
38 | static int __init sh7343_devices_setup(void) | ||
39 | { | ||
40 | return platform_add_devices(sh7343_devices, | ||
41 | ARRAY_SIZE(sh7343_devices)); | ||
42 | } | ||
43 | __initcall(sh7343_devices_setup); | ||
diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7750.c b/arch/sh/kernel/cpu/sh4/setup-sh7750.c new file mode 100644 index 000000000000..50812d57c1c1 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/setup-sh7750.c | |||
@@ -0,0 +1,48 @@ | |||
1 | /* | ||
2 | * SH7750/SH7751 Setup | ||
3 | * | ||
4 | * Copyright (C) 2006 Paul Mundt | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License. See the file "COPYING" in the main directory of this archive | ||
8 | * for more details. | ||
9 | */ | ||
10 | #include <linux/platform_device.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/serial.h> | ||
13 | #include <asm/sci.h> | ||
14 | |||
15 | static struct plat_sci_port sci_platform_data[] = { | ||
16 | { | ||
17 | .mapbase = 0xffe00000, | ||
18 | .flags = UPF_BOOT_AUTOCONF, | ||
19 | .type = PORT_SCI, | ||
20 | .irqs = { 23, 24, 25, 0 }, | ||
21 | }, { | ||
22 | .mapbase = 0xffe80000, | ||
23 | .flags = UPF_BOOT_AUTOCONF, | ||
24 | .type = PORT_SCIF, | ||
25 | .irqs = { 40, 41, 43, 42 }, | ||
26 | }, { | ||
27 | .flags = 0, | ||
28 | } | ||
29 | }; | ||
30 | |||
31 | static struct platform_device sci_device = { | ||
32 | .name = "sh-sci", | ||
33 | .id = -1, | ||
34 | .dev = { | ||
35 | .platform_data = sci_platform_data, | ||
36 | }, | ||
37 | }; | ||
38 | |||
39 | static struct platform_device *sh7750_devices[] __initdata = { | ||
40 | &sci_device, | ||
41 | }; | ||
42 | |||
43 | static int __init sh7750_devices_setup(void) | ||
44 | { | ||
45 | return platform_add_devices(sh7750_devices, | ||
46 | ARRAY_SIZE(sh7750_devices)); | ||
47 | } | ||
48 | __initcall(sh7750_devices_setup); | ||
diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7760.c b/arch/sh/kernel/cpu/sh4/setup-sh7760.c new file mode 100644 index 000000000000..97f1c9af35d6 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/setup-sh7760.c | |||
@@ -0,0 +1,53 @@ | |||
1 | /* | ||
2 | * SH7760 Setup | ||
3 | * | ||
4 | * Copyright (C) 2006 Paul Mundt | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License. See the file "COPYING" in the main directory of this archive | ||
8 | * for more details. | ||
9 | */ | ||
10 | #include <linux/platform_device.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/serial.h> | ||
13 | #include <asm/sci.h> | ||
14 | |||
15 | static struct plat_sci_port sci_platform_data[] = { | ||
16 | { | ||
17 | .mapbase = 0xfe600000, | ||
18 | .flags = UPF_BOOT_AUTOCONF, | ||
19 | .type = PORT_SCIF, | ||
20 | .irqs = { 52, 53, 55, 54 }, | ||
21 | }, { | ||
22 | .mapbase = 0xfe610000, | ||
23 | .flags = UPF_BOOT_AUTOCONF, | ||
24 | .type = PORT_SCIF, | ||
25 | .irqs = { 72, 73, 75, 74 }, | ||
26 | }, { | ||
27 | .mapbase = 0xfe620000, | ||
28 | .flags = UPF_BOOT_AUTOCONF, | ||
29 | .type = PORT_SCIF, | ||
30 | .irqs = { 76, 77, 79, 78 }, | ||
31 | }, { | ||
32 | .flags = 0, | ||
33 | } | ||
34 | }; | ||
35 | |||
36 | static struct platform_device sci_device = { | ||
37 | .name = "sh-sci", | ||
38 | .id = -1, | ||
39 | .dev = { | ||
40 | .platform_data = sci_platform_data, | ||
41 | }, | ||
42 | }; | ||
43 | |||
44 | static struct platform_device *sh7760_devices[] __initdata = { | ||
45 | &sci_device, | ||
46 | }; | ||
47 | |||
48 | static int __init sh7760_devices_setup(void) | ||
49 | { | ||
50 | return platform_add_devices(sh7760_devices, | ||
51 | ARRAY_SIZE(sh7760_devices)); | ||
52 | } | ||
53 | __initcall(sh7760_devices_setup); | ||
diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7770.c b/arch/sh/kernel/cpu/sh4/setup-sh7770.c new file mode 100644 index 000000000000..6a04cc5f5aca --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/setup-sh7770.c | |||
@@ -0,0 +1,53 @@ | |||
1 | /* | ||
2 | * SH7770 Setup | ||
3 | * | ||
4 | * Copyright (C) 2006 Paul Mundt | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License. See the file "COPYING" in the main directory of this archive | ||
8 | * for more details. | ||
9 | */ | ||
10 | #include <linux/platform_device.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/serial.h> | ||
13 | #include <asm/sci.h> | ||
14 | |||
15 | static struct plat_sci_port sci_platform_data[] = { | ||
16 | { | ||
17 | .mapbase = 0xff923000, | ||
18 | .flags = UPF_BOOT_AUTOCONF, | ||
19 | .type = PORT_SCIF, | ||
20 | .irqs = { 61, 61, 61, 61 }, | ||
21 | }, { | ||
22 | .mapbase = 0xff924000, | ||
23 | .flags = UPF_BOOT_AUTOCONF, | ||
24 | .type = PORT_SCIF, | ||
25 | .irqs = { 62, 62, 62, 62 }, | ||
26 | }, { | ||
27 | .mapbase = 0xff925000, | ||
28 | .flags = UPF_BOOT_AUTOCONF, | ||
29 | .type = PORT_SCIF, | ||
30 | .irqs = { 63, 63, 63, 63 }, | ||
31 | }, { | ||
32 | .flags = 0, | ||
33 | } | ||
34 | }; | ||
35 | |||
36 | static struct platform_device sci_device = { | ||
37 | .name = "sh-sci", | ||
38 | .id = -1, | ||
39 | .dev = { | ||
40 | .platform_data = sci_platform_data, | ||
41 | }, | ||
42 | }; | ||
43 | |||
44 | static struct platform_device *sh7770_devices[] __initdata = { | ||
45 | &sci_device, | ||
46 | }; | ||
47 | |||
48 | static int __init sh7770_devices_setup(void) | ||
49 | { | ||
50 | return platform_add_devices(sh7770_devices, | ||
51 | ARRAY_SIZE(sh7770_devices)); | ||
52 | } | ||
53 | __initcall(sh7770_devices_setup); | ||
diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7780.c b/arch/sh/kernel/cpu/sh4/setup-sh7780.c new file mode 100644 index 000000000000..72493f259edc --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/setup-sh7780.c | |||
@@ -0,0 +1,79 @@ | |||
1 | /* | ||
2 | * SH7780 Setup | ||
3 | * | ||
4 | * Copyright (C) 2006 Paul Mundt | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License. See the file "COPYING" in the main directory of this archive | ||
8 | * for more details. | ||
9 | */ | ||
10 | #include <linux/platform_device.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/serial.h> | ||
13 | #include <asm/sci.h> | ||
14 | |||
15 | static struct resource rtc_resources[] = { | ||
16 | [0] = { | ||
17 | .start = 0xffe80000, | ||
18 | .end = 0xffe80000 + 0x58 - 1, | ||
19 | .flags = IORESOURCE_IO, | ||
20 | }, | ||
21 | [1] = { | ||
22 | /* Period IRQ */ | ||
23 | .start = 21, | ||
24 | .flags = IORESOURCE_IRQ, | ||
25 | }, | ||
26 | [2] = { | ||
27 | /* Carry IRQ */ | ||
28 | .start = 22, | ||
29 | .flags = IORESOURCE_IRQ, | ||
30 | }, | ||
31 | [3] = { | ||
32 | /* Alarm IRQ */ | ||
33 | .start = 23, | ||
34 | .flags = IORESOURCE_IRQ, | ||
35 | }, | ||
36 | }; | ||
37 | |||
38 | static struct platform_device rtc_device = { | ||
39 | .name = "sh-rtc", | ||
40 | .id = -1, | ||
41 | .num_resources = ARRAY_SIZE(rtc_resources), | ||
42 | .resource = rtc_resources, | ||
43 | }; | ||
44 | |||
45 | static struct plat_sci_port sci_platform_data[] = { | ||
46 | { | ||
47 | .mapbase = 0xffe00000, | ||
48 | .flags = UPF_BOOT_AUTOCONF, | ||
49 | .type = PORT_SCIF, | ||
50 | .irqs = { 40, 41, 43, 42 }, | ||
51 | }, { | ||
52 | .mapbase = 0xffe10000, | ||
53 | .flags = UPF_BOOT_AUTOCONF, | ||
54 | .type = PORT_SCIF, | ||
55 | .irqs = { 76, 77, 79, 78 }, | ||
56 | }, { | ||
57 | .flags = 0, | ||
58 | } | ||
59 | }; | ||
60 | |||
61 | static struct platform_device sci_device = { | ||
62 | .name = "sh-sci", | ||
63 | .id = -1, | ||
64 | .dev = { | ||
65 | .platform_data = sci_platform_data, | ||
66 | }, | ||
67 | }; | ||
68 | |||
69 | static struct platform_device *sh7780_devices[] __initdata = { | ||
70 | &rtc_device, | ||
71 | &sci_device, | ||
72 | }; | ||
73 | |||
74 | static int __init sh7780_devices_setup(void) | ||
75 | { | ||
76 | return platform_add_devices(sh7780_devices, | ||
77 | ARRAY_SIZE(sh7780_devices)); | ||
78 | } | ||
79 | __initcall(sh7780_devices_setup); | ||
diff --git a/arch/sh/kernel/cpu/sh4/sq.c b/arch/sh/kernel/cpu/sh4/sq.c index b09805f3ee23..7bcc73f9b8df 100644 --- a/arch/sh/kernel/cpu/sh4/sq.c +++ b/arch/sh/kernel/cpu/sh4/sq.c | |||
@@ -1,49 +1,52 @@ | |||
1 | /* | 1 | /* |
2 | * arch/sh/kernel/cpu/sq.c | 2 | * arch/sh/kernel/cpu/sh4/sq.c |
3 | * | 3 | * |
4 | * General management API for SH-4 integrated Store Queues | 4 | * General management API for SH-4 integrated Store Queues |
5 | * | 5 | * |
6 | * Copyright (C) 2001, 2002, 2003, 2004 Paul Mundt | 6 | * Copyright (C) 2001 - 2006 Paul Mundt |
7 | * Copyright (C) 2001, 2002 M. R. Brown | 7 | * Copyright (C) 2001, 2002 M. R. Brown |
8 | * | 8 | * |
9 | * Some of this code has been adopted directly from the old arch/sh/mm/sq.c | ||
10 | * hack that was part of the LinuxDC project. For all intents and purposes, | ||
11 | * this is a completely new interface that really doesn't have much in common | ||
12 | * with the old zone-based approach at all. In fact, it's only listed here for | ||
13 | * general completeness. | ||
14 | * | ||
15 | * This file is subject to the terms and conditions of the GNU General Public | 9 | * This file is subject to the terms and conditions of the GNU General Public |
16 | * License. See the file "COPYING" in the main directory of this archive | 10 | * License. See the file "COPYING" in the main directory of this archive |
17 | * for more details. | 11 | * for more details. |
18 | */ | 12 | */ |
19 | #include <linux/init.h> | 13 | #include <linux/init.h> |
14 | #include <linux/cpu.h> | ||
15 | #include <linux/bitmap.h> | ||
16 | #include <linux/sysdev.h> | ||
20 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
21 | #include <linux/module.h> | 18 | #include <linux/module.h> |
22 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
23 | #include <linux/list.h> | ||
24 | #include <linux/proc_fs.h> | ||
25 | #include <linux/miscdevice.h> | ||
26 | #include <linux/vmalloc.h> | 20 | #include <linux/vmalloc.h> |
27 | 21 | #include <linux/mm.h> | |
28 | #include <asm/io.h> | 22 | #include <asm/io.h> |
29 | #include <asm/page.h> | 23 | #include <asm/page.h> |
30 | #include <asm/mmu_context.h> | 24 | #include <asm/cacheflush.h> |
31 | #include <asm/cpu/sq.h> | 25 | #include <asm/cpu/sq.h> |
32 | 26 | ||
33 | static LIST_HEAD(sq_mapping_list); | 27 | struct sq_mapping; |
28 | |||
29 | struct sq_mapping { | ||
30 | const char *name; | ||
31 | |||
32 | unsigned long sq_addr; | ||
33 | unsigned long addr; | ||
34 | unsigned int size; | ||
35 | |||
36 | struct sq_mapping *next; | ||
37 | }; | ||
38 | |||
39 | static struct sq_mapping *sq_mapping_list; | ||
34 | static DEFINE_SPINLOCK(sq_mapping_lock); | 40 | static DEFINE_SPINLOCK(sq_mapping_lock); |
41 | static kmem_cache_t *sq_cache; | ||
42 | static unsigned long *sq_bitmap; | ||
35 | 43 | ||
36 | /** | 44 | #define store_queue_barrier() \ |
37 | * sq_flush - Flush (prefetch) the store queue cache | 45 | do { \ |
38 | * @addr: the store queue address to flush | 46 | (void)ctrl_inl(P4SEG_STORE_QUE); \ |
39 | * | 47 | ctrl_outl(0, P4SEG_STORE_QUE + 0); \ |
40 | * Executes a prefetch instruction on the specified store queue cache, | 48 | ctrl_outl(0, P4SEG_STORE_QUE + 8); \ |
41 | * so that the cached data is written to physical memory. | 49 | } while (0); |
42 | */ | ||
43 | inline void sq_flush(void *addr) | ||
44 | { | ||
45 | __asm__ __volatile__ ("pref @%0" : : "r" (addr) : "memory"); | ||
46 | } | ||
47 | 50 | ||
48 | /** | 51 | /** |
49 | * sq_flush_range - Flush (prefetch) a specific SQ range | 52 | * sq_flush_range - Flush (prefetch) a specific SQ range |
@@ -56,152 +59,73 @@ inline void sq_flush(void *addr) | |||
56 | void sq_flush_range(unsigned long start, unsigned int len) | 59 | void sq_flush_range(unsigned long start, unsigned int len) |
57 | { | 60 | { |
58 | volatile unsigned long *sq = (unsigned long *)start; | 61 | volatile unsigned long *sq = (unsigned long *)start; |
59 | unsigned long dummy; | ||
60 | 62 | ||
61 | /* Flush the queues */ | 63 | /* Flush the queues */ |
62 | for (len >>= 5; len--; sq += 8) | 64 | for (len >>= 5; len--; sq += 8) |
63 | sq_flush((void *)sq); | 65 | prefetchw((void *)sq); |
64 | 66 | ||
65 | /* Wait for completion */ | 67 | /* Wait for completion */ |
66 | dummy = ctrl_inl(P4SEG_STORE_QUE); | 68 | store_queue_barrier(); |
67 | |||
68 | ctrl_outl(0, P4SEG_STORE_QUE + 0); | ||
69 | ctrl_outl(0, P4SEG_STORE_QUE + 8); | ||
70 | } | 69 | } |
71 | 70 | ||
72 | static struct sq_mapping *__sq_alloc_mapping(unsigned long virt, unsigned long phys, unsigned long size, const char *name) | 71 | static inline void sq_mapping_list_add(struct sq_mapping *map) |
73 | { | 72 | { |
74 | struct sq_mapping *map; | 73 | struct sq_mapping **p, *tmp; |
75 | |||
76 | if (virt + size > SQ_ADDRMAX) | ||
77 | return ERR_PTR(-ENOSPC); | ||
78 | |||
79 | map = kmalloc(sizeof(struct sq_mapping), GFP_KERNEL); | ||
80 | if (!map) | ||
81 | return ERR_PTR(-ENOMEM); | ||
82 | 74 | ||
83 | INIT_LIST_HEAD(&map->list); | 75 | spin_lock_irq(&sq_mapping_lock); |
84 | 76 | ||
85 | map->sq_addr = virt; | 77 | p = &sq_mapping_list; |
86 | map->addr = phys; | 78 | while ((tmp = *p) != NULL) |
87 | map->size = size + 1; | 79 | p = &tmp->next; |
88 | map->name = name; | ||
89 | 80 | ||
90 | list_add(&map->list, &sq_mapping_list); | 81 | map->next = tmp; |
82 | *p = map; | ||
91 | 83 | ||
92 | return map; | 84 | spin_unlock_irq(&sq_mapping_lock); |
93 | } | 85 | } |
94 | 86 | ||
95 | static unsigned long __sq_get_next_addr(void) | 87 | static inline void sq_mapping_list_del(struct sq_mapping *map) |
96 | { | 88 | { |
97 | if (!list_empty(&sq_mapping_list)) { | 89 | struct sq_mapping **p, *tmp; |
98 | struct list_head *pos, *tmp; | 90 | |
99 | 91 | spin_lock_irq(&sq_mapping_lock); | |
100 | /* | 92 | |
101 | * Read one off the list head, as it will have the highest | 93 | for (p = &sq_mapping_list; (tmp = *p); p = &tmp->next) |
102 | * mapped allocation. Set the next one up right above it. | 94 | if (tmp == map) { |
103 | * | 95 | *p = tmp->next; |
104 | * This is somewhat sub-optimal, as we don't look at | 96 | break; |
105 | * gaps between allocations or anything lower then the | ||
106 | * highest-level allocation. | ||
107 | * | ||
108 | * However, in the interest of performance and the general | ||
109 | * lack of desire to do constant list rebalancing, we don't | ||
110 | * worry about it. | ||
111 | */ | ||
112 | list_for_each_safe(pos, tmp, &sq_mapping_list) { | ||
113 | struct sq_mapping *entry; | ||
114 | |||
115 | entry = list_entry(pos, typeof(*entry), list); | ||
116 | |||
117 | return entry->sq_addr + entry->size; | ||
118 | } | 97 | } |
119 | } | ||
120 | 98 | ||
121 | return P4SEG_STORE_QUE; | 99 | spin_unlock_irq(&sq_mapping_lock); |
122 | } | 100 | } |
123 | 101 | ||
124 | /** | 102 | static int __sq_remap(struct sq_mapping *map, unsigned long flags) |
125 | * __sq_remap - Perform a translation from the SQ to a phys addr | ||
126 | * @map: sq mapping containing phys and store queue addresses. | ||
127 | * | ||
128 | * Maps the store queue address specified in the mapping to the physical | ||
129 | * address specified in the mapping. | ||
130 | */ | ||
131 | static struct sq_mapping *__sq_remap(struct sq_mapping *map) | ||
132 | { | 103 | { |
133 | unsigned long flags, pteh, ptel; | 104 | #if defined(CONFIG_MMU) |
134 | struct vm_struct *vma; | 105 | struct vm_struct *vma; |
135 | pgprot_t pgprot; | ||
136 | |||
137 | /* | ||
138 | * Without an MMU (or with it turned off), this is much more | ||
139 | * straightforward, as we can just load up each queue's QACR with | ||
140 | * the physical address appropriately masked. | ||
141 | */ | ||
142 | |||
143 | ctrl_outl(((map->addr >> 26) << 2) & 0x1c, SQ_QACR0); | ||
144 | ctrl_outl(((map->addr >> 26) << 2) & 0x1c, SQ_QACR1); | ||
145 | 106 | ||
146 | #ifdef CONFIG_MMU | ||
147 | /* | ||
148 | * With an MMU on the other hand, things are slightly more involved. | ||
149 | * Namely, we have to have a direct mapping between the SQ addr and | ||
150 | * the associated physical address in the UTLB by way of setting up | ||
151 | * a virt<->phys translation by hand. We do this by simply specifying | ||
152 | * the SQ addr in UTLB.VPN and the associated physical address in | ||
153 | * UTLB.PPN. | ||
154 | * | ||
155 | * Notably, even though this is a special case translation, and some | ||
156 | * of the configuration bits are meaningless, we're still required | ||
157 | * to have a valid ASID context in PTEH. | ||
158 | * | ||
159 | * We could also probably get by without explicitly setting PTEA, but | ||
160 | * we do it here just for good measure. | ||
161 | */ | ||
162 | spin_lock_irqsave(&sq_mapping_lock, flags); | ||
163 | |||
164 | pteh = map->sq_addr; | ||
165 | ctrl_outl((pteh & MMU_VPN_MASK) | get_asid(), MMU_PTEH); | ||
166 | |||
167 | ptel = map->addr & PAGE_MASK; | ||
168 | ctrl_outl(((ptel >> 28) & 0xe) | (ptel & 0x1), MMU_PTEA); | ||
169 | |||
170 | pgprot = pgprot_noncached(PAGE_KERNEL); | ||
171 | |||
172 | ptel &= _PAGE_FLAGS_HARDWARE_MASK; | ||
173 | ptel |= pgprot_val(pgprot); | ||
174 | ctrl_outl(ptel, MMU_PTEL); | ||
175 | |||
176 | __asm__ __volatile__ ("ldtlb" : : : "memory"); | ||
177 | |||
178 | spin_unlock_irqrestore(&sq_mapping_lock, flags); | ||
179 | |||
180 | /* | ||
181 | * Next, we need to map ourselves in the kernel page table, so that | ||
182 | * future accesses after a TLB flush will be handled when we take a | ||
183 | * page fault. | ||
184 | * | ||
185 | * Theoretically we could just do this directly and not worry about | ||
186 | * setting up the translation by hand ahead of time, but for the | ||
187 | * cases where we want a one-shot SQ mapping followed by a quick | ||
188 | * writeout before we hit the TLB flush, we do it anyways. This way | ||
189 | * we at least save ourselves the initial page fault overhead. | ||
190 | */ | ||
191 | vma = __get_vm_area(map->size, VM_ALLOC, map->sq_addr, SQ_ADDRMAX); | 107 | vma = __get_vm_area(map->size, VM_ALLOC, map->sq_addr, SQ_ADDRMAX); |
192 | if (!vma) | 108 | if (!vma) |
193 | return ERR_PTR(-ENOMEM); | 109 | return -ENOMEM; |
194 | 110 | ||
195 | vma->phys_addr = map->addr; | 111 | vma->phys_addr = map->addr; |
196 | 112 | ||
197 | if (remap_area_pages((unsigned long)vma->addr, vma->phys_addr, | 113 | if (remap_area_pages((unsigned long)vma->addr, vma->phys_addr, |
198 | map->size, pgprot_val(pgprot))) { | 114 | map->size, flags)) { |
199 | vunmap(vma->addr); | 115 | vunmap(vma->addr); |
200 | return NULL; | 116 | return -EAGAIN; |
201 | } | 117 | } |
202 | #endif /* CONFIG_MMU */ | 118 | #else |
119 | /* | ||
120 | * Without an MMU (or with it turned off), this is much more | ||
121 | * straightforward, as we can just load up each queue's QACR with | ||
122 | * the physical address appropriately masked. | ||
123 | */ | ||
124 | ctrl_outl(((map->addr >> 26) << 2) & 0x1c, SQ_QACR0); | ||
125 | ctrl_outl(((map->addr >> 26) << 2) & 0x1c, SQ_QACR1); | ||
126 | #endif | ||
203 | 127 | ||
204 | return map; | 128 | return 0; |
205 | } | 129 | } |
206 | 130 | ||
207 | /** | 131 | /** |
@@ -209,42 +133,65 @@ static struct sq_mapping *__sq_remap(struct sq_mapping *map) | |||
209 | * @phys: Physical address of mapping. | 133 | * @phys: Physical address of mapping. |
210 | * @size: Length of mapping. | 134 | * @size: Length of mapping. |
211 | * @name: User invoking mapping. | 135 | * @name: User invoking mapping. |
136 | * @flags: Protection flags. | ||
212 | * | 137 | * |
213 | * Remaps the physical address @phys through the next available store queue | 138 | * Remaps the physical address @phys through the next available store queue |
214 | * address of @size length. @name is logged at boot time as well as through | 139 | * address of @size length. @name is logged at boot time as well as through |
215 | * the procfs interface. | 140 | * the sysfs interface. |
216 | * | ||
217 | * A pre-allocated and filled sq_mapping pointer is returned, and must be | ||
218 | * cleaned up with a call to sq_unmap() when the user is done with the | ||
219 | * mapping. | ||
220 | */ | 141 | */ |
221 | struct sq_mapping *sq_remap(unsigned long phys, unsigned int size, const char *name) | 142 | unsigned long sq_remap(unsigned long phys, unsigned int size, |
143 | const char *name, unsigned long flags) | ||
222 | { | 144 | { |
223 | struct sq_mapping *map; | 145 | struct sq_mapping *map; |
224 | unsigned long virt, end; | 146 | unsigned long end; |
225 | unsigned int psz; | 147 | unsigned int psz; |
148 | int ret, page; | ||
226 | 149 | ||
227 | /* Don't allow wraparound or zero size */ | 150 | /* Don't allow wraparound or zero size */ |
228 | end = phys + size - 1; | 151 | end = phys + size - 1; |
229 | if (!size || end < phys) | 152 | if (unlikely(!size || end < phys)) |
230 | return NULL; | 153 | return -EINVAL; |
231 | /* Don't allow anyone to remap normal memory.. */ | 154 | /* Don't allow anyone to remap normal memory.. */ |
232 | if (phys < virt_to_phys(high_memory)) | 155 | if (unlikely(phys < virt_to_phys(high_memory))) |
233 | return NULL; | 156 | return -EINVAL; |
234 | 157 | ||
235 | phys &= PAGE_MASK; | 158 | phys &= PAGE_MASK; |
159 | size = PAGE_ALIGN(end + 1) - phys; | ||
160 | |||
161 | map = kmem_cache_alloc(sq_cache, GFP_KERNEL); | ||
162 | if (unlikely(!map)) | ||
163 | return -ENOMEM; | ||
164 | |||
165 | map->addr = phys; | ||
166 | map->size = size; | ||
167 | map->name = name; | ||
168 | |||
169 | page = bitmap_find_free_region(sq_bitmap, 0x04000000, | ||
170 | get_order(map->size)); | ||
171 | if (unlikely(page < 0)) { | ||
172 | ret = -ENOSPC; | ||
173 | goto out; | ||
174 | } | ||
175 | |||
176 | map->sq_addr = P4SEG_STORE_QUE + (page << PAGE_SHIFT); | ||
177 | |||
178 | ret = __sq_remap(map, flags); | ||
179 | if (unlikely(ret != 0)) | ||
180 | goto out; | ||
181 | |||
182 | psz = (size + (PAGE_SIZE - 1)) >> PAGE_SHIFT; | ||
183 | pr_info("sqremap: %15s [%4d page%s] va 0x%08lx pa 0x%08lx\n", | ||
184 | likely(map->name) ? map->name : "???", | ||
185 | psz, psz == 1 ? " " : "s", | ||
186 | map->sq_addr, map->addr); | ||
236 | 187 | ||
237 | size = PAGE_ALIGN(end + 1) - phys; | 188 | sq_mapping_list_add(map); |
238 | virt = __sq_get_next_addr(); | ||
239 | psz = (size + (PAGE_SIZE - 1)) / PAGE_SIZE; | ||
240 | map = __sq_alloc_mapping(virt, phys, size, name); | ||
241 | 189 | ||
242 | printk("sqremap: %15s [%4d page%s] va 0x%08lx pa 0x%08lx\n", | 190 | return map->sq_addr; |
243 | map->name ? map->name : "???", | ||
244 | psz, psz == 1 ? " " : "s", | ||
245 | map->sq_addr, map->addr); | ||
246 | 191 | ||
247 | return __sq_remap(map); | 192 | out: |
193 | kmem_cache_free(sq_cache, map); | ||
194 | return ret; | ||
248 | } | 195 | } |
249 | 196 | ||
250 | /** | 197 | /** |
@@ -255,188 +202,198 @@ struct sq_mapping *sq_remap(unsigned long phys, unsigned int size, const char *n | |||
255 | * sq_remap(). Also frees up the pte that was previously inserted into | 202 | * sq_remap(). Also frees up the pte that was previously inserted into |
256 | * the kernel page table and discards the UTLB translation. | 203 | * the kernel page table and discards the UTLB translation. |
257 | */ | 204 | */ |
258 | void sq_unmap(struct sq_mapping *map) | 205 | void sq_unmap(unsigned long vaddr) |
259 | { | 206 | { |
260 | if (map->sq_addr > (unsigned long)high_memory) | 207 | struct sq_mapping **p, *map; |
261 | vfree((void *)(map->sq_addr & PAGE_MASK)); | 208 | struct vm_struct *vma; |
209 | int page; | ||
262 | 210 | ||
263 | list_del(&map->list); | 211 | for (p = &sq_mapping_list; (map = *p); p = &map->next) |
264 | kfree(map); | 212 | if (map->sq_addr == vaddr) |
265 | } | 213 | break; |
266 | 214 | ||
267 | /** | 215 | if (unlikely(!map)) { |
268 | * sq_clear - Clear a store queue range | 216 | printk("%s: bad store queue address 0x%08lx\n", |
269 | * @addr: Address to start clearing from. | 217 | __FUNCTION__, vaddr); |
270 | * @len: Length to clear. | 218 | return; |
271 | * | 219 | } |
272 | * A quick zero-fill implementation for clearing out memory that has been | ||
273 | * remapped through the store queues. | ||
274 | */ | ||
275 | void sq_clear(unsigned long addr, unsigned int len) | ||
276 | { | ||
277 | int i; | ||
278 | 220 | ||
279 | /* Clear out both queues linearly */ | 221 | page = (map->sq_addr - P4SEG_STORE_QUE) >> PAGE_SHIFT; |
280 | for (i = 0; i < 8; i++) { | 222 | bitmap_release_region(sq_bitmap, page, get_order(map->size)); |
281 | ctrl_outl(0, addr + i + 0); | 223 | |
282 | ctrl_outl(0, addr + i + 8); | 224 | #ifdef CONFIG_MMU |
225 | vma = remove_vm_area((void *)(map->sq_addr & PAGE_MASK)); | ||
226 | if (!vma) { | ||
227 | printk(KERN_ERR "%s: bad address 0x%08lx\n", | ||
228 | __FUNCTION__, map->sq_addr); | ||
229 | return; | ||
283 | } | 230 | } |
231 | #endif | ||
232 | |||
233 | sq_mapping_list_del(map); | ||
284 | 234 | ||
285 | sq_flush_range(addr, len); | 235 | kmem_cache_free(sq_cache, map); |
286 | } | 236 | } |
287 | 237 | ||
288 | /** | 238 | /* |
289 | * sq_vma_unmap - Unmap a VMA range | 239 | * Needlessly complex sysfs interface. Unfortunately it doesn't seem like |
290 | * @area: VMA containing range. | 240 | * there is any other easy way to add things on a per-cpu basis without |
291 | * @addr: Start of range. | 241 | * putting the directory entries somewhere stupid and having to create |
292 | * @len: Length of range. | 242 | * links in sysfs by hand back in to the per-cpu directories. |
293 | * | 243 | * |
294 | * Searches the sq_mapping_list for a mapping matching the sq addr @addr, | 244 | * Some day we may want to have an additional abstraction per store |
295 | * and subsequently frees up the entry. Further cleanup is done by generic | 245 | * queue, but considering the kobject hell we already have to deal with, |
296 | * code. | 246 | * it's simply not worth the trouble. |
297 | */ | 247 | */ |
298 | static void sq_vma_unmap(struct vm_area_struct *area, | 248 | static struct kobject *sq_kobject[NR_CPUS]; |
299 | unsigned long addr, size_t len) | ||
300 | { | ||
301 | struct list_head *pos, *tmp; | ||
302 | 249 | ||
303 | list_for_each_safe(pos, tmp, &sq_mapping_list) { | 250 | struct sq_sysfs_attr { |
304 | struct sq_mapping *entry; | 251 | struct attribute attr; |
252 | ssize_t (*show)(char *buf); | ||
253 | ssize_t (*store)(const char *buf, size_t count); | ||
254 | }; | ||
305 | 255 | ||
306 | entry = list_entry(pos, typeof(*entry), list); | 256 | #define to_sq_sysfs_attr(attr) container_of(attr, struct sq_sysfs_attr, attr) |
307 | 257 | ||
308 | if (entry->sq_addr == addr) { | 258 | static ssize_t sq_sysfs_show(struct kobject *kobj, struct attribute *attr, |
309 | /* | 259 | char *buf) |
310 | * We could probably get away without doing the tlb flush | 260 | { |
311 | * here, as generic code should take care of most of this | 261 | struct sq_sysfs_attr *sattr = to_sq_sysfs_attr(attr); |
312 | * when unmapping the rest of the VMA range for us. Leave | ||
313 | * it in for added sanity for the time being.. | ||
314 | */ | ||
315 | __flush_tlb_page(get_asid(), entry->sq_addr & PAGE_MASK); | ||
316 | 262 | ||
317 | list_del(&entry->list); | 263 | if (likely(sattr->show)) |
318 | kfree(entry); | 264 | return sattr->show(buf); |
319 | 265 | ||
320 | return; | 266 | return -EIO; |
321 | } | ||
322 | } | ||
323 | } | 267 | } |
324 | 268 | ||
325 | /** | 269 | static ssize_t sq_sysfs_store(struct kobject *kobj, struct attribute *attr, |
326 | * sq_vma_sync - Sync a VMA range | 270 | const char *buf, size_t count) |
327 | * @area: VMA containing range. | ||
328 | * @start: Start of range. | ||
329 | * @len: Length of range. | ||
330 | * @flags: Additional flags. | ||
331 | * | ||
332 | * Synchronizes an sq mapped range by flushing the store queue cache for | ||
333 | * the duration of the mapping. | ||
334 | * | ||
335 | * Used internally for user mappings, which must use msync() to prefetch | ||
336 | * the store queue cache. | ||
337 | */ | ||
338 | static int sq_vma_sync(struct vm_area_struct *area, | ||
339 | unsigned long start, size_t len, unsigned int flags) | ||
340 | { | 271 | { |
341 | sq_flush_range(start, len); | 272 | struct sq_sysfs_attr *sattr = to_sq_sysfs_attr(attr); |
342 | 273 | ||
343 | return 0; | 274 | if (likely(sattr->store)) |
275 | return sattr->store(buf, count); | ||
276 | |||
277 | return -EIO; | ||
344 | } | 278 | } |
345 | 279 | ||
346 | static struct vm_operations_struct sq_vma_ops = { | 280 | static ssize_t mapping_show(char *buf) |
347 | .unmap = sq_vma_unmap, | 281 | { |
348 | .sync = sq_vma_sync, | 282 | struct sq_mapping **list, *entry; |
349 | }; | 283 | char *p = buf; |
350 | 284 | ||
351 | /** | 285 | for (list = &sq_mapping_list; (entry = *list); list = &entry->next) |
352 | * sq_mmap - mmap() for /dev/cpu/sq | 286 | p += sprintf(p, "%08lx-%08lx [%08lx]: %s\n", |
353 | * @file: unused. | 287 | entry->sq_addr, entry->sq_addr + entry->size, |
354 | * @vma: VMA to remap. | 288 | entry->addr, entry->name); |
355 | * | 289 | |
356 | * Remap the specified vma @vma through the store queues, and setup associated | 290 | return p - buf; |
357 | * information for the new mapping. Also build up the page tables for the new | 291 | } |
358 | * area. | 292 | |
359 | */ | 293 | static ssize_t mapping_store(const char *buf, size_t count) |
360 | static int sq_mmap(struct file *file, struct vm_area_struct *vma) | ||
361 | { | 294 | { |
362 | unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; | 295 | unsigned long base = 0, len = 0; |
363 | unsigned long size = vma->vm_end - vma->vm_start; | ||
364 | struct sq_mapping *map; | ||
365 | 296 | ||
366 | /* | 297 | sscanf(buf, "%lx %lx", &base, &len); |
367 | * We're not interested in any arbitrary virtual address that has | 298 | if (!base) |
368 | * been stuck in the VMA, as we already know what addresses we | 299 | return -EIO; |
369 | * want. Save off the size, and reposition the VMA to begin at | ||
370 | * the next available sq address. | ||
371 | */ | ||
372 | vma->vm_start = __sq_get_next_addr(); | ||
373 | vma->vm_end = vma->vm_start + size; | ||
374 | 300 | ||
375 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | 301 | if (likely(len)) { |
302 | int ret = sq_remap(base, len, "Userspace", | ||
303 | pgprot_val(PAGE_SHARED)); | ||
304 | if (ret < 0) | ||
305 | return ret; | ||
306 | } else | ||
307 | sq_unmap(base); | ||
376 | 308 | ||
377 | vma->vm_flags |= VM_IO | VM_RESERVED; | 309 | return count; |
310 | } | ||
378 | 311 | ||
379 | map = __sq_alloc_mapping(vma->vm_start, offset, size, "Userspace"); | 312 | static struct sq_sysfs_attr mapping_attr = |
313 | __ATTR(mapping, 0644, mapping_show, mapping_store); | ||
380 | 314 | ||
381 | if (io_remap_pfn_range(vma, map->sq_addr, map->addr >> PAGE_SHIFT, | 315 | static struct attribute *sq_sysfs_attrs[] = { |
382 | size, vma->vm_page_prot)) | 316 | &mapping_attr.attr, |
383 | return -EAGAIN; | 317 | NULL, |
318 | }; | ||
384 | 319 | ||
385 | vma->vm_ops = &sq_vma_ops; | 320 | static struct sysfs_ops sq_sysfs_ops = { |
321 | .show = sq_sysfs_show, | ||
322 | .store = sq_sysfs_store, | ||
323 | }; | ||
386 | 324 | ||
387 | return 0; | 325 | static struct kobj_type ktype_percpu_entry = { |
388 | } | 326 | .sysfs_ops = &sq_sysfs_ops, |
327 | .default_attrs = sq_sysfs_attrs, | ||
328 | }; | ||
389 | 329 | ||
390 | #ifdef CONFIG_PROC_FS | 330 | static int __devinit sq_sysdev_add(struct sys_device *sysdev) |
391 | static int sq_mapping_read_proc(char *buf, char **start, off_t off, | ||
392 | int len, int *eof, void *data) | ||
393 | { | 331 | { |
394 | struct list_head *pos; | 332 | unsigned int cpu = sysdev->id; |
395 | char *p = buf; | 333 | struct kobject *kobj; |
396 | 334 | ||
397 | list_for_each_prev(pos, &sq_mapping_list) { | 335 | sq_kobject[cpu] = kzalloc(sizeof(struct kobject), GFP_KERNEL); |
398 | struct sq_mapping *entry; | 336 | if (unlikely(!sq_kobject[cpu])) |
337 | return -ENOMEM; | ||
399 | 338 | ||
400 | entry = list_entry(pos, typeof(*entry), list); | 339 | kobj = sq_kobject[cpu]; |
340 | kobj->parent = &sysdev->kobj; | ||
341 | kobject_set_name(kobj, "%s", "sq"); | ||
342 | kobj->ktype = &ktype_percpu_entry; | ||
401 | 343 | ||
402 | p += sprintf(p, "%08lx-%08lx [%08lx]: %s\n", entry->sq_addr, | 344 | return kobject_register(kobj); |
403 | entry->sq_addr + entry->size - 1, entry->addr, | ||
404 | entry->name); | ||
405 | } | ||
406 | |||
407 | return p - buf; | ||
408 | } | 345 | } |
409 | #endif | ||
410 | 346 | ||
411 | static struct file_operations sq_fops = { | 347 | static int __devexit sq_sysdev_remove(struct sys_device *sysdev) |
412 | .owner = THIS_MODULE, | 348 | { |
413 | .mmap = sq_mmap, | 349 | unsigned int cpu = sysdev->id; |
414 | }; | 350 | struct kobject *kobj = sq_kobject[cpu]; |
415 | 351 | ||
416 | static struct miscdevice sq_dev = { | 352 | kobject_unregister(kobj); |
417 | .minor = STORE_QUEUE_MINOR, | 353 | return 0; |
418 | .name = "sq", | 354 | } |
419 | .fops = &sq_fops, | 355 | |
356 | static struct sysdev_driver sq_sysdev_driver = { | ||
357 | .add = sq_sysdev_add, | ||
358 | .remove = __devexit_p(sq_sysdev_remove), | ||
420 | }; | 359 | }; |
421 | 360 | ||
422 | static int __init sq_api_init(void) | 361 | static int __init sq_api_init(void) |
423 | { | 362 | { |
424 | int ret; | 363 | unsigned int nr_pages = 0x04000000 >> PAGE_SHIFT; |
364 | unsigned int size = (nr_pages + (BITS_PER_LONG - 1)) / BITS_PER_LONG; | ||
365 | int ret = -ENOMEM; | ||
366 | |||
425 | printk(KERN_NOTICE "sq: Registering store queue API.\n"); | 367 | printk(KERN_NOTICE "sq: Registering store queue API.\n"); |
426 | 368 | ||
427 | create_proc_read_entry("sq_mapping", 0, 0, sq_mapping_read_proc, 0); | 369 | sq_cache = kmem_cache_create("store_queue_cache", |
370 | sizeof(struct sq_mapping), 0, 0, | ||
371 | NULL, NULL); | ||
372 | if (unlikely(!sq_cache)) | ||
373 | return ret; | ||
428 | 374 | ||
429 | ret = misc_register(&sq_dev); | 375 | sq_bitmap = kzalloc(size, GFP_KERNEL); |
430 | if (ret) | 376 | if (unlikely(!sq_bitmap)) |
431 | remove_proc_entry("sq_mapping", NULL); | 377 | goto out; |
378 | |||
379 | ret = sysdev_driver_register(&cpu_sysdev_class, &sq_sysdev_driver); | ||
380 | if (unlikely(ret != 0)) | ||
381 | goto out; | ||
382 | |||
383 | return 0; | ||
384 | |||
385 | out: | ||
386 | kfree(sq_bitmap); | ||
387 | kmem_cache_destroy(sq_cache); | ||
432 | 388 | ||
433 | return ret; | 389 | return ret; |
434 | } | 390 | } |
435 | 391 | ||
436 | static void __exit sq_api_exit(void) | 392 | static void __exit sq_api_exit(void) |
437 | { | 393 | { |
438 | misc_deregister(&sq_dev); | 394 | sysdev_driver_unregister(&cpu_sysdev_class, &sq_sysdev_driver); |
439 | remove_proc_entry("sq_mapping", NULL); | 395 | kfree(sq_bitmap); |
396 | kmem_cache_destroy(sq_cache); | ||
440 | } | 397 | } |
441 | 398 | ||
442 | module_init(sq_api_init); | 399 | module_init(sq_api_init); |
@@ -445,11 +402,7 @@ module_exit(sq_api_exit); | |||
445 | MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>, M. R. Brown <mrbrown@0xd6.org>"); | 402 | MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>, M. R. Brown <mrbrown@0xd6.org>"); |
446 | MODULE_DESCRIPTION("Simple API for SH-4 integrated Store Queues"); | 403 | MODULE_DESCRIPTION("Simple API for SH-4 integrated Store Queues"); |
447 | MODULE_LICENSE("GPL"); | 404 | MODULE_LICENSE("GPL"); |
448 | MODULE_ALIAS_MISCDEV(STORE_QUEUE_MINOR); | ||
449 | 405 | ||
450 | EXPORT_SYMBOL(sq_remap); | 406 | EXPORT_SYMBOL(sq_remap); |
451 | EXPORT_SYMBOL(sq_unmap); | 407 | EXPORT_SYMBOL(sq_unmap); |
452 | EXPORT_SYMBOL(sq_clear); | ||
453 | EXPORT_SYMBOL(sq_flush); | ||
454 | EXPORT_SYMBOL(sq_flush_range); | 408 | EXPORT_SYMBOL(sq_flush_range); |
455 | |||