add place-holder directory for the a3000 wd533c93 scsi controller implementation.
[AROS.git] / arch / ppc-sam440 / kernel / mmu.c
blob3e604f68b11eeee12fe794ddce84dbefc627609f
1 /*
2 Copyright © 1995-2014, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 #include <aros/debug.h>
7 #include <asm/amcc440.h>
8 #include <asm/io.h>
9 #include <aros/kernel.h>
11 #include "kernel_intern.h"
13 struct tlb_info {
14 uint32_t bitmap[2];
15 int free;
18 /* Alloc TLB in the bitmap. Returns -1 if the allocation cannot be done */
19 static int alloc_tlb(struct tlb_info *info)
21 /* It should be done in locked state only! */
23 int bit = __builtin_clz(info->bitmap[0]);
24 if (bit == 32)
26 bit += __builtin_clz(info->bitmap[1]);
28 else
30 info->bitmap[0] &= ~(0x80000000 >> bit);
32 if (bit == 64)
34 return -1;
36 else
38 info->bitmap[1] &= ~(0x80000000 >> (bit-32));
40 info->free--;
41 return bit;
44 static void free_tlb(struct tlb_info *info, int entry)
46 if (entry >=0 && entry < 32)
48 asm volatile("tlbwe %0,%1,0;" ::"r"(0), "r"(entry));
49 if (info->bitmap[0] & (0x80000000 >> entry))
51 D(bug("[KRN] Freeing already free TLB!!!\n"));
53 else
55 info->bitmap[0] |= (0x80000000 >> entry);
56 info->free++;
59 else if (entry < 64)
61 asm volatile("tlbwe %0,%1,0;" ::"r"(0), "r"(entry));
62 entry -= 32;
63 if (info->bitmap[1] & (0x80000000 >> entry))
65 D(bug("[KRN] Freeing already free TLB!!!\n"));
67 else
69 info->bitmap[1] |= (0x80000000 >> entry);
70 info->free++;
73 else
75 D(bug("[KRN] Wrong TLB\n"));
76 return;
80 static struct mmu_page_size {
81 uint8_t code;
82 uintptr_t mask;
83 } allowable_pages[] = {
84 { 0x90, 0x0fffffff }, /* 256MB */
85 { 0x70, 0x00ffffff }, /* 16MB */
86 { 0x50, 0x000fffff }, /* 1MB */
87 { 0x40, 0x0003ffff }, /* 256KB */
88 { 0x30, 0x0000ffff }, /* 64KB */
89 { 0x20, 0x00003fff }, /* 16KB */
90 { 0x10, 0x00000fff }, /* 4KB */
91 { 0x00, 0x000003ff }, /* 1KB */
92 { 0xff, 0xffffffff }, /* END MARKER */
95 void map_region(struct tlb_info *info, uint8_t extra, uintptr_t physbase, uintptr_t virtbase, uintptr_t length, uint32_t prot)
97 long tlb_temp = info->free;
99 D(bug("[KRN] map_region(%08x, %08x, %08x, %04x): ", physbase, virtbase, length, prot));
101 /* While there is still something to map */
102 while (length)
104 int tlb;
105 int i = 0;
107 /* Check all available page sizes and try to match the best (the biggest) usable TLB entry */
108 while (allowable_pages[i].code != 0xff)
110 if ((length > allowable_pages[i].mask) && !(physbase & allowable_pages[i].mask) && !(virtbase & allowable_pages[i].mask))
111 break;
112 i++;
115 if (allowable_pages[i].code == 0xff)
117 D(bug("\n[KRN] map_region failed\n"));
118 return;
121 /* get free TLB */
122 tlb = alloc_tlb(info);
123 if (tlb == -1)
125 D(bug("\n[KRN] map_region: No more free TLB entries\n"));
126 return;
129 //D(bug("\n[KRN] TLB%02x: %08x - %08x : %08x - %08x: ", tlb,
130 //physbase, physbase + allowable_pages[i].mask,
131 //virtbase, virtbase + allowable_pages[i].mask));
133 /* Do really write to the tlb */
134 asm volatile("tlbwe %0,%3,0; tlbwe %1,%3,1; tlbwe %2,%3,2"
135 ::"r"(virtbase | allowable_pages[i].code | TLB_V), "r"(physbase | extra), "r"(prot), "r"(tlb));
136 //D(bug("%08x %08x %08x ", virtbase | allowable_pages[i].code | 0x200, physbase, prot));
138 length -= allowable_pages[i].mask + 1;
139 physbase += allowable_pages[i].mask + 1;
140 virtbase += allowable_pages[i].mask + 1;
142 tlb_temp -= info->free;
143 D(bug("%2d TLB%s\n", tlb_temp, tlb_temp > 1 ? "s":""));
146 static void free_remaining(struct tlb_info *info)
148 int tlb;
150 if (info->free == 0)
151 return;
153 tlb = alloc_tlb(info);
154 D(bug("[KRN] TLB%02x: Clear\n", tlb));
155 free_remaining(info);
156 free_tlb(info, tlb);
159 static void copy_tlb_entry(int from, int to)
161 asm volatile("tlbre %%r0,%0,0\n"
162 "tlbwe %%r0,%1,0\n"
163 "tlbre %%r0,%0,1\n"
164 "tlbwe %%r0,%1,1\n"
165 "tlbre %%r0,%0,2\n"
166 "tlbwe %%r0,%1,2\n"
167 "isync"
168 ::"r"(from), "r"(to):"r0");
171 #if DEBUG
172 struct tlb { int tlb; uint32_t reg[3]; };
174 static void tlb_dump_entry(const struct tlb *tlb)
176 uint32_t tlb_0, tlb_1, tlb_2;
177 uint32_t phys, virt, size;
179 tlb_0 = tlb->reg[0];
180 tlb_1 = tlb->reg[1];
181 tlb_2 = tlb->reg[2];
183 if (!(tlb_0 & TLB_V))
184 return;
186 size = 1024 << (2 * ((tlb_0 >> 4) & 0xf));
187 virt = tlb_0 & ~((1 << 10)-1);
188 phys = tlb_1 & ~((1 << 10)-1);
190 D(bug("[KRN] TLB%02x: ",tlb->tlb));
191 D(bug("%c%c%c%c%c%c%c%c%c%c%c ",
192 (tlb_2 & TLB_W) ? 'W' : '-',
193 (tlb_2 & TLB_I) ? 'I' : '-',
194 (tlb_2 & TLB_M) ? 'M' : '-',
195 (tlb_2 & TLB_G) ? 'G' : '-',
196 (tlb_2 & TLB_E) ? 'E' : '-',
197 (tlb_2 & TLB_UR) ? 'r' : '-',
198 (tlb_2 & TLB_UW) ? 'w' : '-',
199 (tlb_2 & TLB_UX) ? 'x' : '-',
200 (tlb_2 & TLB_SR) ? 'r' : '-',
201 (tlb_2 & TLB_SW) ? 'w' : '-',
202 (tlb_2 & TLB_SX) ? 'x' : '-'));
203 D(bug("%08x - %08x : %08x: 0:%08x 1:%08x 2:%08x\n",
204 virt, virt + size - 1, phys,
205 tlb_0, tlb_1, tlb_2));
208 static int tlbcmp(const void *a, const void *b)
210 const struct tlb *ta =a , *tb = b;
212 if (ta->reg[0] < tb->reg[0])
213 return -1;
214 if (ta->reg[0] == tb->reg[0])
215 return 0;
216 return 1;
219 #include <stdlib.h>
221 static void tlb_dump(void)
223 int tlb;
224 struct tlb tlbs[64];
225 D(static int some_bss);
226 D(static int some_data=0xdeadcafe);
228 D(bug("[KRN] Executing at %p, stack at %p, bss at %p, data at %p\n", __builtin_return_address(0), __builtin_frame_address(0), &some_bss, &some_data));
229 for (tlb = 0; tlb < 64; tlb++) {
230 asm volatile("tlbre %0,%3,0; tlbre %1,%3,1; tlbre %2,%3,2"
231 :"=r" (tlbs[tlb].reg[0]),
232 "=r" (tlbs[tlb].reg[1]),
233 "=r" (tlbs[tlb].reg[2])
234 :"r"(tlb));
235 tlbs[tlb].tlb = tlb;
238 qsort(tlbs, 64, sizeof(tlbs[0]), tlbcmp);
240 for (tlb = 0; tlb < 64; tlb++)
241 tlb_dump_entry(&tlbs[tlb]);
243 #endif
245 void mmu_init(struct TagItem *tags)
247 uintptr_t krn_lowest = krnGetTagData(KRN_KernelLowest, 0, tags);
248 uintptr_t krn_highest = krnGetTagData(KRN_KernelHighest, 0, tags);
249 uintptr_t krn_base = krnGetTagData(KRN_KernelBase, 0, tags);
250 struct tlb_info info = {
251 .bitmap = { ~0, ~0 },
252 .free = 64
255 uint32_t pvr = rdspr(PVR);
257 D(bug("[KRN] MMU Init\n"));
258 D(bug("[KRN] lowest = %p, base = %p, highest = %p\n", krn_lowest, krn_base, krn_highest));
259 D(bug("[KRN] Kernel size: %dKB code, %dKB data\n", (krn_highest - krn_base)/1024, (krn_base - krn_lowest)/1024));
261 D(tlb_dump());
263 /* Before we overwrite TLB entries make sure we have a valid entry for this code we are executing
264 * now otherwise we may get exceptions before the appropriate entries are set up. To avoid this
265 * copy the current entry to the end so by the time we overwrite it the first entries cover us */
266 copy_tlb_entry(0, 31);
267 #if DEBUG
268 /* Also make sure we have an entry to access the serial port if we are debugging */
269 copy_tlb_entry(13, 63);
270 #endif
273 * In order to reduce the usage of TLB entries, align the kernel regions.
274 * It wastes a tiny bit of RAM but saves a lot of TLB entries.
277 /* 4K granularity for data sections */
278 krn_lowest &= 0xfffff000;
279 /* 64K granularity for code sections */
280 krn_highest = (krn_highest + 0xffff) & 0xffff0000;
282 /* MEMORY
284 * Please note the 'TLB_W' flag - this is needed to support
285 * the AmigaOS semantic of the CacheClearE() routines,
286 * which appear to assume Write-Through caching.
290 * The very first entry has to cover the executable part of kernel,
291 * where exception handlers are located
293 map_region(&info, 0x0, krn_base, 0xff000000 + krn_base, krn_highest - krn_base, TLB_SR | TLB_SX | TLB_SW | TLB_UR | TLB_UX | TLB_W);
294 /* Now the data area for kernel. Make it read/write for both user and supervisor. No execution allowed */
295 map_region(&info, 0x0, krn_lowest, 0xff000000 + krn_lowest, krn_base - krn_lowest, TLB_SR | TLB_SW | TLB_UR | TLB_UW | TLB_W);
296 /* The low memory will be RW for the supervisor mode, RO from user */
297 map_region(&info, 0x0, 0, 0xff000000, krn_lowest, TLB_SR | TLB_SW | TLB_UR | TLB_W);
299 /* The regular RAM, make 1GB of it - amcc440 cannot do more. */
300 map_region(&info, 0x0, krn_highest, krn_highest, 0x40000000 - krn_highest, TLB_SR | TLB_SW | TLB_UR | TLB_UW | TLB_SX | TLB_UX | TLB_W);
302 /* PERIPHERALS
304 * Note the 'TLB_G | TLB_I' flags - this is need to prevent caching, and
305 * ensure that all operations to peripheral address space is strictly
306 * ordered.
309 if (krnIsPPC440(pvr)) {
310 D(bug("[KRN] MMU: Configure for PPC440\n"));
311 /* map some 440EP peripherials bus */
312 map_region(&info, 0x0, 0x80000000, 0x80000000, 0x20000000, TLB_SR | TLB_SW | TLB_UR | TLB_UW | TLB_G | TLB_I );
313 /* map the PCI bus */
314 map_region(&info, 0x0, 0xa0000000, 0xa0000000, 0x40000000, TLB_SR | TLB_SW | TLB_UR | TLB_UW | TLB_G | TLB_I );
315 /* PCI control registers and onboard devices */
316 map_region(&info, 0x0, 0xe0000000, 0xe0000000, 0x10000000, TLB_SR | TLB_SW | TLB_UR | TLB_UW | TLB_G | TLB_I);
317 } else if (krnIsPPC460(pvr)) {
318 D(bug("[KRN] MMU: Configure for PPC460\n"));
319 /* PCI Memory 0x80000000-0xa0000000 */
320 map_region(&info, 0xc, 0x80000000, 0x80000000, 0x20000000, TLB_SR | TLB_SW | TLB_UR | TLB_UW | TLB_G | TLB_I );
321 /* PCI IO/Control 0xe8000000-0xef000000 */
322 map_region(&info, 0xc, 0x08000000, 0xe8000000, 0x07000000, TLB_SR | TLB_SW | TLB_UR | TLB_UW | TLB_G | TLB_I);
323 /* USB, SATA 0xef000000-0xef010000 */
324 map_region(&info, 0x4, 0xbffd0000, 0xef000000, 0x00010000, TLB_SR | TLB_SW | TLB_UR | TLB_UW | TLB_G | TLB_I);
325 /* UART, GPT, ZMII, EMAC 0xef600000-0xef610000 */
326 map_region(&info, 0x4, 0xef600000, 0xef600000, 0x00010000, TLB_SR | TLB_SW | TLB_UR | TLB_UW | TLB_G | TLB_I);
327 } else {
328 bug("[KRN] MMU: Cannot configure - unknown PVR model 0x%08x\n", pvr);
329 for(;;);
332 free_remaining(&info);
333 D(tlb_dump());
335 D(bug("[KRN] TLB status: %d used, %d free\n", 64 - info.free, info.free));
337 /* flush TLB shadow regs */
338 asm volatile("isync;");