platform_powermac.c revision 259082
1/*- 2 * Copyright (c) 2008 Marcel Moolenaar 3 * Copyright (c) 2009 Nathan Whitehorn 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: head/sys/powerpc/powermac/platform_powermac.c 259082 2013-12-07 22:25:07Z jhibbits $"); 30 31#include <sys/param.h> 32#include <sys/systm.h> 33#include <sys/kernel.h> 34#include <sys/bus.h> 35#include <sys/pcpu.h> 36#include <sys/proc.h> 37#include <sys/smp.h> 38#include <vm/vm.h> 39#include <vm/pmap.h> 40 41#include <machine/bus.h> 42#include <machine/cpu.h> 43#include <machine/hid.h> 44#include <machine/platformvar.h> 45#include <machine/pmap.h> 46#include <machine/smp.h> 47#include <machine/spr.h> 48 49#include <dev/ofw/openfirm.h> 50#include <machine/ofw_machdep.h> 51 52#include "platform_if.h" 53 54#ifdef SMP 55extern void *ap_pcpu; 56#endif 57 58static int powermac_probe(platform_t); 59static int powermac_attach(platform_t); 60void powermac_mem_regions(platform_t, struct mem_region *phys, int *physsz, 61 struct mem_region *avail, int *availsz); 62static u_long powermac_timebase_freq(platform_t, struct cpuref *cpuref); 63static int powermac_smp_first_cpu(platform_t, struct cpuref *cpuref); 64static int powermac_smp_next_cpu(platform_t, struct cpuref *cpuref); 65static int powermac_smp_get_bsp(platform_t, struct cpuref *cpuref); 66static int powermac_smp_start_cpu(platform_t, struct pcpu *cpu); 67static void powermac_reset(platform_t); 68 69static platform_method_t powermac_methods[] = { 70 PLATFORMMETHOD(platform_probe, powermac_probe), 71 PLATFORMMETHOD(platform_attach, powermac_attach), 72 PLATFORMMETHOD(platform_mem_regions, powermac_mem_regions), 73 PLATFORMMETHOD(platform_timebase_freq, powermac_timebase_freq), 74 75 PLATFORMMETHOD(platform_smp_first_cpu, powermac_smp_first_cpu), 76 PLATFORMMETHOD(platform_smp_next_cpu, powermac_smp_next_cpu), 77 PLATFORMMETHOD(platform_smp_get_bsp, powermac_smp_get_bsp), 78 PLATFORMMETHOD(platform_smp_start_cpu, powermac_smp_start_cpu), 79 80 PLATFORMMETHOD(platform_reset, powermac_reset), 81 82 PLATFORMMETHOD_END 83}; 84 85static platform_def_t powermac_platform = { 86 "powermac", 87 powermac_methods, 88 0 89}; 90 91PLATFORM_DEF(powermac_platform); 92 93static int 94powermac_probe(platform_t plat) 95{ 96 char compat[255]; 97 ssize_t compatlen; 98 char *curstr; 99 phandle_t root; 100 101 root = OF_peer(0); 102 if (root == 0) 103 return (ENXIO); 104 105 compatlen = OF_getprop(root, "compatible", compat, sizeof(compat)); 106 107 for (curstr = compat; curstr < compat + compatlen; 108 curstr += strlen(curstr) + 1) { 109 if (strncmp(curstr, "MacRISC", 7) == 0) 110 return (BUS_PROBE_SPECIFIC); 111 } 112 113 return (ENXIO); 114} 115 116void 117powermac_mem_regions(platform_t plat, struct mem_region *phys, int *physsz, 118 struct mem_region *avail, int *availsz) 119{ 120 phandle_t memory; 121 cell_t memoryprop[PHYS_AVAIL_SZ * 2]; 122 ssize_t propsize, i, j; 123 int physacells = 1; 124 125 memory = OF_finddevice("/memory"); 126 127 /* "reg" has variable #address-cells, but #size-cells is always 1 */ 128 OF_getprop(OF_parent(memory), "#address-cells", &physacells, 129 sizeof(physacells)); 130 131 propsize = OF_getprop(memory, "reg", memoryprop, sizeof(memoryprop)); 132 propsize /= sizeof(cell_t); 133 for (i = 0, j = 0; i < propsize; i += physacells+1, j++) { 134 phys[j].mr_start = memoryprop[i]; 135 if (physacells == 2) { 136#ifndef __powerpc64__ 137 /* On 32-bit PPC, ignore regions starting above 4 GB */ 138 if (memoryprop[i] != 0) { 139 j--; 140 continue; 141 } 142#else 143 phys[j].mr_start <<= 32; 144#endif 145 phys[j].mr_start |= memoryprop[i+1]; 146 } 147 phys[j].mr_size = memoryprop[i + physacells]; 148 } 149 *physsz = j; 150 151 /* "available" always has #address-cells = 1 */ 152 propsize = OF_getprop(memory, "available", memoryprop, 153 sizeof(memoryprop)); 154 propsize /= sizeof(cell_t); 155 for (i = 0, j = 0; i < propsize; i += 2, j++) { 156 avail[j].mr_start = memoryprop[i]; 157 avail[j].mr_size = memoryprop[i + 1]; 158 } 159 160#ifdef __powerpc64__ 161 /* Add in regions above 4 GB to the available list */ 162 for (i = 0; i < *physsz; i++) { 163 if (phys[i].mr_start > BUS_SPACE_MAXADDR_32BIT) { 164 avail[j].mr_start = phys[i].mr_start; 165 avail[j].mr_size = phys[i].mr_size; 166 j++; 167 } 168 } 169#endif 170 *availsz = j; 171} 172 173static int 174powermac_attach(platform_t plat) 175{ 176 phandle_t rootnode; 177 char model[32]; 178 179 180 /* 181 * Quiesce Open Firmware on PowerMac11,2 and 12,1. It is 182 * necessary there to shut down a background thread doing fan 183 * management, and is harmful on other machines (it will make OF 184 * shut off power to various system components it had turned on). 185 * 186 * Note: we don't need to worry about which OF module we are 187 * using since this is called only from very early boot, within 188 * OF's boot context. 189 */ 190 191 rootnode = OF_finddevice("/"); 192 if (OF_getprop(rootnode, "model", model, sizeof(model)) > 0) { 193 if (strcmp(model, "PowerMac11,2") == 0 || 194 strcmp(model, "PowerMac12,1") == 0) { 195 ofw_quiesce(); 196 } 197 } 198 199 return (0); 200} 201 202static u_long 203powermac_timebase_freq(platform_t plat, struct cpuref *cpuref) 204{ 205 phandle_t phandle; 206 int32_t ticks = -1; 207 208 phandle = cpuref->cr_hwref; 209 210 OF_getprop(phandle, "timebase-frequency", &ticks, sizeof(ticks)); 211 212 if (ticks <= 0) 213 panic("Unable to determine timebase frequency!"); 214 215 return (ticks); 216} 217 218 219static int 220powermac_smp_fill_cpuref(struct cpuref *cpuref, phandle_t cpu) 221{ 222 cell_t cpuid; 223 int res; 224 225 cpuref->cr_hwref = cpu; 226 res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid)); 227 228 /* 229 * psim doesn't have a reg property, so assume 0 as for the 230 * uniprocessor case in the CHRP spec. 231 */ 232 if (res < 0) { 233 cpuid = 0; 234 } 235 236 cpuref->cr_cpuid = cpuid & 0xff; 237 return (0); 238} 239 240static int 241powermac_smp_first_cpu(platform_t plat, struct cpuref *cpuref) 242{ 243 char buf[8]; 244 phandle_t cpu, dev, root; 245 int res; 246 247 root = OF_peer(0); 248 249 dev = OF_child(root); 250 while (dev != 0) { 251 res = OF_getprop(dev, "name", buf, sizeof(buf)); 252 if (res > 0 && strcmp(buf, "cpus") == 0) 253 break; 254 dev = OF_peer(dev); 255 } 256 if (dev == 0) { 257 /* 258 * psim doesn't have a name property on the /cpus node, 259 * but it can be found directly 260 */ 261 dev = OF_finddevice("/cpus"); 262 if (dev == -1) 263 return (ENOENT); 264 } 265 266 cpu = OF_child(dev); 267 268 while (cpu != 0) { 269 res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); 270 if (res > 0 && strcmp(buf, "cpu") == 0) 271 break; 272 cpu = OF_peer(cpu); 273 } 274 if (cpu == 0) 275 return (ENOENT); 276 277 return (powermac_smp_fill_cpuref(cpuref, cpu)); 278} 279 280static int 281powermac_smp_next_cpu(platform_t plat, struct cpuref *cpuref) 282{ 283 char buf[8]; 284 phandle_t cpu; 285 int res; 286 287 cpu = OF_peer(cpuref->cr_hwref); 288 while (cpu != 0) { 289 res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); 290 if (res > 0 && strcmp(buf, "cpu") == 0) 291 break; 292 cpu = OF_peer(cpu); 293 } 294 if (cpu == 0) 295 return (ENOENT); 296 297 return (powermac_smp_fill_cpuref(cpuref, cpu)); 298} 299 300static int 301powermac_smp_get_bsp(platform_t plat, struct cpuref *cpuref) 302{ 303 ihandle_t inst; 304 phandle_t bsp, chosen; 305 int res; 306 307 chosen = OF_finddevice("/chosen"); 308 if (chosen == -1) 309 return (ENXIO); 310 311 res = OF_getprop(chosen, "cpu", &inst, sizeof(inst)); 312 if (res < 0) 313 return (ENXIO); 314 315 bsp = OF_instance_to_package(inst); 316 return (powermac_smp_fill_cpuref(cpuref, bsp)); 317} 318 319static int 320powermac_smp_start_cpu(platform_t plat, struct pcpu *pc) 321{ 322#ifdef SMP 323 phandle_t cpu; 324 volatile uint8_t *rstvec; 325 static volatile uint8_t *rstvec_virtbase = NULL; 326 int res, reset, timeout; 327 328 cpu = pc->pc_hwref; 329 res = OF_getprop(cpu, "soft-reset", &reset, sizeof(reset)); 330 if (res < 0) { 331 reset = 0x58; 332 333 switch (pc->pc_cpuid) { 334 case 0: 335 reset += 0x03; 336 break; 337 case 1: 338 reset += 0x04; 339 break; 340 case 2: 341 reset += 0x0f; 342 break; 343 case 3: 344 reset += 0x10; 345 break; 346 default: 347 return (ENXIO); 348 } 349 } 350 351 ap_pcpu = pc; 352 353 if (rstvec_virtbase == NULL) 354 rstvec_virtbase = pmap_mapdev(0x80000000, PAGE_SIZE); 355 356 rstvec = rstvec_virtbase + reset; 357 358 *rstvec = 4; 359 powerpc_sync(); 360 (void)(*rstvec); 361 powerpc_sync(); 362 DELAY(1); 363 *rstvec = 0; 364 powerpc_sync(); 365 (void)(*rstvec); 366 powerpc_sync(); 367 368 timeout = 10000; 369 while (!pc->pc_awake && timeout--) 370 DELAY(100); 371 372 return ((pc->pc_awake) ? 0 : EBUSY); 373#else 374 /* No SMP support */ 375 return (ENXIO); 376#endif 377} 378 379static void 380powermac_reset(platform_t platform) 381{ 382 OF_reboot(); 383} 384 385