406 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
			
		
		
	
	
			406 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
| /*
 | |
|  * 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>
 | |
| 
 | |
| /*
 | |
|  * Kernel mode register usage, see entry.S:
 | |
|  *	k0	scratch
 | |
|  *	k1	scratch
 | |
|  */
 | |
| #define k0	r0
 | |
| #define k1	r1
 | |
| 
 | |
| /* manage self-refresh and enter standby mode. must be self-contained.
 | |
|  * this code will be copied to on-chip memory and executed from there.
 | |
|  */
 | |
| 	.balign 4
 | |
| ENTRY(sh_mobile_sleep_enter_start)
 | |
| 
 | |
| 	/* save mode flags */
 | |
| 	mov.l	r4, @(SH_SLEEP_MODE, r5)
 | |
| 
 | |
| 	/* save original vbr */
 | |
| 	stc	vbr, r0
 | |
| 	mov.l	r0, @(SH_SLEEP_VBR, r5)
 | |
| 
 | |
| 	/* point vbr to our on-chip memory page */
 | |
| 	ldc	r5, vbr
 | |
| 
 | |
| 	/* save return address */
 | |
| 	sts	pr, r0
 | |
| 	mov.l	r0, @(SH_SLEEP_SPC, r5)
 | |
| 
 | |
| 	/* save sr */
 | |
| 	stc	sr, r0
 | |
| 	mov.l	r0, @(SH_SLEEP_SR, r5)
 | |
| 
 | |
| 	/* save general purpose registers to stack if needed */
 | |
| 	mov.l	@(SH_SLEEP_MODE, r5), r0
 | |
| 	tst	#SUSP_SH_REGS, r0
 | |
| 	bt	skip_regs_save
 | |
| 
 | |
| 	sts.l	pr, @-r15
 | |
| 	mov.l	r14, @-r15
 | |
| 	mov.l	r13, @-r15
 | |
| 	mov.l	r12, @-r15
 | |
| 	mov.l	r11, @-r15
 | |
| 	mov.l	r10, @-r15
 | |
| 	mov.l	r9, @-r15
 | |
| 	mov.l	r8, @-r15
 | |
| 
 | |
| 	/* make sure bank0 is selected, save low registers */
 | |
| 	mov.l	rb_bit, r9
 | |
| 	not	r9, r9
 | |
| 	bsr	set_sr
 | |
| 	 mov	#0, r10
 | |
| 
 | |
| 	bsr	save_low_regs
 | |
| 	 nop
 | |
| 
 | |
| 	/* switch to bank 1, save low registers */
 | |
| 	mov.l	rb_bit, r10
 | |
| 	bsr	set_sr
 | |
| 	 mov	#-1, r9
 | |
| 
 | |
| 	bsr	save_low_regs
 | |
| 	 nop
 | |
| 
 | |
| 	/* switch back to bank 0 */
 | |
| 	mov.l	rb_bit, r9
 | |
| 	not	r9, r9
 | |
| 	bsr	set_sr
 | |
| 	 mov	#0, r10
 | |
| 
 | |
| skip_regs_save:
 | |
| 
 | |
| 	/* save sp, also set to internal ram */
 | |
| 	mov.l	r15, @(SH_SLEEP_SP, r5)
 | |
| 	mov	r5, r15
 | |
| 
 | |
| 	/* save stbcr */
 | |
| 	bsr     save_register
 | |
| 	 mov    #SH_SLEEP_REG_STBCR, r0
 | |
| 
 | |
| 	/* save mmu and cache context if needed */
 | |
| 	mov.l	@(SH_SLEEP_MODE, r5), r0
 | |
| 	tst	#SUSP_SH_MMU, r0
 | |
| 	bt	skip_mmu_save_disable
 | |
| 
 | |
| 	/* save mmu state */
 | |
| 	bsr	save_register
 | |
| 	 mov	#SH_SLEEP_REG_PTEH, r0
 | |
| 
 | |
| 	bsr	save_register
 | |
| 	 mov	#SH_SLEEP_REG_PTEL, r0
 | |
| 
 | |
| 	bsr	save_register
 | |
| 	 mov	#SH_SLEEP_REG_TTB, r0
 | |
