blob: 50f984d28faffd2bf163d3187baf86ff43b9879a [file] [log] [blame]
Joseph Lo0b25e252012-10-31 17:41:15 +08001/*
2 * CPU idle driver for Tegra CPUs
3 *
4 * Copyright (c) 2010-2012, NVIDIA Corporation.
5 * Copyright (c) 2011 Google, Inc.
6 * Author: Colin Cross <ccross@android.com>
7 * Gary King <gking@nvidia.com>
8 *
9 * Rework for 3.3 by Peter De Schrijver <pdeschrijver@nvidia.com>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful, but WITHOUT
17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
19 * more details.
20 */
21
22#include <linux/kernel.h>
23#include <linux/module.h>
24#include <linux/cpuidle.h>
Joseph Lo5c1350b2013-01-15 22:10:38 +000025#include <linux/cpu_pm.h>
26#include <linux/clockchips.h>
Joseph Lo0b25e252012-10-31 17:41:15 +080027
28#include <asm/cpuidle.h>
Joseph Lo5c1350b2013-01-15 22:10:38 +000029#include <asm/proc-fns.h>
30#include <asm/suspend.h>
31#include <asm/smp_plat.h>
32
33#include "pm.h"
34#include "sleep.h"
35
36#ifdef CONFIG_PM_SLEEP
37static int tegra20_idle_lp2(struct cpuidle_device *dev,
38 struct cpuidle_driver *drv,
39 int index);
40#endif
41
42static struct cpuidle_state tegra_idle_states[] = {
43 [0] = ARM_CPUIDLE_WFI_STATE_PWR(600),
44#ifdef CONFIG_PM_SLEEP
45 [1] = {
46 .enter = tegra20_idle_lp2,
47 .exit_latency = 5000,
48 .target_residency = 10000,
49 .power_usage = 0,
50 .flags = CPUIDLE_FLAG_TIME_VALID,
51 .name = "powered-down",
52 .desc = "CPU power gated",
53 },
54#endif
55};
Joseph Lo0b25e252012-10-31 17:41:15 +080056
57static struct cpuidle_driver tegra_idle_driver = {
58 .name = "tegra_idle",
59 .owner = THIS_MODULE,
60 .en_core_tk_irqen = 1,
Joseph Lo0b25e252012-10-31 17:41:15 +080061};
62
63static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device);
64
Joseph Lo5c1350b2013-01-15 22:10:38 +000065#ifdef CONFIG_PM_SLEEP
66#ifdef CONFIG_SMP
67static bool tegra20_idle_enter_lp2_cpu_1(struct cpuidle_device *dev,
68 struct cpuidle_driver *drv,
69 int index)
70{
71 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
72
73 cpu_suspend(0, tegra20_sleep_cpu_secondary_finish);
74
75 tegra20_cpu_clear_resettable();
76
77 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
78
79 return true;
80}
81#else
82static inline bool tegra20_idle_enter_lp2_cpu_1(struct cpuidle_device *dev,
83 struct cpuidle_driver *drv,
84 int index)
85{
86 return true;
87}
88#endif
89
90static int tegra20_idle_lp2(struct cpuidle_device *dev,
91 struct cpuidle_driver *drv,
92 int index)
93{
94 u32 cpu = is_smp() ? cpu_logical_map(dev->cpu) : dev->cpu;
95 bool entered_lp2 = false;
96
97 local_fiq_disable();
98
99 tegra_set_cpu_in_lp2(cpu);
100 cpu_pm_enter();
101
102 if (cpu == 0)
103 cpu_do_idle();
104 else
105 entered_lp2 = tegra20_idle_enter_lp2_cpu_1(dev, drv, index);
106
107 cpu_pm_exit();
108 tegra_clear_cpu_in_lp2(cpu);
109
110 local_fiq_enable();
111
112 smp_rmb();
113
114 return entered_lp2 ? index : 0;
115}
116#endif
117
Joseph Lo0b25e252012-10-31 17:41:15 +0800118int __init tegra20_cpuidle_init(void)
119{
120 int ret;
121 unsigned int cpu;
122 struct cpuidle_device *dev;
123 struct cpuidle_driver *drv = &tegra_idle_driver;
124
Joseph Lo5c1350b2013-01-15 22:10:38 +0000125 drv->state_count = ARRAY_SIZE(tegra_idle_states);
126 memcpy(drv->states, tegra_idle_states,
127 drv->state_count * sizeof(drv->states[0]));
128
Joseph Lo0b25e252012-10-31 17:41:15 +0800129 ret = cpuidle_register_driver(&tegra_idle_driver);
130 if (ret) {
131 pr_err("CPUidle driver registration failed\n");
132 return ret;
133 }
134
135 for_each_possible_cpu(cpu) {
136 dev = &per_cpu(tegra_idle_device, cpu);
137 dev->cpu = cpu;
138
139 dev->state_count = drv->state_count;
140 ret = cpuidle_register_device(dev);
141 if (ret) {
142 pr_err("CPU%u: CPUidle device registration failed\n",
143 cpu);
144 return ret;
145 }
146 }
147 return 0;
148}