1/*
2 * Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de.
3 * Based on code written by Travis Geiselbrecht for NewOS.
4 *
5 * Distributed under the terms of the MIT License.
6 */
7
8
9#include "mmu.h"
10
11#include <boot/platform.h>
12#include <boot/stdio.h>
13#include <boot/kernel_args.h>
14#include <boot/stage2.h>
15#include <arch/cpu.h>
16#include <arch/ppc/arch_mmu_amcc440.h>
17#include <arch_kernel.h>
18#include <platform/openfirmware/openfirmware.h>
19#ifdef __ARM__
20#include <arm_mmu.h>
21#endif
22#include <kernel.h>
23
24#include <board_config.h>
25
26#include <OS.h>
27
28#include <string.h>
29
30int32 of_address_cells(int package);
31int32 of_size_cells(int package);
32
33#define TRACE_MMU
34#ifdef TRACE_MMU
35#	define TRACE(x) dprintf x
36#else
37#	define TRACE(x) ;
38#endif
39
40
41/*!	Computes the recommended minimal page table size as
42	described in table 7-22 of the PowerPC "Programming
43	Environment for 32-Bit Microprocessors".
44	The page table size ranges from 64 kB (for 8 MB RAM)
45	to 32 MB (for 4 GB RAM).
46	FIXME: account for larger TLB descriptors for Book-E
47*/
48static size_t
49suggested_page_table_size(phys_addr_t total)
50{
51	uint32 max = 23;
52		// 2^23 == 8 MB
53
54	while (max < 32) {
55		if (total <= (1UL << max))
56			break;
57
58		max++;
59	}
60
61	return 1UL << (max - 7);
62		// 2^(23 - 7) == 64 kB
63}
64
65
66static void
67read_TLB(int i, uint32 tlb[3], uint8 &pid)
68{
69	//FIXME:read pid too
70	asm volatile(
71		"tlbre %0,%3,0\n"
72		"\ttlbre %1,%3,1\n"
73		"\ttlbre %2,%3,2"
74		:   "=r"(tlb[0]),
75			"=r"(tlb[1]),
76			"=r"(tlb[2])
77		:	"r"(i)
78	);
79}
80
81
82static void
83write_TLB(int i, uint32 tlb[3], uint8 pid)
84{
85	//FIXME:write pid too
86	asm volatile(
87		"tlbwe %0,%3,0\n"
88		"\ttlbwe %1,%3,1\n"
89		"\ttlbwe %2,%3,2"
90		: : "r"(tlb[0]),
91			"r"(tlb[1]),
92			"r"(tlb[2]),
93			"r"(i)
94	);
95}
96
97
98static void
99dump_TLBs(void)
100{
101	int i;
102	for (i = 0; i < TLB_COUNT; i++) {
103		uint32 tlb[3];// = { 0, 0, 0 };
104		uint8 pid;
105		read_TLB(i, tlb, pid);
106		dprintf("TLB[%02d]: %08lx %08lx %08lx %02x\n",
107			i, tlb[0], tlb[1], tlb[2], pid);
108	}
109}
110
111
112status_t
113arch_mmu_setup_pinned_tlb_amcc440(phys_addr_t totalRam, size_t &tableSize,
114	size_t &tlbSize)
115{
116	dump_TLBs();
117	tlb_length tlbLength = TLB_LENGTH_16MB;
118//XXX:totalRam = 4LL*1024*1024*1024;
119
120	size_t suggestedTableSize = suggested_page_table_size(totalRam);
121	dprintf("suggested page table size = %" B_PRIuSIZE "\n",
122		suggestedTableSize);
123
124	tableSize = suggestedTableSize;
125
126	// add 4MB for kernel and some more for modules...
127	tlbSize = tableSize + 8 * 1024 * 1024;
128
129	// round up to realistic TLB lengths, either 16MB or 256MB
130	// the unused space will be filled with SLAB areas
131	if (tlbSize < 16 * 1024 * 1024)
132		tlbSize = 16 * 1024 * 1024;
133	else {
134		tlbSize = 256 * 1024 * 1024;
135		tlbLength = TLB_LENGTH_256MB;
136	}
137
138	uint32 tlb[3];
139	uint8 pid;
140	int i;
141
142	// Make sure the last TLB is free, else we are in trouble
143	// XXX: allow using a different TLB entry?
144	read_TLB(TLB_COUNT - 1, tlb, pid);
145	if ((tlb[0] & TLB_V) != 0) {
146		panic("Last TLB already in use. FIXME.");
147		return B_ERROR;
148	}
149
150	// TODO: remove existing mapping from U-Boot at KERNEL_BASE !!!
151	// (on Sam460ex it's pci mem)
152	// for now we just move it to AS1 which we don't use, until calling
153	// the kernel.
154	// we could probably swap it with our own KERNEL_BASE TLB to call U-Boot
155	// if required, but it'd be quite ugly.
156	for (i = 0; i < TLB_COUNT; i++) {
157		read_TLB(i, tlb, pid);
158		//dprintf("tlb[%d][0] = %08lx\n", i, tlb[0]);
159		// TODO: make the test more complete and correct
160		if ((tlb[0] & 0xfffffc00) == KERNEL_BASE) {
161			tlb[0] |= 0x100; // AS1
162			write_TLB(i, tlb, pid);
163			dprintf("Moved existing translation in TLB[%d] to AS1\n", i);
164		}
165	}
166
167	// pin the last TLB
168	//XXX:also maybe skip the FDT + initrd + loader ?
169	phys_addr_t physBase = gKernelArgs.physical_memory_range[0].start;
170	//TODO:make sure 1st range is large enough?
171	i = TLB_COUNT - 1; // last one
172	pid = 0; // the kernel's PID
173	tlb[0] = (KERNEL_BASE | tlbLength << 4 | TLB_V);
174	tlb[1] = ((physBase & 0xfffffc00) | (physBase >> 32));
175	tlb[2] = (0x0000003f); // user:RWX kernel:RWX
176	write_TLB(i, tlb, pid);
177
178	return B_OK;
179}
180
181