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: %Ld\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