[PATCH v2 1/6] lib: utils/irqchip/aplic: implement direct mode irqchip callbacks

Anup Patel anup.patel at oss.qualcomm.com
Wed Jul 8 20:43:59 PDT 2026


On Thu, Jun 18, 2026 at 12:16 AM Pawandeep Oza
<pawandeep.oza at oss.qualcomm.com> wrote:
>
> Add inline register accessors (aplic_sourcecfg_write/read,
> aplic_target_write, aplic_irq_setie/clrie/clrip, aplic_idc_base/
> read/write) to consolidate all MMIO access behind typed helpers
> and eliminate open-coded address arithmetic throughout the driver.
>
> Add field-packing macros APLIC_TARGET_HART_IDX, APLIC_TARGET_IPRIO,
> and APLIC_TARGET_GUEST_IDX for constructing
> APLIC_TARGET register values.
>
> Add aplic_is_direct_mode() mode helper, aplic_hwirq_flags_to_sourcecfg()
> to map generic hwirq flags to APLIC source modes, aplic_hwirq_is_delegated()
> to detect S-mode delegated sources, aplic_find_idc_index() to map a
> hart index to its IDC slot, and aplic_first_target_hart() to select
> the first available IDC hart for initial interrupt targeting.
>
> Remove aplic_writel_msicfg() and MSI register programming from
> aplic_init(). Gate IDC structure initialization on aplic_is_direct_mode()
> and set DOMAINCFG_IE unconditionally on init.

If you remove MSI config programing then this patch breaks
git bisectability because now APLIC MSI-mode Linux driver is
broken.

