// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright 2022-2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
 *
 * Authors:
 *   Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
 */
#include <dm.h>
#include <mapmem.h>
#include <string.h>
#include <asm/global_data.h>
#include <asm/sandbox_arm_ffa.h>
#include <asm/sandbox_arm_ffa_priv.h>
#include <dm/device-internal.h>
#include <dm/lists.h>
#include <dm/root.h>
#include <linux/errno.h>
#include <linux/sizes.h>

DECLARE_GLOBAL_DATA_PTR;

/* The partitions (SPs) table */
static struct ffa_partition_desc sandbox_partitions[SANDBOX_PARTITIONS_CNT] = {
	{
		.info = { .id = SANDBOX_SP1_ID, .exec_ctxt = 0x5687, .properties = 0x89325621 },
		.sp_uuid = {
			.a1 = SANDBOX_SERVICE1_UUID_A1,
			.a2 = SANDBOX_SERVICE1_UUID_A2,
			.a3 = SANDBOX_SERVICE1_UUID_A3,
			.a4 = SANDBOX_SERVICE1_UUID_A4,
		}
	},
	{
		.info = { .id = SANDBOX_SP3_ID, .exec_ctxt = 0x7687, .properties = 0x23325621 },
		.sp_uuid = {
			.a1 = SANDBOX_SERVICE2_UUID_A1,
			.a2 = SANDBOX_SERVICE2_UUID_A2,
			.a3 = SANDBOX_SERVICE2_UUID_A3,
			.a4 = SANDBOX_SERVICE2_UUID_A4,
		}
	},
	{
		.info = { .id = SANDBOX_SP2_ID, .exec_ctxt = 0x9587, .properties = 0x45325621 },
		.sp_uuid = {
			.a1 = SANDBOX_SERVICE1_UUID_A1,
			.a2 = SANDBOX_SERVICE1_UUID_A2,
			.a3 = SANDBOX_SERVICE1_UUID_A3,
			.a4 = SANDBOX_SERVICE1_UUID_A4,
		}
	},
	{
		.info = { .id = SANDBOX_SP4_ID, .exec_ctxt = 0x1487, .properties = 0x70325621 },
		.sp_uuid = {
			.a1 = SANDBOX_SERVICE2_UUID_A1,
			.a2 = SANDBOX_SERVICE2_UUID_A2,
			.a3 = SANDBOX_SERVICE2_UUID_A3,
			.a4 = SANDBOX_SERVICE2_UUID_A4,
		}
	}

};

/* The emulator functions */

/**
 * sandbox_ffa_version() - Emulated FFA_VERSION handler function
 * @emul: The sandbox FF-A emulator device
 * @pargs: The SMC call input arguments a0-a7
 * @res:  The SMC return data
 *
 * Emulate FFA_VERSION FF-A function.
 *
 * Return:
 *
 * 0 on success. Otherwise, failure
 */

static int sandbox_ffa_version(struct udevice *emul, ffa_value_t *pargs, ffa_value_t *res)
{
	struct sandbox_ffa_emul *priv = dev_get_priv(emul);

	priv->fwk_version = FFA_VERSION_1_0;
	res->a0 = priv->fwk_version;

	/* x1-x7 MBZ */
	memset(FFA_X1X7_MBZ_REG_START, 0, FFA_X1X7_MBZ_CNT * sizeof(ulong));

	return 0;
}

/**
 * sandbox_ffa_id_get() - Emulated FFA_ID_GET handler function
 * @emul: The sandbox FF-A emulator device
 * @pargs: The SMC call input arguments a0-a7
 * @res:  The SMC return data
 *
 * Emulate FFA_ID_GET FF-A function.
 *
 * Return:
 *
 * 0 on success. Otherwise, failure
 */
static int sandbox_ffa_id_get(struct udevice *emul, ffa_value_t *pargs, ffa_value_t *res)
{
	struct sandbox_ffa_emul *priv = dev_get_priv(emul);

	res->a0 = FFA_SMC_32(FFA_SUCCESS);
	res->a1 = 0;

	priv->id = NS_PHYS_ENDPOINT_ID;
	res->a2 = priv->id;

	/* x3-x7 MBZ */
	memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong));

	return 0;
}

