summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorThierry Reding <treding@nvidia.com>2013-08-13 12:49:25 +0200
committerThierry Reding <treding@nvidia.com>2014-06-04 09:30:42 +0200
commitaa819f16beba15bfcf62ff0dc5994571fd15243e (patch)
treece235262d9fdb731b0983b6c0339a61c9396e98c /drivers
parent63cbc0ed32676744f5e38a804129b83ff7903445 (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.c45
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);