>
> Implement the full sbi_irqchip callback set for direct (IDC) mode:
> - warm_init: enables IDC interrupt delivery and threshold per hart
> - process_hwirqs: reads TOPI and dispatches via sbi_irqchip_process_hwirq,
>   skipping delegated sources
> - hwirq_setup: programs sourcecfg from hwirq_flags and sets the TARGET
>   register to the first available IDC hart
> - hwirq_cleanup: clears IE, IP, sourcecfg, and TARGET on teardown
> - hwirq_eoi: reads CLAIMI to acknowledge the interrupt on the IDC
> - hwirq_set_affinity: reprograms TARGET to the specified hart index
> - hwirq_mask/unmask: clears/sets IE via CLRIENUM/SETIENUM
>
> Register the irqchip device and populate target_harts from idc_map
> only when targets_mmode is set in aplic_cold_irqchip_init().
>
> Signed-off-by: Oza Pawandeep <pawandeep.oza at oss.qualcomm.com>
> Co-developed-by and Signed-off-by "Raymond Mao <raymond.mao at riscstar.com>"
> ---
>  lib/utils/irqchip/aplic.c | 377 +++++++++++++++++++++++++++++++-------
>  1 file changed, 308 insertions(+), 69 deletions(-)
>
> diff --git a/lib/utils/irqchip/aplic.c b/lib/utils/irqchip/aplic.c
> index 6c63620e..7ae77440 100644
> --- a/lib/utils/irqchip/aplic.c
> +++ b/lib/utils/irqchip/aplic.c
> @@ -70,6 +70,10 @@
>         (APLIC_xMSICFGADDR_PPN_HHX_MASK(__hhxw) << \
>          APLIC_xMSICFGADDR_PPN_HHX_SHIFT(__hhxs))
>
> +#define APLIC_TARGET_GUEST_IDX(__gidx) \
> +       ((((u32)(__gidx)) & APLIC_TARGET_GUEST_IDX_MASK) << \
> +        APLIC_TARGET_GUEST_IDX_SHIFT)
> +
>  #define APLIC_SETIP_BASE               0x1c00
>  #define APLIC_SETIPNUM                 0x1cdc
>
> @@ -93,6 +97,12 @@
>  #define APLIC_TARGET_IPRIO_MASK        0xff
>  #define APLIC_TARGET_EIID_MASK 0x7ff
>
> +#define APLIC_TARGET_HART_IDX(__hidx) \
> +       ((((u32)(__hidx)) & APLIC_TARGET_HART_IDX_MASK) << \
> +        APLIC_TARGET_HART_IDX_SHIFT)
> +#define APLIC_TARGET_IPRIO(__prio) \
> +       (((u32)(__prio)) & APLIC_TARGET_IPRIO_MASK)
> +
>  #define APLIC_IDC_BASE                 0x4000
>  #define APLIC_IDC_SIZE                 32
>
> @@ -116,14 +126,105 @@
>  #define APLIC_ENABLE_ITHRESHOLD                0
>
>  static SBI_LIST_HEAD(aplic_list);
> -static void aplic_writel_msicfg(struct aplic_msicfg_data *msicfg,
> -                               void *msicfgaddr, void *msicfgaddrH);
> +
> +static inline bool aplic_is_direct_mode(struct aplic_data *aplic)
> +{
> +       return aplic && aplic->num_idc;
> +}
> +
> +static inline void aplic_sourcecfg_write(struct aplic_data *aplic,
> +                                        u32 hwirq, u32 val)
> +{
> +       writel(val, (void *)(aplic->addr + APLIC_SOURCECFG_BASE +
> +                            (hwirq - 1) * sizeof(u32)));
> +}
> +
> +static inline u32 aplic_sourcecfg_read(struct aplic_data *aplic, u32 hwirq)
> +{
> +       return readl((void *)(aplic->addr + APLIC_SOURCECFG_BASE +
> +                             (hwirq - 1) * sizeof(u32)));
> +}
> +
> +static inline void aplic_target_write(struct aplic_data *aplic,
> +                                     u32 hwirq, u32 val)
> +{
> +       writel(val, (void *)(aplic->addr + APLIC_TARGET_BASE +
> +                            (hwirq - 1) * sizeof(u32)));
> +}
> +
> +static inline void aplic_irq_setie(struct aplic_data *aplic, u32 hwirq)
> +{
> +       writel(hwirq, (void *)(aplic->addr + APLIC_SETIENUM));
> +}
> +
> +static inline void aplic_irq_clrie(struct aplic_data *aplic, u32 hwirq)
> +{
> +       writel(hwirq, (void *)(aplic->addr + APLIC_CLRIENUM));
> +}
> +
> +static inline void aplic_irq_clrip(struct aplic_data *aplic, u32 hwirq)
> +{
> +       writel(hwirq, (void *)(aplic->addr + APLIC_CLRIPNUM));
> +}
> +
> +static inline void *aplic_idc_base(struct aplic_data *aplic, u32 idc_index)
> +{
> +       return (void *)(aplic->addr + APLIC_IDC_BASE +
> +                       idc_index * APLIC_IDC_SIZE);
> +}
> +
> +static inline u32 aplic_idc_read(struct aplic_data *aplic, u32 idc_index,
> +                                u32 reg)
> +{
> +       return readl(aplic_idc_base(aplic, idc_index) + reg);
> +}
> +
> +static inline void aplic_idc_write(struct aplic_data *aplic, u32 idc_index,
> +                                  u32 reg, u32 val)
> +{
> +       writel(val, aplic_idc_base(aplic, idc_index) + reg);
> +}
> +
> +static u32 aplic_hwirq_flags_to_sourcecfg(u32 hwirq_flags)
> +{
> +       switch (hwirq_flags & SBI_HWIRQ_FLAGS_LEVEL_SENSE_MASK) {
> +       case SBI_HWIRQ_FLAGS_EDGE_RISING:
> +               return APLIC_SOURCECFG_SM_EDGE_RISE;
> +       case SBI_HWIRQ_FLAGS_EDGE_FALLING:
> +               return APLIC_SOURCECFG_SM_EDGE_FALL;
> +       case SBI_HWIRQ_FLAGS_LEVEL_HIGH:
> +               return APLIC_SOURCECFG_SM_LEVEL_HIGH;
> +       case SBI_HWIRQ_FLAGS_LEVEL_LOW:
> +               return APLIC_SOURCECFG_SM_LEVEL_LOW;
> +       default:
> +               return APLIC_SOURCECFG_SM_INACTIVE;
> +       }
> +}
> +
> +static bool aplic_hwirq_is_delegated(struct aplic_data *aplic, u32 hwirq)
> +{
> +       u32 sourcecfg;
> +
> +       sourcecfg = aplic_sourcecfg_read(aplic, hwirq);
> +       return !!(sourcecfg & APLIC_SOURCECFG_D);
> +}
> +
> +static int aplic_find_idc_index(struct aplic_data *aplic, u32 hart_index)
> +{
> +       u32 i;
> +
> +       for (i = 0; i < aplic->num_idc; i++) {
> +               if (aplic->idc_map[i] == hart_index)
> +                       return i;
> +       }
> +
> +       return SBI_ENOENT;
> +}
>
>  static void aplic_init(struct aplic_data *aplic)
>  {
>         struct aplic_delegate_data *deleg;
> -       u32 i, j, tmp;
> -       int locked;
> +       u32 i, j, tmp, domaincfg;
>
>         /* Set domain configuration to 0 */
>         writel(0, (void *)(aplic->addr + APLIC_DOMAINCFG));
> @@ -160,35 +261,23 @@ static void aplic_init(struct aplic_data *aplic)
>                         deleg->last_irq = tmp;
>                 }
>                 for (j = deleg->first_irq; j <= deleg->last_irq; j++)
> -                       writel(APLIC_SOURCECFG_D | deleg->child_index,
> -                              (void *)(aplic->addr + APLIC_SOURCECFG_BASE +
> -                              (j - 1) * sizeof(u32)));
> +                       aplic_sourcecfg_write(aplic, j, APLIC_SOURCECFG_D | deleg->child_index);
>         }
>
>         /* Default initialization of IDC structures */
> -       for (i = 0; i < aplic->num_idc; i++) {
> -               writel(0, (void *)(aplic->addr + APLIC_IDC_BASE +
> -                                  i * APLIC_IDC_SIZE + APLIC_IDC_IDELIVERY));
> -               writel(0, (void *)(aplic->addr + APLIC_IDC_BASE +
> -                                  i * APLIC_IDC_SIZE + APLIC_IDC_IFORCE));
> -               writel(APLIC_DISABLE_ITHRESHOLD, (void *)(aplic->addr +
> -                                                 APLIC_IDC_BASE +
> -                                                 (i * APLIC_IDC_SIZE) +
> -                                                 APLIC_IDC_ITHRESHOLD));
> +       if (aplic_is_direct_mode(aplic)) {
> +               for (i = 0; i < aplic->num_idc; i++) {
> +                       aplic_idc_write(aplic, i, APLIC_IDC_IDELIVERY,
> +                                       APLIC_DISABLE_IDELIVERY);
> +                       aplic_idc_write(aplic, i, APLIC_IDC_IFORCE, 0);
> +                       aplic_idc_write(aplic, i, APLIC_IDC_ITHRESHOLD,
> +                                       APLIC_DISABLE_ITHRESHOLD);
> +               }
>         }
>
> -       /* MSI configuration */
> -       locked = readl((void *)(aplic->addr + APLIC_MMSICFGADDRH)) & APLIC_xMSICFGADDRH_L;
> -       if (aplic->targets_mmode && aplic->has_msicfg_mmode && !locked) {
> -               aplic_writel_msicfg(&aplic->msicfg_mmode,
> -                                   (void *)(aplic->addr + APLIC_MMSICFGADDR),
> -                                   (void *)(aplic->addr + APLIC_MMSICFGADDRH));
> -       }
> -       if (aplic->targets_mmode && aplic->has_msicfg_smode && !locked) {
> -               aplic_writel_msicfg(&aplic->msicfg_smode,
> -                                   (void *)(aplic->addr + APLIC_SMSICFGADDR),
> -                                   (void *)(aplic->addr + APLIC_SMSICFGADDRH));
> -       }

Keep the above under the else part of "if (aplic_is_direct_mode(aplic)) {"

> +       domaincfg = APLIC_DOMAINCFG_IE;
> +
> +       writel(domaincfg, (void *)(aplic->addr + APLIC_DOMAINCFG));
>  }
>
>  void aplic_reinit_all(void)
> @@ -199,35 +288,6 @@ void aplic_reinit_all(void)
>                 aplic_init(aplic);
>  }
>
> -static void aplic_writel_msicfg(struct aplic_msicfg_data *msicfg,
> -                               void *msicfgaddr, void *msicfgaddrH)
> -{
> -       u32 val;
> -       unsigned long base_ppn;
> -
> -       /* Compute the MSI base PPN */
> -       base_ppn = msicfg->base_addr >> APLIC_xMSICFGADDR_PPN_SHIFT;
> -       base_ppn &= ~APLIC_xMSICFGADDR_PPN_HART(msicfg->lhxs);
> -       base_ppn &= ~APLIC_xMSICFGADDR_PPN_LHX(msicfg->lhxw, msicfg->lhxs);
> -       base_ppn &= ~APLIC_xMSICFGADDR_PPN_HHX(msicfg->hhxw, msicfg->hhxs);
> -
> -       /* Write the lower MSI config register */
> -       writel((u32)base_ppn, msicfgaddr);
> -
> -       /* Write the upper MSI config register */
> -       val = (((u64)base_ppn) >> 32) &
> -               APLIC_xMSICFGADDRH_BAPPN_MASK;
> -       val |= (msicfg->lhxw & APLIC_xMSICFGADDRH_LHXW_MASK)
> -               << APLIC_xMSICFGADDRH_LHXW_SHIFT;
> -       val |= (msicfg->hhxw & APLIC_xMSICFGADDRH_HHXW_MASK)
> -               << APLIC_xMSICFGADDRH_HHXW_SHIFT;
> -       val |= (msicfg->lhxs & APLIC_xMSICFGADDRH_LHXS_MASK)
> -               << APLIC_xMSICFGADDRH_LHXS_SHIFT;
> -       val |= (msicfg->hhxs & APLIC_xMSICFGADDRH_HHXS_MASK)
> -               << APLIC_xMSICFGADDRH_HHXS_SHIFT;
> -       writel(val, msicfgaddrH);
> -}
> -

