From 5ee055944559e0823c020222a05e5cac48e0c110 Mon Sep 17 00:00:00 2001 From: Pawandeep Oza Date: Tue, 21 Jul 2026 14:48:25 -0700 Subject: [PATCH] lib: utils/irqchip/aplic: implement direct mode irqchip callbacks 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(). Co-developed-by: Raymond Mao Signed-off-by Raymond Mao Signed-off-by: Oza Pawandeep Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260721214833.687361-2-pawandeep.oza@oss.qualcomm.com Signed-off-by: Anup Patel --- 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..6cafab45 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); + + 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; + } } - - /* 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; - /* Attach to the aplic list */ sbi_list_add_tail(&aplic->node, &aplic_list);