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 asm volatile( 220 "mfccr0 %%r3\n" 221 "\tlis %%r4,~(1<<(20-16))\n" 222 "\tand %%r3,%%r3,%%r4\n" 223 "\tmtccr0 %%r3" 224 : : : "r3", "r4"); 225 } 226 227 // we do need an FPU for vsnprintf to work 228 // on Sam460ex at least U-Boot doesn't enable the FPU for us 229 msr = get_msr(); 230 msr |= MSR_FP_AVAILABLE; 231 msr = set_msr(msr); 232 233 if ((msr & MSR_FP_AVAILABLE) == 0) { 234 // sadly panic uses vsnprintf which fails without FPU anyway 235 panic("no FPU!"); 236 return B_ERROR; 237 } 238 239 TRACE(("CPU detection:\n")); 240 TRACE(("PVR revision %sknown: 0x%8lx\n", pvr_unknown ? "un" : "", pvr)); 241 TRACE(("FDT model %sknown: %s\n", fdt_unknown ? "un" : "", fdt_model)); 242 TRACE(("flags: %s440 %s460\n", is_440 ? "" : "!", is_460 ? "" : "!")); 243 244 return B_OK; 245 } 246 247 248 // #pragma mark - 249 250 251 extern "C" void 252 arch_spin(bigtime_t microseconds) 253 { 254 for(bigtime_t i=0;i<microseconds;i=i+1) 255 { 256 /* 257 asm volatile ("mov r0,r0"); 258 asm volatile ("mov r0,r0"); 259 */ 260 } 261 #warning U-Boot:PPC:TODO!! 262 } 263 264 265 extern "C" status_t 266 boot_arch_cpu_init(void) 267 { 268 // This is U-Boot 269 gKernelArgs.arch_args.platform = PPC_PLATFORM_U_BOOT; 270 271 // first check some features 272 // including some necessary for dprintf()... 273 status_t err = check_cpu_features(); 274 275 if (err != B_OK) { 276 panic("You need a Pentium or higher in order to boot!\n"); 277 return err; 278 } 279 280 // now enumerate correctly all CPUs and get system frequencies 281 enumerate_cpus(); 282 283 return B_OK; 284 } 285 286