Don't drop this function.

>  static int aplic_check_msicfg(struct aplic_msicfg_data *msicfg)
>  {
>         if (APLIC_xMSICFGADDRH_LHXS_MASK < msicfg->lhxs)
> @@ -245,6 +305,180 @@ static int aplic_check_msicfg(struct aplic_msicfg_data *msicfg)
>         return 0;
>  }
>
> +static int aplic_warm_init(struct sbi_irqchip_device *chip)
> +{
> +       struct aplic_data *aplic;
> +       u32 hart_index;
> +       int idc_index;
> +
> +       aplic = container_of(chip, struct aplic_data, irqchip);
> +
> +       hart_index = current_hartindex();
> +       idc_index  = aplic_find_idc_index(aplic, hart_index);
> +       if (idc_index < 0)
> +               return 0;
> +
> +       aplic_idc_write(aplic, idc_index, APLIC_IDC_ITHRESHOLD,
> +                       APLIC_ENABLE_ITHRESHOLD);
> +       aplic_idc_write(aplic, idc_index, APLIC_IDC_IDELIVERY,
> +                       APLIC_ENABLE_IDELIVERY);
> +
> +       return 0;
> +}
> +
> +static int aplic_process_hwirqs(struct sbi_irqchip_device *chip)
> +{
> +       struct aplic_data *aplic;
> +       u32 hart_index, topi, hwirq;
> +       int idc_index, rc = 0, tmp;
> +
> +       aplic = container_of(chip, struct aplic_data, irqchip);
> +
> +       hart_index = current_hartindex();
> +       idc_index  = aplic_find_idc_index(aplic, hart_index);
> +       if (idc_index < 0)
> +               return 0;
> +
> +       while (1) {
> +               topi  = aplic_idc_read(aplic, idc_index, APLIC_IDC_TOPI);
> +               hwirq = (topi >> APLIC_IDC_TOPI_ID_SHIFT) &
> +                       APLIC_IDC_TOPI_ID_MASK;
> +
> +               if (!hwirq)
> +                       break;
> +
> +               if (aplic_hwirq_is_delegated(aplic, hwirq))
> +                       break;
> +
> +               tmp = sbi_irqchip_process_hwirq(chip, hwirq);
> +               if (tmp)
> +                       rc = tmp;
> +       }

This is not the right way of processing APLIC direct-mode
interrupts. I know this is inspired from Ray's series but we
need to align this with the expected way of handling APLIC
direct-mode interrupts.

Refer, https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/irqchip/irq-riscv-aplic-direct.c?h=v7.2-rc2#n138

> +
> +       return rc;
> +}
> +
> +static int aplic_hwirq_setup(struct sbi_irqchip_device *chip,
> +                            u32 hwirq, u32 hwirq_flags)
> +{
> +       struct aplic_data *aplic;
> +       u32 sourcecfg;
> +
> +       aplic = container_of(chip, struct aplic_data, irqchip);
> +
> +       sourcecfg = aplic_hwirq_flags_to_sourcecfg(hwirq_flags);
> +       if (sourcecfg == APLIC_SOURCECFG_SM_INACTIVE) {
> +               sbi_printf("aplic_hwirq_setup: unsupported flags=0x%lx hwirq=%lu\n",
> +                          (unsigned long)hwirq_flags,
> +                          (unsigned long)hwirq);
> +               return SBI_EINVAL;
> +       }
> +       aplic_sourcecfg_write(aplic, hwirq, sourcecfg);
> +
> +       if (aplic_hwirq_is_delegated(aplic, hwirq)) {
> +               sbi_printf("aplic_hwirq_setup: hwirq=%lu is delegated\n",
> +                          (unsigned long)hwirq);
> +               return SBI_ENOTSUPP;
> +       }
> +
> +       aplic_irq_clrie(aplic, hwirq);
> +       aplic_irq_clrip(aplic, hwirq);
> +
> +       return 0;
> +}
> +
> +static void aplic_hwirq_cleanup(struct sbi_irqchip_device *chip, u32 hwirq)
> +{
> +       struct aplic_data *aplic;
> +
> +       aplic = container_of(chip, struct aplic_data, irqchip);
> +       if (aplic_hwirq_is_delegated(aplic, hwirq))
> +               return;
> +
> +       aplic_irq_clrie(aplic, hwirq);
> +       aplic_irq_clrip(aplic, hwirq);
> +       aplic_sourcecfg_write(aplic, hwirq, APLIC_SOURCECFG_SM_INACTIVE);
> +       aplic_target_write(aplic, hwirq, APLIC_DEFAULT_PRIORITY);
> +}
> +
> +static void aplic_hwirq_eoi(struct sbi_irqchip_device *chip, u32 hwirq)
> +{
> +       struct aplic_data *aplic;
> +       u32 hart_index, claimi, claimed_hwirq;
> +       int idc_index;
> +
> +       aplic = container_of(chip, struct aplic_data, irqchip);
> +       hart_index = current_hartindex();
> +       idc_index  = aplic_find_idc_index(aplic, hart_index);
> +       if (idc_index < 0)
> +               return;
> +
> +       claimi        = aplic_idc_read(aplic, idc_index, APLIC_IDC_CLAIMI);
> +       claimed_hwirq = (claimi >> APLIC_IDC_TOPI_ID_SHIFT) &
> +                       APLIC_IDC_TOPI_ID_MASK;
> +
> +       if (claimed_hwirq && claimed_hwirq != hwirq)
> +               sbi_printf("aplic_hwirq_eoi: claimed mismatch expected=%lu claimed=%lu hart=%lu idc=%lu\n",
> +                          (unsigned long)hwirq,
> +                          (unsigned long)claimed_hwirq,
> +                          (unsigned long)hart_index,
> +                          (unsigned long)idc_index);
> +}

