linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 00/25] Arm GICv5: Host driver implementation
@ 2025-05-06 12:23 Lorenzo Pieralisi
  2025-05-06 12:23 ` [PATCH v3 01/25] dt-bindings: interrupt-controller: Add Arm GICv5 Lorenzo Pieralisi
                   ` (25 more replies)
  0 siblings, 26 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, 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 organized 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.

This patch series is split into patches matching *logical* entities,
to make the review easier.

Logical entities:

- PPI
- IRS/SPI
- LPI/IPI
- SMP enablement
- ITS

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 in a separate schema; 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 has been dropped owing to issues encountered with
core code DOMAIN_BUS_WIRED_TO_MSI bus token handling:

https://lore.kernel.org/lkml/87tt6310hu.wl-maz@kernel.org/

=============
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 using an IDA. IDA does not support
allocating ranges, which is a bit cumbersome because this forces us
to allocate IDs one by one where the LPIs could actually be allocated
in chunks.

An IDA was chosen because basically it is a dynamic bitmap, which
carries out memory allocation automatically.

Other drivers/subsystems made different choices to allocate ranges,
an IDA was chosen since it is part of the core kernel and an IDA
range API is in the making.

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 driver has been dropped owing to issues encountered with
core code DOMAIN_BUS_WIRED_TO_MSI bus token handling:

https://lore.kernel.org/lkml/87tt6310hu.wl-maz@kernel.org/

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

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

A big thank you to M.Zyngier for his fundamental help/advice.

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>
---
Changes in v3:
- Reintroduced v1 patch split to simplify review
- Reworked IRS/ITS iopoll loop, split in atomic/non-atomic
- Cleaned-up IRS/ITS code with macros addressing review comments
- Dropped IWB driver waiting for IRQ core code to be fixed for DOMAIN_BUS_WIRED_TO_MSI
  https://lore.kernel.org/lkml/87tt6310hu.wl-maz@kernel.org/
- Moved headers to arch/arm64 and include/linux/irqchip
- Reworked GSB barriers definition
- Added extensive GSB/ISB barriers comments
- Limited error checking on IRS/ITS code - introduced couple of fatal
  BUG_ON checks
- Link to v2: https://lore.kernel.org/r/20250424-gicv5-host-v2-0-545edcaf012b@kernel.org

Changes in v2:
- Squashed patches [18-21] into a single logical entity
- Replaced maple tree with IDA for LPI IDs allocation
- Changed coding style to tip-maintainer guidelines
- Tried to consolidate poll wait mechanism into fewer functions
- Added comments related to _relaxed accessors, barriers and kmalloc
  limitations
- Removed IPI affinity check hotplug callback
- Applied DT schema changes requested, moved IWB into a separate schema
- Fixed DT examples
- Fixed guard() usage
- Link to v1: https://lore.kernel.org/r/20250408-gicv5-host-v1-0-1f26db465f8d@kernel.org

---
Lorenzo Pieralisi (24):
      dt-bindings: interrupt-controller: Add Arm GICv5
      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: Rename GICv3 CPU interface capability
      arm64: cpucaps: Add GICv5 CPU interface (GCIE) capability
      arm64: Add support for GICv5 GSB barriers
      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
      arm64: Kconfig: Enable GICv5

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

 .../interrupt-controller/arm,gic-v5-iwb.yaml       |   76 ++
 .../bindings/interrupt-controller/arm,gic-v5.yaml  |  196 ++++
 MAINTAINERS                                        |   10 +
 arch/arm64/Kconfig                                 |    1 +
 arch/arm64/include/asm/barrier.h                   |    3 +
 arch/arm64/include/asm/el2_setup.h                 |   45 +
 arch/arm64/include/asm/smp.h                       |   24 +-
 arch/arm64/include/asm/sysreg.h                    |   83 +-
 arch/arm64/kernel/cpufeature.c                     |   17 +-
 arch/arm64/kernel/smp.c                            |  156 ++-
 arch/arm64/tools/cpucaps                           |    3 +-
 arch/arm64/tools/sysreg                            |  495 +++++++-
 drivers/irqchip/Kconfig                            |   12 +
 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                   |  819 ++++++++++++++
 drivers/irqchip/irq-gic-v5-its.c                   | 1176 ++++++++++++++++++++
 drivers/irqchip/irq-gic-v5.c                       | 1046 +++++++++++++++++
 drivers/irqchip/irq-gic.c                          |    2 +-
 include/linux/irqchip/arm-gic-v5.h                 |  387 +++++++
 23 files changed, 4507 insertions(+), 69 deletions(-)
---
base-commit: 0af2f6be1b4281385b618cb86ad946eded089ac8
change-id: 20250408-gicv5-host-749f316afe84

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



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

* [PATCH v3 01/25] dt-bindings: interrupt-controller: Add Arm GICv5
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-06 19:08   ` Rob Herring
  2025-05-06 12:23 ` [PATCH v3 02/25] arm64/sysreg: Add GCIE field to ID_AA64PFR2_EL1 Lorenzo Pieralisi
                   ` (24 subsequent siblings)
  25 siblings, 1 reply; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, 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 a separate schema.

An IWB is connected to a single ITS, 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>
---
 .../interrupt-controller/arm,gic-v5-iwb.yaml       |  76 ++++++++
 .../bindings/interrupt-controller/arm,gic-v5.yaml  | 196 +++++++++++++++++++++
 MAINTAINERS                                        |   7 +
 3 files changed, 279 insertions(+)

diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5-iwb.yaml b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5-iwb.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..b3eb89567b3457e91b93588d7db1cef69b6b9813
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5-iwb.yaml
@@ -0,0 +1,76 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/interrupt-controller/arm,gic-v5-iwb.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ARM Generic Interrupt Controller, version 5 Interrupt Wire Bridge (IWB)
+
+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
+
+  GICv5 has zero or more Interrupt Wire Bridges (IWB) that are responsible
+  for translating wire signals into interrupt messages to the GICv5 ITS.
+
+allOf:
+  - $ref: /schemas/interrupt-controller.yaml#
+
+properties:
+  compatible:
+    const: arm,gic-v5-iwb
+
+  interrupt-controller: true
+
+  "#address-cells":
+    const: 0
+
+  "#interrupt-cells":
+    description: |
+      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
+
+    const: 2
+
+  reg:
+    items:
+      - description: IWB control frame
+
+  msi-parent:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - msi-parent
+
+additionalProperties: false
+
+examples:
+  - |
+    interrupt-controller@2f000000 {
+      compatible = "arm,gic-v5-iwb";
+      #address-cells = <0>;
+
+      interrupt-controller;
+      #interrupt-cells = <2>;
+
+      reg = <0x2f000000 0x10000>;
+
+      msi-parent = <&its0 64>;
+    };
+...
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..1ba0a2088e6d15bacae22c9fc9eebc4ce5c51b0b
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.yaml
@@ -0,0 +1,196 @@
+# 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)
+
+  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: |
+      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
+
+    const: 3
+
+  interrupts:
+    description:
+      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:
+        description:
+          CPUs managed by the IRS.
+
+      arm,iaffids:
+        $ref: /schemas/types.yaml#/definitions/uint16-array
+        description:
+          Interrupt AFFinity ID (IAFFID) 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
+
+additionalProperties: false
+
+examples:
+  - |
+    interrupt-controller {
+      compatible = "arm,gic-v5";
+      #interrupt-cells = <3>;
+      #address-cells = <1>;
+      #size-cells = <1>;
+      ranges;
+
+      interrupt-controller;
+
+      interrupts = <1 25 4>;
+
+      irs@2f1a0000 {
+        compatible = "arm,gic-v5-irs";
+        #address-cells = <1>;
+        #size-cells = <1>;
+        ranges;
+
+        reg = <0x2f1a0000 0x10000>;  // IRS_CONFIG_FRAME for NS
+
+        arm,iaffids = /bits/ 16 <0 1 2 3 4 5 6 7>;
+        cpus = <&cpu0>, <&cpu1>, <&cpu2>, <&cpu3>, <&cpu4>, <&cpu5>, <&cpu6>, <&cpu7>;
+
+        msi-controller@2f120000 {
+          compatible = "arm,gic-v5-its";
+
+          msi-controller;
+          #msi-cells = <1>;
+
+          reg = <0x2f120000 0x10000    // ITS_CONFIG_FRAME for NS
+                 0x2f130000 0x10000>;  // ITS_TRANSLATE_FRAME
+        };
+      };
+    };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 96b82704950184bd71623ff41fc4df31e4c7fe87..1902291c3cccc06d27c5f79123e67774cf9a0e43 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] 49+ messages in thread

* [PATCH v3 02/25] arm64/sysreg: Add GCIE field to ID_AA64PFR2_EL1
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
  2025-05-06 12:23 ` [PATCH v3 01/25] dt-bindings: interrupt-controller: Add Arm GICv5 Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-06 12:23 ` [PATCH v3 03/25] arm64/sysreg: Add ICC_PPI_PRIORITY<n>_EL1 Lorenzo Pieralisi
                   ` (23 subsequent siblings)
  25 siblings, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, 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] 49+ messages in thread

* [PATCH v3 03/25] arm64/sysreg: Add ICC_PPI_PRIORITY<n>_EL1
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
  2025-05-06 12:23 ` [PATCH v3 01/25] dt-bindings: interrupt-controller: Add Arm GICv5 Lorenzo Pieralisi
  2025-05-06 12:23 ` [PATCH v3 02/25] arm64/sysreg: Add GCIE field to ID_AA64PFR2_EL1 Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-06 12:23 ` [PATCH v3 04/25] arm64/sysreg: Add ICC_ICSR_EL1 Lorenzo Pieralisi
                   ` (22 subsequent siblings)
  25 siblings, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, 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] 49+ messages in thread

* [PATCH v3 04/25] arm64/sysreg: Add ICC_ICSR_EL1
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (2 preceding siblings ...)
  2025-05-06 12:23 ` [PATCH v3 03/25] arm64/sysreg: Add ICC_PPI_PRIORITY<n>_EL1 Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-06 12:23 ` [PATCH v3 05/25] arm64/sysreg: Add ICC_PPI_HMR<n>_EL1 Lorenzo Pieralisi
                   ` (21 subsequent siblings)
  25 siblings, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, 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] 49+ messages in thread

* [PATCH v3 05/25] arm64/sysreg: Add ICC_PPI_HMR<n>_EL1
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (3 preceding siblings ...)
  2025-05-06 12:23 ` [PATCH v3 04/25] arm64/sysreg: Add ICC_ICSR_EL1 Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-06 12:23 ` [PATCH v3 06/25] arm64/sysreg: Add ICC_PPI_ENABLER<n>_EL1 Lorenzo Pieralisi
                   ` (20 subsequent siblings)
  25 siblings, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, 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] 49+ messages in thread

* [PATCH v3 06/25] arm64/sysreg: Add ICC_PPI_ENABLER<n>_EL1
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (4 preceding siblings ...)
  2025-05-06 12:23 ` [PATCH v3 05/25] arm64/sysreg: Add ICC_PPI_HMR<n>_EL1 Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-06 12:23 ` [PATCH v3 07/25] arm64/sysreg: Add ICC_PPI_{C/S}ACTIVER<n>_EL1 Lorenzo Pieralisi
                   ` (19 subsequent siblings)
  25 siblings, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, 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] 49+ messages in thread

* [PATCH v3 07/25] arm64/sysreg: Add ICC_PPI_{C/S}ACTIVER<n>_EL1
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (5 preceding siblings ...)
  2025-05-06 12:23 ` [PATCH v3 06/25] arm64/sysreg: Add ICC_PPI_ENABLER<n>_EL1 Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-06 12:23 ` [PATCH v3 08/25] arm64/sysreg: Add ICC_PPI_{C/S}PENDR<n>_EL1 Lorenzo Pieralisi
                   ` (18 subsequent siblings)
  25 siblings, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, 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] 49+ messages in thread

* [PATCH v3 08/25] arm64/sysreg: Add ICC_PPI_{C/S}PENDR<n>_EL1
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (6 preceding siblings ...)
  2025-05-06 12:23 ` [PATCH v3 07/25] arm64/sysreg: Add ICC_PPI_{C/S}ACTIVER<n>_EL1 Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-06 12:23 ` [PATCH v3 09/25] arm64/sysreg: Add ICC_CR0_EL1 Lorenzo Pieralisi
                   ` (17 subsequent siblings)
  25 siblings, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, 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] 49+ messages in thread

* [PATCH v3 09/25] arm64/sysreg: Add ICC_CR0_EL1
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (7 preceding siblings ...)
  2025-05-06 12:23 ` [PATCH v3 08/25] arm64/sysreg: Add ICC_PPI_{C/S}PENDR<n>_EL1 Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-06 12:23 ` [PATCH v3 10/25] arm64/sysreg: Add ICC_PCR_EL1 Lorenzo Pieralisi
                   ` (16 subsequent siblings)
  25 siblings, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, 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] 49+ messages in thread

* [PATCH v3 10/25] arm64/sysreg: Add ICC_PCR_EL1
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (8 preceding siblings ...)
  2025-05-06 12:23 ` [PATCH v3 09/25] arm64/sysreg: Add ICC_CR0_EL1 Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-06 12:23 ` [PATCH v3 11/25] arm64/sysreg: Add ICC_IDR0_EL1 Lorenzo Pieralisi
                   ` (15 subsequent siblings)
  25 siblings, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, 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] 49+ messages in thread

* [PATCH v3 11/25] arm64/sysreg: Add ICC_IDR0_EL1
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (9 preceding siblings ...)
  2025-05-06 12:23 ` [PATCH v3 10/25] arm64/sysreg: Add ICC_PCR_EL1 Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-06 12:23 ` [PATCH v3 12/25] arm64/sysreg: Add ICH_HFGRTR_EL2 Lorenzo Pieralisi
                   ` (14 subsequent siblings)
  25 siblings, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, 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] 49+ messages in thread

* [PATCH v3 12/25] arm64/sysreg: Add ICH_HFGRTR_EL2
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (10 preceding siblings ...)
  2025-05-06 12:23 ` [PATCH v3 11/25] arm64/sysreg: Add ICC_IDR0_EL1 Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-06 12:23 ` [PATCH v3 13/25] arm64/sysreg: Add ICH_HFGWTR_EL2 Lorenzo Pieralisi
                   ` (13 subsequent siblings)
  25 siblings, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, 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] 49+ messages in thread

* [PATCH v3 13/25] arm64/sysreg: Add ICH_HFGWTR_EL2
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (11 preceding siblings ...)
  2025-05-06 12:23 ` [PATCH v3 12/25] arm64/sysreg: Add ICH_HFGRTR_EL2 Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-06 12:23 ` [PATCH v3 14/25] arm64/sysreg: Add ICH_HFGITR_EL2 Lorenzo Pieralisi
                   ` (12 subsequent siblings)
  25 siblings, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, 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] 49+ messages in thread

* [PATCH v3 14/25] arm64/sysreg: Add ICH_HFGITR_EL2
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (12 preceding siblings ...)
  2025-05-06 12:23 ` [PATCH v3 13/25] arm64/sysreg: Add ICH_HFGWTR_EL2 Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-06 12:23 ` [PATCH v3 15/25] arm64: Disable GICv5 read/write/instruction traps Lorenzo Pieralisi
                   ` (11 subsequent siblings)
  25 siblings, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, 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] 49+ messages in thread

* [PATCH v3 15/25] arm64: Disable GICv5 read/write/instruction traps
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (13 preceding siblings ...)
  2025-05-06 12:23 ` [PATCH v3 14/25] arm64/sysreg: Add ICH_HFGITR_EL2 Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-06 12:23 ` [PATCH v3 16/25] arm64: cpucaps: Rename GICv3 CPU interface capability Lorenzo Pieralisi
                   ` (10 subsequent siblings)
  25 siblings, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, 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..109b72b657d2fbb6d39e0446c10d1b62e0a780b3 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, #(ICH_HFGITR_EL2_GICRCDNMIA		| \
+		      ICH_HFGITR_EL2_GICRCDIA		| \
+		      ICH_HFGITR_EL2_GICCDDI		| \
+		      ICH_HFGITR_EL2_GICCDEOI		| \
+		      ICH_HFGITR_EL2_GICCDHM		| \
+		      ICH_HFGITR_EL2_GICCRDRCFG		| \
+		      ICH_HFGITR_EL2_GICCDPEND		| \
+		      ICH_HFGITR_EL2_GICCDAFF		| \
+		      ICH_HFGITR_EL2_GICCDPRI		| \
+		      ICH_HFGITR_EL2_GICCDDIS		| \
+		      ICH_HFGITR_EL2_GICCDEN)
+	msr_s	SYS_ICH_HFGITR_EL2, x0		// Disable instruction traps
+	mov_q	x0, (ICH_HFGRTR_EL2_ICC_PPI_ACTIVERn_EL1	| \
+		     ICH_HFGRTR_EL2_ICC_PPI_PRIORITYRn_EL1	| \
+		     ICH_HFGRTR_EL2_ICC_PPI_PENDRn_EL1		| \
+		     ICH_HFGRTR_EL2_ICC_PPI_ENABLERn_EL1	| \
+		     ICH_HFGRTR_EL2_ICC_PPI_HMRn_EL1		| \
+		     ICH_HFGRTR_EL2_ICC_IAFFIDR_EL1		| \
+		     ICH_HFGRTR_EL2_ICC_ICSR_EL1		| \
+		     ICH_HFGRTR_EL2_ICC_PCR_EL1			| \
+		     ICH_HFGRTR_EL2_ICC_HPPIR_EL1		| \
+		     ICH_HFGRTR_EL2_ICC_HAPR_EL1		| \
+		     ICH_HFGRTR_EL2_ICC_CR0_EL1			| \
+		     ICH_HFGRTR_EL2_ICC_IDRn_EL1		| \
+		     ICH_HFGRTR_EL2_ICC_APR_EL1)
+	msr_s	SYS_ICH_HFGRTR_EL2, x0		// Disable reg read traps
+	mov_q	x0, (ICH_HFGWTR_EL2_ICC_PPI_ACTIVERn_EL1	| \
+		     ICH_HFGWTR_EL2_ICC_PPI_PRIORITYRn_EL1	| \
+		     ICH_HFGWTR_EL2_ICC_PPI_PENDRn_EL1		| \
+		     ICH_HFGWTR_EL2_ICC_PPI_ENABLERn_EL1	| \
+		     ICH_HFGWTR_EL2_ICC_ICSR_EL1		| \
+		     ICH_HFGWTR_EL2_ICC_PCR_EL1			| \
+		     ICH_HFGWTR_EL2_ICC_CR0_EL1			| \
+		     ICH_HFGWTR_EL2_ICC_APR_EL1)
+	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] 49+ messages in thread

* [PATCH v3 16/25] arm64: cpucaps: Rename GICv3 CPU interface capability
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (14 preceding siblings ...)
  2025-05-06 12:23 ` [PATCH v3 15/25] arm64: Disable GICv5 read/write/instruction traps Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-06 12:23 ` [PATCH v3 17/25] arm64: cpucaps: Add GICv5 CPU interface (GCIE) capability Lorenzo Pieralisi
                   ` (9 subsequent siblings)
  25 siblings, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

In preparation for adding a GICv5 CPU interface capability,
rework the existing GICv3 CPUIF capability - change its name and
description so that the subsequent GICv5 CPUIF capability
can be added with a more consistent naming on top.

Suggested-by: Mark Rutland <mark.rutland@arm.com>
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 | 10 +++++-----
 arch/arm64/tools/cpucaps       |  2 +-
 drivers/irqchip/irq-gic.c      |  2 +-
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 9c4d6d552b25cb3a31d1fb267bd73d3f82513e69..cbb49de451f45fbee3100ea01e77b06352bd55ac 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -2283,11 +2283,11 @@ static bool can_use_gic_priorities(const struct arm64_cpu_capabilities *entry,
 				   int scope)
 {
 	/*
-	 * ARM64_HAS_GIC_CPUIF_SYSREGS has a lower index, and is a boot CPU
+	 * ARM64_HAS_GICV3_CPUIF has a lower index, and is a boot CPU
 	 * feature, so will be detected earlier.
 	 */
-	BUILD_BUG_ON(ARM64_HAS_GIC_PRIO_MASKING <= ARM64_HAS_GIC_CPUIF_SYSREGS);
-	if (!cpus_have_cap(ARM64_HAS_GIC_CPUIF_SYSREGS))
+	BUILD_BUG_ON(ARM64_HAS_GIC_PRIO_MASKING <= ARM64_HAS_GICV3_CPUIF);
+	if (!cpus_have_cap(ARM64_HAS_GICV3_CPUIF))
 		return false;
 
 	return enable_pseudo_nmi;
@@ -2483,8 +2483,8 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
 		.matches = has_always,
 	},
 	{
-		.desc = "GIC system register CPU interface",
-		.capability = ARM64_HAS_GIC_CPUIF_SYSREGS,
+		.desc = "GICv3 CPU interface",
+		.capability = ARM64_HAS_GICV3_CPUIF,
 		.type = ARM64_CPUCAP_STRICT_BOOT_CPU_FEATURE,
 		.matches = has_useable_gicv3_cpuif,
 		ARM64_CPUID_FIELDS(ID_AA64PFR0_EL1, GIC, IMP)
diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps
index 772c1b008e437ed34cedb1c0f663c4dcea8f6759..860ec49cc0530885c138b7dc7f67d58cd69b2593 100644
--- a/arch/arm64/tools/cpucaps
+++ b/arch/arm64/tools/cpucaps
@@ -34,7 +34,7 @@ HAS_GENERIC_AUTH
 HAS_GENERIC_AUTH_ARCH_QARMA3
 HAS_GENERIC_AUTH_ARCH_QARMA5
 HAS_GENERIC_AUTH_IMP_DEF
-HAS_GIC_CPUIF_SYSREGS
+HAS_GICV3_CPUIF
 HAS_GIC_PRIO_MASKING
 HAS_GIC_PRIO_RELAXED_SYNC
 HAS_HCR_NV1
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 6503573557fdf295bc543b16b64e3e7dd6841321..1269ab8eb726afbb80849fd062612861680cb4d1 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -54,7 +54,7 @@
 
 static void gic_check_cpu_features(void)
 {
-	WARN_TAINT_ONCE(this_cpu_has_cap(ARM64_HAS_GIC_CPUIF_SYSREGS),
+	WARN_TAINT_ONCE(this_cpu_has_cap(ARM64_HAS_GICV3_CPUIF),
 			TAINT_CPU_OUT_OF_SPEC,
 			"GICv3 system registers enabled, broken firmware!\n");
 }

-- 
2.48.0



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

* [PATCH v3 17/25] arm64: cpucaps: Add GICv5 CPU interface (GCIE) capability
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (15 preceding siblings ...)
  2025-05-06 12:23 ` [PATCH v3 16/25] arm64: cpucaps: Rename GICv3 CPU interface capability Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-06 12:23 ` [PATCH v3 18/25] arm64: smp: Support non-SGIs for IPIs Lorenzo Pieralisi
                   ` (8 subsequent siblings)
  25 siblings, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, 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.

Plug it in with a naming consistent with the existing GICv3
CPU interface capability.

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 cbb49de451f45fbee3100ea01e77b06352bd55ac..4d5163a20ee0fb09380ea5f1f2d37afb7257edfb 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 = "GICv5 CPU interface",
+		.type = ARM64_CPUCAP_STRICT_BOOT_CPU_FEATURE,
+		.capability = ARM64_HAS_GICV5_CPUIF,
+		.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 860ec49cc0530885c138b7dc7f67d58cd69b2593..c36f4165e2bb460abde81baf453199f62dd265b0 100644
--- a/arch/arm64/tools/cpucaps
+++ b/arch/arm64/tools/cpucaps
@@ -35,6 +35,7 @@ HAS_GENERIC_AUTH_ARCH_QARMA3
 HAS_GENERIC_AUTH_ARCH_QARMA5
 HAS_GENERIC_AUTH_IMP_DEF
 HAS_GICV3_CPUIF
+HAS_GICV5_CPUIF
 HAS_GIC_PRIO_MASKING
 HAS_GIC_PRIO_RELAXED_SYNC
 HAS_HCR_NV1

-- 
2.48.0



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

* [PATCH v3 18/25] arm64: smp: Support non-SGIs for IPIs
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (16 preceding siblings ...)
  2025-05-06 12:23 ` [PATCH v3 17/25] arm64: cpucaps: Add GICv5 CPU interface (GCIE) capability Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-06 12:23 ` [PATCH v3 19/25] arm64: Add support for GICv5 GSB barriers Lorenzo Pieralisi
                   ` (7 subsequent siblings)
  25 siblings, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, 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] 49+ messages in thread

* [PATCH v3 19/25] arm64: Add support for GICv5 GSB barriers
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (17 preceding siblings ...)
  2025-05-06 12:23 ` [PATCH v3 18/25] arm64: smp: Support non-SGIs for IPIs Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-06 12:23 ` [PATCH v3 20/25] irqchip/gic-v5: Add GICv5 PPI support Lorenzo Pieralisi
                   ` (6 subsequent siblings)
  25 siblings, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

