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>
This commit is contained in:
parent
64d6ea602c
commit
073d3dbe9a
@ -223,9 +223,11 @@ struct rockchip_pcie {
|
|||||||
int link_gen;
|
int link_gen;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct irq_domain *irq_domain;
|
struct irq_domain *irq_domain;
|
||||||
u32 io_size;
|
|
||||||
int offset;
|
int offset;
|
||||||
|
struct pci_bus *root_bus;
|
||||||
|
struct resource *io;
|
||||||
phys_addr_t io_bus_addr;
|
phys_addr_t io_bus_addr;
|
||||||
|
u32 io_size;
|
||||||
void __iomem *msg_region;
|
void __iomem *msg_region;
|
||||||
u32 mem_size;
|
u32 mem_size;
|
||||||
phys_addr_t msg_bus_addr;
|
phys_addr_t msg_bus_addr;
|
||||||
@ -1366,6 +1368,7 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
|
|||||||
err, io);
|
err, io);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
rockchip->io = io;
|
||||||
break;
|
break;
|
||||||
case IORESOURCE_MEM:
|
case IORESOURCE_MEM:
|
||||||
mem = win->res;
|
mem = win->res;
|
||||||
@ -1397,6 +1400,7 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
|
|||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto err_free_res;
|
goto err_free_res;
|
||||||
}
|
}
|
||||||
|
rockchip->root_bus = bus;
|
||||||
|
|
||||||
pci_bus_size_bridges(bus);
|
pci_bus_size_bridges(bus);
|
||||||
pci_bus_assign_resources(bus);
|
pci_bus_assign_resources(bus);
|
||||||
@ -1427,6 +1431,34 @@ err_aclk_pcie:
|
|||||||
return err;
|
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 = {
|
static const struct dev_pm_ops rockchip_pcie_pm_ops = {
|
||||||
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rockchip_pcie_suspend_noirq,
|
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rockchip_pcie_suspend_noirq,
|
||||||
rockchip_pcie_resume_noirq)
|
rockchip_pcie_resume_noirq)
|
||||||
@ -1444,6 +1476,6 @@ static struct platform_driver rockchip_pcie_driver = {
|
|||||||
.pm = &rockchip_pcie_pm_ops,
|
.pm = &rockchip_pcie_pm_ops,
|
||||||
},
|
},
|
||||||
.probe = rockchip_pcie_probe,
|
.probe = rockchip_pcie_probe,
|
||||||
|
.remove = rockchip_pcie_remove,
|
||||||
};
|
};
|
||||||
builtin_platform_driver(rockchip_pcie_driver);
|
builtin_platform_driver(rockchip_pcie_driver);
|
||||||
|
Loading…
Reference in New Issue
Block a user