summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/sleep-t3.S
diff options
context:
space:
mode:
authorKarthik Ramakrishnan <karthikr@nvidia.com>2012-08-13 19:09:16 -0700
committerLokesh Pathak <lpathak@nvidia.com>2012-08-29 07:16:07 -0700
commitfee255cde01510e139d02b87473786271d1081ae (patch)
tree14449f2f3af07f9811df3a41c5252eb08f089e0c /arch/arm/mach-tegra/sleep-t3.S
parent7162d051308f53063470412cb2955f12e1f56a1c (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>
Diffstat (limited to 'arch/arm/mach-tegra/sleep-t3.S')
-rw-r--r--arch/arm/mach-tegra/sleep-t3.S150
1 files changed, 149 insertions, 1 deletions
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]