/*
 * Copyright (c) 2016-2021, ARM Limited and Contributors. All rights reserved.
 * Copyright (c) 2020-2023, NVIDIA Corporation. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <arch_helpers.h>
#include <assert.h>
#include <lib/mmio.h>
#include <lib/smccc.h>
#include <services/arm_arch_svc.h>
#include <tegra_def.h>
#include <tegra_platform.h>
#include <tegra_private.h>

/*******************************************************************************
 * Tegra platforms
 ******************************************************************************/
typedef enum tegra_platform {
	TEGRA_PLATFORM_SILICON = 0U,
	TEGRA_PLATFORM_QT,
	TEGRA_PLATFORM_FPGA,
	TEGRA_PLATFORM_EMULATION,
	TEGRA_PLATFORM_LINSIM,
	TEGRA_PLATFORM_UNIT_FPGA,
	TEGRA_PLATFORM_VIRT_DEV_KIT,
	TEGRA_PLATFORM_VSP,
	TEGRA_PLATFORM_MAX,
} tegra_platform_t;

/*******************************************************************************
 * Tegra macros defining all the SoC minor versions
 ******************************************************************************/
#define TEGRA_MINOR_QT			U(0)
#define TEGRA_MINOR_FPGA		U(1)
#define TEGRA_MINOR_ASIM_QT		U(2)
#define TEGRA_MINOR_ASIM_LINSIM		U(3)
#define TEGRA_MINOR_DSIM_ASIM_LINSIM	U(4)
#define TEGRA_MINOR_UNIT_FPGA		U(5)
#define TEGRA_MINOR_VIRT_DEV_KIT	U(6)

/*******************************************************************************
 * Tegra macros defining all the SoC pre_si_platform
 ******************************************************************************/
#define TEGRA_PRE_SI_QT			U(1)
#define TEGRA_PRE_SI_FPGA		U(2)
#define TEGRA_PRE_SI_UNIT_FPGA		U(3)
#define TEGRA_PRE_SI_ASIM_QT		U(4)
#define TEGRA_PRE_SI_ASIM_LINSIM	U(5)
#define TEGRA_PRE_SI_DSIM_ASIM_LINSIM	U(6)
#define TEGRA_PRE_SI_VDK		U(8)
#define TEGRA_PRE_SI_VSP		U(9)

/*
 * Read the chip ID value
 */
static uint32_t tegra_get_chipid(void)
{
#if TEGRA_MISC_BASE
	return (mmio_read_32(TEGRA_MISC_BASE + HARDWARE_REVISION_OFFSET) >>
		CHIP_ID_SHIFT) & CHIP_ID_MASK;
#else
	return 0;
#endif
}

/*
 * Read the chip's major version from chip ID value
 */
static uint32_t tegra_get_chipid_major(void)
{
#if TEGRA_MISC_BASE
	uint32_t val = mmio_read_32(TEGRA_MISC_BASE + HARDWARE_REVISION_OFFSET);
	return (val >> MAJOR_VERSION_SHIFT) & MAJOR_VERSION_MASK;
#else
	return 0;
#endif
}

/*
 * Read the chip's minor version from the chip ID value
 */
static uint32_t tegra_get_chipid_minor(void)
{
#if TEGRA_MISC_BASE
	uint32_t val = mmio_read_32(TEGRA_MISC_BASE + HARDWARE_REVISION_OFFSET);
	return (val >> MINOR_VERSION_SHIFT) & MINOR_VERSION_MASK;
#else
	return 0;
#endif
}

/*
 * Read the chip's pre_si_platform value from the chip ID value
 */
static uint32_t tegra_get_chipid_pre_si_platform(void)
{
#if TEGRA_MISC_BASE
	uint32_t val = mmio_read_32(TEGRA_MISC_BASE + HARDWARE_REVISION_OFFSET);
	return (val >> PRE_SI_PLATFORM_SHIFT) & PRE_SI_PLATFORM_MASK;
#else
	return 0;
#endif
}

/*
 * Helper function to check if the chip's SIPID is supported
 */
