devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v5 0/6] PCI: rzg3s-host: Add PCIe driver for Renesas RZ/G3S SoC
@ 2025-10-07 13:36 Claudiu
  2025-10-07 13:36 ` [PATCH v5 1/6] dt-bindings: PCI: renesas,r9a08g045s33-pcie: Add documentation for the PCIe IP on Renesas RZ/G3S Claudiu
                   ` (5 more replies)
  0 siblings, 6 replies; 29+ messages in thread
From: Claudiu @ 2025-10-07 13:36 UTC (permalink / raw)
  To: lpieralisi, kwilczynski, mani, robh, bhelgaas, krzk+dt, conor+dt,
	geert+renesas, magnus.damm, p.zabel
  Cc: claudiu.beznea, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, 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:
- patches 1-2/6:	add PCIe support for the RZ/G3S SoC
- patches 3-6/6:	add device tree support and defconfig flag

Please provide your feedback.

Merge strategy, if any:
- patches 1-2/6 can go through the PCI tree
- patches 3-6/6 can go through the Renesas tree

Thank you,
Claudiu Beznea

Changes in v5:
- dropped patch
  "arm64: dts: renesas: rzg3s-smarc-som: Update dma-ranges for PCIe"
  and introduced patch
  "arm64: dts: renesas: rzg3s-smarc-som: Add PCIe reference clock"
- addressed review comments
- per-patch changes are described in each individual patch

Changes in v4:
- dropped v3 patches:
  - "clk: renesas: r9a08g045: Add clocks and resets support for PCIe"
  - "soc: renesas: rz-sysc: Add syscon/regmap support"
  as they are already integrated
- dropped v3 patch "PCI: of_property: Restore the arguments of the
  next level parent" as it is not needed anymore in this version due
  port being added in device tree
- addressed review comments
- per-patch changes are described in each individual patch

Changes in v3:
- added patch "PCI: of_property: Restore the arguments of the next level parent"
  to fix the legacy interrupt request
- addressed review comments
- per-patch changes are described in each individual patch

Changes in v2:
- dropped "of/irq: Export of_irq_count()" as it is not needed anymore
  in this version
- added "arm64: dts: renesas: rzg3s-smarc-som: Update dma-ranges for PCIe"
  to reflect the board specific memory constraints
- addressed review comments
- updated patch "soc: renesas: rz-sysc: Add syscon/regmap support"
- per-patch changes are described in each individual patch

Claudiu Beznea (6):
  dt-bindings: PCI: renesas,r9a08g045s33-pcie: Add documentation for the
    PCIe IP on Renesas RZ/G3S
  PCI: rzg3s-host: Add Renesas RZ/G3S SoC host driver
  arm64: dts: renesas: r9a08g045: Add PCIe node
  arm64: dts: renesas: rzg3s-smarc-som: Add PCIe reference clock
  arm64: dts: renesas: rzg3s-smarc: Enable PCIe
  arm64: defconfig: Enable PCIe for the Renesas RZ/G3S SoC

 .../bindings/pci/renesas,r9a08g045-pcie.yaml  |  239 +++
 MAINTAINERS                                   |    8 +
 arch/arm64/boot/dts/renesas/r9a08g045.dtsi    |   66 +
 .../boot/dts/renesas/rzg3s-smarc-som.dtsi     |    5 +
 arch/arm64/boot/dts/renesas/rzg3s-smarc.dtsi  |   11 +
 arch/arm64/configs/defconfig                  |    1 +
 drivers/pci/controller/Kconfig                |    8 +
 drivers/pci/controller/Makefile               |    1 +
 drivers/pci/controller/pcie-rzg3s-host.c      | 1776 +++++++++++++++++
 9 files changed, 2115 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml
 create mode 100644 drivers/pci/controller/pcie-rzg3s-host.c

-- 
2.43.0


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

* [PATCH v5 1/6] dt-bindings: PCI: renesas,r9a08g045s33-pcie: Add documentation for the PCIe IP on Renesas RZ/G3S
  2025-10-07 13:36 [PATCH v5 0/6] PCI: rzg3s-host: Add PCIe driver for Renesas RZ/G3S SoC Claudiu
@ 2025-10-07 13:36 ` Claudiu
  2025-10-10 14:29   ` Rob Herring
  2025-10-10 14:32   ` Rob Herring
  2025-10-07 13:36 ` [PATCH v5 2/6] PCI: rzg3s-host: Add Renesas RZ/G3S SoC host driver Claudiu
                   ` (4 subsequent siblings)
  5 siblings, 2 replies; 29+ messages in thread
From: Claudiu @ 2025-10-07 13:36 UTC (permalink / raw)
  To: lpieralisi, kwilczynski, mani, robh, bhelgaas, krzk+dt, conor+dt,
	geert+renesas, magnus.damm, p.zabel
  Cc: claudiu.beznea, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, 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.

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

Changes in v5:
- dropped Tb tag
- style updates to the dma-ranges and ranges properties from
  examples section
- re-enabled the node from examples section

Changes in v4:
- dropped "s33" string from compatible name
- added port node documentation; due to this dropped Rob's Rb tag
- reorderded properties
- dropped spaces b/w "INT" and "A", "B", "C", "D" in comments

Changes in v3:
- collected tags
- updated the flags of ranges property from example

Changes in v2:
- update the interrupt names by dropping "int" and "rc" string; due
  to this the patch description was adjusted
- added "interrupt-controller" and made it mandatory
- s/clkl1pm/pm/g
- dropped the legacy-interrupt-controller node; with this the gic
  interrupt controller node was dropped as well as it is not needed
  anymore
- updated interrupt-map in example and added interrupt-controller
- added clock-names as required property as the pm clock is not
  handled though PM domains; this will allow the driver to have
  the option to request the pm clock by its name when implementation
  will be adjusted to used the pm clock
- adjusted the size of dma-ranges to reflect the usage on
  SMARC module board
- moved "renesas,sysc" at the end of the node in example to align
  with dts coding style

 .../bindings/pci/renesas,r9a08g045-pcie.yaml  | 239 ++++++++++++++++++
 1 file changed, 239 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml

diff --git a/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml b/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml
new file mode 100644
index 000000000000..d21d16b4e28d
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml
@@ -0,0 +1,239 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/renesas,r9a08g045-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,r9a08g045-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: serr
+      - description: ser_cor
+      - description: serr_nonfatal
+      - description: serr_fatal
+      - description: axi_err
+      - description: inta
+      - description: intb
+      - description: intc
+      - description: intd
+      - description: msi
+      - description: link_bandwidth
+      - description: pm_pme
+      - description: dma
+      - description: pcie_evt
+      - description: msg
+      - description: all
+
+  interrupt-controller: true
+
+  clocks:
+    items:
+      - description: System clock
+      - description: PM control clock
+
+  clock-names:
+    items:
+      - description: aclk
+      - description: 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
+
+patternProperties:
+  "^pcie@0,[0-0]$":
+    type: object
+    allOf:
+      - $ref: /schemas/pci/pci-device.yaml#
+      - $ref: /schemas/pci/pci-pci-bridge.yaml#
+
+    properties:
+      reg:
+        maxItems: 1
+
+      vendor-id:
+        const: 0x1912
+
+      device-id:
+        const: 0x0033
+
+      clocks:
+        items:
+          - description: Reference clock
+
+      clock-names:
+        items:
+          - const: ref
+
+    required:
+      - device_type
+      - vendor-id
+      - device-id
+      - clocks
+      - clock-names
+
+    unevaluatedProperties: false
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - resets
+  - reset-names
+  - interrupts
+  - interrupt-names
+  - interrupt-map
+  - interrupt-map-mask
+  - interrupt-controller
+  - power-domains
+  - "#address-cells"
+  - "#size-cells"
+  - "#interrupt-cells"
+  - renesas,sysc
+
+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>;
+
+        pcie@11e40000 {
+            compatible = "renesas,r9a08g045-pcie";
+            reg = <0 0x11e40000 0 0x10000>;
+            ranges = <0x02000000 0 0x30000000 0 0x30000000 0 0x08000000>;
+            /* Map all possible DRAM ranges (4 GB). */
+            dma-ranges = <0x42000000 0 0x40000000 0 0x40000000 1 0x00000000>;
+            bus-range = <0x0 0xff>;
+            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 = "serr", "serr_cor", "serr_nonfatal",
+                              "serr_fatal", "axi_err", "inta",
+                              "intb", "intc", "intd", "msi",
+                              "link_bandwidth", "pm_pme", "dma",
+                              "pcie_evt", "msg", "all";
+            #interrupt-cells = <1>;
+            interrupt-controller;
+            interrupt-map-mask = <0 0 0 7>;
+            interrupt-map = <0 0 0 1 &pcie 0 0 0 0>, /* INTA */
+                            <0 0 0 2 &pcie 0 0 0 1>, /* INTB */
+                            <0 0 0 3 &pcie 0 0 0 2>, /* INTC */
+                            <0 0 0 4 &pcie 0 0 0 3>; /* INTD */
+            clocks = <&cpg CPG_MOD R9A08G045_PCI_ACLK>,
+                     <&cpg CPG_MOD R9A08G045_PCI_CLKL1PM>;
+            clock-names = "aclk", "pm";
+            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";
+            power-domains = <&cpg>;
+            device_type = "pci";
+            #address-cells = <3>;
+            #size-cells = <2>;
+            max-link-speed = <2>;
+            renesas,sysc = <&sysc>;
+
+            pcie_port0: pcie@0,0 {
+                reg = <0x0 0x0 0x0 0x0 0x0>;
+                ranges;
+                clocks = <&versa3 5>;
+                clock-names = "ref";
+                device_type = "pci";
+                vendor-id = <0x1912>;
+                device-id = <0x0033>;
+                #address-cells = <3>;
+                #size-cells = <2>;
+            };
+        };
+    };
+
+...
-- 
2.43.0


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

* [PATCH v5 2/6] PCI: rzg3s-host: Add Renesas RZ/G3S SoC host driver
  2025-10-07 13:36 [PATCH v5 0/6] PCI: rzg3s-host: Add PCIe driver for Renesas RZ/G3S SoC Claudiu
  2025-10-07 13:36 ` [PATCH v5 1/6] dt-bindings: PCI: renesas,r9a08g045s33-pcie: Add documentation for the PCIe IP on Renesas RZ/G3S Claudiu
