]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - arch/arm/mach-tegra/cpuidle.c
ARM: tegra: Remove redundant tegra_idle_device
[linux-2.6.git] / arch / arm / mach-tegra / cpuidle.c
index a51a72a985c3cdf2d7c31af6bc8e2cfc66cbbf1f..c2a53266bb5332bbd1eb6044573a78cbaf0f9ac0 100644 (file)
@@ -3,11 +3,13 @@
  *
  * CPU idle driver for Tegra CPUs
  *
- * Copyright (c) 2010-2011, NVIDIA Corporation.
+ * Copyright (c) 2010-2013, NVIDIA Corporation.
  * Copyright (c) 2011 Google, Inc.
  * Author: Colin Cross <ccross@android.com>
  *         Gary King <gking@nvidia.com>
  *
+ * Rework for 3.3 by Peter De Schrijver <pdeschrijver@nvidia.com>
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
 #include <linux/smp.h>
 #include <linux/suspend.h>
 #include <linux/tick.h>
-
-#include <asm/cpu_pm.h>
+#include <linux/cpu_pm.h>
+#include <linux/module.h>
+#include <linux/hrtimer.h>
 
 #include <mach/iomap.h>
 #include <mach/irqs.h>
 
-#include <trace/events/power.h>
+#include <trace/events/nvpower.h>
 
 #include "cpuidle.h"
 #include "pm.h"
 #include "sleep.h"
+#include "timer.h"
+
+int tegra_pg_exit_latency;
+static int tegra_pd_power_off_time;
+static unsigned int tegra_pd_min_residency;
+
+extern void tegra_cpu_wfi(void);
 
-int tegra_lp2_exit_latency;
-static int tegra_lp2_power_off_time;
-static unsigned int tegra_lp2_min_residency;
+static int tegra_idle_enter_clock_gating(struct cpuidle_device *dev,
+                               int index);
 
