// SPDX-License-Identifier: GPL-2.0-only /* * PCIe controller driver for Renesas R-Car Gen4 Series SoCs * Copyright (C) 2022-2023 Renesas Electronics Corporation * * The r8a779g0 (R-Car V4H) controller requires a specific firmware to be * provided, to initialize the PHY. Otherwise, the PCIe controller will not * work.
*/
val = readl(rcar->base + PCIEINTSTS0);
mask = RDLH_LINK_UP | SMLH_LINK_UP;
return (val & mask) == mask;
}
/* * Manually initiate the speed change. Return 0 if change succeeded; otherwise * -ETIMEDOUT.
*/ staticint rcar_gen4_pcie_speed_change(struct dw_pcie *dw)
{
u32 val; int i;
val = dw_pcie_readl_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL);
val &= ~PORT_LOGIC_SPEED_CHANGE;
dw_pcie_writel_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
val = dw_pcie_readl_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL);
val |= PORT_LOGIC_SPEED_CHANGE;
dw_pcie_writel_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
for (i = 0; i < RCAR_NUM_SPEED_CHANGE_RETRIES; i++) {
val = dw_pcie_readl_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL); if (!(val & PORT_LOGIC_SPEED_CHANGE)) return 0;
usleep_range(10000, 11000);
}
return -ETIMEDOUT;
}
/* * Enable LTSSM of this controller and manually initiate the speed change. * Always return 0.
*/ staticint rcar_gen4_pcie_start_link(struct dw_pcie *dw)
{ 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;
}
/* * Require direct speed change with retrying here if the max_link_speed * is PCIe Gen2 or higher.
*/
changes = min_not_zero(dw->max_link_speed, RCAR_MAX_LINK_SPEED) - 1;
/* * Since dw_pcie_setup_rc() sets it once, PCIe Gen2 will be trained. * So, this needs remaining times for up to PCIe Gen4 if RC mode.
*/ if (changes && rcar->drvdata->mode == DW_PCIE_RC_TYPE)
changes--;
for (i = 0; i < changes; i++) { /* It may not be connected in EP mode yet. So, break the loop */ if (rcar_gen4_pcie_speed_change(dw)) break;
}
ret = clk_bulk_prepare_enable(DW_PCIE_NUM_CORE_CLKS, dw->core_clks); if (ret) {
dev_err(dw->dev, "Enabling core clocks failed\n"); return ret;
}
if (!reset_control_status(dw->core_rsts[DW_PCIE_PWR_RST].rstc)) {
reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc); /* * R-Car V4H Reference Manual R19UH0186EJ0130 Rev.1.30 Apr. * 21, 2025 page 585 Figure 9.3.2 Software Reset flow (B) * indicates that for peripherals in HSC domain, after * reset has been asserted by writing a matching reset bit * into register SRCR, it is mandatory to wait 1ms.
*/
fsleep(1000);
}
val = readl(rcar->base + PCIEMSR0); if (rcar->drvdata->mode == DW_PCIE_RC_TYPE) {
val |= DEVICE_TYPE_RC;
} elseif (rcar->drvdata->mode == DW_PCIE_EP_TYPE) {
val |= DEVICE_TYPE_EP;
} else {
ret = -EINVAL; goto err_unprepare;
}
if (dw->num_lanes < 4)
val |= BIFUR_MOD_SET_ON;
writel(val, rcar->base + PCIEMSR0);
ret = reset_control_deassert(dw->core_rsts[DW_PCIE_PWR_RST].rstc); if (ret) goto err_unprepare;
/* * Assure the reset is latched and the core is ready for DBI access. * On R-Car V4H, the PCIe reset is asynchronous and does not take * effect immediately, but needs a short time to complete. In case * DBI access happens in that short time, that access generates an * SError. To make sure that condition can never happen, read back the * state of the reset, which should turn the asynchronous reset into * synchronous one, and wait a little over 1ms to add additional * safety margin.
*/
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);
ret = rcar_gen4_pcie_common_init(rcar); if (ret) return ret;
/* * According to the section 3.5.7.2 "RC Mode" in DWC PCIe Dual Mode * Rev.5.20a and 3.5.6.1 "RC mode" in DWC PCIe RC databook v5.20a, we * should disable two BARs to avoid unnecessary memory assignment * during device enumeration.
*/
dw_pcie_writel_dbi2(dw, PCI_BASE_ADDRESS_0, 0x0);
dw_pcie_writel_dbi2(dw, PCI_BASE_ADDRESS_1, 0x0);
/* Enable MSI interrupt signal */
val = readl(rcar->base + PCIEINTSTS0EN);
val |= MSI_CTRL_INT;
writel(val, rcar->base + PCIEINTSTS0EN);
val = readl(rcar->base + PCIERSTCTRL1); if (enable) {
val |= APP_LTSSM_ENABLE;
val &= ~APP_HOLD_PHY_RST;
} else { /* * Since the datasheet of R-Car doesn't mention how to assert * the APP_HOLD_PHY_RST, don't assert it again. Otherwise, * hang-up issue happened in the dw_edma_core_off() when * the controller didn't detect a PCI device.
*/
val &= ~APP_LTSSM_ENABLE;
}
writel(val, rcar->base + PCIERSTCTRL1);
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);
}
/* * SoC datasheet suggests checking port logic register bits during firmware * write. If read returns non-zero value, then this function returns -EAGAIN * indicating that the write needs to be done again. If read returns zero, * then return 0 to indicate success.
*/ staticint rcar_gen4_pcie_reg_test_bit(struct rcar_gen4_pcie *rcar,
u32 offset, u32 mask)
{ struct dw_pcie *dw = &rcar->dw;
if (dw_pcie_readl_dbi(dw, offset) & mask) return -EAGAIN;
return 0;
}
staticint rcar_gen4_pcie_download_phy_firmware(struct rcar_gen4_pcie *rcar)
{ /* The check_addr values are magical numbers in the datasheet */ staticconst u32 check_addr[] = {
0x00101018,
0x00101118,
0x00101021,
0x00101121,
}; struct dw_pcie *dw = &rcar->dw; conststruct firmware *fw; unsignedint i, timeout;
u32 data; int ret;
ret = request_firmware(&fw, RCAR_GEN4_PCIE_FIRMWARE_NAME, dw->dev); if (ret) {
dev_err(dw->dev, "Failed to load firmware (%s): %d\n",
RCAR_GEN4_PCIE_FIRMWARE_NAME, ret); return ret;
}
for (i = 0; i < (fw->size / 2); i++) {
data = fw->data[(i * 2) + 1] << 8 | fw->data[i * 2];
timeout = 100; do {
dw_pcie_writel_dbi(dw, PRTLGC89, RCAR_GEN4_PCIE_FIRMWARE_BASE_ADDR + i);
dw_pcie_writel_dbi(dw, PRTLGC90, data); if (!rcar_gen4_pcie_reg_test_bit(rcar, PRTLGC89, BIT(30))) break; if (!(--timeout)) {
ret = -ETIMEDOUT; gotoexit;
}
usleep_range(100, 200);
} while (1);
}
for (i = 0; i < ARRAY_SIZE(check_addr); i++) {
timeout = 100; do {
dw_pcie_writel_dbi(dw, PRTLGC89, check_addr[i]);
ret = rcar_gen4_pcie_reg_test_bit(rcar, PRTLGC89, BIT(30));
ret |= rcar_gen4_pcie_reg_test_bit(rcar, PRTLGC90, BIT(0)); if (!ret) break; if (!(--timeout)) {
ret = -ETIMEDOUT; gotoexit;
}
usleep_range(100, 200);
} while (1);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.