]> git.karo-electronics.de Git - mv-sheeva.git/blob - arch/mn10300/mm/cache-inv-icache.c
MN10300: Allow some cacheflushes to be avoided if cache snooping is available
[mv-sheeva.git] / arch / mn10300 / mm / cache-inv-icache.c
1 /* Invalidate icache when dcache doesn't need invalidation as it's in
2  * write-through mode
3  *
4  * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved.
5  * Written by David Howells (dhowells@redhat.com)
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public Licence
9  * as published by the Free Software Foundation; either version
10  * 2 of the Licence, or (at your option) any later version.
11  */
12 #include <linux/module.h>
13 #include <linux/mm.h>
14 #include <asm/cacheflush.h>
15
16 /**
17  * flush_icache_page_range - Flush dcache and invalidate icache for part of a
18  *                              single page
19  * @start: The starting virtual address of the page part.
20  * @end: The ending virtual address of the page part.
21  *
22  * Invalidate the icache for part of a single page, as determined by the
23  * virtual addresses given.  The page must be in the paged area.  The dcache is
24  * not flushed as the cache must be in write-through mode to get here.
25  */
26 static void flush_icache_page_range(unsigned long start, unsigned long end)
27 {
28         unsigned long addr, size, off;
29         struct page *page;
30         pgd_t *pgd;
31         pud_t *pud;
32         pmd_t *pmd;
33         pte_t *ppte, pte;
34
35         /* work out how much of the page to flush */
36         off = start & ~PAGE_MASK;
37         size = end - start;
38
39         /* get the physical address the page is mapped to from the page
40          * tables */
41         pgd = pgd_offset(current->mm, start);
42         if (!pgd || !pgd_val(*pgd))
43                 return;
44
45         pud = pud_offset(pgd, start);
46         if (!pud || !pud_val(*pud))
47                 return;
48
49         pmd = pmd_offset(pud, start);
50         if (!pmd || !pmd_val(*pmd))
51                 return;
52
53         ppte = pte_offset_map(pmd, start);
54         if (!ppte)
55                 return;
56         pte = *ppte;
57         pte_unmap(ppte);
58
59         if (pte_none(pte))
60                 return;
61
62         page = pte_page(pte);
63         if (!page)
64                 return;
65
66         addr = page_to_phys(page);
67
68         /* invalidate the icache coverage on that region */
69         mn10300_icache_inv_range2(addr + off, size);
70 }
71
72 /**
73  * flush_icache_range - Globally flush dcache and invalidate icache for region
74  * @start: The starting virtual address of the region.
75  * @end: The ending virtual address of the region.
76  *
77  * This is used by the kernel to globally flush some code it has just written
78  * from the dcache back to RAM and then to globally invalidate the icache over
79  * that region so that that code can be run on all CPUs in the system.
80  */
81 void flush_icache_range(unsigned long start, unsigned long end)
82 {
83         unsigned long start_page, end_page;
84
85         if (end > 0x80000000UL) {
86                 /* addresses above 0xa0000000 do not go through the cache */
87                 if (end > 0xa0000000UL) {
88                         end = 0xa0000000UL;
89                         if (start >= end)
90                                 return;
91                 }
92
93                 /* kernel addresses between 0x80000000 and 0x9fffffff do not
94                  * require page tables, so we just map such addresses
95                  * directly */
96                 start_page = (start >= 0x80000000UL) ? start : 0x80000000UL;
97                 mn10300_dcache_flush_range(start_page, end);
98                 mn10300_icache_inv_range(start_page, end);
99                 if (start_page == start)
100                         return;
101                 end = start_page;
102         }
103
104         start_page = start & PAGE_MASK;
105         end_page = end & PAGE_MASK;
106
107         if (start_page == end_page) {
108                 /* the first and last bytes are on the same page */
109                 flush_icache_page_range(start, end);
110         } else if (start_page + 1 == end_page) {
111                 /* split over two virtually contiguous pages */
112                 flush_icache_page_range(start, end_page);
113                 flush_icache_page_range(end_page, end);
114         } else {
115                 /* more than 2 pages; just flush the entire cache */
116                 mn10300_icache_inv();
117         }
118 }
119 EXPORT_SYMBOL(flush_icache_range);