On Sat, Oct 3, 2026 at 5:31 AM Dave Klotz <[email protected]> wrote:
>
> When the link does not come up within the training timeout, the probe
> returns -ENODEV. An empty slot is a normal configuration, but the
> failed probe leaves the root port unusable, skips the outbound memory
> window, and makes every later lookup of the bus (each "pci" command, a
> boot device scan) probe the controller again, repeating the whole
> bring-up and the one second wait.
>
> Linux's DesignWare core carries on when the link does not come up and
> only refuses config requests to the buses behind the root port while
> the link is down (dw_pcie_other_conf_map_bus()); FreeBSD's
> spacemit_pcie driver also treats a down link as non-fatal. Do the
> same: report the link as down, finish the probe, and answer config
> reads behind the root port with all ones, dropping writes, while the
> link is down.
>
> Signed-off-by: Dave Klotz <[email protected]>
> ---
> drivers/pci/pcie_dw_spacemit.c | 53 +++++++++++++++++++++++++++-------
> 1 file changed, 43 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/pci/pcie_dw_spacemit.c b/drivers/pci/pcie_dw_spacemit.c
> index 9b299ca17a..61c759172d 100644
> --- a/drivers/pci/pcie_dw_spacemit.c
> +++ b/drivers/pci/pcie_dw_spacemit.c
> @@ -338,15 +338,18 @@ static int pcie_dw_spacemit_probe(struct udevice *dev)
> pcie_dw_setup_host(&pci->dw);
> pcie_dw_init_id(pci);
>
> - if (!pcie_dw_spacemit_pcie_link_up(pci)) {
> + /*
> + * An empty slot, or a device that does not train, is not an error:
> + * the root port is still usable and the config accessors keep
> + * requests for the buses behind it off the wire.
> + */
> + if (!pcie_dw_spacemit_pcie_link_up(pci))
> printf("PCIE-%d: Link down\n", dev_seq(dev));
> - return -ENODEV;
> - }
> -
> - printf("PCIE-%d: Link up (Gen%d-x%d, Bus%d)\n", dev_seq(dev),
> - pcie_dw_get_link_speed(&pci->dw),
> - pcie_dw_get_link_width(&pci->dw),
> - hose->first_busno);
> + else
> + printf("PCIE-%d: Link up (Gen%d-x%d, Bus%d)\n", dev_seq(dev),
> + pcie_dw_get_link_speed(&pci->dw),
> + pcie_dw_get_link_width(&pci->dw),
> + hose->first_busno);
>
> ret = pcie_dw_prog_outbound_atu_unroll(&pci->dw,
> PCIE_ATU_REGION_INDEX0,
> PCIE_ATU_TYPE_MEM,
> @@ -430,9 +433,39 @@ static int pcie_dw_spacemit_of_to_plat(struct udevice
> *dev)
> return 0;
> }
>
> +/*
> + * Nothing answers behind the root port while the link is down, so do not
> + * send config requests there; Linux's DesignWare core does the same.
> + */
> +static int pcie_dw_spacemit_read_config(const struct udevice *bus,
> + pci_dev_t bdf, uint offset,
> + ulong *valuep, enum pci_size_t size)
> +{
> + struct pcie_dw_spacemit *pci = dev_get_priv(bus);
> +
> + if (PCI_BUS(bdf) != pci->dw.first_busno && !is_link_up(pci)) {
> + *valuep = pci_get_ff(size);
> + return 0;
> + }
> +
> + return pcie_dw_read_config(bus, bdf, offset, valuep, size);
> +}
> +
> +static int pcie_dw_spacemit_write_config(struct udevice *bus, pci_dev_t bdf,
> + uint offset, ulong value,
> + enum pci_size_t size)
> +{
> + struct pcie_dw_spacemit *pci = dev_get_priv(bus);
> +
> + if (PCI_BUS(bdf) != pci->dw.first_busno && !is_link_up(pci))
> + return 0;
> +
> + return pcie_dw_write_config(bus, bdf, offset, value, size);
> +}
> +
> static const struct dm_pci_ops pcie_dw_spacemit_ops = {
> - .read_config = pcie_dw_read_config,
> - .write_config = pcie_dw_write_config,
> + .read_config = pcie_dw_spacemit_read_config,
> + .write_config = pcie_dw_spacemit_write_config,
> };
>
> static const struct udevice_id pcie_dw_spacemit_ids[] = {
> --
> 2.55.0
>
>
Reviewed-by: Eric Chung <[email protected]>