On Fri, 16 Oct 2026 18:10:52 +0800 Jinjie Ruan <[email protected]> wrote:
> From: Jinjie Ruan <[email protected]> > > Add the two sysbus irq outputs ([0] error, [1] overflow) with the > IHI 0099 level semantics: the error line is active while > MPAMF_ESR.ERR_CODE is non-zero and MPAMF_ECR.INTEN is set (8.2), > deasserted by writing ERRCODE zero; the overflow line is signalled > while any MBWU monitor has latched OFLOW_STATUS with OFLOW_INTR > set (8.3), deasserted by the write-one-to-clear of every overflowed > monitor. Counter overflow latches the status exactly once, and > device reset deasserts both lines. > > Signed-off-by: Jinjie Ruan <[email protected]> This seems to only send interrupts if someone reads the register? Thus no interrupts if no reading is going on. That seems odd or is there a thread or timer making sure we still get those interrupts? Jonathan > --- > hw/arm/mpam.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 55 insertions(+), 1 deletion(-) > > diff --git a/hw/arm/mpam.c b/hw/arm/mpam.c > index c4a7567..0389dad 100644 > --- a/hw/arm/mpam.c > +++ b/hw/arm/mpam.c > @@ -361,6 +361,10 @@ typedef struct MPAMMSCState { > uint32_t ecr; > uint64_t esr; > > + /* Wired interrupt outputs: [0] error, [1] overflow (level) */ > + qemu_irq err_irq; > + qemu_irq oflow_irq; > + > /* > * Wired-interrupt GSIVs the board assigned (INTID = SPI + 32). Zero > * means "no wired interrupt"; the ACPI MPAM table reports them as-is. > @@ -431,6 +435,43 @@ static void mpam_msc_sync_esr_ecr(MPAMMSCState *s) > } > } > > +/* > + * IHI 0099 8.2: the MPAM error interrupt is level-sensitive; it is active > + * while MPAMF_ESR.ERR_CODE is non-zero and MPAMF_ECR.INTEN is set, and is > + * deactivated by software writing ERRCODE to zero. > + */ > +static void mpam_msc_update_err_irq(MPAMMSCState *s) > +{ > + bool int_en = FIELD_EX32(s->ecr, MPAMF_ECR, INTEN); > + bool active = FIELD_EX64(s->esr, MPAMF_ESR, ERR_CODE) != 0; > + > + qemu_set_irq(s->err_irq, int_en && active); > +} > + > +/* > + * IHI 0099 8.3: the overflow interrupt is signalled while any monitor has > + * latched OFLOW_STATUS with OFLOW_INTR set; clearing OFLOW_STATUS of all > + * overflowed monitors (W1C) deasserts it. > + */ > +static void mpam_msc_update_oflow_irq(MPAMMSCState *s) > +{ > + bool active = false; > + int i, m; > + > + for (i = 0; i < s->num_ris && !active; i++) { > + for (m = 0; m < s->num_mbwu_mon; m++) { > + MpamMbwuMon *mon = &s->mpamf[i].mbwu_mons[m]; > + > + if (mon->oflow && > + FIELD_EX32(mon->ctl, MSMON_CFG_MBWU_CTL, OFLOW_INTR)) { > + active = true; > + break; > + } > + } > + } > + qemu_set_irq(s->oflow_irq, active); > +} > + > static Mpamf *mpam_mon_ris(MPAMMSCState *s) > { > unsigned ris = FIELD_EX32(s->mon_sel, MSMON_CFG_MON_SEL, RIS); > @@ -506,7 +547,10 @@ static uint64_t mpam_mbwu_value(MPAMMSCState *s, Mpamf > *mf, MpamMbwuMon *m) > v += muldiv64(delta, rate_mbs, 1000); > /* 48-bit counter wraps (MSMON_MBWU_L is 48 bits of value). */ > if (v > (1ULL << 48)) { > - m->oflow = true; > + if (!m->oflow) { > + m->oflow = true; > + mpam_msc_update_oflow_irq(s); > + } > v &= (1ULL << 48) - 1; > } > } > @@ -966,6 +1010,7 @@ static void mpam_msc_write_reg(void *opaque, hwaddr > offset, uint64_t value, > if (value & R_MSMON_CFG_MBWU_CTL_OFLOW_STATUS_MASK) { > m->oflow = false; /* write-one-to-clear */ > } > + mpam_msc_update_oflow_irq(s); > return; > } > case A_MSMON_MBWU: > @@ -1006,6 +1051,7 @@ static void mpam_msc_write_reg(void *opaque, hwaddr > offset, uint64_t value, > } > s->ecr = value & MPAMF_ECR_WRITE_MASK; > mpam_msc_sync_esr_ecr(s); > + mpam_msc_update_err_irq(s); > return; > case A_MPAMF_ESR: > /* > @@ -1014,10 +1060,12 @@ static void mpam_msc_write_reg(void *opaque, hwaddr > offset, uint64_t value, > */ > s->esr = deposit64(s->esr, 0, 32, value); > mpam_msc_sync_esr_ecr(s); > + mpam_msc_update_err_irq(s); > return; > case A_MPAMF_ESR + 0x4: > s->esr = deposit64(s->esr, 32, 32, value); > mpam_msc_sync_esr_ecr(s); > + mpam_msc_update_err_irq(s); > return; > default: > qemu_log_mask(LOG_UNIMP, > @@ -1096,6 +1144,9 @@ static void mpam_msc_reset(DeviceState *dev) > MPAMMSCState *s = MPAM_MSC_DEVICE(dev); > > mpam_msc_state_init(s); > + /* Deassert the wired interrupt lines */ > + mpam_msc_update_err_irq(s); > + mpam_msc_update_oflow_irq(s); > } > > static void mpam_msc_realize(DeviceState *dev, Error **errp) > @@ -1128,6 +1179,9 @@ static void mpam_msc_realize(DeviceState *dev, Error > **errp) > memory_region_init_io(&s->mr, OBJECT(s), &mpam_msc_ops, s, "mpam_msc", > MPAM_SIZE); > sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr); > + /* Wired interrupt outputs: index 0 = error, index 1 = overflow */ > + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->err_irq); > + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->oflow_irq); > } > > static void mpam_msc_mem_realize(DeviceState *dev, Error **errp)
