[ARM] pxa: add preliminary suspend/resume code for pxa3xx
1. clear RDH bit after resuming back from D3, otherwise, the multi function pins will retain the low power state 2. save/restore essential system registers Signed-off-by: eric miao <eric.miao@marvell.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
parent
16dfdbf038
commit
c4d1fb627f
4 changed files with 183 additions and 14 deletions
|
@ -22,6 +22,7 @@
|
||||||
#include <asm/hardware.h>
|
#include <asm/hardware.h>
|
||||||
#include <asm/arch/mfp.h>
|
#include <asm/arch/mfp.h>
|
||||||
#include <asm/arch/mfp-pxa3xx.h>
|
#include <asm/arch/mfp-pxa3xx.h>
|
||||||
|
#include <asm/arch/pxa3xx-regs.h>
|
||||||
|
|
||||||
/* mfp_spin_lock is used to ensure that MFP register configuration
|
/* mfp_spin_lock is used to ensure that MFP register configuration
|
||||||
* (most likely a read-modify-write operation) is atomic, and that
|
* (most likely a read-modify-write operation) is atomic, and that
|
||||||
|
@ -223,6 +224,14 @@ static int pxa3xx_mfp_resume(struct sys_device *d)
|
||||||
struct pxa3xx_mfp_pin *p = &mfp_table[pin];
|
struct pxa3xx_mfp_pin *p = &mfp_table[pin];
|
||||||
__mfp_config_run(p);
|
__mfp_config_run(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* clear RDH bit when MFP settings are restored
|
||||||
|
*
|
||||||
|
* NOTE: the last 3 bits DxS are write-1-to-clear so carefully
|
||||||
|
* preserve them here in case they will be referenced later
|
||||||
|
*/
|
||||||
|
ASCR &= ~(ASCR_RDH | ASCR_D1S | ASCR_D2S | ASCR_D3S);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#define RO_CLK 60000000
|
#define RO_CLK 60000000
|
||||||
|
|
||||||
#define ACCR_D0CS (1 << 26)
|
#define ACCR_D0CS (1 << 26)
|
||||||
|
#define ACCR_PCCE (1 << 11)
|
||||||
|
|
||||||
/* crystal frequency to static memory controller multiplier (SMCFS) */
|
/* crystal frequency to static memory controller multiplier (SMCFS) */
|
||||||
static unsigned char smcfs_mult[8] = { 6, 0, 8, 0, 0, 16, };
|
static unsigned char smcfs_mult[8] = { 6, 0, 8, 0, 0, 16, };
|
||||||
|
@ -204,7 +205,6 @@ static struct clk pxa3xx_clks[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
#define SLEEP_SAVE_SIZE 4
|
|
||||||
|
|
||||||
#define ISRAM_START 0x5c000000
|
#define ISRAM_START 0x5c000000
|
||||||
#define ISRAM_SIZE SZ_256K
|
#define ISRAM_SIZE SZ_256K
|
||||||
|
@ -212,25 +212,29 @@ static struct clk pxa3xx_clks[] = {
|
||||||
static void __iomem *sram;
|
static void __iomem *sram;
|
||||||
static unsigned long wakeup_src;
|
static unsigned long wakeup_src;
|
||||||
|
|
||||||
|
#define SAVE(x) sleep_save[SLEEP_SAVE_##x] = x
|
||||||
|
#define RESTORE(x) x = sleep_save[SLEEP_SAVE_##x]
|
||||||
|
|
||||||
|
enum { SLEEP_SAVE_START = 0,
|
||||||
|
SLEEP_SAVE_CKENA,
|
||||||
|
SLEEP_SAVE_CKENB,
|
||||||
|
SLEEP_SAVE_ACCR,
|
||||||
|
|
||||||
|
SLEEP_SAVE_SIZE,
|
||||||
|
};
|
||||||
|
|
||||||
static void pxa3xx_cpu_pm_save(unsigned long *sleep_save)
|
static void pxa3xx_cpu_pm_save(unsigned long *sleep_save)
|
||||||
{
|
{
|
||||||
pr_debug("PM: CKENA=%08x CKENB=%08x\n", CKENA, CKENB);
|
SAVE(CKENA);
|
||||||
|
SAVE(CKENB);
|
||||||
if (CKENA & (1 << CKEN_USBH)) {
|
SAVE(ACCR);
|
||||||
printk(KERN_ERR "PM: USB host clock not stopped?\n");
|
|
||||||
CKENA &= ~(1 << CKEN_USBH);
|
|
||||||
}
|
|
||||||
// CKENA |= 1 << (CKEN_ISC & 31);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Low power modes require the HSIO2 clock to be enabled.
|
|
||||||
*/
|
|
||||||
CKENB |= 1 << (CKEN_HSIO2 & 31);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pxa3xx_cpu_pm_restore(unsigned long *sleep_save)
|
static void pxa3xx_cpu_pm_restore(unsigned long *sleep_save)
|
||||||
{
|
{
|
||||||
CKENB &= ~(1 << (CKEN_HSIO2 & 31));
|
RESTORE(ACCR);
|
||||||
|
RESTORE(CKENA);
|
||||||
|
RESTORE(CKENB);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -266,6 +270,46 @@ static void pxa3xx_cpu_standby(unsigned int pwrmode)
|
||||||
printk("PM: AD2D0SR=%08x ASCR=%08x\n", AD2D0SR, ASCR);
|
printk("PM: AD2D0SR=%08x ASCR=%08x\n", AD2D0SR, ASCR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE: currently, the OBM (OEM Boot Module) binary comes along with
|
||||||
|
* PXA3xx development kits assumes that the resuming process continues
|
||||||
|
* with the address stored within the first 4 bytes of SDRAM. The PSPR
|
||||||
|
* register is used privately by BootROM and OBM, and _must_ be set to
|
||||||
|
* 0x5c014000 for the moment.
|
||||||
|
*/
|
||||||
|
static void pxa3xx_cpu_pm_suspend(void)
|
||||||
|
{
|
||||||
|
volatile unsigned long *p = (volatile void *)0xc0000000;
|
||||||
|
unsigned long saved_data = *p;
|
||||||
|
|
||||||
|
extern void pxa3xx_cpu_suspend(void);
|
||||||
|
extern void pxa3xx_cpu_resume(void);
|
||||||
|
|
||||||
|
/* resuming from D2 requires the HSIO2/BOOT/TPM clocks enabled */
|
||||||
|
CKENA |= (1 << CKEN_BOOT) | (1 << CKEN_TPM);
|
||||||
|
CKENB |= 1 << (CKEN_HSIO2 & 0x1f);
|
||||||
|
|
||||||
|
/* clear and setup wakeup source */
|
||||||
|
AD3SR = ~0;
|
||||||
|
AD3ER = wakeup_src;
|
||||||
|
ASCR = ASCR;
|
||||||
|
ARSR = ARSR;
|
||||||
|
|
||||||
|
PCFR |= (1u << 13); /* L1_DIS */
|
||||||
|
PCFR &= ~((1u << 12) | (1u << 1)); /* L0_EN | SL_ROD */
|
||||||
|
|
||||||
|
PSPR = 0x5c014000;
|
||||||
|
|
||||||
|
/* overwrite with the resume address */
|
||||||
|
*p = virt_to_phys(pxa3xx_cpu_resume);
|
||||||
|
|
||||||
|
pxa3xx_cpu_suspend();
|
||||||
|
|
||||||
|
*p = saved_data;
|
||||||
|
|
||||||
|
AD3ER = 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void pxa3xx_cpu_pm_enter(suspend_state_t state)
|
static void pxa3xx_cpu_pm_enter(suspend_state_t state)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -280,6 +324,7 @@ static void pxa3xx_cpu_pm_enter(suspend_state_t state)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PM_SUSPEND_MEM:
|
case PM_SUSPEND_MEM:
|
||||||
|
pxa3xx_cpu_pm_suspend();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,108 @@ pxa_cpu_save_sp:
|
||||||
str r0, [r1]
|
str r0, [r1]
|
||||||
ldr pc, [sp], #4
|
ldr pc, [sp], #4
|
||||||
|
|
||||||
|
#ifdef CONFIG_PXA3xx
|
||||||
|
/*
|
||||||
|
* pxa3xx_cpu_suspend() - forces CPU into sleep state (S2D3C4)
|
||||||
|
*
|
||||||
|
* NOTE: unfortunately, pxa_cpu_save_cp can not be reused here since
|
||||||
|
* the auxiliary control register address is different between pxa3xx
|
||||||
|
* and pxa{25x,27x}
|
||||||
|
*/
|
||||||
|
|
||||||
|
ENTRY(pxa3xx_cpu_suspend)
|
||||||
|
|
||||||
|
#ifndef CONFIG_IWMMXT
|
||||||
|
mra r2, r3, acc0
|
||||||
|
#endif
|
||||||
|
stmfd sp!, {r2 - r12, lr} @ save registers on stack
|
||||||
|
|
||||||
|
mrc p14, 0, r3, c6, c0, 0 @ clock configuration, for turbo mode
|
||||||
|
mrc p15, 0, r4, c15, c1, 0 @ CP access reg
|
||||||
|
mrc p15, 0, r5, c13, c0, 0 @ PID
|
||||||
|
mrc p15, 0, r6, c3, c0, 0 @ domain ID
|
||||||
|
mrc p15, 0, r7, c2, c0, 0 @ translation table base addr
|
||||||
|
mrc p15, 0, r8, c1, c0, 1 @ auxiliary control reg
|
||||||
|
mrc p15, 0, r9, c1, c0, 0 @ control reg
|
||||||
|
|
||||||
|
bic r3, r3, #2 @ clear frequency change bit
|
||||||
|
|
||||||
|
@ store them plus current virtual stack ptr on stack
|
||||||
|
mov r10, sp
|
||||||
|
stmfd sp!, {r3 - r10}
|
||||||
|
|
||||||
|
@ store physical address of stack pointer
|
||||||
|
mov r0, sp
|
||||||
|
bl sleep_phys_sp
|
||||||
|
ldr r1, =sleep_save_sp
|
||||||
|
str r0, [r1]
|
||||||
|
|
||||||
|
@ clean data cache
|
||||||
|
bl xsc3_flush_kern_cache_all
|
||||||
|
|
||||||
|
mov r0, #0x06 @ S2D3C4 mode
|
||||||
|
mcr p14, 0, r0, c7, c0, 0 @ enter sleep
|
||||||
|
|
||||||
|
20: b 20b @ waiting for sleep
|
||||||
|
|
||||||
|
.data
|
||||||
|
.align 5
|
||||||
|
/*
|
||||||
|
* pxa3xx_cpu_resume
|
||||||
|
*/
|
||||||
|
|
||||||
|
ENTRY(pxa3xx_cpu_resume)
|
||||||
|
|
||||||
|
mov r0, #PSR_I_BIT | PSR_F_BIT | SVC_MODE @ set SVC, irqs off
|
||||||
|
msr cpsr_c, r0
|
||||||
|
|
||||||
|
ldr r0, sleep_save_sp @ stack phys addr
|
||||||
|
ldmfd r0, {r3 - r9, sp} @ CP regs + virt stack ptr
|
||||||
|
|
||||||
|
mov r1, #0
|
||||||
|
mcr p15, 0, r1, c7, c7, 0 @ invalidate I & D caches, BTB
|
||||||
|
mcr p15, 0, r1, c7, c10, 4 @ drain write (&fill) buffer
|
||||||
|
mcr p15, 0, r1, c7, c5, 4 @ flush prefetch buffer
|
||||||
|
mcr p15, 0, r1, c8, c7, 0 @ invalidate I & D TLBs
|
||||||
|
|
||||||
|
mcr p14, 0, r3, c6, c0, 0 @ clock configuration, turbo mode.
|
||||||
|
mcr p15, 0, r4, c15, c1, 0 @ CP access reg
|
||||||
|
mcr p15, 0, r5, c13, c0, 0 @ PID
|
||||||
|
mcr p15, 0, r6, c3, c0, 0 @ domain ID
|
||||||
|
mcr p15, 0, r7, c2, c0, 0 @ translation table base addr
|
||||||
|
mcr p15, 0, r8, c1, c0, 1 @ auxiliary control reg
|
||||||
|
|
||||||
|
@ temporarily map resume_turn_on_mmu into the page table,
|
||||||
|
@ otherwise prefetch abort occurs after MMU is turned on
|
||||||
|
mov r1, r7
|
||||||
|
bic r1, r1, #0x00ff
|
||||||
|
bic r1, r1, #0x3f00
|
||||||
|
ldr r2, =0x542e
|
||||||
|
|
||||||
|
adr r3, resume_turn_on_mmu
|
||||||
|
mov r3, r3, lsr #20
|
||||||
|
orr r4, r2, r3, lsl #20
|
||||||
|
ldr r5, [r1, r3, lsl #2]
|
||||||
|
str r4, [r1, r3, lsl #2]
|
||||||
|
|
||||||
|
@ Mapping page table address in the page table
|
||||||
|
mov r6, r1, lsr #20
|
||||||
|
orr r7, r2, r6, lsl #20
|
||||||
|
ldr r8, [r1, r6, lsl #2]
|
||||||
|
str r7, [r1, r6, lsl #2]
|
||||||
|
|
||||||
|
ldr r2, =pxa3xx_resume_after_mmu @ absolute virtual address
|
||||||
|
b resume_turn_on_mmu @ cache align execution
|
||||||
|
|
||||||
|
.text
|
||||||
|
pxa3xx_resume_after_mmu:
|
||||||
|
/* restore the temporary mapping */
|
||||||
|
str r5, [r1, r3, lsl #2]
|
||||||
|
str r8, [r1, r6, lsl #2]
|
||||||
|
b resume_after_mmu
|
||||||
|
|
||||||
|
#endif /* CONFIG_PXA3xx */
|
||||||
|
|
||||||
#ifdef CONFIG_PXA27x
|
#ifdef CONFIG_PXA27x
|
||||||
/*
|
/*
|
||||||
* pxa27x_cpu_suspend()
|
* pxa27x_cpu_suspend()
|
||||||
|
|
|
@ -12,6 +12,19 @@
|
||||||
|
|
||||||
#ifndef __ASM_ARCH_PXA3XX_REGS_H
|
#ifndef __ASM_ARCH_PXA3XX_REGS_H
|
||||||
#define __ASM_ARCH_PXA3XX_REGS_H
|
#define __ASM_ARCH_PXA3XX_REGS_H
|
||||||
|
/*
|
||||||
|
* Service Power Management Unit (MPMU)
|
||||||
|
*/
|
||||||
|
#define PMCR __REG(0x40F50000) /* Power Manager Control Register */
|
||||||
|
#define PSR __REG(0x40F50004) /* Power Manager S2 Status Register */
|
||||||
|
#define PSPR __REG(0x40F50008) /* Power Manager Scratch Pad Register */
|
||||||
|
#define PCFR __REG(0x40F5000C) /* Power Manager General Configuration Register */
|
||||||
|
#define PWER __REG(0x40F50010) /* Power Manager Wake-up Enable Register */
|
||||||
|
#define PWSR __REG(0x40F50014) /* Power Manager Wake-up Status Register */
|
||||||
|
#define PECR __REG(0x40F50018) /* Power Manager EXT_WAKEUP[1:0] Control Register */
|
||||||
|
#define DCDCSR __REG(0x40F50080) /* DC-DC Controller Status Register */
|
||||||
|
#define PVCR __REG(0x40F50100) /* Power Manager Voltage Change Control Register */
|
||||||
|
#define PCMD(x) __REG(0x40F50110 + ((x) << 2))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Slave Power Managment Unit
|
* Slave Power Managment Unit
|
||||||
|
|
Loading…
Add table
Reference in a new issue