static bool tegra_is_sipid_supported(void)
{
#if TEGRA_SIP_MISC_BASE
	return (mmio_read_32(TEGRA_SIP_MISC_BASE + SIP_MISC_MAGIC_OFFSET) ==
		SIP_MISC_MAGIC_VAL);
#else
	return false;
#endif
}

/*
 * Read the chip's SIPID value
 */
static uint32_t tegra_get_sipid(void)
{
#if TEGRA_SIP_MISC_BASE
	return (mmio_read_32(TEGRA_SIP_MISC_BASE + SIP_MISC_ID_OFFSET) &
		SIP_MISC_ID_SIPID_MASK);
#else
	return 0;
#endif
}

/*
 * Read the chip's major version from the SIPID value
 */
static uint32_t tegra_get_sipid_major(void)
{
#if TEGRA_SIP_MISC_BASE
	return (mmio_read_32(TEGRA_SIP_MISC_BASE + SIP_MISC_ID_OFFSET) &
		SIP_MISC_ID_REVISION_MASK) >> SIP_MISC_ID_REVISION_SHIFT;
#else
	return 0;
#endif
}

/*
 * Read the chip's minor version from the SIPID value
 */
static uint32_t tegra_get_sipid_minor(void)
{
#if TEGRA_SIP_MISC_BASE
	return (mmio_read_32(TEGRA_SIP_MISC_BASE + SIP_MISC_FEATURE_OFFSET) &
		SIP_MISC_FEATURE_TBL_VER_MASK) >> SIP_MISC_FEATURE_TBL_VER_SHIFT;
#else
	return 0;
#endif
}

/*
 * Read the chip's pre_si_platform value from the SIP_MISC_FEATURE value
 */
static uint32_t tegra_get_sipid_pre_si_platform(void)
{
#if TEGRA_SIP_MISC_BASE
	return mmio_read_32(TEGRA_SIP_MISC_BASE + SIP_MISC_FEATURE_OFFSET) &
		SIP_MISC_FEATURE_PRE_SI_MASK;
#else
	return 0;
#endif
}

bool tegra_chipid_is_t186(void)
{
	return (tegra_get_chipid() == TEGRA_CHIPID_TEGRA18);
}

bool tegra_chipid_is_t186_a01(void)
{
	return (tegra_get_chipid() == TEGRA_CHIPID_TEGRA18) &&
	       (tegra_get_chipid_major() == 0x1U) &&
	       (tegra_get_chipid_minor() == 0x1U);
}

bool tegra_chipid_is_t210(void)
{
	return (tegra_get_chipid() == TEGRA_CHIPID_TEGRA21);
}

bool tegra_chipid_is_t210_b01(void)
{
	return (tegra_chipid_is_t210() && (tegra_get_chipid_major() == 0x2U));
}

bool tegra_chipid_is_t194(void)
{
	return (tegra_get_chipid() == TEGRA_CHIPID_TEGRA19);
}

bool tegra_chipid_is_t234(void)
{
	return ((tegra_get_chipid() == TEGRA_CHIPID_TEGRA23) &&
		(tegra_get_chipid_major() == 0x4U));
}

bool tegra_chipid_is_t239(void)
{
	return ((tegra_get_chipid() == TEGRA_CHIPID_TEGRA23) &&
		(tegra_get_chipid_major() == 0x9U));
}

bool tegra_chipid_is_th500(void)
{
	return ((tegra_get_chipid() == TEGRA_CHIPID_TEGRA24) &&
		(tegra_get_chipid_major() == 0x1U));
}

/*
 * Read platform type from HIDREV major and minor bits.
 */

