// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2018 MediaTek Inc.
 */

#include <dm/ofnode.h>
#include <log.h>
#include "include/boot_args.h"
#include "include/debug.h"
#include "include/ram_console.h"
#include "include/aee_common.h"
#include "include/uboot_port.h"
#include "dev/mtk_wdt.h"

#include <debug_impl.h>

static int kedump_get_bootreason(unsigned int wdt_status)
{
	unsigned int g_rgu_status = 0;

	if (wdt_status & MTK_WDT_STATUS_HWWDT_RST) {
		/* For E1 bug, that SW reset value is 0xC000,  */
		/* we using "==" to check */
		/* Time out reboot always by pass power key */
		g_rgu_status = RE_BOOT_BY_WDT_HW;
	} else if (wdt_status & MTK_WDT_STATUS_SWWDT_RST) {
		g_rgu_status = RE_BOOT_BY_WDT_SW;
	} else {
		g_rgu_status = RE_BOOT_REASON_UNKNOWN;
	}

	if (wdt_status & MTK_WDT_STATUS_IRQWDT_RST)
		g_rgu_status |= RE_BOOT_WITH_INTTERUPT;

#ifdef MTK_THERMAL_RESET_SUPPORT
	if (wdt_status & MTK_WDT_STATUS_SPM_THERMAL_RST)
		g_rgu_status |= RE_BOOT_BY_SPM_THERMAL;
#endif
	if (wdt_status & MTK_WDT_STATUS_SPMWDT_RST)
		g_rgu_status |= RE_BOOT_BY_SPM;
	if (wdt_status & MTK_WDT_STATUS_THERMAL_CTL_RST)
		g_rgu_status |= RE_BOOT_BY_THERMAL_DIRECT;
	if (wdt_status & MTK_WDT_STATUS_DEBUGWDT_RST)
		g_rgu_status |= RE_BOOT_BY_DEBUG;
	if (wdt_status & MTK_WDT_STATUS_SECURITY_RST)
		g_rgu_status |= RE_BOOT_BY_SECURITY;

#ifdef MTK_PMIC_FULL_RESET
	if (mtk_wdt_is_pmic_full_reset())
		g_rgu_status |= RE_BOOT_BY_PMIC_FULL_RST;
#endif
	return g_rgu_status;
}

/*#define MRDUMP_CB_DRAM_OFF      0x2000
#define MRDUMP_CB_SIZE          0x8000
*/
static void mrdump_ddr_rsv_fdt(void *fdt, int offset, const void *enable)
{
    if (fdt_setprop_string(fdt, offset, "mrdump,ddr_rsv", enable) == 0) {
        printf("%s: set ddr_rsv = %s\n",
        __func__, (char *)enable);
    } else {
        printf("%s: failed to set ddr_rsv %s\n",
        __func__, (char *)enable);
    }
}

/* +++ mrdump control block params +++ */
static int mrdump_cb_fdt(void *fdt, int offset)
{
	u64 addr, size;
	int ret;
	/*const struct reserved_t *cblock_reserved;*/

	if (!fdt) {
		printf("%s: fdt is NULL\n", __func__);
		return -1;
	}

	/*cblock_reserved = mblock_query_reserved_by_name("aee_debug_kinfo", 0);
	  if (cblock_reserved) {
	  if (fdt_setprop_u32(fdt, offset, "mrdump,cblock",
	  cblock_reserved->start + MRDUMP_CB_DRAM_OFF))
	  return -1;

	  if (fdt_appendprop_u32(fdt, offset, "mrdump,cblock", MRDUMP_CB_SIZE))
	  return -1;
	  }*/

	ret = get_mmap_info("/mmap_info/MRDUMP_RESV", &addr, &size);
	if (ret) {
		UBOOT_ERROR("failed to get %s\n", "/mmap_info/MRDUMP_RESV");
		return -1;
	}

	// mediatek,aee_debug_kinfo
	if (fdt_setprop_u32(fdt, offset, "mrdump,cblock",
				(u32)addr + MRDUMP_CB_DRAM_OFF))
		return -1;

	if (fdt_appendprop_u32(fdt, offset, "mrdump,cblock", MRDUMP_CB_SIZE))
		return -1;

	return 1;
}

#define MRDUMP_GO_DUMP "MRDUMP11"
static void mrdump_lk_version_fdt(void *fdt, int offset, const void *lk_version)
{
	if (!fdt) {
		printf("%s: fdt is NULL\n", __func__);
		return;
	}

	if (fdt_setprop_string(fdt, offset, "mrdump,lk", lk_version) == 0) {
		printf("%s: set lk_version = %s\n",
				__func__, (char *)lk_version);
	} else {
		printf("%s: failed to set lk_version %s\n",
				__func__, (char *)lk_version);
	}
}

