xref: /haiku/src/system/boot/platform/u-boot/arch/ppc/arch_cpu.cpp (revision 425ac1b60a56f4df7a0e88bd784545c0ec4fa01f)
1037f252fSFrançois Revol /*
2037f252fSFrançois Revol  * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3037f252fSFrançois Revol  * Distributed under the terms of the MIT License.
4037f252fSFrançois Revol  */
5037f252fSFrançois Revol 
6037f252fSFrançois Revol 
7037f252fSFrançois Revol #include "cpu.h"
8cce9d8cfSFrançois Revol #include "board_config.h"
9037f252fSFrançois Revol 
10037f252fSFrançois Revol #include <OS.h>
11037f252fSFrançois Revol #include <boot/platform.h>
12037f252fSFrançois Revol #include <boot/stdio.h>
13037f252fSFrançois Revol #include <boot/kernel_args.h>
14037f252fSFrançois Revol #include <boot/stage2.h>
15037f252fSFrançois Revol #include <arch/cpu.h>
16037f252fSFrançois Revol #include <arch/ppc/arch_cpu.h>
1794b802f5SFrançois Revol #include <arch/ppc/arch_platform.h>
18037f252fSFrançois Revol #include <arch_kernel.h>
19037f252fSFrançois Revol #include <arch_system_info.h>
200255210bSFrançois Revol #include <platform/openfirmware/devices.h>
210255210bSFrançois Revol #include <platform/openfirmware/openfirmware.h>
22037f252fSFrançois Revol 
23037f252fSFrançois Revol #include <string.h>
24037f252fSFrançois Revol 
250255210bSFrançois Revol // TODO: try to move remaining FDT code to OF calls
26f4b31e85SFrançois Revol extern "C" {
27f4b31e85SFrançois Revol #include <fdt.h>
28f4b31e85SFrançois Revol #include <libfdt.h>
29f4b31e85SFrançois Revol #include <libfdt_env.h>
30f4b31e85SFrançois Revol };
31037f252fSFrançois Revol 
32f4b31e85SFrançois Revol 
33f4b31e85SFrançois Revol extern void *gFDT;
34f4b31e85SFrançois Revol 
35f4b31e85SFrançois Revol #define TRACE_CPU
36037f252fSFrançois Revol #ifdef TRACE_CPU
37037f252fSFrançois Revol #	define TRACE(x) dprintf x
38037f252fSFrançois Revol #else
39037f252fSFrançois Revol #	define TRACE(x) ;
40037f252fSFrançois Revol #endif
41037f252fSFrançois Revol 
42037f252fSFrançois Revol 
43af63ede7SFrançois Revol // FIXME: this is ugly; introduce a cpu type in kernel args
44af63ede7SFrançois Revol bool gIs440 = false;
45af63ede7SFrançois Revol 
46af63ede7SFrançois Revol 
470255210bSFrançois Revol static status_t
enumerate_cpus(void)480255210bSFrançois Revol enumerate_cpus(void)
49037f252fSFrançois Revol {
500255210bSFrançois Revol 	// iterate through the "/cpus" node to find all CPUs
510255210bSFrançois Revol 	int cpus = of_finddevice("/cpus");
520255210bSFrançois Revol 	if (cpus == OF_FAILED) {
530255210bSFrançois Revol 		printf("enumerate_cpus(): Failed to open \"/cpus\"!\n");
540255210bSFrançois Revol 		return B_ERROR;
550255210bSFrançois Revol 	}
560255210bSFrançois Revol 
570255210bSFrançois Revol 	char cpuPath[256];
580255210bSFrançois Revol 	int cookie = 0;
590255210bSFrançois Revol 	int cpuCount = 0;
600255210bSFrançois Revol 	while (of_get_next_device(&cookie, cpus, "cpu", cpuPath,
610255210bSFrançois Revol 			sizeof(cpuPath)) == B_OK) {
620255210bSFrançois Revol 		TRACE(("found CPU: %s\n", cpuPath));
630255210bSFrançois Revol 
640255210bSFrançois Revol 		// For the first CPU get the frequencies of CPU, bus, and time base.
650255210bSFrançois Revol 		// We assume they are the same for all CPUs.
660255210bSFrançois Revol 		if (cpuCount == 0) {
670255210bSFrançois Revol 			int cpu = of_finddevice(cpuPath);
680255210bSFrançois Revol 			if (cpu == OF_FAILED) {
690255210bSFrançois Revol 				printf("enumerate_cpus: Failed get CPU device node!\n");
700255210bSFrançois Revol 				return B_ERROR;
710255210bSFrançois Revol 			}
720255210bSFrançois Revol 
730255210bSFrançois Revol 			// TODO: Does encode-int really encode quadlet (32 bit numbers)
740255210bSFrançois Revol 			// only?
750255210bSFrançois Revol 			int32 clockFrequency;
760255210bSFrançois Revol 			if (of_getprop(cpu, "clock-frequency", &clockFrequency, 4)
770255210bSFrançois Revol 					== OF_FAILED) {
780255210bSFrançois Revol 				printf("enumerate_cpus: Failed to get CPU clock "
790255210bSFrançois Revol 					"frequency!\n");
800255210bSFrançois Revol 				return B_ERROR;
810255210bSFrançois Revol 			}
820255210bSFrançois Revol 			int32 busFrequency = 0;
830255210bSFrançois Revol 			if (of_getprop(cpu, "bus-frequency", &busFrequency, 4)
840255210bSFrançois Revol 					== OF_FAILED) {
850255210bSFrançois Revol 				//printf("enumerate_cpus: Failed to get bus clock "
860255210bSFrançois Revol 				//	"frequency!\n");
870255210bSFrançois Revol 				//return B_ERROR;
880255210bSFrançois Revol 			}
890255210bSFrançois Revol 			int32 timeBaseFrequency;
900255210bSFrançois Revol 			if (of_getprop(cpu, "timebase-frequency", &timeBaseFrequency, 4)
910255210bSFrançois Revol 					== OF_FAILED) {
920255210bSFrançois Revol 				printf("enumerate_cpus: Failed to get time base "
930255210bSFrançois Revol 					"frequency!\n");
940255210bSFrançois Revol 				return B_ERROR;
950255210bSFrançois Revol 			}
960255210bSFrançois Revol 
970255210bSFrançois Revol 			gKernelArgs.arch_args.cpu_frequency = clockFrequency;
980255210bSFrançois Revol 			gKernelArgs.arch_args.bus_frequency = busFrequency;
990255210bSFrançois Revol 			gKernelArgs.arch_args.time_base_frequency = timeBaseFrequency;
1000255210bSFrançois Revol 
1010255210bSFrançois Revol 			TRACE(("  CPU clock frequency: %ld\n", clockFrequency));
1020255210bSFrançois Revol 			//TRACE(("  bus clock frequency: %ld\n", busFrequency));
1030255210bSFrançois Revol 			TRACE(("  time base frequency: %ld\n", timeBaseFrequency));
1040255210bSFrançois Revol 		}
1050255210bSFrançois Revol 
1060255210bSFrançois Revol 		cpuCount++;
1070255210bSFrançois Revol 	}
1080255210bSFrançois Revol 
1090255210bSFrançois Revol 	if (cpuCount == 0) {
1100255210bSFrançois Revol 		printf("enumerate_cpus(): Found no CPUs!\n");
1110255210bSFrançois Revol 		return B_ERROR;
1120255210bSFrançois Revol 	}
1130255210bSFrançois Revol 
1140255210bSFrançois Revol 	gKernelArgs.num_cpus = cpuCount;
1150255210bSFrançois Revol 
1160255210bSFrançois Revol 	// FDT doesn't list bus frequency on the cpu node but on the plb node
1170255210bSFrançois Revol 	if (gKernelArgs.arch_args.bus_frequency == 0) {
1180255210bSFrançois Revol 		int plb = of_finddevice("/plb");
1190255210bSFrançois Revol 
1200255210bSFrançois Revol 		int32 busFrequency = 0;
1210255210bSFrançois Revol 		if (of_getprop(plb, "clock-frequency", &busFrequency, 4)
1220255210bSFrançois Revol 				== OF_FAILED) {
1230255210bSFrançois Revol 			printf("enumerate_cpus: Failed to get bus clock "
1240255210bSFrançois Revol 				"frequency!\n");
1250255210bSFrançois Revol 			return B_ERROR;
1260255210bSFrançois Revol 		}
1270255210bSFrançois Revol 		gKernelArgs.arch_args.bus_frequency = busFrequency;
1280255210bSFrançois Revol 	}
129*425ac1b6SAlexander von Gluck IV 	TRACE(("  bus clock frequency: %lld\n",
13085e1bac2SFrançois Revol 		gKernelArgs.arch_args.bus_frequency));
1310255210bSFrançois Revol 
1320255210bSFrançois Revol #if 0
1330255210bSFrançois Revol //XXX:Classic
1340255210bSFrançois Revol 	// allocate the kernel stacks (the memory stuff is already initialized
1350255210bSFrançois Revol 	// at this point)
1360255210bSFrançois Revol 	addr_t stack = (addr_t)arch_mmu_allocate((void*)0x80000000,
1370255210bSFrançois Revol 		cpuCount * (KERNEL_STACK_SIZE + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE),
1380255210bSFrançois Revol 		B_READ_AREA | B_WRITE_AREA, false);
1390255210bSFrançois Revol 	if (!stack) {
1400255210bSFrançois Revol 		printf("enumerate_cpus(): Failed to allocate kernel stack(s)!\n");
1410255210bSFrançois Revol 		return B_NO_MEMORY;
1420255210bSFrançois Revol 	}
1430255210bSFrançois Revol 
1440255210bSFrançois Revol 	for (int i = 0; i < cpuCount; i++) {
1450255210bSFrançois Revol 		gKernelArgs.cpu_kstack[i].start = stack;
1460255210bSFrançois Revol 		gKernelArgs.cpu_kstack[i].size = KERNEL_STACK_SIZE
1470255210bSFrançois Revol 			+ KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE;
1480255210bSFrançois Revol 		stack += KERNEL_STACK_SIZE + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE;
1490255210bSFrançois Revol 	}
1500255210bSFrançois Revol #endif
1510255210bSFrançois Revol 
1520255210bSFrançois Revol 	return B_OK;
153037f252fSFrançois Revol }
154037f252fSFrançois Revol 
155037f252fSFrançois Revol 
156037f252fSFrançois Revol static status_t
check_cpu_features()157037f252fSFrançois Revol check_cpu_features()
158037f252fSFrançois Revol {
159037f252fSFrançois Revol 	uint32 msr;
160f4b31e85SFrançois Revol 	uint32 pvr;
161f4b31e85SFrançois Revol 	bool is_440 = false;
162f4b31e85SFrançois Revol 	bool is_460 = false;
163f4b31e85SFrançois Revol 	bool pvr_unknown = false;
164f4b31e85SFrançois Revol 	bool fdt_unknown = true;
165f4b31e85SFrançois Revol 	const char *fdt_model = NULL;
166037f252fSFrançois Revol 
167f4b31e85SFrançois Revol 	// attempt to detect processor version, either from PVR or FDT
168f4b31e85SFrançois Revol 	// TODO:someday we'll support other things than 440/460...
169f4b31e85SFrançois Revol 
170f4b31e85SFrançois Revol 	// read the Processor Version Register
171f4b31e85SFrançois Revol 	pvr = get_pvr();
172f4b31e85SFrançois Revol 	uint16 version = (uint16)(pvr >> 16);
173f4b31e85SFrançois Revol 	uint16 revision = (uint16)(pvr & 0xffff);
174f4b31e85SFrançois Revol 
175f4b31e85SFrançois Revol 	switch (version) {
176f4b31e85SFrançois Revol 		case AMCC440EP:
177f4b31e85SFrançois Revol 			is_440 = true;
178f4b31e85SFrançois Revol 			break;
179f4b31e85SFrançois Revol 		case AMCC460EX:
180f4b31e85SFrançois Revol 			is_460 = true;
181f4b31e85SFrançois Revol 			break;
182f4b31e85SFrançois Revol 		default:
183f4b31e85SFrançois Revol 			pvr_unknown = true;
184f4b31e85SFrançois Revol 	}
185f4b31e85SFrançois Revol 
186f4b31e85SFrançois Revol 	// if we have an FDT...
187f4b31e85SFrançois Revol 	// XXX: use it only as fallback?
188f4b31e85SFrançois Revol 	if (gFDT != NULL/* && pvr_unknown*/) {
189acc4e245SFrançois Revol 		// TODO: for MP support we must check /chosen/cpu first
190f4b31e85SFrançois Revol 		int node = fdt_path_offset(gFDT, "/cpus/cpu@0");
191f4b31e85SFrançois Revol 		int len;
192f4b31e85SFrançois Revol 
193f4b31e85SFrançois Revol 		fdt_model = (const char *)fdt_getprop(gFDT, node, "model", &len);
194f4b31e85SFrançois Revol 		// TODO: partial match ? "PowerPC,440" && isalpha(next char) ?
195f4b31e85SFrançois Revol 		if (fdt_model) {
196f4b31e85SFrançois Revol 			if (!strcmp(fdt_model, "PowerPC,440EP")) {
197f4b31e85SFrançois Revol 				is_440 = true;
198f4b31e85SFrançois Revol 				fdt_unknown = false;
199f4b31e85SFrançois Revol 			} else if (!strcmp(fdt_model, "PowerPC,460EX")) {
200f4b31e85SFrançois Revol 				is_460 = true;
201f4b31e85SFrançois Revol 				fdt_unknown = false;
202f4b31e85SFrançois Revol 			}
203f4b31e85SFrançois Revol 		}
204f4b31e85SFrançois Revol 	}
205f4b31e85SFrançois Revol 
206f4b31e85SFrançois Revol 	if (is_460)
207f4b31e85SFrançois Revol 		is_440 = true;
208f4b31e85SFrançois Revol 
209af63ede7SFrançois Revol 	gIs440 = is_440;
210af63ede7SFrançois Revol 
211f4b31e85SFrançois Revol 	// some cpu-dependent tweaking
212f4b31e85SFrançois Revol 
213f4b31e85SFrançois Revol 	if (is_440) {
214cce9d8cfSFrançois Revol 		// the FPU is implemented as an Auxiliary Processing Unit,
215cce9d8cfSFrançois Revol 		// so we must enable transfers by setting the DAPUIB bit to 0
216e57c83d9SFrançois Revol 		// (11th bit)
217cce9d8cfSFrançois Revol 		asm volatile(
218cce9d8cfSFrançois Revol 			"mfccr0 %%r3\n"
219e57c83d9SFrançois Revol 			"\tlis %%r4,~(1<<(31-11-16))\n"
220cce9d8cfSFrançois Revol 			"\tand %%r3,%%r3,%%r4\n"
221cce9d8cfSFrançois Revol 			"\tmtccr0 %%r3"
222cce9d8cfSFrançois Revol 			: : : "r3", "r4");
2239e66f031SFrançois Revol 
2249e66f031SFrançois Revol 		// kernel_args is a packed structure with 64bit fields so...
2259e66f031SFrançois Revol 		// we must enable unaligned transfers by setting the FLSTA bit to 0
2269e66f031SFrançois Revol 		// XXX: actually doesn't work for float ops which gcc emits :-(
2279e66f031SFrançois Revol 		asm volatile(
2289e66f031SFrançois Revol 			"mfccr0 %%r3\n"
2299e66f031SFrançois Revol 			"\tli %%r4,~(1<<(31-23))\n"
2309e66f031SFrançois Revol 			"\tand %%r3,%%r3,%%r4\n"
2319e66f031SFrançois Revol 			"\tmtccr0 %%r3"
2329e66f031SFrançois Revol 			: : : "r3", "r4");
233f4b31e85SFrançois Revol 	}
234cce9d8cfSFrançois Revol 
235cce9d8cfSFrançois Revol 	// we do need an FPU for vsnprintf to work
236037f252fSFrançois Revol 	// on Sam460ex at least U-Boot doesn't enable the FPU for us
237037f252fSFrançois Revol 	msr = get_msr();
238037f252fSFrançois Revol 	msr |= MSR_FP_AVAILABLE;
239037f252fSFrançois Revol 	msr = set_msr(msr);
240037f252fSFrançois Revol 
241037f252fSFrançois Revol 	if ((msr & MSR_FP_AVAILABLE) == 0) {
242037f252fSFrançois Revol 		// sadly panic uses vsnprintf which fails without FPU anyway
243037f252fSFrançois Revol 		panic("no FPU!");
244037f252fSFrançois Revol 		return B_ERROR;
245037f252fSFrançois Revol 	}
246037f252fSFrançois Revol 
247f4b31e85SFrançois Revol 	TRACE(("CPU detection:\n"));
248f4b31e85SFrançois Revol 	TRACE(("PVR revision %sknown: 0x%8lx\n", pvr_unknown ? "un" : "", pvr));
249f4b31e85SFrançois Revol 	TRACE(("FDT model %sknown: %s\n", fdt_unknown ? "un" : "", fdt_model));
250f4b31e85SFrançois Revol 	TRACE(("flags: %s440 %s460\n", is_440 ? "" : "!", is_460 ? "" : "!"));
251f4b31e85SFrançois Revol 
252037f252fSFrançois Revol 	return B_OK;
253037f252fSFrançois Revol }
254037f252fSFrançois Revol 
255037f252fSFrançois Revol 
256037f252fSFrançois Revol //	#pragma mark -
257037f252fSFrançois Revol 
258037f252fSFrançois Revol 
259037f252fSFrançois Revol extern "C" void
spin(bigtime_t microseconds)26048064fbcSAlexander von Gluck IV spin(bigtime_t microseconds)
261037f252fSFrançois Revol {
262037f252fSFrançois Revol 	for(bigtime_t i=0;i<microseconds;i=i+1)
263037f252fSFrançois Revol 	{
264037f252fSFrançois Revol 		/*
265037f252fSFrançois Revol 		asm volatile ("mov r0,r0");
266037f252fSFrançois Revol 		asm volatile ("mov r0,r0");
267037f252fSFrançois Revol 		*/
268037f252fSFrançois Revol 	}
269037f252fSFrançois Revol 	#warning U-Boot:PPC:TODO!!
270037f252fSFrançois Revol }
271037f252fSFrançois Revol 
272037f252fSFrançois Revol 
273037f252fSFrançois Revol extern "C" status_t
boot_arch_cpu_init(void)274037f252fSFrançois Revol boot_arch_cpu_init(void)
275037f252fSFrançois Revol {
27694b802f5SFrançois Revol 	// This is U-Boot
27794b802f5SFrançois Revol 	gKernelArgs.arch_args.platform = PPC_PLATFORM_U_BOOT;
27894b802f5SFrançois Revol 
2790255210bSFrançois Revol 	// first check some features
2800255210bSFrançois Revol 	// including some necessary for dprintf()...
281037f252fSFrançois Revol 	status_t err = check_cpu_features();
282037f252fSFrançois Revol 
283037f252fSFrançois Revol 	if (err != B_OK) {
284037f252fSFrançois Revol 		panic("You need a Pentium or higher in order to boot!\n");
285037f252fSFrançois Revol 		return err;
286037f252fSFrançois Revol 	}
287037f252fSFrançois Revol 
2880255210bSFrançois Revol 	// now enumerate correctly all CPUs and get system frequencies
2890255210bSFrançois Revol 	enumerate_cpus();
290037f252fSFrançois Revol 
291037f252fSFrançois Revol 	return B_OK;
292037f252fSFrançois Revol }
293037f252fSFrançois Revol 
294