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 <marek.vasut+renesas@mailbox.org>
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Link: https://patch.msgid.link/20260921202823.72620-7-marek.vasut+renesas@mailbox.org
This commit is contained in:
Marek Vasut 2026-09-21 22:27:48 +02:00 committed by Bjorn Helgaas
parent 487a731517
commit 3283c4db60

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