diff options
author | Paul Mundt <lethal@linux-sh.org> | 2009-08-15 12:50:17 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2009-08-15 12:50:17 -0400 |
commit | 94ecd224c940830e2f2724c3860eb7fb74c15d31 (patch) | |
tree | b3940834bc26796af862acf1a24810a2d0d865c9 /arch/sh/mm/cache-sh5.c | |
parent | 1ee4ab09f38b77b3a5750429d456d6606b237924 (diff) |
sh: Fix up the SH-5 build with caches enabled.
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/sh/mm/cache-sh5.c')
-rw-r--r-- | arch/sh/mm/cache-sh5.c | 249 |
1 files changed, 21 insertions, 228 deletions
diff --git a/arch/sh/mm/cache-sh5.c b/arch/sh/mm/cache-sh5.c index a8f5142dc2cf..819e0f9e8dba 100644 --- a/arch/sh/mm/cache-sh5.c +++ b/arch/sh/mm/cache-sh5.c | |||
@@ -25,29 +25,6 @@ extern void __weak sh4__flush_region_init(void); | |||
25 | /* Wired TLB entry for the D-cache */ | 25 | /* Wired TLB entry for the D-cache */ |
26 | static unsigned long long dtlb_cache_slot; | 26 | static unsigned long long dtlb_cache_slot; |
27 | 27 | ||
28 | void __init cpu_cache_init(void) | ||
29 | { | ||
30 | /* Reserve a slot for dcache colouring in the DTLB */ | ||
31 | dtlb_cache_slot = sh64_get_wired_dtlb_entry(); | ||
32 | |||
33 | sh4__flush_region_init(); | ||
34 | } | ||
35 | |||
36 | void __init kmap_coherent_init(void) | ||
37 | { | ||
38 | /* XXX ... */ | ||
39 | } | ||
40 | |||
41 | void *kmap_coherent(struct page *page, unsigned long addr) | ||
42 | { | ||
43 | /* XXX ... */ | ||
44 | return NULL; | ||
45 | } | ||
46 | |||
47 | void kunmap_coherent(void) | ||
48 | { | ||
49 | } | ||
50 | |||
51 | #ifdef CONFIG_DCACHE_DISABLED | 28 | #ifdef CONFIG_DCACHE_DISABLED |
52 | #define sh64_dcache_purge_all() do { } while (0) | 29 | #define sh64_dcache_purge_all() do { } while (0) |
53 | #define sh64_dcache_purge_coloured_phy_page(paddr, eaddr) do { } while (0) | 30 | #define sh64_dcache_purge_coloured_phy_page(paddr, eaddr) do { } while (0) |
@@ -233,52 +210,6 @@ static void sh64_icache_inv_user_page_range(struct mm_struct *mm, | |||
233 | } | 210 | } |
234 | } | 211 | } |
235 | 212 | ||
236 | /* | ||
237 | * Invalidate a small range of user context I-cache, not necessarily page | ||
238 | * (or even cache-line) aligned. | ||
239 | * | ||
240 | * Since this is used inside ptrace, the ASID in the mm context typically | ||
241 | * won't match current_asid. We'll have to switch ASID to do this. For | ||
242 | * safety, and given that the range will be small, do all this under cli. | ||
243 | * | ||
244 | * Note, there is a hazard that the ASID in mm->context is no longer | ||
245 | * actually associated with mm, i.e. if the mm->context has started a new | ||
246 | * cycle since mm was last active. However, this is just a performance | ||
247 | * issue: all that happens is that we invalidate lines belonging to | ||
248 | * another mm, so the owning process has to refill them when that mm goes | ||
249 | * live again. mm itself can't have any cache entries because there will | ||
250 | * have been a flush_cache_all when the new mm->context cycle started. | ||
251 | */ | ||
252 | static void sh64_icache_inv_user_small_range(struct mm_struct *mm, | ||
253 | unsigned long start, int len) | ||
254 | { | ||
255 | unsigned long long eaddr = start; | ||
256 | unsigned long long eaddr_end = start + len; | ||
257 | unsigned long current_asid, mm_asid; | ||
258 | unsigned long flags; | ||
259 | unsigned long long epage_start; | ||
260 | |||
261 | /* | ||
262 | * Align to start of cache line. Otherwise, suppose len==8 and | ||
263 | * start was at 32N+28 : the last 4 bytes wouldn't get invalidated. | ||
264 | */ | ||
265 | eaddr = L1_CACHE_ALIGN(start); | ||
266 | eaddr_end = start + len; | ||
267 | |||
268 | mm_asid = cpu_asid(smp_processor_id(), mm); | ||
269 | local_irq_save(flags); | ||
270 | current_asid = switch_and_save_asid(mm_asid); | ||
271 | |||
272 | epage_start = eaddr & PAGE_MASK; | ||
273 | |||
274 | while (eaddr < eaddr_end) { | ||
275 | __asm__ __volatile__("icbi %0, 0" : : "r" (eaddr)); | ||
276 | eaddr += L1_CACHE_BYTES; | ||
277 | } | ||
278 | switch_and_save_asid(current_asid); | ||
279 | local_irq_restore(flags); | ||
280 | } | ||
281 | |||
282 | static void sh64_icache_inv_current_user_range(unsigned long start, unsigned long end) | 213 | static void sh64_icache_inv_current_user_range(unsigned long start, unsigned long end) |
283 | { | 214 | { |
284 | /* The icbi instruction never raises ITLBMISS. i.e. if there's not a | 215 | /* The icbi instruction never raises ITLBMISS. i.e. if there's not a |
@@ -564,7 +495,7 @@ static void sh64_dcache_purge_user_range(struct mm_struct *mm, | |||
564 | * Invalidate the entire contents of both caches, after writing back to | 495 | * Invalidate the entire contents of both caches, after writing back to |
565 | * memory any dirty data from the D-cache. | 496 | * memory any dirty data from the D-cache. |
566 | */ | 497 | */ |
567 | void flush_cache_all(void) | 498 | static void sh5_flush_cache_all(void) |
568 | { | 499 | { |
569 | sh64_dcache_purge_all(); | 500 | sh64_dcache_purge_all(); |
570 | sh64_icache_inv_all(); | 501 | sh64_icache_inv_all(); |
@@ -591,7 +522,7 @@ void flush_cache_all(void) | |||
591 | * I-cache. This is similar to the lack of action needed in | 522 | * I-cache. This is similar to the lack of action needed in |
592 | * flush_tlb_mm - see fault.c. | 523 | * flush_tlb_mm - see fault.c. |
593 | */ | 524 | */ |
594 | void flush_cache_mm(struct mm_struct *mm) | 525 | static void sh5_flush_cache_mm(struct mm_struct *mm) |
595 | { | 526 | { |
596 | sh64_dcache_purge_all(); | 527 | sh64_dcache_purge_all(); |
597 | } | 528 | } |
@@ -603,8 +534,8 @@ void flush_cache_mm(struct mm_struct *mm) | |||
603 | * | 534 | * |
604 | * Note, 'end' is 1 byte beyond the end of the range to flush. | 535 | * Note, 'end' is 1 byte beyond the end of the range to flush. |
605 | */ | 536 | */ |
606 | void flush_cache_range(struct vm_area_struct *vma, unsigned long start, | 537 | static void sh5_flush_cache_range(struct vm_area_struct *vma, |
607 | unsigned long end) | 538 | unsigned long start, unsigned long end) |
608 | { | 539 | { |
609 | struct mm_struct *mm = vma->vm_mm; | 540 | struct mm_struct *mm = vma->vm_mm; |
610 | 541 | ||
@@ -621,8 +552,8 @@ void flush_cache_range(struct vm_area_struct *vma, unsigned long start, | |||
621 | * | 552 | * |
622 | * Note, this is called with pte lock held. | 553 | * Note, this is called with pte lock held. |
623 | */ | 554 | */ |
624 | void flush_cache_page(struct vm_area_struct *vma, unsigned long eaddr, | 555 | static void sh5_flush_cache_page(struct vm_area_struct *vma, |
625 | unsigned long pfn) | 556 | unsigned long eaddr, unsigned long pfn) |
626 | { | 557 | { |
627 | sh64_dcache_purge_phy_page(pfn << PAGE_SHIFT); | 558 | sh64_dcache_purge_phy_page(pfn << PAGE_SHIFT); |
628 | 559 | ||
@@ -630,7 +561,7 @@ void flush_cache_page(struct vm_area_struct *vma, unsigned long eaddr, | |||
630 | sh64_icache_inv_user_page(vma, eaddr); | 561 | sh64_icache_inv_user_page(vma, eaddr); |
631 | } | 562 | } |
632 | 563 | ||
633 | void flush_dcache_page(struct page *page) | 564 | static void sh5_flush_dcache_page(struct page *page) |
634 | { | 565 | { |
635 | sh64_dcache_purge_phy_page(page_to_phys(page)); | 566 | sh64_dcache_purge_phy_page(page_to_phys(page)); |
636 | wmb(); | 567 | wmb(); |
@@ -644,7 +575,7 @@ void flush_dcache_page(struct page *page) | |||
644 | * mapping, therefore it's guaranteed that there no cache entries for | 575 | * mapping, therefore it's guaranteed that there no cache entries for |
645 | * the range in cache sets of the wrong colour. | 576 | * the range in cache sets of the wrong colour. |
646 | */ | 577 | */ |
647 | void flush_icache_range(unsigned long start, unsigned long end) | 578 | static void sh5_flush_icache_range(unsigned long start, unsigned long end) |
648 | { | 579 | { |
649 | __flush_purge_region((void *)start, end); | 580 | __flush_purge_region((void *)start, end); |
650 | wmb(); | 581 | wmb(); |
@@ -652,31 +583,12 @@ void flush_icache_range(unsigned long start, unsigned long end) | |||
652 | } | 583 | } |
653 | 584 | ||
654 | /* | 585 | /* |
655 | * Flush the range of user (defined by vma->vm_mm) address space starting | ||
656 | * at 'addr' for 'len' bytes from the cache. The range does not straddle | ||
657 | * a page boundary, the unique physical page containing the range is | ||
658 | * 'page'. This seems to be used mainly for invalidating an address | ||
659 | * range following a poke into the program text through the ptrace() call | ||
660 | * from another process (e.g. for BRK instruction insertion). | ||
661 | */ | ||
662 | static void flush_icache_user_range(struct vm_area_struct *vma, | ||
663 | struct page *page, unsigned long addr, int len) | ||
664 | { | ||
665 | |||
666 | sh64_dcache_purge_coloured_phy_page(page_to_phys(page), addr); | ||
667 | mb(); | ||
668 | |||
669 | if (vma->vm_flags & VM_EXEC) | ||
670 | sh64_icache_inv_user_small_range(vma->vm_mm, addr, len); | ||
671 | } | ||
672 | |||
673 | /* | ||
674 | * For the address range [start,end), write back the data from the | 586 | * For the address range [start,end), write back the data from the |
675 | * D-cache and invalidate the corresponding region of the I-cache for the | 587 | * D-cache and invalidate the corresponding region of the I-cache for the |
676 | * current process. Used to flush signal trampolines on the stack to | 588 | * current process. Used to flush signal trampolines on the stack to |
677 | * make them executable. | 589 | * make them executable. |
678 | */ | 590 | */ |
679 | void flush_cache_sigtramp(unsigned long vaddr) | 591 | static void sh5_flush_cache_sigtramp(unsigned long vaddr) |
680 | { | 592 | { |
681 | unsigned long end = vaddr + L1_CACHE_BYTES; | 593 | unsigned long end = vaddr + L1_CACHE_BYTES; |
682 | 594 | ||
@@ -685,138 +597,19 @@ void flush_cache_sigtramp(unsigned long vaddr) | |||
685 | sh64_icache_inv_current_user_range(vaddr, end); | 597 | sh64_icache_inv_current_user_range(vaddr, end); |
686 | } | 598 | } |
687 | 599 | ||
688 | #ifdef CONFIG_MMU | 600 | void __init sh5_cache_init(void) |
689 | /* | ||
690 | * These *MUST* lie in an area of virtual address space that's otherwise | ||
691 | * unused. | ||
692 | */ | ||
693 | #define UNIQUE_EADDR_START 0xe0000000UL | ||
694 | #define UNIQUE_EADDR_END 0xe8000000UL | ||
695 | |||
696 | /* | ||
697 | * Given a physical address paddr, and a user virtual address user_eaddr | ||
698 | * which will eventually be mapped to it, create a one-off kernel-private | ||
699 | * eaddr mapped to the same paddr. This is used for creating special | ||
700 | * destination pages for copy_user_page and clear_user_page. | ||
701 | */ | ||
702 | static unsigned long sh64_make_unique_eaddr(unsigned long user_eaddr, | ||
703 | unsigned long paddr) | ||
704 | { | ||
705 | static unsigned long current_pointer = UNIQUE_EADDR_START; | ||
706 | unsigned long coloured_pointer; | ||
707 | |||
708 | if (current_pointer == UNIQUE_EADDR_END) { | ||
709 | sh64_dcache_purge_all(); | ||
710 | current_pointer = UNIQUE_EADDR_START; | ||
711 | } | ||
712 | |||
713 | coloured_pointer = (current_pointer & ~CACHE_OC_SYN_MASK) | | ||
714 | (user_eaddr & CACHE_OC_SYN_MASK); | ||
715 | sh64_setup_dtlb_cache_slot(coloured_pointer, get_asid(), paddr); | ||
716 | |||
717 | current_pointer += (PAGE_SIZE << CACHE_OC_N_SYNBITS); | ||
718 | |||
719 | return coloured_pointer; | ||
720 | } | ||
721 | |||
722 | static void sh64_copy_user_page_coloured(void *to, void *from, | ||
723 | unsigned long address) | ||
724 | { | ||
725 | void *coloured_to; | ||
726 | |||
727 | /* | ||
728 | * Discard any existing cache entries of the wrong colour. These are | ||
729 | * present quite often, if the kernel has recently used the page | ||
730 | * internally, then given it up, then it's been allocated to the user. | ||
731 | */ | ||
732 | sh64_dcache_purge_coloured_phy_page(__pa(to), (unsigned long)to); | ||
733 | |||
734 | coloured_to = (void *)sh64_make_unique_eaddr(address, __pa(to)); | ||
735 | copy_page(from, coloured_to); | ||
736 | |||
737 | sh64_teardown_dtlb_cache_slot(); | ||
738 | } | ||
739 | |||
740 | static void sh64_clear_user_page_coloured(void *to, unsigned long address) | ||
741 | { | ||
742 | void *coloured_to; | ||
743 | |||
744 | /* | ||
745 | * Discard any existing kernel-originated lines of the wrong | ||
746 | * colour (as above) | ||
747 | */ | ||
748 | sh64_dcache_purge_coloured_phy_page(__pa(to), (unsigned long)to); | ||
749 | |||
750 | coloured_to = (void *)sh64_make_unique_eaddr(address, __pa(to)); | ||
751 | clear_page(coloured_to); | ||
752 | |||
753 | sh64_teardown_dtlb_cache_slot(); | ||
754 | } | ||
755 | |||
756 | /* | ||
757 | * 'from' and 'to' are kernel virtual addresses (within the superpage | ||
758 | * mapping of the physical RAM). 'address' is the user virtual address | ||
759 | * where the copy 'to' will be mapped after. This allows a custom | ||
760 | * mapping to be used to ensure that the new copy is placed in the | ||
761 | * right cache sets for the user to see it without having to bounce it | ||
762 | * out via memory. Note however : the call to flush_page_to_ram in | ||
763 | * (generic)/mm/memory.c:(break_cow) undoes all this good work in that one | ||
764 | * very important case! | ||
765 | * | ||
766 | * TBD : can we guarantee that on every call, any cache entries for | ||
767 | * 'from' are in the same colour sets as 'address' also? i.e. is this | ||
768 | * always used just to deal with COW? (I suspect not). | ||
769 | * | ||
770 | * There are two possibilities here for when the page 'from' was last accessed: | ||
771 | * - by the kernel : this is OK, no purge required. | ||
772 | * - by the/a user (e.g. for break_COW) : need to purge. | ||
773 | * | ||
774 | * If the potential user mapping at 'address' is the same colour as | ||
775 | * 'from' there is no need to purge any cache lines from the 'from' | ||
776 | * page mapped into cache sets of colour 'address'. (The copy will be | ||
777 | * accessing the page through 'from'). | ||
778 | */ | ||
779 | void copy_user_page(void *to, void *from, unsigned long address, | ||
780 | struct page *page) | ||
781 | { | 601 | { |
782 | if (((address ^ (unsigned long) from) & CACHE_OC_SYN_MASK) != 0) | 602 | flush_cache_all = sh5_flush_cache_all; |
783 | sh64_dcache_purge_coloured_phy_page(__pa(from), address); | 603 | flush_cache_mm = sh5_flush_cache_mm; |
604 | flush_cache_dup_mm = sh5_flush_cache_mm; | ||
605 | flush_cache_page = sh5_flush_cache_page; | ||
606 | flush_cache_range = sh5_flush_cache_range; | ||
607 | flush_dcache_page = sh5_flush_dcache_page; | ||
608 | flush_icache_range = sh5_flush_icache_range; | ||
609 | flush_cache_sigtramp = sh5_flush_cache_sigtramp; | ||
784 | 610 | ||
785 | if (((address ^ (unsigned long) to) & CACHE_OC_SYN_MASK) == 0) | 611 | /* Reserve a slot for dcache colouring in the DTLB */ |
786 | copy_page(to, from); | 612 | dtlb_cache_slot = sh64_get_wired_dtlb_entry(); |
787 | else | ||
788 | sh64_copy_user_page_coloured(to, from, address); | ||
789 | } | ||
790 | |||
791 | /* | ||
792 | * 'to' is a kernel virtual address (within the superpage mapping of the | ||
793 | * physical RAM). 'address' is the user virtual address where the 'to' | ||
794 | * page will be mapped after. This allows a custom mapping to be used to | ||
795 | * ensure that the new copy is placed in the right cache sets for the | ||
796 | * user to see it without having to bounce it out via memory. | ||
797 | */ | ||
798 | void clear_user_page(void *to, unsigned long address, struct page *page) | ||
799 | { | ||
800 | if (((address ^ (unsigned long) to) & CACHE_OC_SYN_MASK) == 0) | ||
801 | clear_page(to); | ||
802 | else | ||
803 | sh64_clear_user_page_coloured(to, address); | ||
804 | } | ||
805 | |||
806 | void copy_to_user_page(struct vm_area_struct *vma, struct page *page, | ||
807 | unsigned long vaddr, void *dst, const void *src, | ||
808 | unsigned long len) | ||
809 | { | ||
810 | flush_cache_page(vma, vaddr, page_to_pfn(page)); | ||
811 | memcpy(dst, src, len); | ||
812 | flush_icache_user_range(vma, page, vaddr, len); | ||
813 | } | ||
814 | 613 | ||
815 | void copy_from_user_page(struct vm_area_struct *vma, struct page *page, | 614 | sh4__flush_region_init(); |
816 | unsigned long vaddr, void *dst, const void *src, | ||
817 | unsigned long len) | ||
818 | { | ||
819 | flush_cache_page(vma, vaddr, page_to_pfn(page)); | ||
820 | memcpy(dst, src, len); | ||
821 | } | 615 | } |
822 | #endif | ||