Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PCI: iproc: Add 500ms delay during device shutdown

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>

authored by

Oza Pawandeep and committed by
Bjorn Helgaas
b91c26c6 39b7a4ff

+33 -15
+8
drivers/pci/host/pcie-iproc-platform.c
··· 134 134 return iproc_pcie_remove(pcie); 135 135 } 136 136 137 + static void iproc_pcie_pltfm_shutdown(struct platform_device *pdev) 138 + { 139 + struct iproc_pcie *pcie = platform_get_drvdata(pdev); 140 + 141 + iproc_pcie_shutdown(pcie); 142 + } 143 + 137 144 static struct platform_driver iproc_pcie_pltfm_driver = { 138 145 .driver = { 139 146 .name = "iproc-pcie", ··· 148 141 }, 149 142 .probe = iproc_pcie_pltfm_probe, 150 143 .remove = iproc_pcie_pltfm_remove, 144 + .shutdown = iproc_pcie_pltfm_shutdown, 151 145 }; 152 146 module_platform_driver(iproc_pcie_pltfm_driver); 153 147
+24 -15
drivers/pci/host/pcie-iproc.c
··· 671 671 .write = iproc_pcie_config_write32, 672 672 }; 673 673 674 - static void iproc_pcie_reset(struct iproc_pcie *pcie) 674 + static void iproc_pcie_perst_ctrl(struct iproc_pcie *pcie, bool assert) 675 675 { 676 676 u32 val; 677 677 ··· 683 683 if (pcie->ep_is_internal) 684 684 return; 685 685 686 - /* 687 - * Select perst_b signal as reset source. Put the device into reset, 688 - * and then bring it out of reset 689 - */ 690 - val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL); 691 - val &= ~EP_PERST_SOURCE_SELECT & ~EP_MODE_SURVIVE_PERST & 692 - ~RC_PCIE_RST_OUTPUT; 693 - iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val); 694 - udelay(250); 695 - 696 - val |= RC_PCIE_RST_OUTPUT; 697 - iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val); 698 - msleep(100); 686 + if (assert) { 687 + val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL); 688 + val &= ~EP_PERST_SOURCE_SELECT & ~EP_MODE_SURVIVE_PERST & 689 + ~RC_PCIE_RST_OUTPUT; 690 + iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val); 691 + udelay(250); 692 + } else { 693 + val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL); 694 + val |= RC_PCIE_RST_OUTPUT; 695 + iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val); 696 + msleep(100); 697 + } 699 698 } 699 + 700 + int iproc_pcie_shutdown(struct iproc_pcie *pcie) 701 + { 702 + iproc_pcie_perst_ctrl(pcie, true); 703 + msleep(500); 704 + 705 + return 0; 706 + } 707 + EXPORT_SYMBOL_GPL(iproc_pcie_shutdown); 700 708 701 709 static int iproc_pcie_check_link(struct iproc_pcie *pcie) 702 710 { ··· 1387 1379 goto err_exit_phy; 1388 1380 } 1389 1381 1390 - iproc_pcie_reset(pcie); 1382 + iproc_pcie_perst_ctrl(pcie, true); 1383 + iproc_pcie_perst_ctrl(pcie, false); 1391 1384 1392 1385 if (pcie->need_ob_cfg) { 1393 1386 ret = iproc_pcie_map_ranges(pcie, res);
+1
drivers/pci/host/pcie-iproc.h
··· 110 110 111 111 int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res); 112 112 int iproc_pcie_remove(struct iproc_pcie *pcie); 113 + int iproc_pcie_shutdown(struct iproc_pcie *pcie); 113 114 114 115 #ifdef CONFIG_PCIE_IPROC_MSI 115 116 int iproc_msi_init(struct iproc_pcie *pcie, struct device_node *node);