-struct cpuidle_driver tegra_idle = {
+struct cpuidle_driver tegra_idle_driver = {
        .name = "tegra_idle",
        .owner = THIS_MODULE,
 };
 
-static DEFINE_PER_CPU(struct cpuidle_device *, idle_devices);
-
-static int tegra_idle_enter_lp3(struct cpuidle_device *dev,
-       struct cpuidle_state *state)
+static int tegra_idle_enter_clock_gating(struct cpuidle_device *dev,
+       int index)
 {
        ktime_t enter, exit;
        s64 us;
 
-       trace_power_start(POWER_CSTATE, 1, dev->cpu);
+       /* cpu_idle calls us with IRQs disabled */
 
-       local_irq_disable();
        local_fiq_disable();
 
        enter = ktime_get();
 
-       tegra_cpu_wfi();
+       cpu_do_idle();
 
        exit = ktime_sub(ktime_get(), enter);
        us = ktime_to_us(exit);
 
        local_fiq_enable();
+
+       /* cpu_idle expects us to return with IRQs enabled */
        local_irq_enable();
-       return (int)us;
+
+       dev->last_residency = us;
+       return index;
 }
 
-static bool lp2_in_idle __read_mostly = false;
+static bool power_down_in_idle __read_mostly;
 
 #ifdef CONFIG_PM_SLEEP
-static bool lp2_in_idle_modifiable __read_mostly = true;
-static bool lp2_disabled_by_suspend;
+static bool pd_in_idle_modifiable __read_mostly = true;
+static bool pd_disabled_by_suspend;
+static struct tegra_cpuidle_ops tegra_idle_ops;
 
-void tegra_lp2_in_idle(bool enable)
+void tegra_pd_in_idle(bool enable)
 {
-       /* If LP2 in idle is permanently disabled it can't be re-enabled. */
-       if (lp2_in_idle_modifiable) {
-               lp2_in_idle = enable;
-               lp2_in_idle_modifiable = enable;
+       /*
+        * If power down in idle is permanently disabled it can't be
+        * re-enabled.
+        */
+       if (pd_in_idle_modifiable) {
+               power_down_in_idle = enable;
+               pd_in_idle_modifiable = enable;
                if (!enable)
                        pr_warn("LP2 in idle disabled\n");
        }
 }
 
-static int tegra_idle_enter_lp2(struct cpuidle_device *dev,
-       struct cpuidle_state *state)
+void tegra_pd_update_target_residency(struct cpuidle_state *state)
+{
+       state->target_residency = state->exit_latency +
+               tegra_pd_power_off_time;
+       if (state->target_residency < tegra_pd_min_residency)
+               state->target_residency = tegra_pd_min_residency;
+}
+
+static int tegra_idle_enter_pd(struct cpuidle_device *dev,
+       int index)
 {
        ktime_t enter, exit;
        s64 us;
+       struct cpuidle_state *state = &dev->states[index];
+       bool powered_down;
+
+       if (!power_down_in_idle || pd_disabled_by_suspend ||
+           !tegra_idle_ops.pd_is_allowed(dev, state)) {
+               return dev->states[dev->safe_state_index].enter(dev,
+                                       dev->safe_state_index);
+       }
 
-       if (!lp2_in_idle || lp2_disabled_by_suspend ||
-           !tegra_lp2_is_allowed(dev, state))
-               return tegra_idle_enter_lp3(dev, state);
+       /* cpu_idle calls us with IRQs disabled */
+       trace_nvcpu_powergate_rcuidle(NVPOWER_CPU_POWERGATE_ENTRY);
 
-       local_irq_disable();
        enter = ktime_get();
 
-       tegra_cpu_idle_stats_lp2_ready(dev->cpu);
-       tegra_idle_lp2(dev, state);
+       tegra_idle_ops.cpu_idle_stats_pd_ready(dev->cpu);
+       powered_down = tegra_idle_ops.tegra_idle_pd(dev, state);
+
+       trace_nvcpu_powergate_rcuidle(NVPOWER_CPU_POWERGATE_EXIT);
 
        exit = ktime_sub(ktime_get(), enter);
        us = ktime_to_us(exit);
 
+       /* cpu_idle expects us to return with IRQs enabled */
        local_irq_enable();
 
-       /* cpu clockevents may have been reset by powerdown */
-       hrtimer_peek_ahead_timers();
-
        smp_rmb();
-       state->exit_latency = tegra_lp2_exit_latency;
-       state->target_residency = tegra_lp2_exit_latency +
-               tegra_lp2_power_off_time;
-       if (state->target_residency < tegra_lp2_min_residency)
-               state->target_residency = tegra_lp2_min_residency;
 
-       tegra_cpu_idle_stats_lp2_time(dev->cpu, us);
+       /* Update LP2 latency provided no fall back to clock gating */
+       if (powered_down) {
+               tegra_pd_set_global_latency(state);
+               tegra_pd_update_target_residency(state);
+       }
+       tegra_idle_ops.cpu_idle_stats_pd_time(dev->cpu, us);
 
-       return (int)us;
+       dev->last_residency = (int)us;
+       return (powered_down) ? index : 0;
 }
 #endif
 
-static int tegra_idle_prepare(struct cpuidle_device *dev)
-{
-#ifdef CONFIG_PM_SLEEP
-       if (lp2_in_idle)
-               dev->states[1].flags &= ~CPUIDLE_FLAG_IGNORE;
-       else
-               dev->states[1].flags |= CPUIDLE_FLAG_IGNORE;
-#endif
-
-       return 0;
-}
-
 static int tegra_cpuidle_register_device(unsigned int cpu)
 {
        struct cpuidle_device *dev;
@@ -157,45 +174,39 @@ static int tegra_cpuidle_register_device(unsigned int cpu)
 
        dev->state_count = 0;
        dev->cpu = cpu;
+       dev->power_specified = 1;
 
        state = &dev->states[0];
-       snprintf(state->name, CPUIDLE_NAME_LEN, "LP3");
-       snprintf(state->desc, CPUIDLE_DESC_LEN, "CPU flow-controlled");
+       snprintf(state->name, CPUIDLE_NAME_LEN, "clock-gated");
+       snprintf(state->desc, CPUIDLE_DESC_LEN, "CPU clock gated");
        state->exit_latency = 10;
        state->target_residency = 10;
        state->power_usage = 600;
        state->flags = CPUIDLE_FLAG_TIME_VALID;
-       state->enter = tegra_idle_enter_lp3;
-       dev->safe_state = state;
+       state->enter = tegra_idle_enter_clock_gating;
+       dev->safe_state_index = 0;
        dev->state_count++;
 
 #ifdef CONFIG_PM_SLEEP
        state = &dev->states[1];
-       snprintf(state->name, CPUIDLE_NAME_LEN, "LP2");
-       snprintf(state->desc, CPUIDLE_DESC_LEN, "CPU power-gate");
+       snprintf(state->name, CPUIDLE_NAME_LEN, "powered-down");
+       snprintf(state->desc, CPUIDLE_DESC_LEN, "CPU power gated");
        state->exit_latency = tegra_cpu_power_good_time();
-
        state->target_residency = tegra_cpu_power_off_time() +
                tegra_cpu_power_good_time();
-       if (state->target_residency < tegra_lp2_min_residency)
-               state->target_residency = tegra_lp2_min_residency;
+       if (state->target_residency < tegra_pd_min_residency)
+               state->target_residency = tegra_pd_min_residency;
        state->power_usage = 0;
        state->flags = CPUIDLE_FLAG_TIME_VALID;
-       state->enter = tegra_idle_enter_lp2;
-
-       dev->power_specified = 1;
-       dev->safe_state = state;
+       state->enter = tegra_idle_enter_pd;
        dev->state_count++;
 #endif
 
-       dev->prepare = tegra_idle_prepare;
-
        if (cpuidle_register_device(dev)) {
                pr_err("CPU%u: failed to register idle device\n", cpu);
                kfree(dev);
                return -EIO;
        }
-       per_cpu(idle_devices, cpu) = dev;
        return 0;
 }
 
@@ -204,9 +215,9 @@ static int tegra_cpuidle_pm_notify(struct notifier_block *nb,
 {
 #ifdef CONFIG_PM_SLEEP
        if (event == PM_SUSPEND_PREPARE)
-               lp2_disabled_by_suspend = true;
+               pd_disabled_by_suspend = true;
        else if (event == PM_POST_SUSPEND)
-               lp2_disabled_by_suspend = false;
+               pd_disabled_by_suspend = false;
 #endif
 
        return NOTIFY_OK;
@@ -221,45 +232,50 @@ static int __init tegra_cpuidle_init(void)
        unsigned int cpu;
        int ret;
 
-       ret = cpuidle_register_driver(&tegra_idle);
-       if (ret)
+       ret = cpuidle_register_driver(&tegra_idle_driver);
+       if (ret) {
+               pr_err("CPUidle driver registration failed\n");
                return ret;
+       }
 
 #ifdef CONFIG_PM_SLEEP
-       tegra_lp2_min_residency = tegra_cpu_lp2_min_residency();
-       tegra_lp2_exit_latency = tegra_cpu_power_good_time();
-       tegra_lp2_power_off_time = tegra_cpu_power_off_time();
+       tegra_pd_min_residency = tegra_cpu_lp2_min_residency();
+       tegra_pg_exit_latency = tegra_cpu_power_good_time();
+       tegra_pd_power_off_time = tegra_cpu_power_off_time();
 
-       ret = tegra_cpudile_init_soc();
-       if (ret)
-               return ret;
+       tegra_cpuidle_init_soc(&tegra_idle_ops);
 #endif
-
        for_each_possible_cpu(cpu) {
-               if (tegra_cpuidle_register_device(cpu))
-                       pr_err("CPU%u: error initializing idle loop\n", cpu);
+               ret = tegra_cpuidle_register_device(cpu);
+               if (ret) {
+                       pr_err("CPU%u: CPUidle device registration failed\n",
+                               cpu);
+                       return ret;
+               }
        }
 
        register_pm_notifier(&tegra_cpuidle_pm_notifier);
        return 0;
 }
+device_initcall(tegra_cpuidle_init);
 
 static void __exit tegra_cpuidle_exit(void)
 {
        unregister_pm_notifier(&tegra_cpuidle_pm_notifier);
-       cpuidle_unregister_driver(&tegra_idle);
+       cpuidle_unregister_driver(&tegra_idle_driver);
 }
-
-module_init(tegra_cpuidle_init);
 module_exit(tegra_cpuidle_exit);
 
-static int lp2_in_idle_set(const char *arg, const struct kernel_param *kp)
+static int pd_in_idle_set(const char *arg, const struct kernel_param *kp)
 {
 #ifdef CONFIG_PM_SLEEP
        int ret;
 
-       /* If LP2 in idle is permanently disabled it can't be re-enabled. */
-       if (lp2_in_idle_modifiable) {
+       /*
+        * If power down in idle is permanently disabled it can't be
+        * re-enabled.
+        */
+       if (pd_in_idle_modifiable) {
                ret = param_set_bool(arg, kp);
                return ret;
        }
@@ -267,25 +283,26 @@ static int lp2_in_idle_set(const char *arg, const struct kernel_param *kp)
        return -ENODEV;
 }
 
-static int lp2_in_idle_get(char *buffer, const struct kernel_param *kp)
+static int pd_in_idle_get(char *buffer, const struct kernel_param *kp)
 {
        return param_get_bool(buffer, kp);
 }
 
-static struct kernel_param_ops lp2_in_idle_ops = {
-       .set = lp2_in_idle_set,
-       .get = lp2_in_idle_get,
+static struct kernel_param_ops pd_in_idle_ops = {
+       .set = pd_in_idle_set,
+       .get = pd_in_idle_get,
 };
-module_param_cb(lp2_in_idle, &lp2_in_idle_ops, &lp2_in_idle, 0644);
+module_param_cb(power_down_in_idle, &pd_in_idle_ops, &power_down_in_idle, 0644);
 
 #if defined(CONFIG_DEBUG_FS) && defined(CONFIG_PM_SLEEP)
-static int tegra_lp2_debug_open(struct inode *inode, struct file *file)
+static int tegra_pd_debug_open(struct inode *inode, struct file *file)
 {
-       return single_open(file, tegra_lp2_debug_show, inode->i_private);
+       return single_open(file, tegra_idle_ops.pd_debug_show,
+                               inode->i_private);
 }
 
-static const struct file_operations tegra_lp2_debug_ops = {
-       .open           = tegra_lp2_debug_open,
+static const struct file_operations tegra_pd_debug_ops = {
+       .open           = tegra_pd_debug_open,
        .read           = seq_read,
        .llseek         = seq_lseek,
        .release        = single_release,
@@ -300,8 +317,8 @@ static int __init tegra_cpuidle_debug_init(void)
        if (!dir)
                return -ENOMEM;
 
-       d = debugfs_create_file("lp2", S_IRUGO, dir, NULL,
-               &tegra_lp2_debug_ops);
+       d = debugfs_create_file("power_down_stats", S_IRUGO, dir, NULL,
+               &tegra_pd_debug_ops);
        if (!d)
                return -ENOMEM;