summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2022-06-23 14:30:27 -0400
committerTom Rini <trini@konsulko.com>2022-06-23 14:30:27 -0400
commit31016a5a853cb5b8e27e9fdf956d8250ae59eca9 (patch)
treeb80400cbdcabd651926ee4e4eedc95cdb27b05b2
parent3e00721b3b8fed05a99cfcde5b4fdc210f0b33ab (diff)
parent53355bb86c25d9cced1493df9fc95140acece556 (diff)
Merge branch '2022-06-23-scmi-optee-and-smccc-updates' into next
This consists of two slightly related series. For the first, to quote the author: This series implements 2 features in driver/firmware/scmi. First, a single change adds support for SCMI OP-TEE transport to use OP-TEE native shared memory. See the 1st patch in this series: "firmware: scmi: optee: use TEE shared memory for SCMI messages". Then come changes for supporting multi-channel in the SCMI drivers. I've split the implementation in 11 several small incremental changes in the hope it helps the review. Few minor fixup commits are also inserted in the series. And the second series implements some smccc improvements.
-rw-r--r--MAINTAINERS5
-rw-r--r--drivers/clk/clk_scmi.c33
-rw-r--r--drivers/firmware/Kconfig8
-rw-r--r--drivers/firmware/psci.c91
-rw-r--r--drivers/firmware/scmi/mailbox_agent.c65
-rw-r--r--drivers/firmware/scmi/optee_agent.c147
-rw-r--r--drivers/firmware/scmi/sandbox-scmi_agent.c1
-rw-r--r--drivers/firmware/scmi/scmi_agent-uclass.c48
-rw-r--r--drivers/firmware/scmi/smccc_agent.c56
-rw-r--r--drivers/firmware/scmi/smt.c53
-rw-r--r--drivers/firmware/scmi/smt.h45
-rw-r--r--drivers/power/regulator/scmi_regulator.c36
-rw-r--r--drivers/reset/reset-scmi.c25
-rw-r--r--drivers/rng/Kconfig9
-rw-r--r--drivers/rng/Makefile1
-rw-r--r--drivers/rng/smccc_trng.c207
-rw-r--r--include/linux/arm-smccc.h20
-rw-r--r--include/linux/psci.h14
-rw-r--r--include/scmi_agent-uclass.h15
-rw-r--r--include/scmi_agent.h14
20 files changed, 807 insertions, 86 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index ba9fdb5a667..5945ba1c7a9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1278,6 +1278,11 @@ F: drivers/gpio/sl28cpld-gpio.c
F: drivers/misc/sl28cpld.c
F: drivers/watchdog/sl28cpld-wdt.c
+SMCCC TRNG
+M: Etienne Carriere <etienne.carriere@linaro.org>
+S: Maintained
+F: drivers/rng/smccc_trng.c
+
SPI
M: Jagan Teki <jagan@amarulasolutions.com>
S: Maintained
diff --git a/drivers/clk/clk_scmi.c b/drivers/clk/clk_scmi.c
index 5aaabcf0b44..d172fed24c9 100644
--- a/drivers/clk/clk_scmi.c
+++ b/drivers/clk/clk_scmi.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * Copyright (C) 2019-2020 Linaro Limited
+ * Copyright (C) 2019-2022 Linaro Limited
*/
#define LOG_CATEGORY UCLASS_CLK
@@ -13,8 +13,17 @@
#include <asm/types.h>
#include <linux/clk-provider.h>
+/**
+ * struct scmi_clk_priv - Private data for SCMI clocks
+ * @channel: Reference to the SCMI channel to use
+ */
+struct scmi_clk_priv {
+ struct scmi_channel *channel;
+};
+
static int scmi_clk_get_num_clock(struct udevice *dev, size_t *num_clocks)
{
+ struct scmi_clk_priv *priv = dev_get_priv(dev);
struct scmi_clk_protocol_attr_out out;
struct scmi_msg msg = {
.protocol_id = SCMI_PROTOCOL_ID_CLOCK,
@@ -24,7 +33,7 @@ static int scmi_clk_get_num_clock(struct udevice *dev, size_t *num_clocks)
};
int ret;
- ret = devm_scmi_process_msg(dev, &msg);
+ ret = devm_scmi_process_msg(dev, priv->channel, &msg);
if (ret)
return ret;
@@ -35,6 +44,7 @@ static int scmi_clk_get_num_clock(struct udevice *dev, size_t *num_clocks)
static int scmi_clk_get_attibute(struct udevice *dev, int clkid, char **name)
{
+ struct scmi_clk_priv *priv = dev_get_priv(dev);
struct scmi_clk_attribute_in in = {
.clock_id = clkid,
};
@@ -49,7 +59,7 @@ static int scmi_clk_get_attibute(struct udevice *dev, int clkid, char **name)
};
int ret;
- ret = devm_scmi_process_msg(dev, &msg);
+ ret = devm_scmi_process_msg(dev, priv->channel, &msg);
if (ret)
return ret;
@@ -60,6 +70,7 @@ static int scmi_clk_get_attibute(struct udevice *dev, int clkid, char **name)
static int scmi_clk_gate(struct clk *clk, int enable)
{
+ struct scmi_clk_priv *priv = dev_get_priv(clk->dev);
struct scmi_clk_state_in in = {
.clock_id = clk->id,
.attributes = enable,
@@ -70,7 +81,7 @@ static int scmi_clk_gate(struct clk *clk, int enable)
in, out);
int ret;
- ret = devm_scmi_process_msg(clk->dev, &msg);
+ ret = devm_scmi_process_msg(clk->dev, priv->channel, &msg);
if (ret)
return ret;
@@ -89,6 +100,7 @@ static int scmi_clk_disable(struct clk *clk)
static ulong scmi_clk_get_rate(struct clk *clk)
{
+ struct scmi_clk_priv *priv = dev_get_priv(clk->dev);
struct scmi_clk_rate_get_in in = {
.clock_id = clk->id,
};
@@ -98,7 +110,7 @@ static ulong scmi_clk_get_rate(struct clk *clk)
in, out);
int ret;
- ret = devm_scmi_process_msg(clk->dev, &msg);
+ ret = devm_scmi_process_msg(clk->dev, priv->channel, &msg);
if (ret < 0)
return ret;
@@ -111,6 +123,7 @@ static ulong scmi_clk_get_rate(struct clk *clk)
static ulong scmi_clk_set_rate(struct clk *clk, ulong rate)
{
+ struct scmi_clk_priv *priv = dev_get_priv(clk->dev);
struct scmi_clk_rate_set_in in = {
.clock_id = clk->id,
.flags = SCMI_CLK_RATE_ROUND_CLOSEST,
@@ -123,7 +136,7 @@ static ulong scmi_clk_set_rate(struct clk *clk, ulong rate)
in, out);
int ret;
- ret = devm_scmi_process_msg(clk->dev, &msg);
+ ret = devm_scmi_process_msg(clk->dev, priv->channel, &msg);
if (ret < 0)
return ret;
@@ -136,10 +149,15 @@ static ulong scmi_clk_set_rate(struct clk *clk, ulong rate)
static int scmi_clk_probe(struct udevice *dev)
{
+ struct scmi_clk_priv *priv = dev_get_priv(dev);
struct clk *clk;
size_t num_clocks, i;
int ret;
+ ret = devm_scmi_of_get_channel(dev, &priv->channel);
+ if (ret)
+ return ret;
+
if (!CONFIG_IS_ENABLED(CLK_CCF))
return 0;
@@ -186,5 +204,6 @@ U_BOOT_DRIVER(scmi_clock) = {
.name = "scmi_clk",
.id = UCLASS_CLK,
.ops = &scmi_clk_ops,
- .probe = &scmi_clk_probe,
+ .probe = scmi_clk_probe,
+ .priv_auto = sizeof(struct scmi_clk_priv *),
};
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index ef958b3a7a4..f10d1aaf4b6 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -37,4 +37,12 @@ config ZYNQMP_FIRMWARE
Say yes to enable ZynqMP firmware interface driver.
If in doubt, say N.
+config ARM_SMCCC_FEATURES
+ bool "Arm SMCCC features discovery"
+ depends on ARM_PSCI_FW
+ help
+ Discover Arm SMCCC features for which a U-Boot driver is defined. When enabled,
+ the PSCI driver is always probed and binds dirvers registered to the Arm SMCCC
+ services if any and reported as supported by the SMCCC firmware.
+
source "drivers/firmware/scmi/Kconfig"
diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c
index 657e7eb5aea..ef3e9836461 100644
--- a/drivers/firmware/psci.c
+++ b/drivers/firmware/psci.c
@@ -9,18 +9,20 @@
#include <common.h>
#include <command.h>
#include <dm.h>
+#include <efi_loader.h>
#include <irq_func.h>
+#include <linker_lists.h>
#include <log.h>
-#include <dm/lists.h>
-#include <efi_loader.h>
#include <sysreset.h>
-#include <linux/delay.h>
-#include <linux/libfdt.h>
+#include <asm/system.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
#include <linux/arm-smccc.h>
+#include <linux/delay.h>
#include <linux/errno.h>
+#include <linux/libfdt.h>
#include <linux/printk.h>
#include <linux/psci.h>
-#include <asm/system.h>
#define DRIVER_NAME "psci"
@@ -95,6 +97,76 @@ static bool psci_is_system_reset2_supported(void)
return false;
}
+static void smccc_invoke_hvc(unsigned long a0, unsigned long a1,
+ unsigned long a2, unsigned long a3,
+ unsigned long a4, unsigned long a5,
+ unsigned long a6, unsigned long a7,
+ struct arm_smccc_res *res)
+{
+ arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res);
+}
+
+static void smccc_invoke_smc(unsigned long a0, unsigned long a1,
+ unsigned long a2, unsigned long a3,
+ unsigned long a4, unsigned long a5,
+ unsigned long a6, unsigned long a7,
+ struct arm_smccc_res *res)
+{
+ arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res);
+}
+
+static int bind_smccc_features(struct udevice *dev, int psci_method)
+{
+ struct psci_plat_data *pdata = dev_get_plat(dev);
+ struct arm_smccc_feature *feature;
+ size_t feature_cnt, n;
+
+ if (!IS_ENABLED(CONFIG_ARM_SMCCC_FEATURES))
+ return 0;
+
+ /*
+ * SMCCC features discovery invoke SMCCC standard function ID
+ * ARM_SMCCC_ARCH_FEATURES but this sequence requires that this
+ * standard ARM_SMCCC_ARCH_FEATURES function ID itself is supported.
+ * It is queried here with invoking PSCI_FEATURES known available
+ * from PSCI 1.0.
+ */
+ if (!device_is_compatible(dev, "arm,psci-1.0") ||
+ PSCI_VERSION_MAJOR(psci_0_2_get_version()) == 0)
+ return 0;
+
+ if (request_psci_features(ARM_SMCCC_ARCH_FEATURES) ==
+ PSCI_RET_NOT_SUPPORTED)
+ return 0;
+
+ if (psci_method == PSCI_METHOD_HVC)
+ pdata->invoke_fn = smccc_invoke_hvc;
+ else
+ pdata->invoke_fn = smccc_invoke_smc;
+
+ feature_cnt = ll_entry_count(struct arm_smccc_feature, arm_smccc_feature);
+ feature = ll_entry_start(struct arm_smccc_feature, arm_smccc_feature);
+
+ for (n = 0; n < feature_cnt; n++, feature++) {
+ const char *drv_name = feature->driver_name;
+ struct udevice *dev2;
+ int ret;
+
+ if (!feature->is_supported || !feature->is_supported(pdata->invoke_fn))
+ continue;
+
+ ret = device_bind_driver(dev, drv_name, drv_name, &dev2);
+ if (ret) {
+ pr_warn("%s was not bound: %d, ignore\n", drv_name, ret);
+ continue;
+ }
+
+ dev_set_parent_plat(dev2, dev_get_plat(dev));
+ }
+
+ return 0;
+}
+
static int psci_bind(struct udevice *dev)
{
/* No SYSTEM_RESET support for PSCI 0.1 */
@@ -109,6 +181,10 @@ static int psci_bind(struct udevice *dev)
pr_debug("PSCI System Reset was not bound.\n");
}
+ /* From PSCI v1.0 onward we can discover services through ARM_SMCCC_FEATURE */
+ if (IS_ENABLED(CONFIG_ARM_SMCCC_FEATURES) && device_is_compatible(dev, "arm,psci-1.0"))
+ dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND);
+
return 0;
}
@@ -136,7 +212,7 @@ static int psci_probe(struct udevice *dev)
return -EINVAL;
}
- return 0;
+ return bind_smccc_features(dev, psci_method);
}
/**
@@ -240,4 +316,7 @@ U_BOOT_DRIVER(psci) = {
.of_match = psci_of_match,
.bind = psci_bind,
.probe = psci_probe,
+#ifdef CONFIG_ARM_SMCCC_FEATURES
+ .plat_auto = sizeof(struct psci_plat_data),
+#endif
};
diff --git a/drivers/firmware/scmi/mailbox_agent.c b/drivers/firmware/scmi/mailbox_agent.c
index 8e4af0c8faf..3efdab9e723 100644
--- a/drivers/firmware/scmi/mailbox_agent.c
+++ b/drivers/firmware/scmi/mailbox_agent.c
@@ -31,9 +31,19 @@ struct scmi_mbox_channel {
ulong timeout_us;
};
-static int scmi_mbox_process_msg(struct udevice *dev, struct scmi_msg *msg)
+/**
+ * struct scmi_channel - Channel instance referenced in SCMI drivers
+ * @ref: Reference to local channel instance
+ **/
+struct scmi_channel {
+ struct scmi_mbox_channel ref;
+};
+
+static int scmi_mbox_process_msg(struct udevice *dev,
+ struct scmi_channel *channel,
+ struct scmi_msg *msg)
{
- struct scmi_mbox_channel *chan = dev_get_plat(dev);
+ struct scmi_mbox_channel *chan = &channel->ref;
int ret;
ret = scmi_write_msg_to_smt(dev, &chan->smt, msg);
@@ -62,13 +72,10 @@ out:
return ret;
}
-int scmi_mbox_of_to_plat(struct udevice *dev)
+static int setup_channel(struct udevice *dev, struct scmi_mbox_channel *chan)
{
- struct scmi_mbox_channel *chan = dev_get_plat(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);
@@ -76,10 +83,51 @@ int scmi_mbox_of_to_plat(struct udevice *dev)
}
ret = scmi_dt_get_smt_buffer(dev, &chan->smt);
- if (ret)
+ if (ret) {
dev_err(dev, "Failed to get shm resources: %d\n", ret);
+ return ret;
+ }
- return ret;
+ chan->timeout_us = TIMEOUT_US_10MS;
+
+ return 0;
+}
+
+static int scmi_mbox_get_channel(struct udevice *dev,
+ struct scmi_channel **channel)
+{
+ struct scmi_mbox_channel *base_chan = dev_get_plat(dev->parent);
+ struct scmi_mbox_channel *chan;
+ int ret;
+
+ if (!dev_read_prop(dev, "shmem", NULL)) {
+ /* Uses agent base channel */
+ *channel = container_of(base_chan, struct scmi_channel, ref);
+
+ return 0;
+ }
+
+ chan = calloc(1, sizeof(*chan));
+ if (!chan)
+ return -ENOMEM;
+
+ /* Setup a dedicated channel for the protocol */
+ ret = setup_channel(dev, chan);
+ if (ret) {
+ free(chan);
+ return ret;
+ }
+
+ *channel = (void *)chan;
+
+ return 0;
+}
+
+int scmi_mbox_of_to_plat(struct udevice *dev)
+{
+ struct scmi_mbox_channel *chan = dev_get_plat(dev);
+
+ return setup_channel(dev, chan);
}
static const struct udevice_id scmi_mbox_ids[] = {
@@ -88,6 +136,7 @@ static const struct udevice_id scmi_mbox_ids[] = {
};
static const struct scmi_agent_ops scmi_mbox_ops = {
+ .of_get_channel = scmi_mbox_get_channel,
.process_msg = scmi_mbox_process_msg,
};
diff --git a/drivers/firmware/scmi/optee_agent.c b/drivers/firmware/scmi/optee_agent.c
index 1f265922343..2b2b8c1670a 100644
--- a/drivers/firmware/scmi/optee_agent.c
+++ b/drivers/firmware/scmi/optee_agent.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * Copyright (C) 2020-2021 Linaro Limited.
+ * Copyright (C) 2020-2022 Linaro Limited.
*/
#define LOG_CATEGORY UCLASS_SCMI_AGENT
@@ -36,6 +36,14 @@ struct scmi_optee_channel {
};
/**
+ * struct scmi_channel - Channel instance referenced in SCMI drivers
+ * @ref: Reference to local channel instance
+ **/
+struct scmi_channel {
+ struct scmi_optee_channel ref;
+};
+
+/**
* struct channel_session - Aggreates SCMI service session context references
* @tee: OP-TEE device to invoke
* @tee_session: OP-TEE session identifier
@@ -91,13 +99,27 @@ enum optee_smci_pta_cmd {
/*
* PTA_SCMI_CMD_GET_CHANNEL - Get channel handle
*
- * SCMI shm information are 0 if agent expects to use OP-TEE regular SHM
- *
* [in] value[0].a: Channel identifier
* [out] value[0].a: Returned channel handle
* [in] value[0].b: Requested capabilities mask (enum pta_scmi_caps)
*/
PTA_SCMI_CMD_GET_CHANNEL = 3,
+
+ /*
+ * PTA_SCMI_CMD_PROCESS_MSG_CHANNEL - Process SCMI message in MSG
+ * buffers pointed by memref parameters
+ *
+ * [in] value[0].a: Channel handle
+ * [in] memref[1]: Message buffer (MSG header and SCMI payload)
+ * [out] memref[2]: Response buffer (MSG header and SCMI payload)
+ *
+ * Shared memories used for SCMI message/response are MSG buffers
+ * referenced by param[1] and param[2]. MSG transport protocol
+ * uses a 32bit header to carry SCMI meta-data (protocol ID and
+ * protocol message ID) followed by the effective SCMI message
+ * payload.
+ */
+ PTA_SCMI_CMD_PROCESS_MSG_CHANNEL = 4,
};
/*
@@ -106,14 +128,22 @@ enum optee_smci_pta_cmd {
* PTA_SCMI_CAPS_SMT_HEADER
* When set, OP-TEE supports command using SMT header protocol (SCMI shmem) in
* shared memory buffers to carry SCMI protocol synchronisation information.
+ *
+ * PTA_SCMI_CAPS_MSG_HEADER
+ * When set, OP-TEE supports command using MSG header protocol in an OP-TEE
+ * shared memory to carry SCMI protocol synchronisation information and SCMI
+ * message payload.
*/
#define PTA_SCMI_CAPS_NONE 0
#define PTA_SCMI_CAPS_SMT_HEADER BIT(0)
+#define PTA_SCMI_CAPS_MSG_HEADER BIT(1)
+#define PTA_SCMI_CAPS_MASK (PTA_SCMI_CAPS_SMT_HEADER | \
+ PTA_SCMI_CAPS_MSG_HEADER)
-static int open_channel(struct udevice *dev, struct channel_session *sess)
+static int open_channel(struct udevice *dev, struct scmi_optee_channel *chan,
+ struct channel_session *sess)
{
const struct tee_optee_ta_uuid uuid = TA_SCMI_UUID;
- struct scmi_optee_channel *chan = dev_get_plat(dev);
struct tee_open_session_arg sess_arg = { };
struct tee_invoke_arg cmd_arg = { };
struct tee_param param[1] = { };
@@ -139,7 +169,10 @@ static int open_channel(struct udevice *dev, struct channel_session *sess)
param[0].attr = TEE_PARAM_ATTR_TYPE_VALUE_INOUT;
param[0].u.value.a = chan->channel_id;
- param[0].u.value.b = PTA_SCMI_CAPS_SMT_HEADER;
+ if (chan->dyn_shm)
+ param[0].u.value.b = PTA_SCMI_CAPS_MSG_HEADER;
+ else
+ param[0].u.value.b = PTA_SCMI_CAPS_SMT_HEADER;
ret = tee_invoke_func(sess->tee, &cmd_arg, ARRAY_SIZE(param), param);
if (ret || cmd_arg.ret) {
@@ -162,45 +195,58 @@ static void close_channel(struct channel_session *sess)
tee_close_session(sess->tee, sess->tee_session);
}
-static int invoke_cmd(struct udevice *dev, struct channel_session *sess,
- struct scmi_msg *msg)
+static int invoke_cmd(struct udevice *dev, struct scmi_optee_channel *chan,
+ struct channel_session *sess, struct scmi_msg *msg)
{
- struct scmi_optee_channel *chan = dev_get_plat(dev);
struct tee_invoke_arg arg = { };
- struct tee_param param[2] = { };
+ struct tee_param param[3] = { };
int ret;
- scmi_write_msg_to_smt(dev, &chan->smt, msg);
-
arg.session = sess->tee_session;
param[0].attr = TEE_PARAM_ATTR_TYPE_VALUE_INPUT;
param[0].u.value.a = sess->channel_hdl;
- if (chan->dyn_shm) {
- arg.func = PTA_SCMI_CMD_PROCESS_SMT_CHANNEL_MESSAGE;
- param[1].attr = TEE_PARAM_ATTR_TYPE_MEMREF_INOUT;
+ if (sess->tee_shm) {
+ size_t in_size;
+
+ ret = scmi_msg_to_smt_msg(dev, &chan->smt, msg, &in_size);
+ if (ret < 0)
+ return ret;
+
+ arg.func = PTA_SCMI_CMD_PROCESS_MSG_CHANNEL;
+ param[1].attr = TEE_PARAM_ATTR_TYPE_MEMREF_INPUT;
param[1].u.memref.shm = sess->tee_shm;
- param[1].u.memref.size = SCMI_SHM_SIZE;
+ param[1].u.memref.size = in_size;
+ param[2].attr = TEE_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
+ param[2].u.memref.shm = sess->tee_shm;
+ param[2].u.memref.size = sess->tee_shm->size;
} else {
arg.func = PTA_SCMI_CMD_PROCESS_SMT_CHANNEL;
+ scmi_write_msg_to_smt(dev, &chan->smt, msg);
}
ret = tee_invoke_func(sess->tee, &arg, ARRAY_SIZE(param), param);
if (ret || arg.ret) {
if (!ret)
ret = -EPROTO;
+
+ return ret;
+ }
+
+ if (sess->tee_shm) {
+ ret = scmi_msg_from_smt_msg(dev, &chan->smt, msg,
+ param[2].u.memref.size);
} else {
ret = scmi_read_resp_from_smt(dev, &chan->smt, msg);
+ scmi_clear_smt_channel(&chan->smt);
}
- scmi_clear_smt_channel(&chan->smt);
-
return ret;
}
-static int prepare_shm(struct udevice *dev, struct channel_session *sess)
+static int prepare_shm(struct udevice *dev, struct scmi_optee_channel *chan,
+ struct channel_session *sess)
{
- struct scmi_optee_channel *chan = dev_get_plat(dev);
int ret;
/* Static shm is already prepared by the firmware: nothing to do */
@@ -217,9 +263,6 @@ static int prepare_shm(struct udevice *dev, struct channel_session *sess)
chan->smt.buf = sess->tee_shm->addr;
- /* Initialize shm buffer for message exchanges */
- scmi_clear_smt_channel(&chan->smt);
-
return 0;
}
@@ -231,20 +274,23 @@ static void release_shm(struct udevice *dev, struct channel_session *sess)
tee_shm_free(sess->tee_shm);
}
-static int scmi_optee_process_msg(struct udevice *dev, struct scmi_msg *msg)
+static int scmi_optee_process_msg(struct udevice *dev,
+ struct scmi_channel *channel,
+ struct scmi_msg *msg)
{
- struct channel_session sess;
+ struct scmi_optee_channel *chan = &channel->ref;
+ struct channel_session sess = { };
int ret;
- ret = open_channel(dev, &sess);
+ ret = open_channel(dev, chan, &sess);
if (ret)
return ret;
- ret = prepare_shm(dev, &sess);
+ ret = prepare_shm(dev, chan, &sess);
if (ret)
goto out;
- ret = invoke_cmd(dev, &sess, msg);
+ ret = invoke_cmd(dev, chan, &sess, msg);
release_shm(dev, &sess);
@@ -254,9 +300,8 @@ out:
return ret;
}
-static int scmi_optee_of_to_plat(struct udevice *dev)
+static int setup_channel(struct udevice *dev, struct scmi_optee_channel *chan)
{
- struct scmi_optee_channel *chan = dev_get_plat(dev);
int ret;
if (dev_read_u32(dev, "linaro,optee-channel-id", &chan->channel_id)) {
@@ -278,13 +323,52 @@ static int scmi_optee_of_to_plat(struct udevice *dev)
return 0;
}
+static int scmi_optee_get_channel(struct udevice *dev,
+ struct scmi_channel **channel)
+{
+ struct scmi_optee_channel *base_chan = dev_get_plat(dev->parent);
+ struct scmi_optee_channel *chan;
+ u32 channel_id;
+ int ret;
+
+ if (dev_read_u32(dev, "linaro,optee-channel-id", &channel_id)) {
+ /* Uses agent base channel */
+ *channel = container_of(base_chan, struct scmi_channel, ref);
+
+ return 0;
+ }
+
+ /* Setup a dedicated channel */
+ chan = calloc(1, sizeof(*chan));
+ if (!chan)
+ return -ENOMEM;
+
+ ret = setup_channel(dev, chan);
+ if (ret) {
+ free(chan);
+ return ret;
+ }
+
+ *channel = container_of(chan, struct scmi_channel, ref);
+
+ return 0;
+}
+
+static int scmi_optee_of_to_plat(struct udevice *dev)
+{
+ struct scmi_optee_channel *chan = dev_get_plat(dev);
+
+ return setup_channel(dev, chan);
+}
+
static int scmi_optee_probe(struct udevice *dev)
{
+ struct scmi_optee_channel *chan = dev_get_plat(dev);
struct channel_session sess;
int ret;
/* Check OP-TEE service acknowledges the SCMI channel */
- ret = open_channel(dev, &sess);
+ ret = open_channel(dev, chan, &sess);
if (!ret)
close_channel(&sess);
@@ -297,6 +381,7 @@ static const struct udevice_id scmi_optee_ids[] = {
};
static const struct scmi_agent_ops scmi_optee_ops = {
+ .of_get_channel = scmi_optee_get_channel,
.process_msg = scmi_optee_process_msg,
};
diff --git a/drivers/firmware/scmi/sandbox-scmi_agent.c b/drivers/firmware/scmi/sandbox-scmi_agent.c
index c555164d196..031882998df 100644
--- a/drivers/firmware/scmi/sandbox-scmi_agent.c
+++ b/drivers/firmware/scmi/sandbox-scmi_agent.c
@@ -471,6 +471,7 @@ static int sandbox_scmi_voltd_level_get(struct udevice *dev,
}
static int sandbox_scmi_test_process_msg(struct udevice *dev,
+ struct scmi_channel *channel,
struct scmi_msg *msg)
{
switch (msg->protocol_id) {
diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c
index 3819f2fa993..2b6211c4e6a 100644
--- a/drivers/firmware/scmi/scmi_agent-uclass.c
+++ b/drivers/firmware/scmi/scmi_agent-uclass.c
@@ -109,30 +109,56 @@ static int scmi_bind_protocols(struct udevice *dev)
return ret;
}
-static const struct scmi_agent_ops *transport_dev_ops(struct udevice *dev)
+static struct udevice *find_scmi_transport_device(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;
struct udevice *parent = dev;
- /* Find related SCMI agent device */
do {
parent = dev_get_parent(parent);
} while (parent && device_get_uclass_id(parent) != UCLASS_SCMI_AGENT);
- if (!parent) {
+ if (!parent)
dev_err(dev, "Invalid SCMI device, agent not found\n");
+
+ return parent;
+}
+
+static const struct scmi_agent_ops *transport_dev_ops(struct udevice *dev)
+{
+ return (const struct scmi_agent_ops *)dev->driver->ops;
+}
+
+int devm_scmi_of_get_channel(struct udevice *dev, struct scmi_channel **channel)
+{
+ struct udevice *parent;
+
+ parent = find_scmi_transport_device(dev);
+ if (!parent)
+ return -ENODEV;
+
+ if (transport_dev_ops(parent)->of_get_channel)
+ return transport_dev_ops(parent)->of_get_channel(dev, channel);
+
+ /* Drivers without a get_channel operator don't need a channel ref */
+ *channel = NULL;
+
+ return 0;
+}
+
+int devm_scmi_process_msg(struct udevice *dev, struct scmi_channel *channel,
+ struct scmi_msg *msg)
+{
+ const struct scmi_agent_ops *ops;
+ struct udevice *parent;
+
+ parent = find_scmi_transport_device(dev);
+ if (!parent)
return -ENODEV;
- }
ops = transport_dev_ops(parent);
if (ops->process_msg)
- return ops->process_msg(parent, msg);
+ return ops->process_msg(parent, channel, msg);
return -EPROTONOSUPPORT;
}
diff --git a/drivers/firmware/scmi/smccc_agent.c b/drivers/firmware/scmi/smccc_agent.c
index 5e166ca93ee..bc2eb67335b 100644
--- a/drivers/firmware/scmi/smccc_agent.c
+++ b/drivers/firmware/scmi/smccc_agent.c
@@ -30,9 +30,19 @@ struct scmi_smccc_channel {
struct scmi_smt smt;
};
-static int scmi_smccc_process_msg(struct udevice *dev, struct scmi_msg *msg)
+/**
+ * struct scmi_channel - Channel instance referenced in SCMI drivers
+ * @ref: Reference to local channel instance
+ **/
+struct scmi_channel {
+ struct scmi_smccc_channel ref;
+};
+
+static int scmi_smccc_process_msg(struct udevice *dev,
+ struct scmi_channel *channel,
+ struct scmi_msg *msg)
{
- struct scmi_smccc_channel *chan = dev_get_plat(dev);
+ struct scmi_smccc_channel *chan = &channel->ref;
struct arm_smccc_res res;
int ret;
@@ -51,9 +61,8 @@ static int scmi_smccc_process_msg(struct udevice *dev, struct scmi_msg *msg)
return ret;
}
-static int scmi_smccc_of_to_plat(struct udevice *dev)
+static int setup_channel(struct udevice *dev, struct scmi_smccc_channel *chan)
{
- struct scmi_smccc_channel *chan = dev_get_plat(dev);
u32 func_id;
int ret;
@@ -71,12 +80,51 @@ static int scmi_smccc_of_to_plat(struct udevice *dev)
return ret;
}
+static int scmi_smccc_get_channel(struct udevice *dev,
+ struct scmi_channel **channel)
+{
+ struct scmi_smccc_channel *base_chan = dev_get_plat(dev->parent);
+ struct scmi_smccc_channel *chan;
+ u32 func_id;
+ int ret;
+
+ if (dev_read_u32(dev, "arm,smc-id", &func_id)) {
+ /* Uses agent base channel */
+ *channel = container_of(base_chan, struct scmi_channel, ref);
+
+ return 0;
+ }
+
+ /* Setup a dedicated channel */
+ chan = calloc(1, sizeof(*chan));
+ if (!chan)
+ return -ENOMEM;
+
+ ret = setup_channel(dev, chan);
+ if (ret) {
+ free(chan);
+ return ret;
+ }
+
+ *channel = container_of(chan, struct scmi_channel, ref);
+
+ return 0;
+}
+
+static int scmi_smccc_of_to_plat(struct udevice *dev)
+{
+ struct scmi_smccc_channel *chan = dev_get_plat(dev);
+
+ return setup_channel(dev, chan);
+}
+
static const struct udevice_id scmi_smccc_ids[] = {
{ .compatible = "arm,scmi-smc" },
{ }
};
static const struct scmi_agent_ops scmi_smccc_ops = {
+ .of_get_channel = scmi_smccc_get_channel,
.process_msg = scmi_smccc_process_msg,
};
diff --git a/drivers/firmware/scmi/smt.c b/drivers/firmware/scmi/smt.c
index e60c2aebc89..509ed618a99 100644
--- a/drivers/firmware/scmi/smt.c
+++ b/drivers/firmware/scmi/smt.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
- * Copyright (C) 2019-2020 Linaro Limited.
+ * Copyright (C) 2019-2022 Linaro Limited.
*/
#define LOG_CATEGORY UCLASS_SCMI_AGENT
@@ -137,3 +137,54 @@ void scmi_clear_smt_channel(struct scmi_smt *smt)
hdr->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR;
}
+
+/**
+ * Write SCMI message @msg into a SMT_MSG shared buffer @smt.
+ * Return 0 on success and with a negative errno in case of error.
+ */
+int scmi_msg_to_smt_msg(struct udevice *dev, struct scmi_smt *smt,
+ struct scmi_msg *msg, size_t *buf_size)
+{
+ struct scmi_smt_msg_header *hdr = (void *)smt->buf;
+
+ if ((!msg->in_msg && msg->in_msg_sz) ||
+ (!msg->out_msg && msg->out_msg_sz))
+ return -EINVAL;
+
+ 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;
+ }
+
+ *buf_size = 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(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_msg_from_smt_msg(struct udevice *dev, struct scmi_smt *smt,
+ struct scmi_msg *msg, size_t buf_size)
+{
+ struct scmi_smt_msg_header *hdr = (void *)smt->buf;
+
+ if (buf_size > msg->out_msg_sz + sizeof(hdr->msg_header)) {
+ dev_err(dev, "Buffer to small\n");
+ return -ETOOSMALL;
+ }
+
+ msg->out_msg_sz = buf_size - sizeof(hdr->msg_header);
+ memcpy(msg->out_msg, hdr->msg_payload, msg->out_msg_sz);
+
+ return 0;
+}
diff --git a/drivers/firmware/scmi/smt.h b/drivers/firmware/scmi/smt.h
index a8c0987bd30..9d669a6c922 100644
--- a/drivers/firmware/scmi/smt.h
+++ b/drivers/firmware/scmi/smt.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
- * Copyright (C) 2019-2020 Linaro Limited.
+ * Copyright (C) 2019-2022 Linaro Limited.
*/
#ifndef SCMI_SMT_H
#define SCMI_SMT_H
@@ -29,6 +29,17 @@ struct scmi_smt_header {
u8 msg_payload[0];
};
+/**
+ * struct scmi_msg_header - Description of a MSG shared memory message buffer
+ *
+ * MSG communication protocol uses a 32bit header memory cell to store SCMI
+ * protocol data followed by the exchange SCMI message payload.
+ */
+struct scmi_smt_msg_header {
+ __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))
@@ -75,12 +86,44 @@ static inline void scmi_smt_put_channel(struct scmi_smt *smt)
int scmi_dt_get_smt_buffer(struct udevice *dev, struct scmi_smt *smt);
+/*
+ * Write SCMI message to a SMT shared memory
+ * @dev: SCMI device
+ * @smt: Reference to shared memory using SMT header
+ * @msg: Input SCMI message transmitted
+ */
int scmi_write_msg_to_smt(struct udevice *dev, struct scmi_smt *smt,
struct scmi_msg *msg);
+/*
+ * Read SCMI message from a SMT shared memory
+ * @dev: SCMI device
+ * @smt: Reference to shared memory using SMT header
+ * @msg: Output SCMI message received
+ */
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);
+/*
+ * Write SCMI message to SMT_MSG shared memory
+ * @dev: SCMI device
+ * @smt: Reference to shared memory using SMT_MSG header
+ * @msg: Input SCMI message transmitted
+ * @buf_size: Size of the full SMT_MSG buffer transmitted
+ */
+int scmi_msg_to_smt_msg(struct udevice *dev, struct scmi_smt *smt,
+ struct scmi_msg *msg, size_t *buf_size);
+
+/*
+ * Read SCMI message from SMT_MSG shared memory
+ * @dev: SCMI device
+ * @smt: Reference to shared memory using SMT_MSG header
+ * @msg: Output SCMI message received
+ * @buf_size: Size of the full SMT_MSG buffer received
+ */
+int scmi_msg_from_smt_msg(struct udevice *dev, struct scmi_smt *smt,
+ struct scmi_msg *msg, size_t buf_size);
+
#endif /* SCMI_SMT_H */
diff --git a/drivers/power/regulator/scmi_regulator.c b/drivers/power/regulator/scmi_regulator.c
index 2966bdcf830..801148036ff 100644
--- a/drivers/power/regulator/scmi_regulator.c
+++ b/drivers/power/regulator/scmi_regulator.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * Copyright (C) 2020-2021 Linaro Limited
+ * Copyright (C) 2020-2022 Linaro Limited
*/
#define LOG_CATEGORY UCLASS_REGULATOR
@@ -25,9 +25,18 @@ struct scmi_regulator_platdata {
u32 domain_id;
};
+/**
+ * struct scmi_regulator_priv - Private data for SCMI voltage regulator
+ * @channel: Reference to the SCMI channel to use
+ */
+struct scmi_regulator_priv {
+ struct scmi_channel *channel;
+};
+
static int scmi_voltd_set_enable(struct udevice *dev, bool enable)
{
struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
+ struct scmi_regulator_priv *priv = dev_get_priv(dev);
struct scmi_voltd_config_set_in in = {
.domain_id = pdata->domain_id,
.config = enable ? SCMI_VOLTD_CONFIG_ON : SCMI_VOLTD_CONFIG_OFF,
@@ -38,20 +47,17 @@ static int scmi_voltd_set_enable(struct udevice *dev, bool enable)
in, out);
int ret;
- ret = devm_scmi_process_msg(dev, &msg);
- if (ret)
- return ret;
-
- ret = scmi_to_linux_errno(out.status);
+ ret = devm_scmi_process_msg(dev, priv->channel, &msg);
if (ret)
return ret;
- return ret;
+ return scmi_to_linux_errno(out.status);
}
static int scmi_voltd_get_enable(struct udevice *dev)
{
struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
+ struct scmi_regulator_priv *priv = dev_get_priv(dev);
struct scmi_voltd_config_get_in in = {
.domain_id = pdata->domain_id,
};
@@ -61,7 +67,7 @@ static int scmi_voltd_get_enable(struct udevice *dev)
in, out);
int ret;
- ret = devm_scmi_process_msg(dev, &msg);
+ ret = devm_scmi_process_msg(dev, priv->channel, &msg);
if (ret < 0)
return ret;
@@ -74,6 +80,7 @@ static int scmi_voltd_get_enable(struct udevice *dev)
static int scmi_voltd_set_voltage_level(struct udevice *dev, int uV)
{
+ struct scmi_regulator_priv *priv = dev_get_priv(dev);
struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
struct scmi_voltd_level_set_in in = {
.domain_id = pdata->domain_id,
@@ -85,7 +92,7 @@ static int scmi_voltd_set_voltage_level(struct udevice *dev, int uV)
in, out);
int ret;
- ret = devm_scmi_process_msg(dev, &msg);
+ ret = devm_scmi_process_msg(dev, priv->channel, &msg);
if (ret < 0)
return ret;
@@ -94,6 +101,7 @@ static int scmi_voltd_set_voltage_level(struct udevice *dev, int uV)
static int scmi_voltd_get_voltage_level(struct udevice *dev)
{
+ struct scmi_regulator_priv *priv = dev_get_priv(dev);
struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
struct scmi_voltd_level_get_in in = {
.domain_id = pdata->domain_id,
@@ -104,7 +112,7 @@ static int scmi_voltd_get_voltage_level(struct udevice *dev)
in, out);
int ret;
- ret = devm_scmi_process_msg(dev, &msg);
+ ret = devm_scmi_process_msg(dev, priv->channel, &msg);
if (ret < 0)
return ret;
@@ -132,6 +140,7 @@ static int scmi_regulator_of_to_plat(struct udevice *dev)
static int scmi_regulator_probe(struct udevice *dev)
{
struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
+ struct scmi_regulator_priv *priv = dev_get_priv(dev);
struct scmi_voltd_attr_in in = { 0 };
struct scmi_voltd_attr_out out = { 0 };
struct scmi_msg scmi_msg = {
@@ -144,10 +153,14 @@ static int scmi_regulator_probe(struct udevice *dev)
};
int ret;
+ ret = devm_scmi_of_get_channel(dev->parent, &priv->channel);
+ if (ret)
+ return ret;
+
/* Check voltage domain is known from SCMI server */
in.domain_id = pdata->domain_id;
- ret = devm_scmi_process_msg(dev, &scmi_msg);
+ ret = devm_scmi_process_msg(dev, priv->channel, &scmi_msg);
if (ret) {
dev_err(dev, "Failed to query voltage domain %u: %d\n",
pdata->domain_id, ret);
@@ -171,6 +184,7 @@ U_BOOT_DRIVER(scmi_regulator) = {
.probe = scmi_regulator_probe,
.of_to_plat = scmi_regulator_of_to_plat,
.plat_auto = sizeof(struct scmi_regulator_platdata),
+ .priv_auto = sizeof(struct scmi_regulator_priv *),
};
static int scmi_regulator_bind(struct udevice *dev)
diff --git a/drivers/reset/reset-scmi.c b/drivers/reset/reset-scmi.c
index 81d195a06a9..122556162ec 100644
--- a/drivers/reset/reset-scmi.c
+++ b/drivers/reset/reset-scmi.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * Copyright (C) 2019-2020 Linaro Limited
+ * Copyright (C) 2019-2022 Linaro Limited
*/
#define LOG_CATEGORY UCLASS_RESET
@@ -13,8 +13,17 @@
#include <scmi_protocols.h>
#include <asm/types.h>
+/**
+ * struct scmi_reset_priv - Private data for SCMI reset controller
+ * @channel: Reference to the SCMI channel to use
+ */
+struct scmi_reset_priv {
+ struct scmi_channel *channel;
+};
+
static int scmi_reset_set_level(struct reset_ctl *rst, bool assert_not_deassert)
{
+ struct scmi_reset_priv *priv = dev_get_priv(rst->dev);
struct scmi_rd_reset_in in = {
.domain_id = rst->id,
.flags = assert_not_deassert ? SCMI_RD_RESET_FLAG_ASSERT : 0,
@@ -26,7 +35,7 @@ static int scmi_reset_set_level(struct reset_ctl *rst, bool assert_not_deassert)
in, out);
int ret;
- ret = devm_scmi_process_msg(rst->dev, &msg);
+ ret = devm_scmi_process_msg(rst->dev, priv->channel, &msg);
if (ret)
return ret;
@@ -45,6 +54,7 @@ static int scmi_reset_deassert(struct reset_ctl *rst)
static int scmi_reset_request(struct reset_ctl *rst)
{
+ struct scmi_reset_priv *priv = dev_get_priv(rst->dev);
struct scmi_rd_attr_in in = {
.domain_id = rst->id,
};
@@ -58,7 +68,7 @@ static int scmi_reset_request(struct reset_ctl *rst)
* We don't really care about the attribute, just check
* the reset domain exists.
*/
- ret = devm_scmi_process_msg(rst->dev, &msg);
+ ret = devm_scmi_process_msg(rst->dev, priv->channel, &msg);
if (ret)
return ret;
@@ -71,8 +81,17 @@ static const struct reset_ops scmi_reset_domain_ops = {
.rst_deassert = scmi_reset_deassert,
};
+static int scmi_reset_probe(struct udevice *dev)
+{
+ struct scmi_reset_priv *priv = dev_get_priv(dev);
+
+ return devm_scmi_of_get_channel(dev, &priv->channel);
+}
+
U_BOOT_DRIVER(scmi_reset_domain) = {
.name = "scmi_reset_domain",
.id = UCLASS_RESET,
.ops = &scmi_reset_domain_ops,
+ .probe = scmi_reset_probe,
+ .priv_auto = sizeof(struct scmi_reset_priv *),
};
diff --git a/drivers/rng/Kconfig b/drivers/rng/Kconfig
index c0c49c34843..21a9ff01954 100644
--- a/drivers/rng/Kconfig
+++ b/drivers/rng/Kconfig
@@ -65,4 +65,13 @@ config RNG_IPROC200
depends on DM_RNG
help
Enable random number generator for RPI4.
+
+config RNG_SMCCC_TRNG
+ bool "Arm SMCCC TRNG interface"
+ depends on DM_RNG && ARM_PSCI_FW
+ default y if ARM_SMCCC_FEATURES
+ help
+ Enable random number generator for platforms that support Arm
+ SMCCC TRNG interface.
+
endif
diff --git a/drivers/rng/Makefile b/drivers/rng/Makefile
index 0ae0ed4171c..2494717d7c7 100644
--- a/drivers/rng/Makefile
+++ b/drivers/rng/Makefile
@@ -12,3 +12,4 @@ obj-$(CONFIG_RNG_OPTEE) += optee_rng.o
obj-$(CONFIG_RNG_STM32MP1) += stm32mp1_rng.o
obj-$(CONFIG_RNG_ROCKCHIP) += rockchip_rng.o
obj-$(CONFIG_RNG_IPROC200) += iproc_rng200.o
+obj-$(CONFIG_RNG_SMCCC_TRNG) += smccc_trng.o
diff --git a/drivers/rng/smccc_trng.c b/drivers/rng/smccc_trng.c
new file mode 100644
index 00000000000..3a4bb339415
--- /dev/null
+++ b/drivers/rng/smccc_trng.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022, Linaro Limited
+ */
+
+#define LOG_CATEGORY UCLASS_RNG
+
+#include <common.h>
+#include <dm.h>
+#include <linker_lists.h>
+#include <log.h>
+#include <rng.h>
+#include <dm/device_compat.h>
+#include <linux/arm-smccc.h>
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/psci.h>
+
+#define DRIVER_NAME "smccc-trng"
+
+/**
+ * Arm SMCCC TRNG firmware interface specification:
+ * https://developer.arm.com/documentation/den0098/latest/
+ */
+#define ARM_SMCCC_TRNG_VERSION 0x84000050
+#define ARM_SMCCC_TRNG_FEATURES 0x84000051
+#define ARM_SMCCC_TRNG_GET_UUID 0x84000052
+#define ARM_SMCCC_TRNG_RND_32 0x84000053
+#define ARM_SMCCC_TRNG_RND_64 0xC4000053
+
+#define ARM_SMCCC_RET_TRNG_SUCCESS ((ulong)0)
+#define ARM_SMCCC_RET_TRNG_NOT_SUPPORTED ((ulong)-1)
+#define ARM_SMCCC_RET_TRNG_INVALID_PARAMETER ((ulong)-2)
+#define ARM_SMCCC_RET_TRNG_NO_ENTROPY ((ulong)-3)
+
+#define TRNG_MAJOR_MASK GENMASK(30, 16)
+#define TRNG_MAJOR_SHIFT 16
+#define TRNG_MINOR_MASK GENMASK(15, 0)
+#define TRNG_MINOR_SHIFT 0
+
+#define TRNG_MAX_RND_64 (192 / 8)
+#define TRNG_MAX_RND_32 (96 / 8)
+
+/**
+ * struct smccc_trng_priv - Private data for SMCCC TRNG support
+ *
+ * @smc64 - True if TRNG_RND_64 is supported, false if TRNG_RND_32 is supported
+ */
+struct smccc_trng_priv {
+ bool smc64;
+};
+
+/*
+ * Copy random bytes from ulong SMCCC output register to target buffer
+ * Defines 2 function flavors for whether ARM_SMCCC_TRNG_RND_32 or
+ * ARM_SMCCC_TRNG_RND_64 was used to invoke the service.
+ */
+static size_t smc32_copy_sample(u8 **ptr, size_t size, ulong *rnd)
+{
+ size_t len = min(size, sizeof(u32));
+ u32 sample = *rnd;
+
+ memcpy(*ptr, &sample, len);
+ *ptr += len;
+
+ return size - len;
+}
+
+static size_t smc64_copy_sample(u8 **ptr, size_t size, ulong *rnd)
+{
+ size_t len = min(size, sizeof(u64));
+ u64 sample = *rnd;
+
+ memcpy(*ptr, &sample, len);
+ *ptr += len;
+
+ return size - len;
+}
+
+static int smccc_trng_read(struct udevice *dev, void *data, size_t len)
+{
+ struct psci_plat_data *smccc = dev_get_parent_plat(dev);
+ struct smccc_trng_priv *priv = dev_get_priv(dev);
+ struct arm_smccc_res res;
+ u32 func_id;
+ u8 *ptr = data;
+ size_t rem = len;
+ size_t max_sz;
+ size_t (*copy_sample)(u8 **ptr, size_t size, ulong *rnd);
+
+ if (priv->smc64) {
+ copy_sample = smc64_copy_sample;
+ func_id = ARM_SMCCC_TRNG_RND_64;
+ max_sz = TRNG_MAX_RND_64;
+ } else {
+ copy_sample = smc32_copy_sample;
+ func_id = ARM_SMCCC_TRNG_RND_32;
+ max_sz = TRNG_MAX_RND_32;
+ }
+
+ while (rem) {
+ size_t sz = min(rem, max_sz);
+
+ smccc->invoke_fn(func_id, sz * 8, 0, 0, 0, 0, 0, 0, &res);
+
+ switch (res.a0) {
+ case ARM_SMCCC_RET_TRNG_SUCCESS:
+ break;
+ case ARM_SMCCC_RET_TRNG_NO_ENTROPY:
+ continue;
+ default:
+ return -EIO;
+ }
+
+ rem -= sz;
+
+ sz = copy_sample(&ptr, sz, &res.a3);
+ if (sz)
+ sz = copy_sample(&ptr, sz, &res.a2);
+ if (sz)
+ sz = copy_sample(&ptr, sz, &res.a1);
+ }
+
+ return 0;
+}
+
+static const struct dm_rng_ops smccc_trng_ops = {
+ .read = smccc_trng_read,
+};
+
+static bool smccc_trng_is_supported(void (*invoke_fn)(unsigned long a0, unsigned long a1,
+ unsigned long a2, unsigned long a3,
+ unsigned long a4, unsigned long a5,
+ unsigned long a6, unsigned long a7,
+ struct arm_smccc_res *res))
+{
+ struct arm_smccc_res res;
+
+ (*invoke_fn)(ARM_SMCCC_ARCH_FEATURES, ARM_SMCCC_TRNG_VERSION, 0, 0, 0, 0, 0, 0, &res);
+ if (res.a0 == ARM_SMCCC_RET_NOT_SUPPORTED)
+ return false;
+
+ (*invoke_fn)(ARM_SMCCC_TRNG_VERSION, 0, 0, 0, 0, 0, 0, 0, &res);
+ if (res.a0 & BIT(31))
+ return false;
+
+ /* Test 64bit interface and fallback to 32bit interface */
+ invoke_fn(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_TRNG_RND_64,
+ 0, 0, 0, 0, 0, 0, &res);
+
+ if (res.a0 == ARM_SMCCC_RET_TRNG_NOT_SUPPORTED)
+ invoke_fn(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_TRNG_RND_32,
+ 0, 0, 0, 0, 0, 0, &res);
+
+ return res.a0 == ARM_SMCCC_RET_TRNG_SUCCESS;
+}
+
+ARM_SMCCC_FEATURE_DRIVER(smccc_trng) = {
+ .driver_name = DRIVER_NAME,
+ .is_supported = smccc_trng_is_supported,
+};
+
+static int smccc_trng_probe(struct udevice *dev)
+{
+ struct psci_plat_data *smccc = dev_get_parent_plat(dev);
+ struct smccc_trng_priv *priv = dev_get_priv(dev);
+ struct arm_smccc_res res;
+
+ if (!(smccc_trng_is_supported(smccc->invoke_fn)))
+ return -ENODEV;
+
+ /* At least one of 64bit and 32bit interfaces is available */
+ smccc->invoke_fn(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_TRNG_RND_64,
+ 0, 0, 0, 0, 0, 0, &res);
+ priv->smc64 = (res.a0 == ARM_SMCCC_RET_TRNG_SUCCESS);
+
+#ifdef DEBUG
+ smccc->invoke_fn(ARM_SMCCC_TRNG_GET_UUID, 0, 0, 0, 0, 0, 0, 0, &res);
+ if (res.a0 != ARM_SMCCC_RET_TRNG_NOT_SUPPORTED) {
+ unsigned long uuid_a0 = res.a0;
+ unsigned long uuid_a1 = res.a1;
+ unsigned long uuid_a2 = res.a2;
+ unsigned long uuid_a3 = res.a3;
+ unsigned long major, minor;
+
+ smccc->invoke_fn(ARM_SMCCC_TRNG_VERSION, 0, 0, 0, 0, 0, 0, 0, &res);
+ major = (res.a0 & TRNG_MAJOR_MASK) >> TRNG_MAJOR_SHIFT;
+ minor = (res.a0 & TRNG_MINOR_MASK) >> TRNG_MINOR_SHIFT;
+
+ dev_dbg(dev, "Version %lu.%lu, UUID %08lx-%04lx-%04lx-%04lx-%04lx%08lx\n",
+ major, minor, uuid_a0, uuid_a1 >> 16, uuid_a1 & GENMASK(16, 0),
+ uuid_a2 >> 16, uuid_a2 & GENMASK(16, 0), uuid_a3);
+ } else {
+ dev_warn(dev, "Can't get TRNG UUID\n");
+ }
+#endif
+
+ return 0;
+}
+
+U_BOOT_DRIVER(smccc_trng) = {
+ .name = DRIVER_NAME,
+ .id = UCLASS_RNG,
+ .ops = &smccc_trng_ops,
+ .probe = smccc_trng_probe,
+ .priv_auto = sizeof(struct smccc_trng_priv),
+};
diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
index 7f2be233947..e1d09884a1c 100644
--- a/include/linux/arm-smccc.h
+++ b/include/linux/arm-smccc.h
@@ -51,6 +51,10 @@
#define ARM_SMCCC_QUIRK_NONE 0
#define ARM_SMCCC_QUIRK_QCOM_A6 1 /* Save/restore register a6 */
+#define ARM_SMCCC_ARCH_FEATURES 0x80000001
+
+#define ARM_SMCCC_RET_NOT_SUPPORTED ((unsigned long)-1)
+
#ifndef __ASSEMBLY__
#include <linux/linkage.h>
@@ -80,6 +84,22 @@ struct arm_smccc_quirk {
};
/**
+ * struct arm_smccc_feature - Driver registration data for discoverable feature
+ * @driver_name: name of the driver relate to the SMCCC feature
+ * @is_supported: callback to test if SMCCC feature is supported
+ */
+struct arm_smccc_feature {
+ const char *driver_name;
+ bool (*is_supported)(void (*invoke_fn)(unsigned long a0, unsigned long a1, unsigned long a2,
+ unsigned long a3, unsigned long a4, unsigned long a5,
+ unsigned long a6, unsigned long a7,
+ struct arm_smccc_res *res));
+};
+
+#define ARM_SMCCC_FEATURE_DRIVER(__name) \
+ ll_entry_declare(struct arm_smccc_feature, __name, arm_smccc_feature)
+
+/**
* __arm_smccc_smc() - make SMC calls
* @a0-a7: arguments passed in registers 0 to 7
* @res: result values from registers 0 to 3
diff --git a/include/linux/psci.h b/include/linux/psci.h
index c78c1079a82..03e41863432 100644
--- a/include/linux/psci.h
+++ b/include/linux/psci.h
@@ -11,6 +11,8 @@
#ifndef _UAPI_LINUX_PSCI_H
#define _UAPI_LINUX_PSCI_H
+#include <linux/arm-smccc.h>
+
/*
* PSCI v0.1 interface
*
@@ -115,6 +117,18 @@
#define PSCI_RET_DISABLED -8
#define PSCI_RET_INVALID_ADDRESS -9
+/**
+ * struct psci_plat_data - PSCI driver platform data
+ * @method: Selected invocation conduit
+ */
+struct psci_plat_data {
+ void (*invoke_fn)(unsigned long arg0, unsigned long arg1,
+ unsigned long arg2, unsigned long arg3,
+ unsigned long arg4, unsigned long arg5,
+ unsigned long arg6, unsigned long arg7,
+ struct arm_smccc_res *res);
+};
+
#ifdef CONFIG_ARM_PSCI_FW
unsigned long invoke_psci_fn(unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3);
diff --git a/include/scmi_agent-uclass.h b/include/scmi_agent-uclass.h
index a501d1b4825..b1c93532c0e 100644
--- a/include/scmi_agent-uclass.h
+++ b/include/scmi_agent-uclass.h
@@ -7,18 +7,29 @@
struct udevice;
struct scmi_msg;
+struct scmi_channel;
/**
* struct scmi_transport_ops - The functions that a SCMI transport layer must implement.
*/
struct scmi_agent_ops {
/*
+ * of_get_channel - Get SCMI channel from SCMI agent device tree node
+ *
+ * @dev: SCMI protocol device using the transport
+ * @channel: Output reference to SCMI channel upon success
+ * Return 0 upon success and a negative errno on failure
+ */
+ int (*of_get_channel)(struct udevice *dev, struct scmi_channel **channel);
+
+ /*
* process_msg - Request transport to get the SCMI message processed
*
- * @agent: Agent using the transport
+ * @dev: SCMI protocol device using the transport
* @msg: SCMI message to be transmitted
*/
- int (*process_msg)(struct udevice *dev, struct scmi_msg *msg);
+ int (*process_msg)(struct udevice *dev, struct scmi_channel *channel,
+ struct scmi_msg *msg);
};
#endif /* _SCMI_TRANSPORT_UCLASS_H */
diff --git a/include/scmi_agent.h b/include/scmi_agent.h
index 18bcd48a9d4..ee6286366df 100644
--- a/include/scmi_agent.h
+++ b/include/scmi_agent.h
@@ -13,6 +13,7 @@
#include <asm/types.h>
struct udevice;
+struct scmi_channel;
/*
* struct scmi_msg - Context of a SCMI message sent and the response received
@@ -45,6 +46,15 @@ struct scmi_msg {
}
/**
+ * devm_scmi_of_get_channel() - Get SCMI channel handle from SCMI agent DT node
+ *
+ * @dev: Device requesting a channel
+ * @channel: Output reference to the SCMI channel upon success
+ * @return 0 on success and a negative errno on failure
+ */
+int devm_scmi_of_get_channel(struct udevice *dev, struct scmi_channel **channel);
+
+/**
* devm_scmi_process_msg() - Send and process an SCMI message
*
* Send a message to an SCMI server through a target SCMI agent device.
@@ -52,10 +62,12 @@ struct scmi_msg {
* On return, scmi_msg::out_msg_sz stores the response payload size.
*
* @dev: SCMI device
+ * @channel: Communication channel for the 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);
+int devm_scmi_process_msg(struct udevice *dev, struct scmi_channel *channel,
+ struct scmi_msg *msg);
/**
* scmi_to_linux_errno() - Convert an SCMI error code into a Linux errno code