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> | |
25 | #include <linux/clk.h> | |
26 | #include <linux/err.h> | |
89572c77 | 27 | #include <linux/clk/tegra.h> |
d552920a JL |
28 | |
29 | #include <asm/smp_plat.h> | |
30 | #include <asm/cacheflush.h> | |
31 | #include <asm/suspend.h> | |
32 | #include <asm/idmap.h> | |
33 | #include <asm/proc-fns.h> | |
34 | #include <asm/tlbflush.h> | |
d457ef35 JL |
35 | |
36 | #include "iomap.h" | |
37 | #include "reset.h" | |
d552920a | 38 | #include "flowctrl.h" |
5c1350bd | 39 | #include "fuse.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 | |
45 | #define PMC_CPUPWRGOOD_TIMER 0xc8 | |
46 | #define PMC_CPUPWROFF_TIMER 0xcc | |
d457ef35 JL |
47 | |
48 | #ifdef CONFIG_PM_SLEEP | |
d457ef35 | 49 | static DEFINE_SPINLOCK(tegra_lp2_lock); |
d552920a JL |
50 | static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); |
51 | static struct clk *tegra_pclk; | |
52 | void (*tegra_tear_down_cpu)(void); | |
d457ef35 | 53 | |
d552920a JL |
54 | static void set_power_timers(unsigned long us_on, unsigned long us_off) |
55 | { | |
56 | unsigned long long ticks; | |
57 | unsigned long long pclk; | |
58 | unsigned long rate; | |
59 | static unsigned long tegra_last_pclk; | |
60 | ||
61 | if (tegra_pclk == NULL) { | |
62 | tegra_pclk = clk_get_sys(NULL, "pclk"); | |
63 | WARN_ON(IS_ERR(tegra_pclk)); | |
64 | } | |
65 | ||
66 | rate = clk_get_rate(tegra_pclk); | |
67 | ||
68 | if (WARN_ON_ONCE(rate <= 0)) | |
69 | pclk = 100000000; | |
70 | else | |
71 | pclk = rate; | |
72 | ||
73 | if ((rate != tegra_last_pclk)) { | |
74 | ticks = (us_on * pclk) + 999999ull; | |
75 | do_div(ticks, 1000000); | |
76 | writel((unsigned long)ticks, pmc + PMC_CPUPWRGOOD_TIMER); | |
77 | ||
78 | ticks = (us_off * pclk) + 999999ull; | |
79 | do_div(ticks, 1000000); | |
80 | writel((unsigned long)ticks, pmc + PMC_CPUPWROFF_TIMER); | |
81 | wmb(); | |
82 | } | |
83 | tegra_last_pclk = pclk; | |
84 | } | |
85 | ||
86 | /* | |
87 | * restore_cpu_complex | |
88 | * | |
89 | * restores cpu clock setting, clears flow controller | |
90 | * | |
91 | * Always called on CPU 0. | |
92 | */ | |
93 | static void restore_cpu_complex(void) | |
94 | { | |
95 | int cpu = smp_processor_id(); | |
96 | ||
97 | BUG_ON(cpu != 0); | |
98 | ||
99 | #ifdef CONFIG_SMP | |
100 | cpu = cpu_logical_map(cpu); | |
101 | #endif | |
102 | ||
103 | /* Restore the CPU clock settings */ | |
104 | tegra_cpu_clock_resume(); | |
105 | ||
106 | flowctrl_cpu_suspend_exit(cpu); | |
d552920a JL |
107 | } |
108 | ||
109 | /* | |
110 | * suspend_cpu_complex | |
111 | * | |
112 | * saves pll state for use by restart_plls, prepares flow controller for | |
113 | * transition to suspend state | |
114 | * | |
115 | * Must always be called on cpu 0. | |
116 | */ | |
117 | static void suspend_cpu_complex(void) | |
118 | { | |
119 | int cpu = smp_processor_id(); | |
120 | ||
121 | BUG_ON(cpu != 0); | |
122 | ||
123 | #ifdef CONFIG_SMP | |
124 | cpu = cpu_logical_map(cpu); | |
125 | #endif | |
126 | ||
127 | /* Save the CPU clock settings */ | |
128 | tegra_cpu_clock_suspend(); | |
129 | ||
130 | flowctrl_cpu_suspend_enter(cpu); | |
d552920a JL |
131 | } |
132 | ||
8c627fa6 | 133 | void tegra_clear_cpu_in_lp2(int phy_cpu_id) |
d457ef35 JL |
134 | { |
135 | u32 *cpu_in_lp2 = tegra_cpu_lp2_mask; | |
136 | ||
137 | spin_lock(&tegra_lp2_lock); | |
138 | ||
139 | BUG_ON(!(*cpu_in_lp2 & BIT(phy_cpu_id))); | |
140 | *cpu_in_lp2 &= ~BIT(phy_cpu_id); | |
141 | ||
142 | spin_unlock(&tegra_lp2_lock); | |
143 | } | |
144 | ||
8c627fa6 | 145 | bool tegra_set_cpu_in_lp2(int phy_cpu_id) |
d457ef35 JL |
146 | { |
147 | bool last_cpu = false; | |
148 | cpumask_t *cpu_lp2_mask = tegra_cpu_lp2_mask; | |
149 | u32 *cpu_in_lp2 = tegra_cpu_lp2_mask; | |
150 | ||
151 | spin_lock(&tegra_lp2_lock); | |
152 | ||
153 | BUG_ON((*cpu_in_lp2 & BIT(phy_cpu_id))); | |
154 | *cpu_in_lp2 |= BIT(phy_cpu_id); | |
155 | ||
156 | if ((phy_cpu_id == 0) && cpumask_equal(cpu_lp2_mask, cpu_online_mask)) | |
157 | last_cpu = true; | |
5c1350bd JL |
158 | else if (tegra_chip_id == TEGRA20 && phy_cpu_id == 1) |
159 | tegra20_cpu_set_resettable_soon(); | |
d457ef35 JL |
160 | |
161 | spin_unlock(&tegra_lp2_lock); | |
162 | return last_cpu; | |
163 | } | |
d552920a JL |
164 | |
165 | static int tegra_sleep_cpu(unsigned long v2p) | |
166 | { | |
167 | /* Switch to the identity mapping. */ | |
168 | cpu_switch_mm(idmap_pgd, &init_mm); | |
169 | ||
170 | /* Flush the TLB. */ | |
171 | local_flush_tlb_all(); | |
172 | ||
173 | tegra_sleep_cpu_finish(v2p); | |
174 | ||
175 | /* should never here */ | |
176 | BUG(); | |
177 | ||
178 | return 0; | |
179 | } | |
180 | ||
181 | void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time) | |
182 | { | |
183 | u32 mode; | |
184 | ||
185 | /* Only the last cpu down does the final suspend steps */ | |
186 | mode = readl(pmc + PMC_CTRL); | |
187 | mode |= TEGRA_POWER_CPU_PWRREQ_OE; | |
188 | writel(mode, pmc + PMC_CTRL); | |
189 | ||
190 | set_power_timers(cpu_on_time, cpu_off_time); | |
191 | ||
192 | cpu_cluster_pm_enter(); | |
193 | suspend_cpu_complex(); | |
d552920a JL |
194 | |
195 | cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu); | |
196 | ||
d552920a JL |
197 | restore_cpu_complex(); |
198 | cpu_cluster_pm_exit(); | |
199 | } | |
d457ef35 | 200 | #endif |