/**
 * sandbox_ffa_features() - Emulated FFA_FEATURES handler function
 * @pargs: The SMC call input arguments a0-a7
 * @res:  The SMC return data
 *
 * Emulate FFA_FEATURES FF-A function.
 *
 * Return:
 *
 * 0 on success. Otherwise, failure
 */
static int sandbox_ffa_features(ffa_value_t *pargs, ffa_value_t *res)
{
	res->a1 = 0;

	if (pargs->a1 == FFA_SMC_64(FFA_RXTX_MAP)) {
		res->a0 = FFA_SMC_32(FFA_SUCCESS);
		res->a2 = RXTX_BUFFERS_MIN_SIZE;
		res->a3 = 0;
		/* x4-x7 MBZ */
		memset(FFA_X4X7_MBZ_REG_START, 0, FFA_X4X7_MBZ_CNT * sizeof(ulong));
		return 0;
	}

	res->a0 = FFA_SMC_32(FFA_ERROR);
	res->a2 = -NOT_SUPPORTED;
	/* x3-x7 MBZ */
	memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong));
	log_err("FF-A interface %lx not implemented\n", pargs->a1);

	return ffa_to_std_errmap[NOT_SUPPORTED];
}

/**
 * sandbox_ffa_partition_info_get() - Emulated FFA_PARTITION_INFO_GET handler
 * @emul: The sandbox FF-A emulator device
 * @pargs: The SMC call input arguments a0-a7
 * @res:  The SMC return data
 *
 * Emulate FFA_PARTITION_INFO_GET FF-A function.
 *
 * Return:
 *
 * 0 on success. Otherwise, failure
 */
static int sandbox_ffa_partition_info_get(struct udevice *emul, ffa_value_t *pargs,
					  ffa_value_t *res)
{
	struct ffa_partition_info *rxbuf_desc_info = NULL;
	u32 descs_cnt;
	u32 descs_size_bytes;
	int ret;
	struct sandbox_ffa_emul *priv = dev_get_priv(emul);

	res->a0 = FFA_SMC_32(FFA_ERROR);

	if (!priv->pair.rxbuf) {
		res->a2 = -DENIED;
		ret = ffa_to_std_errmap[DENIED];
		goto cleanup;
	}

	if (priv->pair_info.rxbuf_owned) {
		res->a2 = -BUSY;
		ret = ffa_to_std_errmap[BUSY];
		goto cleanup;
	}

	if (!priv->partitions.descs) {
		priv->partitions.descs = sandbox_partitions;
		priv->partitions.count = SANDBOX_PARTITIONS_CNT;
	}

	descs_size_bytes = SANDBOX_PARTITIONS_CNT *
		sizeof(struct ffa_partition_desc);

	/* Abort if the RX buffer size is smaller than the descs buffer size */
	if ((priv->pair_info.rxtx_buf_size * SZ_4K) < descs_size_bytes) {
		res->a2 = -NO_MEMORY;
		ret = ffa_to_std_errmap[NO_MEMORY];
		goto cleanup;
	}

	rxbuf_desc_info = priv->pair.rxbuf;

	/* No UUID specified. Return the information of all partitions */
	if (!pargs->a1 && !pargs->a2 && !pargs->a3 && !pargs->a4) {
		for (descs_cnt = 0; descs_cnt < SANDBOX_PARTITIONS_CNT; descs_cnt++)
			*(rxbuf_desc_info++) = priv->partitions.descs[descs_cnt].info;

		res->a0 = FFA_SMC_32(FFA_SUCCESS);
		res->a2 = SANDBOX_PARTITIONS_CNT;
		/* Transfer ownership to the consumer: the non secure world */
		priv->pair_info.rxbuf_owned = 1;
		ret = 0;

		goto cleanup;
	}

	/* A UUID specified. Return the info of all SPs matching the UUID */

	for (descs_cnt = 0 ; descs_cnt < SANDBOX_PARTITIONS_CNT ; descs_cnt++)
		if (pargs->a1 == priv->partitions.descs[descs_cnt].sp_uuid.a1 &&
		    pargs->a2 == priv->partitions.descs[descs_cnt].sp_uuid.a2 &&
		    pargs->a3 == priv->partitions.descs[descs_cnt].sp_uuid.a3 &&
		    pargs->a4 == priv->partitions.descs[descs_cnt].sp_uuid.a4) {
			*(rxbuf_desc_info++) = priv->partitions.descs[descs_cnt].info;
		}

	if (rxbuf_desc_info != priv->pair.rxbuf) {
		res->a0 = FFA_SMC_32(FFA_SUCCESS);
		/* Store the partitions count */
		res->a2 = (ulong)
			(rxbuf_desc_info - (struct ffa_partition_info *)
			 priv->pair.rxbuf);
		ret = 0;

		/* Transfer ownership to the consumer: the non secure world */
		priv->pair_info.rxbuf_owned = 1;
	} else {
		/* Unrecognized UUID */
		res->a2 = -INVALID_PARAMETERS;
		ret = ffa_to_std_errmap[INVALID_PARAMETERS];
	}

cleanup:

	log_err("FFA_PARTITION_INFO_GET (%ld)\n", res->a2);

	res->a1 = 0;

	/* x3-x7 MBZ */
	memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong));

	return ret;
}

