/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (C) 2016 Broadcom Corporation
 */

#include <asm/asm.h>
#include <asm/regdef.h>
#include <asm/mipsregs.h>
#include <asm/stackframe.h>

#include "pm.h"

	.text
	.set	noreorder
	.align	5

/*
 * a0: u32 params array
 */
LEAF(brcm_pm_do_s2)

	subu	sp, 64
	sw	ra, 0(sp)
	sw	s0, 4(sp)
	sw	s1, 8(sp)
	sw	s2, 12(sp)
	sw	s3, 16(sp)
	sw	s4, 20(sp)
	sw	s5, 24(sp)
	sw	s6, 28(sp)
	sw	s7, 32(sp)

	/*
	 * Dereference the params array
	 * s0: AON_CTRL base register
	 * s1: DDR_PHY base register
	 * s2: TIMERS base register
	 * s3: I-Cache line size
	 * s4: Restart vector address
	 * s5: Restart vector size
	 */
	move	t0, a0

	lw	s0, 0(t0)
	lw	s1, 4(t0)
	lw	s2, 8(t0)
	lw	s3, 12(t0)
	lw	s4, 16(t0)
	lw	s5, 20(t0)

	/* Lock this asm section into the I-cache */
	addiu	t1, s3, -1
	not	t1

	la	t0, brcm_pm_do_s2
	and	t0, t1

	la	t2, asm_end
	and	t2, t1

1:	cache	0x1c, 0(t0)
	bne	t0, t2, 1b
	addu	t0, s3

	/* Lock the interrupt vector into the I-cache */
	move	t0, zero

2:	move	t1, s4
	cache 	0x1c, 0(t1)
	addu	t1, s3
	addu	t0, s3
	ble	t0, s5, 2b
	nop

	sync

	/* Power down request */
	li	t0, PM_S2_COMMAND
	sw	zero, AON_CTRL_PM_CTRL(s0)
	lw	zero, AON_CTRL_PM_CTRL(s0)
	sw	t0, AON_CTRL_PM_CTRL(s0)
	lw	t0, AON_CTRL_PM_CTRL(s0)

	/* Enable CP0 interrupt 2 and wait for interrupt */
	mfc0	t0, CP0_STATUS
	/* Save cp0 sr for restoring later */
	move	s6, t0

	li	t1, ~(ST0_IM | ST0_IE)
	and	t0, t1
	ori	t0, STATUSF_IP2
	mtc0	t0, CP0_STATUS
	nop
	nop
	nop
	ori	t0, ST0_IE
	mtc0	t0, CP0_STATUS

	/* Wait for interrupt */
	wait
	nop

	/* Wait for memc0 */
1:	lw	t0, DDR40_PHY_CONTROL_REGS_0_PLL_STATUS(s1)
	andi	t0, 1
	beqz	t0, 1b
	nop

	/* 1ms delay needed for stable recovery */
	/* Use TIMER1 to count 1 ms */
	li	t0, RESET_TIMER
	sw	t0, TIMER_TIMER1_CTRL(s2)
	lw	t0, TIMER_TIMER1_CTRL(s2)

	li	t0, START_TIMER
	sw	t0, TIMER_TIMER1_CTRL(s2)
	lw	t0, TIMER_TIMER1_CTRL(s2)

	/* Prepare delay */
	li	t0, TIMER_MASK
	lw	t1, TIMER_TIMER1_STAT(s2)
	and	t1, t0
	/* 1ms delay */
	addi	t1, 27000

	/* Wait for the timer value to exceed t1 */
1:	lw	t0, TIMER_TIMER1_STAT(s2)
	sgtu	t2, t1, t0
	bnez	t2, 1b
	nop

	/* Power back up */
	li	t1, 1
	sw	t1, AON_CTRL_HOST_MISC_CMDS(s0)
	lw	t1, AON_CTRL_HOST_MISC_CMDS(s0)

	sw	zero, AON_CTRL_PM_CTRL(s0)
	lw	zero, AON_CTRL_PM_CTRL(s0)

	/* Unlock I-cache */
	addiu	t1, s3, -1
	not	t1

	la	t0, brcm_pm_do_s2
	and 	t0, t1

	la	t2, asm_end
	and	t2, t1

1:	cache	0x00, 0(t0)
	bne	t0, t2, 1b
	addu	t0, s3

	/* Unlock interrupt vector */
	move	t0, zero

2:	move	t1, s4
	cache 	0x00, 0(t1)
	addu	t1, s3
	addu	t0, s3
	ble	t0, s5, 2b
	nop

	/* Restore cp0 sr */
	sync
	nop
	mtc0	s6, CP0_STATUS
	nop

	/* Set return value to success */
	li	v0, 0

	/* Return to caller */
	lw	s7, 32(sp)
	lw	s6, 28(sp)
	lw	s5, 24(sp)
	lw	s4, 20(sp)
	lw	s3, 16(sp)
	lw	s2, 12(sp)
	lw	s1, 8(sp)
	lw	s0, 4(sp)
	lw	ra, 0(sp)
	addiu	sp, 64

	jr ra
	nop
END(brcm_pm_do_s2)

	.globl asm_end
asm_end:
	nop