/*
 * Copyright 2022 Sony Corporation
 */

#include <command.h>
#include <linux/stddef.h>
#include <linux/sizes.h>
#include <memalign.h>
#include <utility.h>
#include <errno.h>
#include <blk.h>
#include <mmc.h>
#include <debug_impl.h>
#include <system_impl.h>
#include <environment.h>
#include <ce_ops_impl.h>
#include <drv_efuse.h>

#define APMCU_RIUBASE	0x1C000000

#define DEV_ID_ELEMENT (6)
#define ELEMENT_BUF_SIZE (5)
#define DID_BUF_SIZE (23)
#define PCI_ENABLE "pci-set"
#define PCI_DISABLE "pci-not-set"

#define IO_READ16(base, offset) \
		(*((unsigned short *)(base + offset)))

#define EFUSE_DID_BASE			(APMCU_RIUBASE + 0x40600)

#define REG_DEVICE_ID_0			(0x0000)    //0x00
#define REG_DEVICE_ID_1			(0x0004)    //0x01
#define REG_DEVICE_ID_2			(0x0008)    //0x02
#define REG_DEVICE_ID_3			(0x0010)    //0x04
#define REG_DEVICE_ID_4			(0x0014)    //0x05
#define REG_DEVICE_ID_5			(0x0018)    //0x06

int check_pci(void)
{
	unsigned short u16SubBank = 0x41; // TVM Product Indicator
	unsigned int u32RegVal = 0;
	unsigned int u32eFuseValue = 0;
	int i_rtn;
	int ret = 0;

	printf("check eFuse pci bit.\n");
	i_rtn = mdrv_efuse_read_subbank_reg_U32((unsigned char)u16SubBank, &u32RegVal);
	if (i_rtn == 0) {
		// offset=18 and length=1
		u32eFuseValue = (u32RegVal & (1 << 18)) >> 18;
	} else {
		UBOOT_ERROR("{%s,%d} mdrv_efuse_read_subbank_reg_U32(u8SubBank:0x%X,) => i_rtn:%d\n", __func__, __LINE__, u16SubBank, i_rtn);
		return -1;
	}
	if (u32eFuseValue & 1) {
		printf("efuse TVW_PCI is set.\n");
		ret = 1;
	} else {
		printf("efuse TVW_PCI is not set.\n");
		ret = 0;
	}
	return ret;
}

int get_device_id(char *des)
{
	unsigned short device_id[DEV_ID_ELEMENT] = {0};
	unsigned char *ptr;
	char num[ELEMENT_BUF_SIZE];
	char did[DID_BUF_SIZE];

	int i;
	int snprintf_len;

	memset(did, 0, DID_BUF_SIZE);

	device_id[0] = IO_READ16(EFUSE_DID_BASE, REG_DEVICE_ID_0);
	device_id[1] = IO_READ16(EFUSE_DID_BASE, REG_DEVICE_ID_1);
	device_id[2] = IO_READ16(EFUSE_DID_BASE, REG_DEVICE_ID_2);
	device_id[3] = IO_READ16(EFUSE_DID_BASE, REG_DEVICE_ID_3);
	device_id[4] = IO_READ16(EFUSE_DID_BASE, REG_DEVICE_ID_4);
	device_id[5] = (IO_READ16(EFUSE_DID_BASE, REG_DEVICE_ID_5) & 0xFF);

	ptr = (unsigned char *)device_id;
	for (i = 10; i >= 0; i--) {
		memset(num, 0, sizeof(num));
		snprintf_len = snprintf(num, sizeof(num)-1, "%02X", ptr[i]);
		if (snprintf_len < 0 || (unsigned int)snprintf_len >= (sizeof(num)-1)) {
			UBOOT_ERROR("The array size is too small(%d), snprintf fail at num'%s'\n", (int)(sizeof(num)-1), num);
			return -1;
		}
		strncat(did, num, DID_BUF_SIZE-1);
	}
	UBOOT_INFO("deviceid_to_bootargs=%s\n", did);

	strncpy(des, did, DID_BUF_SIZE-1);

	return 0;
}

