diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 1396fd2d9..4d583446c 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -4079,6 +4079,14 @@ nomsi Do not use MSI for native PCIe PME signaling (this makes all PCIe root ports use INTx for all services). + pcie_rockchip_host.bus_scan_delay= [PCIE] Delay in ms before + scanning PCIe bus in Rockchip PCIe host driver. Some PCIe + cards seem to need delays that can be several hundred ms. + If set to greater than or equal to 0 this parameter will + override delay that can be set in device tree. + Values less than 0 mean that this parameter is ignored. + default=-1 + pcmv= [HW,PCMCIA] BadgePAD 4 pd_ignore_unused diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c index c52316d0b..a7974007d 100644 --- a/drivers/pci/controller/pcie-rockchip-host.c +++ b/drivers/pci/controller/pcie-rockchip-host.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,9 @@ #include "../pci.h" #include "pcie-rockchip.h" +static int bus_scan_delay = -1; +module_param_named(bus_scan_delay, bus_scan_delay, int, S_IRUGO); + static void rockchip_pcie_enable_bw_int(struct rockchip_pcie *rockchip) { u32 status; @@ -935,6 +939,7 @@ static int rockchip_pcie_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct pci_host_bridge *bridge; int err; + u32 delay = 0; if (!dev->of_node) return -ENODEV; @@ -984,6 +989,26 @@ static int rockchip_pcie_probe(struct platform_device *pdev) bridge->sysdata = rockchip; bridge->ops = &rockchip_pcie_ops; + /* Checking if bus scan delay was given from command line and prefer + * that over the value in device tree (which defaults to 0 if not set). + */ + if (bus_scan_delay >= 0) { + delay = bus_scan_delay; + dev_info(dev, "wait %u ms (from command-line) before bus scan\n", delay); + } else { + delay = rockchip->bus_scan_delay; + dev_info(dev, "wait %u ms (from device tree) before bus scan\n", delay); + } + /* Workaround for some devices crashing on pci_host_probe / pci_scan_root_bus_bridge + * calls: sleep a bit before bus scan. Call trace gets to rockchip_pcie_rd_conf when + * trying to read vendor id (pci_bus_generic_read_dev_vendor_id is in call stack) + * before panicing. I have no idea why this works or what causes the panic. I just + * found this hack by luck when trying to "make it break differently if possible". + */ + if (delay > 0) { + msleep(delay); + } + err = rockchip_pcie_setup_irq(rockchip); if (err) goto err_remove_irq_domain; diff --git a/drivers/pci/controller/pcie-rockchip.c b/drivers/pci/controller/pcie-rockchip.c index 193d26562..ec6cbaadd 100644 --- a/drivers/pci/controller/pcie-rockchip.c +++ b/drivers/pci/controller/pcie-rockchip.c @@ -148,6 +148,12 @@ int rockchip_pcie_parse_dt(struct rockchip_pcie *rockchip) return PTR_ERR(rockchip->clk_pcie_pm); } + err = of_property_read_u32(node, "bus-scan-delay-ms", &rockchip->bus_scan_delay); + if (err) { + dev_info(dev, "no bus scan delay, default to 0 ms\n"); + rockchip->bus_scan_delay = 0; + } + return 0; } EXPORT_SYMBOL_GPL(rockchip_pcie_parse_dt); diff --git a/drivers/pci/controller/pcie-rockchip.h b/drivers/pci/controller/pcie-rockchip.h index 1650a5087..35a8cf157 100644 --- a/drivers/pci/controller/pcie-rockchip.h +++ b/drivers/pci/controller/pcie-rockchip.h @@ -300,6 +300,8 @@ struct rockchip_pcie { phys_addr_t msg_bus_addr; bool is_rc; struct resource *mem_res; + /* Bus scan delay is a workaround for some pcie devices causing crashes */ + u32 bus_scan_delay; }; static u32 rockchip_pcie_read(struct rockchip_pcie *rockchip, u32 reg)