[PATCH v2 3/4] PCI: qcom: Add link retention support
Krishna Chaitanya Chundru
krishna.chundru at oss.qualcomm.com
Thu Jun 18 22:00:11 PDT 2026
On 6/16/2026 5:38 PM, Konrad Dybcio wrote:
> On 5/21/26 2:56 PM, 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 when the driver does not need to retrain the link.
>>
>> 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
>> link_retain and skip controller reset, PERST# toggling, and other post-
>> init steps.
>>
>> If the current link speed or lane width does not satisfy the constraints
>> specified by max-link-speed or num-lanes in the device tree, fall back to
>> normal initialization and retrain the link instead of retaining it.
>>
>> Configure the DBI and ATU base addresses in the retention path, since the
>> bootloader may use different base addresses than those provided by the
>> device tree.
>>
>> Also fix the -EPROBE_DEFER error handling path to return 0 instead of
>> propagating the error, avoiding unnecessary cleanup when probe deferral is
>> requested.
>>
>> Tested-by: Qiang Yu <qiang.yu at oss.qualcomm.com>
>> Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru at oss.qualcomm.com>
>> ---
>> drivers/pci/controller/dwc/pcie-designware.h | 1 +
>> drivers/pci/controller/dwc/pcie-qcom.c | 62 +++++++++++++++++++++++++---
>> 2 files changed, 58 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
>> index 3e69ef60165b..be6c4abf31e8 100644
>> --- a/drivers/pci/controller/dwc/pcie-designware.h
>> +++ b/drivers/pci/controller/dwc/pcie-designware.h
>> @@ -450,6 +450,7 @@ struct dw_pcie_rp {
>> bool ecam_enabled;
>> bool native_ecam;
>> bool skip_l23_ready;
>> + bool link_retain;
>> };
>>
>> struct dw_pcie_ep_ops {
>> diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
>> index bfe873cbf44f..b061eaa227b3 100644
>> --- a/drivers/pci/controller/dwc/pcie-qcom.c
>> +++ b/drivers/pci/controller/dwc/pcie-qcom.c
>> @@ -253,12 +253,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 retaining link from bootloader
>> */
>> 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_perst {
>> @@ -960,6 +962,42 @@ static int qcom_pcie_get_resources_2_7_0(struct qcom_pcie *pcie)
>> return 0;
>> }
>>
>> +/*
>> + * Determine whether the link established by the bootloader can be reused.
>> + *
>> + * Reuse the existing link only if its current speed and lane count match
>> + * the max-link-speed and num-lanes specified in Device Tree; otherwise,
>> + * retrain the link.
>> + */
>> +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)
> I think I raised this concern already, but this goes against what
> max-link-speed is supposed to do, i.e. this will not retrain the link if
> the bootloader had initialized the link to a speed faster than what the
> DT requested
sorry for the miss, it should be speed < pci->max_link_speed same for width
check also it should be width < pci->num_lanes I will correct it next version.
- Krishna Chaitanya.
>> + return false;
>> +
>> + if (pci->num_lanes > 0 && width > pci->num_lanes)
>> + return false;
> Similarly, this should be ==
>
> Konrad
More information about the linux-phy
mailing list