/*
 * S3C2410 Assembler Sleep/WakeUp Management Routines
 * Copyright (c) 2003 SW.LEE <hitchcar@sec.samsung.com>
 * 
 * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License.
 *
 */

#include <linux/linkage.h>
#include <asm/assembler.h>
#include <asm/hardware.h>
#include <asm/arch/power.h>

		.text
/*
 * s3c2410_cpu_suspend()
 *
 * Causes s3c2410 to enter power off state
 *
 */

ENTRY(s3c2410_cpu_suspend)

	stmfd	sp!, {r4 - r12, lr}		@ save registers on stack

	@ get coprocessor registers
	mrc 	p15, 0, r4, c3, c0, 0		@ domain ID
	mrc 	p15, 0, r5, c2, c0, 0		@ translation table base addr
	mrc	p15, 0, r6, c13, c0, 0		@ PID
	mrc 	p15, 0, r7, c1, c0, 0		@ control reg

	@ store them plus current virtual stack ptr on stack
	mov	r8, sp
	stmfd	sp!, {r4 - r8}

	@ preserve phys address of stack
	mov	r0, sp
	bl	sleep_phys_sp
	ldr	r1, =sleep_save_sp
	str	r0, [r1]

	@ clean data cache and invalidate WB
	bl	cpu_arm920_cache_clean_invalidate_all

	@ Already mask all interrupt sources by cli()
	
	@ Ensure that EINTMASK corresponding to wake up source has not to be masked 
	
	@ Set Wakeup external mode
	
	@ Change the Port Congfiguration to get the maximum of power efficiency
	
	@ Read rREFRESH and rCLKCON register in order to fill the TLB

	@ Only a sigle entry in the TLB takes care of Secntions and large pages
	ldr	r0,=MISCCR
	ldr	r0,[r0]
	ldr	r0,=CLKCON
	ldr	r0,[r0]

	@ Put the SDRAM self-refresh mode into r0 register
	ldr	r0,=REFRESH
	ldr	r1,[r0]
	orr	r1,r1,#SELF_REFRESH
	
	@ Set MISCCR[19:17]=111b to make SDRAM signals(SCLK0,SCLK1,SCKE) protected
	ldr	r2,=MISCCR
	ldr	r3,[r2]
	orr	r3,r3,#EN_SCLK

	@ Set the Power_Off mode bit in CLKCON Register
	ldr	r4,=CLKCON
	ldr	r5,=POWER_OFF_CLK 		@ default value + POWER_OFFA
	b	PowerOffAction

	.align 5
PowerOffAction: 
	str	r1,[r0]				@ SDRAM self-refresh enable

	mov	r7,#254
loop1:	
	subs	r1, r1, #1
	bne	loop1
						@ Now we cannot use MMU we must use TLB hit
	str	r3,[r2]				@ MISCCR Set
	str	r5,[r4]				@ Power Off
	nop					@ waiting for power off
	nop
	nop
	b 	.


/*
 * cpu_s3c2410_resume()
 *
 * entry point from bootloader into kernel during resume
 *
 * Note: Yes, part of the following code is located into the .data section.
 *       This is to allow sleep_save_sp to be accessed with a relative load
 *       while we can't rely on any MMU translation.  We could have put
 *       sleep_save_sp in the .text section as well, but some setups might
 *       insist on .text to be truely read-only.
 */

	.data
	.align 5
ENTRY(s3c2410_cpu_resume)
	mov	r0, #I_BIT | F_BIT | MODE_SVC	@ set SVC, irqs off
	msr	cpsr_c, r0
	
	ldr	r0, sleep_save_sp		@ stack phys addr
 	ldr	r2, =resume_after_mmu		@ its absolute virtual address
	ldmfd	r0, {r4 - r7, sp}		@ CP regs + virt stack ptr

	mov	r1, #0
	mcr	p15, 0, r1, c7, c7, 0   	@ flush I&D cache

	mcr	p15, 0, r1, c7, c7, 0   	@ flush I&D cache
	mcr	p15, 0, r1, c7, c10, 4		@ drain write buffer 
	mcr	p15, 0, r1, c8, c7, 0		@ flush I+D TLBs

	mcr 	p15, 0, r4, c3, c0, 0		@ domain ID
	mcr 	p15, 0, r5, c2, c0, 0		@ translation table base addr
	mcr	p15, 0, r6, c13, c0, 0		@ PID
	b	resume_turn_on_mmu		@ cache align execution

	.align 5
resume_turn_on_mmu:
	mcr 	p15, 0, r7, c1, c0, 0		@ turn on MMU, caches, etc.
	mov	pc, r2				@ jump to virtual addr
	nop

sleep_save_sp:
	.word	0				@ preserve stack phys ptr here

	.text
ENTRY(resume_after_mmu)
	ldmfd	sp!, {r4 - r12, pc}		@ return to caller

