summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarthik Ramakrishnan <karthikr@nvidia.com>2012-08-13 19:09:16 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2013-09-14 12:33:06 -0700
commit5d251728785fbf4a34e98b78d07168a0241d5b6c (patch)
tree39a3e2d21cb76b86c12d2150e7115ec2d137b90c
parentf7f40b5fd389527b7902c2989fc192a057568bf4 (diff)
arm: tegra: Set Core to 0.95V in LP1
When the device prepares for LP1, the Core voltage is set to the highest value(1.2V for Enterprise and Kai, and 1.3V for AP37 and Cardhu). This is to set for some of the driver suspend along the sequence need a higher emc frequency and thus a higher Core voltage. Since the sequence of drivers suspend depends on the sequence of their registration in the table, which in turn is platform-dependent, there is no right place in the LP1 entry path where the Core voltage can be set to a lower voltage. Hence, the Core voltage remains high in LP1 resulting in higher power. Thus, the only safe location where the Core voltage can be lowered is once all the drivers are suspended and the DRAM is set in self-refresh, at the final point just before the system is suspended in the IRAM code. This location at the assembly code ensures that no other module will be running and thus that nothing will require a higher core voltage. The Core is set to the lowest possible value since nothing requires it. It is then restored to the highest voltage as soon as the LP1 resume code is started so that all drivers are resumed safely. At the execution point in IRAM during LP1 suspend path, even the I2C clocks are gated. They must be reset first and then the I2C transaction is performed. An I2C transaction involves 4 bytes of data, to send the slave address, the Core voltage register address and 2 bytes of data which has the value to set the voltage(the second byte is not required for this transaction). Once these registers are set, the I2C transaction is performed by setting the I2C transaction register to 0xA02. After sending the I2C transaction, we wait for about 250us to check the status of the transaction and if not updated, wait for more time to check again. If after 2ms and the transaction fails to register, the transaction is aborted and the device is allowed to enter at high voltage. Since the failure rate of I2C transaction is very low at this point in execution where there will be no conflicts in the bus, it is okay to have Core high for some of the LP1 cycles. However, it is unacceptable for the I2C transaction to fail on the way from LP1 resume since the device cannot come up with a lower Core voltage. In this case, the transaction is retried again and again till it is successful. There is no way but to keep trying as the device would fail to resume with Core at 0.95V. Each platform(or each PMU) has different values for the I2C transaction ie. slave address, Core voltage register and the value to set the voltage. For the device in IRAM, it cannot access anything in SDRAM memory, these values needs to be pushed to IRAM memory before the device starts execution in IRAM. This is done during initialization of suspend code when it picks values from the board files and copies it to IRAM part of code, before the whole memory is copied to IRAM. This new feature is controlled by a KConfig variable TEGRA_LP1_950 which should be enabled once the board file of the device is updated with the right values. The device hangs when it does not have the right values for the I2C transaction. With this change in Core, LP1 power is reduced by 12mW in Enterprise, 20mW in AP37 and about 24mW in Kai. Bug 1035684 Change-Id: I4318c66fd70ab227ef0786d6a13286e020e4541d Signed-off-by: Karthik Ramakrishnan <karthikr@nvidia.com> (cherry picked from commit ab476f287376fd0ae51a9f298659f5eba19f0296) Reviewed-on: http://git-master/r/124779 Reviewed-by: Lokesh Pathak <lpathak@nvidia.com> Tested-by: Lokesh Pathak <lpathak@nvidia.com> Rebase-Id: Re0625362698d402125337251c6b8337c2b2eb52d
-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-t30.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 a81944ec183a..f95fcaea20a4 100644
--- a/arch/arm/mach-tegra/Kconfig
+++ b/arch/arm/mach-tegra/Kconfig
@@ -411,4 +411,11 @@ config ARCH_TEGRA_4GB_MEMORY
help
Harmless to select this even if hardware does not support full
4GB physical memory.
+
+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
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index 9f6d08859387..78572d12a5c1 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -1147,6 +1147,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 46ffd3a473f4..259c31a53f29 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-t30.S b/arch/arm/mach-tegra/sleep-t30.S
index 817b85bd9108..8071112bfd7a 100644
--- a/arch/arm/mach-tegra/sleep-t30.S
+++ b/arch/arm/mach-tegra/sleep-t30.S
@@ -87,6 +87,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)
@@ -395,6 +405,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]
@@ -539,6 +609,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
*
@@ -573,9 +658,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 c658e6589f65..43afbd03c6ee 100644
--- a/arch/arm/mach-tegra/sleep.h
+++ b/arch/arm/mach-tegra/sleep.h
@@ -252,6 +252,10 @@ int tegra2_finish_sleep_cpu_secondary(unsigned long int);
#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;
int tegra3_sleep_core_finish(unsigned long int);
int tegra3_sleep_cpu_secondary_finish(unsigned long int);
#endif
@@ -273,5 +277,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