forked from Minki/linux
d552920a02
This is a power gating idle mode. It support power gating vdd_cpu rail after all cpu cores in "powered-down" status. For Tegra30, the CPU0 can enter this state only when all secondary CPU is offline. We need to take care and make sure whole secondary CPUs were offline and checking the CPU power gate status. After that, the CPU0 can go into "powered-down" state safely. Then shut off the CPU rail. Be aware of that, you may see the legacy power state "LP2" in the code which is exactly the same meaning of "CPU power down". Base on the work by: Scott Williams <scwilliams@nvidia.com> Signed-off-by: Joseph Lo <josephl@nvidia.com> Signed-off-by: Stephen Warren <swarren@nvidia.com>
172 lines
4.2 KiB
ArmAsm
172 lines
4.2 KiB
ArmAsm
/*
|
|
* Copyright (c) 2012, NVIDIA Corporation. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <linux/linkage.h>
|
|
|
|
#include <asm/assembler.h>
|
|
#include <asm/asm-offsets.h>
|
|
|
|
#include "sleep.h"
|
|
#include "flowctrl.h"
|
|
|
|
#define TEGRA30_POWER_HOTPLUG_SHUTDOWN (1 << 27) /* Hotplug shutdown */
|
|
|
|
#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP)
|
|
/*
|
|
* tegra30_hotplug_shutdown(void)
|
|
*
|
|
* Powergates the current CPU.
|
|
* Should never return.
|
|
*/
|
|
ENTRY(tegra30_hotplug_shutdown)
|
|
/* Turn off SMP coherency */
|
|
exit_smp r4, r5
|
|
|
|
/* Powergate this CPU */
|
|
mov r0, #TEGRA30_POWER_HOTPLUG_SHUTDOWN
|
|
bl tegra30_cpu_shutdown
|
|
mov pc, lr @ should never get here
|
|
ENDPROC(tegra30_hotplug_shutdown)
|
|
|
|
/*
|
|
* tegra30_cpu_shutdown(unsigned long flags)
|
|
*
|
|
* Puts the current CPU in wait-for-event mode on the flow controller
|
|
* and powergates it -- flags (in R0) indicate the request type.
|
|
* Must never be called for CPU 0.
|
|
*
|
|
* corrupts r0-r4, r12
|
|
*/
|
|
ENTRY(tegra30_cpu_shutdown)
|
|
cpu_id r3
|
|
cmp r3, #0
|
|
moveq pc, lr @ Must never be called for CPU 0
|
|
|
|
ldr r12, =TEGRA_FLOW_CTRL_VIRT
|
|
cpu_to_csr_reg r1, r3
|
|
add r1, r1, r12 @ virtual CSR address for this CPU
|
|
cpu_to_halt_reg r2, r3
|
|
add r2, r2, r12 @ virtual HALT_EVENTS address for this CPU
|
|
|
|
/*
|
|
* Clear this CPU's "event" and "interrupt" flags and power gate
|
|
* it when halting but not before it is in the "WFE" state.
|
|
*/
|
|
movw r12, \
|
|
FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG | \
|
|
FLOW_CTRL_CSR_ENABLE
|
|
mov r4, #(1 << 4)
|
|
orr r12, r12, r4, lsl r3
|
|
str r12, [r1]
|
|
|
|
/* Halt this CPU. */
|
|
mov r3, #0x400
|
|
delay_1:
|
|
subs r3, r3, #1 @ delay as a part of wfe war.
|
|
bge delay_1;
|
|
cpsid a @ disable imprecise aborts.
|
|
ldr r3, [r1] @ read CSR
|
|
str r3, [r1] @ clear CSR
|
|
tst r0, #TEGRA30_POWER_HOTPLUG_SHUTDOWN
|
|
moveq r3, #FLOW_CTRL_WAIT_FOR_INTERRUPT @ For LP2
|
|
movne r3, #FLOW_CTRL_WAITEVENT @ For hotplug
|
|
str r3, [r2]
|
|
ldr r0, [r2]
|
|
b wfe_war
|
|
|
|
__cpu_reset_again:
|
|
dsb
|
|
.align 5
|
|
wfe @ CPU should be power gated here
|
|
wfe_war:
|
|
b __cpu_reset_again
|
|
|
|
/*
|
|
* 38 nop's, which fills reset of wfe cache line and
|
|
* 4 more cachelines with nop
|
|
*/
|
|
.rept 38
|
|
nop
|
|
.endr
|
|
b . @ should never get here
|
|
|
|
ENDPROC(tegra30_cpu_shutdown)
|
|
#endif
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
/*
|
|
* tegra30_sleep_cpu_secondary_finish(unsigned long v2p)
|
|
*
|
|
* Enters LP2 on secondary CPU by exiting coherency and powergating the CPU.
|
|
*/
|
|
ENTRY(tegra30_sleep_cpu_secondary_finish)
|
|
mov r7, lr
|
|
|
|
/* Flush and disable the L1 data cache */
|
|
bl tegra_disable_clean_inv_dcache
|
|
|
|
/* Powergate this CPU. */
|
|
mov r0, #0 @ power mode flags (!hotplug)
|
|
bl tegra30_cpu_shutdown
|
|
mov r0, #1 @ never return here
|
|
mov pc, r7
|
|
ENDPROC(tegra30_sleep_cpu_secondary_finish)
|
|
|
|
/*
|
|
* tegra30_tear_down_cpu
|
|
*
|
|
* Switches the CPU to enter sleep.
|
|
*/
|
|
ENTRY(tegra30_tear_down_cpu)
|
|
mov32 r6, TEGRA_FLOW_CTRL_BASE
|
|
|
|
b tegra30_enter_sleep
|
|
ENDPROC(tegra30_tear_down_cpu)
|
|
|
|
/*
|
|
* tegra30_enter_sleep
|
|
*
|
|
* uses flow controller to enter sleep state
|
|
* executes from IRAM with SDRAM in selfrefresh when target state is LP0 or LP1
|
|
* executes from SDRAM with target state is LP2
|
|
* r6 = TEGRA_FLOW_CTRL_BASE
|
|
*/
|
|
tegra30_enter_sleep:
|
|
cpu_id r1
|
|
|
|
cpu_to_csr_reg r2, r1
|
|
ldr r0, [r6, r2]
|
|
orr r0, r0, #FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG
|
|
orr r0, r0, #FLOW_CTRL_CSR_ENABLE
|
|
str r0, [r6, r2]
|
|
|
|
mov r0, #FLOW_CTRL_WAIT_FOR_INTERRUPT
|
|
orr r0, r0, #FLOW_CTRL_HALT_CPU_IRQ | FLOW_CTRL_HALT_CPU_FIQ
|
|
cpu_to_halt_reg r2, r1
|
|
str r0, [r6, r2]
|
|
dsb
|
|
ldr r0, [r6, r2] /* memory barrier */
|
|
|
|
halted:
|
|
isb
|
|
dsb
|
|
wfi /* CPU should be power gated here */
|
|
|
|
/* !!!FIXME!!! Implement halt failure handler */
|
|
b halted
|
|
|
|
#endif
|