diff options
Diffstat (limited to 'arch/arm/mach-tegra/pmc.c')
-rw-r--r-- | arch/arm/mach-tegra/pmc.c | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c index b30e921cc3a9..32360e540ce6 100644 --- a/arch/arm/mach-tegra/pmc.c +++ b/arch/arm/mach-tegra/pmc.c | |||
@@ -16,10 +16,20 @@ | |||
16 | */ | 16 | */ |
17 | 17 | ||
18 | #include <linux/kernel.h> | 18 | #include <linux/kernel.h> |
19 | #include <linux/clk.h> | ||
19 | #include <linux/io.h> | 20 | #include <linux/io.h> |
20 | #include <linux/of.h> | 21 | #include <linux/of.h> |
21 | #include <linux/of_address.h> | 22 | #include <linux/of_address.h> |
22 | 23 | ||
24 | #include "fuse.h" | ||
25 | #include "pm.h" | ||
26 | #include "pmc.h" | ||
27 | #include "sleep.h" | ||
28 | |||
29 | #define TEGRA_POWER_EFFECT_LP0 (1 << 14) /* LP0 when CPU pwr gated */ | ||
30 | #define TEGRA_POWER_CPU_PWRREQ_POLARITY (1 << 15) /* CPU pwr req polarity */ | ||
31 | #define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */ | ||
32 | |||
23 | #define PMC_CTRL 0x0 | 33 | #define PMC_CTRL 0x0 |
24 | #define PMC_CTRL_INTR_LOW (1 << 17) | 34 | #define PMC_CTRL_INTR_LOW (1 << 17) |
25 | #define PMC_PWRGATE_TOGGLE 0x30 | 35 | #define PMC_PWRGATE_TOGGLE 0x30 |
@@ -27,6 +37,9 @@ | |||
27 | #define PMC_REMOVE_CLAMPING 0x34 | 37 | #define PMC_REMOVE_CLAMPING 0x34 |
28 | #define PMC_PWRGATE_STATUS 0x38 | 38 | #define PMC_PWRGATE_STATUS 0x38 |
29 | 39 | ||
40 | #define PMC_CPUPWRGOOD_TIMER 0xc8 | ||
41 | #define PMC_CPUPWROFF_TIMER 0xcc | ||
42 | |||
30 | #define TEGRA_POWERGATE_PCIE 3 | 43 | #define TEGRA_POWERGATE_PCIE 3 |
31 | #define TEGRA_POWERGATE_VDEC 4 | 44 | #define TEGRA_POWERGATE_VDEC 4 |
32 | #define TEGRA_POWERGATE_CPU1 9 | 45 | #define TEGRA_POWERGATE_CPU1 9 |
@@ -43,6 +56,23 @@ static DEFINE_SPINLOCK(tegra_powergate_lock); | |||
43 | 56 | ||
44 | static void __iomem *tegra_pmc_base; | 57 | static void __iomem *tegra_pmc_base; |
45 | static bool tegra_pmc_invert_interrupt; | 58 | static bool tegra_pmc_invert_interrupt; |
59 | static struct clk *tegra_pclk; | ||
60 | |||
61 | struct pmc_pm_data { | ||
62 | u32 cpu_good_time; /* CPU power good time in uS */ | ||
63 | u32 cpu_off_time; /* CPU power off time in uS */ | ||
64 | u32 core_osc_time; /* Core power good osc time in uS */ | ||
65 | u32 core_pmu_time; /* Core power good pmu time in uS */ | ||
66 | u32 core_off_time; /* Core power off time in uS */ | ||
67 | bool corereq_high; /* Core power request active-high */ | ||
68 | bool sysclkreq_high; /* System clock request active-high */ | ||
69 | bool combined_req; /* Combined pwr req for CPU & Core */ | ||
70 | bool cpu_pwr_good_en; /* CPU power good signal is enabled */ | ||
71 | u32 lp0_vec_phy_addr; /* The phy addr of LP0 warm boot code */ | ||
72 | u32 lp0_vec_size; /* The size of LP0 warm boot code */ | ||
73 | enum tegra_suspend_mode suspend_mode; | ||
74 | }; | ||
75 | static struct pmc_pm_data pmc_pm_data; | ||
46 | 76 | ||
47 | static inline u32 tegra_pmc_readl(u32 reg) | 77 | static inline u32 tegra_pmc_readl(u32 reg) |
48 | { | 78 | { |
@@ -133,6 +163,70 @@ int tegra_pmc_cpu_remove_clamping(int cpuid) | |||
133 | return tegra_pmc_powergate_remove_clamping(id); | 163 | return tegra_pmc_powergate_remove_clamping(id); |
134 | } | 164 | } |
135 | 165 | ||
166 | #ifdef CONFIG_PM_SLEEP | ||
167 | static void set_power_timers(u32 us_on, u32 us_off, unsigned long rate) | ||
168 | { | ||
169 | unsigned long long ticks; | ||
170 | unsigned long long pclk; | ||
171 | static unsigned long tegra_last_pclk; | ||
172 | |||
173 | if (WARN_ON_ONCE(rate <= 0)) | ||
174 | pclk = 100000000; | ||
175 | else | ||
176 | pclk = rate; | ||
177 | |||
178 | if ((rate != tegra_last_pclk)) { | ||
179 | ticks = (us_on * pclk) + 999999ull; | ||
180 | do_div(ticks, 1000000); | ||
181 | tegra_pmc_writel((unsigned long)ticks, PMC_CPUPWRGOOD_TIMER); | ||
182 | |||
183 | ticks = (us_off * pclk) + 999999ull; | ||
184 | do_div(ticks, 1000000); | ||
185 | tegra_pmc_writel((unsigned long)ticks, PMC_CPUPWROFF_TIMER); | ||
186 | wmb(); | ||
187 | } | ||
188 | tegra_last_pclk = pclk; | ||
189 | } | ||
190 | |||
191 | enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void) | ||
192 | { | ||
193 | return pmc_pm_data.suspend_mode; | ||
194 | } | ||
195 | |||
196 | void tegra_pmc_pm_set(enum tegra_suspend_mode mode) | ||
197 | { | ||
198 | u32 reg; | ||
199 | unsigned long rate = 0; | ||
200 | |||
201 | reg = tegra_pmc_readl(PMC_CTRL); | ||
202 | reg |= TEGRA_POWER_CPU_PWRREQ_OE; | ||
203 | reg &= ~TEGRA_POWER_EFFECT_LP0; | ||
204 | |||
205 | switch (mode) { | ||
206 | case TEGRA_SUSPEND_LP2: | ||
207 | rate = clk_get_rate(tegra_pclk); | ||
208 | break; | ||
209 | default: | ||
210 | break; | ||
211 | } | ||
212 | |||
213 | set_power_timers(pmc_pm_data.cpu_good_time, pmc_pm_data.cpu_off_time, | ||
214 | rate); | ||
215 | |||
216 | tegra_pmc_writel(reg, PMC_CTRL); | ||
217 | } | ||
218 | |||
219 | void tegra_pmc_suspend_init(void) | ||
220 | { | ||
221 | u32 reg; | ||
222 | |||
223 | /* Always enable CPU power request */ | ||
224 | reg = tegra_pmc_readl(PMC_CTRL); | ||
225 | reg |= TEGRA_POWER_CPU_PWRREQ_OE; | ||
226 | tegra_pmc_writel(reg, PMC_CTRL); | ||
227 | } | ||
228 | #endif | ||
229 | |||
136 | static const struct of_device_id matches[] __initconst = { | 230 | static const struct of_device_id matches[] __initconst = { |
137 | { .compatible = "nvidia,tegra114-pmc" }, | 231 | { .compatible = "nvidia,tegra114-pmc" }, |
138 | { .compatible = "nvidia,tegra30-pmc" }, | 232 | { .compatible = "nvidia,tegra30-pmc" }, |
@@ -143,6 +237,10 @@ static const struct of_device_id matches[] __initconst = { | |||
143 | static void tegra_pmc_parse_dt(void) | 237 | static void tegra_pmc_parse_dt(void) |
144 | { | 238 | { |
145 | struct device_node *np; | 239 | struct device_node *np; |
240 | u32 prop; | ||
241 | enum tegra_suspend_mode suspend_mode; | ||
242 | u32 core_good_time[2] = {0, 0}; | ||
243 | u32 lp0_vec[2] = {0, 0}; | ||
146 | 244 | ||
147 | np = of_find_matching_node(NULL, matches); | 245 | np = of_find_matching_node(NULL, matches); |
148 | BUG_ON(!np); | 246 | BUG_ON(!np); |
@@ -151,6 +249,70 @@ static void tegra_pmc_parse_dt(void) | |||
151 | 249 | ||
152 | tegra_pmc_invert_interrupt = of_property_read_bool(np, | 250 | tegra_pmc_invert_interrupt = of_property_read_bool(np, |
153 | "nvidia,invert-interrupt"); | 251 | "nvidia,invert-interrupt"); |
252 | tegra_pclk = of_clk_get_by_name(np, "pclk"); | ||
253 | WARN_ON(IS_ERR(tegra_pclk)); | ||
254 | |||
255 | /* Grabbing the power management configurations */ | ||
256 | if (of_property_read_u32(np, "nvidia,suspend-mode", &prop)) { | ||
257 | suspend_mode = TEGRA_SUSPEND_NONE; | ||
258 | } else { | ||
259 | switch (prop) { | ||
260 | case 0: | ||
261 | suspend_mode = TEGRA_SUSPEND_LP0; | ||
262 | break; | ||
263 | case 1: | ||
264 | suspend_mode = TEGRA_SUSPEND_LP1; | ||
265 | break; | ||
266 | case 2: | ||
267 | suspend_mode = TEGRA_SUSPEND_LP2; | ||
268 | break; | ||
269 | default: | ||
270 | suspend_mode = TEGRA_SUSPEND_NONE; | ||
271 | break; | ||
272 | } | ||
273 | } | ||
274 | suspend_mode = tegra_pm_validate_suspend_mode(suspend_mode); | ||
275 | |||
276 | if (of_property_read_u32(np, "nvidia,cpu-pwr-good-time", &prop)) | ||
277 | suspend_mode = TEGRA_SUSPEND_NONE; | ||
278 | pmc_pm_data.cpu_good_time = prop; | ||
279 | |||
280 | if (of_property_read_u32(np, "nvidia,cpu-pwr-off-time", &prop)) | ||
281 | suspend_mode = TEGRA_SUSPEND_NONE; | ||
282 | pmc_pm_data.cpu_off_time = prop; | ||
283 | |||
284 | if (of_property_read_u32_array(np, "nvidia,core-pwr-good-time", | ||
285 | core_good_time, ARRAY_SIZE(core_good_time))) | ||
286 | suspend_mode = TEGRA_SUSPEND_NONE; | ||
287 | pmc_pm_data.core_osc_time = core_good_time[0]; | ||
288 | pmc_pm_data.core_pmu_time = core_good_time[1]; | ||
289 | |||
290 | if (of_property_read_u32(np, "nvidia,core-pwr-off-time", | ||
291 | &prop)) | ||
292 | suspend_mode = TEGRA_SUSPEND_NONE; | ||
293 | pmc_pm_data.core_off_time = prop; | ||
294 | |||
295 | pmc_pm_data.corereq_high = of_property_read_bool(np, | ||
296 | "nvidia,core-power-req-active-high"); | ||
297 | |||
298 | pmc_pm_data.sysclkreq_high = of_property_read_bool(np, | ||
299 | "nvidia,sys-clock-req-active-high"); | ||
300 | |||
301 | pmc_pm_data.combined_req = of_property_read_bool(np, | ||
302 | "nvidia,combined-power-req"); | ||
303 | |||
304 | pmc_pm_data.cpu_pwr_good_en = of_property_read_bool(np, | ||
305 | "nvidia,cpu-pwr-good-en"); | ||
306 | |||
307 | if (of_property_read_u32_array(np, "nvidia,lp0-vec", lp0_vec, | ||
308 | ARRAY_SIZE(lp0_vec))) | ||
309 | if (suspend_mode == TEGRA_SUSPEND_LP0) | ||
310 | suspend_mode = TEGRA_SUSPEND_LP1; | ||
311 | |||
312 | pmc_pm_data.lp0_vec_phy_addr = lp0_vec[0]; | ||
313 | pmc_pm_data.lp0_vec_size = lp0_vec[1]; | ||
314 | |||
315 | pmc_pm_data.suspend_mode = suspend_mode; | ||
154 | } | 316 | } |
155 | 317 | ||
156 | void __init tegra_pmc_init(void) | 318 | void __init tegra_pmc_init(void) |