static tegra_platform_t from_hidrev_major_minor_rev(void)
{
	uint32_t major = tegra_get_chipid_major();
	uint32_t minor = tegra_get_chipid_minor();
	tegra_platform_t ret;

	if (major == 0U) {
		/*
		 * The minor version number is used by simulation platforms
		 */
		switch (minor) {
		/*
		 * Cadence's QuickTurn emulation system is a Solaris-based
		 * chip emulation system
		 */
		case TEGRA_MINOR_QT:
		case TEGRA_MINOR_ASIM_QT:
			ret = TEGRA_PLATFORM_QT;
			break;

		/*
		 * FPGAs are used during early software/hardware development
		 */
		case TEGRA_MINOR_FPGA:
			ret = TEGRA_PLATFORM_FPGA;
			break;
		/*
		 * Linsim is a reconfigurable, clock-driven, mixed RTL/cmodel
		 * simulation framework.
		 */
		case TEGRA_MINOR_ASIM_LINSIM:
		case TEGRA_MINOR_DSIM_ASIM_LINSIM:
			ret = TEGRA_PLATFORM_LINSIM;
			break;

		/*
		 * Unit FPGAs run the actual hardware block IP on the FPGA with
		 * the other parts of the system using Linsim.
		 */
		case TEGRA_MINOR_UNIT_FPGA:
			ret = TEGRA_PLATFORM_UNIT_FPGA;
			break;
		/*
		 * The Virtualizer Development Kit (VDK) is the standard chip
		 * development from Synopsis.
		 */
		case TEGRA_MINOR_VIRT_DEV_KIT:
			ret = TEGRA_PLATFORM_VIRT_DEV_KIT;
			break;

		default:
			ret = TEGRA_PLATFORM_MAX;
			break;
		}
	} else {
		/* Actual silicon platforms have a non-zero major version */
		ret = TEGRA_PLATFORM_SILICON;
	}

	return ret;
}

/*
 * Read platform type from HIDREV pre_si bits.
 */

static tegra_platform_t from_sipid_hidrev_pre_si_platform(void)
{
	uint32_t pre_si_platform;
	tegra_platform_t ret;

	if (tegra_is_sipid_supported()) {
		pre_si_platform = tegra_get_sipid_pre_si_platform();
	} else {
		pre_si_platform = tegra_get_chipid_pre_si_platform();
	}

	if (pre_si_platform > 0U) {
		switch (pre_si_platform) {
		/*
		 * Cadence's QuickTurn emulation system is a Solaris-based
		 * chip emulation system
		 */
		case TEGRA_PRE_SI_QT:
		case TEGRA_PRE_SI_ASIM_QT:
			ret = TEGRA_PLATFORM_QT;
			break;

		/*
		 * FPGAs are used during early software/hardware development
		 */
		case TEGRA_PRE_SI_FPGA:
			ret = TEGRA_PLATFORM_FPGA;
			break;
		/*
		 * Linsim is a reconfigurable, clock-driven, mixed RTL/cmodel
		 * simulation framework.
		 */
		case TEGRA_PRE_SI_ASIM_LINSIM:
		case TEGRA_PRE_SI_DSIM_ASIM_LINSIM:
			ret = TEGRA_PLATFORM_LINSIM;
			break;

		/*
		 * Unit FPGAs run the actual hardware block IP on the FPGA with
		 * the other parts of the system using Linsim.
		 */
		case TEGRA_PRE_SI_UNIT_FPGA:
			ret = TEGRA_PLATFORM_UNIT_FPGA;
			break;
		/*
		 * The Virtualizer Development Kit (VDK) is the standard chip
		 * development from Synopsis.
		 */
		case TEGRA_PRE_SI_VDK:
			ret = TEGRA_PLATFORM_VIRT_DEV_KIT;
			break;

		/*
		 * The VSP pre-si development platform.
		 */
		case TEGRA_PRE_SI_VSP:
			ret = TEGRA_PLATFORM_VSP;
			break;

		default:
			ret = TEGRA_PLATFORM_MAX;
			break;
		}
	} else {
		/* Actual silicon platforms have a non-zero major version */
		ret = TEGRA_PLATFORM_SILICON;
	}

	return ret;
}

/*
 * Read the chip ID value and derive the platform
 */
static tegra_platform_t tegra_get_platform(void)
{
	tegra_platform_t ret;
	uint32_t chip_id;

	if (tegra_is_sipid_supported()) {
		chip_id = tegra_get_sipid();
	} else {
		/* get the chip_id/major/minor chip ID values */
		chip_id = tegra_get_chipid();
	}

	switch (chip_id) {
	case TEGRA_CHIPID_TEGRA13:
	case TEGRA_CHIPID_TEGRA18:
	case TEGRA_CHIPID_TEGRA21:
		ret = from_hidrev_major_minor_rev();
		break;

	/* For chip id T194 and later */
	default:
		ret = from_sipid_hidrev_pre_si_platform();
		break;
	}

	return ret;
}

