Changeset View
Changeset View
Standalone View
Standalone View
sys/powerpc/pseries/xics.c
Show First 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | |||||
#include "phyp-hvcall.h" | #include "phyp-hvcall.h" | ||||
#include "pic_if.h" | #include "pic_if.h" | ||||
#define XICP_PRIORITY 5 /* Random non-zero number */ | #define XICP_PRIORITY 5 /* Random non-zero number */ | ||||
#define XICP_IPI 2 | #define XICP_IPI 2 | ||||
#define MAX_XICP_IRQS (1<<24) /* 24-bit XIRR field */ | #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_probe(device_t); | ||||
static int xicp_attach(device_t); | static int xicp_attach(device_t); | ||||
static int xics_probe(device_t); | static int xics_probe(device_t); | ||||
static int xics_attach(device_t); | static int xics_attach(device_t); | ||||
static void xicp_bind(device_t dev, u_int irq, cpuset_t cpumask); | static void xicp_bind(device_t dev, u_int irq, cpuset_t cpumask); | ||||
static void xicp_dispatch(device_t, struct trapframe *); | static void xicp_dispatch(device_t, struct trapframe *); | ||||
static void xicp_enable(device_t, u_int, u_int); | static void xicp_enable(device_t, u_int, u_int); | ||||
Show All 40 Lines | struct xicp_softc { | ||||
/* XXX: inefficient -- hash table? tree? */ | /* XXX: inefficient -- hash table? tree? */ | ||||
struct { | struct { | ||||
int irq; | int irq; | ||||
int vector; | int vector; | ||||
int cpu; | int cpu; | ||||
} intvecs[256]; | } intvecs[256]; | ||||
int nintvecs; | int nintvecs; | ||||
bool xics_emu; | |||||
}; | }; | ||||
static driver_t xicp_driver = { | static driver_t xicp_driver = { | ||||
"xicp", | "xicp", | ||||
xicp_methods, | xicp_methods, | ||||
sizeof(struct xicp_softc) | sizeof(struct xicp_softc) | ||||
}; | }; | ||||
Show All 28 Lines | xicp_mem_for_cpu(int cpu) | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
#endif | #endif | ||||
static int | static int | ||||
xicp_probe(device_t dev) | 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); | return (ENXIO); | ||||
device_set_desc(dev, "External Interrupt Presentation Controller"); | device_set_desc(dev, "External Interrupt Presentation Controller"); | ||||
return (BUS_PROBE_GENERIC); | return (BUS_PROBE_GENERIC); | ||||
} | } | ||||
static int | static int | ||||
xics_probe(device_t dev) | 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); | return (ENXIO); | ||||
device_set_desc(dev, "External Interrupt Source Controller"); | device_set_desc(dev, "External Interrupt Source Controller"); | ||||
return (BUS_PROBE_GENERIC); | return (BUS_PROBE_GENERIC); | ||||
nwhitehorn: Should we attach at lower priority for opal-xics? It's pretty near the bottom already, but… | |||||
Not Done Inline ActionsI think the XIVE driver should instead attach at BUS_PROBE_DEFAULT, a higher priority than this. jhibbits: I think the XIVE driver should instead attach at BUS_PROBE_DEFAULT, a higher priority than this. | |||||
} | } | ||||
static int | static int | ||||
xicp_attach(device_t dev) | xicp_attach(device_t dev) | ||||
{ | { | ||||
struct xicp_softc *sc = device_get_softc(dev); | struct xicp_softc *sc = device_get_softc(dev); | ||||
phandle_t phandle = ofw_bus_get_node(dev); | phandle_t phandle = ofw_bus_get_node(dev); | ||||
Show All 12 Lines | #endif | ||||
} | } | ||||
if (OF_hasprop(phandle, "ibm,interrupt-server-ranges")) { | if (OF_hasprop(phandle, "ibm,interrupt-server-ranges")) { | ||||
OF_getencprop(phandle, "ibm,interrupt-server-ranges", | OF_getencprop(phandle, "ibm,interrupt-server-ranges", | ||||
sc->cpu_range, sizeof(sc->cpu_range)); | sc->cpu_range, sizeof(sc->cpu_range)); | ||||
sc->cpu_range[1] += sc->cpu_range[0]; | sc->cpu_range[1] += sc->cpu_range[0]; | ||||
device_printf(dev, "Handling CPUs %d-%d\n", sc->cpu_range[0], | device_printf(dev, "Handling CPUs %d-%d\n", sc->cpu_range[0], | ||||
sc->cpu_range[1]-1); | 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); | |||||
Not Done Inline ActionsJustin, I am not able to find the declaration of OPAL_XIVE_RESET. I just rebased my tree, and I still do not find this declaration. The same thing seems to happen with OPAL_INT_GET_XIRR, OPAL_INT_EOI , OPAL_INT_SET_MFRR and OPAL_INT_SET_CPPR. Where did these definitions come from? breno.leitao_gmail.com: Justin,
I am not able to find the declaration of OPAL_XIVE_RESET. I just rebased my tree, and… | |||||
#endif | |||||
} else { | } else { | ||||
sc->cpu_range[0] = 0; | sc->cpu_range[0] = 0; | ||||
sc->cpu_range[1] = mp_ncpus; | sc->cpu_range[1] = mp_ncpus; | ||||
} | } | ||||
#ifdef POWERNV | #ifdef POWERNV | ||||
if (mfmsr() & PSL_HV) { | if (mfmsr() & PSL_HV) { | ||||
int i; | int i; | ||||
Not Done Inline ActionsThis needs #ifdef POWERNV. nwhitehorn: This needs #ifdef POWERNV. | |||||
Not Done Inline ActionsWoops, fixing. jhibbits: Woops, fixing. | |||||
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++) { | for (i = 0; i < sc->cpu_range[1] - sc->cpu_range[0]; i++) { | ||||
sc->mem[i] = bus_alloc_resource_any(dev, SYS_RES_MEMORY, | sc->mem[i] = bus_alloc_resource_any(dev, SYS_RES_MEMORY, | ||||
&i, RF_ACTIVE); | &i, RF_ACTIVE); | ||||
if (sc->mem[i] == NULL) { | if (sc->mem[i] == NULL) { | ||||
device_printf(dev, "Could not alloc mem " | device_printf(dev, "Could not alloc mem " | ||||
"resource %d\n", i); | "resource %d\n", i); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
/* Unmask interrupts on all cores */ | /* Unmask interrupts on all cores */ | ||||
bus_write_1(sc->mem[i], 4, 0xff); | bus_write_1(sc->mem[i], 4, 0xff); | ||||
bus_write_1(sc->mem[i], 12, 0xff); | bus_write_1(sc->mem[i], 12, 0xff); | ||||
} | } | ||||
} | } | ||||
} | |||||
#endif | #endif | ||||
mtx_init(&sc->sc_mtx, "XICP", NULL, MTX_DEF); | mtx_init(&sc->sc_mtx, "XICP", NULL, MTX_DEF); | ||||
sc->nintvecs = 0; | sc->nintvecs = 0; | ||||
powerpc_register_pic(dev, OF_xref_from_node(phandle), MAX_XICP_IRQS, | powerpc_register_pic(dev, OF_xref_from_node(phandle), MAX_XICP_IRQS, | ||||
1 /* Number of IPIs */, FALSE); | 1 /* Number of IPIs */, FALSE); | ||||
root_pic = dev; | root_pic = dev; | ||||
▲ Show 20 Lines • Show All 72 Lines • ▼ Show 20 Lines | |||||
static void | static void | ||||
xicp_dispatch(device_t dev, struct trapframe *tf) | xicp_dispatch(device_t dev, struct trapframe *tf) | ||||
{ | { | ||||
struct xicp_softc *sc; | struct xicp_softc *sc; | ||||
struct resource *regs = NULL; | struct resource *regs = NULL; | ||||
uint64_t xirr, junk; | uint64_t xirr, junk; | ||||
int i; | int i; | ||||
printf("Dispatch!\n"); | |||||
#ifdef POWERNV | #ifdef POWERNV | ||||
if (mfmsr() & PSL_HV) { | if (mfmsr() & PSL_HV) { | ||||
regs = xicp_mem_for_cpu(PCPU_GET(hwref)); | regs = xicp_mem_for_cpu(PCPU_GET(hwref)); | ||||
KASSERT(regs != NULL, | KASSERT(regs != NULL, | ||||
("Can't find regs for CPU %ld", (uintptr_t)PCPU_GET(hwref))); | ("Can't find regs for CPU %ld", (uintptr_t)PCPU_GET(hwref))); | ||||
} | } | ||||
#endif | #endif | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
for (;;) { | for (;;) { | ||||
/* Return value in R4, use the PFT call */ | /* Return value in R4, use the PFT call */ | ||||
if (regs) { | if (regs) { | ||||
xirr = bus_read_4(regs, 4); | xirr = bus_read_4(regs, 4); | ||||
#ifdef POWERNV | |||||
} else if (sc->xics_emu) { | |||||
opal_call(OPAL_INT_GET_XIRR, vtophys(&xirr), false); | |||||
#endif | |||||
} else { | } else { | ||||
/* Return value in R4, use the PFT call */ | /* Return value in R4, use the PFT call */ | ||||
phyp_pft_hcall(H_XIRR, 0, 0, 0, 0, &xirr, &junk, &junk); | phyp_pft_hcall(H_XIRR, 0, 0, 0, 0, &xirr, &junk, &junk); | ||||
} | } | ||||
xirr &= 0x00ffffff; | xirr &= 0x00ffffff; | ||||
if (xirr == 0) { /* No more pending interrupts? */ | if (xirr == 0) { /* No more pending interrupts? */ | ||||
if (regs) | if (regs) | ||||
bus_write_1(regs, 4, 0xff); | bus_write_1(regs, 4, 0xff); | ||||
#ifdef POWERNV | |||||
else if (sc->xics_emu) | |||||
opal_call(OPAL_INT_SET_CPPR, 0xff); | |||||
#endif | |||||
else | else | ||||
phyp_hcall(H_CPPR, (uint64_t)0xff); | phyp_hcall(H_CPPR, (uint64_t)0xff); | ||||
break; | break; | ||||
} | } | ||||
if (xirr == XICP_IPI) { /* Magic number for IPIs */ | if (xirr == XICP_IPI) { /* Magic number for IPIs */ | ||||
xirr = MAX_XICP_IRQS; /* Map to FreeBSD magic */ | xirr = MAX_XICP_IRQS; /* Map to FreeBSD magic */ | ||||
/* Clear IPI */ | /* Clear IPI */ | ||||
if (regs) | if (regs) | ||||
bus_write_1(regs, 12, 0xff); | 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 | else | ||||
phyp_hcall(H_IPI, (uint64_t)(PCPU_GET(hwref)), | phyp_hcall(H_IPI, (uint64_t)(PCPU_GET(hwref)), | ||||
0xff); | 0xff); | ||||
} | } | ||||
/* XXX: super inefficient */ | /* XXX: super inefficient */ | ||||
for (i = 0; i < sc->nintvecs; i++) { | for (i = 0; i < sc->nintvecs; i++) { | ||||
if (sc->intvecs[i].irq == xirr) | if (sc->intvecs[i].irq == xirr) | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | if (status != 0) | ||||
cpu, status); | cpu, status); | ||||
#endif | #endif | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
xicp_eoi(device_t dev, u_int irq) | xicp_eoi(device_t dev, u_int irq) | ||||
{ | { | ||||
#ifdef POWERNV | |||||
struct xicp_softc *sc; | |||||
#endif | |||||
uint64_t xirr; | uint64_t xirr; | ||||
if (irq == MAX_XICP_IRQS) /* Remap IPI interrupt to internal value */ | if (irq == MAX_XICP_IRQS) /* Remap IPI interrupt to internal value */ | ||||
irq = XICP_IPI; | irq = XICP_IPI; | ||||
xirr = irq | (XICP_PRIORITY << 24); | xirr = irq | (XICP_PRIORITY << 24); | ||||
#ifdef POWERNV | #ifdef POWERNV | ||||
if (mfmsr() & PSL_HV) | if (mfmsr() & PSL_HV) { | ||||
bus_write_4(xicp_mem_for_cpu(PCPU_GET(hwref)), 4, xirr); | sc = device_get_softc(dev); | ||||
if (sc->xics_emu) | |||||
device_printf(dev, "Setting EOI for IRQ %u\n", irq); | |||||
if (sc->xics_emu) | |||||
opal_call(OPAL_INT_EOI, xirr); | |||||
else | else | ||||
bus_write_4(xicp_mem_for_cpu(PCPU_GET(hwref)), 4, xirr); | |||||
} else | |||||
#endif | #endif | ||||
phyp_hcall(H_EOI, xirr); | phyp_hcall(H_EOI, xirr); | ||||
} | } | ||||
static void | static void | ||||
xicp_ipi(device_t dev, u_int cpu) | xicp_ipi(device_t dev, u_int cpu) | ||||
{ | { | ||||
#ifdef POWERNV | #ifdef POWERNV | ||||
struct xicp_softc *sc; | |||||
cpu = pcpu_find(cpu)->pc_hwref; | cpu = pcpu_find(cpu)->pc_hwref; | ||||
if (mfmsr() & PSL_HV) | if (mfmsr() & PSL_HV) { | ||||
sc = device_get_softc(dev); | |||||
if (sc->xics_emu) | |||||
device_printf(dev, "Sending IPI to %d\n", cpu); | |||||
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); | bus_write_1(xicp_mem_for_cpu(cpu), 12, XICP_PRIORITY); | ||||
else | } else | ||||
#endif | #endif | ||||
phyp_hcall(H_IPI, (uint64_t)cpu, XICP_PRIORITY); | phyp_hcall(H_IPI, (uint64_t)cpu, XICP_PRIORITY); | ||||
} | } | ||||
static void | static void | ||||
xicp_mask(device_t dev, u_int irq) | xicp_mask(device_t dev, u_int irq) | ||||
{ | { | ||||
struct xicp_softc *sc = device_get_softc(dev); | struct xicp_softc *sc = device_get_softc(dev); | ||||
Show All 29 Lines | if (irq == MAX_XICP_IRQS) | ||||
return; | return; | ||||
if (rtas_exists()) { | if (rtas_exists()) { | ||||
rtas_call_method(sc->ibm_int_on, 1, 1, irq, &status); | rtas_call_method(sc->ibm_int_on, 1, 1, irq, &status); | ||||
#ifdef POWERNV | #ifdef POWERNV | ||||
} else { | } else { | ||||
int i; | int i; | ||||
printf("Unmask %u\n", irq); | |||||
for (i = 0; i < sc->nintvecs; i++) { | for (i = 0; i < sc->nintvecs; i++) { | ||||
if (sc->intvecs[i].irq == irq) { | if (sc->intvecs[i].irq == irq) { | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
KASSERT(i < sc->nintvecs, ("Unmasking unconfigured interrupt")); | KASSERT(i < sc->nintvecs, ("Unmasking unconfigured interrupt")); | ||||
opal_call(OPAL_SET_XIVE, irq, sc->intvecs[i].cpu << 2, | opal_call(OPAL_SET_XIVE, irq, sc->intvecs[i].cpu << 2, | ||||
XICP_PRIORITY); | XICP_PRIORITY); | ||||
#endif | #endif | ||||
} | } | ||||
} | } | ||||
Should we attach at lower priority for opal-xics? It's pretty near the bottom already, but maybe should be adjusted?