diff mbox series

[v2,1/4] firmware: add new driver for SCMI firmwares

Message ID 20200818154133.22028-1-etienne.carriere@linaro.org
State New
Headers show
Series [v2,1/4] firmware: add new driver for SCMI firmwares | expand

Commit Message

Etienne Carriere Aug. 18, 2020, 3:41 p.m. UTC
This change introduces SCMI agent driver in U-Boot in the firmware
U-class.

SCMI agent driver is designed for platforms that embed a SCMI server in
a firmware hosted for example by a companion co-processor or the secure
world of the executing processor.

SCMI protocols allow an SCMI agent to discover and access external
resources as clock, reset controllers and many more. SCMI agent and
server communicate following the SCMI specification [1]. SCMI agent
complies with the DT bindings defined in the Linux kernel source tree
regarding SCMI agent description since v5.8-rc1.

These bindings describe 2 supported message transport layer: using
mailbox uclass devices or using Arm SMC invocation instruction. Both
use a piece or shared memory for message data exchange.

In the current state, the SCMI agent driver does not bind to any SCMI
protocol to a U-Boot device driver. Former changes will implement
dedicated driver (i.e. an SCMI clock driver or an SCMI reset controller
driver) and add bind supported SCMI protocols in scmi_agent_bind().

Links: [1] https://developer.arm.com/architectures/system-architectures/software-standards/scmi
Signed-off-by: Etienne Carriere <etienne.carriere@linaro.org>

Cc: Simon Glass <sjg@chromium.org>
Cc: Peng Fan <peng.fan@nxp.com>
Cc: Sudeep Holla <sudeep.holla@arm.com>
---

Changes in v2:
- Fix CONFIG_SCMI_FIRMWARE description with explicit SCMI reference.
- Move struct, enum and macro definitions at source file top and
  add inline comment description for the structures and local functions.
- Replace rc with ret as return value local variable label.
- Use explicit return 0 on successful return paths.
- Replace EINVAL with more accurate error numbers.
- Use dev_read_u32() instead of ofnode_read_u32(dev_ofnode(), ...).
- Use memcpy_toio()/memcpy_fromio() when copying message payload
  to/from IO memory.
- Embed mailbox transport resources upon CONFIG_DM_MAILBOX and
  SMCCC transport resources upon CONFIG_ARM_SMCCC.

Note: review comments on defining a uclass and sandbox for SCMI
transport drivers are NOT addressed in this v2. Main issue is that
there is no driver/device defined for SCMI transport layer as well as
and no defined compatible ID in the SCMI DT bindings documentation.
---
 drivers/firmware/Kconfig  |  20 ++
 drivers/firmware/Makefile |   1 +
 drivers/firmware/scmi.c   | 490 ++++++++++++++++++++++++++++++++++++++
 include/scmi.h            |  82 +++++++
 4 files changed, 593 insertions(+)
 create mode 100644 drivers/firmware/scmi.c
 create mode 100644 include/scmi.h

-- 
2.17.1

Comments

Etienne Carriere Aug. 18, 2020, 4:03 p.m. UTC | #1
Hello Sudeep,


On Tue, 18 Aug 2020 at 17:44, Etienne Carriere
<etienne.carriere@linaro.org> wrote:
>

> This change introduces SCMI agent driver in U-Boot in the firmware

> U-class.

> (...)

> --- a/drivers/firmware/Kconfig

> +++ b/drivers/firmware/Kconfig

> @@ -1,6 +1,26 @@

>  config FIRMWARE

>         bool "Enable Firmware driver support"

>

> +config SCMI_FIRMWARE

> +       bool "Enable SCMI support"

> +       select FIRMWARE

> +       select OF_TRANSLATE

> +       depends on DM_MAILBOX || ARM_SMCCC

> +       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


You suggested https://developer.arm.com/documentation/den0056/latest.
It is shorter and points straight to the spec document whereas this
link is used above points to more generic info about SCMI among which
one can find the spec doc.
Maybe I should change to the link you suggested.

> +

> +         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. Messages can be exchange over tranport


