// SPDX-License-Identifier: GPL-2.0+
/*
 * (C) Copyright 2020 - Cortina Access Inc.
 *
 */
#include <common.h>
#include <malloc.h>
#include <errno.h>
#include <netdev.h>
#include <asm/io.h>
#include <linux/compiler.h>
#include <configs/presidio_asic.h>
#include <linux/psci.h>
#include <asm/psci.h>
#include <cpu_func.h>
#include <asm/armv8/mmu.h>

DECLARE_GLOBAL_DATA_PTR;

#define CA_PERIPH_BASE                  0xE0000000UL
#define CA_PERIPH_SIZE                  0x20000000UL
#define CA_GLOBAL_BASE                  0xf4320000
#define CA_GLOBAL_JTAG_ID               0xf4320000
#define CA_GLOBAL_BLOCK_RESET           0xf4320004
#define CA_GLOBAL_BLOCK_RESET_RESET_DMA BIT(16)
#define CA_DMA_SEC_SSP_BAUDRATE_CTRL    0xf7001b94
#define CA_DMA_SEC_SSP_ID               0xf7001b80

int print_cpuinfo(void)
{
	printf("CPU:   Cortina Presidio G3\n");
	return 0;
}

static struct mm_region presidio_mem_map[] = {
	{
	.virt = DDR_BASE,
	.phys = DDR_BASE,
	.size = PHYS_SDRAM_1_SIZE,
	.attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
		 PTE_BLOCK_OUTER_SHARE
	},
	{
	.virt = CA_PERIPH_BASE,
	.phys = CA_PERIPH_BASE,
	.size = CA_PERIPH_SIZE,
	.attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
		 PTE_BLOCK_NON_SHARE
	},
	{
	/* List terminator */
	0,
	}
};

struct mm_region *mem_map = presidio_mem_map;

static noinline int invoke_psci_fn_smc(u64 function_id, u64 arg0, u64 arg1,
				       u64 arg2)
{
	asm volatile("mov x0, %0\n"
		    "mov x1, %1\n"
		    "mov x2, %2\n"
		    "mov x3, %3\n"
		    "smc	#0\n"
		    : "+r" (function_id)
		    : "r" (arg0), "r" (arg1), "r" (arg2)
		    );

	return function_id;
}

int board_early_init_r(void)
{
	dcache_disable();
	return 0;
}

int board_init(void)
{
	unsigned int reg_data, jtag_id;

	/* Enable timer */
	writel(1, CONFIG_SYS_TIMER_BASE);

	/* Enable snoop in CCI400 slave port#4 */
	writel(3, 0xF5595000);

	jtag_id = readl(CA_GLOBAL_JTAG_ID);

	/* If this is HGU variant then do not use
	 * the Saturn daughter card ref. clk
	 */
	if (jtag_id == 0x1010D8F3) {
		reg_data = readl(0xF3100064);
		/* change multifunc. REF CLK pin to
		 * a simple GPIO pin
		 */
		reg_data |= (1 << 1);
		writel(reg_data, 0xf3100064);
	}

	return 0;
}

int dram_init(void)
{
	unsigned int ddr_size;

	ddr_size = readl(0x111100c);
	gd->ram_size = ddr_size * 0x100000;
	return 0;
}

void reset_cpu(ulong addr)
{
	invoke_psci_fn_smc(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);
}

#ifdef CONFIG_LAST_STAGE_INIT
int last_stage_init(void)
{
	u32 val;

	val = readl(CA_GLOBAL_BLOCK_RESET);
	val &= ~CA_GLOBAL_BLOCK_RESET_RESET_DMA;
	writel(val, CA_GLOBAL_BLOCK_RESET);

	/* reduce output pclk ~3.7Hz to save power consumption */
	writel(0x000000FF, CA_DMA_SEC_SSP_BAUDRATE_CTRL);

	return 0;
}
#endif
