summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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