void mrdump_params_fdt_init(void *fdt)
{
	int chosen_offset;

	if (!fdt) {
		printf("fdt is NULL");
		return;
	}

	chosen_offset = fdt_path_offset(fdt, "/chosen");
	if (chosen_offset < 0) {
		printf("chosen not found\n");
		return;
	}

	if (fdt_setprop_string(fdt, chosen_offset, "aee,enable", "mini") != 0)
		printf("failed to set aee,enable node\n");

	/*switch (aee_check_enable()) {
		case AEE_ENABLE_NO:
			if (fdt_setprop_string(fdt, chosen_offset, "aee,enable", "no") != 0)
				printf("failed to set aee,enable node\n");
			return;
		case AEE_ENABLE_MINI:
			if (fdt_setprop_string(fdt, chosen_offset, "aee,enable", "mini") != 0)
				printf("failed to set aee,enable node\n");
			break;
		case AEE_ENABLE_FULL:
			if (fdt_setprop_string(fdt, chosen_offset, "aee,enable", "full") != 0)
				printf("failed to set aee,enable node\n");
			break;
		default:
			ASSERT(0);
			return;
	}*/

	chosen_offset = fdt_path_offset(fdt, "/chosen");
	if (chosen_offset < 0) {
		printf("chosen not found\n");
		return;
	}

/*#if LK_DEBUGLEVEL == 0
	if (fdt_setprop_string(fdt, chosen_offset, "aee,load_type", "user") != 0)
		printf("Error: failed to set aee,load_type=user node\n");
#elif LK_DEBUGLEVEL == 1*/
	if (fdt_setprop_string(fdt, chosen_offset, "aee,load_type", "userdebug") != 0)
		printf("Error: failed to set aee,load_type=userdebug node\n");
/*#elif LK_DEBUGLEVEL == 2
	if (fdt_setprop_string(fdt, chosen_offset, "aee,load_type", "eng") != 0)
		printf("Error: failed to set aee,load_type=eng node\n");
#endif*/

/*#ifdef MTK_MRDUMP_ENABLE
	if (fdt_setprop_string(fdt, chosen_offset, "aee,support_usb", "yes") != 0)
		printf("Error: failed to set aee,support_usb=yes node\n");
#else*/
	if (fdt_setprop_string(fdt, chosen_offset, "aee,support_usb", "no") != 0)
		printf("Error: failed to set aee,support_usb=no node\n");
/*#endif*/

	if (fdt_setprop_string(fdt, chosen_offset, "aee,encrypt_version", ZIPVERSION_NEEDED) != 0)
		printf("Error: failed to set aee,encrypt_version=%s node\n", ZIPVERSION_NEEDED);

#if 0
	bool encrypt = aee_encrypt_enable();
	if (encrypt) {
		if (fdt_setprop_string(fdt, chosen_offset, "aee,DB_PUBKEY", DB_PUBKEY))
			printf("Error: fdt_setprop_string failed\n");

		if (fdt_setprop_string(fdt, chosen_offset, "aee,DB_PLATFORM_NAME", DB_PLATFORM_NAME))
			printf("Error: fdt_setprop_string failed\n");

		if (fdt_setprop_string(fdt, chosen_offset, "aee,encrypt_enable", "yes") != 0)
			printf("Error: failed to set aee,encrypt_enable=yes node\n");
	} else {
		if (fdt_setprop_string(fdt, chosen_offset, "aee,encrypt_enable", "no") != 0)
			printf("Error: failed to set aee,enable node\n");
	}

	printf("Set aee,encrypt_enable=%s\n", encrypt ? "yes" : "no");
#else
	if (fdt_setprop_string(fdt, chosen_offset, "aee,encrypt_enable", "no") != 0)
		printf("Error: failed to set aee,enable node\n");
#endif

	/* ddr rsv */
#if 0
	printf("ddr reserve status(%x).\n", aee_ddr_reserve_ready());

	if (aee_ddr_reserve_ready() != AEE_MRDUMP_DDR_RSV_READY)
		mrdump_ddr_rsv_fdt(fdt, chosen_offset, "no");
	else {
		if (aee_check_enable() == AEE_ENABLE_NO)
			mrdump_ddr_rsv_fdt(fdt, chosen_offset, "no");
		else
			mrdump_ddr_rsv_fdt(fdt, chosen_offset, "yes");
	}
#else
	mrdump_ddr_rsv_fdt(fdt, chosen_offset, "no");
#endif

	/* mrdump control block params */
	if (mrdump_cb_fdt(fdt, chosen_offset) <= 0)
		printf("failed to set mrdump,cblock entry\n");

	/* mrdump lk_version */
	mrdump_lk_version_fdt(fdt, chosen_offset, MRDUMP_GO_DUMP);
	/*return 0;*/

	// set mboot param
	mboot_params_dts_write(fdt);
}

