diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index 9f45c48e4e..dd3b43885e 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -356,6 +356,22 @@
 		sandbox_firmware: sandbox-firmware {
 			compatible = "sandbox,firmware";
 		};
+
+		sandbox-scmi-agent@0 {
+			compatible = "sandbox,scmi-agent";
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		sandbox-scmi-agent@1 {
+			compatible = "sandbox,scmi-agent";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			protocol@10 {
+				reg = <0x10>;
+			};
+		};
 	};
 
 	pinctrl-gpio {
diff --git a/arch/sandbox/include/asm/scmi_test.h b/arch/sandbox/include/asm/scmi_test.h
new file mode 100644
index 0000000000..a811fe19c3
--- /dev/null
+++ b/arch/sandbox/include/asm/scmi_test.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020, Linaro Limited
+ */
+
+#ifndef __SANDBOX_SCMI_TEST_H
+#define __SANDBOX_SCMI_TEST_H
+
+struct udevice;
+struct sandbox_scmi_agent;
+struct sandbox_scmi_service;
+
+/**
+ * struct sandbox_scmi_agent - Simulated SCMI service seen by SCMI agent
+ * @idx:	Identifier for the SCMI agent, its index
+ */
+struct sandbox_scmi_agent {
+	uint idx;
+};
+
+/**
+ * struct sandbox_scmi_service - Reference to simutaed SCMI agents/services
+ * @agent:		Pointer to SCMI sandbox agent pointers array
+ * @agent_count:	Number of emulated agents exposed in array @agent.
+ */
+struct sandbox_scmi_service {
+	struct sandbox_scmi_agent **agent;
+	size_t agent_count;
+};
+
+#ifdef CONFIG_SCMI_FIRMWARE
+/**
+ * sandbox_scmi_service_context - Get the simulated SCMI services context
+ * @return:	Reference to backend simulated resources state
+ */
+struct sandbox_scmi_service *sandbox_scmi_service_ctx(void);
+#else
+static inline struct sandbox_scmi_service *sandbox_scmi_service_ctx(void)
+{
+	return NULL;
+}
+#endif /* CONFIG_SCMI_FIRMWARE */
+#endif /* __SANDBOX_SCMI_TEST_H */
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index 6e9f029cc9..2c130c01f0 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -132,6 +132,8 @@ CONFIG_BOARD_SANDBOX=y
 CONFIG_DMA=y
 CONFIG_DMA_CHANNELS=y
 CONFIG_SANDBOX_DMA=y
+CONFIG_FIRMWARE=y
+CONFIG_SCMI_FIRMWARE=y
 CONFIG_GPIO_HOG=y
 CONFIG_DM_GPIO_LOOKUP_LABEL=y
 CONFIG_PM8916_GPIO=y
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index b70a206355..ef958b3a7a 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -36,3 +36,5 @@ config ZYNQMP_FIRMWARE
 	  various platform management services.
 	  Say yes to enable ZynqMP firmware interface driver.
 	  If in doubt, say N.
+
+source "drivers/firmware/scmi/Kconfig"
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index a0c250a473..7ce83d72bd 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -3,3 +3,4 @@ obj-$(CONFIG_$(SPL_)ARM_PSCI_FW)	+= psci.o
 obj-$(CONFIG_TI_SCI_PROTOCOL)	+= ti_sci.o
 obj-$(CONFIG_SANDBOX)		+= firmware-sandbox.o
 obj-$(CONFIG_ZYNQMP_FIRMWARE)	+= firmware-zynqmp.o
+obj-$(CONFIG_SCMI_FIRMWARE)	+= scmi/
diff --git a/drivers/firmware/scmi/Kconfig b/drivers/firmware/scmi/Kconfig
new file mode 100644
index 0000000000..57e2ebbe42
--- /dev/null
+++ b/drivers/firmware/scmi/Kconfig
@@ -0,0 +1,17 @@
+config SCMI_FIRMWARE
+	bool "Enable SCMI support"
+	select FIRMWARE
+	select OF_TRANSLATE
+	depends on SANDBOX
+	help
+	  System Control and Management Interface (SCMI) is a communication
+	  protocol that defines standard interfaces for power, performance
+	  and system management. The SCMI specification is available at
+	  https://developer.arm.com/architectures/system-architectures/software-standards/scmi
+
+	  An SCMI agent communicates with a related SCMI server firmware
+	  located in another sub-system, as a companion micro controller
+	  or a companion host in the CPU system.
+
+	  Communications between agent (client) and the SCMI server are
+	  based on message exchange.
diff --git a/drivers/firmware/scmi/Makefile b/drivers/firmware/scmi/Makefile
new file mode 100644
index 0000000000..336ea1f2a3
--- /dev/null
+++ b/drivers/firmware/scmi/Makefile
@@ -0,0 +1,2 @@
+obj-y	+= scmi_agent-uclass.o
+obj-$(CONFIG_SANDBOX)		+= sandbox-scmi_agent.o
diff --git a/drivers/firmware/scmi/sandbox-scmi_agent.c b/drivers/firmware/scmi/sandbox-scmi_agent.c
new file mode 100644
index 0000000000..ce0d49c951
--- /dev/null
+++ b/drivers/firmware/scmi/sandbox-scmi_agent.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020, Linaro Limited
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <malloc.h>
+#include <scmi_agent.h>
+#include <scmi_agent-uclass.h>
+#include <scmi_protocols.h>
+#include <asm/io.h>
+#include <asm/scmi_test.h>
+#include <dm/device_compat.h>
+
+/*
+ * The sandbox SCMI agent driver simulates to some extend a SCMI message
+ * processing. It simulates few of the SCMI services for some of the
+ * SCMI protocols embedded in U-Boot. Currently none.
+ *
+ * This driver simulates 2 SCMI agents for test purpose.
+ *
+ * This Driver exports sandbox_scmi_service_ct() for the test sequence to
+ * get the state of the simulated services (clock state, rate, ...) and
+ * check back-end device state reflects the request send through the
+ * various uclass devices, currently nothing.
+ */
+
+#define SANDBOX_SCMI_AGENT_COUNT	2
+
+/* The list saves to simulted end devices references for test purpose */
+struct sandbox_scmi_agent *sandbox_scmi_agent_list[SANDBOX_SCMI_AGENT_COUNT];
+
+static struct sandbox_scmi_service sandbox_scmi_service_state = {
+	.agent = sandbox_scmi_agent_list,
+	.agent_count = SANDBOX_SCMI_AGENT_COUNT,
+};
+
+struct sandbox_scmi_service *sandbox_scmi_service_ctx(void)
+{
+	return &sandbox_scmi_service_state;
+}
+
+struct sandbox_scmi_agent *dev2agent(struct udevice *dev)
+{
+	return (struct sandbox_scmi_agent *)dev_get_priv(dev);
+}
+
+static void debug_print_agent_state(struct udevice *dev, char *str)
+{
+	struct sandbox_scmi_agent *agent = dev2agent(dev);
+
+	dev_dbg(dev, "Dump sandbox_scmi_agent %u: %s\n", agent->idx, str);
+};
+
+static int sandbox_scmi_test_process_msg(struct udevice *dev,
+					 struct scmi_msg *msg)
+{
+	switch (msg->protocol_id) {
+	case SCMI_PROTOCOL_ID_BASE:
+	case SCMI_PROTOCOL_ID_POWER_DOMAIN:
+	case SCMI_PROTOCOL_ID_SYSTEM:
+	case SCMI_PROTOCOL_ID_PERF:
+	case SCMI_PROTOCOL_ID_CLOCK:
+	case SCMI_PROTOCOL_ID_SENSOR:
+	case SCMI_PROTOCOL_ID_RESET_DOMAIN:
+		*(u32 *)msg->out_msg = SCMI_NOT_SUPPORTED;
+		return 0;
+	default:
+		break;
+	}
+
+	dev_err(dev, "%s(%s): Unhandled protocol_id %#x/message_id %#x\n",
+		__func__, dev->name, msg->protocol_id, msg->message_id);
+
+	if (msg->out_msg_sz < sizeof(u32))
+		return -EINVAL;
+
+	/* Intentionnaly report unhandled IDs through the SCMI return code */
+	*(u32 *)msg->out_msg = SCMI_PROTOCOL_ERROR;
+	return 0;
+}
+
+static int sandbox_scmi_test_remove(struct udevice *dev)
+{
+	struct sandbox_scmi_agent *agent = dev2agent(dev);
+
+	debug_print_agent_state(dev, "removed");
+
+	/* We only need to dereference the agent in the context */
+	sandbox_scmi_service_ctx()->agent[agent->idx] = NULL;
+
+	return 0;
+}
+
+static int sandbox_scmi_test_probe(struct udevice *dev)
+{
+	static const char basename[] = "sandbox-scmi-agent@";
+	struct sandbox_scmi_agent *agent = dev2agent(dev);
+	const size_t basename_size = sizeof(basename) - 1;
+
+	if (strncmp(basename, dev->name, basename_size))
+		return -ENOENT;
+
+	switch (dev->name[basename_size]) {
+	case '0':
+		*agent = (struct sandbox_scmi_agent){
+			.idx = 0,
+		};
+		break;
+	case '1':
+		*agent = (struct sandbox_scmi_agent){
+			.idx = 1,
+		};
+		break;
+	default:
+		dev_err(dev, "%s(): Unexpected agent ID %s\n",
+			__func__, dev->name + basename_size);
+		return -ENOENT;
+	}
+
+	debug_print_agent_state(dev, "probed");
+
+	/* Save reference for tests purpose */
+	sandbox_scmi_service_ctx()->agent[agent->idx] = agent;
+
+	return 0;
+};
+
+static const struct udevice_id sandbox_scmi_test_ids[] = {
+	{ .compatible = "sandbox,scmi-agent" },
+	{ }
+};
+
+struct scmi_agent_ops sandbox_scmi_test_ops = {
+	.process_msg = sandbox_scmi_test_process_msg,
+};
+
+U_BOOT_DRIVER(sandbox_scmi_agent) = {
+	.name = "sandbox-scmi_agent",
+	.id = UCLASS_SCMI_AGENT,
+	.of_match = sandbox_scmi_test_ids,
+	.priv_auto_alloc_size = sizeof(struct sandbox_scmi_agent),
+	.probe = sandbox_scmi_test_probe,
+	.remove = sandbox_scmi_test_remove,
+	.ops = &sandbox_scmi_test_ops,
+};
diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c
new file mode 100644
index 0000000000..67a6f907c9
--- /dev/null
+++ b/drivers/firmware/scmi/scmi_agent-uclass.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Linaro Limited.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <scmi_agent-uclass.h>
+#include <scmi_protocols.h>
+
+#include <dm/device-internal.h>
+#include <linux/compat.h>
+
+/**
+ * struct error_code - Helper structure for SCMI error code conversion
+ * @scmi:	SCMI error code
+ * @errno:	Related standard error number
+ */
+struct error_code {
+	int scmi;
+	int errno;
+};
+
+static const struct error_code scmi_linux_errmap[] = {
+	{ .scmi = SCMI_NOT_SUPPORTED, .errno = -EOPNOTSUPP, },
+	{ .scmi = SCMI_INVALID_PARAMETERS, .errno = -EINVAL, },
+	{ .scmi = SCMI_DENIED, .errno = -EACCES, },
+	{ .scmi = SCMI_NOT_FOUND, .errno = -ENOENT, },
+	{ .scmi = SCMI_OUT_OF_RANGE, .errno = -ERANGE, },
+	{ .scmi = SCMI_BUSY, .errno = -EBUSY, },
+	{ .scmi = SCMI_COMMS_ERROR, .errno = -ECOMM, },
+	{ .scmi = SCMI_GENERIC_ERROR, .errno = -EIO, },
+	{ .scmi = SCMI_HARDWARE_ERROR, .errno = -EREMOTEIO, },
+	{ .scmi = SCMI_PROTOCOL_ERROR, .errno = -EPROTO, },
+};
+
+int scmi_to_linux_errno(s32 scmi_code)
+{
+	int n;
+
+	if (!scmi_code)
+		return 0;
+
+	for (n = 0; n < ARRAY_SIZE(scmi_linux_errmap); n++)
+		if (scmi_code == scmi_linux_errmap[n].scmi)
+			return scmi_linux_errmap[1].errno;
+
+	return -EPROTO;
+}
+
+/*
+ * SCMI agent devices binds devices of various uclasses depeding on
+ * the FDT description. scmi_bind_protocol() is a generic bind sequence
+ * called by the uclass at bind stage, that is uclass post_bind.
+ */
+static int scmi_bind_protocols(struct udevice *dev)
+{
+	int ret = 0;
+	ofnode node;
+	struct driver *drv;
+
+	dev_for_each_subnode(node, dev) {
+		u32 protocol_id;
+
+		if (!ofnode_is_available(node))
+			continue;
+
+		if (ofnode_read_u32(node, "reg", &protocol_id))
+			continue;
+
+		switch (protocol_id) {
+		default:
+			dev_info(dev, "Ignore unsupported SCMI protocol %#x\n",
+				 protocol_id);
+			continue;
+		}
+
+		ret = device_bind_ofnode(dev, drv, ofnode_get_name(node),
+					 NULL, node, NULL);
+		if (ret)
+			break;
+	}
+
+	return ret;
+}
+
+static const struct scmi_agent_ops *transport_dev_ops(struct udevice *dev)
+{
+	return (const struct scmi_agent_ops *)dev->driver->ops;
+}
+
+int devm_scmi_process_msg(struct udevice *dev, struct scmi_msg *msg)
+{
+	const struct scmi_agent_ops *ops = transport_dev_ops(dev);
+
+	if (ops->process_msg)
+		return ops->process_msg(dev, msg);
+
+	return -EPROTONOSUPPORT;
+}
+
+UCLASS_DRIVER(scmi_agent) = {
+	.id		= UCLASS_SCMI_AGENT,
+	.name		= "scmi_agent",
+	.post_bind	= scmi_bind_protocols,
+};
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 4ec5fa6670..88f10c4622 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -94,6 +94,7 @@ enum uclass_id {
 	UCLASS_RESET,		/* Reset controller device */
 	UCLASS_RNG,		/* Random Number Generator */
 	UCLASS_RTC,		/* Real time clock device */
+	UCLASS_SCMI_AGENT,	/* Interface with an SCMI server */
 	UCLASS_SCSI,		/* SCSI device */
 	UCLASS_SERIAL,		/* Serial UART */
 	UCLASS_SIMPLE_BUS,	/* Bus with child devices */
diff --git a/include/scmi_agent-uclass.h b/include/scmi_agent-uclass.h
new file mode 100644
index 0000000000..a501d1b482
--- /dev/null
+++ b/include/scmi_agent-uclass.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2019-2020 Linaro Limited.
+ */
+#ifndef _SCMI_AGENT_UCLASS_H
+#define _SCMI_AGENT_UCLASS_H
+
+struct udevice;
+struct scmi_msg;
+
+/**
+ * struct scmi_transport_ops - The functions that a SCMI transport layer must implement.
+ */
+struct scmi_agent_ops {
+	/*
+	 * process_msg - Request transport to get the SCMI message processed
+	 *
+	 * @agent:		Agent using the transport
+	 * @msg:		SCMI message to be transmitted
+	 */
+	int (*process_msg)(struct udevice *dev, struct scmi_msg *msg);
+};
+
+#endif /* _SCMI_TRANSPORT_UCLASS_H */
diff --git a/include/scmi_agent.h b/include/scmi_agent.h
new file mode 100644
index 0000000000..f1be9ff209
--- /dev/null
+++ b/include/scmi_agent.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
+ * Copyright (C) 2019-2020, Linaro Limited
+ *
+ * An SCMI agent device represent on communication path from a
+ * device driver to the remote SCMI server which driver sends
+ * messages to and receives response messages from.
+ */
+#ifndef SCMI_AGENT_H
+#define SCMI_AGENT_H
+
+#include <asm/types.h>
+
+struct udevice;
+
+/*
+ * struct scmi_msg - Context of a SCMI message sent and the response received
+ *
+ * @protocol_id:	SCMI protocol ID
+ * @message_id:		SCMI message ID for a defined protocol ID
+ * @in_msg:		Pointer to the message payload sent by the driver
+ * @in_msg_sz:		Byte size of the message payload sent
+ * @out_msg:		Pointer to buffer to store response message payload
+ * @out_msg_sz:		Byte size of the response buffer and response payload
+ */
+struct scmi_msg {
+	unsigned int protocol_id;
+	unsigned int message_id;
+	u8 *in_msg;
+	size_t in_msg_sz;
+	u8 *out_msg;
+	size_t out_msg_sz;
+};
+
+/* Helper macro to match a message on input/output array references */
+#define SCMI_MSG_IN(_protocol, _message, _in_array, _out_array) \
+	(struct scmi_msg){			\
+		.protocol_id = (_protocol),	\
+		.message_id = (_message),	\
+		.in_msg = (uint8_t *)&(_in_array),	\
+		.in_msg_sz = sizeof(_in_array),	\
+		.out_msg = (uint8_t *)&(_out_array),	\
+		.out_msg_sz = sizeof(_out_array),	\
+	}
+
+/**
+ * scmi_send_and_process_msg() - send and process a SCMI message
+ *
+ * Send a message to a SCMI server through a target SCMI agent device.
+ * Caller sets scmi_msg::out_msg_sz to the output message buffer size.
+ * On return, scmi_msg::out_msg_sz stores the response payload size.
+ *
+ * @dev:	SCMI agent device
+ * @msg:	Message structure reference
+ * @return 0 on success and a negative errno on failure
+ */
+int devm_scmi_process_msg(struct udevice *dev, struct scmi_msg *msg);
+
+/**
+ * scmi_to_linux_errno() - Convert an SCMI error code into a Linux errno code
+ *
+ * @scmi_errno:	SCMI error code value
+ * @return 0 for successful status and a negative errno otherwise
+ */
+int scmi_to_linux_errno(s32 scmi_errno);
+
+#endif /* SCMI_H */
diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h
new file mode 100644
index 0000000000..86a2d109c8
--- /dev/null
+++ b/include/scmi_protocols.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
+ * Copyright (C) 2019-2020, Linaro Limited
+ */
+#ifndef _SCMI_PROTOCOLS_H
+#define _SCMI_PROTOCOLS_H
+
+#include <linux/bitops.h>
+
+/*
+ * Subset the SCMI protocols definition
+ * based on SCMI specification v2.0 (DEN0056B)
+ * https://developer.arm.com/docs/den0056/b
+ */
+
+enum scmi_std_protocol {
+	SCMI_PROTOCOL_ID_BASE = 0x10,
+	SCMI_PROTOCOL_ID_POWER_DOMAIN = 0x11,
+	SCMI_PROTOCOL_ID_SYSTEM = 0x12,
+	SCMI_PROTOCOL_ID_PERF = 0x13,
+	SCMI_PROTOCOL_ID_CLOCK = 0x14,
+	SCMI_PROTOCOL_ID_SENSOR = 0x15,
+	SCMI_PROTOCOL_ID_RESET_DOMAIN = 0x16,
+};
+
+enum scmi_status_code {
+	SCMI_SUCCESS =  0,
+	SCMI_NOT_SUPPORTED = -1,
+	SCMI_INVALID_PARAMETERS = -2,
+	SCMI_DENIED = -3,
+	SCMI_NOT_FOUND = -4,
+	SCMI_OUT_OF_RANGE = -5,
+	SCMI_BUSY = -6,
+	SCMI_COMMS_ERROR = -7,
+	SCMI_GENERIC_ERROR = -8,
+	SCMI_HARDWARE_ERROR = -9,
+	SCMI_PROTOCOL_ERROR = -10,
+};
+
+#endif /* _SCMI_PROTOCOLS_H */
diff --git a/test/dm/Makefile b/test/dm/Makefile
index 864c8d0b4c..70ba1b6695 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -80,4 +80,5 @@ obj-$(CONFIG_DM_RNG) += rng.o
 obj-$(CONFIG_CLK_K210_SET_RATE) += k210_pll.o
 obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o
 obj-$(CONFIG_RESET_SYSCON) += syscon-reset.o
+obj-$(CONFIG_SCMI_FIRMWARE) += scmi.o
 endif
diff --git a/test/dm/scmi.c b/test/dm/scmi.c
new file mode 100644
index 0000000000..d8c1e71f12
--- /dev/null
+++ b/test/dm/scmi.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020, Linaro Limited
+ *
+ * Tests scmi_agent uclass and the SCMI drivers implemented in other
+ * uclass devices probe when a SCMI server exposes resources.
+ *
+ * Note in test.dts the protocol@10 node in agent 1. Protocol 0x10 is not
+ * implemented in U-Boot SCMI components but the implementation is exepected
+ * to not complain on unknown protocol IDs, as long as it is not used. Note
+ * in test.dts tests that SCMI drivers probing does not fail for such an
+ * unknown SCMI protocol ID.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <asm/scmi_test.h>
+#include <dm/device-internal.h>
+#include <dm/test.h>
+#include <test/ut.h>
+
+/*
+ * Test SCMI states when loading and releasing resources
+ * related to SCMI drivers.
+ */
+static int dm_test_scmi_sandbox_agent(struct unit_test_state *uts)
+{
+	struct sandbox_scmi_service *scmi_ctx = sandbox_scmi_service_ctx();
+
+	ut_assertnonnull(scmi_ctx);
+	ut_asserteq(2, scmi_ctx->agent_count);
+	ut_assertnull(scmi_ctx->agent[0]);
+	ut_assertnull(scmi_ctx->agent[1]);
+
+	return 0;
+}
+
+DM_TEST(dm_test_scmi_sandbox_agent, UT_TESTF_SCAN_FDT);
