clk: tegra20: Implementing CPU low-power function for tegra_cpu_car_ops
authorJoseph Lo <josephl@nvidia.com>
Tue, 15 Jan 2013 22:10:48 +0000 (22:10 +0000)
committerStephen Warren <swarren@nvidia.com>
Mon, 28 Jan 2013 18:20:38 +0000 (11:20 -0700)
Implementing suspend, resume and rail_off_ready API for tegra_cpu_car_ops. These
functions were used for CPU powered-down state maintenance.

Signed-off-by: Joseph Lo <josephl@nvidia.com>
Signed-off-by: Stephen Warren <swarren@nvidia.com>
drivers/clk/tegra/clk-tegra20.c

index 077d5b9ffe22544e14791ad098525d7892c6e8f3..5d41569883a79534e6c49a40dfd5724635266403 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/clk/tegra.h>
+#include <linux/delay.h>
 
 #include "clk.h"
 
 #define SUPER_SCLK_DIVIDER 0x2c
 #define CLK_SYSTEM_RATE 0x30
 
+#define CCLK_BURST_POLICY_SHIFT        28
+#define CCLK_RUN_POLICY_SHIFT  4
+#define CCLK_IDLE_POLICY_SHIFT 0
+#define CCLK_IDLE_POLICY       1
+#define CCLK_RUN_POLICY                2
+#define CCLK_BURST_POLICY_PLLX 8
+
 #define CLK_SOURCE_I2S1 0x100
 #define CLK_SOURCE_I2S2 0x104
 #define CLK_SOURCE_SPDIF_OUT 0x108
 #define CPU_CLOCK(cpu) (0x1 << (8 + cpu))
 #define CPU_RESET(cpu) (0x1111ul << (cpu))
 
+#ifdef CONFIG_PM_SLEEP
+static struct cpu_clk_suspend_context {
+       u32 pllx_misc;
+       u32 pllx_base;
+
+       u32 cpu_burst;
+       u32 clk_csite_src;
+       u32 cclk_divider;
+} tegra20_cpu_clk_sctx;
+#endif
+
 static int periph_clk_enb_refcnt[CLK_OUT_ENB_NUM * 32];
 
 static void __iomem *clk_base;
@@ -1136,12 +1155,86 @@ static void tegra20_disable_cpu_clock(u32 cpu)
               clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
 }
 
+#ifdef CONFIG_PM_SLEEP
+static bool tegra20_cpu_rail_off_ready(void)
+{
+       unsigned int cpu_rst_status;
+
+       cpu_rst_status = readl(clk_base +
+                              TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
+
+       return !!(cpu_rst_status & 0x2);
+}
+
+static void tegra20_cpu_clock_suspend(void)
+{
+       /* switch coresite to clk_m, save off original source */
+       tegra20_cpu_clk_sctx.clk_csite_src =
+                               readl(clk_base + CLK_SOURCE_CSITE);
+       writel(3<<30, clk_base + CLK_SOURCE_CSITE);
+
+       tegra20_cpu_clk_sctx.cpu_burst =
+                               readl(clk_base + CCLK_BURST_POLICY);
+       tegra20_cpu_clk_sctx.pllx_base =
+                               readl(clk_base + PLLX_BASE);
+       tegra20_cpu_clk_sctx.pllx_misc =
+                               readl(clk_base + PLLX_MISC);
+       tegra20_cpu_clk_sctx.cclk_divider =
+                               readl(clk_base + SUPER_CCLK_DIVIDER);
+}
+
+static void tegra20_cpu_clock_resume(void)
+{
+       unsigned int reg, policy;
+
+       /* Is CPU complex already running on PLLX? */
+       reg = readl(clk_base + CCLK_BURST_POLICY);
+       policy = (reg >> CCLK_BURST_POLICY_SHIFT) & 0xF;
+
+       if (policy == CCLK_IDLE_POLICY)
+               reg = (reg >> CCLK_IDLE_POLICY_SHIFT) & 0xF;
+       else if (policy == CCLK_RUN_POLICY)
+               reg = (reg >> CCLK_RUN_POLICY_SHIFT) & 0xF;
+       else
+               BUG();
+
+       if (reg != CCLK_BURST_POLICY_PLLX) {
+               /* restore PLLX settings if CPU is on different PLL */
+               writel(tegra20_cpu_clk_sctx.pllx_misc,
+                                       clk_base + PLLX_MISC);
+               writel(tegra20_cpu_clk_sctx.pllx_base,
+                                       clk_base + PLLX_BASE);
+
+               /* wait for PLL stabilization if PLLX was enabled */
+               if (tegra20_cpu_clk_sctx.pllx_base & (1 << 30))
+                       udelay(300);
+       }
+
+       /*
+        * Restore original burst policy setting for calls resulting from CPU
+        * LP2 in idle or system suspend.
+        */
+       writel(tegra20_cpu_clk_sctx.cclk_divider,
+                                       clk_base + SUPER_CCLK_DIVIDER);
+       writel(tegra20_cpu_clk_sctx.cpu_burst,
+                                       clk_base + CCLK_BURST_POLICY);
+
+       writel(tegra20_cpu_clk_sctx.clk_csite_src,
+                                       clk_base + CLK_SOURCE_CSITE);
+}
+#endif
+
 static struct tegra_cpu_car_ops tegra20_cpu_car_ops = {
        .wait_for_reset = tegra20_wait_cpu_in_reset,
        .put_in_reset   = tegra20_put_cpu_in_reset,
        .out_of_reset   = tegra20_cpu_out_of_reset,
        .enable_clock   = tegra20_enable_cpu_clock,
        .disable_clock  = tegra20_disable_cpu_clock,
+#ifdef CONFIG_PM_SLEEP
+       .rail_off_ready = tegra20_cpu_rail_off_ready,
+       .suspend        = tegra20_cpu_clock_suspend,
+       .resume         = tegra20_cpu_clock_resume,
+#endif
 };
 
 static __initdata struct tegra_clk_init_table init_table[] = {
This page took 0.029323 seconds and 5 git commands to generate.