PCI: iproc: Add 500ms delay during device shutdown
authorOza Pawandeep <oza.oza@broadcom.com>
Mon, 28 Aug 2017 21:43:35 +0000 (16:43 -0500)
committerBjorn Helgaas <bhelgaas@google.com>
Tue, 5 Sep 2017 17:27:03 +0000 (12:27 -0500)
During soft reset (e.g., "reboot" from Linux) on some iProc-based SOCs, the
LCPLL clock and PERST both go off simultaneously.  This seems in accordance
with the PCIe Card Electromechanical spec, r2.0, sec 2.2.3, which says the
clock goes inactive after PERST# goes active, but doesn't specify how long
the clock should be valid after PERST#.

However, we have observed that with the iProc Stingray, some Intel NVMe
endpoints, e.g., the P3700 400GB series, are not detected correctly upon
the next boot sequence unless the clock remains valid for some time after
PERST# is asserted.

Delay 500ms after asserting PERST# before performing a reboot.  The 500ms
is experimentally determined.

Signed-off-by: Oza Pawandeep <oza.oza@broadcom.com>
[bhelgaas: changelog, add spec reference, fold in iproc_pcie_shutdown()
export from Arnd Bergmann <arnd@arndb.de>]
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Ray Jui <ray.jui@broadcom.com>
Reviewed-by: Scott Branden <scott.branden@broadcom.com>
drivers/pci/host/pcie-iproc-platform.c
drivers/pci/host/pcie-iproc.c
drivers/pci/host/pcie-iproc.h

index 22531190bc401abb8924045b9c46be987e7eabbb..a5073a921a049c483fe412a7939f3d6579a7a8c8 100644 (file)
@@ -134,6 +134,13 @@ static int iproc_pcie_pltfm_remove(struct platform_device *pdev)
        return iproc_pcie_remove(pcie);
 }
 
+static void iproc_pcie_pltfm_shutdown(struct platform_device *pdev)
+{
+       struct iproc_pcie *pcie = platform_get_drvdata(pdev);
+
+       iproc_pcie_shutdown(pcie);
+}
+
 static struct platform_driver iproc_pcie_pltfm_driver = {
        .driver = {
                .name = "iproc-pcie",
@@ -141,6 +148,7 @@ static struct platform_driver iproc_pcie_pltfm_driver = {
        },
        .probe = iproc_pcie_pltfm_probe,
        .remove = iproc_pcie_pltfm_remove,
+       .shutdown = iproc_pcie_pltfm_shutdown,
 };
 module_platform_driver(iproc_pcie_pltfm_driver);
 
index 9a006cbc302181e02fdbabb38faa16c9985e9b78..fe2e5aaaff557b6099dcff151c830ebc6eff7f17 100644 (file)
@@ -671,7 +671,7 @@ static struct pci_ops iproc_pcie_ops = {
        .write = iproc_pcie_config_write32,
 };
 
-static void iproc_pcie_reset(struct iproc_pcie *pcie)
+static void iproc_pcie_perst_ctrl(struct iproc_pcie *pcie, bool assert)
 {
        u32 val;
 
@@ -683,20 +683,28 @@ static void iproc_pcie_reset(struct iproc_pcie *pcie)
        if (pcie->ep_is_internal)
                return;
 
-       /*
-        * Select perst_b signal as reset source. Put the device into reset,
-        * and then bring it out of reset
-        */
-       val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL);
-       val &= ~EP_PERST_SOURCE_SELECT & ~EP_MODE_SURVIVE_PERST &
-               ~RC_PCIE_RST_OUTPUT;
-       iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
-       udelay(250);
-
-       val |= RC_PCIE_RST_OUTPUT;
-       iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
-       msleep(100);
+       if (assert) {
+               val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL);
+               val &= ~EP_PERST_SOURCE_SELECT & ~EP_MODE_SURVIVE_PERST &
+                       ~RC_PCIE_RST_OUTPUT;
+               iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
+               udelay(250);
+       } else {
+               val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL);
+               val |= RC_PCIE_RST_OUTPUT;
+               iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
+               msleep(100);
+       }
+}
+
+int iproc_pcie_shutdown(struct iproc_pcie *pcie)
+{
+       iproc_pcie_perst_ctrl(pcie, true);
+       msleep(500);
+
+       return 0;
 }
+EXPORT_SYMBOL_GPL(iproc_pcie_shutdown);
 
 static int iproc_pcie_check_link(struct iproc_pcie *pcie)
 {
@@ -1379,7 +1387,8 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
                goto err_exit_phy;
        }
 
-       iproc_pcie_reset(pcie);
+       iproc_pcie_perst_ctrl(pcie, true);
+       iproc_pcie_perst_ctrl(pcie, false);
 
        if (pcie->need_ob_cfg) {
                ret = iproc_pcie_map_ranges(pcie, res);
index 0bbe2ea44f3e1559dda22adc85ea70a8862684bd..a6b55cec9a668e6124e6ca43ec2d4ec87732a0c3 100644 (file)
@@ -110,6 +110,7 @@ struct iproc_pcie {
 
 int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res);
 int iproc_pcie_remove(struct iproc_pcie *pcie);
+int iproc_pcie_shutdown(struct iproc_pcie *pcie);
 
 #ifdef CONFIG_PCIE_IPROC_MSI
 int iproc_msi_init(struct iproc_pcie *pcie, struct device_node *node);