Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 76 additions & 1 deletion drivers/irqchip/irq-apple-aic.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Comment thread
maralcbr marked this conversation as resolved.
Resolved
};

static const struct aic_info aic1_info __initconst = {
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);

Expand Down