ARM: tegra: cpuidle driver for tegra
authorPeter De Schrijver <pdeschrijver@nvidia.com>
Thu, 26 Jan 2012 16:22:03 +0000 (18:22 +0200)
committerOlof Johansson <olof@lixom.net>
Mon, 6 Feb 2012 17:16:16 +0000 (09:16 -0800)
CPUidle driver for tegra. In this version only LP3 (clockgating) is supported.

Based on work by:

Colin Cross <ccross@android.com>
Gary King <gking@nvidia.com>

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Acked-by: Stephen Warren <swarren@nvidia.com>
Tested-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Colin Cross <ccross@android.com>
Signed-off-by: Olof Johansson <olof@lixom.net>
arch/arm/mach-tegra/Makefile
arch/arm/mach-tegra/cpuidle.c [new file with mode: 0644]

index b2ac1089651b92cedecedba1c0f4c352cec52189..0a5bc47afbf54a2ac2e8ca73d8842b2c06820af5 100644 (file)
@@ -7,6 +7,8 @@ obj-y                                   += clock.o
 obj-y                                   += timer.o
 obj-y                                   += pinmux.o
 obj-y                                  += fuse.o
+obj-y                                  += cpuidle.o
+obj-y                                  += sleep.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)                += powergate.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += tegra2_clocks.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)                += tegra2_emc.o
diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle.c
new file mode 100644 (file)
index 0000000..d83a8c0
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * arch/arm/mach-tegra/cpuidle.c
+ *
+ * CPU idle driver for Tegra CPUs
+ *
+ * Copyright (c) 2010-2012, 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
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/cpu.h>
+#include <linux/cpuidle.h>
+#include <linux/hrtimer.h>
+
+#include <mach/iomap.h>
+
+extern void tegra_cpu_wfi(void);
+
+static int tegra_idle_enter_lp3(struct cpuidle_device *dev,
+                               struct cpuidle_driver *drv, int index);
+
+struct cpuidle_driver tegra_idle_driver = {
+       .name = "tegra_idle",
+       .owner = THIS_MODULE,
+       .state_count = 1,
+       .states = {
+               [0] = {
+                       .enter                  = tegra_idle_enter_lp3,
+                       .exit_latency           = 10,
+                       .target_residency       = 10,
+                       .power_usage            = 600,
+                       .flags                  = CPUIDLE_FLAG_TIME_VALID,
+                       .name                   = "LP3",
+                       .desc                   = "CPU flow-controlled",
+               },
+       },
+};
+
+static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device);
+
+static int tegra_idle_enter_lp3(struct cpuidle_device *dev,
+       struct cpuidle_driver *drv, int index)
+{
+       ktime_t enter, exit;
+       s64 us;
+
+       local_irq_disable();
+       local_fiq_disable();
+
+       enter = ktime_get();
+
+       tegra_cpu_wfi();
+
+       exit = ktime_sub(ktime_get(), enter);
+       us = ktime_to_us(exit);
+
+       local_fiq_enable();
+       local_irq_enable();
+
+       dev->last_residency = us;
+
+       return index;
+}
+
+static int __init tegra_cpuidle_init(void)
+{
+       int ret;
+       unsigned int cpu;
+       struct cpuidle_device *dev;
+       struct cpuidle_driver *drv = &tegra_idle_driver;
+
+       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;
+
+               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;
+}
+device_initcall(tegra_cpuidle_init);