diff options
author | Brian Norris <briannorris@chromium.org> | 2017-03-09 18:46:15 -0800 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2017-04-21 10:54:35 -0500 |
commit | 073d3dbe9a7c38888d8dafe09df421b174a6cffa (patch) | |
tree | a5d31cfbe63c905a5954096b887e70f631a9a2f7 /drivers/pci | |
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>
Diffstat (limited to 'drivers/pci')
-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 { int link_gen; struct device *dev; struct irq_domain *irq_domain; - u32 io_size; int offset; + struct pci_bus *root_bus; + struct resource *io; phys_addr_t io_bus_addr; + u32 io_size; void __iomem *msg_region; u32 mem_size; phys_addr_t msg_bus_addr; @@ -1366,6 +1368,7 @@ static int rockchip_pcie_probe(struct platform_device *pdev) err, io); continue; } + rockchip->io = io; break; case IORESOURCE_MEM: mem = win->res; @@ -1397,6 +1400,7 @@ static int rockchip_pcie_probe(struct platform_device *pdev) err = -ENOMEM; goto err_free_res; } + rockchip->root_bus = bus; pci_bus_size_bridges(bus); pci_bus_assign_resources(bus); @@ -1427,6 +1431,34 @@ err_aclk_pcie: return err; } +static int rockchip_pcie_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rockchip_pcie *rockchip = dev_get_drvdata(dev); + + pci_stop_root_bus(rockchip->root_bus); + pci_remove_root_bus(rockchip->root_bus); + pci_unmap_iospace(rockchip->io); + irq_domain_remove(rockchip->irq_domain); + + phy_power_off(rockchip->phy); + phy_exit(rockchip->phy); + + clk_disable_unprepare(rockchip->clk_pcie_pm); + clk_disable_unprepare(rockchip->hclk_pcie); + clk_disable_unprepare(rockchip->aclk_perf_pcie); + clk_disable_unprepare(rockchip->aclk_pcie); + + if (!IS_ERR(rockchip->vpcie3v3)) + regulator_disable(rockchip->vpcie3v3); + if (!IS_ERR(rockchip->vpcie1v8)) + regulator_disable(rockchip->vpcie1v8); + if (!IS_ERR(rockchip->vpcie0v9)) + regulator_disable(rockchip->vpcie0v9); + + return 0; +} + static const struct dev_pm_ops rockchip_pcie_pm_ops = { SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rockchip_pcie_suspend_noirq, rockchip_pcie_resume_noirq) @@ -1444,6 +1476,6 @@ static struct platform_driver rockchip_pcie_driver = { .pm = &rockchip_pcie_pm_ops, }, .probe = rockchip_pcie_probe, - + .remove = rockchip_pcie_remove, }; builtin_platform_driver(rockchip_pcie_driver); |