Commit 89ea5be1 authored by Marc Zyngier's avatar Marc Zyngier
Browse files

Merge branch irq/aic-v2 into irq/irqchip-next



* irq/aic-v2:
  : .
  : Add support for the interrupt controller found is the latest
  : incarnation of Apple M1 systems, courtesy of Hector Martin.
  : .
  irqchip/apple-aic: Add support for AICv2
  irqchip/apple-aic: Support multiple dies
  irqchip/apple-aic: Dynamically compute register offsets
  irqchip/apple-aic: Switch to irq_domain_create_tree and sparse hwirqs
  irqchip/apple-aic: Add Fast IPI support
  dt-bindings: interrupt-controller: apple,aic2: New binding for AICv2
  PCI: apple: Change MSI handling to handle 4-cell AIC fwspec form

Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
parents c425060a 768d4435
Loading
Loading
Loading
Loading
+98 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/interrupt-controller/apple,aic2.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: Apple Interrupt Controller 2

maintainers:
  - Hector Martin <marcan@marcan.st>

description: |
  The Apple Interrupt Controller 2 is a simple interrupt controller present on
  Apple ARM SoC platforms starting with t600x (M1 Pro and Max).

  It provides the following features:

  - Level-triggered hardware IRQs wired to SoC blocks
    - Single mask bit per IRQ
    - Automatic masking on event delivery (auto-ack)
    - Software triggering (ORed with hw line)
  - Automatic prioritization (single event/ack register per CPU, lower IRQs =
    higher priority)
  - Automatic masking on ack
  - Support for multiple dies

  This device also represents the FIQ interrupt sources on platforms using AIC,
  which do not go through a discrete interrupt controller. It also handles
  FIQ-based Fast IPIs.

properties:
  compatible:
    items:
      - const: apple,t6000-aic
      - const: apple,aic2

  interrupt-controller: true

  '#interrupt-cells':
    const: 4
    description: |
      The 1st cell contains the interrupt type:
        - 0: Hardware IRQ
        - 1: FIQ

      The 2nd cell contains the die ID.

      The next cell contains the interrupt number.
        - HW IRQs: interrupt number
        - FIQs:
          - 0: physical HV timer
          - 1: virtual HV timer
          - 2: physical guest timer
          - 3: virtual guest timer

      The last cell contains the interrupt flags. This is normally
      IRQ_TYPE_LEVEL_HIGH (4).

  reg:
    items:
      - description: Address and size of the main AIC2 registers.
      - description: Address and size of the AIC2 Event register.

  reg-names:
    items:
      - const: core
      - const: event

  power-domains:
    maxItems: 1

required:
  - compatible
  - '#interrupt-cells'
  - interrupt-controller
  - reg
  - reg-names

additionalProperties: false

allOf:
  - $ref: /schemas/interrupt-controller.yaml#

examples:
  - |
    soc {
        #address-cells = <2>;
        #size-cells = <2>;

        aic: interrupt-controller@28e100000 {
            compatible = "apple,t6000-aic", "apple,aic2";
            #interrupt-cells = <4>;
            interrupt-controller;
            reg = <0x2 0x8e100000 0x0 0xc000>,
                  <0x2 0x8e10c000 0x0 0x4>;
            reg-names = "core", "event";
        };
    };
+1 −1
Original line number Diff line number Diff line
@@ -1767,7 +1767,7 @@ T: git https://github.com/AsahiLinux/linux.git
F:	Documentation/devicetree/bindings/arm/apple.yaml
F:	Documentation/devicetree/bindings/arm/apple/*
F:	Documentation/devicetree/bindings/i2c/apple,i2c.yaml
F:	Documentation/devicetree/bindings/interrupt-controller/apple,aic.yaml
F:	Documentation/devicetree/bindings/interrupt-controller/apple,*
F:	Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml
F:	Documentation/devicetree/bindings/pci/apple,pcie.yaml
F:	Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml
+375 −88
Original line number Diff line number Diff line
@@ -24,7 +24,7 @@
 * - Default "this CPU" register view and explicit per-CPU views
 *
 * In addition, this driver also handles FIQs, as these are routed to the same
 * IRQ vector. These are used for Fast IPIs (TODO), the ARMv8 timer IRQs, and
 * IRQ vector. These are used for Fast IPIs, the ARMv8 timer IRQs, and
 * performance counters (TODO).
 *
 * Implementation notes:
@@ -52,10 +52,12 @@
#include <linux/irqchip.h>
#include <linux/irqchip/arm-vgic-info.h>
#include <linux/irqdomain.h>
#include <linux/jump_label.h>
#include <linux/limits.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <asm/apple_m1_pmu.h>
#include <asm/cputype.h>
#include <asm/exception.h>
#include <asm/sysreg.h>
#include <asm/virt.h>
@@ -63,20 +65,22 @@
#include <dt-bindings/interrupt-controller/apple-aic.h>

/*
 * AIC registers (MMIO)
 * AIC v1 registers (MMIO)
 */