/**
 * sandbox_ffa_rxtx_map() - Emulated FFA_RXTX_MAP handler
 * @emul: The sandbox FF-A emulator device
 * @pargs: The SMC call input arguments a0-a7
 * @res:  The SMC return data
 *
 * Emulate FFA_RXTX_MAP FF-A function.
 *
 * Return:
 *
 * 0 on success. Otherwise, failure
 */
static int sandbox_ffa_rxtx_map(struct udevice *emul, ffa_value_t *pargs, ffa_value_t *res)
{
	int ret;
	struct sandbox_ffa_emul *priv = dev_get_priv(emul);

	res->a0 = FFA_SMC_32(FFA_ERROR);

	if (priv->pair.txbuf && priv->pair.rxbuf) {
		res->a2 = -DENIED;
		ret = ffa_to_std_errmap[DENIED];
		goto feedback;
	}

	if (pargs->a3 >= RXTX_BUFFERS_MIN_PAGES && pargs->a1 && pargs->a2) {
		priv->pair.txbuf = map_sysmem(pargs->a1, 0);
		priv->pair.rxbuf = map_sysmem(pargs->a2, 0);
		priv->pair_info.rxtx_buf_size = pargs->a3;
		priv->pair_info.rxbuf_mapped = 1;
		res->a0 = FFA_SMC_32(FFA_SUCCESS);
		res->a2 = 0;
		ret = 0;
		goto feedback;
	}

	if (!pargs->a1 || !pargs->a2) {
		res->a2 = -INVALID_PARAMETERS;
		ret = ffa_to_std_errmap[INVALID_PARAMETERS];
	} else {
		res->a2 = -NO_MEMORY;
		ret = ffa_to_std_errmap[NO_MEMORY];
	}

	log_err("Error in FFA_RXTX_MAP arguments (%d)\n",
		(int)res->a2);

feedback:

	res->a1 = 0;

	/* x3-x7 MBZ */
	memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong));

	return ret;
}

/**
 * sandbox_ffa_rxtx_unmap() - Emulated FFA_RXTX_UNMAP handler
 * @emul: The sandbox FF-A emulator device
 * @pargs: The SMC call input arguments a0-a7
 * @res:  The SMC return data
 *
 * Emulate FFA_RXTX_UNMAP FF-A function.
 *
 * Return:
 *
 * 0 on success. Otherwise, failure
 */
static int sandbox_ffa_rxtx_unmap(struct udevice *emul, ffa_value_t *pargs, ffa_value_t *res)
{
	int ret;
	struct sandbox_ffa_emul *priv = dev_get_priv(emul);

	res->a0 = FFA_SMC_32(FFA_ERROR);
	res->a2 = -INVALID_PARAMETERS;
	ret = ffa_to_std_errmap[INVALID_PARAMETERS];

	if (GET_NS_PHYS_ENDPOINT_ID(pargs->a1) != priv->id)
		goto feedback;

	if (priv->pair.txbuf && priv->pair.rxbuf) {
		priv->pair.txbuf = 0;
		priv->pair.rxbuf = 0;
		priv->pair_info.rxtx_buf_size = 0;
		priv->pair_info.rxbuf_mapped = 0;
		res->a0 = FFA_SMC_32(FFA_SUCCESS);
		res->a2 = 0;
		ret = 0;
		goto feedback;
	}

	log_err("No buffer pair registered on behalf of the caller\n");

feedback:

	res->a1 = 0;

	/* x3-x7 MBZ */
	memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong));

	return ret;
}

