summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/Kconfig7
-rw-r--r--arch/arm/mach-tegra/pm.c15
-rw-r--r--arch/arm/mach-tegra/pm.h8
-rw-r--r--arch/arm/mach-tegra/sleep-t3.S150
-rw-r--r--arch/arm/mach-tegra/sleep.h40
5 files changed, 219 insertions, 1 deletions
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig
index b9627372564f..422ad20bb094 100644
--- a/arch/arm/mach-tegra/Kconfig
+++ b/arch/arm/mach-tegra/Kconfig
@@ -577,6 +577,13 @@ config TEGRA_SKIN_THROTTLE
help
Enable throttling to control the temperature of the skin/case
of the device.
+
+config TEGRA_LP1_950
+ bool "LP1 low core voltage"
+ default n
+ depends on ARCH_TEGRA_3x_SOC
+ help
+ Enable support for LP1 Core voltage to set to lowest
endif
config TEGRA_DC_USE_HW_BPP
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index d251e57626aa..2cdfdfe6f43d 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -1186,6 +1186,21 @@ out:
plat->suspend_mode = TEGRA_SUSPEND_LP2;
}
+#ifdef CONFIG_TEGRA_LP1_950
+ if (pdata->lp1_lowvolt_support) {
+ u32 lp1_core_lowvolt, lp1_core_highvolt;
+ memcpy(tegra_lp1_register_pmuslave_addr(), &pdata->pmuslave_addr, 4);
+ memcpy(tegra_lp1_register_i2c_base_addr(), &pdata->i2c_base_addr, 4);
+
+ lp1_core_lowvolt = 0;
+ lp1_core_lowvolt = (pdata->lp1_core_volt_low << 8) | pdata->core_reg_addr;
+ memcpy(tegra_lp1_register_core_lowvolt(), &lp1_core_lowvolt, 4);
+
+ lp1_core_highvolt = 0;
+ lp1_core_highvolt = (pdata->lp1_core_volt_high << 8) | pdata->core_reg_addr;
+ memcpy(tegra_lp1_register_core_highvolt(), &lp1_core_highvolt, 4);
+ }
+#endif
/* !!!FIXME!!! THIS IS TEGRA2 ONLY */
/* Initialize scratch registers used for CPU LP2 synchronization */
writel(0, pmc + PMC_SCRATCH37);
diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h
index b78e9b1abc00..5ea2b7f843a2 100644
--- a/arch/arm/mach-tegra/pm.h
+++ b/arch/arm/mach-tegra/pm.h
@@ -65,6 +65,14 @@ struct tegra_suspend_platform_data {
/* lp_state = 0 for LP0 state, 1 for LP1 state, 2 for LP2 state */
void (*board_resume)(int lp_state, enum resume_stage stg);
unsigned int cpu_resume_boost; /* CPU frequency resume boost in kHz */
+#ifdef CONFIG_TEGRA_LP1_950
+ bool lp1_lowvolt_support;
+ unsigned int i2c_base_addr;
+ unsigned int pmuslave_addr;
+ unsigned int core_reg_addr;
+ unsigned int lp1_core_volt_low;
+ unsigned int lp1_core_volt_high;
+#endif
};
/* clears io dpd settings before kernel code */
diff --git a/arch/arm/mach-tegra/sleep-t3.S b/arch/arm/mach-tegra/sleep-t3.S
index 23e96c605b96..4ee73d581a08 100644
--- a/arch/arm/mach-tegra/sleep-t3.S
+++ b/arch/arm/mach-tegra/sleep-t3.S
@@ -95,6 +95,16 @@
#define PMC_PLLM_WB0_OVERRIDE 0x1dc
#define CLK_RESET_CLK_SOURCE_MSELECT 0x3b4
+#define CLK_RESET_CLK_ENB_H_SET 0x328
+#define CLK_RESET_CLK_ENB_H_CLR 0x32c
+#define CLK_RESET_CLK_RST_DEV_H_SET 0x308
+#define CLK_RESET_CLK_RST_DEV_H_CLR 0x30c
+
+#define I2C_CNFG 0x0
+#define I2C_ADDR0 0x4
+#define I2C_DATA1 0xc
+#define I2C_DATA2 0x10
+#define I2C_STATUS 0x1c
#define MSELECT_CLKM (0x3 << 30)
@@ -348,6 +358,66 @@ ENTRY(tegra3_lp1_reset)
mov32 r4, ((1<<28) | (8)) @ burst policy is PLLX
str r4, [r0, #CLK_RESET_CCLK_BURST]
+#ifdef CONFIG_TEGRA_LP1_950
+lp1_voltset:
+ /* Restore the Core voltage to high on LP1 resume */
+ /* Reset(Enable/Disable) the DVC-I2C Controller*/
+ mov r1, #(1 << 15)
+ str r1, [r0, #CLK_RESET_CLK_RST_DEV_H_SET]
+
+ /* Wait for 2us */
+ mov32 r7, TEGRA_TMRUS_BASE
+ wait_for_us r1, r7, r9
+ add r1, r1, #2
+ wait_until r1, r7, r9
+
+ mov r1, #(1 << 15)
+ str r1, [r0, #CLK_RESET_CLK_RST_DEV_H_CLR]
+
+ /* Enable the DVC-I2C Controller */
+ mov r1, #(1 << 15)
+ str r1, [r0, #CLK_RESET_CLK_ENB_H_SET]
+
+
+ /* Same I2C transaction protocol as suspend */
+ ldr r1, lp1_register_pmuslave_addr
+ cmp r1, #0
+ beq lp1_voltskip_resume
+
+ ldr r4, lp1_register_i2c_base_addr
+ str r1, [r4, #I2C_ADDR0]
+
+ mov32 r1, 0x2
+ str r1, [r4, #I2C_CNFG]
+
+ ldr r1, lp1_register_core_highvolt
+ str r1, [r4, #I2C_DATA1]
+
+ mov32 r1, 0
+ str r1, [r4, #I2C_DATA2]
+
+ mov32 r1, 0xA02
+ str r1, [r4, #I2C_CNFG]
+
+ wait_for_us r1, r7, r9
+ mov32 r3, 0x7D0 /* Wait for 2ms and try transaction again */
+ add r0, r1, r3
+loop_i2c_status_resume:
+ add r1, r1, #0xFA /* Check status every 250us */
+ wait_until r1, r7, r9
+ cmp r0, r1
+ beq lp1_voltset
+
+ ldr r3, [r4, #I2C_STATUS]
+ cmp r3, #0
+ bne loop_i2c_status_resume
+
+lp1_voltskip_resume:
+ /* Disable the DVC-I2C Controller */
+ mov r0, #(1 << 15)
+ str r0, [r5, #CLK_RESET_CLK_ENB_H_CLR]
+#endif
+
#if defined (CONFIG_CACHE_L2X0)
/* power up L2 */
ldr r0, [r2, #PMC_PWRGATE_STATUS]
@@ -492,6 +562,21 @@ tegra3_sdram_pad_address:
tegra3_sdram_pad_size:
.word tegra3_sdram_pad_address - tegra3_sdram_pad_save
+#ifdef CONFIG_TEGRA_LP1_950
+ .globl lp1_register_pmuslave_addr
+ .globl lp1_register_i2c_base_addr
+ .globl lp1_register_core_lowvolt
+ .globl lp1_register_core_highvolt
+lp1_register_pmuslave_addr:
+ .word 0
+lp1_register_i2c_base_addr:
+ .word 0
+lp1_register_core_lowvolt:
+ .word 0
+lp1_register_core_highvolt:
+ .word 0
+#endif
+
/*
* tegra3_tear_down_core
*
@@ -526,9 +611,72 @@ tegra3_cpu_clk32k:
str r0, [r4, #PMC_PLLM_WB0_OVERRIDE]
mov pc, lr
+lp1_clocks_prepare:
+ /* Prepare to set the Core to the lowest voltage if supported.
+ * Start by setting the I2C clocks to make the I2C transfer */
+#ifdef CONFIG_TEGRA_LP1_950
+ /* Set up the PWR I2C GPIOs with the right masks*/
+
+ /* Reset(Set/Clr) the DVC-I2C Controller*/
+ mov r0, #(1 << 15)
+ str r0, [r5, #CLK_RESET_CLK_RST_DEV_H_SET]
+
+ /* Wait for 2us */
+ wait_for_us r1, r7, r9
+ mov32 r0, 0x7D0
+ add r1, r1, r0
+ wait_until r1, r7, r9
+
+ mov r0, #(1 << 15)
+ str r0, [r5, #CLK_RESET_CLK_RST_DEV_H_CLR]
+
+ /* Enable the DVC-I2C Controller */
+ mov r0, #(1 << 15)
+ str r0, [r5, #CLK_RESET_CLK_ENB_H_SET]
+
+ /* I2C transfer protocol:
+ * 4 packets: Slaveaddr + WriteConfigure + Data1 + Data2 */
+ ldr r0, lp1_register_pmuslave_addr
+ cmp r0, #0
+ beq lp1_volt_skip
+ ldr r1, lp1_register_i2c_base_addr
+ str r0, [r1, #I2C_ADDR0]
+
+ mov32 r0, 0x2
+ str r0, [r1, #I2C_CNFG]
+
+ ldr r0, lp1_register_core_lowvolt
+ str r0, [r1, #I2C_DATA1]
+
+ mov32 r0, 0
+ str r0, [r1, #I2C_DATA2]
+
+ /* Send I2C transaction */
+ mov32 r0, 0xA02
+ str r0, [r1, #I2C_CNFG]
+
+ /* Check the transaction status before proceeding */
+ wait_for_us r2, r7, r9
+ mov32 r3, 0x7D0 /* Wait for 2ms for I2C transaction */
+ add r3, r2, r3
+loop_i2c_status_suspend:
+ add r2, r2, #0xFA /* Check status every 250us */
+ cmp r3, r2
+ beq lp1_volt_skip /* Waited for 2ms, I2C transaction didn't take place */
+ wait_until r2, r7, r9
+
+ ldr r0, [r1, #I2C_STATUS]
+ cmp r0, #0
+ bne loop_i2c_status_suspend
+lp1_volt_skip:
+
+ /* Disable the DVC-I2C Controller */
+ mov r0, #(1 << 15)
+ str r0, [r5, #CLK_RESET_CLK_ENB_H_CLR]
+
+#endif
/* start by jumping to clkm to safely disable PLLs, then jump
* to clks */
-lp1_clocks_prepare:
mov r0, #(1 << 28)
str r0, [r5, #CLK_RESET_SCLK_BURST]
str r0, [r5, #CLK_RESET_CCLK_BURST]
diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h
index c57399985ecd..48b2bb8cfdb2 100644
--- a/arch/arm/mach-tegra/sleep.h
+++ b/arch/arm/mach-tegra/sleep.h
@@ -223,6 +223,10 @@ void tegra2_sleep_wfi(unsigned long v2p);
#else
extern unsigned int tegra3_iram_start;
extern unsigned int tegra3_iram_end;
+extern unsigned int lp1_register_pmuslave_addr;
+extern unsigned int lp1_register_i2c_base_addr;
+extern unsigned int lp1_register_core_lowvolt;
+extern unsigned int lp1_register_core_highvolt;
void tegra3_sleep_core(unsigned long v2p);
void tegra3_sleep_cpu_secondary(unsigned long v2p);
void tegra3_hotplug_shutdown(void);
@@ -245,5 +249,41 @@ static inline void *tegra_iram_end(void)
return &tegra3_iram_end;
#endif
}
+
+static inline void *tegra_lp1_register_pmuslave_addr(void)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ return NULL;
+#else
+ return &lp1_register_pmuslave_addr;
+#endif
+}
+
+static inline void *tegra_lp1_register_i2c_base_addr(void)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ return NULL;
+#else
+ return &lp1_register_i2c_base_addr;
+#endif
+}
+
+static inline void *tegra_lp1_register_core_lowvolt(void)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ return NULL;
+#else
+ return &lp1_register_core_lowvolt;
+#endif
+}
+
+static inline void *tegra_lp1_register_core_highvolt(void)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ return NULL;
+#else
+ return &lp1_register_core_highvolt;
+#endif
+}
#endif
#endif