mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
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:
parent
487a731517
commit
3283c4db60
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user