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