soc/qualcomm/common: Implement asynchronous PCIe initialization

Introduce SOC_QUALCOMM_PCIE_ASYNCHRONOUS_INIT to allow the PCIe
link training to proceed without blocking the boot flow.

Refactor qcom_setup_pcie_host into two logical phases:
1. Initiate: Power on endpoints and trigger LTSSM (Romstage).
2. Verify: Wait for link-up status (Ramstage).

When the async Kconfig is enabled, the initiation happens in
romstage, but the blocking 'wait_link_up' call is deferred to
ramstage. This allows other SoC and mainboard initializations to
run in between the hardware link training, reducing overall boot time.

BUG=b:449871690
TEST=Verified PCIe link still enumerates correctly on Bluey with
asynchronous init enabled.

Change-Id: Idf368731325b5efcf4db0d1912a8c75417ef11ab
Signed-off-by: Subrata Banik <subratabanik@google.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/91723
Reviewed-by: Kapil Porwal <kapilporwal@google.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
Subrata Banik 2026-03-18 13:49:32 +05:30
commit f1baed6f79
2 changed files with 65 additions and 2 deletions

View file

@ -61,4 +61,14 @@ config SOC_QUALCOMM_DEBUG_TSENS
When enabled, a call to monitor TSENS will dump the sensor data on
the debug console.
config SOC_QUALCOMM_PCIE_ASYNCHRONOUS_INIT
bool
default n
help
When enabled, qcom_setup_pcie_host will initiate the PCIe
hardware power-up sequence but will not block the boot flow
to wait for the link-up status. This can reduce overall
boot time, but requires late-stage drivers to verify link
readiness before access.
endif

View file

@ -178,6 +178,9 @@ static enum cb_err qcom_pcie_dw_link_up(struct qcom_pcie_cntlr_t *pcie)
/* enable link training */
setbits32(pcie->cntlr_cfg->parf + PCIE_PARF_LTSSM, LTSSM_EN);
if (CONFIG(SOC_QUALCOMM_PCIE_ASYNCHRONOUS_INIT))
return CB_SUCCESS;
/* Check that link was established */
if (wait_link_up(pcie)) {
printk(BIOS_INFO, "PCIe link is up\n");
@ -562,8 +565,7 @@ void qcom_pci_domain_read_resources(struct device *dev)
res->flags = IORESOURCE_MEM | IORESOURCE_SUBTRACTIVE | IORESOURCE_ASSIGNED;
}
/* PCI domain ops enable callback */
void qcom_setup_pcie_host(struct device *dev)
static enum cb_err pcie_initiate_link(void)
{
gcom_pcie_get_config(&qcom_pcie_cfg);
@ -576,6 +578,57 @@ void qcom_setup_pcie_host(struct device *dev)
qcom_pcie_configure_gpios(qcom_pcie_cfg.cntlr_cfg);
if (!qcom_dw_pcie_enable(&qcom_pcie_cfg))
return CB_SUCCESS;
else
return CB_ERR;
}
static enum cb_err pcie_verify_link_status(void)
{
gcom_pcie_get_config(&qcom_pcie_cfg);
if (is_pcie_link_up(&qcom_pcie_cfg)) {
printk(BIOS_INFO, "PCIe Link is already up\n");
return CB_SUCCESS;
}
/* Check that link was established */
if (wait_link_up(&qcom_pcie_cfg)) {
printk(BIOS_INFO, "PCIe link is up\n");
return CB_SUCCESS;
}
/*
* Link can be established in Gen 1 as it failed to establish in Gen2.
* So allow some time to do it.
*/
udelay(100);
return is_pcie_link_up(&qcom_pcie_cfg) ? CB_SUCCESS : CB_ERR;
}
/* PCI domain ops enable callback */
void qcom_setup_pcie_host(struct device *dev)
{
/* STAGE 1: Initiate Hardware (Sync or Async)
* In Romstage (or if Async is disabled), we always start the hardware.
*/
if (ENV_SEPARATE_ROMSTAGE || !CONFIG(SOC_QUALCOMM_PCIE_ASYNCHRONOUS_INIT)) {
if (pcie_initiate_link() != CB_SUCCESS) {
printk(BIOS_EMERG, "Failed to enable PCIe\n");
return;
}
/* Async in Romstage */
if (ENV_SEPARATE_ROMSTAGE)
return;
}
/* STAGE 2: Late Check (Async only)
* If we are in Ramstage and Async is enabled, the hardware was already
* kicked off in Romstage. Now we just verify the result.
*/
if (pcie_verify_link_status() == CB_SUCCESS)
printk(BIOS_NOTICE, "PCIe enumerated succussfully..\n");
else
printk(BIOS_EMERG, "Failed to enable PCIe\n");