| 
 | |
| 	bsr	save_register
 | |
| 	 mov	#SH_SLEEP_REG_TEA, r0
 | |
| 
 | |
| 	bsr	save_register
 | |
| 	 mov	#SH_SLEEP_REG_MMUCR, r0
 | |
| 
 | |
| 	bsr	save_register
 | |
| 	 mov	#SH_SLEEP_REG_PTEA, r0
 | |
| 
 | |
| 	bsr	save_register
 | |
| 	 mov	#SH_SLEEP_REG_PASCR, r0
 | |
| 
 | |
| 	bsr	save_register
 | |
| 	 mov	#SH_SLEEP_REG_IRMCR, r0
 | |
| 
 | |
| 	/* invalidate TLBs and disable the MMU */
 | |
| 	bsr	get_register
 | |
| 	 mov	#SH_SLEEP_REG_MMUCR, r0
 | |
| 	mov	#4, r1
 | |
| 	mov.l	r1, @r0
 | |
| 	icbi	@r0
 | |
| 
 | |
| 	/* save cache registers and disable caches */
 | |
| 	bsr	save_register
 | |
| 	 mov	#SH_SLEEP_REG_CCR, r0
 | |
| 
 | |
| 	bsr	save_register
 | |
| 	 mov	#SH_SLEEP_REG_RAMCR, r0
 | |
| 
 | |
| 	bsr	get_register
 | |
| 	 mov	#SH_SLEEP_REG_CCR, r0
 | |
| 	mov	#0, r1
 | |
| 	mov.l	r1, @r0
 | |
| 	icbi	@r0
 | |
| 
 | |
| skip_mmu_save_disable:
 | |
| 	/* call self-refresh entering code if needed */
 | |
| 	mov.l	@(SH_SLEEP_MODE, r5), r0
 | |
| 	tst	#SUSP_SH_SF, r0
 | |
| 	bt	skip_set_sf
 | |
| 
 | |
| 	mov.l	@(SH_SLEEP_SF_PRE, r5), r0
 | |
| 	jsr	@r0
 | |
| 	 nop
 | |
| 
 | |
| skip_set_sf:
 | |
| 	mov.l	@(SH_SLEEP_MODE, r5), r0
 | |
| 	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
 | |
| 
 | |
| 	/* setup BAR register */
 | |
| 	bsr	get_register
 | |
| 	 mov	#SH_SLEEP_REG_BAR, r0
 | |
| 	mov.l	@(SH_SLEEP_RESUME, r5), r1
 | |
| 	mov.l	r1, @r0
 | |
| 
 | |
| 	/* set mode to "r-standby mode" */
 | |
| 	bra	do_sleep
 | |
| 	 mov	#0x20, r1
 | |
| 
 | |
| test_ustandby:
 | |
| 	tst	#SUSP_SH_USTANDBY, r0
 | |
| 	bt	force_sleep
 | |
| 
 | |
| 	/* set mode to "u-standby mode" */
 | |
| 	bra	do_sleep
 | |
| 	 mov	#0x10, r1
 | |
| 
 | |
| force_sleep:
 | |
| 
 | |
| 	/* set mode to "sleep mode" */
 | |
| 	mov	#0x00, r1
 | |
| 
 | |
| do_sleep:
 | |
| 	/* setup and enter selected standby mode */
 | |
| 	bsr     get_register
 | |
| 	 mov    #SH_SLEEP_REG_STBCR, r0
 | |
| 	mov.l	r1, @r0
 | |
| again:
 | |
| 	sleep
 | |
| 	bra	again
 | |
| 	 nop
 | |
| 
 | |
| save_register:
 | |
| 	add	#SH_SLEEP_BASE_ADDR, r0
 | |
| 	mov.l	@(r0, r5), r1
 | |
| 	add	#-SH_SLEEP_BASE_ADDR, r0
 | |
| 	mov.l	@r1, r1
 | |
| 	add	#SH_SLEEP_BASE_DATA, r0
 | |
| 	mov.l	r1, @(r0, r5)
 | |
| 	add	#-SH_SLEEP_BASE_DATA, r0
 | |
| 	rts
 | |
