diff options
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/mach-tegra/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-tegra/cpuidle-tegra30.c | 86 | ||||
-rw-r--r-- | arch/arm/mach-tegra/pm.c | 74 | ||||
-rw-r--r-- | arch/arm/mach-tegra/pm.h | 30 | ||||
-rw-r--r-- | arch/arm/mach-tegra/reset.h | 9 | ||||
-rw-r--r-- | arch/arm/mach-tegra/sleep-tegra30.S | 22 | ||||
-rw-r--r-- | arch/arm/mach-tegra/sleep.S | 29 | ||||
-rw-r--r-- | arch/arm/mach-tegra/sleep.h | 2 |
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 | |||
8 | obj-y += flowctrl.o | 8 | obj-y += flowctrl.o |
9 | obj-y += powergate.o | 9 | obj-y += powergate.o |
10 | obj-y += apbio.o | 10 | obj-y += apbio.o |
11 | obj-y += pm.o | ||
11 | obj-$(CONFIG_CPU_IDLE) += cpuidle.o | 12 | obj-$(CONFIG_CPU_IDLE) += cpuidle.o |
12 | obj-$(CONFIG_CPU_IDLE) += sleep.o | 13 | obj-$(CONFIG_CPU_IDLE) += sleep.o |
13 | obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_clocks.o | 14 | obj-$(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 | ||
37 | static int tegra30_idle_lp2(struct cpuidle_device *dev, | ||
38 | struct cpuidle_driver *drv, | ||
39 | int index); | ||
40 | #endif | ||
27 | 41 | ||
28 | static struct cpuidle_driver tegra_idle_driver = { | 42 | static 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 | ||
38 | static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device); | 67 | static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device); |
39 | 68 | ||
69 | #ifdef CONFIG_PM_SLEEP | ||
70 | #ifdef CONFIG_SMP | ||
71 | static 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 | ||
90 | static 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 | |||
98 | static 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 | |||
40 | int __init tegra30_cpuidle_init(void) | 126 | int __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 | ||
28 | static unsigned int g_diag_reg; | ||
29 | static DEFINE_SPINLOCK(tegra_lp2_lock); | ||
30 | |||
31 | void 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 | |||
38 | void 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 | |||
45 | void __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 | |||
57 | bool __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 | |||
24 | void save_cpu_arch_register(void); | ||
25 | void restore_cpu_arch_register(void); | ||
26 | |||
27 | void tegra_clear_cpu_in_lp2(int phy_cpu_id); | ||
28 | bool 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 | |||
32 | extern unsigned long __tegra_cpu_reset_handler_data[TEGRA_RESET_DATA_SIZE]; | 34 | extern unsigned long __tegra_cpu_reset_handler_data[TEGRA_RESET_DATA_SIZE]; |
33 | 35 | ||
34 | void __tegra_cpu_reset_handler_start(void); | 36 | void __tegra_cpu_reset_handler_start(void); |
@@ -36,6 +38,13 @@ void __tegra_cpu_reset_handler(void); | |||
36 | void __tegra_cpu_reset_handler_end(void); | 38 | void __tegra_cpu_reset_handler_end(void); |
37 | void tegra_secondary_startup(void); | 39 | void 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 | ||
104 | ENDPROC(tegra30_cpu_shutdown) | 106 | ENDPROC(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 | */ | ||
115 | ENTRY(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 | ||
126 | ENDPROC(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 | */ | ||
43 | ENTRY(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} | ||
60 | ENDPROC(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) {} | |||
82 | static inline void tegra30_hotplug_init(void) {} | 82 | static inline void tegra30_hotplug_init(void) {} |
83 | #endif | 83 | #endif |
84 | 84 | ||
85 | int tegra30_sleep_cpu_secondary_finish(unsigned long); | ||
86 | |||
85 | #endif | 87 | #endif |
86 | #endif | 88 | #endif |