Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 073d3dbe authored by Brian Norris's avatar Brian Norris Committed by Bjorn Helgaas
Browse files

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: default avatarBrian Norris <briannorris@chromium.org>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
Acked-by: default avatarShawn Lin <shawn.lin@rock-chips.com>
parent 64d6ea60
Loading
Loading
Loading
Loading
+34 −2
Original line number Diff line number Diff line
@@ -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 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
	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);