/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * linux/arch/unicore32/kernel/sleep.S
 *
 * Code specific to PKUnity SoC and UniCore ISA
 *
 *	Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
 *	Copyright (C) 2001-2010 Guan Xuetao
 */

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

		.text

pkunity_cpu_save_cp:

	@ get coprocessor registers

	movc	r3, p0.c7, #0			@ PID
	movc	r4, p0.c2, #0			@ translation table base addr
	movc	r5, p0.c1, #0			@ control reg


	@ store them plus current virtual stack ptr on stack
	mov	r6, sp
	stm.w	(r3 - r6), [sp-]

	mov	pc, lr

pkunity_cpu_save_sp:
	@ preserve phys address of stack
	mov	r0, sp
	stw.w	lr, [sp+], #-4
	b.l	sleep_phys_sp
	ldw	r1, =sleep_save_sp
	stw	r0, [r1]
	ldw.w	pc, [sp]+, #4

/*
 * puv3_cpu_suspend()
 *
 * Forces CPU into sleep state.
 *
 * r0 = value for PWRMODE M field for desired sleep state
 */

ENTRY(puv3_cpu_suspend)
	stm.w	(r16 - r27, lr), [sp-]		@ save registers on stack
	stm.w	(r4 - r15), [sp-]		@ save registers on stack

#ifdef	CONFIG_UNICORE_FPU_F64
	sfm.w	(f0  - f7 ), [sp-]
	sfm.w	(f8  - f15), [sp-]
	sfm.w	(f16 - f23), [sp-]
	sfm.w	(f24 - f31), [sp-]
	cff	r4, s31
	stm.w	(r4), [sp-]
#endif
	b.l	pkunity_cpu_save_cp

	b.l	pkunity_cpu_save_sp

	@ clean data cache
	mov	r1, #0
	movc	p0.c5, r1, #14
	nop
	nop
	nop
	nop



	@ DDR2 BaseAddr
	ldw	r0, =(PKUNITY_DDR2CTRL_BASE)

	@ PM BaseAddr
	ldw	r1, =(PKUNITY_PM_BASE)

	@ set PLL_SYS_CFG reg, 275
	movl	r6, #0x00002401
	stw	r6, [r1+], #0x18
	@ set PLL_DDR_CFG reg, 66MHz
	movl	r6, #0x00100c00
	stw	r6, [r1+], #0x1c

	@ set wake up source
	movl	r8, #0x800001ff		@ epip4d
	stw	r8, [r1+], #0xc

	@ set PGSR
	movl	r5, #0x40000
	stw	r5, [r1+], #0x10

	@ prepare DDR2 refresh settings
	ldw	r5, [r0+], #0x24
	or	r5, r5, #0x00000001

	@ prepare PMCR for PLL changing
	movl	r6, #0xc

	@ prepare for closing PLL
	movl	r7, #0x1

	@ prepare sleep mode
	mov	r8, #0x1

@	movl	r0, 0x11111111
@	put_word_ocd r0
	b	pkunity_cpu_do_suspend

	.ltorg
	.align	5
pkunity_cpu_do_suspend:
	b	101f
	@ put DDR2 into self-refresh
100:	stw	r5, [r0+], #0x24
	@ change PLL
	stw	r6, [r1]
	b	1f

	.ltorg
	.align	5
101:	b	102f
	@ wait for PLL changing complete
1:	ldw	r6, [r1+], #0x44
	csub.a	r6, #0x1
	bne	1b
	b	2f

	.ltorg
	.align	5
102:	b	100b
	@ close PLL
2:	stw	r7, [r1+], #0x4
	@ enter sleep mode
	stw	r8, [r1]
3:	b	3b




/*
 * puv3_cpu_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 it to be truly read-only.
 */

	.data
	.align 5
ENTRY(puv3_cpu_resume)
@	movl	r0, 0x20202020
@	put_word_ocd r0

	ldw	r0, sleep_save_sp		@ stack phys addr
	ldw	r2, =resume_after_mmu		@ its absolute virtual address
	ldm	(r3 - r6), [r0]+		@ CP regs + virt stack ptr
	mov	sp, r6				@ CP regs + virt stack ptr

	mov	r1, #0
	movc	p0.c6, r1, #6			@ invalidate I & D TLBs
	movc	p0.c5, r1, #28			@ invalidate I & D caches, BTB

	movc	p0.c7, r3, #0			@ PID
	movc	p0.c2, r4, #0			@ translation table base addr
	movc	p0.c1, r5, #0			@ control reg, turn on mmu
	nop
	jump	r2
	nop
	nop
	nop
	nop
	nop

sleep_save_sp:
	.word	0				@ preserve stack phys ptr here

	.text
resume_after_mmu:
@	movl	r0, 0x30303030
@	put_word_ocd r0

#ifdef	CONFIG_UNICORE_FPU_F64
	lfm.w	(f0  - f7 ), [sp]+
	lfm.w	(f8  - f15), [sp]+
	lfm.w	(f16 - f23), [sp]+
	lfm.w	(f24 - f31), [sp]+
	ldm.w	(r4), [sp]+
	ctf	r4, s31
#endif
	ldm.w	(r4 - r15), [sp]+		@ restore registers from stack
	ldm.w	(r16 - r27, pc), [sp]+		@ return to caller
