lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <cc611dda-d1e4-4793-9bb2-0eaa47277584@huawei.com>
Date: Thu, 7 Aug 2025 19:52:58 +0800
From: Jinjie Ruan <ruanjinjie@...wei.com>
To: Lorenzo Pieralisi <lpieralisi@...nel.org>, Marc Zyngier <maz@...nel.org>,
	Thomas Gleixner <tglx@...utronix.de>, Rob Herring <robh@...nel.org>,
	Krzysztof Kozlowski <krzk+dt@...nel.org>, Conor Dooley <conor+dt@...nel.org>,
	Catalin Marinas <catalin.marinas@....com>, Will Deacon <will@...nel.org>
CC: Arnd Bergmann <arnd@...db.de>, Sascha Bischoff <sascha.bischoff@....com>,
	Jonathan Cameron <Jonathan.Cameron@...wei.com>, Timothy Hayes
	<timothy.hayes@....com>, Bjorn Helgaas <bhelgaas@...gle.com>, "Liam R.
 Howlett" <Liam.Howlett@...cle.com>, Peter Maydell <peter.maydell@...aro.org>,
	Mark Rutland <mark.rutland@....com>, Jiri Slaby <jirislaby@...nel.org>,
	<linux-arm-kernel@...ts.infradead.org>, <linux-kernel@...r.kernel.org>,
	<devicetree@...r.kernel.org>, <linux-pci@...r.kernel.org>
Subject: Re: [PATCH v7 22/31] irqchip/gic-v5: Add GICv5 LPI/IPI support



