[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