linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/24] Arm GICv5: Host driver implementation
@ 2025-04-08 10:49 Lorenzo Pieralisi
  2025-04-08 10:50 ` [PATCH 01/24] Documentation: devicetree: bindings: Add GICv5 DT bindings Lorenzo Pieralisi
                   ` (23 more replies)
  0 siblings, 24 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:49 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

Implement the irqchip kernel driver for the Arm GICv5 architecture,
as described in the GICv5 beta0 specification, available at:

https://developer.arm.com/documentation/aes0070

The GICv5 architecture is composed of multiple components:

- one or more IRS (Interrupt Routing Service)
- zero or more ITS (Interrupt Translation Service)
- zero or more IWB (Interrupt Wire Bridge)

The GICv5 host kernel driver is split into units corresponding
to GICv5 components.

The GICv5 architecture defines the following interrupt types:

- PPI (PE-Private Peripheral Interrupt)
- SPI (Shared Peripheral Interrupt)
- LPI (Logical Peripheral Interrupt)

This series adds sysreg entries required to automatically generate
GICv5 registers handling code, one patch per-register.

The GICv5 driver is split into patches matching *logical* entities to
(hopefully) simplify the review. Regardless, PPI, IRS/SPI, LPI/IPI and
SMP booting support (patches [18-21]) should be eventually merged as a
single patch given that only by merging the four patches together the
driver code can be considered fully functional.

Logical entities:

- PPI
- IRS/SPI
- LPI/IPI
- ITS
- IWB

The salient points of the driver are summarized below.

=============
1. Testing
=============

Patchset tested with an architecturally compliant FVP model with
the following setup:

- 1 IRS
- 1 and 2 ITSes
- 1 and 2 IWBs

configured with different parameters that vary the IRS(IST) and
ITS(DT/ITT) table levels and INTID/DEVICEID/EVENTID bits.

A Trusted-Firmware (TF-A) prototype was used for device tree
bindings and component initializations.

================
2. Driver design
================

=====================
2.1 GICv5 DT bindings
=====================

The DT bindings attempt to map directly to the GICv5 component
hierarchy, with a top level node corresponding to the GICv5 "system",
having IRS child nodes, that have in turn ITS child nodes.

The IWB is defined as a GICv5 "system" child since its relationship
with the ITS is explicit through the msi-parent property required
to define the IWB deviceID.

===================
2.2 GICv5 top level
===================

The top-level GICv5 irqchip driver implements separate IRQ
domains - one for each interrupt type, PPI (PE-Private Peripheral
Interrupt), SPI (Shared Peripheral Interrupt) and LPI (Logical
Peripheral Interrupt).

The top-level exception handler routes the IRQ to the relevant IRQ
domain for handling according to the interrupt type detected when the
IRQ is acknowledged.

All IRQs are set to the same priority value.

The driver assumes that the GICv5 components implement enough
physical address bits to address the full system RAM, as required
by the architecture; it does not check whether the physical address
ranges of memory allocated for IRS/ITS tables are within the GICv5
physical address range.

Components are probed by relying on the early DT irqchip probing
scheme. The probing is carried out hierarchically, starting from
the top level.

The IWB driver is not implemented using the wired-to-MSI interface
(i.e. with bus_token == DOMAIN_BUS_WIRED_TO_MSI mechanism), for two
specific reasons:

- An IWB is tightly coupled with a GICv5 ITS and its eventIDs
  are fixed and cannot be allocated by the GICv5 ITS as it does
  for other devices. An IWB is plugged directly into the ITS
  driver for this specific reason.
- The DOMAIN_BUS_WIRED_TO_MSI interface requires the IWBs to be
  platform devices - fiddling with platform device creation and
  DT nodes is doable but a bit hacky.

Having said that, an alternative IWB driver with DOMAIN_BUS_WIRED_TO_MSI
bus token was implemented and can be shared for subsequent patchsets
versions depending on feedback, ref: 2.5 GICv5 IWB section below.

=============
2.3 GICv5 IRS
=============

The GICv5 IRS driver probes and manages SPI interrupts by detecting their
presence and by providing the top-level driver the information required
to set up the SPI interrupt domain.

The GICv5 IRS driver also parses from firmware Interrupt AFFinity ID
(IAFFID) IDs identifying cores and sets up IRS IRQ routing.

The GICv5 IRS driver allocates memory to handle the IRS tables.

The IRS LPI interrupts state is kept in an Interrupt State Table (IST)
and it is managed through CPU instructions.

The IRS driver allocates the IST table that, depending on available HW
features can be either 1- or 2-level.

If the IST is 2-level, memory for the level-2 table entries
is allocated on demand (ie when LPIs are requested), using an IRS
mechanism to make level-1 entry valid on demand after the IST
has already been enabled.

Chunks of memory allocated for IST entries can be smaller or larger than
PAGE_SIZE and are required to be physically contiguous within an IST level
(i.e. a linear IST is a single memory block, a 2-level IST is made up of a
block of memory for the L1 table, whose entries point at different L2 tables
that are in turn allocated as memory chunks).

LPI INTIDs are allocated in software out of an ID pool, that is managed
with a maple tree. Maple tree entries are not used (the maple tree is
used to keep track of free LPI ranges - the value stored is a valid
pointer to mark the ranges as allocated but it is not consumed by the
driver). LPI INTIDs are allocated on demand, mostly one by one.
On LPI allocation the driver tries to merge adjacent maple tree slots to
prevent wasting slots.

Maple tree usage for this purpose is an RFC at this stage.

IPIs are implemented using LPIs and a hierarchical domain is created
specifically for IPIs using the LPI domain as a parent.

arm64 IPI management core code is augmented with a new API to handle
IPIs that are not per-cpu interrupts and force the affinity of the LPI
backing an IPI to a specific and immutable value.

=============
2.4 GICv5 ITS
=============

The ITS driver reuses the existing GICv3/v4 MSI-parent infrastructure
and on top builds an IRQ domain needed to enable message based IRQs.

ITS tables - DT (device table) and ITT (Interrupt Translation Table) are
allocated according to the number of required deviceIDs and eventIDs on
a per device basis. The ITS driver relies on the kmalloc() interface
because memory pages must be physically contiguous within a table level
and can be < or > than PAGE_SIZE.

=============
2.5 GICv5 IWB
=============

The IWB is a wire to message translator; its driver implements the
IRQ domain needed to carry out this translation and passes the
interrupt to its parent ITS domain.

An IWB is connected to an ITS and it has its own deviceID for all
interrupt wires that it manages; the IWB input wire number is
exposed to the ITS as an eventID. This eventID is not programmable
and therefore requires special handling in the ITS driver.

The current driver is tied to the ITS code and basically creates
a wired domain that allocates eventIDs into the ITS driver directly.
The ITS device allocation for the IWB is implemented directly in
the IWB driver at probe time (after all the IWB is a wired interrupt
controller with fixed events). An IWB IRQ domain is assigned to
the IWB wired IRQs, with the corresponding ITS domain as its
parent. Upon IRQ allocation (triggered by firmware parsing
the devices IRQ entries) the domain hierarchy maps the requested IWB
wire to an ITS {device/event}ID fixed pair.

An alternative IWB driver, based on the DOMAIN_BUS_WIRED_TO_MSI bus
token, was already developed and tested but the authors preferred
to post the current version to get feedback on it even though it
makes the IWB driver tied to the ITS driver requiring boilerplate
code that is not really needed (e.g. platform device deviceID parsing).

In particular, the fixed eventID nature of the IWB requires ITS
{device/event}ID allocation code to be IWB aware. To make this
work for the DOMAIN_BUS_WIRED_TO_MSI bus token approach,
the IWB should be made to work as a platform device and
most importantly, ITS code should add special handling for the
IWB (probably using an OF compatible string match or a new MSI
alloc flag). In a way, that's what the current IWB driver does.
Feedback is welcome on the matter.

===================
3. Acknowledgements
===================

The patchset was co-developed with T.Hayes and S.Bischoff from
Arm - thank you so much for your help.

If you have some time to help us review this series and get it into
shape, thank you very much.

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
---
Lorenzo Pieralisi (23):
      Documentation: devicetree: bindings: Add GICv5 DT bindings
      arm64/sysreg: Add GCIE field to ID_AA64PFR2_EL1
      arm64/sysreg: Add ICC_PPI_PRIORITY<n>_EL1
      arm64/sysreg: Add ICC_ICSR_EL1
      arm64/sysreg: Add ICC_PPI_HMR<n>_EL1
      arm64/sysreg: Add ICC_PPI_ENABLER<n>_EL1
      arm64/sysreg: Add ICC_PPI_{C/S}ACTIVER<n>_EL1
      arm64/sysreg: Add ICC_PPI_{C/S}PENDR<n>_EL1
      arm64/sysreg: Add ICC_CR0_EL1
      arm64/sysreg: Add ICC_PCR_EL1
      arm64/sysreg: Add ICC_IDR0_EL1
      arm64/sysreg: Add ICH_HFGRTR_EL2
      arm64/sysreg: Add ICH_HFGWTR_EL2
      arm64/sysreg: Add ICH_HFGITR_EL2
      arm64: Disable GICv5 read/write/instruction traps
      arm64: cpucaps: Add GCIE capability
      irqchip/gic-v5: Add GICv5 PPI support
      irqchip/gic-v5: Add GICv5 IRS/SPI support
      irqchip/gic-v5: Add GICv5 LPI/IPI support
      irqchip/gic-v5: Enable GICv5 SMP booting
      irqchip/gic-v5: Add GICv5 ITS support
      irqchip/gic-v5: Add GICv5 IWB support
      arm64: Kconfig: Enable GICv5

Marc Zyngier (1):
      arm64: smp: Support non-SGIs for IPIs

 .../bindings/interrupt-controller/arm,gic-v5.yaml  |  268 ++++
 MAINTAINERS                                        |   10 +
 arch/arm64/Kconfig                                 |    1 +
 arch/arm64/include/asm/arch_gicv5.h                |   91 ++
 arch/arm64/include/asm/el2_setup.h                 |   45 +
 arch/arm64/include/asm/smp.h                       |   24 +-
 arch/arm64/kernel/cpufeature.c                     |    7 +
 arch/arm64/kernel/smp.c                            |  156 ++-
 arch/arm64/tools/cpucaps                           |    1 +
 arch/arm64/tools/sysreg                            |  495 +++++++-
 drivers/irqchip/Kconfig                            |   16 +
 drivers/irqchip/Makefile                           |    5 +-
 drivers/irqchip/irq-gic-common.h                   |    2 -
 ...3-its-msi-parent.c => irq-gic-its-msi-parent.c} |    3 +-
 drivers/irqchip/irq-gic-its-msi-parent.h           |   13 +
 drivers/irqchip/irq-gic-v3-its.c                   |    3 +-
 drivers/irqchip/irq-gic-v5-irs.c                   |  853 +++++++++++++
 drivers/irqchip/irq-gic-v5-its.c                   | 1326 ++++++++++++++++++++
 drivers/irqchip/irq-gic-v5-iwb.c                   |  377 ++++++
 drivers/irqchip/irq-gic-v5.c                       | 1226 ++++++++++++++++++
 drivers/irqchip/irq-gic-v5.h                       |  372 ++++++
 21 files changed, 5235 insertions(+), 59 deletions(-)
---
base-commit: 0af2f6be1b4281385b618cb86ad946eded089ac8
change-id: 20250408-gicv5-host-749f316afe84

Best regards,
-- 
Lorenzo Pieralisi <lpieralisi@kernel.org>



^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH 01/24] Documentation: devicetree: bindings: Add GICv5 DT bindings
  2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
@ 2025-04-08 10:50 ` Lorenzo Pieralisi
  2025-04-08 12:26   ` Rob Herring (Arm)
  2025-04-08 15:07   ` Rob Herring
  2025-04-08 10:50 ` [PATCH 02/24] arm64/sysreg: Add GCIE field to ID_AA64PFR2_EL1 Lorenzo Pieralisi
                   ` (22 subsequent siblings)
  23 siblings, 2 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:50 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

The GICv5 interrupt controller architecture is composed of:

- one or more Interrupt Routing Service (IRS)
- zero or more Interrupt Translation Service (ITS)
- zero or more Interrupt Wire Bridge (IWB)

Describe a GICv5 implementation by specifying a top level node
corresponding to the GICv5 system component.

IRS nodes are added as GICv5 system component children.

An ITS is associated with an IRS so ITS nodes are described
as IRS children - use the hierarchy explicitly in the device
tree to define the association.

IWB nodes are described as GICv5 system component children - to make it
explicit that are part of the GICv5 system component; an IWB is
connected to a single ITS but the connection is made explicit through
the msi-parent property and therefore is not required to be explicit
through a parent-child relationship in the device tree.

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Conor Dooley <conor+dt@kernel.org>
Cc: Rob Herring <robh@kernel.org>
Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
Cc: Marc Zyngier <maz@kernel.org>
---
 .../bindings/interrupt-controller/arm,gic-v5.yaml  | 268 +++++++++++++++++++++
 MAINTAINERS                                        |   7 +
 2 files changed, 275 insertions(+)

diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.yaml b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..5c78375c298a0115c55872f439eb04d4171c4381
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.yaml
@@ -0,0 +1,268 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/interrupt-controller/arm,gic-v5.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ARM Generic Interrupt Controller, version 5
+
+maintainers:
+  - Lorenzo Pieralisi <lpieralisi@kernel.org>
+  - Marc Zyngier <maz@kernel.org>
+
+description: |
+  The GICv5 architecture defines the guidelines to implement GICv5
+  compliant interrupt controllers for AArch64 systems.
+
+  The GICv5 specification can be found at
+  https://developer.arm.com/documentation/aes0070
+
+  The GICv5 architecture is composed of multiple components:
+    - one or more IRS (Interrupt Routing Service)
+    - zero or more ITS (Interrupt Translation Service)
+    - zero or more IWB (Interrupt Wire Bridge)
+
+  The architecture defines:
+    - PE-Private Peripheral Interrupts (PPI)
+    - Shared Peripheral Interrupts (SPI)
+    - Logical Peripheral Interrupts (LPI)
+
+allOf:
+  - $ref: /schemas/interrupt-controller.yaml#
+
+properties:
+  compatible:
+    const: arm,gic-v5
+
+  interrupt-controller: true
+
+  "#address-cells":
+    enum: [ 1, 2 ]
+  "#size-cells":
+    enum: [ 1, 2 ]
+
+  ranges: true
+
+  "#interrupt-cells":
+    description: |
+      Specifies the number of cells needed to encode an interrupt source.
+      Must be a single cell with a value 3.
+
+      The 1st cell corresponds to the INTID.Type field in the INTID; 1 for PPI,
+      3 for SPI. LPI interrupts must not be described in the bindings since
+      they are allocated dynamically by the software component managing them.
+
+      The 2nd cell contains the interrupt INTID.ID field.
+
+      The 3rd cell is the flags, encoded as follows:
+      bits[3:0] trigger type and level flags.
+
+        1 = low-to-high edge triggered
+        2 = high-to-low edge triggered
+        4 = active high level-sensitive
+        8 = active low level-sensitive
+
+      Cells 4 and beyond are reserved for future use and must have a value
+      of 0 if present.
+    const: 3
+
+  interrupts:
+    description:
+      Interrupt source of the VGIC maintenance interrupt.
+    maxItems: 1
+
+required:
+  - compatible
+
+patternProperties:
+  "^irs@[0-9a-f]+$":
+    type: object
+    description:
+      GICv5 has one or more Interrupt Routing Services (IRS) that are
+      responsible for handling IRQ state and routing.
+
+    additionalProperties: false
+    properties:
+      compatible:
+        const: arm,gic-v5-irs
+
+      "#address-cells":
+        enum: [ 1, 2 ]
+      "#size-cells":
+        enum: [ 1, 2 ]
+
+      ranges: true
+
+      dma-noncoherent:
+        description:
+          Present if the GIC IRS permits programming shareability and
+          cacheability attributes but is connected to a non-coherent
+          downstream interconnect.
+
+      reg:
+        minItems: 1
+        items:
+          - description: IRS control frame
+          - description: IRS setlpi frame
+
+      cpus:
+        $ref: /schemas/types.yaml#/definitions/phandle-array
+        description:
+          Should be a list of phandles to CPU nodes (as described in
+          Documentation/devicetree/bindings/arm/cpus.yaml) corresponding to
+          CPUs managed by the IRS.
+
+      arm,iaffids:
+        $ref: /schemas/types.yaml#/definitions/uint16-array
+        description:
+          Should be a list of u16 values representing IAFFID IDs associated
+          with the CPU whose CPU node phandle is at the same index in the
+          cpus array.
+
+    patternProperties:
+      "^msi-controller@[0-9a-f]+$":
+        type: object
+        description:
+          GICv5 has zero or more Interrupt Translation Services (ITS) that are
+          used to route Message Signalled Interrupts (MSI) to the CPUs. Each
+          ITS is connected to an IRS.
+        additionalProperties: false
+        properties:
+          compatible:
+            const: arm,gic-v5-its
+
+          dma-noncoherent:
+            description:
+              Present if the GIC ITS permits programming shareability and
+              cacheability attributes but is connected to a non-coherent
+              downstream interconnect.
+
+          msi-controller: true
+
+          "#msi-cells":
+            description:
+              The single msi-cell is the DeviceID of the device which will
+              generate the MSI.
+            const: 1
+
+          reg:
+            items:
+              - description: ITS control frame
+              - description: ITS translate frame
+
+        required:
+          - compatible
+          - msi-controller
+          - "#msi-cells"
+          - reg
+
+    required:
+      - compatible
+      - reg
+      - cpus
+      - arm,iaffids
+
+  "^interrupt-controller@[0-9a-f]+$":
+    type: object
+    description:
+      GICv5 has zero or more Interrupt Wire Bridges (IWB) that are responsible
+      for translating wire signals into interrupt messages to the ITS.
+
+    additionalProperties: false
+    properties:
+      compatible:
+        const: arm,gic-v5-iwb
+
+      interrupt-controller: true
+
+      "#address-cells":
+        const: 0
+
+      "#interrupt-cells":
+        description: |
+          Specifies the number of cells needed to encode an interrupt source.
+          Must be a single cell with a value 2.
+
+          The 1st cell corresponds to the IWB wire.
+
+          The 2nd cell is the flags, encoded as follows:
+          bits[3:0] trigger type and level flags.
+
+          1 = low-to-high edge triggered
+          2 = high-to-low edge triggered
+          4 = active high level-sensitive
+          8 = active low level-sensitive
+
+          Cells 3 and beyond are reserved for future use and must have a value
+          of 0 if present.
+        const: 2
+
+      reg:
+        items:
+          - description: IWB control frame
+
+      msi-parent: true
+
+    required:
+      - compatible
+      - reg
+      - msi-parent
+
+additionalProperties: false
+
+examples:
+  - |
+    interrupt-controller {
+      compatible = "arm,gic-v5";
+      #interrupt-cells = <3>;
+      #address-cells = <2>;
+      #size-cells = <2>;
+      ranges;
+
+      interrupt-controller;
+
+      interrupts = <1 25 4>;
+
+      irs@2f1a0000 {
+        compatible = "arm,gic-v5-irs";
+        #address-cells = <2>;
+        #size-cells = <2>;
+        ranges;
+
+        reg = <0x0 0x2f1a0000 0x0 0x10000>;  // IRS_CONFIG_FRAME for NS
+
+        arm,iaffids = /bits 16 <0 1 2 3 4 5 6 7>;
+        cpus = <&{/cpus/cpu@0}>, <&{/cpus/cpu@100}>, <&{/cpus/cpu@200}>,
+               <&{/cpus/cpu@300}>, <&{/cpus/cpu@10000}>, <&{/cpus/cpu@10100}>,
+               <&{/cpus/cpu@10200}>, <&{/cpus/cpu@10300}>;
+
+        msi-controller@2f120000 {
+          compatible = "arm,gic-v5-its";
+
+          msi-controller;
+          #msi-cells = <1>;
+
+          reg = <0x0 0x2f120000 0x0 0x10000    // ITS_CONFIG_FRAME for NS
+                 0x0 0x2f130000 0x0 0x10000>;  // ITS_TRANSLATE_FRAME
+        };
+      };
+
+      interrupt-controller@2f000000 {
+        compatible = "arm,gic-v5-iwb";
+        #address-cells = <0>;
+
+        interrupt-controller;
+        #interrupt-cells = <2>;
+
+        reg = <0x0 0x2f000000 0x0 0x10000>;
+
+        msi-parent = <&its0 64>;
+      };
+    };
+
+    device@0 {
+      reg = <0 4>;
+      interrupts = <3 115 4>;
+    };
+
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 96b82704950184bd71623ff41fc4df31e4c7fe87..f3ed84466da19906953b5396a5f4b50e878c376e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1901,6 +1901,13 @@ F:	drivers/irqchip/irq-gic*.[ch]
 F:	include/linux/irqchip/arm-gic*.h
 F:	include/linux/irqchip/arm-vgic-info.h
 
+ARM GENERIC INTERRUPT CONTROLLER V5 DRIVERS
+M:	Lorenzo Pieralisi <lpieralisi@kernel.org>
+M:	Marc Zyngier <maz@kernel.org>
+L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S:	Maintained
+F:	Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.yaml
+
 ARM HDLCD DRM DRIVER
 M:	Liviu Dudau <liviu.dudau@arm.com>
 S:	Supported

-- 
2.48.0



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 02/24] arm64/sysreg: Add GCIE field to ID_AA64PFR2_EL1
  2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
  2025-04-08 10:50 ` [PATCH 01/24] Documentation: devicetree: bindings: Add GICv5 DT bindings Lorenzo Pieralisi
@ 2025-04-08 10:50 ` Lorenzo Pieralisi
  2025-04-08 10:50 ` [PATCH 03/24] arm64/sysreg: Add ICC_PPI_PRIORITY<n>_EL1 Lorenzo Pieralisi
                   ` (21 subsequent siblings)
  23 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:50 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

Add field reporting the GCIE feature to ID_AA64PFR2_EL1 sysreg.

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Will Deacon <will@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/tools/sysreg | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
index f9476848a2edfad53bb4af7f68bc05cb2a4af9ce..06e1fb5e126b41b7e41fffa0a00553d73197ac3c 100644
--- a/arch/arm64/tools/sysreg
+++ b/arch/arm64/tools/sysreg
@@ -1023,7 +1023,10 @@ UnsignedEnum	19:16	UINJ
 	0b0000	NI
 	0b0001	IMP
 EndEnum
-Res0	15:12
+UnsignedEnum	15:12	GCIE
+	0b0000	NI
+	0b0001	IMP
+EndEnum
 UnsignedEnum	11:8	MTEFAR
 	0b0000	NI
 	0b0001	IMP

-- 
2.48.0



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 03/24] arm64/sysreg: Add ICC_PPI_PRIORITY<n>_EL1
  2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
  2025-04-08 10:50 ` [PATCH 01/24] Documentation: devicetree: bindings: Add GICv5 DT bindings Lorenzo Pieralisi
  2025-04-08 10:50 ` [PATCH 02/24] arm64/sysreg: Add GCIE field to ID_AA64PFR2_EL1 Lorenzo Pieralisi
@ 2025-04-08 10:50 ` Lorenzo Pieralisi
  2025-04-08 10:50 ` [PATCH 04/24] arm64/sysreg: Add ICC_ICSR_EL1 Lorenzo Pieralisi
                   ` (20 subsequent siblings)
  23 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:50 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

Add ICC_PPI_PRIORITY<n>_EL1 sysreg description.

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Will Deacon <will@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/tools/sysreg | 83 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 83 insertions(+)

diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
index 06e1fb5e126b41b7e41fffa0a00553d73197ac3c..0cc1268c0bfad8266da47b441e80c603e46c00ae 100644
--- a/arch/arm64/tools/sysreg
+++ b/arch/arm64/tools/sysreg
@@ -2310,6 +2310,89 @@ Field	31	C
 Field	30:0	P
 EndSysreg
 
+SysregFields	ICC_PPI_PRIORITYRx_EL1
+Res0	63:61
+Field	60:56	Priority7
+Res0	55:53
+Field	52:48	Priority6
+Res0	47:45
+Field	44:40	Priority5
+Res0	39:37
+Field	36:32	Priority4
+Res0	31:29
+Field	28:24	Priority3
+Res0	23:21
+Field	20:16	Priority2
+Res0	15:13
+Field	12:8	Priority1
+Res0	7:5
+Field	4:0	Priority0
+EndSysregFields
+
+Sysreg	ICC_PPI_PRIORITYR0_EL1	3	0	12	14	0
+Fields	ICC_PPI_PRIORITYRx_EL1
+EndSysreg
+
+Sysreg	ICC_PPI_PRIORITYR1_EL1	3	0	12	14	1
+Fields	ICC_PPI_PRIORITYRx_EL1
+EndSysreg
+
+Sysreg	ICC_PPI_PRIORITYR2_EL1	3	0	12	14	2
+Fields	ICC_PPI_PRIORITYRx_EL1
+EndSysreg
+
+Sysreg	ICC_PPI_PRIORITYR3_EL1	3	0	12	14	3
+Fields	ICC_PPI_PRIORITYRx_EL1
+EndSysreg
+
+Sysreg	ICC_PPI_PRIORITYR4_EL1	3	0	12	14	4
+Fields	ICC_PPI_PRIORITYRx_EL1
+EndSysreg
+
+Sysreg	ICC_PPI_PRIORITYR5_EL1	3	0	12	14	5
+Fields	ICC_PPI_PRIORITYRx_EL1
+EndSysreg
+
+Sysreg	ICC_PPI_PRIORITYR6_EL1	3	0	12	14	6
+Fields	ICC_PPI_PRIORITYRx_EL1
+EndSysreg
+
+Sysreg	ICC_PPI_PRIORITYR7_EL1	3	0	12	14	7
+Fields	ICC_PPI_PRIORITYRx_EL1
+EndSysreg
+
+Sysreg	ICC_PPI_PRIORITYR8_EL1	3	0	12	15	0
+Fields	ICC_PPI_PRIORITYRx_EL1
+EndSysreg
+
+Sysreg	ICC_PPI_PRIORITYR9_EL1	3	0	12	15	1
+Fields	ICC_PPI_PRIORITYRx_EL1
+EndSysreg
+
+Sysreg	ICC_PPI_PRIORITYR10_EL1	3	0	12	15	2
+Fields	ICC_PPI_PRIORITYRx_EL1
+EndSysreg
+
+Sysreg	ICC_PPI_PRIORITYR11_EL1	3	0	12	15	3
+Fields	ICC_PPI_PRIORITYRx_EL1
+EndSysreg
+
+Sysreg	ICC_PPI_PRIORITYR12_EL1	3	0	12	15	4
+Fields	ICC_PPI_PRIORITYRx_EL1
+EndSysreg
+
+Sysreg	ICC_PPI_PRIORITYR13_EL1	3	0	12	15	5
+Fields	ICC_PPI_PRIORITYRx_EL1
+EndSysreg
+
+Sysreg	ICC_PPI_PRIORITYR14_EL1	3	0	12	15	6
+Fields	ICC_PPI_PRIORITYRx_EL1
+EndSysreg
+
+Sysreg	ICC_PPI_PRIORITYR15_EL1	3	0	12	15	7
+Fields	ICC_PPI_PRIORITYRx_EL1
+EndSysreg
+
 Sysreg	PMSELR_EL0	3	3	9	12	5
 Res0	63:5
 Field	4:0	SEL

-- 
2.48.0



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 04/24] arm64/sysreg: Add ICC_ICSR_EL1
  2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (2 preceding siblings ...)
  2025-04-08 10:50 ` [PATCH 03/24] arm64/sysreg: Add ICC_PPI_PRIORITY<n>_EL1 Lorenzo Pieralisi
@ 2025-04-08 10:50 ` Lorenzo Pieralisi
  2025-04-08 10:50 ` [PATCH 05/24] arm64/sysreg: Add ICC_PPI_HMR<n>_EL1 Lorenzo Pieralisi
                   ` (19 subsequent siblings)
  23 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:50 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

Add ICC_ICSR_EL1 register sysreg description.

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Will Deacon <will@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/tools/sysreg | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
index 0cc1268c0bfad8266da47b441e80c603e46c00ae..985f2cdb67cfec6df335a3951ecb63f128f6da55 100644
--- a/arch/arm64/tools/sysreg
+++ b/arch/arm64/tools/sysreg
@@ -2310,6 +2310,20 @@ Field	31	C
 Field	30:0	P
 EndSysreg
 
+Sysreg	ICC_ICSR_EL1	3	0	12	10	4
+Res0	63:48
+Field	47:32	IAFFID
+Res0	31:16
+Field	15:11	Priority
+Res0	10:6
+Field	5	HM
+Field	4	Active
+Field	3	IRM
+Field	2	Pending
+Field	1	Enabled
+Field	0	F
+EndSysreg
+
 SysregFields	ICC_PPI_PRIORITYRx_EL1
 Res0	63:61
 Field	60:56	Priority7

-- 
2.48.0



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 05/24] arm64/sysreg: Add ICC_PPI_HMR<n>_EL1
  2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (3 preceding siblings ...)
  2025-04-08 10:50 ` [PATCH 04/24] arm64/sysreg: Add ICC_ICSR_EL1 Lorenzo Pieralisi
@ 2025-04-08 10:50 ` Lorenzo Pieralisi
  2025-04-08 10:50 ` [PATCH 06/24] arm64/sysreg: Add ICC_PPI_ENABLER<n>_EL1 Lorenzo Pieralisi
                   ` (18 subsequent siblings)
  23 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:50 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

Add ICC_PPI_HMR<n>_EL1 registers sysreg description.

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Will Deacon <will@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/tools/sysreg | 75 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 75 insertions(+)

diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
index 985f2cdb67cfec6df335a3951ecb63f128f6da55..d046d719d4f69801aeef51b5b9437a0eaa04134e 100644
--- a/arch/arm64/tools/sysreg
+++ b/arch/arm64/tools/sysreg
@@ -2324,6 +2324,81 @@ Field	1	Enabled
 Field	0	F
 EndSysreg
 
+SysregFields	ICC_PPI_HMRx_EL1
+Field	63	HM63
+Field	62	HM62
+Field	61	HM61
+Field	60	HM60
+Field	59	HM59
+Field	58	HM58
+Field	57	HM57
+Field	56	HM56
+Field	55	HM55
+Field	54	HM54
+Field	53	HM53
+Field	52	HM52
+Field	51	HM51
+Field	50	HM50
+Field	49	HM49
+Field	48	HM48
+Field	47	HM47
+Field	46	HM46
+Field	45	HM45
+Field	44	HM44
+Field	43	HM43
+Field	42	HM42
+Field	41	HM41
+Field	40	HM40
+Field	39	HM39
+Field	38	HM38
+Field	37	HM37
+Field	36	HM36
+Field	35	HM35
+Field	34	HM34
+Field	33	HM33
+Field	32	HM32
+Field	31	HM31
+Field	30	HM30
+Field	29	HM29
+Field	28	HM28
+Field	27	HM27
+Field	26	HM26
+Field	25	HM25
+Field	24	HM24
+Field	23	HM23
+Field	22	HM22
+Field	21	HM21
+Field	20	HM20
+Field	19	HM19
+Field	18	HM18
+Field	17	HM17
+Field	16	HM16
+Field	15	HM15
+Field	14	HM14
+Field	13	HM13
+Field	12	HM12
+Field	11	HM11
+Field	10	HM10
+Field	9	HM9
+Field	8	HM8
+Field	7	HM7
+Field	6	HM6
+Field	5	HM5
+Field	4	HM4
+Field	3	HM3
+Field	2	HM2
+Field	1	HM1
+Field	0	HM0
+EndSysregFields
+
+Sysreg	ICC_PPI_HMR0_EL1	3	0	12	10	0
+Fields ICC_PPI_HMRx_EL1
+EndSysreg
+
+Sysreg	ICC_PPI_HMR1_EL1	3	0	12	10	1
+Fields ICC_PPI_HMRx_EL1
+EndSysreg
+
 SysregFields	ICC_PPI_PRIORITYRx_EL1
 Res0	63:61
 Field	60:56	Priority7

-- 
2.48.0



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 06/24] arm64/sysreg: Add ICC_PPI_ENABLER<n>_EL1
  2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (4 preceding siblings ...)
  2025-04-08 10:50 ` [PATCH 05/24] arm64/sysreg: Add ICC_PPI_HMR<n>_EL1 Lorenzo Pieralisi
@ 2025-04-08 10:50 ` Lorenzo Pieralisi
  2025-04-08 10:50 ` [PATCH 07/24] arm64/sysreg: Add ICC_PPI_{C/S}ACTIVER<n>_EL1 Lorenzo Pieralisi
                   ` (17 subsequent siblings)
  23 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:50 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

Add ICC_PPI_ENABLER<n>_EL1 registers sysreg description.

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Will Deacon <will@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/tools/sysreg | 75 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 75 insertions(+)

diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
index d046d719d4f69801aeef51b5b9437a0eaa04134e..6c5552707ad88c145adc8b7ceb3f63da401191ea 100644
--- a/arch/arm64/tools/sysreg
+++ b/arch/arm64/tools/sysreg
@@ -2399,6 +2399,81 @@ Sysreg	ICC_PPI_HMR1_EL1	3	0	12	10	1
 Fields ICC_PPI_HMRx_EL1
 EndSysreg
 
+SysregFields	ICC_PPI_ENABLERx_EL1
+Field	63	EN63
+Field	62	EN62
+Field	61	EN61
+Field	60	EN60
+Field	59	EN59
+Field	58	EN58
+Field	57	EN57
+Field	56	EN56
+Field	55	EN55
+Field	54	EN54
+Field	53	EN53
+Field	52	EN52
+Field	51	EN51
+Field	50	EN50
+Field	49	EN49
+Field	48	EN48
+Field	47	EN47
+Field	46	EN46
+Field	45	EN45
+Field	44	EN44
+Field	43	EN43
+Field	42	EN42
+Field	41	EN41
+Field	40	EN40
+Field	39	EN39
+Field	38	EN38
+Field	37	EN37
+Field	36	EN36
+Field	35	EN35
+Field	34	EN34
+Field	33	EN33
+Field	32	EN32
+Field	31	EN31
+Field	30	EN30
+Field	29	EN29
+Field	28	EN28
+Field	27	EN27
+Field	26	EN26
+Field	25	EN25
+Field	24	EN24
+Field	23	EN23
+Field	22	EN22
+Field	21	EN21
+Field	20	EN20
+Field	19	EN19
+Field	18	EN18
+Field	17	EN17
+Field	16	EN16
+Field	15	EN15
+Field	14	EN14
+Field	13	EN13
+Field	12	EN12
+Field	11	EN11
+Field	10	EN10
+Field	9	EN9
+Field	8	EN8
+Field	7	EN7
+Field	6	EN6
+Field	5	EN5
+Field	4	EN4
+Field	3	EN3
+Field	2	EN2
+Field	1	EN1
+Field	0	EN0
+EndSysregFields
+
+Sysreg	ICC_PPI_ENABLER0_EL1	3	0	12	10	6
+Fields ICC_PPI_ENABLERx_EL1
+EndSysreg
+
+Sysreg	ICC_PPI_ENABLER1_EL1	3	0	12	10	7
+Fields ICC_PPI_ENABLERx_EL1
+EndSysreg
+
 SysregFields	ICC_PPI_PRIORITYRx_EL1
 Res0	63:61
 Field	60:56	Priority7

-- 
2.48.0



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 07/24] arm64/sysreg: Add ICC_PPI_{C/S}ACTIVER<n>_EL1
  2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (5 preceding siblings ...)
  2025-04-08 10:50 ` [PATCH 06/24] arm64/sysreg: Add ICC_PPI_ENABLER<n>_EL1 Lorenzo Pieralisi
@ 2025-04-08 10:50 ` Lorenzo Pieralisi
  2025-04-08 10:50 ` [PATCH 08/24] arm64/sysreg: Add ICC_PPI_{C/S}PENDR<n>_EL1 Lorenzo Pieralisi
                   ` (16 subsequent siblings)
  23 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:50 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

Add ICC_PPI_{C/S}ACTIVER<n>_EL1 registers description.

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Will Deacon <will@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/tools/sysreg | 83 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 83 insertions(+)

diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
index 6c5552707ad88c145adc8b7ceb3f63da401191ea..0485721e1575c9ed158210c6f02fb9af2828f2d5 100644
--- a/arch/arm64/tools/sysreg
+++ b/arch/arm64/tools/sysreg
@@ -2474,6 +2474,89 @@ Sysreg	ICC_PPI_ENABLER1_EL1	3	0	12	10	7
 Fields ICC_PPI_ENABLERx_EL1
 EndSysreg
 
+SysregFields	ICC_PPI_ACTIVERx_EL1
+Field	63	Active63
+Field	62	Active62
+Field	61	Active61
+Field	60	Active60
+Field	59	Active59
+Field	58	Active58
+Field	57	Active57
+Field	56	Active56
+Field	55	Active55
+Field	54	Active54
+Field	53	Active53
+Field	52	Active52
+Field	51	Active51
+Field	50	Active50
+Field	49	Active49
+Field	48	Active48
+Field	47	Active47
+Field	46	Active46
+Field	45	Active45
+Field	44	Active44
+Field	43	Active43
+Field	42	Active42
+Field	41	Active41
+Field	40	Active40
+Field	39	Active39
+Field	38	Active38
+Field	37	Active37
+Field	36	Active36
+Field	35	Active35
+Field	34	Active34
+Field	33	Active33
+Field	32	Active32
+Field	31	Active31
+Field	30	Active30
+Field	29	Active29
+Field	28	Active28
+Field	27	Active27
+Field	26	Active26
+Field	25	Active25
+Field	24	Active24
+Field	23	Active23
+Field	22	Active22
+Field	21	Active21
+Field	20	Active20
+Field	19	Active19
+Field	18	Active18
+Field	17	Active17
+Field	16	Active16
+Field	15	Active15
+Field	14	Active14
+Field	13	Active13
+Field	12	Active12
+Field	11	Active11
+Field	10	Active10
+Field	9	Active9
+Field	8	Active8
+Field	7	Active7
+Field	6	Active6
+Field	5	Active5
+Field	4	Active4
+Field	3	Active3
+Field	2	Active2
+Field	1	Active1
+Field	0	Active0
+EndSysregFields
+
+Sysreg	ICC_PPI_CACTIVER0_EL1	3	0	12	13	0
+Fields ICC_PPI_ACTIVERx_EL1
+EndSysreg
+
+Sysreg	ICC_PPI_CACTIVER1_EL1	3	0	12	13	1
+Fields ICC_PPI_ACTIVERx_EL1
+EndSysreg
+
+Sysreg	ICC_PPI_SACTIVER0_EL1	3	0	12	13	2
+Fields ICC_PPI_ACTIVERx_EL1
+EndSysreg
+
+Sysreg	ICC_PPI_SACTIVER1_EL1	3	0	12	13	3
+Fields ICC_PPI_ACTIVERx_EL1
+EndSysreg
+
 SysregFields	ICC_PPI_PRIORITYRx_EL1
 Res0	63:61
 Field	60:56	Priority7

-- 
2.48.0



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 08/24] arm64/sysreg: Add ICC_PPI_{C/S}PENDR<n>_EL1
  2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (6 preceding siblings ...)
  2025-04-08 10:50 ` [PATCH 07/24] arm64/sysreg: Add ICC_PPI_{C/S}ACTIVER<n>_EL1 Lorenzo Pieralisi
@ 2025-04-08 10:50 ` Lorenzo Pieralisi
  2025-04-08 10:50 ` [PATCH 09/24] arm64/sysreg: Add ICC_CR0_EL1 Lorenzo Pieralisi
                   ` (15 subsequent siblings)
  23 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:50 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

Add ICC_PPI_{C/S}PENDR<n>_EL1 registers description.

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Will Deacon <will@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/tools/sysreg | 83 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 83 insertions(+)

diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
index 0485721e1575c9ed158210c6f02fb9af2828f2d5..7acad93718c56729ce2a333ed007243ec554dbc9 100644
--- a/arch/arm64/tools/sysreg
+++ b/arch/arm64/tools/sysreg
@@ -2557,6 +2557,89 @@ Sysreg	ICC_PPI_SACTIVER1_EL1	3	0	12	13	3
 Fields ICC_PPI_ACTIVERx_EL1
 EndSysreg
 
+SysregFields	ICC_PPI_PENDRx_EL1
+Field	63	Pend63
+Field	62	Pend62
+Field	61	Pend61
+Field	60	Pend60
+Field	59	Pend59
+Field	58	Pend58
+Field	57	Pend57
+Field	56	Pend56
+Field	55	Pend55
+Field	54	Pend54
+Field	53	Pend53
+Field	52	Pend52
+Field	51	Pend51
+Field	50	Pend50
+Field	49	Pend49
+Field	48	Pend48
+Field	47	Pend47
+Field	46	Pend46
+Field	45	Pend45
+Field	44	Pend44
+Field	43	Pend43
+Field	42	Pend42
+Field	41	Pend41
+Field	40	Pend40
+Field	39	Pend39
+Field	38	Pend38
+Field	37	Pend37
+Field	36	Pend36
+Field	35	Pend35
+Field	34	Pend34
+Field	33	Pend33
+Field	32	Pend32
+Field	31	Pend31
+Field	30	Pend30
+Field	29	Pend29
+Field	28	Pend28
+Field	27	Pend27
+Field	26	Pend26
+Field	25	Pend25
+Field	24	Pend24
+Field	23	Pend23
+Field	22	Pend22
+Field	21	Pend21
+Field	20	Pend20
+Field	19	Pend19
+Field	18	Pend18
+Field	17	Pend17
+Field	16	Pend16
+Field	15	Pend15
+Field	14	Pend14
+Field	13	Pend13
+Field	12	Pend12
+Field	11	Pend11
+Field	10	Pend10
+Field	9	Pend9
+Field	8	Pend8
+Field	7	Pend7
+Field	6	Pend6
+Field	5	Pend5
+Field	4	Pend4
+Field	3	Pend3
+Field	2	Pend2
+Field	1	Pend1
+Field	0	Pend0
+EndSysregFields
+
+Sysreg	ICC_PPI_CPENDR0_EL1	3	0	12	13	4
+Fields ICC_PPI_PENDRx_EL1
+EndSysreg
+
+Sysreg	ICC_PPI_CPENDR1_EL1	3	0	12	13	5
+Fields ICC_PPI_PENDRx_EL1
+EndSysreg
+
+Sysreg	ICC_PPI_SPENDR0_EL1	3	0	12	13	6
+Fields ICC_PPI_PENDRx_EL1
+EndSysreg
+
+Sysreg	ICC_PPI_SPENDR1_EL1	3	0	12	13	7
+Fields ICC_PPI_PENDRx_EL1
+EndSysreg
+
 SysregFields	ICC_PPI_PRIORITYRx_EL1
 Res0	63:61
 Field	60:56	Priority7

-- 
2.48.0



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 09/24] arm64/sysreg: Add ICC_CR0_EL1
  2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (7 preceding siblings ...)
  2025-04-08 10:50 ` [PATCH 08/24] arm64/sysreg: Add ICC_PPI_{C/S}PENDR<n>_EL1 Lorenzo Pieralisi
@ 2025-04-08 10:50 ` Lorenzo Pieralisi
  2025-04-08 10:50 ` [PATCH 10/24] arm64/sysreg: Add ICC_PCR_EL1 Lorenzo Pieralisi
                   ` (14 subsequent siblings)
  23 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:50 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

Add ICC_CR0_EL1 register description.

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Will Deacon <will@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/tools/sysreg | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
index 7acad93718c56729ce2a333ed007243ec554dbc9..c96243505031ea680c04a693fee2c96ad19e30ea 100644
--- a/arch/arm64/tools/sysreg
+++ b/arch/arm64/tools/sysreg
@@ -2798,6 +2798,14 @@ Res0	14:12
 Field	11:0	AFFINITY
 EndSysreg
 
+Sysreg	ICC_CR0_EL1	3	1	12	0	1
+Res0	63:39
+Field	38	PID
+Field	37:32	IPPT
+Res0	31:1
+Field	0	EN
+EndSysreg
+
 Sysreg	CSSELR_EL1	3	2	0	0	0
 Res0	63:5
 Field	4	TnD

-- 
2.48.0



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 10/24] arm64/sysreg: Add ICC_PCR_EL1
  2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (8 preceding siblings ...)
  2025-04-08 10:50 ` [PATCH 09/24] arm64/sysreg: Add ICC_CR0_EL1 Lorenzo Pieralisi
@ 2025-04-08 10:50 ` Lorenzo Pieralisi
  2025-04-08 10:50 ` [PATCH 11/24] arm64/sysreg: Add ICC_IDR0_EL1 Lorenzo Pieralisi
                   ` (13 subsequent siblings)
  23 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:50 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

Add ICC_PCR_EL1 register description.

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Will Deacon <will@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/tools/sysreg | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
index c96243505031ea680c04a693fee2c96ad19e30ea..9a2ddab8661c85586b0e91f7eaabd5a6b3409c67 100644
--- a/arch/arm64/tools/sysreg
+++ b/arch/arm64/tools/sysreg
@@ -2806,6 +2806,11 @@ Res0	31:1
 Field	0	EN
 EndSysreg
 
+Sysreg	ICC_PCR_EL1	3	1	12	0	2
+Res0	63:5
+Field	4:0	PRIORITY
+EndSysreg
+
 Sysreg	CSSELR_EL1	3	2	0	0	0
 Res0	63:5
 Field	4	TnD

-- 
2.48.0



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 11/24] arm64/sysreg: Add ICC_IDR0_EL1
  2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (9 preceding siblings ...)
  2025-04-08 10:50 ` [PATCH 10/24] arm64/sysreg: Add ICC_PCR_EL1 Lorenzo Pieralisi
@ 2025-04-08 10:50 ` Lorenzo Pieralisi
  2025-04-08 10:50 ` [PATCH 12/24] arm64/sysreg: Add ICH_HFGRTR_EL2 Lorenzo Pieralisi
                   ` (12 subsequent siblings)
  23 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:50 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

Add ICC_IDR0_EL1 register description.

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Will Deacon <will@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/tools/sysreg | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
index 9a2ddab8661c85586b0e91f7eaabd5a6b3409c67..1ec8113df713dfea6d38e39c42eba1e3dca5eea5 100644
--- a/arch/arm64/tools/sysreg
+++ b/arch/arm64/tools/sysreg
@@ -2399,6 +2399,22 @@ Sysreg	ICC_PPI_HMR1_EL1	3	0	12	10	1
 Fields ICC_PPI_HMRx_EL1
 EndSysreg
 
+Sysreg	ICC_IDR0_EL1	3	0	12	10	2
+Res0	63:12
+UnsignedEnum	11:8	GCIE_LEGACY
+	0b0000	NI
+	0b0001	IMP
+EndEnum
+UnsignedEnum	7:4	PRI_BITS
+	0b0011	4BITS
+	0b0100	5BITS
+EndEnum
+UnsignedEnum	3:0	ID_BITS
+	0b0000	16BITS
+	0b0001	24BITS
+EndEnum
+EndSysreg
+
 SysregFields	ICC_PPI_ENABLERx_EL1
 Field	63	EN63
 Field	62	EN62

-- 
2.48.0



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 12/24] arm64/sysreg: Add ICH_HFGRTR_EL2
  2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (10 preceding siblings ...)
  2025-04-08 10:50 ` [PATCH 11/24] arm64/sysreg: Add ICC_IDR0_EL1 Lorenzo Pieralisi
@ 2025-04-08 10:50 ` Lorenzo Pieralisi
  2025-04-08 10:50 ` [PATCH 13/24] arm64/sysreg: Add ICH_HFGWTR_EL2 Lorenzo Pieralisi
                   ` (11 subsequent siblings)
  23 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:50 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

Add ICH_HFGRTR_EL2 register description.

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Will Deacon <will@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/tools/sysreg | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
index 1ec8113df713dfea6d38e39c42eba1e3dca5eea5..0c0e805481c84a14ae62d199466171d97d54ef90 100644
--- a/arch/arm64/tools/sysreg
+++ b/arch/arm64/tools/sysreg
@@ -3583,6 +3583,24 @@ Field	31:16	PhyPARTID29
 Field	15:0	PhyPARTID28
 EndSysreg
 
+Sysreg	ICH_HFGRTR_EL2	3	4	12	9	4
+Res0	63:21
+Field	20	ICC_PPI_ACTIVERn_EL1
+Field	19	ICC_PPI_PRIORITYRn_EL1
+Field	18	ICC_PPI_PENDRn_EL1
+Field	17	ICC_PPI_ENABLERn_EL1
+Field	16	ICC_PPI_HMRn_EL1
+Res0	15:8
+Field	7	ICC_IAFFIDR_EL1
+Field	6	ICC_ICSR_EL1
+Field	5	ICC_PCR_EL1
+Field	4	ICC_HPPIR_EL1
+Field	3	ICC_HAPR_EL1
+Field	2	ICC_CR0_EL1
+Field	1	ICC_IDRn_EL1
+Field	0	ICC_APR_EL1
+EndSysreg
+
 Sysreg	ICH_HCR_EL2	3	4	12	11	0
 Res0	63:32
 Field	31:27	EOIcount

-- 
2.48.0



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 13/24] arm64/sysreg: Add ICH_HFGWTR_EL2
  2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (11 preceding siblings ...)
  2025-04-08 10:50 ` [PATCH 12/24] arm64/sysreg: Add ICH_HFGRTR_EL2 Lorenzo Pieralisi
@ 2025-04-08 10:50 ` Lorenzo Pieralisi
  2025-04-09  7:48   ` Arnd Bergmann
  2025-04-08 10:50 ` [PATCH 14/24] arm64/sysreg: Add ICH_HFGITR_EL2 Lorenzo Pieralisi
                   ` (10 subsequent siblings)
  23 siblings, 1 reply; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:50 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

Add ICH_HFGWTR_EL2 register description to sysreg.

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Will Deacon <will@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/tools/sysreg | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
index 0c0e805481c84a14ae62d199466171d97d54ef90..1b519e35000be328acfe26d51e098059f9cf9ef2 100644
--- a/arch/arm64/tools/sysreg
+++ b/arch/arm64/tools/sysreg
@@ -3601,6 +3601,21 @@ Field	1	ICC_IDRn_EL1
 Field	0	ICC_APR_EL1
 EndSysreg
 
+Sysreg	ICH_HFGWTR_EL2	3	4	12	9	6
+Res0	63:21
+Field	20	ICC_PPI_ACTIVERn_EL1
+Field	19	ICC_PPI_PRIORITYRn_EL1
+Field	18	ICC_PPI_PENDRn_EL1
+Field	17	ICC_PPI_ENABLERn_EL1
+Res0	16:7
+Field	6	ICC_ICSR_EL1
+Field	5	ICC_PCR_EL1
+Res0    4:3
+Field	2	ICC_CR0_EL1
+Res0	1
+Field	0	ICC_APR_EL1
+EndSysreg
+
 Sysreg	ICH_HCR_EL2	3	4	12	11	0
 Res0	63:32
 Field	31:27	EOIcount

-- 
2.48.0



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 14/24] arm64/sysreg: Add ICH_HFGITR_EL2
  2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (12 preceding siblings ...)
  2025-04-08 10:50 ` [PATCH 13/24] arm64/sysreg: Add ICH_HFGWTR_EL2 Lorenzo Pieralisi
@ 2025-04-08 10:50 ` Lorenzo Pieralisi
  2025-04-08 10:50 ` [PATCH 15/24] arm64: Disable GICv5 read/write/instruction traps Lorenzo Pieralisi
                   ` (9 subsequent siblings)
  23 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:50 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

Add ICH_HFGITR_EL2 register description to sysreg.

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Will Deacon <will@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/tools/sysreg | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
index 1b519e35000be328acfe26d51e098059f9cf9ef2..5af0dea6e775ea680686dbe4bc836b5f5b69fbc7 100644
--- a/arch/arm64/tools/sysreg
+++ b/arch/arm64/tools/sysreg
@@ -3616,6 +3616,21 @@ Res0	1
 Field	0	ICC_APR_EL1
 EndSysreg
 
+Sysreg	ICH_HFGITR_EL2	3	4	12	9	7
+Res0	63:11
+Field	10	GICRCDNMIA
+Field	9	GICRCDIA
+Field	8	GICCDDI
+Field	7	GICCDEOI
+Field	6	GICCDHM
+Field	5	GICCRDRCFG
+Field	4	GICCDPEND
+Field	3	GICCDAFF
+Field	2	GICCDPRI
+Field	1	GICCDDIS
+Field	0	GICCDEN
+EndSysreg
+
 Sysreg	ICH_HCR_EL2	3	4	12	11	0
 Res0	63:32
 Field	31:27	EOIcount

-- 
2.48.0



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 15/24] arm64: Disable GICv5 read/write/instruction traps
  2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (13 preceding siblings ...)
  2025-04-08 10:50 ` [PATCH 14/24] arm64/sysreg: Add ICH_HFGITR_EL2 Lorenzo Pieralisi
@ 2025-04-08 10:50 ` Lorenzo Pieralisi
  2025-04-08 10:50 ` [PATCH 16/24] arm64: cpucaps: Add GCIE capability Lorenzo Pieralisi
                   ` (8 subsequent siblings)
  23 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:50 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

GICv5 trap configuration registers value is UNKNOWN at reset.

Initialize GICv5 EL2 trap configuration registers to prevent
trapping GICv5 instruction/register access upon entering the
kernel.

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Will Deacon <will@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/include/asm/el2_setup.h | 45 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/arch/arm64/include/asm/el2_setup.h b/arch/arm64/include/asm/el2_setup.h
index ebceaae3c749b84395c9c5eccf0caf874697ad11..1e362bb3b042d51fff15a7c2abc73842930b275a 100644
--- a/arch/arm64/include/asm/el2_setup.h
+++ b/arch/arm64/include/asm/el2_setup.h
@@ -165,6 +165,50 @@
 .Lskip_gicv3_\@:
 .endm
 
+/* GICv5 system register access */
+.macro __init_el2_gicv5
+	mrs_s	x0, SYS_ID_AA64PFR2_EL1
+	ubfx	x0, x0, #ID_AA64PFR2_EL1_GCIE_SHIFT, #4
+	cbz	x0, .Lskip_gicv5_\@
+
+	mov	x0, #(1 << ICH_HFGITR_EL2_GICRCDNMIA_SHIFT	| \
+		      1 << ICH_HFGITR_EL2_GICRCDIA_SHIFT	| \
+		      1 << ICH_HFGITR_EL2_GICCDDI_SHIFT		| \
+		      1 << ICH_HFGITR_EL2_GICCDEOI_SHIFT	| \
+		      1 << ICH_HFGITR_EL2_GICCDHM_SHIFT		| \
+		      1 << ICH_HFGITR_EL2_GICCRDRCFG_SHIFT	| \
+		      1 << ICH_HFGITR_EL2_GICCDPEND_SHIFT	| \
+		      1 << ICH_HFGITR_EL2_GICCDAFF_SHIFT	| \
+		      1 << ICH_HFGITR_EL2_GICCDPRI_SHIFT	| \
+		      1 << ICH_HFGITR_EL2_GICCDDIS_SHIFT	| \
+		      1 << ICH_HFGITR_EL2_GICCDEN_SHIFT)
+	msr_s	SYS_ICH_HFGITR_EL2, x0		// Disable instruction traps
+	mov_q	x0, (1 << ICH_HFGRTR_EL2_ICC_PPI_ACTIVERn_EL1_SHIFT	| \
+		     1 << ICH_HFGRTR_EL2_ICC_PPI_PRIORITYRn_EL1_SHIFT	| \
+		     1 << ICH_HFGRTR_EL2_ICC_PPI_PENDRn_EL1_SHIFT	| \
+		     1 << ICH_HFGRTR_EL2_ICC_PPI_ENABLERn_EL1_SHIFT	| \
+		     1 << ICH_HFGRTR_EL2_ICC_PPI_HMRn_EL1_SHIFT		| \
+		     1 << ICH_HFGRTR_EL2_ICC_IAFFIDR_EL1_SHIFT		| \
+		     1 << ICH_HFGRTR_EL2_ICC_ICSR_EL1_SHIFT		| \
+		     1 << ICH_HFGRTR_EL2_ICC_PCR_EL1_SHIFT		| \
+		     1 << ICH_HFGRTR_EL2_ICC_HPPIR_EL1_SHIFT		| \
+		     1 << ICH_HFGRTR_EL2_ICC_HAPR_EL1_SHIFT		| \
+		     1 << ICH_HFGRTR_EL2_ICC_CR0_EL1_SHIFT		| \
+		     1 << ICH_HFGRTR_EL2_ICC_IDRn_EL1_SHIFT		| \
+		     1 << ICH_HFGRTR_EL2_ICC_APR_EL1_SHIFT)
+	msr_s	SYS_ICH_HFGRTR_EL2, x0		// Disable reg read traps
+	mov_q	x0, (1 << ICH_HFGWTR_EL2_ICC_PPI_ACTIVERn_EL1_SHIFT	| \
+		     1 << ICH_HFGWTR_EL2_ICC_PPI_PRIORITYRn_EL1_SHIFT	| \
+		     1 << ICH_HFGWTR_EL2_ICC_PPI_PENDRn_EL1_SHIFT	| \
+		     1 << ICH_HFGWTR_EL2_ICC_PPI_ENABLERn_EL1_SHIFT	| \
+		     1 << ICH_HFGWTR_EL2_ICC_ICSR_EL1_SHIFT		| \
+		     1 << ICH_HFGWTR_EL2_ICC_PCR_EL1_SHIFT		| \
+		     1 << ICH_HFGWTR_EL2_ICC_CR0_EL1_SHIFT		| \
+		     1 << ICH_HFGWTR_EL2_ICC_APR_EL1_SHIFT)
+	msr_s	SYS_ICH_HFGWTR_EL2, x0		// Disable reg write traps
+.Lskip_gicv5_\@:
+.endm
+
 .macro __init_el2_hstr
 	msr	hstr_el2, xzr			// Disable CP15 traps to EL2
 .endm
@@ -323,6 +367,7 @@
 	__init_el2_lor
 	__init_el2_stage2
 	__init_el2_gicv3
+	__init_el2_gicv5
 	__init_el2_hstr
 	__init_el2_mpam
 	__init_el2_nvhe_idregs

-- 
2.48.0



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 16/24] arm64: cpucaps: Add GCIE capability
  2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (14 preceding siblings ...)
  2025-04-08 10:50 ` [PATCH 15/24] arm64: Disable GICv5 read/write/instruction traps Lorenzo Pieralisi
@ 2025-04-08 10:50 ` Lorenzo Pieralisi
  2025-04-08 11:26   ` Mark Rutland
  2025-04-08 10:50 ` [PATCH 17/24] arm64: smp: Support non-SGIs for IPIs Lorenzo Pieralisi
                   ` (7 subsequent siblings)
  23 siblings, 1 reply; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:50 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

Implement the GCIE capability as a strict boot cpu capability to
detect whether architectural GICv5 support is available in HW.

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Will Deacon <will@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kernel/cpufeature.c | 7 +++++++
 arch/arm64/tools/cpucaps       | 1 +
 2 files changed, 8 insertions(+)

diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 9c4d6d552b25cb3a31d1fb267bd73d3f82513e69..8c60591633f3d435ad9b80a10e484f26af328964 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -3041,6 +3041,13 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
 		.matches = has_pmuv3,
 	},
 #endif
+	{
+		.desc = "GCIE",
+		.type = ARM64_CPUCAP_STRICT_BOOT_CPU_FEATURE,
+		.capability = ARM64_HAS_GCIE,
+		.matches = has_cpuid_feature,
+		ARM64_CPUID_FIELDS(ID_AA64PFR2_EL1, GCIE, IMP)
+	},
 	{},
 };
 
diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps
index 772c1b008e437ed34cedb1c0f663c4dcea8f6759..e87e4bc2501f6b86352c03bd35adf33c160a577c 100644
--- a/arch/arm64/tools/cpucaps
+++ b/arch/arm64/tools/cpucaps
@@ -29,6 +29,7 @@ HAS_EVT
 HAS_FPMR
 HAS_FGT
 HAS_FPSIMD
+HAS_GCIE
 HAS_GCS
 HAS_GENERIC_AUTH
 HAS_GENERIC_AUTH_ARCH_QARMA3

-- 
2.48.0



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 17/24] arm64: smp: Support non-SGIs for IPIs
  2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (15 preceding siblings ...)
  2025-04-08 10:50 ` [PATCH 16/24] arm64: cpucaps: Add GCIE capability Lorenzo Pieralisi
@ 2025-04-08 10:50 ` Lorenzo Pieralisi
  2025-04-08 10:50 ` [PATCH 18/24] irqchip/gic-v5: Add GICv5 PPI support Lorenzo Pieralisi
                   ` (6 subsequent siblings)
  23 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:50 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

From: Marc Zyngier <maz@kernel.org>

The arm64 arch has relied so far on GIC architectural software
generated interrupt (SGIs) to handle IPIs. Those are per-cpu
software generated interrupts.

arm64 architecture code that allocates the IPIs virtual IRQs and
IRQ descriptors was written accordingly.

On GICv5 systems, IPIs are implemented using LPIs that are not
per-cpu interrupts - they are just normal routable IRQs.

Add arch code to set-up IPIs on systems where they are handled
using normal routable IRQs.

For those systems, force the IRQ affinity (and make it immutable)
to the cpu a given IRQ was assigned to.

Signed-off-by: Marc Zyngier <maz@kernel.org>
[timothy.hayes@arm.com: fixed ipi/irq conversion, irq flags]
Signed-off-by: Timothy Hayes <timothy.hayes@arm.com>
[lpieralisi: changed affinity set-up, log]
Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Will Deacon <will@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
---
 arch/arm64/include/asm/smp.h |   7 ++-
 arch/arm64/kernel/smp.c      | 139 ++++++++++++++++++++++++++++++++-----------
 2 files changed, 111 insertions(+), 35 deletions(-)

diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h
index 2510eec026f7e3d6f0ecf1197c3a81b183ddd216..d6fd6efb66a673ae33825971e4aa07e791c02ee5 100644
--- a/arch/arm64/include/asm/smp.h
+++ b/arch/arm64/include/asm/smp.h
@@ -53,7 +53,12 @@ extern void smp_init_cpus(void);
 /*
  * Register IPI interrupts with the arch SMP code
  */
-extern void set_smp_ipi_range(int ipi_base, int nr_ipi);
+extern void set_smp_ipi_range_percpu(int ipi_base, int nr_ipi, int ncpus);
+
+static inline void set_smp_ipi_range(int ipi_base, int n)
+{
+	set_smp_ipi_range_percpu(ipi_base, n, 0);
+}
 
 /*
  * Called from the secondary holding pen, this is the secondary CPU entry point.
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 3b3f6b56e733039cad7ff5b8995db16a68f3c762..3f3712e47c94c62836fb89cd4bfb3595fbb41557 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -83,7 +83,26 @@ enum ipi_msg_type {
 
 static int ipi_irq_base __ro_after_init;
 static int nr_ipi __ro_after_init = NR_IPI;
-static struct irq_desc *ipi_desc[MAX_IPI] __ro_after_init;
+
+struct ipi_descs {
+	struct irq_desc *descs[MAX_IPI];
+};
+
+static DEFINE_PER_CPU(struct ipi_descs, pcpu_ipi_desc);
+
+#define get_ipi_desc(__cpu, __ipi) (per_cpu_ptr(&pcpu_ipi_desc, __cpu)->descs[__ipi])
+
+static bool percpu_ipi_descs __ro_after_init;
+
+static int ipi_to_irq(int ipi, int cpu)
+{
+	return ipi_irq_base + (cpu * nr_ipi) + ipi;
+}
+
+static int irq_to_ipi(int irq)
+{
+	return (irq - ipi_irq_base) % nr_ipi;
+}
 
 static bool crash_stop;
 
@@ -844,7 +863,7 @@ int arch_show_interrupts(struct seq_file *p, int prec)
 		seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i,
 			   prec >= 4 ? " " : "");
 		for_each_online_cpu(cpu)
-			seq_printf(p, "%10u ", irq_desc_kstat_cpu(ipi_desc[i], cpu));
+			seq_printf(p, "%10u ", irq_desc_kstat_cpu(get_ipi_desc(cpu, i), cpu));
 		seq_printf(p, "      %s\n", ipi_types[i]);
 	}
 
@@ -919,7 +938,13 @@ static void __noreturn ipi_cpu_crash_stop(unsigned int cpu, struct pt_regs *regs
 
 static void arm64_backtrace_ipi(cpumask_t *mask)
 {
-	__ipi_send_mask(ipi_desc[IPI_CPU_BACKTRACE], mask);
+	unsigned int cpu;
+
+	if (!percpu_ipi_descs)
+		__ipi_send_mask(get_ipi_desc(0, IPI_CPU_BACKTRACE), mask);
+	else
+		for_each_cpu(cpu, mask)
+			__ipi_send_single(get_ipi_desc(cpu, IPI_CPU_BACKTRACE), cpu);
 }
 
 void arch_trigger_cpumask_backtrace(const cpumask_t *mask, int exclude_cpu)
@@ -944,7 +969,7 @@ void kgdb_roundup_cpus(void)
 		if (cpu == this_cpu)
 			continue;
 
-		__ipi_send_single(ipi_desc[IPI_KGDB_ROUNDUP], cpu);
+		__ipi_send_single(get_ipi_desc(cpu, IPI_KGDB_ROUNDUP), cpu);
 	}
 }
 #endif
@@ -1013,14 +1038,21 @@ static void do_handle_IPI(int ipinr)
 
 static irqreturn_t ipi_handler(int irq, void *data)
 {
-	do_handle_IPI(irq - ipi_irq_base);
+	do_handle_IPI(irq_to_ipi(irq));
 	return IRQ_HANDLED;
 }
 
 static void smp_cross_call(const struct cpumask *target, unsigned int ipinr)
 {
+	unsigned int cpu;
+
 	trace_ipi_raise(target, ipi_types[ipinr]);
-	__ipi_send_mask(ipi_desc[ipinr], target);
+
+	if (!percpu_ipi_descs)
+		__ipi_send_mask(get_ipi_desc(0, ipinr), target);
+	else
+		for_each_cpu(cpu, target)
+			__ipi_send_single(get_ipi_desc(cpu, ipinr), cpu);
 }
 
 static bool ipi_should_be_nmi(enum ipi_msg_type ipi)
@@ -1046,11 +1078,15 @@ static void ipi_setup(int cpu)
 		return;
 
 	for (i = 0; i < nr_ipi; i++) {
-		if (ipi_should_be_nmi(i)) {
-			prepare_percpu_nmi(ipi_irq_base + i);
-			enable_percpu_nmi(ipi_irq_base + i, 0);
+		if (!percpu_ipi_descs) {
+			if (ipi_should_be_nmi(i)) {
+				prepare_percpu_nmi(ipi_irq_base + i);
+				enable_percpu_nmi(ipi_irq_base + i, 0);
+			} else {
+				enable_percpu_irq(ipi_irq_base + i, 0);
+			}
 		} else {
-			enable_percpu_irq(ipi_irq_base + i, 0);
+			enable_irq(irq_desc_get_irq(get_ipi_desc(cpu, i)));
 		}
 	}
 }
@@ -1064,44 +1100,79 @@ static void ipi_teardown(int cpu)
 		return;
 
 	for (i = 0; i < nr_ipi; i++) {
-		if (ipi_should_be_nmi(i)) {
-			disable_percpu_nmi(ipi_irq_base + i);
-			teardown_percpu_nmi(ipi_irq_base + i);
+		if (!percpu_ipi_descs) {
+			if (ipi_should_be_nmi(i)) {
+				disable_percpu_nmi(ipi_irq_base + i);
+				teardown_percpu_nmi(ipi_irq_base + i);
+			} else {
+				disable_percpu_irq(ipi_irq_base + i);
+			}
 		} else {
-			disable_percpu_irq(ipi_irq_base + i);
+			disable_irq(irq_desc_get_irq(get_ipi_desc(cpu, i)));
 		}
 	}
 }
 #endif
 
-void __init set_smp_ipi_range(int ipi_base, int n)
+static void ipi_setup_ppi(int ipi)
+{
+	int err, irq, cpu;
+
+	irq = ipi_irq_base + ipi;
+
+	if (ipi_should_be_nmi(irq)) {
+		err = request_percpu_nmi(irq, ipi_handler, "IPI", &irq_stat);
+		WARN(err, "Could not request IRQ %d as NMI, err=%d\n", irq, err);
+	} else {
+		err = request_percpu_irq(irq, ipi_handler, "IPI", &irq_stat);
+		WARN(err, "Could not request IRQ %d as IRQ, err=%d\n", irq, err);
+	}
+
+	for_each_possible_cpu(cpu)
+		get_ipi_desc(cpu, ipi) = irq_to_desc(irq);
+
+	irq_set_status_flags(irq, IRQ_HIDDEN);
+}
+
+static void ipi_setup_lpi(int ipi, int ncpus)
+{
+	for (int cpu = 0; cpu < ncpus; cpu++) {
+		int err, irq;
+
+		irq = ipi_to_irq(ipi, cpu);
+
+		err = irq_force_affinity(irq, cpumask_of(cpu));
+
+		WARN(err, "Could not force affinity IRQ %d, err=%d\n", irq, err);
+
+		err = request_irq(irq, ipi_handler, IRQF_NO_AUTOEN, "IPI",
+				  &irq_stat);
+
+		WARN(err, "Could not request IRQ %d, err=%d\n", irq, err);
+
+		irq_set_status_flags(irq, (IRQ_HIDDEN | IRQ_NO_BALANCING_MASK));
+
+		get_ipi_desc(cpu, ipi) = irq_to_desc(irq);
+	}
+}
+
+void __init set_smp_ipi_range_percpu(int ipi_base, int n, int ncpus)
 {
 	int i;
 
 	WARN_ON(n < MAX_IPI);
 	nr_ipi = min(n, MAX_IPI);
 
-	for (i = 0; i < nr_ipi; i++) {
-		int err;
-
-		if (ipi_should_be_nmi(i)) {
-			err = request_percpu_nmi(ipi_base + i, ipi_handler,
-						 "IPI", &irq_stat);
-			WARN(err, "Could not request IPI %d as NMI, err=%d\n",
-			     i, err);
-		} else {
-			err = request_percpu_irq(ipi_base + i, ipi_handler,
-						 "IPI", &irq_stat);
-			WARN(err, "Could not request IPI %d as IRQ, err=%d\n",
-			     i, err);
-		}
-
-		ipi_desc[i] = irq_to_desc(ipi_base + i);
-		irq_set_status_flags(ipi_base + i, IRQ_HIDDEN);
-	}
-
+	percpu_ipi_descs = !!ncpus;
 	ipi_irq_base = ipi_base;
 
+	for (i = 0; i < nr_ipi; i++) {
+		if (!percpu_ipi_descs)
+			ipi_setup_ppi(i);
+		else
+			ipi_setup_lpi(i, ncpus);
+	}
+
 	/* Setup the boot CPU immediately */
 	ipi_setup(smp_processor_id());
 }

-- 
2.48.0



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 18/24] irqchip/gic-v5: Add GICv5 PPI support
  2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (16 preceding siblings ...)
  2025-04-08 10:50 ` [PATCH 17/24] arm64: smp: Support non-SGIs for IPIs Lorenzo Pieralisi
@ 2025-04-08 10:50 ` Lorenzo Pieralisi
  2025-04-08 21:42   ` Thomas Gleixner
  2025-04-08 10:50 ` [PATCH 19/24] irqchip/gic-v5: Add GICv5 IRS/SPI support Lorenzo Pieralisi
                   ` (5 subsequent siblings)
  23 siblings, 1 reply; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:50 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

The GICv5 CPU interface implements support for PE-Private Peripheral
Interrupts (PPI), that are handled (enabled/prioritized/delivered)
entirely within the CPU interface hardware.

To enable PPI interrupts, implement the baseline GICv5 host kernel
driver infrastructure required to handle interrupts on a GICv5 system.

Add the exception handling code path and definitions for GICv5
instructions.

Add GICv5 PPI handling code as a specific IRQ domain to:

- Set-up PPI priority
- Manage PPI configuration and state
- Manage IRQ flow handler
- IRQs allocation/free
- Hook-up a PPI specific IRQchip to provide the relevant methods

PPI IRQ priority is chosen as the minimum allowed priority by the
system design (after probing the number of priority bits implemented
by the CPU interface).

Co-developed-by: Sascha Bischoff <sascha.bischoff@arm.com>
Signed-off-by: Sascha Bischoff <sascha.bischoff@arm.com>
Co-developed-by: Timothy Hayes <timothy.hayes@arm.com>
Signed-off-by: Timothy Hayes <timothy.hayes@arm.com>
Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Will Deacon <will@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
---
 MAINTAINERS                         |   2 +
 arch/arm64/include/asm/arch_gicv5.h |  38 +++
 drivers/irqchip/Kconfig             |   5 +
 drivers/irqchip/Makefile            |   1 +
 drivers/irqchip/irq-gic-v5.c        | 497 ++++++++++++++++++++++++++++++++++++
 drivers/irqchip/irq-gic-v5.h        |  19 ++
 6 files changed, 562 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index f3ed84466da19906953b5396a5f4b50e878c376e..cdeceb6782355a4a18609135bf7f03249d8b0bb5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1907,6 +1907,8 @@ M:	Marc Zyngier <maz@kernel.org>
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:	Maintained
 F:	Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.yaml
+F:	arch/arm64/include/asm/arch_gicv5.h
+F:	drivers/irqchip/irq-gic-v5*.[ch]
 
 ARM HDLCD DRM DRIVER
 M:	Liviu Dudau <liviu.dudau@arm.com>
diff --git a/arch/arm64/include/asm/arch_gicv5.h b/arch/arm64/include/asm/arch_gicv5.h
new file mode 100644
index 0000000000000000000000000000000000000000..e86cda5e5b3295c4f9c784d92adad1c6df6dbc34
--- /dev/null
+++ b/arch/arm64/include/asm/arch_gicv5.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2025 ARM Ltd.
+ */
+#ifndef __ASM_ARCH_GICV5_H
+#define __ASM_ARCH_GICV5_H
+
+#include <asm/sysreg.h>
+
+#ifndef __ASSEMBLY__
+
+#define GICV5_OP_GIC_CDDI		sys_insn(1, 0, 12, 2, 0)
+#define GICV5_OP_GIC_CDEOI		sys_insn(1, 0, 12, 1, 7)
+#define GICV5_OP_GICR_CDIA		sys_insn(1, 0, 12, 3, 0)
+
+#define gicr_insn(insn)			read_sysreg_s(insn)
+#define gic_insn(v, insn)		write_sysreg_s(v, insn)
+
+#define GSB_ACK				__emit_inst(0xd5000000 | sys_insn(1, 0, 12, 0, 1) | 31)
+
+#define gsb_ack()			asm volatile(GSB_ACK : : : "memory")
+
+/* Shift and mask definitions for GIC CDDI */
+#define GICV5_GIC_CDDI_TYPE_MASK	GENMASK_ULL(31, 29)
+#define GICV5_GIC_CDDI_TYPE(r)		FIELD_GET(GICV5_GIC_CDDI_TYPE_MASK, r)
+#define GICV5_GIC_CDDI_ID_MASK		GENMASK_ULL(23, 0)
+#define GICV5_GIC_CDDI_ID(r)		FIELD_GET(GICV5_GIC_CDDI_ID_MASK, r)
+
+/* Shift and mask definitions for GICR CDIA */
+#define GICV5_GIC_CDIA_VALID_MASK	BIT_ULL(32)
+#define GICV5_GIC_CDIA_VALID(r)		FIELD_GET(GICV5_GIC_CDIA_VALID_MASK, r)
+#define GICV5_GIC_CDIA_TYPE_MASK	GENMASK_ULL(31, 29)
+#define GICV5_GIC_CDIA_TYPE(r)		FIELD_GET(GICV5_GIC_CDIA_TYPE_MASK, r)
+#define GICV5_GIC_CDIA_ID_MASK		GENMASK_ULL(23, 0)
+#define GICV5_GIC_CDIA_ID(r)		FIELD_GET(GICV5_GIC_CDIA_ID_MASK, r)
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ASM_ARCH_GICV5_H */
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index cec05e443083b8982b3e72f4212d808a22883914..160a4761d5d85f6dbf36f3142fd619c114733e36 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -54,6 +54,11 @@ config ARM_GIC_V3_ITS_FSL_MC
 	depends on FSL_MC_BUS
 	default ARM_GIC_V3_ITS
 
+config ARM_GIC_V5
+	bool
+	select IRQ_DOMAIN_HIERARCHY
+	select GENERIC_IRQ_EFFECTIVE_AFF_MASK if SMP
+
 config ARM_NVIC
 	bool
 	select IRQ_DOMAIN_HIERARCHY
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 365bcea9a61ff89e2cb41034125b3fc8cd494d81..3f8225bba5f0f9ce5dbb629b6d4782eacf85da44 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_ARM_GIC_V3)		+= irq-gic-v3.o irq-gic-v3-mbi.o irq-gic-common.o
 obj-$(CONFIG_ARM_GIC_V3_ITS)		+= irq-gic-v3-its.o irq-gic-v4.o irq-gic-v3-its-msi-parent.o
 obj-$(CONFIG_ARM_GIC_V3_ITS_FSL_MC)	+= irq-gic-v3-its-fsl-mc-msi.o
 obj-$(CONFIG_PARTITION_PERCPU)		+= irq-partition-percpu.o
+obj-$(CONFIG_ARM_GIC_V5)		+= irq-gic-v5.o
 obj-$(CONFIG_HISILICON_IRQ_MBIGEN)	+= irq-mbigen.o
 obj-$(CONFIG_ARM_NVIC)			+= irq-nvic.o
 obj-$(CONFIG_ARM_VIC)			+= irq-vic.o
diff --git a/drivers/irqchip/irq-gic-v5.c b/drivers/irqchip/irq-gic-v5.c
new file mode 100644
index 0000000000000000000000000000000000000000..996e2c992ef33e5ec8d2680ad4026b725ca39b04
--- /dev/null
+++ b/drivers/irqchip/irq-gic-v5.c
@@ -0,0 +1,497 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024-2025 ARM Limited, All Rights Reserved.
+ */
+
+#define pr_fmt(fmt)	"GICv5: " fmt
+
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/wordpart.h>
+
+#include <asm/cpufeature.h>
+#include <asm/exception.h>
+
+#include "irq-gic-v5.h"
+
+static u8 pri_bits = 5;
+#define GICV5_IRQ_PRIORITY_MASK 0x1f
+#define GICV5_IRQ_PRIORITY_MI \
+		(GICV5_IRQ_PRIORITY_MASK & GENMASK(4, 5 - pri_bits))
+
+static bool gicv5_cpuif_has_gcie(void)
+{
+	return this_cpu_has_cap(ARM64_HAS_GCIE);
+}
+
+struct gicv5_chip_data {
+	struct fwnode_handle *fwnode;
+	struct irq_domain *ppi_domain;
+};
+
+static struct gicv5_chip_data gicv5_global_data __read_mostly;
+
+static void gicv5_ppi_priority_init(void)
+{
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRIORITY_MI),
+				 SYS_ICC_PPI_PRIORITYR0_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRIORITY_MI),
+				 SYS_ICC_PPI_PRIORITYR1_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRIORITY_MI),
+				 SYS_ICC_PPI_PRIORITYR2_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRIORITY_MI),
+				 SYS_ICC_PPI_PRIORITYR3_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRIORITY_MI),
+				 SYS_ICC_PPI_PRIORITYR4_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRIORITY_MI),
+				 SYS_ICC_PPI_PRIORITYR5_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRIORITY_MI),
+				 SYS_ICC_PPI_PRIORITYR6_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRIORITY_MI),
+				 SYS_ICC_PPI_PRIORITYR7_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRIORITY_MI),
+				 SYS_ICC_PPI_PRIORITYR8_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRIORITY_MI),
+				 SYS_ICC_PPI_PRIORITYR9_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRIORITY_MI),
+				 SYS_ICC_PPI_PRIORITYR10_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRIORITY_MI),
+				 SYS_ICC_PPI_PRIORITYR11_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRIORITY_MI),
+				 SYS_ICC_PPI_PRIORITYR12_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRIORITY_MI),
+				 SYS_ICC_PPI_PRIORITYR13_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRIORITY_MI),
+				 SYS_ICC_PPI_PRIORITYR14_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRIORITY_MI),
+				 SYS_ICC_PPI_PRIORITYR15_EL1);
+
+	/*
+	 * Context syncronization required to make sure system
+	 * register writes effects are synchronized
+	 */
+	isb();
+}
+
+static void gicv5_ppi_irq_mask(struct irq_data *d)
+{
+	u64 hwirq_id_bit = BIT_ULL(d->hwirq % 64);
+
+	if (d->hwirq < 64)
+		sysreg_clear_set_s(SYS_ICC_PPI_ENABLER0_EL1, hwirq_id_bit, 0);
+	else
+		sysreg_clear_set_s(SYS_ICC_PPI_ENABLER1_EL1, hwirq_id_bit, 0);
+
+	/*
+	 * Ensure that the disable takes effect
+	 */
+	isb();
+}
+
+static void gicv5_ppi_irq_unmask(struct irq_data *d)
+{
+	u64 hwirq_id_bit = BIT_ULL(d->hwirq % 64);
+
+	if (d->hwirq < 64)
+		sysreg_clear_set_s(SYS_ICC_PPI_ENABLER0_EL1, 0, hwirq_id_bit);
+	else
+		sysreg_clear_set_s(SYS_ICC_PPI_ENABLER1_EL1, 0, hwirq_id_bit);
+}
+
+static void gicv5_hwirq_eoi(u32 hwirq_id, u32 hwirq_type)
+{
+	u64 cddi = hwirq_id | FIELD_PREP(GICV5_GIC_CDDI_TYPE_MASK, hwirq_type);
+
+	gic_insn(cddi, GICV5_OP_GIC_CDDI);
+
+	gic_insn(0, GICV5_OP_GIC_CDEOI);
+}
+
+static void gicv5_ppi_irq_eoi(struct irq_data *d)
+{
+	gicv5_hwirq_eoi(d->hwirq, GICV5_HWIRQ_TYPE_PPI);
+}
+
+static int gicv5_ppi_set_type(struct irq_data *d, unsigned int type)
+{
+	/*
+	 * The PPI trigger mode is not configurable at runtime,
+	 * therefore this function simply confirms that the `type`
+	 * parameter matches what is present.
+	 */
+	u64 hmr;
+
+	if (d->hwirq < 64)
+		hmr = read_sysreg_s(SYS_ICC_PPI_HMR0_EL1);
+	else
+		hmr = read_sysreg_s(SYS_ICC_PPI_HMR1_EL1);
+
+	switch (type) {
+	case IRQ_TYPE_LEVEL_HIGH:
+	case IRQ_TYPE_LEVEL_LOW:
+		if (((hmr >> (d->hwirq % 64)) & 0x1) != GICV5_PPI_HM_LEVEL)
+			return -EINVAL;
+		break;
+	case IRQ_TYPE_EDGE_RISING:
+	case IRQ_TYPE_EDGE_FALLING:
+		if (((hmr >> (d->hwirq % 64)) & 0x1) != GICV5_PPI_HM_EDGE)
+			return -EINVAL;
+		break;
+	default:
+		pr_debug("Unexpected PPI trigger mode");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int gicv5_ppi_irq_get_irqchip_state(struct irq_data *d,
+					   enum irqchip_irq_state which,
+					   bool *val)
+{
+	u64 pendr, activer, enabler, hwirq_id_bit = BIT_ULL(d->hwirq % 64);
+
+	switch (which) {
+	case IRQCHIP_STATE_PENDING:
+		if (d->hwirq < 64)
+			pendr = read_sysreg_s(SYS_ICC_PPI_SPENDR0_EL1);
+		else
+			pendr = read_sysreg_s(SYS_ICC_PPI_SPENDR1_EL1);
+
+		*val = !!(pendr & hwirq_id_bit);
+
+		return 0;
+	case IRQCHIP_STATE_ACTIVE:
+		if (d->hwirq < 64)
+			activer = read_sysreg_s(SYS_ICC_PPI_SACTIVER0_EL1);
+		else
+			activer = read_sysreg_s(SYS_ICC_PPI_SACTIVER1_EL1);
+
+		*val = !!(activer & hwirq_id_bit);
+
+		return 0;
+	case IRQCHIP_STATE_MASKED:
+		if (d->hwirq < 64)
+			enabler = read_sysreg_s(SYS_ICC_PPI_ENABLER0_EL1);
+		else
+			enabler = read_sysreg_s(SYS_ICC_PPI_ENABLER1_EL1);
+
+		*val = !(enabler & hwirq_id_bit);
+
+		return 0;
+	default:
+		pr_debug("Unexpected PPI irqchip state\n");
+		return -EINVAL;
+	}
+
+	return -EINVAL;
+}
+
+static int gicv5_ppi_irq_set_irqchip_state(struct irq_data *d,
+					   enum irqchip_irq_state which,
+					   bool val)
+{
+	u64 hwirq_id_bit = BIT_ULL(d->hwirq % 64);
+
+	switch (which) {
+	case IRQCHIP_STATE_PENDING:
+		if (val) {
+			if (d->hwirq < 64)
+				write_sysreg_s(hwirq_id_bit,
+					       SYS_ICC_PPI_SPENDR0_EL1);
+			else
+				write_sysreg_s(hwirq_id_bit,
+					       SYS_ICC_PPI_SPENDR1_EL1);
+
+		} else {
+			if (d->hwirq < 64)
+				write_sysreg_s(hwirq_id_bit,
+					       SYS_ICC_PPI_CPENDR0_EL1);
+			else
+				write_sysreg_s(hwirq_id_bit,
+					       SYS_ICC_PPI_CPENDR1_EL1);
+		}
+
+		return 0;
+	case IRQCHIP_STATE_ACTIVE:
+		if (val) {
+			if (d->hwirq < 64)
+				write_sysreg_s(hwirq_id_bit,
+					       SYS_ICC_PPI_SACTIVER0_EL1);
+			else
+				write_sysreg_s(hwirq_id_bit,
+					       SYS_ICC_PPI_SACTIVER1_EL1);
+		} else {
+			if (d->hwirq < 64)
+				write_sysreg_s(hwirq_id_bit,
+					       SYS_ICC_PPI_CACTIVER0_EL1);
+			else
+				write_sysreg_s(hwirq_id_bit,
+					       SYS_ICC_PPI_CACTIVER1_EL1);
+		}
+
+		return 0;
+	case IRQCHIP_STATE_MASKED:
+		if (val)
+			gicv5_ppi_irq_mask(d);
+		else
+			gicv5_ppi_irq_unmask(d);
+		return 0;
+	default:
+		pr_debug("Unexpected PPI irqchip state\n");
+		return -EINVAL;
+	}
+
+	return -EINVAL;
+}
+
+static const struct irq_chip gicv5_ppi_irq_chip = {
+	.name			= "GICv5-PPI",
+	.irq_mask		= gicv5_ppi_irq_mask,
+	.irq_unmask		= gicv5_ppi_irq_unmask,
+	.irq_eoi		= gicv5_ppi_irq_eoi,
+	.irq_set_type		= gicv5_ppi_set_type,
+	.irq_get_irqchip_state	= gicv5_ppi_irq_get_irqchip_state,
+	.irq_set_irqchip_state	= gicv5_ppi_irq_set_irqchip_state,
+	.flags			= IRQCHIP_SET_TYPE_MASKED |
+				  IRQCHIP_SKIP_SET_WAKE	  |
+				  IRQCHIP_MASK_ON_SUSPEND
+};
+
+static int gicv5_irq_ppi_domain_translate(struct irq_domain *d,
+					  struct irq_fwspec *fwspec,
+					  irq_hw_number_t *hwirq,
+					  unsigned int *type)
+{
+	if (is_of_node(fwspec->fwnode)) {
+		if (fwspec->param_count < 3)
+			return -EINVAL;
+
+		if (fwspec->param[0] != GICV5_HWIRQ_TYPE_PPI)
+			return -EINVAL;
+
+		*hwirq = fwspec->param[1];
+		*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int gicv5_irq_ppi_domain_alloc(struct irq_domain *domain,
+				      unsigned int virq, unsigned int nr_irqs,
+				      void *arg)
+{
+	unsigned int type = IRQ_TYPE_NONE;
+	struct irq_fwspec *fwspec = arg;
+	irq_hw_number_t hwirq;
+	int ret;
+
+	if (WARN_ON(nr_irqs != 1))
+		return -EINVAL;
+
+	ret = gicv5_irq_ppi_domain_translate(domain, fwspec, &hwirq, &type);
+	if (ret)
+		return ret;
+
+	irq_set_percpu_devid(virq);
+	irq_domain_set_info(domain, virq, hwirq, &gicv5_ppi_irq_chip, NULL,
+			    handle_percpu_devid_irq, NULL, NULL);
+
+	return 0;
+}
+
+static void gicv5_irq_ppi_domain_free(struct irq_domain *domain,
+				      unsigned int virq, unsigned int nr_irqs)
+{
+	struct irq_data *d;
+
+	if (WARN_ON(nr_irqs != 1))
+		return;
+
+	d = irq_domain_get_irq_data(domain, virq);
+
+	irq_set_handler(virq, NULL);
+	irq_domain_reset_irq_data(d);
+}
+
+static int gicv5_irq_ppi_domain_select(struct irq_domain *d,
+				       struct irq_fwspec *fwspec,
+				       enum irq_domain_bus_token bus_token)
+{
+	/* Not for us */
+	if (fwspec->fwnode != d->fwnode)
+		return 0;
+
+	if (fwspec->param[0] != GICV5_HWIRQ_TYPE_PPI) {
+		// only handle PPIs
+		return 0;
+	}
+
+	return (d == gicv5_global_data.ppi_domain);
+}
+
+static const struct irq_domain_ops gicv5_irq_ppi_domain_ops = {
+	.translate	= gicv5_irq_ppi_domain_translate,
+	.alloc		= gicv5_irq_ppi_domain_alloc,
+	.free		= gicv5_irq_ppi_domain_free,
+	.select		= gicv5_irq_ppi_domain_select
+};
+
+static inline void handle_irq_per_domain(u32 hwirq)
+{
+	u32 hwirq_id;
+	struct irq_domain *domain = NULL;
+	u8 hwirq_type = FIELD_GET(GICV5_HWIRQ_TYPE, hwirq);
+
+	hwirq_id = FIELD_GET(GICV5_HWIRQ_ID, hwirq);
+
+	if (hwirq_type == GICV5_HWIRQ_TYPE_PPI)
+		domain = gicv5_global_data.ppi_domain;
+
+	if (generic_handle_domain_irq(domain, hwirq_id)) {
+		pr_err("Could not handle, hwirq = 0x%x", hwirq_id);
+		gicv5_hwirq_eoi(hwirq_id, hwirq_type);
+	}
+}
+
+static asmlinkage void __exception_irq_entry
+gicv5_handle_irq(struct pt_regs *regs)
+{
+	u64 ia;
+	bool valid;
+	u32 hwirq;
+
+	ia = gicr_insn(GICV5_OP_GICR_CDIA);
+	valid = GICV5_GIC_CDIA_VALID(ia);
+
+	if (!valid)
+		return;
+
+	/*
+	 * Ensure that the CDIA instruction effects (ie IRQ activation) are
+	 * completed before handling the interrupt.
+	 */
+	gsb_ack();
+
+	/*
+	 * Ensure instruction ordering between an acknowledgment and subsequent
+	 * instructions in the IRQ handler using an ISB.
+	 */
+	isb();
+
+	hwirq = FIELD_GET(GICV5_HWIRQ_INTID, ia);
+
+	handle_irq_per_domain(hwirq);
+}
+
+/*
+ * Disable IRQs for the executing CPU
+ */
+static void gicv5_cpu_disable_interrupts(void)
+{
+	u64 cr0;
+
+	// Disable interrupts for the Interrupt Domain
+	cr0 = FIELD_PREP(ICC_CR0_EL1_EN, 0);
+	write_sysreg_s(cr0, SYS_ICC_CR0_EL1);
+}
+
+/*
+ * Enable IRQs for the executing CPU
+ */
+static void gicv5_cpu_enable_interrupts(void)
+{
+	u64 cr0, pcr;
+
+	write_sysreg_s(0, SYS_ICC_PPI_ENABLER0_EL1);
+	write_sysreg_s(0, SYS_ICC_PPI_ENABLER1_EL1);
+
+	gicv5_ppi_priority_init();
+
+	// Explicitly set the physical interrupt priority of the CPU
+	pcr = FIELD_PREP(ICC_PCR_EL1_PRIORITY, GICV5_IRQ_PRIORITY_MI);
+	write_sysreg_s(pcr, SYS_ICC_PCR_EL1);
+
+	// Enable interrupts for the Interrupt Domain
+	cr0 = FIELD_PREP(ICC_CR0_EL1_EN, 1);
+	write_sysreg_s(cr0, SYS_ICC_CR0_EL1);
+}
+
+static int gicv5_starting_cpu(unsigned int cpu)
+{
+	if (WARN(!gicv5_cpuif_has_gcie(),
+	    "GICv5 system components present but CPU does not have FEAT_GCIE"))
+		return -ENODEV;
+
+	gicv5_cpu_enable_interrupts();
+
+	return 0;
+}
+
+static void __init gicv5_free_domains(void)
+{
+	if (gicv5_global_data.ppi_domain)
+		irq_domain_remove(gicv5_global_data.ppi_domain);
+}
+
+static int __init gicv5_init_domains(struct fwnode_handle *handle)
+{
+	gicv5_global_data.fwnode = handle;
+	gicv5_global_data.ppi_domain = irq_domain_create_linear(
+		handle, 128, &gicv5_irq_ppi_domain_ops, NULL);
+
+	if (WARN_ON(!gicv5_global_data.ppi_domain))
+		return -ENOMEM;
+	irq_domain_update_bus_token(gicv5_global_data.ppi_domain,
+				    DOMAIN_BUS_WIRED);
+
+	return 0;
+}
+
+static void gicv5_set_cpuif_pribits(void)
+{
+	u64 icc_idr0 = read_sysreg_s(SYS_ICC_IDR0_EL1);
+
+	switch (FIELD_GET(ICC_IDR0_EL1_PRI_BITS, icc_idr0)) {
+	case ICC_IDR0_EL1_PRI_BITS_4BITS:
+		pri_bits = 4;
+		break;
+	case ICC_IDR0_EL1_PRI_BITS_5BITS:
+		pri_bits = 5;
+		break;
+	default:
+		pr_err("Unexpected ICC_IDR0_EL1_PRI_BITS value, default to 4");
+		pri_bits = 4;
+		break;
+	}
+}
+
+static int __init gicv5_of_init(struct device_node *node,
+				struct device_node *parent)
+{
+	int ret;
+
+	ret = gicv5_init_domains(&node->fwnode);
+	if (ret)
+		return ret;
+
+	gicv5_set_cpuif_pribits();
+
+	ret = gicv5_starting_cpu(smp_processor_id());
+	if (ret) {
+		gicv5_free_domains();
+		return ret;
+	}
+
+	ret = set_handle_irq(gicv5_handle_irq);
+	if (ret) {
+		gicv5_free_domains();
+		gicv5_cpu_disable_interrupts();
+		return ret;
+	}
+
+	return 0;
+}
+IRQCHIP_DECLARE(gic_v5, "arm,gic-v5", gicv5_of_init);
diff --git a/drivers/irqchip/irq-gic-v5.h b/drivers/irqchip/irq-gic-v5.h
new file mode 100644
index 0000000000000000000000000000000000000000..d8b797cdea2f786646fd88d9c8f60d483380991c
--- /dev/null
+++ b/drivers/irqchip/irq-gic-v5.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2025 ARM Limited, All Rights Reserved.
+ */
+#ifndef __LINUX_IRQCHIP_GIC_V5_H
+#define __LINUX_IRQCHIP_GIC_V5_H
+
+#include <asm/arch_gicv5.h>
+
+#define GICV5_HWIRQ_ID			GENMASK(23, 0)
+#define GICV5_HWIRQ_TYPE		GENMASK(31, 29)
+#define GICV5_HWIRQ_INTID		GENMASK_ULL(31, 0)
+
+#define GICV5_HWIRQ_TYPE_PPI		UL(0x1)
+
+#define GICV5_PPI_HM_EDGE		UL(0x0)
+#define GICV5_PPI_HM_LEVEL		UL(0x1)
+
+#endif

-- 
2.48.0



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 19/24] irqchip/gic-v5: Add GICv5 IRS/SPI support
  2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (17 preceding siblings ...)
  2025-04-08 10:50 ` [PATCH 18/24] irqchip/gic-v5: Add GICv5 PPI support Lorenzo Pieralisi
@ 2025-04-08 10:50 ` Lorenzo Pieralisi
  2025-04-09  7:02   ` Thomas Gleixner
  2025-04-08 10:50 ` [PATCH 20/24] irqchip/gic-v5: Add GICv5 LPI/IPI support Lorenzo Pieralisi
                   ` (4 subsequent siblings)
  23 siblings, 1 reply; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:50 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

The GICv5 Interrupt Routing Service (IRS) component implements
interrupt management and routing in the GICv5 architecture.

A GICv5 system comprises one or more IRSes, that together
handle the interrupt routing and state for the system.

An IRS supports Shared Peripheral Interrupts (SPIs), that are
interrupt sources directly connected to the IRS; they do not
rely on memory for storage. The number of supported SPIs is
fixed for a given implementation and can be probed through IRS
IDR registers.

SPI interrupt state and routing are managed through GICv5
instructions.

Each core (PE in GICv5 terms) in a GICv5 system is identified with
an Interrupt AFFinity ID (IAFFID).

An IRS manages a set of cores that are connected to it.

Firmware provides a topology description that the driver uses
to detect to which IRS a CPU (ie an IAFFID) is associated with.

Use probeable information and firmware description to initialize
the IRSes and implement GICv5 IRS SPIs support through an
SPI-specific IRQ domain.

The GICv5 IRS driver:

- Probes IRSes in the system to detect SPI ranges
- Associates an IRS with a set of cores connected to it
- Adds an IRQchip structure for SPI handling

SPIs priority is set to a value corresponding to the lowest
permissible priority in the system (taking into account the
implemented priority bits of the IRS and CPU interface).

Since all IRQs are set to the same priority value, the value
itself does not matter as long as it is a valid one.

Co-developed-by: Sascha Bischoff <sascha.bischoff@arm.com>
Signed-off-by: Sascha Bischoff <sascha.bischoff@arm.com>
Co-developed-by: Timothy Hayes <timothy.hayes@arm.com>
Signed-off-by: Timothy Hayes <timothy.hayes@arm.com>
Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Will Deacon <will@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/include/asm/arch_gicv5.h |  52 +++++
 drivers/irqchip/Makefile            |   2 +-
 drivers/irqchip/irq-gic-v5-irs.c    | 452 ++++++++++++++++++++++++++++++++++++
 drivers/irqchip/irq-gic-v5.c        | 326 ++++++++++++++++++++++++--
 drivers/irqchip/irq-gic-v5.h        | 101 ++++++++
 5 files changed, 917 insertions(+), 16 deletions(-)

diff --git a/arch/arm64/include/asm/arch_gicv5.h b/arch/arm64/include/asm/arch_gicv5.h
index e86cda5e5b3295c4f9c784d92adad1c6df6dbc34..00c6b7903094ca5d6e496890d029d21d82b7af66 100644
--- a/arch/arm64/include/asm/arch_gicv5.h
+++ b/arch/arm64/include/asm/arch_gicv5.h
@@ -9,16 +9,34 @@
 
 #ifndef __ASSEMBLY__
 
+#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_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)
+#define GICV5_OP_GIC_CDPRI		sys_insn(1, 0, 12, 1, 2)
+#define GICV5_OP_GIC_CDRCFG		sys_insn(1, 0, 12, 1, 5)
 #define GICV5_OP_GICR_CDIA		sys_insn(1, 0, 12, 3, 0)
 
 #define gicr_insn(insn)			read_sysreg_s(insn)
 #define gic_insn(v, insn)		write_sysreg_s(v, insn)
 
 #define GSB_ACK				__emit_inst(0xd5000000 | sys_insn(1, 0, 12, 0, 1) | 31)
+#define GSB_SYS				__emit_inst(0xd5000000 | sys_insn(1, 0, 12, 0, 0) | 31)
 
 #define gsb_ack()			asm volatile(GSB_ACK : : : "memory")
+#define gsb_sys()			asm volatile(GSB_SYS : : : "memory")
+
+/* Shift and mask definitions for GIC CDAFF */
+#define GICV5_GIC_CDAFF_IAFFID_MASK	GENMASK_ULL(47, 32)
+#define GICV5_GIC_CDAFF_IAFFID(r)	FIELD_GET(GICV5_GIC_CDAFF_IAFFID_MASK, r)
+#define GICV5_GIC_CDAFF_TYPE_MASK	GENMASK_ULL(31, 29)
+#define GICV5_GIC_CDAFF_TYPE(r)		FIELD_GET(GICV5_GIC_CDAFF_TYPE_MASK, r)
+#define GICV5_GIC_CDAFF_IRM_MASK	BIT_ULL(28)
+#define GICV5_GIC_CDAFF_IRM(r)		FIELD_GET(GICV5_GIC_CDAFF_IRM_MASK, r)
+#define GICV5_GIC_CDAFF_ID_MASK		GENMASK_ULL(23, 0)
+#define GICV5_GIC_CDAFF_ID(r)		FIELD_GET(GICV5_GIC_CDAFF_ID_MASK, r)
 
 /* Shift and mask definitions for GIC CDDI */
 #define GICV5_GIC_CDDI_TYPE_MASK	GENMASK_ULL(31, 29)
@@ -26,6 +44,40 @@
 #define GICV5_GIC_CDDI_ID_MASK		GENMASK_ULL(23, 0)
 #define GICV5_GIC_CDDI_ID(r)		FIELD_GET(GICV5_GIC_CDDI_ID_MASK, r)
 
+/* Shift and mask definitions for GIC CDDIS */
+#define GICV5_GIC_CDDIS_TYPE_MASK	GENMASK_ULL(31, 29)
+#define GICV5_GIC_CDDIS_TYPE(r)		FIELD_GET(GICV5_GIC_CDDIS_TYPE_MASK, r)
+#define GICV5_GIC_CDDIS_ID_MASK		GENMASK_ULL(23, 0)
+#define GICV5_GIC_CDDIS_ID(r)		FIELD_GET(GICV5_GIC_CDDIS_ID_MASK, r)
+
+/* Shift and mask definitions for GIC CDEN */
+#define GICV5_GIC_CDEN_TYPE_MASK	GENMASK_ULL(31, 29)
+#define GICV5_GIC_CDEN_TYPE(r)		FIELD_GET(GICV5_GIC_CDEN_TYPE_MASK, r)
+#define GICV5_GIC_CDEN_ID_MASK		GENMASK_ULL(23, 0)
+#define GICV5_GIC_CDEN_ID(r)		FIELD_GET(GICV5_GIC_CDEN_ID_MASK, r)
+
+/* Shift and mask definitions for GIC CDPEND */
+#define GICV5_GIC_CDPEND_PENDING_MASK	BIT_ULL(32)
+#define GICV5_GIC_CDPEND_PENDING(r)	FIELD_GET(GICV5_GIC_CDPEND_PENDING_MASK, r)
+#define GICV5_GIC_CDPEND_TYPE_MASK	GENMASK_ULL(31, 29)
+#define GICV5_GIC_CDPEND_TYPE(r)	FIELD_GET(GICV5_GIC_CDPEND_TYPE_MASK, r)
+#define GICV5_GIC_CDPEND_ID_MASK	GENMASK_ULL(23, 0)
+#define GICV5_GIC_CDPEND_ID(r)		FIELD_GET(GICV5_GIC_CDPEND_ID_MASK, r)
+
+/* Shift and mask definitions for GIC CDPRI */
+#define GICV5_GIC_CDPRI_PRIORITY_MASK	GENMASK_ULL(39, 35)
+#define GICV5_GIC_CDPRI_PRIORITY(r)	FIELD_GET(GICV5_GIC_CDPRI_PRIORITY_MASK, r)
+#define GICV5_GIC_CDPRI_TYPE_MASK	GENMASK_ULL(31, 29)
+#define GICV5_GIC_CDPRI_TYPE(r)		FIELD_GET(GICV5_GIC_CDPRI_TYPE_MASK, r)
+#define GICV5_GIC_CDPRI_ID_MASK		GENMASK_ULL(23, 0)
+#define GICV5_GIC_CDPRI_ID(r)		FIELD_GET(GICV5_GIC_CDPRI_ID_MASK, r)
+
+/* Shift and mask definitions for GIC CDRCFG */
+#define GICV5_GIC_CDRCFG_TYPE_MASK	GENMASK_ULL(31, 29)
+#define GICV5_GIC_CDRCFG_TYPE(r)	FIELD_GET(GICV5_GIC_CDRCFG_TYPE_MASK, r)
+#define GICV5_GIC_CDRCFG_ID_MASK	GENMASK_ULL(23, 0)
+#define GICV5_GIC_CDRCFG_ID(r)		FIELD_GET(GICV5_GIC_CDRCFG_ID_MASK, r)
+
 /* Shift and mask definitions for GICR CDIA */
 #define GICV5_GIC_CDIA_VALID_MASK	BIT_ULL(32)
 #define GICV5_GIC_CDIA_VALID(r)		FIELD_GET(GICV5_GIC_CDIA_VALID_MASK, r)
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 3f8225bba5f0f9ce5dbb629b6d4782eacf85da44..3d9c47fa3fdf40b7452c059d84fe8ac24c91bc0f 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -35,7 +35,7 @@ obj-$(CONFIG_ARM_GIC_V3)		+= irq-gic-v3.o irq-gic-v3-mbi.o irq-gic-common.o
 obj-$(CONFIG_ARM_GIC_V3_ITS)		+= irq-gic-v3-its.o irq-gic-v4.o irq-gic-v3-its-msi-parent.o
 obj-$(CONFIG_ARM_GIC_V3_ITS_FSL_MC)	+= irq-gic-v3-its-fsl-mc-msi.o
 obj-$(CONFIG_PARTITION_PERCPU)		+= irq-partition-percpu.o
-obj-$(CONFIG_ARM_GIC_V5)		+= irq-gic-v5.o
+obj-$(CONFIG_ARM_GIC_V5)		+= irq-gic-v5.o irq-gic-v5-irs.o
 obj-$(CONFIG_HISILICON_IRQ_MBIGEN)	+= irq-mbigen.o
 obj-$(CONFIG_ARM_NVIC)			+= irq-nvic.o
 obj-$(CONFIG_ARM_VIC)			+= irq-vic.o
diff --git a/drivers/irqchip/irq-gic-v5-irs.c b/drivers/irqchip/irq-gic-v5-irs.c
new file mode 100644
index 0000000000000000000000000000000000000000..5999f2a2c8dac0ea01ab0aa5df65bf12e1f59f63
--- /dev/null
+++ b/drivers/irqchip/irq-gic-v5-irs.c
@@ -0,0 +1,452 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024-2025 ARM Limited, All Rights Reserved.
+ */
+
+#define pr_fmt(fmt)	"GICv5 IRS: " fmt
+
+#include <linux/iopoll.h>
+#include <linux/irqchip.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include "irq-gic-v5.h"
+
+#define IRS_FLAGS_NON_COHERENT		BIT(0)
+
+static DEFINE_PER_CPU(struct gicv5_irs_chip_data *, per_cpu_irs_data);
+static LIST_HEAD(irs_nodes);
+
+static u32 irs_readl(struct gicv5_irs_chip_data *irs_data, const u64 reg_offset)
+{
+	return readl_relaxed(irs_data->irs_base + reg_offset);
+}
+
+static void irs_writel(struct gicv5_irs_chip_data *irs_data, const u32 val,
+		       const u64 reg_offset)
+{
+	writel_relaxed(val, irs_data->irs_base + reg_offset);
+}
+
+struct iaffid_entry {
+	u16 iaffid;
+	bool valid;
+};
+
+static DEFINE_PER_CPU(struct iaffid_entry, cpu_iaffid);
+
+int gicv5_irs_cpu_to_iaffid(int cpuid, u16 *iaffid)
+{
+	if (!per_cpu(cpu_iaffid, cpuid).valid) {
+		pr_err("IAFFID for CPU %d has not been initialised\n", cpuid);
+		return -ENODEV;
+	}
+
+	*iaffid = per_cpu(cpu_iaffid, cpuid).iaffid;
+
+	return 0;
+}
+
+struct gicv5_irs_chip_data *gicv5_irs_lookup_by_spi_id(u32 spi_id)
+{
+	struct gicv5_irs_chip_data *irs_data;
+	u32 min, max;
+
+	list_for_each_entry(irs_data, &irs_nodes, entry) {
+		if (!irs_data->spi_range)
+			continue;
+
+		min = irs_data->spi_min;
+		max = irs_data->spi_min + irs_data->spi_range - 1;
+		if (spi_id >= min && spi_id <= max)
+			return irs_data;
+	}
+
+	return NULL;
+}
+
+static int gicv5_irs_wait_for_spi_op(struct gicv5_irs_chip_data *irs_data)
+{
+	int ret;
+	u32 statusr;
+
+	ret = readl_relaxed_poll_timeout_atomic(
+			irs_data->irs_base + GICV5_IRS_SPI_STATUSR, statusr,
+			FIELD_GET(GICV5_IRS_SPI_STATUSR_IDLE, statusr), 1,
+			USEC_PER_SEC);
+
+	if (ret == -ETIMEDOUT) {
+		pr_err_ratelimited("Time out waiting for IRS SPI to be configured\n");
+		return ret;
+	}
+
+	return !!FIELD_GET(GICV5_IRS_SPI_STATUSR_V, statusr) ? 0 : -ENXIO;
+}
+
+static int gicv5_irs_wait_for_irs_pe(struct gicv5_irs_chip_data *irs_data,
+				     bool selr)
+{
+	u32 statusr;
+	int ret;
+	bool valid;
+
+	ret = readl_relaxed_poll_timeout_atomic(
+			irs_data->irs_base + GICV5_IRS_PE_STATUSR, statusr,
+			FIELD_GET(GICV5_IRS_PE_STATUSR_IDLE, statusr), 1,
+			USEC_PER_SEC);
+
+	if (ret == -ETIMEDOUT) {
+		pr_err_ratelimited("Time out waiting for PE_STATUSR after %s\n",
+				   selr ? "IRS_PE_SELR" : "IRS_PE_CR0");
+		return ret;
+	}
+
+	if (selr) {
+		valid = !!FIELD_GET(GICV5_IRS_PE_STATUSR_V, statusr);
+		return valid ? 0 : -ENXIO;
+	}
+
+	return 0;
+}
+
+static int gicv5_irs_wait_for_pe_selr(struct gicv5_irs_chip_data *irs_data)
+{
+	return gicv5_irs_wait_for_irs_pe(irs_data, true);
+}
+
+static int gicv5_irs_wait_for_pe_cr0(struct gicv5_irs_chip_data *irs_data)
+{
+	return gicv5_irs_wait_for_irs_pe(irs_data, false);
+}
+
+int gicv5_spi_irq_set_type(struct irq_data *d, unsigned int type)
+{
+	struct gicv5_irs_chip_data *irs_data = d->chip_data;
+	u32 selr, cfgr;
+	bool level;
+
+	switch (type) {
+	case IRQ_TYPE_EDGE_RISING:
+	case IRQ_TYPE_EDGE_FALLING:
+		level = false;
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+	case IRQ_TYPE_LEVEL_LOW:
+		level = true;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	guard(raw_spinlock)(&irs_data->spi_config_lock);
+
+	selr = FIELD_PREP(GICV5_IRS_SPI_SELR_ID, d->hwirq);
+	irs_writel(irs_data, selr, GICV5_IRS_SPI_SELR);
+	if (gicv5_irs_wait_for_spi_op(irs_data))
+		return -EIO;
+
+	cfgr = FIELD_PREP(GICV5_IRS_SPI_CFGR_TM, level);
+
+	irs_writel(irs_data, cfgr, GICV5_IRS_SPI_CFGR);
+	if (gicv5_irs_wait_for_spi_op(irs_data))
+		return -EPERM;
+
+	return 0;
+}
+
+static int gicv5_irs_wait_for_idle(struct gicv5_irs_chip_data *irs_data)
+{
+	u32 cr0;
+	int ret;
+
+	ret = readl_relaxed_poll_timeout_atomic(
+			irs_data->irs_base + GICV5_IRS_CR0, cr0,
+			FIELD_GET(GICV5_IRS_CR0_IDLE, cr0), 1,
+			USEC_PER_SEC);
+
+	if (ret == -ETIMEDOUT)
+		pr_err_ratelimited("CR0 timeout...\n");
+
+	return ret;
+}
+
+int gicv5_irs_register_cpu(int cpuid)
+{
+	struct gicv5_irs_chip_data *irs_data;
+	u32 selr, cr0;
+	u16 iaffid;
+	int ret;
+
+	ret = gicv5_irs_cpu_to_iaffid(cpuid, &iaffid);
+	if (ret) {
+		pr_err("IAFFID for CPU %d has not been initialised\n", cpuid);
+		return ret;
+	}
+
+	irs_data = per_cpu(per_cpu_irs_data, cpuid);
+	if (!irs_data) {
+		pr_err("No IRS associated with CPU %u\n", cpuid);
+		return -ENXIO;
+	}
+
+	selr = FIELD_PREP(GICV5_IRS_PE_SELR_IAFFID, iaffid);
+	irs_writel(irs_data, selr, GICV5_IRS_PE_SELR);
+
+	ret = gicv5_irs_wait_for_pe_selr(irs_data);
+	if (ret) {
+		pr_err("IAFFID 0x%x used in IRS_PE_SELR is invalid\n", iaffid);
+		return -ENXIO;
+	}
+
+	cr0 = FIELD_PREP(GICV5_IRS_PE_CR0_DPS, 0x1);
+	irs_writel(irs_data, cr0, GICV5_IRS_PE_CR0);
+
+	ret = gicv5_irs_wait_for_pe_cr0(irs_data);
+	if (ret)
+		return ret;
+
+	pr_debug("CPU%d enabled PE IAFFID 0x%x\n", cpuid, iaffid);
+
+	return 0;
+}
+
+static int __init gicv5_irs_init_bases(struct gicv5_irs_chip_data *irs_data,
+				       void __iomem *irs_base,
+				       struct fwnode_handle *handle)
+{
+	u32 cr0, cr1;
+	struct device_node *np = to_of_node(handle);
+
+	irs_data->fwnode = handle;
+	irs_data->irs_base = irs_base;
+
+	if (of_property_read_bool(np, "dma-noncoherent")) {
+		/*
+		 * A non-coherent IRS implies that some cache levels cannot be
+		 * used coherently by the cores and GIC. Our only option is to mark
+		 * memory attributes for the GIC as non-cacheable; by default,
+		 * non-cacheable memory attributes imply outer-shareable
+		 * shareability, the value written into IRS_CR1_SH is ignored.
+		 */
+		cr1 = FIELD_PREP(GICV5_IRS_CR1_VPED_WA, GICV5_NO_WRITE_ALLOC) |
+			FIELD_PREP(GICV5_IRS_CR1_VPED_RA, GICV5_NO_READ_ALLOC) |
+			FIELD_PREP(GICV5_IRS_CR1_VMD_WA, GICV5_NO_WRITE_ALLOC) |
+			FIELD_PREP(GICV5_IRS_CR1_VMD_RA, GICV5_NO_READ_ALLOC) |
+			FIELD_PREP(GICV5_IRS_CR1_VPET_RA, GICV5_NO_READ_ALLOC) |
+			FIELD_PREP(GICV5_IRS_CR1_VMT_RA, GICV5_NO_READ_ALLOC) |
+			FIELD_PREP(GICV5_IRS_CR1_IST_WA, GICV5_NO_WRITE_ALLOC) |
+			FIELD_PREP(GICV5_IRS_CR1_IST_RA, GICV5_NO_READ_ALLOC) |
+			FIELD_PREP(GICV5_IRS_CR1_IC, GICV5_NON_CACHE) |
+			FIELD_PREP(GICV5_IRS_CR1_OC, GICV5_NON_CACHE);
+			irs_data->flags |= IRS_FLAGS_NON_COHERENT;
+	} else {
+		cr1 = FIELD_PREP(GICV5_IRS_CR1_VPED_WA, GICV5_WRITE_ALLOC) |
+			FIELD_PREP(GICV5_IRS_CR1_VPED_RA, GICV5_READ_ALLOC) |
+			FIELD_PREP(GICV5_IRS_CR1_VMD_WA, GICV5_WRITE_ALLOC) |
+			FIELD_PREP(GICV5_IRS_CR1_VMD_RA, GICV5_READ_ALLOC) |
+			FIELD_PREP(GICV5_IRS_CR1_VPET_RA, GICV5_READ_ALLOC) |
+			FIELD_PREP(GICV5_IRS_CR1_VMT_RA, GICV5_READ_ALLOC) |
+			FIELD_PREP(GICV5_IRS_CR1_IST_WA, GICV5_WRITE_ALLOC) |
+			FIELD_PREP(GICV5_IRS_CR1_IST_RA, GICV5_READ_ALLOC) |
+			FIELD_PREP(GICV5_IRS_CR1_IC, GICV5_WB_CACHE) |
+			FIELD_PREP(GICV5_IRS_CR1_OC, GICV5_WB_CACHE) |
+			FIELD_PREP(GICV5_IRS_CR1_SH, GICV5_INNER_SHARE);
+	}
+
+	irs_writel(irs_data, cr1, GICV5_IRS_CR1);
+
+	cr0 = FIELD_PREP(GICV5_IRS_CR0_IRSEN, 0x1);
+	irs_writel(irs_data, cr0, GICV5_IRS_CR0);
+	gicv5_irs_wait_for_idle(irs_data);
+
+	return 0;
+}
+
+static int __init gicv5_irs_of_init_affinity(struct device_node *node,
+				      struct gicv5_irs_chip_data *irs_data,
+				      u8 iaffid_bits)
+{
+	/*
+	 * Detect IAFFID<->CPU mappings from the device tree and
+	 * record IRS<->CPU topology information.
+	 */
+	int ret, i, ncpus, niaffids;
+	u16 *iaffids;
+	u16 iaffid_mask = GENMASK(iaffid_bits - 1, 0);
+
+	ncpus = of_property_count_elems_of_size(node, "cpus", sizeof(u32));
+	if (WARN_ON(ncpus < 0))
+		return -EINVAL;
+
+	niaffids = of_property_count_elems_of_size(node, "arm,iaffids",
+						   sizeof(u16));
+	if (WARN_ON(niaffids != ncpus))
+		return -EINVAL;
+
+	iaffids = kcalloc(niaffids, sizeof(*iaffids), GFP_KERNEL);
+	if (!iaffids)
+		return -ENOMEM;
+
+	ret = of_property_read_u16_array(node, "arm,iaffids", iaffids, niaffids);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < ncpus; i++) {
+		struct device_node *cpu_node;
+		u32 cpu_phandle;
+		int cpu;
+
+		if (of_property_read_u32_index(node, "cpus", i, &cpu_phandle))
+			continue;
+
+		cpu_node = of_find_node_by_phandle(cpu_phandle);
+		if (WARN_ON(!cpu_node))
+			continue;
+
+		cpu = of_cpu_node_to_id(cpu_node);
+		of_node_put(cpu_node);
+		if (WARN_ON(cpu < 0))
+			continue;
+
+		if (iaffids[i] & ~iaffid_mask) {
+			pr_warn("CPU %d iaffid 0x%x exceeds IRS iaffid bits\n",
+				cpu, iaffids[i]);
+			continue;
+		}
+
+		per_cpu(cpu_iaffid, cpu).iaffid = iaffids[i];
+		per_cpu(cpu_iaffid, cpu).valid = true;
+
+		// We also know that the CPU is connected to this IRS
+		per_cpu(per_cpu_irs_data, cpu) = irs_data;
+	}
+
+	kfree(iaffids);
+
+	return ret;
+}
+
+static void irs_setup_pri_bits(u32 idr1)
+{
+	switch (FIELD_GET(GICV5_IRS_IDR1_PRIORITY_BITS, idr1)) {
+	case GICV5_IRS_IDR1_PRIORITY_BITS_1BITS:
+		gicv5_global_data.irs_pri_bits = 1;
+		break;
+	case GICV5_IRS_IDR1_PRIORITY_BITS_2BITS:
+		gicv5_global_data.irs_pri_bits = 2;
+		break;
+	case GICV5_IRS_IDR1_PRIORITY_BITS_3BITS:
+		gicv5_global_data.irs_pri_bits = 3;
+		break;
+	case GICV5_IRS_IDR1_PRIORITY_BITS_4BITS:
+		gicv5_global_data.irs_pri_bits = 4;
+		break;
+	case GICV5_IRS_IDR1_PRIORITY_BITS_5BITS:
+		gicv5_global_data.irs_pri_bits = 5;
+		break;
+	default:
+		pr_warn("Detected wrong IDR priority bits value 0x%lx\n",
+			FIELD_GET(GICV5_IRS_IDR1_PRIORITY_BITS, idr1));
+		break;
+	}
+}
+
+static int __init gicv5_irs_init(struct device_node *node)
+{
+	void __iomem *irs_base;
+	struct gicv5_irs_chip_data *irs_data;
+	int ret;
+	u32 idr;
+	u8 iaffid_bits;
+
+	irs_data = kzalloc(sizeof(*irs_data), GFP_KERNEL);
+	if (!irs_data)
+		return -ENOMEM;
+
+	raw_spin_lock_init(&irs_data->spi_config_lock);
+
+	irs_base = of_io_request_and_map(node, 0, "IRS");
+	if (IS_ERR(irs_base)) {
+		pr_err("%pOF: unable to map GICv5 IRS registers\n", node);
+		ret = PTR_ERR(irs_base);
+		goto out_err;
+	}
+
+	gicv5_irs_init_bases(irs_data, irs_base, &node->fwnode);
+
+	idr = irs_readl(irs_data, GICV5_IRS_IDR1);
+	iaffid_bits = FIELD_GET(GICV5_IRS_IDR1_IAFFID_BITS, idr) + 1;
+
+	ret = gicv5_irs_of_init_affinity(node, irs_data, iaffid_bits);
+	if (ret) {
+		pr_err("Failed to parse CPU IAFFIDs from the device tree!\n");
+		goto out_iomem;
+	}
+
+	idr = irs_readl(irs_data, GICV5_IRS_IDR7);
+	irs_data->spi_min = FIELD_GET(GICV5_IRS_IDR7_SPI_BASE, idr);
+	idr = irs_readl(irs_data, GICV5_IRS_IDR6);
+	irs_data->spi_range = FIELD_GET(GICV5_IRS_IDR6_SPI_IRS_RANGE, idr);
+
+	if (irs_data->spi_range)
+		pr_info("%s detected SPI range [%u-%u]\n",
+						of_node_full_name(node),
+						irs_data->spi_min,
+						irs_data->spi_min +
+						irs_data->spi_range - 1);
+
+	/*
+	 * Do the global setting only on the first IRS.
+	 * Global properties (iaffid_bits, global spi count) are guaranteed to
+	 * be consistent across IRSes by the architecture.
+	 */
+	if (list_empty(&irs_nodes)) {
+
+		idr = irs_readl(irs_data, GICV5_IRS_IDR1);
+		irs_setup_pri_bits(idr);
+
+		idr = irs_readl(irs_data, GICV5_IRS_IDR5);
+		gicv5_global_data.global_spi_count =
+			FIELD_GET(GICV5_IRS_IDR5_SPI_RANGE, idr);
+
+		pr_debug("Detected %u SPIs globally\n",
+			 gicv5_global_data.global_spi_count);
+	}
+
+	list_add_tail(&irs_data->entry, &irs_nodes);
+
+	return 0;
+out_iomem:
+	iounmap(irs_base);
+out_err:
+	kfree(irs_data);
+	return ret;
+}
+
+void __init gicv5_irs_remove(void)
+{
+	struct gicv5_irs_chip_data *irs_data, *tmp_data;
+
+	list_for_each_entry_safe(irs_data, tmp_data, &irs_nodes, entry) {
+		iounmap(irs_data->irs_base);
+		list_del(&irs_data->entry);
+		kfree(irs_data);
+	}
+}
+
+int __init gicv5_irs_of_probe(struct device_node *parent)
+{
+	int ret;
+	struct device_node *np;
+
+	for_each_available_child_of_node(parent, np) {
+		if (!of_device_is_compatible(np, "arm,gic-v5-irs"))
+			continue;
+
+		ret = gicv5_irs_init(np);
+		if (ret)
+			pr_err("Failed to init IRS %s\n", np->full_name);
+	}
+
+	return list_empty(&irs_nodes) ? -ENODEV : 0;
+}
diff --git a/drivers/irqchip/irq-gic-v5.c b/drivers/irqchip/irq-gic-v5.c
index 996e2c992ef33e5ec8d2680ad4026b725ca39b04..5c7a9263e0cea36515dbdadd067b8eb5f65e8da4 100644
--- a/drivers/irqchip/irq-gic-v5.c
+++ b/drivers/irqchip/irq-gic-v5.c
@@ -24,12 +24,7 @@ static bool gicv5_cpuif_has_gcie(void)
 	return this_cpu_has_cap(ARM64_HAS_GCIE);
 }
 
-struct gicv5_chip_data {
-	struct fwnode_handle *fwnode;
-	struct irq_domain *ppi_domain;
-};
-
-static struct gicv5_chip_data gicv5_global_data __read_mostly;
+struct gicv5_chip_data gicv5_global_data __read_mostly;
 
 static void gicv5_ppi_priority_init(void)
 {
@@ -73,6 +68,30 @@ static void gicv5_ppi_priority_init(void)
 	isb();
 }
 
+static void gicv5_hwirq_init(irq_hw_number_t hwirq, u8 priority, u8 hwirq_type)
+{
+	u64 cdpri, cdaff;
+	u16 iaffid;
+	int ret;
+
+	if (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);
+		gic_insn(cdpri, GICV5_OP_GIC_CDPRI);
+
+		ret = gicv5_irs_cpu_to_iaffid(smp_processor_id(), &iaffid);
+
+		if (WARN_ON(ret))
+			return;
+
+		cdaff = FIELD_PREP(GICV5_GIC_CDAFF_IAFFID_MASK, iaffid)		|
+			FIELD_PREP(GICV5_GIC_CDAFF_TYPE_MASK, hwirq_type)	|
+			FIELD_PREP(GICV5_GIC_CDAFF_ID_MASK, hwirq);
+		gic_insn(cdaff, GICV5_OP_GIC_CDAFF);
+	}
+}
+
 static void gicv5_ppi_irq_mask(struct irq_data *d)
 {
 	u64 hwirq_id_bit = BIT_ULL(d->hwirq % 64);
@@ -88,6 +107,22 @@ static void gicv5_ppi_irq_mask(struct irq_data *d)
 	isb();
 }
 
+static void gicv5_iri_irq_mask(struct irq_data *d, u8 hwirq_type)
+{
+	u64 cddis = d->hwirq | FIELD_PREP(GICV5_GIC_CDDIS_TYPE_MASK, hwirq_type);
+
+	gic_insn(cddis, GICV5_OP_GIC_CDDIS);
+	/*
+	 * We must make sure that GIC CDDIS write effects are propagated
+	 */
+	gsb_sys();
+}
+
+static void gicv5_spi_irq_mask(struct irq_data *d)
+{
+	gicv5_iri_irq_mask(d, GICV5_HWIRQ_TYPE_SPI);
+}
+
 static void gicv5_ppi_irq_unmask(struct irq_data *d)
 {
 	u64 hwirq_id_bit = BIT_ULL(d->hwirq % 64);
@@ -98,7 +133,19 @@ static void gicv5_ppi_irq_unmask(struct irq_data *d)
 		sysreg_clear_set_s(SYS_ICC_PPI_ENABLER1_EL1, 0, hwirq_id_bit);
 }
 
-static void gicv5_hwirq_eoi(u32 hwirq_id, u32 hwirq_type)
+static void gicv5_iri_irq_unmask(struct irq_data *d, u8 hwirq_type)
+{
+	u64 cden = d->hwirq | FIELD_PREP(GICV5_GIC_CDEN_TYPE_MASK, hwirq_type);
+
+	gic_insn(cden, GICV5_OP_GIC_CDEN);
+}
+
+static void gicv5_spi_irq_unmask(struct irq_data *d)
+{
+	gicv5_iri_irq_unmask(d, GICV5_HWIRQ_TYPE_SPI);
+}
+
+static void gicv5_hwirq_eoi(u32 hwirq_id, u8 hwirq_type)
 {
 	u64 cddi = hwirq_id | FIELD_PREP(GICV5_GIC_CDDI_TYPE_MASK, hwirq_type);
 
@@ -112,6 +159,11 @@ static void gicv5_ppi_irq_eoi(struct irq_data *d)
 	gicv5_hwirq_eoi(d->hwirq, GICV5_HWIRQ_TYPE_PPI);
 }
 
+static void gicv5_spi_irq_eoi(struct irq_data *d)
+{
+	gicv5_hwirq_eoi(d->hwirq, GICV5_HWIRQ_TYPE_SPI);
+}
+
 static int gicv5_ppi_set_type(struct irq_data *d, unsigned int type)
 {
 	/*
@@ -145,6 +197,41 @@ static int gicv5_ppi_set_type(struct irq_data *d, unsigned int type)
 	return 0;
 }
 
+static int gicv5_iri_irq_set_affinity(struct irq_data *d,
+				      const struct cpumask *mask_val,
+				      bool force, u8 hwirq_type)
+{
+	u16 iaffid;
+	u64 cdaff;
+	int ret, cpuid;
+
+	if (force)
+		cpuid = cpumask_first(mask_val);
+	else
+		cpuid = cpumask_any_and(mask_val, cpu_online_mask);
+
+	ret = gicv5_irs_cpu_to_iaffid(cpuid, &iaffid);
+	if (ret)
+		return ret;
+
+	cdaff = FIELD_PREP(GICV5_GIC_CDAFF_IAFFID_MASK, iaffid)		|
+		FIELD_PREP(GICV5_GIC_CDAFF_TYPE_MASK, hwirq_type)	|
+		FIELD_PREP(GICV5_GIC_CDAFF_ID_MASK, d->hwirq);
+	gic_insn(cdaff, GICV5_OP_GIC_CDAFF);
+
+	irq_data_update_effective_affinity(d, cpumask_of(cpuid));
+
+	return IRQ_SET_MASK_OK_DONE;
+}
+
+static int gicv5_spi_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_SPI);
+}
+
 static int gicv5_ppi_irq_get_irqchip_state(struct irq_data *d,
 					   enum irqchip_irq_state which,
 					   bool *val)
@@ -187,6 +274,53 @@ static int gicv5_ppi_irq_get_irqchip_state(struct irq_data *d,
 	return -EINVAL;
 }
 
+static int gicv5_iri_irq_get_irqchip_state(struct irq_data *d,
+					   enum irqchip_irq_state which,
+					   bool *val, u8 hwirq_type)
+{
+	u64 icsr, cdrcfg = d->hwirq | FIELD_PREP(GICV5_GIC_CDRCFG_TYPE_MASK,
+						 hwirq_type);
+
+	preempt_disable();
+	gic_insn(cdrcfg, GICV5_OP_GIC_CDRCFG);
+	isb();
+	icsr = read_sysreg_s(SYS_ICC_ICSR_EL1);
+	preempt_enable();
+
+	if (FIELD_GET(ICC_ICSR_EL1_F, icsr)) {
+		pr_err("ICSR_EL1 is invalid\n");
+		return -EINVAL;
+	}
+
+	switch (which) {
+	case IRQCHIP_STATE_PENDING:
+		*val = !!(FIELD_GET(ICC_ICSR_EL1_Pending, icsr));
+		return 0;
+
+	case IRQCHIP_STATE_ACTIVE:
+		*val = !!(FIELD_GET(ICC_ICSR_EL1_Active, icsr));
+		return 0;
+
+	case IRQCHIP_STATE_MASKED:
+		*val = !(FIELD_GET(ICC_ICSR_EL1_Enabled, icsr));
+		return 0;
+
+	default:
+		pr_debug("Unexpected irqchip_irq_state\n");
+		return -EINVAL;
+	}
+
+	return -EINVAL;
+}
+
+static int gicv5_spi_irq_get_irqchip_state(struct irq_data *d,
+					   enum irqchip_irq_state which,
+					   bool *val)
+{
+	return gicv5_iri_irq_get_irqchip_state(d, which, val,
+					       GICV5_HWIRQ_TYPE_SPI);
+}
+
 static int gicv5_ppi_irq_set_irqchip_state(struct irq_data *d,
 					   enum irqchip_irq_state which,
 					   bool val)
@@ -245,6 +379,51 @@ static int gicv5_ppi_irq_set_irqchip_state(struct irq_data *d,
 	return -EINVAL;
 }
 
+static void gicv5_iri_irq_write_pending_state(struct irq_data *d, bool val,
+					      u8 hwirq_type)
+{
+	u64 cdpend;
+
+	cdpend = FIELD_PREP(GICV5_GIC_CDPEND_TYPE_MASK, hwirq_type)	|
+		 FIELD_PREP(GICV5_GIC_CDPEND_ID_MASK, d->hwirq)		|
+		 FIELD_PREP(GICV5_GIC_CDPEND_PENDING_MASK, val);
+
+	gic_insn(cdpend, GICV5_OP_GIC_CDPEND);
+}
+
+static void gicv5_spi_irq_write_pending_state(struct irq_data *d, bool val)
+{
+	gicv5_iri_irq_write_pending_state(d, val, GICV5_HWIRQ_TYPE_SPI);
+}
+
+static int gicv5_spi_irq_set_irqchip_state(struct irq_data *d,
+					   enum irqchip_irq_state which,
+					   bool val)
+{
+	switch (which) {
+	case IRQCHIP_STATE_PENDING:
+		gicv5_spi_irq_write_pending_state(d, val);
+		break;
+	case IRQCHIP_STATE_MASKED:
+		if (val)
+			gicv5_spi_irq_mask(d);
+		else
+			gicv5_spi_irq_unmask(d);
+		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 const struct irq_chip gicv5_ppi_irq_chip = {
 	.name			= "GICv5-PPI",
 	.irq_mask		= gicv5_ppi_irq_mask,
@@ -258,6 +437,21 @@ static const struct irq_chip gicv5_ppi_irq_chip = {
 				  IRQCHIP_MASK_ON_SUSPEND
 };
 
+static const struct irq_chip gicv5_spi_irq_chip = {
+	.name			= "GICv5-SPI",
+	.irq_mask		= gicv5_spi_irq_mask,
+	.irq_unmask		= gicv5_spi_irq_unmask,
+	.irq_eoi		= gicv5_spi_irq_eoi,
+	.irq_set_type		= gicv5_spi_irq_set_type,
+	.irq_set_affinity	= gicv5_spi_irq_set_affinity,
+	.irq_retrigger		= gicv5_spi_irq_retrigger,
+	.irq_get_irqchip_state	= gicv5_spi_irq_get_irqchip_state,
+	.irq_set_irqchip_state	= gicv5_spi_irq_set_irqchip_state,
+	.flags			= IRQCHIP_SET_TYPE_MASKED |
+				  IRQCHIP_SKIP_SET_WAKE	  |
+				  IRQCHIP_MASK_ON_SUSPEND
+};
+
 static int gicv5_irq_ppi_domain_translate(struct irq_domain *d,
 					  struct irq_fwspec *fwspec,
 					  irq_hw_number_t *hwirq,
@@ -302,8 +496,8 @@ static int gicv5_irq_ppi_domain_alloc(struct irq_domain *domain,
 	return 0;
 }
 
-static void gicv5_irq_ppi_domain_free(struct irq_domain *domain,
-				      unsigned int virq, unsigned int nr_irqs)
+static void gicv5_irq_domain_free(struct irq_domain *domain,
+				  unsigned int virq, unsigned int nr_irqs)
 {
 	struct irq_data *d;
 
@@ -335,10 +529,84 @@ static int gicv5_irq_ppi_domain_select(struct irq_domain *d,
 static const struct irq_domain_ops gicv5_irq_ppi_domain_ops = {
 	.translate	= gicv5_irq_ppi_domain_translate,
 	.alloc		= gicv5_irq_ppi_domain_alloc,
-	.free		= gicv5_irq_ppi_domain_free,
+	.free		= gicv5_irq_domain_free,
 	.select		= gicv5_irq_ppi_domain_select
 };
 
+static int gicv5_irq_spi_domain_translate(struct irq_domain *d,
+					  struct irq_fwspec *fwspec,
+					  irq_hw_number_t *hwirq,
+					  unsigned int *type)
+{
+	if (is_of_node(fwspec->fwnode)) {
+		if (fwspec->param_count < 3)
+			return -EINVAL;
+
+		if (fwspec->param[0] != GICV5_HWIRQ_TYPE_SPI)
+			return -EINVAL;
+
+		*hwirq = fwspec->param[1];
+		*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int gicv5_irq_spi_domain_alloc(struct irq_domain *domain,
+				      unsigned int virq, unsigned int nr_irqs,
+				      void *arg)
+{
+	struct gicv5_irs_chip_data *chip_data;
+	struct irq_data *irqd;
+	unsigned int type = IRQ_TYPE_NONE;
+	struct irq_fwspec *fwspec = arg;
+	irq_hw_number_t hwirq;
+	int ret;
+
+	if (WARN_ON(nr_irqs != 1))
+		return -EINVAL;
+
+	ret = gicv5_irq_spi_domain_translate(domain, fwspec, &hwirq, &type);
+	if (ret)
+		return ret;
+
+	irqd = irq_desc_get_irq_data(irq_to_desc(virq));
+	chip_data = gicv5_irs_lookup_by_spi_id(hwirq);
+
+	irq_domain_set_info(domain, virq, hwirq, &gicv5_spi_irq_chip, chip_data,
+		handle_fasteoi_irq, NULL, NULL);
+	irq_set_probe(virq);
+	irqd_set_single_target(irqd);
+
+	gicv5_hwirq_init(hwirq, GICV5_IRQ_PRIORITY_MI, GICV5_HWIRQ_TYPE_SPI);
+
+	return 0;
+}
+
+static int gicv5_irq_spi_domain_select(struct irq_domain *d,
+				       struct irq_fwspec *fwspec,
+				       enum irq_domain_bus_token bus_token)
+{
+	if (fwspec->fwnode != d->fwnode)
+		return 0;
+
+	if (fwspec->param[0] != GICV5_HWIRQ_TYPE_SPI) {
+		// only handle SPIs
+		return 0;
+	}
+
+	return (d == gicv5_global_data.spi_domain);
+}
+
+static const struct irq_domain_ops gicv5_irq_spi_domain_ops = {
+	.translate	= gicv5_irq_spi_domain_translate,
+	.alloc		= gicv5_irq_spi_domain_alloc,
+	.free		= gicv5_irq_domain_free,
+	.select		= gicv5_irq_spi_domain_select
+};
+
 static inline void handle_irq_per_domain(u32 hwirq)
 {
 	u32 hwirq_id;
@@ -349,6 +617,8 @@ static inline void handle_irq_per_domain(u32 hwirq)
 
 	if (hwirq_type == GICV5_HWIRQ_TYPE_PPI)
 		domain = gicv5_global_data.ppi_domain;
+	else if (hwirq_type == GICV5_HWIRQ_TYPE_SPI)
+		domain = gicv5_global_data.spi_domain;
 
 	if (generic_handle_domain_irq(domain, hwirq_id)) {
 		pr_err("Could not handle, hwirq = 0x%x", hwirq_id);
@@ -427,13 +697,15 @@ static int gicv5_starting_cpu(unsigned int cpu)
 
 	gicv5_cpu_enable_interrupts();
 
-	return 0;
+	return gicv5_irs_register_cpu(cpu);
 }
 
 static void __init gicv5_free_domains(void)
 {
 	if (gicv5_global_data.ppi_domain)
 		irq_domain_remove(gicv5_global_data.ppi_domain);
+	if (gicv5_global_data.spi_domain)
+		irq_domain_remove(gicv5_global_data.spi_domain);
 }
 
 static int __init gicv5_init_domains(struct fwnode_handle *handle)
@@ -447,6 +719,19 @@ static int __init gicv5_init_domains(struct fwnode_handle *handle)
 	irq_domain_update_bus_token(gicv5_global_data.ppi_domain,
 				    DOMAIN_BUS_WIRED);
 
+	if (gicv5_global_data.global_spi_count) {
+		gicv5_global_data.spi_domain = irq_domain_create_linear(
+			handle, gicv5_global_data.global_spi_count,
+			&gicv5_irq_spi_domain_ops, NULL);
+
+		if (WARN_ON(!gicv5_global_data.spi_domain)) {
+			gicv5_free_domains();
+			return -ENOMEM;
+		}
+		irq_domain_update_bus_token(gicv5_global_data.spi_domain,
+					    DOMAIN_BUS_WIRED);
+	}
+
 	return 0;
 }
 
@@ -456,14 +741,14 @@ static void gicv5_set_cpuif_pribits(void)
 
 	switch (FIELD_GET(ICC_IDR0_EL1_PRI_BITS, icc_idr0)) {
 	case ICC_IDR0_EL1_PRI_BITS_4BITS:
-		pri_bits = 4;
+		gicv5_global_data.cpuif_pri_bits = 4;
 		break;
 	case ICC_IDR0_EL1_PRI_BITS_5BITS:
-		pri_bits = 5;
+		gicv5_global_data.cpuif_pri_bits = 5;
 		break;
 	default:
 		pr_err("Unexpected ICC_IDR0_EL1_PRI_BITS value, default to 4");
-		pri_bits = 4;
+		gicv5_global_data.cpuif_pri_bits = 4;
 		break;
 	}
 }
@@ -473,20 +758,31 @@ static int __init gicv5_of_init(struct device_node *node,
 {
 	int ret;
 
-	ret = gicv5_init_domains(&node->fwnode);
+	ret = gicv5_irs_of_probe(node);
 	if (ret)
 		return ret;
 
+	ret = gicv5_init_domains(&node->fwnode);
+	if (ret) {
+		gicv5_irs_remove();
+		return ret;
+	}
+
 	gicv5_set_cpuif_pribits();
 
+	pri_bits = min_not_zero(gicv5_global_data.cpuif_pri_bits,
+		       gicv5_global_data.irs_pri_bits);
+
 	ret = gicv5_starting_cpu(smp_processor_id());
 	if (ret) {
+		gicv5_irs_remove();
 		gicv5_free_domains();
 		return ret;
 	}
 
 	ret = set_handle_irq(gicv5_handle_irq);
 	if (ret) {
+		gicv5_irs_remove();
 		gicv5_free_domains();
 		gicv5_cpu_disable_interrupts();
 		return ret;
diff --git a/drivers/irqchip/irq-gic-v5.h b/drivers/irqchip/irq-gic-v5.h
index d8b797cdea2f786646fd88d9c8f60d483380991c..57e2472f1f2a9984f399d2a8633c824bc208da26 100644
--- a/drivers/irqchip/irq-gic-v5.h
+++ b/drivers/irqchip/irq-gic-v5.h
@@ -12,8 +12,109 @@
 #define GICV5_HWIRQ_INTID		GENMASK_ULL(31, 0)
 
 #define GICV5_HWIRQ_TYPE_PPI		UL(0x1)
+#define GICV5_HWIRQ_TYPE_SPI		UL(0x3)
 
 #define GICV5_PPI_HM_EDGE		UL(0x0)
 #define GICV5_PPI_HM_LEVEL		UL(0x1)
 
+#define GICV5_NO_READ_ALLOC		0b0
+#define GICV5_READ_ALLOC		0b1
+#define GICV5_NO_WRITE_ALLOC		0b0
+#define GICV5_WRITE_ALLOC		0b1
+
+#define GICV5_NON_CACHE			0b00
+#define GICV5_WB_CACHE			0b01
+#define GICV5_WT_CACHE			0b10
+
+#define GICV5_NON_SHARE			0b00
+#define GICV5_OUTER_SHARE		0b10
+#define GICV5_INNER_SHARE		0b11
+
+#define GICV5_IRS_IDR1			0x0004
+#define GICV5_IRS_IDR2			0x0008
+#define GICV5_IRS_IDR5			0x0014
+#define GICV5_IRS_IDR6			0x0018
+#define GICV5_IRS_IDR7			0x001c
+#define GICV5_IRS_CR0			0x0080
+#define GICV5_IRS_CR1			0x0084
+#define GICV5_IRS_SPI_SELR		0x0108
+#define GICV5_IRS_SPI_CFGR		0x0114
+#define GICV5_IRS_SPI_STATUSR		0x0118
+#define GICV5_IRS_PE_SELR		0x0140
+#define GICV5_IRS_PE_STATUSR		0x0144
+#define GICV5_IRS_PE_CR0		0x0148
+
+#define GICV5_IRS_IDR1_PRIORITY_BITS	GENMASK(22, 20)
+#define GICV5_IRS_IDR1_IAFFID_BITS	GENMASK(19, 16)
+
+#define GICV5_IRS_IDR1_PRIORITY_BITS_1BITS	0b000
+#define GICV5_IRS_IDR1_PRIORITY_BITS_2BITS	0b001
+#define GICV5_IRS_IDR1_PRIORITY_BITS_3BITS	0b010
+#define GICV5_IRS_IDR1_PRIORITY_BITS_4BITS	0b011
+#define GICV5_IRS_IDR1_PRIORITY_BITS_5BITS	0b100
+
+#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_CR0_IDLE		BIT(1)
+#define GICV5_IRS_CR0_IRSEN		BIT(0)
+
+#define GICV5_IRS_CR1_VPED_WA		BIT(15)
+#define GICV5_IRS_CR1_VPED_RA		BIT(14)
+#define GICV5_IRS_CR1_VMD_WA		BIT(13)
+#define GICV5_IRS_CR1_VMD_RA		BIT(12)
+#define GICV5_IRS_CR1_VPET_WA		BIT(11)
+#define GICV5_IRS_CR1_VPET_RA		BIT(10)
+#define GICV5_IRS_CR1_VMT_WA		BIT(9)
+#define GICV5_IRS_CR1_VMT_RA		BIT(8)
+#define GICV5_IRS_CR1_IST_WA		BIT(7)
+#define GICV5_IRS_CR1_IST_RA		BIT(6)
+#define GICV5_IRS_CR1_IC		GENMASK(5, 4)
+#define GICV5_IRS_CR1_OC		GENMASK(3, 2)
+#define GICV5_IRS_CR1_SH		GENMASK(1, 0)
+
+#define GICV5_IRS_SPI_STATUSR_V		BIT(1)
+#define GICV5_IRS_SPI_STATUSR_IDLE	BIT(0)
+
+#define GICV5_IRS_SPI_SELR_ID		GENMASK(23, 0)
+
+#define GICV5_IRS_SPI_CFGR_TM		BIT(0)
+
+#define GICV5_IRS_PE_SELR_IAFFID	GENMASK(15, 0)
+
+#define GICV5_IRS_PE_STATUSR_V		BIT(1)
+#define GICV5_IRS_PE_STATUSR_IDLE	BIT(0)
+
+#define GICV5_IRS_PE_CR0_DPS		BIT(0)
+
+struct gicv5_chip_data {
+	struct fwnode_handle	*fwnode;
+	struct irq_domain	*ppi_domain;
+	struct irq_domain	*spi_domain;
+	u32			global_spi_count;
+	u8			cpuif_pri_bits;
+	u8			irs_pri_bits;
+	u8			irs_iaffid_bits;
+};
+
+extern struct gicv5_chip_data gicv5_global_data __read_mostly;
+
+struct gicv5_irs_chip_data {
+	struct list_head	entry;
+	struct fwnode_handle	*fwnode;
+	void __iomem		*irs_base;
+	u32			flags;
+	u32			spi_min;
+	u32			spi_range;
+	raw_spinlock_t		spi_config_lock;
+};
+
+int gicv5_irs_of_probe(struct device_node *parent);
+void gicv5_irs_remove(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);
+
 #endif

-- 
2.48.0



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 20/24] irqchip/gic-v5: Add GICv5 LPI/IPI support
  2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (18 preceding siblings ...)
  2025-04-08 10:50 ` [PATCH 19/24] irqchip/gic-v5: Add GICv5 IRS/SPI support Lorenzo Pieralisi
@ 2025-04-08 10:50 ` Lorenzo Pieralisi
  2025-04-09  8:23   ` Arnd Bergmann
                     ` (2 more replies)
  2025-04-08 10:50 ` [PATCH 21/24] irqchip/gic-v5: Enable GICv5 SMP booting Lorenzo Pieralisi
                   ` (3 subsequent siblings)
  23 siblings, 3 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:50 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

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.

Memory storage is handled by the IRS - that is configured
at probe time by the driver with the required memory.

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.

Add an LPI IRQ domain to:

- Manage LPI state and routing
- Add LPI IRQchip structure and callbacks
- LPI domain allocation/de-allocation

On GICv5 systems, IPIs are implemented using LPIs.

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.

Move the arm64 IPI enum declaration to a header file so that the GICv5
driver code can detect how many IPIs are required by arch code.

IPIs are backed by LPIs, add LPIs allocation/de-allocation
functions.

The LPI INTID namespace is managed using a maple tree which encodes
ranges, add code to alloc/free LPI INTIDs.

Maple tree entries are not used by the driver, only the range tracking
is required - therefore the driver first finds an empty area large
enough to contain the required number of LPIs then checks the
adjacent (and possibly occupied) LPI ranges and try to merge them
together, reducing maple tree slots usage.

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@arm.com>
Signed-off-by: Sascha Bischoff <sascha.bischoff@arm.com>
Co-developed-by: Timothy Hayes <timothy.hayes@arm.com>
Signed-off-by: Timothy Hayes <timothy.hayes@arm.com>
Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Will Deacon <will@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/include/asm/arch_gicv5.h |   1 +
 arch/arm64/include/asm/smp.h        |  17 ++
 arch/arm64/kernel/smp.c             |  17 --
 drivers/irqchip/irq-gic-v5-irs.c    | 369 ++++++++++++++++++++++++++++++++++++
 drivers/irqchip/irq-gic-v5.c        | 347 ++++++++++++++++++++++++++++++++-
 drivers/irqchip/irq-gic-v5.h        |  64 +++++++
 6 files changed, 797 insertions(+), 18 deletions(-)

diff --git a/arch/arm64/include/asm/arch_gicv5.h b/arch/arm64/include/asm/arch_gicv5.h
index 00c6b7903094ca5d6e496890d029d21d82b7af66..75557fdad611fa51d7136126eb80cb861be98a8d 100644
--- a/arch/arm64/include/asm/arch_gicv5.h
+++ b/arch/arm64/include/asm/arch_gicv5.h
@@ -5,6 +5,7 @@
 #ifndef __ASM_ARCH_GICV5_H
 #define __ASM_ARCH_GICV5_H
 
+#include <asm/cacheflush.h>
 #include <asm/sysreg.h>
 
 #ifndef __ASSEMBLY__
diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h
index d6fd6efb66a673ae33825971e4aa07e791c02ee5..d48ef6d5abcc77d1c06ad8e91e72006acf662078 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/kernel/smp.c b/arch/arm64/kernel/smp.c
index 3f3712e47c94c62836fb89cd4bfb3595fbb41557..148145979d83f67469075df1c8b5e366ffe9d907 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 5999f2a2c8dac0ea01ab0aa5df65bf12e1f59f63..909ccf07879a1feb5abb65fa265706f2c276d927 100644
--- a/drivers/irqchip/irq-gic-v5-irs.c
+++ b/drivers/irqchip/irq-gic-v5-irs.c
@@ -7,11 +7,14 @@
 
 #include <linux/iopoll.h>
 #include <linux/irqchip.h>
+#include <linux/log2.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
 
 #include "irq-gic-v5.h"
 
+#define LPI_ID_BITS_LINEAR		12
+
 #define IRS_FLAGS_NON_COHERENT		BIT(0)
 
 static DEFINE_PER_CPU(struct gicv5_irs_chip_data *, per_cpu_irs_data);
@@ -22,12 +25,346 @@ static u32 irs_readl(struct gicv5_irs_chip_data *irs_data, const u64 reg_offset)
 	return readl_relaxed(irs_data->irs_base + reg_offset);
 }
 
+static u64 irs_readq(struct gicv5_irs_chip_data *irs_data, const u64 reg_offset)
+{
+	return readq_relaxed(irs_data->irs_base + reg_offset);
+}
+
 static void irs_writel(struct gicv5_irs_chip_data *irs_data, const u32 val,
 		       const u64 reg_offset)
 {
 	writel_relaxed(val, irs_data->irs_base + reg_offset);
 }
 
+static void irs_writeq(struct gicv5_irs_chip_data *irs_data, const u64 val,
+		       const u64 reg_offset)
+{
+	writeq_relaxed(val, irs_data->irs_base + reg_offset);
+}
+
+/* Wait for completion of an IST change */
+static int gicv5_irs_ist_wait_for_idle(struct gicv5_irs_chip_data *irs_data)
+{
+	int ret;
+	u32 val;
+
+	ret = readl_relaxed_poll_timeout_atomic(
+			irs_data->irs_base + GICV5_IRS_IST_STATUSR, val,
+			FIELD_GET(GICV5_IRS_IST_STATUSR_IDLE, val), 1,
+			USEC_PER_SEC);
+
+	if (ret == -ETIMEDOUT)
+		pr_err_ratelimited("IST_STATUSR.IDLE timeout...\n");
+
+	return ret;
+}
+
+static int __init gicv5_irs_init_ist_linear(struct gicv5_irs_chip_data *irs_data,
+					    unsigned int lpi_id_bits,
+					    unsigned int istsz)
+{
+	void *ist;
+	u32 n, cfgr;
+	u64 baser;
+	size_t l2istsz;
+	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);
+	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);
+	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(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(irs_data, baser, GICV5_IRS_IST_BASER);
+
+	ret = gicv5_irs_ist_wait_for_idle(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)
+{
+	u32 cfgr, n;
+	u64 baser;
+	size_t l1sz;
+	__le64 *l1ist;
+	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(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(irs_data, baser, GICV5_IRS_IST_BASER);
+
+	ret = gicv5_irs_ist_wait_for_idle(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;
+	u32 l2istr, l2bits;
+	size_t l2size;
+	unsigned int index;
+	__le64 *l1ist;
+	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(irs_data, l2istr, GICV5_IRS_MAP_L2_ISTR);
+
+	ret = gicv5_irs_ist_wait_for_idle(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) {
+		/*
+		 * Order the MMIO IRS wait for idle with cache
+		 * invalidation
+		 */
+		dma_rmb();
+		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 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_16K:
+		if (GICV5_IRS_IST_L2SZ_SUPPORT_16KB(idr2))
+			return GICV5_IRS_IST_CFGR_L2SZ_16K;
+		fallthrough;
+	case SZ_4K:
+		if (GICV5_IRS_IST_L2SZ_SUPPORT_4KB(idr2))
+			return GICV5_IRS_IST_CFGR_L2SZ_4K;
+		if (GICV5_IRS_IST_L2SZ_SUPPORT_16KB(idr2))
+			return GICV5_IRS_IST_CFGR_L2SZ_16K;
+		if (GICV5_IRS_IST_L2SZ_SUPPORT_64KB(idr2))
+			return GICV5_IRS_IST_CFGR_L2SZ_64K;
+		break;
+	}
+
+	return -ENODEV;
+}
+
+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, l2_iste_sz_split, idr2;
+	bool two_levels, istmd;
+	u64 baser;
+	int ret;
+
+	baser = irs_readq(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(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 allocating the maximum allowed
+	 * number of IDs.
+	 *
+	 * For 1-level tables, we should allocate 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);
+		if (l2sz < 0)
+			return l2sz;
+	}
+
+	istmd = !!FIELD_GET(GICV5_IRS_IDR2_ISTMD, idr2);
+
+	l2_iste_sz = GICV5_IRS_IST_CFGR_ISTSZ_4;
+
+	// Only supported if IRS_IDR2.ISTMD is 1
+	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
+	 */
+	two_levels = two_levels && (lpi_id_bits > ((10 - l2_iste_sz) +
+						   (2 * l2sz)));
+
+	if (two_levels)
+		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_lpi(BIT(lpi_id_bits));
+
+	return 0;
+}
+
 struct iaffid_entry {
 	u16 iaffid;
 	bool valid;
@@ -383,8 +720,16 @@ static int __init gicv5_irs_init(struct device_node *node)
 		goto out_iomem;
 	}
 
+	idr = irs_readl(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(irs_data, GICV5_IRS_IDR7);
 	irs_data->spi_min = FIELD_GET(GICV5_IRS_IDR7_SPI_BASE, idr);
+
 	idr = irs_readl(irs_data, GICV5_IRS_IDR6);
 	irs_data->spi_range = FIELD_GET(GICV5_IRS_IDR6_SPI_IRS_RANGE, idr);
 
@@ -409,6 +754,8 @@ static int __init gicv5_irs_init(struct device_node *node)
 		gicv5_global_data.global_spi_count =
 			FIELD_GET(GICV5_IRS_IDR5_SPI_RANGE, idr);
 
+		gicv5_init_lpi_domain();
+
 		pr_debug("Detected %u SPIs globally\n",
 			 gicv5_global_data.global_spi_count);
 	}
@@ -427,6 +774,9 @@ void __init gicv5_irs_remove(void)
 {
 	struct gicv5_irs_chip_data *irs_data, *tmp_data;
 
+	gicv5_free_lpi_domain();
+	gicv5_free_lpi();
+
 	list_for_each_entry_safe(irs_data, tmp_data, &irs_nodes, entry) {
 		iounmap(irs_data->irs_base);
 		list_del(&irs_data->entry);
@@ -434,6 +784,25 @@ void __init gicv5_irs_remove(void)
 	}
 }
 
+int __init gicv5_irs_enable(void)
+{
+	int ret;
+	struct gicv5_irs_chip_data *irs_data;
+
+	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)
 {
 	int ret;
diff --git a/drivers/irqchip/irq-gic-v5.c b/drivers/irqchip/irq-gic-v5.c
index 5c7a9263e0cea36515dbdadd067b8eb5f65e8da4..85d951bfd807fc1b67b616dbadeebc150848293f 100644
--- a/drivers/irqchip/irq-gic-v5.c
+++ b/drivers/irqchip/irq-gic-v5.c
@@ -7,6 +7,8 @@
 
 #include <linux/irqchip.h>
 #include <linux/irqdomain.h>
+#include <linux/maple_tree.h>
+#include <linux/slab.h>
 #include <linux/wordpart.h>
 
 #include <asm/cpufeature.h>
@@ -26,6 +28,90 @@ static bool gicv5_cpuif_has_gcie(void)
 
 struct gicv5_chip_data gicv5_global_data __read_mostly;
 
+static struct maple_tree lpi_mt;
+static u32 num_lpis;
+
+void __init gicv5_init_lpi(u32 lpis)
+{
+	mt_init_flags(&lpi_mt, MT_FLAGS_ALLOC_RANGE);
+	num_lpis = lpis;
+}
+
+void __init gicv5_free_lpi(void)
+{
+	mtree_destroy(&lpi_mt);
+	num_lpis = 0;
+}
+
+#define MT_ENTRY	((void *)&lpi_mt) /* Unused - just a valid pointer */
+
+static int alloc_lpi_range(u32 lpis, u32 *base)
+{
+	int ret;
+	void *entry;
+	unsigned long lpi_base, startp, lastp;
+
+	MA_STATE(mas, &lpi_mt, 0, 0);
+
+	if (!num_lpis)
+		return -ENODEV;
+
+	mtree_lock(&lpi_mt);
+	ret = mas_empty_area(&mas, 0, num_lpis - 1, lpis);
+	if (ret) {
+		pr_err("Failed to perform a dynamic alloc in the LPI MT!\n");
+		return ret;
+	}
+
+	lpi_base = mas.index;
+
+	/*
+	 * We don't really care about the entry itself, only about
+	 * allocation of a maple tree ranges describing in use LPIs.
+	 * That's why, upon allocation, we try to merge slots adjacent
+	 * with the empty one we are allocating to minimize the number
+	 * of slots we take from maple tree nodes for nothing, all
+	 * we need to keep track of is in use ranges.
+	 */
+	startp = mas.index;
+	lastp = mas.last;
+
+	entry = mas_next(&mas, num_lpis - 1);
+	if (entry && mas.index == lastp + 1)
+		lastp = mas.last;
+
+	entry = mas_prev(&mas, 0);
+	if (entry)
+		startp = mas.index;
+	mas_set_range(&mas, startp, lastp);
+	mas_store_gfp(&mas, MT_ENTRY, GFP_KERNEL);
+	mtree_unlock(&lpi_mt);
+
+	// startp is the index at which we allocated, i.e. the base LPI.
+	*base = lpi_base;
+
+	return 0;
+}
+
+// Drop entries between min and max (inclusive)
+static int release_lpi_range(u32 min, u32 max)
+{
+	return mtree_store_range(&lpi_mt, min, max, NULL, GFP_KERNEL);
+}
+
+static int gicv5_alloc_lpi_range(u32 nr_lpis, u32 *base)
+{
+	return alloc_lpi_range(nr_lpis, base);
+}
+
+static int gicv5_free_lpi_range(u32 base, u32 nr_lpis)
+{
+	if (nr_lpis < 1)
+		return -EINVAL;
+
+	return release_lpi_range(base, base + nr_lpis - 1);
+}
+
 static void gicv5_ppi_priority_init(void)
 {
 	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRIORITY_MI),
@@ -74,7 +160,8 @@ 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);
@@ -123,6 +210,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);
@@ -145,6 +237,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 = hwirq_id | FIELD_PREP(GICV5_GIC_CDDI_TYPE_MASK, hwirq_type);
@@ -164,6 +261,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_ppi_set_type(struct irq_data *d, unsigned int type)
 {
 	/*
@@ -232,6 +334,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);
+}
+
 static int gicv5_ppi_irq_get_irqchip_state(struct irq_data *d,
 					   enum irqchip_irq_state which,
 					   bool *val)
@@ -321,6 +431,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 *val)
+{
+	return gicv5_iri_irq_get_irqchip_state(d, which, val,
+					       GICV5_HWIRQ_TYPE_LPI);
+}
+
 static int gicv5_ppi_irq_set_irqchip_state(struct irq_data *d,
 					   enum irqchip_irq_state which,
 					   bool val)
@@ -396,6 +514,11 @@ static void gicv5_spi_irq_write_pending_state(struct irq_data *d, bool val)
 	gicv5_iri_irq_write_pending_state(d, val, GICV5_HWIRQ_TYPE_SPI);
 }
 
+static void gicv5_lpi_irq_write_pending_state(struct irq_data *d, bool val)
+{
+	gicv5_iri_irq_write_pending_state(d, val, GICV5_HWIRQ_TYPE_LPI);
+}
+
 static int gicv5_spi_irq_set_irqchip_state(struct irq_data *d,
 					   enum irqchip_irq_state which,
 					   bool val)
@@ -418,12 +541,47 @@ 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 val)
+{
+	switch (which) {
+	case IRQCHIP_STATE_PENDING:
+		gicv5_lpi_irq_write_pending_state(d, val);
+		break;
+	case IRQCHIP_STATE_MASKED:
+		if (val)
+			gicv5_lpi_irq_mask(d);
+		else
+			gicv5_lpi_irq_unmask(d);
+		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 const struct irq_chip gicv5_ppi_irq_chip = {
 	.name			= "GICv5-PPI",
 	.irq_mask		= gicv5_ppi_irq_mask,
@@ -452,6 +610,35 @@ 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_SET_TYPE_MASKED |
+				  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,
+	// We only handle this one in the IPI domain - the rest go to parent
+	.ipi_send_single	= gicv5_ipi_send_single,
+	.flags			= IRQCHIP_SET_TYPE_MASKED |
+				  IRQCHIP_SKIP_SET_WAKE	  |
+				  IRQCHIP_MASK_ON_SUSPEND
+};
+
 static int gicv5_irq_ppi_domain_translate(struct irq_domain *d,
 					  struct irq_fwspec *fwspec,
 					  irq_hw_number_t *hwirq,
@@ -607,6 +794,120 @@ static const struct irq_domain_ops gicv5_irq_spi_domain_ops = {
 	.select		= gicv5_irq_spi_domain_select
 };
 
+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 *base_lpi = arg;
+	int i, ret;
+
+	hwirq = *base_lpi;
+
+	for (i = 0; i < nr_irqs; i++) {
+		irqd = irq_desc_get_irq_data(irq_to_desc(virq + i));
+
+		irq_domain_set_info(domain, virq + i, hwirq + i,
+				    &gicv5_lpi_irq_chip, NULL,
+				    handle_fasteoi_irq, NULL, NULL);
+		irqd_set_single_target(irqd);
+
+		ret = gicv5_irs_iste_alloc(hwirq + i);
+		if (ret < 0)
+			return ret;
+
+		gicv5_hwirq_init((hwirq + i), GICV5_IRQ_PRIORITY_MI,
+					      GICV5_HWIRQ_TYPE_LPI);
+	}
+
+	return 0;
+}
+
+static void gicv5_irq_lpi_domain_free(struct irq_domain *domain,
+				      unsigned int virq, unsigned int nr_irqs)
+{
+	int i;
+
+	for (i = 0; i < nr_irqs; i++) {
+		struct irq_data *d = irq_domain_get_irq_data(domain, virq + i);
+
+		irq_set_handler(virq + i, NULL);
+		irq_domain_reset_irq_data(d);
+	}
+}
+
+static const struct irq_domain_ops gicv5_irq_lpi_domain_ops = {
+	.alloc	= gicv5_irq_lpi_domain_alloc,
+	.free	= gicv5_irq_lpi_domain_free,
+};
+
+void __init gicv5_init_lpi_domain(void)
+{
+	gicv5_global_data.lpi_domain = irq_domain_create_tree(NULL,
+				&gicv5_irq_lpi_domain_ops, NULL);
+}
+
+void __init gicv5_free_lpi_domain(void)
+{
+	irq_domain_remove(gicv5_global_data.lpi_domain);
+}
+
+static int gicv5_irq_ipi_domain_alloc(struct irq_domain *domain,
+				      unsigned int virq, unsigned int nr_irqs,
+				      void *arg)
+{
+	int ret, i;
+	u32 lpi;
+	struct irq_data *irqd = irq_desc_get_irq_data(irq_to_desc(virq));
+
+	// Get LPIs for the IPIs
+	ret = gicv5_alloc_lpi_range(nr_irqs, &lpi);
+	if (ret)
+		return ret;
+
+	ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &lpi);
+	if (ret) {
+		gicv5_free_lpi_range(lpi, nr_irqs);
+		return ret;
+	}
+
+	for (i = 0; i < nr_irqs; i++) {
+		irqd = irq_desc_get_irq_data(irq_to_desc(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)
+{
+	for (unsigned int i = 0; i < nr_irqs; i++) {
+		struct irq_data *d = irq_domain_get_irq_data(domain, virq + i);
+
+		if (WARN_ON(!d))
+			return;
+
+		gicv5_free_lpi_range(d->parent_data->hwirq, 1);
+
+		irq_set_handler(virq + i, NULL);
+		irq_domain_reset_irq_data(d);
+	}
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops gicv5_irq_ipi_domain_ops = {
+	.alloc	= gicv5_irq_ipi_domain_alloc,
+	.free	= gicv5_irq_ipi_domain_free,
+};
+
 static inline void handle_irq_per_domain(u32 hwirq)
 {
 	u32 hwirq_id;
@@ -619,6 +920,8 @@ static inline void handle_irq_per_domain(u32 hwirq)
 		domain = gicv5_global_data.ppi_domain;
 	else if (hwirq_type == GICV5_HWIRQ_TYPE_SPI)
 		domain = gicv5_global_data.spi_domain;
+	else if (hwirq_type == GICV5_HWIRQ_TYPE_LPI)
+		domain = gicv5_global_data.lpi_domain;
 
 	if (generic_handle_domain_irq(domain, hwirq_id)) {
 		pr_err("Could not handle, hwirq = 0x%x", hwirq_id);
@@ -706,6 +1009,8 @@ 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);
 }
 
 static int __init gicv5_init_domains(struct fwnode_handle *handle)
@@ -732,6 +1037,19 @@ static int __init gicv5_init_domains(struct fwnode_handle *handle)
 					    DOMAIN_BUS_WIRED);
 	}
 
+	if (!WARN(!gicv5_global_data.lpi_domain,
+		  "LPI domain uninitialized, can't set up IPIs")) {
+		gicv5_global_data.ipi_domain = 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 (WARN_ON(!gicv5_global_data.ipi_domain)) {
+			gicv5_free_domains();
+			return -ENOMEM;
+		}
+	}
+
 	return 0;
 }
 
@@ -753,6 +1071,25 @@ 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)
 {
@@ -769,6 +1106,7 @@ static int __init gicv5_of_init(struct device_node *node,
 	}
 
 	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);
@@ -781,6 +1119,13 @@ static int __init gicv5_of_init(struct device_node *node,
 	}
 
 	ret = set_handle_irq(gicv5_handle_irq);
+	if (ret) {
+		gicv5_irs_remove();
+		gicv5_free_domains();
+		gicv5_cpu_disable_interrupts();
+	}
+
+	ret = gicv5_irs_enable();
 	if (ret) {
 		gicv5_irs_remove();
 		gicv5_free_domains();
diff --git a/drivers/irqchip/irq-gic-v5.h b/drivers/irqchip/irq-gic-v5.h
index 57e2472f1f2a9984f399d2a8633c824bc208da26..8376da2a342d14568a71c0bc85b79c4e7b83a66c 100644
--- a/drivers/irqchip/irq-gic-v5.h
+++ b/drivers/irqchip/irq-gic-v5.h
@@ -6,12 +6,16 @@
 #define __LINUX_IRQCHIP_GIC_V5_H
 
 #include <asm/arch_gicv5.h>
+#include <asm/smp.h>
+
+#define GICV5_IPIS_PER_CPU		MAX_IPI
 
 #define GICV5_HWIRQ_ID			GENMASK(23, 0)
 #define GICV5_HWIRQ_TYPE		GENMASK(31, 29)
 #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)
 
 #define GICV5_PPI_HM_EDGE		UL(0x0)
@@ -43,6 +47,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)
@@ -53,10 +61,22 @@
 #define GICV5_IRS_IDR1_PRIORITY_BITS_4BITS	0b011
 #define GICV5_IRS_IDR1_PRIORITY_BITS_5BITS	0b100
 
+#define GICV5_IRS_IDR2_ISTMD_SZ		GENMASK(19, 15)
+#define GICV5_IRS_IDR2_ISTMD		BIT(14)
+#define GICV5_IRS_IDR2_IST_L2SZ		GENMASK(13, 11)
+#define GICV5_IRS_IDR2_IST_LEVELS	BIT(10)
+#define GICV5_IRS_IDR2_MIN_LPI_ID_BITS	GENMASK(9, 6)
+#define GICV5_IRS_IDR2_LPI		BIT(5)
+#define GICV5_IRS_IDR2_ID_BITS		GENMASK(4, 0)
+
 #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)
 
@@ -88,14 +108,50 @@
 
 #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)
+
 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;
 	u8			irs_iaffid_bits;
+	struct {
+		__le64 *l1ist_addr;
+		u32 l2_size;
+		u8 l2_bits;
+		bool l2;
+	} ist;
 };
 
 extern struct gicv5_chip_data gicv5_global_data __read_mostly;
@@ -110,11 +166,19 @@ struct gicv5_irs_chip_data {
 	raw_spinlock_t		spi_config_lock;
 };
 
+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_spi_set_type(struct irq_data *d, unsigned int type);
+int gicv5_irs_iste_alloc(u32 lpi);
 
+void gicv5_init_lpi(u32 max);
+void gicv5_free_lpi(void);
 #endif

-- 
2.48.0



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 21/24] irqchip/gic-v5: Enable GICv5 SMP booting
  2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (19 preceding siblings ...)
  2025-04-08 10:50 ` [PATCH 20/24] irqchip/gic-v5: Add GICv5 LPI/IPI support Lorenzo Pieralisi
@ 2025-04-08 10:50 ` Lorenzo Pieralisi
  2025-04-08 10:50 ` [PATCH 22/24] irqchip/gic-v5: Add GICv5 ITS support Lorenzo Pieralisi
                   ` (2 subsequent siblings)
  23 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:50 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

Set up IPIs by allocating IPI IRQs for all cpus and call into
arm64 core code to initialise IPIs IRQ descriptors and
request the related IRQ.

Implement hotplug callback to enable interrupts on a cpu
and register the cpu with an IRS.

Implement hotplug callback to check the IRS affinity setup
for IPIs on each CPU in order to ensure that each CPU's IPIs
affinity is what it is expected to be.

Co-developed-by: Sascha Bischoff <sascha.bischoff@arm.com>
Signed-off-by: Sascha Bischoff <sascha.bischoff@arm.com>
Co-developed-by: Timothy Hayes <timothy.hayes@arm.com>
Signed-off-by: Timothy Hayes <timothy.hayes@arm.com>
Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Marc Zyngier <maz@kernel.org>
---
 drivers/irqchip/irq-gic-v5.c | 84 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 84 insertions(+)

diff --git a/drivers/irqchip/irq-gic-v5.c b/drivers/irqchip/irq-gic-v5.c
index 85d951bfd807fc1b67b616dbadeebc150848293f..24789e9590115e6de7007cd4a74376ae34702ed6 100644
--- a/drivers/irqchip/irq-gic-v5.c
+++ b/drivers/irqchip/irq-gic-v5.c
@@ -5,6 +5,7 @@
 
 #define pr_fmt(fmt)	"GICv5: " fmt
 
+#include <linux/cpuhotplug.h>
 #include <linux/irqchip.h>
 #include <linux/irqdomain.h>
 #include <linux/maple_tree.h>
@@ -992,6 +993,68 @@ static void gicv5_cpu_enable_interrupts(void)
 	write_sysreg_s(cr0, SYS_ICC_CR0_EL1);
 }
 
+static int base_ipi_virq;
+
+static int gicv5_check_ipi_affinity(unsigned int cpu)
+{
+	int ret;
+	u64 cdrcfg, icsr;
+	u16 programmed_iaffid, requested_iaffid;
+	struct irq_data *d;
+	unsigned int i, virq_base = base_ipi_virq + cpu * GICV5_IPIS_PER_CPU;
+
+	for (i = 0; i < GICV5_IPIS_PER_CPU; i++) {
+		d = irq_get_irq_data(virq_base + i);
+
+		if (WARN_ON(!d))
+			return -ENODEV;
+
+		/*
+		 * We need to know the actual LPI that is being used, rather
+		 * than the IPI domain hwirq.
+		 *
+		 * Hence, use the hwirq from the parent (LPI domain) here.
+		 */
+		cdrcfg = d->parent_data->hwirq |
+			 FIELD_PREP(GICV5_HWIRQ_TYPE, GICV5_HWIRQ_TYPE_LPI);
+		preempt_disable();
+		gic_insn(cdrcfg, GICV5_OP_GIC_CDRCFG);
+		isb();
+		icsr = read_sysreg_s(SYS_ICC_ICSR_EL1);
+		preempt_enable();
+
+		if (FIELD_GET(ICC_ICSR_EL1_F, icsr)) {
+			pr_err("SYS_ID_ICC_ICSR_EL1 is not valid");
+			return -ENXIO;
+		}
+
+		if (!FIELD_GET(ICC_ICSR_EL1_Enabled, icsr)) {
+			pr_err("interrupt is disabled");
+			return -ENXIO;
+		}
+
+		if (FIELD_GET(ICC_ICSR_EL1_IRM, icsr)) {
+			pr_debug("Interrupt not using targeted routing");
+			return -ENXIO;
+		}
+
+		programmed_iaffid = (u16)FIELD_GET(ICC_ICSR_EL1_IAFFID, icsr);
+
+		ret = gicv5_irs_cpu_to_iaffid(cpu, &requested_iaffid);
+		if (ret)
+			return ret;
+
+		if (programmed_iaffid != requested_iaffid) {
+			pr_err("Mismatch between programmed_iaffid (%u) and requested_iaffid (%u)",
+			       programmed_iaffid, requested_iaffid);
+
+			return -EIO;
+		}
+	}
+
+	return 0;
+}
+
 static int gicv5_starting_cpu(unsigned int cpu)
 {
 	if (WARN(!gicv5_cpuif_has_gcie(),
@@ -1003,6 +1066,25 @@ static int gicv5_starting_cpu(unsigned int cpu)
 	return gicv5_irs_register_cpu(cpu);
 }
 
+static void __init gicv5_smp_init(void)
+{
+	unsigned int num_ipis = GICV5_IPIS_PER_CPU * nr_cpu_ids;
+
+	cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GIC_STARTING,
+				  "irqchip/arm/gicv5:starting",
+				  gicv5_starting_cpu, NULL);
+
+	base_ipi_virq = irq_domain_alloc_irqs(gicv5_global_data.ipi_domain,
+					  num_ipis, NUMA_NO_NODE, NULL);
+	if (WARN(base_ipi_virq <= 0, "IPI IRQ allocation was not successful"))
+		return;
+
+	set_smp_ipi_range_percpu(base_ipi_virq, GICV5_IPIS_PER_CPU, nr_cpu_ids);
+
+	cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "irqchip/arm/gicv5:online",
+				  gicv5_check_ipi_affinity, NULL);
+}
+
 static void __init gicv5_free_domains(void)
 {
 	if (gicv5_global_data.ppi_domain)
@@ -1133,6 +1215,8 @@ static int __init gicv5_of_init(struct device_node *node,
 		return ret;
 	}
 
+	gicv5_smp_init();
+
 	return 0;
 }
 IRQCHIP_DECLARE(gic_v5, "arm,gic-v5", gicv5_of_init);

-- 
2.48.0



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 22/24] irqchip/gic-v5: Add GICv5 ITS support
  2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (20 preceding siblings ...)
  2025-04-08 10:50 ` [PATCH 21/24] irqchip/gic-v5: Enable GICv5 SMP booting Lorenzo Pieralisi
@ 2025-04-08 10:50 ` Lorenzo Pieralisi
  2025-04-09 11:13   ` Thomas Gleixner
  2025-04-09 18:57   ` Thomas Gleixner
  2025-04-08 10:50 ` [PATCH 23/24] irqchip/gic-v5: Add GICv5 IWB support Lorenzo Pieralisi
  2025-04-08 10:50 ` [PATCH 24/24] arm64: Kconfig: Enable GICv5 Lorenzo Pieralisi
  23 siblings, 2 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:50 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

The GICv5 architecture implements Interrupt Translation Service
(ITS) components in order to translate events coming from peripherals
into interrupt events delivered to the connected IRSes.

Events (ie MSI memory writes to ITS translate frame), are translated
by the ITS using tables kept in memory.

ITS translation tables for peripherals is kept in memory storage
(device table [DT] and Interrupt Translation Table [ITT]) that
is allocated by the driver on boot.

Both tables can be 1- or 2-level; the structure is chosen by the
driver after probing the ITS HW parameters and checking the
allowed table splits and supported {device/event}_IDbits.

DT table entries are allocated on demand (ie when a device is
probed); the DT table is sized using the number of supported
deviceID bits in that that's a system design decision (ie the
number of deviceID bits implemented should reflect the number
of devices expected in a system) therefore it makes sense to
allocate a DT table that can cater for the maximum number of
devices.

DT and ITT tables are allocated using the kmalloc interface;
the allocation size may be smaller than a page or larger,
and must provide contiguous memory pages.

LPIs INTIDs backing the device events are allocated one-by-one
and only upon Linux IRQ allocation; this to avoid preallocating
a large number of LPIs to cover the HW device MSI vector
size whereas few MSI entries are actually enabled by a device.

ITS cacheability/shareability attributes are programmed
according to the provided firmware ITS description.

The GICv5 ITS reuses the GICv3 MSI parent infrastructure,
there is no need to duplicate it, make it common.

Co-developed-by: Sascha Bischoff <sascha.bischoff@arm.com>
Signed-off-by: Sascha Bischoff <sascha.bischoff@arm.com>
Co-developed-by: Timothy Hayes <timothy.hayes@arm.com>
Signed-off-by: Timothy Hayes <timothy.hayes@arm.com>
Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Marc Zyngier <maz@kernel.org>
---
 MAINTAINERS                                        |    1 +
 drivers/irqchip/Kconfig                            |   11 +
 drivers/irqchip/Makefile                           |    4 +-
 drivers/irqchip/irq-gic-common.h                   |    2 -
 ...3-its-msi-parent.c => irq-gic-its-msi-parent.c} |    3 +-
 drivers/irqchip/irq-gic-its-msi-parent.h           |   13 +
 drivers/irqchip/irq-gic-v3-its.c                   |    3 +-
 drivers/irqchip/irq-gic-v5-irs.c                   |   32 +
 drivers/irqchip/irq-gic-v5-its.c                   | 1294 ++++++++++++++++++++
 drivers/irqchip/irq-gic-v5.c                       |    6 +-
 drivers/irqchip/irq-gic-v5.h                       |  160 +++
 11 files changed, 1521 insertions(+), 8 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index cdeceb6782355a4a18609135bf7f03249d8b0bb5..d231077c024deba42153663ac66b6c05f7673f03 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1908,6 +1908,7 @@ L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:	Maintained
 F:	Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.yaml
 F:	arch/arm64/include/asm/arch_gicv5.h
+F:	drivers/irqchip/irq-gic-its-msi-parent.[ch]
 F:	drivers/irqchip/irq-gic-v5*.[ch]
 
 ARM HDLCD DRM DRIVER
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 160a4761d5d85f6dbf36f3142fd619c114733e36..6c348d421b05af0e4f4909877e02ac8ef19178ff 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -41,10 +41,14 @@ config ARM_GIC_V3
 	select HAVE_ARM_SMCCC_DISCOVERY
 	select IRQ_MSI_IOMMU
 
+config ARM_GIC_ITS_PARENT
+	bool
+
 config ARM_GIC_V3_ITS
 	bool
 	select GENERIC_MSI_IRQ
 	select IRQ_MSI_LIB
+	select ARM_GIC_ITS_PARENT
 	default ARM_GIC_V3
 	select IRQ_MSI_IOMMU
 
@@ -59,6 +63,13 @@ config ARM_GIC_V5
 	select IRQ_DOMAIN_HIERARCHY
 	select GENERIC_IRQ_EFFECTIVE_AFF_MASK if SMP
 
+config ARM_GIC_V5_ITS
+	bool
+	select GENERIC_MSI_IRQ
+	select IRQ_MSI_LIB
+	select ARM_GIC_ITS_PARENT
+	default ARM_GIC_V5
+
 config ARM_NVIC
 	bool
 	select IRQ_DOMAIN_HIERARCHY
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 3d9c47fa3fdf40b7452c059d84fe8ac24c91bc0f..4280395e3bdff7858102f0b4eaaea1121cace52f 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -32,10 +32,12 @@ obj-$(CONFIG_ARCH_REALVIEW)		+= irq-gic-realview.o
 obj-$(CONFIG_IRQ_MSI_LIB)		+= irq-msi-lib.o
 obj-$(CONFIG_ARM_GIC_V2M)		+= irq-gic-v2m.o
 obj-$(CONFIG_ARM_GIC_V3)		+= irq-gic-v3.o irq-gic-v3-mbi.o irq-gic-common.o
-obj-$(CONFIG_ARM_GIC_V3_ITS)		+= irq-gic-v3-its.o irq-gic-v4.o irq-gic-v3-its-msi-parent.o
+obj-$(CONFIG_ARM_GIC_ITS_PARENT)	+= irq-gic-its-msi-parent.o
+obj-$(CONFIG_ARM_GIC_V3_ITS)		+= irq-gic-v3-its.o irq-gic-v4.o
 obj-$(CONFIG_ARM_GIC_V3_ITS_FSL_MC)	+= irq-gic-v3-its-fsl-mc-msi.o
 obj-$(CONFIG_PARTITION_PERCPU)		+= irq-partition-percpu.o
 obj-$(CONFIG_ARM_GIC_V5)		+= irq-gic-v5.o irq-gic-v5-irs.o
+obj-$(CONFIG_ARM_GIC_V5_ITS)		+= irq-gic-v5-its.o
 obj-$(CONFIG_HISILICON_IRQ_MBIGEN)	+= irq-mbigen.o
 obj-$(CONFIG_ARM_NVIC)			+= irq-nvic.o
 obj-$(CONFIG_ARM_VIC)			+= irq-vic.o
diff --git a/drivers/irqchip/irq-gic-common.h b/drivers/irqchip/irq-gic-common.h
index 020ecdf16901c9720e5746aec4d0b5b39d3625ed..710cab61d9195a0bd64d57e03c60852c4cd6ff8e 100644
--- a/drivers/irqchip/irq-gic-common.h
+++ b/drivers/irqchip/irq-gic-common.h
@@ -29,8 +29,6 @@ void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks,
 void gic_enable_of_quirks(const struct device_node *np,
 			  const struct gic_quirk *quirks, void *data);
 
-extern const struct msi_parent_ops gic_v3_its_msi_parent_ops;
-
 #define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING    (1 << 0)
 #define RDIST_FLAGS_RD_TABLES_PREALLOCATED     (1 << 1)
 #define RDIST_FLAGS_FORCE_NON_SHAREABLE        (1 << 2)
diff --git a/drivers/irqchip/irq-gic-v3-its-msi-parent.c b/drivers/irqchip/irq-gic-its-msi-parent.c
similarity index 98%
rename from drivers/irqchip/irq-gic-v3-its-msi-parent.c
rename to drivers/irqchip/irq-gic-its-msi-parent.c
index bdb04c8081480de468fb217b68c6933a8e1e2bd7..71edcdb2defdfd5b892d86354039d2e46b832ea5 100644
--- a/drivers/irqchip/irq-gic-v3-its-msi-parent.c
+++ b/drivers/irqchip/irq-gic-its-msi-parent.c
@@ -7,7 +7,6 @@
 #include <linux/acpi_iort.h>
 #include <linux/pci.h>
 
-#include "irq-gic-common.h"
 #include "irq-msi-lib.h"
 
 #define ITS_MSI_FLAGS_REQUIRED  (MSI_FLAG_USE_DEF_DOM_OPS |	\
@@ -200,7 +199,7 @@ static bool its_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
 	return true;
 }
 
-const struct msi_parent_ops gic_v3_its_msi_parent_ops = {
+const struct msi_parent_ops gic_its_msi_parent_ops = {
 	.supported_flags	= ITS_MSI_FLAGS_SUPPORTED,
 	.required_flags		= ITS_MSI_FLAGS_REQUIRED,
 	.chip_flags		= MSI_CHIP_FLAG_SET_EOI | MSI_CHIP_FLAG_SET_ACK,
diff --git a/drivers/irqchip/irq-gic-its-msi-parent.h b/drivers/irqchip/irq-gic-its-msi-parent.h
new file mode 100644
index 0000000000000000000000000000000000000000..e7bb7f3862eef379e5b85fe7bd5eb72f3586d3b7
--- /dev/null
+++ b/drivers/irqchip/irq-gic-its-msi-parent.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2024 ARM Limited, All Rights Reserved.
+ */
+
+#ifndef _IRQ_GIC_ITS_MSI_PARENT_H
+#define _IRQ_GIC_ITS_MSI_PARENT_H
+
+#include <linux/msi.h>
+
+extern const struct msi_parent_ops gic_its_msi_parent_ops;
+
+#endif /* _IRQ_GIC_ITS_MSI_PARENT_H */
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 0115ad6c82593de511c285d99437996919bfa308..6c51bf4e34a38103d612c74476d640cd4126e8b6 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -41,6 +41,7 @@
 #include <asm/exception.h>
 
 #include "irq-gic-common.h"
+#include "irq-gic-its-msi-parent.h"
 #include "irq-msi-lib.h"
 
 #define ITS_FLAGS_CMDQ_NEEDS_FLUSHING		(1ULL << 0)
@@ -5139,7 +5140,7 @@ static int its_init_domain(struct its_node *its)
 
 	irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_NEXUS);
 
-	inner_domain->msi_parent_ops = &gic_v3_its_msi_parent_ops;
+	inner_domain->msi_parent_ops = &gic_its_msi_parent_ops;
 	inner_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT;
 
 	return 0;
diff --git a/drivers/irqchip/irq-gic-v5-irs.c b/drivers/irqchip/irq-gic-v5-irs.c
index 909ccf07879a1feb5abb65fa265706f2c276d927..3078baa57ec669db962796efe72f881342217a52 100644
--- a/drivers/irqchip/irq-gic-v5-irs.c
+++ b/drivers/irqchip/irq-gic-v5-irs.c
@@ -507,6 +507,30 @@ static int gicv5_irs_wait_for_idle(struct gicv5_irs_chip_data *irs_data)
 	return ret;
 }
 
+void gicv5_irs_syncr(void)
+{
+	u32 syncr;
+	u32 statusr;
+	int ret;
+	struct gicv5_irs_chip_data *irs_data;
+
+	irs_data = list_first_entry_or_null(&irs_nodes,
+					    struct gicv5_irs_chip_data, entry);
+	if (WARN_ON(!irs_data))
+		return;
+
+	syncr = FIELD_PREP(GICV5_IRS_SYNCR_SYNC, 1);
+	irs_writel(irs_data, syncr, GICV5_IRS_SYNCR);
+
+	ret = readl_relaxed_poll_timeout_atomic(
+			irs_data->irs_base + GICV5_IRS_SYNC_STATUSR, statusr,
+			FIELD_GET(GICV5_IRS_SYNC_STATUSR_IDLE, statusr), 1,
+			USEC_PER_SEC);
+
+	if (ret == -ETIMEDOUT)
+		pr_err_ratelimited("SYNCR timeout...\n");
+}
+
 int gicv5_irs_register_cpu(int cpuid)
 {
 	struct gicv5_irs_chip_data *irs_data;
@@ -803,6 +827,14 @@ int __init gicv5_irs_enable(void)
 	return 0;
 }
 
+void __init gicv5_irs_its_probe(void)
+{
+	struct gicv5_irs_chip_data *irs_data;
+
+	list_for_each_entry(irs_data, &irs_nodes, entry)
+		gicv5_its_of_probe(to_of_node(irs_data->fwnode));
+}
+
 int __init gicv5_irs_of_probe(struct device_node *parent)
 {
 	int ret;
diff --git a/drivers/irqchip/irq-gic-v5-its.c b/drivers/irqchip/irq-gic-v5-its.c
new file mode 100644
index 0000000000000000000000000000000000000000..f4f381e6c3af896f354e7e03bc2452b8a65cf093
--- /dev/null
+++ b/drivers/irqchip/irq-gic-v5-its.c
@@ -0,0 +1,1294 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024-2025 ARM Limited, All Rights Reserved.
+ */
+
+#define pr_fmt(fmt)	"GICv5 ITS: " fmt
+
+#include <linux/bitmap.h>
+#include <linux/iommu.h>
+#include <linux/iopoll.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/msi.h>
+
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#include <linux/slab.h>
+
+#include <linux/irqchip.h>
+
+#include "irq-gic-v5.h"
+#include "irq-gic-its-msi-parent.h"
+#include "irq-msi-lib.h"
+
+#define ITS_FLAGS_NON_COHERENT		BIT(0)
+
+static LIST_HEAD(its_nodes);
+
+static u32 its_readl(struct gicv5_its_chip_data *its_node, const u64 reg_offset)
+{
+	return readl_relaxed(its_node->its_base + reg_offset);
+}
+
+static void its_writel(struct gicv5_its_chip_data *its_node, const u32 val,
+		       const u64 reg_offset)
+{
+	writel_relaxed(val, its_node->its_base + reg_offset);
+}
+
+static void its_writeq(struct gicv5_its_chip_data *its_node, const u64 val,
+		       const u64 reg_offset)
+{
+	writeq_relaxed(val, its_node->its_base + reg_offset);
+}
+
+static void its_write_table_entry(struct gicv5_its_chip_data *its,
+				  __le64 *entry, u64 val)
+{
+	WRITE_ONCE(*entry, val);
+	if (its->flags & ITS_FLAGS_NON_COHERENT)
+		dcache_clean_inval_poc((unsigned long)entry,
+				       (unsigned long)entry + sizeof(*entry));
+	else
+		dsb(ishst);
+}
+
+static int gicv5_its_wait_for_invalidation(struct gicv5_its_chip_data *its)
+{
+	int ret;
+	u32 statusr;
+
+	ret = readl_relaxed_poll_timeout_atomic(
+			its->its_base + GICV5_ITS_STATUSR, statusr,
+			FIELD_GET(GICV5_ITS_STATUSR_IDLE, statusr), 1,
+			USEC_PER_SEC);
+
+	if (ret == -ETIMEDOUT)
+		pr_err_ratelimited("STATUSR timeout...\n");
+
+	return ret;
+}
+
+static void gicv5_its_syncr(struct gicv5_its_chip_data *its,
+			    struct gicv5_its_dev *its_dev)
+{
+	int ret;
+	u64 syncr;
+	u32 statusr;
+
+	syncr = FIELD_PREP(GICV5_ITS_SYNCR_SYNC, 1) |
+		FIELD_PREP(GICV5_ITS_SYNCR_DEVICEID, its_dev->device_id);
+
+	its_writeq(its, syncr, GICV5_ITS_SYNCR);
+
+	ret = readl_relaxed_poll_timeout_atomic(
+			its->its_base + GICV5_ITS_SYNC_STATUSR, statusr,
+			FIELD_GET(GICV5_ITS_SYNC_STATUSR_IDLE, statusr), 1,
+			USEC_PER_SEC);
+
+	if (ret == -ETIMEDOUT)
+		pr_err_ratelimited("SYNCR timeout...\n");
+}
+
+static int gicv5_its_l2sz_to_l2_bits(unsigned int sz)
+{
+	switch (sz) {
+	case GICV5_ITS_DT_ITT_CFGR_L2SZ_4k:
+		return 9;
+	case GICV5_ITS_DT_ITT_CFGR_L2SZ_16k:
+		return 11;
+	case GICV5_ITS_DT_ITT_CFGR_L2SZ_64k:
+		return 13;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int gicv5_its_itt_cache_inv(struct gicv5_its_chip_data *its,
+				   u32 device_id, u16 event_id)
+{
+	u64 didr;
+	u32 eventr, eidr;
+
+	didr = FIELD_PREP(GICV5_ITS_DIDR_DEVICEID, device_id);
+	eidr = FIELD_PREP(GICV5_ITS_EIDR_EVENTID, event_id);
+	eventr = FIELD_PREP(GICV5_ITS_INV_EVENTR_I, 0x1);
+
+	its_writeq(its, didr, GICV5_ITS_DIDR);
+	its_writel(its, eidr, GICV5_ITS_EIDR);
+	its_writel(its, eventr, GICV5_ITS_INV_EVENTR);
+
+	return gicv5_its_wait_for_invalidation(its);
+}
+
+static void gicv5_its_free_itt_linear(struct gicv5_its_dev *its_dev)
+{
+	kfree(its_dev->itt_cfg.linear.itt);
+}
+
+static void gicv5_its_free_itt_two_level(struct gicv5_its_dev *its_dev)
+{
+	unsigned int i, num_ents = its_dev->itt_cfg.l2.num_l1_ents;
+
+	for (i = 0; i < num_ents; i++)
+		kfree(its_dev->itt_cfg.l2.l2ptrs[i]);
+
+	kfree(its_dev->itt_cfg.l2.l2ptrs);
+	kfree(its_dev->itt_cfg.l2.l1itt);
+}
+
+static void gicv5_its_free_itt(struct gicv5_its_dev *its_dev)
+{
+	if (!its_dev->itt_cfg.l2itt)
+		gicv5_its_free_itt_linear(its_dev);
+	else
+		gicv5_its_free_itt_two_level(its_dev);
+}
+
+static int gicv5_its_create_itt_linear(struct gicv5_its_chip_data *its,
+				       struct gicv5_its_dev *its_dev,
+				       unsigned int event_id_bits)
+{
+	__le64 *itt;
+	unsigned int num_ents = BIT(event_id_bits);
+
+	itt = kcalloc(num_ents, sizeof(*itt), GFP_KERNEL);
+	if (!itt)
+		return -ENOMEM;
+
+	its_dev->itt_cfg.linear.itt = itt;
+	its_dev->itt_cfg.linear.num_ents = num_ents;
+	its_dev->itt_cfg.l2itt = false;
+	its_dev->itt_cfg.event_id_bits = event_id_bits;
+
+	if (its->flags & ITS_FLAGS_NON_COHERENT)
+		dcache_clean_inval_poc((unsigned long)itt,
+				(unsigned long)itt + num_ents * sizeof(*itt));
+	else
+		dsb(ishst);
+
+	return 0;
+}
+
+/*
+ * Allocate a two-level ITT. All ITT entries are allocated in one go, unlike
+ * with the device table. Span may be used to limit the second level table
+ * size, where possible.
+ */
+static int gicv5_its_create_itt_two_level(struct gicv5_its_chip_data *its,
+					  struct gicv5_its_dev *its_dev,
+					  unsigned int event_id_bits,
+					  unsigned int itt_l2sz,
+					  unsigned int num_events)
+{
+	u64 val;
+	int ret, i;
+	__le64 *itt_l1, *itt_l2, **l2ptrs;
+	unsigned int l1_bits, l2_bits, span, events_per_l2_table,
+		complete_tables, final_span, num_ents;
+
+	ret = gicv5_its_l2sz_to_l2_bits(itt_l2sz);
+	if (ret < 0 || ret >= event_id_bits) {
+		pr_debug("Incorrect l2sz (0x%x) for %u EventID bits. Cannot allocate ITT\n",
+		       itt_l2sz, event_id_bits);
+		return -EINVAL;
+	}
+
+	l2_bits = ret;
+
+	l1_bits = event_id_bits - l2_bits;
+
+	num_ents = BIT(l1_bits);
+
+	itt_l1 = kcalloc(num_ents, sizeof(*itt_l1), GFP_KERNEL);
+	if (!itt_l1)
+		return -ENOMEM;
+
+	l2ptrs = kcalloc(num_ents, sizeof(*l2ptrs), GFP_KERNEL);
+	if (!l2ptrs) {
+		kfree(itt_l1);
+		return -ENOMEM;
+	}
+
+	its_dev->itt_cfg.l2.l2ptrs = l2ptrs;
+
+	its_dev->itt_cfg.l2.l2sz = itt_l2sz;
+	its_dev->itt_cfg.l2.l1itt = itt_l1;
+	its_dev->itt_cfg.l2.num_l1_ents = num_ents;
+	its_dev->itt_cfg.l2itt = true;
+	its_dev->itt_cfg.event_id_bits = event_id_bits;
+
+	/*
+	 * Need to determine how many entries there are per L2 - this is based
+	 * on the number of bits in the table.
+	 */
+	events_per_l2_table = BIT(l2_bits);
+	complete_tables = num_events / events_per_l2_table;
+	final_span = order_base_2(num_events % events_per_l2_table);
+
+	for (i = 0; i < num_ents; i++) {
+		size_t l2sz;
+
+		span = i == complete_tables ? final_span : l2_bits;
+
+		itt_l2 = kcalloc(BIT(span), sizeof(*itt_l2), GFP_KERNEL);
+		if (!itt_l2) {
+			ret = -ENOMEM;
+			goto out_free;
+		}
+
+		its_dev->itt_cfg.l2.l2ptrs[i] = itt_l2;
+
+		l2sz = BIT(span) * sizeof(*itt_l2);
+
+		if (its->flags & ITS_FLAGS_NON_COHERENT)
+			dcache_clean_inval_poc((unsigned long)itt_l2,
+				(unsigned long)itt_l2 + l2sz);
+
+		val = (virt_to_phys(itt_l2) & GICV5_ITTL1E_L2_ADDR_MASK) |
+		       FIELD_PREP(GICV5_ITTL1E_SPAN, span)		 |
+		       FIELD_PREP(GICV5_ITTL1E_VALID, 0x1);
+
+		WRITE_ONCE(itt_l1[i], cpu_to_le64(val));
+	}
+
+	if (its->flags & ITS_FLAGS_NON_COHERENT)
+		dcache_clean_inval_poc((unsigned long)itt_l1,
+			(unsigned long)itt_l1 +	 num_ents * sizeof(*itt_l1));
+	else
+		dsb(ishst);
+
+	return 0;
+out_free:
+	for (i = i - 1; i >= 0; i--)
+		kfree(its_dev->itt_cfg.l2.l2ptrs[i]);
+
+	kfree(its_dev->itt_cfg.l2.l2ptrs);
+	kfree(itt_l1);
+	return ret;
+}
+
+/*
+ * Function to check whether the device table or ITT table support
+ * a two-level table and if so depending on the number of id_bits
+ * requested, determine whether a two-level table is required.
+ *
+ * Return the 2-level size value if a two level table is deemed
+ * necessary.
+ */
+static bool gicv5_its_l2sz_two_level(bool devtab, u32 its_idr1, u8 id_bits,
+				     u8 *sz)
+{
+	int l2_bits, l2_sz = -EINVAL;
+
+	if (devtab && !FIELD_GET(GICV5_ITS_IDR1_DT_LEVELS, its_idr1))
+		return false;
+
+	if (!devtab && !FIELD_GET(GICV5_ITS_IDR1_ITT_LEVELS, its_idr1))
+		return false;
+
+	/*
+	 * Pick an L2 size that matches the pagesize; if a match
+	 * is not found, go for the smallest supported l2 size granule.
+	 *
+	 * This ensures that we will always be able to allocate
+	 * contiguous memory at L2.
+	 */
+	switch (PAGE_SIZE) {
+	case SZ_64K:
+		if (GICV5_ITS_IDR1_L2SZ_SUPPORT_64KB(its_idr1)) {
+			l2_sz = GICV5_ITS_DT_ITT_CFGR_L2SZ_64k;
+			break;
+		}
+		fallthrough;
+	case SZ_16K:
+		if (GICV5_ITS_IDR1_L2SZ_SUPPORT_16KB(its_idr1)) {
+			l2_sz = GICV5_ITS_DT_ITT_CFGR_L2SZ_16k;
+			break;
+		}
+		fallthrough;
+	case SZ_4K:
+		if (GICV5_ITS_IDR1_L2SZ_SUPPORT_4KB(its_idr1)) {
+			l2_sz = GICV5_ITS_DT_ITT_CFGR_L2SZ_4k;
+			break;
+		}
+		if (GICV5_ITS_IDR1_L2SZ_SUPPORT_16KB(its_idr1)) {
+			l2_sz = GICV5_ITS_DT_ITT_CFGR_L2SZ_16k;
+			break;
+		}
+		if (GICV5_ITS_IDR1_L2SZ_SUPPORT_64KB(its_idr1)) {
+			l2_sz = GICV5_ITS_DT_ITT_CFGR_L2SZ_64k;
+			break;
+		}
+		break;
+	}
+
+	l2_bits = gicv5_its_l2sz_to_l2_bits(l2_sz);
+
+	if (l2_bits < 0 || l2_bits > id_bits)
+		return false;
+
+	*sz = l2_sz;
+
+	return true;
+}
+
+static int gicv5_its_device_get_itte_ref(struct gicv5_its_dev *its_dev,
+					 __le64 **itte, u16 event_id)
+{
+	if (!its_dev->itt_cfg.l2itt) {
+		__le64 *itt = its_dev->itt_cfg.linear.itt;
+		*itte = &itt[event_id];
+	} else {
+		unsigned int l1_idx, l2_idx, l2_size, l2_bits;
+		__le64 *l2_itt, *l1_itt = its_dev->itt_cfg.l2.l1itt;
+		int ret;
+
+		ret = gicv5_its_l2sz_to_l2_bits(its_dev->itt_cfg.l2.l2sz);
+		if (ret < 0)
+			return ret;
+		l2_bits = ret;
+
+		l1_idx = event_id >> l2_bits;
+
+		if (!FIELD_GET(GICV5_ITTL1E_VALID,
+			       le64_to_cpu(l1_itt[l1_idx]))) {
+			pr_debug("L1 ITT entry is not valid.\n");
+			return -EINVAL;
+		}
+
+		l2_idx = event_id & GENMASK(l2_bits - 1, 0);
+
+		l2_size = BIT(FIELD_GET(GICV5_ITTL1E_SPAN,
+					le64_to_cpu(l1_itt[l1_idx])));
+
+		// Sanity check our indexing
+		if (l2_idx >= l2_size) {
+			pr_debug("L2 ITT index (%u) exceeds L2 table size (%u)!\n",
+			       l2_idx, l2_size);
+			return -EINVAL;
+		}
+		l2_itt = its_dev->itt_cfg.l2.l2ptrs[l1_idx];
+		*itte = &l2_itt[l2_idx];
+	}
+
+	return 0;
+}
+
+static int gicv5_its_device_cache_inv(struct gicv5_its_chip_data *its,
+				      struct gicv5_its_dev *its_dev)
+{
+	u32 devicer;
+	u64 didr;
+
+	didr = FIELD_PREP(GICV5_ITS_DIDR_DEVICEID, its_dev->device_id);
+	devicer = FIELD_PREP(GICV5_ITS_INV_DEVICER_I, 0x1)	|
+		  FIELD_PREP(GICV5_ITS_INV_DEVICER_EVENTID_BITS,
+			     its_dev->itt_cfg.event_id_bits)	|
+		  FIELD_PREP(GICV5_ITS_INV_DEVICER_L1, 0x0);
+	its_writeq(its, didr, GICV5_ITS_DIDR);
+	its_writel(its, devicer, GICV5_ITS_INV_DEVICER);
+
+	return gicv5_its_wait_for_invalidation(its);
+}
+
+/*
+ * Allocate a level 2 device table entry, update L1 parent to reference it.
+ * Only used for 2-level device tables, and it is called on demand.
+ */
+static int gicv5_its_alloc_l2_devtab(struct gicv5_its_chip_data *its,
+				     unsigned int l1_index)
+{
+	int ret;
+	u64 l1dte;
+	u8 span, l2sz, l2_bits;
+	__le64 *l2devtab, *l1devtab = its->devtab_cfgr.l2.l1devtab;
+
+	if (FIELD_GET(GICV5_DTL1E_VALID, le64_to_cpu(l1devtab[l1_index])))
+		return 0;
+
+	span = FIELD_GET(GICV5_DTL1E_SPAN, le64_to_cpu(l1devtab[l1_index]));
+	l2sz = FIELD_GET(GICV5_ITS_DT_CFGR_L2SZ, its->devtab_cfgr.cfgr);
+
+	ret = gicv5_its_l2sz_to_l2_bits(l2sz);
+	if (ret < 0)
+		return ret;
+
+	l2_bits = ret;
+
+	/*
+	 * Span allows us to create a smaller L2 device table.
+	 * If it is too large, use the number of allowed L2 bits.
+	 */
+	if (span > l2_bits)
+		span = l2_bits;
+
+	l2devtab = kcalloc(BIT(span), sizeof(*l2devtab), GFP_KERNEL);
+	if (!l2devtab)
+		return -ENOMEM;
+
+	its->devtab_cfgr.l2.l2ptrs[l1_index] = l2devtab;
+
+	l1dte = FIELD_PREP(GICV5_DTL1E_SPAN, span)			|
+		(virt_to_phys(l2devtab) & GICV5_DTL1E_L2_ADDR_MASK)	|
+		FIELD_PREP(GICV5_DTL1E_VALID, 0x1);
+	its_write_table_entry(its, &l1devtab[l1_index], cpu_to_le64(l1dte));
+
+	return 0;
+}
+
+static int gicv5_its_devtab_get_dte_ref(struct gicv5_its_chip_data *its,
+					__le64 **dte, u32 device_id,
+					bool alloc)
+{
+	int ret;
+	__le64 *l1devtab, *l2devtab;
+	unsigned int l2sz, l2_bits, l1_idx, l2_idx;
+	u8 str = FIELD_GET(GICV5_ITS_DT_CFGR_STRUCTURE, its->devtab_cfgr.cfgr);
+
+	if (str == GICV5_ITS_DT_ITT_CFGR_STRUCTURE_LINEAR) {
+		l2devtab = its->devtab_cfgr.linear.devtab;
+		*dte = &l2devtab[device_id];
+	} else {
+		l2sz = FIELD_GET(GICV5_ITS_DT_CFGR_L2SZ, its->devtab_cfgr.cfgr);
+		l1devtab = its->devtab_cfgr.l2.l1devtab;
+
+		ret = gicv5_its_l2sz_to_l2_bits(l2sz);
+		if (ret < 0)
+			return -EINVAL;
+
+		l2_bits = ret;
+		l1_idx = device_id >> l2_bits;
+		l2_idx = device_id & GENMASK(l2_bits - 1, 0);
+
+		if (alloc) {
+			/*
+			 * Allocate a new L2 device table here before
+			 * continuing. We make the assumption that the span in
+			 * the L1 table has been set correctly, and blindly use
+			 * that value.
+			 */
+			ret = gicv5_its_alloc_l2_devtab(its, l1_idx);
+			if (ret)
+				return ret;
+		} else {
+			if (!FIELD_GET(GICV5_DTL1E_VALID,
+				       le64_to_cpu(l1devtab[l1_idx])))
+				return -EINVAL;
+		}
+
+
+		l2devtab = its->devtab_cfgr.l2.l2ptrs[l1_idx];
+		*dte = &l2devtab[l2_idx];
+	}
+
+	return 0;
+}
+
+/*
+ * Register a new device in the device table. Allocate an ITT and
+ * program the L2DTE entry according to the ITT structure that
+ * was chosen.
+ */
+static int gicv5_its_device_register(struct gicv5_its_chip_data *its,
+				     struct gicv5_its_dev *its_dev)
+{
+	u8 event_id_bits, device_id_bits, itt_struct, itt_l2sz;
+	phys_addr_t itt_phys_base;
+	u32 idr1, idr2;
+	bool two_level_itt;
+	__le64 *dte;
+	int ret;
+	u64 val;
+
+	device_id_bits = FIELD_GET(GICV5_ITS_DT_CFGR_DEVICEID_BITS,
+				   its->devtab_cfgr.cfgr);
+
+	if (its_dev->device_id >= BIT(device_id_bits)) {
+		pr_err("Supplied DeviceID (%u) outside of Device Table range (%u)!",
+		       its_dev->device_id, (u32)GENMASK(device_id_bits - 1, 0));
+		return -EINVAL;
+	}
+
+	ret = gicv5_its_devtab_get_dte_ref(its, &dte, its_dev->device_id, true);
+	if (ret)
+		return ret;
+
+	if (FIELD_GET(GICV5_DTL2E_VALID, le64_to_cpu(*dte)))
+		return -EBUSY;
+
+	/*
+	 * Determine how many bits we need, validate those against the max.
+	 * Based on these, determine if we should go for a 1- or 2-level ITT.
+	 */
+	event_id_bits = order_base_2(its_dev->num_events);
+
+	idr2 = its_readl(its, GICV5_ITS_IDR2);
+
+	if (event_id_bits > FIELD_GET(GICV5_ITS_IDR2_EVENTID_BITS, idr2)) {
+		pr_err("Required EventID bits (%u) larger than supported bits (%u)!",
+		       event_id_bits,
+		       (u8)FIELD_GET(GICV5_ITS_IDR2_EVENTID_BITS, idr2));
+		return -EINVAL;
+	}
+
+	idr1 = its_readl(its, GICV5_ITS_IDR1);
+
+	/*
+	 * L2 ITT size is programmed into the L2DTE regardless of
+	 * whether a two-level or linear ITT is built, init it.
+	 */
+	itt_l2sz = 0;
+
+	two_level_itt = gicv5_its_l2sz_two_level(false, idr1, event_id_bits,
+						  &itt_l2sz);
+	if (two_level_itt)
+		ret = gicv5_its_create_itt_two_level(its, its_dev, event_id_bits,
+						     itt_l2sz,
+						     its_dev->num_events);
+	else
+		ret = gicv5_its_create_itt_linear(its, its_dev, event_id_bits);
+	if (ret)
+		return ret;
+
+	itt_phys_base = two_level_itt ?
+		virt_to_phys(its_dev->itt_cfg.l2.l1itt) :
+		virt_to_phys(its_dev->itt_cfg.linear.itt);
+
+	itt_struct = two_level_itt ? GICV5_ITS_DT_ITT_CFGR_STRUCTURE_TWO_LEVEL :
+				     GICV5_ITS_DT_ITT_CFGR_STRUCTURE_LINEAR;
+
+	val = FIELD_PREP(GICV5_DTL2E_EVENT_ID_BITS, event_id_bits)	|
+	      FIELD_PREP(GICV5_DTL2E_ITT_STRUCTURE, itt_struct)		|
+	      (itt_phys_base & GICV5_DTL2E_ITT_ADDR_MASK)		|
+	      FIELD_PREP(GICV5_DTL2E_ITT_L2SZ, itt_l2sz)		|
+	      FIELD_PREP(GICV5_DTL2E_VALID, 0x1);
+
+	its_write_table_entry(its, dte, cpu_to_le64(val));
+
+	ret = gicv5_its_device_cache_inv(its, its_dev);
+	if (ret) {
+		gicv5_its_free_itt(its_dev);
+		its_write_table_entry(its, dte, 0);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Unregister a device in the device table. Lookup the device by ID, free the
+ * corresponding ITT, mark the device as invalid in the device table.
+ */
+static int gicv5_its_device_unregister(struct gicv5_its_chip_data *its,
+				       struct gicv5_its_dev *its_dev)
+{
+	int ret;
+	__le64 *dte;
+
+	ret = gicv5_its_devtab_get_dte_ref(its, &dte, its_dev->device_id, false);
+	if (ret) {
+		pr_debug("Failed to find DTE for DeviceID 0x%x\n", its_dev->device_id);
+		return -EINVAL;
+	}
+
+	if (!FIELD_GET(GICV5_DTL2E_VALID, le64_to_cpu(*dte))) {
+		pr_debug("Device table entry for DeviceID 0x%x is not valid. Nothing to clean up!",
+		       its_dev->device_id);
+		return -EINVAL;
+	}
+
+	gicv5_its_free_itt(its_dev);
+
+	/* Zero everything - make it clear that this is an invalid entry */
+	its_write_table_entry(its, dte, 0);
+
+	return gicv5_its_device_cache_inv(its, its_dev);
+}
+
+/*
+ * Allocate a 1-level device table. All entries are allocated, but marked
+ * invalid.
+ */
+static int gicv5_its_alloc_devtab_linear(struct gicv5_its_chip_data *its,
+					u8 device_id_bits)
+{
+	u32 cfgr;
+	u64 baser;
+	size_t sz;
+	__le64 *devtab;
+
+	sz = BIT_ULL(device_id_bits) * sizeof(*devtab);
+	if (sz > KMALLOC_MAX_SIZE) {
+		u8 device_id_cap = ilog2(KMALLOC_MAX_SIZE/sizeof(*devtab));
+
+		pr_warn("Limiting device ID bits from %u to %u\n",
+			device_id_bits, device_id_cap);
+		device_id_bits = device_id_cap;
+	}
+
+	devtab = kcalloc(BIT(device_id_bits), sizeof(*devtab), GFP_KERNEL);
+	if (!devtab)
+		return -ENOMEM;
+
+	if (its->flags & ITS_FLAGS_NON_COHERENT)
+		dcache_clean_inval_poc((unsigned long)devtab,
+				       (unsigned long)devtab + sz);
+	else
+		dsb(ishst);
+
+	cfgr = FIELD_PREP(GICV5_ITS_DT_CFGR_STRUCTURE,
+			  GICV5_ITS_DT_ITT_CFGR_STRUCTURE_LINEAR)	|
+	       FIELD_PREP(GICV5_ITS_DT_CFGR_L2SZ, 0)			|
+	       FIELD_PREP(GICV5_ITS_DT_CFGR_DEVICEID_BITS, device_id_bits);
+	its_writel(its, cfgr, GICV5_ITS_DT_CFGR);
+
+	baser = virt_to_phys(devtab) & GICV5_ITS_DT_BASER_ADDR_MASK;
+	its_writeq(its, baser, GICV5_ITS_DT_BASER);
+
+	its->devtab_cfgr.cfgr = cfgr;
+	its->devtab_cfgr.linear.devtab = devtab;
+
+	return 0;
+}
+
+/*
+ * Allocate a 2-level device table. L2 entries are not allocated,
+ * they are allocated on-demand.
+ */
+static int gicv5_its_alloc_devtab_two_level(struct gicv5_its_chip_data *its,
+					    u8 device_id_bits,
+					    u8 devtab_l2sz)
+{
+	int ret;
+	u64 baser;
+	u32 cfgr;
+	size_t l1_sz;
+	__le64 *l1devtab, **l2ptrs;
+	unsigned int l1_bits, l2_bits, i;
+
+	ret = gicv5_its_l2sz_to_l2_bits(devtab_l2sz);
+	if (ret < 0)
+		return ret;
+
+	l2_bits = ret;
+
+	l1_bits = device_id_bits - l2_bits;
+	l1_sz = BIT(l1_bits) * sizeof(*l1devtab);
+	if (l1_sz > KMALLOC_MAX_SIZE) {
+		l1_bits = ilog2(KMALLOC_MAX_SIZE/sizeof(*l1devtab));
+
+		pr_warn("Limiting device ID bits from %u to %u\n",
+			device_id_bits, l1_bits + l2_bits);
+		device_id_bits = l1_bits + l2_bits;
+		l1_sz = KMALLOC_MAX_SIZE;
+	}
+
+	l1devtab = kcalloc(BIT(l1_bits), sizeof(*l1devtab), GFP_KERNEL);
+	if (!l1devtab)
+		return -ENOMEM;
+
+	l2ptrs = kcalloc(BIT(l1_bits), sizeof(*l2ptrs), GFP_KERNEL);
+	if (!l2ptrs) {
+		kfree(l1devtab);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < BIT(l1_bits); i++)
+		l1devtab[i] = cpu_to_le64(FIELD_PREP(GICV5_DTL1E_SPAN, l2_bits));
+
+	if (its->flags & ITS_FLAGS_NON_COHERENT)
+		dcache_clean_inval_poc((unsigned long)l1devtab,
+				       (unsigned long)l1devtab + l1_sz);
+	else
+		dsb(ishst);
+
+	cfgr = FIELD_PREP(GICV5_ITS_DT_CFGR_STRUCTURE,
+			  GICV5_ITS_DT_ITT_CFGR_STRUCTURE_TWO_LEVEL)	|
+	       FIELD_PREP(GICV5_ITS_DT_CFGR_L2SZ, devtab_l2sz)		|
+	       FIELD_PREP(GICV5_ITS_DT_CFGR_DEVICEID_BITS, device_id_bits);
+	its_writel(its, cfgr, GICV5_ITS_DT_CFGR);
+
+	baser = virt_to_phys(l1devtab) & GICV5_ITS_DT_BASER_ADDR_MASK;
+	its_writeq(its, baser, GICV5_ITS_DT_BASER);
+
+	its->devtab_cfgr.cfgr = cfgr;
+	its->devtab_cfgr.l2.l1devtab = l1devtab;
+	its->devtab_cfgr.l2.l2ptrs = l2ptrs;
+
+	return 0;
+}
+
+/*
+ * Initialise the device table as either 1- or 2-level depending on what is
+ * supported by the hardware.
+ */
+static int gicv5_its_init_devtab(struct gicv5_its_chip_data *its)
+{
+	u32 idr1;
+	bool two_level_devtab;
+	u8 device_id_bits, devtab_l2sz;
+
+	idr1 = its_readl(its, GICV5_ITS_IDR1);
+
+	device_id_bits = FIELD_GET(GICV5_ITS_IDR1_DEVICEID_BITS, idr1);
+	two_level_devtab = gicv5_its_l2sz_two_level(true, idr1, device_id_bits,
+						     &devtab_l2sz);
+	if (two_level_devtab)
+		return gicv5_its_alloc_devtab_two_level(its, device_id_bits,
+						       devtab_l2sz);
+	else
+		return gicv5_its_alloc_devtab_linear(its, device_id_bits);
+}
+
+static void gicv5_its_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
+{
+	u64 addr;
+	struct gicv5_its_dev *its_dev = irq_data_get_irq_chip_data(d);
+	struct gicv5_its_chip_data *its = its_dev->its_node;
+
+	addr = its->its_trans_phys_base;
+
+	msg->data = FIELD_GET(GICV5_ITS_HWIRQ_EVENT_ID, d->hwirq);
+	msi_msg_set_addr(irq_data_get_msi_desc(d), msg, addr);
+}
+
+static const struct irq_chip gicv5_its_irq_chip = {
+	.name			= "GICv5-ITS-MSI",
+	.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,
+	.irq_compose_msi_msg	= gicv5_its_compose_msi_msg,
+	.flags			= IRQCHIP_SET_TYPE_MASKED |
+				  IRQCHIP_SKIP_SET_WAKE	  |
+				  IRQCHIP_MASK_ON_SUSPEND
+};
+
+static struct gicv5_its_dev *gicv5_its_find_device(
+				struct gicv5_its_chip_data *its, u32 device_id)
+{
+	struct gicv5_its_dev *dev = xa_load(&its->its_devices, device_id);
+
+	return dev ? dev : ERR_PTR(-ENODEV);
+}
+
+static struct gicv5_its_dev *gicv5_its_alloc_device(
+				struct gicv5_its_chip_data *its, int nvec,
+				u32 dev_id)
+{
+	int ret;
+	struct gicv5_its_dev *its_dev;
+
+	its_dev = gicv5_its_find_device(its, dev_id);
+	if (!IS_ERR(its_dev)) {
+		pr_debug("A device with this DeviceID (0x%x) has already been registered.\n",
+			dev_id);
+
+		if (nvec > its_dev->num_events) {
+			pr_debug("Requesting more ITT entries than allocated\n");
+			return ERR_PTR(-ENXIO);
+		}
+
+		its_dev->shared = true;
+
+		return its_dev;
+	}
+
+	its_dev = kzalloc(sizeof(*its_dev), GFP_KERNEL);
+	if (!its_dev)
+		return ERR_PTR(-ENOMEM);
+
+	its_dev->device_id = dev_id;
+	its_dev->num_events = nvec;
+	its_dev->num_mapped_events = 0;
+
+	ret = gicv5_its_device_register(its, its_dev);
+	if (ret) {
+		pr_debug("Failed to register the device\n");
+		kfree(its_dev);
+		return ERR_PTR(ret);
+	}
+
+	gicv5_its_device_cache_inv(its, its_dev);
+
+	/*
+	 * This is the first time we have seen this device. Hence, it is not
+	 * shared.
+	 */
+	its_dev->shared = false;
+
+	its_dev->its_node = its;
+
+	its_dev->event_map =
+		(unsigned long *)bitmap_zalloc(its_dev->num_events, GFP_KERNEL);
+	if (!its_dev->event_map) {
+		gicv5_its_device_unregister(its, its_dev);
+		kfree(its_dev);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	xa_store(&its->its_devices, dev_id, its_dev, GFP_KERNEL);
+
+	return its_dev;
+}
+
+static int gicv5_its_msi_prepare(struct irq_domain *domain, struct device *dev,
+				 int nvec, msi_alloc_info_t *info)
+{
+	struct msi_domain_info *msi_info;
+	struct gicv5_its_chip_data *its;
+	struct gicv5_its_dev *its_dev;
+	u32 dev_id = info->scratchpad[0].ul;
+
+	msi_info = msi_get_domain_info(domain);
+	its = msi_info->data;
+
+	guard(mutex)(&its->dev_alloc_lock);
+
+	its_dev = gicv5_its_alloc_device(its, nvec, dev_id);
+	if (IS_ERR(its_dev))
+		return PTR_ERR(its_dev);
+
+	if (info->flags & MSI_ALLOC_FLAGS_PROXY_DEVICE)
+		its_dev->shared = true;
+
+	return 0;
+}
+
+static struct msi_domain_ops its_msi_domain_ops = {
+	.msi_prepare = gicv5_its_msi_prepare,
+};
+
+static int gicv5_its_alloc_event(struct gicv5_its_dev *its_dev, u16 event_id,
+				 u32 lpi)
+{
+	int ret;
+	__le64 *itte;
+	u64 itt_entry;
+	struct gicv5_its_chip_data *its = its_dev->its_node;
+
+	if (event_id >= its_dev->num_events) {
+		pr_debug("EventID 0x%x outside of ITT range (0x%x)\n", event_id,
+		       its_dev->num_events);
+		return -EINVAL;
+	}
+
+	if (WARN(its_dev->num_mapped_events == its_dev->num_events,
+		"Reached maximum number of events\n"))
+		return -EINVAL;
+
+	ret = gicv5_its_device_get_itte_ref(its_dev, &itte, event_id);
+	if (ret)
+		return ret;
+
+	if (FIELD_GET(GICV5_ITTL2E_VALID, *itte))
+		return -EEXIST;
+
+	itt_entry = FIELD_PREP(GICV5_ITTL2E_LPI_ID, lpi) |
+		    FIELD_PREP(GICV5_ITTL2E_VALID, 0x1);
+
+	its_write_table_entry(its, itte, cpu_to_le64(itt_entry));
+
+	gicv5_its_itt_cache_inv(its, its_dev->device_id, event_id);
+
+	its_dev->num_mapped_events += 1;
+
+	return 0;
+}
+
+static void gicv5_its_free_event(struct gicv5_its_dev *its_dev, u16 event_id)
+{
+	int ret;
+	__le64 *itte;
+	u64 itte_val;
+	struct gicv5_its_chip_data *its = its_dev->its_node;
+
+	if (WARN(!its_dev->num_mapped_events, "No mapped events\n"))
+		return;
+
+	ret = gicv5_its_device_get_itte_ref(its_dev, &itte, event_id);
+	if (ret) {
+		pr_debug("Failed to get the ITTE!\n");
+		return;
+	}
+
+	itte_val = le64_to_cpu(*itte);
+	itte_val &= ~GICV5_ITTL2E_VALID;
+
+	its_write_table_entry(its, itte, cpu_to_le64(itte_val));
+
+	gicv5_its_itt_cache_inv(its, its_dev->device_id, event_id);
+
+	its_dev->num_mapped_events -= 1;
+}
+
+static int gicv5_its_alloc_eventid(struct gicv5_its_dev *its_dev,
+				   unsigned int nr_irqs, u32 *eventid)
+{
+	int ret;
+
+	ret = bitmap_find_free_region(its_dev->event_map,
+				      its_dev->num_events,
+				      get_count_order(nr_irqs));
+
+	if (ret < 0)
+		return ret;
+
+	*eventid = ret;
+
+	return 0;
+}
+
+static void gicv5_its_free_eventid(struct gicv5_its_dev *its_dev,
+				   u32 event_id_base,
+				   unsigned int nr_irqs)
+{
+	bitmap_release_region(its_dev->event_map, event_id_base,
+				      get_count_order(nr_irqs));
+}
+
+static int gicv5_its_irq_domain_alloc(struct irq_domain *domain,
+				      unsigned int virq, unsigned int nr_irqs,
+				      void *arg)
+{
+	int ret, i;
+	irq_hw_number_t hwirq;
+	struct irq_data *irqd;
+	struct gicv5_its_dev *its_dev;
+	struct gicv5_its_chip_data *its;
+	u32 event_id_base, lpi_base, lpi;
+	struct msi_domain_info *msi_info;
+	msi_alloc_info_t *info = arg;
+	unsigned long device_id = info->scratchpad[0].ul;
+
+	msi_info = msi_get_domain_info(domain);
+	its = msi_info->data;
+
+	guard(mutex)(&its->dev_alloc_lock);
+
+	its_dev = gicv5_its_find_device(its, device_id);
+	if (IS_ERR(its_dev))
+		return PTR_ERR(its_dev);
+
+	ret = gicv5_its_alloc_eventid(its_dev, nr_irqs, &event_id_base);
+	if (ret)
+		return ret;
+
+	ret = iommu_dma_prepare_msi(info->desc,
+				    its->its_trans_phys_base);
+	if (ret)
+		goto out_eventid;
+
+	ret = gicv5_alloc_lpi_range(nr_irqs, &lpi_base);
+	if (ret) {
+		pr_debug("Failed to find free LPIs!\n");
+		goto out_eventid;
+	}
+
+	for (i = 0; i < nr_irqs; i++) {
+		lpi = lpi_base + i;
+
+		ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, &lpi);
+		if (ret)
+			goto out_free_lpi;
+
+		/*
+		 * Store eventid and deviceid into the hwirq for later use.
+		 *
+		 *	hwirq  = event_id << 32 | device_id
+		 */
+		hwirq = FIELD_PREP(GICV5_ITS_HWIRQ_DEVICE_ID, device_id) |
+			FIELD_PREP(GICV5_ITS_HWIRQ_EVENT_ID,
+				    (u64)event_id_base + i);
+		irq_domain_set_info(domain, virq + i, hwirq,
+				    &gicv5_its_irq_chip, its_dev,
+				    handle_fasteoi_irq, NULL, NULL);
+
+		irqd = irq_get_irq_data(virq + i);
+		irqd_set_single_target(irqd);
+		irqd_set_affinity_on_activate(irqd);
+		irqd_set_resend_when_in_progress(irqd);
+	}
+
+	return 0;
+
+out_free_lpi:
+	gicv5_free_lpi_range(lpi_base, nr_irqs);
+out_eventid:
+	gicv5_its_free_eventid(its_dev, event_id_base, nr_irqs);
+
+	return ret;
+}
+
+static void gicv5_its_irq_domain_free(struct irq_domain *domain,
+				      unsigned int virq, unsigned int nr_irqs)
+{
+	int i;
+	u16 event_id_base;
+	u32 device_id;
+	bool free_device;
+	struct irq_data *d;
+	struct gicv5_its_dev *its_dev;
+	struct msi_domain_info *msi_info;
+	struct gicv5_its_chip_data *its;
+
+	msi_info = msi_get_domain_info(domain);
+	its = msi_info->data;
+
+	d = irq_domain_get_irq_data(domain, virq);
+	device_id = FIELD_GET(GICV5_ITS_HWIRQ_DEVICE_ID, d->hwirq);
+	event_id_base = FIELD_GET(GICV5_ITS_HWIRQ_EVENT_ID, d->hwirq);
+
+	guard(mutex)(&its->dev_alloc_lock);
+
+	its_dev = gicv5_its_find_device(its, device_id);
+	if (IS_ERR(its_dev)) {
+		pr_debug("Couldn't find the ITS device!\n");
+		return;
+	}
+
+	bitmap_release_region(its_dev->event_map, event_id_base,
+			      get_count_order(nr_irqs));
+
+	free_device = !its_dev->shared && bitmap_empty(its_dev->event_map,
+						       its_dev->num_events);
+
+	/*  Hierarchically free irq data */
+	for (i = 0; i < nr_irqs; i++) {
+		d = irq_domain_get_irq_data(domain, virq + i);
+
+		WARN_ON(gicv5_free_lpi_range(d->parent_data->hwirq, 1));
+		irq_domain_reset_irq_data(d);
+	}
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+
+	gicv5_its_syncr(its, its_dev);
+	gicv5_irs_syncr();
+
+	if (free_device) {
+		gicv5_its_device_unregister(its, its_dev);
+		bitmap_free(its_dev->event_map);
+		xa_erase(&its->its_devices, device_id);
+		kfree(its_dev);
+	}
+}
+
+static int gicv5_its_irq_domain_activate(struct irq_domain *domain,
+					 struct irq_data *d, bool reserve)
+{
+	u16 event_id;
+	u32 lpi;
+	struct gicv5_its_dev *its_dev = irq_data_get_irq_chip_data(d);
+
+	event_id = FIELD_GET(GICV5_ITS_HWIRQ_EVENT_ID, d->hwirq);
+	lpi = d->parent_data->hwirq;
+
+	return gicv5_its_alloc_event(its_dev, event_id, lpi);
+}
+
+static void gicv5_its_irq_domain_deactivate(struct irq_domain *domain,
+					    struct irq_data *d)
+{
+	u16 event_id;
+	struct gicv5_its_dev *its_dev = irq_data_get_irq_chip_data(d);
+
+	event_id = FIELD_GET(GICV5_ITS_HWIRQ_EVENT_ID, d->hwirq);
+
+	gicv5_its_free_event(its_dev, event_id);
+}
+static const struct irq_domain_ops gicv5_its_irq_domain_ops = {
+	.alloc		= gicv5_its_irq_domain_alloc,
+	.free		= gicv5_its_irq_domain_free,
+	.activate	= gicv5_its_irq_domain_activate,
+	.deactivate	= gicv5_its_irq_domain_deactivate,
+	.select		= msi_lib_irq_domain_select,
+};
+
+static int gicv5_its_wait_for_idle(struct gicv5_its_chip_data *its)
+{
+	int ret;
+	u32 cr0;
+
+	ret = readl_relaxed_poll_timeout_atomic(
+			its->its_base + GICV5_ITS_CR0, cr0,
+			FIELD_GET(GICV5_ITS_CR0_IDLE, cr0), 1,
+			USEC_PER_SEC);
+
+	if (ret == -ETIMEDOUT)
+		pr_err_ratelimited("GICv5 ITS CR0 timeout...\n");
+
+	return ret;
+}
+
+static void gicv5_its_print_info(struct gicv5_its_chip_data *its_node)
+{
+	u8 str;
+	bool devtab_linear;
+	u8 device_id_bits = FIELD_GET(GICV5_ITS_DT_CFGR_DEVICEID_BITS,
+			    its_node->devtab_cfgr.cfgr);
+
+	str = FIELD_GET(GICV5_ITS_DT_CFGR_STRUCTURE, its_node->devtab_cfgr.cfgr);
+	devtab_linear = (str == GICV5_ITS_DT_ITT_CFGR_STRUCTURE_LINEAR);
+
+	pr_info("ITS %s enabled using %s device table device_id_bits %u\n",
+		fwnode_get_name(its_node->fwnode),
+		devtab_linear ? "linear" : "2-level",
+		device_id_bits);
+}
+
+static int __init gicv5_its_init_bases(phys_addr_t its_trans_base,
+				       void __iomem *its_base,
+				       struct fwnode_handle *handle,
+				       struct irq_domain *parent_domain)
+
+{
+	int ret;
+	bool enabled;
+	u32 cr0, cr1;
+	struct gicv5_its_chip_data *its_node;
+	struct msi_domain_info *info;
+	struct device_node *np = to_of_node(handle);
+
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	its_node = kzalloc(sizeof(*its_node), GFP_KERNEL);
+	if (!its_node) {
+		kfree(info);
+		return -ENOMEM;
+	}
+
+	info->ops = &its_msi_domain_ops;
+	info->data = its_node;
+
+	mutex_init(&its_node->dev_alloc_lock);
+	xa_init(&its_node->its_devices);
+	its_node->fwnode = handle;
+	its_node->its_base = its_base;
+	its_node->its_trans_phys_base = its_trans_base;
+
+	its_node->domain = irq_domain_create_hierarchy(
+		parent_domain, IRQ_DOMAIN_FLAG_ISOLATED_MSI, 0, handle,
+		&gicv5_its_irq_domain_ops, info);
+	irq_domain_update_bus_token(its_node->domain,
+				    DOMAIN_BUS_NEXUS);
+
+	its_node->domain->msi_parent_ops = &gic_its_msi_parent_ops;
+	its_node->domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT;
+
+	cr0 = its_readl(its_node, GICV5_ITS_CR0);
+	enabled = FIELD_GET(GICV5_ITS_CR0_ITSEN, cr0);
+	if (WARN(enabled, "ITS %s enabled, disabling it before proceeding\n",
+		 np->full_name)) {
+		cr0 = FIELD_PREP(GICV5_ITS_CR0_ITSEN, 0x0);
+		its_writel(its_node, cr0, GICV5_ITS_CR0);
+		ret = gicv5_its_wait_for_idle(its_node);
+		if (ret) {
+			irq_domain_remove(its_node->domain);
+			kfree(info);
+			kfree(its_node);
+			return ret;
+		}
+	}
+
+	if (of_property_read_bool(np, "dma-noncoherent")) {
+		/*
+		 * A non-coherent ITS implies that some cache levels cannot be
+		 * used coherently by the cores and GIC. Our only option is to mark
+		 * memory attributes for the GIC as non-cacheable; by default,
+		 * non-cacheable memory attributes imply outer-shareable
+		 * shareability, the value written into ITS_CR1_SH is ignored.
+		 */
+		cr1 = FIELD_PREP(GICV5_ITS_CR1_ITT_RA, GICV5_NO_READ_ALLOC)	|
+		      FIELD_PREP(GICV5_ITS_CR1_DT_RA, GICV5_NO_READ_ALLOC)	|
+		      FIELD_PREP(GICV5_ITS_CR1_IC, GICV5_NON_CACHE)		|
+		      FIELD_PREP(GICV5_ITS_CR1_OC, GICV5_NON_CACHE);
+		its_node->flags |= ITS_FLAGS_NON_COHERENT;
+	} else {
+		cr1 = FIELD_PREP(GICV5_ITS_CR1_ITT_RA, GICV5_READ_ALLOC)	|
+		      FIELD_PREP(GICV5_ITS_CR1_DT_RA, GICV5_READ_ALLOC)		|
+		      FIELD_PREP(GICV5_ITS_CR1_IC, GICV5_WB_CACHE)		|
+		      FIELD_PREP(GICV5_ITS_CR1_OC, GICV5_WB_CACHE)		|
+		      FIELD_PREP(GICV5_ITS_CR1_SH, GICV5_INNER_SHARE);
+	}
+
+	its_writel(its_node, cr1, GICV5_ITS_CR1);
+
+	ret = gicv5_its_init_devtab(its_node);
+	if (ret) {
+		irq_domain_remove(its_node->domain);
+		kfree(info);
+		kfree(its_node);
+		return ret;
+	}
+
+	cr0 = FIELD_PREP(GICV5_ITS_CR0_ITSEN, 0x1);
+	its_writel(its_node, cr0, GICV5_ITS_CR0);
+
+	ret = gicv5_its_wait_for_idle(its_node);
+	if (ret) {
+		irq_domain_remove(its_node->domain);
+		kfree(info);
+		kfree(its_node);
+		return ret;
+	}
+
+	list_add(&its_node->entry, &its_nodes);
+
+	gicv5_its_print_info(its_node);
+
+	return 0;
+}
+
+static int __init gicv5_its_init(struct device_node *node)
+{
+	int ret = 0;
+	void __iomem *its_base;
+	struct resource res;
+
+	its_base = of_io_request_and_map(node, 0, "ITS");
+	if (IS_ERR(its_base)) {
+		pr_err("%pOF: unable to map GICv5 ITS_CONFIG_FRAME\n", node);
+		return PTR_ERR(its_base);
+	}
+
+	/*
+	 * The ITS_TRANSLATE_FRAME is the second reg entry, (first is the
+	 * ITS_CONFIG_FRAME) - extract it and use it to init ITS data
+	 * structures.
+	 */
+	ret = of_address_to_resource(node, 1, &res);
+	if (ret)
+		goto out_unmap;
+
+	ret = gicv5_its_init_bases(res.start, its_base, &node->fwnode,
+				   gicv5_global_data.lpi_domain);
+	if (ret)
+		goto out_unmap;
+
+	return 0;
+out_unmap:
+	iounmap(its_base);
+	return ret;
+}
+
+void __init gicv5_its_of_probe(struct device_node *parent)
+{
+	struct device_node *np;
+
+	for_each_available_child_of_node(parent, np) {
+		if (!of_device_is_compatible(np, "arm,gic-v5-its"))
+			continue;
+
+		if (gicv5_its_init(np))
+			pr_err("Failed to init ITS %s\n", np->full_name);
+	}
+}
diff --git a/drivers/irqchip/irq-gic-v5.c b/drivers/irqchip/irq-gic-v5.c
index 24789e9590115e6de7007cd4a74376ae34702ed6..13145d8b0a038b7c13fee32a3b1bc235029e38f1 100644
--- a/drivers/irqchip/irq-gic-v5.c
+++ b/drivers/irqchip/irq-gic-v5.c
@@ -100,12 +100,12 @@ static int release_lpi_range(u32 min, u32 max)
 	return mtree_store_range(&lpi_mt, min, max, NULL, GFP_KERNEL);
 }
 
-static int gicv5_alloc_lpi_range(u32 nr_lpis, u32 *base)
+int gicv5_alloc_lpi_range(u32 nr_lpis, u32 *base)
 {
 	return alloc_lpi_range(nr_lpis, base);
 }
 
-static int gicv5_free_lpi_range(u32 base, u32 nr_lpis)
+int gicv5_free_lpi_range(u32 base, u32 nr_lpis)
 {
 	if (nr_lpis < 1)
 		return -EINVAL;
@@ -1217,6 +1217,8 @@ static int __init gicv5_of_init(struct device_node *node,
 
 	gicv5_smp_init();
 
+	gicv5_irs_its_probe();
+
 	return 0;
 }
 IRQCHIP_DECLARE(gic_v5, "arm,gic-v5", gicv5_of_init);
diff --git a/drivers/irqchip/irq-gic-v5.h b/drivers/irqchip/irq-gic-v5.h
index 8376da2a342d14568a71c0bc85b79c4e7b83a66c..b5c3f69305f30dfa6332fd50c0b98300e3484a05 100644
--- a/drivers/irqchip/irq-gic-v5.h
+++ b/drivers/irqchip/irq-gic-v5.h
@@ -41,6 +41,8 @@
 #define GICV5_IRS_IDR7			0x001c
 #define GICV5_IRS_CR0			0x0080
 #define GICV5_IRS_CR1			0x0084
+#define GICV5_IRS_SYNCR			0x00c0
+#define GICV5_IRS_SYNC_STATUSR		0x00c4
 #define GICV5_IRS_SPI_SELR		0x0108
 #define GICV5_IRS_SPI_CFGR		0x0114
 #define GICV5_IRS_SPI_STATUSR		0x0118
@@ -94,6 +96,10 @@
 #define GICV5_IRS_CR1_OC		GENMASK(3, 2)
 #define GICV5_IRS_CR1_SH		GENMASK(1, 0)
 
+#define GICV5_IRS_SYNCR_SYNC		BIT(31)
+
+#define GICV5_IRS_SYNC_STATUSR_IDLE	BIT(0)
+
 #define GICV5_IRS_SPI_STATUSR_V		BIT(1)
 #define GICV5_IRS_SPI_STATUSR_IDLE	BIT(0)
 
@@ -135,6 +141,101 @@
 
 #define GICV5_ISTL1E_L2_ADDR_MASK	GENMASK_ULL(55, 12)
 
+#define GICV5_ITS_IDR1		0x0004
+#define GICV5_ITS_IDR2		0x0008
+#define GICV5_ITS_CR0		0x0080
+#define GICV5_ITS_CR1		0x0084
+#define GICV5_ITS_DT_BASER	0x00c0
+#define GICV5_ITS_DT_CFGR	0x00d0
+#define GICV5_ITS_DIDR		0x0100
+#define GICV5_ITS_EIDR		0x0108
+#define GICV5_ITS_INV_EVENTR	0x010c
+#define GICV5_ITS_INV_DEVICER	0x0110
+#define GICV5_ITS_STATUSR	0x0120
+#define GICV5_ITS_SYNCR		0x0140
+#define GICV5_ITS_SYNC_STATUSR	0x0148
+
+#define GICV5_ITS_IDR1_L2SZ			GENMASK(10, 8)
+#define GICV5_ITS_IDR1_ITT_LEVELS		BIT(7)
+#define GICV5_ITS_IDR1_DT_LEVELS		BIT(6)
+#define GICV5_ITS_IDR1_DEVICEID_BITS		GENMASK(5, 0)
+
+#define GICV5_ITS_IDR1_L2SZ_SUPPORT_4KB(r)	FIELD_GET(BIT(8), (r))
+#define GICV5_ITS_IDR1_L2SZ_SUPPORT_16KB(r)	FIELD_GET(BIT(9), (r))
+#define GICV5_ITS_IDR1_L2SZ_SUPPORT_64KB(r)	FIELD_GET(BIT(10), (r))
+
+#define GICV5_ITS_IDR2_XDMN_EVENTs		GENMASK(6, 5)
+#define GICV5_ITS_IDR2_EVENTID_BITS		GENMASK(4, 0)
+
+#define GICV5_ITS_CR0_IDLE			BIT(1)
+#define GICV5_ITS_CR0_ITSEN			BIT(0)
+
+#define GICV5_ITS_CR1_ITT_RA			BIT(7)
+#define GICV5_ITS_CR1_DT_RA			BIT(6)
+#define GICV5_ITS_CR1_IC			GENMASK(5, 4)
+#define GICV5_ITS_CR1_OC			GENMASK(3, 2)
+#define GICV5_ITS_CR1_SH			GENMASK(1, 0)
+
+#define GICV5_ITS_DT_CFGR_STRUCTURE		BIT(16)
+#define GICV5_ITS_DT_CFGR_L2SZ			GENMASK(7, 6)
+#define GICV5_ITS_DT_CFGR_DEVICEID_BITS		GENMASK(5, 0)
+
+#define GICV5_ITS_DT_BASER_ADDR_MASK		GENMASK_ULL(55, 3)
+
+#define GICV5_ITS_INV_DEVICER_I			BIT(31)
+#define GICV5_ITS_INV_DEVICER_EVENTID_BITS	GENMASK(5, 1)
+#define GICV5_ITS_INV_DEVICER_L1		BIT(0)
+
+#define GICV5_ITS_DIDR_DEVICEID			GENMASK_ULL(31, 0)
+
+#define GICV5_ITS_EIDR_EVENTID			GENMASK(15, 0)
+
+#define GICV5_ITS_INV_EVENTR_I			BIT(31)
+#define GICV5_ITS_INV_EVENTR_ITT_L2SZ		GENMASK(2, 1)
+#define GICV5_ITS_INV_EVENTR_L1			BIT(0)
+
+#define GICV5_ITS_STATUSR_IDLE			BIT(0)
+
+#define GICV5_ITS_SYNCR_SYNC			BIT_ULL(63)
+#define GICV5_ITS_SYNCR_SYNCALL			BIT_ULL(32)
+#define GICV5_ITS_SYNCR_DEVICEID		GENMASK_ULL(31, 0)
+
+#define GICV5_ITS_SYNC_STATUSR_IDLE		BIT(0)
+
+#define GICV5_DTL1E_VALID			BIT_ULL(0)
+// Note that there is no shift for the address by design
+#define GICV5_DTL1E_L2_ADDR_MASK		GENMASK_ULL(55, 3)
+#define GICV5_DTL1E_SPAN			GENMASK_ULL(63, 60)
+
+#define GICV5_DTL2E_VALID			BIT_ULL(0)
+#define GICV5_DTL2E_ITT_L2SZ			GENMASK_ULL(2, 1)
+// Note that there is no shift for the address by design
+#define GICV5_DTL2E_ITT_ADDR_MASK		GENMASK_ULL(55, 3)
+#define GICV5_DTL2E_ITT_DSWE			BIT_ULL(57)
+#define GICV5_DTL2E_ITT_STRUCTURE		BIT_ULL(58)
+#define GICV5_DTL2E_EVENT_ID_BITS		GENMASK_ULL(63, 59)
+
+#define GICV5_ITTL1E_VALID			BIT_ULL(0)
+// Note that there is no shift for the address by design
+#define GICV5_ITTL1E_L2_ADDR_MASK		GENMASK_ULL(55, 3)
+#define GICV5_ITTL1E_SPAN			GENMASK_ULL(63, 60)
+
+#define GICV5_ITTL2E_LPI_ID			GENMASK_ULL(23, 0)
+#define GICV5_ITTL2E_DAC			GENMASK_ULL(29, 28)
+#define GICV5_ITTL2E_VIRTUAL			BIT_ULL(30)
+#define GICV5_ITTL2E_VALID			BIT_ULL(31)
+#define GICV5_ITTL2E_VM_ID			GENMASK_ULL(47, 32)
+
+#define GICV5_ITS_DT_ITT_CFGR_L2SZ_4k		0b00
+#define GICV5_ITS_DT_ITT_CFGR_L2SZ_16k		0b01
+#define GICV5_ITS_DT_ITT_CFGR_L2SZ_64k		0b10
+
+#define GICV5_ITS_DT_ITT_CFGR_STRUCTURE_LINEAR		0
+#define GICV5_ITS_DT_ITT_CFGR_STRUCTURE_TWO_LEVEL	1
+
+#define GICV5_ITS_HWIRQ_DEVICE_ID		GENMASK_ULL(31, 0)
+#define GICV5_ITS_HWIRQ_EVENT_ID		GENMASK_ULL(63, 32)
+
 struct gicv5_chip_data {
 	struct fwnode_handle	*fwnode;
 	struct irq_domain	*ppi_domain;
@@ -172,13 +273,72 @@ 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);
+void gicv5_irs_its_probe(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_spi_set_type(struct irq_data *d, unsigned int type);
 int gicv5_irs_iste_alloc(u32 lpi);
+void gicv5_irs_syncr(void);
+
+struct gicv5_its_devtab_cfg {
+	union {
+		struct {
+			__le64	*devtab;
+		} linear;
+		struct {
+			__le64	*l1devtab;
+			__le64	**l2ptrs;
+		} l2;
+	};
+	u32	cfgr;
+};
+
+struct gicv5_its_itt_cfg {
+	union {
+		struct {
+			__le64	*itt;
+			unsigned int num_ents;
+		} linear;
+		struct {
+			__le64	*l1itt;
+			__le64	**l2ptrs;
+			unsigned int num_l1_ents;
+			u8 l2sz;
+		} l2;
+	};
+	u8 event_id_bits;
+	bool l2itt;
+};
+
+struct gicv5_its_chip_data {
+	struct	list_head		entry;
+	struct	xarray			its_devices;
+	struct	mutex			dev_alloc_lock;
+	struct	fwnode_handle		*fwnode;
+	struct gicv5_its_devtab_cfg	devtab_cfgr;
+	struct irq_domain		*domain;
+	void	__iomem			*its_base;
+	phys_addr_t			its_trans_phys_base;
+	u32				flags;
+};
+
+struct gicv5_its_dev {
+	struct gicv5_its_chip_data	*its_node;
+	struct gicv5_its_itt_cfg	itt_cfg;
+	unsigned long			*event_map;
+	u32				device_id;
+	u32				num_events;
+	u32				num_mapped_events;
+	bool				shared;
+};
 
 void gicv5_init_lpi(u32 max);
 void gicv5_free_lpi(void);
+
+int gicv5_alloc_lpi_range(u32 nr_lpis, u32 *base);
+int gicv5_free_lpi_range(u32 base, u32 nr_lpis);
+
+void __init gicv5_its_of_probe(struct device_node *parent);
 #endif

-- 
2.48.0



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 23/24] irqchip/gic-v5: Add GICv5 IWB support
  2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (21 preceding siblings ...)
  2025-04-08 10:50 ` [PATCH 22/24] irqchip/gic-v5: Add GICv5 ITS support Lorenzo Pieralisi
@ 2025-04-08 10:50 ` Lorenzo Pieralisi
  2025-04-08 10:50 ` [PATCH 24/24] arm64: Kconfig: Enable GICv5 Lorenzo Pieralisi
  23 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:50 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

The GICv5 architecture implements the Interrupt Wire Bridge (IWB) in
order to support wired interrupts that cannot be connected directly
to an IRS and instead uses the ITS to translate a wire event into
an IRQ signal.

An IWB is a special ITS device with its own deviceID; upon probe,
an IWB calls into the ITS driver to allocate DT/ITT tables for its
events (ie wires).

An IWB is always associated with a single ITS in the system.

An IWB is connected to an ITS and it has its own deviceID for all
interrupt wires that it manages; the IWB input wire number is
exposed to the ITS as an eventID. This eventID is not programmable
and therefore requires special handling in the ITS driver.

Add an IWB driver in order to:

- Probe IWBs in the system and allocate ITS tables
- Manage IWB IRQ domains
- Handle IWB input wires state (enable/disable)
- Add the required IWB IRQchip representation
- Handle firmware representation to Linux IRQ translation

Co-developed-by: Sascha Bischoff <sascha.bischoff@arm.com>
Signed-off-by: Sascha Bischoff <sascha.bischoff@arm.com>
Co-developed-by: Timothy Hayes <timothy.hayes@arm.com>
Signed-off-by: Timothy Hayes <timothy.hayes@arm.com>
Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Marc Zyngier <maz@kernel.org>
---
 drivers/irqchip/Makefile         |   2 +-
 drivers/irqchip/irq-gic-v5-its.c |  70 ++++++--
 drivers/irqchip/irq-gic-v5-iwb.c | 377 +++++++++++++++++++++++++++++++++++++++
 drivers/irqchip/irq-gic-v5.c     |   2 +
 drivers/irqchip/irq-gic-v5.h     |  28 +++
 5 files changed, 459 insertions(+), 20 deletions(-)

diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 4280395e3bdff7858102f0b4eaaea1121cace52f..7bfb2369fbe494a64b72308d95ae33de93c6b8c6 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -37,7 +37,7 @@ obj-$(CONFIG_ARM_GIC_V3_ITS)		+= irq-gic-v3-its.o irq-gic-v4.o
 obj-$(CONFIG_ARM_GIC_V3_ITS_FSL_MC)	+= irq-gic-v3-its-fsl-mc-msi.o
 obj-$(CONFIG_PARTITION_PERCPU)		+= irq-partition-percpu.o
 obj-$(CONFIG_ARM_GIC_V5)		+= irq-gic-v5.o irq-gic-v5-irs.o
-obj-$(CONFIG_ARM_GIC_V5_ITS)		+= irq-gic-v5-its.o
+obj-$(CONFIG_ARM_GIC_V5_ITS)		+= irq-gic-v5-its.o irq-gic-v5-iwb.o
 obj-$(CONFIG_HISILICON_IRQ_MBIGEN)	+= irq-mbigen.o
 obj-$(CONFIG_ARM_NVIC)			+= irq-nvic.o
 obj-$(CONFIG_ARM_VIC)			+= irq-vic.o
diff --git a/drivers/irqchip/irq-gic-v5-its.c b/drivers/irqchip/irq-gic-v5-its.c
index f4f381e6c3af896f354e7e03bc2452b8a65cf093..84ce6032f36724e4f17d6169a7d1933ed11f9110 100644
--- a/drivers/irqchip/irq-gic-v5-its.c
+++ b/drivers/irqchip/irq-gic-v5-its.c
@@ -779,9 +779,8 @@ static struct gicv5_its_dev *gicv5_its_find_device(
 	return dev ? dev : ERR_PTR(-ENODEV);
 }
 
-static struct gicv5_its_dev *gicv5_its_alloc_device(
-				struct gicv5_its_chip_data *its, int nvec,
-				u32 dev_id)
+struct gicv5_its_dev *gicv5_its_alloc_device(struct gicv5_its_chip_data *its,
+					     int nvec, u32 dev_id, bool is_iwb)
 {
 	int ret;
 	struct gicv5_its_dev *its_dev;
@@ -808,6 +807,7 @@ static struct gicv5_its_dev *gicv5_its_alloc_device(
 	its_dev->device_id = dev_id;
 	its_dev->num_events = nvec;
 	its_dev->num_mapped_events = 0;
+	its_dev->is_iwb = is_iwb;
 
 	ret = gicv5_its_device_register(its, its_dev);
 	if (ret) {
@@ -820,9 +820,11 @@ static struct gicv5_its_dev *gicv5_its_alloc_device(
 
 	/*
 	 * This is the first time we have seen this device. Hence, it is not
-	 * shared.
+	 * shared, unless it is an IWB that is a shared ITS device by
+	 * definition, its eventids are hardcoded and never change - we allocate
+	 * it once for all and never free it.
 	 */
-	its_dev->shared = false;
+	its_dev->shared = is_iwb;
 
 	its_dev->its_node = its;
 
@@ -852,7 +854,7 @@ static int gicv5_its_msi_prepare(struct irq_domain *domain, struct device *dev,
 
 	guard(mutex)(&its->dev_alloc_lock);
 
-	its_dev = gicv5_its_alloc_device(its, nvec, dev_id);
+	its_dev = gicv5_its_alloc_device(its, nvec, dev_id, false);
 	if (IS_ERR(its_dev))
 		return PTR_ERR(its_dev);
 
@@ -930,28 +932,55 @@ static void gicv5_its_free_event(struct gicv5_its_dev *its_dev, u16 event_id)
 }
 
 static int gicv5_its_alloc_eventid(struct gicv5_its_dev *its_dev,
+				   msi_alloc_info_t *info,
 				   unsigned int nr_irqs, u32 *eventid)
 {
-	int ret;
+	int event_id_base;
 
-	ret = bitmap_find_free_region(its_dev->event_map,
-				      its_dev->num_events,
-				      get_count_order(nr_irqs));
+	if (!its_dev->is_iwb) {
 
-	if (ret < 0)
-		return ret;
+		event_id_base = bitmap_find_free_region(
+			its_dev->event_map, its_dev->num_events,
+			get_count_order(nr_irqs));
+		if (event_id_base < 0)
+			return event_id_base;
+	} else {
+		/*
+		 * We want to have a fixed EventID mapped for the IWB.
+		 */
+		if (WARN(nr_irqs != 1, "IWB requesting nr_irqs != 1\n"))
+			return -EINVAL;
 
-	*eventid = ret;
+		event_id_base = info->scratchpad[1].ul;
+
+		if (event_id_base >= its_dev->num_events) {
+			pr_err("EventID ouside of ITT range; cannot allocate an ITT entry!\n");
+
+			return -EINVAL;
+		}
+
+		if (test_and_set_bit(event_id_base, its_dev->event_map)) {
+			pr_warn("Can't reserve event_id bitmap\n");
+			return -EINVAL;
+
+		}
+	}
+
+	*eventid = event_id_base;
 
 	return 0;
+
 }
 
 static void gicv5_its_free_eventid(struct gicv5_its_dev *its_dev,
 				   u32 event_id_base,
 				   unsigned int nr_irqs)
 {
-	bitmap_release_region(its_dev->event_map, event_id_base,
+	if (!its_dev->is_iwb)
+		bitmap_release_region(its_dev->event_map, event_id_base,
 				      get_count_order(nr_irqs));
+	else
+		clear_bit(event_id_base, its_dev->event_map);
 }
 
 static int gicv5_its_irq_domain_alloc(struct irq_domain *domain,
@@ -977,14 +1006,17 @@ static int gicv5_its_irq_domain_alloc(struct irq_domain *domain,
 	if (IS_ERR(its_dev))
 		return PTR_ERR(its_dev);
 
-	ret = gicv5_its_alloc_eventid(its_dev, nr_irqs, &event_id_base);
+	ret = gicv5_its_alloc_eventid(its_dev, info, nr_irqs, &event_id_base);
 	if (ret)
 		return ret;
 
-	ret = iommu_dma_prepare_msi(info->desc,
-				    its->its_trans_phys_base);
-	if (ret)
-		goto out_eventid;
+	/* IWB are never upstream an IOMMU */
+	if (!its_dev->is_iwb) {
+		ret = iommu_dma_prepare_msi(info->desc,
+					    its->its_trans_phys_base);
+		if (ret)
+			goto out_eventid;
+	}
 
 	ret = gicv5_alloc_lpi_range(nr_irqs, &lpi_base);
 	if (ret) {
diff --git a/drivers/irqchip/irq-gic-v5-iwb.c b/drivers/irqchip/irq-gic-v5-iwb.c
new file mode 100644
index 0000000000000000000000000000000000000000..5938d271f5bcf2c926dc58d1664e30ee75139479
--- /dev/null
+++ b/drivers/irqchip/irq-gic-v5-iwb.c
@@ -0,0 +1,377 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024-2025 ARM Limited, All Rights Reserved.
+ */
+#define pr_fmt(fmt)	"GICv5 IWB: " fmt
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/irqchip.h>
+#include <linux/iopoll.h>
+#include <linux/msi.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include "irq-gic-v5.h"
+
+static u32 iwb_readl(struct gicv5_iwb_chip_data *iwb_node, const u64 reg_offset)
+{
+	return readl_relaxed(iwb_node->iwb_base + reg_offset);
+}
+
+static void iwb_writel(struct gicv5_iwb_chip_data *iwb_node, const u32 val,
+		       const u64 reg_offset)
+{
+	writel_relaxed(val, iwb_node->iwb_base + reg_offset);
+}
+
+static int gicv5_iwb_wait_for_wenabler(struct gicv5_iwb_chip_data *iwb_node)
+{
+	int ret;
+	u32 val;
+
+	ret = readl_relaxed_poll_timeout_atomic(
+			iwb_node->iwb_base + GICV5_IWB_WENABLE_STATUSR, val,
+			FIELD_GET(GICV5_IWB_WENABLE_STATUSR_IDLE, val), 1,
+			USEC_PER_SEC);
+
+	if (ret == -ETIMEDOUT)
+		pr_err_ratelimited("GICv5 IWB_WENABLE_STATUSR timeout\n");
+
+	return ret;
+}
+
+static int __gicv5_iwb_set_wire_enable(struct gicv5_iwb_chip_data *iwb_node,
+				       u32 iwb_wire, bool enable)
+{
+	u32 val;
+	u32 n = iwb_wire / 32;
+	u8 i = iwb_wire % 32;
+
+	if (n >= iwb_node->nr_regs) {
+		pr_err("IWB_WENABLER<n> is invalid for n=%u\n", n);
+		return -EINVAL;
+	}
+
+	/*
+	 * Enable IWB wire/pin at this point
+	 * Note: This is not the same as enabling the interrupt
+	 */
+	val = iwb_readl(iwb_node, GICV5_IWB_WENABLER + (4 * n));
+	if (enable)
+		val |= BIT(i);
+	else
+		val &= ~BIT(i);
+	iwb_writel(iwb_node, val, GICV5_IWB_WENABLER + (4 * n));
+
+	return gicv5_iwb_wait_for_wenabler(iwb_node);
+}
+
+static int gicv5_iwb_enable_wire(struct gicv5_iwb_chip_data *iwb_node,
+				 u32 iwb_wire)
+{
+	return __gicv5_iwb_set_wire_enable(iwb_node, iwb_wire, true);
+}
+
+static int gicv5_iwb_disable_wire(struct gicv5_iwb_chip_data *iwb_node,
+				  u32 iwb_wire)
+{
+	return __gicv5_iwb_set_wire_enable(iwb_node, iwb_wire, false);
+}
+
+static int gicv5_iwb_set_type(struct irq_data *d, unsigned int type)
+{
+	struct gicv5_iwb_chip_data *iwb_node = irq_data_get_irq_chip_data(d);
+	u32 iwb_wire, n, wtmr;
+	u8 i;
+
+	iwb_wire = d->hwirq;
+
+	i = iwb_wire % 32;
+	n = iwb_wire / 32;
+
+	WARN_ON(n >= iwb_node->nr_regs);
+
+	wtmr = iwb_readl(iwb_node, GICV5_IWB_WTMR + (4 * n));
+
+	switch (type) {
+	case IRQ_TYPE_LEVEL_HIGH:
+	case IRQ_TYPE_LEVEL_LOW:
+		wtmr |= BIT(i);
+		break;
+	case IRQ_TYPE_EDGE_RISING:
+	case IRQ_TYPE_EDGE_FALLING:
+		wtmr &= ~BIT(i);
+		break;
+	default:
+		pr_debug("unexpected wire trigger mode");
+		return -EINVAL;
+	}
+
+	iwb_writel(iwb_node, wtmr, GICV5_IWB_WTMR + (4 * n));
+
+	return 0;
+}
+
+static const struct irq_chip gicv5_iwb_chip = {
+	.name			= "GICv5-IWB",
+	.irq_mask		= irq_chip_mask_parent,
+	.irq_unmask		= irq_chip_unmask_parent,
+	.irq_eoi		= irq_chip_eoi_parent,
+	.irq_set_type		= gicv5_iwb_set_type,
+	.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,
+	.flags			= IRQCHIP_SET_TYPE_MASKED |
+				  IRQCHIP_SKIP_SET_WAKE |
+				  IRQCHIP_MASK_ON_SUSPEND
+};
+
+static int gicv5_iwb_irq_domain_translate(struct irq_domain *d,
+					  struct irq_fwspec *fwspec,
+					  irq_hw_number_t *hwirq,
+					  unsigned int *type)
+{
+	if (is_of_node(fwspec->fwnode)) {
+		if (fwspec->param_count < 2)
+			return -EINVAL;
+
+		/*
+		 * param[0] is be the wire
+		 * param[1] is the interrupt type
+		 */
+		*hwirq = fwspec->param[0];
+
+		*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static void gicv5_iwb_irq_domain_free(struct irq_domain *domain,
+				      unsigned int virq, unsigned int nr_irqs)
+{
+	/* Free the local data, and then go up the hierarchy doing the same */
+	struct gicv5_iwb_chip_data *iwb_node = domain->host_data;
+	struct irq_data *data;
+
+	if (WARN_ON(nr_irqs != 1))
+		return;
+
+	data = irq_domain_get_irq_data(domain, virq);
+	gicv5_iwb_disable_wire(iwb_node, data->hwirq);
+
+	irq_domain_reset_irq_data(data);
+
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+/*
+ * Our parent is the ITS, which expects MSI devices with programmable
+ * event IDs. IWB event IDs are hardcoded.
+ *
+ * Use the msi_alloc_info_t structure to convey both our DeviceID
+ * (scratchpad[0]), and the wire that we are attempting to map to an LPI in
+ * the ITT (scratchpad[1]).
+ */
+static int iwb_alloc_lpi_irq_parent(struct irq_domain *domain,
+				    unsigned int virq, irq_hw_number_t hwirq)
+{
+	msi_alloc_info_t info;
+	struct gicv5_iwb_chip_data *iwb_node = domain->host_data;
+
+	info.scratchpad[0].ul = iwb_node->device_id;
+	info.scratchpad[1].ul = hwirq;
+	info.hwirq = hwirq;
+
+	return irq_domain_alloc_irqs_parent(domain, virq, 1, &info);
+}
+
+static int gicv5_iwb_irq_domain_alloc(struct irq_domain *domain,
+				      unsigned int virq, unsigned int nr_irqs,
+				      void *arg)
+{
+	int ret;
+	irq_hw_number_t hwirq;
+	unsigned int type = IRQ_TYPE_NONE;
+	struct irq_fwspec *fwspec = arg;
+	struct irq_data *irqd;
+	struct gicv5_iwb_chip_data *iwb_node;
+
+	if (WARN_ON(nr_irqs != 1))
+		return -EINVAL;
+
+	ret = gicv5_iwb_irq_domain_translate(domain, fwspec, &hwirq, &type);
+	if (ret)
+		return ret;
+
+	irqd = irq_desc_get_irq_data(irq_to_desc(virq));
+	iwb_node = domain->host_data;
+
+	if (iwb_alloc_lpi_irq_parent(domain, virq, hwirq))
+		return -EINVAL;
+
+	gicv5_iwb_enable_wire(iwb_node, hwirq);
+
+	irq_domain_set_info(domain, virq, hwirq, &gicv5_iwb_chip, iwb_node,
+			    handle_fasteoi_irq, NULL, NULL);
+	irq_set_probe(virq);
+	irqd_set_single_target(irqd);
+
+	return 0;
+}
+
+static int gicv5_iwb_irq_domain_select(struct irq_domain *d,
+				       struct irq_fwspec *fwspec,
+				       enum irq_domain_bus_token bus_token)
+{
+	struct gicv5_iwb_chip_data *iwb_node;
+
+	/* Not for us */
+	if (fwspec->fwnode != d->fwnode)
+		return 0;
+
+	iwb_node = d->host_data;
+
+	return (d == iwb_node->domain);
+}
+
+static const struct irq_domain_ops gicv5_iwb_irq_domain_ops = {
+	.translate	= gicv5_iwb_irq_domain_translate,
+	.alloc		= gicv5_iwb_irq_domain_alloc,
+	.free		= gicv5_iwb_irq_domain_free,
+	.select		= gicv5_iwb_irq_domain_select
+};
+
+static struct gicv5_iwb_chip_data *
+__init gicv5_iwb_init_bases(void __iomem *iwb_base,
+			     struct fwnode_handle *handle,
+			     struct irq_domain *parent_domain, u32 device_id)
+{
+	u32 nr_wires, idr0, cr0;
+	struct gicv5_iwb_chip_data *iwb_node;
+	struct msi_domain_info *msi_info;
+	struct gicv5_its_chip_data *its;
+	struct gicv5_its_dev *its_dev;
+	int ret;
+
+	msi_info = msi_get_domain_info(parent_domain);
+	its = msi_info->data;
+	if (!its) {
+		pr_warn("IWB %pOF can't find parent ITS, bailing\n",
+			to_of_node(handle));
+		return ERR_PTR(-ENODEV);
+	}
+
+	iwb_node = kzalloc(sizeof(*iwb_node), GFP_KERNEL);
+	if (!iwb_node)
+		return ERR_PTR(-ENOMEM);
+
+	iwb_node->iwb_base = iwb_base;
+	iwb_node->device_id = device_id;
+
+	idr0 = iwb_readl(iwb_node, GICV5_IWB_IDR0);
+	nr_wires = (FIELD_GET(GICV5_IWB_IDR0_IW_RANGE, idr0) + 1) * 32;
+
+	iwb_node->domain = irq_domain_create_hierarchy(parent_domain, 0,
+			   nr_wires, handle, &gicv5_iwb_irq_domain_ops,
+			   iwb_node);
+	irq_domain_update_bus_token(iwb_node->domain, DOMAIN_BUS_WIRED);
+
+	cr0 = iwb_readl(iwb_node, GICV5_IWB_CR0);
+	if (!FIELD_GET(GICV5_IWB_CR0_IWBEN, cr0)) {
+		pr_err("IWB %s must be enabled in firmware\n",
+		       fwnode_get_name(handle));
+		ret = -EINVAL;
+		goto out_free;
+	}
+
+	iwb_node->nr_regs = FIELD_GET(GICV5_IWB_IDR0_IW_RANGE, idr0) + 1;
+
+	for (unsigned int n = 0; n < iwb_node->nr_regs; n++)
+		iwb_writel(iwb_node, 0, GICV5_IWB_WENABLER + (sizeof(u32) * n));
+
+	ret = gicv5_iwb_wait_for_wenabler(iwb_node);
+	if (ret)
+		goto out_free;
+
+	guard(mutex)(&its->dev_alloc_lock);
+
+	its_dev = gicv5_its_alloc_device(its, roundup_pow_of_two(nr_wires),
+					 device_id, true);
+	if (IS_ERR(its_dev)) {
+		ret = -ENODEV;
+		goto out_free;
+	}
+
+	return iwb_node;
+out_free:
+	irq_domain_remove(iwb_node->domain);
+	kfree(iwb_node);
+
+	return ERR_PTR(ret);
+}
+
+static int __init gicv5_iwb_of_init(struct device_node *node)
+{
+	struct gicv5_iwb_chip_data *iwb_node;
+	void __iomem *iwb_base;
+	u32 device_id;
+	struct irq_domain *parent_domain;
+	struct device_node *parent_its __free(device_node) = NULL;
+	struct of_phandle_args args;
+	int ret;
+
+	iwb_base = of_io_request_and_map(node, 0, "IWB");
+	if (IS_ERR(iwb_base)) {
+		pr_err("%pOF: unable to map GICv5 IWB registers\n", node);
+		return PTR_ERR(iwb_base);
+	}
+
+	ret = of_parse_phandle_with_args(node, "msi-parent", "#msi-cells", 0,
+					 &args);
+	if (ret) {
+		pr_err("%pOF: Can't retrieve deviceID\n", node);
+		goto out_unmap;
+	}
+
+	parent_its = args.np;
+	parent_domain = irq_find_matching_host(parent_its, DOMAIN_BUS_NEXUS);
+	if (!parent_domain) {
+		pr_err("Unable to find the parent ITS domain for %pOF!\n", node);
+		ret = -ENXIO;
+		goto out_unmap;
+	}
+
+	device_id = args.args[0];
+	pr_debug("IWB deviceID: 0x%x\n", device_id);
+
+	iwb_node = gicv5_iwb_init_bases(iwb_base, &node->fwnode, parent_domain,
+					device_id);
+	if (IS_ERR(iwb_node)) {
+		ret = PTR_ERR(iwb_node);
+		goto out_unmap;
+	}
+
+	return 0;
+out_unmap:
+	iounmap(iwb_base);
+	return ret;
+}
+
+void __init gicv5_iwb_of_probe(struct device_node *parent)
+{
+	int ret;
+	struct device_node *np;
+
+	for_each_available_child_of_node(parent, np) {
+		if (!of_device_is_compatible(np, "arm,gic-v5-iwb"))
+			continue;
+
+		ret = gicv5_iwb_of_init(np);
+		if (ret)
+			pr_err("Failed to init IWB %s\n", np->full_name);
+	}
+}
diff --git a/drivers/irqchip/irq-gic-v5.c b/drivers/irqchip/irq-gic-v5.c
index 13145d8b0a038b7c13fee32a3b1bc235029e38f1..798c9772c2963f4dc08520673b90d43b88ae3fa2 100644
--- a/drivers/irqchip/irq-gic-v5.c
+++ b/drivers/irqchip/irq-gic-v5.c
@@ -1219,6 +1219,8 @@ static int __init gicv5_of_init(struct device_node *node,
 
 	gicv5_irs_its_probe();
 
+	gicv5_iwb_of_probe(node);
+
 	return 0;
 }
 IRQCHIP_DECLARE(gic_v5, "arm,gic-v5", gicv5_of_init);
diff --git a/drivers/irqchip/irq-gic-v5.h b/drivers/irqchip/irq-gic-v5.h
index b5c3f69305f30dfa6332fd50c0b98300e3484a05..9cd4fa98a6640de7c30ef46120a65c0f7c73c789 100644
--- a/drivers/irqchip/irq-gic-v5.h
+++ b/drivers/irqchip/irq-gic-v5.h
@@ -236,6 +236,20 @@
 #define GICV5_ITS_HWIRQ_DEVICE_ID		GENMASK_ULL(31, 0)
 #define GICV5_ITS_HWIRQ_EVENT_ID		GENMASK_ULL(63, 32)
 
+#define GICV5_IWB_IDR0				0x0000
+#define GICV5_IWB_CR0				0x0080
+#define GICV5_IWB_WENABLE_STATUSR		0x00c0
+#define GICV5_IWB_WENABLER			0x2000
+#define GICV5_IWB_WTMR				0x4000
+
+#define GICV5_IWB_IDR0_INT_DOMS			GENMASK(14, 11)
+#define GICV5_IWB_IDR0_IW_RANGE			GENMASK(10, 0)
+
+#define GICV5_IWB_CR0_IDLE			BIT(1)
+#define GICV5_IWB_CR0_IWBEN			BIT(0)
+
+#define GICV5_IWB_WENABLE_STATUSR_IDLE		BIT(0)
+
 struct gicv5_chip_data {
 	struct fwnode_handle	*fwnode;
 	struct irq_domain	*ppi_domain;
@@ -332,6 +346,7 @@ struct gicv5_its_dev {
 	u32				num_events;
 	u32				num_mapped_events;
 	bool				shared;
+	bool				is_iwb;
 };
 
 void gicv5_init_lpi(u32 max);
@@ -341,4 +356,17 @@ int gicv5_alloc_lpi_range(u32 nr_lpis, u32 *base);
 int gicv5_free_lpi_range(u32 base, u32 nr_lpis);
 
 void __init gicv5_its_of_probe(struct device_node *parent);
+struct gicv5_its_dev *gicv5_its_alloc_device(struct gicv5_its_chip_data *its,
+			   int nvec, u32 dev_id, bool is_iwb);
+
+struct gicv5_iwb_chip_data {
+	void __iomem		*iwb_base;
+	struct irq_domain	*domain;
+	u64			flags;
+	u32			device_id;
+	u16			nr_regs;
+};
+
+void gicv5_iwb_of_probe(struct device_node *parent);
+
 #endif

-- 
2.48.0



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 24/24] arm64: Kconfig: Enable GICv5
  2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (22 preceding siblings ...)
  2025-04-08 10:50 ` [PATCH 23/24] irqchip/gic-v5: Add GICv5 IWB support Lorenzo Pieralisi
@ 2025-04-08 10:50 ` Lorenzo Pieralisi
  2025-04-09 13:44   ` kernel test robot
  23 siblings, 1 reply; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 10:50 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

Enable GICv5 driver code for the ARM64 architecture.

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Will Deacon <will@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/Kconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index a182295e6f08bfa0f3e6f630dc4adfe797a4d273..f1b3c695b376717979ae864865238ae12ad65ca2 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -128,6 +128,7 @@ config ARM64
 	select ARM_GIC_V2M if PCI
 	select ARM_GIC_V3
 	select ARM_GIC_V3_ITS if PCI
+	select ARM_GIC_V5
 	select ARM_PSCI_FW
 	select BUILDTIME_TABLE_SORT
 	select CLONE_BACKWARDS

-- 
2.48.0



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* Re: [PATCH 16/24] arm64: cpucaps: Add GCIE capability
  2025-04-08 10:50 ` [PATCH 16/24] arm64: cpucaps: Add GCIE capability Lorenzo Pieralisi
@ 2025-04-08 11:26   ` Mark Rutland
  2025-04-08 15:02     ` Lorenzo Pieralisi
  0 siblings, 1 reply; 62+ messages in thread
From: Mark Rutland @ 2025-04-08 11:26 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon, Sascha Bischoff,
	Timothy Hayes, linux-arm-kernel, linux-kernel, devicetree

On Tue, Apr 08, 2025 at 12:50:15PM +0200, Lorenzo Pieralisi wrote:
> Implement the GCIE capability as a strict boot cpu capability to
> detect whether architectural GICv5 support is available in HW.
> 
> Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
> Cc: Will Deacon <will@kernel.org>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Marc Zyngier <maz@kernel.org>

This looks good; I have a minor consistency/bikeshedding concern below.

> ---
>  arch/arm64/kernel/cpufeature.c | 7 +++++++
>  arch/arm64/tools/cpucaps       | 1 +
>  2 files changed, 8 insertions(+)
> 
> diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> index 9c4d6d552b25cb3a31d1fb267bd73d3f82513e69..8c60591633f3d435ad9b80a10e484f26af328964 100644
> --- a/arch/arm64/kernel/cpufeature.c
> +++ b/arch/arm64/kernel/cpufeature.c
> @@ -3041,6 +3041,13 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
>  		.matches = has_pmuv3,
>  	},
>  #endif
> +	{
> +		.desc = "GCIE",
> +		.type = ARM64_CPUCAP_STRICT_BOOT_CPU_FEATURE,
> +		.capability = ARM64_HAS_GCIE,
> +		.matches = has_cpuid_feature,
> +		ARM64_CPUID_FIELDS(ID_AA64PFR2_EL1, GCIE, IMP)
> +	},

I reckon it's worth making the desc a bit clearer, e.g. "GICv5 CPU
interface".

It might be worth cleaning up the existing ARM64_HAS_GIC_CPUIF_SYSREGS
feature, e.g. making that have "GICv3 CPU interface" as its desc.

Likewise, could make the names consistent, e.g. have:

	ARM64_HAS_GICV3_CPUIF
	ARM64_HAS_GICV5_CPUIF

... ?

Mark.


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 01/24] Documentation: devicetree: bindings: Add GICv5 DT bindings
  2025-04-08 10:50 ` [PATCH 01/24] Documentation: devicetree: bindings: Add GICv5 DT bindings Lorenzo Pieralisi
@ 2025-04-08 12:26   ` Rob Herring (Arm)
  2025-04-08 14:58     ` Lorenzo Pieralisi
  2025-04-08 15:07   ` Rob Herring
  1 sibling, 1 reply; 62+ messages in thread
From: Rob Herring (Arm) @ 2025-04-08 12:26 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Will Deacon, Timothy Hayes, linux-arm-kernel, devicetree,
	Sascha Bischoff, Krzysztof Kozlowski, Catalin Marinas,
	Thomas Gleixner, Marc Zyngier, linux-kernel, Conor Dooley


On Tue, 08 Apr 2025 12:50:00 +0200, Lorenzo Pieralisi wrote:
> The GICv5 interrupt controller architecture is composed of:
> 
> - one or more Interrupt Routing Service (IRS)
> - zero or more Interrupt Translation Service (ITS)
> - zero or more Interrupt Wire Bridge (IWB)
> 
> Describe a GICv5 implementation by specifying a top level node
> corresponding to the GICv5 system component.
> 
> IRS nodes are added as GICv5 system component children.
> 
> An ITS is associated with an IRS so ITS nodes are described
> as IRS children - use the hierarchy explicitly in the device
> tree to define the association.
> 
> IWB nodes are described as GICv5 system component children - to make it
> explicit that are part of the GICv5 system component; an IWB is
> connected to a single ITS but the connection is made explicit through
> the msi-parent property and therefore is not required to be explicit
> through a parent-child relationship in the device tree.
> 
> Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
> Cc: Conor Dooley <conor+dt@kernel.org>
> Cc: Rob Herring <robh@kernel.org>
> Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
> Cc: Marc Zyngier <maz@kernel.org>
> ---
>  .../bindings/interrupt-controller/arm,gic-v5.yaml  | 268 +++++++++++++++++++++
>  MAINTAINERS                                        |   7 +
>  2 files changed, 275 insertions(+)
> 

My bot found errors running 'make dt_binding_check' on your patch:

yamllint warnings/errors:

dtschema/dtc warnings/errors:
Error: Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.example.dts:43.27-28 syntax error
FATAL ERROR: Unable to parse input tree
make[2]: *** [scripts/Makefile.dtbs:131: Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.example.dtb] Error 1
make[2]: *** Waiting for unfinished jobs....
make[1]: *** [/builds/robherring/dt-review-ci/linux/Makefile:1522: dt_binding_check] Error 2
make: *** [Makefile:248: __sub-make] Error 2

doc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20250408-gicv5-host-v1-1-1f26db465f8d@kernel.org

The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.



^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 01/24] Documentation: devicetree: bindings: Add GICv5 DT bindings
  2025-04-08 12:26   ` Rob Herring (Arm)
@ 2025-04-08 14:58     ` Lorenzo Pieralisi
  0 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 14:58 UTC (permalink / raw)
  To: Rob Herring (Arm)
  Cc: Will Deacon, Timothy Hayes, linux-arm-kernel, devicetree,
	Sascha Bischoff, Krzysztof Kozlowski, Catalin Marinas,
	Thomas Gleixner, Marc Zyngier, linux-kernel, Conor Dooley

On Tue, Apr 08, 2025 at 07:26:53AM -0500, Rob Herring (Arm) wrote:
> 
> On Tue, 08 Apr 2025 12:50:00 +0200, Lorenzo Pieralisi wrote:
> > The GICv5 interrupt controller architecture is composed of:
> > 
> > - one or more Interrupt Routing Service (IRS)
> > - zero or more Interrupt Translation Service (ITS)
> > - zero or more Interrupt Wire Bridge (IWB)
> > 
> > Describe a GICv5 implementation by specifying a top level node
> > corresponding to the GICv5 system component.
> > 
> > IRS nodes are added as GICv5 system component children.
> > 
> > An ITS is associated with an IRS so ITS nodes are described
> > as IRS children - use the hierarchy explicitly in the device
> > tree to define the association.
> > 
> > IWB nodes are described as GICv5 system component children - to make it
> > explicit that are part of the GICv5 system component; an IWB is
> > connected to a single ITS but the connection is made explicit through
> > the msi-parent property and therefore is not required to be explicit
> > through a parent-child relationship in the device tree.
> > 
> > Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
> > Cc: Conor Dooley <conor+dt@kernel.org>
> > Cc: Rob Herring <robh@kernel.org>
> > Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
> > Cc: Marc Zyngier <maz@kernel.org>
> > ---
> >  .../bindings/interrupt-controller/arm,gic-v5.yaml  | 268 +++++++++++++++++++++
> >  MAINTAINERS                                        |   7 +
> >  2 files changed, 275 insertions(+)
> > 
> 
> My bot found errors running 'make dt_binding_check' on your patch:
> 
> yamllint warnings/errors:
> 
> dtschema/dtc warnings/errors:
> Error: Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.example.dts:43.27-28 syntax error
> FATAL ERROR: Unable to parse input tree
> make[2]: *** [scripts/Makefile.dtbs:131: Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.example.dtb] Error 1
> make[2]: *** Waiting for unfinished jobs....
> make[1]: *** [/builds/robherring/dt-review-ci/linux/Makefile:1522: dt_binding_check] Error 2
> make: *** [Makefile:248: __sub-make] Error 2
> 
> doc reference errors (make refcheckdocs):
> 
> See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20250408-gicv5-host-v1-1-1f26db465f8d@kernel.org
> 
> The base for the series is generally the latest rc1. A different dependency
> should be noted in *this* patch.
> 
> If you already ran 'make dt_binding_check' and didn't see the above
> error(s), then make sure 'yamllint' is installed and dt-schema is up to
> date:
> 
> pip3 install dtschema --upgrade
> 
> Please check and re-submit after running the above command yourself. Note
> that DT_SCHEMA_FILES can be set to your schema file to speed up checking
> your schema. However, it must be unset to test all examples with your schema.

Validated the bindings, not the example within, that caused this to trigger,
apologies.

Already fixed it - please review the bindings though, feedback on them
appreciated, thanks.

Lorenzo


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 16/24] arm64: cpucaps: Add GCIE capability
  2025-04-08 11:26   ` Mark Rutland
@ 2025-04-08 15:02     ` Lorenzo Pieralisi
  0 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-08 15:02 UTC (permalink / raw)
  To: Mark Rutland
  Cc: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon, Sascha Bischoff,
	Timothy Hayes, linux-arm-kernel, linux-kernel, devicetree

On Tue, Apr 08, 2025 at 12:26:11PM +0100, Mark Rutland wrote:
> On Tue, Apr 08, 2025 at 12:50:15PM +0200, Lorenzo Pieralisi wrote:
> > Implement the GCIE capability as a strict boot cpu capability to
> > detect whether architectural GICv5 support is available in HW.
> > 
> > Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
> > Cc: Will Deacon <will@kernel.org>
> > Cc: Catalin Marinas <catalin.marinas@arm.com>
> > Cc: Marc Zyngier <maz@kernel.org>
> 
> This looks good; I have a minor consistency/bikeshedding concern below.
> 
> > ---
> >  arch/arm64/kernel/cpufeature.c | 7 +++++++
> >  arch/arm64/tools/cpucaps       | 1 +
> >  2 files changed, 8 insertions(+)
> > 
> > diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> > index 9c4d6d552b25cb3a31d1fb267bd73d3f82513e69..8c60591633f3d435ad9b80a10e484f26af328964 100644
> > --- a/arch/arm64/kernel/cpufeature.c
> > +++ b/arch/arm64/kernel/cpufeature.c
> > @@ -3041,6 +3041,13 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
> >  		.matches = has_pmuv3,
> >  	},
> >  #endif
> > +	{
> > +		.desc = "GCIE",
> > +		.type = ARM64_CPUCAP_STRICT_BOOT_CPU_FEATURE,
> > +		.capability = ARM64_HAS_GCIE,
> > +		.matches = has_cpuid_feature,
> > +		ARM64_CPUID_FIELDS(ID_AA64PFR2_EL1, GCIE, IMP)
> > +	},
> 
> I reckon it's worth making the desc a bit clearer, e.g. "GICv5 CPU
> interface".
> 
> It might be worth cleaning up the existing ARM64_HAS_GIC_CPUIF_SYSREGS
> feature, e.g. making that have "GICv3 CPU interface" as its desc.
> 
> Likewise, could make the names consistent, e.g. have:
> 
> 	ARM64_HAS_GICV3_CPUIF
> 	ARM64_HAS_GICV5_CPUIF
> 
> ... ?

This makes sense to me, I will do it in preparation for v2.

Thanks,
Lorenzo


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 01/24] Documentation: devicetree: bindings: Add GICv5 DT bindings
  2025-04-08 10:50 ` [PATCH 01/24] Documentation: devicetree: bindings: Add GICv5 DT bindings Lorenzo Pieralisi
  2025-04-08 12:26   ` Rob Herring (Arm)
@ 2025-04-08 15:07   ` Rob Herring
  2025-04-09  8:20     ` Lorenzo Pieralisi
  1 sibling, 1 reply; 62+ messages in thread
From: Rob Herring @ 2025-04-08 15:07 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Marc Zyngier, Thomas Gleixner, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Sascha Bischoff, Timothy Hayes,
	linux-arm-kernel, linux-kernel, devicetree

On Tue, Apr 8, 2025 at 5:50 AM Lorenzo Pieralisi <lpieralisi@kernel.org> wrote:
>

No need to say DT bindings twice in the subject line. Follow the
subsystem conventions.

dt-bindings: interrupt-controller: Add Arm GICv5

> The GICv5 interrupt controller architecture is composed of:
>
> - one or more Interrupt Routing Service (IRS)
> - zero or more Interrupt Translation Service (ITS)
> - zero or more Interrupt Wire Bridge (IWB)
>
> Describe a GICv5 implementation by specifying a top level node
> corresponding to the GICv5 system component.
>
> IRS nodes are added as GICv5 system component children.
>
> An ITS is associated with an IRS so ITS nodes are described
> as IRS children - use the hierarchy explicitly in the device
> tree to define the association.
>
> IWB nodes are described as GICv5 system component children - to make it
> explicit that are part of the GICv5 system component; an IWB is
> connected to a single ITS but the connection is made explicit through
> the msi-parent property and therefore is not required to be explicit
> through a parent-child relationship in the device tree.
>
> Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
> Cc: Conor Dooley <conor+dt@kernel.org>
> Cc: Rob Herring <robh@kernel.org>
> Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
> Cc: Marc Zyngier <maz@kernel.org>
> ---
>  .../bindings/interrupt-controller/arm,gic-v5.yaml  | 268 +++++++++++++++++++++
>  MAINTAINERS                                        |   7 +
>  2 files changed, 275 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.yaml b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.yaml
> new file mode 100644
> index 0000000000000000000000000000000000000000..5c78375c298a0115c55872f439eb04d4171c4381
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.yaml
> @@ -0,0 +1,268 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/interrupt-controller/arm,gic-v5.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: ARM Generic Interrupt Controller, version 5
> +
> +maintainers:
> +  - Lorenzo Pieralisi <lpieralisi@kernel.org>
> +  - Marc Zyngier <maz@kernel.org>
> +
> +description: |
> +  The GICv5 architecture defines the guidelines to implement GICv5
> +  compliant interrupt controllers for AArch64 systems.
> +
> +  The GICv5 specification can be found at
> +  https://developer.arm.com/documentation/aes0070
> +
> +  The GICv5 architecture is composed of multiple components:
> +    - one or more IRS (Interrupt Routing Service)
> +    - zero or more ITS (Interrupt Translation Service)
> +    - zero or more IWB (Interrupt Wire Bridge)
> +
> +  The architecture defines:
> +    - PE-Private Peripheral Interrupts (PPI)
> +    - Shared Peripheral Interrupts (SPI)
> +    - Logical Peripheral Interrupts (LPI)
> +
> +allOf:
> +  - $ref: /schemas/interrupt-controller.yaml#
> +
> +properties:
> +  compatible:
> +    const: arm,gic-v5
> +
> +  interrupt-controller: true
> +
> +  "#address-cells":
> +    enum: [ 1, 2 ]

blank line

> +  "#size-cells":
> +    enum: [ 1, 2 ]
> +
> +  ranges: true
> +
> +  "#interrupt-cells":
> +    description: |
> +      Specifies the number of cells needed to encode an interrupt source.
> +      Must be a single cell with a value 3.

Drop this paragraph. The first sentence is just describing a common
property. The 2nd is expressed as schema already.

> +
> +      The 1st cell corresponds to the INTID.Type field in the INTID; 1 for PPI,
> +      3 for SPI. LPI interrupts must not be described in the bindings since
> +      they are allocated dynamically by the software component managing them.
> +
> +      The 2nd cell contains the interrupt INTID.ID field.
> +
> +      The 3rd cell is the flags, encoded as follows:
> +      bits[3:0] trigger type and level flags.
> +
> +        1 = low-to-high edge triggered
> +        2 = high-to-low edge triggered
> +        4 = active high level-sensitive
> +        8 = active low level-sensitive
> +
> +      Cells 4 and beyond are reserved for future use and must have a value
> +      of 0 if present.

Drop. You can't have 4 or more cells because only 3 is allowed:

> +    const: 3
> +
> +  interrupts:
> +    description:
> +      Interrupt source of the VGIC maintenance interrupt.

Drop "Interrupt source of ".

> +    maxItems: 1
> +
> +required:
> +  - compatible
> +
> +patternProperties:
> +  "^irs@[0-9a-f]+$":
> +    type: object
> +    description:
> +      GICv5 has one or more Interrupt Routing Services (IRS) that are
> +      responsible for handling IRQ state and routing.
> +
> +    additionalProperties: false

blank line

> +    properties:
> +      compatible:
> +        const: arm,gic-v5-irs
> +
> +      "#address-cells":
> +        enum: [ 1, 2 ]

blank line

> +      "#size-cells":
> +        enum: [ 1, 2 ]
> +
> +      ranges: true
> +
> +      dma-noncoherent:
> +        description:
> +          Present if the GIC IRS permits programming shareability and
> +          cacheability attributes but is connected to a non-coherent
> +          downstream interconnect.
> +
> +      reg:
> +        minItems: 1
> +        items:
> +          - description: IRS control frame
> +          - description: IRS setlpi frame
> +
> +      cpus:
> +        $ref: /schemas/types.yaml#/definitions/phandle-array

Already has a type, drop.

> +        description:
> +          Should be a list of phandles to CPU nodes (as described in
> +          Documentation/devicetree/bindings/arm/cpus.yaml) corresponding to
> +          CPUs managed by the IRS.

The actual cpu binding is outside the scope of this binding. Just
'CPUs managed by the IRS.' is enough.

Is there a maximum number of CPUs?

> +
> +      arm,iaffids:
> +        $ref: /schemas/types.yaml#/definitions/uint16-array
> +        description:
> +          Should be a list of u16 values representing IAFFID IDs associated

The type says it is 'a list of u16 values', so don't repeat that here.
IAFFID needs to be defined somewhere. Is the 2nd 'ID' redundant?

> +          with the CPU whose CPU node phandle is at the same index in the
> +          cpus array.
> +
> +    patternProperties:
> +      "^msi-controller@[0-9a-f]+$":
> +        type: object
> +        description:
> +          GICv5 has zero or more Interrupt Translation Services (ITS) that are
> +          used to route Message Signalled Interrupts (MSI) to the CPUs. Each
> +          ITS is connected to an IRS.
> +        additionalProperties: false

blank line

> +        properties:
> +          compatible:
> +            const: arm,gic-v5-its
> +
> +          dma-noncoherent:
> +            description:
> +              Present if the GIC ITS permits programming shareability and
> +              cacheability attributes but is connected to a non-coherent
> +              downstream interconnect.
> +
> +          msi-controller: true
> +
> +          "#msi-cells":
> +            description:
> +              The single msi-cell is the DeviceID of the device which will
> +              generate the MSI.
> +            const: 1
> +
> +          reg:
> +            items:
> +              - description: ITS control frame
> +              - description: ITS translate frame
> +
> +        required:
> +          - compatible
> +          - msi-controller
> +          - "#msi-cells"
> +          - reg
> +
> +    required:
> +      - compatible
> +      - reg
> +      - cpus
> +      - arm,iaffids
> +
> +  "^interrupt-controller@[0-9a-f]+$":
> +    type: object
> +    description:
> +      GICv5 has zero or more Interrupt Wire Bridges (IWB) that are responsible
> +      for translating wire signals into interrupt messages to the ITS.

I wonder if this should be a separate schema and not a child of the
GIC? Seems like these would be implemented standalone (even if the
arch doesn't define that) at arbitrary addresses that aren't within
the GIC's address range. To put it another way, there's nothing here
it is getting from the parent node.

> +
> +    additionalProperties: false
> +    properties:
> +      compatible:
> +        const: arm,gic-v5-iwb
> +
> +      interrupt-controller: true
> +
> +      "#address-cells":
> +        const: 0
> +
> +      "#interrupt-cells":
> +        description: |
> +          Specifies the number of cells needed to encode an interrupt source.
> +          Must be a single cell with a value 2.

Drop

> +
> +          The 1st cell corresponds to the IWB wire.
> +
> +          The 2nd cell is the flags, encoded as follows:
> +          bits[3:0] trigger type and level flags.
> +
> +          1 = low-to-high edge triggered
> +          2 = high-to-low edge triggered
> +          4 = active high level-sensitive
> +          8 = active low level-sensitive
> +
> +          Cells 3 and beyond are reserved for future use and must have a value
> +          of 0 if present.

Drop

> +        const: 2
> +
> +      reg:
> +        items:
> +          - description: IWB control frame
> +
> +      msi-parent: true

maxItems: 1

Because the common definition allows any number of parents.

> +
> +    required:
> +      - compatible
> +      - reg
> +      - msi-parent
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    interrupt-controller {
> +      compatible = "arm,gic-v5";
> +      #interrupt-cells = <3>;
> +      #address-cells = <2>;
> +      #size-cells = <2>;
> +      ranges;
> +
> +      interrupt-controller;
> +
> +      interrupts = <1 25 4>;
> +
> +      irs@2f1a0000 {
> +        compatible = "arm,gic-v5-irs";
> +        #address-cells = <2>;
> +        #size-cells = <2>;
> +        ranges;
> +
> +        reg = <0x0 0x2f1a0000 0x0 0x10000>;  // IRS_CONFIG_FRAME for NS
> +
> +        arm,iaffids = /bits 16 <0 1 2 3 4 5 6 7>;
> +        cpus = <&{/cpus/cpu@0}>, <&{/cpus/cpu@100}>, <&{/cpus/cpu@200}>,
> +               <&{/cpus/cpu@300}>, <&{/cpus/cpu@10000}>, <&{/cpus/cpu@10100}>,
> +               <&{/cpus/cpu@10200}>, <&{/cpus/cpu@10300}>;

Use labels instead of paths.

> +
> +        msi-controller@2f120000 {
> +          compatible = "arm,gic-v5-its";
> +
> +          msi-controller;
> +          #msi-cells = <1>;
> +
> +          reg = <0x0 0x2f120000 0x0 0x10000    // ITS_CONFIG_FRAME for NS
> +                 0x0 0x2f130000 0x0 0x10000>;  // ITS_TRANSLATE_FRAME
> +        };
> +      };
> +
> +      interrupt-controller@2f000000 {
> +        compatible = "arm,gic-v5-iwb";
> +        #address-cells = <0>;
> +
> +        interrupt-controller;
> +        #interrupt-cells = <2>;
> +
> +        reg = <0x0 0x2f000000 0x0 0x10000>;
> +
> +        msi-parent = <&its0 64>;
> +      };
> +    };
> +
> +    device@0 {

Drop. We don't put consumers in provider examples and vice-versa.

> +      reg = <0 4>;
> +      interrupts = <3 115 4>;
> +    };
> +
> +...


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 18/24] irqchip/gic-v5: Add GICv5 PPI support
  2025-04-08 10:50 ` [PATCH 18/24] irqchip/gic-v5: Add GICv5 PPI support Lorenzo Pieralisi
@ 2025-04-08 21:42   ` Thomas Gleixner
  2025-04-09  7:30     ` Lorenzo Pieralisi
  2025-04-11 17:06     ` Lorenzo Pieralisi
  0 siblings, 2 replies; 62+ messages in thread
From: Thomas Gleixner @ 2025-04-08 21:42 UTC (permalink / raw)
  To: Lorenzo Pieralisi, Marc Zyngier, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

On Tue, Apr 08 2025 at 12:50, Lorenzo Pieralisi wrote:
> +
> +static void gicv5_ppi_priority_init(void)
> +{
> +	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRIORITY_MI),
> +				 SYS_ICC_PPI_PRIORITYR0_EL1);

Just let stick it out. You have 100 characters. All over the place...

> +static int gicv5_ppi_irq_set_irqchip_state(struct irq_data *d,
> +					   enum irqchip_irq_state which,
> +					   bool val)
> +{
> +	u64 hwirq_id_bit = BIT_ULL(d->hwirq % 64);
> +
> +	switch (which) {
> +	case IRQCHIP_STATE_PENDING:
> +		if (val) {
> +			if (d->hwirq < 64)
> +				write_sysreg_s(hwirq_id_bit,
> +					       SYS_ICC_PPI_SPENDR0_EL1);
> +			else
> +				write_sysreg_s(hwirq_id_bit,
> +					       SYS_ICC_PPI_SPENDR1_EL1);
> +
> +		} else {
> +			if (d->hwirq < 64)
> +				write_sysreg_s(hwirq_id_bit,
> +					       SYS_ICC_PPI_CPENDR0_EL1);
> +			else
> +				write_sysreg_s(hwirq_id_bit,
> +					       SYS_ICC_PPI_CPENDR1_EL1);
> +		}
> +
> +		return 0;
> +	case IRQCHIP_STATE_ACTIVE:
> +		if (val) {
> +			if (d->hwirq < 64)
> +				write_sysreg_s(hwirq_id_bit,
> +					       SYS_ICC_PPI_SACTIVER0_EL1);
> +			else
> +				write_sysreg_s(hwirq_id_bit,
> +					       SYS_ICC_PPI_SACTIVER1_EL1);
> +		} else {
> +			if (d->hwirq < 64)
> +				write_sysreg_s(hwirq_id_bit,
> +					       SYS_ICC_PPI_CACTIVER0_EL1);
> +			else
> +				write_sysreg_s(hwirq_id_bit,
> +					       SYS_ICC_PPI_CACTIVER1_EL1);
> +		}

You already precalculate hwirq_id_bit. Can't you do something similar
for the registers?

	case IRQCHIP_STATE_PENDING:
        	u32 reg = val ? SYS_ICC_PPI_SPENDR1_EL1 : SYS_ICC_PPI_SPENDR0_EL1;

                write_sysreg_s(hwirq_id_bit, reg);
                return 0;
	case IRQCHIP_STATE_ACTIVE:
                ....

Ditto in the get_state() function.

No?

> +static int gicv5_irq_ppi_domain_translate(struct irq_domain *d,
> +					  struct irq_fwspec *fwspec,
> +					  irq_hw_number_t *hwirq,
> +					  unsigned int *type)
> +{
> +	if (is_of_node(fwspec->fwnode)) {

It'd be way more readable to invert this check

     if (!is_of_node(...))
     	return -EINVAL;

so that the subsequent checks are just a read through.

> +		if (fwspec->param_count < 3)
> +			return -EINVAL;
> +
> +		if (fwspec->param[0] != GICV5_HWIRQ_TYPE_PPI)
> +			return -EINVAL;
> +
> +		*hwirq = fwspec->param[1];
> +		*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
> +
> +		return 0;
> +	}
> +
> +	return -EINVAL;
> +}

> +static void gicv5_irq_ppi_domain_free(struct irq_domain *domain,
> +				      unsigned int virq, unsigned int nr_irqs)
> +{
> +	struct irq_data *d;
> +
> +	if (WARN_ON(nr_irqs != 1))

WARN_ON_ONCE ?

> +		return;
> +
> +	d = irq_domain_get_irq_data(domain, virq);
> +
> +	irq_set_handler(virq, NULL);
> +	irq_domain_reset_irq_data(d);
> +}
> +
> +static int gicv5_irq_ppi_domain_select(struct irq_domain *d,
> +				       struct irq_fwspec *fwspec,
> +				       enum irq_domain_bus_token bus_token)
> +{
> +	/* Not for us */
> +	if (fwspec->fwnode != d->fwnode)
> +		return 0;
> +
> +	if (fwspec->param[0] != GICV5_HWIRQ_TYPE_PPI) {
> +		// only handle PPIs

Commenting the obvious?

> +		return 0;
> +	}
> +
> +	return (d == gicv5_global_data.ppi_domain);
> +}
> +
> +static const struct irq_domain_ops gicv5_irq_ppi_domain_ops = {
> +	.translate	= gicv5_irq_ppi_domain_translate,
> +	.alloc		= gicv5_irq_ppi_domain_alloc,
> +	.free		= gicv5_irq_ppi_domain_free,
> +	.select		= gicv5_irq_ppi_domain_select
> +};
> +
> +static inline void handle_irq_per_domain(u32 hwirq)
> +{
> +	u32 hwirq_id;
> +	struct irq_domain *domain = NULL;
> +	u8 hwirq_type = FIELD_GET(GICV5_HWIRQ_TYPE, hwirq);

So far you managed to comply with the documented reverse fir tree
ordering.

  https://www.kernel.org/doc/html/latest/process/maintainer-tip.html#variable-declarations

Why are you changing coding style in the middle of the code?

> +
> +	hwirq_id = FIELD_GET(GICV5_HWIRQ_ID, hwirq);
> +
> +	if (hwirq_type == GICV5_HWIRQ_TYPE_PPI)
> +		domain = gicv5_global_data.ppi_domain;
> +
> +	if (generic_handle_domain_irq(domain, hwirq_id)) {
> +		pr_err("Could not handle, hwirq = 0x%x", hwirq_id);

pr_err_once() perhaps?

> +		gicv5_hwirq_eoi(hwirq_id, hwirq_type);
> +	}
> +}
> +
> +static asmlinkage void __exception_irq_entry
> +gicv5_handle_irq(struct pt_regs *regs)
> +{
> +	u64 ia;
> +	bool valid;
> +	u32 hwirq;

See above

> +	ia = gicr_insn(GICV5_OP_GICR_CDIA);
> +	valid = GICV5_GIC_CDIA_VALID(ia);

And please move that to the declaration lines

> +static int __init gicv5_init_domains(struct fwnode_handle *handle)
> +{
> +	gicv5_global_data.fwnode = handle;
> +	gicv5_global_data.ppi_domain = irq_domain_create_linear(
> +		handle, 128, &gicv5_irq_ppi_domain_ops, NULL);

The ever changing choice of coding styles across functions is really
interesting. Obviously the length of 'gicv5_global_data.ppi_domain'
forces ugly, but that does not mean it needs to be that way:

       struct irqdomain *d;

       d = irq_domain_create_linear(handle, 128, &gicv5_irq_ppi_domain_ops, NULL);
       if (!d)
       		return - ENOMEM;

       irq_domain_update_bus_token(d, DOMAIN_BUS_WIRED);
       gicv5_global_data.fwnode = handle;
       gicv5_global_data.ppi_domain = d;
       return 0;

No?

> +static int __init gicv5_of_init(struct device_node *node,
> +				struct device_node *parent)
> +{
> +	int ret;
> +
> +	ret = gicv5_init_domains(&node->fwnode);
> +	if (ret)
> +		return ret;
> +
> +	gicv5_set_cpuif_pribits();
> +
> +	ret = gicv5_starting_cpu(smp_processor_id());

You invoke the CPU hotplug callback for the boot CPU explicitly, but
what the heck installs the actual hotplug callback for the secondary
CPUs?

Thanks,

        tglx


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 19/24] irqchip/gic-v5: Add GICv5 IRS/SPI support
  2025-04-08 10:50 ` [PATCH 19/24] irqchip/gic-v5: Add GICv5 IRS/SPI support Lorenzo Pieralisi
@ 2025-04-09  7:02   ` Thomas Gleixner
  2025-04-09  7:40     ` Lorenzo Pieralisi
  0 siblings, 1 reply; 62+ messages in thread
From: Thomas Gleixner @ 2025-04-09  7:02 UTC (permalink / raw)
  To: Lorenzo Pieralisi, Marc Zyngier, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

On Tue, Apr 08 2025 at 12:50, Lorenzo Pieralisi wrote:
> +struct iaffid_entry {
> +	u16 iaffid;
> +	bool valid;
> +};

See the previous documentation link and search for struct definitions on
that page.

> +static int gicv5_irs_wait_for_spi_op(struct gicv5_irs_chip_data *irs_data)
> +{
> +	int ret;
> +	u32 statusr;

See documentaion...

> +	ret = readl_relaxed_poll_timeout_atomic(
> +			irs_data->irs_base + GICV5_IRS_SPI_STATUSR, statusr,
> +			FIELD_GET(GICV5_IRS_SPI_STATUSR_IDLE, statusr), 1,
> +			USEC_PER_SEC);

See previous mail about how to make stuff like this readable. My eyes
bleed already.

> +	if (ret == -ETIMEDOUT) {

unlikely(ret == ...) perhaps?

> +		pr_err_ratelimited("Time out waiting for IRS SPI to be configured\n");

> +static int __init gicv5_irs_init_bases(struct gicv5_irs_chip_data *irs_data,
> +				       void __iomem *irs_base,
> +				       struct fwnode_handle *handle)
> +{
> +	u32 cr0, cr1;
> +	struct device_node *np = to_of_node(handle);

Sigh

> +static int __init gicv5_irs_of_init_affinity(struct device_node *node,
> +				      struct gicv5_irs_chip_data *irs_data,
> +				      u8 iaffid_bits)

Moar random coding style choices.

> +{
> +	/*
> +	 * Detect IAFFID<->CPU mappings from the device tree and
> +	 * record IRS<->CPU topology information.
> +	 */
> +	int ret, i, ncpus, niaffids;
> +	u16 *iaffids;
> +	u16 iaffid_mask = GENMASK(iaffid_bits - 1, 0);
> +
> +	ncpus = of_property_count_elems_of_size(node, "cpus", sizeof(u32));
> +	if (WARN_ON(ncpus < 0))
> +		return -EINVAL;

Do you really need all these warnings?

> +
> +	niaffids = of_property_count_elems_of_size(node, "arm,iaffids",
> +						   sizeof(u16));
> +	if (WARN_ON(niaffids != ncpus))
> +		return -EINVAL;
> +
> +	iaffids = kcalloc(niaffids, sizeof(*iaffids), GFP_KERNEL);
> +	if (!iaffids)
> +		return -ENOMEM;
> +
> +	ret = of_property_read_u16_array(node, "arm,iaffids", iaffids, niaffids);
> +	if (ret)
> +		return ret;

Leaks iaffids. Please use

      u16 *iaffids __free(kfree) = kcalloc(...);

and the compiler will take care of that.

> +static int __init gicv5_irs_init(struct device_node *node)
> +{
> +	void __iomem *irs_base;
> +	struct gicv5_irs_chip_data *irs_data;
> +	int ret;
> +	u32 idr;
> +	u8 iaffid_bits;
> +
> +	irs_data = kzalloc(sizeof(*irs_data), GFP_KERNEL);

__free(kfree)

> +	if (!irs_data)
> +		return -ENOMEM;

> +	if (irs_data->spi_range)
> +		pr_info("%s detected SPI range [%u-%u]\n",
> +						of_node_full_name(node),
> +						irs_data->spi_min,
> +						irs_data->spi_min +
> +						irs_data->spi_range - 1);

Please put those _five_ lines into brackets. It's not required by the
compiler, but for reading. Brackets should be omitted only if the
statement which follows ‘if’, ‘for’, ‘while’ etc. is truly a single
line.

> +static int gicv5_iri_irq_get_irqchip_state(struct irq_data *d,
> +					   enum irqchip_irq_state which,
> +					   bool *val, u8 hwirq_type)
> +{
> +	u64 icsr, cdrcfg = d->hwirq | FIELD_PREP(GICV5_GIC_CDRCFG_TYPE_MASK,
> +						 hwirq_type);
> +
> +	preempt_disable();

That's required because the calling contexts protection (raw spinlock
held and interrupts disabled) is not enough, right?

> +	gic_insn(cdrcfg, GICV5_OP_GIC_CDRCFG);
> +	isb();
> +	icsr = read_sysreg_s(SYS_ICC_ICSR_EL1);
> +	preempt_enable();

> +static int gicv5_irq_spi_domain_translate(struct irq_domain *d,
> +					  struct irq_fwspec *fwspec,
> +					  irq_hw_number_t *hwirq,
> +					  unsigned int *type)
> +{
> +	if (is_of_node(fwspec->fwnode)) {
> +		if (fwspec->param_count < 3)
> +			return -EINVAL;
> +
> +		if (fwspec->param[0] != GICV5_HWIRQ_TYPE_SPI)
> +			return -EINVAL;
> +
> +		*hwirq = fwspec->param[1];
> +		*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
> +
> +		return 0;
> +	}

The only difference between this and the ppi variant is the type check
of param[0]. Common helper perhaps?

Thanks,

        tglx


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 18/24] irqchip/gic-v5: Add GICv5 PPI support
  2025-04-08 21:42   ` Thomas Gleixner
@ 2025-04-09  7:30     ` Lorenzo Pieralisi
  2025-04-17 14:49       ` Lorenzo Pieralisi
  2025-04-11 17:06     ` Lorenzo Pieralisi
  1 sibling, 1 reply; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-09  7:30 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Sascha Bischoff, Timothy Hayes,
	linux-arm-kernel, linux-kernel, devicetree

On Tue, Apr 08, 2025 at 11:42:29PM +0200, Thomas Gleixner wrote:
> On Tue, Apr 08 2025 at 12:50, Lorenzo Pieralisi wrote:
> > +
> > +static void gicv5_ppi_priority_init(void)
> > +{
> > +	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRIORITY_MI),
> > +				 SYS_ICC_PPI_PRIORITYR0_EL1);
> 
> Just let stick it out. You have 100 characters. All over the place...

I will do.

> > +static int gicv5_ppi_irq_set_irqchip_state(struct irq_data *d,
> > +					   enum irqchip_irq_state which,
> > +					   bool val)
> > +{
> > +	u64 hwirq_id_bit = BIT_ULL(d->hwirq % 64);
> > +
> > +	switch (which) {
> > +	case IRQCHIP_STATE_PENDING:
> > +		if (val) {
> > +			if (d->hwirq < 64)
> > +				write_sysreg_s(hwirq_id_bit,
> > +					       SYS_ICC_PPI_SPENDR0_EL1);
> > +			else
> > +				write_sysreg_s(hwirq_id_bit,
> > +					       SYS_ICC_PPI_SPENDR1_EL1);
> > +
> > +		} else {
> > +			if (d->hwirq < 64)
> > +				write_sysreg_s(hwirq_id_bit,
> > +					       SYS_ICC_PPI_CPENDR0_EL1);
> > +			else
> > +				write_sysreg_s(hwirq_id_bit,
> > +					       SYS_ICC_PPI_CPENDR1_EL1);
> > +		}
> > +
> > +		return 0;
> > +	case IRQCHIP_STATE_ACTIVE:
> > +		if (val) {
> > +			if (d->hwirq < 64)
> > +				write_sysreg_s(hwirq_id_bit,
> > +					       SYS_ICC_PPI_SACTIVER0_EL1);
> > +			else
> > +				write_sysreg_s(hwirq_id_bit,
> > +					       SYS_ICC_PPI_SACTIVER1_EL1);
> > +		} else {
> > +			if (d->hwirq < 64)
> > +				write_sysreg_s(hwirq_id_bit,
> > +					       SYS_ICC_PPI_CACTIVER0_EL1);
> > +			else
> > +				write_sysreg_s(hwirq_id_bit,
> > +					       SYS_ICC_PPI_CACTIVER1_EL1);
> > +		}
> 
> You already precalculate hwirq_id_bit. Can't you do something similar
> for the registers?
> 
> 	case IRQCHIP_STATE_PENDING:
>         	u32 reg = val ? SYS_ICC_PPI_SPENDR1_EL1 : SYS_ICC_PPI_SPENDR0_EL1;
> 
>                 write_sysreg_s(hwirq_id_bit, reg);
>                 return 0;
> 	case IRQCHIP_STATE_ACTIVE:
>                 ....
> 
> Ditto in the get_state() function.
> 
> No?

Yes, more readable.

> > +static int gicv5_irq_ppi_domain_translate(struct irq_domain *d,
> > +					  struct irq_fwspec *fwspec,
> > +					  irq_hw_number_t *hwirq,
> > +					  unsigned int *type)
> > +{
> > +	if (is_of_node(fwspec->fwnode)) {
> 
> It'd be way more readable to invert this check
> 
>      if (!is_of_node(...))
>      	return -EINVAL;
> 
> so that the subsequent checks are just a read through.

Will do.

> > +		if (fwspec->param_count < 3)
> > +			return -EINVAL;
> > +
> > +		if (fwspec->param[0] != GICV5_HWIRQ_TYPE_PPI)
> > +			return -EINVAL;
> > +
> > +		*hwirq = fwspec->param[1];
> > +		*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
> > +
> > +		return 0;
> > +	}
> > +
> > +	return -EINVAL;
> > +}
> 
> > +static void gicv5_irq_ppi_domain_free(struct irq_domain *domain,
> > +				      unsigned int virq, unsigned int nr_irqs)
> > +{
> > +	struct irq_data *d;
> > +
> > +	if (WARN_ON(nr_irqs != 1))
> 
> WARN_ON_ONCE ?

Yes.

> > +		return;
> > +
> > +	d = irq_domain_get_irq_data(domain, virq);
> > +
> > +	irq_set_handler(virq, NULL);
> > +	irq_domain_reset_irq_data(d);
> > +}
> > +
> > +static int gicv5_irq_ppi_domain_select(struct irq_domain *d,
> > +				       struct irq_fwspec *fwspec,
> > +				       enum irq_domain_bus_token bus_token)
> > +{
> > +	/* Not for us */
> > +	if (fwspec->fwnode != d->fwnode)
> > +		return 0;
> > +
> > +	if (fwspec->param[0] != GICV5_HWIRQ_TYPE_PPI) {
> > +		// only handle PPIs
> 
> Commenting the obvious?
> 

Will remove it.

> > +		return 0;
> > +	}
> > +
> > +	return (d == gicv5_global_data.ppi_domain);
> > +}
> > +
> > +static const struct irq_domain_ops gicv5_irq_ppi_domain_ops = {
> > +	.translate	= gicv5_irq_ppi_domain_translate,
> > +	.alloc		= gicv5_irq_ppi_domain_alloc,
> > +	.free		= gicv5_irq_ppi_domain_free,
> > +	.select		= gicv5_irq_ppi_domain_select
> > +};
> > +
> > +static inline void handle_irq_per_domain(u32 hwirq)
> > +{
> > +	u32 hwirq_id;
> > +	struct irq_domain *domain = NULL;
> > +	u8 hwirq_type = FIELD_GET(GICV5_HWIRQ_TYPE, hwirq);
> 
> So far you managed to comply with the documented reverse fir tree
> ordering.
> 
>   https://www.kernel.org/doc/html/latest/process/maintainer-tip.html#variable-declarations
> 
> Why are you changing coding style in the middle of the code?

Mea culpa, don't bother commenting on this further, point taken.

> > +	hwirq_id = FIELD_GET(GICV5_HWIRQ_ID, hwirq);
> > +
> > +	if (hwirq_type == GICV5_HWIRQ_TYPE_PPI)
> > +		domain = gicv5_global_data.ppi_domain;
> > +
> > +	if (generic_handle_domain_irq(domain, hwirq_id)) {
> > +		pr_err("Could not handle, hwirq = 0x%x", hwirq_id);
> 
> pr_err_once() perhaps?
> 
> > +		gicv5_hwirq_eoi(hwirq_id, hwirq_type);
> > +	}
> > +}
> > +
> > +static asmlinkage void __exception_irq_entry
> > +gicv5_handle_irq(struct pt_regs *regs)
> > +{
> > +	u64 ia;
> > +	bool valid;
> > +	u32 hwirq;
> 
> See above
> 
> > +	ia = gicr_insn(GICV5_OP_GICR_CDIA);
> > +	valid = GICV5_GIC_CDIA_VALID(ia);
> 
> And please move that to the declaration lines
> 
> > +static int __init gicv5_init_domains(struct fwnode_handle *handle)
> > +{
> > +	gicv5_global_data.fwnode = handle;
> > +	gicv5_global_data.ppi_domain = irq_domain_create_linear(
> > +		handle, 128, &gicv5_irq_ppi_domain_ops, NULL);
> 
> The ever changing choice of coding styles across functions is really
> interesting. Obviously the length of 'gicv5_global_data.ppi_domain'
> forces ugly, but that does not mean it needs to be that way:
> 
>        struct irqdomain *d;
> 
>        d = irq_domain_create_linear(handle, 128, &gicv5_irq_ppi_domain_ops, NULL);
>        if (!d)
>        		return - ENOMEM;
> 
>        irq_domain_update_bus_token(d, DOMAIN_BUS_WIRED);
>        gicv5_global_data.fwnode = handle;
>        gicv5_global_data.ppi_domain = d;
>        return 0;
> 
> No?

Yes it is better.

> > +static int __init gicv5_of_init(struct device_node *node,
> > +				struct device_node *parent)
> > +{
> > +	int ret;
> > +
> > +	ret = gicv5_init_domains(&node->fwnode);
> > +	if (ret)
> > +		return ret;
> > +
> > +	gicv5_set_cpuif_pribits();
> > +
> > +	ret = gicv5_starting_cpu(smp_processor_id());
> 
> You invoke the CPU hotplug callback for the boot CPU explicitly, but
> what the heck installs the actual hotplug callback for the secondary
> CPUs?

That comes with a subsequent patch[21]. I mentioned in the cover letter
that I tried to split the functionality into interrupt types to ease
review (well, it does not look like I succeeded, sorry) and then in
patch [21] (when LPIs backing IPIs are implemented), enable SMP.

The point is, we need patches [18-21] to enable SMP booting.

I can squash [18-21] all together or I can enable the hotplug callback
here but this patch stand alone is not functional for the reasons
above, let me know please what's best in your opinion and I will do.

Above all, thank you very much for reviewing the series.

Lorenzo


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 19/24] irqchip/gic-v5: Add GICv5 IRS/SPI support
  2025-04-09  7:02   ` Thomas Gleixner
@ 2025-04-09  7:40     ` Lorenzo Pieralisi
  0 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-09  7:40 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Sascha Bischoff, Timothy Hayes,
	linux-arm-kernel, linux-kernel, devicetree

On Wed, Apr 09, 2025 at 09:02:58AM +0200, Thomas Gleixner wrote:
> On Tue, Apr 08 2025 at 12:50, Lorenzo Pieralisi wrote:
> > +struct iaffid_entry {
> > +	u16 iaffid;
> > +	bool valid;
> > +};
> 
> See the previous documentation link and search for struct definitions on
> that page.

Right, will fix.

> > +static int gicv5_irs_wait_for_spi_op(struct gicv5_irs_chip_data *irs_data)
> > +{
> > +	int ret;
> > +	u32 statusr;
> 
> See documentaion...
> 
> > +	ret = readl_relaxed_poll_timeout_atomic(
> > +			irs_data->irs_base + GICV5_IRS_SPI_STATUSR, statusr,
> > +			FIELD_GET(GICV5_IRS_SPI_STATUSR_IDLE, statusr), 1,
> > +			USEC_PER_SEC);
> 
> See previous mail about how to make stuff like this readable. My eyes
> bleed already.
> 
> > +	if (ret == -ETIMEDOUT) {
> 
> unlikely(ret == ...) perhaps?

Will change it.

> > +		pr_err_ratelimited("Time out waiting for IRS SPI to be configured\n");
> 
> > +static int __init gicv5_irs_init_bases(struct gicv5_irs_chip_data *irs_data,
> > +				       void __iomem *irs_base,
> > +				       struct fwnode_handle *handle)
> > +{
> > +	u32 cr0, cr1;
> > +	struct device_node *np = to_of_node(handle);
> 
> Sigh
> 
> > +static int __init gicv5_irs_of_init_affinity(struct device_node *node,
> > +				      struct gicv5_irs_chip_data *irs_data,
> > +				      u8 iaffid_bits)
> 
> Moar random coding style choices.
> 
> > +{
> > +	/*
> > +	 * Detect IAFFID<->CPU mappings from the device tree and
> > +	 * record IRS<->CPU topology information.
> > +	 */
> > +	int ret, i, ncpus, niaffids;
> > +	u16 *iaffids;
> > +	u16 iaffid_mask = GENMASK(iaffid_bits - 1, 0);
> > +
> > +	ncpus = of_property_count_elems_of_size(node, "cpus", sizeof(u32));
> > +	if (WARN_ON(ncpus < 0))
> > +		return -EINVAL;
> 
> Do you really need all these warnings?

I will review them.

> > +
> > +	niaffids = of_property_count_elems_of_size(node, "arm,iaffids",
> > +						   sizeof(u16));
> > +	if (WARN_ON(niaffids != ncpus))
> > +		return -EINVAL;
> > +
> > +	iaffids = kcalloc(niaffids, sizeof(*iaffids), GFP_KERNEL);
> > +	if (!iaffids)
> > +		return -ENOMEM;
> > +
> > +	ret = of_property_read_u16_array(node, "arm,iaffids", iaffids, niaffids);
> > +	if (ret)
> > +		return ret;
> 
> Leaks iaffids. Please use
> 
>       u16 *iaffids __free(kfree) = kcalloc(...);
> 
> and the compiler will take care of that.

Yes, that's silly.

> > +static int __init gicv5_irs_init(struct device_node *node)
> > +{
> > +	void __iomem *irs_base;
> > +	struct gicv5_irs_chip_data *irs_data;
> > +	int ret;
> > +	u32 idr;
> > +	u8 iaffid_bits;
> > +
> > +	irs_data = kzalloc(sizeof(*irs_data), GFP_KERNEL);
> 
> __free(kfree)

Will do.

> > +	if (!irs_data)
> > +		return -ENOMEM;
> 
> > +	if (irs_data->spi_range)
> > +		pr_info("%s detected SPI range [%u-%u]\n",
> > +						of_node_full_name(node),
> > +						irs_data->spi_min,
> > +						irs_data->spi_min +
> > +						irs_data->spi_range - 1);
> 
> Please put those _five_ lines into brackets. It's not required by the
> compiler, but for reading. Brackets should be omitted only if the
> statement which follows ‘if’, ‘for’, ‘while’ etc. is truly a single
> line.

Ok.

> > +static int gicv5_iri_irq_get_irqchip_state(struct irq_data *d,
> > +					   enum irqchip_irq_state which,
> > +					   bool *val, u8 hwirq_type)
> > +{
> > +	u64 icsr, cdrcfg = d->hwirq | FIELD_PREP(GICV5_GIC_CDRCFG_TYPE_MASK,
> > +						 hwirq_type);
> > +
> > +	preempt_disable();
> 
> That's required because the calling contexts protection (raw spinlock
> held and interrupts disabled) is not enough, right?

Yes it is useless, I will remove it.

> > +	gic_insn(cdrcfg, GICV5_OP_GIC_CDRCFG);
> > +	isb();
> > +	icsr = read_sysreg_s(SYS_ICC_ICSR_EL1);
> > +	preempt_enable();
> 
> > +static int gicv5_irq_spi_domain_translate(struct irq_domain *d,
> > +					  struct irq_fwspec *fwspec,
> > +					  irq_hw_number_t *hwirq,
> > +					  unsigned int *type)
> > +{
> > +	if (is_of_node(fwspec->fwnode)) {
> > +		if (fwspec->param_count < 3)
> > +			return -EINVAL;
> > +
> > +		if (fwspec->param[0] != GICV5_HWIRQ_TYPE_SPI)
> > +			return -EINVAL;
> > +
> > +		*hwirq = fwspec->param[1];
> > +		*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
> > +
> > +		return 0;
> > +	}
> 
> The only difference between this and the ppi variant is the type check
> of param[0]. Common helper perhaps?

Definitely.

Thanks a lot,
Lorenzo


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 13/24] arm64/sysreg: Add ICH_HFGWTR_EL2
  2025-04-08 10:50 ` [PATCH 13/24] arm64/sysreg: Add ICH_HFGWTR_EL2 Lorenzo Pieralisi
@ 2025-04-09  7:48   ` Arnd Bergmann
  2025-04-09  8:51     ` Lorenzo Pieralisi
  0 siblings, 1 reply; 62+ messages in thread
From: Arnd Bergmann @ 2025-04-09  7:48 UTC (permalink / raw)
  To: Lorenzo Pieralisi, Marc Zyngier, Thomas Gleixner, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree

On Tue, Apr 8, 2025, at 12:50, Lorenzo Pieralisi wrote:
> Add ICH_HFGWTR_EL2 register description to sysreg.
>
> Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
> Cc: Will Deacon <will@kernel.org>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Marc Zyngier <maz@kernel.org>

No comment on the contents, but I feel that having ten patches
adding new sysregs one group at a time is a bit worse than a
single patch adding them all. The patch descriptions don't add
any particular information about why these are distinct, and you
need them all anyway, so I'd suggest combining them into a
larger patch.

      Arnd


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 01/24] Documentation: devicetree: bindings: Add GICv5 DT bindings
  2025-04-08 15:07   ` Rob Herring
@ 2025-04-09  8:20     ` Lorenzo Pieralisi
  0 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-09  8:20 UTC (permalink / raw)
  To: Rob Herring
  Cc: Marc Zyngier, Thomas Gleixner, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Sascha Bischoff, Timothy Hayes,
	linux-arm-kernel, linux-kernel, devicetree

On Tue, Apr 08, 2025 at 10:07:06AM -0500, Rob Herring wrote:
> On Tue, Apr 8, 2025 at 5:50 AM Lorenzo Pieralisi <lpieralisi@kernel.org> wrote:
> >
> 
> No need to say DT bindings twice in the subject line. Follow the
> subsystem conventions.
> 
> dt-bindings: interrupt-controller: Add Arm GICv5

Will do.

> > The GICv5 interrupt controller architecture is composed of:
> >
> > - one or more Interrupt Routing Service (IRS)
> > - zero or more Interrupt Translation Service (ITS)
> > - zero or more Interrupt Wire Bridge (IWB)
> >
> > Describe a GICv5 implementation by specifying a top level node
> > corresponding to the GICv5 system component.
> >
> > IRS nodes are added as GICv5 system component children.
> >
> > An ITS is associated with an IRS so ITS nodes are described
> > as IRS children - use the hierarchy explicitly in the device
> > tree to define the association.
> >
> > IWB nodes are described as GICv5 system component children - to make it
> > explicit that are part of the GICv5 system component; an IWB is
> > connected to a single ITS but the connection is made explicit through
> > the msi-parent property and therefore is not required to be explicit
> > through a parent-child relationship in the device tree.
> >
> > Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
> > Cc: Conor Dooley <conor+dt@kernel.org>
> > Cc: Rob Herring <robh@kernel.org>
> > Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
> > Cc: Marc Zyngier <maz@kernel.org>
> > ---
> >  .../bindings/interrupt-controller/arm,gic-v5.yaml  | 268 +++++++++++++++++++++
> >  MAINTAINERS                                        |   7 +
> >  2 files changed, 275 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.yaml b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.yaml
> > new file mode 100644
> > index 0000000000000000000000000000000000000000..5c78375c298a0115c55872f439eb04d4171c4381
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.yaml
> > @@ -0,0 +1,268 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/interrupt-controller/arm,gic-v5.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: ARM Generic Interrupt Controller, version 5
> > +
> > +maintainers:
> > +  - Lorenzo Pieralisi <lpieralisi@kernel.org>
> > +  - Marc Zyngier <maz@kernel.org>
> > +
> > +description: |
> > +  The GICv5 architecture defines the guidelines to implement GICv5
> > +  compliant interrupt controllers for AArch64 systems.
> > +
> > +  The GICv5 specification can be found at
> > +  https://developer.arm.com/documentation/aes0070
> > +
> > +  The GICv5 architecture is composed of multiple components:
> > +    - one or more IRS (Interrupt Routing Service)
> > +    - zero or more ITS (Interrupt Translation Service)
> > +    - zero or more IWB (Interrupt Wire Bridge)
> > +
> > +  The architecture defines:
> > +    - PE-Private Peripheral Interrupts (PPI)
> > +    - Shared Peripheral Interrupts (SPI)
> > +    - Logical Peripheral Interrupts (LPI)
> > +
> > +allOf:
> > +  - $ref: /schemas/interrupt-controller.yaml#
> > +
> > +properties:
> > +  compatible:
> > +    const: arm,gic-v5
> > +
> > +  interrupt-controller: true
> > +
> > +  "#address-cells":
> > +    enum: [ 1, 2 ]
> 
> blank line
> 
> > +  "#size-cells":
> > +    enum: [ 1, 2 ]
> > +
> > +  ranges: true
> > +
> > +  "#interrupt-cells":
> > +    description: |
> > +      Specifies the number of cells needed to encode an interrupt source.
> > +      Must be a single cell with a value 3.
> 
> Drop this paragraph. The first sentence is just describing a common
> property. The 2nd is expressed as schema already.

Will do.

> > +
> > +      The 1st cell corresponds to the INTID.Type field in the INTID; 1 for PPI,
> > +      3 for SPI. LPI interrupts must not be described in the bindings since
> > +      they are allocated dynamically by the software component managing them.
> > +
> > +      The 2nd cell contains the interrupt INTID.ID field.
> > +
> > +      The 3rd cell is the flags, encoded as follows:
> > +      bits[3:0] trigger type and level flags.
> > +
> > +        1 = low-to-high edge triggered
> > +        2 = high-to-low edge triggered
> > +        4 = active high level-sensitive
> > +        8 = active low level-sensitive
> > +
> > +      Cells 4 and beyond are reserved for future use and must have a value
> > +      of 0 if present.
> 
> Drop. You can't have 4 or more cells because only 3 is allowed:

Nothing planned but what if this needs to be extended later ?

> > +    const: 3
> > +
> > +  interrupts:
> > +    description:
> > +      Interrupt source of the VGIC maintenance interrupt.
> 
> Drop "Interrupt source of ".
> 
> > +    maxItems: 1
> > +
> > +required:
> > +  - compatible
> > +
> > +patternProperties:
> > +  "^irs@[0-9a-f]+$":
> > +    type: object
> > +    description:
> > +      GICv5 has one or more Interrupt Routing Services (IRS) that are
> > +      responsible for handling IRQ state and routing.
> > +
> > +    additionalProperties: false
> 
> blank line
> 
> > +    properties:
> > +      compatible:
> > +        const: arm,gic-v5-irs
> > +
> > +      "#address-cells":
> > +        enum: [ 1, 2 ]
> 
> blank line
> 
> > +      "#size-cells":
> > +        enum: [ 1, 2 ]
> > +
> > +      ranges: true
> > +
> > +      dma-noncoherent:
> > +        description:
> > +          Present if the GIC IRS permits programming shareability and
> > +          cacheability attributes but is connected to a non-coherent
> > +          downstream interconnect.
> > +
> > +      reg:
> > +        minItems: 1
> > +        items:
> > +          - description: IRS control frame

On this, is there a way to specify that this has a fixed size ?

> > +          - description: IRS setlpi frame
> > +
> > +      cpus:
> > +        $ref: /schemas/types.yaml#/definitions/phandle-array
> 
> Already has a type, drop.
> 
> > +        description:
> > +          Should be a list of phandles to CPU nodes (as described in
> > +          Documentation/devicetree/bindings/arm/cpus.yaml) corresponding to
> > +          CPUs managed by the IRS.
> 
> The actual cpu binding is outside the scope of this binding. Just
> 'CPUs managed by the IRS.' is enough.
> 
> Is there a maximum number of CPUs?

Yes it is reported in the IRS_IDR1 configuration frame register IAFFID_BITS
field.

Should I add anything to the bindings related to this ?

> > +
> > +      arm,iaffids:
> > +        $ref: /schemas/types.yaml#/definitions/uint16-array
> > +        description:
> > +          Should be a list of u16 values representing IAFFID IDs associated
> 
> The type says it is 'a list of u16 values', so don't repeat that here.
> IAFFID needs to be defined somewhere. Is the 2nd 'ID' redundant?

I will add an IAFFID description (here ? or in the binding description
above ?)

> > +          with the CPU whose CPU node phandle is at the same index in the
> > +          cpus array.
> > +
> > +    patternProperties:
> > +      "^msi-controller@[0-9a-f]+$":
> > +        type: object
> > +        description:
> > +          GICv5 has zero or more Interrupt Translation Services (ITS) that are
> > +          used to route Message Signalled Interrupts (MSI) to the CPUs. Each
> > +          ITS is connected to an IRS.
> > +        additionalProperties: false
> 
> blank line
> 
> > +        properties:
> > +          compatible:
> > +            const: arm,gic-v5-its
> > +
> > +          dma-noncoherent:
> > +            description:
> > +              Present if the GIC ITS permits programming shareability and
> > +              cacheability attributes but is connected to a non-coherent
> > +              downstream interconnect.
> > +
> > +          msi-controller: true
> > +
> > +          "#msi-cells":
> > +            description:
> > +              The single msi-cell is the DeviceID of the device which will
> > +              generate the MSI.
> > +            const: 1
> > +
> > +          reg:
> > +            items:
> > +              - description: ITS control frame
> > +              - description: ITS translate frame
> > +
> > +        required:
> > +          - compatible
> > +          - msi-controller
> > +          - "#msi-cells"
> > +          - reg
> > +
> > +    required:
> > +      - compatible
> > +      - reg
> > +      - cpus
> > +      - arm,iaffids
> > +
> > +  "^interrupt-controller@[0-9a-f]+$":
> > +    type: object
> > +    description:
> > +      GICv5 has zero or more Interrupt Wire Bridges (IWB) that are responsible
> > +      for translating wire signals into interrupt messages to the ITS.
> 
> I wonder if this should be a separate schema and not a child of the
> GIC? Seems like these would be implemented standalone (even if the
> arch doesn't define that) at arbitrary addresses that aren't within
> the GIC's address range. To put it another way, there's nothing here
> it is getting from the parent node.

I could move it to a separate schema even though I can't help thinking
that, by being a GICv5 *only* component, it is clearer for it to live
in this schema, that was my thinking when I drafted the bindings.

I feel like moving it to a different schema could give the wrong
impression, namely that an IWB can be plugged to something else
than an ITS, which is not really possible.

> > +
> > +    additionalProperties: false
> > +    properties:
> > +      compatible:
> > +        const: arm,gic-v5-iwb
> > +
> > +      interrupt-controller: true
> > +
> > +      "#address-cells":
> > +        const: 0
> > +
> > +      "#interrupt-cells":
> > +        description: |
> > +          Specifies the number of cells needed to encode an interrupt source.
> > +          Must be a single cell with a value 2.
> 
> Drop
> 
> > +
> > +          The 1st cell corresponds to the IWB wire.
> > +
> > +          The 2nd cell is the flags, encoded as follows:
> > +          bits[3:0] trigger type and level flags.
> > +
> > +          1 = low-to-high edge triggered
> > +          2 = high-to-low edge triggered
> > +          4 = active high level-sensitive
> > +          8 = active low level-sensitive
> > +
> > +          Cells 3 and beyond are reserved for future use and must have a value
> > +          of 0 if present.
> 
> Drop
> 
> > +        const: 2
> > +
> > +      reg:
> > +        items:
> > +          - description: IWB control frame
> > +
> > +      msi-parent: true
> 
> maxItems: 1
> 
> Because the common definition allows any number of parents.

I will add it.

> > +
> > +    required:
> > +      - compatible
> > +      - reg
> > +      - msi-parent
> > +
> > +additionalProperties: false
> > +
> > +examples:
> > +  - |
> > +    interrupt-controller {
> > +      compatible = "arm,gic-v5";
> > +      #interrupt-cells = <3>;
> > +      #address-cells = <2>;
> > +      #size-cells = <2>;
> > +      ranges;
> > +
> > +      interrupt-controller;
> > +
> > +      interrupts = <1 25 4>;
> > +
> > +      irs@2f1a0000 {
> > +        compatible = "arm,gic-v5-irs";
> > +        #address-cells = <2>;
> > +        #size-cells = <2>;
> > +        ranges;
> > +
> > +        reg = <0x0 0x2f1a0000 0x0 0x10000>;  // IRS_CONFIG_FRAME for NS
> > +
> > +        arm,iaffids = /bits 16 <0 1 2 3 4 5 6 7>;

/bits/

> > +        cpus = <&{/cpus/cpu@0}>, <&{/cpus/cpu@100}>, <&{/cpus/cpu@200}>,
> > +               <&{/cpus/cpu@300}>, <&{/cpus/cpu@10000}>, <&{/cpus/cpu@10100}>,
> > +               <&{/cpus/cpu@10200}>, <&{/cpus/cpu@10300}>;
> 
> Use labels instead of paths.

Yep, noticed, fixed already.

> > +
> > +        msi-controller@2f120000 {
> > +          compatible = "arm,gic-v5-its";
> > +
> > +          msi-controller;
> > +          #msi-cells = <1>;
> > +
> > +          reg = <0x0 0x2f120000 0x0 0x10000    // ITS_CONFIG_FRAME for NS
> > +                 0x0 0x2f130000 0x0 0x10000>;  // ITS_TRANSLATE_FRAME
> > +        };
> > +      };
> > +
> > +      interrupt-controller@2f000000 {
> > +        compatible = "arm,gic-v5-iwb";
> > +        #address-cells = <0>;
> > +
> > +        interrupt-controller;
> > +        #interrupt-cells = <2>;
> > +
> > +        reg = <0x0 0x2f000000 0x0 0x10000>;
> > +
> > +        msi-parent = <&its0 64>;
> > +      };
> > +    };
> > +
> > +    device@0 {
> 
> Drop. We don't put consumers in provider examples and vice-versa.

I will drop it.

Thanks a lot Rob for reviewing it.

Lorenzo

> > +      reg = <0 4>;
> > +      interrupts = <3 115 4>;
> > +    };
> > +
> > +...


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 20/24] irqchip/gic-v5: Add GICv5 LPI/IPI support
  2025-04-08 10:50 ` [PATCH 20/24] irqchip/gic-v5: Add GICv5 LPI/IPI support Lorenzo Pieralisi
@ 2025-04-09  8:23   ` Arnd Bergmann
  2025-04-09 10:11     ` Lorenzo Pieralisi
  2025-04-09  8:27   ` Thomas Gleixner
  2025-04-11  9:26   ` Lorenzo Pieralisi
  2 siblings, 1 reply; 62+ messages in thread
From: Arnd Bergmann @ 2025-04-09  8:23 UTC (permalink / raw)
  To: Lorenzo Pieralisi, Marc Zyngier, Thomas Gleixner, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree

On Tue, Apr 8, 2025, at 12:50, Lorenzo Pieralisi wrote:
> @@ -22,12 +25,346 @@ static u32 irs_readl(struct gicv5_irs_chip_data 
> *irs_data, const u64 reg_offset)
>  	return readl_relaxed(irs_data->irs_base + reg_offset);
>  }
> 
> +static u64 irs_readq(struct gicv5_irs_chip_data *irs_data, const u64 
> reg_offset)
> +{
> +	return readq_relaxed(irs_data->irs_base + reg_offset);
> +}
> +
>  static void irs_writel(struct gicv5_irs_chip_data *irs_data, const u32 
> val,
>  		       const u64 reg_offset)
>  {
>  	writel_relaxed(val, irs_data->irs_base + reg_offset);
>  }
> 
> +static void irs_writeq(struct gicv5_irs_chip_data *irs_data, const u64 
> val,
> +		       const u64 reg_offset)
> +{
> +	writeq_relaxed(val, irs_data->irs_base + reg_offset);
> +}

I think the use of _relaxed memory accessors needs some code
comments here. The definition of these is that you don't care
about ordering relative to DMA master accesses, yet you seem to
very much have accesses to the 'ist' from the GIC, as well as
DMA accesses from an MSI device, and I would expect both to
require ordering.

> +/* Wait for completion of an IST change */
> +static int gicv5_irs_ist_wait_for_idle(struct gicv5_irs_chip_data 
> *irs_data)
> +{
> +	int ret;
> +	u32 val;
> +
> +	ret = readl_relaxed_poll_timeout_atomic(
> +			irs_data->irs_base + GICV5_IRS_IST_STATUSR, val,
> +			FIELD_GET(GICV5_IRS_IST_STATUSR_IDLE, val), 1,
> +			USEC_PER_SEC);
> +

What is the significance of the 1 second timeout? This is probably
a million times longer than I would expect any hardware interaction
to be specified to take. Are you waiting for another thread here?

> +	l2istsz = BIT(n + 1);
> +	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;
> +	}

The use of KMALLOC_MAX_SIZE seem arbitrary here. I remember discussing
this in the past and concluding that this is fine for all cases
that may be relevant, but it would be good to explain the reasoning
in a comment.

> +	if (irs_data->flags & IRS_FLAGS_NON_COHERENT)
> +		dcache_clean_inval_poc((unsigned long)ist,
> +				       (unsigned long)ist + l2istsz);
> +	else
> +		dsb(ishst);
...
> +	baser = (virt_to_phys(ist) & GICV5_IRS_IST_BASER_ADDR_MASK) |
> +		FIELD_PREP(GICV5_IRS_IST_BASER_VALID, 0x1);

Here it seems like you are open-coding the DMA mapping interface
details, in particular the mapping of the 'ist' memory area into
the gic's DMA master space, the coherency and the barrier that is
normally part of a (non-relaxed) writeq().  Is there a reason
you can't use the normal interfaces here, using dma_alloc_coherent()
or dma_alloc_noncoherent()?

Do you expect actual implementation to not be cache-coherent?

        Arnd


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 20/24] irqchip/gic-v5: Add GICv5 LPI/IPI support
  2025-04-08 10:50 ` [PATCH 20/24] irqchip/gic-v5: Add GICv5 LPI/IPI support Lorenzo Pieralisi
  2025-04-09  8:23   ` Arnd Bergmann
@ 2025-04-09  8:27   ` Thomas Gleixner
  2025-04-09 10:30     ` Lorenzo Pieralisi
  2025-04-11  9:26   ` Lorenzo Pieralisi
  2 siblings, 1 reply; 62+ messages in thread
From: Thomas Gleixner @ 2025-04-09  8:27 UTC (permalink / raw)
  To: Lorenzo Pieralisi, Marc Zyngier, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

On Tue, Apr 08 2025 at 12:50, Lorenzo Pieralisi wrote:
> +/* Wait for completion of an IST change */
> +static int gicv5_irs_ist_wait_for_idle(struct gicv5_irs_chip_data *irs_data)
> +{
> +	int ret;
> +	u32 val;
> +
> +	ret = readl_relaxed_poll_timeout_atomic(
> +			irs_data->irs_base + GICV5_IRS_IST_STATUSR, val,
> +			FIELD_GET(GICV5_IRS_IST_STATUSR_IDLE, val), 1,
> +			USEC_PER_SEC);
> +
> +	if (ret == -ETIMEDOUT)
> +		pr_err_ratelimited("IST_STATUSR.IDLE timeout...\n");
> +
> +	return ret;

I'm sure I've seen that code before and without looking I'm sure the
diff between the two functions is ~2 lines.

> +
> +	mtree_lock(&lpi_mt);
> +	ret = mas_empty_area(&mas, 0, num_lpis - 1, lpis);
> +	if (ret) {
> +		pr_err("Failed to perform a dynamic alloc in the LPI MT!\n");
> +		return ret;
> +	}
> +
> +	lpi_base = mas.index;
> +
> +	/*
> +	 * We don't really care about the entry itself, only about
> +	 * allocation of a maple tree ranges describing in use LPIs.
> +	 * That's why, upon allocation, we try to merge slots adjacent
> +	 * with the empty one we are allocating to minimize the number
> +	 * of slots we take from maple tree nodes for nothing, all
> +	 * we need to keep track of is in use ranges.
> +	 */

I'm really not convinced that you need a maple tree and the code
complexity for this. What's wrong with a simple bitmap other than that
it might need 1MB memory?

> +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 *base_lpi = arg;
> +	int i, ret;
> +
> +	hwirq = *base_lpi;
> +
> +	for (i = 0; i < nr_irqs; i++) {
> +		irqd = irq_desc_get_irq_data(irq_to_desc(virq + i));

irq_get_irq_data() and irq_domain_get_irq_data() exist for a reason.

> +
> +		irq_domain_set_info(domain, virq + i, hwirq + i,
> +				    &gicv5_lpi_irq_chip, NULL,
> +				    handle_fasteoi_irq, NULL, NULL);
> +		irqd_set_single_target(irqd);
> +static int gicv5_irq_ipi_domain_alloc(struct irq_domain *domain,
> +				      unsigned int virq, unsigned int nr_irqs,
> +				      void *arg)
> +{
> +	int ret, i;
> +	u32 lpi;
> +	struct irq_data *irqd = irq_desc_get_irq_data(irq_to_desc(virq));

Again. Zero reason to fiddle with irq_desc.

Thanks,

        tglx


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 13/24] arm64/sysreg: Add ICH_HFGWTR_EL2
  2025-04-09  7:48   ` Arnd Bergmann
@ 2025-04-09  8:51     ` Lorenzo Pieralisi
  0 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-09  8:51 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon, Sascha Bischoff,
	Timothy Hayes, linux-arm-kernel, linux-kernel, devicetree

On Wed, Apr 09, 2025 at 09:48:49AM +0200, Arnd Bergmann wrote:
> On Tue, Apr 8, 2025, at 12:50, Lorenzo Pieralisi wrote:
> > Add ICH_HFGWTR_EL2 register description to sysreg.
> >
> > Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
> > Cc: Will Deacon <will@kernel.org>
> > Cc: Catalin Marinas <catalin.marinas@arm.com>
> > Cc: Marc Zyngier <maz@kernel.org>
> 
> No comment on the contents, but I feel that having ten patches
> adding new sysregs one group at a time is a bit worse than a
> single patch adding them all. The patch descriptions don't add
> any particular information about why these are distinct, and you
> need them all anyway, so I'd suggest combining them into a
> larger patch.

I posted them separately to simplify "reviewing" their content,
I will do whatever is preferred.

Thanks,
Lorenzo


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 20/24] irqchip/gic-v5: Add GICv5 LPI/IPI support
  2025-04-09  8:23   ` Arnd Bergmann
@ 2025-04-09 10:11     ` Lorenzo Pieralisi
  2025-04-09 10:56       ` Arnd Bergmann
  0 siblings, 1 reply; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-09 10:11 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon, Sascha Bischoff,
	Timothy Hayes, linux-arm-kernel, linux-kernel, devicetree

On Wed, Apr 09, 2025 at 10:23:57AM +0200, Arnd Bergmann wrote:
> On Tue, Apr 8, 2025, at 12:50, Lorenzo Pieralisi wrote:
> > @@ -22,12 +25,346 @@ static u32 irs_readl(struct gicv5_irs_chip_data 
> > *irs_data, const u64 reg_offset)
> >  	return readl_relaxed(irs_data->irs_base + reg_offset);
> >  }
> > 
> > +static u64 irs_readq(struct gicv5_irs_chip_data *irs_data, const u64 
> > reg_offset)
> > +{
> > +	return readq_relaxed(irs_data->irs_base + reg_offset);
> > +}
> > +
> >  static void irs_writel(struct gicv5_irs_chip_data *irs_data, const u32 
> > val,
> >  		       const u64 reg_offset)
> >  {
> >  	writel_relaxed(val, irs_data->irs_base + reg_offset);
> >  }
> > 
> > +static void irs_writeq(struct gicv5_irs_chip_data *irs_data, const u64 
> > val,
> > +		       const u64 reg_offset)
> > +{
> > +	writeq_relaxed(val, irs_data->irs_base + reg_offset);
> > +}
> 
> I think the use of _relaxed memory accessors needs some code
> comments here. The definition of these is that you don't care
> about ordering relative to DMA master accesses, yet you seem to
> very much have accesses to the 'ist' from the GIC, as well as
> DMA accesses from an MSI device, and I would expect both to
> require ordering.

For the 1-level (linear) IST we allocate it in one go, write
the base address through relaxed access (that sets the IST
valid) and poll completion with a relaxed access. Memory is
cleaned and invalidated from the cache (if the IRS is not
coherent) before the MMIO sequence above, which implies a
dsb().

After that memory is handed over to the GIC.

For a 2-level IST, the code that updates L1 entries already add
a dma_rmb() barrier (ie gicv5_irs_iste_alloc()) to make sure we
order MMIO wait completion with the subsequent cache invalidate
(again, in the yet hypothetical case where the IRS is not coherent).

I think I can add comments where the sequence to initialize the
tables is executed more than here, given that these helpers are
used for other purposes too.

> > +/* Wait for completion of an IST change */
> > +static int gicv5_irs_ist_wait_for_idle(struct gicv5_irs_chip_data 
> > *irs_data)
> > +{
> > +	int ret;
> > +	u32 val;
> > +
> > +	ret = readl_relaxed_poll_timeout_atomic(
> > +			irs_data->irs_base + GICV5_IRS_IST_STATUSR, val,
> > +			FIELD_GET(GICV5_IRS_IST_STATUSR_IDLE, val), 1,
> > +			USEC_PER_SEC);
> > +
> 
> What is the significance of the 1 second timeout? This is probably
> a million times longer than I would expect any hardware interaction
> to be specified to take. Are you waiting for another thread here?

It is arbitrary, agreed.

> > +	l2istsz = BIT(n + 1);
> > +	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;
> > +	}
> 
> The use of KMALLOC_MAX_SIZE seem arbitrary here. I remember discussing
> this in the past and concluding that this is fine for all cases
> that may be relevant, but it would be good to explain the reasoning
> in a comment.

We need contiguous physical memory that can be < PAGE_SIZE or larger.

For allocations larger than the allocator caches kmalloc hands over to
the page allocator, MAX_ORDER is reflected into KMALLOC_MAX_SIZE AFAIU.

That's the reasoning. Does it make sense ?

> > +	if (irs_data->flags & IRS_FLAGS_NON_COHERENT)
> > +		dcache_clean_inval_poc((unsigned long)ist,
> > +				       (unsigned long)ist + l2istsz);
> > +	else
> > +		dsb(ishst);
> ...
> > +	baser = (virt_to_phys(ist) & GICV5_IRS_IST_BASER_ADDR_MASK) |
> > +		FIELD_PREP(GICV5_IRS_IST_BASER_VALID, 0x1);
> 
> Here it seems like you are open-coding the DMA mapping interface
> details, in particular the mapping of the 'ist' memory area into
> the gic's DMA master space, the coherency and the barrier that is
> normally part of a (non-relaxed) writeq().  Is there a reason
> you can't use the normal interfaces here, using dma_alloc_coherent()
> or dma_alloc_noncoherent()?

The GIC IRS must be brought up early, it is not a struct device.

> Do you expect actual implementation to not be cache-coherent?

It is allowed by the architecture - I don't have a crystal ball
but if I want to add support for a non-coherent IRS the DMA mapping
like sequence above has to be there - alternatives are welcome.

Thanks a lot for reviewing.

Lorenzo


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 20/24] irqchip/gic-v5: Add GICv5 LPI/IPI support
  2025-04-09  8:27   ` Thomas Gleixner
@ 2025-04-09 10:30     ` Lorenzo Pieralisi
  0 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-09 10:30 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Sascha Bischoff, Timothy Hayes,
	linux-arm-kernel, linux-kernel, devicetree

On Wed, Apr 09, 2025 at 10:27:24AM +0200, Thomas Gleixner wrote:
> On Tue, Apr 08 2025 at 12:50, Lorenzo Pieralisi wrote:
> > +/* Wait for completion of an IST change */
> > +static int gicv5_irs_ist_wait_for_idle(struct gicv5_irs_chip_data *irs_data)
> > +{
> > +	int ret;
> > +	u32 val;
> > +
> > +	ret = readl_relaxed_poll_timeout_atomic(
> > +			irs_data->irs_base + GICV5_IRS_IST_STATUSR, val,
> > +			FIELD_GET(GICV5_IRS_IST_STATUSR_IDLE, val), 1,
> > +			USEC_PER_SEC);
> > +
> > +	if (ret == -ETIMEDOUT)
> > +		pr_err_ratelimited("IST_STATUSR.IDLE timeout...\n");
> > +
> > +	return ret;
> 
> I'm sure I've seen that code before and without looking I'm sure the
> diff between the two functions is ~2 lines.

Yep, you have a point, will do my best to consolidate them.

> > +
> > +	mtree_lock(&lpi_mt);
> > +	ret = mas_empty_area(&mas, 0, num_lpis - 1, lpis);
> > +	if (ret) {
> > +		pr_err("Failed to perform a dynamic alloc in the LPI MT!\n");
> > +		return ret;
> > +	}
> > +
> > +	lpi_base = mas.index;
> > +
> > +	/*
> > +	 * We don't really care about the entry itself, only about
> > +	 * allocation of a maple tree ranges describing in use LPIs.
> > +	 * That's why, upon allocation, we try to merge slots adjacent
> > +	 * with the empty one we are allocating to minimize the number
> > +	 * of slots we take from maple tree nodes for nothing, all
> > +	 * we need to keep track of is in use ranges.
> > +	 */
> 
> I'm really not convinced that you need a maple tree and the code
> complexity for this. What's wrong with a simple bitmap other than that
> it might need 1MB memory?

Yes, I wrote it in the cover letter (again, easy to miss), I have
to admit I am not convinced either. We need 24 bits max, so a 2MB
chunk of memory worst case but other than that I can't really argue
to be honest.

I also thought about re-using the GICv3 ITS LPI allocator (which
does the same thing - agree, it is an on-purpose one, so maybe
it is better to reuse a core code construct).

Marc obviously has more background knowledge on this - I am
open to reworking this LPI allocator and remove the MT.

> > +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 *base_lpi = arg;
> > +	int i, ret;
> > +
> > +	hwirq = *base_lpi;
> > +
> > +	for (i = 0; i < nr_irqs; i++) {
> > +		irqd = irq_desc_get_irq_data(irq_to_desc(virq + i));
> 
> irq_get_irq_data() and irq_domain_get_irq_data() exist for a reason.

Point taken.

> > +
> > +		irq_domain_set_info(domain, virq + i, hwirq + i,
> > +				    &gicv5_lpi_irq_chip, NULL,
> > +				    handle_fasteoi_irq, NULL, NULL);
> > +		irqd_set_single_target(irqd);
> > +static int gicv5_irq_ipi_domain_alloc(struct irq_domain *domain,
> > +				      unsigned int virq, unsigned int nr_irqs,
> > +				      void *arg)
> > +{
> > +	int ret, i;
> > +	u32 lpi;
> > +	struct irq_data *irqd = irq_desc_get_irq_data(irq_to_desc(virq));
> 
> Again. Zero reason to fiddle with irq_desc.

I will update it.

Thanks Thomas,
Lorenzo


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 20/24] irqchip/gic-v5: Add GICv5 LPI/IPI support
  2025-04-09 10:11     ` Lorenzo Pieralisi
@ 2025-04-09 10:56       ` Arnd Bergmann
  2025-04-09 13:15         ` Lorenzo Pieralisi
  2025-04-18  9:21         ` Lorenzo Pieralisi
  0 siblings, 2 replies; 62+ messages in thread
From: Arnd Bergmann @ 2025-04-09 10:56 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon, Sascha Bischoff,
	Timothy Hayes, linux-arm-kernel, linux-kernel, devicetree

On Wed, Apr 9, 2025, at 12:11, Lorenzo Pieralisi wrote:
> On Wed, Apr 09, 2025 at 10:23:57AM +0200, Arnd Bergmann wrote:
>> On Tue, Apr 8, 2025, at 12:50, Lorenzo Pieralisi wrote:
>> > +static void irs_writeq(struct gicv5_irs_chip_data *irs_data, const u64 
>> > val,
>> > +		       const u64 reg_offset)
>> > +{
>> > +	writeq_relaxed(val, irs_data->irs_base + reg_offset);
>> > +}
>> 
>> I think the use of _relaxed memory accessors needs some code
>> comments here. The definition of these is that you don't care
>> about ordering relative to DMA master accesses, yet you seem to
>> very much have accesses to the 'ist' from the GIC, as well as
>> DMA accesses from an MSI device, and I would expect both to
>> require ordering.
>
> For the 1-level (linear) IST we allocate it in one go, write
> the base address through relaxed access (that sets the IST
> valid) and poll completion with a relaxed access. Memory is
> cleaned and invalidated from the cache (if the IRS is not
> coherent) before the MMIO sequence above, which implies a
> dsb().
>
> After that memory is handed over to the GIC.
>
> For a 2-level IST, the code that updates L1 entries already add
> a dma_rmb() barrier (ie gicv5_irs_iste_alloc()) to make sure we
> order MMIO wait completion with the subsequent cache invalidate
> (again, in the yet hypothetical case where the IRS is not coherent).
>
> I think I can add comments where the sequence to initialize the
> tables is executed more than here, given that these helpers are
> used for other purposes too.

Usually my recommendation is to have abstractions like this
provide both relaxed and normal variants, and then only
use the relaxed ones where it really matters for performance.

That way you can keep relatively short explanations where
you call irs_writeq_relaxed() and use irs_writeq() without
any code comments any place that doesn't care about saving
a few cycles per call.

>> > +/* Wait for completion of an IST change */
>> > +static int gicv5_irs_ist_wait_for_idle(struct gicv5_irs_chip_data 
>> > *irs_data)
>> > +{
>> > +	int ret;
>> > +	u32 val;
>> > +
>> > +	ret = readl_relaxed_poll_timeout_atomic(
>> > +			irs_data->irs_base + GICV5_IRS_IST_STATUSR, val,
>> > +			FIELD_GET(GICV5_IRS_IST_STATUSR_IDLE, val), 1,
>> > +			USEC_PER_SEC);
>> > +
>> 
>> What is the significance of the 1 second timeout? This is probably
>> a million times longer than I would expect any hardware interaction
>> to be specified to take. Are you waiting for another thread here?
>
> It is arbitrary, agreed.

Can you make either much shorter, or non-atomic then?

>> > +	l2istsz = BIT(n + 1);
>> > +	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;
>> > +	}
>> 
>> The use of KMALLOC_MAX_SIZE seem arbitrary here. I remember discussing
>> this in the past and concluding that this is fine for all cases
>> that may be relevant, but it would be good to explain the reasoning
>> in a comment.
>
> We need contiguous physical memory that can be < PAGE_SIZE or larger.
>
> For allocations larger than the allocator caches kmalloc hands over to
> the page allocator, MAX_ORDER is reflected into KMALLOC_MAX_SIZE AFAIU.
>
> That's the reasoning. Does it make sense ?

I'm more worried about what happens when KMALLOC_MAX_SIZE is
really small -- did you show that the allocation is still
going to work, or is this likely to cause runtime problems?

>> > +	if (irs_data->flags & IRS_FLAGS_NON_COHERENT)
>> > +		dcache_clean_inval_poc((unsigned long)ist,
>> > +				       (unsigned long)ist + l2istsz);
>> > +	else
>> > +		dsb(ishst);
>> ...
>> > +	baser = (virt_to_phys(ist) & GICV5_IRS_IST_BASER_ADDR_MASK) |
>> > +		FIELD_PREP(GICV5_IRS_IST_BASER_VALID, 0x1);
>> 
>> Here it seems like you are open-coding the DMA mapping interface
>> details, in particular the mapping of the 'ist' memory area into
>> the gic's DMA master space, the coherency and the barrier that is
>> normally part of a (non-relaxed) writeq().  Is there a reason
>> you can't use the normal interfaces here, using dma_alloc_coherent()
>> or dma_alloc_noncoherent()?
>
> The GIC IRS must be brought up early, it is not a struct device.

Right, that is rather unfortunate.

>> Do you expect actual implementation to not be cache-coherent?
>
> It is allowed by the architecture - I don't have a crystal ball
> but if I want to add support for a non-coherent IRS the DMA mapping
> like sequence above has to be there - alternatives are welcome.

I see that we have a few GICv3 implementations that are marked
as non-coherent in DT. I don't understand why they'd do that,
but I guess there is not much to be done about it.

The only other idea I have would be to use an uncached allocation
for the non-coherent case, the same way that dma_alloc_coherent()
or maybe dma_alloc_wc() does. This still has the same problem
with bypassing the dma-mapping.h interface because of the lack
of a device pointer, but it would at least avoid the cache flushes
at runtime. If I read this code right, the data in here is only
written by the CPU and read by the GIC, so a WC buffer wouldn't
be more expensive, right?

       Arnd


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 22/24] irqchip/gic-v5: Add GICv5 ITS support
  2025-04-08 10:50 ` [PATCH 22/24] irqchip/gic-v5: Add GICv5 ITS support Lorenzo Pieralisi
@ 2025-04-09 11:13   ` Thomas Gleixner
  2025-04-09 13:37     ` Lorenzo Pieralisi
  2025-04-09 18:57   ` Thomas Gleixner
  1 sibling, 1 reply; 62+ messages in thread
From: Thomas Gleixner @ 2025-04-09 11:13 UTC (permalink / raw)
  To: Lorenzo Pieralisi, Marc Zyngier, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

On Tue, Apr 08 2025 at 12:50, Lorenzo Pieralisi wrote:
>  
> +void gicv5_irs_syncr(void)
> +{
> +	u32 syncr;
> +	u32 statusr;
> +	int ret;
> +	struct gicv5_irs_chip_data *irs_data;
> +
> +	irs_data = list_first_entry_or_null(&irs_nodes,
> +					    struct gicv5_irs_chip_data, entry);
> +	if (WARN_ON(!irs_data))
> +		return;
> +
> +	syncr = FIELD_PREP(GICV5_IRS_SYNCR_SYNC, 1);
> +	irs_writel(irs_data, syncr, GICV5_IRS_SYNCR);
> +
> +	ret = readl_relaxed_poll_timeout_atomic(
> +			irs_data->irs_base + GICV5_IRS_SYNC_STATUSR, statusr,
> +			FIELD_GET(GICV5_IRS_SYNC_STATUSR_IDLE, statusr), 1,
> +			USEC_PER_SEC);
> +
> +	if (ret == -ETIMEDOUT)
> +		pr_err_ratelimited("SYNCR timeout...\n");

This timeout poll thing looks very familiar by now. Third variant :)

> +static int gicv5_its_wait_for_invalidation(struct gicv5_its_chip_data *its)
> +{
> +	int ret;
> +	u32 statusr;
> +
> +	ret = readl_relaxed_poll_timeout_atomic(
> +			its->its_base + GICV5_ITS_STATUSR, statusr,
> +			FIELD_GET(GICV5_ITS_STATUSR_IDLE, statusr), 1,
> +			USEC_PER_SEC);
> +
> +	if (ret == -ETIMEDOUT)
> +		pr_err_ratelimited("STATUSR timeout...\n");
> +
> +	return ret;
> +}

And number four follows suit :)

> +
> +static void gicv5_its_syncr(struct gicv5_its_chip_data *its,
> +			    struct gicv5_its_dev *its_dev)
> +{
> +	int ret;
> +	u64 syncr;
> +	u32 statusr;
> +
> +	syncr = FIELD_PREP(GICV5_ITS_SYNCR_SYNC, 1) |
> +		FIELD_PREP(GICV5_ITS_SYNCR_DEVICEID, its_dev->device_id);
> +
> +	its_writeq(its, syncr, GICV5_ITS_SYNCR);
> +
> +	ret = readl_relaxed_poll_timeout_atomic(
> +			its->its_base + GICV5_ITS_SYNC_STATUSR, statusr,
> +			FIELD_GET(GICV5_ITS_SYNC_STATUSR_IDLE, statusr), 1,
> +			USEC_PER_SEC);
> +
> +	if (ret == -ETIMEDOUT)
> +		pr_err_ratelimited("SYNCR timeout...\n");
> +}

Along with #5 

Thanks,

        tglx


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 20/24] irqchip/gic-v5: Add GICv5 LPI/IPI support
  2025-04-09 10:56       ` Arnd Bergmann
@ 2025-04-09 13:15         ` Lorenzo Pieralisi
  2025-04-09 14:25           ` Arnd Bergmann
  2025-04-18  9:21         ` Lorenzo Pieralisi
  1 sibling, 1 reply; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-09 13:15 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon, Sascha Bischoff,
	Timothy Hayes, linux-arm-kernel, linux-kernel, devicetree

On Wed, Apr 09, 2025 at 12:56:52PM +0200, Arnd Bergmann wrote:
> On Wed, Apr 9, 2025, at 12:11, Lorenzo Pieralisi wrote:
> > On Wed, Apr 09, 2025 at 10:23:57AM +0200, Arnd Bergmann wrote:
> >> On Tue, Apr 8, 2025, at 12:50, Lorenzo Pieralisi wrote:
> >> > +static void irs_writeq(struct gicv5_irs_chip_data *irs_data, const u64 
> >> > val,
> >> > +		       const u64 reg_offset)
> >> > +{
> >> > +	writeq_relaxed(val, irs_data->irs_base + reg_offset);
> >> > +}
> >> 
> >> I think the use of _relaxed memory accessors needs some code
> >> comments here. The definition of these is that you don't care
> >> about ordering relative to DMA master accesses, yet you seem to
> >> very much have accesses to the 'ist' from the GIC, as well as
> >> DMA accesses from an MSI device, and I would expect both to
> >> require ordering.
> >
> > For the 1-level (linear) IST we allocate it in one go, write
> > the base address through relaxed access (that sets the IST
> > valid) and poll completion with a relaxed access. Memory is
> > cleaned and invalidated from the cache (if the IRS is not
> > coherent) before the MMIO sequence above, which implies a
> > dsb().
> >
> > After that memory is handed over to the GIC.
> >
> > For a 2-level IST, the code that updates L1 entries already add
> > a dma_rmb() barrier (ie gicv5_irs_iste_alloc()) to make sure we
> > order MMIO wait completion with the subsequent cache invalidate
> > (again, in the yet hypothetical case where the IRS is not coherent).
> >
> > I think I can add comments where the sequence to initialize the
> > tables is executed more than here, given that these helpers are
> > used for other purposes too.
> 
> Usually my recommendation is to have abstractions like this
> provide both relaxed and normal variants, and then only
> use the relaxed ones where it really matters for performance.
> 
> That way you can keep relatively short explanations where
> you call irs_writeq_relaxed() and use irs_writeq() without
> any code comments any place that doesn't care about saving
> a few cycles per call.

I will review and update accordingly.

> >> > +/* Wait for completion of an IST change */
> >> > +static int gicv5_irs_ist_wait_for_idle(struct gicv5_irs_chip_data 
> >> > *irs_data)
> >> > +{
> >> > +	int ret;
> >> > +	u32 val;
> >> > +
> >> > +	ret = readl_relaxed_poll_timeout_atomic(
> >> > +			irs_data->irs_base + GICV5_IRS_IST_STATUSR, val,
> >> > +			FIELD_GET(GICV5_IRS_IST_STATUSR_IDLE, val), 1,
> >> > +			USEC_PER_SEC);
> >> > +
> >> 
> >> What is the significance of the 1 second timeout? This is probably
> >> a million times longer than I would expect any hardware interaction
> >> to be specified to take. Are you waiting for another thread here?
> >
> > It is arbitrary, agreed.
> 
> Can you make either much shorter, or non-atomic then?

Yes sure (and try to consolidate them as Thomas correctly pointed out).

> >> > +	l2istsz = BIT(n + 1);
> >> > +	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;
> >> > +	}
> >> 
> >> The use of KMALLOC_MAX_SIZE seem arbitrary here. I remember discussing
> >> this in the past and concluding that this is fine for all cases
> >> that may be relevant, but it would be good to explain the reasoning
> >> in a comment.
> >
> > We need contiguous physical memory that can be < PAGE_SIZE or larger.
> >
> > For allocations larger than the allocator caches kmalloc hands over to
> > the page allocator, MAX_ORDER is reflected into KMALLOC_MAX_SIZE AFAIU.
> >
> > That's the reasoning. Does it make sense ?
> 
> I'm more worried about what happens when KMALLOC_MAX_SIZE is
> really small -- did you show that the allocation is still
> going to work, or is this likely to cause runtime problems?

Are you referring to KMALLOC_MAX_CACHE_SIZE or KMALLOC_MAX_SIZE ?

KMALLOC_MAX_SIZE is set according to MAX_PAGE_ORDER, that should
be fine for most set-ups (well, obviously implementations that
only support a 1-level IST can't expect a very large number of
IRQs -  we set that to 12 bits worth of IDs deliberately but
given the current memory allocation limits it can be much higher).

A 2-level IST can easily manage 24-bits worth of IDs split into
two-level tables with the current kmalloc() limits.

For the ITS DT and ITT the same reasoning goes, so the capping
is the (rare) exception not the rule and I don't expect this to be a
problem at all or I am missing something.

> >> > +	if (irs_data->flags & IRS_FLAGS_NON_COHERENT)
> >> > +		dcache_clean_inval_poc((unsigned long)ist,
> >> > +				       (unsigned long)ist + l2istsz);
> >> > +	else
> >> > +		dsb(ishst);
> >> ...
> >> > +	baser = (virt_to_phys(ist) & GICV5_IRS_IST_BASER_ADDR_MASK) |
> >> > +		FIELD_PREP(GICV5_IRS_IST_BASER_VALID, 0x1);
> >> 
> >> Here it seems like you are open-coding the DMA mapping interface
> >> details, in particular the mapping of the 'ist' memory area into
> >> the gic's DMA master space, the coherency and the barrier that is
> >> normally part of a (non-relaxed) writeq().  Is there a reason
> >> you can't use the normal interfaces here, using dma_alloc_coherent()
> >> or dma_alloc_noncoherent()?
> >
> > The GIC IRS must be brought up early, it is not a struct device.
> 
> Right, that is rather unfortunate.
> 
> >> Do you expect actual implementation to not be cache-coherent?
> >
> > It is allowed by the architecture - I don't have a crystal ball
> > but if I want to add support for a non-coherent IRS the DMA mapping
> > like sequence above has to be there - alternatives are welcome.
> 
> I see that we have a few GICv3 implementations that are marked
> as non-coherent in DT. I don't understand why they'd do that,
> but I guess there is not much to be done about it.

You don't understand why the GIC HW is not coherent or why we set it
up as such in the driver ?

> The only other idea I have would be to use an uncached allocation
> for the non-coherent case, the same way that dma_alloc_coherent()
> or maybe dma_alloc_wc() does. This still has the same problem
> with bypassing the dma-mapping.h interface because of the lack
> of a device pointer, but it would at least avoid the cache flushes
> at runtime. If I read this code right, the data in here is only
> written by the CPU and read by the GIC, so a WC buffer wouldn't
> be more expensive, right?

The IST is also written by the GIC, the CPU reads it (explicity, with a
memory read rather than through instructions) only if the table is two
level and we are allocating L2 entries on demand to check whether an
L2 entry is valid.

I am not sure the CMOs are that bad given that's what we do
for GICv3 already but it is worth looking into it.

Thanks,
Lorenzo


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 22/24] irqchip/gic-v5: Add GICv5 ITS support
  2025-04-09 11:13   ` Thomas Gleixner
@ 2025-04-09 13:37     ` Lorenzo Pieralisi
  0 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-09 13:37 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Sascha Bischoff, Timothy Hayes,
	linux-arm-kernel, linux-kernel, devicetree

On Wed, Apr 09, 2025 at 01:13:46PM +0200, Thomas Gleixner wrote:
> On Tue, Apr 08 2025 at 12:50, Lorenzo Pieralisi wrote:
> >  
> > +void gicv5_irs_syncr(void)
> > +{
> > +	u32 syncr;
> > +	u32 statusr;
> > +	int ret;
> > +	struct gicv5_irs_chip_data *irs_data;
> > +
> > +	irs_data = list_first_entry_or_null(&irs_nodes,
> > +					    struct gicv5_irs_chip_data, entry);
> > +	if (WARN_ON(!irs_data))
> > +		return;
> > +
> > +	syncr = FIELD_PREP(GICV5_IRS_SYNCR_SYNC, 1);
> > +	irs_writel(irs_data, syncr, GICV5_IRS_SYNCR);
> > +
> > +	ret = readl_relaxed_poll_timeout_atomic(
> > +			irs_data->irs_base + GICV5_IRS_SYNC_STATUSR, statusr,
> > +			FIELD_GET(GICV5_IRS_SYNC_STATUSR_IDLE, statusr), 1,
> > +			USEC_PER_SEC);
> > +
> > +	if (ret == -ETIMEDOUT)
> > +		pr_err_ratelimited("SYNCR timeout...\n");
> 
> This timeout poll thing looks very familiar by now. Third variant :)
> 
> > +static int gicv5_its_wait_for_invalidation(struct gicv5_its_chip_data *its)
> > +{
> > +	int ret;
> > +	u32 statusr;
> > +
> > +	ret = readl_relaxed_poll_timeout_atomic(
> > +			its->its_base + GICV5_ITS_STATUSR, statusr,
> > +			FIELD_GET(GICV5_ITS_STATUSR_IDLE, statusr), 1,
> > +			USEC_PER_SEC);
> > +
> > +	if (ret == -ETIMEDOUT)
> > +		pr_err_ratelimited("STATUSR timeout...\n");
> > +
> > +	return ret;
> > +}
> 
> And number four follows suit :)
> 
> > +
> > +static void gicv5_its_syncr(struct gicv5_its_chip_data *its,
> > +			    struct gicv5_its_dev *its_dev)
> > +{
> > +	int ret;
> > +	u64 syncr;
> > +	u32 statusr;
> > +
> > +	syncr = FIELD_PREP(GICV5_ITS_SYNCR_SYNC, 1) |
> > +		FIELD_PREP(GICV5_ITS_SYNCR_DEVICEID, its_dev->device_id);
> > +
> > +	its_writeq(its, syncr, GICV5_ITS_SYNCR);
> > +
> > +	ret = readl_relaxed_poll_timeout_atomic(
> > +			its->its_base + GICV5_ITS_SYNC_STATUSR, statusr,
> > +			FIELD_GET(GICV5_ITS_SYNC_STATUSR_IDLE, statusr), 1,
> > +			USEC_PER_SEC);
> > +
> > +	if (ret == -ETIMEDOUT)
> > +		pr_err_ratelimited("SYNCR timeout...\n");
> > +}
> 
> Along with #5 

No question about it, I will consolidate them as much as I can, I
noticed while implementing them then focused on getting the driver
functionality in place and forgot to create a single function,
apologies.

Thanks,
Lorenzo


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 24/24] arm64: Kconfig: Enable GICv5
  2025-04-08 10:50 ` [PATCH 24/24] arm64: Kconfig: Enable GICv5 Lorenzo Pieralisi
@ 2025-04-09 13:44   ` kernel test robot
  2025-04-09 14:04     ` Lorenzo Pieralisi
  0 siblings, 1 reply; 62+ messages in thread
From: kernel test robot @ 2025-04-09 13:44 UTC (permalink / raw)
  To: Lorenzo Pieralisi, Marc Zyngier, Thomas Gleixner, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Catalin Marinas, Will Deacon
  Cc: llvm, oe-kbuild-all, Sascha Bischoff, Timothy Hayes,
	linux-arm-kernel, linux-kernel, devicetree, Lorenzo Pieralisi

Hi Lorenzo,

kernel test robot noticed the following build errors:

[auto build test ERROR on 0af2f6be1b4281385b618cb86ad946eded089ac8]

url:    https://github.com/intel-lab-lkp/linux/commits/Lorenzo-Pieralisi/Documentation-devicetree-bindings-Add-GICv5-DT-bindings/20250408-190630
base:   0af2f6be1b4281385b618cb86ad946eded089ac8
patch link:    https://lore.kernel.org/r/20250408-gicv5-host-v1-24-1f26db465f8d%40kernel.org
patch subject: [PATCH 24/24] arm64: Kconfig: Enable GICv5
config: arm64-randconfig-001-20250409 (https://download.01.org/0day-ci/archive/20250409/202504092127.YaPW3UWk-lkp@intel.com/config)
compiler: clang version 21.0.0git (https://github.com/llvm/llvm-project 92c93f5286b9ff33f27ff694d2dc33da1c07afdd)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250409/202504092127.YaPW3UWk-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202504092127.YaPW3UWk-lkp@intel.com/

All errors (new ones prefixed by >>):

>> drivers/irqchip/irq-gic-v5-iwb.c:298:3: error: cannot jump from this goto statement to its label
     298 |                 goto out_free;
         |                 ^
   drivers/irqchip/irq-gic-v5-iwb.c:300:2: note: jump bypasses initialization of variable with __attribute__((cleanup))
     300 |         guard(mutex)(&its->dev_alloc_lock);
         |         ^
   include/linux/cleanup.h:319:15: note: expanded from macro 'guard'
     319 |         CLASS(_name, __UNIQUE_ID(guard))
         |                      ^
   include/linux/compiler.h:166:29: note: expanded from macro '__UNIQUE_ID'
     166 | #define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
         |                             ^
   include/linux/compiler_types.h:84:22: note: expanded from macro '__PASTE'
      84 | #define __PASTE(a,b) ___PASTE(a,b)
         |                      ^
   include/linux/compiler_types.h:83:23: note: expanded from macro '___PASTE'
      83 | #define ___PASTE(a,b) a##b
         |                       ^
   <scratch space>:82:1: note: expanded from here
      82 | __UNIQUE_ID_guard576
         | ^
   drivers/irqchip/irq-gic-v5-iwb.c:288:3: error: cannot jump from this goto statement to its label
     288 |                 goto out_free;
         |                 ^
   drivers/irqchip/irq-gic-v5-iwb.c:300:2: note: jump bypasses initialization of variable with __attribute__((cleanup))
     300 |         guard(mutex)(&its->dev_alloc_lock);
         |         ^
   include/linux/cleanup.h:319:15: note: expanded from macro 'guard'
     319 |         CLASS(_name, __UNIQUE_ID(guard))
         |                      ^
   include/linux/compiler.h:166:29: note: expanded from macro '__UNIQUE_ID'
     166 | #define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
         |                             ^
   include/linux/compiler_types.h:84:22: note: expanded from macro '__PASTE'
      84 | #define __PASTE(a,b) ___PASTE(a,b)
         |                      ^
   include/linux/compiler_types.h:83:23: note: expanded from macro '___PASTE'
      83 | #define ___PASTE(a,b) a##b
         |                       ^
   <scratch space>:82:1: note: expanded from here
      82 | __UNIQUE_ID_guard576
         | ^
   2 errors generated.


vim +298 drivers/irqchip/irq-gic-v5-iwb.c

6b60a5125729caf Lorenzo Pieralisi 2025-04-08  247  
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  248  static struct gicv5_iwb_chip_data *
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  249  __init gicv5_iwb_init_bases(void __iomem *iwb_base,
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  250  			     struct fwnode_handle *handle,
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  251  			     struct irq_domain *parent_domain, u32 device_id)
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  252  {
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  253  	u32 nr_wires, idr0, cr0;
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  254  	struct gicv5_iwb_chip_data *iwb_node;
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  255  	struct msi_domain_info *msi_info;
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  256  	struct gicv5_its_chip_data *its;
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  257  	struct gicv5_its_dev *its_dev;
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  258  	int ret;
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  259  
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  260  	msi_info = msi_get_domain_info(parent_domain);
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  261  	its = msi_info->data;
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  262  	if (!its) {
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  263  		pr_warn("IWB %pOF can't find parent ITS, bailing\n",
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  264  			to_of_node(handle));
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  265  		return ERR_PTR(-ENODEV);
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  266  	}
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  267  
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  268  	iwb_node = kzalloc(sizeof(*iwb_node), GFP_KERNEL);
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  269  	if (!iwb_node)
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  270  		return ERR_PTR(-ENOMEM);
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  271  
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  272  	iwb_node->iwb_base = iwb_base;
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  273  	iwb_node->device_id = device_id;
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  274  
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  275  	idr0 = iwb_readl(iwb_node, GICV5_IWB_IDR0);
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  276  	nr_wires = (FIELD_GET(GICV5_IWB_IDR0_IW_RANGE, idr0) + 1) * 32;
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  277  
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  278  	iwb_node->domain = irq_domain_create_hierarchy(parent_domain, 0,
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  279  			   nr_wires, handle, &gicv5_iwb_irq_domain_ops,
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  280  			   iwb_node);
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  281  	irq_domain_update_bus_token(iwb_node->domain, DOMAIN_BUS_WIRED);
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  282  
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  283  	cr0 = iwb_readl(iwb_node, GICV5_IWB_CR0);
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  284  	if (!FIELD_GET(GICV5_IWB_CR0_IWBEN, cr0)) {
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  285  		pr_err("IWB %s must be enabled in firmware\n",
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  286  		       fwnode_get_name(handle));
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  287  		ret = -EINVAL;
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  288  		goto out_free;
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  289  	}
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  290  
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  291  	iwb_node->nr_regs = FIELD_GET(GICV5_IWB_IDR0_IW_RANGE, idr0) + 1;
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  292  
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  293  	for (unsigned int n = 0; n < iwb_node->nr_regs; n++)
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  294  		iwb_writel(iwb_node, 0, GICV5_IWB_WENABLER + (sizeof(u32) * n));
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  295  
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  296  	ret = gicv5_iwb_wait_for_wenabler(iwb_node);
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  297  	if (ret)
6b60a5125729caf Lorenzo Pieralisi 2025-04-08 @298  		goto out_free;
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  299  
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  300  	guard(mutex)(&its->dev_alloc_lock);
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  301  
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  302  	its_dev = gicv5_its_alloc_device(its, roundup_pow_of_two(nr_wires),
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  303  					 device_id, true);
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  304  	if (IS_ERR(its_dev)) {
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  305  		ret = -ENODEV;
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  306  		goto out_free;
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  307  	}
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  308  
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  309  	return iwb_node;
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  310  out_free:
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  311  	irq_domain_remove(iwb_node->domain);
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  312  	kfree(iwb_node);
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  313  
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  314  	return ERR_PTR(ret);
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  315  }
6b60a5125729caf Lorenzo Pieralisi 2025-04-08  316  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 24/24] arm64: Kconfig: Enable GICv5
  2025-04-09 13:44   ` kernel test robot
@ 2025-04-09 14:04     ` Lorenzo Pieralisi
  2025-04-09 14:07       ` Krzysztof Kozlowski
  0 siblings, 1 reply; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-09 14:04 UTC (permalink / raw)
  To: kernel test robot
  Cc: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon, llvm, oe-kbuild-all,
	Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree

On Wed, Apr 09, 2025 at 09:44:42PM +0800, kernel test robot wrote:
> Hi Lorenzo,
> 
> kernel test robot noticed the following build errors:
> 
> [auto build test ERROR on 0af2f6be1b4281385b618cb86ad946eded089ac8]
> 
> url:    https://github.com/intel-lab-lkp/linux/commits/Lorenzo-Pieralisi/Documentation-devicetree-bindings-Add-GICv5-DT-bindings/20250408-190630
> base:   0af2f6be1b4281385b618cb86ad946eded089ac8
> patch link:    https://lore.kernel.org/r/20250408-gicv5-host-v1-24-1f26db465f8d%40kernel.org
> patch subject: [PATCH 24/24] arm64: Kconfig: Enable GICv5
> config: arm64-randconfig-001-20250409 (https://download.01.org/0day-ci/archive/20250409/202504092127.YaPW3UWk-lkp@intel.com/config)
> compiler: clang version 21.0.0git (https://github.com/llvm/llvm-project 92c93f5286b9ff33f27ff694d2dc33da1c07afdd)
> reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250409/202504092127.YaPW3UWk-lkp@intel.com/reproduce)
> 
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Closes: https://lore.kernel.org/oe-kbuild-all/202504092127.YaPW3UWk-lkp@intel.com/
> 
> All errors (new ones prefixed by >>):
> 
> >> drivers/irqchip/irq-gic-v5-iwb.c:298:3: error: cannot jump from this goto statement to its label
>      298 |                 goto out_free;
>          |                 ^
>    drivers/irqchip/irq-gic-v5-iwb.c:300:2: note: jump bypasses initialization of variable with __attribute__((cleanup))
>      300 |         guard(mutex)(&its->dev_alloc_lock);

This is clearly wrong, will update code in the IWB (if we are keeping
the current IWB driver) and ITS by removing the guard where misused and
keeping the mutex_lock/unlock().

Lorenzo

>          |         ^
>    include/linux/cleanup.h:319:15: note: expanded from macro 'guard'
>      319 |         CLASS(_name, __UNIQUE_ID(guard))
>          |                      ^
>    include/linux/compiler.h:166:29: note: expanded from macro '__UNIQUE_ID'
>      166 | #define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
>          |                             ^
>    include/linux/compiler_types.h:84:22: note: expanded from macro '__PASTE'
>       84 | #define __PASTE(a,b) ___PASTE(a,b)
>          |                      ^
>    include/linux/compiler_types.h:83:23: note: expanded from macro '___PASTE'
>       83 | #define ___PASTE(a,b) a##b
>          |                       ^
>    <scratch space>:82:1: note: expanded from here
>       82 | __UNIQUE_ID_guard576
>          | ^
>    drivers/irqchip/irq-gic-v5-iwb.c:288:3: error: cannot jump from this goto statement to its label
>      288 |                 goto out_free;
>          |                 ^
>    drivers/irqchip/irq-gic-v5-iwb.c:300:2: note: jump bypasses initialization of variable with __attribute__((cleanup))
>      300 |         guard(mutex)(&its->dev_alloc_lock);
>          |         ^
>    include/linux/cleanup.h:319:15: note: expanded from macro 'guard'
>      319 |         CLASS(_name, __UNIQUE_ID(guard))
>          |                      ^
>    include/linux/compiler.h:166:29: note: expanded from macro '__UNIQUE_ID'
>      166 | #define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
>          |                             ^
>    include/linux/compiler_types.h:84:22: note: expanded from macro '__PASTE'
>       84 | #define __PASTE(a,b) ___PASTE(a,b)
>          |                      ^
>    include/linux/compiler_types.h:83:23: note: expanded from macro '___PASTE'
>       83 | #define ___PASTE(a,b) a##b
>          |                       ^
>    <scratch space>:82:1: note: expanded from here
>       82 | __UNIQUE_ID_guard576
>          | ^
>    2 errors generated.
> 
> 
> vim +298 drivers/irqchip/irq-gic-v5-iwb.c
> 
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  247  
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  248  static struct gicv5_iwb_chip_data *
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  249  __init gicv5_iwb_init_bases(void __iomem *iwb_base,
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  250  			     struct fwnode_handle *handle,
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  251  			     struct irq_domain *parent_domain, u32 device_id)
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  252  {
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  253  	u32 nr_wires, idr0, cr0;
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  254  	struct gicv5_iwb_chip_data *iwb_node;
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  255  	struct msi_domain_info *msi_info;
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  256  	struct gicv5_its_chip_data *its;
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  257  	struct gicv5_its_dev *its_dev;
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  258  	int ret;
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  259  
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  260  	msi_info = msi_get_domain_info(parent_domain);
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  261  	its = msi_info->data;
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  262  	if (!its) {
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  263  		pr_warn("IWB %pOF can't find parent ITS, bailing\n",
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  264  			to_of_node(handle));
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  265  		return ERR_PTR(-ENODEV);
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  266  	}
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  267  
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  268  	iwb_node = kzalloc(sizeof(*iwb_node), GFP_KERNEL);
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  269  	if (!iwb_node)
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  270  		return ERR_PTR(-ENOMEM);
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  271  
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  272  	iwb_node->iwb_base = iwb_base;
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  273  	iwb_node->device_id = device_id;
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  274  
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  275  	idr0 = iwb_readl(iwb_node, GICV5_IWB_IDR0);
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  276  	nr_wires = (FIELD_GET(GICV5_IWB_IDR0_IW_RANGE, idr0) + 1) * 32;
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  277  
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  278  	iwb_node->domain = irq_domain_create_hierarchy(parent_domain, 0,
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  279  			   nr_wires, handle, &gicv5_iwb_irq_domain_ops,
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  280  			   iwb_node);
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  281  	irq_domain_update_bus_token(iwb_node->domain, DOMAIN_BUS_WIRED);
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  282  
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  283  	cr0 = iwb_readl(iwb_node, GICV5_IWB_CR0);
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  284  	if (!FIELD_GET(GICV5_IWB_CR0_IWBEN, cr0)) {
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  285  		pr_err("IWB %s must be enabled in firmware\n",
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  286  		       fwnode_get_name(handle));
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  287  		ret = -EINVAL;
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  288  		goto out_free;
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  289  	}
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  290  
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  291  	iwb_node->nr_regs = FIELD_GET(GICV5_IWB_IDR0_IW_RANGE, idr0) + 1;
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  292  
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  293  	for (unsigned int n = 0; n < iwb_node->nr_regs; n++)
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  294  		iwb_writel(iwb_node, 0, GICV5_IWB_WENABLER + (sizeof(u32) * n));
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  295  
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  296  	ret = gicv5_iwb_wait_for_wenabler(iwb_node);
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  297  	if (ret)
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08 @298  		goto out_free;
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  299  
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  300  	guard(mutex)(&its->dev_alloc_lock);
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  301  
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  302  	its_dev = gicv5_its_alloc_device(its, roundup_pow_of_two(nr_wires),
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  303  					 device_id, true);
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  304  	if (IS_ERR(its_dev)) {
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  305  		ret = -ENODEV;
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  306  		goto out_free;
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  307  	}
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  308  
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  309  	return iwb_node;
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  310  out_free:
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  311  	irq_domain_remove(iwb_node->domain);
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  312  	kfree(iwb_node);
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  313  
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  314  	return ERR_PTR(ret);
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  315  }
> 6b60a5125729caf Lorenzo Pieralisi 2025-04-08  316  
> 
> -- 
> 0-DAY CI Kernel Test Service
> https://github.com/intel/lkp-tests/wiki


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 24/24] arm64: Kconfig: Enable GICv5
  2025-04-09 14:04     ` Lorenzo Pieralisi
@ 2025-04-09 14:07       ` Krzysztof Kozlowski
  0 siblings, 0 replies; 62+ messages in thread
From: Krzysztof Kozlowski @ 2025-04-09 14:07 UTC (permalink / raw)
  To: Lorenzo Pieralisi, kernel test robot
  Cc: Marc Zyngier, Thomas Gleixner, Rob Herring, Conor Dooley,
	Catalin Marinas, Will Deacon, llvm, oe-kbuild-all,
	Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree

On 09/04/2025 16:04, Lorenzo Pieralisi wrote:
> On Wed, Apr 09, 2025 at 09:44:42PM +0800, kernel test robot wrote:
>> Hi Lorenzo,
>>
>> kernel test robot noticed the following build errors:
>>
>> [auto build test ERROR on 0af2f6be1b4281385b618cb86ad946eded089ac8]
>>
>> url:    https://github.com/intel-lab-lkp/linux/commits/Lorenzo-Pieralisi/Documentation-devicetree-bindings-Add-GICv5-DT-bindings/20250408-190630
>> base:   0af2f6be1b4281385b618cb86ad946eded089ac8
>> patch link:    https://lore.kernel.org/r/20250408-gicv5-host-v1-24-1f26db465f8d%40kernel.org
>> patch subject: [PATCH 24/24] arm64: Kconfig: Enable GICv5
>> config: arm64-randconfig-001-20250409 (https://download.01.org/0day-ci/archive/20250409/202504092127.YaPW3UWk-lkp@intel.com/config)
>> compiler: clang version 21.0.0git (https://github.com/llvm/llvm-project 92c93f5286b9ff33f27ff694d2dc33da1c07afdd)
>> reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250409/202504092127.YaPW3UWk-lkp@intel.com/reproduce)
>>
>> If you fix the issue in a separate patch/commit (i.e. not just a new version of
>> the same patch/commit), kindly add following tags
>> | Reported-by: kernel test robot <lkp@intel.com>
>> | Closes: https://lore.kernel.org/oe-kbuild-all/202504092127.YaPW3UWk-lkp@intel.com/
>>
>> All errors (new ones prefixed by >>):
>>
>>>> drivers/irqchip/irq-gic-v5-iwb.c:298:3: error: cannot jump from this goto statement to its label
>>      298 |                 goto out_free;
>>          |                 ^
>>    drivers/irqchip/irq-gic-v5-iwb.c:300:2: note: jump bypasses initialization of variable with __attribute__((cleanup))
>>      300 |         guard(mutex)(&its->dev_alloc_lock);
> 
> This is clearly wrong, will update code in the IWB (if we are keeping
> the current IWB driver) and ITS by removing the guard where misused and
> keeping the mutex_lock/unlock().

Just remember to build your code with clang and W=1. Especially if you
use anything from cleanup.

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 20/24] irqchip/gic-v5: Add GICv5 LPI/IPI support
  2025-04-09 13:15         ` Lorenzo Pieralisi
@ 2025-04-09 14:25           ` Arnd Bergmann
  0 siblings, 0 replies; 62+ messages in thread
From: Arnd Bergmann @ 2025-04-09 14:25 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon, Sascha Bischoff,
	Timothy Hayes, linux-arm-kernel, linux-kernel, devicetree

On Wed, Apr 9, 2025, at 15:15, Lorenzo Pieralisi wrote:
> On Wed, Apr 09, 2025 at 12:56:52PM +0200, Arnd Bergmann wrote:
>
> KMALLOC_MAX_SIZE is set according to MAX_PAGE_ORDER, that should
> be fine for most set-ups (well, obviously implementations that
> only support a 1-level IST can't expect a very large number of
> IRQs -  we set that to 12 bits worth of IDs deliberately but
> given the current memory allocation limits it can be much higher).
>
> A 2-level IST can easily manage 24-bits worth of IDs split into
> two-level tables with the current kmalloc() limits.
>
> For the ITS DT and ITT the same reasoning goes, so the capping
> is the (rare) exception not the rule and I don't expect this to be a
> problem at all or I am missing something.

Ok, just mention that estimation in the source code. If someone
ever runs into the limit and it does become a problem, they can
then figure out whether they have an unusually small
KMALLOC_MAX_SIZE or an unusually large number of interupts.

>> >> Do you expect actual implementation to not be cache-coherent?
>> >
>> > It is allowed by the architecture - I don't have a crystal ball
>> > but if I want to add support for a non-coherent IRS the DMA mapping
>> > like sequence above has to be there - alternatives are welcome.
>> 
>> I see that we have a few GICv3 implementations that are marked
>> as non-coherent in DT. I don't understand why they'd do that,
>> but I guess there is not much to be done about it.
>
> You don't understand why the GIC HW is not coherent or why we set it
> up as such in the driver ?

I meant why hardware would be built like that. I would have
assumed that the GIC is designed to be closely tied to the
CPU core and the L2 cache, so it shouldn't be hard to make
it coherent even if the rest of the system is not.

>> The only other idea I have would be to use an uncached allocation
>> for the non-coherent case, the same way that dma_alloc_coherent()
>> or maybe dma_alloc_wc() does. This still has the same problem
>> with bypassing the dma-mapping.h interface because of the lack
>> of a device pointer, but it would at least avoid the cache flushes
>> at runtime. If I read this code right, the data in here is only
>> written by the CPU and read by the GIC, so a WC buffer wouldn't
>> be more expensive, right?
>
> The IST is also written by the GIC, the CPU reads it (explicity, with a
> memory read rather than through instructions) only if the table is two
> level and we are allocating L2 entries on demand to check whether an
> L2 entry is valid.
>
> I am not sure the CMOs are that bad given that's what we do
> for GICv3 already but it is worth looking into it.

If the reads are common enough, then an uncached mapping would
likely be slower than the flushes. Not sure which way is better
without L2, you'd probably have to measure on real hardware,
though you could perhaps do that on a GICv3 one if the access
patterns are similar enough.

      Arnd


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 22/24] irqchip/gic-v5: Add GICv5 ITS support
  2025-04-08 10:50 ` [PATCH 22/24] irqchip/gic-v5: Add GICv5 ITS support Lorenzo Pieralisi
  2025-04-09 11:13   ` Thomas Gleixner
@ 2025-04-09 18:57   ` Thomas Gleixner
  2025-04-10  8:08     ` Lorenzo Pieralisi
  1 sibling, 1 reply; 62+ messages in thread
From: Thomas Gleixner @ 2025-04-09 18:57 UTC (permalink / raw)
  To: Lorenzo Pieralisi, Marc Zyngier, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

On Tue, Apr 08 2025 at 12:50, Lorenzo Pieralisi wrote:
> +	guard(mutex)(&its->dev_alloc_lock);
> +
> +	its_dev = gicv5_its_find_device(its, device_id);
> +	if (IS_ERR(its_dev))
> +		return PTR_ERR(its_dev);
> +
> +	ret = gicv5_its_alloc_eventid(its_dev, nr_irqs, &event_id_base);
> +	if (ret)
> +		return ret;
> +
> +	ret = iommu_dma_prepare_msi(info->desc,
> +				    its->its_trans_phys_base);
> +	if (ret)
> +		goto out_eventid;

As 0-day already complained, guard() and goto does not mix. the simple
solution is to do

foo()
        guard()
        return __foo();

Then you can put the gotos into __foo()


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 22/24] irqchip/gic-v5: Add GICv5 ITS support
  2025-04-09 18:57   ` Thomas Gleixner
@ 2025-04-10  8:08     ` Lorenzo Pieralisi
  2025-04-10  9:20       ` Thomas Gleixner
  0 siblings, 1 reply; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-10  8:08 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Sascha Bischoff, Timothy Hayes,
	linux-arm-kernel, linux-kernel, devicetree

On Wed, Apr 09, 2025 at 08:57:02PM +0200, Thomas Gleixner wrote:
> On Tue, Apr 08 2025 at 12:50, Lorenzo Pieralisi wrote:
> > +	guard(mutex)(&its->dev_alloc_lock);
> > +
> > +	its_dev = gicv5_its_find_device(its, device_id);
> > +	if (IS_ERR(its_dev))
> > +		return PTR_ERR(its_dev);
> > +
> > +	ret = gicv5_its_alloc_eventid(its_dev, nr_irqs, &event_id_base);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = iommu_dma_prepare_msi(info->desc,
> > +				    its->its_trans_phys_base);
> > +	if (ret)
> > +		goto out_eventid;
> 
> As 0-day already complained, guard() and goto does not mix. the simple
> solution is to do
> 
> foo()
>         guard()
>         return __foo();
> 
> Then you can put the gotos into __foo()

Yes even though its usage in this function IIUC is technically fine,
there are other kernel functions where the guard(mutex) is at the
function entry with gotos that follow.

Regardless, bad idea to mix them, as documented, thanks.

Side note: the bug the bot caught is because I moved the guard(mutex)
lower in the function and incidentally got past gotos (and gcc did not
catch it, it has to go up to W=2 to barf).

Anyway - as I mentioned in the cover letter, the current IWB driver
design, (patch 23 - where the guard(mutex) bug is and more readl_poll_*
boilerplate :), sorry), is a bit of a question (should I move it to
DOMAIN_BUS_WIRED_TO_MSI with the fixed eventid caveat to solve somehow),
or is it OK "as-is", treating it basically as an ITS hardcoded plugin ?

Thank you very much for having a look.

Lorenzo


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 22/24] irqchip/gic-v5: Add GICv5 ITS support
  2025-04-10  8:08     ` Lorenzo Pieralisi
@ 2025-04-10  9:20       ` Thomas Gleixner
  0 siblings, 0 replies; 62+ messages in thread
From: Thomas Gleixner @ 2025-04-10  9:20 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Sascha Bischoff, Timothy Hayes,
	linux-arm-kernel, linux-kernel, devicetree

On Thu, Apr 10 2025 at 10:08, Lorenzo Pieralisi wrote:
> On Wed, Apr 09, 2025 at 08:57:02PM +0200, Thomas Gleixner wrote:
> Anyway - as I mentioned in the cover letter, the current IWB driver
> design, (patch 23 - where the guard(mutex) bug is and more readl_poll_*
> boilerplate :), sorry), is a bit of a question (should I move it to
> DOMAIN_BUS_WIRED_TO_MSI with the fixed eventid caveat to solve somehow),
> or is it OK "as-is", treating it basically as an ITS hardcoded plugin ?

I'm not sure whether DOMAIN_BUS_WIRED_TO_MSI buys much, but OTOH from a
layering perspective it's more in line with the existing wire to MSI
implementations. Marc might have an opinion there :)

Thanks,

        tglx


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 20/24] irqchip/gic-v5: Add GICv5 LPI/IPI support
  2025-04-08 10:50 ` [PATCH 20/24] irqchip/gic-v5: Add GICv5 LPI/IPI support Lorenzo Pieralisi
  2025-04-09  8:23   ` Arnd Bergmann
  2025-04-09  8:27   ` Thomas Gleixner
@ 2025-04-11  9:26   ` Lorenzo Pieralisi
  2025-04-11  9:55     ` Thomas Gleixner
  2 siblings, 1 reply; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-11  9:26 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Liam.Howlett, ast

[+Liam, Alexei, since I mentioned to them this maple tree "usage"
in the past, in case they see a point in chiming in]

On Tue, Apr 08, 2025 at 12:50:19PM +0200, Lorenzo Pieralisi wrote:

[...]

> The LPI INTID namespace is managed using a maple tree which encodes
> ranges, add code to alloc/free LPI INTIDs.
> 
> Maple tree entries are not used by the driver, only the range tracking
> is required - therefore the driver first finds an empty area large
> enough to contain the required number of LPIs then checks the
> adjacent (and possibly occupied) LPI ranges and try to merge them
> together, reducing maple tree slots usage.

The maple tree usage for this purpose is an RFC at this stage.

Added Alexei because I know BPF arena used the maple tree in
a similar way in the past and moved to a range tree because
the BPF arena requires a special purpose mem allocator.

As Thomas already pointed out a plain bitmap could do even though
it requires preallocating memory up to 2MB (or we can grow it
dynamically).

We could allocate IDs using an IDA as well, though that's 1 by 1,
we allocate LPI INTIDs 1 by 1 - mostly, upon MSI allocation, so
using an IDA could do (AFAIU it works for 0..INT_MAX we need
0..2^24 worst case).

I don't want to abuse the maple tree for this purpose, opinions
welcome, it is just to understand if there are core code structures
that can be reused.

IDs allocation snippet below.

[...]

> +static struct maple_tree lpi_mt;
> +static u32 num_lpis;
> +
> +void __init gicv5_init_lpi(u32 lpis)
> +{
> +	mt_init_flags(&lpi_mt, MT_FLAGS_ALLOC_RANGE);
> +	num_lpis = lpis;
> +}
> +
> +void __init gicv5_free_lpi(void)
> +{
> +	mtree_destroy(&lpi_mt);
> +	num_lpis = 0;
> +}
> +
> +#define MT_ENTRY	((void *)&lpi_mt) /* Unused - just a valid pointer */
> +
> +static int alloc_lpi_range(u32 lpis, u32 *base)
> +{
> +	int ret;
> +	void *entry;
> +	unsigned long lpi_base, startp, lastp;
> +
> +	MA_STATE(mas, &lpi_mt, 0, 0);
> +
> +	if (!num_lpis)
> +		return -ENODEV;

s/ENODEV/ENOSPC

> +
> +	mtree_lock(&lpi_mt);
> +	ret = mas_empty_area(&mas, 0, num_lpis - 1, lpis);
> +	if (ret) {

//Fixed
+		mtree_unlock(&lpi_mt);

> +		pr_err("Failed to perform a dynamic alloc in the LPI MT!\n");
> +		return ret;
> +	}
> +
> +	lpi_base = mas.index;
> +
> +	/*
> +	 * We don't really care about the entry itself, only about
> +	 * allocation of a maple tree ranges describing in use LPIs.
> +	 * That's why, upon allocation, we try to merge slots adjacent
> +	 * with the empty one we are allocating to minimize the number
> +	 * of slots we take from maple tree nodes for nothing, all
> +	 * we need to keep track of is in use ranges.
> +	 */
> +	startp = mas.index;
> +	lastp = mas.last;
> +
> +	entry = mas_next(&mas, num_lpis - 1);
> +	if (entry && mas.index == lastp + 1)
> +		lastp = mas.last;
> +
> +	entry = mas_prev(&mas, 0);
> +	if (entry)
> +		startp = mas.index;
> +	mas_set_range(&mas, startp, lastp);
> +	mas_store_gfp(&mas, MT_ENTRY, GFP_KERNEL);
> +	mtree_unlock(&lpi_mt);
> +
> +	// startp is the index at which we allocated, i.e. the base LPI.
> +	*base = lpi_base;
> +
> +	return 0;
> +}
> +
> +// Drop entries between min and max (inclusive)
> +static int release_lpi_range(u32 min, u32 max)
> +{
> +	return mtree_store_range(&lpi_mt, min, max, NULL, GFP_KERNEL);
> +}

Thanks,
Lorenzo


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 20/24] irqchip/gic-v5: Add GICv5 LPI/IPI support
  2025-04-11  9:26   ` Lorenzo Pieralisi
@ 2025-04-11  9:55     ` Thomas Gleixner
  2025-04-11 12:37       ` Lorenzo Pieralisi
  0 siblings, 1 reply; 62+ messages in thread
From: Thomas Gleixner @ 2025-04-11  9:55 UTC (permalink / raw)
  To: Lorenzo Pieralisi, Marc Zyngier, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Sascha Bischoff, Timothy Hayes, linux-arm-kernel, linux-kernel,
	devicetree, Liam.Howlett, ast

On Fri, Apr 11 2025 at 11:26, Lorenzo Pieralisi wrote:
> On Tue, Apr 08, 2025 at 12:50:19PM +0200, Lorenzo Pieralisi wrote:
>> Maple tree entries are not used by the driver, only the range tracking
>> is required - therefore the driver first finds an empty area large
>> enough to contain the required number of LPIs then checks the
>> adjacent (and possibly occupied) LPI ranges and try to merge them
>> together, reducing maple tree slots usage.
>
> The maple tree usage for this purpose is an RFC at this stage.
>
> Added Alexei because I know BPF arena used the maple tree in
> a similar way in the past and moved to a range tree because
> the BPF arena requires a special purpose mem allocator.
>
> As Thomas already pointed out a plain bitmap could do even though
> it requires preallocating memory up to 2MB (or we can grow it
> dynamically).
>
> We could allocate IDs using an IDA as well, though that's 1 by 1,
> we allocate LPI INTIDs 1 by 1 - mostly, upon MSI allocation, so
> using an IDA could do (AFAIU it works for 0..INT_MAX we need
> 0..2^24 worst case).

The point is that you really only need a 1-bit storage per entry,
i.e. used/unused. You won't use any of the storage functions of maple
tree, idr or whatever.

So the obvious choice is a bitmap and as you said, it's trivial to start
with a reasonably sized one and reallocate during runtime if the need
arises.

The reallocation happens in domain::ops::alloc() which is fully
preemptible context, i.e. no restrictions vs. allocations.

For the top-most domain, the callers hold domain::mutex, which excludes
concurrency vs. ops::alloc/free(). If the bitmap is in a domain further
down the hierarchy then you need your own mutex there.

Thanks,

       tglx







^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 20/24] irqchip/gic-v5: Add GICv5 LPI/IPI support
  2025-04-11  9:55     ` Thomas Gleixner
@ 2025-04-11 12:37       ` Lorenzo Pieralisi
  2025-04-12 13:01         ` Liam R. Howlett
  0 siblings, 1 reply; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-11 12:37 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Sascha Bischoff, Timothy Hayes,
	linux-arm-kernel, linux-kernel, devicetree, Liam.Howlett, ast

On Fri, Apr 11, 2025 at 11:55:22AM +0200, Thomas Gleixner wrote:
> On Fri, Apr 11 2025 at 11:26, Lorenzo Pieralisi wrote:
> > On Tue, Apr 08, 2025 at 12:50:19PM +0200, Lorenzo Pieralisi wrote:
> >> Maple tree entries are not used by the driver, only the range tracking
> >> is required - therefore the driver first finds an empty area large
> >> enough to contain the required number of LPIs then checks the
> >> adjacent (and possibly occupied) LPI ranges and try to merge them
> >> together, reducing maple tree slots usage.
> >
> > The maple tree usage for this purpose is an RFC at this stage.
> >
> > Added Alexei because I know BPF arena used the maple tree in
> > a similar way in the past and moved to a range tree because
> > the BPF arena requires a special purpose mem allocator.
> >
> > As Thomas already pointed out a plain bitmap could do even though
> > it requires preallocating memory up to 2MB (or we can grow it
> > dynamically).
> >
> > We could allocate IDs using an IDA as well, though that's 1 by 1,
> > we allocate LPI INTIDs 1 by 1 - mostly, upon MSI allocation, so
> > using an IDA could do (AFAIU it works for 0..INT_MAX we need
> > 0..2^24 worst case).
> 
> The point is that you really only need a 1-bit storage per entry,
> i.e. used/unused. You won't use any of the storage functions of maple
> tree, idr or whatever.

IDA does use the XArray entries (i.e. the pointers) to store bitmaps,
the only drawback I see is that it allocates IDs one by one (but that's
not really a problem).

I wonder if it is used in the kernel for IDs larger than 16 bits, it
should work for 0..INT_MAX.

> So the obvious choice is a bitmap and as you said, it's trivial to start
> with a reasonably sized one and reallocate during runtime if the need
> arises.

Yes I can do that too but to avoid fiddling with alloc/free ranges crossing
bitmap chunks we need a single bitmap, AFAICS that may require realloc+copy,
if the need arises.

> The reallocation happens in domain::ops::alloc() which is fully
> preemptible context, i.e. no restrictions vs. allocations.

Yes point taken.

> For the top-most domain, the callers hold domain::mutex, which excludes
> concurrency vs. ops::alloc/free(). If the bitmap is in a domain further
> down the hierarchy then you need your own mutex there.

Thanks for confirming Thomas, I do rely on the topmost mutex to be held
for dynamic IST table entries updates (LPI top domain).

Thanks,
Lorenzo


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 18/24] irqchip/gic-v5: Add GICv5 PPI support
  2025-04-08 21:42   ` Thomas Gleixner
  2025-04-09  7:30     ` Lorenzo Pieralisi
@ 2025-04-11 17:06     ` Lorenzo Pieralisi
  1 sibling, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-11 17:06 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Sascha Bischoff, Timothy Hayes,
	linux-arm-kernel, linux-kernel, devicetree

On Tue, Apr 08, 2025 at 11:42:29PM +0200, Thomas Gleixner wrote:

[...]

> > +static int gicv5_ppi_irq_set_irqchip_state(struct irq_data *d,
> > +					   enum irqchip_irq_state which,
> > +					   bool val)
> > +{
> > +	u64 hwirq_id_bit = BIT_ULL(d->hwirq % 64);
> > +
> > +	switch (which) {
> > +	case IRQCHIP_STATE_PENDING:
> > +		if (val) {
> > +			if (d->hwirq < 64)
> > +				write_sysreg_s(hwirq_id_bit,
> > +					       SYS_ICC_PPI_SPENDR0_EL1);
> > +			else
> > +				write_sysreg_s(hwirq_id_bit,
> > +					       SYS_ICC_PPI_SPENDR1_EL1);
> > +
> > +		} else {
> > +			if (d->hwirq < 64)
> > +				write_sysreg_s(hwirq_id_bit,
> > +					       SYS_ICC_PPI_CPENDR0_EL1);
> > +			else
> > +				write_sysreg_s(hwirq_id_bit,
> > +					       SYS_ICC_PPI_CPENDR1_EL1);
> > +		}
> > +
> > +		return 0;
> > +	case IRQCHIP_STATE_ACTIVE:
> > +		if (val) {
> > +			if (d->hwirq < 64)
> > +				write_sysreg_s(hwirq_id_bit,
> > +					       SYS_ICC_PPI_SACTIVER0_EL1);
> > +			else
> > +				write_sysreg_s(hwirq_id_bit,
> > +					       SYS_ICC_PPI_SACTIVER1_EL1);
> > +		} else {
> > +			if (d->hwirq < 64)
> > +				write_sysreg_s(hwirq_id_bit,
> > +					       SYS_ICC_PPI_CACTIVER0_EL1);
> > +			else
> > +				write_sysreg_s(hwirq_id_bit,
> > +					       SYS_ICC_PPI_CACTIVER1_EL1);
> > +		}
> 
> You already precalculate hwirq_id_bit. Can't you do something similar
> for the registers?
> 
> 	case IRQCHIP_STATE_PENDING:
>         	u32 reg = val ? SYS_ICC_PPI_SPENDR1_EL1 : SYS_ICC_PPI_SPENDR0_EL1;
> 
>                 write_sysreg_s(hwirq_id_bit, reg);
>                 return 0;
> 	case IRQCHIP_STATE_ACTIVE:
>                 ....
> 
> Ditto in the get_state() function.
> 
> No?

Can't do it like that, write_sysreg_s takes a register encoding, not a
u32 offset, I will see if I can wrap it in a macro, even though that
might obfuscate a bit rather than clarify.

> > +static asmlinkage void __exception_irq_entry
> > +gicv5_handle_irq(struct pt_regs *regs)
> > +{
> > +	u64 ia;
> > +	bool valid;
> > +	u32 hwirq;
> 
> See above
> 
> > +	ia = gicr_insn(GICV5_OP_GICR_CDIA);
> > +	valid = GICV5_GIC_CDIA_VALID(ia);
> 
> And please move that to the declaration lines

gicr_insn() is an instruction ACK'ing the IRQ, I would leave it like this
explicitly if you don't mind lest it gave the impression that it
is an initializer.

Thanks,
Lorenzo

> > +static int __init gicv5_init_domains(struct fwnode_handle *handle)
> > +{
> > +	gicv5_global_data.fwnode = handle;
> > +	gicv5_global_data.ppi_domain = irq_domain_create_linear(
> > +		handle, 128, &gicv5_irq_ppi_domain_ops, NULL);
> 
> The ever changing choice of coding styles across functions is really
> interesting. Obviously the length of 'gicv5_global_data.ppi_domain'
> forces ugly, but that does not mean it needs to be that way:
> 
>        struct irqdomain *d;
> 
>        d = irq_domain_create_linear(handle, 128, &gicv5_irq_ppi_domain_ops, NULL);
>        if (!d)
>        		return - ENOMEM;
> 
>        irq_domain_update_bus_token(d, DOMAIN_BUS_WIRED);
>        gicv5_global_data.fwnode = handle;
>        gicv5_global_data.ppi_domain = d;
>        return 0;
> 
> No?
> 
> > +static int __init gicv5_of_init(struct device_node *node,
> > +				struct device_node *parent)
> > +{
> > +	int ret;
> > +
> > +	ret = gicv5_init_domains(&node->fwnode);
> > +	if (ret)
> > +		return ret;
> > +
> > +	gicv5_set_cpuif_pribits();
> > +
> > +	ret = gicv5_starting_cpu(smp_processor_id());
> 
> You invoke the CPU hotplug callback for the boot CPU explicitly, but
> what the heck installs the actual hotplug callback for the secondary
> CPUs?
> 
> Thanks,
> 
>         tglx


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 20/24] irqchip/gic-v5: Add GICv5 LPI/IPI support
  2025-04-11 12:37       ` Lorenzo Pieralisi
@ 2025-04-12 13:01         ` Liam R. Howlett
  2025-04-14  8:26           ` Lorenzo Pieralisi
  0 siblings, 1 reply; 62+ messages in thread
From: Liam R. Howlett @ 2025-04-12 13:01 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Thomas Gleixner, Marc Zyngier, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon, Sascha Bischoff,
	Timothy Hayes, linux-arm-kernel, linux-kernel, devicetree, ast

* Lorenzo Pieralisi <lpieralisi@kernel.org> [250411 08:37]:

Thanks for the Cc.

> On Fri, Apr 11, 2025 at 11:55:22AM +0200, Thomas Gleixner wrote:
> > On Fri, Apr 11 2025 at 11:26, Lorenzo Pieralisi wrote:
> > > On Tue, Apr 08, 2025 at 12:50:19PM +0200, Lorenzo Pieralisi wrote:
> > >> Maple tree entries are not used by the driver, only the range tracking
> > >> is required - therefore the driver first finds an empty area large
> > >> enough to contain the required number of LPIs then checks the
> > >> adjacent (and possibly occupied) LPI ranges and try to merge them
> > >> together, reducing maple tree slots usage.
> > >
> > > The maple tree usage for this purpose is an RFC at this stage.
> > >
> > > Added Alexei because I know BPF arena used the maple tree in
> > > a similar way in the past and moved to a range tree because
> > > the BPF arena requires a special purpose mem allocator.
> > >
> > > As Thomas already pointed out a plain bitmap could do even though
> > > it requires preallocating memory up to 2MB (or we can grow it
> > > dynamically).
> > >
> > > We could allocate IDs using an IDA as well, though that's 1 by 1,
> > > we allocate LPI INTIDs 1 by 1 - mostly, upon MSI allocation, so
> > > using an IDA could do (AFAIU it works for 0..INT_MAX we need
> > > 0..2^24 worst case).
> > 
> > The point is that you really only need a 1-bit storage per entry,
> > i.e. used/unused. You won't use any of the storage functions of maple
> > tree, idr or whatever.
> 
> IDA does use the XArray entries (i.e. the pointers) to store bitmaps,
> the only drawback I see is that it allocates IDs one by one (but that's
> not really a problem).
> 
> I wonder if it is used in the kernel for IDs larger than 16 bits, it
> should work for 0..INT_MAX.
> 
> > So the obvious choice is a bitmap and as you said, it's trivial to start
> > with a reasonably sized one and reallocate during runtime if the need
> > arises.

I think the IDA or the bitmap for space saving would be better - the
xarray does do something under the hood for IDA space savings.

If you want to compare, I can suggest some changes to your maple tree
code (mas_{next/prev}_range might help).

> 
> Yes I can do that too but to avoid fiddling with alloc/free ranges crossing
> bitmap chunks we need a single bitmap, AFAICS that may require realloc+copy,
> if the need arises.

That is the advantage of the IDA or maple tree, the expansion is handled
for you. I'd be inclined to suggest using the IDA, but I'm not sure how
important storing an entire range is for your usecase?

Are there other reasons you want to use the maple tree besides the range
support?

Thanks,
Liam


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 20/24] irqchip/gic-v5: Add GICv5 LPI/IPI support
  2025-04-12 13:01         ` Liam R. Howlett
@ 2025-04-14  8:26           ` Lorenzo Pieralisi
  2025-04-14 14:37             ` Liam R. Howlett
  0 siblings, 1 reply; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-14  8:26 UTC (permalink / raw)
  To: Liam R. Howlett
  Cc: Thomas Gleixner, Marc Zyngier, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon, Sascha Bischoff,
	Timothy Hayes, linux-arm-kernel, linux-kernel, devicetree, ast

On Sat, Apr 12, 2025 at 09:01:18AM -0400, Liam R. Howlett wrote:
> * Lorenzo Pieralisi <lpieralisi@kernel.org> [250411 08:37]:
> 
> Thanks for the Cc.
> 
> > On Fri, Apr 11, 2025 at 11:55:22AM +0200, Thomas Gleixner wrote:
> > > On Fri, Apr 11 2025 at 11:26, Lorenzo Pieralisi wrote:
> > > > On Tue, Apr 08, 2025 at 12:50:19PM +0200, Lorenzo Pieralisi wrote:
> > > >> Maple tree entries are not used by the driver, only the range tracking
> > > >> is required - therefore the driver first finds an empty area large
> > > >> enough to contain the required number of LPIs then checks the
> > > >> adjacent (and possibly occupied) LPI ranges and try to merge them
> > > >> together, reducing maple tree slots usage.
> > > >
> > > > The maple tree usage for this purpose is an RFC at this stage.
> > > >
> > > > Added Alexei because I know BPF arena used the maple tree in
> > > > a similar way in the past and moved to a range tree because
> > > > the BPF arena requires a special purpose mem allocator.
> > > >
> > > > As Thomas already pointed out a plain bitmap could do even though
> > > > it requires preallocating memory up to 2MB (or we can grow it
> > > > dynamically).
> > > >
> > > > We could allocate IDs using an IDA as well, though that's 1 by 1,
> > > > we allocate LPI INTIDs 1 by 1 - mostly, upon MSI allocation, so
> > > > using an IDA could do (AFAIU it works for 0..INT_MAX we need
> > > > 0..2^24 worst case).
> > > 
> > > The point is that you really only need a 1-bit storage per entry,
> > > i.e. used/unused. You won't use any of the storage functions of maple
> > > tree, idr or whatever.
> > 
> > IDA does use the XArray entries (i.e. the pointers) to store bitmaps,
> > the only drawback I see is that it allocates IDs one by one (but that's
> > not really a problem).
> > 
> > I wonder if it is used in the kernel for IDs larger than 16 bits, it
> > should work for 0..INT_MAX.
> > 
> > > So the obvious choice is a bitmap and as you said, it's trivial to start
> > > with a reasonably sized one and reallocate during runtime if the need
> > > arises.
> 
> I think the IDA or the bitmap for space saving would be better - the
> xarray does do something under the hood for IDA space savings.
> 
> If you want to compare, I can suggest some changes to your maple tree
> code (mas_{next/prev}_range might help).

Thank you.

> > Yes I can do that too but to avoid fiddling with alloc/free ranges crossing
> > bitmap chunks we need a single bitmap, AFAICS that may require realloc+copy,
> > if the need arises.
> 
> That is the advantage of the IDA or maple tree, the expansion is handled
> for you. I'd be inclined to suggest using the IDA, but I'm not sure how
> important storing an entire range is for your usecase?

The point is, IDs represent interrupt IDs. We allocate them in batches,
whose length varies, it can be 1 but it can also be a larger vector
(ie 1024).

It is obviously faster to allocate a range than allocating them 1 by 1,
that's the only reason why we have not used an IDA (and also because I
did not know whether an IDA is recommended for a larger ID space > than,
say, 2^16 - but I think it is because it is designed to cover 0..INT_MAX
and I noticed that -mm folks may even ask to extend it).
> 
> Are there other reasons you want to use the maple tree besides the range
> support?

We used the maple tree because it handles ranges, we have not found a
sound usage for the 8 byte entry pointer (there may be some but it is
overengineering), that's why I try to merge adjacent ranges on
allocation, for vectors that are length 1 or 2 it is gross to waste
8 bytes for nothing.

Using an IDA and allocating 1 by 1 has its advantages (ie if the ID
space is fragmented it is easier to find free IDs - even though,
again, given the expected allocation pattern, freeing IRQ IDs is rarer
than allocating them so I am not sure we would end up having a very
sparse ID space).

All in all, other than looking sloppy (allocating 1 by 1 when we could
allocate a range), using an IDA would work.

In terms of memory space efficiency, I think this depends on allocation
patterns (and what I did minimise wasting slot entries for nothing).

I added Alexei because, memory allocation notwithstanding, handling
ranges is what the BPF range tree does:

commit b795379757eb

the reason a range tree was implemented to replace a MT was the
memory allocation requirements - they were using a maple tree before
(with unused entries).

I can go for an IDA unless someone see a point in pursuing the current
approach - that I would update according to feedback, at least with
this thread you get the full picture.

Thanks !
Lorenzo


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 20/24] irqchip/gic-v5: Add GICv5 LPI/IPI support
  2025-04-14  8:26           ` Lorenzo Pieralisi
@ 2025-04-14 14:37             ` Liam R. Howlett
  2025-04-15  8:08               ` Lorenzo Pieralisi
  0 siblings, 1 reply; 62+ messages in thread
From: Liam R. Howlett @ 2025-04-14 14:37 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Thomas Gleixner, Marc Zyngier, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon, Sascha Bischoff,
	Timothy Hayes, linux-arm-kernel, linux-kernel, devicetree, ast

* Lorenzo Pieralisi <lpieralisi@kernel.org> [250414 04:27]:
> On Sat, Apr 12, 2025 at 09:01:18AM -0400, Liam R. Howlett wrote:
> > * Lorenzo Pieralisi <lpieralisi@kernel.org> [250411 08:37]:
> > 

...

> 
> > > Yes I can do that too but to avoid fiddling with alloc/free ranges crossing
> > > bitmap chunks we need a single bitmap, AFAICS that may require realloc+copy,
> > > if the need arises.
> > 
> > That is the advantage of the IDA or maple tree, the expansion is handled
> > for you. I'd be inclined to suggest using the IDA, but I'm not sure how
> > important storing an entire range is for your usecase?
> 
> The point is, IDs represent interrupt IDs. We allocate them in batches,
> whose length varies, it can be 1 but it can also be a larger vector
> (ie 1024).

Ah, that is a lot.

> 
> It is obviously faster to allocate a range than allocating them 1 by 1,
> that's the only reason why we have not used an IDA (and also because I
> did not know whether an IDA is recommended for a larger ID space > than,
> say, 2^16 - but I think it is because it is designed to cover 0..INT_MAX
> and I noticed that -mm folks may even ask to extend it).

Yeah, I see this in the doc:
Some users need to allocate IDs larger than INT_MAX. So far all of these
users have been content with a UINT_MAX limit, and they use
idr_alloc_u32(). If you need IDs that will not fit in a u32, we will
work with you to address your needs.

> > 
> > Are there other reasons you want to use the maple tree besides the range
> > support?
> 
> We used the maple tree because it handles ranges, we have not found a
> sound usage for the 8 byte entry pointer (there may be some but it is
> overengineering), that's why I try to merge adjacent ranges on
> allocation, for vectors that are length 1 or 2 it is gross to waste
> 8 bytes for nothing.
> 
> Using an IDA and allocating 1 by 1 has its advantages (ie if the ID
> space is fragmented it is easier to find free IDs - even though,
> again, given the expected allocation pattern, freeing IRQ IDs is rarer
> than allocating them so I am not sure we would end up having a very
> sparse ID space).
> 
> All in all, other than looking sloppy (allocating 1 by 1 when we could
> allocate a range), using an IDA would work.
> 
> In terms of memory space efficiency, I think this depends on allocation
> patterns (and what I did minimise wasting slot entries for nothing).
> 
> I added Alexei because, memory allocation notwithstanding, handling
> ranges is what the BPF range tree does:
> 
> commit b795379757eb
> 
> the reason a range tree was implemented to replace a MT was the
> memory allocation requirements - they were using a maple tree before
> (with unused entries).

Interesting, Alexei uses the bpf allocator.

> 
> I can go for an IDA unless someone see a point in pursuing the current
> approach - that I would update according to feedback, at least with
> this thread you get the full picture.

Eventually, we plan to swap out the backing of the IDA to use the maple
tree.  I think we could add range support as part of this change.

If you were to add an interface to contiguous allocate a number of IDAs
by putting your loop in the IDA interface, we'd be able to switch that
to a range store during the conversion and keep the use case visible
during the planning stages.

Having that interface would make it obvious the change is necessary and
wouldn't be missed.

Unfortunately, I don't really have a timeline on changing the IDA to the
maple tree.

Please keep me Cc'ed on whatever you decide.

Thanks,
Liam


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 20/24] irqchip/gic-v5: Add GICv5 LPI/IPI support
  2025-04-14 14:37             ` Liam R. Howlett
@ 2025-04-15  8:08               ` Lorenzo Pieralisi
  0 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-15  8:08 UTC (permalink / raw)
  To: Liam R. Howlett
  Cc: Thomas Gleixner, Marc Zyngier, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon, Sascha Bischoff,
	Timothy Hayes, linux-arm-kernel, linux-kernel, devicetree, ast

On Mon, Apr 14, 2025 at 10:37:57AM -0400, Liam R. Howlett wrote:

[...]

> > I can go for an IDA unless someone see a point in pursuing the current
> > approach - that I would update according to feedback, at least with
> > this thread you get the full picture.
> 
> Eventually, we plan to swap out the backing of the IDA to use the maple
> tree.  I think we could add range support as part of this change.
> 
> If you were to add an interface to contiguous allocate a number of IDAs
> by putting your loop in the IDA interface, we'd be able to switch that
> to a range store during the conversion and keep the use case visible
> during the planning stages.
> 
> Having that interface would make it obvious the change is necessary and
> wouldn't be missed.

Yep understood but on the other hand it would force me to allocate a set
of contiguous IDs, which is a bit cumbersome with the current IDA
(alloc them one by one - if any fails to be contiguous restart, rince,
repeat - it is a double whammy).

I allocate a range because I know the maple tree handle them efficiently
and to be honest, a range allocator is all I need, there isn't any in
the core kernel (there are plenty - on purpose - range allocators, BPF
range tree and GICv3 ITS LPIs allocator are good examples, I could reuse
them instead of reinventing the wheel).

FWIW, when Alexei implemented the BPF arena he vetted other examples.

https://lore.kernel.org/bpf/20241105212001.38980-1-alexei.starovoitov@gmail.com/

I don't think the code I wrote is that complex either and it minimizes
entry storage waste - probably you don't want to see it in the kernel
because it is not a MT usage you'd expect - storage wise, I need to
measure it but I think it definitely requires fewer bytes than a
preallocated bitmap - when the IDA range API is in I would be glad
to swap to it.

> Unfortunately, I don't really have a timeline on changing the IDA to the
> maple tree.

I understand, it is a chicken and egg situation, see above.

> Please keep me Cc'ed on whatever you decide.

I'd need something promptly, so I will go for a simple solution (bitmap
or IDA 1-by-1) if the current MT one has to be scrapped.

Thank you,
Lorenzo


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 18/24] irqchip/gic-v5: Add GICv5 PPI support
  2025-04-09  7:30     ` Lorenzo Pieralisi
@ 2025-04-17 14:49       ` Lorenzo Pieralisi
  0 siblings, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-17 14:49 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Sascha Bischoff, Timothy Hayes,
	linux-arm-kernel, linux-kernel, devicetree

On Wed, Apr 09, 2025 at 09:30:52AM +0200, Lorenzo Pieralisi wrote:

[...]

> > > +static int __init gicv5_of_init(struct device_node *node,
> > > +				struct device_node *parent)
> > > +{
> > > +	int ret;
> > > +
> > > +	ret = gicv5_init_domains(&node->fwnode);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	gicv5_set_cpuif_pribits();
> > > +
> > > +	ret = gicv5_starting_cpu(smp_processor_id());
> > 
> > You invoke the CPU hotplug callback for the boot CPU explicitly, but
> > what the heck installs the actual hotplug callback for the secondary
> > CPUs?
> 
> That comes with a subsequent patch[21]. I mentioned in the cover letter
> that I tried to split the functionality into interrupt types to ease
> review (well, it does not look like I succeeded, sorry) and then in
> patch [21] (when LPIs backing IPIs are implemented), enable SMP.
> 
> The point is, we need patches [18-21] to enable SMP booting.
> 
> I can squash [18-21] all together or I can enable the hotplug callback
> here but this patch stand alone is not functional for the reasons
> above, let me know please what's best in your opinion and I will do.

I would squash patches [18-21] for v2 because that's the only option to
have a single logical and functional SMP working entity, unless you are
against it.

We need IPIs for SMP booting, those are built on LPIs that in turn
requires the IRS code to function - I tried to split all this code
but whatever I came up with makes no sense from a functional
perspective.

The only thing I could do is taking SPI enablement code (SPI IRQs are
optional) out of [18-21] and make it a separate patch, to be honest
SPI management I don't think it is a lot of code so that won't change
much IMO.

Please let me know, thanks a lot.

Lorenzo


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 20/24] irqchip/gic-v5: Add GICv5 LPI/IPI support
  2025-04-09 10:56       ` Arnd Bergmann
  2025-04-09 13:15         ` Lorenzo Pieralisi
@ 2025-04-18  9:21         ` Lorenzo Pieralisi
  1 sibling, 0 replies; 62+ messages in thread
From: Lorenzo Pieralisi @ 2025-04-18  9:21 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon, Sascha Bischoff,
	Timothy Hayes, linux-arm-kernel, linux-kernel, devicetree

On Wed, Apr 09, 2025 at 12:56:52PM +0200, Arnd Bergmann wrote:
> On Wed, Apr 9, 2025, at 12:11, Lorenzo Pieralisi wrote:
> > On Wed, Apr 09, 2025 at 10:23:57AM +0200, Arnd Bergmann wrote:
> >> On Tue, Apr 8, 2025, at 12:50, Lorenzo Pieralisi wrote:
> >> > +static void irs_writeq(struct gicv5_irs_chip_data *irs_data, const u64 
> >> > val,
> >> > +		       const u64 reg_offset)
> >> > +{
> >> > +	writeq_relaxed(val, irs_data->irs_base + reg_offset);
> >> > +}
> >> 
> >> I think the use of _relaxed memory accessors needs some code
> >> comments here. The definition of these is that you don't care
> >> about ordering relative to DMA master accesses, yet you seem to
> >> very much have accesses to the 'ist' from the GIC, as well as
> >> DMA accesses from an MSI device, and I would expect both to
> >> require ordering.
> >
> > For the 1-level (linear) IST we allocate it in one go, write
> > the base address through relaxed access (that sets the IST
> > valid) and poll completion with a relaxed access. Memory is
> > cleaned and invalidated from the cache (if the IRS is not
> > coherent) before the MMIO sequence above, which implies a
> > dsb().
> >
> > After that memory is handed over to the GIC.
> >
> > For a 2-level IST, the code that updates L1 entries already add
> > a dma_rmb() barrier (ie gicv5_irs_iste_alloc()) to make sure we
> > order MMIO wait completion with the subsequent cache invalidate
> > (again, in the yet hypothetical case where the IRS is not coherent).
> >
> > I think I can add comments where the sequence to initialize the
> > tables is executed more than here, given that these helpers are
> > used for other purposes too.
> 
> Usually my recommendation is to have abstractions like this
> provide both relaxed and normal variants, and then only
> use the relaxed ones where it really matters for performance.
> 
> That way you can keep relatively short explanations where
> you call irs_writeq_relaxed() and use irs_writeq() without
> any code comments any place that doesn't care about saving
> a few cycles per call.

Technically, memory is always handed over to the GIC following a GICv5
MMIO register poll that guarantees the GIC has seen the change (whether
it is making an IST table valid, or enabling an ITS - that in turn means
device table (DT) translations are enabled).

One thing I could do is to make the readl_relaxed_poll_timeout_atomic()
readl_poll_timeout_atomic() on those "completion registers", which adds
the required barriers (I can add a comment to the related calling
sites).

That should also allow me to remove the dma_rmb() barrier that I need in:

gicv5_irs_iste_alloc()

to prevent the CPU from issuing a cache invalidation before the

readl_relaxed_poll_timeout_atomic()

loop has completed.

I don't see any other corner case where the _relaxed version would
not work I tried to follow other drivers where AFAICS the relaxed
version is used by default unless the non-relaxed one is strictly
needed (GICv3/SMMUv3).

I can obviously update the {irs/its}_ helpers to add a relaxed
name to make sure it is more obvious to understand what they do.

Would that work ?

Thanks,
Lorenzo

> >> > +/* Wait for completion of an IST change */
> >> > +static int gicv5_irs_ist_wait_for_idle(struct gicv5_irs_chip_data 
> >> > *irs_data)
> >> > +{
> >> > +	int ret;
> >> > +	u32 val;
> >> > +
> >> > +	ret = readl_relaxed_poll_timeout_atomic(
> >> > +			irs_data->irs_base + GICV5_IRS_IST_STATUSR, val,
> >> > +			FIELD_GET(GICV5_IRS_IST_STATUSR_IDLE, val), 1,
> >> > +			USEC_PER_SEC);
> >> > +
> >> 
> >> What is the significance of the 1 second timeout? This is probably
> >> a million times longer than I would expect any hardware interaction
> >> to be specified to take. Are you waiting for another thread here?
> >
> > It is arbitrary, agreed.
> 
> Can you make either much shorter, or non-atomic then?
> 
> >> > +	l2istsz = BIT(n + 1);
> >> > +	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;
> >> > +	}
> >> 
> >> The use of KMALLOC_MAX_SIZE seem arbitrary here. I remember discussing
> >> this in the past and concluding that this is fine for all cases
> >> that may be relevant, but it would be good to explain the reasoning
> >> in a comment.
> >
> > We need contiguous physical memory that can be < PAGE_SIZE or larger.
> >
> > For allocations larger than the allocator caches kmalloc hands over to
> > the page allocator, MAX_ORDER is reflected into KMALLOC_MAX_SIZE AFAIU.
> >
> > That's the reasoning. Does it make sense ?
> 
> I'm more worried about what happens when KMALLOC_MAX_SIZE is
> really small -- did you show that the allocation is still
> going to work, or is this likely to cause runtime problems?
> 
> >> > +	if (irs_data->flags & IRS_FLAGS_NON_COHERENT)
> >> > +		dcache_clean_inval_poc((unsigned long)ist,
> >> > +				       (unsigned long)ist + l2istsz);
> >> > +	else
> >> > +		dsb(ishst);
> >> ...
> >> > +	baser = (virt_to_phys(ist) & GICV5_IRS_IST_BASER_ADDR_MASK) |
> >> > +		FIELD_PREP(GICV5_IRS_IST_BASER_VALID, 0x1);
> >> 
> >> Here it seems like you are open-coding the DMA mapping interface
> >> details, in particular the mapping of the 'ist' memory area into
> >> the gic's DMA master space, the coherency and the barrier that is
> >> normally part of a (non-relaxed) writeq().  Is there a reason
> >> you can't use the normal interfaces here, using dma_alloc_coherent()
> >> or dma_alloc_noncoherent()?
> >
> > The GIC IRS must be brought up early, it is not a struct device.
> 
> Right, that is rather unfortunate.
> 
> >> Do you expect actual implementation to not be cache-coherent?
> >
> > It is allowed by the architecture - I don't have a crystal ball
> > but if I want to add support for a non-coherent IRS the DMA mapping
> > like sequence above has to be there - alternatives are welcome.
> 
> I see that we have a few GICv3 implementations that are marked
> as non-coherent in DT. I don't understand why they'd do that,
> but I guess there is not much to be done about it.
> 
> The only other idea I have would be to use an uncached allocation
> for the non-coherent case, the same way that dma_alloc_coherent()
> or maybe dma_alloc_wc() does. This still has the same problem
> with bypassing the dma-mapping.h interface because of the lack
> of a device pointer, but it would at least avoid the cache flushes
> at runtime. If I read this code right, the data in here is only
> written by the CPU and read by the GIC, so a WC buffer wouldn't
> be more expensive, right?
> 
>        Arnd


^ permalink raw reply	[flat|nested] 62+ messages in thread

end of thread, other threads:[~2025-04-18  9:23 UTC | newest]

Thread overview: 62+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-04-08 10:49 [PATCH 00/24] Arm GICv5: Host driver implementation Lorenzo Pieralisi
2025-04-08 10:50 ` [PATCH 01/24] Documentation: devicetree: bindings: Add GICv5 DT bindings Lorenzo Pieralisi
2025-04-08 12:26   ` Rob Herring (Arm)
2025-04-08 14:58     ` Lorenzo Pieralisi
2025-04-08 15:07   ` Rob Herring
2025-04-09  8:20     ` Lorenzo Pieralisi
2025-04-08 10:50 ` [PATCH 02/24] arm64/sysreg: Add GCIE field to ID_AA64PFR2_EL1 Lorenzo Pieralisi
2025-04-08 10:50 ` [PATCH 03/24] arm64/sysreg: Add ICC_PPI_PRIORITY<n>_EL1 Lorenzo Pieralisi
2025-04-08 10:50 ` [PATCH 04/24] arm64/sysreg: Add ICC_ICSR_EL1 Lorenzo Pieralisi
2025-04-08 10:50 ` [PATCH 05/24] arm64/sysreg: Add ICC_PPI_HMR<n>_EL1 Lorenzo Pieralisi
2025-04-08 10:50 ` [PATCH 06/24] arm64/sysreg: Add ICC_PPI_ENABLER<n>_EL1 Lorenzo Pieralisi
2025-04-08 10:50 ` [PATCH 07/24] arm64/sysreg: Add ICC_PPI_{C/S}ACTIVER<n>_EL1 Lorenzo Pieralisi
2025-04-08 10:50 ` [PATCH 08/24] arm64/sysreg: Add ICC_PPI_{C/S}PENDR<n>_EL1 Lorenzo Pieralisi
2025-04-08 10:50 ` [PATCH 09/24] arm64/sysreg: Add ICC_CR0_EL1 Lorenzo Pieralisi
2025-04-08 10:50 ` [PATCH 10/24] arm64/sysreg: Add ICC_PCR_EL1 Lorenzo Pieralisi
2025-04-08 10:50 ` [PATCH 11/24] arm64/sysreg: Add ICC_IDR0_EL1 Lorenzo Pieralisi
2025-04-08 10:50 ` [PATCH 12/24] arm64/sysreg: Add ICH_HFGRTR_EL2 Lorenzo Pieralisi
2025-04-08 10:50 ` [PATCH 13/24] arm64/sysreg: Add ICH_HFGWTR_EL2 Lorenzo Pieralisi
2025-04-09  7:48   ` Arnd Bergmann
2025-04-09  8:51     ` Lorenzo Pieralisi
2025-04-08 10:50 ` [PATCH 14/24] arm64/sysreg: Add ICH_HFGITR_EL2 Lorenzo Pieralisi
2025-04-08 10:50 ` [PATCH 15/24] arm64: Disable GICv5 read/write/instruction traps Lorenzo Pieralisi
2025-04-08 10:50 ` [PATCH 16/24] arm64: cpucaps: Add GCIE capability Lorenzo Pieralisi
2025-04-08 11:26   ` Mark Rutland
2025-04-08 15:02     ` Lorenzo Pieralisi
2025-04-08 10:50 ` [PATCH 17/24] arm64: smp: Support non-SGIs for IPIs Lorenzo Pieralisi
2025-04-08 10:50 ` [PATCH 18/24] irqchip/gic-v5: Add GICv5 PPI support Lorenzo Pieralisi
2025-04-08 21:42   ` Thomas Gleixner
2025-04-09  7:30     ` Lorenzo Pieralisi
2025-04-17 14:49       ` Lorenzo Pieralisi
2025-04-11 17:06     ` Lorenzo Pieralisi
2025-04-08 10:50 ` [PATCH 19/24] irqchip/gic-v5: Add GICv5 IRS/SPI support Lorenzo Pieralisi
2025-04-09  7:02   ` Thomas Gleixner
2025-04-09  7:40     ` Lorenzo Pieralisi
2025-04-08 10:50 ` [PATCH 20/24] irqchip/gic-v5: Add GICv5 LPI/IPI support Lorenzo Pieralisi
2025-04-09  8:23   ` Arnd Bergmann
2025-04-09 10:11     ` Lorenzo Pieralisi
2025-04-09 10:56       ` Arnd Bergmann
2025-04-09 13:15         ` Lorenzo Pieralisi
2025-04-09 14:25           ` Arnd Bergmann
2025-04-18  9:21         ` Lorenzo Pieralisi
2025-04-09  8:27   ` Thomas Gleixner
2025-04-09 10:30     ` Lorenzo Pieralisi
2025-04-11  9:26   ` Lorenzo Pieralisi
2025-04-11  9:55     ` Thomas Gleixner
2025-04-11 12:37       ` Lorenzo Pieralisi
2025-04-12 13:01         ` Liam R. Howlett
2025-04-14  8:26           ` Lorenzo Pieralisi
2025-04-14 14:37             ` Liam R. Howlett
2025-04-15  8:08               ` Lorenzo Pieralisi
2025-04-08 10:50 ` [PATCH 21/24] irqchip/gic-v5: Enable GICv5 SMP booting Lorenzo Pieralisi
2025-04-08 10:50 ` [PATCH 22/24] irqchip/gic-v5: Add GICv5 ITS support Lorenzo Pieralisi
2025-04-09 11:13   ` Thomas Gleixner
2025-04-09 13:37     ` Lorenzo Pieralisi
2025-04-09 18:57   ` Thomas Gleixner
2025-04-10  8:08     ` Lorenzo Pieralisi
2025-04-10  9:20       ` Thomas Gleixner
2025-04-08 10:50 ` [PATCH 23/24] irqchip/gic-v5: Add GICv5 IWB support Lorenzo Pieralisi
2025-04-08 10:50 ` [PATCH 24/24] arm64: Kconfig: Enable GICv5 Lorenzo Pieralisi
2025-04-09 13:44   ` kernel test robot
2025-04-09 14:04     ` Lorenzo Pieralisi
2025-04-09 14:07       ` Krzysztof Kozlowski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).