@ 2025-10-07 13:36 ` Claudiu
  2025-10-19  6:52   ` Manivannan Sadhasivam
                     ` (2 more replies)
  2025-10-07 13:36 ` [PATCH v5 3/6] arm64: dts: renesas: r9a08g045: Add PCIe node Claudiu
                   ` (3 subsequent siblings)
  5 siblings, 3 replies; 29+ messages in thread
From: Claudiu @ 2025-10-07 13:36 UTC (permalink / raw)
  To: lpieralisi, kwilczynski, mani, robh, bhelgaas, krzk+dt, conor+dt,
	geert+renesas, magnus.damm, p.zabel
  Cc: claudiu.beznea, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, Claudiu Beznea, Wolfram Sang

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.

Hardware manual can be downloaded from the address in the "Link" section.
The following steps should be followed to access the manual:
1/ Click the "User Manual" button
2/ Click "Confirm"; this will start downloading an archive
3/ Open the downloaded archive
4/ Navigate to r01uh1014ej*-rzg3s-users-manual-hardware -> Deliverables
5/ Open the file r01uh1014ej*-rzg3s.pdf

Link: https://www.renesas.com/en/products/rz-g3s?queryID=695cc067c2d89e3f271d43656ede4d12
Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---

Changes in v5:
- replaced devm action or resets with gotos
- s/writeb/writeb_relaxed/g
- s/readw/readw_relaxed/g
- s/readl/readl_relaxed/g
- s/writel/writel_relaxed/g
- dropped rzg3s_pcie_child_ops::map_bus, rename rzg3s_pcie_child_map_bus()
  (that was used to instantiate rzg3s_pcie_child_ops::map_bus) to
  rzg3s_pcie_child_prepare_bus() and used it directly in
  rzg3s_pcie_child_read_conf() and rzg3s_pcie_child_write_conf()
- convert the 3rd argument of readl_poll_timeout() to microseconds
- set rzg3s_pcie_driver.driver.probe_type = PROBE_PREFER_ASYNCHRONOUS

Changes in v4:
- updated patch title
- drop the option to build the driver as module
- make it dependent on IRQ_MSI_LIB and adjust the code to use this library
- added code to parse and set PCIe port; moved device ID, vendor ID
  setup under port configuration; added reference clock setup to the port
  (which was missed previously); moved the PHY setup on port specific
  setup function
- used of_pci_get_max_link_speed() to get the maximum available link speed
  from device tree; with this updated the logic that set the max link speed
- simplified the logic in rzg3s_pcie_child_issue_request()
- set the type of access in rzg3s_pcie_child_write_conf/rzg3s_pcie_child_read_conf()
- added comment on rzg3s_pcie_root_write() about the used lock for serialization
- added RZG3S_PCI_MSIRCVWMSKL_MASK mask and use it properly to set the
  MSI size
- fixed typos, dropped "link up" log on probe
- updated the error message on inbound/outbound window failure setup along
  with the returning code
- used unsigned int type for the counter in for loops from
  rzg3s_soc_pcie_init_phy()
- dropped unnecessary comments
- moved msleep(PCIE_RESET_CONFIG_WAIT_MS) in rzg3s_pcie_host_setup() to
  allow resume path to also benefit of it
- drop the power reset de-assert from rzg3s_pcie_resets_prepare(), rename
  rzg3s_pcie_resets_prepare() into rzg3s_pcie_resets_prepare_and_get()
  to reflect that rzg3s_pcie_resets_prepare_and_get() only allocate memory
  for resets and get them; the power resets are now de-asserted outside of
  rzg3s_pcie_resets_prepare_and_get()
- add a comment for pm_runtime_resume_and_get() to reflect it is used
  to power the clock domain the controller belongs to

Changes in v3:
- updated patch description with link to the hardware manual and steps
  to access it
- included <linux/bitfields.h> to solve compilation errors
- used devm_mutex_init()
- used SZ_4K instead of 4096
- dropped PCIe register defines and used the ones from
  include/uapi/linux/pci_regs.h; added RZG3S_PCI_CFG_PCIEC as the starting
  offset of the capabilities to be used before the host bridge is
  registered
- added blank lines around registers and bitfields defines
- kept the defines for bitfies in order (from MSB to LSB)
- dropped timeout defines (except RZG3S_REQ_ISSUE_TIMEOUT_US) and
  used the ones from ../pci.h
- dropped rzg3s_pcie_link_speed and used defines from 
  include/uapi/linux/pci_regs.h
- in rzg3s_pcie_child_write() call directly rzg3s_pcie_child_write_conf()
  if size is 4 and print the warning message on in the other cases
- return NULL in rzg3s_pcie_child_map_bus() and added a comment about it
- in rzg3s_pcie_root_write() and rzg3s_soc_pcie_init_phy() added a comment
  about the setting done on RZG3S_PCI_PERM register
- register rzg3s_pcie_msi_free_domains(), rzg3s_pcie_msi_teardown(),
  rzg3s_pcie_intx_teardown() as devm action or reset functions
- used irq_domain_create_linear() for intx domain
- added the rzg3s_pcie_power_resets_deassert() helper to de-assert the
  power domain and wait before doing it
- wait PCIE_RESET_CONFIG_WAIT_MS before registering the host
- made rzg3s_soc_power_resets[] and rzg3s_soc_cfg_resets[] static
- added suppress_bind_attrs = true
- collected tags

Changes in v2:
- dropped the references to other RZ SoCs from patch description
- dropped the dot at the end of single line comments that are not a
  sentence
- as a result of v2 rework removed unused macros and definitions
  (e.g. RZG3S_PCI_REQISS_TR_TP1_RD, RZG3S_PCI_REQISS_TR_TP1_WR,
  enum rzg3s_pcie_cfg_access_type)
- dropped driver specific defines that are for generic PCI
  register offsets and used the generic ones
- updated the value of RZG3S_PCI_MSI_INT_NR as on RZ/G3S there
  are no more than 32 MSIs (v1 value was due to mistake in the
  HW manual)
- added timeout macros to be used by read_poll_timeout() specific
  functions
- re-worked the reset handling part by using reset subsystem specific
  functions only; with this the struct rzg3s_pcie_soc_data was
  added; reference to PHY initialization function was added to this
  structure as well
- dropped devres_group_id as the issue it tried to address will
  now be fixed in platform bus code (v2 posted [2])
- use 80 columns alignment
- updated function name in the idea of using names similar to
  what is used in other drivers
- added rzg3s_pcie_root_ops and rzg3s_pcie_child_ops and populate
  bridge->ops, bridge->child_ops with it; from probe:
+	bridge->ops = &rzg3s_pcie_root_ops;
+	bridge->child_ops = &rzg3s_pcie_child_ops;
- print a warning for 32 bit accesses (based on the value of
  bus->unsafe_warn as done in the common code)
- dropped dev_dbg() in read/write functions
- added HW manual revision identifier in comments that points to the
  statements from manual
- reworked the rzg3s_pcie_intx_setup() as the legacy interrupt DT
  node is not used anymore
- in rzg3s_pcie_config_init() do not hardcode anymore the
  primary bus, secondary bus, subordinate bus but get this information
  from device tree and update HW registers accordingly
- dropped rzg3s_pcie_remove() and added rzg3s_pcie_host_remove_action()
  to be used as a devm action or reset function
- s/rzg3s_pcie_suspend/rzg3s_pcie_suspend_noirq,
  s/rzg3s_pcie_resume/rzg3s_pcie_resume_noirq
- dropped DEFINE_NOIRQ_DEV_PM_OPS()
- updated driver name (rzg3s-pcie-host) to reflect it is for RZ/G3S 

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

 MAINTAINERS                              |    8 +
 drivers/pci/controller/Kconfig           |    8 +
 drivers/pci/controller/Makefile          |    1 +
 drivers/pci/controller/pcie-rzg3s-host.c | 1776 ++++++++++++++++++++++
 4 files changed, 1793 insertions(+)
 create mode 100644 drivers/pci/controller/pcie-rzg3s-host.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 156fa8eefa69..4e9734cf96a7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19987,6 +19987,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,r9a08g045-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 41748d083b93..8732bafbbcd7 100644
--- a/drivers/pci/controller/Kconfig
+++ b/drivers/pci/controller/Kconfig
@@ -266,6 +266,14 @@ 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
+	bool "Renesas RZ/G3S PCIe host controller"
+	depends on ARCH_RENESAS || COMPILE_TEST
+	select MFD_SYSCON
+	select IRQ_MSI_LIB
+	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..196cf3fc7fd6
--- /dev/null
+++ b/drivers/pci/controller/pcie-rzg3s-host.c
@@ -0,0 +1,1776 @@
+// 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/bitfield.h>
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqchip/irq-msi-lib.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mutex.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/sizes.h>
+#include <linux/slab.h>
+#include <linux/units.h>
+
+#include "../pci.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_MOR_STATUS		GENMASK(18, 16)
+#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_REQ_ISSUE		BIT(0)
+
+#define RZG3S_PCI_MSIRCVWADRL			0x100
+#define RZG3S_PCI_MSIRCVWADRL_MSG_DATA_ENA	BIT(1)
+#define RZG3S_PCI_MSIRCVWADRL_ENA		BIT(0)
+
+#define RZG3S_PCI_MSIRCVWADRU			0x104
+
+#define RZG3S_PCI_MSIRCVWMSKL			0x108
+#define RZG3S_PCI_MSIRCVWMSKL_MASK		GENMASK(31, 2)
+
+#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_RX_DLLP_PM_ENTER	BIT(12)
+#define RZG3S_PCI_PEIS0_DL_UPDOWN		BIT(9)
+
+#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_LTSSM_STATE		GENMASK(14, 10)
+#define RZG3S_PCI_PCSTAT1_DL_DOWN_STS		BIT(0)
+
+#define RZG3S_PCI_PCCTRL2			0x410
+#define RZG3S_PCI_PCCTRL2_LS_CHG		GENMASK(9, 8)
+#define RZG3S_PCI_PCCTRL2_LS_CHG_REQ		BIT(0)
+
+#define RZG3S_PCI_PCSTAT2			0x414
+#define RZG3S_PCI_PCSTAT2_LS_CHG_DONE		BIT(28)
+#define RZG3S_PCI_PCSTAT2_STATE_RX_DETECT	GENMASK(15, 8)
+#define RZG3S_PCI_PCSTAT2_SDRIRE		GENMASK(7, 1)
+
+#define RZG3S_PCI_PERM				0x300
+#define RZG3S_PCI_PERM_CFG_HWINIT_EN		BIT(2)
+#define RZG3S_PCI_PERM_PIPE_PHY_REG_EN		BIT(1)
+
+#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_BARMSK00L			0xa0
+#define RZG3S_PCI_CFG_BARMSK00U			0xa4
+
+#define RZG3S_PCI_CFG_PCIEC			0x60
+
+/* 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
+
+/* Timeouts experimentally determined. */
+#define RZG3S_REQ_ISSUE_TIMEOUT_US		2500
+
+/**
+ * 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
+ * @irq: MSI interrupt
+ */
+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;
+	int irq;
+};
+
+struct rzg3s_pcie_host;
+
+/**
+ * struct rzg3s_pcie_soc_data - SoC specific data
+ * @init_phy: PHY initialization function
+ * @power_resets: array with the resets that need to be de-asserted after
+ *                power-on
+ * @cfg_resets: array with the resets that need to be de-asserted after
+ *              configuration
+ * @num_power_resets: number of power resets
+ * @num_cfg_resets: number of configuration resets
+ */
+struct rzg3s_pcie_soc_data {
+	int (*init_phy)(struct rzg3s_pcie_host *host);
+	const char * const *power_resets;
+	const char * const *cfg_resets;
+	u8 num_power_resets;
+	u8 num_cfg_resets;
+};
+
+/**
+ * struct rzg3s_pcie_port - RZ/G3S PCIe Root port data structure
+ * @refclk: PCIe reference clock
+ * @vendor_id: Vendor ID
+ * @device_id: Device ID
+ */
+struct rzg3s_pcie_port {
+	struct clk *refclk;
+	u32 vendor_id;
+	u32 device_id;
+};
+
+/**
+ * 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
+ * @sysc: SYSC regmap
+ * @intx_domain: INTx IRQ domain
+ * @data: SoC specific data
+ * @msi: MSI data structure
+ * @hw_lock: lock for access to the HW resources
+ * @intx_irqs: INTx interrupts
+ * @max_link_speed: maximum supported link speed
+ * @port: PCIe Root port
+ */
+struct rzg3s_pcie_host {
+	void __iomem *axi;
+	void __iomem *pcie;
+	struct device *dev;
+	struct reset_control_bulk_data *power_resets;
+	struct reset_control_bulk_data *cfg_resets;
+	struct regmap *sysc;
+	struct irq_domain *intx_domain;
+	const struct rzg3s_pcie_soc_data *data;
+	struct rzg3s_pcie_msi msi;
+	raw_spinlock_t hw_lock;
+	int intx_irqs[PCI_NUM_INTX];
+	int max_link_speed;
+	struct rzg3s_pcie_port port;
+};
+
+#define rzg3s_msi_to_host(_msi)	container_of(_msi, struct rzg3s_pcie_host, msi)
+
+static void rzg3s_pcie_update_bits(void __iomem *base, u32 offset, u32 mask,
+				   u32 val)
+{
+	u32 tmp;
+
+	tmp = readl_relaxed(base + offset);
+	tmp &= ~mask;
+	tmp |= val & mask;
+	writel_relaxed(tmp, base + offset);
+}
+
+static int rzg3s_pcie_child_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, RZG3S_REQ_ISSUE_TIMEOUT_US);
+
+	if (val & RZG3S_PCI_REQISS_MOR_STATUS)
+		return -EIO;
+
+	return ret;
+}
+
+static void rzg3s_pcie_child_prepare_bus(struct pci_bus *bus,
+					 unsigned int devfn,
+					 int where)
+{
+	struct rzg3s_pcie_host *host = bus->sysdata;
+	unsigned int dev, func, reg;
+
+	dev = PCI_SLOT(devfn);
+	func = PCI_FUNC(devfn);
+	reg = where & ~0x3;
+
+	/* Set the destination */
+	writel_relaxed(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_relaxed(RZG3S_PCI_REQBE_BYTE_EN, host->axi + RZG3S_PCI_REQBE);
+}
+
+static int rzg3s_pcie_child_read_conf(struct rzg3s_pcie_host *host,
+				      struct pci_bus *bus,
+				      unsigned int devfn, int where,
+				      u32 *data)
+{
+	bool type0 = pci_is_root_bus(bus->parent) ? true : false;
+	int ret;
+
+	rzg3s_pcie_child_prepare_bus(bus, devfn, where);
+
+	/* Set the type of request */
+	writel_relaxed(type0 ? RZG3S_PCI_REQISS_TR_TP0_RD :
+			       RZG3S_PCI_REQISS_TR_TP1_RD,
+		       host->axi + RZG3S_PCI_REQISS);
+
+	/* Issue the request and wait to finish */
+	ret = rzg3s_pcie_child_issue_request(host);
+	if (ret)
+		return PCIBIOS_SET_FAILED;
+
+	/* Read the data */
+	*data = readl_relaxed(host->axi + RZG3S_PCI_REQRCVDAT);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+/* Serialization is provided by 'pci_lock' in drivers/pci/access.c */
+static int rzg3s_pcie_child_read(struct pci_bus *bus, unsigned int devfn,
+				 int where, int size, u32 *val)
+{
+	struct rzg3s_pcie_host *host = bus->sysdata;
+	int ret;
+
+	ret = rzg3s_pcie_child_read_conf(host, bus, devfn, where, val);
+	if (ret != PCIBIOS_SUCCESSFUL)
+		return ret;
+
+	if (size <= 2)
+		*val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int rzg3s_pcie_child_write_conf(struct rzg3s_pcie_host *host,
+				       struct pci_bus *bus,
+				       unsigned int devfn, int where,
+				       u32 data)
+{
+	bool type0 = pci_is_root_bus(bus->parent) ? true : false;
+	int ret;
+
+	rzg3s_pcie_child_prepare_bus(bus, devfn, where);
+
+	/* Set the write data */
+	writel_relaxed(0, host->axi + RZG3S_PCI_REQDATA(0));
+	writel_relaxed(0, host->axi + RZG3S_PCI_REQDATA(1));
+	writel_relaxed(data, host->axi + RZG3S_PCI_REQDATA(2));
+
+	/* Set the type of request */
+	writel_relaxed(type0 ? RZG3S_PCI_REQISS_TR_TP0_WR :
+			       RZG3S_PCI_REQISS_TR_TP1_WR,
+		       host->axi + RZG3S_PCI_REQISS);
+
+	/* Issue the request and wait to finish */
+	ret = rzg3s_pcie_child_issue_request(host);
+	if (ret)
+		return PCIBIOS_SET_FAILED;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+/* Serialization is provided by 'pci_lock' in drivers/pci/access.c */
+static int rzg3s_pcie_child_write(struct pci_bus *bus, unsigned int devfn,
+				  int where, int size, u32 val)
+{
+	struct rzg3s_pcie_host *host = bus->sysdata;
+	u32 data, shift;
+	int ret;
+
+	if (size == 4)
+		return rzg3s_pcie_child_write_conf(host, bus, devfn, where, val);
+
+	/*
+	 * Controller does 32 bit accesses. To do byte accesses software need
+	 * to do read/modify/write. This may have potential side effects. For
+	 * example, software may perform a 16-bit write. If the hardware only
+	 * supports 32-bit accesses, we must do a 32-bit read, merge in the 16
+	 * bits we intend to write, followed by a 32-bit write. If the 16 bits
+	 * we *don't* intend to write happen to have any RW1C
+	 * (write-one-to-clear) bits set, we just inadvertently cleared
+	 * something we shouldn't have.
+	 */
+	if (!bus->unsafe_warn) {
+		dev_warn(&bus->dev, "%d-byte config write to %04x:%02x:%02x.%d offset %#x may corrupt adjacent RW1C bits\n",
+			 size, pci_domain_nr(bus), bus->number,
+			 PCI_SLOT(devfn), PCI_FUNC(devfn), where);
+		bus->unsafe_warn = 1;
+	}
+
+	ret = rzg3s_pcie_child_read_conf(host, bus, devfn, where, &data);
+	if (ret != PCIBIOS_SUCCESSFUL)
+		return ret;
+
+	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_child_write_conf(host, bus, devfn, where, data);
+}
+
+static struct pci_ops rzg3s_pcie_child_ops = {
+	.read		= rzg3s_pcie_child_read,
+	.write		= rzg3s_pcie_child_write,
+};
+
+static void __iomem *rzg3s_pcie_root_map_bus(struct pci_bus *bus,
+					     unsigned int devfn,
+					     int where)
+{
+	struct rzg3s_pcie_host *host = bus->sysdata;
+
+	if (devfn)
+		return NULL;
+
+	return host->pcie + where;
+}
+
+/* Serialization is provided by 'pci_lock' in drivers/pci/access.c */
+static int rzg3s_pcie_root_write(struct pci_bus *bus, unsigned int devfn,
+				 int where, int size, u32 val)
+{
+	struct rzg3s_pcie_host *host = bus->sysdata;
+
+	/* Enable access control to the CFGU */
+	writel_relaxed(RZG3S_PCI_PERM_CFG_HWINIT_EN,
+		       host->axi + RZG3S_PCI_PERM);
+
+	pci_generic_config_write(bus, devfn, where, size, val);
+
+	/* Disable access control to the CFGU */
+	writel_relaxed(0, host->axi + RZG3S_PCI_PERM);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops rzg3s_pcie_root_ops = {
+	.read		= pci_generic_config_read,
+	.write		= rzg3s_pcie_root_write,
+	.map_bus	= rzg3s_pcie_root_map_bus,
+};
+
+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)
+{
+	u8 regs = RZG3S_PCI_MSI_INT_NR / RZG3S_PCI_MSI_INT_PER_REG;
+	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_relaxed(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 < regs; reg_id++) {
+		status = readl_relaxed(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, 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_relaxed(BIT(reg_bit),
+				       host->axi + RZG3S_PCI_MSIRS(reg_id));
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+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_relaxed(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_irq_compose_msi_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 drop_mask = RZG3S_PCI_MSIRCVWADRL_ENA |
+			RZG3S_PCI_MSIRCVWADRL_MSG_DATA_ENA;
+	u32 lo, hi;
+
+	/*
+	 * Enable and msg data enable bits are part of the address lo. Drop
+	 * them.
+	 */
+	lo = readl_relaxed(host->axi + RZG3S_PCI_MSIRCVWADRL) & ~drop_mask;
+	hi = readl_relaxed(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			= "rzg3s-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_irq_compose_msi_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,
+};
+
+#define RZG3S_PCIE_MSI_FLAGS_REQUIRED	(MSI_FLAG_USE_DEF_DOM_OPS	| \
+					 MSI_FLAG_USE_DEF_CHIP_OPS	| \
+					 MSI_FLAG_NO_AFFINITY		| \
+					 MSI_FLAG_PCI_MSI_MASK_PARENT)
+
+#define RZG3S_PCIE_MSI_FLAGS_SUPPORTED	(MSI_FLAG_MULTI_PCI_MSI		| \
+					 MSI_GENERIC_FLAGS_MASK)
+
+static const struct msi_parent_ops rzg3s_pcie_msi_parent_ops = {
+	.required_flags		= RZG3S_PCIE_MSI_FLAGS_REQUIRED,
+	.supported_flags	= RZG3S_PCIE_MSI_FLAGS_SUPPORTED,
+	.bus_select_token	= DOMAIN_BUS_PCI_MSI,
+	.chip_flags		= MSI_CHIP_FLAG_SET_ACK,
+	.prefix			= "RZG3S-",
+	.init_dev_msi_info	= msi_lib_init_dev_msi_info,
+};
+
+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 irq_domain_info info = {
+		.fwnode		= dev_fwnode(dev),
+		.ops		= &rzg3s_pcie_msi_domain_ops,
+		.size		= RZG3S_PCI_MSI_INT_NR,
+		.host_data	= msi,
+	};
+
+	msi->domain = msi_create_parent_irq_domain(&info,
+						   &rzg3s_pcie_msi_parent_ops);
+	if (!msi->domain)
+		return dev_err_probe(dev, -ENOMEM,
+				     "failed to create IRQ domain\n");
+
+	return 0;
+}
+
+static int rzg3s_pcie_msi_hw_setup(struct rzg3s_pcie_host *host)
+{
+	u8 regs = RZG3S_PCI_MSI_INT_NR / RZG3S_PCI_MSI_INT_PER_REG;
+	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_relaxed(FIELD_PREP(RZG3S_PCI_MSIRCVWMSKL_MASK,
+				  RZG3S_PCI_MSI_INT_NR - 1),
+		       host->axi + RZG3S_PCI_MSIRCVWMSKL);
+
+	/* Set MSI window address and enable MSI window */
+	writel_relaxed(upper_32_bits(msi->window_base),
+		       host->axi + RZG3S_PCI_MSIRCVWADRU);
+	writel_relaxed(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 < regs; reg_id++) {
+		writel_relaxed(RZG3S_PCI_MSIRE_ENA,
+			       host->axi + RZG3S_PCI_MSIRE(reg_id));
+	}
+
+	/* Enable message receive interrupts */
+	writel_relaxed(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 (Rev.1.10, section 34.4.5.2 Setting
+	 * the MSI Window) the MSI window needs to fall within one of the
+	 * enabled AXI windows. Find an enabled 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_relaxed(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_relaxed(host->axi + RZG3S_PCI_AWBASEU(id));
+		base = baseu << 32 | basel;
+
+		maskl = readl_relaxed(host->axi + RZG3S_PCI_AWMASKL(id));
+		masku = readl_relaxed(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 void rzg3s_pcie_msi_hw_teardown(struct rzg3s_pcie_host *host)
+{
+	u8 regs = RZG3S_PCI_MSI_INT_NR / RZG3S_PCI_MSI_INT_PER_REG;
+
+	/* 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 < regs; reg_id++)
+		writel_relaxed(0, host->axi + RZG3S_PCI_MSIRE(reg_id));
+
+	/* Disable MSI window */
+	writel_relaxed(0, host->axi + RZG3S_PCI_MSIRCVWADRL);
+}
+
+static void rzg3s_pcie_msi_teardown(struct rzg3s_pcie_host *host)
+{
+	size_t size = RZG3S_PCI_MSI_INT_NR * sizeof(u32);
+	struct rzg3s_pcie_msi *msi = &host->msi;
+
+	rzg3s_pcie_msi_hw_teardown(host);
+
+	free_irq(msi->irq, host);
+	irq_domain_remove(msi->domain);
+
+	/* Free unused memory */
+	dma_unmap_single(host->dev, msi->dma_addr, size * 2, DMA_BIDIRECTIONAL);
+	free_pages(msi->pages, 0);
+}
+
+static int rzg3s_pcie_msi_enable(struct rzg3s_pcie_host *host)
+{
+	struct platform_device *pdev = to_platform_device(host->dev);
+	struct rzg3s_pcie_msi *msi = &host->msi;
+	struct device *dev = host->dev;
+	const char *devname;
+	int irq, ret;
+
+	ret = devm_mutex_init(dev, &msi->map_lock);
+	if (ret)
+		return ret;
+
+	msi->irq = platform_get_irq_byname(pdev, "msi");
+	if (msi->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 = request_irq(msi->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_irq;
+	}
+
+	return 0;
+
+free_irq:
+	free_irq(msi->irq, host);
+free_domains:
+	irq_domain_remove(msi->domain);
+	return ret;
+}
+
+static void rzg3s_pcie_intx_irq_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_irq_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_irq_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_irq_ack,
+	.irq_mask = rzg3s_pcie_intx_irq_mask,
+	.irq_unmask = rzg3s_pcie_intx_irq_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 void rzg3s_pcie_intx_teardown(struct rzg3s_pcie_host *host)
+{
+	irq_domain_remove(host->intx_domain);
+}
+
+static int rzg3s_pcie_intx_setup(struct rzg3s_pcie_host *host)
+{
+	struct device *dev = host->dev;
+
+	for (int i = 0; i < PCI_NUM_INTX; i++) {
+		struct platform_device *pdev = to_platform_device(dev);
+		char irq_name[5] = {0};
+		int irq;
+
+		scnprintf(irq_name, ARRAY_SIZE(irq_name), "int%c", 'a' + i);
+
+		irq = platform_get_irq_byname(pdev, irq_name);
+		if (irq < 0)
+			return dev_err_probe(dev, -EINVAL,
+					     "Failed to parse and map INT%c IRQ\n",
+					     'A' + i);
+
+		host->intx_irqs[i] = irq;
+		irq_set_chained_handler_and_data(irq,
+						 rzg3s_pcie_intx_irq_handler,
+						 host);
+	}
+
+	host->intx_domain = irq_domain_create_linear(of_fwnode_handle(dev->of_node),
+						     PCI_NUM_INTX,
+						     &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 int rzg3s_pcie_set_max_link_speed(struct rzg3s_pcie_host *host)
+{
+	u32 remote_supported_link_speeds, max_supported_link_speeds;
+	u32 cs2, tmp, pcie_cap = RZG3S_PCI_CFG_PCIEC;
+	u32 cur_link_speed, link_speed;
+	u8 ltssm_state_l0 = 0xc;
+	u16 lcs;
+	int ret;
+
+	/*
+	 * According to the RZ/G3S HW manual (Rev.1.10, section 34.6.3 Caution
+	 * when Changing the Speed Spontaneously) link speed change can be done
+	 * only when the LTSSM is in L0.
+	 */
+	ret = readl_poll_timeout(host->axi + RZG3S_PCI_PCSTAT1, tmp,
+				 FIELD_GET(RZG3S_PCI_PCSTAT1_LTSSM_STATE, tmp) == ltssm_state_l0,
+				 PCIE_LINK_WAIT_SLEEP_MS * MILLI,
+				 PCIE_LINK_WAIT_SLEEP_MS * MILLI *
+				 PCIE_LINK_WAIT_MAX_RETRIES);
+	if (ret)
+		return ret;
+
+	lcs = readw_relaxed(host->pcie + pcie_cap + PCI_EXP_LNKSTA);
+	cs2 = readl_relaxed(host->axi + RZG3S_PCI_PCSTAT2);
+
+	switch (pcie_link_speed[host->max_link_speed]) {
+	case PCIE_SPEED_5_0GT:
+		max_supported_link_speeds = GENMASK(PCI_EXP_LNKSTA_CLS_5_0GB - 1, 0);
+		link_speed = PCI_EXP_LNKCTL2_TLS_5_0GT;
+		break;
+	default:
+		/* Should not happen */
+		return -EINVAL;
+	}
+
+	cur_link_speed = FIELD_GET(PCI_EXP_LNKSTA_CLS, lcs);
+	remote_supported_link_speeds = FIELD_GET(RZG3S_PCI_PCSTAT2_SDRIRE, cs2);
+	/* Drop reserved bits */
+	remote_supported_link_speeds &= max_supported_link_speeds;
+
+	/*
+	 * Return if max link speed is already set or the connected device
+	 * doesn't support it.
+	 */
+	if (cur_link_speed == host->max_link_speed ||
+	    remote_supported_link_speeds != max_supported_link_speeds)
+		return 0;
+
+	/* Set target Link speed */
+	rzg3s_pcie_update_bits(host->pcie, pcie_cap + PCI_EXP_LNKCTL2,
+			       PCI_EXP_LNKCTL2_TLS,
+			       FIELD_PREP(PCI_EXP_LNKCTL2_TLS, link_speed));
+
+	/* 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,
+					  link_speed - 1));
+
+	ret = readl_poll_timeout(host->axi + RZG3S_PCI_PCSTAT2, cs2,
+				 (cs2 & RZG3S_PCI_PCSTAT2_LS_CHG_DONE),
+				 PCIE_LINK_WAIT_SLEEP_MS * MILLI,
+				 PCIE_LINK_WAIT_SLEEP_MS * MILLI *
+				 PCIE_LINK_WAIT_MAX_RETRIES);
+
+	/*
+	 * According to the RZ/G3S HW manual (Rev.1.10, 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 int rzg3s_pcie_config_init(struct rzg3s_pcie_host *host)
+{
+	struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host);
+	struct resource_entry *ft;
+	struct resource *bus;
+	u8 subordinate_bus;
+	u8 secondary_bus;
+	u8 primary_bus;
+
+	ft = resource_list_first_type(&bridge->windows, IORESOURCE_BUS);
+	if (!ft)
+		return -ENODEV;
+
+	bus = ft->res;
+	primary_bus = bus->start;
+	secondary_bus = bus->start + 1;
+	subordinate_bus = bus->end;
+
+	/* Enable access control to the CFGU */
+	writel_relaxed(RZG3S_PCI_PERM_CFG_HWINIT_EN,
+		       host->axi + RZG3S_PCI_PERM);
+
+	/* HW manual recommends to write 0xffffffff on initialization */
+	writel_relaxed(0xffffffff, host->pcie + RZG3S_PCI_CFG_BARMSK00L);
+	writel_relaxed(0xffffffff, host->pcie + RZG3S_PCI_CFG_BARMSK00U);
+
+	/* Update bus info */
+	writeb_relaxed(primary_bus, host->pcie + PCI_PRIMARY_BUS);
+	writeb_relaxed(secondary_bus, host->pcie + PCI_SECONDARY_BUS);
+	writeb_relaxed(subordinate_bus, host->pcie + PCI_SUBORDINATE_BUS);
+
+	/* Disable access control to the CFGU */
+	writel_relaxed(0, host->axi + RZG3S_PCI_PERM);
+
+	return 0;
+}
+
+static void rzg3s_pcie_irq_init(struct rzg3s_pcie_host *host)
+{
+	/*
+	 * According to the HW manual of the RZ/G3S (Rev.1.10, 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_relaxed(RZG3S_PCI_PEIS0_DL_UPDOWN |
+		       RZG3S_PCI_PEIS0_RX_DLLP_PM_ENTER,
+		       host->axi + RZG3S_PCI_PEIS0);
+
+	/* Disable all interrupts */
+	writel_relaxed(0, host->axi + RZG3S_PCI_PEIE0);
+
+	/* Clear all parity and ecc error interrupts */
+	writel_relaxed(~0U, host->axi + RZG3S_PCI_PEIS1);
+
+	/* Disable all parity and ecc error interrupts */
+	writel_relaxed(0, host->axi + RZG3S_PCI_PEIE1);
+
+	/* Clear all AXI master error interrupts */
+	writel_relaxed(~0U, host->axi + RZG3S_PCI_AMEIS);
+
+	/* Clear all AXI slave error interrupts */
+	writel_relaxed(~0U, host->axi + RZG3S_PCI_ASEIS1);
+
+	/* Clear all message receive interrupts */
+	writel_relaxed(~0U, host->axi + RZG3S_PCI_MSGRCVIS);
+}
+
+static int rzg3s_pcie_power_resets_deassert(struct rzg3s_pcie_host *host)
+{
+	const struct rzg3s_pcie_soc_data *data = host->data;
+
+	/*
+	 * According to the RZ/G3S HW manual (Rev.1.10, 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);
+	return reset_control_bulk_deassert(data->num_power_resets,
+					   host->power_resets);
+}
+
+static int rzg3s_pcie_resets_prepare_and_get(struct rzg3s_pcie_host *host)
+{
+	const struct rzg3s_pcie_soc_data *data = host->data;
+	int ret;
+
+	host->power_resets = devm_kmalloc_array(host->dev,
+						data->num_power_resets,
+						sizeof(*host->power_resets),
+						GFP_KERNEL);
+	if (!host->power_resets)
+		return -ENOMEM;
+
+	for (unsigned int i = 0; i < data->num_power_resets; i++)
+		host->power_resets[i].id = data->power_resets[i];
+
+	host->cfg_resets = devm_kmalloc_array(host->dev,
+					      data->num_cfg_resets,
+					      sizeof(*host->cfg_resets),
+					      GFP_KERNEL);
+	if (!host->cfg_resets)
+		return -ENOMEM;
+
+	for (unsigned int i = 0; i < data->num_cfg_resets; i++)
+		host->cfg_resets[i].id = data->cfg_resets[i];
+
+	ret = devm_reset_control_bulk_get_exclusive(host->dev,
+						    data->num_power_resets,
+						    host->power_resets);
+	if (ret)
+		return ret;
+
+	return devm_reset_control_bulk_get_exclusive(host->dev,
+						     data->num_cfg_resets,
+						     host->cfg_resets);
+}
+
+static int rzg3s_pcie_host_parse_root_port(struct rzg3s_pcie_host *host)
+{
+	struct device_node *of_port = of_get_next_child(host->dev->of_node, NULL);
+	struct rzg3s_pcie_port *port = &host->port;
+	int ret;
+
+	ret = of_property_read_u32(of_port, "vendor-id", &port->vendor_id);
+	if (ret)
+		return ret;
+
+	ret = of_property_read_u32(of_port, "device-id", &port->device_id);
+	if (ret)
+		return ret;
+
+	port->refclk = of_clk_get_by_name(of_port, "ref");
+	if (IS_ERR(port->refclk))
+		return PTR_ERR(port->refclk);
+
+	return 0;
+}
+
+static int rzg3s_pcie_host_init_root_port(struct rzg3s_pcie_host *host)
+{
+	struct rzg3s_pcie_port *port = &host->port;
+	struct device *dev = host->dev;
+	int ret;
+
+	/* Enable access control to the CFGU */
+	writel_relaxed(RZG3S_PCI_PERM_CFG_HWINIT_EN,
+		       host->axi + RZG3S_PCI_PERM);
+
+	/* Update vendor ID and device ID */
+	writew_relaxed(port->vendor_id, host->pcie + PCI_VENDOR_ID);
+	writew_relaxed(port->device_id, host->pcie + PCI_DEVICE_ID);
+
+	/* Disable access control to the CFGU */
+	writel_relaxed(0, host->axi + RZG3S_PCI_PERM);
+
+	ret = clk_prepare_enable(port->refclk);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to enable refclk!\n");
+
+	/* Set the PHY, if any */
+	if (host->data->init_phy) {
+		ret = host->data->init_phy(host);
+		if (ret) {
+			dev_err_probe(dev, ret, "Failed to set the PHY!\n");
+			goto refclk_disable;
+		}
+	}
+
+	return 0;
+
+refclk_disable:
+	clk_disable_unprepare(port->refclk);
+	return ret;
+}
+
+static int rzg3s_pcie_host_init(struct rzg3s_pcie_host *host)
+{
+	u32 val;
+	int ret;
+
+	/* Initialize the PCIe related registers */
+	ret = rzg3s_pcie_config_init(host);
+	if (ret)
+		return ret;
+
+	ret = rzg3s_pcie_host_init_root_port(host);
+	if (ret)
+		return ret;
+
+	/* Initialize the interrupts */
+	rzg3s_pcie_irq_init(host);
+
+	ret = reset_control_bulk_deassert(host->data->num_cfg_resets,
+					  host->cfg_resets);
+	if (ret)
+		goto disable_root_port_refclk;
+
+	/* Wait for link up */
+	ret = readl_poll_timeout(host->axi + RZG3S_PCI_PCSTAT1, val,
+				 !(val & RZG3S_PCI_PCSTAT1_DL_DOWN_STS),
+				 PCIE_LINK_WAIT_SLEEP_MS * MILLI,
+				 PCIE_LINK_WAIT_SLEEP_MS * MILLI *
+				 PCIE_LINK_WAIT_MAX_RETRIES);
+	if (ret)
+		goto cfg_resets_deassert;
+
+	val = readl_relaxed(host->axi + RZG3S_PCI_PCSTAT2);
+	dev_info(host->dev, "PCIe link status [0x%x]\n", val);
+
+	return 0;
+
+cfg_resets_deassert:
+	reset_control_bulk_assert(host->data->num_cfg_resets,
+				  host->cfg_resets);
+disable_root_port_refclk:
+	clk_disable_unprepare(host->port.refclk);
+	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_relaxed(upper_32_bits(cpu_addr),
+		       host->axi + RZG3S_PCI_ADESTU(id));
+	writel_relaxed(lower_32_bits(cpu_addr),
+		       host->axi + RZG3S_PCI_ADESTL(id));
+
+	/* Set window size */
+	writel_relaxed(upper_32_bits(size), host->axi + RZG3S_PCI_AWMASKU(id));
+	writel_relaxed(lower_32_bits(size), host->axi + RZG3S_PCI_AWMASKL(id));
+
+	/* Set PCIe window base address and enable the window */
+	writel_relaxed(upper_32_bits(pci_addr),
+		       host->axi + RZG3S_PCI_AWBASEU(id));
+	writel_relaxed(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, -ENOSPC,
+					     "Failed to map inbound window for resource (%s)\n",
+					     entry->res->name);
+
+		size = resource_size(entry->res) - size_id;
+
+		/*
+		 * According to the RZ/G3S HW manual (Rev.1.10,
+		 * section 34.3.1.71 AXI Window Mask (Lower) Registers) the min
+		 * size is 4K.
+		 */
+		size = max(size, SZ_4K);
+
+		/*
+		 * According the RZ/G3S HW manual (Rev.1.10, 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 aligned and be a
+		 * power of 2.
+		 */
+		size = ALIGN(size, SZ_4K);
+
+		/*
+		 * According to the RZ/G3S HW manual (Rev.1.10, section
+		 * 34.3.1.71 AXI Window Mask (Lower) Registers) HW expects first
+		 * 12 LSB bits to be 0xfff. Subtract 1 from size for this.
+		 */
+		size = roundup_pow_of_two(size) - 1;
+
+		cpu_addr = ALIGN(cpu_addr, SZ_4K);
+		pci_addr = ALIGN(pci_addr, SZ_4K);
+
+		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 (Rev.1.10, section 34.3.1.75 PCIe
+	 * Window Base (Lower) Registers) the window base address need to be 4K
+	 * aligned.
+	 */
+	res_start = ALIGN(res_start, SZ_4K);
+
+	size = ALIGN(size, SZ_4K);
+	size = roundup_pow_of_two(size) - 1;
+
+	/* Set PCIe destination */
+	writel_relaxed(upper_32_bits(res_start),
+		       host->axi + RZG3S_PCI_PDESTU(id));
+	writel_relaxed(lower_32_bits(res_start),
+		       host->axi + RZG3S_PCI_PDESTL(id));
+
+	/* Set PCIe window mask */
+	writel_relaxed(upper_32_bits(size), host->axi + RZG3S_PCI_PWMASKU(id));
+	writel_relaxed(lower_32_bits(size), host->axi + RZG3S_PCI_PWMASKL(id));
+
+	/* Set PCIe window base and enable the window */
+	writel_relaxed(upper_32_bits(res_start),
+		       host->axi + RZG3S_PCI_PWBASEU(id));
+	writel_relaxed(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, -ENOSPC,
+					     "Failed to map outbound window for resource (%s)\n",
+					     res->name);
+
+		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_soc_pcie_init_phy(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,
+	};
+
+	/*
+	 * Enable access permission for physical layer control and status
+	 * registers.
+	 */
+	writel_relaxed(RZG3S_PCI_PERM_PIPE_PHY_REG_EN,
+		       host->axi + RZG3S_PCI_PERM);
+
+	for (unsigned int i = 0; i < RZG3S_PCI_PHY_XCFGD_NUM; i++) {
+		writel_relaxed(xcfgd_settings[i],
+			       host->axi + RZG3S_PCI_PHY_XCFGD(i));
+	}
+
+	for (unsigned int i = 0; i < RZG3S_PCI_PHY_XCFGA_CMN_NUM; i++) {
+		writel_relaxed(xcfga_cmn_settings[i],
+			       host->axi + RZG3S_PCI_PHY_XCFGA_CMN(i));
+	}
+
+	for (unsigned int i = 0; i < RZG3S_PCI_PHY_XCFGA_RX_NUM; i++) {
+		writel_relaxed(xcfga_rx_settings[i],
+			       host->axi + RZG3S_PCI_PHY_XCFGA_RX(i));
+	}
+
+	writel_relaxed(0x107, host->axi + RZG3S_PCI_PHY_XCFGA_TX);
+
+	/* Select PHY settings values */
+	writel_relaxed(RZG3S_PCI_PHY_XCFG_CTRL_PHYREG_SEL,
+		       host->axi + RZG3S_PCI_PHY_XCFG_CTRL);
+
+	/*
+	 * Disable access permission for physical layer control and status
+	 * registers.
+	 */
+	writel_relaxed(0, host->axi + RZG3S_PCI_PERM);
+
+	return 0;
+}
+
+static int
+rzg3s_pcie_host_setup(struct rzg3s_pcie_host *host,
+		      int (*intx_setup)(struct rzg3s_pcie_host *host),
+		      void (*intx_teardown)(struct rzg3s_pcie_host *host),
+		      int (*msi_setup)(struct rzg3s_pcie_host *host),
+		      void (*msi_teardown)(struct rzg3s_pcie_host *host))
+{
+	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");
+
+	if (intx_setup) {
+		ret = intx_setup(host);
+		if (ret)
+			return dev_err_probe(dev, ret,
+					     "Failed to setup INTx\n");
+	}
+
+	if (IS_ENABLED(CONFIG_PCI_MSI)) {
+		ret = msi_setup(host);
+		if (ret) {
+			dev_err_probe(dev, ret, "Failed to setup MSIs\n");
+			goto intx_teardown;
+		}
+	}
+
+	ret = rzg3s_pcie_host_init(host);
+	if (ret) {
+		dev_err_probe(dev, ret,
+			      "Failed to initialize the HW!\n");
+		goto msi_teardown;
+	}
+
+	ret = rzg3s_pcie_set_max_link_speed(host);
+	if (ret)
+		dev_info(dev, "Failed to set max link speed\n");
+
+	msleep(PCIE_RESET_CONFIG_WAIT_MS);
+
+	return 0;
+
+msi_teardown:
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		msi_teardown(host);
+intx_teardown:
+	if (intx_teardown)
+		intx_teardown(host);
+
+	return ret;
+}
+
+static int rzg3s_pcie_probe(struct platform_device *pdev)
+{
+	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->data = device_get_match_data(dev);
+	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;
+
+	host->max_link_speed = of_pci_get_max_link_speed(np);
+	if (host->max_link_speed < 0)
+		host->max_link_speed = 2;
+
+	ret = rzg3s_pcie_host_parse_root_port(host);
+	if (ret)
+		return ret;
+
+	host->sysc = syscon_node_to_regmap(sysc_np);
+	if (IS_ERR(host->sysc)) {
+		ret = PTR_ERR(host->sysc);
+		goto port_refclk_put;
+	}
+
+	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)
+		goto port_refclk_put;
+
+	ret = rzg3s_pcie_resets_prepare_and_get(host);
+	if (ret)
+		goto sysc_signal_restore;
+
+	ret = rzg3s_pcie_power_resets_deassert(host);
+	if (ret)
+		goto sysc_signal_restore;
+
+	pm_runtime_enable(dev);
+
+	/*
+	 * Controller clocks are part of a clock power domain. Enable them
+	 * through runtime PM.
+	 */
+	ret = pm_runtime_resume_and_get(dev);
+	if (ret)
+		goto rpm_disable;
+
+	raw_spin_lock_init(&host->hw_lock);
+
+	ret = rzg3s_pcie_host_setup(host, rzg3s_pcie_intx_setup,
+				    rzg3s_pcie_intx_teardown,
+				    rzg3s_pcie_msi_enable,
+				    rzg3s_pcie_msi_teardown);
+	if (ret)
+		goto rpm_put;
+
+	bridge->sysdata = host;
+	bridge->ops = &rzg3s_pcie_root_ops;
+	bridge->child_ops = &rzg3s_pcie_child_ops;
+	ret = pci_host_probe(bridge);
+	if (ret)
+		goto host_probe_teardown;
+
+	return 0;
+
+host_probe_teardown:
+	rzg3s_pcie_msi_teardown(host);
+	rzg3s_pcie_intx_teardown(host);
+	reset_control_bulk_deassert(host->data->num_cfg_resets,
+				    host->cfg_resets);
+rpm_put:
+	pm_runtime_put_sync(dev);
+rpm_disable:
+	pm_runtime_disable(dev);
+	reset_control_bulk_assert(host->data->num_power_resets,
+				  host->power_resets);
+sysc_signal_restore:
+	/*
+	 * SYSC RST_RSM_B signal need to be asserted before turning off the
+	 * power to the PHY.
+	 */
+	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, 0));
+port_refclk_put:
+	clk_put(host->port.refclk);
+
+	return ret;
+}
+
+static int rzg3s_pcie_suspend_noirq(struct device *dev)
+{
+	struct rzg3s_pcie_host *host = dev_get_drvdata(dev);
+	const struct rzg3s_pcie_soc_data *data = host->data;
+	struct rzg3s_pcie_port *port = &host->port;
+	struct regmap *sysc = host->sysc;
+	int ret;
+
+	ret = pm_runtime_put_sync(dev);
+	if (ret)
+		return ret;
+
+	clk_disable_unprepare(port->refclk);
+
+	ret = reset_control_bulk_assert(data->num_power_resets,
+					host->power_resets);
+	if (ret)
+		goto refclk_restore;
+
+	ret = reset_control_bulk_assert(data->num_cfg_resets,
+					host->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:
+	reset_control_bulk_deassert(data->num_cfg_resets,
+				    host->cfg_resets);
+power_resets_restore:
+	reset_control_bulk_deassert(data->num_power_resets,
+				    host->power_resets);
+refclk_restore:
+	clk_prepare_enable(port->refclk);
+	pm_runtime_resume_and_get(dev);
+	return ret;
+}
+
+static int rzg3s_pcie_resume_noirq(struct device *dev)
+{
+	struct rzg3s_pcie_host *host = dev_get_drvdata(dev);
+	const struct rzg3s_pcie_soc_data *data = host->data;
+	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;
+
+	ret = rzg3s_pcie_power_resets_deassert(host);
+	if (ret)
+		goto assert_rst_rsm_b;
+
+	ret = pm_runtime_resume_and_get(dev);
+	if (ret)
+		goto assert_power_resets;
+
+	ret = rzg3s_pcie_host_setup(host, NULL, NULL,
+				    rzg3s_pcie_msi_hw_setup,
+				    rzg3s_pcie_msi_hw_teardown);
+	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:
+	reset_control_bulk_assert(data->num_power_resets,
+				  host->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 const struct dev_pm_ops rzg3s_pcie_pm_ops = {
+	NOIRQ_SYSTEM_SLEEP_PM_OPS(rzg3s_pcie_suspend_noirq,
+				  rzg3s_pcie_resume_noirq)
+};
+
+static const char * const rzg3s_soc_power_resets[] = {
+	"aresetn", "rst_cfg_b", "rst_load_b",
+};
+
+static const char * const rzg3s_soc_cfg_resets[] = {
+	"rst_b", "rst_ps_b", "rst_gp_b", "rst_rsm_b",
+};
+
+static const struct rzg3s_pcie_soc_data rzg3s_soc_data = {
+	.power_resets = rzg3s_soc_power_resets,
+	.num_power_resets = ARRAY_SIZE(rzg3s_soc_power_resets),
+	.cfg_resets = rzg3s_soc_cfg_resets,
+	.num_cfg_resets = ARRAY_SIZE(rzg3s_soc_cfg_resets),
+	.init_phy = rzg3s_soc_pcie_init_phy,
+};
+
+static const struct of_device_id rzg3s_pcie_of_match[] = {
+	{
+		.compatible = "renesas,r9a08g045-pcie",
+		.data = &rzg3s_soc_data,
+	},
+	{}
+};
+
+static struct platform_driver rzg3s_pcie_driver = {
+	.driver = {
+		.name = "rzg3s-pcie-host",
+		.of_match_table = rzg3s_pcie_of_match,
+		.pm = pm_ptr(&rzg3s_pcie_pm_ops),
+		.suppress_bind_attrs = true,
+		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
+	},
+	.probe = rzg3s_pcie_probe,
+};
+builtin_platform_driver(rzg3s_pcie_driver);
-- 
2.43.0


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

* [PATCH v5 3/6] arm64: dts: renesas: r9a08g045: Add PCIe node
  2025-10-07 13:36 [PATCH v5 0/6] PCI: rzg3s-host: Add PCIe driver for Renesas RZ/G3S SoC Claudiu
  2025-10-07 13:36 ` [PATCH v5 1/6] dt-bindings: PCI: renesas,r9a08g045s33-pcie: Add documentation for the PCIe IP on Renesas RZ/G3S Claudiu
  2025-10-07 13:36 ` [PATCH v5 2/6] PCI: rzg3s-host: Add Renesas RZ/G3S SoC host driver Claudiu
@ 2025-10-07 13:36 ` Claudiu
  2025-10-07 13:44   ` Biju Das
  2025-10-19  6:57   ` Manivannan Sadhasivam
  2025-10-07 13:36 ` [PATCH v5 4/6] arm64: dts: renesas: rzg3s-smarc-som: Add PCIe reference clock Claudiu
                   ` (2 subsequent siblings)
  5 siblings, 2 replies; 29+ messages in thread
