aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Warren <swarren@nvidia.com>2013-05-06 16:19:19 -0400
committerStephen Warren <swarren@nvidia.com>2013-08-13 14:07:56 -0400
commitb4f173752a56187bd55752b0474429202f2ab1d3 (patch)
tree64b5936abc2c238f5114b42915ba3958c32890af
parent0447cfd75a6bad561ebf2b393dcb2263c261b6c5 (diff)
ARM: tegra: disable LP2 cpuidle state if PCIe is enabled
Tegra20 HW appears to have a bug such that PCIe device interrupts, whether they are legacy IRQs or MSI, are lost when LP2 is enabled. To work around this, simply disable LP2 if any PCIe devices with interrupts are present. Detect this via the IRQ domain map operation. This is slightly over-conservative; if a device with an interrupt is present but the driver does not actually use them, LP2 will still be disabled. However, this is a reasonable trade-off which enables a simpler workaround. Signed-off-by: Stephen Warren <swarren@nvidia.com> Tested-by: Thierry Reding <treding@nvidia.com> Acked-by: Thierry Reding <treding@nvidia.com>
-rw-r--r--arch/arm/mach-tegra/cpuidle-tegra20.c12
-rw-r--r--arch/arm/mach-tegra/cpuidle.c10
-rw-r--r--arch/arm/mach-tegra/cpuidle.h1
-rw-r--r--drivers/pci/host/pci-tegra.c5
-rw-r--r--include/linux/tegra-cpuidle.h19
5 files changed, 47 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/cpuidle-tegra20.c b/arch/arm/mach-tegra/cpuidle-tegra20.c
index 706aa4215c36..b82dcaee2ef4 100644
--- a/arch/arm/mach-tegra/cpuidle-tegra20.c
+++ b/arch/arm/mach-tegra/cpuidle-tegra20.c
@@ -211,6 +211,18 @@ static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev,
211} 211}
212#endif 212#endif
213 213
214/*
215 * Tegra20 HW appears to have a bug such that PCIe device interrupts, whether
216 * they are legacy IRQs or MSI, are lost when LP2 is enabled. To work around
217 * this, simply disable LP2 if the PCI driver and DT node are both enabled.
218 */
219void tegra20_cpuidle_pcie_irqs_in_use(void)
220{
221 pr_info_once(
222 "Disabling cpuidle LP2 state, since PCIe IRQs are in use\n");
223 tegra_idle_driver.states[1].disabled = true;
224}
225
214int __init tegra20_cpuidle_init(void) 226int __init tegra20_cpuidle_init(void)
215{ 227{
216 return cpuidle_register(&tegra_idle_driver, cpu_possible_mask); 228 return cpuidle_register(&tegra_idle_driver, cpu_possible_mask);
diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle.c
index e85973cef037..0961dfcf83a4 100644
--- a/arch/arm/mach-tegra/cpuidle.c
+++ b/arch/arm/mach-tegra/cpuidle.c
@@ -44,3 +44,13 @@ void __init tegra_cpuidle_init(void)
44 break; 44 break;
45 } 45 }
46} 46}
47
48void tegra_cpuidle_pcie_irqs_in_use(void)
49{
50 switch (tegra_chip_id) {
51 case TEGRA20:
52 if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC))
53 tegra20_cpuidle_pcie_irqs_in_use();
54 break;
55 }
56}
diff --git a/arch/arm/mach-tegra/cpuidle.h b/arch/arm/mach-tegra/cpuidle.h
index 9ec2c1ab0fa4..c017dab60ffa 100644
--- a/arch/arm/mach-tegra/cpuidle.h
+++ b/arch/arm/mach-tegra/cpuidle.h
@@ -19,6 +19,7 @@
19 19
20#ifdef CONFIG_CPU_IDLE 20#ifdef CONFIG_CPU_IDLE
21int tegra20_cpuidle_init(void); 21int tegra20_cpuidle_init(void);
22void tegra20_cpuidle_pcie_irqs_in_use(void);
22int tegra30_cpuidle_init(void); 23int tegra30_cpuidle_init(void);
23int tegra114_cpuidle_init(void); 24int tegra114_cpuidle_init(void);
24void tegra_cpuidle_init(void); 25void tegra_cpuidle_init(void);
diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c
index ad95c406a6d0..7356741de36b 100644
--- a/drivers/pci/host/pci-tegra.c
+++ b/drivers/pci/host/pci-tegra.c
@@ -41,6 +41,7 @@
41#include <linux/platform_device.h> 41#include <linux/platform_device.h>
42#include <linux/sizes.h> 42#include <linux/sizes.h>
43#include <linux/slab.h> 43#include <linux/slab.h>
44#include <linux/tegra-cpuidle.h>
44#include <linux/tegra-powergate.h> 45#include <linux/tegra-powergate.h>
45#include <linux/vmalloc.h> 46#include <linux/vmalloc.h>
46#include <linux/regulator/consumer.h> 47#include <linux/regulator/consumer.h>
@@ -636,6 +637,8 @@ static int tegra_pcie_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
636{ 637{
637 struct tegra_pcie *pcie = sys_to_pcie(pdev->bus->sysdata); 638 struct tegra_pcie *pcie = sys_to_pcie(pdev->bus->sysdata);
638 639
640 tegra_cpuidle_pcie_irqs_in_use();
641
639 return pcie->irq; 642 return pcie->irq;
640} 643}
641 644
@@ -1221,6 +1224,8 @@ static int tegra_msi_map(struct irq_domain *domain, unsigned int irq,
1221 irq_set_chip_data(irq, domain->host_data); 1224 irq_set_chip_data(irq, domain->host_data);
1222 set_irq_flags(irq, IRQF_VALID); 1225 set_irq_flags(irq, IRQF_VALID);
1223 1226
1227 tegra_cpuidle_pcie_irqs_in_use();
1228
1224 return 0; 1229 return 0;
1225} 1230}
1226 1231
diff --git a/include/linux/tegra-cpuidle.h b/include/linux/tegra-cpuidle.h
new file mode 100644
index 000000000000..dda3647242a4
--- /dev/null
+++ b/include/linux/tegra-cpuidle.h
@@ -0,0 +1,19 @@
1/*
2 * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 */
13
14#ifndef __LINUX_TEGRA_CPUIDLE_H__
15#define __LINUX_TEGRA_CPUIDLE_H__
16
17void tegra_cpuidle_pcie_irqs_in_use(void);
18
19#endif