diff options
author | Brian Norris <briannorris@chromium.org> | 2017-03-09 21:46:15 -0500 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2017-04-21 11:54:35 -0400 |
commit | 073d3dbe9a7c38888d8dafe09df421b174a6cffa (patch) | |
tree | a5d31cfbe63c905a5954096b887e70f631a9a2f7 | |
parent | 64d6ea602ce619633a6e0af979e2c73738f6aeba (diff) |
PCI: rockchip: Add remove() support
Currently, if we try to unbind the platform device, the remove will
succeed, but the removal won't undo most of the registration, leaving
partially-configured PCI devices in the system.
This allows, for example, a simple 'lspci' to crash the system, as it will
try to touch the freed (via devm_*) driver structures, e.g., on RK3399:
# echo f8000000.pcie > /sys/bus/platform/drivers/rockchip-pcie/unbind
# lspci
So let's implement device remove().
Signed-off-by: Brian Norris <briannorris@chromium.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Shawn Lin <shawn.lin@rock-chips.com>
-rw-r--r-- | drivers/pci/host/pcie-rockchip.c | 36 |
1 files changed, 34 insertions, 2 deletions
diff --git a/drivers/pci/host/pcie-rockchip.c b/drivers/pci/host/pcie-rockchip.c index 94feb7dfd8f4..76f2edc93663 100644 --- a/drivers/pci/host/pcie-rockchip.c +++ b/drivers/pci/host/pcie-rockchip.c | |||
@@ -223,9 +223,11 @@ struct rockchip_pcie { | |||
223 | int link_gen; | 223 | int link_gen; |
224 | struct device *dev; | 224 | struct device *dev; |
225 | struct irq_domain *irq_domain; | 225 | struct irq_domain *irq_domain; |
226 | u32 io_size; | ||
227 | int offset; | 226 | int offset; |
227 | struct pci_bus *root_bus; | ||
228 | struct resource *io; | ||
228 | phys_addr_t io_bus_addr; | 229 | phys_addr_t io_bus_addr; |
230 | u32 io_size; | ||
229 | void __iomem *msg_region; | 231 | void __iomem *msg_region; |
230 | u32 mem_size; | 232 | u32 mem_size; |
231 | phys_addr_t msg_bus_addr; | 233 | phys_addr_t msg_bus_addr; |
@@ -1366,6 +1368,7 @@ static int rockchip_pcie_probe(struct platform_device *pdev) | |||
1366 | err, io); | 1368 | err, io); |
1367 | continue; | 1369 | continue; |
1368 | } | 1370 | } |
1371 | rockchip->io = io; | ||
1369 | break; | 1372 | break; |
1370 | case IORESOURCE_MEM: | 1373 | case IORESOURCE_MEM: |
1371 | mem = win->res; | 1374 | mem = win->res; |
@@ -1397,6 +1400,7 @@ static int rockchip_pcie_probe(struct platform_device *pdev) | |||
1397 | err = -ENOMEM; | 1400 | err = -ENOMEM; |
1398 | goto err_free_res; | 1401 | goto err_free_res; |
1399 | } | 1402 | } |
1403 | rockchip->root_bus = bus; | ||
1400 | 1404 | ||
1401 | pci_bus_size_bridges(bus); | 1405 | pci_bus_size_bridges(bus); |
1402 | pci_bus_assign_resources(bus); | 1406 | pci_bus_assign_resources(bus); |
@@ -1427,6 +1431,34 @@ err_aclk_pcie: | |||
1427 | return err; | 1431 | return err; |
1428 | } | 1432 | } |
1429 | 1433 | ||
1434 | static int rockchip_pcie_remove(struct platform_device *pdev) | ||
1435 | { | ||
1436 | struct device *dev = &pdev->dev; | ||
1437 | struct rockchip_pcie *rockchip = dev_get_drvdata(dev); | ||
1438 | |||
1439 | pci_stop_root_bus(rockchip->root_bus); | ||
1440 | pci_remove_root_bus(rockchip->root_bus); | ||
1441 | pci_unmap_iospace(rockchip->io); | ||
1442 | irq_domain_remove(rockchip->irq_domain); | ||
1443 | |||
1444 | phy_power_off(rockchip->phy); | ||
1445 | phy_exit(rockchip->phy); | ||
1446 | |||
1447 | clk_disable_unprepare(rockchip->clk_pcie_pm); | ||
1448 | clk_disable_unprepare(rockchip->hclk_pcie); | ||
1449 | clk_disable_unprepare(rockchip->aclk_perf_pcie); | ||
1450 | clk_disable_unprepare(rockchip->aclk_pcie); | ||
1451 | |||
1452 | if (!IS_ERR(rockchip->vpcie3v3)) | ||
1453 | regulator_disable(rockchip->vpcie3v3); | ||
1454 | if (!IS_ERR(rockchip->vpcie1v8)) | ||
1455 | regulator_disable(rockchip->vpcie1v8); | ||
1456 | if (!IS_ERR(rockchip->vpcie0v9)) | ||
1457 | regulator_disable(rockchip->vpcie0v9); | ||
1458 | |||
1459 | return 0; | ||
1460 | } | ||
1461 | |||
1430 | static const struct dev_pm_ops rockchip_pcie_pm_ops = { | 1462 | static const struct dev_pm_ops rockchip_pcie_pm_ops = { |
1431 | SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rockchip_pcie_suspend_noirq, | 1463 | SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rockchip_pcie_suspend_noirq, |
1432 | rockchip_pcie_resume_noirq) | 1464 | rockchip_pcie_resume_noirq) |
@@ -1444,6 +1476,6 @@ static struct platform_driver rockchip_pcie_driver = { | |||
1444 | .pm = &rockchip_pcie_pm_ops, | 1476 | .pm = &rockchip_pcie_pm_ops, |
1445 | }, | 1477 | }, |
1446 | .probe = rockchip_pcie_probe, | 1478 | .probe = rockchip_pcie_probe, |
1447 | 1479 | .remove = rockchip_pcie_remove, | |
1448 | }; | 1480 | }; |
1449 | builtin_platform_driver(rockchip_pcie_driver); | 1481 | builtin_platform_driver(rockchip_pcie_driver); |