Add support for the XIVE XICS emulation mode for POWER9 systems

Summary:
POWER9 systems use a new interrupt controller, XIVE, managed through OPAL
firmware calls.  The OPAL firmware includes support for emulating the previous
generation XICS presentation layer in addition to a new "XIVE Exploitation"
mode.  As a stopgap until we have XIVE exploitation mode, enable XICS emulation
mode so that we at least have an interrupt controller.

Since the CPPR is local to the current CPU, it cannot be updated for APs when
initializing on the BSP.  This adds a new function, directly called by the
powernv platform code, to initialize the CPPR on AP bringup.

Reviewed by:	nwhitehorn
Differential Revision: https://reviews.freebsd.org/D15492
This commit is contained in:
Justin Hibbits 2018-05-20 03:23:17 +00:00
parent 056b40e29c
commit ef6da5e5c7
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=333912
7 changed files with 121 additions and 35 deletions

View File

@ -96,7 +96,7 @@ cpudep_ap_early_bootstrap(void)
mtspr(SPR_LPID, 0);
isync();
mtspr(SPR_LPCR, LPCR_LPES);
mtspr(SPR_LPCR, lpcr);
isync();
}
#endif
@ -401,7 +401,7 @@ cpudep_ap_setup()
case IBMPOWER9:
#ifdef __powerpc64__
if (mfmsr() & PSL_HV) {
mtspr(SPR_LPCR, mfspr(SPR_LPCR) | LPCR_LPES |
mtspr(SPR_LPCR, mfspr(SPR_LPCR) | lpcr |
LPCR_PECE_WAKESET);
isync();
}

View File

@ -135,6 +135,7 @@ extern char etext[];
#ifdef __powerpc64__
extern void enter_idle_powerx(void);
extern uint64_t can_wakeup;
extern register_t lpcr;
#endif
void cpu_halt(void);

View File

@ -215,14 +215,6 @@
#define FSL_E300C3 0x8085
#define FSL_E300C4 0x8086
#define SPR_LPCR 0x13e /* Logical Partitioning Control */
#define LPCR_LPES 0x008 /* Bit 60 */
#define LPCR_PECE_DRBL (1ULL << 16) /* Directed Privileged Doorbell */
#define LPCR_PECE_HDRBL (1ULL << 15) /* Directed Hypervisor Doorbell */
#define LPCR_PECE_EXT (1ULL << 14) /* External exceptions */
#define LPCR_PECE_DECR (1ULL << 13) /* Decrementer exceptions */
#define LPCR_PECE_ME (1ULL << 12) /* Machine Check and Hypervisor */
/* Maintenance exceptions */
#define LPCR_PECE_WAKESET (LPCR_PECE_EXT | LPCR_PECE_DECR | LPCR_PECE_ME)
#define SPR_EPCR 0x133
@ -242,7 +234,14 @@
#define SPR_HSRR0 0x13a
#define SPR_HSRR1 0x13b
#define SPR_LPCR 0x13e /* Logical Partitioning Control */
#define LPCR_LPES 0x008 /* Bit 60 */
#define LPCR_LPES 0x008 /* Bit 60 */
#define LPCR_HVICE 0x002 /* Hypervisor Virtualization Interrupt (Arch 3.0) */
#define LPCR_PECE_DRBL (1ULL << 16) /* Directed Privileged Doorbell */
#define LPCR_PECE_HDRBL (1ULL << 15) /* Directed Hypervisor Doorbell */
#define LPCR_PECE_EXT (1ULL << 14) /* External exceptions */
#define LPCR_PECE_DECR (1ULL << 13) /* Decrementer exceptions */
#define LPCR_PECE_ME (1ULL << 12) /* Machine Check and Hypervisor */
/* Maintenance exceptions */
#define SPR_LPID 0x13f /* Logical Partitioning Control */
#define SPR_PTCR 0x1d0 /* Partition Table Control Register */

View File

@ -73,7 +73,12 @@ int opal_call(uint64_t token, ...);
#define OPAL_REINIT_CPUS 70
#define OPAL_CHECK_ASYNC_COMPLETION 86
#define OPAL_I2C_REQUEST 109
#define OPAL_INT_GET_XIRR 122
#define OPAL_INT_SET_CPPR 123
#define OPAL_INT_EOI 124
#define OPAL_INT_SET_MFRR 125
#define OPAL_PCI_TCE_KILL 126
#define OPAL_XIVE_RESET 128
/* For OPAL_PCI_SET_PE */
#define OPAL_UNMAP_PE 0

View File

@ -59,6 +59,7 @@ __FBSDID("$FreeBSD$");
extern void *ap_pcpu;
#endif
extern void xicp_smp_cpu_startup(void);
static int powernv_probe(platform_t);
static int powernv_attach(platform_t);
void powernv_mem_regions(platform_t, struct mem_region *phys, int *physsz,
@ -152,14 +153,14 @@ powernv_attach(platform_t plat)
mtspr(SPR_LPID, 0);
isync();
mtspr(SPR_LPCR, LPCR_LPES);
if (cpu_features2 & PPC_FEATURE2_ARCH_3_00)
lpcr |= LPCR_HVICE;
mtspr(SPR_LPCR, lpcr);
isync();
mtmsr(msr);
/* Init CPU bits */
powernv_smp_ap_init(plat);
powernv_cpuref_init();
/* Set SLB count from device tree */
@ -460,6 +461,8 @@ powernv_reset(platform_t platform)
static void
powernv_smp_ap_init(platform_t platform)
{
xicp_smp_cpu_startup();
}
static void

View File

@ -240,6 +240,10 @@ SYSCTL_OPAQUE(_hw, OID_AUTO, cpu_features, CTLFLAG_RD,
SYSCTL_OPAQUE(_hw, OID_AUTO, cpu_features2, CTLFLAG_RD,
&cpu_features2, sizeof(cpu_features2), "LX", "PowerPC CPU features 2");
#ifdef __powerpc64__
register_t lpcr = LPCR_LPES;
#endif
/* Provide some user-friendly aliases for bits in cpu_features */
SYSCTL_PROC(_hw, OID_AUTO, floatingpoint, CTLTYPE_INT | CTLFLAG_RD,
0, PPC_FEATURE_HAS_FPU, cpu_feature_bit, "I",

View File

@ -61,6 +61,9 @@ __FBSDID("$FreeBSD$");
#define XICP_IPI 2
#define MAX_XICP_IRQS (1<<24) /* 24-bit XIRR field */
#define XIVE_XICS_MODE_EMU 0
#define XIVE_XICS_MODE_EXP 1
static int xicp_probe(device_t);
static int xicp_attach(device_t);
static int xics_probe(device_t);
@ -74,6 +77,10 @@ static void xicp_ipi(device_t, u_int);
static void xicp_mask(device_t, u_int);
static void xicp_unmask(device_t, u_int);
#ifdef POWERNV
void xicp_smp_cpu_startup(void);
#endif
static device_method_t xicp_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, xicp_probe),
@ -117,6 +124,7 @@ struct xicp_softc {
int cpu;
} intvecs[256];
int nintvecs;
bool xics_emu;
};
static driver_t xicp_driver = {
@ -131,6 +139,8 @@ static driver_t xics_driver = {
0
};
static uint32_t cpu_xirr[MAXCPU];
static devclass_t xicp_devclass;
static devclass_t xics_devclass;
@ -161,7 +171,8 @@ static int
xicp_probe(device_t dev)
{
if (!ofw_bus_is_compatible(dev, "ibm,ppc-xicp"))
if (!ofw_bus_is_compatible(dev, "ibm,ppc-xicp") &&
!ofw_bus_is_compatible(dev, "ibm,opal-intc"))
return (ENXIO);
device_set_desc(dev, "External Interrupt Presentation Controller");
@ -172,7 +183,8 @@ static int
xics_probe(device_t dev)
{
if (!ofw_bus_is_compatible(dev, "ibm,ppc-xics"))
if (!ofw_bus_is_compatible(dev, "ibm,ppc-xics") &&
!ofw_bus_is_compatible(dev, "IBM,opal-xics"))
return (ENXIO);
device_set_desc(dev, "External Interrupt Source Controller");
@ -205,6 +217,15 @@ xicp_attach(device_t dev)
sc->cpu_range[1] += sc->cpu_range[0];
device_printf(dev, "Handling CPUs %d-%d\n", sc->cpu_range[0],
sc->cpu_range[1]-1);
#ifdef POWERNV
} else if (ofw_bus_is_compatible(dev, "ibm,opal-intc")) {
/*
* For now run POWER9 XIVE interrupt controller in XICS
* compatibility mode.
*/
sc->xics_emu = true;
opal_call(OPAL_XIVE_RESET, XIVE_XICS_MODE_EMU);
#endif
} else {
sc->cpu_range[0] = 0;
sc->cpu_range[1] = mp_ncpus;
@ -214,18 +235,26 @@ xicp_attach(device_t dev)
if (mfmsr() & PSL_HV) {
int i;
for (i = 0; i < sc->cpu_range[1] - sc->cpu_range[0]; i++) {
sc->mem[i] = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
&i, RF_ACTIVE);
if (sc->mem[i] == NULL) {
device_printf(dev, "Could not alloc mem "
"resource %d\n", i);
return (ENXIO);
if (sc->xics_emu) {
opal_call(OPAL_INT_SET_CPPR, 0xff);
for (i = 0; i < mp_ncpus; i++) {
opal_call(OPAL_INT_SET_MFRR,
pcpu_find(i)->pc_hwref, 0xff);
}
} else {
for (i = 0; i < sc->cpu_range[1] - sc->cpu_range[0]; i++) {
sc->mem[i] = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
&i, RF_ACTIVE);
if (sc->mem[i] == NULL) {
device_printf(dev, "Could not alloc mem "
"resource %d\n", i);
return (ENXIO);
}
/* Unmask interrupts on all cores */
bus_write_1(sc->mem[i], 4, 0xff);
bus_write_1(sc->mem[i], 12, 0xff);
/* Unmask interrupts on all cores */
bus_write_1(sc->mem[i], 4, 0xff);
bus_write_1(sc->mem[i], 12, 0xff);
}
}
}
#endif
@ -316,19 +345,25 @@ xicp_dispatch(device_t dev, struct trapframe *tf)
uint64_t xirr, junk;
int i;
sc = device_get_softc(dev);
#ifdef POWERNV
if (mfmsr() & PSL_HV) {
if ((mfmsr() & PSL_HV) && !sc->xics_emu) {
regs = xicp_mem_for_cpu(PCPU_GET(hwref));
KASSERT(regs != NULL,
("Can't find regs for CPU %ld", (uintptr_t)PCPU_GET(hwref)));
}
#endif
sc = device_get_softc(dev);
for (;;) {
/* Return value in R4, use the PFT call */
if (regs) {
xirr = bus_read_4(regs, 4);
#ifdef POWERNV
} else if (sc->xics_emu) {
opal_call(OPAL_INT_GET_XIRR, &cpu_xirr[PCPU_GET(cpuid)],
false);
xirr = cpu_xirr[PCPU_GET(cpuid)];
#endif
} else {
/* Return value in R4, use the PFT call */
phyp_pft_hcall(H_XIRR, 0, 0, 0, 0, &xirr, &junk, &junk);
@ -338,6 +373,10 @@ xicp_dispatch(device_t dev, struct trapframe *tf)
if (xirr == 0) { /* No more pending interrupts? */
if (regs)
bus_write_1(regs, 4, 0xff);
#ifdef POWERNV
else if (sc->xics_emu)
opal_call(OPAL_INT_SET_CPPR, 0xff);
#endif
else
phyp_hcall(H_CPPR, (uint64_t)0xff);
break;
@ -348,6 +387,11 @@ xicp_dispatch(device_t dev, struct trapframe *tf)
/* Clear IPI */
if (regs)
bus_write_1(regs, 12, 0xff);
#ifdef POWERNV
else if (sc->xics_emu)
opal_call(OPAL_INT_SET_MFRR,
PCPU_GET(hwref), 0xff);
#endif
else
phyp_hcall(H_IPI, (uint64_t)(PCPU_GET(hwref)),
0xff);
@ -409,6 +453,9 @@ xicp_enable(device_t dev, u_int irq, u_int vector)
static void
xicp_eoi(device_t dev, u_int irq)
{
#ifdef POWERNV
struct xicp_softc *sc;
#endif
uint64_t xirr;
if (irq == MAX_XICP_IRQS) /* Remap IPI interrupt to internal value */
@ -416,9 +463,13 @@ xicp_eoi(device_t dev, u_int irq)
xirr = irq | (XICP_PRIORITY << 24);
#ifdef POWERNV
if (mfmsr() & PSL_HV)
bus_write_4(xicp_mem_for_cpu(PCPU_GET(hwref)), 4, xirr);
else
if (mfmsr() & PSL_HV) {
sc = device_get_softc(dev);
if (sc->xics_emu)
opal_call(OPAL_INT_EOI, xirr);
else
bus_write_4(xicp_mem_for_cpu(PCPU_GET(hwref)), 4, xirr);
} else
#endif
phyp_hcall(H_EOI, xirr);
}
@ -428,11 +479,19 @@ xicp_ipi(device_t dev, u_int cpu)
{
#ifdef POWERNV
struct xicp_softc *sc;
cpu = pcpu_find(cpu)->pc_hwref;
if (mfmsr() & PSL_HV)
bus_write_1(xicp_mem_for_cpu(cpu), 12, XICP_PRIORITY);
else
if (mfmsr() & PSL_HV) {
sc = device_get_softc(dev);
if (sc->xics_emu) {
int64_t rv;
rv = opal_call(OPAL_INT_SET_MFRR, cpu, XICP_PRIORITY);
if (rv != 0)
device_printf(dev, "IPI SET_MFRR result: %ld\n", rv);
} else
bus_write_1(xicp_mem_for_cpu(cpu), 12, XICP_PRIORITY);
} else
#endif
phyp_hcall(H_IPI, (uint64_t)cpu, XICP_PRIORITY);
}
@ -490,3 +549,18 @@ xicp_unmask(device_t dev, u_int irq)
}
}
#ifdef POWERNV
/* This is only used on POWER9 systems with the XIVE's XICS emulation. */
void
xicp_smp_cpu_startup(void)
{
struct xicp_softc *sc;
if (mfmsr() & PSL_HV) {
sc = device_get_softc(root_pic);
if (sc->xics_emu)
opal_call(OPAL_INT_SET_CPPR, 0xff);
}
}
#endif