Typo here: s/tranport/transport/

> +         channels as a mailbox device or an Arm SMCCC service with some

> +         piece of identified shared memory.

> +

>  config SPL_FIRMWARE

>         bool "Enable Firmware driver support in SPL"

>         depends on FIRMWARE
Simon Glass Aug. 22, 2020, 3:09 p.m. UTC | #2
Hi Etienne,

On Tue, 18 Aug 2020 at 09:44, Etienne Carriere
<etienne.carriere@linaro.org> wrote:
>

> This change introduces SCMI agent driver in U-Boot in the firmware

> U-class.

>

> SCMI agent driver is designed for platforms that embed a SCMI server in

> a firmware hosted for example by a companion co-processor or the secure

> world of the executing processor.

>

> SCMI protocols allow an SCMI agent to discover and access external

> resources as clock, reset controllers and many more. SCMI agent and

> server communicate following the SCMI specification [1]. SCMI agent

> complies with the DT bindings defined in the Linux kernel source tree

> regarding SCMI agent description since v5.8-rc1.

>

> These bindings describe 2 supported message transport layer: using

> mailbox uclass devices or using Arm SMC invocation instruction. Both

> use a piece or shared memory for message data exchange.

>

> In the current state, the SCMI agent driver does not bind to any SCMI

> protocol to a U-Boot device driver. Former changes will implement

> dedicated driver (i.e. an SCMI clock driver or an SCMI reset controller

> driver) and add bind supported SCMI protocols in scmi_agent_bind().

>

> Links: [1] https://developer.arm.com/architectures/system-architectures/software-standards/scmi

> Signed-off-by: Etienne Carriere <etienne.carriere@linaro.org>

> Cc: Simon Glass <sjg@chromium.org>

> Cc: Peng Fan <peng.fan@nxp.com>

> Cc: Sudeep Holla <sudeep.holla@arm.com>

> ---

>

> Changes in v2:

> - Fix CONFIG_SCMI_FIRMWARE description with explicit SCMI reference.

> - Move struct, enum and macro definitions at source file top and

>   add inline comment description for the structures and local functions.

> - Replace rc with ret as return value local variable label.

> - Use explicit return 0 on successful return paths.

> - Replace EINVAL with more accurate error numbers.

> - Use dev_read_u32() instead of ofnode_read_u32(dev_ofnode(), ...).

> - Use memcpy_toio()/memcpy_fromio() when copying message payload

>   to/from IO memory.

> - Embed mailbox transport resources upon CONFIG_DM_MAILBOX and

>   SMCCC transport resources upon CONFIG_ARM_SMCCC.

>

> Note: review comments on defining a uclass and sandbox for SCMI

> transport drivers are NOT addressed in this v2. Main issue is that

> there is no driver/device defined for SCMI transport layer as well as

> and no defined compatible ID in the SCMI DT bindings documentation.


I'd still like to see this. You can define an API with a header file.
It is certainly easier if the DT binding can cover the transport type
with a separate subnode. But that doesn't stop you creating a uclass
for the transport. It will also allow you to create a sandbox impl so
you can add a test for this code.

Also the two interfaces should really be in separate files rather than
using #ifdefs, I think.

> ---

>  drivers/firmware/Kconfig  |  20 ++

>  drivers/firmware/Makefile |   1 +

>  drivers/firmware/scmi.c   | 490 ++++++++++++++++++++++++++++++++++++++

>  include/scmi.h            |  82 +++++++

>  4 files changed, 593 insertions(+)

>  create mode 100644 drivers/firmware/scmi.c

>  create mode 100644 include/scmi.h

>


Regards,
Simon
Etienne Carriere Aug. 23, 2020, 5:06 p.m. UTC | #3
Hello Simon,

> > This change introduces SCMI agent driver in U-Boot in the firmware

> > U-class.

> > (...)

> > Changes in v2:

> > (...)

> >

> > Note: review comments on defining a uclass and sandbox for SCMI

> > transport drivers are NOT addressed in this v2. Main issue is that

