xref: /haiku/src/system/boot/platform/u-boot/arch/ppc/arch_cpu.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
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
26 extern "C" {
27 #include <fdt.h>
28 #include <libfdt.h>
29 #include <libfdt_env.h>
30 };
31 
32 
33 extern 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
44 bool gIs440 = false;
45 
46 
47 static status_t
48 enumerate_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 
156 static status_t
157 check_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 
259 extern "C" void
260 spin(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 
273 extern "C" status_t
274 boot_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