/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (C) 2014 Imagination Technologies Ltd
 *
 * PM helper macros for CPU power off (e.g. Suspend-to-RAM).
 */

#ifndef __ASM_PM_H
#define __ASM_PM_H

#ifdef __ASSEMBLER__

#include <asm/asm-offsets.h>
#include <asm/asm.h>
#include <asm/mipsregs.h>
#include <asm/regdef.h>

/* Save CPU state to stack for suspend to RAM */
.macro SUSPEND_SAVE_REGS
	PTR_SUBU	sp, PT_SIZE
	/* Call preserved GPRs */
	LONG_S	$16, PT_R16(sp)
	LONG_S	$17, PT_R17(sp)
	LONG_S	$18, PT_R18(sp)
	LONG_S	$19, PT_R19(sp)
	LONG_S	$20, PT_R20(sp)
	LONG_S	$21, PT_R21(sp)
	LONG_S	$22, PT_R22(sp)
	LONG_S	$23, PT_R23(sp)
	LONG_S	$28, PT_R28(sp)
	LONG_S	$30, PT_R30(sp)
	LONG_S	$31, PT_R31(sp)
	/* A couple of CP0 registers with space in pt_regs */
	mfc0	k0, CP0_STATUS
	LONG_S	k0, PT_STATUS(sp)
.endm

/* Restore CPU state from stack after resume from RAM */
.macro RESUME_RESTORE_REGS_RETURN
	.set	push
	.set	noreorder
	/* A couple of CP0 registers with space in pt_regs */
	LONG_L	k0, PT_STATUS(sp)
	mtc0	k0, CP0_STATUS
	/* Call preserved GPRs */
	LONG_L	$16, PT_R16(sp)
	LONG_L	$17, PT_R17(sp)
	LONG_L	$18, PT_R18(sp)
	LONG_L	$19, PT_R19(sp)
	LONG_L	$20, PT_R20(sp)
	LONG_L	$21, PT_R21(sp)
	LONG_L	$22, PT_R22(sp)
	LONG_L	$23, PT_R23(sp)
	LONG_L	$28, PT_R28(sp)
	LONG_L	$30, PT_R30(sp)
	LONG_L	$31, PT_R31(sp)
	/* Pop and return */
	jr	ra
	 PTR_ADDIU	sp, PT_SIZE
	.set	pop
.endm

/* Get address of static suspend state into t1 */
.macro LA_STATIC_SUSPEND
	PTR_LA	t1, mips_static_suspend_state
.endm

/* Save important CPU state for early restoration to global data */
.macro SUSPEND_SAVE_STATIC
#ifdef CONFIG_EVA
	/*
	 * Segment configuration is saved in global data where it can be easily
	 * reloaded without depending on the segment configuration.
	 */
	mfc0	k0, CP0_SEGCTL0
	LONG_S	k0, SSS_SEGCTL0(t1)
	mfc0	k0, CP0_SEGCTL1
	LONG_S	k0, SSS_SEGCTL1(t1)
	mfc0	k0, CP0_SEGCTL2
	LONG_S	k0, SSS_SEGCTL2(t1)
#endif
	/* save stack pointer (pointing to GPRs) */
	LONG_S	sp, SSS_SP(t1)
.endm

/* Restore important CPU state early from global data */
.macro RESUME_RESTORE_STATIC
#ifdef CONFIG_EVA
	/*
	 * Segment configuration must be restored prior to any access to
	 * allocated memory, as it may reside outside of the legacy kernel
	 * segments.
	 */
	LONG_L	k0, SSS_SEGCTL0(t1)
	mtc0	k0, CP0_SEGCTL0
	LONG_L	k0, SSS_SEGCTL1(t1)
	mtc0	k0, CP0_SEGCTL1
	LONG_L	k0, SSS_SEGCTL2(t1)
	mtc0	k0, CP0_SEGCTL2
	tlbw_use_hazard
#endif
	/* restore stack pointer (pointing to GPRs) */
	LONG_L	sp, SSS_SP(t1)
.endm

/* flush caches to make sure context has reached memory */
.macro SUSPEND_CACHE_FLUSH
	.extern	__flush_cache_all
	.set	push
	.set	noreorder
	PTR_LA	t1, __flush_cache_all
	LONG_L	t0, 0(t1)
	jalr	t0
	 nop
	.set	pop
 .endm

/* Save suspend state and flush data caches to RAM */
.macro SUSPEND_SAVE
	SUSPEND_SAVE_REGS
	LA_STATIC_SUSPEND
	SUSPEND_SAVE_STATIC
	SUSPEND_CACHE_FLUSH
.endm

/* Restore saved state after resume from RAM and return */
.macro RESUME_RESTORE_RETURN
	LA_STATIC_SUSPEND
	RESUME_RESTORE_STATIC
	RESUME_RESTORE_REGS_RETURN
.endm

#else /* __ASSEMBLER__ */

/**
 * struct mips_static_suspend_state - Core saved CPU state across S2R.
 * @segctl:	CP0 Segment control registers.
 * @sp:		Stack frame where GP register context is saved.
 *
 * This structure contains minimal CPU state that must be saved in static kernel
 * data in order to be able to restore the rest of the state. This includes
 * segmentation configuration in the case of EVA being enabled, as they must be
 * restored prior to any kmalloc'd memory being referenced (even the stack
 * pointer).
 */
struct mips_static_suspend_state {
#ifdef CONFIG_EVA
	unsigned long segctl[3];
#endif
	unsigned long sp;
};

#endif /* !__ASSEMBLER__ */

#endif /* __ASM_PM_HELPERS_H */