#define AIC_INFO		0x0004
#define AIC_INFO_NR_HW		GENMASK(15, 0)
#define AIC_INFO_NR_IRQ		GENMASK(15, 0)

#define AIC_CONFIG		0x0010

#define AIC_WHOAMI		0x2000
#define AIC_EVENT		0x2004
#define AIC_EVENT_TYPE		GENMASK(31, 16)
#define AIC_EVENT_DIE		GENMASK(31, 24)
#define AIC_EVENT_TYPE		GENMASK(23, 16)
#define AIC_EVENT_NUM		GENMASK(15, 0)

#define AIC_EVENT_TYPE_HW	1
#define AIC_EVENT_TYPE_FIQ	0 /* Software use */
#define AIC_EVENT_TYPE_IRQ	1
#define AIC_EVENT_TYPE_IPI	4
#define AIC_EVENT_IPI_OTHER	1
#define AIC_EVENT_IPI_SELF	2
@@ -92,22 +96,71 @@
#define AIC_IPI_SELF		BIT(31)

#define AIC_TARGET_CPU		0x3000
#define AIC_SW_SET		0x4000
#define AIC_SW_CLR		0x4080
#define AIC_MASK_SET		0x4100
#define AIC_MASK_CLR		0x4180

#define AIC_CPU_IPI_SET(cpu)	(0x5008 + ((cpu) << 7))
#define AIC_CPU_IPI_CLR(cpu)	(0x500c + ((cpu) << 7))
#define AIC_CPU_IPI_MASK_SET(cpu) (0x5024 + ((cpu) << 7))
#define AIC_CPU_IPI_MASK_CLR(cpu) (0x5028 + ((cpu) << 7))

#define AIC_MAX_IRQ		0x400

/*
 * AIC v2 registers (MMIO)
 */

#define AIC2_VERSION		0x0000
#define AIC2_VERSION_VER	GENMASK(7, 0)

#define AIC2_INFO1		0x0004
#define AIC2_INFO1_NR_IRQ	GENMASK(15, 0)
#define AIC2_INFO1_LAST_DIE	GENMASK(27, 24)

#define AIC2_INFO2		0x0008

#define AIC2_INFO3		0x000c
#define AIC2_INFO3_MAX_IRQ	GENMASK(15, 0)
#define AIC2_INFO3_MAX_DIE	GENMASK(27, 24)

#define AIC2_RESET		0x0010
#define AIC2_RESET_RESET	BIT(0)

#define AIC2_CONFIG		0x0014
#define AIC2_CONFIG_ENABLE	BIT(0)
#define AIC2_CONFIG_PREFER_PCPU	BIT(28)

#define AIC2_TIMEOUT		0x0028
#define AIC2_CLUSTER_PRIO	0x0030
#define AIC2_DELAY_GROUPS	0x0100

#define AIC2_IRQ_CFG		0x2000

/*
 * AIC2 registers are laid out like this, starting at AIC2_IRQ_CFG:
 *
 * Repeat for each die:
 *   IRQ_CFG: u32 * MAX_IRQS
 *   SW_SET: u32 * (MAX_IRQS / 32)
 *   SW_CLR: u32 * (MAX_IRQS / 32)
 *   MASK_SET: u32 * (MAX_IRQS / 32)
 *   MASK_CLR: u32 * (MAX_IRQS / 32)
 *   HW_STATE: u32 * (MAX_IRQS / 32)
 *
 * This is followed by a set of event registers, each 16K page aligned.
 * The first one is the AP event register we will use. Unfortunately,
 * the actual implemented die count is not specified anywhere in the
 * capability registers, so we have to explicitly specify the event
 * register as a second reg entry in the device tree to remain
 * forward-compatible.
 */

#define AIC2_IRQ_CFG_TARGET	GENMASK(3, 0)
#define AIC2_IRQ_CFG_DELAY_IDX	GENMASK(7, 5)

