/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (c) ASPEED Technology Inc.
 */
#include <config.h>
#include <asm/armv7.h>
#include <linux/linkage.h>
#include <asm/arch/scu_ast2600.h>

/* SCU register offsets */
#define SCU_BASE		0x1e6e2000
#define SCU_PROT_KEY1		(SCU_BASE + 0x000)
#define SCU_PROT_KEY2		(SCU_BASE + 0x010)
#define SCU_SMP_BOOT		(SCU_BASE + 0x180)
#define SCU_HWSTRAP1		(SCU_BASE + 0x510)
#define SCU_CA7_PARITY_CHK	(SCU_BASE + 0x820)
#define SCU_CA7_PARITY_CLR	(SCU_BASE + 0x824)
#define SCU_MMIO_DEC		(SCU_BASE + 0xc24)

/* FMC SPI register offsets */
#define FMC_BASE		0x1e620000
#define FMC_CE0_CTRL		(FMC_BASE + 0x010)
#define FMC_SW_RST_CTRL		(FMC_BASE + 0x050)
#define FMC_WDT1_CTRL_MODE	(FMC_BASE + 0x060)
#define FMC_WDT2_CTRL_MODE	(FMC_BASE + 0x064)

/*
 * The SMP mailbox provides a space with few instructions in it
 * for secondary cores to execute on and wait for the signal of
 * SMP core bring up.
 *
 *       SMP mailbox
 * +----------------------+
 * |                      |
 * | mailbox insn. for    |
 * | cpuN polling SMP go  |
 * |                      |
 * +----------------------+ 0xC
 * | mailbox ready signal |
 * +----------------------+ 0x8
 * | cpuN GO signal       |
 * +----------------------+ 0x4
 * | cpuN entrypoint      |
 * +----------------------+ SMP_MAILBOX_BASE
 */
#define SMP_MBOX_BASE		(SCU_SMP_BOOT)
#define SMP_MBOX_FIELD_ENTRY	(SMP_MBOX_BASE + 0x0)
#define SMP_MBOX_FIELD_GOSIGN	(SMP_MBOX_BASE + 0x4)
#define SMP_MBOX_FIELD_READY	(SMP_MBOX_BASE + 0x8)
#define SMP_MBOX_FIELD_POLLINSN	(SMP_MBOX_BASE + 0xc)

.macro scu_unlock
	movw	r0, #(SCU_UNLOCK_KEY & 0xffff)
	movt	r0, #(SCU_UNLOCK_KEY >> 16)

	ldr	r1, =SCU_PROT_KEY1
	str	r0, [r1]
	ldr	r1, =SCU_PROT_KEY2
	str	r0, [r1]
.endm

.macro timer_init
	ldr	r1, =SCU_HWSTRAP1
	ldr	r1, [r1]
	and	r1, #0x700
	lsr	r1, #0x8

	/* 1.2GHz */
	cmp	r1, #0x0
	movweq	r0, #0x8c00
	movteq	r0, #0x4786

	/* 1.6GHz */
	cmp	r1, #0x1
	movweq	r0, #0x1000
	movteq	r0, #0x5f5e

	/* 1.2GHz */
	cmp	r1, #0x2
	movweq	r0, #0x8c00
	movteq	r0, #0x4786

	/* 1.6GHz */
	cmp	r1, #0x3
	movweq	r0, #0x1000
	movteq	r0, #0x5f5e

	/* 800MHz */
	cmp	r1, #0x4
	movwge	r0, #0x0800
	movtge	r0, #0x2faf

	mcr	p15, 0, r0, c14, c0, 0	@; update CNTFRQ
.endm


.globl lowlevel_init

lowlevel_init:
#if defined(CONFIG_SPL) && !defined(CONFIG_XPL_BUILD)
	mov	pc, lr
