diff options
-rw-r--r-- | MAINTAINERS | 5 | ||||
-rw-r--r-- | drivers/clk/clk_scmi.c | 33 | ||||
-rw-r--r-- | drivers/firmware/Kconfig | 8 | ||||
-rw-r--r-- | drivers/firmware/psci.c | 91 | ||||
-rw-r--r-- | drivers/firmware/scmi/mailbox_agent.c | 65 | ||||
-rw-r--r-- | drivers/firmware/scmi/optee_agent.c | 147 | ||||
-rw-r--r-- | drivers/firmware/scmi/sandbox-scmi_agent.c | 1 | ||||
-rw-r--r-- | drivers/firmware/scmi/scmi_agent-uclass.c | 48 | ||||
-rw-r--r-- | drivers/firmware/scmi/smccc_agent.c | 56 | ||||
-rw-r--r-- | drivers/firmware/scmi/smt.c | 53 | ||||
-rw-r--r-- | drivers/firmware/scmi/smt.h | 45 | ||||
-rw-r--r-- | drivers/power/regulator/scmi_regulator.c | 36 | ||||
-rw-r--r-- | drivers/reset/reset-scmi.c | 25 | ||||
-rw-r--r-- | drivers/rng/Kconfig | 9 | ||||
-rw-r--r-- | drivers/rng/Makefile | 1 | ||||
-rw-r--r-- | drivers/rng/smccc_trng.c | 207 | ||||
-rw-r--r-- | include/linux/arm-smccc.h | 20 | ||||
-rw-r--r-- | include/linux/psci.h | 14 | ||||
-rw-r--r-- | include/scmi_agent-uclass.h | 15 | ||||
-rw-r--r-- | include/scmi_agent.h | 14 |
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 |