#define MASK_REG(x)		(4 * ((x) >> 5))
#define MASK_BIT(x)		BIT((x) & GENMASK(4, 0))

/*
 * IMP-DEF sysregs that control FIQ sources
 * Note: sysreg-based IPIs are not supported yet.
 */

/* IPI request registers */
@@ -146,6 +199,17 @@
#define SYS_IMP_APL_UPMSR_EL1		sys_reg(3, 7, 15, 6, 4)
#define UPMSR_IACT			BIT(0)

/* MPIDR fields */
#define MPIDR_CPU(x)			MPIDR_AFFINITY_LEVEL(x, 0)
#define MPIDR_CLUSTER(x)		MPIDR_AFFINITY_LEVEL(x, 1)

#define AIC_IRQ_HWIRQ(die, irq)	(FIELD_PREP(AIC_EVENT_DIE, die) | \
				 FIELD_PREP(AIC_EVENT_TYPE, AIC_EVENT_TYPE_IRQ) | \
				 FIELD_PREP(AIC_EVENT_NUM, irq))
#define AIC_FIQ_HWIRQ(x)	(FIELD_PREP(AIC_EVENT_TYPE, AIC_EVENT_TYPE_FIQ) | \
				 FIELD_PREP(AIC_EVENT_NUM, x))
#define AIC_HWIRQ_IRQ(x)	FIELD_GET(AIC_EVENT_NUM, x)
#define AIC_HWIRQ_DIE(x)	FIELD_GET(AIC_EVENT_DIE, x)
#define AIC_NR_FIQ		6
#define AIC_NR_SWIPI		32

@@ -164,14 +228,81 @@
#define AIC_TMR_EL02_PHYS	AIC_TMR_GUEST_PHYS
#define AIC_TMR_EL02_VIRT	AIC_TMR_GUEST_VIRT

DEFINE_STATIC_KEY_TRUE(use_fast_ipi);

struct aic_info {
	int version;

	/* Register offsets */
	u32 event;
	u32 target_cpu;
	u32 irq_cfg;
	u32 sw_set;
	u32 sw_clr;
	u32 mask_set;
	u32 mask_clr;

	u32 die_stride;

	/* Features */
	bool fast_ipi;
};

static const struct aic_info aic1_info = {
	.version	= 1,

	.event		= AIC_EVENT,
	.target_cpu	= AIC_TARGET_CPU,
};

static const struct aic_info aic1_fipi_info = {
	.version	= 1,

	.event		= AIC_EVENT,
	.target_cpu	= AIC_TARGET_CPU,

	.fast_ipi	= true,
};

static const struct aic_info aic2_info = {
	.version	= 2,

	.irq_cfg	= AIC2_IRQ_CFG,

	.fast_ipi	= true,
};

static const struct of_device_id aic_info_match[] = {
	{
		.compatible = "apple,t8103-aic",
		.data = &aic1_fipi_info,
	},
	{
		.compatible = "apple,aic",
		.data = &aic1_info,
	},
	{
		.compatible = "apple,aic2",
		.data = &aic2_info,
	},
	{}
};

struct aic_irq_chip {
	void __iomem *base;
	void __iomem *event;
	struct irq_domain *hw_domain;
	struct irq_domain *ipi_domain;
	struct {
		cpumask_t aff;
	} *fiq_aff[AIC_NR_FIQ];
	int nr_hw;

	int nr_irq;
	int max_irq;
	int nr_die;
	int max_die;

	struct aic_info info;
};

static DEFINE_PER_CPU(uint32_t, aic_fiq_unmasked);
@@ -199,18 +330,24 @@ static void aic_ic_write(struct aic_irq_chip *ic, u32 reg, u32 val)

static void aic_irq_mask(struct irq_data *d)
{
	irq_hw_number_t hwirq = irqd_to_hwirq(d);
	struct aic_irq_chip *ic = irq_data_get_irq_chip_data(d);

	aic_ic_write(ic, AIC_MASK_SET + MASK_REG(irqd_to_hwirq(d)),
		     MASK_BIT(irqd_to_hwirq(d)));
	u32 off = AIC_HWIRQ_DIE(hwirq) * ic->info.die_stride;
	u32 irq = AIC_HWIRQ_IRQ(hwirq);

	aic_ic_write(ic, ic->info.mask_set + off + MASK_REG(irq), MASK_BIT(irq));
}