The GICv5 architecture introduces two barriers instructions
(GSB SYS, GSB ACK) that are used to manage interrupt effects.

Rework macro used to emit the SB barrier instruction and implement
the GSB barriers on top of it.

Suggested-by: Marc Zyngier <maz@kernel.org>
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/barrier.h |  3 +++
 arch/arm64/include/asm/sysreg.h  | 10 +++++++---
 2 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/include/asm/barrier.h b/arch/arm64/include/asm/barrier.h
index 1ca947d5c93963d33fe8fb02d6037fc71bd9fd7a..f5801b0ba9e9e7e0433f16ffedf0ec7dfb3e358e 100644
--- a/arch/arm64/include/asm/barrier.h
+++ b/arch/arm64/include/asm/barrier.h
@@ -44,6 +44,9 @@
 						 SB_BARRIER_INSN"nop\n",	\
 						 ARM64_HAS_SB))
 
+#define gsb_ack()	asm volatile(GSB_ACK_BARRIER_INSN : : : "memory")
+#define gsb_sys()	asm volatile(GSB_SYS_BARRIER_INSN : : : "memory")
+
 #ifdef CONFIG_ARM64_PSEUDO_NMI
 #define pmr_sync()						\
 	do {							\
diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
index 2639d3633073de10f5040a7efff059021f847530..e7734f90bb723bfbd8be99f16dd6d6fdc7fa57e8 100644
--- a/arch/arm64/include/asm/sysreg.h
+++ b/arch/arm64/include/asm/sysreg.h
@@ -112,10 +112,14 @@
 /* Register-based PAN access, for save/restore purposes */
 #define SYS_PSTATE_PAN			sys_reg(3, 0, 4, 2, 3)
 
-#define __SYS_BARRIER_INSN(CRm, op2, Rt) \
-	__emit_inst(0xd5000000 | sys_insn(0, 3, 3, (CRm), (op2)) | ((Rt) & 0x1f))
+#define __SYS_BARRIER_INSN(op0, op1, CRn, CRm, op2, Rt)		\
+	__emit_inst(0xd5000000				|	\
+	sys_insn((op0), (op1), (CRn), (CRm), (op2))	|	\
+	((Rt) & 0x1f))
 
-#define SB_BARRIER_INSN			__SYS_BARRIER_INSN(0, 7, 31)
+#define SB_BARRIER_INSN			__SYS_BARRIER_INSN(0, 3, 3, 0, 7, 31)
+#define GSB_SYS_BARRIER_INSN		__SYS_BARRIER_INSN(1, 0, 12, 0, 0, 31)
+#define GSB_ACK_BARRIER_INSN		__SYS_BARRIER_INSN(1, 0, 12, 0, 1, 31)
 
 #define SYS_DC_ISW			sys_insn(1, 0, 7, 6, 2)
 #define SYS_DC_IGSW			sys_insn(1, 0, 7, 6, 4)

-- 
2.48.0



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

* [PATCH v3 20/25] irqchip/gic-v5: Add GICv5 PPI support
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (18 preceding siblings ...)
  2025-05-06 12:23 ` [PATCH v3 19/25] arm64: Add support for GICv5 GSB barriers Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-06 15:00   ` Thomas Gleixner
  2025-05-06 12:23 ` [PATCH v3 21/25] irqchip/gic-v5: Add GICv5 IRS/SPI support Lorenzo Pieralisi
                   ` (5 subsequent siblings)
  25 siblings, 1 reply; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, 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/sysreg.h    |  23 ++
 drivers/irqchip/Kconfig            |   5 +
 drivers/irqchip/Makefile           |   1 +
 drivers/irqchip/irq-gic-v5.c       | 454 +++++++++++++++++++++++++++++++++++++
 include/linux/irqchip/arm-gic-v5.h |  19 ++
 6 files changed, 504 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 1902291c3cccc06d27c5f79123e67774cf9a0e43..49c377febad72e77dd6d480105c2b6bffa81f9a6 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:	drivers/irqchip/irq-gic-v5*.[ch]
+F:	include/linux/irqchip/arm-gic-v5.h
 
 ARM HDLCD DRM DRIVER
 M:	Liviu Dudau <liviu.dudau@arm.com>
diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
index e7734f90bb723bfbd8be99f16dd6d6fdc7fa57e8..4b48d00842c5750299f2983ecd72f19f2865c0e3 100644
--- a/arch/arm64/include/asm/sysreg.h
+++ b/arch/arm64/include/asm/sysreg.h
@@ -1079,6 +1079,29 @@
 
 #define GCS_CAP(x)	((((unsigned long)x) & GCS_CAP_ADDR_MASK) | \
 					       GCS_CAP_VALID_TOKEN)
