/*******************************************************************************
 * This function is responsible for handling all SiP calls from the NS world
 ******************************************************************************/
uint64_t tegra_sip_handler(uint32_t smc_fid,
			   uint64_t x1,
			   uint64_t x2,
			   uint64_t x3,
			   uint64_t x4,
			   void *cookie,
			   void *handle,
			   uint64_t flags)
{
	uint32_t ns;
	int err;

	/* Determine which security state this SMC originated from */
	ns = is_caller_non_secure(flags);
	if (!ns)
		SMC_RET1(handle, SMC_UNK);

	switch (smc_fid) {

	case TEGRA_SIP_NEW_VIDEOMEM_REGION:

		/*
		 * Check if Video Memory overlaps TZDRAM (contains bl31/bl32)
		 * or falls outside of the valid DRAM range
		 */
		err = bl31_check_ns_address(x1, x2);
		if (err)
			SMC_RET1(handle, err);

		/*
		 * Check if Video Memory is aligned to 1MB.
		 */
		if ((x1 & 0xFFFFF) || (x2 & 0xFFFFF)) {
			ERROR("Unaligned Video Memory base address!\n");
			SMC_RET1(handle, -ENOTSUP);
		}

		/* new video memory carveout settings */
		tegra_memctrl_videomem_setup(x1, x2);

		SMC_RET1(handle, 0);

	default:
		ERROR("%s: unhandled SMC (0x%x)\n", __func__, smc_fid);
		break;
	}

	SMC_RET1(handle, SMC_UNK);
}
/*******************************************************************************
 * This function is responsible for handling all SiP calls
 ******************************************************************************/
uint64_t tegra_sip_handler(uint32_t smc_fid,
			   uint64_t x1,
			   uint64_t x2,
			   uint64_t x3,
			   uint64_t x4,
			   void *cookie,
			   void *handle,
			   uint64_t flags)
{
	uint32_t regval;
	int err;

	/* Check if this is a SoC specific SiP */
	err = plat_sip_handler(smc_fid, x1, x2, x3, x4, cookie, handle, flags);
	if (err == 0)
		SMC_RET1(handle, (uint64_t)err);

	switch (smc_fid) {

	case TEGRA_SIP_NEW_VIDEOMEM_REGION:

		/* clean up the high bits */
		x2 = (uint32_t)x2;

		/*
		 * Check if Video Memory overlaps TZDRAM (contains bl31/bl32)
		 * or falls outside of the valid DRAM range
		 */
		err = bl31_check_ns_address(x1, x2);
		if (err)
			SMC_RET1(handle, err);

		/*
		 * Check if Video Memory is aligned to 1MB.
		 */
		if ((x1 & 0xFFFFF) || (x2 & 0xFFFFF)) {
			ERROR("Unaligned Video Memory base address!\n");
			SMC_RET1(handle, -ENOTSUP);
		}

		/*
		 * The GPU is the user of the Video Memory region. In order to
		 * transition to the new memory region smoothly, we program the
		 * new base/size ONLY if the GPU is in reset mode.
		 */
		regval = mmio_read_32(TEGRA_CAR_RESET_BASE +
				      TEGRA_GPU_RESET_REG_OFFSET);
		if ((regval & GPU_RESET_BIT) == 0U) {
			ERROR("GPU not in reset! Video Memory setup failed\n");
			SMC_RET1(handle, -ENOTSUP);
		}

		/* new video memory carveout settings */
		tegra_memctrl_videomem_setup(x1, x2);

		SMC_RET1(handle, 0);
		break;

	/*
	 * The NS world registers the address of its handler to be
	 * used for processing the FIQ. This is normally used by the
	 * NS FIQ debugger driver to detect system hangs by programming
	 * a watchdog timer to fire a FIQ interrupt.
	 */
	case TEGRA_SIP_FIQ_NS_ENTRYPOINT:

		if (!x1)
			SMC_RET1(handle, SMC_UNK);

		/*
		 * TODO: Check if x1 contains a valid DRAM address
		 */

		/* store the NS world's entrypoint */
		tegra_fiq_set_ns_entrypoint(x1);

		SMC_RET1(handle, 0);
		break;

	/*
	 * The NS world's FIQ handler issues this SMC to get the NS EL1/EL0
	 * CPU context when the FIQ interrupt was triggered. This allows the
	 * NS world to understand the CPU state when the watchdog interrupt
	 * triggered.
	 */
	case TEGRA_SIP_FIQ_NS_GET_CONTEXT:

		/* retrieve context registers when FIQ triggered */
		tegra_fiq_get_intr_context();

		SMC_RET0(handle);
		break;

	case TEGRA_SIP_ENABLE_FAKE_SYSTEM_SUSPEND:
		/*
		 * System suspend fake mode is set if we are on VDK and we make
		 * a debug SIP call. This mode ensures that we excercise debug
		 * path instead of the regular code path to suit the pre-silicon
		 * platform needs. These include replacing the call to WFI by
		 * a warm reset request.
		 */
		if (tegra_platform_is_emulation() != 0U) {

			tegra_fake_system_suspend = 1;
			SMC_RET1(handle, 0);
		}

		/*
		 * We return to the external world as if this SIP is not
		 * implemented in case, we are not running on VDK.
		 */
		break;

	default:
		ERROR("%s: unhandled SMC (0x%x)\n", __func__, smc_fid);
		break;
	}

	SMC_RET1(handle, SMC_UNK);
}
/*******************************************************************************
 * Perform any BL31 specific platform actions. Populate the BL33 and BL32 image
 * info.
 ******************************************************************************/
