All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/9] Linux RISC-V AIA Support
@ 2022-11-11  4:41 ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-11  4:41 UTC (permalink / raw)
  To: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski
  Cc: Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree, Anup Patel

The RISC-V AIA specification is now frozen as-per the RISC-V international
process. The latest frozen specifcation can be found at:
https://github.com/riscv/riscv-aia/releases/download/1.0-RC1/riscv-interrupts-1.0-RC1.pdf

At a high-level, the AIA specification adds three things:
1) AIA CSRs
   - Improved local interrupt support
2) Incoming Message Signaled Interrupt Controller (IMSIC)
   - Per-HART MSI controller
   - Support MSI virtualization
   - Support IPI along with virtualization
3) Advanced Platform-Level Interrupt Controller (APLIC)
   - Wired interrupt controller
   - In MSI-mode, converts wired interrupt into MSIs (i.e. MSI generator)
   - In Direct-mode, injects external interrupts directly into HARTs

For an overview of the AIA specification, refer the recent AIA virtualization
talk at KVM Forum 2022:
https://static.sched.com/hosted_files/kvmforum2022/a1/AIA_Virtualization_in_KVM_RISCV_final.pdf
https://www.youtube.com/watch?v=r071dL8Z0yo

This series adds required Linux irqchip drivers for AIA and it depends on
the recent "RISC-V IPI Improvements".
(Refer, https://lore.kernel.org/lkml/20221101143400.690000-1-apatel@ventanamicro.com/t/)

To test this series, use QEMU v7.1 (or higher) and OpenSBI v1.1 (or higher).

These patches can also be found in the riscv_aia_v1 branch at:
https://github.com/avpatel/linux.git

Anup Patel (9):
  RISC-V: Add AIA related CSR defines
  RISC-V: Detect AIA CSRs from ISA string
  irqchip/riscv-intc: Add support for RISC-V AIA
  dt-bindings: Add RISC-V incoming MSI controller bindings
  irqchip: Add RISC-V incoming MSI controller driver
  dt-bindings: Add RISC-V advanced PLIC bindings
  irqchip: Add RISC-V advanced PLIC driver
  RISC-V: Select APLIC and IMSIC drivers for QEMU virt machine
  MAINTAINERS: Add entry for RISC-V AIA drivers

 .../interrupt-controller/riscv,aplic.yaml     |  136 ++
 .../interrupt-controller/riscv,imsic.yaml     |  174 +++
 MAINTAINERS                                   |   12 +
 arch/riscv/Kconfig.socs                       |    2 +
 arch/riscv/include/asm/csr.h                  |   92 ++
 arch/riscv/include/asm/hwcap.h                |    8 +
 arch/riscv/kernel/cpu.c                       |    2 +
 arch/riscv/kernel/cpufeature.c                |    2 +
 drivers/irqchip/Kconfig                       |   32 +-
 drivers/irqchip/Makefile                      |    2 +
 drivers/irqchip/irq-riscv-aplic.c             |  656 +++++++++
 drivers/irqchip/irq-riscv-imsic.c             | 1207 +++++++++++++++++
 drivers/irqchip/irq-riscv-intc.c              |   37 +-
 include/linux/irqchip/riscv-aplic.h           |  117 ++
 include/linux/irqchip/riscv-imsic.h           |   92 ++
 15 files changed, 2564 insertions(+), 7 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
 create mode 100644 drivers/irqchip/irq-riscv-aplic.c
 create mode 100644 drivers/irqchip/irq-riscv-imsic.c
 create mode 100644 include/linux/irqchip/riscv-aplic.h
 create mode 100644 include/linux/irqchip/riscv-imsic.h

-- 
2.34.1


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

* [PATCH 0/9] Linux RISC-V AIA Support
@ 2022-11-11  4:41 ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-11  4:41 UTC (permalink / raw)
  To: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski
  Cc: Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree, Anup Patel

The RISC-V AIA specification is now frozen as-per the RISC-V international
process. The latest frozen specifcation can be found at:
https://github.com/riscv/riscv-aia/releases/download/1.0-RC1/riscv-interrupts-1.0-RC1.pdf

At a high-level, the AIA specification adds three things:
1) AIA CSRs
   - Improved local interrupt support
2) Incoming Message Signaled Interrupt Controller (IMSIC)
   - Per-HART MSI controller
   - Support MSI virtualization
   - Support IPI along with virtualization
3) Advanced Platform-Level Interrupt Controller (APLIC)
   - Wired interrupt controller
   - In MSI-mode, converts wired interrupt into MSIs (i.e. MSI generator)
   - In Direct-mode, injects external interrupts directly into HARTs

For an overview of the AIA specification, refer the recent AIA virtualization
talk at KVM Forum 2022:
https://static.sched.com/hosted_files/kvmforum2022/a1/AIA_Virtualization_in_KVM_RISCV_final.pdf
https://www.youtube.com/watch?v=r071dL8Z0yo

This series adds required Linux irqchip drivers for AIA and it depends on
the recent "RISC-V IPI Improvements".
(Refer, https://lore.kernel.org/lkml/20221101143400.690000-1-apatel@ventanamicro.com/t/)

To test this series, use QEMU v7.1 (or higher) and OpenSBI v1.1 (or higher).

These patches can also be found in the riscv_aia_v1 branch at:
https://github.com/avpatel/linux.git

Anup Patel (9):
  RISC-V: Add AIA related CSR defines
  RISC-V: Detect AIA CSRs from ISA string
  irqchip/riscv-intc: Add support for RISC-V AIA
  dt-bindings: Add RISC-V incoming MSI controller bindings
  irqchip: Add RISC-V incoming MSI controller driver
  dt-bindings: Add RISC-V advanced PLIC bindings
  irqchip: Add RISC-V advanced PLIC driver
  RISC-V: Select APLIC and IMSIC drivers for QEMU virt machine
  MAINTAINERS: Add entry for RISC-V AIA drivers

 .../interrupt-controller/riscv,aplic.yaml     |  136 ++
 .../interrupt-controller/riscv,imsic.yaml     |  174 +++
 MAINTAINERS                                   |   12 +
 arch/riscv/Kconfig.socs                       |    2 +
 arch/riscv/include/asm/csr.h                  |   92 ++
 arch/riscv/include/asm/hwcap.h                |    8 +
 arch/riscv/kernel/cpu.c                       |    2 +
 arch/riscv/kernel/cpufeature.c                |    2 +
 drivers/irqchip/Kconfig                       |   32 +-
 drivers/irqchip/Makefile                      |    2 +
 drivers/irqchip/irq-riscv-aplic.c             |  656 +++++++++
 drivers/irqchip/irq-riscv-imsic.c             | 1207 +++++++++++++++++
 drivers/irqchip/irq-riscv-intc.c              |   37 +-
 include/linux/irqchip/riscv-aplic.h           |  117 ++
 include/linux/irqchip/riscv-imsic.h           |   92 ++
 15 files changed, 2564 insertions(+), 7 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
 create mode 100644 drivers/irqchip/irq-riscv-aplic.c
 create mode 100644 drivers/irqchip/irq-riscv-imsic.c
 create mode 100644 include/linux/irqchip/riscv-aplic.h
 create mode 100644 include/linux/irqchip/riscv-imsic.h

-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* [PATCH 1/9] RISC-V: Add AIA related CSR defines
  2022-11-11  4:41 ` Anup Patel
@ 2022-11-11  4:41   ` Anup Patel
  -1 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-11  4:41 UTC (permalink / raw)
  To: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski
  Cc: Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree, Anup Patel

The RISC-V AIA specification improves handling per-HART local interrupts
in a backward compatible manner. This patch adds defines for new RISC-V
AIA CSRs.

Signed-off-by: Anup Patel <apatel@ventanamicro.com>
---
 arch/riscv/include/asm/csr.h | 92 ++++++++++++++++++++++++++++++++++++
 1 file changed, 92 insertions(+)

diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h
index 0e571f6483d9..4e1356bad7b2 100644
--- a/arch/riscv/include/asm/csr.h
+++ b/arch/riscv/include/asm/csr.h
@@ -73,7 +73,10 @@
 #define IRQ_S_EXT		9
 #define IRQ_VS_EXT		10
 #define IRQ_M_EXT		11
+#define IRQ_S_GEXT		12
 #define IRQ_PMU_OVF		13
+#define IRQ_LOCAL_MAX		(IRQ_PMU_OVF + 1)
+#define IRQ_LOCAL_MASK		((_AC(1, UL) << IRQ_LOCAL_MAX) - 1)
 
 /* Exception causes */
 #define EXC_INST_MISALIGNED	0
@@ -156,6 +159,26 @@
 				 (_AC(1, UL) << IRQ_S_TIMER) | \
 				 (_AC(1, UL) << IRQ_S_EXT))
 
+/* AIA CSR bits */
+#define TOPI_IID_SHIFT		16
+#define TOPI_IID_MASK		0xfff
+#define TOPI_IPRIO_MASK		0xff
+#define TOPI_IPRIO_BITS		8
+
+#define TOPEI_ID_SHIFT		16
+#define TOPEI_ID_MASK		0x7ff
+#define TOPEI_PRIO_MASK		0x7ff
+
+#define ISELECT_IPRIO0		0x30
+#define ISELECT_IPRIO15		0x3f
+#define ISELECT_MASK		0x1ff
+
+#define HVICTL_VTI		0x40000000
+#define HVICTL_IID		0x0fff0000
+#define HVICTL_IID_SHIFT	16
+#define HVICTL_IPRIOM		0x00000100
+#define HVICTL_IPRIO		0x000000ff
+
 /* xENVCFG flags */
 #define ENVCFG_STCE			(_AC(1, ULL) << 63)
 #define ENVCFG_PBMTE			(_AC(1, ULL) << 62)
@@ -250,6 +273,18 @@
 #define CSR_STIMECMP		0x14D
 #define CSR_STIMECMPH		0x15D
 
+/* Supervisor-Level Window to Indirectly Accessed Registers (AIA) */
+#define CSR_SISELECT		0x150
+#define CSR_SIREG		0x151
+
+/* Supervisor-Level Interrupts (AIA) */
+#define CSR_STOPEI		0x15c
+#define CSR_STOPI		0xdb0
+
+/* Supervisor-Level High-Half CSRs (AIA) */
+#define CSR_SIEH		0x114
+#define CSR_SIPH		0x154
+
 #define CSR_VSSTATUS		0x200
 #define CSR_VSIE		0x204
 #define CSR_VSTVEC		0x205
@@ -279,8 +314,32 @@
 #define CSR_HGATP		0x680
 #define CSR_HGEIP		0xe12
 
+/* Virtual Interrupts and Interrupt Priorities (H-extension with AIA) */
+#define CSR_HVIEN		0x608
+#define CSR_HVICTL		0x609
+#define CSR_HVIPRIO1		0x646
+#define CSR_HVIPRIO2		0x647
+
+/* VS-Level Window to Indirectly Accessed Registers (H-extension with AIA) */
+#define CSR_VSISELECT		0x250
+#define CSR_VSIREG		0x251
+
+/* VS-Level Interrupts (H-extension with AIA) */
+#define CSR_VSTOPEI		0x25c
+#define CSR_VSTOPI		0xeb0
+
+/* Hypervisor and VS-Level High-Half CSRs (H-extension with AIA) */
+#define CSR_HIDELEGH		0x613
+#define CSR_HVIENH		0x618
+#define CSR_HVIPH		0x655
+#define CSR_HVIPRIO1H		0x656
+#define CSR_HVIPRIO2H		0x657
+#define CSR_VSIEH		0x214
+#define CSR_VSIPH		0x254
+
 #define CSR_MSTATUS		0x300
 #define CSR_MISA		0x301
+#define CSR_MIDELEG		0x303
 #define CSR_MIE			0x304
 #define CSR_MTVEC		0x305
 #define CSR_MENVCFG		0x30a
@@ -297,6 +356,25 @@
 #define CSR_MIMPID		0xf13
 #define CSR_MHARTID		0xf14
 
+/* Machine-Level Window to Indirectly Accessed Registers (AIA) */
+#define CSR_MISELECT		0x350
+#define CSR_MIREG		0x351
+
+/* Machine-Level Interrupts (AIA) */
+#define CSR_MTOPEI		0x35c
+#define CSR_MTOPI		0xfb0
+
+/* Virtual Interrupts for Supervisor Level (AIA) */
+#define CSR_MVIEN		0x308
+#define CSR_MVIP		0x309
+
+/* Machine-Level High-Half CSRs (AIA) */
+#define CSR_MIDELEGH		0x313
+#define CSR_MIEH		0x314
+#define CSR_MVIENH		0x318
+#define CSR_MVIPH		0x319
+#define CSR_MIPH		0x354
+
 #ifdef CONFIG_RISCV_M_MODE
 # define CSR_STATUS	CSR_MSTATUS
 # define CSR_IE		CSR_MIE
@@ -307,6 +385,13 @@
 # define CSR_TVAL	CSR_MTVAL
 # define CSR_IP		CSR_MIP
 
+# define CSR_IEH		CSR_MIEH
+# define CSR_ISELECT	CSR_MISELECT
+# define CSR_IREG	CSR_MIREG
+# define CSR_IPH		CSR_MIPH
+# define CSR_TOPEI	CSR_MTOPEI
+# define CSR_TOPI	CSR_MTOPI
+
 # define SR_IE		SR_MIE
 # define SR_PIE		SR_MPIE
 # define SR_PP		SR_MPP
@@ -324,6 +409,13 @@
 # define CSR_TVAL	CSR_STVAL
 # define CSR_IP		CSR_SIP
 
+# define CSR_IEH		CSR_SIEH
+# define CSR_ISELECT	CSR_SISELECT
+# define CSR_IREG	CSR_SIREG
+# define CSR_IPH		CSR_SIPH
+# define CSR_TOPEI	CSR_STOPEI
+# define CSR_TOPI	CSR_STOPI
+
 # define SR_IE		SR_SIE
 # define SR_PIE		SR_SPIE
 # define SR_PP		SR_SPP
-- 
2.34.1


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

* [PATCH 1/9] RISC-V: Add AIA related CSR defines
@ 2022-11-11  4:41   ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-11  4:41 UTC (permalink / raw)
  To: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski
  Cc: Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree, Anup Patel

The RISC-V AIA specification improves handling per-HART local interrupts
in a backward compatible manner. This patch adds defines for new RISC-V
AIA CSRs.

Signed-off-by: Anup Patel <apatel@ventanamicro.com>
---
 arch/riscv/include/asm/csr.h | 92 ++++++++++++++++++++++++++++++++++++
 1 file changed, 92 insertions(+)

diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h
index 0e571f6483d9..4e1356bad7b2 100644
--- a/arch/riscv/include/asm/csr.h
+++ b/arch/riscv/include/asm/csr.h
@@ -73,7 +73,10 @@
 #define IRQ_S_EXT		9
 #define IRQ_VS_EXT		10
 #define IRQ_M_EXT		11
+#define IRQ_S_GEXT		12
 #define IRQ_PMU_OVF		13
+#define IRQ_LOCAL_MAX		(IRQ_PMU_OVF + 1)
+#define IRQ_LOCAL_MASK		((_AC(1, UL) << IRQ_LOCAL_MAX) - 1)
 
 /* Exception causes */
 #define EXC_INST_MISALIGNED	0
@@ -156,6 +159,26 @@
 				 (_AC(1, UL) << IRQ_S_TIMER) | \
 				 (_AC(1, UL) << IRQ_S_EXT))
 
+/* AIA CSR bits */
+#define TOPI_IID_SHIFT		16
+#define TOPI_IID_MASK		0xfff
+#define TOPI_IPRIO_MASK		0xff
+#define TOPI_IPRIO_BITS		8
+
+#define TOPEI_ID_SHIFT		16
+#define TOPEI_ID_MASK		0x7ff
+#define TOPEI_PRIO_MASK		0x7ff
+
+#define ISELECT_IPRIO0		0x30
+#define ISELECT_IPRIO15		0x3f
+#define ISELECT_MASK		0x1ff
+
+#define HVICTL_VTI		0x40000000
+#define HVICTL_IID		0x0fff0000
+#define HVICTL_IID_SHIFT	16
+#define HVICTL_IPRIOM		0x00000100
+#define HVICTL_IPRIO		0x000000ff
+
 /* xENVCFG flags */
 #define ENVCFG_STCE			(_AC(1, ULL) << 63)
 #define ENVCFG_PBMTE			(_AC(1, ULL) << 62)
@@ -250,6 +273,18 @@
 #define CSR_STIMECMP		0x14D
 #define CSR_STIMECMPH		0x15D
 
+/* Supervisor-Level Window to Indirectly Accessed Registers (AIA) */
+#define CSR_SISELECT		0x150
+#define CSR_SIREG		0x151
+
+/* Supervisor-Level Interrupts (AIA) */
+#define CSR_STOPEI		0x15c
+#define CSR_STOPI		0xdb0
+
+/* Supervisor-Level High-Half CSRs (AIA) */
+#define CSR_SIEH		0x114
+#define CSR_SIPH		0x154
+
 #define CSR_VSSTATUS		0x200
 #define CSR_VSIE		0x204
 #define CSR_VSTVEC		0x205
@@ -279,8 +314,32 @@
 #define CSR_HGATP		0x680
 #define CSR_HGEIP		0xe12
 
+/* Virtual Interrupts and Interrupt Priorities (H-extension with AIA) */
+#define CSR_HVIEN		0x608
+#define CSR_HVICTL		0x609
+#define CSR_HVIPRIO1		0x646
+#define CSR_HVIPRIO2		0x647
+
+/* VS-Level Window to Indirectly Accessed Registers (H-extension with AIA) */
+#define CSR_VSISELECT		0x250
+#define CSR_VSIREG		0x251
+
+/* VS-Level Interrupts (H-extension with AIA) */
+#define CSR_VSTOPEI		0x25c
+#define CSR_VSTOPI		0xeb0
+
+/* Hypervisor and VS-Level High-Half CSRs (H-extension with AIA) */
+#define CSR_HIDELEGH		0x613
+#define CSR_HVIENH		0x618
+#define CSR_HVIPH		0x655
+#define CSR_HVIPRIO1H		0x656
+#define CSR_HVIPRIO2H		0x657
+#define CSR_VSIEH		0x214
+#define CSR_VSIPH		0x254
+
 #define CSR_MSTATUS		0x300
 #define CSR_MISA		0x301
+#define CSR_MIDELEG		0x303
 #define CSR_MIE			0x304
 #define CSR_MTVEC		0x305
 #define CSR_MENVCFG		0x30a
@@ -297,6 +356,25 @@
 #define CSR_MIMPID		0xf13
 #define CSR_MHARTID		0xf14
 
+/* Machine-Level Window to Indirectly Accessed Registers (AIA) */
+#define CSR_MISELECT		0x350
+#define CSR_MIREG		0x351
+
+/* Machine-Level Interrupts (AIA) */
+#define CSR_MTOPEI		0x35c
+#define CSR_MTOPI		0xfb0
+
+/* Virtual Interrupts for Supervisor Level (AIA) */
+#define CSR_MVIEN		0x308
+#define CSR_MVIP		0x309
+
+/* Machine-Level High-Half CSRs (AIA) */
+#define CSR_MIDELEGH		0x313
+#define CSR_MIEH		0x314
+#define CSR_MVIENH		0x318
+#define CSR_MVIPH		0x319
+#define CSR_MIPH		0x354
+
 #ifdef CONFIG_RISCV_M_MODE
 # define CSR_STATUS	CSR_MSTATUS
 # define CSR_IE		CSR_MIE
@@ -307,6 +385,13 @@
 # define CSR_TVAL	CSR_MTVAL
 # define CSR_IP		CSR_MIP
 
+# define CSR_IEH		CSR_MIEH
+# define CSR_ISELECT	CSR_MISELECT
+# define CSR_IREG	CSR_MIREG
+# define CSR_IPH		CSR_MIPH
+# define CSR_TOPEI	CSR_MTOPEI
+# define CSR_TOPI	CSR_MTOPI
+
 # define SR_IE		SR_MIE
 # define SR_PIE		SR_MPIE
 # define SR_PP		SR_MPP
@@ -324,6 +409,13 @@
 # define CSR_TVAL	CSR_STVAL
 # define CSR_IP		CSR_SIP
 
+# define CSR_IEH		CSR_SIEH
+# define CSR_ISELECT	CSR_SISELECT
+# define CSR_IREG	CSR_SIREG
+# define CSR_IPH		CSR_SIPH
+# define CSR_TOPEI	CSR_STOPEI
+# define CSR_TOPI	CSR_STOPI
+
 # define SR_IE		SR_SIE
 # define SR_PIE		SR_SPIE
 # define SR_PP		SR_SPP
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* [PATCH 2/9] RISC-V: Detect AIA CSRs from ISA string
  2022-11-11  4:41 ` Anup Patel
@ 2022-11-11  4:42   ` Anup Patel
  -1 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-11  4:42 UTC (permalink / raw)
  To: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski
  Cc: Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree, Anup Patel

We have two extension names for AIA ISA support: Smaia (M-mode AIA CSRs)
and Ssaia (S-mode AIA CSRs).

We extend the ISA string parsing to detect Smaia and Ssaia extensions.

Signed-off-by: Anup Patel <apatel@ventanamicro.com>
---
 arch/riscv/include/asm/hwcap.h | 8 ++++++++
 arch/riscv/kernel/cpu.c        | 2 ++
 arch/riscv/kernel/cpufeature.c | 2 ++
 3 files changed, 12 insertions(+)

diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h
index b22525290073..06314220284f 100644
--- a/arch/riscv/include/asm/hwcap.h
+++ b/arch/riscv/include/asm/hwcap.h
@@ -59,9 +59,17 @@ enum riscv_isa_ext_id {
 	RISCV_ISA_EXT_ZIHINTPAUSE,
 	RISCV_ISA_EXT_SSTC,
 	RISCV_ISA_EXT_SVINVAL,
+	RISCV_ISA_EXT_SSAIA,
+	RISCV_ISA_EXT_SMAIA,
 	RISCV_ISA_EXT_ID_MAX = RISCV_ISA_EXT_MAX,
 };
 
+#ifdef CONFIG_RISCV_M_MODE
+#define RISCV_ISA_EXT_SxAIA		RISCV_ISA_EXT_SMAIA
+#else
+#define RISCV_ISA_EXT_SxAIA		RISCV_ISA_EXT_SSAIA
+#endif
+
 /*
  * This enum represents the logical ID for each RISC-V ISA extension static
  * keys. We can use static key to optimize code path if some ISA extensions
diff --git a/arch/riscv/kernel/cpu.c b/arch/riscv/kernel/cpu.c
index 852ecccd8920..3c84680c2289 100644
--- a/arch/riscv/kernel/cpu.c
+++ b/arch/riscv/kernel/cpu.c
@@ -138,6 +138,8 @@ device_initcall(riscv_cpuinfo_init);
  *    extensions by an underscore.
  */
 static struct riscv_isa_ext_data isa_ext_arr[] = {
+	__RISCV_ISA_EXT_DATA(smaia, RISCV_ISA_EXT_SMAIA),
+	__RISCV_ISA_EXT_DATA(ssaia, RISCV_ISA_EXT_SSAIA),
 	__RISCV_ISA_EXT_DATA(sscofpmf, RISCV_ISA_EXT_SSCOFPMF),
 	__RISCV_ISA_EXT_DATA(sstc, RISCV_ISA_EXT_SSTC),
 	__RISCV_ISA_EXT_DATA(svinval, RISCV_ISA_EXT_SVINVAL),
diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
index 694267d1fe81..e6d750d088ab 100644
--- a/arch/riscv/kernel/cpufeature.c
+++ b/arch/riscv/kernel/cpufeature.c
@@ -205,6 +205,8 @@ void __init riscv_fill_hwcap(void)
 				SET_ISA_EXT_MAP("zihintpause", RISCV_ISA_EXT_ZIHINTPAUSE);
 				SET_ISA_EXT_MAP("sstc", RISCV_ISA_EXT_SSTC);
 				SET_ISA_EXT_MAP("svinval", RISCV_ISA_EXT_SVINVAL);
+				SET_ISA_EXT_MAP("smaia", RISCV_ISA_EXT_SMAIA);
+				SET_ISA_EXT_MAP("ssaia", RISCV_ISA_EXT_SSAIA);
 			}
 #undef SET_ISA_EXT_MAP
 		}
-- 
2.34.1


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

* [PATCH 2/9] RISC-V: Detect AIA CSRs from ISA string
@ 2022-11-11  4:42   ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-11  4:42 UTC (permalink / raw)
  To: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski
  Cc: Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree, Anup Patel

We have two extension names for AIA ISA support: Smaia (M-mode AIA CSRs)
and Ssaia (S-mode AIA CSRs).

We extend the ISA string parsing to detect Smaia and Ssaia extensions.

Signed-off-by: Anup Patel <apatel@ventanamicro.com>
---
 arch/riscv/include/asm/hwcap.h | 8 ++++++++
 arch/riscv/kernel/cpu.c        | 2 ++
 arch/riscv/kernel/cpufeature.c | 2 ++
 3 files changed, 12 insertions(+)

diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h
index b22525290073..06314220284f 100644
--- a/arch/riscv/include/asm/hwcap.h
+++ b/arch/riscv/include/asm/hwcap.h
@@ -59,9 +59,17 @@ enum riscv_isa_ext_id {
 	RISCV_ISA_EXT_ZIHINTPAUSE,
 	RISCV_ISA_EXT_SSTC,
 	RISCV_ISA_EXT_SVINVAL,
+	RISCV_ISA_EXT_SSAIA,
+	RISCV_ISA_EXT_SMAIA,
 	RISCV_ISA_EXT_ID_MAX = RISCV_ISA_EXT_MAX,
 };
 
+#ifdef CONFIG_RISCV_M_MODE
+#define RISCV_ISA_EXT_SxAIA		RISCV_ISA_EXT_SMAIA
+#else
+#define RISCV_ISA_EXT_SxAIA		RISCV_ISA_EXT_SSAIA
+#endif
+
 /*
  * This enum represents the logical ID for each RISC-V ISA extension static
  * keys. We can use static key to optimize code path if some ISA extensions
diff --git a/arch/riscv/kernel/cpu.c b/arch/riscv/kernel/cpu.c
index 852ecccd8920..3c84680c2289 100644
--- a/arch/riscv/kernel/cpu.c
+++ b/arch/riscv/kernel/cpu.c
@@ -138,6 +138,8 @@ device_initcall(riscv_cpuinfo_init);
  *    extensions by an underscore.
  */
 static struct riscv_isa_ext_data isa_ext_arr[] = {
+	__RISCV_ISA_EXT_DATA(smaia, RISCV_ISA_EXT_SMAIA),
+	__RISCV_ISA_EXT_DATA(ssaia, RISCV_ISA_EXT_SSAIA),
 	__RISCV_ISA_EXT_DATA(sscofpmf, RISCV_ISA_EXT_SSCOFPMF),
 	__RISCV_ISA_EXT_DATA(sstc, RISCV_ISA_EXT_SSTC),
 	__RISCV_ISA_EXT_DATA(svinval, RISCV_ISA_EXT_SVINVAL),
diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
index 694267d1fe81..e6d750d088ab 100644
--- a/arch/riscv/kernel/cpufeature.c
+++ b/arch/riscv/kernel/cpufeature.c
@@ -205,6 +205,8 @@ void __init riscv_fill_hwcap(void)
 				SET_ISA_EXT_MAP("zihintpause", RISCV_ISA_EXT_ZIHINTPAUSE);
 				SET_ISA_EXT_MAP("sstc", RISCV_ISA_EXT_SSTC);
 				SET_ISA_EXT_MAP("svinval", RISCV_ISA_EXT_SVINVAL);
+				SET_ISA_EXT_MAP("smaia", RISCV_ISA_EXT_SMAIA);
+				SET_ISA_EXT_MAP("ssaia", RISCV_ISA_EXT_SSAIA);
 			}
 #undef SET_ISA_EXT_MAP
 		}
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* [PATCH 3/9] irqchip/riscv-intc: Add support for RISC-V AIA
  2022-11-11  4:41 ` Anup Patel
@ 2022-11-11  4:42   ` Anup Patel
  -1 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-11  4:42 UTC (permalink / raw)
  To: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski
  Cc: Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree, Anup Patel

The RISC-V advanced interrupt architecture (AIA) extends the per-HART
local interrupts in following ways:
1. Minimum 64 local interrupts for both RV32 and RV64
2. Ability to process multiple pending local interrupts in same
   interrupt handler
3. Priority configuration for each local interrupts
4. Special CSRs to configure/access the per-HART MSI controller

This patch adds support for RISC-V AIA in the RISC-V intc driver.

Signed-off-by: Anup Patel <apatel@ventanamicro.com>
---
 drivers/irqchip/irq-riscv-intc.c | 37 ++++++++++++++++++++++++++------
 1 file changed, 31 insertions(+), 6 deletions(-)

diff --git a/drivers/irqchip/irq-riscv-intc.c b/drivers/irqchip/irq-riscv-intc.c
index 784d25645704..e72969295241 100644
--- a/drivers/irqchip/irq-riscv-intc.c
+++ b/drivers/irqchip/irq-riscv-intc.c
@@ -16,6 +16,7 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/smp.h>
+#include <asm/hwcap.h>
 
 static struct irq_domain *intc_domain;
 
@@ -29,6 +30,15 @@ static asmlinkage void riscv_intc_irq(struct pt_regs *regs)
 	generic_handle_domain_irq(intc_domain, cause);
 }
 
+static asmlinkage void riscv_intc_aia_irq(struct pt_regs *regs)
+{
+	unsigned long topi;
+
+	while ((topi = csr_read(CSR_TOPI)))
+		generic_handle_domain_irq(intc_domain,
+					  topi >> TOPI_IID_SHIFT);
+}
+
 /*
  * On RISC-V systems local interrupts are masked or unmasked by writing
  * the SIE (Supervisor Interrupt Enable) CSR.  As CSRs can only be written
@@ -38,12 +48,18 @@ static asmlinkage void riscv_intc_irq(struct pt_regs *regs)
 
 static void riscv_intc_irq_mask(struct irq_data *d)
 {
-	csr_clear(CSR_IE, BIT(d->hwirq));
+	if (d->hwirq < BITS_PER_LONG)
+		csr_clear(CSR_IE, BIT(d->hwirq));
+	else
+		csr_clear(CSR_IEH, BIT(d->hwirq - BITS_PER_LONG));
 }
 
 static void riscv_intc_irq_unmask(struct irq_data *d)
 {
-	csr_set(CSR_IE, BIT(d->hwirq));
+	if (d->hwirq < BITS_PER_LONG)
+		csr_set(CSR_IE, BIT(d->hwirq));
+	else
+		csr_set(CSR_IEH, BIT(d->hwirq - BITS_PER_LONG));
 }
 
 static struct irq_chip riscv_intc_chip = {
@@ -98,7 +114,7 @@ static struct fwnode_handle *riscv_intc_hwnode(void)
 static int __init riscv_intc_init(struct device_node *node,
 				  struct device_node *parent)
 {
-	int rc;
+	int rc, nr_irqs;
 	unsigned long hartid;
 
 	rc = riscv_of_parent_hartid(node, &hartid);
@@ -116,14 +132,21 @@ static int __init riscv_intc_init(struct device_node *node,
 	if (riscv_hartid_to_cpuid(hartid) != smp_processor_id())
 		return 0;
 
-	intc_domain = irq_domain_add_linear(node, BITS_PER_LONG,
+	nr_irqs = BITS_PER_LONG;
+	if (riscv_isa_extension_available(NULL, SxAIA) && BITS_PER_LONG == 32)
+		nr_irqs = nr_irqs * 2;
+
+	intc_domain = irq_domain_add_linear(node, nr_irqs,
 					    &riscv_intc_domain_ops, NULL);
 	if (!intc_domain) {
 		pr_err("unable to add IRQ domain\n");
 		return -ENXIO;
 	}
 
-	rc = set_handle_irq(&riscv_intc_irq);
+	if (riscv_isa_extension_available(NULL, SxAIA))
+		rc = set_handle_irq(&riscv_intc_aia_irq);
+	else
+		rc = set_handle_irq(&riscv_intc_irq);
 	if (rc) {
 		pr_err("failed to set irq handler\n");
 		return rc;
@@ -131,7 +154,9 @@ static int __init riscv_intc_init(struct device_node *node,
 
 	riscv_set_intc_hwnode_fn(riscv_intc_hwnode);
 
-	pr_info("%d local interrupts mapped\n", BITS_PER_LONG);
+	pr_info("%d local interrupts mapped%s\n",
+		nr_irqs, (riscv_isa_extension_available(NULL, SxAIA)) ?
+			 " using AIA" : "");
 
 	return 0;
 }
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* [PATCH 3/9] irqchip/riscv-intc: Add support for RISC-V AIA
@ 2022-11-11  4:42   ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-11  4:42 UTC (permalink / raw)
  To: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski
  Cc: Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree, Anup Patel

The RISC-V advanced interrupt architecture (AIA) extends the per-HART
local interrupts in following ways:
1. Minimum 64 local interrupts for both RV32 and RV64
2. Ability to process multiple pending local interrupts in same
   interrupt handler
3. Priority configuration for each local interrupts
4. Special CSRs to configure/access the per-HART MSI controller

This patch adds support for RISC-V AIA in the RISC-V intc driver.

Signed-off-by: Anup Patel <apatel@ventanamicro.com>
---
 drivers/irqchip/irq-riscv-intc.c | 37 ++++++++++++++++++++++++++------
 1 file changed, 31 insertions(+), 6 deletions(-)

diff --git a/drivers/irqchip/irq-riscv-intc.c b/drivers/irqchip/irq-riscv-intc.c
index 784d25645704..e72969295241 100644
--- a/drivers/irqchip/irq-riscv-intc.c
+++ b/drivers/irqchip/irq-riscv-intc.c
@@ -16,6 +16,7 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/smp.h>
+#include <asm/hwcap.h>
 
 static struct irq_domain *intc_domain;
 
@@ -29,6 +30,15 @@ static asmlinkage void riscv_intc_irq(struct pt_regs *regs)
 	generic_handle_domain_irq(intc_domain, cause);
 }
 
+static asmlinkage void riscv_intc_aia_irq(struct pt_regs *regs)
+{
+	unsigned long topi;
+
+	while ((topi = csr_read(CSR_TOPI)))
+		generic_handle_domain_irq(intc_domain,
+					  topi >> TOPI_IID_SHIFT);
+}
+
 /*
  * On RISC-V systems local interrupts are masked or unmasked by writing
  * the SIE (Supervisor Interrupt Enable) CSR.  As CSRs can only be written
@@ -38,12 +48,18 @@ static asmlinkage void riscv_intc_irq(struct pt_regs *regs)
 
 static void riscv_intc_irq_mask(struct irq_data *d)
 {
-	csr_clear(CSR_IE, BIT(d->hwirq));
+	if (d->hwirq < BITS_PER_LONG)
+		csr_clear(CSR_IE, BIT(d->hwirq));
+	else
+		csr_clear(CSR_IEH, BIT(d->hwirq - BITS_PER_LONG));
 }
 
 static void riscv_intc_irq_unmask(struct irq_data *d)
 {
-	csr_set(CSR_IE, BIT(d->hwirq));
+	if (d->hwirq < BITS_PER_LONG)
+		csr_set(CSR_IE, BIT(d->hwirq));
+	else
+		csr_set(CSR_IEH, BIT(d->hwirq - BITS_PER_LONG));
 }
 
 static struct irq_chip riscv_intc_chip = {
@@ -98,7 +114,7 @@ static struct fwnode_handle *riscv_intc_hwnode(void)
 static int __init riscv_intc_init(struct device_node *node,
 				  struct device_node *parent)
 {
-	int rc;
+	int rc, nr_irqs;
 	unsigned long hartid;
 
 	rc = riscv_of_parent_hartid(node, &hartid);
@@ -116,14 +132,21 @@ static int __init riscv_intc_init(struct device_node *node,
 	if (riscv_hartid_to_cpuid(hartid) != smp_processor_id())
 		return 0;
 
-	intc_domain = irq_domain_add_linear(node, BITS_PER_LONG,
+	nr_irqs = BITS_PER_LONG;
+	if (riscv_isa_extension_available(NULL, SxAIA) && BITS_PER_LONG == 32)
+		nr_irqs = nr_irqs * 2;
+
+	intc_domain = irq_domain_add_linear(node, nr_irqs,
 					    &riscv_intc_domain_ops, NULL);
 	if (!intc_domain) {
 		pr_err("unable to add IRQ domain\n");
 		return -ENXIO;
 	}
 
-	rc = set_handle_irq(&riscv_intc_irq);
+	if (riscv_isa_extension_available(NULL, SxAIA))
+		rc = set_handle_irq(&riscv_intc_aia_irq);
+	else
+		rc = set_handle_irq(&riscv_intc_irq);
 	if (rc) {
 		pr_err("failed to set irq handler\n");
 		return rc;
@@ -131,7 +154,9 @@ static int __init riscv_intc_init(struct device_node *node,
 
 	riscv_set_intc_hwnode_fn(riscv_intc_hwnode);
 
-	pr_info("%d local interrupts mapped\n", BITS_PER_LONG);
+	pr_info("%d local interrupts mapped%s\n",
+		nr_irqs, (riscv_isa_extension_available(NULL, SxAIA)) ?
+			 " using AIA" : "");
 
 	return 0;
 }
-- 
2.34.1


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

* [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
  2022-11-11  4:41 ` Anup Patel
@ 2022-11-11  4:42   ` Anup Patel
  -1 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-11  4:42 UTC (permalink / raw)
  To: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski
  Cc: Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree, Anup Patel

We add DT bindings document for RISC-V incoming MSI controller (IMSIC)
defined by the RISC-V advanced interrupt architecture (AIA) specification.

Signed-off-by: Anup Patel <apatel@ventanamicro.com>
---
 .../interrupt-controller/riscv,imsic.yaml     | 174 ++++++++++++++++++
 1 file changed, 174 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml

diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
new file mode 100644
index 000000000000..05106eb1955e
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
@@ -0,0 +1,174 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/interrupt-controller/riscv,imsic.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: RISC-V Incoming MSI Controller (IMSIC)
+
+maintainers:
+  - Anup Patel <anup@brainfault.org>
+
+description:
+  The RISC-V advanced interrupt architecture (AIA) defines a per-CPU incoming
+  MSI controller (IMSIC) for handling MSIs in a RISC-V platform. The RISC-V
+  AIA specification can be found at https://github.com/riscv/riscv-aia.
+
+  The IMSIC is a per-CPU (or per-HART) device with separate interrupt file
+  for each privilege level (machine or supervisor). The configuration of
+  a IMSIC interrupt file is done using AIA CSRs and it also has a 4KB MMIO
+  space to receive MSIs from devices. Each IMSIC interrupt file supports a
+  fixed number of interrupt identities (to distinguish MSIs from devices)
+  which is same for given privilege level across CPUs (or HARTs).
+
+  The arrangement of IMSIC interrupt files in MMIO space of a RISC-V platform
+  follows a particular scheme defined by the RISC-V AIA specification. A IMSIC
+  group is a set of IMSIC interrupt files co-located in MMIO space and we can
+  have multiple IMSIC groups (i.e. clusters, sockets, chiplets, etc) in a
+  RISC-V platform. The MSI target address of a IMSIC interrupt file at given
+  privilege level (machine or supervisor) encodes group index, HART index,
+  and guest index (shown below).
+
+  XLEN-1           >=24                                 12    0
+  |                  |                                  |     |
+  -------------------------------------------------------------
+  |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
+  -------------------------------------------------------------
+
+  The device tree of a RISC-V platform will have one IMSIC device tree node
+  for each privilege level (machine or supervisor) which collectively describe
+  IMSIC interrupt files at that privilege level across CPUs (or HARTs).
+
+allOf:
+  - $ref: /schemas/interrupt-controller.yaml#
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - vendor,chip-imsics
+      - const: riscv,imsics
+
+  reg:
+    minItems: 1
+    maxItems: 128
+    description:
+      Base address of each IMSIC group.
+
+  interrupt-controller: true
+
+  "#interrupt-cells":
+    const: 0
+
+  msi-controller: true
+
+  interrupts-extended:
+    minItems: 1
+    maxItems: 32768
+    description:
+      This property represents the set of CPUs (or HARTs) for which given
+      device tree node describes the IMSIC interrupt files. Each node pointed
+      to should be a riscv,cpu-intc node, which has a riscv node (i.e. RISC-V
+      HART) as parent.
+
+  riscv,num-ids:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 63
+    maximum: 2047
+    description:
+      Specifies how many interrupt identities are supported by IMSIC interrupt
+      file.
+
+  riscv,num-guest-ids:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 63
+    maximum: 2047
+    description:
+      Specifies how many interrupt identities are supported by IMSIC guest
+      interrupt file. When not specified the number of interrupt identities
+      supported by IMSIC guest file is assumed to be same as specified by
+      the riscv,num-ids property.
+
+  riscv,slow-ipi:
+    type: boolean
+    description:
+      The presence of this property implies that software interrupts (i.e.
+      IPIs) using IMSIC software injected MSIs is slower compared to other
+      software interrupt mechanisms (such as SBI IPI) on the underlying
+      RISC-V platform.
+
+  riscv,guest-index-bits:
+    minimum: 0
+    maximum: 7
+    description:
+      Specifies number of guest index bits in the MSI target address. When
+      not specified it is assumed to be 0.
+
+  riscv,hart-index-bits:
+    minimum: 0
+    maximum: 15
+    description:
+      Specifies number of HART index bits in the MSI target address. When
+      not specified it is estimated based on the interrupts-extended property.
+
+  riscv,group-index-bits:
+    minimum: 0
+    maximum: 7
+    description:
+      Specifies number of group index bits in the MSI target address. When
+      not specified it is assumed to be 0.
+
+  riscv,group-index-shift:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 24
+    maximum: 55
+    description:
+      Specifies the least significant bit of the group index bits in the
+      MSI target address. When not specified it is assumed to be 24.
+
+additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - interrupt-controller
+  - msi-controller
+  - interrupts-extended
+  - riscv,num-ids
+
+examples:
+  - |
+    // Example 1 (Machine-level IMSIC files with just one group):
+
+    imsic_mlevel: interrupt-controller@24000000 {
+      compatible = "vendor,chip-imsics", "riscv,imsics";
+      interrupts-extended = <&cpu1_intc 11>,
+                            <&cpu2_intc 11>,
+                            <&cpu3_intc 11>,
+                            <&cpu4_intc 11>;
+      reg = <0x28000000 0x4000>;
+      interrupt-controller;
+      #interrupt-cells = <0>;
+      msi-controller;
+      riscv,num-ids = <127>;
+    };
+
+  - |
+    // Example 2 (Supervisor-level IMSIC files with two groups):
+
+    imsic_slevel: interrupt-controller@28000000 {
+      compatible = "vendor,chip-imsics", "riscv,imsics";
+      interrupts-extended = <&cpu1_intc 9>,
+                            <&cpu2_intc 9>,
+                            <&cpu3_intc 9>,
+                            <&cpu4_intc 9>;
+      reg = <0x28000000 0x2000>, /* Group0 IMSICs */
+            <0x29000000 0x2000>; /* Group1 IMSICs */
+      interrupt-controller;
+      #interrupt-cells = <0>;
+      msi-controller;
+      riscv,num-ids = <127>;
+      riscv,group-index-bits = <1>;
+      riscv,group-index-shift = <24>;
+    };
+...
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
@ 2022-11-11  4:42   ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-11  4:42 UTC (permalink / raw)
  To: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski
  Cc: Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree, Anup Patel

We add DT bindings document for RISC-V incoming MSI controller (IMSIC)
defined by the RISC-V advanced interrupt architecture (AIA) specification.

Signed-off-by: Anup Patel <apatel@ventanamicro.com>
---
 .../interrupt-controller/riscv,imsic.yaml     | 174 ++++++++++++++++++
 1 file changed, 174 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml

diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
new file mode 100644
index 000000000000..05106eb1955e
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
@@ -0,0 +1,174 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/interrupt-controller/riscv,imsic.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: RISC-V Incoming MSI Controller (IMSIC)
+
+maintainers:
+  - Anup Patel <anup@brainfault.org>
+
+description:
+  The RISC-V advanced interrupt architecture (AIA) defines a per-CPU incoming
+  MSI controller (IMSIC) for handling MSIs in a RISC-V platform. The RISC-V
+  AIA specification can be found at https://github.com/riscv/riscv-aia.
+
+  The IMSIC is a per-CPU (or per-HART) device with separate interrupt file
+  for each privilege level (machine or supervisor). The configuration of
+  a IMSIC interrupt file is done using AIA CSRs and it also has a 4KB MMIO
+  space to receive MSIs from devices. Each IMSIC interrupt file supports a
+  fixed number of interrupt identities (to distinguish MSIs from devices)
+  which is same for given privilege level across CPUs (or HARTs).
+
+  The arrangement of IMSIC interrupt files in MMIO space of a RISC-V platform
+  follows a particular scheme defined by the RISC-V AIA specification. A IMSIC
+  group is a set of IMSIC interrupt files co-located in MMIO space and we can
+  have multiple IMSIC groups (i.e. clusters, sockets, chiplets, etc) in a
+  RISC-V platform. The MSI target address of a IMSIC interrupt file at given
+  privilege level (machine or supervisor) encodes group index, HART index,
+  and guest index (shown below).
+
+  XLEN-1           >=24                                 12    0
+  |                  |                                  |     |
+  -------------------------------------------------------------
+  |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
+  -------------------------------------------------------------
+
+  The device tree of a RISC-V platform will have one IMSIC device tree node
+  for each privilege level (machine or supervisor) which collectively describe
+  IMSIC interrupt files at that privilege level across CPUs (or HARTs).
+
+allOf:
+  - $ref: /schemas/interrupt-controller.yaml#
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - vendor,chip-imsics
+      - const: riscv,imsics
+
+  reg:
+    minItems: 1
+    maxItems: 128
+    description:
+      Base address of each IMSIC group.
+
+  interrupt-controller: true
+
+  "#interrupt-cells":
+    const: 0
+
+  msi-controller: true
+
+  interrupts-extended:
+    minItems: 1
+    maxItems: 32768
+    description:
+      This property represents the set of CPUs (or HARTs) for which given
+      device tree node describes the IMSIC interrupt files. Each node pointed
+      to should be a riscv,cpu-intc node, which has a riscv node (i.e. RISC-V
+      HART) as parent.
+
+  riscv,num-ids:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 63
+    maximum: 2047
+    description:
+      Specifies how many interrupt identities are supported by IMSIC interrupt
+      file.
+
+  riscv,num-guest-ids:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 63
+    maximum: 2047
+    description:
+      Specifies how many interrupt identities are supported by IMSIC guest
+      interrupt file. When not specified the number of interrupt identities
+      supported by IMSIC guest file is assumed to be same as specified by
+      the riscv,num-ids property.
+
+  riscv,slow-ipi:
+    type: boolean
+    description:
+      The presence of this property implies that software interrupts (i.e.
+      IPIs) using IMSIC software injected MSIs is slower compared to other
+      software interrupt mechanisms (such as SBI IPI) on the underlying
+      RISC-V platform.
+
+  riscv,guest-index-bits:
+    minimum: 0
+    maximum: 7
+    description:
+      Specifies number of guest index bits in the MSI target address. When
+      not specified it is assumed to be 0.
+
+  riscv,hart-index-bits:
+    minimum: 0
+    maximum: 15
+    description:
+      Specifies number of HART index bits in the MSI target address. When
+      not specified it is estimated based on the interrupts-extended property.
+
+  riscv,group-index-bits:
+    minimum: 0
+    maximum: 7
+    description:
+      Specifies number of group index bits in the MSI target address. When
+      not specified it is assumed to be 0.
+
+  riscv,group-index-shift:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 24
+    maximum: 55
+    description:
+      Specifies the least significant bit of the group index bits in the
+      MSI target address. When not specified it is assumed to be 24.
+
+additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - interrupt-controller
+  - msi-controller
+  - interrupts-extended
+  - riscv,num-ids
+
+examples:
+  - |
+    // Example 1 (Machine-level IMSIC files with just one group):
+
+    imsic_mlevel: interrupt-controller@24000000 {
+      compatible = "vendor,chip-imsics", "riscv,imsics";
+      interrupts-extended = <&cpu1_intc 11>,
+                            <&cpu2_intc 11>,
+                            <&cpu3_intc 11>,
+                            <&cpu4_intc 11>;
+      reg = <0x28000000 0x4000>;
+      interrupt-controller;
+      #interrupt-cells = <0>;
+      msi-controller;
+      riscv,num-ids = <127>;
+    };
+
+  - |
+    // Example 2 (Supervisor-level IMSIC files with two groups):
+
+    imsic_slevel: interrupt-controller@28000000 {
+      compatible = "vendor,chip-imsics", "riscv,imsics";
+      interrupts-extended = <&cpu1_intc 9>,
+                            <&cpu2_intc 9>,
+                            <&cpu3_intc 9>,
+                            <&cpu4_intc 9>;
+      reg = <0x28000000 0x2000>, /* Group0 IMSICs */
+            <0x29000000 0x2000>; /* Group1 IMSICs */
+      interrupt-controller;
+      #interrupt-cells = <0>;
+      msi-controller;
+      riscv,num-ids = <127>;
+      riscv,group-index-bits = <1>;
+      riscv,group-index-shift = <24>;
+    };
+...
-- 
2.34.1


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

* [PATCH 5/9] irqchip: Add RISC-V incoming MSI controller driver
  2022-11-11  4:41 ` Anup Patel
@ 2022-11-11  4:42   ` Anup Patel
  -1 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-11  4:42 UTC (permalink / raw)
  To: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski
  Cc: Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree, Anup Patel

The RISC-V advanced interrupt architecture (AIA) specification defines
a new MSI controller for managing MSIs on a RISC-V platform. This new
MSI controller is referred to as incoming message signaled interrupt
controller (IMSIC) which manages MSI on per-HART (or per-CPU) basis.
(For more details refer https://github.com/riscv/riscv-aia)

This patch adds an irqchip driver for RISC-V IMSIC found on RISC-V
platforms.

Signed-off-by: Anup Patel <apatel@ventanamicro.com>
---
 drivers/irqchip/Kconfig             |   20 +-
 drivers/irqchip/Makefile            |    1 +
 drivers/irqchip/irq-riscv-imsic.c   | 1207 +++++++++++++++++++++++++++
 include/linux/irqchip/riscv-imsic.h |   92 ++
 4 files changed, 1319 insertions(+), 1 deletion(-)
 create mode 100644 drivers/irqchip/irq-riscv-imsic.c
 create mode 100644 include/linux/irqchip/riscv-imsic.h

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 7ef9f5e696d3..8246c08f0fd3 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -29,7 +29,6 @@ config ARM_GIC_V2M
 
 config GIC_NON_BANKED
 	bool
-
 config ARM_GIC_V3
 	bool
 	select IRQ_DOMAIN_HIERARCHY
@@ -564,6 +563,25 @@ config SIFIVE_PLIC
 
 	   If you don't know what to do here, say Y.
 
+config RISCV_IMSIC
+	bool "RISC-V Incoming MSI Controller"
+	depends on RISCV
+	select IRQ_DOMAIN_HIERARCHY
+	select GENERIC_MSI_IRQ_DOMAIN
+	help
+	   This enables support for the IMSIC chip found in RISC-V systems.
+	   The IMSIC controls message signaled interrupts and forwards them
+	   to each core as wired local interrupt.
+
+	   If you don't know what to do here, say Y.
+
+config RISCV_IMSIC_PCI
+	bool
+	depends on RISCV_IMSIC
+	depends on PCI
+	depends on PCI_MSI
+	default RISCV_IMSIC
+
 config EXYNOS_IRQ_COMBINER
 	bool "Samsung Exynos IRQ combiner support" if COMPILE_TEST
 	depends on (ARCH_EXYNOS && ARM) || COMPILE_TEST
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 87b49a10962c..22c723cc6ec8 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -96,6 +96,7 @@ obj-$(CONFIG_QCOM_MPM)			+= irq-qcom-mpm.o
 obj-$(CONFIG_CSKY_MPINTC)		+= irq-csky-mpintc.o
 obj-$(CONFIG_CSKY_APB_INTC)		+= irq-csky-apb-intc.o
 obj-$(CONFIG_RISCV_INTC)		+= irq-riscv-intc.o
+obj-$(CONFIG_RISCV_IMSIC)		+= irq-riscv-imsic.o
 obj-$(CONFIG_SIFIVE_PLIC)		+= irq-sifive-plic.o
 obj-$(CONFIG_IMX_IRQSTEER)		+= irq-imx-irqsteer.o
 obj-$(CONFIG_IMX_INTMUX)		+= irq-imx-intmux.o
diff --git a/drivers/irqchip/irq-riscv-imsic.c b/drivers/irqchip/irq-riscv-imsic.c
new file mode 100644
index 000000000000..95324fb4f5ed
--- /dev/null
+++ b/drivers/irqchip/irq-riscv-imsic.c
@@ -0,0 +1,1207 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Western Digital Corporation or its affiliates.
+ * Copyright (C) 2022 Ventana Micro Systems Inc.
+ */
+
+#define pr_fmt(fmt) "riscv-imsic: " fmt
+#include <linux/bitmap.h>
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iommu.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqchip/riscv-imsic.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/smp.h>
+#include <asm/hwcap.h>
+
+#define IMSIC_DISABLE_EIDELIVERY	0
+#define IMSIC_ENABLE_EIDELIVERY		1
+#define IMSIC_DISABLE_EITHRESHOLD	1
+#define IMSIC_ENABLE_EITHRESHOLD	0
+
+#define imsic_csr_write(__c, __v)	\
+do {					\
+	csr_write(CSR_ISELECT, __c);	\
+	csr_write(CSR_IREG, __v);	\
+} while (0)
+
+#define imsic_csr_read(__c)		\
+({					\
+	unsigned long __v;		\
+	csr_write(CSR_ISELECT, __c);	\
+	__v = csr_read(CSR_IREG);	\
+	__v;				\
+})
+
+#define imsic_csr_set(__c, __v)		\
+do {					\
+	csr_write(CSR_ISELECT, __c);	\
+	csr_set(CSR_IREG, __v);		\
+} while (0)
+
+#define imsic_csr_clear(__c, __v)	\
+do {					\
+	csr_write(CSR_ISELECT, __c);	\
+	csr_clear(CSR_IREG, __v);	\
+} while (0)
+
+struct imsic_mmio {
+	phys_addr_t pa;
+	void __iomem *va;
+	unsigned long size;
+};
+
+struct imsic_priv {
+	/* Global configuration common for all HARTs */
+	struct imsic_global_config global;
+
+	/* MMIO regions */
+	u32 num_mmios;
+	struct imsic_mmio *mmios;
+
+	/* Global state of interrupt identities */
+	raw_spinlock_t ids_lock;
+	unsigned long *ids_used_bimap;
+	unsigned long *ids_enabled_bimap;
+	unsigned int *ids_target_cpu;
+
+	/* Mask for connected CPUs */
+	struct cpumask lmask;
+
+	/* IPI interrupt identity */
+	bool slow_ipi;
+	u32 ipi_id;
+	u32 ipi_lsync_id;
+
+	/* IRQ domains */
+	struct irq_domain *base_domain;
+	struct irq_domain *pci_domain;
+	struct irq_domain *plat_domain;
+};
+
+struct imsic_handler {
+	/* Local configuration for given HART */
+	struct imsic_local_config local;
+
+	/* Pointer to private context */
+	struct imsic_priv *priv;
+};
+
+static bool imsic_init_done;
+
+static int imsic_parent_irq;
+static DEFINE_PER_CPU(struct imsic_handler, imsic_handlers);
+
+const struct imsic_global_config *imsic_get_global_config(void)
+{
+	struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
+
+	if (!handler || !handler->priv)
+		return NULL;
+
+	return &handler->priv->global;
+}
+EXPORT_SYMBOL_GPL(imsic_get_global_config);
+
+const struct imsic_local_config *imsic_get_local_config(unsigned int cpu)
+{
+	struct imsic_handler *handler = per_cpu_ptr(&imsic_handlers, cpu);
+
+	if (!handler || !handler->priv)
+		return NULL;
+
+	return &handler->local;
+}
+EXPORT_SYMBOL_GPL(imsic_get_local_config);
+
+static int imsic_cpu_page_phys(unsigned int cpu,
+			       unsigned int guest_index,
+			       phys_addr_t *out_msi_pa)
+{
+	struct imsic_handler *handler = per_cpu_ptr(&imsic_handlers, cpu);
+	struct imsic_global_config *global;
+	struct imsic_local_config *local;
+
+	if (!handler || !handler->priv)
+		return -ENODEV;
+	local = &handler->local;
+	global = &handler->priv->global;
+
+	if (BIT(global->guest_index_bits) <= guest_index)
+		return -EINVAL;
+
+	if (out_msi_pa)
+		*out_msi_pa = local->msi_pa +
+			      (guest_index * IMSIC_MMIO_PAGE_SZ);
+
+	return 0;
+}
+
+static int imsic_get_cpu(struct imsic_priv *priv,
+			 const struct cpumask *mask_val, bool force,
+			 unsigned int *out_target_cpu)
+{
+	struct cpumask amask;
+	unsigned int cpu;
+
+	cpumask_and(&amask, &priv->lmask, mask_val);
+
+	if (force)
+		cpu = cpumask_first(&amask);
+	else
+		cpu = cpumask_any_and(&amask, cpu_online_mask);
+
+	if (cpu >= nr_cpu_ids)
+		return -EINVAL;
+
+	if (out_target_cpu)
+		*out_target_cpu = cpu;
+
+	return 0;
+}
+
+static int imsic_get_cpu_msi_msg(unsigned int cpu, unsigned int id,
+				 struct msi_msg *msg)
+{
+	phys_addr_t msi_addr;
+	int err;
+
+	err = imsic_cpu_page_phys(cpu, 0, &msi_addr);
+	if (err)
+		return err;
+
+	msg->address_hi = upper_32_bits(msi_addr);
+	msg->address_lo = lower_32_bits(msi_addr);
+	msg->data = id;
+
+	return err;
+}
+
+static void imsic_id_set_target(struct imsic_priv *priv,
+				 unsigned int id, unsigned int target_cpu)
+{
+	raw_spin_lock(&priv->ids_lock);
+	priv->ids_target_cpu[id] = target_cpu;
+	raw_spin_unlock(&priv->ids_lock);
+}
+
+static unsigned int imsic_id_get_target(struct imsic_priv *priv,
+					unsigned int id)
+{
+	unsigned int ret;
+
+	raw_spin_lock(&priv->ids_lock);
+	ret = priv->ids_target_cpu[id];
+	raw_spin_unlock(&priv->ids_lock);
+
+	return ret;
+}
+
+static void __imsic_eix_update(unsigned long base_id,
+			       unsigned long num_id, bool pend, bool val)
+{
+	unsigned long i, isel, ireg, flags;
+	unsigned long id = base_id, last_id = base_id + num_id;
+
+	while (id < last_id) {
+		isel = id / BITS_PER_LONG;
+		isel *= BITS_PER_LONG / IMSIC_EIPx_BITS;
+		isel += (pend) ? IMSIC_EIP0 : IMSIC_EIE0;
+
+		ireg = 0;
+		for (i = id & (__riscv_xlen - 1);
+		     (id < last_id) && (i < __riscv_xlen); i++) {
+			ireg |= BIT(i);
+			id++;
+		}
+
+		/*
+		 * The IMSIC EIEx and EIPx registers are indirectly
+		 * accessed via using ISELECT and IREG CSRs so we
+		 * save/restore local IRQ to ensure that we don't
+		 * get preempted while accessing IMSIC registers.
+		 */
+		local_irq_save(flags);
+		if (val)
+			imsic_csr_set(isel, ireg);
+		else
+			imsic_csr_clear(isel, ireg);
+		local_irq_restore(flags);
+	}
+}
+
+#define __imsic_id_enable(__id)		\
+	__imsic_eix_update((__id), 1, false, true)
+#define __imsic_id_disable(__id)	\
+	__imsic_eix_update((__id), 1, false, false)
+
+#ifdef CONFIG_SMP
+static void __imsic_id_smp_sync(struct imsic_priv *priv)
+{
+	struct imsic_handler *handler;
+	struct cpumask amask;
+	int cpu;
+
+	cpumask_and(&amask, &priv->lmask, cpu_online_mask);
+	for_each_cpu(cpu, &amask) {
+		if (cpu == smp_processor_id())
+			continue;
+
+		handler = per_cpu_ptr(&imsic_handlers, cpu);
+		if (!handler || !handler->priv || !handler->local.msi_va) {
+			pr_warn("CPU%d: handler not initialized\n", cpu);
+			continue;
+		}
+
+		writel(handler->priv->ipi_lsync_id, handler->local.msi_va);
+	}
+}
+#else
+#define __imsic_id_smp_sync(__priv)
+#endif
+
+static void imsic_id_enable(struct imsic_priv *priv, unsigned int id)
+{
+	raw_spin_lock(&priv->ids_lock);
+	bitmap_set(priv->ids_enabled_bimap, id, 1);
+	__imsic_id_enable(id);
+	raw_spin_unlock(&priv->ids_lock);
+
+	__imsic_id_smp_sync(priv);
+}
+
+static void imsic_id_disable(struct imsic_priv *priv, unsigned int id)
+{
+	raw_spin_lock(&priv->ids_lock);
+	bitmap_clear(priv->ids_enabled_bimap, id, 1);
+	__imsic_id_disable(id);
+	raw_spin_unlock(&priv->ids_lock);
+
+	__imsic_id_smp_sync(priv);
+}
+
+static void imsic_ids_local_sync(struct imsic_priv *priv)
+{
+	int i;
+
+	raw_spin_lock(&priv->ids_lock);
+	for (i = 1; i <= priv->global.nr_ids; i++) {
+		if (priv->ipi_id == i || priv->ipi_lsync_id == i)
+			continue;
+
+		if (test_bit(i, priv->ids_enabled_bimap))
+			__imsic_id_enable(i);
+		else
+			__imsic_id_disable(i);
+	}
+	raw_spin_unlock(&priv->ids_lock);
+}
+
+static void imsic_ids_local_delivery(struct imsic_priv *priv, bool enable)
+{
+	if (enable) {
+		imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_ENABLE_EITHRESHOLD);
+		imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_ENABLE_EIDELIVERY);
+	} else {
+		imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_DISABLE_EIDELIVERY);
+		imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_DISABLE_EITHRESHOLD);
+	}
+}
+
+static int imsic_ids_alloc(struct imsic_priv *priv,
+			   unsigned int max_id, unsigned int order)
+{
+	int ret;
+
+	if ((priv->global.nr_ids < max_id) ||
+	    (max_id < BIT(order)))
+		return -EINVAL;
+
+	raw_spin_lock(&priv->ids_lock);
+	ret = bitmap_find_free_region(priv->ids_used_bimap,
+				      max_id + 1, order);
+	raw_spin_unlock(&priv->ids_lock);
+
+	return ret;
+}
+
+static void imsic_ids_free(struct imsic_priv *priv, unsigned int base_id,
+			   unsigned int order)
+{
+	raw_spin_lock(&priv->ids_lock);
+	bitmap_release_region(priv->ids_used_bimap, base_id, order);
+	raw_spin_unlock(&priv->ids_lock);
+}
+
+static int __init imsic_ids_init(struct imsic_priv *priv)
+{
+	int i;
+	struct imsic_global_config *global = &priv->global;
+
+	raw_spin_lock_init(&priv->ids_lock);
+
+	/* Allocate used bitmap */
+	priv->ids_used_bimap = kcalloc(BITS_TO_LONGS(global->nr_ids + 1),
+					sizeof(unsigned long), GFP_KERNEL);
+	if (!priv->ids_used_bimap)
+		return -ENOMEM;
+
+	/* Allocate enabled bitmap */
+	priv->ids_enabled_bimap = kcalloc(BITS_TO_LONGS(global->nr_ids + 1),
+					   sizeof(unsigned long), GFP_KERNEL);
+	if (!priv->ids_enabled_bimap) {
+		kfree(priv->ids_used_bimap);
+		return -ENOMEM;
+	}
+
+	/* Allocate target CPU array */
+	priv->ids_target_cpu = kcalloc(global->nr_ids + 1,
+				       sizeof(unsigned int), GFP_KERNEL);
+	if (!priv->ids_target_cpu) {
+		kfree(priv->ids_enabled_bimap);
+		kfree(priv->ids_used_bimap);
+		return -ENOMEM;
+	}
+	for (i = 0; i <= global->nr_ids; i++)
+		priv->ids_target_cpu[i] = UINT_MAX;
+
+	/* Reserve ID#0 because it is special and never implemented */
+	bitmap_set(priv->ids_used_bimap, 0, 1);
+
+	return 0;
+}
+
+static void __init imsic_ids_cleanup(struct imsic_priv *priv)
+{
+	kfree(priv->ids_target_cpu);
+	kfree(priv->ids_enabled_bimap);
+	kfree(priv->ids_used_bimap);
+}
+
+#ifdef CONFIG_SMP
+static void imsic_ipi_send_mask(unsigned int parent_virq, void *data,
+				const struct cpumask *mask)
+{
+	int cpu;
+	struct imsic_handler *handler;
+
+	for_each_cpu(cpu, mask) {
+		handler = per_cpu_ptr(&imsic_handlers, cpu);
+		if (!handler || !handler->priv || !handler->local.msi_va) {
+			pr_warn("CPU%d: handler not initialized\n", cpu);
+			continue;
+		}
+
+		writel(handler->priv->ipi_id, handler->local.msi_va);
+	}
+}
+
+static struct ipi_mux_ops imsic_ipi_ops = {
+	.ipi_mux_send = imsic_ipi_send_mask,
+};
+
+static void imsic_ipi_enable(struct imsic_priv *priv)
+{
+	__imsic_id_enable(priv->ipi_id);
+	__imsic_id_enable(priv->ipi_lsync_id);
+}
+
+static void imsic_ipi_disable(struct imsic_priv *priv)
+{
+	__imsic_id_disable(priv->ipi_lsync_id);
+	__imsic_id_disable(priv->ipi_id);
+}
+
+static int __init imsic_ipi_domain_init(struct imsic_priv *priv)
+{
+	int virq;
+
+	/* Skip IPI setup if IPIs are slow */
+	if (priv->slow_ipi)
+		goto skip_ipi;
+
+	/* Allocate interrupt identity for IPIs */
+	virq = imsic_ids_alloc(priv, priv->global.nr_ids, get_count_order(1));
+	if (virq < 0)
+		return virq;
+	priv->ipi_id = virq;
+
+	/* Create IMSIC IPI multiplexing */
+	virq = ipi_mux_create(0, BITS_PER_BYTE, &imsic_ipi_ops, NULL);
+	if (virq <= 0) {
+		imsic_ids_free(priv, priv->ipi_id, get_count_order(1));
+		return (virq < 0) ? virq : -ENOMEM;
+	}
+
+	/* Set vIRQ range */
+	riscv_ipi_set_virq_range(virq, BITS_PER_BYTE, true);
+
+skip_ipi:
+	/* Allocate interrupt identity for local enable/disable sync */
+	virq = imsic_ids_alloc(priv, priv->global.nr_ids, get_count_order(1));
+	if (virq < 0) {
+		imsic_ids_free(priv, priv->ipi_id, get_count_order(1));
+		return virq;
+	}
+	priv->ipi_lsync_id = virq;
+
+	return 0;
+}
+
+static void __init imsic_ipi_domain_cleanup(struct imsic_priv *priv)
+{
+	imsic_ids_free(priv, priv->ipi_lsync_id, get_count_order(1));
+	if (priv->ipi_id)
+		imsic_ids_free(priv, priv->ipi_id, get_count_order(1));
+}
+#else
+static void imsic_ipi_enable(struct imsic_priv *priv)
+{
+}
+
+static void imsic_ipi_disable(struct imsic_priv *priv)
+{
+}
+
+static int __init imsic_ipi_domain_init(struct imsic_priv *priv)
+{
+	/* Clear the IPI ids because we are not using IPIs */
+	priv->ipi_id = 0;
+	priv->ipi_lsync_id = 0;
+	return 0;
+}
+
+static void __init imsic_ipi_domain_cleanup(struct imsic_priv *priv)
+{
+}
+#endif
+
+static void imsic_irq_mask(struct irq_data *d)
+{
+	imsic_id_disable(irq_data_get_irq_chip_data(d), d->hwirq);
+}
+
+static void imsic_irq_unmask(struct irq_data *d)
+{
+	imsic_id_enable(irq_data_get_irq_chip_data(d), d->hwirq);
+}
+
+static void imsic_irq_compose_msi_msg(struct irq_data *d,
+				      struct msi_msg *msg)
+{
+	struct imsic_priv *priv = irq_data_get_irq_chip_data(d);
+	unsigned int cpu;
+	int err;
+
+	cpu = imsic_id_get_target(priv, d->hwirq);
+	WARN_ON(cpu == UINT_MAX);
+
+	err = imsic_get_cpu_msi_msg(cpu, d->hwirq, msg);
+	WARN_ON(err);
+
+	iommu_dma_compose_msi_msg(irq_data_get_msi_desc(d), msg);
+}
+
+#ifdef CONFIG_SMP
+static int imsic_irq_set_affinity(struct irq_data *d,
+				  const struct cpumask *mask_val,
+				  bool force)
+{
+	struct imsic_priv *priv = irq_data_get_irq_chip_data(d);
+	unsigned int target_cpu;
+	int rc;
+
+	rc = imsic_get_cpu(priv, mask_val, force, &target_cpu);
+	if (rc)
+		return rc;
+
+	imsic_id_set_target(priv, d->hwirq, target_cpu);
+	irq_data_update_effective_affinity(d, cpumask_of(target_cpu));
+
+	return IRQ_SET_MASK_OK;
+}
+#endif
+
+static struct irq_chip imsic_irq_base_chip = {
+	.name			= "RISC-V IMSIC-BASE",
+	.irq_mask		= imsic_irq_mask,
+	.irq_unmask		= imsic_irq_unmask,
+#ifdef CONFIG_SMP
+	.irq_set_affinity	= imsic_irq_set_affinity,
+#endif
+	.irq_compose_msi_msg	= imsic_irq_compose_msi_msg,
+	.flags			= IRQCHIP_SKIP_SET_WAKE |
+				  IRQCHIP_MASK_ON_SUSPEND,
+};
+
+static int imsic_irq_domain_alloc(struct irq_domain *domain,
+				  unsigned int virq,
+				  unsigned int nr_irqs,
+				  void *args)
+{
+	struct imsic_priv *priv = domain->host_data;
+	msi_alloc_info_t *info = args;
+	phys_addr_t msi_addr;
+	int i, hwirq, err = 0;
+	unsigned int cpu;
+
+	err = imsic_get_cpu(priv, &priv->lmask, false, &cpu);
+	if (err)
+		return err;
+
+	err = imsic_cpu_page_phys(cpu, 0, &msi_addr);
+	if (err)
+		return err;
+
+	hwirq = imsic_ids_alloc(priv, priv->global.nr_ids,
+				get_count_order(nr_irqs));
+	if (hwirq < 0)
+		return hwirq;
+
+	err = iommu_dma_prepare_msi(info->desc, msi_addr);
+	if (err)
+		goto fail;
+
+	for (i = 0; i < nr_irqs; i++) {
+		imsic_id_set_target(priv, hwirq + i, cpu);
+		irq_domain_set_info(domain, virq + i, hwirq + i,
+				    &imsic_irq_base_chip, priv,
+				    handle_simple_irq, NULL, NULL);
+		irq_set_noprobe(virq + i);
+		irq_set_affinity(virq + i, &priv->lmask);
+	}
+
+	return 0;
+
+fail:
+	imsic_ids_free(priv, hwirq, get_count_order(nr_irqs));
+	return err;
+}
+
+static void imsic_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 imsic_priv *priv = domain->host_data;
+
+	imsic_ids_free(priv, d->hwirq, get_count_order(nr_irqs));
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops imsic_base_domain_ops = {
+	.alloc		= imsic_irq_domain_alloc,
+	.free		= imsic_irq_domain_free,
+};
+
+#ifdef CONFIG_RISCV_IMSIC_PCI
+
+static void imsic_pci_mask_irq(struct irq_data *d)
+{
+	pci_msi_mask_irq(d);
+	irq_chip_mask_parent(d);
+}
+
+static void imsic_pci_unmask_irq(struct irq_data *d)
+{
+	pci_msi_unmask_irq(d);
+	irq_chip_unmask_parent(d);
+}
+
+static struct irq_chip imsic_pci_irq_chip = {
+	.name			= "RISC-V IMSIC-PCI",
+	.irq_mask		= imsic_pci_mask_irq,
+	.irq_unmask		= imsic_pci_unmask_irq,
+	.irq_eoi		= irq_chip_eoi_parent,
+};
+
+static struct msi_domain_ops imsic_pci_domain_ops = {
+};
+
+static struct msi_domain_info imsic_pci_domain_info = {
+	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+		   MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI),
+	.ops	= &imsic_pci_domain_ops,
+	.chip	= &imsic_pci_irq_chip,
+};
+
+#endif
+
+static struct irq_chip imsic_plat_irq_chip = {
+	.name			= "RISC-V IMSIC-PLAT",
+};
+
+static struct msi_domain_ops imsic_plat_domain_ops = {
+};
+
+static struct msi_domain_info imsic_plat_domain_info = {
+	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
+	.ops	= &imsic_plat_domain_ops,
+	.chip	= &imsic_plat_irq_chip,
+};
+
+static int __init imsic_irq_domains_init(struct imsic_priv *priv,
+					 struct fwnode_handle *fwnode)
+{
+	/* Create Base IRQ domain */
+	priv->base_domain = irq_domain_create_tree(fwnode,
+						&imsic_base_domain_ops, priv);
+	if (!priv->base_domain) {
+		pr_err("Failed to create IMSIC base domain\n");
+		return -ENOMEM;
+	}
+	irq_domain_update_bus_token(priv->base_domain, DOMAIN_BUS_NEXUS);
+
+#ifdef CONFIG_RISCV_IMSIC_PCI
+	/* Create PCI MSI domain */
+	priv->pci_domain = pci_msi_create_irq_domain(fwnode,
+						&imsic_pci_domain_info,
+						priv->base_domain);
+	if (!priv->pci_domain) {
+		pr_err("Failed to create IMSIC PCI domain\n");
+		irq_domain_remove(priv->base_domain);
+		return -ENOMEM;
+	}
+#endif
+
+	/* Create Platform MSI domain */
+	priv->plat_domain = platform_msi_create_irq_domain(fwnode,
+						&imsic_plat_domain_info,
+						priv->base_domain);
+	if (!priv->plat_domain) {
+		pr_err("Failed to create IMSIC platform domain\n");
+		if (priv->pci_domain)
+			irq_domain_remove(priv->pci_domain);
+		irq_domain_remove(priv->base_domain);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/*
+ * To handle an interrupt, we read the TOPEI CSR and write zero in one
+ * instruction. If TOPEI CSR is non-zero then we translate TOPEI.ID to
+ * Linux interrupt number and let Linux IRQ subsystem handle it.
+ */
+static void imsic_handle_irq(struct irq_desc *desc)
+{
+	struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct imsic_priv *priv = handler->priv;
+	irq_hw_number_t hwirq;
+	int err;
+
+	WARN_ON_ONCE(!handler->priv);
+
+	chained_irq_enter(chip, desc);
+
+	while ((hwirq = csr_swap(CSR_TOPEI, 0))) {
+		hwirq = hwirq >> TOPEI_ID_SHIFT;
+
+		if (hwirq == priv->ipi_id) {
+#ifdef CONFIG_SMP
+			ipi_mux_process();
+#endif
+			continue;
+		} else if (hwirq == priv->ipi_lsync_id) {
+			imsic_ids_local_sync(priv);
+			continue;
+		}
+
+		err = generic_handle_domain_irq(priv->base_domain, hwirq);
+		if (unlikely(err))
+			pr_warn_ratelimited(
+				"hwirq %lu mapping not found\n", hwirq);
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static int imsic_dying_cpu(unsigned int cpu)
+{
+	struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
+	struct imsic_priv *priv = handler->priv;
+
+	/* Disable per-CPU parent interrupt */
+	if (imsic_parent_irq)
+		disable_percpu_irq(imsic_parent_irq);
+
+	/* Locally disable interrupt delivery */
+	imsic_ids_local_delivery(priv, false);
+
+	/* Disable IPIs */
+	imsic_ipi_disable(priv);
+
+	return 0;
+}
+
+static int imsic_starting_cpu(unsigned int cpu)
+{
+	struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
+	struct imsic_priv *priv = handler->priv;
+
+	/* Enable per-CPU parent interrupt */
+	if (imsic_parent_irq)
+		enable_percpu_irq(imsic_parent_irq,
+				  irq_get_trigger_type(imsic_parent_irq));
+	else
+		pr_warn("cpu%d: parent irq not available\n", cpu);
+
+	/* Enable IPIs */
+	imsic_ipi_enable(priv);
+
+	/*
+	 * Interrupts identities might have been enabled/disabled while
+	 * this CPU was not running so sync-up local enable/disable state.
+	 */
+	imsic_ids_local_sync(priv);
+
+	/* Locally enable interrupt delivery */
+	imsic_ids_local_delivery(priv, true);
+
+	return 0;
+}
+
+struct imsic_fwnode_ops {
+	u32 (*nr_parent_irq)(struct fwnode_handle *fwnode,
+			     void *fwopaque);
+	int (*parent_hartid)(struct fwnode_handle *fwnode,
+			     void *fwopaque, u32 index,
+			     unsigned long *out_hartid);
+	u32 (*nr_mmio)(struct fwnode_handle *fwnode, void *fwopaque);
+	int (*mmio_to_resource)(struct fwnode_handle *fwnode,
+				void *fwopaque, u32 index,
+				struct resource *res);
+	void __iomem *(*mmio_map)(struct fwnode_handle *fwnode,
+				  void *fwopaque, u32 index);
+	int (*read_u32)(struct fwnode_handle *fwnode,
+			void *fwopaque, const char *prop, u32 *out_val);
+	bool (*read_bool)(struct fwnode_handle *fwnode,
+			  void *fwopaque, const char *prop);
+};
+
+static int __init imsic_init(struct imsic_fwnode_ops *fwops,
+			     struct fwnode_handle *fwnode,
+			     void *fwopaque)
+{
+	struct resource res;
+	phys_addr_t base_addr;
+	int rc, nr_parent_irqs;
+	struct imsic_mmio *mmio;
+	struct imsic_priv *priv;
+	struct irq_domain *domain;
+	struct imsic_handler *handler;
+	struct imsic_global_config *global;
+	u32 i, tmp, nr_handlers = 0;
+
+	if (imsic_init_done) {
+		pr_err("%pfwP: already initialized hence ignoring\n",
+			fwnode);
+		return -ENODEV;
+	}
+
+	if (!riscv_isa_extension_available(NULL, SxAIA)) {
+		pr_err("%pfwP: AIA support not available\n", fwnode);
+		return -ENODEV;
+	}
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	global = &priv->global;
+
+	/* Find number of parent interrupts */
+	nr_parent_irqs = fwops->nr_parent_irq(fwnode, fwopaque);
+	if (!nr_parent_irqs) {
+		pr_err("%pfwP: no parent irqs available\n", fwnode);
+		return -EINVAL;
+	}
+
+	/* Find number of guest index bits in MSI address */
+	rc = fwops->read_u32(fwnode, fwopaque, "riscv,guest-index-bits",
+			     &global->guest_index_bits);
+	if (rc)
+		global->guest_index_bits = 0;
+	tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT;
+	if (tmp < global->guest_index_bits) {
+		pr_err("%pfwP: guest index bits too big\n", fwnode);
+		return -EINVAL;
+	}
+
+	/* Find number of HART index bits */
+	rc = fwops->read_u32(fwnode, fwopaque, "riscv,hart-index-bits",
+			     &global->hart_index_bits);
+	if (rc) {
+		/* Assume default value */
+		global->hart_index_bits = __fls(nr_parent_irqs);
+		if (BIT(global->hart_index_bits) < nr_parent_irqs)
+			global->hart_index_bits++;
+	}
+	tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT -
+	      global->guest_index_bits;
+	if (tmp < global->hart_index_bits) {
+		pr_err("%pfwP: HART index bits too big\n", fwnode);
+		return -EINVAL;
+	}
+
+	/* Find number of group index bits */
+	rc = fwops->read_u32(fwnode, fwopaque, "riscv,group-index-bits",
+			     &global->group_index_bits);
+	if (rc)
+		global->group_index_bits = 0;
+	tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT -
+	      global->guest_index_bits - global->hart_index_bits;
+	if (tmp < global->group_index_bits) {
+		pr_err("%pfwP: group index bits too big\n", fwnode);
+		return -EINVAL;
+	}
+
+	/* Find first bit position of group index */
+	tmp = IMSIC_MMIO_PAGE_SHIFT * 2;
+	rc = fwops->read_u32(fwnode, fwopaque, "riscv,group-index-shift",
+			     &global->group_index_shift);
+	if (rc)
+		global->group_index_shift = tmp;
+	if (global->group_index_shift < tmp) {
+		pr_err("%pfwP: group index shift too small\n", fwnode);
+		return -EINVAL;
+	}
+	tmp = global->group_index_bits + global->group_index_shift - 1;
+	if (tmp >= BITS_PER_LONG) {
+		pr_err("%pfwP: group index shift too big\n", fwnode);
+		return -EINVAL;
+	}
+
+	/* Find number of interrupt identities */
+	rc = fwops->read_u32(fwnode, fwopaque, "riscv,num-ids",
+			     &global->nr_ids);
+	if (rc) {
+		pr_err("%pfwP: number of interrupt identities not found\n",
+			fwnode);
+		return rc;
+	}
+	if ((global->nr_ids < IMSIC_MIN_ID) ||
+	    (global->nr_ids >= IMSIC_MAX_ID) ||
+	    ((global->nr_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID)) {
+		pr_err("%pfwP: invalid number of interrupt identities\n",
+			fwnode);
+		return -EINVAL;
+	}
+
+	/* Find number of guest interrupt identities */
+	if (fwops->read_u32(fwnode, fwopaque, "riscv,num-guest-ids",
+			    &global->nr_guest_ids))
+		global->nr_guest_ids = global->nr_ids;
+	if ((global->nr_guest_ids < IMSIC_MIN_ID) ||
+	    (global->nr_guest_ids >= IMSIC_MAX_ID) ||
+	    ((global->nr_guest_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID)) {
+		pr_err("%pfwP: invalid number of guest interrupt identities\n",
+			fwnode);
+		return -EINVAL;
+	}
+
+	/* Check if IPIs are slow */
+	priv->slow_ipi = fwops->read_bool(fwnode, fwopaque, "riscv,slow-ipi");
+
+	/* Compute base address */
+	rc = fwops->mmio_to_resource(fwnode, fwopaque, 0, &res);
+	if (rc) {
+		pr_err("%pfwP: first MMIO resource not found\n", fwnode);
+		return -EINVAL;
+	}
+	global->base_addr = res.start;
+	global->base_addr &= ~(BIT(global->guest_index_bits +
+				   global->hart_index_bits +
+				   IMSIC_MMIO_PAGE_SHIFT) - 1);
+	global->base_addr &= ~((BIT(global->group_index_bits) - 1) <<
+			       global->group_index_shift);
+
+	/* Find number of MMIO register sets */
+	priv->num_mmios = fwops->nr_mmio(fwnode, fwopaque);
+
+	/* Allocate MMIO register sets */
+	priv->mmios = kcalloc(priv->num_mmios, sizeof(*mmio), GFP_KERNEL);
+	if (!priv->mmios) {
+		rc = -ENOMEM;
+		goto out_free_priv;
+	}
+
+	/* Parse and map MMIO register sets */
+	for (i = 0; i < priv->num_mmios; i++) {
+		mmio = &priv->mmios[i];
+		rc = fwops->mmio_to_resource(fwnode, fwopaque, i, &res);
+		if (rc) {
+			pr_err("%pfwP: unable to parse MMIO regset %d\n",
+				fwnode, i);
+			goto out_iounmap;
+		}
+		mmio->pa = res.start;
+		mmio->size = res.end - res.start + 1;
+
+		base_addr = mmio->pa;
+		base_addr &= ~(BIT(global->guest_index_bits +
+				   global->hart_index_bits +
+				   IMSIC_MMIO_PAGE_SHIFT) - 1);
+		base_addr &= ~((BIT(global->group_index_bits) - 1) <<
+			       global->group_index_shift);
+		if (base_addr != global->base_addr) {
+			rc = -EINVAL;
+			pr_err("%pfwP: address mismatch for regset %d\n",
+				fwnode, i);
+			goto out_iounmap;
+		}
+
+		tmp = BIT(global->guest_index_bits) - 1;
+		if ((mmio->size / IMSIC_MMIO_PAGE_SZ) & tmp) {
+			rc = -EINVAL;
+			pr_err("%pfwP: size mismatch for regset %d\n",
+				fwnode, i);
+			goto out_iounmap;
+		}
+
+		mmio->va = fwops->mmio_map(fwnode, fwopaque, i);
+		if (!mmio->va) {
+			rc = -EIO;
+			pr_err("%pfwP: unable to map MMIO regset %d\n",
+				fwnode, i);
+			goto out_iounmap;
+		}
+	}
+
+	/* Initialize interrupt identity management */
+	rc = imsic_ids_init(priv);
+	if (rc) {
+		pr_err("%pfwP: failed to initialize interrupt management\n",
+		       fwnode);
+		goto out_iounmap;
+	}
+
+	/* Configure handlers for target CPUs */
+	for (i = 0; i < nr_parent_irqs; i++) {
+		unsigned long reloff, hartid;
+		int j, cpu;
+
+		rc = fwops->parent_hartid(fwnode, fwopaque, i, &hartid);
+		if (rc) {
+			pr_warn("%pfwP: hart ID for parent irq%d not found\n",
+				fwnode, i);
+			continue;
+		}
+
+		cpu = riscv_hartid_to_cpuid(hartid);
+		if (cpu < 0) {
+			pr_warn("%pfwP: invalid cpuid for parent irq%d\n",
+				fwnode, i);
+			continue;
+		}
+
+		/* Find MMIO location of MSI page */
+		mmio = NULL;
+		reloff = i * BIT(global->guest_index_bits) *
+			 IMSIC_MMIO_PAGE_SZ;
+		for (j = 0; priv->num_mmios; j++) {
+			if (reloff < priv->mmios[j].size) {
+				mmio = &priv->mmios[j];
+				break;
+			}
+
+			reloff -= priv->mmios[j].size;
+		}
+		if (!mmio) {
+			pr_warn("%pfwP: MMIO not found for parent irq%d\n",
+				fwnode, i);
+			continue;
+		}
+
+		handler = per_cpu_ptr(&imsic_handlers, cpu);
+		if (handler->priv) {
+			pr_warn("%pfwP: CPU%d handler already configured.\n",
+				fwnode, cpu);
+			goto done;
+		}
+
+		cpumask_set_cpu(cpu, &priv->lmask);
+		handler->local.msi_pa = mmio->pa + reloff;
+		handler->local.msi_va = mmio->va + reloff;
+		handler->priv = priv;
+
+done:
+		nr_handlers++;
+	}
+
+	/* If no CPU handlers found then can't take interrupts */
+	if (!nr_handlers) {
+		pr_err("%pfwP: No CPU handlers found\n", fwnode);
+		rc = -ENODEV;
+		goto out_ids_cleanup;
+	}
+
+	/* Find parent domain and register chained handler */
+	domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(),
+					  DOMAIN_BUS_ANY);
+	if (!domain) {
+		pr_err("%pfwP: Failed to find INTC domain\n", fwnode);
+		rc = -ENOENT;
+		goto out_ids_cleanup;
+	}
+	imsic_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT);
+	if (!imsic_parent_irq) {
+		pr_err("%pfwP: Failed to create INTC mapping\n", fwnode);
+		rc = -ENOENT;
+		goto out_ids_cleanup;
+	}
+	irq_set_chained_handler(imsic_parent_irq, imsic_handle_irq);
+
+	/* Initialize IPI domain */
+	rc = imsic_ipi_domain_init(priv);
+	if (rc) {
+		pr_err("%pfwP: Failed to initialize IPI domain\n", fwnode);
+		goto out_ids_cleanup;
+	}
+
+	/* Initialize IRQ and MSI domains */
+	rc = imsic_irq_domains_init(priv, fwnode);
+	if (rc) {
+		pr_err("%pfwP: Failed to initialize IRQ and MSI domains\n",
+		       fwnode);
+		goto out_ipi_domain_cleanup;
+	}
+
+	/* Setup cpuhp state */
+	cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
+			  "irqchip/riscv/imsic:starting",
+			  imsic_starting_cpu, imsic_dying_cpu);
+
+	/*
+	 * Only one IMSIC instance allowed in a platform for clean
+	 * implementation of SMP IRQ affinity and per-CPU IPIs.
+	 *
+	 * This means on a multi-socket (or multi-die) platform we
+	 * will have multiple MMIO regions for one IMSIC instance.
+	 */
+	imsic_init_done = true;
+
+	pr_info("%pfwP:  hart-index-bits: %d,  guest-index-bits: %d\n",
+		fwnode, global->hart_index_bits, global->guest_index_bits);
+	pr_info("%pfwP: group-index-bits: %d, group-index-shift: %d\n",
+		fwnode, global->group_index_bits, global->group_index_shift);
+	pr_info("%pfwP: mapped %d interrupts for %d CPUs at %pa\n",
+		fwnode, global->nr_ids, nr_handlers, &global->base_addr);
+	if (priv->ipi_lsync_id)
+		pr_info("%pfwP: enable/disable sync using interrupt %d\n",
+			fwnode, priv->ipi_lsync_id);
+	if (priv->ipi_id)
+		pr_info("%pfwP: providing IPIs using interrupt %d\n",
+			fwnode, priv->ipi_id);
+
+	return 0;
+
+out_ipi_domain_cleanup:
+	imsic_ipi_domain_cleanup(priv);
+out_ids_cleanup:
+	imsic_ids_cleanup(priv);
+out_iounmap:
+	for (i = 0; i < priv->num_mmios; i++) {
+		if (priv->mmios[i].va)
+			iounmap(priv->mmios[i].va);
+	}
+	kfree(priv->mmios);
+out_free_priv:
+	kfree(priv);
+	return rc;
+}
+
+static u32 __init imsic_dt_nr_parent_irq(struct fwnode_handle *fwnode,
+					 void *fwopaque)
+{
+	return of_irq_count(to_of_node(fwnode));
+}
+
+static int __init imsic_dt_parent_hartid(struct fwnode_handle *fwnode,
+					 void *fwopaque, u32 index,
+					 unsigned long *out_hartid)
+{
+	struct of_phandle_args parent;
+	int rc;
+
+	rc = of_irq_parse_one(to_of_node(fwnode), index, &parent);
+	if (rc)
+		return rc;
+
+	/*
+	 * Skip interrupts other than external interrupts for
+	 * current privilege level.
+	 */
+	if (parent.args[0] != RV_IRQ_EXT)
+		return -EINVAL;
+
+	return riscv_of_parent_hartid(parent.np, out_hartid);
+}
+
+static u32 __init imsic_dt_nr_mmio(struct fwnode_handle *fwnode,
+				   void *fwopaque)
+{
+	u32 ret = 0;
+	struct resource res;
+
+	while (!of_address_to_resource(to_of_node(fwnode), ret, &res))
+		ret++;
+
+	return ret;
+}
+
+static int __init imsic_mmio_to_resource(struct fwnode_handle *fwnode,
+					 void *fwopaque, u32 index,
+					 struct resource *res)
+{
+	return of_address_to_resource(to_of_node(fwnode), index, res);
+}
+
+static void __iomem __init *imsic_dt_mmio_map(struct fwnode_handle *fwnode,
+					      void *fwopaque, u32 index)
+{
+	return of_iomap(to_of_node(fwnode), index);
+}
+
+static int __init imsic_dt_read_u32(struct fwnode_handle *fwnode,
+				    void *fwopaque, const char *prop,
+				    u32 *out_val)
+{
+	return of_property_read_u32(to_of_node(fwnode), prop, out_val);
+}
+
+static bool __init imsic_dt_read_bool(struct fwnode_handle *fwnode,
+				      void *fwopaque, const char *prop)
+{
+	return of_property_read_bool(to_of_node(fwnode), prop);
+}
+
+static int __init imsic_dt_init(struct device_node *node,
+				struct device_node *parent)
+{
+	struct imsic_fwnode_ops ops = {
+		.nr_parent_irq = imsic_dt_nr_parent_irq,
+		.parent_hartid = imsic_dt_parent_hartid,
+		.nr_mmio = imsic_dt_nr_mmio,
+		.mmio_to_resource = imsic_mmio_to_resource,
+		.mmio_map = imsic_dt_mmio_map,
+		.read_u32 = imsic_dt_read_u32,
+		.read_bool = imsic_dt_read_bool,
+	};
+
+	return imsic_init(&ops, &node->fwnode, NULL);
+}
+IRQCHIP_DECLARE(riscv_imsic, "riscv,imsics", imsic_dt_init);
diff --git a/include/linux/irqchip/riscv-imsic.h b/include/linux/irqchip/riscv-imsic.h
new file mode 100644
index 000000000000..5d1387adc0ba
--- /dev/null
+++ b/include/linux/irqchip/riscv-imsic.h
@@ -0,0 +1,92 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2021 Western Digital Corporation or its affiliates.
+ * Copyright (C) 2022 Ventana Micro Systems Inc.
+ */
+#ifndef __LINUX_IRQCHIP_RISCV_IMSIC_H
+#define __LINUX_IRQCHIP_RISCV_IMSIC_H
+
+#include <linux/types.h>
+#include <asm/csr.h>
+
+#define IMSIC_MMIO_PAGE_SHIFT		12
+#define IMSIC_MMIO_PAGE_SZ		(1UL << IMSIC_MMIO_PAGE_SHIFT)
+#define IMSIC_MMIO_PAGE_LE		0x00
+#define IMSIC_MMIO_PAGE_BE		0x04
+
+#define IMSIC_MIN_ID			63
+#define IMSIC_MAX_ID			2048
+
+#define IMSIC_EIDELIVERY		0x70
+
+#define IMSIC_EITHRESHOLD		0x72
+
+#define IMSIC_EIP0			0x80
+#define IMSIC_EIP63			0xbf
+#define IMSIC_EIPx_BITS			32
+
+#define IMSIC_EIE0			0xc0
+#define IMSIC_EIE63			0xff
+#define IMSIC_EIEx_BITS			32
+
+#define IMSIC_FIRST			IMSIC_EIDELIVERY
+#define IMSIC_LAST			IMSIC_EIE63
+
+#define IMSIC_MMIO_SETIPNUM_LE		0x00
+#define IMSIC_MMIO_SETIPNUM_BE		0x04
+
+struct imsic_global_config {
+	/*
+	 * MSI Target Address Scheme
+	 *
+	 * XLEN-1                                                12     0
+	 * |                                                     |     |
+	 * -------------------------------------------------------------
+	 * |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
+	 * -------------------------------------------------------------
+	 */
+
+	/* Bits representing Guest index, HART index, and Group index */
+	u32 guest_index_bits;
+	u32 hart_index_bits;
+	u32 group_index_bits;
+	u32 group_index_shift;
+
+	/* Global base address matching all target MSI addresses */
+	phys_addr_t base_addr;
+
+	/* Number of interrupt identities */
+	u32 nr_ids;
+
+	/* Number of guest interrupt identities */
+	u32 nr_guest_ids;
+};
+
+struct imsic_local_config {
+	phys_addr_t msi_pa;
+	void __iomem *msi_va;
+};
+
+#ifdef CONFIG_RISCV_IMSIC
+
+extern const struct imsic_global_config *imsic_get_global_config(void);
+
+extern const struct imsic_local_config *imsic_get_local_config(
+							unsigned int cpu);
+
+#else
+
+static inline const struct imsic_global_config *imsic_get_global_config(void)
+{
+	return NULL;
+}
+
+static inline const struct imsic_local_config *imsic_get_local_config(
+							unsigned int cpu)
+{
+	return NULL;
+}
+
+#endif
+
+#endif
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* [PATCH 5/9] irqchip: Add RISC-V incoming MSI controller driver
@ 2022-11-11  4:42   ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-11  4:42 UTC (permalink / raw)
  To: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski
  Cc: Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree, Anup Patel

The RISC-V advanced interrupt architecture (AIA) specification defines
a new MSI controller for managing MSIs on a RISC-V platform. This new
MSI controller is referred to as incoming message signaled interrupt
controller (IMSIC) which manages MSI on per-HART (or per-CPU) basis.
(For more details refer https://github.com/riscv/riscv-aia)

This patch adds an irqchip driver for RISC-V IMSIC found on RISC-V
platforms.

Signed-off-by: Anup Patel <apatel@ventanamicro.com>
---
 drivers/irqchip/Kconfig             |   20 +-
 drivers/irqchip/Makefile            |    1 +
 drivers/irqchip/irq-riscv-imsic.c   | 1207 +++++++++++++++++++++++++++
 include/linux/irqchip/riscv-imsic.h |   92 ++
 4 files changed, 1319 insertions(+), 1 deletion(-)
 create mode 100644 drivers/irqchip/irq-riscv-imsic.c
 create mode 100644 include/linux/irqchip/riscv-imsic.h

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 7ef9f5e696d3..8246c08f0fd3 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -29,7 +29,6 @@ config ARM_GIC_V2M
 
 config GIC_NON_BANKED
 	bool
-
 config ARM_GIC_V3
 	bool
 	select IRQ_DOMAIN_HIERARCHY
@@ -564,6 +563,25 @@ config SIFIVE_PLIC
 
 	   If you don't know what to do here, say Y.
 
+config RISCV_IMSIC
+	bool "RISC-V Incoming MSI Controller"
+	depends on RISCV
+	select IRQ_DOMAIN_HIERARCHY
+	select GENERIC_MSI_IRQ_DOMAIN
+	help
+	   This enables support for the IMSIC chip found in RISC-V systems.
+	   The IMSIC controls message signaled interrupts and forwards them
+	   to each core as wired local interrupt.
+
+	   If you don't know what to do here, say Y.
+
+config RISCV_IMSIC_PCI
+	bool
+	depends on RISCV_IMSIC
+	depends on PCI
+	depends on PCI_MSI
+	default RISCV_IMSIC
+
 config EXYNOS_IRQ_COMBINER
 	bool "Samsung Exynos IRQ combiner support" if COMPILE_TEST
 	depends on (ARCH_EXYNOS && ARM) || COMPILE_TEST
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 87b49a10962c..22c723cc6ec8 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -96,6 +96,7 @@ obj-$(CONFIG_QCOM_MPM)			+= irq-qcom-mpm.o
 obj-$(CONFIG_CSKY_MPINTC)		+= irq-csky-mpintc.o
 obj-$(CONFIG_CSKY_APB_INTC)		+= irq-csky-apb-intc.o
 obj-$(CONFIG_RISCV_INTC)		+= irq-riscv-intc.o
+obj-$(CONFIG_RISCV_IMSIC)		+= irq-riscv-imsic.o
 obj-$(CONFIG_SIFIVE_PLIC)		+= irq-sifive-plic.o
 obj-$(CONFIG_IMX_IRQSTEER)		+= irq-imx-irqsteer.o
 obj-$(CONFIG_IMX_INTMUX)		+= irq-imx-intmux.o
diff --git a/drivers/irqchip/irq-riscv-imsic.c b/drivers/irqchip/irq-riscv-imsic.c
new file mode 100644
index 000000000000..95324fb4f5ed
--- /dev/null
+++ b/drivers/irqchip/irq-riscv-imsic.c
@@ -0,0 +1,1207 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Western Digital Corporation or its affiliates.
+ * Copyright (C) 2022 Ventana Micro Systems Inc.
+ */
+
+#define pr_fmt(fmt) "riscv-imsic: " fmt
+#include <linux/bitmap.h>
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iommu.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqchip/riscv-imsic.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/smp.h>
+#include <asm/hwcap.h>
+
+#define IMSIC_DISABLE_EIDELIVERY	0
+#define IMSIC_ENABLE_EIDELIVERY		1
+#define IMSIC_DISABLE_EITHRESHOLD	1
+#define IMSIC_ENABLE_EITHRESHOLD	0
+
+#define imsic_csr_write(__c, __v)	\
+do {					\
+	csr_write(CSR_ISELECT, __c);	\
+	csr_write(CSR_IREG, __v);	\
+} while (0)
+
+#define imsic_csr_read(__c)		\
+({					\
+	unsigned long __v;		\
+	csr_write(CSR_ISELECT, __c);	\
+	__v = csr_read(CSR_IREG);	\
+	__v;				\
+})
+
+#define imsic_csr_set(__c, __v)		\
+do {					\
+	csr_write(CSR_ISELECT, __c);	\
+	csr_set(CSR_IREG, __v);		\
+} while (0)
+
+#define imsic_csr_clear(__c, __v)	\
+do {					\
+	csr_write(CSR_ISELECT, __c);	\
+	csr_clear(CSR_IREG, __v);	\
+} while (0)
+
+struct imsic_mmio {
+	phys_addr_t pa;
+	void __iomem *va;
+	unsigned long size;
+};
+
+struct imsic_priv {
+	/* Global configuration common for all HARTs */
+	struct imsic_global_config global;
+
+	/* MMIO regions */
+	u32 num_mmios;
+	struct imsic_mmio *mmios;
+
+	/* Global state of interrupt identities */
+	raw_spinlock_t ids_lock;
+	unsigned long *ids_used_bimap;
+	unsigned long *ids_enabled_bimap;
+	unsigned int *ids_target_cpu;
+
+	/* Mask for connected CPUs */
+	struct cpumask lmask;
+
+	/* IPI interrupt identity */
+	bool slow_ipi;
+	u32 ipi_id;
+	u32 ipi_lsync_id;
+
+	/* IRQ domains */
+	struct irq_domain *base_domain;
+	struct irq_domain *pci_domain;
+	struct irq_domain *plat_domain;
+};
+
+struct imsic_handler {
+	/* Local configuration for given HART */
+	struct imsic_local_config local;
+
+	/* Pointer to private context */
+	struct imsic_priv *priv;
+};
+
+static bool imsic_init_done;
+
+static int imsic_parent_irq;
+static DEFINE_PER_CPU(struct imsic_handler, imsic_handlers);
+
+const struct imsic_global_config *imsic_get_global_config(void)
+{
+	struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
+
+	if (!handler || !handler->priv)
+		return NULL;
+
+	return &handler->priv->global;
+}
+EXPORT_SYMBOL_GPL(imsic_get_global_config);
+
+const struct imsic_local_config *imsic_get_local_config(unsigned int cpu)
+{
+	struct imsic_handler *handler = per_cpu_ptr(&imsic_handlers, cpu);
+
+	if (!handler || !handler->priv)
+		return NULL;
+
+	return &handler->local;
+}
+EXPORT_SYMBOL_GPL(imsic_get_local_config);
+
+static int imsic_cpu_page_phys(unsigned int cpu,
+			       unsigned int guest_index,
+			       phys_addr_t *out_msi_pa)
+{
+	struct imsic_handler *handler = per_cpu_ptr(&imsic_handlers, cpu);
+	struct imsic_global_config *global;
+	struct imsic_local_config *local;
+
+	if (!handler || !handler->priv)
+		return -ENODEV;
+	local = &handler->local;
+	global = &handler->priv->global;
+
+	if (BIT(global->guest_index_bits) <= guest_index)
+		return -EINVAL;
+
+	if (out_msi_pa)
+		*out_msi_pa = local->msi_pa +
+			      (guest_index * IMSIC_MMIO_PAGE_SZ);
+
+	return 0;
+}
+
+static int imsic_get_cpu(struct imsic_priv *priv,
+			 const struct cpumask *mask_val, bool force,
+			 unsigned int *out_target_cpu)
+{
+	struct cpumask amask;
+	unsigned int cpu;
+
+	cpumask_and(&amask, &priv->lmask, mask_val);
+
+	if (force)
+		cpu = cpumask_first(&amask);
+	else
+		cpu = cpumask_any_and(&amask, cpu_online_mask);
+
+	if (cpu >= nr_cpu_ids)
+		return -EINVAL;
+
+	if (out_target_cpu)
+		*out_target_cpu = cpu;
+
+	return 0;
+}
+
+static int imsic_get_cpu_msi_msg(unsigned int cpu, unsigned int id,
+				 struct msi_msg *msg)
+{
+	phys_addr_t msi_addr;
+	int err;
+
+	err = imsic_cpu_page_phys(cpu, 0, &msi_addr);
+	if (err)
+		return err;
+
+	msg->address_hi = upper_32_bits(msi_addr);
+	msg->address_lo = lower_32_bits(msi_addr);
+	msg->data = id;
+
+	return err;
+}
+
+static void imsic_id_set_target(struct imsic_priv *priv,
+				 unsigned int id, unsigned int target_cpu)
+{
+	raw_spin_lock(&priv->ids_lock);
+	priv->ids_target_cpu[id] = target_cpu;
+	raw_spin_unlock(&priv->ids_lock);
+}
+
+static unsigned int imsic_id_get_target(struct imsic_priv *priv,
+					unsigned int id)
+{
+	unsigned int ret;
+
+	raw_spin_lock(&priv->ids_lock);
+	ret = priv->ids_target_cpu[id];
+	raw_spin_unlock(&priv->ids_lock);
+
+	return ret;
+}
+
+static void __imsic_eix_update(unsigned long base_id,
+			       unsigned long num_id, bool pend, bool val)
+{
+	unsigned long i, isel, ireg, flags;
+	unsigned long id = base_id, last_id = base_id + num_id;
+
+	while (id < last_id) {
+		isel = id / BITS_PER_LONG;
+		isel *= BITS_PER_LONG / IMSIC_EIPx_BITS;
+		isel += (pend) ? IMSIC_EIP0 : IMSIC_EIE0;
+
+		ireg = 0;
+		for (i = id & (__riscv_xlen - 1);
+		     (id < last_id) && (i < __riscv_xlen); i++) {
+			ireg |= BIT(i);
+			id++;
+		}
+
+		/*
+		 * The IMSIC EIEx and EIPx registers are indirectly
+		 * accessed via using ISELECT and IREG CSRs so we
+		 * save/restore local IRQ to ensure that we don't
+		 * get preempted while accessing IMSIC registers.
+		 */
+		local_irq_save(flags);
+		if (val)
+			imsic_csr_set(isel, ireg);
+		else
+			imsic_csr_clear(isel, ireg);
+		local_irq_restore(flags);
+	}
+}
+
+#define __imsic_id_enable(__id)		\
+	__imsic_eix_update((__id), 1, false, true)
+#define __imsic_id_disable(__id)	\
+	__imsic_eix_update((__id), 1, false, false)
+
+#ifdef CONFIG_SMP
+static void __imsic_id_smp_sync(struct imsic_priv *priv)
+{
+	struct imsic_handler *handler;
+	struct cpumask amask;
+	int cpu;
+
+	cpumask_and(&amask, &priv->lmask, cpu_online_mask);
+	for_each_cpu(cpu, &amask) {
+		if (cpu == smp_processor_id())
+			continue;
+
+		handler = per_cpu_ptr(&imsic_handlers, cpu);
+		if (!handler || !handler->priv || !handler->local.msi_va) {
+			pr_warn("CPU%d: handler not initialized\n", cpu);
+			continue;
+		}
+
+		writel(handler->priv->ipi_lsync_id, handler->local.msi_va);
+	}
+}
+#else
+#define __imsic_id_smp_sync(__priv)
+#endif
+
+static void imsic_id_enable(struct imsic_priv *priv, unsigned int id)
+{
+	raw_spin_lock(&priv->ids_lock);
+	bitmap_set(priv->ids_enabled_bimap, id, 1);
+	__imsic_id_enable(id);
+	raw_spin_unlock(&priv->ids_lock);
+
+	__imsic_id_smp_sync(priv);
+}
+
+static void imsic_id_disable(struct imsic_priv *priv, unsigned int id)
+{
+	raw_spin_lock(&priv->ids_lock);
+	bitmap_clear(priv->ids_enabled_bimap, id, 1);
+	__imsic_id_disable(id);
+	raw_spin_unlock(&priv->ids_lock);
+
+	__imsic_id_smp_sync(priv);
+}
+
+static void imsic_ids_local_sync(struct imsic_priv *priv)
+{
+	int i;
+
+	raw_spin_lock(&priv->ids_lock);
+	for (i = 1; i <= priv->global.nr_ids; i++) {
+		if (priv->ipi_id == i || priv->ipi_lsync_id == i)
+			continue;
+
+		if (test_bit(i, priv->ids_enabled_bimap))
+			__imsic_id_enable(i);
+		else
+			__imsic_id_disable(i);
+	}
+	raw_spin_unlock(&priv->ids_lock);
+}
+
+static void imsic_ids_local_delivery(struct imsic_priv *priv, bool enable)
+{
+	if (enable) {
+		imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_ENABLE_EITHRESHOLD);
+		imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_ENABLE_EIDELIVERY);
+	} else {
+		imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_DISABLE_EIDELIVERY);
+		imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_DISABLE_EITHRESHOLD);
+	}
+}
+
+static int imsic_ids_alloc(struct imsic_priv *priv,
+			   unsigned int max_id, unsigned int order)
+{
+	int ret;
+
+	if ((priv->global.nr_ids < max_id) ||
+	    (max_id < BIT(order)))
+		return -EINVAL;
+
+	raw_spin_lock(&priv->ids_lock);
+	ret = bitmap_find_free_region(priv->ids_used_bimap,
+				      max_id + 1, order);
+	raw_spin_unlock(&priv->ids_lock);
+
+	return ret;
+}
+
+static void imsic_ids_free(struct imsic_priv *priv, unsigned int base_id,
+			   unsigned int order)
+{
+	raw_spin_lock(&priv->ids_lock);
+	bitmap_release_region(priv->ids_used_bimap, base_id, order);
+	raw_spin_unlock(&priv->ids_lock);
+}
+
+static int __init imsic_ids_init(struct imsic_priv *priv)
+{
+	int i;
+	struct imsic_global_config *global = &priv->global;
+
+	raw_spin_lock_init(&priv->ids_lock);
+
+	/* Allocate used bitmap */
+	priv->ids_used_bimap = kcalloc(BITS_TO_LONGS(global->nr_ids + 1),
+					sizeof(unsigned long), GFP_KERNEL);
+	if (!priv->ids_used_bimap)
+		return -ENOMEM;
+
+	/* Allocate enabled bitmap */
+	priv->ids_enabled_bimap = kcalloc(BITS_TO_LONGS(global->nr_ids + 1),
+					   sizeof(unsigned long), GFP_KERNEL);
+	if (!priv->ids_enabled_bimap) {
+		kfree(priv->ids_used_bimap);
+		return -ENOMEM;
+	}
+
+	/* Allocate target CPU array */
+	priv->ids_target_cpu = kcalloc(global->nr_ids + 1,
+				       sizeof(unsigned int), GFP_KERNEL);
+	if (!priv->ids_target_cpu) {
+		kfree(priv->ids_enabled_bimap);
+		kfree(priv->ids_used_bimap);
+		return -ENOMEM;
+	}
+	for (i = 0; i <= global->nr_ids; i++)
+		priv->ids_target_cpu[i] = UINT_MAX;
+
+	/* Reserve ID#0 because it is special and never implemented */
+	bitmap_set(priv->ids_used_bimap, 0, 1);
+
+	return 0;
+}
+
+static void __init imsic_ids_cleanup(struct imsic_priv *priv)
+{
+	kfree(priv->ids_target_cpu);
+	kfree(priv->ids_enabled_bimap);
+	kfree(priv->ids_used_bimap);
+}
+
+#ifdef CONFIG_SMP
+static void imsic_ipi_send_mask(unsigned int parent_virq, void *data,
+				const struct cpumask *mask)
+{
+	int cpu;
+	struct imsic_handler *handler;
+
+	for_each_cpu(cpu, mask) {
+		handler = per_cpu_ptr(&imsic_handlers, cpu);
+		if (!handler || !handler->priv || !handler->local.msi_va) {
+			pr_warn("CPU%d: handler not initialized\n", cpu);
+			continue;
+		}
+
+		writel(handler->priv->ipi_id, handler->local.msi_va);
+	}
+}
+
+static struct ipi_mux_ops imsic_ipi_ops = {
+	.ipi_mux_send = imsic_ipi_send_mask,
+};
+
+static void imsic_ipi_enable(struct imsic_priv *priv)
+{
+	__imsic_id_enable(priv->ipi_id);
+	__imsic_id_enable(priv->ipi_lsync_id);
+}
+
+static void imsic_ipi_disable(struct imsic_priv *priv)
+{
+	__imsic_id_disable(priv->ipi_lsync_id);
+	__imsic_id_disable(priv->ipi_id);
+}
+
+static int __init imsic_ipi_domain_init(struct imsic_priv *priv)
+{
+	int virq;
+
+	/* Skip IPI setup if IPIs are slow */
+	if (priv->slow_ipi)
+		goto skip_ipi;
+
+	/* Allocate interrupt identity for IPIs */
+	virq = imsic_ids_alloc(priv, priv->global.nr_ids, get_count_order(1));
+	if (virq < 0)
+		return virq;
+	priv->ipi_id = virq;
+
+	/* Create IMSIC IPI multiplexing */
+	virq = ipi_mux_create(0, BITS_PER_BYTE, &imsic_ipi_ops, NULL);
+	if (virq <= 0) {
+		imsic_ids_free(priv, priv->ipi_id, get_count_order(1));
+		return (virq < 0) ? virq : -ENOMEM;
+	}
+
+	/* Set vIRQ range */
+	riscv_ipi_set_virq_range(virq, BITS_PER_BYTE, true);
+
+skip_ipi:
+	/* Allocate interrupt identity for local enable/disable sync */
+	virq = imsic_ids_alloc(priv, priv->global.nr_ids, get_count_order(1));
+	if (virq < 0) {
+		imsic_ids_free(priv, priv->ipi_id, get_count_order(1));
+		return virq;
+	}
+	priv->ipi_lsync_id = virq;
+
+	return 0;
+}
+
+static void __init imsic_ipi_domain_cleanup(struct imsic_priv *priv)
+{
+	imsic_ids_free(priv, priv->ipi_lsync_id, get_count_order(1));
+	if (priv->ipi_id)
+		imsic_ids_free(priv, priv->ipi_id, get_count_order(1));
+}
+#else
+static void imsic_ipi_enable(struct imsic_priv *priv)
+{
+}
+
+static void imsic_ipi_disable(struct imsic_priv *priv)
+{
+}
+
+static int __init imsic_ipi_domain_init(struct imsic_priv *priv)
+{
+	/* Clear the IPI ids because we are not using IPIs */
+	priv->ipi_id = 0;
+	priv->ipi_lsync_id = 0;
+	return 0;
+}
+
+static void __init imsic_ipi_domain_cleanup(struct imsic_priv *priv)
+{
+}
+#endif
+
+static void imsic_irq_mask(struct irq_data *d)
+{
+	imsic_id_disable(irq_data_get_irq_chip_data(d), d->hwirq);
+}
+
+static void imsic_irq_unmask(struct irq_data *d)
+{
+	imsic_id_enable(irq_data_get_irq_chip_data(d), d->hwirq);
+}
+
+static void imsic_irq_compose_msi_msg(struct irq_data *d,
+				      struct msi_msg *msg)
+{
+	struct imsic_priv *priv = irq_data_get_irq_chip_data(d);
+	unsigned int cpu;
+	int err;
+
+	cpu = imsic_id_get_target(priv, d->hwirq);
+	WARN_ON(cpu == UINT_MAX);
+
+	err = imsic_get_cpu_msi_msg(cpu, d->hwirq, msg);
+	WARN_ON(err);
+
+	iommu_dma_compose_msi_msg(irq_data_get_msi_desc(d), msg);
+}
+
+#ifdef CONFIG_SMP
+static int imsic_irq_set_affinity(struct irq_data *d,
+				  const struct cpumask *mask_val,
+				  bool force)
+{
+	struct imsic_priv *priv = irq_data_get_irq_chip_data(d);
+	unsigned int target_cpu;
+	int rc;
+
+	rc = imsic_get_cpu(priv, mask_val, force, &target_cpu);
+	if (rc)
+		return rc;
+
+	imsic_id_set_target(priv, d->hwirq, target_cpu);
+	irq_data_update_effective_affinity(d, cpumask_of(target_cpu));
+
+	return IRQ_SET_MASK_OK;
+}
+#endif
+
+static struct irq_chip imsic_irq_base_chip = {
+	.name			= "RISC-V IMSIC-BASE",
+	.irq_mask		= imsic_irq_mask,
+	.irq_unmask		= imsic_irq_unmask,
+#ifdef CONFIG_SMP
+	.irq_set_affinity	= imsic_irq_set_affinity,
+#endif
+	.irq_compose_msi_msg	= imsic_irq_compose_msi_msg,
+	.flags			= IRQCHIP_SKIP_SET_WAKE |
+				  IRQCHIP_MASK_ON_SUSPEND,
+};
+
+static int imsic_irq_domain_alloc(struct irq_domain *domain,
+				  unsigned int virq,
+				  unsigned int nr_irqs,
+				  void *args)
+{
+	struct imsic_priv *priv = domain->host_data;
+	msi_alloc_info_t *info = args;
+	phys_addr_t msi_addr;
+	int i, hwirq, err = 0;
+	unsigned int cpu;
+
+	err = imsic_get_cpu(priv, &priv->lmask, false, &cpu);
+	if (err)
+		return err;
+
+	err = imsic_cpu_page_phys(cpu, 0, &msi_addr);
+	if (err)
+		return err;
+
+	hwirq = imsic_ids_alloc(priv, priv->global.nr_ids,
+				get_count_order(nr_irqs));
+	if (hwirq < 0)
+		return hwirq;
+
+	err = iommu_dma_prepare_msi(info->desc, msi_addr);
+	if (err)
+		goto fail;
+
+	for (i = 0; i < nr_irqs; i++) {
+		imsic_id_set_target(priv, hwirq + i, cpu);
+		irq_domain_set_info(domain, virq + i, hwirq + i,
+				    &imsic_irq_base_chip, priv,
+				    handle_simple_irq, NULL, NULL);
+		irq_set_noprobe(virq + i);
+		irq_set_affinity(virq + i, &priv->lmask);
+	}
+
+	return 0;
+
+fail:
+	imsic_ids_free(priv, hwirq, get_count_order(nr_irqs));
+	return err;
+}
+
+static void imsic_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 imsic_priv *priv = domain->host_data;
+
+	imsic_ids_free(priv, d->hwirq, get_count_order(nr_irqs));
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops imsic_base_domain_ops = {
+	.alloc		= imsic_irq_domain_alloc,
+	.free		= imsic_irq_domain_free,
+};
+
+#ifdef CONFIG_RISCV_IMSIC_PCI
+
+static void imsic_pci_mask_irq(struct irq_data *d)
+{
+	pci_msi_mask_irq(d);
+	irq_chip_mask_parent(d);
+}
+
+static void imsic_pci_unmask_irq(struct irq_data *d)
+{
+	pci_msi_unmask_irq(d);
+	irq_chip_unmask_parent(d);
+}
+
+static struct irq_chip imsic_pci_irq_chip = {
+	.name			= "RISC-V IMSIC-PCI",
+	.irq_mask		= imsic_pci_mask_irq,
+	.irq_unmask		= imsic_pci_unmask_irq,
+	.irq_eoi		= irq_chip_eoi_parent,
+};
+
+static struct msi_domain_ops imsic_pci_domain_ops = {
+};
+
+static struct msi_domain_info imsic_pci_domain_info = {
+	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+		   MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI),
+	.ops	= &imsic_pci_domain_ops,
+	.chip	= &imsic_pci_irq_chip,
+};
+
+#endif
+
+static struct irq_chip imsic_plat_irq_chip = {
+	.name			= "RISC-V IMSIC-PLAT",
+};
+
+static struct msi_domain_ops imsic_plat_domain_ops = {
+};
+
+static struct msi_domain_info imsic_plat_domain_info = {
+	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
+	.ops	= &imsic_plat_domain_ops,
+	.chip	= &imsic_plat_irq_chip,
+};
+
+static int __init imsic_irq_domains_init(struct imsic_priv *priv,
+					 struct fwnode_handle *fwnode)
+{
+	/* Create Base IRQ domain */
+	priv->base_domain = irq_domain_create_tree(fwnode,
+						&imsic_base_domain_ops, priv);
+	if (!priv->base_domain) {
+		pr_err("Failed to create IMSIC base domain\n");
+		return -ENOMEM;
+	}
+	irq_domain_update_bus_token(priv->base_domain, DOMAIN_BUS_NEXUS);
+
+#ifdef CONFIG_RISCV_IMSIC_PCI
+	/* Create PCI MSI domain */
+	priv->pci_domain = pci_msi_create_irq_domain(fwnode,
+						&imsic_pci_domain_info,
+						priv->base_domain);
+	if (!priv->pci_domain) {
+		pr_err("Failed to create IMSIC PCI domain\n");
+		irq_domain_remove(priv->base_domain);
+		return -ENOMEM;
+	}
+#endif
+
+	/* Create Platform MSI domain */
+	priv->plat_domain = platform_msi_create_irq_domain(fwnode,
+						&imsic_plat_domain_info,
+						priv->base_domain);
+	if (!priv->plat_domain) {
+		pr_err("Failed to create IMSIC platform domain\n");
+		if (priv->pci_domain)
+			irq_domain_remove(priv->pci_domain);
+		irq_domain_remove(priv->base_domain);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/*
+ * To handle an interrupt, we read the TOPEI CSR and write zero in one
+ * instruction. If TOPEI CSR is non-zero then we translate TOPEI.ID to
+ * Linux interrupt number and let Linux IRQ subsystem handle it.
+ */
+static void imsic_handle_irq(struct irq_desc *desc)
+{
+	struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct imsic_priv *priv = handler->priv;
+	irq_hw_number_t hwirq;
+	int err;
+
+	WARN_ON_ONCE(!handler->priv);
+
+	chained_irq_enter(chip, desc);
+
+	while ((hwirq = csr_swap(CSR_TOPEI, 0))) {
+		hwirq = hwirq >> TOPEI_ID_SHIFT;
+
+		if (hwirq == priv->ipi_id) {
+#ifdef CONFIG_SMP
+			ipi_mux_process();
+#endif
+			continue;
+		} else if (hwirq == priv->ipi_lsync_id) {
+			imsic_ids_local_sync(priv);
+			continue;
+		}
+
+		err = generic_handle_domain_irq(priv->base_domain, hwirq);
+		if (unlikely(err))
+			pr_warn_ratelimited(
+				"hwirq %lu mapping not found\n", hwirq);
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static int imsic_dying_cpu(unsigned int cpu)
+{
+	struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
+	struct imsic_priv *priv = handler->priv;
+
+	/* Disable per-CPU parent interrupt */
+	if (imsic_parent_irq)
+		disable_percpu_irq(imsic_parent_irq);
+
+	/* Locally disable interrupt delivery */
+	imsic_ids_local_delivery(priv, false);
+
+	/* Disable IPIs */
+	imsic_ipi_disable(priv);
+
+	return 0;
+}
+
+static int imsic_starting_cpu(unsigned int cpu)
+{
+	struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
+	struct imsic_priv *priv = handler->priv;
+
+	/* Enable per-CPU parent interrupt */
+	if (imsic_parent_irq)
+		enable_percpu_irq(imsic_parent_irq,
+				  irq_get_trigger_type(imsic_parent_irq));
+	else
+		pr_warn("cpu%d: parent irq not available\n", cpu);
+
+	/* Enable IPIs */
+	imsic_ipi_enable(priv);
+
+	/*
+	 * Interrupts identities might have been enabled/disabled while
+	 * this CPU was not running so sync-up local enable/disable state.
+	 */
+	imsic_ids_local_sync(priv);
+
+	/* Locally enable interrupt delivery */
+	imsic_ids_local_delivery(priv, true);
+
+	return 0;
+}
+
+struct imsic_fwnode_ops {
+	u32 (*nr_parent_irq)(struct fwnode_handle *fwnode,
+			     void *fwopaque);
+	int (*parent_hartid)(struct fwnode_handle *fwnode,
+			     void *fwopaque, u32 index,
+			     unsigned long *out_hartid);
+	u32 (*nr_mmio)(struct fwnode_handle *fwnode, void *fwopaque);
+	int (*mmio_to_resource)(struct fwnode_handle *fwnode,
+				void *fwopaque, u32 index,
+				struct resource *res);
+	void __iomem *(*mmio_map)(struct fwnode_handle *fwnode,
+				  void *fwopaque, u32 index);
+	int (*read_u32)(struct fwnode_handle *fwnode,
+			void *fwopaque, const char *prop, u32 *out_val);
+	bool (*read_bool)(struct fwnode_handle *fwnode,
+			  void *fwopaque, const char *prop);
+};
+
+static int __init imsic_init(struct imsic_fwnode_ops *fwops,
+			     struct fwnode_handle *fwnode,
+			     void *fwopaque)
+{
+	struct resource res;
+	phys_addr_t base_addr;
+	int rc, nr_parent_irqs;
+	struct imsic_mmio *mmio;
+	struct imsic_priv *priv;
+	struct irq_domain *domain;
+	struct imsic_handler *handler;
+	struct imsic_global_config *global;
+	u32 i, tmp, nr_handlers = 0;
+
+	if (imsic_init_done) {
+		pr_err("%pfwP: already initialized hence ignoring\n",
+			fwnode);
+		return -ENODEV;
+	}
+
+	if (!riscv_isa_extension_available(NULL, SxAIA)) {
+		pr_err("%pfwP: AIA support not available\n", fwnode);
+		return -ENODEV;
+	}
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	global = &priv->global;
+
+	/* Find number of parent interrupts */
+	nr_parent_irqs = fwops->nr_parent_irq(fwnode, fwopaque);
+	if (!nr_parent_irqs) {
+		pr_err("%pfwP: no parent irqs available\n", fwnode);
+		return -EINVAL;
+	}
+
+	/* Find number of guest index bits in MSI address */
+	rc = fwops->read_u32(fwnode, fwopaque, "riscv,guest-index-bits",
+			     &global->guest_index_bits);
+	if (rc)
+		global->guest_index_bits = 0;
+	tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT;
+	if (tmp < global->guest_index_bits) {
+		pr_err("%pfwP: guest index bits too big\n", fwnode);
+		return -EINVAL;
+	}
+
+	/* Find number of HART index bits */
+	rc = fwops->read_u32(fwnode, fwopaque, "riscv,hart-index-bits",
+			     &global->hart_index_bits);
+	if (rc) {
+		/* Assume default value */
+		global->hart_index_bits = __fls(nr_parent_irqs);
+		if (BIT(global->hart_index_bits) < nr_parent_irqs)
+			global->hart_index_bits++;
+	}
+	tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT -
+	      global->guest_index_bits;
+	if (tmp < global->hart_index_bits) {
+		pr_err("%pfwP: HART index bits too big\n", fwnode);
+		return -EINVAL;
+	}
+
+	/* Find number of group index bits */
+	rc = fwops->read_u32(fwnode, fwopaque, "riscv,group-index-bits",
+			     &global->group_index_bits);
+	if (rc)
+		global->group_index_bits = 0;
+	tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT -
+	      global->guest_index_bits - global->hart_index_bits;
+	if (tmp < global->group_index_bits) {
+		pr_err("%pfwP: group index bits too big\n", fwnode);
+		return -EINVAL;
+	}
+
+	/* Find first bit position of group index */
+	tmp = IMSIC_MMIO_PAGE_SHIFT * 2;
+	rc = fwops->read_u32(fwnode, fwopaque, "riscv,group-index-shift",
+			     &global->group_index_shift);
+	if (rc)
+		global->group_index_shift = tmp;
+	if (global->group_index_shift < tmp) {
+		pr_err("%pfwP: group index shift too small\n", fwnode);
+		return -EINVAL;
+	}
+	tmp = global->group_index_bits + global->group_index_shift - 1;
+	if (tmp >= BITS_PER_LONG) {
+		pr_err("%pfwP: group index shift too big\n", fwnode);
+		return -EINVAL;
+	}
+
+	/* Find number of interrupt identities */
+	rc = fwops->read_u32(fwnode, fwopaque, "riscv,num-ids",
+			     &global->nr_ids);
+	if (rc) {
+		pr_err("%pfwP: number of interrupt identities not found\n",
+			fwnode);
+		return rc;
+	}
+	if ((global->nr_ids < IMSIC_MIN_ID) ||
+	    (global->nr_ids >= IMSIC_MAX_ID) ||
+	    ((global->nr_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID)) {
+		pr_err("%pfwP: invalid number of interrupt identities\n",
+			fwnode);
+		return -EINVAL;
+	}
+
+	/* Find number of guest interrupt identities */
+	if (fwops->read_u32(fwnode, fwopaque, "riscv,num-guest-ids",
+			    &global->nr_guest_ids))
+		global->nr_guest_ids = global->nr_ids;
+	if ((global->nr_guest_ids < IMSIC_MIN_ID) ||
+	    (global->nr_guest_ids >= IMSIC_MAX_ID) ||
+	    ((global->nr_guest_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID)) {
+		pr_err("%pfwP: invalid number of guest interrupt identities\n",
+			fwnode);
+		return -EINVAL;
+	}
+
+	/* Check if IPIs are slow */
+	priv->slow_ipi = fwops->read_bool(fwnode, fwopaque, "riscv,slow-ipi");
+
+	/* Compute base address */
+	rc = fwops->mmio_to_resource(fwnode, fwopaque, 0, &res);
+	if (rc) {
+		pr_err("%pfwP: first MMIO resource not found\n", fwnode);
+		return -EINVAL;
+	}
+	global->base_addr = res.start;
+	global->base_addr &= ~(BIT(global->guest_index_bits +
+				   global->hart_index_bits +
+				   IMSIC_MMIO_PAGE_SHIFT) - 1);
+	global->base_addr &= ~((BIT(global->group_index_bits) - 1) <<
+			       global->group_index_shift);
+
+	/* Find number of MMIO register sets */
+	priv->num_mmios = fwops->nr_mmio(fwnode, fwopaque);
+
+	/* Allocate MMIO register sets */
+	priv->mmios = kcalloc(priv->num_mmios, sizeof(*mmio), GFP_KERNEL);
+	if (!priv->mmios) {
+		rc = -ENOMEM;
+		goto out_free_priv;
+	}
+
+	/* Parse and map MMIO register sets */
+	for (i = 0; i < priv->num_mmios; i++) {
+		mmio = &priv->mmios[i];
+		rc = fwops->mmio_to_resource(fwnode, fwopaque, i, &res);
+		if (rc) {
+			pr_err("%pfwP: unable to parse MMIO regset %d\n",
+				fwnode, i);
+			goto out_iounmap;
+		}
+		mmio->pa = res.start;
+		mmio->size = res.end - res.start + 1;
+
+		base_addr = mmio->pa;
+		base_addr &= ~(BIT(global->guest_index_bits +
+				   global->hart_index_bits +
+				   IMSIC_MMIO_PAGE_SHIFT) - 1);
+		base_addr &= ~((BIT(global->group_index_bits) - 1) <<
+			       global->group_index_shift);
+		if (base_addr != global->base_addr) {
+			rc = -EINVAL;
+			pr_err("%pfwP: address mismatch for regset %d\n",
+				fwnode, i);
+			goto out_iounmap;
+		}
+
+		tmp = BIT(global->guest_index_bits) - 1;
+		if ((mmio->size / IMSIC_MMIO_PAGE_SZ) & tmp) {
+			rc = -EINVAL;
+			pr_err("%pfwP: size mismatch for regset %d\n",
+				fwnode, i);
+			goto out_iounmap;
+		}
+
+		mmio->va = fwops->mmio_map(fwnode, fwopaque, i);
+		if (!mmio->va) {
+			rc = -EIO;
+			pr_err("%pfwP: unable to map MMIO regset %d\n",
+				fwnode, i);
+			goto out_iounmap;
+		}
+	}
+
+	/* Initialize interrupt identity management */
+	rc = imsic_ids_init(priv);
+	if (rc) {
+		pr_err("%pfwP: failed to initialize interrupt management\n",
+		       fwnode);
+		goto out_iounmap;
+	}
+
+	/* Configure handlers for target CPUs */
+	for (i = 0; i < nr_parent_irqs; i++) {
+		unsigned long reloff, hartid;
+		int j, cpu;
+
+		rc = fwops->parent_hartid(fwnode, fwopaque, i, &hartid);
+		if (rc) {
+			pr_warn("%pfwP: hart ID for parent irq%d not found\n",
+				fwnode, i);
+			continue;
+		}
+
+		cpu = riscv_hartid_to_cpuid(hartid);
+		if (cpu < 0) {
+			pr_warn("%pfwP: invalid cpuid for parent irq%d\n",
+				fwnode, i);
+			continue;
+		}
+
+		/* Find MMIO location of MSI page */
+		mmio = NULL;
+		reloff = i * BIT(global->guest_index_bits) *
+			 IMSIC_MMIO_PAGE_SZ;
+		for (j = 0; priv->num_mmios; j++) {
+			if (reloff < priv->mmios[j].size) {
+				mmio = &priv->mmios[j];
+				break;
+			}
+
+			reloff -= priv->mmios[j].size;
+		}
+		if (!mmio) {
+			pr_warn("%pfwP: MMIO not found for parent irq%d\n",
+				fwnode, i);
+			continue;
+		}
+
+		handler = per_cpu_ptr(&imsic_handlers, cpu);
+		if (handler->priv) {
+			pr_warn("%pfwP: CPU%d handler already configured.\n",
+				fwnode, cpu);
+			goto done;
+		}
+
+		cpumask_set_cpu(cpu, &priv->lmask);
+		handler->local.msi_pa = mmio->pa + reloff;
+		handler->local.msi_va = mmio->va + reloff;
+		handler->priv = priv;
+
+done:
+		nr_handlers++;
+	}
+
+	/* If no CPU handlers found then can't take interrupts */
+	if (!nr_handlers) {
+		pr_err("%pfwP: No CPU handlers found\n", fwnode);
+		rc = -ENODEV;
+		goto out_ids_cleanup;
+	}
+
+	/* Find parent domain and register chained handler */
+	domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(),
+					  DOMAIN_BUS_ANY);
+	if (!domain) {
+		pr_err("%pfwP: Failed to find INTC domain\n", fwnode);
+		rc = -ENOENT;
+		goto out_ids_cleanup;
+	}
+	imsic_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT);
+	if (!imsic_parent_irq) {
+		pr_err("%pfwP: Failed to create INTC mapping\n", fwnode);
+		rc = -ENOENT;
+		goto out_ids_cleanup;
+	}
+	irq_set_chained_handler(imsic_parent_irq, imsic_handle_irq);
+
+	/* Initialize IPI domain */
+	rc = imsic_ipi_domain_init(priv);
+	if (rc) {
+		pr_err("%pfwP: Failed to initialize IPI domain\n", fwnode);
+		goto out_ids_cleanup;
+	}
+
+	/* Initialize IRQ and MSI domains */
+	rc = imsic_irq_domains_init(priv, fwnode);
+	if (rc) {
+		pr_err("%pfwP: Failed to initialize IRQ and MSI domains\n",
+		       fwnode);
+		goto out_ipi_domain_cleanup;
+	}
+
+	/* Setup cpuhp state */
+	cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
+			  "irqchip/riscv/imsic:starting",
+			  imsic_starting_cpu, imsic_dying_cpu);
+
+	/*
+	 * Only one IMSIC instance allowed in a platform for clean
+	 * implementation of SMP IRQ affinity and per-CPU IPIs.
+	 *
+	 * This means on a multi-socket (or multi-die) platform we
+	 * will have multiple MMIO regions for one IMSIC instance.
+	 */
+	imsic_init_done = true;
+
+	pr_info("%pfwP:  hart-index-bits: %d,  guest-index-bits: %d\n",
+		fwnode, global->hart_index_bits, global->guest_index_bits);
+	pr_info("%pfwP: group-index-bits: %d, group-index-shift: %d\n",
+		fwnode, global->group_index_bits, global->group_index_shift);
+	pr_info("%pfwP: mapped %d interrupts for %d CPUs at %pa\n",
+		fwnode, global->nr_ids, nr_handlers, &global->base_addr);
+	if (priv->ipi_lsync_id)
+		pr_info("%pfwP: enable/disable sync using interrupt %d\n",
+			fwnode, priv->ipi_lsync_id);
+	if (priv->ipi_id)
+		pr_info("%pfwP: providing IPIs using interrupt %d\n",
+			fwnode, priv->ipi_id);
+
+	return 0;
+
+out_ipi_domain_cleanup:
+	imsic_ipi_domain_cleanup(priv);
+out_ids_cleanup:
+	imsic_ids_cleanup(priv);
+out_iounmap:
+	for (i = 0; i < priv->num_mmios; i++) {
+		if (priv->mmios[i].va)
+			iounmap(priv->mmios[i].va);
+	}
+	kfree(priv->mmios);
+out_free_priv:
+	kfree(priv);
+	return rc;
+}
+
+static u32 __init imsic_dt_nr_parent_irq(struct fwnode_handle *fwnode,
+					 void *fwopaque)
+{
+	return of_irq_count(to_of_node(fwnode));
+}
+
+static int __init imsic_dt_parent_hartid(struct fwnode_handle *fwnode,
+					 void *fwopaque, u32 index,
+					 unsigned long *out_hartid)
+{
+	struct of_phandle_args parent;
+	int rc;
+
+	rc = of_irq_parse_one(to_of_node(fwnode), index, &parent);
+	if (rc)
+		return rc;
+
+	/*
+	 * Skip interrupts other than external interrupts for
+	 * current privilege level.
+	 */
+	if (parent.args[0] != RV_IRQ_EXT)
+		return -EINVAL;
+
+	return riscv_of_parent_hartid(parent.np, out_hartid);
+}
+
+static u32 __init imsic_dt_nr_mmio(struct fwnode_handle *fwnode,
+				   void *fwopaque)
+{
+	u32 ret = 0;
+	struct resource res;
+
+	while (!of_address_to_resource(to_of_node(fwnode), ret, &res))
+		ret++;
+
+	return ret;
+}
+
+static int __init imsic_mmio_to_resource(struct fwnode_handle *fwnode,
+					 void *fwopaque, u32 index,
+					 struct resource *res)
+{
+	return of_address_to_resource(to_of_node(fwnode), index, res);
+}
+
+static void __iomem __init *imsic_dt_mmio_map(struct fwnode_handle *fwnode,
+					      void *fwopaque, u32 index)
+{
+	return of_iomap(to_of_node(fwnode), index);
+}
+
+static int __init imsic_dt_read_u32(struct fwnode_handle *fwnode,
+				    void *fwopaque, const char *prop,
+				    u32 *out_val)
+{
+	return of_property_read_u32(to_of_node(fwnode), prop, out_val);
+}
+
+static bool __init imsic_dt_read_bool(struct fwnode_handle *fwnode,
+				      void *fwopaque, const char *prop)
+{
+	return of_property_read_bool(to_of_node(fwnode), prop);
+}
+
+static int __init imsic_dt_init(struct device_node *node,
+				struct device_node *parent)
+{
+	struct imsic_fwnode_ops ops = {
+		.nr_parent_irq = imsic_dt_nr_parent_irq,
+		.parent_hartid = imsic_dt_parent_hartid,
+		.nr_mmio = imsic_dt_nr_mmio,
+		.mmio_to_resource = imsic_mmio_to_resource,
+		.mmio_map = imsic_dt_mmio_map,
+		.read_u32 = imsic_dt_read_u32,
+		.read_bool = imsic_dt_read_bool,
+	};
+
+	return imsic_init(&ops, &node->fwnode, NULL);
+}
+IRQCHIP_DECLARE(riscv_imsic, "riscv,imsics", imsic_dt_init);
diff --git a/include/linux/irqchip/riscv-imsic.h b/include/linux/irqchip/riscv-imsic.h
new file mode 100644
index 000000000000..5d1387adc0ba
--- /dev/null
+++ b/include/linux/irqchip/riscv-imsic.h
@@ -0,0 +1,92 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2021 Western Digital Corporation or its affiliates.
+ * Copyright (C) 2022 Ventana Micro Systems Inc.
+ */
+#ifndef __LINUX_IRQCHIP_RISCV_IMSIC_H
+#define __LINUX_IRQCHIP_RISCV_IMSIC_H
+
+#include <linux/types.h>
+#include <asm/csr.h>
+
+#define IMSIC_MMIO_PAGE_SHIFT		12
+#define IMSIC_MMIO_PAGE_SZ		(1UL << IMSIC_MMIO_PAGE_SHIFT)
+#define IMSIC_MMIO_PAGE_LE		0x00
+#define IMSIC_MMIO_PAGE_BE		0x04
+
+#define IMSIC_MIN_ID			63
+#define IMSIC_MAX_ID			2048
+
+#define IMSIC_EIDELIVERY		0x70
+
+#define IMSIC_EITHRESHOLD		0x72
+
+#define IMSIC_EIP0			0x80
+#define IMSIC_EIP63			0xbf
+#define IMSIC_EIPx_BITS			32
+
+#define IMSIC_EIE0			0xc0
+#define IMSIC_EIE63			0xff
+#define IMSIC_EIEx_BITS			32
+
+#define IMSIC_FIRST			IMSIC_EIDELIVERY
+#define IMSIC_LAST			IMSIC_EIE63
+
+#define IMSIC_MMIO_SETIPNUM_LE		0x00
+#define IMSIC_MMIO_SETIPNUM_BE		0x04
+
+struct imsic_global_config {
+	/*
+	 * MSI Target Address Scheme
+	 *
+	 * XLEN-1                                                12     0
+	 * |                                                     |     |
+	 * -------------------------------------------------------------
+	 * |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
+	 * -------------------------------------------------------------
+	 */
+
+	/* Bits representing Guest index, HART index, and Group index */
+	u32 guest_index_bits;
+	u32 hart_index_bits;
+	u32 group_index_bits;
+	u32 group_index_shift;
+
+	/* Global base address matching all target MSI addresses */
+	phys_addr_t base_addr;
+
+	/* Number of interrupt identities */
+	u32 nr_ids;
+
+	/* Number of guest interrupt identities */
+	u32 nr_guest_ids;
+};
+
+struct imsic_local_config {
+	phys_addr_t msi_pa;
+	void __iomem *msi_va;
+};
+
+#ifdef CONFIG_RISCV_IMSIC
+
+extern const struct imsic_global_config *imsic_get_global_config(void);
+
+extern const struct imsic_local_config *imsic_get_local_config(
+							unsigned int cpu);
+
+#else
+
+static inline const struct imsic_global_config *imsic_get_global_config(void)
+{
+	return NULL;
+}
+
+static inline const struct imsic_local_config *imsic_get_local_config(
+							unsigned int cpu)
+{
+	return NULL;
+}
+
+#endif
+
+#endif
-- 
2.34.1


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

* [PATCH 6/9] dt-bindings: Add RISC-V advanced PLIC bindings
  2022-11-11  4:41 ` Anup Patel
@ 2022-11-11  4:42   ` Anup Patel
  -1 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-11  4:42 UTC (permalink / raw)
  To: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski
  Cc: Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree, Anup Patel

We add DT bindings document for RISC-V advanced platform level interrupt
controller (APLIC) defined by the RISC-V advanced interrupt architecture
(AIA) specification.

Signed-off-by: Anup Patel <apatel@ventanamicro.com>
---
 .../interrupt-controller/riscv,aplic.yaml     | 136 ++++++++++++++++++
 1 file changed, 136 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml

diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
new file mode 100644
index 000000000000..0aa48571f3bc
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
@@ -0,0 +1,136 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/interrupt-controller/riscv,aplic.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: RISC-V Advancded Platform Level Interrupt Controller (APLIC)
+
+maintainers:
+  - Anup Patel <anup@brainfault.org>
+
+description:
+  The RISC-V advanced interrupt architecture (AIA) defines advanced platform
+  level interrupt controller (APLIC) for handling wired interrupts in a
+  RISC-V platform. The RISC-V AIA specification can be found at
+  https://github.com/riscv/riscv-aia.
+
+  The RISC-V APLIC is implemented as hierarchical APLIC domains where all
+  interrupt sources connect to the root domain which can further delegate
+  interrupts to child domains. We have one device tree node for each APLIC
+  domain.
+
+allOf:
+  - $ref: /schemas/interrupt-controller.yaml#
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - vendor,chip-aplic
+      - const: riscv,aplic
+
+  reg:
+    maxItems: 1
+
+  interrupt-controller: true
+
+  "#interrupt-cells":
+    const: 2
+
+  interrupts-extended:
+    minItems: 1
+    maxItems: 16384
+    description:
+      The presence of this property implies that given APLIC domain directly
+      injects external interrupts to a set of RISC-V HARTS (or CPUs). Each
+      node pointed to should be a riscv,cpu-intc node, which has a riscv node
+      (i.e. RISC-V HART) as parent.
+
+  msi-parent:
+    description:
+      The presence of this property implies that given APLIC domain forwards
+      wired interrupts as MSIs to a AIA incoming message signaled interrupt
+      controller (IMSIC). This property should be considered only when the
+      interrupts-extended property is absent.
+
+  riscv,num-sources:
+    $ref: "/schemas/types.yaml#/definitions/uint32"
+    minimum: 1
+    maximum: 1023
+    description:
+      Specifies how many wired interrupts are supported by this APLIC domain.
+
+  riscv,children:
+    $ref: '/schemas/types.yaml#/definitions/phandle-array'
+    minItems: 1
+    maxItems: 1024
+    description:
+      This property represents a list of child APLIC domains for the given
+      APLIC domain. Each child APLIC domain is assigned child index in
+      increasing order with the first child APLIC domain assigned child
+      index 0. The APLIC domain child index is used by firmware to delegate
+      interrupts from the given APLIC domain to a particular child APLIC
+      domain.
+
+  riscv,delegate:
+    $ref: '/schemas/types.yaml#/definitions/phandle-array'
+    minItems: 1
+    maxItems: 1024
+    description:
+      This property represents a interrupt delegation list where each entry
+      is a triple consisting of child APLIC domain phandle, first interrupt
+      number, and last interrupt number. The firmware will configure interrupt
+      delegation registers based on interrupt delegation list.
+
+additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - interrupt-controller
+  - "#interrupt-cells"
+  - riscv,num-sources
+
+examples:
+  - |
+    // Example 1 (APIC domain directly injecting interrupt to HARTs):
+
+    aplic0: interrupt-controller@c000000 {
+      compatible = "vendor,chip-aplic", "riscv,aplic";
+      interrupts-extended = <&cpu1_intc 11>,
+                            <&cpu2_intc 11>,
+                            <&cpu3_intc 11>,
+                            <&cpu4_intc 11>;
+      reg = <0xc000000 0x4080>;
+      interrupt-controller;
+      #interrupt-cells = <2>;
+      riscv,num-sources = <63>;
+      riscv,children = <&aplic1>;
+      riscv,delegate = <&aplic1 1 63>;
+    };
+
+    aplic1: interrupt-controller@d000000 {
+      compatible = "vendor,chip-aplic", "riscv,aplic";
+      interrupts-extended = <&cpu1_intc 9>,
+                            <&cpu2_intc 9>,
+                            <&cpu3_intc 9>,
+                            <&cpu4_intc 9>;
+      reg = <0xd000000 0x4080>;
+      interrupt-controller;
+      #interrupt-cells = <2>;
+      riscv,num-sources = <63>;
+    };
+
+  - |
+    // Example 2 (APIC domain forwarding interrupts as MSIs):
+
+    interrupt-controller@d000000 {
+      compatible = "vendor,chip-aplic", "riscv,aplic";
+      msi-parent = <&imsics>;
+      reg = <0xd000000 0x4000>;
+      interrupt-controller;
+      #interrupt-cells = <2>;
+      riscv,num-sources = <63>;
+    };
+...
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* [PATCH 6/9] dt-bindings: Add RISC-V advanced PLIC bindings
@ 2022-11-11  4:42   ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-11  4:42 UTC (permalink / raw)
  To: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski
  Cc: Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree, Anup Patel

We add DT bindings document for RISC-V advanced platform level interrupt
controller (APLIC) defined by the RISC-V advanced interrupt architecture
(AIA) specification.

Signed-off-by: Anup Patel <apatel@ventanamicro.com>
---
 .../interrupt-controller/riscv,aplic.yaml     | 136 ++++++++++++++++++
 1 file changed, 136 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml

diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
new file mode 100644
index 000000000000..0aa48571f3bc
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
@@ -0,0 +1,136 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/interrupt-controller/riscv,aplic.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: RISC-V Advancded Platform Level Interrupt Controller (APLIC)
+
+maintainers:
+  - Anup Patel <anup@brainfault.org>
+
+description:
+  The RISC-V advanced interrupt architecture (AIA) defines advanced platform
+  level interrupt controller (APLIC) for handling wired interrupts in a
+  RISC-V platform. The RISC-V AIA specification can be found at
+  https://github.com/riscv/riscv-aia.
+
+  The RISC-V APLIC is implemented as hierarchical APLIC domains where all
+  interrupt sources connect to the root domain which can further delegate
+  interrupts to child domains. We have one device tree node for each APLIC
+  domain.
+
+allOf:
+  - $ref: /schemas/interrupt-controller.yaml#
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - vendor,chip-aplic
+      - const: riscv,aplic
+
+  reg:
+    maxItems: 1
+
+  interrupt-controller: true
+
+  "#interrupt-cells":
+    const: 2
+
+  interrupts-extended:
+    minItems: 1
+    maxItems: 16384
+    description:
+      The presence of this property implies that given APLIC domain directly
+      injects external interrupts to a set of RISC-V HARTS (or CPUs). Each
+      node pointed to should be a riscv,cpu-intc node, which has a riscv node
+      (i.e. RISC-V HART) as parent.
+
+  msi-parent:
+    description:
+      The presence of this property implies that given APLIC domain forwards
+      wired interrupts as MSIs to a AIA incoming message signaled interrupt
+      controller (IMSIC). This property should be considered only when the
+      interrupts-extended property is absent.
+
+  riscv,num-sources:
+    $ref: "/schemas/types.yaml#/definitions/uint32"
+    minimum: 1
+    maximum: 1023
+    description:
+      Specifies how many wired interrupts are supported by this APLIC domain.
+
+  riscv,children:
+    $ref: '/schemas/types.yaml#/definitions/phandle-array'
+    minItems: 1
+    maxItems: 1024
+    description:
+      This property represents a list of child APLIC domains for the given
+      APLIC domain. Each child APLIC domain is assigned child index in
+      increasing order with the first child APLIC domain assigned child
+      index 0. The APLIC domain child index is used by firmware to delegate
+      interrupts from the given APLIC domain to a particular child APLIC
+      domain.
+
+  riscv,delegate:
+    $ref: '/schemas/types.yaml#/definitions/phandle-array'
+    minItems: 1
+    maxItems: 1024
+    description:
+      This property represents a interrupt delegation list where each entry
+      is a triple consisting of child APLIC domain phandle, first interrupt
+      number, and last interrupt number. The firmware will configure interrupt
+      delegation registers based on interrupt delegation list.
+
+additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - interrupt-controller
+  - "#interrupt-cells"
+  - riscv,num-sources
+
+examples:
+  - |
+    // Example 1 (APIC domain directly injecting interrupt to HARTs):
+
+    aplic0: interrupt-controller@c000000 {
+      compatible = "vendor,chip-aplic", "riscv,aplic";
+      interrupts-extended = <&cpu1_intc 11>,
+                            <&cpu2_intc 11>,
+                            <&cpu3_intc 11>,
+                            <&cpu4_intc 11>;
+      reg = <0xc000000 0x4080>;
+      interrupt-controller;
+      #interrupt-cells = <2>;
+      riscv,num-sources = <63>;
+      riscv,children = <&aplic1>;
+      riscv,delegate = <&aplic1 1 63>;
+    };
+
+    aplic1: interrupt-controller@d000000 {
+      compatible = "vendor,chip-aplic", "riscv,aplic";
+      interrupts-extended = <&cpu1_intc 9>,
+                            <&cpu2_intc 9>,
+                            <&cpu3_intc 9>,
+                            <&cpu4_intc 9>;
+      reg = <0xd000000 0x4080>;
+      interrupt-controller;
+      #interrupt-cells = <2>;
+      riscv,num-sources = <63>;
+    };
+
+  - |
+    // Example 2 (APIC domain forwarding interrupts as MSIs):
+
+    interrupt-controller@d000000 {
+      compatible = "vendor,chip-aplic", "riscv,aplic";
+      msi-parent = <&imsics>;
+      reg = <0xd000000 0x4000>;
+      interrupt-controller;
+      #interrupt-cells = <2>;
+      riscv,num-sources = <63>;
+    };
+...
-- 
2.34.1


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

* [PATCH 7/9] irqchip: Add RISC-V advanced PLIC driver
  2022-11-11  4:41 ` Anup Patel
@ 2022-11-11  4:42   ` Anup Patel
  -1 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-11  4:42 UTC (permalink / raw)
  To: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski
  Cc: Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree, Anup Patel

The RISC-V advanced interrupt architecture (AIA) specification defines
a new interrupt controller for managing wired interrupts on a RISC-V
platform. This new interrupt controller is referred to as advanced
platform-level interrupt controller (APLIC) which can forward wired
interrupts to CPUs (or HARTs) as local interrupts OR as message
signaled interrupts.
(For more details refer https://github.com/riscv/riscv-aia)

This patch adds an irqchip driver for RISC-V APLIC found on RISC-V
platforms.

Signed-off-by: Anup Patel <apatel@ventanamicro.com>
---
 drivers/irqchip/Kconfig             |  12 +
 drivers/irqchip/Makefile            |   1 +
 drivers/irqchip/irq-riscv-aplic.c   | 656 ++++++++++++++++++++++++++++
 include/linux/irqchip/riscv-aplic.h | 117 +++++
 4 files changed, 786 insertions(+)
 create mode 100644 drivers/irqchip/irq-riscv-aplic.c
 create mode 100644 include/linux/irqchip/riscv-aplic.h

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 8246c08f0fd3..9f022e71d937 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -563,6 +563,18 @@ config SIFIVE_PLIC
 
 	   If you don't know what to do here, say Y.
 
+config RISCV_APLIC
+	bool "RISC-V Advanced Platform-Level Interrupt Controller"
+	depends on RISCV
+	select IRQ_DOMAIN_HIERARCHY
+	select GENERIC_MSI_IRQ_DOMAIN
+	help
+	   This enables support for the APLIC chip found in RISC-V systems.
+	   The APLIC controls device wired interrupts and forwards them to
+	   each core as wired local interrupt or per-core MSIs.
+
+	   If you don't know what to do here, say Y.
+
 config RISCV_IMSIC
 	bool "RISC-V Incoming MSI Controller"
 	depends on RISCV
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 22c723cc6ec8..6154e5bc4228 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -96,6 +96,7 @@ obj-$(CONFIG_QCOM_MPM)			+= irq-qcom-mpm.o
 obj-$(CONFIG_CSKY_MPINTC)		+= irq-csky-mpintc.o
 obj-$(CONFIG_CSKY_APB_INTC)		+= irq-csky-apb-intc.o
 obj-$(CONFIG_RISCV_INTC)		+= irq-riscv-intc.o
+obj-$(CONFIG_RISCV_APLIC)		+= irq-riscv-aplic.o
 obj-$(CONFIG_RISCV_IMSIC)		+= irq-riscv-imsic.o
 obj-$(CONFIG_SIFIVE_PLIC)		+= irq-sifive-plic.o
 obj-$(CONFIG_IMX_IRQSTEER)		+= irq-imx-irqsteer.o
diff --git a/drivers/irqchip/irq-riscv-aplic.c b/drivers/irqchip/irq-riscv-aplic.c
new file mode 100644
index 000000000000..e6d0e1bb15a8
--- /dev/null
+++ b/drivers/irqchip/irq-riscv-aplic.c
@@ -0,0 +1,656 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Western Digital Corporation or its affiliates.
+ * Copyright (C) 2022 Ventana Micro Systems Inc.
+ */
+
+#include <linux/bitops.h>
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqchip/riscv-aplic.h>
+#include <linux/irqchip/riscv-imsic.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/smp.h>
+
+#define APLIC_DEFAULT_PRIORITY		1
+#define APLIC_DISABLE_IDELIVERY		0
+#define APLIC_ENABLE_IDELIVERY		1
+#define APLIC_DISABLE_ITHRESHOLD	1
+#define APLIC_ENABLE_ITHRESHOLD		0
+
+struct aplic_msicfg {
+	phys_addr_t		base_ppn;
+	u32			hhxs;
+	u32			hhxw;
+	u32			lhxs;
+	u32			lhxw;
+};
+
+struct aplic_idc {
+	unsigned int		hart_index;
+	void __iomem		*regs;
+	struct aplic_priv	*priv;
+};
+
+struct aplic_priv {
+	struct device		*dev;
+	u32			nr_irqs;
+	u32			nr_idcs;
+	void __iomem		*regs;
+	struct irq_domain	*irqdomain;
+	struct aplic_msicfg	msicfg;
+	struct cpumask		lmask;
+};
+
+static unsigned int aplic_idc_parent_irq;
+static DEFINE_PER_CPU(struct aplic_idc, aplic_idcs);
+
+static void aplic_irq_unmask(struct irq_data *d)
+{
+	struct aplic_priv *priv = irq_data_get_irq_chip_data(d);
+
+	writel(d->hwirq, priv->regs + APLIC_SETIENUM);
+
+	if (!priv->nr_idcs)
+		irq_chip_unmask_parent(d);
+}
+
+static void aplic_irq_mask(struct irq_data *d)
+{
+	struct aplic_priv *priv = irq_data_get_irq_chip_data(d);
+
+	writel(d->hwirq, priv->regs + APLIC_CLRIENUM);
+
+	if (!priv->nr_idcs)
+		irq_chip_mask_parent(d);
+}
+
+static int aplic_set_type(struct irq_data *d, unsigned int type)
+{
+	u32 val = 0;
+	void __iomem *sourcecfg;
+	struct aplic_priv *priv = irq_data_get_irq_chip_data(d);
+
+	switch (type) {
+	case IRQ_TYPE_NONE:
+		val = APLIC_SOURCECFG_SM_INACTIVE;
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		val = APLIC_SOURCECFG_SM_LEVEL_LOW;
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		val = APLIC_SOURCECFG_SM_LEVEL_HIGH;
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		val = APLIC_SOURCECFG_SM_EDGE_FALL;
+		break;
+	case IRQ_TYPE_EDGE_RISING:
+		val = APLIC_SOURCECFG_SM_EDGE_RISE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	sourcecfg = priv->regs + APLIC_SOURCECFG_BASE;
+	sourcecfg += (d->hwirq - 1) * sizeof(u32);
+	writel(val, sourcecfg);
+
+	return 0;
+}
+
+#ifdef CONFIG_SMP
+static int aplic_set_affinity(struct irq_data *d,
+			      const struct cpumask *mask_val, bool force)
+{
+	struct aplic_priv *priv = irq_data_get_irq_chip_data(d);
+	struct aplic_idc *idc;
+	unsigned int cpu, val;
+	struct cpumask amask;
+	void __iomem *target;
+
+	if (!priv->nr_idcs)
+		return irq_chip_set_affinity_parent(d, mask_val, force);
+
+	cpumask_and(&amask, &priv->lmask, mask_val);
+
+	if (force)
+		cpu = cpumask_first(&amask);
+	else
+		cpu = cpumask_any_and(&amask, cpu_online_mask);
+
+	if (cpu >= nr_cpu_ids)
+		return -EINVAL;
+
+	idc = per_cpu_ptr(&aplic_idcs, cpu);
+	target = priv->regs + APLIC_TARGET_BASE;
+	target += (d->hwirq - 1) * sizeof(u32);
+	val = idc->hart_index & APLIC_TARGET_HART_IDX_MASK;
+	val <<= APLIC_TARGET_HART_IDX_SHIFT;
+	val |= APLIC_DEFAULT_PRIORITY;
+	writel(val, target);
+
+	irq_data_update_effective_affinity(d, cpumask_of(cpu));
+
+	return IRQ_SET_MASK_OK_DONE;
+}
+#endif
+
+static struct irq_chip aplic_chip = {
+	.name		= "RISC-V APLIC",
+	.irq_mask	= aplic_irq_mask,
+	.irq_unmask	= aplic_irq_unmask,
+	.irq_set_type	= aplic_set_type,
+#ifdef CONFIG_SMP
+	.irq_set_affinity = aplic_set_affinity,
+#endif
+	.flags		= IRQCHIP_SET_TYPE_MASKED |
+			  IRQCHIP_SKIP_SET_WAKE |
+			  IRQCHIP_MASK_ON_SUSPEND,
+};
+
+static int aplic_irqdomain_translate(struct irq_domain *d,
+				     struct irq_fwspec *fwspec,
+				     unsigned long *hwirq,
+				     unsigned int *type)
+{
+	if (WARN_ON(fwspec->param_count < 2))
+		return -EINVAL;
+	if (WARN_ON(!fwspec->param[0]))
+		return -EINVAL;
+
+	*hwirq = fwspec->param[0];
+	*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
+
+	WARN_ON(*type == IRQ_TYPE_NONE);
+
+	return 0;
+}
+
+static int aplic_irqdomain_msi_alloc(struct irq_domain *domain,
+				     unsigned int virq, unsigned int nr_irqs,
+				     void *arg)
+{
+	int i, ret;
+	unsigned int type;
+	irq_hw_number_t hwirq;
+	struct irq_fwspec *fwspec = arg;
+	struct aplic_priv *priv = platform_msi_get_host_data(domain);
+
+	ret = aplic_irqdomain_translate(domain, fwspec, &hwirq, &type);
+	if (ret)
+		return ret;
+
+	ret = platform_msi_device_domain_alloc(domain, virq, nr_irqs);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < nr_irqs; i++)
+		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
+					      &aplic_chip, priv);
+
+	return 0;
+}
+
+static const struct irq_domain_ops aplic_irqdomain_msi_ops = {
+	.translate	= aplic_irqdomain_translate,
+	.alloc		= aplic_irqdomain_msi_alloc,
+	.free		= platform_msi_device_domain_free,
+};
+
+static int aplic_irqdomain_idc_alloc(struct irq_domain *domain,
+				     unsigned int virq, unsigned int nr_irqs,
+				     void *arg)
+{
+	int i, ret;
+	unsigned int type;
+	irq_hw_number_t hwirq;
+	struct irq_fwspec *fwspec = arg;
+	struct aplic_priv *priv = domain->host_data;
+
+	ret = aplic_irqdomain_translate(domain, fwspec, &hwirq, &type);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < nr_irqs; i++) {
+		irq_domain_set_info(domain, virq + i, hwirq + i,
+				    &aplic_chip, priv, handle_simple_irq,
+				    NULL, NULL);
+		irq_set_affinity(virq + i, &priv->lmask);
+	}
+
+	return 0;
+}
+
+static const struct irq_domain_ops aplic_irqdomain_idc_ops = {
+	.translate	= aplic_irqdomain_translate,
+	.alloc		= aplic_irqdomain_idc_alloc,
+	.free		= irq_domain_free_irqs_top,
+};
+
+static void aplic_init_hw_irqs(struct aplic_priv *priv)
+{
+	int i;
+
+	/* Disable all interrupts */
+	for (i = 0; i <= priv->nr_irqs; i += 32)
+		writel(-1U, priv->regs + APLIC_CLRIE_BASE +
+			    (i / 32) * sizeof(u32));
+
+	/* Set interrupt type and default priority for all interrupts */
+	for (i = 1; i <= priv->nr_irqs; i++) {
+		writel(0, priv->regs + APLIC_SOURCECFG_BASE +
+			  (i - 1) * sizeof(u32));
+		writel(APLIC_DEFAULT_PRIORITY,
+		       priv->regs + APLIC_TARGET_BASE +
+		       (i - 1) * sizeof(u32));
+	}
+
+	/* Clear APLIC domaincfg */
+	writel(0, priv->regs + APLIC_DOMAINCFG);
+}
+
+static void aplic_init_hw_global(struct aplic_priv *priv)
+{
+	u32 val;
+#ifdef CONFIG_RISCV_M_MODE
+	u32 valH;
+
+	if (!priv->nr_idcs) {
+		val = priv->msicfg.base_ppn;
+		valH = (priv->msicfg.base_ppn >> 32) &
+			APLIC_xMSICFGADDRH_BAPPN_MASK;
+		valH |= (priv->msicfg.lhxw & APLIC_xMSICFGADDRH_LHXW_MASK)
+			<< APLIC_xMSICFGADDRH_LHXW_SHIFT;
+		valH |= (priv->msicfg.hhxw & APLIC_xMSICFGADDRH_HHXW_MASK)
+			<< APLIC_xMSICFGADDRH_HHXW_SHIFT;
+		valH |= (priv->msicfg.lhxs & APLIC_xMSICFGADDRH_LHXS_MASK)
+			<< APLIC_xMSICFGADDRH_LHXS_SHIFT;
+		valH |= (priv->msicfg.hhxs & APLIC_xMSICFGADDRH_HHXS_MASK)
+			<< APLIC_xMSICFGADDRH_HHXS_SHIFT;
+		writel(val, priv->regs + APLIC_xMSICFGADDR);
+		writel(valH, priv->regs + APLIC_xMSICFGADDRH);
+	}
+#endif
+
+	/* Setup APLIC domaincfg register */
+	val = readl(priv->regs + APLIC_DOMAINCFG);
+	val |= APLIC_DOMAINCFG_IE;
+	if (!priv->nr_idcs)
+		val |= APLIC_DOMAINCFG_DM;
+	writel(val, priv->regs + APLIC_DOMAINCFG);
+	if (readl(priv->regs + APLIC_DOMAINCFG) != val)
+		dev_warn(priv->dev,
+			 "unable to write 0x%x in domaincfg\n", val);
+}
+
+static void aplic_msi_write_msg(struct msi_desc *desc, struct msi_msg *msg)
+{
+	unsigned int group_index, hart_index, guest_index, val;
+	struct device *dev = msi_desc_to_dev(desc);
+	struct aplic_priv *priv = dev_get_drvdata(dev);
+	struct irq_data *d = irq_get_irq_data(desc->irq);
+	struct aplic_msicfg *mc = &priv->msicfg;
+	phys_addr_t tppn, tbppn, msg_addr;
+	void __iomem *target;
+
+	/* Save the MSI address and data */
+	msg_addr = (((u64)msg->address_hi) << 32) | msg->address_lo;
+	WARN_ON(msg->data > APLIC_TARGET_EIID_MASK);
+
+	/* Compute target HART PPN */
+	tppn = msg_addr >> APLIC_xMSICFGADDR_PPN_SHIFT;
+
+	/* Compute target HART Base PPN */
+	tbppn = tppn;
+	tbppn &= ~APLIC_xMSICFGADDR_PPN_HART(mc->lhxs);
+	tbppn &= ~APLIC_xMSICFGADDR_PPN_LHX(mc->lhxw, mc->lhxs);
+	tbppn &= ~APLIC_xMSICFGADDR_PPN_HHX(mc->hhxw, mc->hhxs);
+	WARN_ON(tbppn != mc->base_ppn);
+
+	/* Compute target group and hart indexes */
+	group_index = (tppn >> APLIC_xMSICFGADDR_PPN_HHX_SHIFT(mc->hhxs)) &
+		     APLIC_xMSICFGADDR_PPN_HHX_MASK(mc->hhxw);
+	hart_index = (tppn >> APLIC_xMSICFGADDR_PPN_LHX_SHIFT(mc->lhxs)) &
+		     APLIC_xMSICFGADDR_PPN_LHX_MASK(mc->lhxw);
+	hart_index |= (group_index << mc->lhxw);
+	WARN_ON(hart_index > APLIC_TARGET_HART_IDX_MASK);
+
+	/* Compute target guest index */
+	guest_index = tppn & APLIC_xMSICFGADDR_PPN_HART(mc->lhxs);
+	WARN_ON(guest_index > APLIC_TARGET_GUEST_IDX_MASK);
+
+	/* Update IRQ TARGET register */
+	target = priv->regs + APLIC_TARGET_BASE;
+	target += (d->hwirq - 1) * sizeof(u32);
+	val = (hart_index & APLIC_TARGET_HART_IDX_MASK)
+				<< APLIC_TARGET_HART_IDX_SHIFT;
+	val |= (guest_index & APLIC_TARGET_GUEST_IDX_MASK)
+				<< APLIC_TARGET_GUEST_IDX_SHIFT;
+	val |= (msg->data & APLIC_TARGET_EIID_MASK);
+	writel(val, target);
+}
+
+static int aplic_setup_msi(struct aplic_priv *priv)
+{
+	struct device *dev = priv->dev;
+	struct aplic_msicfg *mc = &priv->msicfg;
+	const struct imsic_global_config *imsic_global;
+
+	/*
+	 * The APLIC outgoing MSI config registers assume target MSI
+	 * controller to be RISC-V AIA IMSIC controller.
+	 */
+	imsic_global = imsic_get_global_config();
+	if (!imsic_global) {
+		dev_err(dev, "IMSIC global config not found\n");
+		return -ENODEV;
+	}
+
+	/* Find number of guest index bits (LHXS) */
+	mc->lhxs = imsic_global->guest_index_bits;
+	if (APLIC_xMSICFGADDRH_LHXS_MASK < mc->lhxs) {
+		dev_err(dev, "IMSIC guest index bits big for APLIC LHXS\n");
+		return -EINVAL;
+	}
+
+	/* Find number of HART index bits (LHXW) */
+	mc->lhxw = imsic_global->hart_index_bits;
+	if (APLIC_xMSICFGADDRH_LHXW_MASK < mc->lhxw) {
+		dev_err(dev, "IMSIC hart index bits big for APLIC LHXW\n");
+		return -EINVAL;
+	}
+
+	/* Find number of group index bits (HHXW) */
+	mc->hhxw = imsic_global->group_index_bits;
+	if (APLIC_xMSICFGADDRH_HHXW_MASK < mc->hhxw) {
+		dev_err(dev, "IMSIC group index bits big for APLIC HHXW\n");
+		return -EINVAL;
+	}
+
+	/* Find first bit position of group index (HHXS) */
+	mc->hhxs = imsic_global->group_index_shift;
+	if (mc->hhxs < (2 * APLIC_xMSICFGADDR_PPN_SHIFT)) {
+		dev_err(dev, "IMSIC group index shift should be >= %d\n",
+			(2 * APLIC_xMSICFGADDR_PPN_SHIFT));
+		return -EINVAL;
+	}
+	mc->hhxs -= (2 * APLIC_xMSICFGADDR_PPN_SHIFT);
+	if (APLIC_xMSICFGADDRH_HHXS_MASK < mc->hhxs) {
+		dev_err(dev, "IMSIC group index shift big for APLIC HHXS\n");
+		return -EINVAL;
+	}
+
+	/* Compute PPN base */
+	mc->base_ppn = imsic_global->base_addr >> APLIC_xMSICFGADDR_PPN_SHIFT;
+	mc->base_ppn &= ~APLIC_xMSICFGADDR_PPN_HART(mc->lhxs);
+	mc->base_ppn &= ~APLIC_xMSICFGADDR_PPN_LHX(mc->lhxw, mc->lhxs);
+	mc->base_ppn &= ~APLIC_xMSICFGADDR_PPN_HHX(mc->hhxw, mc->hhxs);
+
+	/* Use all possible CPUs as lmask */
+	cpumask_copy(&priv->lmask, cpu_possible_mask);
+
+	return 0;
+}
+
+/*
+ * To handle an APLIC IDC interrupts, we just read the CLAIMI register
+ * which will return highest priority pending interrupt and clear the
+ * pending bit of the interrupt. This process is repeated until CLAIMI
+ * register return zero value.
+ */
+static void aplic_idc_handle_irq(struct irq_desc *desc)
+{
+	struct aplic_idc *idc = this_cpu_ptr(&aplic_idcs);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	irq_hw_number_t hw_irq;
+	int irq;
+
+	chained_irq_enter(chip, desc);
+
+	while ((hw_irq = readl(idc->regs + APLIC_IDC_CLAIMI))) {
+		hw_irq = hw_irq >> APLIC_IDC_TOPI_ID_SHIFT;
+		irq = irq_find_mapping(idc->priv->irqdomain, hw_irq);
+
+		if (unlikely(irq <= 0))
+			pr_warn_ratelimited("hw_irq %lu mapping not found\n",
+					    hw_irq);
+		else
+			generic_handle_irq(irq);
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static void aplic_idc_set_delivery(struct aplic_idc *idc, bool en)
+{
+	u32 de = (en) ? APLIC_ENABLE_IDELIVERY : APLIC_DISABLE_IDELIVERY;
+	u32 th = (en) ? APLIC_ENABLE_ITHRESHOLD : APLIC_DISABLE_ITHRESHOLD;
+
+	/* Priority must be less than threshold for interrupt triggering */
+	writel(th, idc->regs + APLIC_IDC_ITHRESHOLD);
+
+	/* Delivery must be set to 1 for interrupt triggering */
+	writel(de, idc->regs + APLIC_IDC_IDELIVERY);
+}
+
+static int aplic_idc_dying_cpu(unsigned int cpu)
+{
+	if (aplic_idc_parent_irq)
+		disable_percpu_irq(aplic_idc_parent_irq);
+
+	return 0;
+}
+
+static int aplic_idc_starting_cpu(unsigned int cpu)
+{
+	if (aplic_idc_parent_irq)
+		enable_percpu_irq(aplic_idc_parent_irq,
+				  irq_get_trigger_type(aplic_idc_parent_irq));
+
+	return 0;
+}
+
+static int aplic_setup_idc(struct aplic_priv *priv)
+{
+	int i, j, rc, cpu, setup_count = 0;
+	struct device_node *node = priv->dev->of_node;
+	struct device *dev = priv->dev;
+	struct of_phandle_args parent;
+	struct irq_domain *domain;
+	unsigned long hartid;
+	struct aplic_idc *idc;
+	u32 val;
+
+	/* Setup per-CPU IDC and target CPU mask */
+	for (i = 0; i < priv->nr_idcs; i++) {
+		if (of_irq_parse_one(node, i, &parent)) {
+			dev_err(dev, "failed to parse parent for IDC%d.\n",
+				i);
+			return -EIO;
+		}
+
+		/* Skip IDCs which do not connect to external interrupts */
+		if (parent.args[0] != RV_IRQ_EXT)
+			continue;
+
+		rc = riscv_of_parent_hartid(parent.np, &hartid);
+		if (rc) {
+			dev_err(dev, "failed to parse hart ID for IDC%d.\n",
+				i);
+			return rc;
+		}
+
+		cpu = riscv_hartid_to_cpuid(hartid);
+		if (cpu < 0) {
+			dev_warn(dev, "invalid cpuid for IDC%d\n", i);
+			continue;
+		}
+
+		cpumask_set_cpu(cpu, &priv->lmask);
+
+		idc = per_cpu_ptr(&aplic_idcs, cpu);
+		WARN_ON(idc->priv);
+
+		idc->hart_index = i;
+		idc->regs = priv->regs + APLIC_IDC_BASE + i * APLIC_IDC_SIZE;
+		idc->priv = priv;
+
+		aplic_idc_set_delivery(idc, true);
+
+		/*
+		 * Boot cpu might not have APLIC hart_index = 0 so check
+		 * and update target registers of all interrupts.
+		 */
+		if (cpu == smp_processor_id() && idc->hart_index) {
+			val = idc->hart_index & APLIC_TARGET_HART_IDX_MASK;
+			val <<= APLIC_TARGET_HART_IDX_SHIFT;
+			val |= APLIC_DEFAULT_PRIORITY;
+			for (j = 1; j <= priv->nr_irqs; j++)
+				writel(val, priv->regs + APLIC_TARGET_BASE +
+					    (j - 1) * sizeof(u32));
+		}
+
+		setup_count++;
+	}
+
+	/* Find parent domain and register chained handler */
+	domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(),
+					  DOMAIN_BUS_ANY);
+	if (!aplic_idc_parent_irq && domain) {
+		aplic_idc_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT);
+		if (aplic_idc_parent_irq) {
+			irq_set_chained_handler(aplic_idc_parent_irq,
+						aplic_idc_handle_irq);
+
+			/*
+			 * Setup CPUHP notifier to enable IDC parent
+			 * interrupt on all CPUs
+			 */
+			cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
+					  "irqchip/riscv/aplic:starting",
+					  aplic_idc_starting_cpu,
+					  aplic_idc_dying_cpu);
+		}
+	}
+
+	/* Fail if we were not able to setup IDC for any CPU */
+	return (setup_count) ? 0 : -ENODEV;
+}
+
+static int aplic_probe(struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct device *dev = &pdev->dev;
+	struct aplic_priv *priv;
+	struct resource *regs;
+	phys_addr_t pa;
+	int rc;
+
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!regs) {
+		dev_err(dev, "cannot find registers resource\n");
+		return -ENOENT;
+	}
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, priv);
+	priv->dev = dev;
+
+	priv->regs = devm_ioremap(dev, regs->start, resource_size(regs));
+	if (WARN_ON(!priv->regs)) {
+		dev_err(dev, "failed ioremap registers\n");
+		return -EIO;
+	}
+
+	of_property_read_u32(node, "riscv,num-sources", &priv->nr_irqs);
+	if (!priv->nr_irqs) {
+		dev_err(dev, "failed to get number of interrupt sources\n");
+		return -EINVAL;
+	}
+
+	/* Setup initial state APLIC interrupts */
+	aplic_init_hw_irqs(priv);
+
+	/* Setup IDCs or MSIs based on parent interrupts in DT node */
+	priv->nr_idcs = of_irq_count(node);
+	if (priv->nr_idcs)
+		rc = aplic_setup_idc(priv);
+	else
+		rc = aplic_setup_msi(priv);
+	if (rc)
+		return rc;
+
+	/* Setup global config and interrupt delivery */
+	aplic_init_hw_global(priv);
+
+	/* Create irq domain instance for the APLIC */
+	if (priv->nr_idcs)
+		priv->irqdomain = irq_domain_create_linear(
+						of_node_to_fwnode(node),
+						priv->nr_irqs + 1,
+						&aplic_irqdomain_idc_ops,
+						priv);
+	else
+		priv->irqdomain = platform_msi_create_device_domain(dev,
+						priv->nr_irqs + 1,
+						aplic_msi_write_msg,
+						&aplic_irqdomain_msi_ops,
+						priv);
+	if (!priv->irqdomain) {
+		dev_err(dev, "failed to add irq domain\n");
+		return -ENOMEM;
+	}
+
+	/* Advertise the interrupt controller */
+	if (priv->nr_idcs) {
+		dev_info(dev, "%d interrupts directly connected to %d CPUs\n",
+			 priv->nr_irqs, priv->nr_idcs);
+	} else {
+		pa = priv->msicfg.base_ppn << APLIC_xMSICFGADDR_PPN_SHIFT;
+		dev_info(dev, "%d interrupts forwared to MSI base %pa\n",
+			 priv->nr_irqs, &pa);
+	}
+
+	return 0;
+}
+
+static int aplic_remove(struct platform_device *pdev)
+{
+	struct aplic_priv *priv = platform_get_drvdata(pdev);
+
+	irq_domain_remove(priv->irqdomain);
+
+	return 0;
+}
+
+static const struct of_device_id aplic_match[] = {
+	{ .compatible = "riscv,aplic" },
+	{}
+};
+
+static struct platform_driver aplic_driver = {
+	.driver = {
+		.name		= "riscv-aplic",
+		.of_match_table	= aplic_match,
+	},
+	.probe = aplic_probe,
+	.remove = aplic_remove,
+};
+
+static int __init aplic_init(void)
+{
+	return platform_driver_register(&aplic_driver);
+}
+core_initcall(aplic_init);
diff --git a/include/linux/irqchip/riscv-aplic.h b/include/linux/irqchip/riscv-aplic.h
new file mode 100644
index 000000000000..88177eefd411
--- /dev/null
+++ b/include/linux/irqchip/riscv-aplic.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2021 Western Digital Corporation or its affiliates.
+ * Copyright (C) 2022 Ventana Micro Systems Inc.
+ */
+#ifndef __LINUX_IRQCHIP_RISCV_APLIC_H
+#define __LINUX_IRQCHIP_RISCV_APLIC_H
+
+#include <linux/bitops.h>
+
+#define APLIC_MAX_IDC			BIT(14)
+#define APLIC_MAX_SOURCE		1024
+
+#define APLIC_DOMAINCFG			0x0000
+#define APLIC_DOMAINCFG_RDONLY		0x80000000
+#define APLIC_DOMAINCFG_IE		BIT(8)
+#define APLIC_DOMAINCFG_DM		BIT(2)
+#define APLIC_DOMAINCFG_BE		BIT(0)
+
+#define APLIC_SOURCECFG_BASE		0x0004
+#define APLIC_SOURCECFG_D		BIT(10)
+#define APLIC_SOURCECFG_CHILDIDX_MASK	0x000003ff
+#define APLIC_SOURCECFG_SM_MASK	0x00000007
+#define APLIC_SOURCECFG_SM_INACTIVE	0x0
+#define APLIC_SOURCECFG_SM_DETACH	0x1
+#define APLIC_SOURCECFG_SM_EDGE_RISE	0x4
+#define APLIC_SOURCECFG_SM_EDGE_FALL	0x5
+#define APLIC_SOURCECFG_SM_LEVEL_HIGH	0x6
+#define APLIC_SOURCECFG_SM_LEVEL_LOW	0x7
+
+#define APLIC_MMSICFGADDR		0x1bc0
+#define APLIC_MMSICFGADDRH		0x1bc4
+#define APLIC_SMSICFGADDR		0x1bc8
+#define APLIC_SMSICFGADDRH		0x1bcc
+
+#ifdef CONFIG_RISCV_M_MODE
+#define APLIC_xMSICFGADDR		APLIC_MMSICFGADDR
+#define APLIC_xMSICFGADDRH		APLIC_MMSICFGADDRH
+#else
+#define APLIC_xMSICFGADDR		APLIC_SMSICFGADDR
+#define APLIC_xMSICFGADDRH		APLIC_SMSICFGADDRH
+#endif
+
+#define APLIC_xMSICFGADDRH_L		BIT(31)
+#define APLIC_xMSICFGADDRH_HHXS_MASK	0x1f
+#define APLIC_xMSICFGADDRH_HHXS_SHIFT	24
+#define APLIC_xMSICFGADDRH_LHXS_MASK	0x7
+#define APLIC_xMSICFGADDRH_LHXS_SHIFT	20
+#define APLIC_xMSICFGADDRH_HHXW_MASK	0x7
+#define APLIC_xMSICFGADDRH_HHXW_SHIFT	16
+#define APLIC_xMSICFGADDRH_LHXW_MASK	0xf
+#define APLIC_xMSICFGADDRH_LHXW_SHIFT	12
+#define APLIC_xMSICFGADDRH_BAPPN_MASK	0xfff
+
+#define APLIC_xMSICFGADDR_PPN_SHIFT	12
+
+#define APLIC_xMSICFGADDR_PPN_HART(__lhxs) \
+	(BIT(__lhxs) - 1)
+
+#define APLIC_xMSICFGADDR_PPN_LHX_MASK(__lhxw) \
+	(BIT(__lhxw) - 1)
+#define APLIC_xMSICFGADDR_PPN_LHX_SHIFT(__lhxs) \
+	((__lhxs))
+#define APLIC_xMSICFGADDR_PPN_LHX(__lhxw, __lhxs) \
+	(APLIC_xMSICFGADDR_PPN_LHX_MASK(__lhxw) << \
+	 APLIC_xMSICFGADDR_PPN_LHX_SHIFT(__lhxs))
+
+#define APLIC_xMSICFGADDR_PPN_HHX_MASK(__hhxw) \
+	(BIT(__hhxw) - 1)
+#define APLIC_xMSICFGADDR_PPN_HHX_SHIFT(__hhxs) \
+	((__hhxs) + APLIC_xMSICFGADDR_PPN_SHIFT)
+#define APLIC_xMSICFGADDR_PPN_HHX(__hhxw, __hhxs) \
+	(APLIC_xMSICFGADDR_PPN_HHX_MASK(__hhxw) << \
+	 APLIC_xMSICFGADDR_PPN_HHX_SHIFT(__hhxs))
+
+#define APLIC_SETIP_BASE		0x1c00
+#define APLIC_SETIPNUM			0x1cdc
+
+#define APLIC_CLRIP_BASE		0x1d00
+#define APLIC_CLRIPNUM			0x1ddc
+
+#define APLIC_SETIE_BASE		0x1e00
+#define APLIC_SETIENUM			0x1edc
+
+#define APLIC_CLRIE_BASE		0x1f00
+#define APLIC_CLRIENUM			0x1fdc
+
+#define APLIC_SETIPNUM_LE		0x2000
+#define APLIC_SETIPNUM_BE		0x2004
+
+#define APLIC_GENMSI			0x3000
+
+#define APLIC_TARGET_BASE		0x3004
+#define APLIC_TARGET_HART_IDX_SHIFT	18
+#define APLIC_TARGET_HART_IDX_MASK	0x3fff
+#define APLIC_TARGET_GUEST_IDX_SHIFT	12
+#define APLIC_TARGET_GUEST_IDX_MASK	0x3f
+#define APLIC_TARGET_IPRIO_MASK	0xff
+#define APLIC_TARGET_EIID_MASK	0x7ff
+
+#define APLIC_IDC_BASE			0x4000
+#define APLIC_IDC_SIZE			32
+
+#define APLIC_IDC_IDELIVERY		0x00
+
+#define APLIC_IDC_IFORCE		0x04
+
+#define APLIC_IDC_ITHRESHOLD		0x08
+
+#define APLIC_IDC_TOPI			0x18
+#define APLIC_IDC_TOPI_ID_SHIFT	16
+#define APLIC_IDC_TOPI_ID_MASK	0x3ff
+#define APLIC_IDC_TOPI_PRIO_MASK	0xff
+
+#define APLIC_IDC_CLAIMI		0x1c
+
+#endif
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* [PATCH 7/9] irqchip: Add RISC-V advanced PLIC driver
@ 2022-11-11  4:42   ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-11  4:42 UTC (permalink / raw)
  To: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski
  Cc: Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree, Anup Patel

The RISC-V advanced interrupt architecture (AIA) specification defines
a new interrupt controller for managing wired interrupts on a RISC-V
platform. This new interrupt controller is referred to as advanced
platform-level interrupt controller (APLIC) which can forward wired
interrupts to CPUs (or HARTs) as local interrupts OR as message
signaled interrupts.
(For more details refer https://github.com/riscv/riscv-aia)

This patch adds an irqchip driver for RISC-V APLIC found on RISC-V
platforms.

Signed-off-by: Anup Patel <apatel@ventanamicro.com>
---
 drivers/irqchip/Kconfig             |  12 +
 drivers/irqchip/Makefile            |   1 +
 drivers/irqchip/irq-riscv-aplic.c   | 656 ++++++++++++++++++++++++++++
 include/linux/irqchip/riscv-aplic.h | 117 +++++
 4 files changed, 786 insertions(+)
 create mode 100644 drivers/irqchip/irq-riscv-aplic.c
 create mode 100644 include/linux/irqchip/riscv-aplic.h

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 8246c08f0fd3..9f022e71d937 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -563,6 +563,18 @@ config SIFIVE_PLIC
 
 	   If you don't know what to do here, say Y.
 
+config RISCV_APLIC
+	bool "RISC-V Advanced Platform-Level Interrupt Controller"
+	depends on RISCV
+	select IRQ_DOMAIN_HIERARCHY
+	select GENERIC_MSI_IRQ_DOMAIN
+	help
+	   This enables support for the APLIC chip found in RISC-V systems.
+	   The APLIC controls device wired interrupts and forwards them to
+	   each core as wired local interrupt or per-core MSIs.
+
+	   If you don't know what to do here, say Y.
+
 config RISCV_IMSIC
 	bool "RISC-V Incoming MSI Controller"
 	depends on RISCV
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 22c723cc6ec8..6154e5bc4228 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -96,6 +96,7 @@ obj-$(CONFIG_QCOM_MPM)			+= irq-qcom-mpm.o
 obj-$(CONFIG_CSKY_MPINTC)		+= irq-csky-mpintc.o
 obj-$(CONFIG_CSKY_APB_INTC)		+= irq-csky-apb-intc.o
 obj-$(CONFIG_RISCV_INTC)		+= irq-riscv-intc.o
+obj-$(CONFIG_RISCV_APLIC)		+= irq-riscv-aplic.o
 obj-$(CONFIG_RISCV_IMSIC)		+= irq-riscv-imsic.o
 obj-$(CONFIG_SIFIVE_PLIC)		+= irq-sifive-plic.o
 obj-$(CONFIG_IMX_IRQSTEER)		+= irq-imx-irqsteer.o
diff --git a/drivers/irqchip/irq-riscv-aplic.c b/drivers/irqchip/irq-riscv-aplic.c
new file mode 100644
index 000000000000..e6d0e1bb15a8
--- /dev/null
+++ b/drivers/irqchip/irq-riscv-aplic.c
@@ -0,0 +1,656 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Western Digital Corporation or its affiliates.
+ * Copyright (C) 2022 Ventana Micro Systems Inc.
+ */
+
+#include <linux/bitops.h>
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqchip/riscv-aplic.h>
+#include <linux/irqchip/riscv-imsic.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/smp.h>
+
+#define APLIC_DEFAULT_PRIORITY		1
+#define APLIC_DISABLE_IDELIVERY		0
+#define APLIC_ENABLE_IDELIVERY		1
+#define APLIC_DISABLE_ITHRESHOLD	1
+#define APLIC_ENABLE_ITHRESHOLD		0
+
+struct aplic_msicfg {
+	phys_addr_t		base_ppn;
+	u32			hhxs;
+	u32			hhxw;
+	u32			lhxs;
+	u32			lhxw;
+};
+
+struct aplic_idc {
+	unsigned int		hart_index;
+	void __iomem		*regs;
+	struct aplic_priv	*priv;
+};
+
+struct aplic_priv {
+	struct device		*dev;
+	u32			nr_irqs;
+	u32			nr_idcs;
+	void __iomem		*regs;
+	struct irq_domain	*irqdomain;
+	struct aplic_msicfg	msicfg;
+	struct cpumask		lmask;
+};
+
+static unsigned int aplic_idc_parent_irq;
+static DEFINE_PER_CPU(struct aplic_idc, aplic_idcs);
+
+static void aplic_irq_unmask(struct irq_data *d)
+{
+	struct aplic_priv *priv = irq_data_get_irq_chip_data(d);
+
+	writel(d->hwirq, priv->regs + APLIC_SETIENUM);
+
+	if (!priv->nr_idcs)
+		irq_chip_unmask_parent(d);
+}
+
+static void aplic_irq_mask(struct irq_data *d)
+{
+	struct aplic_priv *priv = irq_data_get_irq_chip_data(d);
+
+	writel(d->hwirq, priv->regs + APLIC_CLRIENUM);
+
+	if (!priv->nr_idcs)
+		irq_chip_mask_parent(d);
+}
+
+static int aplic_set_type(struct irq_data *d, unsigned int type)
+{
+	u32 val = 0;
+	void __iomem *sourcecfg;
+	struct aplic_priv *priv = irq_data_get_irq_chip_data(d);
+
+	switch (type) {
+	case IRQ_TYPE_NONE:
+		val = APLIC_SOURCECFG_SM_INACTIVE;
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		val = APLIC_SOURCECFG_SM_LEVEL_LOW;
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		val = APLIC_SOURCECFG_SM_LEVEL_HIGH;
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		val = APLIC_SOURCECFG_SM_EDGE_FALL;
+		break;
+	case IRQ_TYPE_EDGE_RISING:
+		val = APLIC_SOURCECFG_SM_EDGE_RISE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	sourcecfg = priv->regs + APLIC_SOURCECFG_BASE;
+	sourcecfg += (d->hwirq - 1) * sizeof(u32);
+	writel(val, sourcecfg);
+
+	return 0;
+}
+
+#ifdef CONFIG_SMP
+static int aplic_set_affinity(struct irq_data *d,
+			      const struct cpumask *mask_val, bool force)
+{
+	struct aplic_priv *priv = irq_data_get_irq_chip_data(d);
+	struct aplic_idc *idc;
+	unsigned int cpu, val;
+	struct cpumask amask;
+	void __iomem *target;
+
+	if (!priv->nr_idcs)
+		return irq_chip_set_affinity_parent(d, mask_val, force);
+
+	cpumask_and(&amask, &priv->lmask, mask_val);
+
+	if (force)
+		cpu = cpumask_first(&amask);
+	else
+		cpu = cpumask_any_and(&amask, cpu_online_mask);
+
+	if (cpu >= nr_cpu_ids)
+		return -EINVAL;
+
+	idc = per_cpu_ptr(&aplic_idcs, cpu);
+	target = priv->regs + APLIC_TARGET_BASE;
+	target += (d->hwirq - 1) * sizeof(u32);
+	val = idc->hart_index & APLIC_TARGET_HART_IDX_MASK;
+	val <<= APLIC_TARGET_HART_IDX_SHIFT;
+	val |= APLIC_DEFAULT_PRIORITY;
+	writel(val, target);
+
+	irq_data_update_effective_affinity(d, cpumask_of(cpu));
+
+	return IRQ_SET_MASK_OK_DONE;
+}
+#endif
+
+static struct irq_chip aplic_chip = {
+	.name		= "RISC-V APLIC",
+	.irq_mask	= aplic_irq_mask,
+	.irq_unmask	= aplic_irq_unmask,
+	.irq_set_type	= aplic_set_type,
+#ifdef CONFIG_SMP
+	.irq_set_affinity = aplic_set_affinity,
+#endif
+	.flags		= IRQCHIP_SET_TYPE_MASKED |
+			  IRQCHIP_SKIP_SET_WAKE |
+			  IRQCHIP_MASK_ON_SUSPEND,
+};
+
+static int aplic_irqdomain_translate(struct irq_domain *d,
+				     struct irq_fwspec *fwspec,
+				     unsigned long *hwirq,
+				     unsigned int *type)
+{
+	if (WARN_ON(fwspec->param_count < 2))
+		return -EINVAL;
+	if (WARN_ON(!fwspec->param[0]))
+		return -EINVAL;
+
+	*hwirq = fwspec->param[0];
+	*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
+
+	WARN_ON(*type == IRQ_TYPE_NONE);
+
+	return 0;
+}
+
+static int aplic_irqdomain_msi_alloc(struct irq_domain *domain,
+				     unsigned int virq, unsigned int nr_irqs,
+				     void *arg)
+{
+	int i, ret;
+	unsigned int type;
+	irq_hw_number_t hwirq;
+	struct irq_fwspec *fwspec = arg;
+	struct aplic_priv *priv = platform_msi_get_host_data(domain);
+
+	ret = aplic_irqdomain_translate(domain, fwspec, &hwirq, &type);
+	if (ret)
+		return ret;
+
+	ret = platform_msi_device_domain_alloc(domain, virq, nr_irqs);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < nr_irqs; i++)
+		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
+					      &aplic_chip, priv);
+
+	return 0;
+}
+
+static const struct irq_domain_ops aplic_irqdomain_msi_ops = {
+	.translate	= aplic_irqdomain_translate,
+	.alloc		= aplic_irqdomain_msi_alloc,
+	.free		= platform_msi_device_domain_free,
+};
+
+static int aplic_irqdomain_idc_alloc(struct irq_domain *domain,
+				     unsigned int virq, unsigned int nr_irqs,
+				     void *arg)
+{
+	int i, ret;
+	unsigned int type;
+	irq_hw_number_t hwirq;
+	struct irq_fwspec *fwspec = arg;
+	struct aplic_priv *priv = domain->host_data;
+
+	ret = aplic_irqdomain_translate(domain, fwspec, &hwirq, &type);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < nr_irqs; i++) {
+		irq_domain_set_info(domain, virq + i, hwirq + i,
+				    &aplic_chip, priv, handle_simple_irq,
+				    NULL, NULL);
+		irq_set_affinity(virq + i, &priv->lmask);
+	}
+
+	return 0;
+}
+
+static const struct irq_domain_ops aplic_irqdomain_idc_ops = {
+	.translate	= aplic_irqdomain_translate,
+	.alloc		= aplic_irqdomain_idc_alloc,
+	.free		= irq_domain_free_irqs_top,
+};
+
+static void aplic_init_hw_irqs(struct aplic_priv *priv)
+{
+	int i;
+
+	/* Disable all interrupts */
+	for (i = 0; i <= priv->nr_irqs; i += 32)
+		writel(-1U, priv->regs + APLIC_CLRIE_BASE +
+			    (i / 32) * sizeof(u32));
+
+	/* Set interrupt type and default priority for all interrupts */
+	for (i = 1; i <= priv->nr_irqs; i++) {
+		writel(0, priv->regs + APLIC_SOURCECFG_BASE +
+			  (i - 1) * sizeof(u32));
+		writel(APLIC_DEFAULT_PRIORITY,
+		       priv->regs + APLIC_TARGET_BASE +
+		       (i - 1) * sizeof(u32));
+	}
+
+	/* Clear APLIC domaincfg */
+	writel(0, priv->regs + APLIC_DOMAINCFG);
+}
+
+static void aplic_init_hw_global(struct aplic_priv *priv)
+{
+	u32 val;
+#ifdef CONFIG_RISCV_M_MODE
+	u32 valH;
+
+	if (!priv->nr_idcs) {
+		val = priv->msicfg.base_ppn;
+		valH = (priv->msicfg.base_ppn >> 32) &
+			APLIC_xMSICFGADDRH_BAPPN_MASK;
+		valH |= (priv->msicfg.lhxw & APLIC_xMSICFGADDRH_LHXW_MASK)
+			<< APLIC_xMSICFGADDRH_LHXW_SHIFT;
+		valH |= (priv->msicfg.hhxw & APLIC_xMSICFGADDRH_HHXW_MASK)
+			<< APLIC_xMSICFGADDRH_HHXW_SHIFT;
+		valH |= (priv->msicfg.lhxs & APLIC_xMSICFGADDRH_LHXS_MASK)
+			<< APLIC_xMSICFGADDRH_LHXS_SHIFT;
+		valH |= (priv->msicfg.hhxs & APLIC_xMSICFGADDRH_HHXS_MASK)
+			<< APLIC_xMSICFGADDRH_HHXS_SHIFT;
+		writel(val, priv->regs + APLIC_xMSICFGADDR);
+		writel(valH, priv->regs + APLIC_xMSICFGADDRH);
+	}
+#endif
+
+	/* Setup APLIC domaincfg register */
+	val = readl(priv->regs + APLIC_DOMAINCFG);
+	val |= APLIC_DOMAINCFG_IE;
+	if (!priv->nr_idcs)
+		val |= APLIC_DOMAINCFG_DM;
+	writel(val, priv->regs + APLIC_DOMAINCFG);
+	if (readl(priv->regs + APLIC_DOMAINCFG) != val)
+		dev_warn(priv->dev,
+			 "unable to write 0x%x in domaincfg\n", val);
+}
+
+static void aplic_msi_write_msg(struct msi_desc *desc, struct msi_msg *msg)
+{
+	unsigned int group_index, hart_index, guest_index, val;
+	struct device *dev = msi_desc_to_dev(desc);
+	struct aplic_priv *priv = dev_get_drvdata(dev);
+	struct irq_data *d = irq_get_irq_data(desc->irq);
+	struct aplic_msicfg *mc = &priv->msicfg;
+	phys_addr_t tppn, tbppn, msg_addr;
+	void __iomem *target;
+
+	/* Save the MSI address and data */
+	msg_addr = (((u64)msg->address_hi) << 32) | msg->address_lo;
+	WARN_ON(msg->data > APLIC_TARGET_EIID_MASK);
+
+	/* Compute target HART PPN */
+	tppn = msg_addr >> APLIC_xMSICFGADDR_PPN_SHIFT;
+
+	/* Compute target HART Base PPN */
+	tbppn = tppn;
+	tbppn &= ~APLIC_xMSICFGADDR_PPN_HART(mc->lhxs);
+	tbppn &= ~APLIC_xMSICFGADDR_PPN_LHX(mc->lhxw, mc->lhxs);
+	tbppn &= ~APLIC_xMSICFGADDR_PPN_HHX(mc->hhxw, mc->hhxs);
+	WARN_ON(tbppn != mc->base_ppn);
+
+	/* Compute target group and hart indexes */
+	group_index = (tppn >> APLIC_xMSICFGADDR_PPN_HHX_SHIFT(mc->hhxs)) &
+		     APLIC_xMSICFGADDR_PPN_HHX_MASK(mc->hhxw);
+	hart_index = (tppn >> APLIC_xMSICFGADDR_PPN_LHX_SHIFT(mc->lhxs)) &
+		     APLIC_xMSICFGADDR_PPN_LHX_MASK(mc->lhxw);
+	hart_index |= (group_index << mc->lhxw);
+	WARN_ON(hart_index > APLIC_TARGET_HART_IDX_MASK);
+
+	/* Compute target guest index */
+	guest_index = tppn & APLIC_xMSICFGADDR_PPN_HART(mc->lhxs);
+	WARN_ON(guest_index > APLIC_TARGET_GUEST_IDX_MASK);
+
+	/* Update IRQ TARGET register */
+	target = priv->regs + APLIC_TARGET_BASE;
+	target += (d->hwirq - 1) * sizeof(u32);
+	val = (hart_index & APLIC_TARGET_HART_IDX_MASK)
+				<< APLIC_TARGET_HART_IDX_SHIFT;
+	val |= (guest_index & APLIC_TARGET_GUEST_IDX_MASK)
+				<< APLIC_TARGET_GUEST_IDX_SHIFT;
+	val |= (msg->data & APLIC_TARGET_EIID_MASK);
+	writel(val, target);
+}
+
+static int aplic_setup_msi(struct aplic_priv *priv)
+{
+	struct device *dev = priv->dev;
+	struct aplic_msicfg *mc = &priv->msicfg;
+	const struct imsic_global_config *imsic_global;
+
+	/*
+	 * The APLIC outgoing MSI config registers assume target MSI
+	 * controller to be RISC-V AIA IMSIC controller.
+	 */
+	imsic_global = imsic_get_global_config();
+	if (!imsic_global) {
+		dev_err(dev, "IMSIC global config not found\n");
+		return -ENODEV;
+	}
+
+	/* Find number of guest index bits (LHXS) */
+	mc->lhxs = imsic_global->guest_index_bits;
+	if (APLIC_xMSICFGADDRH_LHXS_MASK < mc->lhxs) {
+		dev_err(dev, "IMSIC guest index bits big for APLIC LHXS\n");
+		return -EINVAL;
+	}
+
+	/* Find number of HART index bits (LHXW) */
+	mc->lhxw = imsic_global->hart_index_bits;
+	if (APLIC_xMSICFGADDRH_LHXW_MASK < mc->lhxw) {
+		dev_err(dev, "IMSIC hart index bits big for APLIC LHXW\n");
+		return -EINVAL;
+	}
+
+	/* Find number of group index bits (HHXW) */
+	mc->hhxw = imsic_global->group_index_bits;
+	if (APLIC_xMSICFGADDRH_HHXW_MASK < mc->hhxw) {
+		dev_err(dev, "IMSIC group index bits big for APLIC HHXW\n");
+		return -EINVAL;
+	}
+
+	/* Find first bit position of group index (HHXS) */
+	mc->hhxs = imsic_global->group_index_shift;
+	if (mc->hhxs < (2 * APLIC_xMSICFGADDR_PPN_SHIFT)) {
+		dev_err(dev, "IMSIC group index shift should be >= %d\n",
+			(2 * APLIC_xMSICFGADDR_PPN_SHIFT));
+		return -EINVAL;
+	}
+	mc->hhxs -= (2 * APLIC_xMSICFGADDR_PPN_SHIFT);
+	if (APLIC_xMSICFGADDRH_HHXS_MASK < mc->hhxs) {
+		dev_err(dev, "IMSIC group index shift big for APLIC HHXS\n");
+		return -EINVAL;
+	}
+
+	/* Compute PPN base */
+	mc->base_ppn = imsic_global->base_addr >> APLIC_xMSICFGADDR_PPN_SHIFT;
+	mc->base_ppn &= ~APLIC_xMSICFGADDR_PPN_HART(mc->lhxs);
+	mc->base_ppn &= ~APLIC_xMSICFGADDR_PPN_LHX(mc->lhxw, mc->lhxs);
+	mc->base_ppn &= ~APLIC_xMSICFGADDR_PPN_HHX(mc->hhxw, mc->hhxs);
+
+	/* Use all possible CPUs as lmask */
+	cpumask_copy(&priv->lmask, cpu_possible_mask);
+
+	return 0;
+}
+
+/*
+ * To handle an APLIC IDC interrupts, we just read the CLAIMI register
+ * which will return highest priority pending interrupt and clear the
+ * pending bit of the interrupt. This process is repeated until CLAIMI
+ * register return zero value.
+ */
+static void aplic_idc_handle_irq(struct irq_desc *desc)
+{
+	struct aplic_idc *idc = this_cpu_ptr(&aplic_idcs);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	irq_hw_number_t hw_irq;
+	int irq;
+
+	chained_irq_enter(chip, desc);
+
+	while ((hw_irq = readl(idc->regs + APLIC_IDC_CLAIMI))) {
+		hw_irq = hw_irq >> APLIC_IDC_TOPI_ID_SHIFT;
+		irq = irq_find_mapping(idc->priv->irqdomain, hw_irq);
+
+		if (unlikely(irq <= 0))
+			pr_warn_ratelimited("hw_irq %lu mapping not found\n",
+					    hw_irq);
+		else
+			generic_handle_irq(irq);
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static void aplic_idc_set_delivery(struct aplic_idc *idc, bool en)
+{
+	u32 de = (en) ? APLIC_ENABLE_IDELIVERY : APLIC_DISABLE_IDELIVERY;
+	u32 th = (en) ? APLIC_ENABLE_ITHRESHOLD : APLIC_DISABLE_ITHRESHOLD;
+
+	/* Priority must be less than threshold for interrupt triggering */
+	writel(th, idc->regs + APLIC_IDC_ITHRESHOLD);
+
+	/* Delivery must be set to 1 for interrupt triggering */
+	writel(de, idc->regs + APLIC_IDC_IDELIVERY);
+}
+
+static int aplic_idc_dying_cpu(unsigned int cpu)
+{
+	if (aplic_idc_parent_irq)
+		disable_percpu_irq(aplic_idc_parent_irq);
+
+	return 0;
+}
+
+static int aplic_idc_starting_cpu(unsigned int cpu)
+{
+	if (aplic_idc_parent_irq)
+		enable_percpu_irq(aplic_idc_parent_irq,
+				  irq_get_trigger_type(aplic_idc_parent_irq));
+
+	return 0;
+}
+
+static int aplic_setup_idc(struct aplic_priv *priv)
+{
+	int i, j, rc, cpu, setup_count = 0;
+	struct device_node *node = priv->dev->of_node;
+	struct device *dev = priv->dev;
+	struct of_phandle_args parent;
+	struct irq_domain *domain;
+	unsigned long hartid;
+	struct aplic_idc *idc;
+	u32 val;
+
+	/* Setup per-CPU IDC and target CPU mask */
+	for (i = 0; i < priv->nr_idcs; i++) {
+		if (of_irq_parse_one(node, i, &parent)) {
+			dev_err(dev, "failed to parse parent for IDC%d.\n",
+				i);
+			return -EIO;
+		}
+
+		/* Skip IDCs which do not connect to external interrupts */
+		if (parent.args[0] != RV_IRQ_EXT)
+			continue;
+
+		rc = riscv_of_parent_hartid(parent.np, &hartid);
+		if (rc) {
+			dev_err(dev, "failed to parse hart ID for IDC%d.\n",
+				i);
+			return rc;
+		}
+
+		cpu = riscv_hartid_to_cpuid(hartid);
+		if (cpu < 0) {
+			dev_warn(dev, "invalid cpuid for IDC%d\n", i);
+			continue;
+		}
+
+		cpumask_set_cpu(cpu, &priv->lmask);
+
+		idc = per_cpu_ptr(&aplic_idcs, cpu);
+		WARN_ON(idc->priv);
+
+		idc->hart_index = i;
+		idc->regs = priv->regs + APLIC_IDC_BASE + i * APLIC_IDC_SIZE;
+		idc->priv = priv;
+
+		aplic_idc_set_delivery(idc, true);
+
+		/*
+		 * Boot cpu might not have APLIC hart_index = 0 so check
+		 * and update target registers of all interrupts.
+		 */
+		if (cpu == smp_processor_id() && idc->hart_index) {
+			val = idc->hart_index & APLIC_TARGET_HART_IDX_MASK;
+			val <<= APLIC_TARGET_HART_IDX_SHIFT;
+			val |= APLIC_DEFAULT_PRIORITY;
+			for (j = 1; j <= priv->nr_irqs; j++)
+				writel(val, priv->regs + APLIC_TARGET_BASE +
+					    (j - 1) * sizeof(u32));
+		}
+
+		setup_count++;
+	}
+
+	/* Find parent domain and register chained handler */
+	domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(),
+					  DOMAIN_BUS_ANY);
+	if (!aplic_idc_parent_irq && domain) {
+		aplic_idc_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT);
+		if (aplic_idc_parent_irq) {
+			irq_set_chained_handler(aplic_idc_parent_irq,
+						aplic_idc_handle_irq);
+
+			/*
+			 * Setup CPUHP notifier to enable IDC parent
+			 * interrupt on all CPUs
+			 */
+			cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
+					  "irqchip/riscv/aplic:starting",
+					  aplic_idc_starting_cpu,
+					  aplic_idc_dying_cpu);
+		}
+	}
+
+	/* Fail if we were not able to setup IDC for any CPU */
+	return (setup_count) ? 0 : -ENODEV;
+}
+
+static int aplic_probe(struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct device *dev = &pdev->dev;
+	struct aplic_priv *priv;
+	struct resource *regs;
+	phys_addr_t pa;
+	int rc;
+
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!regs) {
+		dev_err(dev, "cannot find registers resource\n");
+		return -ENOENT;
+	}
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, priv);
+	priv->dev = dev;
+
+	priv->regs = devm_ioremap(dev, regs->start, resource_size(regs));
+	if (WARN_ON(!priv->regs)) {
+		dev_err(dev, "failed ioremap registers\n");
+		return -EIO;
+	}
+
+	of_property_read_u32(node, "riscv,num-sources", &priv->nr_irqs);
+	if (!priv->nr_irqs) {
+		dev_err(dev, "failed to get number of interrupt sources\n");
+		return -EINVAL;
+	}
+
+	/* Setup initial state APLIC interrupts */
+	aplic_init_hw_irqs(priv);
+
+	/* Setup IDCs or MSIs based on parent interrupts in DT node */
+	priv->nr_idcs = of_irq_count(node);
+	if (priv->nr_idcs)
+		rc = aplic_setup_idc(priv);
+	else
+		rc = aplic_setup_msi(priv);
+	if (rc)
+		return rc;
+
+	/* Setup global config and interrupt delivery */
+	aplic_init_hw_global(priv);
+
+	/* Create irq domain instance for the APLIC */
+	if (priv->nr_idcs)
+		priv->irqdomain = irq_domain_create_linear(
+						of_node_to_fwnode(node),
+						priv->nr_irqs + 1,
+						&aplic_irqdomain_idc_ops,
+						priv);
+	else
+		priv->irqdomain = platform_msi_create_device_domain(dev,
+						priv->nr_irqs + 1,
+						aplic_msi_write_msg,
+						&aplic_irqdomain_msi_ops,
+						priv);
+	if (!priv->irqdomain) {
+		dev_err(dev, "failed to add irq domain\n");
+		return -ENOMEM;
+	}
+
+	/* Advertise the interrupt controller */
+	if (priv->nr_idcs) {
+		dev_info(dev, "%d interrupts directly connected to %d CPUs\n",
+			 priv->nr_irqs, priv->nr_idcs);
+	} else {
+		pa = priv->msicfg.base_ppn << APLIC_xMSICFGADDR_PPN_SHIFT;
+		dev_info(dev, "%d interrupts forwared to MSI base %pa\n",
+			 priv->nr_irqs, &pa);
+	}
+
+	return 0;
+}
+
+static int aplic_remove(struct platform_device *pdev)
+{
+	struct aplic_priv *priv = platform_get_drvdata(pdev);
+
+	irq_domain_remove(priv->irqdomain);
+
+	return 0;
+}
+
+static const struct of_device_id aplic_match[] = {
+	{ .compatible = "riscv,aplic" },
+	{}
+};
+
+static struct platform_driver aplic_driver = {
+	.driver = {
+		.name		= "riscv-aplic",
+		.of_match_table	= aplic_match,
+	},
+	.probe = aplic_probe,
+	.remove = aplic_remove,
+};
+
+static int __init aplic_init(void)
+{
+	return platform_driver_register(&aplic_driver);
+}
+core_initcall(aplic_init);
diff --git a/include/linux/irqchip/riscv-aplic.h b/include/linux/irqchip/riscv-aplic.h
new file mode 100644
index 000000000000..88177eefd411
--- /dev/null
+++ b/include/linux/irqchip/riscv-aplic.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2021 Western Digital Corporation or its affiliates.
+ * Copyright (C) 2022 Ventana Micro Systems Inc.
+ */
+#ifndef __LINUX_IRQCHIP_RISCV_APLIC_H
+#define __LINUX_IRQCHIP_RISCV_APLIC_H
+
+#include <linux/bitops.h>
+
+#define APLIC_MAX_IDC			BIT(14)
+#define APLIC_MAX_SOURCE		1024
+
+#define APLIC_DOMAINCFG			0x0000
+#define APLIC_DOMAINCFG_RDONLY		0x80000000
+#define APLIC_DOMAINCFG_IE		BIT(8)
+#define APLIC_DOMAINCFG_DM		BIT(2)
+#define APLIC_DOMAINCFG_BE		BIT(0)
+
+#define APLIC_SOURCECFG_BASE		0x0004
+#define APLIC_SOURCECFG_D		BIT(10)
+#define APLIC_SOURCECFG_CHILDIDX_MASK	0x000003ff
+#define APLIC_SOURCECFG_SM_MASK	0x00000007
+#define APLIC_SOURCECFG_SM_INACTIVE	0x0
+#define APLIC_SOURCECFG_SM_DETACH	0x1
+#define APLIC_SOURCECFG_SM_EDGE_RISE	0x4
+#define APLIC_SOURCECFG_SM_EDGE_FALL	0x5
+#define APLIC_SOURCECFG_SM_LEVEL_HIGH	0x6
+#define APLIC_SOURCECFG_SM_LEVEL_LOW	0x7
+
+#define APLIC_MMSICFGADDR		0x1bc0
+#define APLIC_MMSICFGADDRH		0x1bc4
+#define APLIC_SMSICFGADDR		0x1bc8
+#define APLIC_SMSICFGADDRH		0x1bcc
+
+#ifdef CONFIG_RISCV_M_MODE
+#define APLIC_xMSICFGADDR		APLIC_MMSICFGADDR
+#define APLIC_xMSICFGADDRH		APLIC_MMSICFGADDRH
+#else
+#define APLIC_xMSICFGADDR		APLIC_SMSICFGADDR
+#define APLIC_xMSICFGADDRH		APLIC_SMSICFGADDRH
+#endif
+
+#define APLIC_xMSICFGADDRH_L		BIT(31)
+#define APLIC_xMSICFGADDRH_HHXS_MASK	0x1f
+#define APLIC_xMSICFGADDRH_HHXS_SHIFT	24
+#define APLIC_xMSICFGADDRH_LHXS_MASK	0x7
+#define APLIC_xMSICFGADDRH_LHXS_SHIFT	20
+#define APLIC_xMSICFGADDRH_HHXW_MASK	0x7
+#define APLIC_xMSICFGADDRH_HHXW_SHIFT	16
+#define APLIC_xMSICFGADDRH_LHXW_MASK	0xf
+#define APLIC_xMSICFGADDRH_LHXW_SHIFT	12
+#define APLIC_xMSICFGADDRH_BAPPN_MASK	0xfff
+
+#define APLIC_xMSICFGADDR_PPN_SHIFT	12
+
+#define APLIC_xMSICFGADDR_PPN_HART(__lhxs) \
+	(BIT(__lhxs) - 1)
+
+#define APLIC_xMSICFGADDR_PPN_LHX_MASK(__lhxw) \
+	(BIT(__lhxw) - 1)
+#define APLIC_xMSICFGADDR_PPN_LHX_SHIFT(__lhxs) \
+	((__lhxs))
+#define APLIC_xMSICFGADDR_PPN_LHX(__lhxw, __lhxs) \
+	(APLIC_xMSICFGADDR_PPN_LHX_MASK(__lhxw) << \
+	 APLIC_xMSICFGADDR_PPN_LHX_SHIFT(__lhxs))
+
+#define APLIC_xMSICFGADDR_PPN_HHX_MASK(__hhxw) \
+	(BIT(__hhxw) - 1)
+#define APLIC_xMSICFGADDR_PPN_HHX_SHIFT(__hhxs) \
+	((__hhxs) + APLIC_xMSICFGADDR_PPN_SHIFT)
+#define APLIC_xMSICFGADDR_PPN_HHX(__hhxw, __hhxs) \
+	(APLIC_xMSICFGADDR_PPN_HHX_MASK(__hhxw) << \
+	 APLIC_xMSICFGADDR_PPN_HHX_SHIFT(__hhxs))
+
+#define APLIC_SETIP_BASE		0x1c00
+#define APLIC_SETIPNUM			0x1cdc
+
+#define APLIC_CLRIP_BASE		0x1d00
+#define APLIC_CLRIPNUM			0x1ddc
+
+#define APLIC_SETIE_BASE		0x1e00
+#define APLIC_SETIENUM			0x1edc
+
+#define APLIC_CLRIE_BASE		0x1f00
+#define APLIC_CLRIENUM			0x1fdc
+
+#define APLIC_SETIPNUM_LE		0x2000
+#define APLIC_SETIPNUM_BE		0x2004
+
+#define APLIC_GENMSI			0x3000
+
+#define APLIC_TARGET_BASE		0x3004
+#define APLIC_TARGET_HART_IDX_SHIFT	18
+#define APLIC_TARGET_HART_IDX_MASK	0x3fff
+#define APLIC_TARGET_GUEST_IDX_SHIFT	12
+#define APLIC_TARGET_GUEST_IDX_MASK	0x3f
+#define APLIC_TARGET_IPRIO_MASK	0xff
+#define APLIC_TARGET_EIID_MASK	0x7ff
+
+#define APLIC_IDC_BASE			0x4000
+#define APLIC_IDC_SIZE			32
+
+#define APLIC_IDC_IDELIVERY		0x00
+
+#define APLIC_IDC_IFORCE		0x04
+
+#define APLIC_IDC_ITHRESHOLD		0x08
+
+#define APLIC_IDC_TOPI			0x18
+#define APLIC_IDC_TOPI_ID_SHIFT	16
+#define APLIC_IDC_TOPI_ID_MASK	0x3ff
+#define APLIC_IDC_TOPI_PRIO_MASK	0xff
+
+#define APLIC_IDC_CLAIMI		0x1c
+
+#endif
-- 
2.34.1


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

* [PATCH 8/9] RISC-V: Select APLIC and IMSIC drivers for QEMU virt machine
  2022-11-11  4:41 ` Anup Patel
@ 2022-11-11  4:42   ` Anup Patel
  -1 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-11  4:42 UTC (permalink / raw)
  To: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski
  Cc: Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree, Anup Patel

The QEMU virt machine supports APLIC and IMSIC emulation so let's
select APLIC and IMSIC drivers from SOC_VIRT kconfig option.

Signed-off-by: Anup Patel <apatel@ventanamicro.com>
---
 arch/riscv/Kconfig.socs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs
index 69774bb362d6..c16b32eeadff 100644
--- a/arch/riscv/Kconfig.socs
+++ b/arch/riscv/Kconfig.socs
@@ -35,6 +35,8 @@ config SOC_VIRT
 	select GOLDFISH
 	select RTC_DRV_GOLDFISH if RTC_CLASS
 	select SIFIVE_PLIC
+	select RISCV_APLIC
+	select RISCV_IMSIC
 	select PM_GENERIC_DOMAINS if PM
 	select PM_GENERIC_DOMAINS_OF if PM && OF
 	select RISCV_SBI_CPUIDLE if CPU_IDLE && RISCV_SBI
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* [PATCH 8/9] RISC-V: Select APLIC and IMSIC drivers for QEMU virt machine
@ 2022-11-11  4:42   ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-11  4:42 UTC (permalink / raw)
  To: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski
  Cc: Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree, Anup Patel

The QEMU virt machine supports APLIC and IMSIC emulation so let's
select APLIC and IMSIC drivers from SOC_VIRT kconfig option.

Signed-off-by: Anup Patel <apatel@ventanamicro.com>
---
 arch/riscv/Kconfig.socs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs
index 69774bb362d6..c16b32eeadff 100644
--- a/arch/riscv/Kconfig.socs
+++ b/arch/riscv/Kconfig.socs
@@ -35,6 +35,8 @@ config SOC_VIRT
 	select GOLDFISH
 	select RTC_DRV_GOLDFISH if RTC_CLASS
 	select SIFIVE_PLIC
+	select RISCV_APLIC
+	select RISCV_IMSIC
 	select PM_GENERIC_DOMAINS if PM
 	select PM_GENERIC_DOMAINS_OF if PM && OF
 	select RISCV_SBI_CPUIDLE if CPU_IDLE && RISCV_SBI
-- 
2.34.1


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

* [PATCH 9/9] MAINTAINERS: Add entry for RISC-V AIA drivers
  2022-11-11  4:41 ` Anup Patel
@ 2022-11-11  4:42   ` Anup Patel
  -1 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-11  4:42 UTC (permalink / raw)
  To: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski
  Cc: Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree, Anup Patel

Add myself as maintainer for RISC-V AIA drivers including the
RISC-V INTC driver which supports both AIA and non-AIA platforms.

Signed-off-by: Anup Patel <apatel@ventanamicro.com>
---
 MAINTAINERS | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 046ff06ff97f..8b998144a1bf 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17713,6 +17713,18 @@ F:	drivers/perf/riscv_pmu.c
 F:	drivers/perf/riscv_pmu_legacy.c
 F:	drivers/perf/riscv_pmu_sbi.c
 
+RISC-V AIA DRIVERS
+M:	Anup Patel <anup@brainfault.org>
+L:	linux-riscv@lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
+F:	Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
+F:	drivers/irqchip/irq-riscv-aplic.c
+F:	drivers/irqchip/irq-riscv-imsic.c
+F:	drivers/irqchip/irq-riscv-intc.c
+F:	include/linux/irqchip/riscv-aplic.h
+F:	include/linux/irqchip/riscv-imsic.h
+
 RISC-V ARCHITECTURE
 M:	Paul Walmsley <paul.walmsley@sifive.com>
 M:	Palmer Dabbelt <palmer@dabbelt.com>
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* [PATCH 9/9] MAINTAINERS: Add entry for RISC-V AIA drivers
@ 2022-11-11  4:42   ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-11  4:42 UTC (permalink / raw)
  To: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski
  Cc: Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree, Anup Patel

Add myself as maintainer for RISC-V AIA drivers including the
RISC-V INTC driver which supports both AIA and non-AIA platforms.

Signed-off-by: Anup Patel <apatel@ventanamicro.com>
---
 MAINTAINERS | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 046ff06ff97f..8b998144a1bf 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17713,6 +17713,18 @@ F:	drivers/perf/riscv_pmu.c
 F:	drivers/perf/riscv_pmu_legacy.c
 F:	drivers/perf/riscv_pmu_sbi.c
 
+RISC-V AIA DRIVERS
+M:	Anup Patel <anup@brainfault.org>
+L:	linux-riscv@lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
+F:	Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
+F:	drivers/irqchip/irq-riscv-aplic.c
+F:	drivers/irqchip/irq-riscv-imsic.c
+F:	drivers/irqchip/irq-riscv-intc.c
+F:	include/linux/irqchip/riscv-aplic.h
+F:	include/linux/irqchip/riscv-imsic.h
+
 RISC-V ARCHITECTURE
 M:	Paul Walmsley <paul.walmsley@sifive.com>
 M:	Palmer Dabbelt <palmer@dabbelt.com>
-- 
2.34.1


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

* Re: [PATCH 0/9] Linux RISC-V AIA Support
  2022-11-11  4:41 ` Anup Patel
@ 2022-11-11  9:07   ` Atish Patra
  -1 siblings, 0 replies; 96+ messages in thread
From: Atish Patra @ 2022-11-11  9:07 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Alistair Francis, Anup Patel,
	linux-riscv, linux-kernel, devicetree

On Thu, Nov 10, 2022 at 8:42 PM Anup Patel <apatel@ventanamicro.com> wrote:
>
> The RISC-V AIA specification is now frozen as-per the RISC-V international
> process. The latest frozen specifcation can be found at:
> https://github.com/riscv/riscv-aia/releases/download/1.0-RC1/riscv-interrupts-1.0-RC1.pdf
>
> At a high-level, the AIA specification adds three things:
> 1) AIA CSRs
>    - Improved local interrupt support
> 2) Incoming Message Signaled Interrupt Controller (IMSIC)
>    - Per-HART MSI controller
>    - Support MSI virtualization
>    - Support IPI along with virtualization
> 3) Advanced Platform-Level Interrupt Controller (APLIC)
>    - Wired interrupt controller
>    - In MSI-mode, converts wired interrupt into MSIs (i.e. MSI generator)
>    - In Direct-mode, injects external interrupts directly into HARTs
>
> For an overview of the AIA specification, refer the recent AIA virtualization
> talk at KVM Forum 2022:
> https://static.sched.com/hosted_files/kvmforum2022/a1/AIA_Virtualization_in_KVM_RISCV_final.pdf
> https://www.youtube.com/watch?v=r071dL8Z0yo
>
> This series adds required Linux irqchip drivers for AIA and it depends on
> the recent "RISC-V IPI Improvements".
> (Refer, https://lore.kernel.org/lkml/20221101143400.690000-1-apatel@ventanamicro.com/t/)
>
> To test this series, use QEMU v7.1 (or higher) and OpenSBI v1.1 (or higher).
>
> These patches can also be found in the riscv_aia_v1 branch at:
> https://github.com/avpatel/linux.git
>
> Anup Patel (9):
>   RISC-V: Add AIA related CSR defines
>   RISC-V: Detect AIA CSRs from ISA string
>   irqchip/riscv-intc: Add support for RISC-V AIA
>   dt-bindings: Add RISC-V incoming MSI controller bindings
>   irqchip: Add RISC-V incoming MSI controller driver
>   dt-bindings: Add RISC-V advanced PLIC bindings
>   irqchip: Add RISC-V advanced PLIC driver
>   RISC-V: Select APLIC and IMSIC drivers for QEMU virt machine
>   MAINTAINERS: Add entry for RISC-V AIA drivers
>
>  .../interrupt-controller/riscv,aplic.yaml     |  136 ++
>  .../interrupt-controller/riscv,imsic.yaml     |  174 +++
>  MAINTAINERS                                   |   12 +
>  arch/riscv/Kconfig.socs                       |    2 +
>  arch/riscv/include/asm/csr.h                  |   92 ++
>  arch/riscv/include/asm/hwcap.h                |    8 +
>  arch/riscv/kernel/cpu.c                       |    2 +
>  arch/riscv/kernel/cpufeature.c                |    2 +
>  drivers/irqchip/Kconfig                       |   32 +-
>  drivers/irqchip/Makefile                      |    2 +
>  drivers/irqchip/irq-riscv-aplic.c             |  656 +++++++++
>  drivers/irqchip/irq-riscv-imsic.c             | 1207 +++++++++++++++++
>  drivers/irqchip/irq-riscv-intc.c              |   37 +-
>  include/linux/irqchip/riscv-aplic.h           |  117 ++
>  include/linux/irqchip/riscv-imsic.h           |   92 ++
>  15 files changed, 2564 insertions(+), 7 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
>  create mode 100644 drivers/irqchip/irq-riscv-aplic.c
>  create mode 100644 drivers/irqchip/irq-riscv-imsic.c
>  create mode 100644 include/linux/irqchip/riscv-aplic.h
>  create mode 100644 include/linux/irqchip/riscv-imsic.h
>
> --
> 2.34.1
>

I am seeing the following boot failure with your branch and upstream
qemu (tag: v7.2.0-rc0).
It seems IPIs are probably not getting delivered after a point. I saw
that IPIs are delivered in the same path earlier (via gdb).

[    0.990152] NET: Registered PF_INET6 protocol family
[    1.004885] Segment Routing with IPv6
[    1.005385] In-situ OAM (IOAM) with IPv6
[    1.006371] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
[    1.011609] NET: Registered PF_PACKET protocol family
[    1.014877] 9pnet: Installing 9P2000 support
[    1.015603] Key type dns_resolver registered
[    1.026396] debug_vm_pgtable: [debug_vm_pgtable         ]:
Validating architecture page table helpers
[    1.144097] EXT4-fs (vda2): recovery complete
[    1.146345] EXT4-fs (vda2): mounted filesystem with ordered data
mode. Quota mode: disabled.
[    1.147020] VFS: Mounted root (ext4 filesystem) on device 254:2.
[    1.152238] devtmpfs: mounted
[    1.183914] Freeing unused kernel image (initmem) memory: 2176K
[    1.185898] Run /sbin/init as init process
[   29.412287] rcu: INFO: rcu_sched self-detected stall on CPU
[   29.412992] rcu:     3-....: (5250 ticks this GP)
idle=fc3c/1/0x4000000000000002 softirq=78/78 fqs=2230
[   29.413427]  (t=5251 jiffies g=-1047 q=3 ncpus=8)
[   29.414199] CPU: 3 PID: 1 Comm: init Not tainted
6.1.0-rc4-00024-g5b711f2d7b91 #198
[   29.414578] Hardware name: riscv-virtio,qemu (DT)
[   29.414896] epc : smp_call_function_many_cond+0x138/0x372
[   29.415157]  ra : smp_call_function_many_cond+0x154/0x372
[   29.415318] epc : ffffffff8008d2e6 ra : ffffffff8008d302 sp :
ff2000000004bb40
[   29.415512]  gp : ffffffff812e9eb0 tp : ff600000016d8000 t0 :
ff6000007ed88610
[   29.415686]  t1 : 00000000000000ff t2 : 0000000000000002 s0 :
ff2000000004bc00
[   29.415837]  s1 : ff6000007ed85448 a0 : 0000000000000007 a1 :
00000000000000f7
[   29.416008]  a2 : 0000000000000000 a3 : 0000000000000000 a4 :
ff6000007edd1780
[   29.416188]  a5 : 0000000000000001 a6 : ffffffff812eb1c0 a7 :
ff600000016d8000
[   29.416613]  s2 : ffffffff81323c30 s3 : ffffffff812e9964 s4 :
0000000000000000
[   29.416810]  s5 : 0000000000000000 s6 : ff6000007ed85440 s7 :
0000000000000038
[   29.416997]  s8 : 0000000000000003 s9 : ffffffff81323c30 s10:
ff6000007ed85448
[   29.417159]  s11: 0000000000000008 t3 : 00ffffffad08a000 t4 :
ff60000001613e0c
[   29.417331]  t5 : 0000000000000000 t6 : 00ffffffad177fff
[   29.417482] status: 0000000200000120 badaddr: 0000000000000000
cause: 8000000000000005
[   29.417939] [<ffffffff8008d590>] on_each_cpu_cond_mask+0x20/0x32
[   29.418179] [<ffffffff80008d32>] flush_icache_all+0x38/0x40
[   29.418324] [<ffffffff80008eb2>] flush_icache_pte+0x4a/0x7a
[   29.418442] [<ffffffff80139e00>] do_set_pte+0x132/0x192
[   29.418594] [<ffffffff8010b42e>] filemap_map_pages+0x178/0x3a0
[   29.418738] [<ffffffff8013ad00>] __handle_mm_fault+0x992/0xbac
[   29.418876] [<ffffffff8013afde>] handle_mm_fault+0xc4/0x1d4
[   29.419010] [<ffffffff80008372>] do_page_fault+0x120/0x326
[   29.419145] [<ffffffff800033e6>] ret_from_exception+0x0/0xc



-- 
Regards,
Atish

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

* Re: [PATCH 0/9] Linux RISC-V AIA Support
@ 2022-11-11  9:07   ` Atish Patra
  0 siblings, 0 replies; 96+ messages in thread
From: Atish Patra @ 2022-11-11  9:07 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Alistair Francis, Anup Patel,
	linux-riscv, linux-kernel, devicetree

On Thu, Nov 10, 2022 at 8:42 PM Anup Patel <apatel@ventanamicro.com> wrote:
>
> The RISC-V AIA specification is now frozen as-per the RISC-V international
> process. The latest frozen specifcation can be found at:
> https://github.com/riscv/riscv-aia/releases/download/1.0-RC1/riscv-interrupts-1.0-RC1.pdf
>
> At a high-level, the AIA specification adds three things:
> 1) AIA CSRs
>    - Improved local interrupt support
> 2) Incoming Message Signaled Interrupt Controller (IMSIC)
>    - Per-HART MSI controller
>    - Support MSI virtualization
>    - Support IPI along with virtualization
> 3) Advanced Platform-Level Interrupt Controller (APLIC)
>    - Wired interrupt controller
>    - In MSI-mode, converts wired interrupt into MSIs (i.e. MSI generator)
>    - In Direct-mode, injects external interrupts directly into HARTs
>
> For an overview of the AIA specification, refer the recent AIA virtualization
> talk at KVM Forum 2022:
> https://static.sched.com/hosted_files/kvmforum2022/a1/AIA_Virtualization_in_KVM_RISCV_final.pdf
> https://www.youtube.com/watch?v=r071dL8Z0yo
>
> This series adds required Linux irqchip drivers for AIA and it depends on
> the recent "RISC-V IPI Improvements".
> (Refer, https://lore.kernel.org/lkml/20221101143400.690000-1-apatel@ventanamicro.com/t/)
>
> To test this series, use QEMU v7.1 (or higher) and OpenSBI v1.1 (or higher).
>
> These patches can also be found in the riscv_aia_v1 branch at:
> https://github.com/avpatel/linux.git
>
> Anup Patel (9):
>   RISC-V: Add AIA related CSR defines
>   RISC-V: Detect AIA CSRs from ISA string
>   irqchip/riscv-intc: Add support for RISC-V AIA
>   dt-bindings: Add RISC-V incoming MSI controller bindings
>   irqchip: Add RISC-V incoming MSI controller driver
>   dt-bindings: Add RISC-V advanced PLIC bindings
>   irqchip: Add RISC-V advanced PLIC driver
>   RISC-V: Select APLIC and IMSIC drivers for QEMU virt machine
>   MAINTAINERS: Add entry for RISC-V AIA drivers
>
>  .../interrupt-controller/riscv,aplic.yaml     |  136 ++
>  .../interrupt-controller/riscv,imsic.yaml     |  174 +++
>  MAINTAINERS                                   |   12 +
>  arch/riscv/Kconfig.socs                       |    2 +
>  arch/riscv/include/asm/csr.h                  |   92 ++
>  arch/riscv/include/asm/hwcap.h                |    8 +
>  arch/riscv/kernel/cpu.c                       |    2 +
>  arch/riscv/kernel/cpufeature.c                |    2 +
>  drivers/irqchip/Kconfig                       |   32 +-
>  drivers/irqchip/Makefile                      |    2 +
>  drivers/irqchip/irq-riscv-aplic.c             |  656 +++++++++
>  drivers/irqchip/irq-riscv-imsic.c             | 1207 +++++++++++++++++
>  drivers/irqchip/irq-riscv-intc.c              |   37 +-
>  include/linux/irqchip/riscv-aplic.h           |  117 ++
>  include/linux/irqchip/riscv-imsic.h           |   92 ++
>  15 files changed, 2564 insertions(+), 7 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
>  create mode 100644 drivers/irqchip/irq-riscv-aplic.c
>  create mode 100644 drivers/irqchip/irq-riscv-imsic.c
>  create mode 100644 include/linux/irqchip/riscv-aplic.h
>  create mode 100644 include/linux/irqchip/riscv-imsic.h
>
> --
> 2.34.1
>

I am seeing the following boot failure with your branch and upstream
qemu (tag: v7.2.0-rc0).
It seems IPIs are probably not getting delivered after a point. I saw
that IPIs are delivered in the same path earlier (via gdb).

[    0.990152] NET: Registered PF_INET6 protocol family
[    1.004885] Segment Routing with IPv6
[    1.005385] In-situ OAM (IOAM) with IPv6
[    1.006371] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
[    1.011609] NET: Registered PF_PACKET protocol family
[    1.014877] 9pnet: Installing 9P2000 support
[    1.015603] Key type dns_resolver registered
[    1.026396] debug_vm_pgtable: [debug_vm_pgtable         ]:
Validating architecture page table helpers
[    1.144097] EXT4-fs (vda2): recovery complete
[    1.146345] EXT4-fs (vda2): mounted filesystem with ordered data
mode. Quota mode: disabled.
[    1.147020] VFS: Mounted root (ext4 filesystem) on device 254:2.
[    1.152238] devtmpfs: mounted
[    1.183914] Freeing unused kernel image (initmem) memory: 2176K
[    1.185898] Run /sbin/init as init process
[   29.412287] rcu: INFO: rcu_sched self-detected stall on CPU
[   29.412992] rcu:     3-....: (5250 ticks this GP)
idle=fc3c/1/0x4000000000000002 softirq=78/78 fqs=2230
[   29.413427]  (t=5251 jiffies g=-1047 q=3 ncpus=8)
[   29.414199] CPU: 3 PID: 1 Comm: init Not tainted
6.1.0-rc4-00024-g5b711f2d7b91 #198
[   29.414578] Hardware name: riscv-virtio,qemu (DT)
[   29.414896] epc : smp_call_function_many_cond+0x138/0x372
[   29.415157]  ra : smp_call_function_many_cond+0x154/0x372
[   29.415318] epc : ffffffff8008d2e6 ra : ffffffff8008d302 sp :
ff2000000004bb40
[   29.415512]  gp : ffffffff812e9eb0 tp : ff600000016d8000 t0 :
ff6000007ed88610
[   29.415686]  t1 : 00000000000000ff t2 : 0000000000000002 s0 :
ff2000000004bc00
[   29.415837]  s1 : ff6000007ed85448 a0 : 0000000000000007 a1 :
00000000000000f7
[   29.416008]  a2 : 0000000000000000 a3 : 0000000000000000 a4 :
ff6000007edd1780
[   29.416188]  a5 : 0000000000000001 a6 : ffffffff812eb1c0 a7 :
ff600000016d8000
[   29.416613]  s2 : ffffffff81323c30 s3 : ffffffff812e9964 s4 :
0000000000000000
[   29.416810]  s5 : 0000000000000000 s6 : ff6000007ed85440 s7 :
0000000000000038
[   29.416997]  s8 : 0000000000000003 s9 : ffffffff81323c30 s10:
ff6000007ed85448
[   29.417159]  s11: 0000000000000008 t3 : 00ffffffad08a000 t4 :
ff60000001613e0c
[   29.417331]  t5 : 0000000000000000 t6 : 00ffffffad177fff
[   29.417482] status: 0000000200000120 badaddr: 0000000000000000
cause: 8000000000000005
[   29.417939] [<ffffffff8008d590>] on_each_cpu_cond_mask+0x20/0x32
[   29.418179] [<ffffffff80008d32>] flush_icache_all+0x38/0x40
[   29.418324] [<ffffffff80008eb2>] flush_icache_pte+0x4a/0x7a
[   29.418442] [<ffffffff80139e00>] do_set_pte+0x132/0x192
[   29.418594] [<ffffffff8010b42e>] filemap_map_pages+0x178/0x3a0
[   29.418738] [<ffffffff8013ad00>] __handle_mm_fault+0x992/0xbac
[   29.418876] [<ffffffff8013afde>] handle_mm_fault+0xc4/0x1d4
[   29.419010] [<ffffffff80008372>] do_page_fault+0x120/0x326
[   29.419145] [<ffffffff800033e6>] ret_from_exception+0x0/0xc



-- 
Regards,
Atish

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
  2022-11-11  4:42   ` Anup Patel
@ 2022-11-11  9:11     ` Atish Patra
  -1 siblings, 0 replies; 96+ messages in thread
From: Atish Patra @ 2022-11-11  9:11 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Alistair Francis, Anup Patel,
	linux-riscv, linux-kernel, devicetree

On Thu, Nov 10, 2022 at 8:43 PM Anup Patel <apatel@ventanamicro.com> wrote:
>
> We add DT bindings document for RISC-V incoming MSI controller (IMSIC)
> defined by the RISC-V advanced interrupt architecture (AIA) specification.
>
> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> ---
>  .../interrupt-controller/riscv,imsic.yaml     | 174 ++++++++++++++++++
>  1 file changed, 174 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
>
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> new file mode 100644
> index 000000000000..05106eb1955e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> @@ -0,0 +1,174 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/interrupt-controller/riscv,imsic.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: RISC-V Incoming MSI Controller (IMSIC)
> +
> +maintainers:
> +  - Anup Patel <anup@brainfault.org>
> +
> +description:
> +  The RISC-V advanced interrupt architecture (AIA) defines a per-CPU incoming
> +  MSI controller (IMSIC) for handling MSIs in a RISC-V platform. The RISC-V
> +  AIA specification can be found at https://github.com/riscv/riscv-aia.
> +
> +  The IMSIC is a per-CPU (or per-HART) device with separate interrupt file
> +  for each privilege level (machine or supervisor). The configuration of
> +  a IMSIC interrupt file is done using AIA CSRs and it also has a 4KB MMIO
> +  space to receive MSIs from devices. Each IMSIC interrupt file supports a
> +  fixed number of interrupt identities (to distinguish MSIs from devices)
> +  which is same for given privilege level across CPUs (or HARTs).
> +
> +  The arrangement of IMSIC interrupt files in MMIO space of a RISC-V platform
> +  follows a particular scheme defined by the RISC-V AIA specification. A IMSIC
> +  group is a set of IMSIC interrupt files co-located in MMIO space and we can
> +  have multiple IMSIC groups (i.e. clusters, sockets, chiplets, etc) in a
> +  RISC-V platform. The MSI target address of a IMSIC interrupt file at given
> +  privilege level (machine or supervisor) encodes group index, HART index,
> +  and guest index (shown below).
> +
> +  XLEN-1           >=24                                 12    0
> +  |                  |                                  |     |
> +  -------------------------------------------------------------
> +  |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
> +  -------------------------------------------------------------
> +
> +  The device tree of a RISC-V platform will have one IMSIC device tree node
> +  for each privilege level (machine or supervisor) which collectively describe
> +  IMSIC interrupt files at that privilege level across CPUs (or HARTs).
> +
> +allOf:
> +  - $ref: /schemas/interrupt-controller.yaml#
> +
> +properties:
> +  compatible:
> +    items:
> +      - enum:
> +          - vendor,chip-imsics
> +      - const: riscv,imsics
> +
> +  reg:
> +    minItems: 1
> +    maxItems: 128
> +    description:
> +      Base address of each IMSIC group.
> +
> +  interrupt-controller: true
> +
> +  "#interrupt-cells":
> +    const: 0
> +
> +  msi-controller: true
> +
> +  interrupts-extended:
> +    minItems: 1
> +    maxItems: 32768
> +    description:
> +      This property represents the set of CPUs (or HARTs) for which given
> +      device tree node describes the IMSIC interrupt files. Each node pointed
> +      to should be a riscv,cpu-intc node, which has a riscv node (i.e. RISC-V
> +      HART) as parent.
> +
> +  riscv,num-ids:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 63
> +    maximum: 2047
> +    description:
> +      Specifies how many interrupt identities are supported by IMSIC interrupt
> +      file.
> +
> +  riscv,num-guest-ids:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 63
> +    maximum: 2047
> +    description:
> +      Specifies how many interrupt identities are supported by IMSIC guest
> +      interrupt file. When not specified the number of interrupt identities
> +      supported by IMSIC guest file is assumed to be same as specified by
> +      the riscv,num-ids property.
> +
> +  riscv,slow-ipi:
> +    type: boolean
> +    description:
> +      The presence of this property implies that software interrupts (i.e.
> +      IPIs) using IMSIC software injected MSIs is slower compared to other
> +      software interrupt mechanisms (such as SBI IPI) on the underlying
> +      RISC-V platform.
> +
> +  riscv,guest-index-bits:
> +    minimum: 0
> +    maximum: 7
> +    description:
> +      Specifies number of guest index bits in the MSI target address. When
> +      not specified it is assumed to be 0.
> +
> +  riscv,hart-index-bits:
> +    minimum: 0
> +    maximum: 15
> +    description:
> +      Specifies number of HART index bits in the MSI target address. When
> +      not specified it is estimated based on the interrupts-extended property.
> +
> +  riscv,group-index-bits:
> +    minimum: 0
> +    maximum: 7
> +    description:
> +      Specifies number of group index bits in the MSI target address. When
> +      not specified it is assumed to be 0.
> +
> +  riscv,group-index-shift:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 24
> +    maximum: 55
> +    description:
> +      Specifies the least significant bit of the group index bits in the
> +      MSI target address. When not specified it is assumed to be 24.
> +
> +additionalProperties: false
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupt-controller
> +  - msi-controller
> +  - interrupts-extended
> +  - riscv,num-ids
> +
> +examples:
> +  - |
> +    // Example 1 (Machine-level IMSIC files with just one group):
> +
> +    imsic_mlevel: interrupt-controller@24000000 {
> +      compatible = "vendor,chip-imsics", "riscv,imsics";
> +      interrupts-extended = <&cpu1_intc 11>,
> +                            <&cpu2_intc 11>,
> +                            <&cpu3_intc 11>,
> +                            <&cpu4_intc 11>;
> +      reg = <0x28000000 0x4000>;

nit: /s/0x28000000/24000000

> +      interrupt-controller;
> +      #interrupt-cells = <0>;
> +      msi-controller;
> +      riscv,num-ids = <127>;
> +    };
> +
> +  - |
> +    // Example 2 (Supervisor-level IMSIC files with two groups):
> +
> +    imsic_slevel: interrupt-controller@28000000 {
> +      compatible = "vendor,chip-imsics", "riscv,imsics";
> +      interrupts-extended = <&cpu1_intc 9>,
> +                            <&cpu2_intc 9>,
> +                            <&cpu3_intc 9>,
> +                            <&cpu4_intc 9>;
> +      reg = <0x28000000 0x2000>, /* Group0 IMSICs */
> +            <0x29000000 0x2000>; /* Group1 IMSICs */
> +      interrupt-controller;
> +      #interrupt-cells = <0>;
> +      msi-controller;
> +      riscv,num-ids = <127>;
> +      riscv,group-index-bits = <1>;
> +      riscv,group-index-shift = <24>;
> +    };
> +...
> --
> 2.34.1
>


-- 
Regards,
Atish

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
@ 2022-11-11  9:11     ` Atish Patra
  0 siblings, 0 replies; 96+ messages in thread
From: Atish Patra @ 2022-11-11  9:11 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Alistair Francis, Anup Patel,
	linux-riscv, linux-kernel, devicetree

On Thu, Nov 10, 2022 at 8:43 PM Anup Patel <apatel@ventanamicro.com> wrote:
>
> We add DT bindings document for RISC-V incoming MSI controller (IMSIC)
> defined by the RISC-V advanced interrupt architecture (AIA) specification.
>
> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> ---
>  .../interrupt-controller/riscv,imsic.yaml     | 174 ++++++++++++++++++
>  1 file changed, 174 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
>
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> new file mode 100644
> index 000000000000..05106eb1955e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> @@ -0,0 +1,174 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/interrupt-controller/riscv,imsic.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: RISC-V Incoming MSI Controller (IMSIC)
> +
> +maintainers:
> +  - Anup Patel <anup@brainfault.org>
> +
> +description:
> +  The RISC-V advanced interrupt architecture (AIA) defines a per-CPU incoming
> +  MSI controller (IMSIC) for handling MSIs in a RISC-V platform. The RISC-V
> +  AIA specification can be found at https://github.com/riscv/riscv-aia.
> +
> +  The IMSIC is a per-CPU (or per-HART) device with separate interrupt file
> +  for each privilege level (machine or supervisor). The configuration of
> +  a IMSIC interrupt file is done using AIA CSRs and it also has a 4KB MMIO
> +  space to receive MSIs from devices. Each IMSIC interrupt file supports a
> +  fixed number of interrupt identities (to distinguish MSIs from devices)
> +  which is same for given privilege level across CPUs (or HARTs).
> +
> +  The arrangement of IMSIC interrupt files in MMIO space of a RISC-V platform
> +  follows a particular scheme defined by the RISC-V AIA specification. A IMSIC
> +  group is a set of IMSIC interrupt files co-located in MMIO space and we can
> +  have multiple IMSIC groups (i.e. clusters, sockets, chiplets, etc) in a
> +  RISC-V platform. The MSI target address of a IMSIC interrupt file at given
> +  privilege level (machine or supervisor) encodes group index, HART index,
> +  and guest index (shown below).
> +
> +  XLEN-1           >=24                                 12    0
> +  |                  |                                  |     |
> +  -------------------------------------------------------------
> +  |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
> +  -------------------------------------------------------------
> +
> +  The device tree of a RISC-V platform will have one IMSIC device tree node
> +  for each privilege level (machine or supervisor) which collectively describe
> +  IMSIC interrupt files at that privilege level across CPUs (or HARTs).
> +
> +allOf:
> +  - $ref: /schemas/interrupt-controller.yaml#
> +
> +properties:
> +  compatible:
> +    items:
> +      - enum:
> +          - vendor,chip-imsics
> +      - const: riscv,imsics
> +
> +  reg:
> +    minItems: 1
> +    maxItems: 128
> +    description:
> +      Base address of each IMSIC group.
> +
> +  interrupt-controller: true
> +
> +  "#interrupt-cells":
> +    const: 0
> +
> +  msi-controller: true
> +
> +  interrupts-extended:
> +    minItems: 1
> +    maxItems: 32768
> +    description:
> +      This property represents the set of CPUs (or HARTs) for which given
> +      device tree node describes the IMSIC interrupt files. Each node pointed
> +      to should be a riscv,cpu-intc node, which has a riscv node (i.e. RISC-V
> +      HART) as parent.
> +
> +  riscv,num-ids:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 63
> +    maximum: 2047
> +    description:
> +      Specifies how many interrupt identities are supported by IMSIC interrupt
> +      file.
> +
> +  riscv,num-guest-ids:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 63
> +    maximum: 2047
> +    description:
> +      Specifies how many interrupt identities are supported by IMSIC guest
> +      interrupt file. When not specified the number of interrupt identities
> +      supported by IMSIC guest file is assumed to be same as specified by
> +      the riscv,num-ids property.
> +
> +  riscv,slow-ipi:
> +    type: boolean
> +    description:
> +      The presence of this property implies that software interrupts (i.e.
> +      IPIs) using IMSIC software injected MSIs is slower compared to other
> +      software interrupt mechanisms (such as SBI IPI) on the underlying
> +      RISC-V platform.
> +
> +  riscv,guest-index-bits:
> +    minimum: 0
> +    maximum: 7
> +    description:
> +      Specifies number of guest index bits in the MSI target address. When
> +      not specified it is assumed to be 0.
> +
> +  riscv,hart-index-bits:
> +    minimum: 0
> +    maximum: 15
> +    description:
> +      Specifies number of HART index bits in the MSI target address. When
> +      not specified it is estimated based on the interrupts-extended property.
> +
> +  riscv,group-index-bits:
> +    minimum: 0
> +    maximum: 7
> +    description:
> +      Specifies number of group index bits in the MSI target address. When
> +      not specified it is assumed to be 0.
> +
> +  riscv,group-index-shift:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 24
> +    maximum: 55
> +    description:
> +      Specifies the least significant bit of the group index bits in the
> +      MSI target address. When not specified it is assumed to be 24.
> +
> +additionalProperties: false
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupt-controller
> +  - msi-controller
> +  - interrupts-extended
> +  - riscv,num-ids
> +
> +examples:
> +  - |
> +    // Example 1 (Machine-level IMSIC files with just one group):
> +
> +    imsic_mlevel: interrupt-controller@24000000 {
> +      compatible = "vendor,chip-imsics", "riscv,imsics";
> +      interrupts-extended = <&cpu1_intc 11>,
> +                            <&cpu2_intc 11>,
> +                            <&cpu3_intc 11>,
> +                            <&cpu4_intc 11>;
> +      reg = <0x28000000 0x4000>;

nit: /s/0x28000000/24000000

> +      interrupt-controller;
> +      #interrupt-cells = <0>;
> +      msi-controller;
> +      riscv,num-ids = <127>;
> +    };
> +
> +  - |
> +    // Example 2 (Supervisor-level IMSIC files with two groups):
> +
> +    imsic_slevel: interrupt-controller@28000000 {
> +      compatible = "vendor,chip-imsics", "riscv,imsics";
> +      interrupts-extended = <&cpu1_intc 9>,
> +                            <&cpu2_intc 9>,
> +                            <&cpu3_intc 9>,
> +                            <&cpu4_intc 9>;
> +      reg = <0x28000000 0x2000>, /* Group0 IMSICs */
> +            <0x29000000 0x2000>; /* Group1 IMSICs */
> +      interrupt-controller;
> +      #interrupt-cells = <0>;
> +      msi-controller;
> +      riscv,num-ids = <127>;
> +      riscv,group-index-bits = <1>;
> +      riscv,group-index-shift = <24>;
> +    };
> +...
> --
> 2.34.1
>


-- 
Regards,
Atish

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 0/9] Linux RISC-V AIA Support
  2022-11-11  9:07   ` Atish Patra
@ 2022-11-11  9:13     ` Atish Patra
  -1 siblings, 0 replies; 96+ messages in thread
From: Atish Patra @ 2022-11-11  9:13 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Alistair Francis, Anup Patel,
	linux-riscv, linux-kernel, devicetree

On Fri, Nov 11, 2022 at 1:07 AM Atish Patra <atishp@atishpatra.org> wrote:
>
> On Thu, Nov 10, 2022 at 8:42 PM Anup Patel <apatel@ventanamicro.com> wrote:
> >
> > The RISC-V AIA specification is now frozen as-per the RISC-V international
> > process. The latest frozen specifcation can be found at:
> > https://github.com/riscv/riscv-aia/releases/download/1.0-RC1/riscv-interrupts-1.0-RC1.pdf
> >
> > At a high-level, the AIA specification adds three things:
> > 1) AIA CSRs
> >    - Improved local interrupt support
> > 2) Incoming Message Signaled Interrupt Controller (IMSIC)
> >    - Per-HART MSI controller
> >    - Support MSI virtualization
> >    - Support IPI along with virtualization
> > 3) Advanced Platform-Level Interrupt Controller (APLIC)
> >    - Wired interrupt controller
> >    - In MSI-mode, converts wired interrupt into MSIs (i.e. MSI generator)
> >    - In Direct-mode, injects external interrupts directly into HARTs
> >
> > For an overview of the AIA specification, refer the recent AIA virtualization
> > talk at KVM Forum 2022:
> > https://static.sched.com/hosted_files/kvmforum2022/a1/AIA_Virtualization_in_KVM_RISCV_final.pdf
> > https://www.youtube.com/watch?v=r071dL8Z0yo
> >
> > This series adds required Linux irqchip drivers for AIA and it depends on
> > the recent "RISC-V IPI Improvements".
> > (Refer, https://lore.kernel.org/lkml/20221101143400.690000-1-apatel@ventanamicro.com/t/)
> >
> > To test this series, use QEMU v7.1 (or higher) and OpenSBI v1.1 (or higher).
> >
> > These patches can also be found in the riscv_aia_v1 branch at:
> > https://github.com/avpatel/linux.git
> >
> > Anup Patel (9):
> >   RISC-V: Add AIA related CSR defines
> >   RISC-V: Detect AIA CSRs from ISA string
> >   irqchip/riscv-intc: Add support for RISC-V AIA
> >   dt-bindings: Add RISC-V incoming MSI controller bindings
> >   irqchip: Add RISC-V incoming MSI controller driver
> >   dt-bindings: Add RISC-V advanced PLIC bindings
> >   irqchip: Add RISC-V advanced PLIC driver
> >   RISC-V: Select APLIC and IMSIC drivers for QEMU virt machine
> >   MAINTAINERS: Add entry for RISC-V AIA drivers
> >
> >  .../interrupt-controller/riscv,aplic.yaml     |  136 ++
> >  .../interrupt-controller/riscv,imsic.yaml     |  174 +++
> >  MAINTAINERS                                   |   12 +
> >  arch/riscv/Kconfig.socs                       |    2 +
> >  arch/riscv/include/asm/csr.h                  |   92 ++
> >  arch/riscv/include/asm/hwcap.h                |    8 +
> >  arch/riscv/kernel/cpu.c                       |    2 +
> >  arch/riscv/kernel/cpufeature.c                |    2 +
> >  drivers/irqchip/Kconfig                       |   32 +-
> >  drivers/irqchip/Makefile                      |    2 +
> >  drivers/irqchip/irq-riscv-aplic.c             |  656 +++++++++
> >  drivers/irqchip/irq-riscv-imsic.c             | 1207 +++++++++++++++++
> >  drivers/irqchip/irq-riscv-intc.c              |   37 +-
> >  include/linux/irqchip/riscv-aplic.h           |  117 ++
> >  include/linux/irqchip/riscv-imsic.h           |   92 ++
> >  15 files changed, 2564 insertions(+), 7 deletions(-)
> >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> >  create mode 100644 drivers/irqchip/irq-riscv-aplic.c
> >  create mode 100644 drivers/irqchip/irq-riscv-imsic.c
> >  create mode 100644 include/linux/irqchip/riscv-aplic.h
> >  create mode 100644 include/linux/irqchip/riscv-imsic.h
> >
> > --
> > 2.34.1
> >
>
> I am seeing the following boot failure with your branch and upstream
> qemu (tag: v7.2.0-rc0).
> It seems IPIs are probably not getting delivered after a point. I saw
> that IPIs are delivered in the same path earlier (via gdb).
>
> [    0.990152] NET: Registered PF_INET6 protocol family
> [    1.004885] Segment Routing with IPv6
> [    1.005385] In-situ OAM (IOAM) with IPv6
> [    1.006371] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
> [    1.011609] NET: Registered PF_PACKET protocol family
> [    1.014877] 9pnet: Installing 9P2000 support
> [    1.015603] Key type dns_resolver registered
> [    1.026396] debug_vm_pgtable: [debug_vm_pgtable         ]:
> Validating architecture page table helpers
> [    1.144097] EXT4-fs (vda2): recovery complete
> [    1.146345] EXT4-fs (vda2): mounted filesystem with ordered data
> mode. Quota mode: disabled.
> [    1.147020] VFS: Mounted root (ext4 filesystem) on device 254:2.
> [    1.152238] devtmpfs: mounted
> [    1.183914] Freeing unused kernel image (initmem) memory: 2176K
> [    1.185898] Run /sbin/init as init process
> [   29.412287] rcu: INFO: rcu_sched self-detected stall on CPU
> [   29.412992] rcu:     3-....: (5250 ticks this GP)
> idle=fc3c/1/0x4000000000000002 softirq=78/78 fqs=2230
> [   29.413427]  (t=5251 jiffies g=-1047 q=3 ncpus=8)
> [   29.414199] CPU: 3 PID: 1 Comm: init Not tainted
> 6.1.0-rc4-00024-g5b711f2d7b91 #198
> [   29.414578] Hardware name: riscv-virtio,qemu (DT)
> [   29.414896] epc : smp_call_function_many_cond+0x138/0x372
> [   29.415157]  ra : smp_call_function_many_cond+0x154/0x372
> [   29.415318] epc : ffffffff8008d2e6 ra : ffffffff8008d302 sp :
> ff2000000004bb40
> [   29.415512]  gp : ffffffff812e9eb0 tp : ff600000016d8000 t0 :
> ff6000007ed88610
> [   29.415686]  t1 : 00000000000000ff t2 : 0000000000000002 s0 :
> ff2000000004bc00
> [   29.415837]  s1 : ff6000007ed85448 a0 : 0000000000000007 a1 :
> 00000000000000f7
> [   29.416008]  a2 : 0000000000000000 a3 : 0000000000000000 a4 :
> ff6000007edd1780
> [   29.416188]  a5 : 0000000000000001 a6 : ffffffff812eb1c0 a7 :
> ff600000016d8000
> [   29.416613]  s2 : ffffffff81323c30 s3 : ffffffff812e9964 s4 :
> 0000000000000000
> [   29.416810]  s5 : 0000000000000000 s6 : ff6000007ed85440 s7 :
> 0000000000000038
> [   29.416997]  s8 : 0000000000000003 s9 : ffffffff81323c30 s10:
> ff6000007ed85448
> [   29.417159]  s11: 0000000000000008 t3 : 00ffffffad08a000 t4 :
> ff60000001613e0c
> [   29.417331]  t5 : 0000000000000000 t6 : 00ffffffad177fff
> [   29.417482] status: 0000000200000120 badaddr: 0000000000000000
> cause: 8000000000000005
> [   29.417939] [<ffffffff8008d590>] on_each_cpu_cond_mask+0x20/0x32
> [   29.418179] [<ffffffff80008d32>] flush_icache_all+0x38/0x40
> [   29.418324] [<ffffffff80008eb2>] flush_icache_pte+0x4a/0x7a
> [   29.418442] [<ffffffff80139e00>] do_set_pte+0x132/0x192
> [   29.418594] [<ffffffff8010b42e>] filemap_map_pages+0x178/0x3a0
> [   29.418738] [<ffffffff8013ad00>] __handle_mm_fault+0x992/0xbac
> [   29.418876] [<ffffffff8013afde>] handle_mm_fault+0xc4/0x1d4
> [   29.419010] [<ffffffff80008372>] do_page_fault+0x120/0x326
> [   29.419145] [<ffffffff800033e6>] ret_from_exception+0x0/0xc
>
>

Sorry. I forgot to mention that this happens while booting Fedora on
an 8 cpu virt machine.
I am yet to reproduce this issue for 4 or 2 cpus.

>
> --
> Regards,
> Atish



-- 
Regards,
Atish

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

* Re: [PATCH 0/9] Linux RISC-V AIA Support
@ 2022-11-11  9:13     ` Atish Patra
  0 siblings, 0 replies; 96+ messages in thread
From: Atish Patra @ 2022-11-11  9:13 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Alistair Francis, Anup Patel,
	linux-riscv, linux-kernel, devicetree

On Fri, Nov 11, 2022 at 1:07 AM Atish Patra <atishp@atishpatra.org> wrote:
>
> On Thu, Nov 10, 2022 at 8:42 PM Anup Patel <apatel@ventanamicro.com> wrote:
> >
> > The RISC-V AIA specification is now frozen as-per the RISC-V international
> > process. The latest frozen specifcation can be found at:
> > https://github.com/riscv/riscv-aia/releases/download/1.0-RC1/riscv-interrupts-1.0-RC1.pdf
> >
> > At a high-level, the AIA specification adds three things:
> > 1) AIA CSRs
> >    - Improved local interrupt support
> > 2) Incoming Message Signaled Interrupt Controller (IMSIC)
> >    - Per-HART MSI controller
> >    - Support MSI virtualization
> >    - Support IPI along with virtualization
> > 3) Advanced Platform-Level Interrupt Controller (APLIC)
> >    - Wired interrupt controller
> >    - In MSI-mode, converts wired interrupt into MSIs (i.e. MSI generator)
> >    - In Direct-mode, injects external interrupts directly into HARTs
> >
> > For an overview of the AIA specification, refer the recent AIA virtualization
> > talk at KVM Forum 2022:
> > https://static.sched.com/hosted_files/kvmforum2022/a1/AIA_Virtualization_in_KVM_RISCV_final.pdf
> > https://www.youtube.com/watch?v=r071dL8Z0yo
> >
> > This series adds required Linux irqchip drivers for AIA and it depends on
> > the recent "RISC-V IPI Improvements".
> > (Refer, https://lore.kernel.org/lkml/20221101143400.690000-1-apatel@ventanamicro.com/t/)
> >
> > To test this series, use QEMU v7.1 (or higher) and OpenSBI v1.1 (or higher).
> >
> > These patches can also be found in the riscv_aia_v1 branch at:
> > https://github.com/avpatel/linux.git
> >
> > Anup Patel (9):
> >   RISC-V: Add AIA related CSR defines
> >   RISC-V: Detect AIA CSRs from ISA string
> >   irqchip/riscv-intc: Add support for RISC-V AIA
> >   dt-bindings: Add RISC-V incoming MSI controller bindings
> >   irqchip: Add RISC-V incoming MSI controller driver
> >   dt-bindings: Add RISC-V advanced PLIC bindings
> >   irqchip: Add RISC-V advanced PLIC driver
> >   RISC-V: Select APLIC and IMSIC drivers for QEMU virt machine
> >   MAINTAINERS: Add entry for RISC-V AIA drivers
> >
> >  .../interrupt-controller/riscv,aplic.yaml     |  136 ++
> >  .../interrupt-controller/riscv,imsic.yaml     |  174 +++
> >  MAINTAINERS                                   |   12 +
> >  arch/riscv/Kconfig.socs                       |    2 +
> >  arch/riscv/include/asm/csr.h                  |   92 ++
> >  arch/riscv/include/asm/hwcap.h                |    8 +
> >  arch/riscv/kernel/cpu.c                       |    2 +
> >  arch/riscv/kernel/cpufeature.c                |    2 +
> >  drivers/irqchip/Kconfig                       |   32 +-
> >  drivers/irqchip/Makefile                      |    2 +
> >  drivers/irqchip/irq-riscv-aplic.c             |  656 +++++++++
> >  drivers/irqchip/irq-riscv-imsic.c             | 1207 +++++++++++++++++
> >  drivers/irqchip/irq-riscv-intc.c              |   37 +-
> >  include/linux/irqchip/riscv-aplic.h           |  117 ++
> >  include/linux/irqchip/riscv-imsic.h           |   92 ++
> >  15 files changed, 2564 insertions(+), 7 deletions(-)
> >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> >  create mode 100644 drivers/irqchip/irq-riscv-aplic.c
> >  create mode 100644 drivers/irqchip/irq-riscv-imsic.c
> >  create mode 100644 include/linux/irqchip/riscv-aplic.h
> >  create mode 100644 include/linux/irqchip/riscv-imsic.h
> >
> > --
> > 2.34.1
> >
>
> I am seeing the following boot failure with your branch and upstream
> qemu (tag: v7.2.0-rc0).
> It seems IPIs are probably not getting delivered after a point. I saw
> that IPIs are delivered in the same path earlier (via gdb).
>
> [    0.990152] NET: Registered PF_INET6 protocol family
> [    1.004885] Segment Routing with IPv6
> [    1.005385] In-situ OAM (IOAM) with IPv6
> [    1.006371] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
> [    1.011609] NET: Registered PF_PACKET protocol family
> [    1.014877] 9pnet: Installing 9P2000 support
> [    1.015603] Key type dns_resolver registered
> [    1.026396] debug_vm_pgtable: [debug_vm_pgtable         ]:
> Validating architecture page table helpers
> [    1.144097] EXT4-fs (vda2): recovery complete
> [    1.146345] EXT4-fs (vda2): mounted filesystem with ordered data
> mode. Quota mode: disabled.
> [    1.147020] VFS: Mounted root (ext4 filesystem) on device 254:2.
> [    1.152238] devtmpfs: mounted
> [    1.183914] Freeing unused kernel image (initmem) memory: 2176K
> [    1.185898] Run /sbin/init as init process
> [   29.412287] rcu: INFO: rcu_sched self-detected stall on CPU
> [   29.412992] rcu:     3-....: (5250 ticks this GP)
> idle=fc3c/1/0x4000000000000002 softirq=78/78 fqs=2230
> [   29.413427]  (t=5251 jiffies g=-1047 q=3 ncpus=8)
> [   29.414199] CPU: 3 PID: 1 Comm: init Not tainted
> 6.1.0-rc4-00024-g5b711f2d7b91 #198
> [   29.414578] Hardware name: riscv-virtio,qemu (DT)
> [   29.414896] epc : smp_call_function_many_cond+0x138/0x372
> [   29.415157]  ra : smp_call_function_many_cond+0x154/0x372
> [   29.415318] epc : ffffffff8008d2e6 ra : ffffffff8008d302 sp :
> ff2000000004bb40
> [   29.415512]  gp : ffffffff812e9eb0 tp : ff600000016d8000 t0 :
> ff6000007ed88610
> [   29.415686]  t1 : 00000000000000ff t2 : 0000000000000002 s0 :
> ff2000000004bc00
> [   29.415837]  s1 : ff6000007ed85448 a0 : 0000000000000007 a1 :
> 00000000000000f7
> [   29.416008]  a2 : 0000000000000000 a3 : 0000000000000000 a4 :
> ff6000007edd1780
> [   29.416188]  a5 : 0000000000000001 a6 : ffffffff812eb1c0 a7 :
> ff600000016d8000
> [   29.416613]  s2 : ffffffff81323c30 s3 : ffffffff812e9964 s4 :
> 0000000000000000
> [   29.416810]  s5 : 0000000000000000 s6 : ff6000007ed85440 s7 :
> 0000000000000038
> [   29.416997]  s8 : 0000000000000003 s9 : ffffffff81323c30 s10:
> ff6000007ed85448
> [   29.417159]  s11: 0000000000000008 t3 : 00ffffffad08a000 t4 :
> ff60000001613e0c
> [   29.417331]  t5 : 0000000000000000 t6 : 00ffffffad177fff
> [   29.417482] status: 0000000200000120 badaddr: 0000000000000000
> cause: 8000000000000005
> [   29.417939] [<ffffffff8008d590>] on_each_cpu_cond_mask+0x20/0x32
> [   29.418179] [<ffffffff80008d32>] flush_icache_all+0x38/0x40
> [   29.418324] [<ffffffff80008eb2>] flush_icache_pte+0x4a/0x7a
> [   29.418442] [<ffffffff80139e00>] do_set_pte+0x132/0x192
> [   29.418594] [<ffffffff8010b42e>] filemap_map_pages+0x178/0x3a0
> [   29.418738] [<ffffffff8013ad00>] __handle_mm_fault+0x992/0xbac
> [   29.418876] [<ffffffff8013afde>] handle_mm_fault+0xc4/0x1d4
> [   29.419010] [<ffffffff80008372>] do_page_fault+0x120/0x326
> [   29.419145] [<ffffffff800033e6>] ret_from_exception+0x0/0xc
>
>

Sorry. I forgot to mention that this happens while booting Fedora on
an 8 cpu virt machine.
I am yet to reproduce this issue for 4 or 2 cpus.

>
> --
> Regards,
> Atish



-- 
Regards,
Atish

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 5/9] irqchip: Add RISC-V incoming MSI controller driver
  2022-11-11  4:42   ` Anup Patel
@ 2022-11-11 16:02     ` Andrew Bresticker
  -1 siblings, 0 replies; 96+ messages in thread
From: Andrew Bresticker @ 2022-11-11 16:02 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

On Thu, Nov 10, 2022 at 11:44 PM Anup Patel <apatel@ventanamicro.com> wrote:
>
> The RISC-V advanced interrupt architecture (AIA) specification defines
> a new MSI controller for managing MSIs on a RISC-V platform. This new
> MSI controller is referred to as incoming message signaled interrupt
> controller (IMSIC) which manages MSI on per-HART (or per-CPU) basis.
> (For more details refer https://github.com/riscv/riscv-aia)
>
> This patch adds an irqchip driver for RISC-V IMSIC found on RISC-V
> platforms.
>
> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> ---
>  drivers/irqchip/Kconfig             |   20 +-
>  drivers/irqchip/Makefile            |    1 +
>  drivers/irqchip/irq-riscv-imsic.c   | 1207 +++++++++++++++++++++++++++
>  include/linux/irqchip/riscv-imsic.h |   92 ++
>  4 files changed, 1319 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/irqchip/irq-riscv-imsic.c
>  create mode 100644 include/linux/irqchip/riscv-imsic.h
>
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index 7ef9f5e696d3..8246c08f0fd3 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -29,7 +29,6 @@ config ARM_GIC_V2M
>
>  config GIC_NON_BANKED
>         bool
> -
>  config ARM_GIC_V3
>         bool
>         select IRQ_DOMAIN_HIERARCHY
> @@ -564,6 +563,25 @@ config SIFIVE_PLIC
>
>            If you don't know what to do here, say Y.
>
> +config RISCV_IMSIC
> +       bool "RISC-V Incoming MSI Controller"
> +       depends on RISCV
> +       select IRQ_DOMAIN_HIERARCHY
> +       select GENERIC_MSI_IRQ_DOMAIN
> +       help
> +          This enables support for the IMSIC chip found in RISC-V systems.
> +          The IMSIC controls message signaled interrupts and forwards them
> +          to each core as wired local interrupt.
> +
> +          If you don't know what to do here, say Y.
> +
> +config RISCV_IMSIC_PCI
> +       bool
> +       depends on RISCV_IMSIC
> +       depends on PCI
> +       depends on PCI_MSI
> +       default RISCV_IMSIC
> +
>  config EXYNOS_IRQ_COMBINER
>         bool "Samsung Exynos IRQ combiner support" if COMPILE_TEST
>         depends on (ARCH_EXYNOS && ARM) || COMPILE_TEST
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index 87b49a10962c..22c723cc6ec8 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -96,6 +96,7 @@ obj-$(CONFIG_QCOM_MPM)                        += irq-qcom-mpm.o
>  obj-$(CONFIG_CSKY_MPINTC)              += irq-csky-mpintc.o
>  obj-$(CONFIG_CSKY_APB_INTC)            += irq-csky-apb-intc.o
>  obj-$(CONFIG_RISCV_INTC)               += irq-riscv-intc.o
> +obj-$(CONFIG_RISCV_IMSIC)              += irq-riscv-imsic.o
>  obj-$(CONFIG_SIFIVE_PLIC)              += irq-sifive-plic.o
>  obj-$(CONFIG_IMX_IRQSTEER)             += irq-imx-irqsteer.o
>  obj-$(CONFIG_IMX_INTMUX)               += irq-imx-intmux.o
> diff --git a/drivers/irqchip/irq-riscv-imsic.c b/drivers/irqchip/irq-riscv-imsic.c
> new file mode 100644
> index 000000000000..95324fb4f5ed
> --- /dev/null
> +++ b/drivers/irqchip/irq-riscv-imsic.c
> @@ -0,0 +1,1207 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2021 Western Digital Corporation or its affiliates.
> + * Copyright (C) 2022 Ventana Micro Systems Inc.
> + */
> +
> +#define pr_fmt(fmt) "riscv-imsic: " fmt
> +#include <linux/bitmap.h>
> +#include <linux/cpu.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/iommu.h>
> +#include <linux/irq.h>
> +#include <linux/irqchip.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/irqchip/riscv-imsic.h>
> +#include <linux/irqdomain.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +#include <linux/smp.h>
> +#include <asm/hwcap.h>
> +
> +#define IMSIC_DISABLE_EIDELIVERY       0
> +#define IMSIC_ENABLE_EIDELIVERY                1
> +#define IMSIC_DISABLE_EITHRESHOLD      1
> +#define IMSIC_ENABLE_EITHRESHOLD       0
> +
> +#define imsic_csr_write(__c, __v)      \
> +do {                                   \
> +       csr_write(CSR_ISELECT, __c);    \
> +       csr_write(CSR_IREG, __v);       \
> +} while (0)
> +
> +#define imsic_csr_read(__c)            \
> +({                                     \
> +       unsigned long __v;              \
> +       csr_write(CSR_ISELECT, __c);    \
> +       __v = csr_read(CSR_IREG);       \
> +       __v;                            \
> +})
> +
> +#define imsic_csr_set(__c, __v)                \
> +do {                                   \
> +       csr_write(CSR_ISELECT, __c);    \
> +       csr_set(CSR_IREG, __v);         \
> +} while (0)
> +
> +#define imsic_csr_clear(__c, __v)      \
> +do {                                   \
> +       csr_write(CSR_ISELECT, __c);    \
> +       csr_clear(CSR_IREG, __v);       \
> +} while (0)
> +
> +struct imsic_mmio {
> +       phys_addr_t pa;
> +       void __iomem *va;
> +       unsigned long size;
> +};
> +
> +struct imsic_priv {
> +       /* Global configuration common for all HARTs */
> +       struct imsic_global_config global;
> +
> +       /* MMIO regions */
> +       u32 num_mmios;
> +       struct imsic_mmio *mmios;
> +
> +       /* Global state of interrupt identities */
> +       raw_spinlock_t ids_lock;
> +       unsigned long *ids_used_bimap;
> +       unsigned long *ids_enabled_bimap;
> +       unsigned int *ids_target_cpu;
> +
> +       /* Mask for connected CPUs */
> +       struct cpumask lmask;
> +
> +       /* IPI interrupt identity */
> +       bool slow_ipi;
> +       u32 ipi_id;
> +       u32 ipi_lsync_id;
> +
> +       /* IRQ domains */
> +       struct irq_domain *base_domain;
> +       struct irq_domain *pci_domain;
> +       struct irq_domain *plat_domain;
> +};
> +
> +struct imsic_handler {
> +       /* Local configuration for given HART */
> +       struct imsic_local_config local;
> +
> +       /* Pointer to private context */
> +       struct imsic_priv *priv;
> +};
> +
> +static bool imsic_init_done;
> +
> +static int imsic_parent_irq;
> +static DEFINE_PER_CPU(struct imsic_handler, imsic_handlers);
> +
> +const struct imsic_global_config *imsic_get_global_config(void)
> +{
> +       struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
> +
> +       if (!handler || !handler->priv)
> +               return NULL;
> +
> +       return &handler->priv->global;
> +}
> +EXPORT_SYMBOL_GPL(imsic_get_global_config);
> +
> +const struct imsic_local_config *imsic_get_local_config(unsigned int cpu)
> +{
> +       struct imsic_handler *handler = per_cpu_ptr(&imsic_handlers, cpu);
> +
> +       if (!handler || !handler->priv)
> +               return NULL;
> +
> +       return &handler->local;
> +}
> +EXPORT_SYMBOL_GPL(imsic_get_local_config);
> +
> +static int imsic_cpu_page_phys(unsigned int cpu,
> +                              unsigned int guest_index,
> +                              phys_addr_t *out_msi_pa)
> +{
> +       struct imsic_handler *handler = per_cpu_ptr(&imsic_handlers, cpu);
> +       struct imsic_global_config *global;
> +       struct imsic_local_config *local;
> +
> +       if (!handler || !handler->priv)
> +               return -ENODEV;
> +       local = &handler->local;
> +       global = &handler->priv->global;
> +
> +       if (BIT(global->guest_index_bits) <= guest_index)
> +               return -EINVAL;
> +
> +       if (out_msi_pa)
> +               *out_msi_pa = local->msi_pa +
> +                             (guest_index * IMSIC_MMIO_PAGE_SZ);
> +
> +       return 0;
> +}
> +
> +static int imsic_get_cpu(struct imsic_priv *priv,
> +                        const struct cpumask *mask_val, bool force,
> +                        unsigned int *out_target_cpu)
> +{
> +       struct cpumask amask;
> +       unsigned int cpu;
> +
> +       cpumask_and(&amask, &priv->lmask, mask_val);
> +
> +       if (force)
> +               cpu = cpumask_first(&amask);
> +       else
> +               cpu = cpumask_any_and(&amask, cpu_online_mask);
> +
> +       if (cpu >= nr_cpu_ids)
> +               return -EINVAL;
> +
> +       if (out_target_cpu)
> +               *out_target_cpu = cpu;
> +
> +       return 0;
> +}
> +
> +static int imsic_get_cpu_msi_msg(unsigned int cpu, unsigned int id,
> +                                struct msi_msg *msg)
> +{
> +       phys_addr_t msi_addr;
> +       int err;
> +
> +       err = imsic_cpu_page_phys(cpu, 0, &msi_addr);
> +       if (err)
> +               return err;
> +
> +       msg->address_hi = upper_32_bits(msi_addr);
> +       msg->address_lo = lower_32_bits(msi_addr);
> +       msg->data = id;
> +
> +       return err;
> +}
> +
> +static void imsic_id_set_target(struct imsic_priv *priv,
> +                                unsigned int id, unsigned int target_cpu)
> +{
> +       raw_spin_lock(&priv->ids_lock);
> +       priv->ids_target_cpu[id] = target_cpu;
> +       raw_spin_unlock(&priv->ids_lock);
> +}
> +
> +static unsigned int imsic_id_get_target(struct imsic_priv *priv,
> +                                       unsigned int id)
> +{
> +       unsigned int ret;
> +
> +       raw_spin_lock(&priv->ids_lock);

This needs to be a raw_spin_lock_irqsave() since hardirqs are enabled
here and we also take priv->ids_lock in hardirq context (via
imsic_ids_local_sync()). Booting with lockdep enabled produces this
splat:

[    1.830055] ================================
[    1.830227] WARNING: inconsistent lock state
[    1.830425] 6.1.0-rc4-00026-g78e09ffc5534 #42 Not tainted
[    1.830655] --------------------------------
[    1.830784] inconsistent {HARDIRQ-ON-W} -> {IN-HARDIRQ-W} usage.
[    1.830972] swapper/0/1 [HC1[1]:SC0[0]:HE0:SE1] takes:
[    1.831162] ff6000000229d048 (&priv->ids_lock){?.+.}-{2:2}, at:
imsic_ids_local_sync+0x1e/0x98
[    1.831951] {HARDIRQ-ON-W} state was registered at:
[    1.832132]   __lock_acquire+0x93c/0x1e0c
[    1.832305]   lock_acquire+0xfa/0x2da
[    1.832412]   _raw_spin_lock+0x2c/0x40
[    1.832531]   imsic_ids_local_sync+0x1e/0x98
[    1.832674]   imsic_starting_cpu+0x72/0xac
[    1.832852]   cpuhp_invoke_callback+0x18c/0x93c
[    1.833033]   cpuhp_thread_fun+0x156/0x1b0
[    1.833158]   smpboot_thread_fn+0xea/0x1c8
[    1.833293]   kthread+0xc8/0xde
[    1.833391]   ret_from_exception+0x0/0x10
[    1.833530] irq event stamp: 33336
[    1.833647] hardirqs last  enabled at (33335): [<ffffffff808f2fcc>]
_raw_spin_unlock_irqrestore+0x4c/0x4e
[    1.833925] hardirqs last disabled at (33336): [<ffffffff800082f4>]
__trace_hardirqs_off+0xc/0x14
[    1.834179] softirqs last  enabled at (33056): [<ffffffff808f3f3e>]
__do_softirq+0x3de/0x51e
[    1.834450] softirqs last disabled at (33051): [<ffffffff8001788a>]
irq_exit+0xd6/0x104
[    1.834705]
[    1.834705] other info that might help us debug this:
[    1.834925]  Possible unsafe locking scenario:
[    1.834925]
[    1.835093]        CPU0
[    1.835176]        ----
[    1.835253]   lock(&priv->ids_lock);
[    1.835381]   <Interrupt>
[    1.835462]     lock(&priv->ids_lock);
...

Applies here and elsewhere where priv->ids_lock is taken in a possibly
hardirqs-enabled context.

-Andrew

> +       ret = priv->ids_target_cpu[id];
> +       raw_spin_unlock(&priv->ids_lock);
> +
> +       return ret;
> +}
> +
> +static void __imsic_eix_update(unsigned long base_id,
> +                              unsigned long num_id, bool pend, bool val)
> +{
> +       unsigned long i, isel, ireg, flags;
> +       unsigned long id = base_id, last_id = base_id + num_id;
> +
> +       while (id < last_id) {
> +               isel = id / BITS_PER_LONG;
> +               isel *= BITS_PER_LONG / IMSIC_EIPx_BITS;
> +               isel += (pend) ? IMSIC_EIP0 : IMSIC_EIE0;
> +
> +               ireg = 0;
> +               for (i = id & (__riscv_xlen - 1);
> +                    (id < last_id) && (i < __riscv_xlen); i++) {
> +                       ireg |= BIT(i);
> +                       id++;
> +               }
> +
> +               /*
> +                * The IMSIC EIEx and EIPx registers are indirectly
> +                * accessed via using ISELECT and IREG CSRs so we
> +                * save/restore local IRQ to ensure that we don't
> +                * get preempted while accessing IMSIC registers.
> +                */
> +               local_irq_save(flags);
> +               if (val)
> +                       imsic_csr_set(isel, ireg);
> +               else
> +                       imsic_csr_clear(isel, ireg);
> +               local_irq_restore(flags);
> +       }
> +}
> +
> +#define __imsic_id_enable(__id)                \
> +       __imsic_eix_update((__id), 1, false, true)
> +#define __imsic_id_disable(__id)       \
> +       __imsic_eix_update((__id), 1, false, false)
> +
> +#ifdef CONFIG_SMP
> +static void __imsic_id_smp_sync(struct imsic_priv *priv)
> +{
> +       struct imsic_handler *handler;
> +       struct cpumask amask;
> +       int cpu;
> +
> +       cpumask_and(&amask, &priv->lmask, cpu_online_mask);
> +       for_each_cpu(cpu, &amask) {
> +               if (cpu == smp_processor_id())
> +                       continue;
> +
> +               handler = per_cpu_ptr(&imsic_handlers, cpu);
> +               if (!handler || !handler->priv || !handler->local.msi_va) {
> +                       pr_warn("CPU%d: handler not initialized\n", cpu);
> +                       continue;
> +               }
> +
> +               writel(handler->priv->ipi_lsync_id, handler->local.msi_va);
> +       }
> +}
> +#else
> +#define __imsic_id_smp_sync(__priv)
> +#endif
> +
> +static void imsic_id_enable(struct imsic_priv *priv, unsigned int id)
> +{
> +       raw_spin_lock(&priv->ids_lock);
> +       bitmap_set(priv->ids_enabled_bimap, id, 1);
> +       __imsic_id_enable(id);
> +       raw_spin_unlock(&priv->ids_lock);
> +
> +       __imsic_id_smp_sync(priv);
> +}
> +
> +static void imsic_id_disable(struct imsic_priv *priv, unsigned int id)
> +{
> +       raw_spin_lock(&priv->ids_lock);
> +       bitmap_clear(priv->ids_enabled_bimap, id, 1);
> +       __imsic_id_disable(id);
> +       raw_spin_unlock(&priv->ids_lock);
> +
> +       __imsic_id_smp_sync(priv);
> +}
> +
> +static void imsic_ids_local_sync(struct imsic_priv *priv)
> +{
> +       int i;
> +
> +       raw_spin_lock(&priv->ids_lock);
> +       for (i = 1; i <= priv->global.nr_ids; i++) {
> +               if (priv->ipi_id == i || priv->ipi_lsync_id == i)
> +                       continue;
> +
> +               if (test_bit(i, priv->ids_enabled_bimap))
> +                       __imsic_id_enable(i);
> +               else
> +                       __imsic_id_disable(i);
> +       }
> +       raw_spin_unlock(&priv->ids_lock);
> +}
> +
> +static void imsic_ids_local_delivery(struct imsic_priv *priv, bool enable)
> +{
> +       if (enable) {
> +               imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_ENABLE_EITHRESHOLD);
> +               imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_ENABLE_EIDELIVERY);
> +       } else {
> +               imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_DISABLE_EIDELIVERY);
> +               imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_DISABLE_EITHRESHOLD);
> +       }
> +}
> +
> +static int imsic_ids_alloc(struct imsic_priv *priv,
> +                          unsigned int max_id, unsigned int order)
> +{
> +       int ret;
> +
> +       if ((priv->global.nr_ids < max_id) ||
> +           (max_id < BIT(order)))
> +               return -EINVAL;
> +
> +       raw_spin_lock(&priv->ids_lock);
> +       ret = bitmap_find_free_region(priv->ids_used_bimap,
> +                                     max_id + 1, order);
> +       raw_spin_unlock(&priv->ids_lock);
> +
> +       return ret;
> +}
> +
> +static void imsic_ids_free(struct imsic_priv *priv, unsigned int base_id,
> +                          unsigned int order)
> +{
> +       raw_spin_lock(&priv->ids_lock);
> +       bitmap_release_region(priv->ids_used_bimap, base_id, order);
> +       raw_spin_unlock(&priv->ids_lock);
> +}
> +
> +static int __init imsic_ids_init(struct imsic_priv *priv)
> +{
> +       int i;
> +       struct imsic_global_config *global = &priv->global;
> +
> +       raw_spin_lock_init(&priv->ids_lock);
> +
> +       /* Allocate used bitmap */
> +       priv->ids_used_bimap = kcalloc(BITS_TO_LONGS(global->nr_ids + 1),
> +                                       sizeof(unsigned long), GFP_KERNEL);
> +       if (!priv->ids_used_bimap)
> +               return -ENOMEM;
> +
> +       /* Allocate enabled bitmap */
> +       priv->ids_enabled_bimap = kcalloc(BITS_TO_LONGS(global->nr_ids + 1),
> +                                          sizeof(unsigned long), GFP_KERNEL);
> +       if (!priv->ids_enabled_bimap) {
> +               kfree(priv->ids_used_bimap);
> +               return -ENOMEM;
> +       }
> +
> +       /* Allocate target CPU array */
> +       priv->ids_target_cpu = kcalloc(global->nr_ids + 1,
> +                                      sizeof(unsigned int), GFP_KERNEL);
> +       if (!priv->ids_target_cpu) {
> +               kfree(priv->ids_enabled_bimap);
> +               kfree(priv->ids_used_bimap);
> +               return -ENOMEM;
> +       }
> +       for (i = 0; i <= global->nr_ids; i++)
> +               priv->ids_target_cpu[i] = UINT_MAX;
> +
> +       /* Reserve ID#0 because it is special and never implemented */
> +       bitmap_set(priv->ids_used_bimap, 0, 1);
> +
> +       return 0;
> +}
> +
> +static void __init imsic_ids_cleanup(struct imsic_priv *priv)
> +{
> +       kfree(priv->ids_target_cpu);
> +       kfree(priv->ids_enabled_bimap);
> +       kfree(priv->ids_used_bimap);
> +}
> +
> +#ifdef CONFIG_SMP
> +static void imsic_ipi_send_mask(unsigned int parent_virq, void *data,
> +                               const struct cpumask *mask)
> +{
> +       int cpu;
> +       struct imsic_handler *handler;
> +
> +       for_each_cpu(cpu, mask) {
> +               handler = per_cpu_ptr(&imsic_handlers, cpu);
> +               if (!handler || !handler->priv || !handler->local.msi_va) {
> +                       pr_warn("CPU%d: handler not initialized\n", cpu);
> +                       continue;
> +               }
> +
> +               writel(handler->priv->ipi_id, handler->local.msi_va);
> +       }
> +}
> +
> +static struct ipi_mux_ops imsic_ipi_ops = {
> +       .ipi_mux_send = imsic_ipi_send_mask,
> +};
> +
> +static void imsic_ipi_enable(struct imsic_priv *priv)
> +{
> +       __imsic_id_enable(priv->ipi_id);
> +       __imsic_id_enable(priv->ipi_lsync_id);
> +}
> +
> +static void imsic_ipi_disable(struct imsic_priv *priv)
> +{
> +       __imsic_id_disable(priv->ipi_lsync_id);
> +       __imsic_id_disable(priv->ipi_id);
> +}
> +
> +static int __init imsic_ipi_domain_init(struct imsic_priv *priv)
> +{
> +       int virq;
> +
> +       /* Skip IPI setup if IPIs are slow */
> +       if (priv->slow_ipi)
> +               goto skip_ipi;
> +
> +       /* Allocate interrupt identity for IPIs */
> +       virq = imsic_ids_alloc(priv, priv->global.nr_ids, get_count_order(1));
> +       if (virq < 0)
> +               return virq;
> +       priv->ipi_id = virq;
> +
> +       /* Create IMSIC IPI multiplexing */
> +       virq = ipi_mux_create(0, BITS_PER_BYTE, &imsic_ipi_ops, NULL);
> +       if (virq <= 0) {
> +               imsic_ids_free(priv, priv->ipi_id, get_count_order(1));
> +               return (virq < 0) ? virq : -ENOMEM;
> +       }
> +
> +       /* Set vIRQ range */
> +       riscv_ipi_set_virq_range(virq, BITS_PER_BYTE, true);
> +
> +skip_ipi:
> +       /* Allocate interrupt identity for local enable/disable sync */
> +       virq = imsic_ids_alloc(priv, priv->global.nr_ids, get_count_order(1));
> +       if (virq < 0) {
> +               imsic_ids_free(priv, priv->ipi_id, get_count_order(1));
> +               return virq;
> +       }
> +       priv->ipi_lsync_id = virq;
> +
> +       return 0;
> +}
> +
> +static void __init imsic_ipi_domain_cleanup(struct imsic_priv *priv)
> +{
> +       imsic_ids_free(priv, priv->ipi_lsync_id, get_count_order(1));
> +       if (priv->ipi_id)
> +               imsic_ids_free(priv, priv->ipi_id, get_count_order(1));
> +}
> +#else
> +static void imsic_ipi_enable(struct imsic_priv *priv)
> +{
> +}
> +
> +static void imsic_ipi_disable(struct imsic_priv *priv)
> +{
> +}
> +
> +static int __init imsic_ipi_domain_init(struct imsic_priv *priv)
> +{
> +       /* Clear the IPI ids because we are not using IPIs */
> +       priv->ipi_id = 0;
> +       priv->ipi_lsync_id = 0;
> +       return 0;
> +}
> +
> +static void __init imsic_ipi_domain_cleanup(struct imsic_priv *priv)
> +{
> +}
> +#endif
> +
> +static void imsic_irq_mask(struct irq_data *d)
> +{
> +       imsic_id_disable(irq_data_get_irq_chip_data(d), d->hwirq);
> +}
> +
> +static void imsic_irq_unmask(struct irq_data *d)
> +{
> +       imsic_id_enable(irq_data_get_irq_chip_data(d), d->hwirq);
> +}
> +
> +static void imsic_irq_compose_msi_msg(struct irq_data *d,
> +                                     struct msi_msg *msg)
> +{
> +       struct imsic_priv *priv = irq_data_get_irq_chip_data(d);
> +       unsigned int cpu;
> +       int err;
> +
> +       cpu = imsic_id_get_target(priv, d->hwirq);
> +       WARN_ON(cpu == UINT_MAX);
> +
> +       err = imsic_get_cpu_msi_msg(cpu, d->hwirq, msg);
> +       WARN_ON(err);
> +
> +       iommu_dma_compose_msi_msg(irq_data_get_msi_desc(d), msg);
> +}
> +
> +#ifdef CONFIG_SMP
> +static int imsic_irq_set_affinity(struct irq_data *d,
> +                                 const struct cpumask *mask_val,
> +                                 bool force)
> +{
> +       struct imsic_priv *priv = irq_data_get_irq_chip_data(d);
> +       unsigned int target_cpu;
> +       int rc;
> +
> +       rc = imsic_get_cpu(priv, mask_val, force, &target_cpu);
> +       if (rc)
> +               return rc;
> +
> +       imsic_id_set_target(priv, d->hwirq, target_cpu);
> +       irq_data_update_effective_affinity(d, cpumask_of(target_cpu));
> +
> +       return IRQ_SET_MASK_OK;
> +}
> +#endif
> +
> +static struct irq_chip imsic_irq_base_chip = {
> +       .name                   = "RISC-V IMSIC-BASE",
> +       .irq_mask               = imsic_irq_mask,
> +       .irq_unmask             = imsic_irq_unmask,
> +#ifdef CONFIG_SMP
> +       .irq_set_affinity       = imsic_irq_set_affinity,
> +#endif
> +       .irq_compose_msi_msg    = imsic_irq_compose_msi_msg,
> +       .flags                  = IRQCHIP_SKIP_SET_WAKE |
> +                                 IRQCHIP_MASK_ON_SUSPEND,
> +};
> +
> +static int imsic_irq_domain_alloc(struct irq_domain *domain,
> +                                 unsigned int virq,
> +                                 unsigned int nr_irqs,
> +                                 void *args)
> +{
> +       struct imsic_priv *priv = domain->host_data;
> +       msi_alloc_info_t *info = args;
> +       phys_addr_t msi_addr;
> +       int i, hwirq, err = 0;
> +       unsigned int cpu;
> +
> +       err = imsic_get_cpu(priv, &priv->lmask, false, &cpu);
> +       if (err)
> +               return err;
> +
> +       err = imsic_cpu_page_phys(cpu, 0, &msi_addr);
> +       if (err)
> +               return err;
> +
> +       hwirq = imsic_ids_alloc(priv, priv->global.nr_ids,
> +                               get_count_order(nr_irqs));
> +       if (hwirq < 0)
> +               return hwirq;
> +
> +       err = iommu_dma_prepare_msi(info->desc, msi_addr);
> +       if (err)
> +               goto fail;
> +
> +       for (i = 0; i < nr_irqs; i++) {
> +               imsic_id_set_target(priv, hwirq + i, cpu);
> +               irq_domain_set_info(domain, virq + i, hwirq + i,
> +                                   &imsic_irq_base_chip, priv,
> +                                   handle_simple_irq, NULL, NULL);
> +               irq_set_noprobe(virq + i);
> +               irq_set_affinity(virq + i, &priv->lmask);
> +       }
> +
> +       return 0;
> +
> +fail:
> +       imsic_ids_free(priv, hwirq, get_count_order(nr_irqs));
> +       return err;
> +}
> +
> +static void imsic_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 imsic_priv *priv = domain->host_data;
> +
> +       imsic_ids_free(priv, d->hwirq, get_count_order(nr_irqs));
> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops imsic_base_domain_ops = {
> +       .alloc          = imsic_irq_domain_alloc,
> +       .free           = imsic_irq_domain_free,
> +};
> +
> +#ifdef CONFIG_RISCV_IMSIC_PCI
> +
> +static void imsic_pci_mask_irq(struct irq_data *d)
> +{
> +       pci_msi_mask_irq(d);
> +       irq_chip_mask_parent(d);
> +}
> +
> +static void imsic_pci_unmask_irq(struct irq_data *d)
> +{
> +       pci_msi_unmask_irq(d);
> +       irq_chip_unmask_parent(d);
> +}
> +
> +static struct irq_chip imsic_pci_irq_chip = {
> +       .name                   = "RISC-V IMSIC-PCI",
> +       .irq_mask               = imsic_pci_mask_irq,
> +       .irq_unmask             = imsic_pci_unmask_irq,
> +       .irq_eoi                = irq_chip_eoi_parent,
> +};
> +
> +static struct msi_domain_ops imsic_pci_domain_ops = {
> +};
> +
> +static struct msi_domain_info imsic_pci_domain_info = {
> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +                  MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI),
> +       .ops    = &imsic_pci_domain_ops,
> +       .chip   = &imsic_pci_irq_chip,
> +};
> +
> +#endif
> +
> +static struct irq_chip imsic_plat_irq_chip = {
> +       .name                   = "RISC-V IMSIC-PLAT",
> +};
> +
> +static struct msi_domain_ops imsic_plat_domain_ops = {
> +};
> +
> +static struct msi_domain_info imsic_plat_domain_info = {
> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
> +       .ops    = &imsic_plat_domain_ops,
> +       .chip   = &imsic_plat_irq_chip,
> +};
> +
> +static int __init imsic_irq_domains_init(struct imsic_priv *priv,
> +                                        struct fwnode_handle *fwnode)
> +{
> +       /* Create Base IRQ domain */
> +       priv->base_domain = irq_domain_create_tree(fwnode,
> +                                               &imsic_base_domain_ops, priv);
> +       if (!priv->base_domain) {
> +               pr_err("Failed to create IMSIC base domain\n");
> +               return -ENOMEM;
> +       }
> +       irq_domain_update_bus_token(priv->base_domain, DOMAIN_BUS_NEXUS);
> +
> +#ifdef CONFIG_RISCV_IMSIC_PCI
> +       /* Create PCI MSI domain */
> +       priv->pci_domain = pci_msi_create_irq_domain(fwnode,
> +                                               &imsic_pci_domain_info,
> +                                               priv->base_domain);
> +       if (!priv->pci_domain) {
> +               pr_err("Failed to create IMSIC PCI domain\n");
> +               irq_domain_remove(priv->base_domain);
> +               return -ENOMEM;
> +       }
> +#endif
> +
> +       /* Create Platform MSI domain */
> +       priv->plat_domain = platform_msi_create_irq_domain(fwnode,
> +                                               &imsic_plat_domain_info,
> +                                               priv->base_domain);
> +       if (!priv->plat_domain) {
> +               pr_err("Failed to create IMSIC platform domain\n");
> +               if (priv->pci_domain)
> +                       irq_domain_remove(priv->pci_domain);
> +               irq_domain_remove(priv->base_domain);
> +               return -ENOMEM;
> +       }
> +
> +       return 0;
> +}
> +
> +/*
> + * To handle an interrupt, we read the TOPEI CSR and write zero in one
> + * instruction. If TOPEI CSR is non-zero then we translate TOPEI.ID to
> + * Linux interrupt number and let Linux IRQ subsystem handle it.
> + */
> +static void imsic_handle_irq(struct irq_desc *desc)
> +{
> +       struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
> +       struct irq_chip *chip = irq_desc_get_chip(desc);
> +       struct imsic_priv *priv = handler->priv;
> +       irq_hw_number_t hwirq;
> +       int err;
> +
> +       WARN_ON_ONCE(!handler->priv);
> +
> +       chained_irq_enter(chip, desc);
> +
> +       while ((hwirq = csr_swap(CSR_TOPEI, 0))) {
> +               hwirq = hwirq >> TOPEI_ID_SHIFT;
> +
> +               if (hwirq == priv->ipi_id) {
> +#ifdef CONFIG_SMP
> +                       ipi_mux_process();
> +#endif
> +                       continue;
> +               } else if (hwirq == priv->ipi_lsync_id) {
> +                       imsic_ids_local_sync(priv);
> +                       continue;
> +               }
> +
> +               err = generic_handle_domain_irq(priv->base_domain, hwirq);
> +               if (unlikely(err))
> +                       pr_warn_ratelimited(
> +                               "hwirq %lu mapping not found\n", hwirq);
> +       }
> +
> +       chained_irq_exit(chip, desc);
> +}
> +
> +static int imsic_dying_cpu(unsigned int cpu)
> +{
> +       struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
> +       struct imsic_priv *priv = handler->priv;
> +
> +       /* Disable per-CPU parent interrupt */
> +       if (imsic_parent_irq)
> +               disable_percpu_irq(imsic_parent_irq);
> +
> +       /* Locally disable interrupt delivery */
> +       imsic_ids_local_delivery(priv, false);
> +
> +       /* Disable IPIs */
> +       imsic_ipi_disable(priv);
> +
> +       return 0;
> +}
> +
> +static int imsic_starting_cpu(unsigned int cpu)
> +{
> +       struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
> +       struct imsic_priv *priv = handler->priv;
> +
> +       /* Enable per-CPU parent interrupt */
> +       if (imsic_parent_irq)
> +               enable_percpu_irq(imsic_parent_irq,
> +                                 irq_get_trigger_type(imsic_parent_irq));
> +       else
> +               pr_warn("cpu%d: parent irq not available\n", cpu);
> +
> +       /* Enable IPIs */
> +       imsic_ipi_enable(priv);
> +
> +       /*
> +        * Interrupts identities might have been enabled/disabled while
> +        * this CPU was not running so sync-up local enable/disable state.
> +        */
> +       imsic_ids_local_sync(priv);
> +
> +       /* Locally enable interrupt delivery */
> +       imsic_ids_local_delivery(priv, true);
> +
> +       return 0;
> +}
> +
> +struct imsic_fwnode_ops {
> +       u32 (*nr_parent_irq)(struct fwnode_handle *fwnode,
> +                            void *fwopaque);
> +       int (*parent_hartid)(struct fwnode_handle *fwnode,
> +                            void *fwopaque, u32 index,
> +                            unsigned long *out_hartid);
> +       u32 (*nr_mmio)(struct fwnode_handle *fwnode, void *fwopaque);
> +       int (*mmio_to_resource)(struct fwnode_handle *fwnode,
> +                               void *fwopaque, u32 index,
> +                               struct resource *res);
> +       void __iomem *(*mmio_map)(struct fwnode_handle *fwnode,
> +                                 void *fwopaque, u32 index);
> +       int (*read_u32)(struct fwnode_handle *fwnode,
> +                       void *fwopaque, const char *prop, u32 *out_val);
> +       bool (*read_bool)(struct fwnode_handle *fwnode,
> +                         void *fwopaque, const char *prop);
> +};
> +
> +static int __init imsic_init(struct imsic_fwnode_ops *fwops,
> +                            struct fwnode_handle *fwnode,
> +                            void *fwopaque)
> +{
> +       struct resource res;
> +       phys_addr_t base_addr;
> +       int rc, nr_parent_irqs;
> +       struct imsic_mmio *mmio;
> +       struct imsic_priv *priv;
> +       struct irq_domain *domain;
> +       struct imsic_handler *handler;
> +       struct imsic_global_config *global;
> +       u32 i, tmp, nr_handlers = 0;
> +
> +       if (imsic_init_done) {
> +               pr_err("%pfwP: already initialized hence ignoring\n",
> +                       fwnode);
> +               return -ENODEV;
> +       }
> +
> +       if (!riscv_isa_extension_available(NULL, SxAIA)) {
> +               pr_err("%pfwP: AIA support not available\n", fwnode);
> +               return -ENODEV;
> +       }
> +
> +       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> +       if (!priv)
> +               return -ENOMEM;
> +       global = &priv->global;
> +
> +       /* Find number of parent interrupts */
> +       nr_parent_irqs = fwops->nr_parent_irq(fwnode, fwopaque);
> +       if (!nr_parent_irqs) {
> +               pr_err("%pfwP: no parent irqs available\n", fwnode);
> +               return -EINVAL;
> +       }
> +
> +       /* Find number of guest index bits in MSI address */
> +       rc = fwops->read_u32(fwnode, fwopaque, "riscv,guest-index-bits",
> +                            &global->guest_index_bits);
> +       if (rc)
> +               global->guest_index_bits = 0;
> +       tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT;
> +       if (tmp < global->guest_index_bits) {
> +               pr_err("%pfwP: guest index bits too big\n", fwnode);
> +               return -EINVAL;
> +       }
> +
> +       /* Find number of HART index bits */
> +       rc = fwops->read_u32(fwnode, fwopaque, "riscv,hart-index-bits",
> +                            &global->hart_index_bits);
> +       if (rc) {
> +               /* Assume default value */
> +               global->hart_index_bits = __fls(nr_parent_irqs);
> +               if (BIT(global->hart_index_bits) < nr_parent_irqs)
> +                       global->hart_index_bits++;
> +       }
> +       tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT -
> +             global->guest_index_bits;
> +       if (tmp < global->hart_index_bits) {
> +               pr_err("%pfwP: HART index bits too big\n", fwnode);
> +               return -EINVAL;
> +       }
> +
> +       /* Find number of group index bits */
> +       rc = fwops->read_u32(fwnode, fwopaque, "riscv,group-index-bits",
> +                            &global->group_index_bits);
> +       if (rc)
> +               global->group_index_bits = 0;
> +       tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT -
> +             global->guest_index_bits - global->hart_index_bits;
> +       if (tmp < global->group_index_bits) {
> +               pr_err("%pfwP: group index bits too big\n", fwnode);
> +               return -EINVAL;
> +       }
> +
> +       /* Find first bit position of group index */
> +       tmp = IMSIC_MMIO_PAGE_SHIFT * 2;
> +       rc = fwops->read_u32(fwnode, fwopaque, "riscv,group-index-shift",
> +                            &global->group_index_shift);
> +       if (rc)
> +               global->group_index_shift = tmp;
> +       if (global->group_index_shift < tmp) {
> +               pr_err("%pfwP: group index shift too small\n", fwnode);
> +               return -EINVAL;
> +       }
> +       tmp = global->group_index_bits + global->group_index_shift - 1;
> +       if (tmp >= BITS_PER_LONG) {
> +               pr_err("%pfwP: group index shift too big\n", fwnode);
> +               return -EINVAL;
> +       }
> +
> +       /* Find number of interrupt identities */
> +       rc = fwops->read_u32(fwnode, fwopaque, "riscv,num-ids",
> +                            &global->nr_ids);
> +       if (rc) {
> +               pr_err("%pfwP: number of interrupt identities not found\n",
> +                       fwnode);
> +               return rc;
> +       }
> +       if ((global->nr_ids < IMSIC_MIN_ID) ||
> +           (global->nr_ids >= IMSIC_MAX_ID) ||
> +           ((global->nr_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID)) {
> +               pr_err("%pfwP: invalid number of interrupt identities\n",
> +                       fwnode);
> +               return -EINVAL;
> +       }
> +
> +       /* Find number of guest interrupt identities */
> +       if (fwops->read_u32(fwnode, fwopaque, "riscv,num-guest-ids",
> +                           &global->nr_guest_ids))
> +               global->nr_guest_ids = global->nr_ids;
> +       if ((global->nr_guest_ids < IMSIC_MIN_ID) ||
> +           (global->nr_guest_ids >= IMSIC_MAX_ID) ||
> +           ((global->nr_guest_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID)) {
> +               pr_err("%pfwP: invalid number of guest interrupt identities\n",
> +                       fwnode);
> +               return -EINVAL;
> +       }
> +
> +       /* Check if IPIs are slow */
> +       priv->slow_ipi = fwops->read_bool(fwnode, fwopaque, "riscv,slow-ipi");
> +
> +       /* Compute base address */
> +       rc = fwops->mmio_to_resource(fwnode, fwopaque, 0, &res);
> +       if (rc) {
> +               pr_err("%pfwP: first MMIO resource not found\n", fwnode);
> +               return -EINVAL;
> +       }
> +       global->base_addr = res.start;
> +       global->base_addr &= ~(BIT(global->guest_index_bits +
> +                                  global->hart_index_bits +
> +                                  IMSIC_MMIO_PAGE_SHIFT) - 1);
> +       global->base_addr &= ~((BIT(global->group_index_bits) - 1) <<
> +                              global->group_index_shift);
> +
> +       /* Find number of MMIO register sets */
> +       priv->num_mmios = fwops->nr_mmio(fwnode, fwopaque);
> +
> +       /* Allocate MMIO register sets */
> +       priv->mmios = kcalloc(priv->num_mmios, sizeof(*mmio), GFP_KERNEL);
> +       if (!priv->mmios) {
> +               rc = -ENOMEM;
> +               goto out_free_priv;
> +       }
> +
> +       /* Parse and map MMIO register sets */
> +       for (i = 0; i < priv->num_mmios; i++) {
> +               mmio = &priv->mmios[i];
> +               rc = fwops->mmio_to_resource(fwnode, fwopaque, i, &res);
> +               if (rc) {
> +                       pr_err("%pfwP: unable to parse MMIO regset %d\n",
> +                               fwnode, i);
> +                       goto out_iounmap;
> +               }
> +               mmio->pa = res.start;
> +               mmio->size = res.end - res.start + 1;
> +
> +               base_addr = mmio->pa;
> +               base_addr &= ~(BIT(global->guest_index_bits +
> +                                  global->hart_index_bits +
> +                                  IMSIC_MMIO_PAGE_SHIFT) - 1);
> +               base_addr &= ~((BIT(global->group_index_bits) - 1) <<
> +                              global->group_index_shift);
> +               if (base_addr != global->base_addr) {
> +                       rc = -EINVAL;
> +                       pr_err("%pfwP: address mismatch for regset %d\n",
> +                               fwnode, i);
> +                       goto out_iounmap;
> +               }
> +
> +               tmp = BIT(global->guest_index_bits) - 1;
> +               if ((mmio->size / IMSIC_MMIO_PAGE_SZ) & tmp) {
> +                       rc = -EINVAL;
> +                       pr_err("%pfwP: size mismatch for regset %d\n",
> +                               fwnode, i);
> +                       goto out_iounmap;
> +               }
> +
> +               mmio->va = fwops->mmio_map(fwnode, fwopaque, i);
> +               if (!mmio->va) {
> +                       rc = -EIO;
> +                       pr_err("%pfwP: unable to map MMIO regset %d\n",
> +                               fwnode, i);
> +                       goto out_iounmap;
> +               }
> +       }
> +
> +       /* Initialize interrupt identity management */
> +       rc = imsic_ids_init(priv);
> +       if (rc) {
> +               pr_err("%pfwP: failed to initialize interrupt management\n",
> +                      fwnode);
> +               goto out_iounmap;
> +       }
> +
> +       /* Configure handlers for target CPUs */
> +       for (i = 0; i < nr_parent_irqs; i++) {
> +               unsigned long reloff, hartid;
> +               int j, cpu;
> +
> +               rc = fwops->parent_hartid(fwnode, fwopaque, i, &hartid);
> +               if (rc) {
> +                       pr_warn("%pfwP: hart ID for parent irq%d not found\n",
> +                               fwnode, i);
> +                       continue;
> +               }
> +
> +               cpu = riscv_hartid_to_cpuid(hartid);
> +               if (cpu < 0) {
> +                       pr_warn("%pfwP: invalid cpuid for parent irq%d\n",
> +                               fwnode, i);
> +                       continue;
> +               }
> +
> +               /* Find MMIO location of MSI page */
> +               mmio = NULL;
> +               reloff = i * BIT(global->guest_index_bits) *
> +                        IMSIC_MMIO_PAGE_SZ;
> +               for (j = 0; priv->num_mmios; j++) {
> +                       if (reloff < priv->mmios[j].size) {
> +                               mmio = &priv->mmios[j];
> +                               break;
> +                       }
> +
> +                       reloff -= priv->mmios[j].size;
> +               }
> +               if (!mmio) {
> +                       pr_warn("%pfwP: MMIO not found for parent irq%d\n",
> +                               fwnode, i);
> +                       continue;
> +               }
> +
> +               handler = per_cpu_ptr(&imsic_handlers, cpu);
> +               if (handler->priv) {
> +                       pr_warn("%pfwP: CPU%d handler already configured.\n",
> +                               fwnode, cpu);
> +                       goto done;
> +               }
> +
> +               cpumask_set_cpu(cpu, &priv->lmask);
> +               handler->local.msi_pa = mmio->pa + reloff;
> +               handler->local.msi_va = mmio->va + reloff;
> +               handler->priv = priv;
> +
> +done:
> +               nr_handlers++;
> +       }
> +
> +       /* If no CPU handlers found then can't take interrupts */
> +       if (!nr_handlers) {
> +               pr_err("%pfwP: No CPU handlers found\n", fwnode);
> +               rc = -ENODEV;
> +               goto out_ids_cleanup;
> +       }
> +
> +       /* Find parent domain and register chained handler */
> +       domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(),
> +                                         DOMAIN_BUS_ANY);
> +       if (!domain) {
> +               pr_err("%pfwP: Failed to find INTC domain\n", fwnode);
> +               rc = -ENOENT;
> +               goto out_ids_cleanup;
> +       }
> +       imsic_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT);
> +       if (!imsic_parent_irq) {
> +               pr_err("%pfwP: Failed to create INTC mapping\n", fwnode);
> +               rc = -ENOENT;
> +               goto out_ids_cleanup;
> +       }
> +       irq_set_chained_handler(imsic_parent_irq, imsic_handle_irq);
> +
> +       /* Initialize IPI domain */
> +       rc = imsic_ipi_domain_init(priv);
> +       if (rc) {
> +               pr_err("%pfwP: Failed to initialize IPI domain\n", fwnode);
> +               goto out_ids_cleanup;
> +       }
> +
> +       /* Initialize IRQ and MSI domains */
> +       rc = imsic_irq_domains_init(priv, fwnode);
> +       if (rc) {
> +               pr_err("%pfwP: Failed to initialize IRQ and MSI domains\n",
> +                      fwnode);
> +               goto out_ipi_domain_cleanup;
> +       }
> +
> +       /* Setup cpuhp state */
> +       cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
> +                         "irqchip/riscv/imsic:starting",
> +                         imsic_starting_cpu, imsic_dying_cpu);
> +
> +       /*
> +        * Only one IMSIC instance allowed in a platform for clean
> +        * implementation of SMP IRQ affinity and per-CPU IPIs.
> +        *
> +        * This means on a multi-socket (or multi-die) platform we
> +        * will have multiple MMIO regions for one IMSIC instance.
> +        */
> +       imsic_init_done = true;
> +
> +       pr_info("%pfwP:  hart-index-bits: %d,  guest-index-bits: %d\n",
> +               fwnode, global->hart_index_bits, global->guest_index_bits);
> +       pr_info("%pfwP: group-index-bits: %d, group-index-shift: %d\n",
> +               fwnode, global->group_index_bits, global->group_index_shift);
> +       pr_info("%pfwP: mapped %d interrupts for %d CPUs at %pa\n",
> +               fwnode, global->nr_ids, nr_handlers, &global->base_addr);
> +       if (priv->ipi_lsync_id)
> +               pr_info("%pfwP: enable/disable sync using interrupt %d\n",
> +                       fwnode, priv->ipi_lsync_id);
> +       if (priv->ipi_id)
> +               pr_info("%pfwP: providing IPIs using interrupt %d\n",
> +                       fwnode, priv->ipi_id);
> +
> +       return 0;
> +
> +out_ipi_domain_cleanup:
> +       imsic_ipi_domain_cleanup(priv);
> +out_ids_cleanup:
> +       imsic_ids_cleanup(priv);
> +out_iounmap:
> +       for (i = 0; i < priv->num_mmios; i++) {
> +               if (priv->mmios[i].va)
> +                       iounmap(priv->mmios[i].va);
> +       }
> +       kfree(priv->mmios);
> +out_free_priv:
> +       kfree(priv);
> +       return rc;
> +}
> +
> +static u32 __init imsic_dt_nr_parent_irq(struct fwnode_handle *fwnode,
> +                                        void *fwopaque)
> +{
> +       return of_irq_count(to_of_node(fwnode));
> +}
> +
> +static int __init imsic_dt_parent_hartid(struct fwnode_handle *fwnode,
> +                                        void *fwopaque, u32 index,
> +                                        unsigned long *out_hartid)
> +{
> +       struct of_phandle_args parent;
> +       int rc;
> +
> +       rc = of_irq_parse_one(to_of_node(fwnode), index, &parent);
> +       if (rc)
> +               return rc;
> +
> +       /*
> +        * Skip interrupts other than external interrupts for
> +        * current privilege level.
> +        */
> +       if (parent.args[0] != RV_IRQ_EXT)
> +               return -EINVAL;
> +
> +       return riscv_of_parent_hartid(parent.np, out_hartid);
> +}
> +
> +static u32 __init imsic_dt_nr_mmio(struct fwnode_handle *fwnode,
> +                                  void *fwopaque)
> +{
> +       u32 ret = 0;
> +       struct resource res;
> +
> +       while (!of_address_to_resource(to_of_node(fwnode), ret, &res))
> +               ret++;
> +
> +       return ret;
> +}
> +
> +static int __init imsic_mmio_to_resource(struct fwnode_handle *fwnode,
> +                                        void *fwopaque, u32 index,
> +                                        struct resource *res)
> +{
> +       return of_address_to_resource(to_of_node(fwnode), index, res);
> +}
> +
> +static void __iomem __init *imsic_dt_mmio_map(struct fwnode_handle *fwnode,
> +                                             void *fwopaque, u32 index)
> +{
> +       return of_iomap(to_of_node(fwnode), index);
> +}
> +
> +static int __init imsic_dt_read_u32(struct fwnode_handle *fwnode,
> +                                   void *fwopaque, const char *prop,
> +                                   u32 *out_val)
> +{
> +       return of_property_read_u32(to_of_node(fwnode), prop, out_val);
> +}
> +
> +static bool __init imsic_dt_read_bool(struct fwnode_handle *fwnode,
> +                                     void *fwopaque, const char *prop)
> +{
> +       return of_property_read_bool(to_of_node(fwnode), prop);
> +}
> +
> +static int __init imsic_dt_init(struct device_node *node,
> +                               struct device_node *parent)
> +{
> +       struct imsic_fwnode_ops ops = {
> +               .nr_parent_irq = imsic_dt_nr_parent_irq,
> +               .parent_hartid = imsic_dt_parent_hartid,
> +               .nr_mmio = imsic_dt_nr_mmio,
> +               .mmio_to_resource = imsic_mmio_to_resource,
> +               .mmio_map = imsic_dt_mmio_map,
> +               .read_u32 = imsic_dt_read_u32,
> +               .read_bool = imsic_dt_read_bool,
> +       };
> +
> +       return imsic_init(&ops, &node->fwnode, NULL);
> +}
> +IRQCHIP_DECLARE(riscv_imsic, "riscv,imsics", imsic_dt_init);
> diff --git a/include/linux/irqchip/riscv-imsic.h b/include/linux/irqchip/riscv-imsic.h
> new file mode 100644
> index 000000000000..5d1387adc0ba
> --- /dev/null
> +++ b/include/linux/irqchip/riscv-imsic.h
> @@ -0,0 +1,92 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2021 Western Digital Corporation or its affiliates.
> + * Copyright (C) 2022 Ventana Micro Systems Inc.
> + */
> +#ifndef __LINUX_IRQCHIP_RISCV_IMSIC_H
> +#define __LINUX_IRQCHIP_RISCV_IMSIC_H
> +
> +#include <linux/types.h>
> +#include <asm/csr.h>
> +
> +#define IMSIC_MMIO_PAGE_SHIFT          12
> +#define IMSIC_MMIO_PAGE_SZ             (1UL << IMSIC_MMIO_PAGE_SHIFT)
> +#define IMSIC_MMIO_PAGE_LE             0x00
> +#define IMSIC_MMIO_PAGE_BE             0x04
> +
> +#define IMSIC_MIN_ID                   63
> +#define IMSIC_MAX_ID                   2048
> +
> +#define IMSIC_EIDELIVERY               0x70
> +
> +#define IMSIC_EITHRESHOLD              0x72
> +
> +#define IMSIC_EIP0                     0x80
> +#define IMSIC_EIP63                    0xbf
> +#define IMSIC_EIPx_BITS                        32
> +
> +#define IMSIC_EIE0                     0xc0
> +#define IMSIC_EIE63                    0xff
> +#define IMSIC_EIEx_BITS                        32
> +
> +#define IMSIC_FIRST                    IMSIC_EIDELIVERY
> +#define IMSIC_LAST                     IMSIC_EIE63
> +
> +#define IMSIC_MMIO_SETIPNUM_LE         0x00
> +#define IMSIC_MMIO_SETIPNUM_BE         0x04
> +
> +struct imsic_global_config {
> +       /*
> +        * MSI Target Address Scheme
> +        *
> +        * XLEN-1                                                12     0
> +        * |                                                     |     |
> +        * -------------------------------------------------------------
> +        * |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
> +        * -------------------------------------------------------------
> +        */
> +
> +       /* Bits representing Guest index, HART index, and Group index */
> +       u32 guest_index_bits;
> +       u32 hart_index_bits;
> +       u32 group_index_bits;
> +       u32 group_index_shift;
> +
> +       /* Global base address matching all target MSI addresses */
> +       phys_addr_t base_addr;
> +
> +       /* Number of interrupt identities */
> +       u32 nr_ids;
> +
> +       /* Number of guest interrupt identities */
> +       u32 nr_guest_ids;
> +};
> +
> +struct imsic_local_config {
> +       phys_addr_t msi_pa;
> +       void __iomem *msi_va;
> +};
> +
> +#ifdef CONFIG_RISCV_IMSIC
> +
> +extern const struct imsic_global_config *imsic_get_global_config(void);
> +
> +extern const struct imsic_local_config *imsic_get_local_config(
> +                                                       unsigned int cpu);
> +
> +#else
> +
> +static inline const struct imsic_global_config *imsic_get_global_config(void)
> +{
> +       return NULL;
> +}
> +
> +static inline const struct imsic_local_config *imsic_get_local_config(
> +                                                       unsigned int cpu)
> +{
> +       return NULL;
> +}
> +
> +#endif
> +
> +#endif
> --
> 2.34.1
>

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

* Re: [PATCH 5/9] irqchip: Add RISC-V incoming MSI controller driver
@ 2022-11-11 16:02     ` Andrew Bresticker
  0 siblings, 0 replies; 96+ messages in thread
From: Andrew Bresticker @ 2022-11-11 16:02 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

On Thu, Nov 10, 2022 at 11:44 PM Anup Patel <apatel@ventanamicro.com> wrote:
>
> The RISC-V advanced interrupt architecture (AIA) specification defines
> a new MSI controller for managing MSIs on a RISC-V platform. This new
> MSI controller is referred to as incoming message signaled interrupt
> controller (IMSIC) which manages MSI on per-HART (or per-CPU) basis.
> (For more details refer https://github.com/riscv/riscv-aia)
>
> This patch adds an irqchip driver for RISC-V IMSIC found on RISC-V
> platforms.
>
> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> ---
>  drivers/irqchip/Kconfig             |   20 +-
>  drivers/irqchip/Makefile            |    1 +
>  drivers/irqchip/irq-riscv-imsic.c   | 1207 +++++++++++++++++++++++++++
>  include/linux/irqchip/riscv-imsic.h |   92 ++
>  4 files changed, 1319 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/irqchip/irq-riscv-imsic.c
>  create mode 100644 include/linux/irqchip/riscv-imsic.h
>
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index 7ef9f5e696d3..8246c08f0fd3 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -29,7 +29,6 @@ config ARM_GIC_V2M
>
>  config GIC_NON_BANKED
>         bool
> -
>  config ARM_GIC_V3
>         bool
>         select IRQ_DOMAIN_HIERARCHY
> @@ -564,6 +563,25 @@ config SIFIVE_PLIC
>
>            If you don't know what to do here, say Y.
>
> +config RISCV_IMSIC
> +       bool "RISC-V Incoming MSI Controller"
> +       depends on RISCV
> +       select IRQ_DOMAIN_HIERARCHY
> +       select GENERIC_MSI_IRQ_DOMAIN
> +       help
> +          This enables support for the IMSIC chip found in RISC-V systems.
> +          The IMSIC controls message signaled interrupts and forwards them
> +          to each core as wired local interrupt.
> +
> +          If you don't know what to do here, say Y.
> +
> +config RISCV_IMSIC_PCI
> +       bool
> +       depends on RISCV_IMSIC
> +       depends on PCI
> +       depends on PCI_MSI
> +       default RISCV_IMSIC
> +
>  config EXYNOS_IRQ_COMBINER
>         bool "Samsung Exynos IRQ combiner support" if COMPILE_TEST
>         depends on (ARCH_EXYNOS && ARM) || COMPILE_TEST
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index 87b49a10962c..22c723cc6ec8 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -96,6 +96,7 @@ obj-$(CONFIG_QCOM_MPM)                        += irq-qcom-mpm.o
>  obj-$(CONFIG_CSKY_MPINTC)              += irq-csky-mpintc.o
>  obj-$(CONFIG_CSKY_APB_INTC)            += irq-csky-apb-intc.o
>  obj-$(CONFIG_RISCV_INTC)               += irq-riscv-intc.o
> +obj-$(CONFIG_RISCV_IMSIC)              += irq-riscv-imsic.o
>  obj-$(CONFIG_SIFIVE_PLIC)              += irq-sifive-plic.o
>  obj-$(CONFIG_IMX_IRQSTEER)             += irq-imx-irqsteer.o
>  obj-$(CONFIG_IMX_INTMUX)               += irq-imx-intmux.o
> diff --git a/drivers/irqchip/irq-riscv-imsic.c b/drivers/irqchip/irq-riscv-imsic.c
> new file mode 100644
> index 000000000000..95324fb4f5ed
> --- /dev/null
> +++ b/drivers/irqchip/irq-riscv-imsic.c
> @@ -0,0 +1,1207 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2021 Western Digital Corporation or its affiliates.
> + * Copyright (C) 2022 Ventana Micro Systems Inc.
> + */
> +
> +#define pr_fmt(fmt) "riscv-imsic: " fmt
> +#include <linux/bitmap.h>
> +#include <linux/cpu.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/iommu.h>
> +#include <linux/irq.h>
> +#include <linux/irqchip.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/irqchip/riscv-imsic.h>
> +#include <linux/irqdomain.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +#include <linux/smp.h>
> +#include <asm/hwcap.h>
> +
> +#define IMSIC_DISABLE_EIDELIVERY       0
> +#define IMSIC_ENABLE_EIDELIVERY                1
> +#define IMSIC_DISABLE_EITHRESHOLD      1
> +#define IMSIC_ENABLE_EITHRESHOLD       0
> +
> +#define imsic_csr_write(__c, __v)      \
> +do {                                   \
> +       csr_write(CSR_ISELECT, __c);    \
> +       csr_write(CSR_IREG, __v);       \
> +} while (0)
> +
> +#define imsic_csr_read(__c)            \
> +({                                     \
> +       unsigned long __v;              \
> +       csr_write(CSR_ISELECT, __c);    \
> +       __v = csr_read(CSR_IREG);       \
> +       __v;                            \
> +})
> +
> +#define imsic_csr_set(__c, __v)                \
> +do {                                   \
> +       csr_write(CSR_ISELECT, __c);    \
> +       csr_set(CSR_IREG, __v);         \
> +} while (0)
> +
> +#define imsic_csr_clear(__c, __v)      \
> +do {                                   \
> +       csr_write(CSR_ISELECT, __c);    \
> +       csr_clear(CSR_IREG, __v);       \
> +} while (0)
> +
> +struct imsic_mmio {
> +       phys_addr_t pa;
> +       void __iomem *va;
> +       unsigned long size;
> +};
> +
> +struct imsic_priv {
> +       /* Global configuration common for all HARTs */
> +       struct imsic_global_config global;
> +
> +       /* MMIO regions */
> +       u32 num_mmios;
> +       struct imsic_mmio *mmios;
> +
> +       /* Global state of interrupt identities */
> +       raw_spinlock_t ids_lock;
> +       unsigned long *ids_used_bimap;
> +       unsigned long *ids_enabled_bimap;
> +       unsigned int *ids_target_cpu;
> +
> +       /* Mask for connected CPUs */
> +       struct cpumask lmask;
> +
> +       /* IPI interrupt identity */
> +       bool slow_ipi;
> +       u32 ipi_id;
> +       u32 ipi_lsync_id;
> +
> +       /* IRQ domains */
> +       struct irq_domain *base_domain;
> +       struct irq_domain *pci_domain;
> +       struct irq_domain *plat_domain;
> +};
> +
> +struct imsic_handler {
> +       /* Local configuration for given HART */
> +       struct imsic_local_config local;
> +
> +       /* Pointer to private context */
> +       struct imsic_priv *priv;
> +};
> +
> +static bool imsic_init_done;
> +
> +static int imsic_parent_irq;
> +static DEFINE_PER_CPU(struct imsic_handler, imsic_handlers);
> +
> +const struct imsic_global_config *imsic_get_global_config(void)
> +{
> +       struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
> +
> +       if (!handler || !handler->priv)
> +               return NULL;
> +
> +       return &handler->priv->global;
> +}
> +EXPORT_SYMBOL_GPL(imsic_get_global_config);
> +
> +const struct imsic_local_config *imsic_get_local_config(unsigned int cpu)
> +{
> +       struct imsic_handler *handler = per_cpu_ptr(&imsic_handlers, cpu);
> +
> +       if (!handler || !handler->priv)
> +               return NULL;
> +
> +       return &handler->local;
> +}
> +EXPORT_SYMBOL_GPL(imsic_get_local_config);
> +
> +static int imsic_cpu_page_phys(unsigned int cpu,
> +                              unsigned int guest_index,
> +                              phys_addr_t *out_msi_pa)
> +{
> +       struct imsic_handler *handler = per_cpu_ptr(&imsic_handlers, cpu);
> +       struct imsic_global_config *global;
> +       struct imsic_local_config *local;
> +
> +       if (!handler || !handler->priv)
> +               return -ENODEV;
> +       local = &handler->local;
> +       global = &handler->priv->global;
> +
> +       if (BIT(global->guest_index_bits) <= guest_index)
> +               return -EINVAL;
> +
> +       if (out_msi_pa)
> +               *out_msi_pa = local->msi_pa +
> +                             (guest_index * IMSIC_MMIO_PAGE_SZ);
> +
> +       return 0;
> +}
> +
> +static int imsic_get_cpu(struct imsic_priv *priv,
> +                        const struct cpumask *mask_val, bool force,
> +                        unsigned int *out_target_cpu)
> +{
> +       struct cpumask amask;
> +       unsigned int cpu;
> +
> +       cpumask_and(&amask, &priv->lmask, mask_val);
> +
> +       if (force)
> +               cpu = cpumask_first(&amask);
> +       else
> +               cpu = cpumask_any_and(&amask, cpu_online_mask);
> +
> +       if (cpu >= nr_cpu_ids)
> +               return -EINVAL;
> +
> +       if (out_target_cpu)
> +               *out_target_cpu = cpu;
> +
> +       return 0;
> +}
> +
> +static int imsic_get_cpu_msi_msg(unsigned int cpu, unsigned int id,
> +                                struct msi_msg *msg)
> +{
> +       phys_addr_t msi_addr;
> +       int err;
> +
> +       err = imsic_cpu_page_phys(cpu, 0, &msi_addr);
> +       if (err)
> +               return err;
> +
> +       msg->address_hi = upper_32_bits(msi_addr);
> +       msg->address_lo = lower_32_bits(msi_addr);
> +       msg->data = id;
> +
> +       return err;
> +}
> +
> +static void imsic_id_set_target(struct imsic_priv *priv,
> +                                unsigned int id, unsigned int target_cpu)
> +{
> +       raw_spin_lock(&priv->ids_lock);
> +       priv->ids_target_cpu[id] = target_cpu;
> +       raw_spin_unlock(&priv->ids_lock);
> +}
> +
> +static unsigned int imsic_id_get_target(struct imsic_priv *priv,
> +                                       unsigned int id)
> +{
> +       unsigned int ret;
> +
> +       raw_spin_lock(&priv->ids_lock);

This needs to be a raw_spin_lock_irqsave() since hardirqs are enabled
here and we also take priv->ids_lock in hardirq context (via
imsic_ids_local_sync()). Booting with lockdep enabled produces this
splat:

[    1.830055] ================================
[    1.830227] WARNING: inconsistent lock state
[    1.830425] 6.1.0-rc4-00026-g78e09ffc5534 #42 Not tainted
[    1.830655] --------------------------------
[    1.830784] inconsistent {HARDIRQ-ON-W} -> {IN-HARDIRQ-W} usage.
[    1.830972] swapper/0/1 [HC1[1]:SC0[0]:HE0:SE1] takes:
[    1.831162] ff6000000229d048 (&priv->ids_lock){?.+.}-{2:2}, at:
imsic_ids_local_sync+0x1e/0x98
[    1.831951] {HARDIRQ-ON-W} state was registered at:
[    1.832132]   __lock_acquire+0x93c/0x1e0c
[    1.832305]   lock_acquire+0xfa/0x2da
[    1.832412]   _raw_spin_lock+0x2c/0x40
[    1.832531]   imsic_ids_local_sync+0x1e/0x98
[    1.832674]   imsic_starting_cpu+0x72/0xac
[    1.832852]   cpuhp_invoke_callback+0x18c/0x93c
[    1.833033]   cpuhp_thread_fun+0x156/0x1b0
[    1.833158]   smpboot_thread_fn+0xea/0x1c8
[    1.833293]   kthread+0xc8/0xde
[    1.833391]   ret_from_exception+0x0/0x10
[    1.833530] irq event stamp: 33336
[    1.833647] hardirqs last  enabled at (33335): [<ffffffff808f2fcc>]
_raw_spin_unlock_irqrestore+0x4c/0x4e
[    1.833925] hardirqs last disabled at (33336): [<ffffffff800082f4>]
__trace_hardirqs_off+0xc/0x14
[    1.834179] softirqs last  enabled at (33056): [<ffffffff808f3f3e>]
__do_softirq+0x3de/0x51e
[    1.834450] softirqs last disabled at (33051): [<ffffffff8001788a>]
irq_exit+0xd6/0x104
[    1.834705]
[    1.834705] other info that might help us debug this:
[    1.834925]  Possible unsafe locking scenario:
[    1.834925]
[    1.835093]        CPU0
[    1.835176]        ----
[    1.835253]   lock(&priv->ids_lock);
[    1.835381]   <Interrupt>
[    1.835462]     lock(&priv->ids_lock);
...

Applies here and elsewhere where priv->ids_lock is taken in a possibly
hardirqs-enabled context.

-Andrew

> +       ret = priv->ids_target_cpu[id];
> +       raw_spin_unlock(&priv->ids_lock);
> +
> +       return ret;
> +}
> +
> +static void __imsic_eix_update(unsigned long base_id,
> +                              unsigned long num_id, bool pend, bool val)
> +{
> +       unsigned long i, isel, ireg, flags;
> +       unsigned long id = base_id, last_id = base_id + num_id;
> +
> +       while (id < last_id) {
> +               isel = id / BITS_PER_LONG;
> +               isel *= BITS_PER_LONG / IMSIC_EIPx_BITS;
> +               isel += (pend) ? IMSIC_EIP0 : IMSIC_EIE0;
> +
> +               ireg = 0;
> +               for (i = id & (__riscv_xlen - 1);
> +                    (id < last_id) && (i < __riscv_xlen); i++) {
> +                       ireg |= BIT(i);
> +                       id++;
> +               }
> +
> +               /*
> +                * The IMSIC EIEx and EIPx registers are indirectly
> +                * accessed via using ISELECT and IREG CSRs so we
> +                * save/restore local IRQ to ensure that we don't
> +                * get preempted while accessing IMSIC registers.
> +                */
> +               local_irq_save(flags);
> +               if (val)
> +                       imsic_csr_set(isel, ireg);
> +               else
> +                       imsic_csr_clear(isel, ireg);
> +               local_irq_restore(flags);
> +       }
> +}
> +
> +#define __imsic_id_enable(__id)                \
> +       __imsic_eix_update((__id), 1, false, true)
> +#define __imsic_id_disable(__id)       \
> +       __imsic_eix_update((__id), 1, false, false)
> +
> +#ifdef CONFIG_SMP
> +static void __imsic_id_smp_sync(struct imsic_priv *priv)
> +{
> +       struct imsic_handler *handler;
> +       struct cpumask amask;
> +       int cpu;
> +
> +       cpumask_and(&amask, &priv->lmask, cpu_online_mask);
> +       for_each_cpu(cpu, &amask) {
> +               if (cpu == smp_processor_id())
> +                       continue;
> +
> +               handler = per_cpu_ptr(&imsic_handlers, cpu);
> +               if (!handler || !handler->priv || !handler->local.msi_va) {
> +                       pr_warn("CPU%d: handler not initialized\n", cpu);
> +                       continue;
> +               }
> +
> +               writel(handler->priv->ipi_lsync_id, handler->local.msi_va);
> +       }
> +}
> +#else
> +#define __imsic_id_smp_sync(__priv)
> +#endif
> +
> +static void imsic_id_enable(struct imsic_priv *priv, unsigned int id)
> +{
> +       raw_spin_lock(&priv->ids_lock);
> +       bitmap_set(priv->ids_enabled_bimap, id, 1);
> +       __imsic_id_enable(id);
> +       raw_spin_unlock(&priv->ids_lock);
> +
> +       __imsic_id_smp_sync(priv);
> +}
> +
> +static void imsic_id_disable(struct imsic_priv *priv, unsigned int id)
> +{
> +       raw_spin_lock(&priv->ids_lock);
> +       bitmap_clear(priv->ids_enabled_bimap, id, 1);
> +       __imsic_id_disable(id);
> +       raw_spin_unlock(&priv->ids_lock);
> +
> +       __imsic_id_smp_sync(priv);
> +}
> +
> +static void imsic_ids_local_sync(struct imsic_priv *priv)
> +{
> +       int i;
> +
> +       raw_spin_lock(&priv->ids_lock);
> +       for (i = 1; i <= priv->global.nr_ids; i++) {
> +               if (priv->ipi_id == i || priv->ipi_lsync_id == i)
> +                       continue;
> +
> +               if (test_bit(i, priv->ids_enabled_bimap))
> +                       __imsic_id_enable(i);
> +               else
> +                       __imsic_id_disable(i);
> +       }
> +       raw_spin_unlock(&priv->ids_lock);
> +}
> +
> +static void imsic_ids_local_delivery(struct imsic_priv *priv, bool enable)
> +{
> +       if (enable) {
> +               imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_ENABLE_EITHRESHOLD);
> +               imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_ENABLE_EIDELIVERY);
> +       } else {
> +               imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_DISABLE_EIDELIVERY);
> +               imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_DISABLE_EITHRESHOLD);
> +       }
> +}
> +
> +static int imsic_ids_alloc(struct imsic_priv *priv,
> +                          unsigned int max_id, unsigned int order)
> +{
> +       int ret;
> +
> +       if ((priv->global.nr_ids < max_id) ||
> +           (max_id < BIT(order)))
> +               return -EINVAL;
> +
> +       raw_spin_lock(&priv->ids_lock);
> +       ret = bitmap_find_free_region(priv->ids_used_bimap,
> +                                     max_id + 1, order);
> +       raw_spin_unlock(&priv->ids_lock);
> +
> +       return ret;
> +}
> +
> +static void imsic_ids_free(struct imsic_priv *priv, unsigned int base_id,
> +                          unsigned int order)
> +{
> +       raw_spin_lock(&priv->ids_lock);
> +       bitmap_release_region(priv->ids_used_bimap, base_id, order);
> +       raw_spin_unlock(&priv->ids_lock);
> +}
> +
> +static int __init imsic_ids_init(struct imsic_priv *priv)
> +{
> +       int i;
> +       struct imsic_global_config *global = &priv->global;
> +
> +       raw_spin_lock_init(&priv->ids_lock);
> +
> +       /* Allocate used bitmap */
> +       priv->ids_used_bimap = kcalloc(BITS_TO_LONGS(global->nr_ids + 1),
> +                                       sizeof(unsigned long), GFP_KERNEL);
> +       if (!priv->ids_used_bimap)
> +               return -ENOMEM;
> +
> +       /* Allocate enabled bitmap */
> +       priv->ids_enabled_bimap = kcalloc(BITS_TO_LONGS(global->nr_ids + 1),
> +                                          sizeof(unsigned long), GFP_KERNEL);
> +       if (!priv->ids_enabled_bimap) {
> +               kfree(priv->ids_used_bimap);
> +               return -ENOMEM;
> +       }
> +
> +       /* Allocate target CPU array */
> +       priv->ids_target_cpu = kcalloc(global->nr_ids + 1,
> +                                      sizeof(unsigned int), GFP_KERNEL);
> +       if (!priv->ids_target_cpu) {
> +               kfree(priv->ids_enabled_bimap);
> +               kfree(priv->ids_used_bimap);
> +               return -ENOMEM;
> +       }
> +       for (i = 0; i <= global->nr_ids; i++)
> +               priv->ids_target_cpu[i] = UINT_MAX;
> +
> +       /* Reserve ID#0 because it is special and never implemented */
> +       bitmap_set(priv->ids_used_bimap, 0, 1);
> +
> +       return 0;
> +}
> +
> +static void __init imsic_ids_cleanup(struct imsic_priv *priv)
> +{
> +       kfree(priv->ids_target_cpu);
> +       kfree(priv->ids_enabled_bimap);
> +       kfree(priv->ids_used_bimap);
> +}
> +
> +#ifdef CONFIG_SMP
> +static void imsic_ipi_send_mask(unsigned int parent_virq, void *data,
> +                               const struct cpumask *mask)
> +{
> +       int cpu;
> +       struct imsic_handler *handler;
> +
> +       for_each_cpu(cpu, mask) {
> +               handler = per_cpu_ptr(&imsic_handlers, cpu);
> +               if (!handler || !handler->priv || !handler->local.msi_va) {
> +                       pr_warn("CPU%d: handler not initialized\n", cpu);
> +                       continue;
> +               }
> +
> +               writel(handler->priv->ipi_id, handler->local.msi_va);
> +       }
> +}
> +
> +static struct ipi_mux_ops imsic_ipi_ops = {
> +       .ipi_mux_send = imsic_ipi_send_mask,
> +};
> +
> +static void imsic_ipi_enable(struct imsic_priv *priv)
> +{
> +       __imsic_id_enable(priv->ipi_id);
> +       __imsic_id_enable(priv->ipi_lsync_id);
> +}
> +
> +static void imsic_ipi_disable(struct imsic_priv *priv)
> +{
> +       __imsic_id_disable(priv->ipi_lsync_id);
> +       __imsic_id_disable(priv->ipi_id);
> +}
> +
> +static int __init imsic_ipi_domain_init(struct imsic_priv *priv)
> +{
> +       int virq;
> +
> +       /* Skip IPI setup if IPIs are slow */
> +       if (priv->slow_ipi)
> +               goto skip_ipi;
> +
> +       /* Allocate interrupt identity for IPIs */
> +       virq = imsic_ids_alloc(priv, priv->global.nr_ids, get_count_order(1));
> +       if (virq < 0)
> +               return virq;
> +       priv->ipi_id = virq;
> +
> +       /* Create IMSIC IPI multiplexing */
> +       virq = ipi_mux_create(0, BITS_PER_BYTE, &imsic_ipi_ops, NULL);
> +       if (virq <= 0) {
> +               imsic_ids_free(priv, priv->ipi_id, get_count_order(1));
> +               return (virq < 0) ? virq : -ENOMEM;
> +       }
> +
> +       /* Set vIRQ range */
> +       riscv_ipi_set_virq_range(virq, BITS_PER_BYTE, true);
> +
> +skip_ipi:
> +       /* Allocate interrupt identity for local enable/disable sync */
> +       virq = imsic_ids_alloc(priv, priv->global.nr_ids, get_count_order(1));
> +       if (virq < 0) {
> +               imsic_ids_free(priv, priv->ipi_id, get_count_order(1));
> +               return virq;
> +       }
> +       priv->ipi_lsync_id = virq;
> +
> +       return 0;
> +}
> +
> +static void __init imsic_ipi_domain_cleanup(struct imsic_priv *priv)
> +{
> +       imsic_ids_free(priv, priv->ipi_lsync_id, get_count_order(1));
> +       if (priv->ipi_id)
> +               imsic_ids_free(priv, priv->ipi_id, get_count_order(1));
> +}
> +#else
> +static void imsic_ipi_enable(struct imsic_priv *priv)
> +{
> +}
> +
> +static void imsic_ipi_disable(struct imsic_priv *priv)
> +{
> +}
> +
> +static int __init imsic_ipi_domain_init(struct imsic_priv *priv)
> +{
> +       /* Clear the IPI ids because we are not using IPIs */
> +       priv->ipi_id = 0;
> +       priv->ipi_lsync_id = 0;
> +       return 0;
> +}
> +
> +static void __init imsic_ipi_domain_cleanup(struct imsic_priv *priv)
> +{
> +}
> +#endif
> +
> +static void imsic_irq_mask(struct irq_data *d)
> +{
> +       imsic_id_disable(irq_data_get_irq_chip_data(d), d->hwirq);
> +}
> +
> +static void imsic_irq_unmask(struct irq_data *d)
> +{
> +       imsic_id_enable(irq_data_get_irq_chip_data(d), d->hwirq);
> +}
> +
> +static void imsic_irq_compose_msi_msg(struct irq_data *d,
> +                                     struct msi_msg *msg)
> +{
> +       struct imsic_priv *priv = irq_data_get_irq_chip_data(d);
> +       unsigned int cpu;
> +       int err;
> +
> +       cpu = imsic_id_get_target(priv, d->hwirq);
> +       WARN_ON(cpu == UINT_MAX);
> +
> +       err = imsic_get_cpu_msi_msg(cpu, d->hwirq, msg);
> +       WARN_ON(err);
> +
> +       iommu_dma_compose_msi_msg(irq_data_get_msi_desc(d), msg);
> +}
> +
> +#ifdef CONFIG_SMP
> +static int imsic_irq_set_affinity(struct irq_data *d,
> +                                 const struct cpumask *mask_val,
> +                                 bool force)
> +{
> +       struct imsic_priv *priv = irq_data_get_irq_chip_data(d);
> +       unsigned int target_cpu;
> +       int rc;
> +
> +       rc = imsic_get_cpu(priv, mask_val, force, &target_cpu);
> +       if (rc)
> +               return rc;
> +
> +       imsic_id_set_target(priv, d->hwirq, target_cpu);
> +       irq_data_update_effective_affinity(d, cpumask_of(target_cpu));
> +
> +       return IRQ_SET_MASK_OK;
> +}
> +#endif
> +
> +static struct irq_chip imsic_irq_base_chip = {
> +       .name                   = "RISC-V IMSIC-BASE",
> +       .irq_mask               = imsic_irq_mask,
> +       .irq_unmask             = imsic_irq_unmask,
> +#ifdef CONFIG_SMP
> +       .irq_set_affinity       = imsic_irq_set_affinity,
> +#endif
> +       .irq_compose_msi_msg    = imsic_irq_compose_msi_msg,
> +       .flags                  = IRQCHIP_SKIP_SET_WAKE |
> +                                 IRQCHIP_MASK_ON_SUSPEND,
> +};
> +
> +static int imsic_irq_domain_alloc(struct irq_domain *domain,
> +                                 unsigned int virq,
> +                                 unsigned int nr_irqs,
> +                                 void *args)
> +{
> +       struct imsic_priv *priv = domain->host_data;
> +       msi_alloc_info_t *info = args;
> +       phys_addr_t msi_addr;
> +       int i, hwirq, err = 0;
> +       unsigned int cpu;
> +
> +       err = imsic_get_cpu(priv, &priv->lmask, false, &cpu);
> +       if (err)
> +               return err;
> +
> +       err = imsic_cpu_page_phys(cpu, 0, &msi_addr);
> +       if (err)
> +               return err;
> +
> +       hwirq = imsic_ids_alloc(priv, priv->global.nr_ids,
> +                               get_count_order(nr_irqs));
> +       if (hwirq < 0)
> +               return hwirq;
> +
> +       err = iommu_dma_prepare_msi(info->desc, msi_addr);
> +       if (err)
> +               goto fail;
> +
> +       for (i = 0; i < nr_irqs; i++) {
> +               imsic_id_set_target(priv, hwirq + i, cpu);
> +               irq_domain_set_info(domain, virq + i, hwirq + i,
> +                                   &imsic_irq_base_chip, priv,
> +                                   handle_simple_irq, NULL, NULL);
> +               irq_set_noprobe(virq + i);
> +               irq_set_affinity(virq + i, &priv->lmask);
> +       }
> +
> +       return 0;
> +
> +fail:
> +       imsic_ids_free(priv, hwirq, get_count_order(nr_irqs));
> +       return err;
> +}
> +
> +static void imsic_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 imsic_priv *priv = domain->host_data;
> +
> +       imsic_ids_free(priv, d->hwirq, get_count_order(nr_irqs));
> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops imsic_base_domain_ops = {
> +       .alloc          = imsic_irq_domain_alloc,
> +       .free           = imsic_irq_domain_free,
> +};
> +
> +#ifdef CONFIG_RISCV_IMSIC_PCI
> +
> +static void imsic_pci_mask_irq(struct irq_data *d)
> +{
> +       pci_msi_mask_irq(d);
> +       irq_chip_mask_parent(d);
> +}
> +
> +static void imsic_pci_unmask_irq(struct irq_data *d)
> +{
> +       pci_msi_unmask_irq(d);
> +       irq_chip_unmask_parent(d);
> +}
> +
> +static struct irq_chip imsic_pci_irq_chip = {
> +       .name                   = "RISC-V IMSIC-PCI",
> +       .irq_mask               = imsic_pci_mask_irq,
> +       .irq_unmask             = imsic_pci_unmask_irq,
> +       .irq_eoi                = irq_chip_eoi_parent,
> +};
> +
> +static struct msi_domain_ops imsic_pci_domain_ops = {
> +};
> +
> +static struct msi_domain_info imsic_pci_domain_info = {
> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +                  MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI),
> +       .ops    = &imsic_pci_domain_ops,
> +       .chip   = &imsic_pci_irq_chip,
> +};
> +
> +#endif
> +
> +static struct irq_chip imsic_plat_irq_chip = {
> +       .name                   = "RISC-V IMSIC-PLAT",
> +};
> +
> +static struct msi_domain_ops imsic_plat_domain_ops = {
> +};
> +
> +static struct msi_domain_info imsic_plat_domain_info = {
> +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
> +       .ops    = &imsic_plat_domain_ops,
> +       .chip   = &imsic_plat_irq_chip,
> +};
> +
> +static int __init imsic_irq_domains_init(struct imsic_priv *priv,
> +                                        struct fwnode_handle *fwnode)
> +{
> +       /* Create Base IRQ domain */
> +       priv->base_domain = irq_domain_create_tree(fwnode,
> +                                               &imsic_base_domain_ops, priv);
> +       if (!priv->base_domain) {
> +               pr_err("Failed to create IMSIC base domain\n");
> +               return -ENOMEM;
> +       }
> +       irq_domain_update_bus_token(priv->base_domain, DOMAIN_BUS_NEXUS);
> +
> +#ifdef CONFIG_RISCV_IMSIC_PCI
> +       /* Create PCI MSI domain */
> +       priv->pci_domain = pci_msi_create_irq_domain(fwnode,
> +                                               &imsic_pci_domain_info,
> +                                               priv->base_domain);
> +       if (!priv->pci_domain) {
> +               pr_err("Failed to create IMSIC PCI domain\n");
> +               irq_domain_remove(priv->base_domain);
> +               return -ENOMEM;
> +       }
> +#endif
> +
> +       /* Create Platform MSI domain */
> +       priv->plat_domain = platform_msi_create_irq_domain(fwnode,
> +                                               &imsic_plat_domain_info,
> +                                               priv->base_domain);
> +       if (!priv->plat_domain) {
> +               pr_err("Failed to create IMSIC platform domain\n");
> +               if (priv->pci_domain)
> +                       irq_domain_remove(priv->pci_domain);
> +               irq_domain_remove(priv->base_domain);
> +               return -ENOMEM;
> +       }
> +
> +       return 0;
> +}
> +
> +/*
> + * To handle an interrupt, we read the TOPEI CSR and write zero in one
> + * instruction. If TOPEI CSR is non-zero then we translate TOPEI.ID to
> + * Linux interrupt number and let Linux IRQ subsystem handle it.
> + */
> +static void imsic_handle_irq(struct irq_desc *desc)
> +{
> +       struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
> +       struct irq_chip *chip = irq_desc_get_chip(desc);
> +       struct imsic_priv *priv = handler->priv;
> +       irq_hw_number_t hwirq;
> +       int err;
> +
> +       WARN_ON_ONCE(!handler->priv);
> +
> +       chained_irq_enter(chip, desc);
> +
> +       while ((hwirq = csr_swap(CSR_TOPEI, 0))) {
> +               hwirq = hwirq >> TOPEI_ID_SHIFT;
> +
> +               if (hwirq == priv->ipi_id) {
> +#ifdef CONFIG_SMP
> +                       ipi_mux_process();
> +#endif
> +                       continue;
> +               } else if (hwirq == priv->ipi_lsync_id) {
> +                       imsic_ids_local_sync(priv);
> +                       continue;
> +               }
> +
> +               err = generic_handle_domain_irq(priv->base_domain, hwirq);
> +               if (unlikely(err))
> +                       pr_warn_ratelimited(
> +                               "hwirq %lu mapping not found\n", hwirq);
> +       }
> +
> +       chained_irq_exit(chip, desc);
> +}
> +
> +static int imsic_dying_cpu(unsigned int cpu)
> +{
> +       struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
> +       struct imsic_priv *priv = handler->priv;
> +
> +       /* Disable per-CPU parent interrupt */
> +       if (imsic_parent_irq)
> +               disable_percpu_irq(imsic_parent_irq);
> +
> +       /* Locally disable interrupt delivery */
> +       imsic_ids_local_delivery(priv, false);
> +
> +       /* Disable IPIs */
> +       imsic_ipi_disable(priv);
> +
> +       return 0;
> +}
> +
> +static int imsic_starting_cpu(unsigned int cpu)
> +{
> +       struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
> +       struct imsic_priv *priv = handler->priv;
> +
> +       /* Enable per-CPU parent interrupt */
> +       if (imsic_parent_irq)
> +               enable_percpu_irq(imsic_parent_irq,
> +                                 irq_get_trigger_type(imsic_parent_irq));
> +       else
> +               pr_warn("cpu%d: parent irq not available\n", cpu);
> +
> +       /* Enable IPIs */
> +       imsic_ipi_enable(priv);
> +
> +       /*
> +        * Interrupts identities might have been enabled/disabled while
> +        * this CPU was not running so sync-up local enable/disable state.
> +        */
> +       imsic_ids_local_sync(priv);
> +
> +       /* Locally enable interrupt delivery */
> +       imsic_ids_local_delivery(priv, true);
> +
> +       return 0;
> +}
> +
> +struct imsic_fwnode_ops {
> +       u32 (*nr_parent_irq)(struct fwnode_handle *fwnode,
> +                            void *fwopaque);
> +       int (*parent_hartid)(struct fwnode_handle *fwnode,
> +                            void *fwopaque, u32 index,
> +                            unsigned long *out_hartid);
> +       u32 (*nr_mmio)(struct fwnode_handle *fwnode, void *fwopaque);
> +       int (*mmio_to_resource)(struct fwnode_handle *fwnode,
> +                               void *fwopaque, u32 index,
> +                               struct resource *res);
> +       void __iomem *(*mmio_map)(struct fwnode_handle *fwnode,
> +                                 void *fwopaque, u32 index);
> +       int (*read_u32)(struct fwnode_handle *fwnode,
> +                       void *fwopaque, const char *prop, u32 *out_val);
> +       bool (*read_bool)(struct fwnode_handle *fwnode,
> +                         void *fwopaque, const char *prop);
> +};
> +
> +static int __init imsic_init(struct imsic_fwnode_ops *fwops,
> +                            struct fwnode_handle *fwnode,
> +                            void *fwopaque)
> +{
> +       struct resource res;
> +       phys_addr_t base_addr;
> +       int rc, nr_parent_irqs;
> +       struct imsic_mmio *mmio;
> +       struct imsic_priv *priv;
> +       struct irq_domain *domain;
> +       struct imsic_handler *handler;
> +       struct imsic_global_config *global;
> +       u32 i, tmp, nr_handlers = 0;
> +
> +       if (imsic_init_done) {
> +               pr_err("%pfwP: already initialized hence ignoring\n",
> +                       fwnode);
> +               return -ENODEV;
> +       }
> +
> +       if (!riscv_isa_extension_available(NULL, SxAIA)) {
> +               pr_err("%pfwP: AIA support not available\n", fwnode);
> +               return -ENODEV;
> +       }
> +
> +       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> +       if (!priv)
> +               return -ENOMEM;
> +       global = &priv->global;
> +
> +       /* Find number of parent interrupts */
> +       nr_parent_irqs = fwops->nr_parent_irq(fwnode, fwopaque);
> +       if (!nr_parent_irqs) {
> +               pr_err("%pfwP: no parent irqs available\n", fwnode);
> +               return -EINVAL;
> +       }
> +
> +       /* Find number of guest index bits in MSI address */
> +       rc = fwops->read_u32(fwnode, fwopaque, "riscv,guest-index-bits",
> +                            &global->guest_index_bits);
> +       if (rc)
> +               global->guest_index_bits = 0;
> +       tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT;
> +       if (tmp < global->guest_index_bits) {
> +               pr_err("%pfwP: guest index bits too big\n", fwnode);
> +               return -EINVAL;
> +       }
> +
> +       /* Find number of HART index bits */
> +       rc = fwops->read_u32(fwnode, fwopaque, "riscv,hart-index-bits",
> +                            &global->hart_index_bits);
> +       if (rc) {
> +               /* Assume default value */
> +               global->hart_index_bits = __fls(nr_parent_irqs);
> +               if (BIT(global->hart_index_bits) < nr_parent_irqs)
> +                       global->hart_index_bits++;
> +       }
> +       tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT -
> +             global->guest_index_bits;
> +       if (tmp < global->hart_index_bits) {
> +               pr_err("%pfwP: HART index bits too big\n", fwnode);
> +               return -EINVAL;
> +       }
> +
> +       /* Find number of group index bits */
> +       rc = fwops->read_u32(fwnode, fwopaque, "riscv,group-index-bits",
> +                            &global->group_index_bits);
> +       if (rc)
> +               global->group_index_bits = 0;
> +       tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT -
> +             global->guest_index_bits - global->hart_index_bits;
> +       if (tmp < global->group_index_bits) {
> +               pr_err("%pfwP: group index bits too big\n", fwnode);
> +               return -EINVAL;
> +       }
> +
> +       /* Find first bit position of group index */
> +       tmp = IMSIC_MMIO_PAGE_SHIFT * 2;
> +       rc = fwops->read_u32(fwnode, fwopaque, "riscv,group-index-shift",
> +                            &global->group_index_shift);
> +       if (rc)
> +               global->group_index_shift = tmp;
> +       if (global->group_index_shift < tmp) {
> +               pr_err("%pfwP: group index shift too small\n", fwnode);
> +               return -EINVAL;
> +       }
> +       tmp = global->group_index_bits + global->group_index_shift - 1;
> +       if (tmp >= BITS_PER_LONG) {
> +               pr_err("%pfwP: group index shift too big\n", fwnode);
> +               return -EINVAL;
> +       }
> +
> +       /* Find number of interrupt identities */
> +       rc = fwops->read_u32(fwnode, fwopaque, "riscv,num-ids",
> +                            &global->nr_ids);
> +       if (rc) {
> +               pr_err("%pfwP: number of interrupt identities not found\n",
> +                       fwnode);
> +               return rc;
> +       }
> +       if ((global->nr_ids < IMSIC_MIN_ID) ||
> +           (global->nr_ids >= IMSIC_MAX_ID) ||
> +           ((global->nr_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID)) {
> +               pr_err("%pfwP: invalid number of interrupt identities\n",
> +                       fwnode);
> +               return -EINVAL;
> +       }
> +
> +       /* Find number of guest interrupt identities */
> +       if (fwops->read_u32(fwnode, fwopaque, "riscv,num-guest-ids",
> +                           &global->nr_guest_ids))
> +               global->nr_guest_ids = global->nr_ids;
> +       if ((global->nr_guest_ids < IMSIC_MIN_ID) ||
> +           (global->nr_guest_ids >= IMSIC_MAX_ID) ||
> +           ((global->nr_guest_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID)) {
> +               pr_err("%pfwP: invalid number of guest interrupt identities\n",
> +                       fwnode);
> +               return -EINVAL;
> +       }
> +
> +       /* Check if IPIs are slow */
> +       priv->slow_ipi = fwops->read_bool(fwnode, fwopaque, "riscv,slow-ipi");
> +
> +       /* Compute base address */
> +       rc = fwops->mmio_to_resource(fwnode, fwopaque, 0, &res);
> +       if (rc) {
> +               pr_err("%pfwP: first MMIO resource not found\n", fwnode);
> +               return -EINVAL;
> +       }
> +       global->base_addr = res.start;
> +       global->base_addr &= ~(BIT(global->guest_index_bits +
> +                                  global->hart_index_bits +
> +                                  IMSIC_MMIO_PAGE_SHIFT) - 1);
> +       global->base_addr &= ~((BIT(global->group_index_bits) - 1) <<
> +                              global->group_index_shift);
> +
> +       /* Find number of MMIO register sets */
> +       priv->num_mmios = fwops->nr_mmio(fwnode, fwopaque);
> +
> +       /* Allocate MMIO register sets */
> +       priv->mmios = kcalloc(priv->num_mmios, sizeof(*mmio), GFP_KERNEL);
> +       if (!priv->mmios) {
> +               rc = -ENOMEM;
> +               goto out_free_priv;
> +       }
> +
> +       /* Parse and map MMIO register sets */
> +       for (i = 0; i < priv->num_mmios; i++) {
> +               mmio = &priv->mmios[i];
> +               rc = fwops->mmio_to_resource(fwnode, fwopaque, i, &res);
> +               if (rc) {
> +                       pr_err("%pfwP: unable to parse MMIO regset %d\n",
> +                               fwnode, i);
> +                       goto out_iounmap;
> +               }
> +               mmio->pa = res.start;
> +               mmio->size = res.end - res.start + 1;
> +
> +               base_addr = mmio->pa;
> +               base_addr &= ~(BIT(global->guest_index_bits +
> +                                  global->hart_index_bits +
> +                                  IMSIC_MMIO_PAGE_SHIFT) - 1);
> +               base_addr &= ~((BIT(global->group_index_bits) - 1) <<
> +                              global->group_index_shift);
> +               if (base_addr != global->base_addr) {
> +                       rc = -EINVAL;
> +                       pr_err("%pfwP: address mismatch for regset %d\n",
> +                               fwnode, i);
> +                       goto out_iounmap;
> +               }
> +
> +               tmp = BIT(global->guest_index_bits) - 1;
> +               if ((mmio->size / IMSIC_MMIO_PAGE_SZ) & tmp) {
> +                       rc = -EINVAL;
> +                       pr_err("%pfwP: size mismatch for regset %d\n",
> +                               fwnode, i);
> +                       goto out_iounmap;
> +               }
> +
> +               mmio->va = fwops->mmio_map(fwnode, fwopaque, i);
> +               if (!mmio->va) {
> +                       rc = -EIO;
> +                       pr_err("%pfwP: unable to map MMIO regset %d\n",
> +                               fwnode, i);
> +                       goto out_iounmap;
> +               }
> +       }
> +
> +       /* Initialize interrupt identity management */
> +       rc = imsic_ids_init(priv);
> +       if (rc) {
> +               pr_err("%pfwP: failed to initialize interrupt management\n",
> +                      fwnode);
> +               goto out_iounmap;
> +       }
> +
> +       /* Configure handlers for target CPUs */
> +       for (i = 0; i < nr_parent_irqs; i++) {
> +               unsigned long reloff, hartid;
> +               int j, cpu;
> +
> +               rc = fwops->parent_hartid(fwnode, fwopaque, i, &hartid);
> +               if (rc) {
> +                       pr_warn("%pfwP: hart ID for parent irq%d not found\n",
> +                               fwnode, i);
> +                       continue;
> +               }
> +
> +               cpu = riscv_hartid_to_cpuid(hartid);
> +               if (cpu < 0) {
> +                       pr_warn("%pfwP: invalid cpuid for parent irq%d\n",
> +                               fwnode, i);
> +                       continue;
> +               }
> +
> +               /* Find MMIO location of MSI page */
> +               mmio = NULL;
> +               reloff = i * BIT(global->guest_index_bits) *
> +                        IMSIC_MMIO_PAGE_SZ;
> +               for (j = 0; priv->num_mmios; j++) {
> +                       if (reloff < priv->mmios[j].size) {
> +                               mmio = &priv->mmios[j];
> +                               break;
> +                       }
> +
> +                       reloff -= priv->mmios[j].size;
> +               }
> +               if (!mmio) {
> +                       pr_warn("%pfwP: MMIO not found for parent irq%d\n",
> +                               fwnode, i);
> +                       continue;
> +               }
> +
> +               handler = per_cpu_ptr(&imsic_handlers, cpu);
> +               if (handler->priv) {
> +                       pr_warn("%pfwP: CPU%d handler already configured.\n",
> +                               fwnode, cpu);
> +                       goto done;
> +               }
> +
> +               cpumask_set_cpu(cpu, &priv->lmask);
> +               handler->local.msi_pa = mmio->pa + reloff;
> +               handler->local.msi_va = mmio->va + reloff;
> +               handler->priv = priv;
> +
> +done:
> +               nr_handlers++;
> +       }
> +
> +       /* If no CPU handlers found then can't take interrupts */
> +       if (!nr_handlers) {
> +               pr_err("%pfwP: No CPU handlers found\n", fwnode);
> +               rc = -ENODEV;
> +               goto out_ids_cleanup;
> +       }
> +
> +       /* Find parent domain and register chained handler */
> +       domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(),
> +                                         DOMAIN_BUS_ANY);
> +       if (!domain) {
> +               pr_err("%pfwP: Failed to find INTC domain\n", fwnode);
> +               rc = -ENOENT;
> +               goto out_ids_cleanup;
> +       }
> +       imsic_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT);
> +       if (!imsic_parent_irq) {
> +               pr_err("%pfwP: Failed to create INTC mapping\n", fwnode);
> +               rc = -ENOENT;
> +               goto out_ids_cleanup;
> +       }
> +       irq_set_chained_handler(imsic_parent_irq, imsic_handle_irq);
> +
> +       /* Initialize IPI domain */
> +       rc = imsic_ipi_domain_init(priv);
> +       if (rc) {
> +               pr_err("%pfwP: Failed to initialize IPI domain\n", fwnode);
> +               goto out_ids_cleanup;
> +       }
> +
> +       /* Initialize IRQ and MSI domains */
> +       rc = imsic_irq_domains_init(priv, fwnode);
> +       if (rc) {
> +               pr_err("%pfwP: Failed to initialize IRQ and MSI domains\n",
> +                      fwnode);
> +               goto out_ipi_domain_cleanup;
> +       }
> +
> +       /* Setup cpuhp state */
> +       cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
> +                         "irqchip/riscv/imsic:starting",
> +                         imsic_starting_cpu, imsic_dying_cpu);
> +
> +       /*
> +        * Only one IMSIC instance allowed in a platform for clean
> +        * implementation of SMP IRQ affinity and per-CPU IPIs.
> +        *
> +        * This means on a multi-socket (or multi-die) platform we
> +        * will have multiple MMIO regions for one IMSIC instance.
> +        */
> +       imsic_init_done = true;
> +
> +       pr_info("%pfwP:  hart-index-bits: %d,  guest-index-bits: %d\n",
> +               fwnode, global->hart_index_bits, global->guest_index_bits);
> +       pr_info("%pfwP: group-index-bits: %d, group-index-shift: %d\n",
> +               fwnode, global->group_index_bits, global->group_index_shift);
> +       pr_info("%pfwP: mapped %d interrupts for %d CPUs at %pa\n",
> +               fwnode, global->nr_ids, nr_handlers, &global->base_addr);
> +       if (priv->ipi_lsync_id)
> +               pr_info("%pfwP: enable/disable sync using interrupt %d\n",
> +                       fwnode, priv->ipi_lsync_id);
> +       if (priv->ipi_id)
> +               pr_info("%pfwP: providing IPIs using interrupt %d\n",
> +                       fwnode, priv->ipi_id);
> +
> +       return 0;
> +
> +out_ipi_domain_cleanup:
> +       imsic_ipi_domain_cleanup(priv);
> +out_ids_cleanup:
> +       imsic_ids_cleanup(priv);
> +out_iounmap:
> +       for (i = 0; i < priv->num_mmios; i++) {
> +               if (priv->mmios[i].va)
> +                       iounmap(priv->mmios[i].va);
> +       }
> +       kfree(priv->mmios);
> +out_free_priv:
> +       kfree(priv);
> +       return rc;
> +}
> +
> +static u32 __init imsic_dt_nr_parent_irq(struct fwnode_handle *fwnode,
> +                                        void *fwopaque)
> +{
> +       return of_irq_count(to_of_node(fwnode));
> +}
> +
> +static int __init imsic_dt_parent_hartid(struct fwnode_handle *fwnode,
> +                                        void *fwopaque, u32 index,
> +                                        unsigned long *out_hartid)
> +{
> +       struct of_phandle_args parent;
> +       int rc;
> +
> +       rc = of_irq_parse_one(to_of_node(fwnode), index, &parent);
> +       if (rc)
> +               return rc;
> +
> +       /*
> +        * Skip interrupts other than external interrupts for
> +        * current privilege level.
> +        */
> +       if (parent.args[0] != RV_IRQ_EXT)
> +               return -EINVAL;
> +
> +       return riscv_of_parent_hartid(parent.np, out_hartid);
> +}
> +
> +static u32 __init imsic_dt_nr_mmio(struct fwnode_handle *fwnode,
> +                                  void *fwopaque)
> +{
> +       u32 ret = 0;
> +       struct resource res;
> +
> +       while (!of_address_to_resource(to_of_node(fwnode), ret, &res))
> +               ret++;
> +
> +       return ret;
> +}
> +
> +static int __init imsic_mmio_to_resource(struct fwnode_handle *fwnode,
> +                                        void *fwopaque, u32 index,
> +                                        struct resource *res)
> +{
> +       return of_address_to_resource(to_of_node(fwnode), index, res);
> +}
> +
> +static void __iomem __init *imsic_dt_mmio_map(struct fwnode_handle *fwnode,
> +                                             void *fwopaque, u32 index)
> +{
> +       return of_iomap(to_of_node(fwnode), index);
> +}
> +
> +static int __init imsic_dt_read_u32(struct fwnode_handle *fwnode,
> +                                   void *fwopaque, const char *prop,
> +                                   u32 *out_val)
> +{
> +       return of_property_read_u32(to_of_node(fwnode), prop, out_val);
> +}
> +
> +static bool __init imsic_dt_read_bool(struct fwnode_handle *fwnode,
> +                                     void *fwopaque, const char *prop)
> +{
> +       return of_property_read_bool(to_of_node(fwnode), prop);
> +}
> +
> +static int __init imsic_dt_init(struct device_node *node,
> +                               struct device_node *parent)
> +{
> +       struct imsic_fwnode_ops ops = {
> +               .nr_parent_irq = imsic_dt_nr_parent_irq,
> +               .parent_hartid = imsic_dt_parent_hartid,
> +               .nr_mmio = imsic_dt_nr_mmio,
> +               .mmio_to_resource = imsic_mmio_to_resource,
> +               .mmio_map = imsic_dt_mmio_map,
> +               .read_u32 = imsic_dt_read_u32,
> +               .read_bool = imsic_dt_read_bool,
> +       };
> +
> +       return imsic_init(&ops, &node->fwnode, NULL);
> +}
> +IRQCHIP_DECLARE(riscv_imsic, "riscv,imsics", imsic_dt_init);
> diff --git a/include/linux/irqchip/riscv-imsic.h b/include/linux/irqchip/riscv-imsic.h
> new file mode 100644
> index 000000000000..5d1387adc0ba
> --- /dev/null
> +++ b/include/linux/irqchip/riscv-imsic.h
> @@ -0,0 +1,92 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2021 Western Digital Corporation or its affiliates.
> + * Copyright (C) 2022 Ventana Micro Systems Inc.
> + */
> +#ifndef __LINUX_IRQCHIP_RISCV_IMSIC_H
> +#define __LINUX_IRQCHIP_RISCV_IMSIC_H
> +
> +#include <linux/types.h>
> +#include <asm/csr.h>
> +
> +#define IMSIC_MMIO_PAGE_SHIFT          12
> +#define IMSIC_MMIO_PAGE_SZ             (1UL << IMSIC_MMIO_PAGE_SHIFT)
> +#define IMSIC_MMIO_PAGE_LE             0x00
> +#define IMSIC_MMIO_PAGE_BE             0x04
> +
> +#define IMSIC_MIN_ID                   63
> +#define IMSIC_MAX_ID                   2048
> +
> +#define IMSIC_EIDELIVERY               0x70
> +
> +#define IMSIC_EITHRESHOLD              0x72
> +
> +#define IMSIC_EIP0                     0x80
> +#define IMSIC_EIP63                    0xbf
> +#define IMSIC_EIPx_BITS                        32
> +
> +#define IMSIC_EIE0                     0xc0
> +#define IMSIC_EIE63                    0xff
> +#define IMSIC_EIEx_BITS                        32
> +
> +#define IMSIC_FIRST                    IMSIC_EIDELIVERY
> +#define IMSIC_LAST                     IMSIC_EIE63
> +
> +#define IMSIC_MMIO_SETIPNUM_LE         0x00
> +#define IMSIC_MMIO_SETIPNUM_BE         0x04
> +
> +struct imsic_global_config {
> +       /*
> +        * MSI Target Address Scheme
> +        *
> +        * XLEN-1                                                12     0
> +        * |                                                     |     |
> +        * -------------------------------------------------------------
> +        * |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
> +        * -------------------------------------------------------------
> +        */
> +
> +       /* Bits representing Guest index, HART index, and Group index */
> +       u32 guest_index_bits;
> +       u32 hart_index_bits;
> +       u32 group_index_bits;
> +       u32 group_index_shift;
> +
> +       /* Global base address matching all target MSI addresses */
> +       phys_addr_t base_addr;
> +
> +       /* Number of interrupt identities */
> +       u32 nr_ids;
> +
> +       /* Number of guest interrupt identities */
> +       u32 nr_guest_ids;
> +};
> +
> +struct imsic_local_config {
> +       phys_addr_t msi_pa;
> +       void __iomem *msi_va;
> +};
> +
> +#ifdef CONFIG_RISCV_IMSIC
> +
> +extern const struct imsic_global_config *imsic_get_global_config(void);
> +
> +extern const struct imsic_local_config *imsic_get_local_config(
> +                                                       unsigned int cpu);
> +
> +#else
> +
> +static inline const struct imsic_global_config *imsic_get_global_config(void)
> +{
> +       return NULL;
> +}
> +
> +static inline const struct imsic_local_config *imsic_get_local_config(
> +                                                       unsigned int cpu)
> +{
> +       return NULL;
> +}
> +
> +#endif
> +
> +#endif
> --
> 2.34.1
>

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 0/9] Linux RISC-V AIA Support
  2022-11-11  9:13     ` Atish Patra
@ 2022-11-11 19:01       ` Atish Patra
  -1 siblings, 0 replies; 96+ messages in thread
From: Atish Patra @ 2022-11-11 19:01 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Alistair Francis, Anup Patel,
	linux-riscv, linux-kernel, devicetree

On Fri, Nov 11, 2022 at 1:13 AM Atish Patra <atishp@atishpatra.org> wrote:
>
> On Fri, Nov 11, 2022 at 1:07 AM Atish Patra <atishp@atishpatra.org> wrote:
> >
> > On Thu, Nov 10, 2022 at 8:42 PM Anup Patel <apatel@ventanamicro.com> wrote:
> > >
> > > The RISC-V AIA specification is now frozen as-per the RISC-V international
> > > process. The latest frozen specifcation can be found at:
> > > https://github.com/riscv/riscv-aia/releases/download/1.0-RC1/riscv-interrupts-1.0-RC1.pdf
> > >
> > > At a high-level, the AIA specification adds three things:
> > > 1) AIA CSRs
> > >    - Improved local interrupt support
> > > 2) Incoming Message Signaled Interrupt Controller (IMSIC)
> > >    - Per-HART MSI controller
> > >    - Support MSI virtualization
> > >    - Support IPI along with virtualization
> > > 3) Advanced Platform-Level Interrupt Controller (APLIC)
> > >    - Wired interrupt controller
> > >    - In MSI-mode, converts wired interrupt into MSIs (i.e. MSI generator)
> > >    - In Direct-mode, injects external interrupts directly into HARTs
> > >
> > > For an overview of the AIA specification, refer the recent AIA virtualization
> > > talk at KVM Forum 2022:
> > > https://static.sched.com/hosted_files/kvmforum2022/a1/AIA_Virtualization_in_KVM_RISCV_final.pdf
> > > https://www.youtube.com/watch?v=r071dL8Z0yo
> > >
> > > This series adds required Linux irqchip drivers for AIA and it depends on
> > > the recent "RISC-V IPI Improvements".
> > > (Refer, https://lore.kernel.org/lkml/20221101143400.690000-1-apatel@ventanamicro.com/t/)
> > >
> > > To test this series, use QEMU v7.1 (or higher) and OpenSBI v1.1 (or higher).
> > >
> > > These patches can also be found in the riscv_aia_v1 branch at:
> > > https://github.com/avpatel/linux.git
> > >
> > > Anup Patel (9):
> > >   RISC-V: Add AIA related CSR defines
> > >   RISC-V: Detect AIA CSRs from ISA string
> > >   irqchip/riscv-intc: Add support for RISC-V AIA
> > >   dt-bindings: Add RISC-V incoming MSI controller bindings
> > >   irqchip: Add RISC-V incoming MSI controller driver
> > >   dt-bindings: Add RISC-V advanced PLIC bindings
> > >   irqchip: Add RISC-V advanced PLIC driver
> > >   RISC-V: Select APLIC and IMSIC drivers for QEMU virt machine
> > >   MAINTAINERS: Add entry for RISC-V AIA drivers
> > >
> > >  .../interrupt-controller/riscv,aplic.yaml     |  136 ++
> > >  .../interrupt-controller/riscv,imsic.yaml     |  174 +++
> > >  MAINTAINERS                                   |   12 +
> > >  arch/riscv/Kconfig.socs                       |    2 +
> > >  arch/riscv/include/asm/csr.h                  |   92 ++
> > >  arch/riscv/include/asm/hwcap.h                |    8 +
> > >  arch/riscv/kernel/cpu.c                       |    2 +
> > >  arch/riscv/kernel/cpufeature.c                |    2 +
> > >  drivers/irqchip/Kconfig                       |   32 +-
> > >  drivers/irqchip/Makefile                      |    2 +
> > >  drivers/irqchip/irq-riscv-aplic.c             |  656 +++++++++
> > >  drivers/irqchip/irq-riscv-imsic.c             | 1207 +++++++++++++++++
> > >  drivers/irqchip/irq-riscv-intc.c              |   37 +-
> > >  include/linux/irqchip/riscv-aplic.h           |  117 ++
> > >  include/linux/irqchip/riscv-imsic.h           |   92 ++
> > >  15 files changed, 2564 insertions(+), 7 deletions(-)
> > >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> > >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > >  create mode 100644 drivers/irqchip/irq-riscv-aplic.c
> > >  create mode 100644 drivers/irqchip/irq-riscv-imsic.c
> > >  create mode 100644 include/linux/irqchip/riscv-aplic.h
> > >  create mode 100644 include/linux/irqchip/riscv-imsic.h
> > >
> > > --
> > > 2.34.1
> > >
> >
> > I am seeing the following boot failure with your branch and upstream
> > qemu (tag: v7.2.0-rc0).
> > It seems IPIs are probably not getting delivered after a point. I saw
> > that IPIs are delivered in the same path earlier (via gdb).
> >
> > [    0.990152] NET: Registered PF_INET6 protocol family
> > [    1.004885] Segment Routing with IPv6
> > [    1.005385] In-situ OAM (IOAM) with IPv6
> > [    1.006371] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
> > [    1.011609] NET: Registered PF_PACKET protocol family
> > [    1.014877] 9pnet: Installing 9P2000 support
> > [    1.015603] Key type dns_resolver registered
> > [    1.026396] debug_vm_pgtable: [debug_vm_pgtable         ]:
> > Validating architecture page table helpers
> > [    1.144097] EXT4-fs (vda2): recovery complete
> > [    1.146345] EXT4-fs (vda2): mounted filesystem with ordered data
> > mode. Quota mode: disabled.
> > [    1.147020] VFS: Mounted root (ext4 filesystem) on device 254:2.
> > [    1.152238] devtmpfs: mounted
> > [    1.183914] Freeing unused kernel image (initmem) memory: 2176K
> > [    1.185898] Run /sbin/init as init process
> > [   29.412287] rcu: INFO: rcu_sched self-detected stall on CPU
> > [   29.412992] rcu:     3-....: (5250 ticks this GP)
> > idle=fc3c/1/0x4000000000000002 softirq=78/78 fqs=2230
> > [   29.413427]  (t=5251 jiffies g=-1047 q=3 ncpus=8)
> > [   29.414199] CPU: 3 PID: 1 Comm: init Not tainted
> > 6.1.0-rc4-00024-g5b711f2d7b91 #198
> > [   29.414578] Hardware name: riscv-virtio,qemu (DT)
> > [   29.414896] epc : smp_call_function_many_cond+0x138/0x372
> > [   29.415157]  ra : smp_call_function_many_cond+0x154/0x372
> > [   29.415318] epc : ffffffff8008d2e6 ra : ffffffff8008d302 sp :
> > ff2000000004bb40
> > [   29.415512]  gp : ffffffff812e9eb0 tp : ff600000016d8000 t0 :
> > ff6000007ed88610
> > [   29.415686]  t1 : 00000000000000ff t2 : 0000000000000002 s0 :
> > ff2000000004bc00
> > [   29.415837]  s1 : ff6000007ed85448 a0 : 0000000000000007 a1 :
> > 00000000000000f7
> > [   29.416008]  a2 : 0000000000000000 a3 : 0000000000000000 a4 :
> > ff6000007edd1780
> > [   29.416188]  a5 : 0000000000000001 a6 : ffffffff812eb1c0 a7 :
> > ff600000016d8000
> > [   29.416613]  s2 : ffffffff81323c30 s3 : ffffffff812e9964 s4 :
> > 0000000000000000
> > [   29.416810]  s5 : 0000000000000000 s6 : ff6000007ed85440 s7 :
> > 0000000000000038
> > [   29.416997]  s8 : 0000000000000003 s9 : ffffffff81323c30 s10:
> > ff6000007ed85448
> > [   29.417159]  s11: 0000000000000008 t3 : 00ffffffad08a000 t4 :
> > ff60000001613e0c
> > [   29.417331]  t5 : 0000000000000000 t6 : 00ffffffad177fff
> > [   29.417482] status: 0000000200000120 badaddr: 0000000000000000
> > cause: 8000000000000005
> > [   29.417939] [<ffffffff8008d590>] on_each_cpu_cond_mask+0x20/0x32
> > [   29.418179] [<ffffffff80008d32>] flush_icache_all+0x38/0x40
> > [   29.418324] [<ffffffff80008eb2>] flush_icache_pte+0x4a/0x7a
> > [   29.418442] [<ffffffff80139e00>] do_set_pte+0x132/0x192
> > [   29.418594] [<ffffffff8010b42e>] filemap_map_pages+0x178/0x3a0
> > [   29.418738] [<ffffffff8013ad00>] __handle_mm_fault+0x992/0xbac
> > [   29.418876] [<ffffffff8013afde>] handle_mm_fault+0xc4/0x1d4
> > [   29.419010] [<ffffffff80008372>] do_page_fault+0x120/0x326
> > [   29.419145] [<ffffffff800033e6>] ret_from_exception+0x0/0xc
> >
> >
>
> Sorry. I forgot to mention that this happens while booting Fedora on
> an 8 cpu virt machine.
> I am yet to reproduce this issue for 4 or 2 cpus.
>

This warning splat appears with 4 cpus though.

[  126.977213] e1000e 0000:00:02.0 enp0s2: NIC Link is Up 1000 Mbps
Full Duplex, Flow Control: Rx/Tx
[  126.988275] IPv6: ADDRCONF(NETDEV_CHANGE): enp0s2: link becomes ready
[  128.210090] ------------[ cut here ]------------
[  128.210476] WARNING: CPU: 0 PID: 294 at
drivers/irqchip/irq-riscv-aplic.c:318 aplic_msi_write_msg+0x110/0x12a
[  128.211702] Modules linked in:
[  128.212473] CPU: 0 PID: 294 Comm: (agetty) Not tainted
6.1.0-rc4-00024-g5b711f2d7b91 #198
[  128.212839] Hardware name: riscv-virtio,qemu (DT)
[  128.213459] epc : aplic_msi_write_msg+0x110/0x12a
[  128.213652]  ra : aplic_msi_write_msg+0x1c/0x12a
[  128.213823] epc : ffffffff8039eb80 ra : ffffffff8039ea8c sp :
ff2000001054bb10
[  128.213884]  gp : ffffffff812e9eb0 tp : ff600000032d3840 t0 :
0000000000000002
[  128.213945]  t1 : 0000000000000008 t2 : 0000000000000040 s0 :
ff2000001054bb30
[  128.214015]  s1 : ff600000018443a8 a0 : ff600000017e8c30 a1 :
0000000000000000
[  128.214083]  a2 : 0000000000000001 a3 : 000000000000000c a4 :
0000000000000003
[  128.214141]  a5 : 0000000000000000 a6 : 0000000000000000 a7 :
0000000000000000
[  128.214173]  s2 : ff2000001054bb40 s3 : ff600000017e8c30 s4 :
0000000200000022
[  128.214246]  s5 : ff600000017e8d78 s6 : 000000000000000c s7 :
ff600000017e8cb0
[  128.214295]  s8 : ffffffff80c5c8f0 s9 : ff6000000e97ab60 s10:
0000000000000000
[  128.214327]  s11: ff6000000e97ab78 t3 : 0000000000000004 t4 :
0000000000000002
[  128.214383]  t5 : 0000000000000000 t6 : 0000000000028000
[  128.214414] status: 0000000200000100 badaddr: 0000000000000000
cause: 0000000000000003
[  128.215267] [<ffffffff80453060>] platform_msi_write_msg+0x18/0x20
[  128.215413] [<ffffffff80060b7a>] msi_domain_deactivate+0x2a/0x48
[  128.215432] [<ffffffff8005e5a2>] __irq_domain_deactivate_irq+0x1c/0x30
[  128.215450] [<ffffffff8005e5aa>] __irq_domain_deactivate_irq+0x24/0x30
[  128.215464] [<ffffffff8005f9ea>] irq_domain_deactivate_irq+0x1c/0x30
[  128.215477] [<ffffffff8005b8f6>] free_irq+0x1d0/0x27c
[  128.219393] [<ffffffff8041be06>] univ8250_release_irq+0xa4/0xaa
[  128.219417] [<ffffffff8041e018>] serial8250_do_shutdown+0xf0/0x166
[  128.219431] [<ffffffff8041e0a8>] serial8250_shutdown+0x1a/0x22
[  128.219444] [<ffffffff804174f0>] uart_port_shutdown+0x2c/0x42
[  128.220230] [<ffffffff8041908a>] uart_shutdown+0x66/0x11e
[  128.220247] [<ffffffff8041a69a>] uart_hangup+0x5e/0x100
[  128.220260] [<ffffffff803fdaa2>] __tty_hangup.part.0+0x286/0x2ac
[  128.220859] [<ffffffff803fec6c>] tty_ioctl+0x684/0x802
[  128.220875] [<ffffffff8019ec48>] sys_ioctl+0x37e/0x95e
[  128.221284] [<ffffffff800033d8>] ret_from_syscall+0x0/0x2
[  128.221653] ---[ end trace 0000000000000000 ]---


> >
> > --
> > Regards,
> > Atish
>
>
>
> --
> Regards,
> Atish



-- 
Regards,
Atish

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

* Re: [PATCH 0/9] Linux RISC-V AIA Support
@ 2022-11-11 19:01       ` Atish Patra
  0 siblings, 0 replies; 96+ messages in thread
From: Atish Patra @ 2022-11-11 19:01 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Alistair Francis, Anup Patel,
	linux-riscv, linux-kernel, devicetree

On Fri, Nov 11, 2022 at 1:13 AM Atish Patra <atishp@atishpatra.org> wrote:
>
> On Fri, Nov 11, 2022 at 1:07 AM Atish Patra <atishp@atishpatra.org> wrote:
> >
> > On Thu, Nov 10, 2022 at 8:42 PM Anup Patel <apatel@ventanamicro.com> wrote:
> > >
> > > The RISC-V AIA specification is now frozen as-per the RISC-V international
> > > process. The latest frozen specifcation can be found at:
> > > https://github.com/riscv/riscv-aia/releases/download/1.0-RC1/riscv-interrupts-1.0-RC1.pdf
> > >
> > > At a high-level, the AIA specification adds three things:
> > > 1) AIA CSRs
> > >    - Improved local interrupt support
> > > 2) Incoming Message Signaled Interrupt Controller (IMSIC)
> > >    - Per-HART MSI controller
> > >    - Support MSI virtualization
> > >    - Support IPI along with virtualization
> > > 3) Advanced Platform-Level Interrupt Controller (APLIC)
> > >    - Wired interrupt controller
> > >    - In MSI-mode, converts wired interrupt into MSIs (i.e. MSI generator)
> > >    - In Direct-mode, injects external interrupts directly into HARTs
> > >
> > > For an overview of the AIA specification, refer the recent AIA virtualization
> > > talk at KVM Forum 2022:
> > > https://static.sched.com/hosted_files/kvmforum2022/a1/AIA_Virtualization_in_KVM_RISCV_final.pdf
> > > https://www.youtube.com/watch?v=r071dL8Z0yo
> > >
> > > This series adds required Linux irqchip drivers for AIA and it depends on
> > > the recent "RISC-V IPI Improvements".
> > > (Refer, https://lore.kernel.org/lkml/20221101143400.690000-1-apatel@ventanamicro.com/t/)
> > >
> > > To test this series, use QEMU v7.1 (or higher) and OpenSBI v1.1 (or higher).
> > >
> > > These patches can also be found in the riscv_aia_v1 branch at:
> > > https://github.com/avpatel/linux.git
> > >
> > > Anup Patel (9):
> > >   RISC-V: Add AIA related CSR defines
> > >   RISC-V: Detect AIA CSRs from ISA string
> > >   irqchip/riscv-intc: Add support for RISC-V AIA
> > >   dt-bindings: Add RISC-V incoming MSI controller bindings
> > >   irqchip: Add RISC-V incoming MSI controller driver
> > >   dt-bindings: Add RISC-V advanced PLIC bindings
> > >   irqchip: Add RISC-V advanced PLIC driver
> > >   RISC-V: Select APLIC and IMSIC drivers for QEMU virt machine
> > >   MAINTAINERS: Add entry for RISC-V AIA drivers
> > >
> > >  .../interrupt-controller/riscv,aplic.yaml     |  136 ++
> > >  .../interrupt-controller/riscv,imsic.yaml     |  174 +++
> > >  MAINTAINERS                                   |   12 +
> > >  arch/riscv/Kconfig.socs                       |    2 +
> > >  arch/riscv/include/asm/csr.h                  |   92 ++
> > >  arch/riscv/include/asm/hwcap.h                |    8 +
> > >  arch/riscv/kernel/cpu.c                       |    2 +
> > >  arch/riscv/kernel/cpufeature.c                |    2 +
> > >  drivers/irqchip/Kconfig                       |   32 +-
> > >  drivers/irqchip/Makefile                      |    2 +
> > >  drivers/irqchip/irq-riscv-aplic.c             |  656 +++++++++
> > >  drivers/irqchip/irq-riscv-imsic.c             | 1207 +++++++++++++++++
> > >  drivers/irqchip/irq-riscv-intc.c              |   37 +-
> > >  include/linux/irqchip/riscv-aplic.h           |  117 ++
> > >  include/linux/irqchip/riscv-imsic.h           |   92 ++
> > >  15 files changed, 2564 insertions(+), 7 deletions(-)
> > >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> > >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > >  create mode 100644 drivers/irqchip/irq-riscv-aplic.c
> > >  create mode 100644 drivers/irqchip/irq-riscv-imsic.c
> > >  create mode 100644 include/linux/irqchip/riscv-aplic.h
> > >  create mode 100644 include/linux/irqchip/riscv-imsic.h
> > >
> > > --
> > > 2.34.1
> > >
> >
> > I am seeing the following boot failure with your branch and upstream
> > qemu (tag: v7.2.0-rc0).
> > It seems IPIs are probably not getting delivered after a point. I saw
> > that IPIs are delivered in the same path earlier (via gdb).
> >
> > [    0.990152] NET: Registered PF_INET6 protocol family
> > [    1.004885] Segment Routing with IPv6
> > [    1.005385] In-situ OAM (IOAM) with IPv6
> > [    1.006371] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
> > [    1.011609] NET: Registered PF_PACKET protocol family
> > [    1.014877] 9pnet: Installing 9P2000 support
> > [    1.015603] Key type dns_resolver registered
> > [    1.026396] debug_vm_pgtable: [debug_vm_pgtable         ]:
> > Validating architecture page table helpers
> > [    1.144097] EXT4-fs (vda2): recovery complete
> > [    1.146345] EXT4-fs (vda2): mounted filesystem with ordered data
> > mode. Quota mode: disabled.
> > [    1.147020] VFS: Mounted root (ext4 filesystem) on device 254:2.
> > [    1.152238] devtmpfs: mounted
> > [    1.183914] Freeing unused kernel image (initmem) memory: 2176K
> > [    1.185898] Run /sbin/init as init process
> > [   29.412287] rcu: INFO: rcu_sched self-detected stall on CPU
> > [   29.412992] rcu:     3-....: (5250 ticks this GP)
> > idle=fc3c/1/0x4000000000000002 softirq=78/78 fqs=2230
> > [   29.413427]  (t=5251 jiffies g=-1047 q=3 ncpus=8)
> > [   29.414199] CPU: 3 PID: 1 Comm: init Not tainted
> > 6.1.0-rc4-00024-g5b711f2d7b91 #198
> > [   29.414578] Hardware name: riscv-virtio,qemu (DT)
> > [   29.414896] epc : smp_call_function_many_cond+0x138/0x372
> > [   29.415157]  ra : smp_call_function_many_cond+0x154/0x372
> > [   29.415318] epc : ffffffff8008d2e6 ra : ffffffff8008d302 sp :
> > ff2000000004bb40
> > [   29.415512]  gp : ffffffff812e9eb0 tp : ff600000016d8000 t0 :
> > ff6000007ed88610
> > [   29.415686]  t1 : 00000000000000ff t2 : 0000000000000002 s0 :
> > ff2000000004bc00
> > [   29.415837]  s1 : ff6000007ed85448 a0 : 0000000000000007 a1 :
> > 00000000000000f7
> > [   29.416008]  a2 : 0000000000000000 a3 : 0000000000000000 a4 :
> > ff6000007edd1780
> > [   29.416188]  a5 : 0000000000000001 a6 : ffffffff812eb1c0 a7 :
> > ff600000016d8000
> > [   29.416613]  s2 : ffffffff81323c30 s3 : ffffffff812e9964 s4 :
> > 0000000000000000
> > [   29.416810]  s5 : 0000000000000000 s6 : ff6000007ed85440 s7 :
> > 0000000000000038
> > [   29.416997]  s8 : 0000000000000003 s9 : ffffffff81323c30 s10:
> > ff6000007ed85448
> > [   29.417159]  s11: 0000000000000008 t3 : 00ffffffad08a000 t4 :
> > ff60000001613e0c
> > [   29.417331]  t5 : 0000000000000000 t6 : 00ffffffad177fff
> > [   29.417482] status: 0000000200000120 badaddr: 0000000000000000
> > cause: 8000000000000005
> > [   29.417939] [<ffffffff8008d590>] on_each_cpu_cond_mask+0x20/0x32
> > [   29.418179] [<ffffffff80008d32>] flush_icache_all+0x38/0x40
> > [   29.418324] [<ffffffff80008eb2>] flush_icache_pte+0x4a/0x7a
> > [   29.418442] [<ffffffff80139e00>] do_set_pte+0x132/0x192
> > [   29.418594] [<ffffffff8010b42e>] filemap_map_pages+0x178/0x3a0
> > [   29.418738] [<ffffffff8013ad00>] __handle_mm_fault+0x992/0xbac
> > [   29.418876] [<ffffffff8013afde>] handle_mm_fault+0xc4/0x1d4
> > [   29.419010] [<ffffffff80008372>] do_page_fault+0x120/0x326
> > [   29.419145] [<ffffffff800033e6>] ret_from_exception+0x0/0xc
> >
> >
>
> Sorry. I forgot to mention that this happens while booting Fedora on
> an 8 cpu virt machine.
> I am yet to reproduce this issue for 4 or 2 cpus.
>

This warning splat appears with 4 cpus though.

[  126.977213] e1000e 0000:00:02.0 enp0s2: NIC Link is Up 1000 Mbps
Full Duplex, Flow Control: Rx/Tx
[  126.988275] IPv6: ADDRCONF(NETDEV_CHANGE): enp0s2: link becomes ready
[  128.210090] ------------[ cut here ]------------
[  128.210476] WARNING: CPU: 0 PID: 294 at
drivers/irqchip/irq-riscv-aplic.c:318 aplic_msi_write_msg+0x110/0x12a
[  128.211702] Modules linked in:
[  128.212473] CPU: 0 PID: 294 Comm: (agetty) Not tainted
6.1.0-rc4-00024-g5b711f2d7b91 #198
[  128.212839] Hardware name: riscv-virtio,qemu (DT)
[  128.213459] epc : aplic_msi_write_msg+0x110/0x12a
[  128.213652]  ra : aplic_msi_write_msg+0x1c/0x12a
[  128.213823] epc : ffffffff8039eb80 ra : ffffffff8039ea8c sp :
ff2000001054bb10
[  128.213884]  gp : ffffffff812e9eb0 tp : ff600000032d3840 t0 :
0000000000000002
[  128.213945]  t1 : 0000000000000008 t2 : 0000000000000040 s0 :
ff2000001054bb30
[  128.214015]  s1 : ff600000018443a8 a0 : ff600000017e8c30 a1 :
0000000000000000
[  128.214083]  a2 : 0000000000000001 a3 : 000000000000000c a4 :
0000000000000003
[  128.214141]  a5 : 0000000000000000 a6 : 0000000000000000 a7 :
0000000000000000
[  128.214173]  s2 : ff2000001054bb40 s3 : ff600000017e8c30 s4 :
0000000200000022
[  128.214246]  s5 : ff600000017e8d78 s6 : 000000000000000c s7 :
ff600000017e8cb0
[  128.214295]  s8 : ffffffff80c5c8f0 s9 : ff6000000e97ab60 s10:
0000000000000000
[  128.214327]  s11: ff6000000e97ab78 t3 : 0000000000000004 t4 :
0000000000000002
[  128.214383]  t5 : 0000000000000000 t6 : 0000000000028000
[  128.214414] status: 0000000200000100 badaddr: 0000000000000000
cause: 0000000000000003
[  128.215267] [<ffffffff80453060>] platform_msi_write_msg+0x18/0x20
[  128.215413] [<ffffffff80060b7a>] msi_domain_deactivate+0x2a/0x48
[  128.215432] [<ffffffff8005e5a2>] __irq_domain_deactivate_irq+0x1c/0x30
[  128.215450] [<ffffffff8005e5aa>] __irq_domain_deactivate_irq+0x24/0x30
[  128.215464] [<ffffffff8005f9ea>] irq_domain_deactivate_irq+0x1c/0x30
[  128.215477] [<ffffffff8005b8f6>] free_irq+0x1d0/0x27c
[  128.219393] [<ffffffff8041be06>] univ8250_release_irq+0xa4/0xaa
[  128.219417] [<ffffffff8041e018>] serial8250_do_shutdown+0xf0/0x166
[  128.219431] [<ffffffff8041e0a8>] serial8250_shutdown+0x1a/0x22
[  128.219444] [<ffffffff804174f0>] uart_port_shutdown+0x2c/0x42
[  128.220230] [<ffffffff8041908a>] uart_shutdown+0x66/0x11e
[  128.220247] [<ffffffff8041a69a>] uart_hangup+0x5e/0x100
[  128.220260] [<ffffffff803fdaa2>] __tty_hangup.part.0+0x286/0x2ac
[  128.220859] [<ffffffff803fec6c>] tty_ioctl+0x684/0x802
[  128.220875] [<ffffffff8019ec48>] sys_ioctl+0x37e/0x95e
[  128.221284] [<ffffffff800033d8>] ret_from_syscall+0x0/0x2
[  128.221653] ---[ end trace 0000000000000000 ]---


> >
> > --
> > Regards,
> > Atish
>
>
>
> --
> Regards,
> Atish



-- 
Regards,
Atish

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 7/9] irqchip: Add RISC-V advanced PLIC driver
  2022-11-11  4:42   ` Anup Patel
@ 2022-11-11 23:17     ` Andrew Bresticker
  -1 siblings, 0 replies; 96+ messages in thread
From: Andrew Bresticker @ 2022-11-11 23:17 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

On Thu, Nov 10, 2022 at 11:45 PM Anup Patel <apatel@ventanamicro.com> wrote:
>
> The RISC-V advanced interrupt architecture (AIA) specification defines
> a new interrupt controller for managing wired interrupts on a RISC-V
> platform. This new interrupt controller is referred to as advanced
> platform-level interrupt controller (APLIC) which can forward wired
> interrupts to CPUs (or HARTs) as local interrupts OR as message
> signaled interrupts.
> (For more details refer https://github.com/riscv/riscv-aia)
>
> This patch adds an irqchip driver for RISC-V APLIC found on RISC-V
> platforms.
>
> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> ---
>  drivers/irqchip/Kconfig             |  12 +
>  drivers/irqchip/Makefile            |   1 +
>  drivers/irqchip/irq-riscv-aplic.c   | 656 ++++++++++++++++++++++++++++
>  include/linux/irqchip/riscv-aplic.h | 117 +++++
>  4 files changed, 786 insertions(+)
>  create mode 100644 drivers/irqchip/irq-riscv-aplic.c
>  create mode 100644 include/linux/irqchip/riscv-aplic.h
>
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index 8246c08f0fd3..9f022e71d937 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -563,6 +563,18 @@ config SIFIVE_PLIC
>
>            If you don't know what to do here, say Y.
>
> +config RISCV_APLIC
> +       bool "RISC-V Advanced Platform-Level Interrupt Controller"
> +       depends on RISCV
> +       select IRQ_DOMAIN_HIERARCHY
> +       select GENERIC_MSI_IRQ_DOMAIN
> +       help
> +          This enables support for the APLIC chip found in RISC-V systems.
> +          The APLIC controls device wired interrupts and forwards them to
> +          each core as wired local interrupt or per-core MSIs.
> +
> +          If you don't know what to do here, say Y.
> +
>  config RISCV_IMSIC
>         bool "RISC-V Incoming MSI Controller"
>         depends on RISCV
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index 22c723cc6ec8..6154e5bc4228 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -96,6 +96,7 @@ obj-$(CONFIG_QCOM_MPM)                        += irq-qcom-mpm.o
>  obj-$(CONFIG_CSKY_MPINTC)              += irq-csky-mpintc.o
>  obj-$(CONFIG_CSKY_APB_INTC)            += irq-csky-apb-intc.o
>  obj-$(CONFIG_RISCV_INTC)               += irq-riscv-intc.o
> +obj-$(CONFIG_RISCV_APLIC)              += irq-riscv-aplic.o
>  obj-$(CONFIG_RISCV_IMSIC)              += irq-riscv-imsic.o
>  obj-$(CONFIG_SIFIVE_PLIC)              += irq-sifive-plic.o
>  obj-$(CONFIG_IMX_IRQSTEER)             += irq-imx-irqsteer.o
> diff --git a/drivers/irqchip/irq-riscv-aplic.c b/drivers/irqchip/irq-riscv-aplic.c
> new file mode 100644
> index 000000000000..e6d0e1bb15a8
> --- /dev/null
> +++ b/drivers/irqchip/irq-riscv-aplic.c
> @@ -0,0 +1,656 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2021 Western Digital Corporation or its affiliates.
> + * Copyright (C) 2022 Ventana Micro Systems Inc.
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/cpu.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/irq.h>
> +#include <linux/irqchip.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/irqchip/riscv-aplic.h>
> +#include <linux/irqchip/riscv-imsic.h>
> +#include <linux/irqdomain.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/platform_device.h>
> +#include <linux/smp.h>
> +
> +#define APLIC_DEFAULT_PRIORITY         1
> +#define APLIC_DISABLE_IDELIVERY                0
> +#define APLIC_ENABLE_IDELIVERY         1
> +#define APLIC_DISABLE_ITHRESHOLD       1
> +#define APLIC_ENABLE_ITHRESHOLD                0
> +
> +struct aplic_msicfg {
> +       phys_addr_t             base_ppn;
> +       u32                     hhxs;
> +       u32                     hhxw;
> +       u32                     lhxs;
> +       u32                     lhxw;
> +};
> +
> +struct aplic_idc {
> +       unsigned int            hart_index;
> +       void __iomem            *regs;
> +       struct aplic_priv       *priv;
> +};
> +
> +struct aplic_priv {
> +       struct device           *dev;
> +       u32                     nr_irqs;
> +       u32                     nr_idcs;
> +       void __iomem            *regs;
> +       struct irq_domain       *irqdomain;
> +       struct aplic_msicfg     msicfg;
> +       struct cpumask          lmask;
> +};
> +
> +static unsigned int aplic_idc_parent_irq;
> +static DEFINE_PER_CPU(struct aplic_idc, aplic_idcs);
> +
> +static void aplic_irq_unmask(struct irq_data *d)
> +{
> +       struct aplic_priv *priv = irq_data_get_irq_chip_data(d);
> +
> +       writel(d->hwirq, priv->regs + APLIC_SETIENUM);
> +
> +       if (!priv->nr_idcs)
> +               irq_chip_unmask_parent(d);
> +}
> +
> +static void aplic_irq_mask(struct irq_data *d)
> +{
> +       struct aplic_priv *priv = irq_data_get_irq_chip_data(d);
> +
> +       writel(d->hwirq, priv->regs + APLIC_CLRIENUM);
> +
> +       if (!priv->nr_idcs)
> +               irq_chip_mask_parent(d);
> +}
> +
> +static int aplic_set_type(struct irq_data *d, unsigned int type)
> +{
> +       u32 val = 0;
> +       void __iomem *sourcecfg;
> +       struct aplic_priv *priv = irq_data_get_irq_chip_data(d);
> +
> +       switch (type) {
> +       case IRQ_TYPE_NONE:
> +               val = APLIC_SOURCECFG_SM_INACTIVE;
> +               break;
> +       case IRQ_TYPE_LEVEL_LOW:
> +               val = APLIC_SOURCECFG_SM_LEVEL_LOW;
> +               break;
> +       case IRQ_TYPE_LEVEL_HIGH:
> +               val = APLIC_SOURCECFG_SM_LEVEL_HIGH;
> +               break;
> +       case IRQ_TYPE_EDGE_FALLING:
> +               val = APLIC_SOURCECFG_SM_EDGE_FALL;
> +               break;
> +       case IRQ_TYPE_EDGE_RISING:
> +               val = APLIC_SOURCECFG_SM_EDGE_RISE;
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       sourcecfg = priv->regs + APLIC_SOURCECFG_BASE;
> +       sourcecfg += (d->hwirq - 1) * sizeof(u32);
> +       writel(val, sourcecfg);
> +
> +       return 0;
> +}
> +
> +#ifdef CONFIG_SMP
> +static int aplic_set_affinity(struct irq_data *d,
> +                             const struct cpumask *mask_val, bool force)
> +{
> +       struct aplic_priv *priv = irq_data_get_irq_chip_data(d);
> +       struct aplic_idc *idc;
> +       unsigned int cpu, val;
> +       struct cpumask amask;
> +       void __iomem *target;
> +
> +       if (!priv->nr_idcs)
> +               return irq_chip_set_affinity_parent(d, mask_val, force);
> +
> +       cpumask_and(&amask, &priv->lmask, mask_val);
> +
> +       if (force)
> +               cpu = cpumask_first(&amask);
> +       else
> +               cpu = cpumask_any_and(&amask, cpu_online_mask);
> +
> +       if (cpu >= nr_cpu_ids)
> +               return -EINVAL;
> +
> +       idc = per_cpu_ptr(&aplic_idcs, cpu);
> +       target = priv->regs + APLIC_TARGET_BASE;
> +       target += (d->hwirq - 1) * sizeof(u32);
> +       val = idc->hart_index & APLIC_TARGET_HART_IDX_MASK;
> +       val <<= APLIC_TARGET_HART_IDX_SHIFT;
> +       val |= APLIC_DEFAULT_PRIORITY;
> +       writel(val, target);
> +
> +       irq_data_update_effective_affinity(d, cpumask_of(cpu));
> +
> +       return IRQ_SET_MASK_OK_DONE;
> +}
> +#endif
> +
> +static struct irq_chip aplic_chip = {
> +       .name           = "RISC-V APLIC",
> +       .irq_mask       = aplic_irq_mask,
> +       .irq_unmask     = aplic_irq_unmask,
> +       .irq_set_type   = aplic_set_type,
> +#ifdef CONFIG_SMP
> +       .irq_set_affinity = aplic_set_affinity,
> +#endif
> +       .flags          = IRQCHIP_SET_TYPE_MASKED |
> +                         IRQCHIP_SKIP_SET_WAKE |
> +                         IRQCHIP_MASK_ON_SUSPEND,
> +};
> +
> +static int aplic_irqdomain_translate(struct irq_domain *d,
> +                                    struct irq_fwspec *fwspec,
> +                                    unsigned long *hwirq,
> +                                    unsigned int *type)
> +{
> +       if (WARN_ON(fwspec->param_count < 2))
> +               return -EINVAL;
> +       if (WARN_ON(!fwspec->param[0]))
> +               return -EINVAL;
> +
> +       *hwirq = fwspec->param[0];
> +       *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
> +
> +       WARN_ON(*type == IRQ_TYPE_NONE);
> +
> +       return 0;
> +}
> +
> +static int aplic_irqdomain_msi_alloc(struct irq_domain *domain,
> +                                    unsigned int virq, unsigned int nr_irqs,
> +                                    void *arg)
> +{
> +       int i, ret;
> +       unsigned int type;
> +       irq_hw_number_t hwirq;
> +       struct irq_fwspec *fwspec = arg;
> +       struct aplic_priv *priv = platform_msi_get_host_data(domain);
> +
> +       ret = aplic_irqdomain_translate(domain, fwspec, &hwirq, &type);
> +       if (ret)
> +               return ret;
> +
> +       ret = platform_msi_device_domain_alloc(domain, virq, nr_irqs);
> +       if (ret)
> +               return ret;
> +
> +       for (i = 0; i < nr_irqs; i++)
> +               irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
> +                                             &aplic_chip, priv);
> +
> +       return 0;
> +}
> +
> +static const struct irq_domain_ops aplic_irqdomain_msi_ops = {
> +       .translate      = aplic_irqdomain_translate,
> +       .alloc          = aplic_irqdomain_msi_alloc,
> +       .free           = platform_msi_device_domain_free,
> +};
> +
> +static int aplic_irqdomain_idc_alloc(struct irq_domain *domain,
> +                                    unsigned int virq, unsigned int nr_irqs,
> +                                    void *arg)
> +{
> +       int i, ret;
> +       unsigned int type;
> +       irq_hw_number_t hwirq;
> +       struct irq_fwspec *fwspec = arg;
> +       struct aplic_priv *priv = domain->host_data;
> +
> +       ret = aplic_irqdomain_translate(domain, fwspec, &hwirq, &type);
> +       if (ret)
> +               return ret;
> +
> +       for (i = 0; i < nr_irqs; i++) {
> +               irq_domain_set_info(domain, virq + i, hwirq + i,
> +                                   &aplic_chip, priv, handle_simple_irq,
> +                                   NULL, NULL);
> +               irq_set_affinity(virq + i, &priv->lmask);
> +       }
> +
> +       return 0;
> +}
> +
> +static const struct irq_domain_ops aplic_irqdomain_idc_ops = {
> +       .translate      = aplic_irqdomain_translate,
> +       .alloc          = aplic_irqdomain_idc_alloc,
> +       .free           = irq_domain_free_irqs_top,
> +};
> +
> +static void aplic_init_hw_irqs(struct aplic_priv *priv)
> +{
> +       int i;
> +
> +       /* Disable all interrupts */
> +       for (i = 0; i <= priv->nr_irqs; i += 32)
> +               writel(-1U, priv->regs + APLIC_CLRIE_BASE +
> +                           (i / 32) * sizeof(u32));
> +
> +       /* Set interrupt type and default priority for all interrupts */
> +       for (i = 1; i <= priv->nr_irqs; i++) {
> +               writel(0, priv->regs + APLIC_SOURCECFG_BASE +
> +                         (i - 1) * sizeof(u32));
> +               writel(APLIC_DEFAULT_PRIORITY,
> +                      priv->regs + APLIC_TARGET_BASE +
> +                      (i - 1) * sizeof(u32));
> +       }
> +
> +       /* Clear APLIC domaincfg */
> +       writel(0, priv->regs + APLIC_DOMAINCFG);
> +}
> +
> +static void aplic_init_hw_global(struct aplic_priv *priv)
> +{
> +       u32 val;
> +#ifdef CONFIG_RISCV_M_MODE
> +       u32 valH;
> +
> +       if (!priv->nr_idcs) {
> +               val = priv->msicfg.base_ppn;
> +               valH = (priv->msicfg.base_ppn >> 32) &
> +                       APLIC_xMSICFGADDRH_BAPPN_MASK;
> +               valH |= (priv->msicfg.lhxw & APLIC_xMSICFGADDRH_LHXW_MASK)
> +                       << APLIC_xMSICFGADDRH_LHXW_SHIFT;
> +               valH |= (priv->msicfg.hhxw & APLIC_xMSICFGADDRH_HHXW_MASK)
> +                       << APLIC_xMSICFGADDRH_HHXW_SHIFT;
> +               valH |= (priv->msicfg.lhxs & APLIC_xMSICFGADDRH_LHXS_MASK)
> +                       << APLIC_xMSICFGADDRH_LHXS_SHIFT;
> +               valH |= (priv->msicfg.hhxs & APLIC_xMSICFGADDRH_HHXS_MASK)
> +                       << APLIC_xMSICFGADDRH_HHXS_SHIFT;
> +               writel(val, priv->regs + APLIC_xMSICFGADDR);
> +               writel(valH, priv->regs + APLIC_xMSICFGADDRH);
> +       }
> +#endif
> +
> +       /* Setup APLIC domaincfg register */
> +       val = readl(priv->regs + APLIC_DOMAINCFG);
> +       val |= APLIC_DOMAINCFG_IE;
> +       if (!priv->nr_idcs)
> +               val |= APLIC_DOMAINCFG_DM;
> +       writel(val, priv->regs + APLIC_DOMAINCFG);
> +       if (readl(priv->regs + APLIC_DOMAINCFG) != val)
> +               dev_warn(priv->dev,
> +                        "unable to write 0x%x in domaincfg\n", val);
> +}
> +
> +static void aplic_msi_write_msg(struct msi_desc *desc, struct msi_msg *msg)
> +{
> +       unsigned int group_index, hart_index, guest_index, val;
> +       struct device *dev = msi_desc_to_dev(desc);
> +       struct aplic_priv *priv = dev_get_drvdata(dev);
> +       struct irq_data *d = irq_get_irq_data(desc->irq);
> +       struct aplic_msicfg *mc = &priv->msicfg;
> +       phys_addr_t tppn, tbppn, msg_addr;
> +       void __iomem *target;
> +
> +       /* Save the MSI address and data */
> +       msg_addr = (((u64)msg->address_hi) << 32) | msg->address_lo;
> +       WARN_ON(msg->data > APLIC_TARGET_EIID_MASK);
> +
> +       /* Compute target HART PPN */
> +       tppn = msg_addr >> APLIC_xMSICFGADDR_PPN_SHIFT;
> +
> +       /* Compute target HART Base PPN */
> +       tbppn = tppn;
> +       tbppn &= ~APLIC_xMSICFGADDR_PPN_HART(mc->lhxs);
> +       tbppn &= ~APLIC_xMSICFGADDR_PPN_LHX(mc->lhxw, mc->lhxs);
> +       tbppn &= ~APLIC_xMSICFGADDR_PPN_HHX(mc->hhxw, mc->hhxs);
> +       WARN_ON(tbppn != mc->base_ppn);

platform_msi_write_msg() will get called with a zeroed-out msg_msg in
the free_irq() path, triggering this warning. Should detect that case
and (I assume) just write 0 to the APLIC target register.

-Andrew

> +
> +       /* Compute target group and hart indexes */
> +       group_index = (tppn >> APLIC_xMSICFGADDR_PPN_HHX_SHIFT(mc->hhxs)) &
> +                    APLIC_xMSICFGADDR_PPN_HHX_MASK(mc->hhxw);
> +       hart_index = (tppn >> APLIC_xMSICFGADDR_PPN_LHX_SHIFT(mc->lhxs)) &
> +                    APLIC_xMSICFGADDR_PPN_LHX_MASK(mc->lhxw);
> +       hart_index |= (group_index << mc->lhxw);
> +       WARN_ON(hart_index > APLIC_TARGET_HART_IDX_MASK);
> +
> +       /* Compute target guest index */
> +       guest_index = tppn & APLIC_xMSICFGADDR_PPN_HART(mc->lhxs);
> +       WARN_ON(guest_index > APLIC_TARGET_GUEST_IDX_MASK);
> +
> +       /* Update IRQ TARGET register */
> +       target = priv->regs + APLIC_TARGET_BASE;
> +       target += (d->hwirq - 1) * sizeof(u32);
> +       val = (hart_index & APLIC_TARGET_HART_IDX_MASK)
> +                               << APLIC_TARGET_HART_IDX_SHIFT;
> +       val |= (guest_index & APLIC_TARGET_GUEST_IDX_MASK)
> +                               << APLIC_TARGET_GUEST_IDX_SHIFT;
> +       val |= (msg->data & APLIC_TARGET_EIID_MASK);
> +       writel(val, target);
> +}
> +
> +static int aplic_setup_msi(struct aplic_priv *priv)
> +{
> +       struct device *dev = priv->dev;
> +       struct aplic_msicfg *mc = &priv->msicfg;
> +       const struct imsic_global_config *imsic_global;
> +
> +       /*
> +        * The APLIC outgoing MSI config registers assume target MSI
> +        * controller to be RISC-V AIA IMSIC controller.
> +        */
> +       imsic_global = imsic_get_global_config();
> +       if (!imsic_global) {
> +               dev_err(dev, "IMSIC global config not found\n");
> +               return -ENODEV;
> +       }
> +
> +       /* Find number of guest index bits (LHXS) */
> +       mc->lhxs = imsic_global->guest_index_bits;
> +       if (APLIC_xMSICFGADDRH_LHXS_MASK < mc->lhxs) {
> +               dev_err(dev, "IMSIC guest index bits big for APLIC LHXS\n");
> +               return -EINVAL;
> +       }
> +
> +       /* Find number of HART index bits (LHXW) */
> +       mc->lhxw = imsic_global->hart_index_bits;
> +       if (APLIC_xMSICFGADDRH_LHXW_MASK < mc->lhxw) {
> +               dev_err(dev, "IMSIC hart index bits big for APLIC LHXW\n");
> +               return -EINVAL;
> +       }
> +
> +       /* Find number of group index bits (HHXW) */
> +       mc->hhxw = imsic_global->group_index_bits;
> +       if (APLIC_xMSICFGADDRH_HHXW_MASK < mc->hhxw) {
> +               dev_err(dev, "IMSIC group index bits big for APLIC HHXW\n");
> +               return -EINVAL;
> +       }
> +
> +       /* Find first bit position of group index (HHXS) */
> +       mc->hhxs = imsic_global->group_index_shift;
> +       if (mc->hhxs < (2 * APLIC_xMSICFGADDR_PPN_SHIFT)) {
> +               dev_err(dev, "IMSIC group index shift should be >= %d\n",
> +                       (2 * APLIC_xMSICFGADDR_PPN_SHIFT));
> +               return -EINVAL;
> +       }
> +       mc->hhxs -= (2 * APLIC_xMSICFGADDR_PPN_SHIFT);
> +       if (APLIC_xMSICFGADDRH_HHXS_MASK < mc->hhxs) {
> +               dev_err(dev, "IMSIC group index shift big for APLIC HHXS\n");
> +               return -EINVAL;
> +       }
> +
> +       /* Compute PPN base */
> +       mc->base_ppn = imsic_global->base_addr >> APLIC_xMSICFGADDR_PPN_SHIFT;
> +       mc->base_ppn &= ~APLIC_xMSICFGADDR_PPN_HART(mc->lhxs);
> +       mc->base_ppn &= ~APLIC_xMSICFGADDR_PPN_LHX(mc->lhxw, mc->lhxs);
> +       mc->base_ppn &= ~APLIC_xMSICFGADDR_PPN_HHX(mc->hhxw, mc->hhxs);
> +
> +       /* Use all possible CPUs as lmask */
> +       cpumask_copy(&priv->lmask, cpu_possible_mask);
> +
> +       return 0;
> +}
> +
> +/*
> + * To handle an APLIC IDC interrupts, we just read the CLAIMI register
> + * which will return highest priority pending interrupt and clear the
> + * pending bit of the interrupt. This process is repeated until CLAIMI
> + * register return zero value.
> + */
> +static void aplic_idc_handle_irq(struct irq_desc *desc)
> +{
> +       struct aplic_idc *idc = this_cpu_ptr(&aplic_idcs);
> +       struct irq_chip *chip = irq_desc_get_chip(desc);
> +       irq_hw_number_t hw_irq;
> +       int irq;
> +
> +       chained_irq_enter(chip, desc);
> +
> +       while ((hw_irq = readl(idc->regs + APLIC_IDC_CLAIMI))) {
> +               hw_irq = hw_irq >> APLIC_IDC_TOPI_ID_SHIFT;
> +               irq = irq_find_mapping(idc->priv->irqdomain, hw_irq);
> +
> +               if (unlikely(irq <= 0))
> +                       pr_warn_ratelimited("hw_irq %lu mapping not found\n",
> +                                           hw_irq);
> +               else
> +                       generic_handle_irq(irq);
> +       }
> +
> +       chained_irq_exit(chip, desc);
> +}
> +
> +static void aplic_idc_set_delivery(struct aplic_idc *idc, bool en)
> +{
> +       u32 de = (en) ? APLIC_ENABLE_IDELIVERY : APLIC_DISABLE_IDELIVERY;
> +       u32 th = (en) ? APLIC_ENABLE_ITHRESHOLD : APLIC_DISABLE_ITHRESHOLD;
> +
> +       /* Priority must be less than threshold for interrupt triggering */
> +       writel(th, idc->regs + APLIC_IDC_ITHRESHOLD);
> +
> +       /* Delivery must be set to 1 for interrupt triggering */
> +       writel(de, idc->regs + APLIC_IDC_IDELIVERY);
> +}
> +
> +static int aplic_idc_dying_cpu(unsigned int cpu)
> +{
> +       if (aplic_idc_parent_irq)
> +               disable_percpu_irq(aplic_idc_parent_irq);
> +
> +       return 0;
> +}
> +
> +static int aplic_idc_starting_cpu(unsigned int cpu)
> +{
> +       if (aplic_idc_parent_irq)
> +               enable_percpu_irq(aplic_idc_parent_irq,
> +                                 irq_get_trigger_type(aplic_idc_parent_irq));
> +
> +       return 0;
> +}
> +
> +static int aplic_setup_idc(struct aplic_priv *priv)
> +{
> +       int i, j, rc, cpu, setup_count = 0;
> +       struct device_node *node = priv->dev->of_node;
> +       struct device *dev = priv->dev;
> +       struct of_phandle_args parent;
> +       struct irq_domain *domain;
> +       unsigned long hartid;
> +       struct aplic_idc *idc;
> +       u32 val;
> +
> +       /* Setup per-CPU IDC and target CPU mask */
> +       for (i = 0; i < priv->nr_idcs; i++) {
> +               if (of_irq_parse_one(node, i, &parent)) {
> +                       dev_err(dev, "failed to parse parent for IDC%d.\n",
> +                               i);
> +                       return -EIO;
> +               }
> +
> +               /* Skip IDCs which do not connect to external interrupts */
> +               if (parent.args[0] != RV_IRQ_EXT)
> +                       continue;
> +
> +               rc = riscv_of_parent_hartid(parent.np, &hartid);
> +               if (rc) {
> +                       dev_err(dev, "failed to parse hart ID for IDC%d.\n",
> +                               i);
> +                       return rc;
> +               }
> +
> +               cpu = riscv_hartid_to_cpuid(hartid);
> +               if (cpu < 0) {
> +                       dev_warn(dev, "invalid cpuid for IDC%d\n", i);
> +                       continue;
> +               }
> +
> +               cpumask_set_cpu(cpu, &priv->lmask);
> +
> +               idc = per_cpu_ptr(&aplic_idcs, cpu);
> +               WARN_ON(idc->priv);
> +
> +               idc->hart_index = i;
> +               idc->regs = priv->regs + APLIC_IDC_BASE + i * APLIC_IDC_SIZE;
> +               idc->priv = priv;
> +
> +               aplic_idc_set_delivery(idc, true);
> +
> +               /*
> +                * Boot cpu might not have APLIC hart_index = 0 so check
> +                * and update target registers of all interrupts.
> +                */
> +               if (cpu == smp_processor_id() && idc->hart_index) {
> +                       val = idc->hart_index & APLIC_TARGET_HART_IDX_MASK;
> +                       val <<= APLIC_TARGET_HART_IDX_SHIFT;
> +                       val |= APLIC_DEFAULT_PRIORITY;
> +                       for (j = 1; j <= priv->nr_irqs; j++)
> +                               writel(val, priv->regs + APLIC_TARGET_BASE +
> +                                           (j - 1) * sizeof(u32));
> +               }
> +
> +               setup_count++;
> +       }
> +
> +       /* Find parent domain and register chained handler */
> +       domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(),
> +                                         DOMAIN_BUS_ANY);
> +       if (!aplic_idc_parent_irq && domain) {
> +               aplic_idc_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT);
> +               if (aplic_idc_parent_irq) {
> +                       irq_set_chained_handler(aplic_idc_parent_irq,
> +                                               aplic_idc_handle_irq);
> +
> +                       /*
> +                        * Setup CPUHP notifier to enable IDC parent
> +                        * interrupt on all CPUs
> +                        */
> +                       cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
> +                                         "irqchip/riscv/aplic:starting",
> +                                         aplic_idc_starting_cpu,
> +                                         aplic_idc_dying_cpu);
> +               }
> +       }
> +
> +       /* Fail if we were not able to setup IDC for any CPU */
> +       return (setup_count) ? 0 : -ENODEV;
> +}
> +
> +static int aplic_probe(struct platform_device *pdev)
> +{
> +       struct device_node *node = pdev->dev.of_node;
> +       struct device *dev = &pdev->dev;
> +       struct aplic_priv *priv;
> +       struct resource *regs;
> +       phys_addr_t pa;
> +       int rc;
> +
> +       regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (!regs) {
> +               dev_err(dev, "cannot find registers resource\n");
> +               return -ENOENT;
> +       }
> +
> +       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +       if (!priv)
> +               return -ENOMEM;
> +       platform_set_drvdata(pdev, priv);
> +       priv->dev = dev;
> +
> +       priv->regs = devm_ioremap(dev, regs->start, resource_size(regs));
> +       if (WARN_ON(!priv->regs)) {
> +               dev_err(dev, "failed ioremap registers\n");
> +               return -EIO;
> +       }
> +
> +       of_property_read_u32(node, "riscv,num-sources", &priv->nr_irqs);
> +       if (!priv->nr_irqs) {
> +               dev_err(dev, "failed to get number of interrupt sources\n");
> +               return -EINVAL;
> +       }
> +
> +       /* Setup initial state APLIC interrupts */
> +       aplic_init_hw_irqs(priv);
> +
> +       /* Setup IDCs or MSIs based on parent interrupts in DT node */
> +       priv->nr_idcs = of_irq_count(node);
> +       if (priv->nr_idcs)
> +               rc = aplic_setup_idc(priv);
> +       else
> +               rc = aplic_setup_msi(priv);
> +       if (rc)
> +               return rc;
> +
> +       /* Setup global config and interrupt delivery */
> +       aplic_init_hw_global(priv);
> +
> +       /* Create irq domain instance for the APLIC */
> +       if (priv->nr_idcs)
> +               priv->irqdomain = irq_domain_create_linear(
> +                                               of_node_to_fwnode(node),
> +                                               priv->nr_irqs + 1,
> +                                               &aplic_irqdomain_idc_ops,
> +                                               priv);
> +       else
> +               priv->irqdomain = platform_msi_create_device_domain(dev,
> +                                               priv->nr_irqs + 1,
> +                                               aplic_msi_write_msg,
> +                                               &aplic_irqdomain_msi_ops,
> +                                               priv);
> +       if (!priv->irqdomain) {
> +               dev_err(dev, "failed to add irq domain\n");
> +               return -ENOMEM;
> +       }
> +
> +       /* Advertise the interrupt controller */
> +       if (priv->nr_idcs) {
> +               dev_info(dev, "%d interrupts directly connected to %d CPUs\n",
> +                        priv->nr_irqs, priv->nr_idcs);
> +       } else {
> +               pa = priv->msicfg.base_ppn << APLIC_xMSICFGADDR_PPN_SHIFT;
> +               dev_info(dev, "%d interrupts forwared to MSI base %pa\n",
> +                        priv->nr_irqs, &pa);
> +       }
> +
> +       return 0;
> +}
> +
> +static int aplic_remove(struct platform_device *pdev)
> +{
> +       struct aplic_priv *priv = platform_get_drvdata(pdev);
> +
> +       irq_domain_remove(priv->irqdomain);
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id aplic_match[] = {
> +       { .compatible = "riscv,aplic" },
> +       {}
> +};
> +
> +static struct platform_driver aplic_driver = {
> +       .driver = {
> +               .name           = "riscv-aplic",
> +               .of_match_table = aplic_match,
> +       },
> +       .probe = aplic_probe,
> +       .remove = aplic_remove,
> +};
> +
> +static int __init aplic_init(void)
> +{
> +       return platform_driver_register(&aplic_driver);
> +}
> +core_initcall(aplic_init);
> diff --git a/include/linux/irqchip/riscv-aplic.h b/include/linux/irqchip/riscv-aplic.h
> new file mode 100644
> index 000000000000..88177eefd411
> --- /dev/null
> +++ b/include/linux/irqchip/riscv-aplic.h
> @@ -0,0 +1,117 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2021 Western Digital Corporation or its affiliates.
> + * Copyright (C) 2022 Ventana Micro Systems Inc.
> + */
> +#ifndef __LINUX_IRQCHIP_RISCV_APLIC_H
> +#define __LINUX_IRQCHIP_RISCV_APLIC_H
> +
> +#include <linux/bitops.h>
> +
> +#define APLIC_MAX_IDC                  BIT(14)
> +#define APLIC_MAX_SOURCE               1024
> +
> +#define APLIC_DOMAINCFG                        0x0000
> +#define APLIC_DOMAINCFG_RDONLY         0x80000000
> +#define APLIC_DOMAINCFG_IE             BIT(8)
> +#define APLIC_DOMAINCFG_DM             BIT(2)
> +#define APLIC_DOMAINCFG_BE             BIT(0)
> +
> +#define APLIC_SOURCECFG_BASE           0x0004
> +#define APLIC_SOURCECFG_D              BIT(10)
> +#define APLIC_SOURCECFG_CHILDIDX_MASK  0x000003ff
> +#define APLIC_SOURCECFG_SM_MASK        0x00000007
> +#define APLIC_SOURCECFG_SM_INACTIVE    0x0
> +#define APLIC_SOURCECFG_SM_DETACH      0x1
> +#define APLIC_SOURCECFG_SM_EDGE_RISE   0x4
> +#define APLIC_SOURCECFG_SM_EDGE_FALL   0x5
> +#define APLIC_SOURCECFG_SM_LEVEL_HIGH  0x6
> +#define APLIC_SOURCECFG_SM_LEVEL_LOW   0x7
> +
> +#define APLIC_MMSICFGADDR              0x1bc0
> +#define APLIC_MMSICFGADDRH             0x1bc4
> +#define APLIC_SMSICFGADDR              0x1bc8
> +#define APLIC_SMSICFGADDRH             0x1bcc
> +
> +#ifdef CONFIG_RISCV_M_MODE
> +#define APLIC_xMSICFGADDR              APLIC_MMSICFGADDR
> +#define APLIC_xMSICFGADDRH             APLIC_MMSICFGADDRH
> +#else
> +#define APLIC_xMSICFGADDR              APLIC_SMSICFGADDR
> +#define APLIC_xMSICFGADDRH             APLIC_SMSICFGADDRH
> +#endif
> +
> +#define APLIC_xMSICFGADDRH_L           BIT(31)
> +#define APLIC_xMSICFGADDRH_HHXS_MASK   0x1f
> +#define APLIC_xMSICFGADDRH_HHXS_SHIFT  24
> +#define APLIC_xMSICFGADDRH_LHXS_MASK   0x7
> +#define APLIC_xMSICFGADDRH_LHXS_SHIFT  20
> +#define APLIC_xMSICFGADDRH_HHXW_MASK   0x7
> +#define APLIC_xMSICFGADDRH_HHXW_SHIFT  16
> +#define APLIC_xMSICFGADDRH_LHXW_MASK   0xf
> +#define APLIC_xMSICFGADDRH_LHXW_SHIFT  12
> +#define APLIC_xMSICFGADDRH_BAPPN_MASK  0xfff
> +
> +#define APLIC_xMSICFGADDR_PPN_SHIFT    12
> +
> +#define APLIC_xMSICFGADDR_PPN_HART(__lhxs) \
> +       (BIT(__lhxs) - 1)
> +
> +#define APLIC_xMSICFGADDR_PPN_LHX_MASK(__lhxw) \
> +       (BIT(__lhxw) - 1)
> +#define APLIC_xMSICFGADDR_PPN_LHX_SHIFT(__lhxs) \
> +       ((__lhxs))
> +#define APLIC_xMSICFGADDR_PPN_LHX(__lhxw, __lhxs) \
> +       (APLIC_xMSICFGADDR_PPN_LHX_MASK(__lhxw) << \
> +        APLIC_xMSICFGADDR_PPN_LHX_SHIFT(__lhxs))
> +
> +#define APLIC_xMSICFGADDR_PPN_HHX_MASK(__hhxw) \
> +       (BIT(__hhxw) - 1)
> +#define APLIC_xMSICFGADDR_PPN_HHX_SHIFT(__hhxs) \
> +       ((__hhxs) + APLIC_xMSICFGADDR_PPN_SHIFT)
> +#define APLIC_xMSICFGADDR_PPN_HHX(__hhxw, __hhxs) \
> +       (APLIC_xMSICFGADDR_PPN_HHX_MASK(__hhxw) << \
> +        APLIC_xMSICFGADDR_PPN_HHX_SHIFT(__hhxs))
> +
> +#define APLIC_SETIP_BASE               0x1c00
> +#define APLIC_SETIPNUM                 0x1cdc
> +
> +#define APLIC_CLRIP_BASE               0x1d00
> +#define APLIC_CLRIPNUM                 0x1ddc
> +
> +#define APLIC_SETIE_BASE               0x1e00
> +#define APLIC_SETIENUM                 0x1edc
> +
> +#define APLIC_CLRIE_BASE               0x1f00
> +#define APLIC_CLRIENUM                 0x1fdc
> +
> +#define APLIC_SETIPNUM_LE              0x2000
> +#define APLIC_SETIPNUM_BE              0x2004
> +
> +#define APLIC_GENMSI                   0x3000
> +
> +#define APLIC_TARGET_BASE              0x3004
> +#define APLIC_TARGET_HART_IDX_SHIFT    18
> +#define APLIC_TARGET_HART_IDX_MASK     0x3fff
> +#define APLIC_TARGET_GUEST_IDX_SHIFT   12
> +#define APLIC_TARGET_GUEST_IDX_MASK    0x3f
> +#define APLIC_TARGET_IPRIO_MASK        0xff
> +#define APLIC_TARGET_EIID_MASK 0x7ff
> +
> +#define APLIC_IDC_BASE                 0x4000
> +#define APLIC_IDC_SIZE                 32
> +
> +#define APLIC_IDC_IDELIVERY            0x00
> +
> +#define APLIC_IDC_IFORCE               0x04
> +
> +#define APLIC_IDC_ITHRESHOLD           0x08
> +
> +#define APLIC_IDC_TOPI                 0x18
> +#define APLIC_IDC_TOPI_ID_SHIFT        16
> +#define APLIC_IDC_TOPI_ID_MASK 0x3ff
> +#define APLIC_IDC_TOPI_PRIO_MASK       0xff
> +
> +#define APLIC_IDC_CLAIMI               0x1c
> +
> +#endif
> --
> 2.34.1
>

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

* Re: [PATCH 7/9] irqchip: Add RISC-V advanced PLIC driver
@ 2022-11-11 23:17     ` Andrew Bresticker
  0 siblings, 0 replies; 96+ messages in thread
From: Andrew Bresticker @ 2022-11-11 23:17 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

On Thu, Nov 10, 2022 at 11:45 PM Anup Patel <apatel@ventanamicro.com> wrote:
>
> The RISC-V advanced interrupt architecture (AIA) specification defines
> a new interrupt controller for managing wired interrupts on a RISC-V
> platform. This new interrupt controller is referred to as advanced
> platform-level interrupt controller (APLIC) which can forward wired
> interrupts to CPUs (or HARTs) as local interrupts OR as message
> signaled interrupts.
> (For more details refer https://github.com/riscv/riscv-aia)
>
> This patch adds an irqchip driver for RISC-V APLIC found on RISC-V
> platforms.
>
> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> ---
>  drivers/irqchip/Kconfig             |  12 +
>  drivers/irqchip/Makefile            |   1 +
>  drivers/irqchip/irq-riscv-aplic.c   | 656 ++++++++++++++++++++++++++++
>  include/linux/irqchip/riscv-aplic.h | 117 +++++
>  4 files changed, 786 insertions(+)
>  create mode 100644 drivers/irqchip/irq-riscv-aplic.c
>  create mode 100644 include/linux/irqchip/riscv-aplic.h
>
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index 8246c08f0fd3..9f022e71d937 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -563,6 +563,18 @@ config SIFIVE_PLIC
>
>            If you don't know what to do here, say Y.
>
> +config RISCV_APLIC
> +       bool "RISC-V Advanced Platform-Level Interrupt Controller"
> +       depends on RISCV
> +       select IRQ_DOMAIN_HIERARCHY
> +       select GENERIC_MSI_IRQ_DOMAIN
> +       help
> +          This enables support for the APLIC chip found in RISC-V systems.
> +          The APLIC controls device wired interrupts and forwards them to
> +          each core as wired local interrupt or per-core MSIs.
> +
> +          If you don't know what to do here, say Y.
> +
>  config RISCV_IMSIC
>         bool "RISC-V Incoming MSI Controller"
>         depends on RISCV
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index 22c723cc6ec8..6154e5bc4228 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -96,6 +96,7 @@ obj-$(CONFIG_QCOM_MPM)                        += irq-qcom-mpm.o
>  obj-$(CONFIG_CSKY_MPINTC)              += irq-csky-mpintc.o
>  obj-$(CONFIG_CSKY_APB_INTC)            += irq-csky-apb-intc.o
>  obj-$(CONFIG_RISCV_INTC)               += irq-riscv-intc.o
> +obj-$(CONFIG_RISCV_APLIC)              += irq-riscv-aplic.o
>  obj-$(CONFIG_RISCV_IMSIC)              += irq-riscv-imsic.o
>  obj-$(CONFIG_SIFIVE_PLIC)              += irq-sifive-plic.o
>  obj-$(CONFIG_IMX_IRQSTEER)             += irq-imx-irqsteer.o
> diff --git a/drivers/irqchip/irq-riscv-aplic.c b/drivers/irqchip/irq-riscv-aplic.c
> new file mode 100644
> index 000000000000..e6d0e1bb15a8
> --- /dev/null
> +++ b/drivers/irqchip/irq-riscv-aplic.c
> @@ -0,0 +1,656 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2021 Western Digital Corporation or its affiliates.
> + * Copyright (C) 2022 Ventana Micro Systems Inc.
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/cpu.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/irq.h>
> +#include <linux/irqchip.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/irqchip/riscv-aplic.h>
> +#include <linux/irqchip/riscv-imsic.h>
> +#include <linux/irqdomain.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/platform_device.h>
> +#include <linux/smp.h>
> +
> +#define APLIC_DEFAULT_PRIORITY         1
> +#define APLIC_DISABLE_IDELIVERY                0
> +#define APLIC_ENABLE_IDELIVERY         1
> +#define APLIC_DISABLE_ITHRESHOLD       1
> +#define APLIC_ENABLE_ITHRESHOLD                0
> +
> +struct aplic_msicfg {
> +       phys_addr_t             base_ppn;
> +       u32                     hhxs;
> +       u32                     hhxw;
> +       u32                     lhxs;
> +       u32                     lhxw;
> +};
> +
> +struct aplic_idc {
> +       unsigned int            hart_index;
> +       void __iomem            *regs;
> +       struct aplic_priv       *priv;
> +};
> +
> +struct aplic_priv {
> +       struct device           *dev;
> +       u32                     nr_irqs;
> +       u32                     nr_idcs;
> +       void __iomem            *regs;
> +       struct irq_domain       *irqdomain;
> +       struct aplic_msicfg     msicfg;
> +       struct cpumask          lmask;
> +};
> +
> +static unsigned int aplic_idc_parent_irq;
> +static DEFINE_PER_CPU(struct aplic_idc, aplic_idcs);
> +
> +static void aplic_irq_unmask(struct irq_data *d)
> +{
> +       struct aplic_priv *priv = irq_data_get_irq_chip_data(d);
> +
> +       writel(d->hwirq, priv->regs + APLIC_SETIENUM);
> +
> +       if (!priv->nr_idcs)
> +               irq_chip_unmask_parent(d);
> +}
> +
> +static void aplic_irq_mask(struct irq_data *d)
> +{
> +       struct aplic_priv *priv = irq_data_get_irq_chip_data(d);
> +
> +       writel(d->hwirq, priv->regs + APLIC_CLRIENUM);
> +
> +       if (!priv->nr_idcs)
> +               irq_chip_mask_parent(d);
> +}
> +
> +static int aplic_set_type(struct irq_data *d, unsigned int type)
> +{
> +       u32 val = 0;
> +       void __iomem *sourcecfg;
> +       struct aplic_priv *priv = irq_data_get_irq_chip_data(d);
> +
> +       switch (type) {
> +       case IRQ_TYPE_NONE:
> +               val = APLIC_SOURCECFG_SM_INACTIVE;
> +               break;
> +       case IRQ_TYPE_LEVEL_LOW:
> +               val = APLIC_SOURCECFG_SM_LEVEL_LOW;
> +               break;
> +       case IRQ_TYPE_LEVEL_HIGH:
> +               val = APLIC_SOURCECFG_SM_LEVEL_HIGH;
> +               break;
> +       case IRQ_TYPE_EDGE_FALLING:
> +               val = APLIC_SOURCECFG_SM_EDGE_FALL;
> +               break;
> +       case IRQ_TYPE_EDGE_RISING:
> +               val = APLIC_SOURCECFG_SM_EDGE_RISE;
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       sourcecfg = priv->regs + APLIC_SOURCECFG_BASE;
> +       sourcecfg += (d->hwirq - 1) * sizeof(u32);
> +       writel(val, sourcecfg);
> +
> +       return 0;
> +}
> +
> +#ifdef CONFIG_SMP
> +static int aplic_set_affinity(struct irq_data *d,
> +                             const struct cpumask *mask_val, bool force)
> +{
> +       struct aplic_priv *priv = irq_data_get_irq_chip_data(d);
> +       struct aplic_idc *idc;
> +       unsigned int cpu, val;
> +       struct cpumask amask;
> +       void __iomem *target;
> +
> +       if (!priv->nr_idcs)
> +               return irq_chip_set_affinity_parent(d, mask_val, force);
> +
> +       cpumask_and(&amask, &priv->lmask, mask_val);
> +
> +       if (force)
> +               cpu = cpumask_first(&amask);
> +       else
> +               cpu = cpumask_any_and(&amask, cpu_online_mask);
> +
> +       if (cpu >= nr_cpu_ids)
> +               return -EINVAL;
> +
> +       idc = per_cpu_ptr(&aplic_idcs, cpu);
> +       target = priv->regs + APLIC_TARGET_BASE;
> +       target += (d->hwirq - 1) * sizeof(u32);
> +       val = idc->hart_index & APLIC_TARGET_HART_IDX_MASK;
> +       val <<= APLIC_TARGET_HART_IDX_SHIFT;
> +       val |= APLIC_DEFAULT_PRIORITY;
> +       writel(val, target);
> +
> +       irq_data_update_effective_affinity(d, cpumask_of(cpu));
> +
> +       return IRQ_SET_MASK_OK_DONE;
> +}
> +#endif
> +
> +static struct irq_chip aplic_chip = {
> +       .name           = "RISC-V APLIC",
> +       .irq_mask       = aplic_irq_mask,
> +       .irq_unmask     = aplic_irq_unmask,
> +       .irq_set_type   = aplic_set_type,
> +#ifdef CONFIG_SMP
> +       .irq_set_affinity = aplic_set_affinity,
> +#endif
> +       .flags          = IRQCHIP_SET_TYPE_MASKED |
> +                         IRQCHIP_SKIP_SET_WAKE |
> +                         IRQCHIP_MASK_ON_SUSPEND,
> +};
> +
> +static int aplic_irqdomain_translate(struct irq_domain *d,
> +                                    struct irq_fwspec *fwspec,
> +                                    unsigned long *hwirq,
> +                                    unsigned int *type)
> +{
> +       if (WARN_ON(fwspec->param_count < 2))
> +               return -EINVAL;
> +       if (WARN_ON(!fwspec->param[0]))
> +               return -EINVAL;
> +
> +       *hwirq = fwspec->param[0];
> +       *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
> +
> +       WARN_ON(*type == IRQ_TYPE_NONE);
> +
> +       return 0;
> +}
> +
> +static int aplic_irqdomain_msi_alloc(struct irq_domain *domain,
> +                                    unsigned int virq, unsigned int nr_irqs,
> +                                    void *arg)
> +{
> +       int i, ret;
> +       unsigned int type;
> +       irq_hw_number_t hwirq;
> +       struct irq_fwspec *fwspec = arg;
> +       struct aplic_priv *priv = platform_msi_get_host_data(domain);
> +
> +       ret = aplic_irqdomain_translate(domain, fwspec, &hwirq, &type);
> +       if (ret)
> +               return ret;
> +
> +       ret = platform_msi_device_domain_alloc(domain, virq, nr_irqs);
> +       if (ret)
> +               return ret;
> +
> +       for (i = 0; i < nr_irqs; i++)
> +               irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
> +                                             &aplic_chip, priv);
> +
> +       return 0;
> +}
> +
> +static const struct irq_domain_ops aplic_irqdomain_msi_ops = {
> +       .translate      = aplic_irqdomain_translate,
> +       .alloc          = aplic_irqdomain_msi_alloc,
> +       .free           = platform_msi_device_domain_free,
> +};
> +
> +static int aplic_irqdomain_idc_alloc(struct irq_domain *domain,
> +                                    unsigned int virq, unsigned int nr_irqs,
> +                                    void *arg)
> +{
> +       int i, ret;
> +       unsigned int type;
> +       irq_hw_number_t hwirq;
> +       struct irq_fwspec *fwspec = arg;
> +       struct aplic_priv *priv = domain->host_data;
> +
> +       ret = aplic_irqdomain_translate(domain, fwspec, &hwirq, &type);
> +       if (ret)
> +               return ret;
> +
> +       for (i = 0; i < nr_irqs; i++) {
> +               irq_domain_set_info(domain, virq + i, hwirq + i,
> +                                   &aplic_chip, priv, handle_simple_irq,
> +                                   NULL, NULL);
> +               irq_set_affinity(virq + i, &priv->lmask);
> +       }
> +
> +       return 0;
> +}
> +
> +static const struct irq_domain_ops aplic_irqdomain_idc_ops = {
> +       .translate      = aplic_irqdomain_translate,
> +       .alloc          = aplic_irqdomain_idc_alloc,
> +       .free           = irq_domain_free_irqs_top,
> +};
> +
> +static void aplic_init_hw_irqs(struct aplic_priv *priv)
> +{
> +       int i;
> +
> +       /* Disable all interrupts */
> +       for (i = 0; i <= priv->nr_irqs; i += 32)
> +               writel(-1U, priv->regs + APLIC_CLRIE_BASE +
> +                           (i / 32) * sizeof(u32));
> +
> +       /* Set interrupt type and default priority for all interrupts */
> +       for (i = 1; i <= priv->nr_irqs; i++) {
> +               writel(0, priv->regs + APLIC_SOURCECFG_BASE +
> +                         (i - 1) * sizeof(u32));
> +               writel(APLIC_DEFAULT_PRIORITY,
> +                      priv->regs + APLIC_TARGET_BASE +
> +                      (i - 1) * sizeof(u32));
> +       }
> +
> +       /* Clear APLIC domaincfg */
> +       writel(0, priv->regs + APLIC_DOMAINCFG);
> +}
> +
> +static void aplic_init_hw_global(struct aplic_priv *priv)
> +{
> +       u32 val;
> +#ifdef CONFIG_RISCV_M_MODE
> +       u32 valH;
> +
> +       if (!priv->nr_idcs) {
> +               val = priv->msicfg.base_ppn;
> +               valH = (priv->msicfg.base_ppn >> 32) &
> +                       APLIC_xMSICFGADDRH_BAPPN_MASK;
> +               valH |= (priv->msicfg.lhxw & APLIC_xMSICFGADDRH_LHXW_MASK)
> +                       << APLIC_xMSICFGADDRH_LHXW_SHIFT;
> +               valH |= (priv->msicfg.hhxw & APLIC_xMSICFGADDRH_HHXW_MASK)
> +                       << APLIC_xMSICFGADDRH_HHXW_SHIFT;
> +               valH |= (priv->msicfg.lhxs & APLIC_xMSICFGADDRH_LHXS_MASK)
> +                       << APLIC_xMSICFGADDRH_LHXS_SHIFT;
> +               valH |= (priv->msicfg.hhxs & APLIC_xMSICFGADDRH_HHXS_MASK)
> +                       << APLIC_xMSICFGADDRH_HHXS_SHIFT;
> +               writel(val, priv->regs + APLIC_xMSICFGADDR);
> +               writel(valH, priv->regs + APLIC_xMSICFGADDRH);
> +       }
> +#endif
> +
> +       /* Setup APLIC domaincfg register */
> +       val = readl(priv->regs + APLIC_DOMAINCFG);
> +       val |= APLIC_DOMAINCFG_IE;
> +       if (!priv->nr_idcs)
> +               val |= APLIC_DOMAINCFG_DM;
> +       writel(val, priv->regs + APLIC_DOMAINCFG);
> +       if (readl(priv->regs + APLIC_DOMAINCFG) != val)
> +               dev_warn(priv->dev,
> +                        "unable to write 0x%x in domaincfg\n", val);
> +}
> +
> +static void aplic_msi_write_msg(struct msi_desc *desc, struct msi_msg *msg)
> +{
> +       unsigned int group_index, hart_index, guest_index, val;
> +       struct device *dev = msi_desc_to_dev(desc);
> +       struct aplic_priv *priv = dev_get_drvdata(dev);
> +       struct irq_data *d = irq_get_irq_data(desc->irq);
> +       struct aplic_msicfg *mc = &priv->msicfg;
> +       phys_addr_t tppn, tbppn, msg_addr;
> +       void __iomem *target;
> +
> +       /* Save the MSI address and data */
> +       msg_addr = (((u64)msg->address_hi) << 32) | msg->address_lo;
> +       WARN_ON(msg->data > APLIC_TARGET_EIID_MASK);
> +
> +       /* Compute target HART PPN */
> +       tppn = msg_addr >> APLIC_xMSICFGADDR_PPN_SHIFT;
> +
> +       /* Compute target HART Base PPN */
> +       tbppn = tppn;
> +       tbppn &= ~APLIC_xMSICFGADDR_PPN_HART(mc->lhxs);
> +       tbppn &= ~APLIC_xMSICFGADDR_PPN_LHX(mc->lhxw, mc->lhxs);
> +       tbppn &= ~APLIC_xMSICFGADDR_PPN_HHX(mc->hhxw, mc->hhxs);
> +       WARN_ON(tbppn != mc->base_ppn);

platform_msi_write_msg() will get called with a zeroed-out msg_msg in
the free_irq() path, triggering this warning. Should detect that case
and (I assume) just write 0 to the APLIC target register.

-Andrew

> +
> +       /* Compute target group and hart indexes */
> +       group_index = (tppn >> APLIC_xMSICFGADDR_PPN_HHX_SHIFT(mc->hhxs)) &
> +                    APLIC_xMSICFGADDR_PPN_HHX_MASK(mc->hhxw);
> +       hart_index = (tppn >> APLIC_xMSICFGADDR_PPN_LHX_SHIFT(mc->lhxs)) &
> +                    APLIC_xMSICFGADDR_PPN_LHX_MASK(mc->lhxw);
> +       hart_index |= (group_index << mc->lhxw);
> +       WARN_ON(hart_index > APLIC_TARGET_HART_IDX_MASK);
> +
> +       /* Compute target guest index */
> +       guest_index = tppn & APLIC_xMSICFGADDR_PPN_HART(mc->lhxs);
> +       WARN_ON(guest_index > APLIC_TARGET_GUEST_IDX_MASK);
> +
> +       /* Update IRQ TARGET register */
> +       target = priv->regs + APLIC_TARGET_BASE;
> +       target += (d->hwirq - 1) * sizeof(u32);
> +       val = (hart_index & APLIC_TARGET_HART_IDX_MASK)
> +                               << APLIC_TARGET_HART_IDX_SHIFT;
> +       val |= (guest_index & APLIC_TARGET_GUEST_IDX_MASK)
> +                               << APLIC_TARGET_GUEST_IDX_SHIFT;
> +       val |= (msg->data & APLIC_TARGET_EIID_MASK);
> +       writel(val, target);
> +}
> +
> +static int aplic_setup_msi(struct aplic_priv *priv)
> +{
> +       struct device *dev = priv->dev;
> +       struct aplic_msicfg *mc = &priv->msicfg;
> +       const struct imsic_global_config *imsic_global;
> +
> +       /*
> +        * The APLIC outgoing MSI config registers assume target MSI
> +        * controller to be RISC-V AIA IMSIC controller.
> +        */
> +       imsic_global = imsic_get_global_config();
> +       if (!imsic_global) {
> +               dev_err(dev, "IMSIC global config not found\n");
> +               return -ENODEV;
> +       }
> +
> +       /* Find number of guest index bits (LHXS) */
> +       mc->lhxs = imsic_global->guest_index_bits;
> +       if (APLIC_xMSICFGADDRH_LHXS_MASK < mc->lhxs) {
> +               dev_err(dev, "IMSIC guest index bits big for APLIC LHXS\n");
> +               return -EINVAL;
> +       }
> +
> +       /* Find number of HART index bits (LHXW) */
> +       mc->lhxw = imsic_global->hart_index_bits;
> +       if (APLIC_xMSICFGADDRH_LHXW_MASK < mc->lhxw) {
> +               dev_err(dev, "IMSIC hart index bits big for APLIC LHXW\n");
> +               return -EINVAL;
> +       }
> +
> +       /* Find number of group index bits (HHXW) */
> +       mc->hhxw = imsic_global->group_index_bits;
> +       if (APLIC_xMSICFGADDRH_HHXW_MASK < mc->hhxw) {
> +               dev_err(dev, "IMSIC group index bits big for APLIC HHXW\n");
> +               return -EINVAL;
> +       }
> +
> +       /* Find first bit position of group index (HHXS) */
> +       mc->hhxs = imsic_global->group_index_shift;
> +       if (mc->hhxs < (2 * APLIC_xMSICFGADDR_PPN_SHIFT)) {
> +               dev_err(dev, "IMSIC group index shift should be >= %d\n",
> +                       (2 * APLIC_xMSICFGADDR_PPN_SHIFT));
> +               return -EINVAL;
> +       }
> +       mc->hhxs -= (2 * APLIC_xMSICFGADDR_PPN_SHIFT);
> +       if (APLIC_xMSICFGADDRH_HHXS_MASK < mc->hhxs) {
> +               dev_err(dev, "IMSIC group index shift big for APLIC HHXS\n");
> +               return -EINVAL;
> +       }
> +
> +       /* Compute PPN base */
> +       mc->base_ppn = imsic_global->base_addr >> APLIC_xMSICFGADDR_PPN_SHIFT;
> +       mc->base_ppn &= ~APLIC_xMSICFGADDR_PPN_HART(mc->lhxs);
> +       mc->base_ppn &= ~APLIC_xMSICFGADDR_PPN_LHX(mc->lhxw, mc->lhxs);
> +       mc->base_ppn &= ~APLIC_xMSICFGADDR_PPN_HHX(mc->hhxw, mc->hhxs);
> +
> +       /* Use all possible CPUs as lmask */
> +       cpumask_copy(&priv->lmask, cpu_possible_mask);
> +
> +       return 0;
> +}
> +
> +/*
> + * To handle an APLIC IDC interrupts, we just read the CLAIMI register
> + * which will return highest priority pending interrupt and clear the
> + * pending bit of the interrupt. This process is repeated until CLAIMI
> + * register return zero value.
> + */
> +static void aplic_idc_handle_irq(struct irq_desc *desc)
> +{
> +       struct aplic_idc *idc = this_cpu_ptr(&aplic_idcs);
> +       struct irq_chip *chip = irq_desc_get_chip(desc);
> +       irq_hw_number_t hw_irq;
> +       int irq;
> +
> +       chained_irq_enter(chip, desc);
> +
> +       while ((hw_irq = readl(idc->regs + APLIC_IDC_CLAIMI))) {
> +               hw_irq = hw_irq >> APLIC_IDC_TOPI_ID_SHIFT;
> +               irq = irq_find_mapping(idc->priv->irqdomain, hw_irq);
> +
> +               if (unlikely(irq <= 0))
> +                       pr_warn_ratelimited("hw_irq %lu mapping not found\n",
> +                                           hw_irq);
> +               else
> +                       generic_handle_irq(irq);
> +       }
> +
> +       chained_irq_exit(chip, desc);
> +}
> +
> +static void aplic_idc_set_delivery(struct aplic_idc *idc, bool en)
> +{
> +       u32 de = (en) ? APLIC_ENABLE_IDELIVERY : APLIC_DISABLE_IDELIVERY;
> +       u32 th = (en) ? APLIC_ENABLE_ITHRESHOLD : APLIC_DISABLE_ITHRESHOLD;
> +
> +       /* Priority must be less than threshold for interrupt triggering */
> +       writel(th, idc->regs + APLIC_IDC_ITHRESHOLD);
> +
> +       /* Delivery must be set to 1 for interrupt triggering */
> +       writel(de, idc->regs + APLIC_IDC_IDELIVERY);
> +}
> +
> +static int aplic_idc_dying_cpu(unsigned int cpu)
> +{
> +       if (aplic_idc_parent_irq)
> +               disable_percpu_irq(aplic_idc_parent_irq);
> +
> +       return 0;
> +}
> +
> +static int aplic_idc_starting_cpu(unsigned int cpu)
> +{
> +       if (aplic_idc_parent_irq)
> +               enable_percpu_irq(aplic_idc_parent_irq,
> +                                 irq_get_trigger_type(aplic_idc_parent_irq));
> +
> +       return 0;
> +}
> +
> +static int aplic_setup_idc(struct aplic_priv *priv)
> +{
> +       int i, j, rc, cpu, setup_count = 0;
> +       struct device_node *node = priv->dev->of_node;
> +       struct device *dev = priv->dev;
> +       struct of_phandle_args parent;
> +       struct irq_domain *domain;
> +       unsigned long hartid;
> +       struct aplic_idc *idc;
> +       u32 val;
> +
> +       /* Setup per-CPU IDC and target CPU mask */
> +       for (i = 0; i < priv->nr_idcs; i++) {
> +               if (of_irq_parse_one(node, i, &parent)) {
> +                       dev_err(dev, "failed to parse parent for IDC%d.\n",
> +                               i);
> +                       return -EIO;
> +               }
> +
> +               /* Skip IDCs which do not connect to external interrupts */
> +               if (parent.args[0] != RV_IRQ_EXT)
> +                       continue;
> +
> +               rc = riscv_of_parent_hartid(parent.np, &hartid);
> +               if (rc) {
> +                       dev_err(dev, "failed to parse hart ID for IDC%d.\n",
> +                               i);
> +                       return rc;
> +               }
> +
> +               cpu = riscv_hartid_to_cpuid(hartid);
> +               if (cpu < 0) {
> +                       dev_warn(dev, "invalid cpuid for IDC%d\n", i);
> +                       continue;
> +               }
> +
> +               cpumask_set_cpu(cpu, &priv->lmask);
> +
> +               idc = per_cpu_ptr(&aplic_idcs, cpu);
> +               WARN_ON(idc->priv);
> +
> +               idc->hart_index = i;
> +               idc->regs = priv->regs + APLIC_IDC_BASE + i * APLIC_IDC_SIZE;
> +               idc->priv = priv;
> +
> +               aplic_idc_set_delivery(idc, true);
> +
> +               /*
> +                * Boot cpu might not have APLIC hart_index = 0 so check
> +                * and update target registers of all interrupts.
> +                */
> +               if (cpu == smp_processor_id() && idc->hart_index) {
> +                       val = idc->hart_index & APLIC_TARGET_HART_IDX_MASK;
> +                       val <<= APLIC_TARGET_HART_IDX_SHIFT;
> +                       val |= APLIC_DEFAULT_PRIORITY;
> +                       for (j = 1; j <= priv->nr_irqs; j++)
> +                               writel(val, priv->regs + APLIC_TARGET_BASE +
> +                                           (j - 1) * sizeof(u32));
> +               }
> +
> +               setup_count++;
> +       }
> +
> +       /* Find parent domain and register chained handler */
> +       domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(),
> +                                         DOMAIN_BUS_ANY);
> +       if (!aplic_idc_parent_irq && domain) {
> +               aplic_idc_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT);
> +               if (aplic_idc_parent_irq) {
> +                       irq_set_chained_handler(aplic_idc_parent_irq,
> +                                               aplic_idc_handle_irq);
> +
> +                       /*
> +                        * Setup CPUHP notifier to enable IDC parent
> +                        * interrupt on all CPUs
> +                        */
> +                       cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
> +                                         "irqchip/riscv/aplic:starting",
> +                                         aplic_idc_starting_cpu,
> +                                         aplic_idc_dying_cpu);
> +               }
> +       }
> +
> +       /* Fail if we were not able to setup IDC for any CPU */
> +       return (setup_count) ? 0 : -ENODEV;
> +}
> +
> +static int aplic_probe(struct platform_device *pdev)
> +{
> +       struct device_node *node = pdev->dev.of_node;
> +       struct device *dev = &pdev->dev;
> +       struct aplic_priv *priv;
> +       struct resource *regs;
> +       phys_addr_t pa;
> +       int rc;
> +
> +       regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (!regs) {
> +               dev_err(dev, "cannot find registers resource\n");
> +               return -ENOENT;
> +       }
> +
> +       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +       if (!priv)
> +               return -ENOMEM;
> +       platform_set_drvdata(pdev, priv);
> +       priv->dev = dev;
> +
> +       priv->regs = devm_ioremap(dev, regs->start, resource_size(regs));
> +       if (WARN_ON(!priv->regs)) {
> +               dev_err(dev, "failed ioremap registers\n");
> +               return -EIO;
> +       }
> +
> +       of_property_read_u32(node, "riscv,num-sources", &priv->nr_irqs);
> +       if (!priv->nr_irqs) {
> +               dev_err(dev, "failed to get number of interrupt sources\n");
> +               return -EINVAL;
> +       }
> +
> +       /* Setup initial state APLIC interrupts */
> +       aplic_init_hw_irqs(priv);
> +
> +       /* Setup IDCs or MSIs based on parent interrupts in DT node */
> +       priv->nr_idcs = of_irq_count(node);
> +       if (priv->nr_idcs)
> +               rc = aplic_setup_idc(priv);
> +       else
> +               rc = aplic_setup_msi(priv);
> +       if (rc)
> +               return rc;
> +
> +       /* Setup global config and interrupt delivery */
> +       aplic_init_hw_global(priv);
> +
> +       /* Create irq domain instance for the APLIC */
> +       if (priv->nr_idcs)
> +               priv->irqdomain = irq_domain_create_linear(
> +                                               of_node_to_fwnode(node),
> +                                               priv->nr_irqs + 1,
> +                                               &aplic_irqdomain_idc_ops,
> +                                               priv);
> +       else
> +               priv->irqdomain = platform_msi_create_device_domain(dev,
> +                                               priv->nr_irqs + 1,
> +                                               aplic_msi_write_msg,
> +                                               &aplic_irqdomain_msi_ops,
> +                                               priv);
> +       if (!priv->irqdomain) {
> +               dev_err(dev, "failed to add irq domain\n");
> +               return -ENOMEM;
> +       }
> +
> +       /* Advertise the interrupt controller */
> +       if (priv->nr_idcs) {
> +               dev_info(dev, "%d interrupts directly connected to %d CPUs\n",
> +                        priv->nr_irqs, priv->nr_idcs);
> +       } else {
> +               pa = priv->msicfg.base_ppn << APLIC_xMSICFGADDR_PPN_SHIFT;
> +               dev_info(dev, "%d interrupts forwared to MSI base %pa\n",
> +                        priv->nr_irqs, &pa);
> +       }
> +
> +       return 0;
> +}
> +
> +static int aplic_remove(struct platform_device *pdev)
> +{
> +       struct aplic_priv *priv = platform_get_drvdata(pdev);
> +
> +       irq_domain_remove(priv->irqdomain);
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id aplic_match[] = {
> +       { .compatible = "riscv,aplic" },
> +       {}
> +};
> +
> +static struct platform_driver aplic_driver = {
> +       .driver = {
> +               .name           = "riscv-aplic",
> +               .of_match_table = aplic_match,
> +       },
> +       .probe = aplic_probe,
> +       .remove = aplic_remove,
> +};
> +
> +static int __init aplic_init(void)
> +{
> +       return platform_driver_register(&aplic_driver);
> +}
> +core_initcall(aplic_init);
> diff --git a/include/linux/irqchip/riscv-aplic.h b/include/linux/irqchip/riscv-aplic.h
> new file mode 100644
> index 000000000000..88177eefd411
> --- /dev/null
> +++ b/include/linux/irqchip/riscv-aplic.h
> @@ -0,0 +1,117 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2021 Western Digital Corporation or its affiliates.
> + * Copyright (C) 2022 Ventana Micro Systems Inc.
> + */
> +#ifndef __LINUX_IRQCHIP_RISCV_APLIC_H
> +#define __LINUX_IRQCHIP_RISCV_APLIC_H
> +
> +#include <linux/bitops.h>
> +
> +#define APLIC_MAX_IDC                  BIT(14)
> +#define APLIC_MAX_SOURCE               1024
> +
> +#define APLIC_DOMAINCFG                        0x0000
> +#define APLIC_DOMAINCFG_RDONLY         0x80000000
> +#define APLIC_DOMAINCFG_IE             BIT(8)
> +#define APLIC_DOMAINCFG_DM             BIT(2)
> +#define APLIC_DOMAINCFG_BE             BIT(0)
> +
> +#define APLIC_SOURCECFG_BASE           0x0004
> +#define APLIC_SOURCECFG_D              BIT(10)
> +#define APLIC_SOURCECFG_CHILDIDX_MASK  0x000003ff
> +#define APLIC_SOURCECFG_SM_MASK        0x00000007
> +#define APLIC_SOURCECFG_SM_INACTIVE    0x0
> +#define APLIC_SOURCECFG_SM_DETACH      0x1
> +#define APLIC_SOURCECFG_SM_EDGE_RISE   0x4
> +#define APLIC_SOURCECFG_SM_EDGE_FALL   0x5
> +#define APLIC_SOURCECFG_SM_LEVEL_HIGH  0x6
> +#define APLIC_SOURCECFG_SM_LEVEL_LOW   0x7
> +
> +#define APLIC_MMSICFGADDR              0x1bc0
> +#define APLIC_MMSICFGADDRH             0x1bc4
> +#define APLIC_SMSICFGADDR              0x1bc8
> +#define APLIC_SMSICFGADDRH             0x1bcc
> +
> +#ifdef CONFIG_RISCV_M_MODE
> +#define APLIC_xMSICFGADDR              APLIC_MMSICFGADDR
> +#define APLIC_xMSICFGADDRH             APLIC_MMSICFGADDRH
> +#else
> +#define APLIC_xMSICFGADDR              APLIC_SMSICFGADDR
> +#define APLIC_xMSICFGADDRH             APLIC_SMSICFGADDRH
> +#endif
> +
> +#define APLIC_xMSICFGADDRH_L           BIT(31)
> +#define APLIC_xMSICFGADDRH_HHXS_MASK   0x1f
> +#define APLIC_xMSICFGADDRH_HHXS_SHIFT  24
> +#define APLIC_xMSICFGADDRH_LHXS_MASK   0x7
> +#define APLIC_xMSICFGADDRH_LHXS_SHIFT  20
> +#define APLIC_xMSICFGADDRH_HHXW_MASK   0x7
> +#define APLIC_xMSICFGADDRH_HHXW_SHIFT  16
> +#define APLIC_xMSICFGADDRH_LHXW_MASK   0xf
> +#define APLIC_xMSICFGADDRH_LHXW_SHIFT  12
> +#define APLIC_xMSICFGADDRH_BAPPN_MASK  0xfff
> +
> +#define APLIC_xMSICFGADDR_PPN_SHIFT    12
> +
> +#define APLIC_xMSICFGADDR_PPN_HART(__lhxs) \
> +       (BIT(__lhxs) - 1)
> +
> +#define APLIC_xMSICFGADDR_PPN_LHX_MASK(__lhxw) \
> +       (BIT(__lhxw) - 1)
> +#define APLIC_xMSICFGADDR_PPN_LHX_SHIFT(__lhxs) \
> +       ((__lhxs))
> +#define APLIC_xMSICFGADDR_PPN_LHX(__lhxw, __lhxs) \
> +       (APLIC_xMSICFGADDR_PPN_LHX_MASK(__lhxw) << \
> +        APLIC_xMSICFGADDR_PPN_LHX_SHIFT(__lhxs))
> +
> +#define APLIC_xMSICFGADDR_PPN_HHX_MASK(__hhxw) \
> +       (BIT(__hhxw) - 1)
> +#define APLIC_xMSICFGADDR_PPN_HHX_SHIFT(__hhxs) \
> +       ((__hhxs) + APLIC_xMSICFGADDR_PPN_SHIFT)
> +#define APLIC_xMSICFGADDR_PPN_HHX(__hhxw, __hhxs) \
> +       (APLIC_xMSICFGADDR_PPN_HHX_MASK(__hhxw) << \
> +        APLIC_xMSICFGADDR_PPN_HHX_SHIFT(__hhxs))
> +
> +#define APLIC_SETIP_BASE               0x1c00
> +#define APLIC_SETIPNUM                 0x1cdc
> +
> +#define APLIC_CLRIP_BASE               0x1d00
> +#define APLIC_CLRIPNUM                 0x1ddc
> +
> +#define APLIC_SETIE_BASE               0x1e00
> +#define APLIC_SETIENUM                 0x1edc
> +
> +#define APLIC_CLRIE_BASE               0x1f00
> +#define APLIC_CLRIENUM                 0x1fdc
> +
> +#define APLIC_SETIPNUM_LE              0x2000
> +#define APLIC_SETIPNUM_BE              0x2004
> +
> +#define APLIC_GENMSI                   0x3000
> +
> +#define APLIC_TARGET_BASE              0x3004
> +#define APLIC_TARGET_HART_IDX_SHIFT    18
> +#define APLIC_TARGET_HART_IDX_MASK     0x3fff
> +#define APLIC_TARGET_GUEST_IDX_SHIFT   12
> +#define APLIC_TARGET_GUEST_IDX_MASK    0x3f
> +#define APLIC_TARGET_IPRIO_MASK        0xff
> +#define APLIC_TARGET_EIID_MASK 0x7ff
> +
> +#define APLIC_IDC_BASE                 0x4000
> +#define APLIC_IDC_SIZE                 32
> +
> +#define APLIC_IDC_IDELIVERY            0x00
> +
> +#define APLIC_IDC_IFORCE               0x04
> +
> +#define APLIC_IDC_ITHRESHOLD           0x08
> +
> +#define APLIC_IDC_TOPI                 0x18
> +#define APLIC_IDC_TOPI_ID_SHIFT        16
> +#define APLIC_IDC_TOPI_ID_MASK 0x3ff
> +#define APLIC_IDC_TOPI_PRIO_MASK       0xff
> +
> +#define APLIC_IDC_CLAIMI               0x1c
> +
> +#endif
> --
> 2.34.1
>

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 2/9] RISC-V: Detect AIA CSRs from ISA string
  2022-11-11  4:42   ` Anup Patel
@ 2022-11-13 14:20     ` Conor Dooley
  -1 siblings, 0 replies; 96+ messages in thread
From: Conor Dooley @ 2022-11-13 14:20 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

On Fri, Nov 11, 2022 at 10:12:00AM +0530, Anup Patel wrote:
> We have two extension names for AIA ISA support: Smaia (M-mode AIA CSRs)
> and Ssaia (S-mode AIA CSRs).
> 
> We extend the ISA string parsing to detect Smaia and Ssaia extensions.
> 
> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> ---
>  arch/riscv/include/asm/hwcap.h | 8 ++++++++
>  arch/riscv/kernel/cpu.c        | 2 ++
>  arch/riscv/kernel/cpufeature.c | 2 ++
>  3 files changed, 12 insertions(+)
> 
> diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h
> index b22525290073..06314220284f 100644
> --- a/arch/riscv/include/asm/hwcap.h
> +++ b/arch/riscv/include/asm/hwcap.h
> @@ -59,9 +59,17 @@ enum riscv_isa_ext_id {
>  	RISCV_ISA_EXT_ZIHINTPAUSE,
>  	RISCV_ISA_EXT_SSTC,
>  	RISCV_ISA_EXT_SVINVAL,
> +	RISCV_ISA_EXT_SSAIA,
> +	RISCV_ISA_EXT_SMAIA,
>  	RISCV_ISA_EXT_ID_MAX = RISCV_ISA_EXT_MAX,
>  };
>  
> +#ifdef CONFIG_RISCV_M_MODE
> +#define RISCV_ISA_EXT_SxAIA		RISCV_ISA_EXT_SMAIA
> +#else
> +#define RISCV_ISA_EXT_SxAIA		RISCV_ISA_EXT_SSAIA
> +#endif
> +
>  /*
>   * This enum represents the logical ID for each RISC-V ISA extension static
>   * keys. We can use static key to optimize code path if some ISA extensions
> diff --git a/arch/riscv/kernel/cpu.c b/arch/riscv/kernel/cpu.c
> index 852ecccd8920..3c84680c2289 100644
> --- a/arch/riscv/kernel/cpu.c
> +++ b/arch/riscv/kernel/cpu.c
> @@ -138,6 +138,8 @@ device_initcall(riscv_cpuinfo_init);
>   *    extensions by an underscore.
>   */
>  static struct riscv_isa_ext_data isa_ext_arr[] = {
> +	__RISCV_ISA_EXT_DATA(smaia, RISCV_ISA_EXT_SMAIA),
> +	__RISCV_ISA_EXT_DATA(ssaia, RISCV_ISA_EXT_SSAIA),

Trivially minor question: Is there a reason you added these before
after svinval elsewhere but before it here?

>  	__RISCV_ISA_EXT_DATA(sscofpmf, RISCV_ISA_EXT_SSCOFPMF),
>  	__RISCV_ISA_EXT_DATA(sstc, RISCV_ISA_EXT_SSTC),
>  	__RISCV_ISA_EXT_DATA(svinval, RISCV_ISA_EXT_SVINVAL),
> diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
> index 694267d1fe81..e6d750d088ab 100644
> --- a/arch/riscv/kernel/cpufeature.c
> +++ b/arch/riscv/kernel/cpufeature.c
> @@ -205,6 +205,8 @@ void __init riscv_fill_hwcap(void)
>  				SET_ISA_EXT_MAP("zihintpause", RISCV_ISA_EXT_ZIHINTPAUSE);
>  				SET_ISA_EXT_MAP("sstc", RISCV_ISA_EXT_SSTC);
>  				SET_ISA_EXT_MAP("svinval", RISCV_ISA_EXT_SVINVAL);
> +				SET_ISA_EXT_MAP("smaia", RISCV_ISA_EXT_SMAIA);
> +				SET_ISA_EXT_MAP("ssaia", RISCV_ISA_EXT_SSAIA);
>  			}
>  #undef SET_ISA_EXT_MAP
>  		}
> -- 
> 2.34.1
> 

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

* Re: [PATCH 2/9] RISC-V: Detect AIA CSRs from ISA string
@ 2022-11-13 14:20     ` Conor Dooley
  0 siblings, 0 replies; 96+ messages in thread
From: Conor Dooley @ 2022-11-13 14:20 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

On Fri, Nov 11, 2022 at 10:12:00AM +0530, Anup Patel wrote:
> We have two extension names for AIA ISA support: Smaia (M-mode AIA CSRs)
> and Ssaia (S-mode AIA CSRs).
> 
> We extend the ISA string parsing to detect Smaia and Ssaia extensions.
> 
> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> ---
>  arch/riscv/include/asm/hwcap.h | 8 ++++++++
>  arch/riscv/kernel/cpu.c        | 2 ++
>  arch/riscv/kernel/cpufeature.c | 2 ++
>  3 files changed, 12 insertions(+)
> 
> diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h
> index b22525290073..06314220284f 100644
> --- a/arch/riscv/include/asm/hwcap.h
> +++ b/arch/riscv/include/asm/hwcap.h
> @@ -59,9 +59,17 @@ enum riscv_isa_ext_id {
>  	RISCV_ISA_EXT_ZIHINTPAUSE,
>  	RISCV_ISA_EXT_SSTC,
>  	RISCV_ISA_EXT_SVINVAL,
> +	RISCV_ISA_EXT_SSAIA,
> +	RISCV_ISA_EXT_SMAIA,
>  	RISCV_ISA_EXT_ID_MAX = RISCV_ISA_EXT_MAX,
>  };
>  
> +#ifdef CONFIG_RISCV_M_MODE
> +#define RISCV_ISA_EXT_SxAIA		RISCV_ISA_EXT_SMAIA
> +#else
> +#define RISCV_ISA_EXT_SxAIA		RISCV_ISA_EXT_SSAIA
> +#endif
> +
>  /*
>   * This enum represents the logical ID for each RISC-V ISA extension static
>   * keys. We can use static key to optimize code path if some ISA extensions
> diff --git a/arch/riscv/kernel/cpu.c b/arch/riscv/kernel/cpu.c
> index 852ecccd8920..3c84680c2289 100644
> --- a/arch/riscv/kernel/cpu.c
> +++ b/arch/riscv/kernel/cpu.c
> @@ -138,6 +138,8 @@ device_initcall(riscv_cpuinfo_init);
>   *    extensions by an underscore.
>   */
>  static struct riscv_isa_ext_data isa_ext_arr[] = {
> +	__RISCV_ISA_EXT_DATA(smaia, RISCV_ISA_EXT_SMAIA),
> +	__RISCV_ISA_EXT_DATA(ssaia, RISCV_ISA_EXT_SSAIA),

Trivially minor question: Is there a reason you added these before
after svinval elsewhere but before it here?

>  	__RISCV_ISA_EXT_DATA(sscofpmf, RISCV_ISA_EXT_SSCOFPMF),
>  	__RISCV_ISA_EXT_DATA(sstc, RISCV_ISA_EXT_SSTC),
>  	__RISCV_ISA_EXT_DATA(svinval, RISCV_ISA_EXT_SVINVAL),
> diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
> index 694267d1fe81..e6d750d088ab 100644
> --- a/arch/riscv/kernel/cpufeature.c
> +++ b/arch/riscv/kernel/cpufeature.c
> @@ -205,6 +205,8 @@ void __init riscv_fill_hwcap(void)
>  				SET_ISA_EXT_MAP("zihintpause", RISCV_ISA_EXT_ZIHINTPAUSE);
>  				SET_ISA_EXT_MAP("sstc", RISCV_ISA_EXT_SSTC);
>  				SET_ISA_EXT_MAP("svinval", RISCV_ISA_EXT_SVINVAL);
> +				SET_ISA_EXT_MAP("smaia", RISCV_ISA_EXT_SMAIA);
> +				SET_ISA_EXT_MAP("ssaia", RISCV_ISA_EXT_SSAIA);
>  			}
>  #undef SET_ISA_EXT_MAP
>  		}
> -- 
> 2.34.1
> 

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
  2022-11-11  4:42   ` Anup Patel
@ 2022-11-13 14:48     ` Conor Dooley
  -1 siblings, 0 replies; 96+ messages in thread
From: Conor Dooley @ 2022-11-13 14:48 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

Hey Anup,

On Fri, Nov 11, 2022 at 10:12:02AM +0530, Anup Patel wrote:
> dt-bindings: Add RISC-V incoming MSI controller bindings

nit: it looks like the usual prefix here is "dt-bindings:
interrupt-controller".

> We add DT bindings document for RISC-V incoming MSI controller (IMSIC)
> defined by the RISC-V advanced interrupt architecture (AIA) specification.
> 
> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> ---
>  .../interrupt-controller/riscv,imsic.yaml     | 174 ++++++++++++++++++
>  1 file changed, 174 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> 
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> new file mode 100644
> index 000000000000..05106eb1955e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> @@ -0,0 +1,174 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/interrupt-controller/riscv,imsic.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: RISC-V Incoming MSI Controller (IMSIC)
> +
> +maintainers:
> +  - Anup Patel <anup@brainfault.org>
> +
> +description:

Is this one of the situations where we want to have a | after
"description:" to preserve formatting?

> +  The RISC-V advanced interrupt architecture (AIA) defines a per-CPU incoming
> +  MSI controller (IMSIC) for handling MSIs in a RISC-V platform. The RISC-V
> +  AIA specification can be found at https://github.com/riscv/riscv-aia.
> +
> +  The IMSIC is a per-CPU (or per-HART) device with separate interrupt file
> +  for each privilege level (machine or supervisor). The configuration of
> +  a IMSIC interrupt file is done using AIA CSRs and it also has a 4KB MMIO
> +  space to receive MSIs from devices. Each IMSIC interrupt file supports a
> +  fixed number of interrupt identities (to distinguish MSIs from devices)
> +  which is same for given privilege level across CPUs (or HARTs).
> +
> +  The arrangement of IMSIC interrupt files in MMIO space of a RISC-V platform
> +  follows a particular scheme defined by the RISC-V AIA specification. A IMSIC
> +  group is a set of IMSIC interrupt files co-located in MMIO space and we can
> +  have multiple IMSIC groups (i.e. clusters, sockets, chiplets, etc) in a
> +  RISC-V platform. The MSI target address of a IMSIC interrupt file at given
> +  privilege level (machine or supervisor) encodes group index, HART index,
> +  and guest index (shown below).
> +
> +  XLEN-1           >=24                                 12    0
> +  |                  |                                  |     |
> +  -------------------------------------------------------------
> +  |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
> +  -------------------------------------------------------------
> +
> +  The device tree of a RISC-V platform will have one IMSIC device tree node
> +  for each privilege level (machine or supervisor) which collectively describe
> +  IMSIC interrupt files at that privilege level across CPUs (or HARTs).
> +
> +allOf:
> +  - $ref: /schemas/interrupt-controller.yaml#
> +
> +properties:
> +  compatible:
> +    items:
> +      - enum:
> +          - vendor,chip-imsics

Is it valid to have a dummy here? I did a bit of grepping & could not
see a single other yaml binding which used a placeholder like this -
other than the example schema itself. I assume you're trying to get
across the point that using the bare riscv,imsics is not okay and a
vendor should create a custom string for their implementation?

Also, the file name says "riscv,imsic", the description says "IMSIC" but
you've used "imsics" in the compatible. Is this a typo, or a plural?

Thanks,
Conor.

> +      - const: riscv,imsics
> +
> +  reg:
> +    minItems: 1
> +    maxItems: 128
> +    description:
> +      Base address of each IMSIC group.
> +
> +  interrupt-controller: true
> +
> +  "#interrupt-cells":
> +    const: 0
> +
> +  msi-controller: true
> +
> +  interrupts-extended:
> +    minItems: 1
> +    maxItems: 32768
> +    description:
> +      This property represents the set of CPUs (or HARTs) for which given
> +      device tree node describes the IMSIC interrupt files. Each node pointed
> +      to should be a riscv,cpu-intc node, which has a riscv node (i.e. RISC-V
> +      HART) as parent.
> +
> +  riscv,num-ids:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 63
> +    maximum: 2047
> +    description:
> +      Specifies how many interrupt identities are supported by IMSIC interrupt
> +      file.
> +
> +  riscv,num-guest-ids:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 63
> +    maximum: 2047
> +    description:
> +      Specifies how many interrupt identities are supported by IMSIC guest
> +      interrupt file. When not specified the number of interrupt identities
> +      supported by IMSIC guest file is assumed to be same as specified by
> +      the riscv,num-ids property.
> +
> +  riscv,slow-ipi:
> +    type: boolean
> +    description:
> +      The presence of this property implies that software interrupts (i.e.
> +      IPIs) using IMSIC software injected MSIs is slower compared to other
> +      software interrupt mechanisms (such as SBI IPI) on the underlying
> +      RISC-V platform.
> +
> +  riscv,guest-index-bits:
> +    minimum: 0
> +    maximum: 7
> +    description:
> +      Specifies number of guest index bits in the MSI target address. When
> +      not specified it is assumed to be 0.
> +
> +  riscv,hart-index-bits:
> +    minimum: 0
> +    maximum: 15
> +    description:
> +      Specifies number of HART index bits in the MSI target address. When
> +      not specified it is estimated based on the interrupts-extended property.
> +
> +  riscv,group-index-bits:
> +    minimum: 0
> +    maximum: 7
> +    description:
> +      Specifies number of group index bits in the MSI target address. When
> +      not specified it is assumed to be 0.
> +
> +  riscv,group-index-shift:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 24
> +    maximum: 55
> +    description:
> +      Specifies the least significant bit of the group index bits in the
> +      MSI target address. When not specified it is assumed to be 24.
> +
> +additionalProperties: false
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupt-controller
> +  - msi-controller
> +  - interrupts-extended
> +  - riscv,num-ids
> +
> +examples:
> +  - |
> +    // Example 1 (Machine-level IMSIC files with just one group):
> +
> +    imsic_mlevel: interrupt-controller@24000000 {
> +      compatible = "vendor,chip-imsics", "riscv,imsics";
> +      interrupts-extended = <&cpu1_intc 11>,
> +                            <&cpu2_intc 11>,
> +                            <&cpu3_intc 11>,
> +                            <&cpu4_intc 11>;
> +      reg = <0x28000000 0x4000>;
> +      interrupt-controller;
> +      #interrupt-cells = <0>;
> +      msi-controller;
> +      riscv,num-ids = <127>;
> +    };
> +
> +  - |
> +    // Example 2 (Supervisor-level IMSIC files with two groups):
> +
> +    imsic_slevel: interrupt-controller@28000000 {
> +      compatible = "vendor,chip-imsics", "riscv,imsics";
> +      interrupts-extended = <&cpu1_intc 9>,
> +                            <&cpu2_intc 9>,
> +                            <&cpu3_intc 9>,
> +                            <&cpu4_intc 9>;
> +      reg = <0x28000000 0x2000>, /* Group0 IMSICs */
> +            <0x29000000 0x2000>; /* Group1 IMSICs */
> +      interrupt-controller;
> +      #interrupt-cells = <0>;
> +      msi-controller;
> +      riscv,num-ids = <127>;
> +      riscv,group-index-bits = <1>;
> +      riscv,group-index-shift = <24>;
> +    };
> +...
> -- 
> 2.34.1
> 
> 
> _______________________________________________
> linux-riscv mailing list
> linux-riscv@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
@ 2022-11-13 14:48     ` Conor Dooley
  0 siblings, 0 replies; 96+ messages in thread
From: Conor Dooley @ 2022-11-13 14:48 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

Hey Anup,

On Fri, Nov 11, 2022 at 10:12:02AM +0530, Anup Patel wrote:
> dt-bindings: Add RISC-V incoming MSI controller bindings

nit: it looks like the usual prefix here is "dt-bindings:
interrupt-controller".

> We add DT bindings document for RISC-V incoming MSI controller (IMSIC)
> defined by the RISC-V advanced interrupt architecture (AIA) specification.
> 
> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> ---
>  .../interrupt-controller/riscv,imsic.yaml     | 174 ++++++++++++++++++
>  1 file changed, 174 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> 
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> new file mode 100644
> index 000000000000..05106eb1955e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> @@ -0,0 +1,174 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/interrupt-controller/riscv,imsic.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: RISC-V Incoming MSI Controller (IMSIC)
> +
> +maintainers:
> +  - Anup Patel <anup@brainfault.org>
> +
> +description:

Is this one of the situations where we want to have a | after
"description:" to preserve formatting?

> +  The RISC-V advanced interrupt architecture (AIA) defines a per-CPU incoming
> +  MSI controller (IMSIC) for handling MSIs in a RISC-V platform. The RISC-V
> +  AIA specification can be found at https://github.com/riscv/riscv-aia.
> +
> +  The IMSIC is a per-CPU (or per-HART) device with separate interrupt file
> +  for each privilege level (machine or supervisor). The configuration of
> +  a IMSIC interrupt file is done using AIA CSRs and it also has a 4KB MMIO
> +  space to receive MSIs from devices. Each IMSIC interrupt file supports a
> +  fixed number of interrupt identities (to distinguish MSIs from devices)
> +  which is same for given privilege level across CPUs (or HARTs).
> +
> +  The arrangement of IMSIC interrupt files in MMIO space of a RISC-V platform
> +  follows a particular scheme defined by the RISC-V AIA specification. A IMSIC
> +  group is a set of IMSIC interrupt files co-located in MMIO space and we can
> +  have multiple IMSIC groups (i.e. clusters, sockets, chiplets, etc) in a
> +  RISC-V platform. The MSI target address of a IMSIC interrupt file at given
> +  privilege level (machine or supervisor) encodes group index, HART index,
> +  and guest index (shown below).
> +
> +  XLEN-1           >=24                                 12    0
> +  |                  |                                  |     |
> +  -------------------------------------------------------------
> +  |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
> +  -------------------------------------------------------------
> +
> +  The device tree of a RISC-V platform will have one IMSIC device tree node
> +  for each privilege level (machine or supervisor) which collectively describe
> +  IMSIC interrupt files at that privilege level across CPUs (or HARTs).
> +
> +allOf:
> +  - $ref: /schemas/interrupt-controller.yaml#
> +
> +properties:
> +  compatible:
> +    items:
> +      - enum:
> +          - vendor,chip-imsics

Is it valid to have a dummy here? I did a bit of grepping & could not
see a single other yaml binding which used a placeholder like this -
other than the example schema itself. I assume you're trying to get
across the point that using the bare riscv,imsics is not okay and a
vendor should create a custom string for their implementation?

Also, the file name says "riscv,imsic", the description says "IMSIC" but
you've used "imsics" in the compatible. Is this a typo, or a plural?

Thanks,
Conor.

> +      - const: riscv,imsics
> +
> +  reg:
> +    minItems: 1
> +    maxItems: 128
> +    description:
> +      Base address of each IMSIC group.
> +
> +  interrupt-controller: true
> +
> +  "#interrupt-cells":
> +    const: 0
> +
> +  msi-controller: true
> +
> +  interrupts-extended:
> +    minItems: 1
> +    maxItems: 32768
> +    description:
> +      This property represents the set of CPUs (or HARTs) for which given
> +      device tree node describes the IMSIC interrupt files. Each node pointed
> +      to should be a riscv,cpu-intc node, which has a riscv node (i.e. RISC-V
> +      HART) as parent.
> +
> +  riscv,num-ids:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 63
> +    maximum: 2047
> +    description:
> +      Specifies how many interrupt identities are supported by IMSIC interrupt
> +      file.
> +
> +  riscv,num-guest-ids:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 63
> +    maximum: 2047
> +    description:
> +      Specifies how many interrupt identities are supported by IMSIC guest
> +      interrupt file. When not specified the number of interrupt identities
> +      supported by IMSIC guest file is assumed to be same as specified by
> +      the riscv,num-ids property.
> +
> +  riscv,slow-ipi:
> +    type: boolean
> +    description:
> +      The presence of this property implies that software interrupts (i.e.
> +      IPIs) using IMSIC software injected MSIs is slower compared to other
> +      software interrupt mechanisms (such as SBI IPI) on the underlying
> +      RISC-V platform.
> +
> +  riscv,guest-index-bits:
> +    minimum: 0
> +    maximum: 7
> +    description:
> +      Specifies number of guest index bits in the MSI target address. When
> +      not specified it is assumed to be 0.
> +
> +  riscv,hart-index-bits:
> +    minimum: 0
> +    maximum: 15
> +    description:
> +      Specifies number of HART index bits in the MSI target address. When
> +      not specified it is estimated based on the interrupts-extended property.
> +
> +  riscv,group-index-bits:
> +    minimum: 0
> +    maximum: 7
> +    description:
> +      Specifies number of group index bits in the MSI target address. When
> +      not specified it is assumed to be 0.
> +
> +  riscv,group-index-shift:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 24
> +    maximum: 55
> +    description:
> +      Specifies the least significant bit of the group index bits in the
> +      MSI target address. When not specified it is assumed to be 24.
> +
> +additionalProperties: false
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupt-controller
> +  - msi-controller
> +  - interrupts-extended
> +  - riscv,num-ids
> +
> +examples:
> +  - |
> +    // Example 1 (Machine-level IMSIC files with just one group):
> +
> +    imsic_mlevel: interrupt-controller@24000000 {
> +      compatible = "vendor,chip-imsics", "riscv,imsics";
> +      interrupts-extended = <&cpu1_intc 11>,
> +                            <&cpu2_intc 11>,
> +                            <&cpu3_intc 11>,
> +                            <&cpu4_intc 11>;
> +      reg = <0x28000000 0x4000>;
> +      interrupt-controller;
> +      #interrupt-cells = <0>;
> +      msi-controller;
> +      riscv,num-ids = <127>;
> +    };
> +
> +  - |
> +    // Example 2 (Supervisor-level IMSIC files with two groups):
> +
> +    imsic_slevel: interrupt-controller@28000000 {
> +      compatible = "vendor,chip-imsics", "riscv,imsics";
> +      interrupts-extended = <&cpu1_intc 9>,
> +                            <&cpu2_intc 9>,
> +                            <&cpu3_intc 9>,
> +                            <&cpu4_intc 9>;
> +      reg = <0x28000000 0x2000>, /* Group0 IMSICs */
> +            <0x29000000 0x2000>; /* Group1 IMSICs */
> +      interrupt-controller;
> +      #interrupt-cells = <0>;
> +      msi-controller;
> +      riscv,num-ids = <127>;
> +      riscv,group-index-bits = <1>;
> +      riscv,group-index-shift = <24>;
> +    };
> +...
> -- 
> 2.34.1
> 
> 
> _______________________________________________
> linux-riscv mailing list
> linux-riscv@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-riscv

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 6/9] dt-bindings: Add RISC-V advanced PLIC bindings
  2022-11-11  4:42   ` Anup Patel
@ 2022-11-13 15:44     ` Conor Dooley
  -1 siblings, 0 replies; 96+ messages in thread
From: Conor Dooley @ 2022-11-13 15:44 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

Hey Anup,

Ditto the $subject nit here.

On Fri, Nov 11, 2022 at 10:12:04AM +0530, Anup Patel wrote:
> We add DT bindings document for RISC-V advanced platform level interrupt
> controller (APLIC) defined by the RISC-V advanced interrupt architecture
> (AIA) specification.
> 
> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> ---
>  .../interrupt-controller/riscv,aplic.yaml     | 136 ++++++++++++++++++
>  1 file changed, 136 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> 
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> new file mode 100644
> index 000000000000..0aa48571f3bc
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> @@ -0,0 +1,136 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/interrupt-controller/riscv,aplic.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: RISC-V Advancded Platform Level Interrupt Controller (APLIC)

Typo: Advanced

> +
> +maintainers:
> +  - Anup Patel <anup@brainfault.org>
> +
> +description:
> +  The RISC-V advanced interrupt architecture (AIA) defines advanced platform
                                                             ^
Missing an article here?

> +  level interrupt controller (APLIC) for handling wired interrupts in a
> +  RISC-V platform. The RISC-V AIA specification can be found at
> +  https://github.com/riscv/riscv-aia.
> +
> +  The RISC-V APLIC is implemented as hierarchical APLIC domains where all
> +  interrupt sources connect to the root domain which can further delegate
> +  interrupts to child domains. We have one device tree node for each APLIC

While I am nitpicking, s/We have/There is/ ?

> +  domain.
> +
> +allOf:
> +  - $ref: /schemas/interrupt-controller.yaml#
> +
> +properties:
> +  compatible:
> +    items:
> +      - enum:
> +          - vendor,chip-aplic

Same comment here about the validity of this placeholder.

> +      - const: riscv,aplic
> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupt-controller: true
> +
> +  "#interrupt-cells":
> +    const: 2
> +
> +  interrupts-extended:
> +    minItems: 1
> +    maxItems: 16384
> +    description:
> +      The presence of this property implies that given APLIC domain directly
                                                   ^
Missing indefinite article here (and in msi-parent)?

> +      injects external interrupts to a set of RISC-V HARTS (or CPUs). Each
> +      node pointed to should be a riscv,cpu-intc node, which has a riscv node
> +      (i.e. RISC-V HART) as parent.
> +
> +  msi-parent:
> +    description:
> +      The presence of this property implies that given APLIC domain forwards
> +      wired interrupts as MSIs to a AIA incoming message signaled interrupt
> +      controller (IMSIC). This property should be considered only when the
> +      interrupts-extended property is absent.

This mutual exclusion can be represented, can't it?
IIRC it is some sort of oneOf thing, somewhat like below:
oneOf:
  - required:
      - msi-parent
  - required:
      - interrupts-extended

AFAIR from doing the i2c ocores binding, this will force the addition of
one, but not both, to a node.

Or is this not actually mutually exclusive & the msi-parent property is
permitted but just left unused if interrupts-extended is present?

> +  riscv,num-sources:
> +    $ref: "/schemas/types.yaml#/definitions/uint32"
> +    minimum: 1
> +    maximum: 1023
> +    description:
> +      Specifies how many wired interrupts are supported by this APLIC domain.
> +
> +  riscv,children:
> +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> +    minItems: 1
> +    maxItems: 1024
> +    description:
> +      This property represents a list of child APLIC domains for the given
> +      APLIC domain. Each child APLIC domain is assigned child index in
> +      increasing order with the first child APLIC domain assigned child
> +      index 0. The APLIC domain child index is used by firmware to delegate
> +      interrupts from the given APLIC domain to a particular child APLIC
> +      domain.
> +
> +  riscv,delegate:
> +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> +    minItems: 1
> +    maxItems: 1024
> +    description:
> +      This property represents a interrupt delegation list where each entry
> +      is a triple consisting of child APLIC domain phandle, first interrupt
> +      number, and last interrupt number. The firmware will configure interrupt
> +      delegation registers based on interrupt delegation list.

What is the inter dependence of the children and delegate?
Is it valid to have a delegate property without children?
Can the firmware delegate interrupts without the delegation list, based
on the children property alone? Or is it effectively useless without a
children property?

In your examples, the second has msi-parent but neither of these custom
properties. Do the children/delegate properties have a meaning in the
msi-parent case?

I think the binding should enforce whatever dependency exists there.
Thanks,
Conor.

> +
> +additionalProperties: false
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupt-controller
> +  - "#interrupt-cells"
> +  - riscv,num-sources
> +
> +examples:
> +  - |
> +    // Example 1 (APIC domain directly injecting interrupt to HARTs):
> +
> +    aplic0: interrupt-controller@c000000 {
> +      compatible = "vendor,chip-aplic", "riscv,aplic";
> +      interrupts-extended = <&cpu1_intc 11>,
> +                            <&cpu2_intc 11>,
> +                            <&cpu3_intc 11>,
> +                            <&cpu4_intc 11>;
> +      reg = <0xc000000 0x4080>;
> +      interrupt-controller;
> +      #interrupt-cells = <2>;
> +      riscv,num-sources = <63>;
> +      riscv,children = <&aplic1>;
> +      riscv,delegate = <&aplic1 1 63>;
> +    };
> +
> +    aplic1: interrupt-controller@d000000 {
> +      compatible = "vendor,chip-aplic", "riscv,aplic";
> +      interrupts-extended = <&cpu1_intc 9>,
> +                            <&cpu2_intc 9>,
> +                            <&cpu3_intc 9>,
> +                            <&cpu4_intc 9>;
> +      reg = <0xd000000 0x4080>;
> +      interrupt-controller;
> +      #interrupt-cells = <2>;
> +      riscv,num-sources = <63>;
> +    };
> +
> +  - |
> +    // Example 2 (APIC domain forwarding interrupts as MSIs):
> +
> +    interrupt-controller@d000000 {
> +      compatible = "vendor,chip-aplic", "riscv,aplic";
> +      msi-parent = <&imsics>;
> +      reg = <0xd000000 0x4000>;
> +      interrupt-controller;
> +      #interrupt-cells = <2>;
> +      riscv,num-sources = <63>;
> +    };
> +...
> -- 
> 2.34.1
> 
> 
> _______________________________________________
> linux-riscv mailing list
> linux-riscv@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 6/9] dt-bindings: Add RISC-V advanced PLIC bindings
@ 2022-11-13 15:44     ` Conor Dooley
  0 siblings, 0 replies; 96+ messages in thread
From: Conor Dooley @ 2022-11-13 15:44 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

Hey Anup,

Ditto the $subject nit here.

On Fri, Nov 11, 2022 at 10:12:04AM +0530, Anup Patel wrote:
> We add DT bindings document for RISC-V advanced platform level interrupt
> controller (APLIC) defined by the RISC-V advanced interrupt architecture
> (AIA) specification.
> 
> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> ---
>  .../interrupt-controller/riscv,aplic.yaml     | 136 ++++++++++++++++++
>  1 file changed, 136 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> 
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> new file mode 100644
> index 000000000000..0aa48571f3bc
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> @@ -0,0 +1,136 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/interrupt-controller/riscv,aplic.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: RISC-V Advancded Platform Level Interrupt Controller (APLIC)

Typo: Advanced

> +
> +maintainers:
> +  - Anup Patel <anup@brainfault.org>
> +
> +description:
> +  The RISC-V advanced interrupt architecture (AIA) defines advanced platform
                                                             ^
Missing an article here?

> +  level interrupt controller (APLIC) for handling wired interrupts in a
> +  RISC-V platform. The RISC-V AIA specification can be found at
> +  https://github.com/riscv/riscv-aia.
> +
> +  The RISC-V APLIC is implemented as hierarchical APLIC domains where all
> +  interrupt sources connect to the root domain which can further delegate
> +  interrupts to child domains. We have one device tree node for each APLIC

While I am nitpicking, s/We have/There is/ ?

> +  domain.
> +
> +allOf:
> +  - $ref: /schemas/interrupt-controller.yaml#
> +
> +properties:
> +  compatible:
> +    items:
> +      - enum:
> +          - vendor,chip-aplic

Same comment here about the validity of this placeholder.

> +      - const: riscv,aplic
> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupt-controller: true
> +
> +  "#interrupt-cells":
> +    const: 2
> +
> +  interrupts-extended:
> +    minItems: 1
> +    maxItems: 16384
> +    description:
> +      The presence of this property implies that given APLIC domain directly
                                                   ^
Missing indefinite article here (and in msi-parent)?

> +      injects external interrupts to a set of RISC-V HARTS (or CPUs). Each
> +      node pointed to should be a riscv,cpu-intc node, which has a riscv node
> +      (i.e. RISC-V HART) as parent.
> +
> +  msi-parent:
> +    description:
> +      The presence of this property implies that given APLIC domain forwards
> +      wired interrupts as MSIs to a AIA incoming message signaled interrupt
> +      controller (IMSIC). This property should be considered only when the
> +      interrupts-extended property is absent.

This mutual exclusion can be represented, can't it?
IIRC it is some sort of oneOf thing, somewhat like below:
oneOf:
  - required:
      - msi-parent
  - required:
      - interrupts-extended

AFAIR from doing the i2c ocores binding, this will force the addition of
one, but not both, to a node.

Or is this not actually mutually exclusive & the msi-parent property is
permitted but just left unused if interrupts-extended is present?

> +  riscv,num-sources:
> +    $ref: "/schemas/types.yaml#/definitions/uint32"
> +    minimum: 1
> +    maximum: 1023
> +    description:
> +      Specifies how many wired interrupts are supported by this APLIC domain.
> +
> +  riscv,children:
> +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> +    minItems: 1
> +    maxItems: 1024
> +    description:
> +      This property represents a list of child APLIC domains for the given
> +      APLIC domain. Each child APLIC domain is assigned child index in
> +      increasing order with the first child APLIC domain assigned child
> +      index 0. The APLIC domain child index is used by firmware to delegate
> +      interrupts from the given APLIC domain to a particular child APLIC
> +      domain.
> +
> +  riscv,delegate:
> +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> +    minItems: 1
> +    maxItems: 1024
> +    description:
> +      This property represents a interrupt delegation list where each entry
> +      is a triple consisting of child APLIC domain phandle, first interrupt
> +      number, and last interrupt number. The firmware will configure interrupt
> +      delegation registers based on interrupt delegation list.

What is the inter dependence of the children and delegate?
Is it valid to have a delegate property without children?
Can the firmware delegate interrupts without the delegation list, based
on the children property alone? Or is it effectively useless without a
children property?

In your examples, the second has msi-parent but neither of these custom
properties. Do the children/delegate properties have a meaning in the
msi-parent case?

I think the binding should enforce whatever dependency exists there.
Thanks,
Conor.

> +
> +additionalProperties: false
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupt-controller
> +  - "#interrupt-cells"
> +  - riscv,num-sources
> +
> +examples:
> +  - |
> +    // Example 1 (APIC domain directly injecting interrupt to HARTs):
> +
> +    aplic0: interrupt-controller@c000000 {
> +      compatible = "vendor,chip-aplic", "riscv,aplic";
> +      interrupts-extended = <&cpu1_intc 11>,
> +                            <&cpu2_intc 11>,
> +                            <&cpu3_intc 11>,
> +                            <&cpu4_intc 11>;
> +      reg = <0xc000000 0x4080>;
> +      interrupt-controller;
> +      #interrupt-cells = <2>;
> +      riscv,num-sources = <63>;
> +      riscv,children = <&aplic1>;
> +      riscv,delegate = <&aplic1 1 63>;
> +    };
> +
> +    aplic1: interrupt-controller@d000000 {
> +      compatible = "vendor,chip-aplic", "riscv,aplic";
> +      interrupts-extended = <&cpu1_intc 9>,
> +                            <&cpu2_intc 9>,
> +                            <&cpu3_intc 9>,
> +                            <&cpu4_intc 9>;
> +      reg = <0xd000000 0x4080>;
> +      interrupt-controller;
> +      #interrupt-cells = <2>;
> +      riscv,num-sources = <63>;
> +    };
> +
> +  - |
> +    // Example 2 (APIC domain forwarding interrupts as MSIs):
> +
> +    interrupt-controller@d000000 {
> +      compatible = "vendor,chip-aplic", "riscv,aplic";
> +      msi-parent = <&imsics>;
> +      reg = <0xd000000 0x4000>;
> +      interrupt-controller;
> +      #interrupt-cells = <2>;
> +      riscv,num-sources = <63>;
> +    };
> +...
> -- 
> 2.34.1
> 
> 
> _______________________________________________
> linux-riscv mailing list
> linux-riscv@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-riscv

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
  2022-11-11  4:42   ` Anup Patel
@ 2022-11-14  9:49     ` Krzysztof Kozlowski
  -1 siblings, 0 replies; 96+ messages in thread
From: Krzysztof Kozlowski @ 2022-11-14  9:49 UTC (permalink / raw)
  To: Anup Patel, Palmer Dabbelt, Paul Walmsley, Thomas Gleixner,
	Marc Zyngier, Rob Herring, Krzysztof Kozlowski
  Cc: Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree

On 11/11/2022 05:42, Anup Patel wrote:
> We add DT bindings document for RISC-V incoming MSI controller (IMSIC)
> defined by the RISC-V advanced interrupt architecture (AIA) specification.
> 
> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> ---
>  .../interrupt-controller/riscv,imsic.yaml     | 174 ++++++++++++++++++
>  1 file changed, 174 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> 
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> new file mode 100644
> index 000000000000..05106eb1955e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> @@ -0,0 +1,174 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/interrupt-controller/riscv,imsic.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: RISC-V Incoming MSI Controller (IMSIC)
> +
> +maintainers:
> +  - Anup Patel <anup@brainfault.org>
> +
> +description:
> +  The RISC-V advanced interrupt architecture (AIA) defines a per-CPU incoming
> +  MSI controller (IMSIC) for handling MSIs in a RISC-V platform. The RISC-V
> +  AIA specification can be found at https://github.com/riscv/riscv-aia.
> +
> +  The IMSIC is a per-CPU (or per-HART) device with separate interrupt file
> +  for each privilege level (machine or supervisor). The configuration of
> +  a IMSIC interrupt file is done using AIA CSRs and it also has a 4KB MMIO
> +  space to receive MSIs from devices. Each IMSIC interrupt file supports a
> +  fixed number of interrupt identities (to distinguish MSIs from devices)
> +  which is same for given privilege level across CPUs (or HARTs).
> +
> +  The arrangement of IMSIC interrupt files in MMIO space of a RISC-V platform
> +  follows a particular scheme defined by the RISC-V AIA specification. A IMSIC
> +  group is a set of IMSIC interrupt files co-located in MMIO space and we can
> +  have multiple IMSIC groups (i.e. clusters, sockets, chiplets, etc) in a
> +  RISC-V platform. The MSI target address of a IMSIC interrupt file at given
> +  privilege level (machine or supervisor) encodes group index, HART index,
> +  and guest index (shown below).
> +
> +  XLEN-1           >=24                                 12    0
> +  |                  |                                  |     |
> +  -------------------------------------------------------------
> +  |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
> +  -------------------------------------------------------------
> +
> +  The device tree of a RISC-V platform will have one IMSIC device tree node
> +  for each privilege level (machine or supervisor) which collectively describe
> +  IMSIC interrupt files at that privilege level across CPUs (or HARTs).
> +
> +allOf:
> +  - $ref: /schemas/interrupt-controller.yaml#
> +
> +properties:
> +  compatible:
> +    items:
> +      - enum:
> +          - vendor,chip-imsics

There is no such vendor... As Conor pointed out, this does not look
correct. Compatibles must be real and specific.

> +      - const: riscv,imsics
> +
> +  reg:
> +    minItems: 1
> +    maxItems: 128

Is there a DTS with 128 reg items?

> +    description:
> +      Base address of each IMSIC group.
> +
> +  interrupt-controller: true
> +
> +  "#interrupt-cells":
> +    const: 0
> +
> +  msi-controller: true

You want then msi-controller.yaml schema and you can drop properties
described there.

> +
> +  interrupts-extended:
> +    minItems: 1
> +    maxItems: 32768

I just wonder if you are not putting some random stuff here... just like
this "vendor" company.

32768 inputs it is quite a big chip. Are you sure you have so many pins
or internal connections?

> +    description:
> +      This property represents the set of CPUs (or HARTs) for which given
> +      device tree node describes the IMSIC interrupt files. Each node pointed
> +      to should be a riscv,cpu-intc node, which has a riscv node (i.e. RISC-V
> +      HART) as parent.
> +
> +  riscv,num-ids:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 63
> +    maximum: 2047
> +    description:
> +      Specifies how many interrupt identities are supported by IMSIC interrupt
> +      file.
> +
> +  riscv,num-guest-ids:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 63
> +    maximum: 2047
> +    description:
> +      Specifies how many interrupt identities are supported by IMSIC guest
> +      interrupt file. When not specified the number of interrupt identities
> +      supported by IMSIC guest file is assumed to be same as specified by
> +      the riscv,num-ids property.
> +
> +  riscv,slow-ipi:
> +    type: boolean
> +    description:
> +      The presence of this property implies that software interrupts (i.e.
> +      IPIs) using IMSIC software injected MSIs is slower compared to other
> +      software interrupt mechanisms (such as SBI IPI) on the underlying
> +      RISC-V platform.

Is this a property of software or hardware?

> +
> +  riscv,guest-index-bits:
> +    minimum: 0
> +    maximum: 7
> +    description:
> +      Specifies number of guest index bits in the MSI target address. When
> +      not specified it is assumed to be 0.
> +
> +  riscv,hart-index-bits:
> +    minimum: 0
> +    maximum: 15
> +    description:
> +      Specifies number of HART index bits in the MSI target address. When
> +      not specified it is estimated based on the interrupts-extended property.
> +
> +  riscv,group-index-bits:
> +    minimum: 0
> +    maximum: 7
> +    description:
> +      Specifies number of group index bits in the MSI target address. When
> +      not specified it is assumed to be 0.

Then default: 0.

> +
> +  riscv,group-index-shift:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 24
> +    maximum: 55
> +    description:
> +      Specifies the least significant bit of the group index bits in the

Please drop everywhere "Specifies the" and instead just describe the
hardware.

> +      MSI target address. When not specified it is assumed to be 24.
> +
> +additionalProperties: false

unevaluatedProperties: false and drop all properties already described
by other schemas.

> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupt-controller
> +  - msi-controller
> +  - interrupts-extended
> +  - riscv,num-ids
> +
> +examples:
> +  - |
> +    // Example 1 (Machine-level IMSIC files with just one group):
> +
> +    imsic_mlevel: interrupt-controller@24000000 {
> +      compatible = "vendor,chip-imsics", "riscv,imsics";
> +      interrupts-extended = <&cpu1_intc 11>,
> +                            <&cpu2_intc 11>,
> +                            <&cpu3_intc 11>,
> +                            <&cpu4_intc 11>;
> +      reg = <0x28000000 0x4000>;
> +      interrupt-controller;
> +      #interrupt-cells = <0>;
> +      msi-controller;
> +      riscv,num-ids = <127>;
> +    };
> +
> +  - |
> +    // Example 2 (Supervisor-level IMSIC files with two groups):
> +
> +    imsic_slevel: interrupt-controller@28000000 {
> +      compatible = "vendor,chip-imsics", "riscv,imsics";

Please run scripts/checkpatch.pl and fix reported warnings.

Best regards,
Krzysztof


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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
@ 2022-11-14  9:49     ` Krzysztof Kozlowski
  0 siblings, 0 replies; 96+ messages in thread
From: Krzysztof Kozlowski @ 2022-11-14  9:49 UTC (permalink / raw)
  To: Anup Patel, Palmer Dabbelt, Paul Walmsley, Thomas Gleixner,
	Marc Zyngier, Rob Herring, Krzysztof Kozlowski
  Cc: Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree

On 11/11/2022 05:42, Anup Patel wrote:
> We add DT bindings document for RISC-V incoming MSI controller (IMSIC)
> defined by the RISC-V advanced interrupt architecture (AIA) specification.
> 
> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> ---
>  .../interrupt-controller/riscv,imsic.yaml     | 174 ++++++++++++++++++
>  1 file changed, 174 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> 
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> new file mode 100644
> index 000000000000..05106eb1955e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> @@ -0,0 +1,174 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/interrupt-controller/riscv,imsic.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: RISC-V Incoming MSI Controller (IMSIC)
> +
> +maintainers:
> +  - Anup Patel <anup@brainfault.org>
> +
> +description:
> +  The RISC-V advanced interrupt architecture (AIA) defines a per-CPU incoming
> +  MSI controller (IMSIC) for handling MSIs in a RISC-V platform. The RISC-V
> +  AIA specification can be found at https://github.com/riscv/riscv-aia.
> +
> +  The IMSIC is a per-CPU (or per-HART) device with separate interrupt file
> +  for each privilege level (machine or supervisor). The configuration of
> +  a IMSIC interrupt file is done using AIA CSRs and it also has a 4KB MMIO
> +  space to receive MSIs from devices. Each IMSIC interrupt file supports a
> +  fixed number of interrupt identities (to distinguish MSIs from devices)
> +  which is same for given privilege level across CPUs (or HARTs).
> +
> +  The arrangement of IMSIC interrupt files in MMIO space of a RISC-V platform
> +  follows a particular scheme defined by the RISC-V AIA specification. A IMSIC
> +  group is a set of IMSIC interrupt files co-located in MMIO space and we can
> +  have multiple IMSIC groups (i.e. clusters, sockets, chiplets, etc) in a
> +  RISC-V platform. The MSI target address of a IMSIC interrupt file at given
> +  privilege level (machine or supervisor) encodes group index, HART index,
> +  and guest index (shown below).
> +
> +  XLEN-1           >=24                                 12    0
> +  |                  |                                  |     |
> +  -------------------------------------------------------------
> +  |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
> +  -------------------------------------------------------------
> +
> +  The device tree of a RISC-V platform will have one IMSIC device tree node
> +  for each privilege level (machine or supervisor) which collectively describe
> +  IMSIC interrupt files at that privilege level across CPUs (or HARTs).
> +
> +allOf:
> +  - $ref: /schemas/interrupt-controller.yaml#
> +
> +properties:
> +  compatible:
> +    items:
> +      - enum:
> +          - vendor,chip-imsics

There is no such vendor... As Conor pointed out, this does not look
correct. Compatibles must be real and specific.

> +      - const: riscv,imsics
> +
> +  reg:
> +    minItems: 1
> +    maxItems: 128

Is there a DTS with 128 reg items?

> +    description:
> +      Base address of each IMSIC group.
> +
> +  interrupt-controller: true
> +
> +  "#interrupt-cells":
> +    const: 0
> +
> +  msi-controller: true

You want then msi-controller.yaml schema and you can drop properties
described there.

> +
> +  interrupts-extended:
> +    minItems: 1
> +    maxItems: 32768

I just wonder if you are not putting some random stuff here... just like
this "vendor" company.

32768 inputs it is quite a big chip. Are you sure you have so many pins
or internal connections?

> +    description:
> +      This property represents the set of CPUs (or HARTs) for which given
> +      device tree node describes the IMSIC interrupt files. Each node pointed
> +      to should be a riscv,cpu-intc node, which has a riscv node (i.e. RISC-V
> +      HART) as parent.
> +
> +  riscv,num-ids:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 63
> +    maximum: 2047
> +    description:
> +      Specifies how many interrupt identities are supported by IMSIC interrupt
> +      file.
> +
> +  riscv,num-guest-ids:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 63
> +    maximum: 2047
> +    description:
> +      Specifies how many interrupt identities are supported by IMSIC guest
> +      interrupt file. When not specified the number of interrupt identities
> +      supported by IMSIC guest file is assumed to be same as specified by
> +      the riscv,num-ids property.
> +
> +  riscv,slow-ipi:
> +    type: boolean
> +    description:
> +      The presence of this property implies that software interrupts (i.e.
> +      IPIs) using IMSIC software injected MSIs is slower compared to other
> +      software interrupt mechanisms (such as SBI IPI) on the underlying
> +      RISC-V platform.

Is this a property of software or hardware?

> +
> +  riscv,guest-index-bits:
> +    minimum: 0
> +    maximum: 7
> +    description:
> +      Specifies number of guest index bits in the MSI target address. When
> +      not specified it is assumed to be 0.
> +
> +  riscv,hart-index-bits:
> +    minimum: 0
> +    maximum: 15
> +    description:
> +      Specifies number of HART index bits in the MSI target address. When
> +      not specified it is estimated based on the interrupts-extended property.
> +
> +  riscv,group-index-bits:
> +    minimum: 0
> +    maximum: 7
> +    description:
> +      Specifies number of group index bits in the MSI target address. When
> +      not specified it is assumed to be 0.

Then default: 0.

> +
> +  riscv,group-index-shift:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 24
> +    maximum: 55
> +    description:
> +      Specifies the least significant bit of the group index bits in the

Please drop everywhere "Specifies the" and instead just describe the
hardware.

> +      MSI target address. When not specified it is assumed to be 24.
> +
> +additionalProperties: false

unevaluatedProperties: false and drop all properties already described
by other schemas.

> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupt-controller
> +  - msi-controller
> +  - interrupts-extended
> +  - riscv,num-ids
> +
> +examples:
> +  - |
> +    // Example 1 (Machine-level IMSIC files with just one group):
> +
> +    imsic_mlevel: interrupt-controller@24000000 {
> +      compatible = "vendor,chip-imsics", "riscv,imsics";
> +      interrupts-extended = <&cpu1_intc 11>,
> +                            <&cpu2_intc 11>,
> +                            <&cpu3_intc 11>,
> +                            <&cpu4_intc 11>;
> +      reg = <0x28000000 0x4000>;
> +      interrupt-controller;
> +      #interrupt-cells = <0>;
> +      msi-controller;
> +      riscv,num-ids = <127>;
> +    };
> +
> +  - |
> +    // Example 2 (Supervisor-level IMSIC files with two groups):
> +
> +    imsic_slevel: interrupt-controller@28000000 {
> +      compatible = "vendor,chip-imsics", "riscv,imsics";

Please run scripts/checkpatch.pl and fix reported warnings.

Best regards,
Krzysztof


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 6/9] dt-bindings: Add RISC-V advanced PLIC bindings
  2022-11-11  4:42   ` Anup Patel
@ 2022-11-14  9:51     ` Krzysztof Kozlowski
  -1 siblings, 0 replies; 96+ messages in thread
From: Krzysztof Kozlowski @ 2022-11-14  9:51 UTC (permalink / raw)
  To: Anup Patel, Palmer Dabbelt, Paul Walmsley, Thomas Gleixner,
	Marc Zyngier, Rob Herring, Krzysztof Kozlowski
  Cc: Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree

On 11/11/2022 05:42, Anup Patel wrote:
> We add DT bindings document for RISC-V advanced platform level interrupt
> controller (APLIC) defined by the RISC-V advanced interrupt architecture
> (AIA) specification.
> 
> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> ---
>  .../interrupt-controller/riscv,aplic.yaml     | 136 ++++++++++++++++++
>  1 file changed, 136 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> 
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> new file mode 100644
> index 000000000000..0aa48571f3bc
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> @@ -0,0 +1,136 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/interrupt-controller/riscv,aplic.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: RISC-V Advancded Platform Level Interrupt Controller (APLIC)
> +
> +maintainers:
> +  - Anup Patel <anup@brainfault.org>
> +
> +description:
> +  The RISC-V advanced interrupt architecture (AIA) defines advanced platform
> +  level interrupt controller (APLIC) for handling wired interrupts in a
> +  RISC-V platform. The RISC-V AIA specification can be found at
> +  https://github.com/riscv/riscv-aia.
> +
> +  The RISC-V APLIC is implemented as hierarchical APLIC domains where all
> +  interrupt sources connect to the root domain which can further delegate
> +  interrupts to child domains. We have one device tree node for each APLIC
> +  domain.
> +
> +allOf:
> +  - $ref: /schemas/interrupt-controller.yaml#
> +
> +properties:
> +  compatible:
> +    items:
> +      - enum:
> +          - vendor,chip-aplic
> +      - const: riscv,aplic
> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupt-controller: true
> +
> +  "#interrupt-cells":
> +    const: 2
> +
> +  interrupts-extended:
> +    minItems: 1
> +    maxItems: 16384
> +    description:
> +      The presence of this property implies that given APLIC domain directly
> +      injects external interrupts to a set of RISC-V HARTS (or CPUs). Each
> +      node pointed to should be a riscv,cpu-intc node, which has a riscv node
> +      (i.e. RISC-V HART) as parent.
> +
> +  msi-parent:
> +    description:
> +      The presence of this property implies that given APLIC domain forwards

Drop "The presence of this property" and make it a proper sentence
describing hardware.

> +      wired interrupts as MSIs to a AIA incoming message signaled interrupt
> +      controller (IMSIC). This property should be considered only when the
> +      interrupts-extended property is absent.
> +
> +  riscv,num-sources:
> +    $ref: "/schemas/types.yaml#/definitions/uint32"

Drop quotes.

> +    minimum: 1
> +    maximum: 1023
> +    description:
> +      Specifies how many wired interrupts are supported by this APLIC domain.
> +
> +  riscv,children:
> +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> +    minItems: 1
> +    maxItems: 1024
> +    description:
> +      This property represents a list of child APLIC domains for the given
> +      APLIC domain. Each child APLIC domain is assigned child index in
> +      increasing order with the first child APLIC domain assigned child
> +      index 0. The APLIC domain child index is used by firmware to delegate
> +      interrupts from the given APLIC domain to a particular child APLIC
> +      domain.
> +
> +  riscv,delegate:
> +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> +    minItems: 1
> +    maxItems: 1024
> +    description:
> +      This property represents a interrupt delegation list where each entry

Drop "This property represents".

> +      is a triple consisting of child APLIC domain phandle, first interrupt
> +      number, and last interrupt number. The firmware will configure interrupt
> +      delegation registers based on interrupt delegation list.
> +
> +additionalProperties: false

Same comments as in previous patch,

> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupt-controller
> +  - "#interrupt-cells"
> +  - riscv,num-sources
> +
> +examples:
> +  - |
> +    // Example 1 (APIC domain directly injecting interrupt to HARTs):
> +
> +    aplic0: interrupt-controller@c000000 {
> +      compatible = "vendor,chip-aplic", "riscv,aplic";
> +      interrupts-extended = <&cpu1_intc 11>,
> +                            <&cpu2_intc 11>,
> +                            <&cpu3_intc 11>,
> +                            <&cpu4_intc 11>;
> +      reg = <0xc000000 0x4080>;
> +      interrupt-controller;
> +      #interrupt-cells = <2>;
> +      riscv,num-sources = <63>;
> +      riscv,children = <&aplic1>;
> +      riscv,delegate = <&aplic1 1 63>;
> +    };
> +
> +    aplic1: interrupt-controller@d000000 {
> +      compatible = "vendor,chip-aplic", "riscv,aplic";
> +      interrupts-extended = <&cpu1_intc 9>,
> +                            <&cpu2_intc 9>,
> +                            <&cpu3_intc 9>,
> +                            <&cpu4_intc 9>;
> +      reg = <0xd000000 0x4080>;
> +      interrupt-controller;
> +      #interrupt-cells = <2>;
> +      riscv,num-sources = <63>;
> +    };
> +
> +  - |
> +    // Example 2 (APIC domain forwarding interrupts as MSIs):
> +
> +    interrupt-controller@d000000 {
> +      compatible = "vendor,chip-aplic", "riscv,aplic";
> +      msi-parent = <&imsics>;
> +      reg = <0xd000000 0x4000>;
> +      interrupt-controller;
> +      #interrupt-cells = <2>;
> +      riscv,num-sources = <63>;

It's almost the same as previous... don't add unnecessary examples
(difference in one property usually does not mean you need new example).

> +    };
> +...

Best regards,
Krzysztof


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

* Re: [PATCH 6/9] dt-bindings: Add RISC-V advanced PLIC bindings
@ 2022-11-14  9:51     ` Krzysztof Kozlowski
  0 siblings, 0 replies; 96+ messages in thread
From: Krzysztof Kozlowski @ 2022-11-14  9:51 UTC (permalink / raw)
  To: Anup Patel, Palmer Dabbelt, Paul Walmsley, Thomas Gleixner,
	Marc Zyngier, Rob Herring, Krzysztof Kozlowski
  Cc: Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree

On 11/11/2022 05:42, Anup Patel wrote:
> We add DT bindings document for RISC-V advanced platform level interrupt
> controller (APLIC) defined by the RISC-V advanced interrupt architecture
> (AIA) specification.
> 
> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> ---
>  .../interrupt-controller/riscv,aplic.yaml     | 136 ++++++++++++++++++
>  1 file changed, 136 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> 
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> new file mode 100644
> index 000000000000..0aa48571f3bc
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> @@ -0,0 +1,136 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/interrupt-controller/riscv,aplic.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: RISC-V Advancded Platform Level Interrupt Controller (APLIC)
> +
> +maintainers:
> +  - Anup Patel <anup@brainfault.org>
> +
> +description:
> +  The RISC-V advanced interrupt architecture (AIA) defines advanced platform
> +  level interrupt controller (APLIC) for handling wired interrupts in a
> +  RISC-V platform. The RISC-V AIA specification can be found at
> +  https://github.com/riscv/riscv-aia.
> +
> +  The RISC-V APLIC is implemented as hierarchical APLIC domains where all
> +  interrupt sources connect to the root domain which can further delegate
> +  interrupts to child domains. We have one device tree node for each APLIC
> +  domain.
> +
> +allOf:
> +  - $ref: /schemas/interrupt-controller.yaml#
> +
> +properties:
> +  compatible:
> +    items:
> +      - enum:
> +          - vendor,chip-aplic
> +      - const: riscv,aplic
> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupt-controller: true
> +
> +  "#interrupt-cells":
> +    const: 2
> +
> +  interrupts-extended:
> +    minItems: 1
> +    maxItems: 16384
> +    description:
> +      The presence of this property implies that given APLIC domain directly
> +      injects external interrupts to a set of RISC-V HARTS (or CPUs). Each
> +      node pointed to should be a riscv,cpu-intc node, which has a riscv node
> +      (i.e. RISC-V HART) as parent.
> +
> +  msi-parent:
> +    description:
> +      The presence of this property implies that given APLIC domain forwards

Drop "The presence of this property" and make it a proper sentence
describing hardware.

> +      wired interrupts as MSIs to a AIA incoming message signaled interrupt
> +      controller (IMSIC). This property should be considered only when the
> +      interrupts-extended property is absent.
> +
> +  riscv,num-sources:
> +    $ref: "/schemas/types.yaml#/definitions/uint32"

Drop quotes.

> +    minimum: 1
> +    maximum: 1023
> +    description:
> +      Specifies how many wired interrupts are supported by this APLIC domain.
> +
> +  riscv,children:
> +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> +    minItems: 1
> +    maxItems: 1024
> +    description:
> +      This property represents a list of child APLIC domains for the given
> +      APLIC domain. Each child APLIC domain is assigned child index in
> +      increasing order with the first child APLIC domain assigned child
> +      index 0. The APLIC domain child index is used by firmware to delegate
> +      interrupts from the given APLIC domain to a particular child APLIC
> +      domain.
> +
> +  riscv,delegate:
> +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> +    minItems: 1
> +    maxItems: 1024
> +    description:
> +      This property represents a interrupt delegation list where each entry

Drop "This property represents".

> +      is a triple consisting of child APLIC domain phandle, first interrupt
> +      number, and last interrupt number. The firmware will configure interrupt
> +      delegation registers based on interrupt delegation list.
> +
> +additionalProperties: false

Same comments as in previous patch,

> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupt-controller
> +  - "#interrupt-cells"
> +  - riscv,num-sources
> +
> +examples:
> +  - |
> +    // Example 1 (APIC domain directly injecting interrupt to HARTs):
> +
> +    aplic0: interrupt-controller@c000000 {
> +      compatible = "vendor,chip-aplic", "riscv,aplic";
> +      interrupts-extended = <&cpu1_intc 11>,
> +                            <&cpu2_intc 11>,
> +                            <&cpu3_intc 11>,
> +                            <&cpu4_intc 11>;
> +      reg = <0xc000000 0x4080>;
> +      interrupt-controller;
> +      #interrupt-cells = <2>;
> +      riscv,num-sources = <63>;
> +      riscv,children = <&aplic1>;
> +      riscv,delegate = <&aplic1 1 63>;
> +    };
> +
> +    aplic1: interrupt-controller@d000000 {
> +      compatible = "vendor,chip-aplic", "riscv,aplic";
> +      interrupts-extended = <&cpu1_intc 9>,
> +                            <&cpu2_intc 9>,
> +                            <&cpu3_intc 9>,
> +                            <&cpu4_intc 9>;
> +      reg = <0xd000000 0x4080>;
> +      interrupt-controller;
> +      #interrupt-cells = <2>;
> +      riscv,num-sources = <63>;
> +    };
> +
> +  - |
> +    // Example 2 (APIC domain forwarding interrupts as MSIs):
> +
> +    interrupt-controller@d000000 {
> +      compatible = "vendor,chip-aplic", "riscv,aplic";
> +      msi-parent = <&imsics>;
> +      reg = <0xd000000 0x4000>;
> +      interrupt-controller;
> +      #interrupt-cells = <2>;
> +      riscv,num-sources = <63>;

It's almost the same as previous... don't add unnecessary examples
(difference in one property usually does not mean you need new example).

> +    };
> +...

Best regards,
Krzysztof


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
  2022-11-14  9:49     ` Krzysztof Kozlowski
@ 2022-11-14 12:06       ` Anup Patel
  -1 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-14 12:06 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

On Mon, Nov 14, 2022 at 3:19 PM Krzysztof Kozlowski
<krzysztof.kozlowski@linaro.org> wrote:
>
> On 11/11/2022 05:42, Anup Patel wrote:
> > We add DT bindings document for RISC-V incoming MSI controller (IMSIC)
> > defined by the RISC-V advanced interrupt architecture (AIA) specification.
> >
> > Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> > ---
> >  .../interrupt-controller/riscv,imsic.yaml     | 174 ++++++++++++++++++
> >  1 file changed, 174 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > new file mode 100644
> > index 000000000000..05106eb1955e
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > @@ -0,0 +1,174 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/interrupt-controller/riscv,imsic.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: RISC-V Incoming MSI Controller (IMSIC)
> > +
> > +maintainers:
> > +  - Anup Patel <anup@brainfault.org>
> > +
> > +description:
> > +  The RISC-V advanced interrupt architecture (AIA) defines a per-CPU incoming
> > +  MSI controller (IMSIC) for handling MSIs in a RISC-V platform. The RISC-V
> > +  AIA specification can be found at https://github.com/riscv/riscv-aia.
> > +
> > +  The IMSIC is a per-CPU (or per-HART) device with separate interrupt file
> > +  for each privilege level (machine or supervisor). The configuration of
> > +  a IMSIC interrupt file is done using AIA CSRs and it also has a 4KB MMIO
> > +  space to receive MSIs from devices. Each IMSIC interrupt file supports a
> > +  fixed number of interrupt identities (to distinguish MSIs from devices)
> > +  which is same for given privilege level across CPUs (or HARTs).
> > +
> > +  The arrangement of IMSIC interrupt files in MMIO space of a RISC-V platform
> > +  follows a particular scheme defined by the RISC-V AIA specification. A IMSIC
> > +  group is a set of IMSIC interrupt files co-located in MMIO space and we can
> > +  have multiple IMSIC groups (i.e. clusters, sockets, chiplets, etc) in a
> > +  RISC-V platform. The MSI target address of a IMSIC interrupt file at given
> > +  privilege level (machine or supervisor) encodes group index, HART index,
> > +  and guest index (shown below).
> > +
> > +  XLEN-1           >=24                                 12    0
> > +  |                  |                                  |     |
> > +  -------------------------------------------------------------
> > +  |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
> > +  -------------------------------------------------------------
> > +
> > +  The device tree of a RISC-V platform will have one IMSIC device tree node
> > +  for each privilege level (machine or supervisor) which collectively describe
> > +  IMSIC interrupt files at that privilege level across CPUs (or HARTs).
> > +
> > +allOf:
> > +  - $ref: /schemas/interrupt-controller.yaml#
> > +
> > +properties:
> > +  compatible:
> > +    items:
> > +      - enum:
> > +          - vendor,chip-imsics
>
> There is no such vendor... As Conor pointed out, this does not look
> correct. Compatibles must be real and specific.

Previously, Rob had suggest to:
1) Mandate two compatible strings: one for implementation and
    and second for specification
2) Since this is new specification with QEMU being the only
    implementation, we add "vendor,chip-imsics" as dummy
    implementation specific string for DT schema checkers
    to pass the examples. Once we have an actual implementation,
   we will replace this dummy string.

Refer, https://www.spinics.net/lists/devicetree/msg442720.html

>
> > +      - const: riscv,imsics
> > +
> > +  reg:
> > +    minItems: 1
> > +    maxItems: 128
>
> Is there a DTS with 128 reg items?

Not at the moment since this is a new specification.

The value "128" is because maximum number of
IMSIC groups on an system with both IMSIC and
APLIC is 128 where each IMSIC group has a
separate base address. This is not a hard limit so
I am willing to drop it as well.

>
> > +    description:
> > +      Base address of each IMSIC group.
> > +
> > +  interrupt-controller: true
> > +
> > +  "#interrupt-cells":
> > +    const: 0
> > +
> > +  msi-controller: true
>
> You want then msi-controller.yaml schema and you can drop properties
> described there.

Okay, I will include msi-controller.yaml in the next revision.

>
> > +
> > +  interrupts-extended:
> > +    minItems: 1
> > +    maxItems: 32768
>
> I just wonder if you are not putting some random stuff here... just like
> this "vendor" company.
>
> 32768 inputs it is quite a big chip. Are you sure you have so many pins
> or internal connections?

The interrupts-extended property describes the association of IMSIC
interrupt files with the HARTs. If there are N HARTs then we will have
N entries in the interrupts-extended (just like the existing PLIC DT bindings).

For example, if the first entry points to HART1 and the second entry points
to HART0 then the first interrupt file is associated with HART1 and the
second interrupt file is associated with HART0.

Currently, the "maxItems" limit reflects the max IMSICs which an APLIC
domain can target on a system with both IMSIC and APLIC.

Actually, there is a typo here. The "maxItems" should be 16384 as-per
the frozen AIA specification. I will update "maxItems" accordingly in
next patch revision.

>
> > +    description:
> > +      This property represents the set of CPUs (or HARTs) for which given
> > +      device tree node describes the IMSIC interrupt files. Each node pointed
> > +      to should be a riscv,cpu-intc node, which has a riscv node (i.e. RISC-V
> > +      HART) as parent.
> > +
> > +  riscv,num-ids:
> > +    $ref: /schemas/types.yaml#/definitions/uint32
> > +    minimum: 63
> > +    maximum: 2047
> > +    description:
> > +      Specifies how many interrupt identities are supported by IMSIC interrupt
> > +      file.
> > +
> > +  riscv,num-guest-ids:
> > +    $ref: /schemas/types.yaml#/definitions/uint32
> > +    minimum: 63
> > +    maximum: 2047
> > +    description:
> > +      Specifies how many interrupt identities are supported by IMSIC guest
> > +      interrupt file. When not specified the number of interrupt identities
> > +      supported by IMSIC guest file is assumed to be same as specified by
> > +      the riscv,num-ids property.
> > +
> > +  riscv,slow-ipi:
> > +    type: boolean
> > +    description:
> > +      The presence of this property implies that software interrupts (i.e.
> > +      IPIs) using IMSIC software injected MSIs is slower compared to other
> > +      software interrupt mechanisms (such as SBI IPI) on the underlying
> > +      RISC-V platform.
>
> Is this a property of software or hardware?

This is a property of hardware (or implementation) because IPIs
in IMSIC are software injected MSIs so if IMSIC is trap-n-emulated
by a hypervisor then all writes to MSI register will trap to hypervisor
in which case IPI injection via IMSIC is slow.

The presence of "riscv,slow-ipi" DT property provides a hint to
driver that using IPIs through IMSIC is slow on this platform so
if there are other IPI mechanisms (such as SBI IPI calls) then
OS should prefer those mechanisms.

>
> > +
> > +  riscv,guest-index-bits:
> > +    minimum: 0
> > +    maximum: 7
> > +    description:
> > +      Specifies number of guest index bits in the MSI target address. When
> > +      not specified it is assumed to be 0.
> > +
> > +  riscv,hart-index-bits:
> > +    minimum: 0
> > +    maximum: 15
> > +    description:
> > +      Specifies number of HART index bits in the MSI target address. When
> > +      not specified it is estimated based on the interrupts-extended property.
> > +
> > +  riscv,group-index-bits:
> > +    minimum: 0
> > +    maximum: 7
> > +    description:
> > +      Specifies number of group index bits in the MSI target address. When
> > +      not specified it is assumed to be 0.
>
> Then default: 0.

Okay, I will update.

>
> > +
> > +  riscv,group-index-shift:
> > +    $ref: /schemas/types.yaml#/definitions/uint32
> > +    minimum: 24
> > +    maximum: 55
> > +    description:
> > +      Specifies the least significant bit of the group index bits in the
>
> Please drop everywhere "Specifies the" and instead just describe the
> hardware.

Okay, I will update.

>
> > +      MSI target address. When not specified it is assumed to be 24.
> > +
> > +additionalProperties: false
>
> unevaluatedProperties: false and drop all properties already described
> by other schemas.

Okay, I will update.

>
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - interrupt-controller
> > +  - msi-controller
> > +  - interrupts-extended
> > +  - riscv,num-ids
> > +
> > +examples:
> > +  - |
> > +    // Example 1 (Machine-level IMSIC files with just one group):
> > +
> > +    imsic_mlevel: interrupt-controller@24000000 {
> > +      compatible = "vendor,chip-imsics", "riscv,imsics";
> > +      interrupts-extended = <&cpu1_intc 11>,
> > +                            <&cpu2_intc 11>,
> > +                            <&cpu3_intc 11>,
> > +                            <&cpu4_intc 11>;
> > +      reg = <0x28000000 0x4000>;
> > +      interrupt-controller;
> > +      #interrupt-cells = <0>;
> > +      msi-controller;
> > +      riscv,num-ids = <127>;
> > +    };
> > +
> > +  - |
> > +    // Example 2 (Supervisor-level IMSIC files with two groups):
> > +
> > +    imsic_slevel: interrupt-controller@28000000 {
> > +      compatible = "vendor,chip-imsics", "riscv,imsics";
>
> Please run scripts/checkpatch.pl and fix reported warnings.

I did not see any warnings with ./scripts/checkpatch.pl.

Is there any parameter of checkpatch.pl which I should try ?

Best Regards,
Anup

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
@ 2022-11-14 12:06       ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-14 12:06 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

On Mon, Nov 14, 2022 at 3:19 PM Krzysztof Kozlowski
<krzysztof.kozlowski@linaro.org> wrote:
>
> On 11/11/2022 05:42, Anup Patel wrote:
> > We add DT bindings document for RISC-V incoming MSI controller (IMSIC)
> > defined by the RISC-V advanced interrupt architecture (AIA) specification.
> >
> > Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> > ---
> >  .../interrupt-controller/riscv,imsic.yaml     | 174 ++++++++++++++++++
> >  1 file changed, 174 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > new file mode 100644
> > index 000000000000..05106eb1955e
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > @@ -0,0 +1,174 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/interrupt-controller/riscv,imsic.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: RISC-V Incoming MSI Controller (IMSIC)
> > +
> > +maintainers:
> > +  - Anup Patel <anup@brainfault.org>
> > +
> > +description:
> > +  The RISC-V advanced interrupt architecture (AIA) defines a per-CPU incoming
> > +  MSI controller (IMSIC) for handling MSIs in a RISC-V platform. The RISC-V
> > +  AIA specification can be found at https://github.com/riscv/riscv-aia.
> > +
> > +  The IMSIC is a per-CPU (or per-HART) device with separate interrupt file
> > +  for each privilege level (machine or supervisor). The configuration of
> > +  a IMSIC interrupt file is done using AIA CSRs and it also has a 4KB MMIO
> > +  space to receive MSIs from devices. Each IMSIC interrupt file supports a
> > +  fixed number of interrupt identities (to distinguish MSIs from devices)
> > +  which is same for given privilege level across CPUs (or HARTs).
> > +
> > +  The arrangement of IMSIC interrupt files in MMIO space of a RISC-V platform
> > +  follows a particular scheme defined by the RISC-V AIA specification. A IMSIC
> > +  group is a set of IMSIC interrupt files co-located in MMIO space and we can
> > +  have multiple IMSIC groups (i.e. clusters, sockets, chiplets, etc) in a
> > +  RISC-V platform. The MSI target address of a IMSIC interrupt file at given
> > +  privilege level (machine or supervisor) encodes group index, HART index,
> > +  and guest index (shown below).
> > +
> > +  XLEN-1           >=24                                 12    0
> > +  |                  |                                  |     |
> > +  -------------------------------------------------------------
> > +  |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
> > +  -------------------------------------------------------------
> > +
> > +  The device tree of a RISC-V platform will have one IMSIC device tree node
> > +  for each privilege level (machine or supervisor) which collectively describe
> > +  IMSIC interrupt files at that privilege level across CPUs (or HARTs).
> > +
> > +allOf:
> > +  - $ref: /schemas/interrupt-controller.yaml#
> > +
> > +properties:
> > +  compatible:
> > +    items:
> > +      - enum:
> > +          - vendor,chip-imsics
>
> There is no such vendor... As Conor pointed out, this does not look
> correct. Compatibles must be real and specific.

Previously, Rob had suggest to:
1) Mandate two compatible strings: one for implementation and
    and second for specification
2) Since this is new specification with QEMU being the only
    implementation, we add "vendor,chip-imsics" as dummy
    implementation specific string for DT schema checkers
    to pass the examples. Once we have an actual implementation,
   we will replace this dummy string.

Refer, https://www.spinics.net/lists/devicetree/msg442720.html

>
> > +      - const: riscv,imsics
> > +
> > +  reg:
> > +    minItems: 1
> > +    maxItems: 128
>
> Is there a DTS with 128 reg items?

Not at the moment since this is a new specification.

The value "128" is because maximum number of
IMSIC groups on an system with both IMSIC and
APLIC is 128 where each IMSIC group has a
separate base address. This is not a hard limit so
I am willing to drop it as well.

>
> > +    description:
> > +      Base address of each IMSIC group.
> > +
> > +  interrupt-controller: true
> > +
> > +  "#interrupt-cells":
> > +    const: 0
> > +
> > +  msi-controller: true
>
> You want then msi-controller.yaml schema and you can drop properties
> described there.

Okay, I will include msi-controller.yaml in the next revision.

>
> > +
> > +  interrupts-extended:
> > +    minItems: 1
> > +    maxItems: 32768
>
> I just wonder if you are not putting some random stuff here... just like
> this "vendor" company.
>
> 32768 inputs it is quite a big chip. Are you sure you have so many pins
> or internal connections?

The interrupts-extended property describes the association of IMSIC
interrupt files with the HARTs. If there are N HARTs then we will have
N entries in the interrupts-extended (just like the existing PLIC DT bindings).

For example, if the first entry points to HART1 and the second entry points
to HART0 then the first interrupt file is associated with HART1 and the
second interrupt file is associated with HART0.

Currently, the "maxItems" limit reflects the max IMSICs which an APLIC
domain can target on a system with both IMSIC and APLIC.

Actually, there is a typo here. The "maxItems" should be 16384 as-per
the frozen AIA specification. I will update "maxItems" accordingly in
next patch revision.

>
> > +    description:
> > +      This property represents the set of CPUs (or HARTs) for which given
> > +      device tree node describes the IMSIC interrupt files. Each node pointed
> > +      to should be a riscv,cpu-intc node, which has a riscv node (i.e. RISC-V
> > +      HART) as parent.
> > +
> > +  riscv,num-ids:
> > +    $ref: /schemas/types.yaml#/definitions/uint32
> > +    minimum: 63
> > +    maximum: 2047
> > +    description:
> > +      Specifies how many interrupt identities are supported by IMSIC interrupt
> > +      file.
> > +
> > +  riscv,num-guest-ids:
> > +    $ref: /schemas/types.yaml#/definitions/uint32
> > +    minimum: 63
> > +    maximum: 2047
> > +    description:
> > +      Specifies how many interrupt identities are supported by IMSIC guest
> > +      interrupt file. When not specified the number of interrupt identities
> > +      supported by IMSIC guest file is assumed to be same as specified by
> > +      the riscv,num-ids property.
> > +
> > +  riscv,slow-ipi:
> > +    type: boolean
> > +    description:
> > +      The presence of this property implies that software interrupts (i.e.
> > +      IPIs) using IMSIC software injected MSIs is slower compared to other
> > +      software interrupt mechanisms (such as SBI IPI) on the underlying
> > +      RISC-V platform.
>
> Is this a property of software or hardware?

This is a property of hardware (or implementation) because IPIs
in IMSIC are software injected MSIs so if IMSIC is trap-n-emulated
by a hypervisor then all writes to MSI register will trap to hypervisor
in which case IPI injection via IMSIC is slow.

The presence of "riscv,slow-ipi" DT property provides a hint to
driver that using IPIs through IMSIC is slow on this platform so
if there are other IPI mechanisms (such as SBI IPI calls) then
OS should prefer those mechanisms.

>
> > +
> > +  riscv,guest-index-bits:
> > +    minimum: 0
> > +    maximum: 7
> > +    description:
> > +      Specifies number of guest index bits in the MSI target address. When
> > +      not specified it is assumed to be 0.
> > +
> > +  riscv,hart-index-bits:
> > +    minimum: 0
> > +    maximum: 15
> > +    description:
> > +      Specifies number of HART index bits in the MSI target address. When
> > +      not specified it is estimated based on the interrupts-extended property.
> > +
> > +  riscv,group-index-bits:
> > +    minimum: 0
> > +    maximum: 7
> > +    description:
> > +      Specifies number of group index bits in the MSI target address. When
> > +      not specified it is assumed to be 0.
>
> Then default: 0.

Okay, I will update.

>
> > +
> > +  riscv,group-index-shift:
> > +    $ref: /schemas/types.yaml#/definitions/uint32
> > +    minimum: 24
> > +    maximum: 55
> > +    description:
> > +      Specifies the least significant bit of the group index bits in the
>
> Please drop everywhere "Specifies the" and instead just describe the
> hardware.

Okay, I will update.

>
> > +      MSI target address. When not specified it is assumed to be 24.
> > +
> > +additionalProperties: false
>
> unevaluatedProperties: false and drop all properties already described
> by other schemas.

Okay, I will update.

>
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - interrupt-controller
> > +  - msi-controller
> > +  - interrupts-extended
> > +  - riscv,num-ids
> > +
> > +examples:
> > +  - |
> > +    // Example 1 (Machine-level IMSIC files with just one group):
> > +
> > +    imsic_mlevel: interrupt-controller@24000000 {
> > +      compatible = "vendor,chip-imsics", "riscv,imsics";
> > +      interrupts-extended = <&cpu1_intc 11>,
> > +                            <&cpu2_intc 11>,
> > +                            <&cpu3_intc 11>,
> > +                            <&cpu4_intc 11>;
> > +      reg = <0x28000000 0x4000>;
> > +      interrupt-controller;
> > +      #interrupt-cells = <0>;
> > +      msi-controller;
> > +      riscv,num-ids = <127>;
> > +    };
> > +
> > +  - |
> > +    // Example 2 (Supervisor-level IMSIC files with two groups):
> > +
> > +    imsic_slevel: interrupt-controller@28000000 {
> > +      compatible = "vendor,chip-imsics", "riscv,imsics";
>
> Please run scripts/checkpatch.pl and fix reported warnings.

I did not see any warnings with ./scripts/checkpatch.pl.

Is there any parameter of checkpatch.pl which I should try ?

Best Regards,
Anup

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 6/9] dt-bindings: Add RISC-V advanced PLIC bindings
  2022-11-14  9:51     ` Krzysztof Kozlowski
@ 2022-11-14 12:11       ` Anup Patel
  -1 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-14 12:11 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

On Mon, Nov 14, 2022 at 3:21 PM Krzysztof Kozlowski
<krzysztof.kozlowski@linaro.org> wrote:
>
> On 11/11/2022 05:42, Anup Patel wrote:
> > We add DT bindings document for RISC-V advanced platform level interrupt
> > controller (APLIC) defined by the RISC-V advanced interrupt architecture
> > (AIA) specification.
> >
> > Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> > ---
> >  .../interrupt-controller/riscv,aplic.yaml     | 136 ++++++++++++++++++
> >  1 file changed, 136 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> > new file mode 100644
> > index 000000000000..0aa48571f3bc
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> > @@ -0,0 +1,136 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/interrupt-controller/riscv,aplic.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: RISC-V Advancded Platform Level Interrupt Controller (APLIC)
> > +
> > +maintainers:
> > +  - Anup Patel <anup@brainfault.org>
> > +
> > +description:
> > +  The RISC-V advanced interrupt architecture (AIA) defines advanced platform
> > +  level interrupt controller (APLIC) for handling wired interrupts in a
> > +  RISC-V platform. The RISC-V AIA specification can be found at
> > +  https://github.com/riscv/riscv-aia.
> > +
> > +  The RISC-V APLIC is implemented as hierarchical APLIC domains where all
> > +  interrupt sources connect to the root domain which can further delegate
> > +  interrupts to child domains. We have one device tree node for each APLIC
> > +  domain.
> > +
> > +allOf:
> > +  - $ref: /schemas/interrupt-controller.yaml#
> > +
> > +properties:
> > +  compatible:
> > +    items:
> > +      - enum:
> > +          - vendor,chip-aplic
> > +      - const: riscv,aplic
> > +
> > +  reg:
> > +    maxItems: 1
> > +
> > +  interrupt-controller: true
> > +
> > +  "#interrupt-cells":
> > +    const: 2
> > +
> > +  interrupts-extended:
> > +    minItems: 1
> > +    maxItems: 16384
> > +    description:
> > +      The presence of this property implies that given APLIC domain directly
> > +      injects external interrupts to a set of RISC-V HARTS (or CPUs). Each
> > +      node pointed to should be a riscv,cpu-intc node, which has a riscv node
> > +      (i.e. RISC-V HART) as parent.
> > +
> > +  msi-parent:
> > +    description:
> > +      The presence of this property implies that given APLIC domain forwards
>
> Drop "The presence of this property" and make it a proper sentence
> describing hardware.

Okay, I will update.

>
> > +      wired interrupts as MSIs to a AIA incoming message signaled interrupt
> > +      controller (IMSIC). This property should be considered only when the
> > +      interrupts-extended property is absent.
> > +
> > +  riscv,num-sources:
> > +    $ref: "/schemas/types.yaml#/definitions/uint32"
>
> Drop quotes.

Okay, I will update.

>
> > +    minimum: 1
> > +    maximum: 1023
> > +    description:
> > +      Specifies how many wired interrupts are supported by this APLIC domain.
> > +
> > +  riscv,children:
> > +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> > +    minItems: 1
> > +    maxItems: 1024
> > +    description:
> > +      This property represents a list of child APLIC domains for the given
> > +      APLIC domain. Each child APLIC domain is assigned child index in
> > +      increasing order with the first child APLIC domain assigned child
> > +      index 0. The APLIC domain child index is used by firmware to delegate
> > +      interrupts from the given APLIC domain to a particular child APLIC
> > +      domain.
> > +
> > +  riscv,delegate:
> > +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> > +    minItems: 1
> > +    maxItems: 1024
> > +    description:
> > +      This property represents a interrupt delegation list where each entry
>
> Drop "This property represents".

Okay, I will update.

>
> > +      is a triple consisting of child APLIC domain phandle, first interrupt
> > +      number, and last interrupt number. The firmware will configure interrupt
> > +      delegation registers based on interrupt delegation list.
> > +
> > +additionalProperties: false
>
> Same comments as in previous patch,

Okay, I will update.

>
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - interrupt-controller
> > +  - "#interrupt-cells"
> > +  - riscv,num-sources
> > +
> > +examples:
> > +  - |
> > +    // Example 1 (APIC domain directly injecting interrupt to HARTs):
> > +
> > +    aplic0: interrupt-controller@c000000 {
> > +      compatible = "vendor,chip-aplic", "riscv,aplic";
> > +      interrupts-extended = <&cpu1_intc 11>,
> > +                            <&cpu2_intc 11>,
> > +                            <&cpu3_intc 11>,
> > +                            <&cpu4_intc 11>;
> > +      reg = <0xc000000 0x4080>;
> > +      interrupt-controller;
> > +      #interrupt-cells = <2>;
> > +      riscv,num-sources = <63>;
> > +      riscv,children = <&aplic1>;
> > +      riscv,delegate = <&aplic1 1 63>;
> > +    };
> > +
> > +    aplic1: interrupt-controller@d000000 {
> > +      compatible = "vendor,chip-aplic", "riscv,aplic";
> > +      interrupts-extended = <&cpu1_intc 9>,
> > +                            <&cpu2_intc 9>,
> > +                            <&cpu3_intc 9>,
> > +                            <&cpu4_intc 9>;
> > +      reg = <0xd000000 0x4080>;
> > +      interrupt-controller;
> > +      #interrupt-cells = <2>;
> > +      riscv,num-sources = <63>;
> > +    };
> > +
> > +  - |
> > +    // Example 2 (APIC domain forwarding interrupts as MSIs):
> > +
> > +    interrupt-controller@d000000 {
> > +      compatible = "vendor,chip-aplic", "riscv,aplic";
> > +      msi-parent = <&imsics>;
> > +      reg = <0xd000000 0x4000>;
> > +      interrupt-controller;
> > +      #interrupt-cells = <2>;
> > +      riscv,num-sources = <63>;
>
> It's almost the same as previous... don't add unnecessary examples
> (difference in one property usually does not mean you need new example).

The second example shows the DT node of an APLIC in MSI-mode.
Most noteworthy part of this node is presence of "msi-parent" DT
property instead of "interrupts-extended" DT property to describe
an APLIC in MSI mode.

>
> > +    };
> > +...
>

Best Regards,
Anup

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

* Re: [PATCH 6/9] dt-bindings: Add RISC-V advanced PLIC bindings
@ 2022-11-14 12:11       ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-14 12:11 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

On Mon, Nov 14, 2022 at 3:21 PM Krzysztof Kozlowski
<krzysztof.kozlowski@linaro.org> wrote:
>
> On 11/11/2022 05:42, Anup Patel wrote:
> > We add DT bindings document for RISC-V advanced platform level interrupt
> > controller (APLIC) defined by the RISC-V advanced interrupt architecture
> > (AIA) specification.
> >
> > Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> > ---
> >  .../interrupt-controller/riscv,aplic.yaml     | 136 ++++++++++++++++++
> >  1 file changed, 136 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> > new file mode 100644
> > index 000000000000..0aa48571f3bc
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> > @@ -0,0 +1,136 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/interrupt-controller/riscv,aplic.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: RISC-V Advancded Platform Level Interrupt Controller (APLIC)
> > +
> > +maintainers:
> > +  - Anup Patel <anup@brainfault.org>
> > +
> > +description:
> > +  The RISC-V advanced interrupt architecture (AIA) defines advanced platform
> > +  level interrupt controller (APLIC) for handling wired interrupts in a
> > +  RISC-V platform. The RISC-V AIA specification can be found at
> > +  https://github.com/riscv/riscv-aia.
> > +
> > +  The RISC-V APLIC is implemented as hierarchical APLIC domains where all
> > +  interrupt sources connect to the root domain which can further delegate
> > +  interrupts to child domains. We have one device tree node for each APLIC
> > +  domain.
> > +
> > +allOf:
> > +  - $ref: /schemas/interrupt-controller.yaml#
> > +
> > +properties:
> > +  compatible:
> > +    items:
> > +      - enum:
> > +          - vendor,chip-aplic
> > +      - const: riscv,aplic
> > +
> > +  reg:
> > +    maxItems: 1
> > +
> > +  interrupt-controller: true
> > +
> > +  "#interrupt-cells":
> > +    const: 2
> > +
> > +  interrupts-extended:
> > +    minItems: 1
> > +    maxItems: 16384
> > +    description:
> > +      The presence of this property implies that given APLIC domain directly
> > +      injects external interrupts to a set of RISC-V HARTS (or CPUs). Each
> > +      node pointed to should be a riscv,cpu-intc node, which has a riscv node
> > +      (i.e. RISC-V HART) as parent.
> > +
> > +  msi-parent:
> > +    description:
> > +      The presence of this property implies that given APLIC domain forwards
>
> Drop "The presence of this property" and make it a proper sentence
> describing hardware.

Okay, I will update.

>
> > +      wired interrupts as MSIs to a AIA incoming message signaled interrupt
> > +      controller (IMSIC). This property should be considered only when the
> > +      interrupts-extended property is absent.
> > +
> > +  riscv,num-sources:
> > +    $ref: "/schemas/types.yaml#/definitions/uint32"
>
> Drop quotes.

Okay, I will update.

>
> > +    minimum: 1
> > +    maximum: 1023
> > +    description:
> > +      Specifies how many wired interrupts are supported by this APLIC domain.
> > +
> > +  riscv,children:
> > +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> > +    minItems: 1
> > +    maxItems: 1024
> > +    description:
> > +      This property represents a list of child APLIC domains for the given
> > +      APLIC domain. Each child APLIC domain is assigned child index in
> > +      increasing order with the first child APLIC domain assigned child
> > +      index 0. The APLIC domain child index is used by firmware to delegate
> > +      interrupts from the given APLIC domain to a particular child APLIC
> > +      domain.
> > +
> > +  riscv,delegate:
> > +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> > +    minItems: 1
> > +    maxItems: 1024
> > +    description:
> > +      This property represents a interrupt delegation list where each entry
>
> Drop "This property represents".

Okay, I will update.

>
> > +      is a triple consisting of child APLIC domain phandle, first interrupt
> > +      number, and last interrupt number. The firmware will configure interrupt
> > +      delegation registers based on interrupt delegation list.
> > +
> > +additionalProperties: false
>
> Same comments as in previous patch,

Okay, I will update.

>
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - interrupt-controller
> > +  - "#interrupt-cells"
> > +  - riscv,num-sources
> > +
> > +examples:
> > +  - |
> > +    // Example 1 (APIC domain directly injecting interrupt to HARTs):
> > +
> > +    aplic0: interrupt-controller@c000000 {
> > +      compatible = "vendor,chip-aplic", "riscv,aplic";
> > +      interrupts-extended = <&cpu1_intc 11>,
> > +                            <&cpu2_intc 11>,
> > +                            <&cpu3_intc 11>,
> > +                            <&cpu4_intc 11>;
> > +      reg = <0xc000000 0x4080>;
> > +      interrupt-controller;
> > +      #interrupt-cells = <2>;
> > +      riscv,num-sources = <63>;
> > +      riscv,children = <&aplic1>;
> > +      riscv,delegate = <&aplic1 1 63>;
> > +    };
> > +
> > +    aplic1: interrupt-controller@d000000 {
> > +      compatible = "vendor,chip-aplic", "riscv,aplic";
> > +      interrupts-extended = <&cpu1_intc 9>,
> > +                            <&cpu2_intc 9>,
> > +                            <&cpu3_intc 9>,
> > +                            <&cpu4_intc 9>;
> > +      reg = <0xd000000 0x4080>;
> > +      interrupt-controller;
> > +      #interrupt-cells = <2>;
> > +      riscv,num-sources = <63>;
> > +    };
> > +
> > +  - |
> > +    // Example 2 (APIC domain forwarding interrupts as MSIs):
> > +
> > +    interrupt-controller@d000000 {
> > +      compatible = "vendor,chip-aplic", "riscv,aplic";
> > +      msi-parent = <&imsics>;
> > +      reg = <0xd000000 0x4000>;
> > +      interrupt-controller;
> > +      #interrupt-cells = <2>;
> > +      riscv,num-sources = <63>;
>
> It's almost the same as previous... don't add unnecessary examples
> (difference in one property usually does not mean you need new example).

The second example shows the DT node of an APLIC in MSI-mode.
Most noteworthy part of this node is presence of "msi-parent" DT
property instead of "interrupts-extended" DT property to describe
an APLIC in MSI mode.

>
> > +    };
> > +...
>

Best Regards,
Anup

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
  2022-11-14 12:06       ` Anup Patel
@ 2022-11-14 12:14         ` Conor Dooley
  -1 siblings, 0 replies; 96+ messages in thread
From: Conor Dooley @ 2022-11-14 12:14 UTC (permalink / raw)
  To: Anup Patel
  Cc: Krzysztof Kozlowski, Palmer Dabbelt, Paul Walmsley,
	Thomas Gleixner, Marc Zyngier, Rob Herring, Krzysztof Kozlowski,
	Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree

On Mon, Nov 14, 2022 at 05:36:06PM +0530, Anup Patel wrote:
> On Mon, Nov 14, 2022 at 3:19 PM Krzysztof Kozlowski
> <krzysztof.kozlowski@linaro.org> wrote:
> >
> > On 11/11/2022 05:42, Anup Patel wrote:
> > > We add DT bindings document for RISC-V incoming MSI controller (IMSIC)
> > > defined by the RISC-V advanced interrupt architecture (AIA) specification.
> > >
> > > Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> > > ---
> > >  .../interrupt-controller/riscv,imsic.yaml     | 174 ++++++++++++++++++
> > >  1 file changed, 174 insertions(+)
> > >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > >
> > > diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > > new file mode 100644
> > > index 000000000000..05106eb1955e
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > > @@ -0,0 +1,174 @@
> > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > +%YAML 1.2
> > > +---
> > > +$id: http://devicetree.org/schemas/interrupt-controller/riscv,imsic.yaml#
> > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > +
> > > +title: RISC-V Incoming MSI Controller (IMSIC)
> > > +
> > > +maintainers:
> > > +  - Anup Patel <anup@brainfault.org>
> > > +
> > > +description:
> > > +  The RISC-V advanced interrupt architecture (AIA) defines a per-CPU incoming
> > > +  MSI controller (IMSIC) for handling MSIs in a RISC-V platform. The RISC-V
> > > +  AIA specification can be found at https://github.com/riscv/riscv-aia.
> > > +
> > > +  The IMSIC is a per-CPU (or per-HART) device with separate interrupt file
> > > +  for each privilege level (machine or supervisor). The configuration of
> > > +  a IMSIC interrupt file is done using AIA CSRs and it also has a 4KB MMIO
> > > +  space to receive MSIs from devices. Each IMSIC interrupt file supports a
> > > +  fixed number of interrupt identities (to distinguish MSIs from devices)
> > > +  which is same for given privilege level across CPUs (or HARTs).
> > > +
> > > +  The arrangement of IMSIC interrupt files in MMIO space of a RISC-V platform
> > > +  follows a particular scheme defined by the RISC-V AIA specification. A IMSIC
> > > +  group is a set of IMSIC interrupt files co-located in MMIO space and we can
> > > +  have multiple IMSIC groups (i.e. clusters, sockets, chiplets, etc) in a
> > > +  RISC-V platform. The MSI target address of a IMSIC interrupt file at given
> > > +  privilege level (machine or supervisor) encodes group index, HART index,
> > > +  and guest index (shown below).
> > > +
> > > +  XLEN-1           >=24                                 12    0
> > > +  |                  |                                  |     |
> > > +  -------------------------------------------------------------
> > > +  |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
> > > +  -------------------------------------------------------------
> > > +
> > > +  The device tree of a RISC-V platform will have one IMSIC device tree node
> > > +  for each privilege level (machine or supervisor) which collectively describe
> > > +  IMSIC interrupt files at that privilege level across CPUs (or HARTs).
> > > +
> > > +allOf:
> > > +  - $ref: /schemas/interrupt-controller.yaml#
> > > +
> > > +properties:
> > > +  compatible:
> > > +    items:
> > > +      - enum:
> > > +          - vendor,chip-imsics
> >
> > There is no such vendor... As Conor pointed out, this does not look
> > correct. Compatibles must be real and specific.
> 
> Previously, Rob had suggest to:
> 1) Mandate two compatible strings: one for implementation and
>     and second for specification
> 2) Since this is new specification with QEMU being the only
>     implementation, we add "vendor,chip-imsics" as dummy
>     implementation specific string for DT schema checkers
>     to pass the examples. Once we have an actual implementation,
>    we will replace this dummy string.
> 
> Refer, https://www.spinics.net/lists/devicetree/msg442720.html

AFAIU, <vendor> and <chip> are wildcards and do not have the same
meaning as vendor & chip. That's going off of the dt submitting patches
doc though and I don't know if the tooling supports this.


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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
@ 2022-11-14 12:14         ` Conor Dooley
  0 siblings, 0 replies; 96+ messages in thread
From: Conor Dooley @ 2022-11-14 12:14 UTC (permalink / raw)
  To: Anup Patel
  Cc: Krzysztof Kozlowski, Palmer Dabbelt, Paul Walmsley,
	Thomas Gleixner, Marc Zyngier, Rob Herring, Krzysztof Kozlowski,
	Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree

On Mon, Nov 14, 2022 at 05:36:06PM +0530, Anup Patel wrote:
> On Mon, Nov 14, 2022 at 3:19 PM Krzysztof Kozlowski
> <krzysztof.kozlowski@linaro.org> wrote:
> >
> > On 11/11/2022 05:42, Anup Patel wrote:
> > > We add DT bindings document for RISC-V incoming MSI controller (IMSIC)
> > > defined by the RISC-V advanced interrupt architecture (AIA) specification.
> > >
> > > Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> > > ---
> > >  .../interrupt-controller/riscv,imsic.yaml     | 174 ++++++++++++++++++
> > >  1 file changed, 174 insertions(+)
> > >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > >
> > > diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > > new file mode 100644
> > > index 000000000000..05106eb1955e
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > > @@ -0,0 +1,174 @@
> > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > +%YAML 1.2
> > > +---
> > > +$id: http://devicetree.org/schemas/interrupt-controller/riscv,imsic.yaml#
> > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > +
> > > +title: RISC-V Incoming MSI Controller (IMSIC)
> > > +
> > > +maintainers:
> > > +  - Anup Patel <anup@brainfault.org>
> > > +
> > > +description:
> > > +  The RISC-V advanced interrupt architecture (AIA) defines a per-CPU incoming
> > > +  MSI controller (IMSIC) for handling MSIs in a RISC-V platform. The RISC-V
> > > +  AIA specification can be found at https://github.com/riscv/riscv-aia.
> > > +
> > > +  The IMSIC is a per-CPU (or per-HART) device with separate interrupt file
> > > +  for each privilege level (machine or supervisor). The configuration of
> > > +  a IMSIC interrupt file is done using AIA CSRs and it also has a 4KB MMIO
> > > +  space to receive MSIs from devices. Each IMSIC interrupt file supports a
> > > +  fixed number of interrupt identities (to distinguish MSIs from devices)
> > > +  which is same for given privilege level across CPUs (or HARTs).
> > > +
> > > +  The arrangement of IMSIC interrupt files in MMIO space of a RISC-V platform
> > > +  follows a particular scheme defined by the RISC-V AIA specification. A IMSIC
> > > +  group is a set of IMSIC interrupt files co-located in MMIO space and we can
> > > +  have multiple IMSIC groups (i.e. clusters, sockets, chiplets, etc) in a
> > > +  RISC-V platform. The MSI target address of a IMSIC interrupt file at given
> > > +  privilege level (machine or supervisor) encodes group index, HART index,
> > > +  and guest index (shown below).
> > > +
> > > +  XLEN-1           >=24                                 12    0
> > > +  |                  |                                  |     |
> > > +  -------------------------------------------------------------
> > > +  |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
> > > +  -------------------------------------------------------------
> > > +
> > > +  The device tree of a RISC-V platform will have one IMSIC device tree node
> > > +  for each privilege level (machine or supervisor) which collectively describe
> > > +  IMSIC interrupt files at that privilege level across CPUs (or HARTs).
> > > +
> > > +allOf:
> > > +  - $ref: /schemas/interrupt-controller.yaml#
> > > +
> > > +properties:
> > > +  compatible:
> > > +    items:
> > > +      - enum:
> > > +          - vendor,chip-imsics
> >
> > There is no such vendor... As Conor pointed out, this does not look
> > correct. Compatibles must be real and specific.
> 
> Previously, Rob had suggest to:
> 1) Mandate two compatible strings: one for implementation and
>     and second for specification
> 2) Since this is new specification with QEMU being the only
>     implementation, we add "vendor,chip-imsics" as dummy
>     implementation specific string for DT schema checkers
>     to pass the examples. Once we have an actual implementation,
>    we will replace this dummy string.
> 
> Refer, https://www.spinics.net/lists/devicetree/msg442720.html

AFAIU, <vendor> and <chip> are wildcards and do not have the same
meaning as vendor & chip. That's going off of the dt submitting patches
doc though and I don't know if the tooling supports this.


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
  2022-11-14 12:06       ` Anup Patel
@ 2022-11-14 12:21         ` Krzysztof Kozlowski
  -1 siblings, 0 replies; 96+ messages in thread
From: Krzysztof Kozlowski @ 2022-11-14 12:21 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

On 14/11/2022 13:06, Anup Patel wrote:
> On Mon, Nov 14, 2022 at 3:19 PM Krzysztof Kozlowski
> <krzysztof.kozlowski@linaro.org> wrote:
>>
>> On 11/11/2022 05:42, Anup Patel wrote:
>>> We add DT bindings document for RISC-V incoming MSI controller (IMSIC)
>>> defined by the RISC-V advanced interrupt architecture (AIA) specification.
>>>
>>> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
>>> ---
>>>  .../interrupt-controller/riscv,imsic.yaml     | 174 ++++++++++++++++++
>>>  1 file changed, 174 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
>>>
>>> diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
>>> new file mode 100644
>>> index 000000000000..05106eb1955e
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
>>> @@ -0,0 +1,174 @@
>>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>>> +%YAML 1.2
>>> +---
>>> +$id: http://devicetree.org/schemas/interrupt-controller/riscv,imsic.yaml#
>>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>>> +
>>> +title: RISC-V Incoming MSI Controller (IMSIC)
>>> +
>>> +maintainers:
>>> +  - Anup Patel <anup@brainfault.org>
>>> +
>>> +description:
>>> +  The RISC-V advanced interrupt architecture (AIA) defines a per-CPU incoming
>>> +  MSI controller (IMSIC) for handling MSIs in a RISC-V platform. The RISC-V
>>> +  AIA specification can be found at https://github.com/riscv/riscv-aia.
>>> +
>>> +  The IMSIC is a per-CPU (or per-HART) device with separate interrupt file
>>> +  for each privilege level (machine or supervisor). The configuration of
>>> +  a IMSIC interrupt file is done using AIA CSRs and it also has a 4KB MMIO
>>> +  space to receive MSIs from devices. Each IMSIC interrupt file supports a
>>> +  fixed number of interrupt identities (to distinguish MSIs from devices)
>>> +  which is same for given privilege level across CPUs (or HARTs).
>>> +
>>> +  The arrangement of IMSIC interrupt files in MMIO space of a RISC-V platform
>>> +  follows a particular scheme defined by the RISC-V AIA specification. A IMSIC
>>> +  group is a set of IMSIC interrupt files co-located in MMIO space and we can
>>> +  have multiple IMSIC groups (i.e. clusters, sockets, chiplets, etc) in a
>>> +  RISC-V platform. The MSI target address of a IMSIC interrupt file at given
>>> +  privilege level (machine or supervisor) encodes group index, HART index,
>>> +  and guest index (shown below).
>>> +
>>> +  XLEN-1           >=24                                 12    0
>>> +  |                  |                                  |     |
>>> +  -------------------------------------------------------------
>>> +  |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
>>> +  -------------------------------------------------------------
>>> +
>>> +  The device tree of a RISC-V platform will have one IMSIC device tree node
>>> +  for each privilege level (machine or supervisor) which collectively describe
>>> +  IMSIC interrupt files at that privilege level across CPUs (or HARTs).
>>> +
>>> +allOf:
>>> +  - $ref: /schemas/interrupt-controller.yaml#
>>> +
>>> +properties:
>>> +  compatible:
>>> +    items:
>>> +      - enum:
>>> +          - vendor,chip-imsics
>>
>> There is no such vendor... As Conor pointed out, this does not look
>> correct. Compatibles must be real and specific.
> 
> Previously, Rob had suggest to:
> 1) Mandate two compatible strings: one for implementation and
>     and second for specification
> 2) Since this is new specification with QEMU being the only
>     implementation, we add "vendor,chip-imsics" as dummy
>     implementation specific string for DT schema checkers
>     to pass the examples. Once we have an actual implementation,
>    we will replace this dummy string.
> 
> Refer, https://www.spinics.net/lists/devicetree/msg442720.html

And Rob did not propose vendor as vendor and chip-imsics as device. Read
his message again.

> 
>>
>>> +      - const: riscv,imsics
>>> +
>>> +  reg:
>>> +    minItems: 1
>>> +    maxItems: 128
>>
>> Is there a DTS with 128 reg items?
> 
> Not at the moment since this is a new specification.
> 
> The value "128" is because maximum number of
> IMSIC groups on an system with both IMSIC and
> APLIC is 128 where each IMSIC group has a
> separate base address. This is not a hard limit so
> I am willing to drop it as well.

Is "separate base address" really a separate different range or just
spaced by few registers?

> 
>>
>>> +    description:
>>> +      Base address of each IMSIC group.
>>> +
>>> +  interrupt-controller: true
>>> +
>>> +  "#interrupt-cells":
>>> +    const: 0
>>> +
>>> +  msi-controller: true
>>
>> You want then msi-controller.yaml schema and you can drop properties
>> described there.
> 
> Okay, I will include msi-controller.yaml in the next revision.
> 
>>
>>> +
>>> +  interrupts-extended:
>>> +    minItems: 1
>>> +    maxItems: 32768
>>
>> I just wonder if you are not putting some random stuff here... just like
>> this "vendor" company.
>>
>> 32768 inputs it is quite a big chip. Are you sure you have so many pins
>> or internal connections?
> 
> The interrupts-extended property describes the association of IMSIC
> interrupt files with the HARTs. If there are N HARTs then we will have
> N entries in the interrupts-extended (just like the existing PLIC DT bindings).
> 
> For example, if the first entry points to HART1 and the second entry points
> to HART0 then the first interrupt file is associated with HART1 and the
> second interrupt file is associated with HART0.
> 
> Currently, the "maxItems" limit reflects the max IMSICs which an APLIC
> domain can target on a system with both IMSIC and APLIC.
> 
> Actually, there is a typo here. The "maxItems" should be 16384 as-per
> the frozen AIA specification. I will update "maxItems" accordingly in
> next patch revision.
> 
>>
>>> +    description:
>>> +      This property represents the set of CPUs (or HARTs) for which given
>>> +      device tree node describes the IMSIC interrupt files. Each node pointed
>>> +      to should be a riscv,cpu-intc node, which has a riscv node (i.e. RISC-V
>>> +      HART) as parent.
>>> +
>>> +  riscv,num-ids:
>>> +    $ref: /schemas/types.yaml#/definitions/uint32
>>> +    minimum: 63
>>> +    maximum: 2047
>>> +    description:
>>> +      Specifies how many interrupt identities are supported by IMSIC interrupt
>>> +      file.
>>> +
>>> +  riscv,num-guest-ids:
>>> +    $ref: /schemas/types.yaml#/definitions/uint32
>>> +    minimum: 63
>>> +    maximum: 2047
>>> +    description:
>>> +      Specifies how many interrupt identities are supported by IMSIC guest
>>> +      interrupt file. When not specified the number of interrupt identities
>>> +      supported by IMSIC guest file is assumed to be same as specified by
>>> +      the riscv,num-ids property.
>>> +
>>> +  riscv,slow-ipi:
>>> +    type: boolean
>>> +    description:
>>> +      The presence of this property implies that software interrupts (i.e.
>>> +      IPIs) using IMSIC software injected MSIs is slower compared to other
>>> +      software interrupt mechanisms (such as SBI IPI) on the underlying
>>> +      RISC-V platform.
>>
>> Is this a property of software or hardware?
> 
> This is a property of hardware (or implementation) because IPIs
> in IMSIC are software injected MSIs so if IMSIC is trap-n-emulated
> by a hypervisor then all writes to MSI register will trap to hypervisor
> in which case IPI injection via IMSIC is slow.
> 
> The presence of "riscv,slow-ipi" DT property provides a hint to
> driver that using IPIs through IMSIC is slow on this platform so
> if there are other IPI mechanisms (such as SBI IPI calls) then
> OS should prefer those mechanisms.

If this is specific to implementation, why it is not included already in
the compatible?

The name is anyway too vague. What is "slow"? Describe real
characteristics of hardware, e.g. trapped via hypervisor.
> 
>>
>>> +
>>> +  riscv,guest-index-bits:
>>> +    minimum: 0
>>> +    maximum: 7
>>> +    description:
>>> +      Specifies number of guest index bits in the MSI target address. When
>>> +      not specified it is assumed to be 0.
>>> +
>>> +  riscv,hart-index-bits:
>>> +    minimum: 0
>>> +    maximum: 15
>>> +    description:
>>> +      Specifies number of HART index bits in the MSI target address. When
>>> +      not specified it is estimated based on the interrupts-extended property.
>>> +
>>> +  riscv,group-index-bits:
>>> +    minimum: 0
>>> +    maximum: 7
>>> +    description:
>>> +      Specifies number of group index bits in the MSI target address. When
>>> +      not specified it is assumed to be 0.
>>
>> Then default: 0.
> 
> Okay, I will update.
> 
>>
>>> +
>>> +  riscv,group-index-shift:
>>> +    $ref: /schemas/types.yaml#/definitions/uint32
>>> +    minimum: 24
>>> +    maximum: 55
>>> +    description:
>>> +      Specifies the least significant bit of the group index bits in the
>>
>> Please drop everywhere "Specifies the" and instead just describe the
>> hardware.
> 
> Okay, I will update.
> 
>>
>>> +      MSI target address. When not specified it is assumed to be 24.
>>> +
>>> +additionalProperties: false
>>
>> unevaluatedProperties: false and drop all properties already described
>> by other schemas.
> 
> Okay, I will update.
> 
>>
>>> +
>>> +required:
>>> +  - compatible
>>> +  - reg
>>> +  - interrupt-controller
>>> +  - msi-controller
>>> +  - interrupts-extended
>>> +  - riscv,num-ids
>>> +
>>> +examples:
>>> +  - |
>>> +    // Example 1 (Machine-level IMSIC files with just one group):
>>> +
>>> +    imsic_mlevel: interrupt-controller@24000000 {
>>> +      compatible = "vendor,chip-imsics", "riscv,imsics";
>>> +      interrupts-extended = <&cpu1_intc 11>,
>>> +                            <&cpu2_intc 11>,
>>> +                            <&cpu3_intc 11>,
>>> +                            <&cpu4_intc 11>;
>>> +      reg = <0x28000000 0x4000>;
>>> +      interrupt-controller;
>>> +      #interrupt-cells = <0>;
>>> +      msi-controller;
>>> +      riscv,num-ids = <127>;
>>> +    };
>>> +
>>> +  - |
>>> +    // Example 2 (Supervisor-level IMSIC files with two groups):
>>> +
>>> +    imsic_slevel: interrupt-controller@28000000 {
>>> +      compatible = "vendor,chip-imsics", "riscv,imsics";
>>
>> Please run scripts/checkpatch.pl and fix reported warnings.
> 
> I did not see any warnings with ./scripts/checkpatch.pl.
> 
> Is there any parameter of checkpatch.pl which I should try ?

You should see here or with your DTS warnings about undocumented vendor
prefix.

Best regards,
Krzysztof


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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
@ 2022-11-14 12:21         ` Krzysztof Kozlowski
  0 siblings, 0 replies; 96+ messages in thread
From: Krzysztof Kozlowski @ 2022-11-14 12:21 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

On 14/11/2022 13:06, Anup Patel wrote:
> On Mon, Nov 14, 2022 at 3:19 PM Krzysztof Kozlowski
> <krzysztof.kozlowski@linaro.org> wrote:
>>
>> On 11/11/2022 05:42, Anup Patel wrote:
>>> We add DT bindings document for RISC-V incoming MSI controller (IMSIC)
>>> defined by the RISC-V advanced interrupt architecture (AIA) specification.
>>>
>>> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
>>> ---
>>>  .../interrupt-controller/riscv,imsic.yaml     | 174 ++++++++++++++++++
>>>  1 file changed, 174 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
>>>
>>> diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
>>> new file mode 100644
>>> index 000000000000..05106eb1955e
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
>>> @@ -0,0 +1,174 @@
>>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>>> +%YAML 1.2
>>> +---
>>> +$id: http://devicetree.org/schemas/interrupt-controller/riscv,imsic.yaml#
>>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>>> +
>>> +title: RISC-V Incoming MSI Controller (IMSIC)
>>> +
>>> +maintainers:
>>> +  - Anup Patel <anup@brainfault.org>
>>> +
>>> +description:
>>> +  The RISC-V advanced interrupt architecture (AIA) defines a per-CPU incoming
>>> +  MSI controller (IMSIC) for handling MSIs in a RISC-V platform. The RISC-V
>>> +  AIA specification can be found at https://github.com/riscv/riscv-aia.
>>> +
>>> +  The IMSIC is a per-CPU (or per-HART) device with separate interrupt file
>>> +  for each privilege level (machine or supervisor). The configuration of
>>> +  a IMSIC interrupt file is done using AIA CSRs and it also has a 4KB MMIO
>>> +  space to receive MSIs from devices. Each IMSIC interrupt file supports a
>>> +  fixed number of interrupt identities (to distinguish MSIs from devices)
>>> +  which is same for given privilege level across CPUs (or HARTs).
>>> +
>>> +  The arrangement of IMSIC interrupt files in MMIO space of a RISC-V platform
>>> +  follows a particular scheme defined by the RISC-V AIA specification. A IMSIC
>>> +  group is a set of IMSIC interrupt files co-located in MMIO space and we can
>>> +  have multiple IMSIC groups (i.e. clusters, sockets, chiplets, etc) in a
>>> +  RISC-V platform. The MSI target address of a IMSIC interrupt file at given
>>> +  privilege level (machine or supervisor) encodes group index, HART index,
>>> +  and guest index (shown below).
>>> +
>>> +  XLEN-1           >=24                                 12    0
>>> +  |                  |                                  |     |
>>> +  -------------------------------------------------------------
>>> +  |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
>>> +  -------------------------------------------------------------
>>> +
>>> +  The device tree of a RISC-V platform will have one IMSIC device tree node
>>> +  for each privilege level (machine or supervisor) which collectively describe
>>> +  IMSIC interrupt files at that privilege level across CPUs (or HARTs).
>>> +
>>> +allOf:
>>> +  - $ref: /schemas/interrupt-controller.yaml#
>>> +
>>> +properties:
>>> +  compatible:
>>> +    items:
>>> +      - enum:
>>> +          - vendor,chip-imsics
>>
>> There is no such vendor... As Conor pointed out, this does not look
>> correct. Compatibles must be real and specific.
> 
> Previously, Rob had suggest to:
> 1) Mandate two compatible strings: one for implementation and
>     and second for specification
> 2) Since this is new specification with QEMU being the only
>     implementation, we add "vendor,chip-imsics" as dummy
>     implementation specific string for DT schema checkers
>     to pass the examples. Once we have an actual implementation,
>    we will replace this dummy string.
> 
> Refer, https://www.spinics.net/lists/devicetree/msg442720.html

And Rob did not propose vendor as vendor and chip-imsics as device. Read
his message again.

> 
>>
>>> +      - const: riscv,imsics
>>> +
>>> +  reg:
>>> +    minItems: 1
>>> +    maxItems: 128
>>
>> Is there a DTS with 128 reg items?
> 
> Not at the moment since this is a new specification.
> 
> The value "128" is because maximum number of
> IMSIC groups on an system with both IMSIC and
> APLIC is 128 where each IMSIC group has a
> separate base address. This is not a hard limit so
> I am willing to drop it as well.

Is "separate base address" really a separate different range or just
spaced by few registers?

> 
>>
>>> +    description:
>>> +      Base address of each IMSIC group.
>>> +
>>> +  interrupt-controller: true
>>> +
>>> +  "#interrupt-cells":
>>> +    const: 0
>>> +
>>> +  msi-controller: true
>>
>> You want then msi-controller.yaml schema and you can drop properties
>> described there.
> 
> Okay, I will include msi-controller.yaml in the next revision.
> 
>>
>>> +
>>> +  interrupts-extended:
>>> +    minItems: 1
>>> +    maxItems: 32768
>>
>> I just wonder if you are not putting some random stuff here... just like
>> this "vendor" company.
>>
>> 32768 inputs it is quite a big chip. Are you sure you have so many pins
>> or internal connections?
> 
> The interrupts-extended property describes the association of IMSIC
> interrupt files with the HARTs. If there are N HARTs then we will have
> N entries in the interrupts-extended (just like the existing PLIC DT bindings).
> 
> For example, if the first entry points to HART1 and the second entry points
> to HART0 then the first interrupt file is associated with HART1 and the
> second interrupt file is associated with HART0.
> 
> Currently, the "maxItems" limit reflects the max IMSICs which an APLIC
> domain can target on a system with both IMSIC and APLIC.
> 
> Actually, there is a typo here. The "maxItems" should be 16384 as-per
> the frozen AIA specification. I will update "maxItems" accordingly in
> next patch revision.
> 
>>
>>> +    description:
>>> +      This property represents the set of CPUs (or HARTs) for which given
>>> +      device tree node describes the IMSIC interrupt files. Each node pointed
>>> +      to should be a riscv,cpu-intc node, which has a riscv node (i.e. RISC-V
>>> +      HART) as parent.
>>> +
>>> +  riscv,num-ids:
>>> +    $ref: /schemas/types.yaml#/definitions/uint32
>>> +    minimum: 63
>>> +    maximum: 2047
>>> +    description:
>>> +      Specifies how many interrupt identities are supported by IMSIC interrupt
>>> +      file.
>>> +
>>> +  riscv,num-guest-ids:
>>> +    $ref: /schemas/types.yaml#/definitions/uint32
>>> +    minimum: 63
>>> +    maximum: 2047
>>> +    description:
>>> +      Specifies how many interrupt identities are supported by IMSIC guest
>>> +      interrupt file. When not specified the number of interrupt identities
>>> +      supported by IMSIC guest file is assumed to be same as specified by
>>> +      the riscv,num-ids property.
>>> +
>>> +  riscv,slow-ipi:
>>> +    type: boolean
>>> +    description:
>>> +      The presence of this property implies that software interrupts (i.e.
>>> +      IPIs) using IMSIC software injected MSIs is slower compared to other
>>> +      software interrupt mechanisms (such as SBI IPI) on the underlying
>>> +      RISC-V platform.
>>
>> Is this a property of software or hardware?
> 
> This is a property of hardware (or implementation) because IPIs
> in IMSIC are software injected MSIs so if IMSIC is trap-n-emulated
> by a hypervisor then all writes to MSI register will trap to hypervisor
> in which case IPI injection via IMSIC is slow.
> 
> The presence of "riscv,slow-ipi" DT property provides a hint to
> driver that using IPIs through IMSIC is slow on this platform so
> if there are other IPI mechanisms (such as SBI IPI calls) then
> OS should prefer those mechanisms.

If this is specific to implementation, why it is not included already in
the compatible?

The name is anyway too vague. What is "slow"? Describe real
characteristics of hardware, e.g. trapped via hypervisor.
> 
>>
>>> +
>>> +  riscv,guest-index-bits:
>>> +    minimum: 0
>>> +    maximum: 7
>>> +    description:
>>> +      Specifies number of guest index bits in the MSI target address. When
>>> +      not specified it is assumed to be 0.
>>> +
>>> +  riscv,hart-index-bits:
>>> +    minimum: 0
>>> +    maximum: 15
>>> +    description:
>>> +      Specifies number of HART index bits in the MSI target address. When
>>> +      not specified it is estimated based on the interrupts-extended property.
>>> +
>>> +  riscv,group-index-bits:
>>> +    minimum: 0
>>> +    maximum: 7
>>> +    description:
>>> +      Specifies number of group index bits in the MSI target address. When
>>> +      not specified it is assumed to be 0.
>>
>> Then default: 0.
> 
> Okay, I will update.
> 
>>
>>> +
>>> +  riscv,group-index-shift:
>>> +    $ref: /schemas/types.yaml#/definitions/uint32
>>> +    minimum: 24
>>> +    maximum: 55
>>> +    description:
>>> +      Specifies the least significant bit of the group index bits in the
>>
>> Please drop everywhere "Specifies the" and instead just describe the
>> hardware.
> 
> Okay, I will update.
> 
>>
>>> +      MSI target address. When not specified it is assumed to be 24.
>>> +
>>> +additionalProperties: false
>>
>> unevaluatedProperties: false and drop all properties already described
>> by other schemas.
> 
> Okay, I will update.
> 
>>
>>> +
>>> +required:
>>> +  - compatible
>>> +  - reg
>>> +  - interrupt-controller
>>> +  - msi-controller
>>> +  - interrupts-extended
>>> +  - riscv,num-ids
>>> +
>>> +examples:
>>> +  - |
>>> +    // Example 1 (Machine-level IMSIC files with just one group):
>>> +
>>> +    imsic_mlevel: interrupt-controller@24000000 {
>>> +      compatible = "vendor,chip-imsics", "riscv,imsics";
>>> +      interrupts-extended = <&cpu1_intc 11>,
>>> +                            <&cpu2_intc 11>,
>>> +                            <&cpu3_intc 11>,
>>> +                            <&cpu4_intc 11>;
>>> +      reg = <0x28000000 0x4000>;
>>> +      interrupt-controller;
>>> +      #interrupt-cells = <0>;
>>> +      msi-controller;
>>> +      riscv,num-ids = <127>;
>>> +    };
>>> +
>>> +  - |
>>> +    // Example 2 (Supervisor-level IMSIC files with two groups):
>>> +
>>> +    imsic_slevel: interrupt-controller@28000000 {
>>> +      compatible = "vendor,chip-imsics", "riscv,imsics";
>>
>> Please run scripts/checkpatch.pl and fix reported warnings.
> 
> I did not see any warnings with ./scripts/checkpatch.pl.
> 
> Is there any parameter of checkpatch.pl which I should try ?

You should see here or with your DTS warnings about undocumented vendor
prefix.

Best regards,
Krzysztof


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
  2022-11-13 14:48     ` Conor Dooley
@ 2022-11-14 12:29       ` Anup Patel
  -1 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-14 12:29 UTC (permalink / raw)
  To: Conor Dooley
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

On Sun, Nov 13, 2022 at 8:18 PM Conor Dooley <conor@kernel.org> wrote:
>
> Hey Anup,
>
> On Fri, Nov 11, 2022 at 10:12:02AM +0530, Anup Patel wrote:
> > dt-bindings: Add RISC-V incoming MSI controller bindings
>
> nit: it looks like the usual prefix here is "dt-bindings:
> interrupt-controller".

Okay, I will update.

>
> > We add DT bindings document for RISC-V incoming MSI controller (IMSIC)
> > defined by the RISC-V advanced interrupt architecture (AIA) specification.
> >
> > Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> > ---
> >  .../interrupt-controller/riscv,imsic.yaml     | 174 ++++++++++++++++++
> >  1 file changed, 174 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > new file mode 100644
> > index 000000000000..05106eb1955e
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > @@ -0,0 +1,174 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/interrupt-controller/riscv,imsic.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: RISC-V Incoming MSI Controller (IMSIC)
> > +
> > +maintainers:
> > +  - Anup Patel <anup@brainfault.org>
> > +
> > +description:
>
> Is this one of the situations where we want to have a | after
> "description:" to preserve formatting?

Okay, I will update.

>
> > +  The RISC-V advanced interrupt architecture (AIA) defines a per-CPU incoming
> > +  MSI controller (IMSIC) for handling MSIs in a RISC-V platform. The RISC-V
> > +  AIA specification can be found at https://github.com/riscv/riscv-aia.
> > +
> > +  The IMSIC is a per-CPU (or per-HART) device with separate interrupt file
> > +  for each privilege level (machine or supervisor). The configuration of
> > +  a IMSIC interrupt file is done using AIA CSRs and it also has a 4KB MMIO
> > +  space to receive MSIs from devices. Each IMSIC interrupt file supports a
> > +  fixed number of interrupt identities (to distinguish MSIs from devices)
> > +  which is same for given privilege level across CPUs (or HARTs).
> > +
> > +  The arrangement of IMSIC interrupt files in MMIO space of a RISC-V platform
> > +  follows a particular scheme defined by the RISC-V AIA specification. A IMSIC
> > +  group is a set of IMSIC interrupt files co-located in MMIO space and we can
> > +  have multiple IMSIC groups (i.e. clusters, sockets, chiplets, etc) in a
> > +  RISC-V platform. The MSI target address of a IMSIC interrupt file at given
> > +  privilege level (machine or supervisor) encodes group index, HART index,
> > +  and guest index (shown below).
> > +
> > +  XLEN-1           >=24                                 12    0
> > +  |                  |                                  |     |
> > +  -------------------------------------------------------------
> > +  |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
> > +  -------------------------------------------------------------
> > +
> > +  The device tree of a RISC-V platform will have one IMSIC device tree node
> > +  for each privilege level (machine or supervisor) which collectively describe
> > +  IMSIC interrupt files at that privilege level across CPUs (or HARTs).
> > +
> > +allOf:
> > +  - $ref: /schemas/interrupt-controller.yaml#
> > +
> > +properties:
> > +  compatible:
> > +    items:
> > +      - enum:
> > +          - vendor,chip-imsics
>
> Is it valid to have a dummy here? I did a bit of grepping & could not
> see a single other yaml binding which used a placeholder like this -
> other than the example schema itself. I assume you're trying to get
> across the point that using the bare riscv,imsics is not okay and a
> vendor should create a custom string for their implementation?

Yes, this dummy is a placeholder to mandate two compatible strings.
The dummy can eventually be replaced by some actual implementation
compatible string.

>
> Also, the file name says "riscv,imsic", the description says "IMSIC" but
> you've used "imsics" in the compatible. Is this a typo, or a plural?

Yes, the file name should be consistent. I will update the file name.

>
> Thanks,
> Conor.
>
> > +      - const: riscv,imsics
> > +
> > +  reg:
> > +    minItems: 1
> > +    maxItems: 128
> > +    description:
> > +      Base address of each IMSIC group.
> > +
> > +  interrupt-controller: true
> > +
> > +  "#interrupt-cells":
> > +    const: 0
> > +
> > +  msi-controller: true
> > +
> > +  interrupts-extended:
> > +    minItems: 1
> > +    maxItems: 32768
> > +    description:
> > +      This property represents the set of CPUs (or HARTs) for which given
> > +      device tree node describes the IMSIC interrupt files. Each node pointed
> > +      to should be a riscv,cpu-intc node, which has a riscv node (i.e. RISC-V
> > +      HART) as parent.
> > +
> > +  riscv,num-ids:
> > +    $ref: /schemas/types.yaml#/definitions/uint32
> > +    minimum: 63
> > +    maximum: 2047
> > +    description:
> > +      Specifies how many interrupt identities are supported by IMSIC interrupt
> > +      file.
> > +
> > +  riscv,num-guest-ids:
> > +    $ref: /schemas/types.yaml#/definitions/uint32
> > +    minimum: 63
> > +    maximum: 2047
> > +    description:
> > +      Specifies how many interrupt identities are supported by IMSIC guest
> > +      interrupt file. When not specified the number of interrupt identities
> > +      supported by IMSIC guest file is assumed to be same as specified by
> > +      the riscv,num-ids property.
> > +
> > +  riscv,slow-ipi:
> > +    type: boolean
> > +    description:
> > +      The presence of this property implies that software interrupts (i.e.
> > +      IPIs) using IMSIC software injected MSIs is slower compared to other
> > +      software interrupt mechanisms (such as SBI IPI) on the underlying
> > +      RISC-V platform.
> > +
> > +  riscv,guest-index-bits:
> > +    minimum: 0
> > +    maximum: 7
> > +    description:
> > +      Specifies number of guest index bits in the MSI target address. When
> > +      not specified it is assumed to be 0.
> > +
> > +  riscv,hart-index-bits:
> > +    minimum: 0
> > +    maximum: 15
> > +    description:
> > +      Specifies number of HART index bits in the MSI target address. When
> > +      not specified it is estimated based on the interrupts-extended property.
> > +
> > +  riscv,group-index-bits:
> > +    minimum: 0
> > +    maximum: 7
> > +    description:
> > +      Specifies number of group index bits in the MSI target address. When
> > +      not specified it is assumed to be 0.
> > +
> > +  riscv,group-index-shift:
> > +    $ref: /schemas/types.yaml#/definitions/uint32
> > +    minimum: 24
> > +    maximum: 55
> > +    description:
> > +      Specifies the least significant bit of the group index bits in the
> > +      MSI target address. When not specified it is assumed to be 24.
> > +
> > +additionalProperties: false
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - interrupt-controller
> > +  - msi-controller
> > +  - interrupts-extended
> > +  - riscv,num-ids
> > +
> > +examples:
> > +  - |
> > +    // Example 1 (Machine-level IMSIC files with just one group):
> > +
> > +    imsic_mlevel: interrupt-controller@24000000 {
> > +      compatible = "vendor,chip-imsics", "riscv,imsics";
> > +      interrupts-extended = <&cpu1_intc 11>,
> > +                            <&cpu2_intc 11>,
> > +                            <&cpu3_intc 11>,
> > +                            <&cpu4_intc 11>;
> > +      reg = <0x28000000 0x4000>;
> > +      interrupt-controller;
> > +      #interrupt-cells = <0>;
> > +      msi-controller;
> > +      riscv,num-ids = <127>;
> > +    };
> > +
> > +  - |
> > +    // Example 2 (Supervisor-level IMSIC files with two groups):
> > +
> > +    imsic_slevel: interrupt-controller@28000000 {
> > +      compatible = "vendor,chip-imsics", "riscv,imsics";
> > +      interrupts-extended = <&cpu1_intc 9>,
> > +                            <&cpu2_intc 9>,
> > +                            <&cpu3_intc 9>,
> > +                            <&cpu4_intc 9>;
> > +      reg = <0x28000000 0x2000>, /* Group0 IMSICs */
> > +            <0x29000000 0x2000>; /* Group1 IMSICs */
> > +      interrupt-controller;
> > +      #interrupt-cells = <0>;
> > +      msi-controller;
> > +      riscv,num-ids = <127>;
> > +      riscv,group-index-bits = <1>;
> > +      riscv,group-index-shift = <24>;
> > +    };
> > +...
> > --
> > 2.34.1
> >
> >
> > _______________________________________________
> > linux-riscv mailing list
> > linux-riscv@lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/linux-riscv

Regards,
Anup

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
@ 2022-11-14 12:29       ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-14 12:29 UTC (permalink / raw)
  To: Conor Dooley
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

On Sun, Nov 13, 2022 at 8:18 PM Conor Dooley <conor@kernel.org> wrote:
>
> Hey Anup,
>
> On Fri, Nov 11, 2022 at 10:12:02AM +0530, Anup Patel wrote:
> > dt-bindings: Add RISC-V incoming MSI controller bindings
>
> nit: it looks like the usual prefix here is "dt-bindings:
> interrupt-controller".

Okay, I will update.

>
> > We add DT bindings document for RISC-V incoming MSI controller (IMSIC)
> > defined by the RISC-V advanced interrupt architecture (AIA) specification.
> >
> > Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> > ---
> >  .../interrupt-controller/riscv,imsic.yaml     | 174 ++++++++++++++++++
> >  1 file changed, 174 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > new file mode 100644
> > index 000000000000..05106eb1955e
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > @@ -0,0 +1,174 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/interrupt-controller/riscv,imsic.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: RISC-V Incoming MSI Controller (IMSIC)
> > +
> > +maintainers:
> > +  - Anup Patel <anup@brainfault.org>
> > +
> > +description:
>
> Is this one of the situations where we want to have a | after
> "description:" to preserve formatting?

Okay, I will update.

>
> > +  The RISC-V advanced interrupt architecture (AIA) defines a per-CPU incoming
> > +  MSI controller (IMSIC) for handling MSIs in a RISC-V platform. The RISC-V
> > +  AIA specification can be found at https://github.com/riscv/riscv-aia.
> > +
> > +  The IMSIC is a per-CPU (or per-HART) device with separate interrupt file
> > +  for each privilege level (machine or supervisor). The configuration of
> > +  a IMSIC interrupt file is done using AIA CSRs and it also has a 4KB MMIO
> > +  space to receive MSIs from devices. Each IMSIC interrupt file supports a
> > +  fixed number of interrupt identities (to distinguish MSIs from devices)
> > +  which is same for given privilege level across CPUs (or HARTs).
> > +
> > +  The arrangement of IMSIC interrupt files in MMIO space of a RISC-V platform
> > +  follows a particular scheme defined by the RISC-V AIA specification. A IMSIC
> > +  group is a set of IMSIC interrupt files co-located in MMIO space and we can
> > +  have multiple IMSIC groups (i.e. clusters, sockets, chiplets, etc) in a
> > +  RISC-V platform. The MSI target address of a IMSIC interrupt file at given
> > +  privilege level (machine or supervisor) encodes group index, HART index,
> > +  and guest index (shown below).
> > +
> > +  XLEN-1           >=24                                 12    0
> > +  |                  |                                  |     |
> > +  -------------------------------------------------------------
> > +  |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
> > +  -------------------------------------------------------------
> > +
> > +  The device tree of a RISC-V platform will have one IMSIC device tree node
> > +  for each privilege level (machine or supervisor) which collectively describe
> > +  IMSIC interrupt files at that privilege level across CPUs (or HARTs).
> > +
> > +allOf:
> > +  - $ref: /schemas/interrupt-controller.yaml#
> > +
> > +properties:
> > +  compatible:
> > +    items:
> > +      - enum:
> > +          - vendor,chip-imsics
>
> Is it valid to have a dummy here? I did a bit of grepping & could not
> see a single other yaml binding which used a placeholder like this -
> other than the example schema itself. I assume you're trying to get
> across the point that using the bare riscv,imsics is not okay and a
> vendor should create a custom string for their implementation?

Yes, this dummy is a placeholder to mandate two compatible strings.
The dummy can eventually be replaced by some actual implementation
compatible string.

>
> Also, the file name says "riscv,imsic", the description says "IMSIC" but
> you've used "imsics" in the compatible. Is this a typo, or a plural?

Yes, the file name should be consistent. I will update the file name.

>
> Thanks,
> Conor.
>
> > +      - const: riscv,imsics
> > +
> > +  reg:
> > +    minItems: 1
> > +    maxItems: 128
> > +    description:
> > +      Base address of each IMSIC group.
> > +
> > +  interrupt-controller: true
> > +
> > +  "#interrupt-cells":
> > +    const: 0
> > +
> > +  msi-controller: true
> > +
> > +  interrupts-extended:
> > +    minItems: 1
> > +    maxItems: 32768
> > +    description:
> > +      This property represents the set of CPUs (or HARTs) for which given
> > +      device tree node describes the IMSIC interrupt files. Each node pointed
> > +      to should be a riscv,cpu-intc node, which has a riscv node (i.e. RISC-V
> > +      HART) as parent.
> > +
> > +  riscv,num-ids:
> > +    $ref: /schemas/types.yaml#/definitions/uint32
> > +    minimum: 63
> > +    maximum: 2047
> > +    description:
> > +      Specifies how many interrupt identities are supported by IMSIC interrupt
> > +      file.
> > +
> > +  riscv,num-guest-ids:
> > +    $ref: /schemas/types.yaml#/definitions/uint32
> > +    minimum: 63
> > +    maximum: 2047
> > +    description:
> > +      Specifies how many interrupt identities are supported by IMSIC guest
> > +      interrupt file. When not specified the number of interrupt identities
> > +      supported by IMSIC guest file is assumed to be same as specified by
> > +      the riscv,num-ids property.
> > +
> > +  riscv,slow-ipi:
> > +    type: boolean
> > +    description:
> > +      The presence of this property implies that software interrupts (i.e.
> > +      IPIs) using IMSIC software injected MSIs is slower compared to other
> > +      software interrupt mechanisms (such as SBI IPI) on the underlying
> > +      RISC-V platform.
> > +
> > +  riscv,guest-index-bits:
> > +    minimum: 0
> > +    maximum: 7
> > +    description:
> > +      Specifies number of guest index bits in the MSI target address. When
> > +      not specified it is assumed to be 0.
> > +
> > +  riscv,hart-index-bits:
> > +    minimum: 0
> > +    maximum: 15
> > +    description:
> > +      Specifies number of HART index bits in the MSI target address. When
> > +      not specified it is estimated based on the interrupts-extended property.
> > +
> > +  riscv,group-index-bits:
> > +    minimum: 0
> > +    maximum: 7
> > +    description:
> > +      Specifies number of group index bits in the MSI target address. When
> > +      not specified it is assumed to be 0.
> > +
> > +  riscv,group-index-shift:
> > +    $ref: /schemas/types.yaml#/definitions/uint32
> > +    minimum: 24
> > +    maximum: 55
> > +    description:
> > +      Specifies the least significant bit of the group index bits in the
> > +      MSI target address. When not specified it is assumed to be 24.
> > +
> > +additionalProperties: false
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - interrupt-controller
> > +  - msi-controller
> > +  - interrupts-extended
> > +  - riscv,num-ids
> > +
> > +examples:
> > +  - |
> > +    // Example 1 (Machine-level IMSIC files with just one group):
> > +
> > +    imsic_mlevel: interrupt-controller@24000000 {
> > +      compatible = "vendor,chip-imsics", "riscv,imsics";
> > +      interrupts-extended = <&cpu1_intc 11>,
> > +                            <&cpu2_intc 11>,
> > +                            <&cpu3_intc 11>,
> > +                            <&cpu4_intc 11>;
> > +      reg = <0x28000000 0x4000>;
> > +      interrupt-controller;
> > +      #interrupt-cells = <0>;
> > +      msi-controller;
> > +      riscv,num-ids = <127>;
> > +    };
> > +
> > +  - |
> > +    // Example 2 (Supervisor-level IMSIC files with two groups):
> > +
> > +    imsic_slevel: interrupt-controller@28000000 {
> > +      compatible = "vendor,chip-imsics", "riscv,imsics";
> > +      interrupts-extended = <&cpu1_intc 9>,
> > +                            <&cpu2_intc 9>,
> > +                            <&cpu3_intc 9>,
> > +                            <&cpu4_intc 9>;
> > +      reg = <0x28000000 0x2000>, /* Group0 IMSICs */
> > +            <0x29000000 0x2000>; /* Group1 IMSICs */
> > +      interrupt-controller;
> > +      #interrupt-cells = <0>;
> > +      msi-controller;
> > +      riscv,num-ids = <127>;
> > +      riscv,group-index-bits = <1>;
> > +      riscv,group-index-shift = <24>;
> > +    };
> > +...
> > --
> > 2.34.1
> >
> >
> > _______________________________________________
> > linux-riscv mailing list
> > linux-riscv@lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/linux-riscv

Regards,
Anup

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
  2022-11-14 12:21         ` Krzysztof Kozlowski
@ 2022-11-14 15:04           ` Anup Patel
  -1 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-14 15:04 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

On Mon, Nov 14, 2022 at 5:52 PM Krzysztof Kozlowski
<krzysztof.kozlowski@linaro.org> wrote:
>
> On 14/11/2022 13:06, Anup Patel wrote:
> > On Mon, Nov 14, 2022 at 3:19 PM Krzysztof Kozlowski
> > <krzysztof.kozlowski@linaro.org> wrote:
> >>
> >> On 11/11/2022 05:42, Anup Patel wrote:
> >>> We add DT bindings document for RISC-V incoming MSI controller (IMSIC)
> >>> defined by the RISC-V advanced interrupt architecture (AIA) specification.
> >>>
> >>> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> >>> ---
> >>>  .../interrupt-controller/riscv,imsic.yaml     | 174 ++++++++++++++++++
> >>>  1 file changed, 174 insertions(+)
> >>>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> >>>
> >>> diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> >>> new file mode 100644
> >>> index 000000000000..05106eb1955e
> >>> --- /dev/null
> >>> +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> >>> @@ -0,0 +1,174 @@
> >>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> >>> +%YAML 1.2
> >>> +---
> >>> +$id: http://devicetree.org/schemas/interrupt-controller/riscv,imsic.yaml#
> >>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> >>> +
> >>> +title: RISC-V Incoming MSI Controller (IMSIC)
> >>> +
> >>> +maintainers:
> >>> +  - Anup Patel <anup@brainfault.org>
> >>> +
> >>> +description:
> >>> +  The RISC-V advanced interrupt architecture (AIA) defines a per-CPU incoming
> >>> +  MSI controller (IMSIC) for handling MSIs in a RISC-V platform. The RISC-V
> >>> +  AIA specification can be found at https://github.com/riscv/riscv-aia.
> >>> +
> >>> +  The IMSIC is a per-CPU (or per-HART) device with separate interrupt file
> >>> +  for each privilege level (machine or supervisor). The configuration of
> >>> +  a IMSIC interrupt file is done using AIA CSRs and it also has a 4KB MMIO
> >>> +  space to receive MSIs from devices. Each IMSIC interrupt file supports a
> >>> +  fixed number of interrupt identities (to distinguish MSIs from devices)
> >>> +  which is same for given privilege level across CPUs (or HARTs).
> >>> +
> >>> +  The arrangement of IMSIC interrupt files in MMIO space of a RISC-V platform
> >>> +  follows a particular scheme defined by the RISC-V AIA specification. A IMSIC
> >>> +  group is a set of IMSIC interrupt files co-located in MMIO space and we can
> >>> +  have multiple IMSIC groups (i.e. clusters, sockets, chiplets, etc) in a
> >>> +  RISC-V platform. The MSI target address of a IMSIC interrupt file at given
> >>> +  privilege level (machine or supervisor) encodes group index, HART index,
> >>> +  and guest index (shown below).
> >>> +
> >>> +  XLEN-1           >=24                                 12    0
> >>> +  |                  |                                  |     |
> >>> +  -------------------------------------------------------------
> >>> +  |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
> >>> +  -------------------------------------------------------------
> >>> +
> >>> +  The device tree of a RISC-V platform will have one IMSIC device tree node
> >>> +  for each privilege level (machine or supervisor) which collectively describe
> >>> +  IMSIC interrupt files at that privilege level across CPUs (or HARTs).
> >>> +
> >>> +allOf:
> >>> +  - $ref: /schemas/interrupt-controller.yaml#
> >>> +
> >>> +properties:
> >>> +  compatible:
> >>> +    items:
> >>> +      - enum:
> >>> +          - vendor,chip-imsics
> >>
> >> There is no such vendor... As Conor pointed out, this does not look
> >> correct. Compatibles must be real and specific.
> >
> > Previously, Rob had suggest to:
> > 1) Mandate two compatible strings: one for implementation and
> >     and second for specification
> > 2) Since this is new specification with QEMU being the only
> >     implementation, we add "vendor,chip-imsics" as dummy
> >     implementation specific string for DT schema checkers
> >     to pass the examples. Once we have an actual implementation,
> >    we will replace this dummy string.
> >
> > Refer, https://www.spinics.net/lists/devicetree/msg442720.html
>
> And Rob did not propose vendor as vendor and chip-imsics as device. Read
> his message again.

Okay

>
> >
> >>
> >>> +      - const: riscv,imsics
> >>> +
> >>> +  reg:
> >>> +    minItems: 1
> >>> +    maxItems: 128
> >>
> >> Is there a DTS with 128 reg items?
> >
> > Not at the moment since this is a new specification.
> >
> > The value "128" is because maximum number of
> > IMSIC groups on an system with both IMSIC and
> > APLIC is 128 where each IMSIC group has a
> > separate base address. This is not a hard limit so
> > I am willing to drop it as well.
>
> Is "separate base address" really a separate different range or just
> spaced by few registers?

Yes, "separate base address" of an IMSIC group
means a separate different range.

We can think of an IMSIC group as a CPU cluster or
chiplet or die. The IMSIC files within a group are
located next to each other whereas the groups can
be far away from each other.

>
> >
> >>
> >>> +    description:
> >>> +      Base address of each IMSIC group.
> >>> +
> >>> +  interrupt-controller: true
> >>> +
> >>> +  "#interrupt-cells":
> >>> +    const: 0
> >>> +
> >>> +  msi-controller: true
> >>
> >> You want then msi-controller.yaml schema and you can drop properties
> >> described there.
> >
> > Okay, I will include msi-controller.yaml in the next revision.
> >
> >>
> >>> +
> >>> +  interrupts-extended:
> >>> +    minItems: 1
> >>> +    maxItems: 32768
> >>
> >> I just wonder if you are not putting some random stuff here... just like
> >> this "vendor" company.
> >>
> >> 32768 inputs it is quite a big chip. Are you sure you have so many pins
> >> or internal connections?
> >
> > The interrupts-extended property describes the association of IMSIC
> > interrupt files with the HARTs. If there are N HARTs then we will have
> > N entries in the interrupts-extended (just like the existing PLIC DT bindings).
> >
> > For example, if the first entry points to HART1 and the second entry points
> > to HART0 then the first interrupt file is associated with HART1 and the
> > second interrupt file is associated with HART0.
> >
> > Currently, the "maxItems" limit reflects the max IMSICs which an APLIC
> > domain can target on a system with both IMSIC and APLIC.
> >
> > Actually, there is a typo here. The "maxItems" should be 16384 as-per
> > the frozen AIA specification. I will update "maxItems" accordingly in
> > next patch revision.
> >
> >>
> >>> +    description:
> >>> +      This property represents the set of CPUs (or HARTs) for which given
> >>> +      device tree node describes the IMSIC interrupt files. Each node pointed
> >>> +      to should be a riscv,cpu-intc node, which has a riscv node (i.e. RISC-V
> >>> +      HART) as parent.
> >>> +
> >>> +  riscv,num-ids:
> >>> +    $ref: /schemas/types.yaml#/definitions/uint32
> >>> +    minimum: 63
> >>> +    maximum: 2047
> >>> +    description:
> >>> +      Specifies how many interrupt identities are supported by IMSIC interrupt
> >>> +      file.
> >>> +
> >>> +  riscv,num-guest-ids:
> >>> +    $ref: /schemas/types.yaml#/definitions/uint32
> >>> +    minimum: 63
> >>> +    maximum: 2047
> >>> +    description:
> >>> +      Specifies how many interrupt identities are supported by IMSIC guest
> >>> +      interrupt file. When not specified the number of interrupt identities
> >>> +      supported by IMSIC guest file is assumed to be same as specified by
> >>> +      the riscv,num-ids property.
> >>> +
> >>> +  riscv,slow-ipi:
> >>> +    type: boolean
> >>> +    description:
> >>> +      The presence of this property implies that software interrupts (i.e.
> >>> +      IPIs) using IMSIC software injected MSIs is slower compared to other
> >>> +      software interrupt mechanisms (such as SBI IPI) on the underlying
> >>> +      RISC-V platform.
> >>
> >> Is this a property of software or hardware?
> >
> > This is a property of hardware (or implementation) because IPIs
> > in IMSIC are software injected MSIs so if IMSIC is trap-n-emulated
> > by a hypervisor then all writes to MSI register will trap to hypervisor
> > in which case IPI injection via IMSIC is slow.
> >
> > The presence of "riscv,slow-ipi" DT property provides a hint to
> > driver that using IPIs through IMSIC is slow on this platform so
> > if there are other IPI mechanisms (such as SBI IPI calls) then
> > OS should prefer those mechanisms.
>
> If this is specific to implementation, why it is not included already in
> the compatible?
>
> The name is anyway too vague. What is "slow"? Describe real
> characteristics of hardware, e.g. trapped via hypervisor.

Okay, how about renaming it to "riscv,trap-n-emulated" ?

Alternately, we can add "riscv,soft-imsics" as an implementation
specific compatible string which hypervisors can use to describe
trap-n-emulated IMSICs. This "riscv,soft-imsics" can also replace
"vendor,chip-imsics" dummy string ?


> >
> >>
> >>> +
> >>> +  riscv,guest-index-bits:
> >>> +    minimum: 0
> >>> +    maximum: 7
> >>> +    description:
> >>> +      Specifies number of guest index bits in the MSI target address. When
> >>> +      not specified it is assumed to be 0.
> >>> +
> >>> +  riscv,hart-index-bits:
> >>> +    minimum: 0
> >>> +    maximum: 15
> >>> +    description:
> >>> +      Specifies number of HART index bits in the MSI target address. When
> >>> +      not specified it is estimated based on the interrupts-extended property.
> >>> +
> >>> +  riscv,group-index-bits:
> >>> +    minimum: 0
> >>> +    maximum: 7
> >>> +    description:
> >>> +      Specifies number of group index bits in the MSI target address. When
> >>> +      not specified it is assumed to be 0.
> >>
> >> Then default: 0.
> >
> > Okay, I will update.
> >
> >>
> >>> +
> >>> +  riscv,group-index-shift:
> >>> +    $ref: /schemas/types.yaml#/definitions/uint32
> >>> +    minimum: 24
> >>> +    maximum: 55
> >>> +    description:
> >>> +      Specifies the least significant bit of the group index bits in the
> >>
> >> Please drop everywhere "Specifies the" and instead just describe the
> >> hardware.
> >
> > Okay, I will update.
> >
> >>
> >>> +      MSI target address. When not specified it is assumed to be 24.
> >>> +
> >>> +additionalProperties: false
> >>
> >> unevaluatedProperties: false and drop all properties already described
> >> by other schemas.
> >
> > Okay, I will update.
> >
> >>
> >>> +
> >>> +required:
> >>> +  - compatible
> >>> +  - reg
> >>> +  - interrupt-controller
> >>> +  - msi-controller
> >>> +  - interrupts-extended
> >>> +  - riscv,num-ids
> >>> +
> >>> +examples:
> >>> +  - |
> >>> +    // Example 1 (Machine-level IMSIC files with just one group):
> >>> +
> >>> +    imsic_mlevel: interrupt-controller@24000000 {
> >>> +      compatible = "vendor,chip-imsics", "riscv,imsics";
> >>> +      interrupts-extended = <&cpu1_intc 11>,
> >>> +                            <&cpu2_intc 11>,
> >>> +                            <&cpu3_intc 11>,
> >>> +                            <&cpu4_intc 11>;
> >>> +      reg = <0x28000000 0x4000>;
> >>> +      interrupt-controller;
> >>> +      #interrupt-cells = <0>;
> >>> +      msi-controller;
> >>> +      riscv,num-ids = <127>;
> >>> +    };
> >>> +
> >>> +  - |
> >>> +    // Example 2 (Supervisor-level IMSIC files with two groups):
> >>> +
> >>> +    imsic_slevel: interrupt-controller@28000000 {
> >>> +      compatible = "vendor,chip-imsics", "riscv,imsics";
> >>
> >> Please run scripts/checkpatch.pl and fix reported warnings.
> >
> > I did not see any warnings with ./scripts/checkpatch.pl.
> >
> > Is there any parameter of checkpatch.pl which I should try ?
>
> You should see here or with your DTS warnings about undocumented vendor
> prefix.

Okay, I will check.

>
> Best regards,
> Krzysztof
>

Best Regards,
Anup

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
@ 2022-11-14 15:04           ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-14 15:04 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

On Mon, Nov 14, 2022 at 5:52 PM Krzysztof Kozlowski
<krzysztof.kozlowski@linaro.org> wrote:
>
> On 14/11/2022 13:06, Anup Patel wrote:
> > On Mon, Nov 14, 2022 at 3:19 PM Krzysztof Kozlowski
> > <krzysztof.kozlowski@linaro.org> wrote:
> >>
> >> On 11/11/2022 05:42, Anup Patel wrote:
> >>> We add DT bindings document for RISC-V incoming MSI controller (IMSIC)
> >>> defined by the RISC-V advanced interrupt architecture (AIA) specification.
> >>>
> >>> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> >>> ---
> >>>  .../interrupt-controller/riscv,imsic.yaml     | 174 ++++++++++++++++++
> >>>  1 file changed, 174 insertions(+)
> >>>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> >>>
> >>> diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> >>> new file mode 100644
> >>> index 000000000000..05106eb1955e
> >>> --- /dev/null
> >>> +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> >>> @@ -0,0 +1,174 @@
> >>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> >>> +%YAML 1.2
> >>> +---
> >>> +$id: http://devicetree.org/schemas/interrupt-controller/riscv,imsic.yaml#
> >>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> >>> +
> >>> +title: RISC-V Incoming MSI Controller (IMSIC)
> >>> +
> >>> +maintainers:
> >>> +  - Anup Patel <anup@brainfault.org>
> >>> +
> >>> +description:
> >>> +  The RISC-V advanced interrupt architecture (AIA) defines a per-CPU incoming
> >>> +  MSI controller (IMSIC) for handling MSIs in a RISC-V platform. The RISC-V
> >>> +  AIA specification can be found at https://github.com/riscv/riscv-aia.
> >>> +
> >>> +  The IMSIC is a per-CPU (or per-HART) device with separate interrupt file
> >>> +  for each privilege level (machine or supervisor). The configuration of
> >>> +  a IMSIC interrupt file is done using AIA CSRs and it also has a 4KB MMIO
> >>> +  space to receive MSIs from devices. Each IMSIC interrupt file supports a
> >>> +  fixed number of interrupt identities (to distinguish MSIs from devices)
> >>> +  which is same for given privilege level across CPUs (or HARTs).
> >>> +
> >>> +  The arrangement of IMSIC interrupt files in MMIO space of a RISC-V platform
> >>> +  follows a particular scheme defined by the RISC-V AIA specification. A IMSIC
> >>> +  group is a set of IMSIC interrupt files co-located in MMIO space and we can
> >>> +  have multiple IMSIC groups (i.e. clusters, sockets, chiplets, etc) in a
> >>> +  RISC-V platform. The MSI target address of a IMSIC interrupt file at given
> >>> +  privilege level (machine or supervisor) encodes group index, HART index,
> >>> +  and guest index (shown below).
> >>> +
> >>> +  XLEN-1           >=24                                 12    0
> >>> +  |                  |                                  |     |
> >>> +  -------------------------------------------------------------
> >>> +  |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
> >>> +  -------------------------------------------------------------
> >>> +
> >>> +  The device tree of a RISC-V platform will have one IMSIC device tree node
> >>> +  for each privilege level (machine or supervisor) which collectively describe
> >>> +  IMSIC interrupt files at that privilege level across CPUs (or HARTs).
> >>> +
> >>> +allOf:
> >>> +  - $ref: /schemas/interrupt-controller.yaml#
> >>> +
> >>> +properties:
> >>> +  compatible:
> >>> +    items:
> >>> +      - enum:
> >>> +          - vendor,chip-imsics
> >>
> >> There is no such vendor... As Conor pointed out, this does not look
> >> correct. Compatibles must be real and specific.
> >
> > Previously, Rob had suggest to:
> > 1) Mandate two compatible strings: one for implementation and
> >     and second for specification
> > 2) Since this is new specification with QEMU being the only
> >     implementation, we add "vendor,chip-imsics" as dummy
> >     implementation specific string for DT schema checkers
> >     to pass the examples. Once we have an actual implementation,
> >    we will replace this dummy string.
> >
> > Refer, https://www.spinics.net/lists/devicetree/msg442720.html
>
> And Rob did not propose vendor as vendor and chip-imsics as device. Read
> his message again.

Okay

>
> >
> >>
> >>> +      - const: riscv,imsics
> >>> +
> >>> +  reg:
> >>> +    minItems: 1
> >>> +    maxItems: 128
> >>
> >> Is there a DTS with 128 reg items?
> >
> > Not at the moment since this is a new specification.
> >
> > The value "128" is because maximum number of
> > IMSIC groups on an system with both IMSIC and
> > APLIC is 128 where each IMSIC group has a
> > separate base address. This is not a hard limit so
> > I am willing to drop it as well.
>
> Is "separate base address" really a separate different range or just
> spaced by few registers?

Yes, "separate base address" of an IMSIC group
means a separate different range.

We can think of an IMSIC group as a CPU cluster or
chiplet or die. The IMSIC files within a group are
located next to each other whereas the groups can
be far away from each other.

>
> >
> >>
> >>> +    description:
> >>> +      Base address of each IMSIC group.
> >>> +
> >>> +  interrupt-controller: true
> >>> +
> >>> +  "#interrupt-cells":
> >>> +    const: 0
> >>> +
> >>> +  msi-controller: true
> >>
> >> You want then msi-controller.yaml schema and you can drop properties
> >> described there.
> >
> > Okay, I will include msi-controller.yaml in the next revision.
> >
> >>
> >>> +
> >>> +  interrupts-extended:
> >>> +    minItems: 1
> >>> +    maxItems: 32768
> >>
> >> I just wonder if you are not putting some random stuff here... just like
> >> this "vendor" company.
> >>
> >> 32768 inputs it is quite a big chip. Are you sure you have so many pins
> >> or internal connections?
> >
> > The interrupts-extended property describes the association of IMSIC
> > interrupt files with the HARTs. If there are N HARTs then we will have
> > N entries in the interrupts-extended (just like the existing PLIC DT bindings).
> >
> > For example, if the first entry points to HART1 and the second entry points
> > to HART0 then the first interrupt file is associated with HART1 and the
> > second interrupt file is associated with HART0.
> >
> > Currently, the "maxItems" limit reflects the max IMSICs which an APLIC
> > domain can target on a system with both IMSIC and APLIC.
> >
> > Actually, there is a typo here. The "maxItems" should be 16384 as-per
> > the frozen AIA specification. I will update "maxItems" accordingly in
> > next patch revision.
> >
> >>
> >>> +    description:
> >>> +      This property represents the set of CPUs (or HARTs) for which given
> >>> +      device tree node describes the IMSIC interrupt files. Each node pointed
> >>> +      to should be a riscv,cpu-intc node, which has a riscv node (i.e. RISC-V
> >>> +      HART) as parent.
> >>> +
> >>> +  riscv,num-ids:
> >>> +    $ref: /schemas/types.yaml#/definitions/uint32
> >>> +    minimum: 63
> >>> +    maximum: 2047
> >>> +    description:
> >>> +      Specifies how many interrupt identities are supported by IMSIC interrupt
> >>> +      file.
> >>> +
> >>> +  riscv,num-guest-ids:
> >>> +    $ref: /schemas/types.yaml#/definitions/uint32
> >>> +    minimum: 63
> >>> +    maximum: 2047
> >>> +    description:
> >>> +      Specifies how many interrupt identities are supported by IMSIC guest
> >>> +      interrupt file. When not specified the number of interrupt identities
> >>> +      supported by IMSIC guest file is assumed to be same as specified by
> >>> +      the riscv,num-ids property.
> >>> +
> >>> +  riscv,slow-ipi:
> >>> +    type: boolean
> >>> +    description:
> >>> +      The presence of this property implies that software interrupts (i.e.
> >>> +      IPIs) using IMSIC software injected MSIs is slower compared to other
> >>> +      software interrupt mechanisms (such as SBI IPI) on the underlying
> >>> +      RISC-V platform.
> >>
> >> Is this a property of software or hardware?
> >
> > This is a property of hardware (or implementation) because IPIs
> > in IMSIC are software injected MSIs so if IMSIC is trap-n-emulated
> > by a hypervisor then all writes to MSI register will trap to hypervisor
> > in which case IPI injection via IMSIC is slow.
> >
> > The presence of "riscv,slow-ipi" DT property provides a hint to
> > driver that using IPIs through IMSIC is slow on this platform so
> > if there are other IPI mechanisms (such as SBI IPI calls) then
> > OS should prefer those mechanisms.
>
> If this is specific to implementation, why it is not included already in
> the compatible?
>
> The name is anyway too vague. What is "slow"? Describe real
> characteristics of hardware, e.g. trapped via hypervisor.

Okay, how about renaming it to "riscv,trap-n-emulated" ?

Alternately, we can add "riscv,soft-imsics" as an implementation
specific compatible string which hypervisors can use to describe
trap-n-emulated IMSICs. This "riscv,soft-imsics" can also replace
"vendor,chip-imsics" dummy string ?


> >
> >>
> >>> +
> >>> +  riscv,guest-index-bits:
> >>> +    minimum: 0
> >>> +    maximum: 7
> >>> +    description:
> >>> +      Specifies number of guest index bits in the MSI target address. When
> >>> +      not specified it is assumed to be 0.
> >>> +
> >>> +  riscv,hart-index-bits:
> >>> +    minimum: 0
> >>> +    maximum: 15
> >>> +    description:
> >>> +      Specifies number of HART index bits in the MSI target address. When
> >>> +      not specified it is estimated based on the interrupts-extended property.
> >>> +
> >>> +  riscv,group-index-bits:
> >>> +    minimum: 0
> >>> +    maximum: 7
> >>> +    description:
> >>> +      Specifies number of group index bits in the MSI target address. When
> >>> +      not specified it is assumed to be 0.
> >>
> >> Then default: 0.
> >
> > Okay, I will update.
> >
> >>
> >>> +
> >>> +  riscv,group-index-shift:
> >>> +    $ref: /schemas/types.yaml#/definitions/uint32
> >>> +    minimum: 24
> >>> +    maximum: 55
> >>> +    description:
> >>> +      Specifies the least significant bit of the group index bits in the
> >>
> >> Please drop everywhere "Specifies the" and instead just describe the
> >> hardware.
> >
> > Okay, I will update.
> >
> >>
> >>> +      MSI target address. When not specified it is assumed to be 24.
> >>> +
> >>> +additionalProperties: false
> >>
> >> unevaluatedProperties: false and drop all properties already described
> >> by other schemas.
> >
> > Okay, I will update.
> >
> >>
> >>> +
> >>> +required:
> >>> +  - compatible
> >>> +  - reg
> >>> +  - interrupt-controller
> >>> +  - msi-controller
> >>> +  - interrupts-extended
> >>> +  - riscv,num-ids
> >>> +
> >>> +examples:
> >>> +  - |
> >>> +    // Example 1 (Machine-level IMSIC files with just one group):
> >>> +
> >>> +    imsic_mlevel: interrupt-controller@24000000 {
> >>> +      compatible = "vendor,chip-imsics", "riscv,imsics";
> >>> +      interrupts-extended = <&cpu1_intc 11>,
> >>> +                            <&cpu2_intc 11>,
> >>> +                            <&cpu3_intc 11>,
> >>> +                            <&cpu4_intc 11>;
> >>> +      reg = <0x28000000 0x4000>;
> >>> +      interrupt-controller;
> >>> +      #interrupt-cells = <0>;
> >>> +      msi-controller;
> >>> +      riscv,num-ids = <127>;
> >>> +    };
> >>> +
> >>> +  - |
> >>> +    // Example 2 (Supervisor-level IMSIC files with two groups):
> >>> +
> >>> +    imsic_slevel: interrupt-controller@28000000 {
> >>> +      compatible = "vendor,chip-imsics", "riscv,imsics";
> >>
> >> Please run scripts/checkpatch.pl and fix reported warnings.
> >
> > I did not see any warnings with ./scripts/checkpatch.pl.
> >
> > Is there any parameter of checkpatch.pl which I should try ?
>
> You should see here or with your DTS warnings about undocumented vendor
> prefix.

Okay, I will check.

>
> Best regards,
> Krzysztof
>

Best Regards,
Anup

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
  2022-11-14 15:04           ` Anup Patel
@ 2022-11-15 14:15             ` Krzysztof Kozlowski
  -1 siblings, 0 replies; 96+ messages in thread
From: Krzysztof Kozlowski @ 2022-11-15 14:15 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

On 14/11/2022 16:04, Anup Patel wrote:
> On Mon, Nov 14, 2022 at 5:52 PM Krzysztof Kozlowski

>>>>> +  riscv,slow-ipi:
>>>>> +    type: boolean
>>>>> +    description:
>>>>> +      The presence of this property implies that software interrupts (i.e.
>>>>> +      IPIs) using IMSIC software injected MSIs is slower compared to other
>>>>> +      software interrupt mechanisms (such as SBI IPI) on the underlying
>>>>> +      RISC-V platform.
>>>>
>>>> Is this a property of software or hardware?
>>>
>>> This is a property of hardware (or implementation) because IPIs
>>> in IMSIC are software injected MSIs so if IMSIC is trap-n-emulated
>>> by a hypervisor then all writes to MSI register will trap to hypervisor
>>> in which case IPI injection via IMSIC is slow.
>>>
>>> The presence of "riscv,slow-ipi" DT property provides a hint to
>>> driver that using IPIs through IMSIC is slow on this platform so
>>> if there are other IPI mechanisms (such as SBI IPI calls) then
>>> OS should prefer those mechanisms.
>>
>> If this is specific to implementation, why it is not included already in
>> the compatible?
>>
>> The name is anyway too vague. What is "slow"? Describe real
>> characteristics of hardware, e.g. trapped via hypervisor.
> 
> Okay, how about renaming it to "riscv,trap-n-emulated" ?

Sounds ok.

> 
> Alternately, we can add "riscv,soft-imsics" as an implementation
> specific compatible string which hypervisors can use to describe
> trap-n-emulated IMSICs. This "riscv,soft-imsics" can also replace
> "vendor,chip-imsics" dummy string ?

soft-imsics would work only if it is a real device. My question was
rather whether this is something configurable or fixed in given
implementation.

Best regards,
Krzysztof


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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
@ 2022-11-15 14:15             ` Krzysztof Kozlowski
  0 siblings, 0 replies; 96+ messages in thread
From: Krzysztof Kozlowski @ 2022-11-15 14:15 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

On 14/11/2022 16:04, Anup Patel wrote:
> On Mon, Nov 14, 2022 at 5:52 PM Krzysztof Kozlowski

>>>>> +  riscv,slow-ipi:
>>>>> +    type: boolean
>>>>> +    description:
>>>>> +      The presence of this property implies that software interrupts (i.e.
>>>>> +      IPIs) using IMSIC software injected MSIs is slower compared to other
>>>>> +      software interrupt mechanisms (such as SBI IPI) on the underlying
>>>>> +      RISC-V platform.
>>>>
>>>> Is this a property of software or hardware?
>>>
>>> This is a property of hardware (or implementation) because IPIs
>>> in IMSIC are software injected MSIs so if IMSIC is trap-n-emulated
>>> by a hypervisor then all writes to MSI register will trap to hypervisor
>>> in which case IPI injection via IMSIC is slow.
>>>
>>> The presence of "riscv,slow-ipi" DT property provides a hint to
>>> driver that using IPIs through IMSIC is slow on this platform so
>>> if there are other IPI mechanisms (such as SBI IPI calls) then
>>> OS should prefer those mechanisms.
>>
>> If this is specific to implementation, why it is not included already in
>> the compatible?
>>
>> The name is anyway too vague. What is "slow"? Describe real
>> characteristics of hardware, e.g. trapped via hypervisor.
> 
> Okay, how about renaming it to "riscv,trap-n-emulated" ?

Sounds ok.

> 
> Alternately, we can add "riscv,soft-imsics" as an implementation
> specific compatible string which hypervisors can use to describe
> trap-n-emulated IMSICs. This "riscv,soft-imsics" can also replace
> "vendor,chip-imsics" dummy string ?

soft-imsics would work only if it is a real device. My question was
rather whether this is something configurable or fixed in given
implementation.

Best regards,
Krzysztof


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 8/9] RISC-V: Select APLIC and IMSIC drivers for QEMU virt machine
  2022-11-11  4:42   ` Anup Patel
@ 2022-11-15 22:29     ` Conor Dooley
  -1 siblings, 0 replies; 96+ messages in thread
From: Conor Dooley @ 2022-11-15 22:29 UTC (permalink / raw)
  To: Anup Patel, palmer
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

Hey Anup,

On Fri, Nov 11, 2022 at 10:12:06AM +0530, Anup Patel wrote:
> The QEMU virt machine supports APLIC and IMSIC emulation so let's
> select APLIC and IMSIC drivers from SOC_VIRT kconfig option.

I'm kinda torn with this as I've been trying to get rid of the selects
from the file in the first place. As Maz seems to have decided that the
SiFive plic driver is really the RISC-V plic driver - is there a reason
not to either:
- select the sifive plic at an arch level, or
- put a "default RISCV" type thing in the driver entry?

Similarly here, should we default the APLIC and IMSIC drivers to enabled
for RISCV rather than using selects?

> 
> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> ---
>  arch/riscv/Kconfig.socs | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs
> index 69774bb362d6..c16b32eeadff 100644
> --- a/arch/riscv/Kconfig.socs
> +++ b/arch/riscv/Kconfig.socs
> @@ -35,6 +35,8 @@ config SOC_VIRT
>  	select GOLDFISH
>  	select RTC_DRV_GOLDFISH if RTC_CLASS
>  	select SIFIVE_PLIC
> +	select RISCV_APLIC
> +	select RISCV_IMSIC
>  	select PM_GENERIC_DOMAINS if PM
>  	select PM_GENERIC_DOMAINS_OF if PM && OF
>  	select RISCV_SBI_CPUIDLE if CPU_IDLE && RISCV_SBI
> -- 
> 2.34.1
> 
> 
> _______________________________________________
> linux-riscv mailing list
> linux-riscv@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 8/9] RISC-V: Select APLIC and IMSIC drivers for QEMU virt machine
@ 2022-11-15 22:29     ` Conor Dooley
  0 siblings, 0 replies; 96+ messages in thread
From: Conor Dooley @ 2022-11-15 22:29 UTC (permalink / raw)
  To: Anup Patel, palmer
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

Hey Anup,

On Fri, Nov 11, 2022 at 10:12:06AM +0530, Anup Patel wrote:
> The QEMU virt machine supports APLIC and IMSIC emulation so let's
> select APLIC and IMSIC drivers from SOC_VIRT kconfig option.

I'm kinda torn with this as I've been trying to get rid of the selects
from the file in the first place. As Maz seems to have decided that the
SiFive plic driver is really the RISC-V plic driver - is there a reason
not to either:
- select the sifive plic at an arch level, or
- put a "default RISCV" type thing in the driver entry?

Similarly here, should we default the APLIC and IMSIC drivers to enabled
for RISCV rather than using selects?

> 
> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> ---
>  arch/riscv/Kconfig.socs | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs
> index 69774bb362d6..c16b32eeadff 100644
> --- a/arch/riscv/Kconfig.socs
> +++ b/arch/riscv/Kconfig.socs
> @@ -35,6 +35,8 @@ config SOC_VIRT
>  	select GOLDFISH
>  	select RTC_DRV_GOLDFISH if RTC_CLASS
>  	select SIFIVE_PLIC
> +	select RISCV_APLIC
> +	select RISCV_IMSIC
>  	select PM_GENERIC_DOMAINS if PM
>  	select PM_GENERIC_DOMAINS_OF if PM && OF
>  	select RISCV_SBI_CPUIDLE if CPU_IDLE && RISCV_SBI
> -- 
> 2.34.1
> 
> 
> _______________________________________________
> linux-riscv mailing list
> linux-riscv@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-riscv

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
  2022-11-14 12:29       ` Anup Patel
@ 2022-11-15 22:34         ` Conor Dooley
  -1 siblings, 0 replies; 96+ messages in thread
From: Conor Dooley @ 2022-11-15 22:34 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

On Mon, Nov 14, 2022 at 05:59:00PM +0530, Anup Patel wrote:
> On Sun, Nov 13, 2022 at 8:18 PM Conor Dooley <conor@kernel.org> wrote:

> > Also, the file name says "riscv,imsic", the description says "IMSIC" but
> > you've used "imsics" in the compatible. Is this a typo, or a plural?
> 
> Yes, the file name should be consistent. I will update the file name.

Is there a reason why the compatible is plural when all of the other
mentions etc do not have an "s"? It really did look like a typo to me.

It's the "incoming MSI controller", so I am unsure as to where the "s"
actually even comes from. Why not just use "riscv,imsic"?

Thanks,
Conor.


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
@ 2022-11-15 22:34         ` Conor Dooley
  0 siblings, 0 replies; 96+ messages in thread
From: Conor Dooley @ 2022-11-15 22:34 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

On Mon, Nov 14, 2022 at 05:59:00PM +0530, Anup Patel wrote:
> On Sun, Nov 13, 2022 at 8:18 PM Conor Dooley <conor@kernel.org> wrote:

> > Also, the file name says "riscv,imsic", the description says "IMSIC" but
> > you've used "imsics" in the compatible. Is this a typo, or a plural?
> 
> Yes, the file name should be consistent. I will update the file name.

Is there a reason why the compatible is plural when all of the other
mentions etc do not have an "s"? It really did look like a typo to me.

It's the "incoming MSI controller", so I am unsure as to where the "s"
actually even comes from. Why not just use "riscv,imsic"?

Thanks,
Conor.


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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
  2022-11-15 22:34         ` Conor Dooley
@ 2022-11-16  9:00           ` Krzysztof Kozlowski
  -1 siblings, 0 replies; 96+ messages in thread
From: Krzysztof Kozlowski @ 2022-11-16  9:00 UTC (permalink / raw)
  To: Conor Dooley, Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

On 15/11/2022 23:34, Conor Dooley wrote:
> On Mon, Nov 14, 2022 at 05:59:00PM +0530, Anup Patel wrote:
>> On Sun, Nov 13, 2022 at 8:18 PM Conor Dooley <conor@kernel.org> wrote:
> 
>>> Also, the file name says "riscv,imsic", the description says "IMSIC" but
>>> you've used "imsics" in the compatible. Is this a typo, or a plural?
>>
>> Yes, the file name should be consistent. I will update the file name.
> 
> Is there a reason why the compatible is plural when all of the other
> mentions etc do not have an "s"? It really did look like a typo to me.
> 
> It's the "incoming MSI controller", so I am unsure as to where the "s"
> actually even comes from. Why not just use "riscv,imsic"?

Yep, should be rather consistent with all others, and IMSIC stands for
Integrated Circuit?

Best regards,
Krzysztof


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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
@ 2022-11-16  9:00           ` Krzysztof Kozlowski
  0 siblings, 0 replies; 96+ messages in thread
From: Krzysztof Kozlowski @ 2022-11-16  9:00 UTC (permalink / raw)
  To: Conor Dooley, Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Rob Herring, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	Anup Patel, linux-riscv, linux-kernel, devicetree

On 15/11/2022 23:34, Conor Dooley wrote:
> On Mon, Nov 14, 2022 at 05:59:00PM +0530, Anup Patel wrote:
>> On Sun, Nov 13, 2022 at 8:18 PM Conor Dooley <conor@kernel.org> wrote:
> 
>>> Also, the file name says "riscv,imsic", the description says "IMSIC" but
>>> you've used "imsics" in the compatible. Is this a typo, or a plural?
>>
>> Yes, the file name should be consistent. I will update the file name.
> 
> Is there a reason why the compatible is plural when all of the other
> mentions etc do not have an "s"? It really did look like a typo to me.
> 
> It's the "incoming MSI controller", so I am unsure as to where the "s"
> actually even comes from. Why not just use "riscv,imsic"?

Yep, should be rather consistent with all others, and IMSIC stands for
Integrated Circuit?

Best regards,
Krzysztof


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
  2022-11-16  9:00           ` Krzysztof Kozlowski
@ 2022-11-16  9:20             ` Conor Dooley
  -1 siblings, 0 replies; 96+ messages in thread
From: Conor Dooley @ 2022-11-16  9:20 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Conor Dooley, Anup Patel, Palmer Dabbelt, Paul Walmsley,
	Thomas Gleixner, Marc Zyngier, Rob Herring, Krzysztof Kozlowski,
	Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree

On Wed, Nov 16, 2022 at 10:00:27AM +0100, Krzysztof Kozlowski wrote:
> On 15/11/2022 23:34, Conor Dooley wrote:
> > On Mon, Nov 14, 2022 at 05:59:00PM +0530, Anup Patel wrote:
> >> On Sun, Nov 13, 2022 at 8:18 PM Conor Dooley <conor@kernel.org> wrote:
> > 
> >>> Also, the file name says "riscv,imsic", the description says "IMSIC" but
> >>> you've used "imsics" in the compatible. Is this a typo, or a plural?
> >>
> >> Yes, the file name should be consistent. I will update the file name.
> > 
> > Is there a reason why the compatible is plural when all of the other
> > mentions etc do not have an "s"? It really did look like a typo to me.
> > 
> > It's the "incoming MSI controller", so I am unsure as to where the "s"
> > actually even comes from. Why not just use "riscv,imsic"?
> 
> Yep, should be rather consistent with all others, and IMSIC stands for
> Integrated Circuit?

Incoming Message Signalled Interrupts Controller, no?

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
@ 2022-11-16  9:20             ` Conor Dooley
  0 siblings, 0 replies; 96+ messages in thread
From: Conor Dooley @ 2022-11-16  9:20 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Conor Dooley, Anup Patel, Palmer Dabbelt, Paul Walmsley,
	Thomas Gleixner, Marc Zyngier, Rob Herring, Krzysztof Kozlowski,
	Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree

On Wed, Nov 16, 2022 at 10:00:27AM +0100, Krzysztof Kozlowski wrote:
> On 15/11/2022 23:34, Conor Dooley wrote:
> > On Mon, Nov 14, 2022 at 05:59:00PM +0530, Anup Patel wrote:
> >> On Sun, Nov 13, 2022 at 8:18 PM Conor Dooley <conor@kernel.org> wrote:
> > 
> >>> Also, the file name says "riscv,imsic", the description says "IMSIC" but
> >>> you've used "imsics" in the compatible. Is this a typo, or a plural?
> >>
> >> Yes, the file name should be consistent. I will update the file name.
> > 
> > Is there a reason why the compatible is plural when all of the other
> > mentions etc do not have an "s"? It really did look like a typo to me.
> > 
> > It's the "incoming MSI controller", so I am unsure as to where the "s"
> > actually even comes from. Why not just use "riscv,imsic"?
> 
> Yep, should be rather consistent with all others, and IMSIC stands for
> Integrated Circuit?

Incoming Message Signalled Interrupts Controller, no?

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
  2022-11-16  9:20             ` Conor Dooley
@ 2022-11-16  9:21               ` Krzysztof Kozlowski
  -1 siblings, 0 replies; 96+ messages in thread
From: Krzysztof Kozlowski @ 2022-11-16  9:21 UTC (permalink / raw)
  To: Conor Dooley
  Cc: Conor Dooley, Anup Patel, Palmer Dabbelt, Paul Walmsley,
	Thomas Gleixner, Marc Zyngier, Rob Herring, Krzysztof Kozlowski,
	Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree

On 16/11/2022 10:20, Conor Dooley wrote:
> On Wed, Nov 16, 2022 at 10:00:27AM +0100, Krzysztof Kozlowski wrote:
>> On 15/11/2022 23:34, Conor Dooley wrote:
>>> On Mon, Nov 14, 2022 at 05:59:00PM +0530, Anup Patel wrote:
>>>> On Sun, Nov 13, 2022 at 8:18 PM Conor Dooley <conor@kernel.org> wrote:
>>>
>>>>> Also, the file name says "riscv,imsic", the description says "IMSIC" but
>>>>> you've used "imsics" in the compatible. Is this a typo, or a plural?
>>>>
>>>> Yes, the file name should be consistent. I will update the file name.
>>>
>>> Is there a reason why the compatible is plural when all of the other
>>> mentions etc do not have an "s"? It really did look like a typo to me.
>>>
>>> It's the "incoming MSI controller", so I am unsure as to where the "s"
>>> actually even comes from. Why not just use "riscv,imsic"?
>>
>> Yep, should be rather consistent with all others, and IMSIC stands for
>> Integrated Circuit?
> 
> Incoming Message Signalled Interrupts Controller, no?

Ah, then still singular :)

Best regards,
Krzysztof


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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
@ 2022-11-16  9:21               ` Krzysztof Kozlowski
  0 siblings, 0 replies; 96+ messages in thread
From: Krzysztof Kozlowski @ 2022-11-16  9:21 UTC (permalink / raw)
  To: Conor Dooley
  Cc: Conor Dooley, Anup Patel, Palmer Dabbelt, Paul Walmsley,
	Thomas Gleixner, Marc Zyngier, Rob Herring, Krzysztof Kozlowski,
	Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree

On 16/11/2022 10:20, Conor Dooley wrote:
> On Wed, Nov 16, 2022 at 10:00:27AM +0100, Krzysztof Kozlowski wrote:
>> On 15/11/2022 23:34, Conor Dooley wrote:
>>> On Mon, Nov 14, 2022 at 05:59:00PM +0530, Anup Patel wrote:
>>>> On Sun, Nov 13, 2022 at 8:18 PM Conor Dooley <conor@kernel.org> wrote:
>>>
>>>>> Also, the file name says "riscv,imsic", the description says "IMSIC" but
>>>>> you've used "imsics" in the compatible. Is this a typo, or a plural?
>>>>
>>>> Yes, the file name should be consistent. I will update the file name.
>>>
>>> Is there a reason why the compatible is plural when all of the other
>>> mentions etc do not have an "s"? It really did look like a typo to me.
>>>
>>> It's the "incoming MSI controller", so I am unsure as to where the "s"
>>> actually even comes from. Why not just use "riscv,imsic"?
>>
>> Yep, should be rather consistent with all others, and IMSIC stands for
>> Integrated Circuit?
> 
> Incoming Message Signalled Interrupts Controller, no?

Ah, then still singular :)

Best regards,
Krzysztof


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
  2022-11-16  9:00           ` Krzysztof Kozlowski
@ 2022-11-16 10:34             ` Anup Patel
  -1 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-16 10:34 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Conor Dooley, Palmer Dabbelt, Paul Walmsley, Thomas Gleixner,
	Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Atish Patra,
	Alistair Francis, Anup Patel, linux-riscv, linux-kernel,
	devicetree

On Wed, Nov 16, 2022 at 2:30 PM Krzysztof Kozlowski
<krzysztof.kozlowski@linaro.org> wrote:
>
> On 15/11/2022 23:34, Conor Dooley wrote:
> > On Mon, Nov 14, 2022 at 05:59:00PM +0530, Anup Patel wrote:
> >> On Sun, Nov 13, 2022 at 8:18 PM Conor Dooley <conor@kernel.org> wrote:
> >
> >>> Also, the file name says "riscv,imsic", the description says "IMSIC" but
> >>> you've used "imsics" in the compatible. Is this a typo, or a plural?
> >>
> >> Yes, the file name should be consistent. I will update the file name.
> >
> > Is there a reason why the compatible is plural when all of the other
> > mentions etc do not have an "s"? It really did look like a typo to me.
> >
> > It's the "incoming MSI controller", so I am unsure as to where the "s"
> > actually even comes from. Why not just use "riscv,imsic"?
>
> Yep, should be rather consistent with all others, and IMSIC stands for
> Integrated Circuit?

This is intentionally plural because even though we have one
IMSIC per-CPU, Linux (and various OSes) expect one DT node
as MSI controller targeting all CPUs.

The plural compatible string "riscv,imsics" was chosen based
on consensus on RISC-V AIA Task Group meetings.

Regards,
Anup

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
@ 2022-11-16 10:34             ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2022-11-16 10:34 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Conor Dooley, Palmer Dabbelt, Paul Walmsley, Thomas Gleixner,
	Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Atish Patra,
	Alistair Francis, Anup Patel, linux-riscv, linux-kernel,
	devicetree

On Wed, Nov 16, 2022 at 2:30 PM Krzysztof Kozlowski
<krzysztof.kozlowski@linaro.org> wrote:
>
> On 15/11/2022 23:34, Conor Dooley wrote:
> > On Mon, Nov 14, 2022 at 05:59:00PM +0530, Anup Patel wrote:
> >> On Sun, Nov 13, 2022 at 8:18 PM Conor Dooley <conor@kernel.org> wrote:
> >
> >>> Also, the file name says "riscv,imsic", the description says "IMSIC" but
> >>> you've used "imsics" in the compatible. Is this a typo, or a plural?
> >>
> >> Yes, the file name should be consistent. I will update the file name.
> >
> > Is there a reason why the compatible is plural when all of the other
> > mentions etc do not have an "s"? It really did look like a typo to me.
> >
> > It's the "incoming MSI controller", so I am unsure as to where the "s"
> > actually even comes from. Why not just use "riscv,imsic"?
>
> Yep, should be rather consistent with all others, and IMSIC stands for
> Integrated Circuit?

This is intentionally plural because even though we have one
IMSIC per-CPU, Linux (and various OSes) expect one DT node
as MSI controller targeting all CPUs.

The plural compatible string "riscv,imsics" was chosen based
on consensus on RISC-V AIA Task Group meetings.

Regards,
Anup

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
  2022-11-16 10:34             ` Anup Patel
@ 2022-11-16 13:29               ` Conor Dooley
  -1 siblings, 0 replies; 96+ messages in thread
From: Conor Dooley @ 2022-11-16 13:29 UTC (permalink / raw)
  To: Anup Patel
  Cc: Krzysztof Kozlowski, Conor Dooley, Palmer Dabbelt, Paul Walmsley,
	Thomas Gleixner, Marc Zyngier, Rob Herring, Krzysztof Kozlowski,
	Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree

On Wed, Nov 16, 2022 at 04:04:45PM +0530, Anup Patel wrote:
> On Wed, Nov 16, 2022 at 2:30 PM Krzysztof Kozlowski
> <krzysztof.kozlowski@linaro.org> wrote:
> >
> > On 15/11/2022 23:34, Conor Dooley wrote:
> > > On Mon, Nov 14, 2022 at 05:59:00PM +0530, Anup Patel wrote:
> > >> On Sun, Nov 13, 2022 at 8:18 PM Conor Dooley <conor@kernel.org> wrote:
> > >
> > >>> Also, the file name says "riscv,imsic", the description says "IMSIC" but
> > >>> you've used "imsics" in the compatible. Is this a typo, or a plural?
> > >>
> > >> Yes, the file name should be consistent. I will update the file name.
> > >
> > > Is there a reason why the compatible is plural when all of the other
> > > mentions etc do not have an "s"? It really did look like a typo to me.
> > >
> > > It's the "incoming MSI controller", so I am unsure as to where the "s"
> > > actually even comes from. Why not just use "riscv,imsic"?
> >
> > Yep, should be rather consistent with all others, and IMSIC stands for
> > Integrated Circuit?
> 
> This is intentionally plural because even though we have one
> IMSIC per-CPU, Linux (and various OSes) expect one DT node
> as MSI controller targeting all CPUs.

Even still, calling it "riscv,imsic" would seem fair to me given the
multiple regs make the distinct regions clear.

I think I must have missed the bit at the end of the description though:

+  The device tree of a RISC-V platform will have one IMSIC device tree node
+  for each privilege level (machine or supervisor) which collectively describe
+  IMSIC interrupt files at that privilege level across CPUs (or HARTs).

Perhaps, for eejits like me, that paragraph should become paragraph 3
instead of hiding it below the register layout etc?

Anyways, existing name seems fine to me then w/ the filename update &
increased prominence of the many-controllers-in-one statement.

Maybe the devicetree gods think differently!

> The plural compatible string "riscv,imsics" was chosen based
> on consensus on RISC-V AIA Task Group meetings.

btw, I see the following in the example:

+      reg = <0x28000000 0x2000>, /* Group0 IMSICs */
+            <0x29000000 0x2000>; /* Group1 IMSICs */

And in the property:
+  reg:
+    minItems: 1
+    maxItems: 128
+    description:
+      Base address of each IMSIC group.

It would appear that the comment there conflicts with the description of
the reg property itself & it's that lack of consistency me confused (:
Should the comments be in the singular form?

Thanks,
Conor.


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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
@ 2022-11-16 13:29               ` Conor Dooley
  0 siblings, 0 replies; 96+ messages in thread
From: Conor Dooley @ 2022-11-16 13:29 UTC (permalink / raw)
  To: Anup Patel
  Cc: Krzysztof Kozlowski, Conor Dooley, Palmer Dabbelt, Paul Walmsley,
	Thomas Gleixner, Marc Zyngier, Rob Herring, Krzysztof Kozlowski,
	Atish Patra, Alistair Francis, Anup Patel, linux-riscv,
	linux-kernel, devicetree

On Wed, Nov 16, 2022 at 04:04:45PM +0530, Anup Patel wrote:
> On Wed, Nov 16, 2022 at 2:30 PM Krzysztof Kozlowski
> <krzysztof.kozlowski@linaro.org> wrote:
> >
> > On 15/11/2022 23:34, Conor Dooley wrote:
> > > On Mon, Nov 14, 2022 at 05:59:00PM +0530, Anup Patel wrote:
> > >> On Sun, Nov 13, 2022 at 8:18 PM Conor Dooley <conor@kernel.org> wrote:
> > >
> > >>> Also, the file name says "riscv,imsic", the description says "IMSIC" but
> > >>> you've used "imsics" in the compatible. Is this a typo, or a plural?
> > >>
> > >> Yes, the file name should be consistent. I will update the file name.
> > >
> > > Is there a reason why the compatible is plural when all of the other
> > > mentions etc do not have an "s"? It really did look like a typo to me.
> > >
> > > It's the "incoming MSI controller", so I am unsure as to where the "s"
> > > actually even comes from. Why not just use "riscv,imsic"?
> >
> > Yep, should be rather consistent with all others, and IMSIC stands for
> > Integrated Circuit?
> 
> This is intentionally plural because even though we have one
> IMSIC per-CPU, Linux (and various OSes) expect one DT node
> as MSI controller targeting all CPUs.

Even still, calling it "riscv,imsic" would seem fair to me given the
multiple regs make the distinct regions clear.

I think I must have missed the bit at the end of the description though:

+  The device tree of a RISC-V platform will have one IMSIC device tree node
+  for each privilege level (machine or supervisor) which collectively describe
+  IMSIC interrupt files at that privilege level across CPUs (or HARTs).

Perhaps, for eejits like me, that paragraph should become paragraph 3
instead of hiding it below the register layout etc?

Anyways, existing name seems fine to me then w/ the filename update &
increased prominence of the many-controllers-in-one statement.

Maybe the devicetree gods think differently!

> The plural compatible string "riscv,imsics" was chosen based
> on consensus on RISC-V AIA Task Group meetings.

btw, I see the following in the example:

+      reg = <0x28000000 0x2000>, /* Group0 IMSICs */
+            <0x29000000 0x2000>; /* Group1 IMSICs */

And in the property:
+  reg:
+    minItems: 1
+    maxItems: 128
+    description:
+      Base address of each IMSIC group.

It would appear that the comment there conflicts with the description of
the reg property itself & it's that lack of consistency me confused (:
Should the comments be in the singular form?

Thanks,
Conor.


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
  2022-11-14 12:06       ` Anup Patel
@ 2022-11-16 19:14         ` Rob Herring
  -1 siblings, 0 replies; 96+ messages in thread
From: Rob Herring @ 2022-11-16 19:14 UTC (permalink / raw)
  To: Anup Patel
  Cc: Krzysztof Kozlowski, Palmer Dabbelt, Paul Walmsley,
	Thomas Gleixner, Marc Zyngier, Krzysztof Kozlowski, Atish Patra,
	Alistair Francis, Anup Patel, linux-riscv, linux-kernel,
	devicetree

On Mon, Nov 14, 2022 at 05:36:06PM +0530, Anup Patel wrote:
> On Mon, Nov 14, 2022 at 3:19 PM Krzysztof Kozlowski
> <krzysztof.kozlowski@linaro.org> wrote:
> >
> > On 11/11/2022 05:42, Anup Patel wrote:
> > > We add DT bindings document for RISC-V incoming MSI controller (IMSIC)
> > > defined by the RISC-V advanced interrupt architecture (AIA) specification.
> > >
> > > Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> > > ---
> > >  .../interrupt-controller/riscv,imsic.yaml     | 174 ++++++++++++++++++
> > >  1 file changed, 174 insertions(+)
> > >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > >
> > > diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > > new file mode 100644
> > > index 000000000000..05106eb1955e
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > > @@ -0,0 +1,174 @@
> > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > +%YAML 1.2
> > > +---
> > > +$id: http://devicetree.org/schemas/interrupt-controller/riscv,imsic.yaml#
> > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > +
> > > +title: RISC-V Incoming MSI Controller (IMSIC)
> > > +
> > > +maintainers:
> > > +  - Anup Patel <anup@brainfault.org>
> > > +
> > > +description:
> > > +  The RISC-V advanced interrupt architecture (AIA) defines a per-CPU incoming
> > > +  MSI controller (IMSIC) for handling MSIs in a RISC-V platform. The RISC-V
> > > +  AIA specification can be found at https://github.com/riscv/riscv-aia.
> > > +
> > > +  The IMSIC is a per-CPU (or per-HART) device with separate interrupt file
> > > +  for each privilege level (machine or supervisor). The configuration of
> > > +  a IMSIC interrupt file is done using AIA CSRs and it also has a 4KB MMIO
> > > +  space to receive MSIs from devices. Each IMSIC interrupt file supports a
> > > +  fixed number of interrupt identities (to distinguish MSIs from devices)
> > > +  which is same for given privilege level across CPUs (or HARTs).
> > > +
> > > +  The arrangement of IMSIC interrupt files in MMIO space of a RISC-V platform
> > > +  follows a particular scheme defined by the RISC-V AIA specification. A IMSIC
> > > +  group is a set of IMSIC interrupt files co-located in MMIO space and we can
> > > +  have multiple IMSIC groups (i.e. clusters, sockets, chiplets, etc) in a
> > > +  RISC-V platform. The MSI target address of a IMSIC interrupt file at given
> > > +  privilege level (machine or supervisor) encodes group index, HART index,
> > > +  and guest index (shown below).
> > > +
> > > +  XLEN-1           >=24                                 12    0
> > > +  |                  |                                  |     |
> > > +  -------------------------------------------------------------
> > > +  |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
> > > +  -------------------------------------------------------------
> > > +
> > > +  The device tree of a RISC-V platform will have one IMSIC device tree node
> > > +  for each privilege level (machine or supervisor) which collectively describe
> > > +  IMSIC interrupt files at that privilege level across CPUs (or HARTs).
> > > +
> > > +allOf:
> > > +  - $ref: /schemas/interrupt-controller.yaml#
> > > +
> > > +properties:
> > > +  compatible:
> > > +    items:
> > > +      - enum:
> > > +          - vendor,chip-imsics
> >
> > There is no such vendor... As Conor pointed out, this does not look
> > correct. Compatibles must be real and specific.
> 
> Previously, Rob had suggest to:
> 1) Mandate two compatible strings: one for implementation and
>     and second for specification
> 2) Since this is new specification with QEMU being the only
>     implementation, we add "vendor,chip-imsics" as dummy
>     implementation specific string for DT schema checkers
>     to pass the examples. Once we have an actual implementation,
>    we will replace this dummy string.

What will QEMU's DT use? That's an implementation we can and do run 
validation on. Your choices are define a QEMU specific compatible string 
or allow the fallback alone. I'm fine either way. With the latter, 
someone has to review that the fallback is not used alone in .dts files 
while doing the former allows the tools to check for you. It also 
encourages making every new difference a property rather than implied by 
compatible, but those should be caught in review.

If you go with the fallback only, just make it clear that it's for QEMU 
or s/w models only.

Rob

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
@ 2022-11-16 19:14         ` Rob Herring
  0 siblings, 0 replies; 96+ messages in thread
From: Rob Herring @ 2022-11-16 19:14 UTC (permalink / raw)
  To: Anup Patel
  Cc: Krzysztof Kozlowski, Palmer Dabbelt, Paul Walmsley,
	Thomas Gleixner, Marc Zyngier, Krzysztof Kozlowski, Atish Patra,
	Alistair Francis, Anup Patel, linux-riscv, linux-kernel,
	devicetree

On Mon, Nov 14, 2022 at 05:36:06PM +0530, Anup Patel wrote:
> On Mon, Nov 14, 2022 at 3:19 PM Krzysztof Kozlowski
> <krzysztof.kozlowski@linaro.org> wrote:
> >
> > On 11/11/2022 05:42, Anup Patel wrote:
> > > We add DT bindings document for RISC-V incoming MSI controller (IMSIC)
> > > defined by the RISC-V advanced interrupt architecture (AIA) specification.
> > >
> > > Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> > > ---
> > >  .../interrupt-controller/riscv,imsic.yaml     | 174 ++++++++++++++++++
> > >  1 file changed, 174 insertions(+)
> > >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > >
> > > diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > > new file mode 100644
> > > index 000000000000..05106eb1955e
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > > @@ -0,0 +1,174 @@
> > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > +%YAML 1.2
> > > +---
> > > +$id: http://devicetree.org/schemas/interrupt-controller/riscv,imsic.yaml#
> > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > +
> > > +title: RISC-V Incoming MSI Controller (IMSIC)
> > > +
> > > +maintainers:
> > > +  - Anup Patel <anup@brainfault.org>
> > > +
> > > +description:
> > > +  The RISC-V advanced interrupt architecture (AIA) defines a per-CPU incoming
> > > +  MSI controller (IMSIC) for handling MSIs in a RISC-V platform. The RISC-V
> > > +  AIA specification can be found at https://github.com/riscv/riscv-aia.
> > > +
> > > +  The IMSIC is a per-CPU (or per-HART) device with separate interrupt file
> > > +  for each privilege level (machine or supervisor). The configuration of
> > > +  a IMSIC interrupt file is done using AIA CSRs and it also has a 4KB MMIO
> > > +  space to receive MSIs from devices. Each IMSIC interrupt file supports a
> > > +  fixed number of interrupt identities (to distinguish MSIs from devices)
> > > +  which is same for given privilege level across CPUs (or HARTs).
> > > +
> > > +  The arrangement of IMSIC interrupt files in MMIO space of a RISC-V platform
> > > +  follows a particular scheme defined by the RISC-V AIA specification. A IMSIC
> > > +  group is a set of IMSIC interrupt files co-located in MMIO space and we can
> > > +  have multiple IMSIC groups (i.e. clusters, sockets, chiplets, etc) in a
> > > +  RISC-V platform. The MSI target address of a IMSIC interrupt file at given
> > > +  privilege level (machine or supervisor) encodes group index, HART index,
> > > +  and guest index (shown below).
> > > +
> > > +  XLEN-1           >=24                                 12    0
> > > +  |                  |                                  |     |
> > > +  -------------------------------------------------------------
> > > +  |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
> > > +  -------------------------------------------------------------
> > > +
> > > +  The device tree of a RISC-V platform will have one IMSIC device tree node
> > > +  for each privilege level (machine or supervisor) which collectively describe
> > > +  IMSIC interrupt files at that privilege level across CPUs (or HARTs).
> > > +
> > > +allOf:
> > > +  - $ref: /schemas/interrupt-controller.yaml#
> > > +
> > > +properties:
> > > +  compatible:
> > > +    items:
> > > +      - enum:
> > > +          - vendor,chip-imsics
> >
> > There is no such vendor... As Conor pointed out, this does not look
> > correct. Compatibles must be real and specific.
> 
> Previously, Rob had suggest to:
> 1) Mandate two compatible strings: one for implementation and
>     and second for specification
> 2) Since this is new specification with QEMU being the only
>     implementation, we add "vendor,chip-imsics" as dummy
>     implementation specific string for DT schema checkers
>     to pass the examples. Once we have an actual implementation,
>    we will replace this dummy string.

What will QEMU's DT use? That's an implementation we can and do run 
validation on. Your choices are define a QEMU specific compatible string 
or allow the fallback alone. I'm fine either way. With the latter, 
someone has to review that the fallback is not used alone in .dts files 
while doing the former allows the tools to check for you. It also 
encourages making every new difference a property rather than implied by 
compatible, but those should be caught in review.

If you go with the fallback only, just make it clear that it's for QEMU 
or s/w models only.

Rob

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 6/9] dt-bindings: Add RISC-V advanced PLIC bindings
  2022-11-11  4:42   ` Anup Patel
@ 2022-11-16 19:27     ` Rob Herring
  -1 siblings, 0 replies; 96+ messages in thread
From: Rob Herring @ 2022-11-16 19:27 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Krzysztof Kozlowski, Atish Patra, Alistair Francis, Anup Patel,
	linux-riscv, linux-kernel, devicetree

On Fri, Nov 11, 2022 at 10:12:04AM +0530, Anup Patel wrote:
> We add DT bindings document for RISC-V advanced platform level interrupt
> controller (APLIC) defined by the RISC-V advanced interrupt architecture
> (AIA) specification.
> 
> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> ---
>  .../interrupt-controller/riscv,aplic.yaml     | 136 ++++++++++++++++++
>  1 file changed, 136 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> 
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> new file mode 100644
> index 000000000000..0aa48571f3bc
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> @@ -0,0 +1,136 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/interrupt-controller/riscv,aplic.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: RISC-V Advancded Platform Level Interrupt Controller (APLIC)
> +
> +maintainers:
> +  - Anup Patel <anup@brainfault.org>
> +
> +description:
> +  The RISC-V advanced interrupt architecture (AIA) defines advanced platform
> +  level interrupt controller (APLIC) for handling wired interrupts in a
> +  RISC-V platform. The RISC-V AIA specification can be found at
> +  https://github.com/riscv/riscv-aia.
> +
> +  The RISC-V APLIC is implemented as hierarchical APLIC domains where all
> +  interrupt sources connect to the root domain which can further delegate
> +  interrupts to child domains. We have one device tree node for each APLIC
> +  domain.
> +
> +allOf:
> +  - $ref: /schemas/interrupt-controller.yaml#
> +
> +properties:
> +  compatible:
> +    items:
> +      - enum:
> +          - vendor,chip-aplic
> +      - const: riscv,aplic
> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupt-controller: true
> +
> +  "#interrupt-cells":
> +    const: 2
> +
> +  interrupts-extended:
> +    minItems: 1
> +    maxItems: 16384
> +    description:
> +      The presence of this property implies that given APLIC domain directly
> +      injects external interrupts to a set of RISC-V HARTS (or CPUs). Each
> +      node pointed to should be a riscv,cpu-intc node, which has a riscv node
> +      (i.e. RISC-V HART) as parent.
> +
> +  msi-parent:
> +    description:
> +      The presence of this property implies that given APLIC domain forwards
> +      wired interrupts as MSIs to a AIA incoming message signaled interrupt
> +      controller (IMSIC). This property should be considered only when the
> +      interrupts-extended property is absent.
> +
> +  riscv,num-sources:
> +    $ref: "/schemas/types.yaml#/definitions/uint32"
> +    minimum: 1
> +    maximum: 1023
> +    description:
> +      Specifies how many wired interrupts are supported by this APLIC domain.
> +
> +  riscv,children:
> +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> +    minItems: 1
> +    maxItems: 1024

As each entry is a single phandle:

       items:
         maxItems: 1

> +    description:
> +      This property represents a list of child APLIC domains for the given
> +      APLIC domain. Each child APLIC domain is assigned child index in
> +      increasing order with the first child APLIC domain assigned child
> +      index 0. The APLIC domain child index is used by firmware to delegate
> +      interrupts from the given APLIC domain to a particular child APLIC
> +      domain.
> +
> +  riscv,delegate:
> +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> +    minItems: 1
> +    maxItems: 1024

       items:
         items:
           - description: child APLIC domain phandle
           - description: ...
           - description: ...

> +    description:
> +      This property represents a interrupt delegation list where each entry
> +      is a triple consisting of child APLIC domain phandle, first interrupt
> +      number, and last interrupt number. The firmware will configure interrupt
> +      delegation registers based on interrupt delegation list.

First and last are inclusive?

Couldn't riscv,children and riscv,delegate be combined? How would they 
be different? If some children don't have any delegated interrupts, you 
could use -1 for the cells for example.

An example showing the need would be nice.

> +
> +additionalProperties: false
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupt-controller
> +  - "#interrupt-cells"
> +  - riscv,num-sources
> +
> +examples:
> +  - |
> +    // Example 1 (APIC domain directly injecting interrupt to HARTs):

Is than an x86 APIC or a typo?

> +
> +    aplic0: interrupt-controller@c000000 {
> +      compatible = "vendor,chip-aplic", "riscv,aplic";
> +      interrupts-extended = <&cpu1_intc 11>,
> +                            <&cpu2_intc 11>,
> +                            <&cpu3_intc 11>,
> +                            <&cpu4_intc 11>;
> +      reg = <0xc000000 0x4080>;
> +      interrupt-controller;
> +      #interrupt-cells = <2>;
> +      riscv,num-sources = <63>;
> +      riscv,children = <&aplic1>;
> +      riscv,delegate = <&aplic1 1 63>;
> +    };
> +
> +    aplic1: interrupt-controller@d000000 {
> +      compatible = "vendor,chip-aplic", "riscv,aplic";
> +      interrupts-extended = <&cpu1_intc 9>,
> +                            <&cpu2_intc 9>,
> +                            <&cpu3_intc 9>,
> +                            <&cpu4_intc 9>;
> +      reg = <0xd000000 0x4080>;
> +      interrupt-controller;
> +      #interrupt-cells = <2>;
> +      riscv,num-sources = <63>;
> +    };
> +
> +  - |
> +    // Example 2 (APIC domain forwarding interrupts as MSIs):
> +
> +    interrupt-controller@d000000 {
> +      compatible = "vendor,chip-aplic", "riscv,aplic";
> +      msi-parent = <&imsics>;
> +      reg = <0xd000000 0x4000>;
> +      interrupt-controller;
> +      #interrupt-cells = <2>;
> +      riscv,num-sources = <63>;
> +    };
> +...
> -- 
> 2.34.1
> 
> 

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

* Re: [PATCH 6/9] dt-bindings: Add RISC-V advanced PLIC bindings
@ 2022-11-16 19:27     ` Rob Herring
  0 siblings, 0 replies; 96+ messages in thread
From: Rob Herring @ 2022-11-16 19:27 UTC (permalink / raw)
  To: Anup Patel
  Cc: Palmer Dabbelt, Paul Walmsley, Thomas Gleixner, Marc Zyngier,
	Krzysztof Kozlowski, Atish Patra, Alistair Francis, Anup Patel,
	linux-riscv, linux-kernel, devicetree

On Fri, Nov 11, 2022 at 10:12:04AM +0530, Anup Patel wrote:
> We add DT bindings document for RISC-V advanced platform level interrupt
> controller (APLIC) defined by the RISC-V advanced interrupt architecture
> (AIA) specification.
> 
> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> ---
>  .../interrupt-controller/riscv,aplic.yaml     | 136 ++++++++++++++++++
>  1 file changed, 136 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> 
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> new file mode 100644
> index 000000000000..0aa48571f3bc
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> @@ -0,0 +1,136 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/interrupt-controller/riscv,aplic.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: RISC-V Advancded Platform Level Interrupt Controller (APLIC)
> +
> +maintainers:
> +  - Anup Patel <anup@brainfault.org>
> +
> +description:
> +  The RISC-V advanced interrupt architecture (AIA) defines advanced platform
> +  level interrupt controller (APLIC) for handling wired interrupts in a
> +  RISC-V platform. The RISC-V AIA specification can be found at
> +  https://github.com/riscv/riscv-aia.
> +
> +  The RISC-V APLIC is implemented as hierarchical APLIC domains where all
> +  interrupt sources connect to the root domain which can further delegate
> +  interrupts to child domains. We have one device tree node for each APLIC
> +  domain.
> +
> +allOf:
> +  - $ref: /schemas/interrupt-controller.yaml#
> +
> +properties:
> +  compatible:
> +    items:
> +      - enum:
> +          - vendor,chip-aplic
> +      - const: riscv,aplic
> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupt-controller: true
> +
> +  "#interrupt-cells":
> +    const: 2
> +
> +  interrupts-extended:
> +    minItems: 1
> +    maxItems: 16384
> +    description:
> +      The presence of this property implies that given APLIC domain directly
> +      injects external interrupts to a set of RISC-V HARTS (or CPUs). Each
> +      node pointed to should be a riscv,cpu-intc node, which has a riscv node
> +      (i.e. RISC-V HART) as parent.
> +
> +  msi-parent:
> +    description:
> +      The presence of this property implies that given APLIC domain forwards
> +      wired interrupts as MSIs to a AIA incoming message signaled interrupt
> +      controller (IMSIC). This property should be considered only when the
> +      interrupts-extended property is absent.
> +
> +  riscv,num-sources:
> +    $ref: "/schemas/types.yaml#/definitions/uint32"
> +    minimum: 1
> +    maximum: 1023
> +    description:
> +      Specifies how many wired interrupts are supported by this APLIC domain.
> +
> +  riscv,children:
> +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> +    minItems: 1
> +    maxItems: 1024

As each entry is a single phandle:

       items:
         maxItems: 1

> +    description:
> +      This property represents a list of child APLIC domains for the given
> +      APLIC domain. Each child APLIC domain is assigned child index in
> +      increasing order with the first child APLIC domain assigned child
> +      index 0. The APLIC domain child index is used by firmware to delegate
> +      interrupts from the given APLIC domain to a particular child APLIC
> +      domain.
> +
> +  riscv,delegate:
> +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> +    minItems: 1
> +    maxItems: 1024

       items:
         items:
           - description: child APLIC domain phandle
           - description: ...
           - description: ...

> +    description:
> +      This property represents a interrupt delegation list where each entry
> +      is a triple consisting of child APLIC domain phandle, first interrupt
> +      number, and last interrupt number. The firmware will configure interrupt
> +      delegation registers based on interrupt delegation list.

First and last are inclusive?

Couldn't riscv,children and riscv,delegate be combined? How would they 
be different? If some children don't have any delegated interrupts, you 
could use -1 for the cells for example.

An example showing the need would be nice.

> +
> +additionalProperties: false
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupt-controller
> +  - "#interrupt-cells"
> +  - riscv,num-sources
> +
> +examples:
> +  - |
> +    // Example 1 (APIC domain directly injecting interrupt to HARTs):

Is than an x86 APIC or a typo?

> +
> +    aplic0: interrupt-controller@c000000 {
> +      compatible = "vendor,chip-aplic", "riscv,aplic";
> +      interrupts-extended = <&cpu1_intc 11>,
> +                            <&cpu2_intc 11>,
> +                            <&cpu3_intc 11>,
> +                            <&cpu4_intc 11>;
> +      reg = <0xc000000 0x4080>;
> +      interrupt-controller;
> +      #interrupt-cells = <2>;
> +      riscv,num-sources = <63>;
> +      riscv,children = <&aplic1>;
> +      riscv,delegate = <&aplic1 1 63>;
> +    };
> +
> +    aplic1: interrupt-controller@d000000 {
> +      compatible = "vendor,chip-aplic", "riscv,aplic";
> +      interrupts-extended = <&cpu1_intc 9>,
> +                            <&cpu2_intc 9>,
> +                            <&cpu3_intc 9>,
> +                            <&cpu4_intc 9>;
> +      reg = <0xd000000 0x4080>;
> +      interrupt-controller;
> +      #interrupt-cells = <2>;
> +      riscv,num-sources = <63>;
> +    };
> +
> +  - |
> +    // Example 2 (APIC domain forwarding interrupts as MSIs):
> +
> +    interrupt-controller@d000000 {
> +      compatible = "vendor,chip-aplic", "riscv,aplic";
> +      msi-parent = <&imsics>;
> +      reg = <0xd000000 0x4000>;
> +      interrupt-controller;
> +      #interrupt-cells = <2>;
> +      riscv,num-sources = <63>;
> +    };
> +...
> -- 
> 2.34.1
> 
> 

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 5/9] irqchip: Add RISC-V incoming MSI controller driver
  2022-11-11  4:42   ` Anup Patel
  (?)
  (?)
@ 2022-11-23  7:28   ` Ruan Jinjie
  2023-01-03 13:42     ` Anup Patel
  -1 siblings, 1 reply; 96+ messages in thread
From: Ruan Jinjie @ 2022-11-23  7:28 UTC (permalink / raw)
  To: linux-riscv



On 2022/11/11 12:42, Anup Patel wrote:
> The RISC-V advanced interrupt architecture (AIA) specification defines
> a new MSI controller for managing MSIs on a RISC-V platform. This new
> MSI controller is referred to as incoming message signaled interrupt
> controller (IMSIC) which manages MSI on per-HART (or per-CPU) basis.
> (For more details refer https://github.com/riscv/riscv-aia)
> 
> This patch adds an irqchip driver for RISC-V IMSIC found on RISC-V
> platforms.
> 
> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> ---
>  drivers/irqchip/Kconfig             |   20 +-
>  drivers/irqchip/Makefile            |    1 +
>  drivers/irqchip/irq-riscv-imsic.c   | 1207 +++++++++++++++++++++++++++
>  include/linux/irqchip/riscv-imsic.h |   92 ++
>  4 files changed, 1319 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/irqchip/irq-riscv-imsic.c
>  create mode 100644 include/linux/irqchip/riscv-imsic.h
> 
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index 7ef9f5e696d3..8246c08f0fd3 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -29,7 +29,6 @@ config ARM_GIC_V2M
>  
>  config GIC_NON_BANKED
>  	bool
> -
>  config ARM_GIC_V3
>  	bool
>  	select IRQ_DOMAIN_HIERARCHY
> @@ -564,6 +563,25 @@ config SIFIVE_PLIC
>  
>  	   If you don't know what to do here, say Y.
>  
> +config RISCV_IMSIC
> +	bool "RISC-V Incoming MSI Controller"
> +	depends on RISCV
> +	select IRQ_DOMAIN_HIERARCHY
> +	select GENERIC_MSI_IRQ_DOMAIN
> +	help
> +	   This enables support for the IMSIC chip found in RISC-V systems.
> +	   The IMSIC controls message signaled interrupts and forwards them
> +	   to each core as wired local interrupt.
> +
> +	   If you don't know what to do here, say Y.
> +
> +config RISCV_IMSIC_PCI
> +	bool
> +	depends on RISCV_IMSIC
> +	depends on PCI
> +	depends on PCI_MSI
> +	default RISCV_IMSIC
> +
>  config EXYNOS_IRQ_COMBINER
>  	bool "Samsung Exynos IRQ combiner support" if COMPILE_TEST
>  	depends on (ARCH_EXYNOS && ARM) || COMPILE_TEST
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index 87b49a10962c..22c723cc6ec8 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -96,6 +96,7 @@ obj-$(CONFIG_QCOM_MPM)			+= irq-qcom-mpm.o
>  obj-$(CONFIG_CSKY_MPINTC)		+= irq-csky-mpintc.o
>  obj-$(CONFIG_CSKY_APB_INTC)		+= irq-csky-apb-intc.o
>  obj-$(CONFIG_RISCV_INTC)		+= irq-riscv-intc.o
> +obj-$(CONFIG_RISCV_IMSIC)		+= irq-riscv-imsic.o
>  obj-$(CONFIG_SIFIVE_PLIC)		+= irq-sifive-plic.o
>  obj-$(CONFIG_IMX_IRQSTEER)		+= irq-imx-irqsteer.o
>  obj-$(CONFIG_IMX_INTMUX)		+= irq-imx-intmux.o
> diff --git a/drivers/irqchip/irq-riscv-imsic.c b/drivers/irqchip/irq-riscv-imsic.c
> new file mode 100644
> index 000000000000..95324fb4f5ed
> --- /dev/null
> +++ b/drivers/irqchip/irq-riscv-imsic.c
> @@ -0,0 +1,1207 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2021 Western Digital Corporation or its affiliates.
> + * Copyright (C) 2022 Ventana Micro Systems Inc.
> + */
> +
> +#define pr_fmt(fmt) "riscv-imsic: " fmt
> +#include <linux/bitmap.h>
> +#include <linux/cpu.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/iommu.h>
> +#include <linux/irq.h>
> +#include <linux/irqchip.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/irqchip/riscv-imsic.h>
> +#include <linux/irqdomain.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +#include <linux/smp.h>
> +#include <asm/hwcap.h>
> +
> +#define IMSIC_DISABLE_EIDELIVERY	0
> +#define IMSIC_ENABLE_EIDELIVERY		1
> +#define IMSIC_DISABLE_EITHRESHOLD	1
> +#define IMSIC_ENABLE_EITHRESHOLD	0
> +
> +#define imsic_csr_write(__c, __v)	\
> +do {					\
> +	csr_write(CSR_ISELECT, __c);	\
> +	csr_write(CSR_IREG, __v);	\
> +} while (0)
> +
> +#define imsic_csr_read(__c)		\
> +({					\
> +	unsigned long __v;		\
> +	csr_write(CSR_ISELECT, __c);	\
> +	__v = csr_read(CSR_IREG);	\
> +	__v;				\
> +})
> +
> +#define imsic_csr_set(__c, __v)		\
> +do {					\
> +	csr_write(CSR_ISELECT, __c);	\
> +	csr_set(CSR_IREG, __v);		\
> +} while (0)
> +
> +#define imsic_csr_clear(__c, __v)	\
> +do {					\
> +	csr_write(CSR_ISELECT, __c);	\
> +	csr_clear(CSR_IREG, __v);	\
> +} while (0)
> +
> +struct imsic_mmio {
> +	phys_addr_t pa;
> +	void __iomem *va;
> +	unsigned long size;
> +};
> +
> +struct imsic_priv {
> +	/* Global configuration common for all HARTs */
> +	struct imsic_global_config global;
> +
> +	/* MMIO regions */
> +	u32 num_mmios;
> +	struct imsic_mmio *mmios;
> +
> +	/* Global state of interrupt identities */
> +	raw_spinlock_t ids_lock;
> +	unsigned long *ids_used_bimap;
> +	unsigned long *ids_enabled_bimap;
> +	unsigned int *ids_target_cpu;
> +
> +	/* Mask for connected CPUs */
> +	struct cpumask lmask;
> +
> +	/* IPI interrupt identity */
> +	bool slow_ipi;
> +	u32 ipi_id;
> +	u32 ipi_lsync_id;
> +
> +	/* IRQ domains */
> +	struct irq_domain *base_domain;
> +	struct irq_domain *pci_domain;
> +	struct irq_domain *plat_domain;
> +};
> +
> +struct imsic_handler {
> +	/* Local configuration for given HART */
> +	struct imsic_local_config local;
> +
> +	/* Pointer to private context */
> +	struct imsic_priv *priv;
> +};
> +
> +static bool imsic_init_done;
> +
> +static int imsic_parent_irq;
> +static DEFINE_PER_CPU(struct imsic_handler, imsic_handlers);
> +
> +const struct imsic_global_config *imsic_get_global_config(void)
> +{
> +	struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
> +
> +	if (!handler || !handler->priv)
> +		return NULL;
> +
> +	return &handler->priv->global;
> +}
> +EXPORT_SYMBOL_GPL(imsic_get_global_config);
> +
> +const struct imsic_local_config *imsic_get_local_config(unsigned int cpu)
> +{
> +	struct imsic_handler *handler = per_cpu_ptr(&imsic_handlers, cpu);
> +
> +	if (!handler || !handler->priv)
> +		return NULL;
> +
> +	return &handler->local;
> +}
> +EXPORT_SYMBOL_GPL(imsic_get_local_config);
> +
> +static int imsic_cpu_page_phys(unsigned int cpu,
> +			       unsigned int guest_index,
> +			       phys_addr_t *out_msi_pa)
> +{
> +	struct imsic_handler *handler = per_cpu_ptr(&imsic_handlers, cpu);
> +	struct imsic_global_config *global;
> +	struct imsic_local_config *local;
> +
> +	if (!handler || !handler->priv)
> +		return -ENODEV;
> +	local = &handler->local;
> +	global = &handler->priv->global;
> +
> +	if (BIT(global->guest_index_bits) <= guest_index)
> +		return -EINVAL;
> +
> +	if (out_msi_pa)
> +		*out_msi_pa = local->msi_pa +
> +			      (guest_index * IMSIC_MMIO_PAGE_SZ);
> +
> +	return 0;
> +}
> +
> +static int imsic_get_cpu(struct imsic_priv *priv,
> +			 const struct cpumask *mask_val, bool force,
> +			 unsigned int *out_target_cpu)
> +{
> +	struct cpumask amask;
> +	unsigned int cpu;
> +
> +	cpumask_and(&amask, &priv->lmask, mask_val);
> +
> +	if (force)
> +		cpu = cpumask_first(&amask);
> +	else
> +		cpu = cpumask_any_and(&amask, cpu_online_mask);
> +
> +	if (cpu >= nr_cpu_ids)
> +		return -EINVAL;
> +
> +	if (out_target_cpu)
> +		*out_target_cpu = cpu;
> +
> +	return 0;
> +}
> +
> +static int imsic_get_cpu_msi_msg(unsigned int cpu, unsigned int id,
> +				 struct msi_msg *msg)
> +{
> +	phys_addr_t msi_addr;
> +	int err;
> +
> +	err = imsic_cpu_page_phys(cpu, 0, &msi_addr);
> +	if (err)
> +		return err;
> +
> +	msg->address_hi = upper_32_bits(msi_addr);
> +	msg->address_lo = lower_32_bits(msi_addr);
> +	msg->data = id;
> +
> +	return err;
> +}
> +
> +static void imsic_id_set_target(struct imsic_priv *priv,
> +				 unsigned int id, unsigned int target_cpu)
> +{
> +	raw_spin_lock(&priv->ids_lock);
> +	priv->ids_target_cpu[id] = target_cpu;
> +	raw_spin_unlock(&priv->ids_lock);
> +}
> +
> +static unsigned int imsic_id_get_target(struct imsic_priv *priv,
> +					unsigned int id)
> +{
> +	unsigned int ret;
> +
> +	raw_spin_lock(&priv->ids_lock);
> +	ret = priv->ids_target_cpu[id];
> +	raw_spin_unlock(&priv->ids_lock);
> +
> +	return ret;
> +}
> +
> +static void __imsic_eix_update(unsigned long base_id,
> +			       unsigned long num_id, bool pend, bool val)
> +{
> +	unsigned long i, isel, ireg, flags;
> +	unsigned long id = base_id, last_id = base_id + num_id;
> +
> +	while (id < last_id) {
> +		isel = id / BITS_PER_LONG;
> +		isel *= BITS_PER_LONG / IMSIC_EIPx_BITS;
> +		isel += (pend) ? IMSIC_EIP0 : IMSIC_EIE0;
> +
> +		ireg = 0;
> +		for (i = id & (__riscv_xlen - 1);
> +		     (id < last_id) && (i < __riscv_xlen); i++) {
> +			ireg |= BIT(i);
> +			id++;
> +		}
> +
> +		/*
> +		 * The IMSIC EIEx and EIPx registers are indirectly
> +		 * accessed via using ISELECT and IREG CSRs so we
> +		 * save/restore local IRQ to ensure that we don't
> +		 * get preempted while accessing IMSIC registers.
> +		 */
> +		local_irq_save(flags);
> +		if (val)
> +			imsic_csr_set(isel, ireg);
> +		else
> +			imsic_csr_clear(isel, ireg);
> +		local_irq_restore(flags);
> +	}
> +}
> +
> +#define __imsic_id_enable(__id)		\
> +	__imsic_eix_update((__id), 1, false, true)
> +#define __imsic_id_disable(__id)	\
> +	__imsic_eix_update((__id), 1, false, false)
> +
> +#ifdef CONFIG_SMP
> +static void __imsic_id_smp_sync(struct imsic_priv *priv)
> +{
> +	struct imsic_handler *handler;
> +	struct cpumask amask;
> +	int cpu;
> +
> +	cpumask_and(&amask, &priv->lmask, cpu_online_mask);
> +	for_each_cpu(cpu, &amask) {
> +		if (cpu == smp_processor_id())
> +			continue;
> +
> +		handler = per_cpu_ptr(&imsic_handlers, cpu);
> +		if (!handler || !handler->priv || !handler->local.msi_va) {
> +			pr_warn("CPU%d: handler not initialized\n", cpu);
> +			continue;
> +		}
> +
> +		writel(handler->priv->ipi_lsync_id, handler->local.msi_va);
> +	}
> +}
> +#else
> +#define __imsic_id_smp_sync(__priv)
> +#endif
> +
> +static void imsic_id_enable(struct imsic_priv *priv, unsigned int id)
> +{
> +	raw_spin_lock(&priv->ids_lock);
> +	bitmap_set(priv->ids_enabled_bimap, id, 1);
> +	__imsic_id_enable(id);
> +	raw_spin_unlock(&priv->ids_lock);
> +
> +	__imsic_id_smp_sync(priv);
> +}
> +
> +static void imsic_id_disable(struct imsic_priv *priv, unsigned int id)
> +{
> +	raw_spin_lock(&priv->ids_lock);
> +	bitmap_clear(priv->ids_enabled_bimap, id, 1);
> +	__imsic_id_disable(id);
> +	raw_spin_unlock(&priv->ids_lock);
> +
> +	__imsic_id_smp_sync(priv);
> +}
> +
> +static void imsic_ids_local_sync(struct imsic_priv *priv)
> +{
> +	int i;
> +
> +	raw_spin_lock(&priv->ids_lock);
> +	for (i = 1; i <= priv->global.nr_ids; i++) {
> +		if (priv->ipi_id == i || priv->ipi_lsync_id == i)
> +			continue;
> +
> +		if (test_bit(i, priv->ids_enabled_bimap))
> +			__imsic_id_enable(i);
> +		else
> +			__imsic_id_disable(i);
> +	}
> +	raw_spin_unlock(&priv->ids_lock);
> +}
> +
> +static void imsic_ids_local_delivery(struct imsic_priv *priv, bool enable)
> +{
> +	if (enable) {
> +		imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_ENABLE_EITHRESHOLD);
> +		imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_ENABLE_EIDELIVERY);
> +	} else {
> +		imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_DISABLE_EIDELIVERY);
> +		imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_DISABLE_EITHRESHOLD);
> +	}
> +}
> +
> +static int imsic_ids_alloc(struct imsic_priv *priv,
> +			   unsigned int max_id, unsigned int order)
> +{
> +	int ret;
> +
> +	if ((priv->global.nr_ids < max_id) ||
> +	    (max_id < BIT(order)))
> +		return -EINVAL;
> +
> +	raw_spin_lock(&priv->ids_lock);
> +	ret = bitmap_find_free_region(priv->ids_used_bimap,
> +				      max_id + 1, order);
> +	raw_spin_unlock(&priv->ids_lock);
> +
> +	return ret;
> +}
> +
> +static void imsic_ids_free(struct imsic_priv *priv, unsigned int base_id,
> +			   unsigned int order)
> +{
> +	raw_spin_lock(&priv->ids_lock);
> +	bitmap_release_region(priv->ids_used_bimap, base_id, order);
> +	raw_spin_unlock(&priv->ids_lock);
> +}
> +
> +static int __init imsic_ids_init(struct imsic_priv *priv)
> +{
> +	int i;
> +	struct imsic_global_config *global = &priv->global;
> +
> +	raw_spin_lock_init(&priv->ids_lock);
> +
> +	/* Allocate used bitmap */
> +	priv->ids_used_bimap = kcalloc(BITS_TO_LONGS(global->nr_ids + 1),
> +					sizeof(unsigned long), GFP_KERNEL);
> +	if (!priv->ids_used_bimap)
> +		return -ENOMEM;
> +
> +	/* Allocate enabled bitmap */
> +	priv->ids_enabled_bimap = kcalloc(BITS_TO_LONGS(global->nr_ids + 1),
> +					   sizeof(unsigned long), GFP_KERNEL);
> +	if (!priv->ids_enabled_bimap) {
> +		kfree(priv->ids_used_bimap);
> +		return -ENOMEM;
> +	}
> +
> +	/* Allocate target CPU array */
> +	priv->ids_target_cpu = kcalloc(global->nr_ids + 1,
> +				       sizeof(unsigned int), GFP_KERNEL);
> +	if (!priv->ids_target_cpu) {
> +		kfree(priv->ids_enabled_bimap);
> +		kfree(priv->ids_used_bimap);
> +		return -ENOMEM;
> +	}
> +	for (i = 0; i <= global->nr_ids; i++)
> +		priv->ids_target_cpu[i] = UINT_MAX;
> +
> +	/* Reserve ID#0 because it is special and never implemented */
> +	bitmap_set(priv->ids_used_bimap, 0, 1);
> +
> +	return 0;
> +}
> +
> +static void __init imsic_ids_cleanup(struct imsic_priv *priv)
> +{
> +	kfree(priv->ids_target_cpu);
> +	kfree(priv->ids_enabled_bimap);
> +	kfree(priv->ids_used_bimap);
> +}
> +
> +#ifdef CONFIG_SMP
> +static void imsic_ipi_send_mask(unsigned int parent_virq, void *data,
> +				const struct cpumask *mask)
> +{
> +	int cpu;
> +	struct imsic_handler *handler;
> +
> +	for_each_cpu(cpu, mask) {
> +		handler = per_cpu_ptr(&imsic_handlers, cpu);
> +		if (!handler || !handler->priv || !handler->local.msi_va) {
> +			pr_warn("CPU%d: handler not initialized\n", cpu);
> +			continue;
> +		}
> +
> +		writel(handler->priv->ipi_id, handler->local.msi_va);
> +	}
> +}
> +
> +static struct ipi_mux_ops imsic_ipi_ops = {
> +	.ipi_mux_send = imsic_ipi_send_mask,
> +};
> +
> +static void imsic_ipi_enable(struct imsic_priv *priv)
> +{
> +	__imsic_id_enable(priv->ipi_id);
> +	__imsic_id_enable(priv->ipi_lsync_id);
> +}
> +
> +static void imsic_ipi_disable(struct imsic_priv *priv)
> +{
> +	__imsic_id_disable(priv->ipi_lsync_id);
> +	__imsic_id_disable(priv->ipi_id);
> +}
> +
> +static int __init imsic_ipi_domain_init(struct imsic_priv *priv)
> +{
> +	int virq;
> +
> +	/* Skip IPI setup if IPIs are slow */
> +	if (priv->slow_ipi)
> +		goto skip_ipi;
> +
> +	/* Allocate interrupt identity for IPIs */
> +	virq = imsic_ids_alloc(priv, priv->global.nr_ids, get_count_order(1));
> +	if (virq < 0)
> +		return virq;
> +	priv->ipi_id = virq;
> +
> +	/* Create IMSIC IPI multiplexing */
> +	virq = ipi_mux_create(0, BITS_PER_BYTE, &imsic_ipi_ops, NULL);
> +	if (virq <= 0) {
> +		imsic_ids_free(priv, priv->ipi_id, get_count_order(1));
> +		return (virq < 0) ? virq : -ENOMEM;
> +	}
> +
> +	/* Set vIRQ range */
> +	riscv_ipi_set_virq_range(virq, BITS_PER_BYTE, true);
> +
> +skip_ipi:
> +	/* Allocate interrupt identity for local enable/disable sync */
> +	virq = imsic_ids_alloc(priv, priv->global.nr_ids, get_count_order(1));
> +	if (virq < 0) {
> +		imsic_ids_free(priv, priv->ipi_id, get_count_order(1));
> +		return virq;
> +	}
> +	priv->ipi_lsync_id = virq;
> +
> +	return 0;
> +}
> +
> +static void __init imsic_ipi_domain_cleanup(struct imsic_priv *priv)
> +{
> +	imsic_ids_free(priv, priv->ipi_lsync_id, get_count_order(1));
> +	if (priv->ipi_id)
> +		imsic_ids_free(priv, priv->ipi_id, get_count_order(1));
> +}
> +#else
> +static void imsic_ipi_enable(struct imsic_priv *priv)
> +{
> +}
> +
> +static void imsic_ipi_disable(struct imsic_priv *priv)
> +{
> +}
> +
> +static int __init imsic_ipi_domain_init(struct imsic_priv *priv)
> +{
> +	/* Clear the IPI ids because we are not using IPIs */
> +	priv->ipi_id = 0;
> +	priv->ipi_lsync_id = 0;
> +	return 0;
> +}
> +
> +static void __init imsic_ipi_domain_cleanup(struct imsic_priv *priv)
> +{
> +}
> +#endif
> +
> +static void imsic_irq_mask(struct irq_data *d)
> +{
> +	imsic_id_disable(irq_data_get_irq_chip_data(d), d->hwirq);
> +}
> +
> +static void imsic_irq_unmask(struct irq_data *d)
> +{
> +	imsic_id_enable(irq_data_get_irq_chip_data(d), d->hwirq);
> +}
> +
> +static void imsic_irq_compose_msi_msg(struct irq_data *d,
> +				      struct msi_msg *msg)
> +{
> +	struct imsic_priv *priv = irq_data_get_irq_chip_data(d);
> +	unsigned int cpu;
> +	int err;
> +
> +	cpu = imsic_id_get_target(priv, d->hwirq);
> +	WARN_ON(cpu == UINT_MAX);
> +
> +	err = imsic_get_cpu_msi_msg(cpu, d->hwirq, msg);
> +	WARN_ON(err);
> +
> +	iommu_dma_compose_msi_msg(irq_data_get_msi_desc(d), msg);
> +}
> +
> +#ifdef CONFIG_SMP
> +static int imsic_irq_set_affinity(struct irq_data *d,
> +				  const struct cpumask *mask_val,
> +				  bool force)
> +{
> +	struct imsic_priv *priv = irq_data_get_irq_chip_data(d);
> +	unsigned int target_cpu;
> +	int rc;
> +
> +	rc = imsic_get_cpu(priv, mask_val, force, &target_cpu);
> +	if (rc)
> +		return rc;
> +
> +	imsic_id_set_target(priv, d->hwirq, target_cpu);
> +	irq_data_update_effective_affinity(d, cpumask_of(target_cpu));
> +
> +	return IRQ_SET_MASK_OK;
> +}
> +#endif
> +
> +static struct irq_chip imsic_irq_base_chip = {
> +	.name			= "RISC-V IMSIC-BASE",
> +	.irq_mask		= imsic_irq_mask,
> +	.irq_unmask		= imsic_irq_unmask,
> +#ifdef CONFIG_SMP
> +	.irq_set_affinity	= imsic_irq_set_affinity,
> +#endif
> +	.irq_compose_msi_msg	= imsic_irq_compose_msi_msg,
> +	.flags			= IRQCHIP_SKIP_SET_WAKE |
> +				  IRQCHIP_MASK_ON_SUSPEND,
> +};
> +
> +static int imsic_irq_domain_alloc(struct irq_domain *domain,
> +				  unsigned int virq,
> +				  unsigned int nr_irqs,
> +				  void *args)
> +{
> +	struct imsic_priv *priv = domain->host_data;
> +	msi_alloc_info_t *info = args;
> +	phys_addr_t msi_addr;
> +	int i, hwirq, err = 0;
> +	unsigned int cpu;
> +
> +	err = imsic_get_cpu(priv, &priv->lmask, false, &cpu);
> +	if (err)
> +		return err;
> +
> +	err = imsic_cpu_page_phys(cpu, 0, &msi_addr);
> +	if (err)
> +		return err;
> +
> +	hwirq = imsic_ids_alloc(priv, priv->global.nr_ids,
> +				get_count_order(nr_irqs));
> +	if (hwirq < 0)
> +		return hwirq;
> +
> +	err = iommu_dma_prepare_msi(info->desc, msi_addr);
> +	if (err)
> +		goto fail;
> +
> +	for (i = 0; i < nr_irqs; i++) {
> +		imsic_id_set_target(priv, hwirq + i, cpu);
> +		irq_domain_set_info(domain, virq + i, hwirq + i,
> +				    &imsic_irq_base_chip, priv,
> +				    handle_simple_irq, NULL, NULL);
> +		irq_set_noprobe(virq + i);
> +		irq_set_affinity(virq + i, &priv->lmask);
> +	}
> +
> +	return 0;
> +
> +fail:
> +	imsic_ids_free(priv, hwirq, get_count_order(nr_irqs));
> +	return err;
> +}
> +
> +static void imsic_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 imsic_priv *priv = domain->host_data;
> +
> +	imsic_ids_free(priv, d->hwirq, get_count_order(nr_irqs));
> +	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops imsic_base_domain_ops = {
> +	.alloc		= imsic_irq_domain_alloc,
> +	.free		= imsic_irq_domain_free,
> +};
> +
> +#ifdef CONFIG_RISCV_IMSIC_PCI
> +
> +static void imsic_pci_mask_irq(struct irq_data *d)
> +{
> +	pci_msi_mask_irq(d);
> +	irq_chip_mask_parent(d);
> +}
> +
> +static void imsic_pci_unmask_irq(struct irq_data *d)
> +{
> +	pci_msi_unmask_irq(d);
> +	irq_chip_unmask_parent(d);
> +}
> +
> +static struct irq_chip imsic_pci_irq_chip = {
> +	.name			= "RISC-V IMSIC-PCI",
> +	.irq_mask		= imsic_pci_mask_irq,
> +	.irq_unmask		= imsic_pci_unmask_irq,
> +	.irq_eoi		= irq_chip_eoi_parent,
> +};
> +
> +static struct msi_domain_ops imsic_pci_domain_ops = {
> +};
> +
> +static struct msi_domain_info imsic_pci_domain_info = {
> +	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +		   MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI),
> +	.ops	= &imsic_pci_domain_ops,
> +	.chip	= &imsic_pci_irq_chip,
> +};
> +
> +#endif
> +
> +static struct irq_chip imsic_plat_irq_chip = {
> +	.name			= "RISC-V IMSIC-PLAT",
> +};
> +
> +static struct msi_domain_ops imsic_plat_domain_ops = {
> +};
> +
> +static struct msi_domain_info imsic_plat_domain_info = {
> +	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
> +	.ops	= &imsic_plat_domain_ops,
> +	.chip	= &imsic_plat_irq_chip,
> +};
> +
> +static int __init imsic_irq_domains_init(struct imsic_priv *priv,
> +					 struct fwnode_handle *fwnode)
> +{
> +	/* Create Base IRQ domain */
> +	priv->base_domain = irq_domain_create_tree(fwnode,
> +						&imsic_base_domain_ops, priv);
> +	if (!priv->base_domain) {
> +		pr_err("Failed to create IMSIC base domain\n");
> +		return -ENOMEM;
> +	}
> +	irq_domain_update_bus_token(priv->base_domain, DOMAIN_BUS_NEXUS);
> +
> +#ifdef CONFIG_RISCV_IMSIC_PCI
> +	/* Create PCI MSI domain */
> +	priv->pci_domain = pci_msi_create_irq_domain(fwnode,
> +						&imsic_pci_domain_info,
> +						priv->base_domain);
> +	if (!priv->pci_domain) {
> +		pr_err("Failed to create IMSIC PCI domain\n");
> +		irq_domain_remove(priv->base_domain);
> +		return -ENOMEM;
> +	}
> +#endif
> +
> +	/* Create Platform MSI domain */
> +	priv->plat_domain = platform_msi_create_irq_domain(fwnode,
> +						&imsic_plat_domain_info,
> +						priv->base_domain);
> +	if (!priv->plat_domain) {
> +		pr_err("Failed to create IMSIC platform domain\n");
> +		if (priv->pci_domain)
> +			irq_domain_remove(priv->pci_domain);
> +		irq_domain_remove(priv->base_domain);
> +		return -ENOMEM;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * To handle an interrupt, we read the TOPEI CSR and write zero in one
> + * instruction. If TOPEI CSR is non-zero then we translate TOPEI.ID to
> + * Linux interrupt number and let Linux IRQ subsystem handle it.
> + */
> +static void imsic_handle_irq(struct irq_desc *desc)
> +{
> +	struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
> +	struct irq_chip *chip = irq_desc_get_chip(desc);
> +	struct imsic_priv *priv = handler->priv;
> +	irq_hw_number_t hwirq;
> +	int err;
> +
> +	WARN_ON_ONCE(!handler->priv);
> +
> +	chained_irq_enter(chip, desc);
> +
> +	while ((hwirq = csr_swap(CSR_TOPEI, 0))) {
> +		hwirq = hwirq >> TOPEI_ID_SHIFT;
> +
> +		if (hwirq == priv->ipi_id) {
> +#ifdef CONFIG_SMP
> +			ipi_mux_process();
> +#endif
> +			continue;
> +		} else if (hwirq == priv->ipi_lsync_id) {
> +			imsic_ids_local_sync(priv);
> +			continue;
> +		}
> +
> +		err = generic_handle_domain_irq(priv->base_domain, hwirq);
> +		if (unlikely(err))
> +			pr_warn_ratelimited(
> +				"hwirq %lu mapping not found\n", hwirq);
> +	}
> +
> +	chained_irq_exit(chip, desc);
> +}
> +
> +static int imsic_dying_cpu(unsigned int cpu)
> +{
> +	struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
> +	struct imsic_priv *priv = handler->priv;
> +
> +	/* Disable per-CPU parent interrupt */
> +	if (imsic_parent_irq)
> +		disable_percpu_irq(imsic_parent_irq);
> +
> +	/* Locally disable interrupt delivery */
> +	imsic_ids_local_delivery(priv, false);
> +
> +	/* Disable IPIs */
> +	imsic_ipi_disable(priv);
> +
> +	return 0;
> +}
> +
> +static int imsic_starting_cpu(unsigned int cpu)
> +{
> +	struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
> +	struct imsic_priv *priv = handler->priv;
> +
> +	/* Enable per-CPU parent interrupt */
> +	if (imsic_parent_irq)
> +		enable_percpu_irq(imsic_parent_irq,
> +				  irq_get_trigger_type(imsic_parent_irq));
> +	else
> +		pr_warn("cpu%d: parent irq not available\n", cpu);
> +
> +	/* Enable IPIs */
> +	imsic_ipi_enable(priv);
> +
> +	/*
> +	 * Interrupts identities might have been enabled/disabled while
> +	 * this CPU was not running so sync-up local enable/disable state.
> +	 */
> +	imsic_ids_local_sync(priv);
> +
> +	/* Locally enable interrupt delivery */
> +	imsic_ids_local_delivery(priv, true);
> +
> +	return 0;
> +}
> +
> +struct imsic_fwnode_ops {
> +	u32 (*nr_parent_irq)(struct fwnode_handle *fwnode,
> +			     void *fwopaque);
> +	int (*parent_hartid)(struct fwnode_handle *fwnode,
> +			     void *fwopaque, u32 index,
> +			     unsigned long *out_hartid);
> +	u32 (*nr_mmio)(struct fwnode_handle *fwnode, void *fwopaque);
> +	int (*mmio_to_resource)(struct fwnode_handle *fwnode,
> +				void *fwopaque, u32 index,
> +				struct resource *res);
> +	void __iomem *(*mmio_map)(struct fwnode_handle *fwnode,
> +				  void *fwopaque, u32 index);
> +	int (*read_u32)(struct fwnode_handle *fwnode,
> +			void *fwopaque, const char *prop, u32 *out_val);
> +	bool (*read_bool)(struct fwnode_handle *fwnode,
> +			  void *fwopaque, const char *prop);
> +};
> +
> +static int __init imsic_init(struct imsic_fwnode_ops *fwops,
> +			     struct fwnode_handle *fwnode,
> +			     void *fwopaque)
> +{
> +	struct resource res;
> +	phys_addr_t base_addr;
> +	int rc, nr_parent_irqs;
> +	struct imsic_mmio *mmio;
> +	struct imsic_priv *priv;
> +	struct irq_domain *domain;
> +	struct imsic_handler *handler;
> +	struct imsic_global_config *global;
> +	u32 i, tmp, nr_handlers = 0;
> +
> +	if (imsic_init_done) {
> +		pr_err("%pfwP: already initialized hence ignoring\n",
> +			fwnode);
> +		return -ENODEV;
> +	}
> +
> +	if (!riscv_isa_extension_available(NULL, SxAIA)) {

why pass NULL to check SxAIA isa extension?I encounter following error
when launch Linux kernel.

[    0.000000] riscv-imsic: imsics@28000000: AIA support not available
[    0.000000] OF: of_irq_init: Failed to init /soc/imsics@28000000
((____ptrval____)), parent (____ptrval____)

[    0.940357] riscv-aplic d000000.aplic: IMSIC global config not found


> +		pr_err("%pfwP: AIA support not available\n", fwnode);
> +		return -ENODEV;
> +	}
> +
> +	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +	global = &priv->global;
> +
> +	/* Find number of parent interrupts */
> +	nr_parent_irqs = fwops->nr_parent_irq(fwnode, fwopaque);
> +	if (!nr_parent_irqs) {
> +		pr_err("%pfwP: no parent irqs available\n", fwnode);
> +		return -EINVAL;
> +	}
> +
> +	/* Find number of guest index bits in MSI address */
> +	rc = fwops->read_u32(fwnode, fwopaque, "riscv,guest-index-bits",
> +			     &global->guest_index_bits);
> +	if (rc)
> +		global->guest_index_bits = 0;
> +	tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT;
> +	if (tmp < global->guest_index_bits) {
> +		pr_err("%pfwP: guest index bits too big\n", fwnode);
> +		return -EINVAL;
> +	}
> +
> +	/* Find number of HART index bits */
> +	rc = fwops->read_u32(fwnode, fwopaque, "riscv,hart-index-bits",
> +			     &global->hart_index_bits);
> +	if (rc) {
> +		/* Assume default value */
> +		global->hart_index_bits = __fls(nr_parent_irqs);
> +		if (BIT(global->hart_index_bits) < nr_parent_irqs)
> +			global->hart_index_bits++;
> +	}
> +	tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT -
> +	      global->guest_index_bits;
> +	if (tmp < global->hart_index_bits) {
> +		pr_err("%pfwP: HART index bits too big\n", fwnode);
> +		return -EINVAL;
> +	}
> +
> +	/* Find number of group index bits */
> +	rc = fwops->read_u32(fwnode, fwopaque, "riscv,group-index-bits",
> +			     &global->group_index_bits);
> +	if (rc)
> +		global->group_index_bits = 0;
> +	tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT -
> +	      global->guest_index_bits - global->hart_index_bits;
> +	if (tmp < global->group_index_bits) {
> +		pr_err("%pfwP: group index bits too big\n", fwnode);
> +		return -EINVAL;
> +	}
> +
> +	/* Find first bit position of group index */
> +	tmp = IMSIC_MMIO_PAGE_SHIFT * 2;
> +	rc = fwops->read_u32(fwnode, fwopaque, "riscv,group-index-shift",
> +			     &global->group_index_shift);
> +	if (rc)
> +		global->group_index_shift = tmp;
> +	if (global->group_index_shift < tmp) {
> +		pr_err("%pfwP: group index shift too small\n", fwnode);
> +		return -EINVAL;
> +	}
> +	tmp = global->group_index_bits + global->group_index_shift - 1;
> +	if (tmp >= BITS_PER_LONG) {
> +		pr_err("%pfwP: group index shift too big\n", fwnode);
> +		return -EINVAL;
> +	}
> +
> +	/* Find number of interrupt identities */
> +	rc = fwops->read_u32(fwnode, fwopaque, "riscv,num-ids",
> +			     &global->nr_ids);
> +	if (rc) {
> +		pr_err("%pfwP: number of interrupt identities not found\n",
> +			fwnode);
> +		return rc;
> +	}
> +	if ((global->nr_ids < IMSIC_MIN_ID) ||
> +	    (global->nr_ids >= IMSIC_MAX_ID) ||
> +	    ((global->nr_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID)) {
> +		pr_err("%pfwP: invalid number of interrupt identities\n",
> +			fwnode);
> +		return -EINVAL;
> +	}
> +
> +	/* Find number of guest interrupt identities */
> +	if (fwops->read_u32(fwnode, fwopaque, "riscv,num-guest-ids",
> +			    &global->nr_guest_ids))
> +		global->nr_guest_ids = global->nr_ids;
> +	if ((global->nr_guest_ids < IMSIC_MIN_ID) ||
> +	    (global->nr_guest_ids >= IMSIC_MAX_ID) ||
> +	    ((global->nr_guest_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID)) {
> +		pr_err("%pfwP: invalid number of guest interrupt identities\n",
> +			fwnode);
> +		return -EINVAL;
> +	}
> +
> +	/* Check if IPIs are slow */
> +	priv->slow_ipi = fwops->read_bool(fwnode, fwopaque, "riscv,slow-ipi");
> +
> +	/* Compute base address */
> +	rc = fwops->mmio_to_resource(fwnode, fwopaque, 0, &res);
> +	if (rc) {
> +		pr_err("%pfwP: first MMIO resource not found\n", fwnode);
> +		return -EINVAL;
> +	}
> +	global->base_addr = res.start;
> +	global->base_addr &= ~(BIT(global->guest_index_bits +
> +				   global->hart_index_bits +
> +				   IMSIC_MMIO_PAGE_SHIFT) - 1);
> +	global->base_addr &= ~((BIT(global->group_index_bits) - 1) <<
> +			       global->group_index_shift);
> +
> +	/* Find number of MMIO register sets */
> +	priv->num_mmios = fwops->nr_mmio(fwnode, fwopaque);
> +
> +	/* Allocate MMIO register sets */
> +	priv->mmios = kcalloc(priv->num_mmios, sizeof(*mmio), GFP_KERNEL);
> +	if (!priv->mmios) {
> +		rc = -ENOMEM;
> +		goto out_free_priv;
> +	}
> +
> +	/* Parse and map MMIO register sets */
> +	for (i = 0; i < priv->num_mmios; i++) {
> +		mmio = &priv->mmios[i];
> +		rc = fwops->mmio_to_resource(fwnode, fwopaque, i, &res);
> +		if (rc) {
> +			pr_err("%pfwP: unable to parse MMIO regset %d\n",
> +				fwnode, i);
> +			goto out_iounmap;
> +		}
> +		mmio->pa = res.start;
> +		mmio->size = res.end - res.start + 1;
> +
> +		base_addr = mmio->pa;
> +		base_addr &= ~(BIT(global->guest_index_bits +
> +				   global->hart_index_bits +
> +				   IMSIC_MMIO_PAGE_SHIFT) - 1);
> +		base_addr &= ~((BIT(global->group_index_bits) - 1) <<
> +			       global->group_index_shift);
> +		if (base_addr != global->base_addr) {
> +			rc = -EINVAL;
> +			pr_err("%pfwP: address mismatch for regset %d\n",
> +				fwnode, i);
> +			goto out_iounmap;
> +		}
> +
> +		tmp = BIT(global->guest_index_bits) - 1;
> +		if ((mmio->size / IMSIC_MMIO_PAGE_SZ) & tmp) {
> +			rc = -EINVAL;
> +			pr_err("%pfwP: size mismatch for regset %d\n",
> +				fwnode, i);
> +			goto out_iounmap;
> +		}
> +
> +		mmio->va = fwops->mmio_map(fwnode, fwopaque, i);
> +		if (!mmio->va) {
> +			rc = -EIO;
> +			pr_err("%pfwP: unable to map MMIO regset %d\n",
> +				fwnode, i);
> +			goto out_iounmap;
> +		}
> +	}
> +
> +	/* Initialize interrupt identity management */
> +	rc = imsic_ids_init(priv);
> +	if (rc) {
> +		pr_err("%pfwP: failed to initialize interrupt management\n",
> +		       fwnode);
> +		goto out_iounmap;
> +	}
> +
> +	/* Configure handlers for target CPUs */
> +	for (i = 0; i < nr_parent_irqs; i++) {
> +		unsigned long reloff, hartid;
> +		int j, cpu;
> +
> +		rc = fwops->parent_hartid(fwnode, fwopaque, i, &hartid);
> +		if (rc) {
> +			pr_warn("%pfwP: hart ID for parent irq%d not found\n",
> +				fwnode, i);
> +			continue;
> +		}
> +
> +		cpu = riscv_hartid_to_cpuid(hartid);
> +		if (cpu < 0) {
> +			pr_warn("%pfwP: invalid cpuid for parent irq%d\n",
> +				fwnode, i);
> +			continue;
> +		}
> +
> +		/* Find MMIO location of MSI page */
> +		mmio = NULL;
> +		reloff = i * BIT(global->guest_index_bits) *
> +			 IMSIC_MMIO_PAGE_SZ;
> +		for (j = 0; priv->num_mmios; j++) {
> +			if (reloff < priv->mmios[j].size) {
> +				mmio = &priv->mmios[j];
> +				break;
> +			}
> +
> +			reloff -= priv->mmios[j].size;
> +		}
> +		if (!mmio) {
> +			pr_warn("%pfwP: MMIO not found for parent irq%d\n",
> +				fwnode, i);
> +			continue;
> +		}
> +
> +		handler = per_cpu_ptr(&imsic_handlers, cpu);
> +		if (handler->priv) {
> +			pr_warn("%pfwP: CPU%d handler already configured.\n",
> +				fwnode, cpu);
> +			goto done;
> +		}
> +
> +		cpumask_set_cpu(cpu, &priv->lmask);
> +		handler->local.msi_pa = mmio->pa + reloff;
> +		handler->local.msi_va = mmio->va + reloff;
> +		handler->priv = priv;
> +
> +done:
> +		nr_handlers++;
> +	}
> +
> +	/* If no CPU handlers found then can't take interrupts */
> +	if (!nr_handlers) {
> +		pr_err("%pfwP: No CPU handlers found\n", fwnode);
> +		rc = -ENODEV;
> +		goto out_ids_cleanup;
> +	}
> +
> +	/* Find parent domain and register chained handler */
> +	domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(),
> +					  DOMAIN_BUS_ANY);
> +	if (!domain) {
> +		pr_err("%pfwP: Failed to find INTC domain\n", fwnode);
> +		rc = -ENOENT;
> +		goto out_ids_cleanup;
> +	}
> +	imsic_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT);
> +	if (!imsic_parent_irq) {
> +		pr_err("%pfwP: Failed to create INTC mapping\n", fwnode);
> +		rc = -ENOENT;
> +		goto out_ids_cleanup;
> +	}
> +	irq_set_chained_handler(imsic_parent_irq, imsic_handle_irq);
> +
> +	/* Initialize IPI domain */
> +	rc = imsic_ipi_domain_init(priv);
> +	if (rc) {
> +		pr_err("%pfwP: Failed to initialize IPI domain\n", fwnode);
> +		goto out_ids_cleanup;
> +	}
> +
> +	/* Initialize IRQ and MSI domains */
> +	rc = imsic_irq_domains_init(priv, fwnode);
> +	if (rc) {
> +		pr_err("%pfwP: Failed to initialize IRQ and MSI domains\n",
> +		       fwnode);
> +		goto out_ipi_domain_cleanup;
> +	}
> +
> +	/* Setup cpuhp state */
> +	cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
> +			  "irqchip/riscv/imsic:starting",
> +			  imsic_starting_cpu, imsic_dying_cpu);
> +
> +	/*
> +	 * Only one IMSIC instance allowed in a platform for clean
> +	 * implementation of SMP IRQ affinity and per-CPU IPIs.
> +	 *
> +	 * This means on a multi-socket (or multi-die) platform we
> +	 * will have multiple MMIO regions for one IMSIC instance.
> +	 */
> +	imsic_init_done = true;
> +
> +	pr_info("%pfwP:  hart-index-bits: %d,  guest-index-bits: %d\n",
> +		fwnode, global->hart_index_bits, global->guest_index_bits);
> +	pr_info("%pfwP: group-index-bits: %d, group-index-shift: %d\n",
> +		fwnode, global->group_index_bits, global->group_index_shift);
> +	pr_info("%pfwP: mapped %d interrupts for %d CPUs at %pa\n",
> +		fwnode, global->nr_ids, nr_handlers, &global->base_addr);
> +	if (priv->ipi_lsync_id)
> +		pr_info("%pfwP: enable/disable sync using interrupt %d\n",
> +			fwnode, priv->ipi_lsync_id);
> +	if (priv->ipi_id)
> +		pr_info("%pfwP: providing IPIs using interrupt %d\n",
> +			fwnode, priv->ipi_id);
> +
> +	return 0;
> +
> +out_ipi_domain_cleanup:
> +	imsic_ipi_domain_cleanup(priv);
> +out_ids_cleanup:
> +	imsic_ids_cleanup(priv);
> +out_iounmap:
> +	for (i = 0; i < priv->num_mmios; i++) {
> +		if (priv->mmios[i].va)
> +			iounmap(priv->mmios[i].va);
> +	}
> +	kfree(priv->mmios);
> +out_free_priv:
> +	kfree(priv);
> +	return rc;
> +}
> +
> +static u32 __init imsic_dt_nr_parent_irq(struct fwnode_handle *fwnode,
> +					 void *fwopaque)
> +{
> +	return of_irq_count(to_of_node(fwnode));
> +}
> +
> +static int __init imsic_dt_parent_hartid(struct fwnode_handle *fwnode,
> +					 void *fwopaque, u32 index,
> +					 unsigned long *out_hartid)
> +{
> +	struct of_phandle_args parent;
> +	int rc;
> +
> +	rc = of_irq_parse_one(to_of_node(fwnode), index, &parent);
> +	if (rc)
> +		return rc;
> +
> +	/*
> +	 * Skip interrupts other than external interrupts for
> +	 * current privilege level.
> +	 */
> +	if (parent.args[0] != RV_IRQ_EXT)
> +		return -EINVAL;
> +
> +	return riscv_of_parent_hartid(parent.np, out_hartid);
> +}
> +
> +static u32 __init imsic_dt_nr_mmio(struct fwnode_handle *fwnode,
> +				   void *fwopaque)
> +{
> +	u32 ret = 0;
> +	struct resource res;
> +
> +	while (!of_address_to_resource(to_of_node(fwnode), ret, &res))
> +		ret++;
> +
> +	return ret;
> +}
> +
> +static int __init imsic_mmio_to_resource(struct fwnode_handle *fwnode,
> +					 void *fwopaque, u32 index,
> +					 struct resource *res)
> +{
> +	return of_address_to_resource(to_of_node(fwnode), index, res);
> +}
> +
> +static void __iomem __init *imsic_dt_mmio_map(struct fwnode_handle *fwnode,
> +					      void *fwopaque, u32 index)
> +{
> +	return of_iomap(to_of_node(fwnode), index);
> +}
> +
> +static int __init imsic_dt_read_u32(struct fwnode_handle *fwnode,
> +				    void *fwopaque, const char *prop,
> +				    u32 *out_val)
> +{
> +	return of_property_read_u32(to_of_node(fwnode), prop, out_val);
> +}
> +
> +static bool __init imsic_dt_read_bool(struct fwnode_handle *fwnode,
> +				      void *fwopaque, const char *prop)
> +{
> +	return of_property_read_bool(to_of_node(fwnode), prop);
> +}
> +
> +static int __init imsic_dt_init(struct device_node *node,
> +				struct device_node *parent)
> +{
> +	struct imsic_fwnode_ops ops = {
> +		.nr_parent_irq = imsic_dt_nr_parent_irq,
> +		.parent_hartid = imsic_dt_parent_hartid,
> +		.nr_mmio = imsic_dt_nr_mmio,
> +		.mmio_to_resource = imsic_mmio_to_resource,
> +		.mmio_map = imsic_dt_mmio_map,
> +		.read_u32 = imsic_dt_read_u32,
> +		.read_bool = imsic_dt_read_bool,
> +	};
> +
> +	return imsic_init(&ops, &node->fwnode, NULL);
> +}
> +IRQCHIP_DECLARE(riscv_imsic, "riscv,imsics", imsic_dt_init);
> diff --git a/include/linux/irqchip/riscv-imsic.h b/include/linux/irqchip/riscv-imsic.h
> new file mode 100644
> index 000000000000..5d1387adc0ba
> --- /dev/null
> +++ b/include/linux/irqchip/riscv-imsic.h
> @@ -0,0 +1,92 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2021 Western Digital Corporation or its affiliates.
> + * Copyright (C) 2022 Ventana Micro Systems Inc.
> + */
> +#ifndef __LINUX_IRQCHIP_RISCV_IMSIC_H
> +#define __LINUX_IRQCHIP_RISCV_IMSIC_H
> +
> +#include <linux/types.h>
> +#include <asm/csr.h>
> +
> +#define IMSIC_MMIO_PAGE_SHIFT		12
> +#define IMSIC_MMIO_PAGE_SZ		(1UL << IMSIC_MMIO_PAGE_SHIFT)
> +#define IMSIC_MMIO_PAGE_LE		0x00
> +#define IMSIC_MMIO_PAGE_BE		0x04
> +
> +#define IMSIC_MIN_ID			63
> +#define IMSIC_MAX_ID			2048
> +
> +#define IMSIC_EIDELIVERY		0x70
> +
> +#define IMSIC_EITHRESHOLD		0x72
> +
> +#define IMSIC_EIP0			0x80
> +#define IMSIC_EIP63			0xbf
> +#define IMSIC_EIPx_BITS			32
> +
> +#define IMSIC_EIE0			0xc0
> +#define IMSIC_EIE63			0xff
> +#define IMSIC_EIEx_BITS			32
> +
> +#define IMSIC_FIRST			IMSIC_EIDELIVERY
> +#define IMSIC_LAST			IMSIC_EIE63
> +
> +#define IMSIC_MMIO_SETIPNUM_LE		0x00
> +#define IMSIC_MMIO_SETIPNUM_BE		0x04
> +
> +struct imsic_global_config {
> +	/*
> +	 * MSI Target Address Scheme
> +	 *
> +	 * XLEN-1                                                12     0
> +	 * |                                                     |     |
> +	 * -------------------------------------------------------------
> +	 * |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
> +	 * -------------------------------------------------------------
> +	 */
> +
> +	/* Bits representing Guest index, HART index, and Group index */
> +	u32 guest_index_bits;
> +	u32 hart_index_bits;
> +	u32 group_index_bits;
> +	u32 group_index_shift;
> +
> +	/* Global base address matching all target MSI addresses */
> +	phys_addr_t base_addr;
> +
> +	/* Number of interrupt identities */
> +	u32 nr_ids;
> +
> +	/* Number of guest interrupt identities */
> +	u32 nr_guest_ids;
> +};
> +
> +struct imsic_local_config {
> +	phys_addr_t msi_pa;
> +	void __iomem *msi_va;
> +};
> +
> +#ifdef CONFIG_RISCV_IMSIC
> +
> +extern const struct imsic_global_config *imsic_get_global_config(void);
> +
> +extern const struct imsic_local_config *imsic_get_local_config(
> +							unsigned int cpu);
> +
> +#else
> +
> +static inline const struct imsic_global_config *imsic_get_global_config(void)
> +{
> +	return NULL;
> +}
> +
> +static inline const struct imsic_local_config *imsic_get_local_config(
> +							unsigned int cpu)
> +{
> +	return NULL;
> +}
> +
> +#endif
> +
> +#endif

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 0/9] Linux RISC-V AIA Support
  2022-11-11  9:07   ` Atish Patra
@ 2023-01-02 10:05     ` Anup Patel
  -1 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2023-01-02 10:05 UTC (permalink / raw)
  To: Atish Patra
  Cc: Anup Patel, Palmer Dabbelt, Paul Walmsley, Thomas Gleixner,
	Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Alistair Francis,
	linux-riscv, linux-kernel, devicetree

On Fri, Nov 11, 2022 at 2:37 PM Atish Patra <atishp@atishpatra.org> wrote:
>
> On Thu, Nov 10, 2022 at 8:42 PM Anup Patel <apatel@ventanamicro.com> wrote:
> >
> > The RISC-V AIA specification is now frozen as-per the RISC-V international
> > process. The latest frozen specifcation can be found at:
> > https://github.com/riscv/riscv-aia/releases/download/1.0-RC1/riscv-interrupts-1.0-RC1.pdf
> >
> > At a high-level, the AIA specification adds three things:
> > 1) AIA CSRs
> >    - Improved local interrupt support
> > 2) Incoming Message Signaled Interrupt Controller (IMSIC)
> >    - Per-HART MSI controller
> >    - Support MSI virtualization
> >    - Support IPI along with virtualization
> > 3) Advanced Platform-Level Interrupt Controller (APLIC)
> >    - Wired interrupt controller
> >    - In MSI-mode, converts wired interrupt into MSIs (i.e. MSI generator)
> >    - In Direct-mode, injects external interrupts directly into HARTs
> >
> > For an overview of the AIA specification, refer the recent AIA virtualization
> > talk at KVM Forum 2022:
> > https://static.sched.com/hosted_files/kvmforum2022/a1/AIA_Virtualization_in_KVM_RISCV_final.pdf
> > https://www.youtube.com/watch?v=r071dL8Z0yo
> >
> > This series adds required Linux irqchip drivers for AIA and it depends on
> > the recent "RISC-V IPI Improvements".
> > (Refer, https://lore.kernel.org/lkml/20221101143400.690000-1-apatel@ventanamicro.com/t/)
> >
> > To test this series, use QEMU v7.1 (or higher) and OpenSBI v1.1 (or higher).
> >
> > These patches can also be found in the riscv_aia_v1 branch at:
> > https://github.com/avpatel/linux.git
> >
> > Anup Patel (9):
> >   RISC-V: Add AIA related CSR defines
> >   RISC-V: Detect AIA CSRs from ISA string
> >   irqchip/riscv-intc: Add support for RISC-V AIA
> >   dt-bindings: Add RISC-V incoming MSI controller bindings
> >   irqchip: Add RISC-V incoming MSI controller driver
> >   dt-bindings: Add RISC-V advanced PLIC bindings
> >   irqchip: Add RISC-V advanced PLIC driver
> >   RISC-V: Select APLIC and IMSIC drivers for QEMU virt machine
> >   MAINTAINERS: Add entry for RISC-V AIA drivers
> >
> >  .../interrupt-controller/riscv,aplic.yaml     |  136 ++
> >  .../interrupt-controller/riscv,imsic.yaml     |  174 +++
> >  MAINTAINERS                                   |   12 +
> >  arch/riscv/Kconfig.socs                       |    2 +
> >  arch/riscv/include/asm/csr.h                  |   92 ++
> >  arch/riscv/include/asm/hwcap.h                |    8 +
> >  arch/riscv/kernel/cpu.c                       |    2 +
> >  arch/riscv/kernel/cpufeature.c                |    2 +
> >  drivers/irqchip/Kconfig                       |   32 +-
> >  drivers/irqchip/Makefile                      |    2 +
> >  drivers/irqchip/irq-riscv-aplic.c             |  656 +++++++++
> >  drivers/irqchip/irq-riscv-imsic.c             | 1207 +++++++++++++++++
> >  drivers/irqchip/irq-riscv-intc.c              |   37 +-
> >  include/linux/irqchip/riscv-aplic.h           |  117 ++
> >  include/linux/irqchip/riscv-imsic.h           |   92 ++
> >  15 files changed, 2564 insertions(+), 7 deletions(-)
> >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> >  create mode 100644 drivers/irqchip/irq-riscv-aplic.c
> >  create mode 100644 drivers/irqchip/irq-riscv-imsic.c
> >  create mode 100644 include/linux/irqchip/riscv-aplic.h
> >  create mode 100644 include/linux/irqchip/riscv-imsic.h
> >
> > --
> > 2.34.1
> >
>
> I am seeing the following boot failure with your branch and upstream
> qemu (tag: v7.2.0-rc0).
> It seems IPIs are probably not getting delivered after a point. I saw
> that IPIs are delivered in the same path earlier (via gdb).
>
> [    0.990152] NET: Registered PF_INET6 protocol family
> [    1.004885] Segment Routing with IPv6
> [    1.005385] In-situ OAM (IOAM) with IPv6
> [    1.006371] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
> [    1.011609] NET: Registered PF_PACKET protocol family
> [    1.014877] 9pnet: Installing 9P2000 support
> [    1.015603] Key type dns_resolver registered
> [    1.026396] debug_vm_pgtable: [debug_vm_pgtable         ]:
> Validating architecture page table helpers
> [    1.144097] EXT4-fs (vda2): recovery complete
> [    1.146345] EXT4-fs (vda2): mounted filesystem with ordered data
> mode. Quota mode: disabled.
> [    1.147020] VFS: Mounted root (ext4 filesystem) on device 254:2.
> [    1.152238] devtmpfs: mounted
> [    1.183914] Freeing unused kernel image (initmem) memory: 2176K
> [    1.185898] Run /sbin/init as init process
> [   29.412287] rcu: INFO: rcu_sched self-detected stall on CPU
> [   29.412992] rcu:     3-....: (5250 ticks this GP)
> idle=fc3c/1/0x4000000000000002 softirq=78/78 fqs=2230
> [   29.413427]  (t=5251 jiffies g=-1047 q=3 ncpus=8)
> [   29.414199] CPU: 3 PID: 1 Comm: init Not tainted
> 6.1.0-rc4-00024-g5b711f2d7b91 #198
> [   29.414578] Hardware name: riscv-virtio,qemu (DT)
> [   29.414896] epc : smp_call_function_many_cond+0x138/0x372
> [   29.415157]  ra : smp_call_function_many_cond+0x154/0x372
> [   29.415318] epc : ffffffff8008d2e6 ra : ffffffff8008d302 sp :
> ff2000000004bb40
> [   29.415512]  gp : ffffffff812e9eb0 tp : ff600000016d8000 t0 :
> ff6000007ed88610
> [   29.415686]  t1 : 00000000000000ff t2 : 0000000000000002 s0 :
> ff2000000004bc00
> [   29.415837]  s1 : ff6000007ed85448 a0 : 0000000000000007 a1 :
> 00000000000000f7
> [   29.416008]  a2 : 0000000000000000 a3 : 0000000000000000 a4 :
> ff6000007edd1780
> [   29.416188]  a5 : 0000000000000001 a6 : ffffffff812eb1c0 a7 :
> ff600000016d8000
> [   29.416613]  s2 : ffffffff81323c30 s3 : ffffffff812e9964 s4 :
> 0000000000000000
> [   29.416810]  s5 : 0000000000000000 s6 : ff6000007ed85440 s7 :
> 0000000000000038
> [   29.416997]  s8 : 0000000000000003 s9 : ffffffff81323c30 s10:
> ff6000007ed85448
> [   29.417159]  s11: 0000000000000008 t3 : 00ffffffad08a000 t4 :
> ff60000001613e0c
> [   29.417331]  t5 : 0000000000000000 t6 : 00ffffffad177fff
> [   29.417482] status: 0000000200000120 badaddr: 0000000000000000
> cause: 8000000000000005
> [   29.417939] [<ffffffff8008d590>] on_each_cpu_cond_mask+0x20/0x32
> [   29.418179] [<ffffffff80008d32>] flush_icache_all+0x38/0x40
> [   29.418324] [<ffffffff80008eb2>] flush_icache_pte+0x4a/0x7a
> [   29.418442] [<ffffffff80139e00>] do_set_pte+0x132/0x192
> [   29.418594] [<ffffffff8010b42e>] filemap_map_pages+0x178/0x3a0
> [   29.418738] [<ffffffff8013ad00>] __handle_mm_fault+0x992/0xbac
> [   29.418876] [<ffffffff8013afde>] handle_mm_fault+0xc4/0x1d4
> [   29.419010] [<ffffffff80008372>] do_page_fault+0x120/0x326
> [   29.419145] [<ffffffff800033e6>] ret_from_exception+0x0/0xc

This has been addressed in the RISC-V IPI improvement series.

Regards,
Anup

>
>
>
> --
> Regards,
> Atish

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

* Re: [PATCH 0/9] Linux RISC-V AIA Support
@ 2023-01-02 10:05     ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2023-01-02 10:05 UTC (permalink / raw)
  To: Atish Patra
  Cc: Anup Patel, Palmer Dabbelt, Paul Walmsley, Thomas Gleixner,
	Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Alistair Francis,
	linux-riscv, linux-kernel, devicetree

On Fri, Nov 11, 2022 at 2:37 PM Atish Patra <atishp@atishpatra.org> wrote:
>
> On Thu, Nov 10, 2022 at 8:42 PM Anup Patel <apatel@ventanamicro.com> wrote:
> >
> > The RISC-V AIA specification is now frozen as-per the RISC-V international
> > process. The latest frozen specifcation can be found at:
> > https://github.com/riscv/riscv-aia/releases/download/1.0-RC1/riscv-interrupts-1.0-RC1.pdf
> >
> > At a high-level, the AIA specification adds three things:
> > 1) AIA CSRs
> >    - Improved local interrupt support
> > 2) Incoming Message Signaled Interrupt Controller (IMSIC)
> >    - Per-HART MSI controller
> >    - Support MSI virtualization
> >    - Support IPI along with virtualization
> > 3) Advanced Platform-Level Interrupt Controller (APLIC)
> >    - Wired interrupt controller
> >    - In MSI-mode, converts wired interrupt into MSIs (i.e. MSI generator)
> >    - In Direct-mode, injects external interrupts directly into HARTs
> >
> > For an overview of the AIA specification, refer the recent AIA virtualization
> > talk at KVM Forum 2022:
> > https://static.sched.com/hosted_files/kvmforum2022/a1/AIA_Virtualization_in_KVM_RISCV_final.pdf
> > https://www.youtube.com/watch?v=r071dL8Z0yo
> >
> > This series adds required Linux irqchip drivers for AIA and it depends on
> > the recent "RISC-V IPI Improvements".
> > (Refer, https://lore.kernel.org/lkml/20221101143400.690000-1-apatel@ventanamicro.com/t/)
> >
> > To test this series, use QEMU v7.1 (or higher) and OpenSBI v1.1 (or higher).
> >
> > These patches can also be found in the riscv_aia_v1 branch at:
> > https://github.com/avpatel/linux.git
> >
> > Anup Patel (9):
> >   RISC-V: Add AIA related CSR defines
> >   RISC-V: Detect AIA CSRs from ISA string
> >   irqchip/riscv-intc: Add support for RISC-V AIA
> >   dt-bindings: Add RISC-V incoming MSI controller bindings
> >   irqchip: Add RISC-V incoming MSI controller driver
> >   dt-bindings: Add RISC-V advanced PLIC bindings
> >   irqchip: Add RISC-V advanced PLIC driver
> >   RISC-V: Select APLIC and IMSIC drivers for QEMU virt machine
> >   MAINTAINERS: Add entry for RISC-V AIA drivers
> >
> >  .../interrupt-controller/riscv,aplic.yaml     |  136 ++
> >  .../interrupt-controller/riscv,imsic.yaml     |  174 +++
> >  MAINTAINERS                                   |   12 +
> >  arch/riscv/Kconfig.socs                       |    2 +
> >  arch/riscv/include/asm/csr.h                  |   92 ++
> >  arch/riscv/include/asm/hwcap.h                |    8 +
> >  arch/riscv/kernel/cpu.c                       |    2 +
> >  arch/riscv/kernel/cpufeature.c                |    2 +
> >  drivers/irqchip/Kconfig                       |   32 +-
> >  drivers/irqchip/Makefile                      |    2 +
> >  drivers/irqchip/irq-riscv-aplic.c             |  656 +++++++++
> >  drivers/irqchip/irq-riscv-imsic.c             | 1207 +++++++++++++++++
> >  drivers/irqchip/irq-riscv-intc.c              |   37 +-
> >  include/linux/irqchip/riscv-aplic.h           |  117 ++
> >  include/linux/irqchip/riscv-imsic.h           |   92 ++
> >  15 files changed, 2564 insertions(+), 7 deletions(-)
> >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> >  create mode 100644 drivers/irqchip/irq-riscv-aplic.c
> >  create mode 100644 drivers/irqchip/irq-riscv-imsic.c
> >  create mode 100644 include/linux/irqchip/riscv-aplic.h
> >  create mode 100644 include/linux/irqchip/riscv-imsic.h
> >
> > --
> > 2.34.1
> >
>
> I am seeing the following boot failure with your branch and upstream
> qemu (tag: v7.2.0-rc0).
> It seems IPIs are probably not getting delivered after a point. I saw
> that IPIs are delivered in the same path earlier (via gdb).
>
> [    0.990152] NET: Registered PF_INET6 protocol family
> [    1.004885] Segment Routing with IPv6
> [    1.005385] In-situ OAM (IOAM) with IPv6
> [    1.006371] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
> [    1.011609] NET: Registered PF_PACKET protocol family
> [    1.014877] 9pnet: Installing 9P2000 support
> [    1.015603] Key type dns_resolver registered
> [    1.026396] debug_vm_pgtable: [debug_vm_pgtable         ]:
> Validating architecture page table helpers
> [    1.144097] EXT4-fs (vda2): recovery complete
> [    1.146345] EXT4-fs (vda2): mounted filesystem with ordered data
> mode. Quota mode: disabled.
> [    1.147020] VFS: Mounted root (ext4 filesystem) on device 254:2.
> [    1.152238] devtmpfs: mounted
> [    1.183914] Freeing unused kernel image (initmem) memory: 2176K
> [    1.185898] Run /sbin/init as init process
> [   29.412287] rcu: INFO: rcu_sched self-detected stall on CPU
> [   29.412992] rcu:     3-....: (5250 ticks this GP)
> idle=fc3c/1/0x4000000000000002 softirq=78/78 fqs=2230
> [   29.413427]  (t=5251 jiffies g=-1047 q=3 ncpus=8)
> [   29.414199] CPU: 3 PID: 1 Comm: init Not tainted
> 6.1.0-rc4-00024-g5b711f2d7b91 #198
> [   29.414578] Hardware name: riscv-virtio,qemu (DT)
> [   29.414896] epc : smp_call_function_many_cond+0x138/0x372
> [   29.415157]  ra : smp_call_function_many_cond+0x154/0x372
> [   29.415318] epc : ffffffff8008d2e6 ra : ffffffff8008d302 sp :
> ff2000000004bb40
> [   29.415512]  gp : ffffffff812e9eb0 tp : ff600000016d8000 t0 :
> ff6000007ed88610
> [   29.415686]  t1 : 00000000000000ff t2 : 0000000000000002 s0 :
> ff2000000004bc00
> [   29.415837]  s1 : ff6000007ed85448 a0 : 0000000000000007 a1 :
> 00000000000000f7
> [   29.416008]  a2 : 0000000000000000 a3 : 0000000000000000 a4 :
> ff6000007edd1780
> [   29.416188]  a5 : 0000000000000001 a6 : ffffffff812eb1c0 a7 :
> ff600000016d8000
> [   29.416613]  s2 : ffffffff81323c30 s3 : ffffffff812e9964 s4 :
> 0000000000000000
> [   29.416810]  s5 : 0000000000000000 s6 : ff6000007ed85440 s7 :
> 0000000000000038
> [   29.416997]  s8 : 0000000000000003 s9 : ffffffff81323c30 s10:
> ff6000007ed85448
> [   29.417159]  s11: 0000000000000008 t3 : 00ffffffad08a000 t4 :
> ff60000001613e0c
> [   29.417331]  t5 : 0000000000000000 t6 : 00ffffffad177fff
> [   29.417482] status: 0000000200000120 badaddr: 0000000000000000
> cause: 8000000000000005
> [   29.417939] [<ffffffff8008d590>] on_each_cpu_cond_mask+0x20/0x32
> [   29.418179] [<ffffffff80008d32>] flush_icache_all+0x38/0x40
> [   29.418324] [<ffffffff80008eb2>] flush_icache_pte+0x4a/0x7a
> [   29.418442] [<ffffffff80139e00>] do_set_pte+0x132/0x192
> [   29.418594] [<ffffffff8010b42e>] filemap_map_pages+0x178/0x3a0
> [   29.418738] [<ffffffff8013ad00>] __handle_mm_fault+0x992/0xbac
> [   29.418876] [<ffffffff8013afde>] handle_mm_fault+0xc4/0x1d4
> [   29.419010] [<ffffffff80008372>] do_page_fault+0x120/0x326
> [   29.419145] [<ffffffff800033e6>] ret_from_exception+0x0/0xc

This has been addressed in the RISC-V IPI improvement series.

Regards,
Anup

>
>
>
> --
> Regards,
> Atish

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 0/9] Linux RISC-V AIA Support
  2022-11-11 19:01       ` Atish Patra
@ 2023-01-02 10:06         ` Anup Patel
  -1 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2023-01-02 10:06 UTC (permalink / raw)
  To: Atish Patra
  Cc: Anup Patel, Palmer Dabbelt, Paul Walmsley, Thomas Gleixner,
	Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Alistair Francis,
	linux-riscv, linux-kernel, devicetree

On Sat, Nov 12, 2022 at 12:31 AM Atish Patra <atishp@atishpatra.org> wrote:
>
> On Fri, Nov 11, 2022 at 1:13 AM Atish Patra <atishp@atishpatra.org> wrote:
> >
> > On Fri, Nov 11, 2022 at 1:07 AM Atish Patra <atishp@atishpatra.org> wrote:
> > >
> > > On Thu, Nov 10, 2022 at 8:42 PM Anup Patel <apatel@ventanamicro.com> wrote:
> > > >
> > > > The RISC-V AIA specification is now frozen as-per the RISC-V international
> > > > process. The latest frozen specifcation can be found at:
> > > > https://github.com/riscv/riscv-aia/releases/download/1.0-RC1/riscv-interrupts-1.0-RC1.pdf
> > > >
> > > > At a high-level, the AIA specification adds three things:
> > > > 1) AIA CSRs
> > > >    - Improved local interrupt support
> > > > 2) Incoming Message Signaled Interrupt Controller (IMSIC)
> > > >    - Per-HART MSI controller
> > > >    - Support MSI virtualization
> > > >    - Support IPI along with virtualization
> > > > 3) Advanced Platform-Level Interrupt Controller (APLIC)
> > > >    - Wired interrupt controller
> > > >    - In MSI-mode, converts wired interrupt into MSIs (i.e. MSI generator)
> > > >    - In Direct-mode, injects external interrupts directly into HARTs
> > > >
> > > > For an overview of the AIA specification, refer the recent AIA virtualization
> > > > talk at KVM Forum 2022:
> > > > https://static.sched.com/hosted_files/kvmforum2022/a1/AIA_Virtualization_in_KVM_RISCV_final.pdf
> > > > https://www.youtube.com/watch?v=r071dL8Z0yo
> > > >
> > > > This series adds required Linux irqchip drivers for AIA and it depends on
> > > > the recent "RISC-V IPI Improvements".
> > > > (Refer, https://lore.kernel.org/lkml/20221101143400.690000-1-apatel@ventanamicro.com/t/)
> > > >
> > > > To test this series, use QEMU v7.1 (or higher) and OpenSBI v1.1 (or higher).
> > > >
> > > > These patches can also be found in the riscv_aia_v1 branch at:
> > > > https://github.com/avpatel/linux.git
> > > >
> > > > Anup Patel (9):
> > > >   RISC-V: Add AIA related CSR defines
> > > >   RISC-V: Detect AIA CSRs from ISA string
> > > >   irqchip/riscv-intc: Add support for RISC-V AIA
> > > >   dt-bindings: Add RISC-V incoming MSI controller bindings
> > > >   irqchip: Add RISC-V incoming MSI controller driver
> > > >   dt-bindings: Add RISC-V advanced PLIC bindings
> > > >   irqchip: Add RISC-V advanced PLIC driver
> > > >   RISC-V: Select APLIC and IMSIC drivers for QEMU virt machine
> > > >   MAINTAINERS: Add entry for RISC-V AIA drivers
> > > >
> > > >  .../interrupt-controller/riscv,aplic.yaml     |  136 ++
> > > >  .../interrupt-controller/riscv,imsic.yaml     |  174 +++
> > > >  MAINTAINERS                                   |   12 +
> > > >  arch/riscv/Kconfig.socs                       |    2 +
> > > >  arch/riscv/include/asm/csr.h                  |   92 ++
> > > >  arch/riscv/include/asm/hwcap.h                |    8 +
> > > >  arch/riscv/kernel/cpu.c                       |    2 +
> > > >  arch/riscv/kernel/cpufeature.c                |    2 +
> > > >  drivers/irqchip/Kconfig                       |   32 +-
> > > >  drivers/irqchip/Makefile                      |    2 +
> > > >  drivers/irqchip/irq-riscv-aplic.c             |  656 +++++++++
> > > >  drivers/irqchip/irq-riscv-imsic.c             | 1207 +++++++++++++++++
> > > >  drivers/irqchip/irq-riscv-intc.c              |   37 +-
> > > >  include/linux/irqchip/riscv-aplic.h           |  117 ++
> > > >  include/linux/irqchip/riscv-imsic.h           |   92 ++
> > > >  15 files changed, 2564 insertions(+), 7 deletions(-)
> > > >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> > > >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > > >  create mode 100644 drivers/irqchip/irq-riscv-aplic.c
> > > >  create mode 100644 drivers/irqchip/irq-riscv-imsic.c
> > > >  create mode 100644 include/linux/irqchip/riscv-aplic.h
> > > >  create mode 100644 include/linux/irqchip/riscv-imsic.h
> > > >
> > > > --
> > > > 2.34.1
> > > >
> > >
> > > I am seeing the following boot failure with your branch and upstream
> > > qemu (tag: v7.2.0-rc0).
> > > It seems IPIs are probably not getting delivered after a point. I saw
> > > that IPIs are delivered in the same path earlier (via gdb).
> > >
> > > [    0.990152] NET: Registered PF_INET6 protocol family
> > > [    1.004885] Segment Routing with IPv6
> > > [    1.005385] In-situ OAM (IOAM) with IPv6
> > > [    1.006371] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
> > > [    1.011609] NET: Registered PF_PACKET protocol family
> > > [    1.014877] 9pnet: Installing 9P2000 support
> > > [    1.015603] Key type dns_resolver registered
> > > [    1.026396] debug_vm_pgtable: [debug_vm_pgtable         ]:
> > > Validating architecture page table helpers
> > > [    1.144097] EXT4-fs (vda2): recovery complete
> > > [    1.146345] EXT4-fs (vda2): mounted filesystem with ordered data
> > > mode. Quota mode: disabled.
> > > [    1.147020] VFS: Mounted root (ext4 filesystem) on device 254:2.
> > > [    1.152238] devtmpfs: mounted
> > > [    1.183914] Freeing unused kernel image (initmem) memory: 2176K
> > > [    1.185898] Run /sbin/init as init process
> > > [   29.412287] rcu: INFO: rcu_sched self-detected stall on CPU
> > > [   29.412992] rcu:     3-....: (5250 ticks this GP)
> > > idle=fc3c/1/0x4000000000000002 softirq=78/78 fqs=2230
> > > [   29.413427]  (t=5251 jiffies g=-1047 q=3 ncpus=8)
> > > [   29.414199] CPU: 3 PID: 1 Comm: init Not tainted
> > > 6.1.0-rc4-00024-g5b711f2d7b91 #198
> > > [   29.414578] Hardware name: riscv-virtio,qemu (DT)
> > > [   29.414896] epc : smp_call_function_many_cond+0x138/0x372
> > > [   29.415157]  ra : smp_call_function_many_cond+0x154/0x372
> > > [   29.415318] epc : ffffffff8008d2e6 ra : ffffffff8008d302 sp :
> > > ff2000000004bb40
> > > [   29.415512]  gp : ffffffff812e9eb0 tp : ff600000016d8000 t0 :
> > > ff6000007ed88610
> > > [   29.415686]  t1 : 00000000000000ff t2 : 0000000000000002 s0 :
> > > ff2000000004bc00
> > > [   29.415837]  s1 : ff6000007ed85448 a0 : 0000000000000007 a1 :
> > > 00000000000000f7
> > > [   29.416008]  a2 : 0000000000000000 a3 : 0000000000000000 a4 :
> > > ff6000007edd1780
> > > [   29.416188]  a5 : 0000000000000001 a6 : ffffffff812eb1c0 a7 :
> > > ff600000016d8000
> > > [   29.416613]  s2 : ffffffff81323c30 s3 : ffffffff812e9964 s4 :
> > > 0000000000000000
> > > [   29.416810]  s5 : 0000000000000000 s6 : ff6000007ed85440 s7 :
> > > 0000000000000038
> > > [   29.416997]  s8 : 0000000000000003 s9 : ffffffff81323c30 s10:
> > > ff6000007ed85448
> > > [   29.417159]  s11: 0000000000000008 t3 : 00ffffffad08a000 t4 :
> > > ff60000001613e0c
> > > [   29.417331]  t5 : 0000000000000000 t6 : 00ffffffad177fff
> > > [   29.417482] status: 0000000200000120 badaddr: 0000000000000000
> > > cause: 8000000000000005
> > > [   29.417939] [<ffffffff8008d590>] on_each_cpu_cond_mask+0x20/0x32
> > > [   29.418179] [<ffffffff80008d32>] flush_icache_all+0x38/0x40
> > > [   29.418324] [<ffffffff80008eb2>] flush_icache_pte+0x4a/0x7a
> > > [   29.418442] [<ffffffff80139e00>] do_set_pte+0x132/0x192
> > > [   29.418594] [<ffffffff8010b42e>] filemap_map_pages+0x178/0x3a0
> > > [   29.418738] [<ffffffff8013ad00>] __handle_mm_fault+0x992/0xbac
> > > [   29.418876] [<ffffffff8013afde>] handle_mm_fault+0xc4/0x1d4
> > > [   29.419010] [<ffffffff80008372>] do_page_fault+0x120/0x326
> > > [   29.419145] [<ffffffff800033e6>] ret_from_exception+0x0/0xc
> > >
> > >
> >
> > Sorry. I forgot to mention that this happens while booting Fedora on
> > an 8 cpu virt machine.
> > I am yet to reproduce this issue for 4 or 2 cpus.
> >
>
> This warning splat appears with 4 cpus though.
>
> [  126.977213] e1000e 0000:00:02.0 enp0s2: NIC Link is Up 1000 Mbps
> Full Duplex, Flow Control: Rx/Tx
> [  126.988275] IPv6: ADDRCONF(NETDEV_CHANGE): enp0s2: link becomes ready
> [  128.210090] ------------[ cut here ]------------
> [  128.210476] WARNING: CPU: 0 PID: 294 at
> drivers/irqchip/irq-riscv-aplic.c:318 aplic_msi_write_msg+0x110/0x12a
> [  128.211702] Modules linked in:
> [  128.212473] CPU: 0 PID: 294 Comm: (agetty) Not tainted
> 6.1.0-rc4-00024-g5b711f2d7b91 #198
> [  128.212839] Hardware name: riscv-virtio,qemu (DT)
> [  128.213459] epc : aplic_msi_write_msg+0x110/0x12a
> [  128.213652]  ra : aplic_msi_write_msg+0x1c/0x12a
> [  128.213823] epc : ffffffff8039eb80 ra : ffffffff8039ea8c sp :
> ff2000001054bb10
> [  128.213884]  gp : ffffffff812e9eb0 tp : ff600000032d3840 t0 :
> 0000000000000002
> [  128.213945]  t1 : 0000000000000008 t2 : 0000000000000040 s0 :
> ff2000001054bb30
> [  128.214015]  s1 : ff600000018443a8 a0 : ff600000017e8c30 a1 :
> 0000000000000000
> [  128.214083]  a2 : 0000000000000001 a3 : 000000000000000c a4 :
> 0000000000000003
> [  128.214141]  a5 : 0000000000000000 a6 : 0000000000000000 a7 :
> 0000000000000000
> [  128.214173]  s2 : ff2000001054bb40 s3 : ff600000017e8c30 s4 :
> 0000000200000022
> [  128.214246]  s5 : ff600000017e8d78 s6 : 000000000000000c s7 :
> ff600000017e8cb0
> [  128.214295]  s8 : ffffffff80c5c8f0 s9 : ff6000000e97ab60 s10:
> 0000000000000000
> [  128.214327]  s11: ff6000000e97ab78 t3 : 0000000000000004 t4 :
> 0000000000000002
> [  128.214383]  t5 : 0000000000000000 t6 : 0000000000028000
> [  128.214414] status: 0000000200000100 badaddr: 0000000000000000
> cause: 0000000000000003
> [  128.215267] [<ffffffff80453060>] platform_msi_write_msg+0x18/0x20
> [  128.215413] [<ffffffff80060b7a>] msi_domain_deactivate+0x2a/0x48
> [  128.215432] [<ffffffff8005e5a2>] __irq_domain_deactivate_irq+0x1c/0x30
> [  128.215450] [<ffffffff8005e5aa>] __irq_domain_deactivate_irq+0x24/0x30
> [  128.215464] [<ffffffff8005f9ea>] irq_domain_deactivate_irq+0x1c/0x30
> [  128.215477] [<ffffffff8005b8f6>] free_irq+0x1d0/0x27c
> [  128.219393] [<ffffffff8041be06>] univ8250_release_irq+0xa4/0xaa
> [  128.219417] [<ffffffff8041e018>] serial8250_do_shutdown+0xf0/0x166
> [  128.219431] [<ffffffff8041e0a8>] serial8250_shutdown+0x1a/0x22
> [  128.219444] [<ffffffff804174f0>] uart_port_shutdown+0x2c/0x42
> [  128.220230] [<ffffffff8041908a>] uart_shutdown+0x66/0x11e
> [  128.220247] [<ffffffff8041a69a>] uart_hangup+0x5e/0x100
> [  128.220260] [<ffffffff803fdaa2>] __tty_hangup.part.0+0x286/0x2ac
> [  128.220859] [<ffffffff803fec6c>] tty_ioctl+0x684/0x802
> [  128.220875] [<ffffffff8019ec48>] sys_ioctl+0x37e/0x95e
> [  128.221284] [<ffffffff800033d8>] ret_from_syscall+0x0/0x2
> [  128.221653] ---[ end trace 0000000000000000 ]---

I will address this in v2 of the AIA series.

Regards,
Anup

>
>
> > >
> > > --
> > > Regards,
> > > Atish
> >
> >
> >
> > --
> > Regards,
> > Atish
>
>
>
> --
> Regards,
> Atish

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

* Re: [PATCH 0/9] Linux RISC-V AIA Support
@ 2023-01-02 10:06         ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2023-01-02 10:06 UTC (permalink / raw)
  To: Atish Patra
  Cc: Anup Patel, Palmer Dabbelt, Paul Walmsley, Thomas Gleixner,
	Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Alistair Francis,
	linux-riscv, linux-kernel, devicetree

On Sat, Nov 12, 2022 at 12:31 AM Atish Patra <atishp@atishpatra.org> wrote:
>
> On Fri, Nov 11, 2022 at 1:13 AM Atish Patra <atishp@atishpatra.org> wrote:
> >
> > On Fri, Nov 11, 2022 at 1:07 AM Atish Patra <atishp@atishpatra.org> wrote:
> > >
> > > On Thu, Nov 10, 2022 at 8:42 PM Anup Patel <apatel@ventanamicro.com> wrote:
> > > >
> > > > The RISC-V AIA specification is now frozen as-per the RISC-V international
> > > > process. The latest frozen specifcation can be found at:
> > > > https://github.com/riscv/riscv-aia/releases/download/1.0-RC1/riscv-interrupts-1.0-RC1.pdf
> > > >
> > > > At a high-level, the AIA specification adds three things:
> > > > 1) AIA CSRs
> > > >    - Improved local interrupt support
> > > > 2) Incoming Message Signaled Interrupt Controller (IMSIC)
> > > >    - Per-HART MSI controller
> > > >    - Support MSI virtualization
> > > >    - Support IPI along with virtualization
> > > > 3) Advanced Platform-Level Interrupt Controller (APLIC)
> > > >    - Wired interrupt controller
> > > >    - In MSI-mode, converts wired interrupt into MSIs (i.e. MSI generator)
> > > >    - In Direct-mode, injects external interrupts directly into HARTs
> > > >
> > > > For an overview of the AIA specification, refer the recent AIA virtualization
> > > > talk at KVM Forum 2022:
> > > > https://static.sched.com/hosted_files/kvmforum2022/a1/AIA_Virtualization_in_KVM_RISCV_final.pdf
> > > > https://www.youtube.com/watch?v=r071dL8Z0yo
> > > >
> > > > This series adds required Linux irqchip drivers for AIA and it depends on
> > > > the recent "RISC-V IPI Improvements".
> > > > (Refer, https://lore.kernel.org/lkml/20221101143400.690000-1-apatel@ventanamicro.com/t/)
> > > >
> > > > To test this series, use QEMU v7.1 (or higher) and OpenSBI v1.1 (or higher).
> > > >
> > > > These patches can also be found in the riscv_aia_v1 branch at:
> > > > https://github.com/avpatel/linux.git
> > > >
> > > > Anup Patel (9):
> > > >   RISC-V: Add AIA related CSR defines
> > > >   RISC-V: Detect AIA CSRs from ISA string
> > > >   irqchip/riscv-intc: Add support for RISC-V AIA
> > > >   dt-bindings: Add RISC-V incoming MSI controller bindings
> > > >   irqchip: Add RISC-V incoming MSI controller driver
> > > >   dt-bindings: Add RISC-V advanced PLIC bindings
> > > >   irqchip: Add RISC-V advanced PLIC driver
> > > >   RISC-V: Select APLIC and IMSIC drivers for QEMU virt machine
> > > >   MAINTAINERS: Add entry for RISC-V AIA drivers
> > > >
> > > >  .../interrupt-controller/riscv,aplic.yaml     |  136 ++
> > > >  .../interrupt-controller/riscv,imsic.yaml     |  174 +++
> > > >  MAINTAINERS                                   |   12 +
> > > >  arch/riscv/Kconfig.socs                       |    2 +
> > > >  arch/riscv/include/asm/csr.h                  |   92 ++
> > > >  arch/riscv/include/asm/hwcap.h                |    8 +
> > > >  arch/riscv/kernel/cpu.c                       |    2 +
> > > >  arch/riscv/kernel/cpufeature.c                |    2 +
> > > >  drivers/irqchip/Kconfig                       |   32 +-
> > > >  drivers/irqchip/Makefile                      |    2 +
> > > >  drivers/irqchip/irq-riscv-aplic.c             |  656 +++++++++
> > > >  drivers/irqchip/irq-riscv-imsic.c             | 1207 +++++++++++++++++
> > > >  drivers/irqchip/irq-riscv-intc.c              |   37 +-
> > > >  include/linux/irqchip/riscv-aplic.h           |  117 ++
> > > >  include/linux/irqchip/riscv-imsic.h           |   92 ++
> > > >  15 files changed, 2564 insertions(+), 7 deletions(-)
> > > >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> > > >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > > >  create mode 100644 drivers/irqchip/irq-riscv-aplic.c
> > > >  create mode 100644 drivers/irqchip/irq-riscv-imsic.c
> > > >  create mode 100644 include/linux/irqchip/riscv-aplic.h
> > > >  create mode 100644 include/linux/irqchip/riscv-imsic.h
> > > >
> > > > --
> > > > 2.34.1
> > > >
> > >
> > > I am seeing the following boot failure with your branch and upstream
> > > qemu (tag: v7.2.0-rc0).
> > > It seems IPIs are probably not getting delivered after a point. I saw
> > > that IPIs are delivered in the same path earlier (via gdb).
> > >
> > > [    0.990152] NET: Registered PF_INET6 protocol family
> > > [    1.004885] Segment Routing with IPv6
> > > [    1.005385] In-situ OAM (IOAM) with IPv6
> > > [    1.006371] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
> > > [    1.011609] NET: Registered PF_PACKET protocol family
> > > [    1.014877] 9pnet: Installing 9P2000 support
> > > [    1.015603] Key type dns_resolver registered
> > > [    1.026396] debug_vm_pgtable: [debug_vm_pgtable         ]:
> > > Validating architecture page table helpers
> > > [    1.144097] EXT4-fs (vda2): recovery complete
> > > [    1.146345] EXT4-fs (vda2): mounted filesystem with ordered data
> > > mode. Quota mode: disabled.
> > > [    1.147020] VFS: Mounted root (ext4 filesystem) on device 254:2.
> > > [    1.152238] devtmpfs: mounted
> > > [    1.183914] Freeing unused kernel image (initmem) memory: 2176K
> > > [    1.185898] Run /sbin/init as init process
> > > [   29.412287] rcu: INFO: rcu_sched self-detected stall on CPU
> > > [   29.412992] rcu:     3-....: (5250 ticks this GP)
> > > idle=fc3c/1/0x4000000000000002 softirq=78/78 fqs=2230
> > > [   29.413427]  (t=5251 jiffies g=-1047 q=3 ncpus=8)
> > > [   29.414199] CPU: 3 PID: 1 Comm: init Not tainted
> > > 6.1.0-rc4-00024-g5b711f2d7b91 #198
> > > [   29.414578] Hardware name: riscv-virtio,qemu (DT)
> > > [   29.414896] epc : smp_call_function_many_cond+0x138/0x372
> > > [   29.415157]  ra : smp_call_function_many_cond+0x154/0x372
> > > [   29.415318] epc : ffffffff8008d2e6 ra : ffffffff8008d302 sp :
> > > ff2000000004bb40
> > > [   29.415512]  gp : ffffffff812e9eb0 tp : ff600000016d8000 t0 :
> > > ff6000007ed88610
> > > [   29.415686]  t1 : 00000000000000ff t2 : 0000000000000002 s0 :
> > > ff2000000004bc00
> > > [   29.415837]  s1 : ff6000007ed85448 a0 : 0000000000000007 a1 :
> > > 00000000000000f7
> > > [   29.416008]  a2 : 0000000000000000 a3 : 0000000000000000 a4 :
> > > ff6000007edd1780
> > > [   29.416188]  a5 : 0000000000000001 a6 : ffffffff812eb1c0 a7 :
> > > ff600000016d8000
> > > [   29.416613]  s2 : ffffffff81323c30 s3 : ffffffff812e9964 s4 :
> > > 0000000000000000
> > > [   29.416810]  s5 : 0000000000000000 s6 : ff6000007ed85440 s7 :
> > > 0000000000000038
> > > [   29.416997]  s8 : 0000000000000003 s9 : ffffffff81323c30 s10:
> > > ff6000007ed85448
> > > [   29.417159]  s11: 0000000000000008 t3 : 00ffffffad08a000 t4 :
> > > ff60000001613e0c
> > > [   29.417331]  t5 : 0000000000000000 t6 : 00ffffffad177fff
> > > [   29.417482] status: 0000000200000120 badaddr: 0000000000000000
> > > cause: 8000000000000005
> > > [   29.417939] [<ffffffff8008d590>] on_each_cpu_cond_mask+0x20/0x32
> > > [   29.418179] [<ffffffff80008d32>] flush_icache_all+0x38/0x40
> > > [   29.418324] [<ffffffff80008eb2>] flush_icache_pte+0x4a/0x7a
> > > [   29.418442] [<ffffffff80139e00>] do_set_pte+0x132/0x192
> > > [   29.418594] [<ffffffff8010b42e>] filemap_map_pages+0x178/0x3a0
> > > [   29.418738] [<ffffffff8013ad00>] __handle_mm_fault+0x992/0xbac
> > > [   29.418876] [<ffffffff8013afde>] handle_mm_fault+0xc4/0x1d4
> > > [   29.419010] [<ffffffff80008372>] do_page_fault+0x120/0x326
> > > [   29.419145] [<ffffffff800033e6>] ret_from_exception+0x0/0xc
> > >
> > >
> >
> > Sorry. I forgot to mention that this happens while booting Fedora on
> > an 8 cpu virt machine.
> > I am yet to reproduce this issue for 4 or 2 cpus.
> >
>
> This warning splat appears with 4 cpus though.
>
> [  126.977213] e1000e 0000:00:02.0 enp0s2: NIC Link is Up 1000 Mbps
> Full Duplex, Flow Control: Rx/Tx
> [  126.988275] IPv6: ADDRCONF(NETDEV_CHANGE): enp0s2: link becomes ready
> [  128.210090] ------------[ cut here ]------------
> [  128.210476] WARNING: CPU: 0 PID: 294 at
> drivers/irqchip/irq-riscv-aplic.c:318 aplic_msi_write_msg+0x110/0x12a
> [  128.211702] Modules linked in:
> [  128.212473] CPU: 0 PID: 294 Comm: (agetty) Not tainted
> 6.1.0-rc4-00024-g5b711f2d7b91 #198
> [  128.212839] Hardware name: riscv-virtio,qemu (DT)
> [  128.213459] epc : aplic_msi_write_msg+0x110/0x12a
> [  128.213652]  ra : aplic_msi_write_msg+0x1c/0x12a
> [  128.213823] epc : ffffffff8039eb80 ra : ffffffff8039ea8c sp :
> ff2000001054bb10
> [  128.213884]  gp : ffffffff812e9eb0 tp : ff600000032d3840 t0 :
> 0000000000000002
> [  128.213945]  t1 : 0000000000000008 t2 : 0000000000000040 s0 :
> ff2000001054bb30
> [  128.214015]  s1 : ff600000018443a8 a0 : ff600000017e8c30 a1 :
> 0000000000000000
> [  128.214083]  a2 : 0000000000000001 a3 : 000000000000000c a4 :
> 0000000000000003
> [  128.214141]  a5 : 0000000000000000 a6 : 0000000000000000 a7 :
> 0000000000000000
> [  128.214173]  s2 : ff2000001054bb40 s3 : ff600000017e8c30 s4 :
> 0000000200000022
> [  128.214246]  s5 : ff600000017e8d78 s6 : 000000000000000c s7 :
> ff600000017e8cb0
> [  128.214295]  s8 : ffffffff80c5c8f0 s9 : ff6000000e97ab60 s10:
> 0000000000000000
> [  128.214327]  s11: ff6000000e97ab78 t3 : 0000000000000004 t4 :
> 0000000000000002
> [  128.214383]  t5 : 0000000000000000 t6 : 0000000000028000
> [  128.214414] status: 0000000200000100 badaddr: 0000000000000000
> cause: 0000000000000003
> [  128.215267] [<ffffffff80453060>] platform_msi_write_msg+0x18/0x20
> [  128.215413] [<ffffffff80060b7a>] msi_domain_deactivate+0x2a/0x48
> [  128.215432] [<ffffffff8005e5a2>] __irq_domain_deactivate_irq+0x1c/0x30
> [  128.215450] [<ffffffff8005e5aa>] __irq_domain_deactivate_irq+0x24/0x30
> [  128.215464] [<ffffffff8005f9ea>] irq_domain_deactivate_irq+0x1c/0x30
> [  128.215477] [<ffffffff8005b8f6>] free_irq+0x1d0/0x27c
> [  128.219393] [<ffffffff8041be06>] univ8250_release_irq+0xa4/0xaa
> [  128.219417] [<ffffffff8041e018>] serial8250_do_shutdown+0xf0/0x166
> [  128.219431] [<ffffffff8041e0a8>] serial8250_shutdown+0x1a/0x22
> [  128.219444] [<ffffffff804174f0>] uart_port_shutdown+0x2c/0x42
> [  128.220230] [<ffffffff8041908a>] uart_shutdown+0x66/0x11e
> [  128.220247] [<ffffffff8041a69a>] uart_hangup+0x5e/0x100
> [  128.220260] [<ffffffff803fdaa2>] __tty_hangup.part.0+0x286/0x2ac
> [  128.220859] [<ffffffff803fec6c>] tty_ioctl+0x684/0x802
> [  128.220875] [<ffffffff8019ec48>] sys_ioctl+0x37e/0x95e
> [  128.221284] [<ffffffff800033d8>] ret_from_syscall+0x0/0x2
> [  128.221653] ---[ end trace 0000000000000000 ]---

I will address this in v2 of the AIA series.

Regards,
Anup

>
>
> > >
> > > --
> > > Regards,
> > > Atish
> >
> >
> >
> > --
> > Regards,
> > Atish
>
>
>
> --
> Regards,
> Atish

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
  2022-11-16 19:14         ` Rob Herring
@ 2023-01-02 15:59           ` Anup Patel
  -1 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2023-01-02 15:59 UTC (permalink / raw)
  To: Rob Herring
  Cc: Anup Patel, Krzysztof Kozlowski, Palmer Dabbelt, Paul Walmsley,
	Thomas Gleixner, Marc Zyngier, Krzysztof Kozlowski, Atish Patra,
	Alistair Francis, linux-riscv, linux-kernel, devicetree

On Thu, Nov 17, 2022 at 12:44 AM Rob Herring <robh@kernel.org> wrote:
>
> On Mon, Nov 14, 2022 at 05:36:06PM +0530, Anup Patel wrote:
> > On Mon, Nov 14, 2022 at 3:19 PM Krzysztof Kozlowski
> > <krzysztof.kozlowski@linaro.org> wrote:
> > >
> > > On 11/11/2022 05:42, Anup Patel wrote:
> > > > We add DT bindings document for RISC-V incoming MSI controller (IMSIC)
> > > > defined by the RISC-V advanced interrupt architecture (AIA) specification.
> > > >
> > > > Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> > > > ---
> > > >  .../interrupt-controller/riscv,imsic.yaml     | 174 ++++++++++++++++++
> > > >  1 file changed, 174 insertions(+)
> > > >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > > >
> > > > diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > > > new file mode 100644
> > > > index 000000000000..05106eb1955e
> > > > --- /dev/null
> > > > +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > > > @@ -0,0 +1,174 @@
> > > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > > +%YAML 1.2
> > > > +---
> > > > +$id: http://devicetree.org/schemas/interrupt-controller/riscv,imsic.yaml#
> > > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > > +
> > > > +title: RISC-V Incoming MSI Controller (IMSIC)
> > > > +
> > > > +maintainers:
> > > > +  - Anup Patel <anup@brainfault.org>
> > > > +
> > > > +description:
> > > > +  The RISC-V advanced interrupt architecture (AIA) defines a per-CPU incoming
> > > > +  MSI controller (IMSIC) for handling MSIs in a RISC-V platform. The RISC-V
> > > > +  AIA specification can be found at https://github.com/riscv/riscv-aia.
> > > > +
> > > > +  The IMSIC is a per-CPU (or per-HART) device with separate interrupt file
> > > > +  for each privilege level (machine or supervisor). The configuration of
> > > > +  a IMSIC interrupt file is done using AIA CSRs and it also has a 4KB MMIO
> > > > +  space to receive MSIs from devices. Each IMSIC interrupt file supports a
> > > > +  fixed number of interrupt identities (to distinguish MSIs from devices)
> > > > +  which is same for given privilege level across CPUs (or HARTs).
> > > > +
> > > > +  The arrangement of IMSIC interrupt files in MMIO space of a RISC-V platform
> > > > +  follows a particular scheme defined by the RISC-V AIA specification. A IMSIC
> > > > +  group is a set of IMSIC interrupt files co-located in MMIO space and we can
> > > > +  have multiple IMSIC groups (i.e. clusters, sockets, chiplets, etc) in a
> > > > +  RISC-V platform. The MSI target address of a IMSIC interrupt file at given
> > > > +  privilege level (machine or supervisor) encodes group index, HART index,
> > > > +  and guest index (shown below).
> > > > +
> > > > +  XLEN-1           >=24                                 12    0
> > > > +  |                  |                                  |     |
> > > > +  -------------------------------------------------------------
> > > > +  |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
> > > > +  -------------------------------------------------------------
> > > > +
> > > > +  The device tree of a RISC-V platform will have one IMSIC device tree node
> > > > +  for each privilege level (machine or supervisor) which collectively describe
> > > > +  IMSIC interrupt files at that privilege level across CPUs (or HARTs).
> > > > +
> > > > +allOf:
> > > > +  - $ref: /schemas/interrupt-controller.yaml#
> > > > +
> > > > +properties:
> > > > +  compatible:
> > > > +    items:
> > > > +      - enum:
> > > > +          - vendor,chip-imsics
> > >
> > > There is no such vendor... As Conor pointed out, this does not look
> > > correct. Compatibles must be real and specific.
> >
> > Previously, Rob had suggest to:
> > 1) Mandate two compatible strings: one for implementation and
> >     and second for specification
> > 2) Since this is new specification with QEMU being the only
> >     implementation, we add "vendor,chip-imsics" as dummy
> >     implementation specific string for DT schema checkers
> >     to pass the examples. Once we have an actual implementation,
> >    we will replace this dummy string.
>
> What will QEMU's DT use? That's an implementation we can and do run
> validation on. Your choices are define a QEMU specific compatible string
> or allow the fallback alone. I'm fine either way. With the latter,
> someone has to review that the fallback is not used alone in .dts files
> while doing the former allows the tools to check for you. It also
> encourages making every new difference a property rather than implied by
> compatible, but those should be caught in review.
>
> If you go with the fallback only, just make it clear that it's for QEMU
> or s/w models only.

Sure, I will add  "riscv,qemu-imsics" as QEMU specific compatible string.

Regards,
Anup

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

* Re: [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings
@ 2023-01-02 15:59           ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2023-01-02 15:59 UTC (permalink / raw)
  To: Rob Herring
  Cc: Anup Patel, Krzysztof Kozlowski, Palmer Dabbelt, Paul Walmsley,
	Thomas Gleixner, Marc Zyngier, Krzysztof Kozlowski, Atish Patra,
	Alistair Francis, linux-riscv, linux-kernel, devicetree

On Thu, Nov 17, 2022 at 12:44 AM Rob Herring <robh@kernel.org> wrote:
>
> On Mon, Nov 14, 2022 at 05:36:06PM +0530, Anup Patel wrote:
> > On Mon, Nov 14, 2022 at 3:19 PM Krzysztof Kozlowski
> > <krzysztof.kozlowski@linaro.org> wrote:
> > >
> > > On 11/11/2022 05:42, Anup Patel wrote:
> > > > We add DT bindings document for RISC-V incoming MSI controller (IMSIC)
> > > > defined by the RISC-V advanced interrupt architecture (AIA) specification.
> > > >
> > > > Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> > > > ---
> > > >  .../interrupt-controller/riscv,imsic.yaml     | 174 ++++++++++++++++++
> > > >  1 file changed, 174 insertions(+)
> > > >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > > >
> > > > diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > > > new file mode 100644
> > > > index 000000000000..05106eb1955e
> > > > --- /dev/null
> > > > +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,imsic.yaml
> > > > @@ -0,0 +1,174 @@
> > > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > > +%YAML 1.2
> > > > +---
> > > > +$id: http://devicetree.org/schemas/interrupt-controller/riscv,imsic.yaml#
> > > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > > +
> > > > +title: RISC-V Incoming MSI Controller (IMSIC)
> > > > +
> > > > +maintainers:
> > > > +  - Anup Patel <anup@brainfault.org>
> > > > +
> > > > +description:
> > > > +  The RISC-V advanced interrupt architecture (AIA) defines a per-CPU incoming
> > > > +  MSI controller (IMSIC) for handling MSIs in a RISC-V platform. The RISC-V
> > > > +  AIA specification can be found at https://github.com/riscv/riscv-aia.
> > > > +
> > > > +  The IMSIC is a per-CPU (or per-HART) device with separate interrupt file
> > > > +  for each privilege level (machine or supervisor). The configuration of
> > > > +  a IMSIC interrupt file is done using AIA CSRs and it also has a 4KB MMIO
> > > > +  space to receive MSIs from devices. Each IMSIC interrupt file supports a
> > > > +  fixed number of interrupt identities (to distinguish MSIs from devices)
> > > > +  which is same for given privilege level across CPUs (or HARTs).
> > > > +
> > > > +  The arrangement of IMSIC interrupt files in MMIO space of a RISC-V platform
> > > > +  follows a particular scheme defined by the RISC-V AIA specification. A IMSIC
> > > > +  group is a set of IMSIC interrupt files co-located in MMIO space and we can
> > > > +  have multiple IMSIC groups (i.e. clusters, sockets, chiplets, etc) in a
> > > > +  RISC-V platform. The MSI target address of a IMSIC interrupt file at given
> > > > +  privilege level (machine or supervisor) encodes group index, HART index,
> > > > +  and guest index (shown below).
> > > > +
> > > > +  XLEN-1           >=24                                 12    0
> > > > +  |                  |                                  |     |
> > > > +  -------------------------------------------------------------
> > > > +  |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
> > > > +  -------------------------------------------------------------
> > > > +
> > > > +  The device tree of a RISC-V platform will have one IMSIC device tree node
> > > > +  for each privilege level (machine or supervisor) which collectively describe
> > > > +  IMSIC interrupt files at that privilege level across CPUs (or HARTs).
> > > > +
> > > > +allOf:
> > > > +  - $ref: /schemas/interrupt-controller.yaml#
> > > > +
> > > > +properties:
> > > > +  compatible:
> > > > +    items:
> > > > +      - enum:
> > > > +          - vendor,chip-imsics
> > >
> > > There is no such vendor... As Conor pointed out, this does not look
> > > correct. Compatibles must be real and specific.
> >
> > Previously, Rob had suggest to:
> > 1) Mandate two compatible strings: one for implementation and
> >     and second for specification
> > 2) Since this is new specification with QEMU being the only
> >     implementation, we add "vendor,chip-imsics" as dummy
> >     implementation specific string for DT schema checkers
> >     to pass the examples. Once we have an actual implementation,
> >    we will replace this dummy string.
>
> What will QEMU's DT use? That's an implementation we can and do run
> validation on. Your choices are define a QEMU specific compatible string
> or allow the fallback alone. I'm fine either way. With the latter,
> someone has to review that the fallback is not used alone in .dts files
> while doing the former allows the tools to check for you. It also
> encourages making every new difference a property rather than implied by
> compatible, but those should be caught in review.
>
> If you go with the fallback only, just make it clear that it's for QEMU
> or s/w models only.

Sure, I will add  "riscv,qemu-imsics" as QEMU specific compatible string.

Regards,
Anup

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 5/9] irqchip: Add RISC-V incoming MSI controller driver
  2022-11-11 16:02     ` Andrew Bresticker
@ 2023-01-02 16:25       ` Anup Patel
  -1 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2023-01-02 16:25 UTC (permalink / raw)
  To: Andrew Bresticker
  Cc: Anup Patel, Palmer Dabbelt, Paul Walmsley, Thomas Gleixner,
	Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Atish Patra,
	Alistair Francis, linux-riscv, linux-kernel, devicetree

On Fri, Nov 11, 2022 at 9:32 PM Andrew Bresticker <abrestic@rivosinc.com> wrote:
>
> On Thu, Nov 10, 2022 at 11:44 PM Anup Patel <apatel@ventanamicro.com> wrote:
> >
> > The RISC-V advanced interrupt architecture (AIA) specification defines
> > a new MSI controller for managing MSIs on a RISC-V platform. This new
> > MSI controller is referred to as incoming message signaled interrupt
> > controller (IMSIC) which manages MSI on per-HART (or per-CPU) basis.
> > (For more details refer https://github.com/riscv/riscv-aia)
> >
> > This patch adds an irqchip driver for RISC-V IMSIC found on RISC-V
> > platforms.
> >
> > Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> > ---
> >  drivers/irqchip/Kconfig             |   20 +-
> >  drivers/irqchip/Makefile            |    1 +
> >  drivers/irqchip/irq-riscv-imsic.c   | 1207 +++++++++++++++++++++++++++
> >  include/linux/irqchip/riscv-imsic.h |   92 ++
> >  4 files changed, 1319 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/irqchip/irq-riscv-imsic.c
> >  create mode 100644 include/linux/irqchip/riscv-imsic.h
> >
> > diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> > index 7ef9f5e696d3..8246c08f0fd3 100644
> > --- a/drivers/irqchip/Kconfig
> > +++ b/drivers/irqchip/Kconfig
> > @@ -29,7 +29,6 @@ config ARM_GIC_V2M
> >
> >  config GIC_NON_BANKED
> >         bool
> > -
> >  config ARM_GIC_V3
> >         bool
> >         select IRQ_DOMAIN_HIERARCHY
> > @@ -564,6 +563,25 @@ config SIFIVE_PLIC
> >
> >            If you don't know what to do here, say Y.
> >
> > +config RISCV_IMSIC
> > +       bool "RISC-V Incoming MSI Controller"
> > +       depends on RISCV
> > +       select IRQ_DOMAIN_HIERARCHY
> > +       select GENERIC_MSI_IRQ_DOMAIN
> > +       help
> > +          This enables support for the IMSIC chip found in RISC-V systems.
> > +          The IMSIC controls message signaled interrupts and forwards them
> > +          to each core as wired local interrupt.
> > +
> > +          If you don't know what to do here, say Y.
> > +
> > +config RISCV_IMSIC_PCI
> > +       bool
> > +       depends on RISCV_IMSIC
> > +       depends on PCI
> > +       depends on PCI_MSI
> > +       default RISCV_IMSIC
> > +
> >  config EXYNOS_IRQ_COMBINER
> >         bool "Samsung Exynos IRQ combiner support" if COMPILE_TEST
> >         depends on (ARCH_EXYNOS && ARM) || COMPILE_TEST
> > diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> > index 87b49a10962c..22c723cc6ec8 100644
> > --- a/drivers/irqchip/Makefile
> > +++ b/drivers/irqchip/Makefile
> > @@ -96,6 +96,7 @@ obj-$(CONFIG_QCOM_MPM)                        += irq-qcom-mpm.o
> >  obj-$(CONFIG_CSKY_MPINTC)              += irq-csky-mpintc.o
> >  obj-$(CONFIG_CSKY_APB_INTC)            += irq-csky-apb-intc.o
> >  obj-$(CONFIG_RISCV_INTC)               += irq-riscv-intc.o
> > +obj-$(CONFIG_RISCV_IMSIC)              += irq-riscv-imsic.o
> >  obj-$(CONFIG_SIFIVE_PLIC)              += irq-sifive-plic.o
> >  obj-$(CONFIG_IMX_IRQSTEER)             += irq-imx-irqsteer.o
> >  obj-$(CONFIG_IMX_INTMUX)               += irq-imx-intmux.o
> > diff --git a/drivers/irqchip/irq-riscv-imsic.c b/drivers/irqchip/irq-riscv-imsic.c
> > new file mode 100644
> > index 000000000000..95324fb4f5ed
> > --- /dev/null
> > +++ b/drivers/irqchip/irq-riscv-imsic.c
> > @@ -0,0 +1,1207 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2021 Western Digital Corporation or its affiliates.
> > + * Copyright (C) 2022 Ventana Micro Systems Inc.
> > + */
> > +
> > +#define pr_fmt(fmt) "riscv-imsic: " fmt
> > +#include <linux/bitmap.h>
> > +#include <linux/cpu.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/iommu.h>
> > +#include <linux/irq.h>
> > +#include <linux/irqchip.h>
> > +#include <linux/irqchip/chained_irq.h>
> > +#include <linux/irqchip/riscv-imsic.h>
> > +#include <linux/irqdomain.h>
> > +#include <linux/module.h>
> > +#include <linux/msi.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/pci.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/smp.h>
> > +#include <asm/hwcap.h>
> > +
> > +#define IMSIC_DISABLE_EIDELIVERY       0
> > +#define IMSIC_ENABLE_EIDELIVERY                1
> > +#define IMSIC_DISABLE_EITHRESHOLD      1
> > +#define IMSIC_ENABLE_EITHRESHOLD       0
> > +
> > +#define imsic_csr_write(__c, __v)      \
> > +do {                                   \
> > +       csr_write(CSR_ISELECT, __c);    \
> > +       csr_write(CSR_IREG, __v);       \
> > +} while (0)
> > +
> > +#define imsic_csr_read(__c)            \
> > +({                                     \
> > +       unsigned long __v;              \
> > +       csr_write(CSR_ISELECT, __c);    \
> > +       __v = csr_read(CSR_IREG);       \
> > +       __v;                            \
> > +})
> > +
> > +#define imsic_csr_set(__c, __v)                \
> > +do {                                   \
> > +       csr_write(CSR_ISELECT, __c);    \
> > +       csr_set(CSR_IREG, __v);         \
> > +} while (0)
> > +
> > +#define imsic_csr_clear(__c, __v)      \
> > +do {                                   \
> > +       csr_write(CSR_ISELECT, __c);    \
> > +       csr_clear(CSR_IREG, __v);       \
> > +} while (0)
> > +
> > +struct imsic_mmio {
> > +       phys_addr_t pa;
> > +       void __iomem *va;
> > +       unsigned long size;
> > +};
> > +
> > +struct imsic_priv {
> > +       /* Global configuration common for all HARTs */
> > +       struct imsic_global_config global;
> > +
> > +       /* MMIO regions */
> > +       u32 num_mmios;
> > +       struct imsic_mmio *mmios;
> > +
> > +       /* Global state of interrupt identities */
> > +       raw_spinlock_t ids_lock;
> > +       unsigned long *ids_used_bimap;
> > +       unsigned long *ids_enabled_bimap;
> > +       unsigned int *ids_target_cpu;
> > +
> > +       /* Mask for connected CPUs */
> > +       struct cpumask lmask;
> > +
> > +       /* IPI interrupt identity */
> > +       bool slow_ipi;
> > +       u32 ipi_id;
> > +       u32 ipi_lsync_id;
> > +
> > +       /* IRQ domains */
> > +       struct irq_domain *base_domain;
> > +       struct irq_domain *pci_domain;
> > +       struct irq_domain *plat_domain;
> > +};
> > +
> > +struct imsic_handler {
> > +       /* Local configuration for given HART */
> > +       struct imsic_local_config local;
> > +
> > +       /* Pointer to private context */
> > +       struct imsic_priv *priv;
> > +};
> > +
> > +static bool imsic_init_done;
> > +
> > +static int imsic_parent_irq;
> > +static DEFINE_PER_CPU(struct imsic_handler, imsic_handlers);
> > +
> > +const struct imsic_global_config *imsic_get_global_config(void)
> > +{
> > +       struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
> > +
> > +       if (!handler || !handler->priv)
> > +               return NULL;
> > +
> > +       return &handler->priv->global;
> > +}
> > +EXPORT_SYMBOL_GPL(imsic_get_global_config);
> > +
> > +const struct imsic_local_config *imsic_get_local_config(unsigned int cpu)
> > +{
> > +       struct imsic_handler *handler = per_cpu_ptr(&imsic_handlers, cpu);
> > +
> > +       if (!handler || !handler->priv)
> > +               return NULL;
> > +
> > +       return &handler->local;
> > +}
> > +EXPORT_SYMBOL_GPL(imsic_get_local_config);
> > +
> > +static int imsic_cpu_page_phys(unsigned int cpu,
> > +                              unsigned int guest_index,
> > +                              phys_addr_t *out_msi_pa)
> > +{
> > +       struct imsic_handler *handler = per_cpu_ptr(&imsic_handlers, cpu);
> > +       struct imsic_global_config *global;
> > +       struct imsic_local_config *local;
> > +
> > +       if (!handler || !handler->priv)
> > +               return -ENODEV;
> > +       local = &handler->local;
> > +       global = &handler->priv->global;
> > +
> > +       if (BIT(global->guest_index_bits) <= guest_index)
> > +               return -EINVAL;
> > +
> > +       if (out_msi_pa)
> > +               *out_msi_pa = local->msi_pa +
> > +                             (guest_index * IMSIC_MMIO_PAGE_SZ);
> > +
> > +       return 0;
> > +}
> > +
> > +static int imsic_get_cpu(struct imsic_priv *priv,
> > +                        const struct cpumask *mask_val, bool force,
> > +                        unsigned int *out_target_cpu)
> > +{
> > +       struct cpumask amask;
> > +       unsigned int cpu;
> > +
> > +       cpumask_and(&amask, &priv->lmask, mask_val);
> > +
> > +       if (force)
> > +               cpu = cpumask_first(&amask);
> > +       else
> > +               cpu = cpumask_any_and(&amask, cpu_online_mask);
> > +
> > +       if (cpu >= nr_cpu_ids)
> > +               return -EINVAL;
> > +
> > +       if (out_target_cpu)
> > +               *out_target_cpu = cpu;
> > +
> > +       return 0;
> > +}
> > +
> > +static int imsic_get_cpu_msi_msg(unsigned int cpu, unsigned int id,
> > +                                struct msi_msg *msg)
> > +{
> > +       phys_addr_t msi_addr;
> > +       int err;
> > +
> > +       err = imsic_cpu_page_phys(cpu, 0, &msi_addr);
> > +       if (err)
> > +               return err;
> > +
> > +       msg->address_hi = upper_32_bits(msi_addr);
> > +       msg->address_lo = lower_32_bits(msi_addr);
> > +       msg->data = id;
> > +
> > +       return err;
> > +}
> > +
> > +static void imsic_id_set_target(struct imsic_priv *priv,
> > +                                unsigned int id, unsigned int target_cpu)
> > +{
> > +       raw_spin_lock(&priv->ids_lock);
> > +       priv->ids_target_cpu[id] = target_cpu;
> > +       raw_spin_unlock(&priv->ids_lock);
> > +}
> > +
> > +static unsigned int imsic_id_get_target(struct imsic_priv *priv,
> > +                                       unsigned int id)
> > +{
> > +       unsigned int ret;
> > +
> > +       raw_spin_lock(&priv->ids_lock);
>
> This needs to be a raw_spin_lock_irqsave() since hardirqs are enabled
> here and we also take priv->ids_lock in hardirq context (via
> imsic_ids_local_sync()). Booting with lockdep enabled produces this
> splat:
>
> [    1.830055] ================================
> [    1.830227] WARNING: inconsistent lock state
> [    1.830425] 6.1.0-rc4-00026-g78e09ffc5534 #42 Not tainted
> [    1.830655] --------------------------------
> [    1.830784] inconsistent {HARDIRQ-ON-W} -> {IN-HARDIRQ-W} usage.
> [    1.830972] swapper/0/1 [HC1[1]:SC0[0]:HE0:SE1] takes:
> [    1.831162] ff6000000229d048 (&priv->ids_lock){?.+.}-{2:2}, at:
> imsic_ids_local_sync+0x1e/0x98
> [    1.831951] {HARDIRQ-ON-W} state was registered at:
> [    1.832132]   __lock_acquire+0x93c/0x1e0c
> [    1.832305]   lock_acquire+0xfa/0x2da
> [    1.832412]   _raw_spin_lock+0x2c/0x40
> [    1.832531]   imsic_ids_local_sync+0x1e/0x98
> [    1.832674]   imsic_starting_cpu+0x72/0xac
> [    1.832852]   cpuhp_invoke_callback+0x18c/0x93c
> [    1.833033]   cpuhp_thread_fun+0x156/0x1b0
> [    1.833158]   smpboot_thread_fn+0xea/0x1c8
> [    1.833293]   kthread+0xc8/0xde
> [    1.833391]   ret_from_exception+0x0/0x10
> [    1.833530] irq event stamp: 33336
> [    1.833647] hardirqs last  enabled at (33335): [<ffffffff808f2fcc>]
> _raw_spin_unlock_irqrestore+0x4c/0x4e
> [    1.833925] hardirqs last disabled at (33336): [<ffffffff800082f4>]
> __trace_hardirqs_off+0xc/0x14
> [    1.834179] softirqs last  enabled at (33056): [<ffffffff808f3f3e>]
> __do_softirq+0x3de/0x51e
> [    1.834450] softirqs last disabled at (33051): [<ffffffff8001788a>]
> irq_exit+0xd6/0x104
> [    1.834705]
> [    1.834705] other info that might help us debug this:
> [    1.834925]  Possible unsafe locking scenario:
> [    1.834925]
> [    1.835093]        CPU0
> [    1.835176]        ----
> [    1.835253]   lock(&priv->ids_lock);
> [    1.835381]   <Interrupt>
> [    1.835462]     lock(&priv->ids_lock);
> ...
>
> Applies here and elsewhere where priv->ids_lock is taken in a possibly
> hardirqs-enabled context.

Okay, I will use raw_spin_lock_irqsave() here.

Thanks,
Anup

>
> -Andrew
>
> > +       ret = priv->ids_target_cpu[id];
> > +       raw_spin_unlock(&priv->ids_lock);
> > +
> > +       return ret;
> > +}
> > +
> > +static void __imsic_eix_update(unsigned long base_id,
> > +                              unsigned long num_id, bool pend, bool val)
> > +{
> > +       unsigned long i, isel, ireg, flags;
> > +       unsigned long id = base_id, last_id = base_id + num_id;
> > +
> > +       while (id < last_id) {
> > +               isel = id / BITS_PER_LONG;
> > +               isel *= BITS_PER_LONG / IMSIC_EIPx_BITS;
> > +               isel += (pend) ? IMSIC_EIP0 : IMSIC_EIE0;
> > +
> > +               ireg = 0;
> > +               for (i = id & (__riscv_xlen - 1);
> > +                    (id < last_id) && (i < __riscv_xlen); i++) {
> > +                       ireg |= BIT(i);
> > +                       id++;
> > +               }
> > +
> > +               /*
> > +                * The IMSIC EIEx and EIPx registers are indirectly
> > +                * accessed via using ISELECT and IREG CSRs so we
> > +                * save/restore local IRQ to ensure that we don't
> > +                * get preempted while accessing IMSIC registers.
> > +                */
> > +               local_irq_save(flags);
> > +               if (val)
> > +                       imsic_csr_set(isel, ireg);
> > +               else
> > +                       imsic_csr_clear(isel, ireg);
> > +               local_irq_restore(flags);
> > +       }
> > +}
> > +
> > +#define __imsic_id_enable(__id)                \
> > +       __imsic_eix_update((__id), 1, false, true)
> > +#define __imsic_id_disable(__id)       \
> > +       __imsic_eix_update((__id), 1, false, false)
> > +
> > +#ifdef CONFIG_SMP
> > +static void __imsic_id_smp_sync(struct imsic_priv *priv)
> > +{
> > +       struct imsic_handler *handler;
> > +       struct cpumask amask;
> > +       int cpu;
> > +
> > +       cpumask_and(&amask, &priv->lmask, cpu_online_mask);
> > +       for_each_cpu(cpu, &amask) {
> > +               if (cpu == smp_processor_id())
> > +                       continue;
> > +
> > +               handler = per_cpu_ptr(&imsic_handlers, cpu);
> > +               if (!handler || !handler->priv || !handler->local.msi_va) {
> > +                       pr_warn("CPU%d: handler not initialized\n", cpu);
> > +                       continue;
> > +               }
> > +
> > +               writel(handler->priv->ipi_lsync_id, handler->local.msi_va);
> > +       }
> > +}
> > +#else
> > +#define __imsic_id_smp_sync(__priv)
> > +#endif
> > +
> > +static void imsic_id_enable(struct imsic_priv *priv, unsigned int id)
> > +{
> > +       raw_spin_lock(&priv->ids_lock);
> > +       bitmap_set(priv->ids_enabled_bimap, id, 1);
> > +       __imsic_id_enable(id);
> > +       raw_spin_unlock(&priv->ids_lock);
> > +
> > +       __imsic_id_smp_sync(priv);
> > +}
> > +
> > +static void imsic_id_disable(struct imsic_priv *priv, unsigned int id)
> > +{
> > +       raw_spin_lock(&priv->ids_lock);
> > +       bitmap_clear(priv->ids_enabled_bimap, id, 1);
> > +       __imsic_id_disable(id);
> > +       raw_spin_unlock(&priv->ids_lock);
> > +
> > +       __imsic_id_smp_sync(priv);
> > +}
> > +
> > +static void imsic_ids_local_sync(struct imsic_priv *priv)
> > +{
> > +       int i;
> > +
> > +       raw_spin_lock(&priv->ids_lock);
> > +       for (i = 1; i <= priv->global.nr_ids; i++) {
> > +               if (priv->ipi_id == i || priv->ipi_lsync_id == i)
> > +                       continue;
> > +
> > +               if (test_bit(i, priv->ids_enabled_bimap))
> > +                       __imsic_id_enable(i);
> > +               else
> > +                       __imsic_id_disable(i);
> > +       }
> > +       raw_spin_unlock(&priv->ids_lock);
> > +}
> > +
> > +static void imsic_ids_local_delivery(struct imsic_priv *priv, bool enable)
> > +{
> > +       if (enable) {
> > +               imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_ENABLE_EITHRESHOLD);
> > +               imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_ENABLE_EIDELIVERY);
> > +       } else {
> > +               imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_DISABLE_EIDELIVERY);
> > +               imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_DISABLE_EITHRESHOLD);
> > +       }
> > +}
> > +
> > +static int imsic_ids_alloc(struct imsic_priv *priv,
> > +                          unsigned int max_id, unsigned int order)
> > +{
> > +       int ret;
> > +
> > +       if ((priv->global.nr_ids < max_id) ||
> > +           (max_id < BIT(order)))
> > +               return -EINVAL;
> > +
> > +       raw_spin_lock(&priv->ids_lock);
> > +       ret = bitmap_find_free_region(priv->ids_used_bimap,
> > +                                     max_id + 1, order);
> > +       raw_spin_unlock(&priv->ids_lock);
> > +
> > +       return ret;
> > +}
> > +
> > +static void imsic_ids_free(struct imsic_priv *priv, unsigned int base_id,
> > +                          unsigned int order)
> > +{
> > +       raw_spin_lock(&priv->ids_lock);
> > +       bitmap_release_region(priv->ids_used_bimap, base_id, order);
> > +       raw_spin_unlock(&priv->ids_lock);
> > +}
> > +
> > +static int __init imsic_ids_init(struct imsic_priv *priv)
> > +{
> > +       int i;
> > +       struct imsic_global_config *global = &priv->global;
> > +
> > +       raw_spin_lock_init(&priv->ids_lock);
> > +
> > +       /* Allocate used bitmap */
> > +       priv->ids_used_bimap = kcalloc(BITS_TO_LONGS(global->nr_ids + 1),
> > +                                       sizeof(unsigned long), GFP_KERNEL);
> > +       if (!priv->ids_used_bimap)
> > +               return -ENOMEM;
> > +
> > +       /* Allocate enabled bitmap */
> > +       priv->ids_enabled_bimap = kcalloc(BITS_TO_LONGS(global->nr_ids + 1),
> > +                                          sizeof(unsigned long), GFP_KERNEL);
> > +       if (!priv->ids_enabled_bimap) {
> > +               kfree(priv->ids_used_bimap);
> > +               return -ENOMEM;
> > +       }
> > +
> > +       /* Allocate target CPU array */
> > +       priv->ids_target_cpu = kcalloc(global->nr_ids + 1,
> > +                                      sizeof(unsigned int), GFP_KERNEL);
> > +       if (!priv->ids_target_cpu) {
> > +               kfree(priv->ids_enabled_bimap);
> > +               kfree(priv->ids_used_bimap);
> > +               return -ENOMEM;
> > +       }
> > +       for (i = 0; i <= global->nr_ids; i++)
> > +               priv->ids_target_cpu[i] = UINT_MAX;
> > +
> > +       /* Reserve ID#0 because it is special and never implemented */
> > +       bitmap_set(priv->ids_used_bimap, 0, 1);
> > +
> > +       return 0;
> > +}
> > +
> > +static void __init imsic_ids_cleanup(struct imsic_priv *priv)
> > +{
> > +       kfree(priv->ids_target_cpu);
> > +       kfree(priv->ids_enabled_bimap);
> > +       kfree(priv->ids_used_bimap);
> > +}
> > +
> > +#ifdef CONFIG_SMP
> > +static void imsic_ipi_send_mask(unsigned int parent_virq, void *data,
> > +                               const struct cpumask *mask)
> > +{
> > +       int cpu;
> > +       struct imsic_handler *handler;
> > +
> > +       for_each_cpu(cpu, mask) {
> > +               handler = per_cpu_ptr(&imsic_handlers, cpu);
> > +               if (!handler || !handler->priv || !handler->local.msi_va) {
> > +                       pr_warn("CPU%d: handler not initialized\n", cpu);
> > +                       continue;
> > +               }
> > +
> > +               writel(handler->priv->ipi_id, handler->local.msi_va);
> > +       }
> > +}
> > +
> > +static struct ipi_mux_ops imsic_ipi_ops = {
> > +       .ipi_mux_send = imsic_ipi_send_mask,
> > +};
> > +
> > +static void imsic_ipi_enable(struct imsic_priv *priv)
> > +{
> > +       __imsic_id_enable(priv->ipi_id);
> > +       __imsic_id_enable(priv->ipi_lsync_id);
> > +}
> > +
> > +static void imsic_ipi_disable(struct imsic_priv *priv)
> > +{
> > +       __imsic_id_disable(priv->ipi_lsync_id);
> > +       __imsic_id_disable(priv->ipi_id);
> > +}
> > +
> > +static int __init imsic_ipi_domain_init(struct imsic_priv *priv)
> > +{
> > +       int virq;
> > +
> > +       /* Skip IPI setup if IPIs are slow */
> > +       if (priv->slow_ipi)
> > +               goto skip_ipi;
> > +
> > +       /* Allocate interrupt identity for IPIs */
> > +       virq = imsic_ids_alloc(priv, priv->global.nr_ids, get_count_order(1));
> > +       if (virq < 0)
> > +               return virq;
> > +       priv->ipi_id = virq;
> > +
> > +       /* Create IMSIC IPI multiplexing */
> > +       virq = ipi_mux_create(0, BITS_PER_BYTE, &imsic_ipi_ops, NULL);
> > +       if (virq <= 0) {
> > +               imsic_ids_free(priv, priv->ipi_id, get_count_order(1));
> > +               return (virq < 0) ? virq : -ENOMEM;
> > +       }
> > +
> > +       /* Set vIRQ range */
> > +       riscv_ipi_set_virq_range(virq, BITS_PER_BYTE, true);
> > +
> > +skip_ipi:
> > +       /* Allocate interrupt identity for local enable/disable sync */
> > +       virq = imsic_ids_alloc(priv, priv->global.nr_ids, get_count_order(1));
> > +       if (virq < 0) {
> > +               imsic_ids_free(priv, priv->ipi_id, get_count_order(1));
> > +               return virq;
> > +       }
> > +       priv->ipi_lsync_id = virq;
> > +
> > +       return 0;
> > +}
> > +
> > +static void __init imsic_ipi_domain_cleanup(struct imsic_priv *priv)
> > +{
> > +       imsic_ids_free(priv, priv->ipi_lsync_id, get_count_order(1));
> > +       if (priv->ipi_id)
> > +               imsic_ids_free(priv, priv->ipi_id, get_count_order(1));
> > +}
> > +#else
> > +static void imsic_ipi_enable(struct imsic_priv *priv)
> > +{
> > +}
> > +
> > +static void imsic_ipi_disable(struct imsic_priv *priv)
> > +{
> > +}
> > +
> > +static int __init imsic_ipi_domain_init(struct imsic_priv *priv)
> > +{
> > +       /* Clear the IPI ids because we are not using IPIs */
> > +       priv->ipi_id = 0;
> > +       priv->ipi_lsync_id = 0;
> > +       return 0;
> > +}
> > +
> > +static void __init imsic_ipi_domain_cleanup(struct imsic_priv *priv)
> > +{
> > +}
> > +#endif
> > +
> > +static void imsic_irq_mask(struct irq_data *d)
> > +{
> > +       imsic_id_disable(irq_data_get_irq_chip_data(d), d->hwirq);
> > +}
> > +
> > +static void imsic_irq_unmask(struct irq_data *d)
> > +{
> > +       imsic_id_enable(irq_data_get_irq_chip_data(d), d->hwirq);
> > +}
> > +
> > +static void imsic_irq_compose_msi_msg(struct irq_data *d,
> > +                                     struct msi_msg *msg)
> > +{
> > +       struct imsic_priv *priv = irq_data_get_irq_chip_data(d);
> > +       unsigned int cpu;
> > +       int err;
> > +
> > +       cpu = imsic_id_get_target(priv, d->hwirq);
> > +       WARN_ON(cpu == UINT_MAX);
> > +
> > +       err = imsic_get_cpu_msi_msg(cpu, d->hwirq, msg);
> > +       WARN_ON(err);
> > +
> > +       iommu_dma_compose_msi_msg(irq_data_get_msi_desc(d), msg);
> > +}
> > +
> > +#ifdef CONFIG_SMP
> > +static int imsic_irq_set_affinity(struct irq_data *d,
> > +                                 const struct cpumask *mask_val,
> > +                                 bool force)
> > +{
> > +       struct imsic_priv *priv = irq_data_get_irq_chip_data(d);
> > +       unsigned int target_cpu;
> > +       int rc;
> > +
> > +       rc = imsic_get_cpu(priv, mask_val, force, &target_cpu);
> > +       if (rc)
> > +               return rc;
> > +
> > +       imsic_id_set_target(priv, d->hwirq, target_cpu);
> > +       irq_data_update_effective_affinity(d, cpumask_of(target_cpu));
> > +
> > +       return IRQ_SET_MASK_OK;
> > +}
> > +#endif
> > +
> > +static struct irq_chip imsic_irq_base_chip = {
> > +       .name                   = "RISC-V IMSIC-BASE",
> > +       .irq_mask               = imsic_irq_mask,
> > +       .irq_unmask             = imsic_irq_unmask,
> > +#ifdef CONFIG_SMP
> > +       .irq_set_affinity       = imsic_irq_set_affinity,
> > +#endif
> > +       .irq_compose_msi_msg    = imsic_irq_compose_msi_msg,
> > +       .flags                  = IRQCHIP_SKIP_SET_WAKE |
> > +                                 IRQCHIP_MASK_ON_SUSPEND,
> > +};
> > +
> > +static int imsic_irq_domain_alloc(struct irq_domain *domain,
> > +                                 unsigned int virq,
> > +                                 unsigned int nr_irqs,
> > +                                 void *args)
> > +{
> > +       struct imsic_priv *priv = domain->host_data;
> > +       msi_alloc_info_t *info = args;
> > +       phys_addr_t msi_addr;
> > +       int i, hwirq, err = 0;
> > +       unsigned int cpu;
> > +
> > +       err = imsic_get_cpu(priv, &priv->lmask, false, &cpu);
> > +       if (err)
> > +               return err;
> > +
> > +       err = imsic_cpu_page_phys(cpu, 0, &msi_addr);
> > +       if (err)
> > +               return err;
> > +
> > +       hwirq = imsic_ids_alloc(priv, priv->global.nr_ids,
> > +                               get_count_order(nr_irqs));
> > +       if (hwirq < 0)
> > +               return hwirq;
> > +
> > +       err = iommu_dma_prepare_msi(info->desc, msi_addr);
> > +       if (err)
> > +               goto fail;
> > +
> > +       for (i = 0; i < nr_irqs; i++) {
> > +               imsic_id_set_target(priv, hwirq + i, cpu);
> > +               irq_domain_set_info(domain, virq + i, hwirq + i,
> > +                                   &imsic_irq_base_chip, priv,
> > +                                   handle_simple_irq, NULL, NULL);
> > +               irq_set_noprobe(virq + i);
> > +               irq_set_affinity(virq + i, &priv->lmask);
> > +       }
> > +
> > +       return 0;
> > +
> > +fail:
> > +       imsic_ids_free(priv, hwirq, get_count_order(nr_irqs));
> > +       return err;
> > +}
> > +
> > +static void imsic_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 imsic_priv *priv = domain->host_data;
> > +
> > +       imsic_ids_free(priv, d->hwirq, get_count_order(nr_irqs));
> > +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> > +}
> > +
> > +static const struct irq_domain_ops imsic_base_domain_ops = {
> > +       .alloc          = imsic_irq_domain_alloc,
> > +       .free           = imsic_irq_domain_free,
> > +};
> > +
> > +#ifdef CONFIG_RISCV_IMSIC_PCI
> > +
> > +static void imsic_pci_mask_irq(struct irq_data *d)
> > +{
> > +       pci_msi_mask_irq(d);
> > +       irq_chip_mask_parent(d);
> > +}
> > +
> > +static void imsic_pci_unmask_irq(struct irq_data *d)
> > +{
> > +       pci_msi_unmask_irq(d);
> > +       irq_chip_unmask_parent(d);
> > +}
> > +
> > +static struct irq_chip imsic_pci_irq_chip = {
> > +       .name                   = "RISC-V IMSIC-PCI",
> > +       .irq_mask               = imsic_pci_mask_irq,
> > +       .irq_unmask             = imsic_pci_unmask_irq,
> > +       .irq_eoi                = irq_chip_eoi_parent,
> > +};
> > +
> > +static struct msi_domain_ops imsic_pci_domain_ops = {
> > +};
> > +
> > +static struct msi_domain_info imsic_pci_domain_info = {
> > +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> > +                  MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI),
> > +       .ops    = &imsic_pci_domain_ops,
> > +       .chip   = &imsic_pci_irq_chip,
> > +};
> > +
> > +#endif
> > +
> > +static struct irq_chip imsic_plat_irq_chip = {
> > +       .name                   = "RISC-V IMSIC-PLAT",
> > +};
> > +
> > +static struct msi_domain_ops imsic_plat_domain_ops = {
> > +};
> > +
> > +static struct msi_domain_info imsic_plat_domain_info = {
> > +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
> > +       .ops    = &imsic_plat_domain_ops,
> > +       .chip   = &imsic_plat_irq_chip,
> > +};
> > +
> > +static int __init imsic_irq_domains_init(struct imsic_priv *priv,
> > +                                        struct fwnode_handle *fwnode)
> > +{
> > +       /* Create Base IRQ domain */
> > +       priv->base_domain = irq_domain_create_tree(fwnode,
> > +                                               &imsic_base_domain_ops, priv);
> > +       if (!priv->base_domain) {
> > +               pr_err("Failed to create IMSIC base domain\n");
> > +               return -ENOMEM;
> > +       }
> > +       irq_domain_update_bus_token(priv->base_domain, DOMAIN_BUS_NEXUS);
> > +
> > +#ifdef CONFIG_RISCV_IMSIC_PCI
> > +       /* Create PCI MSI domain */
> > +       priv->pci_domain = pci_msi_create_irq_domain(fwnode,
> > +                                               &imsic_pci_domain_info,
> > +                                               priv->base_domain);
> > +       if (!priv->pci_domain) {
> > +               pr_err("Failed to create IMSIC PCI domain\n");
> > +               irq_domain_remove(priv->base_domain);
> > +               return -ENOMEM;
> > +       }
> > +#endif
> > +
> > +       /* Create Platform MSI domain */
> > +       priv->plat_domain = platform_msi_create_irq_domain(fwnode,
> > +                                               &imsic_plat_domain_info,
> > +                                               priv->base_domain);
> > +       if (!priv->plat_domain) {
> > +               pr_err("Failed to create IMSIC platform domain\n");
> > +               if (priv->pci_domain)
> > +                       irq_domain_remove(priv->pci_domain);
> > +               irq_domain_remove(priv->base_domain);
> > +               return -ENOMEM;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +/*
> > + * To handle an interrupt, we read the TOPEI CSR and write zero in one
> > + * instruction. If TOPEI CSR is non-zero then we translate TOPEI.ID to
> > + * Linux interrupt number and let Linux IRQ subsystem handle it.
> > + */
> > +static void imsic_handle_irq(struct irq_desc *desc)
> > +{
> > +       struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
> > +       struct irq_chip *chip = irq_desc_get_chip(desc);
> > +       struct imsic_priv *priv = handler->priv;
> > +       irq_hw_number_t hwirq;
> > +       int err;
> > +
> > +       WARN_ON_ONCE(!handler->priv);
> > +
> > +       chained_irq_enter(chip, desc);
> > +
> > +       while ((hwirq = csr_swap(CSR_TOPEI, 0))) {
> > +               hwirq = hwirq >> TOPEI_ID_SHIFT;
> > +
> > +               if (hwirq == priv->ipi_id) {
> > +#ifdef CONFIG_SMP
> > +                       ipi_mux_process();
> > +#endif
> > +                       continue;
> > +               } else if (hwirq == priv->ipi_lsync_id) {
> > +                       imsic_ids_local_sync(priv);
> > +                       continue;
> > +               }
> > +
> > +               err = generic_handle_domain_irq(priv->base_domain, hwirq);
> > +               if (unlikely(err))
> > +                       pr_warn_ratelimited(
> > +                               "hwirq %lu mapping not found\n", hwirq);
> > +       }
> > +
> > +       chained_irq_exit(chip, desc);
> > +}
> > +
> > +static int imsic_dying_cpu(unsigned int cpu)
> > +{
> > +       struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
> > +       struct imsic_priv *priv = handler->priv;
> > +
> > +       /* Disable per-CPU parent interrupt */
> > +       if (imsic_parent_irq)
> > +               disable_percpu_irq(imsic_parent_irq);
> > +
> > +       /* Locally disable interrupt delivery */
> > +       imsic_ids_local_delivery(priv, false);
> > +
> > +       /* Disable IPIs */
> > +       imsic_ipi_disable(priv);
> > +
> > +       return 0;
> > +}
> > +
> > +static int imsic_starting_cpu(unsigned int cpu)
> > +{
> > +       struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
> > +       struct imsic_priv *priv = handler->priv;
> > +
> > +       /* Enable per-CPU parent interrupt */
> > +       if (imsic_parent_irq)
> > +               enable_percpu_irq(imsic_parent_irq,
> > +                                 irq_get_trigger_type(imsic_parent_irq));
> > +       else
> > +               pr_warn("cpu%d: parent irq not available\n", cpu);
> > +
> > +       /* Enable IPIs */
> > +       imsic_ipi_enable(priv);
> > +
> > +       /*
> > +        * Interrupts identities might have been enabled/disabled while
> > +        * this CPU was not running so sync-up local enable/disable state.
> > +        */
> > +       imsic_ids_local_sync(priv);
> > +
> > +       /* Locally enable interrupt delivery */
> > +       imsic_ids_local_delivery(priv, true);
> > +
> > +       return 0;
> > +}
> > +
> > +struct imsic_fwnode_ops {
> > +       u32 (*nr_parent_irq)(struct fwnode_handle *fwnode,
> > +                            void *fwopaque);
> > +       int (*parent_hartid)(struct fwnode_handle *fwnode,
> > +                            void *fwopaque, u32 index,
> > +                            unsigned long *out_hartid);
> > +       u32 (*nr_mmio)(struct fwnode_handle *fwnode, void *fwopaque);
> > +       int (*mmio_to_resource)(struct fwnode_handle *fwnode,
> > +                               void *fwopaque, u32 index,
> > +                               struct resource *res);
> > +       void __iomem *(*mmio_map)(struct fwnode_handle *fwnode,
> > +                                 void *fwopaque, u32 index);
> > +       int (*read_u32)(struct fwnode_handle *fwnode,
> > +                       void *fwopaque, const char *prop, u32 *out_val);
> > +       bool (*read_bool)(struct fwnode_handle *fwnode,
> > +                         void *fwopaque, const char *prop);
> > +};
> > +
> > +static int __init imsic_init(struct imsic_fwnode_ops *fwops,
> > +                            struct fwnode_handle *fwnode,
> > +                            void *fwopaque)
> > +{
> > +       struct resource res;
> > +       phys_addr_t base_addr;
> > +       int rc, nr_parent_irqs;
> > +       struct imsic_mmio *mmio;
> > +       struct imsic_priv *priv;
> > +       struct irq_domain *domain;
> > +       struct imsic_handler *handler;
> > +       struct imsic_global_config *global;
> > +       u32 i, tmp, nr_handlers = 0;
> > +
> > +       if (imsic_init_done) {
> > +               pr_err("%pfwP: already initialized hence ignoring\n",
> > +                       fwnode);
> > +               return -ENODEV;
> > +       }
> > +
> > +       if (!riscv_isa_extension_available(NULL, SxAIA)) {
> > +               pr_err("%pfwP: AIA support not available\n", fwnode);
> > +               return -ENODEV;
> > +       }
> > +
> > +       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> > +       if (!priv)
> > +               return -ENOMEM;
> > +       global = &priv->global;
> > +
> > +       /* Find number of parent interrupts */
> > +       nr_parent_irqs = fwops->nr_parent_irq(fwnode, fwopaque);
> > +       if (!nr_parent_irqs) {
> > +               pr_err("%pfwP: no parent irqs available\n", fwnode);
> > +               return -EINVAL;
> > +       }
> > +
> > +       /* Find number of guest index bits in MSI address */
> > +       rc = fwops->read_u32(fwnode, fwopaque, "riscv,guest-index-bits",
> > +                            &global->guest_index_bits);
> > +       if (rc)
> > +               global->guest_index_bits = 0;
> > +       tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT;
> > +       if (tmp < global->guest_index_bits) {
> > +               pr_err("%pfwP: guest index bits too big\n", fwnode);
> > +               return -EINVAL;
> > +       }
> > +
> > +       /* Find number of HART index bits */
> > +       rc = fwops->read_u32(fwnode, fwopaque, "riscv,hart-index-bits",
> > +                            &global->hart_index_bits);
> > +       if (rc) {
> > +               /* Assume default value */
> > +               global->hart_index_bits = __fls(nr_parent_irqs);
> > +               if (BIT(global->hart_index_bits) < nr_parent_irqs)
> > +                       global->hart_index_bits++;
> > +       }
> > +       tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT -
> > +             global->guest_index_bits;
> > +       if (tmp < global->hart_index_bits) {
> > +               pr_err("%pfwP: HART index bits too big\n", fwnode);
> > +               return -EINVAL;
> > +       }
> > +
> > +       /* Find number of group index bits */
> > +       rc = fwops->read_u32(fwnode, fwopaque, "riscv,group-index-bits",
> > +                            &global->group_index_bits);
> > +       if (rc)
> > +               global->group_index_bits = 0;
> > +       tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT -
> > +             global->guest_index_bits - global->hart_index_bits;
> > +       if (tmp < global->group_index_bits) {
> > +               pr_err("%pfwP: group index bits too big\n", fwnode);
> > +               return -EINVAL;
> > +       }
> > +
> > +       /* Find first bit position of group index */
> > +       tmp = IMSIC_MMIO_PAGE_SHIFT * 2;
> > +       rc = fwops->read_u32(fwnode, fwopaque, "riscv,group-index-shift",
> > +                            &global->group_index_shift);
> > +       if (rc)
> > +               global->group_index_shift = tmp;
> > +       if (global->group_index_shift < tmp) {
> > +               pr_err("%pfwP: group index shift too small\n", fwnode);
> > +               return -EINVAL;
> > +       }
> > +       tmp = global->group_index_bits + global->group_index_shift - 1;
> > +       if (tmp >= BITS_PER_LONG) {
> > +               pr_err("%pfwP: group index shift too big\n", fwnode);
> > +               return -EINVAL;
> > +       }
> > +
> > +       /* Find number of interrupt identities */
> > +       rc = fwops->read_u32(fwnode, fwopaque, "riscv,num-ids",
> > +                            &global->nr_ids);
> > +       if (rc) {
> > +               pr_err("%pfwP: number of interrupt identities not found\n",
> > +                       fwnode);
> > +               return rc;
> > +       }
> > +       if ((global->nr_ids < IMSIC_MIN_ID) ||
> > +           (global->nr_ids >= IMSIC_MAX_ID) ||
> > +           ((global->nr_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID)) {
> > +               pr_err("%pfwP: invalid number of interrupt identities\n",
> > +                       fwnode);
> > +               return -EINVAL;
> > +       }
> > +
> > +       /* Find number of guest interrupt identities */
> > +       if (fwops->read_u32(fwnode, fwopaque, "riscv,num-guest-ids",
> > +                           &global->nr_guest_ids))
> > +               global->nr_guest_ids = global->nr_ids;
> > +       if ((global->nr_guest_ids < IMSIC_MIN_ID) ||
> > +           (global->nr_guest_ids >= IMSIC_MAX_ID) ||
> > +           ((global->nr_guest_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID)) {
> > +               pr_err("%pfwP: invalid number of guest interrupt identities\n",
> > +                       fwnode);
> > +               return -EINVAL;
> > +       }
> > +
> > +       /* Check if IPIs are slow */
> > +       priv->slow_ipi = fwops->read_bool(fwnode, fwopaque, "riscv,slow-ipi");
> > +
> > +       /* Compute base address */
> > +       rc = fwops->mmio_to_resource(fwnode, fwopaque, 0, &res);
> > +       if (rc) {
> > +               pr_err("%pfwP: first MMIO resource not found\n", fwnode);
> > +               return -EINVAL;
> > +       }
> > +       global->base_addr = res.start;
> > +       global->base_addr &= ~(BIT(global->guest_index_bits +
> > +                                  global->hart_index_bits +
> > +                                  IMSIC_MMIO_PAGE_SHIFT) - 1);
> > +       global->base_addr &= ~((BIT(global->group_index_bits) - 1) <<
> > +                              global->group_index_shift);
> > +
> > +       /* Find number of MMIO register sets */
> > +       priv->num_mmios = fwops->nr_mmio(fwnode, fwopaque);
> > +
> > +       /* Allocate MMIO register sets */
> > +       priv->mmios = kcalloc(priv->num_mmios, sizeof(*mmio), GFP_KERNEL);
> > +       if (!priv->mmios) {
> > +               rc = -ENOMEM;
> > +               goto out_free_priv;
> > +       }
> > +
> > +       /* Parse and map MMIO register sets */
> > +       for (i = 0; i < priv->num_mmios; i++) {
> > +               mmio = &priv->mmios[i];
> > +               rc = fwops->mmio_to_resource(fwnode, fwopaque, i, &res);
> > +               if (rc) {
> > +                       pr_err("%pfwP: unable to parse MMIO regset %d\n",
> > +                               fwnode, i);
> > +                       goto out_iounmap;
> > +               }
> > +               mmio->pa = res.start;
> > +               mmio->size = res.end - res.start + 1;
> > +
> > +               base_addr = mmio->pa;
> > +               base_addr &= ~(BIT(global->guest_index_bits +
> > +                                  global->hart_index_bits +
> > +                                  IMSIC_MMIO_PAGE_SHIFT) - 1);
> > +               base_addr &= ~((BIT(global->group_index_bits) - 1) <<
> > +                              global->group_index_shift);
> > +               if (base_addr != global->base_addr) {
> > +                       rc = -EINVAL;
> > +                       pr_err("%pfwP: address mismatch for regset %d\n",
> > +                               fwnode, i);
> > +                       goto out_iounmap;
> > +               }
> > +
> > +               tmp = BIT(global->guest_index_bits) - 1;
> > +               if ((mmio->size / IMSIC_MMIO_PAGE_SZ) & tmp) {
> > +                       rc = -EINVAL;
> > +                       pr_err("%pfwP: size mismatch for regset %d\n",
> > +                               fwnode, i);
> > +                       goto out_iounmap;
> > +               }
> > +
> > +               mmio->va = fwops->mmio_map(fwnode, fwopaque, i);
> > +               if (!mmio->va) {
> > +                       rc = -EIO;
> > +                       pr_err("%pfwP: unable to map MMIO regset %d\n",
> > +                               fwnode, i);
> > +                       goto out_iounmap;
> > +               }
> > +       }
> > +
> > +       /* Initialize interrupt identity management */
> > +       rc = imsic_ids_init(priv);
> > +       if (rc) {
> > +               pr_err("%pfwP: failed to initialize interrupt management\n",
> > +                      fwnode);
> > +               goto out_iounmap;
> > +       }
> > +
> > +       /* Configure handlers for target CPUs */
> > +       for (i = 0; i < nr_parent_irqs; i++) {
> > +               unsigned long reloff, hartid;
> > +               int j, cpu;
> > +
> > +               rc = fwops->parent_hartid(fwnode, fwopaque, i, &hartid);
> > +               if (rc) {
> > +                       pr_warn("%pfwP: hart ID for parent irq%d not found\n",
> > +                               fwnode, i);
> > +                       continue;
> > +               }
> > +
> > +               cpu = riscv_hartid_to_cpuid(hartid);
> > +               if (cpu < 0) {
> > +                       pr_warn("%pfwP: invalid cpuid for parent irq%d\n",
> > +                               fwnode, i);
> > +                       continue;
> > +               }
> > +
> > +               /* Find MMIO location of MSI page */
> > +               mmio = NULL;
> > +               reloff = i * BIT(global->guest_index_bits) *
> > +                        IMSIC_MMIO_PAGE_SZ;
> > +               for (j = 0; priv->num_mmios; j++) {
> > +                       if (reloff < priv->mmios[j].size) {
> > +                               mmio = &priv->mmios[j];
> > +                               break;
> > +                       }
> > +
> > +                       reloff -= priv->mmios[j].size;
> > +               }
> > +               if (!mmio) {
> > +                       pr_warn("%pfwP: MMIO not found for parent irq%d\n",
> > +                               fwnode, i);
> > +                       continue;
> > +               }
> > +
> > +               handler = per_cpu_ptr(&imsic_handlers, cpu);
> > +               if (handler->priv) {
> > +                       pr_warn("%pfwP: CPU%d handler already configured.\n",
> > +                               fwnode, cpu);
> > +                       goto done;
> > +               }
> > +
> > +               cpumask_set_cpu(cpu, &priv->lmask);
> > +               handler->local.msi_pa = mmio->pa + reloff;
> > +               handler->local.msi_va = mmio->va + reloff;
> > +               handler->priv = priv;
> > +
> > +done:
> > +               nr_handlers++;
> > +       }
> > +
> > +       /* If no CPU handlers found then can't take interrupts */
> > +       if (!nr_handlers) {
> > +               pr_err("%pfwP: No CPU handlers found\n", fwnode);
> > +               rc = -ENODEV;
> > +               goto out_ids_cleanup;
> > +       }
> > +
> > +       /* Find parent domain and register chained handler */
> > +       domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(),
> > +                                         DOMAIN_BUS_ANY);
> > +       if (!domain) {
> > +               pr_err("%pfwP: Failed to find INTC domain\n", fwnode);
> > +               rc = -ENOENT;
> > +               goto out_ids_cleanup;
> > +       }
> > +       imsic_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT);
> > +       if (!imsic_parent_irq) {
> > +               pr_err("%pfwP: Failed to create INTC mapping\n", fwnode);
> > +               rc = -ENOENT;
> > +               goto out_ids_cleanup;
> > +       }
> > +       irq_set_chained_handler(imsic_parent_irq, imsic_handle_irq);
> > +
> > +       /* Initialize IPI domain */
> > +       rc = imsic_ipi_domain_init(priv);
> > +       if (rc) {
> > +               pr_err("%pfwP: Failed to initialize IPI domain\n", fwnode);
> > +               goto out_ids_cleanup;
> > +       }
> > +
> > +       /* Initialize IRQ and MSI domains */
> > +       rc = imsic_irq_domains_init(priv, fwnode);
> > +       if (rc) {
> > +               pr_err("%pfwP: Failed to initialize IRQ and MSI domains\n",
> > +                      fwnode);
> > +               goto out_ipi_domain_cleanup;
> > +       }
> > +
> > +       /* Setup cpuhp state */
> > +       cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
> > +                         "irqchip/riscv/imsic:starting",
> > +                         imsic_starting_cpu, imsic_dying_cpu);
> > +
> > +       /*
> > +        * Only one IMSIC instance allowed in a platform for clean
> > +        * implementation of SMP IRQ affinity and per-CPU IPIs.
> > +        *
> > +        * This means on a multi-socket (or multi-die) platform we
> > +        * will have multiple MMIO regions for one IMSIC instance.
> > +        */
> > +       imsic_init_done = true;
> > +
> > +       pr_info("%pfwP:  hart-index-bits: %d,  guest-index-bits: %d\n",
> > +               fwnode, global->hart_index_bits, global->guest_index_bits);
> > +       pr_info("%pfwP: group-index-bits: %d, group-index-shift: %d\n",
> > +               fwnode, global->group_index_bits, global->group_index_shift);
> > +       pr_info("%pfwP: mapped %d interrupts for %d CPUs at %pa\n",
> > +               fwnode, global->nr_ids, nr_handlers, &global->base_addr);
> > +       if (priv->ipi_lsync_id)
> > +               pr_info("%pfwP: enable/disable sync using interrupt %d\n",
> > +                       fwnode, priv->ipi_lsync_id);
> > +       if (priv->ipi_id)
> > +               pr_info("%pfwP: providing IPIs using interrupt %d\n",
> > +                       fwnode, priv->ipi_id);
> > +
> > +       return 0;
> > +
> > +out_ipi_domain_cleanup:
> > +       imsic_ipi_domain_cleanup(priv);
> > +out_ids_cleanup:
> > +       imsic_ids_cleanup(priv);
> > +out_iounmap:
> > +       for (i = 0; i < priv->num_mmios; i++) {
> > +               if (priv->mmios[i].va)
> > +                       iounmap(priv->mmios[i].va);
> > +       }
> > +       kfree(priv->mmios);
> > +out_free_priv:
> > +       kfree(priv);
> > +       return rc;
> > +}
> > +
> > +static u32 __init imsic_dt_nr_parent_irq(struct fwnode_handle *fwnode,
> > +                                        void *fwopaque)
> > +{
> > +       return of_irq_count(to_of_node(fwnode));
> > +}
> > +
> > +static int __init imsic_dt_parent_hartid(struct fwnode_handle *fwnode,
> > +                                        void *fwopaque, u32 index,
> > +                                        unsigned long *out_hartid)
> > +{
> > +       struct of_phandle_args parent;
> > +       int rc;
> > +
> > +       rc = of_irq_parse_one(to_of_node(fwnode), index, &parent);
> > +       if (rc)
> > +               return rc;
> > +
> > +       /*
> > +        * Skip interrupts other than external interrupts for
> > +        * current privilege level.
> > +        */
> > +       if (parent.args[0] != RV_IRQ_EXT)
> > +               return -EINVAL;
> > +
> > +       return riscv_of_parent_hartid(parent.np, out_hartid);
> > +}
> > +
> > +static u32 __init imsic_dt_nr_mmio(struct fwnode_handle *fwnode,
> > +                                  void *fwopaque)
> > +{
> > +       u32 ret = 0;
> > +       struct resource res;
> > +
> > +       while (!of_address_to_resource(to_of_node(fwnode), ret, &res))
> > +               ret++;
> > +
> > +       return ret;
> > +}
> > +
> > +static int __init imsic_mmio_to_resource(struct fwnode_handle *fwnode,
> > +                                        void *fwopaque, u32 index,
> > +                                        struct resource *res)
> > +{
> > +       return of_address_to_resource(to_of_node(fwnode), index, res);
> > +}
> > +
> > +static void __iomem __init *imsic_dt_mmio_map(struct fwnode_handle *fwnode,
> > +                                             void *fwopaque, u32 index)
> > +{
> > +       return of_iomap(to_of_node(fwnode), index);
> > +}
> > +
> > +static int __init imsic_dt_read_u32(struct fwnode_handle *fwnode,
> > +                                   void *fwopaque, const char *prop,
> > +                                   u32 *out_val)
> > +{
> > +       return of_property_read_u32(to_of_node(fwnode), prop, out_val);
> > +}
> > +
> > +static bool __init imsic_dt_read_bool(struct fwnode_handle *fwnode,
> > +                                     void *fwopaque, const char *prop)
> > +{
> > +       return of_property_read_bool(to_of_node(fwnode), prop);
> > +}
> > +
> > +static int __init imsic_dt_init(struct device_node *node,
> > +                               struct device_node *parent)
> > +{
> > +       struct imsic_fwnode_ops ops = {
> > +               .nr_parent_irq = imsic_dt_nr_parent_irq,
> > +               .parent_hartid = imsic_dt_parent_hartid,
> > +               .nr_mmio = imsic_dt_nr_mmio,
> > +               .mmio_to_resource = imsic_mmio_to_resource,
> > +               .mmio_map = imsic_dt_mmio_map,
> > +               .read_u32 = imsic_dt_read_u32,
> > +               .read_bool = imsic_dt_read_bool,
> > +       };
> > +
> > +       return imsic_init(&ops, &node->fwnode, NULL);
> > +}
> > +IRQCHIP_DECLARE(riscv_imsic, "riscv,imsics", imsic_dt_init);
> > diff --git a/include/linux/irqchip/riscv-imsic.h b/include/linux/irqchip/riscv-imsic.h
> > new file mode 100644
> > index 000000000000..5d1387adc0ba
> > --- /dev/null
> > +++ b/include/linux/irqchip/riscv-imsic.h
> > @@ -0,0 +1,92 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * Copyright (C) 2021 Western Digital Corporation or its affiliates.
> > + * Copyright (C) 2022 Ventana Micro Systems Inc.
> > + */
> > +#ifndef __LINUX_IRQCHIP_RISCV_IMSIC_H
> > +#define __LINUX_IRQCHIP_RISCV_IMSIC_H
> > +
> > +#include <linux/types.h>
> > +#include <asm/csr.h>
> > +
> > +#define IMSIC_MMIO_PAGE_SHIFT          12
> > +#define IMSIC_MMIO_PAGE_SZ             (1UL << IMSIC_MMIO_PAGE_SHIFT)
> > +#define IMSIC_MMIO_PAGE_LE             0x00
> > +#define IMSIC_MMIO_PAGE_BE             0x04
> > +
> > +#define IMSIC_MIN_ID                   63
> > +#define IMSIC_MAX_ID                   2048
> > +
> > +#define IMSIC_EIDELIVERY               0x70
> > +
> > +#define IMSIC_EITHRESHOLD              0x72
> > +
> > +#define IMSIC_EIP0                     0x80
> > +#define IMSIC_EIP63                    0xbf
> > +#define IMSIC_EIPx_BITS                        32
> > +
> > +#define IMSIC_EIE0                     0xc0
> > +#define IMSIC_EIE63                    0xff
> > +#define IMSIC_EIEx_BITS                        32
> > +
> > +#define IMSIC_FIRST                    IMSIC_EIDELIVERY
> > +#define IMSIC_LAST                     IMSIC_EIE63
> > +
> > +#define IMSIC_MMIO_SETIPNUM_LE         0x00
> > +#define IMSIC_MMIO_SETIPNUM_BE         0x04
> > +
> > +struct imsic_global_config {
> > +       /*
> > +        * MSI Target Address Scheme
> > +        *
> > +        * XLEN-1                                                12     0
> > +        * |                                                     |     |
> > +        * -------------------------------------------------------------
> > +        * |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
> > +        * -------------------------------------------------------------
> > +        */
> > +
> > +       /* Bits representing Guest index, HART index, and Group index */
> > +       u32 guest_index_bits;
> > +       u32 hart_index_bits;
> > +       u32 group_index_bits;
> > +       u32 group_index_shift;
> > +
> > +       /* Global base address matching all target MSI addresses */
> > +       phys_addr_t base_addr;
> > +
> > +       /* Number of interrupt identities */
> > +       u32 nr_ids;
> > +
> > +       /* Number of guest interrupt identities */
> > +       u32 nr_guest_ids;
> > +};
> > +
> > +struct imsic_local_config {
> > +       phys_addr_t msi_pa;
> > +       void __iomem *msi_va;
> > +};
> > +
> > +#ifdef CONFIG_RISCV_IMSIC
> > +
> > +extern const struct imsic_global_config *imsic_get_global_config(void);
> > +
> > +extern const struct imsic_local_config *imsic_get_local_config(
> > +                                                       unsigned int cpu);
> > +
> > +#else
> > +
> > +static inline const struct imsic_global_config *imsic_get_global_config(void)
> > +{
> > +       return NULL;
> > +}
> > +
> > +static inline const struct imsic_local_config *imsic_get_local_config(
> > +                                                       unsigned int cpu)
> > +{
> > +       return NULL;
> > +}
> > +
> > +#endif
> > +
> > +#endif
> > --
> > 2.34.1
> >

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

* Re: [PATCH 5/9] irqchip: Add RISC-V incoming MSI controller driver
@ 2023-01-02 16:25       ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2023-01-02 16:25 UTC (permalink / raw)
  To: Andrew Bresticker
  Cc: Anup Patel, Palmer Dabbelt, Paul Walmsley, Thomas Gleixner,
	Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Atish Patra,
	Alistair Francis, linux-riscv, linux-kernel, devicetree

On Fri, Nov 11, 2022 at 9:32 PM Andrew Bresticker <abrestic@rivosinc.com> wrote:
>
> On Thu, Nov 10, 2022 at 11:44 PM Anup Patel <apatel@ventanamicro.com> wrote:
> >
> > The RISC-V advanced interrupt architecture (AIA) specification defines
> > a new MSI controller for managing MSIs on a RISC-V platform. This new
> > MSI controller is referred to as incoming message signaled interrupt
> > controller (IMSIC) which manages MSI on per-HART (or per-CPU) basis.
> > (For more details refer https://github.com/riscv/riscv-aia)
> >
> > This patch adds an irqchip driver for RISC-V IMSIC found on RISC-V
> > platforms.
> >
> > Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> > ---
> >  drivers/irqchip/Kconfig             |   20 +-
> >  drivers/irqchip/Makefile            |    1 +
> >  drivers/irqchip/irq-riscv-imsic.c   | 1207 +++++++++++++++++++++++++++
> >  include/linux/irqchip/riscv-imsic.h |   92 ++
> >  4 files changed, 1319 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/irqchip/irq-riscv-imsic.c
> >  create mode 100644 include/linux/irqchip/riscv-imsic.h
> >
> > diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> > index 7ef9f5e696d3..8246c08f0fd3 100644
> > --- a/drivers/irqchip/Kconfig
> > +++ b/drivers/irqchip/Kconfig
> > @@ -29,7 +29,6 @@ config ARM_GIC_V2M
> >
> >  config GIC_NON_BANKED
> >         bool
> > -
> >  config ARM_GIC_V3
> >         bool
> >         select IRQ_DOMAIN_HIERARCHY
> > @@ -564,6 +563,25 @@ config SIFIVE_PLIC
> >
> >            If you don't know what to do here, say Y.
> >
> > +config RISCV_IMSIC
> > +       bool "RISC-V Incoming MSI Controller"
> > +       depends on RISCV
> > +       select IRQ_DOMAIN_HIERARCHY
> > +       select GENERIC_MSI_IRQ_DOMAIN
> > +       help
> > +          This enables support for the IMSIC chip found in RISC-V systems.
> > +          The IMSIC controls message signaled interrupts and forwards them
> > +          to each core as wired local interrupt.
> > +
> > +          If you don't know what to do here, say Y.
> > +
> > +config RISCV_IMSIC_PCI
> > +       bool
> > +       depends on RISCV_IMSIC
> > +       depends on PCI
> > +       depends on PCI_MSI
> > +       default RISCV_IMSIC
> > +
> >  config EXYNOS_IRQ_COMBINER
> >         bool "Samsung Exynos IRQ combiner support" if COMPILE_TEST
> >         depends on (ARCH_EXYNOS && ARM) || COMPILE_TEST
> > diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> > index 87b49a10962c..22c723cc6ec8 100644
> > --- a/drivers/irqchip/Makefile
> > +++ b/drivers/irqchip/Makefile
> > @@ -96,6 +96,7 @@ obj-$(CONFIG_QCOM_MPM)                        += irq-qcom-mpm.o
> >  obj-$(CONFIG_CSKY_MPINTC)              += irq-csky-mpintc.o
> >  obj-$(CONFIG_CSKY_APB_INTC)            += irq-csky-apb-intc.o
> >  obj-$(CONFIG_RISCV_INTC)               += irq-riscv-intc.o
> > +obj-$(CONFIG_RISCV_IMSIC)              += irq-riscv-imsic.o
> >  obj-$(CONFIG_SIFIVE_PLIC)              += irq-sifive-plic.o
> >  obj-$(CONFIG_IMX_IRQSTEER)             += irq-imx-irqsteer.o
> >  obj-$(CONFIG_IMX_INTMUX)               += irq-imx-intmux.o
> > diff --git a/drivers/irqchip/irq-riscv-imsic.c b/drivers/irqchip/irq-riscv-imsic.c
> > new file mode 100644
> > index 000000000000..95324fb4f5ed
> > --- /dev/null
> > +++ b/drivers/irqchip/irq-riscv-imsic.c
> > @@ -0,0 +1,1207 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2021 Western Digital Corporation or its affiliates.
> > + * Copyright (C) 2022 Ventana Micro Systems Inc.
> > + */
> > +
> > +#define pr_fmt(fmt) "riscv-imsic: " fmt
> > +#include <linux/bitmap.h>
> > +#include <linux/cpu.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/iommu.h>
> > +#include <linux/irq.h>
> > +#include <linux/irqchip.h>
> > +#include <linux/irqchip/chained_irq.h>
> > +#include <linux/irqchip/riscv-imsic.h>
> > +#include <linux/irqdomain.h>
> > +#include <linux/module.h>
> > +#include <linux/msi.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/pci.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/smp.h>
> > +#include <asm/hwcap.h>
> > +
> > +#define IMSIC_DISABLE_EIDELIVERY       0
> > +#define IMSIC_ENABLE_EIDELIVERY                1
> > +#define IMSIC_DISABLE_EITHRESHOLD      1
> > +#define IMSIC_ENABLE_EITHRESHOLD       0
> > +
> > +#define imsic_csr_write(__c, __v)      \
> > +do {                                   \
> > +       csr_write(CSR_ISELECT, __c);    \
> > +       csr_write(CSR_IREG, __v);       \
> > +} while (0)
> > +
> > +#define imsic_csr_read(__c)            \
> > +({                                     \
> > +       unsigned long __v;              \
> > +       csr_write(CSR_ISELECT, __c);    \
> > +       __v = csr_read(CSR_IREG);       \
> > +       __v;                            \
> > +})
> > +
> > +#define imsic_csr_set(__c, __v)                \
> > +do {                                   \
> > +       csr_write(CSR_ISELECT, __c);    \
> > +       csr_set(CSR_IREG, __v);         \
> > +} while (0)
> > +
> > +#define imsic_csr_clear(__c, __v)      \
> > +do {                                   \
> > +       csr_write(CSR_ISELECT, __c);    \
> > +       csr_clear(CSR_IREG, __v);       \
> > +} while (0)
> > +
> > +struct imsic_mmio {
> > +       phys_addr_t pa;
> > +       void __iomem *va;
> > +       unsigned long size;
> > +};
> > +
> > +struct imsic_priv {
> > +       /* Global configuration common for all HARTs */
> > +       struct imsic_global_config global;
> > +
> > +       /* MMIO regions */
> > +       u32 num_mmios;
> > +       struct imsic_mmio *mmios;
> > +
> > +       /* Global state of interrupt identities */
> > +       raw_spinlock_t ids_lock;
> > +       unsigned long *ids_used_bimap;
> > +       unsigned long *ids_enabled_bimap;
> > +       unsigned int *ids_target_cpu;
> > +
> > +       /* Mask for connected CPUs */
> > +       struct cpumask lmask;
> > +
> > +       /* IPI interrupt identity */
> > +       bool slow_ipi;
> > +       u32 ipi_id;
> > +       u32 ipi_lsync_id;
> > +
> > +       /* IRQ domains */
> > +       struct irq_domain *base_domain;
> > +       struct irq_domain *pci_domain;
> > +       struct irq_domain *plat_domain;
> > +};
> > +
> > +struct imsic_handler {
> > +       /* Local configuration for given HART */
> > +       struct imsic_local_config local;
> > +
> > +       /* Pointer to private context */
> > +       struct imsic_priv *priv;
> > +};
> > +
> > +static bool imsic_init_done;
> > +
> > +static int imsic_parent_irq;
> > +static DEFINE_PER_CPU(struct imsic_handler, imsic_handlers);
> > +
> > +const struct imsic_global_config *imsic_get_global_config(void)
> > +{
> > +       struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
> > +
> > +       if (!handler || !handler->priv)
> > +               return NULL;
> > +
> > +       return &handler->priv->global;
> > +}
> > +EXPORT_SYMBOL_GPL(imsic_get_global_config);
> > +
> > +const struct imsic_local_config *imsic_get_local_config(unsigned int cpu)
> > +{
> > +       struct imsic_handler *handler = per_cpu_ptr(&imsic_handlers, cpu);
> > +
> > +       if (!handler || !handler->priv)
> > +               return NULL;
> > +
> > +       return &handler->local;
> > +}
> > +EXPORT_SYMBOL_GPL(imsic_get_local_config);
> > +
> > +static int imsic_cpu_page_phys(unsigned int cpu,
> > +                              unsigned int guest_index,
> > +                              phys_addr_t *out_msi_pa)
> > +{
> > +       struct imsic_handler *handler = per_cpu_ptr(&imsic_handlers, cpu);
> > +       struct imsic_global_config *global;
> > +       struct imsic_local_config *local;
> > +
> > +       if (!handler || !handler->priv)
> > +               return -ENODEV;
> > +       local = &handler->local;
> > +       global = &handler->priv->global;
> > +
> > +       if (BIT(global->guest_index_bits) <= guest_index)
> > +               return -EINVAL;
> > +
> > +       if (out_msi_pa)
> > +               *out_msi_pa = local->msi_pa +
> > +                             (guest_index * IMSIC_MMIO_PAGE_SZ);
> > +
> > +       return 0;
> > +}
> > +
> > +static int imsic_get_cpu(struct imsic_priv *priv,
> > +                        const struct cpumask *mask_val, bool force,
> > +                        unsigned int *out_target_cpu)
> > +{
> > +       struct cpumask amask;
> > +       unsigned int cpu;
> > +
> > +       cpumask_and(&amask, &priv->lmask, mask_val);
> > +
> > +       if (force)
> > +               cpu = cpumask_first(&amask);
> > +       else
> > +               cpu = cpumask_any_and(&amask, cpu_online_mask);
> > +
> > +       if (cpu >= nr_cpu_ids)
> > +               return -EINVAL;
> > +
> > +       if (out_target_cpu)
> > +               *out_target_cpu = cpu;
> > +
> > +       return 0;
> > +}
> > +
> > +static int imsic_get_cpu_msi_msg(unsigned int cpu, unsigned int id,
> > +                                struct msi_msg *msg)
> > +{
> > +       phys_addr_t msi_addr;
> > +       int err;
> > +
> > +       err = imsic_cpu_page_phys(cpu, 0, &msi_addr);
> > +       if (err)
> > +               return err;
> > +
> > +       msg->address_hi = upper_32_bits(msi_addr);
> > +       msg->address_lo = lower_32_bits(msi_addr);
> > +       msg->data = id;
> > +
> > +       return err;
> > +}
> > +
> > +static void imsic_id_set_target(struct imsic_priv *priv,
> > +                                unsigned int id, unsigned int target_cpu)
> > +{
> > +       raw_spin_lock(&priv->ids_lock);
> > +       priv->ids_target_cpu[id] = target_cpu;
> > +       raw_spin_unlock(&priv->ids_lock);
> > +}
> > +
> > +static unsigned int imsic_id_get_target(struct imsic_priv *priv,
> > +                                       unsigned int id)
> > +{
> > +       unsigned int ret;
> > +
> > +       raw_spin_lock(&priv->ids_lock);
>
> This needs to be a raw_spin_lock_irqsave() since hardirqs are enabled
> here and we also take priv->ids_lock in hardirq context (via
> imsic_ids_local_sync()). Booting with lockdep enabled produces this
> splat:
>
> [    1.830055] ================================
> [    1.830227] WARNING: inconsistent lock state
> [    1.830425] 6.1.0-rc4-00026-g78e09ffc5534 #42 Not tainted
> [    1.830655] --------------------------------
> [    1.830784] inconsistent {HARDIRQ-ON-W} -> {IN-HARDIRQ-W} usage.
> [    1.830972] swapper/0/1 [HC1[1]:SC0[0]:HE0:SE1] takes:
> [    1.831162] ff6000000229d048 (&priv->ids_lock){?.+.}-{2:2}, at:
> imsic_ids_local_sync+0x1e/0x98
> [    1.831951] {HARDIRQ-ON-W} state was registered at:
> [    1.832132]   __lock_acquire+0x93c/0x1e0c
> [    1.832305]   lock_acquire+0xfa/0x2da
> [    1.832412]   _raw_spin_lock+0x2c/0x40
> [    1.832531]   imsic_ids_local_sync+0x1e/0x98
> [    1.832674]   imsic_starting_cpu+0x72/0xac
> [    1.832852]   cpuhp_invoke_callback+0x18c/0x93c
> [    1.833033]   cpuhp_thread_fun+0x156/0x1b0
> [    1.833158]   smpboot_thread_fn+0xea/0x1c8
> [    1.833293]   kthread+0xc8/0xde
> [    1.833391]   ret_from_exception+0x0/0x10
> [    1.833530] irq event stamp: 33336
> [    1.833647] hardirqs last  enabled at (33335): [<ffffffff808f2fcc>]
> _raw_spin_unlock_irqrestore+0x4c/0x4e
> [    1.833925] hardirqs last disabled at (33336): [<ffffffff800082f4>]
> __trace_hardirqs_off+0xc/0x14
> [    1.834179] softirqs last  enabled at (33056): [<ffffffff808f3f3e>]
> __do_softirq+0x3de/0x51e
> [    1.834450] softirqs last disabled at (33051): [<ffffffff8001788a>]
> irq_exit+0xd6/0x104
> [    1.834705]
> [    1.834705] other info that might help us debug this:
> [    1.834925]  Possible unsafe locking scenario:
> [    1.834925]
> [    1.835093]        CPU0
> [    1.835176]        ----
> [    1.835253]   lock(&priv->ids_lock);
> [    1.835381]   <Interrupt>
> [    1.835462]     lock(&priv->ids_lock);
> ...
>
> Applies here and elsewhere where priv->ids_lock is taken in a possibly
> hardirqs-enabled context.

Okay, I will use raw_spin_lock_irqsave() here.

Thanks,
Anup

>
> -Andrew
>
> > +       ret = priv->ids_target_cpu[id];
> > +       raw_spin_unlock(&priv->ids_lock);
> > +
> > +       return ret;
> > +}
> > +
> > +static void __imsic_eix_update(unsigned long base_id,
> > +                              unsigned long num_id, bool pend, bool val)
> > +{
> > +       unsigned long i, isel, ireg, flags;
> > +       unsigned long id = base_id, last_id = base_id + num_id;
> > +
> > +       while (id < last_id) {
> > +               isel = id / BITS_PER_LONG;
> > +               isel *= BITS_PER_LONG / IMSIC_EIPx_BITS;
> > +               isel += (pend) ? IMSIC_EIP0 : IMSIC_EIE0;
> > +
> > +               ireg = 0;
> > +               for (i = id & (__riscv_xlen - 1);
> > +                    (id < last_id) && (i < __riscv_xlen); i++) {
> > +                       ireg |= BIT(i);
> > +                       id++;
> > +               }
> > +
> > +               /*
> > +                * The IMSIC EIEx and EIPx registers are indirectly
> > +                * accessed via using ISELECT and IREG CSRs so we
> > +                * save/restore local IRQ to ensure that we don't
> > +                * get preempted while accessing IMSIC registers.
> > +                */
> > +               local_irq_save(flags);
> > +               if (val)
> > +                       imsic_csr_set(isel, ireg);
> > +               else
> > +                       imsic_csr_clear(isel, ireg);
> > +               local_irq_restore(flags);
> > +       }
> > +}
> > +
> > +#define __imsic_id_enable(__id)                \
> > +       __imsic_eix_update((__id), 1, false, true)
> > +#define __imsic_id_disable(__id)       \
> > +       __imsic_eix_update((__id), 1, false, false)
> > +
> > +#ifdef CONFIG_SMP
> > +static void __imsic_id_smp_sync(struct imsic_priv *priv)
> > +{
> > +       struct imsic_handler *handler;
> > +       struct cpumask amask;
> > +       int cpu;
> > +
> > +       cpumask_and(&amask, &priv->lmask, cpu_online_mask);
> > +       for_each_cpu(cpu, &amask) {
> > +               if (cpu == smp_processor_id())
> > +                       continue;
> > +
> > +               handler = per_cpu_ptr(&imsic_handlers, cpu);
> > +               if (!handler || !handler->priv || !handler->local.msi_va) {
> > +                       pr_warn("CPU%d: handler not initialized\n", cpu);
> > +                       continue;
> > +               }
> > +
> > +               writel(handler->priv->ipi_lsync_id, handler->local.msi_va);
> > +       }
> > +}
> > +#else
> > +#define __imsic_id_smp_sync(__priv)
> > +#endif
> > +
> > +static void imsic_id_enable(struct imsic_priv *priv, unsigned int id)
> > +{
> > +       raw_spin_lock(&priv->ids_lock);
> > +       bitmap_set(priv->ids_enabled_bimap, id, 1);
> > +       __imsic_id_enable(id);
> > +       raw_spin_unlock(&priv->ids_lock);
> > +
> > +       __imsic_id_smp_sync(priv);
> > +}
> > +
> > +static void imsic_id_disable(struct imsic_priv *priv, unsigned int id)
> > +{
> > +       raw_spin_lock(&priv->ids_lock);
> > +       bitmap_clear(priv->ids_enabled_bimap, id, 1);
> > +       __imsic_id_disable(id);
> > +       raw_spin_unlock(&priv->ids_lock);
> > +
> > +       __imsic_id_smp_sync(priv);
> > +}
> > +
> > +static void imsic_ids_local_sync(struct imsic_priv *priv)
> > +{
> > +       int i;
> > +
> > +       raw_spin_lock(&priv->ids_lock);
> > +       for (i = 1; i <= priv->global.nr_ids; i++) {
> > +               if (priv->ipi_id == i || priv->ipi_lsync_id == i)
> > +                       continue;
> > +
> > +               if (test_bit(i, priv->ids_enabled_bimap))
> > +                       __imsic_id_enable(i);
> > +               else
> > +                       __imsic_id_disable(i);
> > +       }
> > +       raw_spin_unlock(&priv->ids_lock);
> > +}
> > +
> > +static void imsic_ids_local_delivery(struct imsic_priv *priv, bool enable)
> > +{
> > +       if (enable) {
> > +               imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_ENABLE_EITHRESHOLD);
> > +               imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_ENABLE_EIDELIVERY);
> > +       } else {
> > +               imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_DISABLE_EIDELIVERY);
> > +               imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_DISABLE_EITHRESHOLD);
> > +       }
> > +}
> > +
> > +static int imsic_ids_alloc(struct imsic_priv *priv,
> > +                          unsigned int max_id, unsigned int order)
> > +{
> > +       int ret;
> > +
> > +       if ((priv->global.nr_ids < max_id) ||
> > +           (max_id < BIT(order)))
> > +               return -EINVAL;
> > +
> > +       raw_spin_lock(&priv->ids_lock);
> > +       ret = bitmap_find_free_region(priv->ids_used_bimap,
> > +                                     max_id + 1, order);
> > +       raw_spin_unlock(&priv->ids_lock);
> > +
> > +       return ret;
> > +}
> > +
> > +static void imsic_ids_free(struct imsic_priv *priv, unsigned int base_id,
> > +                          unsigned int order)
> > +{
> > +       raw_spin_lock(&priv->ids_lock);
> > +       bitmap_release_region(priv->ids_used_bimap, base_id, order);
> > +       raw_spin_unlock(&priv->ids_lock);
> > +}
> > +
> > +static int __init imsic_ids_init(struct imsic_priv *priv)
> > +{
> > +       int i;
> > +       struct imsic_global_config *global = &priv->global;
> > +
> > +       raw_spin_lock_init(&priv->ids_lock);
> > +
> > +       /* Allocate used bitmap */
> > +       priv->ids_used_bimap = kcalloc(BITS_TO_LONGS(global->nr_ids + 1),
> > +                                       sizeof(unsigned long), GFP_KERNEL);
> > +       if (!priv->ids_used_bimap)
> > +               return -ENOMEM;
> > +
> > +       /* Allocate enabled bitmap */
> > +       priv->ids_enabled_bimap = kcalloc(BITS_TO_LONGS(global->nr_ids + 1),
> > +                                          sizeof(unsigned long), GFP_KERNEL);
> > +       if (!priv->ids_enabled_bimap) {
> > +               kfree(priv->ids_used_bimap);
> > +               return -ENOMEM;
> > +       }
> > +
> > +       /* Allocate target CPU array */
> > +       priv->ids_target_cpu = kcalloc(global->nr_ids + 1,
> > +                                      sizeof(unsigned int), GFP_KERNEL);
> > +       if (!priv->ids_target_cpu) {
> > +               kfree(priv->ids_enabled_bimap);
> > +               kfree(priv->ids_used_bimap);
> > +               return -ENOMEM;
> > +       }
> > +       for (i = 0; i <= global->nr_ids; i++)
> > +               priv->ids_target_cpu[i] = UINT_MAX;
> > +
> > +       /* Reserve ID#0 because it is special and never implemented */
> > +       bitmap_set(priv->ids_used_bimap, 0, 1);
> > +
> > +       return 0;
> > +}
> > +
> > +static void __init imsic_ids_cleanup(struct imsic_priv *priv)
> > +{
> > +       kfree(priv->ids_target_cpu);
> > +       kfree(priv->ids_enabled_bimap);
> > +       kfree(priv->ids_used_bimap);
> > +}
> > +
> > +#ifdef CONFIG_SMP
> > +static void imsic_ipi_send_mask(unsigned int parent_virq, void *data,
> > +                               const struct cpumask *mask)
> > +{
> > +       int cpu;
> > +       struct imsic_handler *handler;
> > +
> > +       for_each_cpu(cpu, mask) {
> > +               handler = per_cpu_ptr(&imsic_handlers, cpu);
> > +               if (!handler || !handler->priv || !handler->local.msi_va) {
> > +                       pr_warn("CPU%d: handler not initialized\n", cpu);
> > +                       continue;
> > +               }
> > +
> > +               writel(handler->priv->ipi_id, handler->local.msi_va);
> > +       }
> > +}
> > +
> > +static struct ipi_mux_ops imsic_ipi_ops = {
> > +       .ipi_mux_send = imsic_ipi_send_mask,
> > +};
> > +
> > +static void imsic_ipi_enable(struct imsic_priv *priv)
> > +{
> > +       __imsic_id_enable(priv->ipi_id);
> > +       __imsic_id_enable(priv->ipi_lsync_id);
> > +}
> > +
> > +static void imsic_ipi_disable(struct imsic_priv *priv)
> > +{
> > +       __imsic_id_disable(priv->ipi_lsync_id);
> > +       __imsic_id_disable(priv->ipi_id);
> > +}
> > +
> > +static int __init imsic_ipi_domain_init(struct imsic_priv *priv)
> > +{
> > +       int virq;
> > +
> > +       /* Skip IPI setup if IPIs are slow */
> > +       if (priv->slow_ipi)
> > +               goto skip_ipi;
> > +
> > +       /* Allocate interrupt identity for IPIs */
> > +       virq = imsic_ids_alloc(priv, priv->global.nr_ids, get_count_order(1));
> > +       if (virq < 0)
> > +               return virq;
> > +       priv->ipi_id = virq;
> > +
> > +       /* Create IMSIC IPI multiplexing */
> > +       virq = ipi_mux_create(0, BITS_PER_BYTE, &imsic_ipi_ops, NULL);
> > +       if (virq <= 0) {
> > +               imsic_ids_free(priv, priv->ipi_id, get_count_order(1));
> > +               return (virq < 0) ? virq : -ENOMEM;
> > +       }
> > +
> > +       /* Set vIRQ range */
> > +       riscv_ipi_set_virq_range(virq, BITS_PER_BYTE, true);
> > +
> > +skip_ipi:
> > +       /* Allocate interrupt identity for local enable/disable sync */
> > +       virq = imsic_ids_alloc(priv, priv->global.nr_ids, get_count_order(1));
> > +       if (virq < 0) {
> > +               imsic_ids_free(priv, priv->ipi_id, get_count_order(1));
> > +               return virq;
> > +       }
> > +       priv->ipi_lsync_id = virq;
> > +
> > +       return 0;
> > +}
> > +
> > +static void __init imsic_ipi_domain_cleanup(struct imsic_priv *priv)
> > +{
> > +       imsic_ids_free(priv, priv->ipi_lsync_id, get_count_order(1));
> > +       if (priv->ipi_id)
> > +               imsic_ids_free(priv, priv->ipi_id, get_count_order(1));
> > +}
> > +#else
> > +static void imsic_ipi_enable(struct imsic_priv *priv)
> > +{
> > +}
> > +
> > +static void imsic_ipi_disable(struct imsic_priv *priv)
> > +{
> > +}
> > +
> > +static int __init imsic_ipi_domain_init(struct imsic_priv *priv)
> > +{
> > +       /* Clear the IPI ids because we are not using IPIs */
> > +       priv->ipi_id = 0;
> > +       priv->ipi_lsync_id = 0;
> > +       return 0;
> > +}
> > +
> > +static void __init imsic_ipi_domain_cleanup(struct imsic_priv *priv)
> > +{
> > +}
> > +#endif
> > +
> > +static void imsic_irq_mask(struct irq_data *d)
> > +{
> > +       imsic_id_disable(irq_data_get_irq_chip_data(d), d->hwirq);
> > +}
> > +
> > +static void imsic_irq_unmask(struct irq_data *d)
> > +{
> > +       imsic_id_enable(irq_data_get_irq_chip_data(d), d->hwirq);
> > +}
> > +
> > +static void imsic_irq_compose_msi_msg(struct irq_data *d,
> > +                                     struct msi_msg *msg)
> > +{
> > +       struct imsic_priv *priv = irq_data_get_irq_chip_data(d);
> > +       unsigned int cpu;
> > +       int err;
> > +
> > +       cpu = imsic_id_get_target(priv, d->hwirq);
> > +       WARN_ON(cpu == UINT_MAX);
> > +
> > +       err = imsic_get_cpu_msi_msg(cpu, d->hwirq, msg);
> > +       WARN_ON(err);
> > +
> > +       iommu_dma_compose_msi_msg(irq_data_get_msi_desc(d), msg);
> > +}
> > +
> > +#ifdef CONFIG_SMP
> > +static int imsic_irq_set_affinity(struct irq_data *d,
> > +                                 const struct cpumask *mask_val,
> > +                                 bool force)
> > +{
> > +       struct imsic_priv *priv = irq_data_get_irq_chip_data(d);
> > +       unsigned int target_cpu;
> > +       int rc;
> > +
> > +       rc = imsic_get_cpu(priv, mask_val, force, &target_cpu);
> > +       if (rc)
> > +               return rc;
> > +
> > +       imsic_id_set_target(priv, d->hwirq, target_cpu);
> > +       irq_data_update_effective_affinity(d, cpumask_of(target_cpu));
> > +
> > +       return IRQ_SET_MASK_OK;
> > +}
> > +#endif
> > +
> > +static struct irq_chip imsic_irq_base_chip = {
> > +       .name                   = "RISC-V IMSIC-BASE",
> > +       .irq_mask               = imsic_irq_mask,
> > +       .irq_unmask             = imsic_irq_unmask,
> > +#ifdef CONFIG_SMP
> > +       .irq_set_affinity       = imsic_irq_set_affinity,
> > +#endif
> > +       .irq_compose_msi_msg    = imsic_irq_compose_msi_msg,
> > +       .flags                  = IRQCHIP_SKIP_SET_WAKE |
> > +                                 IRQCHIP_MASK_ON_SUSPEND,
> > +};
> > +
> > +static int imsic_irq_domain_alloc(struct irq_domain *domain,
> > +                                 unsigned int virq,
> > +                                 unsigned int nr_irqs,
> > +                                 void *args)
> > +{
> > +       struct imsic_priv *priv = domain->host_data;
> > +       msi_alloc_info_t *info = args;
> > +       phys_addr_t msi_addr;
> > +       int i, hwirq, err = 0;
> > +       unsigned int cpu;
> > +
> > +       err = imsic_get_cpu(priv, &priv->lmask, false, &cpu);
> > +       if (err)
> > +               return err;
> > +
> > +       err = imsic_cpu_page_phys(cpu, 0, &msi_addr);
> > +       if (err)
> > +               return err;
> > +
> > +       hwirq = imsic_ids_alloc(priv, priv->global.nr_ids,
> > +                               get_count_order(nr_irqs));
> > +       if (hwirq < 0)
> > +               return hwirq;
> > +
> > +       err = iommu_dma_prepare_msi(info->desc, msi_addr);
> > +       if (err)
> > +               goto fail;
> > +
> > +       for (i = 0; i < nr_irqs; i++) {
> > +               imsic_id_set_target(priv, hwirq + i, cpu);
> > +               irq_domain_set_info(domain, virq + i, hwirq + i,
> > +                                   &imsic_irq_base_chip, priv,
> > +                                   handle_simple_irq, NULL, NULL);
> > +               irq_set_noprobe(virq + i);
> > +               irq_set_affinity(virq + i, &priv->lmask);
> > +       }
> > +
> > +       return 0;
> > +
> > +fail:
> > +       imsic_ids_free(priv, hwirq, get_count_order(nr_irqs));
> > +       return err;
> > +}
> > +
> > +static void imsic_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 imsic_priv *priv = domain->host_data;
> > +
> > +       imsic_ids_free(priv, d->hwirq, get_count_order(nr_irqs));
> > +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> > +}
> > +
> > +static const struct irq_domain_ops imsic_base_domain_ops = {
> > +       .alloc          = imsic_irq_domain_alloc,
> > +       .free           = imsic_irq_domain_free,
> > +};
> > +
> > +#ifdef CONFIG_RISCV_IMSIC_PCI
> > +
> > +static void imsic_pci_mask_irq(struct irq_data *d)
> > +{
> > +       pci_msi_mask_irq(d);
> > +       irq_chip_mask_parent(d);
> > +}
> > +
> > +static void imsic_pci_unmask_irq(struct irq_data *d)
> > +{
> > +       pci_msi_unmask_irq(d);
> > +       irq_chip_unmask_parent(d);
> > +}
> > +
> > +static struct irq_chip imsic_pci_irq_chip = {
> > +       .name                   = "RISC-V IMSIC-PCI",
> > +       .irq_mask               = imsic_pci_mask_irq,
> > +       .irq_unmask             = imsic_pci_unmask_irq,
> > +       .irq_eoi                = irq_chip_eoi_parent,
> > +};
> > +
> > +static struct msi_domain_ops imsic_pci_domain_ops = {
> > +};
> > +
> > +static struct msi_domain_info imsic_pci_domain_info = {
> > +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> > +                  MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI),
> > +       .ops    = &imsic_pci_domain_ops,
> > +       .chip   = &imsic_pci_irq_chip,
> > +};
> > +
> > +#endif
> > +
> > +static struct irq_chip imsic_plat_irq_chip = {
> > +       .name                   = "RISC-V IMSIC-PLAT",
> > +};
> > +
> > +static struct msi_domain_ops imsic_plat_domain_ops = {
> > +};
> > +
> > +static struct msi_domain_info imsic_plat_domain_info = {
> > +       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
> > +       .ops    = &imsic_plat_domain_ops,
> > +       .chip   = &imsic_plat_irq_chip,
> > +};
> > +
> > +static int __init imsic_irq_domains_init(struct imsic_priv *priv,
> > +                                        struct fwnode_handle *fwnode)
> > +{
> > +       /* Create Base IRQ domain */
> > +       priv->base_domain = irq_domain_create_tree(fwnode,
> > +                                               &imsic_base_domain_ops, priv);
> > +       if (!priv->base_domain) {
> > +               pr_err("Failed to create IMSIC base domain\n");
> > +               return -ENOMEM;
> > +       }
> > +       irq_domain_update_bus_token(priv->base_domain, DOMAIN_BUS_NEXUS);
> > +
> > +#ifdef CONFIG_RISCV_IMSIC_PCI
> > +       /* Create PCI MSI domain */
> > +       priv->pci_domain = pci_msi_create_irq_domain(fwnode,
> > +                                               &imsic_pci_domain_info,
> > +                                               priv->base_domain);
> > +       if (!priv->pci_domain) {
> > +               pr_err("Failed to create IMSIC PCI domain\n");
> > +               irq_domain_remove(priv->base_domain);
> > +               return -ENOMEM;
> > +       }
> > +#endif
> > +
> > +       /* Create Platform MSI domain */
> > +       priv->plat_domain = platform_msi_create_irq_domain(fwnode,
> > +                                               &imsic_plat_domain_info,
> > +                                               priv->base_domain);
> > +       if (!priv->plat_domain) {
> > +               pr_err("Failed to create IMSIC platform domain\n");
> > +               if (priv->pci_domain)
> > +                       irq_domain_remove(priv->pci_domain);
> > +               irq_domain_remove(priv->base_domain);
> > +               return -ENOMEM;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +/*
> > + * To handle an interrupt, we read the TOPEI CSR and write zero in one
> > + * instruction. If TOPEI CSR is non-zero then we translate TOPEI.ID to
> > + * Linux interrupt number and let Linux IRQ subsystem handle it.
> > + */
> > +static void imsic_handle_irq(struct irq_desc *desc)
> > +{
> > +       struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
> > +       struct irq_chip *chip = irq_desc_get_chip(desc);
> > +       struct imsic_priv *priv = handler->priv;
> > +       irq_hw_number_t hwirq;
> > +       int err;
> > +
> > +       WARN_ON_ONCE(!handler->priv);
> > +
> > +       chained_irq_enter(chip, desc);
> > +
> > +       while ((hwirq = csr_swap(CSR_TOPEI, 0))) {
> > +               hwirq = hwirq >> TOPEI_ID_SHIFT;
> > +
> > +               if (hwirq == priv->ipi_id) {
> > +#ifdef CONFIG_SMP
> > +                       ipi_mux_process();
> > +#endif
> > +                       continue;
> > +               } else if (hwirq == priv->ipi_lsync_id) {
> > +                       imsic_ids_local_sync(priv);
> > +                       continue;
> > +               }
> > +
> > +               err = generic_handle_domain_irq(priv->base_domain, hwirq);
> > +               if (unlikely(err))
> > +                       pr_warn_ratelimited(
> > +                               "hwirq %lu mapping not found\n", hwirq);
> > +       }
> > +
> > +       chained_irq_exit(chip, desc);
> > +}
> > +
> > +static int imsic_dying_cpu(unsigned int cpu)
> > +{
> > +       struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
> > +       struct imsic_priv *priv = handler->priv;
> > +
> > +       /* Disable per-CPU parent interrupt */
> > +       if (imsic_parent_irq)
> > +               disable_percpu_irq(imsic_parent_irq);
> > +
> > +       /* Locally disable interrupt delivery */
> > +       imsic_ids_local_delivery(priv, false);
> > +
> > +       /* Disable IPIs */
> > +       imsic_ipi_disable(priv);
> > +
> > +       return 0;
> > +}
> > +
> > +static int imsic_starting_cpu(unsigned int cpu)
> > +{
> > +       struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
> > +       struct imsic_priv *priv = handler->priv;
> > +
> > +       /* Enable per-CPU parent interrupt */
> > +       if (imsic_parent_irq)
> > +               enable_percpu_irq(imsic_parent_irq,
> > +                                 irq_get_trigger_type(imsic_parent_irq));
> > +       else
> > +               pr_warn("cpu%d: parent irq not available\n", cpu);
> > +
> > +       /* Enable IPIs */
> > +       imsic_ipi_enable(priv);
> > +
> > +       /*
> > +        * Interrupts identities might have been enabled/disabled while
> > +        * this CPU was not running so sync-up local enable/disable state.
> > +        */
> > +       imsic_ids_local_sync(priv);
> > +
> > +       /* Locally enable interrupt delivery */
> > +       imsic_ids_local_delivery(priv, true);
> > +
> > +       return 0;
> > +}
> > +
> > +struct imsic_fwnode_ops {
> > +       u32 (*nr_parent_irq)(struct fwnode_handle *fwnode,
> > +                            void *fwopaque);
> > +       int (*parent_hartid)(struct fwnode_handle *fwnode,
> > +                            void *fwopaque, u32 index,
> > +                            unsigned long *out_hartid);
> > +       u32 (*nr_mmio)(struct fwnode_handle *fwnode, void *fwopaque);
> > +       int (*mmio_to_resource)(struct fwnode_handle *fwnode,
> > +                               void *fwopaque, u32 index,
> > +                               struct resource *res);
> > +       void __iomem *(*mmio_map)(struct fwnode_handle *fwnode,
> > +                                 void *fwopaque, u32 index);
> > +       int (*read_u32)(struct fwnode_handle *fwnode,
> > +                       void *fwopaque, const char *prop, u32 *out_val);
> > +       bool (*read_bool)(struct fwnode_handle *fwnode,
> > +                         void *fwopaque, const char *prop);
> > +};
> > +
> > +static int __init imsic_init(struct imsic_fwnode_ops *fwops,
> > +                            struct fwnode_handle *fwnode,
> > +                            void *fwopaque)
> > +{
> > +       struct resource res;
> > +       phys_addr_t base_addr;
> > +       int rc, nr_parent_irqs;
> > +       struct imsic_mmio *mmio;
> > +       struct imsic_priv *priv;
> > +       struct irq_domain *domain;
> > +       struct imsic_handler *handler;
> > +       struct imsic_global_config *global;
> > +       u32 i, tmp, nr_handlers = 0;
> > +
> > +       if (imsic_init_done) {
> > +               pr_err("%pfwP: already initialized hence ignoring\n",
> > +                       fwnode);
> > +               return -ENODEV;
> > +       }
> > +
> > +       if (!riscv_isa_extension_available(NULL, SxAIA)) {
> > +               pr_err("%pfwP: AIA support not available\n", fwnode);
> > +               return -ENODEV;
> > +       }
> > +
> > +       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> > +       if (!priv)
> > +               return -ENOMEM;
> > +       global = &priv->global;
> > +
> > +       /* Find number of parent interrupts */
> > +       nr_parent_irqs = fwops->nr_parent_irq(fwnode, fwopaque);
> > +       if (!nr_parent_irqs) {
> > +               pr_err("%pfwP: no parent irqs available\n", fwnode);
> > +               return -EINVAL;
> > +       }
> > +
> > +       /* Find number of guest index bits in MSI address */
> > +       rc = fwops->read_u32(fwnode, fwopaque, "riscv,guest-index-bits",
> > +                            &global->guest_index_bits);
> > +       if (rc)
> > +               global->guest_index_bits = 0;
> > +       tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT;
> > +       if (tmp < global->guest_index_bits) {
> > +               pr_err("%pfwP: guest index bits too big\n", fwnode);
> > +               return -EINVAL;
> > +       }
> > +
> > +       /* Find number of HART index bits */
> > +       rc = fwops->read_u32(fwnode, fwopaque, "riscv,hart-index-bits",
> > +                            &global->hart_index_bits);
> > +       if (rc) {
> > +               /* Assume default value */
> > +               global->hart_index_bits = __fls(nr_parent_irqs);
> > +               if (BIT(global->hart_index_bits) < nr_parent_irqs)
> > +                       global->hart_index_bits++;
> > +       }
> > +       tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT -
> > +             global->guest_index_bits;
> > +       if (tmp < global->hart_index_bits) {
> > +               pr_err("%pfwP: HART index bits too big\n", fwnode);
> > +               return -EINVAL;
> > +       }
> > +
> > +       /* Find number of group index bits */
> > +       rc = fwops->read_u32(fwnode, fwopaque, "riscv,group-index-bits",
> > +                            &global->group_index_bits);
> > +       if (rc)
> > +               global->group_index_bits = 0;
> > +       tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT -
> > +             global->guest_index_bits - global->hart_index_bits;
> > +       if (tmp < global->group_index_bits) {
> > +               pr_err("%pfwP: group index bits too big\n", fwnode);
> > +               return -EINVAL;
> > +       }
> > +
> > +       /* Find first bit position of group index */
> > +       tmp = IMSIC_MMIO_PAGE_SHIFT * 2;
> > +       rc = fwops->read_u32(fwnode, fwopaque, "riscv,group-index-shift",
> > +                            &global->group_index_shift);
> > +       if (rc)
> > +               global->group_index_shift = tmp;
> > +       if (global->group_index_shift < tmp) {
> > +               pr_err("%pfwP: group index shift too small\n", fwnode);
> > +               return -EINVAL;
> > +       }
> > +       tmp = global->group_index_bits + global->group_index_shift - 1;
> > +       if (tmp >= BITS_PER_LONG) {
> > +               pr_err("%pfwP: group index shift too big\n", fwnode);
> > +               return -EINVAL;
> > +       }
> > +
> > +       /* Find number of interrupt identities */
> > +       rc = fwops->read_u32(fwnode, fwopaque, "riscv,num-ids",
> > +                            &global->nr_ids);
> > +       if (rc) {
> > +               pr_err("%pfwP: number of interrupt identities not found\n",
> > +                       fwnode);
> > +               return rc;
> > +       }
> > +       if ((global->nr_ids < IMSIC_MIN_ID) ||
> > +           (global->nr_ids >= IMSIC_MAX_ID) ||
> > +           ((global->nr_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID)) {
> > +               pr_err("%pfwP: invalid number of interrupt identities\n",
> > +                       fwnode);
> > +               return -EINVAL;
> > +       }
> > +
> > +       /* Find number of guest interrupt identities */
> > +       if (fwops->read_u32(fwnode, fwopaque, "riscv,num-guest-ids",
> > +                           &global->nr_guest_ids))
> > +               global->nr_guest_ids = global->nr_ids;
> > +       if ((global->nr_guest_ids < IMSIC_MIN_ID) ||
> > +           (global->nr_guest_ids >= IMSIC_MAX_ID) ||
> > +           ((global->nr_guest_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID)) {
> > +               pr_err("%pfwP: invalid number of guest interrupt identities\n",
> > +                       fwnode);
> > +               return -EINVAL;
> > +       }
> > +
> > +       /* Check if IPIs are slow */
> > +       priv->slow_ipi = fwops->read_bool(fwnode, fwopaque, "riscv,slow-ipi");
> > +
> > +       /* Compute base address */
> > +       rc = fwops->mmio_to_resource(fwnode, fwopaque, 0, &res);
> > +       if (rc) {
> > +               pr_err("%pfwP: first MMIO resource not found\n", fwnode);
> > +               return -EINVAL;
> > +       }
> > +       global->base_addr = res.start;
> > +       global->base_addr &= ~(BIT(global->guest_index_bits +
> > +                                  global->hart_index_bits +
> > +                                  IMSIC_MMIO_PAGE_SHIFT) - 1);
> > +       global->base_addr &= ~((BIT(global->group_index_bits) - 1) <<
> > +                              global->group_index_shift);
> > +
> > +       /* Find number of MMIO register sets */
> > +       priv->num_mmios = fwops->nr_mmio(fwnode, fwopaque);
> > +
> > +       /* Allocate MMIO register sets */
> > +       priv->mmios = kcalloc(priv->num_mmios, sizeof(*mmio), GFP_KERNEL);
> > +       if (!priv->mmios) {
> > +               rc = -ENOMEM;
> > +               goto out_free_priv;
> > +       }
> > +
> > +       /* Parse and map MMIO register sets */
> > +       for (i = 0; i < priv->num_mmios; i++) {
> > +               mmio = &priv->mmios[i];
> > +               rc = fwops->mmio_to_resource(fwnode, fwopaque, i, &res);
> > +               if (rc) {
> > +                       pr_err("%pfwP: unable to parse MMIO regset %d\n",
> > +                               fwnode, i);
> > +                       goto out_iounmap;
> > +               }
> > +               mmio->pa = res.start;
> > +               mmio->size = res.end - res.start + 1;
> > +
> > +               base_addr = mmio->pa;
> > +               base_addr &= ~(BIT(global->guest_index_bits +
> > +                                  global->hart_index_bits +
> > +                                  IMSIC_MMIO_PAGE_SHIFT) - 1);
> > +               base_addr &= ~((BIT(global->group_index_bits) - 1) <<
> > +                              global->group_index_shift);
> > +               if (base_addr != global->base_addr) {
> > +                       rc = -EINVAL;
> > +                       pr_err("%pfwP: address mismatch for regset %d\n",
> > +                               fwnode, i);
> > +                       goto out_iounmap;
> > +               }
> > +
> > +               tmp = BIT(global->guest_index_bits) - 1;
> > +               if ((mmio->size / IMSIC_MMIO_PAGE_SZ) & tmp) {
> > +                       rc = -EINVAL;
> > +                       pr_err("%pfwP: size mismatch for regset %d\n",
> > +                               fwnode, i);
> > +                       goto out_iounmap;
> > +               }
> > +
> > +               mmio->va = fwops->mmio_map(fwnode, fwopaque, i);
> > +               if (!mmio->va) {
> > +                       rc = -EIO;
> > +                       pr_err("%pfwP: unable to map MMIO regset %d\n",
> > +                               fwnode, i);
> > +                       goto out_iounmap;
> > +               }
> > +       }
> > +
> > +       /* Initialize interrupt identity management */
> > +       rc = imsic_ids_init(priv);
> > +       if (rc) {
> > +               pr_err("%pfwP: failed to initialize interrupt management\n",
> > +                      fwnode);
> > +               goto out_iounmap;
> > +       }
> > +
> > +       /* Configure handlers for target CPUs */
> > +       for (i = 0; i < nr_parent_irqs; i++) {
> > +               unsigned long reloff, hartid;
> > +               int j, cpu;
> > +
> > +               rc = fwops->parent_hartid(fwnode, fwopaque, i, &hartid);
> > +               if (rc) {
> > +                       pr_warn("%pfwP: hart ID for parent irq%d not found\n",
> > +                               fwnode, i);
> > +                       continue;
> > +               }
> > +
> > +               cpu = riscv_hartid_to_cpuid(hartid);
> > +               if (cpu < 0) {
> > +                       pr_warn("%pfwP: invalid cpuid for parent irq%d\n",
> > +                               fwnode, i);
> > +                       continue;
> > +               }
> > +
> > +               /* Find MMIO location of MSI page */
> > +               mmio = NULL;
> > +               reloff = i * BIT(global->guest_index_bits) *
> > +                        IMSIC_MMIO_PAGE_SZ;
> > +               for (j = 0; priv->num_mmios; j++) {
> > +                       if (reloff < priv->mmios[j].size) {
> > +                               mmio = &priv->mmios[j];
> > +                               break;
> > +                       }
> > +
> > +                       reloff -= priv->mmios[j].size;
> > +               }
> > +               if (!mmio) {
> > +                       pr_warn("%pfwP: MMIO not found for parent irq%d\n",
> > +                               fwnode, i);
> > +                       continue;
> > +               }
> > +
> > +               handler = per_cpu_ptr(&imsic_handlers, cpu);
> > +               if (handler->priv) {
> > +                       pr_warn("%pfwP: CPU%d handler already configured.\n",
> > +                               fwnode, cpu);
> > +                       goto done;
> > +               }
> > +
> > +               cpumask_set_cpu(cpu, &priv->lmask);
> > +               handler->local.msi_pa = mmio->pa + reloff;
> > +               handler->local.msi_va = mmio->va + reloff;
> > +               handler->priv = priv;
> > +
> > +done:
> > +               nr_handlers++;
> > +       }
> > +
> > +       /* If no CPU handlers found then can't take interrupts */
> > +       if (!nr_handlers) {
> > +               pr_err("%pfwP: No CPU handlers found\n", fwnode);
> > +               rc = -ENODEV;
> > +               goto out_ids_cleanup;
> > +       }
> > +
> > +       /* Find parent domain and register chained handler */
> > +       domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(),
> > +                                         DOMAIN_BUS_ANY);
> > +       if (!domain) {
> > +               pr_err("%pfwP: Failed to find INTC domain\n", fwnode);
> > +               rc = -ENOENT;
> > +               goto out_ids_cleanup;
> > +       }
> > +       imsic_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT);
> > +       if (!imsic_parent_irq) {
> > +               pr_err("%pfwP: Failed to create INTC mapping\n", fwnode);
> > +               rc = -ENOENT;
> > +               goto out_ids_cleanup;
> > +       }
> > +       irq_set_chained_handler(imsic_parent_irq, imsic_handle_irq);
> > +
> > +       /* Initialize IPI domain */
> > +       rc = imsic_ipi_domain_init(priv);
> > +       if (rc) {
> > +               pr_err("%pfwP: Failed to initialize IPI domain\n", fwnode);
> > +               goto out_ids_cleanup;
> > +       }
> > +
> > +       /* Initialize IRQ and MSI domains */
> > +       rc = imsic_irq_domains_init(priv, fwnode);
> > +       if (rc) {
> > +               pr_err("%pfwP: Failed to initialize IRQ and MSI domains\n",
> > +                      fwnode);
> > +               goto out_ipi_domain_cleanup;
> > +       }
> > +
> > +       /* Setup cpuhp state */
> > +       cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
> > +                         "irqchip/riscv/imsic:starting",
> > +                         imsic_starting_cpu, imsic_dying_cpu);
> > +
> > +       /*
> > +        * Only one IMSIC instance allowed in a platform for clean
> > +        * implementation of SMP IRQ affinity and per-CPU IPIs.
> > +        *
> > +        * This means on a multi-socket (or multi-die) platform we
> > +        * will have multiple MMIO regions for one IMSIC instance.
> > +        */
> > +       imsic_init_done = true;
> > +
> > +       pr_info("%pfwP:  hart-index-bits: %d,  guest-index-bits: %d\n",
> > +               fwnode, global->hart_index_bits, global->guest_index_bits);
> > +       pr_info("%pfwP: group-index-bits: %d, group-index-shift: %d\n",
> > +               fwnode, global->group_index_bits, global->group_index_shift);
> > +       pr_info("%pfwP: mapped %d interrupts for %d CPUs at %pa\n",
> > +               fwnode, global->nr_ids, nr_handlers, &global->base_addr);
> > +       if (priv->ipi_lsync_id)
> > +               pr_info("%pfwP: enable/disable sync using interrupt %d\n",
> > +                       fwnode, priv->ipi_lsync_id);
> > +       if (priv->ipi_id)
> > +               pr_info("%pfwP: providing IPIs using interrupt %d\n",
> > +                       fwnode, priv->ipi_id);
> > +
> > +       return 0;
> > +
> > +out_ipi_domain_cleanup:
> > +       imsic_ipi_domain_cleanup(priv);
> > +out_ids_cleanup:
> > +       imsic_ids_cleanup(priv);
> > +out_iounmap:
> > +       for (i = 0; i < priv->num_mmios; i++) {
> > +               if (priv->mmios[i].va)
> > +                       iounmap(priv->mmios[i].va);
> > +       }
> > +       kfree(priv->mmios);
> > +out_free_priv:
> > +       kfree(priv);
> > +       return rc;
> > +}
> > +
> > +static u32 __init imsic_dt_nr_parent_irq(struct fwnode_handle *fwnode,
> > +                                        void *fwopaque)
> > +{
> > +       return of_irq_count(to_of_node(fwnode));
> > +}
> > +
> > +static int __init imsic_dt_parent_hartid(struct fwnode_handle *fwnode,
> > +                                        void *fwopaque, u32 index,
> > +                                        unsigned long *out_hartid)
> > +{
> > +       struct of_phandle_args parent;
> > +       int rc;
> > +
> > +       rc = of_irq_parse_one(to_of_node(fwnode), index, &parent);
> > +       if (rc)
> > +               return rc;
> > +
> > +       /*
> > +        * Skip interrupts other than external interrupts for
> > +        * current privilege level.
> > +        */
> > +       if (parent.args[0] != RV_IRQ_EXT)
> > +               return -EINVAL;
> > +
> > +       return riscv_of_parent_hartid(parent.np, out_hartid);
> > +}
> > +
> > +static u32 __init imsic_dt_nr_mmio(struct fwnode_handle *fwnode,
> > +                                  void *fwopaque)
> > +{
> > +       u32 ret = 0;
> > +       struct resource res;
> > +
> > +       while (!of_address_to_resource(to_of_node(fwnode), ret, &res))
> > +               ret++;
> > +
> > +       return ret;
> > +}
> > +
> > +static int __init imsic_mmio_to_resource(struct fwnode_handle *fwnode,
> > +                                        void *fwopaque, u32 index,
> > +                                        struct resource *res)
> > +{
> > +       return of_address_to_resource(to_of_node(fwnode), index, res);
> > +}
> > +
> > +static void __iomem __init *imsic_dt_mmio_map(struct fwnode_handle *fwnode,
> > +                                             void *fwopaque, u32 index)
> > +{
> > +       return of_iomap(to_of_node(fwnode), index);
> > +}
> > +
> > +static int __init imsic_dt_read_u32(struct fwnode_handle *fwnode,
> > +                                   void *fwopaque, const char *prop,
> > +                                   u32 *out_val)
> > +{
> > +       return of_property_read_u32(to_of_node(fwnode), prop, out_val);
> > +}
> > +
> > +static bool __init imsic_dt_read_bool(struct fwnode_handle *fwnode,
> > +                                     void *fwopaque, const char *prop)
> > +{
> > +       return of_property_read_bool(to_of_node(fwnode), prop);
> > +}
> > +
> > +static int __init imsic_dt_init(struct device_node *node,
> > +                               struct device_node *parent)
> > +{
> > +       struct imsic_fwnode_ops ops = {
> > +               .nr_parent_irq = imsic_dt_nr_parent_irq,
> > +               .parent_hartid = imsic_dt_parent_hartid,
> > +               .nr_mmio = imsic_dt_nr_mmio,
> > +               .mmio_to_resource = imsic_mmio_to_resource,
> > +               .mmio_map = imsic_dt_mmio_map,
> > +               .read_u32 = imsic_dt_read_u32,
> > +               .read_bool = imsic_dt_read_bool,
> > +       };
> > +
> > +       return imsic_init(&ops, &node->fwnode, NULL);
> > +}
> > +IRQCHIP_DECLARE(riscv_imsic, "riscv,imsics", imsic_dt_init);
> > diff --git a/include/linux/irqchip/riscv-imsic.h b/include/linux/irqchip/riscv-imsic.h
> > new file mode 100644
> > index 000000000000..5d1387adc0ba
> > --- /dev/null
> > +++ b/include/linux/irqchip/riscv-imsic.h
> > @@ -0,0 +1,92 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * Copyright (C) 2021 Western Digital Corporation or its affiliates.
> > + * Copyright (C) 2022 Ventana Micro Systems Inc.
> > + */
> > +#ifndef __LINUX_IRQCHIP_RISCV_IMSIC_H
> > +#define __LINUX_IRQCHIP_RISCV_IMSIC_H
> > +
> > +#include <linux/types.h>
> > +#include <asm/csr.h>
> > +
> > +#define IMSIC_MMIO_PAGE_SHIFT          12
> > +#define IMSIC_MMIO_PAGE_SZ             (1UL << IMSIC_MMIO_PAGE_SHIFT)
> > +#define IMSIC_MMIO_PAGE_LE             0x00
> > +#define IMSIC_MMIO_PAGE_BE             0x04
> > +
> > +#define IMSIC_MIN_ID                   63
> > +#define IMSIC_MAX_ID                   2048
> > +
> > +#define IMSIC_EIDELIVERY               0x70
> > +
> > +#define IMSIC_EITHRESHOLD              0x72
> > +
> > +#define IMSIC_EIP0                     0x80
> > +#define IMSIC_EIP63                    0xbf
> > +#define IMSIC_EIPx_BITS                        32
> > +
> > +#define IMSIC_EIE0                     0xc0
> > +#define IMSIC_EIE63                    0xff
> > +#define IMSIC_EIEx_BITS                        32
> > +
> > +#define IMSIC_FIRST                    IMSIC_EIDELIVERY
> > +#define IMSIC_LAST                     IMSIC_EIE63
> > +
> > +#define IMSIC_MMIO_SETIPNUM_LE         0x00
> > +#define IMSIC_MMIO_SETIPNUM_BE         0x04
> > +
> > +struct imsic_global_config {
> > +       /*
> > +        * MSI Target Address Scheme
> > +        *
> > +        * XLEN-1                                                12     0
> > +        * |                                                     |     |
> > +        * -------------------------------------------------------------
> > +        * |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
> > +        * -------------------------------------------------------------
> > +        */
> > +
> > +       /* Bits representing Guest index, HART index, and Group index */
> > +       u32 guest_index_bits;
> > +       u32 hart_index_bits;
> > +       u32 group_index_bits;
> > +       u32 group_index_shift;
> > +
> > +       /* Global base address matching all target MSI addresses */
> > +       phys_addr_t base_addr;
> > +
> > +       /* Number of interrupt identities */
> > +       u32 nr_ids;
> > +
> > +       /* Number of guest interrupt identities */
> > +       u32 nr_guest_ids;
> > +};
> > +
> > +struct imsic_local_config {
> > +       phys_addr_t msi_pa;
> > +       void __iomem *msi_va;
> > +};
> > +
> > +#ifdef CONFIG_RISCV_IMSIC
> > +
> > +extern const struct imsic_global_config *imsic_get_global_config(void);
> > +
> > +extern const struct imsic_local_config *imsic_get_local_config(
> > +                                                       unsigned int cpu);
> > +
> > +#else
> > +
> > +static inline const struct imsic_global_config *imsic_get_global_config(void)
> > +{
> > +       return NULL;
> > +}
> > +
> > +static inline const struct imsic_local_config *imsic_get_local_config(
> > +                                                       unsigned int cpu)
> > +{
> > +       return NULL;
> > +}
> > +
> > +#endif
> > +
> > +#endif
> > --
> > 2.34.1
> >

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 6/9] dt-bindings: Add RISC-V advanced PLIC bindings
  2022-11-13 15:44     ` Conor Dooley
@ 2023-01-02 16:50       ` Anup Patel
  -1 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2023-01-02 16:50 UTC (permalink / raw)
  To: Conor Dooley
  Cc: Anup Patel, Palmer Dabbelt, Paul Walmsley, Thomas Gleixner,
	Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Atish Patra,
	Alistair Francis, linux-riscv, linux-kernel, devicetree

On Sun, Nov 13, 2022 at 9:14 PM Conor Dooley <conor@kernel.org> wrote:
>
> Hey Anup,
>
> Ditto the $subject nit here.

Adding "interrupt-controller:" to subject makes it longer than 80 characters.

>
> On Fri, Nov 11, 2022 at 10:12:04AM +0530, Anup Patel wrote:
> > We add DT bindings document for RISC-V advanced platform level interrupt
> > controller (APLIC) defined by the RISC-V advanced interrupt architecture
> > (AIA) specification.
> >
> > Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> > ---
> >  .../interrupt-controller/riscv,aplic.yaml     | 136 ++++++++++++++++++
> >  1 file changed, 136 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> > new file mode 100644
> > index 000000000000..0aa48571f3bc
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> > @@ -0,0 +1,136 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/interrupt-controller/riscv,aplic.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: RISC-V Advancded Platform Level Interrupt Controller (APLIC)
>
> Typo: Advanced

Okay, I will update.

>
> > +
> > +maintainers:
> > +  - Anup Patel <anup@brainfault.org>
> > +
> > +description:
> > +  The RISC-V advanced interrupt architecture (AIA) defines advanced platform
>                                                              ^
> Missing an article here?

Okay, I will update.

>
> > +  level interrupt controller (APLIC) for handling wired interrupts in a
> > +  RISC-V platform. The RISC-V AIA specification can be found at
> > +  https://github.com/riscv/riscv-aia.
> > +
> > +  The RISC-V APLIC is implemented as hierarchical APLIC domains where all
> > +  interrupt sources connect to the root domain which can further delegate
> > +  interrupts to child domains. We have one device tree node for each APLIC
>
> While I am nitpicking, s/We have/There is/ ?

Okay, I will update.

>
> > +  domain.
> > +
> > +allOf:
> > +  - $ref: /schemas/interrupt-controller.yaml#
> > +
> > +properties:
> > +  compatible:
> > +    items:
> > +      - enum:
> > +          - vendor,chip-aplic
>
> Same comment here about the validity of this placeholder.

Okay, I will add "riscv,qemu-aplic" as QEMU specific compatible string.

>
> > +      - const: riscv,aplic
> > +
> > +  reg:
> > +    maxItems: 1
> > +
> > +  interrupt-controller: true
> > +
> > +  "#interrupt-cells":
> > +    const: 2
> > +
> > +  interrupts-extended:
> > +    minItems: 1
> > +    maxItems: 16384
> > +    description:
> > +      The presence of this property implies that given APLIC domain directly
>                                                    ^
> Missing indefinite article here (and in msi-parent)?
>
> > +      injects external interrupts to a set of RISC-V HARTS (or CPUs). Each
> > +      node pointed to should be a riscv,cpu-intc node, which has a riscv node
> > +      (i.e. RISC-V HART) as parent.
> > +
> > +  msi-parent:
> > +    description:
> > +      The presence of this property implies that given APLIC domain forwards
> > +      wired interrupts as MSIs to a AIA incoming message signaled interrupt
> > +      controller (IMSIC). This property should be considered only when the
> > +      interrupts-extended property is absent.
>
> This mutual exclusion can be represented, can't it?
> IIRC it is some sort of oneOf thing, somewhat like below:
> oneOf:
>   - required:
>       - msi-parent
>   - required:
>       - interrupts-extended
>
> AFAIR from doing the i2c ocores binding, this will force the addition of
> one, but not both, to a node.
>
> Or is this not actually mutually exclusive & the msi-parent property is
> permitted but just left unused if interrupts-extended is present?

If both are present then interrupts-extended is preferred.

>
> > +  riscv,num-sources:
> > +    $ref: "/schemas/types.yaml#/definitions/uint32"
> > +    minimum: 1
> > +    maximum: 1023
> > +    description:
> > +      Specifies how many wired interrupts are supported by this APLIC domain.
> > +
> > +  riscv,children:
> > +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> > +    minItems: 1
> > +    maxItems: 1024
> > +    description:
> > +      This property represents a list of child APLIC domains for the given
> > +      APLIC domain. Each child APLIC domain is assigned child index in
> > +      increasing order with the first child APLIC domain assigned child
> > +      index 0. The APLIC domain child index is used by firmware to delegate
> > +      interrupts from the given APLIC domain to a particular child APLIC
> > +      domain.
> > +
> > +  riscv,delegate:
> > +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> > +    minItems: 1
> > +    maxItems: 1024
> > +    description:
> > +      This property represents a interrupt delegation list where each entry
> > +      is a triple consisting of child APLIC domain phandle, first interrupt
> > +      number, and last interrupt number. The firmware will configure interrupt
> > +      delegation registers based on interrupt delegation list.
>
> What is the inter dependence of the children and delegate?
> Is it valid to have a delegate property without children?
> Can the firmware delegate interrupts without the delegation list, based
> on the children property alone? Or is it effectively useless without a
> children property?

Both properties convey different information. The "riscv,childen" describes
the association of child indexes with child APLIC domains whereas the
"riscv,delegate" describes the interrupt delegation to few of the child
APLIC domains.


>
> In your examples, the second has msi-parent but neither of these custom
> properties. Do the children/delegate properties have a meaning in the
> msi-parent case?

The "riscv,childern" and "riscv,delegate" are only useful when we have
hierarchy of multiple APLIC domains. The second example only has
one APLIC domain hence these custom properties are absent.

>
> I think the binding should enforce whatever dependency exists there.
> Thanks,
> Conor.
>
> > +
> > +additionalProperties: false
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - interrupt-controller
> > +  - "#interrupt-cells"
> > +  - riscv,num-sources
> > +
> > +examples:
> > +  - |
> > +    // Example 1 (APIC domain directly injecting interrupt to HARTs):
> > +
> > +    aplic0: interrupt-controller@c000000 {
> > +      compatible = "vendor,chip-aplic", "riscv,aplic";
> > +      interrupts-extended = <&cpu1_intc 11>,
> > +                            <&cpu2_intc 11>,
> > +                            <&cpu3_intc 11>,
> > +                            <&cpu4_intc 11>;
> > +      reg = <0xc000000 0x4080>;
> > +      interrupt-controller;
> > +      #interrupt-cells = <2>;
> > +      riscv,num-sources = <63>;
> > +      riscv,children = <&aplic1>;
> > +      riscv,delegate = <&aplic1 1 63>;
> > +    };
> > +
> > +    aplic1: interrupt-controller@d000000 {
> > +      compatible = "vendor,chip-aplic", "riscv,aplic";
> > +      interrupts-extended = <&cpu1_intc 9>,
> > +                            <&cpu2_intc 9>,
> > +                            <&cpu3_intc 9>,
> > +                            <&cpu4_intc 9>;
> > +      reg = <0xd000000 0x4080>;
> > +      interrupt-controller;
> > +      #interrupt-cells = <2>;
> > +      riscv,num-sources = <63>;
> > +    };
> > +
> > +  - |
> > +    // Example 2 (APIC domain forwarding interrupts as MSIs):
> > +
> > +    interrupt-controller@d000000 {
> > +      compatible = "vendor,chip-aplic", "riscv,aplic";
> > +      msi-parent = <&imsics>;
> > +      reg = <0xd000000 0x4000>;
> > +      interrupt-controller;
> > +      #interrupt-cells = <2>;
> > +      riscv,num-sources = <63>;
> > +    };
> > +...
> > --
> > 2.34.1
> >
> >
> > _______________________________________________
> > linux-riscv mailing list
> > linux-riscv@lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/linux-riscv

Regards,
Anup

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

* Re: [PATCH 6/9] dt-bindings: Add RISC-V advanced PLIC bindings
@ 2023-01-02 16:50       ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2023-01-02 16:50 UTC (permalink / raw)
  To: Conor Dooley
  Cc: Anup Patel, Palmer Dabbelt, Paul Walmsley, Thomas Gleixner,
	Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Atish Patra,
	Alistair Francis, linux-riscv, linux-kernel, devicetree

On Sun, Nov 13, 2022 at 9:14 PM Conor Dooley <conor@kernel.org> wrote:
>
> Hey Anup,
>
> Ditto the $subject nit here.

Adding "interrupt-controller:" to subject makes it longer than 80 characters.

>
> On Fri, Nov 11, 2022 at 10:12:04AM +0530, Anup Patel wrote:
> > We add DT bindings document for RISC-V advanced platform level interrupt
> > controller (APLIC) defined by the RISC-V advanced interrupt architecture
> > (AIA) specification.
> >
> > Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> > ---
> >  .../interrupt-controller/riscv,aplic.yaml     | 136 ++++++++++++++++++
> >  1 file changed, 136 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> > new file mode 100644
> > index 000000000000..0aa48571f3bc
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> > @@ -0,0 +1,136 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/interrupt-controller/riscv,aplic.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: RISC-V Advancded Platform Level Interrupt Controller (APLIC)
>
> Typo: Advanced

Okay, I will update.

>
> > +
> > +maintainers:
> > +  - Anup Patel <anup@brainfault.org>
> > +
> > +description:
> > +  The RISC-V advanced interrupt architecture (AIA) defines advanced platform
>                                                              ^
> Missing an article here?

Okay, I will update.

>
> > +  level interrupt controller (APLIC) for handling wired interrupts in a
> > +  RISC-V platform. The RISC-V AIA specification can be found at
> > +  https://github.com/riscv/riscv-aia.
> > +
> > +  The RISC-V APLIC is implemented as hierarchical APLIC domains where all
> > +  interrupt sources connect to the root domain which can further delegate
> > +  interrupts to child domains. We have one device tree node for each APLIC
>
> While I am nitpicking, s/We have/There is/ ?

Okay, I will update.

>
> > +  domain.
> > +
> > +allOf:
> > +  - $ref: /schemas/interrupt-controller.yaml#
> > +
> > +properties:
> > +  compatible:
> > +    items:
> > +      - enum:
> > +          - vendor,chip-aplic
>
> Same comment here about the validity of this placeholder.

Okay, I will add "riscv,qemu-aplic" as QEMU specific compatible string.

>
> > +      - const: riscv,aplic
> > +
> > +  reg:
> > +    maxItems: 1
> > +
> > +  interrupt-controller: true
> > +
> > +  "#interrupt-cells":
> > +    const: 2
> > +
> > +  interrupts-extended:
> > +    minItems: 1
> > +    maxItems: 16384
> > +    description:
> > +      The presence of this property implies that given APLIC domain directly
>                                                    ^
> Missing indefinite article here (and in msi-parent)?
>
> > +      injects external interrupts to a set of RISC-V HARTS (or CPUs). Each
> > +      node pointed to should be a riscv,cpu-intc node, which has a riscv node
> > +      (i.e. RISC-V HART) as parent.
> > +
> > +  msi-parent:
> > +    description:
> > +      The presence of this property implies that given APLIC domain forwards
> > +      wired interrupts as MSIs to a AIA incoming message signaled interrupt
> > +      controller (IMSIC). This property should be considered only when the
> > +      interrupts-extended property is absent.
>
> This mutual exclusion can be represented, can't it?
> IIRC it is some sort of oneOf thing, somewhat like below:
> oneOf:
>   - required:
>       - msi-parent
>   - required:
>       - interrupts-extended
>
> AFAIR from doing the i2c ocores binding, this will force the addition of
> one, but not both, to a node.
>
> Or is this not actually mutually exclusive & the msi-parent property is
> permitted but just left unused if interrupts-extended is present?

If both are present then interrupts-extended is preferred.

>
> > +  riscv,num-sources:
> > +    $ref: "/schemas/types.yaml#/definitions/uint32"
> > +    minimum: 1
> > +    maximum: 1023
> > +    description:
> > +      Specifies how many wired interrupts are supported by this APLIC domain.
> > +
> > +  riscv,children:
> > +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> > +    minItems: 1
> > +    maxItems: 1024
> > +    description:
> > +      This property represents a list of child APLIC domains for the given
> > +      APLIC domain. Each child APLIC domain is assigned child index in
> > +      increasing order with the first child APLIC domain assigned child
> > +      index 0. The APLIC domain child index is used by firmware to delegate
> > +      interrupts from the given APLIC domain to a particular child APLIC
> > +      domain.
> > +
> > +  riscv,delegate:
> > +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> > +    minItems: 1
> > +    maxItems: 1024
> > +    description:
> > +      This property represents a interrupt delegation list where each entry
> > +      is a triple consisting of child APLIC domain phandle, first interrupt
> > +      number, and last interrupt number. The firmware will configure interrupt
> > +      delegation registers based on interrupt delegation list.
>
> What is the inter dependence of the children and delegate?
> Is it valid to have a delegate property without children?
> Can the firmware delegate interrupts without the delegation list, based
> on the children property alone? Or is it effectively useless without a
> children property?

Both properties convey different information. The "riscv,childen" describes
the association of child indexes with child APLIC domains whereas the
"riscv,delegate" describes the interrupt delegation to few of the child
APLIC domains.


>
> In your examples, the second has msi-parent but neither of these custom
> properties. Do the children/delegate properties have a meaning in the
> msi-parent case?

The "riscv,childern" and "riscv,delegate" are only useful when we have
hierarchy of multiple APLIC domains. The second example only has
one APLIC domain hence these custom properties are absent.

>
> I think the binding should enforce whatever dependency exists there.
> Thanks,
> Conor.
>
> > +
> > +additionalProperties: false
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - interrupt-controller
> > +  - "#interrupt-cells"
> > +  - riscv,num-sources
> > +
> > +examples:
> > +  - |
> > +    // Example 1 (APIC domain directly injecting interrupt to HARTs):
> > +
> > +    aplic0: interrupt-controller@c000000 {
> > +      compatible = "vendor,chip-aplic", "riscv,aplic";
> > +      interrupts-extended = <&cpu1_intc 11>,
> > +                            <&cpu2_intc 11>,
> > +                            <&cpu3_intc 11>,
> > +                            <&cpu4_intc 11>;
> > +      reg = <0xc000000 0x4080>;
> > +      interrupt-controller;
> > +      #interrupt-cells = <2>;
> > +      riscv,num-sources = <63>;
> > +      riscv,children = <&aplic1>;
> > +      riscv,delegate = <&aplic1 1 63>;
> > +    };
> > +
> > +    aplic1: interrupt-controller@d000000 {
> > +      compatible = "vendor,chip-aplic", "riscv,aplic";
> > +      interrupts-extended = <&cpu1_intc 9>,
> > +                            <&cpu2_intc 9>,
> > +                            <&cpu3_intc 9>,
> > +                            <&cpu4_intc 9>;
> > +      reg = <0xd000000 0x4080>;
> > +      interrupt-controller;
> > +      #interrupt-cells = <2>;
> > +      riscv,num-sources = <63>;
> > +    };
> > +
> > +  - |
> > +    // Example 2 (APIC domain forwarding interrupts as MSIs):
> > +
> > +    interrupt-controller@d000000 {
> > +      compatible = "vendor,chip-aplic", "riscv,aplic";
> > +      msi-parent = <&imsics>;
> > +      reg = <0xd000000 0x4000>;
> > +      interrupt-controller;
> > +      #interrupt-cells = <2>;
> > +      riscv,num-sources = <63>;
> > +    };
> > +...
> > --
> > 2.34.1
> >
> >
> > _______________________________________________
> > linux-riscv mailing list
> > linux-riscv@lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/linux-riscv

Regards,
Anup

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 6/9] dt-bindings: Add RISC-V advanced PLIC bindings
  2022-11-16 19:27     ` Rob Herring
@ 2023-01-02 17:18       ` Anup Patel
  -1 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2023-01-02 17:18 UTC (permalink / raw)
  To: Rob Herring
  Cc: Anup Patel, Palmer Dabbelt, Paul Walmsley, Thomas Gleixner,
	Marc Zyngier, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	linux-riscv, linux-kernel, devicetree

On Thu, Nov 17, 2022 at 12:57 AM Rob Herring <robh@kernel.org> wrote:
>
> On Fri, Nov 11, 2022 at 10:12:04AM +0530, Anup Patel wrote:
> > We add DT bindings document for RISC-V advanced platform level interrupt
> > controller (APLIC) defined by the RISC-V advanced interrupt architecture
> > (AIA) specification.
> >
> > Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> > ---
> >  .../interrupt-controller/riscv,aplic.yaml     | 136 ++++++++++++++++++
> >  1 file changed, 136 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> > new file mode 100644
> > index 000000000000..0aa48571f3bc
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> > @@ -0,0 +1,136 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/interrupt-controller/riscv,aplic.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: RISC-V Advancded Platform Level Interrupt Controller (APLIC)
> > +
> > +maintainers:
> > +  - Anup Patel <anup@brainfault.org>
> > +
> > +description:
> > +  The RISC-V advanced interrupt architecture (AIA) defines advanced platform
> > +  level interrupt controller (APLIC) for handling wired interrupts in a
> > +  RISC-V platform. The RISC-V AIA specification can be found at
> > +  https://github.com/riscv/riscv-aia.
> > +
> > +  The RISC-V APLIC is implemented as hierarchical APLIC domains where all
> > +  interrupt sources connect to the root domain which can further delegate
> > +  interrupts to child domains. We have one device tree node for each APLIC
> > +  domain.
> > +
> > +allOf:
> > +  - $ref: /schemas/interrupt-controller.yaml#
> > +
> > +properties:
> > +  compatible:
> > +    items:
> > +      - enum:
> > +          - vendor,chip-aplic
> > +      - const: riscv,aplic
> > +
> > +  reg:
> > +    maxItems: 1
> > +
> > +  interrupt-controller: true
> > +
> > +  "#interrupt-cells":
> > +    const: 2
> > +
> > +  interrupts-extended:
> > +    minItems: 1
> > +    maxItems: 16384
> > +    description:
> > +      The presence of this property implies that given APLIC domain directly
> > +      injects external interrupts to a set of RISC-V HARTS (or CPUs). Each
> > +      node pointed to should be a riscv,cpu-intc node, which has a riscv node
> > +      (i.e. RISC-V HART) as parent.
> > +
> > +  msi-parent:
> > +    description:
> > +      The presence of this property implies that given APLIC domain forwards
> > +      wired interrupts as MSIs to a AIA incoming message signaled interrupt
> > +      controller (IMSIC). This property should be considered only when the
> > +      interrupts-extended property is absent.
> > +
> > +  riscv,num-sources:
> > +    $ref: "/schemas/types.yaml#/definitions/uint32"
> > +    minimum: 1
> > +    maximum: 1023
> > +    description:
> > +      Specifies how many wired interrupts are supported by this APLIC domain.
> > +
> > +  riscv,children:
> > +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> > +    minItems: 1
> > +    maxItems: 1024
>
> As each entry is a single phandle:
>
>        items:
>          maxItems: 1

Okay, I will update.

>
> > +    description:
> > +      This property represents a list of child APLIC domains for the given
> > +      APLIC domain. Each child APLIC domain is assigned child index in
> > +      increasing order with the first child APLIC domain assigned child
> > +      index 0. The APLIC domain child index is used by firmware to delegate
> > +      interrupts from the given APLIC domain to a particular child APLIC
> > +      domain.
> > +
> > +  riscv,delegate:
> > +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> > +    minItems: 1
> > +    maxItems: 1024
>
>        items:
>          items:
>            - description: child APLIC domain phandle
>            - description: ...
>            - description: ...

Okay, I will update.

>
> > +    description:
> > +      This property represents a interrupt delegation list where each entry
> > +      is a triple consisting of child APLIC domain phandle, first interrupt
> > +      number, and last interrupt number. The firmware will configure interrupt
> > +      delegation registers based on interrupt delegation list.
>
> First and last are inclusive?

Yes, first and last are inclusive. I will clarify this in the description.

>
> Couldn't riscv,children and riscv,delegate be combined? How would they
> be different? If some children don't have any delegated interrupts, you
> could use -1 for the cells for example.

The "riscv,children" describes the hierarchy of APLIC domains in HW
whereas "riscv,delegate" describes the system choices of delegating
interrupts from a parent APLIC domain to one of its children. I feel
it's not natural to combine these two properties.

>
> An example showing the need would be nice.
>
> > +
> > +additionalProperties: false
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - interrupt-controller
> > +  - "#interrupt-cells"
> > +  - riscv,num-sources
> > +
> > +examples:
> > +  - |
> > +    // Example 1 (APIC domain directly injecting interrupt to HARTs):
>
> Is than an x86 APIC or a typo?
>
> > +
> > +    aplic0: interrupt-controller@c000000 {
> > +      compatible = "vendor,chip-aplic", "riscv,aplic";
> > +      interrupts-extended = <&cpu1_intc 11>,
> > +                            <&cpu2_intc 11>,
> > +                            <&cpu3_intc 11>,
> > +                            <&cpu4_intc 11>;
> > +      reg = <0xc000000 0x4080>;
> > +      interrupt-controller;
> > +      #interrupt-cells = <2>;
> > +      riscv,num-sources = <63>;
> > +      riscv,children = <&aplic1>;
> > +      riscv,delegate = <&aplic1 1 63>;
> > +    };
> > +
> > +    aplic1: interrupt-controller@d000000 {
> > +      compatible = "vendor,chip-aplic", "riscv,aplic";
> > +      interrupts-extended = <&cpu1_intc 9>,
> > +                            <&cpu2_intc 9>,
> > +                            <&cpu3_intc 9>,
> > +                            <&cpu4_intc 9>;
> > +      reg = <0xd000000 0x4080>;
> > +      interrupt-controller;
> > +      #interrupt-cells = <2>;
> > +      riscv,num-sources = <63>;
> > +    };
> > +
> > +  - |
> > +    // Example 2 (APIC domain forwarding interrupts as MSIs):
> > +
> > +    interrupt-controller@d000000 {
> > +      compatible = "vendor,chip-aplic", "riscv,aplic";
> > +      msi-parent = <&imsics>;
> > +      reg = <0xd000000 0x4000>;
> > +      interrupt-controller;
> > +      #interrupt-cells = <2>;
> > +      riscv,num-sources = <63>;
> > +    };
> > +...
> > --
> > 2.34.1
> >
> >

Regards,
Anup

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

* Re: [PATCH 6/9] dt-bindings: Add RISC-V advanced PLIC bindings
@ 2023-01-02 17:18       ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2023-01-02 17:18 UTC (permalink / raw)
  To: Rob Herring
  Cc: Anup Patel, Palmer Dabbelt, Paul Walmsley, Thomas Gleixner,
	Marc Zyngier, Krzysztof Kozlowski, Atish Patra, Alistair Francis,
	linux-riscv, linux-kernel, devicetree

On Thu, Nov 17, 2022 at 12:57 AM Rob Herring <robh@kernel.org> wrote:
>
> On Fri, Nov 11, 2022 at 10:12:04AM +0530, Anup Patel wrote:
> > We add DT bindings document for RISC-V advanced platform level interrupt
> > controller (APLIC) defined by the RISC-V advanced interrupt architecture
> > (AIA) specification.
> >
> > Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> > ---
> >  .../interrupt-controller/riscv,aplic.yaml     | 136 ++++++++++++++++++
> >  1 file changed, 136 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> > new file mode 100644
> > index 000000000000..0aa48571f3bc
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml
> > @@ -0,0 +1,136 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/interrupt-controller/riscv,aplic.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: RISC-V Advancded Platform Level Interrupt Controller (APLIC)
> > +
> > +maintainers:
> > +  - Anup Patel <anup@brainfault.org>
> > +
> > +description:
> > +  The RISC-V advanced interrupt architecture (AIA) defines advanced platform
> > +  level interrupt controller (APLIC) for handling wired interrupts in a
> > +  RISC-V platform. The RISC-V AIA specification can be found at
> > +  https://github.com/riscv/riscv-aia.
> > +
> > +  The RISC-V APLIC is implemented as hierarchical APLIC domains where all
> > +  interrupt sources connect to the root domain which can further delegate
> > +  interrupts to child domains. We have one device tree node for each APLIC
> > +  domain.
> > +
> > +allOf:
> > +  - $ref: /schemas/interrupt-controller.yaml#
> > +
> > +properties:
> > +  compatible:
> > +    items:
> > +      - enum:
> > +          - vendor,chip-aplic
> > +      - const: riscv,aplic
> > +
> > +  reg:
> > +    maxItems: 1
> > +
> > +  interrupt-controller: true
> > +
> > +  "#interrupt-cells":
> > +    const: 2
> > +
> > +  interrupts-extended:
> > +    minItems: 1
> > +    maxItems: 16384
> > +    description:
> > +      The presence of this property implies that given APLIC domain directly
> > +      injects external interrupts to a set of RISC-V HARTS (or CPUs). Each
> > +      node pointed to should be a riscv,cpu-intc node, which has a riscv node
> > +      (i.e. RISC-V HART) as parent.
> > +
> > +  msi-parent:
> > +    description:
> > +      The presence of this property implies that given APLIC domain forwards
> > +      wired interrupts as MSIs to a AIA incoming message signaled interrupt
> > +      controller (IMSIC). This property should be considered only when the
> > +      interrupts-extended property is absent.
> > +
> > +  riscv,num-sources:
> > +    $ref: "/schemas/types.yaml#/definitions/uint32"
> > +    minimum: 1
> > +    maximum: 1023
> > +    description:
> > +      Specifies how many wired interrupts are supported by this APLIC domain.
> > +
> > +  riscv,children:
> > +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> > +    minItems: 1
> > +    maxItems: 1024
>
> As each entry is a single phandle:
>
>        items:
>          maxItems: 1

Okay, I will update.

>
> > +    description:
> > +      This property represents a list of child APLIC domains for the given
> > +      APLIC domain. Each child APLIC domain is assigned child index in
> > +      increasing order with the first child APLIC domain assigned child
> > +      index 0. The APLIC domain child index is used by firmware to delegate
> > +      interrupts from the given APLIC domain to a particular child APLIC
> > +      domain.
> > +
> > +  riscv,delegate:
> > +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> > +    minItems: 1
> > +    maxItems: 1024
>
>        items:
>          items:
>            - description: child APLIC domain phandle
>            - description: ...
>            - description: ...

Okay, I will update.

>
> > +    description:
> > +      This property represents a interrupt delegation list where each entry
> > +      is a triple consisting of child APLIC domain phandle, first interrupt
> > +      number, and last interrupt number. The firmware will configure interrupt
> > +      delegation registers based on interrupt delegation list.
>
> First and last are inclusive?

Yes, first and last are inclusive. I will clarify this in the description.

>
> Couldn't riscv,children and riscv,delegate be combined? How would they
> be different? If some children don't have any delegated interrupts, you
> could use -1 for the cells for example.

The "riscv,children" describes the hierarchy of APLIC domains in HW
whereas "riscv,delegate" describes the system choices of delegating
interrupts from a parent APLIC domain to one of its children. I feel
it's not natural to combine these two properties.

>
> An example showing the need would be nice.
>
> > +
> > +additionalProperties: false
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - interrupt-controller
> > +  - "#interrupt-cells"
> > +  - riscv,num-sources
> > +
> > +examples:
> > +  - |
> > +    // Example 1 (APIC domain directly injecting interrupt to HARTs):
>
> Is than an x86 APIC or a typo?
>
> > +
> > +    aplic0: interrupt-controller@c000000 {
> > +      compatible = "vendor,chip-aplic", "riscv,aplic";
> > +      interrupts-extended = <&cpu1_intc 11>,
> > +                            <&cpu2_intc 11>,
> > +                            <&cpu3_intc 11>,
> > +                            <&cpu4_intc 11>;
> > +      reg = <0xc000000 0x4080>;
> > +      interrupt-controller;
> > +      #interrupt-cells = <2>;
> > +      riscv,num-sources = <63>;
> > +      riscv,children = <&aplic1>;
> > +      riscv,delegate = <&aplic1 1 63>;
> > +    };
> > +
> > +    aplic1: interrupt-controller@d000000 {
> > +      compatible = "vendor,chip-aplic", "riscv,aplic";
> > +      interrupts-extended = <&cpu1_intc 9>,
> > +                            <&cpu2_intc 9>,
> > +                            <&cpu3_intc 9>,
> > +                            <&cpu4_intc 9>;
> > +      reg = <0xd000000 0x4080>;
> > +      interrupt-controller;
> > +      #interrupt-cells = <2>;
> > +      riscv,num-sources = <63>;
> > +    };
> > +
> > +  - |
> > +    // Example 2 (APIC domain forwarding interrupts as MSIs):
> > +
> > +    interrupt-controller@d000000 {
> > +      compatible = "vendor,chip-aplic", "riscv,aplic";
> > +      msi-parent = <&imsics>;
> > +      reg = <0xd000000 0x4000>;
> > +      interrupt-controller;
> > +      #interrupt-cells = <2>;
> > +      riscv,num-sources = <63>;
> > +    };
> > +...
> > --
> > 2.34.1
> >
> >

Regards,
Anup

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 6/9] dt-bindings: Add RISC-V advanced PLIC bindings
  2023-01-02 16:50       ` Anup Patel
@ 2023-01-02 18:17         ` Conor Dooley
  -1 siblings, 0 replies; 96+ messages in thread
From: Conor Dooley @ 2023-01-02 18:17 UTC (permalink / raw)
  To: Anup Patel
  Cc: Anup Patel, Palmer Dabbelt, Paul Walmsley, Thomas Gleixner,
	Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Atish Patra,
	Alistair Francis, linux-riscv, linux-kernel, devicetree

[-- Attachment #1: Type: text/plain, Size: 4020 bytes --]

On Mon, Jan 02, 2023 at 10:20:48PM +0530, Anup Patel wrote:
> On Sun, Nov 13, 2022 at 9:14 PM Conor Dooley <conor@kernel.org> wrote:

> > > +  domain.
> > > +
> > > +allOf:
> > > +  - $ref: /schemas/interrupt-controller.yaml#
> > > +
> > > +properties:
> > > +  compatible:
> > > +    items:
> > > +      - enum:
> > > +          - vendor,chip-aplic
> >
> > Same comment here about the validity of this placeholder.
> 
> Okay, I will add "riscv,qemu-aplic" as QEMU specific compatible string.

Ah neat. I think that's a fair compromise.

> > > +      - const: riscv,aplic

> > > +  msi-parent:
> > > +    description:
> > > +      The presence of this property implies that given APLIC domain forwards
> > > +      wired interrupts as MSIs to a AIA incoming message signaled interrupt
> > > +      controller (IMSIC). This property should be considered only when the
> > > +      interrupts-extended property is absent.
> >
> > This mutual exclusion can be represented, can't it?
> > IIRC it is some sort of oneOf thing, somewhat like below:
> > oneOf:
> >   - required:
> >       - msi-parent
> >   - required:
> >       - interrupts-extended
> >
> > AFAIR from doing the i2c ocores binding, this will force the addition of
> > one, but not both, to a node.
> >
> > Or is this not actually mutually exclusive & the msi-parent property is
> > permitted but just left unused if interrupts-extended is present?
> 
> If both are present then interrupts-extended is preferred.

Perhaps I am making a fool of myself here, but why would someone include
both of them at once, if only one is going to be used?
It would appear that making them explicitly mutually exclusive would
make the binding easier to understand.
What am I missing?

> > > +  riscv,children:
> > > +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> > > +    minItems: 1
> > > +    maxItems: 1024
> > > +    description:
> > > +      This property represents a list of child APLIC domains for the given
> > > +      APLIC domain. Each child APLIC domain is assigned child index in
> > > +      increasing order with the first child APLIC domain assigned child
> > > +      index 0. The APLIC domain child index is used by firmware to delegate
> > > +      interrupts from the given APLIC domain to a particular child APLIC
> > > +      domain.
> > > +
> > > +  riscv,delegate:
> > > +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> > > +    minItems: 1
> > > +    maxItems: 1024
> > > +    description:
> > > +      This property represents a interrupt delegation list where each entry
> > > +      is a triple consisting of child APLIC domain phandle, first interrupt
> > > +      number, and last interrupt number. The firmware will configure interrupt
> > > +      delegation registers based on interrupt delegation list.
> >
> > What is the inter dependence of the children and delegate?
> > Is it valid to have a delegate property without children?
> > Can the firmware delegate interrupts without the delegation list, based
> > on the children property alone? Or is it effectively useless without a
> > children property?
> 
> Both properties convey different information. The "riscv,childen" describes
> the association of child indexes with child APLIC domains whereas the
> "riscv,delegate" describes the interrupt delegation to few of the child
> APLIC domains.
> 
> 
> >
> > In your examples, the second has msi-parent but neither of these custom
> > properties. Do the children/delegate properties have a meaning in the
> > msi-parent case?
> 
> The "riscv,childern" and "riscv,delegate" are only useful when we have
> hierarchy of multiple APLIC domains. The second example only has
> one APLIC domain hence these custom properties are absent.

It'd be great if you could include an example that explains the
difference as, IIRC, both Rob and I both were kinda confused as to how
the properties differ.

Thanks,
Conor.


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

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

* Re: [PATCH 6/9] dt-bindings: Add RISC-V advanced PLIC bindings
@ 2023-01-02 18:17         ` Conor Dooley
  0 siblings, 0 replies; 96+ messages in thread
From: Conor Dooley @ 2023-01-02 18:17 UTC (permalink / raw)
  To: Anup Patel
  Cc: Anup Patel, Palmer Dabbelt, Paul Walmsley, Thomas Gleixner,
	Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Atish Patra,
	Alistair Francis, linux-riscv, linux-kernel, devicetree


[-- Attachment #1.1: Type: text/plain, Size: 4020 bytes --]

On Mon, Jan 02, 2023 at 10:20:48PM +0530, Anup Patel wrote:
> On Sun, Nov 13, 2022 at 9:14 PM Conor Dooley <conor@kernel.org> wrote:

> > > +  domain.
> > > +
> > > +allOf:
> > > +  - $ref: /schemas/interrupt-controller.yaml#
> > > +
> > > +properties:
> > > +  compatible:
> > > +    items:
> > > +      - enum:
> > > +          - vendor,chip-aplic
> >
> > Same comment here about the validity of this placeholder.
> 
> Okay, I will add "riscv,qemu-aplic" as QEMU specific compatible string.

Ah neat. I think that's a fair compromise.

> > > +      - const: riscv,aplic

> > > +  msi-parent:
> > > +    description:
> > > +      The presence of this property implies that given APLIC domain forwards
> > > +      wired interrupts as MSIs to a AIA incoming message signaled interrupt
> > > +      controller (IMSIC). This property should be considered only when the
> > > +      interrupts-extended property is absent.
> >
> > This mutual exclusion can be represented, can't it?
> > IIRC it is some sort of oneOf thing, somewhat like below:
> > oneOf:
> >   - required:
> >       - msi-parent
> >   - required:
> >       - interrupts-extended
> >
> > AFAIR from doing the i2c ocores binding, this will force the addition of
> > one, but not both, to a node.
> >
> > Or is this not actually mutually exclusive & the msi-parent property is
> > permitted but just left unused if interrupts-extended is present?
> 
> If both are present then interrupts-extended is preferred.

Perhaps I am making a fool of myself here, but why would someone include
both of them at once, if only one is going to be used?
It would appear that making them explicitly mutually exclusive would
make the binding easier to understand.
What am I missing?

> > > +  riscv,children:
> > > +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> > > +    minItems: 1
> > > +    maxItems: 1024
> > > +    description:
> > > +      This property represents a list of child APLIC domains for the given
> > > +      APLIC domain. Each child APLIC domain is assigned child index in
> > > +      increasing order with the first child APLIC domain assigned child
> > > +      index 0. The APLIC domain child index is used by firmware to delegate
> > > +      interrupts from the given APLIC domain to a particular child APLIC
> > > +      domain.
> > > +
> > > +  riscv,delegate:
> > > +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> > > +    minItems: 1
> > > +    maxItems: 1024
> > > +    description:
> > > +      This property represents a interrupt delegation list where each entry
> > > +      is a triple consisting of child APLIC domain phandle, first interrupt
> > > +      number, and last interrupt number. The firmware will configure interrupt
> > > +      delegation registers based on interrupt delegation list.
> >
> > What is the inter dependence of the children and delegate?
> > Is it valid to have a delegate property without children?
> > Can the firmware delegate interrupts without the delegation list, based
> > on the children property alone? Or is it effectively useless without a
> > children property?
> 
> Both properties convey different information. The "riscv,childen" describes
> the association of child indexes with child APLIC domains whereas the
> "riscv,delegate" describes the interrupt delegation to few of the child
> APLIC domains.
> 
> 
> >
> > In your examples, the second has msi-parent but neither of these custom
> > properties. Do the children/delegate properties have a meaning in the
> > msi-parent case?
> 
> The "riscv,childern" and "riscv,delegate" are only useful when we have
> hierarchy of multiple APLIC domains. The second example only has
> one APLIC domain hence these custom properties are absent.

It'd be great if you could include an example that explains the
difference as, IIRC, both Rob and I both were kinda confused as to how
the properties differ.

Thanks,
Conor.


[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

[-- Attachment #2: Type: text/plain, Size: 161 bytes --]

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 6/9] dt-bindings: Add RISC-V advanced PLIC bindings
  2023-01-02 18:17         ` Conor Dooley
@ 2023-01-03  5:10           ` Anup Patel
  -1 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2023-01-03  5:10 UTC (permalink / raw)
  To: Conor Dooley
  Cc: Anup Patel, Palmer Dabbelt, Paul Walmsley, Thomas Gleixner,
	Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Atish Patra,
	Alistair Francis, linux-riscv, linux-kernel, devicetree

On Mon, Jan 2, 2023 at 11:48 PM Conor Dooley <conor@kernel.org> wrote:
>
> On Mon, Jan 02, 2023 at 10:20:48PM +0530, Anup Patel wrote:
> > On Sun, Nov 13, 2022 at 9:14 PM Conor Dooley <conor@kernel.org> wrote:
>
> > > > +  domain.
> > > > +
> > > > +allOf:
> > > > +  - $ref: /schemas/interrupt-controller.yaml#
> > > > +
> > > > +properties:
> > > > +  compatible:
> > > > +    items:
> > > > +      - enum:
> > > > +          - vendor,chip-aplic
> > >
> > > Same comment here about the validity of this placeholder.
> >
> > Okay, I will add "riscv,qemu-aplic" as QEMU specific compatible string.
>
> Ah neat. I think that's a fair compromise.
>
> > > > +      - const: riscv,aplic
>
> > > > +  msi-parent:
> > > > +    description:
> > > > +      The presence of this property implies that given APLIC domain forwards
> > > > +      wired interrupts as MSIs to a AIA incoming message signaled interrupt
> > > > +      controller (IMSIC). This property should be considered only when the
> > > > +      interrupts-extended property is absent.
> > >
> > > This mutual exclusion can be represented, can't it?
> > > IIRC it is some sort of oneOf thing, somewhat like below:
> > > oneOf:
> > >   - required:
> > >       - msi-parent
> > >   - required:
> > >       - interrupts-extended
> > >
> > > AFAIR from doing the i2c ocores binding, this will force the addition of
> > > one, but not both, to a node.
> > >
> > > Or is this not actually mutually exclusive & the msi-parent property is
> > > permitted but just left unused if interrupts-extended is present?
> >
> > If both are present then interrupts-extended is preferred.
>
> Perhaps I am making a fool of myself here, but why would someone include
> both of them at once, if only one is going to be used?
> It would appear that making them explicitly mutually exclusive would
> make the binding easier to understand.
> What am I missing?

If both "interrupts-extended" and "msi-parent" are present then it means
the APLIC domain supports both MSI mode and Direct mode in HW. In this
case, the APLIC driver has to choose between MSI mode or Direct mode.

>
> > > > +  riscv,children:
> > > > +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> > > > +    minItems: 1
> > > > +    maxItems: 1024
> > > > +    description:
> > > > +      This property represents a list of child APLIC domains for the given
> > > > +      APLIC domain. Each child APLIC domain is assigned child index in
> > > > +      increasing order with the first child APLIC domain assigned child
> > > > +      index 0. The APLIC domain child index is used by firmware to delegate
> > > > +      interrupts from the given APLIC domain to a particular child APLIC
> > > > +      domain.
> > > > +
> > > > +  riscv,delegate:
> > > > +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> > > > +    minItems: 1
> > > > +    maxItems: 1024
> > > > +    description:
> > > > +      This property represents a interrupt delegation list where each entry
> > > > +      is a triple consisting of child APLIC domain phandle, first interrupt
> > > > +      number, and last interrupt number. The firmware will configure interrupt
> > > > +      delegation registers based on interrupt delegation list.
> > >
> > > What is the inter dependence of the children and delegate?
> > > Is it valid to have a delegate property without children?
> > > Can the firmware delegate interrupts without the delegation list, based
> > > on the children property alone? Or is it effectively useless without a
> > > children property?
> >
> > Both properties convey different information. The "riscv,childen" describes
> > the association of child indexes with child APLIC domains whereas the
> > "riscv,delegate" describes the interrupt delegation to few of the child
> > APLIC domains.
> >
> >
> > >
> > > In your examples, the second has msi-parent but neither of these custom
> > > properties. Do the children/delegate properties have a meaning in the
> > > msi-parent case?
> >
> > The "riscv,childern" and "riscv,delegate" are only useful when we have
> > hierarchy of multiple APLIC domains. The second example only has
> > one APLIC domain hence these custom properties are absent.
>
> It'd be great if you could include an example that explains the
> difference as, IIRC, both Rob and I both were kinda confused as to how
> the properties differ.

Okay, I will try to improve the examples.

Regards,
Anup

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

* Re: [PATCH 6/9] dt-bindings: Add RISC-V advanced PLIC bindings
@ 2023-01-03  5:10           ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2023-01-03  5:10 UTC (permalink / raw)
  To: Conor Dooley
  Cc: Anup Patel, Palmer Dabbelt, Paul Walmsley, Thomas Gleixner,
	Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Atish Patra,
	Alistair Francis, linux-riscv, linux-kernel, devicetree

On Mon, Jan 2, 2023 at 11:48 PM Conor Dooley <conor@kernel.org> wrote:
>
> On Mon, Jan 02, 2023 at 10:20:48PM +0530, Anup Patel wrote:
> > On Sun, Nov 13, 2022 at 9:14 PM Conor Dooley <conor@kernel.org> wrote:
>
> > > > +  domain.
> > > > +
> > > > +allOf:
> > > > +  - $ref: /schemas/interrupt-controller.yaml#
> > > > +
> > > > +properties:
> > > > +  compatible:
> > > > +    items:
> > > > +      - enum:
> > > > +          - vendor,chip-aplic
> > >
> > > Same comment here about the validity of this placeholder.
> >
> > Okay, I will add "riscv,qemu-aplic" as QEMU specific compatible string.
>
> Ah neat. I think that's a fair compromise.
>
> > > > +      - const: riscv,aplic
>
> > > > +  msi-parent:
> > > > +    description:
> > > > +      The presence of this property implies that given APLIC domain forwards
> > > > +      wired interrupts as MSIs to a AIA incoming message signaled interrupt
> > > > +      controller (IMSIC). This property should be considered only when the
> > > > +      interrupts-extended property is absent.
> > >
> > > This mutual exclusion can be represented, can't it?
> > > IIRC it is some sort of oneOf thing, somewhat like below:
> > > oneOf:
> > >   - required:
> > >       - msi-parent
> > >   - required:
> > >       - interrupts-extended
> > >
> > > AFAIR from doing the i2c ocores binding, this will force the addition of
> > > one, but not both, to a node.
> > >
> > > Or is this not actually mutually exclusive & the msi-parent property is
> > > permitted but just left unused if interrupts-extended is present?
> >
> > If both are present then interrupts-extended is preferred.
>
> Perhaps I am making a fool of myself here, but why would someone include
> both of them at once, if only one is going to be used?
> It would appear that making them explicitly mutually exclusive would
> make the binding easier to understand.
> What am I missing?

If both "interrupts-extended" and "msi-parent" are present then it means
the APLIC domain supports both MSI mode and Direct mode in HW. In this
case, the APLIC driver has to choose between MSI mode or Direct mode.

>
> > > > +  riscv,children:
> > > > +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> > > > +    minItems: 1
> > > > +    maxItems: 1024
> > > > +    description:
> > > > +      This property represents a list of child APLIC domains for the given
> > > > +      APLIC domain. Each child APLIC domain is assigned child index in
> > > > +      increasing order with the first child APLIC domain assigned child
> > > > +      index 0. The APLIC domain child index is used by firmware to delegate
> > > > +      interrupts from the given APLIC domain to a particular child APLIC
> > > > +      domain.
> > > > +
> > > > +  riscv,delegate:
> > > > +    $ref: '/schemas/types.yaml#/definitions/phandle-array'
> > > > +    minItems: 1
> > > > +    maxItems: 1024
> > > > +    description:
> > > > +      This property represents a interrupt delegation list where each entry
> > > > +      is a triple consisting of child APLIC domain phandle, first interrupt
> > > > +      number, and last interrupt number. The firmware will configure interrupt
> > > > +      delegation registers based on interrupt delegation list.
> > >
> > > What is the inter dependence of the children and delegate?
> > > Is it valid to have a delegate property without children?
> > > Can the firmware delegate interrupts without the delegation list, based
> > > on the children property alone? Or is it effectively useless without a
> > > children property?
> >
> > Both properties convey different information. The "riscv,childen" describes
> > the association of child indexes with child APLIC domains whereas the
> > "riscv,delegate" describes the interrupt delegation to few of the child
> > APLIC domains.
> >
> >
> > >
> > > In your examples, the second has msi-parent but neither of these custom
> > > properties. Do the children/delegate properties have a meaning in the
> > > msi-parent case?
> >
> > The "riscv,childern" and "riscv,delegate" are only useful when we have
> > hierarchy of multiple APLIC domains. The second example only has
> > one APLIC domain hence these custom properties are absent.
>
> It'd be great if you could include an example that explains the
> difference as, IIRC, both Rob and I both were kinda confused as to how
> the properties differ.

Okay, I will try to improve the examples.

Regards,
Anup

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 6/9] dt-bindings: Add RISC-V advanced PLIC bindings
  2023-01-02 16:50       ` Anup Patel
@ 2023-01-03  8:59         ` Krzysztof Kozlowski
  -1 siblings, 0 replies; 96+ messages in thread
From: Krzysztof Kozlowski @ 2023-01-03  8:59 UTC (permalink / raw)
  To: Anup Patel, Conor Dooley
  Cc: Anup Patel, Palmer Dabbelt, Paul Walmsley, Thomas Gleixner,
	Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Atish Patra,
	Alistair Francis, linux-riscv, linux-kernel, devicetree

On 02/01/2023 17:50, Anup Patel wrote:
> On Sun, Nov 13, 2022 at 9:14 PM Conor Dooley <conor@kernel.org> wrote:
>>
>> Hey Anup,
>>
>> Ditto the $subject nit here.
> 
> Adding "interrupt-controller:" to subject makes it longer than 80 characters.

Because you added redundant double "bindings". Subject line is precious,
so do not add useless words.


Best regards,
Krzysztof


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

* Re: [PATCH 6/9] dt-bindings: Add RISC-V advanced PLIC bindings
@ 2023-01-03  8:59         ` Krzysztof Kozlowski
  0 siblings, 0 replies; 96+ messages in thread
From: Krzysztof Kozlowski @ 2023-01-03  8:59 UTC (permalink / raw)
  To: Anup Patel, Conor Dooley
  Cc: Anup Patel, Palmer Dabbelt, Paul Walmsley, Thomas Gleixner,
	Marc Zyngier, Rob Herring, Krzysztof Kozlowski, Atish Patra,
	Alistair Francis, linux-riscv, linux-kernel, devicetree

On 02/01/2023 17:50, Anup Patel wrote:
> On Sun, Nov 13, 2022 at 9:14 PM Conor Dooley <conor@kernel.org> wrote:
>>
>> Hey Anup,
>>
>> Ditto the $subject nit here.
> 
> Adding "interrupt-controller:" to subject makes it longer than 80 characters.

Because you added redundant double "bindings". Subject line is precious,
so do not add useless words.


Best regards,
Krzysztof


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 6/9] dt-bindings: Add RISC-V advanced PLIC bindings
  2023-01-03  8:59         ` Krzysztof Kozlowski
@ 2023-01-03 13:05           ` Anup Patel
  -1 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2023-01-03 13:05 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Anup Patel, Conor Dooley, Palmer Dabbelt, Paul Walmsley,
	Thomas Gleixner, Marc Zyngier, Rob Herring, Krzysztof Kozlowski,
	Atish Patra, Alistair Francis, linux-riscv, linux-kernel,
	devicetree

On Tue, Jan 3, 2023 at 2:29 PM Krzysztof Kozlowski
<krzysztof.kozlowski@linaro.org> wrote:
>
> On 02/01/2023 17:50, Anup Patel wrote:
> > On Sun, Nov 13, 2022 at 9:14 PM Conor Dooley <conor@kernel.org> wrote:
> >>
> >> Hey Anup,
> >>
> >> Ditto the $subject nit here.
> >
> > Adding "interrupt-controller:" to subject makes it longer than 80 characters.
>
> Because you added redundant double "bindings". Subject line is precious,
> so do not add useless words.

Okay, I will update the patch subject based on your suggestion.

Regards,
Anup

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

* Re: [PATCH 6/9] dt-bindings: Add RISC-V advanced PLIC bindings
@ 2023-01-03 13:05           ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2023-01-03 13:05 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Anup Patel, Conor Dooley, Palmer Dabbelt, Paul Walmsley,
	Thomas Gleixner, Marc Zyngier, Rob Herring, Krzysztof Kozlowski,
	Atish Patra, Alistair Francis, linux-riscv, linux-kernel,
	devicetree

On Tue, Jan 3, 2023 at 2:29 PM Krzysztof Kozlowski
<krzysztof.kozlowski@linaro.org> wrote:
>
> On 02/01/2023 17:50, Anup Patel wrote:
> > On Sun, Nov 13, 2022 at 9:14 PM Conor Dooley <conor@kernel.org> wrote:
> >>
> >> Hey Anup,
> >>
> >> Ditto the $subject nit here.
> >
> > Adding "interrupt-controller:" to subject makes it longer than 80 characters.
>
> Because you added redundant double "bindings". Subject line is precious,
> so do not add useless words.

Okay, I will update the patch subject based on your suggestion.

Regards,
Anup

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

* Re: [PATCH 5/9] irqchip: Add RISC-V incoming MSI controller driver
  2022-11-23  7:28   ` Ruan Jinjie
@ 2023-01-03 13:42     ` Anup Patel
  0 siblings, 0 replies; 96+ messages in thread
From: Anup Patel @ 2023-01-03 13:42 UTC (permalink / raw)
  To: Ruan Jinjie; +Cc: linux-riscv

On Wed, Nov 23, 2022 at 12:59 PM Ruan Jinjie <ruanjinjie@huawei.com> wrote:
>
>
>
> On 2022/11/11 12:42, Anup Patel wrote:
> > The RISC-V advanced interrupt architecture (AIA) specification defines
> > a new MSI controller for managing MSIs on a RISC-V platform. This new
> > MSI controller is referred to as incoming message signaled interrupt
> > controller (IMSIC) which manages MSI on per-HART (or per-CPU) basis.
> > (For more details refer https://github.com/riscv/riscv-aia)
> >
> > This patch adds an irqchip driver for RISC-V IMSIC found on RISC-V
> > platforms.
> >
> > Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> > ---
> >  drivers/irqchip/Kconfig             |   20 +-
> >  drivers/irqchip/Makefile            |    1 +
> >  drivers/irqchip/irq-riscv-imsic.c   | 1207 +++++++++++++++++++++++++++
> >  include/linux/irqchip/riscv-imsic.h |   92 ++
> >  4 files changed, 1319 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/irqchip/irq-riscv-imsic.c
> >  create mode 100644 include/linux/irqchip/riscv-imsic.h
> >
> > diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> > index 7ef9f5e696d3..8246c08f0fd3 100644
> > --- a/drivers/irqchip/Kconfig
> > +++ b/drivers/irqchip/Kconfig
> > @@ -29,7 +29,6 @@ config ARM_GIC_V2M
> >
> >  config GIC_NON_BANKED
> >       bool
> > -
> >  config ARM_GIC_V3
> >       bool
> >       select IRQ_DOMAIN_HIERARCHY
> > @@ -564,6 +563,25 @@ config SIFIVE_PLIC
> >
> >          If you don't know what to do here, say Y.
> >
> > +config RISCV_IMSIC
> > +     bool "RISC-V Incoming MSI Controller"
> > +     depends on RISCV
> > +     select IRQ_DOMAIN_HIERARCHY
> > +     select GENERIC_MSI_IRQ_DOMAIN
> > +     help
> > +        This enables support for the IMSIC chip found in RISC-V systems.
> > +        The IMSIC controls message signaled interrupts and forwards them
> > +        to each core as wired local interrupt.
> > +
> > +        If you don't know what to do here, say Y.
> > +
> > +config RISCV_IMSIC_PCI
> > +     bool
> > +     depends on RISCV_IMSIC
> > +     depends on PCI
> > +     depends on PCI_MSI
> > +     default RISCV_IMSIC
> > +
> >  config EXYNOS_IRQ_COMBINER
> >       bool "Samsung Exynos IRQ combiner support" if COMPILE_TEST
> >       depends on (ARCH_EXYNOS && ARM) || COMPILE_TEST
> > diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> > index 87b49a10962c..22c723cc6ec8 100644
> > --- a/drivers/irqchip/Makefile
> > +++ b/drivers/irqchip/Makefile
> > @@ -96,6 +96,7 @@ obj-$(CONFIG_QCOM_MPM)                      += irq-qcom-mpm.o
> >  obj-$(CONFIG_CSKY_MPINTC)            += irq-csky-mpintc.o
> >  obj-$(CONFIG_CSKY_APB_INTC)          += irq-csky-apb-intc.o
> >  obj-$(CONFIG_RISCV_INTC)             += irq-riscv-intc.o
> > +obj-$(CONFIG_RISCV_IMSIC)            += irq-riscv-imsic.o
> >  obj-$(CONFIG_SIFIVE_PLIC)            += irq-sifive-plic.o
> >  obj-$(CONFIG_IMX_IRQSTEER)           += irq-imx-irqsteer.o
> >  obj-$(CONFIG_IMX_INTMUX)             += irq-imx-intmux.o
> > diff --git a/drivers/irqchip/irq-riscv-imsic.c b/drivers/irqchip/irq-riscv-imsic.c
> > new file mode 100644
> > index 000000000000..95324fb4f5ed
> > --- /dev/null
> > +++ b/drivers/irqchip/irq-riscv-imsic.c
> > @@ -0,0 +1,1207 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2021 Western Digital Corporation or its affiliates.
> > + * Copyright (C) 2022 Ventana Micro Systems Inc.
> > + */
> > +
> > +#define pr_fmt(fmt) "riscv-imsic: " fmt
> > +#include <linux/bitmap.h>
> > +#include <linux/cpu.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/iommu.h>
> > +#include <linux/irq.h>
> > +#include <linux/irqchip.h>
> > +#include <linux/irqchip/chained_irq.h>
> > +#include <linux/irqchip/riscv-imsic.h>
> > +#include <linux/irqdomain.h>
> > +#include <linux/module.h>
> > +#include <linux/msi.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/pci.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/smp.h>
> > +#include <asm/hwcap.h>
> > +
> > +#define IMSIC_DISABLE_EIDELIVERY     0
> > +#define IMSIC_ENABLE_EIDELIVERY              1
> > +#define IMSIC_DISABLE_EITHRESHOLD    1
> > +#define IMSIC_ENABLE_EITHRESHOLD     0
> > +
> > +#define imsic_csr_write(__c, __v)    \
> > +do {                                 \
> > +     csr_write(CSR_ISELECT, __c);    \
> > +     csr_write(CSR_IREG, __v);       \
> > +} while (0)
> > +
> > +#define imsic_csr_read(__c)          \
> > +({                                   \
> > +     unsigned long __v;              \
> > +     csr_write(CSR_ISELECT, __c);    \
> > +     __v = csr_read(CSR_IREG);       \
> > +     __v;                            \
> > +})
> > +
> > +#define imsic_csr_set(__c, __v)              \
> > +do {                                 \
> > +     csr_write(CSR_ISELECT, __c);    \
> > +     csr_set(CSR_IREG, __v);         \
> > +} while (0)
> > +
> > +#define imsic_csr_clear(__c, __v)    \
> > +do {                                 \
> > +     csr_write(CSR_ISELECT, __c);    \
> > +     csr_clear(CSR_IREG, __v);       \
> > +} while (0)
> > +
> > +struct imsic_mmio {
> > +     phys_addr_t pa;
> > +     void __iomem *va;
> > +     unsigned long size;
> > +};
> > +
> > +struct imsic_priv {
> > +     /* Global configuration common for all HARTs */
> > +     struct imsic_global_config global;
> > +
> > +     /* MMIO regions */
> > +     u32 num_mmios;
> > +     struct imsic_mmio *mmios;
> > +
> > +     /* Global state of interrupt identities */
> > +     raw_spinlock_t ids_lock;
> > +     unsigned long *ids_used_bimap;
> > +     unsigned long *ids_enabled_bimap;
> > +     unsigned int *ids_target_cpu;
> > +
> > +     /* Mask for connected CPUs */
> > +     struct cpumask lmask;
> > +
> > +     /* IPI interrupt identity */
> > +     bool slow_ipi;
> > +     u32 ipi_id;
> > +     u32 ipi_lsync_id;
> > +
> > +     /* IRQ domains */
> > +     struct irq_domain *base_domain;
> > +     struct irq_domain *pci_domain;
> > +     struct irq_domain *plat_domain;
> > +};
> > +
> > +struct imsic_handler {
> > +     /* Local configuration for given HART */
> > +     struct imsic_local_config local;
> > +
> > +     /* Pointer to private context */
> > +     struct imsic_priv *priv;
> > +};
> > +
> > +static bool imsic_init_done;
> > +
> > +static int imsic_parent_irq;
> > +static DEFINE_PER_CPU(struct imsic_handler, imsic_handlers);
> > +
> > +const struct imsic_global_config *imsic_get_global_config(void)
> > +{
> > +     struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
> > +
> > +     if (!handler || !handler->priv)
> > +             return NULL;
> > +
> > +     return &handler->priv->global;
> > +}
> > +EXPORT_SYMBOL_GPL(imsic_get_global_config);
> > +
> > +const struct imsic_local_config *imsic_get_local_config(unsigned int cpu)
> > +{
> > +     struct imsic_handler *handler = per_cpu_ptr(&imsic_handlers, cpu);
> > +
> > +     if (!handler || !handler->priv)
> > +             return NULL;
> > +
> > +     return &handler->local;
> > +}
> > +EXPORT_SYMBOL_GPL(imsic_get_local_config);
> > +
> > +static int imsic_cpu_page_phys(unsigned int cpu,
> > +                            unsigned int guest_index,
> > +                            phys_addr_t *out_msi_pa)
> > +{
> > +     struct imsic_handler *handler = per_cpu_ptr(&imsic_handlers, cpu);
> > +     struct imsic_global_config *global;
> > +     struct imsic_local_config *local;
> > +
> > +     if (!handler || !handler->priv)
> > +             return -ENODEV;
> > +     local = &handler->local;
> > +     global = &handler->priv->global;
> > +
> > +     if (BIT(global->guest_index_bits) <= guest_index)
> > +             return -EINVAL;
> > +
> > +     if (out_msi_pa)
> > +             *out_msi_pa = local->msi_pa +
> > +                           (guest_index * IMSIC_MMIO_PAGE_SZ);
> > +
> > +     return 0;
> > +}
> > +
> > +static int imsic_get_cpu(struct imsic_priv *priv,
> > +                      const struct cpumask *mask_val, bool force,
> > +                      unsigned int *out_target_cpu)
> > +{
> > +     struct cpumask amask;
> > +     unsigned int cpu;
> > +
> > +     cpumask_and(&amask, &priv->lmask, mask_val);
> > +
> > +     if (force)
> > +             cpu = cpumask_first(&amask);
> > +     else
> > +             cpu = cpumask_any_and(&amask, cpu_online_mask);
> > +
> > +     if (cpu >= nr_cpu_ids)
> > +             return -EINVAL;
> > +
> > +     if (out_target_cpu)
> > +             *out_target_cpu = cpu;
> > +
> > +     return 0;
> > +}
> > +
> > +static int imsic_get_cpu_msi_msg(unsigned int cpu, unsigned int id,
> > +                              struct msi_msg *msg)
> > +{
> > +     phys_addr_t msi_addr;
> > +     int err;
> > +
> > +     err = imsic_cpu_page_phys(cpu, 0, &msi_addr);
> > +     if (err)
> > +             return err;
> > +
> > +     msg->address_hi = upper_32_bits(msi_addr);
> > +     msg->address_lo = lower_32_bits(msi_addr);
> > +     msg->data = id;
> > +
> > +     return err;
> > +}
> > +
> > +static void imsic_id_set_target(struct imsic_priv *priv,
> > +                              unsigned int id, unsigned int target_cpu)
> > +{
> > +     raw_spin_lock(&priv->ids_lock);
> > +     priv->ids_target_cpu[id] = target_cpu;
> > +     raw_spin_unlock(&priv->ids_lock);
> > +}
> > +
> > +static unsigned int imsic_id_get_target(struct imsic_priv *priv,
> > +                                     unsigned int id)
> > +{
> > +     unsigned int ret;
> > +
> > +     raw_spin_lock(&priv->ids_lock);
> > +     ret = priv->ids_target_cpu[id];
> > +     raw_spin_unlock(&priv->ids_lock);
> > +
> > +     return ret;
> > +}
> > +
> > +static void __imsic_eix_update(unsigned long base_id,
> > +                            unsigned long num_id, bool pend, bool val)
> > +{
> > +     unsigned long i, isel, ireg, flags;
> > +     unsigned long id = base_id, last_id = base_id + num_id;
> > +
> > +     while (id < last_id) {
> > +             isel = id / BITS_PER_LONG;
> > +             isel *= BITS_PER_LONG / IMSIC_EIPx_BITS;
> > +             isel += (pend) ? IMSIC_EIP0 : IMSIC_EIE0;
> > +
> > +             ireg = 0;
> > +             for (i = id & (__riscv_xlen - 1);
> > +                  (id < last_id) && (i < __riscv_xlen); i++) {
> > +                     ireg |= BIT(i);
> > +                     id++;
> > +             }
> > +
> > +             /*
> > +              * The IMSIC EIEx and EIPx registers are indirectly
> > +              * accessed via using ISELECT and IREG CSRs so we
> > +              * save/restore local IRQ to ensure that we don't
> > +              * get preempted while accessing IMSIC registers.
> > +              */
> > +             local_irq_save(flags);
> > +             if (val)
> > +                     imsic_csr_set(isel, ireg);
> > +             else
> > +                     imsic_csr_clear(isel, ireg);
> > +             local_irq_restore(flags);
> > +     }
> > +}
> > +
> > +#define __imsic_id_enable(__id)              \
> > +     __imsic_eix_update((__id), 1, false, true)
> > +#define __imsic_id_disable(__id)     \
> > +     __imsic_eix_update((__id), 1, false, false)
> > +
> > +#ifdef CONFIG_SMP
> > +static void __imsic_id_smp_sync(struct imsic_priv *priv)
> > +{
> > +     struct imsic_handler *handler;
> > +     struct cpumask amask;
> > +     int cpu;
> > +
> > +     cpumask_and(&amask, &priv->lmask, cpu_online_mask);
> > +     for_each_cpu(cpu, &amask) {
> > +             if (cpu == smp_processor_id())
> > +                     continue;
> > +
> > +             handler = per_cpu_ptr(&imsic_handlers, cpu);
> > +             if (!handler || !handler->priv || !handler->local.msi_va) {
> > +                     pr_warn("CPU%d: handler not initialized\n", cpu);
> > +                     continue;
> > +             }
> > +
> > +             writel(handler->priv->ipi_lsync_id, handler->local.msi_va);
> > +     }
> > +}
> > +#else
> > +#define __imsic_id_smp_sync(__priv)
> > +#endif
> > +
> > +static void imsic_id_enable(struct imsic_priv *priv, unsigned int id)
> > +{
> > +     raw_spin_lock(&priv->ids_lock);
> > +     bitmap_set(priv->ids_enabled_bimap, id, 1);
> > +     __imsic_id_enable(id);
> > +     raw_spin_unlock(&priv->ids_lock);
> > +
> > +     __imsic_id_smp_sync(priv);
> > +}
> > +
> > +static void imsic_id_disable(struct imsic_priv *priv, unsigned int id)
> > +{
> > +     raw_spin_lock(&priv->ids_lock);
> > +     bitmap_clear(priv->ids_enabled_bimap, id, 1);
> > +     __imsic_id_disable(id);
> > +     raw_spin_unlock(&priv->ids_lock);
> > +
> > +     __imsic_id_smp_sync(priv);
> > +}
> > +
> > +static void imsic_ids_local_sync(struct imsic_priv *priv)
> > +{
> > +     int i;
> > +
> > +     raw_spin_lock(&priv->ids_lock);
> > +     for (i = 1; i <= priv->global.nr_ids; i++) {
> > +             if (priv->ipi_id == i || priv->ipi_lsync_id == i)
> > +                     continue;
> > +
> > +             if (test_bit(i, priv->ids_enabled_bimap))
> > +                     __imsic_id_enable(i);
> > +             else
> > +                     __imsic_id_disable(i);
> > +     }
> > +     raw_spin_unlock(&priv->ids_lock);
> > +}
> > +
> > +static void imsic_ids_local_delivery(struct imsic_priv *priv, bool enable)
> > +{
> > +     if (enable) {
> > +             imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_ENABLE_EITHRESHOLD);
> > +             imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_ENABLE_EIDELIVERY);
> > +     } else {
> > +             imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_DISABLE_EIDELIVERY);
> > +             imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_DISABLE_EITHRESHOLD);
> > +     }
> > +}
> > +
> > +static int imsic_ids_alloc(struct imsic_priv *priv,
> > +                        unsigned int max_id, unsigned int order)
> > +{
> > +     int ret;
> > +
> > +     if ((priv->global.nr_ids < max_id) ||
> > +         (max_id < BIT(order)))
> > +             return -EINVAL;
> > +
> > +     raw_spin_lock(&priv->ids_lock);
> > +     ret = bitmap_find_free_region(priv->ids_used_bimap,
> > +                                   max_id + 1, order);
> > +     raw_spin_unlock(&priv->ids_lock);
> > +
> > +     return ret;
> > +}
> > +
> > +static void imsic_ids_free(struct imsic_priv *priv, unsigned int base_id,
> > +                        unsigned int order)
> > +{
> > +     raw_spin_lock(&priv->ids_lock);
> > +     bitmap_release_region(priv->ids_used_bimap, base_id, order);
> > +     raw_spin_unlock(&priv->ids_lock);
> > +}
> > +
> > +static int __init imsic_ids_init(struct imsic_priv *priv)
> > +{
> > +     int i;
> > +     struct imsic_global_config *global = &priv->global;
> > +
> > +     raw_spin_lock_init(&priv->ids_lock);
> > +
> > +     /* Allocate used bitmap */
> > +     priv->ids_used_bimap = kcalloc(BITS_TO_LONGS(global->nr_ids + 1),
> > +                                     sizeof(unsigned long), GFP_KERNEL);
> > +     if (!priv->ids_used_bimap)
> > +             return -ENOMEM;
> > +
> > +     /* Allocate enabled bitmap */
> > +     priv->ids_enabled_bimap = kcalloc(BITS_TO_LONGS(global->nr_ids + 1),
> > +                                        sizeof(unsigned long), GFP_KERNEL);
> > +     if (!priv->ids_enabled_bimap) {
> > +             kfree(priv->ids_used_bimap);
> > +             return -ENOMEM;
> > +     }
> > +
> > +     /* Allocate target CPU array */
> > +     priv->ids_target_cpu = kcalloc(global->nr_ids + 1,
> > +                                    sizeof(unsigned int), GFP_KERNEL);
> > +     if (!priv->ids_target_cpu) {
> > +             kfree(priv->ids_enabled_bimap);
> > +             kfree(priv->ids_used_bimap);
> > +             return -ENOMEM;
> > +     }
> > +     for (i = 0; i <= global->nr_ids; i++)
> > +             priv->ids_target_cpu[i] = UINT_MAX;
> > +
> > +     /* Reserve ID#0 because it is special and never implemented */
> > +     bitmap_set(priv->ids_used_bimap, 0, 1);
> > +
> > +     return 0;
> > +}
> > +
> > +static void __init imsic_ids_cleanup(struct imsic_priv *priv)
> > +{
> > +     kfree(priv->ids_target_cpu);
> > +     kfree(priv->ids_enabled_bimap);
> > +     kfree(priv->ids_used_bimap);
> > +}
> > +
> > +#ifdef CONFIG_SMP
> > +static void imsic_ipi_send_mask(unsigned int parent_virq, void *data,
> > +                             const struct cpumask *mask)
> > +{
> > +     int cpu;
> > +     struct imsic_handler *handler;
> > +
> > +     for_each_cpu(cpu, mask) {
> > +             handler = per_cpu_ptr(&imsic_handlers, cpu);
> > +             if (!handler || !handler->priv || !handler->local.msi_va) {
> > +                     pr_warn("CPU%d: handler not initialized\n", cpu);
> > +                     continue;
> > +             }
> > +
> > +             writel(handler->priv->ipi_id, handler->local.msi_va);
> > +     }
> > +}
> > +
> > +static struct ipi_mux_ops imsic_ipi_ops = {
> > +     .ipi_mux_send = imsic_ipi_send_mask,
> > +};
> > +
> > +static void imsic_ipi_enable(struct imsic_priv *priv)
> > +{
> > +     __imsic_id_enable(priv->ipi_id);
> > +     __imsic_id_enable(priv->ipi_lsync_id);
> > +}
> > +
> > +static void imsic_ipi_disable(struct imsic_priv *priv)
> > +{
> > +     __imsic_id_disable(priv->ipi_lsync_id);
> > +     __imsic_id_disable(priv->ipi_id);
> > +}
> > +
> > +static int __init imsic_ipi_domain_init(struct imsic_priv *priv)
> > +{
> > +     int virq;
> > +
> > +     /* Skip IPI setup if IPIs are slow */
> > +     if (priv->slow_ipi)
> > +             goto skip_ipi;
> > +
> > +     /* Allocate interrupt identity for IPIs */
> > +     virq = imsic_ids_alloc(priv, priv->global.nr_ids, get_count_order(1));
> > +     if (virq < 0)
> > +             return virq;
> > +     priv->ipi_id = virq;
> > +
> > +     /* Create IMSIC IPI multiplexing */
> > +     virq = ipi_mux_create(0, BITS_PER_BYTE, &imsic_ipi_ops, NULL);
> > +     if (virq <= 0) {
> > +             imsic_ids_free(priv, priv->ipi_id, get_count_order(1));
> > +             return (virq < 0) ? virq : -ENOMEM;
> > +     }
> > +
> > +     /* Set vIRQ range */
> > +     riscv_ipi_set_virq_range(virq, BITS_PER_BYTE, true);
> > +
> > +skip_ipi:
> > +     /* Allocate interrupt identity for local enable/disable sync */
> > +     virq = imsic_ids_alloc(priv, priv->global.nr_ids, get_count_order(1));
> > +     if (virq < 0) {
> > +             imsic_ids_free(priv, priv->ipi_id, get_count_order(1));
> > +             return virq;
> > +     }
> > +     priv->ipi_lsync_id = virq;
> > +
> > +     return 0;
> > +}
> > +
> > +static void __init imsic_ipi_domain_cleanup(struct imsic_priv *priv)
> > +{
> > +     imsic_ids_free(priv, priv->ipi_lsync_id, get_count_order(1));
> > +     if (priv->ipi_id)
> > +             imsic_ids_free(priv, priv->ipi_id, get_count_order(1));
> > +}
> > +#else
> > +static void imsic_ipi_enable(struct imsic_priv *priv)
> > +{
> > +}
> > +
> > +static void imsic_ipi_disable(struct imsic_priv *priv)
> > +{
> > +}
> > +
> > +static int __init imsic_ipi_domain_init(struct imsic_priv *priv)
> > +{
> > +     /* Clear the IPI ids because we are not using IPIs */
> > +     priv->ipi_id = 0;
> > +     priv->ipi_lsync_id = 0;
> > +     return 0;
> > +}
> > +
> > +static void __init imsic_ipi_domain_cleanup(struct imsic_priv *priv)
> > +{
> > +}
> > +#endif
> > +
> > +static void imsic_irq_mask(struct irq_data *d)
> > +{
> > +     imsic_id_disable(irq_data_get_irq_chip_data(d), d->hwirq);
> > +}
> > +
> > +static void imsic_irq_unmask(struct irq_data *d)
> > +{
> > +     imsic_id_enable(irq_data_get_irq_chip_data(d), d->hwirq);
> > +}
> > +
> > +static void imsic_irq_compose_msi_msg(struct irq_data *d,
> > +                                   struct msi_msg *msg)
> > +{
> > +     struct imsic_priv *priv = irq_data_get_irq_chip_data(d);
> > +     unsigned int cpu;
> > +     int err;
> > +
> > +     cpu = imsic_id_get_target(priv, d->hwirq);
> > +     WARN_ON(cpu == UINT_MAX);
> > +
> > +     err = imsic_get_cpu_msi_msg(cpu, d->hwirq, msg);
> > +     WARN_ON(err);
> > +
> > +     iommu_dma_compose_msi_msg(irq_data_get_msi_desc(d), msg);
> > +}
> > +
> > +#ifdef CONFIG_SMP
> > +static int imsic_irq_set_affinity(struct irq_data *d,
> > +                               const struct cpumask *mask_val,
> > +                               bool force)
> > +{
> > +     struct imsic_priv *priv = irq_data_get_irq_chip_data(d);
> > +     unsigned int target_cpu;
> > +     int rc;
> > +
> > +     rc = imsic_get_cpu(priv, mask_val, force, &target_cpu);
> > +     if (rc)
> > +             return rc;
> > +
> > +     imsic_id_set_target(priv, d->hwirq, target_cpu);
> > +     irq_data_update_effective_affinity(d, cpumask_of(target_cpu));
> > +
> > +     return IRQ_SET_MASK_OK;
> > +}
> > +#endif
> > +
> > +static struct irq_chip imsic_irq_base_chip = {
> > +     .name                   = "RISC-V IMSIC-BASE",
> > +     .irq_mask               = imsic_irq_mask,
> > +     .irq_unmask             = imsic_irq_unmask,
> > +#ifdef CONFIG_SMP
> > +     .irq_set_affinity       = imsic_irq_set_affinity,
> > +#endif
> > +     .irq_compose_msi_msg    = imsic_irq_compose_msi_msg,
> > +     .flags                  = IRQCHIP_SKIP_SET_WAKE |
> > +                               IRQCHIP_MASK_ON_SUSPEND,
> > +};
> > +
> > +static int imsic_irq_domain_alloc(struct irq_domain *domain,
> > +                               unsigned int virq,
> > +                               unsigned int nr_irqs,
> > +                               void *args)
> > +{
> > +     struct imsic_priv *priv = domain->host_data;
> > +     msi_alloc_info_t *info = args;
> > +     phys_addr_t msi_addr;
> > +     int i, hwirq, err = 0;
> > +     unsigned int cpu;
> > +
> > +     err = imsic_get_cpu(priv, &priv->lmask, false, &cpu);
> > +     if (err)
> > +             return err;
> > +
> > +     err = imsic_cpu_page_phys(cpu, 0, &msi_addr);
> > +     if (err)
> > +             return err;
> > +
> > +     hwirq = imsic_ids_alloc(priv, priv->global.nr_ids,
> > +                             get_count_order(nr_irqs));
> > +     if (hwirq < 0)
> > +             return hwirq;
> > +
> > +     err = iommu_dma_prepare_msi(info->desc, msi_addr);
> > +     if (err)
> > +             goto fail;
> > +
> > +     for (i = 0; i < nr_irqs; i++) {
> > +             imsic_id_set_target(priv, hwirq + i, cpu);
> > +             irq_domain_set_info(domain, virq + i, hwirq + i,
> > +                                 &imsic_irq_base_chip, priv,
> > +                                 handle_simple_irq, NULL, NULL);
> > +             irq_set_noprobe(virq + i);
> > +             irq_set_affinity(virq + i, &priv->lmask);
> > +     }
> > +
> > +     return 0;
> > +
> > +fail:
> > +     imsic_ids_free(priv, hwirq, get_count_order(nr_irqs));
> > +     return err;
> > +}
> > +
> > +static void imsic_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 imsic_priv *priv = domain->host_data;
> > +
> > +     imsic_ids_free(priv, d->hwirq, get_count_order(nr_irqs));
> > +     irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> > +}
> > +
> > +static const struct irq_domain_ops imsic_base_domain_ops = {
> > +     .alloc          = imsic_irq_domain_alloc,
> > +     .free           = imsic_irq_domain_free,
> > +};
> > +
> > +#ifdef CONFIG_RISCV_IMSIC_PCI
> > +
> > +static void imsic_pci_mask_irq(struct irq_data *d)
> > +{
> > +     pci_msi_mask_irq(d);
> > +     irq_chip_mask_parent(d);
> > +}
> > +
> > +static void imsic_pci_unmask_irq(struct irq_data *d)
> > +{
> > +     pci_msi_unmask_irq(d);
> > +     irq_chip_unmask_parent(d);
> > +}
> > +
> > +static struct irq_chip imsic_pci_irq_chip = {
> > +     .name                   = "RISC-V IMSIC-PCI",
> > +     .irq_mask               = imsic_pci_mask_irq,
> > +     .irq_unmask             = imsic_pci_unmask_irq,
> > +     .irq_eoi                = irq_chip_eoi_parent,
> > +};
> > +
> > +static struct msi_domain_ops imsic_pci_domain_ops = {
> > +};
> > +
> > +static struct msi_domain_info imsic_pci_domain_info = {
> > +     .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> > +                MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI),
> > +     .ops    = &imsic_pci_domain_ops,
> > +     .chip   = &imsic_pci_irq_chip,
> > +};
> > +
> > +#endif
> > +
> > +static struct irq_chip imsic_plat_irq_chip = {
> > +     .name                   = "RISC-V IMSIC-PLAT",
> > +};
> > +
> > +static struct msi_domain_ops imsic_plat_domain_ops = {
> > +};
> > +
> > +static struct msi_domain_info imsic_plat_domain_info = {
> > +     .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
> > +     .ops    = &imsic_plat_domain_ops,
> > +     .chip   = &imsic_plat_irq_chip,
> > +};
> > +
> > +static int __init imsic_irq_domains_init(struct imsic_priv *priv,
> > +                                      struct fwnode_handle *fwnode)
> > +{
> > +     /* Create Base IRQ domain */
> > +     priv->base_domain = irq_domain_create_tree(fwnode,
> > +                                             &imsic_base_domain_ops, priv);
> > +     if (!priv->base_domain) {
> > +             pr_err("Failed to create IMSIC base domain\n");
> > +             return -ENOMEM;
> > +     }
> > +     irq_domain_update_bus_token(priv->base_domain, DOMAIN_BUS_NEXUS);
> > +
> > +#ifdef CONFIG_RISCV_IMSIC_PCI
> > +     /* Create PCI MSI domain */
> > +     priv->pci_domain = pci_msi_create_irq_domain(fwnode,
> > +                                             &imsic_pci_domain_info,
> > +                                             priv->base_domain);
> > +     if (!priv->pci_domain) {
> > +             pr_err("Failed to create IMSIC PCI domain\n");
> > +             irq_domain_remove(priv->base_domain);
> > +             return -ENOMEM;
> > +     }
> > +#endif
> > +
> > +     /* Create Platform MSI domain */
> > +     priv->plat_domain = platform_msi_create_irq_domain(fwnode,
> > +                                             &imsic_plat_domain_info,
> > +                                             priv->base_domain);
> > +     if (!priv->plat_domain) {
> > +             pr_err("Failed to create IMSIC platform domain\n");
> > +             if (priv->pci_domain)
> > +                     irq_domain_remove(priv->pci_domain);
> > +             irq_domain_remove(priv->base_domain);
> > +             return -ENOMEM;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +/*
> > + * To handle an interrupt, we read the TOPEI CSR and write zero in one
> > + * instruction. If TOPEI CSR is non-zero then we translate TOPEI.ID to
> > + * Linux interrupt number and let Linux IRQ subsystem handle it.
> > + */
> > +static void imsic_handle_irq(struct irq_desc *desc)
> > +{
> > +     struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
> > +     struct irq_chip *chip = irq_desc_get_chip(desc);
> > +     struct imsic_priv *priv = handler->priv;
> > +     irq_hw_number_t hwirq;
> > +     int err;
> > +
> > +     WARN_ON_ONCE(!handler->priv);
> > +
> > +     chained_irq_enter(chip, desc);
> > +
> > +     while ((hwirq = csr_swap(CSR_TOPEI, 0))) {
> > +             hwirq = hwirq >> TOPEI_ID_SHIFT;
> > +
> > +             if (hwirq == priv->ipi_id) {
> > +#ifdef CONFIG_SMP
> > +                     ipi_mux_process();
> > +#endif
> > +                     continue;
> > +             } else if (hwirq == priv->ipi_lsync_id) {
> > +                     imsic_ids_local_sync(priv);
> > +                     continue;
> > +             }
> > +
> > +             err = generic_handle_domain_irq(priv->base_domain, hwirq);
> > +             if (unlikely(err))
> > +                     pr_warn_ratelimited(
> > +                             "hwirq %lu mapping not found\n", hwirq);
> > +     }
> > +
> > +     chained_irq_exit(chip, desc);
> > +}
> > +
> > +static int imsic_dying_cpu(unsigned int cpu)
> > +{
> > +     struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
> > +     struct imsic_priv *priv = handler->priv;
> > +
> > +     /* Disable per-CPU parent interrupt */
> > +     if (imsic_parent_irq)
> > +             disable_percpu_irq(imsic_parent_irq);
> > +
> > +     /* Locally disable interrupt delivery */
> > +     imsic_ids_local_delivery(priv, false);
> > +
> > +     /* Disable IPIs */
> > +     imsic_ipi_disable(priv);
> > +
> > +     return 0;
> > +}
> > +
> > +static int imsic_starting_cpu(unsigned int cpu)
> > +{
> > +     struct imsic_handler *handler = this_cpu_ptr(&imsic_handlers);
> > +     struct imsic_priv *priv = handler->priv;
> > +
> > +     /* Enable per-CPU parent interrupt */
> > +     if (imsic_parent_irq)
> > +             enable_percpu_irq(imsic_parent_irq,
> > +                               irq_get_trigger_type(imsic_parent_irq));
> > +     else
> > +             pr_warn("cpu%d: parent irq not available\n", cpu);
> > +
> > +     /* Enable IPIs */
> > +     imsic_ipi_enable(priv);
> > +
> > +     /*
> > +      * Interrupts identities might have been enabled/disabled while
> > +      * this CPU was not running so sync-up local enable/disable state.
> > +      */
> > +     imsic_ids_local_sync(priv);
> > +
> > +     /* Locally enable interrupt delivery */
> > +     imsic_ids_local_delivery(priv, true);
> > +
> > +     return 0;
> > +}
> > +
> > +struct imsic_fwnode_ops {
> > +     u32 (*nr_parent_irq)(struct fwnode_handle *fwnode,
> > +                          void *fwopaque);
> > +     int (*parent_hartid)(struct fwnode_handle *fwnode,
> > +                          void *fwopaque, u32 index,
> > +                          unsigned long *out_hartid);
> > +     u32 (*nr_mmio)(struct fwnode_handle *fwnode, void *fwopaque);
> > +     int (*mmio_to_resource)(struct fwnode_handle *fwnode,
> > +                             void *fwopaque, u32 index,
> > +                             struct resource *res);
> > +     void __iomem *(*mmio_map)(struct fwnode_handle *fwnode,
> > +                               void *fwopaque, u32 index);
> > +     int (*read_u32)(struct fwnode_handle *fwnode,
> > +                     void *fwopaque, const char *prop, u32 *out_val);
> > +     bool (*read_bool)(struct fwnode_handle *fwnode,
> > +                       void *fwopaque, const char *prop);
> > +};
> > +
> > +static int __init imsic_init(struct imsic_fwnode_ops *fwops,
> > +                          struct fwnode_handle *fwnode,
> > +                          void *fwopaque)
> > +{
> > +     struct resource res;
> > +     phys_addr_t base_addr;
> > +     int rc, nr_parent_irqs;
> > +     struct imsic_mmio *mmio;
> > +     struct imsic_priv *priv;
> > +     struct irq_domain *domain;
> > +     struct imsic_handler *handler;
> > +     struct imsic_global_config *global;
> > +     u32 i, tmp, nr_handlers = 0;
> > +
> > +     if (imsic_init_done) {
> > +             pr_err("%pfwP: already initialized hence ignoring\n",
> > +                     fwnode);
> > +             return -ENODEV;
> > +     }
> > +
> > +     if (!riscv_isa_extension_available(NULL, SxAIA)) {
>
> why pass NULL to check SxAIA isa extension?I encounter following error
> when launch Linux kernel.
>
> [    0.000000] riscv-imsic: imsics@28000000: AIA support not available
> [    0.000000] OF: of_irq_init: Failed to init /soc/imsics@28000000
> ((____ptrval____)), parent (____ptrval____)
>
> [    0.940357] riscv-aplic d000000.aplic: IMSIC global config not found

This is not related to the NULL parameter. It seems you don't have
Ssaia in the ISA strings of CPU DT nodes.

Regards,
Anup

>
>
> > +             pr_err("%pfwP: AIA support not available\n", fwnode);
> > +             return -ENODEV;
> > +     }
> > +
> > +     priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> > +     if (!priv)
> > +             return -ENOMEM;
> > +     global = &priv->global;
> > +
> > +     /* Find number of parent interrupts */
> > +     nr_parent_irqs = fwops->nr_parent_irq(fwnode, fwopaque);
> > +     if (!nr_parent_irqs) {
> > +             pr_err("%pfwP: no parent irqs available\n", fwnode);
> > +             return -EINVAL;
> > +     }
> > +
> > +     /* Find number of guest index bits in MSI address */
> > +     rc = fwops->read_u32(fwnode, fwopaque, "riscv,guest-index-bits",
> > +                          &global->guest_index_bits);
> > +     if (rc)
> > +             global->guest_index_bits = 0;
> > +     tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT;
> > +     if (tmp < global->guest_index_bits) {
> > +             pr_err("%pfwP: guest index bits too big\n", fwnode);
> > +             return -EINVAL;
> > +     }
> > +
> > +     /* Find number of HART index bits */
> > +     rc = fwops->read_u32(fwnode, fwopaque, "riscv,hart-index-bits",
> > +                          &global->hart_index_bits);
> > +     if (rc) {
> > +             /* Assume default value */
> > +             global->hart_index_bits = __fls(nr_parent_irqs);
> > +             if (BIT(global->hart_index_bits) < nr_parent_irqs)
> > +                     global->hart_index_bits++;
> > +     }
> > +     tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT -
> > +           global->guest_index_bits;
> > +     if (tmp < global->hart_index_bits) {
> > +             pr_err("%pfwP: HART index bits too big\n", fwnode);
> > +             return -EINVAL;
> > +     }
> > +
> > +     /* Find number of group index bits */
> > +     rc = fwops->read_u32(fwnode, fwopaque, "riscv,group-index-bits",
> > +                          &global->group_index_bits);
> > +     if (rc)
> > +             global->group_index_bits = 0;
> > +     tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT -
> > +           global->guest_index_bits - global->hart_index_bits;
> > +     if (tmp < global->group_index_bits) {
> > +             pr_err("%pfwP: group index bits too big\n", fwnode);
> > +             return -EINVAL;
> > +     }
> > +
> > +     /* Find first bit position of group index */
> > +     tmp = IMSIC_MMIO_PAGE_SHIFT * 2;
> > +     rc = fwops->read_u32(fwnode, fwopaque, "riscv,group-index-shift",
> > +                          &global->group_index_shift);
> > +     if (rc)
> > +             global->group_index_shift = tmp;
> > +     if (global->group_index_shift < tmp) {
> > +             pr_err("%pfwP: group index shift too small\n", fwnode);
> > +             return -EINVAL;
> > +     }
> > +     tmp = global->group_index_bits + global->group_index_shift - 1;
> > +     if (tmp >= BITS_PER_LONG) {
> > +             pr_err("%pfwP: group index shift too big\n", fwnode);
> > +             return -EINVAL;
> > +     }
> > +
> > +     /* Find number of interrupt identities */
> > +     rc = fwops->read_u32(fwnode, fwopaque, "riscv,num-ids",
> > +                          &global->nr_ids);
> > +     if (rc) {
> > +             pr_err("%pfwP: number of interrupt identities not found\n",
> > +                     fwnode);
> > +             return rc;
> > +     }
> > +     if ((global->nr_ids < IMSIC_MIN_ID) ||
> > +         (global->nr_ids >= IMSIC_MAX_ID) ||
> > +         ((global->nr_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID)) {
> > +             pr_err("%pfwP: invalid number of interrupt identities\n",
> > +                     fwnode);
> > +             return -EINVAL;
> > +     }
> > +
> > +     /* Find number of guest interrupt identities */
> > +     if (fwops->read_u32(fwnode, fwopaque, "riscv,num-guest-ids",
> > +                         &global->nr_guest_ids))
> > +             global->nr_guest_ids = global->nr_ids;
> > +     if ((global->nr_guest_ids < IMSIC_MIN_ID) ||
> > +         (global->nr_guest_ids >= IMSIC_MAX_ID) ||
> > +         ((global->nr_guest_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID)) {
> > +             pr_err("%pfwP: invalid number of guest interrupt identities\n",
> > +                     fwnode);
> > +             return -EINVAL;
> > +     }
> > +
> > +     /* Check if IPIs are slow */
> > +     priv->slow_ipi = fwops->read_bool(fwnode, fwopaque, "riscv,slow-ipi");
> > +
> > +     /* Compute base address */
> > +     rc = fwops->mmio_to_resource(fwnode, fwopaque, 0, &res);
> > +     if (rc) {
> > +             pr_err("%pfwP: first MMIO resource not found\n", fwnode);
> > +             return -EINVAL;
> > +     }
> > +     global->base_addr = res.start;
> > +     global->base_addr &= ~(BIT(global->guest_index_bits +
> > +                                global->hart_index_bits +
> > +                                IMSIC_MMIO_PAGE_SHIFT) - 1);
> > +     global->base_addr &= ~((BIT(global->group_index_bits) - 1) <<
> > +                            global->group_index_shift);
> > +
> > +     /* Find number of MMIO register sets */
> > +     priv->num_mmios = fwops->nr_mmio(fwnode, fwopaque);
> > +
> > +     /* Allocate MMIO register sets */
> > +     priv->mmios = kcalloc(priv->num_mmios, sizeof(*mmio), GFP_KERNEL);
> > +     if (!priv->mmios) {
> > +             rc = -ENOMEM;
> > +             goto out_free_priv;
> > +     }
> > +
> > +     /* Parse and map MMIO register sets */
> > +     for (i = 0; i < priv->num_mmios; i++) {
> > +             mmio = &priv->mmios[i];
> > +             rc = fwops->mmio_to_resource(fwnode, fwopaque, i, &res);
> > +             if (rc) {
> > +                     pr_err("%pfwP: unable to parse MMIO regset %d\n",
> > +                             fwnode, i);
> > +                     goto out_iounmap;
> > +             }
> > +             mmio->pa = res.start;
> > +             mmio->size = res.end - res.start + 1;
> > +
> > +             base_addr = mmio->pa;
> > +             base_addr &= ~(BIT(global->guest_index_bits +
> > +                                global->hart_index_bits +
> > +                                IMSIC_MMIO_PAGE_SHIFT) - 1);
> > +             base_addr &= ~((BIT(global->group_index_bits) - 1) <<
> > +                            global->group_index_shift);
> > +             if (base_addr != global->base_addr) {
> > +                     rc = -EINVAL;
> > +                     pr_err("%pfwP: address mismatch for regset %d\n",
> > +                             fwnode, i);
> > +                     goto out_iounmap;
> > +             }
> > +
> > +             tmp = BIT(global->guest_index_bits) - 1;
> > +             if ((mmio->size / IMSIC_MMIO_PAGE_SZ) & tmp) {
> > +                     rc = -EINVAL;
> > +                     pr_err("%pfwP: size mismatch for regset %d\n",
> > +                             fwnode, i);
> > +                     goto out_iounmap;
> > +             }
> > +
> > +             mmio->va = fwops->mmio_map(fwnode, fwopaque, i);
> > +             if (!mmio->va) {
> > +                     rc = -EIO;
> > +                     pr_err("%pfwP: unable to map MMIO regset %d\n",
> > +                             fwnode, i);
> > +                     goto out_iounmap;
> > +             }
> > +     }
> > +
> > +     /* Initialize interrupt identity management */
> > +     rc = imsic_ids_init(priv);
> > +     if (rc) {
> > +             pr_err("%pfwP: failed to initialize interrupt management\n",
> > +                    fwnode);
> > +             goto out_iounmap;
> > +     }
> > +
> > +     /* Configure handlers for target CPUs */
> > +     for (i = 0; i < nr_parent_irqs; i++) {
> > +             unsigned long reloff, hartid;
> > +             int j, cpu;
> > +
> > +             rc = fwops->parent_hartid(fwnode, fwopaque, i, &hartid);
> > +             if (rc) {
> > +                     pr_warn("%pfwP: hart ID for parent irq%d not found\n",
> > +                             fwnode, i);
> > +                     continue;
> > +             }
> > +
> > +             cpu = riscv_hartid_to_cpuid(hartid);
> > +             if (cpu < 0) {
> > +                     pr_warn("%pfwP: invalid cpuid for parent irq%d\n",
> > +                             fwnode, i);
> > +                     continue;
> > +             }
> > +
> > +             /* Find MMIO location of MSI page */
> > +             mmio = NULL;
> > +             reloff = i * BIT(global->guest_index_bits) *
> > +                      IMSIC_MMIO_PAGE_SZ;
> > +             for (j = 0; priv->num_mmios; j++) {
> > +                     if (reloff < priv->mmios[j].size) {
> > +                             mmio = &priv->mmios[j];
> > +                             break;
> > +                     }
> > +
> > +                     reloff -= priv->mmios[j].size;
> > +             }
> > +             if (!mmio) {
> > +                     pr_warn("%pfwP: MMIO not found for parent irq%d\n",
> > +                             fwnode, i);
> > +                     continue;
> > +             }
> > +
> > +             handler = per_cpu_ptr(&imsic_handlers, cpu);
> > +             if (handler->priv) {
> > +                     pr_warn("%pfwP: CPU%d handler already configured.\n",
> > +                             fwnode, cpu);
> > +                     goto done;
> > +             }
> > +
> > +             cpumask_set_cpu(cpu, &priv->lmask);
> > +             handler->local.msi_pa = mmio->pa + reloff;
> > +             handler->local.msi_va = mmio->va + reloff;
> > +             handler->priv = priv;
> > +
> > +done:
> > +             nr_handlers++;
> > +     }
> > +
> > +     /* If no CPU handlers found then can't take interrupts */
> > +     if (!nr_handlers) {
> > +             pr_err("%pfwP: No CPU handlers found\n", fwnode);
> > +             rc = -ENODEV;
> > +             goto out_ids_cleanup;
> > +     }
> > +
> > +     /* Find parent domain and register chained handler */
> > +     domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(),
> > +                                       DOMAIN_BUS_ANY);
> > +     if (!domain) {
> > +             pr_err("%pfwP: Failed to find INTC domain\n", fwnode);
> > +             rc = -ENOENT;
> > +             goto out_ids_cleanup;
> > +     }
> > +     imsic_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT);
> > +     if (!imsic_parent_irq) {
> > +             pr_err("%pfwP: Failed to create INTC mapping\n", fwnode);
> > +             rc = -ENOENT;
> > +             goto out_ids_cleanup;
> > +     }
> > +     irq_set_chained_handler(imsic_parent_irq, imsic_handle_irq);
> > +
> > +     /* Initialize IPI domain */
> > +     rc = imsic_ipi_domain_init(priv);
> > +     if (rc) {
> > +             pr_err("%pfwP: Failed to initialize IPI domain\n", fwnode);
> > +             goto out_ids_cleanup;
> > +     }
> > +
> > +     /* Initialize IRQ and MSI domains */
> > +     rc = imsic_irq_domains_init(priv, fwnode);
> > +     if (rc) {
> > +             pr_err("%pfwP: Failed to initialize IRQ and MSI domains\n",
> > +                    fwnode);
> > +             goto out_ipi_domain_cleanup;
> > +     }
> > +
> > +     /* Setup cpuhp state */
> > +     cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
> > +                       "irqchip/riscv/imsic:starting",
> > +                       imsic_starting_cpu, imsic_dying_cpu);
> > +
> > +     /*
> > +      * Only one IMSIC instance allowed in a platform for clean
> > +      * implementation of SMP IRQ affinity and per-CPU IPIs.
> > +      *
> > +      * This means on a multi-socket (or multi-die) platform we
> > +      * will have multiple MMIO regions for one IMSIC instance.
> > +      */
> > +     imsic_init_done = true;
> > +
> > +     pr_info("%pfwP:  hart-index-bits: %d,  guest-index-bits: %d\n",
> > +             fwnode, global->hart_index_bits, global->guest_index_bits);
> > +     pr_info("%pfwP: group-index-bits: %d, group-index-shift: %d\n",
> > +             fwnode, global->group_index_bits, global->group_index_shift);
> > +     pr_info("%pfwP: mapped %d interrupts for %d CPUs at %pa\n",
> > +             fwnode, global->nr_ids, nr_handlers, &global->base_addr);
> > +     if (priv->ipi_lsync_id)
> > +             pr_info("%pfwP: enable/disable sync using interrupt %d\n",
> > +                     fwnode, priv->ipi_lsync_id);
> > +     if (priv->ipi_id)
> > +             pr_info("%pfwP: providing IPIs using interrupt %d\n",
> > +                     fwnode, priv->ipi_id);
> > +
> > +     return 0;
> > +
> > +out_ipi_domain_cleanup:
> > +     imsic_ipi_domain_cleanup(priv);
> > +out_ids_cleanup:
> > +     imsic_ids_cleanup(priv);
> > +out_iounmap:
> > +     for (i = 0; i < priv->num_mmios; i++) {
> > +             if (priv->mmios[i].va)
> > +                     iounmap(priv->mmios[i].va);
> > +     }
> > +     kfree(priv->mmios);
> > +out_free_priv:
> > +     kfree(priv);
> > +     return rc;
> > +}
> > +
> > +static u32 __init imsic_dt_nr_parent_irq(struct fwnode_handle *fwnode,
> > +                                      void *fwopaque)
> > +{
> > +     return of_irq_count(to_of_node(fwnode));
> > +}
> > +
> > +static int __init imsic_dt_parent_hartid(struct fwnode_handle *fwnode,
> > +                                      void *fwopaque, u32 index,
> > +                                      unsigned long *out_hartid)
> > +{
> > +     struct of_phandle_args parent;
> > +     int rc;
> > +
> > +     rc = of_irq_parse_one(to_of_node(fwnode), index, &parent);
> > +     if (rc)
> > +             return rc;
> > +
> > +     /*
> > +      * Skip interrupts other than external interrupts for
> > +      * current privilege level.
> > +      */
> > +     if (parent.args[0] != RV_IRQ_EXT)
> > +             return -EINVAL;
> > +
> > +     return riscv_of_parent_hartid(parent.np, out_hartid);
> > +}
> > +
> > +static u32 __init imsic_dt_nr_mmio(struct fwnode_handle *fwnode,
> > +                                void *fwopaque)
> > +{
> > +     u32 ret = 0;
> > +     struct resource res;
> > +
> > +     while (!of_address_to_resource(to_of_node(fwnode), ret, &res))
> > +             ret++;
> > +
> > +     return ret;
> > +}
> > +
> > +static int __init imsic_mmio_to_resource(struct fwnode_handle *fwnode,
> > +                                      void *fwopaque, u32 index,
> > +                                      struct resource *res)
> > +{
> > +     return of_address_to_resource(to_of_node(fwnode), index, res);
> > +}
> > +
> > +static void __iomem __init *imsic_dt_mmio_map(struct fwnode_handle *fwnode,
> > +                                           void *fwopaque, u32 index)
> > +{
> > +     return of_iomap(to_of_node(fwnode), index);
> > +}
> > +
> > +static int __init imsic_dt_read_u32(struct fwnode_handle *fwnode,
> > +                                 void *fwopaque, const char *prop,
> > +                                 u32 *out_val)
> > +{
> > +     return of_property_read_u32(to_of_node(fwnode), prop, out_val);
> > +}
> > +
> > +static bool __init imsic_dt_read_bool(struct fwnode_handle *fwnode,
> > +                                   void *fwopaque, const char *prop)
> > +{
> > +     return of_property_read_bool(to_of_node(fwnode), prop);
> > +}
> > +
> > +static int __init imsic_dt_init(struct device_node *node,
> > +                             struct device_node *parent)
> > +{
> > +     struct imsic_fwnode_ops ops = {
> > +             .nr_parent_irq = imsic_dt_nr_parent_irq,
> > +             .parent_hartid = imsic_dt_parent_hartid,
> > +             .nr_mmio = imsic_dt_nr_mmio,
> > +             .mmio_to_resource = imsic_mmio_to_resource,
> > +             .mmio_map = imsic_dt_mmio_map,
> > +             .read_u32 = imsic_dt_read_u32,
> > +             .read_bool = imsic_dt_read_bool,
> > +     };
> > +
> > +     return imsic_init(&ops, &node->fwnode, NULL);
> > +}
> > +IRQCHIP_DECLARE(riscv_imsic, "riscv,imsics", imsic_dt_init);
> > diff --git a/include/linux/irqchip/riscv-imsic.h b/include/linux/irqchip/riscv-imsic.h
> > new file mode 100644
> > index 000000000000..5d1387adc0ba
> > --- /dev/null
> > +++ b/include/linux/irqchip/riscv-imsic.h
> > @@ -0,0 +1,92 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * Copyright (C) 2021 Western Digital Corporation or its affiliates.
> > + * Copyright (C) 2022 Ventana Micro Systems Inc.
> > + */
> > +#ifndef __LINUX_IRQCHIP_RISCV_IMSIC_H
> > +#define __LINUX_IRQCHIP_RISCV_IMSIC_H
> > +
> > +#include <linux/types.h>
> > +#include <asm/csr.h>
> > +
> > +#define IMSIC_MMIO_PAGE_SHIFT                12
> > +#define IMSIC_MMIO_PAGE_SZ           (1UL << IMSIC_MMIO_PAGE_SHIFT)
> > +#define IMSIC_MMIO_PAGE_LE           0x00
> > +#define IMSIC_MMIO_PAGE_BE           0x04
> > +
> > +#define IMSIC_MIN_ID                 63
> > +#define IMSIC_MAX_ID                 2048
> > +
> > +#define IMSIC_EIDELIVERY             0x70
> > +
> > +#define IMSIC_EITHRESHOLD            0x72
> > +
> > +#define IMSIC_EIP0                   0x80
> > +#define IMSIC_EIP63                  0xbf
> > +#define IMSIC_EIPx_BITS                      32
> > +
> > +#define IMSIC_EIE0                   0xc0
> > +#define IMSIC_EIE63                  0xff
> > +#define IMSIC_EIEx_BITS                      32
> > +
> > +#define IMSIC_FIRST                  IMSIC_EIDELIVERY
> > +#define IMSIC_LAST                   IMSIC_EIE63
> > +
> > +#define IMSIC_MMIO_SETIPNUM_LE               0x00
> > +#define IMSIC_MMIO_SETIPNUM_BE               0x04
> > +
> > +struct imsic_global_config {
> > +     /*
> > +      * MSI Target Address Scheme
> > +      *
> > +      * XLEN-1                                                12     0
> > +      * |                                                     |     |
> > +      * -------------------------------------------------------------
> > +      * |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index|  0  |
> > +      * -------------------------------------------------------------
> > +      */
> > +
> > +     /* Bits representing Guest index, HART index, and Group index */
> > +     u32 guest_index_bits;
> > +     u32 hart_index_bits;
> > +     u32 group_index_bits;
> > +     u32 group_index_shift;
> > +
> > +     /* Global base address matching all target MSI addresses */
> > +     phys_addr_t base_addr;
> > +
> > +     /* Number of interrupt identities */
> > +     u32 nr_ids;
> > +
> > +     /* Number of guest interrupt identities */
> > +     u32 nr_guest_ids;
> > +};
> > +
> > +struct imsic_local_config {
> > +     phys_addr_t msi_pa;
> > +     void __iomem *msi_va;
> > +};
> > +
> > +#ifdef CONFIG_RISCV_IMSIC
> > +
> > +extern const struct imsic_global_config *imsic_get_global_config(void);
> > +
> > +extern const struct imsic_local_config *imsic_get_local_config(
> > +                                                     unsigned int cpu);
> > +
> > +#else
> > +
> > +static inline const struct imsic_global_config *imsic_get_global_config(void)
> > +{
> > +     return NULL;
> > +}
> > +
> > +static inline const struct imsic_local_config *imsic_get_local_config(
> > +                                                     unsigned int cpu)
> > +{
> > +     return NULL;
> > +}
> > +
> > +#endif
> > +
> > +#endif
>
> _______________________________________________
> linux-riscv mailing list
> linux-riscv@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-riscv

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

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

end of thread, other threads:[~2023-01-03 16:45 UTC | newest]

Thread overview: 96+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-11  4:41 [PATCH 0/9] Linux RISC-V AIA Support Anup Patel
2022-11-11  4:41 ` Anup Patel
2022-11-11  4:41 ` [PATCH 1/9] RISC-V: Add AIA related CSR defines Anup Patel
2022-11-11  4:41   ` Anup Patel
2022-11-11  4:42 ` [PATCH 2/9] RISC-V: Detect AIA CSRs from ISA string Anup Patel
2022-11-11  4:42   ` Anup Patel
2022-11-13 14:20   ` Conor Dooley
2022-11-13 14:20     ` Conor Dooley
2022-11-11  4:42 ` [PATCH 3/9] irqchip/riscv-intc: Add support for RISC-V AIA Anup Patel
2022-11-11  4:42   ` Anup Patel
2022-11-11  4:42 ` [PATCH 4/9] dt-bindings: Add RISC-V incoming MSI controller bindings Anup Patel
2022-11-11  4:42   ` Anup Patel
2022-11-11  9:11   ` Atish Patra
2022-11-11  9:11     ` Atish Patra
2022-11-13 14:48   ` Conor Dooley
2022-11-13 14:48     ` Conor Dooley
2022-11-14 12:29     ` Anup Patel
2022-11-14 12:29       ` Anup Patel
2022-11-15 22:34       ` Conor Dooley
2022-11-15 22:34         ` Conor Dooley
2022-11-16  9:00         ` Krzysztof Kozlowski
2022-11-16  9:00           ` Krzysztof Kozlowski
2022-11-16  9:20           ` Conor Dooley
2022-11-16  9:20             ` Conor Dooley
2022-11-16  9:21             ` Krzysztof Kozlowski
2022-11-16  9:21               ` Krzysztof Kozlowski
2022-11-16 10:34           ` Anup Patel
2022-11-16 10:34             ` Anup Patel
2022-11-16 13:29             ` Conor Dooley
2022-11-16 13:29               ` Conor Dooley
2022-11-14  9:49   ` Krzysztof Kozlowski
2022-11-14  9:49     ` Krzysztof Kozlowski
2022-11-14 12:06     ` Anup Patel
2022-11-14 12:06       ` Anup Patel
2022-11-14 12:14       ` Conor Dooley
2022-11-14 12:14         ` Conor Dooley
2022-11-14 12:21       ` Krzysztof Kozlowski
2022-11-14 12:21         ` Krzysztof Kozlowski
2022-11-14 15:04         ` Anup Patel
2022-11-14 15:04           ` Anup Patel
2022-11-15 14:15           ` Krzysztof Kozlowski
2022-11-15 14:15             ` Krzysztof Kozlowski
2022-11-16 19:14       ` Rob Herring
2022-11-16 19:14         ` Rob Herring
2023-01-02 15:59         ` Anup Patel
2023-01-02 15:59           ` Anup Patel
2022-11-11  4:42 ` [PATCH 5/9] irqchip: Add RISC-V incoming MSI controller driver Anup Patel
2022-11-11  4:42   ` Anup Patel
2022-11-11 16:02   ` Andrew Bresticker
2022-11-11 16:02     ` Andrew Bresticker
2023-01-02 16:25     ` Anup Patel
2023-01-02 16:25       ` Anup Patel
2022-11-23  7:28   ` Ruan Jinjie
2023-01-03 13:42     ` Anup Patel
2022-11-11  4:42 ` [PATCH 6/9] dt-bindings: Add RISC-V advanced PLIC bindings Anup Patel
2022-11-11  4:42   ` Anup Patel
2022-11-13 15:44   ` Conor Dooley
2022-11-13 15:44     ` Conor Dooley
2023-01-02 16:50     ` Anup Patel
2023-01-02 16:50       ` Anup Patel
2023-01-02 18:17       ` Conor Dooley
2023-01-02 18:17         ` Conor Dooley
2023-01-03  5:10         ` Anup Patel
2023-01-03  5:10           ` Anup Patel
2023-01-03  8:59       ` Krzysztof Kozlowski
2023-01-03  8:59         ` Krzysztof Kozlowski
2023-01-03 13:05         ` Anup Patel
2023-01-03 13:05           ` Anup Patel
2022-11-14  9:51   ` Krzysztof Kozlowski
2022-11-14  9:51     ` Krzysztof Kozlowski
2022-11-14 12:11     ` Anup Patel
2022-11-14 12:11       ` Anup Patel
2022-11-16 19:27   ` Rob Herring
2022-11-16 19:27     ` Rob Herring
2023-01-02 17:18     ` Anup Patel
2023-01-02 17:18       ` Anup Patel
2022-11-11  4:42 ` [PATCH 7/9] irqchip: Add RISC-V advanced PLIC driver Anup Patel
2022-11-11  4:42   ` Anup Patel
2022-11-11 23:17   ` Andrew Bresticker
2022-11-11 23:17     ` Andrew Bresticker
2022-11-11  4:42 ` [PATCH 8/9] RISC-V: Select APLIC and IMSIC drivers for QEMU virt machine Anup Patel
2022-11-11  4:42   ` Anup Patel
2022-11-15 22:29   ` Conor Dooley
2022-11-15 22:29     ` Conor Dooley
2022-11-11  4:42 ` [PATCH 9/9] MAINTAINERS: Add entry for RISC-V AIA drivers Anup Patel
2022-11-11  4:42   ` Anup Patel
2022-11-11  9:07 ` [PATCH 0/9] Linux RISC-V AIA Support Atish Patra
2022-11-11  9:07   ` Atish Patra
2022-11-11  9:13   ` Atish Patra
2022-11-11  9:13     ` Atish Patra
2022-11-11 19:01     ` Atish Patra
2022-11-11 19:01       ` Atish Patra
2023-01-02 10:06       ` Anup Patel
2023-01-02 10:06         ` Anup Patel
2023-01-02 10:05   ` Anup Patel
2023-01-02 10:05     ` Anup Patel

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.