#define MMAP_ENTRY_NEMBER	(4)
#define SHIFT_32_BIT	(32)

static int test_mmap(void)
{
	ofnode node;
	u32 addr[MMAP_ENTRY_NEMBER];
	int ret;
	int i;
	char *mrdump_used_mem[] = {
		"/mmap_info/ram-console-reserved-memory",
		"/mmap_info/minirdump-reserved-memory",
		"/mmap_info/mrdump-reserve",
	};

	UBOOT_INFO("mrdump: parse mmap\n");
	for (i = 0; i < 3; i++) {
		printf("mark_dbg parse %s\n", mrdump_used_mem[i]);
		node = ofnode_path(mrdump_used_mem[i]);
		if (!ofnode_valid(node)) {
			printf("%s node not found!\n", mrdump_used_mem[i]);
			continue;
		}

		ret = ofnode_read_u32_array(node, "reg", addr, MMAP_ENTRY_NEMBER);
		if (ret) {
			printf("fail to parse %s\n", mrdump_used_mem[i]);
			continue;
		}

		printf("%s with addr %lx size %lx\n", mrdump_used_mem[i],
				(((ulong)addr[0] << SHIFT_32_BIT) | addr[1]),
				(((ulong)addr[2] << SHIFT_32_BIT) | addr[3]));
	}

	return 0;
}

/*
 * "/mmap_info/ram-console-reserved-memory",
 * "/mmap_info/minirdump-reserved-memory",
 * "/mmap_info/mrdump-reserve",
 */
int get_mmap_info(const char* path, u64 *addr, u64 *size)
{
	ofnode node;
	u32 regs[MMAP_ENTRY_NEMBER];
	int ret;

	node = ofnode_path(path);
	if (!ofnode_valid(node)) {
		UBOOT_ERROR("%s node not found!\n", path);
		return -ENODEV;
	}

	ret = ofnode_read_u32_array(node, "reg", regs, MMAP_ENTRY_NEMBER);
	if (ret) {
		UBOOT_ERROR("fail to parse %s\n", path);
		return -ENODEV;
	}

	*addr = ((ulong)regs[0] << SHIFT_32_BIT) | regs[1];
	*size = (((ulong)regs[2] << SHIFT_32_BIT) | regs[3]);
	UBOOT_INFO("%s with addr %llx size %llx\n", path, *addr, *size);
	return 0;
}

int kedump_init(void)
{
	static int kedump_dumped;
	u32 reg_boot_reason = 0;
	unsigned int boot_reason;
#if 0
	u64 addr, size;
#endif

	test_mmap();
#if 0
	get_mmap_info("/mmap_info/RAM_CONSOLE_RESV", &addr, &size);
	printf("/mmap_info/RAM_CONSOLE_RESV %llx %llx %p\n", addr, size, phys_to_virt(addr));
	memset(phys_to_virt(addr), 0xa, size);

	get_mmap_info("/mmap_info/MINIRDUMP_RESV", &addr, &size);
	printf("/mmap_info/MINIRDUMP_RESV %llx %llx %p\n", addr, size, phys_to_virt(addr));
	memset(phys_to_virt(addr), 0xa, size);

	get_mmap_info("/mmap_info/MRDUMP_RESV", &addr, &size);
	printf("/mmap_info/MRDUMP_RESV %llx %llx %p\n", addr, size, phys_to_virt(addr));
	memset(phys_to_virt(addr), 0xa, size);
#endif
	/*mrdump_params_fdt_init(fdt);*/
#if 0
	reg_boot_reason = get_wdt_sta_from_boot_args();
#else // mark_dbg
	reg_boot_reason = RE_BOOT_BY_WDT_SW;
#endif
	if(reg_boot_reason == -1) {
		aee_pr_crit("get wdt_sta fail, set to 2\n");
		boot_reason = 2;
	} else {
		boot_reason = kedump_get_bootreason(reg_boot_reason);
	}

	// info from pl
	pl_ram_console_init();
	// save boot reason to ramconsole
	ram_console_reboot_reason_save(boot_reason);
	ram_console_init();
	printf("mark_dbg: mboot param init\n");
	mboot_params_init(0);

	/* this flow should be executed once only */
	if (kedump_dumped == 0) {
		if (ram_console_is_abnormal_boot())
			return 1;
	}

	return 0;
}