| 	 nop
 | |
| 
 | |
| get_register:
 | |
| 	add	#SH_SLEEP_BASE_ADDR, r0
 | |
| 	mov.l	@(r0, r5), r0
 | |
| 	rts
 | |
| 	 nop
 | |
| 
 | |
| set_sr:
 | |
| 	stc	sr, r8
 | |
| 	and	r9, r8
 | |
| 	or	r10, r8
 | |
| 	ldc	r8, sr
 | |
| 	rts
 | |
| 	 nop
 | |
| 
 | |
| save_low_regs:
 | |
| 	mov.l	r7, @-r15
 | |
| 	mov.l	r6, @-r15
 | |
| 	mov.l	r5, @-r15
 | |
| 	mov.l	r4, @-r15
 | |
| 	mov.l	r3, @-r15
 | |
| 	mov.l	r2, @-r15
 | |
| 	mov.l	r1, @-r15
 | |
| 	rts
 | |
| 	 mov.l	r0, @-r15
 | |
| 
 | |
| 	.balign 4
 | |
| rb_bit:	.long	0x20000000 ! RB=1
 | |
| 
 | |
| ENTRY(sh_mobile_sleep_enter_end)
 | |
| 
 | |
| 	.balign 4
 | |
| ENTRY(sh_mobile_sleep_resume_start)
 | |
| 
 | |
| 	/* figure out start address */
 | |
| 	bsr	0f
 | |
| 	 nop
 | |
| 0:
 | |
| 	sts	pr, k1
 | |
| 	mov.l	1f, k0
 | |
| 	and	k0, k1
 | |
| 
 | |
| 	/* store pointer to data area in VBR */
 | |
| 	ldc	k1, vbr
 | |
| 
 | |
| 	/* setup sr with saved sr */
 | |
| 	mov.l	@(SH_SLEEP_SR, k1), k0
 | |
| 	ldc	k0, sr
 | |
| 
 | |
| 	/* now: user register set! */
 | |
| 	stc	vbr, r5
 | |
| 
 | |
| 	/* setup spc with return address to c code */
 | |
| 	mov.l	@(SH_SLEEP_SPC, r5), r0
 | |
| 	ldc	r0, spc
 | |
| 
 | |
| 	/* restore vbr */
 | |
| 	mov.l	@(SH_SLEEP_VBR, r5), r0
 | |
| 	ldc	r0, vbr
 | |
| 
 | |
| 	/* setup ssr with saved sr */
 | |
| 	mov.l	@(SH_SLEEP_SR, r5), r0
 | |
| 	ldc	r0, ssr
 | |
| 
 | |
| 	/* restore sp */
 | |
| 	mov.l   @(SH_SLEEP_SP, r5), r15
 | |
| 
 | |
| 	/* restore sleep mode register */
 | |
| 	bsr     restore_register
 | |
| 	 mov    #SH_SLEEP_REG_STBCR, r0
 | |
| 
 | |
| 	/* call self-refresh resume code if needed */
 | |
| 	mov.l	@(SH_SLEEP_MODE, r5), r0
 | |
| 	tst	#SUSP_SH_SF, r0
 | |
| 	bt	skip_restore_sf
 | |
| 
 | |
| 	mov.l	@(SH_SLEEP_SF_POST, r5), r0
 | |
| 	jsr	@r0
 | |
| 	 nop
 | |
| 
 | |
| skip_restore_sf:
 | |
| 	/* restore mmu and cache state if needed */
 | |
| 	mov.l	@(SH_SLEEP_MODE, r5), r0
 | |
| 	tst	#SUSP_SH_MMU, r0
 | |
| 	bt	skip_restore_mmu
 | |
| 
 | |
| 	/* restore mmu state */
 | |
| 	bsr	restore_register
 | |
| 	 mov	#SH_SLEEP_REG_PTEH, r0
 | |
| 
 | |
| 	bsr	restore_register
 | |
| 	 mov	#SH_SLEEP_REG_PTEL, r0
 | |
| 
 | |
| 	bsr	restore_register
 | |
| 	 mov	#SH_SLEEP_REG_TTB, r0
 | |
| 
 | |
