1/*
2 * Copyright 2004-2005, Axel D��rfler, axeld@pinc-software.de. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "cpu.h"
8#include "board_config.h"
9
10#include <OS.h>
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_cpu.h>
17#include <arch/ppc/arch_platform.h>
18#include <arch_kernel.h>
19#include <arch_system_info.h>
20#include <platform/openfirmware/devices.h>
21#include <platform/openfirmware/openfirmware.h>
22
23#include <string.h>
24
25// TODO: try to move remaining FDT code to OF calls
26extern "C" {
27#include <fdt.h>
28#include <libfdt.h>
29#include <libfdt_env.h>
30};
31
32
33extern void *gFDT;
34
35#define TRACE_CPU
36#ifdef TRACE_CPU
37#	define TRACE(x) dprintf x
38#else
39#	define TRACE(x) ;
40#endif
41
42
43// FIXME: this is ugly; introduce a cpu type in kernel args
44bool gIs440 = false;
45
46
47static status_t
48enumerate_cpus(void)
49{
50	// iterate through the "/cpus" node to find all CPUs
51	int cpus = of_finddevice("/cpus");
52	if (cpus == OF_FAILED) {
53		printf("enumerate_cpus(): Failed to open \"/cpus\"!\n");
54		return B_ERROR;
55	}
56
57	char cpuPath[256];
58	int cookie = 0;
59	int cpuCount = 0;
60	while (of_get_next_device(&cookie, cpus, "cpu", cpuPath,
61			sizeof(cpuPath)) == B_OK) {
62		TRACE(("found CPU: %s\n", cpuPath));
63
64		// For the first CPU get the frequencies of CPU, bus, and time base.
65		// We assume they are the same for all CPUs.
66		if (cpuCount == 0) {
67			int cpu = of_finddevice(cpuPath);
68			if (cpu == OF_FAILED) {
69				printf("enumerate_cpus: Failed get CPU device node!\n");
70				return B_ERROR;
71			}
72
73			// TODO: Does encode-int really encode quadlet (32 bit numbers)
74			// only?
75			int32 clockFrequency;
76			if (of_getprop(cpu, "clock-frequency", &clockFrequency, 4)
77					== OF_FAILED) {
78				printf("enumerate_cpus: Failed to get CPU clock "
79					"frequency!\n");
80				return B_ERROR;
81			}
82			int32 busFrequency = 0;
83			if (of_getprop(cpu, "bus-frequency", &busFrequency, 4)
84					== OF_FAILED) {
85				//printf("enumerate_cpus: Failed to get bus clock "
86				//	"frequency!\n");
87				//return B_ERROR;
88			}
89			int32 timeBaseFrequency;
90			if (of_getprop(cpu, "timebase-frequency", &timeBaseFrequency, 4)
91					== OF_FAILED) {
92				printf("enumerate_cpus: Failed to get time base "
93					"frequency!\n");
94				return B_ERROR;
95			}
96
97			gKernelArgs.arch_args.cpu_frequency = clockFrequency;
98			gKernelArgs.arch_args.bus_frequency = busFrequency;
99			gKernelArgs.arch_args.time_base_frequency = timeBaseFrequency;
100
101			TRACE(("  CPU clock frequency: %ld\n", clockFrequency));
102			//TRACE(("  bus clock frequency: %ld\n", busFrequency));
103			TRACE(("  time base frequency: %ld\n", timeBaseFrequency));
104		}
105
106		cpuCount++;
107	}
108
109	if (cpuCount == 0) {
110		printf("enumerate_cpus(): Found no CPUs!\n");
111		return B_ERROR;
112	}
113
114	gKernelArgs.num_cpus = cpuCount;
115
116	// FDT doesn't list bus frequency on the cpu node but on the plb node
117	if (gKernelArgs.arch_args.bus_frequency == 0) {
118		int plb = of_finddevice("/plb");
119
120		int32 busFrequency = 0;
121		if (of_getprop(plb, "clock-frequency", &busFrequency, 4)
122				== OF_FAILED) {
123			printf("enumerate_cpus: Failed to get bus clock "
124				"frequency!\n");
125			return B_ERROR;
126		}
127		gKernelArgs.arch_args.bus_frequency = busFrequency;
128	}
129	TRACE(("  bus clock frequency: %lld\n",
130		gKernelArgs.arch_args.bus_frequency));
131
132#if 0
133//XXX:Classic
134	// allocate the kernel stacks (the memory stuff is already initialized
135	// at this point)
136	addr_t stack = (addr_t)arch_mmu_allocate((void*)0x80000000,
137		cpuCount * (KERNEL_STACK_SIZE + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE),
138		B_READ_AREA | B_WRITE_AREA, false);
139	if (!stack) {
140		printf("enumerate_cpus(): Failed to allocate kernel stack(s)!\n");
141		return B_NO_MEMORY;
142	}
143
144	for (int i = 0; i < cpuCount; i++) {
145		gKernelArgs.cpu_kstack[i].start = stack;
146		gKernelArgs.cpu_kstack[i].size = KERNEL_STACK_SIZE
147			+ KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE;
148		stack += KERNEL_STACK_SIZE + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE;
149	}
150#endif
151
152	return B_OK;
153}
154
155
156static status_t
157check_cpu_features()
158{
159	uint32 msr;
160	uint32 pvr;
161	bool is_440 = false;
162	bool is_460 = false;
163	bool pvr_unknown = false;
164	bool fdt_unknown = true;
165	const char *fdt_model = NULL;
166
167	// attempt to detect processor version, either from PVR or FDT
168	// TODO:someday we'll support other things than 440/460...
169
170	// read the Processor Version Register
171	pvr = get_pvr();
172	uint16 version = (uint16)(pvr >> 16);
173	uint16 revision = (uint16)(pvr & 0xffff);
174
175	switch (version) {
176		case AMCC440EP:
177			is_440 = true;
178			break;
179		case AMCC460EX:
180			is_460 = true;
181			break;
182		default:
183			pvr_unknown = true;
184	}
185
186	// if we have an FDT...
187	// XXX: use it only as fallback?
188	if (gFDT != NULL/* && pvr_unknown*/) {
189		// TODO: for MP support we must check /chosen/cpu first
190		int node = fdt_path_offset(gFDT, "/cpus/cpu@0");
191		int len;
192
193		fdt_model = (const char *)fdt_getprop(gFDT, node, "model", &len);
194		// TODO: partial match ? "PowerPC,440" && isalpha(next char) ?
195		if (fdt_model) {
196			if (!strcmp(fdt_model, "PowerPC,440EP")) {
197				is_440 = true;
198				fdt_unknown = false;
199			} else if (!strcmp(fdt_model, "PowerPC,460EX")) {
200				is_460 = true;
201				fdt_unknown = false;
202			}
203		}
204	}
205
206	if (is_460)
207		is_440 = true;
208
209	gIs440 = is_440;
210
211	// some cpu-dependent tweaking
212
213	if (is_440) {
214		// the FPU is implemented as an Auxiliary Processing Unit,
215		// so we must enable transfers by setting the DAPUIB bit to 0
216		// (11th bit)
217		asm volatile(
218			"mfccr0 %%r3\n"
219			"\tlis %%r4,~(1<<(31-11-16))\n"
220			"\tand %%r3,%%r3,%%r4\n"
221			"\tmtccr0 %%r3"
222			: : : "r3", "r4");
223
224		// kernel_args is a packed structure with 64bit fields so...
225		// we must enable unaligned transfers by setting the FLSTA bit to 0
226		// XXX: actually doesn't work for float ops which gcc emits :-(
227		asm volatile(
228			"mfccr0 %%r3\n"
229			"\tli %%r4,~(1<<(31-23))\n"
230			"\tand %%r3,%%r3,%%r4\n"
231			"\tmtccr0 %%r3"
232			: : : "r3", "r4");
233	}
234
235	// we do need an FPU for vsnprintf to work
236	// on Sam460ex at least U-Boot doesn't enable the FPU for us
237	msr = get_msr();
238	msr |= MSR_FP_AVAILABLE;
239	msr = set_msr(msr);
240
241	if ((msr & MSR_FP_AVAILABLE) == 0) {
242		// sadly panic uses vsnprintf which fails without FPU anyway
243		panic("no FPU!");
244		return B_ERROR;
245	}
246
247	TRACE(("CPU detection:\n"));
248	TRACE(("PVR revision %sknown: 0x%8lx\n", pvr_unknown ? "un" : "", pvr));
249	TRACE(("FDT model %sknown: %s\n", fdt_unknown ? "un" : "", fdt_model));
250	TRACE(("flags: %s440 %s460\n", is_440 ? "" : "!", is_460 ? "" : "!"));
251
252	return B_OK;
253}
254
255
256//	#pragma mark -
257
258
259extern "C" void
260spin(bigtime_t microseconds)
261{
262	for(bigtime_t i=0;i<microseconds;i=i+1)
263	{
264		/*
265		asm volatile ("mov r0,r0");
266		asm volatile ("mov r0,r0");
267		*/
268	}
269	#warning U-Boot:PPC:TODO!!
270}
271
272
273extern "C" status_t
274boot_arch_cpu_init(void)
275{
276	// This is U-Boot
277	gKernelArgs.arch_args.platform = PPC_PLATFORM_U_BOOT;
278
279	// first check some features
280	// including some necessary for dprintf()...
281	status_t err = check_cpu_features();
282
283	if (err != B_OK) {
284		panic("You need a Pentium or higher in order to boot!\n");
285		return err;
286	}
287
288	// now enumerate correctly all CPUs and get system frequencies
289	enumerate_cpus();
290
291	return B_OK;
292}
293
294