diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 487c926..d09a7e5 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -21,6 +21,7 @@ #include #include #include +#include /* * PCIe unit register offsets. @@ -951,6 +952,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev) for_each_child_of_node(pdev->dev.of_node, child) { struct mvebu_pcie_port *port = &pcie->ports[i]; enum of_gpio_flags flags; + bool enabled; if (!of_device_is_available(child)) continue; @@ -1022,6 +1024,9 @@ static int mvebu_pcie_probe(struct platform_device *pdev) continue; } + // Does this work on MVEBU? + enabled = __clk_is_enabled(port->clk); + ret = clk_prepare_enable(port->clk); if (ret) continue; @@ -1035,7 +1040,45 @@ static int mvebu_pcie_probe(struct platform_device *pdev) continue; } - mvebu_pcie_set_local_dev_nr(port, 1); + if (!enabled) { + u32 reg; + unsigned int tries; + + /* The clock is being turned on for the first time, do + * a PHY reset + */ + dev_info(&pdev->dev, + "PCIe%d.%d: performing link reset\n", + port->port, port->lane); + reg = mvebu_readl(port, PCIE_CTRL_OFF); + mvebu_writel(port, + reg & ~BIT(30), // Conf_TrainingDisable + PCIE_CTRL_OFF); + + for (tries = 0; + mvebu_pcie_link_up(port) && tries < 100; tries++) + mdelay(1); + + dev_info(&pdev->dev, + "PCIe%d.%d: link went down after %d tries\n", + port->port, port->lane, tries); + + mvebu_pcie_set_local_dev_nr(port, 1); + mvebu_writel(port, reg | BIT(30), PCIE_CTRL_OFF); + + for (tries = 0; + !mvebu_pcie_link_up(port) && tries != 100; tries++) + mdelay(1); + + dev_info(&pdev->dev, + "PCIe%d.%d: link came back up after %d tries\n", + port->port, port->lane, tries); + } else { + /* We expect the bootloader has setup the port and + * waited for the link to go up + */ + mvebu_pcie_set_local_dev_nr(port, 1); + } port->dn = child; spin_lock_init(&port->conf_lock);