Commit | Line | Data |
---|---|---|
d457ef35 JL |
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> | |
d552920a JL |
23 | #include <linux/delay.h> |
24 | #include <linux/cpu_pm.h> | |
d552920a | 25 | #include <linux/err.h> |
89572c77 | 26 | #include <linux/clk/tegra.h> |
d552920a JL |
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> | |
d457ef35 JL |
34 | |
35 | #include "iomap.h" | |
36 | #include "reset.h" | |
d552920a | 37 | #include "flowctrl.h" |
5c1350bd | 38 | #include "fuse.h" |
0337c3e0 | 39 | #include "pmc.h" |
d552920a | 40 | #include "sleep.h" |
d552920a JL |
41 | |
42 | #define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */ | |
43 | ||
44 | #define PMC_CTRL 0x0 | |
d457ef35 JL |
45 | |
46 | #ifdef CONFIG_PM_SLEEP | |
d457ef35 | 47 | static DEFINE_SPINLOCK(tegra_lp2_lock); |
d552920a | 48 | static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); |
d552920a | 49 | void (*tegra_tear_down_cpu)(void); |
d457ef35 | 50 | |
d552920a JL |
51 | /* |
52 | * restore_cpu_complex | |
53 | * | |
54 | * restores cpu clock setting, clears flow controller | |
55 | * | |
56 | * Always called on CPU 0. | |
57 | */ | |
58 | static void restore_cpu_complex(void) | |
59 | { | |
60 | int cpu = smp_processor_id(); | |
61 | ||
62 | BUG_ON(cpu != 0); | |
63 | ||
64 | #ifdef CONFIG_SMP | |
65 | cpu = cpu_logical_map(cpu); | |
66 | #endif | |
67 | ||
68 | /* Restore the CPU clock settings */ | |
69 | tegra_cpu_clock_resume(); | |
70 | ||
71 | flowctrl_cpu_suspend_exit(cpu); | |
d552920a JL |
72 | } |
73 | ||
74 | /* | |
75 | * suspend_cpu_complex | |
76 | * | |
77 | * saves pll state for use by restart_plls, prepares flow controller for | |
78 | * transition to suspend state | |
79 | * | |
80 | * Must always be called on cpu 0. | |
81 | */ | |
82 | static void suspend_cpu_complex(void) | |
83 | { | |
84 | int cpu = smp_processor_id(); | |
85 | ||
86 | BUG_ON(cpu != 0); | |
87 | ||
88 | #ifdef CONFIG_SMP | |
89 | cpu = cpu_logical_map(cpu); | |
90 | #endif | |
91 | ||
92 | /* Save the CPU clock settings */ | |
93 | tegra_cpu_clock_suspend(); | |
94 | ||
95 | flowctrl_cpu_suspend_enter(cpu); | |
d552920a JL |
96 | } |
97 | ||
8c627fa6 | 98 | void tegra_clear_cpu_in_lp2(int phy_cpu_id) |
d457ef35 JL |
99 | { |
100 | u32 *cpu_in_lp2 = tegra_cpu_lp2_mask; | |
101 | ||
102 | spin_lock(&tegra_lp2_lock); | |
103 | ||
104 | BUG_ON(!(*cpu_in_lp2 & BIT(phy_cpu_id))); | |
105 | *cpu_in_lp2 &= ~BIT(phy_cpu_id); | |
106 | ||
107 | spin_unlock(&tegra_lp2_lock); | |
108 | } | |
109 | ||
8c627fa6 | 110 | bool tegra_set_cpu_in_lp2(int phy_cpu_id) |
d457ef35 JL |
111 | { |
112 | bool last_cpu = false; | |
113 | cpumask_t *cpu_lp2_mask = tegra_cpu_lp2_mask; | |
114 | u32 *cpu_in_lp2 = tegra_cpu_lp2_mask; | |
115 | ||
116 | spin_lock(&tegra_lp2_lock); | |
117 | ||
118 | BUG_ON((*cpu_in_lp2 & BIT(phy_cpu_id))); | |
119 | *cpu_in_lp2 |= BIT(phy_cpu_id); | |
120 | ||
121 | if ((phy_cpu_id == 0) && cpumask_equal(cpu_lp2_mask, cpu_online_mask)) | |
122 | last_cpu = true; | |
5c1350bd JL |
123 | else if (tegra_chip_id == TEGRA20 && phy_cpu_id == 1) |
124 | tegra20_cpu_set_resettable_soon(); | |
d457ef35 JL |
125 | |
126 | spin_unlock(&tegra_lp2_lock); | |
127 | return last_cpu; | |
128 | } | |
d552920a JL |
129 | |
130 | static int tegra_sleep_cpu(unsigned long v2p) | |
131 | { | |
132 | /* Switch to the identity mapping. */ | |
133 | cpu_switch_mm(idmap_pgd, &init_mm); | |
134 | ||
135 | /* Flush the TLB. */ | |
136 | local_flush_tlb_all(); | |
137 | ||
138 | tegra_sleep_cpu_finish(v2p); | |
139 | ||
140 | /* should never here */ | |
141 | BUG(); | |
142 | ||
143 | return 0; | |
144 | } | |
145 | ||
146 | void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time) | |
147 | { | |
148 | u32 mode; | |
149 | ||
150 | /* Only the last cpu down does the final suspend steps */ | |
151 | mode = readl(pmc + PMC_CTRL); | |
152 | mode |= TEGRA_POWER_CPU_PWRREQ_OE; | |
153 | writel(mode, pmc + PMC_CTRL); | |
154 | ||
155 | set_power_timers(cpu_on_time, cpu_off_time); | |
156 | ||
157 | cpu_cluster_pm_enter(); | |
158 | suspend_cpu_complex(); | |
d552920a JL |
159 | |
160 | cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu); | |
161 | ||
d552920a JL |
162 | restore_cpu_complex(); |
163 | cpu_cluster_pm_exit(); | |
164 | } | |
d457ef35 | 165 | #endif |