From: Claudiu @ 2025-10-07 13:36 UTC (permalink / raw)
  To: lpieralisi, kwilczynski, mani, robh, bhelgaas, krzk+dt, conor+dt,
	geert+renesas, magnus.damm, p.zabel
  Cc: claudiu.beznea, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, Claudiu Beznea, Wolfram Sang

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

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

Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---

Changes in v5:
- updated the last part of ranges and dma-ranges
- collected tags

Changes in v4:
- moved the node to r9a08g045.dtsi
- dropped the "s33" from the compatible string
- added port node
- re-ordered properties to have them grouped together

Changes in v3:
- collected tags
- changed the ranges flags

Changes in v2:
- updated the dma-ranges to reflect the SoC capability; added a
  comment about it.
- updated clock-names, interrupt names
- dropped legacy-interrupt-controller node
- added interrupt-controller property
- moved renesas,sysc at the end of the node to comply with
  DT coding style

 arch/arm64/boot/dts/renesas/r9a08g045.dtsi | 66 ++++++++++++++++++++++
 1 file changed, 66 insertions(+)

diff --git a/arch/arm64/boot/dts/renesas/r9a08g045.dtsi b/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
index 16e6ac614417..00b43377877e 100644
--- a/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
+++ b/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
@@ -717,6 +717,72 @@ eth1: ethernet@11c40000 {
 			status = "disabled";
 		};
 
+		pcie: pcie@11e40000 {
+			compatible = "renesas,r9a08g045-pcie";
+			reg = <0 0x11e40000 0 0x10000>;
+			ranges = <0x02000000 0 0x30000000 0 0x30000000 0 0x08000000>;
+			/* Map all possible DRAM ranges (4 GB). */
+			dma-ranges = <0x42000000 0 0x40000000 0 0x40000000 1 0x00000000>;
+			bus-range = <0x0 0xff>;
+			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 = "serr", "serr_cor", "serr_nonfatal",
+					  "serr_fatal", "axi_err", "inta",
+					  "intb", "intc", "intd", "msi",
+					  "link_bandwidth", "pm_pme", "dma",
+					  "pcie_evt", "msg", "all";
+			#interrupt-cells = <1>;
+			interrupt-controller;
+			interrupt-map-mask = <0 0 0 7>;
+			interrupt-map = <0 0 0 1 &pcie 0 0 0 0>, /* INTA */
+					<0 0 0 2 &pcie 0 0 0 1>, /* INTB */
+					<0 0 0 3 &pcie 0 0 0 2>, /* INTC */
+					<0 0 0 4 &pcie 0 0 0 3>; /* INTD */
+			clocks = <&cpg CPG_MOD R9A08G045_PCI_ACLK>,
+				 <&cpg CPG_MOD R9A08G045_PCI_CLKL1PM>;
+			clock-names = "aclk", "pm";
+			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";
+			power-domains = <&cpg>;
+			device_type = "pci";
+			#address-cells = <3>;
+			#size-cells = <2>;
+			max-link-speed = <2>;
+			renesas,sysc = <&sysc>;
+			status = "disabled";
+
+			pcie_port0: pcie@0,0 {
+				reg = <0x0 0x0 0x0 0x0 0x0>;
+				ranges;
+				device_type = "pci";
+				vendor-id = <0x1912>;
+				device-id = <0x0033>;
+				#address-cells = <3>;
+				#size-cells = <2>;
+			};
+		};
+
 		gic: interrupt-controller@12400000 {
 			compatible = "arm,gic-v3";
 			#interrupt-cells = <3>;
-- 
2.43.0


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

* [PATCH v5 4/6] arm64: dts: renesas: rzg3s-smarc-som: Add PCIe reference clock
  2025-10-07 13:36 [PATCH v5 0/6] PCI: rzg3s-host: Add PCIe driver for Renesas RZ/G3S SoC Claudiu
                   ` (2 preceding siblings ...)
  2025-10-07 13:36 ` [PATCH v5 3/6] arm64: dts: renesas: r9a08g045: Add PCIe node Claudiu
@ 2025-10-07 13:36 ` Claudiu
  2025-10-08 12:15   ` Geert Uytterhoeven
  2025-10-07 13:36 ` [PATCH v5 5/6] arm64: dts: renesas: rzg3s-smarc: Enable PCIe Claudiu
  2025-10-07 13:36 ` [PATCH v5 6/6] arm64: defconfig: Enable PCIe for the Renesas RZ/G3S SoC Claudiu
  5 siblings, 1 reply; 29+ messages in thread
From: Claudiu @ 2025-10-07 13:36 UTC (permalink / raw)
  To: lpieralisi, kwilczynski, mani, robh, bhelgaas, krzk+dt, conor+dt,
	geert+renesas, magnus.damm, p.zabel
  Cc: claudiu.beznea, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, Claudiu Beznea

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

Versa3 clock generator available on RZ/G3S SMARC Module provides the
reference clock for SoC PCIe interface. Update the device tree to reflect
this connection.

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

Changes in v5:
- this patch is the result of dropping the updates to dma-ranges for
  secure area and keeping only the remaining bits

 arch/arm64/boot/dts/renesas/rzg3s-smarc-som.dtsi | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm64/boot/dts/renesas/rzg3s-smarc-som.dtsi b/arch/arm64/boot/dts/renesas/rzg3s-smarc-som.dtsi
index 39845faec894..b196f57fd551 100644
--- a/arch/arm64/boot/dts/renesas/rzg3s-smarc-som.dtsi
+++ b/arch/arm64/boot/dts/renesas/rzg3s-smarc-som.dtsi
@@ -172,6 +172,11 @@ a0 80 30 30 9c
 	};
 };
 
+&pcie_port0 {
+	clocks = <&versa3 5>;
+	clock-names = "ref";
+};
+
 #if SW_CONFIG2 == SW_ON
 /* SD0 slot */
 &sdhi0 {
-- 
2.43.0


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

* [PATCH v5 5/6] arm64: dts: renesas: rzg3s-smarc: Enable PCIe
  2025-10-07 13:36 [PATCH v5 0/6] PCI: rzg3s-host: Add PCIe driver for Renesas RZ/G3S SoC Claudiu
                   ` (3 preceding siblings ...)
  2025-10-07 13:36 ` [PATCH v5 4/6] arm64: dts: renesas: rzg3s-smarc-som: Add PCIe reference clock Claudiu
@ 2025-10-07 13:36 ` Claudiu
  2025-10-07 13:36 ` [PATCH v5 6/6] arm64: defconfig: Enable PCIe for the Renesas RZ/G3S SoC Claudiu
  5 siblings, 0 replies; 29+ messages in thread
From: Claudiu @ 2025-10-07 13:36 UTC (permalink / raw)
  To: lpieralisi, kwilczynski, mani, robh, bhelgaas, krzk+dt, conor+dt,
	geert+renesas, magnus.damm, p.zabel
  Cc: claudiu.beznea, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, Claudiu Beznea, Wolfram Sang

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

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

Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---

Changes in v5:
- collected tags

Changes in v4:
- none

Changes in v3:
- collected tags

Changes in v2:
- none

 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] 29+ messages in thread

* [PATCH v5 6/6] arm64: defconfig: Enable PCIe for the Renesas RZ/G3S SoC
  2025-10-07 13:36 [PATCH v5 0/6] PCI: rzg3s-host: Add PCIe driver for Renesas RZ/G3S SoC Claudiu
                   ` (4 preceding siblings ...)
  2025-10-07 13:36 ` [PATCH v5 5/6] arm64: dts: renesas: rzg3s-smarc: Enable PCIe Claudiu
@ 2025-10-07 13:36 ` Claudiu
  2025-10-08 12:12   ` Geert Uytterhoeven
  5 siblings, 1 reply; 29+ messages in thread
From: Claudiu @ 2025-10-07 13:36 UTC (permalink / raw)
  To: lpieralisi, kwilczynski, mani, robh, bhelgaas, krzk+dt, conor+dt,
	geert+renesas, magnus.damm, p.zabel
  Cc: claudiu.beznea, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, 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>
---

Changes in v5:
- dropped Tb tag

Changes in v4:
- made it builtin

Changes in v3:
- collected tags

Changes in v2:
- none

 arch/arm64/configs/defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index e3a2d37bd104..54fd09317edf 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -230,6 +230,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=y
 CONFIG_PCIE_ROCKCHIP_HOST=m
 CONFIG_PCI_XGENE=y
 CONFIG_PCI_IMX6_HOST=y
-- 
2.43.0


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

* RE: [PATCH v5 3/6] arm64: dts: renesas: r9a08g045: Add PCIe node
  2025-10-07 13:36 ` [PATCH v5 3/6] arm64: dts: renesas: r9a08g045: Add PCIe node Claudiu
@ 2025-10-07 13:44   ` Biju Das
  2025-10-10 11:17     ` Claudiu Beznea
  2025-10-19  6:57   ` Manivannan Sadhasivam
  1 sibling, 1 reply; 29+ messages in thread
From: Biju Das @ 2025-10-07 13:44 UTC (permalink / raw)
  To: Claudiu.Beznea, lpieralisi@kernel.org, kwilczynski@kernel.org,
	mani@kernel.org, robh@kernel.org, bhelgaas@google.com,
	krzk+dt@kernel.org, conor+dt@kernel.org, geert+renesas@glider.be,
	magnus.damm, p.zabel@pengutronix.de
  Cc: Claudiu.Beznea, linux-pci@vger.kernel.org,
	linux-renesas-soc@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, Claudiu Beznea, wsa+renesas

Hi Claudiu,

