Merge branch 'pci/controller/dwc-rcar-gen4'

- Limit Max_Read_Request_Size to 256 to avoid hardware erratum (Marek
  Vasut)

- Add a .post_deinit() callback to handle dw_pcie_ep_init() failures (Marek
  Vasut)

- Rework .additional_common_init() into .init() to simplify flow (Marek
  Vasut)

- Add .deinit() and .speed_control() callbacks, both to prepare for R-Car
  Gen5 SoC support (Marek Vasut)

- Support PERST# via reset subsystem as well as the existing GPIO support
  to prepare for R-Car Gen5 SoCs (Marek Vasut)

- Add DT and driver support for the R-Car Gen 5 X5H (R8A78000) PCIe Gen 4
  core (Marek Vasut)

- Drop documentation about obtaining R-Car platform firmware, which is
  obsolete now that it's available in the linux-firmware.git repository
  (Marek Vasut)

- Add Application/Local register reset DT description and driver support
  for both host and endpoint mode (Marek Vasut)

- Add PM ops so controller works after suspend/resume (Marek Vasut)

* pci/controller/dwc-rcar-gen4:
  PCI: rcar-gen4: Add missing PM ops
  PCI: rcar-gen4: Add Application/Local register reset control
  dt-bindings: PCI: rcar-gen4-pci-ep: Document Application/Local register reset
  dt-bindings: PCI: rcar-gen4-pci-host: Document Application/Local register reset
  PCI: rcar-gen4: Drop documentation about obtaining platform firmware
  PCI: rcar-gen4: Add support for R-Car X5H PCIe4
  PCI: rcar-gen4: Handle PERST# via reset subsystem
  PCI: rcar-gen4: Add .speed_control() callback
  PCI: rcar-gen4: Add .deinit() callback
  PCI: rcar-gen4: Rework .additional_common_init() into .init()
  dt-bindings: PCI: rcar-gen4-pci-host: Add R-Car X5H PCIe4 compatible
  PCI: rcar-gen4: Use .post_deinit() to handle dw_pcie_ep_init() failures
  PCI: dwc: Add dw_pcie_ep_ops->post_deinit() callback
  PCI: rcar-gen4: Limit Max_Read_Request_Size to 256 Bytes

# Conflicts:
#	drivers/pci/controller/dwc/pcie-rcar-gen4.c
This commit is contained in:
Bjorn Helgaas 2026-10-21 13:20:34 -05:00
commit 6a220069f3
8 changed files with 378 additions and 107 deletions

View File

@ -8,4 +8,3 @@ PCI Native Host Bridge and Endpoint Drivers
:maxdepth: 2
pci-controller-drivers
rcar-pcie-firmware

View File

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

View File

@ -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>;

View File

@ -10,30 +10,20 @@ title: Renesas R-Car Gen4 PCIe Host
maintainers:
- Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
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
@ -57,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
@ -84,6 +77,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:
@ -111,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>;

View File

@ -20954,7 +20954,6 @@ M: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
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*

View File

@ -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;
}

View File

@ -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);

View File

@ -19,6 +19,7 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/pci.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
@ -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
@ -87,8 +94,10 @@ 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);
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;
};
@ -96,7 +105,9 @@ 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;
};
#define to_rcar_gen4_pcie(_dw) container_of(_dw, struct rcar_gen4_pcie, dw)
@ -140,20 +151,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
@ -177,6 +178,45 @@ static int rcar_gen4_pcie_start_link(struct dw_pcie *dw)
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.
*/
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);
@ -197,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);
/*
@ -241,8 +283,74 @@ 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:
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;
}
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, PORT_LANE_SKEW);
val &= ~PORT_LANE_SKEW_INSERT_MASK;
if (dw->num_lanes < 4)
val |= BIT(6);
dw_pcie_writel_dbi(dw, 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 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, PORT_LANE_SKEW);
val &= ~PORT_LANE_SKEW_INSERT_MASK;
if (dw->num_lanes < 8)
val |= BIT(6);
dw_pcie_writel_dbi(dw, 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;
@ -257,9 +365,16 @@ 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);
}
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;
@ -285,12 +400,29 @@ 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);
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) {
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);
}
@ -411,6 +543,72 @@ 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;
}
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)
{
@ -418,9 +616,12 @@ static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
int ret;
gpiod_set_value_cansleep(dw->pe_rst, 1);
if (pp->bridge)
pp->bridge->enable_device = rcar_gen4_pcie_enable_device;
ret = rcar_gen4_pcie_common_init(rcar);
rcar_gen4_pcie_host_perst_assert(pp, true);
ret = rcar->drvdata->init(rcar);
if (ret)
return ret;
@ -439,12 +640,12 @@ 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;
err:
rcar_gen4_pcie_common_deinit(rcar);
rcar->drvdata->deinit(rcar);
return ret;
}
@ -453,8 +654,8 @@ 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_common_deinit(rcar);
rcar_gen4_pcie_host_perst_assert(pp, true);
rcar->drvdata->deinit(rcar);
}
static const struct dw_pcie_host_ops rcar_gen4_pcie_host_ops = {
@ -470,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);
@ -487,7 +690,9 @@ 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;
ret = rcar_gen4_pcie_common_init(rcar);
writel(0, rcar->base + PCIEDMAINTSTSEN);
ret = rcar->drvdata->init(rcar);
if (ret)
return ret;
@ -496,10 +701,13 @@ 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);
rcar->drvdata->deinit(rcar);
}
static int rcar_gen4_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
@ -552,6 +760,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,
@ -570,16 +779,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);
@ -590,7 +796,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 */
@ -625,7 +830,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)
@ -636,6 +841,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;
}
@ -659,6 +867,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)
@ -683,20 +892,26 @@ 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)
static int rcar_gen5_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable)
{
struct dw_pcie *dw = &rcar->dw;
u32 val;
val = dw_pcie_readl_dbi(dw, PORT_LANE_SKEW);
val &= ~PORT_LANE_SKEW_INSERT_MASK;
if (dw->num_lanes < 4)
val |= BIT(6);
dw_pcie_writel_dbi(dw, PORT_LANE_SKEW, 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);
val = readl(rcar->base + PCIEPWRMNGCTRL);
val |= APP_CLK_REQ_N | APP_CLK_PM_EN;
writel(val, rcar->base + PCIEPWRMNGCTRL);
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,
@ -849,28 +1064,66 @@ 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,
.ltssm_control = r8a779f0_pcie_ltssm_control,
.speed_control = rcar_gen4_pcie_speed_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,
.speed_control = rcar_gen4_pcie_speed_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,
.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,
};
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,
.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,
};
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",
@ -888,14 +1141,23 @@ 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);
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,