summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Agner <stefan.agner@toradex.com>2018-02-19 17:15:23 +0100
committerStefan Agner <stefan.agner@toradex.com>2018-02-19 17:15:23 +0100
commit91165e6fe1c95e7786385cafed9897ef9c7803c1 (patch)
tree7b83f927dbbc5ae4d1455334f814adfc30549915
parent97627bc30c391695a5bc10eeb19f762420b9772d (diff)
PCI: imx6: make sure peripheral clock is available early
When the second controller initializes PCIe while the first failed to initialize, the X2 peripheral clock will be off. This leads to the following crash: OF: PCI: host bridge /pcie@0x5f010000 ranges: OF: PCI: No bus range found for /pcie@0x5f010000, using [bus 00-ff] OF: PCI: IO 0x7ff80000..0x7ff8ffff -> 0x00000000 OF: PCI: MEM 0x70000000..0x7fefffff -> 0x70000000 Unhandled fault: synchronous external abort (0x96000210) at 0xffff00000e5b0008 Internal error: : 96000210 [#1] PREEMPT SMP Modules linked in: CPU: 2 PID: 1212 Comm: kworker/2:1 Not tainted 4.9.51-imx_4.9.51_8qm_beta2_toradex-06824-ga0b0515b3992-dirty #68 Hardware name: Toradex Apalis iMX8QM (DT) Workqueue: events deferred_probe_work_func task: ffff8008786a8000 task.stack: ffff800878e88000 PC is at regmap_mmio_read32le+0x8/0x18 LR is at regmap_mmio_read+0x40/0x68 pc : [<ffff000008629870>] lr : [<ffff000008629968>] pstate: 000001c5 sp : ffff800878e8b9a0 x29: ffff800878e8b9a0 x28: 0000000000000000 x27: ffff800878e8bba0 x26: ffff8008793ab700 x25: 0000000000000000 x24: 0000000000000000 x23: 0000000000200000 x22: ffff800878e8ba5c x21: ffff800878e8ba5c x20: 00000000000b0008 x19: ffff8008793abc80 x18: 0000000000000001 x17: 00000000dbe14aa8 x16: 0000000069551cdf x15: ffffffffffffffff x14: ffff800879342f04 x13: ffff800879342f03 x12: 0000000000000038 x11: 0101010101010101 x10: 0000000000000040 x9 : ffff00000921e600 x8 : ffff80087651c000 x7 : ffff80087651c028 x6 : 0000000000000000 x5 : 0000000000000000 x4 : 0000000000000000 x3 : ffff000008629928 x2 : ffff000008629868 x1 : 00000000000b0008 x0 : ffff00000e5b0008 Process kworker/2:1 (pid: 1212, stack limit = 0xffff800878e88020) Stack: (0xffff800878e8b9a0 to 0xffff800878e8c000) ... [<ffff000008629870>] regmap_mmio_read32le+0x8/0x18 [<ffff00000862208c>] _regmap_bus_reg_read+0x14/0x20 [<ffff000008623988>] _regmap_read+0x60/0xe0 [<ffff000008623d6c>] _regmap_update_bits+0x9c/0xd8 [<ffff000008624d78>] regmap_update_bits_base+0x60/0x90 [<ffff0000084633dc>] imx6_pcie_assert_core_reset+0x184/0x338 [<ffff000008465ce4>] imx6_pcie_host_init+0x34/0x2c0 [<ffff000008462bf8>] dw_pcie_host_init+0x230/0x518 [<ffff000008464ea4>] imx6_pcie_probe+0x49c/0x970 [<ffff000008609fe0>] platform_drv_probe+0x58/0xb8 [<ffff000008608304>] driver_probe_device+0x1cc/0x2c0 [<ffff000008608550>] __device_attach_driver+0x98/0xf0 [<ffff000008606508>] bus_for_each_drv+0x48/0x98 [<ffff000008608018>] __device_attach+0xc0/0x130 [<ffff000008608600>] device_initial_probe+0x10/0x18 [<ffff000008607580>] bus_probe_device+0x90/0x98 [<ffff000008607a1c>] deferred_probe_work_func+0x7c/0xb8 [<ffff0000080d3dac>] process_one_work+0x1d4/0x370 [<ffff0000080d3f8c>] worker_thread+0x44/0x4a8 [<ffff0000080d9e68>] kthread+0xf8/0x100 [<ffff000008082e80>] ret_from_fork+0x10/0x50 Code: d65f03c0 d503201f f9400000 8b214000 (b9400000) Make sure that the peripheral clock is enabled right from start before any register access. Signed-off-by: Stefan Agner <stefan.agner@toradex.com>
-rw-r--r--drivers/pci/host/pci-imx6.c52
1 files changed, 30 insertions, 22 deletions
diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c
index a92679e13eb0..84db823d04dc 100644
--- a/drivers/pci/host/pci-imx6.c
+++ b/drivers/pci/host/pci-imx6.c
@@ -541,12 +541,6 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
dev_err(dev, "unable to enable pcie_axi clock\n");
break;
}
- ret = clk_prepare_enable(imx6_pcie->pcie_per);
- if (ret) {
- dev_err(dev, "unable to enable pcie_per clock\n");
- clk_disable_unprepare(imx6_pcie->pcie_inbound_axi);
- break;
- }
break;
}
@@ -666,10 +660,12 @@ static int imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
goto err_pcie_bus;
}
- ret = clk_prepare_enable(imx6_pcie->pcie_ext);
- if (ret) {
- dev_err(pp->dev, "unable to enable pcie_ext clock\n");
- goto err_pcie_bus;
+ if (imx6_pcie->pcie_ext) {
+ ret = clk_prepare_enable(imx6_pcie->pcie_ext);
+ if (ret) {
+ dev_err(pp->dev, "unable to enable pcie_ext clock\n");
+ goto err_pcie_bus;
+ }
}
@@ -1239,7 +1235,6 @@ static void pci_imx_clk_disable(struct device *dev)
break;
case IMX8QXP:
case IMX8QM:
- clk_disable_unprepare(imx6_pcie->pcie_per);
clk_disable_unprepare(imx6_pcie->pcie_inbound_axi);
break;
}
@@ -2128,7 +2123,6 @@ static int imx6_pcie_probe(struct platform_device *pdev)
dev_err(dev, "pcie_per clock source missing or invalid\n");
return PTR_ERR(imx6_pcie->pcie_per);
}
-
imx6_pcie->iomuxc_gpr =
syscon_regmap_lookup_by_phandle(node, "hsio");
imx6_pcie->pcie_inbound_axi = devm_clk_get(&pdev->dev,
@@ -2138,6 +2132,12 @@ static int imx6_pcie_probe(struct platform_device *pdev)
"pcie clock source missing or invalid\n");
return PTR_ERR(imx6_pcie->pcie_inbound_axi);
}
+
+ ret = clk_prepare_enable(imx6_pcie->pcie_per);
+ if (ret) {
+ dev_err(dev, "unable to enable pcie_per clock\n");
+ return ret;
+ }
} else {
imx6_pcie->iomuxc_gpr =
syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
@@ -2145,7 +2145,8 @@ static int imx6_pcie_probe(struct platform_device *pdev)
if (IS_ERR(imx6_pcie->iomuxc_gpr)) {
dev_err(dev, "unable to find iomuxc registers\n");
- return PTR_ERR(imx6_pcie->iomuxc_gpr);
+ ret = PTR_ERR(imx6_pcie->iomuxc_gpr);
+ goto err_disable_pcie_per;
}
/* Grab PCIe PHY Tx Settings */
@@ -2192,18 +2193,18 @@ static int imx6_pcie_probe(struct platform_device *pdev)
/* add attributes for device */
ret = sysfs_create_group(&pdev->dev.kobj, &imx_pcie_attrgroup);
if (ret)
- return -EINVAL;
+ goto err_disable_pcie_per;
ret = of_pci_get_host_bridge_resources(np, 0, 0xff, &res,
&pp->io_base);
if (ret)
- return ret;
+ goto err_disable_pcie_per;
ret = devm_request_pci_bus_resources(&pdev->dev, &res);
if (ret) {
dev_err(pp->dev, "missing ranges property\n");
pci_free_resource_list(&res);
- return ret;
+ goto err_disable_pcie_per;
}
/* Get the I/O and memory ranges from DT */
@@ -2224,7 +2225,7 @@ static int imx6_pcie_probe(struct platform_device *pdev)
ret = imx6_pcie_host_init(pp);
if (ret) {
dev_info(dev, " fail to initialize pcie ep.\n");
- return ret;
+ goto err_disable_pcie_per;
}
imx6_pcie_setup_ep(pp);
@@ -2261,7 +2262,7 @@ static int imx6_pcie_probe(struct platform_device *pdev)
if (!test_reg1) {
dev_err(dev, "pcie ep: can't alloc the test reg1.\n");
ret = PTR_ERR(test_reg1);
- return ret;
+ goto err_disable_pcie_per;
}
test_reg2 = devm_kzalloc(&pdev->dev,
@@ -2269,7 +2270,7 @@ static int imx6_pcie_probe(struct platform_device *pdev)
if (!test_reg2) {
dev_err(dev, "pcie ep: can't alloc the test reg2.\n");
ret = PTR_ERR(test_reg1);
- return ret;
+ goto err_disable_pcie_per;
}
/*
@@ -2284,7 +2285,7 @@ static int imx6_pcie_probe(struct platform_device *pdev)
if (!pcie_arb_base_addr) {
dev_err(dev, "error with ioremap in ep selftest\n");
ret = PTR_ERR(pcie_arb_base_addr);
- return ret;
+ goto err_disable_pcie_per;
}
for (i = 0; i < test_region_size; i = i + 4) {
@@ -2337,7 +2338,7 @@ static int imx6_pcie_probe(struct platform_device *pdev)
imx_pcie_attrgroup.attrs = imx_pcie_rc_attrs;
ret = sysfs_create_group(&pdev->dev.kobj, &imx_pcie_attrgroup);
if (ret)
- return -EINVAL;
+ goto err_disable_pcie_per;
ret = imx6_add_pcie_port(imx6_pcie, pdev);
if (ret < 0) {
@@ -2348,13 +2349,18 @@ static int imx6_pcie_probe(struct platform_device *pdev)
} else {
dev_err(dev, "unable to add pcie port.\n");
}
- return ret;
+ goto err_disable_pcie_per;
}
if (IS_ENABLED(CONFIG_RC_MODE_IN_EP_RC_SYS)
&& (imx6_pcie->hard_wired == 0))
imx_pcie_regions_setup(&pdev->dev);
}
+
return 0;
+
+err_disable_pcie_per:
+ clk_disable_unprepare(imx6_pcie->pcie_per);
+ return ret;
}
static void imx6_pcie_shutdown(struct platform_device *pdev)
@@ -2364,6 +2370,8 @@ static void imx6_pcie_shutdown(struct platform_device *pdev)
/* bring down link, so bootloader gets clean state in case of reboot */
if (imx6_pcie->variant == IMX6Q)
imx6_pcie_assert_core_reset(imx6_pcie);
+
+ clk_disable_unprepare(imx6_pcie->pcie_per);
}
static const struct of_device_id imx6_pcie_of_match[] = {