aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell King <[email protected]>2015-10-03 19:13:27 +0100
committerBjorn Helgaas <[email protected]>2015-10-09 11:21:38 -0500
commitd609a8d8e88a4292a0b4c42d1c942f8d088a6ebf (patch)
tree1d275e60103b6673b3fab0d33ebf5f421cae58b7
parent8a182c2e4b661ec2a4056c93557258f089f14fe7 (diff)
PCI: mvebu: Improve clock/reset handling
Add an implementation to handle clock and reset handling that is compliant with the PCIe specification. The clock should be running and stable for 100us prior to reset being released, and we should re-assert reset prior to stopping the clock. Tested-by: Willy Tarreau <[email protected]> (Iomega iConnect Kirkwood, MiraBox Armada 370) Tested-by: Andrew Lunn <[email protected]> (D-Link DIR664 Kirkwood) Tested-by: Thomas Petazzoni <[email protected]> (Armada XP GP) Signed-off-by: Russell King <[email protected]> Signed-off-by: Bjorn Helgaas <[email protected]> Reviewed-by: Thomas Petazzoni <[email protected]>
-rw-r--r--drivers/pci/host/pci-mvebu.c56
1 files changed, 43 insertions, 13 deletions
diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c
index ab619ee0ef49..97208233d901 100644
--- a/drivers/pci/host/pci-mvebu.c
+++ b/drivers/pci/host/pci-mvebu.c
@@ -1042,6 +1042,46 @@ err:
return ret;
}
+/*
+ * Power up a PCIe port. PCIe requires the refclk to be stable for 100µs
+ * prior to releasing PERST. See table 2-4 in section 2.6.2 AC Specifications
+ * of the PCI Express Card Electromechanical Specification, 1.1.
+ */
+static int mvebu_pcie_powerup(struct mvebu_pcie_port *port)
+{
+ int ret;
+
+ ret = clk_prepare_enable(port->clk);
+ if (ret < 0)
+ return ret;
+
+ if (port->reset_gpio) {
+ u32 reset_udelay = 20000;
+
+ of_property_read_u32(port->dn, "reset-delay-us",
+ &reset_udelay);
+
+ udelay(100);
+
+ gpiod_set_value_cansleep(port->reset_gpio, 0);
+ msleep(reset_udelay / 1000);
+ }
+
+ return 0;
+}
+
+/*
+ * Power down a PCIe port. Strictly, PCIe requires us to place the card
+ * in D3hot state before asserting PERST#.
+ */
+static void mvebu_pcie_powerdown(struct mvebu_pcie_port *port)
+{
+ if (port->reset_gpio)
+ gpiod_set_value_cansleep(port->reset_gpio, 1);
+
+ clk_disable_unprepare(port->clk);
+}
+
static int mvebu_pcie_probe(struct platform_device *pdev)
{
struct mvebu_pcie *pcie;
@@ -1114,18 +1154,8 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
if (!child)
continue;
- if (port->reset_gpio) {
- u32 reset_udelay = 20000;
-
- of_property_read_u32(child, "reset-delay-us",
- &reset_udelay);
-
- gpiod_set_value_cansleep(port->reset_gpio, 0);
- msleep(reset_udelay / 1000);
- }
-
- ret = clk_prepare_enable(port->clk);
- if (ret)
+ ret = mvebu_pcie_powerup(port);
+ if (ret < 0)
continue;
port->base = mvebu_pcie_map_registers(pdev, child, port);
@@ -1133,7 +1163,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "%s: cannot map registers\n",
port->name);
port->base = NULL;
- clk_disable_unprepare(port->clk);
+ mvebu_pcie_powerdown(port);
continue;
}