diff mbox series

[9/9] pci: spacemit: do not fail the probe when the link is down

Message ID bee14dfeb671f9b8c951879cfaa4160c0cdc16a6.1790975598.git.DaveWK225@protonmail.com
State New
Delegated to: Tim Ouyang
Headers show
Series pci: spacemit: fixes for the K1 PCIe host driver | expand

Commit Message

Dave Klotz Oct. 2, 2026, 9:31 p.m. UTC
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 <DaveWK225@protonmail.com>
---
 drivers/pci/pcie_dw_spacemit.c | 53 +++++++++++++++++++++++++++-------
 1 file changed, 43 insertions(+), 10 deletions(-)
diff mbox series

Patch

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[] = {