]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
PCI: rockchip: Add remove() support
authorBrian Norris <briannorris@chromium.org>
Fri, 10 Mar 2017 02:46:15 +0000 (18:46 -0800)
committerBjorn Helgaas <bhelgaas@google.com>
Fri, 21 Apr 2017 15:54:35 +0000 (10:54 -0500)
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>
drivers/pci/host/pcie-rockchip.c

index 94feb7dfd8f4f454e4887042865c3f60fb478525..76f2edc9366363bb038dfbd0ec6c4cb911323d09 100644 (file)
@@ -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);