summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJay Agarwal <jagarwal@nvidia.com>2012-06-20 13:30:39 +0530
committerSimone Willett <swillett@nvidia.com>2012-06-25 16:45:23 -0700
commit2e5fb506472adfaf0b9d7a801b77f4e707140dfb (patch)
tree9a4a05dae4e0ccf362e1fc17db74feac4f8294ee
parentb8c4bf16077a04daf2c895be7cf8da030e45b60e (diff)
arm: tegra: pcie: Fix suspend/resume code
1. Do add port and rescan in resume 2. Assert pcie xclk on power on 3. Remove all bus and devices in suspend 4. Enable msi once after resume also 5. Remove Most of hacks for save and restore config spaces Bug 959642 Bug 956573 Change-Id: Ibfa6902ad1aa2ed0d97f7fe1e305287e38ea0be1 Signed-off-by: Jay Agarwal <jagarwal@nvidia.com> Reviewed-on: http://git-master/r/109700 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
-rw-r--r--arch/arm/mach-tegra/pcie.c177
1 files changed, 30 insertions, 147 deletions
diff --git a/arch/arm/mach-tegra/pcie.c b/arch/arm/mach-tegra/pcie.c
index 94ac556b686a..3c23426e8376 100644
--- a/arch/arm/mach-tegra/pcie.c
+++ b/arch/arm/mach-tegra/pcie.c
@@ -352,14 +352,8 @@ static struct resource pcie_prefetch_mem_space;
/* disable read write while noirq operation
* is performed since pcie is powered off */
static bool is_pcie_noirq_op = false;
-/* used to backup config space registers of all pcie devices */
-static u32 *pbackup_config_space = NULL;
-static u16 *pbackup_pcie_cap_space = NULL;
-static u16 *pbackup_pcix_cap_space = NULL;
-/* use same save state and position variables to store pcie */
-/* and pcix capability offsets at even & odd index respectively */
-static struct pci_cap_saved_state **pcie_save_state;
-static int *pos;
+/* enable and init msi once during boot or resume */
+static bool msi_enable;
void __iomem *tegra_pcie_io_base;
EXPORT_SYMBOL(tegra_pcie_io_base);
@@ -956,6 +950,8 @@ static int tegra_pcie_power_on(void)
err = tegra_unpowergate_partition_with_clk_on(TEGRA_POWERGATE_PCIE);
if (err)
goto err_exit;
+
+ tegra_periph_reset_assert(tegra_pcie.pcie_xclk);
if (tegra_pcie.pll_e)
clk_enable(tegra_pcie.pll_e);
@@ -1180,7 +1176,6 @@ static void tegra_pcie_add_port(int index, u32 offset, u32 reset_reg)
tegra_enable_clock_clamp(index);
tegra_pcie.num_ports++;
pp->index = index;
- pp->root_bus_nr = -1;
memset(pp->res, 0, sizeof(pp->res));
}
@@ -1248,47 +1243,9 @@ err_irq:
return err;
}
-static int tegra_pcie_allocate_config_states(int ndev, int size)
-{
- /* backup config space registers of all devices since it gets reset in
- save state call from suspend noirq due to disabling of read in it */
- pbackup_config_space = kzalloc(ndev*size*sizeof(u32), GFP_KERNEL);
- if (!pbackup_config_space)
- return -ENODEV;
- pbackup_pcie_cap_space = kzalloc(ndev*PCIE_CTRL_REGS*sizeof(u16), GFP_KERNEL);
- if (!pbackup_pcie_cap_space)
- return -ENODEV;
- pbackup_pcix_cap_space = kzalloc(ndev*sizeof(u16), GFP_KERNEL);
- if (!pbackup_pcix_cap_space)
- return -ENODEV;
- pcie_save_state = kzalloc(COMBINE_PCIE_PCIX_SPACE*ndev*
- sizeof(struct pci_cap_saved_state*), GFP_KERNEL);
- if (!pbackup_pcix_cap_space)
- return -ENODEV;
- pos = kzalloc(COMBINE_PCIE_PCIX_SPACE*ndev*sizeof(int), GFP_KERNEL);
- if (!pos)
- return -ENODEV;
-
- return 0;
-}
-
-static void tegra_pcie_deallocate_config_states(void)
-{
- if (pbackup_config_space)
- kzfree(pbackup_config_space);
- if (pbackup_pcie_cap_space)
- kzfree(pbackup_pcie_cap_space);
- if (pbackup_pcix_cap_space)
- kzfree(pbackup_pcix_cap_space);
- if (pcie_save_state)
- kzfree(pcie_save_state);
- if (pos)
- kzfree(pos);
-}
-
static int tegra_pci_probe(struct platform_device *pdev)
{
- int ret, size = 0, ndev = 0;
+ int ret;
struct pci_dev *dev = NULL;
tegra_pcie.plat_data = pdev->dev.platform_data;
@@ -1302,93 +1259,24 @@ static int tegra_pci_probe(struct platform_device *pdev)
/* disable async PM of pci devices to ensure right order */
/* suspend/resume calls of tegra and bus driver */
- for_each_pci_dev(dev){
+ for_each_pci_dev(dev)
device_disable_async_suspend(&dev->dev);
- size = sizeof(dev->saved_config_space) / sizeof(u32);
- ndev++;
- }
- tegra_pcie_allocate_config_states(ndev, size);
return ret;
}
#ifdef CONFIG_PM
-static int tegra_pcie_save_state(struct pci_dev *pdev, int ndev)
-{
- int size;
-
- /*save pcie control registers */
- pos[ndev] = pci_pcie_cap(pdev);
- if (pos[ndev]){
- pcie_save_state[ndev] = pci_find_saved_cap(pdev, PCI_CAP_ID_EXP);
- if (!pcie_save_state[ndev]) {
- dev_err(&pdev->dev, "buffer not found in %s\n", __func__);
- return -ENOMEM;
- }
- memcpy(&pbackup_pcie_cap_space[PCIE_CTRL_REGS*(ndev/2)],
- pcie_save_state[ndev]->cap.data, PCIE_CTRL_REGS*sizeof(u16));
- }
- /* save pcix state */
- pos[ndev+1] = pci_find_capability(pdev, PCI_CAP_ID_PCIX);
- if (pos[ndev+1] > 0){
- pcie_save_state[ndev+1] = pci_find_saved_cap(pdev, PCI_CAP_ID_PCIX);
- if (!pcie_save_state[ndev+1]) {
- dev_err(&pdev->dev, "buffer not found in %s\n", __func__);
- return -ENOMEM;
- }
- memcpy(&pbackup_pcix_cap_space[ndev/2],
- pcie_save_state[ndev+1]->cap.data, sizeof(u16));
- }
- /* save config space registers */
- size = sizeof(pdev->saved_config_space) / sizeof(u32);
- memcpy(&pbackup_config_space[size*ndev/2],
- pdev->saved_config_space, size*sizeof(u32));
-
- return 0;
-}
-
-static void tegra_pcie_restore_state(struct pci_dev *pdev, int ndev)
-{
- int size;
-
- /* restore pcie control registers */
- if (pcie_save_state[ndev] && (pos[ndev] > 0))
- memcpy(pcie_save_state[ndev]->cap.data,
- &pbackup_pcie_cap_space[PCIE_CTRL_REGS*(ndev/2)],
- PCIE_CTRL_REGS*sizeof(u16));
-
- /* restore pcix state */
- if (pcie_save_state[ndev+1] && (pos[ndev+1] > 0))
- memcpy(pcie_save_state[ndev+1]->cap.data,
- &pbackup_pcix_cap_space[ndev/2], sizeof(u16));
-
- /* restore config space registers */
- size = sizeof(pdev->saved_config_space) / sizeof(u32);
- memcpy(pdev->saved_config_space,
- &pbackup_config_space[size*ndev/2], size*sizeof(u32));
-}
-
static int tegra_pci_suspend(struct device *dev)
{
- int ret = 0, ndev = 0;
+ int ret = 0;
struct pci_dev *pdev = NULL;
if (!tegra_pcie.num_ports)
return ret;
for_each_pci_dev(pdev) {
- /* save state of pcie devices before powering off regulators */
- pci_save_state(pdev);
- if (!pdev->subordinate)
- pci_prepare_to_sleep(pdev);
- }
-
- for_each_pci_dev(pdev) {
- /* save control and config space registers*/
- ret = tegra_pcie_save_state(pdev, ndev*2);
- if (ret < 0)
- return ret;
- ndev++;
+ pci_remove_bus_device(pdev);
+ break;
}
/* disable read/write registers before powering off */
@@ -1397,41 +1285,40 @@ static int tegra_pci_suspend(struct device *dev)
return tegra_pcie_power_off();
}
-static int tegra_pci_resume_noirq(struct device *dev)
-{
- struct pci_dev *pdev = NULL;
-
- /* set this flag to avoid restore state in resume noirq */
- for_each_pci_dev(pdev)
- pdev->state_saved = 0;
-
- return 0;
-}
-
static int tegra_pci_resume(struct device *dev)
{
- int ret = 0, ndev = 0;
+ int ret = 0;
+ struct pci_bus *b = NULL;
struct pci_dev *pdev = NULL;
- int port;
+ int port, rp_offset = 0;
+ int ctrl_offset = AFI_PEX0_CTRL;
if (!tegra_pcie.num_ports)
return ret;
+
ret = tegra_pcie_power_on();
/* enable read/write registers after powering on */
is_pcie_noirq_op = false;
tegra_pcie_enable_controller();
tegra_pcie_setup_translations();
+ msi_enable = false;
- for (port = 0; port < MAX_PCIE_SUPPORTED_PORTS; port++)
+ /* reset number of ports before adding port */
+ tegra_pcie.num_ports = 0;
+ for (port = 0; port < MAX_PCIE_SUPPORTED_PORTS; port++) {
+ ctrl_offset += (port * 8);
+ rp_offset = (rp_offset + 0x1000) * port;
if (tegra_pcie.plat_data->port_status[port])
- tegra_enable_clock_clamp(port);
+ tegra_pcie_add_port(port, rp_offset, ctrl_offset);
+ }
+ while ((b = pci_find_next_bus(b)) != NULL)
+ pci_rescan_bus(b);
+
+ /* Update irq line register since it is not done while scan */
for_each_pci_dev(pdev) {
- /* restore control and config space registers*/
- tegra_pcie_restore_state(pdev, ndev*2);
- /* set this flag to force restore state in resume */
- pdev->state_saved = 1;
- ndev++;
+ pdev->irq = tegra_pcie_map_irq(pdev,0,0);
+ pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, pdev->irq);
}
return ret;
@@ -1440,14 +1327,12 @@ static int tegra_pci_resume(struct device *dev)
static int tegra_pci_remove(struct platform_device *pdev)
{
- tegra_pcie_deallocate_config_states();
return 0;
}
#ifdef CONFIG_PM
static const struct dev_pm_ops tegra_pci_pm_ops = {
.suspend = tegra_pci_suspend,
.resume = tegra_pci_resume,
- .resume_noirq = tegra_pci_resume_noirq,
};
#endif
@@ -1573,18 +1458,16 @@ static irqreturn_t pci_tegra_msi_isr(int irq, void *arg)
static bool pci_tegra_enable_msi(void)
{
bool retval = false;
- static bool already_done;
u32 reg;
u32 msi_base = 0;
u32 msi_aligned = 0;
/* enables MSI interrupts. */
/* this only happens once. */
- if (already_done) {
+ if (msi_enable) {
retval = true;
goto exit;
}
-
msi_map_init();
if (request_irq(INT_PCIE_MSI, pci_tegra_msi_isr,
@@ -1629,7 +1512,7 @@ static bool pci_tegra_enable_msi(void)
set_irq_flags(INT_PCIE_MSI, IRQF_VALID);
- already_done = true;
+ msi_enable = true;
retval = true;
exit:
if (!retval) {