diff options
Diffstat (limited to 'arch/arm/mach-tegra/tegra30_clocks.c')
-rw-r--r-- | arch/arm/mach-tegra/tegra30_clocks.c | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/tegra30_clocks.c b/arch/arm/mach-tegra/tegra30_clocks.c index 000239d68393..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 | */ |
@@ -791,6 +818,112 @@ struct clk_ops tegra30_twd_ops = { | |||
791 | .recalc_rate = tegra30_twd_clk_recalc_rate, | 818 | .recalc_rate = tegra30_twd_clk_recalc_rate, |
792 | }; | 819 | }; |
793 | 820 | ||
821 | /* bus clock functions */ | ||
822 | static int tegra30_bus_clk_is_enabled(struct clk_hw *hw) | ||
823 | { | ||
824 | struct clk_tegra *c = to_clk_tegra(hw); | ||
825 | u32 val = clk_readl(c->reg); | ||
826 | |||
827 | c->state = ((val >> c->reg_shift) & BUS_CLK_DISABLE) ? OFF : ON; | ||
828 | return c->state; | ||
829 | } | ||
830 | |||
831 | static int tegra30_bus_clk_enable(struct clk_hw *hw) | ||
832 | { | ||
833 | struct clk_tegra *c = to_clk_tegra(hw); | ||
834 | u32 val; | ||
835 | |||
836 | val = clk_readl(c->reg); | ||
837 | val &= ~(BUS_CLK_DISABLE << c->reg_shift); | ||
838 | clk_writel(val, c->reg); | ||
839 | |||
840 | return 0; | ||
841 | } | ||
842 | |||
843 | static void tegra30_bus_clk_disable(struct clk_hw *hw) | ||
844 | { | ||
845 | struct clk_tegra *c = to_clk_tegra(hw); | ||
846 | u32 val; | ||
847 | |||
848 | val = clk_readl(c->reg); | ||
849 | val |= BUS_CLK_DISABLE << c->reg_shift; | ||
850 | clk_writel(val, c->reg); | ||
851 | } | ||
852 | |||
853 | static unsigned long tegra30_bus_clk_recalc_rate(struct clk_hw *hw, | ||
854 | unsigned long prate) | ||
855 | { | ||
856 | struct clk_tegra *c = to_clk_tegra(hw); | ||
857 | u32 val = clk_readl(c->reg); | ||
858 | u64 rate = prate; | ||
859 | |||
860 | c->div = ((val >> c->reg_shift) & BUS_CLK_DIV_MASK) + 1; | ||
861 | c->mul = 1; | ||
862 | |||
863 | if (c->mul != 0 && c->div != 0) { | ||
864 | rate *= c->mul; | ||
865 | rate += c->div - 1; /* round up */ | ||
866 | do_div(rate, c->div); | ||
867 | } | ||
868 | return rate; | ||
869 | } | ||
870 | |||
871 | static int tegra30_bus_clk_set_rate(struct clk_hw *hw, unsigned long rate, | ||
872 | unsigned long parent_rate) | ||
873 | { | ||
874 | struct clk_tegra *c = to_clk_tegra(hw); | ||
875 | int ret = -EINVAL; | ||
876 | u32 val; | ||
877 | int i; | ||
878 | |||
879 | val = clk_readl(c->reg); | ||
880 | for (i = 1; i <= 4; i++) { | ||
881 | if (rate == parent_rate / i) { | ||
882 | val &= ~(BUS_CLK_DIV_MASK << c->reg_shift); | ||
883 | val |= (i - 1) << c->reg_shift; | ||
884 | clk_writel(val, c->reg); | ||
885 | c->div = i; | ||
886 | c->mul = 1; | ||
887 | ret = 0; | ||
888 | break; | ||
889 | } | ||
890 | } | ||
891 | |||
892 | return ret; | ||
893 | } | ||
894 | |||
895 | static long tegra30_bus_clk_round_rate(struct clk_hw *hw, unsigned long rate, | ||
896 | unsigned long *prate) | ||
897 | { | ||
898 | unsigned long parent_rate = *prate; | ||
899 | s64 divider; | ||
900 | |||
901 | if (rate >= parent_rate) | ||
902 | return parent_rate; | ||
903 | |||
904 | divider = parent_rate; | ||
905 | divider += rate - 1; | ||
906 | do_div(divider, rate); | ||
907 | |||
908 | if (divider < 0) | ||
909 | return divider; | ||
910 | |||
911 | if (divider > 4) | ||
912 | divider = 4; | ||
913 | do_div(parent_rate, divider); | ||
914 | |||
915 | return parent_rate; | ||
916 | } | ||
917 | |||
918 | struct clk_ops tegra30_bus_ops = { | ||
919 | .is_enabled = tegra30_bus_clk_is_enabled, | ||
920 | .enable = tegra30_bus_clk_enable, | ||
921 | .disable = tegra30_bus_clk_disable, | ||
922 | .set_rate = tegra30_bus_clk_set_rate, | ||
923 | .round_rate = tegra30_bus_clk_round_rate, | ||
924 | .recalc_rate = tegra30_bus_clk_recalc_rate, | ||
925 | }; | ||
926 | |||
794 | /* Blink output functions */ | 927 | /* Blink output functions */ |
795 | static int tegra30_blink_clk_is_enabled(struct clk_hw *hw) | 928 | static int tegra30_blink_clk_is_enabled(struct clk_hw *hw) |
796 | { | 929 | { |
@@ -2280,12 +2413,93 @@ static void tegra30_disable_cpu_clock(u32 cpu) | |||
2280 | reg_clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX); | 2413 | reg_clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX); |
2281 | } | 2414 | } |
2282 | 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 | |||
2283 | static struct tegra_cpu_car_ops tegra30_cpu_car_ops = { | 2492 | static struct tegra_cpu_car_ops tegra30_cpu_car_ops = { |
2284 | .wait_for_reset = tegra30_wait_cpu_in_reset, | 2493 | .wait_for_reset = tegra30_wait_cpu_in_reset, |
2285 | .put_in_reset = tegra30_put_cpu_in_reset, | 2494 | .put_in_reset = tegra30_put_cpu_in_reset, |
2286 | .out_of_reset = tegra30_cpu_out_of_reset, | 2495 | .out_of_reset = tegra30_cpu_out_of_reset, |
2287 | .enable_clock = tegra30_enable_cpu_clock, | 2496 | .enable_clock = tegra30_enable_cpu_clock, |
2288 | .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 | ||
2289 | }; | 2503 | }; |
2290 | 2504 | ||
2291 | void __init tegra30_cpu_car_ops_init(void) | 2505 | void __init tegra30_cpu_car_ops_init(void) |