#else
	/* setup ARM arch timer frequency */
	timer_init

	/* reset SMP mailbox as early as possible */
	mov	r0, #0x0
	ldr	r1, =SMP_MBOX_FIELD_READY
	str	r0, [r1]

	/* set ACTLR.SMP to enable cache use */
	mrc	p15, 0, r0, c1, c0, 1
	orr	r0, #0x40
	mcr	p15, 0, r0, c1, c0, 1

	/*
	 * we treat cpu0 as the primary core and
	 * put secondary core (cpuN) to sleep
	 */
	mrc   p15, 0, r0, c0, c0, 5	@; Read CPU ID register
	ands  r0, #0xff			@; Mask off, leaving the CPU ID field
	movw  r2, #0xab00
	movt  r2, #0xabba
	orr   r2, r0

	beq   do_primary_core_setup

	/* hold cpuN until mailbox is ready */
poll_mailbox_ready:
	wfe
	ldr	r0, =SMP_MBOX_FIELD_READY
	ldr	r0, [r0]
	movw	r1, #0xcafe
	movt	r1, #0xbabe
	cmp	r1, r0
	bne	poll_mailbox_ready

	/* parameters for relocated SMP go polling insn. */
	ldr	r0, =SMP_MBOX_FIELD_GOSIGN
	ldr	r1, =SMP_MBOX_FIELD_ENTRY

	/* no return */
	ldr	pc, =SMP_MBOX_FIELD_POLLINSN

do_primary_core_setup:
	scu_unlock

	/* MMIO decode setting */
	ldr	r0, =SCU_MMIO_DEC
	mov	r1, #0x2000
	str	r1, [r0]

	/* enable CA7 cache parity check */
	mov	r0, #0
	ldr	r1, =SCU_CA7_PARITY_CLR
	str	r0, [r1]

	mov	r0, #0x1
	ldr	r1, =SCU_CA7_PARITY_CHK
	str	r0, [r1]

	/* do not fill FMC50[1] if boot from eMMC */
	ldr	r0, =SCU_HWSTRAP1
	ldr	r1, [r0]
	ands	r1, #0x04
	bne	skip_fill_wip_bit

	/* fill FMC50[1] for waiting WIP idle */
	mov	r0, #0x02
	ldr	r1, =FMC_SW_RST_CTRL
	str	r0, [r1]

skip_fill_wip_bit:
	/* disable FMC WDT for SPI address mode detection */
	mov	r0, #0
	ldr	r1, =FMC_WDT1_CTRL_MODE
	str	r0, [r1]

	/* relocate mailbox insn. for cpuN polling SMP go signal */
	adrl	r0, mailbox_insn
	adrl	r1, mailbox_insn_end

	ldr	r2, =#SMP_MBOX_FIELD_POLLINSN

relocate_mailbox_insn:
	ldr	r3, [r0], #0x4
	str	r3, [r2], #0x4
	cmp	r0, r1
	bne	relocate_mailbox_insn

	/* reset SMP go sign */
	mov	r0, #0
	ldr	r1, =SMP_MBOX_FIELD_GOSIGN
	str	r0, [r1]

	/* notify cpuN mailbox is ready */
	movw	r0, #0xCAFE
	movt	r0, #0xBABE
	ldr	r1, =SMP_MBOX_FIELD_READY
	str	r0, [r1]
	sev

	/* back to arch calling code */
	mov	pc, lr

/*
 * insn. inside mailbox to poll SMP go signal.
 *
 * Note that as this code will be relocated, any
 * pc-relative assembly should NOT be used.
 */
mailbox_insn:
	/*
	 * r0 ~ r3 are parameters:
	 *  r0 = SMP_MBOX_FIELD_GOSIGN
	 *  r1 = SMP_MBOX_FIELD_ENTRY
	 *  r2 = per-cpu go sign value
	 *  r3 = no used now
	 */
poll_mailbox_smp_go:
	wfe
	ldr	r4, [r0]
	cmp	r2, r4
	bne	poll_mailbox_smp_go

	/* SMP GO signal confirmed, release cpuN */
	ldr	pc, [r1]

mailbox_insn_end:
	/* should never reach */
	b	.

#endif
