Re: [PATCH 4/5] PCI: qcom: Add link retention support

From: Manivannan Sadhasivam

Date: Mon Feb 16 2026 - 10:17:16 EST


On Fri, Jan 09, 2026 at 12:51:09PM +0530, Krishna Chaitanya Chundru wrote:
> Some platforms keep the PCIe link active across bootloader and kernel
> handoff. Reinitializing the controller and toggling PERST# in such cases
> is unnecessary if driver doesn't want to do link training again.
>
> Introduce link_retain in both qcom_pcie_cfg and qcom_pcie to
> indicate when link retention is supported. During initialization, check
> the LTSSM state, if the link is already in L0 or L1 idle and LTSSM is
> enabled, set pp.link_retain and skip controller reset, PERST# toggling,
> and other post-init steps.
>
> If there is a devicetree property to restrict PCIe data rate and lane
> width go with the normal execution instead of link retantion logic.
>

Mention the properties here and also make it clear that the link will not be
retained if the values do not match bootloader programmed ones.

> Configure DBI and ATU base in this scenerio, since bootloader DBI & ATU
> base may differ from HLOS one. So use the DBI & ATU provided from the
> devicetree.
>
> Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@xxxxxxxxxxxxxxxx>
> ---
> drivers/pci/controller/dwc/pcie-qcom.c | 50 ++++++++++++++++++++++++++++++++--
> 1 file changed, 47 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
> index 9342f9c75f1c3017b55614069a7aa821a6fb8da7..bdd5bdb462c5f6814c8311be96411173456b6b14 100644
> --- a/drivers/pci/controller/dwc/pcie-qcom.c
> +++ b/drivers/pci/controller/dwc/pcie-qcom.c
> @@ -259,12 +259,14 @@ struct qcom_pcie_ops {
> * @override_no_snoop: Override NO_SNOOP attribute in TLP to enable cache
> * snooping
> * @firmware_managed: Set if the Root Complex is firmware managed
> + * @link_retain: Set if controller supports link retain from bootloader

s/"link retain"/"retaining link"

> */
> struct qcom_pcie_cfg {
> const struct qcom_pcie_ops *ops;
> bool override_no_snoop;
> bool firmware_managed;
> bool no_l0s;
> + bool link_retain;
> };
>
> struct qcom_pcie_port {
> @@ -965,6 +967,35 @@ static int qcom_pcie_get_resources_2_7_0(struct qcom_pcie *pcie)
> return 0;
> }
>
> +static bool qcom_pcie_check_link_retain(struct qcom_pcie *pcie)
> +{
> + u32 cap, speed, val, ltssm, width;
> + struct dw_pcie *pci = pcie->pci;
> + u8 offset;
> +
> + val = readl(pcie->parf + PARF_LTSSM);
> + ltssm = val & 0x1f;
> + if ((val & LTSSM_EN) &&
> + (ltssm == DW_PCIE_LTSSM_L0 || ltssm == DW_PCIE_LTSSM_L1_IDLE)) {
> + qcom_pcie_configure_dbi_atu_base(pcie);
> +
> + offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
> + cap = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP);
> + speed = FIELD_GET(PCI_EXP_LNKCAP_SLS, cap);
> + width = dw_pcie_link_get_max_link_width(pci);
> +
> + if (pci->max_link_speed > 0 && speed < pci->max_link_speed)
> + return false;
> +
> + if (pci->num_lanes > 0 && width > pci->num_lanes)
> + return false;

IIUC, these checks are in place to override the link retention if the users want
to change the lane/width count than what was already programmed by the
bootloader.

If so, please add comment on top of this helper to make it explicit.

> +
> + return true;
> + }
> +
> + return false;
> +}
> +
> static int qcom_pcie_init_2_7_0(struct qcom_pcie *pcie)
> {
> struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0;
> @@ -983,6 +1014,14 @@ static int qcom_pcie_init_2_7_0(struct qcom_pcie *pcie)
> if (ret < 0)
> goto err_disable_regulators;
>
> + if (pcie->cfg->link_retain) {
> + pci->pp.link_retain = qcom_pcie_check_link_retain(pcie);
> + if (pci->pp.link_retain) {
> + dev_info(dev, "Enabling link retain\n");

"Retaining PCIe link"

- Mani

--
மணிவண்ணன் சதாசிவம்