/**
 * sandbox_ffa_rx_release() - Emulated FFA_RX_RELEASE handler
 * @emul: The sandbox FF-A emulator device
 * @pargs: The SMC call input arguments a0-a7
 * @res:  The SMC return data
 *
 * Emulate FFA_RX_RELEASE FF-A function.
 *
 * Return:
 *
 * 0 on success. Otherwise, failure
 */
static int sandbox_ffa_rx_release(struct udevice *emul, ffa_value_t *pargs, ffa_value_t *res)
{
	int ret;
	struct sandbox_ffa_emul *priv = dev_get_priv(emul);

	if (!priv->pair_info.rxbuf_owned) {
		res->a0 = FFA_SMC_32(FFA_ERROR);
		res->a2 = -DENIED;
		ret = ffa_to_std_errmap[DENIED];
	} else {
		priv->pair_info.rxbuf_owned = 0;
		res->a0 = FFA_SMC_32(FFA_SUCCESS);
		res->a2 = 0;
		ret = 0;
	}

	res->a1 = 0;

	/* x3-x7 MBZ */
	memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong));

	return ret;
}

/**
 * sandbox_ffa_sp_valid() - Check SP validity
 * @emul: The sandbox FF-A emulator device
 * @part_id:	partition ID to check
 *
 * Search the input ID in the descriptors table.
 *
 * Return:
 *
 * 1 on success (Partition found). Otherwise, failure
 */
static int sandbox_ffa_sp_valid(struct udevice *emul, u16 part_id)
{
	u32 descs_cnt;
	struct sandbox_ffa_emul *priv = dev_get_priv(emul);

	for (descs_cnt = 0 ; descs_cnt < SANDBOX_PARTITIONS_CNT ; descs_cnt++)
		if (priv->partitions.descs[descs_cnt].info.id == part_id)
			return 1;

	return 0;
}

/**
 * sandbox_ffa_msg_send_direct_req() - Emulated FFA_MSG_SEND_DIRECT_{REQ,RESP} handler
 * @emul: The sandbox FF-A emulator device
 * @pargs: The SMC call input arguments a0-a7
 * @res:  The SMC return data
 *
 * Emulate FFA_MSG_SEND_DIRECT_{REQ,RESP} FF-A ABIs.
 * Only SMC 64-bit is supported in Sandbox.
 *
 * Emulating interrupts is not supported. So, FFA_RUN and FFA_INTERRUPT are not
 * supported. In case of success FFA_MSG_SEND_DIRECT_RESP is returned with
 * default pattern data (0xff).
 *
 * Return:
 *
 * 0 on success. Otherwise, failure
 */
static int sandbox_ffa_msg_send_direct_req(struct udevice *emul,
					   ffa_value_t *pargs, ffa_value_t *res)
{
	u16 part_id;
	struct sandbox_ffa_emul *priv = dev_get_priv(emul);

	part_id = GET_DST_SP_ID(pargs->a1);

	if (GET_NS_PHYS_ENDPOINT_ID(pargs->a1) != priv->id ||
	    !sandbox_ffa_sp_valid(emul, part_id) || pargs->a2) {
		res->a0 = FFA_SMC_32(FFA_ERROR);
		res->a1 = 0;
		res->a2 = -INVALID_PARAMETERS;

		/* x3-x7 MBZ */
		memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong));

		return ffa_to_std_errmap[INVALID_PARAMETERS];
	}

	res->a0 = FFA_SMC_64(FFA_MSG_SEND_DIRECT_RESP);

	res->a1 = PREP_SRC_SP_ID(part_id) |
		PREP_NS_PHYS_ENDPOINT_ID(priv->id);

	res->a2 = 0;

	/* Return 0xff bytes as a response */
	res->a3 = -1UL;
	res->a4 = -1UL;
	res->a5 = -1UL;
	res->a6 = -1UL;
	res->a7 = -1UL;

	return 0;
}