+/*
+ * Definitions for GICv5 instructions
+ */
+#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)
+
+/* 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)
+
+#define gicr_insn(insn)			read_sysreg_s(GICV5_OP_GICR_##insn)
+#define gic_insn(v, insn)		write_sysreg_s(v, GICV5_OP_GIC_##insn)
 
 #define ARM64_FEATURE_FIELD_BITS	4
 
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index cec05e443083b8982b3e72f4212d808a22883914..6b3d70924186bd8ca04294832409d1e379c9cbd4 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
+
 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..29915a82e798211b61b611ed3ad457047b299298
--- /dev/null
+++ b/drivers/irqchip/irq-gic-v5.c
@@ -0,0 +1,454 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024-2025 ARM Limited, All Rights Reserved.
+ */
+
+#define pr_fmt(fmt)	"GICv5: " fmt
+
+#include <linux/irqdomain.h>
+#include <linux/wordpart.h>
+
+#include <linux/irqchip.h>
+#include <linux/irqchip/arm-gic-v5.h>
+
+#include <asm/cpufeature.h>
+#include <asm/exception.h>
+
+static u8 pri_bits = 5;
+#define GICV5_IRQ_PRI_MASK 0x1f
+#define GICV5_IRQ_PRI_MI \
+		(GICV5_IRQ_PRI_MASK & GENMASK(4, 5 - pri_bits))
+
+#define PPI_NR	128
+
+static bool gicv5_cpuif_has_gcie(void)
+{
+	return this_cpu_has_cap(ARM64_HAS_GICV5_CPUIF);
+}
+
+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_PRI_MI), SYS_ICC_PPI_PRIORITYR0_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRI_MI), SYS_ICC_PPI_PRIORITYR1_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRI_MI), SYS_ICC_PPI_PRIORITYR2_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRI_MI), SYS_ICC_PPI_PRIORITYR3_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRI_MI), SYS_ICC_PPI_PRIORITYR4_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRI_MI), SYS_ICC_PPI_PRIORITYR5_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRI_MI), SYS_ICC_PPI_PRIORITYR6_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRI_MI), SYS_ICC_PPI_PRIORITYR7_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRI_MI), SYS_ICC_PPI_PRIORITYR8_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRI_MI), SYS_ICC_PPI_PRIORITYR9_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRI_MI), SYS_ICC_PPI_PRIORITYR10_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRI_MI), SYS_ICC_PPI_PRIORITYR11_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRI_MI), SYS_ICC_PPI_PRIORITYR12_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRI_MI), SYS_ICC_PPI_PRIORITYR13_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRI_MI), SYS_ICC_PPI_PRIORITYR14_EL1);
+	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRI_MI), SYS_ICC_PPI_PRIORITYR15_EL1);
+
+	/*
+	 * Context syncronization required to make sure system register writes
+	 * effects are synchronised.
+	 */
+	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);
+
+	/*
+	 * We must ensure that the disable takes effect immediately to
+	 * guarantee that the lazy-disabled IRQ mechanism works.
+	 * A context synchronization event is required to guarantee it.
+	 * Reference: I_ZLTKB/R_YRGMH GICv5 specification - section 2.9.1.
+	 */
+	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);
+	/*
+	 * We must ensure that the enable takes effect in finite time - a
+	 * context synchronization event is required to guarantee it, we
+	 * can not take for granted that would happen (eg a core going straight
+	 * into idle after enabling a PPI).
+	 * Reference: I_ZLTKB/R_YRGMH GICv5 specification - section 2.9.1.
+	 */
+	isb();
+}
+
+static void gicv5_hwirq_eoi(u32 hwirq_id, u8 hwirq_type)
+{
+	u64 cddi = hwirq_id | FIELD_PREP(GICV5_GIC_CDDI_TYPE_MASK, hwirq_type);
+
+	gic_insn(cddi, CDDI);
+
+	gic_insn(0, CDEOI);
+}
+
+static void gicv5_ppi_irq_eoi(struct irq_data *d)
+{
+	gicv5_hwirq_eoi(d->hwirq, GICV5_HWIRQ_TYPE_PPI);
+}
+
+#define READ_PPI_REG(irq, reg)							\
+	({									\
+		u64 __ppi_val;							\
+										\
+		if (irq < 64)							\
+			__ppi_val = read_sysreg_s(SYS_ICC_PPI_##reg##R0_EL1);	\
+		else								\
+			__ppi_val = read_sysreg_s(SYS_ICC_PPI_##reg##R1_EL1);	\
+		__ppi_val;							\
+	})
+
+#define WRITE_PPI_REG(set, irq, bit, reg)					\
+	do {									\
+		if (set) {							\
+			if (irq < 64)						\
+				write_sysreg_s(bit, SYS_ICC_PPI_S##reg##R0_EL1);\
+			else							\
+				write_sysreg_s(bit, SYS_ICC_PPI_S##reg##R1_EL1);\
+		} else {							\
+			if (irq < 64)						\
+				write_sysreg_s(bit, SYS_ICC_PPI_C##reg##R0_EL1);\
+			else							\
+				write_sysreg_s(bit, SYS_ICC_PPI_C##reg##R1_EL1);\
+		}								\
+	} while (0)
+
+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 = READ_PPI_REG(d->hwirq, HM);
+
+	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, hwirq_id_bit = BIT_ULL(d->hwirq % 64);
+
+	switch (which) {
+	case IRQCHIP_STATE_PENDING:
+		pendr = READ_PPI_REG(d->hwirq, SPEND);
+
+		*val = !!(pendr & hwirq_id_bit);
+
+		return 0;
+	case IRQCHIP_STATE_ACTIVE:
+		activer = READ_PPI_REG(d->hwirq, SACTIVE);
+
+		*val = !!(activer & hwirq_id_bit);
+
+		return 0;
+	default:
+		pr_debug("Unexpected PPI irqchip state\n");
+	}
+
+	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:
+		WRITE_PPI_REG(val, d->hwirq, hwirq_id_bit, PEND);
+		return 0;
+	case IRQCHIP_STATE_ACTIVE:
+		WRITE_PPI_REG(val, d->hwirq, hwirq_id_bit, ACTIVE);
+		return 0;
+	default:
+		pr_debug("Unexpected PPI irqchip state\n");
+	}
+
+	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))
+		return -EINVAL;
+
+	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;
+}
+
+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_ONCE(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_domain_free(struct irq_domain *domain, unsigned int virq,
+				  unsigned int nr_irqs)
+{
+	struct irq_data *d;
+
+	if (WARN_ON_ONCE(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)
+{
+	if (fwspec->fwnode != d->fwnode)
+		return 0;
+
+	if (fwspec->param[0] != GICV5_HWIRQ_TYPE_PPI)
+		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_domain_free,
+	.select		= gicv5_irq_ppi_domain_select
+};
+
+static void handle_irq_per_domain(u32 hwirq)
+{
+	u8 hwirq_type = FIELD_GET(GICV5_HWIRQ_TYPE, hwirq);
+	u32 hwirq_id = FIELD_GET(GICV5_HWIRQ_ID, hwirq);
+	struct irq_domain *domain;
+
+	switch (hwirq_type) {
+	case GICV5_HWIRQ_TYPE_PPI:
+		domain = gicv5_global_data.ppi_domain;
+		break;
+	default:
+		pr_err_once("Unknown IRQ type, bail out\n");
+		return;
+	}
+
+	if (generic_handle_domain_irq(domain, hwirq_id)) {
+		pr_err_once("Could not handle, hwirq = 0x%x", hwirq_id);
+		gicv5_hwirq_eoi(hwirq_id, hwirq_type);
+	}
+}
+
+static void __exception_irq_entry gicv5_handle_irq(struct pt_regs *regs)
+{
+	bool valid;
+	u32 hwirq;
+	u64 ia;
+
+	ia = gicr_insn(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);
+}
+
+static void gicv5_cpu_disable_interrupts(void)
+{
+	u64 cr0;
+
+	cr0 = FIELD_PREP(ICC_CR0_EL1_EN, 0);
+	write_sysreg_s(cr0, SYS_ICC_CR0_EL1);
+}
+
+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();
+
+	pcr = FIELD_PREP(ICC_PCR_EL1_PRIORITY, GICV5_IRQ_PRI_MI);
+	write_sysreg_s(pcr, SYS_ICC_PCR_EL1);
+
+	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);
+
+	gicv5_global_data.ppi_domain = NULL;
+}
+
+static int __init gicv5_init_domains(struct fwnode_handle *handle)
+{
+	struct irq_domain *d;
+
+	d = irq_domain_create_linear(handle, PPI_NR, &gicv5_irq_ppi_domain_ops,
+				     NULL);
+	if (!d)
+		return -ENOMEM;
+
+	irq_domain_update_bus_token(d, DOMAIN_BUS_WIRED);
+	gicv5_global_data.ppi_domain = d;
+
+	gicv5_global_data.fwnode = handle;
+
+	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)
+		goto out_dom;
+
+	ret = set_handle_irq(gicv5_handle_irq);
+	if (ret)
+		goto out_int;
+
+	return 0;
+out_int:
+	gicv5_cpu_disable_interrupts();
+out_dom:
+	gicv5_free_domains();
+
+	return ret;
+}
+IRQCHIP_DECLARE(gic_v5, "arm,gic-v5", gicv5_of_init);
diff --git a/include/linux/irqchip/arm-gic-v5.h b/include/linux/irqchip/arm-gic-v5.h
new file mode 100644
index 0000000000000000000000000000000000000000..cc3da79e615b2b6ee27456e98c17061e4508030f
--- /dev/null
+++ b/include/linux/irqchip/arm-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_ARM_GIC_V5_H
+#define __LINUX_IRQCHIP_ARM_GIC_V5_H
+
+#include <asm/sysreg.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] 49+ messages in thread

* [PATCH v3 21/25] irqchip/gic-v5: Add GICv5 IRS/SPI support
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (19 preceding siblings ...)
  2025-05-06 12:23 ` [PATCH v3 20/25] irqchip/gic-v5: Add GICv5 PPI support Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-06 12:23 ` [PATCH v3 22/25] irqchip/gic-v5: Add GICv5 LPI/IPI support Lorenzo Pieralisi
                   ` (4 subsequent siblings)
  25 siblings, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, 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/sysreg.h    |  50 +++++
 drivers/irqchip/Makefile           |   2 +-
 drivers/irqchip/irq-gic-v5-irs.c   | 433 +++++++++++++++++++++++++++++++++++++
 drivers/irqchip/irq-gic-v5.c       | 317 +++++++++++++++++++++++++--
 include/linux/irqchip/arm-gic-v5.h | 130 +++++++++++
 5 files changed, 917 insertions(+), 15 deletions(-)

diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
index 4b48d00842c5750299f2983ecd72f19f2865c0e3..c735721cf083e8b59e88bd5888fa2519f40cc51e 100644
--- a/arch/arm64/include/asm/sysreg.h
+++ b/arch/arm64/include/asm/sysreg.h
@@ -1082,16 +1082,66 @@
 /*
  * Definitions for GICv5 instructions
  */
+#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)
 
+/* 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)
 #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 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..e4e0e90993f4b3e901b14d379e219131e634aa75
--- /dev/null
+++ b/drivers/irqchip/irq-gic-v5-irs.c
@@ -0,0 +1,433 @@
+// 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/of.h>
+#include <linux/of_address.h>
+
+#include <linux/irqchip.h>
+#include <linux/irqchip/arm-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);
+static LIST_HEAD(irs_nodes);
+
+static u32 irs_readl_relaxed(struct gicv5_irs_chip_data *irs_data,
+			     const u32 reg_offset)
+{
+	return readl_relaxed(irs_data->irs_base + reg_offset);
+}
+
+static void irs_writel_relaxed(struct gicv5_irs_chip_data *irs_data,
+			       const u32 val, const u32 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)
+{
+	u32 statusr;
+	int ret;
+
+	ret = gicv5_wait_for_op_atomic(irs_data->irs_base, GICV5_IRS_SPI_STATUSR,
+				       GICV5_IRS_SPI_STATUSR_IDLE, &statusr);
+	if (ret)
+		return ret;
+
+	return !!FIELD_GET(GICV5_IRS_SPI_STATUSR_V, statusr) ? 0 : -EIO;
+}
+
+static int gicv5_irs_wait_for_irs_pe(struct gicv5_irs_chip_data *irs_data,
+				     bool selr)
+{
+	bool valid = true;
+	u32 statusr;
+	int ret;
+
+	ret = gicv5_wait_for_op_atomic(irs_data->irs_base, GICV5_IRS_PE_STATUSR,
+				       GICV5_IRS_PE_STATUSR_IDLE, &statusr);
+	if (ret)
+		return ret;
+
+	if (selr)
+		valid = !!FIELD_GET(GICV5_IRS_PE_STATUSR_V, statusr);
+
+	return valid ? 0 : -EIO;
+}
+
+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;
+	int ret;
+
+	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_relaxed(irs_data, selr, GICV5_IRS_SPI_SELR);
+	ret = gicv5_irs_wait_for_spi_op(irs_data);
+	if (ret)
+		return ret;
+
+	cfgr = FIELD_PREP(GICV5_IRS_SPI_CFGR_TM, level);
+
+	irs_writel_relaxed(irs_data, cfgr, GICV5_IRS_SPI_CFGR);
+	ret = gicv5_irs_wait_for_spi_op(irs_data);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int gicv5_irs_wait_for_idle(struct gicv5_irs_chip_data *irs_data)
+{
+	return gicv5_wait_for_op_atomic(irs_data->irs_base, GICV5_IRS_CR0,
+					GICV5_IRS_CR0_IDLE, NULL);
+}
+
+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_relaxed(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_relaxed(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 void __init gicv5_irs_init_bases(struct gicv5_irs_chip_data *irs_data,
+					void __iomem *irs_base,
+					struct fwnode_handle *handle)
+{
+	struct device_node *np = to_of_node(handle);
+	u32 cr0, cr1;
+
+	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_relaxed(irs_data, cr1, GICV5_IRS_CR1);
+
+	cr0 = FIELD_PREP(GICV5_IRS_CR0_IRSEN, 0x1);
+	irs_writel_relaxed(irs_data, cr0, GICV5_IRS_CR0);
+	gicv5_irs_wait_for_idle(irs_data);
+}
+
+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.
+	 */
+	u16 iaffid_mask = GENMASK(iaffid_bits - 1, 0);
+	u16 *iaffids __free(kfree) = NULL;
+	int ret, i, ncpus, niaffids;
+
+	ncpus = of_property_count_elems_of_size(node, "cpus", sizeof(u32));
+	if (ncpus < 0)
+		return -EINVAL;
+
+	niaffids = of_property_count_elems_of_size(node, "arm,iaffids",
+						   sizeof(u16));
+	if (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;
+	}
+
+	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));
+		gicv5_global_data.irs_pri_bits = 1;
+		break;
+	}
+}
+
+static int __init gicv5_irs_init(struct device_node *node)
+{
+	struct gicv5_irs_chip_data *irs_data;
+	void __iomem *irs_base;
+	u32 idr, spi_count;
+	u8 iaffid_bits;
+	int ret;
+
+	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_relaxed(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_relaxed(irs_data, GICV5_IRS_IDR7);
+	irs_data->spi_min = FIELD_GET(GICV5_IRS_IDR7_SPI_BASE, idr);
+
+	idr = irs_readl_relaxed(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_relaxed(irs_data, GICV5_IRS_IDR1);
+		irs_setup_pri_bits(idr);
+
+		idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR5);
+
+		spi_count = FIELD_GET(GICV5_IRS_IDR5_SPI_RANGE, idr);
+		gicv5_global_data.global_spi_count = spi_count;
+
+		pr_debug("Detected %u SPIs globally\n", 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)
+{
+	struct device_node *np;
+	int ret;
+
+	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 29915a82e798211b61b611ed3ad457047b299298..e7ff82628ee67f368ea64e5e85e4474e6b82ef62 100644
--- a/drivers/irqchip/irq-gic-v5.c
+++ b/drivers/irqchip/irq-gic-v5.c
@@ -26,12 +26,7 @@ static bool gicv5_cpuif_has_gcie(void)
 	return this_cpu_has_cap(ARM64_HAS_GICV5_CPUIF);
 }
 
-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)
 {
@@ -59,6 +54,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, CDPRI);
+
+		ret = gicv5_irs_cpu_to_iaffid(smp_processor_id(), &iaffid);
+
+		if (WARN_ON_ONCE(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, CDAFF);
+	}
+}
+
 static void gicv5_ppi_irq_mask(struct irq_data *d)
 {
 	u64 hwirq_id_bit = BIT_ULL(d->hwirq % 64);
@@ -77,6 +96,29 @@ 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, CDDIS);
+	/*
+	 * We must make sure that GIC CDDIS write effects are propagated
+	 * immediately to make sure the disable takes effect to guarantee
+	 * that the lazy-disabled IRQ mechanism works.
+	 * Rule R_XCLJC states that the effects of a GIC system instruction
+	 * complete in finite time.
+	 * The GSB ensures completion of the GIC instruction and prevents
+	 * loads, stores and GIC instructions from executing part of their
+	 * functionality before the GSB SYS.
+	 */
+	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);
@@ -95,6 +137,22 @@ static void gicv5_ppi_irq_unmask(struct irq_data *d)
 	isb();
 }
 
+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);
+	/*
+	 * Rule R_XCLJC states that the effects of a GIC system instruction
+	 * complete in finite time and that's the only requirement when
+	 * unmasking an SPI IRQ.
+	 */
+	gic_insn(cden, 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);
@@ -109,6 +167,46 @@ 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_iri_irq_set_affinity(struct irq_data *d,
+				      const struct cpumask *mask_val,
+				      bool force, u8 hwirq_type)
+{
+	int ret, cpuid;
+	u16 iaffid;
+	u64 cdaff;
+
+	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, 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);
+}
+
 #define READ_PPI_REG(irq, reg)							\
 	({									\
 		u64 __ppi_val;							\
@@ -189,6 +287,47 @@ 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;
+
+	cdrcfg = d->hwirq | FIELD_PREP(GICV5_GIC_CDRCFG_TYPE_MASK, hwirq_type);
+
+	gic_insn(cdrcfg, CDRCFG);
+	isb();
+	icsr = read_sysreg_s(SYS_ICC_ICSR_EL1);
+
+	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;
+
+	default:
+		pr_debug("Unexpected irqchip_irq_state\n");
+	}
+
+	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)
@@ -209,6 +348,45 @@ 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, 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;
+	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,
@@ -222,10 +400,26 @@ static const struct irq_chip gicv5_ppi_irq_chip = {
 				  IRQCHIP_MASK_ON_SUSPEND
 };
 
-static int gicv5_irq_ppi_domain_translate(struct irq_domain *d,
+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_domain_translate(struct irq_domain *d,
 				      struct irq_fwspec *fwspec,
 				      irq_hw_number_t *hwirq,
-				      unsigned int *type)
+				      unsigned int *type,
+				      u8 hwirq_type)
 {
 	if (!is_of_node(fwspec->fwnode))
 		return -EINVAL;
@@ -233,7 +427,7 @@ static int gicv5_irq_ppi_domain_translate(struct irq_domain *d,
 	if (fwspec->param_count < 3)
 		return -EINVAL;
 
-	if (fwspec->param[0] != GICV5_HWIRQ_TYPE_PPI)
+	if (fwspec->param[0] != hwirq_type)
 		return -EINVAL;
 
 	*hwirq = fwspec->param[1];
@@ -242,6 +436,15 @@ static int gicv5_irq_ppi_domain_translate(struct irq_domain *d,
 	return 0;
 }
 
+static int gicv5_irq_ppi_domain_translate(struct irq_domain *d,
+					  struct irq_fwspec *fwspec,
+					  irq_hw_number_t *hwirq,
+					  unsigned int *type)
+{
+	return gicv5_irq_domain_translate(d, fwspec, hwirq, type,
+					  GICV5_HWIRQ_TYPE_PPI);
+}
+
 static int gicv5_irq_ppi_domain_alloc(struct irq_domain *domain, unsigned int virq,
 				      unsigned int nr_irqs, void *arg)
 {
@@ -297,6 +500,63 @@ static const struct irq_domain_ops gicv5_irq_ppi_domain_ops = {
 	.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)
+{
+	return gicv5_irq_domain_translate(d, fwspec, hwirq, type,
+					  GICV5_HWIRQ_TYPE_SPI);
+}
+
+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;
+	unsigned int type = IRQ_TYPE_NONE;
+	struct irq_fwspec *fwspec = arg;
+	struct irq_data *irqd;
+	irq_hw_number_t hwirq;
+	int ret;
+
+	if (WARN_ON_ONCE(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_PRI_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)
+		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 void handle_irq_per_domain(u32 hwirq)
 {
 	u8 hwirq_type = FIELD_GET(GICV5_HWIRQ_TYPE, hwirq);
@@ -307,6 +567,9 @@ static void handle_irq_per_domain(u32 hwirq)
 	case GICV5_HWIRQ_TYPE_PPI:
 		domain = gicv5_global_data.ppi_domain;
 		break;
+	case GICV5_HWIRQ_TYPE_SPI:
+		domain = gicv5_global_data.spi_domain;
+		break;
 	default:
 		pr_err_once("Unknown IRQ type, bail out\n");
 		return;
@@ -379,19 +642,23 @@ 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);
 
 	gicv5_global_data.ppi_domain = NULL;
+	gicv5_global_data.spi_domain = NULL;
 }
 
 static int __init gicv5_init_domains(struct fwnode_handle *handle)
 {
+	u32 spi_count = gicv5_global_data.global_spi_count;
 	struct irq_domain *d;
 
 	d = irq_domain_create_linear(handle, PPI_NR, &gicv5_irq_ppi_domain_ops,
@@ -402,6 +669,19 @@ static int __init gicv5_init_domains(struct fwnode_handle *handle)
 	irq_domain_update_bus_token(d, DOMAIN_BUS_WIRED);
 	gicv5_global_data.ppi_domain = d;
 
+	if (spi_count) {
+		d = irq_domain_create_linear(handle, spi_count,
+					     &gicv5_irq_spi_domain_ops, NULL);
+
+		if (!d) {
+			gicv5_free_domains();
+			return -ENOMEM;
+		}
+
+		gicv5_global_data.spi_domain = d;
+		irq_domain_update_bus_token(d, DOMAIN_BUS_WIRED);
+	}
+
 	gicv5_global_data.fwnode = handle;
 
 	return 0;
@@ -413,14 +693,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;
 	}
 }
@@ -429,12 +709,19 @@ static int __init gicv5_of_init(struct device_node *node, struct device_node *pa
 {
 	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)
+		goto out_irs;
+
 	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)
 		goto out_dom;
@@ -448,6 +735,8 @@ static int __init gicv5_of_init(struct device_node *node, struct device_node *pa
 	gicv5_cpu_disable_interrupts();
 out_dom:
 	gicv5_free_domains();
+out_irs:
+	gicv5_irs_remove();
 
 	return ret;
 }
diff --git a/include/linux/irqchip/arm-gic-v5.h b/include/linux/irqchip/arm-gic-v5.h
index cc3da79e615b2b6ee27456e98c17061e4508030f..65d5cecf39101f95d4c93ade60939fe481d1e466 100644
--- a/include/linux/irqchip/arm-gic-v5.h
+++ b/include/linux/irqchip/arm-gic-v5.h
@@ -5,6 +5,8 @@
 #ifndef __LINUX_IRQCHIP_ARM_GIC_V5_H
 #define __LINUX_IRQCHIP_ARM_GIC_V5_H
 
+#include <linux/iopoll.h>
+
 #include <asm/sysreg.h>
 
 #define GICV5_HWIRQ_ID			GENMASK(23, 0)
@@ -12,8 +14,136 @@
 #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_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_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;
+};
+
+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;
+};
+
+static inline int gicv5_wait_for_op_s_atomic(void __iomem *addr, u32 offset,
+					     const char *reg_s, u32 mask,
+					     u32 *val)
+{
+	void __iomem *reg = addr + offset;
+	u32 tmp;
+	int ret;
+
+	ret = readl_poll_timeout_atomic(reg, tmp, tmp & mask, 1, 10 * USEC_PER_MSEC);
+	if (unlikely(ret == -ETIMEDOUT)) {
+		pr_err_ratelimited("%s timeout...\n", reg_s);
+		return ret;
+	}
+
+	if (val)
+		*val = tmp;
+
+	return 0;
+}
+
+#define gicv5_wait_for_op_atomic(base, reg, mask, val) \
+	gicv5_wait_for_op_s_atomic(base, reg, #reg, mask, val)
+
+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] 49+ messages in thread

* [PATCH v3 22/25] irqchip/gic-v5: Add GICv5 LPI/IPI support
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (20 preceding siblings ...)
  2025-05-06 12:23 ` [PATCH v3 21/25] irqchip/gic-v5: Add GICv5 IRS/SPI support Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-06 15:07   ` Thomas Gleixner
  2025-05-06 12:23 ` [PATCH v3 23/25] irqchip/gic-v5: Enable GICv5 SMP booting Lorenzo Pieralisi
                   ` (3 subsequent siblings)
  25 siblings, 1 reply; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, 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.

LPIs state and routing information is kept in the Interrupt
State Table (IST).

IRSes provide support for 1- or 2-level IST tables configured
to support a maximum number of interrupts that depend on the
OS configuration and the HW capabilities.

On systems that provide 2-level IST support, always allow
the maximum number of LPIs; On systems with only 1-level
support, limit the number of LPIs to 2^12 to prevent
wasting memory (presumably a system that supports a 1-level
only IST is not expecting a large number of interrupts).

On a 2-level IST system, L2 entries are allocated on
demand.

The IST table memory is allocated using the kmalloc() interface;
the allocation required may be smaller than a page and must be
made up of contiguous physical pages if larger than a page.

On systems where the IRS is not cache-coherent with the CPUs,
cache mainteinance operations are executed to clean and
invalidate the allocated memory to the point of coherency
making it visible to the IRS components.

On GICv5 systems, IPIs are implemented using LPIs.

Add an LPI IRQ domain and implement an IPI-specific IRQ domain created
as a child/subdomain of the LPI domain to allocate the required number
of LPIs needed to implement the IPIs.

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

The LPI INTID namespace is managed using an IDA to alloc/free LPI INTIDs.

Associate an IPI irqchip with IPI IRQ descriptors to provide
core code with the irqchip.ipi_send_single() method required
to raise an IPI.

Co-developed-by: Sascha Bischoff <sascha.bischoff@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/smp.h       |  17 ++
 arch/arm64/kernel/smp.c            |  17 --
 drivers/irqchip/irq-gic-v5-irs.c   | 361 +++++++++++++++++++++++++++++++++++++
 drivers/irqchip/irq-gic-v5.c       | 284 ++++++++++++++++++++++++++++-
 include/linux/irqchip/arm-gic-v5.h |  59 ++++++
 5 files changed, 719 insertions(+), 19 deletions(-)

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 e4e0e90993f4b3e901b14d379e219131e634aa75..a3de1c1b918c0ae123ae204f0dea6992db33ecd0 100644
--- a/drivers/irqchip/irq-gic-v5-irs.c
+++ b/drivers/irqchip/irq-gic-v5-irs.c
@@ -5,6 +5,7 @@
 
 #define pr_fmt(fmt)	"GICv5 IRS: " fmt
 
+#include <linux/log2.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
 
@@ -30,6 +31,335 @@ static void irs_writel_relaxed(struct gicv5_irs_chip_data *irs_data,
 	writel_relaxed(val, irs_data->irs_base + reg_offset);
 }
 
+static u64 irs_readq_relaxed(struct gicv5_irs_chip_data *irs_data,
+			     const u32 reg_offset)
+{
+	return readq_relaxed(irs_data->irs_base + reg_offset);
+}
+
+static void irs_writeq_relaxed(struct gicv5_irs_chip_data *irs_data,
+			       const u64 val, const u32 reg_offset)
+{
+	writeq_relaxed(val, irs_data->irs_base + reg_offset);
+}
+
+/*
+ * The polling wait (in gicv5_wait_for_op_s_atomic()) on a GIC register
+ * provides the memory barriers (through MMIO accessors)
+ * required to synchronize CPU and GIC access to IST memory.
+ */
+static int gicv5_irs_ist_synchronise(struct gicv5_irs_chip_data *irs_data)
+{
+	return gicv5_wait_for_op_atomic(irs_data->irs_base, GICV5_IRS_IST_STATUSR,
+					GICV5_IRS_IST_STATUSR_IDLE, NULL);
+}
+
+static int __init gicv5_irs_init_ist_linear(struct gicv5_irs_chip_data *irs_data,
+					    unsigned int lpi_id_bits,
+					    unsigned int istsz)
+{
+	size_t l2istsz;
+	u32 n, cfgr;
+	void *ist;
+	u64 baser;
+	int ret;
+
+	/* Taken from GICv5 specifications 10.2.1.13 IRS_IST_BASER */
+	n = max(5, lpi_id_bits + 1 + istsz);
+
+	l2istsz = BIT(n + 1);
+	/*
+	 * Check memory requirements. For a linear IST we cap the
+	 * number of ID bits to a value that should never exceed
+	 * kmalloc interface memory allocation limits, so this
+	 * check is really belt and braces.
+	 */
+	if (l2istsz > KMALLOC_MAX_SIZE) {
+		u8 lpi_id_cap = ilog2(KMALLOC_MAX_SIZE) - 2 + istsz;
+
+		pr_warn("Limiting LPI ID bits from %u to %u\n",
+			lpi_id_bits, lpi_id_cap);
+		lpi_id_bits = lpi_id_cap;
+		l2istsz = KMALLOC_MAX_SIZE;
+	}
+
+	ist = kzalloc(l2istsz, GFP_KERNEL);
+	if (!ist)
+		return -ENOMEM;
+
+	if (irs_data->flags & IRS_FLAGS_NON_COHERENT)
+		dcache_clean_inval_poc((unsigned long)ist,
+				       (unsigned long)ist + l2istsz);
+	else
+		dsb(ishst);
+
+	cfgr = FIELD_PREP(GICV5_IRS_IST_CFGR_STRUCTURE,
+			  GICV5_IRS_IST_CFGR_STRUCTURE_LINEAR)	|
+	       FIELD_PREP(GICV5_IRS_IST_CFGR_ISTSZ, istsz)	|
+	       FIELD_PREP(GICV5_IRS_IST_CFGR_L2SZ,
+			  GICV5_IRS_IST_CFGR_L2SZ_4K)		|
+	       FIELD_PREP(GICV5_IRS_IST_CFGR_LPI_ID_BITS, lpi_id_bits);
+	irs_writel_relaxed(irs_data, cfgr, GICV5_IRS_IST_CFGR);
+
+	gicv5_global_data.ist.l2 = false;
+
+	baser = (virt_to_phys(ist) & GICV5_IRS_IST_BASER_ADDR_MASK) |
+		FIELD_PREP(GICV5_IRS_IST_BASER_VALID, 0x1);
+	irs_writeq_relaxed(irs_data, baser, GICV5_IRS_IST_BASER);
+
+	ret = gicv5_irs_ist_synchronise(irs_data);
+	if (ret) {
+		kfree(ist);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int __init gicv5_irs_init_ist_two_level(struct gicv5_irs_chip_data *irs_data,
+					       unsigned int lpi_id_bits,
+					       unsigned int istsz,
+					       unsigned int l2sz)
+{
+	__le64 *l1ist;
+	u32 cfgr, n;
+	size_t l1sz;
+	u64 baser;
+	int ret;
+
+	/* Taken from GICv5 specifications 10.2.1.13 IRS_IST_BASER */
+	n = max(5, lpi_id_bits - ((10 - istsz) + (2 * l2sz)) + 2);
+
+	l1sz = BIT(n + 1);
+
+	l1ist = kzalloc(l1sz, GFP_KERNEL);
+	if (!l1ist)
+		return -ENOMEM;
+
+	if (irs_data->flags & IRS_FLAGS_NON_COHERENT)
+		dcache_clean_inval_poc((unsigned long)l1ist,
+				       (unsigned long)l1ist + l1sz);
+	else
+		dsb(ishst);
+
+	cfgr = FIELD_PREP(GICV5_IRS_IST_CFGR_STRUCTURE,
+			  GICV5_IRS_IST_CFGR_STRUCTURE_TWO_LEVEL)	|
+	       FIELD_PREP(GICV5_IRS_IST_CFGR_ISTSZ, istsz)		|
+	       FIELD_PREP(GICV5_IRS_IST_CFGR_L2SZ, l2sz)		|
+	       FIELD_PREP(GICV5_IRS_IST_CFGR_LPI_ID_BITS, lpi_id_bits);
+	irs_writel_relaxed(irs_data, cfgr, GICV5_IRS_IST_CFGR);
+
+	/*
+	 * The L2SZ determine bits required at L2 level. Number of bytes
+	 * required by metadata is reported through istsz - the number of bits
+	 * covered by L2 entries scales accordingly.
+	 */
+	gicv5_global_data.ist.l2_size = BIT(11 + (2 * l2sz) + 1);
+	gicv5_global_data.ist.l2_bits = (10 - istsz) + (2 * l2sz);
+	gicv5_global_data.ist.l1ist_addr = l1ist;
+	gicv5_global_data.ist.l2 = true;
+
+	baser = (virt_to_phys(l1ist) & GICV5_IRS_IST_BASER_ADDR_MASK) |
+		FIELD_PREP(GICV5_IRS_IST_BASER_VALID, 0x1);
+	irs_writeq_relaxed(irs_data, baser, GICV5_IRS_IST_BASER);
+
+	ret = gicv5_irs_ist_synchronise(irs_data);
+	if (ret) {
+		kfree(l1ist);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Alloc L2 IST entries on demand.
+ *
+ * Locking/serialization is guaranteed by irqdomain core code by
+ * taking the hierarchical domain struct irq_domain.root->mutex.
+ */
+int gicv5_irs_iste_alloc(const u32 lpi)
+{
+	struct gicv5_irs_chip_data *irs_data;
+	unsigned int index;
+	u32 l2istr, l2bits;
+	__le64 *l1ist;
+	size_t l2size;
+	void *l2ist;
+	int ret;
+
+	if (!gicv5_global_data.ist.l2)
+		return 0;
+
+	irs_data = per_cpu(per_cpu_irs_data, smp_processor_id());
+	if (!irs_data)
+		return -ENOENT;
+
+	l2size = gicv5_global_data.ist.l2_size;
+	l2bits = gicv5_global_data.ist.l2_bits;
+
+	l1ist = gicv5_global_data.ist.l1ist_addr;
+
+	index = lpi >> l2bits;
+
+	if (FIELD_GET(GICV5_ISTL1E_VALID, le64_to_cpu(l1ist[index])))
+		return 0;
+
+	l2ist = kzalloc(l2size, GFP_KERNEL);
+	if (!l2ist)
+		return -ENOMEM;
+
+	l1ist[index] = cpu_to_le64(virt_to_phys(l2ist) & GICV5_ISTL1E_L2_ADDR_MASK);
+
+	if (irs_data->flags & IRS_FLAGS_NON_COHERENT) {
+		dcache_clean_inval_poc((unsigned long)l2ist,
+				       (unsigned long)l2ist + l2size);
+		dcache_clean_poc((unsigned long)(l1ist + index),
+				 (unsigned long)(l1ist + index) + sizeof(*l1ist));
+	} else {
+		dsb(ishst);
+	}
+
+	l2istr = FIELD_PREP(GICV5_IRS_MAP_L2_ISTR_ID, lpi);
+	irs_writel_relaxed(irs_data, l2istr, GICV5_IRS_MAP_L2_ISTR);
+
+	ret = gicv5_irs_ist_synchronise(irs_data);
+	if (ret) {
+		l1ist[index] = 0;
+		kfree(l2ist);
+		return ret;
+	}
+
+	/*
+	 * Make sure we invalidate the cache line pulled before the IRS
+	 * had a chance to update the L1 entry and mark it valid.
+	 */
+	if (irs_data->flags & IRS_FLAGS_NON_COHERENT) {
+		/*
+		 * gicv5_irs_ist_synchronise() includes memory
+		 * barriers (MMIO accessors) required to guarantee that the
+		 * following dcache invalidation is not executed before the
+		 * IST mapping operation has completed.
+		 */
+		dcache_inval_poc((unsigned long)(l1ist + index),
+				 (unsigned long)(l1ist + index) + sizeof(*l1ist));
+	}
+
+	return 0;
+}
+
+/*
+ * Try to match the L2 IST size to the pagesize, and if this is not possible
+ * pick the smallest supported L2 size in order to minimise the requirement for
+ * physically contiguous blocks of memory as page-sized allocations are
+ * guaranteed to be physically contiguous, and are by definition the easiest to
+ * find.
+ *
+ * Fall back to the smallest supported size (in the event that the pagesize
+ * itself is not supported) again serves to make it easier to find physically
+ * contiguous blocks of memory.
+ */
+static unsigned int gicv5_irs_l2_sz(u32 idr2)
+{
+	switch (PAGE_SIZE) {
+	case SZ_64K:
+		if (GICV5_IRS_IST_L2SZ_SUPPORT_64KB(idr2))
+			return GICV5_IRS_IST_CFGR_L2SZ_64K;
+		fallthrough;
+	case SZ_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;
+		break;
+	}
+
+	if (GICV5_IRS_IST_L2SZ_SUPPORT_16KB(idr2))
+		return GICV5_IRS_IST_CFGR_L2SZ_16K;
+
+	return GICV5_IRS_IST_CFGR_L2SZ_64K;
+}
+
+static int __init gicv5_irs_init_ist(struct gicv5_irs_chip_data *irs_data)
+{
+	u32 lpi_id_bits, idr2_id_bits, idr2_min_lpi_id_bits,
+	    l2_iste_sz, l2sz, l2_iste_sz_split, idr2;
+	bool two_levels, istmd;
+	u64 baser;
+	int ret;
+
+	baser = irs_readq_relaxed(irs_data, GICV5_IRS_IST_BASER);
+	if (FIELD_GET(GICV5_IRS_IST_BASER_VALID, baser)) {
+		pr_err("IST is marked as valid already; cannot allocate\n");
+		return -EPERM;
+	}
+
+	idr2 = irs_readl_relaxed(irs_data, GICV5_IRS_IDR2);
+
+	two_levels = !!FIELD_GET(GICV5_IRS_IDR2_IST_LEVELS, idr2);
+
+	idr2_id_bits = FIELD_GET(GICV5_IRS_IDR2_ID_BITS, idr2);
+	idr2_min_lpi_id_bits = FIELD_GET(GICV5_IRS_IDR2_MIN_LPI_ID_BITS, idr2);
+
+	/*
+	 * For two level tables we are always supporting the maximum allowed
+	 * number of IDs.
+	 *
+	 * For 1-level tables, we should support a number of bits that
+	 * is >= min_lpi_id_bits but cap it to LPI_ID_BITS_LINEAR lest
+	 * the level 1-table gets too large and its memory allocation
+	 * may fail.
+	 */
+	if (two_levels) {
+		lpi_id_bits = idr2_id_bits;
+	} else {
+		lpi_id_bits = max(LPI_ID_BITS_LINEAR, idr2_min_lpi_id_bits);
+		lpi_id_bits = min(lpi_id_bits, idr2_id_bits);
+	}
+
+	/*
+	 * Cap the ID bits according to the CPUIF supported ID bits
+	 */
+	lpi_id_bits = min(lpi_id_bits, gicv5_global_data.cpuif_id_bits);
+
+	if (two_levels)
+		l2sz = gicv5_irs_l2_sz(idr2);
+
+	istmd = !!FIELD_GET(GICV5_IRS_IDR2_ISTMD, idr2);
+
+	l2_iste_sz = GICV5_IRS_IST_CFGR_ISTSZ_4;
+
+	// 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 (ref: 10.2.1.14 IRS_IST_CFGR).
+	 */
+	if (two_levels && (lpi_id_bits > ((10 - l2_iste_sz) + (2 * l2sz)))) {
+		ret = gicv5_irs_init_ist_two_level(irs_data, lpi_id_bits,
+						   l2_iste_sz, l2sz);
+	} else {
+		ret = gicv5_irs_init_ist_linear(irs_data, lpi_id_bits,
+						l2_iste_sz);
+	}
+	if (ret)
+		return ret;
+
+	gicv5_init_lpis(BIT(lpi_id_bits));
+
+	return 0;
+}
+
 struct iaffid_entry {
 	u16	iaffid;
 	bool	valid;
@@ -362,6 +692,13 @@ static int __init gicv5_irs_init(struct device_node *node)
 		goto out_iomem;
 	}
 
+	idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR2);
+	if (WARN(!FIELD_GET(GICV5_IRS_IDR2_LPI, idr),
+		 "LPI support not available - no IPIs, can't proceed\n")) {
+		ret = -ENODEV;
+		goto out_iomem;
+	}
+
 	idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR7);
 	irs_data->spi_min = FIELD_GET(GICV5_IRS_IDR7_SPI_BASE, idr);
 
@@ -391,6 +728,8 @@ static int __init gicv5_irs_init(struct device_node *node)
 		spi_count = FIELD_GET(GICV5_IRS_IDR5_SPI_RANGE, idr);
 		gicv5_global_data.global_spi_count = spi_count;
 
+		gicv5_init_lpi_domain();
+
 		pr_debug("Detected %u SPIs globally\n", spi_count);
 	}
 
@@ -408,6 +747,9 @@ void __init gicv5_irs_remove(void)
 {
 	struct gicv5_irs_chip_data *irs_data, *tmp_data;
 
+	gicv5_free_lpi_domain();
+	gicv5_deinit_lpis();
+
 	list_for_each_entry_safe(irs_data, tmp_data, &irs_nodes, entry) {
 		iounmap(irs_data->irs_base);
 		list_del(&irs_data->entry);
@@ -415,6 +757,25 @@ void __init gicv5_irs_remove(void)
 	}
 }
 
+int __init gicv5_irs_enable(void)
+{
+	struct gicv5_irs_chip_data *irs_data;
+	int ret;
+
+	irs_data = list_first_entry_or_null(&irs_nodes,
+					    struct gicv5_irs_chip_data, entry);
+	if (!irs_data)
+		return -ENODEV;
+
+	ret = gicv5_irs_init_ist(irs_data);
+	if (ret) {
+		pr_err("Failed to init IST\n");
+		return ret;
+	}
+
+	return 0;
+}
+
 int __init gicv5_irs_of_probe(struct device_node *parent)
 {
 	struct device_node *np;
diff --git a/drivers/irqchip/irq-gic-v5.c b/drivers/irqchip/irq-gic-v5.c
index e7ff82628ee67f368ea64e5e85e4474e6b82ef62..c2c05f4411197365079c7d997665c81e3a0f4b74 100644
--- a/drivers/irqchip/irq-gic-v5.c
+++ b/drivers/irqchip/irq-gic-v5.c
@@ -5,7 +5,9 @@
 
 #define pr_fmt(fmt)	"GICv5: " fmt
 
+#include <linux/idr.h>
 #include <linux/irqdomain.h>
+#include <linux/slab.h>
 #include <linux/wordpart.h>
 
 #include <linux/irqchip.h>
@@ -28,6 +30,42 @@ static bool gicv5_cpuif_has_gcie(void)
 
 struct gicv5_chip_data gicv5_global_data __read_mostly;
 
+static DEFINE_IDA(lpi_ida);
+static u32 num_lpis;
+
+void __init gicv5_init_lpis(u32 lpis)
+{
+	num_lpis = lpis;
+}
+
+void __init gicv5_deinit_lpis(void)
+{
+	num_lpis = 0;
+}
+
+static int alloc_lpi(void)
+{
+	if (!num_lpis)
+		return -ENOSPC;
+
+	return ida_alloc_max(&lpi_ida, num_lpis - 1, GFP_KERNEL);
+}
+
+static void release_lpi(u32 lpi)
+{
+	ida_free(&lpi_ida, lpi);
+}
+
+static int gicv5_alloc_lpi(void)
+{
+	return alloc_lpi();
+}
+
+static void gicv5_free_lpi(u32 lpi)
+{
+	release_lpi(lpi);
+}
+
 static void gicv5_ppi_priority_init(void)
 {
 	write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRI_MI), SYS_ICC_PPI_PRIORITYR0_EL1);
@@ -60,7 +98,7 @@ static void gicv5_hwirq_init(irq_hw_number_t hwirq, u8 priority, u8 hwirq_type)
 	u16 iaffid;
 	int ret;
 
-	if (hwirq_type == GICV5_HWIRQ_TYPE_SPI) {
+	if (hwirq_type == GICV5_HWIRQ_TYPE_LPI || hwirq_type == GICV5_HWIRQ_TYPE_SPI) {
 		cdpri = FIELD_PREP(GICV5_GIC_CDPRI_PRIORITY_MASK, priority)	|
 			FIELD_PREP(GICV5_GIC_CDPRI_TYPE_MASK, hwirq_type)	|
 			FIELD_PREP(GICV5_GIC_CDPRI_ID_MASK, hwirq);
@@ -119,6 +157,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);
@@ -143,7 +186,7 @@ static void gicv5_iri_irq_unmask(struct irq_data *d, u8 hwirq_type)
 	/*
 	 * Rule R_XCLJC states that the effects of a GIC system instruction
 	 * complete in finite time and that's the only requirement when
-	 * unmasking an SPI IRQ.
+	 * unmasking an SPI/LPI IRQ.
 	 */
 	gic_insn(cden, CDEN);
 }
@@ -153,6 +196,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);
@@ -172,6 +220,11 @@ static void gicv5_spi_irq_eoi(struct irq_data *d)
 	gicv5_hwirq_eoi(d->hwirq, GICV5_HWIRQ_TYPE_SPI);
 }
 
+static void gicv5_lpi_irq_eoi(struct irq_data *d)
+{
+	gicv5_hwirq_eoi(d->hwirq, GICV5_HWIRQ_TYPE_LPI);
+}
+
 static int gicv5_iri_irq_set_affinity(struct irq_data *d,
 				      const struct cpumask *mask_val,
 				      bool force, u8 hwirq_type)
@@ -207,6 +260,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);
+}
+
 #define READ_PPI_REG(irq, reg)							\
 	({									\
 		u64 __ppi_val;							\
@@ -328,6 +389,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)
@@ -365,6 +434,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)
@@ -381,12 +455,41 @@ static int gicv5_spi_irq_set_irqchip_state(struct irq_data *d,
 	return 0;
 }
 
+static int gicv5_lpi_irq_set_irqchip_state(struct irq_data *d,
+					   enum irqchip_irq_state which,
+					   bool val)
+{
+	switch (which) {
+	case IRQCHIP_STATE_PENDING:
+		gicv5_lpi_irq_write_pending_state(d, val);
+		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,
@@ -557,6 +660,141 @@ static const struct irq_domain_ops gicv5_irq_spi_domain_ops = {
 	.free		= gicv5_irq_domain_free,
 	.select		= gicv5_irq_spi_domain_select
 };
+
+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_lpi_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				      unsigned int nr_irqs, void *arg)
+{
+	irq_hw_number_t hwirq;
+	struct irq_data *irqd;
+	u32 *lpi = arg;
+	int ret;
+
+	if (WARN_ON_ONCE(nr_irqs != 1))
+		return -EINVAL;
+
+	hwirq = *lpi;
+
+	irqd = irq_domain_get_irq_data(domain, virq);
+
+	irq_domain_set_info(domain, virq, hwirq, &gicv5_lpi_irq_chip, NULL,
+			    handle_fasteoi_irq, NULL, NULL);
+	irqd_set_single_target(irqd);
+
+	ret = gicv5_irs_iste_alloc(hwirq);
+	if (ret < 0)
+		return ret;
+
+	gicv5_hwirq_init(hwirq, GICV5_IRQ_PRI_MI, GICV5_HWIRQ_TYPE_LPI);
+
+	return 0;
+}
+
+static const struct irq_domain_ops gicv5_irq_lpi_domain_ops = {
+	.alloc	= gicv5_irq_lpi_domain_alloc,
+	.free	= gicv5_irq_domain_free,
+};
+
+void __init gicv5_init_lpi_domain(void)
+{
+	struct irq_domain *d;
+
+	d = irq_domain_create_tree(NULL, &gicv5_irq_lpi_domain_ops, NULL);
+	gicv5_global_data.lpi_domain = d;
+}
+
+void __init gicv5_free_lpi_domain(void)
+{
+	irq_domain_remove(gicv5_global_data.lpi_domain);
+	gicv5_global_data.lpi_domain = NULL;
+}
+
+static int gicv5_irq_ipi_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				      unsigned int nr_irqs, void *arg)
+{
+	struct irq_data *irqd;
+	int ret, i;
+	u32 lpi;
+
+	for (i = 0; i < nr_irqs; i++) {
+		ret = gicv5_alloc_lpi();
+		if (ret < 0)
+			return ret;
+
+		lpi = ret;
+
+		ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, &lpi);
+		if (ret) {
+			gicv5_free_lpi(lpi);
+			return ret;
+		}
+
+		irqd = irq_domain_get_irq_data(domain, virq + i);
+
+		irq_domain_set_hwirq_and_chip(domain, virq + i, i,
+				&gicv5_ipi_irq_chip, NULL);
+
+		irqd_set_single_target(irqd);
+
+		irq_set_handler(virq + i, handle_percpu_irq);
+	}
+
+	return 0;
+}
+
+static void gicv5_irq_ipi_domain_free(struct irq_domain *domain, unsigned int virq,
+				      unsigned int nr_irqs)
+{
+	struct irq_data *d;
+	unsigned int i;
+
+	for (i = 0; i < nr_irqs; i++) {
+		d = irq_domain_get_irq_data(domain, virq + i);
+
+		if (!d)
+			return;
+
+		gicv5_free_lpi(d->parent_data->hwirq);
+
+		irq_set_handler(virq + i, NULL);
+		irq_domain_reset_irq_data(d);
+		irq_domain_free_irqs_parent(domain, virq + i, 1);
+	}
+}
+
+static const struct irq_domain_ops gicv5_irq_ipi_domain_ops = {
+	.alloc	= gicv5_irq_ipi_domain_alloc,
+	.free	= gicv5_irq_ipi_domain_free,
+};
+
 static void handle_irq_per_domain(u32 hwirq)
 {
 	u8 hwirq_type = FIELD_GET(GICV5_HWIRQ_TYPE, hwirq);
@@ -570,6 +808,9 @@ static void handle_irq_per_domain(u32 hwirq)
 	case GICV5_HWIRQ_TYPE_SPI:
 		domain = gicv5_global_data.spi_domain;
 		break;
+	case GICV5_HWIRQ_TYPE_LPI:
+		domain = gicv5_global_data.lpi_domain;
+		break;
 	default:
 		pr_err_once("Unknown IRQ type, bail out\n");
 		return;
@@ -651,9 +892,12 @@ static void __init gicv5_free_domains(void)
 		irq_domain_remove(gicv5_global_data.ppi_domain);
 	if (gicv5_global_data.spi_domain)
 		irq_domain_remove(gicv5_global_data.spi_domain);
+	if (gicv5_global_data.ipi_domain)
+		irq_domain_remove(gicv5_global_data.ipi_domain);
 
 	gicv5_global_data.ppi_domain = NULL;
 	gicv5_global_data.spi_domain = NULL;
+	gicv5_global_data.ipi_domain = NULL;
 }
 
 static int __init gicv5_init_domains(struct fwnode_handle *handle)
@@ -682,6 +926,19 @@ static int __init gicv5_init_domains(struct fwnode_handle *handle)
 		irq_domain_update_bus_token(d, DOMAIN_BUS_WIRED);
 	}
 
+	if (!WARN(!gicv5_global_data.lpi_domain,
+		  "LPI domain uninitialized, can't set up IPIs")) {
+		d = irq_domain_create_hierarchy(gicv5_global_data.lpi_domain,
+						0, GICV5_IPIS_PER_CPU * nr_cpu_ids,
+						NULL, &gicv5_irq_ipi_domain_ops,
+						NULL);
+
+		if (!d) {
+			gicv5_free_domains();
+			return -ENOMEM;
+		}
+		gicv5_global_data.ipi_domain = d;
+	}
 	gicv5_global_data.fwnode = handle;
 
 	return 0;
@@ -705,6 +962,24 @@ static void gicv5_set_cpuif_pribits(void)
 	}
 }
 
+static void gicv5_set_cpuif_idbits(void)
+{
+	u32 icc_idr0 = read_sysreg_s(SYS_ICC_IDR0_EL1);
+
+	switch (FIELD_GET(ICC_IDR0_EL1_ID_BITS, icc_idr0)) {
+	case ICC_IDR0_EL1_ID_BITS_16BITS:
+		gicv5_global_data.cpuif_id_bits = 16;
+		break;
+	case ICC_IDR0_EL1_ID_BITS_24BITS:
+		gicv5_global_data.cpuif_id_bits = 24;
+		break;
+	default:
+		pr_err("Unexpected ICC_IDR0_EL1_ID_BITS value, default to 16");
+		gicv5_global_data.cpuif_id_bits = 16;
+		break;
+	}
+}
+
 static int __init gicv5_of_init(struct device_node *node, struct device_node *parent)
 {
 	int ret;
@@ -718,6 +993,7 @@ static int __init gicv5_of_init(struct device_node *node, struct device_node *pa
 		goto out_irs;
 
 	gicv5_set_cpuif_pribits();
+	gicv5_set_cpuif_idbits();
 
 	pri_bits = min_not_zero(gicv5_global_data.cpuif_pri_bits,
 				gicv5_global_data.irs_pri_bits);
@@ -730,6 +1006,10 @@ static int __init gicv5_of_init(struct device_node *node, struct device_node *pa
 	if (ret)
 		goto out_int;
 
+	ret = gicv5_irs_enable();
+	if (ret)
+		goto out_int;
+
 	return 0;
 out_int:
 	gicv5_cpu_disable_interrupts();
diff --git a/include/linux/irqchip/arm-gic-v5.h b/include/linux/irqchip/arm-gic-v5.h
index 65d5cecf39101f95d4c93ade60939fe481d1e466..ff00c12cb9ec6758e135e6a7411873066d76763d 100644
--- a/include/linux/irqchip/arm-gic-v5.h
+++ b/include/linux/irqchip/arm-gic-v5.h
@@ -7,13 +7,18 @@
 
 #include <linux/iopoll.h>
 
+#include <asm/cacheflush.h>
+#include <asm/smp.h>
 #include <asm/sysreg.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)
@@ -45,6 +50,11 @@
 #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)
 
@@ -65,6 +75,11 @@
 #define GICV5_IRS_IDR5_SPI_RANGE	GENMASK(24, 0)
 #define GICV5_IRS_IDR6_SPI_IRS_RANGE	GENMASK(24, 0)
 #define GICV5_IRS_IDR7_SPI_BASE		GENMASK(23, 0)
+
+#define GICV5_IRS_IST_L2SZ_SUPPORT_4KB(r)	FIELD_GET(BIT(11), (r))
+#define GICV5_IRS_IST_L2SZ_SUPPORT_16KB(r)	FIELD_GET(BIT(12), (r))
+#define GICV5_IRS_IST_L2SZ_SUPPORT_64KB(r)	FIELD_GET(BIT(13), (r))
+
 #define GICV5_IRS_CR0_IDLE		BIT(1)
 #define GICV5_IRS_CR0_IRSEN		BIT(0)
 
@@ -96,13 +111,49 @@
 
 #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;
+	struct {
+		__le64 *l1ist_addr;
+		u32 l2_size;
+		u8 l2_bits;
+		bool l2;
+	} ist;
 };
 
 extern struct gicv5_chip_data gicv5_global_data __read_mostly;
@@ -140,10 +191,18 @@ static inline int gicv5_wait_for_op_s_atomic(void __iomem *addr, u32 offset,
 #define gicv5_wait_for_op_atomic(base, reg, mask, val) \
 	gicv5_wait_for_op_s_atomic(base, reg, #reg, mask, val)
 
+void __init gicv5_init_lpi_domain(void);
+void __init gicv5_free_lpi_domain(void);
+
 int gicv5_irs_of_probe(struct device_node *parent);
 void gicv5_irs_remove(void);
+int gicv5_irs_enable(void);
 int gicv5_irs_register_cpu(int cpuid);
 int gicv5_irs_cpu_to_iaffid(int cpu_id, u16 *iaffid);
 struct gicv5_irs_chip_data *gicv5_irs_lookup_by_spi_id(u32 spi_id);
 int gicv5_spi_irq_set_type(struct irq_data *d, unsigned int type);
+int gicv5_irs_iste_alloc(u32 lpi);
+
+void gicv5_init_lpis(u32 max);
+void gicv5_deinit_lpis(void);
 #endif

-- 
2.48.0



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

* [PATCH v3 23/25] irqchip/gic-v5: Enable GICv5 SMP booting
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (21 preceding siblings ...)
  2025-05-06 12:23 ` [PATCH v3 22/25] irqchip/gic-v5: Add GICv5 LPI/IPI support Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-06 12:23 ` [PATCH v3 24/25] irqchip/gic-v5: Add GICv5 ITS support Lorenzo Pieralisi
                   ` (2 subsequent siblings)
  25 siblings, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, 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.

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 | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/drivers/irqchip/irq-gic-v5.c b/drivers/irqchip/irq-gic-v5.c
index c2c05f4411197365079c7d997665c81e3a0f4b74..e4bb02a1988b01a23db61288ff1b1fe3db02e0e4 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/idr.h>
 #include <linux/irqdomain.h>
 #include <linux/slab.h>
@@ -875,6 +876,8 @@ static void gicv5_cpu_enable_interrupts(void)
 	write_sysreg_s(cr0, SYS_ICC_CR0_EL1);
 }
 
+static int base_ipi_virq;
+
 static int gicv5_starting_cpu(unsigned int cpu)
 {
 	if (WARN(!gicv5_cpuif_has_gcie(),
@@ -886,6 +889,22 @@ 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);
+}
+
 static void __init gicv5_free_domains(void)
 {
 	if (gicv5_global_data.ppi_domain)
@@ -1010,6 +1029,8 @@ static int __init gicv5_of_init(struct device_node *node, struct device_node *pa
 	if (ret)
 		goto out_int;
 
+	gicv5_smp_init();
+
 	return 0;
 out_int:
 	gicv5_cpu_disable_interrupts();

-- 
2.48.0



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

* [PATCH v3 24/25] irqchip/gic-v5: Add GICv5 ITS support
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (22 preceding siblings ...)
  2025-05-06 12:23 ` [PATCH v3 23/25] irqchip/gic-v5: Enable GICv5 SMP booting Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-09  0:47   ` kernel test robot
  2025-05-06 12:23 ` [PATCH v3 25/25] arm64: Kconfig: Enable GICv5 Lorenzo Pieralisi
  2025-05-06 14:05 ` [PATCH v3 00/25] Arm GICv5: Host driver implementation Marc Zyngier
  25 siblings, 1 reply; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, 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                            |    7 +
 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                   |   25 +
 drivers/irqchip/irq-gic-v5-its.c                   | 1176 ++++++++++++++++++++
 drivers/irqchip/irq-gic-v5.c                       |    6 +-
 include/linux/irqchip/arm-gic-v5.h                 |  179 +++
 11 files changed, 1411 insertions(+), 9 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 49c377febad72e77dd6d480105c2b6bffa81f9a6..5159c517ca1968e6df1678b269559257d2fcc86a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1907,6 +1907,7 @@ 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:	drivers/irqchip/irq-gic-its-msi-parent.[ch]
 F:	drivers/irqchip/irq-gic-v5*.[ch]
 F:	include/linux/irqchip/arm-gic-v5.h
 
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 6b3d70924186bd8ca04294832409d1e379c9cbd4..d73f9bcde8fd995b5b6c33108b9d40b424437b3f 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
 
@@ -58,6 +62,9 @@ config ARM_GIC_V5
 	bool
 	select IRQ_DOMAIN_HIERARCHY
 	select GENERIC_IRQ_EFFECTIVE_AFF_MASK
+	select GENERIC_MSI_IRQ
+	select IRQ_MSI_LIB
+	select ARM_GIC_ITS_PARENT
 
 config ARM_NVIC
 	bool
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 3d9c47fa3fdf40b7452c059d84fe8ac24c91bc0f..18724910f2bdc20597d3d3e4852d593a4bd163da 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -32,10 +32,11 @@ 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)		+= irq-gic-v5.o irq-gic-v5-irs.o 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 a3de1c1b918c0ae123ae204f0dea6992db33ecd0..921472b958d84c6ded65c8f745bca90f7385e03f 100644
--- a/drivers/irqchip/irq-gic-v5-irs.c
+++ b/drivers/irqchip/irq-gic-v5-irs.c
@@ -482,6 +482,23 @@ static int gicv5_irs_wait_for_idle(struct gicv5_irs_chip_data *irs_data)
 					GICV5_IRS_CR0_IDLE, NULL);
 }
 
+void gicv5_irs_syncr(void)
+{
+	struct gicv5_irs_chip_data *irs_data;
+	u32 syncr;
+
+	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_relaxed(irs_data, syncr, GICV5_IRS_SYNCR);
+
+	gicv5_wait_for_op(irs_data->irs_base, GICV5_IRS_SYNC_STATUSR,
+			  GICV5_IRS_SYNC_STATUSR_IDLE);
+}
+
 int gicv5_irs_register_cpu(int cpuid)
 {
 	struct gicv5_irs_chip_data *irs_data;
@@ -776,6 +793,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)
 {
 	struct device_node *np;
diff --git a/drivers/irqchip/irq-gic-v5-its.c b/drivers/irqchip/irq-gic-v5-its.c
new file mode 100644
index 0000000000000000000000000000000000000000..e449be39a75c664ec34d616bc39474f599c11de5
--- /dev/null
+++ b/drivers/irqchip/irq-gic-v5-its.c
@@ -0,0 +1,1176 @@
+// 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/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 <linux/irqchip/arm-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_relaxed(struct gicv5_its_chip_data *its_node,
+			     const u32 reg_offset)
+{
+	return readl_relaxed(its_node->its_base + reg_offset);
+}
+
+static void its_writel_relaxed(struct gicv5_its_chip_data *its_node,
+			       const u32 val, const u32 reg_offset)
+{
+	writel_relaxed(val, its_node->its_base + reg_offset);
+}
+
+static void its_writeq_relaxed(struct gicv5_its_chip_data *its_node,
+			       const u64 val, const u32 reg_offset)
+{
+	writeq_relaxed(val, its_node->its_base + reg_offset);
+}
+
+static void gicv5_its_dcache_clean(struct gicv5_its_chip_data *its, void *start,
+				   size_t sz)
+{
+	void *end = start + sz;
+
+	if (its->flags & ITS_FLAGS_NON_COHERENT)
+		dcache_clean_inval_poc((unsigned long)start, (unsigned long)end);
+	else
+		dsb(ishst);
+}
+
+static void its_write_table_entry(struct gicv5_its_chip_data *its,
+				  __le64 *entry, u64 val)
+{
+	WRITE_ONCE(*entry, cpu_to_le64(val));
+	gicv5_its_dcache_clean(its, entry, sizeof(*entry));
+}
+
+#define devtab_cfgr_field(its, f)	\
+	FIELD_GET(GICV5_ITS_DT_CFGR_##f, (its)->devtab_cfgr.cfgr)
+
+static int gicv5_its_cache_sync(struct gicv5_its_chip_data *its)
+{
+	return gicv5_wait_for_op_atomic(its->its_base, GICV5_ITS_STATUSR,
+					GICV5_ITS_STATUSR_IDLE, NULL);
+}
+
+static void gicv5_its_syncr(struct gicv5_its_chip_data *its,
+			    struct gicv5_its_dev *its_dev)
+{
+	u64 syncr;
+
+	syncr = FIELD_PREP(GICV5_ITS_SYNCR_SYNC, 1) |
+		FIELD_PREP(GICV5_ITS_SYNCR_DEVICEID, its_dev->device_id);
+
+	its_writeq_relaxed(its, syncr, GICV5_ITS_SYNCR);
+
+	gicv5_wait_for_op(its->its_base, GICV5_ITS_SYNC_STATUSR, GICV5_ITS_SYNC_STATUSR_IDLE);
+}
+
+static unsigned int gicv5_its_l2sz_to_l2_bits(unsigned int sz)
+{
+	switch (sz) {
+	case GICV5_ITS_DT_ITT_CFGR_L2SZ_64k:
+		return 13;
+	case GICV5_ITS_DT_ITT_CFGR_L2SZ_16k:
+		return 11;
+	case GICV5_ITS_DT_ITT_CFGR_L2SZ_4k:
+	default:
+		return 9;
+	}
+}
+
+static int gicv5_its_itt_cache_inv(struct gicv5_its_chip_data *its,
+				   u32 device_id, u16 event_id)
+{
+	u32 eventr, eidr;
+	u64 didr;
+
+	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_relaxed(its, didr, GICV5_ITS_DIDR);
+	its_writel_relaxed(its, eidr, GICV5_ITS_EIDR);
+	its_writel_relaxed(its, eventr, GICV5_ITS_INV_EVENTR);
+
+	return gicv5_its_cache_sync(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)
+{
+	unsigned int num_ents = BIT(event_id_bits);
+	__le64 *itt;
+
+	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;
+
+	gicv5_its_dcache_clean(its, itt, num_ents * sizeof(*itt));
+
+	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)
+{
+	unsigned int l1_bits, l2_bits, span, events_per_l2_table,
+		     complete_tables, final_span, num_ents;
+	__le64 *itt_l1, *itt_l2, **l2ptrs;
+	unsigned int i;
+	int ret;
+	u64 val;
+
+	ret = gicv5_its_l2sz_to_l2_bits(itt_l2sz);
+	if (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);
+
+		gicv5_its_dcache_clean(its, 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));
+	}
+
+	gicv5_its_dcache_clean(its, itt_l1, num_ents * sizeof(*itt_l1));
+
+	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)
+{
+	unsigned int l2_bits, l2_sz;
+
+	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;
+		}
+
+		l2_sz = GICV5_ITS_DT_ITT_CFGR_L2SZ_4k;
+		break;
+	}
+
+	l2_bits = gicv5_its_l2sz_to_l2_bits(l2_sz);
+
+	if (l2_bits > id_bits)
+		return false;
+
+	*sz = l2_sz;
+
+	return true;
+}
+
+static __le64 *gicv5_its_device_get_itte_ref(struct gicv5_its_dev *its_dev,
+					     u16 event_id)
+{
+	unsigned int l1_idx, l2_idx, l2_bits;
+	__le64 *l2_itt, *l1_itt;
+
+	if (!its_dev->itt_cfg.l2itt) {
+		__le64 *itt = its_dev->itt_cfg.linear.itt;
+
+		return &itt[event_id];
+	}
+
+	l1_itt = its_dev->itt_cfg.l2.l1itt;
+
+	l2_bits = gicv5_its_l2sz_to_l2_bits(its_dev->itt_cfg.l2.l2sz);
+
+	l1_idx = event_id >> l2_bits;
+
+	BUG_ON(!FIELD_GET(GICV5_ITTL1E_VALID, le64_to_cpu(l1_itt[l1_idx])));
+
+	l2_idx = event_id & GENMASK(l2_bits - 1, 0);
+
+	l2_itt = its_dev->itt_cfg.l2.l2ptrs[l1_idx];
+
+	return &l2_itt[l2_idx];
+}
+
+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_relaxed(its, didr, GICV5_ITS_DIDR);
+	its_writel_relaxed(its, devicer, GICV5_ITS_INV_DEVICER);
+
+	return gicv5_its_cache_sync(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)
+{
+	__le64 *l2devtab, *l1devtab = its->devtab_cfgr.l2.l1devtab;
+	u8 span, l2sz, l2_bits;
+	u64 l1dte;
+
+	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 = devtab_cfgr_field(its, L2SZ);
+
+	l2_bits = gicv5_its_l2sz_to_l2_bits(l2sz);
+
+	/*
+	 * 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], l1dte);
+
+	return 0;
+}
+
+static __le64 *gicv5_its_devtab_get_dte_ref(struct gicv5_its_chip_data *its,
+					    u32 device_id, bool alloc)
+{
+	u8 str = devtab_cfgr_field(its, STRUCTURE);
+	unsigned int l2sz, l2_bits, l1_idx, l2_idx;
+	__le64 *l1devtab, *l2devtab;
+	int ret;
+
+	if (str == GICV5_ITS_DT_ITT_CFGR_STRUCTURE_LINEAR) {
+		l2devtab = its->devtab_cfgr.linear.devtab;
+		return &l2devtab[device_id];
+	}
+
+	l2sz = devtab_cfgr_field(its, L2SZ);
+	l1devtab = its->devtab_cfgr.l2.l1devtab;
+
+	l2_bits = gicv5_its_l2sz_to_l2_bits(l2sz);
+
+	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 NULL;
+	}
+
+	BUG_ON(!FIELD_GET(GICV5_DTL1E_VALID, le64_to_cpu(l1devtab[l1_idx])));
+
+	l2devtab = its->devtab_cfgr.l2.l2ptrs[l1_idx];
+	return &l2devtab[l2_idx];
+}
+
+/*
+ * 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;
+	bool two_level_itt;
+	u32 idr1, idr2;
+	__le64 *dte;
+	u64 val;
+	int ret;
+
+	device_id_bits = devtab_cfgr_field(its, DEVICEID_BITS);
+
+	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;
+	}
+
+	dte = gicv5_its_devtab_get_dte_ref(its, its_dev->device_id, true);
+	if (!dte)
+		return -ENOMEM;
+
+	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_relaxed(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_relaxed(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, 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)
+{
+	__le64 *dte;
+
+	dte = gicv5_its_devtab_get_dte_ref(its, its_dev->device_id, false);
+
+	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)
+{
+	__le64 *devtab;
+	size_t sz;
+	u64 baser;
+	u32 cfgr;
+
+	/*
+	 * We expect a GICv5 implementation requiring a large number of
+	 * deviceID bits to support a 2-level device table. If that's not
+	 * the case, cap the number of deviceIDs supported according to the
+	 * kmalloc limits so that the system can chug along with a linear
+	 * device table.
+	 */
+	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;
+
+	gicv5_its_dcache_clean(its, devtab, sz);
+
+	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_relaxed(its, cfgr, GICV5_ITS_DT_CFGR);
+
+	baser = virt_to_phys(devtab) & GICV5_ITS_DT_BASER_ADDR_MASK;
+	its_writeq_relaxed(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)
+{
+	unsigned int l1_bits, l2_bits, i;
+	__le64 *l1devtab, **l2ptrs;
+	size_t l1_sz;
+	u64 baser;
+	u32 cfgr;
+
+	l2_bits = gicv5_its_l2sz_to_l2_bits(devtab_l2sz);
+
+	l1_bits = device_id_bits - l2_bits;
+	l1_sz = BIT(l1_bits) * sizeof(*l1devtab);
+	/*
+	 * With 2-level device table support it is highly unlikely
+	 * that we are not able to allocate the required amount of
+	 * device table memory to cover deviceID space; cap the
+	 * deviceID space if we encounter such set-up.
+	 * If this ever becomes a problem we could revisit the policy
+	 * behind level 2 size selection to reduce level-1 deviceID bits.
+	 */
+	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));
+
+	gicv5_its_dcache_clean(its, l1devtab, l1_sz);
+
+	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_relaxed(its, cfgr, GICV5_ITS_DT_CFGR);
+
+	baser = virt_to_phys(l1devtab) & GICV5_ITS_DT_BASER_ADDR_MASK;
+	its_writeq_relaxed(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)
+{
+	u8 device_id_bits, devtab_l2sz;
+	bool two_level_devtab;
+	u32 idr1;
+
+	idr1 = its_readl_relaxed(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)
+{
+	struct gicv5_its_dev *its_dev = irq_data_get_irq_chip_data(d);
+	struct gicv5_its_chip_data *its = its_dev->its_node;
+	u64 addr;
+
+	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)
+{
+	struct gicv5_its_dev *its_dev;
+	void *entry;
+	int ret;
+
+	its_dev = gicv5_its_find_device(its, dev_id);
+	if (!IS_ERR(its_dev)) {
+		pr_err("A device with this DeviceID (0x%x) has already been registered.\n",
+		       dev_id);
+
+		return ERR_PTR(-EBUSY);
+	}
+
+	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;
+
+	ret = gicv5_its_device_register(its, its_dev);
+	if (ret) {
+		pr_err("Failed to register the device\n");
+		goto out_dev_free;
+	}
+
+	gicv5_its_device_cache_inv(its, its_dev);
+
+	its_dev->its_node = its;
+
+	its_dev->event_map = (unsigned long *)bitmap_zalloc(its_dev->num_events, GFP_KERNEL);
+	if (!its_dev->event_map) {
+		ret = -ENOMEM;
+		goto out_unregister;
+	}
+
+	entry = xa_store(&its->its_devices, dev_id, its_dev, GFP_KERNEL);
+	if (xa_is_err(entry)) {
+		ret = xa_err(entry);
+		goto out_bitmap_free;
+	}
+
+	return its_dev;
+out_bitmap_free:
+	bitmap_free(its_dev->event_map);
+out_unregister:
+	gicv5_its_device_unregister(its, its_dev);
+out_dev_free:
+	kfree(its_dev);
+	return ERR_PTR(ret);
+}
+
+static int gicv5_its_msi_prepare(struct irq_domain *domain, struct device *dev,
+				 int nvec, msi_alloc_info_t *info)
+{
+	u32 dev_id = info->scratchpad[0].ul;
+	struct msi_domain_info *msi_info;
+	struct gicv5_its_chip_data *its;
+	struct gicv5_its_dev *its_dev;
+
+	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);
+
+	info->scratchpad[0].ptr = its_dev;
+
+	return 0;
+}
+
+static struct msi_domain_ops its_msi_domain_ops = {
+	.msi_prepare	= gicv5_its_msi_prepare,
+};
+
+static int gicv5_its_map_event(struct gicv5_its_dev *its_dev, u16 event_id,
+			       u32 lpi)
+{
+	struct gicv5_its_chip_data *its = its_dev->its_node;
+	u64 itt_entry;
+	__le64 *itte;
+
+	itte = gicv5_its_device_get_itte_ref(its_dev, event_id);
+
+	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, itt_entry);
+
+	gicv5_its_itt_cache_inv(its, its_dev->device_id, event_id);
+
+	return 0;
+}
+
+static void gicv5_its_unmap_event(struct gicv5_its_dev *its_dev, u16 event_id)
+{
+	struct gicv5_its_chip_data *its = its_dev->its_node;
+	u64 itte_val;
+	__le64 *itte;
+
+	itte = gicv5_its_device_get_itte_ref(its_dev, event_id);
+
+	itte_val = le64_to_cpu(*itte);
+	itte_val &= ~GICV5_ITTL2E_VALID;
+
+	its_write_table_entry(its, itte, itte_val);
+
+	gicv5_its_itt_cache_inv(its, its_dev->device_id, event_id);
+}
+
+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)
+{
+	u32 device_id, event_id_base, lpi;
+	struct msi_domain_info *msi_info;
+	struct gicv5_its_chip_data *its;
+	struct gicv5_its_dev *its_dev;
+	msi_alloc_info_t *info = arg;
+	irq_hw_number_t hwirq;
+	struct irq_data *irqd;
+	int ret, i;
+
+	its_dev = info->scratchpad[0].ptr;
+	device_id = its_dev->device_id;
+
+	msi_info = msi_get_domain_info(domain);
+	its = msi_info->data;
+
+	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;
+
+	for (i = 0; i < nr_irqs; i++) {
+		lpi = gicv5_alloc_lpi();
+		if (ret < 0) {
+			pr_debug("Failed to find free LPI!\n");
+			goto out_eventid;
+		}
+
+		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(lpi);
+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)
+{
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+	struct gicv5_its_chip_data *its;
+	struct gicv5_its_dev *its_dev;
+	u16 event_id_base;
+	bool free_device;
+	unsigned int i;
+
+	its_dev = irq_data_get_irq_chip_data(d);
+	its = its_dev->its_node;
+
+	event_id_base = FIELD_GET(GICV5_ITS_HWIRQ_EVENT_ID, d->hwirq);
+
+	guard(mutex)(&its->dev_alloc_lock);
+
+	bitmap_release_region(its_dev->event_map, event_id_base,
+			      get_count_order(nr_irqs));
+
+	free_device = 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);
+
+		gicv5_free_lpi(d->parent_data->hwirq);
+		irq_domain_reset_irq_data(d);
+		irq_domain_free_irqs_parent(domain, virq + i, 1);
+	}
+
+	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, its_dev->device_id);
+		kfree(its_dev);
+	}
+}
+
+static int gicv5_its_irq_domain_activate(struct irq_domain *domain,
+					 struct irq_data *d, bool reserve)
+{
+	struct gicv5_its_dev *its_dev = irq_data_get_irq_chip_data(d);
+	u16 event_id;
+	u32 lpi;
+
+	event_id = FIELD_GET(GICV5_ITS_HWIRQ_EVENT_ID, d->hwirq);
+	lpi = d->parent_data->hwirq;
+
+	return gicv5_its_map_event(its_dev, event_id, lpi);
+}
+
+static void gicv5_its_irq_domain_deactivate(struct irq_domain *domain,
+					    struct irq_data *d)
+{
+	struct gicv5_its_dev *its_dev = irq_data_get_irq_chip_data(d);
+	u16 event_id;
+
+	event_id = FIELD_GET(GICV5_ITS_HWIRQ_EVENT_ID, d->hwirq);
+
+	gicv5_its_unmap_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_cr0(struct gicv5_its_chip_data *its)
+{
+	return gicv5_wait_for_op_atomic(its->its_base, GICV5_ITS_CR0,
+					GICV5_ITS_CR0_IDLE, NULL);
+}
+
+static void gicv5_its_print_info(struct gicv5_its_chip_data *its_node)
+{
+	bool devtab_linear;
+	u8 device_id_bits;
+	u8 str;
+
+	device_id_bits = devtab_cfgr_field(its_node, DEVICEID_BITS);
+
+	str = devtab_cfgr_field(its_node, STRUCTURE);
+	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)
+{
+	struct device_node *np = to_of_node(handle);
+	struct gicv5_its_chip_data *its_node;
+	struct msi_domain_info *info;
+	struct irq_domain *d;
+	u32 cr0, cr1;
+	bool enabled;
+	int ret;
+
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	its_node = kzalloc(sizeof(*its_node), GFP_KERNEL);
+	if (!its_node) {
+		ret = -ENOMEM;
+		goto out_free;
+	}
+
+	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;
+
+	d = irq_domain_create_hierarchy(parent_domain, IRQ_DOMAIN_FLAG_ISOLATED_MSI,
+					0, handle, &gicv5_its_irq_domain_ops, info);
+	if (!d) {
+		ret = -ENOMEM;
+		goto out_free_node;
+	}
+	its_node->domain = d;
+	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_relaxed(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_relaxed(its_node, cr0, GICV5_ITS_CR0);
+		ret = gicv5_its_wait_for_cr0(its_node);
+		if (ret)
+			goto out_dom_remove;
+	}
+
+	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_relaxed(its_node, cr1, GICV5_ITS_CR1);
+
+	ret = gicv5_its_init_devtab(its_node);
+	if (ret)
+		goto out_dom_remove;
+
+	cr0 = FIELD_PREP(GICV5_ITS_CR0_ITSEN, 0x1);
+	its_writel_relaxed(its_node, cr0, GICV5_ITS_CR0);
+
+	ret = gicv5_its_wait_for_cr0(its_node);
+	if (ret)
+		goto out_dom_remove;
+
+	list_add(&its_node->entry, &its_nodes);
+
+	gicv5_its_print_info(its_node);
+
+	return 0;
+out_dom_remove:
+	irq_domain_remove(its_node->domain);
+out_free_node:
+	kfree(its_node);
+out_free:
+	kfree(info);
+	return ret;
+}
+
+static int __init gicv5_its_init(struct device_node *node)
+{
+	void __iomem *its_base;
+	struct resource res;
+	int ret;
+
+	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 e4bb02a1988b01a23db61288ff1b1fe3db02e0e4..910d7cdc7fa502b1a9ed37a05b126a82c9dc2807 100644
--- a/drivers/irqchip/irq-gic-v5.c
+++ b/drivers/irqchip/irq-gic-v5.c
@@ -57,12 +57,12 @@ static void release_lpi(u32 lpi)
 	ida_free(&lpi_ida, lpi);
 }
 
-static int gicv5_alloc_lpi(void)
+int gicv5_alloc_lpi(void)
 {
 	return alloc_lpi();
 }
 
-static void gicv5_free_lpi(u32 lpi)
+void gicv5_free_lpi(u32 lpi)
 {
 	release_lpi(lpi);
 }
@@ -1031,6 +1031,8 @@ static int __init gicv5_of_init(struct device_node *node, struct device_node *pa
 
 	gicv5_smp_init();
 
+	gicv5_irs_its_probe();
+
 	return 0;
 out_int:
 	gicv5_cpu_disable_interrupts();
diff --git a/include/linux/irqchip/arm-gic-v5.h b/include/linux/irqchip/arm-gic-v5.h
index ff00c12cb9ec6758e135e6a7411873066d76763d..2d83ec87a4cf4d0ea7809d58f5a8bdadaa7c7135 100644
--- a/include/linux/irqchip/arm-gic-v5.h
+++ b/include/linux/irqchip/arm-gic-v5.h
@@ -8,6 +8,8 @@
 #include <linux/iopoll.h>
 
 #include <asm/cacheflush.h>
+#include <linux/iopoll.h>
+
 #include <asm/smp.h>
 #include <asm/sysreg.h>
 
@@ -44,6 +46,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
@@ -97,6 +101,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)
 
@@ -138,6 +146,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;
@@ -188,21 +291,97 @@ static inline int gicv5_wait_for_op_s_atomic(void __iomem *addr, u32 offset,
 	return 0;
 }
 
+static inline int gicv5_wait_for_op_s(void __iomem *addr, u32 offset,
+				      const char *reg_s, u32 mask)
+{
+	void __iomem *reg = addr + offset;
+	u32 val;
+	int ret;
+
+	ret = readl_poll_timeout(reg, val, val & mask, 1, 10 * USEC_PER_MSEC);
+	if (unlikely(ret == -ETIMEDOUT)) {
+		pr_err_ratelimited("%s timeout...\n", reg_s);
+		return ret;
+	}
+
+	return 0;
+}
+
 #define gicv5_wait_for_op_atomic(base, reg, mask, val) \
 	gicv5_wait_for_op_s_atomic(base, reg, #reg, mask, val)
 
+#define gicv5_wait_for_op(base, reg, mask) \
+	gicv5_wait_for_op_s(base, reg, #reg, mask)
+
 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);
+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_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;
+};
 
 void gicv5_init_lpis(u32 max);
 void gicv5_deinit_lpis(void);
+
+int gicv5_alloc_lpi(void);
+void gicv5_free_lpi(u32 lpi);
+
+void __init gicv5_its_of_probe(struct device_node *parent);
 #endif

-- 
2.48.0



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

* [PATCH v3 25/25] arm64: Kconfig: Enable GICv5
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (23 preceding siblings ...)
  2025-05-06 12:23 ` [PATCH v3 24/25] irqchip/gic-v5: Add GICv5 ITS support Lorenzo Pieralisi
@ 2025-05-06 12:23 ` Lorenzo Pieralisi
  2025-05-06 14:05 ` [PATCH v3 00/25] Arm GICv5: Host driver implementation Marc Zyngier
  25 siblings, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-06 12:23 UTC (permalink / raw)
  To: Marc Zyngier, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, 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] 49+ messages in thread

* Re: [PATCH v3 00/25] Arm GICv5: Host driver implementation
  2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
                   ` (24 preceding siblings ...)
  2025-05-06 12:23 ` [PATCH v3 25/25] arm64: Kconfig: Enable GICv5 Lorenzo Pieralisi
@ 2025-05-06 14:05 ` Marc Zyngier
  2025-05-07  7:54   ` Lorenzo Pieralisi
  25 siblings, 1 reply; 49+ messages in thread
From: Marc Zyngier @ 2025-05-06 14:05 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Arnd Bergmann, Sascha Bischoff,
	Timothy Hayes, Liam R. Howlett, Mark Rutland, Jiri Slaby,
	linux-arm-kernel, linux-kernel, devicetree

On Tue, 06 May 2025 13:23:29 +0100,
Lorenzo Pieralisi <lpieralisi@kernel.org> wrote:
> 
> =============
> 2.5 GICv5 IWB
> =============
> 
> The IWB driver has been dropped owing to issues encountered with
> core code DOMAIN_BUS_WIRED_TO_MSI bus token handling:
> 
> https://lore.kernel.org/lkml/87tt6310hu.wl-maz@kernel.org/

This problem does not have much to do with DOMAIN_BUS_WIRED_TO_MSI.

The issues are that:

- the core code calls into the .prepare domain on a per-interrupt
  basis instead of on a per *device* basis. This is a complete
  violation of the MSI API, because .prepare is when you are supposed
  to perform resource reservation (in the GICv3 parlance, that's ITT
  allocation + MAPD command).

- the same function calls .prepare for a *single* interrupt,
  effectively telling the irqchip "my device has only one interrupt".
  Because I'm super generous (and don't like wasting precious bytes),
  I allocate 32 LPIs at the minimum. Only snag is that I could do with
  300+ interrupts, and calling repeatedly doesn't help at all, since
  we cannot *grow* an ITT.

So this code needs to be taken to the backyard and beaten into shape
before we can make use of it. My D05 (with its collection of MBIGENs)
only works by accident at the moment, as I found out yesterday, and
GICv5 IWB is in the same boat, since it reuses the msi-parent thing,
and therefore the same heuristic.

I guess not having the IWB immediately isn't too big a deal, but I
really didn't expect to find this...

Thanks,

	M.

-- 
Without deviation from the norm, progress is not possible.


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

* Re: [PATCH v3 20/25] irqchip/gic-v5: Add GICv5 PPI support
  2025-05-06 12:23 ` [PATCH v3 20/25] irqchip/gic-v5: Add GICv5 PPI support Lorenzo Pieralisi
@ 2025-05-06 15:00   ` Thomas Gleixner
  2025-05-07  8:29     ` Lorenzo Pieralisi
  2025-05-07  9:14     ` Marc Zyngier
  0 siblings, 2 replies; 49+ messages in thread
From: Thomas Gleixner @ 2025-05-06 15:00 UTC (permalink / raw)
  To: Lorenzo Pieralisi, Marc Zyngier, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

On Tue, May 06 2025 at 14:23, Lorenzo Pieralisi wrote:
> +
> +static u8 pri_bits = 5;

__ro_after_init ?

> +#define GICV5_IRQ_PRI_MASK 0x1f

Please put a new line before the #define and use a TAB between the
symbol and the value.

> +#define GICV5_IRQ_PRI_MI \
> +		(GICV5_IRQ_PRI_MASK & GENMASK(4, 5 - pri_bits))

No line break required. You have 100 characters

> +#define READ_PPI_REG(irq, reg)							\
> +	({									\
> +		u64 __ppi_val;							\
> +										\
> +		if (irq < 64)							\
> +			__ppi_val = read_sysreg_s(SYS_ICC_PPI_##reg##R0_EL1);	\
> +		else								\
> +			__ppi_val = read_sysreg_s(SYS_ICC_PPI_##reg##R1_EL1);	\
> +		__ppi_val;							\
> +	})
> +
> +#define WRITE_PPI_REG(set, irq, bit, reg)					\
> +	do {									\
> +		if (set) {							\
> +			if (irq < 64)						\
> +				write_sysreg_s(bit, SYS_ICC_PPI_S##reg##R0_EL1);\
> +			else							\
> +				write_sysreg_s(bit, SYS_ICC_PPI_S##reg##R1_EL1);\
> +		} else {							\
> +			if (irq < 64)						\
> +				write_sysreg_s(bit, SYS_ICC_PPI_C##reg##R0_EL1);\
> +			else							\
> +				write_sysreg_s(bit, SYS_ICC_PPI_C##reg##R1_EL1);\
> +		}								\
> +	} while (0)

I'm not convinced that these need to be macros.

static __always_inline u64 read_ppi_sysreg_s(unsigned int irq, const unsigned int which)
{
        switch (which) {
        case PPI_HM:
        	return irq < 64 ? read_sysreg_s(SYS_ICC_PPI_HM_R0_EL1) :
                		  read_sysreg_s(SYS_ICC_PPI_HM_R1_EL1;
        case ....:

        default:
                BUILD_BUG_ON(1);
        }
}

static __always_inline void write_ppi_sysreg_s(unsigned int irq, bool set, const unsigned int which)
{
	u64 bit = BIT_ULL(irq % 64);

        switch (which) {  
        case PPI_HM:
        	if (irq < 64)
                	write_sysreg_s(bit, SYS_ICC_PPI_HM_R0_EL1);
                else
                	write_sysreg_s(bit, SYS_ICC_PPI_HM_R1_EL1;
                return;
        case ....:

        default:
                BUILD_BUG_ON(1);
        }
}

Or something like that.

> +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 = READ_PPI_REG(d->hwirq, HM);
> +
> +	switch (type) {
> +	case IRQ_TYPE_LEVEL_HIGH:
> +	case IRQ_TYPE_LEVEL_LOW:
> +		if (((hmr >> (d->hwirq % 64)) & 0x1) != GICV5_PPI_HM_LEVEL)
> +			return -EINVAL;

Blink!

How does this test distinguish between LEVEL_LOW and LEVEL_HIGH? It only
tests for level, no? So the test is interesting at best ...

Secondly this comparison is confusing at best especially given that you
mask with a hex constant (0x1) first.

     		if (hmr & BIT_UL(d->hwirq % 64))
                	return -EINVAL;

Aside of that why do you have a set_type() function if there is no way
to set the type?

> +
> +static int gicv5_ppi_irq_get_irqchip_state(struct irq_data *d,
> +					   enum irqchip_irq_state which,
> +					   bool *val)
> +{
> +	u64 pendr, activer, hwirq_id_bit = BIT_ULL(d->hwirq % 64);
> +
> +	switch (which) {
> +	case IRQCHIP_STATE_PENDING:
> +		pendr = READ_PPI_REG(d->hwirq, SPEND);
> +
> +		*val = !!(pendr & hwirq_id_bit);
> +
> +		return 0;

		*val = !!(read_ppi_reg(d->hwirq, PPI_SPEND) & bit);
                return 0;

would take up less space and be readable.

> +	case IRQCHIP_STATE_ACTIVE:
> +		activer = READ_PPI_REG(d->hwirq, SACTIVE);
> +
> +		*val = !!(activer & hwirq_id_bit);
> +
> +		return 0;
> +	default:
> +		pr_debug("Unexpected PPI irqchip state\n");
> +	}
> +
> +	return -EINVAL;

Move the return into the default case.

> +static int __init gicv5_init_domains(struct fwnode_handle *handle)
> +{
> +	struct irq_domain *d;
> +
> +	d = irq_domain_create_linear(handle, PPI_NR, &gicv5_irq_ppi_domain_ops,
> +				     NULL);

Please use the full 100 charactes all over the place.

> +	if (!d)
> +		return -ENOMEM;
> +
> +	irq_domain_update_bus_token(d, DOMAIN_BUS_WIRED);
> +	gicv5_global_data.ppi_domain = d;
> +
> +	gicv5_global_data.fwnode = handle;

The random choices of seperating code with new lines are really
amazing.

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

        int ret = ....;

> +	if (ret)

Thanks,

        tglx


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

* Re: [PATCH v3 22/25] irqchip/gic-v5: Add GICv5 LPI/IPI support
  2025-05-06 12:23 ` [PATCH v3 22/25] irqchip/gic-v5: Add GICv5 LPI/IPI support Lorenzo Pieralisi
@ 2025-05-06 15:07   ` Thomas Gleixner
  2025-05-07  8:30     ` Lorenzo Pieralisi
  0 siblings, 1 reply; 49+ messages in thread
From: Thomas Gleixner @ 2025-05-06 15:07 UTC (permalink / raw)
  To: Lorenzo Pieralisi, Marc Zyngier, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Catalin Marinas, Will Deacon
  Cc: Arnd Bergmann, Sascha Bischoff, Timothy Hayes, Liam R. Howlett,
	Mark Rutland, Jiri Slaby, linux-arm-kernel, linux-kernel,
	devicetree, Lorenzo Pieralisi

On Tue, May 06 2025 at 14:23, Lorenzo Pieralisi wrote:
> +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;

Please don't do that. That's horrible to read. If it does not fit into a
single line, make it

       u32 a,....,h;
       u32 i,...,m;



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

* Re: [PATCH v3 01/25] dt-bindings: interrupt-controller: Add Arm GICv5
  2025-05-06 12:23 ` [PATCH v3 01/25] dt-bindings: interrupt-controller: Add Arm GICv5 Lorenzo Pieralisi
@ 2025-05-06 19:08   ` Rob Herring
  2025-05-07  8:35     ` Lorenzo Pieralisi
  0 siblings, 1 reply; 49+ messages in thread
From: Rob Herring @ 2025-05-06 19:08 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Marc Zyngier, Thomas Gleixner, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Arnd Bergmann, Sascha Bischoff,
	Timothy Hayes, Liam R. Howlett, Mark Rutland, Jiri Slaby,
	linux-arm-kernel, linux-kernel, devicetree

On Tue, May 6, 2025 at 7:24 AM Lorenzo Pieralisi <lpieralisi@kernel.org> 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 a separate schema.
>
> An IWB is connected to a single ITS, 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>
> ---
>  .../interrupt-controller/arm,gic-v5-iwb.yaml       |  76 ++++++++
>  .../bindings/interrupt-controller/arm,gic-v5.yaml  | 196 +++++++++++++++++++++
>  MAINTAINERS                                        |   7 +
>  3 files changed, 279 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5-iwb.yaml b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5-iwb.yaml
> new file mode 100644
> index 0000000000000000000000000000000000000000..b3eb89567b3457e91b93588d7db1cef69b6b9813
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5-iwb.yaml
> @@ -0,0 +1,76 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/interrupt-controller/arm,gic-v5-iwb.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: ARM Generic Interrupt Controller, version 5 Interrupt Wire Bridge (IWB)
> +
> +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
> +
> +  GICv5 has zero or more Interrupt Wire Bridges (IWB) that are responsible
> +  for translating wire signals into interrupt messages to the GICv5 ITS.
> +
> +allOf:
> +  - $ref: /schemas/interrupt-controller.yaml#
> +
> +properties:
> +  compatible:
> +    const: arm,gic-v5-iwb
> +
> +  interrupt-controller: true

Move next to #interrupt-cells

> +
> +  "#address-cells":
> +    const: 0
> +
> +  "#interrupt-cells":
> +    description: |
> +      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
> +
> +    const: 2
> +
> +  reg:

Generally, the order is compatible, reg, common properties, vendor
properties, child nodes. Related properties grouped together and
alphabetical order (ignoring '#') within common and vendor properties.

> +    items:
> +      - description: IWB control frame
> +
> +  msi-parent:
> +    maxItems: 1
> +
> +required:
> +  - compatible
> +  - reg
> +  - msi-parent

interrupt-controller and #interrupt-cells should be required

> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    interrupt-controller@2f000000 {
> +      compatible = "arm,gic-v5-iwb";
> +      #address-cells = <0>;
> +
> +      interrupt-controller;
> +      #interrupt-cells = <2>;
> +
> +      reg = <0x2f000000 0x10000>;

Use the same order as the schema.

> +
> +      msi-parent = <&its0 64>;
> +    };
> +...
> 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..1ba0a2088e6d15bacae22c9fc9eebc4ce5c51b0b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.yaml
> @@ -0,0 +1,196 @@
> +# 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)
> +
> +  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: |
> +      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
> +
> +    const: 3
> +
> +  interrupts:
> +    description:
> +      The VGIC maintenance interrupt.
> +    maxItems: 1
> +
> +required:
> +  - compatible

If you always have an IRS which you say there is, then #address-cells,
#size-cells, and ranges are required. And interrupt-controller and
#interrupt-cells.

> +
> +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:

Move after compatible

> +        minItems: 1
> +        items:
> +          - description: IRS control frame
> +          - description: IRS setlpi frame
> +
> +      cpus:
> +        description:
> +          CPUs managed by the IRS.
> +
> +      arm,iaffids:
> +        $ref: /schemas/types.yaml#/definitions/uint16-array
> +        description:
> +          Interrupt AFFinity ID (IAFFID) 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:

Move after compatible.

> +            items:
> +              - description: ITS control frame
> +              - description: ITS translate frame
> +
> +        required:
> +          - compatible
> +          - msi-controller
> +          - "#msi-cells"
> +          - reg
> +
> +    required:
> +      - compatible
> +      - reg
> +      - cpus
> +      - arm,iaffids
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    interrupt-controller {
> +      compatible = "arm,gic-v5";
> +      #interrupt-cells = <3>;
> +      #address-cells = <1>;
> +      #size-cells = <1>;
> +      ranges;
> +
> +      interrupt-controller;
> +
> +      interrupts = <1 25 4>;
> +
> +      irs@2f1a0000 {
> +        compatible = "arm,gic-v5-irs";
> +        #address-cells = <1>;
> +        #size-cells = <1>;
> +        ranges;
> +
> +        reg = <0x2f1a0000 0x10000>;  // IRS_CONFIG_FRAME for NS
> +
> +        arm,iaffids = /bits/ 16 <0 1 2 3 4 5 6 7>;
> +        cpus = <&cpu0>, <&cpu1>, <&cpu2>, <&cpu3>, <&cpu4>, <&cpu5>, <&cpu6>, <&cpu7>;
> +
> +        msi-controller@2f120000 {
> +          compatible = "arm,gic-v5-its";
> +
> +          msi-controller;
> +          #msi-cells = <1>;
> +
> +          reg = <0x2f120000 0x10000    // ITS_CONFIG_FRAME for NS

Enclose each entry in <>'s.

> +                 0x2f130000 0x10000>;  // ITS_TRANSLATE_FRAME
> +        };
> +      };
> +    };
> +...
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 96b82704950184bd71623ff41fc4df31e4c7fe87..1902291c3cccc06d27c5f79123e67774cf9a0e43 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	[flat|nested] 49+ messages in thread

* Re: [PATCH v3 00/25] Arm GICv5: Host driver implementation
  2025-05-06 14:05 ` [PATCH v3 00/25] Arm GICv5: Host driver implementation Marc Zyngier
@ 2025-05-07  7:54   ` Lorenzo Pieralisi
  2025-05-07  9:09     ` Marc Zyngier
  0 siblings, 1 reply; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-07  7:54 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Arnd Bergmann, Sascha Bischoff,
	Timothy Hayes, Liam R. Howlett, Mark Rutland, Jiri Slaby,
	linux-arm-kernel, linux-kernel, devicetree

On Tue, May 06, 2025 at 03:05:39PM +0100, Marc Zyngier wrote:
> On Tue, 06 May 2025 13:23:29 +0100,
> Lorenzo Pieralisi <lpieralisi@kernel.org> wrote:
> > 
> > =============
> > 2.5 GICv5 IWB
> > =============
> > 
> > The IWB driver has been dropped owing to issues encountered with
> > core code DOMAIN_BUS_WIRED_TO_MSI bus token handling:
> > 
> > https://lore.kernel.org/lkml/87tt6310hu.wl-maz@kernel.org/
> 
> This problem does not have much to do with DOMAIN_BUS_WIRED_TO_MSI.
> 
> The issues are that:
> 
> - the core code calls into the .prepare domain on a per-interrupt
>   basis instead of on a per *device* basis. This is a complete
>   violation of the MSI API, because .prepare is when you are supposed
>   to perform resource reservation (in the GICv3 parlance, that's ITT
>   allocation + MAPD command).
> 
> - the same function calls .prepare for a *single* interrupt,
>   effectively telling the irqchip "my device has only one interrupt".
>   Because I'm super generous (and don't like wasting precious bytes),
>   I allocate 32 LPIs at the minimum. Only snag is that I could do with
>   300+ interrupts, and calling repeatedly doesn't help at all, since
>   we cannot *grow* an ITT.