static void aic_irq_unmask(struct irq_data *d)
{
	irq_hw_number_t hwirq = irqd_to_hwirq(d);
	struct aic_irq_chip *ic = irq_data_get_irq_chip_data(d);

	aic_ic_write(ic, AIC_MASK_CLR + MASK_REG(d->hwirq),
		     MASK_BIT(irqd_to_hwirq(d)));
	u32 off = AIC_HWIRQ_DIE(hwirq) * ic->info.die_stride;
	u32 irq = AIC_HWIRQ_IRQ(hwirq);

	aic_ic_write(ic, ic->info.mask_clr + off + MASK_REG(irq), MASK_BIT(irq));
}

static void aic_irq_eoi(struct irq_data *d)
@@ -233,12 +370,12 @@ static void __exception_irq_entry aic_handle_irq(struct pt_regs *regs)
		 * We cannot use a relaxed read here, as reads from DMA buffers
		 * need to be ordered after the IRQ fires.
		 */
		event = readl(ic->base + AIC_EVENT);
		event = readl(ic->event + ic->info.event);
		type = FIELD_GET(AIC_EVENT_TYPE, event);
		irq = FIELD_GET(AIC_EVENT_NUM, event);

		if (type == AIC_EVENT_TYPE_HW)
			generic_handle_domain_irq(aic_irqc->hw_domain, irq);
		if (type == AIC_EVENT_TYPE_IRQ)
			generic_handle_domain_irq(aic_irqc->hw_domain, event);
		else if (type == AIC_EVENT_TYPE_IPI && irq == 1)
			aic_handle_ipi(regs);
		else if (event != 0)
