From 1a10581c385e8a78f966824be91d8f03072ce2ba Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Sun, 25 Oct 2026 11:18:56 -0400 Subject: [PATCH] irqchip/apple-aic: Report the effective affinity of AICv2+ IRQs AICv2 and later have no per-IRQ CPU target. The controller hands each hardware IRQ to one of the CPUs that accept IRQs, and each CPU says whether it does in its SIQ_CFG system register: a CPU whose routing field (bits 1:0) reads 3 never receives one. On T8140 the boot CPU reads 0 and every secondary reads 3, so every hardware IRQ is taken on CPU 0, even while CPU 0 holds IRQs masked for tens of milliseconds. Setting a secondary to 1 or 2 makes the controller deliver to it as well. The AICv2 irqchip has no irq_set_affinity, so the effective affinity of its IRQs stays empty and their threaded handlers may run on any CPU. The scheduler often places them away from the CPU taking the hard IRQ, which adds a cross-CPU wakeup to every interrupt. On SoCs where this routing has been verified on hardware, currently T8140, and only at EL2, since a hypervisor or secure monitor may trap SIQ_CFG, map AICv2+ IRQs to a second irqchip. It records at CPU bring-up which CPUs accept IRQs and reports them as each IRQ's effective affinity, so that genirq binds threaded handlers to the CPUs the hard IRQ arrives on. The requested mask cannot change delivery; it is accepted and kept only for reporting. On T8140 every AIC IRQ thread, PCIe MSI ones included, therefore runs on CPU 0, and smp_affinity writes do not move it. Every other SoC keeps the existing irqchip unchanged. The accepting set grows as CPUs start and existing IRQs keep theirs, which is enough while only one CPU accepts IRQs. A SoC with more than one accepting CPU would also need the masks refreshed when such a CPU comes online. On T8140 the G17 GPU completion thread used to run on CPU 5 most of the time and now stays on CPU 0. Over two boots of each kernel, compute submission at depth 2 goes from 47-49 us to 37 us per submit, and render submission at depth 1 from a CPU 0 submitter from 128 us to 108 us. Other submission rows are unchanged. (cherry picked from commit 28ceabdc84fdeabdd5c03041f1c23c8439998247) Signed-off-by: DjDeveloperr --- drivers/irqchip/irq-apple-aic.c | 77 ++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/drivers/irqchip/irq-apple-aic.c b/drivers/irqchip/irq-apple-aic.c index 3214501a106355..1125008130ef45 100644 --- a/drivers/irqchip/irq-apple-aic.c +++ b/drivers/irqchip/irq-apple-aic.c @@ -203,6 +203,17 @@ #define SYS_IMP_APL_UPMSR_EL1 sys_reg(3, 7, 15, 6, 4) #define UPMSR_IACT BIT(0) +/* + * System interrupt configuration. AICv2 and later have no per-IRQ CPU target: + * the controller delivers each IRQ to one of the CPUs that accept IRQs, and + * this per-CPU field says whether a CPU does. A CPU whose field reads + * SIQ_CFG_ROUTE_NONE never receives an AIC IRQ. Only used on SoCs where this + * has been verified on hardware (aic_info.siq_cfg_routing). + */ +#define SYS_IMP_APL_SIQ_CFG_EL1 sys_reg(3, 4, 15, 10, 4) +#define SIQ_CFG_ROUTE GENMASK(1, 0) +#define SIQ_CFG_ROUTE_NONE 3 + /* MPIDR fields */ #define MPIDR_CPU(x) MPIDR_AFFINITY_LEVEL(x, 0) #define MPIDR_CLUSTER(x) MPIDR_AFFINITY_LEVEL(x, 1) @@ -261,6 +272,15 @@ struct aic_info { bool fast_ipi; bool local_fast_ipi; bool vm_timer_fiq_readonly; + /* + * SIQ_CFG routing verified on hardware: at EL2, AICv2+ IRQs report the + * CPUs that accept them as their effective affinity. The set grows as + * CPUs start and IRQs keep the set they were given, which is enough + * while only one CPU accepts IRQs. A SoC listed here with more than one + * accepting CPU would also need those masks refreshed when such a CPU + * comes online. + */ + bool siq_cfg_routing; }; static const struct aic_info aic1_info __initconst = { @@ -307,6 +327,14 @@ static const struct aic_info aic3_info __initconst = { .local_fast_ipi = true, }; +static const struct aic_info aic3_t8140_info __initconst = { + .version = 3, + .irq_cfg = AIC3_IRQ_CFG, + .fast_ipi = true, + .local_fast_ipi = true, + .siq_cfg_routing = true, +}; + static const struct aic_info aic3_t8142_info __initconst = { .version = 3, .irq_cfg = AIC3_IRQ_CFG, @@ -316,6 +344,10 @@ static const struct aic_info aic3_t8142_info __initconst = { }; static const struct of_device_id aic_info_match[] = { + { + .compatible = "apple,t8140-aic3", + .data = &aic3_t8140_info, + }, { .compatible = "apple,t8142-aic3", .data = &aic3_t8142_info, @@ -363,6 +395,9 @@ static DEFINE_PER_CPU(uint32_t, aic_fiq_unmasked); static struct aic_irq_chip *aic_irqc; +/* CPUs that accept hardware IRQs, read from SIQ_CFG where siq_cfg_routing is set */ +static struct cpumask aic_irq_cpus; + static void aic_handle_ipi(struct pt_regs *regs); static u32 aic_ic_read(struct aic_irq_chip *ic, u32 reg) @@ -510,6 +545,32 @@ static struct irq_chip aic2_chip = { .flags = IRQCHIP_SKIP_SET_WAKE, }; +/* + * AICv2 and later cannot be steered: the controller delivers each IRQ to one of + * the CPUs that accept IRQs, whatever the requested mask. Where SIQ_CFG says + * which CPUs those are, report them, so that threaded handlers run where the + * hard IRQ is taken. The requested mask is accepted and kept only for + * reporting. + */ +static int aic2_routed_irq_set_affinity(struct irq_data *d, + const struct cpumask *mask_val, bool force) +{ + irq_data_update_effective_affinity(d, cpumask_empty(&aic_irq_cpus) ? + cpu_possible_mask : &aic_irq_cpus); + return IRQ_SET_MASK_OK; +} + +/* AIC2 on SoCs whose SIQ_CFG routing is read (aic_info.siq_cfg_routing, at EL2) */ +static struct irq_chip aic2_routed_chip = { + .name = "AIC2", + .irq_mask = aic_irq_mask, + .irq_unmask = aic_irq_unmask, + .irq_eoi = aic_irq_eoi, + .irq_set_affinity = aic2_routed_irq_set_affinity, + .irq_set_type = aic_irq_set_type, + .flags = IRQCHIP_SKIP_SET_WAKE, +}; + /* * FIQ irqchip */ @@ -684,7 +745,8 @@ static int aic_irq_domain_map(struct irq_domain *id, unsigned int irq, struct irq_chip *chip = &aic_chip; if (ic->info.version == 2 || ic->info.version == 3) - chip = &aic2_chip; + chip = ic->info.siq_cfg_routing && is_kernel_in_hyp_mode() ? + &aic2_routed_chip : &aic2_chip; if (type == AIC_EVENT_TYPE_IRQ) { irq_domain_set_info(id, irq, hw, chip, id->host_data, @@ -958,6 +1020,19 @@ static int aic_init_cpu(unsigned int cpu) } } + /* + * Record whether the controller delivers IRQs to this CPU. SIQ_CFG is + * read only on SoCs where its routing has been verified on hardware, + * and only at EL2, since a hypervisor or secure monitor may trap it. + */ + if (aic_irqc->info.siq_cfg_routing && is_kernel_in_hyp_mode()) { + if (FIELD_GET(SIQ_CFG_ROUTE, read_sysreg_s(SYS_IMP_APL_SIQ_CFG_EL1)) == + SIQ_CFG_ROUTE_NONE) + cpumask_clear_cpu(cpu, &aic_irq_cpus); + else + cpumask_set_cpu(cpu, &aic_irq_cpus); + } + /* Initialize the local mask state */ __this_cpu_write(aic_fiq_unmasked, 0);