mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
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:
commit
6a220069f3
|
|
@ -8,4 +8,3 @@ PCI Native Host Bridge and Endpoint Drivers
|
|||
:maxdepth: 2
|
||||
|
||||
pci-controller-drivers
|
||||
rcar-pcie-firmware
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user