summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJay Agarwal <jagarwal@nvidia.com>2012-06-22 15:36:34 +0530
committerSimone Willett <swillett@nvidia.com>2012-06-25 16:45:37 -0700
commit5d811e3f6beb17bc273e480d1631fbe622fe3c79 (patch)
tree9eda2c0d63554b5c596ba0915dbf9bb18a4798d5
parentbaf58babc3f4729b57f6d70b23475031076d3d03 (diff)
arm: tegra: pcie: Add hotplug functionality
1. do power off on suspend and power on while resume and other initialization. 2. call same functionality as suspend/resume for disconnect/connect of hotplug also. Bug 946385 Change-Id: Ic6906a8641f418cc3e5ee86beaf6fb3f71081174 Signed-off-by: Jay Agarwal <jagarwal@nvidia.com> Reviewed-on: http://git-master/r/110343 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com> GVS: Gerrit_Virtual_Submit Reviewed-by: Varun Wadekar <vwadekar@nvidia.com>
-rw-r--r--arch/arm/mach-tegra/pcie.c71
1 files changed, 65 insertions, 6 deletions
diff --git a/arch/arm/mach-tegra/pcie.c b/arch/arm/mach-tegra/pcie.c
index 3c23426e8376..6b23f7d3909e 100644
--- a/arch/arm/mach-tegra/pcie.c
+++ b/arch/arm/mach-tegra/pcie.c
@@ -354,6 +354,9 @@ static struct resource pcie_prefetch_mem_space;
static bool is_pcie_noirq_op = false;
/* enable and init msi once during boot or resume */
static bool msi_enable;
+/* this flag is used for enumeration by hotplug */
+/* when dock is not connected while system boot */
+static bool is_dock_conn_at_boot = true;
void __iomem *tegra_pcie_io_base;
EXPORT_SYMBOL(tegra_pcie_io_base);
@@ -616,6 +619,60 @@ static struct hw_pci tegra_pcie_hw = {
.map_irq = tegra_pcie_map_irq,
};
+#ifdef CONFIG_PM
+static int tegra_pci_suspend(struct device *dev);
+static int tegra_pci_resume(struct device *dev);
+
+/* It enumerates the devices when dock is connected after system boot */
+/* this is similar to pcibios_init_hw in bios32.c */
+static void tegra_pcie_hotplug_init(void)
+{
+ struct pci_sys_data *sys = NULL;
+ int ret, nr;
+
+ if (is_dock_conn_at_boot)
+ return;
+
+ tegra_pcie_preinit();
+ for (nr = 0; nr < tegra_pcie_hw.nr_controllers; nr++) {
+ sys = kzalloc(sizeof(struct pci_sys_data), GFP_KERNEL);
+ if (!sys)
+ panic("PCI: unable to allocate sys data!");
+
+#ifdef CONFIG_PCI_DOMAINS
+ sys->domain = tegra_pcie_hw.domain;
+#endif
+ sys->hw = &tegra_pcie_hw;
+ sys->busnr = nr;
+ sys->swizzle = tegra_pcie_hw.swizzle;
+ sys->map_irq = tegra_pcie_hw.map_irq;
+ sys->resource[0] = &ioport_resource;
+ sys->resource[1] = &iomem_resource;
+ ret = tegra_pcie_setup(nr, sys);
+ if (ret > 0)
+ pci_create_bus(NULL, nr, &tegra_pcie_ops, sys);
+ }
+ is_dock_conn_at_boot = true;
+}
+#endif
+
+static void tegra_pcie_attach(void)
+{
+ /* this hardcode is just to bypass the check in resume */
+ if (!is_dock_conn_at_boot)
+ tegra_pcie.num_ports = 1;
+#ifdef CONFIG_PM
+ tegra_pci_resume(NULL);
+#endif
+}
+
+static void tegra_pcie_detach(void)
+{
+#ifdef CONFIG_PM
+ tegra_pci_suspend(NULL);
+#endif
+}
+
static void work_hotplug_handler(struct work_struct *work)
{
struct tegra_pcie_info *pcie_driver =
@@ -626,13 +683,11 @@ static void work_hotplug_handler(struct work_struct *work)
return;
val = gpio_get_value(pcie_driver->plat_data->gpio);
if (val == 0) {
- pr_info("Pcie Dock Connected but hotplug functionality not supported yet\n");
+ pr_info("Pcie Dock Connected\n");
+ tegra_pcie_attach();
} else {
- struct pci_dev *dev = NULL;
-
pr_info("Pcie Dock DisConnected\n");
- for_each_pci_dev(dev)
- pci_stop_bus_device(dev);
+ tegra_pcie_detach();
}
}
@@ -1235,8 +1290,11 @@ static int tegra_pcie_init(void)
if (tegra_pcie.num_ports)
pci_common_init(&tegra_pcie_hw);
- else
+ else {
+ /* no dock is connected, hotplug will occur after boot */
err = tegra_pcie_power_off();
+ is_dock_conn_at_boot = false;
+ }
err_irq:
@@ -1312,6 +1370,7 @@ static int tegra_pci_resume(struct device *dev)
tegra_pcie_add_port(port, rp_offset, ctrl_offset);
}
+ tegra_pcie_hotplug_init();
while ((b = pci_find_next_bus(b)) != NULL)
pci_rescan_bus(b);