PCI: mvebu: Improve clock/reset handling
authorRussell King <rmk+kernel@arm.linux.org.uk>
Sat, 3 Oct 2015 18:13:27 +0000 (19:13 +0100)
committerBjorn Helgaas <bhelgaas@google.com>
Fri, 9 Oct 2015 16:21:38 +0000 (11:21 -0500)
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 <w@1wt.eu> (Iomega iConnect Kirkwood, MiraBox Armada 370)
Tested-by: Andrew Lunn <andrew@lunn.ch> (D-Link DIR664 Kirkwood)
Tested-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> (Armada XP GP)
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
drivers/pci/host/pci-mvebu.c

index ab619ee0ef4916222e5e9ce8d72d688fb45c7f48..97208233d9015b5e4777fec36ef0d26696bc7739 100644 (file)
@@ -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;
                }
 
This page took 0.026202 seconds and 5 git commands to generate.