@@ -265,12 +402,14 @@ static int aic_irq_set_affinity(struct irq_data *d,
	struct aic_irq_chip *ic = irq_data_get_irq_chip_data(d);
	int cpu;

	BUG_ON(!ic->info.target_cpu);

	if (force)
		cpu = cpumask_first(mask_val);
	else
		cpu = cpumask_any_and(mask_val, cpu_online_mask);

	aic_ic_write(ic, AIC_TARGET_CPU + hwirq * 4, BIT(cpu));
	aic_ic_write(ic, ic->info.target_cpu + AIC_HWIRQ_IRQ(hwirq) * 4, BIT(cpu));
	irq_data_update_effective_affinity(d, cpumask_of(cpu));

	return IRQ_SET_MASK_OK;
@@ -294,15 +433,21 @@ static struct irq_chip aic_chip = {
	.irq_set_type = aic_irq_set_type,
};

static struct irq_chip aic2_chip = {
	.name = "AIC2",
	.irq_mask = aic_irq_mask,
	.irq_unmask = aic_irq_unmask,
	.irq_eoi = aic_irq_eoi,
	.irq_set_type = aic_irq_set_type,
};

/*
 * FIQ irqchip
 */

static unsigned long aic_fiq_get_idx(struct irq_data *d)
{
	struct aic_irq_chip *ic = irq_data_get_irq_chip_data(d);

	return irqd_to_hwirq(d) - ic->nr_hw;
	return AIC_HWIRQ_IRQ(irqd_to_hwirq(d));
}

static void aic_fiq_set_mask(struct irq_data *d)
@@ -380,17 +525,21 @@ static void __exception_irq_entry aic_handle_fiq(struct pt_regs *regs)
	 */

	if (read_sysreg_s(SYS_IMP_APL_IPI_SR_EL1) & IPI_SR_PENDING) {
		if (static_branch_likely(&use_fast_ipi)) {
			aic_handle_ipi(regs);
		} else {
			pr_err_ratelimited("Fast IPI fired. Acking.\n");
			write_sysreg_s(IPI_SR_PENDING, SYS_IMP_APL_IPI_SR_EL1);
		}
	}

	if (TIMER_FIRING(read_sysreg(cntp_ctl_el0)))
		generic_handle_domain_irq(aic_irqc->hw_domain,
					  aic_irqc->nr_hw + AIC_TMR_EL0_PHYS);
					  AIC_FIQ_HWIRQ(AIC_TMR_EL0_PHYS));

	if (TIMER_FIRING(read_sysreg(cntv_ctl_el0)))
		generic_handle_domain_irq(aic_irqc->hw_domain,
					  aic_irqc->nr_hw + AIC_TMR_EL0_VIRT);
					  AIC_FIQ_HWIRQ(AIC_TMR_EL0_VIRT));

	if (is_kernel_in_hyp_mode()) {
		uint64_t enabled = read_sysreg_s(SYS_IMP_APL_VM_TMR_FIQ_ENA_EL2);
@@ -398,12 +547,12 @@ static void __exception_irq_entry aic_handle_fiq(struct pt_regs *regs)
		if ((enabled & VM_TMR_FIQ_ENABLE_P) &&
		    TIMER_FIRING(read_sysreg_s(SYS_CNTP_CTL_EL02)))
			generic_handle_domain_irq(aic_irqc->hw_domain,
						  aic_irqc->nr_hw + AIC_TMR_EL02_PHYS);
						  AIC_FIQ_HWIRQ(AIC_TMR_EL02_PHYS));

		if ((enabled & VM_TMR_FIQ_ENABLE_V) &&
		    TIMER_FIRING(read_sysreg_s(SYS_CNTV_CTL_EL02)))
			generic_handle_domain_irq(aic_irqc->hw_domain,
						  aic_irqc->nr_hw + AIC_TMR_EL02_VIRT);
						  AIC_FIQ_HWIRQ(AIC_TMR_EL02_VIRT));
	}

	if (read_sysreg_s(SYS_IMP_APL_PMCR0_EL1) & PMCR0_IACT) {
@@ -414,7 +563,7 @@ static void __exception_irq_entry aic_handle_fiq(struct pt_regs *regs)
		else
			irq = AIC_CPU_PMU_E;
		generic_handle_domain_irq(aic_irqc->hw_domain,
					  aic_irqc->nr_hw + irq);
					  AIC_FIQ_HWIRQ(irq));
	}

	if (FIELD_GET(UPMCR0_IMODE, read_sysreg_s(SYS_IMP_APL_UPMCR0_EL1)) == UPMCR0_IMODE_FIQ &&
@@ -448,13 +597,18 @@ static int aic_irq_domain_map(struct irq_domain *id, unsigned int irq,
			      irq_hw_number_t hw)
{
	struct aic_irq_chip *ic = id->host_data;
	u32 type = FIELD_GET(AIC_EVENT_TYPE, hw);
	struct irq_chip *chip = &aic_chip;

	if (hw < ic->nr_hw) {
		irq_domain_set_info(id, irq, hw, &aic_chip, id->host_data,
	if (ic->info.version == 2)
		chip = &aic2_chip;

	if (type == AIC_EVENT_TYPE_IRQ) {
		irq_domain_set_info(id, irq, hw, chip, id->host_data,
				    handle_fasteoi_irq, NULL, NULL);
		irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(irq)));
	} else {
		int fiq = hw - ic->nr_hw;
		int fiq = FIELD_GET(AIC_EVENT_NUM, hw);

		switch (fiq) {
		case AIC_CPU_PMU_P:
@@ -479,32 +633,46 @@ static int aic_irq_domain_translate(struct irq_domain *id,
				    unsigned int *type)
{
	struct aic_irq_chip *ic = id->host_data;
	u32 *args;
	u32 die = 0;

	if (fwspec->param_count != 3 || !is_of_node(fwspec->fwnode))
	if (fwspec->param_count < 3 || fwspec->param_count > 4 ||
	    !is_of_node(fwspec->fwnode))
		return -EINVAL;

	args = &fwspec->param[1];

	if (fwspec->param_count == 4) {
		die = args[0];
		args++;
	}

	switch (fwspec->param[0]) {
	case AIC_IRQ:
		if (fwspec->param[1] >= ic->nr_hw)
		if (die >= ic->nr_die)
			return -EINVAL;
		if (args[0] >= ic->nr_irq)
			return -EINVAL;
		*hwirq = fwspec->param[1];
		*hwirq = AIC_IRQ_HWIRQ(die, args[0]);
		break;
	case AIC_FIQ:
		if (fwspec->param[1] >= AIC_NR_FIQ)
		if (die != 0)
			return -EINVAL;
		*hwirq = ic->nr_hw + fwspec->param[1];
		if (args[0] >= AIC_NR_FIQ)
			return -EINVAL;
		*hwirq = AIC_FIQ_HWIRQ(args[0]);

		/*
		 * In EL1 the non-redirected registers are the guest's,
		 * not EL2's, so remap the hwirqs to match.
		 */
		if (!is_kernel_in_hyp_mode()) {
			switch (fwspec->param[1]) {
			switch (args[0]) {
			case AIC_TMR_GUEST_PHYS:
				*hwirq = ic->nr_hw + AIC_TMR_EL0_PHYS;
				*hwirq = AIC_FIQ_HWIRQ(AIC_TMR_EL0_PHYS);
				break;
			case AIC_TMR_GUEST_VIRT:
				*hwirq = ic->nr_hw + AIC_TMR_EL0_VIRT;
				*hwirq = AIC_FIQ_HWIRQ(AIC_TMR_EL0_VIRT);
				break;
			case AIC_TMR_HV_PHYS:
			case AIC_TMR_HV_VIRT:
@@ -518,7 +686,7 @@ static int aic_irq_domain_translate(struct irq_domain *id,
		return -EINVAL;
	}

	*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
	*type = args[1] & IRQ_TYPE_SENSE_MASK;

	return 0;
}
@@ -567,6 +735,22 @@ static const struct irq_domain_ops aic_irq_domain_ops = {
 * IPI irqchip
 */

static void aic_ipi_send_fast(int cpu)
{
	u64 mpidr = cpu_logical_map(cpu);
	u64 my_mpidr = read_cpuid_mpidr();
	u64 cluster = MPIDR_CLUSTER(mpidr);
	u64 idx = MPIDR_CPU(mpidr);

	if (MPIDR_CLUSTER(my_mpidr) == cluster)
		write_sysreg_s(FIELD_PREP(IPI_RR_CPU, idx),
			       SYS_IMP_APL_IPI_RR_LOCAL_EL1);
	else
		write_sysreg_s(FIELD_PREP(IPI_RR_CPU, idx) | FIELD_PREP(IPI_RR_CLUSTER, cluster),
			       SYS_IMP_APL_IPI_RR_GLOBAL_EL1);
	isb();
}

static void aic_ipi_mask(struct irq_data *d)
{
	u32 irq_bit = BIT(irqd_to_hwirq(d));
@@ -592,9 +776,13 @@ static void aic_ipi_unmask(struct irq_data *d)
	 * If a pending vIPI was unmasked, raise a HW IPI to ourselves.
	 * No barriers needed here since this is a self-IPI.
	 */
	if (atomic_read(this_cpu_ptr(&aic_vipi_flag)) & irq_bit)
	if (atomic_read(this_cpu_ptr(&aic_vipi_flag)) & irq_bit) {
		if (static_branch_likely(&use_fast_ipi))
			aic_ipi_send_fast(smp_processor_id());
		else
			aic_ic_write(ic, AIC_IPI_SEND, AIC_IPI_SEND_CPU(smp_processor_id()));
	}
}

static void aic_ipi_send_mask(struct irq_data *d, const struct cpumask *mask)
{
@@ -621,9 +809,13 @@ static void aic_ipi_send_mask(struct irq_data *d, const struct cpumask *mask)
		smp_mb__after_atomic();

		if (!(pending & irq_bit) &&
		    (atomic_read(per_cpu_ptr(&aic_vipi_enable, cpu)) & irq_bit))
		    (atomic_read(per_cpu_ptr(&aic_vipi_enable, cpu)) & irq_bit)) {
			if (static_branch_likely(&use_fast_ipi))
				aic_ipi_send_fast(cpu);
			else
				send |= AIC_IPI_SEND_CPU(cpu);
		}
	}

	/*
	 * The flag writes must complete before the physical IPI is issued
@@ -654,8 +846,16 @@ static void aic_handle_ipi(struct pt_regs *regs)
	/*
	 * Ack the IPI. We need to order this after the AIC event read, but
	 * that is enforced by normal MMIO ordering guarantees.
	 *
	 * For the Fast IPI case, this needs to be ordered before the vIPI
	 * handling below, so we need to isb();
	 */
	if (static_branch_likely(&use_fast_ipi)) {
		write_sysreg_s(IPI_SR_PENDING, SYS_IMP_APL_IPI_SR_EL1);
		isb();
	} else {
		aic_ic_write(aic_irqc, AIC_IPI_ACK, AIC_IPI_OTHER);
	}

	/*
	 * The mask read does not need to be ordered. Only we can change
@@ -683,6 +883,7 @@ static void aic_handle_ipi(struct pt_regs *regs)
	 * No ordering needed here; at worst this just changes the timing of
	 * when the next IPI will be delivered.
	 */
	if (!static_branch_likely(&use_fast_ipi))
		aic_ic_write(aic_irqc, AIC_IPI_MASK_CLR, AIC_IPI_OTHER);
}

@@ -770,6 +971,7 @@ static int aic_init_cpu(unsigned int cpu)
	/* Commit all of the above */
	isb();

	if (aic_irqc->info.version == 1) {
		/*
		 * Make sure the kernel's idea of logical CPU order is the same as AIC's
		 * If we ever end up with a mismatch here, we will have to introduce
@@ -780,10 +982,16 @@ static int aic_init_cpu(unsigned int cpu)
		/*
		 * Always keep IPIs unmasked at the hardware level (except auto-masking
		 * by AIC during processing). We manage masks at the vIPI level.
		 * These registers only exist on AICv1, AICv2 always uses fast IPIs.
		 */
		aic_ic_write(aic_irqc, AIC_IPI_ACK, AIC_IPI_SELF | AIC_IPI_OTHER);
		if (static_branch_likely(&use_fast_ipi)) {
			aic_ic_write(aic_irqc, AIC_IPI_MASK_SET, AIC_IPI_SELF | AIC_IPI_OTHER);
		} else {
			aic_ic_write(aic_irqc, AIC_IPI_MASK_SET, AIC_IPI_SELF);
			aic_ic_write(aic_irqc, AIC_IPI_MASK_CLR, AIC_IPI_OTHER);
		}
	}

	/* Initialize the local mask state */
	__this_cpu_write(aic_fiq_unmasked, 0);
@@ -836,43 +1044,97 @@ static void build_fiq_affinity(struct aic_irq_chip *ic, struct device_node *aff)

static int __init aic_of_ic_init(struct device_node *node, struct device_node *parent)
{
	int i;
	int i, die;
	u32 off, start_off;
	void __iomem *regs;
	u32 info;
	struct aic_irq_chip *irqc;
	struct device_node *affs;
	const struct of_device_id *match;

	regs = of_iomap(node, 0);
	if (WARN_ON(!regs))
		return -EIO;

	irqc = kzalloc(sizeof(*irqc), GFP_KERNEL);
	if (!irqc)
	if (!irqc) {
		iounmap(regs);
		return -ENOMEM;
	}

	aic_irqc = irqc;
	irqc->base = regs;

	match = of_match_node(aic_info_match, node);
	if (!match)
		goto err_unmap;

	irqc->info = *(struct aic_info *)match->data;

	aic_irqc = irqc;

	switch (irqc->info.version) {
	case 1: {
		u32 info;

		info = aic_ic_read(irqc, AIC_INFO);
	irqc->nr_hw = FIELD_GET(AIC_INFO_NR_HW, info);
		irqc->nr_irq = FIELD_GET(AIC_INFO_NR_IRQ, info);
		irqc->max_irq = AIC_MAX_IRQ;
		irqc->nr_die = irqc->max_die = 1;

	irqc->hw_domain = irq_domain_create_linear(of_node_to_fwnode(node),
						   irqc->nr_hw + AIC_NR_FIQ,
						   &aic_irq_domain_ops, irqc);
	if (WARN_ON(!irqc->hw_domain)) {
		iounmap(irqc->base);
		kfree(irqc);
		return -ENODEV;
		off = start_off = irqc->info.target_cpu;
		off += sizeof(u32) * irqc->max_irq; /* TARGET_CPU */

		irqc->event = irqc->base;

		break;
	}
	case 2: {
		u32 info1, info3;

	irq_domain_update_bus_token(irqc->hw_domain, DOMAIN_BUS_WIRED);
		info1 = aic_ic_read(irqc, AIC2_INFO1);
		info3 = aic_ic_read(irqc, AIC2_INFO3);

	if (aic_init_smp(irqc, node)) {
		irq_domain_remove(irqc->hw_domain);
		iounmap(irqc->base);
		kfree(irqc);
		return -ENODEV;
		irqc->nr_irq = FIELD_GET(AIC2_INFO1_NR_IRQ, info1);
		irqc->max_irq = FIELD_GET(AIC2_INFO3_MAX_IRQ, info3);
		irqc->nr_die = FIELD_GET(AIC2_INFO1_LAST_DIE, info1) + 1;
		irqc->max_die = FIELD_GET(AIC2_INFO3_MAX_DIE, info3);

		off = start_off = irqc->info.irq_cfg;
		off += sizeof(u32) * irqc->max_irq; /* IRQ_CFG */

		irqc->event = of_iomap(node, 1);
		if (WARN_ON(!irqc->event))
			goto err_unmap;

		break;
	}
	}

	irqc->info.sw_set = off;
	off += sizeof(u32) * (irqc->max_irq >> 5); /* SW_SET */
	irqc->info.sw_clr = off;
	off += sizeof(u32) * (irqc->max_irq >> 5); /* SW_CLR */
	irqc->info.mask_set = off;
	off += sizeof(u32) * (irqc->max_irq >> 5); /* MASK_SET */
	irqc->info.mask_clr = off;
	off += sizeof(u32) * (irqc->max_irq >> 5); /* MASK_CLR */
	off += sizeof(u32) * (irqc->max_irq >> 5); /* HW_STATE */

	if (irqc->info.fast_ipi)
		static_branch_enable(&use_fast_ipi);
	else
		static_branch_disable(&use_fast_ipi);

	irqc->info.die_stride = off - start_off;

	irqc->hw_domain = irq_domain_create_tree(of_node_to_fwnode(node),
						 &aic_irq_domain_ops, irqc);
	if (WARN_ON(!irqc->hw_domain))
		goto err_unmap;

	irq_domain_update_bus_token(irqc->hw_domain, DOMAIN_BUS_WIRED);

	if (aic_init_smp(irqc, node))
		goto err_remove_domain;

	affs = of_get_child_by_name(node, "affinities");
	if (affs) {
@@ -885,26 +1147,51 @@ static int __init aic_of_ic_init(struct device_node *node, struct device_node *p
	set_handle_irq(aic_handle_irq);
	set_handle_fiq(aic_handle_fiq);

	for (i = 0; i < BITS_TO_U32(irqc->nr_hw); i++)
		aic_ic_write(irqc, AIC_MASK_SET + i * 4, U32_MAX);
	for (i = 0; i < BITS_TO_U32(irqc->nr_hw); i++)
		aic_ic_write(irqc, AIC_SW_CLR + i * 4, U32_MAX);
	for (i = 0; i < irqc->nr_hw; i++)
		aic_ic_write(irqc, AIC_TARGET_CPU + i * 4, 1);
	off = 0;
	for (die = 0; die < irqc->nr_die; die++) {
		for (i = 0; i < BITS_TO_U32(irqc->nr_irq); i++)
			aic_ic_write(irqc, irqc->info.mask_set + off + i * 4, U32_MAX);
		for (i = 0; i < BITS_TO_U32(irqc->nr_irq); i++)
			aic_ic_write(irqc, irqc->info.sw_clr + off + i * 4, U32_MAX);
		if (irqc->info.target_cpu)
			for (i = 0; i < irqc->nr_irq; i++)
				aic_ic_write(irqc, irqc->info.target_cpu + off + i * 4, 1);
		off += irqc->info.die_stride;
	}

	if (irqc->info.version == 2) {
		u32 config = aic_ic_read(irqc, AIC2_CONFIG);

		config |= AIC2_CONFIG_ENABLE;
		aic_ic_write(irqc, AIC2_CONFIG, config);
	}

	if (!is_kernel_in_hyp_mode())
		pr_info("Kernel running in EL1, mapping interrupts");

	if (static_branch_likely(&use_fast_ipi))
		pr_info("Using Fast IPIs");

	cpuhp_setup_state(CPUHP_AP_IRQ_APPLE_AIC_STARTING,
			  "irqchip/apple-aic/ipi:starting",
			  aic_init_cpu, NULL);

	vgic_set_kvm_info(&vgic_info);

	pr_info("Initialized with %d IRQs, %d FIQs, %d vIPIs\n",
		irqc->nr_hw, AIC_NR_FIQ, AIC_NR_SWIPI);
	pr_info("Initialized with %d/%d IRQs * %d/%d die(s), %d FIQs, %d vIPIs",
		irqc->nr_irq, irqc->max_irq, irqc->nr_die, irqc->max_die, AIC_NR_FIQ, AIC_NR_SWIPI);

	return 0;

err_remove_domain:
	irq_domain_remove(irqc->hw_domain);
err_unmap:
	if (irqc->event && irqc->event != irqc->base)
		iounmap(irqc->event);
	iounmap(irqc->base);
	kfree(irqc);
	return -ENODEV;
}

IRQCHIP_DECLARE(apple_m1_aic, "apple,aic", aic_of_ic_init);
IRQCHIP_DECLARE(apple_aic, "apple,aic", aic_of_ic_init);
IRQCHIP_DECLARE(apple_aic2, "apple,aic2", aic_of_ic_init);
+1 −1
Original line number Diff line number Diff line
@@ -219,7 +219,7 @@ static int apple_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
	if (hwirq < 0)
		return -ENOSPC;

	fwspec.param[1] += hwirq;
	fwspec.param[fwspec.param_count - 2] += hwirq;

	ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &fwspec);
	if (ret)