Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c4d1fb62 authored by eric miao's avatar eric miao Committed by Russell King
Browse files

[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: default avatareric miao <eric.miao@marvell.com>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 16dfdbf0
Loading
Loading
Loading
Loading
+9 −0
Original line number Original line Diff line number Diff line
@@ -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;
}
}


+59 −14
Original line number Original line Diff line number Diff line
@@ -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;


static void pxa3xx_cpu_pm_save(unsigned long *sleep_save)
#define SAVE(x)		sleep_save[SLEEP_SAVE_##x] = x
{
#define RESTORE(x)	x = sleep_save[SLEEP_SAVE_##x]
	pr_debug("PM: CKENA=%08x CKENB=%08x\n", CKENA, CKENB);


	if (CKENA & (1 << CKEN_USBH)) {
enum {	SLEEP_SAVE_START = 0,
		printk(KERN_ERR "PM: USB host clock not stopped?\n");
	SLEEP_SAVE_CKENA,
		CKENA &= ~(1 << CKEN_USBH);
	SLEEP_SAVE_CKENB,
	}
	SLEEP_SAVE_ACCR,
//	CKENA |= 1 << (CKEN_ISC & 31);


	/*
	SLEEP_SAVE_SIZE,
	 * Low power modes require the HSIO2 clock to be enabled.
};
	 */

	CKENB |= 1 << (CKEN_HSIO2 & 31);
static void pxa3xx_cpu_pm_save(unsigned long *sleep_save)
{
	SAVE(CKENA);
	SAVE(CKENB);
	SAVE(ACCR);
}
}


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;
	}
	}
}
}
+102 −0
Original line number Original line Diff line number Diff line
@@ -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()
+13 −0
Original line number Original line Diff line number Diff line
@@ -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