Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4a2e3279 authored by Joseph Lo's avatar Joseph Lo Committed by Stephen Warren
Browse files

clk: tegra20: Implementing CPU low-power function for tegra_cpu_car_ops



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: default avatarJoseph Lo <josephl@nvidia.com>
Signed-off-by: default avatarStephen Warren <swarren@nvidia.com>
parent 5c1350bd
Loading
Loading
Loading
Loading
+93 −0
Original line number Diff line number Diff line
@@ -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"

@@ -104,6 +105,13 @@
#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
@@ -169,6 +177,17 @@
#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[] = {