]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
firmware: scmi: mailbox/smt agent device
authorEtienne Carriere <etienne.carriere@linaro.org>
Wed, 9 Sep 2020 16:44:01 +0000 (18:44 +0200)
committerTom Rini <trini@konsulko.com>
Wed, 30 Sep 2020 15:55:23 +0000 (11:55 -0400)
This change implements a mailbox transport using SMT format for SCMI
exchanges. This implementation follows the Linux kernel and
SCP-firmware [1] as references implementation for SCMI message
processing using SMT format for communication channel meta-data.

Use of mailboxes in SCMI FDT bindings are defined in the Linux kernel
DT bindings since v4.17.

Links: [1] https://github.com/ARM-software/SCP-firmware
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>
Reviewed-by: Simon Glass <sjg@chromium.org>
drivers/firmware/scmi/Kconfig
drivers/firmware/scmi/Makefile
drivers/firmware/scmi/mailbox_agent.c [new file with mode: 0644]
drivers/firmware/scmi/smt.c [new file with mode: 0644]
drivers/firmware/scmi/smt.h [new file with mode: 0644]

index 57e2ebbe4281dbe4c83c11f83df127c8dd2fd130..c501bf4943ffc325cebc7d81c4d4231588548188 100644 (file)
@@ -2,7 +2,7 @@ config SCMI_FIRMWARE
        bool "Enable SCMI support"
        select FIRMWARE
        select OF_TRANSLATE
-       depends on SANDBOX
+       depends on SANDBOX || DM_MAILBOX
        help
          System Control and Management Interface (SCMI) is a communication
          protocol that defines standard interfaces for power, performance
@@ -14,4 +14,6 @@ config SCMI_FIRMWARE
          or a companion host in the CPU system.
 
          Communications between agent (client) and the SCMI server are
-         based on message exchange.
+         based on message exchange. Messages can be exchange over tranport
+         channels as a mailbox device with some piece of identified shared
+         memory.
index 336ea1f2a326cbe9cc36db3e398b2c26eba4150f..d22f53efe7fd947bf8ac6f2a40293617b58af435 100644 (file)
@@ -1,2 +1,4 @@
 obj-y  += scmi_agent-uclass.o
+obj-y  += smt.o
+obj-$(CONFIG_DM_MAILBOX)       += mailbox_agent.o
 obj-$(CONFIG_SANDBOX)          += sandbox-scmi_agent.o