hwirq_eoi() callback is not needed for APLIC direct-mode. Even,
Linux APLIC driver does nothing. On the other hand, the APLIC
MSI-mode will need hwirq_eoi() callback for level triggered interrupts.

Drop aplic_hwirq_eoi() from this patch, once you change
process_hwirqs() to use APLIC_IDC_CLAIMI.

> +
> +static int aplic_hwirq_set_affinity(struct sbi_irqchip_device *chip,
> +                                   u32 hwirq, u32 hart_index)
> +{
> +       struct aplic_data *aplic;
> +       int idc_index;
> +
> +       aplic = container_of(chip, struct aplic_data, irqchip);
> +       if (aplic_hwirq_is_delegated(aplic, hwirq))
> +               return SBI_ENOTSUPP;
> +
> +       idc_index = aplic_find_idc_index(aplic, hart_index);
> +       if (idc_index < 0)
> +               return SBI_EINVAL;
> +
> +       aplic_target_write(aplic, hwirq,
> +                          APLIC_TARGET_HART_IDX(hart_index) |
> +                          APLIC_TARGET_IPRIO(APLIC_DEFAULT_PRIORITY));
> +
> +       return 0;
> +}
> +
> +static void aplic_hwirq_mask(struct sbi_irqchip_device *chip, u32 hwirq)
> +{
> +       struct aplic_data *aplic;
> +
> +       aplic = container_of(chip, struct aplic_data, irqchip);
> +       if (aplic_hwirq_is_delegated(aplic, hwirq))
> +               return;
> +
> +       aplic_irq_clrie(aplic, hwirq);
> +}
> +
> +static void aplic_hwirq_unmask(struct sbi_irqchip_device *chip, u32 hwirq)
> +{
> +       struct aplic_data *aplic;
> +
> +       aplic = container_of(chip, struct aplic_data, irqchip);
> +       if (aplic_hwirq_is_delegated(aplic, hwirq))
> +               return;
> +
> +       aplic_irq_setie(aplic, hwirq);
> +}
> +
> +static struct sbi_irqchip_device aplic_irqchip_template = {
> +       .warm_init              = aplic_warm_init,
> +       .process_hwirqs         = aplic_process_hwirqs,
> +       .hwirq_setup            = aplic_hwirq_setup,
> +       .hwirq_cleanup          = aplic_hwirq_cleanup,
> +       .hwirq_eoi              = aplic_hwirq_eoi,
> +       .hwirq_set_affinity     = aplic_hwirq_set_affinity,
> +       .hwirq_mask             = aplic_hwirq_mask,
> +       .hwirq_unmask           = aplic_hwirq_unmask,
> +};
> +
>  int aplic_cold_irqchip_init(struct aplic_data *aplic)
>  {
>         int rc;
> @@ -297,22 +531,27 @@ int aplic_cold_irqchip_init(struct aplic_data *aplic)
>                         return rc;
>         }
>
> -       if (aplic->num_idc) {
> +       if ((aplic->targets_mmode) && aplic_is_direct_mode(aplic)) {
> +               aplic->irqchip           = aplic_irqchip_template;
> +               aplic->irqchip.id        = aplic->unique_id;
> +               aplic->irqchip.caps = SBI_IRQCHIP_CAPS_WIRED;
> +               aplic->irqchip.num_hwirq = aplic->num_source + 1;
> +
>                 for (i = 0; i < aplic->num_idc; i++)
>                         sbi_hartmask_set_hartindex(aplic->idc_map[i],
>                                                    &aplic->irqchip.target_harts);
> -       } else {
> -               sbi_hartmask_set_all(&aplic->irqchip.target_harts);
> -       }
> -
> -       /* Register irqchip device */
> -       aplic->irqchip.id = aplic->unique_id;
> -       aplic->irqchip.caps = SBI_IRQCHIP_CAPS_WIRED;
> -       aplic->irqchip.num_hwirq = aplic->num_source + 1;
> -       rc = sbi_irqchip_add_device(&aplic->irqchip);
> -       if (rc)
> -               return rc;
>
> +               rc = sbi_irqchip_add_device(&aplic->irqchip);
> +               if (rc) {
> +                       sbi_printf("aplic_cold_irqchip_init: sbi_irqchip_add_device failed rc=%d id=%lu mode=%s target_weight=%lu\n",
> +                               rc,
> +                               (unsigned long)aplic->irqchip.id,
> +                               "direct",
> +                               (unsigned long)sbi_hartmask_weight(
> +                                       &aplic->irqchip.target_harts));
> +                       return rc;
> +               }
> +       }
>         /* Attach to the aplic list */
>         sbi_list_add_tail(&aplic->node, &aplic_list);
>
> --
> 2.43.0
>
>
> --
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi

Regards,
Anup



More information about the opensbi mailing list