/**
 * sandbox_ffa_get_rxbuf_flags() - Read the mapping/ownership flags
 * @emul: The sandbox FF-A emulator device
 * @queried_func_id:	The FF-A function to be queried
 * @func_data:  Pointer to the FF-A function arguments container structure
 *
 * Query the status flags of the following emulated
 * ABIs: FFA_RXTX_MAP, FFA_RXTX_UNMAP, FFA_RX_RELEASE.
 *
 * Return:
 *
 * 0 on success. Otherwise, failure
 */
static int sandbox_ffa_get_rxbuf_flags(struct udevice *emul, u32 queried_func_id,
				       struct ffa_sandbox_data *func_data)
{
	struct sandbox_ffa_emul *priv = dev_get_priv(emul);

	if (!func_data)
		return -EINVAL;

	if (!func_data->data0 || func_data->data0_size != sizeof(u8))
		return -EINVAL;

	switch (queried_func_id) {
	case FFA_RXTX_MAP:
	case FFA_RXTX_UNMAP:
		*((u8 *)func_data->data0) = priv->pair_info.rxbuf_mapped;
		return 0;
	case FFA_RX_RELEASE:
		*((u8 *)func_data->data0) = priv->pair_info.rxbuf_owned;
		return 0;
	default:
		log_err("The querried FF-A interface flag (%d) undefined\n",
			queried_func_id);
		return -EINVAL;
	}
}

/**
 * sandbox_ffa_get_fwk_version() - Return the FFA framework version
 * @emul: The sandbox FF-A emulator device
 * @func_data:  Pointer to the FF-A function arguments container structure
 *
 * Return the FFA framework version read from the FF-A emulator data.
 *
 * Return:
 *
 * 0 on success. Otherwise, failure
 */
static int sandbox_ffa_get_fwk_version(struct udevice *emul, struct ffa_sandbox_data *func_data)
{
	struct sandbox_ffa_emul *priv = dev_get_priv(emul);

	if (!func_data)
		return -EINVAL;

	if (!func_data->data0 ||
	    func_data->data0_size != sizeof(priv->fwk_version))
		return -EINVAL;

	*((u32 *)func_data->data0) = priv->fwk_version;

	return 0;
}

/**
 * sandbox_ffa_get_parts() - Return the address of partitions data
 * @emul: The sandbox FF-A emulator device
 * @func_data:  Pointer to the FF-A function arguments container structure
 *
 * Return the address of partitions data read from the FF-A emulator data.
 *
 * Return:
 *
 * 0 on success. Otherwise, failure
 */
static int sandbox_ffa_get_parts(struct udevice *emul, struct ffa_sandbox_data *func_data)
{
	struct sandbox_ffa_emul *priv = dev_get_priv(emul);

	if (!func_data)
		return -EINVAL;

	if (!func_data->data0 ||
	    func_data->data0_size != sizeof(struct ffa_partitions *))
		return -EINVAL;

	*((struct ffa_partitions **)func_data->data0) = &priv->partitions;

	return 0;
}

/**
 * sandbox_query_ffa_emul_state() - Inspect the FF-A ABIs
 * @queried_func_id:	The FF-A function to be queried
 * @func_data:  Pointer to the FF-A function arguments container structure
 *
 * Query the status of FF-A ABI specified in the input argument.
 *
 * Return:
 *
 * 0 on success. Otherwise, failure
 */
int sandbox_query_ffa_emul_state(u32 queried_func_id,
				 struct ffa_sandbox_data *func_data)
{
	struct udevice *emul;
	int ret;

	ret = uclass_first_device_err(UCLASS_FFA_EMUL, &emul);
	if (ret) {
		log_err("Cannot find FF-A emulator during querying state\n");
		return ret;
	}

	switch (queried_func_id) {
	case FFA_RXTX_MAP:
	case FFA_RXTX_UNMAP:
	case FFA_RX_RELEASE:
		return sandbox_ffa_get_rxbuf_flags(emul, queried_func_id, func_data);
	case FFA_VERSION:
		return sandbox_ffa_get_fwk_version(emul, func_data);
	case FFA_PARTITION_INFO_GET:
		return sandbox_ffa_get_parts(emul, func_data);
	default:
		log_err("Undefined FF-A interface (%d)\n",
			queried_func_id);
		return -EINVAL;
	}
}

