linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/8] PCI: rzg3s-host: Add PCIe driver for Renesas RZ/G3S SoC
@ 2025-04-30 10:32 Claudiu
  2025-04-30 10:32 ` [PATCH 1/8] soc: renesas: r9a08g045-sysc: Add max reg offset Claudiu
                   ` (7 more replies)
  0 siblings, 8 replies; 28+ messages in thread
From: Claudiu @ 2025-04-30 10:32 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kw, manivannan.sadhasivam, robh, krzk+dt,
	conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel
  Cc: claudiu.beznea, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, linux-arm-kernel, linux-clk, Claudiu Beznea

From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>

Hi,

Series adds a PCIe driver for the Renesas RZ/G3S SoC.
It is split as follows:
- patch 1/8:		updates the max register offset for RZ/G3S SYSC;
			this is necessary as the PCIe need to setup the
			SYSC for proper functioning
- patch 2/8:		adds clock, reset and power domain support for
			the PCIe IP
- patch 3/8:		exports of_irq_count() symbol; this is used by the
			proposed driver to get the number of INTx interrupts
- patches 4-5/8:	add PCIe support for the RZ/G3S SoC
- patches 6-8/8:	add device tree support

Please provide your feedback.

Merge strategy, if any:
- patches 1-2,6-8/8 can go through the Renesas tree
- patches 4-5/8 can go through the PCI tree; these depends on patch 3/8,
  proper sync will be needed b/w PCI tree and Rob's tree

Thank you,
Claudiu Beznea

Claudiu Beznea (8):
  soc: renesas: r9a08g045-sysc: Add max reg offset
  clk: renesas: r9a08g045: Add clocks, resets and power domain support
    for the PCIe
  of/irq: Export of_irq_count()
  dt-bindings: PCI: renesas,r9a08g045s33-pcie: Add documentation for the
    PCIe IP on Renesas RZ/G3S
  PCI: rzg3s-host: Add Initial PCIe Host Driver for Renesas RZ/G3S SoC
  arm64: dts: renesas: r9a08g045s33: Add PCIe node
  arm64: dts: renesas: rzg3s-smarc: Enable PCIe
  arm64: defconfig: Enable PCIe for the Renesas RZ/G3S SoC

 .../pci/renesas,r9a08g045s33-pcie.yaml        |  242 +++
 MAINTAINERS                                   |    8 +
 arch/arm64/boot/dts/renesas/r9a08g045s33.dtsi |   70 +
 arch/arm64/boot/dts/renesas/rzg3s-smarc.dtsi  |   11 +
 arch/arm64/configs/defconfig                  |    1 +
 drivers/clk/renesas/r9a08g045-cpg.c           |   19 +
 drivers/of/irq.c                              |    1 +
 drivers/pci/controller/Kconfig                |    7 +
 drivers/pci/controller/Makefile               |    1 +
 drivers/pci/controller/pcie-rzg3s-host.c      | 1561 +++++++++++++++++
 drivers/soc/renesas/r9a08g045-sysc.c          |    1 +
 11 files changed, 1922 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/renesas,r9a08g045s33-pcie.yaml
 create mode 100644 drivers/pci/controller/pcie-rzg3s-host.c

-- 
2.43.0



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

* [PATCH 1/8] soc: renesas: r9a08g045-sysc: Add max reg offset
  2025-04-30 10:32 [PATCH 0/8] PCI: rzg3s-host: Add PCIe driver for Renesas RZ/G3S SoC Claudiu
@ 2025-04-30 10:32 ` Claudiu
  2025-05-01  9:26   ` kernel test robot
                     ` (2 more replies)
  2025-04-30 10:32 ` [PATCH 2/8] clk: renesas: r9a08g045: Add clocks, resets and power domain support for the PCIe Claudiu
                   ` (6 subsequent siblings)
  7 siblings, 3 replies; 28+ messages in thread
From: Claudiu @ 2025-04-30 10:32 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kw, manivannan.sadhasivam, robh, krzk+dt,
	conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel
  Cc: claudiu.beznea, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, linux-arm-kernel, linux-clk, Claudiu Beznea

From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>

Add max register offset.

Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---

This patch depends on https://lore.kernel.org/all/20250330214945.185725-2-john.madieu.xa@bp.renesas.com/


 drivers/soc/renesas/r9a08g045-sysc.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/soc/renesas/r9a08g045-sysc.c b/drivers/soc/renesas/r9a08g045-sysc.c
index f4db1431e036..2eea95531290 100644
--- a/drivers/soc/renesas/r9a08g045-sysc.c
+++ b/drivers/soc/renesas/r9a08g045-sysc.c
@@ -20,4 +20,5 @@ static const struct rz_sysc_soc_id_init_data rzg3s_sysc_soc_id_init_data __initc
 
 const struct rz_sysc_init_data rzg3s_sysc_init_data __initconst = {
 	.soc_id_init_data = &rzg3s_sysc_soc_id_init_data,
+	.max_register_offset = 0xe28,
 };
-- 
2.43.0



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

* [PATCH 2/8] clk: renesas: r9a08g045: Add clocks, resets and power domain support for the PCIe
  2025-04-30 10:32 [PATCH 0/8] PCI: rzg3s-host: Add PCIe driver for Renesas RZ/G3S SoC Claudiu
  2025-04-30 10:32 ` [PATCH 1/8] soc: renesas: r9a08g045-sysc: Add max reg offset Claudiu
@ 2025-04-30 10:32 ` Claudiu
  2025-04-30 10:32 ` [PATCH 3/8] of/irq: Export of_irq_count() Claudiu
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 28+ messages in thread
From: Claudiu @ 2025-04-30 10:32 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kw, manivannan.sadhasivam, robh, krzk+dt,
	conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel
  Cc: claudiu.beznea, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, linux-arm-kernel, linux-clk, Claudiu Beznea

From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>

Add clocks, resets and power domains for the PCIe IP available on the
Renesas RZ/G3S SoC. The clkl1pm clock is required for PCIe link power
management (PM) control and should be enabled based on the state of the
CLKREQ# pin. Therefore, mark it as a no_pm clock to allow the PCIe driver
to manage it during link PM transitions.

Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
 drivers/clk/renesas/r9a08g045-cpg.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/drivers/clk/renesas/r9a08g045-cpg.c b/drivers/clk/renesas/r9a08g045-cpg.c
index 4035f3443598..33219164a19a 100644
--- a/drivers/clk/renesas/r9a08g045-cpg.c
+++ b/drivers/clk/renesas/r9a08g045-cpg.c
@@ -243,6 +243,8 @@ static const struct rzg2l_mod_clk r9a08g045_mod_clks[] = {
 	DEF_MOD("adc_adclk",		R9A08G045_ADC_ADCLK, R9A08G045_CLK_TSU, 0x5a8, 0),
 	DEF_MOD("adc_pclk",		R9A08G045_ADC_PCLK, R9A08G045_CLK_TSU, 0x5a8, 1),
 	DEF_MOD("tsu_pclk",		R9A08G045_TSU_PCLK, R9A08G045_CLK_TSU, 0x5ac, 0),
+	DEF_MOD("pci_aclk",		R9A08G045_PCI_ACLK, R9A08G045_CLK_M0, 0x608, 0),
+	DEF_MOD("pci_clk1pm",		R9A08G045_PCI_CLKL1PM, R9A08G045_CLK_ZT, 0x608, 1),
 	DEF_MOD("vbat_bclk",		R9A08G045_VBAT_BCLK, R9A08G045_OSCCLK, 0x614, 0),
 };
 
@@ -282,6 +284,13 @@ static const struct rzg2l_reset r9a08g045_resets[] = {
 	DEF_RST(R9A08G045_ADC_PRESETN, 0x8a8, 0),
 	DEF_RST(R9A08G045_ADC_ADRST_N, 0x8a8, 1),
 	DEF_RST(R9A08G045_TSU_PRESETN, 0x8ac, 0),
+	DEF_RST(R9A08G045_PCI_ARESETN, 0x908, 0),
+	DEF_RST(R9A08G045_PCI_RST_B, 0x908, 1),
+	DEF_RST(R9A08G045_PCI_RST_GP_B, 0x908, 2),
+	DEF_RST(R9A08G045_PCI_RST_PS_B, 0x908, 3),
+	DEF_RST(R9A08G045_PCI_RST_RSM_B, 0x908, 4),
+	DEF_RST(R9A08G045_PCI_RST_CFG_B, 0x908, 5),
+	DEF_RST(R9A08G045_PCI_RST_LOAD_B, 0x908, 6),
 	DEF_RST(R9A08G045_VBAT_BRESETN, 0x914, 0),
 };
 
@@ -358,6 +367,8 @@ static const struct rzg2l_cpg_pm_domain_init_data r9a08g045_pm_domains[] = {
 				DEF_REG_CONF(CPG_BUS_MCPU2_MSTOP, BIT(14)), 0),
 	DEF_PD("tsu",		R9A08G045_PD_TSU,
 				DEF_REG_CONF(CPG_BUS_MCPU2_MSTOP, BIT(15)), 0),
+	DEF_PD("pci",		R9A08G045_PD_PCI,
+				DEF_REG_CONF(CPG_BUS_PERI_COM_MSTOP, BIT(10)), 0),
 	DEF_PD("vbat",		R9A08G045_PD_VBAT,
 				DEF_REG_CONF(CPG_BUS_MCPU3_MSTOP, BIT(8)),
 				GENPD_FLAG_ALWAYS_ON),
@@ -365,6 +376,10 @@ static const struct rzg2l_cpg_pm_domain_init_data r9a08g045_pm_domains[] = {
 				DEF_REG_CONF(CPG_BUS_MCPU3_MSTOP, BIT(7)), 0),
 };
 
+static const unsigned int r9a08g045_no_pm_mod_clks[] = {
+	MOD_CLK_BASE + R9A08G045_PCI_CLKL1PM,
+};
+
 const struct rzg2l_cpg_info r9a08g045_cpg_info = {
 	/* Core Clocks */
 	.core_clks = r9a08g045_core_clks,
@@ -381,6 +396,10 @@ const struct rzg2l_cpg_info r9a08g045_cpg_info = {
 	.num_mod_clks = ARRAY_SIZE(r9a08g045_mod_clks),
 	.num_hw_mod_clks = R9A08G045_VBAT_BCLK + 1,
 
+	/* No PM modules Clocks */
+	.no_pm_mod_clks = r9a08g045_no_pm_mod_clks,
+	.num_no_pm_mod_clks = ARRAY_SIZE(r9a08g045_no_pm_mod_clks),
+
 	/* Resets */
 	.resets = r9a08g045_resets,
 	.num_resets = R9A08G045_VBAT_BRESETN + 1, /* Last reset ID + 1 */
-- 
2.43.0



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

* [PATCH 3/8] of/irq: Export of_irq_count()
  2025-04-30 10:32 [PATCH 0/8] PCI: rzg3s-host: Add PCIe driver for Renesas RZ/G3S SoC Claudiu
  2025-04-30 10:32 ` [PATCH 1/8] soc: renesas: r9a08g045-sysc: Add max reg offset Claudiu
  2025-04-30 10:32 ` [PATCH 2/8] clk: renesas: r9a08g045: Add clocks, resets and power domain support for the PCIe Claudiu
@ 2025-04-30 10:32 ` Claudiu
  2025-05-09 19:36   ` Rob Herring
  2025-04-30 10:32 ` [PATCH 4/8] dt-bindings: PCI: renesas,r9a08g045s33-pcie: Add documentation for the PCIe IP on Renesas RZ/G3S Claudiu
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 28+ messages in thread
From: Claudiu @ 2025-04-30 10:32 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kw, manivannan.sadhasivam, robh, krzk+dt,
	conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel
  Cc: claudiu.beznea, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, linux-arm-kernel, linux-clk, Claudiu Beznea

From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>

Export of_irq_count() to be able to use it in modules.

Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
 drivers/of/irq.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index f8ad79b9b1c9..5adda1dac3cf 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -519,6 +519,7 @@ int of_irq_count(struct device_node *dev)
 
 	return nr;
 }
+EXPORT_SYMBOL_GPL(of_irq_count);
 
 /**
  * of_irq_to_resource_table - Fill in resource table with node's IRQ info
-- 
2.43.0



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

* [PATCH 4/8] dt-bindings: PCI: renesas,r9a08g045s33-pcie: Add documentation for the PCIe IP on Renesas RZ/G3S
  2025-04-30 10:32 [PATCH 0/8] PCI: rzg3s-host: Add PCIe driver for Renesas RZ/G3S SoC Claudiu
                   ` (2 preceding siblings ...)
  2025-04-30 10:32 ` [PATCH 3/8] of/irq: Export of_irq_count() Claudiu
@ 2025-04-30 10:32 ` Claudiu
  2025-05-01 20:16   ` Bjorn Helgaas
  2025-05-09 21:08   ` Rob Herring
  2025-04-30 10:32 ` [PATCH 5/8] PCI: rzg3s-host: Add Initial PCIe Host Driver for Renesas RZ/G3S SoC Claudiu
                   ` (3 subsequent siblings)
  7 siblings, 2 replies; 28+ messages in thread
From: Claudiu @ 2025-04-30 10:32 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kw, manivannan.sadhasivam, robh, krzk+dt,
	conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel
  Cc: claudiu.beznea, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, linux-arm-kernel, linux-clk, Claudiu Beznea

From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>

The PCIe IP available on the Renesas RZ/G3S complies with the PCI Express
Base Specification 4.0. It is designed for root complex applications and
features a single-lane (x1) implementation. Add documentation for it.
The interrupts, interrupt-names, resets, reset-names, clocks, clock-names
description were obtained from the hardware manual.

Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
 .../pci/renesas,r9a08g045s33-pcie.yaml        | 242 ++++++++++++++++++
 1 file changed, 242 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/renesas,r9a08g045s33-pcie.yaml

diff --git a/Documentation/devicetree/bindings/pci/renesas,r9a08g045s33-pcie.yaml b/Documentation/devicetree/bindings/pci/renesas,r9a08g045s33-pcie.yaml
new file mode 100644
index 000000000000..354f9c3be139
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/renesas,r9a08g045s33-pcie.yaml
@@ -0,0 +1,242 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/renesas,r9a08g045s33-pcie.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Renesas RZ/G3S PCIe host controller
+
+maintainers:
+  - Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
+
+description:
+  Renesas RZ/G3S PCIe host controller complies with PCIe Base Specification
+  4.0 and supports up to 5 GT/s (Gen2).
+
+properties:
+  compatible:
+    const: renesas,r9a08g045s33-pcie # RZ/G3S
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    items:
+      - description: System error interrupt
+      - description: System error on correctable error interrupt
+      - description: System error on non-fatal error interrupt
+      - description: System error on fatal error interrupt
+      - description: AXI error interrupt
+      - description: INTA interrupt
+      - description: INTB interrupt
+      - description: INTC interrupt
+      - description: INTD interrupt
+      - description: MSI interrupt
+      - description: Link bandwidth interrupt
+      - description: PME interrupt
+      - description: DMA interrupt
+      - description: PCIe event interrupt
+      - description: Message interrupt
+      - description: All interrupts
+
+  interrupt-names:
+    items:
+      - description: int_serr
+      - description: int_ser_cor
+      - description: int_serr_nonfatal
+      - description: int_serr_fatal
+      - description: axi_err_int
+      - description: inta_rc
+      - description: intb_rc
+      - description: intc_rc
+      - description: intd_rc
+      - description: intmsi_rc
+      - description: int_link_bandwidth
+      - description: int_pm_pme
+      - description: dma_int
+      - description: pcie_evt_int
+      - description: msg_int
+      - description: int_all
+
+  clocks:
+    items:
+      - description: System clock
+      - description: PM control clock
+
+  clock-names:
+    items:
+      - description: aclk
+      - description: clkl1pm
+
+  resets:
+    items:
+      - description: AXI2PCIe Bridge reset
+      - description: Data link layer/transaction layer reset
+      - description: Transaction layer (ACLK domain) reset
+      - description: Transaction layer (PCLK domain) reset
+      - description: Physical layer reset
+      - description: Configuration register reset
+      - description: Configuration register reset
+
+  reset-names:
+    items:
+      - description: aresetn
+      - description: rst_b
+      - description: rst_gp_b
+      - description: rst_ps_b
+      - description: rst_rsm_b
+      - description: rst_cfg_b
+      - description: rst_load_b
+
+  power-domains:
+    maxItems: 1
+
+  dma-ranges:
+    description:
+      A single range for the inbound memory region.
+    maxItems: 1
+
+  renesas,sysc:
+    description: System controller phandle
+    $ref: /schemas/types.yaml#/definitions/phandle
+
+  vendor-id:
+    const: 0x1912
+
+  device-id:
+    const: 0x0033
+
+  legacy-interrupt-controller:
+    description: Interrupt controller node for handling legacy PCI interrupts
+    type: object
+
+    properties:
+      "#address-cells":
+        const: 0
+
+      "#interrupt-cells":
+        const: 1
+
+      interrupt-controller: true
+
+      interrupts:
+        items:
+          - description: INTA interrupt
+          - description: INTB interrupt
+          - description: INTC interrupt
+          - description: INTD interrupt
+
+    required:
+      - "#address-cells"
+      - "#interrupt-cells"
+      - interrupt-controller
+      - interrupts
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - resets
+  - reset-names
+  - interrupts
+  - interrupt-names
+  - interrupt-map
+  - interrupt-map-mask
+  - power-domains
+  - "#address-cells"
+  - "#size-cells"
+  - "#interrupt-cells"
+  - renesas,sysc
+  - vendor-id
+  - device-id
+
+allOf:
+  - $ref: /schemas/pci/pci-host-bridge.yaml#
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/r9a08g045-cpg.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    bus {
+        #address-cells = <2>;
+        #size-cells = <2>;
+
+        gic: interrupt-controller {
+            interrupt-controller;
+            #interrupt-cells = <3>;
+        };
+
+        pcie@11e40000 {
+            compatible = "renesas,r9a08g045s33-pcie";
+            reg = <0 0x11e40000 0 0x10000>;
+            ranges = <0x03000000 0 0x30000000 0 0x30000000 0 0x8000000>;
+            dma-ranges = <0x42000000 0 0x48000000 0 0x48000000 0 0x8000000>;
+            bus-range = <0x0 0xff>;
+            clocks = <&cpg CPG_MOD R9A08G045_PCI_ACLK>,
+                     <&cpg CPG_MOD R9A08G045_PCI_CLKL1PM>;
+            clock-names = "aclk", "clkl1pm";
+            resets = <&cpg R9A08G045_PCI_ARESETN>,
+                     <&cpg R9A08G045_PCI_RST_B>,
+                     <&cpg R9A08G045_PCI_RST_GP_B>,
+                     <&cpg R9A08G045_PCI_RST_PS_B>,
+                     <&cpg R9A08G045_PCI_RST_RSM_B>,
+                     <&cpg R9A08G045_PCI_RST_CFG_B>,
+                     <&cpg R9A08G045_PCI_RST_LOAD_B>;
+            reset-names = "aresetn", "rst_b", "rst_gp_b", "rst_ps_b",
+                          "rst_rsm_b", "rst_cfg_b", "rst_load_b";
+            interrupts = <GIC_SPI 395 IRQ_TYPE_LEVEL_HIGH>,
+                         <GIC_SPI 396 IRQ_TYPE_LEVEL_HIGH>,
+                         <GIC_SPI 397 IRQ_TYPE_LEVEL_HIGH>,
+                         <GIC_SPI 398 IRQ_TYPE_LEVEL_HIGH>,
+                         <GIC_SPI 399 IRQ_TYPE_LEVEL_HIGH>,
+                         <GIC_SPI 400 IRQ_TYPE_LEVEL_HIGH>,
+                         <GIC_SPI 401 IRQ_TYPE_LEVEL_HIGH>,
+                         <GIC_SPI 402 IRQ_TYPE_LEVEL_HIGH>,
+                         <GIC_SPI 403 IRQ_TYPE_LEVEL_HIGH>,
+                         <GIC_SPI 404 IRQ_TYPE_LEVEL_HIGH>,
+                         <GIC_SPI 405 IRQ_TYPE_LEVEL_HIGH>,
+                         <GIC_SPI 406 IRQ_TYPE_LEVEL_HIGH>,
+                         <GIC_SPI 407 IRQ_TYPE_LEVEL_HIGH>,
+                         <GIC_SPI 408 IRQ_TYPE_LEVEL_HIGH>,
+                         <GIC_SPI 409 IRQ_TYPE_LEVEL_HIGH>,
+                         <GIC_SPI 410 IRQ_TYPE_LEVEL_HIGH>;
+            interrupt-names = "int_serr", "int_serr_cor", "int_serr_nonfatal",
+                              "int_serr_fatal", "axi_err_int", "inta_rc",
+                              "intb_rc", "intc_rc", "intd_rc",
+                              "intmsi_rc", "int_link_bandwidth", "int_pm_pme",
+                              "dma_int", "pcie_evt_int", "msg_int",
+                              "int_all";
+            #interrupt-cells = <1>;
+            interrupt-map-mask = <0 0 0 7>;
+            interrupt-map = <0 0 0 1 &pcie_intx 0>, /* INT A */
+                            <0 0 0 2 &pcie_intx 1>, /* INT B */
+                            <0 0 0 3 &pcie_intx 2>, /* INT C */
+                            <0 0 0 4 &pcie_intx 3>; /* INT D */
+            device_type = "pci";
+            num-lanes = <1>;
+            #address-cells = <3>;
+            #size-cells = <2>;
+            power-domains = <&cpg>;
+            renesas,sysc = <&sysc>;
+            vendor-id = <0x1912>;
+            device-id = <0x0033>;
+
+            pcie_intx: legacy-interrupt-controller {
+                interrupt-controller;
+                #interrupt-cells = <1>;
+                #address-cells = <0>;
+                interrupt-parent = <&gic>;
+                interrupts = <GIC_SPI 400 IRQ_TYPE_LEVEL_HIGH>,
+                             <GIC_SPI 401 IRQ_TYPE_LEVEL_HIGH>,
+                             <GIC_SPI 402 IRQ_TYPE_LEVEL_HIGH>,
+                             <GIC_SPI 403 IRQ_TYPE_LEVEL_HIGH>;
+            };
+        };
+    };
+
+...
-- 
2.43.0



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

* [PATCH 5/8] PCI: rzg3s-host: Add Initial PCIe Host Driver for Renesas RZ/G3S SoC
  2025-04-30 10:32 [PATCH 0/8] PCI: rzg3s-host: Add PCIe driver for Renesas RZ/G3S SoC Claudiu
                   ` (3 preceding siblings ...)
  2025-04-30 10:32 ` [PATCH 4/8] dt-bindings: PCI: renesas,r9a08g045s33-pcie: Add documentation for the PCIe IP on Renesas RZ/G3S Claudiu
@ 2025-04-30 10:32 ` Claudiu
  2025-05-01 20:12   ` Bjorn Helgaas
                     ` (2 more replies)
  2025-04-30 10:32 ` [PATCH 6/8] arm64: dts: renesas: r9a08g045s33: Add PCIe node Claudiu
                   ` (2 subsequent siblings)
  7 siblings, 3 replies; 28+ messages in thread
From: Claudiu @ 2025-04-30 10:32 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kw, manivannan.sadhasivam, robh, krzk+dt,
	conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel
  Cc: claudiu.beznea, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, linux-arm-kernel, linux-clk, Claudiu Beznea

From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>

The Renesas RZ/G3S features a PCIe IP that complies with the PCI Express
Base Specification 4.0 and supports speeds of up to 5 GT/s. It functions
only as a root complex, with a single-lane (x1) configuration. The
controller includes Type 1 configuration registers, as well as IP
specific registers (called AXI registers) required for various adjustments.

Other Renesas RZ SoCs (e.g., RZ/G3E, RZ/V2H) share the same AXI registers
but have both Root Complex and Endpoint capabilities. As a result, the PCIe
host driver can be reused for these variants with minimal adjustments.

Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
 MAINTAINERS                              |    8 +
 drivers/pci/controller/Kconfig           |    7 +
 drivers/pci/controller/Makefile          |    1 +
 drivers/pci/controller/pcie-rzg3s-host.c | 1561 ++++++++++++++++++++++
 4 files changed, 1577 insertions(+)
 create mode 100644 drivers/pci/controller/pcie-rzg3s-host.c

diff --git a/MAINTAINERS b/MAINTAINERS
index e2e2942779eb..d7d985d174e2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19039,6 +19039,14 @@ S:	Maintained
 F:	drivers/pci/controller/dwc/pcie-qcom-common.c
 F:	drivers/pci/controller/dwc/pcie-qcom.c
 
+PCIE DRIVER FOR RENESAS RZ/G3S SERIES
+M:	Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
+L:	linux-pci@vger.kernel.org
+L:	linux-renesas-soc@vger.kernel.org
+S:	Supported
+F:	Documentation/devicetree/bindings/pci/renesas,r9a08g045s33-pcie.yaml
+F:	drivers/pci/controller/pcie-rzg3s-host.c
+
 PCIE DRIVER FOR ROCKCHIP
 M:	Shawn Lin <shawn.lin@rock-chips.com>
 L:	linux-pci@vger.kernel.org
diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
index 9800b7681054..102f361fcc63 100644
--- a/drivers/pci/controller/Kconfig
+++ b/drivers/pci/controller/Kconfig
@@ -256,6 +256,13 @@ config PCI_RCAR_GEN2
 	  There are 3 internal PCI controllers available with a single
 	  built-in EHCI/OHCI host controller present on each one.
 
+config PCIE_RENESAS_RZG3S_HOST
+	tristate "Renesas RZ/G3S PCIe host controller"
+	depends on ARCH_RENESAS || COMPILE_TEST
+	select MFD_SYSCON
+	help
+	  Say Y here if you want PCIe host controller support on Renesas RZ/G3S SoC.
+
 config PCIE_ROCKCHIP
 	bool
 	depends on PCI
diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile
index 038ccbd9e3ba..229929a945c2 100644
--- a/drivers/pci/controller/Makefile
+++ b/drivers/pci/controller/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o
 obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
 obj-$(CONFIG_PCIE_RCAR_HOST) += pcie-rcar.o pcie-rcar-host.o
 obj-$(CONFIG_PCIE_RCAR_EP) += pcie-rcar.o pcie-rcar-ep.o
+obj-$(CONFIG_PCIE_RENESAS_RZG3S_HOST) += pcie-rzg3s-host.o
 obj-$(CONFIG_PCI_HOST_COMMON) += pci-host-common.o
 obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o
 obj-$(CONFIG_PCI_HOST_THUNDER_ECAM) += pci-thunder-ecam.o
diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c
new file mode 100644
index 000000000000..c3bce0acd57e
--- /dev/null
+++ b/drivers/pci/controller/pcie-rzg3s-host.c
@@ -0,0 +1,1561 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCIe driver for Renesas RZ/G3S SoCs
+ *
+ * Copyright (C) 2025 Renesas Electronics Corp.
+ *
+ * Based on:
+ *  drivers/pci/controller/pcie-rcar-host.c
+ *  Copyright (C) 2009 - 2011  Paul Mundt
+ */
+
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/device/devres.h>
+#include <linux/iopoll.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+/* AXI registers. */
+#define RZG3S_PCI_REQDATA(id)			(0x80 + (id) * 0x4)
+#define RZG3S_PCI_REQRCVDAT			0x8c
+#define RZG3S_PCI_REQADR1			0x90
+#define RZG3S_PCI_REQADR1_BUS			GENMASK(31, 24)
+#define RZG3S_PCI_REQADR1_DEV			GENMASK(23, 19)
+#define RZG3S_PCI_REQADR1_FUNC			GENMASK(18, 16)
+#define RZG3S_PCI_REQADR1_REG			GENMASK(11, 0)
+#define RZG3S_PCI_REQBE				0x98
+#define RZG3S_PCI_REQBE_BYTE_EN			GENMASK(3, 0)
+#define RZG3S_PCI_REQISS			0x9c
+#define RZG3S_PCI_REQISS_REQ_ISSUE		BIT(0)
+#define RZG3S_PCI_REQISS_TR_TYPE		GENMASK(11, 8)
+#define RZG3S_PCI_REQISS_TR_TP0_RD		FIELD_PREP(RZG3S_PCI_REQISS_TR_TYPE, 0x4)
+#define RZG3S_PCI_REQISS_TR_TP0_WR		FIELD_PREP(RZG3S_PCI_REQISS_TR_TYPE, 0x5)
+#define RZG3S_PCI_REQISS_TR_TP1_RD		FIELD_PREP(RZG3S_PCI_REQISS_TR_TYPE, 0x6)
+#define RZG3S_PCI_REQISS_TR_TP1_WR		FIELD_PREP(RZG3S_PCI_REQISS_TR_TYPE, 0x7)
+#define RZG3S_PCI_REQISS_MOR_STATUS		GENMASK(18, 16)
+#define RZG3S_PCI_MSIRCVWADRL			0x100
+#define RZG3S_PCI_MSIRCVWADRL_ENA		BIT(0)
+#define RZG3S_PCI_MSIRCVWADRL_MSG_DATA_ENA	BIT(1)
+#define RZG3S_PCI_MSIRCVWADRU			0x104
+#define RZG3S_PCI_MSIRCVWMSKL			0x108
+#define RZG3S_PCI_MSIRCVWMSKU			0x10c
+#define RZG3S_PCI_PINTRCVIE			0x110
+#define RZG3S_PCI_PINTRCVIE_INTX(i)		BIT(i)
+#define RZG3S_PCI_PINTRCVIE_MSI			BIT(4)
+#define RZG3S_PCI_PINTRCVIS			0x114
+#define RZG3S_PCI_PINTRCVIS_INTX(i)		BIT(i)
+#define RZG3S_PCI_PINTRCVIS_MSI			BIT(4)
+#define RZG3S_PCI_MSGRCVIE			0x120
+#define RZG3S_PCI_MSGRCVIE_MSG_RCV		BIT(24)
+#define RZG3S_PCI_MSGRCVIS			0x124
+#define RZG3S_PCI_MSGRCVIS_MRI			BIT(24)
+#define RZG3S_PCI_PEIE0				0x200
+#define RZG3S_PCI_PEIS0				0x204
+#define RZG3S_PCI_PEIS0_DL_UPDOWN		BIT(9)
+#define RZG3S_PCI_PEIS0_RX_DLLP_PM_ENTER	BIT(12)
+#define RZG3S_PCI_PEIE1				0x208
+#define RZG3S_PCI_PEIS1				0x20c
+#define RZG3S_PCI_AMEIE				0x210
+#define RZG3S_PCI_AMEIS				0x214
+#define RZG3S_PCI_ASEIE1			0x220
+#define RZG3S_PCI_ASEIS1			0x224
+#define RZG3S_PCI_PCSTAT1			0x408
+#define RZG3S_PCI_PCSTAT1_DL_DOWN_STS		BIT(0)
+#define RZG3S_PCI_PCSTAT1_LTSSM_STATE		GENMASK(14, 10)
+#define RZG3S_PCI_PCCTRL2			0x410
+#define RZG3S_PCI_PCCTRL2_LS_CHG_REQ		BIT(0)
+#define RZG3S_PCI_PCCTRL2_LS_CHG		GENMASK(9, 8)
+#define RZG3S_PCI_PCSTAT2			0x414
+#define RZG3S_PCI_PCSTAT2_STATE_RX_DETECT	GENMASK(15, 8)
+#define RZG3S_PCI_PCSTAT2_SDRIRE		GENMASK(7, 0)
+#define RZG3S_PCI_PCSTAT2_LS_CHG_DONE		BIT(28)
+#define RZG3S_PCI_PERM				0x300
+#define RZG3S_PCI_PERM_PIPE_PHY_REG_EN		BIT(1)
+#define RZG3S_PCI_PERM_CFG_HWINIT_EN		BIT(2)
+#define RZG3S_PCI_MSIRE(id)			(0x600 + (id) * 0x10)
+#define RZG3S_PCI_MSIRE_ENA			BIT(0)
+#define RZG3S_PCI_MSIRM(id)			(0x608 + (id) * 0x10)
+#define RZG3S_PCI_MSIRS(id)			(0x60c + (id) * 0x10)
+#define RZG3S_PCI_AWBASEL(id)			(0x1000 + (id) * 0x20)
+#define RZG3S_PCI_AWBASEL_WIN_ENA		BIT(0)
+#define RZG3S_PCI_AWBASEU(id)			(0x1004 + (id) * 0x20)
+#define RZG3S_PCI_AWMASKL(id)			(0x1008 + (id) * 0x20)
+#define RZG3S_PCI_AWMASKU(id)			(0x100c + (id) * 0x20)
+#define RZG3S_PCI_ADESTL(id)			(0x1010 + (id) * 0x20)
+#define RZG3S_PCI_ADESTU(id)			(0x1014 + (id) * 0x20)
+#define RZG3S_PCI_PWBASEL(id)			(0x1100 + (id) * 0x20)
+#define RZG3S_PCI_PWBASEL_ENA			BIT(0)
+#define RZG3S_PCI_PWBASEU(id)			(0x1104 + (id) * 0x20)
+#define RZG3S_PCI_PDESTL(id)			(0x1110 + (id) * 0x20)
+#define RZG3S_PCI_PDESTU(id)			(0x1114 + (id) * 0x20)
+#define RZG3S_PCI_PWMASKL(id)			(0x1108 + (id) * 0x20)
+#define RZG3S_PCI_PWMASKU(id)			(0x110c + (id) * 0x20)
+
+/* PHY control registers. */
+#define RZG3S_PCI_PHY_XCFGD(id)			(0x2000 + (id) * 0x10)
+#define RZG3S_PCI_PHY_XCFGD_NUM			39
+#define RZG3S_PCI_PHY_XCFGA_CMN(id)		(0x2400 + (id) * 0x10)
+#define RZG3S_PCI_PHY_XCFGA_CMN_NUM		16
+#define RZG3S_PCI_PHY_XCFGA_RX(id)		(0x2500 + (id) * 0x10)
+#define RZG3S_PCI_PHY_XCFGA_RX_NUM		13
+#define RZG3S_PCI_PHY_XCFGA_TX			0x25d0
+#define RZG3S_PCI_PHY_XCFG_CTRL			0x2a20
+#define RZG3S_PCI_PHY_XCFG_CTRL_PHYREG_SEL	BIT(0)
+
+/* PCIe registers. */
+#define RZG3S_PCI_CFG_BASE			0x6000
+#define RZG3S_PCI_CFG_VID			0x0
+#define RZG3S_PCI_CFG_VID_DEVICE		GENMASK(31, 16)
+#define RZG3S_PCI_CFG_VID_VENDOR		GENMASK(15, 0)
+#define RZG3S_PCI_CFG_BNR			0x18
+#define RZG3S_PCI_CFG_BNR_SUBORDINATE_BUS	GENMASK(23, 16)
+#define RZG3S_PCI_CFG_BNR_SECONDARY_BUS		GENMASK(15, 8)
+#define RZG3S_PCI_CFG_BARMSK00L			0xa0
+#define RZG3S_PCI_CFG_BARMSK00U			0xa4
+#define RZG3S_PCI_CFG_LINKCS			0x70
+#define RZG3S_PCI_CFG_LINKCS_CUR_LS		GENMASK(19, 16)
+#define RZG3S_PCI_CFG_LINCS2			0x90
+#define RZG3S_PCI_CFG_LINCS2_TARGET_LS		GENMASK(3, 0)
+
+/* System controller registers. */
+#define RZG3S_SYS_PCIE_RST_RSM_B		0xd74
+#define RZG3S_SYS_PCIE_RST_RSM_B_MASK		BIT(0)
+
+/* Maximum number of windows. */
+#define RZG3S_MAX_WINDOWS			8
+
+/* Number of MSI interrupts per register. */
+#define RZG3S_PCI_MSI_INT_PER_REG		32
+/* The number of MSI interrupts. */
+#define RZG3S_PCI_MSI_INT_NR			(RZG3S_PCI_MSI_INT_PER_REG * 2)
+
+/**
+ * enum rzg3s_pcie_link_speed - RZ/G3S PCIe available link speeds
+ * @RZG3S_PCIE_LINK_SPEED_2_5_GTS: 2.5 GT/s
+ * @RZG3S_PCIE_LINK_SPEED_5_0_GTS: 5.0 GT/s
+ */
+enum rzg3s_pcie_link_speed {
+	RZG3S_PCIE_LINK_SPEED_2_5_GTS = 1,
+	RZG3S_PCIE_LINK_SPEED_5_0_GTS
+};
+
+/**
+ * struct rzg3s_pcie_msi - RZ/G3S PCIe MSI data structure
+ * @domain: IRQ domain
+ * @map: bitmap with the allocated MSIs
+ * @dma_addr: address of the allocated MSI window
+ * @window_base: base address of the MSI window
+ * @pages: allocated pages for MSI window mapping
+ * @map_lock: lock for bitmap with the allocated MSIs
+ */
+struct rzg3s_pcie_msi {
+	struct irq_domain *domain;
+	DECLARE_BITMAP(map, RZG3S_PCI_MSI_INT_NR);
+	dma_addr_t dma_addr;
+	dma_addr_t window_base;
+	unsigned long pages;
+	struct mutex map_lock;
+};
+
+/**
+ * struct rzg3s_pcie_host - RZ/G3S PCIe data structure
+ * @axi: base address for AXI registers
+ * @pcie: base address for PCIe registers
+ * @dev: struct device
+ * @power_resets: reset control signals that should be set after power up
+ * @cfg_resets: reset control signals that should be set after configuration
+ * @phy_init: PHY specific initialization function
+ * @sysc: SYSC regmap
+ * @intx_domain: INTx IRQ domain
+ * @devres_group_id: devres group which allows unconfiguring everything at
+ * the end of the driver remove function (or, on probe failure path, just after
+ * leaving the driver probe function); otherwise, the dev_pm_domain_detach()
+ * called in the platform driver remove function disable the PCIe clocks leaving
+ * the other action or reset function being executed (later) with clocks disabled;
+ * this leads to system being blocked and eventually restarted by watchdog
+ * @msi: MSI data structure
+ * @hw_lock: lock for access to the HW resources
+ * @intx_irqs: INTx interrupts
+ * @vendor_id: Vendor ID
+ * @device_id: Device ID
+ * @num_power_resets: the number of power_resets
+ * @num_cfg_resets: the number of cfg_resets
+ */
+struct rzg3s_pcie_host {
+	void __iomem *axi;
+	void __iomem *pcie;
+	struct device *dev;
+	struct reset_control **power_resets;
+	struct reset_control **cfg_resets;
+	int (*phy_init)(struct rzg3s_pcie_host *host);
+	struct regmap *sysc;
+	struct irq_domain *intx_domain;
+	void *devres_group_id;
+	struct rzg3s_pcie_msi msi;
+	raw_spinlock_t hw_lock;
+	int intx_irqs[PCI_NUM_INTX];
+	u32 vendor_id;
+	u32 device_id;
+	u8 num_power_resets;
+	u8 num_cfg_resets;
+};
+
+#define rzg3s_msi_to_host(_msi)	container_of(_msi, struct rzg3s_pcie_host, msi)
+
+/**
+ * enum rzg3s_pcie_cfg_access_type - RZ/G3S PCIe access type
+ * @RZG3S_PCIE_CFG_ACCESS_RD: read access
+ * @RZG3S_PCIE_CFG_ACCESS_WR: write access
+ */
+enum rzg3s_pcie_cfg_access_type {
+	RZG3S_PCIE_CFG_ACCESS_RD,
+	RZG3S_PCIE_CFG_ACCESS_WR,
+};
+
+static void rzg3s_pcie_update_bits(void __iomem *base, u32 offset, u32 mask, u32 val)
+{
+	u32 tmp;
+
+	tmp = readl(base + offset);
+	tmp &= ~mask;
+	tmp |= val & mask;
+	writel(tmp, base + offset);
+}
+
+static bool rzg3s_pcie_issue_request(struct rzg3s_pcie_host *host)
+{
+	u32 val;
+	int ret;
+
+	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_REQISS, RZG3S_PCI_REQISS_REQ_ISSUE,
+			       RZG3S_PCI_REQISS_REQ_ISSUE);
+	ret = readl_poll_timeout_atomic(host->axi + RZG3S_PCI_REQISS, val,
+					!(val & RZG3S_PCI_REQISS_REQ_ISSUE),
+					5, 2500);
+
+	return !!ret || (val & RZG3S_PCI_REQISS_MOR_STATUS);
+}
+
+static int rzg3s_pcie_config_access(struct rzg3s_pcie_host *host, struct pci_bus *bus,
+				    unsigned int devfn, int where, u32 *data,
+				    enum rzg3s_pcie_cfg_access_type access_type)
+{
+	bool type0 = pci_is_root_bus(bus->parent) ? true : false;
+	unsigned int dev, func, reg, tr_type;
+	int ret;
+
+	dev = PCI_SLOT(devfn);
+	func = PCI_FUNC(devfn);
+	reg = where & ~0x3;
+
+	/* Set the destination. */
+	writel(FIELD_PREP(RZG3S_PCI_REQADR1_BUS, bus->number) |
+	       FIELD_PREP(RZG3S_PCI_REQADR1_DEV, dev) |
+	       FIELD_PREP(RZG3S_PCI_REQADR1_FUNC, func) |
+	       FIELD_PREP(RZG3S_PCI_REQADR1_REG, reg),
+	       host->axi + RZG3S_PCI_REQADR1);
+
+	/* Set byte enable. */
+	writel(RZG3S_PCI_REQBE_BYTE_EN, host->axi + RZG3S_PCI_REQBE);
+
+	if (access_type == RZG3S_PCIE_CFG_ACCESS_RD) {
+		tr_type = type0 ? RZG3S_PCI_REQISS_TR_TP0_RD : RZG3S_PCI_REQISS_TR_TP1_RD;
+	} else {
+		tr_type = type0 ? RZG3S_PCI_REQISS_TR_TP0_WR : RZG3S_PCI_REQISS_TR_TP1_WR;
+
+		/* Set the write data.  */
+		writel(0, host->axi + RZG3S_PCI_REQDATA(0));
+		writel(0, host->axi + RZG3S_PCI_REQDATA(1));
+		writel(*data, host->axi + RZG3S_PCI_REQDATA(2));
+	}
+
+	/* Set the type of request. */
+	writel(tr_type, host->axi + RZG3S_PCI_REQISS);
+
+	/* Issue the request and wait to finish. */
+	ret = rzg3s_pcie_issue_request(host);
+	if (ret) {
+		if (access_type == RZG3S_PCIE_CFG_ACCESS_RD)
+			PCI_SET_ERROR_RESPONSE(data);
+
+		return PCIBIOS_SET_FAILED;
+	}
+
+	/* Read the data. */
+	if (access_type == RZG3S_PCIE_CFG_ACCESS_RD)
+		*data = readl(host->axi + RZG3S_PCI_REQRCVDAT);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int rzg3s_pcie_read_config_access(struct rzg3s_pcie_host *host, struct pci_bus *bus,
+					 unsigned int devfn, int where, u32 *data)
+{
+	/* Controller can't address itself. Just read the registers. */
+	if (pci_is_root_bus(bus) && !devfn) {
+		u32 reg = where & ~0x3;
+
+		*data = readl(host->pcie + reg);
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	return rzg3s_pcie_config_access(host, bus, devfn, where, data, RZG3S_PCIE_CFG_ACCESS_RD);
+}
+
+static int rzg3s_pcie_read_conf(struct pci_bus *bus, unsigned int devfn, int where, int size,
+				u32 *val)
+{
+	struct rzg3s_pcie_host *host = bus->sysdata;
+	int ret;
+
+	/*
+	 * Bus number for root complex is set to zero. Skip other requests for
+	 * vendor and device ID (where = 0x00).
+	 */
+	if (pci_is_root_bus(bus) && PCI_SLOT(devfn) && !where)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	ret = rzg3s_pcie_read_config_access(host, bus, devfn, where, val);
+	if (ret != PCIBIOS_SUCCESSFUL)
+		return ret;
+
+	if (size == 1)
+		*val = (*val >> (BITS_PER_BYTE * (where & 3))) & 0xff;
+	else if (size == 2)
+		*val = (*val >> (BITS_PER_BYTE * (where & 2))) & 0xffff;
+
+	dev_dbg(&bus->dev, "%s(): bus=%3d devfn=0x%04x where=0x%04x size=%d val=0x%08x\n",
+		__func__, bus->number, devfn, where, size, *val);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int rzg3s_pcie_write_config_access(struct rzg3s_pcie_host *host, struct pci_bus *bus,
+					  unsigned int devfn, int where, u32 data)
+{
+	/* Controller can't target itself. */
+	if (pci_is_root_bus(bus) && !devfn) {
+		u32 reg = where & ~0x3;
+
+		/* Enable access control to the CFGU. */
+		writel(RZG3S_PCI_PERM_CFG_HWINIT_EN, host->axi + RZG3S_PCI_PERM);
+		/* Write data. */
+		writel(data, host->pcie + reg);
+		/* Disable access control to the CFGU. */
+		writel(0, host->axi + RZG3S_PCI_PERM);
+
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	return rzg3s_pcie_config_access(host, bus, devfn, where, &data, RZG3S_PCIE_CFG_ACCESS_WR);
+}
+
+static int rzg3s_pcie_write_conf(struct pci_bus *bus, unsigned int devfn, int where, int size,
+				 u32 val)
+{
+	struct rzg3s_pcie_host *host = bus->sysdata;
+	u32 shift, data;
+	int ret;
+
+	ret = rzg3s_pcie_read_config_access(host, bus, devfn, where, &data);
+	if (ret != PCIBIOS_SUCCESSFUL)
+		return ret;
+
+	dev_dbg(&bus->dev, "%s(): bus=%3d devfn=0x%04x where=0x%04x size=%d val=0x%08x\n",
+		__func__, bus->number, devfn, where, size, val);
+
+	if (size == 1) {
+		shift = BITS_PER_BYTE * (where & 3);
+		data &= ~(0xff << shift);
+		data |= ((val & 0xff) << shift);
+	} else if (size == 2) {
+		shift = BITS_PER_BYTE * (where & 2);
+		data &= ~(0xffff << shift);
+		data |= ((val & 0xffff) << shift);
+	} else {
+		data = val;
+	}
+
+	return rzg3s_pcie_write_config_access(host, bus, devfn, where, data);
+}
+
+static struct pci_ops rzg3s_pcie_ops = {
+	.read	= rzg3s_pcie_read_conf,
+	.write	= rzg3s_pcie_write_conf,
+};
+
+static void rzg3s_pcie_intx_irq_handler(struct irq_desc *desc)
+{
+	struct rzg3s_pcie_host *host = irq_desc_get_handler_data(desc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	unsigned int irq = irq_desc_get_irq(desc);
+	u32 intx = irq - host->intx_irqs[0];
+
+	chained_irq_enter(chip, desc);
+	generic_handle_domain_irq(host->intx_domain, intx);
+	chained_irq_exit(chip, desc);
+}
+
+static irqreturn_t rzg3s_pcie_msi_irq(int irq, void *data)
+{
+	DECLARE_BITMAP(bitmap, RZG3S_PCI_MSI_INT_NR);
+	struct rzg3s_pcie_host *host = data;
+	struct rzg3s_pcie_msi *msi = &host->msi;
+	unsigned long bit;
+	u32 status;
+
+	status = readl(host->axi + RZG3S_PCI_PINTRCVIS);
+	if (!(status & RZG3S_PCI_PINTRCVIS_MSI))
+		return IRQ_NONE;
+
+	/* Clear the MSI. */
+	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PINTRCVIS, RZG3S_PCI_PINTRCVIS_MSI,
+			       RZG3S_PCI_PINTRCVIS_MSI);
+	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_MSGRCVIS, RZG3S_PCI_MSGRCVIS_MRI,
+			       RZG3S_PCI_MSGRCVIS_MRI);
+
+	for (u8 reg_id = 0; reg_id < RZG3S_PCI_MSI_INT_NR / RZG3S_PCI_MSI_INT_PER_REG; reg_id++) {
+		status = readl(host->axi + RZG3S_PCI_MSIRS(reg_id));
+		bitmap_write(bitmap, status, reg_id * RZG3S_PCI_MSI_INT_PER_REG,
+			     RZG3S_PCI_MSI_INT_PER_REG);
+	}
+
+	for_each_set_bit(bit, bitmap, RZG3S_PCI_MSI_INT_NR) {
+		int ret;
+
+		ret = generic_handle_domain_irq(msi->domain->parent, bit);
+		if (ret) {
+			u8 reg_bit = bit % RZG3S_PCI_MSI_INT_PER_REG;
+			u8 reg_id = bit / RZG3S_PCI_MSI_INT_PER_REG;
+
+			/* Unknown MSI, just clear it. */
+			writel(BIT(reg_bit), host->axi + RZG3S_PCI_MSIRS(reg_id));
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void rzg3s_pcie_msi_top_irq_ack(struct irq_data *d)
+{
+	irq_chip_ack_parent(d);
+}
+
+static void rzg3s_pcie_msi_top_irq_mask(struct irq_data *d)
+{
+	pci_msi_mask_irq(d);
+	irq_chip_mask_parent(d);
+}
+
+static void rzg3s_pcie_msi_top_irq_unmask(struct irq_data *d)
+{
+	pci_msi_unmask_irq(d);
+	irq_chip_unmask_parent(d);
+}
+
+static struct irq_chip rzg3s_pcie_msi_top_chip = {
+	.name		= "PCIe MSI",
+	.irq_ack	= rzg3s_pcie_msi_top_irq_ack,
+	.irq_mask	= rzg3s_pcie_msi_top_irq_mask,
+	.irq_unmask	= rzg3s_pcie_msi_top_irq_unmask,
+};
+
+static void rzg3s_pcie_msi_irq_ack(struct irq_data *d)
+{
+	struct rzg3s_pcie_msi *msi = irq_data_get_irq_chip_data(d);
+	struct rzg3s_pcie_host *host = rzg3s_msi_to_host(msi);
+	u8 reg_bit = d->hwirq % RZG3S_PCI_MSI_INT_PER_REG;
+	u8 reg_id = d->hwirq / RZG3S_PCI_MSI_INT_PER_REG;
+
+	guard(raw_spinlock_irqsave)(&host->hw_lock);
+
+	writel(BIT(reg_bit), host->axi + RZG3S_PCI_MSIRS(reg_id));
+}
+
+static void rzg3s_pcie_msi_irq_mask(struct irq_data *d)
+{
+	struct rzg3s_pcie_msi *msi = irq_data_get_irq_chip_data(d);
+	struct rzg3s_pcie_host *host = rzg3s_msi_to_host(msi);
+	u8 reg_bit = d->hwirq % RZG3S_PCI_MSI_INT_PER_REG;
+	u8 reg_id = d->hwirq / RZG3S_PCI_MSI_INT_PER_REG;
+
+	guard(raw_spinlock_irqsave)(&host->hw_lock);
+
+	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_MSIRM(reg_id), BIT(reg_bit), BIT(reg_bit));
+}
+
+static void rzg3s_pcie_msi_irq_unmask(struct irq_data *d)
+{
+	struct rzg3s_pcie_msi *msi = irq_data_get_irq_chip_data(d);
+	struct rzg3s_pcie_host *host = rzg3s_msi_to_host(msi);
+	u8 reg_bit = d->hwirq % RZG3S_PCI_MSI_INT_PER_REG;
+	u8 reg_id = d->hwirq / RZG3S_PCI_MSI_INT_PER_REG;
+
+	guard(raw_spinlock_irqsave)(&host->hw_lock);
+
+	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_MSIRM(reg_id), BIT(reg_bit), 0);
+}
+
+static void rzg3s_pcie_msi_compose_msg(struct irq_data *data, struct msi_msg *msg)
+{
+	struct rzg3s_pcie_msi *msi = irq_data_get_irq_chip_data(data);
+	struct rzg3s_pcie_host *host = rzg3s_msi_to_host(msi);
+	u32 lo, hi;
+
+	/* Enable and msg data enable bits are part of the address lo. Drop them. */
+	lo = readl(host->axi + RZG3S_PCI_MSIRCVWADRL) &
+		   ~(RZG3S_PCI_MSIRCVWADRL_ENA | RZG3S_PCI_MSIRCVWADRL_MSG_DATA_ENA);
+	hi = readl(host->axi + RZG3S_PCI_MSIRCVWADRU);
+
+	msg->address_lo = lo;
+	msg->address_hi = hi;
+	msg->data = data->hwirq;
+}
+
+static struct irq_chip rzg3s_pcie_msi_bottom_chip = {
+	.name			= "rz-pcie-msi",
+	.irq_ack		= rzg3s_pcie_msi_irq_ack,
+	.irq_mask		= rzg3s_pcie_msi_irq_mask,
+	.irq_unmask		= rzg3s_pcie_msi_irq_unmask,
+	.irq_compose_msi_msg	= rzg3s_pcie_msi_compose_msg,
+};
+
+static int rzg3s_pcie_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				       unsigned int nr_irqs, void *args)
+{
+	struct rzg3s_pcie_msi *msi = domain->host_data;
+	int hwirq;
+
+	scoped_guard(mutex, &msi->map_lock) {
+		hwirq = bitmap_find_free_region(msi->map, RZG3S_PCI_MSI_INT_NR,
+						order_base_2(nr_irqs));
+	}
+
+	if (hwirq < 0)
+		return -ENOSPC;
+
+	for (unsigned int i = 0; i < nr_irqs; i++) {
+		irq_domain_set_info(domain, virq + i, hwirq + i, &rzg3s_pcie_msi_bottom_chip,
+				    domain->host_data, handle_edge_irq, NULL, NULL);
+	}
+
+	return 0;
+}
+
+static void rzg3s_pcie_msi_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 rzg3s_pcie_msi *msi = domain->host_data;
+
+	guard(mutex)(&msi->map_lock);
+
+	bitmap_release_region(msi->map, d->hwirq, order_base_2(nr_irqs));
+}
+
+static const struct irq_domain_ops rzg3s_pcie_msi_domain_ops = {
+	.alloc	= rzg3s_pcie_msi_domain_alloc,
+	.free	= rzg3s_pcie_msi_domain_free,
+};
+
+static struct msi_domain_info rzg3s_pcie_msi_info = {
+	.flags	= MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+		  MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_NO_AFFINITY,
+	.chip	= &rzg3s_pcie_msi_top_chip,
+};
+
+static int rzg3s_pcie_msi_allocate_domains(struct rzg3s_pcie_msi *msi)
+{
+	struct rzg3s_pcie_host *host = rzg3s_msi_to_host(msi);
+	struct device *dev = host->dev;
+	struct fwnode_handle *fwnode = dev_fwnode(dev);
+	struct irq_domain *parent;
+
+	parent = irq_domain_create_linear(fwnode, RZG3S_PCI_MSI_INT_NR,
+					  &rzg3s_pcie_msi_domain_ops, msi);
+	if (!parent)
+		return dev_err_probe(dev, -ENOMEM, "failed to create IRQ domain\n");
+	irq_domain_update_bus_token(parent, DOMAIN_BUS_NEXUS);
+
+	msi->domain = pci_msi_create_irq_domain(fwnode, &rzg3s_pcie_msi_info, parent);
+	if (!msi->domain) {
+		irq_domain_remove(parent);
+		return dev_err_probe(dev, -ENOMEM, "failed to create MSI domain\n");
+	}
+
+	return 0;
+}
+
+static void rzg3s_pcie_msi_free_domains(struct rzg3s_pcie_msi *msi)
+{
+	struct irq_domain *parent = msi->domain->parent;
+
+	irq_domain_remove(msi->domain);
+	irq_domain_remove(parent);
+}
+
+static int rzg3s_pcie_msi_hw_setup(struct rzg3s_pcie_host *host)
+{
+	struct rzg3s_pcie_msi *msi = &host->msi;
+
+	/* Set MSI window size. HW will set the window to RZG3S_PCI_MSI_INT_NR * 4 bytes. */
+	writel(RZG3S_PCI_MSI_INT_NR - 1, host->axi + RZG3S_PCI_MSIRCVWMSKL);
+
+	/* Set MSI window address and enable MSI window. */
+	writel(upper_32_bits(msi->window_base), host->axi + RZG3S_PCI_MSIRCVWADRU);
+	writel(lower_32_bits(msi->window_base) | RZG3S_PCI_MSIRCVWADRL_ENA |
+	       RZG3S_PCI_MSIRCVWADRL_MSG_DATA_ENA,
+	       host->axi + RZG3S_PCI_MSIRCVWADRL);
+
+	/* Set MSI receive enable. */
+	for (u8 reg_id = 0; reg_id < RZG3S_PCI_MSI_INT_NR / RZG3S_PCI_MSI_INT_PER_REG; reg_id++)
+		writel(RZG3S_PCI_MSIRE_ENA, host->axi + RZG3S_PCI_MSIRE(reg_id));
+
+	/* Enable message receive interrupts. */
+	writel(RZG3S_PCI_MSGRCVIE_MSG_RCV, host->axi + RZG3S_PCI_MSGRCVIE);
+
+	/* Enable MSI. */
+	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PINTRCVIE, RZG3S_PCI_PINTRCVIE_MSI,
+			       RZG3S_PCI_PINTRCVIE_MSI);
+
+	return 0;
+}
+
+static int rzg3s_pcie_msi_setup(struct rzg3s_pcie_host *host)
+{
+	size_t size = RZG3S_PCI_MSI_INT_NR * sizeof(u32);
+	struct rzg3s_pcie_msi *msi = &host->msi;
+	struct device *dev = host->dev;
+	int id, ret;
+
+	msi->pages = __get_free_pages(GFP_KERNEL | GFP_DMA, 0);
+	if (!msi->pages)
+		return -ENOMEM;
+
+	msi->dma_addr = dma_map_single(dev, (void *)msi->pages, size * 2, DMA_BIDIRECTIONAL);
+	if (dma_mapping_error(dev, msi->dma_addr)) {
+		ret = -ENOMEM;
+		goto free_pages;
+	}
+
+	/*
+	 * According to the RZ/G3S HW manual (section 34.4.5.2 Setting the MSI Window)
+	 * the MSI window need to be within any AXI window. Find an AXI window to setup
+	 * the MSI window.
+	 */
+	for (id = 0; id < RZG3S_MAX_WINDOWS; id++) {
+		u64 base, basel, baseu;
+		u64 mask, maskl, masku;
+
+		basel = readl(host->axi + RZG3S_PCI_AWBASEL(id));
+		/* Skip checking this AXI window if it's not enabled. */
+		if (!(basel & RZG3S_PCI_AWBASEL_WIN_ENA))
+			continue;
+
+		baseu = readl(host->axi + RZG3S_PCI_AWBASEU(id));
+		base = baseu << 32 | basel;
+
+		maskl = readl(host->axi + RZG3S_PCI_AWMASKL(id));
+		masku = readl(host->axi + RZG3S_PCI_AWMASKU(id));
+		mask = masku << 32 | maskl;
+
+		if (msi->dma_addr < base || msi->dma_addr > base + mask)
+			continue;
+
+		break;
+	}
+
+	if (id == RZG3S_MAX_WINDOWS) {
+		ret = -EINVAL;
+		goto dma_unmap;
+	}
+
+	/* The MSI base address need to be aligned to the MSI size. */
+	msi->window_base = ALIGN(msi->dma_addr, size);
+	if (msi->window_base < msi->dma_addr) {
+		ret = -EINVAL;
+		goto dma_unmap;
+	}
+
+	rzg3s_pcie_msi_hw_setup(host);
+
+	return 0;
+
+dma_unmap:
+	dma_unmap_single(dev, msi->dma_addr, size * 2, DMA_BIDIRECTIONAL);
+free_pages:
+	free_pages(msi->pages, 0);
+	return ret;
+}
+
+static int rzg3s_pcie_msi_enable(struct rzg3s_pcie_host *host)
+{
+	struct rzg3s_pcie_msi *msi = &host->msi;
+	struct device *dev = host->dev;
+	const char *devname;
+	int irq, ret;
+
+	mutex_init(&msi->map_lock);
+
+	irq = of_irq_get_byname(dev->of_node, "intmsi_rc");
+	if (irq <= 0)
+		return dev_err_probe(dev, irq ? irq : -EINVAL, "Failed to get MSI IRQ!\n");
+
+	devname = devm_kasprintf(dev, GFP_KERNEL, "%s-msi", dev_name(dev));
+	if (!devname)
+		return -ENOMEM;
+
+	ret = rzg3s_pcie_msi_allocate_domains(msi);
+	if (ret)
+		return ret;
+
+	ret = devm_request_irq(dev, irq, rzg3s_pcie_msi_irq, 0, devname, host);
+	if (ret) {
+		dev_err_probe(dev, ret, "Failed to request IRQ: %d\n", ret);
+		goto free_domains;
+	}
+
+	ret = rzg3s_pcie_msi_setup(host);
+	if (ret) {
+		dev_err_probe(dev, ret, "Failed to setup MSI!\n");
+		goto free_domains;
+	}
+
+	return 0;
+
+free_domains:
+	rzg3s_pcie_msi_free_domains(msi);
+	return ret;
+}
+
+static void rzg3s_pcie_msi_teardown(void *data)
+{
+	size_t size = RZG3S_PCI_MSI_INT_NR * sizeof(u32);
+	struct rzg3s_pcie_host *host = data;
+	struct rzg3s_pcie_msi *msi = &host->msi;
+
+	/* Disable MSI. */
+	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PINTRCVIE, RZG3S_PCI_PINTRCVIE_MSI, 0);
+
+	/* Disable message receive interrupts. */
+	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_MSGRCVIE, RZG3S_PCI_MSGRCVIE_MSG_RCV, 0);
+
+	/* Disable MSI receive enable. */
+	for (u8 reg_id = 0; reg_id < RZG3S_PCI_MSI_INT_NR / RZG3S_PCI_MSI_INT_PER_REG; reg_id++)
+		writel(0, host->axi + RZG3S_PCI_MSIRE(reg_id));
+
+	/* Disable MSI window. */
+	writel(0, host->axi + RZG3S_PCI_MSIRCVWADRL);
+
+	/* Free unused memory. */
+	dma_unmap_single(host->dev, msi->dma_addr, size * 2, DMA_BIDIRECTIONAL);
+	free_pages(msi->pages, 0);
+
+	rzg3s_pcie_msi_free_domains(msi);
+}
+
+static void rzg3s_pcie_intx_ack(struct irq_data *d)
+{
+	struct rzg3s_pcie_host *host = irq_data_get_irq_chip_data(d);
+
+	guard(raw_spinlock_irqsave)(&host->hw_lock);
+
+	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PINTRCVIS, RZG3S_PCI_PINTRCVIS_INTX(d->hwirq),
+			       RZG3S_PCI_PINTRCVIS_INTX(d->hwirq));
+}
+
+static void rzg3s_pcie_intx_mask(struct irq_data *d)
+{
+	struct rzg3s_pcie_host *host = irq_data_get_irq_chip_data(d);
+
+	guard(raw_spinlock_irqsave)(&host->hw_lock);
+
+	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PINTRCVIE, RZG3S_PCI_PINTRCVIE_INTX(d->hwirq),
+			       0);
+}
+
+static void rzg3s_pcie_intx_unmask(struct irq_data *d)
+{
+	struct rzg3s_pcie_host *host = irq_data_get_irq_chip_data(d);
+
+	guard(raw_spinlock_irqsave)(&host->hw_lock);
+
+	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PINTRCVIE, RZG3S_PCI_PINTRCVIE_INTX(d->hwirq),
+			       RZG3S_PCI_PINTRCVIE_INTX(d->hwirq));
+}
+
+static struct irq_chip rzg3s_pcie_intx_irq_chip = {
+	.name = "PCIe INTx",
+	.irq_ack = rzg3s_pcie_intx_ack,
+	.irq_mask = rzg3s_pcie_intx_mask,
+	.irq_unmask = rzg3s_pcie_intx_unmask,
+};
+
+static int rzg3s_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
+			       irq_hw_number_t hwirq)
+{
+	irq_set_chip_and_handler(irq, &rzg3s_pcie_intx_irq_chip, handle_level_irq);
+	irq_set_chip_data(irq, domain->host_data);
+
+	return 0;
+}
+
+static const struct irq_domain_ops rzg3s_pcie_intx_domain_ops = {
+	.map = rzg3s_pcie_intx_map,
+	.xlate = irq_domain_xlate_onetwocell,
+};
+
+static int rzg3s_pcie_intx_setup(struct rzg3s_pcie_host *host)
+{
+	struct device *dev = host->dev;
+	struct device_node *np = dev->of_node;
+	struct device_node *intc_np __free(device_node) = of_get_child_by_name(np,
+								"legacy-interrupt-controller");
+	int irq_count;
+
+	if (!intc_np) {
+		dev_warn(dev, "legacy-interrupt-controller node is absent\n");
+		return 0;
+	}
+
+	irq_count = of_irq_count(intc_np);
+	if (irq_count != PCI_NUM_INTX)
+		return dev_err_probe(dev, -EINVAL,
+				     "Invalid IRQ entries in legacy-interrupt-controller\n");
+
+	for (int i = 0; i < irq_count; i++) {
+		int irq = irq_of_parse_and_map(intc_np, i);
+
+		if (!irq)
+			return dev_err_probe(dev, -EINVAL, "Failed to parse and map INTx IRQ\n");
+
+		host->intx_irqs[i] = irq;
+		irq_set_chained_handler_and_data(irq, rzg3s_pcie_intx_irq_handler, host);
+	}
+
+	host->intx_domain = irq_domain_add_linear(intc_np, irq_count,
+						  &rzg3s_pcie_intx_domain_ops, host);
+	if (!host->intx_domain)
+		return dev_err_probe(dev, -EINVAL, "Failed to add irq domain for INTX irqs\n");
+	irq_domain_update_bus_token(host->intx_domain, DOMAIN_BUS_WIRED);
+
+	return 0;
+}
+
+static void rzg3s_pcie_intx_teardown(void *data)
+{
+	struct rzg3s_pcie_host *host = data;
+
+	irq_domain_remove(host->intx_domain);
+}
+
+static int rzg3s_pcie_set_max_link_speed(struct rzg3s_pcie_host *host)
+{
+	u32 lcs, cs2, link_speed, remote_supported_link_speeds, tmp;
+	u8 ltssm_state_l0 = 0xc;
+	int ret;
+
+	/*
+	 * According to the RZ/G3S HW manual (section 34.6.3 Caution when Changing
+	 * the Speed Spontaneously) link speed change can be done only when the
+	 * link training and status state machine in the PCIe Core Link is L0.
+	 */
+	ret = readl_poll_timeout(host->axi + RZG3S_PCI_PCSTAT1, tmp,
+				 FIELD_GET(RZG3S_PCI_PCSTAT1_LTSSM_STATE, tmp) == ltssm_state_l0,
+				 100, 1000);
+	if (ret) {
+		dev_dbg(host->dev, "Could not set max link speed! LTSSM not in L0, state=%lx\n",
+			FIELD_GET(RZG3S_PCI_PCSTAT1_LTSSM_STATE, tmp));
+		return ret;
+	}
+
+	lcs = readl(host->pcie + RZG3S_PCI_CFG_LINKCS);
+	cs2 = readl(host->axi + RZG3S_PCI_PCSTAT2);
+
+	link_speed = FIELD_GET(RZG3S_PCI_CFG_LINKCS_CUR_LS, lcs);
+	remote_supported_link_speeds = FIELD_GET(RZG3S_PCI_PCSTAT2_SDRIRE, cs2);
+
+	/* Return if link is @ 5.0 GT/s or the connected device doesn't support it. */
+	if (link_speed == RZG3S_PCIE_LINK_SPEED_5_0_GTS ||
+	    !(remote_supported_link_speeds & BIT(RZG3S_PCIE_LINK_SPEED_5_0_GTS)))
+		return 0;
+
+	/* Set target Link speed to 5.0 GT/s. */
+	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_CFG_LINCS2, RZG3S_PCI_CFG_LINCS2_TARGET_LS,
+			       FIELD_PREP(RZG3S_PCI_CFG_LINCS2_TARGET_LS,
+					  RZG3S_PCIE_LINK_SPEED_5_0_GTS));
+
+	/* Request link speed change. */
+	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PCCTRL2,
+			       RZG3S_PCI_PCCTRL2_LS_CHG_REQ | RZG3S_PCI_PCCTRL2_LS_CHG,
+			       RZG3S_PCI_PCCTRL2_LS_CHG_REQ |
+			       FIELD_PREP(RZG3S_PCI_PCCTRL2_LS_CHG,
+					  RZG3S_PCIE_LINK_SPEED_5_0_GTS - 1));
+
+	ret = readl_poll_timeout(host->axi + RZG3S_PCI_PCSTAT2, cs2,
+				 (cs2 & RZG3S_PCI_PCSTAT2_LS_CHG_DONE), 100, 1000);
+
+	/*
+	 * According to the RZ/G3S HW manual (section 34.6.3 Caution when Changing
+	 * the Speed Spontaneously) the PCI_PCCTRL2_LS_CHG_REQ should be
+	 * de-asserted after checking for PCI_PCSTAT2_LS_CHG_DONE.
+	 */
+	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PCCTRL2, RZG3S_PCI_PCCTRL2_LS_CHG_REQ, 0);
+
+	return ret;
+}
+
+static void rzg3s_pcie_cfg_init(struct rzg3s_pcie_host *host)
+{
+	/* Enable access control to the CFGU. */
+	writel(RZG3S_PCI_PERM_CFG_HWINIT_EN, host->axi + RZG3S_PCI_PERM);
+
+	/* Update vendor ID and device ID. */
+	writel(FIELD_PREP(RZG3S_PCI_CFG_VID_VENDOR, host->vendor_id) |
+	       FIELD_PREP(RZG3S_PCI_CFG_VID_DEVICE, host->device_id),
+	       host->pcie + RZG3S_PCI_CFG_VID);
+
+	/* HW manual recommends to write 0xffffffff on initialization. */
+	writel(0xffffffff, host->pcie + RZG3S_PCI_CFG_BARMSK00L);
+	writel(0xffffffff, host->pcie + RZG3S_PCI_CFG_BARMSK00U);
+
+	/* Primary bus = 0, secondary bus = 1, subordinate bus = 1. */
+	writel(FIELD_PREP(RZG3S_PCI_CFG_BNR_SUBORDINATE_BUS, 1) |
+	       FIELD_PREP(RZG3S_PCI_CFG_BNR_SECONDARY_BUS, 1),
+	       host->pcie + RZG3S_PCI_CFG_BNR);
+
+	/* Disable access control to the CFGU. */
+	writel(0, host->axi + RZG3S_PCI_PERM);
+}
+
+static void rzg3s_pcie_irqs_init(struct rzg3s_pcie_host *host)
+{
+	/*
+	 * According to the HW manual of the RZ/G3S (sections corresponding
+	 * to all registers written with ~0U), the hardware ignores value
+	 * written to unused bits. Writing ~0U to these registers should be
+	 * safe.
+	 */
+
+	/* Clear the link state and PM transitions. */
+	writel(RZG3S_PCI_PEIS0_DL_UPDOWN | RZG3S_PCI_PEIS0_RX_DLLP_PM_ENTER,
+	       host->axi + RZG3S_PCI_PEIS0);
+
+	/* Disable all interrupts. */
+	writel(0, host->axi + RZG3S_PCI_PEIE0);
+
+	/* Clear all parity and ecc error interrupts. */
+	writel(~0U, host->axi + RZG3S_PCI_PEIS1);
+
+	/* Disable all parity and ecc error interrupts. */
+	writel(0, host->axi + RZG3S_PCI_PEIE1);
+
+	/* Clear all AXI master error interrupts. */
+	writel(~0U, host->axi + RZG3S_PCI_AMEIS);
+
+	/* Clear all AXI slave error interrupts. */
+	writel(~0U, host->axi + RZG3S_PCI_ASEIS1);
+
+	/* Clear all message receive interrupts. */
+	writel(~0U, host->axi + RZG3S_PCI_MSGRCVIS);
+}
+
+static int rzg3s_pcie_resets_bulk_set(int (*action)(int num, struct reset_control_bulk_data *rstcs),
+				      struct reset_control **resets, u8 num_resets)
+{
+	struct reset_control_bulk_data *data __free(kfree) =
+		kcalloc(num_resets, sizeof(*data), GFP_KERNEL);
+
+	if (!data)
+		return -ENOMEM;
+
+	for (u8 i = 0; i < num_resets; i++)
+		data[i].rstc = resets[i];
+
+	return action(num_resets, data);
+}
+
+static int
+rzg3s_pcie_resets_init(struct device *dev, struct reset_control ***resets,
+		       struct reset_control *(*action)(struct device *dev, const char *id),
+		       const char * const *reset_names, u8 num_resets)
+{
+	*resets = devm_kcalloc(dev, num_resets, sizeof(struct reset_control *), GFP_KERNEL);
+	if (!*resets)
+		return -ENOMEM;
+
+	for (u8 i = 0; i < num_resets; i++) {
+		(*resets)[i] = action(dev, reset_names[i]);
+		if (IS_ERR((*resets)[i]))
+			return PTR_ERR((*resets)[i]);
+	}
+
+	return 0;
+}
+
+static int rzg3s_pcie_resets_prepare(struct rzg3s_pcie_host *host)
+{
+	const char * const cfg_resets[] = { "rst_b", "rst_ps_b", "rst_gp_b", "rst_rsm_b", };
+	const char * const power_resets[] = { "aresetn", "rst_cfg_b", "rst_load_b", };
+	int ret;
+
+	/*
+	 * According to the RZ/G3S HW manual (section 34.5.1.2 De-asserting the Reset)
+	 * the PCIe IP needs to wait 5ms from power on to the de-assertion of reset.
+	 */
+	usleep_range(5000, 5100);
+	host->num_power_resets = ARRAY_SIZE(power_resets);
+	ret = rzg3s_pcie_resets_init(host->dev, &host->power_resets,
+				     devm_reset_control_get_exclusive_deasserted,
+				     power_resets, ARRAY_SIZE(power_resets));
+	if (ret)
+		return ret;
+
+	host->num_cfg_resets = ARRAY_SIZE(cfg_resets);
+	return rzg3s_pcie_resets_init(host->dev, &host->cfg_resets,
+				      devm_reset_control_get_exclusive,
+				      cfg_resets, ARRAY_SIZE(cfg_resets));
+}
+
+static void rzg3s_pcie_cfg_resets_action(void *data)
+{
+	struct rzg3s_pcie_host *host = data;
+
+	rzg3s_pcie_resets_bulk_set(reset_control_bulk_assert, host->cfg_resets,
+				   host->num_cfg_resets);
+}
+
+static int rzg3s_pcie_hw_init(struct rzg3s_pcie_host *host, bool probe)
+{
+	u32 val;
+	int ret;
+
+	/* Set the PCIe related registers. */
+	rzg3s_pcie_cfg_init(host);
+
+	/* Set the interrupts. */
+	rzg3s_pcie_irqs_init(host);
+
+	ret = rzg3s_pcie_resets_bulk_set(reset_control_bulk_deassert, host->cfg_resets,
+					 host->num_cfg_resets);
+	if (ret)
+		return ret;
+
+	/* Wait for link up. */
+	ret = readl_poll_timeout(host->axi + RZG3S_PCI_PCSTAT1, val,
+				 !(val & RZG3S_PCI_PCSTAT1_DL_DOWN_STS), 5000, 500000);
+	if (ret) {
+		rzg3s_pcie_resets_bulk_set(reset_control_bulk_assert, host->cfg_resets,
+					   host->num_cfg_resets);
+		return ret;
+	}
+
+	val = readl(host->axi + RZG3S_PCI_PCSTAT2);
+	dev_info(host->dev, "PCIe link status [0x%x]\n", val);
+
+	val = FIELD_GET(RZG3S_PCI_PCSTAT2_STATE_RX_DETECT, val);
+	dev_info(host->dev, "PCIe x%d: link up\n", hweight32(val));
+
+	if (probe)
+		ret = devm_add_action_or_reset(host->dev, rzg3s_pcie_cfg_resets_action, host);
+
+	return ret;
+}
+
+static void rzg3s_pcie_set_inbound_window(struct rzg3s_pcie_host *host, u64 cpu_addr,
+					  u64 pci_addr, u64 size, int id)
+{
+	/* Set CPU window base address. */
+	writel(upper_32_bits(cpu_addr), host->axi + RZG3S_PCI_ADESTU(id));
+	writel(lower_32_bits(cpu_addr), host->axi + RZG3S_PCI_ADESTL(id));
+
+	/* Set window size. */
+	writel(upper_32_bits(size), host->axi + RZG3S_PCI_AWMASKU(id));
+	writel(lower_32_bits(size), host->axi + RZG3S_PCI_AWMASKL(id));
+
+	/* Set PCIe window base address and enable the window.. */
+	writel(upper_32_bits(pci_addr), host->axi + RZG3S_PCI_AWBASEU(id));
+	writel(lower_32_bits(pci_addr) | RZG3S_PCI_AWBASEL_WIN_ENA,
+	       host->axi + RZG3S_PCI_AWBASEL(id));
+}
+
+static int rzg3s_pcie_set_inbound_windows(struct rzg3s_pcie_host *host,
+					  struct resource_entry *entry, int *index)
+{
+	u64 pci_addr = entry->res->start - entry->offset;
+	u64 cpu_addr = entry->res->start;
+	u64 cpu_end = entry->res->end;
+	u64 size_id = 0;
+	int id = *index;
+	u64 size;
+
+	while (cpu_addr < cpu_end) {
+		if (id >= RZG3S_MAX_WINDOWS)
+			return dev_err_probe(host->dev, -EINVAL,
+					     "Failed to set inbound windows!\n");
+
+		size = resource_size(entry->res) - size_id;
+
+		/*
+		 * According to the RZ/G3S HW manual (section 34.3.1.71 AXI Window Mask
+		 * (Lower) Registers) the min size is 4K.
+		 */
+		size = max(size, 4096);
+
+		/*
+		 * According the RZ/G3S HW manual (sections:
+		 * - 34.3.1.69 AXI Window Base (Lower) Registers
+		 * - 34.3.1.71 AXI Window Mask (Lower) Registers
+		 * - 34.3.1.73 AXI Destination (Lower) Registers)
+		 * the CPU addr, PCIe addr, size should be 4K alined and be a power of 2.
+		 */
+		size = ALIGN(size, 4096);
+
+		/*
+		 * According to the RZ/G3S HW manual (section 34.3.1.71 AXI Window Mask
+		 * (Lower) Registers) HW expects first 12 LSB bits to be 0xfff.
+		 * Extract 1 from size for this.
+		 */
+		size = roundup_pow_of_two(size) - 1;
+
+		cpu_addr = ALIGN(cpu_addr, 4096);
+		pci_addr = ALIGN(pci_addr, 4096);
+
+		rzg3s_pcie_set_inbound_window(host, cpu_addr, pci_addr, size, id);
+
+		pci_addr += size;
+		cpu_addr += size;
+		size_id = size;
+		id++;
+	}
+	*index = id;
+
+	return 0;
+}
+
+static int rzg3s_pcie_parse_map_dma_ranges(struct rzg3s_pcie_host *host)
+{
+	struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host);
+	struct resource_entry *entry;
+	int i = 0, ret;
+
+	resource_list_for_each_entry(entry, &bridge->dma_ranges) {
+		ret = rzg3s_pcie_set_inbound_windows(host, entry, &i);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static void rzg3s_pcie_set_outbound_window(struct rzg3s_pcie_host *host, struct resource_entry *win,
+					   int id)
+{
+	struct resource *res = win->res;
+	resource_size_t size = resource_size(res);
+	resource_size_t res_start;
+
+	if (res->flags & IORESOURCE_IO)
+		res_start = pci_pio_to_address(res->start) - win->offset;
+	else
+		res_start = res->start - win->offset;
+
+	/*
+	 * According to the RZ/G3S HW manual (section 34.3.1.75 PCIe Window Base
+	 * (Lower) Registers) the window base address need to be 4K aligned.
+	 */
+	res_start = ALIGN(res_start, 4096);
+
+	size = ALIGN(size, 4096);
+	size = roundup_pow_of_two(size) - 1;
+
+	/* Set PCIe destination. */
+	writel(upper_32_bits(res_start), host->axi + RZG3S_PCI_PDESTU(id));
+	writel(lower_32_bits(res_start), host->axi + RZG3S_PCI_PDESTL(id));
+
+	/* Set PCIe window mask. */
+	writel(upper_32_bits(size), host->axi + RZG3S_PCI_PWMASKU(id));
+	writel(lower_32_bits(size), host->axi + RZG3S_PCI_PWMASKL(id));
+
+	/* Set PCIe window base and enable the window. */
+	writel(upper_32_bits(res_start), host->axi + RZG3S_PCI_PWBASEU(id));
+	writel(lower_32_bits(res_start) | RZG3S_PCI_PWBASEL_ENA,
+	       host->axi + RZG3S_PCI_PWBASEL(id));
+}
+
+static int rzg3s_pcie_parse_map_ranges(struct rzg3s_pcie_host *host)
+{
+	struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host);
+	struct resource_entry *win;
+	int i = 0;
+
+	resource_list_for_each_entry(win, &bridge->windows) {
+		struct resource *res = win->res;
+
+		if (i >= RZG3S_MAX_WINDOWS)
+			return dev_err_probe(host->dev, -EINVAL,
+					     "Failed to set outbound windows!\n");
+
+		if (!res->flags)
+			continue;
+
+		switch (resource_type(res)) {
+		case IORESOURCE_IO:
+		case IORESOURCE_MEM:
+			rzg3s_pcie_set_outbound_window(host, win, i);
+			i++;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int rzg3s_pcie_phy_init(struct rzg3s_pcie_host *host)
+{
+	static const u32 xcfgd_settings[RZG3S_PCI_PHY_XCFGD_NUM] = {
+		[8]  = 0xe0006801, 0x007f7e30, 0x183e0000, 0x978ff500,
+		       0xec000000, 0x009f1400, 0x0000d009,
+		[17] = 0x78000000,
+		[19] = 0x00880000, 0x000005c0, 0x07000000, 0x00780920,
+		       0xc9400ce2, 0x90000c0c, 0x000c1414, 0x00005034,
+		       0x00006000, 0x00000001,
+	};
+	static const u32 xcfga_cmn_settings[RZG3S_PCI_PHY_XCFGA_CMN_NUM] = {
+		0x00000d10, 0x08310100, 0x00c21404, 0x013c0010, 0x01874440,
+		0x1a216082, 0x00103440, 0x00000080, 0x00000010, 0x0c1000c1,
+		0x1000c100, 0x0222000c, 0x00640019, 0x00a00028, 0x01d11228,
+		0x0201001d,
+	};
+	static const u32 xcfga_rx_settings[RZG3S_PCI_PHY_XCFGA_RX_NUM] = {
+		0x07d55000, 0x030e3f00, 0x00000288, 0x102c5880, 0x0000000b,
+		0x04141441, 0x00641641, 0x00d63d63, 0x00641641, 0x01970377,
+		0x00190287, 0x00190028, 0x00000028,
+	};
+
+	writel(RZG3S_PCI_PERM_PIPE_PHY_REG_EN, host->axi + RZG3S_PCI_PERM);
+
+	for (u8 i = 0; i < RZG3S_PCI_PHY_XCFGD_NUM; i++)
+		writel(xcfgd_settings[i], host->axi + RZG3S_PCI_PHY_XCFGD(i));
+
+	for (u8 i = 0; i < RZG3S_PCI_PHY_XCFGA_CMN_NUM; i++)
+		writel(xcfga_cmn_settings[i], host->axi + RZG3S_PCI_PHY_XCFGA_CMN(i));
+
+	for (u8 i = 0; i < RZG3S_PCI_PHY_XCFGA_RX_NUM; i++)
+		writel(xcfga_rx_settings[i], host->axi + RZG3S_PCI_PHY_XCFGA_RX(i));
+
+	writel(0x107, host->axi + RZG3S_PCI_PHY_XCFGA_TX);
+
+	/* Select PHY settings values. */
+	writel(RZG3S_PCI_PHY_XCFG_CTRL_PHYREG_SEL, host->axi + RZG3S_PCI_PHY_XCFG_CTRL);
+
+	writel(0, host->axi + RZG3S_PCI_PERM);
+
+	return 0;
+}
+
+static void rzg3s_pcie_pm_runtime_put(void *data)
+{
+	pm_runtime_put_sync(data);
+}
+
+static void rzg3s_pcie_sysc_signal_action(void *data)
+{
+	struct regmap *sysc = data;
+
+	/*
+	 * SYSC RST_RSM_B signal need to be asserted before turning off the power to
+	 * the PHY.
+	 */
+	regmap_update_bits(sysc, RZG3S_SYS_PCIE_RST_RSM_B, RZG3S_SYS_PCIE_RST_RSM_B_MASK,
+			   FIELD_PREP(RZG3S_SYS_PCIE_RST_RSM_B_MASK, 0));
+}
+
+static int rzg3s_pcie_setup(struct rzg3s_pcie_host *host,
+			    int (*intx_setup)(struct rzg3s_pcie_host *host),
+			    int (*msi_setup)(struct rzg3s_pcie_host *host),
+			    bool probe)
+{
+	struct device *dev = host->dev;
+	int ret;
+
+	/* Set inbound windows. */
+	ret = rzg3s_pcie_parse_map_dma_ranges(host);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to set inbound windows!\n");
+
+	/* Set outbound windows. */
+	ret = rzg3s_pcie_parse_map_ranges(host);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to set outbound windows!\n");
+
+	/* Set the PHY, if any. */
+	if (host->phy_init) {
+		ret = host->phy_init(host);
+		if (ret)
+			return dev_err_probe(dev, ret, "Failed to set the PHY!\n");
+	}
+
+	if (intx_setup) {
+		ret = intx_setup(host);
+		if (ret)
+			return dev_err_probe(dev, ret, "Failed to setup INTx\n");
+
+		if (probe) {
+			ret = devm_add_action_or_reset(dev, rzg3s_pcie_intx_teardown, host);
+			if (ret)
+				return dev_err_probe(dev, ret, "Failed to add INTx action\n");
+		}
+	}
+
+	/* Set the MSIs. */
+	if (IS_ENABLED(CONFIG_PCI_MSI)) {
+		ret = msi_setup(host);
+		if (ret)
+			return dev_err_probe(dev, ret, "Failed to setup MSIs\n");
+
+		if (probe) {
+			ret = devm_add_action_or_reset(dev, rzg3s_pcie_msi_teardown, host);
+			if (ret)
+				return dev_err_probe(dev, ret, "Failed to add MSI action\n");
+		}
+	}
+
+	/* Initialize the HW. */
+	ret = rzg3s_pcie_hw_init(host, probe);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to initialize the HW!\n");
+
+	/* Try to set maximum supported link speed (5.0 GT/s). */
+	ret = rzg3s_pcie_set_max_link_speed(host);
+	if (ret)
+		dev_info(dev, "Failed to set link speed to 5.0 GT/s\n");
+
+	return 0;
+}
+
+static int rzg3s_pcie_probe_helper(struct platform_device *pdev, void *devres_group_id)
+{
+	struct pci_host_bridge *bridge;
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct device_node *sysc_np __free(device_node) =
+		of_parse_phandle(np, "renesas,sysc", 0);
+	struct rzg3s_pcie_host *host;
+	int ret;
+
+	bridge = devm_pci_alloc_host_bridge(dev, sizeof(*host));
+	if (!bridge)
+		return -ENOMEM;
+
+	host = pci_host_bridge_priv(bridge);
+	host->dev = dev;
+	host->phy_init = device_get_match_data(dev);
+	host->devres_group_id = devres_group_id;
+	platform_set_drvdata(pdev, host);
+
+	host->axi = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(host->axi))
+		return PTR_ERR(host->axi);
+	host->pcie = host->axi + RZG3S_PCI_CFG_BASE;
+
+	ret = of_property_read_u32(np, "vendor-id", &host->vendor_id);
+	if (ret)
+		return ret;
+
+	ret = of_property_read_u32(np, "device-id", &host->device_id);
+	if (ret)
+		return ret;
+
+	host->sysc = syscon_node_to_regmap(sysc_np);
+	if (IS_ERR(host->sysc))
+		return PTR_ERR(host->sysc);
+
+	ret = regmap_update_bits(host->sysc, RZG3S_SYS_PCIE_RST_RSM_B,
+				 RZG3S_SYS_PCIE_RST_RSM_B_MASK,
+				 FIELD_PREP(RZG3S_SYS_PCIE_RST_RSM_B_MASK, 1));
+	if (ret)
+		return ret;
+
+	ret = devm_add_action_or_reset(dev, rzg3s_pcie_sysc_signal_action, host->sysc);
+	if (ret)
+		return ret;
+
+	ret = rzg3s_pcie_resets_prepare(host);
+	if (ret)
+		return ret;
+
+	ret = devm_pm_runtime_enable(dev);
+	if (ret)
+		return ret;
+
+	ret = pm_runtime_resume_and_get(dev);
+	if (ret)
+		return ret;
+
+	ret = devm_add_action_or_reset(dev, rzg3s_pcie_pm_runtime_put, dev);
+	if (ret)
+		return ret;
+
+	raw_spin_lock_init(&host->hw_lock);
+
+	ret = rzg3s_pcie_setup(host, rzg3s_pcie_intx_setup, rzg3s_pcie_msi_enable, true);
+	if (ret)
+		return ret;
+
+	bridge->sysdata = host;
+	bridge->ops = &rzg3s_pcie_ops;
+	return pci_host_probe(bridge);
+}
+
+static int rzg3s_pcie_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	void *devres_group_id;
+	int ret;
+
+	devres_group_id = devres_open_group(dev, NULL, GFP_KERNEL);
+	if (!devres_group_id)
+		return -ENOMEM;
+
+	ret = rzg3s_pcie_probe_helper(pdev, devres_group_id);
+	if (ret)
+		devres_release_group(dev, devres_group_id);
+
+	return ret;
+}
+
+static void rzg3s_pcie_remove(struct platform_device *pdev)
+{
+	struct rzg3s_pcie_host *host = dev_get_drvdata(&pdev->dev);
+	struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host);
+
+	pci_lock_rescan_remove();
+	pci_stop_root_bus(bridge->bus);
+	pci_remove_root_bus(bridge->bus);
+	pci_unlock_rescan_remove();
+
+	devres_release_group(host->dev, host->devres_group_id);
+}
+
+static int rzg3s_pcie_suspend(struct device *dev)
+{
+	struct rzg3s_pcie_host *host = dev_get_drvdata(dev);
+	struct regmap *sysc = host->sysc;
+	int ret;
+
+	ret = pm_runtime_put_sync(dev);
+	if (ret)
+		return ret;
+
+	ret = rzg3s_pcie_resets_bulk_set(reset_control_bulk_assert, host->power_resets,
+					 host->num_power_resets);
+	if (ret)
+		goto rpm_restore;
+
+	ret = rzg3s_pcie_resets_bulk_set(reset_control_bulk_assert, host->cfg_resets,
+					 host->num_cfg_resets);
+	if (ret)
+		goto power_resets_restore;
+
+	ret = regmap_update_bits(sysc, RZG3S_SYS_PCIE_RST_RSM_B, RZG3S_SYS_PCIE_RST_RSM_B_MASK,
+				 FIELD_PREP(RZG3S_SYS_PCIE_RST_RSM_B_MASK, 0));
+	if (ret)
+		goto cfg_resets_restore;
+
+	return 0;
+
+	/* Restore the previous state if any error happens. */
+cfg_resets_restore:
+	rzg3s_pcie_resets_bulk_set(reset_control_bulk_deassert, host->cfg_resets,
+				   host->num_cfg_resets);
+power_resets_restore:
+	rzg3s_pcie_resets_bulk_set(reset_control_bulk_deassert, host->power_resets,
+				   host->num_power_resets);
+rpm_restore:
+	pm_runtime_resume_and_get(dev);
+	return ret;
+}
+
+static int rzg3s_pcie_resume(struct device *dev)
+{
+	struct rzg3s_pcie_host *host = dev_get_drvdata(dev);
+	struct regmap *sysc = host->sysc;
+	int ret;
+
+	ret = regmap_update_bits(sysc, RZG3S_SYS_PCIE_RST_RSM_B, RZG3S_SYS_PCIE_RST_RSM_B_MASK,
+				 FIELD_PREP(RZG3S_SYS_PCIE_RST_RSM_B_MASK, 1));
+	if (ret)
+		return ret;
+
+	/*
+	 * According to the RZ/G3S HW manual (section 34.5.1.2 De-asserting the Reset)
+	 * the PCIe IP needs to wait 5ms from power on to the de-assertion of reset.
+	 */
+	usleep_range(5000, 5100);
+	ret = rzg3s_pcie_resets_bulk_set(reset_control_bulk_deassert, host->power_resets,
+					 host->num_power_resets);
+	if (ret)
+		goto assert_rst_rsm_b;
+
+	ret = pm_runtime_resume_and_get(dev);
+	if (ret)
+		goto assert_power_resets;
+
+	ret = rzg3s_pcie_setup(host, NULL, rzg3s_pcie_msi_hw_setup, false);
+	if (ret)
+		goto rpm_put;
+
+	return 0;
+
+	/*
+	 * If any error happens there is no way to recover the IP. Put it in the
+	 * lowest possible power state.
+	 */
+rpm_put:
+	pm_runtime_put_sync(dev);
+assert_power_resets:
+	rzg3s_pcie_resets_bulk_set(reset_control_bulk_assert, host->power_resets,
+				   host->num_power_resets);
+assert_rst_rsm_b:
+	regmap_update_bits(sysc, RZG3S_SYS_PCIE_RST_RSM_B, RZG3S_SYS_PCIE_RST_RSM_B_MASK,
+			   FIELD_PREP(RZG3S_SYS_PCIE_RST_RSM_B_MASK, 0));
+	return ret;
+}
+
+static DEFINE_NOIRQ_DEV_PM_OPS(rzg3s_pcie_pm_ops, rzg3s_pcie_suspend, rzg3s_pcie_resume);
+
+static const struct of_device_id rzg3s_pcie_of_match[] = {
+	{ .compatible = "renesas,r9a08g045s33-pcie", .data = rzg3s_pcie_phy_init },
+	{},
+};
+
+static struct platform_driver rzg3s_pcie_driver = {
+	.driver = {
+		.name = "rz-pcie-host",
+		.of_match_table = rzg3s_pcie_of_match,
+		.pm = pm_ptr(&rzg3s_pcie_pm_ops),
+	},
+	.probe = rzg3s_pcie_probe,
+	.remove = rzg3s_pcie_remove,
+};
+module_platform_driver(rzg3s_pcie_driver);
+
+MODULE_DESCRIPTION("Renesas RZ/G3S PCIe host driver");
+MODULE_AUTHOR("Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>");
+MODULE_LICENSE("GPL");
-- 
2.43.0



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

* [PATCH 6/8] arm64: dts: renesas: r9a08g045s33: Add PCIe node
  2025-04-30 10:32 [PATCH 0/8] PCI: rzg3s-host: Add PCIe driver for Renesas RZ/G3S SoC Claudiu
                   ` (4 preceding siblings ...)
  2025-04-30 10:32 ` [PATCH 5/8] PCI: rzg3s-host: Add Initial PCIe Host Driver for Renesas RZ/G3S SoC Claudiu
@ 2025-04-30 10:32 ` Claudiu
  2025-04-30 10:32 ` [PATCH 7/8] arm64: dts: renesas: rzg3s-smarc: Enable PCIe Claudiu
  2025-04-30 10:32 ` [PATCH 8/8] arm64: defconfig: Enable PCIe for the Renesas RZ/G3S SoC Claudiu
  7 siblings, 0 replies; 28+ messages in thread
From: Claudiu @ 2025-04-30 10:32 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kw, manivannan.sadhasivam, robh, krzk+dt,
	conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel
  Cc: claudiu.beznea, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, linux-arm-kernel, linux-clk, Claudiu Beznea

From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>

The RZ/G3S SoC has a variant (R9A08G045S33) which support PCIe. Add the
PCIe node.

Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
 arch/arm64/boot/dts/renesas/r9a08g045s33.dtsi | 70 +++++++++++++++++++
 1 file changed, 70 insertions(+)

diff --git a/arch/arm64/boot/dts/renesas/r9a08g045s33.dtsi b/arch/arm64/boot/dts/renesas/r9a08g045s33.dtsi
index 3351f26c7a2a..d8e1dc80e56e 100644
--- a/arch/arm64/boot/dts/renesas/r9a08g045s33.dtsi
+++ b/arch/arm64/boot/dts/renesas/r9a08g045s33.dtsi
@@ -12,3 +12,73 @@
 / {
 	compatible = "renesas,r9a08g045s33", "renesas,r9a08g045";
 };
+
+&soc {
+	pcie: pcie@11e40000 {
+		compatible = "renesas,r9a08g045s33-pcie";
+		reg = <0 0x11e40000 0 0x10000>;
+		ranges = <0x03000000 0 0x30000000 0 0x30000000 0 0x8000000>;
+		dma-ranges = <0x42000000 0 0x48000000 0 0x48000000 0 0x38000000>;
+		bus-range = <0x0 0xff>;
+		clocks = <&cpg CPG_MOD R9A08G045_PCI_ACLK>,
+			 <&cpg CPG_MOD R9A08G045_PCI_CLKL1PM>;
+		clock-names = "aclk", "clkl1pm";
+		resets = <&cpg R9A08G045_PCI_ARESETN>,
+			 <&cpg R9A08G045_PCI_RST_B>,
+			 <&cpg R9A08G045_PCI_RST_GP_B>,
+			 <&cpg R9A08G045_PCI_RST_PS_B>,
+			 <&cpg R9A08G045_PCI_RST_RSM_B>,
+			 <&cpg R9A08G045_PCI_RST_CFG_B>,
+			 <&cpg R9A08G045_PCI_RST_LOAD_B>;
+		reset-names = "aresetn", "rst_b", "rst_gp_b", "rst_ps_b",
+			      "rst_rsm_b", "rst_cfg_b", "rst_load_b";
+		interrupts = <GIC_SPI 395 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 396 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 397 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 398 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 399 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 400 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 401 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 402 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 403 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 404 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 405 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 406 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 407 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 408 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 409 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 410 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-names = "int_serr", "int_serr_cor", "int_serr_nonfatal",
+				  "int_serr_fatal", "axi_err_int", "inta_rc",
+				  "intb_rc", "intc_rc", "intd_rc",
+				  "intmsi_rc", "int_link_bandwidth", "int_pm_pme",
+				  "dma_int", "pcie_evt_int", "msg_int",
+				  "int_all";
+		#interrupt-cells = <1>;
+		interrupt-map-mask = <0 0 0 7>;
+		interrupt-map = <0 0 0 1 &pcie_intx 0>, /* INT A */
+				<0 0 0 2 &pcie_intx 1>, /* INT B */
+				<0 0 0 3 &pcie_intx 2>, /* INT C */
+				<0 0 0 4 &pcie_intx 3>; /* INT D */
+		device_type = "pci";
+		num-lanes = <1>;
+		#address-cells = <3>;
+		#size-cells = <2>;
+		power-domains = <&cpg>;
+		renesas,sysc = <&sysc>;
+		vendor-id = <0x1912>;
+		device-id = <0x0033>;
+		status = "disabled";
+
+		pcie_intx: legacy-interrupt-controller {
+			interrupt-controller;
+			#interrupt-cells = <1>;
+			#address-cells = <0>;
+			interrupt-parent = <&gic>;
+			interrupts = <GIC_SPI 400 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 401 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 402 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 403 IRQ_TYPE_LEVEL_HIGH>;
+		};
+	};
+};
-- 
2.43.0



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

* [PATCH 7/8] arm64: dts: renesas: rzg3s-smarc: Enable PCIe
  2025-04-30 10:32 [PATCH 0/8] PCI: rzg3s-host: Add PCIe driver for Renesas RZ/G3S SoC Claudiu
                   ` (5 preceding siblings ...)
  2025-04-30 10:32 ` [PATCH 6/8] arm64: dts: renesas: r9a08g045s33: Add PCIe node Claudiu
@ 2025-04-30 10:32 ` Claudiu
  2025-04-30 10:32 ` [PATCH 8/8] arm64: defconfig: Enable PCIe for the Renesas RZ/G3S SoC Claudiu
  7 siblings, 0 replies; 28+ messages in thread
From: Claudiu @ 2025-04-30 10:32 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kw, manivannan.sadhasivam, robh, krzk+dt,
	conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel
  Cc: claudiu.beznea, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, linux-arm-kernel, linux-clk, Claudiu Beznea

From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>

The RZ Smarc Carrier-II board has PCIe headers mounted on it. Enable PCIe
support.

Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
 arch/arm64/boot/dts/renesas/rzg3s-smarc.dtsi | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/arch/arm64/boot/dts/renesas/rzg3s-smarc.dtsi b/arch/arm64/boot/dts/renesas/rzg3s-smarc.dtsi
index 5e044a4d0234..6e9e78aca0b0 100644
--- a/arch/arm64/boot/dts/renesas/rzg3s-smarc.dtsi
+++ b/arch/arm64/boot/dts/renesas/rzg3s-smarc.dtsi
@@ -132,6 +132,12 @@ power-monitor@44 {
 	};
 };
 
+&pcie {
+	pinctrl-0 = <&pcie_pins>;
+	pinctrl-names = "default";
+	status = "okay";
+};
+
 &pinctrl {
 	audio_clock_pins: audio-clock {
 		pins = "AUDIO_CLK1", "AUDIO_CLK2";
@@ -159,6 +165,11 @@ key-3-gpio-hog {
 		line-name = "key-3-gpio-irq";
 	};
 
+	pcie_pins: pcie {
+		pinmux = <RZG2L_PORT_PINMUX(13, 2, 2)>, /* PCIE_RST_OUT_B */
+			 <RZG2L_PORT_PINMUX(13, 3, 2)>; /* PCIE_CLKREQ_B */
+	};
+
 	scif0_pins: scif0 {
 		pinmux = <RZG2L_PORT_PINMUX(6, 3, 1)>, /* RXD */
 			 <RZG2L_PORT_PINMUX(6, 4, 1)>; /* TXD */
-- 
2.43.0



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

* [PATCH 8/8] arm64: defconfig: Enable PCIe for the Renesas RZ/G3S SoC
  2025-04-30 10:32 [PATCH 0/8] PCI: rzg3s-host: Add PCIe driver for Renesas RZ/G3S SoC Claudiu
                   ` (6 preceding siblings ...)
  2025-04-30 10:32 ` [PATCH 7/8] arm64: dts: renesas: rzg3s-smarc: Enable PCIe Claudiu
@ 2025-04-30 10:32 ` Claudiu
  7 siblings, 0 replies; 28+ messages in thread
From: Claudiu @ 2025-04-30 10:32 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kw, manivannan.sadhasivam, robh, krzk+dt,
	conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel
  Cc: claudiu.beznea, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, linux-arm-kernel, linux-clk, Claudiu Beznea

From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>

Enable PCIe for the Renesas RZ/G3S SoC.

Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
 arch/arm64/configs/defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index a92c4122a8b9..b36a96777018 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -226,6 +226,7 @@ CONFIG_PCIE_MEDIATEK_GEN3=m
 CONFIG_PCI_TEGRA=y
 CONFIG_PCIE_RCAR_HOST=y
 CONFIG_PCIE_RCAR_EP=y
+CONFIG_PCIE_RENESAS_RZG3S_HOST=m
 CONFIG_PCIE_ROCKCHIP_HOST=m
 CONFIG_PCI_XGENE=y
 CONFIG_PCI_IMX6_HOST=y
-- 
2.43.0



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

* Re: [PATCH 1/8] soc: renesas: r9a08g045-sysc: Add max reg offset
  2025-04-30 10:32 ` [PATCH 1/8] soc: renesas: r9a08g045-sysc: Add max reg offset Claudiu
@ 2025-05-01  9:26   ` kernel test robot
  2025-05-01 10:32   ` kernel test robot
  2025-05-01 16:12   ` kernel test robot
  2 siblings, 0 replies; 28+ messages in thread
From: kernel test robot @ 2025-05-01  9:26 UTC (permalink / raw)
  To: Claudiu, bhelgaas, lpieralisi, kw, manivannan.sadhasivam, robh,
	krzk+dt, conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel
  Cc: oe-kbuild-all, claudiu.beznea, linux-pci, linux-renesas-soc,
	devicetree, linux-kernel, linux-arm-kernel, linux-clk,
	Claudiu Beznea

Hi Claudiu,

kernel test robot noticed the following build warnings:

[auto build test WARNING on pci/next]
[also build test WARNING on pci/for-linus geert-renesas-devel/next geert-renesas-drivers/renesas-clk robh/for-next arm64/for-next/core linus/master v6.15-rc4 next-20250430]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Claudiu/soc-renesas-r9a08g045-sysc-Add-max-reg-offset/20250430-183951
base:   https://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git next
patch link:    https://lore.kernel.org/r/20250430103236.3511989-2-claudiu.beznea.uj%40bp.renesas.com
patch subject: [PATCH 1/8] soc: renesas: r9a08g045-sysc: Add max reg offset
config: arm64-randconfig-001-20250501 (https://download.01.org/0day-ci/archive/20250501/202505011714.p63Hj7Fe-lkp@intel.com/config)
compiler: aarch64-linux-gcc (GCC) 9.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250501/202505011714.p63Hj7Fe-lkp@intel.com/reproduce)

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

All warnings (new ones prefixed by >>):

   drivers/soc/renesas/r9a08g045-sysc.c:23:3: error: 'const struct rz_sysc_init_data' has no member named 'max_register_offset'
      23 |  .max_register_offset = 0xe28,
         |   ^~~~~~~~~~~~~~~~~~~
>> drivers/soc/renesas/r9a08g045-sysc.c:23:25: warning: excess elements in struct initializer
      23 |  .max_register_offset = 0xe28,
         |                         ^~~~~
   drivers/soc/renesas/r9a08g045-sysc.c:23:25: note: (near initialization for 'rzg3s_sysc_init_data')


vim +23 drivers/soc/renesas/r9a08g045-sysc.c

    20	
    21	const struct rz_sysc_init_data rzg3s_sysc_init_data __initconst = {
    22		.soc_id_init_data = &rzg3s_sysc_soc_id_init_data,
  > 23		.max_register_offset = 0xe28,

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


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

* Re: [PATCH 1/8] soc: renesas: r9a08g045-sysc: Add max reg offset
  2025-04-30 10:32 ` [PATCH 1/8] soc: renesas: r9a08g045-sysc: Add max reg offset Claudiu
  2025-05-01  9:26   ` kernel test robot
@ 2025-05-01 10:32   ` kernel test robot
  2025-05-01 16:12   ` kernel test robot
  2 siblings, 0 replies; 28+ messages in thread
From: kernel test robot @ 2025-05-01 10:32 UTC (permalink / raw)
  To: Claudiu, bhelgaas, lpieralisi, kw, manivannan.sadhasivam, robh,
	krzk+dt, conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel
  Cc: llvm, oe-kbuild-all, claudiu.beznea, linux-pci, linux-renesas-soc,
	devicetree, linux-kernel, linux-arm-kernel, linux-clk,
	Claudiu Beznea

Hi Claudiu,

kernel test robot noticed the following build errors:

[auto build test ERROR on pci/next]
[also build test ERROR on pci/for-linus geert-renesas-devel/next geert-renesas-drivers/renesas-clk robh/for-next arm64/for-next/core linus/master v6.15-rc4 next-20250430]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Claudiu/soc-renesas-r9a08g045-sysc-Add-max-reg-offset/20250430-183951
base:   https://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git next
patch link:    https://lore.kernel.org/r/20250430103236.3511989-2-claudiu.beznea.uj%40bp.renesas.com
patch subject: [PATCH 1/8] soc: renesas: r9a08g045-sysc: Add max reg offset
config: arm64-randconfig-003-20250501 (https://download.01.org/0day-ci/archive/20250501/202505011843.2uKgWFN0-lkp@intel.com/config)
compiler: clang version 21.0.0git (https://github.com/llvm/llvm-project f819f46284f2a79790038e1f6649172789734ae8)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250501/202505011843.2uKgWFN0-lkp@intel.com/reproduce)

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

All errors (new ones prefixed by >>):

>> drivers/soc/renesas/r9a08g045-sysc.c:23:3: error: field designator 'max_register_offset' does not refer to any field in type 'const struct rz_sysc_init_data'
      23 |         .max_register_offset = 0xe28,
         |         ~^~~~~~~~~~~~~~~~~~~~~~~~~~~
   1 error generated.


vim +23 drivers/soc/renesas/r9a08g045-sysc.c

    20	
    21	const struct rz_sysc_init_data rzg3s_sysc_init_data __initconst = {
    22		.soc_id_init_data = &rzg3s_sysc_soc_id_init_data,
  > 23		.max_register_offset = 0xe28,

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


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

* Re: [PATCH 1/8] soc: renesas: r9a08g045-sysc: Add max reg offset
  2025-04-30 10:32 ` [PATCH 1/8] soc: renesas: r9a08g045-sysc: Add max reg offset Claudiu
  2025-05-01  9:26   ` kernel test robot
  2025-05-01 10:32   ` kernel test robot
@ 2025-05-01 16:12   ` kernel test robot
  2 siblings, 0 replies; 28+ messages in thread
From: kernel test robot @ 2025-05-01 16:12 UTC (permalink / raw)
  To: Claudiu, bhelgaas, lpieralisi, kw, manivannan.sadhasivam, robh,
	krzk+dt, conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel
  Cc: oe-kbuild-all, claudiu.beznea, linux-pci, linux-renesas-soc,
	devicetree, linux-kernel, linux-arm-kernel, linux-clk,
	Claudiu Beznea

Hi Claudiu,

kernel test robot noticed the following build errors:

[auto build test ERROR on pci/next]
[also build test ERROR on pci/for-linus geert-renesas-devel/next geert-renesas-drivers/renesas-clk robh/for-next arm64/for-next/core linus/master v6.15-rc4 next-20250501]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Claudiu/soc-renesas-r9a08g045-sysc-Add-max-reg-offset/20250430-183951
base:   https://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git next
patch link:    https://lore.kernel.org/r/20250430103236.3511989-2-claudiu.beznea.uj%40bp.renesas.com
patch subject: [PATCH 1/8] soc: renesas: r9a08g045-sysc: Add max reg offset
config: arm64-randconfig-001-20250501 (https://download.01.org/0day-ci/archive/20250501/202505012344.lIxS3e4X-lkp@intel.com/config)
compiler: aarch64-linux-gcc (GCC) 9.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250501/202505012344.lIxS3e4X-lkp@intel.com/reproduce)

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

All errors (new ones prefixed by >>):

>> drivers/soc/renesas/r9a08g045-sysc.c:23:3: error: 'const struct rz_sysc_init_data' has no member named 'max_register_offset'
      23 |  .max_register_offset = 0xe28,
         |   ^~~~~~~~~~~~~~~~~~~
   drivers/soc/renesas/r9a08g045-sysc.c:23:25: warning: excess elements in struct initializer
      23 |  .max_register_offset = 0xe28,
         |                         ^~~~~
   drivers/soc/renesas/r9a08g045-sysc.c:23:25: note: (near initialization for 'rzg3s_sysc_init_data')


vim +23 drivers/soc/renesas/r9a08g045-sysc.c

    20	
    21	const struct rz_sysc_init_data rzg3s_sysc_init_data __initconst = {
    22		.soc_id_init_data = &rzg3s_sysc_soc_id_init_data,
  > 23		.max_register_offset = 0xe28,

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


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

* Re: [PATCH 5/8] PCI: rzg3s-host: Add Initial PCIe Host Driver for Renesas RZ/G3S SoC
  2025-04-30 10:32 ` [PATCH 5/8] PCI: rzg3s-host: Add Initial PCIe Host Driver for Renesas RZ/G3S SoC Claudiu
@ 2025-05-01 20:12   ` Bjorn Helgaas
  2025-05-05 11:26     ` Claudiu Beznea
  2025-05-09 10:51   ` Philipp Zabel
  2025-05-09 20:49   ` Rob Herring
  2 siblings, 1 reply; 28+ messages in thread
From: Bjorn Helgaas @ 2025-05-01 20:12 UTC (permalink / raw)
  To: Claudiu
  Cc: bhelgaas, lpieralisi, kw, manivannan.sadhasivam, robh, krzk+dt,
	conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, linux-arm-kernel, linux-clk, Claudiu Beznea

On Wed, Apr 30, 2025 at 01:32:33PM +0300, Claudiu wrote:
> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> 
> The Renesas RZ/G3S features a PCIe IP that complies with the PCI Express
> Base Specification 4.0 and supports speeds of up to 5 GT/s. It functions
> only as a root complex, with a single-lane (x1) configuration. The
> controller includes Type 1 configuration registers, as well as IP
> specific registers (called AXI registers) required for various adjustments.
> 
> Other Renesas RZ SoCs (e.g., RZ/G3E, RZ/V2H) share the same AXI registers
> but have both Root Complex and Endpoint capabilities. As a result, the PCIe
> host driver can be reused for these variants with minimal adjustments.

I guess this current driver only supports RZ/GS3 in Root Complex mode?
If so, I don't think this paragraph is necessary or really relevant.

> +++ b/drivers/pci/controller/pcie-rzg3s-host.c
> @@ -0,0 +1,1561 @@

I can't figure out the line width you're using.  Generally code in
drivers/pci/ is formatted to fit in 80 columns.  Much of this file is
formatted for that, but there are many cases that seem to use 90 or
100 columns.

For single-line comments that are not a sentence or are a single
sentence, it's typical to omit the period at end.

> +static void rzg3s_pcie_update_bits(void __iomem *base, u32 offset, u32 mask, u32 val)
> +{
> +	u32 tmp;
> +
> +	tmp = readl(base + offset);
> +	tmp &= ~mask;
> +	tmp |= val & mask;
> +	writel(tmp, base + offset);
> +}

Nothing rzg3s-specific here.

I think u32p_replace_bits() (include/linux/bitfield.h) is basically this.

> +static int rzg3s_pcie_config_access(struct rzg3s_pcie_host *host, struct pci_bus *bus,
> +				    unsigned int devfn, int where, u32 *data,
> +				    enum rzg3s_pcie_cfg_access_type access_type)
> +{
> +	bool type0 = pci_is_root_bus(bus->parent) ? true : false;
> +	unsigned int dev, func, reg, tr_type;
> +	int ret;
> +
> +	dev = PCI_SLOT(devfn);
> +	func = PCI_FUNC(devfn);
> +	reg = where & ~0x3;

This obviously has to be serialized with other config accesses.  Can
you add a comment about what provides that serialization?  I assume
it's probably pci_lock via pci_lock_config()?

> +	/* Set the destination. */
> +	writel(FIELD_PREP(RZG3S_PCI_REQADR1_BUS, bus->number) |
> +	       FIELD_PREP(RZG3S_PCI_REQADR1_DEV, dev) |
> +	       FIELD_PREP(RZG3S_PCI_REQADR1_FUNC, func) |
> +	       FIELD_PREP(RZG3S_PCI_REQADR1_REG, reg),
> +	       host->axi + RZG3S_PCI_REQADR1);
> +
> +	/* Set byte enable. */
> +	writel(RZG3S_PCI_REQBE_BYTE_EN, host->axi + RZG3S_PCI_REQBE);
> +
> +	if (access_type == RZG3S_PCIE_CFG_ACCESS_RD) {
> +		tr_type = type0 ? RZG3S_PCI_REQISS_TR_TP0_RD : RZG3S_PCI_REQISS_TR_TP1_RD;
> +	} else {
> +		tr_type = type0 ? RZG3S_PCI_REQISS_TR_TP0_WR : RZG3S_PCI_REQISS_TR_TP1_WR;
> +
> +		/* Set the write data.  */
> +		writel(0, host->axi + RZG3S_PCI_REQDATA(0));
> +		writel(0, host->axi + RZG3S_PCI_REQDATA(1));
> +		writel(*data, host->axi + RZG3S_PCI_REQDATA(2));
> +	}
> +
> +	/* Set the type of request. */
> +	writel(tr_type, host->axi + RZG3S_PCI_REQISS);
> +
> +	/* Issue the request and wait to finish. */
> +	ret = rzg3s_pcie_issue_request(host);
> +	if (ret) {
> +		if (access_type == RZG3S_PCIE_CFG_ACCESS_RD)
> +			PCI_SET_ERROR_RESPONSE(data);
> +
> +		return PCIBIOS_SET_FAILED;
> +	}
> +
> +	/* Read the data. */
> +	if (access_type == RZG3S_PCIE_CFG_ACCESS_RD)
> +		*data = readl(host->axi + RZG3S_PCI_REQRCVDAT);
> +
> +	return PCIBIOS_SUCCESSFUL;
> +}

> +static int rzg3s_pcie_read_conf(struct pci_bus *bus, unsigned int devfn, int where, int size,
> +				u32 *val)
> +{
> +	struct rzg3s_pcie_host *host = bus->sysdata;
> +	int ret;
> +
> +	/*
> +	 * Bus number for root complex is set to zero. 

A root bus is not always bus number zero.  It sounds like the bus
number is configurable on this hardware?  I don't see a dependency on
the bus number, so I'm wondering why this sentence is here.

> +	   ... Skip other requests for
> +	 * vendor and device ID (where = 0x00).

Why do we need to avoid config reads for root bus devices other than
00.0?  Does the hardware not fabricate ~0 read data when a PCI error
occurs?

It looks like you allow reads for other root bus devices at non-zero
offsets?  E.g., we could read the Device ID at offset 0x02?

> +	 */
> +	if (pci_is_root_bus(bus) && PCI_SLOT(devfn) && !where)
> +		return PCIBIOS_DEVICE_NOT_FOUND;
> +
> +	ret = rzg3s_pcie_read_config_access(host, bus, devfn, where, val);
> +	if (ret != PCIBIOS_SUCCESSFUL)
> +		return ret;
> +
> +	if (size == 1)
> +		*val = (*val >> (BITS_PER_BYTE * (where & 3))) & 0xff;
> +	else if (size == 2)
> +		*val = (*val >> (BITS_PER_BYTE * (where & 2))) & 0xffff;
> +
> +	dev_dbg(&bus->dev, "%s(): bus=%3d devfn=0x%04x where=0x%04x size=%d val=0x%08x\n",
> +		__func__, bus->number, devfn, where, size, *val);
> +
> +	return PCIBIOS_SUCCESSFUL;
> +}

> +static int rzg3s_pcie_write_conf(struct pci_bus *bus, unsigned int devfn, int where, int size,
> +				 u32 val)
> +{
> +	struct rzg3s_pcie_host *host = bus->sysdata;
> +	u32 shift, data;
> +	int ret;
> +
> +	ret = rzg3s_pcie_read_config_access(host, bus, devfn, where, &data);
> +	if (ret != PCIBIOS_SUCCESSFUL)
> +		return ret;

I guess this controller can't do an atomic config write, so you have
to do a read/modify/write?  That's broken in some corner cases, e.g.,
it can corrupt RW1C bits in adjacent registers.

We emit a warning about this in pci_generic_config_write32().  I don't
think we've been consistent about similar warnings in driver-specific
code like this, but I would like to have a dmesg hint so that if we
ever trip over this, it might be easier to debug.

> +	dev_dbg(&bus->dev, "%s(): bus=%3d devfn=0x%04x where=0x%04x size=%d val=0x%08x\n",
> +		__func__, bus->number, devfn, where, size, val);
> +
> +	if (size == 1) {
> +		shift = BITS_PER_BYTE * (where & 3);
> +		data &= ~(0xff << shift);
> +		data |= ((val & 0xff) << shift);
> +	} else if (size == 2) {
> +		shift = BITS_PER_BYTE * (where & 2);
> +		data &= ~(0xffff << shift);
> +		data |= ((val & 0xffff) << shift);
> +	} else {
> +		data = val;
> +	}
> +
> +	return rzg3s_pcie_write_config_access(host, bus, devfn, where, data);
> +}

> +static struct irq_chip rzg3s_pcie_msi_bottom_chip = {
> +	.name			= "rz-pcie-msi",
> +	.irq_ack		= rzg3s_pcie_msi_irq_ack,
> +	.irq_mask		= rzg3s_pcie_msi_irq_mask,
> +	.irq_unmask		= rzg3s_pcie_msi_irq_unmask,
> +	.irq_compose_msi_msg	= rzg3s_pcie_msi_compose_msg,

s/msi_compose_msg/compose_msi_msg/
to match .irq_compose_msi_msg, which doesn't quite match the others
but makes it easier to grep and compare .irq_compose_msi_msg
implementations.

> +	 * According to the RZ/G3S HW manual (section 34.4.5.2 Setting the MSI Window)
> +	 * the MSI window need to be within any AXI window. Find an AXI window to setup
> +	 * the MSI window.

There may be several revisions of the RZ/G3S spec and they might not
preserve section numbers, so it might be worth including "r1.0" or
whatever to make the citation unambiguous.

> +static struct irq_chip rzg3s_pcie_intx_irq_chip = {
> +	.name = "PCIe INTx",
> +	.irq_ack = rzg3s_pcie_intx_ack,
> +	.irq_mask = rzg3s_pcie_intx_mask,
> +	.irq_unmask = rzg3s_pcie_intx_unmask,

Would prefer *_irq_ack, *_irq_mask, *_irq_unmask similar to MSI ops
above.

> +static int rzg3s_pcie_intx_setup(struct rzg3s_pcie_host *host)
> +{
> +	struct device *dev = host->dev;
> +	struct device_node *np = dev->of_node;
> +	struct device_node *intc_np __free(device_node) = of_get_child_by_name(np,
> +								"legacy-interrupt-controller");
> +	int irq_count;
> +
> +	if (!intc_np) {
> +		dev_warn(dev, "legacy-interrupt-controller node is absent\n");
> +		return 0;
> +	}
> +
> +	irq_count = of_irq_count(intc_np);
> +	if (irq_count != PCI_NUM_INTX)
> +		return dev_err_probe(dev, -EINVAL,
> +				     "Invalid IRQ entries in legacy-interrupt-controller\n");

We now know the value of irq_count, so why not just use PCI_NUM_INTX
below?

> +	for (int i = 0; i < irq_count; i++) {
> +		int irq = irq_of_parse_and_map(intc_np, i);
> +
> +		if (!irq)
> +			return dev_err_probe(dev, -EINVAL, "Failed to parse and map INTx IRQ\n");

Could say which one?

> +
> +		host->intx_irqs[i] = irq;
> +		irq_set_chained_handler_and_data(irq, rzg3s_pcie_intx_irq_handler, host);
> +	}
> +
> +	host->intx_domain = irq_domain_add_linear(intc_np, irq_count,
> +						  &rzg3s_pcie_intx_domain_ops, host);
> +	if (!host->intx_domain)
> +		return dev_err_probe(dev, -EINVAL, "Failed to add irq domain for INTX irqs\n");

s/INTX/INTx/ to match above.
s/irqs/IRQs/ to match above.

> +static int rzg3s_pcie_set_max_link_speed(struct rzg3s_pcie_host *host)
> +{
> +	u32 lcs, cs2, link_speed, remote_supported_link_speeds, tmp;
> +	u8 ltssm_state_l0 = 0xc;
> +	int ret;
> +
> +	/*
> +	 * According to the RZ/G3S HW manual (section 34.6.3 Caution when Changing
> +	 * the Speed Spontaneously) link speed change can be done only when the
> +	 * link training and status state machine in the PCIe Core Link is L0.
> +	 */
> +	ret = readl_poll_timeout(host->axi + RZG3S_PCI_PCSTAT1, tmp,
> +				 FIELD_GET(RZG3S_PCI_PCSTAT1_LTSSM_STATE, tmp) == ltssm_state_l0,
> +				 100, 1000);

Where do these timeout values come from?  Is there or should there be
a #define for them?

> +static void rzg3s_pcie_cfg_init(struct rzg3s_pcie_host *host)
> +{
> +	/* Enable access control to the CFGU. */
> +	writel(RZG3S_PCI_PERM_CFG_HWINIT_EN, host->axi + RZG3S_PCI_PERM);
> +
> +	/* Update vendor ID and device ID. */
> +	writel(FIELD_PREP(RZG3S_PCI_CFG_VID_VENDOR, host->vendor_id) |
> +	       FIELD_PREP(RZG3S_PCI_CFG_VID_DEVICE, host->device_id),
> +	       host->pcie + RZG3S_PCI_CFG_VID);
> +
> +	/* HW manual recommends to write 0xffffffff on initialization. */
> +	writel(0xffffffff, host->pcie + RZG3S_PCI_CFG_BARMSK00L);
> +	writel(0xffffffff, host->pcie + RZG3S_PCI_CFG_BARMSK00U);
> +
> +	/* Primary bus = 0, secondary bus = 1, subordinate bus = 1. */
> +	writel(FIELD_PREP(RZG3S_PCI_CFG_BNR_SUBORDINATE_BUS, 1) |
> +	       FIELD_PREP(RZG3S_PCI_CFG_BNR_SECONDARY_BUS, 1),
> +	       host->pcie + RZG3S_PCI_CFG_BNR);

I assume this configures a Root Port.  Is the primary bus hardwired to
zero?  There's no PCIe requirement that a root bus be bus 0.  This
looks like it limits the subordinate bus unnecessarily.  The PCI core
can program the secondary and subordinate bus numbers to accommodate
the attached hierarchy.

> +static void rzg3s_pcie_irqs_init(struct rzg3s_pcie_host *host)

This and many of the following functions have names that don't
correspond to anything in other drivers, which makes it harder to
transfer knowledge between the drivers.  If you can find a pattern
somewhere to follow, it will make it easier for others to read the
driver.

> +static int rzg3s_pcie_hw_init(struct rzg3s_pcie_host *host, bool probe)
> +{
> +	u32 val;
> +	int ret;
> +
> +	/* Set the PCIe related registers. */
> +	rzg3s_pcie_cfg_init(host);
> +
> +	/* Set the interrupts. */
> +	rzg3s_pcie_irqs_init(host);
> +
> +	ret = rzg3s_pcie_resets_bulk_set(reset_control_bulk_deassert, host->cfg_resets,
> +					 host->num_cfg_resets);
> +	if (ret)
> +		return ret;
> +
> +	/* Wait for link up. */
> +	ret = readl_poll_timeout(host->axi + RZG3S_PCI_PCSTAT1, val,
> +				 !(val & RZG3S_PCI_PCSTAT1_DL_DOWN_STS), 5000, 500000);

Ditto (source of timeout values).

> +static int rzg3s_pcie_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	void *devres_group_id;
> +	int ret;
> +
> +	devres_group_id = devres_open_group(dev, NULL, GFP_KERNEL);
> +	if (!devres_group_id)
> +		return -ENOMEM;

What's the benefit of using devres_open_group()?  No other PCI
controller drivers use it.

> +	ret = rzg3s_pcie_probe_helper(pdev, devres_group_id);

This is the only driver that uses a *_pcie_probe_helper() function.

> +static DEFINE_NOIRQ_DEV_PM_OPS(rzg3s_pcie_pm_ops, rzg3s_pcie_suspend, rzg3s_pcie_resume);

Adding "_noirq" to the function names would be a good hint.

Only a dozen drivers in the whole tree use DEFINE_NOIRQ_DEV_PM_OPS(),
which makes me wonder why this one is different.  There are a dozen or
so PCI drivers that use NOIRQ_SYSTEM_SLEEP_PM_OPS().

Bjorn


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

* Re: [PATCH 4/8] dt-bindings: PCI: renesas,r9a08g045s33-pcie: Add documentation for the PCIe IP on Renesas RZ/G3S
  2025-04-30 10:32 ` [PATCH 4/8] dt-bindings: PCI: renesas,r9a08g045s33-pcie: Add documentation for the PCIe IP on Renesas RZ/G3S Claudiu
@ 2025-05-01 20:16   ` Bjorn Helgaas
  2025-05-05 11:28     ` Claudiu Beznea
  2025-05-09 21:08   ` Rob Herring
  1 sibling, 1 reply; 28+ messages in thread
From: Bjorn Helgaas @ 2025-05-01 20:16 UTC (permalink / raw)
  To: Claudiu
  Cc: bhelgaas, lpieralisi, kw, manivannan.sadhasivam, robh, krzk+dt,
	conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, linux-arm-kernel, linux-clk, Claudiu Beznea

On Wed, Apr 30, 2025 at 01:32:32PM +0300, Claudiu wrote:
> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> 
> The PCIe IP available on the Renesas RZ/G3S complies with the PCI Express
> Base Specification 4.0. It is designed for root complex applications and
> features a single-lane (x1) implementation. Add documentation for it.
> The interrupts, interrupt-names, resets, reset-names, clocks, clock-names
> description were obtained from the hardware manual.

> +        pcie@11e40000 {
> +            compatible = "renesas,r9a08g045s33-pcie";
> +            reg = <0 0x11e40000 0 0x10000>;
> +            ranges = <0x03000000 0 0x30000000 0 0x30000000 0 0x8000000>;
> +            dma-ranges = <0x42000000 0 0x48000000 0 0x48000000 0 0x8000000>;
> +            bus-range = <0x0 0xff>;
> +            clocks = <&cpg CPG_MOD R9A08G045_PCI_ACLK>,
> +                     <&cpg CPG_MOD R9A08G045_PCI_CLKL1PM>;
> +            clock-names = "aclk", "clkl1pm";
> +            resets = <&cpg R9A08G045_PCI_ARESETN>,
> +                     <&cpg R9A08G045_PCI_RST_B>,
> +                     <&cpg R9A08G045_PCI_RST_GP_B>,
> +                     <&cpg R9A08G045_PCI_RST_PS_B>,
> +                     <&cpg R9A08G045_PCI_RST_RSM_B>,
> +                     <&cpg R9A08G045_PCI_RST_CFG_B>,
> +                     <&cpg R9A08G045_PCI_RST_LOAD_B>;
> +            reset-names = "aresetn", "rst_b", "rst_gp_b", "rst_ps_b",
> +                          "rst_rsm_b", "rst_cfg_b", "rst_load_b";

Could this be structured in a way that separates the shared Root
Complex properties from the ones that are specific to the Root Port?
I know the current hardware only supports a single Root Port, but I
think we should plan to be able to support multiple Root Ports.

> +            interrupts = <GIC_SPI 395 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 396 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 397 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 398 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 399 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 400 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 401 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 402 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 403 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 404 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 405 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 406 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 407 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 408 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 409 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 410 IRQ_TYPE_LEVEL_HIGH>;
> +            interrupt-names = "int_serr", "int_serr_cor", "int_serr_nonfatal",
> +                              "int_serr_fatal", "axi_err_int", "inta_rc",
> +                              "intb_rc", "intc_rc", "intd_rc",
> +                              "intmsi_rc", "int_link_bandwidth", "int_pm_pme",
> +                              "dma_int", "pcie_evt_int", "msg_int",
> +                              "int_all";
> +            #interrupt-cells = <1>;
> +            interrupt-map-mask = <0 0 0 7>;
> +            interrupt-map = <0 0 0 1 &pcie_intx 0>, /* INT A */
> +                            <0 0 0 2 &pcie_intx 1>, /* INT B */
> +                            <0 0 0 3 &pcie_intx 2>, /* INT C */
> +                            <0 0 0 4 &pcie_intx 3>; /* INT D */
> +            device_type = "pci";
> +            num-lanes = <1>;
> +            #address-cells = <3>;
> +            #size-cells = <2>;
> +            power-domains = <&cpg>;
> +            renesas,sysc = <&sysc>;
> +            vendor-id = <0x1912>;
> +            device-id = <0x0033>;
> +
> +            pcie_intx: legacy-interrupt-controller {
> +                interrupt-controller;
> +                #interrupt-cells = <1>;
> +                #address-cells = <0>;
> +                interrupt-parent = <&gic>;
> +                interrupts = <GIC_SPI 400 IRQ_TYPE_LEVEL_HIGH>,
> +                             <GIC_SPI 401 IRQ_TYPE_LEVEL_HIGH>,
> +                             <GIC_SPI 402 IRQ_TYPE_LEVEL_HIGH>,
> +                             <GIC_SPI 403 IRQ_TYPE_LEVEL_HIGH>;
> +            };
> +        };
> +    };
> +
> +...
> -- 
> 2.43.0
> 


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

* Re: [PATCH 5/8] PCI: rzg3s-host: Add Initial PCIe Host Driver for Renesas RZ/G3S SoC
  2025-05-01 20:12   ` Bjorn Helgaas
@ 2025-05-05 11:26     ` Claudiu Beznea
  2025-05-09 10:29       ` Claudiu Beznea
  2025-05-12 20:25       ` Bjorn Helgaas
  0 siblings, 2 replies; 28+ messages in thread
From: Claudiu Beznea @ 2025-05-05 11:26 UTC (permalink / raw)
  To: Bjorn Helgaas
  Cc: bhelgaas, lpieralisi, kw, manivannan.sadhasivam, robh, krzk+dt,
	conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, linux-arm-kernel, linux-clk, Claudiu Beznea

Hi, Bjorn,

On 01.05.2025 23:12, Bjorn Helgaas wrote:
> On Wed, Apr 30, 2025 at 01:32:33PM +0300, Claudiu wrote:
>> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>>
>> The Renesas RZ/G3S features a PCIe IP that complies with the PCI Express
>> Base Specification 4.0 and supports speeds of up to 5 GT/s. It functions
>> only as a root complex, with a single-lane (x1) configuration. The
>> controller includes Type 1 configuration registers, as well as IP
>> specific registers (called AXI registers) required for various adjustments.
>>
>> Other Renesas RZ SoCs (e.g., RZ/G3E, RZ/V2H) share the same AXI registers
>> but have both Root Complex and Endpoint capabilities. As a result, the PCIe
>> host driver can be reused for these variants with minimal adjustments.
> 
> I guess this current driver only supports RZ/GS3 in Root Complex mode?

That's right.

> If so, I don't think this paragraph is necessary or really relevant.

OK, I'll drop it.

> 
>> +++ b/drivers/pci/controller/pcie-rzg3s-host.c
>> @@ -0,0 +1,1561 @@
> 
> I can't figure out the line width you're using.  Generally code in
> drivers/pci/ is formatted to fit in 80 columns.  Much of this file is
> formatted for that, but there are many cases that seem to use 90 or
> 100 columns.

I formated it at 100 columns where the lines were longer. I wasn't aware
the PCI rule is to have line formated at 80 columns. I'll switch to it in
the next version.

> 
> For single-line comments that are not a sentence or are a single
> sentence, it's typical to omit the period at end.

I'll follow this rule, too.

> 
>> +static void rzg3s_pcie_update_bits(void __iomem *base, u32 offset, u32 mask, u32 val)
>> +{
>> +	u32 tmp;
>> +
>> +	tmp = readl(base + offset);
>> +	tmp &= ~mask;
>> +	tmp |= val & mask;
>> +	writel(tmp, base + offset);
>> +}
> 
> Nothing rzg3s-specific here.
> 
> I think u32p_replace_bits() (include/linux/bitfield.h) is basically this.

I wasn't aware of it. I'll use it in the next version. Thank for pointing it.

> 
>> +static int rzg3s_pcie_config_access(struct rzg3s_pcie_host *host, struct pci_bus *bus,
>> +				    unsigned int devfn, int where, u32 *data,
>> +				    enum rzg3s_pcie_cfg_access_type access_type)
>> +{
>> +	bool type0 = pci_is_root_bus(bus->parent) ? true : false;
>> +	unsigned int dev, func, reg, tr_type;
>> +	int ret;
>> +
>> +	dev = PCI_SLOT(devfn);
>> +	func = PCI_FUNC(devfn);
>> +	reg = where & ~0x3;
> 
> This obviously has to be serialized with other config accesses.  Can
> you add a comment about what provides that serialization?  I assume
> it's probably pci_lock via pci_lock_config()?

Yes, it is the pci_lock via pci_{lock, unlock}_config(). I'll add a comment
for it.

> 
>> +	/* Set the destination. */
>> +	writel(FIELD_PREP(RZG3S_PCI_REQADR1_BUS, bus->number) |
>> +	       FIELD_PREP(RZG3S_PCI_REQADR1_DEV, dev) |
>> +	       FIELD_PREP(RZG3S_PCI_REQADR1_FUNC, func) |
>> +	       FIELD_PREP(RZG3S_PCI_REQADR1_REG, reg),
>> +	       host->axi + RZG3S_PCI_REQADR1);
>> +
>> +	/* Set byte enable. */
>> +	writel(RZG3S_PCI_REQBE_BYTE_EN, host->axi + RZG3S_PCI_REQBE);
>> +
>> +	if (access_type == RZG3S_PCIE_CFG_ACCESS_RD) {
>> +		tr_type = type0 ? RZG3S_PCI_REQISS_TR_TP0_RD : RZG3S_PCI_REQISS_TR_TP1_RD;
>> +	} else {
>> +		tr_type = type0 ? RZG3S_PCI_REQISS_TR_TP0_WR : RZG3S_PCI_REQISS_TR_TP1_WR;
>> +
>> +		/* Set the write data.  */
>> +		writel(0, host->axi + RZG3S_PCI_REQDATA(0));
>> +		writel(0, host->axi + RZG3S_PCI_REQDATA(1));
>> +		writel(*data, host->axi + RZG3S_PCI_REQDATA(2));
>> +	}
>> +
>> +	/* Set the type of request. */
>> +	writel(tr_type, host->axi + RZG3S_PCI_REQISS);
>> +
>> +	/* Issue the request and wait to finish. */
>> +	ret = rzg3s_pcie_issue_request(host);
>> +	if (ret) {
>> +		if (access_type == RZG3S_PCIE_CFG_ACCESS_RD)
>> +			PCI_SET_ERROR_RESPONSE(data);
>> +
>> +		return PCIBIOS_SET_FAILED;
>> +	}
>> +
>> +	/* Read the data. */
>> +	if (access_type == RZG3S_PCIE_CFG_ACCESS_RD)
>> +		*data = readl(host->axi + RZG3S_PCI_REQRCVDAT);
>> +
>> +	return PCIBIOS_SUCCESSFUL;
>> +}
> 
>> +static int rzg3s_pcie_read_conf(struct pci_bus *bus, unsigned int devfn, int where, int size,
>> +				u32 *val)
>> +{
>> +	struct rzg3s_pcie_host *host = bus->sysdata;
>> +	int ret;
>> +
>> +	/*
>> +	 * Bus number for root complex is set to zero. 
> 
> A root bus is not always bus number zero.  It sounds like the bus
> number is configurable on this hardware?

According to the documentation, yes, it is configurable. However, I haven't
tried with anything other than zero.

>  I don't see a dependency on
> the bus number, so I'm wondering why this sentence is here.

In probe, this function is called from
https://elixir.bootlin.com/linux/v6.14.5/source/drivers/pci/probe.c#L2992
with devfn in range [0, 256).
I had issues with letting the code configuring the controller with these
requests. Double checking it now, I think I've managed to make it work.
Thank you for pointing it.

> 
>> +	   ... Skip other requests for
>> +	 * vendor and device ID (where = 0x00).
> 
> Why do we need to avoid config reads for root bus devices other than
> 00.0?  Does the hardware not fabricate ~0 read data when a PCI error
> occurs?
> 
> It looks like you allow reads for other root bus devices at non-zero
> offsets?  E.g., we could read the Device ID at offset 0x02?

I had initially issues with the initialization sequence pointed above. But
your question made me re-checking it and I've manage to make it work. I'll
drop this limitation in the next version.

> 
>> +	 */
>> +	if (pci_is_root_bus(bus) && PCI_SLOT(devfn) && !where)
>> +		return PCIBIOS_DEVICE_NOT_FOUND;
>> +
>> +	ret = rzg3s_pcie_read_config_access(host, bus, devfn, where, val);
>> +	if (ret != PCIBIOS_SUCCESSFUL)
>> +		return ret;
>> +
>> +	if (size == 1)
>> +		*val = (*val >> (BITS_PER_BYTE * (where & 3))) & 0xff;
>> +	else if (size == 2)
>> +		*val = (*val >> (BITS_PER_BYTE * (where & 2))) & 0xffff;
>> +
>> +	dev_dbg(&bus->dev, "%s(): bus=%3d devfn=0x%04x where=0x%04x size=%d val=0x%08x\n",
>> +		__func__, bus->number, devfn, where, size, *val);
>> +
>> +	return PCIBIOS_SUCCESSFUL;
>> +}
> 
>> +static int rzg3s_pcie_write_conf(struct pci_bus *bus, unsigned int devfn, int where, int size,
>> +				 u32 val)
>> +{
>> +	struct rzg3s_pcie_host *host = bus->sysdata;
>> +	u32 shift, data;
>> +	int ret;
>> +
>> +	ret = rzg3s_pcie_read_config_access(host, bus, devfn, where, &data);
>> +	if (ret != PCIBIOS_SUCCESSFUL)
>> +		return ret;
> 
> I guess this controller can't do an atomic config write, so you have
> to do a read/modify/write?

That's true.

>  That's broken in some corner cases, e.g.,
> it can corrupt RW1C bits in adjacent registers.
> 
> We emit a warning about this in pci_generic_config_write32().  I don't
> think we've been consistent about similar warnings in driver-specific
> code like this, but I would like to have a dmesg hint so that if we
> ever trip over this, it might be easier to debug.

OK, I'll add a one time debug message for this.

> 
>> +	dev_dbg(&bus->dev, "%s(): bus=%3d devfn=0x%04x where=0x%04x size=%d val=0x%08x\n",
>> +		__func__, bus->number, devfn, where, size, val);
>> +
>> +	if (size == 1) {
>> +		shift = BITS_PER_BYTE * (where & 3);
>> +		data &= ~(0xff << shift);
>> +		data |= ((val & 0xff) << shift);
>> +	} else if (size == 2) {
>> +		shift = BITS_PER_BYTE * (where & 2);
>> +		data &= ~(0xffff << shift);
>> +		data |= ((val & 0xffff) << shift);
>> +	} else {
>> +		data = val;
>> +	}
>> +
>> +	return rzg3s_pcie_write_config_access(host, bus, devfn, where, data);
>> +}
> 
>> +static struct irq_chip rzg3s_pcie_msi_bottom_chip = {
>> +	.name			= "rz-pcie-msi",
>> +	.irq_ack		= rzg3s_pcie_msi_irq_ack,
>> +	.irq_mask		= rzg3s_pcie_msi_irq_mask,
>> +	.irq_unmask		= rzg3s_pcie_msi_irq_unmask,
>> +	.irq_compose_msi_msg	= rzg3s_pcie_msi_compose_msg,
> 
> s/msi_compose_msg/compose_msi_msg/
> to match .irq_compose_msi_msg, which doesn't quite match the others
> but makes it easier to grep and compare .irq_compose_msi_msg
> implementations.

OK

> 
>> +	 * According to the RZ/G3S HW manual (section 34.4.5.2 Setting the MSI Window)
>> +	 * the MSI window need to be within any AXI window. Find an AXI window to setup
>> +	 * the MSI window.
> 
> There may be several revisions of the RZ/G3S spec and they might not
> preserve section numbers, so it might be worth including "r1.0" or
> whatever to make the citation unambiguous.

OK

> 
>> +static struct irq_chip rzg3s_pcie_intx_irq_chip = {
>> +	.name = "PCIe INTx",
>> +	.irq_ack = rzg3s_pcie_intx_ack,
>> +	.irq_mask = rzg3s_pcie_intx_mask,
>> +	.irq_unmask = rzg3s_pcie_intx_unmask,
> 
> Would prefer *_irq_ack, *_irq_mask, *_irq_unmask similar to MSI ops
> above.

OK

> 
>> +static int rzg3s_pcie_intx_setup(struct rzg3s_pcie_host *host)
>> +{
>> +	struct device *dev = host->dev;
>> +	struct device_node *np = dev->of_node;
>> +	struct device_node *intc_np __free(device_node) = of_get_child_by_name(np,
>> +								"legacy-interrupt-controller");
>> +	int irq_count;
>> +
>> +	if (!intc_np) {
>> +		dev_warn(dev, "legacy-interrupt-controller node is absent\n");
>> +		return 0;
>> +	}
>> +
>> +	irq_count = of_irq_count(intc_np);
>> +	if (irq_count != PCI_NUM_INTX)
>> +		return dev_err_probe(dev, -EINVAL,
>> +				     "Invalid IRQ entries in legacy-interrupt-controller\n");
> 
> We now know the value of irq_count, so why not just use PCI_NUM_INTX
> below?

Will use PCI_NUM_INTX in the next version.

> 
>> +	for (int i = 0; i < irq_count; i++) {
>> +		int irq = irq_of_parse_and_map(intc_np, i);
>> +
>> +		if (!irq)
>> +			return dev_err_probe(dev, -EINVAL, "Failed to parse and map INTx IRQ\n");
> 
> Could say which one?

Sure!

> 
>> +
>> +		host->intx_irqs[i] = irq;
>> +		irq_set_chained_handler_and_data(irq, rzg3s_pcie_intx_irq_handler, host);
>> +	}
>> +
>> +	host->intx_domain = irq_domain_add_linear(intc_np, irq_count,
>> +						  &rzg3s_pcie_intx_domain_ops, host);
>> +	if (!host->intx_domain)
>> +		return dev_err_probe(dev, -EINVAL, "Failed to add irq domain for INTX irqs\n");
> 
> s/INTX/INTx/ to match above.
> s/irqs/IRQs/ to match above.

OK

> 
>> +static int rzg3s_pcie_set_max_link_speed(struct rzg3s_pcie_host *host)
>> +{
>> +	u32 lcs, cs2, link_speed, remote_supported_link_speeds, tmp;
>> +	u8 ltssm_state_l0 = 0xc;
>> +	int ret;
>> +
>> +	/*
>> +	 * According to the RZ/G3S HW manual (section 34.6.3 Caution when Changing
>> +	 * the Speed Spontaneously) link speed change can be done only when the
>> +	 * link training and status state machine in the PCIe Core Link is L0.
>> +	 */
>> +	ret = readl_poll_timeout(host->axi + RZG3S_PCI_PCSTAT1, tmp,
>> +				 FIELD_GET(RZG3S_PCI_PCSTAT1_LTSSM_STATE, tmp) == ltssm_state_l0,
>> +				 100, 1000);
> 
> Where do these timeout values come from?

These were obtained from experimenting.

>  Is there or should there be
> a #define for them?

I'll add defines in the next version.

> 
>> +static void rzg3s_pcie_cfg_init(struct rzg3s_pcie_host *host)
>> +{
>> +	/* Enable access control to the CFGU. */
>> +	writel(RZG3S_PCI_PERM_CFG_HWINIT_EN, host->axi + RZG3S_PCI_PERM);
>> +
>> +	/* Update vendor ID and device ID. */
>> +	writel(FIELD_PREP(RZG3S_PCI_CFG_VID_VENDOR, host->vendor_id) |
>> +	       FIELD_PREP(RZG3S_PCI_CFG_VID_DEVICE, host->device_id),
>> +	       host->pcie + RZG3S_PCI_CFG_VID);
>> +
>> +	/* HW manual recommends to write 0xffffffff on initialization. */
>> +	writel(0xffffffff, host->pcie + RZG3S_PCI_CFG_BARMSK00L);
>> +	writel(0xffffffff, host->pcie + RZG3S_PCI_CFG_BARMSK00U);
>> +
>> +	/* Primary bus = 0, secondary bus = 1, subordinate bus = 1. */
>> +	writel(FIELD_PREP(RZG3S_PCI_CFG_BNR_SUBORDINATE_BUS, 1) |
>> +	       FIELD_PREP(RZG3S_PCI_CFG_BNR_SECONDARY_BUS, 1),
>> +	       host->pcie + RZG3S_PCI_CFG_BNR);
> 
> I assume this configures a Root Port.  Is the primary bus hardwired to
> zero?

The documentation doens't mentioned anything about limiting it to zero.

>  There's no PCIe requirement that a root bus be bus 0.  This
> looks like it limits the subordinate bus unnecessarily.  The PCI core
> can program the secondary and subordinate bus numbers to accommodate
> the attached hierarchy.

I'll work on dropping this limitation in the next version.


> 
>> +static void rzg3s_pcie_irqs_init(struct rzg3s_pcie_host *host)
> 
> This and many of the following functions have names that don't
> correspond to anything in other drivers, which makes it harder to
> transfer knowledge between the drivers.  If you can find a pattern
> somewhere to follow, it will make it easier for others to read the
> driver.

OK, I'll think about it. Do you have a recomentation?

> 
>> +static int rzg3s_pcie_hw_init(struct rzg3s_pcie_host *host, bool probe)
>> +{
>> +	u32 val;
>> +	int ret;
>> +
>> +	/* Set the PCIe related registers. */
>> +	rzg3s_pcie_cfg_init(host);
>> +
>> +	/* Set the interrupts. */
>> +	rzg3s_pcie_irqs_init(host);
>> +
>> +	ret = rzg3s_pcie_resets_bulk_set(reset_control_bulk_deassert, host->cfg_resets,
>> +					 host->num_cfg_resets);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Wait for link up. */
>> +	ret = readl_poll_timeout(host->axi + RZG3S_PCI_PCSTAT1, val,
>> +				 !(val & RZG3S_PCI_PCSTAT1_DL_DOWN_STS), 5000, 500000);
> 
> Ditto (source of timeout values).
> 
>> +static int rzg3s_pcie_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	void *devres_group_id;
>> +	int ret;
>> +
>> +	devres_group_id = devres_open_group(dev, NULL, GFP_KERNEL);
>> +	if (!devres_group_id)
>> +		return -ENOMEM;
> 
> What's the benefit of using devres_open_group()?  No other PCI
> controller drivers use it.

This driver uses devm_add_action_or_reset() to keep the error path simpler.
Some of the action or reset registered handlers access the controller
registers. Because the driver is attached to the platform bus and the
dev_pm_domain_detach() is called right after driver remove [1] having devm
action or reset handlers accessing controller register will later lead to
hangs when the device_unbind_cleanup() -> devres_release_all() will be
called on remove path. Other issue described in [2] may arries when doing
continuous unbind/bind if the driver has runtime PM API (not case for this
driver at the moment) that access directly controller registers.

This is because the dev_pm_domain_detach() drops the clocks from PM domain
and any subsequent pm_runtime_resume() (or similar function) call will lead
to no runtime resume of the device.

There is a solution proposed to this here [2] but it slowly progresses.
Until this will be solved I chosed the appraoch of having the devres group
opened here. If you agree with it, I had the intention to drop this call if
there will be an accepted solution for it. If you are OK with going forward
like this, for the moment, would to prefer me to add a comment about the
reason the devres_open_group() is used here?

This is not PCIe specific but platform bus specific. There are other
affected drivers on this side (e.g. rzg2l-adc [3], rzg3s-thermal [4]).

A similar solution as [2] is already used by the i2c subsystem.

[1]
https://elixir.bootlin.com/linux/v6.14.5/source/drivers/base/platform.c#L1425
[2]
https://lore.kernel.org/all/20250215130849.227812-1-claudiu.beznea.uj@bp.renesas.com/
[3] https://lore.kernel.org/all/20250330163627.152d76ef@jic23-huawei/
[4]
https://lore.kernel.org/all/20250324135701.179827-3-claudiu.beznea.uj@bp.renesas.com/

> 
>> +	ret = rzg3s_pcie_probe_helper(pdev, devres_group_id);
> 
> This is the only driver that uses a *_pcie_probe_helper() function.
> 
>> +static DEFINE_NOIRQ_DEV_PM_OPS(rzg3s_pcie_pm_ops, rzg3s_pcie_suspend, rzg3s_pcie_resume);
> 
> Adding "_noirq" to the function names would be a good hint.

I missed it. I'll add it in the next version.

> 
> Only a dozen drivers in the whole tree use DEFINE_NOIRQ_DEV_PM_OPS(),
> which makes me wonder why this one is different.  There are a dozen or
> so PCI drivers that use NOIRQ_SYSTEM_SLEEP_PM_OPS().

This is how I'm used to approach it when there are simple dev_pm_ops. I
didn't know the:

const struct dev_pm_ops name = {

        NOIRQ_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn)

};

is prefered. I'll switch to it in the next version.

Thank you for your review,
Claudiu


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

* Re: [PATCH 4/8] dt-bindings: PCI: renesas,r9a08g045s33-pcie: Add documentation for the PCIe IP on Renesas RZ/G3S
  2025-05-01 20:16   ` Bjorn Helgaas
@ 2025-05-05 11:28     ` Claudiu Beznea
  0 siblings, 0 replies; 28+ messages in thread
From: Claudiu Beznea @ 2025-05-05 11:28 UTC (permalink / raw)
  To: Bjorn Helgaas
  Cc: bhelgaas, lpieralisi, kw, manivannan.sadhasivam, robh, krzk+dt,
	conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, linux-arm-kernel, linux-clk, Claudiu Beznea

Hi, Bjorn,

On 01.05.2025 23:16, Bjorn Helgaas wrote:
> On Wed, Apr 30, 2025 at 01:32:32PM +0300, Claudiu wrote:
>> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>>
>> The PCIe IP available on the Renesas RZ/G3S complies with the PCI Express
>> Base Specification 4.0. It is designed for root complex applications and
>> features a single-lane (x1) implementation. Add documentation for it.
>> The interrupts, interrupt-names, resets, reset-names, clocks, clock-names
>> description were obtained from the hardware manual.
> 
>> +        pcie@11e40000 {
>> +            compatible = "renesas,r9a08g045s33-pcie";
>> +            reg = <0 0x11e40000 0 0x10000>;
>> +            ranges = <0x03000000 0 0x30000000 0 0x30000000 0 0x8000000>;
>> +            dma-ranges = <0x42000000 0 0x48000000 0 0x48000000 0 0x8000000>;
>> +            bus-range = <0x0 0xff>;
>> +            clocks = <&cpg CPG_MOD R9A08G045_PCI_ACLK>,
>> +                     <&cpg CPG_MOD R9A08G045_PCI_CLKL1PM>;
>> +            clock-names = "aclk", "clkl1pm";
>> +            resets = <&cpg R9A08G045_PCI_ARESETN>,
>> +                     <&cpg R9A08G045_PCI_RST_B>,
>> +                     <&cpg R9A08G045_PCI_RST_GP_B>,
>> +                     <&cpg R9A08G045_PCI_RST_PS_B>,
>> +                     <&cpg R9A08G045_PCI_RST_RSM_B>,
>> +                     <&cpg R9A08G045_PCI_RST_CFG_B>,
>> +                     <&cpg R9A08G045_PCI_RST_LOAD_B>;
>> +            reset-names = "aresetn", "rst_b", "rst_gp_b", "rst_ps_b",
>> +                          "rst_rsm_b", "rst_cfg_b", "rst_load_b";
> 
> Could this be structured in a way that separates the shared Root
> Complex properties from the ones that are specific to the Root Port?
> I know the current hardware only supports a single Root Port, but I
> think we should plan to be able to support multiple Root Ports.

I'm not sure how should I do this. These are just the reset signals as the
manual describes them. Can you, please, point me an example driver/device
tree that I could check?

Thank you for your review,
Claudiu

> 
>> +            interrupts = <GIC_SPI 395 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 396 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 397 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 398 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 399 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 400 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 401 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 402 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 403 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 404 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 405 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 406 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 407 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 408 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 409 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 410 IRQ_TYPE_LEVEL_HIGH>;
>> +            interrupt-names = "int_serr", "int_serr_cor", "int_serr_nonfatal",
>> +                              "int_serr_fatal", "axi_err_int", "inta_rc",
>> +                              "intb_rc", "intc_rc", "intd_rc",
>> +                              "intmsi_rc", "int_link_bandwidth", "int_pm_pme",
>> +                              "dma_int", "pcie_evt_int", "msg_int",
>> +                              "int_all";
>> +            #interrupt-cells = <1>;
>> +            interrupt-map-mask = <0 0 0 7>;
>> +            interrupt-map = <0 0 0 1 &pcie_intx 0>, /* INT A */
>> +                            <0 0 0 2 &pcie_intx 1>, /* INT B */
>> +                            <0 0 0 3 &pcie_intx 2>, /* INT C */
>> +                            <0 0 0 4 &pcie_intx 3>; /* INT D */
>> +            device_type = "pci";
>> +            num-lanes = <1>;
>> +            #address-cells = <3>;
>> +            #size-cells = <2>;
>> +            power-domains = <&cpg>;
>> +            renesas,sysc = <&sysc>;
>> +            vendor-id = <0x1912>;
>> +            device-id = <0x0033>;
>> +
>> +            pcie_intx: legacy-interrupt-controller {
>> +                interrupt-controller;
>> +                #interrupt-cells = <1>;
>> +                #address-cells = <0>;
>> +                interrupt-parent = <&gic>;
>> +                interrupts = <GIC_SPI 400 IRQ_TYPE_LEVEL_HIGH>,
>> +                             <GIC_SPI 401 IRQ_TYPE_LEVEL_HIGH>,
>> +                             <GIC_SPI 402 IRQ_TYPE_LEVEL_HIGH>,
>> +                             <GIC_SPI 403 IRQ_TYPE_LEVEL_HIGH>;
>> +            };
>> +        };
>> +    };
>> +
>> +...
>> -- 
>> 2.43.0
>>



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

* Re: [PATCH 5/8] PCI: rzg3s-host: Add Initial PCIe Host Driver for Renesas RZ/G3S SoC
  2025-05-05 11:26     ` Claudiu Beznea
@ 2025-05-09 10:29       ` Claudiu Beznea
  2025-05-12 20:38         ` Bjorn Helgaas
  2025-05-12 20:25       ` Bjorn Helgaas
  1 sibling, 1 reply; 28+ messages in thread
From: Claudiu Beznea @ 2025-05-09 10:29 UTC (permalink / raw)
  To: Bjorn Helgaas
  Cc: bhelgaas, lpieralisi, kw, manivannan.sadhasivam, robh, krzk+dt,
	conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, linux-arm-kernel, linux-clk, Claudiu Beznea

Hi, Bjorn,

On 05.05.2025 14:26, Claudiu Beznea wrote:
> Hi, Bjorn,
> 
> On 01.05.2025 23:12, Bjorn Helgaas wrote:
>> On Wed, Apr 30, 2025 at 01:32:33PM +0300, Claudiu wrote:
>>> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>>>
>>> The Renesas RZ/G3S features a PCIe IP that complies with the PCI Express
>>> Base Specification 4.0 and supports speeds of up to 5 GT/s. It functions
>>> only as a root complex, with a single-lane (x1) configuration. The
>>> controller includes Type 1 configuration registers, as well as IP
>>> specific registers (called AXI registers) required for various adjustments.
>>>
>>> Other Renesas RZ SoCs (e.g., RZ/G3E, RZ/V2H) share the same AXI registers
>>> but have both Root Complex and Endpoint capabilities. As a result, the PCIe
>>> host driver can be reused for these variants with minimal adjustments.
>>
>> I guess this current driver only supports RZ/GS3 in Root Complex mode?
> 
> That's right.
> 
>> If so, I don't think this paragraph is necessary or really relevant.
> 
> OK, I'll drop it.
> 
>>
>>> +++ b/drivers/pci/controller/pcie-rzg3s-host.c
>>> @@ -0,0 +1,1561 @@
>>
>> I can't figure out the line width you're using.  Generally code in
>> drivers/pci/ is formatted to fit in 80 columns.  Much of this file is
>> formatted for that, but there are many cases that seem to use 90 or
>> 100 columns.
> 
> I formated it at 100 columns where the lines were longer. I wasn't aware
> the PCI rule is to have line formated at 80 columns. I'll switch to it in
> the next version.
> 
>>
>> For single-line comments that are not a sentence or are a single
>> sentence, it's typical to omit the period at end.
> 
> I'll follow this rule, too.
> 
>>
>>> +static void rzg3s_pcie_update_bits(void __iomem *base, u32 offset, u32 mask, u32 val)
>>> +{
>>> +	u32 tmp;
>>> +
>>> +	tmp = readl(base + offset);
>>> +	tmp &= ~mask;
>>> +	tmp |= val & mask;
>>> +	writel(tmp, base + offset);
>>> +}
>>
>> Nothing rzg3s-specific here.
>>
>> I think u32p_replace_bits() (include/linux/bitfield.h) is basically this.
> 
> I wasn't aware of it. I'll use it in the next version. Thank for pointing it.

I look into changing to u32p_replace_bits() but this one needs a mask that
can be verified at build time. It cannot be used directly in this function.
Would you prefer me to replace all the calls to rzg3s_pcie_update_bits() with:

tmp = readl();
u32p_replace_bits(&tmp, ...)
writel(tmp);

or is it OK for you to keep it as is?

Thank you,
Claudiu


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

* Re: [PATCH 5/8] PCI: rzg3s-host: Add Initial PCIe Host Driver for Renesas RZ/G3S SoC
  2025-04-30 10:32 ` [PATCH 5/8] PCI: rzg3s-host: Add Initial PCIe Host Driver for Renesas RZ/G3S SoC Claudiu
  2025-05-01 20:12   ` Bjorn Helgaas
@ 2025-05-09 10:51   ` Philipp Zabel
  2025-05-09 11:41     ` Claudiu Beznea
  2025-05-09 20:49   ` Rob Herring
  2 siblings, 1 reply; 28+ messages in thread
From: Philipp Zabel @ 2025-05-09 10:51 UTC (permalink / raw)
  To: Claudiu, bhelgaas, lpieralisi, kw, manivannan.sadhasivam, robh,
	krzk+dt, conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak
  Cc: linux-pci, linux-renesas-soc, devicetree, linux-kernel,
	linux-arm-kernel, linux-clk, Claudiu Beznea

Hi Claudiu,

On Mi, 2025-04-30 at 13:32 +0300, Claudiu wrote:
> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> 
> The Renesas RZ/G3S features a PCIe IP that complies with the PCI Express
> Base Specification 4.0 and supports speeds of up to 5 GT/s. It functions
> only as a root complex, with a single-lane (x1) configuration. The
> controller includes Type 1 configuration registers, as well as IP
> specific registers (called AXI registers) required for various adjustments.
> 
> Other Renesas RZ SoCs (e.g., RZ/G3E, RZ/V2H) share the same AXI registers
> but have both Root Complex and Endpoint capabilities. As a result, the PCIe
> host driver can be reused for these variants with minimal adjustments.
> 
> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> ---
>  MAINTAINERS                              |    8 +
>  drivers/pci/controller/Kconfig           |    7 +
>  drivers/pci/controller/Makefile          |    1 +
>  drivers/pci/controller/pcie-rzg3s-host.c | 1561 ++++++++++++++++++++++
>  4 files changed, 1577 insertions(+)
>  create mode 100644 drivers/pci/controller/pcie-rzg3s-host.c
> 
[...]
> diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c
> new file mode 100644
> index 000000000000..c3bce0acd57e
> --- /dev/null
> +++ b/drivers/pci/controller/pcie-rzg3s-host.c
> @@ -0,0 +1,1561 @@
[...]
> +static int rzg3s_pcie_resets_bulk_set(int (*action)(int num, struct reset_control_bulk_data *rstcs),
> +				      struct reset_control **resets, u8 num_resets)
> +{
> +	struct reset_control_bulk_data *data __free(kfree) =
> +		kcalloc(num_resets, sizeof(*data), GFP_KERNEL);
> +
> +	if (!data)
> +		return -ENOMEM;
> +
> +	for (u8 i = 0; i < num_resets; i++)
> +		data[i].rstc = resets[i];
> +
> +	return action(num_resets, data);
> +}

What is the purpose of this? Can't you just store struct
reset_control_bulk_data in struct rzg3s_pcie_host and call
reset_control_bulk_assert/deassert() directly?

> +static int
> +rzg3s_pcie_resets_init(struct device *dev, struct reset_control ***resets,
> +		       struct reset_control *(*action)(struct device *dev, const char *id),
> +		       const char * const *reset_names, u8 num_resets)
> +{
> +	*resets = devm_kcalloc(dev, num_resets, sizeof(struct reset_control *), GFP_KERNEL);
> +	if (!*resets)
> +		return -ENOMEM;
> +
> +	for (u8 i = 0; i < num_resets; i++) {
> +		(*resets)[i] = action(dev, reset_names[i]);
> +		if (IS_ERR((*resets)[i]))
> +			return PTR_ERR((*resets)[i]);
> +	}
> +
> +	return 0;
> +}

Why not use devm_reset_control_bulk_get_exclusive() directly?


regards
Philipp


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

* Re: [PATCH 5/8] PCI: rzg3s-host: Add Initial PCIe Host Driver for Renesas RZ/G3S SoC
  2025-05-09 10:51   ` Philipp Zabel
@ 2025-05-09 11:41     ` Claudiu Beznea
  0 siblings, 0 replies; 28+ messages in thread
From: Claudiu Beznea @ 2025-05-09 11:41 UTC (permalink / raw)
  To: Philipp Zabel, bhelgaas, lpieralisi, kw, manivannan.sadhasivam,
	robh, krzk+dt, conor+dt, geert+renesas, magnus.damm, mturquette,
	sboyd, saravanak
  Cc: linux-pci, linux-renesas-soc, devicetree, linux-kernel,
	linux-arm-kernel, linux-clk, Claudiu Beznea

Hi, Philipp,

On 09.05.2025 13:51, Philipp Zabel wrote:
> Hi Claudiu,
> 
> On Mi, 2025-04-30 at 13:32 +0300, Claudiu wrote:
>> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>>
>> The Renesas RZ/G3S features a PCIe IP that complies with the PCI Express
>> Base Specification 4.0 and supports speeds of up to 5 GT/s. It functions
>> only as a root complex, with a single-lane (x1) configuration. The
>> controller includes Type 1 configuration registers, as well as IP
>> specific registers (called AXI registers) required for various adjustments.
>>
>> Other Renesas RZ SoCs (e.g., RZ/G3E, RZ/V2H) share the same AXI registers
>> but have both Root Complex and Endpoint capabilities. As a result, the PCIe
>> host driver can be reused for these variants with minimal adjustments.
>>
>> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>> ---
>>  MAINTAINERS                              |    8 +
>>  drivers/pci/controller/Kconfig           |    7 +
>>  drivers/pci/controller/Makefile          |    1 +
>>  drivers/pci/controller/pcie-rzg3s-host.c | 1561 ++++++++++++++++++++++
>>  4 files changed, 1577 insertions(+)
>>  create mode 100644 drivers/pci/controller/pcie-rzg3s-host.c
>>
> [...]
>> diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c
>> new file mode 100644
>> index 000000000000..c3bce0acd57e
>> --- /dev/null
>> +++ b/drivers/pci/controller/pcie-rzg3s-host.c
>> @@ -0,0 +1,1561 @@
> [...]
>> +static int rzg3s_pcie_resets_bulk_set(int (*action)(int num, struct reset_control_bulk_data *rstcs),
>> +				      struct reset_control **resets, u8 num_resets)
>> +{
>> +	struct reset_control_bulk_data *data __free(kfree) =
>> +		kcalloc(num_resets, sizeof(*data), GFP_KERNEL);
>> +
>> +	if (!data)
>> +		return -ENOMEM;
>> +
>> +	for (u8 i = 0; i < num_resets; i++)
>> +		data[i].rstc = resets[i];
>> +
>> +	return action(num_resets, data);
>> +}
> 
> What is the purpose of this? Can't you just store struct
> reset_control_bulk_data in struct rzg3s_pcie_host and call
> reset_control_bulk_assert/deassert() directly?

Yes, I can. I was trying to avoid storing also the reset_control_bulk_data
in struct rzg3s_pcie_host since all that is needed can be retrieved from
the already parsed in probe cfg_resets and power_resets.

> 
>> +static int
>> +rzg3s_pcie_resets_init(struct device *dev, struct reset_control ***resets,
>> +		       struct reset_control *(*action)(struct device *dev, const char *id),
>> +		       const char * const *reset_names, u8 num_resets)
>> +{
>> +	*resets = devm_kcalloc(dev, num_resets, sizeof(struct reset_control *), GFP_KERNEL);
>> +	if (!*resets)
>> +		return -ENOMEM;
>> +
>> +	for (u8 i = 0; i < num_resets; i++) {
>> +		(*resets)[i] = action(dev, reset_names[i]);
>> +		if (IS_ERR((*resets)[i]))
>> +			return PTR_ERR((*resets)[i]);
>> +	}
>> +
>> +	return 0;
>> +}
> 
> Why not use devm_reset_control_bulk_get_exclusive() directly?

I wasn't able to find a bulk_get_exclusive_deasserted() kind of API.

This IP needs particular sequence for configuration. First, after power on,
the following resets need to be de-asserted:

	const char * const power_resets[] = {
		"aresetn", "rst_cfg_b", "rst_load_b",
	};

then, after proper values are written into the configuration registers, the
rest of the resets need to be de-asserted:

	const char * const cfg_resets[] = {
		"rst_b", "rst_ps_b", "rst_gp_b", "rst_rsm_b",
	};

So I was trying to get and de-assert the power_resets in probe and just get
the cfg_resets in the 1st step of the initialization, and later to
de-assert the cfg_resets as well.

Now, after you pointed it out, maybe you are proposing to just
get_exclusive everything in one shot and then to de-assert what is needed
at proper moments with generic reset control APIs?

Thank you for your review,
Claudiu


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

* Re: [PATCH 3/8] of/irq: Export of_irq_count()
  2025-04-30 10:32 ` [PATCH 3/8] of/irq: Export of_irq_count() Claudiu
@ 2025-05-09 19:36   ` Rob Herring
  0 siblings, 0 replies; 28+ messages in thread
From: Rob Herring @ 2025-05-09 19:36 UTC (permalink / raw)
  To: Claudiu
  Cc: bhelgaas, lpieralisi, kw, manivannan.sadhasivam, krzk+dt,
	conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, linux-arm-kernel, linux-clk, Claudiu Beznea

On Wed, Apr 30, 2025 at 01:32:31PM +0300, Claudiu wrote:
> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> 
> Export of_irq_count() to be able to use it in modules.
> 
> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> ---
>  drivers/of/irq.c | 1 +
>  1 file changed, 1 insertion(+)

Already have a similar patch pending.

Rob

[1] https://lore.kernel.org/all/20250331230034.806124-2-willmcvicker@google.com/


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

* Re: [PATCH 5/8] PCI: rzg3s-host: Add Initial PCIe Host Driver for Renesas RZ/G3S SoC
  2025-04-30 10:32 ` [PATCH 5/8] PCI: rzg3s-host: Add Initial PCIe Host Driver for Renesas RZ/G3S SoC Claudiu
  2025-05-01 20:12   ` Bjorn Helgaas
  2025-05-09 10:51   ` Philipp Zabel
@ 2025-05-09 20:49   ` Rob Herring
  2025-05-14 11:39     ` Claudiu Beznea
  2 siblings, 1 reply; 28+ messages in thread
From: Rob Herring @ 2025-05-09 20:49 UTC (permalink / raw)
  To: Claudiu
  Cc: bhelgaas, lpieralisi, kw, manivannan.sadhasivam, krzk+dt,
	conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, linux-arm-kernel, linux-clk, Claudiu Beznea

On Wed, Apr 30, 2025 at 01:32:33PM +0300, Claudiu wrote:
> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> 
> The Renesas RZ/G3S features a PCIe IP that complies with the PCI Express
> Base Specification 4.0 and supports speeds of up to 5 GT/s. It functions
> only as a root complex, with a single-lane (x1) configuration. The
> controller includes Type 1 configuration registers, as well as IP
> specific registers (called AXI registers) required for various adjustments.
> 
> Other Renesas RZ SoCs (e.g., RZ/G3E, RZ/V2H) share the same AXI registers
> but have both Root Complex and Endpoint capabilities. As a result, the PCIe
> host driver can be reused for these variants with minimal adjustments.
> 
> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> ---
>  MAINTAINERS                              |    8 +
>  drivers/pci/controller/Kconfig           |    7 +
>  drivers/pci/controller/Makefile          |    1 +
>  drivers/pci/controller/pcie-rzg3s-host.c | 1561 ++++++++++++++++++++++
>  4 files changed, 1577 insertions(+)
>  create mode 100644 drivers/pci/controller/pcie-rzg3s-host.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index e2e2942779eb..d7d985d174e2 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -19039,6 +19039,14 @@ S:	Maintained
>  F:	drivers/pci/controller/dwc/pcie-qcom-common.c
>  F:	drivers/pci/controller/dwc/pcie-qcom.c
>  
> +PCIE DRIVER FOR RENESAS RZ/G3S SERIES
> +M:	Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> +L:	linux-pci@vger.kernel.org
> +L:	linux-renesas-soc@vger.kernel.org
> +S:	Supported
> +F:	Documentation/devicetree/bindings/pci/renesas,r9a08g045s33-pcie.yaml
> +F:	drivers/pci/controller/pcie-rzg3s-host.c
> +
>  PCIE DRIVER FOR ROCKCHIP
>  M:	Shawn Lin <shawn.lin@rock-chips.com>
>  L:	linux-pci@vger.kernel.org
> diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
> index 9800b7681054..102f361fcc63 100644
> --- a/drivers/pci/controller/Kconfig
> +++ b/drivers/pci/controller/Kconfig
> @@ -256,6 +256,13 @@ config PCI_RCAR_GEN2
>  	  There are 3 internal PCI controllers available with a single
>  	  built-in EHCI/OHCI host controller present on each one.
>  
> +config PCIE_RENESAS_RZG3S_HOST
> +	tristate "Renesas RZ/G3S PCIe host controller"
> +	depends on ARCH_RENESAS || COMPILE_TEST
> +	select MFD_SYSCON
> +	help
> +	  Say Y here if you want PCIe host controller support on Renesas RZ/G3S SoC.
> +
>  config PCIE_ROCKCHIP
>  	bool
>  	depends on PCI
> diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile
> index 038ccbd9e3ba..229929a945c2 100644
> --- a/drivers/pci/controller/Makefile
> +++ b/drivers/pci/controller/Makefile
> @@ -10,6 +10,7 @@ obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o
>  obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
>  obj-$(CONFIG_PCIE_RCAR_HOST) += pcie-rcar.o pcie-rcar-host.o
>  obj-$(CONFIG_PCIE_RCAR_EP) += pcie-rcar.o pcie-rcar-ep.o
> +obj-$(CONFIG_PCIE_RENESAS_RZG3S_HOST) += pcie-rzg3s-host.o
>  obj-$(CONFIG_PCI_HOST_COMMON) += pci-host-common.o
>  obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o
>  obj-$(CONFIG_PCI_HOST_THUNDER_ECAM) += pci-thunder-ecam.o
> diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c
> new file mode 100644
> index 000000000000..c3bce0acd57e
> --- /dev/null
> +++ b/drivers/pci/controller/pcie-rzg3s-host.c
> @@ -0,0 +1,1561 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * PCIe driver for Renesas RZ/G3S SoCs
> + *
> + * Copyright (C) 2025 Renesas Electronics Corp.
> + *
> + * Based on:
> + *  drivers/pci/controller/pcie-rcar-host.c
> + *  Copyright (C) 2009 - 2011  Paul Mundt
> + */
> +
> +#include <linux/bitmap.h>
> +#include <linux/bitops.h>
> +#include <linux/cleanup.h>
> +#include <linux/delay.h>
> +#include <linux/device/devres.h>
> +#include <linux/iopoll.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/irqdomain.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/msi.h>
> +#include <linux/of_irq.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +#include <linux/slab.h>
> +
> +/* AXI registers. */
> +#define RZG3S_PCI_REQDATA(id)			(0x80 + (id) * 0x4)
> +#define RZG3S_PCI_REQRCVDAT			0x8c
> +#define RZG3S_PCI_REQADR1			0x90
> +#define RZG3S_PCI_REQADR1_BUS			GENMASK(31, 24)
> +#define RZG3S_PCI_REQADR1_DEV			GENMASK(23, 19)
> +#define RZG3S_PCI_REQADR1_FUNC			GENMASK(18, 16)
> +#define RZG3S_PCI_REQADR1_REG			GENMASK(11, 0)
> +#define RZG3S_PCI_REQBE				0x98
> +#define RZG3S_PCI_REQBE_BYTE_EN			GENMASK(3, 0)
> +#define RZG3S_PCI_REQISS			0x9c
> +#define RZG3S_PCI_REQISS_REQ_ISSUE		BIT(0)
> +#define RZG3S_PCI_REQISS_TR_TYPE		GENMASK(11, 8)
> +#define RZG3S_PCI_REQISS_TR_TP0_RD		FIELD_PREP(RZG3S_PCI_REQISS_TR_TYPE, 0x4)
> +#define RZG3S_PCI_REQISS_TR_TP0_WR		FIELD_PREP(RZG3S_PCI_REQISS_TR_TYPE, 0x5)
> +#define RZG3S_PCI_REQISS_TR_TP1_RD		FIELD_PREP(RZG3S_PCI_REQISS_TR_TYPE, 0x6)
> +#define RZG3S_PCI_REQISS_TR_TP1_WR		FIELD_PREP(RZG3S_PCI_REQISS_TR_TYPE, 0x7)
> +#define RZG3S_PCI_REQISS_MOR_STATUS		GENMASK(18, 16)
> +#define RZG3S_PCI_MSIRCVWADRL			0x100
> +#define RZG3S_PCI_MSIRCVWADRL_ENA		BIT(0)
> +#define RZG3S_PCI_MSIRCVWADRL_MSG_DATA_ENA	BIT(1)
> +#define RZG3S_PCI_MSIRCVWADRU			0x104
> +#define RZG3S_PCI_MSIRCVWMSKL			0x108
> +#define RZG3S_PCI_MSIRCVWMSKU			0x10c
> +#define RZG3S_PCI_PINTRCVIE			0x110
> +#define RZG3S_PCI_PINTRCVIE_INTX(i)		BIT(i)
> +#define RZG3S_PCI_PINTRCVIE_MSI			BIT(4)
> +#define RZG3S_PCI_PINTRCVIS			0x114
> +#define RZG3S_PCI_PINTRCVIS_INTX(i)		BIT(i)
> +#define RZG3S_PCI_PINTRCVIS_MSI			BIT(4)
> +#define RZG3S_PCI_MSGRCVIE			0x120
> +#define RZG3S_PCI_MSGRCVIE_MSG_RCV		BIT(24)
> +#define RZG3S_PCI_MSGRCVIS			0x124
> +#define RZG3S_PCI_MSGRCVIS_MRI			BIT(24)
> +#define RZG3S_PCI_PEIE0				0x200
> +#define RZG3S_PCI_PEIS0				0x204
> +#define RZG3S_PCI_PEIS0_DL_UPDOWN		BIT(9)
> +#define RZG3S_PCI_PEIS0_RX_DLLP_PM_ENTER	BIT(12)
> +#define RZG3S_PCI_PEIE1				0x208
> +#define RZG3S_PCI_PEIS1				0x20c
> +#define RZG3S_PCI_AMEIE				0x210
> +#define RZG3S_PCI_AMEIS				0x214
> +#define RZG3S_PCI_ASEIE1			0x220
> +#define RZG3S_PCI_ASEIS1			0x224
> +#define RZG3S_PCI_PCSTAT1			0x408
> +#define RZG3S_PCI_PCSTAT1_DL_DOWN_STS		BIT(0)
> +#define RZG3S_PCI_PCSTAT1_LTSSM_STATE		GENMASK(14, 10)
> +#define RZG3S_PCI_PCCTRL2			0x410
> +#define RZG3S_PCI_PCCTRL2_LS_CHG_REQ		BIT(0)
> +#define RZG3S_PCI_PCCTRL2_LS_CHG		GENMASK(9, 8)
> +#define RZG3S_PCI_PCSTAT2			0x414
> +#define RZG3S_PCI_PCSTAT2_STATE_RX_DETECT	GENMASK(15, 8)
> +#define RZG3S_PCI_PCSTAT2_SDRIRE		GENMASK(7, 0)
> +#define RZG3S_PCI_PCSTAT2_LS_CHG_DONE		BIT(28)
> +#define RZG3S_PCI_PERM				0x300
> +#define RZG3S_PCI_PERM_PIPE_PHY_REG_EN		BIT(1)
> +#define RZG3S_PCI_PERM_CFG_HWINIT_EN		BIT(2)
> +#define RZG3S_PCI_MSIRE(id)			(0x600 + (id) * 0x10)
> +#define RZG3S_PCI_MSIRE_ENA			BIT(0)
> +#define RZG3S_PCI_MSIRM(id)			(0x608 + (id) * 0x10)
> +#define RZG3S_PCI_MSIRS(id)			(0x60c + (id) * 0x10)
> +#define RZG3S_PCI_AWBASEL(id)			(0x1000 + (id) * 0x20)
> +#define RZG3S_PCI_AWBASEL_WIN_ENA		BIT(0)
> +#define RZG3S_PCI_AWBASEU(id)			(0x1004 + (id) * 0x20)
> +#define RZG3S_PCI_AWMASKL(id)			(0x1008 + (id) * 0x20)
> +#define RZG3S_PCI_AWMASKU(id)			(0x100c + (id) * 0x20)
> +#define RZG3S_PCI_ADESTL(id)			(0x1010 + (id) * 0x20)
> +#define RZG3S_PCI_ADESTU(id)			(0x1014 + (id) * 0x20)
> +#define RZG3S_PCI_PWBASEL(id)			(0x1100 + (id) * 0x20)
> +#define RZG3S_PCI_PWBASEL_ENA			BIT(0)
> +#define RZG3S_PCI_PWBASEU(id)			(0x1104 + (id) * 0x20)
> +#define RZG3S_PCI_PDESTL(id)			(0x1110 + (id) * 0x20)
> +#define RZG3S_PCI_PDESTU(id)			(0x1114 + (id) * 0x20)
> +#define RZG3S_PCI_PWMASKL(id)			(0x1108 + (id) * 0x20)
> +#define RZG3S_PCI_PWMASKU(id)			(0x110c + (id) * 0x20)
> +
> +/* PHY control registers. */
> +#define RZG3S_PCI_PHY_XCFGD(id)			(0x2000 + (id) * 0x10)
> +#define RZG3S_PCI_PHY_XCFGD_NUM			39
> +#define RZG3S_PCI_PHY_XCFGA_CMN(id)		(0x2400 + (id) * 0x10)
> +#define RZG3S_PCI_PHY_XCFGA_CMN_NUM		16
> +#define RZG3S_PCI_PHY_XCFGA_RX(id)		(0x2500 + (id) * 0x10)
> +#define RZG3S_PCI_PHY_XCFGA_RX_NUM		13
> +#define RZG3S_PCI_PHY_XCFGA_TX			0x25d0
> +#define RZG3S_PCI_PHY_XCFG_CTRL			0x2a20
> +#define RZG3S_PCI_PHY_XCFG_CTRL_PHYREG_SEL	BIT(0)

Some reason the PHY can't be a PHY driver? Registers look somewhat 
separate at least.

> +
> +/* PCIe registers. */
> +#define RZG3S_PCI_CFG_BASE			0x6000
> +#define RZG3S_PCI_CFG_VID			0x0

Don't redefine standard config space defines. This is just 
PCI_VENDOR_ID.

> +#define RZG3S_PCI_CFG_VID_DEVICE		GENMASK(31, 16)
> +#define RZG3S_PCI_CFG_VID_VENDOR		GENMASK(15, 0)
> +#define RZG3S_PCI_CFG_BNR			0x18

This is PCI_PRIMARY_BUS.

> +#define RZG3S_PCI_CFG_BNR_SUBORDINATE_BUS	GENMASK(23, 16)
> +#define RZG3S_PCI_CFG_BNR_SECONDARY_BUS		GENMASK(15, 8)
> +#define RZG3S_PCI_CFG_BARMSK00L			0xa0
> +#define RZG3S_PCI_CFG_BARMSK00U			0xa4
> +#define RZG3S_PCI_CFG_LINKCS			0x70
> +#define RZG3S_PCI_CFG_LINKCS_CUR_LS		GENMASK(19, 16)
> +#define RZG3S_PCI_CFG_LINCS2			0x90
> +#define RZG3S_PCI_CFG_LINCS2_TARGET_LS		GENMASK(3, 0)
> +
> +/* System controller registers. */
> +#define RZG3S_SYS_PCIE_RST_RSM_B		0xd74
> +#define RZG3S_SYS_PCIE_RST_RSM_B_MASK		BIT(0)
> +
> +/* Maximum number of windows. */
> +#define RZG3S_MAX_WINDOWS			8
> +
> +/* Number of MSI interrupts per register. */
> +#define RZG3S_PCI_MSI_INT_PER_REG		32
> +/* The number of MSI interrupts. */
> +#define RZG3S_PCI_MSI_INT_NR			(RZG3S_PCI_MSI_INT_PER_REG * 2)
> +
> +/**
> + * enum rzg3s_pcie_link_speed - RZ/G3S PCIe available link speeds
> + * @RZG3S_PCIE_LINK_SPEED_2_5_GTS: 2.5 GT/s
> + * @RZG3S_PCIE_LINK_SPEED_5_0_GTS: 5.0 GT/s
> + */
> +enum rzg3s_pcie_link_speed {
> +	RZG3S_PCIE_LINK_SPEED_2_5_GTS = 1,
> +	RZG3S_PCIE_LINK_SPEED_5_0_GTS
> +};
> +
> +/**
> + * struct rzg3s_pcie_msi - RZ/G3S PCIe MSI data structure
> + * @domain: IRQ domain
> + * @map: bitmap with the allocated MSIs
> + * @dma_addr: address of the allocated MSI window
> + * @window_base: base address of the MSI window
> + * @pages: allocated pages for MSI window mapping
> + * @map_lock: lock for bitmap with the allocated MSIs
> + */
> +struct rzg3s_pcie_msi {
> +	struct irq_domain *domain;
> +	DECLARE_BITMAP(map, RZG3S_PCI_MSI_INT_NR);
> +	dma_addr_t dma_addr;
> +	dma_addr_t window_base;
> +	unsigned long pages;
> +	struct mutex map_lock;
> +};
> +
> +/**
> + * struct rzg3s_pcie_host - RZ/G3S PCIe data structure
> + * @axi: base address for AXI registers
> + * @pcie: base address for PCIe registers
> + * @dev: struct device
> + * @power_resets: reset control signals that should be set after power up
> + * @cfg_resets: reset control signals that should be set after configuration
> + * @phy_init: PHY specific initialization function
> + * @sysc: SYSC regmap
> + * @intx_domain: INTx IRQ domain
> + * @devres_group_id: devres group which allows unconfiguring everything at
> + * the end of the driver remove function (or, on probe failure path, just after
> + * leaving the driver probe function); otherwise, the dev_pm_domain_detach()
> + * called in the platform driver remove function disable the PCIe clocks leaving
> + * the other action or reset function being executed (later) with clocks disabled;
> + * this leads to system being blocked and eventually restarted by watchdog
> + * @msi: MSI data structure
> + * @hw_lock: lock for access to the HW resources
> + * @intx_irqs: INTx interrupts
> + * @vendor_id: Vendor ID
> + * @device_id: Device ID
> + * @num_power_resets: the number of power_resets
> + * @num_cfg_resets: the number of cfg_resets
> + */
> +struct rzg3s_pcie_host {
> +	void __iomem *axi;
> +	void __iomem *pcie;
> +	struct device *dev;
> +	struct reset_control **power_resets;
> +	struct reset_control **cfg_resets;
> +	int (*phy_init)(struct rzg3s_pcie_host *host);
> +	struct regmap *sysc;
> +	struct irq_domain *intx_domain;
> +	void *devres_group_id;
> +	struct rzg3s_pcie_msi msi;
> +	raw_spinlock_t hw_lock;
> +	int intx_irqs[PCI_NUM_INTX];
> +	u32 vendor_id;
> +	u32 device_id;
> +	u8 num_power_resets;
> +	u8 num_cfg_resets;
> +};
> +
> +#define rzg3s_msi_to_host(_msi)	container_of(_msi, struct rzg3s_pcie_host, msi)
> +
> +/**
> + * enum rzg3s_pcie_cfg_access_type - RZ/G3S PCIe access type
> + * @RZG3S_PCIE_CFG_ACCESS_RD: read access
> + * @RZG3S_PCIE_CFG_ACCESS_WR: write access
> + */
> +enum rzg3s_pcie_cfg_access_type {
> +	RZG3S_PCIE_CFG_ACCESS_RD,
> +	RZG3S_PCIE_CFG_ACCESS_WR,
> +};
> +
> +static void rzg3s_pcie_update_bits(void __iomem *base, u32 offset, u32 mask, u32 val)
> +{
> +	u32 tmp;
> +
> +	tmp = readl(base + offset);
> +	tmp &= ~mask;
> +	tmp |= val & mask;
> +	writel(tmp, base + offset);
> +}
> +
> +static bool rzg3s_pcie_issue_request(struct rzg3s_pcie_host *host)
> +{
> +	u32 val;
> +	int ret;
> +
> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_REQISS, RZG3S_PCI_REQISS_REQ_ISSUE,
> +			       RZG3S_PCI_REQISS_REQ_ISSUE);
> +	ret = readl_poll_timeout_atomic(host->axi + RZG3S_PCI_REQISS, val,
> +					!(val & RZG3S_PCI_REQISS_REQ_ISSUE),
> +					5, 2500);
> +
> +	return !!ret || (val & RZG3S_PCI_REQISS_MOR_STATUS);
> +}
> +
> +static int rzg3s_pcie_config_access(struct rzg3s_pcie_host *host, struct pci_bus *bus,
> +				    unsigned int devfn, int where, u32 *data,
> +				    enum rzg3s_pcie_cfg_access_type access_type)
> +{
> +	bool type0 = pci_is_root_bus(bus->parent) ? true : false;
> +	unsigned int dev, func, reg, tr_type;
> +	int ret;
> +
> +	dev = PCI_SLOT(devfn);
> +	func = PCI_FUNC(devfn);
> +	reg = where & ~0x3;
> +
> +	/* Set the destination. */
> +	writel(FIELD_PREP(RZG3S_PCI_REQADR1_BUS, bus->number) |
> +	       FIELD_PREP(RZG3S_PCI_REQADR1_DEV, dev) |
> +	       FIELD_PREP(RZG3S_PCI_REQADR1_FUNC, func) |
> +	       FIELD_PREP(RZG3S_PCI_REQADR1_REG, reg),
> +	       host->axi + RZG3S_PCI_REQADR1);
> +
> +	/* Set byte enable. */
> +	writel(RZG3S_PCI_REQBE_BYTE_EN, host->axi + RZG3S_PCI_REQBE);

All the above can go in .map_bus().

> +
> +	if (access_type == RZG3S_PCIE_CFG_ACCESS_RD) {
> +		tr_type = type0 ? RZG3S_PCI_REQISS_TR_TP0_RD : RZG3S_PCI_REQISS_TR_TP1_RD;
> +	} else {
> +		tr_type = type0 ? RZG3S_PCI_REQISS_TR_TP0_WR : RZG3S_PCI_REQISS_TR_TP1_WR;
> +
> +		/* Set the write data.  */
> +		writel(0, host->axi + RZG3S_PCI_REQDATA(0));
> +		writel(0, host->axi + RZG3S_PCI_REQDATA(1));
> +		writel(*data, host->axi + RZG3S_PCI_REQDATA(2));
> +	}
> +
> +	/* Set the type of request. */
> +	writel(tr_type, host->axi + RZG3S_PCI_REQISS);

Split all this into read and write helpers or just move these to the 
callers. IOW, get rid of all the 'if read' or 'if write' conditionals.

> +
> +	/* Issue the request and wait to finish. */
> +	ret = rzg3s_pcie_issue_request(host);
> +	if (ret) {
> +		if (access_type == RZG3S_PCIE_CFG_ACCESS_RD)
> +			PCI_SET_ERROR_RESPONSE(data);
> +
> +		return PCIBIOS_SET_FAILED;
> +	}
> +
> +	/* Read the data. */
> +	if (access_type == RZG3S_PCIE_CFG_ACCESS_RD)
> +		*data = readl(host->axi + RZG3S_PCI_REQRCVDAT);
> +
> +	return PCIBIOS_SUCCESSFUL;
> +}
> +
> +static int rzg3s_pcie_read_config_access(struct rzg3s_pcie_host *host, struct pci_bus *bus,
> +					 unsigned int devfn, int where, u32 *data)
> +{
> +	/* Controller can't address itself. Just read the registers. */
> +	if (pci_is_root_bus(bus) && !devfn) {

There are 2 sets of bus ops for root bus and child buses. Use them.

> +		u32 reg = where & ~0x3;
> +
> +		*data = readl(host->pcie + reg);
> +		return PCIBIOS_SUCCESSFUL;
> +	}
> +
> +	return rzg3s_pcie_config_access(host, bus, devfn, where, data, RZG3S_PCIE_CFG_ACCESS_RD);
> +}
> +
> +static int rzg3s_pcie_read_conf(struct pci_bus *bus, unsigned int devfn, int where, int size,
> +				u32 *val)
> +{
> +	struct rzg3s_pcie_host *host = bus->sysdata;
> +	int ret;
> +
> +	/*
> +	 * Bus number for root complex is set to zero. Skip other requests for
> +	 * vendor and device ID (where = 0x00).
> +	 */
> +	if (pci_is_root_bus(bus) && PCI_SLOT(devfn) && !where)
> +		return PCIBIOS_DEVICE_NOT_FOUND;
> +
> +	ret = rzg3s_pcie_read_config_access(host, bus, devfn, where, val);
> +	if (ret != PCIBIOS_SUCCESSFUL)
> +		return ret;
> +
> +	if (size == 1)
> +		*val = (*val >> (BITS_PER_BYTE * (where & 3))) & 0xff;
> +	else if (size == 2)
> +		*val = (*val >> (BITS_PER_BYTE * (where & 2))) & 0xffff;

If you can't use the core implementation, at least copy it:

        if (size <= 2)
                *val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1);

> +
> +	dev_dbg(&bus->dev, "%s(): bus=%3d devfn=0x%04x where=0x%04x size=%d val=0x%08x\n",
> +		__func__, bus->number, devfn, where, size, *val);

If this kind of tracing is important, put it in the core code for 
everyone to use. Might already be there, I don't remember.

> +
> +	return PCIBIOS_SUCCESSFUL;
> +}
> +
> +static int rzg3s_pcie_write_config_access(struct rzg3s_pcie_host *host, struct pci_bus *bus,
> +					  unsigned int devfn, int where, u32 data)
> +{
> +	/* Controller can't target itself. */
> +	if (pci_is_root_bus(bus) && !devfn) {
> +		u32 reg = where & ~0x3;
> +
> +		/* Enable access control to the CFGU. */
> +		writel(RZG3S_PCI_PERM_CFG_HWINIT_EN, host->axi + RZG3S_PCI_PERM);
> +		/* Write data. */
> +		writel(data, host->pcie + reg);
> +		/* Disable access control to the CFGU. */
> +		writel(0, host->axi + RZG3S_PCI_PERM);

If you make this just a root bus write op, I think this could be:

{
	writel(RZG3S_PCI_PERM_CFG_HWINIT_EN, host->axi + RZG3S_PCI_PERM);
	ret = pci_generic_config_write32(...);
	writel(0, host->axi + RZG3S_PCI_PERM);

	return ret;
}

> +
> +		return PCIBIOS_SUCCESSFUL;
> +	}
> +
> +	return rzg3s_pcie_config_access(host, bus, devfn, where, &data, RZG3S_PCIE_CFG_ACCESS_WR);
> +}
> +
> +static int rzg3s_pcie_write_conf(struct pci_bus *bus, unsigned int devfn, int where, int size,
> +				 u32 val)
> +{
> +	struct rzg3s_pcie_host *host = bus->sysdata;
> +	u32 shift, data;
> +	int ret;
> +
> +	ret = rzg3s_pcie_read_config_access(host, bus, devfn, where, &data);
> +	if (ret != PCIBIOS_SUCCESSFUL)
> +		return ret;
> +
> +	dev_dbg(&bus->dev, "%s(): bus=%3d devfn=0x%04x where=0x%04x size=%d val=0x%08x\n",
> +		__func__, bus->number, devfn, where, size, val);
> +
> +	if (size == 1) {
> +		shift = BITS_PER_BYTE * (where & 3);
> +		data &= ~(0xff << shift);
> +		data |= ((val & 0xff) << shift);
> +	} else if (size == 2) {
> +		shift = BITS_PER_BYTE * (where & 2);
> +		data &= ~(0xffff << shift);
> +		data |= ((val & 0xffff) << shift);
> +	} else {
> +		data = val;
> +	}
> +
> +	return rzg3s_pcie_write_config_access(host, bus, devfn, where, data);
> +}
> +
> +static struct pci_ops rzg3s_pcie_ops = {
> +	.read	= rzg3s_pcie_read_conf,
> +	.write	= rzg3s_pcie_write_conf,
> +};
> +
> +static void rzg3s_pcie_intx_irq_handler(struct irq_desc *desc)
> +{
> +	struct rzg3s_pcie_host *host = irq_desc_get_handler_data(desc);
> +	struct irq_chip *chip = irq_desc_get_chip(desc);
> +	unsigned int irq = irq_desc_get_irq(desc);
> +	u32 intx = irq - host->intx_irqs[0];
> +
> +	chained_irq_enter(chip, desc);
> +	generic_handle_domain_irq(host->intx_domain, intx);
> +	chained_irq_exit(chip, desc);
> +}
> +
> +static irqreturn_t rzg3s_pcie_msi_irq(int irq, void *data)
> +{
> +	DECLARE_BITMAP(bitmap, RZG3S_PCI_MSI_INT_NR);
> +	struct rzg3s_pcie_host *host = data;
> +	struct rzg3s_pcie_msi *msi = &host->msi;
> +	unsigned long bit;
> +	u32 status;
> +
> +	status = readl(host->axi + RZG3S_PCI_PINTRCVIS);
> +	if (!(status & RZG3S_PCI_PINTRCVIS_MSI))
> +		return IRQ_NONE;
> +
> +	/* Clear the MSI. */
> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PINTRCVIS, RZG3S_PCI_PINTRCVIS_MSI,
> +			       RZG3S_PCI_PINTRCVIS_MSI);
> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_MSGRCVIS, RZG3S_PCI_MSGRCVIS_MRI,
> +			       RZG3S_PCI_MSGRCVIS_MRI);
> +
> +	for (u8 reg_id = 0; reg_id < RZG3S_PCI_MSI_INT_NR / RZG3S_PCI_MSI_INT_PER_REG; reg_id++) {
> +		status = readl(host->axi + RZG3S_PCI_MSIRS(reg_id));
> +		bitmap_write(bitmap, status, reg_id * RZG3S_PCI_MSI_INT_PER_REG,
> +			     RZG3S_PCI_MSI_INT_PER_REG);
> +	}
> +
> +	for_each_set_bit(bit, bitmap, RZG3S_PCI_MSI_INT_NR) {
> +		int ret;
> +
> +		ret = generic_handle_domain_irq(msi->domain->parent, bit);
> +		if (ret) {
> +			u8 reg_bit = bit % RZG3S_PCI_MSI_INT_PER_REG;
> +			u8 reg_id = bit / RZG3S_PCI_MSI_INT_PER_REG;
> +
> +			/* Unknown MSI, just clear it. */
> +			writel(BIT(reg_bit), host->axi + RZG3S_PCI_MSIRS(reg_id));
> +		}
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void rzg3s_pcie_msi_top_irq_ack(struct irq_data *d)
> +{
> +	irq_chip_ack_parent(d);
> +}
> +
> +static void rzg3s_pcie_msi_top_irq_mask(struct irq_data *d)
> +{
> +	pci_msi_mask_irq(d);
> +	irq_chip_mask_parent(d);
> +}
> +
> +static void rzg3s_pcie_msi_top_irq_unmask(struct irq_data *d)
> +{
> +	pci_msi_unmask_irq(d);
> +	irq_chip_unmask_parent(d);
> +}
> +
> +static struct irq_chip rzg3s_pcie_msi_top_chip = {
> +	.name		= "PCIe MSI",
> +	.irq_ack	= rzg3s_pcie_msi_top_irq_ack,
> +	.irq_mask	= rzg3s_pcie_msi_top_irq_mask,
> +	.irq_unmask	= rzg3s_pcie_msi_top_irq_unmask,
> +};
> +
> +static void rzg3s_pcie_msi_irq_ack(struct irq_data *d)
> +{
> +	struct rzg3s_pcie_msi *msi = irq_data_get_irq_chip_data(d);
> +	struct rzg3s_pcie_host *host = rzg3s_msi_to_host(msi);
> +	u8 reg_bit = d->hwirq % RZG3S_PCI_MSI_INT_PER_REG;
> +	u8 reg_id = d->hwirq / RZG3S_PCI_MSI_INT_PER_REG;
> +
> +	guard(raw_spinlock_irqsave)(&host->hw_lock);
> +
> +	writel(BIT(reg_bit), host->axi + RZG3S_PCI_MSIRS(reg_id));
> +}
> +
> +static void rzg3s_pcie_msi_irq_mask(struct irq_data *d)
> +{
> +	struct rzg3s_pcie_msi *msi = irq_data_get_irq_chip_data(d);
> +	struct rzg3s_pcie_host *host = rzg3s_msi_to_host(msi);
> +	u8 reg_bit = d->hwirq % RZG3S_PCI_MSI_INT_PER_REG;
> +	u8 reg_id = d->hwirq / RZG3S_PCI_MSI_INT_PER_REG;
> +
> +	guard(raw_spinlock_irqsave)(&host->hw_lock);
> +
> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_MSIRM(reg_id), BIT(reg_bit), BIT(reg_bit));
> +}
> +
> +static void rzg3s_pcie_msi_irq_unmask(struct irq_data *d)
> +{
> +	struct rzg3s_pcie_msi *msi = irq_data_get_irq_chip_data(d);
> +	struct rzg3s_pcie_host *host = rzg3s_msi_to_host(msi);
> +	u8 reg_bit = d->hwirq % RZG3S_PCI_MSI_INT_PER_REG;
> +	u8 reg_id = d->hwirq / RZG3S_PCI_MSI_INT_PER_REG;
> +
> +	guard(raw_spinlock_irqsave)(&host->hw_lock);
> +
> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_MSIRM(reg_id), BIT(reg_bit), 0);
> +}
> +
> +static void rzg3s_pcie_msi_compose_msg(struct irq_data *data, struct msi_msg *msg)
> +{
> +	struct rzg3s_pcie_msi *msi = irq_data_get_irq_chip_data(data);
> +	struct rzg3s_pcie_host *host = rzg3s_msi_to_host(msi);
> +	u32 lo, hi;
> +
> +	/* Enable and msg data enable bits are part of the address lo. Drop them. */
> +	lo = readl(host->axi + RZG3S_PCI_MSIRCVWADRL) &
> +		   ~(RZG3S_PCI_MSIRCVWADRL_ENA | RZG3S_PCI_MSIRCVWADRL_MSG_DATA_ENA);
> +	hi = readl(host->axi + RZG3S_PCI_MSIRCVWADRU);
> +
> +	msg->address_lo = lo;
> +	msg->address_hi = hi;
> +	msg->data = data->hwirq;
> +}
> +
> +static struct irq_chip rzg3s_pcie_msi_bottom_chip = {
> +	.name			= "rz-pcie-msi",
> +	.irq_ack		= rzg3s_pcie_msi_irq_ack,
> +	.irq_mask		= rzg3s_pcie_msi_irq_mask,
> +	.irq_unmask		= rzg3s_pcie_msi_irq_unmask,
> +	.irq_compose_msi_msg	= rzg3s_pcie_msi_compose_msg,
> +};
> +
> +static int rzg3s_pcie_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +				       unsigned int nr_irqs, void *args)
> +{
> +	struct rzg3s_pcie_msi *msi = domain->host_data;
> +	int hwirq;
> +
> +	scoped_guard(mutex, &msi->map_lock) {
> +		hwirq = bitmap_find_free_region(msi->map, RZG3S_PCI_MSI_INT_NR,
> +						order_base_2(nr_irqs));
> +	}
> +
> +	if (hwirq < 0)
> +		return -ENOSPC;
> +
> +	for (unsigned int i = 0; i < nr_irqs; i++) {
> +		irq_domain_set_info(domain, virq + i, hwirq + i, &rzg3s_pcie_msi_bottom_chip,
> +				    domain->host_data, handle_edge_irq, NULL, NULL);
> +	}
> +
> +	return 0;
> +}
> +
> +static void rzg3s_pcie_msi_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 rzg3s_pcie_msi *msi = domain->host_data;
> +
> +	guard(mutex)(&msi->map_lock);
> +
> +	bitmap_release_region(msi->map, d->hwirq, order_base_2(nr_irqs));
> +}
> +
> +static const struct irq_domain_ops rzg3s_pcie_msi_domain_ops = {
> +	.alloc	= rzg3s_pcie_msi_domain_alloc,
> +	.free	= rzg3s_pcie_msi_domain_free,
> +};
> +
> +static struct msi_domain_info rzg3s_pcie_msi_info = {
> +	.flags	= MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +		  MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_NO_AFFINITY,
> +	.chip	= &rzg3s_pcie_msi_top_chip,
> +};
> +
> +static int rzg3s_pcie_msi_allocate_domains(struct rzg3s_pcie_msi *msi)
> +{
> +	struct rzg3s_pcie_host *host = rzg3s_msi_to_host(msi);
> +	struct device *dev = host->dev;
> +	struct fwnode_handle *fwnode = dev_fwnode(dev);
> +	struct irq_domain *parent;
> +
> +	parent = irq_domain_create_linear(fwnode, RZG3S_PCI_MSI_INT_NR,
> +					  &rzg3s_pcie_msi_domain_ops, msi);
> +	if (!parent)
> +		return dev_err_probe(dev, -ENOMEM, "failed to create IRQ domain\n");
> +	irq_domain_update_bus_token(parent, DOMAIN_BUS_NEXUS);
> +
> +	msi->domain = pci_msi_create_irq_domain(fwnode, &rzg3s_pcie_msi_info, parent);
> +	if (!msi->domain) {
> +		irq_domain_remove(parent);
> +		return dev_err_probe(dev, -ENOMEM, "failed to create MSI domain\n");
> +	}
> +
> +	return 0;
> +}
> +
> +static void rzg3s_pcie_msi_free_domains(struct rzg3s_pcie_msi *msi)
> +{
> +	struct irq_domain *parent = msi->domain->parent;
> +
> +	irq_domain_remove(msi->domain);
> +	irq_domain_remove(parent);
> +}
> +
> +static int rzg3s_pcie_msi_hw_setup(struct rzg3s_pcie_host *host)
> +{
> +	struct rzg3s_pcie_msi *msi = &host->msi;
> +
> +	/* Set MSI window size. HW will set the window to RZG3S_PCI_MSI_INT_NR * 4 bytes. */
> +	writel(RZG3S_PCI_MSI_INT_NR - 1, host->axi + RZG3S_PCI_MSIRCVWMSKL);
> +
> +	/* Set MSI window address and enable MSI window. */
> +	writel(upper_32_bits(msi->window_base), host->axi + RZG3S_PCI_MSIRCVWADRU);
> +	writel(lower_32_bits(msi->window_base) | RZG3S_PCI_MSIRCVWADRL_ENA |
> +	       RZG3S_PCI_MSIRCVWADRL_MSG_DATA_ENA,
> +	       host->axi + RZG3S_PCI_MSIRCVWADRL);
> +
> +	/* Set MSI receive enable. */
> +	for (u8 reg_id = 0; reg_id < RZG3S_PCI_MSI_INT_NR / RZG3S_PCI_MSI_INT_PER_REG; reg_id++)
> +		writel(RZG3S_PCI_MSIRE_ENA, host->axi + RZG3S_PCI_MSIRE(reg_id));
> +
> +	/* Enable message receive interrupts. */
> +	writel(RZG3S_PCI_MSGRCVIE_MSG_RCV, host->axi + RZG3S_PCI_MSGRCVIE);
> +
> +	/* Enable MSI. */
> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PINTRCVIE, RZG3S_PCI_PINTRCVIE_MSI,
> +			       RZG3S_PCI_PINTRCVIE_MSI);
> +
> +	return 0;
> +}
> +
> +static int rzg3s_pcie_msi_setup(struct rzg3s_pcie_host *host)
> +{
> +	size_t size = RZG3S_PCI_MSI_INT_NR * sizeof(u32);
> +	struct rzg3s_pcie_msi *msi = &host->msi;
> +	struct device *dev = host->dev;
> +	int id, ret;
> +
> +	msi->pages = __get_free_pages(GFP_KERNEL | GFP_DMA, 0);
> +	if (!msi->pages)
> +		return -ENOMEM;
> +
> +	msi->dma_addr = dma_map_single(dev, (void *)msi->pages, size * 2, DMA_BIDIRECTIONAL);
> +	if (dma_mapping_error(dev, msi->dma_addr)) {
> +		ret = -ENOMEM;
> +		goto free_pages;
> +	}
> +
> +	/*
> +	 * According to the RZ/G3S HW manual (section 34.4.5.2 Setting the MSI Window)
> +	 * the MSI window need to be within any AXI window. Find an AXI window to setup
> +	 * the MSI window.
> +	 */
> +	for (id = 0; id < RZG3S_MAX_WINDOWS; id++) {
> +		u64 base, basel, baseu;
> +		u64 mask, maskl, masku;
> +
> +		basel = readl(host->axi + RZG3S_PCI_AWBASEL(id));
> +		/* Skip checking this AXI window if it's not enabled. */
> +		if (!(basel & RZG3S_PCI_AWBASEL_WIN_ENA))
> +			continue;
> +
> +		baseu = readl(host->axi + RZG3S_PCI_AWBASEU(id));
> +		base = baseu << 32 | basel;
> +
> +		maskl = readl(host->axi + RZG3S_PCI_AWMASKL(id));
> +		masku = readl(host->axi + RZG3S_PCI_AWMASKU(id));
> +		mask = masku << 32 | maskl;
> +
> +		if (msi->dma_addr < base || msi->dma_addr > base + mask)
> +			continue;
> +
> +		break;
> +	}
> +
> +	if (id == RZG3S_MAX_WINDOWS) {
> +		ret = -EINVAL;
> +		goto dma_unmap;
> +	}
> +
> +	/* The MSI base address need to be aligned to the MSI size. */
> +	msi->window_base = ALIGN(msi->dma_addr, size);
> +	if (msi->window_base < msi->dma_addr) {
> +		ret = -EINVAL;
> +		goto dma_unmap;
> +	}
> +
> +	rzg3s_pcie_msi_hw_setup(host);
> +
> +	return 0;
> +
> +dma_unmap:
> +	dma_unmap_single(dev, msi->dma_addr, size * 2, DMA_BIDIRECTIONAL);
> +free_pages:
> +	free_pages(msi->pages, 0);
> +	return ret;
> +}
> +
> +static int rzg3s_pcie_msi_enable(struct rzg3s_pcie_host *host)
> +{
> +	struct rzg3s_pcie_msi *msi = &host->msi;
> +	struct device *dev = host->dev;
> +	const char *devname;
> +	int irq, ret;
> +
> +	mutex_init(&msi->map_lock);
> +
> +	irq = of_irq_get_byname(dev->of_node, "intmsi_rc");
> +	if (irq <= 0)
> +		return dev_err_probe(dev, irq ? irq : -EINVAL, "Failed to get MSI IRQ!\n");
> +
> +	devname = devm_kasprintf(dev, GFP_KERNEL, "%s-msi", dev_name(dev));
> +	if (!devname)
> +		return -ENOMEM;
> +
> +	ret = rzg3s_pcie_msi_allocate_domains(msi);
> +	if (ret)
> +		return ret;
> +
> +	ret = devm_request_irq(dev, irq, rzg3s_pcie_msi_irq, 0, devname, host);
> +	if (ret) {
> +		dev_err_probe(dev, ret, "Failed to request IRQ: %d\n", ret);
> +		goto free_domains;
> +	}
> +
> +	ret = rzg3s_pcie_msi_setup(host);
> +	if (ret) {
> +		dev_err_probe(dev, ret, "Failed to setup MSI!\n");
> +		goto free_domains;
> +	}
> +
> +	return 0;
> +
> +free_domains:
> +	rzg3s_pcie_msi_free_domains(msi);
> +	return ret;
> +}
> +
> +static void rzg3s_pcie_msi_teardown(void *data)
> +{
> +	size_t size = RZG3S_PCI_MSI_INT_NR * sizeof(u32);
> +	struct rzg3s_pcie_host *host = data;
> +	struct rzg3s_pcie_msi *msi = &host->msi;
> +
> +	/* Disable MSI. */
> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PINTRCVIE, RZG3S_PCI_PINTRCVIE_MSI, 0);
> +
> +	/* Disable message receive interrupts. */
> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_MSGRCVIE, RZG3S_PCI_MSGRCVIE_MSG_RCV, 0);
> +
> +	/* Disable MSI receive enable. */
> +	for (u8 reg_id = 0; reg_id < RZG3S_PCI_MSI_INT_NR / RZG3S_PCI_MSI_INT_PER_REG; reg_id++)
> +		writel(0, host->axi + RZG3S_PCI_MSIRE(reg_id));
> +
> +	/* Disable MSI window. */
> +	writel(0, host->axi + RZG3S_PCI_MSIRCVWADRL);
> +
> +	/* Free unused memory. */
> +	dma_unmap_single(host->dev, msi->dma_addr, size * 2, DMA_BIDIRECTIONAL);
> +	free_pages(msi->pages, 0);
> +
> +	rzg3s_pcie_msi_free_domains(msi);
> +}
> +
> +static void rzg3s_pcie_intx_ack(struct irq_data *d)
> +{
> +	struct rzg3s_pcie_host *host = irq_data_get_irq_chip_data(d);
> +
> +	guard(raw_spinlock_irqsave)(&host->hw_lock);
> +
> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PINTRCVIS, RZG3S_PCI_PINTRCVIS_INTX(d->hwirq),
> +			       RZG3S_PCI_PINTRCVIS_INTX(d->hwirq));
> +}
> +
> +static void rzg3s_pcie_intx_mask(struct irq_data *d)
> +{
> +	struct rzg3s_pcie_host *host = irq_data_get_irq_chip_data(d);
> +
> +	guard(raw_spinlock_irqsave)(&host->hw_lock);
> +
> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PINTRCVIE, RZG3S_PCI_PINTRCVIE_INTX(d->hwirq),
> +			       0);
> +}
> +
> +static void rzg3s_pcie_intx_unmask(struct irq_data *d)
> +{
> +	struct rzg3s_pcie_host *host = irq_data_get_irq_chip_data(d);
> +
> +	guard(raw_spinlock_irqsave)(&host->hw_lock);
> +
> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PINTRCVIE, RZG3S_PCI_PINTRCVIE_INTX(d->hwirq),
> +			       RZG3S_PCI_PINTRCVIE_INTX(d->hwirq));
> +}
> +
> +static struct irq_chip rzg3s_pcie_intx_irq_chip = {
> +	.name = "PCIe INTx",
> +	.irq_ack = rzg3s_pcie_intx_ack,
> +	.irq_mask = rzg3s_pcie_intx_mask,
> +	.irq_unmask = rzg3s_pcie_intx_unmask,
> +};
> +
> +static int rzg3s_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
> +			       irq_hw_number_t hwirq)
> +{
> +	irq_set_chip_and_handler(irq, &rzg3s_pcie_intx_irq_chip, handle_level_irq);
> +	irq_set_chip_data(irq, domain->host_data);
> +
> +	return 0;
> +}
> +
> +static const struct irq_domain_ops rzg3s_pcie_intx_domain_ops = {
> +	.map = rzg3s_pcie_intx_map,
> +	.xlate = irq_domain_xlate_onetwocell,
> +};
> +
> +static int rzg3s_pcie_intx_setup(struct rzg3s_pcie_host *host)
> +{
> +	struct device *dev = host->dev;
> +	struct device_node *np = dev->of_node;
> +	struct device_node *intc_np __free(device_node) = of_get_child_by_name(np,
> +								"legacy-interrupt-controller");
> +	int irq_count;
> +
> +	if (!intc_np) {
> +		dev_warn(dev, "legacy-interrupt-controller node is absent\n");

You don't have to make a child interrupt-controller node any more. You 
can have both 'interrupt-controller' and 'interrupt-map' in the host 
bridge node.

> +		return 0;
> +	}
> +
> +	irq_count = of_irq_count(intc_np);
> +	if (irq_count != PCI_NUM_INTX)
> +		return dev_err_probe(dev, -EINVAL,
> +				     "Invalid IRQ entries in legacy-interrupt-controller\n");

Don't use the kernel to validate your DT. You don't use the count for 
anything.

> +
> +	for (int i = 0; i < irq_count; i++) {

s/irq_count/PCI_NUM_INTX/

> +		int irq = irq_of_parse_and_map(intc_np, i);

Without the legacy node, you can use platform_get_irq() instead.

> +
> +		if (!irq)
> +			return dev_err_probe(dev, -EINVAL, "Failed to parse and map INTx IRQ\n");
> +
> +		host->intx_irqs[i] = irq;
> +		irq_set_chained_handler_and_data(irq, rzg3s_pcie_intx_irq_handler, host);
> +	}
> +
> +	host->intx_domain = irq_domain_add_linear(intc_np, irq_count,
> +						  &rzg3s_pcie_intx_domain_ops, host);
> +	if (!host->intx_domain)
> +		return dev_err_probe(dev, -EINVAL, "Failed to add irq domain for INTX irqs\n");
> +	irq_domain_update_bus_token(host->intx_domain, DOMAIN_BUS_WIRED);
> +
> +	return 0;
> +}
> +
> +static void rzg3s_pcie_intx_teardown(void *data)
> +{
> +	struct rzg3s_pcie_host *host = data;
> +
> +	irq_domain_remove(host->intx_domain);
> +}
> +
> +static int rzg3s_pcie_set_max_link_speed(struct rzg3s_pcie_host *host)
> +{
> +	u32 lcs, cs2, link_speed, remote_supported_link_speeds, tmp;
> +	u8 ltssm_state_l0 = 0xc;
> +	int ret;
> +
> +	/*
> +	 * According to the RZ/G3S HW manual (section 34.6.3 Caution when Changing
> +	 * the Speed Spontaneously) link speed change can be done only when the
> +	 * link training and status state machine in the PCIe Core Link is L0.
> +	 */
> +	ret = readl_poll_timeout(host->axi + RZG3S_PCI_PCSTAT1, tmp,
> +				 FIELD_GET(RZG3S_PCI_PCSTAT1_LTSSM_STATE, tmp) == ltssm_state_l0,
> +				 100, 1000);
> +	if (ret) {
> +		dev_dbg(host->dev, "Could not set max link speed! LTSSM not in L0, state=%lx\n",
> +			FIELD_GET(RZG3S_PCI_PCSTAT1_LTSSM_STATE, tmp));
> +		return ret;
> +	}
> +
> +	lcs = readl(host->pcie + RZG3S_PCI_CFG_LINKCS);
> +	cs2 = readl(host->axi + RZG3S_PCI_PCSTAT2);
> +
> +	link_speed = FIELD_GET(RZG3S_PCI_CFG_LINKCS_CUR_LS, lcs);
> +	remote_supported_link_speeds = FIELD_GET(RZG3S_PCI_PCSTAT2_SDRIRE, cs2);
> +
> +	/* Return if link is @ 5.0 GT/s or the connected device doesn't support it. */
> +	if (link_speed == RZG3S_PCIE_LINK_SPEED_5_0_GTS ||
> +	    !(remote_supported_link_speeds & BIT(RZG3S_PCIE_LINK_SPEED_5_0_GTS)))
> +		return 0;
> +
> +	/* Set target Link speed to 5.0 GT/s. */
> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_CFG_LINCS2, RZG3S_PCI_CFG_LINCS2_TARGET_LS,
> +			       FIELD_PREP(RZG3S_PCI_CFG_LINCS2_TARGET_LS,
> +					  RZG3S_PCIE_LINK_SPEED_5_0_GTS));
> +
> +	/* Request link speed change. */
> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PCCTRL2,
> +			       RZG3S_PCI_PCCTRL2_LS_CHG_REQ | RZG3S_PCI_PCCTRL2_LS_CHG,
> +			       RZG3S_PCI_PCCTRL2_LS_CHG_REQ |
> +			       FIELD_PREP(RZG3S_PCI_PCCTRL2_LS_CHG,
> +					  RZG3S_PCIE_LINK_SPEED_5_0_GTS - 1));
> +
> +	ret = readl_poll_timeout(host->axi + RZG3S_PCI_PCSTAT2, cs2,
> +				 (cs2 & RZG3S_PCI_PCSTAT2_LS_CHG_DONE), 100, 1000);
> +
> +	/*
> +	 * According to the RZ/G3S HW manual (section 34.6.3 Caution when Changing
> +	 * the Speed Spontaneously) the PCI_PCCTRL2_LS_CHG_REQ should be
> +	 * de-asserted after checking for PCI_PCSTAT2_LS_CHG_DONE.
> +	 */
> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PCCTRL2, RZG3S_PCI_PCCTRL2_LS_CHG_REQ, 0);
> +
> +	return ret;
> +}
> +
> +static void rzg3s_pcie_cfg_init(struct rzg3s_pcie_host *host)
> +{
> +	/* Enable access control to the CFGU. */
> +	writel(RZG3S_PCI_PERM_CFG_HWINIT_EN, host->axi + RZG3S_PCI_PERM);
> +
> +	/* Update vendor ID and device ID. */
> +	writel(FIELD_PREP(RZG3S_PCI_CFG_VID_VENDOR, host->vendor_id) |
> +	       FIELD_PREP(RZG3S_PCI_CFG_VID_DEVICE, host->device_id),
> +	       host->pcie + RZG3S_PCI_CFG_VID);
> +
> +	/* HW manual recommends to write 0xffffffff on initialization. */
> +	writel(0xffffffff, host->pcie + RZG3S_PCI_CFG_BARMSK00L);
> +	writel(0xffffffff, host->pcie + RZG3S_PCI_CFG_BARMSK00U);
> +
> +	/* Primary bus = 0, secondary bus = 1, subordinate bus = 1. */
> +	writel(FIELD_PREP(RZG3S_PCI_CFG_BNR_SUBORDINATE_BUS, 1) |
> +	       FIELD_PREP(RZG3S_PCI_CFG_BNR_SECONDARY_BUS, 1),
> +	       host->pcie + RZG3S_PCI_CFG_BNR);
> +
> +	/* Disable access control to the CFGU. */
> +	writel(0, host->axi + RZG3S_PCI_PERM);
> +}
> +
> +static void rzg3s_pcie_irqs_init(struct rzg3s_pcie_host *host)
> +{
> +	/*
> +	 * According to the HW manual of the RZ/G3S (sections corresponding
> +	 * to all registers written with ~0U), the hardware ignores value
> +	 * written to unused bits. Writing ~0U to these registers should be
> +	 * safe.
> +	 */
> +
> +	/* Clear the link state and PM transitions. */
> +	writel(RZG3S_PCI_PEIS0_DL_UPDOWN | RZG3S_PCI_PEIS0_RX_DLLP_PM_ENTER,
> +	       host->axi + RZG3S_PCI_PEIS0);
> +
> +	/* Disable all interrupts. */
> +	writel(0, host->axi + RZG3S_PCI_PEIE0);
> +
> +	/* Clear all parity and ecc error interrupts. */
> +	writel(~0U, host->axi + RZG3S_PCI_PEIS1);
> +
> +	/* Disable all parity and ecc error interrupts. */
> +	writel(0, host->axi + RZG3S_PCI_PEIE1);
> +
> +	/* Clear all AXI master error interrupts. */
> +	writel(~0U, host->axi + RZG3S_PCI_AMEIS);
> +
> +	/* Clear all AXI slave error interrupts. */
> +	writel(~0U, host->axi + RZG3S_PCI_ASEIS1);
> +
> +	/* Clear all message receive interrupts. */
> +	writel(~0U, host->axi + RZG3S_PCI_MSGRCVIS);
> +}
> +
> +static int rzg3s_pcie_resets_bulk_set(int (*action)(int num, struct reset_control_bulk_data *rstcs),
> +				      struct reset_control **resets, u8 num_resets)
> +{
> +	struct reset_control_bulk_data *data __free(kfree) =
> +		kcalloc(num_resets, sizeof(*data), GFP_KERNEL);
> +
> +	if (!data)
> +		return -ENOMEM;
> +
> +	for (u8 i = 0; i < num_resets; i++)
> +		data[i].rstc = resets[i];
> +
> +	return action(num_resets, data);
> +}
> +
> +static int
> +rzg3s_pcie_resets_init(struct device *dev, struct reset_control ***resets,
> +		       struct reset_control *(*action)(struct device *dev, const char *id),
> +		       const char * const *reset_names, u8 num_resets)
> +{
> +	*resets = devm_kcalloc(dev, num_resets, sizeof(struct reset_control *), GFP_KERNEL);
> +	if (!*resets)
> +		return -ENOMEM;
> +
> +	for (u8 i = 0; i < num_resets; i++) {
> +		(*resets)[i] = action(dev, reset_names[i]);
> +		if (IS_ERR((*resets)[i]))
> +			return PTR_ERR((*resets)[i]);
> +	}
> +
> +	return 0;
> +}
> +
> +static int rzg3s_pcie_resets_prepare(struct rzg3s_pcie_host *host)
> +{
> +	const char * const cfg_resets[] = { "rst_b", "rst_ps_b", "rst_gp_b", "rst_rsm_b", };
> +	const char * const power_resets[] = { "aresetn", "rst_cfg_b", "rst_load_b", };
> +	int ret;
> +
> +	/*
> +	 * According to the RZ/G3S HW manual (section 34.5.1.2 De-asserting the Reset)
> +	 * the PCIe IP needs to wait 5ms from power on to the de-assertion of reset.
> +	 */
> +	usleep_range(5000, 5100);
> +	host->num_power_resets = ARRAY_SIZE(power_resets);
> +	ret = rzg3s_pcie_resets_init(host->dev, &host->power_resets,
> +				     devm_reset_control_get_exclusive_deasserted,
> +				     power_resets, ARRAY_SIZE(power_resets));
> +	if (ret)
> +		return ret;
> +
> +	host->num_cfg_resets = ARRAY_SIZE(cfg_resets);
> +	return rzg3s_pcie_resets_init(host->dev, &host->cfg_resets,
> +				      devm_reset_control_get_exclusive,
> +				      cfg_resets, ARRAY_SIZE(cfg_resets));
> +}
> +
> +static void rzg3s_pcie_cfg_resets_action(void *data)
> +{
> +	struct rzg3s_pcie_host *host = data;
> +
> +	rzg3s_pcie_resets_bulk_set(reset_control_bulk_assert, host->cfg_resets,
> +				   host->num_cfg_resets);
> +}
> +
> +static int rzg3s_pcie_hw_init(struct rzg3s_pcie_host *host, bool probe)
> +{
> +	u32 val;
> +	int ret;
> +
> +	/* Set the PCIe related registers. */
> +	rzg3s_pcie_cfg_init(host);
> +
> +	/* Set the interrupts. */
> +	rzg3s_pcie_irqs_init(host);
> +
> +	ret = rzg3s_pcie_resets_bulk_set(reset_control_bulk_deassert, host->cfg_resets,
> +					 host->num_cfg_resets);
> +	if (ret)
> +		return ret;
> +
> +	/* Wait for link up. */
> +	ret = readl_poll_timeout(host->axi + RZG3S_PCI_PCSTAT1, val,
> +				 !(val & RZG3S_PCI_PCSTAT1_DL_DOWN_STS), 5000, 500000);
> +	if (ret) {
> +		rzg3s_pcie_resets_bulk_set(reset_control_bulk_assert, host->cfg_resets,
> +					   host->num_cfg_resets);
> +		return ret;
> +	}
> +
> +	val = readl(host->axi + RZG3S_PCI_PCSTAT2);
> +	dev_info(host->dev, "PCIe link status [0x%x]\n", val);
> +
> +	val = FIELD_GET(RZG3S_PCI_PCSTAT2_STATE_RX_DETECT, val);
> +	dev_info(host->dev, "PCIe x%d: link up\n", hweight32(val));
> +
> +	if (probe)
> +		ret = devm_add_action_or_reset(host->dev, rzg3s_pcie_cfg_resets_action, host);
> +
> +	return ret;
> +}
> +
> +static void rzg3s_pcie_set_inbound_window(struct rzg3s_pcie_host *host, u64 cpu_addr,
> +					  u64 pci_addr, u64 size, int id)
> +{
> +	/* Set CPU window base address. */
> +	writel(upper_32_bits(cpu_addr), host->axi + RZG3S_PCI_ADESTU(id));
> +	writel(lower_32_bits(cpu_addr), host->axi + RZG3S_PCI_ADESTL(id));
> +
> +	/* Set window size. */
> +	writel(upper_32_bits(size), host->axi + RZG3S_PCI_AWMASKU(id));
> +	writel(lower_32_bits(size), host->axi + RZG3S_PCI_AWMASKL(id));
> +
> +	/* Set PCIe window base address and enable the window.. */
> +	writel(upper_32_bits(pci_addr), host->axi + RZG3S_PCI_AWBASEU(id));
> +	writel(lower_32_bits(pci_addr) | RZG3S_PCI_AWBASEL_WIN_ENA,
> +	       host->axi + RZG3S_PCI_AWBASEL(id));
> +}
> +
> +static int rzg3s_pcie_set_inbound_windows(struct rzg3s_pcie_host *host,
> +					  struct resource_entry *entry, int *index)
> +{
> +	u64 pci_addr = entry->res->start - entry->offset;
> +	u64 cpu_addr = entry->res->start;
> +	u64 cpu_end = entry->res->end;
> +	u64 size_id = 0;
> +	int id = *index;
> +	u64 size;
> +
> +	while (cpu_addr < cpu_end) {
> +		if (id >= RZG3S_MAX_WINDOWS)
> +			return dev_err_probe(host->dev, -EINVAL,
> +					     "Failed to set inbound windows!\n");
> +
> +		size = resource_size(entry->res) - size_id;
> +
> +		/*
> +		 * According to the RZ/G3S HW manual (section 34.3.1.71 AXI Window Mask
> +		 * (Lower) Registers) the min size is 4K.
> +		 */
> +		size = max(size, 4096);
> +
> +		/*
> +		 * According the RZ/G3S HW manual (sections:
> +		 * - 34.3.1.69 AXI Window Base (Lower) Registers
> +		 * - 34.3.1.71 AXI Window Mask (Lower) Registers
> +		 * - 34.3.1.73 AXI Destination (Lower) Registers)
> +		 * the CPU addr, PCIe addr, size should be 4K alined and be a power of 2.
> +		 */
> +		size = ALIGN(size, 4096);
> +
> +		/*
> +		 * According to the RZ/G3S HW manual (section 34.3.1.71 AXI Window Mask
> +		 * (Lower) Registers) HW expects first 12 LSB bits to be 0xfff.
> +		 * Extract 1 from size for this.
> +		 */
> +		size = roundup_pow_of_two(size) - 1;
> +
> +		cpu_addr = ALIGN(cpu_addr, 4096);
> +		pci_addr = ALIGN(pci_addr, 4096);
> +
> +		rzg3s_pcie_set_inbound_window(host, cpu_addr, pci_addr, size, id);
> +
> +		pci_addr += size;
> +		cpu_addr += size;
> +		size_id = size;
> +		id++;
> +	}
> +	*index = id;
> +
> +	return 0;
> +}
> +
> +static int rzg3s_pcie_parse_map_dma_ranges(struct rzg3s_pcie_host *host)
> +{
> +	struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host);
> +	struct resource_entry *entry;
> +	int i = 0, ret;
> +
> +	resource_list_for_each_entry(entry, &bridge->dma_ranges) {
> +		ret = rzg3s_pcie_set_inbound_windows(host, entry, &i);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void rzg3s_pcie_set_outbound_window(struct rzg3s_pcie_host *host, struct resource_entry *win,
> +					   int id)
> +{
> +	struct resource *res = win->res;
> +	resource_size_t size = resource_size(res);
> +	resource_size_t res_start;
> +
> +	if (res->flags & IORESOURCE_IO)
> +		res_start = pci_pio_to_address(res->start) - win->offset;
> +	else
> +		res_start = res->start - win->offset;
> +
> +	/*
> +	 * According to the RZ/G3S HW manual (section 34.3.1.75 PCIe Window Base
> +	 * (Lower) Registers) the window base address need to be 4K aligned.
> +	 */
> +	res_start = ALIGN(res_start, 4096);
> +
> +	size = ALIGN(size, 4096);
> +	size = roundup_pow_of_two(size) - 1;
> +
> +	/* Set PCIe destination. */
> +	writel(upper_32_bits(res_start), host->axi + RZG3S_PCI_PDESTU(id));
> +	writel(lower_32_bits(res_start), host->axi + RZG3S_PCI_PDESTL(id));
> +
> +	/* Set PCIe window mask. */
> +	writel(upper_32_bits(size), host->axi + RZG3S_PCI_PWMASKU(id));
> +	writel(lower_32_bits(size), host->axi + RZG3S_PCI_PWMASKL(id));
> +
> +	/* Set PCIe window base and enable the window. */
> +	writel(upper_32_bits(res_start), host->axi + RZG3S_PCI_PWBASEU(id));
> +	writel(lower_32_bits(res_start) | RZG3S_PCI_PWBASEL_ENA,
> +	       host->axi + RZG3S_PCI_PWBASEL(id));
> +}
> +
> +static int rzg3s_pcie_parse_map_ranges(struct rzg3s_pcie_host *host)
> +{
> +	struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host);
> +	struct resource_entry *win;
> +	int i = 0;
> +
> +	resource_list_for_each_entry(win, &bridge->windows) {
> +		struct resource *res = win->res;
> +
> +		if (i >= RZG3S_MAX_WINDOWS)
> +			return dev_err_probe(host->dev, -EINVAL,
> +					     "Failed to set outbound windows!\n");
> +
> +		if (!res->flags)
> +			continue;
> +
> +		switch (resource_type(res)) {
> +		case IORESOURCE_IO:
> +		case IORESOURCE_MEM:
> +			rzg3s_pcie_set_outbound_window(host, win, i);
> +			i++;
> +			break;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int rzg3s_pcie_phy_init(struct rzg3s_pcie_host *host)
> +{
> +	static const u32 xcfgd_settings[RZG3S_PCI_PHY_XCFGD_NUM] = {
> +		[8]  = 0xe0006801, 0x007f7e30, 0x183e0000, 0x978ff500,
> +		       0xec000000, 0x009f1400, 0x0000d009,
> +		[17] = 0x78000000,
> +		[19] = 0x00880000, 0x000005c0, 0x07000000, 0x00780920,
> +		       0xc9400ce2, 0x90000c0c, 0x000c1414, 0x00005034,
> +		       0x00006000, 0x00000001,
> +	};
> +	static const u32 xcfga_cmn_settings[RZG3S_PCI_PHY_XCFGA_CMN_NUM] = {
> +		0x00000d10, 0x08310100, 0x00c21404, 0x013c0010, 0x01874440,
> +		0x1a216082, 0x00103440, 0x00000080, 0x00000010, 0x0c1000c1,
> +		0x1000c100, 0x0222000c, 0x00640019, 0x00a00028, 0x01d11228,
> +		0x0201001d,
> +	};
> +	static const u32 xcfga_rx_settings[RZG3S_PCI_PHY_XCFGA_RX_NUM] = {
> +		0x07d55000, 0x030e3f00, 0x00000288, 0x102c5880, 0x0000000b,
> +		0x04141441, 0x00641641, 0x00d63d63, 0x00641641, 0x01970377,
> +		0x00190287, 0x00190028, 0x00000028,
> +	};
> +
> +	writel(RZG3S_PCI_PERM_PIPE_PHY_REG_EN, host->axi + RZG3S_PCI_PERM);
> +
> +	for (u8 i = 0; i < RZG3S_PCI_PHY_XCFGD_NUM; i++)
> +		writel(xcfgd_settings[i], host->axi + RZG3S_PCI_PHY_XCFGD(i));
> +
> +	for (u8 i = 0; i < RZG3S_PCI_PHY_XCFGA_CMN_NUM; i++)
> +		writel(xcfga_cmn_settings[i], host->axi + RZG3S_PCI_PHY_XCFGA_CMN(i));
> +
> +	for (u8 i = 0; i < RZG3S_PCI_PHY_XCFGA_RX_NUM; i++)
> +		writel(xcfga_rx_settings[i], host->axi + RZG3S_PCI_PHY_XCFGA_RX(i));
> +
> +	writel(0x107, host->axi + RZG3S_PCI_PHY_XCFGA_TX);
> +
> +	/* Select PHY settings values. */
> +	writel(RZG3S_PCI_PHY_XCFG_CTRL_PHYREG_SEL, host->axi + RZG3S_PCI_PHY_XCFG_CTRL);
> +
> +	writel(0, host->axi + RZG3S_PCI_PERM);
> +
> +	return 0;
> +}
> +
> +static void rzg3s_pcie_pm_runtime_put(void *data)
> +{
> +	pm_runtime_put_sync(data);

Seems like a pointless wrapper.

> +}
> +
> +static void rzg3s_pcie_sysc_signal_action(void *data)
> +{
> +	struct regmap *sysc = data;
> +
> +	/*
> +	 * SYSC RST_RSM_B signal need to be asserted before turning off the power to
> +	 * the PHY.
> +	 */
> +	regmap_update_bits(sysc, RZG3S_SYS_PCIE_RST_RSM_B, RZG3S_SYS_PCIE_RST_RSM_B_MASK,
> +			   FIELD_PREP(RZG3S_SYS_PCIE_RST_RSM_B_MASK, 0));
> +}
> +
> +static int rzg3s_pcie_setup(struct rzg3s_pcie_host *host,
> +			    int (*intx_setup)(struct rzg3s_pcie_host *host),
> +			    int (*msi_setup)(struct rzg3s_pcie_host *host),
> +			    bool probe)
> +{
> +	struct device *dev = host->dev;
> +	int ret;
> +
> +	/* Set inbound windows. */
> +	ret = rzg3s_pcie_parse_map_dma_ranges(host);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to set inbound windows!\n");
> +
> +	/* Set outbound windows. */
> +	ret = rzg3s_pcie_parse_map_ranges(host);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to set outbound windows!\n");
> +
> +	/* Set the PHY, if any. */
> +	if (host->phy_init) {
> +		ret = host->phy_init(host);
> +		if (ret)
> +			return dev_err_probe(dev, ret, "Failed to set the PHY!\n");
> +	}
> +
> +	if (intx_setup) {
> +		ret = intx_setup(host);
> +		if (ret)
> +			return dev_err_probe(dev, ret, "Failed to setup INTx\n");
> +
> +		if (probe) {
> +			ret = devm_add_action_or_reset(dev, rzg3s_pcie_intx_teardown, host);
> +			if (ret)
> +				return dev_err_probe(dev, ret, "Failed to add INTx action\n");
> +		}
> +	}
> +
> +	/* Set the MSIs. */
> +	if (IS_ENABLED(CONFIG_PCI_MSI)) {
> +		ret = msi_setup(host);
> +		if (ret)
> +			return dev_err_probe(dev, ret, "Failed to setup MSIs\n");
> +
> +		if (probe) {
> +			ret = devm_add_action_or_reset(dev, rzg3s_pcie_msi_teardown, host);
> +			if (ret)
> +				return dev_err_probe(dev, ret, "Failed to add MSI action\n");
> +		}
> +	}
> +
> +	/* Initialize the HW. */
> +	ret = rzg3s_pcie_hw_init(host, probe);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to initialize the HW!\n");
> +
> +	/* Try to set maximum supported link speed (5.0 GT/s). */
> +	ret = rzg3s_pcie_set_max_link_speed(host);
> +	if (ret)
> +		dev_info(dev, "Failed to set link speed to 5.0 GT/s\n");
> +
> +	return 0;
> +}
> +
> +static int rzg3s_pcie_probe_helper(struct platform_device *pdev, void *devres_group_id)
> +{
> +	struct pci_host_bridge *bridge;
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = dev->of_node;
> +	struct device_node *sysc_np __free(device_node) =
> +		of_parse_phandle(np, "renesas,sysc", 0);
> +	struct rzg3s_pcie_host *host;
> +	int ret;
> +
> +	bridge = devm_pci_alloc_host_bridge(dev, sizeof(*host));
> +	if (!bridge)
> +		return -ENOMEM;
> +
> +	host = pci_host_bridge_priv(bridge);
> +	host->dev = dev;
> +	host->phy_init = device_get_match_data(dev);
> +	host->devres_group_id = devres_group_id;
> +	platform_set_drvdata(pdev, host);
> +
> +	host->axi = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(host->axi))
> +		return PTR_ERR(host->axi);
> +	host->pcie = host->axi + RZG3S_PCI_CFG_BASE;
> +
> +	ret = of_property_read_u32(np, "vendor-id", &host->vendor_id);
> +	if (ret)
> +		return ret;
> +
> +	ret = of_property_read_u32(np, "device-id", &host->device_id);
> +	if (ret)
> +		return ret;
> +
> +	host->sysc = syscon_node_to_regmap(sysc_np);
> +	if (IS_ERR(host->sysc))
> +		return PTR_ERR(host->sysc);
> +
> +	ret = regmap_update_bits(host->sysc, RZG3S_SYS_PCIE_RST_RSM_B,
> +				 RZG3S_SYS_PCIE_RST_RSM_B_MASK,
> +				 FIELD_PREP(RZG3S_SYS_PCIE_RST_RSM_B_MASK, 1));
> +	if (ret)
> +		return ret;
> +
> +	ret = devm_add_action_or_reset(dev, rzg3s_pcie_sysc_signal_action, host->sysc);
> +	if (ret)
> +		return ret;
> +
> +	ret = rzg3s_pcie_resets_prepare(host);
> +	if (ret)
> +		return ret;
> +
> +	ret = devm_pm_runtime_enable(dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = pm_runtime_resume_and_get(dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = devm_add_action_or_reset(dev, rzg3s_pcie_pm_runtime_put, dev);
> +	if (ret)
> +		return ret;
> +
> +	raw_spin_lock_init(&host->hw_lock);
> +
> +	ret = rzg3s_pcie_setup(host, rzg3s_pcie_intx_setup, rzg3s_pcie_msi_enable, true);
> +	if (ret)
> +		return ret;
> +
> +	bridge->sysdata = host;
> +	bridge->ops = &rzg3s_pcie_ops;
> +	return pci_host_probe(bridge);
> +}
> +
> +static int rzg3s_pcie_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	void *devres_group_id;
> +	int ret;
> +
> +	devres_group_id = devres_open_group(dev, NULL, GFP_KERNEL);

I don't think you need this...

> +	if (!devres_group_id)
> +		return -ENOMEM;
> +
> +	ret = rzg3s_pcie_probe_helper(pdev, devres_group_id);
> +	if (ret)
> +		devres_release_group(dev, devres_group_id);
> +
> +	return ret;
> +}
> +
> +static void rzg3s_pcie_remove(struct platform_device *pdev)
> +{
> +	struct rzg3s_pcie_host *host = dev_get_drvdata(&pdev->dev);
> +	struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host);
> +
> +	pci_lock_rescan_remove();
> +	pci_stop_root_bus(bridge->bus);
> +	pci_remove_root_bus(bridge->bus);
> +	pci_unlock_rescan_remove();

If you put all this into the devm_add_action_or_reset() callback, then 
you can get rid of remove().

> +
> +	devres_release_group(host->dev, host->devres_group_id);
> +}
> +
> +static int rzg3s_pcie_suspend(struct device *dev)
> +{
> +	struct rzg3s_pcie_host *host = dev_get_drvdata(dev);
> +	struct regmap *sysc = host->sysc;
> +	int ret;
> +
> +	ret = pm_runtime_put_sync(dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = rzg3s_pcie_resets_bulk_set(reset_control_bulk_assert, host->power_resets,
> +					 host->num_power_resets);
> +	if (ret)
> +		goto rpm_restore;
> +
> +	ret = rzg3s_pcie_resets_bulk_set(reset_control_bulk_assert, host->cfg_resets,
> +					 host->num_cfg_resets);
> +	if (ret)
> +		goto power_resets_restore;
> +
> +	ret = regmap_update_bits(sysc, RZG3S_SYS_PCIE_RST_RSM_B, RZG3S_SYS_PCIE_RST_RSM_B_MASK,
> +				 FIELD_PREP(RZG3S_SYS_PCIE_RST_RSM_B_MASK, 0));
> +	if (ret)
> +		goto cfg_resets_restore;
> +
> +	return 0;
> +
> +	/* Restore the previous state if any error happens. */
> +cfg_resets_restore:
> +	rzg3s_pcie_resets_bulk_set(reset_control_bulk_deassert, host->cfg_resets,
> +				   host->num_cfg_resets);
> +power_resets_restore:
> +	rzg3s_pcie_resets_bulk_set(reset_control_bulk_deassert, host->power_resets,
> +				   host->num_power_resets);
> +rpm_restore:
> +	pm_runtime_resume_and_get(dev);
> +	return ret;
> +}
> +
> +static int rzg3s_pcie_resume(struct device *dev)
> +{
> +	struct rzg3s_pcie_host *host = dev_get_drvdata(dev);
> +	struct regmap *sysc = host->sysc;
> +	int ret;
> +
> +	ret = regmap_update_bits(sysc, RZG3S_SYS_PCIE_RST_RSM_B, RZG3S_SYS_PCIE_RST_RSM_B_MASK,
> +				 FIELD_PREP(RZG3S_SYS_PCIE_RST_RSM_B_MASK, 1));
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * According to the RZ/G3S HW manual (section 34.5.1.2 De-asserting the Reset)
> +	 * the PCIe IP needs to wait 5ms from power on to the de-assertion of reset.
> +	 */
> +	usleep_range(5000, 5100);
> +	ret = rzg3s_pcie_resets_bulk_set(reset_control_bulk_deassert, host->power_resets,
> +					 host->num_power_resets);
> +	if (ret)
> +		goto assert_rst_rsm_b;
> +
> +	ret = pm_runtime_resume_and_get(dev);
> +	if (ret)
> +		goto assert_power_resets;
> +
> +	ret = rzg3s_pcie_setup(host, NULL, rzg3s_pcie_msi_hw_setup, false);
> +	if (ret)
> +		goto rpm_put;
> +
> +	return 0;
> +
> +	/*
> +	 * If any error happens there is no way to recover the IP. Put it in the
> +	 * lowest possible power state.
> +	 */
> +rpm_put:
> +	pm_runtime_put_sync(dev);
> +assert_power_resets:
> +	rzg3s_pcie_resets_bulk_set(reset_control_bulk_assert, host->power_resets,
> +				   host->num_power_resets);
> +assert_rst_rsm_b:
> +	regmap_update_bits(sysc, RZG3S_SYS_PCIE_RST_RSM_B, RZG3S_SYS_PCIE_RST_RSM_B_MASK,
> +			   FIELD_PREP(RZG3S_SYS_PCIE_RST_RSM_B_MASK, 0));
> +	return ret;
> +}
> +
> +static DEFINE_NOIRQ_DEV_PM_OPS(rzg3s_pcie_pm_ops, rzg3s_pcie_suspend, rzg3s_pcie_resume);
> +
> +static const struct of_device_id rzg3s_pcie_of_match[] = {
> +	{ .compatible = "renesas,r9a08g045s33-pcie", .data = rzg3s_pcie_phy_init },
> +	{},
> +};
> +
> +static struct platform_driver rzg3s_pcie_driver = {
> +	.driver = {
> +		.name = "rz-pcie-host",
> +		.of_match_table = rzg3s_pcie_of_match,
> +		.pm = pm_ptr(&rzg3s_pcie_pm_ops),
> +	},
> +	.probe = rzg3s_pcie_probe,
> +	.remove = rzg3s_pcie_remove,
> +};
> +module_platform_driver(rzg3s_pcie_driver);
> +
> +MODULE_DESCRIPTION("Renesas RZ/G3S PCIe host driver");
> +MODULE_AUTHOR("Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>");
> +MODULE_LICENSE("GPL");
> -- 
> 2.43.0
> 


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

* Re: [PATCH 4/8] dt-bindings: PCI: renesas,r9a08g045s33-pcie: Add documentation for the PCIe IP on Renesas RZ/G3S
  2025-04-30 10:32 ` [PATCH 4/8] dt-bindings: PCI: renesas,r9a08g045s33-pcie: Add documentation for the PCIe IP on Renesas RZ/G3S Claudiu
  2025-05-01 20:16   ` Bjorn Helgaas
@ 2025-05-09 21:08   ` Rob Herring
  2025-05-14 11:41     ` Claudiu Beznea
  1 sibling, 1 reply; 28+ messages in thread
From: Rob Herring @ 2025-05-09 21:08 UTC (permalink / raw)
  To: Claudiu
  Cc: bhelgaas, lpieralisi, kw, manivannan.sadhasivam, krzk+dt,
	conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, linux-arm-kernel, linux-clk, Claudiu Beznea

On Wed, Apr 30, 2025 at 01:32:32PM +0300, Claudiu wrote:
> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> 
> The PCIe IP available on the Renesas RZ/G3S complies with the PCI Express
> Base Specification 4.0. It is designed for root complex applications and
> features a single-lane (x1) implementation. Add documentation for it.
> The interrupts, interrupt-names, resets, reset-names, clocks, clock-names
> description were obtained from the hardware manual.
> 
> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> ---
>  .../pci/renesas,r9a08g045s33-pcie.yaml        | 242 ++++++++++++++++++
>  1 file changed, 242 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/pci/renesas,r9a08g045s33-pcie.yaml
> 
> diff --git a/Documentation/devicetree/bindings/pci/renesas,r9a08g045s33-pcie.yaml b/Documentation/devicetree/bindings/pci/renesas,r9a08g045s33-pcie.yaml
> new file mode 100644
> index 000000000000..354f9c3be139
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pci/renesas,r9a08g045s33-pcie.yaml
> @@ -0,0 +1,242 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/pci/renesas,r9a08g045s33-pcie.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Renesas RZ/G3S PCIe host controller
> +
> +maintainers:
> +  - Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> +
> +description:
> +  Renesas RZ/G3S PCIe host controller complies with PCIe Base Specification
> +  4.0 and supports up to 5 GT/s (Gen2).
> +
> +properties:
> +  compatible:
> +    const: renesas,r9a08g045s33-pcie # RZ/G3S
> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupts:
> +    items:
> +      - description: System error interrupt
> +      - description: System error on correctable error interrupt
> +      - description: System error on non-fatal error interrupt
> +      - description: System error on fatal error interrupt
> +      - description: AXI error interrupt
> +      - description: INTA interrupt
> +      - description: INTB interrupt
> +      - description: INTC interrupt
> +      - description: INTD interrupt
> +      - description: MSI interrupt
> +      - description: Link bandwidth interrupt
> +      - description: PME interrupt
> +      - description: DMA interrupt
> +      - description: PCIe event interrupt
> +      - description: Message interrupt
> +      - description: All interrupts
> +
> +  interrupt-names:
> +    items:
> +      - description: int_serr
> +      - description: int_ser_cor
> +      - description: int_serr_nonfatal
> +      - description: int_serr_fatal
> +      - description: axi_err_int
> +      - description: inta_rc
> +      - description: intb_rc
> +      - description: intc_rc
> +      - description: intd_rc
> +      - description: intmsi_rc

Isn't every interrupt for the root complex?

> +      - description: int_link_bandwidth
> +      - description: int_pm_pme
> +      - description: dma_int
> +      - description: pcie_evt_int
> +      - description: msg_int
> +      - description: int_all

'int_' or '_int' is redundant (and inconsistent). Drop.

> +
> +  clocks:
> +    items:
> +      - description: System clock
> +      - description: PM control clock
> +
> +  clock-names:
> +    items:
> +      - description: aclk
> +      - description: clkl1pm

'l1pm' or 'pm'

> +
> +  resets:
> +    items:
> +      - description: AXI2PCIe Bridge reset
> +      - description: Data link layer/transaction layer reset
> +      - description: Transaction layer (ACLK domain) reset
> +      - description: Transaction layer (PCLK domain) reset
> +      - description: Physical layer reset
> +      - description: Configuration register reset
> +      - description: Configuration register reset
> +
> +  reset-names:
> +    items:
> +      - description: aresetn
> +      - description: rst_b
> +      - description: rst_gp_b
> +      - description: rst_ps_b
> +      - description: rst_rsm_b
> +      - description: rst_cfg_b
> +      - description: rst_load_b
> +
> +  power-domains:
> +    maxItems: 1
> +
> +  dma-ranges:
> +    description:
> +      A single range for the inbound memory region.
> +    maxItems: 1
> +
> +  renesas,sysc:
> +    description: System controller phandle
> +    $ref: /schemas/types.yaml#/definitions/phandle
> +
> +  vendor-id:
> +    const: 0x1912
> +
> +  device-id:
> +    const: 0x0033
> +
> +  legacy-interrupt-controller:
> +    description: Interrupt controller node for handling legacy PCI interrupts
> +    type: object
> +
> +    properties:
> +      "#address-cells":
> +        const: 0
> +
> +      "#interrupt-cells":
> +        const: 1
> +
> +      interrupt-controller: true
> +
> +      interrupts:
> +        items:
> +          - description: INTA interrupt
> +          - description: INTB interrupt
> +          - description: INTC interrupt
> +          - description: INTD interrupt
> +
> +    required:
> +      - "#address-cells"
> +      - "#interrupt-cells"
> +      - interrupt-controller
> +      - interrupts
> +
> +    additionalProperties: false
> +
> +required:
> +  - compatible
> +  - reg
> +  - clocks
> +  - resets
> +  - reset-names
> +  - interrupts
> +  - interrupt-names
> +  - interrupt-map
> +  - interrupt-map-mask
> +  - power-domains
> +  - "#address-cells"
> +  - "#size-cells"
> +  - "#interrupt-cells"
> +  - renesas,sysc
> +  - vendor-id
> +  - device-id
> +
> +allOf:
> +  - $ref: /schemas/pci/pci-host-bridge.yaml#
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/clock/r9a08g045-cpg.h>
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +
> +    bus {
> +        #address-cells = <2>;
> +        #size-cells = <2>;
> +
> +        gic: interrupt-controller {
> +            interrupt-controller;
> +            #interrupt-cells = <3>;
> +        };

Drop. Don't need to show provider nodes for the example.

> +
> +        pcie@11e40000 {
> +            compatible = "renesas,r9a08g045s33-pcie";
> +            reg = <0 0x11e40000 0 0x10000>;
> +            ranges = <0x03000000 0 0x30000000 0 0x30000000 0 0x8000000>;
> +            dma-ranges = <0x42000000 0 0x48000000 0 0x48000000 0 0x8000000>;
> +            bus-range = <0x0 0xff>;
> +            clocks = <&cpg CPG_MOD R9A08G045_PCI_ACLK>,
> +                     <&cpg CPG_MOD R9A08G045_PCI_CLKL1PM>;
> +            clock-names = "aclk", "clkl1pm";
> +            resets = <&cpg R9A08G045_PCI_ARESETN>,
> +                     <&cpg R9A08G045_PCI_RST_B>,
> +                     <&cpg R9A08G045_PCI_RST_GP_B>,
> +                     <&cpg R9A08G045_PCI_RST_PS_B>,
> +                     <&cpg R9A08G045_PCI_RST_RSM_B>,
> +                     <&cpg R9A08G045_PCI_RST_CFG_B>,
> +                     <&cpg R9A08G045_PCI_RST_LOAD_B>;
> +            reset-names = "aresetn", "rst_b", "rst_gp_b", "rst_ps_b",
> +                          "rst_rsm_b", "rst_cfg_b", "rst_load_b";
> +            interrupts = <GIC_SPI 395 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 396 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 397 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 398 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 399 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 400 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 401 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 402 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 403 IRQ_TYPE_LEVEL_HIGH>,

It is very odd that you have the INTx interrupts here and below.

As I mentioned in the driver, you don't need the legacy node any more. 
Just add 'interrupt-controller' to this node and point interrupt-map to 
this node.

> +                         <GIC_SPI 404 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 405 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 406 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 407 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 408 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 409 IRQ_TYPE_LEVEL_HIGH>,
> +                         <GIC_SPI 410 IRQ_TYPE_LEVEL_HIGH>;
> +            interrupt-names = "int_serr", "int_serr_cor", "int_serr_nonfatal",
> +                              "int_serr_fatal", "axi_err_int", "inta_rc",
> +                              "intb_rc", "intc_rc", "intd_rc",
> +                              "intmsi_rc", "int_link_bandwidth", "int_pm_pme",
> +                              "dma_int", "pcie_evt_int", "msg_int",
> +                              "int_all";
> +            #interrupt-cells = <1>;
> +            interrupt-map-mask = <0 0 0 7>;
> +            interrupt-map = <0 0 0 1 &pcie_intx 0>, /* INT A */
> +                            <0 0 0 2 &pcie_intx 1>, /* INT B */
> +                            <0 0 0 3 &pcie_intx 2>, /* INT C */
> +                            <0 0 0 4 &pcie_intx 3>; /* INT D */
> +            device_type = "pci";
> +            num-lanes = <1>;
> +            #address-cells = <3>;
> +            #size-cells = <2>;
> +            power-domains = <&cpg>;
> +            renesas,sysc = <&sysc>;
> +            vendor-id = <0x1912>;
> +            device-id = <0x0033>;
> +
> +            pcie_intx: legacy-interrupt-controller {
> +                interrupt-controller;
> +                #interrupt-cells = <1>;
> +                #address-cells = <0>;
> +                interrupt-parent = <&gic>;
> +                interrupts = <GIC_SPI 400 IRQ_TYPE_LEVEL_HIGH>,
> +                             <GIC_SPI 401 IRQ_TYPE_LEVEL_HIGH>,
> +                             <GIC_SPI 402 IRQ_TYPE_LEVEL_HIGH>,
> +                             <GIC_SPI 403 IRQ_TYPE_LEVEL_HIGH>;
> +            };
> +        };
> +    };
> +
> +...
> -- 
> 2.43.0
> 


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

* Re: [PATCH 5/8] PCI: rzg3s-host: Add Initial PCIe Host Driver for Renesas RZ/G3S SoC
  2025-05-05 11:26     ` Claudiu Beznea
  2025-05-09 10:29       ` Claudiu Beznea
@ 2025-05-12 20:25       ` Bjorn Helgaas
  2025-05-14  9:37         ` Claudiu Beznea
  1 sibling, 1 reply; 28+ messages in thread
From: Bjorn Helgaas @ 2025-05-12 20:25 UTC (permalink / raw)
  To: Claudiu Beznea
  Cc: bhelgaas, lpieralisi, kw, manivannan.sadhasivam, robh, krzk+dt,
	conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, linux-arm-kernel, linux-clk, Claudiu Beznea

On Mon, May 05, 2025 at 02:26:43PM +0300, Claudiu Beznea wrote:
> On 01.05.2025 23:12, Bjorn Helgaas wrote:
> > On Wed, Apr 30, 2025 at 01:32:33PM +0300, Claudiu wrote:
> >> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> >>
> >> The Renesas RZ/G3S features a PCIe IP that complies with the PCI Express
> >> Base Specification 4.0 and supports speeds of up to 5 GT/s. It functions
> >> only as a root complex, with a single-lane (x1) configuration. The
> >> controller includes Type 1 configuration registers, as well as IP
> >> specific registers (called AXI registers) required for various adjustments.
> >>
> >> Other Renesas RZ SoCs (e.g., RZ/G3E, RZ/V2H) share the same AXI registers
> >> but have both Root Complex and Endpoint capabilities. As a result, the PCIe
> >> host driver can be reused for these variants with minimal adjustments.
> ...

> >> +static void rzg3s_pcie_irqs_init(struct rzg3s_pcie_host *host)
> > 
> > This and many of the following functions have names that don't
> > correspond to anything in other drivers, which makes it harder to
> > transfer knowledge between the drivers.  If you can find a pattern
> > somewhere to follow, it will make it easier for others to read the
> > driver.
> 
> OK, I'll think about it. Do you have a recomentation?

Not really.  Maybe pick a driver with recent activity.

> >> +static int rzg3s_pcie_probe(struct platform_device *pdev)
> >> +{
> >> +	struct device *dev = &pdev->dev;
> >> +	void *devres_group_id;
> >> +	int ret;
> >> +
> >> +	devres_group_id = devres_open_group(dev, NULL, GFP_KERNEL);
> >> +	if (!devres_group_id)
> >> +		return -ENOMEM;
> > 
> > What's the benefit of using devres_open_group()?  No other PCI
> > controller drivers use it.
> 
> This driver uses devm_add_action_or_reset() to keep the error path simpler.
> Some of the action or reset registered handlers access the controller
> registers. Because the driver is attached to the platform bus and the
> dev_pm_domain_detach() is called right after driver remove [1] having devm
> action or reset handlers accessing controller register will later lead to
> hangs when the device_unbind_cleanup() -> devres_release_all() will be
> called on remove path. Other issue described in [2] may arries when doing
> continuous unbind/bind if the driver has runtime PM API (not case for this
> driver at the moment) that access directly controller registers.
> 
> This is because the dev_pm_domain_detach() drops the clocks from PM domain
> and any subsequent pm_runtime_resume() (or similar function) call will lead
> to no runtime resume of the device.
> 
> There is a solution proposed to this here [2] but it slowly progresses.
> Until this will be solved I chosed the appraoch of having the devres group
> opened here. If you agree with it, I had the intention to drop this call if
> there will be an accepted solution for it. If you are OK with going forward
> like this, for the moment, would to prefer me to add a comment about the
> reason the devres_open_group() is used here?
> 
> This is not PCIe specific but platform bus specific. There are other
> affected drivers on this side (e.g. rzg2l-adc [3], rzg3s-thermal [4]).
> 
> A similar solution as [2] is already used by the i2c subsystem.

OK.  Is there something unique about rzg3s that means it needs
devres_open_group(), while other PCI controller drivers do not?  Or
should the other drivers be using it too?  Maybe they have similar
latent defects that should be fixed.

If there's something unique about rzg3s, please add a brief comment
about what it is so we know why it needs devres_open_group().

Bjorn


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

* Re: [PATCH 5/8] PCI: rzg3s-host: Add Initial PCIe Host Driver for Renesas RZ/G3S SoC
  2025-05-09 10:29       ` Claudiu Beznea
@ 2025-05-12 20:38         ` Bjorn Helgaas
  2025-05-14 10:29           ` Claudiu Beznea
  0 siblings, 1 reply; 28+ messages in thread
From: Bjorn Helgaas @ 2025-05-12 20:38 UTC (permalink / raw)
  To: Claudiu Beznea
  Cc: bhelgaas, lpieralisi, kw, manivannan.sadhasivam, robh, krzk+dt,
	conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, linux-arm-kernel, linux-clk, Claudiu Beznea

On Fri, May 09, 2025 at 01:29:40PM +0300, Claudiu Beznea wrote:
> On 05.05.2025 14:26, Claudiu Beznea wrote:
> > On 01.05.2025 23:12, Bjorn Helgaas wrote:
> >> On Wed, Apr 30, 2025 at 01:32:33PM +0300, Claudiu wrote:
> >>> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> >>>
> >>> The Renesas RZ/G3S features a PCIe IP that complies with the PCI Express
> >>> Base Specification 4.0 and supports speeds of up to 5 GT/s. It functions
> >>> only as a root complex, with a single-lane (x1) configuration. The
> >>> controller includes Type 1 configuration registers, as well as IP
> >>> specific registers (called AXI registers) required for various adjustments.
> >>>
> >>> Other Renesas RZ SoCs (e.g., RZ/G3E, RZ/V2H) share the same AXI registers
> >>> but have both Root Complex and Endpoint capabilities. As a result, the PCIe
> >>> host driver can be reused for these variants with minimal adjustments.
> ...

> >>> +static void rzg3s_pcie_update_bits(void __iomem *base, u32 offset, u32 mask, u32 val)
> >>> +{
> >>> +	u32 tmp;
> >>> +
> >>> +	tmp = readl(base + offset);
> >>> +	tmp &= ~mask;
> >>> +	tmp |= val & mask;
> >>> +	writel(tmp, base + offset);
> >>> +}
> >>
> >> Nothing rzg3s-specific here.
> >>
> >> I think u32p_replace_bits() (include/linux/bitfield.h) is basically this.
> > 
> > I wasn't aware of it. I'll use it in the next version. Thank for pointing it.
> 
> I look into changing to u32p_replace_bits() but this one needs a mask that
> can be verified at build time. It cannot be used directly in this function.
> Would you prefer me to replace all the calls to rzg3s_pcie_update_bits() with:
> 
> tmp = readl();
> u32p_replace_bits(&tmp, ...)
> writel(tmp);

It seems like this is the prevailing way it's used.

You have ~20 calls, so it seems like it might be excessive to replace
each with readl/u32p_replace_bits/writel.

But maybe you could use u32p_replace_bits() inside
rzg3s_pcie_update_bits().

Bjorn


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

* Re: [PATCH 5/8] PCI: rzg3s-host: Add Initial PCIe Host Driver for Renesas RZ/G3S SoC
  2025-05-12 20:25       ` Bjorn Helgaas
@ 2025-05-14  9:37         ` Claudiu Beznea
  0 siblings, 0 replies; 28+ messages in thread
From: Claudiu Beznea @ 2025-05-14  9:37 UTC (permalink / raw)
  To: Bjorn Helgaas
  Cc: bhelgaas, lpieralisi, kw, manivannan.sadhasivam, robh, krzk+dt,
	conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, linux-arm-kernel, linux-clk, Claudiu Beznea

Hi, Bjorn,

On 12.05.2025 23:25, Bjorn Helgaas wrote:
> On Mon, May 05, 2025 at 02:26:43PM +0300, Claudiu Beznea wrote:
>> On 01.05.2025 23:12, Bjorn Helgaas wrote:
>>> On Wed, Apr 30, 2025 at 01:32:33PM +0300, Claudiu wrote:
>>>> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>>>>
>>>> The Renesas RZ/G3S features a PCIe IP that complies with the PCI Express
>>>> Base Specification 4.0 and supports speeds of up to 5 GT/s. It functions
>>>> only as a root complex, with a single-lane (x1) configuration. The
>>>> controller includes Type 1 configuration registers, as well as IP
>>>> specific registers (called AXI registers) required for various adjustments.
>>>>
>>>> Other Renesas RZ SoCs (e.g., RZ/G3E, RZ/V2H) share the same AXI registers
>>>> but have both Root Complex and Endpoint capabilities. As a result, the PCIe
>>>> host driver can be reused for these variants with minimal adjustments.
>> ...
> 
>>>> +static void rzg3s_pcie_irqs_init(struct rzg3s_pcie_host *host)
>>>
>>> This and many of the following functions have names that don't
>>> correspond to anything in other drivers, which makes it harder to
>>> transfer knowledge between the drivers.  If you can find a pattern
>>> somewhere to follow, it will make it easier for others to read the
>>> driver.
>>
>> OK, I'll think about it. Do you have a recomentation?
> 
> Not really.  Maybe pick a driver with recent activity.
> 
>>>> +static int rzg3s_pcie_probe(struct platform_device *pdev)
>>>> +{
>>>> +	struct device *dev = &pdev->dev;
>>>> +	void *devres_group_id;
>>>> +	int ret;
>>>> +
>>>> +	devres_group_id = devres_open_group(dev, NULL, GFP_KERNEL);
>>>> +	if (!devres_group_id)
>>>> +		return -ENOMEM;
>>>
>>> What's the benefit of using devres_open_group()?  No other PCI
>>> controller drivers use it.
>>
>> This driver uses devm_add_action_or_reset() to keep the error path simpler.
>> Some of the action or reset registered handlers access the controller
>> registers. Because the driver is attached to the platform bus and the
>> dev_pm_domain_detach() is called right after driver remove [1] having devm
>> action or reset handlers accessing controller register will later lead to
>> hangs when the device_unbind_cleanup() -> devres_release_all() will be
>> called on remove path. Other issue described in [2] may arries when doing
>> continuous unbind/bind if the driver has runtime PM API (not case for this
>> driver at the moment) that access directly controller registers.
>>
>> This is because the dev_pm_domain_detach() drops the clocks from PM domain
>> and any subsequent pm_runtime_resume() (or similar function) call will lead
>> to no runtime resume of the device.
>>
>> There is a solution proposed to this here [2] but it slowly progresses.
>> Until this will be solved I chosed the appraoch of having the devres group
>> opened here. If you agree with it, I had the intention to drop this call if
>> there will be an accepted solution for it. If you are OK with going forward
>> like this, for the moment, would to prefer me to add a comment about the
>> reason the devres_open_group() is used here?
>>
>> This is not PCIe specific but platform bus specific. There are other
>> affected drivers on this side (e.g. rzg2l-adc [3], rzg3s-thermal [4]).
>>
>> A similar solution as [2] is already used by the i2c subsystem.
> 
> OK.  Is there something unique about rzg3s that means it needs
> devres_open_group(), while other PCI controller drivers do not?  Or
> should the other drivers be using it too?  Maybe they have similar
> latent defects that should be fixed.

Nothing particular for RZ/G3S. The issue is there for every drivers
attached to platform bus (at least the ones that have their clocks as part
of their PM domain as RZ/G3S is having) which are accessing IP registers
though devres cleanup APIs or are accessing IP registers in the runtime PM
APIs. This is because these accesses ends up to be done when the clocks
cannot be enabled anymore though runtime resume calls (detailed explanation
in [1]). This is the reason for which I am trying to impose it in the
platform bus driver, but the discussion is slowly progressing and not all
involved parties agrees with having the fix in the platform bus driver [1].

[1]
https://lore.kernel.org/all/20250215130849.227812-1-claudiu.beznea.uj@bp.renesas.com/

> 
> If there's something unique about rzg3s, please add a brief comment
> about what it is so we know why it needs devres_open_group().

In the initial version I've added a comment in the documentation of struct
rzg3s_pcie_host::devres_group_id. I'll update the place where this is call,
too.

Thank you for your review,
Claudiu


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

* Re: [PATCH 5/8] PCI: rzg3s-host: Add Initial PCIe Host Driver for Renesas RZ/G3S SoC
  2025-05-12 20:38         ` Bjorn Helgaas
@ 2025-05-14 10:29           ` Claudiu Beznea
  0 siblings, 0 replies; 28+ messages in thread
From: Claudiu Beznea @ 2025-05-14 10:29 UTC (permalink / raw)
  To: Bjorn Helgaas
  Cc: bhelgaas, lpieralisi, kw, manivannan.sadhasivam, robh, krzk+dt,
	conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, linux-arm-kernel, linux-clk, Claudiu Beznea

Hi, Bjorn,

On 12.05.2025 23:38, Bjorn Helgaas wrote:
> On Fri, May 09, 2025 at 01:29:40PM +0300, Claudiu Beznea wrote:
>> On 05.05.2025 14:26, Claudiu Beznea wrote:
>>> On 01.05.2025 23:12, Bjorn Helgaas wrote:
>>>> On Wed, Apr 30, 2025 at 01:32:33PM +0300, Claudiu wrote:
>>>>> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>>>>>
>>>>> The Renesas RZ/G3S features a PCIe IP that complies with the PCI Express
>>>>> Base Specification 4.0 and supports speeds of up to 5 GT/s. It functions
>>>>> only as a root complex, with a single-lane (x1) configuration. The
>>>>> controller includes Type 1 configuration registers, as well as IP
>>>>> specific registers (called AXI registers) required for various adjustments.
>>>>>
>>>>> Other Renesas RZ SoCs (e.g., RZ/G3E, RZ/V2H) share the same AXI registers
>>>>> but have both Root Complex and Endpoint capabilities. As a result, the PCIe
>>>>> host driver can be reused for these variants with minimal adjustments.
>> ...
> 
>>>>> +static void rzg3s_pcie_update_bits(void __iomem *base, u32 offset, u32 mask, u32 val)
>>>>> +{
>>>>> +	u32 tmp;
>>>>> +
>>>>> +	tmp = readl(base + offset);
>>>>> +	tmp &= ~mask;
>>>>> +	tmp |= val & mask;
>>>>> +	writel(tmp, base + offset);
>>>>> +}
>>>>
>>>> Nothing rzg3s-specific here.
>>>>
>>>> I think u32p_replace_bits() (include/linux/bitfield.h) is basically this.
>>>
>>> I wasn't aware of it. I'll use it in the next version. Thank for pointing it.
>>
>> I look into changing to u32p_replace_bits() but this one needs a mask that
>> can be verified at build time. It cannot be used directly in this function.
>> Would you prefer me to replace all the calls to rzg3s_pcie_update_bits() with:
>>
>> tmp = readl();
>> u32p_replace_bits(&tmp, ...)
>> writel(tmp);
> 
> It seems like this is the prevailing way it's used.
> 
> You have ~20 calls, so it seems like it might be excessive to replace
> each with readl/u32p_replace_bits/writel.
> 
> But maybe you could use u32p_replace_bits() inside
> rzg3s_pcie_update_bits().

I tried it like:

#define rzg3s_pcie_update_bits(base, offset, mask, val)	\
	do {						\
		u32 tmp = readl((base) + (offset));	\
		u32p_replace_bits(&tmp, (val), (mask));	\
		writel(tmp, (base) + (offset));		\
	} while (0)

But the mask may still depend on runtime variable. E.g. there is this call
in the driver (and other similar):

static void rzg3s_pcie_msi_irq_mask(struct irq_data *d)
{
        struct rzg3s_pcie_msi *msi = irq_data_get_irq_chip_data(d);
        struct rzg3s_pcie_host *host = rzg3s_msi_to_host(msi);
        u8 reg_bit = d->hwirq % RZG3S_PCI_MSI_INT_PER_REG;
        u8 reg_id = d->hwirq / RZG3S_PCI_MSI_INT_PER_REG;

        guard(raw_spinlock_irqsave)(&host->hw_lock);

        rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_MSIRM(reg_id),
                               BIT(reg_bit), BIT(reg_bit));

}

reg_id is a runtime variable and cannot be checked at compile time thus the
compilation of u32p_replace_bits() fails with:

../include/linux/bitfield.h:166:3: error: call to ‘__bad_mask’ declared
with attribute error: bad bitfield mask
  166 |   __bad_mask();
      |   ^~~~~~~~~~~~

Please let me know if you have other suggestions.

Thank you,
Claudiu


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

* Re: [PATCH 5/8] PCI: rzg3s-host: Add Initial PCIe Host Driver for Renesas RZ/G3S SoC
  2025-05-09 20:49   ` Rob Herring
@ 2025-05-14 11:39     ` Claudiu Beznea
  0 siblings, 0 replies; 28+ messages in thread
From: Claudiu Beznea @ 2025-05-14 11:39 UTC (permalink / raw)
  To: Rob Herring
  Cc: bhelgaas, lpieralisi, kw, manivannan.sadhasivam, krzk+dt,
	conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, linux-arm-kernel, linux-clk, Claudiu Beznea

Hi, Rob,

On 09.05.2025 23:49, Rob Herring wrote:
> On Wed, Apr 30, 2025 at 01:32:33PM +0300, Claudiu wrote:
>> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>>
>> The Renesas RZ/G3S features a PCIe IP that complies with the PCI Express
>> Base Specification 4.0 and supports speeds of up to 5 GT/s. It functions
>> only as a root complex, with a single-lane (x1) configuration. The
>> controller includes Type 1 configuration registers, as well as IP
>> specific registers (called AXI registers) required for various adjustments.
>>
>> Other Renesas RZ SoCs (e.g., RZ/G3E, RZ/V2H) share the same AXI registers
>> but have both Root Complex and Endpoint capabilities. As a result, the PCIe
>> host driver can be reused for these variants with minimal adjustments.
>>
>> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>> ---
>>  MAINTAINERS                              |    8 +
>>  drivers/pci/controller/Kconfig           |    7 +
>>  drivers/pci/controller/Makefile          |    1 +
>>  drivers/pci/controller/pcie-rzg3s-host.c | 1561 ++++++++++++++++++++++
>>  4 files changed, 1577 insertions(+)
>>  create mode 100644 drivers/pci/controller/pcie-rzg3s-host.c
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index e2e2942779eb..d7d985d174e2 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -19039,6 +19039,14 @@ S:	Maintained
>>  F:	drivers/pci/controller/dwc/pcie-qcom-common.c
>>  F:	drivers/pci/controller/dwc/pcie-qcom.c
>>  
>> +PCIE DRIVER FOR RENESAS RZ/G3S SERIES
>> +M:	Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>> +L:	linux-pci@vger.kernel.org
>> +L:	linux-renesas-soc@vger.kernel.org
>> +S:	Supported
>> +F:	Documentation/devicetree/bindings/pci/renesas,r9a08g045s33-pcie.yaml
>> +F:	drivers/pci/controller/pcie-rzg3s-host.c
>> +
>>  PCIE DRIVER FOR ROCKCHIP
>>  M:	Shawn Lin <shawn.lin@rock-chips.com>
>>  L:	linux-pci@vger.kernel.org
>> diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
>> index 9800b7681054..102f361fcc63 100644
>> --- a/drivers/pci/controller/Kconfig
>> +++ b/drivers/pci/controller/Kconfig
>> @@ -256,6 +256,13 @@ config PCI_RCAR_GEN2
>>  	  There are 3 internal PCI controllers available with a single
>>  	  built-in EHCI/OHCI host controller present on each one.
>>  
>> +config PCIE_RENESAS_RZG3S_HOST
>> +	tristate "Renesas RZ/G3S PCIe host controller"
>> +	depends on ARCH_RENESAS || COMPILE_TEST
>> +	select MFD_SYSCON
>> +	help
>> +	  Say Y here if you want PCIe host controller support on Renesas RZ/G3S SoC.
>> +
>>  config PCIE_ROCKCHIP
>>  	bool
>>  	depends on PCI
>> diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile
>> index 038ccbd9e3ba..229929a945c2 100644
>> --- a/drivers/pci/controller/Makefile
>> +++ b/drivers/pci/controller/Makefile
>> @@ -10,6 +10,7 @@ obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o
>>  obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
>>  obj-$(CONFIG_PCIE_RCAR_HOST) += pcie-rcar.o pcie-rcar-host.o
>>  obj-$(CONFIG_PCIE_RCAR_EP) += pcie-rcar.o pcie-rcar-ep.o
>> +obj-$(CONFIG_PCIE_RENESAS_RZG3S_HOST) += pcie-rzg3s-host.o
>>  obj-$(CONFIG_PCI_HOST_COMMON) += pci-host-common.o
>>  obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o
>>  obj-$(CONFIG_PCI_HOST_THUNDER_ECAM) += pci-thunder-ecam.o
>> diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c
>> new file mode 100644
>> index 000000000000..c3bce0acd57e
>> --- /dev/null
>> +++ b/drivers/pci/controller/pcie-rzg3s-host.c
>> @@ -0,0 +1,1561 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * PCIe driver for Renesas RZ/G3S SoCs
>> + *
>> + * Copyright (C) 2025 Renesas Electronics Corp.
>> + *
>> + * Based on:
>> + *  drivers/pci/controller/pcie-rcar-host.c
>> + *  Copyright (C) 2009 - 2011  Paul Mundt
>> + */
>> +
>> +#include <linux/bitmap.h>
>> +#include <linux/bitops.h>
>> +#include <linux/cleanup.h>
>> +#include <linux/delay.h>
>> +#include <linux/device/devres.h>
>> +#include <linux/iopoll.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/irq.h>
>> +#include <linux/irqchip/chained_irq.h>
>> +#include <linux/irqdomain.h>
>> +#include <linux/kernel.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/msi.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/pci.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/regmap.h>
>> +#include <linux/reset.h>
>> +#include <linux/slab.h>
>> +
>> +/* AXI registers. */
>> +#define RZG3S_PCI_REQDATA(id)			(0x80 + (id) * 0x4)
>> +#define RZG3S_PCI_REQRCVDAT			0x8c
>> +#define RZG3S_PCI_REQADR1			0x90
>> +#define RZG3S_PCI_REQADR1_BUS			GENMASK(31, 24)
>> +#define RZG3S_PCI_REQADR1_DEV			GENMASK(23, 19)
>> +#define RZG3S_PCI_REQADR1_FUNC			GENMASK(18, 16)
>> +#define RZG3S_PCI_REQADR1_REG			GENMASK(11, 0)
>> +#define RZG3S_PCI_REQBE				0x98
>> +#define RZG3S_PCI_REQBE_BYTE_EN			GENMASK(3, 0)
>> +#define RZG3S_PCI_REQISS			0x9c
>> +#define RZG3S_PCI_REQISS_REQ_ISSUE		BIT(0)
>> +#define RZG3S_PCI_REQISS_TR_TYPE		GENMASK(11, 8)
>> +#define RZG3S_PCI_REQISS_TR_TP0_RD		FIELD_PREP(RZG3S_PCI_REQISS_TR_TYPE, 0x4)
>> +#define RZG3S_PCI_REQISS_TR_TP0_WR		FIELD_PREP(RZG3S_PCI_REQISS_TR_TYPE, 0x5)
>> +#define RZG3S_PCI_REQISS_TR_TP1_RD		FIELD_PREP(RZG3S_PCI_REQISS_TR_TYPE, 0x6)
>> +#define RZG3S_PCI_REQISS_TR_TP1_WR		FIELD_PREP(RZG3S_PCI_REQISS_TR_TYPE, 0x7)
>> +#define RZG3S_PCI_REQISS_MOR_STATUS		GENMASK(18, 16)
>> +#define RZG3S_PCI_MSIRCVWADRL			0x100
>> +#define RZG3S_PCI_MSIRCVWADRL_ENA		BIT(0)
>> +#define RZG3S_PCI_MSIRCVWADRL_MSG_DATA_ENA	BIT(1)
>> +#define RZG3S_PCI_MSIRCVWADRU			0x104
>> +#define RZG3S_PCI_MSIRCVWMSKL			0x108
>> +#define RZG3S_PCI_MSIRCVWMSKU			0x10c
>> +#define RZG3S_PCI_PINTRCVIE			0x110
>> +#define RZG3S_PCI_PINTRCVIE_INTX(i)		BIT(i)
>> +#define RZG3S_PCI_PINTRCVIE_MSI			BIT(4)
>> +#define RZG3S_PCI_PINTRCVIS			0x114
>> +#define RZG3S_PCI_PINTRCVIS_INTX(i)		BIT(i)
>> +#define RZG3S_PCI_PINTRCVIS_MSI			BIT(4)
>> +#define RZG3S_PCI_MSGRCVIE			0x120
>> +#define RZG3S_PCI_MSGRCVIE_MSG_RCV		BIT(24)
>> +#define RZG3S_PCI_MSGRCVIS			0x124
>> +#define RZG3S_PCI_MSGRCVIS_MRI			BIT(24)
>> +#define RZG3S_PCI_PEIE0				0x200
>> +#define RZG3S_PCI_PEIS0				0x204
>> +#define RZG3S_PCI_PEIS0_DL_UPDOWN		BIT(9)
>> +#define RZG3S_PCI_PEIS0_RX_DLLP_PM_ENTER	BIT(12)
>> +#define RZG3S_PCI_PEIE1				0x208
>> +#define RZG3S_PCI_PEIS1				0x20c
>> +#define RZG3S_PCI_AMEIE				0x210
>> +#define RZG3S_PCI_AMEIS				0x214
>> +#define RZG3S_PCI_ASEIE1			0x220
>> +#define RZG3S_PCI_ASEIS1			0x224
>> +#define RZG3S_PCI_PCSTAT1			0x408
>> +#define RZG3S_PCI_PCSTAT1_DL_DOWN_STS		BIT(0)
>> +#define RZG3S_PCI_PCSTAT1_LTSSM_STATE		GENMASK(14, 10)
>> +#define RZG3S_PCI_PCCTRL2			0x410
>> +#define RZG3S_PCI_PCCTRL2_LS_CHG_REQ		BIT(0)
>> +#define RZG3S_PCI_PCCTRL2_LS_CHG		GENMASK(9, 8)
>> +#define RZG3S_PCI_PCSTAT2			0x414
>> +#define RZG3S_PCI_PCSTAT2_STATE_RX_DETECT	GENMASK(15, 8)
>> +#define RZG3S_PCI_PCSTAT2_SDRIRE		GENMASK(7, 0)
>> +#define RZG3S_PCI_PCSTAT2_LS_CHG_DONE		BIT(28)
>> +#define RZG3S_PCI_PERM				0x300
>> +#define RZG3S_PCI_PERM_PIPE_PHY_REG_EN		BIT(1)
>> +#define RZG3S_PCI_PERM_CFG_HWINIT_EN		BIT(2)
>> +#define RZG3S_PCI_MSIRE(id)			(0x600 + (id) * 0x10)
>> +#define RZG3S_PCI_MSIRE_ENA			BIT(0)
>> +#define RZG3S_PCI_MSIRM(id)			(0x608 + (id) * 0x10)
>> +#define RZG3S_PCI_MSIRS(id)			(0x60c + (id) * 0x10)
>> +#define RZG3S_PCI_AWBASEL(id)			(0x1000 + (id) * 0x20)
>> +#define RZG3S_PCI_AWBASEL_WIN_ENA		BIT(0)
>> +#define RZG3S_PCI_AWBASEU(id)			(0x1004 + (id) * 0x20)
>> +#define RZG3S_PCI_AWMASKL(id)			(0x1008 + (id) * 0x20)
>> +#define RZG3S_PCI_AWMASKU(id)			(0x100c + (id) * 0x20)
>> +#define RZG3S_PCI_ADESTL(id)			(0x1010 + (id) * 0x20)
>> +#define RZG3S_PCI_ADESTU(id)			(0x1014 + (id) * 0x20)
>> +#define RZG3S_PCI_PWBASEL(id)			(0x1100 + (id) * 0x20)
>> +#define RZG3S_PCI_PWBASEL_ENA			BIT(0)
>> +#define RZG3S_PCI_PWBASEU(id)			(0x1104 + (id) * 0x20)
>> +#define RZG3S_PCI_PDESTL(id)			(0x1110 + (id) * 0x20)
>> +#define RZG3S_PCI_PDESTU(id)			(0x1114 + (id) * 0x20)
>> +#define RZG3S_PCI_PWMASKL(id)			(0x1108 + (id) * 0x20)
>> +#define RZG3S_PCI_PWMASKU(id)			(0x110c + (id) * 0x20)
>> +
>> +/* PHY control registers. */
>> +#define RZG3S_PCI_PHY_XCFGD(id)			(0x2000 + (id) * 0x10)
>> +#define RZG3S_PCI_PHY_XCFGD_NUM			39
>> +#define RZG3S_PCI_PHY_XCFGA_CMN(id)		(0x2400 + (id) * 0x10)
>> +#define RZG3S_PCI_PHY_XCFGA_CMN_NUM		16
>> +#define RZG3S_PCI_PHY_XCFGA_RX(id)		(0x2500 + (id) * 0x10)
>> +#define RZG3S_PCI_PHY_XCFGA_RX_NUM		13
>> +#define RZG3S_PCI_PHY_XCFGA_TX			0x25d0
>> +#define RZG3S_PCI_PHY_XCFG_CTRL			0x2a20
>> +#define RZG3S_PCI_PHY_XCFG_CTRL_PHYREG_SEL	BIT(0)
> 
> Some reason the PHY can't be a PHY driver? Registers look somewhat 
> separate at least.

Registers are at separate offset compared to AXI and PCIe registers,
indeed. But they are all part of the PCIe controller address space, same
clocks are used to access them, same resets.

I thought at implemeting it as a separate driver considering that I will
attach it to the auxiliary bus. For that, if I'm not wrong, I would have
been registering that PHY driver from this PCIe driver probe function and
later on, in the PCIe driver probe, I would have been requesting the same
previously registered PHY. For this, I consider to be overcomplicated thus
I ended up having it as proposed here.

> 
>> +
>> +/* PCIe registers. */
>> +#define RZG3S_PCI_CFG_BASE			0x6000
>> +#define RZG3S_PCI_CFG_VID			0x0
> 
> Don't redefine standard config space defines. This is just 
> PCI_VENDOR_ID.

I thought at it. I used it like this to have this offset and the above
registers mask (for configuring device id and vendor id )grouped toghether.
I also noticed the approach accross already preset drivers is mixed.

> 
>> +#define RZG3S_PCI_CFG_VID_DEVICE		GENMASK(31, 16)
>> +#define RZG3S_PCI_CFG_VID_VENDOR		GENMASK(15, 0)
>> +#define RZG3S_PCI_CFG_BNR			0x18
> 
> This is PCI_PRIMARY_BUS.

Agree. I used it like this with the same rationale as expresed above.

> 
>> +#define RZG3S_PCI_CFG_BNR_SUBORDINATE_BUS	GENMASK(23, 16)
>> +#define RZG3S_PCI_CFG_BNR_SECONDARY_BUS		GENMASK(15, 8)
>> +#define RZG3S_PCI_CFG_BARMSK00L			0xa0
>> +#define RZG3S_PCI_CFG_BARMSK00U			0xa4
>> +#define RZG3S_PCI_CFG_LINKCS			0x70
>> +#define RZG3S_PCI_CFG_LINKCS_CUR_LS		GENMASK(19, 16)
>> +#define RZG3S_PCI_CFG_LINCS2			0x90
>> +#define RZG3S_PCI_CFG_LINCS2_TARGET_LS		GENMASK(3, 0)
>> +
>> +/* System controller registers. */
>> +#define RZG3S_SYS_PCIE_RST_RSM_B		0xd74
>> +#define RZG3S_SYS_PCIE_RST_RSM_B_MASK		BIT(0)
>> +
>> +/* Maximum number of windows. */
>> +#define RZG3S_MAX_WINDOWS			8
>> +
>> +/* Number of MSI interrupts per register. */
>> +#define RZG3S_PCI_MSI_INT_PER_REG		32
>> +/* The number of MSI interrupts. */
>> +#define RZG3S_PCI_MSI_INT_NR			(RZG3S_PCI_MSI_INT_PER_REG * 2)
>> +
>> +/**
>> + * enum rzg3s_pcie_link_speed - RZ/G3S PCIe available link speeds
>> + * @RZG3S_PCIE_LINK_SPEED_2_5_GTS: 2.5 GT/s
>> + * @RZG3S_PCIE_LINK_SPEED_5_0_GTS: 5.0 GT/s
>> + */
>> +enum rzg3s_pcie_link_speed {
>> +	RZG3S_PCIE_LINK_SPEED_2_5_GTS = 1,
>> +	RZG3S_PCIE_LINK_SPEED_5_0_GTS
>> +};
>> +
>> +/**
>> + * struct rzg3s_pcie_msi - RZ/G3S PCIe MSI data structure
>> + * @domain: IRQ domain
>> + * @map: bitmap with the allocated MSIs
>> + * @dma_addr: address of the allocated MSI window
>> + * @window_base: base address of the MSI window
>> + * @pages: allocated pages for MSI window mapping
>> + * @map_lock: lock for bitmap with the allocated MSIs
>> + */
>> +struct rzg3s_pcie_msi {
>> +	struct irq_domain *domain;
>> +	DECLARE_BITMAP(map, RZG3S_PCI_MSI_INT_NR);
>> +	dma_addr_t dma_addr;
>> +	dma_addr_t window_base;
>> +	unsigned long pages;
>> +	struct mutex map_lock;
>> +};
>> +
>> +/**
>> + * struct rzg3s_pcie_host - RZ/G3S PCIe data structure
>> + * @axi: base address for AXI registers
>> + * @pcie: base address for PCIe registers
>> + * @dev: struct device
>> + * @power_resets: reset control signals that should be set after power up
>> + * @cfg_resets: reset control signals that should be set after configuration
>> + * @phy_init: PHY specific initialization function
>> + * @sysc: SYSC regmap
>> + * @intx_domain: INTx IRQ domain
>> + * @devres_group_id: devres group which allows unconfiguring everything at
>> + * the end of the driver remove function (or, on probe failure path, just after
>> + * leaving the driver probe function); otherwise, the dev_pm_domain_detach()
>> + * called in the platform driver remove function disable the PCIe clocks leaving
>> + * the other action or reset function being executed (later) with clocks disabled;
>> + * this leads to system being blocked and eventually restarted by watchdog
>> + * @msi: MSI data structure
>> + * @hw_lock: lock for access to the HW resources
>> + * @intx_irqs: INTx interrupts
>> + * @vendor_id: Vendor ID
>> + * @device_id: Device ID
>> + * @num_power_resets: the number of power_resets
>> + * @num_cfg_resets: the number of cfg_resets
>> + */
>> +struct rzg3s_pcie_host {
>> +	void __iomem *axi;
>> +	void __iomem *pcie;
>> +	struct device *dev;
>> +	struct reset_control **power_resets;
>> +	struct reset_control **cfg_resets;
>> +	int (*phy_init)(struct rzg3s_pcie_host *host);
>> +	struct regmap *sysc;
>> +	struct irq_domain *intx_domain;
>> +	void *devres_group_id;
>> +	struct rzg3s_pcie_msi msi;
>> +	raw_spinlock_t hw_lock;
>> +	int intx_irqs[PCI_NUM_INTX];
>> +	u32 vendor_id;
>> +	u32 device_id;
>> +	u8 num_power_resets;
>> +	u8 num_cfg_resets;
>> +};
>> +
>> +#define rzg3s_msi_to_host(_msi)	container_of(_msi, struct rzg3s_pcie_host, msi)
>> +
>> +/**
>> + * enum rzg3s_pcie_cfg_access_type - RZ/G3S PCIe access type
>> + * @RZG3S_PCIE_CFG_ACCESS_RD: read access
>> + * @RZG3S_PCIE_CFG_ACCESS_WR: write access
>> + */
>> +enum rzg3s_pcie_cfg_access_type {
>> +	RZG3S_PCIE_CFG_ACCESS_RD,
>> +	RZG3S_PCIE_CFG_ACCESS_WR,
>> +};
>> +
>> +static void rzg3s_pcie_update_bits(void __iomem *base, u32 offset, u32 mask, u32 val)
>> +{
>> +	u32 tmp;
>> +
>> +	tmp = readl(base + offset);
>> +	tmp &= ~mask;
>> +	tmp |= val & mask;
>> +	writel(tmp, base + offset);
>> +}
>> +
>> +static bool rzg3s_pcie_issue_request(struct rzg3s_pcie_host *host)
>> +{
>> +	u32 val;
>> +	int ret;
>> +
>> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_REQISS, RZG3S_PCI_REQISS_REQ_ISSUE,
>> +			       RZG3S_PCI_REQISS_REQ_ISSUE);
>> +	ret = readl_poll_timeout_atomic(host->axi + RZG3S_PCI_REQISS, val,
>> +					!(val & RZG3S_PCI_REQISS_REQ_ISSUE),
>> +					5, 2500);
>> +
>> +	return !!ret || (val & RZG3S_PCI_REQISS_MOR_STATUS);
>> +}
>> +
>> +static int rzg3s_pcie_config_access(struct rzg3s_pcie_host *host, struct pci_bus *bus,
>> +				    unsigned int devfn, int where, u32 *data,
>> +				    enum rzg3s_pcie_cfg_access_type access_type)
>> +{
>> +	bool type0 = pci_is_root_bus(bus->parent) ? true : false;
>> +	unsigned int dev, func, reg, tr_type;
>> +	int ret;
>> +
>> +	dev = PCI_SLOT(devfn);
>> +	func = PCI_FUNC(devfn);
>> +	reg = where & ~0x3;
>> +
>> +	/* Set the destination. */
>> +	writel(FIELD_PREP(RZG3S_PCI_REQADR1_BUS, bus->number) |
>> +	       FIELD_PREP(RZG3S_PCI_REQADR1_DEV, dev) |
>> +	       FIELD_PREP(RZG3S_PCI_REQADR1_FUNC, func) |
>> +	       FIELD_PREP(RZG3S_PCI_REQADR1_REG, reg),
>> +	       host->axi + RZG3S_PCI_REQADR1);
>> +
>> +	/* Set byte enable. */
>> +	writel(RZG3S_PCI_REQBE_BYTE_EN, host->axi + RZG3S_PCI_REQBE);
> 
> All the above can go in .map_bus().

I'll check it. Thank you for pointing it.

> 
>> +
>> +	if (access_type == RZG3S_PCIE_CFG_ACCESS_RD) {
>> +		tr_type = type0 ? RZG3S_PCI_REQISS_TR_TP0_RD : RZG3S_PCI_REQISS_TR_TP1_RD;
>> +	} else {
>> +		tr_type = type0 ? RZG3S_PCI_REQISS_TR_TP0_WR : RZG3S_PCI_REQISS_TR_TP1_WR;
>> +
>> +		/* Set the write data.  */
>> +		writel(0, host->axi + RZG3S_PCI_REQDATA(0));
>> +		writel(0, host->axi + RZG3S_PCI_REQDATA(1));
>> +		writel(*data, host->axi + RZG3S_PCI_REQDATA(2));
>> +	}
>> +
>> +	/* Set the type of request. */
>> +	writel(tr_type, host->axi + RZG3S_PCI_REQISS);
> 
> Split all this into read and write helpers or just move these to the 
> callers. IOW, get rid of all the 'if read' or 'if write' conditionals.

OK.

> 
>> +
>> +	/* Issue the request and wait to finish. */
>> +	ret = rzg3s_pcie_issue_request(host);
>> +	if (ret) {
>> +		if (access_type == RZG3S_PCIE_CFG_ACCESS_RD)
>> +			PCI_SET_ERROR_RESPONSE(data);
>> +
>> +		return PCIBIOS_SET_FAILED;
>> +	}
>> +
>> +	/* Read the data. */
>> +	if (access_type == RZG3S_PCIE_CFG_ACCESS_RD)
>> +		*data = readl(host->axi + RZG3S_PCI_REQRCVDAT);
>> +
>> +	return PCIBIOS_SUCCESSFUL;
>> +}
>> +
>> +static int rzg3s_pcie_read_config_access(struct rzg3s_pcie_host *host, struct pci_bus *bus,
>> +					 unsigned int devfn, int where, u32 *data)
>> +{
>> +	/* Controller can't address itself. Just read the registers. */
>> +	if (pci_is_root_bus(bus) && !devfn) {
> 
> There are 2 sets of bus ops for root bus and child buses. Use them.

I'll check it.

> 
>> +		u32 reg = where & ~0x3;
>> +
>> +		*data = readl(host->pcie + reg);
>> +		return PCIBIOS_SUCCESSFUL;
>> +	}
>> +
>> +	return rzg3s_pcie_config_access(host, bus, devfn, where, data, RZG3S_PCIE_CFG_ACCESS_RD);
>> +}
>> +
>> +static int rzg3s_pcie_read_conf(struct pci_bus *bus, unsigned int devfn, int where, int size,
>> +				u32 *val)
>> +{
>> +	struct rzg3s_pcie_host *host = bus->sysdata;
>> +	int ret;
>> +
>> +	/*
>> +	 * Bus number for root complex is set to zero. Skip other requests for
>> +	 * vendor and device ID (where = 0x00).
>> +	 */
>> +	if (pci_is_root_bus(bus) && PCI_SLOT(devfn) && !where)
>> +		return PCIBIOS_DEVICE_NOT_FOUND;
>> +
>> +	ret = rzg3s_pcie_read_config_access(host, bus, devfn, where, val);
>> +	if (ret != PCIBIOS_SUCCESSFUL)
>> +		return ret;
>> +
>> +	if (size == 1)
>> +		*val = (*val >> (BITS_PER_BYTE * (where & 3))) & 0xff;
>> +	else if (size == 2)
>> +		*val = (*val >> (BITS_PER_BYTE * (where & 2))) & 0xffff;
> 
> If you can't use the core implementation, at least copy it:
> 
>         if (size <= 2)
>                 *val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1);

OK

> 
>> +
>> +	dev_dbg(&bus->dev, "%s(): bus=%3d devfn=0x%04x where=0x%04x size=%d val=0x%08x\n",
>> +		__func__, bus->number, devfn, where, size, *val);
> 
> If this kind of tracing is important, put it in the core code for 
> everyone to use. Might already be there, I don't remember.

OK

> 
>> +
>> +	return PCIBIOS_SUCCESSFUL;
>> +}
>> +
>> +static int rzg3s_pcie_write_config_access(struct rzg3s_pcie_host *host, struct pci_bus *bus,
>> +					  unsigned int devfn, int where, u32 data)
>> +{
>> +	/* Controller can't target itself. */
>> +	if (pci_is_root_bus(bus) && !devfn) {
>> +		u32 reg = where & ~0x3;
>> +
>> +		/* Enable access control to the CFGU. */
>> +		writel(RZG3S_PCI_PERM_CFG_HWINIT_EN, host->axi + RZG3S_PCI_PERM);
>> +		/* Write data. */
>> +		writel(data, host->pcie + reg);
>> +		/* Disable access control to the CFGU. */
>> +		writel(0, host->axi + RZG3S_PCI_PERM);
> 
> If you make this just a root bus write op, I think this could be:
> 
> {
> 	writel(RZG3S_PCI_PERM_CFG_HWINIT_EN, host->axi + RZG3S_PCI_PERM);
> 	ret = pci_generic_config_write32(...);
> 	writel(0, host->axi + RZG3S_PCI_PERM);
> 
> 	return ret;
> }

Will look into it, thanks for pointer.

> 
>> +
>> +		return PCIBIOS_SUCCESSFUL;
>> +	}
>> +
>> +	return rzg3s_pcie_config_access(host, bus, devfn, where, &data, RZG3S_PCIE_CFG_ACCESS_WR);
>> +}
>> +
>> +static int rzg3s_pcie_write_conf(struct pci_bus *bus, unsigned int devfn, int where, int size,
>> +				 u32 val)
>> +{
>> +	struct rzg3s_pcie_host *host = bus->sysdata;
>> +	u32 shift, data;
>> +	int ret;
>> +
>> +	ret = rzg3s_pcie_read_config_access(host, bus, devfn, where, &data);
>> +	if (ret != PCIBIOS_SUCCESSFUL)
>> +		return ret;
>> +
>> +	dev_dbg(&bus->dev, "%s(): bus=%3d devfn=0x%04x where=0x%04x size=%d val=0x%08x\n",
>> +		__func__, bus->number, devfn, where, size, val);
>> +
>> +	if (size == 1) {
>> +		shift = BITS_PER_BYTE * (where & 3);
>> +		data &= ~(0xff << shift);
>> +		data |= ((val & 0xff) << shift);
>> +	} else if (size == 2) {
>> +		shift = BITS_PER_BYTE * (where & 2);
>> +		data &= ~(0xffff << shift);
>> +		data |= ((val & 0xffff) << shift);
>> +	} else {
>> +		data = val;
>> +	}
>> +
>> +	return rzg3s_pcie_write_config_access(host, bus, devfn, where, data);
>> +}
>> +
>> +static struct pci_ops rzg3s_pcie_ops = {
>> +	.read	= rzg3s_pcie_read_conf,
>> +	.write	= rzg3s_pcie_write_conf,
>> +};
>> +
>> +static void rzg3s_pcie_intx_irq_handler(struct irq_desc *desc)
>> +{
>> +	struct rzg3s_pcie_host *host = irq_desc_get_handler_data(desc);
>> +	struct irq_chip *chip = irq_desc_get_chip(desc);
>> +	unsigned int irq = irq_desc_get_irq(desc);
>> +	u32 intx = irq - host->intx_irqs[0];
>> +
>> +	chained_irq_enter(chip, desc);
>> +	generic_handle_domain_irq(host->intx_domain, intx);
>> +	chained_irq_exit(chip, desc);
>> +}
>> +
>> +static irqreturn_t rzg3s_pcie_msi_irq(int irq, void *data)
>> +{
>> +	DECLARE_BITMAP(bitmap, RZG3S_PCI_MSI_INT_NR);
>> +	struct rzg3s_pcie_host *host = data;
>> +	struct rzg3s_pcie_msi *msi = &host->msi;
>> +	unsigned long bit;
>> +	u32 status;
>> +
>> +	status = readl(host->axi + RZG3S_PCI_PINTRCVIS);
>> +	if (!(status & RZG3S_PCI_PINTRCVIS_MSI))
>> +		return IRQ_NONE;
>> +
>> +	/* Clear the MSI. */
>> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PINTRCVIS, RZG3S_PCI_PINTRCVIS_MSI,
>> +			       RZG3S_PCI_PINTRCVIS_MSI);
>> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_MSGRCVIS, RZG3S_PCI_MSGRCVIS_MRI,
>> +			       RZG3S_PCI_MSGRCVIS_MRI);
>> +
>> +	for (u8 reg_id = 0; reg_id < RZG3S_PCI_MSI_INT_NR / RZG3S_PCI_MSI_INT_PER_REG; reg_id++) {
>> +		status = readl(host->axi + RZG3S_PCI_MSIRS(reg_id));
>> +		bitmap_write(bitmap, status, reg_id * RZG3S_PCI_MSI_INT_PER_REG,
>> +			     RZG3S_PCI_MSI_INT_PER_REG);
>> +	}
>> +
>> +	for_each_set_bit(bit, bitmap, RZG3S_PCI_MSI_INT_NR) {
>> +		int ret;
>> +
>> +		ret = generic_handle_domain_irq(msi->domain->parent, bit);
>> +		if (ret) {
>> +			u8 reg_bit = bit % RZG3S_PCI_MSI_INT_PER_REG;
>> +			u8 reg_id = bit / RZG3S_PCI_MSI_INT_PER_REG;
>> +
>> +			/* Unknown MSI, just clear it. */
>> +			writel(BIT(reg_bit), host->axi + RZG3S_PCI_MSIRS(reg_id));
>> +		}
>> +	}
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static void rzg3s_pcie_msi_top_irq_ack(struct irq_data *d)
>> +{
>> +	irq_chip_ack_parent(d);
>> +}
>> +
>> +static void rzg3s_pcie_msi_top_irq_mask(struct irq_data *d)
>> +{
>> +	pci_msi_mask_irq(d);
>> +	irq_chip_mask_parent(d);
>> +}
>> +
>> +static void rzg3s_pcie_msi_top_irq_unmask(struct irq_data *d)
>> +{
>> +	pci_msi_unmask_irq(d);
>> +	irq_chip_unmask_parent(d);
>> +}
>> +
>> +static struct irq_chip rzg3s_pcie_msi_top_chip = {
>> +	.name		= "PCIe MSI",
>> +	.irq_ack	= rzg3s_pcie_msi_top_irq_ack,
>> +	.irq_mask	= rzg3s_pcie_msi_top_irq_mask,
>> +	.irq_unmask	= rzg3s_pcie_msi_top_irq_unmask,
>> +};
>> +
>> +static void rzg3s_pcie_msi_irq_ack(struct irq_data *d)
>> +{
>> +	struct rzg3s_pcie_msi *msi = irq_data_get_irq_chip_data(d);
>> +	struct rzg3s_pcie_host *host = rzg3s_msi_to_host(msi);
>> +	u8 reg_bit = d->hwirq % RZG3S_PCI_MSI_INT_PER_REG;
>> +	u8 reg_id = d->hwirq / RZG3S_PCI_MSI_INT_PER_REG;
>> +
>> +	guard(raw_spinlock_irqsave)(&host->hw_lock);
>> +
>> +	writel(BIT(reg_bit), host->axi + RZG3S_PCI_MSIRS(reg_id));
>> +}
>> +
>> +static void rzg3s_pcie_msi_irq_mask(struct irq_data *d)
>> +{
>> +	struct rzg3s_pcie_msi *msi = irq_data_get_irq_chip_data(d);
>> +	struct rzg3s_pcie_host *host = rzg3s_msi_to_host(msi);
>> +	u8 reg_bit = d->hwirq % RZG3S_PCI_MSI_INT_PER_REG;
>> +	u8 reg_id = d->hwirq / RZG3S_PCI_MSI_INT_PER_REG;
>> +
>> +	guard(raw_spinlock_irqsave)(&host->hw_lock);
>> +
>> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_MSIRM(reg_id), BIT(reg_bit), BIT(reg_bit));
>> +}
>> +
>> +static void rzg3s_pcie_msi_irq_unmask(struct irq_data *d)
>> +{
>> +	struct rzg3s_pcie_msi *msi = irq_data_get_irq_chip_data(d);
>> +	struct rzg3s_pcie_host *host = rzg3s_msi_to_host(msi);
>> +	u8 reg_bit = d->hwirq % RZG3S_PCI_MSI_INT_PER_REG;
>> +	u8 reg_id = d->hwirq / RZG3S_PCI_MSI_INT_PER_REG;
>> +
>> +	guard(raw_spinlock_irqsave)(&host->hw_lock);
>> +
>> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_MSIRM(reg_id), BIT(reg_bit), 0);
>> +}
>> +
>> +static void rzg3s_pcie_msi_compose_msg(struct irq_data *data, struct msi_msg *msg)
>> +{
>> +	struct rzg3s_pcie_msi *msi = irq_data_get_irq_chip_data(data);
>> +	struct rzg3s_pcie_host *host = rzg3s_msi_to_host(msi);
>> +	u32 lo, hi;
>> +
>> +	/* Enable and msg data enable bits are part of the address lo. Drop them. */
>> +	lo = readl(host->axi + RZG3S_PCI_MSIRCVWADRL) &
>> +		   ~(RZG3S_PCI_MSIRCVWADRL_ENA | RZG3S_PCI_MSIRCVWADRL_MSG_DATA_ENA);
>> +	hi = readl(host->axi + RZG3S_PCI_MSIRCVWADRU);
>> +
>> +	msg->address_lo = lo;
>> +	msg->address_hi = hi;
>> +	msg->data = data->hwirq;
>> +}
>> +
>> +static struct irq_chip rzg3s_pcie_msi_bottom_chip = {
>> +	.name			= "rz-pcie-msi",
>> +	.irq_ack		= rzg3s_pcie_msi_irq_ack,
>> +	.irq_mask		= rzg3s_pcie_msi_irq_mask,
>> +	.irq_unmask		= rzg3s_pcie_msi_irq_unmask,
>> +	.irq_compose_msi_msg	= rzg3s_pcie_msi_compose_msg,
>> +};
>> +
>> +static int rzg3s_pcie_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
>> +				       unsigned int nr_irqs, void *args)
>> +{
>> +	struct rzg3s_pcie_msi *msi = domain->host_data;
>> +	int hwirq;
>> +
>> +	scoped_guard(mutex, &msi->map_lock) {
>> +		hwirq = bitmap_find_free_region(msi->map, RZG3S_PCI_MSI_INT_NR,
>> +						order_base_2(nr_irqs));
>> +	}
>> +
>> +	if (hwirq < 0)
>> +		return -ENOSPC;
>> +
>> +	for (unsigned int i = 0; i < nr_irqs; i++) {
>> +		irq_domain_set_info(domain, virq + i, hwirq + i, &rzg3s_pcie_msi_bottom_chip,
>> +				    domain->host_data, handle_edge_irq, NULL, NULL);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static void rzg3s_pcie_msi_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 rzg3s_pcie_msi *msi = domain->host_data;
>> +
>> +	guard(mutex)(&msi->map_lock);
>> +
>> +	bitmap_release_region(msi->map, d->hwirq, order_base_2(nr_irqs));
>> +}
>> +
>> +static const struct irq_domain_ops rzg3s_pcie_msi_domain_ops = {
>> +	.alloc	= rzg3s_pcie_msi_domain_alloc,
>> +	.free	= rzg3s_pcie_msi_domain_free,
>> +};
>> +
>> +static struct msi_domain_info rzg3s_pcie_msi_info = {
>> +	.flags	= MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
>> +		  MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_NO_AFFINITY,
>> +	.chip	= &rzg3s_pcie_msi_top_chip,
>> +};
>> +
>> +static int rzg3s_pcie_msi_allocate_domains(struct rzg3s_pcie_msi *msi)
>> +{
>> +	struct rzg3s_pcie_host *host = rzg3s_msi_to_host(msi);
>> +	struct device *dev = host->dev;
>> +	struct fwnode_handle *fwnode = dev_fwnode(dev);
>> +	struct irq_domain *parent;
>> +
>> +	parent = irq_domain_create_linear(fwnode, RZG3S_PCI_MSI_INT_NR,
>> +					  &rzg3s_pcie_msi_domain_ops, msi);
>> +	if (!parent)
>> +		return dev_err_probe(dev, -ENOMEM, "failed to create IRQ domain\n");
>> +	irq_domain_update_bus_token(parent, DOMAIN_BUS_NEXUS);
>> +
>> +	msi->domain = pci_msi_create_irq_domain(fwnode, &rzg3s_pcie_msi_info, parent);
>> +	if (!msi->domain) {
>> +		irq_domain_remove(parent);
>> +		return dev_err_probe(dev, -ENOMEM, "failed to create MSI domain\n");
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static void rzg3s_pcie_msi_free_domains(struct rzg3s_pcie_msi *msi)
>> +{
>> +	struct irq_domain *parent = msi->domain->parent;
>> +
>> +	irq_domain_remove(msi->domain);
>> +	irq_domain_remove(parent);
>> +}
>> +
>> +static int rzg3s_pcie_msi_hw_setup(struct rzg3s_pcie_host *host)
>> +{
>> +	struct rzg3s_pcie_msi *msi = &host->msi;
>> +
>> +	/* Set MSI window size. HW will set the window to RZG3S_PCI_MSI_INT_NR * 4 bytes. */
>> +	writel(RZG3S_PCI_MSI_INT_NR - 1, host->axi + RZG3S_PCI_MSIRCVWMSKL);
>> +
>> +	/* Set MSI window address and enable MSI window. */
>> +	writel(upper_32_bits(msi->window_base), host->axi + RZG3S_PCI_MSIRCVWADRU);
>> +	writel(lower_32_bits(msi->window_base) | RZG3S_PCI_MSIRCVWADRL_ENA |
>> +	       RZG3S_PCI_MSIRCVWADRL_MSG_DATA_ENA,
>> +	       host->axi + RZG3S_PCI_MSIRCVWADRL);
>> +
>> +	/* Set MSI receive enable. */
>> +	for (u8 reg_id = 0; reg_id < RZG3S_PCI_MSI_INT_NR / RZG3S_PCI_MSI_INT_PER_REG; reg_id++)
>> +		writel(RZG3S_PCI_MSIRE_ENA, host->axi + RZG3S_PCI_MSIRE(reg_id));
>> +
>> +	/* Enable message receive interrupts. */
>> +	writel(RZG3S_PCI_MSGRCVIE_MSG_RCV, host->axi + RZG3S_PCI_MSGRCVIE);
>> +
>> +	/* Enable MSI. */
>> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PINTRCVIE, RZG3S_PCI_PINTRCVIE_MSI,
>> +			       RZG3S_PCI_PINTRCVIE_MSI);
>> +
>> +	return 0;
>> +}
>> +
>> +static int rzg3s_pcie_msi_setup(struct rzg3s_pcie_host *host)
>> +{
>> +	size_t size = RZG3S_PCI_MSI_INT_NR * sizeof(u32);
>> +	struct rzg3s_pcie_msi *msi = &host->msi;
>> +	struct device *dev = host->dev;
>> +	int id, ret;
>> +
>> +	msi->pages = __get_free_pages(GFP_KERNEL | GFP_DMA, 0);
>> +	if (!msi->pages)
>> +		return -ENOMEM;
>> +
>> +	msi->dma_addr = dma_map_single(dev, (void *)msi->pages, size * 2, DMA_BIDIRECTIONAL);
>> +	if (dma_mapping_error(dev, msi->dma_addr)) {
>> +		ret = -ENOMEM;
>> +		goto free_pages;
>> +	}
>> +
>> +	/*
>> +	 * According to the RZ/G3S HW manual (section 34.4.5.2 Setting the MSI Window)
>> +	 * the MSI window need to be within any AXI window. Find an AXI window to setup
>> +	 * the MSI window.
>> +	 */
>> +	for (id = 0; id < RZG3S_MAX_WINDOWS; id++) {
>> +		u64 base, basel, baseu;
>> +		u64 mask, maskl, masku;
>> +
>> +		basel = readl(host->axi + RZG3S_PCI_AWBASEL(id));
>> +		/* Skip checking this AXI window if it's not enabled. */
>> +		if (!(basel & RZG3S_PCI_AWBASEL_WIN_ENA))
>> +			continue;
>> +
>> +		baseu = readl(host->axi + RZG3S_PCI_AWBASEU(id));
>> +		base = baseu << 32 | basel;
>> +
>> +		maskl = readl(host->axi + RZG3S_PCI_AWMASKL(id));
>> +		masku = readl(host->axi + RZG3S_PCI_AWMASKU(id));
>> +		mask = masku << 32 | maskl;
>> +
>> +		if (msi->dma_addr < base || msi->dma_addr > base + mask)
>> +			continue;
>> +
>> +		break;
>> +	}
>> +
>> +	if (id == RZG3S_MAX_WINDOWS) {
>> +		ret = -EINVAL;
>> +		goto dma_unmap;
>> +	}
>> +
>> +	/* The MSI base address need to be aligned to the MSI size. */
>> +	msi->window_base = ALIGN(msi->dma_addr, size);
>> +	if (msi->window_base < msi->dma_addr) {
>> +		ret = -EINVAL;
>> +		goto dma_unmap;
>> +	}
>> +
>> +	rzg3s_pcie_msi_hw_setup(host);
>> +
>> +	return 0;
>> +
>> +dma_unmap:
>> +	dma_unmap_single(dev, msi->dma_addr, size * 2, DMA_BIDIRECTIONAL);
>> +free_pages:
>> +	free_pages(msi->pages, 0);
>> +	return ret;
>> +}
>> +
>> +static int rzg3s_pcie_msi_enable(struct rzg3s_pcie_host *host)
>> +{
>> +	struct rzg3s_pcie_msi *msi = &host->msi;
>> +	struct device *dev = host->dev;
>> +	const char *devname;
>> +	int irq, ret;
>> +
>> +	mutex_init(&msi->map_lock);
>> +
>> +	irq = of_irq_get_byname(dev->of_node, "intmsi_rc");
>> +	if (irq <= 0)
>> +		return dev_err_probe(dev, irq ? irq : -EINVAL, "Failed to get MSI IRQ!\n");
>> +
>> +	devname = devm_kasprintf(dev, GFP_KERNEL, "%s-msi", dev_name(dev));
>> +	if (!devname)
>> +		return -ENOMEM;
>> +
>> +	ret = rzg3s_pcie_msi_allocate_domains(msi);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = devm_request_irq(dev, irq, rzg3s_pcie_msi_irq, 0, devname, host);
>> +	if (ret) {
>> +		dev_err_probe(dev, ret, "Failed to request IRQ: %d\n", ret);
>> +		goto free_domains;
>> +	}
>> +
>> +	ret = rzg3s_pcie_msi_setup(host);
>> +	if (ret) {
>> +		dev_err_probe(dev, ret, "Failed to setup MSI!\n");
>> +		goto free_domains;
>> +	}
>> +
>> +	return 0;
>> +
>> +free_domains:
>> +	rzg3s_pcie_msi_free_domains(msi);
>> +	return ret;
>> +}
>> +
>> +static void rzg3s_pcie_msi_teardown(void *data)
>> +{
>> +	size_t size = RZG3S_PCI_MSI_INT_NR * sizeof(u32);
>> +	struct rzg3s_pcie_host *host = data;
>> +	struct rzg3s_pcie_msi *msi = &host->msi;
>> +
>> +	/* Disable MSI. */
>> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PINTRCVIE, RZG3S_PCI_PINTRCVIE_MSI, 0);
>> +
>> +	/* Disable message receive interrupts. */
>> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_MSGRCVIE, RZG3S_PCI_MSGRCVIE_MSG_RCV, 0);
>> +
>> +	/* Disable MSI receive enable. */
>> +	for (u8 reg_id = 0; reg_id < RZG3S_PCI_MSI_INT_NR / RZG3S_PCI_MSI_INT_PER_REG; reg_id++)
>> +		writel(0, host->axi + RZG3S_PCI_MSIRE(reg_id));
>> +
>> +	/* Disable MSI window. */
>> +	writel(0, host->axi + RZG3S_PCI_MSIRCVWADRL);
>> +
>> +	/* Free unused memory. */
>> +	dma_unmap_single(host->dev, msi->dma_addr, size * 2, DMA_BIDIRECTIONAL);
>> +	free_pages(msi->pages, 0);
>> +
>> +	rzg3s_pcie_msi_free_domains(msi);
>> +}
>> +
>> +static void rzg3s_pcie_intx_ack(struct irq_data *d)
>> +{
>> +	struct rzg3s_pcie_host *host = irq_data_get_irq_chip_data(d);
>> +
>> +	guard(raw_spinlock_irqsave)(&host->hw_lock);
>> +
>> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PINTRCVIS, RZG3S_PCI_PINTRCVIS_INTX(d->hwirq),
>> +			       RZG3S_PCI_PINTRCVIS_INTX(d->hwirq));
>> +}
>> +
>> +static void rzg3s_pcie_intx_mask(struct irq_data *d)
>> +{
>> +	struct rzg3s_pcie_host *host = irq_data_get_irq_chip_data(d);
>> +
>> +	guard(raw_spinlock_irqsave)(&host->hw_lock);
>> +
>> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PINTRCVIE, RZG3S_PCI_PINTRCVIE_INTX(d->hwirq),
>> +			       0);
>> +}
>> +
>> +static void rzg3s_pcie_intx_unmask(struct irq_data *d)
>> +{
>> +	struct rzg3s_pcie_host *host = irq_data_get_irq_chip_data(d);
>> +
>> +	guard(raw_spinlock_irqsave)(&host->hw_lock);
>> +
>> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PINTRCVIE, RZG3S_PCI_PINTRCVIE_INTX(d->hwirq),
>> +			       RZG3S_PCI_PINTRCVIE_INTX(d->hwirq));
>> +}
>> +
>> +static struct irq_chip rzg3s_pcie_intx_irq_chip = {
>> +	.name = "PCIe INTx",
>> +	.irq_ack = rzg3s_pcie_intx_ack,
>> +	.irq_mask = rzg3s_pcie_intx_mask,
>> +	.irq_unmask = rzg3s_pcie_intx_unmask,
>> +};
>> +
>> +static int rzg3s_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
>> +			       irq_hw_number_t hwirq)
>> +{
>> +	irq_set_chip_and_handler(irq, &rzg3s_pcie_intx_irq_chip, handle_level_irq);
>> +	irq_set_chip_data(irq, domain->host_data);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct irq_domain_ops rzg3s_pcie_intx_domain_ops = {
>> +	.map = rzg3s_pcie_intx_map,
>> +	.xlate = irq_domain_xlate_onetwocell,
>> +};
>> +
>> +static int rzg3s_pcie_intx_setup(struct rzg3s_pcie_host *host)
>> +{
>> +	struct device *dev = host->dev;
>> +	struct device_node *np = dev->of_node;
>> +	struct device_node *intc_np __free(device_node) = of_get_child_by_name(np,
>> +								"legacy-interrupt-controller");
>> +	int irq_count;
>> +
>> +	if (!intc_np) {
>> +		dev_warn(dev, "legacy-interrupt-controller node is absent\n");
> 
> You don't have to make a child interrupt-controller node any more. You 
> can have both 'interrupt-controller' and 'interrupt-map' in the host 
> bridge node.

By any chance, do you have in mind an example driver doing it like this?

> 
>> +		return 0;
>> +	}
>> +
>> +	irq_count = of_irq_count(intc_np);
>> +	if (irq_count != PCI_NUM_INTX)
>> +		return dev_err_probe(dev, -EINVAL,
>> +				     "Invalid IRQ entries in legacy-interrupt-controller\n");
> 
> Don't use the kernel to validate your DT. You don't use the count for 
> anything.
> 
>> +
>> +	for (int i = 0; i < irq_count; i++) {
> 
> s/irq_count/PCI_NUM_INTX/
> 
>> +		int irq = irq_of_parse_and_map(intc_np, i);
> 
> Without the legacy node, you can use platform_get_irq() instead.

OK

> 
>> +
>> +		if (!irq)
>> +			return dev_err_probe(dev, -EINVAL, "Failed to parse and map INTx IRQ\n");
>> +
>> +		host->intx_irqs[i] = irq;
>> +		irq_set_chained_handler_and_data(irq, rzg3s_pcie_intx_irq_handler, host);
>> +	}
>> +
>> +	host->intx_domain = irq_domain_add_linear(intc_np, irq_count,
>> +						  &rzg3s_pcie_intx_domain_ops, host);
>> +	if (!host->intx_domain)
>> +		return dev_err_probe(dev, -EINVAL, "Failed to add irq domain for INTX irqs\n");
>> +	irq_domain_update_bus_token(host->intx_domain, DOMAIN_BUS_WIRED);
>> +
>> +	return 0;
>> +}
>> +
>> +static void rzg3s_pcie_intx_teardown(void *data)
>> +{
>> +	struct rzg3s_pcie_host *host = data;
>> +
>> +	irq_domain_remove(host->intx_domain);
>> +}
>> +
>> +static int rzg3s_pcie_set_max_link_speed(struct rzg3s_pcie_host *host)
>> +{
>> +	u32 lcs, cs2, link_speed, remote_supported_link_speeds, tmp;
>> +	u8 ltssm_state_l0 = 0xc;
>> +	int ret;
>> +
>> +	/*
>> +	 * According to the RZ/G3S HW manual (section 34.6.3 Caution when Changing
>> +	 * the Speed Spontaneously) link speed change can be done only when the
>> +	 * link training and status state machine in the PCIe Core Link is L0.
>> +	 */
>> +	ret = readl_poll_timeout(host->axi + RZG3S_PCI_PCSTAT1, tmp,
>> +				 FIELD_GET(RZG3S_PCI_PCSTAT1_LTSSM_STATE, tmp) == ltssm_state_l0,
>> +				 100, 1000);
>> +	if (ret) {
>> +		dev_dbg(host->dev, "Could not set max link speed! LTSSM not in L0, state=%lx\n",
>> +			FIELD_GET(RZG3S_PCI_PCSTAT1_LTSSM_STATE, tmp));
>> +		return ret;
>> +	}
>> +
>> +	lcs = readl(host->pcie + RZG3S_PCI_CFG_LINKCS);
>> +	cs2 = readl(host->axi + RZG3S_PCI_PCSTAT2);
>> +
>> +	link_speed = FIELD_GET(RZG3S_PCI_CFG_LINKCS_CUR_LS, lcs);
>> +	remote_supported_link_speeds = FIELD_GET(RZG3S_PCI_PCSTAT2_SDRIRE, cs2);
>> +
>> +	/* Return if link is @ 5.0 GT/s or the connected device doesn't support it. */
>> +	if (link_speed == RZG3S_PCIE_LINK_SPEED_5_0_GTS ||
>> +	    !(remote_supported_link_speeds & BIT(RZG3S_PCIE_LINK_SPEED_5_0_GTS)))
>> +		return 0;
>> +
>> +	/* Set target Link speed to 5.0 GT/s. */
>> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_CFG_LINCS2, RZG3S_PCI_CFG_LINCS2_TARGET_LS,
>> +			       FIELD_PREP(RZG3S_PCI_CFG_LINCS2_TARGET_LS,
>> +					  RZG3S_PCIE_LINK_SPEED_5_0_GTS));
>> +
>> +	/* Request link speed change. */
>> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PCCTRL2,
>> +			       RZG3S_PCI_PCCTRL2_LS_CHG_REQ | RZG3S_PCI_PCCTRL2_LS_CHG,
>> +			       RZG3S_PCI_PCCTRL2_LS_CHG_REQ |
>> +			       FIELD_PREP(RZG3S_PCI_PCCTRL2_LS_CHG,
>> +					  RZG3S_PCIE_LINK_SPEED_5_0_GTS - 1));
>> +
>> +	ret = readl_poll_timeout(host->axi + RZG3S_PCI_PCSTAT2, cs2,
>> +				 (cs2 & RZG3S_PCI_PCSTAT2_LS_CHG_DONE), 100, 1000);
>> +
>> +	/*
>> +	 * According to the RZ/G3S HW manual (section 34.6.3 Caution when Changing
>> +	 * the Speed Spontaneously) the PCI_PCCTRL2_LS_CHG_REQ should be
>> +	 * de-asserted after checking for PCI_PCSTAT2_LS_CHG_DONE.
>> +	 */
>> +	rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PCCTRL2, RZG3S_PCI_PCCTRL2_LS_CHG_REQ, 0);
>> +
>> +	return ret;
>> +}
>> +
>> +static void rzg3s_pcie_cfg_init(struct rzg3s_pcie_host *host)
>> +{
>> +	/* Enable access control to the CFGU. */
>> +	writel(RZG3S_PCI_PERM_CFG_HWINIT_EN, host->axi + RZG3S_PCI_PERM);
>> +
>> +	/* Update vendor ID and device ID. */
>> +	writel(FIELD_PREP(RZG3S_PCI_CFG_VID_VENDOR, host->vendor_id) |
>> +	       FIELD_PREP(RZG3S_PCI_CFG_VID_DEVICE, host->device_id),
>> +	       host->pcie + RZG3S_PCI_CFG_VID);
>> +
>> +	/* HW manual recommends to write 0xffffffff on initialization. */
>> +	writel(0xffffffff, host->pcie + RZG3S_PCI_CFG_BARMSK00L);
>> +	writel(0xffffffff, host->pcie + RZG3S_PCI_CFG_BARMSK00U);
>> +
>> +	/* Primary bus = 0, secondary bus = 1, subordinate bus = 1. */
>> +	writel(FIELD_PREP(RZG3S_PCI_CFG_BNR_SUBORDINATE_BUS, 1) |
>> +	       FIELD_PREP(RZG3S_PCI_CFG_BNR_SECONDARY_BUS, 1),
>> +	       host->pcie + RZG3S_PCI_CFG_BNR);
>> +
>> +	/* Disable access control to the CFGU. */
>> +	writel(0, host->axi + RZG3S_PCI_PERM);
>> +}
>> +
>> +static void rzg3s_pcie_irqs_init(struct rzg3s_pcie_host *host)
>> +{
>> +	/*
>> +	 * According to the HW manual of the RZ/G3S (sections corresponding
>> +	 * to all registers written with ~0U), the hardware ignores value
>> +	 * written to unused bits. Writing ~0U to these registers should be
>> +	 * safe.
>> +	 */
>> +
>> +	/* Clear the link state and PM transitions. */
>> +	writel(RZG3S_PCI_PEIS0_DL_UPDOWN | RZG3S_PCI_PEIS0_RX_DLLP_PM_ENTER,
>> +	       host->axi + RZG3S_PCI_PEIS0);
>> +
>> +	/* Disable all interrupts. */
>> +	writel(0, host->axi + RZG3S_PCI_PEIE0);
>> +
>> +	/* Clear all parity and ecc error interrupts. */
>> +	writel(~0U, host->axi + RZG3S_PCI_PEIS1);
>> +
>> +	/* Disable all parity and ecc error interrupts. */
>> +	writel(0, host->axi + RZG3S_PCI_PEIE1);
>> +
>> +	/* Clear all AXI master error interrupts. */
>> +	writel(~0U, host->axi + RZG3S_PCI_AMEIS);
>> +
>> +	/* Clear all AXI slave error interrupts. */
>> +	writel(~0U, host->axi + RZG3S_PCI_ASEIS1);
>> +
>> +	/* Clear all message receive interrupts. */
>> +	writel(~0U, host->axi + RZG3S_PCI_MSGRCVIS);
>> +}
>> +
>> +static int rzg3s_pcie_resets_bulk_set(int (*action)(int num, struct reset_control_bulk_data *rstcs),
>> +				      struct reset_control **resets, u8 num_resets)
>> +{
>> +	struct reset_control_bulk_data *data __free(kfree) =
>> +		kcalloc(num_resets, sizeof(*data), GFP_KERNEL);
>> +
>> +	if (!data)
>> +		return -ENOMEM;
>> +
>> +	for (u8 i = 0; i < num_resets; i++)
>> +		data[i].rstc = resets[i];
>> +
>> +	return action(num_resets, data);
>> +}
>> +
>> +static int
>> +rzg3s_pcie_resets_init(struct device *dev, struct reset_control ***resets,
>> +		       struct reset_control *(*action)(struct device *dev, const char *id),
>> +		       const char * const *reset_names, u8 num_resets)
>> +{
>> +	*resets = devm_kcalloc(dev, num_resets, sizeof(struct reset_control *), GFP_KERNEL);
>> +	if (!*resets)
>> +		return -ENOMEM;
>> +
>> +	for (u8 i = 0; i < num_resets; i++) {
>> +		(*resets)[i] = action(dev, reset_names[i]);
>> +		if (IS_ERR((*resets)[i]))
>> +			return PTR_ERR((*resets)[i]);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int rzg3s_pcie_resets_prepare(struct rzg3s_pcie_host *host)
>> +{
>> +	const char * const cfg_resets[] = { "rst_b", "rst_ps_b", "rst_gp_b", "rst_rsm_b", };
>> +	const char * const power_resets[] = { "aresetn", "rst_cfg_b", "rst_load_b", };
>> +	int ret;
>> +
>> +	/*
>> +	 * According to the RZ/G3S HW manual (section 34.5.1.2 De-asserting the Reset)
>> +	 * the PCIe IP needs to wait 5ms from power on to the de-assertion of reset.
>> +	 */
>> +	usleep_range(5000, 5100);
>> +	host->num_power_resets = ARRAY_SIZE(power_resets);
>> +	ret = rzg3s_pcie_resets_init(host->dev, &host->power_resets,
>> +				     devm_reset_control_get_exclusive_deasserted,
>> +				     power_resets, ARRAY_SIZE(power_resets));
>> +	if (ret)
>> +		return ret;
>> +
>> +	host->num_cfg_resets = ARRAY_SIZE(cfg_resets);
>> +	return rzg3s_pcie_resets_init(host->dev, &host->cfg_resets,
>> +				      devm_reset_control_get_exclusive,
>> +				      cfg_resets, ARRAY_SIZE(cfg_resets));
>> +}
>> +
>> +static void rzg3s_pcie_cfg_resets_action(void *data)
>> +{
>> +	struct rzg3s_pcie_host *host = data;
>> +
>> +	rzg3s_pcie_resets_bulk_set(reset_control_bulk_assert, host->cfg_resets,
>> +				   host->num_cfg_resets);
>> +}
>> +
>> +static int rzg3s_pcie_hw_init(struct rzg3s_pcie_host *host, bool probe)
>> +{
>> +	u32 val;
>> +	int ret;
>> +
>> +	/* Set the PCIe related registers. */
>> +	rzg3s_pcie_cfg_init(host);
>> +
>> +	/* Set the interrupts. */
>> +	rzg3s_pcie_irqs_init(host);
>> +
>> +	ret = rzg3s_pcie_resets_bulk_set(reset_control_bulk_deassert, host->cfg_resets,
>> +					 host->num_cfg_resets);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Wait for link up. */
>> +	ret = readl_poll_timeout(host->axi + RZG3S_PCI_PCSTAT1, val,
>> +				 !(val & RZG3S_PCI_PCSTAT1_DL_DOWN_STS), 5000, 500000);
>> +	if (ret) {
>> +		rzg3s_pcie_resets_bulk_set(reset_control_bulk_assert, host->cfg_resets,
>> +					   host->num_cfg_resets);
>> +		return ret;
>> +	}
>> +
>> +	val = readl(host->axi + RZG3S_PCI_PCSTAT2);
>> +	dev_info(host->dev, "PCIe link status [0x%x]\n", val);
>> +
>> +	val = FIELD_GET(RZG3S_PCI_PCSTAT2_STATE_RX_DETECT, val);
>> +	dev_info(host->dev, "PCIe x%d: link up\n", hweight32(val));
>> +
>> +	if (probe)
>> +		ret = devm_add_action_or_reset(host->dev, rzg3s_pcie_cfg_resets_action, host);
>> +
>> +	return ret;
>> +}
>> +
>> +static void rzg3s_pcie_set_inbound_window(struct rzg3s_pcie_host *host, u64 cpu_addr,
>> +					  u64 pci_addr, u64 size, int id)
>> +{
>> +	/* Set CPU window base address. */
>> +	writel(upper_32_bits(cpu_addr), host->axi + RZG3S_PCI_ADESTU(id));
>> +	writel(lower_32_bits(cpu_addr), host->axi + RZG3S_PCI_ADESTL(id));
>> +
>> +	/* Set window size. */
>> +	writel(upper_32_bits(size), host->axi + RZG3S_PCI_AWMASKU(id));
>> +	writel(lower_32_bits(size), host->axi + RZG3S_PCI_AWMASKL(id));
>> +
>> +	/* Set PCIe window base address and enable the window.. */
>> +	writel(upper_32_bits(pci_addr), host->axi + RZG3S_PCI_AWBASEU(id));
>> +	writel(lower_32_bits(pci_addr) | RZG3S_PCI_AWBASEL_WIN_ENA,
>> +	       host->axi + RZG3S_PCI_AWBASEL(id));
>> +}
>> +
>> +static int rzg3s_pcie_set_inbound_windows(struct rzg3s_pcie_host *host,
>> +					  struct resource_entry *entry, int *index)
>> +{
>> +	u64 pci_addr = entry->res->start - entry->offset;
>> +	u64 cpu_addr = entry->res->start;
>> +	u64 cpu_end = entry->res->end;
>> +	u64 size_id = 0;
>> +	int id = *index;
>> +	u64 size;
>> +
>> +	while (cpu_addr < cpu_end) {
>> +		if (id >= RZG3S_MAX_WINDOWS)
>> +			return dev_err_probe(host->dev, -EINVAL,
>> +					     "Failed to set inbound windows!\n");
>> +
>> +		size = resource_size(entry->res) - size_id;
>> +
>> +		/*
>> +		 * According to the RZ/G3S HW manual (section 34.3.1.71 AXI Window Mask
>> +		 * (Lower) Registers) the min size is 4K.
>> +		 */
>> +		size = max(size, 4096);
>> +
>> +		/*
>> +		 * According the RZ/G3S HW manual (sections:
>> +		 * - 34.3.1.69 AXI Window Base (Lower) Registers
>> +		 * - 34.3.1.71 AXI Window Mask (Lower) Registers
>> +		 * - 34.3.1.73 AXI Destination (Lower) Registers)
>> +		 * the CPU addr, PCIe addr, size should be 4K alined and be a power of 2.
>> +		 */
>> +		size = ALIGN(size, 4096);
>> +
>> +		/*
>> +		 * According to the RZ/G3S HW manual (section 34.3.1.71 AXI Window Mask
>> +		 * (Lower) Registers) HW expects first 12 LSB bits to be 0xfff.
>> +		 * Extract 1 from size for this.
>> +		 */
>> +		size = roundup_pow_of_two(size) - 1;
>> +
>> +		cpu_addr = ALIGN(cpu_addr, 4096);
>> +		pci_addr = ALIGN(pci_addr, 4096);
>> +
>> +		rzg3s_pcie_set_inbound_window(host, cpu_addr, pci_addr, size, id);
>> +
>> +		pci_addr += size;
>> +		cpu_addr += size;
>> +		size_id = size;
>> +		id++;
>> +	}
>> +	*index = id;
>> +
>> +	return 0;
>> +}
>> +
>> +static int rzg3s_pcie_parse_map_dma_ranges(struct rzg3s_pcie_host *host)
>> +{
>> +	struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host);
>> +	struct resource_entry *entry;
>> +	int i = 0, ret;
>> +
>> +	resource_list_for_each_entry(entry, &bridge->dma_ranges) {
>> +		ret = rzg3s_pcie_set_inbound_windows(host, entry, &i);
>> +		if (ret)
>> +			return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static void rzg3s_pcie_set_outbound_window(struct rzg3s_pcie_host *host, struct resource_entry *win,
>> +					   int id)
>> +{
>> +	struct resource *res = win->res;
>> +	resource_size_t size = resource_size(res);
>> +	resource_size_t res_start;
>> +
>> +	if (res->flags & IORESOURCE_IO)
>> +		res_start = pci_pio_to_address(res->start) - win->offset;
>> +	else
>> +		res_start = res->start - win->offset;
>> +
>> +	/*
>> +	 * According to the RZ/G3S HW manual (section 34.3.1.75 PCIe Window Base
>> +	 * (Lower) Registers) the window base address need to be 4K aligned.
>> +	 */
>> +	res_start = ALIGN(res_start, 4096);
>> +
>> +	size = ALIGN(size, 4096);
>> +	size = roundup_pow_of_two(size) - 1;
>> +
>> +	/* Set PCIe destination. */
>> +	writel(upper_32_bits(res_start), host->axi + RZG3S_PCI_PDESTU(id));
>> +	writel(lower_32_bits(res_start), host->axi + RZG3S_PCI_PDESTL(id));
>> +
>> +	/* Set PCIe window mask. */
>> +	writel(upper_32_bits(size), host->axi + RZG3S_PCI_PWMASKU(id));
>> +	writel(lower_32_bits(size), host->axi + RZG3S_PCI_PWMASKL(id));
>> +
>> +	/* Set PCIe window base and enable the window. */
>> +	writel(upper_32_bits(res_start), host->axi + RZG3S_PCI_PWBASEU(id));
>> +	writel(lower_32_bits(res_start) | RZG3S_PCI_PWBASEL_ENA,
>> +	       host->axi + RZG3S_PCI_PWBASEL(id));
>> +}
>> +
>> +static int rzg3s_pcie_parse_map_ranges(struct rzg3s_pcie_host *host)
>> +{
>> +	struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host);
>> +	struct resource_entry *win;
>> +	int i = 0;
>> +
>> +	resource_list_for_each_entry(win, &bridge->windows) {
>> +		struct resource *res = win->res;
>> +
>> +		if (i >= RZG3S_MAX_WINDOWS)
>> +			return dev_err_probe(host->dev, -EINVAL,
>> +					     "Failed to set outbound windows!\n");
>> +
>> +		if (!res->flags)
>> +			continue;
>> +
>> +		switch (resource_type(res)) {
>> +		case IORESOURCE_IO:
>> +		case IORESOURCE_MEM:
>> +			rzg3s_pcie_set_outbound_window(host, win, i);
>> +			i++;
>> +			break;
>> +		}
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int rzg3s_pcie_phy_init(struct rzg3s_pcie_host *host)
>> +{
>> +	static const u32 xcfgd_settings[RZG3S_PCI_PHY_XCFGD_NUM] = {
>> +		[8]  = 0xe0006801, 0x007f7e30, 0x183e0000, 0x978ff500,
>> +		       0xec000000, 0x009f1400, 0x0000d009,
>> +		[17] = 0x78000000,
>> +		[19] = 0x00880000, 0x000005c0, 0x07000000, 0x00780920,
>> +		       0xc9400ce2, 0x90000c0c, 0x000c1414, 0x00005034,
>> +		       0x00006000, 0x00000001,
>> +	};
>> +	static const u32 xcfga_cmn_settings[RZG3S_PCI_PHY_XCFGA_CMN_NUM] = {
>> +		0x00000d10, 0x08310100, 0x00c21404, 0x013c0010, 0x01874440,
>> +		0x1a216082, 0x00103440, 0x00000080, 0x00000010, 0x0c1000c1,
>> +		0x1000c100, 0x0222000c, 0x00640019, 0x00a00028, 0x01d11228,
>> +		0x0201001d,
>> +	};
>> +	static const u32 xcfga_rx_settings[RZG3S_PCI_PHY_XCFGA_RX_NUM] = {
>> +		0x07d55000, 0x030e3f00, 0x00000288, 0x102c5880, 0x0000000b,
>> +		0x04141441, 0x00641641, 0x00d63d63, 0x00641641, 0x01970377,
>> +		0x00190287, 0x00190028, 0x00000028,
>> +	};
>> +
>> +	writel(RZG3S_PCI_PERM_PIPE_PHY_REG_EN, host->axi + RZG3S_PCI_PERM);
>> +
>> +	for (u8 i = 0; i < RZG3S_PCI_PHY_XCFGD_NUM; i++)
>> +		writel(xcfgd_settings[i], host->axi + RZG3S_PCI_PHY_XCFGD(i));
>> +
>> +	for (u8 i = 0; i < RZG3S_PCI_PHY_XCFGA_CMN_NUM; i++)
>> +		writel(xcfga_cmn_settings[i], host->axi + RZG3S_PCI_PHY_XCFGA_CMN(i));
>> +
>> +	for (u8 i = 0; i < RZG3S_PCI_PHY_XCFGA_RX_NUM; i++)
>> +		writel(xcfga_rx_settings[i], host->axi + RZG3S_PCI_PHY_XCFGA_RX(i));
>> +
>> +	writel(0x107, host->axi + RZG3S_PCI_PHY_XCFGA_TX);
>> +
>> +	/* Select PHY settings values. */
>> +	writel(RZG3S_PCI_PHY_XCFG_CTRL_PHYREG_SEL, host->axi + RZG3S_PCI_PHY_XCFG_CTRL);
>> +
>> +	writel(0, host->axi + RZG3S_PCI_PERM);
>> +
>> +	return 0;
>> +}
>> +
>> +static void rzg3s_pcie_pm_runtime_put(void *data)
>> +{
>> +	pm_runtime_put_sync(data);
> 
> Seems like a pointless wrapper.

Ah, indeed.

> 
>> +}
>> +
>> +static void rzg3s_pcie_sysc_signal_action(void *data)
>> +{
>> +	struct regmap *sysc = data;
>> +
>> +	/*
>> +	 * SYSC RST_RSM_B signal need to be asserted before turning off the power to
>> +	 * the PHY.
>> +	 */
>> +	regmap_update_bits(sysc, RZG3S_SYS_PCIE_RST_RSM_B, RZG3S_SYS_PCIE_RST_RSM_B_MASK,
>> +			   FIELD_PREP(RZG3S_SYS_PCIE_RST_RSM_B_MASK, 0));
>> +}
>> +
>> +static int rzg3s_pcie_setup(struct rzg3s_pcie_host *host,
>> +			    int (*intx_setup)(struct rzg3s_pcie_host *host),
>> +			    int (*msi_setup)(struct rzg3s_pcie_host *host),
>> +			    bool probe)
>> +{
>> +	struct device *dev = host->dev;
>> +	int ret;
>> +
>> +	/* Set inbound windows. */
>> +	ret = rzg3s_pcie_parse_map_dma_ranges(host);
>> +	if (ret)
>> +		return dev_err_probe(dev, ret, "Failed to set inbound windows!\n");
>> +
>> +	/* Set outbound windows. */
>> +	ret = rzg3s_pcie_parse_map_ranges(host);
>> +	if (ret)
>> +		return dev_err_probe(dev, ret, "Failed to set outbound windows!\n");
>> +
>> +	/* Set the PHY, if any. */
>> +	if (host->phy_init) {
>> +		ret = host->phy_init(host);
>> +		if (ret)
>> +			return dev_err_probe(dev, ret, "Failed to set the PHY!\n");
>> +	}
>> +
>> +	if (intx_setup) {
>> +		ret = intx_setup(host);
>> +		if (ret)
>> +			return dev_err_probe(dev, ret, "Failed to setup INTx\n");
>> +
>> +		if (probe) {
>> +			ret = devm_add_action_or_reset(dev, rzg3s_pcie_intx_teardown, host);
>> +			if (ret)
>> +				return dev_err_probe(dev, ret, "Failed to add INTx action\n");
>> +		}
>> +	}
>> +
>> +	/* Set the MSIs. */
>> +	if (IS_ENABLED(CONFIG_PCI_MSI)) {
>> +		ret = msi_setup(host);
>> +		if (ret)
>> +			return dev_err_probe(dev, ret, "Failed to setup MSIs\n");
>> +
>> +		if (probe) {
>> +			ret = devm_add_action_or_reset(dev, rzg3s_pcie_msi_teardown, host);
>> +			if (ret)
>> +				return dev_err_probe(dev, ret, "Failed to add MSI action\n");
>> +		}
>> +	}
>> +
>> +	/* Initialize the HW. */
>> +	ret = rzg3s_pcie_hw_init(host, probe);
>> +	if (ret)
>> +		return dev_err_probe(dev, ret, "Failed to initialize the HW!\n");
>> +
>> +	/* Try to set maximum supported link speed (5.0 GT/s). */
>> +	ret = rzg3s_pcie_set_max_link_speed(host);
>> +	if (ret)
>> +		dev_info(dev, "Failed to set link speed to 5.0 GT/s\n");
>> +
>> +	return 0;
>> +}
>> +
>> +static int rzg3s_pcie_probe_helper(struct platform_device *pdev, void *devres_group_id)
>> +{
>> +	struct pci_host_bridge *bridge;
>> +	struct device *dev = &pdev->dev;
>> +	struct device_node *np = dev->of_node;
>> +	struct device_node *sysc_np __free(device_node) =
>> +		of_parse_phandle(np, "renesas,sysc", 0);
>> +	struct rzg3s_pcie_host *host;
>> +	int ret;
>> +
>> +	bridge = devm_pci_alloc_host_bridge(dev, sizeof(*host));
>> +	if (!bridge)
>> +		return -ENOMEM;
>> +
>> +	host = pci_host_bridge_priv(bridge);
>> +	host->dev = dev;
>> +	host->phy_init = device_get_match_data(dev);
>> +	host->devres_group_id = devres_group_id;
>> +	platform_set_drvdata(pdev, host);
>> +
>> +	host->axi = devm_platform_ioremap_resource(pdev, 0);
>> +	if (IS_ERR(host->axi))
>> +		return PTR_ERR(host->axi);
>> +	host->pcie = host->axi + RZG3S_PCI_CFG_BASE;
>> +
>> +	ret = of_property_read_u32(np, "vendor-id", &host->vendor_id);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = of_property_read_u32(np, "device-id", &host->device_id);
>> +	if (ret)
>> +		return ret;
>> +
>> +	host->sysc = syscon_node_to_regmap(sysc_np);
>> +	if (IS_ERR(host->sysc))
>> +		return PTR_ERR(host->sysc);
>> +
>> +	ret = regmap_update_bits(host->sysc, RZG3S_SYS_PCIE_RST_RSM_B,
>> +				 RZG3S_SYS_PCIE_RST_RSM_B_MASK,
>> +				 FIELD_PREP(RZG3S_SYS_PCIE_RST_RSM_B_MASK, 1));
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = devm_add_action_or_reset(dev, rzg3s_pcie_sysc_signal_action, host->sysc);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = rzg3s_pcie_resets_prepare(host);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = devm_pm_runtime_enable(dev);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = pm_runtime_resume_and_get(dev);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = devm_add_action_or_reset(dev, rzg3s_pcie_pm_runtime_put, dev);
>> +	if (ret)
>> +		return ret;
>> +
>> +	raw_spin_lock_init(&host->hw_lock);
>> +
>> +	ret = rzg3s_pcie_setup(host, rzg3s_pcie_intx_setup, rzg3s_pcie_msi_enable, true);
>> +	if (ret)
>> +		return ret;
>> +
>> +	bridge->sysdata = host;
>> +	bridge->ops = &rzg3s_pcie_ops;
>> +	return pci_host_probe(bridge);
>> +}
>> +
>> +static int rzg3s_pcie_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	void *devres_group_id;
>> +	int ret;
>> +
>> +	devres_group_id = devres_open_group(dev, NULL, GFP_KERNEL);
> 
> I don't think you need this...

I explain it's necessity here:
https://lore.kernel.org/all/20250512202550.GA1126561@bhelgaas/

Please have a look there

> 
>> +	if (!devres_group_id)
>> +		return -ENOMEM;
>> +
>> +	ret = rzg3s_pcie_probe_helper(pdev, devres_group_id);
>> +	if (ret)
>> +		devres_release_group(dev, devres_group_id);
>> +
>> +	return ret;
>> +}
>> +
>> +static void rzg3s_pcie_remove(struct platform_device *pdev)
>> +{
>> +	struct rzg3s_pcie_host *host = dev_get_drvdata(&pdev->dev);
>> +	struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host);
>> +
>> +	pci_lock_rescan_remove();
>> +	pci_stop_root_bus(bridge->bus);
>> +	pci_remove_root_bus(bridge->bus);
>> +	pci_unlock_rescan_remove();
> 
> If you put all this into the devm_add_action_or_reset() callback, then 
> you can get rid of remove().

That will lead to problems when doing unbind/bind due to the issue
described in the above link:
https://lore.kernel.org/all/20250512202550.GA1126561@bhelgaas/

Thank you for your review,
Claudiu



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

* Re: [PATCH 4/8] dt-bindings: PCI: renesas,r9a08g045s33-pcie: Add documentation for the PCIe IP on Renesas RZ/G3S
  2025-05-09 21:08   ` Rob Herring
@ 2025-05-14 11:41     ` Claudiu Beznea
  0 siblings, 0 replies; 28+ messages in thread
From: Claudiu Beznea @ 2025-05-14 11:41 UTC (permalink / raw)
  To: Rob Herring
  Cc: bhelgaas, lpieralisi, kw, manivannan.sadhasivam, krzk+dt,
	conor+dt, geert+renesas, magnus.damm, mturquette, sboyd,
	saravanak, p.zabel, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, linux-arm-kernel, linux-clk, Claudiu Beznea

Hi, Rob,

On 10.05.2025 00:08, Rob Herring wrote:
> On Wed, Apr 30, 2025 at 01:32:32PM +0300, Claudiu wrote:
>> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>>
>> The PCIe IP available on the Renesas RZ/G3S complies with the PCI Express
>> Base Specification 4.0. It is designed for root complex applications and
>> features a single-lane (x1) implementation. Add documentation for it.
>> The interrupts, interrupt-names, resets, reset-names, clocks, clock-names
>> description were obtained from the hardware manual.
>>
>> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>> ---
>>  .../pci/renesas,r9a08g045s33-pcie.yaml        | 242 ++++++++++++++++++
>>  1 file changed, 242 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/pci/renesas,r9a08g045s33-pcie.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/pci/renesas,r9a08g045s33-pcie.yaml b/Documentation/devicetree/bindings/pci/renesas,r9a08g045s33-pcie.yaml
>> new file mode 100644
>> index 000000000000..354f9c3be139
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/pci/renesas,r9a08g045s33-pcie.yaml
>> @@ -0,0 +1,242 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/pci/renesas,r9a08g045s33-pcie.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Renesas RZ/G3S PCIe host controller
>> +
>> +maintainers:
>> +  - Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>> +
>> +description:
>> +  Renesas RZ/G3S PCIe host controller complies with PCIe Base Specification
>> +  4.0 and supports up to 5 GT/s (Gen2).
>> +
>> +properties:
>> +  compatible:
>> +    const: renesas,r9a08g045s33-pcie # RZ/G3S
>> +
>> +  reg:
>> +    maxItems: 1
>> +
>> +  interrupts:
>> +    items:
>> +      - description: System error interrupt
>> +      - description: System error on correctable error interrupt
>> +      - description: System error on non-fatal error interrupt
>> +      - description: System error on fatal error interrupt
>> +      - description: AXI error interrupt
>> +      - description: INTA interrupt
>> +      - description: INTB interrupt
>> +      - description: INTC interrupt
>> +      - description: INTD interrupt
>> +      - description: MSI interrupt
>> +      - description: Link bandwidth interrupt
>> +      - description: PME interrupt
>> +      - description: DMA interrupt
>> +      - description: PCIe event interrupt
>> +      - description: Message interrupt
>> +      - description: All interrupts
>> +
>> +  interrupt-names:
>> +    items:
>> +      - description: int_serr
>> +      - description: int_ser_cor
>> +      - description: int_serr_nonfatal
>> +      - description: int_serr_fatal
>> +      - description: axi_err_int
>> +      - description: inta_rc
>> +      - description: intb_rc
>> +      - description: intc_rc
>> +      - description: intd_rc
>> +      - description: intmsi_rc
> 
> Isn't every interrupt for the root complex?

It is! I just used the names that were available in the hardware manual.
I'll drop the "_rc" it in the next version.

> 
>> +      - description: int_link_bandwidth
>> +      - description: int_pm_pme
>> +      - description: dma_int
>> +      - description: pcie_evt_int
>> +      - description: msg_int
>> +      - description: int_all
> 
> 'int_' or '_int' is redundant (and inconsistent). Drop.

OK

> 
>> +
>> +  clocks:
>> +    items:
>> +      - description: System clock
>> +      - description: PM control clock
>> +
>> +  clock-names:
>> +    items:
>> +      - description: aclk
>> +      - description: clkl1pm
> 
> 'l1pm' or 'pm'

OK

> 
>> +
>> +  resets:
>> +    items:
>> +      - description: AXI2PCIe Bridge reset
>> +      - description: Data link layer/transaction layer reset
>> +      - description: Transaction layer (ACLK domain) reset
>> +      - description: Transaction layer (PCLK domain) reset
>> +      - description: Physical layer reset
>> +      - description: Configuration register reset
>> +      - description: Configuration register reset
>> +
>> +  reset-names:
>> +    items:
>> +      - description: aresetn
>> +      - description: rst_b
>> +      - description: rst_gp_b
>> +      - description: rst_ps_b
>> +      - description: rst_rsm_b
>> +      - description: rst_cfg_b
>> +      - description: rst_load_b
>> +
>> +  power-domains:
>> +    maxItems: 1
>> +
>> +  dma-ranges:
>> +    description:
>> +      A single range for the inbound memory region.
>> +    maxItems: 1
>> +
>> +  renesas,sysc:
>> +    description: System controller phandle
>> +    $ref: /schemas/types.yaml#/definitions/phandle
>> +
>> +  vendor-id:
>> +    const: 0x1912
>> +
>> +  device-id:
>> +    const: 0x0033
>> +
>> +  legacy-interrupt-controller:
>> +    description: Interrupt controller node for handling legacy PCI interrupts
>> +    type: object
>> +
>> +    properties:
>> +      "#address-cells":
>> +        const: 0
>> +
>> +      "#interrupt-cells":
>> +        const: 1
>> +
>> +      interrupt-controller: true
>> +
>> +      interrupts:
>> +        items:
>> +          - description: INTA interrupt
>> +          - description: INTB interrupt
>> +          - description: INTC interrupt
>> +          - description: INTD interrupt
>> +
>> +    required:
>> +      - "#address-cells"
>> +      - "#interrupt-cells"
>> +      - interrupt-controller
>> +      - interrupts
>> +
>> +    additionalProperties: false
>> +
>> +required:
>> +  - compatible
>> +  - reg
>> +  - clocks
>> +  - resets
>> +  - reset-names
>> +  - interrupts
>> +  - interrupt-names
>> +  - interrupt-map
>> +  - interrupt-map-mask
>> +  - power-domains
>> +  - "#address-cells"
>> +  - "#size-cells"
>> +  - "#interrupt-cells"
>> +  - renesas,sysc
>> +  - vendor-id
>> +  - device-id
>> +
>> +allOf:
>> +  - $ref: /schemas/pci/pci-host-bridge.yaml#
>> +
>> +unevaluatedProperties: false
>> +
>> +examples:
>> +  - |
>> +    #include <dt-bindings/clock/r9a08g045-cpg.h>
>> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
>> +
>> +    bus {
>> +        #address-cells = <2>;
>> +        #size-cells = <2>;
>> +
>> +        gic: interrupt-controller {
>> +            interrupt-controller;
>> +            #interrupt-cells = <3>;
>> +        };
> 
> Drop. Don't need to show provider nodes for the example.

This was needed for the schema to compile with the intx node.

> 
>> +
>> +        pcie@11e40000 {
>> +            compatible = "renesas,r9a08g045s33-pcie";
>> +            reg = <0 0x11e40000 0 0x10000>;
>> +            ranges = <0x03000000 0 0x30000000 0 0x30000000 0 0x8000000>;
>> +            dma-ranges = <0x42000000 0 0x48000000 0 0x48000000 0 0x8000000>;
>> +            bus-range = <0x0 0xff>;
>> +            clocks = <&cpg CPG_MOD R9A08G045_PCI_ACLK>,
>> +                     <&cpg CPG_MOD R9A08G045_PCI_CLKL1PM>;
>> +            clock-names = "aclk", "clkl1pm";
>> +            resets = <&cpg R9A08G045_PCI_ARESETN>,
>> +                     <&cpg R9A08G045_PCI_RST_B>,
>> +                     <&cpg R9A08G045_PCI_RST_GP_B>,
>> +                     <&cpg R9A08G045_PCI_RST_PS_B>,
>> +                     <&cpg R9A08G045_PCI_RST_RSM_B>,
>> +                     <&cpg R9A08G045_PCI_RST_CFG_B>,
>> +                     <&cpg R9A08G045_PCI_RST_LOAD_B>;
>> +            reset-names = "aresetn", "rst_b", "rst_gp_b", "rst_ps_b",
>> +                          "rst_rsm_b", "rst_cfg_b", "rst_load_b";
>> +            interrupts = <GIC_SPI 395 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 396 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 397 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 398 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 399 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 400 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 401 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 402 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 403 IRQ_TYPE_LEVEL_HIGH>,
> 
> It is very odd that you have the INTx interrupts here and below.
> 
> As I mentioned in the driver, you don't need the legacy node any more. 
> Just add 'interrupt-controller' to this node and point interrupt-map to 
> this node.
Will try it like this.

Thank you for your review,
Claudiu

> 
>> +                         <GIC_SPI 404 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 405 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 406 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 407 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 408 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 409 IRQ_TYPE_LEVEL_HIGH>,
>> +                         <GIC_SPI 410 IRQ_TYPE_LEVEL_HIGH>;
>> +            interrupt-names = "int_serr", "int_serr_cor", "int_serr_nonfatal",
>> +                              "int_serr_fatal", "axi_err_int", "inta_rc",
>> +                              "intb_rc", "intc_rc", "intd_rc",
>> +                              "intmsi_rc", "int_link_bandwidth", "int_pm_pme",
>> +                              "dma_int", "pcie_evt_int", "msg_int",
>> +                              "int_all";
>> +            #interrupt-cells = <1>;
>> +            interrupt-map-mask = <0 0 0 7>;
>> +            interrupt-map = <0 0 0 1 &pcie_intx 0>, /* INT A */
>> +                            <0 0 0 2 &pcie_intx 1>, /* INT B */
>> +                            <0 0 0 3 &pcie_intx 2>, /* INT C */
>> +                            <0 0 0 4 &pcie_intx 3>; /* INT D */
>> +            device_type = "pci";
>> +            num-lanes = <1>;
>> +            #address-cells = <3>;
>> +            #size-cells = <2>;
>> +            power-domains = <&cpg>;
>> +            renesas,sysc = <&sysc>;
>> +            vendor-id = <0x1912>;
>> +            device-id = <0x0033>;
>> +
>> +            pcie_intx: legacy-interrupt-controller {
>> +                interrupt-controller;
>> +                #interrupt-cells = <1>;
>> +                #address-cells = <0>;
>> +                interrupt-parent = <&gic>;
>> +                interrupts = <GIC_SPI 400 IRQ_TYPE_LEVEL_HIGH>,
>> +                             <GIC_SPI 401 IRQ_TYPE_LEVEL_HIGH>,
>> +                             <GIC_SPI 402 IRQ_TYPE_LEVEL_HIGH>,
>> +                             <GIC_SPI 403 IRQ_TYPE_LEVEL_HIGH>;
>> +            };
>> +        };
>> +    };
>> +
>> +...
>> -- 
>> 2.43.0
>>



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

end of thread, other threads:[~2025-05-14 13:54 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-04-30 10:32 [PATCH 0/8] PCI: rzg3s-host: Add PCIe driver for Renesas RZ/G3S SoC Claudiu
2025-04-30 10:32 ` [PATCH 1/8] soc: renesas: r9a08g045-sysc: Add max reg offset Claudiu
2025-05-01  9:26   ` kernel test robot
2025-05-01 10:32   ` kernel test robot
2025-05-01 16:12   ` kernel test robot
2025-04-30 10:32 ` [PATCH 2/8] clk: renesas: r9a08g045: Add clocks, resets and power domain support for the PCIe Claudiu
2025-04-30 10:32 ` [PATCH 3/8] of/irq: Export of_irq_count() Claudiu
2025-05-09 19:36   ` Rob Herring
2025-04-30 10:32 ` [PATCH 4/8] dt-bindings: PCI: renesas,r9a08g045s33-pcie: Add documentation for the PCIe IP on Renesas RZ/G3S Claudiu
2025-05-01 20:16   ` Bjorn Helgaas
2025-05-05 11:28     ` Claudiu Beznea
2025-05-09 21:08   ` Rob Herring
2025-05-14 11:41     ` Claudiu Beznea
2025-04-30 10:32 ` [PATCH 5/8] PCI: rzg3s-host: Add Initial PCIe Host Driver for Renesas RZ/G3S SoC Claudiu
2025-05-01 20:12   ` Bjorn Helgaas
2025-05-05 11:26     ` Claudiu Beznea
2025-05-09 10:29       ` Claudiu Beznea
2025-05-12 20:38         ` Bjorn Helgaas
2025-05-14 10:29           ` Claudiu Beznea
2025-05-12 20:25       ` Bjorn Helgaas
2025-05-14  9:37         ` Claudiu Beznea
2025-05-09 10:51   ` Philipp Zabel
2025-05-09 11:41     ` Claudiu Beznea
2025-05-09 20:49   ` Rob Herring
2025-05-14 11:39     ` Claudiu Beznea
2025-04-30 10:32 ` [PATCH 6/8] arm64: dts: renesas: r9a08g045s33: Add PCIe node Claudiu
2025-04-30 10:32 ` [PATCH 7/8] arm64: dts: renesas: rzg3s-smarc: Enable PCIe Claudiu
2025-04-30 10:32 ` [PATCH 8/8] arm64: defconfig: Enable PCIe for the Renesas RZ/G3S SoC Claudiu

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