On the IWB driver code that I could not post I noticed that it is
true that the .prepare callback is called on a per-interrupt basis
but the vector size is the domain size (ie number of wires) which
is correct AFAICS, so the ITT size should be fine I don't get why
it would need to grow.

The difference with this series is that on v3 LPIs are allocated
on .prepare(), we allocate them on .alloc().

So yes, calling .prepare on a per-interrupt basis looks like a bug
but if we allow reusing a deviceID (ie the "shared" thingy) it could
be harmless.

> So this code needs to be taken to the backyard and beaten into shape
> before we can make use of it. My D05 (with its collection of MBIGENs)
> only works by accident at the moment, as I found out yesterday, and
> GICv5 IWB is in the same boat, since it reuses the msi-parent thing,
> and therefore the same heuristic.
> 
> I guess not having the IWB immediately isn't too big a deal, but I
> really didn't expect to find this...

To be honest, it was expected. We found these snags while designing
the code (that explains how IWB was structured in v1 - by the way)
but we didn't know if the behaviour above was by construction, we
always thought "we must be making a mistake".

The same goes for the fixed eventID but I would not resume that
discussion again, there are things that are impossible to
know unless you are aware of the background story behind them.

Thanks,
Lorenzo


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

* Re: [PATCH v3 20/25] irqchip/gic-v5: Add GICv5 PPI support
  2025-05-06 15:00   ` Thomas Gleixner
@ 2025-05-07  8:29     ` Lorenzo Pieralisi
  2025-05-07  9:14     ` Marc Zyngier
  1 sibling, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-07  8:29 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Arnd Bergmann, Sascha Bischoff,
	Timothy Hayes, Liam R. Howlett, Mark Rutland, Jiri Slaby,
	linux-arm-kernel, linux-kernel, devicetree

On Tue, May 06, 2025 at 05:00:31PM +0200, Thomas Gleixner wrote:
> On Tue, May 06 2025 at 14:23, Lorenzo Pieralisi wrote:
> > +
> > +static u8 pri_bits = 5;
> 
> __ro_after_init ?

Ok.

> 
> > +#define GICV5_IRQ_PRI_MASK 0x1f
> 
> Please put a new line before the #define and use a TAB between the
> symbol and the value.

Ok, sorry.

> > +#define GICV5_IRQ_PRI_MI \
> > +		(GICV5_IRQ_PRI_MASK & GENMASK(4, 5 - pri_bits))
> 
> No line break required. You have 100 characters

Right.

> > +#define READ_PPI_REG(irq, reg)							\
> > +	({									\
> > +		u64 __ppi_val;							\
> > +										\
> > +		if (irq < 64)							\
> > +			__ppi_val = read_sysreg_s(SYS_ICC_PPI_##reg##R0_EL1);	\
> > +		else								\
> > +			__ppi_val = read_sysreg_s(SYS_ICC_PPI_##reg##R1_EL1);	\
> > +		__ppi_val;							\
> > +	})
> > +
> > +#define WRITE_PPI_REG(set, irq, bit, reg)					\
> > +	do {									\
> > +		if (set) {							\
> > +			if (irq < 64)						\
> > +				write_sysreg_s(bit, SYS_ICC_PPI_S##reg##R0_EL1);\
> > +			else							\
> > +				write_sysreg_s(bit, SYS_ICC_PPI_S##reg##R1_EL1);\
> > +		} else {							\
> > +			if (irq < 64)						\
> > +				write_sysreg_s(bit, SYS_ICC_PPI_C##reg##R0_EL1);\
> > +			else							\
> > +				write_sysreg_s(bit, SYS_ICC_PPI_C##reg##R1_EL1);\
> > +		}								\
> > +	} while (0)
> 
> I'm not convinced that these need to be macros.
> 
> static __always_inline u64 read_ppi_sysreg_s(unsigned int irq, const unsigned int which)
> {
>         switch (which) {
>         case PPI_HM:
>         	return irq < 64 ? read_sysreg_s(SYS_ICC_PPI_HM_R0_EL1) :
>                 		  read_sysreg_s(SYS_ICC_PPI_HM_R1_EL1;
>         case ....:
> 
>         default:
>                 BUILD_BUG_ON(1);
>         }
> }
> 
> static __always_inline void write_ppi_sysreg_s(unsigned int irq, bool set, const unsigned int which)
> {
> 	u64 bit = BIT_ULL(irq % 64);
> 
>         switch (which) {  
>         case PPI_HM:
>         	if (irq < 64)
>                 	write_sysreg_s(bit, SYS_ICC_PPI_HM_R0_EL1);
>                 else
>                 	write_sysreg_s(bit, SYS_ICC_PPI_HM_R1_EL1;
>                 return;
>         case ....:
> 
>         default:
>                 BUILD_BUG_ON(1);
>         }
> }
> 
> Or something like that.

Done.

> > +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 = READ_PPI_REG(d->hwirq, HM);
> > +
> > +	switch (type) {
> > +	case IRQ_TYPE_LEVEL_HIGH:
> > +	case IRQ_TYPE_LEVEL_LOW:
> > +		if (((hmr >> (d->hwirq % 64)) & 0x1) != GICV5_PPI_HM_LEVEL)
> > +			return -EINVAL;
> 
> Blink!
> 
> How does this test distinguish between LEVEL_LOW and LEVEL_HIGH? It only
> tests for level, no? So the test is interesting at best ...

There is no HIGH/LOW concept for level interrupts in the architecture,
level interrupts are asserted/de-asserted. On top of that, as you
already noticed, for PPIs this can't even be changed so this function
is utterly pointless.

> Secondly this comparison is confusing at best especially given that you
> mask with a hex constant (0x1) first.
> 
>      		if (hmr & BIT_UL(d->hwirq % 64))
>                 	return -EINVAL;
> 
> Aside of that why do you have a set_type() function if there is no way
> to set the type?

Yes, that's useless, the kernel is not there to validate firmware, I
will remove it.

> > +
> > +static int gicv5_ppi_irq_get_irqchip_state(struct irq_data *d,
> > +					   enum irqchip_irq_state which,
> > +					   bool *val)
> > +{
> > +	u64 pendr, activer, hwirq_id_bit = BIT_ULL(d->hwirq % 64);
> > +
> > +	switch (which) {
> > +	case IRQCHIP_STATE_PENDING:
> > +		pendr = READ_PPI_REG(d->hwirq, SPEND);
> > +
> > +		*val = !!(pendr & hwirq_id_bit);
> > +
> > +		return 0;
> 
> 		*val = !!(read_ppi_reg(d->hwirq, PPI_SPEND) & bit);
>                 return 0;
> 
> would take up less space and be readable.

Ok done.

> > +	case IRQCHIP_STATE_ACTIVE:
> > +		activer = READ_PPI_REG(d->hwirq, SACTIVE);
> > +
> > +		*val = !!(activer & hwirq_id_bit);
> > +
> > +		return 0;
> > +	default:
> > +		pr_debug("Unexpected PPI irqchip state\n");
> > +	}
> > +
> > +	return -EINVAL;
> 
> Move the return into the default case.

Ok.

> > +static int __init gicv5_init_domains(struct fwnode_handle *handle)
> > +{
> > +	struct irq_domain *d;
> > +
> > +	d = irq_domain_create_linear(handle, PPI_NR, &gicv5_irq_ppi_domain_ops,
> > +				     NULL);
> 
> Please use the full 100 charactes all over the place.

Ok.

> > +	if (!d)
> > +		return -ENOMEM;
> > +
> > +	irq_domain_update_bus_token(d, DOMAIN_BUS_WIRED);
> > +	gicv5_global_data.ppi_domain = d;
> > +
> > +	gicv5_global_data.fwnode = handle;
> 
> The random choices of seperating code with new lines are really
> amazing.

I separated code that initializes the domain from one that initialises
fwnode - it made sense to *me*, I don't know what makes sense to others
unless there are rules (or a script/bot reformatting the code for a
given subsytem) that one can follow I am afraid.

> > +static int __init gicv5_of_init(struct device_node *node, struct device_node *parent)
> > +{
> > +	int ret;
> > +
> > +	ret = gicv5_init_domains(&node->fwnode);
> 
>         int ret = ....;

Ok.

> > +	if (ret)
> 
> Thanks,
> 
>         tglx

Thanks for reviewing.

Lorenzo


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

* Re: [PATCH v3 22/25] irqchip/gic-v5: Add GICv5 LPI/IPI support
  2025-05-06 15:07   ` Thomas Gleixner
