/*
 * arch/sh/kernel/cpu/sh4a/sleep-sh_mobile.S
 *
 * Sleep mode and Standby modes support for SuperH Mobile
 *
 *  Copyright (C) 2009 Magnus Damm
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 */

#include <linux/sys.h>
#include <linux/errno.h>
#include <linux/linkage.h>
#include <asm/asm-offsets.h>
#include <asm/suspend.h>

/* manage self-refresh and enter standby mode.
 * this code will be copied to on-chip memory and executed from there.
 */

	.balign 	4096,0,4096
ENTRY(sh_mobile_standby)
	mov	r4, r0

	tst	#SUSP_SH_SF, r0
	bt	skip_set_sf
#ifdef CONFIG_CPU_SUBTYPE_SH7724
	/* DBSC: put memory in self-refresh mode */

	mov.l	dben_reg, r4
	mov.l	dben_data0, r1
	mov.l	r1, @r4

	mov.l	dbrfpdn0_reg, r4
	mov.l	dbrfpdn0_data0, r1
	mov.l	r1, @r4

	mov.l	dbcmdcnt_reg, r4
	mov.l	dbcmdcnt_data0, r1
	mov.l	r1, @r4

	mov.l	dbcmdcnt_reg, r4
	mov.l	dbcmdcnt_data1, r1
	mov.l	r1, @r4

	mov.l	dbrfpdn0_reg, r4
	mov.l	dbrfpdn0_data1, r1
	mov.l	r1, @r4
#else
	/* SBSC: disable power down and put in self-refresh mode */
	mov.l	1f, r4
	mov.l	2f, r1
	mov.l	@r4, r2
	or	r1, r2
	mov.l   3f, r3
	and	r3, r2
	mov.l	r2, @r4
#endif

skip_set_sf:
	tst	#SUSP_SH_SLEEP, r0
	bt	test_standby

	/* set mode to "sleep mode" */
	bra	do_sleep
	 mov	#0x00, r1

test_standby:
	tst	#SUSP_SH_STANDBY, r0
	bt	test_rstandby

	/* set mode to "software standby mode" */
	bra	do_sleep
	 mov	#0x80, r1

test_rstandby:
	tst	#SUSP_SH_RSTANDBY, r0
	bt	test_ustandby

	/* set mode to "r-standby mode" */
	bra	do_sleep
	 mov	#0x20, r1

test_ustandby:
	tst	#SUSP_SH_USTANDBY, r0
	bt	done_sleep

	/* set mode to "u-standby mode" */
	mov	#0x10, r1

	/* fall-through */

do_sleep:
	/* setup and enter selected standby mode */
	mov.l	5f, r4
	mov.l	r1, @r4
	sleep

done_sleep:
	/* reset standby mode to sleep mode */
	mov.l	5f, r4
	mov	#0x00, r1
	mov.l	r1, @r4

	tst	#SUSP_SH_SF, r0
	bt	skip_restore_sf

#ifdef CONFIG_CPU_SUBTYPE_SH7724
	/* DBSC: put memory in auto-refresh mode */

	mov.l	dbrfpdn0_reg, r4
	mov.l	dbrfpdn0_data0, r1
	mov.l	r1, @r4

	/* sleep 140 ns */
	nop
	nop
	nop
	nop

	mov.l	dbcmdcnt_reg, r4
	mov.l	dbcmdcnt_data0, r1
	mov.l	r1, @r4

	mov.l	dbcmdcnt_reg, r4
	mov.l	dbcmdcnt_data1, r1
	mov.l	r1, @r4

	mov.l	dben_reg, r4
	mov.l	dben_data1, r1
	mov.l	r1, @r4

	mov.l	dbrfpdn0_reg, r4
	mov.l	dbrfpdn0_data2, r1
	mov.l	r1, @r4
#else
	/* SBSC: set auto-refresh mode */
	mov.l	1f, r4
	mov.l	@r4, r2
	mov.l   4f, r3
	and	r3, r2
	mov.l	r2, @r4
	mov.l	6f, r4
	mov.l	7f, r1
	mov.l	8f, r2
	mov.l	@r4, r3
	mov	#-1, r4
	add	r4, r3
	or	r2, r3
	mov.l	r3, @r1
#endif
skip_restore_sf:
	rts
	 nop

	.balign 4
#ifdef CONFIG_CPU_SUBTYPE_SH7724
dben_reg:	.long	0xfd000010 /* DBEN */
dben_data0:	.long	0
dben_data1:	.long	1
dbrfpdn0_reg:	.long	0xfd000040 /* DBRFPDN0 */
dbrfpdn0_data0:	.long	0
dbrfpdn0_data1:	.long	1
dbrfpdn0_data2:	.long	0x00010000
dbcmdcnt_reg:	.long	0xfd000014 /* DBCMDCNT */
dbcmdcnt_data0:	.long	2
dbcmdcnt_data1:	.long	4
#else
1:	.long	0xfe400008 /* SDCR0 */
2:	.long	0x00000400
3:	.long	0xffff7fff
4:	.long	0xfffffbff
#endif
5:	.long	0xa4150020 /* STBCR */
6:	.long   0xfe40001c /* RTCOR */
7:	.long   0xfe400018 /* RTCNT */
8:	.long   0xa55a0000

/* interrupt vector @ 0x600 */
	.balign 	0x400,0,0x400
	.long	0xdeadbeef
	.balign 	0x200,0,0x200
	/* sh7722 will end up here in sleep mode */
	rte
	 nop
sh_mobile_standby_end:

ENTRY(sh_mobile_standby_size)
	.long sh_mobile_standby_end - sh_mobile_standby