> -----Original Message-----
> From: Claudiu <claudiu.beznea@tuxon.dev>
> Sent: 07 October 2025 14:37
> Subject: [PATCH v5 3/6] arm64: dts: renesas: r9a08g045: Add PCIe node
> 
> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> 
> The RZ/G3S SoC has a variant (R9A08G045S33) which supports PCIe. Add the PCIe node.
> 
> Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> ---
> 
> Changes in v5:
> - updated the last part of ranges and dma-ranges
> - collected tags
> 
> Changes in v4:
> - moved the node to r9a08g045.dtsi
> - dropped the "s33" from the compatible string
> - added port node
> - re-ordered properties to have them grouped together
> 
> Changes in v3:
> - collected tags
> - changed the ranges flags
> 
> Changes in v2:
> - updated the dma-ranges to reflect the SoC capability; added a
>   comment about it.
> - updated clock-names, interrupt names
> - dropped legacy-interrupt-controller node
> - added interrupt-controller property
> - moved renesas,sysc at the end of the node to comply with
>   DT coding style
> 
>  arch/arm64/boot/dts/renesas/r9a08g045.dtsi | 66 ++++++++++++++++++++++
>  1 file changed, 66 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/renesas/r9a08g045.dtsi b/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
> index 16e6ac614417..00b43377877e 100644
> --- a/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
> +++ b/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
> @@ -717,6 +717,72 @@ eth1: ethernet@11c40000 {
>  			status = "disabled";
>  		};
> 
> +		pcie: pcie@11e40000 {
> +			compatible = "renesas,r9a08g045-pcie";
> +			reg = <0 0x11e40000 0 0x10000>;
> +			ranges = <0x02000000 0 0x30000000 0 0x30000000 0 0x08000000>;
> +			/* Map all possible DRAM ranges (4 GB). */
> +			dma-ranges = <0x42000000 0 0x40000000 0 0x40000000 1 0x00000000>;

On RZ/G3E, HW manual mentions PCIe can access up to a 36-bit address space (access to DDR and PCIE0).

Not sure about RZ/G3S?

Cheers,
Biju



> +			bus-range = <0x0 0xff>;
> +			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 = "serr", "serr_cor", "serr_nonfatal",
> +					  "serr_fatal", "axi_err", "inta",
> +					  "intb", "intc", "intd", "msi",
> +					  "link_bandwidth", "pm_pme", "dma",
> +					  "pcie_evt", "msg", "all";
> +			#interrupt-cells = <1>;
> +			interrupt-controller;
> +			interrupt-map-mask = <0 0 0 7>;
> +			interrupt-map = <0 0 0 1 &pcie 0 0 0 0>, /* INTA */
> +					<0 0 0 2 &pcie 0 0 0 1>, /* INTB */
> +					<0 0 0 3 &pcie 0 0 0 2>, /* INTC */
> +					<0 0 0 4 &pcie 0 0 0 3>; /* INTD */
> +			clocks = <&cpg CPG_MOD R9A08G045_PCI_ACLK>,
> +				 <&cpg CPG_MOD R9A08G045_PCI_CLKL1PM>;
> +			clock-names = "aclk", "pm";
> +			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";
> +			power-domains = <&cpg>;
> +			device_type = "pci";
> +			#address-cells = <3>;
> +			#size-cells = <2>;
> +			max-link-speed = <2>;
> +			renesas,sysc = <&sysc>;
> +			status = "disabled";
> +
> +			pcie_port0: pcie@0,0 {
> +				reg = <0x0 0x0 0x0 0x0 0x0>;
> +				ranges;
> +				device_type = "pci";
> +				vendor-id = <0x1912>;
> +				device-id = <0x0033>;
> +				#address-cells = <3>;
> +				#size-cells = <2>;
> +			};
> +		};
> +
>  		gic: interrupt-controller@12400000 {
>  			compatible = "arm,gic-v3";
>  			#interrupt-cells = <3>;
> --
> 2.43.0
> 


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

* Re: [PATCH v5 6/6] arm64: defconfig: Enable PCIe for the Renesas RZ/G3S SoC
  2025-10-07 13:36 ` [PATCH v5 6/6] arm64: defconfig: Enable PCIe for the Renesas RZ/G3S SoC Claudiu
@ 2025-10-08 12:12   ` Geert Uytterhoeven
  0 siblings, 0 replies; 29+ messages in thread
From: Geert Uytterhoeven @ 2025-10-08 12:12 UTC (permalink / raw)
  To: Claudiu
  Cc: lpieralisi, kwilczynski, mani, robh, bhelgaas, krzk+dt, conor+dt,
	magnus.damm, p.zabel, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, Claudiu Beznea

On Tue, 7 Oct 2025 at 15:37, Claudiu <claudiu.beznea@tuxon.dev> wrote:
> 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>

Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

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

* Re: [PATCH v5 4/6] arm64: dts: renesas: rzg3s-smarc-som: Add PCIe reference clock
  2025-10-07 13:36 ` [PATCH v5 4/6] arm64: dts: renesas: rzg3s-smarc-som: Add PCIe reference clock Claudiu
@ 2025-10-08 12:15   ` Geert Uytterhoeven
  0 siblings, 0 replies; 29+ messages in thread
From: Geert Uytterhoeven @ 2025-10-08 12:15 UTC (permalink / raw)
  To: Claudiu
  Cc: lpieralisi, kwilczynski, mani, robh, bhelgaas, krzk+dt, conor+dt,
	magnus.damm, p.zabel, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, Claudiu Beznea

On Tue, 7 Oct 2025 at 15:37, Claudiu <claudiu.beznea@tuxon.dev> wrote:
> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>
> Versa3 clock generator available on RZ/G3S SMARC Module provides the
> reference clock for SoC PCIe interface. Update the device tree to reflect
> this connection.
>
> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> ---
>
> Changes in v5:
> - this patch is the result of dropping the updates to dma-ranges for
>   secure area and keeping only the remaining bits

Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

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

* Re: [PATCH v5 3/6] arm64: dts: renesas: r9a08g045: Add PCIe node
  2025-10-07 13:44   ` Biju Das
@ 2025-10-10 11:17     ` Claudiu Beznea
  2025-10-10 11:36       ` Biju Das
  0 siblings, 1 reply; 29+ messages in thread
From: Claudiu Beznea @ 2025-10-10 11:17 UTC (permalink / raw)
  To: Biju Das, lpieralisi@kernel.org, kwilczynski@kernel.org,
	mani@kernel.org, robh@kernel.org, bhelgaas@google.com,
	krzk+dt@kernel.org, conor+dt@kernel.org, geert+renesas@glider.be,
	magnus.damm, p.zabel@pengutronix.de
  Cc: linux-pci@vger.kernel.org, linux-renesas-soc@vger.kernel.org,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	Claudiu Beznea, wsa+renesas

Hi, Biju,

On 10/7/25 16:44, Biju Das wrote:
> Hi Claudiu,
> 
>> -----Original Message-----
>> From: Claudiu <claudiu.beznea@tuxon.dev>
>> Sent: 07 October 2025 14:37
>> Subject: [PATCH v5 3/6] arm64: dts: renesas: r9a08g045: Add PCIe node
>>
>> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>>
>> The RZ/G3S SoC has a variant (R9A08G045S33) which supports PCIe. Add the PCIe node.
>>
>> Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
>> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
>> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>> ---
>>
>> Changes in v5:
>> - updated the last part of ranges and dma-ranges
>> - collected tags
>>
>> Changes in v4:
>> - moved the node to r9a08g045.dtsi
>> - dropped the "s33" from the compatible string
>> - added port node
>> - re-ordered properties to have them grouped together
>>
>> Changes in v3:
>> - collected tags
>> - changed the ranges flags
>>
>> Changes in v2:
>> - updated the dma-ranges to reflect the SoC capability; added a
>>   comment about it.
>> - updated clock-names, interrupt names
>> - dropped legacy-interrupt-controller node
>> - added interrupt-controller property
>> - moved renesas,sysc at the end of the node to comply with
>>   DT coding style
>>
>>  arch/arm64/boot/dts/renesas/r9a08g045.dtsi | 66 ++++++++++++++++++++++
>>  1 file changed, 66 insertions(+)
>>
>> diff --git a/arch/arm64/boot/dts/renesas/r9a08g045.dtsi b/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
>> index 16e6ac614417..00b43377877e 100644
>> --- a/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
>> +++ b/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
>> @@ -717,6 +717,72 @@ eth1: ethernet@11c40000 {
>>  			status = "disabled";
>>  		};
>>
>> +		pcie: pcie@11e40000 {
>> +			compatible = "renesas,r9a08g045-pcie";
>> +			reg = <0 0x11e40000 0 0x10000>;
>> +			ranges = <0x02000000 0 0x30000000 0 0x30000000 0 0x08000000>;
>> +			/* Map all possible DRAM ranges (4 GB). */
>> +			dma-ranges = <0x42000000 0 0x40000000 0 0x40000000 1 0x00000000>;
> 
> On RZ/G3E, HW manual mentions PCIe can access up to a 36-bit address space (access to DDR and PCIE0).
> 
> Not sure about RZ/G3S?

As of my knowledge/investigation, according to chapter 5.4.2.1 34-Bit
Address Space Access of HW manual, revision 1.10, on RZ/G3S there are some
bus masters that can access up to 34-bit address space, these being
SDHI/eMMC, GEthernet, USB2.0, DMAC. The rest can access up to 32-bit
address space.

Thank you,
Claudiu

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

* RE: [PATCH v5 3/6] arm64: dts: renesas: r9a08g045: Add PCIe node
  2025-10-10 11:17     ` Claudiu Beznea
@ 2025-10-10 11:36       ` Biju Das
  2025-10-11  1:39         ` Biju Das
  0 siblings, 1 reply; 29+ messages in thread
From: Biju Das @ 2025-10-10 11:36 UTC (permalink / raw)
  To: Claudiu.Beznea, lpieralisi@kernel.org, kwilczynski@kernel.org,
	mani@kernel.org, robh@kernel.org, bhelgaas@google.com,
	krzk+dt@kernel.org, conor+dt@kernel.org, geert+renesas@glider.be,
	magnus.damm, p.zabel@pengutronix.de
  Cc: linux-pci@vger.kernel.org, linux-renesas-soc@vger.kernel.org,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	Claudiu Beznea, wsa+renesas

Hi Claudiu,

> -----Original Message-----
> From: Claudiu Beznea <claudiu.beznea@tuxon.dev>
> Sent: 10 October 2025 12:18
,> Subject: Re: [PATCH v5 3/6] arm64: dts: renesas: r9a08g045: Add PCIe node
> 
> Hi, Biju,
> 
> On 10/7/25 16:44, Biju Das wrote:
> > Hi Claudiu,
> >
> >> -----Original Message-----
> >> From: Claudiu <claudiu.beznea@tuxon.dev>
> >> Sent: 07 October 2025 14:37
> >> Subject: [PATCH v5 3/6] arm64: dts: renesas: r9a08g045: Add PCIe node
> >>
> >> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> >>
> >> The RZ/G3S SoC has a variant (R9A08G045S33) which supports PCIe. Add the PCIe node.
> >>
> >> Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
> >> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> >> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> >> ---
> >>
> >> Changes in v5:
> >> - updated the last part of ranges and dma-ranges
> >> - collected tags
> >>
> >> Changes in v4:
> >> - moved the node to r9a08g045.dtsi
> >> - dropped the "s33" from the compatible string
> >> - added port node
> >> - re-ordered properties to have them grouped together
> >>
> >> Changes in v3:
> >> - collected tags
> >> - changed the ranges flags
> >>
> >> Changes in v2:
> >> - updated the dma-ranges to reflect the SoC capability; added a
> >>   comment about it.
> >> - updated clock-names, interrupt names
> >> - dropped legacy-interrupt-controller node
> >> - added interrupt-controller property
> >> - moved renesas,sysc at the end of the node to comply with
> >>   DT coding style
> >>
> >>  arch/arm64/boot/dts/renesas/r9a08g045.dtsi | 66
> >> ++++++++++++++++++++++
> >>  1 file changed, 66 insertions(+)
> >>
> >> diff --git a/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
> >> b/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
> >> index 16e6ac614417..00b43377877e 100644
> >> --- a/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
> >> +++ b/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
> >> @@ -717,6 +717,72 @@ eth1: ethernet@11c40000 {
> >>  			status = "disabled";
> >>  		};
> >>
> >> +		pcie: pcie@11e40000 {
> >> +			compatible = "renesas,r9a08g045-pcie";
> >> +			reg = <0 0x11e40000 0 0x10000>;
> >> +			ranges = <0x02000000 0 0x30000000 0 0x30000000 0 0x08000000>;
> >> +			/* Map all possible DRAM ranges (4 GB). */
> >> +			dma-ranges = <0x42000000 0 0x40000000 0 0x40000000 1 0x00000000>;
> >
> > On RZ/G3E, HW manual mentions PCIe can access up to a 36-bit address space (access to DDR and
> PCIE0).
> >
> > Not sure about RZ/G3S?
> 
> As of my knowledge/investigation, according to chapter 5.4.2.1 34-Bit Address Space Access of HW
> manual, revision 1.10, on RZ/G3S there are some bus masters that can access up to 34-bit address
> space, these being SDHI/eMMC, GEthernet, USB2.0, DMAC. The rest can access up to 32-bit address
> space.

OK, Thanks for the info. 

I am just wondering, later how to handle the Cross-Over 4G memory in
driver as here we have size of 0x1_0000_0000 and start address is 0x4000_0000 which
crosses the first 4G boundary. 

Cheers,
Biju

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

* Re: [PATCH v5 1/6] dt-bindings: PCI: renesas,r9a08g045s33-pcie: Add documentation for the PCIe IP on Renesas RZ/G3S
  2025-10-07 13:36 ` [PATCH v5 1/6] dt-bindings: PCI: renesas,r9a08g045s33-pcie: Add documentation for the PCIe IP on Renesas RZ/G3S Claudiu
@ 2025-10-10 14:29   ` Rob Herring
  2025-10-10 14:32   ` Rob Herring
  1 sibling, 0 replies; 29+ messages in thread
From: Rob Herring @ 2025-10-10 14:29 UTC (permalink / raw)
  To: Claudiu
  Cc: lpieralisi, kwilczynski, mani, bhelgaas, krzk+dt, conor+dt,
	geert+renesas, magnus.damm, p.zabel, linux-pci, linux-renesas-soc,
	devicetree, linux-kernel, Claudiu Beznea

On Tue, Oct 07, 2025 at 04:36:52PM +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.
> 
> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> ---
> 
> Changes in v5:
> - dropped Tb tag
> - style updates to the dma-ranges and ranges properties from
>   examples section
> - re-enabled the node from examples section
> 
> Changes in v4:
> - dropped "s33" string from compatible name
> - added port node documentation; due to this dropped Rob's Rb tag
> - reorderded properties
> - dropped spaces b/w "INT" and "A", "B", "C", "D" in comments
> 
> Changes in v3:
> - collected tags
> - updated the flags of ranges property from example
> 
> Changes in v2:
> - update the interrupt names by dropping "int" and "rc" string; due
>   to this the patch description was adjusted
> - added "interrupt-controller" and made it mandatory
> - s/clkl1pm/pm/g
> - dropped the legacy-interrupt-controller node; with this the gic
>   interrupt controller node was dropped as well as it is not needed
>   anymore
> - updated interrupt-map in example and added interrupt-controller
> - added clock-names as required property as the pm clock is not
>   handled though PM domains; this will allow the driver to have
>   the option to request the pm clock by its name when implementation
>   will be adjusted to used the pm clock
> - adjusted the size of dma-ranges to reflect the usage on
>   SMARC module board
> - moved "renesas,sysc" at the end of the node in example to align
>   with dts coding style
> 
>  .../bindings/pci/renesas,r9a08g045-pcie.yaml  | 239 ++++++++++++++++++
>  1 file changed, 239 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml
> 
> diff --git a/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml b/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml
> new file mode 100644
> index 000000000000..d21d16b4e28d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml
> @@ -0,0 +1,239 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/pci/renesas,r9a08g045-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,r9a08g045-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: serr
> +      - description: ser_cor
> +      - description: serr_nonfatal
> +      - description: serr_fatal
> +      - description: axi_err
> +      - description: inta
> +      - description: intb
> +      - description: intc
> +      - description: intd
> +      - description: msi
> +      - description: link_bandwidth
> +      - description: pm_pme
> +      - description: dma
> +      - description: pcie_evt
> +      - description: msg
> +      - description: all
> +
> +  interrupt-controller: true
> +
> +  clocks:
> +    items:
> +      - description: System clock
> +      - description: PM control clock
> +
> +  clock-names:
> +    items:
> +      - description: aclk
> +      - description: 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

Please say something about what controls are in this.

> +    $ref: /schemas/types.yaml#/definitions/phandle
> +
> +patternProperties:
> +  "^pcie@0,[0-0]$":
> +    type: object
> +    allOf:
> +      - $ref: /schemas/pci/pci-device.yaml#

A PCI-PCI bridge is a PCI device, so drop.

> +      - $ref: /schemas/pci/pci-pci-bridge.yaml#
> +
> +    properties:
> +      reg:
> +        maxItems: 1
> +
> +      vendor-id:
> +        const: 0x1912
> +
> +      device-id:
> +        const: 0x0033
> +
> +      clocks:
> +        items:
> +          - description: Reference clock
> +
> +      clock-names:
> +        items:
> +          - const: ref
> +
> +    required:
> +      - device_type
> +      - vendor-id
> +      - device-id
> +      - clocks
> +      - clock-names
> +
> +    unevaluatedProperties: false
> +
> +required:
> +  - compatible
> +  - reg
> +  - clocks
> +  - clock-names
> +  - resets
> +  - reset-names
> +  - interrupts
> +  - interrupt-names
> +  - interrupt-map
> +  - interrupt-map-mask
> +  - interrupt-controller
> +  - power-domains
> +  - "#address-cells"
> +  - "#size-cells"
> +  - "#interrupt-cells"
> +  - renesas,sysc
> +
> +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>;
> +
> +        pcie@11e40000 {
> +            compatible = "renesas,r9a08g045-pcie";
> +            reg = <0 0x11e40000 0 0x10000>;
> +            ranges = <0x02000000 0 0x30000000 0 0x30000000 0 0x08000000>;
> +            /* Map all possible DRAM ranges (4 GB). */
> +            dma-ranges = <0x42000000 0 0x40000000 0 0x40000000 1 0x00000000>;
> +            bus-range = <0x0 0xff>;
> +            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 = "serr", "serr_cor", "serr_nonfatal",
> +                              "serr_fatal", "axi_err", "inta",
> +                              "intb", "intc", "intd", "msi",
> +                              "link_bandwidth", "pm_pme", "dma",
> +                              "pcie_evt", "msg", "all";
> +            #interrupt-cells = <1>;
> +            interrupt-controller;
> +            interrupt-map-mask = <0 0 0 7>;
> +            interrupt-map = <0 0 0 1 &pcie 0 0 0 0>, /* INTA */
> +                            <0 0 0 2 &pcie 0 0 0 1>, /* INTB */
> +                            <0 0 0 3 &pcie 0 0 0 2>, /* INTC */
> +                            <0 0 0 4 &pcie 0 0 0 3>; /* INTD */
> +            clocks = <&cpg CPG_MOD R9A08G045_PCI_ACLK>,
> +                     <&cpg CPG_MOD R9A08G045_PCI_CLKL1PM>;
> +            clock-names = "aclk", "pm";
> +            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";
> +            power-domains = <&cpg>;
> +            device_type = "pci";
> +            #address-cells = <3>;
> +            #size-cells = <2>;
> +            max-link-speed = <2>;
> +            renesas,sysc = <&sysc>;
> +
> +            pcie_port0: pcie@0,0 {

Drop unused labels.

> +                reg = <0x0 0x0 0x0 0x0 0x0>;
> +                ranges;
> +                clocks = <&versa3 5>;
> +                clock-names = "ref";
> +                device_type = "pci";
> +                vendor-id = <0x1912>;
> +                device-id = <0x0033>;
> +                #address-cells = <3>;
> +                #size-cells = <2>;
> +            };
> +        };
> +    };
> +
> +...
> -- 
> 2.43.0
> 

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

* Re: [PATCH v5 1/6] dt-bindings: PCI: renesas,r9a08g045s33-pcie: Add documentation for the PCIe IP on Renesas RZ/G3S
  2025-10-07 13:36 ` [PATCH v5 1/6] dt-bindings: PCI: renesas,r9a08g045s33-pcie: Add documentation for the PCIe IP on Renesas RZ/G3S Claudiu
  2025-10-10 14:29   ` Rob Herring
@ 2025-10-10 14:32   ` Rob Herring
  1 sibling, 0 replies; 29+ messages in thread
From: Rob Herring @ 2025-10-10 14:32 UTC (permalink / raw)
  To: Claudiu
  Cc: lpieralisi, kwilczynski, mani, bhelgaas, krzk+dt, conor+dt,
	geert+renesas, magnus.damm, p.zabel, linux-pci, linux-renesas-soc,
	devicetree, linux-kernel, Claudiu Beznea

On Tue, Oct 07, 2025 at 04:36:52PM +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.

Also, for the subject, "Add Renesas RZ/G3S" is sufficient. We already 
know it is for PCI and is documentation.

Rob

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

* RE: [PATCH v5 3/6] arm64: dts: renesas: r9a08g045: Add PCIe node
  2025-10-10 11:36       ` Biju Das
@ 2025-10-11  1:39         ` Biju Das
  0 siblings, 0 replies; 29+ messages in thread
From: Biju Das @ 2025-10-11  1:39 UTC (permalink / raw)
  To: Claudiu.Beznea, lpieralisi@kernel.org, kwilczynski@kernel.org,
	mani@kernel.org, robh@kernel.org, bhelgaas@google.com,
	krzk+dt@kernel.org, conor+dt@kernel.org, geert+renesas@glider.be,
	magnus.damm, p.zabel@pengutronix.de
  Cc: linux-pci@vger.kernel.org, linux-renesas-soc@vger.kernel.org,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	Claudiu Beznea, wsa+renesas



> -----Original Message-----
> From: Biju Das
> Sent: 10 October 2025 12:37
> To: 'Claudiu Beznea' <claudiu.beznea@tuxon.dev>; lpieralisi@kernel.org; kwilczynski@kernel.org;
> mani@kernel.org; robh@kernel.org; bhelgaas@google.com; krzk+dt@kernel.org; conor+dt@kernel.org;
> geert+renesas@glider.be; magnus.damm <magnus.damm@gmail.com>; p.zabel@pengutronix.de
> Cc: linux-pci@vger.kernel.org; linux-renesas-soc@vger.kernel.org; devicetree@vger.kernel.org; linux-
> kernel@vger.kernel.org; Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>; wsa+renesas <wsa+renesas@sang-
> engineering.com>
> Subject: RE: [PATCH v5 3/6] arm64: dts: renesas: r9a08g045: Add PCIe node
> 
> Hi Claudiu,
> 
> > -----Original Message-----
> > From: Claudiu Beznea <claudiu.beznea@tuxon.dev>
> > Sent: 10 October 2025 12:18
> ,> Subject: Re: [PATCH v5 3/6] arm64: dts: renesas: r9a08g045: Add PCIe node
> >
> > Hi, Biju,
> >
> > On 10/7/25 16:44, Biju Das wrote:
> > > Hi Claudiu,
> > >
> > >> -----Original Message-----
> > >> From: Claudiu <claudiu.beznea@tuxon.dev>
> > >> Sent: 07 October 2025 14:37
> > >> Subject: [PATCH v5 3/6] arm64: dts: renesas: r9a08g045: Add PCIe
> > >> node
> > >>
> > >> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> > >>
> > >> The RZ/G3S SoC has a variant (R9A08G045S33) which supports PCIe. Add the PCIe node.
> > >>
> > >> Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
> > >> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> > >> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> > >> ---
> > >>
> > >> Changes in v5:
> > >> - updated the last part of ranges and dma-ranges
> > >> - collected tags
> > >>
> > >> Changes in v4:
> > >> - moved the node to r9a08g045.dtsi
> > >> - dropped the "s33" from the compatible string
> > >> - added port node
> > >> - re-ordered properties to have them grouped together
> > >>
> > >> Changes in v3:
> > >> - collected tags
> > >> - changed the ranges flags
> > >>
> > >> Changes in v2:
> > >> - updated the dma-ranges to reflect the SoC capability; added a
> > >>   comment about it.
> > >> - updated clock-names, interrupt names
> > >> - dropped legacy-interrupt-controller node
> > >> - added interrupt-controller property
> > >> - moved renesas,sysc at the end of the node to comply with
> > >>   DT coding style
> > >>
> > >>  arch/arm64/boot/dts/renesas/r9a08g045.dtsi | 66
> > >> ++++++++++++++++++++++
> > >>  1 file changed, 66 insertions(+)
> > >>
> > >> diff --git a/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
> > >> b/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
> > >> index 16e6ac614417..00b43377877e 100644
> > >> --- a/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
> > >> +++ b/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
> > >> @@ -717,6 +717,72 @@ eth1: ethernet@11c40000 {
> > >>  			status = "disabled";
> > >>  		};
> > >>
> > >> +		pcie: pcie@11e40000 {
> > >> +			compatible = "renesas,r9a08g045-pcie";
> > >> +			reg = <0 0x11e40000 0 0x10000>;
> > >> +			ranges = <0x02000000 0 0x30000000 0 0x30000000 0 0x08000000>;
> > >> +			/* Map all possible DRAM ranges (4 GB). */
> > >> +			dma-ranges = <0x42000000 0 0x40000000 0 0x40000000 1
> > >> +0x00000000>;
> > >
> > > On RZ/G3E, HW manual mentions PCIe can access up to a 36-bit address
> > > space (access to DDR and
> > PCIE0).
> > >
> > > Not sure about RZ/G3S?
> >
> > As of my knowledge/investigation, according to chapter 5.4.2.1 34-Bit
> > Address Space Access of HW manual, revision 1.10, on RZ/G3S there are
> > some bus masters that can access up to 34-bit address space, these
> > being SDHI/eMMC, GEthernet, USB2.0, DMAC. The rest can access up to 32-bit address space.
> 
> OK, Thanks for the info.
> 
> I am just wondering, later how to handle the Cross-Over 4G memory in driver as here we have size of
> 0x1_0000_0000 and start address is 0x4000_0000 which crosses the first 4G boundary.

I got confused with comparing RZ/G3E hardware manual. Looks like PCIe bus master does not
have any restriction.

Cheers,
Biju


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

* Re: [PATCH v5 2/6] PCI: rzg3s-host: Add Renesas RZ/G3S SoC host driver
  2025-10-07 13:36 ` [PATCH v5 2/6] PCI: rzg3s-host: Add Renesas RZ/G3S SoC host driver Claudiu
@ 2025-10-19  6:52   ` Manivannan Sadhasivam
  2025-10-22 19:49   ` Bjorn Helgaas
  2025-10-23  8:00   ` Geert Uytterhoeven
  2 siblings, 0 replies; 29+ messages in thread
From: Manivannan Sadhasivam @ 2025-10-19  6:52 UTC (permalink / raw)
  To: Claudiu
  Cc: lpieralisi, kwilczynski, robh, bhelgaas, krzk+dt, conor+dt,
	geert+renesas, magnus.damm, p.zabel, linux-pci, linux-renesas-soc,
	devicetree, linux-kernel, Claudiu Beznea, Wolfram Sang

On Tue, Oct 07, 2025 at 04:36:53PM +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.
> 
> Hardware manual can be downloaded from the address in the "Link" section.
> The following steps should be followed to access the manual:
> 1/ Click the "User Manual" button
> 2/ Click "Confirm"; this will start downloading an archive
> 3/ Open the downloaded archive
> 4/ Navigate to r01uh1014ej*-rzg3s-users-manual-hardware -> Deliverables
> 5/ Open the file r01uh1014ej*-rzg3s.pdf
> 
> Link: https://www.renesas.com/en/products/rz-g3s?queryID=695cc067c2d89e3f271d43656ede4d12
> Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>

Driver looks good to me, but I'm waiting for the binding patch to be fixed (as
per Rob's comment).

- Mani

> ---
> 
> Changes in v5:
> - replaced devm action or resets with gotos
> - s/writeb/writeb_relaxed/g
> - s/readw/readw_relaxed/g
> - s/readl/readl_relaxed/g
> - s/writel/writel_relaxed/g
> - dropped rzg3s_pcie_child_ops::map_bus, rename rzg3s_pcie_child_map_bus()
>   (that was used to instantiate rzg3s_pcie_child_ops::map_bus) to
>   rzg3s_pcie_child_prepare_bus() and used it directly in
>   rzg3s_pcie_child_read_conf() and rzg3s_pcie_child_write_conf()
> - convert the 3rd argument of readl_poll_timeout() to microseconds
> - set rzg3s_pcie_driver.driver.probe_type = PROBE_PREFER_ASYNCHRONOUS
> 
> Changes in v4:
> - updated patch title
> - drop the option to build the driver as module
> - make it dependent on IRQ_MSI_LIB and adjust the code to use this library
> - added code to parse and set PCIe port; moved device ID, vendor ID
>   setup under port configuration; added reference clock setup to the port
>   (which was missed previously); moved the PHY setup on port specific
>   setup function
> - used of_pci_get_max_link_speed() to get the maximum available link speed
>   from device tree; with this updated the logic that set the max link speed
> - simplified the logic in rzg3s_pcie_child_issue_request()
> - set the type of access in rzg3s_pcie_child_write_conf/rzg3s_pcie_child_read_conf()
> - added comment on rzg3s_pcie_root_write() about the used lock for serialization
> - added RZG3S_PCI_MSIRCVWMSKL_MASK mask and use it properly to set the
>   MSI size
> - fixed typos, dropped "link up" log on probe
> - updated the error message on inbound/outbound window failure setup along
>   with the returning code
> - used unsigned int type for the counter in for loops from
>   rzg3s_soc_pcie_init_phy()
> - dropped unnecessary comments
> - moved msleep(PCIE_RESET_CONFIG_WAIT_MS) in rzg3s_pcie_host_setup() to
>   allow resume path to also benefit of it
> - drop the power reset de-assert from rzg3s_pcie_resets_prepare(), rename
>   rzg3s_pcie_resets_prepare() into rzg3s_pcie_resets_prepare_and_get()
>   to reflect that rzg3s_pcie_resets_prepare_and_get() only allocate memory
>   for resets and get them; the power resets are now de-asserted outside of
>   rzg3s_pcie_resets_prepare_and_get()
> - add a comment for pm_runtime_resume_and_get() to reflect it is used
>   to power the clock domain the controller belongs to
> 
> Changes in v3:
> - updated patch description with link to the hardware manual and steps
>   to access it
> - included <linux/bitfields.h> to solve compilation errors
> - used devm_mutex_init()
> - used SZ_4K instead of 4096
> - dropped PCIe register defines and used the ones from
>   include/uapi/linux/pci_regs.h; added RZG3S_PCI_CFG_PCIEC as the starting
>   offset of the capabilities to be used before the host bridge is
>   registered
> - added blank lines around registers and bitfields defines
> - kept the defines for bitfies in order (from MSB to LSB)
> - dropped timeout defines (except RZG3S_REQ_ISSUE_TIMEOUT_US) and
>   used the ones from ../pci.h
> - dropped rzg3s_pcie_link_speed and used defines from 
>   include/uapi/linux/pci_regs.h
> - in rzg3s_pcie_child_write() call directly rzg3s_pcie_child_write_conf()
>   if size is 4 and print the warning message on in the other cases
> - return NULL in rzg3s_pcie_child_map_bus() and added a comment about it
> - in rzg3s_pcie_root_write() and rzg3s_soc_pcie_init_phy() added a comment
>   about the setting done on RZG3S_PCI_PERM register
> - register rzg3s_pcie_msi_free_domains(), rzg3s_pcie_msi_teardown(),
>   rzg3s_pcie_intx_teardown() as devm action or reset functions
> - used irq_domain_create_linear() for intx domain
> - added the rzg3s_pcie_power_resets_deassert() helper to de-assert the
>   power domain and wait before doing it
> - wait PCIE_RESET_CONFIG_WAIT_MS before registering the host
> - made rzg3s_soc_power_resets[] and rzg3s_soc_cfg_resets[] static
> - added suppress_bind_attrs = true
> - collected tags
> 
> Changes in v2:
> - dropped the references to other RZ SoCs from patch description
> - dropped the dot at the end of single line comments that are not a
>   sentence
> - as a result of v2 rework removed unused macros and definitions
>   (e.g. RZG3S_PCI_REQISS_TR_TP1_RD, RZG3S_PCI_REQISS_TR_TP1_WR,
>   enum rzg3s_pcie_cfg_access_type)
> - dropped driver specific defines that are for generic PCI
>   register offsets and used the generic ones
> - updated the value of RZG3S_PCI_MSI_INT_NR as on RZ/G3S there
>   are no more than 32 MSIs (v1 value was due to mistake in the
>   HW manual)
> - added timeout macros to be used by read_poll_timeout() specific
>   functions
> - re-worked the reset handling part by using reset subsystem specific
>   functions only; with this the struct rzg3s_pcie_soc_data was
>   added; reference to PHY initialization function was added to this
>   structure as well
> - dropped devres_group_id as the issue it tried to address will
>   now be fixed in platform bus code (v2 posted [2])
> - use 80 columns alignment
> - updated function name in the idea of using names similar to
>   what is used in other drivers
> - added rzg3s_pcie_root_ops and rzg3s_pcie_child_ops and populate
>   bridge->ops, bridge->child_ops with it; from probe:
> +	bridge->ops = &rzg3s_pcie_root_ops;
> +	bridge->child_ops = &rzg3s_pcie_child_ops;
> - print a warning for 32 bit accesses (based on the value of
>   bus->unsafe_warn as done in the common code)
> - dropped dev_dbg() in read/write functions
> - added HW manual revision identifier in comments that points to the
>   statements from manual
> - reworked the rzg3s_pcie_intx_setup() as the legacy interrupt DT
>   node is not used anymore
> - in rzg3s_pcie_config_init() do not hardcode anymore the
>   primary bus, secondary bus, subordinate bus but get this information
>   from device tree and update HW registers accordingly
> - dropped rzg3s_pcie_remove() and added rzg3s_pcie_host_remove_action()
>   to be used as a devm action or reset function
> - s/rzg3s_pcie_suspend/rzg3s_pcie_suspend_noirq,
>   s/rzg3s_pcie_resume/rzg3s_pcie_resume_noirq
> - dropped DEFINE_NOIRQ_DEV_PM_OPS()
> - updated driver name (rzg3s-pcie-host) to reflect it is for RZ/G3S 
> 
> [2] https://lore.kernel.org/all/20250526122054.65532-2-claudiu.beznea.uj@bp.renesas.com
> 
>  MAINTAINERS                              |    8 +
>  drivers/pci/controller/Kconfig           |    8 +
>  drivers/pci/controller/Makefile          |    1 +
>  drivers/pci/controller/pcie-rzg3s-host.c | 1776 ++++++++++++++++++++++
>  4 files changed, 1793 insertions(+)
>  create mode 100644 drivers/pci/controller/pcie-rzg3s-host.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 156fa8eefa69..4e9734cf96a7 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -19987,6 +19987,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,r9a08g045-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 41748d083b93..8732bafbbcd7 100644
> --- a/drivers/pci/controller/Kconfig
> +++ b/drivers/pci/controller/Kconfig
> @@ -266,6 +266,14 @@ 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
> +	bool "Renesas RZ/G3S PCIe host controller"
> +	depends on ARCH_RENESAS || COMPILE_TEST
> +	select MFD_SYSCON
> +	select IRQ_MSI_LIB
> +	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..196cf3fc7fd6
> --- /dev/null
> +++ b/drivers/pci/controller/pcie-rzg3s-host.c
> @@ -0,0 +1,1776 @@
> +// 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/bitfield.h>
> +#include <linux/bitmap.h>
> +#include <linux/bitops.h>
> +#include <linux/cleanup.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/iopoll.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/irqchip/irq-msi-lib.h>
> +#include <linux/irqdomain.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/mutex.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/sizes.h>
> +#include <linux/slab.h>
> +#include <linux/units.h>
> +
> +#include "../pci.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_MOR_STATUS		GENMASK(18, 16)
> +#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_REQ_ISSUE		BIT(0)
> +
> +#define RZG3S_PCI_MSIRCVWADRL			0x100
> +#define RZG3S_PCI_MSIRCVWADRL_MSG_DATA_ENA	BIT(1)
> +#define RZG3S_PCI_MSIRCVWADRL_ENA		BIT(0)
> +
> +#define RZG3S_PCI_MSIRCVWADRU			0x104
> +
> +#define RZG3S_PCI_MSIRCVWMSKL			0x108
> +#define RZG3S_PCI_MSIRCVWMSKL_MASK		GENMASK(31, 2)
> +
> +#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_RX_DLLP_PM_ENTER	BIT(12)
> +#define RZG3S_PCI_PEIS0_DL_UPDOWN		BIT(9)
> +
> +#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_LTSSM_STATE		GENMASK(14, 10)
> +#define RZG3S_PCI_PCSTAT1_DL_DOWN_STS		BIT(0)
> +
> +#define RZG3S_PCI_PCCTRL2			0x410
> +#define RZG3S_PCI_PCCTRL2_LS_CHG		GENMASK(9, 8)
> +#define RZG3S_PCI_PCCTRL2_LS_CHG_REQ		BIT(0)
> +
> +#define RZG3S_PCI_PCSTAT2			0x414
> +#define RZG3S_PCI_PCSTAT2_LS_CHG_DONE		BIT(28)
> +#define RZG3S_PCI_PCSTAT2_STATE_RX_DETECT	GENMASK(15, 8)
> +#define RZG3S_PCI_PCSTAT2_SDRIRE		GENMASK(7, 1)
> +
> +#define RZG3S_PCI_PERM				0x300
> +#define RZG3S_PCI_PERM_CFG_HWINIT_EN		BIT(2)
> +#define RZG3S_PCI_PERM_PIPE_PHY_REG_EN		BIT(1)
> +
> +#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_BARMSK00L			0xa0
> +#define RZG3S_PCI_CFG_BARMSK00U			0xa4
> +
> +#define RZG3S_PCI_CFG_PCIEC			0x60
> +
> +/* 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
> +
> +/* Timeouts experimentally determined. */
> +#define RZG3S_REQ_ISSUE_TIMEOUT_US		2500
> +
> +/**
> + * 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
> + * @irq: MSI interrupt
> + */
> +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;
> +	int irq;
> +};
> +
> +struct rzg3s_pcie_host;
> +
> +/**
> + * struct rzg3s_pcie_soc_data - SoC specific data
> + * @init_phy: PHY initialization function
> + * @power_resets: array with the resets that need to be de-asserted after
> + *                power-on
> + * @cfg_resets: array with the resets that need to be de-asserted after
> + *              configuration
> + * @num_power_resets: number of power resets
> + * @num_cfg_resets: number of configuration resets
> + */
> +struct rzg3s_pcie_soc_data {
> +	int (*init_phy)(struct rzg3s_pcie_host *host);
> +	const char * const *power_resets;
> +	const char * const *cfg_resets;
> +	u8 num_power_resets;
> +	u8 num_cfg_resets;
> +};
> +
> +/**
> + * struct rzg3s_pcie_port - RZ/G3S PCIe Root port data structure
> + * @refclk: PCIe reference clock
> + * @vendor_id: Vendor ID
> + * @device_id: Device ID
> + */
> +struct rzg3s_pcie_port {
> +	struct clk *refclk;
> +	u32 vendor_id;
> +	u32 device_id;
> +};
> +
> +/**
> + * 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
> + * @sysc: SYSC regmap
> + * @intx_domain: INTx IRQ domain
> + * @data: SoC specific data
> + * @msi: MSI data structure
> + * @hw_lock: lock for access to the HW resources
> + * @intx_irqs: INTx interrupts
> + * @max_link_speed: maximum supported link speed
> + * @port: PCIe Root port
> + */
> +struct rzg3s_pcie_host {
> +	void __iomem *axi;
> +	void __iomem *pcie;
> +	struct device *dev;
> +	struct reset_control_bulk_data *power_resets;
> +	struct reset_control_bulk_data *cfg_resets;
> +	struct regmap *sysc;
> +	struct irq_domain *intx_domain;
> +	const struct rzg3s_pcie_soc_data *data;
> +	struct rzg3s_pcie_msi msi;
> +	raw_spinlock_t hw_lock;
> +	int intx_irqs[PCI_NUM_INTX];
> +	int max_link_speed;
> +	struct rzg3s_pcie_port port;
> +};
> +
> +#define rzg3s_msi_to_host(_msi)	container_of(_msi, struct rzg3s_pcie_host, msi)
> +
> +static void rzg3s_pcie_update_bits(void __iomem *base, u32 offset, u32 mask,
> +				   u32 val)
> +{
> +	u32 tmp;
> +
> +	tmp = readl_relaxed(base + offset);
> +	tmp &= ~mask;
> +	tmp |= val & mask;
> +	writel_relaxed(tmp, base + offset);
> +}
> +
> +static int rzg3s_pcie_child_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, RZG3S_REQ_ISSUE_TIMEOUT_US);
> +
> +	if (val & RZG3S_PCI_REQISS_MOR_STATUS)
> +		return -EIO;
> +
> +	return ret;
> +}
> +
> +static void rzg3s_pcie_child_prepare_bus(struct pci_bus *bus,
> +					 unsigned int devfn,
> +					 int where)
> +{
> +	struct rzg3s_pcie_host *host = bus->sysdata;
> +	unsigned int dev, func, reg;
> +
> +	dev = PCI_SLOT(devfn);
> +	func = PCI_FUNC(devfn);
> +	reg = where & ~0x3;
> +
> +	/* Set the destination */
> +	writel_relaxed(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_relaxed(RZG3S_PCI_REQBE_BYTE_EN, host->axi + RZG3S_PCI_REQBE);
> +}
> +
> +static int rzg3s_pcie_child_read_conf(struct rzg3s_pcie_host *host,
> +				      struct pci_bus *bus,
> +				      unsigned int devfn, int where,
> +				      u32 *data)
> +{
> +	bool type0 = pci_is_root_bus(bus->parent) ? true : false;
> +	int ret;
> +
> +	rzg3s_pcie_child_prepare_bus(bus, devfn, where);
> +
> +	/* Set the type of request */
> +	writel_relaxed(type0 ? RZG3S_PCI_REQISS_TR_TP0_RD :
> +			       RZG3S_PCI_REQISS_TR_TP1_RD,
> +		       host->axi + RZG3S_PCI_REQISS);
> +
> +	/* Issue the request and wait to finish */
> +	ret = rzg3s_pcie_child_issue_request(host);
> +	if (ret)
> +		return PCIBIOS_SET_FAILED;
> +
> +	/* Read the data */
> +	*data = readl_relaxed(host->axi + RZG3S_PCI_REQRCVDAT);
> +
> +	return PCIBIOS_SUCCESSFUL;
> +}
> +
> +/* Serialization is provided by 'pci_lock' in drivers/pci/access.c */
> +static int rzg3s_pcie_child_read(struct pci_bus *bus, unsigned int devfn,
> +				 int where, int size, u32 *val)
> +{
> +	struct rzg3s_pcie_host *host = bus->sysdata;
> +	int ret;
> +
> +	ret = rzg3s_pcie_child_read_conf(host, bus, devfn, where, val);
> +	if (ret != PCIBIOS_SUCCESSFUL)
> +		return ret;
> +
> +	if (size <= 2)
> +		*val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1);
> +
> +	return PCIBIOS_SUCCESSFUL;
> +}
> +
> +static int rzg3s_pcie_child_write_conf(struct rzg3s_pcie_host *host,
> +				       struct pci_bus *bus,
> +				       unsigned int devfn, int where,
> +				       u32 data)
> +{
> +	bool type0 = pci_is_root_bus(bus->parent) ? true : false;
> +	int ret;
> +
> +	rzg3s_pcie_child_prepare_bus(bus, devfn, where);
> +
> +	/* Set the write data */
> +	writel_relaxed(0, host->axi + RZG3S_PCI_REQDATA(0));
> +	writel_relaxed(0, host->axi + RZG3S_PCI_REQDATA(1));
> +	writel_relaxed(data, host->axi + RZG3S_PCI_REQDATA(2));
> +
> +	/* Set the type of request */
> +	writel_relaxed(type0 ? RZG3S_PCI_REQISS_TR_TP0_WR :
> +			       RZG3S_PCI_REQISS_TR_TP1_WR,
> +		       host->axi + RZG3S_PCI_REQISS);
> +
> +	/* Issue the request and wait to finish */
> +	ret = rzg3s_pcie_child_issue_request(host);
> +	if (ret)
> +		return PCIBIOS_SET_FAILED;
> +
> +	return PCIBIOS_SUCCESSFUL;
> +}
> +
> +/* Serialization is provided by 'pci_lock' in drivers/pci/access.c */
> +static int rzg3s_pcie_child_write(struct pci_bus *bus, unsigned int devfn,
> +				  int where, int size, u32 val)
> +{
> +	struct rzg3s_pcie_host *host = bus->sysdata;
> +	u32 data, shift;
> +	int ret;
> +
> +	if (size == 4)
> +		return rzg3s_pcie_child_write_conf(host, bus, devfn, where, val);
> +
> +	/*
> +	 * Controller does 32 bit accesses. To do byte accesses software need
> +	 * to do read/modify/write. This may have potential side effects. For
> +	 * example, software may perform a 16-bit write. If the hardware only
> +	 * supports 32-bit accesses, we must do a 32-bit read, merge in the 16
> +	 * bits we intend to write, followed by a 32-bit write. If the 16 bits
> +	 * we *don't* intend to write happen to have any RW1C
> +	 * (write-one-to-clear) bits set, we just inadvertently cleared
> +	 * something we shouldn't have.
> +	 */
> +	if (!bus->unsafe_warn) {
> +		dev_warn(&bus->dev, "%d-byte config write to %04x:%02x:%02x.%d offset %#x may corrupt adjacent RW1C bits\n",
> +			 size, pci_domain_nr(bus), bus->number,
> +			 PCI_SLOT(devfn), PCI_FUNC(devfn), where);
> +		bus->unsafe_warn = 1;
> +	}
> +
> +	ret = rzg3s_pcie_child_read_conf(host, bus, devfn, where, &data);
> +	if (ret != PCIBIOS_SUCCESSFUL)
> +		return ret;
> +
> +	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_child_write_conf(host, bus, devfn, where, data);
> +}
> +
> +static struct pci_ops rzg3s_pcie_child_ops = {
> +	.read		= rzg3s_pcie_child_read,
> +	.write		= rzg3s_pcie_child_write,
> +};
> +
> +static void __iomem *rzg3s_pcie_root_map_bus(struct pci_bus *bus,
> +					     unsigned int devfn,
> +					     int where)
> +{
> +	struct rzg3s_pcie_host *host = bus->sysdata;
> +
> +	if (devfn)
> +		return NULL;
> +
> +	return host->pcie + where;
> +}
> +
> +/* Serialization is provided by 'pci_lock' in drivers/pci/access.c */
> +static int rzg3s_pcie_root_write(struct pci_bus *bus, unsigned int devfn,
> +				 int where, int size, u32 val)
> +{
> +	struct rzg3s_pcie_host *host = bus->sysdata;
> +
> +	/* Enable access control to the CFGU */
> +	writel_relaxed(RZG3S_PCI_PERM_CFG_HWINIT_EN,
> +		       host->axi + RZG3S_PCI_PERM);
> +
> +	pci_generic_config_write(bus, devfn, where, size, val);
> +
> +	/* Disable access control to the CFGU */
> +	writel_relaxed(0, host->axi + RZG3S_PCI_PERM);
> +
> +	return PCIBIOS_SUCCESSFUL;
> +}
> +
> +static struct pci_ops rzg3s_pcie_root_ops = {
> +	.read		= pci_generic_config_read,
> +	.write		= rzg3s_pcie_root_write,
> +	.map_bus	= rzg3s_pcie_root_map_bus,
> +};
> +
> +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)
> +{
> +	u8 regs = RZG3S_PCI_MSI_INT_NR / RZG3S_PCI_MSI_INT_PER_REG;
> +	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_relaxed(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 < regs; reg_id++) {
> +		status = readl_relaxed(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, 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_relaxed(BIT(reg_bit),
> +				       host->axi + RZG3S_PCI_MSIRS(reg_id));
> +		}
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +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_relaxed(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_irq_compose_msi_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 drop_mask = RZG3S_PCI_MSIRCVWADRL_ENA |
> +			RZG3S_PCI_MSIRCVWADRL_MSG_DATA_ENA;
> +	u32 lo, hi;
> +
> +	/*
> +	 * Enable and msg data enable bits are part of the address lo. Drop
> +	 * them.
> +	 */
> +	lo = readl_relaxed(host->axi + RZG3S_PCI_MSIRCVWADRL) & ~drop_mask;
> +	hi = readl_relaxed(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			= "rzg3s-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_irq_compose_msi_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,
> +};
> +
> +#define RZG3S_PCIE_MSI_FLAGS_REQUIRED	(MSI_FLAG_USE_DEF_DOM_OPS	| \
> +					 MSI_FLAG_USE_DEF_CHIP_OPS	| \
> +					 MSI_FLAG_NO_AFFINITY		| \
> +					 MSI_FLAG_PCI_MSI_MASK_PARENT)
> +
> +#define RZG3S_PCIE_MSI_FLAGS_SUPPORTED	(MSI_FLAG_MULTI_PCI_MSI		| \
> +					 MSI_GENERIC_FLAGS_MASK)
> +
> +static const struct msi_parent_ops rzg3s_pcie_msi_parent_ops = {
> +	.required_flags		= RZG3S_PCIE_MSI_FLAGS_REQUIRED,
> +	.supported_flags	= RZG3S_PCIE_MSI_FLAGS_SUPPORTED,
> +	.bus_select_token	= DOMAIN_BUS_PCI_MSI,
> +	.chip_flags		= MSI_CHIP_FLAG_SET_ACK,
> +	.prefix			= "RZG3S-",
> +	.init_dev_msi_info	= msi_lib_init_dev_msi_info,
> +};
> +
> +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 irq_domain_info info = {
> +		.fwnode		= dev_fwnode(dev),
> +		.ops		= &rzg3s_pcie_msi_domain_ops,
> +		.size		= RZG3S_PCI_MSI_INT_NR,
> +		.host_data	= msi,
> +	};
> +
> +	msi->domain = msi_create_parent_irq_domain(&info,
> +						   &rzg3s_pcie_msi_parent_ops);
> +	if (!msi->domain)
> +		return dev_err_probe(dev, -ENOMEM,
> +				     "failed to create IRQ domain\n");
> +
> +	return 0;
> +}
> +
> +static int rzg3s_pcie_msi_hw_setup(struct rzg3s_pcie_host *host)
> +{
> +	u8 regs = RZG3S_PCI_MSI_INT_NR / RZG3S_PCI_MSI_INT_PER_REG;
> +	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_relaxed(FIELD_PREP(RZG3S_PCI_MSIRCVWMSKL_MASK,
> +				  RZG3S_PCI_MSI_INT_NR - 1),
> +		       host->axi + RZG3S_PCI_MSIRCVWMSKL);
> +
> +	/* Set MSI window address and enable MSI window */
> +	writel_relaxed(upper_32_bits(msi->window_base),
> +		       host->axi + RZG3S_PCI_MSIRCVWADRU);
> +	writel_relaxed(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 < regs; reg_id++) {
> +		writel_relaxed(RZG3S_PCI_MSIRE_ENA,
> +			       host->axi + RZG3S_PCI_MSIRE(reg_id));
> +	}
> +
> +	/* Enable message receive interrupts */
> +	writel_relaxed(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 (Rev.1.10, section 34.4.5.2 Setting
> +	 * the MSI Window) the MSI window needs to fall within one of the
> +	 * enabled AXI windows. Find an enabled 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_relaxed(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_relaxed(host->axi + RZG3S_PCI_AWBASEU(id));
> +		base = baseu << 32 | basel;
> +
> +		maskl = readl_relaxed(host->axi + RZG3S_PCI_AWMASKL(id));
> +		masku = readl_relaxed(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 void rzg3s_pcie_msi_hw_teardown(struct rzg3s_pcie_host *host)
> +{
> +	u8 regs = RZG3S_PCI_MSI_INT_NR / RZG3S_PCI_MSI_INT_PER_REG;
> +
> +	/* 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 < regs; reg_id++)
> +		writel_relaxed(0, host->axi + RZG3S_PCI_MSIRE(reg_id));
> +
> +	/* Disable MSI window */
> +	writel_relaxed(0, host->axi + RZG3S_PCI_MSIRCVWADRL);
> +}
> +
> +static void rzg3s_pcie_msi_teardown(struct rzg3s_pcie_host *host)
> +{
> +	size_t size = RZG3S_PCI_MSI_INT_NR * sizeof(u32);
> +	struct rzg3s_pcie_msi *msi = &host->msi;
> +
> +	rzg3s_pcie_msi_hw_teardown(host);
> +
> +	free_irq(msi->irq, host);
> +	irq_domain_remove(msi->domain);
> +
> +	/* Free unused memory */
> +	dma_unmap_single(host->dev, msi->dma_addr, size * 2, DMA_BIDIRECTIONAL);
> +	free_pages(msi->pages, 0);
> +}
> +
> +static int rzg3s_pcie_msi_enable(struct rzg3s_pcie_host *host)
> +{
> +	struct platform_device *pdev = to_platform_device(host->dev);
> +	struct rzg3s_pcie_msi *msi = &host->msi;
> +	struct device *dev = host->dev;
> +	const char *devname;
> +	int irq, ret;
> +
> +	ret = devm_mutex_init(dev, &msi->map_lock);
> +	if (ret)
> +		return ret;
> +
> +	msi->irq = platform_get_irq_byname(pdev, "msi");
> +	if (msi->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 = request_irq(msi->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_irq;
> +	}
> +
> +	return 0;
> +
> +free_irq:
> +	free_irq(msi->irq, host);
> +free_domains:
> +	irq_domain_remove(msi->domain);
> +	return ret;
> +}
> +
> +static void rzg3s_pcie_intx_irq_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_irq_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_irq_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_irq_ack,
> +	.irq_mask = rzg3s_pcie_intx_irq_mask,
> +	.irq_unmask = rzg3s_pcie_intx_irq_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 void rzg3s_pcie_intx_teardown(struct rzg3s_pcie_host *host)
> +{
> +	irq_domain_remove(host->intx_domain);
> +}
> +
> +static int rzg3s_pcie_intx_setup(struct rzg3s_pcie_host *host)
> +{
> +	struct device *dev = host->dev;
> +
> +	for (int i = 0; i < PCI_NUM_INTX; i++) {
> +		struct platform_device *pdev = to_platform_device(dev);
> +		char irq_name[5] = {0};
> +		int irq;
> +
> +		scnprintf(irq_name, ARRAY_SIZE(irq_name), "int%c", 'a' + i);
> +
> +		irq = platform_get_irq_byname(pdev, irq_name);
> +		if (irq < 0)
> +			return dev_err_probe(dev, -EINVAL,
> +					     "Failed to parse and map INT%c IRQ\n",
> +					     'A' + i);
> +
> +		host->intx_irqs[i] = irq;
> +		irq_set_chained_handler_and_data(irq,
> +						 rzg3s_pcie_intx_irq_handler,
> +						 host);
> +	}
> +
> +	host->intx_domain = irq_domain_create_linear(of_fwnode_handle(dev->of_node),
> +						     PCI_NUM_INTX,
> +						     &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 int rzg3s_pcie_set_max_link_speed(struct rzg3s_pcie_host *host)
> +{
> +	u32 remote_supported_link_speeds, max_supported_link_speeds;
> +	u32 cs2, tmp, pcie_cap = RZG3S_PCI_CFG_PCIEC;
> +	u32 cur_link_speed, link_speed;
> +	u8 ltssm_state_l0 = 0xc;
> +	u16 lcs;
> +	int ret;
> +
> +	/*
> +	 * According to the RZ/G3S HW manual (Rev.1.10, section 34.6.3 Caution
> +	 * when Changing the Speed Spontaneously) link speed change can be done
> +	 * only when the LTSSM is in L0.
> +	 */
> +	ret = readl_poll_timeout(host->axi + RZG3S_PCI_PCSTAT1, tmp,
> +				 FIELD_GET(RZG3S_PCI_PCSTAT1_LTSSM_STATE, tmp) == ltssm_state_l0,
> +				 PCIE_LINK_WAIT_SLEEP_MS * MILLI,
> +				 PCIE_LINK_WAIT_SLEEP_MS * MILLI *
> +				 PCIE_LINK_WAIT_MAX_RETRIES);
> +	if (ret)
> +		return ret;
> +
> +	lcs = readw_relaxed(host->pcie + pcie_cap + PCI_EXP_LNKSTA);
> +	cs2 = readl_relaxed(host->axi + RZG3S_PCI_PCSTAT2);
> +
> +	switch (pcie_link_speed[host->max_link_speed]) {
> +	case PCIE_SPEED_5_0GT:
> +		max_supported_link_speeds = GENMASK(PCI_EXP_LNKSTA_CLS_5_0GB - 1, 0);
> +		link_speed = PCI_EXP_LNKCTL2_TLS_5_0GT;
> +		break;
> +	default:
> +		/* Should not happen */
> +		return -EINVAL;
> +	}
> +
> +	cur_link_speed = FIELD_GET(PCI_EXP_LNKSTA_CLS, lcs);
> +	remote_supported_link_speeds = FIELD_GET(RZG3S_PCI_PCSTAT2_SDRIRE, cs2);
> +	/* Drop reserved bits */
> +	remote_supported_link_speeds &= max_supported_link_speeds;
> +
> +	/*
> +	 * Return if max link speed is already set or the connected device
> +	 * doesn't support it.
> +	 */
> +	if (cur_link_speed == host->max_link_speed ||
> +	    remote_supported_link_speeds != max_supported_link_speeds)
> +		return 0;
> +
> +	/* Set target Link speed */
> +	rzg3s_pcie_update_bits(host->pcie, pcie_cap + PCI_EXP_LNKCTL2,
> +			       PCI_EXP_LNKCTL2_TLS,
> +			       FIELD_PREP(PCI_EXP_LNKCTL2_TLS, link_speed));
> +
> +	/* 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,
> +					  link_speed - 1));
> +
> +	ret = readl_poll_timeout(host->axi + RZG3S_PCI_PCSTAT2, cs2,
> +				 (cs2 & RZG3S_PCI_PCSTAT2_LS_CHG_DONE),
> +				 PCIE_LINK_WAIT_SLEEP_MS * MILLI,
> +				 PCIE_LINK_WAIT_SLEEP_MS * MILLI *
> +				 PCIE_LINK_WAIT_MAX_RETRIES);
> +
> +	/*
> +	 * According to the RZ/G3S HW manual (Rev.1.10, 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 int rzg3s_pcie_config_init(struct rzg3s_pcie_host *host)
> +{
> +	struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host);
> +	struct resource_entry *ft;
> +	struct resource *bus;
> +	u8 subordinate_bus;
> +	u8 secondary_bus;
> +	u8 primary_bus;
> +
> +	ft = resource_list_first_type(&bridge->windows, IORESOURCE_BUS);
> +	if (!ft)
> +		return -ENODEV;
> +
> +	bus = ft->res;
> +	primary_bus = bus->start;
> +	secondary_bus = bus->start + 1;
> +	subordinate_bus = bus->end;
> +
> +	/* Enable access control to the CFGU */
> +	writel_relaxed(RZG3S_PCI_PERM_CFG_HWINIT_EN,
> +		       host->axi + RZG3S_PCI_PERM);
> +
> +	/* HW manual recommends to write 0xffffffff on initialization */
> +	writel_relaxed(0xffffffff, host->pcie + RZG3S_PCI_CFG_BARMSK00L);
> +	writel_relaxed(0xffffffff, host->pcie + RZG3S_PCI_CFG_BARMSK00U);
> +
> +	/* Update bus info */
> +	writeb_relaxed(primary_bus, host->pcie + PCI_PRIMARY_BUS);
> +	writeb_relaxed(secondary_bus, host->pcie + PCI_SECONDARY_BUS);
> +	writeb_relaxed(subordinate_bus, host->pcie + PCI_SUBORDINATE_BUS);
> +
> +	/* Disable access control to the CFGU */
> +	writel_relaxed(0, host->axi + RZG3S_PCI_PERM);
> +
> +	return 0;
> +}
> +
> +static void rzg3s_pcie_irq_init(struct rzg3s_pcie_host *host)
> +{
> +	/*
> +	 * According to the HW manual of the RZ/G3S (Rev.1.10, 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_relaxed(RZG3S_PCI_PEIS0_DL_UPDOWN |
> +		       RZG3S_PCI_PEIS0_RX_DLLP_PM_ENTER,
> +		       host->axi + RZG3S_PCI_PEIS0);
> +
> +	/* Disable all interrupts */
> +	writel_relaxed(0, host->axi + RZG3S_PCI_PEIE0);
> +
> +	/* Clear all parity and ecc error interrupts */
> +	writel_relaxed(~0U, host->axi + RZG3S_PCI_PEIS1);
> +
> +	/* Disable all parity and ecc error interrupts */
> +	writel_relaxed(0, host->axi + RZG3S_PCI_PEIE1);
> +
> +	/* Clear all AXI master error interrupts */
> +	writel_relaxed(~0U, host->axi + RZG3S_PCI_AMEIS);
> +
> +	/* Clear all AXI slave error interrupts */
> +	writel_relaxed(~0U, host->axi + RZG3S_PCI_ASEIS1);
> +
> +	/* Clear all message receive interrupts */
> +	writel_relaxed(~0U, host->axi + RZG3S_PCI_MSGRCVIS);
> +}
> +
> +static int rzg3s_pcie_power_resets_deassert(struct rzg3s_pcie_host *host)
> +{
> +	const struct rzg3s_pcie_soc_data *data = host->data;
> +
> +	/*
> +	 * According to the RZ/G3S HW manual (Rev.1.10, 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);
> +	return reset_control_bulk_deassert(data->num_power_resets,
> +					   host->power_resets);
> +}
> +
> +static int rzg3s_pcie_resets_prepare_and_get(struct rzg3s_pcie_host *host)
> +{
> +	const struct rzg3s_pcie_soc_data *data = host->data;
> +	int ret;
> +
> +	host->power_resets = devm_kmalloc_array(host->dev,
> +						data->num_power_resets,
> +						sizeof(*host->power_resets),
> +						GFP_KERNEL);
> +	if (!host->power_resets)
> +		return -ENOMEM;
> +
> +	for (unsigned int i = 0; i < data->num_power_resets; i++)
> +		host->power_resets[i].id = data->power_resets[i];
> +
> +	host->cfg_resets = devm_kmalloc_array(host->dev,
> +					      data->num_cfg_resets,
> +					      sizeof(*host->cfg_resets),
> +					      GFP_KERNEL);
> +	if (!host->cfg_resets)
> +		return -ENOMEM;
> +
> +	for (unsigned int i = 0; i < data->num_cfg_resets; i++)
> +		host->cfg_resets[i].id = data->cfg_resets[i];
> +
> +	ret = devm_reset_control_bulk_get_exclusive(host->dev,
> +						    data->num_power_resets,
> +						    host->power_resets);
> +	if (ret)
> +		return ret;
> +
> +	return devm_reset_control_bulk_get_exclusive(host->dev,
> +						     data->num_cfg_resets,
> +						     host->cfg_resets);
> +}
> +
> +static int rzg3s_pcie_host_parse_root_port(struct rzg3s_pcie_host *host)
> +{
> +	struct device_node *of_port = of_get_next_child(host->dev->of_node, NULL);
> +	struct rzg3s_pcie_port *port = &host->port;
> +	int ret;
> +
> +	ret = of_property_read_u32(of_port, "vendor-id", &port->vendor_id);
> +	if (ret)
> +		return ret;
> +
> +	ret = of_property_read_u32(of_port, "device-id", &port->device_id);
> +	if (ret)
> +		return ret;
> +
> +	port->refclk = of_clk_get_by_name(of_port, "ref");
> +	if (IS_ERR(port->refclk))
> +		return PTR_ERR(port->refclk);
> +
> +	return 0;
> +}
> +
> +static int rzg3s_pcie_host_init_root_port(struct rzg3s_pcie_host *host)
> +{
> +	struct rzg3s_pcie_port *port = &host->port;
> +	struct device *dev = host->dev;
> +	int ret;
> +
> +	/* Enable access control to the CFGU */
> +	writel_relaxed(RZG3S_PCI_PERM_CFG_HWINIT_EN,
> +		       host->axi + RZG3S_PCI_PERM);
> +
> +	/* Update vendor ID and device ID */
> +	writew_relaxed(port->vendor_id, host->pcie + PCI_VENDOR_ID);
> +	writew_relaxed(port->device_id, host->pcie + PCI_DEVICE_ID);
> +
> +	/* Disable access control to the CFGU */
> +	writel_relaxed(0, host->axi + RZG3S_PCI_PERM);
> +
> +	ret = clk_prepare_enable(port->refclk);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to enable refclk!\n");
> +
> +	/* Set the PHY, if any */
> +	if (host->data->init_phy) {
> +		ret = host->data->init_phy(host);
> +		if (ret) {
> +			dev_err_probe(dev, ret, "Failed to set the PHY!\n");
> +			goto refclk_disable;
> +		}
> +	}
> +
> +	return 0;
> +
> +refclk_disable:
> +	clk_disable_unprepare(port->refclk);
> +	return ret;
> +}
> +
> +static int rzg3s_pcie_host_init(struct rzg3s_pcie_host *host)
> +{
> +	u32 val;
> +	int ret;
> +
> +	/* Initialize the PCIe related registers */
> +	ret = rzg3s_pcie_config_init(host);
> +	if (ret)
> +		return ret;
> +
> +	ret = rzg3s_pcie_host_init_root_port(host);
> +	if (ret)
> +		return ret;
> +
> +	/* Initialize the interrupts */
> +	rzg3s_pcie_irq_init(host);
> +
> +	ret = reset_control_bulk_deassert(host->data->num_cfg_resets,
> +					  host->cfg_resets);
> +	if (ret)
> +		goto disable_root_port_refclk;
> +
> +	/* Wait for link up */
> +	ret = readl_poll_timeout(host->axi + RZG3S_PCI_PCSTAT1, val,
> +				 !(val & RZG3S_PCI_PCSTAT1_DL_DOWN_STS),
> +				 PCIE_LINK_WAIT_SLEEP_MS * MILLI,
> +				 PCIE_LINK_WAIT_SLEEP_MS * MILLI *
> +				 PCIE_LINK_WAIT_MAX_RETRIES);
> +	if (ret)
> +		goto cfg_resets_deassert;
> +
> +	val = readl_relaxed(host->axi + RZG3S_PCI_PCSTAT2);
> +	dev_info(host->dev, "PCIe link status [0x%x]\n", val);
> +
> +	return 0;
> +
> +cfg_resets_deassert:
> +	reset_control_bulk_assert(host->data->num_cfg_resets,
> +				  host->cfg_resets);
> +disable_root_port_refclk:
> +	clk_disable_unprepare(host->port.refclk);
> +	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_relaxed(upper_32_bits(cpu_addr),
> +		       host->axi + RZG3S_PCI_ADESTU(id));
> +	writel_relaxed(lower_32_bits(cpu_addr),
> +		       host->axi + RZG3S_PCI_ADESTL(id));
> +
> +	/* Set window size */
> +	writel_relaxed(upper_32_bits(size), host->axi + RZG3S_PCI_AWMASKU(id));
> +	writel_relaxed(lower_32_bits(size), host->axi + RZG3S_PCI_AWMASKL(id));
> +
> +	/* Set PCIe window base address and enable the window */
> +	writel_relaxed(upper_32_bits(pci_addr),
> +		       host->axi + RZG3S_PCI_AWBASEU(id));
> +	writel_relaxed(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, -ENOSPC,
> +					     "Failed to map inbound window for resource (%s)\n",
> +					     entry->res->name);
> +
> +		size = resource_size(entry->res) - size_id;
> +
> +		/*
> +		 * According to the RZ/G3S HW manual (Rev.1.10,
> +		 * section 34.3.1.71 AXI Window Mask (Lower) Registers) the min
> +		 * size is 4K.
> +		 */
> +		size = max(size, SZ_4K);
> +
> +		/*
> +		 * According the RZ/G3S HW manual (Rev.1.10, 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 aligned and be a
> +		 * power of 2.
> +		 */
> +		size = ALIGN(size, SZ_4K);
> +
> +		/*
> +		 * According to the RZ/G3S HW manual (Rev.1.10, section
> +		 * 34.3.1.71 AXI Window Mask (Lower) Registers) HW expects first
> +		 * 12 LSB bits to be 0xfff. Subtract 1 from size for this.
> +		 */
> +		size = roundup_pow_of_two(size) - 1;
> +
> +		cpu_addr = ALIGN(cpu_addr, SZ_4K);
> +		pci_addr = ALIGN(pci_addr, SZ_4K);
> +
> +		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 (Rev.1.10, section 34.3.1.75 PCIe
> +	 * Window Base (Lower) Registers) the window base address need to be 4K
> +	 * aligned.
> +	 */
> +	res_start = ALIGN(res_start, SZ_4K);
> +
> +	size = ALIGN(size, SZ_4K);
> +	size = roundup_pow_of_two(size) - 1;
> +
> +	/* Set PCIe destination */
> +	writel_relaxed(upper_32_bits(res_start),
> +		       host->axi + RZG3S_PCI_PDESTU(id));
> +	writel_relaxed(lower_32_bits(res_start),
> +		       host->axi + RZG3S_PCI_PDESTL(id));
> +
> +	/* Set PCIe window mask */
> +	writel_relaxed(upper_32_bits(size), host->axi + RZG3S_PCI_PWMASKU(id));
> +	writel_relaxed(lower_32_bits(size), host->axi + RZG3S_PCI_PWMASKL(id));
> +
> +	/* Set PCIe window base and enable the window */
> +	writel_relaxed(upper_32_bits(res_start),
> +		       host->axi + RZG3S_PCI_PWBASEU(id));
> +	writel_relaxed(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, -ENOSPC,
> +					     "Failed to map outbound window for resource (%s)\n",
> +					     res->name);
> +
> +		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_soc_pcie_init_phy(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,
> +	};
> +
> +	/*
> +	 * Enable access permission for physical layer control and status
> +	 * registers.
> +	 */
> +	writel_relaxed(RZG3S_PCI_PERM_PIPE_PHY_REG_EN,
> +		       host->axi + RZG3S_PCI_PERM);
> +
> +	for (unsigned int i = 0; i < RZG3S_PCI_PHY_XCFGD_NUM; i++) {
> +		writel_relaxed(xcfgd_settings[i],
> +			       host->axi + RZG3S_PCI_PHY_XCFGD(i));
> +	}
> +
> +	for (unsigned int i = 0; i < RZG3S_PCI_PHY_XCFGA_CMN_NUM; i++) {
> +		writel_relaxed(xcfga_cmn_settings[i],
> +			       host->axi + RZG3S_PCI_PHY_XCFGA_CMN(i));
> +	}
> +
> +	for (unsigned int i = 0; i < RZG3S_PCI_PHY_XCFGA_RX_NUM; i++) {
> +		writel_relaxed(xcfga_rx_settings[i],
> +			       host->axi + RZG3S_PCI_PHY_XCFGA_RX(i));
> +	}
> +
> +	writel_relaxed(0x107, host->axi + RZG3S_PCI_PHY_XCFGA_TX);
> +
> +	/* Select PHY settings values */
> +	writel_relaxed(RZG3S_PCI_PHY_XCFG_CTRL_PHYREG_SEL,
> +		       host->axi + RZG3S_PCI_PHY_XCFG_CTRL);
> +
> +	/*
> +	 * Disable access permission for physical layer control and status
> +	 * registers.
> +	 */
> +	writel_relaxed(0, host->axi + RZG3S_PCI_PERM);
> +
> +	return 0;
> +}
> +
> +static int
> +rzg3s_pcie_host_setup(struct rzg3s_pcie_host *host,
> +		      int (*intx_setup)(struct rzg3s_pcie_host *host),
> +		      void (*intx_teardown)(struct rzg3s_pcie_host *host),
> +		      int (*msi_setup)(struct rzg3s_pcie_host *host),
> +		      void (*msi_teardown)(struct rzg3s_pcie_host *host))
> +{
> +	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");
> +
> +	if (intx_setup) {
> +		ret = intx_setup(host);
> +		if (ret)
> +			return dev_err_probe(dev, ret,
> +					     "Failed to setup INTx\n");
> +	}
> +
> +	if (IS_ENABLED(CONFIG_PCI_MSI)) {
> +		ret = msi_setup(host);
> +		if (ret) {
> +			dev_err_probe(dev, ret, "Failed to setup MSIs\n");
> +			goto intx_teardown;
> +		}
> +	}
> +
> +	ret = rzg3s_pcie_host_init(host);
> +	if (ret) {
> +		dev_err_probe(dev, ret,
> +			      "Failed to initialize the HW!\n");
> +		goto msi_teardown;
> +	}
> +
> +	ret = rzg3s_pcie_set_max_link_speed(host);
> +	if (ret)
> +		dev_info(dev, "Failed to set max link speed\n");
> +
> +	msleep(PCIE_RESET_CONFIG_WAIT_MS);
> +
> +	return 0;
> +
> +msi_teardown:
> +	if (IS_ENABLED(CONFIG_PCI_MSI))
> +		msi_teardown(host);
> +intx_teardown:
> +	if (intx_teardown)
> +		intx_teardown(host);
> +
> +	return ret;
> +}
> +
> +static int rzg3s_pcie_probe(struct platform_device *pdev)
> +{
> +	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->data = device_get_match_data(dev);
> +	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;
> +
> +	host->max_link_speed = of_pci_get_max_link_speed(np);
> +	if (host->max_link_speed < 0)
> +		host->max_link_speed = 2;
> +
> +	ret = rzg3s_pcie_host_parse_root_port(host);
> +	if (ret)
> +		return ret;
> +
> +	host->sysc = syscon_node_to_regmap(sysc_np);
> +	if (IS_ERR(host->sysc)) {
> +		ret = PTR_ERR(host->sysc);
> +		goto port_refclk_put;
> +	}
> +
> +	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)
> +		goto port_refclk_put;
> +
> +	ret = rzg3s_pcie_resets_prepare_and_get(host);
> +	if (ret)
> +		goto sysc_signal_restore;
> +
> +	ret = rzg3s_pcie_power_resets_deassert(host);
> +	if (ret)
> +		goto sysc_signal_restore;
> +
> +	pm_runtime_enable(dev);
> +
> +	/*
> +	 * Controller clocks are part of a clock power domain. Enable them
> +	 * through runtime PM.
> +	 */
> +	ret = pm_runtime_resume_and_get(dev);
> +	if (ret)
> +		goto rpm_disable;
> +
> +	raw_spin_lock_init(&host->hw_lock);
> +
> +	ret = rzg3s_pcie_host_setup(host, rzg3s_pcie_intx_setup,
> +				    rzg3s_pcie_intx_teardown,
> +				    rzg3s_pcie_msi_enable,
> +				    rzg3s_pcie_msi_teardown);
> +	if (ret)
> +		goto rpm_put;
> +
> +	bridge->sysdata = host;
> +	bridge->ops = &rzg3s_pcie_root_ops;
> +	bridge->child_ops = &rzg3s_pcie_child_ops;
> +	ret = pci_host_probe(bridge);
> +	if (ret)
> +		goto host_probe_teardown;
> +
> +	return 0;
> +
> +host_probe_teardown:
> +	rzg3s_pcie_msi_teardown(host);
> +	rzg3s_pcie_intx_teardown(host);
> +	reset_control_bulk_deassert(host->data->num_cfg_resets,
> +				    host->cfg_resets);
> +rpm_put:
> +	pm_runtime_put_sync(dev);
> +rpm_disable:
> +	pm_runtime_disable(dev);
> +	reset_control_bulk_assert(host->data->num_power_resets,
> +				  host->power_resets);
> +sysc_signal_restore:
> +	/*
> +	 * SYSC RST_RSM_B signal need to be asserted before turning off the
> +	 * power to the PHY.
> +	 */
> +	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, 0));
> +port_refclk_put:
> +	clk_put(host->port.refclk);
> +
> +	return ret;
> +}
> +
> +static int rzg3s_pcie_suspend_noirq(struct device *dev)
> +{
> +	struct rzg3s_pcie_host *host = dev_get_drvdata(dev);
> +	const struct rzg3s_pcie_soc_data *data = host->data;
> +	struct rzg3s_pcie_port *port = &host->port;
> +	struct regmap *sysc = host->sysc;
> +	int ret;
> +
> +	ret = pm_runtime_put_sync(dev);
> +	if (ret)
> +		return ret;
> +
> +	clk_disable_unprepare(port->refclk);
> +
> +	ret = reset_control_bulk_assert(data->num_power_resets,
> +					host->power_resets);
> +	if (ret)
> +		goto refclk_restore;
> +
> +	ret = reset_control_bulk_assert(data->num_cfg_resets,
> +					host->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:
> +	reset_control_bulk_deassert(data->num_cfg_resets,
> +				    host->cfg_resets);
> +power_resets_restore:
> +	reset_control_bulk_deassert(data->num_power_resets,
> +				    host->power_resets);
> +refclk_restore:
> +	clk_prepare_enable(port->refclk);
> +	pm_runtime_resume_and_get(dev);
> +	return ret;
> +}
> +
> +static int rzg3s_pcie_resume_noirq(struct device *dev)
> +{
> +	struct rzg3s_pcie_host *host = dev_get_drvdata(dev);
> +	const struct rzg3s_pcie_soc_data *data = host->data;
> +	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;
> +
> +	ret = rzg3s_pcie_power_resets_deassert(host);
> +	if (ret)
> +		goto assert_rst_rsm_b;
> +
> +	ret = pm_runtime_resume_and_get(dev);
> +	if (ret)
> +		goto assert_power_resets;
> +
> +	ret = rzg3s_pcie_host_setup(host, NULL, NULL,
> +				    rzg3s_pcie_msi_hw_setup,
> +				    rzg3s_pcie_msi_hw_teardown);
> +	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:
> +	reset_control_bulk_assert(data->num_power_resets,
> +				  host->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 const struct dev_pm_ops rzg3s_pcie_pm_ops = {
> +	NOIRQ_SYSTEM_SLEEP_PM_OPS(rzg3s_pcie_suspend_noirq,
> +				  rzg3s_pcie_resume_noirq)
> +};
> +
> +static const char * const rzg3s_soc_power_resets[] = {
> +	"aresetn", "rst_cfg_b", "rst_load_b",
> +};
> +
> +static const char * const rzg3s_soc_cfg_resets[] = {
> +	"rst_b", "rst_ps_b", "rst_gp_b", "rst_rsm_b",
> +};
> +
> +static const struct rzg3s_pcie_soc_data rzg3s_soc_data = {
> +	.power_resets = rzg3s_soc_power_resets,
> +	.num_power_resets = ARRAY_SIZE(rzg3s_soc_power_resets),
> +	.cfg_resets = rzg3s_soc_cfg_resets,
> +	.num_cfg_resets = ARRAY_SIZE(rzg3s_soc_cfg_resets),
> +	.init_phy = rzg3s_soc_pcie_init_phy,
> +};
> +
> +static const struct of_device_id rzg3s_pcie_of_match[] = {
> +	{
> +		.compatible = "renesas,r9a08g045-pcie",
> +		.data = &rzg3s_soc_data,
> +	},
> +	{}
> +};
> +
> +static struct platform_driver rzg3s_pcie_driver = {
> +	.driver = {
> +		.name = "rzg3s-pcie-host",
> +		.of_match_table = rzg3s_pcie_of_match,
> +		.pm = pm_ptr(&rzg3s_pcie_pm_ops),
> +		.suppress_bind_attrs = true,
> +		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
> +	},
> +	.probe = rzg3s_pcie_probe,
> +};
> +builtin_platform_driver(rzg3s_pcie_driver);
> -- 
> 2.43.0
> 

-- 
மணிவண்ணன் சதாசிவம்

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

* Re: [PATCH v5 3/6] arm64: dts: renesas: r9a08g045: Add PCIe node
  2025-10-07 13:36 ` [PATCH v5 3/6] arm64: dts: renesas: r9a08g045: Add PCIe node Claudiu
  2025-10-07 13:44   ` Biju Das
@ 2025-10-19  6:57   ` Manivannan Sadhasivam
  2025-10-20  6:15     ` Claudiu Beznea
  1 sibling, 1 reply; 29+ messages in thread
From: Manivannan Sadhasivam @ 2025-10-19  6:57 UTC (permalink / raw)
  To: Claudiu
  Cc: lpieralisi, kwilczynski, robh, bhelgaas, krzk+dt, conor+dt,
	geert+renesas, magnus.damm, p.zabel, linux-pci, linux-renesas-soc,
	devicetree, linux-kernel, Claudiu Beznea, Wolfram Sang

On Tue, Oct 07, 2025 at 04:36:54PM +0300, Claudiu wrote:
> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> 
> The RZ/G3S SoC has a variant (R9A08G045S33) which supports PCIe. Add the
> PCIe node.
> 
> Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> ---
> 
> Changes in v5:
> - updated the last part of ranges and dma-ranges
> - collected tags
> 
> Changes in v4:
> - moved the node to r9a08g045.dtsi
> - dropped the "s33" from the compatible string
> - added port node
> - re-ordered properties to have them grouped together
> 
> Changes in v3:
> - collected tags
> - changed the ranges flags
> 
> Changes in v2:
> - updated the dma-ranges to reflect the SoC capability; added a
>   comment about it.
> - updated clock-names, interrupt names
> - dropped legacy-interrupt-controller node
> - added interrupt-controller property
> - moved renesas,sysc at the end of the node to comply with
>   DT coding style
> 
>  arch/arm64/boot/dts/renesas/r9a08g045.dtsi | 66 ++++++++++++++++++++++
>  1 file changed, 66 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/renesas/r9a08g045.dtsi b/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
> index 16e6ac614417..00b43377877e 100644
> --- a/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
> +++ b/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
> @@ -717,6 +717,72 @@ eth1: ethernet@11c40000 {
>  			status = "disabled";
>  		};
>  
> +		pcie: pcie@11e40000 {
> +			compatible = "renesas,r9a08g045-pcie";
> +			reg = <0 0x11e40000 0 0x10000>;
> +			ranges = <0x02000000 0 0x30000000 0 0x30000000 0 0x08000000>;
> +			/* Map all possible DRAM ranges (4 GB). */
> +			dma-ranges = <0x42000000 0 0x40000000 0 0x40000000 1 0x00000000>;
> +			bus-range = <0x0 0xff>;
> +			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 = "serr", "serr_cor", "serr_nonfatal",
> +					  "serr_fatal", "axi_err", "inta",
> +					  "intb", "intc", "intd", "msi",
> +					  "link_bandwidth", "pm_pme", "dma",
> +					  "pcie_evt", "msg", "all";
> +			#interrupt-cells = <1>;
> +			interrupt-controller;
> +			interrupt-map-mask = <0 0 0 7>;
> +			interrupt-map = <0 0 0 1 &pcie 0 0 0 0>, /* INTA */
> +					<0 0 0 2 &pcie 0 0 0 1>, /* INTB */
> +					<0 0 0 3 &pcie 0 0 0 2>, /* INTC */
> +					<0 0 0 4 &pcie 0 0 0 3>; /* INTD */

Why can't you describe the SPI interrupt for INTx in 'interrupt-map' itself?

> +			clocks = <&cpg CPG_MOD R9A08G045_PCI_ACLK>,
> +				 <&cpg CPG_MOD R9A08G045_PCI_CLKL1PM>;
> +			clock-names = "aclk", "pm";
> +			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";
> +			power-domains = <&cpg>;
> +			device_type = "pci";
> +			#address-cells = <3>;
> +			#size-cells = <2>;
> +			max-link-speed = <2>;

'max-link-speed' is used to limit the link speed. Why do you need to limit the
link speed all the time for this controller?

- Mani

-- 
மணிவண்ணன் சதாசிவம்

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

* Re: [PATCH v5 3/6] arm64: dts: renesas: r9a08g045: Add PCIe node
  2025-10-19  6:57   ` Manivannan Sadhasivam
@ 2025-10-20  6:15     ` Claudiu Beznea
  2025-10-21  2:16       ` Manivannan Sadhasivam
  0 siblings, 1 reply; 29+ messages in thread
From: Claudiu Beznea @ 2025-10-20  6:15 UTC (permalink / raw)
  To: Manivannan Sadhasivam
  Cc: lpieralisi, kwilczynski, robh, bhelgaas, krzk+dt, conor+dt,
	geert+renesas, magnus.damm, p.zabel, linux-pci, linux-renesas-soc,
	devicetree, linux-kernel, Claudiu Beznea, Wolfram Sang

Hi, Mani,

On 10/19/25 09:57, Manivannan Sadhasivam wrote:
> On Tue, Oct 07, 2025 at 04:36:54PM +0300, Claudiu wrote:
>> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>>
>> The RZ/G3S SoC has a variant (R9A08G045S33) which supports PCIe. Add the
>> PCIe node.
>>
>> Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
>> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
>> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>> ---
>>
>> Changes in v5:
>> - updated the last part of ranges and dma-ranges
>> - collected tags
>>
>> Changes in v4:
>> - moved the node to r9a08g045.dtsi
>> - dropped the "s33" from the compatible string
>> - added port node
>> - re-ordered properties to have them grouped together
>>
>> Changes in v3:
>> - collected tags
>> - changed the ranges flags
>>
>> Changes in v2:
>> - updated the dma-ranges to reflect the SoC capability; added a
>>   comment about it.
>> - updated clock-names, interrupt names
>> - dropped legacy-interrupt-controller node
>> - added interrupt-controller property
>> - moved renesas,sysc at the end of the node to comply with
>>   DT coding style
>>
>>  arch/arm64/boot/dts/renesas/r9a08g045.dtsi | 66 ++++++++++++++++++++++
>>  1 file changed, 66 insertions(+)
>>
>> diff --git a/arch/arm64/boot/dts/renesas/r9a08g045.dtsi b/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
>> index 16e6ac614417..00b43377877e 100644
>> --- a/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
>> +++ b/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
>> @@ -717,6 +717,72 @@ eth1: ethernet@11c40000 {
>>  			status = "disabled";
>>  		};
>>  
>> +		pcie: pcie@11e40000 {
>> +			compatible = "renesas,r9a08g045-pcie";
>> +			reg = <0 0x11e40000 0 0x10000>;
>> +			ranges = <0x02000000 0 0x30000000 0 0x30000000 0 0x08000000>;
>> +			/* Map all possible DRAM ranges (4 GB). */
>> +			dma-ranges = <0x42000000 0 0x40000000 0 0x40000000 1 0x00000000>;
>> +			bus-range = <0x0 0xff>;
>> +			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 = "serr", "serr_cor", "serr_nonfatal",
>> +					  "serr_fatal", "axi_err", "inta",
>> +					  "intb", "intc", "intd", "msi",
>> +					  "link_bandwidth", "pm_pme", "dma",
>> +					  "pcie_evt", "msg", "all";
>> +			#interrupt-cells = <1>;
>> +			interrupt-controller;
>> +			interrupt-map-mask = <0 0 0 7>;
>> +			interrupt-map = <0 0 0 1 &pcie 0 0 0 0>, /* INTA */
>> +					<0 0 0 2 &pcie 0 0 0 1>, /* INTB */
>> +					<0 0 0 3 &pcie 0 0 0 2>, /* INTC */
>> +					<0 0 0 4 &pcie 0 0 0 3>; /* INTD */
> 
> Why can't you describe the SPI interrupt for INTx in 'interrupt-map' itself?

Because the INTx need to be controlled at PCIe controller level, too.
Driver implements struct irq_chip::{irq_ack, irq_mask, irq_unmask} for
these interrupts. Describing them like:

interrupt-map = <0 0 0 1 &gic GIC_SPI 400 IRQ_TYPE_LEVEL_HIGH>,
                <0 0 0 2 &gic GIC_SPI 401 IRQ_TYPE_LEVEL_HIGH>,
                <0 0 0 3 &gic GIC_SPI 400 IRQ_TYPE_LEVEL_HIGH>,
                <0 0 0 4 &gic GIC_SPI 400 IRQ_TYPE_LEVEL_HIGH>;

wouldn't allow for this, AFAICT.

> 
>> +			clocks = <&cpg CPG_MOD R9A08G045_PCI_ACLK>,
>> +				 <&cpg CPG_MOD R9A08G045_PCI_CLKL1PM>;
>> +			clock-names = "aclk", "pm";
>> +			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";
>> +			power-domains = <&cpg>;
>> +			device_type = "pci";
>> +			#address-cells = <3>;
>> +			#size-cells = <2>;
>> +			max-link-speed = <2>;
> 
> 'max-link-speed' is used to limit the link speed. Why do you need to limit the
> link speed all the time for this controller?

I though it is to describe the max link speed supported by controller. This
controller max link speed is 5GT/s. Should I drop this property?

Thank you,
Claudiu


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

* Re: [PATCH v5 3/6] arm64: dts: renesas: r9a08g045: Add PCIe node
  2025-10-20  6:15     ` Claudiu Beznea
@ 2025-10-21  2:16       ` Manivannan Sadhasivam
  0 siblings, 0 replies; 29+ messages in thread
From: Manivannan Sadhasivam @ 2025-10-21  2:16 UTC (permalink / raw)
  To: Claudiu Beznea
  Cc: lpieralisi, kwilczynski, robh, bhelgaas, krzk+dt, conor+dt,
	geert+renesas, magnus.damm, p.zabel, linux-pci, linux-renesas-soc,
	devicetree, linux-kernel, Claudiu Beznea, Wolfram Sang

On Mon, Oct 20, 2025 at 09:15:47AM +0300, Claudiu Beznea wrote:
> Hi, Mani,
> 
> On 10/19/25 09:57, Manivannan Sadhasivam wrote:
> > On Tue, Oct 07, 2025 at 04:36:54PM +0300, Claudiu wrote:
> >> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> >>
> >> The RZ/G3S SoC has a variant (R9A08G045S33) which supports PCIe. Add the
> >> PCIe node.
> >>
> >> Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
> >> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> >> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> >> ---
> >>
> >> Changes in v5:
> >> - updated the last part of ranges and dma-ranges
> >> - collected tags
> >>
> >> Changes in v4:
> >> - moved the node to r9a08g045.dtsi
> >> - dropped the "s33" from the compatible string
> >> - added port node
> >> - re-ordered properties to have them grouped together
> >>
> >> Changes in v3:
> >> - collected tags
> >> - changed the ranges flags
> >>
> >> Changes in v2:
> >> - updated the dma-ranges to reflect the SoC capability; added a
> >>   comment about it.
> >> - updated clock-names, interrupt names
> >> - dropped legacy-interrupt-controller node
> >> - added interrupt-controller property
> >> - moved renesas,sysc at the end of the node to comply with
> >>   DT coding style
> >>
> >>  arch/arm64/boot/dts/renesas/r9a08g045.dtsi | 66 ++++++++++++++++++++++
> >>  1 file changed, 66 insertions(+)
> >>
> >> diff --git a/arch/arm64/boot/dts/renesas/r9a08g045.dtsi b/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
> >> index 16e6ac614417..00b43377877e 100644
> >> --- a/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
> >> +++ b/arch/arm64/boot/dts/renesas/r9a08g045.dtsi
> >> @@ -717,6 +717,72 @@ eth1: ethernet@11c40000 {
> >>  			status = "disabled";
> >>  		};
> >>  
> >> +		pcie: pcie@11e40000 {
> >> +			compatible = "renesas,r9a08g045-pcie";
> >> +			reg = <0 0x11e40000 0 0x10000>;
> >> +			ranges = <0x02000000 0 0x30000000 0 0x30000000 0 0x08000000>;
> >> +			/* Map all possible DRAM ranges (4 GB). */
> >> +			dma-ranges = <0x42000000 0 0x40000000 0 0x40000000 1 0x00000000>;
> >> +			bus-range = <0x0 0xff>;
> >> +			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 = "serr", "serr_cor", "serr_nonfatal",
> >> +					  "serr_fatal", "axi_err", "inta",
> >> +					  "intb", "intc", "intd", "msi",
> >> +					  "link_bandwidth", "pm_pme", "dma",
> >> +					  "pcie_evt", "msg", "all";
> >> +			#interrupt-cells = <1>;
> >> +			interrupt-controller;
> >> +			interrupt-map-mask = <0 0 0 7>;
> >> +			interrupt-map = <0 0 0 1 &pcie 0 0 0 0>, /* INTA */
> >> +					<0 0 0 2 &pcie 0 0 0 1>, /* INTB */
> >> +					<0 0 0 3 &pcie 0 0 0 2>, /* INTC */
> >> +					<0 0 0 4 &pcie 0 0 0 3>; /* INTD */
> > 
> > Why can't you describe the SPI interrupt for INTx in 'interrupt-map' itself?
> 
> Because the INTx need to be controlled at PCIe controller level, too.
> Driver implements struct irq_chip::{irq_ack, irq_mask, irq_unmask} for
> these interrupts. Describing them like:
> 
> interrupt-map = <0 0 0 1 &gic GIC_SPI 400 IRQ_TYPE_LEVEL_HIGH>,
>                 <0 0 0 2 &gic GIC_SPI 401 IRQ_TYPE_LEVEL_HIGH>,
>                 <0 0 0 3 &gic GIC_SPI 400 IRQ_TYPE_LEVEL_HIGH>,
>                 <0 0 0 4 &gic GIC_SPI 400 IRQ_TYPE_LEVEL_HIGH>;
> 
> wouldn't allow for this, AFAICT.
> 

Ah, so these interrupts are masked at the PCI controller level... Fine then.

> > 
> >> +			clocks = <&cpg CPG_MOD R9A08G045_PCI_ACLK>,
> >> +				 <&cpg CPG_MOD R9A08G045_PCI_CLKL1PM>;
> >> +			clock-names = "aclk", "pm";
> >> +			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";
> >> +			power-domains = <&cpg>;
> >> +			device_type = "pci";
> >> +			#address-cells = <3>;
> >> +			#size-cells = <2>;
> >> +			max-link-speed = <2>;
> > 
> > 'max-link-speed' is used to limit the link speed. Why do you need to limit the
> > link speed all the time for this controller?
> 
> I though it is to describe the max link speed supported by controller. This
> controller max link speed is 5GT/s. Should I drop this property?
> 

Yes. The driver should be able to query the Link Capabilities register and get
the max supported link speed of the hardware. This property should only be used
when the firmware decides to override the hardware supported max link speed.

- Mani

-- 
மணிவண்ணன் சதாசிவம்

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

* Re: [PATCH v5 2/6] PCI: rzg3s-host: Add Renesas RZ/G3S SoC host driver
  2025-10-07 13:36 ` [PATCH v5 2/6] PCI: rzg3s-host: Add Renesas RZ/G3S SoC host driver Claudiu
  2025-10-19  6:52   ` Manivannan Sadhasivam
@ 2025-10-22 19:49   ` Bjorn Helgaas
  2025-10-23  5:11     ` Claudiu Beznea
  2025-10-23  7:55     ` Geert Uytterhoeven
  2025-10-23  8:00   ` Geert Uytterhoeven
  2 siblings, 2 replies; 29+ messages in thread
From: Bjorn Helgaas @ 2025-10-22 19:49 UTC (permalink / raw)
  To: Claudiu
  Cc: lpieralisi, kwilczynski, mani, robh, bhelgaas, krzk+dt, conor+dt,
	geert+renesas, magnus.damm, p.zabel, linux-pci, linux-renesas-soc,
	devicetree, linux-kernel, Claudiu Beznea, Wolfram Sang

On Tue, Oct 07, 2025 at 04:36:53PM +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.

> +++ b/drivers/pci/controller/Kconfig
> @@ -266,6 +266,14 @@ 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
> +	bool "Renesas RZ/G3S PCIe host controller"
> +	depends on ARCH_RENESAS || COMPILE_TEST
> +	select MFD_SYSCON
> +	select IRQ_MSI_LIB
> +	help
> +	  Say Y here if you want PCIe host controller support on Renesas RZ/G3S SoC.

Wrap to fit in 80 columns like the rest of the file.

> +++ b/drivers/pci/controller/pcie-rzg3s-host.c

> +#define RZG3S_PCI_MSIRCVWMSKL			0x108
> +#define RZG3S_PCI_MSIRCVWMSKL_MASK		GENMASK(31, 2)

Unfortunate to have to add _MASK here when none of the other GENMASKs
need it.  Can't think of a better name though.

> +#define RZG3S_PCI_MSIRCVWMSKU			0x10c

Unused.

> +#define RZG3S_PCI_AMEIE				0x210

Unused.

> +#define RZG3S_PCI_ASEIE1			0x220

Unused.

> +#define RZG3S_PCI_PCSTAT2_STATE_RX_DETECT	GENMASK(15, 8)

Unused.

> +/* Timeouts experimentally determined. */

No need for period at end.

> +static int rzg3s_pcie_child_read_conf(struct rzg3s_pcie_host *host,
> +				      struct pci_bus *bus,
> +				      unsigned int devfn, int where,
> +				      u32 *data)

Would fit in three lines if you want.

> +static int rzg3s_pcie_child_write_conf(struct rzg3s_pcie_host *host,
> +				       struct pci_bus *bus,
> +				       unsigned int devfn, int where,
> +				       u32 data)

Ditto.

> +static int rzg3s_pcie_msi_enable(struct rzg3s_pcie_host *host)
> +{
> +	struct platform_device *pdev = to_platform_device(host->dev);
> +	struct rzg3s_pcie_msi *msi = &host->msi;
> +	struct device *dev = host->dev;
> +	const char *devname;
> +	int irq, ret;
> +
> +	ret = devm_mutex_init(dev, &msi->map_lock);
> +	if (ret)
> +		return ret;
> +
> +	msi->irq = platform_get_irq_byname(pdev, "msi");
> +	if (msi->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 = request_irq(msi->irq, rzg3s_pcie_msi_irq, 0, devname, host);

Should this be devm_request_irq()?  Most drivers use it, although
pci-tegra.c and pcie-apple.c do not.  Maybe there's some special rule
about using request_irq() even though the driver uses devm in general?
I dunno.

> +static int rzg3s_pcie_intx_setup(struct rzg3s_pcie_host *host)
> +{
> +	struct device *dev = host->dev;
> +
> +	for (int i = 0; i < PCI_NUM_INTX; i++) {
> +		struct platform_device *pdev = to_platform_device(dev);

Looks like this should be outside the loop.

> +		char irq_name[5] = {0};
> +		int irq;
> +
> +		scnprintf(irq_name, ARRAY_SIZE(irq_name), "int%c", 'a' + i);
> +
> +		irq = platform_get_irq_byname(pdev, irq_name);
> +		if (irq < 0)
> +			return dev_err_probe(dev, -EINVAL,
> +					     "Failed to parse and map INT%c IRQ\n",
> +					     'A' + i);
> +
> +		host->intx_irqs[i] = irq;
> +		irq_set_chained_handler_and_data(irq,
> +						 rzg3s_pcie_intx_irq_handler,
> +						 host);
> +	}

> +static int rzg3s_pcie_power_resets_deassert(struct rzg3s_pcie_host *host)
> +{
> +	const struct rzg3s_pcie_soc_data *data = host->data;
> +
> +	/*
> +	 * According to the RZ/G3S HW manual (Rev.1.10, 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);

Consider fsleep() so we don't have to make up the 100us interval.

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

* Re: [PATCH v5 2/6] PCI: rzg3s-host: Add Renesas RZ/G3S SoC host driver
  2025-10-22 19:49   ` Bjorn Helgaas
@ 2025-10-23  5:11     ` Claudiu Beznea
  2025-10-23 15:55       ` Bjorn Helgaas
  2025-10-23  7:55     ` Geert Uytterhoeven
  1 sibling, 1 reply; 29+ messages in thread
From: Claudiu Beznea @ 2025-10-23  5:11 UTC (permalink / raw)
  To: Bjorn Helgaas
  Cc: lpieralisi, kwilczynski, mani, robh, bhelgaas, krzk+dt, conor+dt,
	geert+renesas, magnus.damm, p.zabel, linux-pci, linux-renesas-soc,
	devicetree, linux-kernel, Claudiu Beznea, Wolfram Sang

Hi, Bjorn,

On 10/22/25 22:49, Bjorn Helgaas wrote:
> On Tue, Oct 07, 2025 at 04:36:53PM +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.
> 
>> +++ b/drivers/pci/controller/Kconfig
>> @@ -266,6 +266,14 @@ 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
>> +	bool "Renesas RZ/G3S PCIe host controller"
>> +	depends on ARCH_RENESAS || COMPILE_TEST
>> +	select MFD_SYSCON
>> +	select IRQ_MSI_LIB
>> +	help
>> +	  Say Y here if you want PCIe host controller support on Renesas RZ/G3S SoC.
> 
> Wrap to fit in 80 columns like the rest of the file.

OK

> 
>> +++ b/drivers/pci/controller/pcie-rzg3s-host.c
> 
>> +#define RZG3S_PCI_MSIRCVWMSKL			0x108
>> +#define RZG3S_PCI_MSIRCVWMSKL_MASK		GENMASK(31, 2)
> 
> Unfortunate to have to add _MASK here when none of the other GENMASKs
> need it.  Can't think of a better name though.

Most of the register offsets and fields defines tried to use the naming
from the HW manual. Register at offset 0x108 have bits 31..2 read/writable
and is where we should be writing through driver, and bits 1..0 are read
only and have fixed value. These fields are named in HW manual as:

- MSI Receive Window Mask (Lower) [31:2]
- MSI Receive Window Mask (Lower) [1:0]

As bits 31..2 are read/writable, would you prefer something like:

#define RZG3S_PCI_MSIRCVWMSKL_RW		GENMASK(31, 2)

?

> 
>> +#define RZG3S_PCI_MSIRCVWMSKU			0x10c
> 
> Unused.
> 
>> +#define RZG3S_PCI_AMEIE				0x210
> 
> Unused.
> 
>> +#define RZG3S_PCI_ASEIE1			0x220
> 
> Unused.
> 
>> +#define RZG3S_PCI_PCSTAT2_STATE_RX_DETECT	GENMASK(15, 8)
> 
> Unused.

I agree with all the unused defines pointed. Will be dropped in the next
version.

> 
>> +/* Timeouts experimentally determined. */
> 
> No need for period at end.

Missed this one. I'll update it.

> 
>> +static int rzg3s_pcie_child_read_conf(struct rzg3s_pcie_host *host,
>> +				      struct pci_bus *bus,
>> +				      unsigned int devfn, int where,
>> +				      u32 *data)
> 
> Would fit in three lines if you want.
> 
>> +static int rzg3s_pcie_child_write_conf(struct rzg3s_pcie_host *host,
>> +				       struct pci_bus *bus,
>> +				       unsigned int devfn, int where,
>> +				       u32 data)
> 
> Ditto.

Will update both of these along with:

rzg3s_pcie_child_prepare_bus()
rzg3s_pcie_root_map_bus()
rzg3s_pcie_set_outbound_window()

that have the same symptom.

> 
>> +static int rzg3s_pcie_msi_enable(struct rzg3s_pcie_host *host)
>> +{
>> +	struct platform_device *pdev = to_platform_device(host->dev);
>> +	struct rzg3s_pcie_msi *msi = &host->msi;
>> +	struct device *dev = host->dev;
>> +	const char *devname;
>> +	int irq, ret;
>> +
>> +	ret = devm_mutex_init(dev, &msi->map_lock);
>> +	if (ret)
>> +		return ret;
>> +
>> +	msi->irq = platform_get_irq_byname(pdev, "msi");
>> +	if (msi->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 = request_irq(msi->irq, rzg3s_pcie_msi_irq, 0, devname, host);
> 
> Should this be devm_request_irq()?  Most drivers use it, although
> pci-tegra.c and pcie-apple.c do not.  Maybe there's some special rule
> about using request_irq() even though the driver uses devm in general?
> I dunno.

In general is not good to mix devm cleanups with driver specific one.

As it was requested to drop the devm cleanups from this driver (especially
devm_pm_runtime_enable() which enables the also the clocks) I switched the
initial devm_request_irq() to request_irq() to avoid keeping the interrupt
requested on error path, after driver's probed was executed, until devm
cleanups are called, and potentially having it firing w/o hardware
resourced being enabled (e.g. clocks), and potentially reading HW registers.

E.g., accessing the HW registers while clocks are disabled on the SoC I'm
working with leads to synchronous aborts.

So, I only kept the devm helpers for memory allocations, resets
assert/de-assert and the mutex initialization.

> 
>> +static int rzg3s_pcie_intx_setup(struct rzg3s_pcie_host *host)
>> +{
>> +	struct device *dev = host->dev;
>> +
>> +	for (int i = 0; i < PCI_NUM_INTX; i++) {
>> +		struct platform_device *pdev = to_platform_device(dev);
> 
> Looks like this should be outside the loop.

OK, I kept it here as it is used only inside this block.

> 
>> +		char irq_name[5] = {0};
>> +		int irq;
>> +
>> +		scnprintf(irq_name, ARRAY_SIZE(irq_name), "int%c", 'a' + i);
>> +
>> +		irq = platform_get_irq_byname(pdev, irq_name);
>> +		if (irq < 0)
>> +			return dev_err_probe(dev, -EINVAL,
>> +					     "Failed to parse and map INT%c IRQ\n",
>> +					     'A' + i);
>> +
>> +		host->intx_irqs[i] = irq;
>> +		irq_set_chained_handler_and_data(irq,
>> +						 rzg3s_pcie_intx_irq_handler,
>> +						 host);
>> +	}
> 
>> +static int rzg3s_pcie_power_resets_deassert(struct rzg3s_pcie_host *host)
>> +{
>> +	const struct rzg3s_pcie_soc_data *data = host->data;
>> +
>> +	/*
>> +	 * According to the RZ/G3S HW manual (Rev.1.10, 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);
> 
> Consider fsleep() so we don't have to make up the 100us interval.

OK

Thank you for your review,
Claudiu

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

* Re: [PATCH v5 2/6] PCI: rzg3s-host: Add Renesas RZ/G3S SoC host driver
  2025-10-22 19:49   ` Bjorn Helgaas
  2025-10-23  5:11     ` Claudiu Beznea
@ 2025-10-23  7:55     ` Geert Uytterhoeven
  1 sibling, 0 replies; 29+ messages in thread
From: Geert Uytterhoeven @ 2025-10-23  7:55 UTC (permalink / raw)
  To: Bjorn Helgaas
  Cc: Claudiu, lpieralisi, kwilczynski, mani, robh, bhelgaas, krzk+dt,
	conor+dt, magnus.damm, p.zabel, linux-pci, linux-renesas-soc,
	devicetree, linux-kernel, Claudiu Beznea, Wolfram Sang

Hi Bjorn,

On Wed, 22 Oct 2025 at 21:49, Bjorn Helgaas <helgaas@kernel.org> wrote:
> On Tue, Oct 07, 2025 at 04:36:53PM +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.

> > +++ b/drivers/pci/controller/pcie-rzg3s-host.c
>
> > +#define RZG3S_PCI_MSIRCVWMSKL                        0x108
> > +#define RZG3S_PCI_MSIRCVWMSKL_MASK           GENMASK(31, 2)
>
> Unfortunate to have to add _MASK here when none of the other GENMASKs

Actually the unused RZG3S_PCI_MSIRCVWMSKU below would
need one, too:

    #define RZG3S_PCI_MSIRCVWMSKU_MASK   GENMASK(30, 0)

> need it.  Can't think of a better name though.

MASK is a good name, as the register bits actually specify (part of) the
window mask.

>
> > +#define RZG3S_PCI_MSIRCVWMSKU                        0x10c
>
> Unused.

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

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

* Re: [PATCH v5 2/6] PCI: rzg3s-host: Add Renesas RZ/G3S SoC host driver
  2025-10-07 13:36 ` [PATCH v5 2/6] PCI: rzg3s-host: Add Renesas RZ/G3S SoC host driver Claudiu
  2025-10-19  6:52   ` Manivannan Sadhasivam
  2025-10-22 19:49   ` Bjorn Helgaas
@ 2025-10-23  8:00   ` Geert Uytterhoeven
  2025-10-23  9:34     ` Claudiu Beznea
  2 siblings, 1 reply; 29+ messages in thread
From: Geert Uytterhoeven @ 2025-10-23  8:00 UTC (permalink / raw)
  To: Claudiu
  Cc: lpieralisi, kwilczynski, mani, robh, bhelgaas, krzk+dt, conor+dt,
	geert+renesas, magnus.damm, p.zabel, linux-pci, linux-renesas-soc,
	devicetree, linux-kernel, Claudiu Beznea, Wolfram Sang

Hi Claudiu,

On Tue, 7 Oct 2025 at 15:37, Claudiu <claudiu.beznea@tuxon.dev> 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.
>
> Hardware manual can be downloaded from the address in the "Link" section.
> The following steps should be followed to access the manual:
> 1/ Click the "User Manual" button
> 2/ Click "Confirm"; this will start downloading an archive
> 3/ Open the downloaded archive
> 4/ Navigate to r01uh1014ej*-rzg3s-users-manual-hardware -> Deliverables
> 5/ Open the file r01uh1014ej*-rzg3s.pdf
>
> Link: https://www.renesas.com/en/products/rz-g3s?queryID=695cc067c2d89e3f271d43656ede4d12
> Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>

Thanks for your patch!

> --- /dev/null
> +++ b/drivers/pci/controller/pcie-rzg3s-host.c

> +static void rzg3s_pcie_irq_compose_msi_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 drop_mask = RZG3S_PCI_MSIRCVWADRL_ENA |
> +                       RZG3S_PCI_MSIRCVWADRL_MSG_DATA_ENA;

This should include bit 2 (which is hardwired to zero (for now)),
so I think you better add

    #define RZG3S_PCI_MSIRCVWADRL_ADDR  GENMASK(31, 3)

> +       u32 lo, hi;
> +
> +       /*
> +        * Enable and msg data enable bits are part of the address lo. Drop
> +        * them.
> +        */
> +       lo = readl_relaxed(host->axi + RZG3S_PCI_MSIRCVWADRL) & ~drop_mask;

... and use FIELD_GET() with the new definition here.

> +       hi = readl_relaxed(host->axi + RZG3S_PCI_MSIRCVWADRU);
> +
> +       msg->address_lo = lo;
> +       msg->address_hi = hi;
> +       msg->data = data->hwirq;
> +}

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

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

* Re: [PATCH v5 2/6] PCI: rzg3s-host: Add Renesas RZ/G3S SoC host driver
  2025-10-23  8:00   ` Geert Uytterhoeven
@ 2025-10-23  9:34     ` Claudiu Beznea
  2025-10-23 11:02       ` Geert Uytterhoeven
  0 siblings, 1 reply; 29+ messages in thread
From: Claudiu Beznea @ 2025-10-23  9:34 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: lpieralisi, kwilczynski, mani, robh, bhelgaas, krzk+dt, conor+dt,
	geert+renesas, magnus.damm, p.zabel, linux-pci, linux-renesas-soc,
	devicetree, linux-kernel, Claudiu Beznea, Wolfram Sang

Hi, Geert,

On 10/23/25 11:00, Geert Uytterhoeven wrote:
> Hi Claudiu,
> 
> On Tue, 7 Oct 2025 at 15:37, Claudiu <claudiu.beznea@tuxon.dev> 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.
>>
>> Hardware manual can be downloaded from the address in the "Link" section.
>> The following steps should be followed to access the manual:
>> 1/ Click the "User Manual" button
>> 2/ Click "Confirm"; this will start downloading an archive
>> 3/ Open the downloaded archive
>> 4/ Navigate to r01uh1014ej*-rzg3s-users-manual-hardware -> Deliverables
>> 5/ Open the file r01uh1014ej*-rzg3s.pdf
>>
>> Link: https://www.renesas.com/en/products/rz-g3s?queryID=695cc067c2d89e3f271d43656ede4d12
>> Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
>> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> 
> Thanks for your patch!
> 
>> --- /dev/null
>> +++ b/drivers/pci/controller/pcie-rzg3s-host.c
> 
>> +static void rzg3s_pcie_irq_compose_msi_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 drop_mask = RZG3S_PCI_MSIRCVWADRL_ENA |
>> +                       RZG3S_PCI_MSIRCVWADRL_MSG_DATA_ENA;
> 
> This should include bit 2 (which is hardwired to zero (for now)),
> so I think you better add
> 
>     #define RZG3S_PCI_MSIRCVWADRL_ADDR  GENMASK(31, 3)
> 
>> +       u32 lo, hi;
>> +
>> +       /*
>> +        * Enable and msg data enable bits are part of the address lo. Drop
>> +        * them.
>> +        */
>> +       lo = readl_relaxed(host->axi + RZG3S_PCI_MSIRCVWADRL) & ~drop_mask;
> 
> ... and use FIELD_GET() with the new definition here.

Bits 31..3 of RZG3S_PCI_MSIRCVWADRL contains only bits 31..3 of the MSI
receive window address low, AFAIU. Using FIELD_GET() for bits 31..3 on the
value read from RZG3S_PCI_MSIRCVWADRL and passing this value to
msg->address_lo will lead to an NVMe device not working.

The documentation of RZG3S_PCI_MSIRCVWADRL on bits 31..3 specifies: "Set
the MSI receiving window's Start Address [31:3]. However, they must be aligned
 to the size set by the MSI Receive Window Mask"

The RZG3S_PCI_MSIRCVWMSKL have the last 2 bits set to 0x3, always, as of
the current documentation.

The value written to RZG3S_PCI_MSIRCVWADRL in rzg3s_pcie_msi_hw_setup() is
aligned to 128 (RZG3S_PCI_MSI_INT_NR * sizeof(u32)) and thus bits 2..0 will
be zero, and so, these bits are used by HW to allow us, e.g., to enable the
MSI window.

RZ/G3E have 64 MSI interrupts and this will be aligned to 256, thus, the
last 3 LSB bits of the address written to RZG3S_PCI_MSIRCVWADRL will always
be zero (at least with the current known setups) and we can use the
register RZG3S_PCI_MSIRCVWADRL as proposed in this patch.

Due to these I haven't added more alignment constraints on the value set in
RZG3S_PCI_MSIRCVWADRL.

Thank you for your review,
Claudiu

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

* Re: [PATCH v5 2/6] PCI: rzg3s-host: Add Renesas RZ/G3S SoC host driver
  2025-10-23  9:34     ` Claudiu Beznea
@ 2025-10-23 11:02       ` Geert Uytterhoeven
  2025-10-23 11:23         ` Claudiu Beznea
  0 siblings, 1 reply; 29+ messages in thread
From: Geert Uytterhoeven @ 2025-10-23 11:02 UTC (permalink / raw)
  To: Claudiu Beznea
  Cc: lpieralisi, kwilczynski, mani, robh, bhelgaas, krzk+dt, conor+dt,
	magnus.damm, p.zabel, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, Claudiu Beznea, Wolfram Sang

Hi Claudiu,

On Thu, 23 Oct 2025 at 12:54, Claudiu Beznea <claudiu.beznea@tuxon.dev> wrote:
> On 10/23/25 11:00, Geert Uytterhoeven wrote:
> > On Tue, 7 Oct 2025 at 15:37, Claudiu <claudiu.beznea@tuxon.dev> 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.
> >>
> >> Hardware manual can be downloaded from the address in the "Link" section.
> >> The following steps should be followed to access the manual:
> >> 1/ Click the "User Manual" button
> >> 2/ Click "Confirm"; this will start downloading an archive
> >> 3/ Open the downloaded archive
> >> 4/ Navigate to r01uh1014ej*-rzg3s-users-manual-hardware -> Deliverables
> >> 5/ Open the file r01uh1014ej*-rzg3s.pdf
> >>
> >> Link: https://www.renesas.com/en/products/rz-g3s?queryID=695cc067c2d89e3f271d43656ede4d12
> >> Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
> >> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> >
> > Thanks for your patch!
> >
> >> --- /dev/null
> >> +++ b/drivers/pci/controller/pcie-rzg3s-host.c
> >
> >> +static void rzg3s_pcie_irq_compose_msi_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 drop_mask = RZG3S_PCI_MSIRCVWADRL_ENA |
> >> +                       RZG3S_PCI_MSIRCVWADRL_MSG_DATA_ENA;
> >
> > This should include bit 2 (which is hardwired to zero (for now)),
> > so I think you better add
> >
> >     #define RZG3S_PCI_MSIRCVWADRL_ADDR  GENMASK(31, 3)
> >
> >> +       u32 lo, hi;
> >> +
> >> +       /*
> >> +        * Enable and msg data enable bits are part of the address lo. Drop
> >> +        * them.
> >> +        */
> >> +       lo = readl_relaxed(host->axi + RZG3S_PCI_MSIRCVWADRL) & ~drop_mask;
> >
> > ... and use FIELD_GET() with the new definition here.
>
> Bits 31..3 of RZG3S_PCI_MSIRCVWADRL contains only bits 31..3 of the MSI
> receive window address low, AFAIU. Using FIELD_GET() for bits 31..3 on the
> value read from RZG3S_PCI_MSIRCVWADRL and passing this value to
> msg->address_lo will lead to an NVMe device not working.

Oops, yes you are right, I went a bit too far with the FIELD_GET()
suggestion. But replacing drop_mask by RZG3S_PCI_MSIRCVWADRL_ADDR
would still be worthwhile, IMHO.

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

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

* Re: [PATCH v5 2/6] PCI: rzg3s-host: Add Renesas RZ/G3S SoC host driver
  2025-10-23 11:02       ` Geert Uytterhoeven
@ 2025-10-23 11:23         ` Claudiu Beznea
  2025-10-23 14:21           ` Geert Uytterhoeven
  0 siblings, 1 reply; 29+ messages in thread
From: Claudiu Beznea @ 2025-10-23 11:23 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: lpieralisi, kwilczynski, mani, robh, bhelgaas, krzk+dt, conor+dt,
	magnus.damm, p.zabel, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, Claudiu Beznea, Wolfram Sang

Hi, Geert,

On 10/23/25 14:02, Geert Uytterhoeven wrote:
> Hi Claudiu,
> 
> On Thu, 23 Oct 2025 at 12:54, Claudiu Beznea <claudiu.beznea@tuxon.dev> wrote:
>> On 10/23/25 11:00, Geert Uytterhoeven wrote:
>>> On Tue, 7 Oct 2025 at 15:37, Claudiu <claudiu.beznea@tuxon.dev> 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.
>>>>
>>>> Hardware manual can be downloaded from the address in the "Link" section.
>>>> The following steps should be followed to access the manual:
>>>> 1/ Click the "User Manual" button
>>>> 2/ Click "Confirm"; this will start downloading an archive
>>>> 3/ Open the downloaded archive
>>>> 4/ Navigate to r01uh1014ej*-rzg3s-users-manual-hardware -> Deliverables
>>>> 5/ Open the file r01uh1014ej*-rzg3s.pdf
>>>>
>>>> Link: https://www.renesas.com/en/products/rz-g3s?queryID=695cc067c2d89e3f271d43656ede4d12
>>>> Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
>>>> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>>>
>>> Thanks for your patch!
>>>
>>>> --- /dev/null
>>>> +++ b/drivers/pci/controller/pcie-rzg3s-host.c
>>>
>>>> +static void rzg3s_pcie_irq_compose_msi_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 drop_mask = RZG3S_PCI_MSIRCVWADRL_ENA |
>>>> +                       RZG3S_PCI_MSIRCVWADRL_MSG_DATA_ENA;
>>>
>>> This should include bit 2 (which is hardwired to zero (for now)),
>>> so I think you better add
>>>
>>>     #define RZG3S_PCI_MSIRCVWADRL_ADDR  GENMASK(31, 3)
>>>
>>>> +       u32 lo, hi;
>>>> +
>>>> +       /*
>>>> +        * Enable and msg data enable bits are part of the address lo. Drop
>>>> +        * them.
>>>> +        */
>>>> +       lo = readl_relaxed(host->axi + RZG3S_PCI_MSIRCVWADRL) & ~drop_mask;
>>>
>>> ... and use FIELD_GET() with the new definition here.
>>
>> Bits 31..3 of RZG3S_PCI_MSIRCVWADRL contains only bits 31..3 of the MSI
>> receive window address low, AFAIU. Using FIELD_GET() for bits 31..3 on the
>> value read from RZG3S_PCI_MSIRCVWADRL and passing this value to
>> msg->address_lo will lead to an NVMe device not working.
> 
> Oops, yes you are right, I went a bit too far with the FIELD_GET()
> suggestion. But replacing drop_mask by RZG3S_PCI_MSIRCVWADRL_ADDR
> would still be worthwhile, IMHO.

OK, you mean updating it like:

+#define RZG3S_PCI_MSIRCVWADRL_ADDR  GENMASK(31, 3)

// ...


-    lo = readl_relaxed(host->axi + RZG3S_PCI_MSIRCVWADRL) & ~drop_mask;
+    lo = readl_relaxed(host->axi + RZG3S_PCI_MSIRCVWADRL) &
          RZG3S_PCI_MSIRCVWADRL_ADDR;

Thank you for your review,
Claudiu

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

* Re: [PATCH v5 2/6] PCI: rzg3s-host: Add Renesas RZ/G3S SoC host driver
  2025-10-23 11:23         ` Claudiu Beznea
@ 2025-10-23 14:21           ` Geert Uytterhoeven
  0 siblings, 0 replies; 29+ messages in thread
From: Geert Uytterhoeven @ 2025-10-23 14:21 UTC (permalink / raw)
  To: Claudiu Beznea
  Cc: lpieralisi, kwilczynski, mani, robh, bhelgaas, krzk+dt, conor+dt,
	magnus.damm, p.zabel, linux-pci, linux-renesas-soc, devicetree,
	linux-kernel, Claudiu Beznea, Wolfram Sang

Hi Claudiu,

On Thu, 23 Oct 2025 at 16:13, Claudiu Beznea <claudiu.beznea@tuxon.dev> wrote:
> On 10/23/25 14:02, Geert Uytterhoeven wrote:
> > On Thu, 23 Oct 2025 at 12:54, Claudiu Beznea <claudiu.beznea@tuxon.dev> wrote:
> >> On 10/23/25 11:00, Geert Uytterhoeven wrote:
> >>> On Tue, 7 Oct 2025 at 15:37, Claudiu <claudiu.beznea@tuxon.dev> 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.
> >>>>
> >>>> Hardware manual can be downloaded from the address in the "Link" section.
> >>>> The following steps should be followed to access the manual:
> >>>> 1/ Click the "User Manual" button
> >>>> 2/ Click "Confirm"; this will start downloading an archive
> >>>> 3/ Open the downloaded archive
> >>>> 4/ Navigate to r01uh1014ej*-rzg3s-users-manual-hardware -> Deliverables
> >>>> 5/ Open the file r01uh1014ej*-rzg3s.pdf
> >>>>
> >>>> Link: https://www.renesas.com/en/products/rz-g3s?queryID=695cc067c2d89e3f271d43656ede4d12
> >>>> Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
> >>>> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> >>>
> >>> Thanks for your patch!
> >>>
> >>>> --- /dev/null
> >>>> +++ b/drivers/pci/controller/pcie-rzg3s-host.c
> >>>
> >>>> +static void rzg3s_pcie_irq_compose_msi_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 drop_mask = RZG3S_PCI_MSIRCVWADRL_ENA |
> >>>> +                       RZG3S_PCI_MSIRCVWADRL_MSG_DATA_ENA;
> >>>
> >>> This should include bit 2 (which is hardwired to zero (for now)),
> >>> so I think you better add
> >>>
> >>>     #define RZG3S_PCI_MSIRCVWADRL_ADDR  GENMASK(31, 3)
> >>>
> >>>> +       u32 lo, hi;
> >>>> +
> >>>> +       /*
> >>>> +        * Enable and msg data enable bits are part of the address lo. Drop
> >>>> +        * them.
> >>>> +        */
> >>>> +       lo = readl_relaxed(host->axi + RZG3S_PCI_MSIRCVWADRL) & ~drop_mask;
> >>>
> >>> ... and use FIELD_GET() with the new definition here.
> >>
> >> Bits 31..3 of RZG3S_PCI_MSIRCVWADRL contains only bits 31..3 of the MSI
> >> receive window address low, AFAIU. Using FIELD_GET() for bits 31..3 on the
> >> value read from RZG3S_PCI_MSIRCVWADRL and passing this value to
> >> msg->address_lo will lead to an NVMe device not working.
> >
> > Oops, yes you are right, I went a bit too far with the FIELD_GET()
> > suggestion. But replacing drop_mask by RZG3S_PCI_MSIRCVWADRL_ADDR
> > would still be worthwhile, IMHO.
>
> OK, you mean updating it like:
>
> +#define RZG3S_PCI_MSIRCVWADRL_ADDR  GENMASK(31, 3)
>
> // ...
>
>
> -    lo = readl_relaxed(host->axi + RZG3S_PCI_MSIRCVWADRL) & ~drop_mask;
> +    lo = readl_relaxed(host->axi + RZG3S_PCI_MSIRCVWADRL) &
>           RZG3S_PCI_MSIRCVWADRL_ADDR;

Exactly.
Thanks!

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

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

* Re: [PATCH v5 2/6] PCI: rzg3s-host: Add Renesas RZ/G3S SoC host driver
  2025-10-23  5:11     ` Claudiu Beznea
@ 2025-10-23 15:55       ` Bjorn Helgaas
  2025-10-24  6:18         ` Claudiu Beznea
  0 siblings, 1 reply; 29+ messages in thread
From: Bjorn Helgaas @ 2025-10-23 15:55 UTC (permalink / raw)
  To: Claudiu Beznea
  Cc: lpieralisi, kwilczynski, mani, robh, bhelgaas, krzk+dt, conor+dt,
	geert+renesas, magnus.damm, p.zabel, linux-pci, linux-renesas-soc,
	devicetree, linux-kernel, Claudiu Beznea, Wolfram Sang

On Thu, Oct 23, 2025 at 08:11:17AM +0300, Claudiu Beznea wrote:
> On 10/22/25 22:49, Bjorn Helgaas wrote:
> > On Tue, Oct 07, 2025 at 04:36:53PM +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.

> >> +++ b/drivers/pci/controller/pcie-rzg3s-host.c
> > 
> >> +#define RZG3S_PCI_MSIRCVWMSKL			0x108
> >> +#define RZG3S_PCI_MSIRCVWMSKL_MASK		GENMASK(31, 2)
> > 
> > Unfortunate to have to add _MASK here when none of the other GENMASKs
> > need it.  Can't think of a better name though.
> 
> Most of the register offsets and fields defines tried to use the naming
> from the HW manual. ...

It's OK as-is.

> >> +static int rzg3s_pcie_msi_enable(struct rzg3s_pcie_host *host)
> >> +{
> >> +	struct platform_device *pdev = to_platform_device(host->dev);
> >> +	struct rzg3s_pcie_msi *msi = &host->msi;
> >> +	struct device *dev = host->dev;
> >> +	const char *devname;
> >> +	int irq, ret;
> >> +
> >> +	ret = devm_mutex_init(dev, &msi->map_lock);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	msi->irq = platform_get_irq_byname(pdev, "msi");
> >> +	if (msi->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 = request_irq(msi->irq, rzg3s_pcie_msi_irq, 0, devname, host);
> > 
> > Should this be devm_request_irq()?  Most drivers use it, although
> > pci-tegra.c and pcie-apple.c do not.  Maybe there's some special
> > rule about using request_irq() even though the driver uses devm in
> > general?  I dunno.
> 
> In general is not good to mix devm cleanups with driver specific
> one.
> 
> As it was requested to drop the devm cleanups from this driver
> (especially devm_pm_runtime_enable() which enables the also the
> clocks) I switched the initial devm_request_irq() to request_irq()
> to avoid keeping the interrupt requested on error path, after
> driver's probed was executed, until devm cleanups are called, and
> potentially having it firing w/o hardware resourced being enabled
> (e.g. clocks), and potentially reading HW registers.

I couldn't find that request to drop devm, and I don't see where
devm_pm_runtime_enable() enables clocks.

> E.g., accessing the HW registers while clocks are disabled on the
> SoC I'm working with leads to synchronous aborts.
> 
> So, I only kept the devm helpers for memory allocations, resets
> assert/de-assert and the mutex initialization.

I'm OK with request_irq() here since you have a good reason.  This
problem of accessing registers while clocks are disabled sounds
familiar, so I think other hardware has a similar issue.  It would be
nice if everybody handled it the same way.

I don't know enough to identify other similar hardware and see how
they handled it (or identify drivers that *don't* handle it).  It
might be worth a one-line comment here to help future code readers.

> >> +static int rzg3s_pcie_intx_setup(struct rzg3s_pcie_host *host)
> >> +{
> >> +	struct device *dev = host->dev;
> >> +
> >> +	for (int i = 0; i < PCI_NUM_INTX; i++) {
> >> +		struct platform_device *pdev = to_platform_device(dev);
> > 
> > Looks like this should be outside the loop.
> 
> OK, I kept it here as it is used only inside this block.

Ah, I see the motivation.  I suppose the compiler is smarter than I am
and hoists it out of the loop anyway, but I think it is easier for
humans to read if the loop only contains things that change for each
iteration.

> >> +		char irq_name[5] = {0};
> >> +		int irq;
> >> +
> >> +		scnprintf(irq_name, ARRAY_SIZE(irq_name), "int%c", 'a' + i);
> >> +
> >> +		irq = platform_get_irq_byname(pdev, irq_name);
> >> +		if (irq < 0)
> >> +			return dev_err_probe(dev, -EINVAL,
> >> +					     "Failed to parse and map INT%c IRQ\n",
> >> +					     'A' + i);
> >> +
> >> +		host->intx_irqs[i] = irq;
> >> +		irq_set_chained_handler_and_data(irq,
> >> +						 rzg3s_pcie_intx_irq_handler,
> >> +						 host);
> >> +	}

> +     host->intx_domain = irq_domain_create_linear(of_fwnode_handle(dev->of_node),
> +                                                  PCI_NUM_INTX,
> +                                                  &rzg3s_pcie_intx_domain_ops,
> +                                                  host);
> ...
> +     irq_domain_update_bus_token(host->intx_domain, DOMAIN_BUS_WIRED);
> +

Can we use dev_fwnode(dev) here instead of of_fwnode_handle() so it
matches the one in rzg3s_pcie_msi_allocate_domains()?

I think irq_domain_update_bus_token() is needed here because
host->intx_domain and msi->domain are identified by the same fwnode,
and this will be easier to see if we get the fwnode the same way.

(See 61d0a000b774 ("genirq/irqdomain: Add irq_domain_update_bus_token
helper").  I wish irq_domain_update_bus_token() had a function comment
to this effect.  Maybe even a mention in Documentation/.)

It would also help code readers if we could use function names similar
to other drivers.  For instance, rzg3s_pcie_intx_setup() creates the
INTx IRQ domain, but no other driver uses a name like *_intx_setup().
The general consensus seems to be *_pcie_init_irq_domain().

Bjorn

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

* Re: [PATCH v5 2/6] PCI: rzg3s-host: Add Renesas RZ/G3S SoC host driver
  2025-10-23 15:55       ` Bjorn Helgaas
@ 2025-10-24  6:18         ` Claudiu Beznea
  0 siblings, 0 replies; 29+ messages in thread
From: Claudiu Beznea @ 2025-10-24  6:18 UTC (permalink / raw)
  To: Bjorn Helgaas
  Cc: lpieralisi, kwilczynski, mani, robh, bhelgaas, krzk+dt, conor+dt,
	geert+renesas, magnus.damm, p.zabel, linux-pci, linux-renesas-soc,
	devicetree, linux-kernel, Claudiu Beznea, Wolfram Sang

Hi, Bjorn,

On 10/23/25 18:55, Bjorn Helgaas wrote:
> On Thu, Oct 23, 2025 at 08:11:17AM +0300, Claudiu Beznea wrote:
>> On 10/22/25 22:49, Bjorn Helgaas wrote:
>>> On Tue, Oct 07, 2025 at 04:36:53PM +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.
> 
>>>> +++ b/drivers/pci/controller/pcie-rzg3s-host.c
>>>
>>>> +#define RZG3S_PCI_MSIRCVWMSKL			0x108
>>>> +#define RZG3S_PCI_MSIRCVWMSKL_MASK		GENMASK(31, 2)
>>>
>>> Unfortunate to have to add _MASK here when none of the other GENMASKs
>>> need it.  Can't think of a better name though.
>>
>> Most of the register offsets and fields defines tried to use the naming
>> from the HW manual. ...
> 
> It's OK as-is.
> 
>>>> +static int rzg3s_pcie_msi_enable(struct rzg3s_pcie_host *host)
>>>> +{
>>>> +	struct platform_device *pdev = to_platform_device(host->dev);
>>>> +	struct rzg3s_pcie_msi *msi = &host->msi;
>>>> +	struct device *dev = host->dev;
>>>> +	const char *devname;
>>>> +	int irq, ret;
>>>> +
>>>> +	ret = devm_mutex_init(dev, &msi->map_lock);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	msi->irq = platform_get_irq_byname(pdev, "msi");
>>>> +	if (msi->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 = request_irq(msi->irq, rzg3s_pcie_msi_irq, 0, devname, host);
>>>
>>> Should this be devm_request_irq()?  Most drivers use it, although
>>> pci-tegra.c and pcie-apple.c do not.  Maybe there's some special
>>> rule about using request_irq() even though the driver uses devm in
>>> general?  I dunno.
>>
>> In general is not good to mix devm cleanups with driver specific
>> one.
>>
>> As it was requested to drop the devm cleanups from this driver
>> (especially devm_pm_runtime_enable() which enables the also the
>> clocks) I switched the initial devm_request_irq() to request_irq()
>> to avoid keeping the interrupt requested on error path, after
>> driver's probed was executed, until devm cleanups are called, and
>> potentially having it firing w/o hardware resourced being enabled
>> (e.g. clocks), and potentially reading HW registers.
> 
> I couldn't find that request to drop devm,

In v3 and v4 it was requested to drop the devm_add_action_or_reset() who's
purpose was to allow using the currently generalized devm helpers (like
devm_request_threaded_irq()) and don't mix devm cleanup with driver
specific cleanup:

v4 -
https://lore.kernel.org/all/pnph54wv3736lemzren64ig4karlulffkvmc3dzgrhgyv2cpwu@2mcgvlqdr6wu/
v3 -
https://lore.kernel.org/all/ddxayjj5wcuuish4kvyluzrujkes5seo7zlusmomyjfjcgzcyj@xe3zzzmy2zaj/

> and I don't see where
> devm_pm_runtime_enable() enables clocks.

Sorry, I wanted to refer to pm_runtime_resume_and_get() and its devm
cleanup helper implemented in v3, v4:

+	/*
+	 * Controller clocks are part of a clock power domain. Enable them
+	 * through runtime PM.
+	 */
+	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;


> 
>> E.g., accessing the HW registers while clocks are disabled on the
>> SoC I'm working with leads to synchronous aborts.
>>
>> So, I only kept the devm helpers for memory allocations, resets
>> assert/de-assert and the mutex initialization.
> 
> I'm OK with request_irq() here since you have a good reason.  This
> problem of accessing registers while clocks are disabled sounds
> familiar, so I think other hardware has a similar issue.  It would be
> nice if everybody handled it the same way.
> 
> I don't know enough to identify other similar hardware and see how
> they handled it (or identify drivers that *don't* handle it).  It
> might be worth a one-line comment here to help future code readers.

OK, I'll add a comment on top of request_irq() call.

> 
>>>> +static int rzg3s_pcie_intx_setup(struct rzg3s_pcie_host *host)
>>>> +{
>>>> +	struct device *dev = host->dev;
>>>> +
>>>> +	for (int i = 0; i < PCI_NUM_INTX; i++) {
>>>> +		struct platform_device *pdev = to_platform_device(dev);
>>>
>>> Looks like this should be outside the loop.
>>
>> OK, I kept it here as it is used only inside this block.
> 
> Ah, I see the motivation.  I suppose the compiler is smarter than I am
> and hoists it out of the loop anyway, but I think it is easier for
> humans to read if the loop only contains things that change for each
> iteration.

OK, I'll move pdev out of this block.

> 
>>>> +		char irq_name[5] = {0};
>>>> +		int irq;
>>>> +
>>>> +		scnprintf(irq_name, ARRAY_SIZE(irq_name), "int%c", 'a' + i);
>>>> +
>>>> +		irq = platform_get_irq_byname(pdev, irq_name);
>>>> +		if (irq < 0)
>>>> +			return dev_err_probe(dev, -EINVAL,
>>>> +					     "Failed to parse and map INT%c IRQ\n",
>>>> +					     'A' + i);
>>>> +
>>>> +		host->intx_irqs[i] = irq;
>>>> +		irq_set_chained_handler_and_data(irq,
>>>> +						 rzg3s_pcie_intx_irq_handler,
>>>> +						 host);
>>>> +	}
> 
>> +     host->intx_domain = irq_domain_create_linear(of_fwnode_handle(dev->of_node),
>> +                                                  PCI_NUM_INTX,
>> +                                                  &rzg3s_pcie_intx_domain_ops,
>> +                                                  host);
>> ...
>> +     irq_domain_update_bus_token(host->intx_domain, DOMAIN_BUS_WIRED);
>> +
> 
> Can we use dev_fwnode(dev) here instead of of_fwnode_handle() so it
> matches the one in rzg3s_pcie_msi_allocate_domains()?

Sure, I'll update it next time.

> 
> I think irq_domain_update_bus_token() is needed here because
> host->intx_domain and msi->domain are identified by the same fwnode,
> and this will be easier to see if we get the fwnode the same way.
> 
> (See 61d0a000b774 ("genirq/irqdomain: Add irq_domain_update_bus_token
> helper").  I wish irq_domain_update_bus_token() had a function comment
> to this effect.  Maybe even a mention in Documentation/.)
> 
> It would also help code readers if we could use function names similar
> to other drivers.  For instance, rzg3s_pcie_intx_setup() creates the
> INTx IRQ domain, but no other driver uses a name like *_intx_setup().
> The general consensus seems to be *_pcie_init_irq_domain().

I tried initially to align all the function name with the patterns I found
though the tree as you suggested.

With the pattern for MSI/INTx configuration I wanted to re-use the same
probe code on resume path. My point was to re-use the
rzg3s_pcie_host_setup() on resume, and on resume to configure only the HW
registers for MSI, INTx (as the rest is not necessary) by passing different
MSI, INTx setup functions along with their correspondent cleanup functions
for failures.

For the function names rzg3s_pcie_intx_setup(), rzg3s_pcie_msi_setup() I
took as reference (probably) tegra_pcie_msi_setup().

I'll try to switch to *_pcie_init_irq_domain() pattern if this is the
preferred one.

Thank you for your review,
Claudiu

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

end of thread, other threads:[~2025-10-24  6:19 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-07 13:36 [PATCH v5 0/6] PCI: rzg3s-host: Add PCIe driver for Renesas RZ/G3S SoC Claudiu
2025-10-07 13:36 ` [PATCH v5 1/6] dt-bindings: PCI: renesas,r9a08g045s33-pcie: Add documentation for the PCIe IP on Renesas RZ/G3S Claudiu
2025-10-10 14:29   ` Rob Herring
2025-10-10 14:32   ` Rob Herring
2025-10-07 13:36 ` [PATCH v5 2/6] PCI: rzg3s-host: Add Renesas RZ/G3S SoC host driver Claudiu
2025-10-19  6:52   ` Manivannan Sadhasivam
2025-10-22 19:49   ` Bjorn Helgaas
2025-10-23  5:11     ` Claudiu Beznea
2025-10-23 15:55       ` Bjorn Helgaas
2025-10-24  6:18         ` Claudiu Beznea
2025-10-23  7:55     ` Geert Uytterhoeven
2025-10-23  8:00   ` Geert Uytterhoeven
2025-10-23  9:34     ` Claudiu Beznea
2025-10-23 11:02       ` Geert Uytterhoeven
2025-10-23 11:23         ` Claudiu Beznea
2025-10-23 14:21           ` Geert Uytterhoeven
2025-10-07 13:36 ` [PATCH v5 3/6] arm64: dts: renesas: r9a08g045: Add PCIe node Claudiu
2025-10-07 13:44   ` Biju Das
2025-10-10 11:17     ` Claudiu Beznea
2025-10-10 11:36       ` Biju Das
2025-10-11  1:39         ` Biju Das
2025-10-19  6:57   ` Manivannan Sadhasivam
2025-10-20  6:15     ` Claudiu Beznea
2025-10-21  2:16       ` Manivannan Sadhasivam
2025-10-07 13:36 ` [PATCH v5 4/6] arm64: dts: renesas: rzg3s-smarc-som: Add PCIe reference clock Claudiu
2025-10-08 12:15   ` Geert Uytterhoeven
2025-10-07 13:36 ` [PATCH v5 5/6] arm64: dts: renesas: rzg3s-smarc: Enable PCIe Claudiu
2025-10-07 13:36 ` [PATCH v5 6/6] arm64: defconfig: Enable PCIe for the Renesas RZ/G3S SoC Claudiu
2025-10-08 12:12   ` Geert Uytterhoeven

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