diff options
author | Joseph Lo <josephl@nvidia.com> | 2012-10-31 05:41:19 -0400 |
---|---|---|
committer | Stephen Warren <swarren@nvidia.com> | 2012-11-15 17:09:21 -0500 |
commit | a6e293eef2eafc31dbe008301182e7124bd87755 (patch) | |
tree | b4feec69bd092c576fc17584961d84a9c2e6dfe4 /arch/arm/mach-tegra | |
parent | fe508d776908b8512c6d936eb29e40bef1f4b8fc (diff) |
ARM: tegra30: clocks: add CPU low-power function into tegra_cpu_car_ops
Add suspend, resume and rail_off_ready API into tegra_cpu_car_ops. These
functions were used for CPU powered-down state maintenance. One thing
needs to notice the rail_off_ready API only availalbe for cpu_g cluster
not cpu_lp cluster.
Signed-off-by: Joseph Lo <josephl@nvidia.com>
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra')
-rw-r--r-- | arch/arm/mach-tegra/tegra30_clocks.c | 108 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra_cpu_car.h | 37 |
2 files changed, 145 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/tegra30_clocks.c b/arch/arm/mach-tegra/tegra30_clocks.c index f5b453f4bf4d..efc000e32e1c 100644 --- a/arch/arm/mach-tegra/tegra30_clocks.c +++ b/arch/arm/mach-tegra/tegra30_clocks.c | |||
@@ -31,6 +31,8 @@ | |||
31 | 31 | ||
32 | #include <asm/clkdev.h> | 32 | #include <asm/clkdev.h> |
33 | 33 | ||
34 | #include <mach/powergate.h> | ||
35 | |||
34 | #include "clock.h" | 36 | #include "clock.h" |
35 | #include "fuse.h" | 37 | #include "fuse.h" |
36 | #include "iomap.h" | 38 | #include "iomap.h" |
@@ -309,6 +311,31 @@ | |||
309 | #define CPU_CLOCK(cpu) (0x1 << (8 + cpu)) | 311 | #define CPU_CLOCK(cpu) (0x1 << (8 + cpu)) |
310 | #define CPU_RESET(cpu) (0x1111ul << (cpu)) | 312 | #define CPU_RESET(cpu) (0x1111ul << (cpu)) |
311 | 313 | ||
314 | #define CLK_RESET_CCLK_BURST 0x20 | ||
315 | #define CLK_RESET_CCLK_DIVIDER 0x24 | ||
316 | #define CLK_RESET_PLLX_BASE 0xe0 | ||
317 | #define CLK_RESET_PLLX_MISC 0xe4 | ||
318 | |||
319 | #define CLK_RESET_SOURCE_CSITE 0x1d4 | ||
320 | |||
321 | #define CLK_RESET_CCLK_BURST_POLICY_SHIFT 28 | ||
322 | #define CLK_RESET_CCLK_RUN_POLICY_SHIFT 4 | ||
323 | #define CLK_RESET_CCLK_IDLE_POLICY_SHIFT 0 | ||
324 | #define CLK_RESET_CCLK_IDLE_POLICY 1 | ||
325 | #define CLK_RESET_CCLK_RUN_POLICY 2 | ||
326 | #define CLK_RESET_CCLK_BURST_POLICY_PLLX 8 | ||
327 | |||
328 | #ifdef CONFIG_PM_SLEEP | ||
329 | static struct cpu_clk_suspend_context { | ||
330 | u32 pllx_misc; | ||
331 | u32 pllx_base; | ||
332 | |||
333 | u32 cpu_burst; | ||
334 | u32 clk_csite_src; | ||
335 | u32 cclk_divider; | ||
336 | } tegra30_cpu_clk_sctx; | ||
337 | #endif | ||
338 | |||
312 | /** | 339 | /** |
313 | * Structure defining the fields for USB UTMI clocks Parameters. | 340 | * Structure defining the fields for USB UTMI clocks Parameters. |
314 | */ | 341 | */ |
@@ -2386,12 +2413,93 @@ static void tegra30_disable_cpu_clock(u32 cpu) | |||
2386 | reg_clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX); | 2413 | reg_clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX); |
2387 | } | 2414 | } |
2388 | 2415 | ||
2416 | #ifdef CONFIG_PM_SLEEP | ||
2417 | static bool tegra30_cpu_rail_off_ready(void) | ||
2418 | { | ||
2419 | unsigned int cpu_rst_status; | ||
2420 | int cpu_pwr_status; | ||
2421 | |||
2422 | cpu_rst_status = readl(reg_clk_base + | ||
2423 | TEGRA30_CLK_RST_CONTROLLER_CPU_CMPLX_STATUS); | ||
2424 | cpu_pwr_status = tegra_powergate_is_powered(TEGRA_POWERGATE_CPU1) || | ||
2425 | tegra_powergate_is_powered(TEGRA_POWERGATE_CPU2) || | ||
2426 | tegra_powergate_is_powered(TEGRA_POWERGATE_CPU3); | ||
2427 | |||
2428 | if (((cpu_rst_status & 0xE) != 0xE) || cpu_pwr_status) | ||
2429 | return false; | ||
2430 | |||
2431 | return true; | ||
2432 | } | ||
2433 | |||
2434 | static void tegra30_cpu_clock_suspend(void) | ||
2435 | { | ||
2436 | /* switch coresite to clk_m, save off original source */ | ||
2437 | tegra30_cpu_clk_sctx.clk_csite_src = | ||
2438 | readl(reg_clk_base + CLK_RESET_SOURCE_CSITE); | ||
2439 | writel(3<<30, reg_clk_base + CLK_RESET_SOURCE_CSITE); | ||
2440 | |||
2441 | tegra30_cpu_clk_sctx.cpu_burst = | ||
2442 | readl(reg_clk_base + CLK_RESET_CCLK_BURST); | ||
2443 | tegra30_cpu_clk_sctx.pllx_base = | ||
2444 | readl(reg_clk_base + CLK_RESET_PLLX_BASE); | ||
2445 | tegra30_cpu_clk_sctx.pllx_misc = | ||
2446 | readl(reg_clk_base + CLK_RESET_PLLX_MISC); | ||
2447 | tegra30_cpu_clk_sctx.cclk_divider = | ||
2448 | readl(reg_clk_base + CLK_RESET_CCLK_DIVIDER); | ||
2449 | } | ||
2450 | |||
2451 | static void tegra30_cpu_clock_resume(void) | ||
2452 | { | ||
2453 | unsigned int reg, policy; | ||
2454 | |||
2455 | /* Is CPU complex already running on PLLX? */ | ||
2456 | reg = readl(reg_clk_base + CLK_RESET_CCLK_BURST); | ||
2457 | policy = (reg >> CLK_RESET_CCLK_BURST_POLICY_SHIFT) & 0xF; | ||
2458 | |||
2459 | if (policy == CLK_RESET_CCLK_IDLE_POLICY) | ||
2460 | reg = (reg >> CLK_RESET_CCLK_IDLE_POLICY_SHIFT) & 0xF; | ||
2461 | else if (policy == CLK_RESET_CCLK_RUN_POLICY) | ||
2462 | reg = (reg >> CLK_RESET_CCLK_RUN_POLICY_SHIFT) & 0xF; | ||
2463 | else | ||
2464 | BUG(); | ||
2465 | |||
2466 | if (reg != CLK_RESET_CCLK_BURST_POLICY_PLLX) { | ||
2467 | /* restore PLLX settings if CPU is on different PLL */ | ||
2468 | writel(tegra30_cpu_clk_sctx.pllx_misc, | ||
2469 | reg_clk_base + CLK_RESET_PLLX_MISC); | ||
2470 | writel(tegra30_cpu_clk_sctx.pllx_base, | ||
2471 | reg_clk_base + CLK_RESET_PLLX_BASE); | ||
2472 | |||
2473 | /* wait for PLL stabilization if PLLX was enabled */ | ||
2474 | if (tegra30_cpu_clk_sctx.pllx_base & (1 << 30)) | ||
2475 | udelay(300); | ||
2476 | } | ||
2477 | |||
2478 | /* | ||
2479 | * Restore original burst policy setting for calls resulting from CPU | ||
2480 | * LP2 in idle or system suspend. | ||
2481 | */ | ||
2482 | writel(tegra30_cpu_clk_sctx.cclk_divider, | ||
2483 | reg_clk_base + CLK_RESET_CCLK_DIVIDER); | ||
2484 | writel(tegra30_cpu_clk_sctx.cpu_burst, | ||
2485 | reg_clk_base + CLK_RESET_CCLK_BURST); | ||
2486 | |||
2487 | writel(tegra30_cpu_clk_sctx.clk_csite_src, | ||
2488 | reg_clk_base + CLK_RESET_SOURCE_CSITE); | ||
2489 | } | ||
2490 | #endif | ||
2491 | |||
2389 | static struct tegra_cpu_car_ops tegra30_cpu_car_ops = { | 2492 | static struct tegra_cpu_car_ops tegra30_cpu_car_ops = { |
2390 | .wait_for_reset = tegra30_wait_cpu_in_reset, | 2493 | .wait_for_reset = tegra30_wait_cpu_in_reset, |
2391 | .put_in_reset = tegra30_put_cpu_in_reset, | 2494 | .put_in_reset = tegra30_put_cpu_in_reset, |
2392 | .out_of_reset = tegra30_cpu_out_of_reset, | 2495 | .out_of_reset = tegra30_cpu_out_of_reset, |
2393 | .enable_clock = tegra30_enable_cpu_clock, | 2496 | .enable_clock = tegra30_enable_cpu_clock, |
2394 | .disable_clock = tegra30_disable_cpu_clock, | 2497 | .disable_clock = tegra30_disable_cpu_clock, |
2498 | #ifdef CONFIG_PM_SLEEP | ||
2499 | .rail_off_ready = tegra30_cpu_rail_off_ready, | ||
2500 | .suspend = tegra30_cpu_clock_suspend, | ||
2501 | .resume = tegra30_cpu_clock_resume, | ||
2502 | #endif | ||
2395 | }; | 2503 | }; |
2396 | 2504 | ||
2397 | void __init tegra30_cpu_car_ops_init(void) | 2505 | void __init tegra30_cpu_car_ops_init(void) |
diff --git a/arch/arm/mach-tegra/tegra_cpu_car.h b/arch/arm/mach-tegra/tegra_cpu_car.h index 30d063ad2bef..9764d31032b7 100644 --- a/arch/arm/mach-tegra/tegra_cpu_car.h +++ b/arch/arm/mach-tegra/tegra_cpu_car.h | |||
@@ -30,6 +30,12 @@ | |||
30 | * CPU clock un-gate | 30 | * CPU clock un-gate |
31 | * disable_clock: | 31 | * disable_clock: |
32 | * CPU clock gate | 32 | * CPU clock gate |
33 | * rail_off_ready: | ||
34 | * CPU is ready for rail off | ||
35 | * suspend: | ||
36 | * save the clock settings when CPU go into low-power state | ||
37 | * resume: | ||
38 | * restore the clock settings when CPU exit low-power state | ||
33 | */ | 39 | */ |
34 | struct tegra_cpu_car_ops { | 40 | struct tegra_cpu_car_ops { |
35 | void (*wait_for_reset)(u32 cpu); | 41 | void (*wait_for_reset)(u32 cpu); |
@@ -37,6 +43,11 @@ struct tegra_cpu_car_ops { | |||
37 | void (*out_of_reset)(u32 cpu); | 43 | void (*out_of_reset)(u32 cpu); |
38 | void (*enable_clock)(u32 cpu); | 44 | void (*enable_clock)(u32 cpu); |
39 | void (*disable_clock)(u32 cpu); | 45 | void (*disable_clock)(u32 cpu); |
46 | #ifdef CONFIG_PM_SLEEP | ||
47 | bool (*rail_off_ready)(void); | ||
48 | void (*suspend)(void); | ||
49 | void (*resume)(void); | ||
50 | #endif | ||
40 | }; | 51 | }; |
41 | 52 | ||
42 | extern struct tegra_cpu_car_ops *tegra_cpu_car_ops; | 53 | extern struct tegra_cpu_car_ops *tegra_cpu_car_ops; |
@@ -81,6 +92,32 @@ static inline void tegra_disable_cpu_clock(u32 cpu) | |||
81 | tegra_cpu_car_ops->disable_clock(cpu); | 92 | tegra_cpu_car_ops->disable_clock(cpu); |
82 | } | 93 | } |
83 | 94 | ||
95 | #ifdef CONFIG_PM_SLEEP | ||
96 | static inline bool tegra_cpu_rail_off_ready(void) | ||
97 | { | ||
98 | if (WARN_ON(!tegra_cpu_car_ops->rail_off_ready)) | ||
99 | return false; | ||
100 | |||
101 | return tegra_cpu_car_ops->rail_off_ready(); | ||
102 | } | ||
103 | |||
104 | static inline void tegra_cpu_clock_suspend(void) | ||
105 | { | ||
106 | if (WARN_ON(!tegra_cpu_car_ops->suspend)) | ||
107 | return; | ||
108 | |||
109 | tegra_cpu_car_ops->suspend(); | ||
110 | } | ||
111 | |||
112 | static inline void tegra_cpu_clock_resume(void) | ||
113 | { | ||
114 | if (WARN_ON(!tegra_cpu_car_ops->resume)) | ||
115 | return; | ||
116 | |||
117 | tegra_cpu_car_ops->resume(); | ||
118 | } | ||
119 | #endif | ||
120 | |||
84 | void tegra20_cpu_car_ops_init(void); | 121 | void tegra20_cpu_car_ops_init(void); |
85 | void tegra30_cpu_car_ops_init(void); | 122 | void tegra30_cpu_car_ops_init(void); |
86 | 123 | ||