1
2
3
4
5
6
7
8
9
10
11
12
13#include <linux/init.h>
14#include <linux/mman.h>
15#include <linux/mm.h>
16#include <asm/tlb.h>
17#include <asm/processor.h>
18#include <asm/cache.h>
19#include <asm/pgalloc.h>
20#include <asm/uaccess.h>
21#include <asm/mmu_context.h>
22
23
24static unsigned long long dtlb_cache_slot;
25
26void __init p3_cache_init(void)
27{
28
29 dtlb_cache_slot = sh64_get_wired_dtlb_entry();
30}
31
32#ifdef CONFIG_DCACHE_DISABLED
33#define sh64_dcache_purge_all() do { } while (0)
34#define sh64_dcache_purge_coloured_phy_page(paddr, eaddr) do { } while (0)
35#define sh64_dcache_purge_user_range(mm, start, end) do { } while (0)
36#define sh64_dcache_purge_phy_page(paddr) do { } while (0)
37#define sh64_dcache_purge_virt_page(mm, eaddr) do { } while (0)
38#endif
39
40
41
42
43
44
45static inline void
46sh64_setup_dtlb_cache_slot(unsigned long eaddr, unsigned long asid,
47 unsigned long paddr)
48{
49 local_irq_disable();
50 sh64_setup_tlb_slot(dtlb_cache_slot, eaddr, asid, paddr);
51}
52
53static inline void sh64_teardown_dtlb_cache_slot(void)
54{
55 sh64_teardown_tlb_slot(dtlb_cache_slot);
56 local_irq_enable();
57}
58
59#ifndef CONFIG_ICACHE_DISABLED
60static inline void sh64_icache_inv_all(void)
61{
62 unsigned long long addr, flag, data;
63 unsigned int flags;
64
65 addr = ICCR0;
66 flag = ICCR0_ICI;
67 data = 0;
68
69
70 local_irq_save(flags);
71
72
73 __asm__ __volatile__ (
74 "getcfg %3, 0, %0\n\t"
75 "or %0, %2, %0\n\t"
76 "putcfg %3, 0, %0\n\t"
77 "synci"
78 : "=&r" (data)
79 : "0" (data), "r" (flag), "r" (addr));
80
81 local_irq_restore(flags);
82}
83
84static void sh64_icache_inv_kernel_range(unsigned long start, unsigned long end)
85{
86
87
88
89 unsigned long long ullend, addr, aligned_start;
90 aligned_start = (unsigned long long)(signed long long)(signed long) start;
91 addr = L1_CACHE_ALIGN(aligned_start);
92 ullend = (unsigned long long) (signed long long) (signed long) end;
93
94 while (addr <= ullend) {
95 __asm__ __volatile__ ("icbi %0, 0" : : "r" (addr));
96 addr += L1_CACHE_BYTES;
97 }
98}
99
100static void sh64_icache_inv_user_page(struct vm_area_struct *vma, unsigned long eaddr)
101{
102
103
104 unsigned int cpu = smp_processor_id();
105 unsigned long long addr, end_addr;
106 unsigned long flags = 0;
107 unsigned long running_asid, vma_asid;
108 addr = eaddr;
109 end_addr = addr + PAGE_SIZE;
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125 running_asid = get_asid();
126 vma_asid = cpu_asid(cpu, vma->vm_mm);
127 if (running_asid != vma_asid) {
128 local_irq_save(flags);
129 switch_and_save_asid(vma_asid);
130 }
131 while (addr < end_addr) {
132
133 __asm__ __volatile__("icbi %0, 0" : : "r" (addr));
134 __asm__ __volatile__("icbi %0, 32" : : "r" (addr));
135 __asm__ __volatile__("icbi %0, 64" : : "r" (addr));
136 __asm__ __volatile__("icbi %0, 96" : : "r" (addr));
137 addr += 128;
138 }
139 if (running_asid != vma_asid) {
140 switch_and_save_asid(running_asid);
141 local_irq_restore(flags);
142 }
143}
144
145static void sh64_icache_inv_user_page_range(struct mm_struct *mm,
146 unsigned long start, unsigned long end)
147{
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162 int n_pages;
163
164 if (!mm)
165 return;
166
167 n_pages = ((end - start) >> PAGE_SHIFT);
168 if (n_pages >= 64) {
169 sh64_icache_inv_all();
170 } else {
171 unsigned long aligned_start;
172 unsigned long eaddr;
173 unsigned long after_last_page_start;
174 unsigned long mm_asid, current_asid;
175 unsigned long long flags = 0ULL;
176
177 mm_asid = cpu_asid(smp_processor_id(), mm);
178 current_asid = get_asid();
179
180 if (mm_asid != current_asid) {
181
182 local_irq_save(flags);
183 switch_and_save_asid(mm_asid);
184 }
185
186 aligned_start = start & PAGE_MASK;
187 after_last_page_start = PAGE_SIZE + ((end - 1) & PAGE_MASK);
188
189 while (aligned_start < after_last_page_start) {
190 struct vm_area_struct *vma;
191 unsigned long vma_end;
192 vma = find_vma(mm, aligned_start);
193 if (!vma || (aligned_start <= vma->vm_end)) {
194
195 aligned_start += PAGE_SIZE;
196 continue;
197 }
198 vma_end = vma->vm_end;
199 if (vma->vm_flags & VM_EXEC) {
200
201 eaddr = aligned_start;
202 while (eaddr < vma_end) {
203 sh64_icache_inv_user_page(vma, eaddr);
204 eaddr += PAGE_SIZE;
205 }
206 }
207 aligned_start = vma->vm_end;
208 }
209
210 if (mm_asid != current_asid) {
211 switch_and_save_asid(current_asid);
212 local_irq_restore(flags);
213 }
214 }
215}
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233static void sh64_icache_inv_user_small_range(struct mm_struct *mm,
234 unsigned long start, int len)
235{
236 unsigned long long eaddr = start;
237 unsigned long long eaddr_end = start + len;
238 unsigned long current_asid, mm_asid;
239 unsigned long long flags;
240 unsigned long long epage_start;
241
242
243
244
245
246 eaddr = L1_CACHE_ALIGN(start);
247 eaddr_end = start + len;
248
249 mm_asid = cpu_asid(smp_processor_id(), mm);
250 local_irq_save(flags);
251 current_asid = switch_and_save_asid(mm_asid);
252
253 epage_start = eaddr & PAGE_MASK;
254
255 while (eaddr < eaddr_end) {
256 __asm__ __volatile__("icbi %0, 0" : : "r" (eaddr));
257 eaddr += L1_CACHE_BYTES;
258 }
259 switch_and_save_asid(current_asid);
260 local_irq_restore(flags);
261}
262
263static void sh64_icache_inv_current_user_range(unsigned long start, unsigned long end)
264{
265
266
267
268
269 unsigned long long aligned_start;
270 unsigned long long ull_end;
271 unsigned long long addr;
272
273 ull_end = end;
274
275
276
277
278
279
280
281 aligned_start = L1_CACHE_ALIGN(start);
282 addr = aligned_start;
283 while (addr < ull_end) {
284 __asm__ __volatile__ ("icbi %0, 0" : : "r" (addr));
285 __asm__ __volatile__ ("nop");
286 __asm__ __volatile__ ("nop");
287 addr += L1_CACHE_BYTES;
288 }
289}
290#endif
291
292#ifndef CONFIG_DCACHE_DISABLED
293
294
295#define DUMMY_ALLOCO_AREA_SIZE ((L1_CACHE_BYTES << 10) + (1024 * 4))
296static unsigned char dummy_alloco_area[DUMMY_ALLOCO_AREA_SIZE] __cacheline_aligned = { 0, };
297
298static void inline sh64_dcache_purge_sets(int sets_to_purge_base, int n_sets)
299{
300
301
302
303
304 int dummy_buffer_base_set;
305 unsigned long long eaddr, eaddr0, eaddr1;
306 int j;
307 int set_offset;
308
309 dummy_buffer_base_set = ((int)&dummy_alloco_area &
310 cpu_data->dcache.entry_mask) >>
311 cpu_data->dcache.entry_shift;
312 set_offset = sets_to_purge_base - dummy_buffer_base_set;
313
314 for (j = 0; j < n_sets; j++, set_offset++) {
315 set_offset &= (cpu_data->dcache.sets - 1);
316 eaddr0 = (unsigned long long)dummy_alloco_area +
317 (set_offset << cpu_data->dcache.entry_shift);
318
319
320
321
322
323
324
325
326 eaddr1 = eaddr0 + cpu_data->dcache.way_size *
327 cpu_data->dcache.ways;
328
329 for (eaddr = eaddr0; eaddr < eaddr1;
330 eaddr += cpu_data->dcache.way_size) {
331 __asm__ __volatile__ ("alloco %0, 0" : : "r" (eaddr));
332 __asm__ __volatile__ ("synco");
333 }
334
335 eaddr1 = eaddr0 + cpu_data->dcache.way_size *
336 cpu_data->dcache.ways;
337
338 for (eaddr = eaddr0; eaddr < eaddr1;
339 eaddr += cpu_data->dcache.way_size) {
340
341
342
343
344 if (test_bit(SH_CACHE_MODE_WT, &(cpu_data->dcache.flags)))
345 ctrl_inb(eaddr);
346 }
347 }
348
349
350
351
352
353
354}
355
356
357
358
359
360
361
362
363
364static void sh64_dcache_purge_all(void)
365{
366
367 sh64_dcache_purge_sets(0, cpu_data->dcache.sets);
368}
369
370
371
372
373#define MAGIC_PAGE0_START 0xffffffffec000000ULL
374
375
376
377
378
379
380
381
382
383
384
385
386static void sh64_dcache_purge_coloured_phy_page(unsigned long paddr,
387 unsigned long eaddr)
388{
389 unsigned long long magic_page_start;
390 unsigned long long magic_eaddr, magic_eaddr_end;
391
392 magic_page_start = MAGIC_PAGE0_START + (eaddr & CACHE_OC_SYN_MASK);
393
394
395
396 sh64_setup_dtlb_cache_slot(magic_page_start, get_asid(), paddr);
397
398 magic_eaddr = magic_page_start;
399 magic_eaddr_end = magic_eaddr + PAGE_SIZE;
400
401 while (magic_eaddr < magic_eaddr_end) {
402
403
404
405 __asm__ __volatile__ ("ocbp %0, 0" : : "r" (magic_eaddr));
406 magic_eaddr += L1_CACHE_BYTES;
407 }
408
409 sh64_teardown_dtlb_cache_slot();
410}
411
412
413
414
415
416
417
418
419
420static void sh64_dcache_purge_phy_page(unsigned long paddr)
421{
422 unsigned long long eaddr_start, eaddr, eaddr_end;
423 int i;
424
425
426
427 eaddr_start = MAGIC_PAGE0_START;
428 for (i = 0; i < (1 << CACHE_OC_N_SYNBITS); i++) {
429 sh64_setup_dtlb_cache_slot(eaddr_start, get_asid(), paddr);
430
431 eaddr = eaddr_start;
432 eaddr_end = eaddr + PAGE_SIZE;
433 while (eaddr < eaddr_end) {
434 __asm__ __volatile__ ("ocbp %0, 0" : : "r" (eaddr));
435 eaddr += L1_CACHE_BYTES;
436 }
437
438 sh64_teardown_dtlb_cache_slot();
439 eaddr_start += PAGE_SIZE;
440 }
441}
442
443static void sh64_dcache_purge_user_pages(struct mm_struct *mm,
444 unsigned long addr, unsigned long end)
445{
446 pgd_t *pgd;
447 pud_t *pud;
448 pmd_t *pmd;
449 pte_t *pte;
450 pte_t entry;
451 spinlock_t *ptl;
452 unsigned long paddr;
453
454 if (!mm)
455 return;
456
457 pgd = pgd_offset(mm, addr);
458 if (pgd_bad(*pgd))
459 return;
460
461 pud = pud_offset(pgd, addr);
462 if (pud_none(*pud) || pud_bad(*pud))
463 return;
464
465 pmd = pmd_offset(pud, addr);
466 if (pmd_none(*pmd) || pmd_bad(*pmd))
467 return;
468
469 pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
470 do {
471 entry = *pte;
472 if (pte_none(entry) || !pte_present(entry))
473 continue;
474 paddr = pte_val(entry) & PAGE_MASK;
475 sh64_dcache_purge_coloured_phy_page(paddr, addr);
476 } while (pte++, addr += PAGE_SIZE, addr != end);
477 pte_unmap_unlock(pte - 1, ptl);
478}
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528static void sh64_dcache_purge_user_range(struct mm_struct *mm,
529 unsigned long start, unsigned long end)
530{
531 int n_pages = ((end - start) >> PAGE_SHIFT);
532
533 if (n_pages >= 64 || ((start ^ (end - 1)) & PMD_MASK)) {
534 sh64_dcache_purge_all();
535 } else {
536
537 start &= PAGE_MASK;
538 end = PAGE_ALIGN(end);
539 sh64_dcache_purge_user_pages(mm, start, end);
540 }
541}
542
543
544
545
546
547
548
549void __flush_purge_region(void *start, int size)
550{
551 unsigned long long ullend, addr, aligned_start;
552
553 aligned_start = (unsigned long long)(signed long long)(signed long) start;
554 addr = L1_CACHE_ALIGN(aligned_start);
555 ullend = (unsigned long long) (signed long long) (signed long) start + size;
556
557 while (addr <= ullend) {
558 __asm__ __volatile__ ("ocbp %0, 0" : : "r" (addr));
559 addr += L1_CACHE_BYTES;
560 }
561}
562
563void __flush_wback_region(void *start, int size)
564{
565 unsigned long long ullend, addr, aligned_start;
566
567 aligned_start = (unsigned long long)(signed long long)(signed long) start;
568 addr = L1_CACHE_ALIGN(aligned_start);
569 ullend = (unsigned long long) (signed long long) (signed long) start + size;
570
571 while (addr < ullend) {
572 __asm__ __volatile__ ("ocbwb %0, 0" : : "r" (addr));
573 addr += L1_CACHE_BYTES;
574 }
575}
576
577void __flush_invalidate_region(void *start, int size)
578{
579 unsigned long long ullend, addr, aligned_start;
580
581 aligned_start = (unsigned long long)(signed long long)(signed long) start;
582 addr = L1_CACHE_ALIGN(aligned_start);
583 ullend = (unsigned long long) (signed long long) (signed long) start + size;
584
585 while (addr < ullend) {
586 __asm__ __volatile__ ("ocbi %0, 0" : : "r" (addr));
587 addr += L1_CACHE_BYTES;
588 }
589}
590#endif
591
592
593
594
595
596void flush_cache_all(void)
597{
598 sh64_dcache_purge_all();
599 sh64_icache_inv_all();
600}
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623void flush_cache_mm(struct mm_struct *mm)
624{
625 sh64_dcache_purge_all();
626}
627
628
629
630
631
632
633
634
635void flush_cache_range(struct vm_area_struct *vma, unsigned long start,
636 unsigned long end)
637{
638 struct mm_struct *mm = vma->vm_mm;
639
640 sh64_dcache_purge_user_range(mm, start, end);
641 sh64_icache_inv_user_page_range(mm, start, end);
642}
643
644
645
646
647
648
649
650
651
652
653void flush_cache_page(struct vm_area_struct *vma, unsigned long eaddr,
654 unsigned long pfn)
655{
656 sh64_dcache_purge_phy_page(pfn << PAGE_SHIFT);
657
658 if (vma->vm_flags & VM_EXEC)
659 sh64_icache_inv_user_page(vma, eaddr);
660}
661
662void flush_dcache_page(struct page *page)
663{
664 sh64_dcache_purge_phy_page(page_to_phys(page));
665 wmb();
666}
667
668
669
670
671
672
673
674
675
676void flush_icache_range(unsigned long start, unsigned long end)
677{
678 __flush_purge_region((void *)start, end);
679 wmb();
680 sh64_icache_inv_kernel_range(start, end);
681}
682
683
684
685
686
687
688
689
690
691void flush_icache_user_range(struct vm_area_struct *vma,
692 struct page *page, unsigned long addr, int len)
693{
694
695 sh64_dcache_purge_coloured_phy_page(page_to_phys(page), addr);
696 mb();
697
698 if (vma->vm_flags & VM_EXEC)
699 sh64_icache_inv_user_small_range(vma->vm_mm, addr, len);
700}
701
702
703
704
705
706
707
708void flush_cache_sigtramp(unsigned long vaddr)
709{
710 unsigned long end = vaddr + L1_CACHE_BYTES;
711
712 __flush_wback_region((void *)vaddr, L1_CACHE_BYTES);
713 wmb();
714 sh64_icache_inv_current_user_range(vaddr, end);
715}
716
717#ifdef CONFIG_MMU
718
719
720
721
722#define UNIQUE_EADDR_START 0xe0000000UL
723#define UNIQUE_EADDR_END 0xe8000000UL
724
725
726
727
728
729
730
731static unsigned long sh64_make_unique_eaddr(unsigned long user_eaddr,
732 unsigned long paddr)
733{
734 static unsigned long current_pointer = UNIQUE_EADDR_START;
735 unsigned long coloured_pointer;
736
737 if (current_pointer == UNIQUE_EADDR_END) {
738 sh64_dcache_purge_all();
739 current_pointer = UNIQUE_EADDR_START;
740 }
741
742 coloured_pointer = (current_pointer & ~CACHE_OC_SYN_MASK) |
743 (user_eaddr & CACHE_OC_SYN_MASK);
744 sh64_setup_dtlb_cache_slot(coloured_pointer, get_asid(), paddr);
745
746 current_pointer += (PAGE_SIZE << CACHE_OC_N_SYNBITS);
747
748 return coloured_pointer;
749}
750
751static void sh64_copy_user_page_coloured(void *to, void *from,
752 unsigned long address)
753{
754 void *coloured_to;
755
756
757
758
759
760
761 sh64_dcache_purge_coloured_phy_page(__pa(to), (unsigned long)to);
762
763 coloured_to = (void *)sh64_make_unique_eaddr(address, __pa(to));
764 copy_page(from, coloured_to);
765
766 sh64_teardown_dtlb_cache_slot();
767}
768
769static void sh64_clear_user_page_coloured(void *to, unsigned long address)
770{
771 void *coloured_to;
772
773
774
775
776
777 sh64_dcache_purge_coloured_phy_page(__pa(to), (unsigned long)to);
778
779 coloured_to = (void *)sh64_make_unique_eaddr(address, __pa(to));
780 clear_page(coloured_to);
781
782 sh64_teardown_dtlb_cache_slot();
783}
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808void copy_user_page(void *to, void *from, unsigned long address,
809 struct page *page)
810{
811 if (((address ^ (unsigned long) from) & CACHE_OC_SYN_MASK) != 0)
812 sh64_dcache_purge_coloured_phy_page(__pa(from), address);
813
814 if (((address ^ (unsigned long) to) & CACHE_OC_SYN_MASK) == 0)
815 copy_page(to, from);
816 else
817 sh64_copy_user_page_coloured(to, from, address);
818}
819
820
821
822
823
824
825
826
827void clear_user_page(void *to, unsigned long address, struct page *page)
828{
829 if (((address ^ (unsigned long) to) & CACHE_OC_SYN_MASK) == 0)
830 clear_page(to);
831 else
832 sh64_clear_user_page_coloured(to, address);
833}
834#endif
835