Re: [PATCH v3 1/6] lib: utils/irqchip/aplic: implement direct mode irqchip callbacks
Anup Patel <[email protected]> Fri, 17 Jul 2026 13:52:56 +0530
| Newsgroups | org.infradead.lists.opensbi |
|---|---|
| Message-ID | <CABBgwmsuakvEVR482ZfggNa++0d_n=1VNr0xT3Anc8_26+L1_Q@mail.gmail.com> |
On Sun, Jul 12, 2026 at 1:44 AM Pawandeep Oza <[email protected]> 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. > > 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 <[email protected]> > Co-developed-by and Signed-off-by "Raymond Mao <[email protected]>" LGTM. Reviewed-by: Anup Patel <[email protected]> Thanks, Anup > --- > lib/utils/irqchip/aplic.c | 332 +++++++++++++++++++++++++++++++++----- > 1 file changed, 295 insertions(+), 37 deletions(-) > > diff --git a/lib/utils/irqchip/aplic.c b/lib/utils/irqchip/aplic.c > index 6c63620e..efc25555 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 > > @@ -119,10 +129,104 @@ 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; > + u32 i, j, tmp, domaincfg; > int locked; > > /* Set domain configuration to 0 */ > @@ -160,35 +264,36 @@ 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); > + } > + } else { > + /* 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)); > + } > } > > - /* 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)); > - } > + domaincfg = APLIC_DOMAINCFG_IE; > + > + writel(domaincfg, (void *)(aplic->addr + APLIC_DOMAINCFG)); > } > > void aplic_reinit_all(void) > @@ -245,6 +350,154 @@ 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, claimi, 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 ((claimi = aplic_idc_read(aplic, idc_index, APLIC_IDC_CLAIMI))) { > + hwirq = (claimi >> APLIC_IDC_TOPI_ID_SHIFT) & > + APLIC_IDC_TOPI_ID_MASK; > + > + if (!hwirq) > + break; > + > + if (aplic_hwirq_is_delegated(aplic, hwirq)) > + continue; > + > + tmp = sbi_irqchip_process_hwirq(chip, hwirq); > + if (tmp) > + rc = tmp; > + } > + > + 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 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_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 +550,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 > [email protected] > http://lists.infradead.org/mailman/listinfo/opensbi -- opensbi mailing list [email protected] http://lists.infradead.org/mailman/listinfo/opensbi