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