diff options
author | Thierry Reding <treding@nvidia.com> | 2013-08-13 12:49:25 +0200 |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2014-06-04 09:30:42 +0200 |
commit | aa819f16beba15bfcf62ff0dc5994571fd15243e (patch) | |
tree | ce235262d9fdb731b0983b6c0339a61c9396e98c /drivers | |
parent | 63cbc0ed32676744f5e38a804129b83ff7903445 (diff) |
PCI: tegra: Support driver unbinding
Implement the platform driver's .remove() callback to free all resources
allocated during driver setup and call pci_common_exit() to cleanup ARM
specific datastructures. Unmap the fixed PCI I/O mapping by calling the
new pci_iounmap_io() function in the new .teardown() callback.
Finally, no longer set the .suppress_bind_attrs field to true to allow
the driver to unbind from a device.
Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pci/host/pci-tegra.c | 45 |
1 files changed, 44 insertions, 1 deletions
diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index d6d70e6bdd3..02a2809d6bc 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -251,6 +251,7 @@ struct tegra_pcie { int irq; struct list_head buses; + struct list_head sys; struct resource *cs; struct resource all; @@ -686,6 +687,11 @@ static struct pci_bus *tegra_pcie_scan_bus(int nr, struct pci_sys_data *sys) return bus; } +static void tegra_pcie_teardown(int nr, struct pci_sys_data *sys) +{ + pci_iounmap_io(nr * SZ_64K); +} + static irqreturn_t tegra_pcie_isr(int irq, void *arg) { const char *err_msg[] = { @@ -1628,7 +1634,9 @@ static int tegra_pcie_enable(struct tegra_pcie *pcie) hw.map_irq = tegra_pcie_map_irq; hw.add_bus = tegra_pcie_add_bus; hw.scan = tegra_pcie_scan_bus; + hw.teardown = tegra_pcie_teardown; hw.ops = &tegra_pcie_ops; + hw.sys = &pcie->sys; pci_common_init_dev(pcie->dev, &hw); @@ -1775,6 +1783,11 @@ remove: return -ENOMEM; } +static void tegra_pcie_debugfs_exit(struct tegra_pcie *pcie) +{ + debugfs_remove_recursive(pcie->debugfs); +} + static int tegra_pcie_probe(struct platform_device *pdev) { const struct of_device_id *match; @@ -1791,6 +1804,7 @@ static int tegra_pcie_probe(struct platform_device *pdev) INIT_LIST_HEAD(&pcie->buses); INIT_LIST_HEAD(&pcie->ports); + INIT_LIST_HEAD(&pcie->sys); pcie->soc_data = match->data; pcie->dev = &pdev->dev; @@ -1847,14 +1861,43 @@ put_resources: return err; } +static int tegra_pcie_remove(struct platform_device *pdev) +{ + struct tegra_pcie *pcie = platform_get_drvdata(pdev); + struct tegra_pcie_bus *bus, *tmp; + int err; + + if (IS_ENABLED(CONFIG_DEBUG_FS)) + tegra_pcie_debugfs_exit(pcie); + + pci_common_exit(&pcie->sys); + + list_for_each_entry_safe(bus, tmp, &pcie->buses, list) { + vunmap(bus->area->addr); + kfree(bus); + } + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + err = tegra_pcie_disable_msi(pcie); + if (err < 0) + return err; + } + + err = tegra_pcie_put_resources(pcie); + if (err < 0) + return err; + + return 0; +} + static struct platform_driver tegra_pcie_driver = { .driver = { .name = "tegra-pcie", .owner = THIS_MODULE, .of_match_table = tegra_pcie_of_match, - .suppress_bind_attrs = true, }, .probe = tegra_pcie_probe, + .remove = tegra_pcie_remove, }; module_platform_driver(tegra_pcie_driver); |