bool tegra_platform_is_silicon(void)
{
	return ((tegra_get_platform() == TEGRA_PLATFORM_SILICON) ? true : false);
}

bool tegra_platform_is_qt(void)
{
	return ((tegra_get_platform() == TEGRA_PLATFORM_QT) ? true : false);
}

bool tegra_platform_is_linsim(void)
{
	tegra_platform_t plat = tegra_get_platform();

	return (((plat == TEGRA_PLATFORM_LINSIM) ||
	       (plat == TEGRA_PLATFORM_UNIT_FPGA)) ? true : false);
}

bool tegra_platform_is_fpga(void)
{
	return ((tegra_get_platform() == TEGRA_PLATFORM_FPGA) ? true : false);
}

bool tegra_platform_is_emulation(void)
{
	return (tegra_get_platform() == TEGRA_PLATFORM_EMULATION);
}

bool tegra_platform_is_unit_fpga(void)
{
	return ((tegra_get_platform() == TEGRA_PLATFORM_UNIT_FPGA) ? true : false);
}

bool tegra_platform_is_virt_dev_kit(void)
{
	return ((tegra_get_platform() == TEGRA_PLATFORM_VIRT_DEV_KIT) ? true : false);
}

bool tegra_platform_is_vsp(void)
{
	return ((tegra_get_platform() == TEGRA_PLATFORM_VSP) ? true : false);
}

/*
 * This function returns soc version which mainly consist of below fields
 *
 *  soc_version[30:24] = JEP-106 continuation code for the SiP
 *  soc_version[23:16] = JEP-106 identification code with parity bit for the SiP
 *  soc_version[0:15]  = chip identification
 */
int32_t plat_get_soc_version(void)
{
	uint32_t chip_id;
	uint32_t major_rev;
	uint32_t manfid = SOC_ID_SET_JEP_106(JEDEC_NVIDIA_BKID, JEDEC_NVIDIA_MFID);

	if (tegra_is_sipid_supported()) {
		chip_id = tegra_get_sipid();
		major_rev = tegra_get_sipid_major();
	} else {
		chip_id = tegra_get_chipid();
		major_rev = tegra_get_chipid_major();
	}

	return (int32_t)(manfid | (((chip_id << MAJOR_VERSION_SHIFT) | major_rev) &
			 SOC_ID_IMPL_DEF_MASK));
}

/*
 * This function returns soc revision in below format
 *
 * For Tegra234 onwards, the encoding is
 *   soc_revision[30:0] = minor version number
 *
 * For older SOCs, the encoding is
 *   soc_revision[30:16] = 0
 *   soc_revision[15:8] = major version number
 *   soc_revision[7:0]  = minor version number
 */
int32_t plat_get_soc_revision(void)
{
	int32_t revision;

	/*
	 * From Tegra234 onwards, the minor version field encodes the
	 * revision. For older SOCs, it is a combination of the major and
	 * minor version fields.
	 */
	if (tegra_get_sipid() >= TEGRA_CHIPID_TEGRA410) {
		revision = tegra_get_sipid_minor();
	} else if ((tegra_get_chipid() >= TEGRA_CHIPID_TEGRA23) && (tegra_get_chipid() < TEGRA_CHIPID_TEGRA410)) {
		revision = tegra_get_chipid_minor();
	} else {
		revision = (int32_t)((tegra_get_chipid_major() << 8) |
				       tegra_get_chipid_minor());
	}

	return (revision & SOC_ID_REV_MASK);
}

/*****************************************************************************
 * plat_is_smccc_feature_available() - This function checks whether SMCCC feature
 *                                  is availabile for the platform or not.
 * @fid: SMCCC function id
 *
 * Return SMC_ARCH_CALL_SUCCESS if SMCCC feature is available and
 * SMC_ARCH_CALL_NOT_SUPPORTED otherwise.
 *****************************************************************************/
int32_t plat_is_smccc_feature_available(u_register_t fid)
{
	switch (fid) {
	case SMCCC_ARCH_SOC_ID:
		return SMC_ARCH_CALL_SUCCESS;
	default:
		return SMC_ARCH_CALL_NOT_SUPPORTED;
	}
}