On 2025/7/3 18:25, Lorenzo Pieralisi wrote:
> An IRS supports Logical Peripheral Interrupts (LPIs) and implement
> Linux IPIs on top of it.
> 
> LPIs are used for interrupt signals that are translated by a
> GICv5 ITS (Interrupt Translation Service) but also for software
> generated IRQs - namely interrupts that are not driven by a HW
> signal, ie IPIs.
> 
> LPIs rely on memory storage for interrupt routing and state.
> 
> LPIs state and routing information is kept in the Interrupt
> State Table (IST).
> 
> IRSes provide support for 1- or 2-level IST tables configured
> to support a maximum number of interrupts that depend on the
> OS configuration and the HW capabilities.
> 
> On systems that provide 2-level IST support, always allow
> the maximum number of LPIs; On systems with only 1-level
> support, limit the number of LPIs to 2^12 to prevent
> wasting memory (presumably a system that supports a 1-level
> only IST is not expecting a large number of interrupts).
> 
> On a 2-level IST system, L2 entries are allocated on
> demand.
> 
> The IST table memory is allocated using the kmalloc() interface;
> the allocation required may be smaller than a page and must be
> made up of contiguous physical pages if larger than a page.
> 
> On systems where the IRS is not cache-coherent with the CPUs,
> cache mainteinance operations are executed to clean and
> invalidate the allocated memory to the point of coherency
> making it visible to the IRS components.
> 
> On GICv5 systems, IPIs are implemented using LPIs.
> 
> Add an LPI IRQ domain and implement an IPI-specific IRQ domain created
> as a child/subdomain of the LPI domain to allocate the required number
> of LPIs needed to implement the IPIs.
> 
> IPIs are backed by LPIs, add LPIs allocation/de-allocation
> functions.
> 
> The LPI INTID namespace is managed using an IDA to alloc/free LPI INTIDs.
> 
> Associate an IPI irqchip with IPI IRQ descriptors to provide
> core code with the irqchip.ipi_send_single() method required
> to raise an IPI.
> 
> Co-developed-by: Sascha Bischoff <sascha.bischoff@....com>
> Signed-off-by: Sascha Bischoff <sascha.bischoff@....com>
> Co-developed-by: Timothy Hayes <timothy.hayes@....com>
> Signed-off-by: Timothy Hayes <timothy.hayes@....com>
> Signed-off-by: Lorenzo Pieralisi <lpieralisi@...nel.org>
> Reviewed-by: Marc Zyngier <maz@...nel.org>
> Cc: Will Deacon <will@...nel.org>
> Cc: Thomas Gleixner <tglx@...utronix.de>
> Cc: Catalin Marinas <catalin.marinas@....com>
> Cc: Marc Zyngier <maz@...nel.org>
> ---
>  arch/arm64/include/asm/smp.h       |  17 ++
>  arch/arm64/include/asm/sysreg.h    |   6 +
>  arch/arm64/kernel/smp.c            |  17 --
>  drivers/irqchip/irq-gic-v5-irs.c   | 364 +++++++++++++++++++++++++++++++++++++
>  drivers/irqchip/irq-gic-v5.c       | 299 +++++++++++++++++++++++++++++-
>  include/linux/irqchip/arm-gic-v5.h |  63 ++++++-
>  6 files changed, 746 insertions(+), 20 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h
> index d6fd6efb66a6..d48ef6d5abcc 100644
> --- a/arch/arm64/include/asm/smp.h
> +++ b/arch/arm64/include/asm/smp.h
> @@ -50,6 +50,23 @@ struct seq_file;
>   */
>  extern void smp_init_cpus(void);
>  
> +enum ipi_msg_type {
> +	IPI_RESCHEDULE,
> +	IPI_CALL_FUNC,
> +	IPI_CPU_STOP,
> +	IPI_CPU_STOP_NMI,
> +	IPI_TIMER,
> +	IPI_IRQ_WORK,
> +	NR_IPI,
> +	/*
> +	 * Any enum >= NR_IPI and < MAX_IPI is special and not tracable
> +	 * with trace_ipi_*
> +	 */
> +	IPI_CPU_BACKTRACE = NR_IPI,
> +	IPI_KGDB_ROUNDUP,
> +	MAX_IPI
> +};
> +
>  /*
>   * Register IPI interrupts with the arch SMP code
>   */
> diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
> index efd2e7a1fbe2..948007cd3684 100644
> --- a/arch/arm64/include/asm/sysreg.h
> +++ b/arch/arm64/include/asm/sysreg.h
> @@ -1088,6 +1088,7 @@
>  #define GICV5_OP_GIC_CDAFF		sys_insn(1, 0, 12, 1, 3)
>  #define GICV5_OP_GIC_CDDI		sys_insn(1, 0, 12, 2, 0)
>  #define GICV5_OP_GIC_CDDIS		sys_insn(1, 0, 12, 1, 0)
> +#define GICV5_OP_GIC_CDHM		sys_insn(1, 0, 12, 2, 1)
>  #define GICV5_OP_GIC_CDEN		sys_insn(1, 0, 12, 1, 1)
>  #define GICV5_OP_GIC_CDEOI		sys_insn(1, 0, 12, 1, 7)
>  #define GICV5_OP_GIC_CDPEND		sys_insn(1, 0, 12, 1, 4)
> @@ -1115,6 +1116,11 @@
>  #define GICV5_GIC_CDEN_TYPE_MASK	GENMASK_ULL(31, 29)
>  #define GICV5_GIC_CDEN_ID_MASK		GENMASK_ULL(23, 0)
>  
> +/* Definitions for GIC CDHM */
> +#define GICV5_GIC_CDHM_HM_MASK		BIT_ULL(32)
> +#define GICV5_GIC_CDHM_TYPE_MASK	GENMASK_ULL(31, 29)
> +#define GICV5_GIC_CDHM_ID_MASK		GENMASK_ULL(23, 0)
> +
>  /* Definitions for GIC CDPEND */
>  #define GICV5_GIC_CDPEND_PENDING_MASK	BIT_ULL(32)
>  #define GICV5_GIC_CDPEND_TYPE_MASK	GENMASK_ULL(31, 29)
> diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
> index 2c501e917d38..4797e2c70014 100644
> --- a/arch/arm64/kernel/smp.c
> +++ b/arch/arm64/kernel/smp.c
> @@ -64,23 +64,6 @@ struct secondary_data secondary_data;
>  /* Number of CPUs which aren't online, but looping in kernel text. */
>  static int cpus_stuck_in_kernel;
>  
> -enum ipi_msg_type {
> -	IPI_RESCHEDULE,
> -	IPI_CALL_FUNC,
> -	IPI_CPU_STOP,
> -	IPI_CPU_STOP_NMI,
> -	IPI_TIMER,
> -	IPI_IRQ_WORK,
> -	NR_IPI,
> -	/*
> -	 * Any enum >= NR_IPI and < MAX_IPI is special and not tracable
> -	 * with trace_ipi_*
> -	 */
> -	IPI_CPU_BACKTRACE = NR_IPI,
> -	IPI_KGDB_ROUNDUP,
> -	MAX_IPI
> -};
> -
>  static int ipi_irq_base __ro_after_init;
>  static int nr_ipi __ro_after_init = NR_IPI;
>  
> diff --git a/drivers/irqchip/irq-gic-v5-irs.c b/drivers/irqchip/irq-gic-v5-irs.c
> index fba8efceb26e..f00a4a6fece7 100644
> --- a/drivers/irqchip/irq-gic-v5-irs.c
> +++ b/drivers/irqchip/irq-gic-v5-irs.c
> @@ -5,12 +5,20 @@
>  
>  #define pr_fmt(fmt)	"GICv5 IRS: " fmt
>  
> +#include <linux/log2.h>
>  #include <linux/of.h>
>  #include <linux/of_address.h>
>  
>  #include <linux/irqchip.h>
>  #include <linux/irqchip/arm-gic-v5.h>
>  
> +/*
> + * Hardcoded ID_BITS limit for systems supporting only a 1-level IST
> + * table. Systems supporting only a 1-level IST table aren't expected
> + * to require more than 2^12 LPIs. Tweak as required.
> + */
> +#define LPI_ID_BITS_LINEAR		12
> +
>  #define IRS_FLAGS_NON_COHERENT		BIT(0)
>  
>  static DEFINE_PER_CPU_READ_MOSTLY(struct gicv5_irs_chip_data *, per_cpu_irs_data);
> @@ -28,6 +36,331 @@ static void irs_writel_relaxed(struct gicv5_irs_chip_data *irs_data,
>  	writel_relaxed(val, irs_data->irs_base + reg_offset);
>  }
>  
> +static u64 irs_readq_relaxed(struct gicv5_irs_chip_data *irs_data,
> +			     const u32 reg_offset)
> +{
> +	return readq_relaxed(irs_data->irs_base + reg_offset);
> +}
> +
> +static void irs_writeq_relaxed(struct gicv5_irs_chip_data *irs_data,
> +			       const u64 val, const u32 reg_offset)
> +{
> +	writeq_relaxed(val, irs_data->irs_base + reg_offset);
> +}
> +
> +/*
> + * The polling wait (in gicv5_wait_for_op_s_atomic()) on a GIC register
> + * provides the memory barriers (through MMIO accessors)
> + * required to synchronize CPU and GIC access to IST memory.
> + */
> +static int gicv5_irs_ist_synchronise(struct gicv5_irs_chip_data *irs_data)
> +{
> +	return gicv5_wait_for_op_atomic(irs_data->irs_base, GICV5_IRS_IST_STATUSR,
> +					GICV5_IRS_IST_STATUSR_IDLE, NULL);
> +}
> +
> +static int __init gicv5_irs_init_ist_linear(struct gicv5_irs_chip_data *irs_data,
> +					    unsigned int lpi_id_bits,
> +					    unsigned int istsz)
> +{
> +	size_t l2istsz;
> +	u32 n, cfgr;
> +	void *ist;
> +	u64 baser;
> +	int ret;
> +
> +	/* Taken from GICv5 specifications 10.2.1.13 IRS_IST_BASER */
> +	n = max(5, lpi_id_bits + 1 + istsz);
> +
> +	l2istsz = BIT(n + 1);
> +	/*
> +	 * Check memory requirements. For a linear IST we cap the
> +	 * number of ID bits to a value that should never exceed
> +	 * kmalloc interface memory allocation limits, so this
> +	 * check is really belt and braces.
> +	 */
> +	if (l2istsz > KMALLOC_MAX_SIZE) {
> +		u8 lpi_id_cap = ilog2(KMALLOC_MAX_SIZE) - 2 + istsz;
> +
> +		pr_warn("Limiting LPI ID bits from %u to %u\n",
> +			lpi_id_bits, lpi_id_cap);
> +		lpi_id_bits = lpi_id_cap;
> +		l2istsz = KMALLOC_MAX_SIZE;
> +	}
> +
> +	ist = kzalloc(l2istsz, GFP_KERNEL);


When kmemleak is on, There is a memory leak occurring as below:


unreferenced object 0xffff00080039a000 (size 4096):
  comm "swapper/0", pid 0, jiffies 4294892296
  hex dump (first 32 bytes):
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  backtrace (crc 0):
    kmemleak_alloc+0x34/0x40
    __kmalloc_noprof+0x320/0x464
    gicv5_irs_iste_alloc+0x1a4/0x484
    gicv5_irq_lpi_domain_alloc+0xe4/0x194
    irq_domain_alloc_irqs_parent+0x78/0xd8
    gicv5_irq_ipi_domain_alloc+0x180/0x238
    irq_domain_alloc_irqs_locked+0x238/0x7d4
    __irq_domain_alloc_irqs+0x88/0x114
    gicv5_of_init+0x284/0x37c
    of_irq_init+0x3b8/0xb18
    irqchip_init+0x18/0x40
    init_IRQ+0x104/0x164
    start_kernel+0x1a4/0x3d4
    __primary_switched+0x8c/0x94


> +	if (!ist)
> +		return -ENOMEM;
> +
> +	if (irs_data->flags & IRS_FLAGS_NON_COHERENT)
> +		dcache_clean_inval_poc((unsigned long)ist,
> +				       (unsigned long)ist + l2istsz);
> +	else
> +		dsb(ishst);
> +
> +	cfgr = FIELD_PREP(GICV5_IRS_IST_CFGR_STRUCTURE,
> +			  GICV5_IRS_IST_CFGR_STRUCTURE_LINEAR)	|
> +	       FIELD_PREP(GICV5_IRS_IST_CFGR_ISTSZ, istsz)	|
> +	       FIELD_PREP(GICV5_IRS_IST_CFGR_L2SZ,
> +			  GICV5_IRS_IST_CFGR_L2SZ_4K)		|
> +	       FIELD_PREP(GICV5_IRS_IST_CFGR_LPI_ID_BITS, lpi_id_bits);
> +	irs_writel_relaxed(irs_data, cfgr, GICV5_IRS_IST_CFGR);
> +
> +	gicv5_global_data.ist.l2 = false;
> +
> +	baser = (virt_to_phys(ist) & GICV5_IRS_IST_BASER_ADDR_MASK) |
> +		FIELD_PREP(GICV5_IRS_IST_BASER_VALID, 0x1);
> +	irs_writeq_relaxed(irs_data, baser, GICV5_IRS_IST_BASER);
> +
> +	ret = gicv5_irs_ist_synchronise(irs_data);
> +	if (ret) {
> +		kfree(ist);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int __init gicv5_irs_init_ist_two_level(struct gicv5_irs_chip_data *irs_data,
> +					       unsigned int lpi_id_bits,
> +					       unsigned int istsz,
> +					       unsigned int l2sz)
> +{
> +	__le64 *l1ist;
> +	u32 cfgr, n;
> +	size_t l1sz;
> +	u64 baser;
> +	int ret;
> +
> +	/* Taken from GICv5 specifications 10.2.1.13 IRS_IST_BASER */
> +	n = max(5, lpi_id_bits - ((10 - istsz) + (2 * l2sz)) + 2);
> +
> +	l1sz = BIT(n + 1);
> +
> +	l1ist = kzalloc(l1sz, GFP_KERNEL);
> +	if (!l1ist)
> +		return -ENOMEM;
> +
> +	if (irs_data->flags & IRS_FLAGS_NON_COHERENT)
> +		dcache_clean_inval_poc((unsigned long)l1ist,
> +				       (unsigned long)l1ist + l1sz);
> +	else
> +		dsb(ishst);
> +
> +	cfgr = FIELD_PREP(GICV5_IRS_IST_CFGR_STRUCTURE,
> +			  GICV5_IRS_IST_CFGR_STRUCTURE_TWO_LEVEL)	|
> +	       FIELD_PREP(GICV5_IRS_IST_CFGR_ISTSZ, istsz)		|
> +	       FIELD_PREP(GICV5_IRS_IST_CFGR_L2SZ, l2sz)		|
> +	       FIELD_PREP(GICV5_IRS_IST_CFGR_LPI_ID_BITS, lpi_id_bits);
> +	irs_writel_relaxed(irs_data, cfgr, GICV5_IRS_IST_CFGR);
> +
> +	/*
> +	 * The L2SZ determine bits required at L2 level. Number of bytes
> +	 * required by metadata is reported through istsz - the number of bits
> +	 * covered by L2 entries scales accordingly.
> +	 */
> +	gicv5_global_data.ist.l2_size = BIT(11 + (2 * l2sz) + 1);
> +	gicv5_global_data.ist.l2_bits = (10 - istsz) + (2 * l2sz);
> +	gicv5_global_data.ist.l1ist_addr = l1ist;
> +	gicv5_global_data.ist.l2 = true;
> +
> +	baser = (virt_to_phys(l1ist) & GICV5_IRS_IST_BASER_ADDR_MASK) |
> +		FIELD_PREP(GICV5_IRS_IST_BASER_VALID, 0x1);
> +	irs_writeq_relaxed(irs_data, baser, GICV5_IRS_IST_BASER);
> +
> +	ret = gicv5_irs_ist_synchronise(irs_data);
> +	if (ret) {
> +		kfree(l1ist);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * Alloc L2 IST entries on demand.
> + *
> + * Locking/serialization is guaranteed by irqdomain core code by
> + * taking the hierarchical domain struct irq_domain.root->mutex.
> + */
> +int gicv5_irs_iste_alloc(const u32 lpi)
> +{
> +	struct gicv5_irs_chip_data *irs_data;
> +	unsigned int index;
> +	u32 l2istr, l2bits;
> +	__le64 *l1ist;
> +	size_t l2size;
> +	void *l2ist;
> +	int ret;
> +
> +	if (!gicv5_global_data.ist.l2)
> +		return 0;
> +
> +	irs_data = per_cpu(per_cpu_irs_data, smp_processor_id());
> +	if (!irs_data)
> +		return -ENOENT;
> +
> +	l2size = gicv5_global_data.ist.l2_size;
> +	l2bits = gicv5_global_data.ist.l2_bits;
> +	l1ist = gicv5_global_data.ist.l1ist_addr;
> +	index = lpi >> l2bits;
> +
> +	if (FIELD_GET(GICV5_ISTL1E_VALID, le64_to_cpu(l1ist[index])))
> +		return 0;
> +
> +	l2ist = kzalloc(l2size, GFP_KERNEL);
> +	if (!l2ist)
> +		return -ENOMEM;
> +
> +	l1ist[index] = cpu_to_le64(virt_to_phys(l2ist) & GICV5_ISTL1E_L2_ADDR_MASK);
> +
> +	if (irs_data->flags & IRS_FLAGS_NON_COHERENT) {
> +		dcache_clean_inval_poc((unsigned long)l2ist,
> +				       (unsigned long)l2ist + l2size);
> +		dcache_clean_poc((unsigned long)(l1ist + index),
> +				 (unsigned long)(l1ist + index) + sizeof(*l1ist));
> +	} else {
> +		dsb(ishst);
> +	}
> +
> +	l2istr = FIELD_PREP(GICV5_IRS_MAP_L2_ISTR_ID, lpi);
> +	irs_writel_relaxed(irs_data, l2istr, GICV5_IRS_MAP_L2_ISTR);
> +
> +	ret = gicv5_irs_ist_synchronise(irs_data);
> +	if (ret) {
> +		l1ist[index] = 0;
> +		kfree(l2ist);
> +		return ret;
> +	}
> +
> +	/*
> +	 * Make sure we invalidate the cache line pulled before the IRS
> +	 * had a chance to update the L1 entry and mark it valid.
> +	 */
> +	if (irs_data->flags & IRS_FLAGS_NON_COHERENT) {
> +		/*
> +		 * gicv5_irs_ist_synchronise() includes memory
> +		 * barriers (MMIO accessors) required to guarantee that the
> +		 * following dcache invalidation is not executed before the
> +		 * IST mapping operation has completed.
> +		 */
> +		dcache_inval_poc((unsigned long)(l1ist + index),
> +				 (unsigned long)(l1ist + index) + sizeof(*l1ist));
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * Try to match the L2 IST size to the pagesize, and if this is not possible
> + * pick the smallest supported L2 size in order to minimise the requirement for
> + * physically contiguous blocks of memory as page-sized allocations are
> + * guaranteed to be physically contiguous, and are by definition the easiest to
> + * find.
> + *
> + * Fall back to the smallest supported size (in the event that the pagesize
> + * itself is not supported) again serves to make it easier to find physically
> + * contiguous blocks of memory.
> + */
> +static unsigned int gicv5_irs_l2_sz(u32 idr2)
> +{
> +	switch (PAGE_SIZE) {
> +	case SZ_64K:
> +		if (GICV5_IRS_IST_L2SZ_SUPPORT_64KB(idr2))
> +			return GICV5_IRS_IST_CFGR_L2SZ_64K;
> +		fallthrough;
> +	case SZ_4K:
> +		if (GICV5_IRS_IST_L2SZ_SUPPORT_4KB(idr2))
> +			return GICV5_IRS_IST_CFGR_L2SZ_4K;
> +		fallthrough;
> +	case SZ_16K:
> +		if (GICV5_IRS_IST_L2SZ_SUPPORT_16KB(idr2))
> +			return GICV5_IRS_IST_CFGR_L2SZ_16K;
> +		break;
> +	}
> +
> +	if (GICV5_IRS_IST_L2SZ_SUPPORT_4KB(idr2))
> +		return GICV5_IRS_IST_CFGR_L2SZ_4K;
> +
> +	return GICV5_IRS_IST_CFGR_L2SZ_64K;
> +}
> +
> +static int __init gicv5_irs_init_ist(struct gicv5_irs_chip_data *irs_data)
> +{
> +	u32 lpi_id_bits, idr2_id_bits, idr2_min_lpi_id_bits, l2_iste_sz, l2sz;
> +	u32 l2_iste_sz_split, idr2;
> +	bool two_levels, istmd;
> +	u64 baser;
> +	int ret;
> +
> +	baser = irs_readq_relaxed(irs_data, GICV5_IRS_IST_BASER);
> +	if (FIELD_GET(GICV5_IRS_IST_BASER_VALID, baser)) {
> +		pr_err("IST is marked as valid already; cannot allocate\n");
> +		return -EPERM;
> +	}
> +
> +	idr2 = irs_readl_relaxed(irs_data, GICV5_IRS_IDR2);
> +	two_levels = !!FIELD_GET(GICV5_IRS_IDR2_IST_LEVELS, idr2);
> +
> +	idr2_id_bits = FIELD_GET(GICV5_IRS_IDR2_ID_BITS, idr2);
> +	idr2_min_lpi_id_bits = FIELD_GET(GICV5_IRS_IDR2_MIN_LPI_ID_BITS, idr2);
> +
> +	/*
> +	 * For two level tables we are always supporting the maximum allowed
> +	 * number of IDs.
> +	 *
> +	 * For 1-level tables, we should support a number of bits that
> +	 * is >= min_lpi_id_bits but cap it to LPI_ID_BITS_LINEAR lest
> +	 * the level 1-table gets too large and its memory allocation
> +	 * may fail.
> +	 */
> +	if (two_levels) {
> +		lpi_id_bits = idr2_id_bits;
> +	} else {
> +		lpi_id_bits = max(LPI_ID_BITS_LINEAR, idr2_min_lpi_id_bits);
> +		lpi_id_bits = min(lpi_id_bits, idr2_id_bits);
> +	}
> +
> +	/*
> +	 * Cap the ID bits according to the CPUIF supported ID bits
> +	 */
> +	lpi_id_bits = min(lpi_id_bits, gicv5_global_data.cpuif_id_bits);
> +
> +	if (two_levels)
> +		l2sz = gicv5_irs_l2_sz(idr2);
> +
> +	istmd = !!FIELD_GET(GICV5_IRS_IDR2_ISTMD, idr2);
> +
> +	l2_iste_sz = GICV5_IRS_IST_CFGR_ISTSZ_4;
> +
> +	if (istmd) {
> +		l2_iste_sz_split = FIELD_GET(GICV5_IRS_IDR2_ISTMD_SZ, idr2);
> +
> +		if (lpi_id_bits < l2_iste_sz_split)
> +			l2_iste_sz = GICV5_IRS_IST_CFGR_ISTSZ_8;
> +		else
> +			l2_iste_sz = GICV5_IRS_IST_CFGR_ISTSZ_16;
> +	}
> +
> +	/*
> +	 * Follow GICv5 specification recommendation to opt in for two
> +	 * level tables (ref: 10.2.1.14 IRS_IST_CFGR).
> +	 */
> +	if (two_levels && (lpi_id_bits > ((10 - l2_iste_sz) + (2 * l2sz)))) {
> +		ret = gicv5_irs_init_ist_two_level(irs_data, lpi_id_bits,
> +						   l2_iste_sz, l2sz);
> +	} else {
> +		ret = gicv5_irs_init_ist_linear(irs_data, lpi_id_bits,
> +						l2_iste_sz);
> +	}
> +	if (ret)
> +		return ret;
> +
> +	gicv5_init_lpis(BIT(lpi_id_bits));
> +
> +	return 0;
> +}
> +
>  struct iaffid_entry {
>  	u16	iaffid;
>  	bool	valid;
> @@ -362,6 +695,13 @@ static int __init gicv5_irs_init(struct device_node *node)
>  		goto out_iomem;
>  	}
>  
> +	idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR2);
> +	if (WARN(!FIELD_GET(GICV5_IRS_IDR2_LPI, idr),
> +		 "LPI support not available - no IPIs, can't proceed\n")) {
> +		ret = -ENODEV;
> +		goto out_iomem;
> +	}
> +
>  	idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR7);
>  	irs_data->spi_min = FIELD_GET(GICV5_IRS_IDR7_SPI_BASE, idr);
>  
> @@ -391,6 +731,8 @@ static int __init gicv5_irs_init(struct device_node *node)
>  		spi_count = FIELD_GET(GICV5_IRS_IDR5_SPI_RANGE, idr);
>  		gicv5_global_data.global_spi_count = spi_count;
>  
> +		gicv5_init_lpi_domain();
> +
>  		pr_debug("Detected %u SPIs globally\n", spi_count);
>  	}
>  
> @@ -409,6 +751,9 @@ void __init gicv5_irs_remove(void)
>  {
>  	struct gicv5_irs_chip_data *irs_data, *tmp_data;
>  
> +	gicv5_free_lpi_domain();
> +	gicv5_deinit_lpis();
> +
>  	list_for_each_entry_safe(irs_data, tmp_data, &irs_nodes, entry) {
>  		iounmap(irs_data->irs_base);
>  		list_del(&irs_data->entry);
> @@ -416,6 +761,25 @@ void __init gicv5_irs_remove(void)
>  	}
>  }
>  
> +int __init gicv5_irs_enable(void)
> +{
> +	struct gicv5_irs_chip_data *irs_data;
> +	int ret;
> +
> +	irs_data = list_first_entry_or_null(&irs_nodes,
> +					    struct gicv5_irs_chip_data, entry);
> +	if (!irs_data)
> +		return -ENODEV;
> +
> +	ret = gicv5_irs_init_ist(irs_data);
> +	if (ret) {
> +		pr_err("Failed to init IST\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
>  int __init gicv5_irs_of_probe(struct device_node *parent)
>  {
>  	struct device_node *np;
> diff --git a/drivers/irqchip/irq-gic-v5.c b/drivers/irqchip/irq-gic-v5.c
> index 9c55ddcfa0df..84ed13c4f2b1 100644
> --- a/drivers/irqchip/irq-gic-v5.c
> +++ b/drivers/irqchip/irq-gic-v5.c
> @@ -5,7 +5,9 @@
>  
>  #define pr_fmt(fmt)	"GICv5: " fmt
>  
> +#include <linux/idr.h>
>  #include <linux/irqdomain.h>
> +#include <linux/slab.h>
>  #include <linux/wordpart.h>
>  
>  #include <linux/irqchip.h>
> @@ -28,6 +30,42 @@ static bool gicv5_cpuif_has_gcie(void)
>  
>  struct gicv5_chip_data gicv5_global_data __read_mostly;
>  
> +static DEFINE_IDA(lpi_ida);
> +static u32 num_lpis __ro_after_init;
> +
> +void __init gicv5_init_lpis(u32 lpis)
> +{
> +	num_lpis = lpis;
> +}
> +
> +void __init gicv5_deinit_lpis(void)
> +{
> +	num_lpis = 0;
> +}
> +
> +static int alloc_lpi(void)
> +{
> +	if (!num_lpis)
> +		return -ENOSPC;
> +
> +	return ida_alloc_max(&lpi_ida, num_lpis - 1, GFP_KERNEL);
> +}
> +
> +static void release_lpi(u32 lpi)
> +{
> +	ida_free(&lpi_ida, lpi);
> +}
> +
> +int gicv5_alloc_lpi(void)
> +{
> +	return alloc_lpi();
> +}
> +
> +void gicv5_free_lpi(u32 lpi)
> +{
> +	release_lpi(lpi);
> +}
> +
>  static void gicv5_ppi_priority_init(void)
>  {
>  	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRI_MI), SYS_ICC_PPI_PRIORITYR0_EL1);
> @@ -60,7 +98,7 @@ static void gicv5_hwirq_init(irq_hw_number_t hwirq, u8 priority, u8 hwirq_type)
>  	u16 iaffid;
>  	int ret;
>  
> -	if (hwirq_type == GICV5_HWIRQ_TYPE_SPI) {
> +	if (hwirq_type == GICV5_HWIRQ_TYPE_LPI || hwirq_type == GICV5_HWIRQ_TYPE_SPI) {
>  		cdpri = FIELD_PREP(GICV5_GIC_CDPRI_PRIORITY_MASK, priority)	|
>  			FIELD_PREP(GICV5_GIC_CDPRI_TYPE_MASK, hwirq_type)	|
>  			FIELD_PREP(GICV5_GIC_CDPRI_ID_MASK, hwirq);
> @@ -122,6 +160,11 @@ static void gicv5_spi_irq_mask(struct irq_data *d)
>  	gicv5_iri_irq_mask(d, GICV5_HWIRQ_TYPE_SPI);
>  }
>  
> +static void gicv5_lpi_irq_mask(struct irq_data *d)
> +{
> +	gicv5_iri_irq_mask(d, GICV5_HWIRQ_TYPE_LPI);
> +}
> +
>  static void gicv5_ppi_irq_unmask(struct irq_data *d)
>  {
>  	u64 hwirq_id_bit = BIT_ULL(d->hwirq % 64);
> @@ -149,7 +192,7 @@ static void gicv5_iri_irq_unmask(struct irq_data *d, u8 hwirq_type)
>  	/*
>  	 * Rule R_XCLJC states that the effects of a GIC system instruction
>  	 * complete in finite time and that's the only requirement when
> -	 * unmasking an SPI IRQ.
> +	 * unmasking an SPI/LPI IRQ.
>  	 */
>  	gic_insn(cden, CDEN);
>  }
> @@ -159,6 +202,11 @@ static void gicv5_spi_irq_unmask(struct irq_data *d)
>  	gicv5_iri_irq_unmask(d, GICV5_HWIRQ_TYPE_SPI);
>  }
>  
> +static void gicv5_lpi_irq_unmask(struct irq_data *d)
> +{
> +	gicv5_iri_irq_unmask(d, GICV5_HWIRQ_TYPE_LPI);
> +}
> +
>  static void gicv5_hwirq_eoi(u32 hwirq_id, u8 hwirq_type)
>  {
>  	u64 cddi;
> @@ -181,6 +229,11 @@ static void gicv5_spi_irq_eoi(struct irq_data *d)
>  	gicv5_hwirq_eoi(d->hwirq, GICV5_HWIRQ_TYPE_SPI);
>  }
>  
> +static void gicv5_lpi_irq_eoi(struct irq_data *d)
> +{
> +	gicv5_hwirq_eoi(d->hwirq, GICV5_HWIRQ_TYPE_LPI);
> +}
> +
>  static int gicv5_iri_irq_set_affinity(struct irq_data *d,
>  				      const struct cpumask *mask_val,
>  				      bool force, u8 hwirq_type)
> @@ -216,6 +269,14 @@ static int gicv5_spi_irq_set_affinity(struct irq_data *d,
>  					  GICV5_HWIRQ_TYPE_SPI);
>  }
>  
> +static int gicv5_lpi_irq_set_affinity(struct irq_data *d,
> +				      const struct cpumask *mask_val,
> +				      bool force)
> +{
> +	return gicv5_iri_irq_set_affinity(d, mask_val, force,
> +					  GICV5_HWIRQ_TYPE_LPI);
> +}
> +
>  enum ppi_reg {
>  	PPI_PENDING,
>  	PPI_ACTIVE,
> @@ -336,6 +397,14 @@ static int gicv5_spi_irq_get_irqchip_state(struct irq_data *d,
>  					       GICV5_HWIRQ_TYPE_SPI);
>  }
>  
> +static int gicv5_lpi_irq_get_irqchip_state(struct irq_data *d,
> +					   enum irqchip_irq_state which,
> +					   bool *state)
> +{
> +	return gicv5_iri_irq_get_irqchip_state(d, which, state,
> +					       GICV5_HWIRQ_TYPE_LPI);
> +}
> +
>  static int gicv5_ppi_irq_set_irqchip_state(struct irq_data *d,
>  					   enum irqchip_irq_state which,
>  					   bool state)
> @@ -370,6 +439,11 @@ static void gicv5_spi_irq_write_pending_state(struct irq_data *d, bool state)
>  	gicv5_iri_irq_write_pending_state(d, state, GICV5_HWIRQ_TYPE_SPI);
>  }
>  
> +static void gicv5_lpi_irq_write_pending_state(struct irq_data *d, bool state)
> +{
> +	gicv5_iri_irq_write_pending_state(d, state, GICV5_HWIRQ_TYPE_LPI);
> +}
> +
>  static int gicv5_spi_irq_set_irqchip_state(struct irq_data *d,
>  					   enum irqchip_irq_state which,
>  					   bool state)
> @@ -386,12 +460,41 @@ static int gicv5_spi_irq_set_irqchip_state(struct irq_data *d,
>  	return 0;
>  }
>  
> +static int gicv5_lpi_irq_set_irqchip_state(struct irq_data *d,
> +					   enum irqchip_irq_state which,
> +					   bool state)
> +{
> +	switch (which) {
> +	case IRQCHIP_STATE_PENDING:
> +		gicv5_lpi_irq_write_pending_state(d, state);
> +		break;
> +
> +	default:
> +		pr_debug("Unexpected irqchip_irq_state\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
>  static int gicv5_spi_irq_retrigger(struct irq_data *data)
>  {
>  	return !gicv5_spi_irq_set_irqchip_state(data, IRQCHIP_STATE_PENDING,
>  						true);
>  }
>  
> +static int gicv5_lpi_irq_retrigger(struct irq_data *data)
> +{
> +	return !gicv5_lpi_irq_set_irqchip_state(data, IRQCHIP_STATE_PENDING,
> +						true);
> +}
> +
> +static void gicv5_ipi_send_single(struct irq_data *d, unsigned int cpu)
> +{
> +	/* Mark the LPI pending */
> +	irq_chip_retrigger_hierarchy(d);
> +}
> +
>  static bool gicv5_ppi_irq_is_level(irq_hw_number_t hwirq)
>  {
>  	u64 bit = BIT_ULL(hwirq % 64);
> @@ -425,6 +528,32 @@ static const struct irq_chip gicv5_spi_irq_chip = {
>  				  IRQCHIP_MASK_ON_SUSPEND,
>  };
>  
> +static const struct irq_chip gicv5_lpi_irq_chip = {
> +	.name			= "GICv5-LPI",
> +	.irq_mask		= gicv5_lpi_irq_mask,
> +	.irq_unmask		= gicv5_lpi_irq_unmask,
> +	.irq_eoi		= gicv5_lpi_irq_eoi,
> +	.irq_set_affinity	= gicv5_lpi_irq_set_affinity,
> +	.irq_retrigger		= gicv5_lpi_irq_retrigger,
> +	.irq_get_irqchip_state	= gicv5_lpi_irq_get_irqchip_state,
> +	.irq_set_irqchip_state	= gicv5_lpi_irq_set_irqchip_state,
> +	.flags			= IRQCHIP_SKIP_SET_WAKE	  |
> +				  IRQCHIP_MASK_ON_SUSPEND,
> +};
> +
> +static const struct irq_chip gicv5_ipi_irq_chip = {
> +	.name			= "GICv5-IPI",
> +	.irq_mask		= irq_chip_mask_parent,
> +	.irq_unmask		= irq_chip_unmask_parent,
> +	.irq_eoi		= irq_chip_eoi_parent,
> +	.irq_set_affinity	= irq_chip_set_affinity_parent,
> +	.irq_get_irqchip_state	= irq_chip_get_parent_state,
> +	.irq_set_irqchip_state	= irq_chip_set_parent_state,
> +	.ipi_send_single	= gicv5_ipi_send_single,
> +	.flags			= IRQCHIP_SKIP_SET_WAKE	  |
> +				  IRQCHIP_MASK_ON_SUSPEND,
> +};
> +
>  static __always_inline int gicv5_irq_domain_translate(struct irq_domain *d,
>  						      struct irq_fwspec *fwspec,
>  						      irq_hw_number_t *hwirq,
> @@ -585,6 +714,130 @@ static const struct irq_domain_ops gicv5_irq_spi_domain_ops = {
>  	.free		= gicv5_irq_domain_free,
>  	.select		= gicv5_irq_spi_domain_select
>  };
> +
> +static void gicv5_lpi_config_reset(struct irq_data *d)
> +{
> +	u64 cdhm;
> +
> +	/*
> +	 * Reset LPIs handling mode to edge by default and clear pending
> +	 * state to make sure we start the LPI with a clean state from
> +	 * previous incarnations.
> +	 */
> +	cdhm = FIELD_PREP(GICV5_GIC_CDHM_HM_MASK, 0)				|
> +	       FIELD_PREP(GICV5_GIC_CDHM_TYPE_MASK, GICV5_HWIRQ_TYPE_LPI)	|
> +	       FIELD_PREP(GICV5_GIC_CDHM_ID_MASK, d->hwirq);
> +	gic_insn(cdhm, CDHM);
> +
> +	gicv5_lpi_irq_write_pending_state(d, false);
> +}
> +
> +static int gicv5_irq_lpi_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +				      unsigned int nr_irqs, void *arg)
> +{
> +	irq_hw_number_t hwirq;
> +	struct irq_data *irqd;
> +	u32 *lpi = arg;
> +	int ret;
> +
> +	if (WARN_ON_ONCE(nr_irqs != 1))
> +		return -EINVAL;
> +
> +	hwirq = *lpi;
> +
> +	irqd = irq_domain_get_irq_data(domain, virq);
> +
> +	irq_domain_set_info(domain, virq, hwirq, &gicv5_lpi_irq_chip, NULL,
> +			    handle_fasteoi_irq, NULL, NULL);
> +	irqd_set_single_target(irqd);
> +
> +	ret = gicv5_irs_iste_alloc(hwirq);
> +	if (ret < 0)
> +		return ret;
> +
> +	gicv5_hwirq_init(hwirq, GICV5_IRQ_PRI_MI, GICV5_HWIRQ_TYPE_LPI);
> +	gicv5_lpi_config_reset(irqd);
> +
> +	return 0;
> +}
> +
> +static const struct irq_domain_ops gicv5_irq_lpi_domain_ops = {
> +	.alloc	= gicv5_irq_lpi_domain_alloc,
> +	.free	= gicv5_irq_domain_free,
> +};
> +
> +void __init gicv5_init_lpi_domain(void)
> +{
> +	struct irq_domain *d;
> +
> +	d = irq_domain_create_tree(NULL, &gicv5_irq_lpi_domain_ops, NULL);
> +	gicv5_global_data.lpi_domain = d;
> +}
> +
> +void __init gicv5_free_lpi_domain(void)
> +{
> +	irq_domain_remove(gicv5_global_data.lpi_domain);
> +	gicv5_global_data.lpi_domain = NULL;
> +}
> +
> +static int gicv5_irq_ipi_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +				      unsigned int nr_irqs, void *arg)
> +{
> +	struct irq_data *irqd;
> +	int ret, i;
> +	u32 lpi;
> +
> +	for (i = 0; i < nr_irqs; i++) {
> +		ret = gicv5_alloc_lpi();
> +		if (ret < 0)
> +			return ret;
> +
> +		lpi = ret;
> +
> +		ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, &lpi);
> +		if (ret) {
> +			gicv5_free_lpi(lpi);
> +			return ret;
> +		}
> +
> +		irqd = irq_domain_get_irq_data(domain, virq + i);
> +
> +		irq_domain_set_hwirq_and_chip(domain, virq + i, i,
> +				&gicv5_ipi_irq_chip, NULL);
> +
> +		irqd_set_single_target(irqd);
> +
> +		irq_set_handler(virq + i, handle_percpu_irq);
> +	}
> +
> +	return 0;
> +}
> +
> +static void gicv5_irq_ipi_domain_free(struct irq_domain *domain, unsigned int virq,
> +				      unsigned int nr_irqs)
> +{
> +	struct irq_data *d;
> +	unsigned int i;
> +
> +	for (i = 0; i < nr_irqs; i++) {
> +		d = irq_domain_get_irq_data(domain, virq + i);
> +
> +		if (!d)
> +			return;
> +
> +		gicv5_free_lpi(d->parent_data->hwirq);
> +
> +		irq_set_handler(virq + i, NULL);
> +		irq_domain_reset_irq_data(d);
> +		irq_domain_free_irqs_parent(domain, virq + i, 1);
> +	}
> +}
> +
> +static const struct irq_domain_ops gicv5_irq_ipi_domain_ops = {
> +	.alloc	= gicv5_irq_ipi_domain_alloc,
> +	.free	= gicv5_irq_ipi_domain_free,
> +};
> +
>  static void handle_irq_per_domain(u32 hwirq)
>  {
>  	u8 hwirq_type = FIELD_GET(GICV5_HWIRQ_TYPE, hwirq);
> @@ -598,6 +851,9 @@ static void handle_irq_per_domain(u32 hwirq)
>  	case GICV5_HWIRQ_TYPE_SPI:
>  		domain = gicv5_global_data.spi_domain;
>  		break;
> +	case GICV5_HWIRQ_TYPE_LPI:
> +		domain = gicv5_global_data.lpi_domain;
> +		break;
>  	default:
>  		pr_err_once("Unknown IRQ type, bail out\n");
>  		return;
> @@ -679,9 +935,12 @@ static void __init gicv5_free_domains(void)
>  		irq_domain_remove(gicv5_global_data.ppi_domain);
>  	if (gicv5_global_data.spi_domain)
>  		irq_domain_remove(gicv5_global_data.spi_domain);
> +	if (gicv5_global_data.ipi_domain)
> +		irq_domain_remove(gicv5_global_data.ipi_domain);
>  
>  	gicv5_global_data.ppi_domain = NULL;
>  	gicv5_global_data.spi_domain = NULL;
> +	gicv5_global_data.ipi_domain = NULL;
>  }
>  
>  static int __init gicv5_init_domains(struct fwnode_handle *handle)
> @@ -709,6 +968,19 @@ static int __init gicv5_init_domains(struct fwnode_handle *handle)
>  		irq_domain_update_bus_token(d, DOMAIN_BUS_WIRED);
>  	}
>  
> +	if (!WARN(!gicv5_global_data.lpi_domain,
> +		  "LPI domain uninitialized, can't set up IPIs")) {
> +		d = irq_domain_create_hierarchy(gicv5_global_data.lpi_domain,
> +						0, GICV5_IPIS_PER_CPU * nr_cpu_ids,
> +						NULL, &gicv5_irq_ipi_domain_ops,
> +						NULL);
> +
> +		if (!d) {
> +			gicv5_free_domains();
> +			return -ENOMEM;
> +		}
> +		gicv5_global_data.ipi_domain = d;
> +	}
>  	gicv5_global_data.fwnode = handle;
>  
>  	return 0;
> @@ -732,6 +1004,24 @@ static void gicv5_set_cpuif_pribits(void)
>  	}
>  }
>  
> +static void gicv5_set_cpuif_idbits(void)
> +{
> +	u32 icc_idr0 = read_sysreg_s(SYS_ICC_IDR0_EL1);
> +
> +	switch (FIELD_GET(ICC_IDR0_EL1_ID_BITS, icc_idr0)) {
> +	case ICC_IDR0_EL1_ID_BITS_16BITS:
> +		gicv5_global_data.cpuif_id_bits = 16;
> +		break;
> +	case ICC_IDR0_EL1_ID_BITS_24BITS:
> +		gicv5_global_data.cpuif_id_bits = 24;
> +		break;
> +	default:
> +		pr_err("Unexpected ICC_IDR0_EL1_ID_BITS value, default to 16");
> +		gicv5_global_data.cpuif_id_bits = 16;
> +		break;
> +	}
> +}
> +
>  static int __init gicv5_of_init(struct device_node *node, struct device_node *parent)
>  {
>  	int ret = gicv5_irs_of_probe(node);
> @@ -743,6 +1033,7 @@ static int __init gicv5_of_init(struct device_node *node, struct device_node *pa
>  		goto out_irs;
>  
>  	gicv5_set_cpuif_pribits();
> +	gicv5_set_cpuif_idbits();
>  
>  	pri_bits = min_not_zero(gicv5_global_data.cpuif_pri_bits,
>  				gicv5_global_data.irs_pri_bits);
> @@ -755,6 +1046,10 @@ static int __init gicv5_of_init(struct device_node *node, struct device_node *pa
>  	if (ret)
>  		goto out_int;
>  
> +	ret = gicv5_irs_enable();
> +	if (ret)
> +		goto out_int;
> +
>  	return 0;
>  
>  out_int:
> diff --git a/include/linux/irqchip/arm-gic-v5.h b/include/linux/irqchip/arm-gic-v5.h
> index 1064a69ab33f..680eed794a35 100644
> --- a/include/linux/irqchip/arm-gic-v5.h
> +++ b/include/linux/irqchip/arm-gic-v5.h
> @@ -7,8 +7,12 @@
>  
>  #include <linux/iopoll.h>
>  
> +#include <asm/cacheflush.h>
> +#include <asm/smp.h>
>  #include <asm/sysreg.h>
>  
> +#define GICV5_IPIS_PER_CPU		MAX_IPI
> +
>  /*
>   * INTID handling
>   */
> @@ -17,6 +21,7 @@
>  #define GICV5_HWIRQ_INTID		GENMASK_ULL(31, 0)
>  
>  #define GICV5_HWIRQ_TYPE_PPI		UL(0x1)
> +#define GICV5_HWIRQ_TYPE_LPI		UL(0x2)
>  #define GICV5_HWIRQ_TYPE_SPI		UL(0x3)
>  
>  /*
> @@ -36,7 +41,7 @@
>  #define GICV5_INNER_SHARE		0b11
>  
>  /*
> - * IRS registers
> + * IRS registers and tables structures
>   */
>  #define GICV5_IRS_IDR1			0x0004
>  #define GICV5_IRS_IDR2			0x0008
> @@ -51,6 +56,10 @@
>  #define GICV5_IRS_PE_SELR		0x0140
>  #define GICV5_IRS_PE_STATUSR		0x0144
>  #define GICV5_IRS_PE_CR0		0x0148
> +#define GICV5_IRS_IST_BASER		0x0180
> +#define GICV5_IRS_IST_CFGR		0x0190
> +#define GICV5_IRS_IST_STATUSR		0x0194
> +#define GICV5_IRS_MAP_L2_ISTR		0x01c0
>  
>  #define GICV5_IRS_IDR1_PRIORITY_BITS	GENMASK(22, 20)
>  #define GICV5_IRS_IDR1_IAFFID_BITS	GENMASK(19, 16)
> @@ -72,6 +81,11 @@
>  #define GICV5_IRS_IDR5_SPI_RANGE	GENMASK(24, 0)
>  #define GICV5_IRS_IDR6_SPI_IRS_RANGE	GENMASK(24, 0)
>  #define GICV5_IRS_IDR7_SPI_BASE		GENMASK(23, 0)
> +
> +#define GICV5_IRS_IST_L2SZ_SUPPORT_4KB(r)	FIELD_GET(BIT(11), (r))
> +#define GICV5_IRS_IST_L2SZ_SUPPORT_16KB(r)	FIELD_GET(BIT(12), (r))
> +#define GICV5_IRS_IST_L2SZ_SUPPORT_64KB(r)	FIELD_GET(BIT(13), (r))
> +
>  #define GICV5_IRS_CR0_IDLE		BIT(1)
>  #define GICV5_IRS_CR0_IRSEN		BIT(0)
>  
> @@ -103,6 +117,33 @@
>  
>  #define GICV5_IRS_PE_CR0_DPS		BIT(0)
>  
> +#define GICV5_IRS_IST_STATUSR_IDLE	BIT(0)
> +
> +#define GICV5_IRS_IST_CFGR_STRUCTURE	BIT(16)
> +#define GICV5_IRS_IST_CFGR_ISTSZ	GENMASK(8, 7)
> +#define GICV5_IRS_IST_CFGR_L2SZ		GENMASK(6, 5)
> +#define GICV5_IRS_IST_CFGR_LPI_ID_BITS	GENMASK(4, 0)
> +
> +#define GICV5_IRS_IST_CFGR_STRUCTURE_LINEAR	0b0
> +#define GICV5_IRS_IST_CFGR_STRUCTURE_TWO_LEVEL	0b1
> +
> +#define GICV5_IRS_IST_CFGR_ISTSZ_4	0b00
> +#define GICV5_IRS_IST_CFGR_ISTSZ_8	0b01
> +#define GICV5_IRS_IST_CFGR_ISTSZ_16	0b10
> +
> +#define GICV5_IRS_IST_CFGR_L2SZ_4K	0b00
> +#define GICV5_IRS_IST_CFGR_L2SZ_16K	0b01
> +#define GICV5_IRS_IST_CFGR_L2SZ_64K	0b10
> +
> +#define GICV5_IRS_IST_BASER_ADDR_MASK	GENMASK_ULL(55, 6)
> +#define GICV5_IRS_IST_BASER_VALID	BIT_ULL(0)
> +
> +#define GICV5_IRS_MAP_L2_ISTR_ID	GENMASK(23, 0)
> +
> +#define GICV5_ISTL1E_VALID		BIT_ULL(0)
> +
> +#define GICV5_ISTL1E_L2_ADDR_MASK	GENMASK_ULL(55, 12)
> +
>  /*
>   * Global Data structures and functions
>   */
> @@ -110,9 +151,18 @@ struct gicv5_chip_data {
>  	struct fwnode_handle	*fwnode;
>  	struct irq_domain	*ppi_domain;
>  	struct irq_domain	*spi_domain;
> +	struct irq_domain	*lpi_domain;
> +	struct irq_domain	*ipi_domain;
>  	u32			global_spi_count;
>  	u8			cpuif_pri_bits;
> +	u8			cpuif_id_bits;
>  	u8			irs_pri_bits;
> +	struct {
> +		__le64 *l1ist_addr;
> +		u32 l2_size;
> +		u8 l2_bits;
> +		bool l2;
> +	} ist;
>  };
>  
>  extern struct gicv5_chip_data gicv5_global_data __read_mostly;
> @@ -150,10 +200,21 @@ static inline int gicv5_wait_for_op_s_atomic(void __iomem *addr, u32 offset,
>  #define gicv5_wait_for_op_atomic(base, reg, mask, val) \
>  	gicv5_wait_for_op_s_atomic(base, reg, #reg, mask, val)
>  
> +void __init gicv5_init_lpi_domain(void);
> +void __init gicv5_free_lpi_domain(void);
> +
>  int gicv5_irs_of_probe(struct device_node *parent);
>  void gicv5_irs_remove(void);
> +int gicv5_irs_enable(void);
>  int gicv5_irs_register_cpu(int cpuid);
>  int gicv5_irs_cpu_to_iaffid(int cpu_id, u16 *iaffid);
>  struct gicv5_irs_chip_data *gicv5_irs_lookup_by_spi_id(u32 spi_id);
>  int gicv5_spi_irq_set_type(struct irq_data *d, unsigned int type);
> +int gicv5_irs_iste_alloc(u32 lpi);
> +
> +void gicv5_init_lpis(u32 max);
> +void gicv5_deinit_lpis(void);
> +
> +int gicv5_alloc_lpi(void);
> +void gicv5_free_lpi(u32 lpi);
>  #endif
> 

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