From 8bcb1b4898be4849b711687fd140b43074b648da Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Tue, 31 May 2022 18:09:18 +0200 Subject: firmware: scmi: prepare scmi uclass API to multi-channel Changes SCMI driver API function devm_scmi_process_msg() to add an SCMI channel reference argument for when SCMI agent supports SCMI protocol specific channels. First argument of devm_scmi_process_msg() is also change to point to the caller SCMI protocol device rather than its parent device (the SCMI agent device). The argument is a pointer to opaque struct scmi_channel known from the SCMI transport drivers. It is currently unused and caller a pass NULL value. A later change will enable such support once SCMI protocol drivers have means to get the channel reference during initialization. Cc: Lukasz Majewski Cc: Sean Anderson Cc: Jaehoon Chung Signed-off-by: Etienne Carriere --- include/scmi_agent-uclass.h | 2 +- include/scmi_agent.h | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/scmi_agent-uclass.h b/include/scmi_agent-uclass.h index a501d1b4825..861ac6d1100 100644 --- a/include/scmi_agent-uclass.h +++ b/include/scmi_agent-uclass.h @@ -15,7 +15,7 @@ struct scmi_agent_ops { /* * 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); diff --git a/include/scmi_agent.h b/include/scmi_agent.h index 18bcd48a9d4..f4d85cae773 100644 --- a/include/scmi_agent.h +++ b/include/scmi_agent.h @@ -13,6 +13,7 @@ #include struct udevice; +struct scmi_channel; /* * struct scmi_msg - Context of a SCMI message sent and the response received @@ -52,10 +53,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 -- cgit v1.3.1 From 85dc582892384600709acff796bbce10153255bf Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Tue, 31 May 2022 18:09:19 +0200 Subject: firmware: scmi: prepare uclass to pass channel reference Changes SCMI transport operator ::process_msg to pass the SCMI channel reference provided by caller SCMI protocol device. Signed-off-by: Etienne Carriere --- drivers/firmware/scmi/mailbox_agent.c | 4 +++- drivers/firmware/scmi/optee_agent.c | 4 +++- drivers/firmware/scmi/sandbox-scmi_agent.c | 1 + drivers/firmware/scmi/scmi_agent-uclass.c | 2 +- drivers/firmware/scmi/smccc_agent.c | 4 +++- include/scmi_agent-uclass.h | 4 +++- 6 files changed, 14 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/firmware/scmi/mailbox_agent.c b/drivers/firmware/scmi/mailbox_agent.c index 8e4af0c8faf..aa4929aafae 100644 --- a/drivers/firmware/scmi/mailbox_agent.c +++ b/drivers/firmware/scmi/mailbox_agent.c @@ -31,7 +31,9 @@ struct scmi_mbox_channel { ulong timeout_us; }; -static int scmi_mbox_process_msg(struct udevice *dev, struct scmi_msg *msg) +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); int ret; diff --git a/drivers/firmware/scmi/optee_agent.c b/drivers/firmware/scmi/optee_agent.c index bf0647bafd1..771fa25e989 100644 --- a/drivers/firmware/scmi/optee_agent.c +++ b/drivers/firmware/scmi/optee_agent.c @@ -267,7 +267,9 @@ 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 = { }; int ret; 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 93cfc9c395b..c9c9c00384a 100644 --- a/drivers/firmware/scmi/scmi_agent-uclass.c +++ b/drivers/firmware/scmi/scmi_agent-uclass.c @@ -133,7 +133,7 @@ int devm_scmi_process_msg(struct udevice *dev, struct scmi_channel *channel, ops = transport_dev_ops(parent); if (ops->process_msg) - return ops->process_msg(parent, msg); + return ops->process_msg(parent, NULL, msg); return -EPROTONOSUPPORT; } diff --git a/drivers/firmware/scmi/smccc_agent.c b/drivers/firmware/scmi/smccc_agent.c index 5e166ca93ee..b7a930b24df 100644 --- a/drivers/firmware/scmi/smccc_agent.c +++ b/drivers/firmware/scmi/smccc_agent.c @@ -30,7 +30,9 @@ struct scmi_smccc_channel { struct scmi_smt smt; }; -static int scmi_smccc_process_msg(struct udevice *dev, struct scmi_msg *msg) +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 arm_smccc_res res; diff --git a/include/scmi_agent-uclass.h b/include/scmi_agent-uclass.h index 861ac6d1100..562a4cc99af 100644 --- a/include/scmi_agent-uclass.h +++ b/include/scmi_agent-uclass.h @@ -7,6 +7,7 @@ struct udevice; struct scmi_msg; +struct scmi_channel; /** * struct scmi_transport_ops - The functions that a SCMI transport layer must implement. @@ -18,7 +19,8 @@ struct scmi_agent_ops { * @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 */ -- cgit v1.3.1 From 8e96801aa6ae99905acaf9ca36c30b373d441e68 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Tue, 31 May 2022 18:09:21 +0200 Subject: firmware: scmi: add multi-channel support Adds resources for SCMI protocols to possibly use a dedicated SCMI channel instead of the default channel allocated by the SCMI agent during initialization. As per DT binding documentation, some SCMI transports can define a specific SCMI communication channel for given SCMI protocols. It allows SCMI protocols to pass messages concurrently each other. This change introduces new scmi agent uclass API function devm_scmi_of_get_channel() for SCMI drivers probe sequences to get a reference to the SCMI channel assigned to its related SCMI protocol. The function queries the channel reference to its SCMI transport driver through new scmi agent uclass operator .of_get_channel that uses Device Tree information from related SCMI agent node. Operator .of_get_channel returns a reference to the SCMI channel assigned to SCMI protocol used by the caller device. SCMI transport drivers that do not support multi-channel are not mandated to register this operator. When so, API function devm_scmi_of_get_channel() returns NULL and SCMI transport driver are expected to retrieve by their own means the reference to the unique SCMI channel, for example using platform data as these drivers currently do in U-Boot source tree. Signed-off-by: Etienne Carriere --- drivers/firmware/scmi/scmi_agent-uclass.c | 19 ++++++++++++++++++- include/scmi_agent-uclass.h | 9 +++++++++ include/scmi_agent.h | 9 +++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c index f7fa5df214c..2b6211c4e6a 100644 --- a/drivers/firmware/scmi/scmi_agent-uclass.c +++ b/drivers/firmware/scmi/scmi_agent-uclass.c @@ -128,6 +128,23 @@ 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) { @@ -141,7 +158,7 @@ int devm_scmi_process_msg(struct udevice *dev, struct scmi_channel *channel, ops = transport_dev_ops(parent); if (ops->process_msg) - return ops->process_msg(parent, NULL, msg); + return ops->process_msg(parent, channel, msg); return -EPROTONOSUPPORT; } diff --git a/include/scmi_agent-uclass.h b/include/scmi_agent-uclass.h index 562a4cc99af..b1c93532c0e 100644 --- a/include/scmi_agent-uclass.h +++ b/include/scmi_agent-uclass.h @@ -13,6 +13,15 @@ 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 * diff --git a/include/scmi_agent.h b/include/scmi_agent.h index f4d85cae773..ee6286366df 100644 --- a/include/scmi_agent.h +++ b/include/scmi_agent.h @@ -45,6 +45,15 @@ struct scmi_msg { .out_msg_sz = sizeof(_out_array), \ } +/** + * 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 * -- cgit v1.3.1 From f7f124001540878b68b50c6d7ca188a9872a3cb5 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Wed, 1 Jun 2022 10:27:31 +0200 Subject: smccc: define generic IDs for feature discovery Defines function IDs ARM_SMCCC_ARCH_FEATURES used to query SMCCC feature support, applicable from Arm SMCCC v1.1 specification. Defines macro ARM_SMCCC_RET_NOT_SUPPORTED as generic return identifier for when a SMCCC feature is not supported. Signed-off-by: Etienne Carriere --- include/linux/arm-smccc.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h index 7f2be233947..94a20c97937 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 -- cgit v1.3.1 From 2fbe47b7e77134c81d8def15a2a6e028abe0f077 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Wed, 1 Jun 2022 10:27:33 +0200 Subject: firmware: psci: bind arm smccc features when discovered Use PSCI device to query Arm SMCCC v1.1 support from secure monitor and if so, bind drivers for the SMCCC features that monitor supports. Drivers willing to be bound from Arm SMCCC features discovery can use macro ARM_SMCCC_FEATURE_DRIVER() to register to smccc feature discovery, providing target driver name and a callback function that returns whether or not the SMCCC feature is supported by the system. Signed-off-by: Etienne Carriere --- drivers/firmware/Kconfig | 8 +++++ drivers/firmware/psci.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++- include/linux/arm-smccc.h | 16 ++++++++++ include/linux/psci.h | 14 ++++++++ 4 files changed, 118 insertions(+), 1 deletion(-) (limited to 'include') 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 f845ba67f8f..ef3e9836461 100644 --- a/drivers/firmware/psci.c +++ b/drivers/firmware/psci.c @@ -11,9 +11,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -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/include/linux/arm-smccc.h b/include/linux/arm-smccc.h index 94a20c97937..e1d09884a1c 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -83,6 +83,22 @@ struct arm_smccc_quirk { } state; }; +/** + * 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 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 + /* * 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); -- cgit v1.3.1