From f0a5c6bd797c2b9900034983a1118d27eb2f2f16 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 21 Aug 2026 04:05:51 +0200 Subject: [PATCH 01/14] PCI: rcar-gen4: Limit Max_Read_Request_Size to 256 Bytes R-Car Gen4 PCIe controller has a hardware limitation of 256 Bytes Max_Payload_Size (MPS). PCIe specification indicates that the MPS must not exceed minimum MPS of any element along the packet path. The PCI core already enforces this, but add a check to warn if MPS is ever set larger than 256. R-Car Gen4 Reference Manual, chapter 104.4.8 Usage notes for MRRS (Max Read Request Size) states: Please set "Max Read Request Size" to 128 bytes or 256 bytes. If "Max Read Request Size" is set to anything other than the above, the transferred data will not match the expected value. This limitation also seems to apply to devices doing DMA reads, i.e., issuing MRd TLPs. This limitation can be triggered by using non-HMB NVMe SSD with Max_Read_Request_Size 512 Bytes, for example Crucial P5 Plus. Any write to the SSD (MRd TLP issued by the SSD controller) longer than 256 Bytes wraps around at the 256 Byte boundary, and the same data are written to the SSD starting at offset 0 and at 256 Bytes. Limit Max_Read_Request_Size to at most 256 Bytes for each device connected to this PCIe controller to avoid this behavior. A non-HMB (Host Memory Buffer) NVMe SSD can be identified using the following command. Affected SSD reports 'hmpre' field as 0: $ nvme id-ctrl /dev/nvme0 | grep hmpre hmpre : 0 The symptom is a read from the SSD which wraps around at 256 Byte boundary. The test for this symptom can be implemented by writing 512 Byte of random data into the SSD and reading the data back. If the read back data repeat after 256 Bytes, the device is affected. $ dd if=/dev/urandom of=/tmp/data.bin bs=256 count=2 ; \ dd if=/tmp/data.bin of=/dev/nvme0n1 bs=256 count=2 ; \ dd if=/dev/nvme0n1 bs=256 count=2 of=/tmp/readback.bin Expected data: $ hexdump -vC /tmp/data.bin 00000000 97 81 b7 3b 0e 38 2b 4d a7 d3 e0 47 ff c2 4b ca 00000010 c1 85 98 f0 4a ac 03 a0 3b ab f3 19 44 dd 06 8b ... 00000100 7a ce 3c b2 e1 d5 d9 11 88 63 10 59 76 3c dc 32 <-- random 00000110 72 32 2a 7d a3 e1 aa 13 7c da 58 a1 7b 21 11 50 <-- data Faulty readback, collected without this change in place: $ hexdump -vC /tmp/readback.bin 00000000 97 81 b7 3b 0e 38 2b 4d a7 d3 e0 47 ff c2 4b ca <---. 00000010 c1 85 98 f0 4a ac 03 a0 3b ab f3 19 44 dd 06 8b <-. | ... | | 00000100 97 81 b7 3b 0e 38 2b 4d a7 d3 e0 47 ff c2 4b ca <-:-+- repeated 00000110 c1 85 98 f0 4a ac 03 a0 3b ab f3 19 44 dd 06 8b <-+--- data ^^^ | '--- Repeat starts at offset 0x100 = 256 Bytes Fixes: 0d0c551011df ("PCI: rcar-gen4: Add R-Car Gen4 PCIe controller support for host mode") Signed-off-by: Marek Vasut Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260821020636.111719-1-marek.vasut+renesas@mailbox.org --- drivers/pci/controller/dwc/pcie-rcar-gen4.c | 54 +++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c index fbe465a29068..241adcd72789 100644 --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -411,6 +411,57 @@ static int rcar_gen4_pcie_host_msi_init(struct dw_pcie_rp *pp) return ret; } +static int rcar_gen4_pcie_enable_device(struct pci_host_bridge *bridge, + struct pci_dev *dev) +{ + /* + * R-Car Gen4 PCIe controller has a hardware limitation of 256 Bytes + * Max_Payload_Size (MPS). PCIe specification indicates that the MPS + * must not exceed minimum MPS of any element along the packet path. + * The controller reports Max_Payload_Size_Supported (MPSS) 256 Bytes + * for header type 0 and 128 Bytes for header type 1. The PCIe core + * will not allow MPS to be set higher than MPSS; warn here in case + * something went very wrong in the core. + * + * For details, refer to chapter "104.1.1 Features" in either of: + * R-Car S4 R19UH0161EJ0140 Rev.1.40 Jul. 31, 2026 or + * R-Car V4H R19UH0186EJ0140 Rev.1.40 Aug. 7, 2026 or + * R-Car V4M R19UH0217EJ0110 Rev.1.10 Jun. 30, 2026. + */ + WARN_ON(pcie_get_mps(dev) > 256); + + /* + * R-Car Gen4 Reference Manual, chapter 104.4.8 Usage notes for + * MRRS (Max Read Request Size) states: + * + * Please set "Max Read Request Size" to 128 bytes or 256 bytes. + * If "Max Read Request Size" is set to anything other than the + * above, the transferred data will not match the expected value. + * + * This limitation also seems to apply to devices issuing MRd TLPs. + * This limitation can be triggered by using non-HMB NVMe SSD with + * Max_Read_Request_Size 512 Bytes, for example Crucial P5 Plus. + * Any write into the SSD (MRd TLP issued by the SSD) longer than + * 256 Bytes wraps around at 256 Byte boundary, and the same data + * are written into the SSD starting at offset 0 and at 256 Bytes. + * + * Limit Max_Read_Request_Size to at most 256 Bytes for each + * device connected to this PCIe controller to avoid this behavior. + * + * For details, refer to aforementioned chapter in either of: + * R-Car S4 R19UH0161EJ0140 Rev.1.40 Jul. 31, 2026 or + * R-Car V4H R19UH0186EJ0140 Rev.1.40 Aug. 7, 2026 or + * R-Car V4M R19UH0217EJ0110 Rev.1.10 Jun. 30, 2026. + */ + bridge->no_inc_mrrs = 1; + if (pcie_get_readrq(dev) > 256) { + pci_info(dev, "Limiting MRRS to 256 bytes\n"); + pcie_set_readrq(dev, 256); + } + + return 0; +} + /* Host mode */ static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp) { @@ -418,6 +469,9 @@ static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp) struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw); int ret; + if (pp->bridge) + pp->bridge->enable_device = rcar_gen4_pcie_enable_device; + gpiod_set_value_cansleep(dw->pe_rst, 1); ret = rcar_gen4_pcie_common_init(rcar); From 355039f010cee6d90bc76bc1bccf2afe12d8d461 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 21 Aug 2026 14:27:49 +0200 Subject: [PATCH 02/14] PCI: dwc: Add dw_pcie_ep_ops->post_deinit() callback If .pre_init() succeeds but subsequent initialization fails, it is mandatory to correctly undo the hardware configuration which the .pre_init() callback did. Introduce a .post_deinit() callback to do exactly that, undo what the .pre_init() callback did. Usually, that means stop the clock, assert reset, and possibly program some registers to quiesce the hardware. Invoke the .post_deinit() callback in dw_pcie_ep_deinit() to retain functional symmetry. Signed-off-by: Marek Vasut Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Koichiro Den Reviewed-by: Koichiro Den Link: https://patch.msgid.link/20260821122808.238248-1-marek.vasut+renesas@mailbox.org --- drivers/pci/controller/dwc/pcie-designware-ep.c | 8 +++++++- drivers/pci/controller/dwc/pcie-designware.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index de8ee3db4360..1a3491b5003e 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -1194,6 +1194,9 @@ void dw_pcie_ep_deinit(struct dw_pcie_ep *ep) epc->mem->window.page_size); pci_epc_mem_exit(epc); + + if (ep->ops->post_deinit) + ep->ops->post_deinit(ep); } EXPORT_SYMBOL_GPL(dw_pcie_ep_deinit); @@ -1553,7 +1556,7 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) ep->page_size); if (ret < 0) { dev_err(dev, "Failed to initialize address space\n"); - return ret; + goto err_deinit; } ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys, @@ -1568,6 +1571,9 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) err_exit_epc_mem: pci_epc_mem_exit(epc); +err_deinit: + if (ep->ops->post_deinit) + ep->ops->post_deinit(ep); return ret; } diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 0735ae940924..a53ac27cd244 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -475,6 +475,7 @@ struct dw_pcie_rp { struct dw_pcie_ep_ops { int (*pre_init)(struct dw_pcie_ep *ep); + void (*post_deinit)(struct dw_pcie_ep *ep); int (*init)(struct dw_pcie_ep *ep); int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no, unsigned int type, u16 interrupt_num); From 0b4b2674688c6a719e93f0e52ddaf6432847bc1a Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 21 Aug 2026 14:27:50 +0200 Subject: [PATCH 03/14] PCI: rcar-gen4: Use .post_deinit() to handle dw_pcie_ep_init() failures Implement .post_deinit() callback in R-Car Gen4 'struct dw_pcie_ep_ops' which asserts reset and stops the clock. This undoes start of clock and deassert of reset performed in .pre_init(), in case dw_pcie_ep_init() fails after successful call of .pre_init() callback. The use of .post_deinit() callback correctly handles the clock and reset unwind, unlike the call of rcar_gen4_pcie_ep_deinit() in dw_pcie_ep_init() which could not discern at which point the dw_pcie_ep_init() failed and can attempt to stop clock and assert reset twice. Since dw_pcie_ep_deinit() also invokes the .post_deinit() callback, drop calls to rcar_gen4_pcie_ep_deinit() in both rcar_gen4_add_dw_pcie_ep() and dw_pcie_ep_init_registers() fail paths and rcar_gen4_remove_dw_pcie_ep() to avoid duplicate stop of clock and assert of reset, and drop no longer used rcar_gen4_pcie_ep_deinit() entirely. Initialize PCIEDMAINTSTSEN early in rcar_gen4_pcie_ep_pre_init() to 0, to make sure that edma_int bits will never be left set in case of failure of dw_pcie_ep_init(), and will only be set in case dw_pcie_ep_init() succeeds. Signed-off-by: Marek Vasut Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Koichiro Den Reviewed-by: Koichiro Den Link: https://patch.msgid.link/20260821122808.238248-2-marek.vasut+renesas@mailbox.org --- drivers/pci/controller/dwc/pcie-rcar-gen4.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c index 241adcd72789..5a076aa3f490 100644 --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -541,6 +541,8 @@ static int rcar_gen4_pcie_ep_pre_init(struct dw_pcie_ep *ep) struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw); int ret; + writel(0, rcar->base + PCIEDMAINTSTSEN); + ret = rcar_gen4_pcie_common_init(rcar); if (ret) return ret; @@ -550,8 +552,11 @@ static int rcar_gen4_pcie_ep_pre_init(struct dw_pcie_ep *ep) return 0; } -static void rcar_gen4_pcie_ep_deinit(struct rcar_gen4_pcie *rcar) +static void rcar_gen4_pcie_ep_post_deinit(struct dw_pcie_ep *ep) { + struct dw_pcie *dw = to_dw_pcie_from_ep(ep); + struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw); + writel(0, rcar->base + PCIEDMAINTSTSEN); rcar_gen4_pcie_common_deinit(rcar); } @@ -606,6 +611,7 @@ static unsigned int rcar_gen4_pcie_ep_get_dbi2_offset(struct dw_pcie_ep *ep, static const struct dw_pcie_ep_ops pcie_ep_ops = { .pre_init = rcar_gen4_pcie_ep_pre_init, + .post_deinit = rcar_gen4_pcie_ep_post_deinit, .raise_irq = rcar_gen4_pcie_ep_raise_irq, .get_features = rcar_gen4_pcie_ep_get_features, .get_dbi_offset = rcar_gen4_pcie_ep_get_dbi_offset, @@ -624,16 +630,13 @@ static int rcar_gen4_add_dw_pcie_ep(struct rcar_gen4_pcie *rcar) ep->ops = &pcie_ep_ops; ret = dw_pcie_ep_init(ep); - if (ret) { - rcar_gen4_pcie_ep_deinit(rcar); + if (ret) return ret; - } ret = dw_pcie_ep_init_registers(ep); if (ret) { dev_err(dev, "Failed to initialize DWC endpoint registers\n"); dw_pcie_ep_deinit(ep); - rcar_gen4_pcie_ep_deinit(rcar); } pci_epc_init_notify(ep->epc); @@ -644,7 +647,6 @@ static int rcar_gen4_add_dw_pcie_ep(struct rcar_gen4_pcie *rcar) static void rcar_gen4_remove_dw_pcie_ep(struct rcar_gen4_pcie *rcar) { dw_pcie_ep_deinit(&rcar->dw.ep); - rcar_gen4_pcie_ep_deinit(rcar); } /* Common */ From 5b555a496160914394c330748fc95e46a1d71800 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 21 Sep 2026 22:27:43 +0200 Subject: [PATCH 04/14] dt-bindings: PCI: rcar-gen4-pci-host: Add R-Car X5H PCIe4 compatible Document bindings for R-Car X5H (R8A78000) PCIe4 host module. The binding document is deliberately using "renesas,rcar-gen5-pcie4" DT compatible string to discern R-Car X5H PCIe4 controller supported by this binding, from R-Car X5H PCIe6 controller which will use a separate binding. The R-Car X5H PCIe4 controller does no longer include PHY register range, the PHY is now a separate IP and referenced via the 'phy' DT property. The 'reg' and 'reg-names' DT properties therefore differ between R-Car Gen4 PCIe and R-Car X5H PCIe4, and the difference is handled in the allOf section. Signed-off-by: Marek Vasut Signed-off-by: Manivannan Sadhasivam Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260921202823.72620-2-marek.vasut+renesas@mailbox.org --- .../bindings/pci/rcar-gen4-pci-host.yaml | 72 +++++++++++++------ 1 file changed, 51 insertions(+), 21 deletions(-) diff --git a/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml b/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml index 64f456c6a75a..9767a52f3904 100644 --- a/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml +++ b/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml @@ -10,30 +10,20 @@ title: Renesas R-Car Gen4 PCIe Host maintainers: - Yoshihiro Shimoda -allOf: - - $ref: snps,dw-pcie.yaml# - properties: compatible: - items: - - enum: - - renesas,r8a779f0-pcie # R-Car S4-8 - - renesas,r8a779g0-pcie # R-Car V4H - - renesas,r8a779h0-pcie # R-Car V4M - - const: renesas,rcar-gen4-pcie # R-Car Gen4 + oneOf: + - items: + - enum: + - renesas,r8a779f0-pcie # R-Car S4-8 + - renesas,r8a779g0-pcie # R-Car V4H + - renesas,r8a779h0-pcie # R-Car V4M + - const: renesas,rcar-gen4-pcie # R-Car Gen4 - reg: - maxItems: 7 - - reg-names: - items: - - const: dbi - - const: dbi2 - - const: atu - - const: dma - - const: app - - const: phy - - const: config + - items: + - enum: + - renesas,r8a78000-pcie4 # R-Car X5H PCIe4 + - const: renesas,rcar-gen5-pcie4 # R-Car Gen5 PCIe4 interrupts: maxItems: 4 @@ -84,6 +74,46 @@ required: - resets - reset-names +allOf: + - $ref: snps,dw-pcie.yaml# + - if: + properties: + compatible: + contains: + const: renesas,rcar-gen4-pcie + then: + properties: + reg: + maxItems: 7 + + reg-names: + items: + - const: dbi + - const: dbi2 + - const: atu + - const: dma + - const: app + - const: phy + - const: config + - if: + properties: + compatible: + contains: + const: renesas,rcar-gen5-pcie4 + then: + properties: + reg: + maxItems: 6 + + reg-names: + items: + - const: dbi + - const: dbi2 + - const: atu + - const: dma + - const: app + - const: config + unevaluatedProperties: false examples: From eeeb72fab4a569c1cc547c9bf220c808dd37f83d Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 21 Sep 2026 22:27:44 +0200 Subject: [PATCH 05/14] PCI: rcar-gen4: Rework .additional_common_init() into .init() Rework the current complicated initialization callchain that invokes host_init->common_init->additional_common_init() for host and ep_init->common_init->additional_common_init() for endpoint into simpler host_init->init() and ep_init->init() . The init callback is now always implemented for all R-Car Gen4 SoCs; therefore there is no need to check whether the callback is not NULL. For R-Car S4, the .init() callback is rcar_gen4_pcie_common_init(), for R-Car V4H and V4M, the .init() callback is implemented using a new function rcar_gen4_v4h_v4m_pcie_init(), which calls the common rcar_gen4_pcie_common_init() followed by R-Car V4H- and V4M-specific hardware initialization. The hardware initialization sequence remains unchanged. Signed-off-by: Marek Vasut Signed-off-by: Manivannan Sadhasivam [bhelgaas: drop 'dwc' tags, commit log typo] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260921202823.72620-3-marek.vasut+renesas@mailbox.org --- drivers/pci/controller/dwc/pcie-rcar-gen4.c | 56 ++++++++++++--------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c index 5a076aa3f490..a19ebbab2f8c 100644 --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -87,7 +87,7 @@ MODULE_FIRMWARE(RCAR_GEN4_PCIE_FIRMWARE_NAME); struct rcar_gen4_pcie; struct rcar_gen4_pcie_drvdata { - void (*additional_common_init)(struct rcar_gen4_pcie *rcar); + int (*init)(struct rcar_gen4_pcie *rcar); int (*ltssm_control)(struct rcar_gen4_pcie *rcar, bool enable); enum dw_pcie_device_mode mode; }; @@ -241,9 +241,6 @@ static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar) reset_control_status(dw->core_rsts[DW_PCIE_PWR_RST].rstc); fsleep(1000); - if (rcar->drvdata->additional_common_init) - rcar->drvdata->additional_common_init(rcar); - return 0; err_unprepare: @@ -252,6 +249,31 @@ static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar) return ret; } +static int rcar_gen4_v4h_v4m_pcie_init(struct rcar_gen4_pcie *rcar) +{ + struct dw_pcie *dw = &rcar->dw; + u32 val; + int ret; + + /* R-Car Gen4 common initialization. */ + ret = rcar_gen4_pcie_common_init(rcar); + if (ret) + return ret; + + /* R-Car V4H and V4M specific additional initialization. */ + val = dw_pcie_readl_dbi(dw, PCIE_PORT_LANE_SKEW); + val &= ~PORT_LANE_SKEW_INSERT_MASK; + if (dw->num_lanes < 4) + val |= BIT(6); + dw_pcie_writel_dbi(dw, PCIE_PORT_LANE_SKEW, val); + + val = readl(rcar->base + PCIEPWRMNGCTRL); + val |= APP_CLK_REQ_N | APP_CLK_PM_EN; + writel(val, rcar->base + PCIEPWRMNGCTRL); + + return 0; +} + static void rcar_gen4_pcie_common_deinit(struct rcar_gen4_pcie *rcar) { struct dw_pcie *dw = &rcar->dw; @@ -474,7 +496,7 @@ static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp) gpiod_set_value_cansleep(dw->pe_rst, 1); - ret = rcar_gen4_pcie_common_init(rcar); + ret = rcar->drvdata->init(rcar); if (ret) return ret; @@ -543,7 +565,7 @@ static int rcar_gen4_pcie_ep_pre_init(struct dw_pcie_ep *ep) writel(0, rcar->base + PCIEDMAINTSTSEN); - ret = rcar_gen4_pcie_common_init(rcar); + ret = rcar->drvdata->init(rcar); if (ret) return ret; @@ -739,22 +761,6 @@ static int r8a779f0_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable) return 0; } -static void rcar_gen4_pcie_additional_common_init(struct rcar_gen4_pcie *rcar) -{ - struct dw_pcie *dw = &rcar->dw; - u32 val; - - val = dw_pcie_readl_dbi(dw, PCIE_PORT_LANE_SKEW); - val &= ~PORT_LANE_SKEW_INSERT_MASK; - if (dw->num_lanes < 4) - val |= BIT(6); - dw_pcie_writel_dbi(dw, PCIE_PORT_LANE_SKEW, val); - - val = readl(rcar->base + PCIEPWRMNGCTRL); - val |= APP_CLK_REQ_N | APP_CLK_PM_EN; - writel(val, rcar->base + PCIEPWRMNGCTRL); -} - static void rcar_gen4_pcie_phy_reg_update_bits(struct rcar_gen4_pcie *rcar, u32 offset, u32 mask, u32 val) { @@ -906,23 +912,25 @@ static int rcar_gen4_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable } static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie = { + .init = rcar_gen4_pcie_common_init, .ltssm_control = r8a779f0_pcie_ltssm_control, .mode = DW_PCIE_RC_TYPE, }; static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie_ep = { + .init = rcar_gen4_pcie_common_init, .ltssm_control = r8a779f0_pcie_ltssm_control, .mode = DW_PCIE_EP_TYPE, }; static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie = { - .additional_common_init = rcar_gen4_pcie_additional_common_init, + .init = rcar_gen4_v4h_v4m_pcie_init, .ltssm_control = rcar_gen4_pcie_ltssm_control, .mode = DW_PCIE_RC_TYPE, }; static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie_ep = { - .additional_common_init = rcar_gen4_pcie_additional_common_init, + .init = rcar_gen4_v4h_v4m_pcie_init, .ltssm_control = rcar_gen4_pcie_ltssm_control, .mode = DW_PCIE_EP_TYPE, }; From a9b2dc6de8d0e0a040fefe04636c82287cdf7845 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 21 Sep 2026 22:27:45 +0200 Subject: [PATCH 06/14] PCI: rcar-gen4: Add .deinit() callback Add rcar_gen4_pcie_drvdata->deinit() callback in preparation for R-Car Gen5 support. The .deinit() callback will be used to tear down the PHY on R-Car Gen5. For now, it does what rcar_gen4_pcie_common_deinit() used to do directly. Signed-off-by: Marek Vasut Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260921202823.72620-4-marek.vasut+renesas@mailbox.org --- drivers/pci/controller/dwc/pcie-rcar-gen4.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c index a19ebbab2f8c..04871ac11c34 100644 --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -88,6 +88,7 @@ MODULE_FIRMWARE(RCAR_GEN4_PCIE_FIRMWARE_NAME); struct rcar_gen4_pcie; struct rcar_gen4_pcie_drvdata { int (*init)(struct rcar_gen4_pcie *rcar); + void (*deinit)(struct rcar_gen4_pcie *rcar); int (*ltssm_control)(struct rcar_gen4_pcie *rcar, bool enable); enum dw_pcie_device_mode mode; }; @@ -520,7 +521,7 @@ static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp) return 0; err: - rcar_gen4_pcie_common_deinit(rcar); + rcar->drvdata->deinit(rcar); return ret; } @@ -530,7 +531,7 @@ static void rcar_gen4_pcie_host_deinit(struct dw_pcie_rp *pp) struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw); gpiod_set_value_cansleep(dw->pe_rst, 1); - rcar_gen4_pcie_common_deinit(rcar); + rcar->drvdata->deinit(rcar); } static const struct dw_pcie_host_ops rcar_gen4_pcie_host_ops = { @@ -580,7 +581,7 @@ static void rcar_gen4_pcie_ep_post_deinit(struct dw_pcie_ep *ep) struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw); writel(0, rcar->base + PCIEDMAINTSTSEN); - rcar_gen4_pcie_common_deinit(rcar); + rcar->drvdata->deinit(rcar); } static int rcar_gen4_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, @@ -913,24 +914,28 @@ static int rcar_gen4_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie = { .init = rcar_gen4_pcie_common_init, + .deinit = rcar_gen4_pcie_common_deinit, .ltssm_control = r8a779f0_pcie_ltssm_control, .mode = DW_PCIE_RC_TYPE, }; static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie_ep = { .init = rcar_gen4_pcie_common_init, + .deinit = rcar_gen4_pcie_common_deinit, .ltssm_control = r8a779f0_pcie_ltssm_control, .mode = DW_PCIE_EP_TYPE, }; static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie = { .init = rcar_gen4_v4h_v4m_pcie_init, + .deinit = rcar_gen4_pcie_common_deinit, .ltssm_control = rcar_gen4_pcie_ltssm_control, .mode = DW_PCIE_RC_TYPE, }; static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie_ep = { .init = rcar_gen4_v4h_v4m_pcie_init, + .deinit = rcar_gen4_pcie_common_deinit, .ltssm_control = rcar_gen4_pcie_ltssm_control, .mode = DW_PCIE_EP_TYPE, }; From bb0cf158cfa61d4b5336bdc1bde1ac515ffe2871 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 21 Sep 2026 22:27:46 +0200 Subject: [PATCH 07/14] PCI: rcar-gen4: Add .speed_control() callback Introduce new rcar_gen4_pcie_drvdata->speed_control() callback and implement it on R-Car Gen4. The callback implements the second half of what is currently present in rcar_gen4_pcie_start_link(), and rcar_gen4_pcie_start_link() does call the .speed_control() callback. This is a preparatory work for R-Car Gen5 support, where the .speed_control() implementation is different. Signed-off-by: Marek Vasut [mani: commit log] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260921202823.72620-5-marek.vasut+renesas@mailbox.org --- drivers/pci/controller/dwc/pcie-rcar-gen4.c | 39 ++++++++++++++------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c index 04871ac11c34..c768a9c7b3b7 100644 --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -90,6 +90,7 @@ struct rcar_gen4_pcie_drvdata { int (*init)(struct rcar_gen4_pcie *rcar); void (*deinit)(struct rcar_gen4_pcie *rcar); int (*ltssm_control)(struct rcar_gen4_pcie *rcar, bool enable); + int (*speed_control)(struct rcar_gen4_pcie *rcar); enum dw_pcie_device_mode mode; }; @@ -141,20 +142,10 @@ static int rcar_gen4_pcie_speed_change(struct dw_pcie *dw) return -ETIMEDOUT; } -/* - * Enable LTSSM of this controller and manually initiate the speed change. - * Always return 0. - */ -static int rcar_gen4_pcie_start_link(struct dw_pcie *dw) +static int rcar_gen4_pcie_speed_control(struct rcar_gen4_pcie *rcar) { - struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw); - int i, changes, ret; - - if (rcar->drvdata->ltssm_control) { - ret = rcar->drvdata->ltssm_control(rcar, true); - if (ret) - return ret; - } + struct dw_pcie *dw = &rcar->dw; + int i, changes; /* * Require direct speed change with retrying here if the max_link_speed @@ -178,6 +169,24 @@ static int rcar_gen4_pcie_start_link(struct dw_pcie *dw) return 0; } +/* + * Enable LTSSM of this controller and manually initiate the speed change. + * Always return 0. + */ +static int rcar_gen4_pcie_start_link(struct dw_pcie *dw) +{ + struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw); + int ret; + + if (rcar->drvdata->ltssm_control) { + ret = rcar->drvdata->ltssm_control(rcar, true); + if (ret) + return ret; + } + + return rcar->drvdata->speed_control(rcar); +} + static void rcar_gen4_pcie_stop_link(struct dw_pcie *dw) { struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw); @@ -916,6 +925,7 @@ static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie = { .init = rcar_gen4_pcie_common_init, .deinit = rcar_gen4_pcie_common_deinit, .ltssm_control = r8a779f0_pcie_ltssm_control, + .speed_control = rcar_gen4_pcie_speed_control, .mode = DW_PCIE_RC_TYPE, }; @@ -923,6 +933,7 @@ static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie_ep = { .init = rcar_gen4_pcie_common_init, .deinit = rcar_gen4_pcie_common_deinit, .ltssm_control = r8a779f0_pcie_ltssm_control, + .speed_control = rcar_gen4_pcie_speed_control, .mode = DW_PCIE_EP_TYPE, }; @@ -930,6 +941,7 @@ static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie = { .init = rcar_gen4_v4h_v4m_pcie_init, .deinit = rcar_gen4_pcie_common_deinit, .ltssm_control = rcar_gen4_pcie_ltssm_control, + .speed_control = rcar_gen4_pcie_speed_control, .mode = DW_PCIE_RC_TYPE, }; @@ -937,6 +949,7 @@ static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie_ep = { .init = rcar_gen4_v4h_v4m_pcie_init, .deinit = rcar_gen4_pcie_common_deinit, .ltssm_control = rcar_gen4_pcie_ltssm_control, + .speed_control = rcar_gen4_pcie_speed_control, .mode = DW_PCIE_EP_TYPE, }; From 487a731517391bbc6e887ac242fc11d4f61088e8 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 21 Sep 2026 22:27:47 +0200 Subject: [PATCH 08/14] PCI: rcar-gen4: Handle PERST# via reset subsystem Handle PERST# via both GPIO and reset subsystem. On R-Car Gen4, the PERST# signal is operated as a GPIO, on R-Car Gen5 it might only be accessible via SCMI reset via reset subsystem. Support both options. This is a preparatory work for R-Car Gen5 support. Signed-off-by: Marek Vasut Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260921202823.72620-6-marek.vasut+renesas@mailbox.org --- drivers/pci/controller/dwc/pcie-rcar-gen4.c | 41 +++++++++++++++++++-- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c index c768a9c7b3b7..cf1f5de45182 100644 --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -99,6 +99,7 @@ struct rcar_gen4_pcie { void __iomem *base; void __iomem *phy_base; struct platform_device *pdev; + struct reset_control *perst; const struct rcar_gen4_pcie_drvdata *drvdata; }; #define to_rcar_gen4_pcie(_dw) container_of(_dw, struct rcar_gen4_pcie, dw) @@ -317,12 +318,25 @@ static void rcar_gen4_pcie_unprepare(struct rcar_gen4_pcie *rcar) static int rcar_gen4_pcie_get_resources(struct rcar_gen4_pcie *rcar) { + struct device *dev = rcar->dw.dev; + struct device_node *root_port; + rcar->phy_base = devm_platform_ioremap_resource_byname(rcar->pdev, "phy"); if (IS_ERR(rcar->phy_base)) return PTR_ERR(rcar->phy_base); + root_port = of_get_next_available_child(dev->of_node, NULL); + if (root_port) { + rcar->perst = of_reset_control_get_optional_exclusive(root_port, "perst"); + of_node_put(root_port); + if (IS_ERR(rcar->perst)) + return dev_err_probe(dev, PTR_ERR(rcar->perst), "Failed to get PERST#\n"); + } + /* Renesas-specific registers */ rcar->base = devm_platform_ioremap_resource_byname(rcar->pdev, "app"); + if (IS_ERR(rcar->base)) + reset_control_put(rcar->perst); return PTR_ERR_OR_ZERO(rcar->base); } @@ -494,6 +508,21 @@ static int rcar_gen4_pcie_enable_device(struct pci_host_bridge *bridge, return 0; } +static void rcar_gen4_pcie_host_perst_assert(struct dw_pcie_rp *pp, bool assert) +{ + struct dw_pcie *dw = to_dw_pcie_from_pp(pp); + struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw); + + if (dw->pe_rst) { + gpiod_set_value_cansleep(dw->pe_rst, assert); + } else { + if (assert) + reset_control_assert(rcar->perst); + else + reset_control_deassert(rcar->perst); + } +} + /* Host mode */ static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp) { @@ -504,7 +533,7 @@ static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp) if (pp->bridge) pp->bridge->enable_device = rcar_gen4_pcie_enable_device; - gpiod_set_value_cansleep(dw->pe_rst, 1); + rcar_gen4_pcie_host_perst_assert(pp, true); ret = rcar->drvdata->init(rcar); if (ret) @@ -525,7 +554,7 @@ static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp) msleep(PCIE_T_PVPERL_MS); /* pe_rst requires 100msec delay */ - gpiod_set_value_cansleep(dw->pe_rst, 0); + rcar_gen4_pcie_host_perst_assert(pp, false); return 0; @@ -539,7 +568,7 @@ static void rcar_gen4_pcie_host_deinit(struct dw_pcie_rp *pp) struct dw_pcie *dw = to_dw_pcie_from_pp(pp); struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw); - gpiod_set_value_cansleep(dw->pe_rst, 1); + rcar_gen4_pcie_host_perst_assert(pp, true); rcar->drvdata->deinit(rcar); } @@ -713,7 +742,7 @@ static int rcar_gen4_pcie_probe(struct platform_device *pdev) err = rcar_gen4_pcie_prepare(rcar); if (err) - return err; + goto err_prepare; err = rcar_gen4_add_dw_pcie(rcar); if (err) @@ -724,6 +753,9 @@ static int rcar_gen4_pcie_probe(struct platform_device *pdev) err_unprepare: rcar_gen4_pcie_unprepare(rcar); +err_prepare: + reset_control_put(rcar->perst); + return err; } @@ -747,6 +779,7 @@ static void rcar_gen4_pcie_remove(struct platform_device *pdev) rcar_gen4_remove_dw_pcie(rcar); rcar_gen4_pcie_unprepare(rcar); + reset_control_put(rcar->perst); } static int r8a779f0_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable) From 3283c4db60093970509b4b11e0b8a18f342aafb5 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 21 Sep 2026 22:27:48 +0200 Subject: [PATCH 09/14] PCI: rcar-gen4: Add support for R-Car X5H PCIe4 Add support for R8A78000 (R-Car X5H) PCIe4. This driver previously supported R-Car Gen4 S4/V4H/V4M. PCIe features of R-Car X5H PCIe4 are almost all the same. The controller initialization sequence is slightly different and is factored out into controller-specific callbacks, in a manner similar to previous R-Car Gen4 handling. The controller does have a PHY attached to it, but the PHY is operated by a separate PHY driver. The PHY driver instance binding is handled in rcar_gen4_pcie_get_resources() and controlled in the aforementioned controller-specific callbacks. The controller driver is deliberately using "renesas,rcar-gen5-pcie4" DT compatible string to discern R-Car X5H PCIe4 controller supported by this driver, from R-Car X5H PCIe6 controller which will most likely use a separate driver. The R-Car X5H PCIe4 controller embeds HDMA instead of EDMA embedded in the R-Car Gen4 PCIe controller, "dw-edma" driver supports both DMA variants. Endpoint mode is currently not implemented for R-Car Gen5 PCIe4. Signed-off-by: Marek Vasut Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260921202823.72620-7-marek.vasut+renesas@mailbox.org --- drivers/pci/controller/dwc/pcie-rcar-gen4.c | 120 +++++++++++++++++++- 1 file changed, 118 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c index cf1f5de45182..0b7ae4a69308 100644 --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ /* MSI Capability */ #define MSICAP0 0x0050 +#define MSICAP0_MMESCAP_MASK GENMASK(19, 17) #define MSICAP0_MSIE BIT(16) /* PCIe Interrupt Status 0 */ @@ -74,6 +76,11 @@ #define PCIEPWRMNGCTRL 0x0070 #define APP_CLK_REQ_N BIT(11) #define APP_CLK_PM_EN BIT(10) +#define APP_READY_ENTR_L23 BIT(6) +#define APP_REQ_ENTR_L1 BIT(5) + +/* PCI Express capability */ +#define EXPCAP(x) (0x0070 + (x)) #define RCAR_NUM_SPEED_CHANGE_RETRIES 10 #define RCAR_MAX_LINK_SPEED 4 @@ -98,6 +105,7 @@ struct rcar_gen4_pcie { struct dw_pcie dw; void __iomem *base; void __iomem *phy_base; + struct phy *phy; struct platform_device *pdev; struct reset_control *perst; const struct rcar_gen4_pcie_drvdata *drvdata; @@ -170,6 +178,27 @@ static int rcar_gen4_pcie_speed_control(struct rcar_gen4_pcie *rcar) return 0; } +static int rcar_gen5_pcie_speed_control(struct rcar_gen4_pcie *rcar) +{ + struct dw_pcie *dw = &rcar->dw; + u32 lnkcap = dw_pcie_readl_dbi(dw, EXPCAP(PCI_EXP_LNKCAP)); + u32 lnksta = dw_pcie_readw_dbi(dw, EXPCAP(PCI_EXP_LNKSTA)); + u32 val; + + if ((lnksta & PCI_EXP_LNKSTA_CLS) == (lnkcap & PCI_EXP_LNKCAP_SLS)) + return 0; + + /* Retrain link */ + val = dw_pcie_readw_dbi(dw, EXPCAP(PCI_EXP_LNKCTL)); + val |= PCI_EXP_LNKCTL_RL; + dw_pcie_writew_dbi(dw, EXPCAP(PCI_EXP_LNKCTL), val); + + /* Wait for link retrain, 500ms must be enough for all link rates. */ + return read_poll_timeout(dw_pcie_readw_dbi, lnksta, !(lnksta & PCI_EXP_LNKSTA_LT), + 1000, 5 * PCIE_RESET_CONFIG_WAIT_MS * USEC_PER_MSEC, + false, dw, EXPCAP(PCI_EXP_LNKSTA)); +} + /* * Enable LTSSM of this controller and manually initiate the speed change. * Always return 0. @@ -285,6 +314,49 @@ static int rcar_gen4_v4h_v4m_pcie_init(struct rcar_gen4_pcie *rcar) return 0; } +static int rcar_gen5_pcie_init(struct rcar_gen4_pcie *rcar) +{ + struct dw_pcie *dw = &rcar->dw; + int ret; + u32 val; + + /* R-Car Gen4 and Gen5 common initialization. */ + ret = rcar_gen4_pcie_common_init(rcar); + if (ret) + return ret; + + /* R-Car Gen5 specific additional initialization. */ + ret = phy_init(rcar->phy); + if (ret) + goto err_unprepare; + + dw_pcie_dbi_ro_wr_en(dw); + + val = dw_pcie_readl_dbi(dw, PCIE_PORT_LANE_SKEW); + val &= ~PORT_LANE_SKEW_INSERT_MASK; + if (dw->num_lanes < 8) + val |= BIT(6); + dw_pcie_writel_dbi(dw, PCIE_PORT_LANE_SKEW, val); + + val = dw_pcie_readl_dbi(dw, MSICAP0); + FIELD_MODIFY(MSICAP0_MMESCAP_MASK, &val, 4); + dw_pcie_writel_dbi(dw, MSICAP0, val); + + dw_pcie_dbi_ro_wr_dis(dw); + + val = readl(rcar->base + PCIEPWRMNGCTRL); + val |= APP_CLK_REQ_N | APP_CLK_PM_EN | + APP_READY_ENTR_L23 | APP_REQ_ENTR_L1; + writel(val, rcar->base + PCIEPWRMNGCTRL); + + return 0; + +err_unprepare: + clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks); + + return ret; +} + static void rcar_gen4_pcie_common_deinit(struct rcar_gen4_pcie *rcar) { struct dw_pcie *dw = &rcar->dw; @@ -293,6 +365,12 @@ static void rcar_gen4_pcie_common_deinit(struct rcar_gen4_pcie *rcar) clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks); } +static void rcar_gen5_pcie_deinit(struct rcar_gen4_pcie *rcar) +{ + phy_exit(rcar->phy); + rcar_gen4_pcie_common_deinit(rcar); +} + static int rcar_gen4_pcie_prepare(struct rcar_gen4_pcie *rcar) { struct device *dev = rcar->dw.dev; @@ -322,8 +400,12 @@ static int rcar_gen4_pcie_get_resources(struct rcar_gen4_pcie *rcar) struct device_node *root_port; rcar->phy_base = devm_platform_ioremap_resource_byname(rcar->pdev, "phy"); - if (IS_ERR(rcar->phy_base)) - return PTR_ERR(rcar->phy_base); + if (IS_ERR(rcar->phy_base)) { + rcar->phy_base = NULL; + rcar->phy = devm_phy_get(dev, NULL); + if (IS_ERR(rcar->phy)) + return PTR_ERR(rcar->phy); + } root_port = of_get_next_available_child(dev->of_node, NULL); if (root_port) { @@ -804,6 +886,28 @@ static int r8a779f0_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable) return 0; } +static int rcar_gen5_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable) +{ + u32 val; + + val = readl(rcar->base + PCIERSTCTRL1); + if (enable) { + val |= APP_LTSSM_ENABLE; + val &= ~APP_HOLD_PHY_RST; + } else { + val &= ~APP_LTSSM_ENABLE; + val |= APP_HOLD_PHY_RST; + } + writel(val, rcar->base + PCIERSTCTRL1); + + if (enable) + phy_power_on(rcar->phy); + else + phy_power_off(rcar->phy); + + return 0; +} + static void rcar_gen4_pcie_phy_reg_update_bits(struct rcar_gen4_pcie *rcar, u32 offset, u32 mask, u32 val) { @@ -986,6 +1090,14 @@ static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie_ep = { .mode = DW_PCIE_EP_TYPE, }; +static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen5_pcie = { + .init = rcar_gen5_pcie_init, + .deinit = rcar_gen5_pcie_deinit, + .ltssm_control = rcar_gen5_pcie_ltssm_control, + .speed_control = rcar_gen5_pcie_speed_control, + .mode = DW_PCIE_RC_TYPE, +}; + static const struct of_device_id rcar_gen4_pcie_of_match[] = { { .compatible = "renesas,r8a779f0-pcie", @@ -1003,6 +1115,10 @@ static const struct of_device_id rcar_gen4_pcie_of_match[] = { .compatible = "renesas,rcar-gen4-pcie-ep", .data = &drvdata_rcar_gen4_pcie_ep, }, + { + .compatible = "renesas,rcar-gen5-pcie4", + .data = &drvdata_rcar_gen5_pcie, + }, {}, }; MODULE_DEVICE_TABLE(of, rcar_gen4_pcie_of_match); From 056fd7eb017d73d9d175b733043edfbeb43569be Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 21 Sep 2026 22:36:46 +0200 Subject: [PATCH 10/14] PCI: rcar-gen4: Drop documentation about obtaining platform firmware Renesas R-Car V4H (r8a779g0) has a PCIe controller, and it requires specific firmware downloading. The firmware is now available via linux-firmware.git repository. So the existing documentation about obtaining the firmware becomes obsolete. Hence, drop it. Signed-off-by: Marek Vasut Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260921203716.73663-1-marek.vasut+renesas@mailbox.org --- Documentation/PCI/controller/index.rst | 1 - .../PCI/controller/rcar-pcie-firmware.rst | 32 ------------------- MAINTAINERS | 1 - 3 files changed, 34 deletions(-) delete mode 100644 Documentation/PCI/controller/rcar-pcie-firmware.rst diff --git a/Documentation/PCI/controller/index.rst b/Documentation/PCI/controller/index.rst index c15e2c9ac855..096d1c3d9807 100644 --- a/Documentation/PCI/controller/index.rst +++ b/Documentation/PCI/controller/index.rst @@ -8,4 +8,3 @@ PCI Native Host Bridge and Endpoint Drivers :maxdepth: 2 pci-controller-drivers - rcar-pcie-firmware diff --git a/Documentation/PCI/controller/rcar-pcie-firmware.rst b/Documentation/PCI/controller/rcar-pcie-firmware.rst deleted file mode 100644 index 67d3bf66e315..000000000000 --- a/Documentation/PCI/controller/rcar-pcie-firmware.rst +++ /dev/null @@ -1,32 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -================================================= -Firmware of PCIe controller for Renesas R-Car V4H -================================================= - -Renesas R-Car V4H (r8a779g0) has a PCIe controller, requiring a specific -firmware download during startup. - -However, Renesas currently cannot distribute the firmware free of charge. - -The firmware file "104_PCIe_fw_addr_data_ver1.05.txt" (note that the file name -might be different between different datasheet revisions) can be found in the -datasheet encoded as text, and as such, the file's content must be converted -back to binary form. This can be achieved using the following example script: - -.. code-block:: sh - - $ awk '/^\s*0x[0-9A-Fa-f]{4}\s+0x[0-9A-Fa-f]{4}/ { print substr($2,5,2) substr($2,3,2) }' \ - 104_PCIe_fw_addr_data_ver1.05.txt | \ - xxd -p -r > rcar_gen4_pcie.bin - -Once the text content has been converted into a binary firmware file, verify -its checksum as follows: - -.. code-block:: sh - - $ sha1sum rcar_gen4_pcie.bin - 1d0bd4b189b4eb009f5d564b1f93a79112994945 rcar_gen4_pcie.bin - -The resulting binary file called "rcar_gen4_pcie.bin" should be placed in the -"/lib/firmware" directory before the driver runs. diff --git a/MAINTAINERS b/MAINTAINERS index 3a19da74d00c..8df569bbf3a9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20954,7 +20954,6 @@ M: Yoshihiro Shimoda L: linux-pci@vger.kernel.org L: linux-renesas-soc@vger.kernel.org S: Maintained -F: Documentation/PCI/controller/rcar-pcie-firmware.rst F: Documentation/devicetree/bindings/pci/*rcar* F: drivers/pci/controller/*rcar* F: drivers/pci/controller/dwc/*rcar* From c687d310b1cf294e9365151db07dfd49e75b40f9 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 3 Sep 2026 22:50:52 +0200 Subject: [PATCH 11/14] dt-bindings: PCI: rcar-gen4-pci-host: Document Application/Local register reset The R-Car Gen4 PCIe controller does contain core reset, which puts the controller back into well-defined state, but it does also tear down the PCIe link. This is called Application/Local register reset in the documentation. Document this reset. Signed-off-by: Marek Vasut Signed-off-by: Manivannan Sadhasivam [bhelgaas: squash maxItems fix from https://patch.msgid.link/20260925203039.110653-1-marek.vasut+renesas@mailbox.org] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260903205153.283553-1-marek.vasut+renesas@mailbox.org --- .../devicetree/bindings/pci/rcar-gen4-pci-host.yaml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml b/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml index 9767a52f3904..362c43a1a15d 100644 --- a/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml +++ b/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml @@ -47,11 +47,14 @@ properties: maxItems: 1 resets: - maxItems: 1 + minItems: 1 + maxItems: 2 reset-names: + minItems: 1 items: - const: pwr + - const: core max-link-speed: maximum: 4 @@ -141,8 +144,8 @@ examples: clocks = <&cpg CPG_MOD 624>, <&pcie0_clkref>; clock-names = "core", "ref"; power-domains = <&sysc R8A779F0_PD_ALWAYS_ON>; - resets = <&cpg 624>; - reset-names = "pwr"; + resets = <&cpg 624>,<&cpg 1121>; + reset-names = "pwr", "core"; max-link-speed = <4>; num-lanes = <2>; #address-cells = <3>; From 26a78bf6d4c62be68eed2db7d0d2431331be1bb4 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 3 Sep 2026 22:50:53 +0200 Subject: [PATCH 12/14] dt-bindings: PCI: rcar-gen4-pci-ep: Document Application/Local register reset The R-Car Gen4 PCIe controller does contain core reset, which puts the controller back into well-defined state, but it does also tear down the PCIe link. This is called Application/Local register reset in the documentation. Document this reset. Signed-off-by: Marek Vasut Signed-off-by: Manivannan Sadhasivam [bhelgaas: squash maxItems fix from https://patch.msgid.link/20260925203039.110653-2-marek.vasut+renesas@mailbox.org] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260903205153.283553-2-marek.vasut+renesas@mailbox.org --- .../devicetree/bindings/pci/rcar-gen4-pci-ep.yaml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/pci/rcar-gen4-pci-ep.yaml b/Documentation/devicetree/bindings/pci/rcar-gen4-pci-ep.yaml index b23293314a6d..ce177f0eca75 100644 --- a/Documentation/devicetree/bindings/pci/rcar-gen4-pci-ep.yaml +++ b/Documentation/devicetree/bindings/pci/rcar-gen4-pci-ep.yaml @@ -56,11 +56,14 @@ properties: maxItems: 1 resets: - maxItems: 1 + minItems: 1 + maxItems: 2 reset-names: + minItems: 1 items: - const: pwr + - const: core max-link-speed: maximum: 4 @@ -109,8 +112,8 @@ examples: clocks = <&cpg CPG_MOD 624>, <&pcie0_clkref>; clock-names = "core", "ref"; power-domains = <&sysc R8A779F0_PD_ALWAYS_ON>; - resets = <&cpg 624>; - reset-names = "pwr"; + resets = <&cpg 624>,<&cpg 1121>; + reset-names = "pwr", "core"; max-link-speed = <4>; num-lanes = <2>; max-functions = /bits/ 8 <2>; From 295657e392df0ccd19e95f3eb7db309ee2aad449 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 3 Sep 2026 22:50:54 +0200 Subject: [PATCH 13/14] PCI: rcar-gen4: Add Application/Local register reset control The R-Car Gen4 PCIe controller does contain core reset, which puts the controller back into well-defined state, but it does also tear down the PCIe link. This is called Application/Local register reset in the documentation. Deassert the core reset after the core clock has been enabled, and assert the core reset in case of error or when shutting down the controller. This also fixes reinitialization failure of the controller, where if the controller driver is unbound and bound again via sysfs attributes, the controller driver will fail to probe the second time because it fails to access the controller DBI, which returns all zeroes unless the core reset is toggled. The following commands trigger this fault on R-Car V4H: $ echo e65d0000.pcie > /sys/bus/platform/drivers/pcie-rcar-gen4/unbind $ echo e65d0000.pcie > /sys/bus/platform/drivers/pcie-rcar-gen4/bind Fixes: faf5a975ee3b ("PCI: rcar-gen4: Add support for R-Car V4H") Signed-off-by: Marek Vasut [mani: added fixes tag and cced noautosel] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Cc: # DTS dependency Link: https://patch.msgid.link/20260903205153.283553-3-marek.vasut+renesas@mailbox.org --- drivers/pci/controller/dwc/pcie-rcar-gen4.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c index 0b7ae4a69308..21c3dd9b8891 100644 --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -237,6 +237,8 @@ static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar) return ret; } + reset_control_deassert(dw->core_rsts[DW_PCIE_CORE_RST].rstc); + if (!reset_control_status(dw->core_rsts[DW_PCIE_PWR_RST].rstc)) { reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc); /* @@ -284,6 +286,7 @@ static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar) return 0; err_unprepare: + reset_control_assert(dw->core_rsts[DW_PCIE_CORE_RST].rstc); clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks); return ret; @@ -362,6 +365,7 @@ static void rcar_gen4_pcie_common_deinit(struct rcar_gen4_pcie *rcar) struct dw_pcie *dw = &rcar->dw; reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc); + reset_control_assert(dw->core_rsts[DW_PCIE_CORE_RST].rstc); clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks); } From 6b8c12d354dfa9c80d82eea8c0c45d331a28be11 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 4 Oct 2026 03:18:13 +0200 Subject: [PATCH 14/14] PCI: rcar-gen4: Add missing PM ops The R-Car Gen4 PCIe controller is part of a power domain. On R-Car S4 and V4H, this is an always-on power domain which is not shut down in suspend. On R-Car V4M, this is a dedicated A2PCIPHY power domain, which is shut down during suspend, and the controller loses state, which prevents the PCIe from working after resume. Fix this by adding generic suspend/resume noirq ops for the controller, which tear the link down on suspend, and restart it on resume. Use the same PM ops on all of R-Car Gen4 to gracefully suspend and resume the PCIe link on V4H and S4 too. Test case which demonstrates the problem on R-Car V4M: $ hexdump -vC /sys/bus/pci/devices/0000:00:00.0/config > /tmp/pre $ echo s2idle > /sys/power/mem_sleep $ echo platform > /sys/power/pm_test $ echo mem > /sys/power/state $ hexdump -vC /sys/bus/pci/devices/0000:00:00.0/config > /tmp/post $ diff -Naru /tmp/pre /tmp/post ... -00000000 12 19 32 00 07 05 10 00 00 00 04 06 00 00 01 00 +00000000 12 19 32 00 07 05 10 00 00 00 00 ff 00 00 80 00 ^^^^^ Class 0604->00ff Fixes: 0270dbe4f891 ("arm64: dts: renesas: r8a779h0: Add PCIe Host and Endpoint nodes") Reported-by: Geert Uytterhoeven Signed-off-by: Marek Vasut Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Geert Uytterhoeven Tested-by: Koichiro Den Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20261004011851.866833-1-marek.vasut+renesas@mailbox.org --- drivers/pci/controller/dwc/pcie-rcar-gen4.c | 27 +++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c index 21c3dd9b8891..b8237962de18 100644 --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -671,6 +671,8 @@ static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar) return -ENODEV; pp->num_vectors = MAX_MSI_IRQS; + /* Reserve an iATU window for the generic PME_Turn_Off implementation. */ + pp->use_atu_msg = true; pp->ops = &rcar_gen4_pcie_host_ops; return dw_pcie_host_init(pp); @@ -1062,6 +1064,26 @@ static int rcar_gen4_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable return 0; } +static int rcar_gen4_pcie_suspend_noirq(struct device *dev) +{ + struct rcar_gen4_pcie *rcar = dev_get_drvdata(dev); + + if (rcar->drvdata->mode != DW_PCIE_RC_TYPE) + return 0; + + return dw_pcie_suspend_noirq(&rcar->dw); +} + +static int rcar_gen4_pcie_resume_noirq(struct device *dev) +{ + struct rcar_gen4_pcie *rcar = dev_get_drvdata(dev); + + if (rcar->drvdata->mode != DW_PCIE_RC_TYPE) + return 0; + + return dw_pcie_resume_noirq(&rcar->dw); +} + static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie = { .init = rcar_gen4_pcie_common_init, .deinit = rcar_gen4_pcie_common_deinit, @@ -1127,10 +1149,15 @@ static const struct of_device_id rcar_gen4_pcie_of_match[] = { }; MODULE_DEVICE_TABLE(of, rcar_gen4_pcie_of_match); +static DEFINE_NOIRQ_DEV_PM_OPS(rcar_gen4_pcie_pm_ops, + rcar_gen4_pcie_suspend_noirq, + rcar_gen4_pcie_resume_noirq); + static struct platform_driver rcar_gen4_pcie_driver = { .driver = { .name = "pcie-rcar-gen4", .of_match_table = rcar_gen4_pcie_of_match, + .pm = pm_sleep_ptr(&rcar_gen4_pcie_pm_ops), .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, .probe = rcar_gen4_pcie_probe,