summaryrefslogtreecommitdiff
path: root/drivers/firmware
diff options
context:
space:
mode:
authorEtienne Carriere <etienne.carriere@linaro.org>2020-09-09 19:44:00 +0300
committerTom Rini <trini@konsulko.com>2020-09-30 18:55:23 +0300
commit358599efd827b0ee48af864537cc86facc9167c0 (patch)
tree4390020cc2353ec659f9e69a40ea043a0834e551 /drivers/firmware
parent0ced26a494ec089156dc4fd0a57c6ea45942c0dd (diff)
downloadu-boot-358599efd827b0ee48af864537cc86facc9167c0.tar.xz
firmware: add SCMI agent uclass
This change introduces SCMI agent uclass to interact with a firmware using the SCMI protocols [1]. SCMI agent uclass currently supports a single method to request processing of the SCMI message by an identified server. A SCMI message is made of a byte payload associated to a protocol ID and a message ID, all defined by the SCMI specification [1]. On return from process_msg() method, the caller gets the service response. SCMI agent uclass defines a post bind generic sequence for all devices. The sequence binds all the SCMI protocols listed in the FDT for that SCMI agent device. Currently none, but later change will introduce protocols. This change implements a simple sandbox device for the SCMI agent uclass. The sandbox nicely answers SCMI_NOT_SUPPORTED to SCMI messages. To prepare for further test support, the sandbox exposes a architecture function for test application to read the sandbox emulated devices state. Currently supports 2 SCMI agents, identified by an ID in the FDT device name. The simplistic DM test does nothing yet. SCMI agent uclass is designed for platforms that embed a SCMI server in a firmware hosted somewhere, for example in a companion co-processor or in the secure world of the executing processor. SCMI protocols allow an SCMI agent to discover and access external resources as clock, reset controllers and more. SCMI agent and server communicate following the SCMI specification [1]. This SCMI agent implementation complies with the DT bindings defined in the Linux kernel source tree regarding SCMI agent description since v5.8. 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> Reviewed-by: Simon Glass <sjg@chromium.org>
Diffstat (limited to 'drivers/firmware')
-rw-r--r--drivers/firmware/Kconfig2
-rw-r--r--drivers/firmware/Makefile1
-rw-r--r--drivers/firmware/scmi/Kconfig17
-rw-r--r--drivers/firmware/scmi/Makefile2
-rw-r--r--drivers/firmware/scmi/sandbox-scmi_agent.c142
-rw-r--r--drivers/firmware/scmi/scmi_agent-uclass.c107
6 files changed, 271 insertions, 0 deletions
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..3179438aab
--- /dev/null
+++ b/drivers/firmware/scmi/sandbox-scmi_agent.c
@@ -0,0 +1,142 @@
+// 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;
+}
+
+static void debug_print_agent_state(struct udevice *dev, char *str)
+{
+ struct sandbox_scmi_agent *agent = dev_get_priv(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 = dev_get_priv(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 = dev_get_priv(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,
+};