@ 2025-05-07  8:30     ` Lorenzo Pieralisi
  0 siblings, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-07  8:30 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Arnd Bergmann, Sascha Bischoff,
	Timothy Hayes, Liam R. Howlett, Mark Rutland, Jiri Slaby,
	linux-arm-kernel, linux-kernel, devicetree

On Tue, May 06, 2025 at 05:07:24PM +0200, Thomas Gleixner wrote:
> On Tue, May 06 2025 at 14:23, Lorenzo Pieralisi wrote:
> > +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;
> 
> Please don't do that. That's horrible to read. If it does not fit into a
> single line, make it
> 
>        u32 a,....,h;
>        u32 i,...,m;

Done.

Thanks,
Lorenzo


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

* Re: [PATCH v3 01/25] dt-bindings: interrupt-controller: Add Arm GICv5
  2025-05-06 19:08   ` Rob Herring
@ 2025-05-07  8:35     ` Lorenzo Pieralisi
  0 siblings, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-07  8:35 UTC (permalink / raw)
  To: Rob Herring
  Cc: Marc Zyngier, Thomas Gleixner, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Arnd Bergmann, Sascha Bischoff,
	Timothy Hayes, Liam R. Howlett, Mark Rutland, Jiri Slaby,
	linux-arm-kernel, linux-kernel, devicetree

On Tue, May 06, 2025 at 02:08:00PM -0500, Rob Herring wrote:
> On Tue, May 6, 2025 at 7:24 AM Lorenzo Pieralisi <lpieralisi@kernel.org> 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 a separate schema.
> >
> > An IWB is connected to a single ITS, 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>
> > ---
> >  .../interrupt-controller/arm,gic-v5-iwb.yaml       |  76 ++++++++
> >  .../bindings/interrupt-controller/arm,gic-v5.yaml  | 196 +++++++++++++++++++++
> >  MAINTAINERS                                        |   7 +
> >  3 files changed, 279 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5-iwb.yaml b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5-iwb.yaml
> > new file mode 100644
> > index 0000000000000000000000000000000000000000..b3eb89567b3457e91b93588d7db1cef69b6b9813
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5-iwb.yaml
> > @@ -0,0 +1,76 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/interrupt-controller/arm,gic-v5-iwb.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: ARM Generic Interrupt Controller, version 5 Interrupt Wire Bridge (IWB)
> > +
> > +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
> > +
> > +  GICv5 has zero or more Interrupt Wire Bridges (IWB) that are responsible
> > +  for translating wire signals into interrupt messages to the GICv5 ITS.
> > +
> > +allOf:
> > +  - $ref: /schemas/interrupt-controller.yaml#
> > +
> > +properties:
> > +  compatible:
> > +    const: arm,gic-v5-iwb
> > +
> > +  interrupt-controller: true
> 
> Move next to #interrupt-cells

I will move it below #interrupt-cells (ie alphabetical order ignoring
'#'), is that OK (it is a bit counterintuitive, that's why I am asking) ?

Same goes for msi-controller after #msi-cells.

> > +
> > +  "#address-cells":
> > +    const: 0
> > +
> > +  "#interrupt-cells":
> > +    description: |
> > +      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
> > +
> > +    const: 2
> > +
> > +  reg:
> 
> Generally, the order is compatible, reg, common properties, vendor
> properties, child nodes. Related properties grouped together and
> alphabetical order (ignoring '#') within common and vendor properties.

Updated, noted.

> > +    items:
> > +      - description: IWB control frame
> > +
> > +  msi-parent:
> > +    maxItems: 1
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - msi-parent
> 
> interrupt-controller and #interrupt-cells should be required

Done.

> > +
> > +additionalProperties: false
> > +
> > +examples:
> > +  - |
> > +    interrupt-controller@2f000000 {
> > +      compatible = "arm,gic-v5-iwb";
> > +      #address-cells = <0>;
> > +
> > +      interrupt-controller;
> > +      #interrupt-cells = <2>;
> > +
> > +      reg = <0x2f000000 0x10000>;
> 
> Use the same order as the schema.

Done.

> 
> > +
> > +      msi-parent = <&its0 64>;
> > +    };
> > +...
> > 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..1ba0a2088e6d15bacae22c9fc9eebc4ce5c51b0b
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.yaml
> > @@ -0,0 +1,196 @@
> > +# 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)
> > +
> > +  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: |
> > +      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
> > +
> > +    const: 3
> > +
> > +  interrupts:
> > +    description:
> > +      The VGIC maintenance interrupt.
> > +    maxItems: 1
> > +
> > +required:
> > +  - compatible
> 
> If you always have an IRS which you say there is, then #address-cells,
> #size-cells, and ranges are required. And interrupt-controller and
> #interrupt-cells.

Right, done.

> > +
> > +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:
> 
> Move after compatible

Done.

> > +        minItems: 1
> > +        items:
> > +          - description: IRS control frame
> > +          - description: IRS setlpi frame
> > +
> > +      cpus:
> > +        description:
> > +          CPUs managed by the IRS.
> > +
> > +      arm,iaffids:
> > +        $ref: /schemas/types.yaml#/definitions/uint16-array
> > +        description:
> > +          Interrupt AFFinity ID (IAFFID) 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:
> 
> Move after compatible.

Done.

> > +            items:
> > +              - description: ITS control frame
> > +              - description: ITS translate frame
> > +
> > +        required:
> > +          - compatible
> > +          - msi-controller
> > +          - "#msi-cells"
> > +          - reg
> > +
> > +    required:
> > +      - compatible
> > +      - reg
> > +      - cpus
> > +      - arm,iaffids
> > +
> > +additionalProperties: false
> > +
> > +examples:
> > +  - |
> > +    interrupt-controller {
> > +      compatible = "arm,gic-v5";
> > +      #interrupt-cells = <3>;
> > +      #address-cells = <1>;
> > +      #size-cells = <1>;
> > +      ranges;
> > +
> > +      interrupt-controller;
> > +
> > +      interrupts = <1 25 4>;
> > +
> > +      irs@2f1a0000 {
> > +        compatible = "arm,gic-v5-irs";
> > +        #address-cells = <1>;
> > +        #size-cells = <1>;
> > +        ranges;
> > +
> > +        reg = <0x2f1a0000 0x10000>;  // IRS_CONFIG_FRAME for NS
> > +
> > +        arm,iaffids = /bits/ 16 <0 1 2 3 4 5 6 7>;
> > +        cpus = <&cpu0>, <&cpu1>, <&cpu2>, <&cpu3>, <&cpu4>, <&cpu5>, <&cpu6>, <&cpu7>;
> > +
> > +        msi-controller@2f120000 {
> > +          compatible = "arm,gic-v5-its";
> > +
> > +          msi-controller;
> > +          #msi-cells = <1>;
> > +
> > +          reg = <0x2f120000 0x10000    // ITS_CONFIG_FRAME for NS
> 
> Enclose each entry in <>'s.

Done.

Thanks a lot,
Lorenzo

> > +                 0x2f130000 0x10000>;  // ITS_TRANSLATE_FRAME
> > +        };
> > +      };
> > +    };
> > +...
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 96b82704950184bd71623ff41fc4df31e4c7fe87..1902291c3cccc06d27c5f79123e67774cf9a0e43 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	[flat|nested] 49+ messages in thread

* Re: [PATCH v3 00/25] Arm GICv5: Host driver implementation
  2025-05-07  7:54   ` Lorenzo Pieralisi
