/* * PCIe requires the refclk to be stable for 100µs prior to releasing * PERST. See table 2-4 in section 2.6.2 AC Specifications of the PCI * Express Card Electromechanical Specification, 1.1. However, we don't * know if the refclk is coming from RC's PHY or external OSC. If it's * from RC, so enabling LTSSM is the just right place to release #PERST. * We need more extra time as before, rather than setting just * 100us as we don't know how long should the device need to reset.
*/
msleep(PCIE_T_PVPERL_MS);
gpiod_set_value_cansleep(rockchip->rst_gpio, 1);
/* * ATS does not work on RK3588 when running in EP mode. * * After the host has enabled ATS on the EP side, it will send an IOTLB * invalidation request to the EP side. However, the RK3588 will never send * a completion back and eventually the host will print an IOTLB_INV_TIMEOUT * error, and the EP will not be operational. If we hide the ATS capability, * things work as expected.
*/ staticvoid rockchip_pcie_ep_hide_broken_ats_cap_rk3588(struct dw_pcie_ep *ep)
{ struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct device *dev = pci->dev;
/* Only hide the ATS capability for RK3588 running in EP mode. */ if (!of_device_is_compatible(dev->of_node, "rockchip,rk3588-pcie-ep")) return;
if (dw_pcie_ep_hide_ext_capability(pci, PCI_EXT_CAP_ID_SECPCI,
PCI_EXT_CAP_ID_ATS))
dev_err(dev, "failed to hide ATS capability\n");
}
/* * BAR4 on rk3588 exposes the ATU Port Logic Structure to the host regardless of * iATU settings for BAR4. This means that BAR4 cannot be used by an EPF driver, * so mark it as RESERVED. (rockchip_pcie_ep_init() will disable all BARs by * default.) If the host could write to BAR4, the iATU settings (for all other * BARs) would be overwritten, resulting in (all other BARs) no longer working.
*/ staticconststruct pci_epc_features rockchip_pcie_epc_features_rk3588 = {
.linkup_notifier = true,
.msi_capable = true,
.msix_capable = true,
.intx_capable = false,
.align = SZ_64K,
.bar[BAR_0] = { .type = BAR_RESIZABLE, },
.bar[BAR_1] = { .type = BAR_RESIZABLE, },
.bar[BAR_2] = { .type = BAR_RESIZABLE, },
.bar[BAR_3] = { .type = BAR_RESIZABLE, },
.bar[BAR_4] = { .type = BAR_RESERVED, },
.bar[BAR_5] = { .type = BAR_RESIZABLE, },
};
rockchip->rst_gpio = devm_gpiod_get_optional(&pdev->dev, "reset",
GPIOD_OUT_LOW); if (IS_ERR(rockchip->rst_gpio)) return dev_err_probe(&pdev->dev, PTR_ERR(rockchip->rst_gpio), "failed to get reset gpio\n");
rockchip->rst = devm_reset_control_array_get_exclusive(&pdev->dev); if (IS_ERR(rockchip->rst)) return dev_err_probe(&pdev->dev, PTR_ERR(rockchip->rst), "failed to get reset lines\n");
if (reg & PCIE_RDLH_LINK_UP_CHGED) { if (rockchip_pcie_link_up(pci)) {
msleep(PCIE_RESET_CONFIG_WAIT_MS);
dev_dbg(dev, "Received Link up event. Starting enumeration!\n"); /* Rescan the bus to enumerate endpoint devices */
pci_lock_rescan_remove();
pci_rescan_bus(pp->bridge->bus);
pci_unlock_rescan_remove();
}
}
if (!IS_ENABLED(CONFIG_PCIE_ROCKCHIP_DW_EP)) return -ENODEV;
irq = platform_get_irq_byname(pdev, "sys"); if (irq < 0) return irq;
ret = devm_request_threaded_irq(dev, irq, NULL,
rockchip_pcie_ep_sys_irq_thread,
IRQF_ONESHOT, "pcie-sys-ep", rockchip); if (ret) {
dev_err(dev, "failed to request PCIe sys IRQ\n"); return ret;
}
/* * LTSSM enable control mode, and automatically delay link training on * hot reset/link-down reset.
*/
val = HIWORD_UPDATE_BIT(PCIE_LTSSM_ENABLE_ENHANCE | PCIE_LTSSM_APP_DLY2_EN);
rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_HOT_RESET_CTRL);
/* Default N_FTS value (210) is broken, override it to 255 */
rockchip->pci.n_fts[0] = 255; /* Gen1 */
rockchip->pci.n_fts[1] = 255; /* Gen2+ */
ret = rockchip_pcie_resource_get(pdev, rockchip); if (ret) return ret;
ret = reset_control_assert(rockchip->rst); if (ret) return ret;
/* DON'T MOVE ME: must be enable before PHY init */
rockchip->vpcie3v3 = devm_regulator_get_optional(dev, "vpcie3v3"); if (IS_ERR(rockchip->vpcie3v3)) { if (PTR_ERR(rockchip->vpcie3v3) != -ENODEV) return dev_err_probe(dev, PTR_ERR(rockchip->vpcie3v3), "failed to get vpcie3v3 regulator\n");
rockchip->vpcie3v3 = NULL;
} else {
ret = regulator_enable(rockchip->vpcie3v3); if (ret) return dev_err_probe(dev, ret, "failed to enable vpcie3v3 regulator\n");
}
ret = rockchip_pcie_phy_init(rockchip); if (ret) goto disable_regulator;
ret = reset_control_deassert(rockchip->rst); if (ret) goto deinit_phy;
ret = rockchip_pcie_clk_init(rockchip); if (ret) goto deinit_phy;
switch (data->mode) { case DW_PCIE_RC_TYPE:
ret = rockchip_pcie_configure_rc(pdev, rockchip); if (ret) goto deinit_clk; break; case DW_PCIE_EP_TYPE:
ret = rockchip_pcie_configure_ep(pdev, rockchip); if (ret) goto deinit_clk; break; default:
dev_err(dev, "INVALID device type %d\n", data->mode);
ret = -EINVAL; goto deinit_clk;
}
return 0;
deinit_clk:
clk_bulk_disable_unprepare(rockchip->clk_cnt, rockchip->clks);
deinit_phy:
rockchip_pcie_phy_deinit(rockchip);
disable_regulator: if (rockchip->vpcie3v3)
regulator_disable(rockchip->vpcie3v3);
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.