aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/mach-tegra/Makefile1
-rw-r--r--arch/arm/mach-tegra/cpuidle-tegra30.c86
-rw-r--r--arch/arm/mach-tegra/pm.c74
-rw-r--r--arch/arm/mach-tegra/pm.h30
-rw-r--r--arch/arm/mach-tegra/reset.h9
-rw-r--r--arch/arm/mach-tegra/sleep-tegra30.S22
-rw-r--r--arch/arm/mach-tegra/sleep.S29
-rw-r--r--arch/arm/mach-tegra/sleep.h2
8 files changed, 253 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 488159eaa42..0979e8bba78 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -8,6 +8,7 @@ obj-y += pmc.o
8obj-y += flowctrl.o 8obj-y += flowctrl.o
9obj-y += powergate.o 9obj-y += powergate.o
10obj-y += apbio.o 10obj-y += apbio.o
11obj-y += pm.o
11obj-$(CONFIG_CPU_IDLE) += cpuidle.o 12obj-$(CONFIG_CPU_IDLE) += cpuidle.o
12obj-$(CONFIG_CPU_IDLE) += sleep.o 13obj-$(CONFIG_CPU_IDLE) += sleep.o
13obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_clocks.o 14obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_clocks.o
diff --git a/arch/arm/mach-tegra/cpuidle-tegra30.c b/arch/arm/mach-tegra/cpuidle-tegra30.c
index 37e75512f69..cc48d7fa335 100644
--- a/arch/arm/mach-tegra/cpuidle-tegra30.c
+++ b/arch/arm/mach-tegra/cpuidle-tegra30.c
@@ -22,21 +22,107 @@
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>
25 27
26#include <asm/cpuidle.h> 28#include <asm/cpuidle.h>
29#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 tegra30_idle_lp2(struct cpuidle_device *dev,
38 struct cpuidle_driver *drv,
39 int index);
40#endif
27 41
28static struct cpuidle_driver tegra_idle_driver = { 42static struct cpuidle_driver tegra_idle_driver = {
29 .name = "tegra_idle", 43 .name = "tegra_idle",
30 .owner = THIS_MODULE, 44 .owner = THIS_MODULE,
31 .en_core_tk_irqen = 1, 45 .en_core_tk_irqen = 1,
46#ifdef CONFIG_PM_SLEEP
47 .state_count = 2,
48#else
32 .state_count = 1, 49 .state_count = 1,
50#endif
33 .states = { 51 .states = {
34 [0] = ARM_CPUIDLE_WFI_STATE_PWR(600), 52 [0] = ARM_CPUIDLE_WFI_STATE_PWR(600),
53#ifdef CONFIG_PM_SLEEP
54 [1] = {
55 .enter = tegra30_idle_lp2,
56 .exit_latency = 2000,
57 .target_residency = 2200,
58 .power_usage = 0,
59 .flags = CPUIDLE_FLAG_TIME_VALID,
60 .name = "powered-down",
61 .desc = "CPU power gated",
62 },
63#endif
35 }, 64 },
36}; 65};
37 66
38static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device); 67static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device);
39 68
69#ifdef CONFIG_PM_SLEEP
70#ifdef CONFIG_SMP
71static bool tegra30_cpu_core_power_down(struct cpuidle_device *dev,
72 struct cpuidle_driver *drv,
73 int index)
74{
75 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
76
77 smp_wmb();
78
79 save_cpu_arch_register();
80
81 cpu_suspend(0, tegra30_sleep_cpu_secondary_finish);
82
83 restore_cpu_arch_register();
84
85 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
86
87 return true;
88}
89#else
90static inline bool tegra30_cpu_core_power_down(struct cpuidle_device *dev,
91 struct cpuidle_driver *drv,
92 int index)
93{
94 return true;
95}
96#endif
97
98static int __cpuinit tegra30_idle_lp2(struct cpuidle_device *dev,
99 struct cpuidle_driver *drv,
100 int index)
101{
102 u32 cpu = is_smp() ? cpu_logical_map(dev->cpu) : dev->cpu;
103 bool entered_lp2 = false;
104
105 local_fiq_disable();
106
107 tegra_set_cpu_in_lp2(cpu);
108 cpu_pm_enter();
109
110 if (cpu == 0)
111 cpu_do_idle();
112 else
113 entered_lp2 = tegra30_cpu_core_power_down(dev, drv, index);
114
115 cpu_pm_exit();
116 tegra_clear_cpu_in_lp2(cpu);
117
118 local_fiq_enable();
119
120 smp_rmb();
121
122 return (entered_lp2) ? index : 0;
123}
124#endif
125
40int __init tegra30_cpuidle_init(void) 126int __init tegra30_cpuidle_init(void)
41{ 127{
42 int ret; 128 int ret;
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
new file mode 100644
index 00000000000..f88595abc47
--- /dev/null
+++ b/arch/arm/mach-tegra/pm.c
@@ -0,0 +1,74 @@
1/*
2 * CPU complex suspend & resume functions for Tegra SoCs
3 *
4 * Copyright (c) 2009-2012, NVIDIA Corporation. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <linux/kernel.h>
20#include <linux/spinlock.h>
21#include <linux/io.h>
22#include <linux/cpumask.h>
23
24#include "iomap.h"
25#include "reset.h"
26
27#ifdef CONFIG_PM_SLEEP
28static unsigned int g_diag_reg;
29static DEFINE_SPINLOCK(tegra_lp2_lock);
30
31void save_cpu_arch_register(void)
32{
33 /* read diagnostic register */
34 asm("mrc p15, 0, %0, c15, c0, 1" : "=r"(g_diag_reg) : : "cc");
35 return;
36}
37
38void restore_cpu_arch_register(void)
39{
40 /* write diagnostic register */
41 asm("mcr p15, 0, %0, c15, c0, 1" : : "r"(g_diag_reg) : "cc");
42 return;
43}
44
45void __cpuinit tegra_clear_cpu_in_lp2(int phy_cpu_id)
46{
47 u32 *cpu_in_lp2 = tegra_cpu_lp2_mask;
48
49 spin_lock(&tegra_lp2_lock);
50
51 BUG_ON(!(*cpu_in_lp2 & BIT(phy_cpu_id)));
52 *cpu_in_lp2 &= ~BIT(phy_cpu_id);
53
54 spin_unlock(&tegra_lp2_lock);
55}
56
57bool __cpuinit tegra_set_cpu_in_lp2(int phy_cpu_id)
58{
59 bool last_cpu = false;
60 cpumask_t *cpu_lp2_mask = tegra_cpu_lp2_mask;
61 u32 *cpu_in_lp2 = tegra_cpu_lp2_mask;
62
63 spin_lock(&tegra_lp2_lock);
64
65 BUG_ON((*cpu_in_lp2 & BIT(phy_cpu_id)));
66 *cpu_in_lp2 |= BIT(phy_cpu_id);
67
68 if ((phy_cpu_id == 0) && cpumask_equal(cpu_lp2_mask, cpu_online_mask))
69 last_cpu = true;
70
71 spin_unlock(&tegra_lp2_lock);
72 return last_cpu;
73}
74#endif
diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h
new file mode 100644
index 00000000000..bcfc45faad7
--- /dev/null
+++ b/arch/arm/mach-tegra/pm.h
@@ -0,0 +1,30 @@
1/*
2 * Copyright (C) 2010 Google, Inc.
3 * Copyright (c) 2010-2012 NVIDIA Corporation. All rights reserved.
4 *
5 * Author:
6 * Colin Cross <ccross@google.com>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms and conditions of the GNU General Public License,
10 * version 2, as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#ifndef _MACH_TEGRA_PM_H_
22#define _MACH_TEGRA_PM_H_
23
24void save_cpu_arch_register(void);
25void restore_cpu_arch_register(void);
26
27void tegra_clear_cpu_in_lp2(int phy_cpu_id);
28bool tegra_set_cpu_in_lp2(int phy_cpu_id);
29
30#endif /* _MACH_TEGRA_PM_H_ */
diff --git a/arch/arm/mach-tegra/reset.h b/arch/arm/mach-tegra/reset.h
index de88bf851dd..c90d8e9c4ad 100644
--- a/arch/arm/mach-tegra/reset.h
+++ b/arch/arm/mach-tegra/reset.h
@@ -29,6 +29,8 @@
29 29
30#ifndef __ASSEMBLY__ 30#ifndef __ASSEMBLY__
31 31
32#include "irammap.h"
33
32extern unsigned long __tegra_cpu_reset_handler_data[TEGRA_RESET_DATA_SIZE]; 34extern unsigned long __tegra_cpu_reset_handler_data[TEGRA_RESET_DATA_SIZE];
33 35
34void __tegra_cpu_reset_handler_start(void); 36void __tegra_cpu_reset_handler_start(void);
@@ -36,6 +38,13 @@ void __tegra_cpu_reset_handler(void);
36void __tegra_cpu_reset_handler_end(void); 38void __tegra_cpu_reset_handler_end(void);
37void tegra_secondary_startup(void); 39void tegra_secondary_startup(void);
38 40
41#ifdef CONFIG_PM_SLEEP
42#define tegra_cpu_lp2_mask \
43 (IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \
44 ((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP2] - \
45 (u32)__tegra_cpu_reset_handler_start)))
46#endif
47
39#define tegra_cpu_reset_handler_offset \ 48#define tegra_cpu_reset_handler_offset \
40 ((u32)__tegra_cpu_reset_handler - \ 49 ((u32)__tegra_cpu_reset_handler - \
41 (u32)__tegra_cpu_reset_handler_start) 50 (u32)__tegra_cpu_reset_handler_start)
diff --git a/arch/arm/mach-tegra/sleep-tegra30.S b/arch/arm/mach-tegra/sleep-tegra30.S
index be7614b7c5c..59984d71848 100644
--- a/arch/arm/mach-tegra/sleep-tegra30.S
+++ b/arch/arm/mach-tegra/sleep-tegra30.S
@@ -17,6 +17,7 @@
17#include <linux/linkage.h> 17#include <linux/linkage.h>
18 18
19#include <asm/assembler.h> 19#include <asm/assembler.h>
20#include <asm/asm-offsets.h>
20 21
21#include "sleep.h" 22#include "sleep.h"
22#include "flowctrl.h" 23#include "flowctrl.h"
@@ -80,6 +81,7 @@ delay_1:
80 ldr r3, [r1] @ read CSR 81 ldr r3, [r1] @ read CSR
81 str r3, [r1] @ clear CSR 82 str r3, [r1] @ clear CSR
82 tst r0, #TEGRA30_POWER_HOTPLUG_SHUTDOWN 83 tst r0, #TEGRA30_POWER_HOTPLUG_SHUTDOWN
84 moveq r3, #FLOW_CTRL_WAIT_FOR_INTERRUPT @ For LP2
83 movne r3, #FLOW_CTRL_WAITEVENT @ For hotplug 85 movne r3, #FLOW_CTRL_WAITEVENT @ For hotplug
84 str r3, [r2] 86 str r3, [r2]
85 ldr r0, [r2] 87 ldr r0, [r2]
@@ -103,3 +105,23 @@ wfe_war:
103 105
104ENDPROC(tegra30_cpu_shutdown) 106ENDPROC(tegra30_cpu_shutdown)
105#endif 107#endif
108
109#ifdef CONFIG_PM_SLEEP
110/*
111 * tegra30_sleep_cpu_secondary_finish(unsigned long v2p)
112 *
113 * Enters LP2 on secondary CPU by exiting coherency and powergating the CPU.
114 */
115ENTRY(tegra30_sleep_cpu_secondary_finish)
116 mov r7, lr
117
118 /* Flush and disable the L1 data cache */
119 bl tegra_disable_clean_inv_dcache
120
121 /* Powergate this CPU. */
122 mov r0, #0 @ power mode flags (!hotplug)
123 bl tegra30_cpu_shutdown
124 mov r0, #1 @ never return here
125 mov pc, r7
126ENDPROC(tegra30_sleep_cpu_secondary_finish)
127#endif
diff --git a/arch/arm/mach-tegra/sleep.S b/arch/arm/mach-tegra/sleep.S
index 08e9481c049..91548a77dd9 100644
--- a/arch/arm/mach-tegra/sleep.S
+++ b/arch/arm/mach-tegra/sleep.S
@@ -25,9 +25,38 @@
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/cp15.h>
28 29
29#include "iomap.h" 30#include "iomap.h"
30 31
31#include "flowctrl.h" 32#include "flowctrl.h"
32#include "sleep.h" 33#include "sleep.h"
33 34
35#ifdef CONFIG_PM_SLEEP
36/*
37 * tegra_disable_clean_inv_dcache
38 *
39 * disable, clean & invalidate the D-cache
40 *
41 * Corrupted registers: r1-r3, r6, r8, r9-r11
42 */
43ENTRY(tegra_disable_clean_inv_dcache)
44 stmfd sp!, {r0, r4-r5, r7, r9-r11, lr}
45 dmb @ ensure ordering
46
47 /* Disable the D-cache */
48 mrc p15, 0, r2, c1, c0, 0
49 bic r2, r2, #CR_C
50 mcr p15, 0, r2, c1, c0, 0
51 isb
52
53 /* Flush the D-cache */
54 bl v7_flush_dcache_louis
55
56 /* Trun off coherency */
57 exit_smp r4, r5
58
59 ldmfd sp!, {r0, r4-r5, r7, r9-r11, pc}
60ENDPROC(tegra_disable_clean_inv_dcache)
61
62#endif
diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h
index addb83f5bc7..bacf549bd54 100644
--- a/arch/arm/mach-tegra/sleep.h
+++ b/arch/arm/mach-tegra/sleep.h
@@ -82,5 +82,7 @@ static inline void tegra20_hotplug_init(void) {}
82static inline void tegra30_hotplug_init(void) {} 82static inline void tegra30_hotplug_init(void) {}
83#endif 83#endif
84 84
85int tegra30_sleep_cpu_secondary_finish(unsigned long);
86
85#endif 87#endif
86#endif 88#endif