int do_add_device_id(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
	char key_did[ADD_BOOTARGS_KEY_SIZE];
	char set_cfg_did[ADD_BOOTARGS_CFG_SIZE];
	char value_did[ADD_BOOTARGS_CFG_SIZE-ADD_BOOTARGS_KEY_SIZE];
	int ret;
	int snprintf_len;

	memset(value_did, 0, sizeof(value_did));
	if (get_device_id(value_did) != 0) {
		UBOOT_ERROR("{%s,%d} failed to get device id info", __func__, __LINE__);
		return -1;
	}

	memset(key_did, 0, ADD_BOOTARGS_KEY_SIZE);
	memset(set_cfg_did, 0, ADD_BOOTARGS_CFG_SIZE);
	strncpy(key_did, "androidboot.deviceid", sizeof(key_did)-1);

	snprintf_len = snprintf(set_cfg_did, sizeof(set_cfg_did)-1, "%s=%s", key_did, value_did);

	if (snprintf_len < 0 || (unsigned int)snprintf_len >= (sizeof(set_cfg_did)-1)) {
		UBOOT_ERROR("The array size is too small(%d), snprintf fail at set_cfg '%s', need size is %d.\n", (int)(sizeof(set_cfg_did)-1), set_cfg_did, snprintf_len);
		return -1;
	}
	ret = add_bootargs(key_did, set_cfg_did, 0);
	if (ret == -1) {
		UBOOT_ERROR("Add deviceid to bootargs failure\n");
		return -1;
	}
	return 0;
}

U_BOOT_CMD(
	adddid, 1, 0, do_add_device_id,
	"Add device id to bootargs\n",
	NULL
);


int do_add_pci(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
	char key_pci[ADD_BOOTARGS_KEY_SIZE];
	char set_cfg_pci[ADD_BOOTARGS_CFG_SIZE];
	char value_pci[ADD_BOOTARGS_CFG_SIZE-ADD_BOOTARGS_KEY_SIZE];
	int pci;
	int ret;
	int snprintf_len;

	memset(value_pci, 0, sizeof(value_pci));

	pci = check_pci();
	if (pci == 1) {
		strncat(value_pci, PCI_ENABLE, ADD_BOOTARGS_CFG_SIZE-ADD_BOOTARGS_KEY_SIZE-1);
	} else if (pci == 0) {
		strncat(value_pci, PCI_DISABLE, ADD_BOOTARGS_CFG_SIZE-ADD_BOOTARGS_KEY_SIZE-1);
	} else {
		UBOOT_ERROR("Failed to get pci value in fuse.\n");
		return -1;
	}

	memset(key_pci, 0, ADD_BOOTARGS_KEY_SIZE);
	memset(set_cfg_pci, 0, ADD_BOOTARGS_CFG_SIZE);
	strncpy(key_pci, "androidboot.pci", sizeof(key_pci)-1);

	snprintf_len = snprintf(set_cfg_pci, sizeof(set_cfg_pci)-1, "%s=%s", key_pci, value_pci);

	if (snprintf_len < 0 || (unsigned int)snprintf_len >= (sizeof(set_cfg_pci)-1)) {
		UBOOT_ERROR("The array size is too small(%d), snprintf fail at set_cfg '%s', need size is %d.\n", (int)(sizeof(set_cfg_pci)-1), set_cfg_pci, snprintf_len);
		return -1;
	}
	ret = add_bootargs(key_pci, set_cfg_pci, 0);
	if (ret == -1) {
		UBOOT_ERROR("Add deviceid to bootargs failure\n");
		return -1;
	}
	return 0;
}

U_BOOT_CMD(
	addpci, 1, 0, do_add_pci,
	"Check PCI add to bootargs\n",
	NULL
);
