/* SPDX-License-Identifier: GPL-2.0
 *
 * arch/sh/kernel/cpu/sh3/swsusp.S
 *
 * Copyright (C) 2009 Magnus Damm
 */
#include <linux/sys.h>
#include <linux/errno.h>
#include <linux/linkage.h>
#include <asm/asm-offsets.h>
#include <asm/page.h>

#define k0	r0
#define k1	r1
#define k2	r2
#define k3	r3
#define k4	r4

! swsusp_arch_resume()
! - copy restore_pblist pages
! - restore registers from swsusp_arch_regs_cpu0

ENTRY(swsusp_arch_resume)
	mov.l	1f, r15
	mov.l	2f, r4
	mov.l	@r4, r4

swsusp_copy_loop:
	mov	r4, r0
	cmp/eq	#0, r0
	bt	swsusp_restore_regs

	mov.l	@(PBE_ADDRESS, r4), r2
	mov.l	@(PBE_ORIG_ADDRESS, r4), r5

	mov	#(PAGE_SIZE >> 10), r3
	shll8	r3
	shlr2	r3 /* PAGE_SIZE / 16 */
swsusp_copy_page:
	dt	r3
	mov.l	@r2+,r1   /*  16n+0 */
	mov.l	r1,@r5
	add	#4,r5
	mov.l	@r2+,r1	  /*  16n+4 */
	mov.l	r1,@r5
	add	#4,r5
	mov.l	@r2+,r1   /*  16n+8 */
	mov.l	r1,@r5
	add	#4,r5
	mov.l	@r2+,r1   /*  16n+12 */
	mov.l	r1,@r5
	bf/s	swsusp_copy_page
	 add	#4,r5

	bra	swsusp_copy_loop
	 mov.l	@(PBE_NEXT, r4), r4

swsusp_restore_regs:
	! BL=0: R7->R0 is bank0
	mov.l	3f, r8
	mov.l	4f, r5
	jsr	@r5
	 nop

	! BL=1: R7->R0 is bank1
	lds	k2, pr
	ldc	k3, ssr

	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
	mov.l	@r15+, r7

	rte
	 nop
	! BL=0: R7->R0 is bank0

	.align	2
1:	.long	swsusp_arch_regs_cpu0
2:	.long	restore_pblist
3:	.long	0x20000000 ! RB=1
4:	.long	restore_regs

! swsusp_arch_suspend()
! - prepare pc for resume, return from function without swsusp_save on resume
! - save registers in swsusp_arch_regs_cpu0
! - call swsusp_save write suspend image

ENTRY(swsusp_arch_suspend)
	sts	pr, r0		! save pr in r0
	mov	r15, r2		! save sp in r2
	mov	r8, r5		! save r8 in r5
	stc	sr, r1
	ldc	r1, ssr		! save sr in ssr
	mov.l	1f, r1
	ldc	r1, spc		! setup pc value for resuming
	mov.l	5f, r15		! use swsusp_arch_regs_cpu0 as stack
	mov.l	6f, r3
	add	r3, r15		! save from top of structure

	! BL=0: R7->R0 is bank0
	mov.l	2f, r3		! get new SR value for bank1
	mov	#0, r4
	mov.l	7f, r1
	jsr	@r1		! switch to bank1 and save bank1 r7->r0
	 not	r4, r4

	! BL=1: R7->R0 is bank1
	stc	r2_bank, k0	! fetch old sp from r2_bank0
	mov.l	3f, k4		! SR bits to clear in k4
	mov.l	8f, k1
	jsr	@k1		! switch to bank0 and save all regs
	 stc	r0_bank, k3	! fetch old pr from r0_bank0

	! BL=0: R7->R0 is bank0
	mov	r2, r15		! restore old sp
	mov	r5, r8		! restore old r8
	stc	ssr, r1
	ldc	r1, sr		! restore old sr
	lds	r0, pr		! restore old pr
	mov.l	4f, r0
	jmp	@r0
	 nop

swsusp_call_save:
	mov	r2, r15		! restore old sp
	mov	r5, r8		! restore old r8
	lds	r0, pr		! restore old pr
	rts
	 mov	#0, r0

	.align	2
1:	.long	swsusp_call_save
2:	.long	0x20000000 ! RB=1
3:	.long	0xdfffffff ! RB=0
4:	.long	swsusp_save
5:	.long	swsusp_arch_regs_cpu0
6:	.long	SWSUSP_ARCH_REGS_SIZE
7:	.long	save_low_regs
8:	.long	save_regs