diff options
Diffstat (limited to 'arch/arm/mach-tegra/cpuidle-tegra20.c')
| -rw-r--r-- | arch/arm/mach-tegra/cpuidle-tegra20.c | 100 |
1 files changed, 37 insertions, 63 deletions
diff --git a/arch/arm/mach-tegra/cpuidle-tegra20.c b/arch/arm/mach-tegra/cpuidle-tegra20.c index 825ced4f7a4..b82dcaee2ef 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra20.c +++ b/arch/arm/mach-tegra/cpuidle-tegra20.c @@ -43,32 +43,33 @@ static atomic_t abort_barrier; static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index); +#define TEGRA20_MAX_STATES 2 +#else +#define TEGRA20_MAX_STATES 1 #endif -static struct cpuidle_state tegra_idle_states[] = { - [0] = ARM_CPUIDLE_WFI_STATE_PWR(600), -#ifdef CONFIG_PM_SLEEP - [1] = { - .enter = tegra20_idle_lp2_coupled, - .exit_latency = 5000, - .target_residency = 10000, - .power_usage = 0, - .flags = CPUIDLE_FLAG_TIME_VALID | - CPUIDLE_FLAG_COUPLED, - .name = "powered-down", - .desc = "CPU power gated", - }, -#endif -}; - static struct cpuidle_driver tegra_idle_driver = { .name = "tegra_idle", .owner = THIS_MODULE, - .en_core_tk_irqen = 1, + .states = { + ARM_CPUIDLE_WFI_STATE_PWR(600), +#ifdef CONFIG_PM_SLEEP + { + .enter = tegra20_idle_lp2_coupled, + .exit_latency = 5000, + .target_residency = 10000, + .power_usage = 0, + .flags = CPUIDLE_FLAG_TIME_VALID | + CPUIDLE_FLAG_COUPLED, + .name = "powered-down", + .desc = "CPU power gated", + }, +#endif + }, + .state_count = TEGRA20_MAX_STATES, + .safe_state_index = 0, }; -static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device); - #ifdef CONFIG_PM_SLEEP #ifdef CONFIG_SMP static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); @@ -130,10 +131,6 @@ static bool tegra20_cpu_cluster_power_down(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { - struct cpuidle_state *state = &drv->states[index]; - u32 cpu_on_time = state->exit_latency; - u32 cpu_off_time = state->target_residency - state->exit_latency; - while (tegra20_cpu_is_resettable_soon()) cpu_relax(); @@ -142,7 +139,7 @@ static bool tegra20_cpu_cluster_power_down(struct cpuidle_device *dev, clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu); - tegra_idle_lp2_last(cpu_on_time, cpu_off_time); + tegra_idle_lp2_last(); clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu); @@ -180,7 +177,6 @@ static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { - u32 cpu = is_smp() ? cpu_logical_map(dev->cpu) : dev->cpu; bool entered_lp2 = false; if (tegra_pending_sgi()) @@ -196,16 +192,16 @@ static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev, local_fiq_disable(); - tegra_set_cpu_in_lp2(cpu); + tegra_set_cpu_in_lp2(); cpu_pm_enter(); - if (cpu == 0) + if (dev->cpu == 0) entered_lp2 = tegra20_cpu_cluster_power_down(dev, drv, index); else entered_lp2 = tegra20_idle_enter_lp2_cpu_1(dev, drv, index); cpu_pm_exit(); - tegra_clear_cpu_in_lp2(cpu); + tegra_clear_cpu_in_lp2(); local_fiq_enable(); @@ -215,41 +211,19 @@ static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev, } #endif -int __init tegra20_cpuidle_init(void) +/* + * 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 the PCI driver and DT node are both enabled. + */ +void tegra20_cpuidle_pcie_irqs_in_use(void) { - int ret; - unsigned int cpu; - struct cpuidle_device *dev; - struct cpuidle_driver *drv = &tegra_idle_driver; - -#ifdef CONFIG_PM_SLEEP - tegra_tear_down_cpu = tegra20_tear_down_cpu; -#endif - - drv->state_count = ARRAY_SIZE(tegra_idle_states); - memcpy(drv->states, tegra_idle_states, - drv->state_count * sizeof(drv->states[0])); - - ret = cpuidle_register_driver(&tegra_idle_driver); - if (ret) { - pr_err("CPUidle driver registration failed\n"); - return ret; - } - - for_each_possible_cpu(cpu) { - dev = &per_cpu(tegra_idle_device, cpu); - dev->cpu = cpu; -#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED - dev->coupled_cpus = *cpu_possible_mask; -#endif + pr_info_once( + "Disabling cpuidle LP2 state, since PCIe IRQs are in use\n"); + tegra_idle_driver.states[1].disabled = true; +} - dev->state_count = drv->state_count; - ret = cpuidle_register_device(dev); - if (ret) { - pr_err("CPU%u: CPUidle device registration failed\n", - cpu); - return ret; - } - } - return 0; +int __init tegra20_cpuidle_init(void) +{ + return cpuidle_register(&tegra_idle_driver, cpu_possible_mask); } |
