aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-tegra
diff options
context:
space:
mode:
authorOlof Johansson <olof@lixom.net>2013-02-05 15:46:11 -0500
committerOlof Johansson <olof@lixom.net>2013-02-05 15:46:18 -0500
commitc8bfea3636215e50b519e6fdb29f1bf776b0f166 (patch)
treeb580cc2463e3adefaf761af46e95146979887c58 /arch/arm/mach-tegra
parentceca718f21270dcd7cdf7e124f15b35cb68eca13 (diff)
parent1d328606c66b9bb1c0552f585943d596f37ae3b9 (diff)
Merge tag 'tegra-for-3.9-soc-cpuidle' of git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-tegra into next/soc
From Stephen Warren: ARM: tegra: cpuidle enhancements This pull request implements a new "LP2" cpuidle state for Tegra20, which makes use of the couple cpuidle feature. It is based on (most of) the previous pull request, with tag tegra-for-3.9-soc-usb. * tag 'tegra-for-3.9-soc-cpuidle' of git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-tegra: ARM: tegra20: cpuidle: apply coupled cpuidle for powered-down mode ARM: tegra20: flowctrl: add support for cpu_suspend_enter/exit clk: tegra20: Implementing CPU low-power function for tegra_cpu_car_ops ARM: tegra20: cpuidle: add powered-down state for secondary CPU ARM: tegra: add pending SGI checking API Signed-off-by: Olof Johansson <olof@lixom.net>
Diffstat (limited to 'arch/arm/mach-tegra')
-rw-r--r--arch/arm/mach-tegra/Kconfig1
-rw-r--r--arch/arm/mach-tegra/cpuidle-tegra20.c197
-rw-r--r--arch/arm/mach-tegra/flowctrl.c38
-rw-r--r--arch/arm/mach-tegra/flowctrl.h4
-rw-r--r--arch/arm/mach-tegra/irq.c15
-rw-r--r--arch/arm/mach-tegra/irq.h22
-rw-r--r--arch/arm/mach-tegra/pm.c3
-rw-r--r--arch/arm/mach-tegra/sleep-tegra20.S200
-rw-r--r--arch/arm/mach-tegra/sleep.S19
-rw-r--r--arch/arm/mach-tegra/sleep.h26
10 files changed, 516 insertions, 9 deletions
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig
index 1ec7f80e2af5..abc688fd4807 100644
--- a/arch/arm/mach-tegra/Kconfig
+++ b/arch/arm/mach-tegra/Kconfig
@@ -4,6 +4,7 @@ comment "NVIDIA Tegra options"
4 4
5config ARCH_TEGRA_2x_SOC 5config ARCH_TEGRA_2x_SOC
6 bool "Enable support for Tegra20 family" 6 bool "Enable support for Tegra20 family"
7 select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP
7 select ARCH_REQUIRE_GPIOLIB 8 select ARCH_REQUIRE_GPIOLIB
8 select ARM_ERRATA_720789 9 select ARM_ERRATA_720789
9 select ARM_ERRATA_742230 if SMP 10 select ARM_ERRATA_742230 if SMP
diff --git a/arch/arm/mach-tegra/cpuidle-tegra20.c b/arch/arm/mach-tegra/cpuidle-tegra20.c
index d32e8b0dbd4f..825ced4f7a40 100644
--- a/arch/arm/mach-tegra/cpuidle-tegra20.c
+++ b/arch/arm/mach-tegra/cpuidle-tegra20.c
@@ -22,21 +22,199 @@
22#include <linux/kernel.h> 22#include <linux/kernel.h>
23#include <linux/module.h> 23#include <linux/module.h>
24#include <linux/cpuidle.h> 24#include <linux/cpuidle.h>
25#include <linux/cpu_pm.h>
26#include <linux/clockchips.h>
27#include <linux/clk/tegra.h>
25 28
26#include <asm/cpuidle.h> 29#include <asm/cpuidle.h>
30#include <asm/proc-fns.h>
31#include <asm/suspend.h>
32#include <asm/smp_plat.h>
33
34#include "pm.h"
35#include "sleep.h"
36#include "iomap.h"
37#include "irq.h"
38#include "flowctrl.h"
39
40#ifdef CONFIG_PM_SLEEP
41static bool abort_flag;
42static atomic_t abort_barrier;
43static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev,
44 struct cpuidle_driver *drv,
45 int index);
46#endif
47
48static struct cpuidle_state tegra_idle_states[] = {
49 [0] = ARM_CPUIDLE_WFI_STATE_PWR(600),
50#ifdef CONFIG_PM_SLEEP
51 [1] = {
52 .enter = tegra20_idle_lp2_coupled,
53 .exit_latency = 5000,
54 .target_residency = 10000,
55 .power_usage = 0,
56 .flags = CPUIDLE_FLAG_TIME_VALID |
57 CPUIDLE_FLAG_COUPLED,
58 .name = "powered-down",
59 .desc = "CPU power gated",
60 },
61#endif
62};
27 63
28static struct cpuidle_driver tegra_idle_driver = { 64static struct cpuidle_driver tegra_idle_driver = {
29 .name = "tegra_idle", 65 .name = "tegra_idle",
30 .owner = THIS_MODULE, 66 .owner = THIS_MODULE,
31 .en_core_tk_irqen = 1, 67 .en_core_tk_irqen = 1,
32 .state_count = 1,
33 .states = {
34 [0] = ARM_CPUIDLE_WFI_STATE_PWR(600),
35 },
36}; 68};
37 69
38static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device); 70static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device);
39 71
72#ifdef CONFIG_PM_SLEEP
73#ifdef CONFIG_SMP
74static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
75
76static int tegra20_reset_sleeping_cpu_1(void)
77{
78 int ret = 0;
79
80 tegra_pen_lock();
81
82 if (readl(pmc + PMC_SCRATCH41) == CPU_RESETTABLE)
83 tegra20_cpu_shutdown(1);
84 else
85 ret = -EINVAL;
86
87 tegra_pen_unlock();
88
89 return ret;
90}
91
92static void tegra20_wake_cpu1_from_reset(void)
93{
94 tegra_pen_lock();
95
96 tegra20_cpu_clear_resettable();
97
98 /* enable cpu clock on cpu */
99 tegra_enable_cpu_clock(1);
100
101 /* take the CPU out of reset */
102 tegra_cpu_out_of_reset(1);
103
104 /* unhalt the cpu */
105 flowctrl_write_cpu_halt(1, 0);
106
107 tegra_pen_unlock();
108}
109
110static int tegra20_reset_cpu_1(void)
111{
112 if (!cpu_online(1) || !tegra20_reset_sleeping_cpu_1())
113 return 0;
114
115 tegra20_wake_cpu1_from_reset();
116 return -EBUSY;
117}
118#else
119static inline void tegra20_wake_cpu1_from_reset(void)
120{
121}
122
123static inline int tegra20_reset_cpu_1(void)
124{
125 return 0;
126}
127#endif
128
129static bool tegra20_cpu_cluster_power_down(struct cpuidle_device *dev,
130 struct cpuidle_driver *drv,
131 int index)
132{
133 struct cpuidle_state *state = &drv->states[index];
134 u32 cpu_on_time = state->exit_latency;
135 u32 cpu_off_time = state->target_residency - state->exit_latency;
136
137 while (tegra20_cpu_is_resettable_soon())
138 cpu_relax();
139
140 if (tegra20_reset_cpu_1() || !tegra_cpu_rail_off_ready())
141 return false;
142
143 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
144
145 tegra_idle_lp2_last(cpu_on_time, cpu_off_time);
146
147 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
148
149 if (cpu_online(1))
150 tegra20_wake_cpu1_from_reset();
151
152 return true;
153}
154
155#ifdef CONFIG_SMP
156static bool tegra20_idle_enter_lp2_cpu_1(struct cpuidle_device *dev,
157 struct cpuidle_driver *drv,
158 int index)
159{
160 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
161
162 cpu_suspend(0, tegra20_sleep_cpu_secondary_finish);
163
164 tegra20_cpu_clear_resettable();
165
166 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
167
168 return true;
169}
170#else
171static inline bool tegra20_idle_enter_lp2_cpu_1(struct cpuidle_device *dev,
172 struct cpuidle_driver *drv,
173 int index)
174{
175 return true;
176}
177#endif
178
179static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev,
180 struct cpuidle_driver *drv,
181 int index)
182{
183 u32 cpu = is_smp() ? cpu_logical_map(dev->cpu) : dev->cpu;
184 bool entered_lp2 = false;
185
186 if (tegra_pending_sgi())
187 ACCESS_ONCE(abort_flag) = true;
188
189 cpuidle_coupled_parallel_barrier(dev, &abort_barrier);
190
191 if (abort_flag) {
192 cpuidle_coupled_parallel_barrier(dev, &abort_barrier);
193 abort_flag = false; /* clean flag for next coming */
194 return -EINTR;
195 }
196
197 local_fiq_disable();
198
199 tegra_set_cpu_in_lp2(cpu);
200 cpu_pm_enter();
201
202 if (cpu == 0)
203 entered_lp2 = tegra20_cpu_cluster_power_down(dev, drv, index);
204 else
205 entered_lp2 = tegra20_idle_enter_lp2_cpu_1(dev, drv, index);
206
207 cpu_pm_exit();
208 tegra_clear_cpu_in_lp2(cpu);
209
210 local_fiq_enable();
211
212 smp_rmb();
213
214 return entered_lp2 ? index : 0;
215}
216#endif
217
40int __init tegra20_cpuidle_init(void) 218int __init tegra20_cpuidle_init(void)
41{ 219{
42 int ret; 220 int ret;
@@ -44,6 +222,14 @@ int __init tegra20_cpuidle_init(void)
44 struct cpuidle_device *dev; 222 struct cpuidle_device *dev;
45 struct cpuidle_driver *drv = &tegra_idle_driver; 223 struct cpuidle_driver *drv = &tegra_idle_driver;
46 224
225#ifdef CONFIG_PM_SLEEP
226 tegra_tear_down_cpu = tegra20_tear_down_cpu;
227#endif
228
229 drv->state_count = ARRAY_SIZE(tegra_idle_states);
230 memcpy(drv->states, tegra_idle_states,
231 drv->state_count * sizeof(drv->states[0]));
232
47 ret = cpuidle_register_driver(&tegra_idle_driver); 233 ret = cpuidle_register_driver(&tegra_idle_driver);
48 if (ret) { 234 if (ret) {
49 pr_err("CPUidle driver registration failed\n"); 235 pr_err("CPUidle driver registration failed\n");
@@ -53,6 +239,9 @@ int __init tegra20_cpuidle_init(void)
53 for_each_possible_cpu(cpu) { 239 for_each_possible_cpu(cpu) {
54 dev = &per_cpu(tegra_idle_device, cpu); 240 dev = &per_cpu(tegra_idle_device, cpu);
55 dev->cpu = cpu; 241 dev->cpu = cpu;
242#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED
243 dev->coupled_cpus = *cpu_possible_mask;
244#endif
56 245
57 dev->state_count = drv->state_count; 246 dev->state_count = drv->state_count;
58 ret = cpuidle_register_device(dev); 247 ret = cpuidle_register_device(dev);
diff --git a/arch/arm/mach-tegra/flowctrl.c b/arch/arm/mach-tegra/flowctrl.c
index 5393eb2cae21..b477ef310dcd 100644
--- a/arch/arm/mach-tegra/flowctrl.c
+++ b/arch/arm/mach-tegra/flowctrl.c
@@ -25,6 +25,7 @@
25 25
26#include "flowctrl.h" 26#include "flowctrl.h"
27#include "iomap.h" 27#include "iomap.h"
28#include "fuse.h"
28 29
29static u8 flowctrl_offset_halt_cpu[] = { 30static u8 flowctrl_offset_halt_cpu[] = {
30 FLOW_CTRL_HALT_CPU0_EVENTS, 31 FLOW_CTRL_HALT_CPU0_EVENTS,
@@ -75,11 +76,26 @@ void flowctrl_cpu_suspend_enter(unsigned int cpuid)
75 int i; 76 int i;
76 77
77 reg = flowctrl_read_cpu_csr(cpuid); 78 reg = flowctrl_read_cpu_csr(cpuid);
78 reg &= ~TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP; /* clear wfe bitmap */ 79 switch (tegra_chip_id) {
79 reg &= ~TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP; /* clear wfi bitmap */ 80 case TEGRA20:
81 /* clear wfe bitmap */
82 reg &= ~TEGRA20_FLOW_CTRL_CSR_WFE_BITMAP;
83 /* clear wfi bitmap */
84 reg &= ~TEGRA20_FLOW_CTRL_CSR_WFI_BITMAP;
85 /* pwr gating on wfe */
86 reg |= TEGRA20_FLOW_CTRL_CSR_WFE_CPU0 << cpuid;
87 break;
88 case TEGRA30:
89 /* clear wfe bitmap */
90 reg &= ~TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP;
91 /* clear wfi bitmap */
92 reg &= ~TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP;
93 /* pwr gating on wfi */
94 reg |= TEGRA30_FLOW_CTRL_CSR_WFI_CPU0 << cpuid;
95 break;
96 }
80 reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr flag */ 97 reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr flag */
81 reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event flag */ 98 reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event flag */
82 reg |= TEGRA30_FLOW_CTRL_CSR_WFI_CPU0 << cpuid; /* pwr gating on wfi */
83 reg |= FLOW_CTRL_CSR_ENABLE; /* pwr gating */ 99 reg |= FLOW_CTRL_CSR_ENABLE; /* pwr gating */
84 flowctrl_write_cpu_csr(cpuid, reg); 100 flowctrl_write_cpu_csr(cpuid, reg);
85 101
@@ -99,8 +115,20 @@ void flowctrl_cpu_suspend_exit(unsigned int cpuid)
99 115
100 /* Disable powergating via flow controller for CPU0 */ 116 /* Disable powergating via flow controller for CPU0 */
101 reg = flowctrl_read_cpu_csr(cpuid); 117 reg = flowctrl_read_cpu_csr(cpuid);
102 reg &= ~TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP; /* clear wfe bitmap */ 118 switch (tegra_chip_id) {
103 reg &= ~TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP; /* clear wfi bitmap */ 119 case TEGRA20:
120 /* clear wfe bitmap */
121 reg &= ~TEGRA20_FLOW_CTRL_CSR_WFE_BITMAP;
122 /* clear wfi bitmap */
123 reg &= ~TEGRA20_FLOW_CTRL_CSR_WFI_BITMAP;
124 break;
125 case TEGRA30:
126 /* clear wfe bitmap */
127 reg &= ~TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP;
128 /* clear wfi bitmap */
129 reg &= ~TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP;
130 break;
131 }
104 reg &= ~FLOW_CTRL_CSR_ENABLE; /* clear enable */ 132 reg &= ~FLOW_CTRL_CSR_ENABLE; /* clear enable */
105 reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr */ 133 reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr */
106 reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event */ 134 reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event */
diff --git a/arch/arm/mach-tegra/flowctrl.h b/arch/arm/mach-tegra/flowctrl.h
index 0798dec1832d..67eab56699bd 100644
--- a/arch/arm/mach-tegra/flowctrl.h
+++ b/arch/arm/mach-tegra/flowctrl.h
@@ -34,6 +34,10 @@
34#define FLOW_CTRL_HALT_CPU1_EVENTS 0x14 34#define FLOW_CTRL_HALT_CPU1_EVENTS 0x14
35#define FLOW_CTRL_CPU1_CSR 0x18 35#define FLOW_CTRL_CPU1_CSR 0x18
36 36
37#define TEGRA20_FLOW_CTRL_CSR_WFE_CPU0 (1 << 4)
38#define TEGRA20_FLOW_CTRL_CSR_WFE_BITMAP (3 << 4)
39#define TEGRA20_FLOW_CTRL_CSR_WFI_BITMAP 0
40
37#define TEGRA30_FLOW_CTRL_CSR_WFI_CPU0 (1 << 8) 41#define TEGRA30_FLOW_CTRL_CSR_WFI_CPU0 (1 << 8)
38#define TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP (0xF << 4) 42#define TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP (0xF << 4)
39#define TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP (0xF << 8) 43#define TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP (0xF << 8)
diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c
index 2ff2128cb9d8..1952e82797cc 100644
--- a/arch/arm/mach-tegra/irq.c
+++ b/arch/arm/mach-tegra/irq.c
@@ -44,6 +44,8 @@
44 44
45#define FIRST_LEGACY_IRQ 32 45#define FIRST_LEGACY_IRQ 32
46 46
47#define SGI_MASK 0xFFFF
48
47static int num_ictlrs; 49static int num_ictlrs;
48 50
49static void __iomem *ictlr_reg_base[] = { 51static void __iomem *ictlr_reg_base[] = {
@@ -54,6 +56,19 @@ static void __iomem *ictlr_reg_base[] = {
54 IO_ADDRESS(TEGRA_QUINARY_ICTLR_BASE), 56 IO_ADDRESS(TEGRA_QUINARY_ICTLR_BASE),
55}; 57};
56 58
59bool tegra_pending_sgi(void)
60{
61 u32 pending_set;
62 void __iomem *distbase = IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE);
63
64 pending_set = readl_relaxed(distbase + GIC_DIST_PENDING_SET);
65
66 if (pending_set & SGI_MASK)
67 return true;
68
69 return false;
70}
71
57static inline void tegra_irq_write_mask(unsigned int irq, unsigned long reg) 72static inline void tegra_irq_write_mask(unsigned int irq, unsigned long reg)
58{ 73{
59 void __iomem *base; 74 void __iomem *base;
diff --git a/arch/arm/mach-tegra/irq.h b/arch/arm/mach-tegra/irq.h
new file mode 100644
index 000000000000..5142649bba05
--- /dev/null
+++ b/arch/arm/mach-tegra/irq.h
@@ -0,0 +1,22 @@
1/*
2 * Copyright (c) 2012, NVIDIA Corporation. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef __TEGRA_IRQ_H
18#define __TEGRA_IRQ_H
19
20bool tegra_pending_sgi(void);
21
22#endif
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index abfe9b93cc0c..523604de666f 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -36,6 +36,7 @@
36#include "iomap.h" 36#include "iomap.h"
37#include "reset.h" 37#include "reset.h"
38#include "flowctrl.h" 38#include "flowctrl.h"
39#include "fuse.h"
39#include "sleep.h" 40#include "sleep.h"
40 41
41#define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */ 42#define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */
@@ -173,6 +174,8 @@ bool tegra_set_cpu_in_lp2(int phy_cpu_id)
173 174
174 if ((phy_cpu_id == 0) && cpumask_equal(cpu_lp2_mask, cpu_online_mask)) 175 if ((phy_cpu_id == 0) && cpumask_equal(cpu_lp2_mask, cpu_online_mask))
175 last_cpu = true; 176 last_cpu = true;
177 else if (tegra_chip_id == TEGRA20 && phy_cpu_id == 1)
178 tegra20_cpu_set_resettable_soon();
176 179
177 spin_unlock(&tegra_lp2_lock); 180 spin_unlock(&tegra_lp2_lock);
178 return last_cpu; 181 return last_cpu;
diff --git a/arch/arm/mach-tegra/sleep-tegra20.S b/arch/arm/mach-tegra/sleep-tegra20.S
index ad2ca07d0578..9f6bfafdd512 100644
--- a/arch/arm/mach-tegra/sleep-tegra20.S
+++ b/arch/arm/mach-tegra/sleep-tegra20.S
@@ -21,6 +21,8 @@
21#include <linux/linkage.h> 21#include <linux/linkage.h>
22 22
23#include <asm/assembler.h> 23#include <asm/assembler.h>
24#include <asm/proc-fns.h>
25#include <asm/cp15.h>
24 26
25#include "sleep.h" 27#include "sleep.h"
26#include "flowctrl.h" 28#include "flowctrl.h"
@@ -55,6 +57,9 @@ ENDPROC(tegra20_hotplug_shutdown)
55ENTRY(tegra20_cpu_shutdown) 57ENTRY(tegra20_cpu_shutdown)
56 cmp r0, #0 58 cmp r0, #0
57 moveq pc, lr @ must not be called for CPU 0 59 moveq pc, lr @ must not be called for CPU 0
60 mov32 r1, TEGRA_PMC_VIRT + PMC_SCRATCH41
61 mov r12, #CPU_RESETTABLE
62 str r12, [r1]
58 63
59 cpu_to_halt_reg r1, r0 64 cpu_to_halt_reg r1, r0
60 ldr r3, =TEGRA_FLOW_CTRL_VIRT 65 ldr r3, =TEGRA_FLOW_CTRL_VIRT
@@ -75,3 +80,198 @@ ENTRY(tegra20_cpu_shutdown)
75 mov pc, lr 80 mov pc, lr
76ENDPROC(tegra20_cpu_shutdown) 81ENDPROC(tegra20_cpu_shutdown)
77#endif 82#endif
83
84#ifdef CONFIG_PM_SLEEP
85/*
86 * tegra_pen_lock
87 *
88 * spinlock implementation with no atomic test-and-set and no coherence
89 * using Peterson's algorithm on strongly-ordered registers
90 * used to synchronize a cpu waking up from wfi with entering lp2 on idle
91 *
92 * The reference link of Peterson's algorithm:
93 * http://en.wikipedia.org/wiki/Peterson's_algorithm
94 *
95 * SCRATCH37 = r1 = !turn (inverted from Peterson's algorithm)
96 * on cpu 0:
97 * r2 = flag[0] (in SCRATCH38)
98 * r3 = flag[1] (in SCRATCH39)
99 * on cpu1:
100 * r2 = flag[1] (in SCRATCH39)
101 * r3 = flag[0] (in SCRATCH38)
102 *
103 * must be called with MMU on
104 * corrupts r0-r3, r12
105 */
106ENTRY(tegra_pen_lock)
107 mov32 r3, TEGRA_PMC_VIRT
108 cpu_id r0
109 add r1, r3, #PMC_SCRATCH37
110 cmp r0, #0
111 addeq r2, r3, #PMC_SCRATCH38
112 addeq r3, r3, #PMC_SCRATCH39
113 addne r2, r3, #PMC_SCRATCH39
114 addne r3, r3, #PMC_SCRATCH38
115
116 mov r12, #1
117 str r12, [r2] @ flag[cpu] = 1
118 dsb
119 str r12, [r1] @ !turn = cpu
1201: dsb
121 ldr r12, [r3]
122 cmp r12, #1 @ flag[!cpu] == 1?
123 ldreq r12, [r1]
124 cmpeq r12, r0 @ !turn == cpu?
125 beq 1b @ while !turn == cpu && flag[!cpu] == 1
126
127 mov pc, lr @ locked
128ENDPROC(tegra_pen_lock)
129
130ENTRY(tegra_pen_unlock)
131 dsb
132 mov32 r3, TEGRA_PMC_VIRT
133 cpu_id r0
134 cmp r0, #0
135 addeq r2, r3, #PMC_SCRATCH38
136 addne r2, r3, #PMC_SCRATCH39
137 mov r12, #0
138 str r12, [r2]
139 mov pc, lr
140ENDPROC(tegra_pen_unlock)
141
142/*
143 * tegra20_cpu_clear_resettable(void)
144 *
145 * Called to clear the "resettable soon" flag in PMC_SCRATCH41 when
146 * it is expected that the secondary CPU will be idle soon.
147 */
148ENTRY(tegra20_cpu_clear_resettable)
149 mov32 r1, TEGRA_PMC_VIRT + PMC_SCRATCH41
150 mov r12, #CPU_NOT_RESETTABLE
151 str r12, [r1]
152 mov pc, lr
153ENDPROC(tegra20_cpu_clear_resettable)
154
155/*
156 * tegra20_cpu_set_resettable_soon(void)
157 *
158 * Called to set the "resettable soon" flag in PMC_SCRATCH41 when
159 * it is expected that the secondary CPU will be idle soon.
160 */
161ENTRY(tegra20_cpu_set_resettable_soon)
162 mov32 r1, TEGRA_PMC_VIRT + PMC_SCRATCH41
163 mov r12, #CPU_RESETTABLE_SOON
164 str r12, [r1]
165 mov pc, lr
166ENDPROC(tegra20_cpu_set_resettable_soon)
167
168/*
169 * tegra20_cpu_is_resettable_soon(void)
170 *
171 * Returns true if the "resettable soon" flag in PMC_SCRATCH41 has been
172 * set because it is expected that the secondary CPU will be idle soon.
173 */
174ENTRY(tegra20_cpu_is_resettable_soon)
175 mov32 r1, TEGRA_PMC_VIRT + PMC_SCRATCH41
176 ldr r12, [r1]
177 cmp r12, #CPU_RESETTABLE_SOON
178 moveq r0, #1
179 movne r0, #0
180 mov pc, lr
181ENDPROC(tegra20_cpu_is_resettable_soon)
182
183/*
184 * tegra20_sleep_cpu_secondary_finish(unsigned long v2p)
185 *
186 * Enters WFI on secondary CPU by exiting coherency.
187 */
188ENTRY(tegra20_sleep_cpu_secondary_finish)
189 stmfd sp!, {r4-r11, lr}
190
191 mrc p15, 0, r11, c1, c0, 1 @ save actlr before exiting coherency
192
193 /* Flush and disable the L1 data cache */
194 bl tegra_disable_clean_inv_dcache
195
196 mov32 r0, TEGRA_PMC_VIRT + PMC_SCRATCH41
197 mov r3, #CPU_RESETTABLE
198 str r3, [r0]
199
200 bl cpu_do_idle
201
202 /*
203 * cpu may be reset while in wfi, which will return through
204 * tegra_resume to cpu_resume
205 * or interrupt may wake wfi, which will return here
206 * cpu state is unchanged - MMU is on, cache is on, coherency
207 * is off, and the data cache is off
208 *
209 * r11 contains the original actlr
210 */
211
212 bl tegra_pen_lock
213
214 mov32 r3, TEGRA_PMC_VIRT
215 add r0, r3, #PMC_SCRATCH41
216 mov r3, #CPU_NOT_RESETTABLE
217 str r3, [r0]
218
219 bl tegra_pen_unlock
220
221 /* Re-enable the data cache */
222 mrc p15, 0, r10, c1, c0, 0
223 orr r10, r10, #CR_C
224 mcr p15, 0, r10, c1, c0, 0
225 isb
226
227 mcr p15, 0, r11, c1, c0, 1 @ reenable coherency
228
229 /* Invalidate the TLBs & BTAC */
230 mov r1, #0
231 mcr p15, 0, r1, c8, c3, 0 @ invalidate shared TLBs
232 mcr p15, 0, r1, c7, c1, 6 @ invalidate shared BTAC
233 dsb
234 isb
235
236 /* the cpu was running with coherency disabled,
237 * caches may be out of date */
238 bl v7_flush_kern_cache_louis
239
240 ldmfd sp!, {r4 - r11, pc}
241ENDPROC(tegra20_sleep_cpu_secondary_finish)
242
243/*
244 * tegra20_tear_down_cpu
245 *
246 * Switches the CPU cluster to PLL-P and enters sleep.
247 */
248ENTRY(tegra20_tear_down_cpu)
249 bl tegra_switch_cpu_to_pllp
250 b tegra20_enter_sleep
251ENDPROC(tegra20_tear_down_cpu)
252
253/*
254 * tegra20_enter_sleep
255 *
256 * uses flow controller to enter sleep state
257 * executes from IRAM with SDRAM in selfrefresh when target state is LP0 or LP1
258 * executes from SDRAM with target state is LP2
259 */
260tegra20_enter_sleep:
261 mov32 r6, TEGRA_FLOW_CTRL_BASE
262
263 mov r0, #FLOW_CTRL_WAIT_FOR_INTERRUPT
264 orr r0, r0, #FLOW_CTRL_HALT_CPU_IRQ | FLOW_CTRL_HALT_CPU_FIQ
265 cpu_id r1
266 cpu_to_halt_reg r1, r1
267 str r0, [r6, r1]
268 dsb
269 ldr r0, [r6, r1] /* memory barrier */
270
271halted:
272 dsb
273 wfe /* CPU should be power gated here */
274 isb
275 b halted
276
277#endif
diff --git a/arch/arm/mach-tegra/sleep.S b/arch/arm/mach-tegra/sleep.S
index addae357da3f..364d84523fba 100644
--- a/arch/arm/mach-tegra/sleep.S
+++ b/arch/arm/mach-tegra/sleep.S
@@ -34,6 +34,9 @@
34#include "flowctrl.h" 34#include "flowctrl.h"
35#include "sleep.h" 35#include "sleep.h"
36 36
37#define CLK_RESET_CCLK_BURST 0x20
38#define CLK_RESET_CCLK_DIVIDER 0x24
39
37#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP) 40#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP)
38/* 41/*
39 * tegra_disable_clean_inv_dcache 42 * tegra_disable_clean_inv_dcache
@@ -110,4 +113,20 @@ ENTRY(tegra_shut_off_mmu)
110 mov pc, r0 113 mov pc, r0
111ENDPROC(tegra_shut_off_mmu) 114ENDPROC(tegra_shut_off_mmu)
112 .popsection 115 .popsection
116
117/*
118 * tegra_switch_cpu_to_pllp
119 *
120 * In LP2 the normal cpu clock pllx will be turned off. Switch the CPU to pllp
121 */
122ENTRY(tegra_switch_cpu_to_pllp)
123 /* in LP2 idle (SDRAM active), set the CPU burst policy to PLLP */
124 mov32 r5, TEGRA_CLK_RESET_BASE
125 mov r0, #(2 << 28) @ burst policy = run mode
126 orr r0, r0, #(4 << 4) @ use PLLP in run mode burst
127 str r0, [r5, #CLK_RESET_CCLK_BURST]
128 mov r0, #0
129 str r0, [r5, #CLK_RESET_CCLK_DIVIDER]
130 mov pc, lr
131ENDPROC(tegra_switch_cpu_to_pllp)
113#endif 132#endif
diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h
index 56505c381ea8..4ffae541726e 100644
--- a/arch/arm/mach-tegra/sleep.h
+++ b/arch/arm/mach-tegra/sleep.h
@@ -25,6 +25,19 @@
25 + IO_PPSB_VIRT) 25 + IO_PPSB_VIRT)
26#define TEGRA_CLK_RESET_VIRT (TEGRA_CLK_RESET_BASE - IO_PPSB_PHYS \ 26#define TEGRA_CLK_RESET_VIRT (TEGRA_CLK_RESET_BASE - IO_PPSB_PHYS \
27 + IO_PPSB_VIRT) 27 + IO_PPSB_VIRT)
28#define TEGRA_PMC_VIRT (TEGRA_PMC_BASE - IO_APB_PHYS + IO_APB_VIRT)
29
30/* PMC_SCRATCH37-39 and 41 are used for tegra_pen_lock and idle */
31#define PMC_SCRATCH37 0x130
32#define PMC_SCRATCH38 0x134
33#define PMC_SCRATCH39 0x138
34#define PMC_SCRATCH41 0x140
35
36#ifdef CONFIG_ARCH_TEGRA_2x_SOC
37#define CPU_RESETTABLE 2
38#define CPU_RESETTABLE_SOON 1
39#define CPU_NOT_RESETTABLE 0
40#endif
28 41
29#ifdef __ASSEMBLY__ 42#ifdef __ASSEMBLY__
30/* returns the offset of the flow controller halt register for a cpu */ 43/* returns the offset of the flow controller halt register for a cpu */
@@ -104,6 +117,8 @@ exit_l2_resume:
104.endm 117.endm
105#endif /* CONFIG_CACHE_L2X0 */ 118#endif /* CONFIG_CACHE_L2X0 */
106#else 119#else
120void tegra_pen_lock(void);
121void tegra_pen_unlock(void);
107void tegra_resume(void); 122void tegra_resume(void);
108int tegra_sleep_cpu_finish(unsigned long); 123int tegra_sleep_cpu_finish(unsigned long);
109void tegra_disable_clean_inv_dcache(void); 124void tegra_disable_clean_inv_dcache(void);
@@ -116,6 +131,17 @@ static inline void tegra20_hotplug_init(void) {}
116static inline void tegra30_hotplug_init(void) {} 131static inline void tegra30_hotplug_init(void) {}
117#endif 132#endif
118 133
134void tegra20_cpu_shutdown(int cpu);
135int tegra20_cpu_is_resettable_soon(void);
136void tegra20_cpu_clear_resettable(void);
137#ifdef CONFIG_ARCH_TEGRA_2x_SOC
138void tegra20_cpu_set_resettable_soon(void);
139#else
140static inline void tegra20_cpu_set_resettable_soon(void) {}
141#endif
142
143int tegra20_sleep_cpu_secondary_finish(unsigned long);
144void tegra20_tear_down_cpu(void);
119int tegra30_sleep_cpu_secondary_finish(unsigned long); 145int tegra30_sleep_cpu_secondary_finish(unsigned long);
120void tegra30_tear_down_cpu(void); 146void tegra30_tear_down_cpu(void);
121 147