97991657be
Add sh7372 Core Standby sleep mode support and tie it in with the shared SH-Mobile ARM suspend code. The Core Standby mode is the lightest sh7372-specific sleep mode, cutting power to the ARM core excluding the L2 cache. Any interrupt source can be used for wakeups. The low level portion of this code is based on the TI OMAP sleep code in sleep34xx.S, thanks to them. Signed-off-by: Magnus Damm <damm@opensource.se> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
260 lines
6.7 KiB
ArmAsm
260 lines
6.7 KiB
ArmAsm
/*
|
|
* sh7372 lowlevel sleep code for "Core Standby Mode"
|
|
*
|
|
* Copyright (C) 2011 Magnus Damm
|
|
*
|
|
* In "Core Standby Mode" the ARM core is off, but L2 cache is still on
|
|
*
|
|
* Based on mach-omap2/sleep34xx.S
|
|
*
|
|
* (C) Copyright 2007 Texas Instruments
|
|
* Karthik Dasu <karthik-dp@ti.com>
|
|
*
|
|
* (C) Copyright 2004 Texas Instruments, <www.ti.com>
|
|
* Richard Woodruff <r-woodruff2@ti.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that 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, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
|
* MA 02111-1307 USA
|
|
*/
|
|
|
|
#include <linux/linkage.h>
|
|
#include <asm/assembler.h>
|
|
|
|
#define SMFRAM 0xe6a70000
|
|
|
|
.align
|
|
kernel_flush:
|
|
.word v7_flush_dcache_all
|
|
|
|
.align 3
|
|
ENTRY(sh7372_cpu_suspend)
|
|
stmfd sp!, {r0-r12, lr} @ save registers on stack
|
|
|
|
ldr r8, =SMFRAM
|
|
|
|
mov r4, sp @ Store sp
|
|
mrs r5, spsr @ Store spsr
|
|
mov r6, lr @ Store lr
|
|
stmia r8!, {r4-r6}
|
|
|
|
mrc p15, 0, r4, c1, c0, 2 @ Coprocessor access control register
|
|
mrc p15, 0, r5, c2, c0, 0 @ TTBR0
|
|
mrc p15, 0, r6, c2, c0, 1 @ TTBR1
|
|
mrc p15, 0, r7, c2, c0, 2 @ TTBCR
|
|
stmia r8!, {r4-r7}
|
|
|
|
mrc p15, 0, r4, c3, c0, 0 @ Domain access Control Register
|
|
mrc p15, 0, r5, c10, c2, 0 @ PRRR
|
|
mrc p15, 0, r6, c10, c2, 1 @ NMRR
|
|
stmia r8!,{r4-r6}
|
|
|
|
mrc p15, 0, r4, c13, c0, 1 @ Context ID
|
|
mrc p15, 0, r5, c13, c0, 2 @ User r/w thread and process ID
|
|
mrc p15, 0, r6, c12, c0, 0 @ Secure or NS vector base address
|
|
mrs r7, cpsr @ Store current cpsr
|
|
stmia r8!, {r4-r7}
|
|
|
|
mrc p15, 0, r4, c1, c0, 0 @ save control register
|
|
stmia r8!, {r4}
|
|
|
|
/*
|
|
* jump out to kernel flush routine
|
|
* - reuse that code is better
|
|
* - it executes in a cached space so is faster than refetch per-block
|
|
* - should be faster and will change with kernel
|
|
* - 'might' have to copy address, load and jump to it
|
|
* Flush all data from the L1 data cache before disabling
|
|
* SCTLR.C bit.
|
|
*/
|
|
ldr r1, kernel_flush
|
|
mov lr, pc
|
|
bx r1
|
|
|
|
/*
|
|
* Clear the SCTLR.C bit to prevent further data cache
|
|
* allocation. Clearing SCTLR.C would make all the data accesses
|
|
* strongly ordered and would not hit the cache.
|
|
*/
|
|
mrc p15, 0, r0, c1, c0, 0
|
|
bic r0, r0, #(1 << 2) @ Disable the C bit
|
|
mcr p15, 0, r0, c1, c0, 0
|
|
isb
|
|
|
|
/*
|
|
* Invalidate L1 data cache. Even though only invalidate is
|
|
* necessary exported flush API is used here. Doing clean
|
|
* on already clean cache would be almost NOP.
|
|
*/
|
|
ldr r1, kernel_flush
|
|
blx r1
|
|
/*
|
|
* The kernel doesn't interwork: v7_flush_dcache_all in particluar will
|
|
* always return in Thumb state when CONFIG_THUMB2_KERNEL is enabled.
|
|
* This sequence switches back to ARM. Note that .align may insert a
|
|
* nop: bx pc needs to be word-aligned in order to work.
|
|
*/
|
|
THUMB( .thumb )
|
|
THUMB( .align )
|
|
THUMB( bx pc )
|
|
THUMB( nop )
|
|
.arm
|
|
|
|
/* Data memory barrier and Data sync barrier */
|
|
dsb
|
|
dmb
|
|
|
|
/*
|
|
* ===================================
|
|
* == WFI instruction => Enter idle ==
|
|
* ===================================
|
|
*/
|
|
wfi @ wait for interrupt
|
|
|
|
/*
|
|
* ===================================
|
|
* == Resume path for non-OFF modes ==
|
|
* ===================================
|
|
*/
|
|
mrc p15, 0, r0, c1, c0, 0
|
|
tst r0, #(1 << 2) @ Check C bit enabled?
|
|
orreq r0, r0, #(1 << 2) @ Enable the C bit if cleared
|
|
mcreq p15, 0, r0, c1, c0, 0
|
|
isb
|
|
|
|
/*
|
|
* ===================================
|
|
* == Exit point from non-OFF modes ==
|
|
* ===================================
|
|
*/
|
|
ldmfd sp!, {r0-r12, pc} @ restore regs and return
|
|
|
|
.pool
|
|
|
|
.align 12
|
|
.text
|
|
.global sh7372_cpu_resume
|
|
sh7372_cpu_resume:
|
|
|
|
mov r1, #0
|
|
/*
|
|
* Invalidate all instruction caches to PoU
|
|
* and flush branch target cache
|
|
*/
|
|
mcr p15, 0, r1, c7, c5, 0
|
|
|
|
ldr r3, =SMFRAM
|
|
|
|
ldmia r3!, {r4-r6}
|
|
mov sp, r4 @ Restore sp
|
|
msr spsr_cxsf, r5 @ Restore spsr
|
|
mov lr, r6 @ Restore lr
|
|
|
|
ldmia r3!, {r4-r7}
|
|
mcr p15, 0, r4, c1, c0, 2 @ Coprocessor access Control Register
|
|
mcr p15, 0, r5, c2, c0, 0 @ TTBR0
|
|
mcr p15, 0, r6, c2, c0, 1 @ TTBR1
|
|
mcr p15, 0, r7, c2, c0, 2 @ TTBCR
|
|
|
|
ldmia r3!,{r4-r6}
|
|
mcr p15, 0, r4, c3, c0, 0 @ Domain access Control Register
|
|
mcr p15, 0, r5, c10, c2, 0 @ PRRR
|
|
mcr p15, 0, r6, c10, c2, 1 @ NMRR
|
|
|
|
ldmia r3!,{r4-r7}
|
|
mcr p15, 0, r4, c13, c0, 1 @ Context ID
|
|
mcr p15, 0, r5, c13, c0, 2 @ User r/w thread and process ID
|
|
mrc p15, 0, r6, c12, c0, 0 @ Secure or NS vector base address
|
|
msr cpsr, r7 @ store cpsr
|
|
|
|
/* Starting to enable MMU here */
|
|
mrc p15, 0, r7, c2, c0, 2 @ Read TTBRControl
|
|
/* Extract N (0:2) bits and decide whether to use TTBR0 or TTBR1 */
|
|
and r7, #0x7
|
|
cmp r7, #0x0
|
|
beq usettbr0
|
|
ttbr_error:
|
|
/*
|
|
* More work needs to be done to support N[0:2] value other than 0
|
|
* So looping here so that the error can be detected
|
|
*/
|
|
b ttbr_error
|
|
|
|
.align
|
|
cache_pred_disable_mask:
|
|
.word 0xFFFFE7FB
|
|
ttbrbit_mask:
|
|
.word 0xFFFFC000
|
|
table_index_mask:
|
|
.word 0xFFF00000
|
|
table_entry:
|
|
.word 0x00000C02
|
|
usettbr0:
|
|
|
|
mrc p15, 0, r2, c2, c0, 0
|
|
ldr r5, ttbrbit_mask
|
|
and r2, r5
|
|
mov r4, pc
|
|
ldr r5, table_index_mask
|
|
and r4, r5 @ r4 = 31 to 20 bits of pc
|
|
/* Extract the value to be written to table entry */
|
|
ldr r6, table_entry
|
|
/* r6 has the value to be written to table entry */
|
|
add r6, r6, r4
|
|
/* Getting the address of table entry to modify */
|
|
lsr r4, #18
|
|
/* r2 has the location which needs to be modified */
|
|
add r2, r4
|
|
ldr r4, [r2]
|
|
str r6, [r2] /* modify the table entry */
|
|
|
|
mov r7, r6
|
|
mov r5, r2
|
|
mov r6, r4
|
|
/* r5 = original page table address */
|
|
/* r6 = original page table data */
|
|
|
|
mov r0, #0
|
|
mcr p15, 0, r0, c7, c5, 4 @ Flush prefetch buffer
|
|
mcr p15, 0, r0, c7, c5, 6 @ Invalidate branch predictor array
|
|
mcr p15, 0, r0, c8, c5, 0 @ Invalidate instruction TLB
|
|
mcr p15, 0, r0, c8, c6, 0 @ Invalidate data TLB
|
|
|
|
/*
|
|
* Restore control register. This enables the MMU.
|
|
* The caches and prediction are not enabled here, they
|
|
* will be enabled after restoring the MMU table entry.
|
|
*/
|
|
ldmia r3!, {r4}
|
|
stmia r3!, {r5} /* save original page table address */
|
|
stmia r3!, {r6} /* save original page table data */
|
|
stmia r3!, {r7} /* save modified page table data */
|
|
|
|
ldr r2, cache_pred_disable_mask
|
|
and r4, r2
|
|
mcr p15, 0, r4, c1, c0, 0
|
|
dsb
|
|
isb
|
|
|
|
ldr r0, =restoremmu_on
|
|
bx r0
|
|
|
|
/*
|
|
* ==============================
|
|
* == Exit point from OFF mode ==
|
|
* ==============================
|
|
*/
|
|
restoremmu_on:
|
|
|
|
ldmfd sp!, {r0-r12, pc} @ restore regs and return
|