Commit | Line | Data |
---|---|---|
a372acfa CL |
1 | /* |
2 | * This file contains the routines for initializing the MMU | |
3 | * on the 8xx series of chips. | |
4 | * -- christophe | |
5 | * | |
6 | * Derived from arch/powerpc/mm/40x_mmu.c: | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public License | |
10 | * as published by the Free Software Foundation; either version | |
11 | * 2 of the License, or (at your option) any later version. | |
12 | * | |
13 | */ | |
14 | ||
15 | #include <linux/memblock.h> | |
4badd43a CL |
16 | #include <asm/fixmap.h> |
17 | #include <asm/code-patching.h> | |
a372acfa CL |
18 | |
19 | #include "mmu_decl.h" | |
20 | ||
4badd43a CL |
21 | #define IMMR_SIZE (FIX_IMMR_SIZE << PAGE_SHIFT) |
22 | ||
a372acfa | 23 | extern int __map_without_ltlbs; |
4badd43a CL |
24 | |
25 | /* | |
26 | * Return PA for this VA if it is in IMMR area, or 0 | |
27 | */ | |
28 | phys_addr_t v_block_mapped(unsigned long va) | |
29 | { | |
30 | unsigned long p = PHYS_IMMR_BASE; | |
31 | ||
32 | if (__map_without_ltlbs) | |
33 | return 0; | |
34 | if (va >= VIRT_IMMR_BASE && va < VIRT_IMMR_BASE + IMMR_SIZE) | |
35 | return p + va - VIRT_IMMR_BASE; | |
36 | return 0; | |
37 | } | |
38 | ||
39 | /* | |
40 | * Return VA for a given PA or 0 if not mapped | |
41 | */ | |
42 | unsigned long p_block_mapped(phys_addr_t pa) | |
43 | { | |
44 | unsigned long p = PHYS_IMMR_BASE; | |
45 | ||
46 | if (__map_without_ltlbs) | |
47 | return 0; | |
48 | if (pa >= p && pa < p + IMMR_SIZE) | |
49 | return VIRT_IMMR_BASE + pa - p; | |
50 | return 0; | |
51 | } | |
52 | ||
4ad27450 CL |
53 | #define LARGE_PAGE_SIZE_8M (1<<23) |
54 | ||
a372acfa CL |
55 | /* |
56 | * MMU_init_hw does the chip-specific initialization of the MMU hardware. | |
57 | */ | |
58 | void __init MMU_init_hw(void) | |
59 | { | |
4ad27450 CL |
60 | /* PIN up to the 3 first 8Mb after IMMR in DTLB table */ |
61 | #ifdef CONFIG_PIN_TLB | |
62 | unsigned long ctr = mfspr(SPRN_MD_CTR) & 0xfe000000; | |
63 | unsigned long flags = 0xf0 | MD_SPS16K | _PAGE_SHARED | _PAGE_DIRTY; | |
62f64b49 CL |
64 | #ifdef CONFIG_PIN_TLB_IMMR |
65 | int i = 29; | |
66 | #else | |
67 | int i = 28; | |
68 | #endif | |
4ad27450 CL |
69 | unsigned long addr = 0; |
70 | unsigned long mem = total_lowmem; | |
71 | ||
62f64b49 | 72 | for (; i < 32 && mem >= LARGE_PAGE_SIZE_8M; i++) { |
4ad27450 CL |
73 | mtspr(SPRN_MD_CTR, ctr | (i << 8)); |
74 | mtspr(SPRN_MD_EPN, (unsigned long)__va(addr) | MD_EVALID); | |
75 | mtspr(SPRN_MD_TWC, MD_PS8MEG | MD_SVALID); | |
76 | mtspr(SPRN_MD_RPN, addr | flags | _PAGE_PRESENT); | |
77 | addr += LARGE_PAGE_SIZE_8M; | |
78 | mem -= LARGE_PAGE_SIZE_8M; | |
79 | } | |
80 | #endif | |
a372acfa CL |
81 | } |
82 | ||
4badd43a CL |
83 | static void mmu_mapin_immr(void) |
84 | { | |
85 | unsigned long p = PHYS_IMMR_BASE; | |
86 | unsigned long v = VIRT_IMMR_BASE; | |
87 | unsigned long f = pgprot_val(PAGE_KERNEL_NCG); | |
88 | int offset; | |
89 | ||
90 | for (offset = 0; offset < IMMR_SIZE; offset += PAGE_SIZE) | |
91 | map_page(v + offset, p + offset, f); | |
92 | } | |
93 | ||
94 | /* Address of instructions to patch */ | |
62f64b49 | 95 | #ifndef CONFIG_PIN_TLB_IMMR |
4badd43a CL |
96 | extern unsigned int DTLBMiss_jmp; |
97 | #endif | |
bb7f3808 | 98 | extern unsigned int DTLBMiss_cmp, FixupDAR_cmp; |
4badd43a | 99 | |
bb7f3808 | 100 | void mmu_patch_cmp_limit(unsigned int *addr, unsigned long mapped) |
a372acfa | 101 | { |
bb7f3808 | 102 | unsigned int instr = *addr; |
a372acfa | 103 | |
bb7f3808 CL |
104 | instr &= 0xffff0000; |
105 | instr |= (unsigned long)__va(mapped) >> 16; | |
106 | patch_instruction(addr, instr); | |
107 | } | |
108 | ||
109 | unsigned long __init mmu_mapin_ram(unsigned long top) | |
110 | { | |
111 | unsigned long mapped; | |
a372acfa | 112 | |
4badd43a | 113 | if (__map_without_ltlbs) { |
bb7f3808 | 114 | mapped = 0; |
4badd43a | 115 | mmu_mapin_immr(); |
62f64b49 | 116 | #ifndef CONFIG_PIN_TLB_IMMR |
4badd43a CL |
117 | patch_instruction(&DTLBMiss_jmp, PPC_INST_NOP); |
118 | #endif | |
bb7f3808 CL |
119 | } else { |
120 | mapped = top & ~(LARGE_PAGE_SIZE_8M - 1); | |
a372acfa | 121 | } |
a372acfa | 122 | |
bb7f3808 CL |
123 | mmu_patch_cmp_limit(&DTLBMiss_cmp, mapped); |
124 | mmu_patch_cmp_limit(&FixupDAR_cmp, mapped); | |
a372acfa CL |
125 | |
126 | /* If the size of RAM is not an exact power of two, we may not | |
127 | * have covered RAM in its entirety with 8 MiB | |
128 | * pages. Consequently, restrict the top end of RAM currently | |
129 | * allocable so that calls to the MEMBLOCK to allocate PTEs for "tail" | |
130 | * coverage with normal-sized pages (or other reasons) do not | |
131 | * attempt to allocate outside the allowed range. | |
132 | */ | |
bb7f3808 CL |
133 | if (mapped) |
134 | memblock_set_current_limit(mapped); | |
a372acfa CL |
135 | |
136 | return mapped; | |
137 | } | |
516d9189 CL |
138 | |
139 | void setup_initial_memory_limit(phys_addr_t first_memblock_base, | |
140 | phys_addr_t first_memblock_size) | |
141 | { | |
142 | /* We don't currently support the first MEMBLOCK not mapping 0 | |
143 | * physical on those processors | |
144 | */ | |
145 | BUG_ON(first_memblock_base != 0); | |
146 | ||
516d9189 CL |
147 | /* 8xx can only access 24MB at the moment */ |
148 | memblock_set_current_limit(min_t(u64, first_memblock_size, 0x01800000)); | |
516d9189 | 149 | } |
a7761fe4 CL |
150 | |
151 | /* | |
152 | * Set up to use a given MMU context. | |
153 | * id is context number, pgd is PGD pointer. | |
154 | * | |
155 | * We place the physical address of the new task page directory loaded | |
156 | * into the MMU base register, and set the ASID compare register with | |
157 | * the new "context." | |
158 | */ | |
159 | void set_context(unsigned long id, pgd_t *pgd) | |
160 | { | |
161 | s16 offset = (s16)(__pa(swapper_pg_dir)); | |
162 | ||
163 | #ifdef CONFIG_BDI_SWITCH | |
164 | pgd_t **ptr = *(pgd_t ***)(KERNELBASE + 0xf0); | |
165 | ||
166 | /* Context switch the PTE pointer for the Abatron BDI2000. | |
167 | * The PGDIR is passed as second argument. | |
168 | */ | |
169 | *(ptr + 1) = pgd; | |
170 | #endif | |
171 | ||
172 | /* Register M_TW will contain base address of level 1 table minus the | |
173 | * lower part of the kernel PGDIR base address, so that all accesses to | |
174 | * level 1 table are done relative to lower part of kernel PGDIR base | |
175 | * address. | |
176 | */ | |
177 | mtspr(SPRN_M_TW, __pa(pgd) - offset); | |
178 | ||
179 | /* Update context */ | |
180 | mtspr(SPRN_M_CASID, id); | |
181 | /* sync */ | |
182 | mb(); | |
183 | } | |
766d45cb CL |
184 | |
185 | void flush_instruction_cache(void) | |
186 | { | |
187 | isync(); | |
188 | mtspr(SPRN_IC_CST, IDC_INVALL); | |
189 | isync(); | |
190 | } |