Re: [PATCH v3 2/8] iommu/riscv: Fix WSI mode IRQ number handling

Andrew Jones
2026-02-04T17:20:08Z

On Wed, Feb 04, 2026 at 05:08:52PM +0800, Lv Zheng wrote:
> From: Jingyu Li <joey.li@spacemit.com>
>
> In WSI mode, ICVEC doesn't exist, thus reading it returns 0, which
> causes IOMMU driver to fail to find IRQ numbers from device tree
> IRQ arrary. The issue is fixed by applying icvec indexes of WSI IRQs.

ICVEC always exists, however it may be hardwired to zero when an
implementation only supports a single vector. But, that has nothing
to do with whether wired interrupts or MSIs are used.

If ICVEC on this IOMMU is always reading as zero, even when 0xf is
written to it first, then it should be interpreted as there only
being a single vector (or that the IOMMU's ICVEC is broken, if the
number of sources is known to be more).

>
> Signed-off-by: Jingyu Li <joey.li@spacemit.com>
> Signed-off-by: Lv Zheng <lv.zheng@linux.spacemit.com>
> ---
> drivers/iommu/riscv/iommu.c | 25 ++++++++++++++++++++-----
> 1 file changed, 20 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> index d9429097a2b5..26630979473b 100644
> --- a/drivers/iommu/riscv/iommu.c
> +++ b/drivers/iommu/riscv/iommu.c
> @@ -1593,11 +1593,26 @@ static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
> FIELD_PREP(RISCV_IOMMU_ICVEC_PMIV, 3 % iommu->irqs_count);
> riscv_iommu_writeq(iommu, RISCV_IOMMU_REG_ICVEC, iommu->icvec);
> iommu->icvec = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_ICVEC);
> - if (max(max(FIELD_GET(RISCV_IOMMU_ICVEC_CIV, iommu->icvec),
> - FIELD_GET(RISCV_IOMMU_ICVEC_FIV, iommu->icvec)),
> - max(FIELD_GET(RISCV_IOMMU_ICVEC_PIV, iommu->icvec),
> - FIELD_GET(RISCV_IOMMU_ICVEC_PMIV, iommu->icvec))) >= iommu->irqs_count)
> - return -EINVAL;
> + /*
> + * In WSI mode, ICVEC may read as zero. Only validate if using MSI.
> + * Check if FCTL.WSI is set to determine interrupt mode.
> + */
> + if (!(iommu->fctl & RISCV_IOMMU_FCTL_WSI)) {

The behavior of ICVEC does not depend on FCTL.WSI

> + if (max(max(FIELD_GET(RISCV_IOMMU_ICVEC_CIV, iommu->icvec),
> + FIELD_GET(RISCV_IOMMU_ICVEC_FIV, iommu->icvec)),
> + max(FIELD_GET(RISCV_IOMMU_ICVEC_PIV, iommu->icvec),
> + FIELD_GET(RISCV_IOMMU_ICVEC_PMIV, iommu->icvec))) >= iommu->irqs_count)
> + return -EINVAL;
> + } else {
> + /*
> + * WSI mode: ICVEC is not used. Set to identity mapping for
> + * riscv_iommu_queue_vec() to work correctly.
> + */
> + iommu->icvec = FIELD_PREP(RISCV_IOMMU_ICVEC_CIV, 0) |
> + FIELD_PREP(RISCV_IOMMU_ICVEC_FIV, 1) |
> + FIELD_PREP(RISCV_IOMMU_ICVEC_PIV, 2) |
> + FIELD_PREP(RISCV_IOMMU_ICVEC_PMIV, 3);

It's certainly not correct to set iommu->icvec to anything that can't be
written to the IOMMU's WARL ICVEC fields and read back again.

Thanks,
drew