platform_bare.c revision 224618
1290000Sglebius/*- 2132451Sroberto * Copyright (c) 2008-2009 Semihalf, Rafal Jaworowski 354359Sroberto * All rights reserved. 4290000Sglebius * 5290000Sglebius * Redistribution and use in source and binary forms, with or without 654359Sroberto * modification, are permitted provided that the following conditions 754359Sroberto * are met: 854359Sroberto * 954359Sroberto * 1. Redistributions of source code must retain the above copyright 1054359Sroberto * notice, this list of conditions and the following disclaimer. 1154359Sroberto * 2. Redistributions in binary form must reproduce the above copyright 1254359Sroberto * notice, this list of conditions and the following disclaimer in the 1354359Sroberto * documentation and/or other materials provided with the distribution. 1454359Sroberto * 15106163Sroberto * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16106163Sroberto * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1754359Sroberto * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18290000Sglebius * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19290000Sglebius * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20290000Sglebius * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21290000Sglebius * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22290000Sglebius * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23290000Sglebius * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24290000Sglebius * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25290000Sglebius */ 26290000Sglebius 27290000Sglebius#include <sys/cdefs.h> 28290000Sglebius__FBSDID("$FreeBSD: head/sys/powerpc/booke/platform_bare.c 224618 2011-08-02 23:49:23Z marcel $"); 29290000Sglebius 30290000Sglebius#include <sys/param.h> 31290000Sglebius#include <sys/systm.h> 32290000Sglebius#include <sys/kernel.h> 33290000Sglebius#include <sys/bus.h> 34290000Sglebius#include <sys/pcpu.h> 35290000Sglebius#include <sys/proc.h> 36290000Sglebius#include <sys/smp.h> 37290000Sglebius 38290000Sglebius#include <machine/bus.h> 39290000Sglebius#include <machine/cpu.h> 40290000Sglebius#include <machine/hid.h> 41290000Sglebius#include <machine/platform.h> 42290000Sglebius#include <machine/platformvar.h> 43290000Sglebius#include <machine/smp.h> 44290000Sglebius#include <machine/spr.h> 45290000Sglebius#include <machine/vmparam.h> 46290000Sglebius 47290000Sglebius#include <dev/fdt/fdt_common.h> 48290000Sglebius#include <dev/ofw/ofw_bus.h> 49290000Sglebius#include <dev/ofw/ofw_bus_subr.h> 50290000Sglebius#include <dev/ofw/openfirm.h> 51290000Sglebius 52290000Sglebius#include <powerpc/mpc85xx/mpc85xx.h> 53290000Sglebius 54290000Sglebius#include "platform_if.h" 55290000Sglebius 56290000Sglebius#ifdef SMP 57290000Sglebiusextern void *ap_pcpu; 58290000Sglebiusextern uint8_t __boot_page[]; /* Boot page body */ 59290000Sglebiusextern uint32_t kernload_ap; /* Kernel physical load address */ 60290000Sglebius#endif 61290000Sglebius 62290000Sglebiusextern uint32_t *bootinfo; 63290000Sglebius 64290000Sglebiusstatic int cpu, maxcpu; 65290000Sglebius 66290000Sglebiusstatic int bare_probe(platform_t); 67290000Sglebiusstatic void bare_mem_regions(platform_t, struct mem_region **phys, int *physsz, 68290000Sglebius struct mem_region **avail, int *availsz); 69290000Sglebiusstatic u_long bare_timebase_freq(platform_t, struct cpuref *cpuref); 70290000Sglebiusstatic int bare_smp_first_cpu(platform_t, struct cpuref *cpuref); 71290000Sglebiusstatic int bare_smp_next_cpu(platform_t, struct cpuref *cpuref); 72290000Sglebiusstatic int bare_smp_get_bsp(platform_t, struct cpuref *cpuref); 7354359Srobertostatic int bare_smp_start_cpu(platform_t, struct pcpu *cpu); 74200576Sroberto 7554359Srobertostatic void e500_reset(platform_t); 76200576Sroberto 77132451Srobertostatic platform_method_t bare_methods[] = { 78132451Sroberto PLATFORMMETHOD(platform_probe, bare_probe), 79132451Sroberto PLATFORMMETHOD(platform_mem_regions, bare_mem_regions), 80132451Sroberto PLATFORMMETHOD(platform_timebase_freq, bare_timebase_freq), 8182498Sroberto 82132451Sroberto PLATFORMMETHOD(platform_smp_first_cpu, bare_smp_first_cpu), 8354359Sroberto PLATFORMMETHOD(platform_smp_next_cpu, bare_smp_next_cpu), 8454359Sroberto PLATFORMMETHOD(platform_smp_get_bsp, bare_smp_get_bsp), 8554359Sroberto PLATFORMMETHOD(platform_smp_start_cpu, bare_smp_start_cpu), 8654359Sroberto 8754359Sroberto PLATFORMMETHOD(platform_reset, e500_reset), 8854359Sroberto 8954359Sroberto { 0, 0 } 9054359Sroberto}; 91182007Sroberto 92182007Srobertostatic platform_def_t bare_platform = { 93182007Sroberto "bare metal", 94290000Sglebius bare_methods, 95290000Sglebius 0 96290000Sglebius}; 97290000Sglebius 98290000SglebiusPLATFORM_DEF(bare_platform); 99290000Sglebius 100290000Sglebiusstatic int 101290000Sglebiusbare_probe(platform_t plat) 102290000Sglebius{ 103290000Sglebius uint32_t ver, sr; 104290000Sglebius int i, law_max, tgt; 105290000Sglebius 106290000Sglebius ver = SVR_VER(mfspr(SPR_SVR)); 107290000Sglebius switch (ver & ~0x0008) { /* Mask Security Enabled bit */ 108290000Sglebius case SVR_P4080: 109290000Sglebius maxcpu = 8; 110290000Sglebius break; 111290000Sglebius case SVR_P4040: 112290000Sglebius maxcpu = 4; 113290000Sglebius break; 114290000Sglebius case SVR_MPC8572: 115290000Sglebius case SVR_P1020: 116290000Sglebius case SVR_P2020: 117290000Sglebius maxcpu = 2; 118290000Sglebius break; 119290000Sglebius default: 120290000Sglebius maxcpu = 1; 121290000Sglebius break; 122290000Sglebius } 123290000Sglebius 124290000Sglebius /* 125290000Sglebius * Clear local access windows. Skip DRAM entries, so we don't shoot 126290000Sglebius * ourselves in the foot. 127290000Sglebius */ 128290000Sglebius law_max = law_getmax(); 129290000Sglebius for (i = 0; i < law_max; i++) { 130290000Sglebius sr = ccsr_read4(OCP85XX_LAWSR(i)); 131290000Sglebius if ((sr & 0x80000000) == 0) 132182007Sroberto continue; 133182007Sroberto tgt = (sr & 0x01f00000) >> 20; 134290000Sglebius if (tgt == OCP85XX_TGTIF_RAM1 || tgt == OCP85XX_TGTIF_RAM2 || 135182007Sroberto tgt == OCP85XX_TGTIF_RAM_INTL) 136182007Sroberto continue; 137182007Sroberto 138200576Sroberto ccsr_write4(OCP85XX_LAWSR(i), sr & 0x7fffffff); 139182007Sroberto } 140182007Sroberto 141290000Sglebius return (BUS_PROBE_GENERIC); 142182007Sroberto} 143290000Sglebius 144290000Sglebius#define MEM_REGIONS 8 145290000Sglebiusstatic struct mem_region avail_regions[MEM_REGIONS]; 146290000Sglebius 147290000Sglebiusvoid 148290000Sglebiusbare_mem_regions(platform_t plat, struct mem_region **phys, int *physsz, 149290000Sglebius struct mem_region **avail, int *availsz) 150182007Sroberto{ 151290000Sglebius uint32_t memsize; 152182007Sroberto int i, rv; 153290000Sglebius 154290000Sglebius rv = fdt_get_mem_regions(avail_regions, availsz, &memsize); 155290000Sglebius 156290000Sglebius if (rv != 0) 157290000Sglebius return; 158290000Sglebius 159290000Sglebius for (i = 0; i < *availsz; i++) { 160290000Sglebius if (avail_regions[i].mr_start < 1048576) { 161290000Sglebius avail_regions[i].mr_size = 162290000Sglebius avail_regions[i].mr_size - 163290000Sglebius (1048576 - avail_regions[i].mr_start); 164290000Sglebius avail_regions[i].mr_start = 1048576; 165290000Sglebius } 166290000Sglebius } 167200576Sroberto *avail = avail_regions; 168290000Sglebius 169182007Sroberto /* On the bare metal platform phys == avail memory */ 170200576Sroberto *physsz = *availsz; 171182007Sroberto *phys = *avail; 172182007Sroberto} 173290000Sglebius 174290000Sglebiusstatic u_long 175290000Sglebiusbare_timebase_freq(platform_t plat, struct cpuref *cpuref) 176290000Sglebius{ 177290000Sglebius u_long ticks; 178290000Sglebius phandle_t cpus, child; 179290000Sglebius pcell_t freq; 180290000Sglebius 181182007Sroberto if (bootinfo != NULL) { 182290000Sglebius if (bootinfo[0] == 1) { 183290000Sglebius /* Backward compatibility. See 8-STABLE. */ 184290000Sglebius ticks = bootinfo[3] >> 3; 185290000Sglebius } else { 186290000Sglebius /* Compatibility with Juniper's loader. */ 187290000Sglebius ticks = bootinfo[5] >> 3; 188290000Sglebius } 189182007Sroberto } else 190182007Sroberto ticks = 0; 191290000Sglebius 192290000Sglebius if ((cpus = OF_finddevice("/cpus")) == 0) 193290000Sglebius goto out; 194290000Sglebius 195290000Sglebius if ((child = OF_child(cpus)) == 0) 196290000Sglebius goto out; 197290000Sglebius 198290000Sglebius freq = 0; 199290000Sglebius if (OF_getprop(child, "bus-frequency", (void *)&freq, 200290000Sglebius sizeof(freq)) <= 0) 201290000Sglebius goto out; 202290000Sglebius 203290000Sglebius /* 204290000Sglebius * Time Base and Decrementer are updated every 8 CCB bus clocks. 205290000Sglebius * HID0[SEL_TBCLK] = 0 206290000Sglebius */ 207290000Sglebius if (freq != 0) 208290000Sglebius ticks = freq / 8; 209290000Sglebius 210290000Sglebiusout: 211290000Sglebius if (ticks <= 0) 212290000Sglebius panic("Unable to determine timebase frequency!"); 213182007Sroberto 214182007Sroberto return (ticks); 215290000Sglebius} 216290000Sglebius 217290000Sglebiusstatic int 218182007Srobertobare_smp_first_cpu(platform_t plat, struct cpuref *cpuref) 219132451Sroberto{ 220290000Sglebius 22154359Sroberto cpu = 0; 222290000Sglebius cpuref->cr_cpuid = cpu; 223182007Sroberto cpuref->cr_hwref = cpuref->cr_cpuid; 224132451Sroberto if (bootverbose) 225132451Sroberto printf("powerpc_smp_first_cpu: cpuid %d\n", cpuref->cr_cpuid); 226132451Sroberto cpu++; 22754359Sroberto 228290000Sglebius return (0); 229290000Sglebius} 230290000Sglebius 231290000Sglebiusstatic int 232290000Sglebiusbare_smp_next_cpu(platform_t plat, struct cpuref *cpuref) 233290000Sglebius{ 23454359Sroberto 235132451Sroberto if (cpu >= maxcpu) 23654359Sroberto return (ENOENT); 237290000Sglebius 238290000Sglebius cpuref->cr_cpuid = cpu++; 23954359Sroberto cpuref->cr_hwref = cpuref->cr_cpuid; 240290000Sglebius if (bootverbose) 24154359Sroberto printf("powerpc_smp_next_cpu: cpuid %d\n", cpuref->cr_cpuid); 242132451Sroberto 243290000Sglebius return (0); 244290000Sglebius} 245290000Sglebius 246290000Sglebiusstatic int 247290000Sglebiusbare_smp_get_bsp(platform_t plat, struct cpuref *cpuref) 248290000Sglebius{ 249132451Sroberto 25054359Sroberto cpuref->cr_cpuid = mfspr(SPR_PIR); 251132451Sroberto cpuref->cr_hwref = cpuref->cr_cpuid; 25256746Sroberto 253290000Sglebius return (0); 254290000Sglebius} 255290000Sglebius 256132451Srobertostatic int 257132451Srobertobare_smp_start_cpu(platform_t plat, struct pcpu *pc) 258132451Sroberto{ 259290000Sglebius#ifdef SMP 260132451Sroberto uint32_t bptr, eebpcr; 261106163Sroberto int timeout; 262290000Sglebius 263200576Sroberto eebpcr = ccsr_read4(OCP85XX_EEBPCR); 264290000Sglebius if ((eebpcr & (1 << (pc->pc_cpuid + 24))) != 0) { 265290000Sglebius printf("%s: CPU=%d already out of hold-off state!\n", 266290000Sglebius __func__, pc->pc_cpuid); 267290000Sglebius return (ENXIO); 268290000Sglebius } 269200576Sroberto 270290000Sglebius ap_pcpu = pc; 271290000Sglebius __asm __volatile("msync; isync"); 272290000Sglebius 273200576Sroberto /* 274132451Sroberto * Set BPTR to the physical address of the boot page 275132451Sroberto */ 276132451Sroberto bptr = ((uint32_t)__boot_page - KERNBASE) + kernload_ap; 27782498Sroberto ccsr_write4(OCP85XX_BPTR, (bptr >> 12) | 0x80000000); 278290000Sglebius 279290000Sglebius /* 280290000Sglebius * Release AP from hold-off state 281290000Sglebius */ 282290000Sglebius eebpcr |= (1 << (pc->pc_cpuid + 24)); 283290000Sglebius ccsr_write4(OCP85XX_EEBPCR, eebpcr); 28454359Sroberto __asm __volatile("isync; msync"); 285290000Sglebius 286290000Sglebius timeout = 500; 287290000Sglebius while (!pc->pc_awake && timeout--) 288132451Sroberto DELAY(1000); /* wait 1ms */ 289182007Sroberto 290182007Sroberto return ((pc->pc_awake) ? 0 : EBUSY); 291182007Sroberto#else 29254359Sroberto /* No SMP support */ 293132451Sroberto return (ENXIO); 294182007Sroberto#endif 295290000Sglebius} 296290000Sglebius 29754359Srobertostatic void 298200576Srobertoe500_reset(platform_t plat) 299290000Sglebius{ 300132451Sroberto 301132451Sroberto /* 30254359Sroberto * Try the dedicated reset register first. 303182007Sroberto * If the SoC doesn't have one, we'll fall 30454359Sroberto * back to using the debug control register. 305132451Sroberto */ 30654359Sroberto ccsr_write4(OCP85XX_RSTCR, 2); 30754359Sroberto 308132451Sroberto /* Clear DBCR0, disables debug interrupts and events. */ 309290000Sglebius mtspr(SPR_DBCR0, 0); 31054359Sroberto __asm __volatile("isync"); 31154359Sroberto 31254359Sroberto /* Enable Debug Interrupts in MSR. */ 31382498Sroberto mtmsr(mfmsr() | PSL_DE); 314290000Sglebius 315290000Sglebius /* Enable debug interrupts and issue reset. */ 316200576Sroberto mtspr(SPR_DBCR0, mfspr(SPR_DBCR0) | DBCR0_IDM | DBCR0_RST_SYSTEM); 317290000Sglebius 318290000Sglebius printf("Reset failed...\n"); 319290000Sglebius while (1); 320290000Sglebius} 321290000Sglebius 322290000Sglebius