diff options
author | Karthik Ramakrishnan <karthikr@nvidia.com> | 2012-08-13 19:09:16 -0700 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2013-09-14 12:33:06 -0700 |
commit | 5d251728785fbf4a34e98b78d07168a0241d5b6c (patch) | |
tree | 39a3e2d21cb76b86c12d2150e7115ec2d137b90c | |
parent | f7f40b5fd389527b7902c2989fc192a057568bf4 (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/Kconfig | 7 | ||||
-rw-r--r-- | arch/arm/mach-tegra/pm.c | 15 | ||||
-rw-r--r-- | arch/arm/mach-tegra/pm.h | 8 | ||||
-rw-r--r-- | arch/arm/mach-tegra/sleep-t30.S | 150 | ||||
-rw-r--r-- | arch/arm/mach-tegra/sleep.h | 40 |
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 |