diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 0d638b7..7b7d19a 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. @@ -973,6 +974,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; @@ -1044,6 +1046,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; @@ -1057,7 +1062,35 @@ 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); + do { + udelay(100); // Guess? + } while (mvebu_pcie_link_up(port)); + 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++) + udelay(100); + } 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);