/**
 * sandbox_arm_ffa_smccc_smc() - FF-A SMC call emulation
 * @args:	the SMC call arguments
 * @res:	the SMC call returned data
 *
 * Emulate the FF-A ABIs SMC call.
 * The emulated FF-A ABI is identified and invoked.
 * FF-A emulation is based on the FF-A specification 1.0
 *
 * Return:
 *
 * 0 on success. Otherwise, failure.
 * FF-A protocol error codes are returned using the registers arguments as
 * described by the specification
 */
void sandbox_arm_ffa_smccc_smc(ffa_value_t *args, ffa_value_t *res)
{
	int ret = 0;
	struct udevice *emul;

	ret = uclass_first_device_err(UCLASS_FFA_EMUL, &emul);
	if (ret) {
		log_err("Cannot find FF-A emulator during SMC emulation\n");
		return;
	}

	switch (args->a0) {
	case FFA_SMC_32(FFA_VERSION):
		ret = sandbox_ffa_version(emul, args, res);
		break;
	case FFA_SMC_32(FFA_PARTITION_INFO_GET):
		ret = sandbox_ffa_partition_info_get(emul, args, res);
		break;
	case FFA_SMC_32(FFA_RXTX_UNMAP):
		ret = sandbox_ffa_rxtx_unmap(emul, args, res);
		break;
	case FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ):
		ret = sandbox_ffa_msg_send_direct_req(emul, args, res);
		break;
	case FFA_SMC_32(FFA_ID_GET):
		ret = sandbox_ffa_id_get(emul, args, res);
		break;
	case FFA_SMC_32(FFA_FEATURES):
		ret = sandbox_ffa_features(args, res);
		break;
	case FFA_SMC_64(FFA_RXTX_MAP):
		ret = sandbox_ffa_rxtx_map(emul, args, res);
		break;
	case FFA_SMC_32(FFA_RX_RELEASE):
		ret = sandbox_ffa_rx_release(emul, args, res);
		break;
	default:
		log_err("Undefined FF-A interface (%lx)\n",
			args->a0);
	}

	if (ret != 0)
		log_err("FF-A ABI internal failure (%d)\n", ret);
}

/**
 * invoke_ffa_fn() - SMC wrapper
 * @args: FF-A ABI arguments to be copied to Xn registers
 * @res: FF-A ABI return data to be copied from Xn registers
 *
 * Calls the emulated SMC call.
 */
void invoke_ffa_fn(ffa_value_t args, ffa_value_t *res)
{
	sandbox_arm_ffa_smccc_smc(&args, res);
}

/**
 * ffa_emul_find() - Find the FF-A emulator
 * @dev:	the sandbox FF-A device (sandbox-arm-ffa)
 * @emulp:	the FF-A emulator device (sandbox-ffa-emul)
 *
 * Search for the FF-A emulator and returns its device pointer.
 *
 * Return:
 * 0 on success. Otherwise, failure
 */
int ffa_emul_find(struct udevice *dev, struct udevice **emulp)
{
	int ret;

	ret = uclass_first_device_err(UCLASS_FFA_EMUL, emulp);
	if (ret) {
		log_err("Cannot find FF-A emulator\n");
		return ret;
	}

	log_debug("FF-A emulator ready to use\n");

	return 0;
}

UCLASS_DRIVER(ffa_emul) = {
	.name		= "ffa_emul",
	.id		= UCLASS_FFA_EMUL,
	.post_bind = dm_scan_fdt_dev,
};

static const struct udevice_id sandbox_ffa_emul_ids[] = {
	{ .compatible = "sandbox,arm-ffa-emul" },
	{ }
};

/* Declaring the sandbox FF-A emulator under UCLASS_FFA_EMUL */
U_BOOT_DRIVER(sandbox_ffa_emul) = {
	.name		= "sandbox_ffa_emul",
	.id		= UCLASS_FFA_EMUL,
	.of_match	= sandbox_ffa_emul_ids,
	.priv_auto	= sizeof(struct sandbox_ffa_emul),
};