> > there is no driver/device defined for SCMI transport layer as well as

> > and no defined compatible ID in the SCMI DT bindings documentation.

>

> I'd still like to see this. You can define an API with a header file.

> It is certainly easier if the DT binding can cover the transport type

> with a separate subnode.


The bindings are already defined for scmi (ref is the Linux kernel
source tree) and there is no sub-node currently defined for the
scmi driver transport configuration. It's done through the
compatible property and dedicated optional properties.
I think changing the scmi DT binding is a bit out of the scope
of my patch series :)

> But that doesn't stop you creating a uclass

> for the transport. It will also allow you to create a sandbox impl so

> you can add a test for this code.


Ok, thanks, I understand.

>

> Also the two interfaces should really be in separate files rather than

> using #ifdefs, I think.


I'll send a v3 with the implementation over several source files and
the requested uclass/sandbox.
I think I'll create sub-directory drivers/firmware/scmi/ for the source files.

Thanks again for the feedback on this v2.
Regards,
Etienne

>

> > ---

> >  drivers/firmware/Kconfig  |  20 ++

> >  drivers/firmware/Makefile |   1 +

> >  drivers/firmware/scmi.c   | 490 ++++++++++++++++++++++++++++++++++++++

> >  include/scmi.h            |  82 +++++++

> >  4 files changed, 593 insertions(+)

> >  create mode 100644 drivers/firmware/scmi.c

> >  create mode 100644 include/scmi.h

> >

>

> Regards,

> Simon
Simon Glass Aug. 25, 2020, 3:04 p.m. UTC | #4
Hi Etienne,

On Sun, 23 Aug 2020 at 11:07, Etienne Carriere
<etienne.carriere@linaro.org> wrote:
>

> Hello Simon,

>

> > > This change introduces SCMI agent driver in U-Boot in the firmware

> > > U-class.

> > > (...)

> > > Changes in v2:

> > > (...)

> > >

> > > Note: review comments on defining a uclass and sandbox for SCMI

> > > transport drivers are NOT addressed in this v2. Main issue is that

> > > there is no driver/device defined for SCMI transport layer as well as

> > > and no defined compatible ID in the SCMI DT bindings documentation.

> >

> > I'd still like to see this. You can define an API with a header file.

> > It is certainly easier if the DT binding can cover the transport type

> > with a separate subnode.

>

> The bindings are already defined for scmi (ref is the Linux kernel

> source tree) and there is no sub-node currently defined for the

> scmi driver transport configuration. It's done through the

> compatible property and dedicated optional properties.

> I think changing the scmi DT binding is a bit out of the scope

> of my patch series :)


Fair enough. The bindings are pretty linux-specific of course, since
that is the only user. In general it seems hard to change them for
U-Boot although I haven't tried in years.

>

> > But that doesn't stop you creating a uclass

> > for the transport. It will also allow you to create a sandbox impl so

> > you can add a test for this code.

>

> Ok, thanks, I understand.

>

> >

> > Also the two interfaces should really be in separate files rather than

> > using #ifdefs, I think.

>

> I'll send a v3 with the implementation over several source files and

> the requested uclass/sandbox.

> I think I'll create sub-directory drivers/firmware/scmi/ for the source files.


I think that's a good idea.
>

> Thanks again for the feedback on this v2.


You're welcome.

Regards,
Simon
diff mbox series

Patch

diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index b70a206355..4d3cd5c6f2 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -1,6 +1,26 @@ 
 config FIRMWARE
 	bool "Enable Firmware driver support"
 
+config SCMI_FIRMWARE
+	bool "Enable SCMI support"
+	select FIRMWARE
+	select OF_TRANSLATE
+	depends on DM_MAILBOX || ARM_SMCCC
+	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. Messages can be exchange over tranport
+	  channels as a mailbox device or an Arm SMCCC service with some
+	  piece of identified shared memory.
+
 config SPL_FIRMWARE
 	bool "Enable Firmware driver support in SPL"
 	depends on FIRMWARE
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index a0c250a473..9d16055510 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -2,4 +2,5 @@  obj-$(CONFIG_FIRMWARE)		+= firmware-uclass.o
 obj-$(CONFIG_$(SPL_)ARM_PSCI_FW)	+= psci.o
 obj-$(CONFIG_TI_SCI_PROTOCOL)	+= ti_sci.o
 obj-$(CONFIG_SANDBOX)		+= firmware-sandbox.o
