373 lines
9.6 KiB
ArmAsm
373 lines
9.6 KiB
ArmAsm
/* sleep.S: power saving mode entry
|
|
*
|
|
* Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
|
|
* Written by David Woodhouse (dwmw2@infradead.org)
|
|
*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#include <linux/sys.h>
|
|
#include <linux/linkage.h>
|
|
#include <asm/setup.h>
|
|
#include <asm/segment.h>
|
|
#include <asm/page.h>
|
|
#include <asm/ptrace.h>
|
|
#include <asm/errno.h>
|
|
#include <asm/cache.h>
|
|
#include <asm/spr-regs.h>
|
|
|
|
#define __addr_MASK 0xfeff9820 /* interrupt controller mask */
|
|
|
|
#define __addr_FR55X_DRCN 0xfeff0218 /* Address of DRCN register */
|
|
#define FR55X_DSTS_OFFSET -4 /* Offset from DRCN to DSTS */
|
|
#define FR55X_SDRAMC_DSTS_SSI 0x00000002 /* indicates that the SDRAM is in self-refresh mode */
|
|
|
|
#define __addr_FR4XX_DRCN 0xfe000430 /* Address of DRCN register */
|
|
#define FR4XX_DSTS_OFFSET -8 /* Offset from DRCN to DSTS */
|
|
#define FR4XX_SDRAMC_DSTS_SSI 0x00000001 /* indicates that the SDRAM is in self-refresh mode */
|
|
|
|
#define SDRAMC_DRCN_SR 0x00000001 /* transition SDRAM into self-refresh mode */
|
|
|
|
.section .bss
|
|
.balign 8
|
|
.globl __sleep_save_area
|
|
__sleep_save_area:
|
|
.space 16
|
|
|
|
|
|
.text
|
|
.balign 4
|
|
|
|
.macro li v r
|
|
sethi.p %hi(\v),\r
|
|
setlo %lo(\v),\r
|
|
.endm
|
|
|
|
#ifdef CONFIG_PM
|
|
###############################################################################
|
|
#
|
|
# CPU suspension routine
|
|
# - void frv_cpu_suspend(unsigned long pdm_mode)
|
|
#
|
|
###############################################################################
|
|
.globl frv_cpu_suspend
|
|
.type frv_cpu_suspend,@function
|
|
frv_cpu_suspend:
|
|
|
|
#----------------------------------------------------
|
|
# save hsr0, psr, isr, and lr for resume code
|
|
#----------------------------------------------------
|
|
li __sleep_save_area,gr11
|
|
|
|
movsg hsr0,gr4
|
|
movsg psr,gr5
|
|
movsg isr,gr6
|
|
movsg lr,gr7
|
|
stdi gr4,@(gr11,#0)
|
|
stdi gr6,@(gr11,#8)
|
|
|
|
# store the return address from sleep in GR14, and its complement in GR13 as a check
|
|
li __ramboot_resume,gr14
|
|
#ifdef CONFIG_MMU
|
|
# Resume via RAMBOOT# will turn MMU off, so bootloader needs a physical address.
|
|
sethi.p %hi(__page_offset),gr13
|
|
setlo %lo(__page_offset),gr13
|
|
sub gr14,gr13,gr14
|
|
#endif
|
|
not gr14,gr13
|
|
|
|
#----------------------------------------------------
|
|
# preload and lock into icache that code which may have to run
|
|
# when dram is in self-refresh state.
|
|
#----------------------------------------------------
|
|
movsg hsr0, gr3
|
|
li HSR0_ICE,gr4
|
|
or gr3,gr4,gr3
|
|
movgs gr3,hsr0
|
|
or gr3,gr8,gr7 // add the sleep bits for later
|
|
|
|
li #__icache_lock_start,gr3
|
|
li #__icache_lock_end,gr4
|
|
1: icpl gr3,gr0,#1
|
|
addi gr3,#L1_CACHE_BYTES,gr3
|
|
cmp gr4,gr3,icc0
|
|
bhi icc0,#0,1b
|
|
|
|
# disable exceptions
|
|
movsg psr,gr8
|
|
andi.p gr8,#~PSR_PIL,gr8
|
|
andi gr8,~PSR_ET,gr8
|
|
movgs gr8,psr
|
|
ori gr8,#PSR_ET,gr8
|
|
|
|
srli gr8,#28,gr4
|
|
subicc gr4,#3,gr0,icc0
|
|
beq icc0,#0,1f
|
|
# FR4xx
|
|
li __addr_FR4XX_DRCN,gr4
|
|
li FR4XX_SDRAMC_DSTS_SSI,gr5
|
|
li FR4XX_DSTS_OFFSET,gr6
|
|
bra __icache_lock_start
|
|
1:
|
|
# FR5xx
|
|
li __addr_FR55X_DRCN,gr4
|
|
li FR55X_SDRAMC_DSTS_SSI,gr5
|
|
li FR55X_DSTS_OFFSET,gr6
|
|
bra __icache_lock_start
|
|
|
|
.size frv_cpu_suspend, .-frv_cpu_suspend
|
|
|
|
#
|
|
# the final part of the sleep sequence...
|
|
# - we want it to be be cacheline aligned so we can lock it into the icache easily
|
|
# On entry: gr7 holds desired hsr0 sleep value
|
|
# gr8 holds desired psr sleep value
|
|
#
|
|
.balign L1_CACHE_BYTES
|
|
.type __icache_lock_start,@function
|
|
__icache_lock_start:
|
|
|
|
#----------------------------------------------------
|
|
# put SDRAM in self-refresh mode
|
|
#----------------------------------------------------
|
|
|
|
# Flush all data in the cache using the DCEF instruction.
|
|
dcef @(gr0,gr0),#1
|
|
|
|
# Stop DMAC transfer
|
|
|
|
# Execute dummy load from SDRAM
|
|
ldi @(gr11,#0),gr11
|
|
|
|
# put the SDRAM into self-refresh mode
|
|
ld @(gr4,gr0),gr11
|
|
ori gr11,#SDRAMC_DRCN_SR,gr11
|
|
st gr11,@(gr4,gr0)
|
|
membar
|
|
|
|
# wait for SDRAM to reach self-refresh mode
|
|
1: ld @(gr4,gr6),gr11
|
|
andcc gr11,gr5,gr11,icc0
|
|
beq icc0,#0,1b
|
|
|
|
# Set the GPIO register so that the IRQ[3:0] pins become valid, as required.
|
|
# Set the clock mode (CLKC register) as required.
|
|
# - At this time, also set the CLKC register P0 bit.
|
|
|
|
# Set the HSR0 register PDM field.
|
|
movgs gr7,hsr0
|
|
|
|
# Execute NOP 32 times.
|
|
.rept 32
|
|
nop
|
|
.endr
|
|
|
|
#if 0 // Fujitsu recommend to skip this and will update docs.
|
|
# Release the interrupt mask setting of the MASK register of the
|
|
# interrupt controller if necessary.
|
|
sti gr10,@(gr9,#0)
|
|
membar
|
|
#endif
|
|
|
|
# Set the PSR register ET bit to 1 to enable interrupts.
|
|
movgs gr8,psr
|
|
|
|
###################################################
|
|
# this is only reached if waking up via interrupt
|
|
###################################################
|
|
|
|
# Execute NOP 32 times.
|
|
.rept 32
|
|
nop
|
|
.endr
|
|
|
|
#----------------------------------------------------
|
|
# wake SDRAM from self-refresh mode
|
|
#----------------------------------------------------
|
|
ld @(gr4,gr0),gr11
|
|
andi gr11,#~SDRAMC_DRCN_SR,gr11
|
|
st gr11,@(gr4,gr0)
|
|
membar
|
|
2:
|
|
ld @(gr4,gr6),gr11 // Wait for it to come back...
|
|
andcc gr11,gr5,gr0,icc0
|
|
bne icc0,0,2b
|
|
|
|
# wait for the SDRAM to stabilise
|
|
li 0x0100000,gr3
|
|
3: subicc gr3,#1,gr3,icc0
|
|
bne icc0,#0,3b
|
|
|
|
# now that DRAM is back, this is the end of the code which gets
|
|
# locked in icache.
|
|
__icache_lock_end:
|
|
.size __icache_lock_start, .-__icache_lock_start
|
|
|
|
# Fall-through to the RAMBOOT# wakeup path
|
|
|
|
###############################################################################
|
|
#
|
|
# resume from suspend re-entry point reached via RAMBOOT# and bootloader
|
|
#
|
|
###############################################################################
|
|
__ramboot_resume:
|
|
|
|
#----------------------------------------------------
|
|
# restore hsr0, psr, isr, and leave saved lr in gr7
|
|
#----------------------------------------------------
|
|
li __sleep_save_area,gr11
|
|
#ifdef CONFIG_MMU
|
|
movsg hsr0,gr4
|
|
sethi.p %hi(HSR0_EXMMU),gr3
|
|
setlo %lo(HSR0_EXMMU),gr3
|
|
andcc gr3,gr4,gr0,icc0
|
|
bne icc0,#0,2f
|
|
|
|
# need to use physical address
|
|
sethi.p %hi(__page_offset),gr3
|
|
setlo %lo(__page_offset),gr3
|
|
sub gr11,gr3,gr11
|
|
|
|
# flush all tlb entries
|
|
setlos #64,gr4
|
|
setlos.p #PAGE_SIZE,gr5
|
|
setlos #0,gr6
|
|
1:
|
|
tlbpr gr6,gr0,#6,#0
|
|
subicc.p gr4,#1,gr4,icc0
|
|
add gr6,gr5,gr6
|
|
bne icc0,#2,1b
|
|
|
|
# need a temporary mapping for the current physical address we are
|
|
# using between time MMU is enabled and jump to virtual address is
|
|
# made.
|
|
sethi.p %hi(0x00000000),gr4
|
|
setlo %lo(0x00000000),gr4 ; physical address
|
|
setlos #xAMPRx_L|xAMPRx_M|xAMPRx_SS_256Mb|xAMPRx_S_KERNEL|xAMPRx_V,gr5
|
|
or gr4,gr5,gr5
|
|
|
|
movsg cxnr,gr13
|
|
or gr4,gr13,gr4
|
|
|
|
movgs gr4,iamlr1 ; mapped from real address 0
|
|
movgs gr5,iampr1 ; cached kernel memory at 0x00000000
|
|
2:
|
|
#endif
|
|
|
|
lddi @(gr11,#0),gr4 ; hsr0, psr
|
|
lddi @(gr11,#8),gr6 ; isr, lr
|
|
movgs gr4,hsr0
|
|
bar
|
|
|
|
#ifdef CONFIG_MMU
|
|
sethi.p %hi(1f),gr11
|
|
setlo %lo(1f),gr11
|
|
jmpl @(gr11,gr0)
|
|
1:
|
|
movgs gr0,iampr1 ; get rid of temporary mapping
|
|
#endif
|
|
movgs gr5,psr
|
|
movgs gr6,isr
|
|
|
|
#----------------------------------------------------
|
|
# unlock the icache which was locked before going to sleep
|
|
#----------------------------------------------------
|
|
li __icache_lock_start,gr3
|
|
li __icache_lock_end,gr4
|
|
1: icul gr3
|
|
addi gr3,#L1_CACHE_BYTES,gr3
|
|
cmp gr4,gr3,icc0
|
|
bhi icc0,#0,1b
|
|
|
|
#----------------------------------------------------
|
|
# back to business as usual
|
|
#----------------------------------------------------
|
|
jmpl @(gr7,gr0) ;
|
|
|
|
#endif /* CONFIG_PM */
|
|
|
|
###############################################################################
|
|
#
|
|
# CPU core sleep mode routine
|
|
#
|
|
###############################################################################
|
|
.globl frv_cpu_core_sleep
|
|
.type frv_cpu_core_sleep,@function
|
|
frv_cpu_core_sleep:
|
|
|
|
# Preload into icache.
|
|
li #__core_sleep_icache_lock_start,gr3
|
|
li #__core_sleep_icache_lock_end,gr4
|
|
|
|
1: icpl gr3,gr0,#1
|
|
addi gr3,#L1_CACHE_BYTES,gr3
|
|
cmp gr4,gr3,icc0
|
|
bhi icc0,#0,1b
|
|
|
|
bra __core_sleep_icache_lock_start
|
|
|
|
.balign L1_CACHE_BYTES
|
|
__core_sleep_icache_lock_start:
|
|
|
|
# (1) Set the PSR register ET bit to 0 to disable interrupts.
|
|
movsg psr,gr8
|
|
andi.p gr8,#~(PSR_PIL),gr8
|
|
andi gr8,#~(PSR_ET),gr4
|
|
movgs gr4,psr
|
|
|
|
#if 0 // Fujitsu recommend to skip this and will update docs.
|
|
# (2) Set '1' to all bits in the MASK register of the interrupt
|
|
# controller and mask interrupts.
|
|
sethi.p %hi(__addr_MASK),gr9
|
|
setlo %lo(__addr_MASK),gr9
|
|
sethi.p %hi(0xffff0000),gr4
|
|
setlo %lo(0xffff0000),gr4
|
|
ldi @(gr9,#0),gr10
|
|
sti gr4,@(gr9,#0)
|
|
#endif
|
|
# (3) Flush all data in the cache using the DCEF instruction.
|
|
dcef @(gr0,gr0),#1
|
|
|
|
# (4) Execute the memory barrier instruction
|
|
membar
|
|
|
|
# (5) Set the GPIO register so that the IRQ[3:0] pins become valid, as required.
|
|
# (6) Set the clock mode (CLKC register) as required.
|
|
# - At this time, also set the CLKC register P0 bit.
|
|
# (7) Set the HSR0 register PDM field to 001 .
|
|
movsg hsr0,gr4
|
|
ori gr4,HSR0_PDM_CORE_SLEEP,gr4
|
|
movgs gr4,hsr0
|
|
|
|
# (8) Execute NOP 32 times.
|
|
.rept 32
|
|
nop
|
|
.endr
|
|
|
|
#if 0 // Fujitsu recommend to skip this and will update docs.
|
|
# (9) Release the interrupt mask setting of the MASK register of the
|
|
# interrupt controller if necessary.
|
|
sti gr10,@(gr9,#0)
|
|
membar
|
|
#endif
|
|
|
|
# (10) Set the PSR register ET bit to 1 to enable interrupts.
|
|
movgs gr8,psr
|
|
|
|
__core_sleep_icache_lock_end:
|
|
|
|
# Unlock from icache
|
|
li __core_sleep_icache_lock_start,gr3
|
|
li __core_sleep_icache_lock_end,gr4
|
|
1: icul gr3
|
|
addi gr3,#L1_CACHE_BYTES,gr3
|
|
cmp gr4,gr3,icc0
|
|
bhi icc0,#0,1b
|
|
|
|
bralr
|
|
|
|
.size frv_cpu_core_sleep, .-frv_cpu_core_sleep
|