/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Early initialization code for POWER8/POWER9.
 */

#include <cpu/power/spr.h>

#define FIXUP_ENDIAN						   \
	tdi   0,0,0x48;	  /* Reverse endian of b . + 8		*/ \
	b     $+44;	  /* Skip trampoline if endian is good	*/ \
	.long 0xa600607d; /* mfmsr r11				*/ \
	.long 0x01006b69; /* xori r11,r11,1			*/ \
	.long 0x00004039; /* li r10,0				*/ \
	.long 0x6401417d; /* mtmsrd r10,1			*/ \
	.long 0x05009f42; /* bcl 20,31,$+4			*/ \
	.long 0xa602487d; /* mflr r10				*/ \
	.long 0x14004a39; /* addi r10,r10,20			*/ \
	.long 0xa6035a7d; /* mtsrr0 r10				*/ \
	.long 0xa6037b7d; /* mtsrr1 r11				*/ \
	.long 0x2400004c  /* rfid				*/

/* Load an immediate 64-bit value into a register */
#define LOAD_IMM64(r, e)			\
	lis     r,(e)@highest;			\
	ori     r,r,(e)@higher;			\
	rldicr  r,r, 32, 31;			\
	oris    r,r, (e)@h;			\
	ori     r,r, (e)@l;

.section ".text._start", "ax", %progbits
.globl _start
_start:
	/* QEMU with hb-mode=on starts at address 0x10, while hardware at 0x0 */
	nop
	nop
	nop
	nop
	FIXUP_ENDIAN

	/* Store FDT address provided by QEMU in %r3 to pass it later to
	 * payload */
	mtspr	SPR_HSPRG0, %r3

	/* Set program priority to medium */
	or	%r2, %r2, %r2

	/* Stack */
	lis	%r1, _estack@ha
	addi	%r1, %r1, _estack@l

	/* Clear .bss section */
	/* Currently not needed, .bss is zeroed in the file. If it were to be
	 * used, make sure that .bss is 128B aligned (size of cache line),
	 * otherwise dcbz will clear (part of) .opd section! */
/*
	lis	%r5, _bss@ha
	addi	%r5, %r5, _bss@l
	lis	%r6, _ebss@ha
	addi	%r6, %r6, _ebss@l
	addi	%r6, %r6, -1
1:
	dcbz	0, %r5
	addi	%r5, %r5, 128
	cmpld	cr7, %r5, %r6
	blt	cr7, 1b
*/

	/* This is tested by checkstack() just before jumping to payload */
	LOAD_IMM64(%r3, 0xDEADBEEFDEADBEEF)
	lis	%r5, _stack@ha
	addi	%r5, %r5, _stack@l
	subi	%r5, %r5, 8
	sub	%r4, %r1, %r5
	sradi	%r4, %r4, 3		/* Divide by 8 */
	mtctr	%r4
1:
	stdu	%r3, 8(%r5)
	bc	25, 0, 1b

	/* Enable floating point and vector operations */
	/* Vector operations are sometimes generated for code like
	 * 'uint8_t x[32] = {0}', this results in an exception when vector
	 * registers (VEC) are not enabled. VSX (vector-scalar extension) is
	 * also enabled, there is no reason not to. Floating point must also be
	 * enabled for VSX.
	 */
	mfmsr	%r3
	ori	%r3, %r3, 0x2000	/* FP = 1 */
	oris	%r3, %r3, 0x0280	/* VEC = 1, VSX = 1 */
	mtmsr	%r3

	/* Load official procedure descriptor address for main() */
	lis	%r12, main@ha
	addi	%r12, %r12, main@l

	/* Load TOC pointer and jump to main() */
	ld	%r2, 8(%r12)
	b main