@ 2025-05-07  9:09     ` Marc Zyngier
  2025-05-07 10:01       ` Lorenzo Pieralisi
  0 siblings, 1 reply; 49+ messages in thread
From: Marc Zyngier @ 2025-05-07  9:09 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Arnd Bergmann, Sascha Bischoff,
	Timothy Hayes, Liam R. Howlett, Mark Rutland, Jiri Slaby,
	linux-arm-kernel, linux-kernel, devicetree

On Wed, 07 May 2025 08:54:36 +0100,
Lorenzo Pieralisi <lpieralisi@kernel.org> wrote:
> 
> On Tue, May 06, 2025 at 03:05:39PM +0100, Marc Zyngier wrote:
> > On Tue, 06 May 2025 13:23:29 +0100,
> > Lorenzo Pieralisi <lpieralisi@kernel.org> wrote:
> > > 
> > > =============
> > > 2.5 GICv5 IWB
> > > =============
> > > 
> > > The IWB driver has been dropped owing to issues encountered with
> > > core code DOMAIN_BUS_WIRED_TO_MSI bus token handling:
> > > 
> > > https://lore.kernel.org/lkml/87tt6310hu.wl-maz@kernel.org/
> > 
> > This problem does not have much to do with DOMAIN_BUS_WIRED_TO_MSI.
> > 
> > The issues are that:
> > 
> > - the core code calls into the .prepare domain on a per-interrupt
> >   basis instead of on a per *device* basis. This is a complete
> >   violation of the MSI API, because .prepare is when you are supposed
> >   to perform resource reservation (in the GICv3 parlance, that's ITT
> >   allocation + MAPD command).
> > 
> > - the same function calls .prepare for a *single* interrupt,
> >   effectively telling the irqchip "my device has only one interrupt".
> >   Because I'm super generous (and don't like wasting precious bytes),
> >   I allocate 32 LPIs at the minimum. Only snag is that I could do with
> >   300+ interrupts, and calling repeatedly doesn't help at all, since
> >   we cannot *grow* an ITT.
> 
> On the IWB driver code that I could not post I noticed that it is
> true that the .prepare callback is called on a per-interrupt basis
> but the vector size is the domain size (ie number of wires) which
> is correct AFAICS, so the ITT size should be fine I don't get why
> it would need to grow.

Look again. The only reason you are getting something that *looks*
correct is that its_pmsi_prepare() has this nugget:

	/* Allocate at least 32 MSIs, and always as a power of 2 */
	nvec = max_t(int, 32, roundup_pow_of_two(nvec));

and that the IWB is, conveniently, in sets of 32. However, the caller
of this function (__msi_domain_alloc_irqs()) passes a  nvec value that
is always exactly *1* when allocating an interrupt.

So you're just lucky that I picked a minimum ITT size that matches the
IWB on your model. Configure your IWB to be, let's say, 256 interrupts
and use the last one, and you'll have a very different behaviour.

> The difference with this series is that on v3 LPIs are allocated
> on .prepare(), we allocate them on .alloc().

Absolutely not. Even on v3, we never allocate LPIs in .prepare(). We
allocate the ITT, perform the MAPD, and that's it. That's why it's
called *prepare*.

> So yes, calling .prepare on a per-interrupt basis looks like a bug
> but if we allow reusing a deviceID (ie the "shared" thingy) it could
> be harmless.

Harmless? No. It is really *bad*. It means you lose any sort of sane
tracking of what owns the ITT and how you can free things. Seeing a
devid twice is the admission that we have no idea of what is going on.

GICv3 is already in that sorry state, but I am hopeful that GICv5 can
be a bit less crap.