+obj-$(CONFIG_SCMI_FIRMWARE)	+= scmi.o
 obj-$(CONFIG_ZYNQMP_FIRMWARE)	+= firmware-zynqmp.o
diff --git a/drivers/firmware/scmi.c b/drivers/firmware/scmi.c
new file mode 100644
index 0000000000..264f3d99c8
--- /dev/null
+++ b/drivers/firmware/scmi.c
@@ -0,0 +1,490 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
+ * Copyright (C) 2019-2020 Linaro Limited.
+ */
+
+#include <common.h>
+#include <cpu_func.h>
+#include <dm.h>
+#include <errno.h>
+#include <mailbox.h>
+#include <memalign.h>
+#include <scmi.h>
+#include <asm/system.h>
+#include <asm/types.h>
+#include <dm/device-internal.h>
+#include <dm/devres.h>
+#include <dm/lists.h>
+#include <dm/ofnode.h>
+#include <linux/arm-smccc.h>
+#include <linux/compat.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+
+#define TIMEOUT_US_10MS			10000
+
+/**
+ * enum scmi_transport_channel - Supported SCMI transport layers
+ */
+enum scmi_transport_channel {
+	SCMI_MAILBOX_TRANSPORT,
+	SCMI_ARM_SMCCC_TRANSPORT,
+};
+
+/**
+ * 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;
+};
+
+/**
+ * struct method_ops - Operations related to an SCMI transport layer
+ * @process_msg:	Send message thru the SCMI transport
+ * @remove_agent:	Release SCMI transport resource
+ */
+struct method_ops {
+	int (*process_msg)(struct udevice *dev, struct scmi_msg *msg);
+	int (*remove_agent)(struct udevice *dev);
+};
+
+/**
+ * struct scmi_agent - Description of SCMI agent transport layer
+ * @method_ops:		Operations for the transport layer used the agent
+ * @method_priv:	Private data for the transport layer used the agent
+ */
+struct scmi_agent {
+	struct method_ops *method_ops;
+	void *method_priv;
+};
+
+/**
+ * struct scmi_smt_header - Description of the shared memory message buffer
+ *
+ * SMT stands for Shared Memory based Transport.
+ * SMT uses 28 byte header prior message payload to handle the state of
+ * the communication channel realized by the shared memory area and
+ * to define SCMI protocol information the payload relates to.
+ */
+struct scmi_smt_header {
+	__le32 reserved;
+	__le32 channel_status;
+#define SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR	BIT(1)
+#define SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE	BIT(0)
+	__le32 reserved1[2];
+	__le32 flags;
+#define SCMI_SHMEM_FLAG_INTR_ENABLED		BIT(0)
+	__le32 length;
+	__le32 msg_header;
+	u8 msg_payload[0];
+};
+
+#define SMT_HEADER_TOKEN(token)		(((token) << 18) & GENMASK(31, 18))
+#define SMT_HEADER_PROTOCOL_ID(proto)	(((proto) << 10) & GENMASK(17, 10))
+#define SMT_HEADER_MESSAGE_TYPE(type)	(((type) << 18) & GENMASK(9, 8))
+#define SMT_HEADER_MESSAGE_ID(id)	((id) & GENMASK(7, 0))
+
+/**
+ * struct scmi_shm_buf - Description of a shared memory buffer
+ * @buf:	Shared memory base address
+ * @size:	Shared memory byte size
+ */
+struct scmi_shm_buf {
+	u8 *buf;
+	size_t size;
+};
+
+/**
+ * struct scmi_mbox_channel - Description of an SCMI mailbox transport
+ * @shm_buf:	Shared memory buffer
+ * @mbox:	Mailbox channel description
+ * @timeout_us:	Timeout in microseconds for the mailbox transfer
+ */
+struct scmi_mbox_channel {
+	struct scmi_shm_buf shm_buf;
+	struct mbox_chan mbox;
+	ulong timeout_us;
+};
+
+/**
+ * struct scmi_arm_smc_channel - Description of an SCMI SMCCC transport
+ * @func_id:	SMCCC function ID used by the SCMI transport
+ * @shm_buf:	Shared memory buffer
+ */
+struct scmi_arm_smc_channel {
+	ulong func_id;
+	struct scmi_shm_buf shm_buf;
+};
+
+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;
+}
+
+/**
+ * Get shared memory configuration defined by the referred DT phandle
+ * Return with a errno compliant value.
+ */
+static int get_shm_buffer(struct udevice *dev, struct scmi_shm_buf *shm)
+{
+	int ret;
+	struct ofnode_phandle_args args;
+	struct resource resource;
+	fdt32_t faddr;
+	phys_addr_t paddr;
+
+	ret = dev_read_phandle_with_args(dev, "shmem", NULL, 0, 0, &args);
+	if (ret)
+		return ret;
+
+	ret = ofnode_read_resource(args.node, 0, &resource);
+	if (ret)
+		return ret;
+
+	faddr = cpu_to_fdt32(resource.start);
+	paddr = ofnode_translate_address(args.node, &faddr);
+
+	shm->size = resource_size(&resource);
+	if (shm->size < sizeof(struct scmi_smt_header)) {
+		dev_err(dev, "Shared memory buffer too small\n");
+		return -EINVAL;
+	}
+
+	shm->buf = devm_ioremap(dev, paddr, shm->size);
+	if (!shm->buf)
+		return -ENOMEM;
+
+	if (dcache_status())
+		mmu_set_region_dcache_behaviour((uintptr_t)shm->buf,
+						shm->size, DCACHE_OFF);
+
+	return 0;
+}
+
+/**
+ * Write SCMI message @msg into a SMT shared buffer @shm_buf.
+ * Return 0 on success and with a negative errno in case of error.
+ */
+static int write_msg_to_smt(struct udevice *dev, struct scmi_shm_buf *shm_buf,
+			    struct scmi_msg *msg)
+{
+	struct scmi_smt_header *hdr = (void *)shm_buf->buf;
+
+	if ((!msg->in_msg && msg->in_msg_sz) ||
+	    (!msg->out_msg && msg->out_msg_sz))
+		return -EINVAL;
+
+	if (!(hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE)) {
+		dev_dbg(dev, "Channel busy\n");
+		return -EBUSY;
+	}
+
+	if (shm_buf->size < (sizeof(*hdr) + msg->in_msg_sz) ||
+	    shm_buf->size < (sizeof(*hdr) + msg->out_msg_sz)) {
+		dev_dbg(dev, "Buffer too small\n");
+		return -ETOOSMALL;
+	}
+
+	/* Load message in shared memory */
+	hdr->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE;
+	hdr->length = msg->in_msg_sz + sizeof(hdr->msg_header);
+	hdr->msg_header = SMT_HEADER_TOKEN(0) |
+			  SMT_HEADER_MESSAGE_TYPE(0) |
+			  SMT_HEADER_PROTOCOL_ID(msg->protocol_id) |
+			  SMT_HEADER_MESSAGE_ID(msg->message_id);
+
+	memcpy_toio(hdr->msg_payload, msg->in_msg, msg->in_msg_sz);
+
+	return 0;
+}
+
+/**
+ * Read SCMI message from a SMT shared buffer @shm_buf and copy it into @msg.
+ * Return 0 on success and with a negative errno in case of error.
+ */
+static int read_resp_from_smt(struct udevice *dev, struct scmi_shm_buf *shm_buf,
+			      struct scmi_msg *msg)
+{
+	struct scmi_smt_header *hdr = (void *)shm_buf->buf;
+
+	if (!(hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE)) {
+		dev_err(dev, "Channel unexpectedly busy\n");
+		return -EBUSY;
+	}
+
+	if (hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR) {
+		dev_err(dev, "Channel error reported, reset channel\n");
+		return -ECOMM;
+	}
+
+	if (hdr->length > msg->out_msg_sz + sizeof(hdr->msg_header)) {
+		dev_err(dev, "Buffer to small\n");
+		return -ETOOSMALL;
+	}
+
+	/* Get the data */
+	msg->out_msg_sz = hdr->length - sizeof(hdr->msg_header);
+	memcpy_fromio(msg->out_msg, hdr->msg_payload, msg->out_msg_sz);
+
+	return 0;
+}
+
+/**
+ * Clear SMT flags in shared buffer to allow further message exchange
+ */
+static void clear_smt_channel(struct scmi_shm_buf *shm_buf)
+{
+	struct scmi_smt_header *hdr = (void *)shm_buf->buf;
+
+	hdr->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR;
+}
+
+#ifdef CONFIG_DM_MAILBOX
+/*
+ * SCMI over mailbox transport
+ */
+
+static int mbox_process_msg(struct udevice *dev, struct scmi_msg *msg)
+{
+	struct scmi_agent *agent = dev_get_priv(dev);
+	struct scmi_mbox_channel *chan = agent->method_priv;
+	int ret;
+
+	ret = write_msg_to_smt(dev, &chan->shm_buf, msg);
+	if (ret)
+		return ret;
+
+	/* Give shm addr to mbox in case it is meaningful */
+	ret = mbox_send(&chan->mbox, chan->shm_buf.buf);
+	if (ret) {
+		dev_err(dev, "Message send failed: %d\n", ret);
+		goto out;
+	}
+
+	/* Receive the response */
+	ret = mbox_recv(&chan->mbox, chan->shm_buf.buf, chan->timeout_us);
+	if (ret) {
+		dev_err(dev, "Response failed: %d, abort\n", ret);
+		goto out;
+	}
+
+	ret = read_resp_from_smt(dev, &chan->shm_buf, msg);
+
+out:
+	clear_smt_channel(&chan->shm_buf);
+
+	return ret;
+}
+
+struct method_ops mbox_channel_ops = {
+	.process_msg = mbox_process_msg,
+};
+
+static int probe_mailbox_channel(struct udevice *dev)
+{
+	struct scmi_agent *agent = dev_get_priv(dev);
+	struct scmi_mbox_channel *chan;
+	int ret;
+
+	chan = devm_kzalloc(dev, sizeof(*chan), GFP_KERNEL);
+	if (!chan)
+		return -ENOMEM;
+
+	chan->timeout_us = TIMEOUT_US_10MS;
+
+	ret = mbox_get_by_index(dev, 0, &chan->mbox);
+	if (ret) {
+		dev_err(dev, "Failed to find mailbox: %d\n", ret);
+		goto out;
+	}
+
+	ret = get_shm_buffer(dev, &chan->shm_buf);
+	if (ret)
+		dev_err(dev, "Failed to get shm resources: %d\n", ret);
+
+out:
+	if (ret) {
+		devm_kfree(dev, chan);
+		return ret;
+	}
+
+	agent->method_ops = &mbox_channel_ops;
+	agent->method_priv = (void *)chan;
+
+	return 0;
+}
+#endif /* CONFIG_DM_MAILBOX */
+
+#ifdef CONFIG_ARM_SMCCC
+/*
+ * SCMI over SMCCC transport
+ */
+
+#define SMCCC_RET_NOT_SUPPORTED         ((unsigned long)-1)
+
+static int arm_smc_process_msg(struct udevice *dev, struct scmi_msg *msg)
+{
+	struct scmi_agent *agent = dev_get_priv(dev);
+	struct scmi_arm_smc_channel *chan = agent->method_priv;
+	struct arm_smccc_res res;
+	int ret;
+
+	ret = write_msg_to_smt(dev, &chan->shm_buf, msg);
+	if (ret)
+		return ret;
+
+	arm_smccc_smc(chan->func_id, 0, 0, 0, 0, 0, 0, 0, &res);
+	if (res.a0 == SMCCC_RET_NOT_SUPPORTED)
+		ret = -ENXIO;
+	else
+		ret = read_resp_from_smt(dev, &chan->shm_buf, msg);
+
+	clear_smt_channel(&chan->shm_buf);
+
+	return ret;
+}
+
+struct method_ops arm_smc_channel_ops = {
+	.process_msg = arm_smc_process_msg,
+};
+
+static int probe_arm_smc_channel(struct udevice *dev)
+{
+	struct scmi_agent *agent = dev_get_priv(dev);
+	struct scmi_arm_smc_channel *chan;
+	u32 func_id;
+	int ret;
+
+	chan = devm_kzalloc(dev, sizeof(*chan), GFP_KERNEL);
+	if (!chan)
+		return -ENOMEM;
+
+	if (dev_read_u32(dev, "arm,smc-id", &func_id)) {
+		dev_err(dev, "Missing property func-id\n");
+		return -EINVAL;
+	}
+
+	chan->func_id = func_id;
+
+	ret = get_shm_buffer(dev, &chan->shm_buf);
+	if (ret) {
+		dev_err(dev, "Failed to get shm resources: %d\n", ret);
+		return ret;
+	}
+
+	agent->method_ops = &arm_smc_channel_ops;
+	agent->method_priv = (void *)chan;
+
+	return 0;
+}
+#endif /* CONFIG_ARM_SMCCC */
+
+int scmi_send_and_process_msg(struct udevice *dev, struct scmi_msg *msg)
+{
+	struct scmi_agent *agent = dev_get_priv(dev);
+
+	return agent->method_ops->process_msg(dev, msg);
+}
+
+static int scmi_remove(struct udevice *dev)
+{
+	struct scmi_agent *agent = dev_get_priv(dev);
+
+	if (agent->method_ops->remove_agent)
+		return agent->method_ops->remove_agent(dev);
+
+	return 0;
+}
+
+static int scmi_probe(struct udevice *dev)
+{
+	switch (dev_get_driver_data(dev)) {
+	case SCMI_MAILBOX_TRANSPORT:
+		if (IS_ENABLED(CONFIG_DM_MAILBOX))
+			return probe_mailbox_channel(dev);
+		break;
+	case SCMI_ARM_SMCCC_TRANSPORT:
+		if (IS_ENABLED(CONFIG_ARM_SMCCC))
+			return probe_arm_smc_channel(dev);
+		break;
+	default:
+		break;
+	}
+
+	return -ENOENT;
+}
+
+static int scmi_bind(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 %u\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 udevice_id scmi_ids[] = {
+	{ .compatible = "arm,scmi", .data = SCMI_MAILBOX_TRANSPORT },
+	{ .compatible = "arm,scmi-smc", .data = SCMI_ARM_SMCCC_TRANSPORT },
+	{ }
+};
+
+U_BOOT_DRIVER(scmi) = {
+	.name		= "scmi",
+	.id		= UCLASS_FIRMWARE,
+	.of_match	= scmi_ids,
+	.priv_auto_alloc_size = sizeof(struct scmi_agent),
+	.bind		= scmi_bind,
+	.probe		= scmi_probe,
+	.remove		= scmi_remove,
+	.flags		= DM_FLAG_OS_PREPARE,
+};
diff --git a/include/scmi.h b/include/scmi.h
new file mode 100644
index 0000000000..a75d38916d
--- /dev/null
+++ b/include/scmi.h
@@ -0,0 +1,82 @@ 
+/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */
+/*
+ * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
+ * Copyright (C) 2019, Linaro Limited
+ */
+#ifndef SCMI_H
+#define SCMI_H
+
+#include <asm/types.h>
+
+/**
+ * An SCMI agent represent on communication path from a device driver to
+ * the remote SCMI server which driver sends messages to and receives
+ * response messages from.
+ */
+struct scmi_agent;
+
+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,
+};
+
+/*
+ * 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 or 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;
+};
+
+/**
+ * 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, a negative errno otherwise
+ */
+int scmi_send_and_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 */