162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright The Asahi Linux Contributors
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Based on irq-lpc32xx:
662306a36Sopenharmony_ci *   Copyright 2015-2016 Vladimir Zapolskiy <vz@mleia.com>
762306a36Sopenharmony_ci * Based on irq-bcm2836:
862306a36Sopenharmony_ci *   Copyright 2015 Broadcom
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/*
1262306a36Sopenharmony_ci * AIC is a fairly simple interrupt controller with the following features:
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * - 896 level-triggered hardware IRQs
1562306a36Sopenharmony_ci *   - Single mask bit per IRQ
1662306a36Sopenharmony_ci *   - Per-IRQ affinity setting
1762306a36Sopenharmony_ci *   - Automatic masking on event delivery (auto-ack)
1862306a36Sopenharmony_ci *   - Software triggering (ORed with hw line)
1962306a36Sopenharmony_ci * - 2 per-CPU IPIs (meant as "self" and "other", but they are
2062306a36Sopenharmony_ci *   interchangeable if not symmetric)
2162306a36Sopenharmony_ci * - Automatic prioritization (single event/ack register per CPU, lower IRQs =
2262306a36Sopenharmony_ci *   higher priority)
2362306a36Sopenharmony_ci * - Automatic masking on ack
2462306a36Sopenharmony_ci * - Default "this CPU" register view and explicit per-CPU views
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci * In addition, this driver also handles FIQs, as these are routed to the same
2762306a36Sopenharmony_ci * IRQ vector. These are used for Fast IPIs, the ARMv8 timer IRQs, and
2862306a36Sopenharmony_ci * performance counters (TODO).
2962306a36Sopenharmony_ci *
3062306a36Sopenharmony_ci * Implementation notes:
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * - This driver creates two IRQ domains, one for HW IRQs and internal FIQs,
3362306a36Sopenharmony_ci *   and one for IPIs.
3462306a36Sopenharmony_ci * - Since Linux needs more than 2 IPIs, we implement a software IRQ controller
3562306a36Sopenharmony_ci *   and funnel all IPIs into one per-CPU IPI (the second "self" IPI is unused).
3662306a36Sopenharmony_ci * - FIQ hwirq numbers are assigned after true hwirqs, and are per-cpu.
3762306a36Sopenharmony_ci * - DT bindings use 3-cell form (like GIC):
3862306a36Sopenharmony_ci *   - <0 nr flags> - hwirq #nr
3962306a36Sopenharmony_ci *   - <1 nr flags> - FIQ #nr
4062306a36Sopenharmony_ci *     - nr=0  Physical HV timer
4162306a36Sopenharmony_ci *     - nr=1  Virtual HV timer
4262306a36Sopenharmony_ci *     - nr=2  Physical guest timer
4362306a36Sopenharmony_ci *     - nr=3  Virtual guest timer
4462306a36Sopenharmony_ci */
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#include <linux/bits.h>
4962306a36Sopenharmony_ci#include <linux/bitfield.h>
5062306a36Sopenharmony_ci#include <linux/cpuhotplug.h>
5162306a36Sopenharmony_ci#include <linux/io.h>
5262306a36Sopenharmony_ci#include <linux/irqchip.h>
5362306a36Sopenharmony_ci#include <linux/irqchip/arm-vgic-info.h>
5462306a36Sopenharmony_ci#include <linux/irqdomain.h>
5562306a36Sopenharmony_ci#include <linux/jump_label.h>
5662306a36Sopenharmony_ci#include <linux/limits.h>
5762306a36Sopenharmony_ci#include <linux/of_address.h>
5862306a36Sopenharmony_ci#include <linux/slab.h>
5962306a36Sopenharmony_ci#include <asm/apple_m1_pmu.h>
6062306a36Sopenharmony_ci#include <asm/cputype.h>
6162306a36Sopenharmony_ci#include <asm/exception.h>
6262306a36Sopenharmony_ci#include <asm/sysreg.h>
6362306a36Sopenharmony_ci#include <asm/virt.h>
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#include <dt-bindings/interrupt-controller/apple-aic.h>
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/*
6862306a36Sopenharmony_ci * AIC v1 registers (MMIO)
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci#define AIC_INFO		0x0004
7262306a36Sopenharmony_ci#define AIC_INFO_NR_IRQ		GENMASK(15, 0)
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci#define AIC_CONFIG		0x0010
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci#define AIC_WHOAMI		0x2000
7762306a36Sopenharmony_ci#define AIC_EVENT		0x2004
7862306a36Sopenharmony_ci#define AIC_EVENT_DIE		GENMASK(31, 24)
7962306a36Sopenharmony_ci#define AIC_EVENT_TYPE		GENMASK(23, 16)
8062306a36Sopenharmony_ci#define AIC_EVENT_NUM		GENMASK(15, 0)
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci#define AIC_EVENT_TYPE_FIQ	0 /* Software use */
8362306a36Sopenharmony_ci#define AIC_EVENT_TYPE_IRQ	1
8462306a36Sopenharmony_ci#define AIC_EVENT_TYPE_IPI	4
8562306a36Sopenharmony_ci#define AIC_EVENT_IPI_OTHER	1
8662306a36Sopenharmony_ci#define AIC_EVENT_IPI_SELF	2
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci#define AIC_IPI_SEND		0x2008
8962306a36Sopenharmony_ci#define AIC_IPI_ACK		0x200c
9062306a36Sopenharmony_ci#define AIC_IPI_MASK_SET	0x2024
9162306a36Sopenharmony_ci#define AIC_IPI_MASK_CLR	0x2028
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define AIC_IPI_SEND_CPU(cpu)	BIT(cpu)
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci#define AIC_IPI_OTHER		BIT(0)
9662306a36Sopenharmony_ci#define AIC_IPI_SELF		BIT(31)
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci#define AIC_TARGET_CPU		0x3000
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci#define AIC_CPU_IPI_SET(cpu)	(0x5008 + ((cpu) << 7))
10162306a36Sopenharmony_ci#define AIC_CPU_IPI_CLR(cpu)	(0x500c + ((cpu) << 7))
10262306a36Sopenharmony_ci#define AIC_CPU_IPI_MASK_SET(cpu) (0x5024 + ((cpu) << 7))
10362306a36Sopenharmony_ci#define AIC_CPU_IPI_MASK_CLR(cpu) (0x5028 + ((cpu) << 7))
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci#define AIC_MAX_IRQ		0x400
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/*
10862306a36Sopenharmony_ci * AIC v2 registers (MMIO)
10962306a36Sopenharmony_ci */
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci#define AIC2_VERSION		0x0000
11262306a36Sopenharmony_ci#define AIC2_VERSION_VER	GENMASK(7, 0)
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci#define AIC2_INFO1		0x0004
11562306a36Sopenharmony_ci#define AIC2_INFO1_NR_IRQ	GENMASK(15, 0)
11662306a36Sopenharmony_ci#define AIC2_INFO1_LAST_DIE	GENMASK(27, 24)
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci#define AIC2_INFO2		0x0008
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci#define AIC2_INFO3		0x000c
12162306a36Sopenharmony_ci#define AIC2_INFO3_MAX_IRQ	GENMASK(15, 0)
12262306a36Sopenharmony_ci#define AIC2_INFO3_MAX_DIE	GENMASK(27, 24)
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci#define AIC2_RESET		0x0010
12562306a36Sopenharmony_ci#define AIC2_RESET_RESET	BIT(0)
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci#define AIC2_CONFIG		0x0014
12862306a36Sopenharmony_ci#define AIC2_CONFIG_ENABLE	BIT(0)
12962306a36Sopenharmony_ci#define AIC2_CONFIG_PREFER_PCPU	BIT(28)
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci#define AIC2_TIMEOUT		0x0028
13262306a36Sopenharmony_ci#define AIC2_CLUSTER_PRIO	0x0030
13362306a36Sopenharmony_ci#define AIC2_DELAY_GROUPS	0x0100
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci#define AIC2_IRQ_CFG		0x2000
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci/*
13862306a36Sopenharmony_ci * AIC2 registers are laid out like this, starting at AIC2_IRQ_CFG:
13962306a36Sopenharmony_ci *
14062306a36Sopenharmony_ci * Repeat for each die:
14162306a36Sopenharmony_ci *   IRQ_CFG: u32 * MAX_IRQS
14262306a36Sopenharmony_ci *   SW_SET: u32 * (MAX_IRQS / 32)
14362306a36Sopenharmony_ci *   SW_CLR: u32 * (MAX_IRQS / 32)
14462306a36Sopenharmony_ci *   MASK_SET: u32 * (MAX_IRQS / 32)
14562306a36Sopenharmony_ci *   MASK_CLR: u32 * (MAX_IRQS / 32)
14662306a36Sopenharmony_ci *   HW_STATE: u32 * (MAX_IRQS / 32)
14762306a36Sopenharmony_ci *
14862306a36Sopenharmony_ci * This is followed by a set of event registers, each 16K page aligned.
14962306a36Sopenharmony_ci * The first one is the AP event register we will use. Unfortunately,
15062306a36Sopenharmony_ci * the actual implemented die count is not specified anywhere in the
15162306a36Sopenharmony_ci * capability registers, so we have to explicitly specify the event
15262306a36Sopenharmony_ci * register as a second reg entry in the device tree to remain
15362306a36Sopenharmony_ci * forward-compatible.
15462306a36Sopenharmony_ci */
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci#define AIC2_IRQ_CFG_TARGET	GENMASK(3, 0)
15762306a36Sopenharmony_ci#define AIC2_IRQ_CFG_DELAY_IDX	GENMASK(7, 5)
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci#define MASK_REG(x)		(4 * ((x) >> 5))
16062306a36Sopenharmony_ci#define MASK_BIT(x)		BIT((x) & GENMASK(4, 0))
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci/*
16362306a36Sopenharmony_ci * IMP-DEF sysregs that control FIQ sources
16462306a36Sopenharmony_ci */
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci/* IPI request registers */
16762306a36Sopenharmony_ci#define SYS_IMP_APL_IPI_RR_LOCAL_EL1	sys_reg(3, 5, 15, 0, 0)
16862306a36Sopenharmony_ci#define SYS_IMP_APL_IPI_RR_GLOBAL_EL1	sys_reg(3, 5, 15, 0, 1)
16962306a36Sopenharmony_ci#define IPI_RR_CPU			GENMASK(7, 0)
17062306a36Sopenharmony_ci/* Cluster only used for the GLOBAL register */
17162306a36Sopenharmony_ci#define IPI_RR_CLUSTER			GENMASK(23, 16)
17262306a36Sopenharmony_ci#define IPI_RR_TYPE			GENMASK(29, 28)
17362306a36Sopenharmony_ci#define IPI_RR_IMMEDIATE		0
17462306a36Sopenharmony_ci#define IPI_RR_RETRACT			1
17562306a36Sopenharmony_ci#define IPI_RR_DEFERRED			2
17662306a36Sopenharmony_ci#define IPI_RR_NOWAKE			3
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci/* IPI status register */
17962306a36Sopenharmony_ci#define SYS_IMP_APL_IPI_SR_EL1		sys_reg(3, 5, 15, 1, 1)
18062306a36Sopenharmony_ci#define IPI_SR_PENDING			BIT(0)
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci/* Guest timer FIQ enable register */
18362306a36Sopenharmony_ci#define SYS_IMP_APL_VM_TMR_FIQ_ENA_EL2	sys_reg(3, 5, 15, 1, 3)
18462306a36Sopenharmony_ci#define VM_TMR_FIQ_ENABLE_V		BIT(0)
18562306a36Sopenharmony_ci#define VM_TMR_FIQ_ENABLE_P		BIT(1)
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci/* Deferred IPI countdown register */
18862306a36Sopenharmony_ci#define SYS_IMP_APL_IPI_CR_EL1		sys_reg(3, 5, 15, 3, 1)
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci/* Uncore PMC control register */
19162306a36Sopenharmony_ci#define SYS_IMP_APL_UPMCR0_EL1		sys_reg(3, 7, 15, 0, 4)
19262306a36Sopenharmony_ci#define UPMCR0_IMODE			GENMASK(18, 16)
19362306a36Sopenharmony_ci#define UPMCR0_IMODE_OFF		0
19462306a36Sopenharmony_ci#define UPMCR0_IMODE_AIC		2
19562306a36Sopenharmony_ci#define UPMCR0_IMODE_HALT		3
19662306a36Sopenharmony_ci#define UPMCR0_IMODE_FIQ		4
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci/* Uncore PMC status register */
19962306a36Sopenharmony_ci#define SYS_IMP_APL_UPMSR_EL1		sys_reg(3, 7, 15, 6, 4)
20062306a36Sopenharmony_ci#define UPMSR_IACT			BIT(0)
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci/* MPIDR fields */
20362306a36Sopenharmony_ci#define MPIDR_CPU(x)			MPIDR_AFFINITY_LEVEL(x, 0)
20462306a36Sopenharmony_ci#define MPIDR_CLUSTER(x)		MPIDR_AFFINITY_LEVEL(x, 1)
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci#define AIC_IRQ_HWIRQ(die, irq)	(FIELD_PREP(AIC_EVENT_DIE, die) | \
20762306a36Sopenharmony_ci				 FIELD_PREP(AIC_EVENT_TYPE, AIC_EVENT_TYPE_IRQ) | \
20862306a36Sopenharmony_ci				 FIELD_PREP(AIC_EVENT_NUM, irq))
20962306a36Sopenharmony_ci#define AIC_FIQ_HWIRQ(x)	(FIELD_PREP(AIC_EVENT_TYPE, AIC_EVENT_TYPE_FIQ) | \
21062306a36Sopenharmony_ci				 FIELD_PREP(AIC_EVENT_NUM, x))
21162306a36Sopenharmony_ci#define AIC_HWIRQ_IRQ(x)	FIELD_GET(AIC_EVENT_NUM, x)
21262306a36Sopenharmony_ci#define AIC_HWIRQ_DIE(x)	FIELD_GET(AIC_EVENT_DIE, x)
21362306a36Sopenharmony_ci#define AIC_NR_SWIPI		32
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci/*
21662306a36Sopenharmony_ci * FIQ hwirq index definitions: FIQ sources use the DT binding defines
21762306a36Sopenharmony_ci * directly, except that timers are special. At the irqchip level, the
21862306a36Sopenharmony_ci * two timer types are represented by their access method: _EL0 registers
21962306a36Sopenharmony_ci * or _EL02 registers. In the DT binding, the timers are represented
22062306a36Sopenharmony_ci * by their purpose (HV or guest). This mapping is for when the kernel is
22162306a36Sopenharmony_ci * running at EL2 (with VHE). When the kernel is running at EL1, the
22262306a36Sopenharmony_ci * mapping differs and aic_irq_domain_translate() performs the remapping.
22362306a36Sopenharmony_ci */
22462306a36Sopenharmony_cienum fiq_hwirq {
22562306a36Sopenharmony_ci	/* Must be ordered as in apple-aic.h */
22662306a36Sopenharmony_ci	AIC_TMR_EL0_PHYS	= AIC_TMR_HV_PHYS,
22762306a36Sopenharmony_ci	AIC_TMR_EL0_VIRT	= AIC_TMR_HV_VIRT,
22862306a36Sopenharmony_ci	AIC_TMR_EL02_PHYS	= AIC_TMR_GUEST_PHYS,
22962306a36Sopenharmony_ci	AIC_TMR_EL02_VIRT	= AIC_TMR_GUEST_VIRT,
23062306a36Sopenharmony_ci	AIC_CPU_PMU_Effi	= AIC_CPU_PMU_E,
23162306a36Sopenharmony_ci	AIC_CPU_PMU_Perf	= AIC_CPU_PMU_P,
23262306a36Sopenharmony_ci	/* No need for this to be discovered from DT */
23362306a36Sopenharmony_ci	AIC_VGIC_MI,
23462306a36Sopenharmony_ci	AIC_NR_FIQ
23562306a36Sopenharmony_ci};
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic DEFINE_STATIC_KEY_TRUE(use_fast_ipi);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistruct aic_info {
24062306a36Sopenharmony_ci	int version;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	/* Register offsets */
24362306a36Sopenharmony_ci	u32 event;
24462306a36Sopenharmony_ci	u32 target_cpu;
24562306a36Sopenharmony_ci	u32 irq_cfg;
24662306a36Sopenharmony_ci	u32 sw_set;
24762306a36Sopenharmony_ci	u32 sw_clr;
24862306a36Sopenharmony_ci	u32 mask_set;
24962306a36Sopenharmony_ci	u32 mask_clr;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	u32 die_stride;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	/* Features */
25462306a36Sopenharmony_ci	bool fast_ipi;
25562306a36Sopenharmony_ci};
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic const struct aic_info aic1_info __initconst = {
25862306a36Sopenharmony_ci	.version	= 1,
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	.event		= AIC_EVENT,
26162306a36Sopenharmony_ci	.target_cpu	= AIC_TARGET_CPU,
26262306a36Sopenharmony_ci};
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic const struct aic_info aic1_fipi_info __initconst = {
26562306a36Sopenharmony_ci	.version	= 1,
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	.event		= AIC_EVENT,
26862306a36Sopenharmony_ci	.target_cpu	= AIC_TARGET_CPU,
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	.fast_ipi	= true,
27162306a36Sopenharmony_ci};
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic const struct aic_info aic2_info __initconst = {
27462306a36Sopenharmony_ci	.version	= 2,
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	.irq_cfg	= AIC2_IRQ_CFG,
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	.fast_ipi	= true,
27962306a36Sopenharmony_ci};
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic const struct of_device_id aic_info_match[] = {
28262306a36Sopenharmony_ci	{
28362306a36Sopenharmony_ci		.compatible = "apple,t8103-aic",
28462306a36Sopenharmony_ci		.data = &aic1_fipi_info,
28562306a36Sopenharmony_ci	},
28662306a36Sopenharmony_ci	{
28762306a36Sopenharmony_ci		.compatible = "apple,aic",
28862306a36Sopenharmony_ci		.data = &aic1_info,
28962306a36Sopenharmony_ci	},
29062306a36Sopenharmony_ci	{
29162306a36Sopenharmony_ci		.compatible = "apple,aic2",
29262306a36Sopenharmony_ci		.data = &aic2_info,
29362306a36Sopenharmony_ci	},
29462306a36Sopenharmony_ci	{}
29562306a36Sopenharmony_ci};
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistruct aic_irq_chip {
29862306a36Sopenharmony_ci	void __iomem *base;
29962306a36Sopenharmony_ci	void __iomem *event;
30062306a36Sopenharmony_ci	struct irq_domain *hw_domain;
30162306a36Sopenharmony_ci	struct {
30262306a36Sopenharmony_ci		cpumask_t aff;
30362306a36Sopenharmony_ci	} *fiq_aff[AIC_NR_FIQ];
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	int nr_irq;
30662306a36Sopenharmony_ci	int max_irq;
30762306a36Sopenharmony_ci	int nr_die;
30862306a36Sopenharmony_ci	int max_die;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	struct aic_info info;
31162306a36Sopenharmony_ci};
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic DEFINE_PER_CPU(uint32_t, aic_fiq_unmasked);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic struct aic_irq_chip *aic_irqc;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic void aic_handle_ipi(struct pt_regs *regs);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic u32 aic_ic_read(struct aic_irq_chip *ic, u32 reg)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	return readl_relaxed(ic->base + reg);
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic void aic_ic_write(struct aic_irq_chip *ic, u32 reg, u32 val)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	writel_relaxed(val, ic->base + reg);
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci/*
33062306a36Sopenharmony_ci * IRQ irqchip
33162306a36Sopenharmony_ci */
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic void aic_irq_mask(struct irq_data *d)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	irq_hw_number_t hwirq = irqd_to_hwirq(d);
33662306a36Sopenharmony_ci	struct aic_irq_chip *ic = irq_data_get_irq_chip_data(d);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	u32 off = AIC_HWIRQ_DIE(hwirq) * ic->info.die_stride;
33962306a36Sopenharmony_ci	u32 irq = AIC_HWIRQ_IRQ(hwirq);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	aic_ic_write(ic, ic->info.mask_set + off + MASK_REG(irq), MASK_BIT(irq));
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistatic void aic_irq_unmask(struct irq_data *d)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	irq_hw_number_t hwirq = irqd_to_hwirq(d);
34762306a36Sopenharmony_ci	struct aic_irq_chip *ic = irq_data_get_irq_chip_data(d);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	u32 off = AIC_HWIRQ_DIE(hwirq) * ic->info.die_stride;
35062306a36Sopenharmony_ci	u32 irq = AIC_HWIRQ_IRQ(hwirq);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	aic_ic_write(ic, ic->info.mask_clr + off + MASK_REG(irq), MASK_BIT(irq));
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic void aic_irq_eoi(struct irq_data *d)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	/*
35862306a36Sopenharmony_ci	 * Reading the interrupt reason automatically acknowledges and masks
35962306a36Sopenharmony_ci	 * the IRQ, so we just unmask it here if needed.
36062306a36Sopenharmony_ci	 */
36162306a36Sopenharmony_ci	if (!irqd_irq_masked(d))
36262306a36Sopenharmony_ci		aic_irq_unmask(d);
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic void __exception_irq_entry aic_handle_irq(struct pt_regs *regs)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	struct aic_irq_chip *ic = aic_irqc;
36862306a36Sopenharmony_ci	u32 event, type, irq;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	do {
37162306a36Sopenharmony_ci		/*
37262306a36Sopenharmony_ci		 * We cannot use a relaxed read here, as reads from DMA buffers
37362306a36Sopenharmony_ci		 * need to be ordered after the IRQ fires.
37462306a36Sopenharmony_ci		 */
37562306a36Sopenharmony_ci		event = readl(ic->event + ic->info.event);
37662306a36Sopenharmony_ci		type = FIELD_GET(AIC_EVENT_TYPE, event);
37762306a36Sopenharmony_ci		irq = FIELD_GET(AIC_EVENT_NUM, event);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci		if (type == AIC_EVENT_TYPE_IRQ)
38062306a36Sopenharmony_ci			generic_handle_domain_irq(aic_irqc->hw_domain, event);
38162306a36Sopenharmony_ci		else if (type == AIC_EVENT_TYPE_IPI && irq == 1)
38262306a36Sopenharmony_ci			aic_handle_ipi(regs);
38362306a36Sopenharmony_ci		else if (event != 0)
38462306a36Sopenharmony_ci			pr_err_ratelimited("Unknown IRQ event %d, %d\n", type, irq);
38562306a36Sopenharmony_ci	} while (event);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	/*
38862306a36Sopenharmony_ci	 * vGIC maintenance interrupts end up here too, so we need to check
38962306a36Sopenharmony_ci	 * for them separately. It should however only trigger when NV is
39062306a36Sopenharmony_ci	 * in use, and be cleared when coming back from the handler.
39162306a36Sopenharmony_ci	 */
39262306a36Sopenharmony_ci	if (is_kernel_in_hyp_mode() &&
39362306a36Sopenharmony_ci	    (read_sysreg_s(SYS_ICH_HCR_EL2) & ICH_HCR_EN) &&
39462306a36Sopenharmony_ci	    read_sysreg_s(SYS_ICH_MISR_EL2) != 0) {
39562306a36Sopenharmony_ci		generic_handle_domain_irq(aic_irqc->hw_domain,
39662306a36Sopenharmony_ci					  AIC_FIQ_HWIRQ(AIC_VGIC_MI));
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci		if (unlikely((read_sysreg_s(SYS_ICH_HCR_EL2) & ICH_HCR_EN) &&
39962306a36Sopenharmony_ci			     read_sysreg_s(SYS_ICH_MISR_EL2))) {
40062306a36Sopenharmony_ci			pr_err_ratelimited("vGIC IRQ fired and not handled by KVM, disabling.\n");
40162306a36Sopenharmony_ci			sysreg_clear_set_s(SYS_ICH_HCR_EL2, ICH_HCR_EN, 0);
40262306a36Sopenharmony_ci		}
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistatic int aic_irq_set_affinity(struct irq_data *d,
40762306a36Sopenharmony_ci				const struct cpumask *mask_val, bool force)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	irq_hw_number_t hwirq = irqd_to_hwirq(d);
41062306a36Sopenharmony_ci	struct aic_irq_chip *ic = irq_data_get_irq_chip_data(d);
41162306a36Sopenharmony_ci	int cpu;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	BUG_ON(!ic->info.target_cpu);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	if (force)
41662306a36Sopenharmony_ci		cpu = cpumask_first(mask_val);
41762306a36Sopenharmony_ci	else
41862306a36Sopenharmony_ci		cpu = cpumask_any_and(mask_val, cpu_online_mask);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	aic_ic_write(ic, ic->info.target_cpu + AIC_HWIRQ_IRQ(hwirq) * 4, BIT(cpu));
42162306a36Sopenharmony_ci	irq_data_update_effective_affinity(d, cpumask_of(cpu));
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	return IRQ_SET_MASK_OK;
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic int aic_irq_set_type(struct irq_data *d, unsigned int type)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	/*
42962306a36Sopenharmony_ci	 * Some IRQs (e.g. MSIs) implicitly have edge semantics, and we don't
43062306a36Sopenharmony_ci	 * have a way to find out the type of any given IRQ, so just allow both.
43162306a36Sopenharmony_ci	 */
43262306a36Sopenharmony_ci	return (type == IRQ_TYPE_LEVEL_HIGH || type == IRQ_TYPE_EDGE_RISING) ? 0 : -EINVAL;
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic struct irq_chip aic_chip = {
43662306a36Sopenharmony_ci	.name = "AIC",
43762306a36Sopenharmony_ci	.irq_mask = aic_irq_mask,
43862306a36Sopenharmony_ci	.irq_unmask = aic_irq_unmask,
43962306a36Sopenharmony_ci	.irq_eoi = aic_irq_eoi,
44062306a36Sopenharmony_ci	.irq_set_affinity = aic_irq_set_affinity,
44162306a36Sopenharmony_ci	.irq_set_type = aic_irq_set_type,
44262306a36Sopenharmony_ci};
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic struct irq_chip aic2_chip = {
44562306a36Sopenharmony_ci	.name = "AIC2",
44662306a36Sopenharmony_ci	.irq_mask = aic_irq_mask,
44762306a36Sopenharmony_ci	.irq_unmask = aic_irq_unmask,
44862306a36Sopenharmony_ci	.irq_eoi = aic_irq_eoi,
44962306a36Sopenharmony_ci	.irq_set_type = aic_irq_set_type,
45062306a36Sopenharmony_ci};
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci/*
45362306a36Sopenharmony_ci * FIQ irqchip
45462306a36Sopenharmony_ci */
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_cistatic unsigned long aic_fiq_get_idx(struct irq_data *d)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	return AIC_HWIRQ_IRQ(irqd_to_hwirq(d));
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic void aic_fiq_set_mask(struct irq_data *d)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	/* Only the guest timers have real mask bits, unfortunately. */
46462306a36Sopenharmony_ci	switch (aic_fiq_get_idx(d)) {
46562306a36Sopenharmony_ci	case AIC_TMR_EL02_PHYS:
46662306a36Sopenharmony_ci		sysreg_clear_set_s(SYS_IMP_APL_VM_TMR_FIQ_ENA_EL2, VM_TMR_FIQ_ENABLE_P, 0);
46762306a36Sopenharmony_ci		isb();
46862306a36Sopenharmony_ci		break;
46962306a36Sopenharmony_ci	case AIC_TMR_EL02_VIRT:
47062306a36Sopenharmony_ci		sysreg_clear_set_s(SYS_IMP_APL_VM_TMR_FIQ_ENA_EL2, VM_TMR_FIQ_ENABLE_V, 0);
47162306a36Sopenharmony_ci		isb();
47262306a36Sopenharmony_ci		break;
47362306a36Sopenharmony_ci	default:
47462306a36Sopenharmony_ci		break;
47562306a36Sopenharmony_ci	}
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_cistatic void aic_fiq_clear_mask(struct irq_data *d)
47962306a36Sopenharmony_ci{
48062306a36Sopenharmony_ci	switch (aic_fiq_get_idx(d)) {
48162306a36Sopenharmony_ci	case AIC_TMR_EL02_PHYS:
48262306a36Sopenharmony_ci		sysreg_clear_set_s(SYS_IMP_APL_VM_TMR_FIQ_ENA_EL2, 0, VM_TMR_FIQ_ENABLE_P);
48362306a36Sopenharmony_ci		isb();
48462306a36Sopenharmony_ci		break;
48562306a36Sopenharmony_ci	case AIC_TMR_EL02_VIRT:
48662306a36Sopenharmony_ci		sysreg_clear_set_s(SYS_IMP_APL_VM_TMR_FIQ_ENA_EL2, 0, VM_TMR_FIQ_ENABLE_V);
48762306a36Sopenharmony_ci		isb();
48862306a36Sopenharmony_ci		break;
48962306a36Sopenharmony_ci	default:
49062306a36Sopenharmony_ci		break;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_cistatic void aic_fiq_mask(struct irq_data *d)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	aic_fiq_set_mask(d);
49762306a36Sopenharmony_ci	__this_cpu_and(aic_fiq_unmasked, ~BIT(aic_fiq_get_idx(d)));
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_cistatic void aic_fiq_unmask(struct irq_data *d)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	aic_fiq_clear_mask(d);
50362306a36Sopenharmony_ci	__this_cpu_or(aic_fiq_unmasked, BIT(aic_fiq_get_idx(d)));
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic void aic_fiq_eoi(struct irq_data *d)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	/* We mask to ack (where we can), so we need to unmask at EOI. */
50962306a36Sopenharmony_ci	if (__this_cpu_read(aic_fiq_unmasked) & BIT(aic_fiq_get_idx(d)))
51062306a36Sopenharmony_ci		aic_fiq_clear_mask(d);
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci#define TIMER_FIRING(x)                                                        \
51462306a36Sopenharmony_ci	(((x) & (ARCH_TIMER_CTRL_ENABLE | ARCH_TIMER_CTRL_IT_MASK |            \
51562306a36Sopenharmony_ci		 ARCH_TIMER_CTRL_IT_STAT)) ==                                  \
51662306a36Sopenharmony_ci	 (ARCH_TIMER_CTRL_ENABLE | ARCH_TIMER_CTRL_IT_STAT))
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_cistatic void __exception_irq_entry aic_handle_fiq(struct pt_regs *regs)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	/*
52162306a36Sopenharmony_ci	 * It would be really nice if we had a system register that lets us get
52262306a36Sopenharmony_ci	 * the FIQ source state without having to peek down into sources...
52362306a36Sopenharmony_ci	 * but such a register does not seem to exist.
52462306a36Sopenharmony_ci	 *
52562306a36Sopenharmony_ci	 * So, we have these potential sources to test for:
52662306a36Sopenharmony_ci	 *  - Fast IPIs (not yet used)
52762306a36Sopenharmony_ci	 *  - The 4 timers (CNTP, CNTV for each of HV and guest)
52862306a36Sopenharmony_ci	 *  - Per-core PMCs (not yet supported)
52962306a36Sopenharmony_ci	 *  - Per-cluster uncore PMCs (not yet supported)
53062306a36Sopenharmony_ci	 *
53162306a36Sopenharmony_ci	 * Since not dealing with any of these results in a FIQ storm,
53262306a36Sopenharmony_ci	 * we check for everything here, even things we don't support yet.
53362306a36Sopenharmony_ci	 */
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	if (read_sysreg_s(SYS_IMP_APL_IPI_SR_EL1) & IPI_SR_PENDING) {
53662306a36Sopenharmony_ci		if (static_branch_likely(&use_fast_ipi)) {
53762306a36Sopenharmony_ci			aic_handle_ipi(regs);
53862306a36Sopenharmony_ci		} else {
53962306a36Sopenharmony_ci			pr_err_ratelimited("Fast IPI fired. Acking.\n");
54062306a36Sopenharmony_ci			write_sysreg_s(IPI_SR_PENDING, SYS_IMP_APL_IPI_SR_EL1);
54162306a36Sopenharmony_ci		}
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	if (TIMER_FIRING(read_sysreg(cntp_ctl_el0)))
54562306a36Sopenharmony_ci		generic_handle_domain_irq(aic_irqc->hw_domain,
54662306a36Sopenharmony_ci					  AIC_FIQ_HWIRQ(AIC_TMR_EL0_PHYS));
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	if (TIMER_FIRING(read_sysreg(cntv_ctl_el0)))
54962306a36Sopenharmony_ci		generic_handle_domain_irq(aic_irqc->hw_domain,
55062306a36Sopenharmony_ci					  AIC_FIQ_HWIRQ(AIC_TMR_EL0_VIRT));
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	if (is_kernel_in_hyp_mode()) {
55362306a36Sopenharmony_ci		uint64_t enabled = read_sysreg_s(SYS_IMP_APL_VM_TMR_FIQ_ENA_EL2);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci		if ((enabled & VM_TMR_FIQ_ENABLE_P) &&
55662306a36Sopenharmony_ci		    TIMER_FIRING(read_sysreg_s(SYS_CNTP_CTL_EL02)))
55762306a36Sopenharmony_ci			generic_handle_domain_irq(aic_irqc->hw_domain,
55862306a36Sopenharmony_ci						  AIC_FIQ_HWIRQ(AIC_TMR_EL02_PHYS));
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci		if ((enabled & VM_TMR_FIQ_ENABLE_V) &&
56162306a36Sopenharmony_ci		    TIMER_FIRING(read_sysreg_s(SYS_CNTV_CTL_EL02)))
56262306a36Sopenharmony_ci			generic_handle_domain_irq(aic_irqc->hw_domain,
56362306a36Sopenharmony_ci						  AIC_FIQ_HWIRQ(AIC_TMR_EL02_VIRT));
56462306a36Sopenharmony_ci	}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	if (read_sysreg_s(SYS_IMP_APL_PMCR0_EL1) & PMCR0_IACT) {
56762306a36Sopenharmony_ci		int irq;
56862306a36Sopenharmony_ci		if (cpumask_test_cpu(smp_processor_id(),
56962306a36Sopenharmony_ci				     &aic_irqc->fiq_aff[AIC_CPU_PMU_P]->aff))
57062306a36Sopenharmony_ci			irq = AIC_CPU_PMU_P;
57162306a36Sopenharmony_ci		else
57262306a36Sopenharmony_ci			irq = AIC_CPU_PMU_E;
57362306a36Sopenharmony_ci		generic_handle_domain_irq(aic_irqc->hw_domain,
57462306a36Sopenharmony_ci					  AIC_FIQ_HWIRQ(irq));
57562306a36Sopenharmony_ci	}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	if (FIELD_GET(UPMCR0_IMODE, read_sysreg_s(SYS_IMP_APL_UPMCR0_EL1)) == UPMCR0_IMODE_FIQ &&
57862306a36Sopenharmony_ci			(read_sysreg_s(SYS_IMP_APL_UPMSR_EL1) & UPMSR_IACT)) {
57962306a36Sopenharmony_ci		/* Same story with uncore PMCs */
58062306a36Sopenharmony_ci		pr_err_ratelimited("Uncore PMC FIQ fired. Masking.\n");
58162306a36Sopenharmony_ci		sysreg_clear_set_s(SYS_IMP_APL_UPMCR0_EL1, UPMCR0_IMODE,
58262306a36Sopenharmony_ci				   FIELD_PREP(UPMCR0_IMODE, UPMCR0_IMODE_OFF));
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_cistatic int aic_fiq_set_type(struct irq_data *d, unsigned int type)
58762306a36Sopenharmony_ci{
58862306a36Sopenharmony_ci	return (type == IRQ_TYPE_LEVEL_HIGH) ? 0 : -EINVAL;
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_cistatic struct irq_chip fiq_chip = {
59262306a36Sopenharmony_ci	.name = "AIC-FIQ",
59362306a36Sopenharmony_ci	.irq_mask = aic_fiq_mask,
59462306a36Sopenharmony_ci	.irq_unmask = aic_fiq_unmask,
59562306a36Sopenharmony_ci	.irq_ack = aic_fiq_set_mask,
59662306a36Sopenharmony_ci	.irq_eoi = aic_fiq_eoi,
59762306a36Sopenharmony_ci	.irq_set_type = aic_fiq_set_type,
59862306a36Sopenharmony_ci};
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci/*
60162306a36Sopenharmony_ci * Main IRQ domain
60262306a36Sopenharmony_ci */
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_cistatic int aic_irq_domain_map(struct irq_domain *id, unsigned int irq,
60562306a36Sopenharmony_ci			      irq_hw_number_t hw)
60662306a36Sopenharmony_ci{
60762306a36Sopenharmony_ci	struct aic_irq_chip *ic = id->host_data;
60862306a36Sopenharmony_ci	u32 type = FIELD_GET(AIC_EVENT_TYPE, hw);
60962306a36Sopenharmony_ci	struct irq_chip *chip = &aic_chip;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	if (ic->info.version == 2)
61262306a36Sopenharmony_ci		chip = &aic2_chip;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	if (type == AIC_EVENT_TYPE_IRQ) {
61562306a36Sopenharmony_ci		irq_domain_set_info(id, irq, hw, chip, id->host_data,
61662306a36Sopenharmony_ci				    handle_fasteoi_irq, NULL, NULL);
61762306a36Sopenharmony_ci		irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(irq)));
61862306a36Sopenharmony_ci	} else {
61962306a36Sopenharmony_ci		int fiq = FIELD_GET(AIC_EVENT_NUM, hw);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci		switch (fiq) {
62262306a36Sopenharmony_ci		case AIC_CPU_PMU_P:
62362306a36Sopenharmony_ci		case AIC_CPU_PMU_E:
62462306a36Sopenharmony_ci			irq_set_percpu_devid_partition(irq, &ic->fiq_aff[fiq]->aff);
62562306a36Sopenharmony_ci			break;
62662306a36Sopenharmony_ci		default:
62762306a36Sopenharmony_ci			irq_set_percpu_devid(irq);
62862306a36Sopenharmony_ci			break;
62962306a36Sopenharmony_ci		}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci		irq_domain_set_info(id, irq, hw, &fiq_chip, id->host_data,
63262306a36Sopenharmony_ci				    handle_percpu_devid_irq, NULL, NULL);
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	return 0;
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_cistatic int aic_irq_domain_translate(struct irq_domain *id,
63962306a36Sopenharmony_ci				    struct irq_fwspec *fwspec,
64062306a36Sopenharmony_ci				    unsigned long *hwirq,
64162306a36Sopenharmony_ci				    unsigned int *type)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	struct aic_irq_chip *ic = id->host_data;
64462306a36Sopenharmony_ci	u32 *args;
64562306a36Sopenharmony_ci	u32 die = 0;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	if (fwspec->param_count < 3 || fwspec->param_count > 4 ||
64862306a36Sopenharmony_ci	    !is_of_node(fwspec->fwnode))
64962306a36Sopenharmony_ci		return -EINVAL;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	args = &fwspec->param[1];
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	if (fwspec->param_count == 4) {
65462306a36Sopenharmony_ci		die = args[0];
65562306a36Sopenharmony_ci		args++;
65662306a36Sopenharmony_ci	}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	switch (fwspec->param[0]) {
65962306a36Sopenharmony_ci	case AIC_IRQ:
66062306a36Sopenharmony_ci		if (die >= ic->nr_die)
66162306a36Sopenharmony_ci			return -EINVAL;
66262306a36Sopenharmony_ci		if (args[0] >= ic->nr_irq)
66362306a36Sopenharmony_ci			return -EINVAL;
66462306a36Sopenharmony_ci		*hwirq = AIC_IRQ_HWIRQ(die, args[0]);
66562306a36Sopenharmony_ci		break;
66662306a36Sopenharmony_ci	case AIC_FIQ:
66762306a36Sopenharmony_ci		if (die != 0)
66862306a36Sopenharmony_ci			return -EINVAL;
66962306a36Sopenharmony_ci		if (args[0] >= AIC_NR_FIQ)
67062306a36Sopenharmony_ci			return -EINVAL;
67162306a36Sopenharmony_ci		*hwirq = AIC_FIQ_HWIRQ(args[0]);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci		/*
67462306a36Sopenharmony_ci		 * In EL1 the non-redirected registers are the guest's,
67562306a36Sopenharmony_ci		 * not EL2's, so remap the hwirqs to match.
67662306a36Sopenharmony_ci		 */
67762306a36Sopenharmony_ci		if (!is_kernel_in_hyp_mode()) {
67862306a36Sopenharmony_ci			switch (args[0]) {
67962306a36Sopenharmony_ci			case AIC_TMR_GUEST_PHYS:
68062306a36Sopenharmony_ci				*hwirq = AIC_FIQ_HWIRQ(AIC_TMR_EL0_PHYS);
68162306a36Sopenharmony_ci				break;
68262306a36Sopenharmony_ci			case AIC_TMR_GUEST_VIRT:
68362306a36Sopenharmony_ci				*hwirq = AIC_FIQ_HWIRQ(AIC_TMR_EL0_VIRT);
68462306a36Sopenharmony_ci				break;
68562306a36Sopenharmony_ci			case AIC_TMR_HV_PHYS:
68662306a36Sopenharmony_ci			case AIC_TMR_HV_VIRT:
68762306a36Sopenharmony_ci				return -ENOENT;
68862306a36Sopenharmony_ci			default:
68962306a36Sopenharmony_ci				break;
69062306a36Sopenharmony_ci			}
69162306a36Sopenharmony_ci		}
69262306a36Sopenharmony_ci		break;
69362306a36Sopenharmony_ci	default:
69462306a36Sopenharmony_ci		return -EINVAL;
69562306a36Sopenharmony_ci	}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	*type = args[1] & IRQ_TYPE_SENSE_MASK;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	return 0;
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_cistatic int aic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
70362306a36Sopenharmony_ci				unsigned int nr_irqs, void *arg)
70462306a36Sopenharmony_ci{
70562306a36Sopenharmony_ci	unsigned int type = IRQ_TYPE_NONE;
70662306a36Sopenharmony_ci	struct irq_fwspec *fwspec = arg;
70762306a36Sopenharmony_ci	irq_hw_number_t hwirq;
70862306a36Sopenharmony_ci	int i, ret;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	ret = aic_irq_domain_translate(domain, fwspec, &hwirq, &type);
71162306a36Sopenharmony_ci	if (ret)
71262306a36Sopenharmony_ci		return ret;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	for (i = 0; i < nr_irqs; i++) {
71562306a36Sopenharmony_ci		ret = aic_irq_domain_map(domain, virq + i, hwirq + i);
71662306a36Sopenharmony_ci		if (ret)
71762306a36Sopenharmony_ci			return ret;
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	return 0;
72162306a36Sopenharmony_ci}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_cistatic void aic_irq_domain_free(struct irq_domain *domain, unsigned int virq,
72462306a36Sopenharmony_ci				unsigned int nr_irqs)
72562306a36Sopenharmony_ci{
72662306a36Sopenharmony_ci	int i;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	for (i = 0; i < nr_irqs; i++) {
72962306a36Sopenharmony_ci		struct irq_data *d = irq_domain_get_irq_data(domain, virq + i);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci		irq_set_handler(virq + i, NULL);
73262306a36Sopenharmony_ci		irq_domain_reset_irq_data(d);
73362306a36Sopenharmony_ci	}
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_cistatic const struct irq_domain_ops aic_irq_domain_ops = {
73762306a36Sopenharmony_ci	.translate	= aic_irq_domain_translate,
73862306a36Sopenharmony_ci	.alloc		= aic_irq_domain_alloc,
73962306a36Sopenharmony_ci	.free		= aic_irq_domain_free,
74062306a36Sopenharmony_ci};
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci/*
74362306a36Sopenharmony_ci * IPI irqchip
74462306a36Sopenharmony_ci */
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_cistatic void aic_ipi_send_fast(int cpu)
74762306a36Sopenharmony_ci{
74862306a36Sopenharmony_ci	u64 mpidr = cpu_logical_map(cpu);
74962306a36Sopenharmony_ci	u64 my_mpidr = read_cpuid_mpidr();
75062306a36Sopenharmony_ci	u64 cluster = MPIDR_CLUSTER(mpidr);
75162306a36Sopenharmony_ci	u64 idx = MPIDR_CPU(mpidr);
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	if (MPIDR_CLUSTER(my_mpidr) == cluster)
75462306a36Sopenharmony_ci		write_sysreg_s(FIELD_PREP(IPI_RR_CPU, idx),
75562306a36Sopenharmony_ci			       SYS_IMP_APL_IPI_RR_LOCAL_EL1);
75662306a36Sopenharmony_ci	else
75762306a36Sopenharmony_ci		write_sysreg_s(FIELD_PREP(IPI_RR_CPU, idx) | FIELD_PREP(IPI_RR_CLUSTER, cluster),
75862306a36Sopenharmony_ci			       SYS_IMP_APL_IPI_RR_GLOBAL_EL1);
75962306a36Sopenharmony_ci	isb();
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_cistatic void aic_handle_ipi(struct pt_regs *regs)
76362306a36Sopenharmony_ci{
76462306a36Sopenharmony_ci	/*
76562306a36Sopenharmony_ci	 * Ack the IPI. We need to order this after the AIC event read, but
76662306a36Sopenharmony_ci	 * that is enforced by normal MMIO ordering guarantees.
76762306a36Sopenharmony_ci	 *
76862306a36Sopenharmony_ci	 * For the Fast IPI case, this needs to be ordered before the vIPI
76962306a36Sopenharmony_ci	 * handling below, so we need to isb();
77062306a36Sopenharmony_ci	 */
77162306a36Sopenharmony_ci	if (static_branch_likely(&use_fast_ipi)) {
77262306a36Sopenharmony_ci		write_sysreg_s(IPI_SR_PENDING, SYS_IMP_APL_IPI_SR_EL1);
77362306a36Sopenharmony_ci		isb();
77462306a36Sopenharmony_ci	} else {
77562306a36Sopenharmony_ci		aic_ic_write(aic_irqc, AIC_IPI_ACK, AIC_IPI_OTHER);
77662306a36Sopenharmony_ci	}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	ipi_mux_process();
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	/*
78162306a36Sopenharmony_ci	 * No ordering needed here; at worst this just changes the timing of
78262306a36Sopenharmony_ci	 * when the next IPI will be delivered.
78362306a36Sopenharmony_ci	 */
78462306a36Sopenharmony_ci	if (!static_branch_likely(&use_fast_ipi))
78562306a36Sopenharmony_ci		aic_ic_write(aic_irqc, AIC_IPI_MASK_CLR, AIC_IPI_OTHER);
78662306a36Sopenharmony_ci}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_cistatic void aic_ipi_send_single(unsigned int cpu)
78962306a36Sopenharmony_ci{
79062306a36Sopenharmony_ci	if (static_branch_likely(&use_fast_ipi))
79162306a36Sopenharmony_ci		aic_ipi_send_fast(cpu);
79262306a36Sopenharmony_ci	else
79362306a36Sopenharmony_ci		aic_ic_write(aic_irqc, AIC_IPI_SEND, AIC_IPI_SEND_CPU(cpu));
79462306a36Sopenharmony_ci}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_cistatic int __init aic_init_smp(struct aic_irq_chip *irqc, struct device_node *node)
79762306a36Sopenharmony_ci{
79862306a36Sopenharmony_ci	int base_ipi;
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	base_ipi = ipi_mux_create(AIC_NR_SWIPI, aic_ipi_send_single);
80162306a36Sopenharmony_ci	if (WARN_ON(base_ipi <= 0))
80262306a36Sopenharmony_ci		return -ENODEV;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	set_smp_ipi_range(base_ipi, AIC_NR_SWIPI);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	return 0;
80762306a36Sopenharmony_ci}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_cistatic int aic_init_cpu(unsigned int cpu)
81062306a36Sopenharmony_ci{
81162306a36Sopenharmony_ci	/* Mask all hard-wired per-CPU IRQ/FIQ sources */
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	/* Pending Fast IPI FIQs */
81462306a36Sopenharmony_ci	write_sysreg_s(IPI_SR_PENDING, SYS_IMP_APL_IPI_SR_EL1);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	/* Timer FIQs */
81762306a36Sopenharmony_ci	sysreg_clear_set(cntp_ctl_el0, 0, ARCH_TIMER_CTRL_IT_MASK);
81862306a36Sopenharmony_ci	sysreg_clear_set(cntv_ctl_el0, 0, ARCH_TIMER_CTRL_IT_MASK);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	/* EL2-only (VHE mode) IRQ sources */
82162306a36Sopenharmony_ci	if (is_kernel_in_hyp_mode()) {
82262306a36Sopenharmony_ci		/* Guest timers */
82362306a36Sopenharmony_ci		sysreg_clear_set_s(SYS_IMP_APL_VM_TMR_FIQ_ENA_EL2,
82462306a36Sopenharmony_ci				   VM_TMR_FIQ_ENABLE_V | VM_TMR_FIQ_ENABLE_P, 0);
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci		/* vGIC maintenance IRQ */
82762306a36Sopenharmony_ci		sysreg_clear_set_s(SYS_ICH_HCR_EL2, ICH_HCR_EN, 0);
82862306a36Sopenharmony_ci	}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	/* PMC FIQ */
83162306a36Sopenharmony_ci	sysreg_clear_set_s(SYS_IMP_APL_PMCR0_EL1, PMCR0_IMODE | PMCR0_IACT,
83262306a36Sopenharmony_ci			   FIELD_PREP(PMCR0_IMODE, PMCR0_IMODE_OFF));
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	/* Uncore PMC FIQ */
83562306a36Sopenharmony_ci	sysreg_clear_set_s(SYS_IMP_APL_UPMCR0_EL1, UPMCR0_IMODE,
83662306a36Sopenharmony_ci			   FIELD_PREP(UPMCR0_IMODE, UPMCR0_IMODE_OFF));
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	/* Commit all of the above */
83962306a36Sopenharmony_ci	isb();
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	if (aic_irqc->info.version == 1) {
84262306a36Sopenharmony_ci		/*
84362306a36Sopenharmony_ci		 * Make sure the kernel's idea of logical CPU order is the same as AIC's
84462306a36Sopenharmony_ci		 * If we ever end up with a mismatch here, we will have to introduce
84562306a36Sopenharmony_ci		 * a mapping table similar to what other irqchip drivers do.
84662306a36Sopenharmony_ci		 */
84762306a36Sopenharmony_ci		WARN_ON(aic_ic_read(aic_irqc, AIC_WHOAMI) != smp_processor_id());
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci		/*
85062306a36Sopenharmony_ci		 * Always keep IPIs unmasked at the hardware level (except auto-masking
85162306a36Sopenharmony_ci		 * by AIC during processing). We manage masks at the vIPI level.
85262306a36Sopenharmony_ci		 * These registers only exist on AICv1, AICv2 always uses fast IPIs.
85362306a36Sopenharmony_ci		 */
85462306a36Sopenharmony_ci		aic_ic_write(aic_irqc, AIC_IPI_ACK, AIC_IPI_SELF | AIC_IPI_OTHER);
85562306a36Sopenharmony_ci		if (static_branch_likely(&use_fast_ipi)) {
85662306a36Sopenharmony_ci			aic_ic_write(aic_irqc, AIC_IPI_MASK_SET, AIC_IPI_SELF | AIC_IPI_OTHER);
85762306a36Sopenharmony_ci		} else {
85862306a36Sopenharmony_ci			aic_ic_write(aic_irqc, AIC_IPI_MASK_SET, AIC_IPI_SELF);
85962306a36Sopenharmony_ci			aic_ic_write(aic_irqc, AIC_IPI_MASK_CLR, AIC_IPI_OTHER);
86062306a36Sopenharmony_ci		}
86162306a36Sopenharmony_ci	}
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	/* Initialize the local mask state */
86462306a36Sopenharmony_ci	__this_cpu_write(aic_fiq_unmasked, 0);
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	return 0;
86762306a36Sopenharmony_ci}
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_cistatic struct gic_kvm_info vgic_info __initdata = {
87062306a36Sopenharmony_ci	.type			= GIC_V3,
87162306a36Sopenharmony_ci	.no_maint_irq_mask	= true,
87262306a36Sopenharmony_ci	.no_hw_deactivation	= true,
87362306a36Sopenharmony_ci};
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_cistatic void build_fiq_affinity(struct aic_irq_chip *ic, struct device_node *aff)
87662306a36Sopenharmony_ci{
87762306a36Sopenharmony_ci	int i, n;
87862306a36Sopenharmony_ci	u32 fiq;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	if (of_property_read_u32(aff, "apple,fiq-index", &fiq) ||
88162306a36Sopenharmony_ci	    WARN_ON(fiq >= AIC_NR_FIQ) || ic->fiq_aff[fiq])
88262306a36Sopenharmony_ci		return;
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	n = of_property_count_elems_of_size(aff, "cpus", sizeof(u32));
88562306a36Sopenharmony_ci	if (WARN_ON(n < 0))
88662306a36Sopenharmony_ci		return;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	ic->fiq_aff[fiq] = kzalloc(sizeof(*ic->fiq_aff[fiq]), GFP_KERNEL);
88962306a36Sopenharmony_ci	if (!ic->fiq_aff[fiq])
89062306a36Sopenharmony_ci		return;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	for (i = 0; i < n; i++) {
89362306a36Sopenharmony_ci		struct device_node *cpu_node;
89462306a36Sopenharmony_ci		u32 cpu_phandle;
89562306a36Sopenharmony_ci		int cpu;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci		if (of_property_read_u32_index(aff, "cpus", i, &cpu_phandle))
89862306a36Sopenharmony_ci			continue;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci		cpu_node = of_find_node_by_phandle(cpu_phandle);
90162306a36Sopenharmony_ci		if (WARN_ON(!cpu_node))
90262306a36Sopenharmony_ci			continue;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci		cpu = of_cpu_node_to_id(cpu_node);
90562306a36Sopenharmony_ci		of_node_put(cpu_node);
90662306a36Sopenharmony_ci		if (WARN_ON(cpu < 0))
90762306a36Sopenharmony_ci			continue;
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci		cpumask_set_cpu(cpu, &ic->fiq_aff[fiq]->aff);
91062306a36Sopenharmony_ci	}
91162306a36Sopenharmony_ci}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_cistatic int __init aic_of_ic_init(struct device_node *node, struct device_node *parent)
91462306a36Sopenharmony_ci{
91562306a36Sopenharmony_ci	int i, die;
91662306a36Sopenharmony_ci	u32 off, start_off;
91762306a36Sopenharmony_ci	void __iomem *regs;
91862306a36Sopenharmony_ci	struct aic_irq_chip *irqc;
91962306a36Sopenharmony_ci	struct device_node *affs;
92062306a36Sopenharmony_ci	const struct of_device_id *match;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	regs = of_iomap(node, 0);
92362306a36Sopenharmony_ci	if (WARN_ON(!regs))
92462306a36Sopenharmony_ci		return -EIO;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	irqc = kzalloc(sizeof(*irqc), GFP_KERNEL);
92762306a36Sopenharmony_ci	if (!irqc) {
92862306a36Sopenharmony_ci		iounmap(regs);
92962306a36Sopenharmony_ci		return -ENOMEM;
93062306a36Sopenharmony_ci	}
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	irqc->base = regs;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	match = of_match_node(aic_info_match, node);
93562306a36Sopenharmony_ci	if (!match)
93662306a36Sopenharmony_ci		goto err_unmap;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	irqc->info = *(struct aic_info *)match->data;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	aic_irqc = irqc;
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	switch (irqc->info.version) {
94362306a36Sopenharmony_ci	case 1: {
94462306a36Sopenharmony_ci		u32 info;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci		info = aic_ic_read(irqc, AIC_INFO);
94762306a36Sopenharmony_ci		irqc->nr_irq = FIELD_GET(AIC_INFO_NR_IRQ, info);
94862306a36Sopenharmony_ci		irqc->max_irq = AIC_MAX_IRQ;
94962306a36Sopenharmony_ci		irqc->nr_die = irqc->max_die = 1;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci		off = start_off = irqc->info.target_cpu;
95262306a36Sopenharmony_ci		off += sizeof(u32) * irqc->max_irq; /* TARGET_CPU */
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci		irqc->event = irqc->base;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci		break;
95762306a36Sopenharmony_ci	}
95862306a36Sopenharmony_ci	case 2: {
95962306a36Sopenharmony_ci		u32 info1, info3;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci		info1 = aic_ic_read(irqc, AIC2_INFO1);
96262306a36Sopenharmony_ci		info3 = aic_ic_read(irqc, AIC2_INFO3);
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci		irqc->nr_irq = FIELD_GET(AIC2_INFO1_NR_IRQ, info1);
96562306a36Sopenharmony_ci		irqc->max_irq = FIELD_GET(AIC2_INFO3_MAX_IRQ, info3);
96662306a36Sopenharmony_ci		irqc->nr_die = FIELD_GET(AIC2_INFO1_LAST_DIE, info1) + 1;
96762306a36Sopenharmony_ci		irqc->max_die = FIELD_GET(AIC2_INFO3_MAX_DIE, info3);
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci		off = start_off = irqc->info.irq_cfg;
97062306a36Sopenharmony_ci		off += sizeof(u32) * irqc->max_irq; /* IRQ_CFG */
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci		irqc->event = of_iomap(node, 1);
97362306a36Sopenharmony_ci		if (WARN_ON(!irqc->event))
97462306a36Sopenharmony_ci			goto err_unmap;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci		break;
97762306a36Sopenharmony_ci	}
97862306a36Sopenharmony_ci	}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	irqc->info.sw_set = off;
98162306a36Sopenharmony_ci	off += sizeof(u32) * (irqc->max_irq >> 5); /* SW_SET */
98262306a36Sopenharmony_ci	irqc->info.sw_clr = off;
98362306a36Sopenharmony_ci	off += sizeof(u32) * (irqc->max_irq >> 5); /* SW_CLR */
98462306a36Sopenharmony_ci	irqc->info.mask_set = off;
98562306a36Sopenharmony_ci	off += sizeof(u32) * (irqc->max_irq >> 5); /* MASK_SET */
98662306a36Sopenharmony_ci	irqc->info.mask_clr = off;
98762306a36Sopenharmony_ci	off += sizeof(u32) * (irqc->max_irq >> 5); /* MASK_CLR */
98862306a36Sopenharmony_ci	off += sizeof(u32) * (irqc->max_irq >> 5); /* HW_STATE */
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	if (irqc->info.fast_ipi)
99162306a36Sopenharmony_ci		static_branch_enable(&use_fast_ipi);
99262306a36Sopenharmony_ci	else
99362306a36Sopenharmony_ci		static_branch_disable(&use_fast_ipi);
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	irqc->info.die_stride = off - start_off;
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	irqc->hw_domain = irq_domain_create_tree(of_node_to_fwnode(node),
99862306a36Sopenharmony_ci						 &aic_irq_domain_ops, irqc);
99962306a36Sopenharmony_ci	if (WARN_ON(!irqc->hw_domain))
100062306a36Sopenharmony_ci		goto err_unmap;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	irq_domain_update_bus_token(irqc->hw_domain, DOMAIN_BUS_WIRED);
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	if (aic_init_smp(irqc, node))
100562306a36Sopenharmony_ci		goto err_remove_domain;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	affs = of_get_child_by_name(node, "affinities");
100862306a36Sopenharmony_ci	if (affs) {
100962306a36Sopenharmony_ci		struct device_node *chld;
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci		for_each_child_of_node(affs, chld)
101262306a36Sopenharmony_ci			build_fiq_affinity(irqc, chld);
101362306a36Sopenharmony_ci	}
101462306a36Sopenharmony_ci	of_node_put(affs);
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	set_handle_irq(aic_handle_irq);
101762306a36Sopenharmony_ci	set_handle_fiq(aic_handle_fiq);
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	off = 0;
102062306a36Sopenharmony_ci	for (die = 0; die < irqc->nr_die; die++) {
102162306a36Sopenharmony_ci		for (i = 0; i < BITS_TO_U32(irqc->nr_irq); i++)
102262306a36Sopenharmony_ci			aic_ic_write(irqc, irqc->info.mask_set + off + i * 4, U32_MAX);
102362306a36Sopenharmony_ci		for (i = 0; i < BITS_TO_U32(irqc->nr_irq); i++)
102462306a36Sopenharmony_ci			aic_ic_write(irqc, irqc->info.sw_clr + off + i * 4, U32_MAX);
102562306a36Sopenharmony_ci		if (irqc->info.target_cpu)
102662306a36Sopenharmony_ci			for (i = 0; i < irqc->nr_irq; i++)
102762306a36Sopenharmony_ci				aic_ic_write(irqc, irqc->info.target_cpu + off + i * 4, 1);
102862306a36Sopenharmony_ci		off += irqc->info.die_stride;
102962306a36Sopenharmony_ci	}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	if (irqc->info.version == 2) {
103262306a36Sopenharmony_ci		u32 config = aic_ic_read(irqc, AIC2_CONFIG);
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci		config |= AIC2_CONFIG_ENABLE;
103562306a36Sopenharmony_ci		aic_ic_write(irqc, AIC2_CONFIG, config);
103662306a36Sopenharmony_ci	}
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	if (!is_kernel_in_hyp_mode())
103962306a36Sopenharmony_ci		pr_info("Kernel running in EL1, mapping interrupts");
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	if (static_branch_likely(&use_fast_ipi))
104262306a36Sopenharmony_ci		pr_info("Using Fast IPIs");
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	cpuhp_setup_state(CPUHP_AP_IRQ_APPLE_AIC_STARTING,
104562306a36Sopenharmony_ci			  "irqchip/apple-aic/ipi:starting",
104662306a36Sopenharmony_ci			  aic_init_cpu, NULL);
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	if (is_kernel_in_hyp_mode()) {
104962306a36Sopenharmony_ci		struct irq_fwspec mi = {
105062306a36Sopenharmony_ci			.fwnode		= of_node_to_fwnode(node),
105162306a36Sopenharmony_ci			.param_count	= 3,
105262306a36Sopenharmony_ci			.param		= {
105362306a36Sopenharmony_ci				[0]	= AIC_FIQ, /* This is a lie */
105462306a36Sopenharmony_ci				[1]	= AIC_VGIC_MI,
105562306a36Sopenharmony_ci				[2]	= IRQ_TYPE_LEVEL_HIGH,
105662306a36Sopenharmony_ci			},
105762306a36Sopenharmony_ci		};
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci		vgic_info.maint_irq = irq_create_fwspec_mapping(&mi);
106062306a36Sopenharmony_ci		WARN_ON(!vgic_info.maint_irq);
106162306a36Sopenharmony_ci	}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	vgic_set_kvm_info(&vgic_info);
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	pr_info("Initialized with %d/%d IRQs * %d/%d die(s), %d FIQs, %d vIPIs",
106662306a36Sopenharmony_ci		irqc->nr_irq, irqc->max_irq, irqc->nr_die, irqc->max_die, AIC_NR_FIQ, AIC_NR_SWIPI);
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	return 0;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_cierr_remove_domain:
107162306a36Sopenharmony_ci	irq_domain_remove(irqc->hw_domain);
107262306a36Sopenharmony_cierr_unmap:
107362306a36Sopenharmony_ci	if (irqc->event && irqc->event != irqc->base)
107462306a36Sopenharmony_ci		iounmap(irqc->event);
107562306a36Sopenharmony_ci	iounmap(irqc->base);
107662306a36Sopenharmony_ci	kfree(irqc);
107762306a36Sopenharmony_ci	return -ENODEV;
107862306a36Sopenharmony_ci}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ciIRQCHIP_DECLARE(apple_aic, "apple,aic", aic_of_ic_init);
108162306a36Sopenharmony_ciIRQCHIP_DECLARE(apple_aic2, "apple,aic2", aic_of_ic_init);
1082