> > So this code needs to be taken to the backyard and beaten into shape
> > before we can make use of it. My D05 (with its collection of MBIGENs)
> > only works by accident at the moment, as I found out yesterday, and
> > GICv5 IWB is in the same boat, since it reuses the msi-parent thing,
> > and therefore the same heuristic.
> > 
> > I guess not having the IWB immediately isn't too big a deal, but I
> > really didn't expect to find this...
> 
> To be honest, it was expected. We found these snags while designing
> the code (that explains how IWB was structured in v1 - by the way)
> but we didn't know if the behaviour above was by construction, we
> always thought "we must be making a mistake".

Then why didn't you report it? We could have caught this very early
on, before the fscked-up code was in a stable release...

	M.

-- 
Without deviation from the norm, progress is not possible.


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

* Re: [PATCH v3 20/25] irqchip/gic-v5: Add GICv5 PPI support
  2025-05-06 15:00   ` Thomas Gleixner
  2025-05-07  8:29     ` Lorenzo Pieralisi
@ 2025-05-07  9:14     ` Marc Zyngier
  2025-05-07 13:42       ` Thomas Gleixner
  1 sibling, 1 reply; 49+ messages in thread
From: Marc Zyngier @ 2025-05-07  9:14 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Lorenzo Pieralisi, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Arnd Bergmann, Sascha Bischoff,
	Timothy Hayes, Liam R. Howlett, Mark Rutland, Jiri Slaby,
	linux-arm-kernel, linux-kernel, devicetree

On Tue, 06 May 2025 16:00:31 +0100,
Thomas Gleixner <tglx@linutronix.de> wrote:
> 
> How does this test distinguish between LEVEL_LOW and LEVEL_HIGH? It only
> tests for level, no? So the test is interesting at best ...

There is no distinction between HIGH and LOW, RISING and FALLING, in
any revision of the GIC architecture.

	M.

-- 
Without deviation from the norm, progress is not possible.


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

* Re: [PATCH v3 00/25] Arm GICv5: Host driver implementation
  2025-05-07  9:09     ` Marc Zyngier
@ 2025-05-07 10:01       ` Lorenzo Pieralisi
  0 siblings, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-07 10:01 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Arnd Bergmann, Sascha Bischoff,
	Timothy Hayes, Liam R. Howlett, Mark Rutland, Jiri Slaby,
	linux-arm-kernel, linux-kernel, devicetree

On Wed, May 07, 2025 at 10:09:44AM +0100, Marc Zyngier wrote:
> On Wed, 07 May 2025 08:54:36 +0100,
> Lorenzo Pieralisi <lpieralisi@kernel.org> wrote:
> > 
> > On Tue, May 06, 2025 at 03:05:39PM +0100, Marc Zyngier wrote:
> > > On Tue, 06 May 2025 13:23:29 +0100,
> > > Lorenzo Pieralisi <lpieralisi@kernel.org> wrote:
> > > > 
> > > > =============
> > > > 2.5 GICv5 IWB
> > > > =============
> > > > 
> > > > The IWB driver has been dropped owing to issues encountered with
> > > > core code DOMAIN_BUS_WIRED_TO_MSI bus token handling:
> > > > 
> > > > https://lore.kernel.org/lkml/87tt6310hu.wl-maz@kernel.org/
> > > 
> > > This problem does not have much to do with DOMAIN_BUS_WIRED_TO_MSI.
> > > 
> > > The issues are that:
> > > 
> > > - the core code calls into the .prepare domain on a per-interrupt
> > >   basis instead of on a per *device* basis. This is a complete
> > >   violation of the MSI API, because .prepare is when you are supposed
> > >   to perform resource reservation (in the GICv3 parlance, that's ITT
> > >   allocation + MAPD command).
> > > 
> > > - the same function calls .prepare for a *single* interrupt,
> > >   effectively telling the irqchip "my device has only one interrupt".
> > >   Because I'm super generous (and don't like wasting precious bytes),
> > >   I allocate 32 LPIs at the minimum. Only snag is that I could do with
> > >   300+ interrupts, and calling repeatedly doesn't help at all, since
> > >   we cannot *grow* an ITT.
> > 
> > On the IWB driver code that I could not post I noticed that it is
> > true that the .prepare callback is called on a per-interrupt basis
> > but the vector size is the domain size (ie number of wires) which
> > is correct AFAICS, so the ITT size should be fine I don't get why
> > it would need to grow.
> 
> Look again. The only reason you are getting something that *looks*
> correct is that its_pmsi_prepare() has this nugget:
> 
> 	/* Allocate at least 32 MSIs, and always as a power of 2 */
> 	nvec = max_t(int, 32, roundup_pow_of_two(nvec));
> 
> and that the IWB is, conveniently, in sets of 32. However, the caller
> of this function (__msi_domain_alloc_irqs()) passes a  nvec value that
> is always exactly *1* when allocating an interrupt.

nvec is one but this does not work for the reason above, it works
because of AFAICS (for the IWB set-up I have):

	msi_info = msi_get_domain_info(domain);
	if (msi_info->hwsize > nvec)
		nvec = msi_info->hwsize;

> 
> So you're just lucky that I picked a minimum ITT size that matches the
> IWB on your model.

Not really, we test with wires above 32, we end up calling .prepare()
with the precise number of wires, don't know why that does not work for
the MBIgen (possibly because the interrupt-controller platform devices
are children of the "main" MBIgen platform device ? The IWB one is created
by OF code, MBIgen has to create children, maybe that's what is
going wrong with the device/domain hierarchy ?).

> Configure your IWB to be, let's say, 256 interrupts and use the last
> one, and you'll have a very different behaviour.

See above.

> > The difference with this series is that on v3 LPIs are allocated
> > on .prepare(), we allocate them on .alloc().
> 
> Absolutely not. Even on v3, we never allocate LPIs in .prepare(). We
> allocate the ITT, perform the MAPD, and that's it. That's why it's
> called *prepare*.

I supposed that's what its_lpi_alloc() does in its_create_device() but
OK, won't mention that any further.

> > So yes, calling .prepare on a per-interrupt basis looks like a bug
> > but if we allow reusing a deviceID (ie the "shared" thingy) it could
> > be harmless.
> 
> Harmless? No. It is really *bad*. It means you lose any sort of sane
> tracking of what owns the ITT and how you can free things. Seeing a
> devid twice is the admission that we have no idea of what is going on.
> 
> GICv3 is already in that sorry state, but I am hopeful that GICv5 can
> be a bit less crap.

Well, GICv5 will have to cope with designs, hopefully deviceIDs sharing
is a thing of the past I am not eulogizing the concept :)

> > > So this code needs to be taken to the backyard and beaten into shape
> > > before we can make use of it. My D05 (with its collection of MBIGENs)
> > > only works by accident at the moment, as I found out yesterday, and
> > > GICv5 IWB is in the same boat, since it reuses the msi-parent thing,
> > > and therefore the same heuristic.
> > > 
> > > I guess not having the IWB immediately isn't too big a deal, but I
> > > really didn't expect to find this...
> > 
> > To be honest, it was expected. We found these snags while designing
> > the code (that explains how IWB was structured in v1 - by the way)
> > but we didn't know if the behaviour above was by construction, we
> > always thought "we must be making a mistake".
> 
> Then why didn't you report it? We could have caught this very early
> on, before the fscked-up code was in a stable release...

We spotted it late March - planned to discuss the IWB design while
reviewing v5.

Thanks,
Lorenzo


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

* Re: [PATCH v3 20/25] irqchip/gic-v5: Add GICv5 PPI support
  2025-05-07  9:14     ` Marc Zyngier
@ 2025-05-07 13:42       ` Thomas Gleixner
  2025-05-07 13:52         ` Marc Zyngier
  0 siblings, 1 reply; 49+ messages in thread
From: Thomas Gleixner @ 2025-05-07 13:42 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Lorenzo Pieralisi, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Arnd Bergmann, Sascha Bischoff,
	Timothy Hayes, Liam R. Howlett, Mark Rutland, Jiri Slaby,
	linux-arm-kernel, linux-kernel, devicetree

On Wed, May 07 2025 at 10:14, Marc Zyngier wrote:
> On Tue, 06 May 2025 16:00:31 +0100,
> Thomas Gleixner <tglx@linutronix.de> wrote:
>> 
>> How does this test distinguish between LEVEL_LOW and LEVEL_HIGH? It only
>> tests for level, no? So the test is interesting at best ...
>
> There is no distinction between HIGH and LOW, RISING and FALLING, in
> any revision of the GIC architecture.

Then pretending that there is a set_type() functionality is pretty daft


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

* Re: [PATCH v3 20/25] irqchip/gic-v5: Add GICv5 PPI support
  2025-05-07 13:42       ` Thomas Gleixner
@ 2025-05-07 13:52         ` Marc Zyngier
  2025-05-07 14:57           ` Thomas Gleixner
  0 siblings, 1 reply; 49+ messages in thread
From: Marc Zyngier @ 2025-05-07 13:52 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Lorenzo Pieralisi, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Arnd Bergmann, Sascha Bischoff,
	Timothy Hayes, Liam R. Howlett, Mark Rutland, Jiri Slaby,
	linux-arm-kernel, linux-kernel, devicetree

On Wed, 07 May 2025 14:42:42 +0100,
Thomas Gleixner <tglx@linutronix.de> wrote:
> 
> On Wed, May 07 2025 at 10:14, Marc Zyngier wrote:
> > On Tue, 06 May 2025 16:00:31 +0100,
> > Thomas Gleixner <tglx@linutronix.de> wrote:
> >> 
> >> How does this test distinguish between LEVEL_LOW and LEVEL_HIGH? It only
> >> tests for level, no? So the test is interesting at best ...
> >
> > There is no distinction between HIGH and LOW, RISING and FALLING, in
> > any revision of the GIC architecture.
> 
> Then pretending that there is a set_type() functionality is pretty daft

You still need to distinguish between level and edge when this is
programmable (which is the case for a subset of the PPIs).

	M.

-- 
Without deviation from the norm, progress is not possible.


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

* Re: [PATCH v3 20/25] irqchip/gic-v5: Add GICv5 PPI support
  2025-05-07 13:52         ` Marc Zyngier
@ 2025-05-07 14:57           ` Thomas Gleixner
  2025-05-07 15:48             ` Lorenzo Pieralisi
  2025-05-08  7:42             ` Lorenzo Pieralisi
  0 siblings, 2 replies; 49+ messages in thread
From: Thomas Gleixner @ 2025-05-07 14:57 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Lorenzo Pieralisi, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Arnd Bergmann, Sascha Bischoff,
	Timothy Hayes, Liam R. Howlett, Mark Rutland, Jiri Slaby,
	linux-arm-kernel, linux-kernel, devicetree

On Wed, May 07 2025 at 14:52, Marc Zyngier wrote:
> On Wed, 07 May 2025 14:42:42 +0100,
> Thomas Gleixner <tglx@linutronix.de> wrote:
>> 
>> On Wed, May 07 2025 at 10:14, Marc Zyngier wrote:
>> > On Tue, 06 May 2025 16:00:31 +0100,
>> > Thomas Gleixner <tglx@linutronix.de> wrote:
>> >> 
>> >> How does this test distinguish between LEVEL_LOW and LEVEL_HIGH? It only
>> >> tests for level, no? So the test is interesting at best ...
>> >
>> > There is no distinction between HIGH and LOW, RISING and FALLING, in
>> > any revision of the GIC architecture.
>> 
>> Then pretending that there is a set_type() functionality is pretty daft
>
> You still need to distinguish between level and edge when this is
> programmable (which is the case for a subset of the PPIs).

Fair enough, but can we please add a comment to this function which
explains this oddity.

Thanks,

        tglx


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

* Re: [PATCH v3 20/25] irqchip/gic-v5: Add GICv5 PPI support
  2025-05-07 14:57           ` Thomas Gleixner
@ 2025-05-07 15:48             ` Lorenzo Pieralisi
  2025-05-08  7:42             ` Lorenzo Pieralisi
  1 sibling, 0 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-07 15:48 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Arnd Bergmann, Sascha Bischoff,
	Timothy Hayes, Liam R. Howlett, Mark Rutland, Jiri Slaby,
	linux-arm-kernel, linux-kernel, devicetree

On Wed, May 07, 2025 at 04:57:07PM +0200, Thomas Gleixner wrote:
> On Wed, May 07 2025 at 14:52, Marc Zyngier wrote:
> > On Wed, 07 May 2025 14:42:42 +0100,
> > Thomas Gleixner <tglx@linutronix.de> wrote:
> >> 
> >> On Wed, May 07 2025 at 10:14, Marc Zyngier wrote:
> >> > On Tue, 06 May 2025 16:00:31 +0100,
> >> > Thomas Gleixner <tglx@linutronix.de> wrote:
> >> >> 
> >> >> How does this test distinguish between LEVEL_LOW and LEVEL_HIGH? It only
> >> >> tests for level, no? So the test is interesting at best ...
> >> >
> >> > There is no distinction between HIGH and LOW, RISING and FALLING, in
> >> > any revision of the GIC architecture.
> >> 
> >> Then pretending that there is a set_type() functionality is pretty daft
> >
> > You still need to distinguish between level and edge when this is
> > programmable (which is the case for a subset of the PPIs).
> 
> Fair enough, but can we please add a comment to this function which
> explains this oddity.

GICv5 PPIs handling mode is fixed (ie ICC_PPI_HMR<n>_EL1 is RO), can't be
programmed, I removed the irq_set_type() function (for the PPI irqchip).

Lorenzo


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

* Re: [PATCH v3 20/25] irqchip/gic-v5: Add GICv5 PPI support
  2025-05-07 14:57           ` Thomas Gleixner
  2025-05-07 15:48             ` Lorenzo Pieralisi
@ 2025-05-08  7:42             ` Lorenzo Pieralisi
  2025-05-08  8:42               ` Marc Zyngier
  1 sibling, 1 reply; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-08  7:42 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Arnd Bergmann, Sascha Bischoff,
	Timothy Hayes, Liam R. Howlett, Mark Rutland, Jiri Slaby,
	linux-arm-kernel, linux-kernel, devicetree

On Wed, May 07, 2025 at 04:57:07PM +0200, Thomas Gleixner wrote:
> On Wed, May 07 2025 at 14:52, Marc Zyngier wrote:
> > On Wed, 07 May 2025 14:42:42 +0100,
> > Thomas Gleixner <tglx@linutronix.de> wrote:
> >> 
> >> On Wed, May 07 2025 at 10:14, Marc Zyngier wrote:
> >> > On Tue, 06 May 2025 16:00:31 +0100,
> >> > Thomas Gleixner <tglx@linutronix.de> wrote:
> >> >> 
> >> >> How does this test distinguish between LEVEL_LOW and LEVEL_HIGH? It only
> >> >> tests for level, no? So the test is interesting at best ...
> >> >
> >> > There is no distinction between HIGH and LOW, RISING and FALLING, in
> >> > any revision of the GIC architecture.
> >> 
> >> Then pretending that there is a set_type() functionality is pretty daft
> >
> > You still need to distinguish between level and edge when this is
> > programmable (which is the case for a subset of the PPIs).
> 
> Fair enough, but can we please add a comment to this function which
> explains this oddity.

Getting back to this, I would need your/Marc's input on this.

I think it is fair to remove the irq_set_type() irqchip callback for
GICv5 PPIs because there is nothing to set, as I said handling mode
for these IRQs is fixed. I don't think this can cause any trouble
(IIUC a value within the IRQF_TRIGGER_MASK should be set on requesting
an IRQ to "force" the trigger to be programmed and even then core code
would not fail if the irq_set_type() irqchip callback is not
implemented).

I am thinking about *existing* drivers that request GICv3 PPIs with
values in IRQF_TRIGGER_MASK set (are there any ? Don't think so but you
know better than I do), when we switch over to GICv5 we would have no
irq_set_type() callback for PPIs but I think we are still fine, not
implementing irqchip.irq_set_type() is correct IMO.

On the other hand, given that on GICv5 PPI handling mode is fixed,
do you think that in the ppi_irq_domain_ops.translate() callback,
I should check the type the firmware provided and fail the translation
if it does not match the HW hardcoded value ?

Obviously if firmware exposes the wrong type that's a firmware bug
but I was wondering whether it is better to fail the firmware-to-Linux
IRQ translation if the firmware provided type is wrong rather than carry
on pretending that the type is correct (I was abusing the irq_set_type()
callback to do just that - namely, check that the type provided by
firmware matches HW but I think that's the wrong place to put it).

Thanks !
Lorenzo


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

* Re: [PATCH v3 20/25] irqchip/gic-v5: Add GICv5 PPI support
  2025-05-08  7:42             ` Lorenzo Pieralisi
@ 2025-05-08  8:42               ` Marc Zyngier
  2025-05-08 10:44                 ` Lorenzo Pieralisi
  0 siblings, 1 reply; 49+ messages in thread
From: Marc Zyngier @ 2025-05-08  8:42 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Arnd Bergmann, Sascha Bischoff,
	Timothy Hayes, Liam R. Howlett, Mark Rutland, Jiri Slaby,
	linux-arm-kernel, linux-kernel, devicetree

On Thu, 08 May 2025 08:42:41 +0100,
Lorenzo Pieralisi <lpieralisi@kernel.org> wrote:
> 
> On Wed, May 07, 2025 at 04:57:07PM +0200, Thomas Gleixner wrote:
> > On Wed, May 07 2025 at 14:52, Marc Zyngier wrote:
> > > On Wed, 07 May 2025 14:42:42 +0100,
> > > Thomas Gleixner <tglx@linutronix.de> wrote:
> > >> 
> > >> On Wed, May 07 2025 at 10:14, Marc Zyngier wrote:
> > >> > On Tue, 06 May 2025 16:00:31 +0100,
> > >> > Thomas Gleixner <tglx@linutronix.de> wrote:
> > >> >> 
> > >> >> How does this test distinguish between LEVEL_LOW and LEVEL_HIGH? It only
> > >> >> tests for level, no? So the test is interesting at best ...
> > >> >
> > >> > There is no distinction between HIGH and LOW, RISING and FALLING, in
> > >> > any revision of the GIC architecture.
> > >> 
> > >> Then pretending that there is a set_type() functionality is pretty daft
> > >
> > > You still need to distinguish between level and edge when this is
> > > programmable (which is the case for a subset of the PPIs).
> > 
> > Fair enough, but can we please add a comment to this function which
> > explains this oddity.
> 
> Getting back to this, I would need your/Marc's input on this.
> 
> I think it is fair to remove the irq_set_type() irqchip callback for
> GICv5 PPIs because there is nothing to set, as I said handling mode
> for these IRQs is fixed. I don't think this can cause any trouble
> (IIUC a value within the IRQF_TRIGGER_MASK should be set on requesting
> an IRQ to "force" the trigger to be programmed and even then core code
> would not fail if the irq_set_type() irqchip callback is not
> implemented).
> 
> I am thinking about *existing* drivers that request GICv3 PPIs with
> values in IRQF_TRIGGER_MASK set (are there any ? Don't think so but you
> know better than I do), when we switch over to GICv5 we would have no
> irq_set_type() callback for PPIs but I think we are still fine, not
> implementing irqchip.irq_set_type() is correct IMO.

