Merge with /pub/scm/linux/kernel/git/torvalds/linux-2.6.git
[linux-2.6] / arch / parisc / mm / kmap.c
1 /* 
2  *    kmap/page table map and unmap support routines
3  *
4  *    Copyright 1999,2000 Hewlett-Packard Company
5  *    Copyright 2000 John Marvin <jsm at hp.com>
6  *    Copyright 2000 Grant Grundler <grundler at parisc-linux.org>
7  *    Copyright 2000 Philipp Rumpf <prumpf@tux.org>
8  *
9  *
10  *    This program is free software; you can redistribute it and/or modify
11  *    it under the terms of the GNU General Public License as published by
12  *    the Free Software Foundation; either version 2 of the License, or
13  *    (at your option) any later version.
14  *
15  *    This program is distributed in the hope that it will be useful,
16  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *    GNU General Public License for more details.
19  *
20  *    You should have received a copy of the GNU General Public License
21  *    along with this program; if not, write to the Free Software
22  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24 /*
25 ** Stolen mostly from arch/parisc/kernel/pci-dma.c
26 */
27
28 #include <linux/types.h>
29 #include <linux/mm.h>
30 #include <linux/string.h>
31 #include <linux/pci.h>
32
33 #include <linux/slab.h>
34 #include <linux/vmalloc.h>
35
36 #include <asm/uaccess.h>
37 #include <asm/pgalloc.h>
38
39 #include <asm/io.h>
40 #include <asm/page.h>           /* get_order */
41
42 #undef flush_cache_all
43 #define flush_cache_all flush_all_caches
44
45 typedef void (*pte_iterator_t) (pte_t * pte, unsigned long arg);
46
47 #if 0
48 /* XXX This routine could be used with iterate_page() to replace
49  * unmap_uncached_page() and save a little code space but I didn't
50  * do that since I'm not certain whether this is the right path. -PB
51  */
52 static void unmap_cached_pte(pte_t * pte, unsigned long addr, unsigned long arg)
53 {
54         pte_t page = *pte;
55         pte_clear(&init_mm, addr, pte);
56         if (!pte_none(page)) {
57                 if (pte_present(page)) {
58                         unsigned long map_nr = pte_pagenr(page);
59                         if (map_nr < max_mapnr)
60                                 __free_page(mem_map + map_nr);
61                 } else {
62                         printk(KERN_CRIT
63                                "Whee.. Swapped out page in kernel page table\n");
64                 }
65         }
66 }
67 #endif
68
69 /* These two routines should probably check a few things... */
70 static void set_uncached(pte_t * pte, unsigned long arg)
71 {
72         pte_val(*pte) |= _PAGE_NO_CACHE;
73 }
74
75 static void set_cached(pte_t * pte, unsigned long arg)
76 {
77         pte_val(*pte) &= ~_PAGE_NO_CACHE;
78 }
79
80 static inline void iterate_pte(pmd_t * pmd, unsigned long address,
81                                unsigned long size, pte_iterator_t op,
82                                unsigned long arg)
83 {
84         pte_t *pte;
85         unsigned long end;
86
87         if (pmd_none(*pmd))
88                 return;
89         if (pmd_bad(*pmd)) {
90                 pmd_ERROR(*pmd);
91                 pmd_clear(pmd);
92                 return;
93         }
94         pte = pte_offset(pmd, address);
95         address &= ~PMD_MASK;
96         end = address + size;
97         if (end > PMD_SIZE)
98                 end = PMD_SIZE;
99         do {
100                 op(pte, arg);
101                 address += PAGE_SIZE;
102                 pte++;
103         } while (address < end);
104 }
105
106 static inline void iterate_pmd(pgd_t * dir, unsigned long address,
107                                unsigned long size, pte_iterator_t op,
108                                unsigned long arg)
109 {
110         pmd_t *pmd;
111         unsigned long end;
112
113         if (pgd_none(*dir))
114                 return;
115         if (pgd_bad(*dir)) {
116                 pgd_ERROR(*dir);
117                 pgd_clear(dir);
118                 return;
119         }
120         pmd = pmd_offset(dir, address);
121         address &= ~PGDIR_MASK;
122         end = address + size;
123         if (end > PGDIR_SIZE)
124                 end = PGDIR_SIZE;
125         do {
126                 iterate_pte(pmd, address, end - address, op, arg);
127                 address = (address + PMD_SIZE) & PMD_MASK;
128                 pmd++;
129         } while (address < end);
130 }
131
132 static void iterate_pages(unsigned long address, unsigned long size,
133                           pte_iterator_t op, unsigned long arg)
134 {
135         pgd_t *dir;
136         unsigned long end = address + size;
137
138         dir = pgd_offset_k(address);
139         flush_cache_all();
140         do {
141                 iterate_pmd(dir, address, end - address, op, arg);
142                 address = (address + PGDIR_SIZE) & PGDIR_MASK;
143                 dir++;
144         } while (address && (address < end));
145         flush_tlb_all();
146 }
147
148 void
149 kernel_set_cachemode(unsigned long vaddr, unsigned long size, int what)
150 {
151         switch (what) {
152         case IOMAP_FULL_CACHING:
153                 iterate_pages(vaddr, size, set_cached, 0);
154                 flush_tlb_range(NULL, vaddr, size);
155                 break;
156         case IOMAP_NOCACHE_SER:
157                 iterate_pages(vaddr, size, set_uncached, 0);
158                 flush_tlb_range(NULL, vaddr, size);
159                 break;
160         default:
161                 printk(KERN_CRIT
162                        "kernel_set_cachemode mode %d not understood\n",
163                        what);
164                 break;
165         }
166 }