diff --git a/drivers/firmware/scmi/mailbox_agent.c b/drivers/firmware/scmi/mailbox_agent.c
new file mode 100644 (file)
index 0000000..7d9fb36
--- /dev/null
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Linaro Limited.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <mailbox.h>
+#include <scmi_agent.h>
+#include <scmi_agent-uclass.h>
+#include <dm/devres.h>
+#include <linux/compat.h>
+
+#include "smt.h"
+
+#define TIMEOUT_US_10MS                        10000
+
+/**
+ * struct scmi_mbox_channel - Description of an SCMI mailbox transport
+ * @smt:       Shared memory buffer
+ * @mbox:      Mailbox channel description
+ * @timeout_us:        Timeout in microseconds for the mailbox transfer
+ */
+struct scmi_mbox_channel {
+       struct scmi_smt smt;
+       struct mbox_chan mbox;
+       ulong timeout_us;
+};
+
+static int scmi_mbox_process_msg(struct udevice *dev, struct scmi_msg *msg)
+{
+       struct scmi_mbox_channel *chan = dev_get_priv(dev);
+       int ret;
+
+       ret = scmi_write_msg_to_smt(dev, &chan->smt, msg);
+       if (ret)
+               return ret;
+
+       /* Give shm addr to mbox in case it is meaningful */
+       ret = mbox_send(&chan->mbox, chan->smt.buf);
+       if (ret) {
+               dev_err(dev, "Message send failed: %d\n", ret);
+               goto out;
+       }
+
+       /* Receive the response */
+       ret = mbox_recv(&chan->mbox, chan->smt.buf, chan->timeout_us);
+       if (ret) {
+               dev_err(dev, "Response failed: %d, abort\n", ret);
+               goto out;
+       }
+
+       ret = scmi_read_resp_from_smt(dev, &chan->smt, msg);
+
+out:
+       scmi_clear_smt_channel(&chan->smt);
+
+       return ret;
+}
+
+int scmi_mbox_probe(struct udevice *dev)
+{
+       struct scmi_mbox_channel *chan = dev_get_priv(dev);
+       int ret;
+
+       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 = scmi_dt_get_smt_buffer(dev, &chan->smt);
+       if (ret)
+               dev_err(dev, "Failed to get shm resources: %d\n", ret);
+
+out:
+       if (ret)
+               devm_kfree(dev, chan);
+
+       return ret;
+}
+
+static const struct udevice_id scmi_mbox_ids[] = {
+       { .compatible = "arm,scmi" },
+       { }
+};
+
+static const struct scmi_agent_ops scmi_mbox_ops = {
+       .process_msg = scmi_mbox_process_msg,
+};
+
+U_BOOT_DRIVER(scmi_mbox) = {
+       .name           = "scmi-over-mailbox",
+       .id             = UCLASS_SCMI_AGENT,
+       .of_match       = scmi_mbox_ids,
+       .priv_auto_alloc_size = sizeof(struct scmi_mbox_channel),
+       .probe          = scmi_mbox_probe,
+       .ops            = &scmi_mbox_ops,
+};
diff --git a/drivers/firmware/scmi/smt.c b/drivers/firmware/scmi/smt.c
new file mode 100644 (file)
index 0000000..ce8fe49
--- /dev/null
@@ -0,0 +1,139 @@
+// 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 <scmi_agent.h>
+#include <asm/cache.h>
+#include <asm/system.h>
+#include <dm/ofnode.h>
+#include <linux/compat.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+
+#include "smt.h"
+
+/**
+ * Get shared memory configuration defined by the referred DT phandle
+ * Return with a errno compliant value.
+ */
+int scmi_dt_get_smt_buffer(struct udevice *dev, struct scmi_smt *smt)
+{
+       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);
+
+       smt->size = resource_size(&resource);
+       if (smt->size < sizeof(struct scmi_smt_header)) {
+               dev_err(dev, "Shared memory buffer too small\n");
+               return -EINVAL;
+       }
+
+       smt->buf = devm_ioremap(dev, paddr, smt->size);
+       if (!smt->buf)
+               return -ENOMEM;
+
+#ifdef CONFIG_ARM
+       if (dcache_status())
+               mmu_set_region_dcache_behaviour((uintptr_t)smt->buf,
+                                               smt->size, DCACHE_OFF);
+#endif
+
+       return 0;
+}
+
+/**
+ * Write SCMI message @msg into a SMT shared buffer @smt.
+ * Return 0 on success and with a negative errno in case of error.
+ */
+int scmi_write_msg_to_smt(struct udevice *dev, struct scmi_smt *smt,
+                         struct scmi_msg *msg)
+{
+       struct scmi_smt_header *hdr = (void *)smt->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 (smt->size < (sizeof(*hdr) + msg->in_msg_sz) ||
+           smt->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 @smt and copy it into @msg.
+ * Return 0 on success and with a negative errno in case of error.
+ */
+int scmi_read_resp_from_smt(struct udevice *dev, struct scmi_smt *smt,
+                           struct scmi_msg *msg)
+{
+       struct scmi_smt_header *hdr = (void *)smt->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
+ */
+void scmi_clear_smt_channel(struct scmi_smt *smt)
+{
+       struct scmi_smt_header *hdr = (void *)smt->buf;
+
+       hdr->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR;
+}
diff --git a/drivers/firmware/scmi/smt.h b/drivers/firmware/scmi/smt.h
new file mode 100644 (file)
index 0000000..a8c0987
--- /dev/null
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
+ * Copyright (C) 2019-2020 Linaro Limited.
+ */
+#ifndef SCMI_SMT_H
+#define SCMI_SMT_H
+
+#include <asm/types.h>
+
+/**
+ * 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_smt - Description of a SMT memory buffer
+ * @buf:       Shared memory base address
+ * @size:      Shared memory byte size
+ */
+struct scmi_smt {
+       u8 *buf;
+       size_t size;
+};
+
+static inline bool scmi_smt_channel_is_free(struct scmi_smt *smt)
+{
+       struct scmi_smt_header *hdr = (void *)smt->buf;
+
+       return hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE;
+}
+
+static inline bool scmi_smt_channel_reports_error(struct scmi_smt *smt)
+{
+       struct scmi_smt_header *hdr = (void *)smt->buf;
+
+       return hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR;
+}
+
+static inline void scmi_smt_get_channel(struct scmi_smt *smt)
+{
+       struct scmi_smt_header *hdr = (void *)smt->buf;
+
+       hdr->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE;
+}
+
+static inline void scmi_smt_put_channel(struct scmi_smt *smt)
+{
+       struct scmi_smt_header *hdr = (void *)smt->buf;
+
+       hdr->channel_status |= SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE;
+       hdr->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR;
+}
+
+int scmi_dt_get_smt_buffer(struct udevice *dev, struct scmi_smt *smt);
+
+int scmi_write_msg_to_smt(struct udevice *dev, struct scmi_smt *smt,
+                         struct scmi_msg *msg);
+
+int scmi_read_resp_from_smt(struct udevice *dev, struct scmi_smt *smt,
+                           struct scmi_msg *msg);
+
+void scmi_clear_smt_channel(struct scmi_smt *smt);
+
+#endif /* SCMI_SMT_H */