aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/cpuidle-tegra30.c44
-rw-r--r--arch/arm/mach-tegra/pm.c144
-rw-r--r--arch/arm/mach-tegra/pm.h3
-rw-r--r--arch/arm/mach-tegra/sleep-tegra30.S44
-rw-r--r--arch/arm/mach-tegra/sleep.S42
-rw-r--r--arch/arm/mach-tegra/sleep.h2
6 files changed, 275 insertions, 4 deletions
diff --git a/arch/arm/mach-tegra/cpuidle-tegra30.c b/arch/arm/mach-tegra/cpuidle-tegra30.c
index cc48d7fa3358..5e8cbf5b799f 100644
--- a/arch/arm/mach-tegra/cpuidle-tegra30.c
+++ b/arch/arm/mach-tegra/cpuidle-tegra30.c
@@ -32,6 +32,7 @@
32 32
33#include "pm.h" 33#include "pm.h"
34#include "sleep.h" 34#include "sleep.h"
35#include "tegra_cpu_car.h"
35 36
36#ifdef CONFIG_PM_SLEEP 37#ifdef CONFIG_PM_SLEEP
37static int tegra30_idle_lp2(struct cpuidle_device *dev, 38static int tegra30_idle_lp2(struct cpuidle_device *dev,
@@ -67,6 +68,31 @@ static struct cpuidle_driver tegra_idle_driver = {
67static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device); 68static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device);
68 69
69#ifdef CONFIG_PM_SLEEP 70#ifdef CONFIG_PM_SLEEP
71static bool tegra30_cpu_cluster_power_down(struct cpuidle_device *dev,
72 struct cpuidle_driver *drv,
73 int index)
74{
75 struct cpuidle_state *state = &drv->states[index];
76 u32 cpu_on_time = state->exit_latency;
77 u32 cpu_off_time = state->target_residency - state->exit_latency;
78
79 /* All CPUs entering LP2 is not working.
80 * Don't let CPU0 enter LP2 when any secondary CPU is online.
81 */
82 if (num_online_cpus() > 1 || !tegra_cpu_rail_off_ready()) {
83 cpu_do_idle();
84 return false;
85 }
86
87 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
88
89 tegra_idle_lp2_last(cpu_on_time, cpu_off_time);
90
91 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
92
93 return true;
94}
95
70#ifdef CONFIG_SMP 96#ifdef CONFIG_SMP
71static bool tegra30_cpu_core_power_down(struct cpuidle_device *dev, 97static bool tegra30_cpu_core_power_down(struct cpuidle_device *dev,
72 struct cpuidle_driver *drv, 98 struct cpuidle_driver *drv,
@@ -101,16 +127,22 @@ static int __cpuinit tegra30_idle_lp2(struct cpuidle_device *dev,
101{ 127{
102 u32 cpu = is_smp() ? cpu_logical_map(dev->cpu) : dev->cpu; 128 u32 cpu = is_smp() ? cpu_logical_map(dev->cpu) : dev->cpu;
103 bool entered_lp2 = false; 129 bool entered_lp2 = false;
130 bool last_cpu;
104 131
105 local_fiq_disable(); 132 local_fiq_disable();
106 133
107 tegra_set_cpu_in_lp2(cpu); 134 last_cpu = tegra_set_cpu_in_lp2(cpu);
108 cpu_pm_enter(); 135 cpu_pm_enter();
109 136
110 if (cpu == 0) 137 if (cpu == 0) {
111 cpu_do_idle(); 138 if (last_cpu)
112 else 139 entered_lp2 = tegra30_cpu_cluster_power_down(dev, drv,
140 index);
141 else
142 cpu_do_idle();
143 } else {
113 entered_lp2 = tegra30_cpu_core_power_down(dev, drv, index); 144 entered_lp2 = tegra30_cpu_core_power_down(dev, drv, index);
145 }
114 146
115 cpu_pm_exit(); 147 cpu_pm_exit();
116 tegra_clear_cpu_in_lp2(cpu); 148 tegra_clear_cpu_in_lp2(cpu);
@@ -130,6 +162,10 @@ int __init tegra30_cpuidle_init(void)
130 struct cpuidle_device *dev; 162 struct cpuidle_device *dev;
131 struct cpuidle_driver *drv = &tegra_idle_driver; 163 struct cpuidle_driver *drv = &tegra_idle_driver;
132 164
165#ifdef CONFIG_PM_SLEEP
166 tegra_tear_down_cpu = tegra30_tear_down_cpu;
167#endif
168
133 ret = cpuidle_register_driver(&tegra_idle_driver); 169 ret = cpuidle_register_driver(&tegra_idle_driver);
134 if (ret) { 170 if (ret) {
135 pr_err("CPUidle driver registration failed\n"); 171 pr_err("CPUidle driver registration failed\n");
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index f88595abc471..1460c3db8245 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -20,13 +20,36 @@
20#include <linux/spinlock.h> 20#include <linux/spinlock.h>
21#include <linux/io.h> 21#include <linux/io.h>
22#include <linux/cpumask.h> 22#include <linux/cpumask.h>
23#include <linux/delay.h>
24#include <linux/cpu_pm.h>
25#include <linux/clk.h>
26#include <linux/err.h>
27
28#include <asm/smp_plat.h>
29#include <asm/cacheflush.h>
30#include <asm/suspend.h>
31#include <asm/idmap.h>
32#include <asm/proc-fns.h>
33#include <asm/tlbflush.h>
23 34
24#include "iomap.h" 35#include "iomap.h"
25#include "reset.h" 36#include "reset.h"
37#include "flowctrl.h"
38#include "sleep.h"
39#include "tegra_cpu_car.h"
40
41#define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */
42
43#define PMC_CTRL 0x0
44#define PMC_CPUPWRGOOD_TIMER 0xc8
45#define PMC_CPUPWROFF_TIMER 0xcc
26 46
27#ifdef CONFIG_PM_SLEEP 47#ifdef CONFIG_PM_SLEEP
28static unsigned int g_diag_reg; 48static unsigned int g_diag_reg;
29static DEFINE_SPINLOCK(tegra_lp2_lock); 49static DEFINE_SPINLOCK(tegra_lp2_lock);
50static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
51static struct clk *tegra_pclk;
52void (*tegra_tear_down_cpu)(void);
30 53
31void save_cpu_arch_register(void) 54void save_cpu_arch_register(void)
32{ 55{
@@ -42,6 +65,89 @@ void restore_cpu_arch_register(void)
42 return; 65 return;
43} 66}
44 67
68static void set_power_timers(unsigned long us_on, unsigned long us_off)
69{
70 unsigned long long ticks;
71 unsigned long long pclk;
72 unsigned long rate;
73 static unsigned long tegra_last_pclk;
74
75 if (tegra_pclk == NULL) {
76 tegra_pclk = clk_get_sys(NULL, "pclk");
77 WARN_ON(IS_ERR(tegra_pclk));
78 }
79
80 rate = clk_get_rate(tegra_pclk);
81
82 if (WARN_ON_ONCE(rate <= 0))
83 pclk = 100000000;
84 else
85 pclk = rate;
86
87 if ((rate != tegra_last_pclk)) {
88 ticks = (us_on * pclk) + 999999ull;
89 do_div(ticks, 1000000);
90 writel((unsigned long)ticks, pmc + PMC_CPUPWRGOOD_TIMER);
91
92 ticks = (us_off * pclk) + 999999ull;
93 do_div(ticks, 1000000);
94 writel((unsigned long)ticks, pmc + PMC_CPUPWROFF_TIMER);
95 wmb();
96 }
97 tegra_last_pclk = pclk;
98}
99
100/*
101 * restore_cpu_complex
102 *
103 * restores cpu clock setting, clears flow controller
104 *
105 * Always called on CPU 0.
106 */
107static void restore_cpu_complex(void)
108{
109 int cpu = smp_processor_id();
110
111 BUG_ON(cpu != 0);
112
113#ifdef CONFIG_SMP
114 cpu = cpu_logical_map(cpu);
115#endif
116
117 /* Restore the CPU clock settings */
118 tegra_cpu_clock_resume();
119
120 flowctrl_cpu_suspend_exit(cpu);
121
122 restore_cpu_arch_register();
123}
124
125/*
126 * suspend_cpu_complex
127 *
128 * saves pll state for use by restart_plls, prepares flow controller for
129 * transition to suspend state
130 *
131 * Must always be called on cpu 0.
132 */
133static void suspend_cpu_complex(void)
134{
135 int cpu = smp_processor_id();
136
137 BUG_ON(cpu != 0);
138
139#ifdef CONFIG_SMP
140 cpu = cpu_logical_map(cpu);
141#endif
142
143 /* Save the CPU clock settings */
144 tegra_cpu_clock_suspend();
145
146 flowctrl_cpu_suspend_enter(cpu);
147
148 save_cpu_arch_register();
149}
150
45void __cpuinit tegra_clear_cpu_in_lp2(int phy_cpu_id) 151void __cpuinit tegra_clear_cpu_in_lp2(int phy_cpu_id)
46{ 152{
47 u32 *cpu_in_lp2 = tegra_cpu_lp2_mask; 153 u32 *cpu_in_lp2 = tegra_cpu_lp2_mask;
@@ -71,4 +177,42 @@ bool __cpuinit tegra_set_cpu_in_lp2(int phy_cpu_id)
71 spin_unlock(&tegra_lp2_lock); 177 spin_unlock(&tegra_lp2_lock);
72 return last_cpu; 178 return last_cpu;
73} 179}
180
181static int tegra_sleep_cpu(unsigned long v2p)
182{
183 /* Switch to the identity mapping. */
184 cpu_switch_mm(idmap_pgd, &init_mm);
185
186 /* Flush the TLB. */
187 local_flush_tlb_all();
188
189 tegra_sleep_cpu_finish(v2p);
190
191 /* should never here */
192 BUG();
193
194 return 0;
195}
196
197void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time)
198{
199 u32 mode;
200
201 /* Only the last cpu down does the final suspend steps */
202 mode = readl(pmc + PMC_CTRL);
203 mode |= TEGRA_POWER_CPU_PWRREQ_OE;
204 writel(mode, pmc + PMC_CTRL);
205
206 set_power_timers(cpu_on_time, cpu_off_time);
207
208 cpu_cluster_pm_enter();
209 suspend_cpu_complex();
210 outer_disable();
211
212 cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu);
213
214 outer_resume();
215 restore_cpu_complex();
216 cpu_cluster_pm_exit();
217}
74#endif 218#endif
diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h
index bcfc45faad78..512345c9eec3 100644
--- a/arch/arm/mach-tegra/pm.h
+++ b/arch/arm/mach-tegra/pm.h
@@ -27,4 +27,7 @@ void restore_cpu_arch_register(void);
27void tegra_clear_cpu_in_lp2(int phy_cpu_id); 27void tegra_clear_cpu_in_lp2(int phy_cpu_id);
28bool tegra_set_cpu_in_lp2(int phy_cpu_id); 28bool tegra_set_cpu_in_lp2(int phy_cpu_id);
29 29
30void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time);
31extern void (*tegra_tear_down_cpu)(void);
32
30#endif /* _MACH_TEGRA_PM_H_ */ 33#endif /* _MACH_TEGRA_PM_H_ */
diff --git a/arch/arm/mach-tegra/sleep-tegra30.S b/arch/arm/mach-tegra/sleep-tegra30.S
index 59984d718481..562a8e7e413d 100644
--- a/arch/arm/mach-tegra/sleep-tegra30.S
+++ b/arch/arm/mach-tegra/sleep-tegra30.S
@@ -124,4 +124,48 @@ ENTRY(tegra30_sleep_cpu_secondary_finish)
124 mov r0, #1 @ never return here 124 mov r0, #1 @ never return here
125 mov pc, r7 125 mov pc, r7
126ENDPROC(tegra30_sleep_cpu_secondary_finish) 126ENDPROC(tegra30_sleep_cpu_secondary_finish)
127
128/*
129 * tegra30_tear_down_cpu
130 *
131 * Switches the CPU to enter sleep.
132 */
133ENTRY(tegra30_tear_down_cpu)
134 mov32 r6, TEGRA_FLOW_CTRL_BASE
135
136 b tegra30_enter_sleep
137ENDPROC(tegra30_tear_down_cpu)
138
139/*
140 * tegra30_enter_sleep
141 *
142 * uses flow controller to enter sleep state
143 * executes from IRAM with SDRAM in selfrefresh when target state is LP0 or LP1
144 * executes from SDRAM with target state is LP2
145 * r6 = TEGRA_FLOW_CTRL_BASE
146 */
147tegra30_enter_sleep:
148 cpu_id r1
149
150 cpu_to_csr_reg r2, r1
151 ldr r0, [r6, r2]
152 orr r0, r0, #FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG
153 orr r0, r0, #FLOW_CTRL_CSR_ENABLE
154 str r0, [r6, r2]
155
156 mov r0, #FLOW_CTRL_WAIT_FOR_INTERRUPT
157 orr r0, r0, #FLOW_CTRL_HALT_CPU_IRQ | FLOW_CTRL_HALT_CPU_FIQ
158 cpu_to_halt_reg r2, r1
159 str r0, [r6, r2]
160 dsb
161 ldr r0, [r6, r2] /* memory barrier */
162
163halted:
164 isb
165 dsb
166 wfi /* CPU should be power gated here */
167
168 /* !!!FIXME!!! Implement halt failure handler */
169 b halted
170
127#endif 171#endif
diff --git a/arch/arm/mach-tegra/sleep.S b/arch/arm/mach-tegra/sleep.S
index 91548a77dd95..88f4de986a52 100644
--- a/arch/arm/mach-tegra/sleep.S
+++ b/arch/arm/mach-tegra/sleep.S
@@ -25,6 +25,7 @@
25#include <linux/linkage.h> 25#include <linux/linkage.h>
26 26
27#include <asm/assembler.h> 27#include <asm/assembler.h>
28#include <asm/cache.h>
28#include <asm/cp15.h> 29#include <asm/cp15.h>
29 30
30#include "iomap.h" 31#include "iomap.h"
@@ -59,4 +60,45 @@ ENTRY(tegra_disable_clean_inv_dcache)
59 ldmfd sp!, {r0, r4-r5, r7, r9-r11, pc} 60 ldmfd sp!, {r0, r4-r5, r7, r9-r11, pc}
60ENDPROC(tegra_disable_clean_inv_dcache) 61ENDPROC(tegra_disable_clean_inv_dcache)
61 62
63/*
64 * tegra_sleep_cpu_finish(unsigned long v2p)
65 *
66 * enters suspend in LP2 by turning off the mmu and jumping to
67 * tegra?_tear_down_cpu
68 */
69ENTRY(tegra_sleep_cpu_finish)
70 /* Flush and disable the L1 data cache */
71 bl tegra_disable_clean_inv_dcache
72
73 mov32 r6, tegra_tear_down_cpu
74 ldr r1, [r6]
75 add r1, r1, r0
76
77 mov32 r3, tegra_shut_off_mmu
78 add r3, r3, r0
79 mov r0, r1
80
81 mov pc, r3
82ENDPROC(tegra_sleep_cpu_finish)
83
84/*
85 * tegra_shut_off_mmu
86 *
87 * r0 = physical address to jump to with mmu off
88 *
89 * called with VA=PA mapping
90 * turns off MMU, icache, dcache and branch prediction
91 */
92 .align L1_CACHE_SHIFT
93 .pushsection .idmap.text, "ax"
94ENTRY(tegra_shut_off_mmu)
95 mrc p15, 0, r3, c1, c0, 0
96 movw r2, #CR_I | CR_Z | CR_C | CR_M
97 bic r3, r3, r2
98 dsb
99 mcr p15, 0, r3, c1, c0, 0
100 isb
101 mov pc, r0
102ENDPROC(tegra_shut_off_mmu)
103 .popsection
62#endif 104#endif
diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h
index bacf549bd54d..6e1b9490c1cf 100644
--- a/arch/arm/mach-tegra/sleep.h
+++ b/arch/arm/mach-tegra/sleep.h
@@ -73,6 +73,7 @@
73.endm 73.endm
74#else 74#else
75void tegra_resume(void); 75void tegra_resume(void);
76int tegra_sleep_cpu_finish(unsigned long);
76 77
77#ifdef CONFIG_HOTPLUG_CPU 78#ifdef CONFIG_HOTPLUG_CPU
78void tegra20_hotplug_init(void); 79void tegra20_hotplug_init(void);
@@ -83,6 +84,7 @@ static inline void tegra30_hotplug_init(void) {}
83#endif 84#endif
84 85
85int tegra30_sleep_cpu_secondary_finish(unsigned long); 86int tegra30_sleep_cpu_secondary_finish(unsigned long);
87void tegra30_tear_down_cpu(void);
86 88
87#endif 89#endif
88#endif 90#endif