Nobody seems to use a hardcoded trigger (well, there is one exception,
but that's to paper over a firmware bug).

> On the other hand, given that on GICv5 PPI handling mode is fixed,
> do you think that in the ppi_irq_domain_ops.translate() callback,
> I should check the type the firmware provided and fail the translation
> if it does not match the HW hardcoded value ?

Why? The fact that the firmware is wrong doesn't change the hardware
integration. It just indicates that whoever wrote the firmware didn't
read the documentation.

Even more, I wonder what the benefit of having that information in the
firmware tables if the only thing that matters in the immutable HW
view. Yes, having it in the DT/ACPI simplifies the job of the kernel
(only one format to parse). But it is overall useless information.

> Obviously if firmware exposes the wrong type that's a firmware bug
> but I was wondering whether it is better to fail the firmware-to-Linux
> IRQ translation if the firmware provided type is wrong rather than carry
> on pretending that the type is correct (I was abusing the irq_set_type()
> callback to do just that - namely, check that the type provided by
> firmware matches HW but I think that's the wrong place to put it).

I don't think there is anything to do. Worse case, you spit a
pr_warn_once() and carry on.

Thanks,

	M.

-- 
Without deviation from the norm, progress is not possible.


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

* Re: [PATCH v3 20/25] irqchip/gic-v5: Add GICv5 PPI support
  2025-05-08  8:42               ` Marc Zyngier
@ 2025-05-08 10:44                 ` Lorenzo Pieralisi
  2025-05-09  8:07                   ` Lorenzo Pieralisi
  2025-05-12  8:27                   ` Marc Zyngier
  0 siblings, 2 replies; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-08 10:44 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Arnd Bergmann, Sascha Bischoff,
	Timothy Hayes, Liam R. Howlett, Mark Rutland, Jiri Slaby,
	linux-arm-kernel, linux-kernel, devicetree

On Thu, May 08, 2025 at 09:42:27AM +0100, Marc Zyngier wrote:
> On Thu, 08 May 2025 08:42:41 +0100,
> Lorenzo Pieralisi <lpieralisi@kernel.org> wrote:
> > 
> > On Wed, May 07, 2025 at 04:57:07PM +0200, Thomas Gleixner wrote:
> > > On Wed, May 07 2025 at 14:52, Marc Zyngier wrote:
> > > > On Wed, 07 May 2025 14:42:42 +0100,
> > > > Thomas Gleixner <tglx@linutronix.de> wrote:
> > > >> 
> > > >> On Wed, May 07 2025 at 10:14, Marc Zyngier wrote:
> > > >> > On Tue, 06 May 2025 16:00:31 +0100,
> > > >> > Thomas Gleixner <tglx@linutronix.de> wrote:
> > > >> >> 
> > > >> >> How does this test distinguish between LEVEL_LOW and LEVEL_HIGH? It only
> > > >> >> tests for level, no? So the test is interesting at best ...
> > > >> >
> > > >> > There is no distinction between HIGH and LOW, RISING and FALLING, in
> > > >> > any revision of the GIC architecture.
> > > >> 
> > > >> Then pretending that there is a set_type() functionality is pretty daft
> > > >
> > > > You still need to distinguish between level and edge when this is
> > > > programmable (which is the case for a subset of the PPIs).
> > > 
> > > Fair enough, but can we please add a comment to this function which
> > > explains this oddity.
> > 
> > Getting back to this, I would need your/Marc's input on this.
> > 
> > I think it is fair to remove the irq_set_type() irqchip callback for
> > GICv5 PPIs because there is nothing to set, as I said handling mode
> > for these IRQs is fixed. I don't think this can cause any trouble
> > (IIUC a value within the IRQF_TRIGGER_MASK should be set on requesting
> > an IRQ to "force" the trigger to be programmed and even then core code
> > would not fail if the irq_set_type() irqchip callback is not
> > implemented).
> > 
> > I am thinking about *existing* drivers that request GICv3 PPIs with
> > values in IRQF_TRIGGER_MASK set (are there any ? Don't think so but you
> > know better than I do), when we switch over to GICv5 we would have no
> > irq_set_type() callback for PPIs but I think we are still fine, not
> > implementing irqchip.irq_set_type() is correct IMO.
> 
> Nobody seems to use a hardcoded trigger (well, there is one exception,
> but that's to paper over a firmware bug).

That's what I get if I remove the PPI irq_set_type() callback (just one
timer, removed others because they add nothing) and enable debug for
kernel/irq/manage.c (+additional printout):

 genirq: No set_type function for IRQ 70 (GICv5-PPI)
  __irq_set_trigger+0x13c/0x180
  __setup_irq+0x3d8/0x7c0
  __request_percpu_irq+0xbc/0x114
  arch_timer_register+0x84/0x140
  arch_timer_of_init+0x180/0x1d0
  timer_probe+0x74/0x124
  time_init+0x18/0x58
  start_kernel+0x198/0x384
  __primary_switched+0x88/0x90

 arch_timer: check_ppi_trigger irq 70 flags 8
 genirq: enable_percpu_irq irq 70 type 8
 genirq: No set_type function for IRQ 70 (GICv5-PPI)
  __irq_set_trigger+0x13c/0x180
  enable_percpu_irq+0x100/0x140
  arch_timer_starting_cpu+0x54/0xb8
  cpuhp_issue_call+0x254/0x3a8
  __cpuhp_setup_state_cpuslocked+0x208/0x2c8
  __cpuhp_setup_state+0x50/0x74
  arch_timer_register+0xc4/0x140
  arch_timer_of_init+0x180/0x1d0
  timer_probe+0x74/0x124
  time_init+0x18/0x58
  start_kernel+0x198/0x384
  __primary_switched+0x88/0x90

I noticed that, if the irq_set_type() function is not implemented,
we don't execute (in __irq_set_trigger()):

irq_settings_set_level(desc);
irqd_set(&desc->irq_data, IRQD_LEVEL);

which in turn means that irqd_is_level_type(&desc->irq_data) is false
for PPIs (ie arch timers, despite being level interrupts).

An immediate side effect is that they show as edge in:

/proc/interrupts

but that's just what I could notice.

Should I set them myself in PPI translate/alloc functions ?

Removing the irq_set_type() for PPIs does not seem so innocuous, it is a
bit complex to check all ramifications, please let me know if you spot
something I have missed.

> > On the other hand, given that on GICv5 PPI handling mode is fixed,
> > do you think that in the ppi_irq_domain_ops.translate() callback,
> > I should check the type the firmware provided and fail the translation
> > if it does not match the HW hardcoded value ?
> 
> Why? The fact that the firmware is wrong doesn't change the hardware
> integration. It just indicates that whoever wrote the firmware didn't
> read the documentation.
> 
> Even more, I wonder what the benefit of having that information in the
> firmware tables if the only thing that matters in the immutable HW
> view. Yes, having it in the DT/ACPI simplifies the job of the kernel
> (only one format to parse). But it is overall useless information.

Yes, that I agree but it would force firmware bindings to special case
PPIs to remove the type (#interrupt-cells and co.).

From what I read I understand I must ignore the PPI type provided by
firmware.

> > Obviously if firmware exposes the wrong type that's a firmware bug
> > but I was wondering whether it is better to fail the firmware-to-Linux
> > IRQ translation if the firmware provided type is wrong rather than carry
> > on pretending that the type is correct (I was abusing the irq_set_type()
> > callback to do just that - namely, check that the type provided by
> > firmware matches HW but I think that's the wrong place to put it).
> 
> I don't think there is anything to do. Worse case, you spit a
> pr_warn_once() and carry on.

Thanks,
Lorenzo


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

* Re: [PATCH v3 24/25] irqchip/gic-v5: Add GICv5 ITS support
  2025-05-06 12:23 ` [PATCH v3 24/25] irqchip/gic-v5: Add GICv5 ITS support Lorenzo Pieralisi
@ 2025-05-09  0:47   ` kernel test robot
  0 siblings, 0 replies; 49+ messages in thread
From: kernel test robot @ 2025-05-09  0:47 UTC (permalink / raw)
  To: Lorenzo Pieralisi, Marc Zyngier, Thomas Gleixner, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Catalin Marinas, Will Deacon
  Cc: oe-kbuild-all, Arnd Bergmann, Sascha Bischoff, Timothy Hayes,
	Liam R. Howlett, Mark Rutland, Jiri Slaby, linux-arm-kernel,
	linux-kernel, devicetree, Lorenzo Pieralisi

Hi Lorenzo,

kernel test robot noticed the following build warnings:

[auto build test WARNING on 0af2f6be1b4281385b618cb86ad946eded089ac8]

url:    https://github.com/intel-lab-lkp/linux/commits/Lorenzo-Pieralisi/dt-bindings-interrupt-controller-Add-Arm-GICv5/20250506-203528
base:   0af2f6be1b4281385b618cb86ad946eded089ac8
patch link:    https://lore.kernel.org/r/20250506-gicv5-host-v3-24-6edd5a92fd09%40kernel.org
patch subject: [PATCH v3 24/25] irqchip/gic-v5: Add GICv5 ITS support
compiler: clang version 20.1.2 (https://github.com/llvm/llvm-project 58df0ef89dd64126512e4ee27b4ac3fd8ddf6247)

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/202505090857.dvSPkqHI-lkp@intel.com/

includecheck warnings: (new ones prefixed by >>)
>> include/linux/irqchip/arm-gic-v5.h: linux/iopoll.h is included more than once.

vim +8 include/linux/irqchip/arm-gic-v5.h

     7	
   > 8	#include <linux/iopoll.h>
     9	
    10	#include <asm/cacheflush.h>
  > 11	#include <linux/iopoll.h>
    12	

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


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

* Re: [PATCH v3 20/25] irqchip/gic-v5: Add GICv5 PPI support
  2025-05-08 10:44                 ` Lorenzo Pieralisi
@ 2025-05-09  8:07                   ` Lorenzo Pieralisi
  2025-05-09  8:35                     ` Lorenzo Pieralisi
  2025-05-12  8:27                   ` Marc Zyngier
  1 sibling, 1 reply; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-09  8:07 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Arnd Bergmann, Sascha Bischoff,
	Timothy Hayes, Liam R. Howlett, Mark Rutland, Jiri Slaby,
	linux-arm-kernel, linux-kernel, devicetree

On Thu, May 08, 2025 at 12:44:45PM +0200, Lorenzo Pieralisi wrote:

[...]

> I noticed that, if the irq_set_type() function is not implemented,
> we don't execute (in __irq_set_trigger()):
> 
> irq_settings_set_level(desc);
> irqd_set(&desc->irq_data, IRQD_LEVEL);

I don't get why the settings above are written only if the irqchip
has an irq_set_type() method, maybe they should be updated in
irqdomain code (?) where:

irqd_set_trigger_type()

is executed after creating the fwspec mapping ?

Is it possible we never noticed because we have always had irqchips that
do implement irq_set_type() ?

Again, I don't know the history behind the IRQD_LEVEL flag so it is just
a question, I'd need to get this clarified though please if I remove the
PPI irq_set_type() callback.

Thanks,
Lorenzo

> which in turn means that irqd_is_level_type(&desc->irq_data) is false
> for PPIs (ie arch timers, despite being level interrupts).
> 
> An immediate side effect is that they show as edge in:
> 
> /proc/interrupts
> 
> but that's just what I could notice.
> 
> Should I set them myself in PPI translate/alloc functions ?
> 
> Removing the irq_set_type() for PPIs does not seem so innocuous, it is a
> bit complex to check all ramifications, please let me know if you spot
> something I have missed.
> 
> > > On the other hand, given that on GICv5 PPI handling mode is fixed,
> > > do you think that in the ppi_irq_domain_ops.translate() callback,
> > > I should check the type the firmware provided and fail the translation
> > > if it does not match the HW hardcoded value ?
> > 
> > Why? The fact that the firmware is wrong doesn't change the hardware
> > integration. It just indicates that whoever wrote the firmware didn't
> > read the documentation.
> > 
> > Even more, I wonder what the benefit of having that information in the
> > firmware tables if the only thing that matters in the immutable HW
> > view. Yes, having it in the DT/ACPI simplifies the job of the kernel
> > (only one format to parse). But it is overall useless information.
> 
> Yes, that I agree but it would force firmware bindings to special case
> PPIs to remove the type (#interrupt-cells and co.).
> 
> From what I read I understand I must ignore the PPI type provided by
> firmware.
> 
> > > Obviously if firmware exposes the wrong type that's a firmware bug
> > > but I was wondering whether it is better to fail the firmware-to-Linux
> > > IRQ translation if the firmware provided type is wrong rather than carry
> > > on pretending that the type is correct (I was abusing the irq_set_type()
> > > callback to do just that - namely, check that the type provided by
> > > firmware matches HW but I think that's the wrong place to put it).
> > 
> > I don't think there is anything to do. Worse case, you spit a
> > pr_warn_once() and carry on.
> 
> Thanks,
> Lorenzo


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

* Re: [PATCH v3 20/25] irqchip/gic-v5: Add GICv5 PPI support
  2025-05-09  8:07                   ` Lorenzo Pieralisi
@ 2025-05-09  8:35                     ` Lorenzo Pieralisi
  2025-05-12  8:32                       ` Marc Zyngier
  0 siblings, 1 reply; 49+ messages in thread
From: Lorenzo Pieralisi @ 2025-05-09  8:35 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Arnd Bergmann, Sascha Bischoff,
	Timothy Hayes, Liam R. Howlett, Mark Rutland, Jiri Slaby,
	linux-arm-kernel, linux-kernel, devicetree

On Fri, May 09, 2025 at 10:07:44AM +0200, Lorenzo Pieralisi wrote:
> On Thu, May 08, 2025 at 12:44:45PM +0200, Lorenzo Pieralisi wrote:
> 
> [...]
> 
> > I noticed that, if the irq_set_type() function is not implemented,
> > we don't execute (in __irq_set_trigger()):
> > 
> > irq_settings_set_level(desc);
> > irqd_set(&desc->irq_data, IRQD_LEVEL);
> 
> I don't get why the settings above are written only if the irqchip
> has an irq_set_type() method, maybe they should be updated in
> irqdomain code (?) where:
> 
> irqd_set_trigger_type()
> 
> is executed after creating the fwspec mapping ?
> 
> Is it possible we never noticed because we have always had irqchips that
> do implement irq_set_type() ?
> 
> Again, I don't know the history behind the IRQD_LEVEL flag so it is just
> a question, I'd need to get this clarified though please if I remove the
> PPI irq_set_type() callback.

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/powerpc/platforms/52xx/mpc52xx_pic.c?h=v6.15-rc5#n218

There are other examples in powerpc, this does not look right to me.

Lorenzo

> Thanks,
> Lorenzo
> 
> > which in turn means that irqd_is_level_type(&desc->irq_data) is false
> > for PPIs (ie arch timers, despite being level interrupts).
> > 
> > An immediate side effect is that they show as edge in:
> > 
> > /proc/interrupts
> > 
> > but that's just what I could notice.
> > 
> > Should I set them myself in PPI translate/alloc functions ?
> > 
> > Removing the irq_set_type() for PPIs does not seem so innocuous, it is a
> > bit complex to check all ramifications, please let me know if you spot
> > something I have missed.
> > 
> > > > On the other hand, given that on GICv5 PPI handling mode is fixed,
> > > > do you think that in the ppi_irq_domain_ops.translate() callback,
> > > > I should check the type the firmware provided and fail the translation
> > > > if it does not match the HW hardcoded value ?
> > > 
> > > Why? The fact that the firmware is wrong doesn't change the hardware
> > > integration. It just indicates that whoever wrote the firmware didn't
> > > read the documentation.
> > > 
> > > Even more, I wonder what the benefit of having that information in the
> > > firmware tables if the only thing that matters in the immutable HW
> > > view. Yes, having it in the DT/ACPI simplifies the job of the kernel
> > > (only one format to parse). But it is overall useless information.
> > 
> > Yes, that I agree but it would force firmware bindings to special case
> > PPIs to remove the type (#interrupt-cells and co.).
> > 
> > From what I read I understand I must ignore the PPI type provided by
> > firmware.
> > 
> > > > Obviously if firmware exposes the wrong type that's a firmware bug
> > > > but I was wondering whether it is better to fail the firmware-to-Linux
> > > > IRQ translation if the firmware provided type is wrong rather than carry
> > > > on pretending that the type is correct (I was abusing the irq_set_type()
> > > > callback to do just that - namely, check that the type provided by
> > > > firmware matches HW but I think that's the wrong place to put it).
> > > 
> > > I don't think there is anything to do. Worse case, you spit a
> > > pr_warn_once() and carry on.
> > 
> > Thanks,
> > Lorenzo


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

* Re: [PATCH v3 20/25] irqchip/gic-v5: Add GICv5 PPI support
  2025-05-08 10:44                 ` Lorenzo Pieralisi
  2025-05-09  8:07                   ` Lorenzo Pieralisi
@ 2025-05-12  8:27                   ` Marc Zyngier
  1 sibling, 0 replies; 49+ messages in thread
From: Marc Zyngier @ 2025-05-12  8:27 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Arnd Bergmann, Sascha Bischoff,
	Timothy Hayes, Liam R. Howlett, Mark Rutland, Jiri Slaby,
	linux-arm-kernel, linux-kernel, devicetree

On Thu, 08 May 2025 11:44:45 +0100,
Lorenzo Pieralisi <lpieralisi@kernel.org> wrote:
> 
> On Thu, May 08, 2025 at 09:42:27AM +0100, Marc Zyngier wrote:
> > On Thu, 08 May 2025 08:42:41 +0100,
> > Lorenzo Pieralisi <lpieralisi@kernel.org> wrote:
> > > 
> > > On Wed, May 07, 2025 at 04:57:07PM +0200, Thomas Gleixner wrote:
> > > > On Wed, May 07 2025 at 14:52, Marc Zyngier wrote:
> > > > > On Wed, 07 May 2025 14:42:42 +0100,
> > > > > Thomas Gleixner <tglx@linutronix.de> wrote:
> > > > >> 
> > > > >> On Wed, May 07 2025 at 10:14, Marc Zyngier wrote:
> > > > >> > On Tue, 06 May 2025 16:00:31 +0100,
> > > > >> > Thomas Gleixner <tglx@linutronix.de> wrote:
> > > > >> >> 
> > > > >> >> How does this test distinguish between LEVEL_LOW and LEVEL_HIGH? It only
> > > > >> >> tests for level, no? So the test is interesting at best ...
> > > > >> >
> > > > >> > There is no distinction between HIGH and LOW, RISING and FALLING, in
> > > > >> > any revision of the GIC architecture.
> > > > >> 
> > > > >> Then pretending that there is a set_type() functionality is pretty daft
> > > > >
> > > > > You still need to distinguish between level and edge when this is
> > > > > programmable (which is the case for a subset of the PPIs).
> > > > 
> > > > Fair enough, but can we please add a comment to this function which
> > > > explains this oddity.
> > > 
> > > Getting back to this, I would need your/Marc's input on this.
> > > 
> > > I think it is fair to remove the irq_set_type() irqchip callback for
> > > GICv5 PPIs because there is nothing to set, as I said handling mode
> > > for these IRQs is fixed. I don't think this can cause any trouble
> > > (IIUC a value within the IRQF_TRIGGER_MASK should be set on requesting
> > > an IRQ to "force" the trigger to be programmed and even then core code
> > > would not fail if the irq_set_type() irqchip callback is not
> > > implemented).
> > > 
> > > I am thinking about *existing* drivers that request GICv3 PPIs with
> > > values in IRQF_TRIGGER_MASK set (are there any ? Don't think so but you
> > > know better than I do), when we switch over to GICv5 we would have no
> > > irq_set_type() callback for PPIs but I think we are still fine, not
> > > implementing irqchip.irq_set_type() is correct IMO.
> > 
> > Nobody seems to use a hardcoded trigger (well, there is one exception,
> > but that's to paper over a firmware bug).
> 
> That's what I get if I remove the PPI irq_set_type() callback (just one
> timer, removed others because they add nothing) and enable debug for
> kernel/irq/manage.c (+additional printout):
> 
>  genirq: No set_type function for IRQ 70 (GICv5-PPI)
>   __irq_set_trigger+0x13c/0x180
>   __setup_irq+0x3d8/0x7c0
>   __request_percpu_irq+0xbc/0x114
>   arch_timer_register+0x84/0x140
>   arch_timer_of_init+0x180/0x1d0
>   timer_probe+0x74/0x124
>   time_init+0x18/0x58
>   start_kernel+0x198/0x384
>   __primary_switched+0x88/0x90
> 
>  arch_timer: check_ppi_trigger irq 70 flags 8
>  genirq: enable_percpu_irq irq 70 type 8
>  genirq: No set_type function for IRQ 70 (GICv5-PPI)
>   __irq_set_trigger+0x13c/0x180
>   enable_percpu_irq+0x100/0x140
>   arch_timer_starting_cpu+0x54/0xb8
>   cpuhp_issue_call+0x254/0x3a8
>   __cpuhp_setup_state_cpuslocked+0x208/0x2c8
>   __cpuhp_setup_state+0x50/0x74
>   arch_timer_register+0xc4/0x140
>   arch_timer_of_init+0x180/0x1d0
>   timer_probe+0x74/0x124
>   time_init+0x18/0x58
>   start_kernel+0x198/0x384
>   __primary_switched+0x88/0x90
> 
> I noticed that, if the irq_set_type() function is not implemented,
> we don't execute (in __irq_set_trigger()):
> 
> irq_settings_set_level(desc);
> irqd_set(&desc->irq_data, IRQD_LEVEL);
> 
> which in turn means that irqd_is_level_type(&desc->irq_data) is false
> for PPIs (ie arch timers, despite being level interrupts).
> 
> An immediate side effect is that they show as edge in:
> 
> /proc/interrupts
> 
> but that's just what I could notice.
> 
> Should I set them myself in PPI translate/alloc functions ?

When I say "do it in alloc", I mean "do whatever is needed to set
things up so that we can safely ignore the absence of the
.irq_set_type() callback -- this may even include some slight
modification of the core code, but that's not big deal".

> Removing the irq_set_type() for PPIs does not seem so innocuous, it is a
> bit complex to check all ramifications, please let me know if you spot
> something I have missed.

See above.

> 
> > > On the other hand, given that on GICv5 PPI handling mode is fixed,
> > > do you think that in the ppi_irq_domain_ops.translate() callback,
> > > I should check the type the firmware provided and fail the translation
> > > if it does not match the HW hardcoded value ?
> > 
> > Why? The fact that the firmware is wrong doesn't change the hardware
> > integration. It just indicates that whoever wrote the firmware didn't
> > read the documentation.
> > 
> > Even more, I wonder what the benefit of having that information in the
> > firmware tables if the only thing that matters in the immutable HW
> > view. Yes, having it in the DT/ACPI simplifies the job of the kernel
> > (only one format to parse). But it is overall useless information.
> 
> Yes, that I agree but it would force firmware bindings to special case
> PPIs to remove the type (#interrupt-cells and co.).
> 
> From what I read I understand I must ignore the PPI type provided by
> firmware.

You could warn on spotting that is inconsistent, but the HW view is
the only one that actually matters.

	M.

-- 
Without deviation from the norm, progress is not possible.


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

* Re: [PATCH v3 20/25] irqchip/gic-v5: Add GICv5 PPI support
  2025-05-09  8:35                     ` Lorenzo Pieralisi
@ 2025-05-12  8:32                       ` Marc Zyngier
  0 siblings, 0 replies; 49+ messages in thread
From: Marc Zyngier @ 2025-05-12  8:32 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Catalin Marinas, Will Deacon, Arnd Bergmann, Sascha Bischoff,
	Timothy Hayes, Liam R. Howlett, Mark Rutland, Jiri Slaby,
	linux-arm-kernel, linux-kernel, devicetree

On Fri, 09 May 2025 09:35:25 +0100,
Lorenzo Pieralisi <lpieralisi@kernel.org> wrote:
> 
> On Fri, May 09, 2025 at 10:07:44AM +0200, Lorenzo Pieralisi wrote:
> > On Thu, May 08, 2025 at 12:44:45PM +0200, Lorenzo Pieralisi wrote:
> > 
> > [...]
> > 
> > > I noticed that, if the irq_set_type() function is not implemented,
> > > we don't execute (in __irq_set_trigger()):
> > > 
> > > irq_settings_set_level(desc);
> > > irqd_set(&desc->irq_data, IRQD_LEVEL);
> > 
> > I don't get why the settings above are written only if the irqchip
> > has an irq_set_type() method, maybe they should be updated in
> > irqdomain code (?) where:
> > 
> > irqd_set_trigger_type()
> > 
> > is executed after creating the fwspec mapping ?
> > 
> > Is it possible we never noticed because we have always had irqchips that
> > do implement irq_set_type() ?
> > 
> > Again, I don't know the history behind the IRQD_LEVEL flag so it is just
> > a question, I'd need to get this clarified though please if I remove the
> > PPI irq_set_type() callback.
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/powerpc/platforms/52xx/mpc52xx_pic.c?h=v6.15-rc5#n218
> 
> There are other examples in powerpc, this does not look right to me.

I don't see what's wrong with this, given that PPC is about 15 years
behind the curve when it comes to interrupt management. Better than
Sparc or Alpha, though. So they do whatever was possible at the time
this code was written.

It doesn't mean that you need to align with the worse we have in the tree!

	M.

-- 
Without deviation from the norm, progress is not possible.


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

end of thread, other threads:[~2025-05-12  8:39 UTC | newest]

Thread overview: 49+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-06 12:23 [PATCH v3 00/25] Arm GICv5: Host driver implementation Lorenzo Pieralisi
2025-05-06 12:23 ` [PATCH v3 01/25] dt-bindings: interrupt-controller: Add Arm GICv5 Lorenzo Pieralisi
2025-05-06 19:08   ` Rob Herring
2025-05-07  8:35     ` Lorenzo Pieralisi
2025-05-06 12:23 ` [PATCH v3 02/25] arm64/sysreg: Add GCIE field to ID_AA64PFR2_EL1 Lorenzo Pieralisi
2025-05-06 12:23 ` [PATCH v3 03/25] arm64/sysreg: Add ICC_PPI_PRIORITY<n>_EL1 Lorenzo Pieralisi
2025-05-06 12:23 ` [PATCH v3 04/25] arm64/sysreg: Add ICC_ICSR_EL1 Lorenzo Pieralisi
2025-05-06 12:23 ` [PATCH v3 05/25] arm64/sysreg: Add ICC_PPI_HMR<n>_EL1 Lorenzo Pieralisi
2025-05-06 12:23 ` [PATCH v3 06/25] arm64/sysreg: Add ICC_PPI_ENABLER<n>_EL1 Lorenzo Pieralisi
2025-05-06 12:23 ` [PATCH v3 07/25] arm64/sysreg: Add ICC_PPI_{C/S}ACTIVER<n>_EL1 Lorenzo Pieralisi
2025-05-06 12:23 ` [PATCH v3 08/25] arm64/sysreg: Add ICC_PPI_{C/S}PENDR<n>_EL1 Lorenzo Pieralisi
2025-05-06 12:23 ` [PATCH v3 09/25] arm64/sysreg: Add ICC_CR0_EL1 Lorenzo Pieralisi
2025-05-06 12:23 ` [PATCH v3 10/25] arm64/sysreg: Add ICC_PCR_EL1 Lorenzo Pieralisi
2025-05-06 12:23 ` [PATCH v3 11/25] arm64/sysreg: Add ICC_IDR0_EL1 Lorenzo Pieralisi
2025-05-06 12:23 ` [PATCH v3 12/25] arm64/sysreg: Add ICH_HFGRTR_EL2 Lorenzo Pieralisi
2025-05-06 12:23 ` [PATCH v3 13/25] arm64/sysreg: Add ICH_HFGWTR_EL2 Lorenzo Pieralisi
2025-05-06 12:23 ` [PATCH v3 14/25] arm64/sysreg: Add ICH_HFGITR_EL2 Lorenzo Pieralisi
2025-05-06 12:23 ` [PATCH v3 15/25] arm64: Disable GICv5 read/write/instruction traps Lorenzo Pieralisi
2025-05-06 12:23 ` [PATCH v3 16/25] arm64: cpucaps: Rename GICv3 CPU interface capability Lorenzo Pieralisi
2025-05-06 12:23 ` [PATCH v3 17/25] arm64: cpucaps: Add GICv5 CPU interface (GCIE) capability Lorenzo Pieralisi
2025-05-06 12:23 ` [PATCH v3 18/25] arm64: smp: Support non-SGIs for IPIs Lorenzo Pieralisi
2025-05-06 12:23 ` [PATCH v3 19/25] arm64: Add support for GICv5 GSB barriers Lorenzo Pieralisi
2025-05-06 12:23 ` [PATCH v3 20/25] irqchip/gic-v5: Add GICv5 PPI support Lorenzo Pieralisi
2025-05-06 15:00   ` Thomas Gleixner
2025-05-07  8:29     ` Lorenzo Pieralisi
2025-05-07  9:14     ` Marc Zyngier
2025-05-07 13:42       ` Thomas Gleixner
2025-05-07 13:52         ` Marc Zyngier
2025-05-07 14:57           ` Thomas Gleixner
2025-05-07 15:48             ` Lorenzo Pieralisi
2025-05-08  7:42             ` Lorenzo Pieralisi
2025-05-08  8:42               ` Marc Zyngier
2025-05-08 10:44                 ` Lorenzo Pieralisi
2025-05-09  8:07                   ` Lorenzo Pieralisi
2025-05-09  8:35                     ` Lorenzo Pieralisi
2025-05-12  8:32                       ` Marc Zyngier
2025-05-12  8:27                   ` Marc Zyngier
2025-05-06 12:23 ` [PATCH v3 21/25] irqchip/gic-v5: Add GICv5 IRS/SPI support Lorenzo Pieralisi
2025-05-06 12:23 ` [PATCH v3 22/25] irqchip/gic-v5: Add GICv5 LPI/IPI support Lorenzo Pieralisi
2025-05-06 15:07   ` Thomas Gleixner
2025-05-07  8:30     ` Lorenzo Pieralisi
2025-05-06 12:23 ` [PATCH v3 23/25] irqchip/gic-v5: Enable GICv5 SMP booting Lorenzo Pieralisi
2025-05-06 12:23 ` [PATCH v3 24/25] irqchip/gic-v5: Add GICv5 ITS support Lorenzo Pieralisi
2025-05-09  0:47   ` kernel test robot
2025-05-06 12:23 ` [PATCH v3 25/25] arm64: Kconfig: Enable GICv5 Lorenzo Pieralisi
2025-05-06 14:05 ` [PATCH v3 00/25] Arm GICv5: Host driver implementation Marc Zyngier
2025-05-07  7:54   ` Lorenzo Pieralisi
2025-05-07  9:09     ` Marc Zyngier
2025-05-07 10:01       ` Lorenzo Pieralisi

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).