Add ePAPR boot support for PowerPC book-E (MPC85xx) hardware
Summary: u-boot, following the ePAPR specification, puts secondary cores into a spinloop at boot, rather than leaving them shut off. It then relies on the host OS to write the correct values to a special spin table, located in coherent memory (on newer implementations), or noncoherent memory (older implementations). This supports both implementations of ePAPR, as well as continuing to support non-ePAPR booting, by first attempting to use the spintable, and falling back to expecting non-started CPUs. Test Plan: Booted on a P5020 board. Tested before and after the changes. Before the changes, prints the error "SMP: CPU 1 already out of hold-off state!" and panics shortly thereafter. After the changes, same boot method lets it complete boot. Reviewed by: nwhitehorn MFC after: 2 weeks Relnotes: Yes Sponsored by: Alex Perez/Inertial Computing Differential Revision: https://reviews.freebsd.org/D7494
This commit is contained in:
parent
d8caf56e9e
commit
253902b44c
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=304047
@ -412,6 +412,8 @@ bp_kernload:
|
||||
ori %r3, %r3, (MAS3_SX | MAS3_SW | MAS3_SR)@l
|
||||
mtspr SPR_MAS3, %r3
|
||||
isync
|
||||
bl zero_mas7
|
||||
bl zero_mas8
|
||||
tlbwe
|
||||
isync
|
||||
msync
|
||||
|
@ -39,7 +39,9 @@ __FBSDID("$FreeBSD$");
|
||||
#include <machine/bus.h>
|
||||
#include <machine/cpu.h>
|
||||
#include <machine/hid.h>
|
||||
#include <machine/_inttypes.h>
|
||||
#include <machine/machdep.h>
|
||||
#include <machine/md_var.h>
|
||||
#include <machine/platform.h>
|
||||
#include <machine/platformvar.h>
|
||||
#include <machine/smp.h>
|
||||
@ -53,6 +55,7 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/pmap.h>
|
||||
#include <vm/vm_extern.h>
|
||||
|
||||
#include <powerpc/mpc85xx/mpc85xx.h>
|
||||
|
||||
@ -63,6 +66,15 @@ extern void *ap_pcpu;
|
||||
extern vm_paddr_t kernload; /* Kernel physical load address */
|
||||
extern uint8_t __boot_page[]; /* Boot page body */
|
||||
extern uint32_t bp_kernload;
|
||||
|
||||
struct cpu_release {
|
||||
uint32_t entry_h;
|
||||
uint32_t entry_l;
|
||||
uint32_t r3_h;
|
||||
uint32_t r3_l;
|
||||
uint32_t reserved;
|
||||
uint32_t pir;
|
||||
};
|
||||
#endif
|
||||
|
||||
extern uint32_t *bootinfo;
|
||||
@ -316,6 +328,51 @@ mpc85xx_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
|
||||
return (0);
|
||||
}
|
||||
|
||||
#ifdef SMP
|
||||
static int
|
||||
mpc85xx_smp_start_cpu_epapr(platform_t plat, struct pcpu *pc)
|
||||
{
|
||||
vm_paddr_t rel_pa, bptr;
|
||||
volatile struct cpu_release *rel;
|
||||
vm_offset_t rel_va, rel_page;
|
||||
phandle_t node;
|
||||
int i;
|
||||
|
||||
/* If we're calling this, the node already exists. */
|
||||
node = OF_finddevice("/cpus");
|
||||
for (i = 0, node = OF_child(node); i < pc->pc_cpuid;
|
||||
i++, node = OF_peer(node))
|
||||
;
|
||||
if (OF_getencprop(node, "cpu-release-addr", (pcell_t *)&rel_pa,
|
||||
sizeof(rel_pa)) == -1) {
|
||||
return (ENOENT);
|
||||
}
|
||||
|
||||
rel_page = kva_alloc(PAGE_SIZE);
|
||||
if (rel_page == 0)
|
||||
return (ENOMEM);
|
||||
|
||||
critical_enter();
|
||||
rel_va = rel_page + (rel_pa & PAGE_MASK);
|
||||
pmap_kenter(rel_page, rel_pa & ~PAGE_MASK);
|
||||
rel = (struct cpu_release *)rel_va;
|
||||
bptr = ((vm_paddr_t)(uintptr_t)__boot_page - KERNBASE) + kernload;
|
||||
cpu_flush_dcache(__DEVOLATILE(struct cpu_release *,rel), sizeof(*rel));
|
||||
rel->pir = pc->pc_cpuid; __asm __volatile("sync");
|
||||
rel->entry_h = (bptr >> 32);
|
||||
rel->entry_l = bptr; __asm __volatile("sync");
|
||||
cpu_flush_dcache(__DEVOLATILE(struct cpu_release *,rel), sizeof(*rel));
|
||||
if (bootverbose)
|
||||
printf("Waking up CPU %d via CPU release page %p\n",
|
||||
pc->pc_cpuid, rel);
|
||||
critical_exit();
|
||||
pmap_kremove(rel_page);
|
||||
kva_free(rel_page, PAGE_SIZE);
|
||||
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
mpc85xx_smp_start_cpu(platform_t plat, struct pcpu *pc)
|
||||
{
|
||||
@ -325,6 +382,7 @@ mpc85xx_smp_start_cpu(platform_t plat, struct pcpu *pc)
|
||||
int timeout;
|
||||
uintptr_t brr;
|
||||
int cpuid;
|
||||
int epapr_boot = 0;
|
||||
uint32_t tgt;
|
||||
|
||||
if (mpc85xx_is_qoriq()) {
|
||||
@ -342,6 +400,20 @@ mpc85xx_smp_start_cpu(platform_t plat, struct pcpu *pc)
|
||||
cpuid = pc->pc_cpuid + 24;
|
||||
}
|
||||
bp_kernload = kernload;
|
||||
/*
|
||||
* bp_kernload is in the boot page. Sync the cache because ePAPR
|
||||
* booting has the other core(s) already running.
|
||||
*/
|
||||
__syncicache(&bp_kernload, sizeof(bp_kernload));
|
||||
|
||||
ap_pcpu = pc;
|
||||
__asm __volatile("msync; isync");
|
||||
|
||||
/* First try the ePAPR way. */
|
||||
if (mpc85xx_smp_start_cpu_epapr(plat, pc) == 0) {
|
||||
epapr_boot = 1;
|
||||
goto spin_wait;
|
||||
}
|
||||
|
||||
reg = ccsr_read4(brr);
|
||||
if ((reg & (1 << cpuid)) != 0) {
|
||||
@ -350,9 +422,6 @@ mpc85xx_smp_start_cpu(platform_t plat, struct pcpu *pc)
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
ap_pcpu = pc;
|
||||
__asm __volatile("msync; isync");
|
||||
|
||||
/* Flush caches to have our changes hit DRAM. */
|
||||
cpu_flush_dcache(__boot_page, 4096);
|
||||
|
||||
@ -413,6 +482,7 @@ mpc85xx_smp_start_cpu(platform_t plat, struct pcpu *pc)
|
||||
ccsr_write4(brr, reg | (1 << cpuid));
|
||||
__asm __volatile("isync; msync");
|
||||
|
||||
spin_wait:
|
||||
timeout = 500;
|
||||
while (!pc->pc_awake && timeout--)
|
||||
DELAY(1000); /* wait 1ms */
|
||||
@ -422,11 +492,13 @@ mpc85xx_smp_start_cpu(platform_t plat, struct pcpu *pc)
|
||||
* address (= 0xfffff000) isn't permanently remapped and thus not
|
||||
* usable otherwise.
|
||||
*/
|
||||
if (mpc85xx_is_qoriq())
|
||||
ccsr_write4(OCP85XX_BSTAR, 0);
|
||||
else
|
||||
ccsr_write4(OCP85XX_BPTR, 0);
|
||||
__asm __volatile("isync; msync");
|
||||
if (!epapr_boot) {
|
||||
if (mpc85xx_is_qoriq())
|
||||
ccsr_write4(OCP85XX_BSTAR, 0);
|
||||
else
|
||||
ccsr_write4(OCP85XX_BPTR, 0);
|
||||
__asm __volatile("isync; msync");
|
||||
}
|
||||
|
||||
if (!pc->pc_awake)
|
||||
panic("SMP: CPU %d didn't wake up.\n", pc->pc_cpuid);
|
||||
|
Loading…
Reference in New Issue
Block a user