]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - arch/arm/mach-omap2/mmu.c
be8764d17284c7f0afceb329ad2a93e00157ab30
[linux-2.6-omap-h63xx.git] / arch / arm / mach-omap2 / mmu.c
1 /*
2  * linux/arch/arm/mach-omap2/mmu.c
3  *
4  * Support for non-MPU OMAP2 MMUs.
5  *
6  * Copyright (C) 2002-2005 Nokia Corporation
7  *
8  * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
9  *        and Paul Mundt <paul.mundt@nokia.com>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24  */
25 #include <linux/types.h>
26 #include <linux/init.h>
27 #include <linux/rwsem.h>
28 #include <linux/device.h>
29 #include <linux/mm.h>
30 #include "mmu.h"
31 #include <asm/arch/mmu.h>
32 #include <asm/tlbflush.h>
33 #include <asm/io.h>
34 #include <asm/sizes.h>
35
36 static void *dspvect_page;
37 #define DSP_INIT_PAGE   0xfff000
38
39 static inline void
40 omap2_mmu_read_tlb(struct omap_mmu *mmu, struct cam_ram_regset *cr)
41 {
42         cr->cam = omap_mmu_read_reg(mmu, MMU_READ_CAM);
43         cr->ram = omap_mmu_read_reg(mmu, MMU_READ_RAM);
44 }
45
46 static inline void
47 omap2_mmu_load_tlb(struct omap_mmu *mmu, struct cam_ram_regset *cr)
48 {
49         /* Set the CAM and RAM entries */
50         omap_mmu_write_reg(mmu, cr->cam | OMAP_MMU_CAM_V, MMU_CAM);
51         omap_mmu_write_reg(mmu, cr->ram, MMU_RAM);
52 }
53
54 static void exmap_setup_iomap_page(struct omap_mmu *mmu, unsigned long phys,
55                                    unsigned long dsp_io_adr, int index)
56 {
57         unsigned long dspadr;
58         void *virt;
59         struct omap_mmu_tlb_entry tlb_ent;
60
61         dspadr = (IOMAP_VAL << 18) + (dsp_io_adr << 1);
62         virt = omap_mmu_to_virt(mmu, dspadr);
63         exmap_set_armmmu((unsigned long)virt, phys, PAGE_SIZE);
64         INIT_EXMAP_TBL_ENTRY_4KB_PRESERVED(mmu->exmap_tbl + index, NULL, virt);
65         INIT_TLB_ENTRY_4KB_ES32_PRESERVED(&tlb_ent, dspadr, phys);
66         omap_mmu_load_tlb_entry(mmu, &tlb_ent);
67 }
68
69 static void exmap_clear_iomap_page(struct omap_mmu *mmu,
70                                    unsigned long dsp_io_adr)
71 {
72         unsigned long dspadr;
73         void *virt;
74
75         dspadr = (IOMAP_VAL << 18) + (dsp_io_adr << 1);
76         virt = omap_mmu_to_virt(mmu, dspadr);
77         exmap_clear_armmmu((unsigned long)virt, PAGE_SIZE);
78         /* DSP MMU is shutting down. not handled here. */
79 }
80
81 #define OMAP24XX_MAILBOX_BASE   (L4_24XX_BASE + 0x94000)
82 #define OMAP2420_GPT5_BASE      (L4_24XX_BASE + 0x7c000)
83 #define OMAP2420_GPT6_BASE      (L4_24XX_BASE + 0x7e000)
84 #define OMAP2420_GPT7_BASE      (L4_24XX_BASE + 0x80000)
85 #define OMAP2420_GPT8_BASE      (L4_24XX_BASE + 0x82000)
86 #define OMAP24XX_EAC_BASE       (L4_24XX_BASE + 0x90000)
87 #define OMAP24XX_STI_BASE       (L4_24XX_BASE + 0x68000)
88 #define OMAP24XX_STI_CH_BASE    (L4_24XX_BASE + 0x0c000000)
89
90 static int exmap_setup_preserved_entries(struct omap_mmu *mmu)
91 {
92         int i, n = 0;
93
94         exmap_setup_preserved_mem_page(mmu, dspvect_page, DSP_INIT_PAGE, n++);
95
96         exmap_setup_iomap_page(mmu, OMAP24XX_PRCM_BASE, 0x7000, n++);
97         exmap_setup_iomap_page(mmu, OMAP24XX_MAILBOX_BASE, 0x11000, n++);
98
99         if (cpu_is_omap2420()) {
100                 exmap_setup_iomap_page(mmu, OMAP2420_GPT5_BASE, 0xe000, n++);
101                 exmap_setup_iomap_page(mmu, OMAP2420_GPT6_BASE, 0xe800, n++);
102                 exmap_setup_iomap_page(mmu, OMAP2420_GPT7_BASE, 0xf000, n++);
103                 exmap_setup_iomap_page(mmu, OMAP2420_GPT8_BASE, 0xf800, n++);
104                 exmap_setup_iomap_page(mmu, OMAP24XX_EAC_BASE,  0x10000, n++);
105                 exmap_setup_iomap_page(mmu, OMAP24XX_STI_BASE, 0xc800, n++);
106                 for (i = 0; i < 5; i++)
107                         exmap_setup_preserved_mem_page(mmu,
108                                 __va(OMAP24XX_STI_CH_BASE + i*SZ_4K),
109                                 0xfb0000 + i*SZ_4K, n++);
110         }
111
112         return n;
113 }
114
115 static void exmap_clear_preserved_entries(struct omap_mmu *mmu)
116 {
117         int i;
118
119         exmap_clear_iomap_page(mmu, 0x7000);    /* PRCM registers */
120         exmap_clear_iomap_page(mmu, 0x11000);   /* MAILBOX registers */
121
122         if (cpu_is_omap2420()) {
123                 exmap_clear_iomap_page(mmu, 0xe000);    /* GPT5 */
124                 exmap_clear_iomap_page(mmu, 0xe800);    /* GPT6 */
125                 exmap_clear_iomap_page(mmu, 0xf000);    /* GPT7 */
126                 exmap_clear_iomap_page(mmu, 0xf800);    /* GPT8 */
127                 exmap_clear_iomap_page(mmu, 0x10000);   /* EAC */
128                 exmap_clear_iomap_page(mmu, 0xc800);    /* STI */
129                 for (i = 0; i < 5; i++)                 /* STI CH */
130                         exmap_clear_mem_page(mmu, 0xfb0000 + i*SZ_4K);
131         }
132
133         exmap_clear_mem_page(mmu, DSP_INIT_PAGE);
134 }
135
136 #define MMU_IRQ_MASK \
137         (OMAP_MMU_IRQ_MULTIHITFAULT | \
138          OMAP_MMU_IRQ_TABLEWALKFAULT | \
139          OMAP_MMU_IRQ_EMUMISS | \
140          OMAP_MMU_IRQ_TRANSLATIONFAULT | \
141          OMAP_MMU_IRQ_TLBMISS)
142
143 static int omap2_mmu_startup(struct omap_mmu *mmu)
144 {
145         dspvect_page = (void *)__get_dma_pages(GFP_KERNEL, 0);
146         if (dspvect_page == NULL) {
147                 printk(KERN_ERR "MMU: failed to allocate memory "
148                                 "for dsp vector table\n");
149                 return -ENOMEM;
150         }
151
152         mmu->nr_exmap_preserved = exmap_setup_preserved_entries(mmu);
153
154         omap_mmu_write_reg(mmu, MMU_IRQ_MASK, MMU_IRQENABLE);
155
156         return 0;
157 }
158
159 static void omap2_mmu_shutdown(struct omap_mmu *mmu)
160 {
161         exmap_clear_preserved_entries(mmu);
162
163         if (dspvect_page != NULL) {
164                 unsigned long virt;
165
166                 down_read(&mmu->exmap_sem);
167
168                 virt = (unsigned long)omap_mmu_to_virt(mmu, DSP_INIT_PAGE);
169                 flush_tlb_kernel_range(virt, virt + PAGE_SIZE);
170                 free_page((unsigned long)dspvect_page);
171                 dspvect_page = NULL;
172
173                 up_read(&mmu->exmap_sem);
174         }
175 }
176
177 static ssize_t omap2_mmu_show(struct omap_mmu *mmu, char *buf,
178                               struct omap_mmu_tlb_lock *tlb_lock)
179 {
180         int i, len;
181
182         len = sprintf(buf, "P: preserved, V: valid\n"
183                            "B: big endian, L:little endian, "
184                            "M: mixed page attribute\n"
185                            "ety P V size   cam_va     ram_pa E ES M\n");
186                          /* 00: P V  4KB 0x300000 0x10171800 B 16 M */
187
188         for (i = 0; i < mmu->nr_tlb_entries; i++) {
189                 struct omap_mmu_tlb_entry ent;
190                 struct cam_ram_regset cr;
191                 struct omap_mmu_tlb_lock entry_lock;
192                 char *pgsz_str, *elsz_str;
193
194                 /* read a TLB entry */
195                 entry_lock.base   = tlb_lock->base;
196                 entry_lock.victim = i;
197                 omap_mmu_read_tlb(mmu, &entry_lock, &cr);
198
199                 ent.pgsz   = cr.cam & OMAP_MMU_CAM_PAGESIZE_MASK;
200                 ent.prsvd  = cr.cam & OMAP_MMU_CAM_P;
201                 ent.valid  = cr.cam & OMAP_MMU_CAM_V;
202                 ent.va     = cr.cam & OMAP_MMU_CAM_VATAG_MASK;
203                 ent.endian = cr.ram & OMAP_MMU_RAM_ENDIANNESS;
204                 ent.elsz   = cr.ram & OMAP_MMU_RAM_ELEMENTSIZE_MASK;
205                 ent.pa     = cr.ram & OMAP_MMU_RAM_PADDR_MASK;
206                 ent.mixed  = cr.ram & OMAP_MMU_RAM_MIXED;
207
208                 pgsz_str = (ent.pgsz == OMAP_MMU_CAM_PAGESIZE_16MB) ? "64MB":
209                            (ent.pgsz == OMAP_MMU_CAM_PAGESIZE_1MB)  ? " 1MB":
210                            (ent.pgsz == OMAP_MMU_CAM_PAGESIZE_64KB) ? "64KB":
211                            (ent.pgsz == OMAP_MMU_CAM_PAGESIZE_4KB)  ? " 4KB":
212                                                                      " ???";
213                 elsz_str = (ent.elsz == OMAP_MMU_RAM_ELEMENTSIZE_8)  ? " 8":
214                            (ent.elsz == OMAP_MMU_RAM_ELEMENTSIZE_16) ? "16":
215                            (ent.elsz == OMAP_MMU_RAM_ELEMENTSIZE_32) ? "32":
216                                                                       "??";
217
218                 if (i == tlb_lock->base)
219                         len += sprintf(buf + len, "lock base = %d\n",
220                                        tlb_lock->base);
221                 if (i == tlb_lock->victim)
222                         len += sprintf(buf + len, "victim    = %d\n",
223                                        tlb_lock->victim);
224
225                 len += sprintf(buf + len,
226                                /* 00: P V  4KB 0x300000 0x10171800 B 16 M */
227                                "%02d: %c %c %s 0x%06lx 0x%08lx %c %s %c\n",
228                                i,
229                                ent.prsvd ? 'P' : ' ',
230                                ent.valid ? 'V' : ' ',
231                                pgsz_str, ent.va, ent.pa,
232                                ent.endian ? 'B' : 'L',
233                                elsz_str,
234                                ent.mixed ? 'M' : ' ');
235         }
236
237         return len;
238 }
239
240 #define get_cam_va_mask(pgsz) \
241         (((pgsz) == OMAP_MMU_CAM_PAGESIZE_16MB) ? 0xff000000 : \
242          ((pgsz) == OMAP_MMU_CAM_PAGESIZE_1MB)  ? 0xfff00000 : \
243          ((pgsz) == OMAP_MMU_CAM_PAGESIZE_64KB) ? 0xffff0000 : \
244          ((pgsz) == OMAP_MMU_CAM_PAGESIZE_4KB)  ? 0xfffff000 : 0)
245
246 static inline unsigned long omap2_mmu_cam_va(struct cam_ram_regset *cr)
247 {
248         unsigned int page_size = cr->cam & OMAP_MMU_CAM_PAGESIZE_MASK;
249         unsigned int mask = get_cam_va_mask(cr->cam & page_size);
250
251         return cr->cam & mask;
252 }
253
254 static struct cam_ram_regset *
255 omap2_mmu_cam_ram_alloc(struct omap_mmu_tlb_entry *entry)
256 {
257         struct cam_ram_regset *cr;
258
259         if (entry->va & ~(get_cam_va_mask(entry->pgsz))) {
260                 printk(KERN_ERR "MMU: mapping vadr (0x%06lx) is not on an "
261                        "aligned boundary\n", entry->va);
262                 return ERR_PTR(-EINVAL);
263         }
264
265         cr = kmalloc(sizeof(struct cam_ram_regset), GFP_KERNEL);
266
267         cr->cam = (entry->va & OMAP_MMU_CAM_VATAG_MASK) |
268                   entry->prsvd | entry->pgsz;
269         cr->ram = entry->pa | entry->endian | entry->elsz;
270
271         return cr;
272 }
273
274 static inline int omap2_mmu_cam_ram_valid(struct cam_ram_regset *cr)
275 {
276         return cr->cam & OMAP_MMU_CAM_V;
277 }
278
279 struct omap_mmu_ops omap2_mmu_ops = {
280         .startup        = omap2_mmu_startup,
281         .shutdown       = omap2_mmu_shutdown,
282         .read_tlb       = omap2_mmu_read_tlb,
283         .load_tlb       = omap2_mmu_load_tlb,
284         .show           = omap2_mmu_show,
285         .cam_va         = omap2_mmu_cam_va,
286         .cam_ram_alloc  = omap2_mmu_cam_ram_alloc,
287         .cam_ram_valid  = omap2_mmu_cam_ram_valid,
288 };
289 EXPORT_SYMBOL_GPL(omap2_mmu_ops);
290
291 MODULE_LICENSE("GPL");