| 	bsr	restore_register
 | |
| 	 mov	#SH_SLEEP_REG_TEA, r0
 | |
| 
 | |
| 	bsr	restore_register
 | |
| 	 mov	#SH_SLEEP_REG_PTEA, r0
 | |
| 
 | |
| 	bsr	restore_register
 | |
| 	 mov	#SH_SLEEP_REG_PASCR, r0
 | |
| 
 | |
| 	bsr	restore_register
 | |
| 	 mov	#SH_SLEEP_REG_IRMCR, r0
 | |
| 
 | |
| 	bsr	restore_register
 | |
| 	 mov	#SH_SLEEP_REG_MMUCR, r0
 | |
| 	icbi	@r0
 | |
| 
 | |
| 	/* restore cache settings */
 | |
| 	bsr	restore_register
 | |
| 	 mov	#SH_SLEEP_REG_RAMCR, r0
 | |
| 	icbi	@r0
 | |
| 
 | |
| 	bsr	restore_register
 | |
| 	 mov	#SH_SLEEP_REG_CCR, r0
 | |
| 	icbi	@r0
 | |
| 
 | |
| skip_restore_mmu:
 | |
| 
 | |
| 	/* restore general purpose registers if needed */
 | |
| 	mov.l	@(SH_SLEEP_MODE, r5), r0
 | |
| 	tst	#SUSP_SH_REGS, r0
 | |
| 	bt	skip_restore_regs
 | |
| 
 | |
| 	/* switch to bank 1, restore low registers */
 | |
| 	mov.l	_rb_bit, r10
 | |
| 	bsr	_set_sr
 | |
| 	 mov	#-1, r9
 | |
| 
 | |
| 	bsr	restore_low_regs
 | |
| 	 nop
 | |
| 
 | |
| 	/* switch to bank0, restore low registers */
 | |
| 	mov.l	_rb_bit, r9
 | |
| 	not	r9, r9
 | |
| 	bsr	_set_sr
 | |
| 	 mov	#0, r10
 | |
| 
 | |
| 	bsr	restore_low_regs
 | |
| 	 nop
 | |
| 
 | |
| 	/* restore the rest of the registers */
 | |
| 	mov.l	@r15+, r8
 | |
| 	mov.l	@r15+, r9
 | |
| 	mov.l	@r15+, r10
 | |
| 	mov.l	@r15+, r11
 | |
| 	mov.l	@r15+, r12
 | |
| 	mov.l	@r15+, r13
 | |
| 	mov.l	@r15+, r14
 | |
| 	lds.l	@r15+, pr
 | |
| 
 | |
| skip_restore_regs:
 | |
| 	rte
 | |
| 	 nop
 | |
| 
 | |
| restore_register:
 | |
| 	add	#SH_SLEEP_BASE_DATA, r0
 | |
| 	mov.l	@(r0, r5), r1
 | |
| 	add	#-SH_SLEEP_BASE_DATA, r0
 | |
| 	add	#SH_SLEEP_BASE_ADDR, r0
 | |
| 	mov.l	@(r0, r5), r0
 | |
| 	mov.l	r1, @r0
 | |
| 	rts
 | |
| 	 nop
 | |
| 
 | |
| _set_sr:
 | |
| 	stc	sr, r8
 | |
| 	and	r9, r8
 | |
| 	or	r10, r8
 | |
| 	ldc	r8, sr
 | |
| 	rts
 | |
| 	 nop
 | |
| 
 | |
| restore_low_regs:
 | |
| 	mov.l	@r15+, r0
 | |
| 	mov.l	@r15+, r1
 | |
| 	mov.l	@r15+, r2
 | |
| 	mov.l	@r15+, r3
 | |
| 	mov.l	@r15+, r4
 | |
| 	mov.l	@r15+, r5
 | |
| 	mov.l	@r15+, r6
 | |
| 	rts
 | |
| 	 mov.l	@r15+, r7
 | |
| 
 | |
| 	.balign 4
 | |
| _rb_bit:	.long	0x20000000 ! RB=1
 | |
| 1:	.long	~0x7ff
 | |
| ENTRY(sh_mobile_sleep_resume_end)
 | 
