/*
 * Copyright (c) 2018-2025, Arm Limited and Contributors. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <libfdt.h>

#include <arch_helpers.h>
#include <common/debug.h>
#include <common/desc_image_load.h>
#include <drivers/arm/css/sds.h>
#include <lib/fconf/fconf.h>
#include <lib/fconf/fconf_dyn_cfg_getter.h>
#include <plat/arm/common/plat_arm.h>
#include <plat/common/platform.h>
#include <platform_def.h>

#include <nrd_variant.h>

/*
 * Information about the isolated CPUs obtained from SDS.
 */
struct isolated_cpu_mpid_list {
	uint64_t num_entries; /* Number of entries in the list */
	uint64_t mpid_list[PLATFORM_CORE_COUNT]; /* List of isolated CPU MPIDs */
};

/* Function to read isolated CPU MPID list from SDS. */
void plat_arm_nrd_get_isolated_cpu_list(struct isolated_cpu_mpid_list *list)
{
	int ret;

	ret = sds_init(SDS_SCP_AP_REGION_ID);
	if (ret != SDS_OK) {
		ERROR("SDS initialization failed, error: %d\n", ret);
		panic();
	}

	ret = sds_struct_read(SDS_SCP_AP_REGION_ID,
			SDS_ISOLATED_CPU_LIST_ID, 0, &list->num_entries,
			sizeof(list->num_entries), SDS_ACCESS_MODE_CACHED);
	if (ret != SDS_OK) {
		INFO("SDS CPU num elements read failed, error: %d\n", ret);
		list->num_entries = 0;
		return;
	}

	if (list->num_entries > PLATFORM_CORE_COUNT) {
		ERROR("Isolated CPU list count %ld greater than max"
		      " number supported %d\n",
		      list->num_entries, PLATFORM_CORE_COUNT);
		panic();
	} else if (list->num_entries == 0) {
		INFO("SDS isolated CPU list is empty\n");
		return;
	}

	ret = sds_struct_read(SDS_SCP_AP_REGION_ID,
			SDS_ISOLATED_CPU_LIST_ID,
			sizeof(list->num_entries),
			&list->mpid_list,
			sizeof(list->mpid_list[0]) * list->num_entries,
			SDS_ACCESS_MODE_CACHED);
	if (ret != SDS_OK) {
		ERROR("SDS CPU list read failed. error: %d\n", ret);
		panic();
	}
}

/*******************************************************************************
 * This function inserts Platform information via device tree nodes as,
 * system-id {
 *    platform-id = <0>;
 *    config-id = <0>;
 *    isolated-cpu-list = <0>
 * }
 ******************************************************************************/
static int plat_nrd_append_config_node(void)
{
	bl_mem_params_node_t *mem_params;
	void *fdt;
	int nodeoffset, err;
	unsigned int platid = 0, platcfg = 0;
	struct isolated_cpu_mpid_list cpu_mpid_list = {0};

	mem_params = get_bl_mem_params_node(NT_FW_CONFIG_ID);
	if (mem_params == NULL) {
		ERROR("NT_FW CONFIG base address is NULL");
		return -1;
	}

	fdt = (void *)(mem_params->image_info.image_base);

	/* Check the validity of the fdt */
	if (fdt_check_header(fdt) != 0) {
		ERROR("Invalid NT_FW_CONFIG DTB passed\n");
		return -1;
	}

	nodeoffset = fdt_subnode_offset(fdt, 0, "system-id");
	if (nodeoffset < 0) {
		ERROR("Failed to get system-id node offset\n");
		return -1;
	}

	platid = plat_arm_nrd_get_platform_id();
	err = fdt_setprop_u32(fdt, nodeoffset, "platform-id", platid);
	if (err < 0) {
		ERROR("Failed to set platform-id\n");
		return -1;
	}

	platcfg = plat_arm_nrd_get_config_id();
	err = fdt_setprop_u32(fdt, nodeoffset, "config-id", platcfg);
	if (err < 0) {
		ERROR("Failed to set config-id\n");
		return -1;
	}

	platcfg = plat_arm_nrd_get_multi_chip_mode();
	err = fdt_setprop_u32(fdt, nodeoffset, "multi-chip-mode", platcfg);
	if (err < 0) {
		ERROR("Failed to set multi-chip-mode\n");
		return -1;
	}

	plat_arm_nrd_get_isolated_cpu_list(&cpu_mpid_list);
	if (cpu_mpid_list.num_entries > 0) {
		err = fdt_setprop(fdt, nodeoffset, "isolated-cpu-list",
				&cpu_mpid_list,
				(sizeof(cpu_mpid_list.num_entries) *
				(cpu_mpid_list.num_entries + 1)));
		if (err < 0) {
			ERROR("Failed to set isolated-cpu-list, error: %d\n",
			      err);
		}
	}

	flush_dcache_range((uintptr_t)fdt, mem_params->image_info.image_size);

	return 0;
}

/*******************************************************************************
 * This function returns the list of executable images.
 ******************************************************************************/
bl_params_t *plat_get_next_bl_params(void)
{
	struct bl_params *arm_bl_params;
	int ret;

	ret = plat_nrd_append_config_node();
	if (ret != 0)
		panic();

	arm_bl_params = arm_get_next_bl_params();

#if !EL3_PAYLOAD_BASE
	const struct dyn_cfg_dtb_info_t *fw_config_info;
	bl_mem_params_node_t *param_node;
	uintptr_t fw_config_base = 0UL;

	/* Get BL31 image node */
	param_node = get_bl_mem_params_node(BL31_IMAGE_ID);
	assert(param_node != NULL);

	/* Get fw_config load address */
	fw_config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, FW_CONFIG_ID);
	assert(fw_config_info != NULL);

	fw_config_base = fw_config_info->config_addr;
	assert(fw_config_base != 0UL);

	/*
	 * Get the entry point info of next executable image and override
	 * arg1 of entry point info with fw_config base address
	 */
	param_node->ep_info.args.arg1 = (uint64_t)fw_config_base;

#endif
	return arm_bl_params;
}