ppc/pnv: Support LPC host controller irqs other than serirqs

The LPC model has only supported serirqs (ISA device IRQs), however
there are internal sources that can raise other interrupts. Update the
device to handle these interrupt sources.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
This commit is contained in:
Nicholas Piggin 2025-03-01 03:16:29 +10:00
parent d3ce7dc9e2
commit a2dea722cd

View File

@ -456,46 +456,18 @@ static void pnv_lpc_eval_irqs(PnvLpcController *lpc)
{ {
uint32_t active_irqs = 0; uint32_t active_irqs = 0;
if (lpc->lpc_hc_irqstat & PPC_BITMASK32(16, 31)) { active_irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask;
qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented irqs in IRQSTAT: " if (!(lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN)) {
"0x%08"PRIx32"\n", lpc->lpc_hc_irqstat); active_irqs &= ~LPC_HC_IRQ_SERIRQ_ALL;
}
if (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN) {
active_irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask;
} }
/* Reflect the interrupt */ /* Reflect the interrupt */
if (!lpc->psi_has_serirq) { if (lpc->psi_has_serirq) {
/* /*
* POWER8 ORs all irqs together (also with LPCHC internal interrupt * POWER9 and later have routing fields in OPB master registers that
* sources) and outputs a single line that raises the PSI LPCHC irq
* which then latches an OPB IRQ status register that sends the irq
* to PSI.
*
* We don't honor the polarity register, it's pointless and unused
* anyway
*/
if (active_irqs) {
lpc->opb_irq_input |= OPB_MASTER_IRQ_LPC;
} else {
lpc->opb_irq_input &= ~OPB_MASTER_IRQ_LPC;
}
/* Update OPB internal latch */
lpc->opb_irq_stat |= lpc->opb_irq_input & lpc->opb_irq_mask;
qemu_set_irq(lpc->psi_irq_lpchc, lpc->opb_irq_stat != 0);
} else {
/*
* POWER9 and POWER10 have routing fields in OPB master registers that
* send LPC irqs to 4 output lines that raise the PSI SERIRQ irqs. * send LPC irqs to 4 output lines that raise the PSI SERIRQ irqs.
* These don't appear to get latched into an OPB register like the * These don't appear to get latched into an OPB register like the
* LPCHC irqs. * LPCHC irqs.
*
* POWER9 LPC controller internal irqs still go via the OPB
* and LPCHC PSI irqs like P8, but we have no such internal sources
* modelled yet.
*/ */
bool serirq_out[4] = { false, false, false, false }; bool serirq_out[4] = { false, false, false, false };
int irq; int irq;
@ -510,7 +482,33 @@ static void pnv_lpc_eval_irqs(PnvLpcController *lpc)
qemu_set_irq(lpc->psi_irq_serirq[1], serirq_out[1]); qemu_set_irq(lpc->psi_irq_serirq[1], serirq_out[1]);
qemu_set_irq(lpc->psi_irq_serirq[2], serirq_out[2]); qemu_set_irq(lpc->psi_irq_serirq[2], serirq_out[2]);
qemu_set_irq(lpc->psi_irq_serirq[3], serirq_out[3]); qemu_set_irq(lpc->psi_irq_serirq[3], serirq_out[3]);
/*
* POWER9 and later LPC controller internal irqs still go via the OPB
* and LPCHC PSI irqs like P8, so take the SERIRQs out and continue.
*/
active_irqs &= ~LPC_HC_IRQ_SERIRQ_ALL;
} }
/*
* POWER8 ORs all irqs together (also with LPCHC internal interrupt
* sources) and outputs a single line that raises the PSI LPCHC irq
* which then latches an OPB IRQ status register that sends the irq
* to PSI.
*
* We don't honor the polarity register, it's pointless and unused
* anyway
*/
if (active_irqs) {
lpc->opb_irq_input |= OPB_MASTER_IRQ_LPC;
} else {
lpc->opb_irq_input &= ~OPB_MASTER_IRQ_LPC;
}
/* Update OPB internal latch */
lpc->opb_irq_stat |= lpc->opb_irq_input & lpc->opb_irq_mask;
qemu_set_irq(lpc->psi_irq_lpchc, lpc->opb_irq_stat != 0);
} }
static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size) static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size)