aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoseph Lo <josephl@nvidia.com>2012-10-31 05:41:17 -0400
committerStephen Warren <swarren@nvidia.com>2012-11-15 17:09:21 -0500
commitd457ef358f3c7179c428becda45b1dfd2b8cf98a (patch)
tree90299ea88a7fb8ebe02cb2cd45160db13afe6775
parentd3f293656c07a1147c11e8c8774d7955a903cee0 (diff)
ARM: tegra30: cpuidle: add powered-down state for secondary CPUs
This supports power-gated idle on secondary CPUs for Tegra30. The secondary CPUs can go into powered-down state independently. When CPU goes into this state, it saves it's contexts and puts itself to flow controlled WFI state. After that, it will been power gated. Be aware of that, you may see the legacy power state "LP2" in the code which is exactly the same meaning of "CPU power down". Based on the work by: Scott Williams <scwilliams@nvidia.com> Signed-off-by: Joseph Lo <josephl@nvidia.com> Signed-off-by: Stephen Warren <swarren@nvidia.com>
-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