void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1,
				u_register_t arg2, u_register_t arg3)
{
	struct tegra_bl31_params *arg_from_bl2 = (struct tegra_bl31_params *) arg0;
	plat_params_from_bl2_t *plat_params = (plat_params_from_bl2_t *)arg1;
	image_info_t bl32_img_info = { {0} };
	uint64_t tzdram_start, tzdram_end, bl32_start, bl32_end;
	uint32_t console_clock;
	int32_t ret;

	/*
	 * For RESET_TO_BL31 systems, BL31 is the first bootloader to run so
	 * there's no argument to relay from a previous bootloader. Platforms
	 * might use custom ways to get arguments, so provide handlers which
	 * they can override.
	 */
	if (arg_from_bl2 == NULL) {
		arg_from_bl2 = plat_get_bl31_params();
	}
	if (plat_params == NULL) {
		plat_params = plat_get_bl31_plat_params();
	}

	/*
	 * Copy BL3-3, BL3-2 entry point information.
	 * They are stored in Secure RAM, in BL2's address space.
	 */
	assert(arg_from_bl2 != NULL);
	assert(arg_from_bl2->bl33_ep_info != NULL);
	bl33_image_ep_info = *arg_from_bl2->bl33_ep_info;

	if (arg_from_bl2->bl32_ep_info != NULL) {
		bl32_image_ep_info = *arg_from_bl2->bl32_ep_info;
		bl32_mem_size = arg_from_bl2->bl32_ep_info->args.arg0;
		bl32_boot_params = arg_from_bl2->bl32_ep_info->args.arg2;
	}

	/*
	 * Parse platform specific parameters - TZDRAM aperture base and size
	 */
	assert(plat_params != NULL);
	plat_bl31_params_from_bl2.tzdram_base = plat_params->tzdram_base;
	plat_bl31_params_from_bl2.tzdram_size = plat_params->tzdram_size;
	plat_bl31_params_from_bl2.uart_id = plat_params->uart_id;
	plat_bl31_params_from_bl2.l2_ecc_parity_prot_dis = plat_params->l2_ecc_parity_prot_dis;

	/*
	 * It is very important that we run either from TZDRAM or TZSRAM base.
	 * Add an explicit check here.
	 */
	if ((plat_bl31_params_from_bl2.tzdram_base != (uint64_t)BL31_BASE) &&
	    (TEGRA_TZRAM_BASE != BL31_BASE)) {
		panic();
	}

	/*
	 * Reference clock used by the FPGAs is a lot slower.
	 */
	if (tegra_platform_is_fpga()) {
		console_clock = TEGRA_BOOT_UART_CLK_13_MHZ;
	} else {
		console_clock = TEGRA_BOOT_UART_CLK_408_MHZ;
	}

	/*
	 * Get the base address of the UART controller to be used for the
	 * console
	 */
	tegra_console_base = plat_get_console_from_id(plat_params->uart_id);

	if (tegra_console_base != 0U) {
		/*
		 * Configure the UART port to be used as the console
		 */
		(void)console_init(tegra_console_base, console_clock,
			     TEGRA_CONSOLE_BAUDRATE);
	}

	/*
	 * The previous bootloader passes the base address of the shared memory
	 * location to store the boot profiler logs. Sanity check the
	 * address and initilise the profiler library, if it looks ok.
	 */
	if (plat_params->boot_profiler_shmem_base != 0ULL) {

		ret = bl31_check_ns_address(plat_params->boot_profiler_shmem_base,
				PROFILER_SIZE_BYTES);
		if (ret == (int32_t)0) {

			/* store the membase for the profiler lib */
			plat_bl31_params_from_bl2.boot_profiler_shmem_base =
				plat_params->boot_profiler_shmem_base;

			/* initialise the profiler library */
			boot_profiler_init(plat_params->boot_profiler_shmem_base,
					   TEGRA_TMRUS_BASE);
		}
	}

	/*
	 * Add timestamp for platform early setup entry.
	 */
	boot_profiler_add_record("[TF] early setup entry");

	/*
	 * Initialize delay timer
	 */
	tegra_delay_timer_init();

	/* Early platform setup for Tegra SoCs */
	plat_early_platform_setup();

	/*
	 * Do initial security configuration to allow DRAM/device access.
	 */
	tegra_memctrl_tzdram_setup(plat_bl31_params_from_bl2.tzdram_base,
			(uint32_t)plat_bl31_params_from_bl2.tzdram_size);

	/*
	 * The previous bootloader might not have placed the BL32 image
	 * inside the TZDRAM. We check the BL32 image info to find out
	 * the base/PC values and relocate the image if necessary.
	 */
	if (arg_from_bl2->bl32_image_info != NULL) {

		bl32_img_info = *arg_from_bl2->bl32_image_info;

		/* Relocate BL32 if it resides outside of the TZDRAM */
		tzdram_start = plat_bl31_params_from_bl2.tzdram_base;
		tzdram_end = plat_bl31_params_from_bl2.tzdram_base +
				plat_bl31_params_from_bl2.tzdram_size;
		bl32_start = bl32_img_info.image_base;
		bl32_end = bl32_img_info.image_base + bl32_img_info.image_size;

		assert(tzdram_end > tzdram_start);
		assert(bl32_end > bl32_start);
		assert(bl32_image_ep_info.pc > tzdram_start);
		assert(bl32_image_ep_info.pc < tzdram_end);

		/* relocate BL32 */
		if ((bl32_start >= tzdram_end) || (bl32_end <= tzdram_start)) {

			INFO("Relocate BL32 to TZDRAM\n");

			(void)memcpy16((void *)(uintptr_t)bl32_image_ep_info.pc,
				 (void *)(uintptr_t)bl32_start,
				 bl32_img_info.image_size);

			/* clean up non-secure intermediate buffer */
			zeromem((void *)(uintptr_t)bl32_start,
				bl32_img_info.image_size);
		}
	}

	/*
	 * Add timestamp for platform early setup exit.
	 */
	boot_profiler_add_record("[TF] early setup exit");

	INFO("BL3-1: Boot CPU: %s Processor [%lx]\n",
	     (((read_midr() >> MIDR_IMPL_SHIFT) & MIDR_IMPL_MASK)
	      == DENVER_IMPL) ? "Denver" : "ARM", read_mpidr());
}