From 0d423c4a78984dd02f6596d6fd9dd40446eec517 Mon Sep 17 00:00:00 2001 From: Alexey Romanov Date: Wed, 30 Aug 2023 17:08:50 +0300 Subject: drivers: meson: sm: correct meson_sm_* API retval handling 1. Following the ARM SMC32 calling convention, the return value from secure monitor is a 32-bit signed integer. This patch changes the type of the return value of the function meson_sm_call(). 2. Now, when meson_sm_call() returns a 32-bit signed integer, we need to ensure that this value is not negative. It is important to check that the return value is not negative in both the meson_sm_call_read() and meson_sm_call_write() functions. 3. Add a comment explaining why it is necessary to check if the SMC return value is equal to 0 in the function meson_sm_call_read(). It is not obvious when reading this code. Signed-off-by: Alexey Romanov Reviewed-by: Neil Armstrong Link: https://lore.kernel.org/r/20230830140850.17130-1-avromanov@salutedevices.com Signed-off-by: Neil Armstrong --- drivers/firmware/meson/meson_sm.c | 20 +++++++++++++------- include/linux/firmware/meson/meson_sm.h | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/firmware/meson/meson_sm.c b/drivers/firmware/meson/meson_sm.c index 9a2656d73600..53bf56e18743 100644 --- a/drivers/firmware/meson/meson_sm.c +++ b/drivers/firmware/meson/meson_sm.c @@ -67,7 +67,7 @@ static u32 meson_sm_get_cmd(const struct meson_sm_chip *chip, return cmd->smc_id; } -static u32 __meson_sm_call(u32 cmd, u32 arg0, u32 arg1, u32 arg2, +static s32 __meson_sm_call(u32 cmd, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) { struct arm_smccc_res res; @@ -102,9 +102,10 @@ static void __iomem *meson_sm_map_shmem(u32 cmd_shmem, unsigned int size) * Return: 0 on success, a negative value on error */ int meson_sm_call(struct meson_sm_firmware *fw, unsigned int cmd_index, - u32 *ret, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) + s32 *ret, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) { - u32 cmd, lret; + u32 cmd; + s32 lret; if (!fw->chip) return -ENOENT; @@ -143,7 +144,7 @@ int meson_sm_call_read(struct meson_sm_firmware *fw, void *buffer, unsigned int bsize, unsigned int cmd_index, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) { - u32 size; + s32 size; int ret; if (!fw->chip) @@ -158,11 +159,16 @@ int meson_sm_call_read(struct meson_sm_firmware *fw, void *buffer, if (meson_sm_call(fw, cmd_index, &size, arg0, arg1, arg2, arg3, arg4) < 0) return -EINVAL; - if (size > bsize) + if (size < 0 || size > bsize) return -EINVAL; ret = size; + /* In some cases (for example GET_CHIP_ID command), + * SMC doesn't return the number of bytes read, even + * though the bytes were actually read into sm_shmem_out. + * So this check is needed. + */ if (!size) size = bsize; @@ -192,7 +198,7 @@ int meson_sm_call_write(struct meson_sm_firmware *fw, void *buffer, unsigned int size, unsigned int cmd_index, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) { - u32 written; + s32 written; if (!fw->chip) return -ENOENT; @@ -208,7 +214,7 @@ int meson_sm_call_write(struct meson_sm_firmware *fw, void *buffer, if (meson_sm_call(fw, cmd_index, &written, arg0, arg1, arg2, arg3, arg4) < 0) return -EINVAL; - if (!written) + if (written <= 0 || written > size) return -EINVAL; return written; diff --git a/include/linux/firmware/meson/meson_sm.h b/include/linux/firmware/meson/meson_sm.h index 95b0da2326a9..8eaf8922ab02 100644 --- a/include/linux/firmware/meson/meson_sm.h +++ b/include/linux/firmware/meson/meson_sm.h @@ -19,7 +19,7 @@ enum { struct meson_sm_firmware; int meson_sm_call(struct meson_sm_firmware *fw, unsigned int cmd_index, - u32 *ret, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4); + s32 *ret, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4); int meson_sm_call_write(struct meson_sm_firmware *fw, void *buffer, unsigned int b_size, unsigned int cmd_index, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4); -- cgit v1.2.3 From 78a03b9f8e6b317f7c65738a3fc60e1e85106a64 Mon Sep 17 00:00:00 2001 From: Jia Jie Ho Date: Thu, 25 May 2023 14:18:36 +0800 Subject: riscv: Kconfig: Add select ARM_AMBA to SOC_STARFIVE Selects ARM_AMBA platform support for StarFive SoCs required by spi and crypto dma engine. Signed-off-by: Jia Jie Ho Acked-by: Palmer Dabbelt Signed-off-by: Conor Dooley --- arch/riscv/Kconfig.socs | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs index 6833d01e2e70..30fd6a512828 100644 --- a/arch/riscv/Kconfig.socs +++ b/arch/riscv/Kconfig.socs @@ -29,6 +29,7 @@ config SOC_STARFIVE bool "StarFive SoCs" select PINCTRL select RESET_CONTROLLER + select ARM_AMBA help This enables support for StarFive SoC platform hardware. -- cgit v1.2.3 From e4c89f9380017b6b2e63836e2de1af8eb4535384 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 27 Aug 2023 23:14:04 +0200 Subject: lib/ucs2_string: Add UCS-2 strscpy function Add a ucs2_strscpy() function for UCS-2 strings. The behavior is equivalent to the standard strscpy() function, just for 16-bit character UCS-2 strings. Signed-off-by: Maximilian Luz Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230827211408.689076-2-luzmaximilian@gmail.com Signed-off-by: Bjorn Andersson --- include/linux/ucs2_string.h | 1 + lib/ucs2_string.c | 52 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/include/linux/ucs2_string.h b/include/linux/ucs2_string.h index cf3ada3e820e..c499ae809c7d 100644 --- a/include/linux/ucs2_string.h +++ b/include/linux/ucs2_string.h @@ -10,6 +10,7 @@ typedef u16 ucs2_char_t; unsigned long ucs2_strnlen(const ucs2_char_t *s, size_t maxlength); unsigned long ucs2_strlen(const ucs2_char_t *s); unsigned long ucs2_strsize(const ucs2_char_t *data, unsigned long maxlength); +ssize_t ucs2_strscpy(ucs2_char_t *dst, const ucs2_char_t *src, size_t count); int ucs2_strncmp(const ucs2_char_t *a, const ucs2_char_t *b, size_t len); unsigned long ucs2_utf8size(const ucs2_char_t *src); diff --git a/lib/ucs2_string.c b/lib/ucs2_string.c index 0a559a42359b..9308bcfb2ad5 100644 --- a/lib/ucs2_string.c +++ b/lib/ucs2_string.c @@ -32,6 +32,58 @@ ucs2_strsize(const ucs2_char_t *data, unsigned long maxlength) } EXPORT_SYMBOL(ucs2_strsize); +/** + * ucs2_strscpy() - Copy a UCS2 string into a sized buffer. + * + * @dst: Pointer to the destination buffer where to copy the string to. + * @src: Pointer to the source buffer where to copy the string from. + * @count: Size of the destination buffer, in UCS2 (16-bit) characters. + * + * Like strscpy(), only for UCS2 strings. + * + * Copy the source string @src, or as much of it as fits, into the destination + * buffer @dst. The behavior is undefined if the string buffers overlap. The + * destination buffer @dst is always NUL-terminated, unless it's zero-sized. + * + * Return: The number of characters copied into @dst (excluding the trailing + * %NUL terminator) or -E2BIG if @count is 0 or @src was truncated due to the + * destination buffer being too small. + */ +ssize_t ucs2_strscpy(ucs2_char_t *dst, const ucs2_char_t *src, size_t count) +{ + long res; + + /* + * Ensure that we have a valid amount of space. We need to store at + * least one NUL-character. + */ + if (count == 0 || WARN_ON_ONCE(count > INT_MAX / sizeof(*dst))) + return -E2BIG; + + /* + * Copy at most 'count' characters, return early if we find a + * NUL-terminator. + */ + for (res = 0; res < count; res++) { + ucs2_char_t c; + + c = src[res]; + dst[res] = c; + + if (!c) + return res; + } + + /* + * The loop above terminated without finding a NUL-terminator, + * exceeding the 'count': Enforce proper NUL-termination and return + * error. + */ + dst[count - 1] = 0; + return -E2BIG; +} +EXPORT_SYMBOL(ucs2_strscpy); + int ucs2_strncmp(const ucs2_char_t *a, const ucs2_char_t *b, size_t len) { -- cgit v1.2.3 From 00b1248606ba3979ccae30ed11df8cdc1a84245a Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 27 Aug 2023 23:14:05 +0200 Subject: firmware: qcom_scm: Add support for Qualcomm Secure Execution Environment SCM interface Add support for SCM calls to Secure OS and the Secure Execution Environment (SEE) residing in the TrustZone (TZ) via the QSEECOM interface. This allows communication with Secure/TZ applications, for example 'uefisecapp' managing access to UEFI variables. For better separation, make qcom_scm spin up a dedicated child (platform) device in case QSEECOM support has been detected. The corresponding driver for this device is then responsible for managing any QSEECOM clients. Specifically, this driver attempts to automatically detect known and supported applications, creating a client (auxiliary) device for each one. The respective client/auxiliary driver is then responsible for managing and communicating with the application. While this patch introduces only a very basic interface without the more advanced features (such as re-entrant and blocking SCM calls and listeners/callbacks), this is enough to talk to the aforementioned 'uefisecapp'. Signed-off-by: Maximilian Luz Reviewed-by: Johan Hovold Link: https://lore.kernel.org/r/20230827211408.689076-3-luzmaximilian@gmail.com Signed-off-by: Bjorn Andersson --- MAINTAINERS | 6 + drivers/firmware/Kconfig | 16 ++ drivers/firmware/Makefile | 1 + drivers/firmware/qcom_qseecom.c | 118 +++++++++ drivers/firmware/qcom_scm.c | 394 +++++++++++++++++++++++++++++ include/linux/firmware/qcom/qcom_qseecom.h | 46 ++++ include/linux/firmware/qcom/qcom_scm.h | 22 ++ 7 files changed, 603 insertions(+) create mode 100644 drivers/firmware/qcom_qseecom.c create mode 100644 include/linux/firmware/qcom/qcom_qseecom.h diff --git a/MAINTAINERS b/MAINTAINERS index 90f13281d297..8f373d8bdd6a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17800,6 +17800,12 @@ S: Maintained F: Documentation/devicetree/bindings/mtd/qcom,nandc.yaml F: drivers/mtd/nand/raw/qcom_nandc.c +QUALCOMM QSEECOM DRIVER +M: Maximilian Luz +L: linux-arm-msm@vger.kernel.org +S: Maintained +F: drivers/firmware/qcom_qseecom.c + QUALCOMM RMNET DRIVER M: Subash Abhinov Kasiviswanathan M: Sean Tranchetti diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index b59e3041fd62..3e41efe494d4 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -226,6 +226,22 @@ config QCOM_SCM_DOWNLOAD_MODE_DEFAULT Say Y here to enable "download mode" by default. +config QCOM_QSEECOM + bool "Qualcomm QSEECOM interface driver" + depends on QCOM_SCM=y + help + Various Qualcomm SoCs have a Secure Execution Environment (SEE) running + in the Trust Zone. This module provides an interface to that via the + QSEECOM mechanism, using SCM calls. + + The QSEECOM interface allows, among other things, access to applications + running in the SEE. An example of such an application is 'uefisecapp', + which is required to access UEFI variables on certain systems. If + selected, the interface will also attempt to detect and register client + devices for supported applications. + + Select Y here to enable the QSEECOM interface driver. + config SYSFB bool select BOOT_VESA_SUPPORT diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 28fcddcd688f..aa48e0821b7d 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o obj-$(CONFIG_QCOM_SCM) += qcom-scm.o qcom-scm-objs += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o +obj-$(CONFIG_QCOM_QSEECOM) += qcom_qseecom.o obj-$(CONFIG_SYSFB) += sysfb.o obj-$(CONFIG_SYSFB_SIMPLEFB) += sysfb_simplefb.o obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o diff --git a/drivers/firmware/qcom_qseecom.c b/drivers/firmware/qcom_qseecom.c new file mode 100644 index 000000000000..bba32096c956 --- /dev/null +++ b/drivers/firmware/qcom_qseecom.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Qualcomm Secure Execution Environment (SEE) interface (QSEECOM). + * Responsible for setting up and managing QSEECOM client devices. + * + * Copyright (C) 2023 Maximilian Luz + */ +#include +#include +#include +#include +#include + +#include +#include + +struct qseecom_app_desc { + const char *app_name; + const char *dev_name; +}; + +static void qseecom_client_release(struct device *dev) +{ + struct qseecom_client *client; + + client = container_of(dev, struct qseecom_client, aux_dev.dev); + kfree(client); +} + +static void qseecom_client_remove(void *data) +{ + struct qseecom_client *client = data; + + auxiliary_device_delete(&client->aux_dev); + auxiliary_device_uninit(&client->aux_dev); +} + +static int qseecom_client_register(struct platform_device *qseecom_dev, + const struct qseecom_app_desc *desc) +{ + struct qseecom_client *client; + u32 app_id; + int ret; + + /* Try to find the app ID, skip device if not found */ + ret = qcom_scm_qseecom_app_get_id(desc->app_name, &app_id); + if (ret) + return ret == -ENOENT ? 0 : ret; + + dev_info(&qseecom_dev->dev, "setting up client for %s\n", desc->app_name); + + /* Allocate and set-up the client device */ + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) + return -ENOMEM; + + client->aux_dev.name = desc->dev_name; + client->aux_dev.dev.parent = &qseecom_dev->dev; + client->aux_dev.dev.release = qseecom_client_release; + client->app_id = app_id; + + ret = auxiliary_device_init(&client->aux_dev); + if (ret) { + kfree(client); + return ret; + } + + ret = auxiliary_device_add(&client->aux_dev); + if (ret) { + auxiliary_device_uninit(&client->aux_dev); + return ret; + } + + ret = devm_add_action_or_reset(&qseecom_dev->dev, qseecom_client_remove, client); + if (ret) + return ret; + + return 0; +} + +/* + * List of supported applications. One client device will be created per entry, + * assuming the app has already been loaded (usually by firmware bootloaders) + * and its ID can be queried successfully. + */ +static const struct qseecom_app_desc qcom_qseecom_apps[] = {}; + +static int qcom_qseecom_probe(struct platform_device *qseecom_dev) +{ + int ret; + int i; + + /* Set up client devices for each base application */ + for (i = 0; i < ARRAY_SIZE(qcom_qseecom_apps); i++) { + ret = qseecom_client_register(qseecom_dev, &qcom_qseecom_apps[i]); + if (ret) + return ret; + } + + return 0; +} + +static struct platform_driver qcom_qseecom_driver = { + .driver = { + .name = "qcom_qseecom", + }, + .probe = qcom_qseecom_probe, +}; + +static int __init qcom_qseecom_init(void) +{ + return platform_driver_register(&qcom_qseecom_driver); +} +subsys_initcall(qcom_qseecom_init); + +MODULE_AUTHOR("Maximilian Luz "); +MODULE_DESCRIPTION("Driver for the Qualcomm SEE (QSEECOM) interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 06fe8aca870d..f9d5c31b8ec7 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -55,6 +55,53 @@ struct qcom_scm_mem_map_info { __le64 mem_size; }; +/** + * struct qcom_scm_qseecom_resp - QSEECOM SCM call response. + * @result: Result or status of the SCM call. See &enum qcom_scm_qseecom_result. + * @resp_type: Type of the response. See &enum qcom_scm_qseecom_resp_type. + * @data: Response data. The type of this data is given in @resp_type. + */ +struct qcom_scm_qseecom_resp { + u64 result; + u64 resp_type; + u64 data; +}; + +enum qcom_scm_qseecom_result { + QSEECOM_RESULT_SUCCESS = 0, + QSEECOM_RESULT_INCOMPLETE = 1, + QSEECOM_RESULT_BLOCKED_ON_LISTENER = 2, + QSEECOM_RESULT_FAILURE = 0xFFFFFFFF, +}; + +enum qcom_scm_qseecom_resp_type { + QSEECOM_SCM_RES_APP_ID = 0xEE01, + QSEECOM_SCM_RES_QSEOS_LISTENER_ID = 0xEE02, +}; + +enum qcom_scm_qseecom_tz_owner { + QSEECOM_TZ_OWNER_SIP = 2, + QSEECOM_TZ_OWNER_TZ_APPS = 48, + QSEECOM_TZ_OWNER_QSEE_OS = 50 +}; + +enum qcom_scm_qseecom_tz_svc { + QSEECOM_TZ_SVC_APP_ID_PLACEHOLDER = 0, + QSEECOM_TZ_SVC_APP_MGR = 1, + QSEECOM_TZ_SVC_INFO = 6, +}; + +enum qcom_scm_qseecom_tz_cmd_app { + QSEECOM_TZ_CMD_APP_SEND = 1, + QSEECOM_TZ_CMD_APP_LOOKUP = 3, +}; + +enum qcom_scm_qseecom_tz_cmd_info { + QSEECOM_TZ_CMD_INFO_VERSION = 3, +}; + +#define QSEECOM_MAX_APP_NAME_SIZE 64 + /* Each bit configures cold/warm boot address for one of the 4 CPUs */ static const u8 qcom_scm_cpu_cold_bits[QCOM_SCM_BOOT_MAX_CPUS] = { 0, BIT(0), BIT(3), BIT(5) @@ -1321,6 +1368,340 @@ static int qcom_scm_find_dload_address(struct device *dev, u64 *addr) return 0; } +#ifdef CONFIG_QCOM_QSEECOM + +/* Lock for QSEECOM SCM call executions */ +static DEFINE_MUTEX(qcom_scm_qseecom_call_lock); + +static int __qcom_scm_qseecom_call(const struct qcom_scm_desc *desc, + struct qcom_scm_qseecom_resp *res) +{ + struct qcom_scm_res scm_res = {}; + int status; + + /* + * QSEECOM SCM calls should not be executed concurrently. Therefore, we + * require the respective call lock to be held. + */ + lockdep_assert_held(&qcom_scm_qseecom_call_lock); + + status = qcom_scm_call(__scm->dev, desc, &scm_res); + + res->result = scm_res.result[0]; + res->resp_type = scm_res.result[1]; + res->data = scm_res.result[2]; + + if (status) + return status; + + return 0; +} + +/** + * qcom_scm_qseecom_call() - Perform a QSEECOM SCM call. + * @desc: SCM call descriptor. + * @res: SCM call response (output). + * + * Performs the QSEECOM SCM call described by @desc, returning the response in + * @rsp. + * + * Return: Zero on success, nonzero on failure. + */ +static int qcom_scm_qseecom_call(const struct qcom_scm_desc *desc, + struct qcom_scm_qseecom_resp *res) +{ + int status; + + /* + * Note: Multiple QSEECOM SCM calls should not be executed same time, + * so lock things here. This needs to be extended to callback/listener + * handling when support for that is implemented. + */ + + mutex_lock(&qcom_scm_qseecom_call_lock); + status = __qcom_scm_qseecom_call(desc, res); + mutex_unlock(&qcom_scm_qseecom_call_lock); + + dev_dbg(__scm->dev, "%s: owner=%x, svc=%x, cmd=%x, result=%lld, type=%llx, data=%llx\n", + __func__, desc->owner, desc->svc, desc->cmd, res->result, + res->resp_type, res->data); + + if (status) { + dev_err(__scm->dev, "qseecom: scm call failed with error %d\n", status); + return status; + } + + /* + * TODO: Handle incomplete and blocked calls: + * + * Incomplete and blocked calls are not supported yet. Some devices + * and/or commands require those, some don't. Let's warn about them + * prominently in case someone attempts to try these commands with a + * device/command combination that isn't supported yet. + */ + WARN_ON(res->result == QSEECOM_RESULT_INCOMPLETE); + WARN_ON(res->result == QSEECOM_RESULT_BLOCKED_ON_LISTENER); + + return 0; +} + +/** + * qcom_scm_qseecom_get_version() - Query the QSEECOM version. + * @version: Pointer where the QSEECOM version will be stored. + * + * Performs the QSEECOM SCM querying the QSEECOM version currently running in + * the TrustZone. + * + * Return: Zero on success, nonzero on failure. + */ +static int qcom_scm_qseecom_get_version(u32 *version) +{ + struct qcom_scm_desc desc = {}; + struct qcom_scm_qseecom_resp res = {}; + u32 feature = 10; + int ret; + + desc.owner = QSEECOM_TZ_OWNER_SIP; + desc.svc = QSEECOM_TZ_SVC_INFO; + desc.cmd = QSEECOM_TZ_CMD_INFO_VERSION; + desc.arginfo = QCOM_SCM_ARGS(1, QCOM_SCM_VAL); + desc.args[0] = feature; + + ret = qcom_scm_qseecom_call(&desc, &res); + if (ret) + return ret; + + *version = res.result; + return 0; +} + +/** + * qcom_scm_qseecom_app_get_id() - Query the app ID for a given QSEE app name. + * @app_name: The name of the app. + * @app_id: The returned app ID. + * + * Query and return the application ID of the SEE app identified by the given + * name. This returned ID is the unique identifier of the app required for + * subsequent communication. + * + * Return: Zero on success, nonzero on failure, -ENOENT if the app has not been + * loaded or could not be found. + */ +int qcom_scm_qseecom_app_get_id(const char *app_name, u32 *app_id) +{ + unsigned long name_buf_size = QSEECOM_MAX_APP_NAME_SIZE; + unsigned long app_name_len = strlen(app_name); + struct qcom_scm_desc desc = {}; + struct qcom_scm_qseecom_resp res = {}; + dma_addr_t name_buf_phys; + char *name_buf; + int status; + + if (app_name_len >= name_buf_size) + return -EINVAL; + + name_buf = kzalloc(name_buf_size, GFP_KERNEL); + if (!name_buf) + return -ENOMEM; + + memcpy(name_buf, app_name, app_name_len); + + name_buf_phys = dma_map_single(__scm->dev, name_buf, name_buf_size, DMA_TO_DEVICE); + status = dma_mapping_error(__scm->dev, name_buf_phys); + if (status) { + kfree(name_buf); + dev_err(__scm->dev, "qseecom: failed to map dma address\n"); + return status; + } + + desc.owner = QSEECOM_TZ_OWNER_QSEE_OS; + desc.svc = QSEECOM_TZ_SVC_APP_MGR; + desc.cmd = QSEECOM_TZ_CMD_APP_LOOKUP; + desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_RW, QCOM_SCM_VAL); + desc.args[0] = name_buf_phys; + desc.args[1] = app_name_len; + + status = qcom_scm_qseecom_call(&desc, &res); + dma_unmap_single(__scm->dev, name_buf_phys, name_buf_size, DMA_TO_DEVICE); + kfree(name_buf); + + if (status) + return status; + + if (res.result == QSEECOM_RESULT_FAILURE) + return -ENOENT; + + if (res.result != QSEECOM_RESULT_SUCCESS) + return -EINVAL; + + if (res.resp_type != QSEECOM_SCM_RES_APP_ID) + return -EINVAL; + + *app_id = res.data; + return 0; +} +EXPORT_SYMBOL_GPL(qcom_scm_qseecom_app_get_id); + +/** + * qcom_scm_qseecom_app_send() - Send to and receive data from a given QSEE app. + * @app_id: The ID of the target app. + * @req: Request buffer sent to the app (must be DMA-mappable). + * @req_size: Size of the request buffer. + * @rsp: Response buffer, written to by the app (must be DMA-mappable). + * @rsp_size: Size of the response buffer. + * + * Sends a request to the QSEE app associated with the given ID and read back + * its response. The caller must provide two DMA memory regions, one for the + * request and one for the response, and fill out the @req region with the + * respective (app-specific) request data. The QSEE app reads this and returns + * its response in the @rsp region. + * + * Return: Zero on success, nonzero on failure. + */ +int qcom_scm_qseecom_app_send(u32 app_id, void *req, size_t req_size, void *rsp, + size_t rsp_size) +{ + struct qcom_scm_qseecom_resp res = {}; + struct qcom_scm_desc desc = {}; + dma_addr_t req_phys; + dma_addr_t rsp_phys; + int status; + + /* Map request buffer */ + req_phys = dma_map_single(__scm->dev, req, req_size, DMA_TO_DEVICE); + status = dma_mapping_error(__scm->dev, req_phys); + if (status) { + dev_err(__scm->dev, "qseecom: failed to map request buffer\n"); + return status; + } + + /* Map response buffer */ + rsp_phys = dma_map_single(__scm->dev, rsp, rsp_size, DMA_FROM_DEVICE); + status = dma_mapping_error(__scm->dev, rsp_phys); + if (status) { + dma_unmap_single(__scm->dev, req_phys, req_size, DMA_TO_DEVICE); + dev_err(__scm->dev, "qseecom: failed to map response buffer\n"); + return status; + } + + /* Set up SCM call data */ + desc.owner = QSEECOM_TZ_OWNER_TZ_APPS; + desc.svc = QSEECOM_TZ_SVC_APP_ID_PLACEHOLDER; + desc.cmd = QSEECOM_TZ_CMD_APP_SEND; + desc.arginfo = QCOM_SCM_ARGS(5, QCOM_SCM_VAL, + QCOM_SCM_RW, QCOM_SCM_VAL, + QCOM_SCM_RW, QCOM_SCM_VAL); + desc.args[0] = app_id; + desc.args[1] = req_phys; + desc.args[2] = req_size; + desc.args[3] = rsp_phys; + desc.args[4] = rsp_size; + + /* Perform call */ + status = qcom_scm_qseecom_call(&desc, &res); + + /* Unmap buffers */ + dma_unmap_single(__scm->dev, rsp_phys, rsp_size, DMA_FROM_DEVICE); + dma_unmap_single(__scm->dev, req_phys, req_size, DMA_TO_DEVICE); + + if (status) + return status; + + if (res.result != QSEECOM_RESULT_SUCCESS) + return -EIO; + + return 0; +} +EXPORT_SYMBOL_GPL(qcom_scm_qseecom_app_send); + +/* + * We do not yet support re-entrant calls via the qseecom interface. To prevent + + any potential issues with this, only allow validated machines for now. + */ +static const struct of_device_id qcom_scm_qseecom_allowlist[] = { + { .compatible = "lenovo,thinkpad-x13s", }, + { } +}; + +static bool qcom_scm_qseecom_machine_is_allowed(void) +{ + struct device_node *np; + bool match; + + np = of_find_node_by_path("/"); + if (!np) + return false; + + match = of_match_node(qcom_scm_qseecom_allowlist, np); + of_node_put(np); + + return match; +} + +static void qcom_scm_qseecom_free(void *data) +{ + struct platform_device *qseecom_dev = data; + + platform_device_del(qseecom_dev); + platform_device_put(qseecom_dev); +} + +static int qcom_scm_qseecom_init(struct qcom_scm *scm) +{ + struct platform_device *qseecom_dev; + u32 version; + int ret; + + /* + * Note: We do two steps of validation here: First, we try to query the + * QSEECOM version as a check to see if the interface exists on this + * device. Second, we check against known good devices due to current + * driver limitations (see comment in qcom_scm_qseecom_allowlist). + * + * Note that we deliberately do the machine check after the version + * check so that we can log potentially supported devices. This should + * be safe as downstream sources indicate that the version query is + * neither blocking nor reentrant. + */ + ret = qcom_scm_qseecom_get_version(&version); + if (ret) + return 0; + + dev_info(scm->dev, "qseecom: found qseecom with version 0x%x\n", version); + + if (!qcom_scm_qseecom_machine_is_allowed()) { + dev_info(scm->dev, "qseecom: untested machine, skipping\n"); + return 0; + } + + /* + * Set up QSEECOM interface device. All application clients will be + * set up and managed by the corresponding driver for it. + */ + qseecom_dev = platform_device_alloc("qcom_qseecom", -1); + if (!qseecom_dev) + return -ENOMEM; + + qseecom_dev->dev.parent = scm->dev; + + ret = platform_device_add(qseecom_dev); + if (ret) { + platform_device_put(qseecom_dev); + return ret; + } + + return devm_add_action_or_reset(scm->dev, qcom_scm_qseecom_free, qseecom_dev); +} + +#else /* CONFIG_QCOM_QSEECOM */ + +static int qcom_scm_qseecom_init(struct qcom_scm *scm) +{ + return 0; +} + +#endif /* CONFIG_QCOM_QSEECOM */ + /** * qcom_scm_is_available() - Checks if SCM is available */ @@ -1468,6 +1849,19 @@ static int qcom_scm_probe(struct platform_device *pdev) if (download_mode) qcom_scm_set_download_mode(true); + /* + * Initialize the QSEECOM interface. + * + * Note: QSEECOM is fairly self-contained and this only adds the + * interface device (the driver of which does most of the heavy + * lifting). So any errors returned here should be either -ENOMEM or + * -EINVAL (with the latter only in case there's a bug in our code). + * This means that there is no need to bring down the whole SCM driver. + * Just log the error instead and let SCM live. + */ + ret = qcom_scm_qseecom_init(scm); + WARN(ret < 0, "failed to initialize qseecom: %d\n", ret); + return 0; } diff --git a/include/linux/firmware/qcom/qcom_qseecom.h b/include/linux/firmware/qcom/qcom_qseecom.h new file mode 100644 index 000000000000..b531547e1dc9 --- /dev/null +++ b/include/linux/firmware/qcom/qcom_qseecom.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for Qualcomm Secure Execution Environment (SEE) interface (QSEECOM). + * Responsible for setting up and managing QSEECOM client devices. + * + * Copyright (C) 2023 Maximilian Luz + */ +#include +#include + +#include + +/** + * struct qseecom_client - QSEECOM client device. + * @aux_dev: Underlying auxiliary device. + * @app_id: ID of the loaded application. + */ +struct qseecom_client { + struct auxiliary_device aux_dev; + u32 app_id; +}; + +/** + * qcom_qseecom_app_send() - Send to and receive data from a given QSEE app. + * @client: The QSEECOM client associated with the target app. + * @req: Request buffer sent to the app (must be DMA-mappable). + * @req_size: Size of the request buffer. + * @rsp: Response buffer, written to by the app (must be DMA-mappable). + * @rsp_size: Size of the response buffer. + * + * Sends a request to the QSEE app associated with the given client and read + * back its response. The caller must provide two DMA memory regions, one for + * the request and one for the response, and fill out the @req region with the + * respective (app-specific) request data. The QSEE app reads this and returns + * its response in the @rsp region. + * + * Note: This is a convenience wrapper around qcom_scm_qseecom_app_send(). + * Clients should prefer to use this wrapper. + * + * Return: Zero on success, nonzero on failure. + */ +static inline int qcom_qseecom_app_send(struct qseecom_client *client, void *req, size_t req_size, + void *rsp, size_t rsp_size) +{ + return qcom_scm_qseecom_app_send(client->app_id, req, req_size, rsp, rsp_size); +} diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h index 0c091a3f6d49..e1ea2eb56d04 100644 --- a/include/linux/firmware/qcom/qcom_scm.h +++ b/include/linux/firmware/qcom/qcom_scm.h @@ -122,4 +122,26 @@ extern int qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val, extern int qcom_scm_lmh_profile_change(u32 profile_id); extern bool qcom_scm_lmh_dcvsh_available(void); +#ifdef CONFIG_QCOM_QSEECOM + +int qcom_scm_qseecom_app_get_id(const char *app_name, u32 *app_id); +int qcom_scm_qseecom_app_send(u32 app_id, void *req, size_t req_size, void *rsp, + size_t rsp_size); + +#else /* CONFIG_QCOM_QSEECOM */ + +static inline int qcom_scm_qseecom_app_get_id(const char *app_name, u32 *app_id) +{ + return -EINVAL; +} + +static inline int qcom_scm_qseecom_app_send(u32 app_id, void *req, + size_t req_size, void *rsp, + size_t rsp_size) +{ + return -EINVAL; +} + +#endif /* CONFIG_QCOM_QSEECOM */ + #endif -- cgit v1.2.3 From 759e7a2b62eb3ef3c93ffeb5cca788a09627d7d9 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 27 Aug 2023 23:14:06 +0200 Subject: firmware: Add support for Qualcomm UEFI Secure Application On platforms using the Qualcomm UEFI Secure Application (uefisecapp), EFI variables cannot be accessed via the standard interface in EFI runtime mode. The respective functions return EFI_UNSUPPORTED. On these platforms, we instead need to talk to uefisecapp. This commit provides support for this and registers the respective efivars operations to access EFI variables from the kernel. Communication with uefisecapp follows the Qualcomm QSEECOM / Secure OS conventions via the respective SCM call interface. This is also the reason why variable access works normally while boot services are active. During this time, said SCM interface is managed by the boot services. When calling ExitBootServices(), the ownership is transferred to the kernel. Therefore, UEFI must not use that interface itself (as multiple parties accessing this interface at the same time may lead to complications) and cannot access variables for us. Signed-off-by: Maximilian Luz Acked-by: Ard Biesheuvel Reviewed-by: Johan Hovold Link: https://lore.kernel.org/r/20230827211408.689076-4-luzmaximilian@gmail.com Signed-off-by: Bjorn Andersson --- MAINTAINERS | 6 + drivers/firmware/Kconfig | 16 + drivers/firmware/Makefile | 1 + drivers/firmware/qcom_qseecom.c | 4 +- drivers/firmware/qcom_qseecom_uefisecapp.c | 871 +++++++++++++++++++++++++++++ 5 files changed, 897 insertions(+), 1 deletion(-) create mode 100644 drivers/firmware/qcom_qseecom_uefisecapp.c diff --git a/MAINTAINERS b/MAINTAINERS index 8f373d8bdd6a..01dde0acebd3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17806,6 +17806,12 @@ L: linux-arm-msm@vger.kernel.org S: Maintained F: drivers/firmware/qcom_qseecom.c +QUALCOMM QSEECOM UEFISECAPP DRIVER +M: Maximilian Luz +L: linux-arm-msm@vger.kernel.org +S: Maintained +F: drivers/firmware/qcom_qseecom_uefisecapp.c + QUALCOMM RMNET DRIVER M: Subash Abhinov Kasiviswanathan M: Sean Tranchetti diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 3e41efe494d4..a94202229a71 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -242,6 +242,22 @@ config QCOM_QSEECOM Select Y here to enable the QSEECOM interface driver. +config QCOM_QSEECOM_UEFISECAPP + bool "Qualcomm SEE UEFI Secure App client driver" + depends on QCOM_QSEECOM + depends on EFI + help + Various Qualcomm SoCs do not allow direct access to EFI variables. + Instead, these need to be accessed via the UEFI Secure Application + (uefisecapp), residing in the Secure Execution Environment (SEE). + + This module provides a client driver for uefisecapp, installing efivar + operations to allow the kernel accessing EFI variables, and via that also + provide user-space with access to EFI variables via efivarfs. + + Select Y here to provide access to EFI variables on the aforementioned + platforms. + config SYSFB bool select BOOT_VESA_SUPPORT diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index aa48e0821b7d..d41b094a5e58 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o obj-$(CONFIG_QCOM_SCM) += qcom-scm.o qcom-scm-objs += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o obj-$(CONFIG_QCOM_QSEECOM) += qcom_qseecom.o +obj-$(CONFIG_QCOM_QSEECOM_UEFISECAPP) += qcom_qseecom_uefisecapp.o obj-$(CONFIG_SYSFB) += sysfb.o obj-$(CONFIG_SYSFB_SIMPLEFB) += sysfb_simplefb.o obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o diff --git a/drivers/firmware/qcom_qseecom.c b/drivers/firmware/qcom_qseecom.c index bba32096c956..731e6d5719f9 100644 --- a/drivers/firmware/qcom_qseecom.c +++ b/drivers/firmware/qcom_qseecom.c @@ -83,7 +83,9 @@ static int qseecom_client_register(struct platform_device *qseecom_dev, * assuming the app has already been loaded (usually by firmware bootloaders) * and its ID can be queried successfully. */ -static const struct qseecom_app_desc qcom_qseecom_apps[] = {}; +static const struct qseecom_app_desc qcom_qseecom_apps[] = { + { "qcom.tz.uefisecapp", "uefisecapp" }, +}; static int qcom_qseecom_probe(struct platform_device *qseecom_dev) { diff --git a/drivers/firmware/qcom_qseecom_uefisecapp.c b/drivers/firmware/qcom_qseecom_uefisecapp.c new file mode 100644 index 000000000000..a33acdaf7b78 --- /dev/null +++ b/drivers/firmware/qcom_qseecom_uefisecapp.c @@ -0,0 +1,871 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Client driver for Qualcomm UEFI Secure Application (qcom.tz.uefisecapp). + * Provides access to UEFI variables on platforms where they are secured by the + * aforementioned Secure Execution Environment (SEE) application. + * + * Copyright (C) 2023 Maximilian Luz + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* -- Qualcomm "uefisecapp" interface definitions. -------------------------- */ + +/* Maximum length of name string with null-terminator */ +#define QSEE_MAX_NAME_LEN 1024 + +#define QSEE_CMD_UEFI(x) (0x8000 | (x)) +#define QSEE_CMD_UEFI_GET_VARIABLE QSEE_CMD_UEFI(0) +#define QSEE_CMD_UEFI_SET_VARIABLE QSEE_CMD_UEFI(1) +#define QSEE_CMD_UEFI_GET_NEXT_VARIABLE QSEE_CMD_UEFI(2) +#define QSEE_CMD_UEFI_QUERY_VARIABLE_INFO QSEE_CMD_UEFI(3) + +/** + * struct qsee_req_uefi_get_variable - Request for GetVariable command. + * @command_id: The ID of the command. Must be %QSEE_CMD_UEFI_GET_VARIABLE. + * @length: Length of the request in bytes, including this struct and any + * parameters (name, GUID) stored after it as well as any padding + * thereof for alignment. + * @name_offset: Offset from the start of this struct to where the variable + * name is stored (as utf-16 string), in bytes. + * @name_size: Size of the name parameter in bytes, including null-terminator. + * @guid_offset: Offset from the start of this struct to where the GUID + * parameter is stored, in bytes. + * @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t). + * @data_size: Size of the output buffer, in bytes. + */ +struct qsee_req_uefi_get_variable { + u32 command_id; + u32 length; + u32 name_offset; + u32 name_size; + u32 guid_offset; + u32 guid_size; + u32 data_size; +} __packed; + +/** + * struct qsee_rsp_uefi_get_variable - Response for GetVariable command. + * @command_id: The ID of the command. Should be %QSEE_CMD_UEFI_GET_VARIABLE. + * @length: Length of the response in bytes, including this struct and the + * returned data. + * @status: Status of this command. + * @attributes: EFI variable attributes. + * @data_offset: Offset from the start of this struct to where the data is + * stored, in bytes. + * @data_size: Size of the returned data, in bytes. In case status indicates + * that the buffer is too small, this will be the size required + * to store the EFI variable data. + */ +struct qsee_rsp_uefi_get_variable { + u32 command_id; + u32 length; + u32 status; + u32 attributes; + u32 data_offset; + u32 data_size; +} __packed; + +/** + * struct qsee_req_uefi_set_variable - Request for the SetVariable command. + * @command_id: The ID of the command. Must be %QSEE_CMD_UEFI_SET_VARIABLE. + * @length: Length of the request in bytes, including this struct and any + * parameters (name, GUID, data) stored after it as well as any + * padding thereof required for alignment. + * @name_offset: Offset from the start of this struct to where the variable + * name is stored (as utf-16 string), in bytes. + * @name_size: Size of the name parameter in bytes, including null-terminator. + * @guid_offset: Offset from the start of this struct to where the GUID + * parameter is stored, in bytes. + * @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t). + * @attributes: The EFI variable attributes to set for this variable. + * @data_offset: Offset from the start of this struct to where the EFI variable + * data is stored, in bytes. + * @data_size: Size of EFI variable data, in bytes. + * + */ +struct qsee_req_uefi_set_variable { + u32 command_id; + u32 length; + u32 name_offset; + u32 name_size; + u32 guid_offset; + u32 guid_size; + u32 attributes; + u32 data_offset; + u32 data_size; +} __packed; + +/** + * struct qsee_rsp_uefi_set_variable - Response for the SetVariable command. + * @command_id: The ID of the command. Should be %QSEE_CMD_UEFI_SET_VARIABLE. + * @length: The length of this response, i.e. the size of this struct in + * bytes. + * @status: Status of this command. + * @_unknown1: Unknown response field. + * @_unknown2: Unknown response field. + */ +struct qsee_rsp_uefi_set_variable { + u32 command_id; + u32 length; + u32 status; + u32 _unknown1; + u32 _unknown2; +} __packed; + +/** + * struct qsee_req_uefi_get_next_variable - Request for the + * GetNextVariableName command. + * @command_id: The ID of the command. Must be + * %QSEE_CMD_UEFI_GET_NEXT_VARIABLE. + * @length: Length of the request in bytes, including this struct and any + * parameters (name, GUID) stored after it as well as any padding + * thereof for alignment. + * @guid_offset: Offset from the start of this struct to where the GUID + * parameter is stored, in bytes. + * @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t). + * @name_offset: Offset from the start of this struct to where the variable + * name is stored (as utf-16 string), in bytes. + * @name_size: Size of the name parameter in bytes, including null-terminator. + */ +struct qsee_req_uefi_get_next_variable { + u32 command_id; + u32 length; + u32 guid_offset; + u32 guid_size; + u32 name_offset; + u32 name_size; +} __packed; + +/** + * struct qsee_rsp_uefi_get_next_variable - Response for the + * GetNextVariableName command. + * @command_id: The ID of the command. Should be + * %QSEE_CMD_UEFI_GET_NEXT_VARIABLE. + * @length: Length of the response in bytes, including this struct and any + * parameters (name, GUID) stored after it as well as any padding + * thereof for alignment. + * @status: Status of this command. + * @guid_offset: Offset from the start of this struct to where the GUID + * parameter is stored, in bytes. + * @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t). + * @name_offset: Offset from the start of this struct to where the variable + * name is stored (as utf-16 string), in bytes. + * @name_size: Size of the name parameter in bytes, including null-terminator. + */ +struct qsee_rsp_uefi_get_next_variable { + u32 command_id; + u32 length; + u32 status; + u32 guid_offset; + u32 guid_size; + u32 name_offset; + u32 name_size; +} __packed; + +/** + * struct qsee_req_uefi_query_variable_info - Response for the + * GetNextVariableName command. + * @command_id: The ID of the command. Must be + * %QSEE_CMD_UEFI_QUERY_VARIABLE_INFO. + * @length: The length of this request, i.e. the size of this struct in + * bytes. + * @attributes: The storage attributes to query the info for. + */ +struct qsee_req_uefi_query_variable_info { + u32 command_id; + u32 length; + u32 attributes; +} __packed; + +/** + * struct qsee_rsp_uefi_query_variable_info - Response for the + * GetNextVariableName command. + * @command_id: The ID of the command. Must be + * %QSEE_CMD_UEFI_QUERY_VARIABLE_INFO. + * @length: The length of this response, i.e. the size of this + * struct in bytes. + * @status: Status of this command. + * @_pad: Padding. + * @storage_space: Full storage space size, in bytes. + * @remaining_space: Free storage space available, in bytes. + * @max_variable_size: Maximum variable data size, in bytes. + */ +struct qsee_rsp_uefi_query_variable_info { + u32 command_id; + u32 length; + u32 status; + u32 _pad; + u64 storage_space; + u64 remaining_space; + u64 max_variable_size; +} __packed; + +/* -- Alignment helpers ----------------------------------------------------- */ + +/* + * Helper macro to ensure proper alignment of types (fields and arrays) when + * stored in some (contiguous) buffer. + * + * Note: The driver from which this one has been reverse-engineered expects an + * alignment of 8 bytes (64 bits) for GUIDs. Our definition of efi_guid_t, + * however, has an alignment of 4 byte (32 bits). So far, this seems to work + * fine here. See also the comment on the typedef of efi_guid_t. + */ +#define qcuefi_buf_align_fields(fields...) \ + ({ \ + size_t __len = 0; \ + fields \ + __len; \ + }) + +#define __field_impl(size, align, offset) \ + ({ \ + size_t *__offset = (offset); \ + size_t __aligned; \ + \ + __aligned = ALIGN(__len, align); \ + __len = __aligned + (size); \ + \ + if (__offset) \ + *__offset = __aligned; \ + }); + +#define __array_offs(type, count, offset) \ + __field_impl(sizeof(type) * (count), __alignof__(type), offset) + +#define __array(type, count) __array_offs(type, count, NULL) +#define __field_offs(type, offset) __array_offs(type, 1, offset) +#define __field(type) __array_offs(type, 1, NULL) + +/* -- UEFI app interface. --------------------------------------------------- */ + +struct qcuefi_client { + struct qseecom_client *client; + struct efivars efivars; +}; + +static struct device *qcuefi_dev(struct qcuefi_client *qcuefi) +{ + return &qcuefi->client->aux_dev.dev; +} + +static efi_status_t qsee_uefi_status_to_efi(u32 status) +{ + u64 category = status & 0xf0000000; + u64 code = status & 0x0fffffff; + + return category << (BITS_PER_LONG - 32) | code; +} + +static efi_status_t qsee_uefi_get_variable(struct qcuefi_client *qcuefi, const efi_char16_t *name, + const efi_guid_t *guid, u32 *attributes, + unsigned long *data_size, void *data) +{ + struct qsee_req_uefi_get_variable *req_data; + struct qsee_rsp_uefi_get_variable *rsp_data; + unsigned long buffer_size = *data_size; + efi_status_t efi_status = EFI_SUCCESS; + unsigned long name_length; + size_t guid_offs; + size_t name_offs; + size_t req_size; + size_t rsp_size; + ssize_t status; + + if (!name || !guid) + return EFI_INVALID_PARAMETER; + + name_length = ucs2_strnlen(name, QSEE_MAX_NAME_LEN) + 1; + if (name_length > QSEE_MAX_NAME_LEN) + return EFI_INVALID_PARAMETER; + + if (buffer_size && !data) + return EFI_INVALID_PARAMETER; + + req_size = qcuefi_buf_align_fields( + __field(*req_data) + __array_offs(*name, name_length, &name_offs) + __field_offs(*guid, &guid_offs) + ); + + rsp_size = qcuefi_buf_align_fields( + __field(*rsp_data) + __array(u8, buffer_size) + ); + + req_data = kzalloc(req_size, GFP_KERNEL); + if (!req_data) { + efi_status = EFI_OUT_OF_RESOURCES; + goto out; + } + + rsp_data = kzalloc(rsp_size, GFP_KERNEL); + if (!rsp_data) { + efi_status = EFI_OUT_OF_RESOURCES; + goto out_free_req; + } + + req_data->command_id = QSEE_CMD_UEFI_GET_VARIABLE; + req_data->data_size = buffer_size; + req_data->name_offset = name_offs; + req_data->name_size = name_length * sizeof(*name); + req_data->guid_offset = guid_offs; + req_data->guid_size = sizeof(*guid); + req_data->length = req_size; + + status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name, name_length); + if (status < 0) + return EFI_INVALID_PARAMETER; + + memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size); + + status = qcom_qseecom_app_send(qcuefi->client, req_data, req_size, rsp_data, rsp_size); + if (status) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->command_id != QSEE_CMD_UEFI_GET_VARIABLE) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->length < sizeof(*rsp_data)) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->status) { + dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n", + __func__, rsp_data->status); + efi_status = qsee_uefi_status_to_efi(rsp_data->status); + + /* Update size and attributes in case buffer is too small. */ + if (efi_status == EFI_BUFFER_TOO_SMALL) { + *data_size = rsp_data->data_size; + if (attributes) + *attributes = rsp_data->attributes; + } + + goto out_free; + } + + if (rsp_data->length > rsp_size) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->data_offset + rsp_data->data_size > rsp_data->length) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + /* + * Note: We need to set attributes and data size even if the buffer is + * too small and we won't copy any data. This is described in spec, so + * that callers can either allocate a buffer properly (with two calls + * to this function) or just read back attributes withouth having to + * deal with that. + * + * Specifically: + * - If we have a buffer size of zero and no buffer, just return the + * attributes, required size, and indicate success. + * - If the buffer size is nonzero but too small, indicate that as an + * error. + * - Otherwise, we are good to copy the data. + * + * Note that we have already ensured above that the buffer pointer is + * non-NULL if its size is nonzero. + */ + *data_size = rsp_data->data_size; + if (attributes) + *attributes = rsp_data->attributes; + + if (buffer_size == 0 && !data) { + efi_status = EFI_SUCCESS; + goto out_free; + } + + if (buffer_size < rsp_data->data_size) { + efi_status = EFI_BUFFER_TOO_SMALL; + goto out_free; + } + + memcpy(data, ((void *)rsp_data) + rsp_data->data_offset, rsp_data->data_size); + +out_free: + kfree(rsp_data); +out_free_req: + kfree(req_data); +out: + return efi_status; +} + +static efi_status_t qsee_uefi_set_variable(struct qcuefi_client *qcuefi, const efi_char16_t *name, + const efi_guid_t *guid, u32 attributes, + unsigned long data_size, const void *data) +{ + struct qsee_req_uefi_set_variable *req_data; + struct qsee_rsp_uefi_set_variable *rsp_data; + efi_status_t efi_status = EFI_SUCCESS; + unsigned long name_length; + size_t name_offs; + size_t guid_offs; + size_t data_offs; + size_t req_size; + ssize_t status; + + if (!name || !guid) + return EFI_INVALID_PARAMETER; + + name_length = ucs2_strnlen(name, QSEE_MAX_NAME_LEN) + 1; + if (name_length > QSEE_MAX_NAME_LEN) + return EFI_INVALID_PARAMETER; + + /* + * Make sure we have some data if data_size is nonzero. Note that using + * a size of zero is a valid use-case described in spec and deletes the + * variable. + */ + if (data_size && !data) + return EFI_INVALID_PARAMETER; + + req_size = qcuefi_buf_align_fields( + __field(*req_data) + __array_offs(*name, name_length, &name_offs) + __field_offs(*guid, &guid_offs) + __array_offs(u8, data_size, &data_offs) + ); + + req_data = kzalloc(req_size, GFP_KERNEL); + if (!req_data) { + efi_status = EFI_OUT_OF_RESOURCES; + goto out; + } + + rsp_data = kzalloc(sizeof(*rsp_data), GFP_KERNEL); + if (!rsp_data) { + efi_status = EFI_OUT_OF_RESOURCES; + goto out_free_req; + } + + req_data->command_id = QSEE_CMD_UEFI_SET_VARIABLE; + req_data->attributes = attributes; + req_data->name_offset = name_offs; + req_data->name_size = name_length * sizeof(*name); + req_data->guid_offset = guid_offs; + req_data->guid_size = sizeof(*guid); + req_data->data_offset = data_offs; + req_data->data_size = data_size; + req_data->length = req_size; + + status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name, name_length); + if (status < 0) + return EFI_INVALID_PARAMETER; + + memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size); + + if (data_size) + memcpy(((void *)req_data) + req_data->data_offset, data, req_data->data_size); + + status = qcom_qseecom_app_send(qcuefi->client, req_data, req_size, rsp_data, + sizeof(*rsp_data)); + if (status) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->command_id != QSEE_CMD_UEFI_SET_VARIABLE) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->length != sizeof(*rsp_data)) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->status) { + dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n", + __func__, rsp_data->status); + efi_status = qsee_uefi_status_to_efi(rsp_data->status); + } + +out_free: + kfree(rsp_data); +out_free_req: + kfree(req_data); +out: + return efi_status; +} + +static efi_status_t qsee_uefi_get_next_variable(struct qcuefi_client *qcuefi, + unsigned long *name_size, efi_char16_t *name, + efi_guid_t *guid) +{ + struct qsee_req_uefi_get_next_variable *req_data; + struct qsee_rsp_uefi_get_next_variable *rsp_data; + efi_status_t efi_status = EFI_SUCCESS; + size_t guid_offs; + size_t name_offs; + size_t req_size; + size_t rsp_size; + ssize_t status; + + if (!name_size || !name || !guid) + return EFI_INVALID_PARAMETER; + + if (*name_size == 0) + return EFI_INVALID_PARAMETER; + + req_size = qcuefi_buf_align_fields( + __field(*req_data) + __field_offs(*guid, &guid_offs) + __array_offs(*name, *name_size / sizeof(*name), &name_offs) + ); + + rsp_size = qcuefi_buf_align_fields( + __field(*rsp_data) + __field(*guid) + __array(*name, *name_size / sizeof(*name)) + ); + + req_data = kzalloc(req_size, GFP_KERNEL); + if (!req_data) { + efi_status = EFI_OUT_OF_RESOURCES; + goto out; + } + + rsp_data = kzalloc(rsp_size, GFP_KERNEL); + if (!rsp_data) { + efi_status = EFI_OUT_OF_RESOURCES; + goto out_free_req; + } + + req_data->command_id = QSEE_CMD_UEFI_GET_NEXT_VARIABLE; + req_data->guid_offset = guid_offs; + req_data->guid_size = sizeof(*guid); + req_data->name_offset = name_offs; + req_data->name_size = *name_size; + req_data->length = req_size; + + memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size); + status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name, + *name_size / sizeof(*name)); + if (status < 0) + return EFI_INVALID_PARAMETER; + + status = qcom_qseecom_app_send(qcuefi->client, req_data, req_size, rsp_data, rsp_size); + if (status) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->command_id != QSEE_CMD_UEFI_GET_NEXT_VARIABLE) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->length < sizeof(*rsp_data)) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->status) { + dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n", + __func__, rsp_data->status); + efi_status = qsee_uefi_status_to_efi(rsp_data->status); + + /* + * If the buffer to hold the name is too small, update the + * name_size with the required size, so that callers can + * reallocate it accordingly. + */ + if (efi_status == EFI_BUFFER_TOO_SMALL) + *name_size = rsp_data->name_size; + + goto out_free; + } + + if (rsp_data->length > rsp_size) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->name_offset + rsp_data->name_size > rsp_data->length) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->guid_offset + rsp_data->guid_size > rsp_data->length) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->name_size > *name_size) { + *name_size = rsp_data->name_size; + efi_status = EFI_BUFFER_TOO_SMALL; + goto out_free; + } + + if (rsp_data->guid_size != sizeof(*guid)) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + memcpy(guid, ((void *)rsp_data) + rsp_data->guid_offset, rsp_data->guid_size); + status = ucs2_strscpy(name, ((void *)rsp_data) + rsp_data->name_offset, + rsp_data->name_size / sizeof(*name)); + *name_size = rsp_data->name_size; + + if (status < 0) { + /* + * Return EFI_DEVICE_ERROR here because the buffer size should + * have already been validated above, causing this function to + * bail with EFI_BUFFER_TOO_SMALL. + */ + return EFI_DEVICE_ERROR; + } + +out_free: + kfree(rsp_data); +out_free_req: + kfree(req_data); +out: + return efi_status; +} + +static efi_status_t qsee_uefi_query_variable_info(struct qcuefi_client *qcuefi, u32 attr, + u64 *storage_space, u64 *remaining_space, + u64 *max_variable_size) +{ + struct qsee_req_uefi_query_variable_info *req_data; + struct qsee_rsp_uefi_query_variable_info *rsp_data; + efi_status_t efi_status = EFI_SUCCESS; + int status; + + req_data = kzalloc(sizeof(*req_data), GFP_KERNEL); + if (!req_data) { + efi_status = EFI_OUT_OF_RESOURCES; + goto out; + } + + rsp_data = kzalloc(sizeof(*rsp_data), GFP_KERNEL); + if (!rsp_data) { + efi_status = EFI_OUT_OF_RESOURCES; + goto out_free_req; + } + + req_data->command_id = QSEE_CMD_UEFI_QUERY_VARIABLE_INFO; + req_data->attributes = attr; + req_data->length = sizeof(*req_data); + + status = qcom_qseecom_app_send(qcuefi->client, req_data, sizeof(*req_data), rsp_data, + sizeof(*rsp_data)); + if (status) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->command_id != QSEE_CMD_UEFI_QUERY_VARIABLE_INFO) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->length != sizeof(*rsp_data)) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->status) { + dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n", + __func__, rsp_data->status); + efi_status = qsee_uefi_status_to_efi(rsp_data->status); + goto out_free; + } + + if (storage_space) + *storage_space = rsp_data->storage_space; + + if (remaining_space) + *remaining_space = rsp_data->remaining_space; + + if (max_variable_size) + *max_variable_size = rsp_data->max_variable_size; + +out_free: + kfree(rsp_data); +out_free_req: + kfree(req_data); +out: + return efi_status; +} + +/* -- Global efivar interface. ---------------------------------------------- */ + +static struct qcuefi_client *__qcuefi; +static DEFINE_MUTEX(__qcuefi_lock); + +static int qcuefi_set_reference(struct qcuefi_client *qcuefi) +{ + mutex_lock(&__qcuefi_lock); + + if (qcuefi && __qcuefi) { + mutex_unlock(&__qcuefi_lock); + return -EEXIST; + } + + __qcuefi = qcuefi; + + mutex_unlock(&__qcuefi_lock); + return 0; +} + +static struct qcuefi_client *qcuefi_acquire(void) +{ + mutex_lock(&__qcuefi_lock); + return __qcuefi; +} + +static void qcuefi_release(void) +{ + mutex_unlock(&__qcuefi_lock); +} + +static efi_status_t qcuefi_get_variable(efi_char16_t *name, efi_guid_t *vendor, u32 *attr, + unsigned long *data_size, void *data) +{ + struct qcuefi_client *qcuefi; + efi_status_t status; + + qcuefi = qcuefi_acquire(); + if (!qcuefi) + return EFI_NOT_READY; + + status = qsee_uefi_get_variable(qcuefi, name, vendor, attr, data_size, data); + + qcuefi_release(); + return status; +} + +static efi_status_t qcuefi_set_variable(efi_char16_t *name, efi_guid_t *vendor, + u32 attr, unsigned long data_size, void *data) +{ + struct qcuefi_client *qcuefi; + efi_status_t status; + + qcuefi = qcuefi_acquire(); + if (!qcuefi) + return EFI_NOT_READY; + + status = qsee_uefi_set_variable(qcuefi, name, vendor, attr, data_size, data); + + qcuefi_release(); + return status; +} + +static efi_status_t qcuefi_get_next_variable(unsigned long *name_size, efi_char16_t *name, + efi_guid_t *vendor) +{ + struct qcuefi_client *qcuefi; + efi_status_t status; + + qcuefi = qcuefi_acquire(); + if (!qcuefi) + return EFI_NOT_READY; + + status = qsee_uefi_get_next_variable(qcuefi, name_size, name, vendor); + + qcuefi_release(); + return status; +} + +static efi_status_t qcuefi_query_variable_info(u32 attr, u64 *storage_space, u64 *remaining_space, + u64 *max_variable_size) +{ + struct qcuefi_client *qcuefi; + efi_status_t status; + + qcuefi = qcuefi_acquire(); + if (!qcuefi) + return EFI_NOT_READY; + + status = qsee_uefi_query_variable_info(qcuefi, attr, storage_space, remaining_space, + max_variable_size); + + qcuefi_release(); + return status; +} + +static const struct efivar_operations qcom_efivar_ops = { + .get_variable = qcuefi_get_variable, + .set_variable = qcuefi_set_variable, + .get_next_variable = qcuefi_get_next_variable, + .query_variable_info = qcuefi_query_variable_info, +}; + +/* -- Driver setup. --------------------------------------------------------- */ + +static int qcom_uefisecapp_probe(struct auxiliary_device *aux_dev, + const struct auxiliary_device_id *aux_dev_id) +{ + struct qcuefi_client *qcuefi; + int status; + + qcuefi = devm_kzalloc(&aux_dev->dev, sizeof(*qcuefi), GFP_KERNEL); + if (!qcuefi) + return -ENOMEM; + + qcuefi->client = container_of(aux_dev, struct qseecom_client, aux_dev); + + auxiliary_set_drvdata(aux_dev, qcuefi); + status = qcuefi_set_reference(qcuefi); + if (status) + return status; + + status = efivars_register(&qcuefi->efivars, &qcom_efivar_ops); + if (status) + qcuefi_set_reference(NULL); + + return status; +} + +static void qcom_uefisecapp_remove(struct auxiliary_device *aux_dev) +{ + struct qcuefi_client *qcuefi = auxiliary_get_drvdata(aux_dev); + + efivars_unregister(&qcuefi->efivars); + qcuefi_set_reference(NULL); +} + +static const struct auxiliary_device_id qcom_uefisecapp_id_table[] = { + { .name = "qcom_qseecom.uefisecapp" }, + {} +}; +MODULE_DEVICE_TABLE(auxiliary, qcom_uefisecapp_id_table); + +static struct auxiliary_driver qcom_uefisecapp_driver = { + .probe = qcom_uefisecapp_probe, + .remove = qcom_uefisecapp_remove, + .id_table = qcom_uefisecapp_id_table, + .driver = { + .name = "qcom_qseecom_uefisecapp", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; +module_auxiliary_driver(qcom_uefisecapp_driver); + +MODULE_AUTHOR("Maximilian Luz "); +MODULE_DESCRIPTION("Client driver for Qualcomm SEE UEFI Secure App"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From edc3a1fb626bfb191228fb40a9ddd8806a77eeb9 Mon Sep 17 00:00:00 2001 From: Danila Tikhonov Date: Wed, 13 Sep 2023 21:17:21 +0300 Subject: dt-bindings: arm: qcom,ids: Add Soc ID for SM7150P Add the ID for the Qualcomm SM7150P SoC. Signed-off-by: Danila Tikhonov Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20230913181722.13917-2-danila@jiaxyga.com Signed-off-by: Bjorn Andersson --- include/dt-bindings/arm/qcom,ids.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dt-bindings/arm/qcom,ids.h b/include/dt-bindings/arm/qcom,ids.h index be12e1dd1f38..036124336d17 100644 --- a/include/dt-bindings/arm/qcom,ids.h +++ b/include/dt-bindings/arm/qcom,ids.h @@ -193,6 +193,7 @@ #define QCOM_ID_SDA439 363 #define QCOM_ID_SDA429 364 #define QCOM_ID_SM7150 365 +#define QCOM_ID_SM7150P 366 #define QCOM_ID_IPQ8070 375 #define QCOM_ID_IPQ8071 376 #define QCOM_ID_QM215 386 -- cgit v1.2.3 From 776b29eb57849bd81ece3e82b1b1b76452dde017 Mon Sep 17 00:00:00 2001 From: Danila Tikhonov Date: Wed, 13 Sep 2023 21:17:22 +0300 Subject: soc: qcom: socinfo: Add Soc ID for SM7150P Add Soc ID table entries for Qualcomm SM7150P. Signed-off-by: Danila Tikhonov Link: https://lore.kernel.org/r/20230913181722.13917-3-danila@jiaxyga.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/socinfo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 497cfb720fcb..1f433a941c50 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -349,6 +349,7 @@ static const struct soc_id soc_id[] = { { qcom_board_id(SDA439) }, { qcom_board_id(SDA429) }, { qcom_board_id(SM7150) }, + { qcom_board_id(SM7150P) }, { qcom_board_id(IPQ8070) }, { qcom_board_id(IPQ8071) }, { qcom_board_id(QM215) }, -- cgit v1.2.3 From c2bfe2b7a921906858006d72fd822f6121f6649f Mon Sep 17 00:00:00 2001 From: Li Zetao Date: Tue, 8 Aug 2023 10:14:46 +0800 Subject: soc: qcom: wcnss_ctrl: Remove redundant initialization owner in wcnss_ctrl_driver The module_rpmsg_driver() will set "THIS_MODULE" to driver.owner when register a rpmsg_driver driver, so it is redundant initialization to set driver.owner in the statement. Remove it for clean code. Signed-off-by: Li Zetao Link: https://lore.kernel.org/r/20230808021446.2975843-1-lizetao1@huawei.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/wcnss_ctrl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/soc/qcom/wcnss_ctrl.c b/drivers/soc/qcom/wcnss_ctrl.c index ad9942412c58..2894e214cacf 100644 --- a/drivers/soc/qcom/wcnss_ctrl.c +++ b/drivers/soc/qcom/wcnss_ctrl.c @@ -355,7 +355,6 @@ static struct rpmsg_driver wcnss_ctrl_driver = { .callback = wcnss_ctrl_smd_callback, .drv = { .name = "qcom_wcnss_ctrl", - .owner = THIS_MODULE, .of_match_table = wcnss_ctrl_of_match, }, }; -- cgit v1.2.3 From 3b1eba1882b74c0e9b9f158ee027e309b1df4782 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 30 Aug 2023 04:42:43 +0200 Subject: soc: qcom: socinfo: Add SM8550-adjacent PMICs Many of the PMICs were missing, add some of them often coupled with SM8550. Signed-off-by: Konrad Dybcio Reviewed-by: Neil Armstrong Link: https://lore.kernel.org/r/20230830-topic-pm8550abcxyz-v1-1-3c3ef3d92d51@linaro.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/socinfo.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 1f433a941c50..a5a52e1b6dfc 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -117,6 +117,12 @@ static const char *const pmic_models[] = { [55] = "PM2250", [58] = "PM8450", [65] = "PM8010", + [69] = "PM8550VS", + [70] = "PM8550VE", + [71] = "PM8550B", + [72] = "PMR735D", + [73] = "PM8550", + [74] = "PMK8550", }; struct socinfo_params { -- cgit v1.2.3 From ccfb4d8b606302d857a03ea29039e21029311335 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Wed, 30 Aug 2023 11:58:32 +0200 Subject: dt-bindings: arm: qcom,ids: Add SoC ID for QCM6490 Add the ID for the Qualcomm QCM6490 SoC. Signed-off-by: Luca Weiss Reviewed-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230830-fp5-initial-v1-7-5a954519bbad@fairphone.com Signed-off-by: Bjorn Andersson --- include/dt-bindings/arm/qcom,ids.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dt-bindings/arm/qcom,ids.h b/include/dt-bindings/arm/qcom,ids.h index 036124336d17..ef1d8e16cbf4 100644 --- a/include/dt-bindings/arm/qcom,ids.h +++ b/include/dt-bindings/arm/qcom,ids.h @@ -234,6 +234,7 @@ #define QCOM_ID_SM8450_3 482 #define QCOM_ID_SC7280 487 #define QCOM_ID_SC7180P 495 +#define QCOM_ID_QCM6490 497 #define QCOM_ID_IPQ5000 503 #define QCOM_ID_IPQ0509 504 #define QCOM_ID_IPQ0518 505 -- cgit v1.2.3 From 59872d59d164ec67f295d6f96fe818b92973ee40 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Wed, 30 Aug 2023 11:58:33 +0200 Subject: soc: qcom: socinfo: Add SoC ID for QCM6490 Add SoC ID table entries for Qualcomm QCM6490. Signed-off-by: Luca Weiss Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230830-fp5-initial-v1-8-5a954519bbad@fairphone.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/socinfo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index a5a52e1b6dfc..c2af2d7214c7 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -396,6 +396,7 @@ static const struct soc_id soc_id[] = { { qcom_board_id_named(SM8450_3, "SM8450") }, { qcom_board_id(SC7280) }, { qcom_board_id(SC7180P) }, + { qcom_board_id(QCM6490) }, { qcom_board_id(IPQ5000) }, { qcom_board_id(IPQ0509) }, { qcom_board_id(IPQ0518) }, -- cgit v1.2.3 From 274707b773378f4ce8ba214002b3d67a4d0785ae Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 30 Aug 2023 14:48:41 +0200 Subject: dt-bindings: qcom: geni-se: Allow dma-coherent On SM8550, the QUP controller is coherent with the CPU. Allow specifying that. Signed-off-by: Konrad Dybcio Acked-by: Rob Herring Link: https://lore.kernel.org/r/20230830-topic-8550_dmac2-v1-2-49bb25239fb1@linaro.org Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml index 8a4b7ba3aaf6..7b031ef09669 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml @@ -52,6 +52,8 @@ properties: iommus: maxItems: 1 + dma-coherent: true + required: - compatible - reg -- cgit v1.2.3 From b8c889bef9797a58b8b5aad23875cc4d04b3efd3 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Fri, 1 Sep 2023 20:10:04 +0200 Subject: dt-bindings: arm: qcom,ids: Add IDs for IPQ8174 family IPQ8174 (Oak) family is part of the IPQ8074 family, but the ID-s for it are missing so lets add them. Signed-off-by: Robert Marko Reviewed-by: Kathiravan T Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20230901181041.1538999-1-robimarko@gmail.com Signed-off-by: Bjorn Andersson --- include/dt-bindings/arm/qcom,ids.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/dt-bindings/arm/qcom,ids.h b/include/dt-bindings/arm/qcom,ids.h index ef1d8e16cbf4..f7248348a459 100644 --- a/include/dt-bindings/arm/qcom,ids.h +++ b/include/dt-bindings/arm/qcom,ids.h @@ -204,6 +204,9 @@ #define QCOM_ID_SM6125 394 #define QCOM_ID_IPQ8070A 395 #define QCOM_ID_IPQ8071A 396 +#define QCOM_ID_IPQ8172 397 +#define QCOM_ID_IPQ8173 398 +#define QCOM_ID_IPQ8174 399 #define QCOM_ID_IPQ6018 402 #define QCOM_ID_IPQ6028 403 #define QCOM_ID_SDM429W 416 -- cgit v1.2.3 From e9104e73d4fc8a023608be9c18ee1b897d0ccb14 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Fri, 1 Sep 2023 20:10:05 +0200 Subject: soc: qcom: socinfo: Add IDs for IPQ8174 family IPQ8174 (Oak) family is part of the IPQ8074 family, but the ID-s for it are missing so lets add them. Signed-off-by: Robert Marko Reviewed-by: Kathiravan T Link: https://lore.kernel.org/r/20230901181041.1538999-2-robimarko@gmail.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/socinfo.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index c2af2d7214c7..2a15983d9f60 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -366,6 +366,9 @@ static const struct soc_id soc_id[] = { { qcom_board_id(SM6125) }, { qcom_board_id(IPQ8070A) }, { qcom_board_id(IPQ8071A) }, + { qcom_board_id(IPQ8172) }, + { qcom_board_id(IPQ8173) }, + { qcom_board_id(IPQ8174) }, { qcom_board_id(IPQ6018) }, { qcom_board_id(IPQ6028) }, { qcom_board_id(SDM429W) }, -- cgit v1.2.3 From 2758ac3a11d78af56e6969af04dec611806a62de Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 13 Sep 2023 21:28:25 +0200 Subject: firmware: qcom-scm: drop unneeded 'extern' specifiers The 'extern' specifier in front of a function declaration has no effect. Remove all of them from the qcom-scm header. Signed-off-by: Bartosz Golaszewski Reviewed-by: Krzysztof Kozlowski Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230913192826.36187-1-bartosz.golaszewski@linaro.org Signed-off-by: Bjorn Andersson --- include/linux/firmware/qcom/qcom_scm.h | 101 +++++++++++++++------------------ 1 file changed, 47 insertions(+), 54 deletions(-) diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h index e1ea2eb56d04..ccaf28846054 100644 --- a/include/linux/firmware/qcom/qcom_scm.h +++ b/include/linux/firmware/qcom/qcom_scm.h @@ -59,12 +59,12 @@ enum qcom_scm_ice_cipher { #define QCOM_SCM_PERM_RW (QCOM_SCM_PERM_READ | QCOM_SCM_PERM_WRITE) #define QCOM_SCM_PERM_RWX (QCOM_SCM_PERM_RW | QCOM_SCM_PERM_EXEC) -extern bool qcom_scm_is_available(void); +bool qcom_scm_is_available(void); -extern int qcom_scm_set_cold_boot_addr(void *entry); -extern int qcom_scm_set_warm_boot_addr(void *entry); -extern void qcom_scm_cpu_power_down(u32 flags); -extern int qcom_scm_set_remote_state(u32 state, u32 id); +int qcom_scm_set_cold_boot_addr(void *entry); +int qcom_scm_set_warm_boot_addr(void *entry); +void qcom_scm_cpu_power_down(u32 flags); +int qcom_scm_set_remote_state(u32 state, u32 id); struct qcom_scm_pas_metadata { void *ptr; @@ -72,55 +72,48 @@ struct qcom_scm_pas_metadata { ssize_t size; }; -extern int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, - size_t size, - struct qcom_scm_pas_metadata *ctx); -extern void qcom_scm_pas_metadata_release(struct qcom_scm_pas_metadata *ctx); -extern int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, - phys_addr_t size); -extern int qcom_scm_pas_auth_and_reset(u32 peripheral); -extern int qcom_scm_pas_shutdown(u32 peripheral); -extern bool qcom_scm_pas_supported(u32 peripheral); - -extern int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val); -extern int qcom_scm_io_writel(phys_addr_t addr, unsigned int val); - -extern bool qcom_scm_restore_sec_cfg_available(void); -extern int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare); -extern int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size); -extern int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare); -extern int qcom_scm_iommu_set_cp_pool_size(u32 spare, u32 size); -extern int qcom_scm_mem_protect_video_var(u32 cp_start, u32 cp_size, - u32 cp_nonpixel_start, - u32 cp_nonpixel_size); -extern int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, - u64 *src, - const struct qcom_scm_vmperm *newvm, - unsigned int dest_cnt); - -extern bool qcom_scm_ocmem_lock_available(void); -extern int qcom_scm_ocmem_lock(enum qcom_scm_ocmem_client id, u32 offset, - u32 size, u32 mode); -extern int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset, - u32 size); - -extern bool qcom_scm_ice_available(void); -extern int qcom_scm_ice_invalidate_key(u32 index); -extern int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size, - enum qcom_scm_ice_cipher cipher, - u32 data_unit_size); - -extern bool qcom_scm_hdcp_available(void); -extern int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, - u32 *resp); - -extern int qcom_scm_iommu_set_pt_format(u32 sec_id, u32 ctx_num, u32 pt_fmt); -extern int qcom_scm_qsmmu500_wait_safe_toggle(bool en); - -extern int qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val, - u64 limit_node, u32 node_id, u64 version); -extern int qcom_scm_lmh_profile_change(u32 profile_id); -extern bool qcom_scm_lmh_dcvsh_available(void); +int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size, + struct qcom_scm_pas_metadata *ctx); +void qcom_scm_pas_metadata_release(struct qcom_scm_pas_metadata *ctx); +int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size); +int qcom_scm_pas_auth_and_reset(u32 peripheral); +int qcom_scm_pas_shutdown(u32 peripheral); +bool qcom_scm_pas_supported(u32 peripheral); + +int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val); +int qcom_scm_io_writel(phys_addr_t addr, unsigned int val); + +bool qcom_scm_restore_sec_cfg_available(void); +int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare); +int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size); +int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare); +int qcom_scm_iommu_set_cp_pool_size(u32 spare, u32 size); +int qcom_scm_mem_protect_video_var(u32 cp_start, u32 cp_size, + u32 cp_nonpixel_start, u32 cp_nonpixel_size); +int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, u64 *src, + const struct qcom_scm_vmperm *newvm, + unsigned int dest_cnt); + +bool qcom_scm_ocmem_lock_available(void); +int qcom_scm_ocmem_lock(enum qcom_scm_ocmem_client id, u32 offset, u32 size, + u32 mode); +int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset, u32 size); + +bool qcom_scm_ice_available(void); +int qcom_scm_ice_invalidate_key(u32 index); +int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size, + enum qcom_scm_ice_cipher cipher, u32 data_unit_size); + +bool qcom_scm_hdcp_available(void); +int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp); + +int qcom_scm_iommu_set_pt_format(u32 sec_id, u32 ctx_num, u32 pt_fmt); +int qcom_scm_qsmmu500_wait_safe_toggle(bool en); + +int qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val, + u64 limit_node, u32 node_id, u64 version); +int qcom_scm_lmh_profile_change(u32 profile_id); +bool qcom_scm_lmh_dcvsh_available(void); #ifdef CONFIG_QCOM_QSEECOM -- cgit v1.2.3 From bc7fbb5ea701b22c09c0fa5acbc122207283366a Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 13 Sep 2023 21:28:26 +0200 Subject: firmware: qcom-scm: order includes alphabetically For easier maintenance order the included headers in qcom_scm.c alphabetically. Signed-off-by: Bartosz Golaszewski Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230913192826.36187-2-bartosz.golaszewski@linaro.org Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom_scm.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index f9d5c31b8ec7..ca52618e2a8d 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -2,24 +2,25 @@ /* Copyright (c) 2010,2015,2019 The Linux Foundation. All rights reserved. * Copyright (C) 2015 Linaro Ltd. */ -#include -#include -#include + +#include +#include #include #include -#include #include +#include +#include +#include #include +#include #include -#include -#include #include #include #include #include -#include +#include #include -#include +#include #include "qcom_scm.h" -- cgit v1.2.3 From d74612b6da61aeb32e81bcf762b8be4e3c41bda5 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Fri, 15 Sep 2023 15:50:14 -0700 Subject: firmware: qcom: qseecom: Add missing AUXILIARY_BUS dependency The newly introduced QSEECOM driver fail to link if the system is built without CONFIG_AUXILIARY_BUS, make sure it is selected. Fixes: 00b1248606ba ("firmware: qcom_scm: Add support for Qualcomm Secure Execution Environment SCM interface") Reported-by: Randy Dunlap Closes: https://lore.kernel.org/r/9f156fa6-e5aa-4cb2-ab2b-b67fd8fc4840%40infradead.org Signed-off-by: Bjorn Andersson Acked-by: Randy Dunlap Tested-by: Randy Dunlap Link: https://lore.kernel.org/r/20230915-qseecom-auxiliary-fix-v1-1-38a46cfbfdb0@quicinc.com Signed-off-by: Bjorn Andersson --- drivers/firmware/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index a94202229a71..8c608be30060 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -229,6 +229,7 @@ config QCOM_SCM_DOWNLOAD_MODE_DEFAULT config QCOM_QSEECOM bool "Qualcomm QSEECOM interface driver" depends on QCOM_SCM=y + select AUXILIARY_BUS help Various Qualcomm SoCs have a Secure Execution Environment (SEE) running in the Trust Zone. This module provides an interface to that via the -- cgit v1.2.3 From 0b9729cdb895a477ba3551cd2102baee2e697cbb Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 12 Sep 2023 07:51:24 +0300 Subject: soc: renesas: Identify RZ/G3S SoC Add support to identify the RZ/G3S (R9A08G045) SoC. Signed-off-by: Claudiu Beznea Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20230912045157.177966-5-claudiu.beznea.uj@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/soc/renesas/Kconfig | 6 ++++++ drivers/soc/renesas/renesas-soc.c | 13 +++++++++++++ 2 files changed, 19 insertions(+) diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 5a75ab64d1ed..631b17c627a8 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -319,6 +319,12 @@ config ARCH_R9A07G054 help This enables support for the Renesas RZ/V2L SoC variants. +config ARCH_R9A08G045 + bool "ARM64 Platform support for RZ/G3S" + select ARCH_RZG2L + help + This enables support for the Renesas RZ/G3S SoC variants. + config ARCH_R9A09G011 bool "ARM64 Platform support for RZ/V2M" select PM diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index 42af7c09f743..1598b66ffb51 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -72,6 +72,10 @@ static const struct renesas_family fam_rzg2ul __initconst __maybe_unused = { .name = "RZ/G2UL", }; +static const struct renesas_family fam_rzg3s __initconst __maybe_unused = { + .name = "RZ/G3S", +}; + static const struct renesas_family fam_rzv2l __initconst __maybe_unused = { .name = "RZ/V2L", }; @@ -170,6 +174,11 @@ static const struct renesas_soc soc_rz_g2ul __initconst __maybe_unused = { .id = 0x8450447, }; +static const struct renesas_soc soc_rz_g3s __initconst __maybe_unused = { + .family = &fam_rzg3s, + .id = 0x85e0447, +}; + static const struct renesas_soc soc_rz_v2l __initconst __maybe_unused = { .family = &fam_rzv2l, .id = 0x8447447, @@ -386,6 +395,9 @@ static const struct of_device_id renesas_socs[] __initconst __maybe_unused = { #ifdef CONFIG_ARCH_R9A07G054 { .compatible = "renesas,r9a07g054", .data = &soc_rz_v2l }, #endif +#ifdef CONFIG_ARCH_R9A08G045 + { .compatible = "renesas,r9a08g045", .data = &soc_rz_g3s }, +#endif #ifdef CONFIG_ARCH_R9A09G011 { .compatible = "renesas,r9a09g011", .data = &soc_rz_v2m }, #endif @@ -429,6 +441,7 @@ static const struct of_device_id renesas_ids[] __initconst = { { .compatible = "renesas,r9a07g043-sysc", .data = &id_rzg2l }, { .compatible = "renesas,r9a07g044-sysc", .data = &id_rzg2l }, { .compatible = "renesas,r9a07g054-sysc", .data = &id_rzg2l }, + { .compatible = "renesas,r9a08g045-sysc", .data = &id_rzg2l }, { .compatible = "renesas,r9a09g011-sys", .data = &id_rzv2m }, { .compatible = "renesas,prr", .data = &id_prr }, { /* sentinel */ } -- cgit v1.2.3 From 3655167afeaa77ba2a354b826d0770e9b2aa88b0 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 12 Sep 2023 07:51:25 +0300 Subject: soc: renesas: renesas-soc: Remove blank lines Remove blank lines. Signed-off-by: Claudiu Beznea Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20230912045157.177966-6-claudiu.beznea.uj@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/soc/renesas/renesas-soc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index 1598b66ffb51..c732d4a5b26a 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -12,7 +12,6 @@ #include #include - struct renesas_family { const char name[16]; u32 reg; /* CCCR or PRR, if not in DT */ @@ -89,7 +88,6 @@ static const struct renesas_family fam_shmobile __initconst __maybe_unused = { .reg = 0xe600101c, /* CCCR (Common Chip Code Register) */ }; - struct renesas_soc { const struct renesas_family *family; u32 id; -- cgit v1.2.3 From 61ad21ec519774182d23dd83d6cb44d7b80b7421 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 15 Sep 2023 10:58:05 +0200 Subject: soc: renesas: Kconfig: Remove blank line before ARCH_R9A07G043 help text There should not be a blank line before the "help" keyword in the ARCH_R9A07G043 configuration section. Fixes: c6a906cce61a8015 ("soc: renesas: Kconfig: For ARCH_R9A07G043 select the required configs if dependencies are met") Signed-off-by: Geert Uytterhoeven Reviewed-by: Lad Prabhakar Link: https://lore.kernel.org/r/2a3a9d595579b7cea416c12bf9abcfa2227243bb.1694768174.git.geert+renesas@glider.be --- drivers/soc/renesas/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 631b17c627a8..309039710614 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -344,7 +344,6 @@ config ARCH_R9A07G043 select DMA_GLOBAL_POOL select ERRATA_ANDES if RISCV_SBI select ERRATA_ANDES_CMO if ERRATA_ANDES - help This enables support for the Renesas RZ/Five SoC. -- cgit v1.2.3 From abc91aaa1937faaea3b385745956f3aeaae0b605 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Mon, 18 Sep 2023 10:33:32 +0100 Subject: soc: sifive: replace SOC_FOO with ARCH_FOO As part of unifiying what RISC-V calls microarch symbols to use ARCH_, replace users of the SOC_ forms. Signed-off-by: Conor Dooley --- drivers/soc/sifive/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/sifive/Kconfig b/drivers/soc/sifive/Kconfig index e86870be34c9..139884addc41 100644 --- a/drivers/soc/sifive/Kconfig +++ b/drivers/soc/sifive/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -if SOC_SIFIVE || SOC_STARFIVE +if ARCH_SIFIVE || ARCH_STARFIVE config SIFIVE_CCACHE bool "Sifive Composable Cache controller" -- cgit v1.2.3 From 5692aeea5bcb9331e956628c3bc8fc9afcc9765d Mon Sep 17 00:00:00 2001 From: Lu Hongfei Date: Mon, 12 Jun 2023 21:34:52 +0800 Subject: soc: qcom: pmic: Fix resource leaks in a device_for_each_child_node() loop The device_for_each_child_node loop should call fwnode_handle_put() before return in the error cases, to avoid resource leaks. Let's fix this bug in pmic_glink_altmode_probe(). Signed-off-by: Lu Hongfei Link: https://lore.kernel.org/r/20230612133452.47315-1-luhongfei@vivo.com [bjorn: Rebased patch, moved fw_handle_put() from jump target into the loop] Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/pmic_glink_altmode.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/drivers/soc/qcom/pmic_glink_altmode.c b/drivers/soc/qcom/pmic_glink_altmode.c index d05e0d6edf49..9569d999391d 100644 --- a/drivers/soc/qcom/pmic_glink_altmode.c +++ b/drivers/soc/qcom/pmic_glink_altmode.c @@ -444,6 +444,7 @@ static int pmic_glink_altmode_probe(struct auxiliary_device *adev, ret = fwnode_property_read_u32(fwnode, "reg", &port); if (ret < 0) { dev_err(dev, "missing reg property of %pOFn\n", fwnode); + fwnode_handle_put(fwnode); return ret; } @@ -454,6 +455,7 @@ static int pmic_glink_altmode_probe(struct auxiliary_device *adev, if (altmode->ports[port].altmode) { dev_err(dev, "multiple connector definition for port %u\n", port); + fwnode_handle_put(fwnode); return -EINVAL; } @@ -468,45 +470,59 @@ static int pmic_glink_altmode_probe(struct auxiliary_device *adev, alt_port->bridge.type = DRM_MODE_CONNECTOR_USB; ret = devm_drm_bridge_add(dev, &alt_port->bridge); - if (ret) + if (ret) { + fwnode_handle_put(fwnode); return ret; + } alt_port->dp_alt.svid = USB_TYPEC_DP_SID; alt_port->dp_alt.mode = USB_TYPEC_DP_MODE; alt_port->dp_alt.active = 1; alt_port->typec_mux = fwnode_typec_mux_get(fwnode); - if (IS_ERR(alt_port->typec_mux)) + if (IS_ERR(alt_port->typec_mux)) { + fwnode_handle_put(fwnode); return dev_err_probe(dev, PTR_ERR(alt_port->typec_mux), "failed to acquire mode-switch for port: %d\n", port); + } ret = devm_add_action_or_reset(dev, pmic_glink_altmode_put_mux, alt_port->typec_mux); - if (ret) + if (ret) { + fwnode_handle_put(fwnode); return ret; + } alt_port->typec_retimer = fwnode_typec_retimer_get(fwnode); - if (IS_ERR(alt_port->typec_retimer)) + if (IS_ERR(alt_port->typec_retimer)) { + fwnode_handle_put(fwnode); return dev_err_probe(dev, PTR_ERR(alt_port->typec_retimer), "failed to acquire retimer-switch for port: %d\n", port); + } ret = devm_add_action_or_reset(dev, pmic_glink_altmode_put_retimer, alt_port->typec_retimer); - if (ret) + if (ret) { + fwnode_handle_put(fwnode); return ret; + } alt_port->typec_switch = fwnode_typec_switch_get(fwnode); - if (IS_ERR(alt_port->typec_switch)) + if (IS_ERR(alt_port->typec_switch)) { + fwnode_handle_put(fwnode); return dev_err_probe(dev, PTR_ERR(alt_port->typec_switch), "failed to acquire orientation-switch for port: %d\n", port); + } ret = devm_add_action_or_reset(dev, pmic_glink_altmode_put_switch, alt_port->typec_switch); - if (ret) + if (ret) { + fwnode_handle_put(fwnode); return ret; + } } altmode->client = devm_pmic_glink_register_client(dev, -- cgit v1.2.3 From 8e2506d0123149a7b7846fbabbf4295b6005faf4 Mon Sep 17 00:00:00 2001 From: Komal Bajaj Date: Wed, 30 Aug 2023 16:26:49 +0530 Subject: dt-bindings: cache: qcom,llcc: Add LLCC compatible for QDU1000/QRU1000 Add LLCC compatible for QDU1000/QRU1000 SoCs and add optional nvmem-cells and nvmem-cell-names properties to support multiple configurations for multi channel DDR. Signed-off-by: Komal Bajaj Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230830105654.28057-2-quic_kbajaj@quicinc.com Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/cache/qcom,llcc.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/devicetree/bindings/cache/qcom,llcc.yaml b/Documentation/devicetree/bindings/cache/qcom,llcc.yaml index 44892aa589fd..580f9a97ddf7 100644 --- a/Documentation/devicetree/bindings/cache/qcom,llcc.yaml +++ b/Documentation/devicetree/bindings/cache/qcom,llcc.yaml @@ -20,6 +20,7 @@ description: | properties: compatible: enum: + - qcom,qdu1000-llcc - qcom,sc7180-llcc - qcom,sc7280-llcc - qcom,sc8180x-llcc @@ -44,6 +45,14 @@ properties: interrupts: maxItems: 1 + nvmem-cells: + items: + - description: Reference to an nvmem node for multi channel DDR + + nvmem-cell-names: + items: + - const: multi-chan-ddr + required: - compatible - reg @@ -92,6 +101,7 @@ allOf: compatible: contains: enum: + - qcom,qdu1000-llcc - qcom,sc8180x-llcc - qcom,sc8280xp-llcc then: -- cgit v1.2.3 From 16fa93112f26a7a151f3d86a2a9223c564f6e3bf Mon Sep 17 00:00:00 2001 From: Komal Bajaj Date: Wed, 30 Aug 2023 16:26:50 +0530 Subject: soc: qcom: llcc: Refactor llcc driver to support multiple configuration Refactor driver to support multiple configuration for llcc on a target. Signed-off-by: Komal Bajaj Fixes: ee13b5008707 ("qcom: llcc/edac: Fix the base address used for accessing LLCC banks") Reviewed-by: Mukesh Ojha Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230830105654.28057-3-quic_kbajaj@quicinc.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/llcc-qcom.c | 267 +++++++++++++++++++++++++++++-------------- 1 file changed, 181 insertions(+), 86 deletions(-) diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index e32a4161a8d0..cbef8d825361 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -126,6 +126,11 @@ struct qcom_llcc_config { bool no_edac; }; +struct qcom_sct_config { + const struct qcom_llcc_config *llcc_config; + int num_config; +}; + enum llcc_reg_offset { LLCC_COMMON_HW_INFO, LLCC_COMMON_STATUS0, @@ -422,101 +427,185 @@ static const u32 llcc_v2_1_reg_offset[] = { [LLCC_COMMON_STATUS0] = 0x0003400c, }; -static const struct qcom_llcc_config sc7180_cfg = { - .sct_data = sc7180_data, - .size = ARRAY_SIZE(sc7180_data), - .need_llcc_cfg = true, - .reg_offset = llcc_v1_reg_offset, - .edac_reg_offset = &llcc_v1_edac_reg_offset, +static const struct qcom_llcc_config sc7180_cfg[] = { + { + .sct_data = sc7180_data, + .size = ARRAY_SIZE(sc7180_data), + .need_llcc_cfg = true, + .reg_offset = llcc_v1_reg_offset, + .edac_reg_offset = &llcc_v1_edac_reg_offset, + }, +}; + +static const struct qcom_llcc_config sc7280_cfg[] = { + { + .sct_data = sc7280_data, + .size = ARRAY_SIZE(sc7280_data), + .need_llcc_cfg = true, + .reg_offset = llcc_v1_reg_offset, + .edac_reg_offset = &llcc_v1_edac_reg_offset, + }, +}; + +static const struct qcom_llcc_config sc8180x_cfg[] = { + { + .sct_data = sc8180x_data, + .size = ARRAY_SIZE(sc8180x_data), + .need_llcc_cfg = true, + .reg_offset = llcc_v1_reg_offset, + .edac_reg_offset = &llcc_v1_edac_reg_offset, + }, +}; + +static const struct qcom_llcc_config sc8280xp_cfg[] = { + { + .sct_data = sc8280xp_data, + .size = ARRAY_SIZE(sc8280xp_data), + .need_llcc_cfg = true, + .reg_offset = llcc_v1_reg_offset, + .edac_reg_offset = &llcc_v1_edac_reg_offset, + }, +}; + +static const struct qcom_llcc_config sdm845_cfg[] = { + { + .sct_data = sdm845_data, + .size = ARRAY_SIZE(sdm845_data), + .need_llcc_cfg = false, + .reg_offset = llcc_v1_reg_offset, + .edac_reg_offset = &llcc_v1_edac_reg_offset, + .no_edac = true, + }, +}; + +static const struct qcom_llcc_config sm6350_cfg[] = { + { + .sct_data = sm6350_data, + .size = ARRAY_SIZE(sm6350_data), + .need_llcc_cfg = true, + .reg_offset = llcc_v1_reg_offset, + .edac_reg_offset = &llcc_v1_edac_reg_offset, + }, +}; + +static const struct qcom_llcc_config sm7150_cfg[] = { + { + .sct_data = sm7150_data, + .size = ARRAY_SIZE(sm7150_data), + .need_llcc_cfg = true, + .reg_offset = llcc_v1_reg_offset, + .edac_reg_offset = &llcc_v1_edac_reg_offset, + }, +}; + +static const struct qcom_llcc_config sm8150_cfg[] = { + { + .sct_data = sm8150_data, + .size = ARRAY_SIZE(sm8150_data), + .need_llcc_cfg = true, + .reg_offset = llcc_v1_reg_offset, + .edac_reg_offset = &llcc_v1_edac_reg_offset, + }, +}; + +static const struct qcom_llcc_config sm8250_cfg[] = { + { + .sct_data = sm8250_data, + .size = ARRAY_SIZE(sm8250_data), + .need_llcc_cfg = true, + .reg_offset = llcc_v1_reg_offset, + .edac_reg_offset = &llcc_v1_edac_reg_offset, + }, +}; + +static const struct qcom_llcc_config sm8350_cfg[] = { + { + .sct_data = sm8350_data, + .size = ARRAY_SIZE(sm8350_data), + .need_llcc_cfg = true, + .reg_offset = llcc_v1_reg_offset, + .edac_reg_offset = &llcc_v1_edac_reg_offset, + }, }; -static const struct qcom_llcc_config sc7280_cfg = { - .sct_data = sc7280_data, - .size = ARRAY_SIZE(sc7280_data), - .need_llcc_cfg = true, - .reg_offset = llcc_v1_reg_offset, - .edac_reg_offset = &llcc_v1_edac_reg_offset, +static const struct qcom_llcc_config sm8450_cfg[] = { + { + .sct_data = sm8450_data, + .size = ARRAY_SIZE(sm8450_data), + .need_llcc_cfg = true, + .reg_offset = llcc_v2_1_reg_offset, + .edac_reg_offset = &llcc_v2_1_edac_reg_offset, + }, +}; + +static const struct qcom_llcc_config sm8550_cfg[] = { + { + .sct_data = sm8550_data, + .size = ARRAY_SIZE(sm8550_data), + .need_llcc_cfg = true, + .reg_offset = llcc_v2_1_reg_offset, + .edac_reg_offset = &llcc_v2_1_edac_reg_offset, + }, +}; + +static const struct qcom_sct_config sc7180_cfgs = { + .llcc_config = sc7180_cfg, + .num_config = ARRAY_SIZE(sc7180_cfg), +}; + +static const struct qcom_sct_config sc7280_cfgs = { + .llcc_config = sc7280_cfg, + .num_config = ARRAY_SIZE(sc7280_cfg), }; -static const struct qcom_llcc_config sc8180x_cfg = { - .sct_data = sc8180x_data, - .size = ARRAY_SIZE(sc8180x_data), - .need_llcc_cfg = true, - .reg_offset = llcc_v1_reg_offset, - .edac_reg_offset = &llcc_v1_edac_reg_offset, +static const struct qcom_sct_config sc8180x_cfgs = { + .llcc_config = sc8180x_cfg, + .num_config = ARRAY_SIZE(sc8180x_cfg), }; -static const struct qcom_llcc_config sc8280xp_cfg = { - .sct_data = sc8280xp_data, - .size = ARRAY_SIZE(sc8280xp_data), - .need_llcc_cfg = true, - .reg_offset = llcc_v1_reg_offset, - .edac_reg_offset = &llcc_v1_edac_reg_offset, +static const struct qcom_sct_config sc8280xp_cfgs = { + .llcc_config = sc8280xp_cfg, + .num_config = ARRAY_SIZE(sc8280xp_cfg), }; -static const struct qcom_llcc_config sdm845_cfg = { - .sct_data = sdm845_data, - .size = ARRAY_SIZE(sdm845_data), - .need_llcc_cfg = false, - .reg_offset = llcc_v1_reg_offset, - .edac_reg_offset = &llcc_v1_edac_reg_offset, - .no_edac = true, +static const struct qcom_sct_config sdm845_cfgs = { + .llcc_config = sdm845_cfg, + .num_config = ARRAY_SIZE(sdm845_cfg), }; -static const struct qcom_llcc_config sm6350_cfg = { - .sct_data = sm6350_data, - .size = ARRAY_SIZE(sm6350_data), - .need_llcc_cfg = true, - .reg_offset = llcc_v1_reg_offset, - .edac_reg_offset = &llcc_v1_edac_reg_offset, +static const struct qcom_sct_config sm6350_cfgs = { + .llcc_config = sm6350_cfg, + .num_config = ARRAY_SIZE(sm6350_cfg), }; -static const struct qcom_llcc_config sm7150_cfg = { - .sct_data = sm7150_data, - .size = ARRAY_SIZE(sm7150_data), - .need_llcc_cfg = true, - .reg_offset = llcc_v1_reg_offset, - .edac_reg_offset = &llcc_v1_edac_reg_offset, +static const struct qcom_sct_config sm7150_cfgs = { + .llcc_config = sm7150_cfg, + .num_config = ARRAY_SIZE(sm7150_cfg), }; -static const struct qcom_llcc_config sm8150_cfg = { - .sct_data = sm8150_data, - .size = ARRAY_SIZE(sm8150_data), - .need_llcc_cfg = true, - .reg_offset = llcc_v1_reg_offset, - .edac_reg_offset = &llcc_v1_edac_reg_offset, +static const struct qcom_sct_config sm8150_cfgs = { + .llcc_config = sm8150_cfg, + .num_config = ARRAY_SIZE(sm8150_cfg), }; -static const struct qcom_llcc_config sm8250_cfg = { - .sct_data = sm8250_data, - .size = ARRAY_SIZE(sm8250_data), - .need_llcc_cfg = true, - .reg_offset = llcc_v1_reg_offset, - .edac_reg_offset = &llcc_v1_edac_reg_offset, +static const struct qcom_sct_config sm8250_cfgs = { + .llcc_config = sm8250_cfg, + .num_config = ARRAY_SIZE(sm8250_cfg), }; -static const struct qcom_llcc_config sm8350_cfg = { - .sct_data = sm8350_data, - .size = ARRAY_SIZE(sm8350_data), - .need_llcc_cfg = true, - .reg_offset = llcc_v1_reg_offset, - .edac_reg_offset = &llcc_v1_edac_reg_offset, +static const struct qcom_sct_config sm8350_cfgs = { + .llcc_config = sm8350_cfg, + .num_config = ARRAY_SIZE(sm8350_cfg), }; -static const struct qcom_llcc_config sm8450_cfg = { - .sct_data = sm8450_data, - .size = ARRAY_SIZE(sm8450_data), - .need_llcc_cfg = true, - .reg_offset = llcc_v2_1_reg_offset, - .edac_reg_offset = &llcc_v2_1_edac_reg_offset, +static const struct qcom_sct_config sm8450_cfgs = { + .llcc_config = sm8450_cfg, + .num_config = ARRAY_SIZE(sm8450_cfg), }; -static const struct qcom_llcc_config sm8550_cfg = { - .sct_data = sm8550_data, - .size = ARRAY_SIZE(sm8550_data), - .need_llcc_cfg = true, - .reg_offset = llcc_v2_1_reg_offset, - .edac_reg_offset = &llcc_v2_1_edac_reg_offset, +static const struct qcom_sct_config sm8550_cfgs = { + .llcc_config = sm8550_cfg, + .num_config = ARRAY_SIZE(sm8550_cfg), }; static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER; @@ -938,6 +1027,7 @@ static int qcom_llcc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; int ret, i; struct platform_device *llcc_edac; + const struct qcom_sct_config *cfgs; const struct qcom_llcc_config *cfg; const struct llcc_slice_config *llcc_cfg; u32 sz; @@ -957,7 +1047,12 @@ static int qcom_llcc_probe(struct platform_device *pdev) goto err; } - cfg = of_device_get_match_data(&pdev->dev); + cfgs = of_device_get_match_data(&pdev->dev); + if (!cfgs) { + ret = -EINVAL; + goto err; + } + cfg = &cfgs->llcc_config[0]; ret = regmap_read(regmap, cfg->reg_offset[LLCC_COMMON_STATUS0], &num_banks); if (ret) @@ -1050,18 +1145,18 @@ err: } static const struct of_device_id qcom_llcc_of_match[] = { - { .compatible = "qcom,sc7180-llcc", .data = &sc7180_cfg }, - { .compatible = "qcom,sc7280-llcc", .data = &sc7280_cfg }, - { .compatible = "qcom,sc8180x-llcc", .data = &sc8180x_cfg }, - { .compatible = "qcom,sc8280xp-llcc", .data = &sc8280xp_cfg }, - { .compatible = "qcom,sdm845-llcc", .data = &sdm845_cfg }, - { .compatible = "qcom,sm6350-llcc", .data = &sm6350_cfg }, - { .compatible = "qcom,sm7150-llcc", .data = &sm7150_cfg }, - { .compatible = "qcom,sm8150-llcc", .data = &sm8150_cfg }, - { .compatible = "qcom,sm8250-llcc", .data = &sm8250_cfg }, - { .compatible = "qcom,sm8350-llcc", .data = &sm8350_cfg }, - { .compatible = "qcom,sm8450-llcc", .data = &sm8450_cfg }, - { .compatible = "qcom,sm8550-llcc", .data = &sm8550_cfg }, + { .compatible = "qcom,sc7180-llcc", .data = &sc7180_cfgs }, + { .compatible = "qcom,sc7280-llcc", .data = &sc7280_cfgs }, + { .compatible = "qcom,sc8180x-llcc", .data = &sc8180x_cfgs }, + { .compatible = "qcom,sc8280xp-llcc", .data = &sc8280xp_cfgs }, + { .compatible = "qcom,sdm845-llcc", .data = &sdm845_cfgs }, + { .compatible = "qcom,sm6350-llcc", .data = &sm6350_cfgs }, + { .compatible = "qcom,sm7150-llcc", .data = &sm7150_cfgs }, + { .compatible = "qcom,sm8150-llcc", .data = &sm8150_cfgs }, + { .compatible = "qcom,sm8250-llcc", .data = &sm8250_cfgs }, + { .compatible = "qcom,sm8350-llcc", .data = &sm8350_cfgs }, + { .compatible = "qcom,sm8450-llcc", .data = &sm8450_cfgs }, + { .compatible = "qcom,sm8550-llcc", .data = &sm8550_cfgs }, { } }; MODULE_DEVICE_TABLE(of, qcom_llcc_of_match); -- cgit v1.2.3 From 54e1f99d91405417b3ddb6050cfba82733c3aa41 Mon Sep 17 00:00:00 2001 From: Komal Bajaj Date: Wed, 30 Aug 2023 16:26:51 +0530 Subject: nvmem: core: Add stub for nvmem_cell_read_u8 Add the stub nvmem_cell_read_u8() function for drivers running with CONFIG_NVMEM disabled. Signed-off-by: Komal Bajaj Reviewed-by: Mukesh Ojha Link: https://lore.kernel.org/r/20230830105654.28057-4-quic_kbajaj@quicinc.com Signed-off-by: Bjorn Andersson --- include/linux/nvmem-consumer.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/linux/nvmem-consumer.h b/include/linux/nvmem-consumer.h index 4523e4e83319..6ec4b9743e25 100644 --- a/include/linux/nvmem-consumer.h +++ b/include/linux/nvmem-consumer.h @@ -127,6 +127,12 @@ static inline int nvmem_cell_write(struct nvmem_cell *cell, return -EOPNOTSUPP; } +static inline int nvmem_cell_read_u8(struct device *dev, + const char *cell_id, u8 *val) +{ + return -EOPNOTSUPP; +} + static inline int nvmem_cell_read_u16(struct device *dev, const char *cell_id, u16 *val) { -- cgit v1.2.3 From a78502a4b2201235d93b04ac2902e01e32588bd5 Mon Sep 17 00:00:00 2001 From: Komal Bajaj Date: Wed, 30 Aug 2023 16:26:52 +0530 Subject: soc: qcom: Add LLCC support for multi channel DDR Add LLCC support for multi channel DDR configuration based on a feature register. Signed-off-by: Komal Bajaj Acked-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230830105654.28057-5-quic_kbajaj@quicinc.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/llcc-qcom.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index cbef8d825361..c31d9e39e864 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -995,6 +996,24 @@ static int qcom_llcc_cfg_program(struct platform_device *pdev, return ret; } +static int qcom_llcc_get_cfg_index(struct platform_device *pdev, u8 *cfg_index, int num_config) +{ + int ret; + + ret = nvmem_cell_read_u8(&pdev->dev, "multi-chan-ddr", cfg_index); + if (ret == -ENOENT || ret == -EOPNOTSUPP) { + if (num_config > 1) + return -EINVAL; + *cfg_index = 0; + return 0; + } + + if (!ret && *cfg_index >= num_config) + ret = -EINVAL; + + return ret; +} + static int qcom_llcc_remove(struct platform_device *pdev) { /* Set the global pointer to a error code to avoid referencing it */ @@ -1031,6 +1050,7 @@ static int qcom_llcc_probe(struct platform_device *pdev) const struct qcom_llcc_config *cfg; const struct llcc_slice_config *llcc_cfg; u32 sz; + u8 cfg_index; u32 version; struct regmap *regmap; @@ -1052,7 +1072,10 @@ static int qcom_llcc_probe(struct platform_device *pdev) ret = -EINVAL; goto err; } - cfg = &cfgs->llcc_config[0]; + ret = qcom_llcc_get_cfg_index(pdev, &cfg_index, cfgs->num_config); + if (ret) + goto err; + cfg = &cfgs->llcc_config[cfg_index]; ret = regmap_read(regmap, cfg->reg_offset[LLCC_COMMON_STATUS0], &num_banks); if (ret) -- cgit v1.2.3 From 0bc76be64e80b15b975345b6957a87a1893c34f2 Mon Sep 17 00:00:00 2001 From: Komal Bajaj Date: Wed, 30 Aug 2023 16:26:53 +0530 Subject: soc: qcom: llcc: Updating the macro name Update macro name for LLCC_DRE to LLCC_ECC as per the latest specification. Signed-off-by: Komal Bajaj Reviewed-by: Mukesh Ojha Acked-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230830105654.28057-6-quic_kbajaj@quicinc.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/llcc-qcom.c | 2 +- include/linux/soc/qcom/llcc-qcom.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index c31d9e39e864..3bd841e67eba 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -191,7 +191,7 @@ static const struct llcc_slice_config sc8280xp_data[] = { { LLCC_MMUHWT, 13, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 }, { LLCC_DISP, 16, 6144, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 }, { LLCC_AUDHW, 22, 2048, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 }, - { LLCC_DRE, 26, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 }, + { LLCC_ECC, 26, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 }, { LLCC_CVP, 28, 512, 3, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 }, { LLCC_APTCM, 30, 1024, 3, 1, 0x0, 0x1, 1, 0, 0, 1, 0, 0 }, { LLCC_WRCACHE, 31, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 }, diff --git a/include/linux/soc/qcom/llcc-qcom.h b/include/linux/soc/qcom/llcc-qcom.h index 93417ba1ead4..1a886666bbb6 100644 --- a/include/linux/soc/qcom/llcc-qcom.h +++ b/include/linux/soc/qcom/llcc-qcom.h @@ -30,7 +30,7 @@ #define LLCC_NPU 23 #define LLCC_WLHW 24 #define LLCC_PIMEM 25 -#define LLCC_DRE 26 +#define LLCC_ECC 26 #define LLCC_CVP 28 #define LLCC_MODPE 29 #define LLCC_APTCM 30 -- cgit v1.2.3 From db1e57928766966cd542e59789125968ac29b9da Mon Sep 17 00:00:00 2001 From: Komal Bajaj Date: Wed, 30 Aug 2023 16:26:54 +0530 Subject: soc: qcom: llcc: Add QDU1000 and QRU1000 LLCC support Add LLCC configuration data for QDU1000 and QRU1000 SoCs. Signed-off-by: Komal Bajaj Reviewed-by: Mukesh Ojha Acked-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230830105654.28057-7-quic_kbajaj@quicinc.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/llcc-qcom.c | 67 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index 3bd841e67eba..feb21637ac20 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -362,6 +362,36 @@ static const struct llcc_slice_config sm8550_data[] = { {LLCC_VIDVSP, 28, 256, 4, 1, 0xFFFFFF, 0x0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, }; +static const struct llcc_slice_config qdu1000_data_2ch[] = { + { LLCC_MDMHPGRW, 7, 512, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0, 0 }, + { LLCC_MODHW, 9, 256, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0, 0 }, + { LLCC_MDMPNG, 21, 256, 0, 1, 0x3, 0x0, 0, 0, 0, 1, 0, 0, 0 }, + { LLCC_ECC, 26, 512, 3, 1, 0xffc, 0x0, 0, 0, 0, 0, 1, 0, 0 }, + { LLCC_MODPE, 29, 256, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0, 0 }, + { LLCC_APTCM, 30, 256, 3, 1, 0x0, 0xc, 1, 0, 0, 1, 0, 0, 0 }, + { LLCC_WRCACHE, 31, 128, 1, 1, 0x3, 0x0, 0, 0, 0, 0, 1, 0, 0 }, +}; + +static const struct llcc_slice_config qdu1000_data_4ch[] = { + { LLCC_MDMHPGRW, 7, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0, 0 }, + { LLCC_MODHW, 9, 512, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0, 0 }, + { LLCC_MDMPNG, 21, 512, 0, 1, 0x3, 0x0, 0, 0, 0, 1, 0, 0, 0 }, + { LLCC_ECC, 26, 1024, 3, 1, 0xffc, 0x0, 0, 0, 0, 0, 1, 0, 0 }, + { LLCC_MODPE, 29, 512, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0, 0 }, + { LLCC_APTCM, 30, 512, 3, 1, 0x0, 0xc, 1, 0, 0, 1, 0, 0, 0 }, + { LLCC_WRCACHE, 31, 256, 1, 1, 0x3, 0x0, 0, 0, 0, 0, 1, 0, 0 }, +}; + +static const struct llcc_slice_config qdu1000_data_8ch[] = { + { LLCC_MDMHPGRW, 7, 2048, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0, 0 }, + { LLCC_MODHW, 9, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0, 0 }, + { LLCC_MDMPNG, 21, 1024, 0, 1, 0x3, 0x0, 0, 0, 0, 1, 0, 0, 0 }, + { LLCC_ECC, 26, 2048, 3, 1, 0xffc, 0x0, 0, 0, 0, 0, 1, 0, 0 }, + { LLCC_MODPE, 29, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0, 0 }, + { LLCC_APTCM, 30, 1024, 3, 1, 0x0, 0xc, 1, 0, 0, 1, 0, 0, 0 }, + { LLCC_WRCACHE, 31, 512, 1, 1, 0x3, 0x0, 0, 0, 0, 0, 1, 0, 0 }, +}; + static const struct llcc_edac_reg_offset llcc_v1_edac_reg_offset = { .trp_ecc_error_status0 = 0x20344, .trp_ecc_error_status1 = 0x20348, @@ -428,6 +458,37 @@ static const u32 llcc_v2_1_reg_offset[] = { [LLCC_COMMON_STATUS0] = 0x0003400c, }; +static const struct qcom_llcc_config qdu1000_cfg[] = { + { + .sct_data = qdu1000_data_8ch, + .size = ARRAY_SIZE(qdu1000_data_8ch), + .need_llcc_cfg = true, + .reg_offset = llcc_v2_1_reg_offset, + .edac_reg_offset = &llcc_v2_1_edac_reg_offset, + }, + { + .sct_data = qdu1000_data_4ch, + .size = ARRAY_SIZE(qdu1000_data_4ch), + .need_llcc_cfg = true, + .reg_offset = llcc_v2_1_reg_offset, + .edac_reg_offset = &llcc_v2_1_edac_reg_offset, + }, + { + .sct_data = qdu1000_data_4ch, + .size = ARRAY_SIZE(qdu1000_data_4ch), + .need_llcc_cfg = true, + .reg_offset = llcc_v2_1_reg_offset, + .edac_reg_offset = &llcc_v2_1_edac_reg_offset, + }, + { + .sct_data = qdu1000_data_2ch, + .size = ARRAY_SIZE(qdu1000_data_2ch), + .need_llcc_cfg = true, + .reg_offset = llcc_v2_1_reg_offset, + .edac_reg_offset = &llcc_v2_1_edac_reg_offset, + }, +}; + static const struct qcom_llcc_config sc7180_cfg[] = { { .sct_data = sc7180_data, @@ -549,6 +610,11 @@ static const struct qcom_llcc_config sm8550_cfg[] = { }, }; +static const struct qcom_sct_config qdu1000_cfgs = { + .llcc_config = qdu1000_cfg, + .num_config = ARRAY_SIZE(qdu1000_cfg), +}; + static const struct qcom_sct_config sc7180_cfgs = { .llcc_config = sc7180_cfg, .num_config = ARRAY_SIZE(sc7180_cfg), @@ -1168,6 +1234,7 @@ err: } static const struct of_device_id qcom_llcc_of_match[] = { + { .compatible = "qcom,qdu1000-llcc", .data = &qdu1000_cfgs}, { .compatible = "qcom,sc7180-llcc", .data = &sc7180_cfgs }, { .compatible = "qcom,sc7280-llcc", .data = &sc7280_cfgs }, { .compatible = "qcom,sc8180x-llcc", .data = &sc8180x_cfgs }, -- cgit v1.2.3 From eb3da369ccd07c0e3d65cd9cd31ca44f9f709673 Mon Sep 17 00:00:00 2001 From: Danila Tikhonov Date: Wed, 13 Sep 2023 22:44:18 +0300 Subject: dt-bindings: firmware: document Qualcomm SM7150 SCM Document the compatible for Qualcomm SM7150 SCM. Signed-off-by: Danila Tikhonov Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230913194418.30272-1-danila@jiaxyga.com Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/firmware/qcom,scm.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml index 4233ea839bfc..0c073335f8ff 100644 --- a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml +++ b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml @@ -56,6 +56,7 @@ properties: - qcom,scm-sm6125 - qcom,scm-sm6350 - qcom,scm-sm6375 + - qcom,scm-sm7150 - qcom,scm-sm8150 - qcom,scm-sm8250 - qcom,scm-sm8350 -- cgit v1.2.3 From cc3b17cabe9fd2881eace5b7b1581914569d62e9 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 7 Sep 2023 11:55:17 +0200 Subject: soc: dove: add missing of_node_put for_each_available_child_of_node performs an of_node_get on each iteration, so a break out of the loop requires an of_node_put. This was done using the Coccinelle semantic patch iterators/for_each_child.cocci Signed-off-by: Julia Lawall Signed-off-by: Gregory CLEMENT --- drivers/soc/dove/pmu.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/soc/dove/pmu.c b/drivers/soc/dove/pmu.c index ffc5311c0ed8..6202dbcd20a8 100644 --- a/drivers/soc/dove/pmu.c +++ b/drivers/soc/dove/pmu.c @@ -410,13 +410,16 @@ int __init dove_init_pmu(void) struct pmu_domain *domain; domain = kzalloc(sizeof(*domain), GFP_KERNEL); - if (!domain) + if (!domain) { + of_node_put(np); break; + } domain->pmu = pmu; domain->base.name = kasprintf(GFP_KERNEL, "%pOFn", np); if (!domain->base.name) { kfree(domain); + of_node_put(np); break; } -- cgit v1.2.3 From be6d43efb12455dbf85c96ff67582a463d34a0fc Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Wed, 30 Aug 2023 17:03:44 +0800 Subject: firmware: ti_sci: Use list_for_each_entry() helper Convert list_for_each() to list_for_each_entry() so that the p list_head pointer and list_entry() call are no longer needed, which can reduce a few lines of code. No functional changed. Signed-off-by: Jinjie Ruan Link: https://lore.kernel.org/r/20230830090344.528818-1-ruanjinjie@huawei.com Signed-off-by: Nishanth Menon --- drivers/firmware/ti_sci.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 26a37f47f4ca..8444355539c6 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -2886,7 +2886,6 @@ static void ti_sci_setup_ops(struct ti_sci_info *info) const struct ti_sci_handle *ti_sci_get_handle(struct device *dev) { struct device_node *ti_sci_np; - struct list_head *p; struct ti_sci_handle *handle = NULL; struct ti_sci_info *info; @@ -2901,8 +2900,7 @@ const struct ti_sci_handle *ti_sci_get_handle(struct device *dev) } mutex_lock(&ti_sci_list_mutex); - list_for_each(p, &ti_sci_list) { - info = list_entry(p, struct ti_sci_info, node); + list_for_each_entry(info, &ti_sci_list, node) { if (ti_sci_np == info->dev->of_node) { handle = &info->handle; info->users++; @@ -3012,7 +3010,6 @@ const struct ti_sci_handle *ti_sci_get_by_phandle(struct device_node *np, struct ti_sci_handle *handle = NULL; struct device_node *ti_sci_np; struct ti_sci_info *info; - struct list_head *p; if (!np) { pr_err("I need a device pointer\n"); @@ -3024,8 +3021,7 @@ const struct ti_sci_handle *ti_sci_get_by_phandle(struct device_node *np, return ERR_PTR(-ENODEV); mutex_lock(&ti_sci_list_mutex); - list_for_each(p, &ti_sci_list) { - info = list_entry(p, struct ti_sci_info, node); + list_for_each_entry(info, &ti_sci_list, node) { if (ti_sci_np == info->dev->of_node) { handle = &info->handle; info->users++; -- cgit v1.2.3 From d8cce0d5ba4a3157a7a549b9623d1ffc5820ef92 Mon Sep 17 00:00:00 2001 From: Justin Stitt Date: Wed, 13 Sep 2023 20:23:02 +0000 Subject: firmware: ti_sci: refactor deprecated strncpy `strncpy` is deprecated for use on NUL-terminated destination strings [1]. We should prefer more robust and less ambiguous string interfaces. A suitable replacement is `strscpy` [2] due to the fact that it guarantees NUL-termination on the destination buffer. It does not seem like `ver->firmware_description` requires NUL-padding (which is a behavior that strncpy provides) but if it does let's opt for `strscpy_pad()`. Link: https://www.kernel.org/doc/html/latest/process/deprecated.html#strncpy-on-nul-terminated-strings [1] Link: https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html [2] Link: https://github.com/KSPP/linux/issues/90 Cc: linux-hardening@vger.kernel.org Signed-off-by: Justin Stitt Reviewed-by: Kees Cook Link: https://lore.kernel.org/r/20230913-strncpy-drivers-firmware-ti_sci-c-v1-1-740db471110d@google.com Signed-off-by: Nishanth Menon --- drivers/firmware/ti_sci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 8444355539c6..c288ae49f91c 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -485,7 +485,7 @@ static int ti_sci_cmd_get_revision(struct ti_sci_info *info) ver->abi_major = rev_info->abi_major; ver->abi_minor = rev_info->abi_minor; ver->firmware_revision = rev_info->firmware_revision; - strncpy(ver->firmware_description, rev_info->firmware_description, + strscpy(ver->firmware_description, rev_info->firmware_description, sizeof(ver->firmware_description)); fail: -- cgit v1.2.3 From 03a95cf233b5bdd65ddd4eca63cd4874b6b84d50 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Sat, 26 Aug 2023 13:53:03 +0100 Subject: firmware: arm_scmi: Simplify enable/disable clock operations SCMI clock enable/disable operations come in 2 different flavours which simply just differ in how the underlying SCMI transactions is carried on: atomic or not. Currently we expose such SCMI operations through 2 distinctly named wrappers, that, in turn, are wrapped into another couple of similarly and distinctly named callbacks inside SCMI clock driver user. Reduce the churn of duplicated wrappers by adding a param to SCMI clock enable/disable operations to ask for atomic operation while removing the _atomic version of such operations. No functional change. CC: Michael Turquette CC: Stephen Boyd CC: linux-clk@vger.kernel.org Signed-off-by: Cristian Marussi Acked-by: Stephen Boyd Link: https://lore.kernel.org/r/20230826125308.462328-2-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/clk/clk-scmi.c | 11 +++++++---- drivers/firmware/arm_scmi/clock.c | 24 ++++++------------------ include/linux/scmi_protocol.h | 9 ++++----- 3 files changed, 17 insertions(+), 27 deletions(-) diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c index 2c7a830ce308..b7a180b3443e 100644 --- a/drivers/clk/clk-scmi.c +++ b/drivers/clk/clk-scmi.c @@ -13,6 +13,9 @@ #include #include +#define NOT_ATOMIC false +#define ATOMIC true + static const struct scmi_clk_proto_ops *scmi_proto_clk_ops; struct scmi_clk { @@ -78,28 +81,28 @@ static int scmi_clk_enable(struct clk_hw *hw) { struct scmi_clk *clk = to_scmi_clk(hw); - return scmi_proto_clk_ops->enable(clk->ph, clk->id); + return scmi_proto_clk_ops->enable(clk->ph, clk->id, NOT_ATOMIC); } static void scmi_clk_disable(struct clk_hw *hw) { struct scmi_clk *clk = to_scmi_clk(hw); - scmi_proto_clk_ops->disable(clk->ph, clk->id); + scmi_proto_clk_ops->disable(clk->ph, clk->id, NOT_ATOMIC); } static int scmi_clk_atomic_enable(struct clk_hw *hw) { struct scmi_clk *clk = to_scmi_clk(hw); - return scmi_proto_clk_ops->enable_atomic(clk->ph, clk->id); + return scmi_proto_clk_ops->enable(clk->ph, clk->id, ATOMIC); } static void scmi_clk_atomic_disable(struct clk_hw *hw) { struct scmi_clk *clk = to_scmi_clk(hw); - scmi_proto_clk_ops->disable_atomic(clk->ph, clk->id); + scmi_proto_clk_ops->disable(clk->ph, clk->id, ATOMIC); } /* diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 96060bf90a24..447d29b5fc72 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -418,26 +418,16 @@ scmi_clock_config_set(const struct scmi_protocol_handle *ph, u32 clk_id, return ret; } -static int scmi_clock_enable(const struct scmi_protocol_handle *ph, u32 clk_id) +static int scmi_clock_enable(const struct scmi_protocol_handle *ph, u32 clk_id, + bool atomic) { - return scmi_clock_config_set(ph, clk_id, CLOCK_ENABLE, false); + return scmi_clock_config_set(ph, clk_id, CLOCK_ENABLE, atomic); } -static int scmi_clock_disable(const struct scmi_protocol_handle *ph, u32 clk_id) +static int scmi_clock_disable(const struct scmi_protocol_handle *ph, u32 clk_id, + bool atomic) { - return scmi_clock_config_set(ph, clk_id, 0, false); -} - -static int scmi_clock_enable_atomic(const struct scmi_protocol_handle *ph, - u32 clk_id) -{ - return scmi_clock_config_set(ph, clk_id, CLOCK_ENABLE, true); -} - -static int scmi_clock_disable_atomic(const struct scmi_protocol_handle *ph, - u32 clk_id) -{ - return scmi_clock_config_set(ph, clk_id, 0, true); + return scmi_clock_config_set(ph, clk_id, 0, atomic); } static int scmi_clock_count_get(const struct scmi_protocol_handle *ph) @@ -470,8 +460,6 @@ static const struct scmi_clk_proto_ops clk_proto_ops = { .rate_set = scmi_clock_rate_set, .enable = scmi_clock_enable, .disable = scmi_clock_disable, - .enable_atomic = scmi_clock_enable_atomic, - .disable_atomic = scmi_clock_disable_atomic, }; static int scmi_clk_rate_notify(const struct scmi_protocol_handle *ph, diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index e6fe4f73ffe6..b4c631a8d0ac 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -90,11 +90,10 @@ struct scmi_clk_proto_ops { u64 *rate); int (*rate_set)(const struct scmi_protocol_handle *ph, u32 clk_id, u64 rate); - int (*enable)(const struct scmi_protocol_handle *ph, u32 clk_id); - int (*disable)(const struct scmi_protocol_handle *ph, u32 clk_id); - int (*enable_atomic)(const struct scmi_protocol_handle *ph, u32 clk_id); - int (*disable_atomic)(const struct scmi_protocol_handle *ph, - u32 clk_id); + int (*enable)(const struct scmi_protocol_handle *ph, u32 clk_id, + bool atomic); + int (*disable)(const struct scmi_protocol_handle *ph, u32 clk_id, + bool atomic); }; /** -- cgit v1.2.3 From e49e314a2cf70270df304c8b87a52fba27986873 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Sat, 26 Aug 2023 13:53:04 +0100 Subject: firmware: arm_scmi: Add clock v3.2 CONFIG_SET support SCMI v3.2 introduces a new clock CONFIG_SET message format that can optionally carry also OEM specific configuration values beside the usual clock enable/disable requests. Refactor internal helpers and add support to use such new format when talking to a v3.2 compliant SCMI platform. Support existing enable/disable operations across different clock protocol versions: this patch still does not add protocol operations to support the new OEM specific optional configuration capabilities. No functional change for the SCMI drivers users of the related enable and disable clock operations. Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20230826125308.462328-3-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 88 +++++++++++++++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 8 deletions(-) diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 447d29b5fc72..63bd043365cd 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -23,6 +23,13 @@ enum scmi_clock_protocol_cmd { CLOCK_RATE_CHANGE_REQUESTED_NOTIFY = 0xA, }; +enum clk_state { + CLK_STATE_DISABLE, + CLK_STATE_ENABLE, + CLK_STATE_RESERVED, + CLK_STATE_UNCHANGED, +}; + struct scmi_msg_resp_clock_protocol_attributes { __le16 num_clocks; u8 max_async_req; @@ -31,7 +38,6 @@ struct scmi_msg_resp_clock_protocol_attributes { struct scmi_msg_resp_clock_attributes { __le32 attributes; -#define CLOCK_ENABLE BIT(0) #define SUPPORTS_RATE_CHANGED_NOTIF(x) ((x) & BIT(31)) #define SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(x) ((x) & BIT(30)) #define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(29)) @@ -39,9 +45,18 @@ struct scmi_msg_resp_clock_attributes { __le32 clock_enable_latency; }; -struct scmi_clock_set_config { +struct scmi_msg_clock_config_set_v2 { + __le32 id; + __le32 attributes; +}; + +struct scmi_msg_clock_config_set_v21 { __le32 id; __le32 attributes; +#define NULL_OEM_TYPE 0 +#define REGMASK_OEM_TYPE_SET GENMASK(23, 16) +#define REGMASK_CLK_STATE GENMASK(1, 0) + __le32 oem_config_val; }; struct scmi_msg_clock_describe_rates { @@ -100,6 +115,9 @@ struct clock_info { int max_async_req; atomic_t cur_async_req; struct scmi_clock_info *clk; + int (*clock_config_set)(const struct scmi_protocol_handle *ph, + u32 clk_id, enum clk_state state, + u8 oem_type, u32 oem_val, bool atomic); }; static enum scmi_clock_protocol_cmd evt_2_cmd[] = { @@ -394,12 +412,47 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph, } static int -scmi_clock_config_set(const struct scmi_protocol_handle *ph, u32 clk_id, - u32 config, bool atomic) +scmi_clock_config_set_v2(const struct scmi_protocol_handle *ph, u32 clk_id, + enum clk_state state, u8 __unused0, u32 __unused1, + bool atomic) +{ + int ret; + struct scmi_xfer *t; + struct scmi_msg_clock_config_set_v2 *cfg; + + if (state >= CLK_STATE_RESERVED) + return -EINVAL; + + ret = ph->xops->xfer_get_init(ph, CLOCK_CONFIG_SET, + sizeof(*cfg), 0, &t); + if (ret) + return ret; + + t->hdr.poll_completion = atomic; + + cfg = t->tx.buf; + cfg->id = cpu_to_le32(clk_id); + cfg->attributes = cpu_to_le32(state); + + ret = ph->xops->do_xfer(ph, t); + + ph->xops->xfer_put(ph, t); + return ret; +} + +static int +scmi_clock_config_set_v21(const struct scmi_protocol_handle *ph, u32 clk_id, + enum clk_state state, u8 oem_type, u32 oem_val, + bool atomic) { int ret; + u32 attrs; struct scmi_xfer *t; - struct scmi_clock_set_config *cfg; + struct scmi_msg_clock_config_set_v21 *cfg; + + if (state == CLK_STATE_RESERVED || + (!oem_type && state == CLK_STATE_UNCHANGED)) + return -EINVAL; ret = ph->xops->xfer_get_init(ph, CLOCK_CONFIG_SET, sizeof(*cfg), 0, &t); @@ -408,9 +461,16 @@ scmi_clock_config_set(const struct scmi_protocol_handle *ph, u32 clk_id, t->hdr.poll_completion = atomic; + attrs = FIELD_PREP(REGMASK_OEM_TYPE_SET, oem_type) | + FIELD_PREP(REGMASK_CLK_STATE, state); + cfg = t->tx.buf; cfg->id = cpu_to_le32(clk_id); - cfg->attributes = cpu_to_le32(config); + cfg->attributes = cpu_to_le32(attrs); + /* Clear in any case */ + cfg->oem_config_val = cpu_to_le32(0); + if (oem_type) + cfg->oem_config_val = cpu_to_le32(oem_val); ret = ph->xops->do_xfer(ph, t); @@ -421,13 +481,19 @@ scmi_clock_config_set(const struct scmi_protocol_handle *ph, u32 clk_id, static int scmi_clock_enable(const struct scmi_protocol_handle *ph, u32 clk_id, bool atomic) { - return scmi_clock_config_set(ph, clk_id, CLOCK_ENABLE, atomic); + struct clock_info *ci = ph->get_priv(ph); + + return ci->clock_config_set(ph, clk_id, CLK_STATE_ENABLE, + NULL_OEM_TYPE, 0, atomic); } static int scmi_clock_disable(const struct scmi_protocol_handle *ph, u32 clk_id, bool atomic) { - return scmi_clock_config_set(ph, clk_id, 0, atomic); + struct clock_info *ci = ph->get_priv(ph); + + return ci->clock_config_set(ph, clk_id, CLK_STATE_DISABLE, + NULL_OEM_TYPE, 0, atomic); } static int scmi_clock_count_get(const struct scmi_protocol_handle *ph) @@ -592,6 +658,12 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph) scmi_clock_describe_rates_get(ph, clkid, clk); } + if (PROTOCOL_REV_MAJOR(version) >= 0x2 && + PROTOCOL_REV_MINOR(version) >= 0x1) + cinfo->clock_config_set = scmi_clock_config_set_v21; + else + cinfo->clock_config_set = scmi_clock_config_set_v2; + cinfo->version = version; return ph->set_priv(ph, cinfo); } -- cgit v1.2.3 From 34592bf0a5cb0681ce3d64de5598951768f43328 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Sat, 26 Aug 2023 13:53:05 +0100 Subject: firmware: arm_scmi: Add v3.2 clock CONFIG_GET support Add support for v3.2 clock CONFIG_GET command and related new clock protocol operation state_get() to retrieve the status of a clock. Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20230826125308.462328-4-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 64 +++++++++++++++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 3 ++ 2 files changed, 67 insertions(+) diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 63bd043365cd..aaa95624493d 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -21,6 +21,7 @@ enum scmi_clock_protocol_cmd { CLOCK_NAME_GET = 0x8, CLOCK_RATE_NOTIFY = 0x9, CLOCK_RATE_CHANGE_REQUESTED_NOTIFY = 0xA, + CLOCK_CONFIG_GET = 0xB, }; enum clk_state { @@ -59,6 +60,19 @@ struct scmi_msg_clock_config_set_v21 { __le32 oem_config_val; }; +struct scmi_msg_clock_config_get { + __le32 id; + __le32 flags; +#define REGMASK_OEM_TYPE_GET GENMASK(7, 0) +}; + +struct scmi_msg_resp_clock_config_get { + __le32 attributes; + __le32 config; +#define IS_CLK_ENABLED(x) le32_get_bits((x), BIT(0)) + __le32 oem_config_val; +}; + struct scmi_msg_clock_describe_rates { __le32 id; __le32 rate_index; @@ -496,6 +510,55 @@ static int scmi_clock_disable(const struct scmi_protocol_handle *ph, u32 clk_id, NULL_OEM_TYPE, 0, atomic); } +static int +scmi_clock_config_get(const struct scmi_protocol_handle *ph, u32 clk_id, + u8 oem_type, u32 *attributes, bool *enabled, + u32 *oem_val, bool atomic) +{ + int ret; + u32 flags; + struct scmi_xfer *t; + struct scmi_msg_clock_config_get *cfg; + + ret = ph->xops->xfer_get_init(ph, CLOCK_CONFIG_GET, + sizeof(*cfg), 0, &t); + if (ret) + return ret; + + t->hdr.poll_completion = atomic; + + flags = FIELD_PREP(REGMASK_OEM_TYPE_GET, oem_type); + + cfg = t->tx.buf; + cfg->id = cpu_to_le32(clk_id); + cfg->flags = cpu_to_le32(flags); + + ret = ph->xops->do_xfer(ph, t); + if (!ret) { + struct scmi_msg_resp_clock_config_get *resp = t->rx.buf; + + if (attributes) + *attributes = le32_to_cpu(resp->attributes); + + if (enabled) + *enabled = IS_CLK_ENABLED(resp->config); + + if (oem_val && oem_type) + *oem_val = le32_to_cpu(resp->oem_config_val); + } + + ph->xops->xfer_put(ph, t); + + return ret; +} + +static int scmi_clock_state_get(const struct scmi_protocol_handle *ph, + u32 clk_id, bool *enabled, bool atomic) +{ + return scmi_clock_config_get(ph, clk_id, NULL_OEM_TYPE, NULL, + enabled, NULL, atomic); +} + static int scmi_clock_count_get(const struct scmi_protocol_handle *ph) { struct clock_info *ci = ph->get_priv(ph); @@ -526,6 +589,7 @@ static const struct scmi_clk_proto_ops clk_proto_ops = { .rate_set = scmi_clock_rate_set, .enable = scmi_clock_enable, .disable = scmi_clock_disable, + .state_get = scmi_clock_state_get, }; static int scmi_clk_rate_notify(const struct scmi_protocol_handle *ph, diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index b4c631a8d0ac..d11ca4286d57 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -80,6 +80,7 @@ struct scmi_protocol_handle; * @rate_set: set the clock rate of a clock * @enable: enables the specified clock * @disable: disables the specified clock + * @state_get: get the status of the specified clock */ struct scmi_clk_proto_ops { int (*count_get)(const struct scmi_protocol_handle *ph); @@ -94,6 +95,8 @@ struct scmi_clk_proto_ops { bool atomic); int (*disable)(const struct scmi_protocol_handle *ph, u32 clk_id, bool atomic); + int (*state_get)(const struct scmi_protocol_handle *ph, u32 clk_id, + bool *enabled, bool atomic); }; /** -- cgit v1.2.3 From 5b8a8ca37e4949d063b02c0aabd9a258585f3f5f Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Sat, 26 Aug 2023 13:53:06 +0100 Subject: firmware: arm_scmi: Add clock .state_get support to pre-v3.2 Support clock .state_get operation against SCMI platform servers that do not support v3.2 CONFIG_GET dedicated command: while talking with these platforms the command CLOCK_ATTRIBUTES can be used to gather the current clock states. Note that, in case of shared resources, the retrieved clock state 'flavour' (virtual vs physical) depends on the backend SCMI platform server specific kind of implementation. Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20230826125308.462328-5-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 52 +++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index aaa95624493d..333d08822f77 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -132,6 +132,9 @@ struct clock_info { int (*clock_config_set)(const struct scmi_protocol_handle *ph, u32 clk_id, enum clk_state state, u8 oem_type, u32 oem_val, bool atomic); + int (*clock_config_get)(const struct scmi_protocol_handle *ph, + u32 clk_id, u8 oem_type, u32 *attributes, + bool *enabled, u32 *oem_val, bool atomic); }; static enum scmi_clock_protocol_cmd evt_2_cmd[] = { @@ -511,9 +514,9 @@ static int scmi_clock_disable(const struct scmi_protocol_handle *ph, u32 clk_id, } static int -scmi_clock_config_get(const struct scmi_protocol_handle *ph, u32 clk_id, - u8 oem_type, u32 *attributes, bool *enabled, - u32 *oem_val, bool atomic) +scmi_clock_config_get_v21(const struct scmi_protocol_handle *ph, u32 clk_id, + u8 oem_type, u32 *attributes, bool *enabled, + u32 *oem_val, bool atomic) { int ret; u32 flags; @@ -552,11 +555,43 @@ scmi_clock_config_get(const struct scmi_protocol_handle *ph, u32 clk_id, return ret; } +static int +scmi_clock_config_get_v2(const struct scmi_protocol_handle *ph, u32 clk_id, + u8 oem_type, u32 *attributes, bool *enabled, + u32 *oem_val, bool atomic) +{ + int ret; + struct scmi_xfer *t; + struct scmi_msg_resp_clock_attributes *resp; + + if (!enabled) + return -EINVAL; + + ret = ph->xops->xfer_get_init(ph, CLOCK_ATTRIBUTES, + sizeof(clk_id), sizeof(*resp), &t); + if (ret) + return ret; + + t->hdr.poll_completion = atomic; + put_unaligned_le32(clk_id, t->tx.buf); + resp = t->rx.buf; + + ret = ph->xops->do_xfer(ph, t); + if (!ret) + *enabled = IS_CLK_ENABLED(resp->attributes); + + ph->xops->xfer_put(ph, t); + + return ret; +} + static int scmi_clock_state_get(const struct scmi_protocol_handle *ph, u32 clk_id, bool *enabled, bool atomic) { - return scmi_clock_config_get(ph, clk_id, NULL_OEM_TYPE, NULL, - enabled, NULL, atomic); + struct clock_info *ci = ph->get_priv(ph); + + return ci->clock_config_get(ph, clk_id, NULL_OEM_TYPE, NULL, + enabled, NULL, atomic); } static int scmi_clock_count_get(const struct scmi_protocol_handle *ph) @@ -723,10 +758,13 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph) } if (PROTOCOL_REV_MAJOR(version) >= 0x2 && - PROTOCOL_REV_MINOR(version) >= 0x1) + PROTOCOL_REV_MINOR(version) >= 0x1) { cinfo->clock_config_set = scmi_clock_config_set_v21; - else + cinfo->clock_config_get = scmi_clock_config_get_v21; + } else { cinfo->clock_config_set = scmi_clock_config_set_v2; + cinfo->clock_config_get = scmi_clock_config_get_v2; + } cinfo->version = version; return ph->set_priv(ph, cinfo); -- cgit v1.2.3 From 1b39ff5140c92bc45145ace8e78c00e922449abd Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Sat, 26 Aug 2023 13:53:07 +0100 Subject: clk: scmi: Add support for .is_enabled clk_ops Add support for .is_enabled atomic clk_ops using the related SCMI clock operation in atomic mode, if available. Note that the .is_enabled callback will be supported by this SCMI clock driver only if the configured underlying SCMI transport does support atomic operations. CC: Michael Turquette CC: Stephen Boyd CC: linux-clk@vger.kernel.org Signed-off-by: Cristian Marussi Acked-by: Stephen Boyd Link: https://lore.kernel.org/r/20230826125308.462328-6-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/clk/clk-scmi.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c index b7a180b3443e..2e1337b511eb 100644 --- a/drivers/clk/clk-scmi.c +++ b/drivers/clk/clk-scmi.c @@ -20,6 +20,7 @@ static const struct scmi_clk_proto_ops *scmi_proto_clk_ops; struct scmi_clk { u32 id; + struct device *dev; struct clk_hw hw; const struct scmi_clock_info *info; const struct scmi_protocol_handle *ph; @@ -105,10 +106,24 @@ static void scmi_clk_atomic_disable(struct clk_hw *hw) scmi_proto_clk_ops->disable(clk->ph, clk->id, ATOMIC); } +static int scmi_clk_atomic_is_enabled(struct clk_hw *hw) +{ + int ret; + bool enabled = false; + struct scmi_clk *clk = to_scmi_clk(hw); + + ret = scmi_proto_clk_ops->state_get(clk->ph, clk->id, &enabled, ATOMIC); + if (ret) + dev_warn(clk->dev, + "Failed to get state for clock ID %d\n", clk->id); + + return !!enabled; +} + /* - * We can provide enable/disable atomic callbacks only if the underlying SCMI - * transport for an SCMI instance is configured to handle SCMI commands in an - * atomic manner. + * We can provide enable/disable/is_enabled atomic callbacks only if the + * underlying SCMI transport for an SCMI instance is configured to handle + * SCMI commands in an atomic manner. * * When no SCMI atomic transport support is available we instead provide only * the prepare/unprepare API, as allowed by the clock framework when atomic @@ -132,6 +147,7 @@ static const struct clk_ops scmi_atomic_clk_ops = { .set_rate = scmi_clk_set_rate, .enable = scmi_clk_atomic_enable, .disable = scmi_clk_atomic_disable, + .is_enabled = scmi_clk_atomic_is_enabled, }; static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk, @@ -221,6 +237,7 @@ static int scmi_clocks_probe(struct scmi_device *sdev) sclk->id = idx; sclk->ph = ph; + sclk->dev = dev; /* * Note that when transport is atomic but SCMI protocol did not -- cgit v1.2.3 From 141b4fa0362569138653cf0165d92d48576db3fa Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Sat, 26 Aug 2023 13:53:08 +0100 Subject: firmware: arm_scmi: Add clock OEM config clock operations Expose a couple of new SCMI clock operations to get and set OEM specific clock configurations when talking to an SCMI v3.2 compliant. Issuing such requests against an SCMI platform server not supporting v3.2 extension for OEM specific clock configurations will fail. Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20230826125308.462328-7-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 22 ++++++++++++++++++++++ include/linux/scmi_protocol.h | 7 +++++++ 2 files changed, 29 insertions(+) diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 333d08822f77..d18bf789fc24 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -594,6 +594,26 @@ static int scmi_clock_state_get(const struct scmi_protocol_handle *ph, enabled, NULL, atomic); } +static int scmi_clock_config_oem_set(const struct scmi_protocol_handle *ph, + u32 clk_id, u8 oem_type, u32 oem_val, + bool atomic) +{ + struct clock_info *ci = ph->get_priv(ph); + + return ci->clock_config_set(ph, clk_id, CLK_STATE_UNCHANGED, + oem_type, oem_val, atomic); +} + +static int scmi_clock_config_oem_get(const struct scmi_protocol_handle *ph, + u32 clk_id, u8 oem_type, u32 *oem_val, + u32 *attributes, bool atomic) +{ + struct clock_info *ci = ph->get_priv(ph); + + return ci->clock_config_get(ph, clk_id, oem_type, attributes, + NULL, oem_val, atomic); +} + static int scmi_clock_count_get(const struct scmi_protocol_handle *ph) { struct clock_info *ci = ph->get_priv(ph); @@ -625,6 +645,8 @@ static const struct scmi_clk_proto_ops clk_proto_ops = { .enable = scmi_clock_enable, .disable = scmi_clock_disable, .state_get = scmi_clock_state_get, + .config_oem_get = scmi_clock_config_oem_get, + .config_oem_set = scmi_clock_config_oem_set, }; static int scmi_clk_rate_notify(const struct scmi_protocol_handle *ph, diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index d11ca4286d57..e09ac428fa1b 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -81,6 +81,8 @@ struct scmi_protocol_handle; * @enable: enables the specified clock * @disable: disables the specified clock * @state_get: get the status of the specified clock + * @config_oem_get: get the value of an OEM specific clock config + * @config_oem_set: set the value of an OEM specific clock config */ struct scmi_clk_proto_ops { int (*count_get)(const struct scmi_protocol_handle *ph); @@ -97,6 +99,11 @@ struct scmi_clk_proto_ops { bool atomic); int (*state_get)(const struct scmi_protocol_handle *ph, u32 clk_id, bool *enabled, bool atomic); + int (*config_oem_get)(const struct scmi_protocol_handle *ph, u32 clk_id, + u8 oem_type, u32 *oem_val, u32 *attributes, + bool atomic); + int (*config_oem_set)(const struct scmi_protocol_handle *ph, u32 clk_id, + u8 oem_type, u32 oem_val, bool atomic); }; /** -- cgit v1.2.3 From 92dab9ea5f389c12828283146c60054642453a91 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Wed, 16 Aug 2023 18:45:38 +0200 Subject: dt-bindings: firmware: qcom,scm: support indicating SDI default state IPQ5018 has SDI (Secure Debug Image) enabled by TZ by default, and that means that WDT being asserted or just trying to reboot will hang the board in the debug mode and only pulling the power and repowering will help. Some IPQ4019 boards like Google WiFI have it enabled as well. So, lets add a boolean property to indicate that SDI is enabled by default and thus needs to be disabled by the kernel. Signed-off-by: Robert Marko Acked-by: Mukesh Ojha Reviewed-by: Krzysztof Kozlowski Reviewed-by: Brian Norris Link: https://lore.kernel.org/r/20230816164641.3371878-1-robimarko@gmail.com Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/firmware/qcom,scm.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml index 0c073335f8ff..cb706145ae04 100644 --- a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml +++ b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml @@ -90,6 +90,14 @@ properties: protocol to handle sleeping SCM calls. maxItems: 1 + qcom,sdi-enabled: + description: + Indicates that the SDI (Secure Debug Image) has been enabled by TZ + by default and it needs to be disabled. + If not disabled WDT assertion or reboot will cause the board to hang + in the debug mode. + type: boolean + qcom,dload-mode: $ref: /schemas/types.yaml#/definitions/phandle-array items: -- cgit v1.2.3 From ff4aa3bc98258a240b9bbab53fd8d2fb8184c485 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Wed, 16 Aug 2023 18:45:39 +0200 Subject: firmware: qcom_scm: disable SDI if required IPQ5018 has SDI (Secure Debug Image) enabled by TZ by default, and that means that WDT being asserted or just trying to reboot will hang the board in the debug mode and only pulling the power and repowering will help. Some IPQ4019 boards like Google WiFI have it enabled as well. Luckily, SDI can be disabled via an SCM call. So, lets use the boolean DT property to identify boards that have SDI enabled by default and use the SCM call to disable SDI during SCM probe. It is important to disable it as soon as possible as we might have a WDT assertion at any time which would then leave the board in debug mode, thus disabling it during SCM removal is not enough. Signed-off-by: Robert Marko Reviewed-by: Guru Das Srinagesh Link: https://lore.kernel.org/r/20230816164641.3371878-2-robimarko@gmail.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom_scm.c | 30 ++++++++++++++++++++++++++++++ drivers/firmware/qcom_scm.h | 1 + 2 files changed, 31 insertions(+) diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index ca52618e2a8d..c2c7fafef34b 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -451,6 +451,29 @@ int qcom_scm_set_remote_state(u32 state, u32 id) } EXPORT_SYMBOL_GPL(qcom_scm_set_remote_state); +static int qcom_scm_disable_sdi(void) +{ + int ret; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_BOOT, + .cmd = QCOM_SCM_BOOT_SDI_CONFIG, + .args[0] = 1, /* Disable watchdog debug */ + .args[1] = 0, /* Disable SDI */ + .arginfo = QCOM_SCM_ARGS(2), + .owner = ARM_SMCCC_OWNER_SIP, + }; + struct qcom_scm_res res; + + ret = qcom_scm_clk_enable(); + if (ret) + return ret; + ret = qcom_scm_call(__scm->dev, &desc, &res); + + qcom_scm_clk_disable(); + + return ret ? : res.result[0]; +} + static int __qcom_scm_set_dload_mode(struct device *dev, bool enable) { struct qcom_scm_desc desc = { @@ -1850,6 +1873,13 @@ static int qcom_scm_probe(struct platform_device *pdev) if (download_mode) qcom_scm_set_download_mode(true); + + /* + * Disable SDI if indicated by DT that it is enabled by default. + */ + if (of_property_read_bool(pdev->dev.of_node, "qcom,sdi-enabled")) + qcom_scm_disable_sdi(); + /* * Initialize the QSEECOM interface. * diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index e6e512bd57d1..7b68fa820495 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -80,6 +80,7 @@ extern int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc, #define QCOM_SCM_SVC_BOOT 0x01 #define QCOM_SCM_BOOT_SET_ADDR 0x01 #define QCOM_SCM_BOOT_TERMINATE_PC 0x02 +#define QCOM_SCM_BOOT_SDI_CONFIG 0x09 #define QCOM_SCM_BOOT_SET_DLOAD_MODE 0x10 #define QCOM_SCM_BOOT_SET_ADDR_MC 0x11 #define QCOM_SCM_BOOT_SET_REMOTE_STATE 0x0a -- cgit v1.2.3 From f6aa7386bc40b552eea8ec1b1d2168afe3b31110 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Wed, 16 Aug 2023 18:45:40 +0200 Subject: dt-bindings: firmware: qcom,scm: document IPQ5018 compatible It seems that IPQ5018 compatible was never documented in the bindings. Signed-off-by: Robert Marko Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230816164641.3371878-3-robimarko@gmail.com Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/firmware/qcom,scm.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml index cb706145ae04..0613a37a851a 100644 --- a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml +++ b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml @@ -24,6 +24,7 @@ properties: - qcom,scm-apq8064 - qcom,scm-apq8084 - qcom,scm-ipq4019 + - qcom,scm-ipq5018 - qcom,scm-ipq5332 - qcom,scm-ipq6018 - qcom,scm-ipq806x -- cgit v1.2.3 From e9090e70e618cd62ab7bf2914511e5eea31a2535 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 25 Aug 2023 13:26:21 +0200 Subject: firmware: arm_scmi: Extend perf protocol ops to get number of domains Similar to other protocol ops, it's useful for an scmi module driver to get the number of supported performance domains, hence let's make this available by adding a new perf protocol callback. Note that, a user is being added from subsequent changes. Signed-off-by: Ulf Hansson Link: https://lore.kernel.org/r/20230825112633.236607-2-ulf.hansson@linaro.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/perf.c | 8 ++++++++ include/linux/scmi_protocol.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index c0cd556fbaae..9fb63dd44c20 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -423,6 +423,13 @@ scmi_perf_describe_levels_get(const struct scmi_protocol_handle *ph, return ret; } +static int scmi_perf_num_domains_get(const struct scmi_protocol_handle *ph) +{ + struct scmi_perf_info *pi = ph->get_priv(ph); + + return pi->num_domains; +} + static int scmi_perf_msg_limits_set(const struct scmi_protocol_handle *ph, u32 domain, u32 max_perf, u32 min_perf) { @@ -948,6 +955,7 @@ scmi_power_scale_get(const struct scmi_protocol_handle *ph) } static const struct scmi_perf_proto_ops perf_proto_ops = { + .num_domains_get = scmi_perf_num_domains_get, .limits_set = scmi_perf_limits_set, .limits_get = scmi_perf_limits_get, .level_set = scmi_perf_level_set, diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index e09ac428fa1b..14791dd54706 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -110,6 +110,7 @@ struct scmi_clk_proto_ops { * struct scmi_perf_proto_ops - represents the various operations provided * by SCMI Performance Protocol * + * @num_domains_get: gets the number of supported performance domains * @limits_set: sets limits on the performance level of a domain * @limits_get: gets limits on the performance level of a domain * @level_set: sets the performance level of a domain @@ -129,6 +130,7 @@ struct scmi_clk_proto_ops { * or in some other (abstract) scale */ struct scmi_perf_proto_ops { + int (*num_domains_get)(const struct scmi_protocol_handle *ph); int (*limits_set)(const struct scmi_protocol_handle *ph, u32 domain, u32 max_perf, u32 min_perf); int (*limits_get)(const struct scmi_protocol_handle *ph, u32 domain, -- cgit v1.2.3 From 3d99ed60721bf2e108c8fc660775766057689a92 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 25 Aug 2023 13:26:22 +0200 Subject: firmware: arm_scmi: Extend perf protocol ops to get information of a domain Similar to other protocol ops, it's useful for an scmi module driver to get some generic information of a performance domain. Therefore, let's add a new callback to provide this information. The information is currently limited to the name of the performance domain and whether the set-level operation is supported, although this can easily be extended if we find the need for it. Signed-off-by: Ulf Hansson Link: https://lore.kernel.org/r/20230825112633.236607-3-ulf.hansson@linaro.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/perf.c | 47 +++++++++++++++++++++++++--------------- include/linux/scmi_protocol.h | 8 +++++++ 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index 9fb63dd44c20..d20bb6b8abfa 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -145,7 +145,6 @@ struct scmi_msg_resp_perf_describe_levels_v4 { struct perf_dom_info { u32 id; bool set_limits; - bool set_perf; bool perf_limit_notify; bool perf_level_notify; bool perf_fastchannels; @@ -154,7 +153,7 @@ struct perf_dom_info { u32 sustained_freq_khz; u32 sustained_perf_level; u32 mult_factor; - char name[SCMI_MAX_STR_SIZE]; + struct scmi_perf_domain_info info; struct scmi_opp opp[MAX_OPPS]; struct scmi_fc_info *fc_info; struct xarray opps_by_idx; @@ -257,7 +256,7 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph, flags = le32_to_cpu(attr->flags); dom_info->set_limits = SUPPORTS_SET_LIMITS(flags); - dom_info->set_perf = SUPPORTS_SET_PERF_LVL(flags); + dom_info->info.set_perf = SUPPORTS_SET_PERF_LVL(flags); dom_info->perf_limit_notify = SUPPORTS_PERF_LIMIT_NOTIFY(flags); dom_info->perf_level_notify = SUPPORTS_PERF_LEVEL_NOTIFY(flags); dom_info->perf_fastchannels = SUPPORTS_PERF_FASTCHANNELS(flags); @@ -276,7 +275,8 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph, dom_info->mult_factor = (dom_info->sustained_freq_khz * 1000) / dom_info->sustained_perf_level; - strscpy(dom_info->name, attr->name, SCMI_SHORT_NAME_MAX_SIZE); + strscpy(dom_info->info.name, attr->name, + SCMI_SHORT_NAME_MAX_SIZE); } ph->xops->xfer_put(ph, t); @@ -288,7 +288,7 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph, if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 && SUPPORTS_EXTENDED_NAMES(flags)) ph->hops->extended_name_get(ph, PERF_DOMAIN_NAME_GET, - dom_info->id, dom_info->name, + dom_info->id, dom_info->info.name, SCMI_MAX_STR_SIZE); if (dom_info->level_indexing_mode) { @@ -430,6 +430,29 @@ static int scmi_perf_num_domains_get(const struct scmi_protocol_handle *ph) return pi->num_domains; } +static inline struct perf_dom_info * +scmi_perf_domain_lookup(const struct scmi_protocol_handle *ph, u32 domain) +{ + struct scmi_perf_info *pi = ph->get_priv(ph); + + if (domain >= pi->num_domains) + return ERR_PTR(-EINVAL); + + return pi->dom_info + domain; +} + +static const struct scmi_perf_domain_info * +scmi_perf_info_get(const struct scmi_protocol_handle *ph, u32 domain) +{ + struct perf_dom_info *dom; + + dom = scmi_perf_domain_lookup(ph, domain); + if (IS_ERR(dom)) + return ERR_PTR(-EINVAL); + + return &dom->info; +} + static int scmi_perf_msg_limits_set(const struct scmi_protocol_handle *ph, u32 domain, u32 max_perf, u32 min_perf) { @@ -453,17 +476,6 @@ static int scmi_perf_msg_limits_set(const struct scmi_protocol_handle *ph, return ret; } -static inline struct perf_dom_info * -scmi_perf_domain_lookup(const struct scmi_protocol_handle *ph, u32 domain) -{ - struct scmi_perf_info *pi = ph->get_priv(ph); - - if (domain >= pi->num_domains) - return ERR_PTR(-EINVAL); - - return pi->dom_info + domain; -} - static int __scmi_perf_limits_set(const struct scmi_protocol_handle *ph, struct perf_dom_info *dom, u32 max_perf, u32 min_perf) @@ -819,7 +831,7 @@ static int scmi_dvfs_device_opps_add(const struct scmi_protocol_handle *ph, } dev_dbg(dev, "[%d][%s]:: Registered OPP[%d] %lu\n", - domain, dom->name, idx, freq); + domain, dom->info.name, idx, freq); } return 0; } @@ -956,6 +968,7 @@ scmi_power_scale_get(const struct scmi_protocol_handle *ph) static const struct scmi_perf_proto_ops perf_proto_ops = { .num_domains_get = scmi_perf_num_domains_get, + .info_get = scmi_perf_info_get, .limits_set = scmi_perf_limits_set, .limits_get = scmi_perf_limits_get, .level_set = scmi_perf_level_set, diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 14791dd54706..9108fe8ed3e3 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -106,11 +106,17 @@ struct scmi_clk_proto_ops { u8 oem_type, u32 oem_val, bool atomic); }; +struct scmi_perf_domain_info { + char name[SCMI_MAX_STR_SIZE]; + bool set_perf; +}; + /** * struct scmi_perf_proto_ops - represents the various operations provided * by SCMI Performance Protocol * * @num_domains_get: gets the number of supported performance domains + * @info_get: get the information of a performance domain * @limits_set: sets limits on the performance level of a domain * @limits_get: gets limits on the performance level of a domain * @level_set: sets the performance level of a domain @@ -131,6 +137,8 @@ struct scmi_clk_proto_ops { */ struct scmi_perf_proto_ops { int (*num_domains_get)(const struct scmi_protocol_handle *ph); + const struct scmi_perf_domain_info __must_check *(*info_get) + (const struct scmi_protocol_handle *ph, u32 domain); int (*limits_set)(const struct scmi_protocol_handle *ph, u32 domain, u32 max_perf, u32 min_perf); int (*limits_get)(const struct scmi_protocol_handle *ph, u32 domain, -- cgit v1.2.3 From e336baa4193ecc788a06c0c4659e400bb53689b4 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 25 Aug 2023 13:26:23 +0200 Subject: cpufreq: scmi: Prepare to move OF parsing of domain-id to cpufreq The OF parsing of the clock domain specifier seems to better belong in the scmi cpufreq driver, rather than being implemented behind the generic ->device_domain_id() perf protocol ops. To prepare to remove the ->device_domain_id() ops, let's implement the OF parsing in the scmi cpufreq driver instead. Signed-off-by: Ulf Hansson Acked-by: Viresh Kumar Link: https://lore.kernel.org/r/20230825112633.236607-4-ulf.hansson@linaro.org Signed-off-by: Sudeep Holla --- drivers/cpufreq/scmi-cpufreq.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/cpufreq/scmi-cpufreq.c b/drivers/cpufreq/scmi-cpufreq.c index f34e6382a4c5..7d05d48c0337 100644 --- a/drivers/cpufreq/scmi-cpufreq.c +++ b/drivers/cpufreq/scmi-cpufreq.c @@ -70,13 +70,24 @@ static unsigned int scmi_cpufreq_fast_switch(struct cpufreq_policy *policy, return 0; } +static int scmi_cpu_domain_id(struct device *cpu_dev) +{ + struct of_phandle_args clkspec; + + if (of_parse_phandle_with_args(cpu_dev->of_node, "clocks", + "#clock-cells", 0, &clkspec)) + return -EINVAL; + + return clkspec.args[0]; +} + static int scmi_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask) { int cpu, domain, tdomain; struct device *tcpu_dev; - domain = perf_ops->device_domain_id(cpu_dev); + domain = scmi_cpu_domain_id(cpu_dev); if (domain < 0) return domain; @@ -88,7 +99,7 @@ scmi_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask) if (!tcpu_dev) continue; - tdomain = perf_ops->device_domain_id(tcpu_dev); + tdomain = scmi_cpu_domain_id(tcpu_dev); if (tdomain == domain) cpumask_set_cpu(cpu, cpumask); } @@ -104,7 +115,7 @@ scmi_get_cpu_power(struct device *cpu_dev, unsigned long *power, unsigned long Hz; int ret, domain; - domain = perf_ops->device_domain_id(cpu_dev); + domain = scmi_cpu_domain_id(cpu_dev); if (domain < 0) return domain; @@ -209,7 +220,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) } priv->cpu_dev = cpu_dev; - priv->domain_id = perf_ops->device_domain_id(cpu_dev); + priv->domain_id = scmi_cpu_domain_id(cpu_dev); policy->driver_data = priv; policy->freq_table = freq_table; -- cgit v1.2.3 From 39dfa5b9e1f0fa63b811a0a87f1c2fb9c76a0456 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 25 Aug 2023 13:26:24 +0200 Subject: firmware: arm_scmi: Align perf ops to use domain-id as in-parameter Most scmi_perf_proto_ops are already using an "u32 domain" as an in-parameter to indicate what performance domain we shall operate upon. However, some of the ops are using a "struct device *dev", which means that an additional OF parsing is needed each time the perf ops gets called, to find the corresponding domain-id. To avoid the above, but also to make the code more consistent, let's replace the in-parameter "struct device *dev" with an "u32 domain". Note that, this requires us to make some corresponding changes to the scmi cpufreq driver, so let's do that too. Signed-off-by: Ulf Hansson Acked-by: Viresh Kumar Link: https://lore.kernel.org/r/20230825112633.236607-5-ulf.hansson@linaro.org Signed-off-by: Sudeep Holla --- drivers/cpufreq/scmi-cpufreq.c | 14 +++++++++----- drivers/firmware/arm_scmi/perf.c | 22 ++++------------------ include/linux/scmi_protocol.h | 6 +++--- 3 files changed, 16 insertions(+), 26 deletions(-) diff --git a/drivers/cpufreq/scmi-cpufreq.c b/drivers/cpufreq/scmi-cpufreq.c index 7d05d48c0337..125e8a8421fb 100644 --- a/drivers/cpufreq/scmi-cpufreq.c +++ b/drivers/cpufreq/scmi-cpufreq.c @@ -137,7 +137,7 @@ scmi_get_cpu_power(struct device *cpu_dev, unsigned long *power, static int scmi_cpufreq_init(struct cpufreq_policy *policy) { - int ret, nr_opp; + int ret, nr_opp, domain; unsigned int latency; struct device *cpu_dev; struct scmi_data *priv; @@ -149,6 +149,10 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) return -ENODEV; } + domain = scmi_cpu_domain_id(cpu_dev); + if (domain < 0) + return domain; + priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -187,7 +191,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) */ nr_opp = dev_pm_opp_get_opp_count(cpu_dev); if (nr_opp <= 0) { - ret = perf_ops->device_opps_add(ph, cpu_dev); + ret = perf_ops->device_opps_add(ph, cpu_dev, domain); if (ret) { dev_warn(cpu_dev, "failed to add opps to the device\n"); goto out_free_cpumask; @@ -220,7 +224,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) } priv->cpu_dev = cpu_dev; - priv->domain_id = scmi_cpu_domain_id(cpu_dev); + priv->domain_id = domain; policy->driver_data = priv; policy->freq_table = freq_table; @@ -228,14 +232,14 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) /* SCMI allows DVFS request for any domain from any CPU */ policy->dvfs_possible_from_any_cpu = true; - latency = perf_ops->transition_latency_get(ph, cpu_dev); + latency = perf_ops->transition_latency_get(ph, domain); if (!latency) latency = CPUFREQ_ETERNAL; policy->cpuinfo.transition_latency = latency; policy->fast_switch_possible = - perf_ops->fast_switch_possible(ph, cpu_dev); + perf_ops->fast_switch_possible(ph, domain); return 0; diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index d20bb6b8abfa..092b51cf9596 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -795,17 +795,13 @@ static int scmi_dev_domain_id(struct device *dev) } static int scmi_dvfs_device_opps_add(const struct scmi_protocol_handle *ph, - struct device *dev) + struct device *dev, u32 domain) { - int idx, ret, domain; + int idx, ret; unsigned long freq; struct scmi_opp *opp; struct perf_dom_info *dom; - domain = scmi_dev_domain_id(dev); - if (domain < 0) - return -EINVAL; - dom = scmi_perf_domain_lookup(ph, domain); if (IS_ERR(dom)) return PTR_ERR(dom); @@ -838,15 +834,10 @@ static int scmi_dvfs_device_opps_add(const struct scmi_protocol_handle *ph, static int scmi_dvfs_transition_latency_get(const struct scmi_protocol_handle *ph, - struct device *dev) + u32 domain) { - int domain; struct perf_dom_info *dom; - domain = scmi_dev_domain_id(dev); - if (domain < 0) - return -EINVAL; - dom = scmi_perf_domain_lookup(ph, domain); if (IS_ERR(dom)) return PTR_ERR(dom); @@ -942,15 +933,10 @@ static int scmi_dvfs_est_power_get(const struct scmi_protocol_handle *ph, } static bool scmi_fast_switch_possible(const struct scmi_protocol_handle *ph, - struct device *dev) + u32 domain) { - int domain; struct perf_dom_info *dom; - domain = scmi_dev_domain_id(dev); - if (domain < 0) - return false; - dom = scmi_perf_domain_lookup(ph, domain); if (IS_ERR(dom)) return false; diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 9108fe8ed3e3..4e63766adc6f 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -149,9 +149,9 @@ struct scmi_perf_proto_ops { u32 *level, bool poll); int (*device_domain_id)(struct device *dev); int (*transition_latency_get)(const struct scmi_protocol_handle *ph, - struct device *dev); + u32 domain); int (*device_opps_add)(const struct scmi_protocol_handle *ph, - struct device *dev); + struct device *dev, u32 domain); int (*freq_set)(const struct scmi_protocol_handle *ph, u32 domain, unsigned long rate, bool poll); int (*freq_get)(const struct scmi_protocol_handle *ph, u32 domain, @@ -159,7 +159,7 @@ struct scmi_perf_proto_ops { int (*est_power_get)(const struct scmi_protocol_handle *ph, u32 domain, unsigned long *rate, unsigned long *power); bool (*fast_switch_possible)(const struct scmi_protocol_handle *ph, - struct device *dev); + u32 domain); enum scmi_power_scale (*power_scale_get)(const struct scmi_protocol_handle *ph); }; -- cgit v1.2.3 From 9b578d83629e13f81a53d1695a4f700cdb10f772 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 25 Aug 2023 13:26:25 +0200 Subject: firmware: arm_scmi: Drop redundant ->device_domain_id() from perf ops There are no longer any users of the ->device_domain_id() ops in the scmi_perf_proto_ops, therefore let's remove it. Signed-off-by: Ulf Hansson Link: https://lore.kernel.org/r/20230825112633.236607-6-ulf.hansson@linaro.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/perf.c | 13 ------------- include/linux/scmi_protocol.h | 2 -- 2 files changed, 15 deletions(-) diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index 092b51cf9596..9eb58df9124d 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -782,18 +782,6 @@ static void scmi_perf_domain_init_fc(const struct scmi_protocol_handle *ph, *p_fc = fc; } -/* Device specific ops */ -static int scmi_dev_domain_id(struct device *dev) -{ - struct of_phandle_args clkspec; - - if (of_parse_phandle_with_args(dev->of_node, "clocks", "#clock-cells", - 0, &clkspec)) - return -EINVAL; - - return clkspec.args[0]; -} - static int scmi_dvfs_device_opps_add(const struct scmi_protocol_handle *ph, struct device *dev, u32 domain) { @@ -959,7 +947,6 @@ static const struct scmi_perf_proto_ops perf_proto_ops = { .limits_get = scmi_perf_limits_get, .level_set = scmi_perf_level_set, .level_get = scmi_perf_level_get, - .device_domain_id = scmi_dev_domain_id, .transition_latency_get = scmi_dvfs_transition_latency_get, .device_opps_add = scmi_dvfs_device_opps_add, .freq_set = scmi_dvfs_freq_set, diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 4e63766adc6f..27bfa5a65b45 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -121,7 +121,6 @@ struct scmi_perf_domain_info { * @limits_get: gets limits on the performance level of a domain * @level_set: sets the performance level of a domain * @level_get: gets the performance level of a domain - * @device_domain_id: gets the scmi domain id for a given device * @transition_latency_get: gets the DVFS transition latency for a given device * @device_opps_add: adds all the OPPs for a given device * @freq_set: sets the frequency for a given device using sustained frequency @@ -147,7 +146,6 @@ struct scmi_perf_proto_ops { u32 level, bool poll); int (*level_get)(const struct scmi_protocol_handle *ph, u32 domain, u32 *level, bool poll); - int (*device_domain_id)(struct device *dev); int (*transition_latency_get)(const struct scmi_protocol_handle *ph, u32 domain); int (*device_opps_add)(const struct scmi_protocol_handle *ph, -- cgit v1.2.3 From 4f1f0bc8ed1647007ad4ad8d2b8ce0092bb22d43 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 25 Aug 2023 13:26:26 +0200 Subject: cpufreq: scmi: Avoid one OF parsing in scmi_get_sharing_cpus() The domain-id for the cpu_dev has already been parsed at the point when scmi_get_sharing_cpus() is getting called. Let's pass it as an in-parameter to avoid the unnecessary OF parsing. Signed-off-by: Ulf Hansson Acked-by: Viresh Kumar Link: https://lore.kernel.org/r/20230825112633.236607-7-ulf.hansson@linaro.org Signed-off-by: Sudeep Holla --- drivers/cpufreq/scmi-cpufreq.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/cpufreq/scmi-cpufreq.c b/drivers/cpufreq/scmi-cpufreq.c index 125e8a8421fb..78f53e388094 100644 --- a/drivers/cpufreq/scmi-cpufreq.c +++ b/drivers/cpufreq/scmi-cpufreq.c @@ -82,15 +82,12 @@ static int scmi_cpu_domain_id(struct device *cpu_dev) } static int -scmi_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask) +scmi_get_sharing_cpus(struct device *cpu_dev, int domain, + struct cpumask *cpumask) { - int cpu, domain, tdomain; + int cpu, tdomain; struct device *tcpu_dev; - domain = scmi_cpu_domain_id(cpu_dev); - if (domain < 0) - return domain; - for_each_possible_cpu(cpu) { if (cpu == cpu_dev->id) continue; @@ -163,7 +160,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) } /* Obtain CPUs that share SCMI performance controls */ - ret = scmi_get_sharing_cpus(cpu_dev, policy->cpus); + ret = scmi_get_sharing_cpus(cpu_dev, domain, policy->cpus); if (ret) { dev_warn(cpu_dev, "failed to get sharing cpumask\n"); goto out_free_cpumask; -- cgit v1.2.3 From 6e429adc60b1fa87b6e89d68cb9d1c0a8224d58a Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 25 Aug 2023 13:26:28 +0200 Subject: dt-bindings: arm: cpus: Add a power-domain-name for a performance-domain When an CPU's performance domain is managed through the SCMI firmware, let's enable us describe this as a consumer of a power-domain provider, which is the de-facto standard to use for performance domains. In this case, let's specify a corresponding power-domain-name, to point out the corresponding index for it. Cc: Rob Herring Cc: Krzysztof Kozlowski Cc: Conor Dooley Cc: devicetree@vger.kernel.org Cc: Lorenzo Pieralisi Signed-off-by: Ulf Hansson Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20230825112633.236607-9-ulf.hansson@linaro.org Signed-off-by: Sudeep Holla --- Documentation/devicetree/bindings/arm/cpus.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/arm/cpus.yaml b/Documentation/devicetree/bindings/arm/cpus.yaml index 0d17cbad9778..ad7fee0245fa 100644 --- a/Documentation/devicetree/bindings/arm/cpus.yaml +++ b/Documentation/devicetree/bindings/arm/cpus.yaml @@ -308,7 +308,9 @@ properties: power-domains property. For PSCI based platforms, the name corresponding to the index of the PSCI - PM domain provider, must be "psci". + PM domain provider, must be "psci". For SCMI based platforms, the name + corresponding to the index of an SCMI performance domain provider, must be + "perf". qcom,saw: $ref: /schemas/types.yaml#/definitions/phandle -- cgit v1.2.3 From e11c480b6df1942b8f9a6958c2d881d8a9a9fb3b Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 25 Aug 2023 13:26:29 +0200 Subject: dt-bindings: firmware: arm,scmi: Extend bindings for protocol@13 The protocol@13 node is describing the performance scaling option for the ARM SCMI interface, as a clock provider. This is unnecessary limiting, as performance scaling is in many cases not limited to switching a clock's frequency. Therefore, let's extend the binding so the interface can be modelled as a generic performance domain too. The common way to describe this, is to use the power-domain DT bindings, so let's use that. Cc: Rob Herring Cc: Krzysztof Kozlowski Cc: Conor Dooley Cc: devicetree@vger.kernel.org Signed-off-by: Ulf Hansson Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20230825112633.236607-10-ulf.hansson@linaro.org Signed-off-by: Sudeep Holla --- Documentation/devicetree/bindings/firmware/arm,scmi.yaml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml index b138f3d23df8..563a87dfb31a 100644 --- a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml +++ b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml @@ -149,8 +149,15 @@ properties: '#clock-cells': const: 1 - required: - - '#clock-cells' + '#power-domain-cells': + const: 1 + + oneOf: + - required: + - '#clock-cells' + + - required: + - '#power-domain-cells' protocol@14: $ref: '#/$defs/protocol-node' -- cgit v1.2.3 From 0ead1f3e158c44aa274f2d5c49be947fbfcdffe0 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 25 Aug 2023 13:26:30 +0200 Subject: dt-bindings: power: Clarify performance capabilities of power-domains The power-domains bindings has in many years been used to describe so called performance-domains too. Rather than using a separate binding it has been convenient to re-use the power-domain bindings, as in some cases it's in fact a combination of the both that would be the best description. Therefore, let's make it more clear that the power-domains bindings can be used to describe a performance-domain too. Cc: "Rafael J. Wysocki" Cc: Kevin Hilman Cc: Rob Herring Cc: Krzysztof Kozlowski Cc: Conor Dooley Cc: devicetree@vger.kernel.org Signed-off-by: Ulf Hansson Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20230825112633.236607-11-ulf.hansson@linaro.org Signed-off-by: Sudeep Holla --- .../devicetree/bindings/power/power-domain.yaml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/power/power-domain.yaml b/Documentation/devicetree/bindings/power/power-domain.yaml index d1235e562041..8fdb529d560b 100644 --- a/Documentation/devicetree/bindings/power/power-domain.yaml +++ b/Documentation/devicetree/bindings/power/power-domain.yaml @@ -13,8 +13,9 @@ maintainers: description: |+ System on chip designs are often divided into multiple PM domains that can be - used for power gating of selected IP blocks for power saving by reduced leakage - current. + used for power gating of selected IP blocks for power saving by reduced + leakage current. Moreover, in some cases the similar PM domains may also be + capable of scaling performance for a group of IP blocks. This device tree binding can be used to bind PM domain consumer devices with their PM domains provided by PM domain providers. A PM domain provider can be @@ -25,7 +26,7 @@ description: |+ properties: $nodename: - pattern: "^(power-controller|power-domain)([@-].*)?$" + pattern: "^(power-controller|power-domain|performance-domain)([@-].*)?$" domain-idle-states: $ref: /schemas/types.yaml#/definitions/phandle-array @@ -44,11 +45,11 @@ properties: operating-points-v2: description: - Phandles to the OPP tables of power domains provided by a power domain - provider. If the provider provides a single power domain only or all - the power domains provided by the provider have identical OPP tables, - then this shall contain a single phandle. Refer to ../opp/opp-v2-base.yaml - for more information. + Phandles to the OPP tables of power domains that are capable of scaling + performance, provided by a power domain provider. If the provider provides + a single power domain only or all the power domains provided by the + provider have identical OPP tables, then this shall contain a single + phandle. Refer to ../opp/opp-v2-base.yaml for more information. "#power-domain-cells": description: -- cgit v1.2.3 From 92b2028b00ff987272a10fee980c7412ae7ebea6 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 25 Aug 2023 13:26:31 +0200 Subject: cpufreq: scmi: Add support to parse domain-id using #power-domain-cells The performance domain-id can be described in DT using the power-domains property or the clock property. The latter is already supported, so let's add support for the power-domains too. Signed-off-by: Ulf Hansson Acked-by: Viresh Kumar Link: https://lore.kernel.org/r/20230825112633.236607-12-ulf.hansson@linaro.org Signed-off-by: Sudeep Holla --- drivers/cpufreq/scmi-cpufreq.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/cpufreq/scmi-cpufreq.c b/drivers/cpufreq/scmi-cpufreq.c index 78f53e388094..c8a7ccc42c16 100644 --- a/drivers/cpufreq/scmi-cpufreq.c +++ b/drivers/cpufreq/scmi-cpufreq.c @@ -72,13 +72,25 @@ static unsigned int scmi_cpufreq_fast_switch(struct cpufreq_policy *policy, static int scmi_cpu_domain_id(struct device *cpu_dev) { - struct of_phandle_args clkspec; - - if (of_parse_phandle_with_args(cpu_dev->of_node, "clocks", - "#clock-cells", 0, &clkspec)) - return -EINVAL; + struct device_node *np = cpu_dev->of_node; + struct of_phandle_args domain_id; + int index; + + if (of_parse_phandle_with_args(np, "clocks", "#clock-cells", 0, + &domain_id)) { + /* Find the corresponding index for power-domain "perf". */ + index = of_property_match_string(np, "power-domain-names", + "perf"); + if (index < 0) + return -EINVAL; + + if (of_parse_phandle_with_args(np, "power-domains", + "#power-domain-cells", index, + &domain_id)) + return -EINVAL; + } - return clkspec.args[0]; + return domain_id.args[0]; } static int -- cgit v1.2.3 From 3dd91515ef43dd43e32e2a84e4bd881b64fb33ae Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 25 Aug 2023 13:26:32 +0200 Subject: PM: domains: Allow genpd providers to manage OPP tables directly by its FW In some cases the OPP tables aren't specified in device tree, but rather encoded in the FW. To allow a genpd provider to specify them dynamically instead, let's add a new genpd flag, GENPD_FLAG_OPP_TABLE_FW. Signed-off-by: Ulf Hansson Link: https://lore.kernel.org/r/20230825112633.236607-13-ulf.hansson@linaro.org Signed-off-by: Sudeep Holla --- drivers/base/power/domain.c | 11 ++++++----- include/linux/pm_domain.h | 5 +++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 5cb2023581d4..c74edf80417f 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -130,6 +130,7 @@ static const struct genpd_lock_ops genpd_spin_ops = { #define genpd_is_active_wakeup(genpd) (genpd->flags & GENPD_FLAG_ACTIVE_WAKEUP) #define genpd_is_cpu_domain(genpd) (genpd->flags & GENPD_FLAG_CPU_DOMAIN) #define genpd_is_rpm_always_on(genpd) (genpd->flags & GENPD_FLAG_RPM_ALWAYS_ON) +#define genpd_is_opp_table_fw(genpd) (genpd->flags & GENPD_FLAG_OPP_TABLE_FW) static inline bool irq_safe_dev_in_sleep_domain(struct device *dev, const struct generic_pm_domain *genpd) @@ -2328,7 +2329,7 @@ int of_genpd_add_provider_simple(struct device_node *np, genpd->dev.of_node = np; /* Parse genpd OPP table */ - if (genpd->set_performance_state) { + if (!genpd_is_opp_table_fw(genpd) && genpd->set_performance_state) { ret = dev_pm_opp_of_add_table(&genpd->dev); if (ret) return dev_err_probe(&genpd->dev, ret, "Failed to add OPP table\n"); @@ -2343,7 +2344,7 @@ int of_genpd_add_provider_simple(struct device_node *np, ret = genpd_add_provider(np, genpd_xlate_simple, genpd); if (ret) { - if (genpd->set_performance_state) { + if (!genpd_is_opp_table_fw(genpd) && genpd->set_performance_state) { dev_pm_opp_put_opp_table(genpd->opp_table); dev_pm_opp_of_remove_table(&genpd->dev); } @@ -2387,7 +2388,7 @@ int of_genpd_add_provider_onecell(struct device_node *np, genpd->dev.of_node = np; /* Parse genpd OPP table */ - if (genpd->set_performance_state) { + if (!genpd_is_opp_table_fw(genpd) && genpd->set_performance_state) { ret = dev_pm_opp_of_add_table_indexed(&genpd->dev, i); if (ret) { dev_err_probe(&genpd->dev, ret, @@ -2423,7 +2424,7 @@ error: genpd->provider = NULL; genpd->has_provider = false; - if (genpd->set_performance_state) { + if (!genpd_is_opp_table_fw(genpd) && genpd->set_performance_state) { dev_pm_opp_put_opp_table(genpd->opp_table); dev_pm_opp_of_remove_table(&genpd->dev); } @@ -2455,7 +2456,7 @@ void of_genpd_del_provider(struct device_node *np) if (gpd->provider == &np->fwnode) { gpd->has_provider = false; - if (!gpd->set_performance_state) + if (genpd_is_opp_table_fw(gpd) || !gpd->set_performance_state) continue; dev_pm_opp_put_opp_table(gpd->opp_table); diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index f776fb93eaa0..05ad8cefdff1 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -61,6 +61,10 @@ * GENPD_FLAG_MIN_RESIDENCY: Enable the genpd governor to consider its * components' next wakeup when determining the * optimal idle state. + * + * GENPD_FLAG_OPP_TABLE_FW: The genpd provider supports performance states, + * but its corresponding OPP tables are not + * described in DT, but are given directly by FW. */ #define GENPD_FLAG_PM_CLK (1U << 0) #define GENPD_FLAG_IRQ_SAFE (1U << 1) @@ -69,6 +73,7 @@ #define GENPD_FLAG_CPU_DOMAIN (1U << 4) #define GENPD_FLAG_RPM_ALWAYS_ON (1U << 5) #define GENPD_FLAG_MIN_RESIDENCY (1U << 6) +#define GENPD_FLAG_OPP_TABLE_FW (1U << 7) enum gpd_status { GENPD_STATE_ON = 0, /* PM domain is on */ -- cgit v1.2.3 From 2af23ceb8624a419eaf40295c11fcb86ec9ee303 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 19 Sep 2023 14:16:05 +0200 Subject: pmdomain: arm: Add the SCMI performance domain To enable support for performance scaling (DVFS) for generic devices with the SCMI performance protocol, let's add an SCMI performance domain. This is being modelled as a genpd provider, with support for performance scaling through genpd's ->set_performance_state() callback. Note that, this adds the initial support that allows consumer drivers for attached devices, to vote for a new performance state via calling the dev_pm_genpd_set_performance_state(). However, this should be avoided as it's in most cases preferred to use the OPP library to vote for a new OPP instead. The support using the OPP library isn't part of this change, but needs to be implemented from subsequent changes. Signed-off-by: Ulf Hansson Link: https://lore.kernel.org/r/20230919121605.7304-1-ulf.hansson@linaro.org Signed-off-by: Sudeep Holla --- MAINTAINERS | 1 + drivers/firmware/arm_scmi/Kconfig | 12 +++ drivers/pmdomain/Makefile | 1 + drivers/pmdomain/arm/Makefile | 3 + drivers/pmdomain/arm/scmi_perf_domain.c | 150 ++++++++++++++++++++++++++++++++ 5 files changed, 167 insertions(+) create mode 100644 drivers/pmdomain/arm/Makefile create mode 100644 drivers/pmdomain/arm/scmi_perf_domain.c diff --git a/MAINTAINERS b/MAINTAINERS index bf0f54c24f81..aa8c58b16416 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20902,6 +20902,7 @@ F: drivers/clk/clk-sc[mp]i.c F: drivers/cpufreq/sc[mp]i-cpufreq.c F: drivers/firmware/arm_scmi/ F: drivers/firmware/arm_scpi.c +F: drivers/pmdomain/arm/ F: drivers/powercap/arm_scmi_powercap.c F: drivers/regulator/scmi-regulator.c F: drivers/reset/reset-scmi.c diff --git a/drivers/firmware/arm_scmi/Kconfig b/drivers/firmware/arm_scmi/Kconfig index ea0f5083ac47..706d1264d038 100644 --- a/drivers/firmware/arm_scmi/Kconfig +++ b/drivers/firmware/arm_scmi/Kconfig @@ -181,6 +181,18 @@ config ARM_SCMI_POWER_DOMAIN will be called scmi_pm_domain. Note this may needed early in boot before rootfs may be available. +config ARM_SCMI_PERF_DOMAIN + tristate "SCMI performance domain driver" + depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF) + default y + select PM_GENERIC_DOMAINS if PM + help + This enables support for the SCMI performance domains which can be + enabled or disabled via the SCP firmware. + + This driver can also be built as a module. If so, the module will be + called scmi_perf_domain. + config ARM_SCMI_POWER_CONTROL tristate "SCMI system power control driver" depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF) diff --git a/drivers/pmdomain/Makefile b/drivers/pmdomain/Makefile index 666753676e5c..f0326b27b30b 100644 --- a/drivers/pmdomain/Makefile +++ b/drivers/pmdomain/Makefile @@ -2,6 +2,7 @@ obj-y += actions/ obj-y += amlogic/ obj-y += apple/ +obj-y += arm/ obj-y += bcm/ obj-y += imx/ obj-y += mediatek/ diff --git a/drivers/pmdomain/arm/Makefile b/drivers/pmdomain/arm/Makefile new file mode 100644 index 000000000000..7128db96deac --- /dev/null +++ b/drivers/pmdomain/arm/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_ARM_SCMI_PERF_DOMAIN) += scmi_perf_domain.o diff --git a/drivers/pmdomain/arm/scmi_perf_domain.c b/drivers/pmdomain/arm/scmi_perf_domain.c new file mode 100644 index 000000000000..aa100270500f --- /dev/null +++ b/drivers/pmdomain/arm/scmi_perf_domain.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SCMI performance domain support. + * + * Copyright (C) 2023 Linaro Ltd. + */ + +#include +#include +#include +#include +#include +#include + +struct scmi_perf_domain { + struct generic_pm_domain genpd; + const struct scmi_perf_proto_ops *perf_ops; + const struct scmi_protocol_handle *ph; + const struct scmi_perf_domain_info *info; + u32 domain_id; +}; + +#define to_scmi_pd(pd) container_of(pd, struct scmi_perf_domain, genpd) + +static int +scmi_pd_set_perf_state(struct generic_pm_domain *genpd, unsigned int state) +{ + struct scmi_perf_domain *pd = to_scmi_pd(genpd); + int ret; + + if (!pd->info->set_perf) + return 0; + + if (!state) + return -EINVAL; + + ret = pd->perf_ops->level_set(pd->ph, pd->domain_id, state, true); + if (ret) + dev_warn(&genpd->dev, "Failed with %d when trying to set %d perf level", + ret, state); + + return ret; +} + +static int scmi_perf_domain_probe(struct scmi_device *sdev) +{ + struct device *dev = &sdev->dev; + const struct scmi_handle *handle = sdev->handle; + const struct scmi_perf_proto_ops *perf_ops; + struct scmi_protocol_handle *ph; + struct scmi_perf_domain *scmi_pd; + struct genpd_onecell_data *scmi_pd_data; + struct generic_pm_domain **domains; + int num_domains, i, ret = 0; + + if (!handle) + return -ENODEV; + + /* The OF node must specify us as a power-domain provider. */ + if (!of_find_property(dev->of_node, "#power-domain-cells", NULL)) + return 0; + + perf_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_PERF, &ph); + if (IS_ERR(perf_ops)) + return PTR_ERR(perf_ops); + + num_domains = perf_ops->num_domains_get(ph); + if (num_domains < 0) { + dev_warn(dev, "Failed with %d when getting num perf domains\n", + num_domains); + return num_domains; + } else if (!num_domains) { + return 0; + } + + scmi_pd = devm_kcalloc(dev, num_domains, sizeof(*scmi_pd), GFP_KERNEL); + if (!scmi_pd) + return -ENOMEM; + + scmi_pd_data = devm_kzalloc(dev, sizeof(*scmi_pd_data), GFP_KERNEL); + if (!scmi_pd_data) + return -ENOMEM; + + domains = devm_kcalloc(dev, num_domains, sizeof(*domains), GFP_KERNEL); + if (!domains) + return -ENOMEM; + + for (i = 0; i < num_domains; i++, scmi_pd++) { + scmi_pd->info = perf_ops->info_get(ph, i); + + scmi_pd->domain_id = i; + scmi_pd->perf_ops = perf_ops; + scmi_pd->ph = ph; + scmi_pd->genpd.name = scmi_pd->info->name; + scmi_pd->genpd.flags = GENPD_FLAG_ALWAYS_ON | + GENPD_FLAG_OPP_TABLE_FW; + scmi_pd->genpd.set_performance_state = scmi_pd_set_perf_state; + + ret = pm_genpd_init(&scmi_pd->genpd, NULL, false); + if (ret) + goto err; + + domains[i] = &scmi_pd->genpd; + } + + scmi_pd_data->domains = domains; + scmi_pd_data->num_domains = num_domains; + + ret = of_genpd_add_provider_onecell(dev->of_node, scmi_pd_data); + if (ret) + goto err; + + dev_set_drvdata(dev, scmi_pd_data); + dev_info(dev, "Initialized %d performance domains", num_domains); + return 0; +err: + for (i--; i >= 0; i--) + pm_genpd_remove(domains[i]); + return ret; +} + +static void scmi_perf_domain_remove(struct scmi_device *sdev) +{ + struct device *dev = &sdev->dev; + struct genpd_onecell_data *scmi_pd_data = dev_get_drvdata(dev); + int i; + + of_genpd_del_provider(dev->of_node); + + for (i = 0; i < scmi_pd_data->num_domains; i++) + pm_genpd_remove(scmi_pd_data->domains[i]); +} + +static const struct scmi_device_id scmi_id_table[] = { + { SCMI_PROTOCOL_PERF, "perf" }, + { }, +}; +MODULE_DEVICE_TABLE(scmi, scmi_id_table); + +static struct scmi_driver scmi_perf_domain_driver = { + .name = "scmi-perf-domain", + .probe = scmi_perf_domain_probe, + .remove = scmi_perf_domain_remove, + .id_table = scmi_id_table, +}; +module_scmi_driver(scmi_perf_domain_driver); + +MODULE_AUTHOR("Ulf Hansson "); +MODULE_DESCRIPTION("ARM SCMI perf domain driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From af78e5c309c4b797168c8ce87c60ca06b26b1e73 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 21 Sep 2023 12:24:15 +0100 Subject: firmware: arm_scmi: Move power-domain driver to the pmdomain dir To simplify with maintenance let's move the Arm SCMI power-domain driver to the new pmdomain directory. Link: https://lore.kernel.org/all/20230921113328.3208651-1-sudeep.holla@arm.com Reviewed-by: Ulf Hansson Cc: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/Makefile | 1 - drivers/firmware/arm_scmi/scmi_pm_domain.c | 153 ----------------------------- drivers/pmdomain/arm/Makefile | 1 + drivers/pmdomain/arm/scmi_pm_domain.c | 153 +++++++++++++++++++++++++++++ 4 files changed, 154 insertions(+), 154 deletions(-) delete mode 100644 drivers/firmware/arm_scmi/scmi_pm_domain.c create mode 100644 drivers/pmdomain/arm/scmi_pm_domain.c diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile index b31d78fa66cc..a7bc4796519c 100644 --- a/drivers/firmware/arm_scmi/Makefile +++ b/drivers/firmware/arm_scmi/Makefile @@ -16,7 +16,6 @@ scmi-module-objs := $(scmi-driver-y) $(scmi-protocols-y) $(scmi-transport-y) obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-core.o obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o -obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o obj-$(CONFIG_ARM_SCMI_POWER_CONTROL) += scmi_power_control.o ifeq ($(CONFIG_THUMB2_KERNEL)$(CONFIG_CC_IS_CLANG),yy) diff --git a/drivers/firmware/arm_scmi/scmi_pm_domain.c b/drivers/firmware/arm_scmi/scmi_pm_domain.c deleted file mode 100644 index 0e05a79de82d..000000000000 --- a/drivers/firmware/arm_scmi/scmi_pm_domain.c +++ /dev/null @@ -1,153 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * SCMI Generic power domain support. - * - * Copyright (C) 2018-2021 ARM Ltd. - */ - -#include -#include -#include -#include -#include - -static const struct scmi_power_proto_ops *power_ops; - -struct scmi_pm_domain { - struct generic_pm_domain genpd; - const struct scmi_protocol_handle *ph; - const char *name; - u32 domain; -}; - -#define to_scmi_pd(gpd) container_of(gpd, struct scmi_pm_domain, genpd) - -static int scmi_pd_power(struct generic_pm_domain *domain, bool power_on) -{ - int ret; - u32 state, ret_state; - struct scmi_pm_domain *pd = to_scmi_pd(domain); - - if (power_on) - state = SCMI_POWER_STATE_GENERIC_ON; - else - state = SCMI_POWER_STATE_GENERIC_OFF; - - ret = power_ops->state_set(pd->ph, pd->domain, state); - if (!ret) - ret = power_ops->state_get(pd->ph, pd->domain, &ret_state); - if (!ret && state != ret_state) - return -EIO; - - return ret; -} - -static int scmi_pd_power_on(struct generic_pm_domain *domain) -{ - return scmi_pd_power(domain, true); -} - -static int scmi_pd_power_off(struct generic_pm_domain *domain) -{ - return scmi_pd_power(domain, false); -} - -static int scmi_pm_domain_probe(struct scmi_device *sdev) -{ - int num_domains, i; - struct device *dev = &sdev->dev; - struct device_node *np = dev->of_node; - struct scmi_pm_domain *scmi_pd; - struct genpd_onecell_data *scmi_pd_data; - struct generic_pm_domain **domains; - const struct scmi_handle *handle = sdev->handle; - struct scmi_protocol_handle *ph; - - if (!handle) - return -ENODEV; - - power_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_POWER, &ph); - if (IS_ERR(power_ops)) - return PTR_ERR(power_ops); - - num_domains = power_ops->num_domains_get(ph); - if (num_domains < 0) { - dev_err(dev, "number of domains not found\n"); - return num_domains; - } - - scmi_pd = devm_kcalloc(dev, num_domains, sizeof(*scmi_pd), GFP_KERNEL); - if (!scmi_pd) - return -ENOMEM; - - scmi_pd_data = devm_kzalloc(dev, sizeof(*scmi_pd_data), GFP_KERNEL); - if (!scmi_pd_data) - return -ENOMEM; - - domains = devm_kcalloc(dev, num_domains, sizeof(*domains), GFP_KERNEL); - if (!domains) - return -ENOMEM; - - for (i = 0; i < num_domains; i++, scmi_pd++) { - u32 state; - - if (power_ops->state_get(ph, i, &state)) { - dev_warn(dev, "failed to get state for domain %d\n", i); - continue; - } - - scmi_pd->domain = i; - scmi_pd->ph = ph; - scmi_pd->name = power_ops->name_get(ph, i); - scmi_pd->genpd.name = scmi_pd->name; - scmi_pd->genpd.power_off = scmi_pd_power_off; - scmi_pd->genpd.power_on = scmi_pd_power_on; - - pm_genpd_init(&scmi_pd->genpd, NULL, - state == SCMI_POWER_STATE_GENERIC_OFF); - - domains[i] = &scmi_pd->genpd; - } - - scmi_pd_data->domains = domains; - scmi_pd_data->num_domains = num_domains; - - dev_set_drvdata(dev, scmi_pd_data); - - return of_genpd_add_provider_onecell(np, scmi_pd_data); -} - -static void scmi_pm_domain_remove(struct scmi_device *sdev) -{ - int i; - struct genpd_onecell_data *scmi_pd_data; - struct device *dev = &sdev->dev; - struct device_node *np = dev->of_node; - - of_genpd_del_provider(np); - - scmi_pd_data = dev_get_drvdata(dev); - for (i = 0; i < scmi_pd_data->num_domains; i++) { - if (!scmi_pd_data->domains[i]) - continue; - pm_genpd_remove(scmi_pd_data->domains[i]); - } -} - -static const struct scmi_device_id scmi_id_table[] = { - { SCMI_PROTOCOL_POWER, "genpd" }, - { }, -}; -MODULE_DEVICE_TABLE(scmi, scmi_id_table); - -static struct scmi_driver scmi_power_domain_driver = { - .name = "scmi-power-domain", - .probe = scmi_pm_domain_probe, - .remove = scmi_pm_domain_remove, - .id_table = scmi_id_table, -}; -module_scmi_driver(scmi_power_domain_driver); - -MODULE_AUTHOR("Sudeep Holla "); -MODULE_DESCRIPTION("ARM SCMI power domain driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/pmdomain/arm/Makefile b/drivers/pmdomain/arm/Makefile index 7128db96deac..cfcb1f6cdd90 100644 --- a/drivers/pmdomain/arm/Makefile +++ b/drivers/pmdomain/arm/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_ARM_SCMI_PERF_DOMAIN) += scmi_perf_domain.o +obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o diff --git a/drivers/pmdomain/arm/scmi_pm_domain.c b/drivers/pmdomain/arm/scmi_pm_domain.c new file mode 100644 index 000000000000..0e05a79de82d --- /dev/null +++ b/drivers/pmdomain/arm/scmi_pm_domain.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SCMI Generic power domain support. + * + * Copyright (C) 2018-2021 ARM Ltd. + */ + +#include +#include +#include +#include +#include + +static const struct scmi_power_proto_ops *power_ops; + +struct scmi_pm_domain { + struct generic_pm_domain genpd; + const struct scmi_protocol_handle *ph; + const char *name; + u32 domain; +}; + +#define to_scmi_pd(gpd) container_of(gpd, struct scmi_pm_domain, genpd) + +static int scmi_pd_power(struct generic_pm_domain *domain, bool power_on) +{ + int ret; + u32 state, ret_state; + struct scmi_pm_domain *pd = to_scmi_pd(domain); + + if (power_on) + state = SCMI_POWER_STATE_GENERIC_ON; + else + state = SCMI_POWER_STATE_GENERIC_OFF; + + ret = power_ops->state_set(pd->ph, pd->domain, state); + if (!ret) + ret = power_ops->state_get(pd->ph, pd->domain, &ret_state); + if (!ret && state != ret_state) + return -EIO; + + return ret; +} + +static int scmi_pd_power_on(struct generic_pm_domain *domain) +{ + return scmi_pd_power(domain, true); +} + +static int scmi_pd_power_off(struct generic_pm_domain *domain) +{ + return scmi_pd_power(domain, false); +} + +static int scmi_pm_domain_probe(struct scmi_device *sdev) +{ + int num_domains, i; + struct device *dev = &sdev->dev; + struct device_node *np = dev->of_node; + struct scmi_pm_domain *scmi_pd; + struct genpd_onecell_data *scmi_pd_data; + struct generic_pm_domain **domains; + const struct scmi_handle *handle = sdev->handle; + struct scmi_protocol_handle *ph; + + if (!handle) + return -ENODEV; + + power_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_POWER, &ph); + if (IS_ERR(power_ops)) + return PTR_ERR(power_ops); + + num_domains = power_ops->num_domains_get(ph); + if (num_domains < 0) { + dev_err(dev, "number of domains not found\n"); + return num_domains; + } + + scmi_pd = devm_kcalloc(dev, num_domains, sizeof(*scmi_pd), GFP_KERNEL); + if (!scmi_pd) + return -ENOMEM; + + scmi_pd_data = devm_kzalloc(dev, sizeof(*scmi_pd_data), GFP_KERNEL); + if (!scmi_pd_data) + return -ENOMEM; + + domains = devm_kcalloc(dev, num_domains, sizeof(*domains), GFP_KERNEL); + if (!domains) + return -ENOMEM; + + for (i = 0; i < num_domains; i++, scmi_pd++) { + u32 state; + + if (power_ops->state_get(ph, i, &state)) { + dev_warn(dev, "failed to get state for domain %d\n", i); + continue; + } + + scmi_pd->domain = i; + scmi_pd->ph = ph; + scmi_pd->name = power_ops->name_get(ph, i); + scmi_pd->genpd.name = scmi_pd->name; + scmi_pd->genpd.power_off = scmi_pd_power_off; + scmi_pd->genpd.power_on = scmi_pd_power_on; + + pm_genpd_init(&scmi_pd->genpd, NULL, + state == SCMI_POWER_STATE_GENERIC_OFF); + + domains[i] = &scmi_pd->genpd; + } + + scmi_pd_data->domains = domains; + scmi_pd_data->num_domains = num_domains; + + dev_set_drvdata(dev, scmi_pd_data); + + return of_genpd_add_provider_onecell(np, scmi_pd_data); +} + +static void scmi_pm_domain_remove(struct scmi_device *sdev) +{ + int i; + struct genpd_onecell_data *scmi_pd_data; + struct device *dev = &sdev->dev; + struct device_node *np = dev->of_node; + + of_genpd_del_provider(np); + + scmi_pd_data = dev_get_drvdata(dev); + for (i = 0; i < scmi_pd_data->num_domains; i++) { + if (!scmi_pd_data->domains[i]) + continue; + pm_genpd_remove(scmi_pd_data->domains[i]); + } +} + +static const struct scmi_device_id scmi_id_table[] = { + { SCMI_PROTOCOL_POWER, "genpd" }, + { }, +}; +MODULE_DEVICE_TABLE(scmi, scmi_id_table); + +static struct scmi_driver scmi_power_domain_driver = { + .name = "scmi-power-domain", + .probe = scmi_pm_domain_probe, + .remove = scmi_pm_domain_remove, + .id_table = scmi_id_table, +}; +module_scmi_driver(scmi_power_domain_driver); + +MODULE_AUTHOR("Sudeep Holla "); +MODULE_DESCRIPTION("ARM SCMI power domain driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From c1f2c81631df7654c249a457b7a54bbc36042248 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 22 Sep 2023 10:52:15 -0700 Subject: memory: atmel-ebi: Annotate struct atmel_ebi_dev with __counted_by Prepare for the coming implementation by GCC and Clang of the __counted_by attribute. Flexible array members annotated with __counted_by can have their accesses bounds-checked at run-time checking via CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for strcpy/memcpy-family functions). As found with Coccinelle[1], add __counted_by for struct atmel_ebi_dev. [1] https://github.com/kees/kernel-tools/blob/trunk/coccinelle/examples/counted_by.cocci Cc: Nicolas Ferre Cc: Alexandre Belloni Cc: Claudiu Beznea Signed-off-by: Kees Cook Reviewed-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20230922175215.work.122-kees@kernel.org Signed-off-by: Krzysztof Kozlowski --- drivers/memory/atmel-ebi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/memory/atmel-ebi.c b/drivers/memory/atmel-ebi.c index 635966d705cb..ec6100bc94c5 100644 --- a/drivers/memory/atmel-ebi.c +++ b/drivers/memory/atmel-ebi.c @@ -30,7 +30,7 @@ struct atmel_ebi_dev { struct atmel_ebi *ebi; u32 mode; int numcs; - struct atmel_ebi_dev_config configs[]; + struct atmel_ebi_dev_config configs[] __counted_by(numcs); }; struct atmel_ebi_caps { -- cgit v1.2.3 From 22dedf8f4570897f15e156ac5547cef93e802182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:07 +0200 Subject: soc/microchip: mpfs-sys-controller: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However, the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here, there is a quest to make the remove callback return void. In the first step of this quest, all drivers are converted to .remove_new(), which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Signed-off-by: Conor Dooley --- drivers/soc/microchip/mpfs-sys-controller.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/microchip/mpfs-sys-controller.c b/drivers/soc/microchip/mpfs-sys-controller.c index fbcd5fd24d7c..0935e9e94172 100644 --- a/drivers/soc/microchip/mpfs-sys-controller.c +++ b/drivers/soc/microchip/mpfs-sys-controller.c @@ -149,13 +149,11 @@ static int mpfs_sys_controller_probe(struct platform_device *pdev) return 0; } -static int mpfs_sys_controller_remove(struct platform_device *pdev) +static void mpfs_sys_controller_remove(struct platform_device *pdev) { struct mpfs_sys_controller *sys_controller = platform_get_drvdata(pdev); mpfs_sys_controller_put(sys_controller); - - return 0; } static const struct of_device_id mpfs_sys_controller_of_match[] = { @@ -207,7 +205,7 @@ static struct platform_driver mpfs_sys_controller_driver = { .of_match_table = mpfs_sys_controller_of_match, }, .probe = mpfs_sys_controller_probe, - .remove = mpfs_sys_controller_remove, + .remove_new = mpfs_sys_controller_remove, }; module_platform_driver(mpfs_sys_controller_driver); -- cgit v1.2.3 From cb289ce6e2ce4cee594285d0c1e0249ae868c1de Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 22 Sep 2023 10:52:35 -0700 Subject: bus: vexpress-config: Annotate struct vexpress_syscfg_func with __counted_by Prepare for the coming implementation by GCC and Clang of the __counted_by attribute. Flexible array members annotated with __counted_by can have their accesses bounds-checked at run-time checking via CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for strcpy/memcpy-family functions). As found with Coccinelle[1], add __counted_by for struct vexpress_syscfg_func. [1] https://github.com/kees/kernel-tools/blob/trunk/coccinelle/examples/counted_by.cocci Cc: Liviu Dudau Cc: Sudeep Holla Cc: Lorenzo Pieralisi Cc: Rob Herring Cc: Yangtao Li Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Kees Cook Reviewed-by: "Gustavo A. R. Silva" Link: https://lore.kernel.org/r/20230922175234.work.134-kees@kernel.org Signed-off-by: Sudeep Holla --- drivers/bus/vexpress-config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bus/vexpress-config.c b/drivers/bus/vexpress-config.c index c4e1becbb2d2..d2c7ada90186 100644 --- a/drivers/bus/vexpress-config.c +++ b/drivers/bus/vexpress-config.c @@ -54,7 +54,7 @@ struct vexpress_syscfg_func { struct vexpress_syscfg *syscfg; struct regmap *regmap; int num_templates; - u32 template[]; /* Keep it last! */ + u32 template[] __counted_by(num_templates); /* Keep it last! */ }; struct vexpress_config_bridge_ops { -- cgit v1.2.3 From a10aa2584ef5223398cce82310bf5fa6ef9a75ab Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 21 Sep 2023 09:52:57 +0100 Subject: firmware: arm_scmi: Do not use !! on boolean when setting msg->flags Both pc->async_powercap_cap_set and ignore_dresp are already boolean. Use of !! on them is obviously dubious. Sparse reports: drivers/firmware/arm_scmi/powercap.c:363:17: warning: dubious: x & !y drivers/firmware/arm_scmi/powercap.c:363:17: warning: dubious: x & !y Remove the unnecessary !! and get rid of the warning. Reviewed-by: Cristian Marussi Link: https://lore.kernel.org/r/20230921085257.3125744-1-sudeep.holla@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/powercap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_scmi/powercap.c index 244929cb4f3e..cb5617443a14 100644 --- a/drivers/firmware/arm_scmi/powercap.c +++ b/drivers/firmware/arm_scmi/powercap.c @@ -360,8 +360,8 @@ static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph, msg = t->tx.buf; msg->domain = cpu_to_le32(pc->id); msg->flags = - cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, !!pc->async_powercap_cap_set) | - FIELD_PREP(CAP_SET_IGNORE_DRESP, !!ignore_dresp)); + cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, pc->async_powercap_cap_set) | + FIELD_PREP(CAP_SET_IGNORE_DRESP, ignore_dresp)); msg->value = cpu_to_le32(power_cap); if (!pc->async_powercap_cap_set || ignore_dresp) { -- cgit v1.2.3 From 8b6022be4c6e3e0d37c3e1378c9ff0a2c8717b09 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 25 Sep 2023 11:15:57 +0100 Subject: firmware: arm_scmi: Rename scmi_{msg_,}clock_config_{get,set}_{2,21} It is very confusing to use *_v2 for everything applicable until SCMI clock protocol version v2.0 including v1.0 for example. So let us rename such that *_v2 is used only for SCMI clock protocol v2.1 onwards. Also add comment to indicate the same explicitly. Reviewed-by: Cristian Marussi Link: https://lore.kernel.org/r/20230925101557.3839860-1-sudeep.holla@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 41 +++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index d18bf789fc24..9c0e33c1efab 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -46,12 +46,13 @@ struct scmi_msg_resp_clock_attributes { __le32 clock_enable_latency; }; -struct scmi_msg_clock_config_set_v2 { +struct scmi_msg_clock_config_set { __le32 id; __le32 attributes; }; -struct scmi_msg_clock_config_set_v21 { +/* Valid only from SCMI clock v2.1 */ +struct scmi_msg_clock_config_set_v2 { __le32 id; __le32 attributes; #define NULL_OEM_TYPE 0 @@ -429,13 +430,13 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph, } static int -scmi_clock_config_set_v2(const struct scmi_protocol_handle *ph, u32 clk_id, - enum clk_state state, u8 __unused0, u32 __unused1, - bool atomic) +scmi_clock_config_set(const struct scmi_protocol_handle *ph, u32 clk_id, + enum clk_state state, u8 __unused0, u32 __unused1, + bool atomic) { int ret; struct scmi_xfer *t; - struct scmi_msg_clock_config_set_v2 *cfg; + struct scmi_msg_clock_config_set *cfg; if (state >= CLK_STATE_RESERVED) return -EINVAL; @@ -457,15 +458,16 @@ scmi_clock_config_set_v2(const struct scmi_protocol_handle *ph, u32 clk_id, return ret; } +/* For SCMI clock v2.1 and onwards */ static int -scmi_clock_config_set_v21(const struct scmi_protocol_handle *ph, u32 clk_id, - enum clk_state state, u8 oem_type, u32 oem_val, - bool atomic) +scmi_clock_config_set_v2(const struct scmi_protocol_handle *ph, u32 clk_id, + enum clk_state state, u8 oem_type, u32 oem_val, + bool atomic) { int ret; u32 attrs; struct scmi_xfer *t; - struct scmi_msg_clock_config_set_v21 *cfg; + struct scmi_msg_clock_config_set_v2 *cfg; if (state == CLK_STATE_RESERVED || (!oem_type && state == CLK_STATE_UNCHANGED)) @@ -513,10 +515,11 @@ static int scmi_clock_disable(const struct scmi_protocol_handle *ph, u32 clk_id, NULL_OEM_TYPE, 0, atomic); } +/* For SCMI clock v2.1 and onwards */ static int -scmi_clock_config_get_v21(const struct scmi_protocol_handle *ph, u32 clk_id, - u8 oem_type, u32 *attributes, bool *enabled, - u32 *oem_val, bool atomic) +scmi_clock_config_get_v2(const struct scmi_protocol_handle *ph, u32 clk_id, + u8 oem_type, u32 *attributes, bool *enabled, + u32 *oem_val, bool atomic) { int ret; u32 flags; @@ -556,9 +559,9 @@ scmi_clock_config_get_v21(const struct scmi_protocol_handle *ph, u32 clk_id, } static int -scmi_clock_config_get_v2(const struct scmi_protocol_handle *ph, u32 clk_id, - u8 oem_type, u32 *attributes, bool *enabled, - u32 *oem_val, bool atomic) +scmi_clock_config_get(const struct scmi_protocol_handle *ph, u32 clk_id, + u8 oem_type, u32 *attributes, bool *enabled, + u32 *oem_val, bool atomic) { int ret; struct scmi_xfer *t; @@ -781,11 +784,11 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph) if (PROTOCOL_REV_MAJOR(version) >= 0x2 && PROTOCOL_REV_MINOR(version) >= 0x1) { - cinfo->clock_config_set = scmi_clock_config_set_v21; - cinfo->clock_config_get = scmi_clock_config_get_v21; - } else { cinfo->clock_config_set = scmi_clock_config_set_v2; cinfo->clock_config_get = scmi_clock_config_get_v2; + } else { + cinfo->clock_config_set = scmi_clock_config_set; + cinfo->clock_config_get = scmi_clock_config_get; } cinfo->version = version; -- cgit v1.2.3 From 3ad96787949a96256931ca59aff73ea604bc0e6c Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Wed, 20 Sep 2023 19:37:30 -0700 Subject: dt-bindings: reserved-memory: rmtfs: Allow guard pages On some Qualcomm platforms the firwmare, or hardware, does not gracefully handle memory protection of the rmtfs memory region when placed adjacent to other protected region. Some DeviceTree authors have worked around this issue by explicitly reserving the space around the region, but this prevents such author to use rely on the OS to place the region, through the use of "size" (instead of a fixed location). Introduce a flag to indicate that guard pages need be carved at the beginning and end of the memory region. The user shall account for the two pages in the defined size. Signed-off-by: Bjorn Andersson Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230920-rmtfs-mem-guard-pages-v3-1-305b37219b78@quicinc.com Signed-off-by: Bjorn Andersson --- .../devicetree/bindings/reserved-memory/qcom,rmtfs-mem.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Documentation/devicetree/bindings/reserved-memory/qcom,rmtfs-mem.yaml b/Documentation/devicetree/bindings/reserved-memory/qcom,rmtfs-mem.yaml index bab982f00485..46407e9c1d4f 100644 --- a/Documentation/devicetree/bindings/reserved-memory/qcom,rmtfs-mem.yaml +++ b/Documentation/devicetree/bindings/reserved-memory/qcom,rmtfs-mem.yaml @@ -26,6 +26,17 @@ properties: description: > identifier of the client to use this region for buffers + qcom,use-guard-pages: + type: boolean + description: > + Indicates that the firmware, or hardware, does not gracefully handle + memory protection of this region when placed adjacent to other protected + memory regions, and that padding around the used portion of the memory + region is necessary. + + When this is set, the first and last page should be left unused, and the + effective size of the region will thereby shrink with two pages. + qcom,vmid: $ref: /schemas/types.yaml#/definitions/uint32-array description: > -- cgit v1.2.3 From 9265bc6bce6919c771970e5a425a66551a1c78a0 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Wed, 20 Sep 2023 19:37:31 -0700 Subject: soc: qcom: rmtfs: Support discarding guard pages In some configurations, the exact placement of the rmtfs shared memory region isn't so strict. The DeviceTree author can then choose to use the "size" property and rely on the OS for placement (in combination with "alloc-ranges", if desired). But on some platforms the rmtfs memory region may not be allocated adjacent to regions allocated by other clients. Add support for discarding the first and last 4k block in the region, if qcom,use-guard-pages is specified in DeviceTree. Signed-off-by: Bjorn Andersson Reviewed-by: Stephan Gerhold Link: https://lore.kernel.org/r/20230920-rmtfs-mem-guard-pages-v3-2-305b37219b78@quicinc.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rmtfs_mem.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/soc/qcom/rmtfs_mem.c b/drivers/soc/qcom/rmtfs_mem.c index f83811f51175..9c937d24ba42 100644 --- a/drivers/soc/qcom/rmtfs_mem.c +++ b/drivers/soc/qcom/rmtfs_mem.c @@ -200,6 +200,15 @@ static int qcom_rmtfs_mem_probe(struct platform_device *pdev) rmtfs_mem->client_id = client_id; rmtfs_mem->size = rmem->size; + /* + * If requested, discard the first and last 4k block in order to ensure + * that the rmtfs region isn't adjacent to other protected regions. + */ + if (of_property_read_bool(node, "qcom,use-guard-pages")) { + rmtfs_mem->addr += SZ_4K; + rmtfs_mem->size -= 2 * SZ_4K; + } + device_initialize(&rmtfs_mem->dev); rmtfs_mem->dev.parent = &pdev->dev; rmtfs_mem->dev.groups = qcom_rmtfs_mem_groups; -- cgit v1.2.3 From 433ce46a97116f431ee114b4607143860aad4054 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 22 Sep 2023 10:54:14 -0700 Subject: soc: qcom: smem: Annotate struct qcom_smem with __counted_by Prepare for the coming implementation by GCC and Clang of the __counted_by attribute. Flexible array members annotated with __counted_by can have their accesses bounds-checked at run-time checking via CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for strcpy/memcpy-family functions). As found with Coccinelle[1], add __counted_by for struct qcom_smem. [1] https://github.com/kees/kernel-tools/blob/trunk/coccinelle/examples/counted_by.cocci Cc: Andy Gross Cc: Bjorn Andersson Cc: Konrad Dybcio Cc: Nathan Chancellor Cc: Nick Desaulniers Cc: Tom Rix Cc: linux-arm-msm@vger.kernel.org Cc: llvm@lists.linux.dev Signed-off-by: Kees Cook Reviewed-by: "Gustavo A. R. Silva" Link: https://lore.kernel.org/r/20230922175413.work.929-kees@kernel.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/smem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index d4a89d2bb43b..4ce9cf1477ac 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -285,7 +285,7 @@ struct qcom_smem { struct smem_partition partitions[SMEM_HOST_COUNT]; unsigned num_regions; - struct smem_region regions[]; + struct smem_region regions[] __counted_by(num_regions); }; static void * -- cgit v1.2.3 From 9b09c0f289c5a8fc5e9b0c1f3cd2766d33b910dc Mon Sep 17 00:00:00 2001 From: Unnathi Chalicheemala Date: Fri, 22 Sep 2023 11:48:17 -0700 Subject: soc: qcom: Switch to EXPORT_SYMBOL_GPL() Switch to GPL version of EXPORT_SYMBOL for Qualcomm SoC drivers. Signed-off-by: Unnathi Chalicheemala Reviewed-by: Trilok Soni Link: https://lore.kernel.org/r/20230922184817.5183-1-quic_uchalich@quicinc.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/cmd-db.c | 8 ++++---- drivers/soc/qcom/kryo-l2-accessors.c | 4 ++-- drivers/soc/qcom/ocmem.c | 6 +++--- drivers/soc/qcom/pdr_interface.c | 8 ++++---- drivers/soc/qcom/qcom-geni-se.c | 38 ++++++++++++++++++------------------ drivers/soc/qcom/qcom_aoss.c | 6 +++--- drivers/soc/qcom/qmi_encdec.c | 6 +++--- drivers/soc/qcom/qmi_interface.c | 20 +++++++++---------- drivers/soc/qcom/rpmh.c | 8 ++++---- drivers/soc/qcom/smd-rpm.c | 2 +- drivers/soc/qcom/smem.c | 2 +- drivers/soc/qcom/wcnss_ctrl.c | 2 +- 12 files changed, 55 insertions(+), 55 deletions(-) diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c index 34c40368d5b5..a5fd68411bed 100644 --- a/drivers/soc/qcom/cmd-db.c +++ b/drivers/soc/qcom/cmd-db.c @@ -133,7 +133,7 @@ int cmd_db_ready(void) return 0; } -EXPORT_SYMBOL(cmd_db_ready); +EXPORT_SYMBOL_GPL(cmd_db_ready); static int cmd_db_get_header(const char *id, const struct entry_header **eh, const struct rsc_hdr **rh) @@ -193,7 +193,7 @@ u32 cmd_db_read_addr(const char *id) return ret < 0 ? 0 : le32_to_cpu(ent->addr); } -EXPORT_SYMBOL(cmd_db_read_addr); +EXPORT_SYMBOL_GPL(cmd_db_read_addr); /** * cmd_db_read_aux_data() - Query command db for aux data. @@ -218,7 +218,7 @@ const void *cmd_db_read_aux_data(const char *id, size_t *len) return rsc_offset(rsc_hdr, ent); } -EXPORT_SYMBOL(cmd_db_read_aux_data); +EXPORT_SYMBOL_GPL(cmd_db_read_aux_data); /** * cmd_db_read_slave_id - Get the slave ID for a given resource address @@ -240,7 +240,7 @@ enum cmd_db_hw_type cmd_db_read_slave_id(const char *id) addr = le32_to_cpu(ent->addr); return (addr >> SLAVE_ID_SHIFT) & SLAVE_ID_MASK; } -EXPORT_SYMBOL(cmd_db_read_slave_id); +EXPORT_SYMBOL_GPL(cmd_db_read_slave_id); #ifdef CONFIG_DEBUG_FS static int cmd_db_debugfs_dump(struct seq_file *seq, void *p) diff --git a/drivers/soc/qcom/kryo-l2-accessors.c b/drivers/soc/qcom/kryo-l2-accessors.c index 7886af4fd726..50cd710c5e82 100644 --- a/drivers/soc/qcom/kryo-l2-accessors.c +++ b/drivers/soc/qcom/kryo-l2-accessors.c @@ -32,7 +32,7 @@ void kryo_l2_set_indirect_reg(u64 reg, u64 val) isb(); raw_spin_unlock_irqrestore(&l2_access_lock, flags); } -EXPORT_SYMBOL(kryo_l2_set_indirect_reg); +EXPORT_SYMBOL_GPL(kryo_l2_set_indirect_reg); /** * kryo_l2_get_indirect_reg() - read an L2 register value @@ -54,4 +54,4 @@ u64 kryo_l2_get_indirect_reg(u64 reg) return val; } -EXPORT_SYMBOL(kryo_l2_get_indirect_reg); +EXPORT_SYMBOL_GPL(kryo_l2_get_indirect_reg); diff --git a/drivers/soc/qcom/ocmem.c b/drivers/soc/qcom/ocmem.c index 20f5461d46b9..3cf8ab892a70 100644 --- a/drivers/soc/qcom/ocmem.c +++ b/drivers/soc/qcom/ocmem.c @@ -211,7 +211,7 @@ struct ocmem *of_get_ocmem(struct device *dev) } return ocmem; } -EXPORT_SYMBOL(of_get_ocmem); +EXPORT_SYMBOL_GPL(of_get_ocmem); struct ocmem_buf *ocmem_allocate(struct ocmem *ocmem, enum ocmem_client client, unsigned long size) @@ -267,7 +267,7 @@ err_unlock: return ERR_PTR(ret); } -EXPORT_SYMBOL(ocmem_allocate); +EXPORT_SYMBOL_GPL(ocmem_allocate); void ocmem_free(struct ocmem *ocmem, enum ocmem_client client, struct ocmem_buf *buf) @@ -294,7 +294,7 @@ void ocmem_free(struct ocmem *ocmem, enum ocmem_client client, clear_bit_unlock(BIT(client), &ocmem->active_allocations); } -EXPORT_SYMBOL(ocmem_free); +EXPORT_SYMBOL_GPL(ocmem_free); static int ocmem_dev_probe(struct platform_device *pdev) { diff --git a/drivers/soc/qcom/pdr_interface.c b/drivers/soc/qcom/pdr_interface.c index 0034af927b48..a1b6a4081dea 100644 --- a/drivers/soc/qcom/pdr_interface.c +++ b/drivers/soc/qcom/pdr_interface.c @@ -554,7 +554,7 @@ err: kfree(pds); return ERR_PTR(ret); } -EXPORT_SYMBOL(pdr_add_lookup); +EXPORT_SYMBOL_GPL(pdr_add_lookup); /** * pdr_restart_pd() - restart PD @@ -634,7 +634,7 @@ int pdr_restart_pd(struct pdr_handle *pdr, struct pdr_service *pds) return 0; } -EXPORT_SYMBOL(pdr_restart_pd); +EXPORT_SYMBOL_GPL(pdr_restart_pd); /** * pdr_handle_alloc() - initialize the PDR client handle @@ -715,7 +715,7 @@ free_pdr_handle: return ERR_PTR(ret); } -EXPORT_SYMBOL(pdr_handle_alloc); +EXPORT_SYMBOL_GPL(pdr_handle_alloc); /** * pdr_handle_release() - release the PDR client handle @@ -749,7 +749,7 @@ void pdr_handle_release(struct pdr_handle *pdr) kfree(pdr); } -EXPORT_SYMBOL(pdr_handle_release); +EXPORT_SYMBOL_GPL(pdr_handle_release); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Qualcomm Protection Domain Restart helpers"); diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c index ba788762835f..bdcf44b85b2f 100644 --- a/drivers/soc/qcom/qcom-geni-se.c +++ b/drivers/soc/qcom/qcom-geni-se.c @@ -199,7 +199,7 @@ u32 geni_se_get_qup_hw_version(struct geni_se *se) return readl_relaxed(wrapper->base + QUP_HW_VER_REG); } -EXPORT_SYMBOL(geni_se_get_qup_hw_version); +EXPORT_SYMBOL_GPL(geni_se_get_qup_hw_version); static void geni_se_io_set_mode(void __iomem *base) { @@ -272,7 +272,7 @@ void geni_se_init(struct geni_se *se, u32 rx_wm, u32 rx_rfr) val |= S_COMMON_GENI_S_IRQ_EN; writel_relaxed(val, se->base + SE_GENI_S_IRQ_EN); } -EXPORT_SYMBOL(geni_se_init); +EXPORT_SYMBOL_GPL(geni_se_init); static void geni_se_select_fifo_mode(struct geni_se *se) { @@ -364,7 +364,7 @@ void geni_se_select_mode(struct geni_se *se, enum geni_se_xfer_mode mode) break; } } -EXPORT_SYMBOL(geni_se_select_mode); +EXPORT_SYMBOL_GPL(geni_se_select_mode); /** * DOC: Overview @@ -481,7 +481,7 @@ void geni_se_config_packing(struct geni_se *se, int bpw, int pack_words, if (pack_words || bpw == 32) writel_relaxed(bpw / 16, se->base + SE_GENI_BYTE_GRAN); } -EXPORT_SYMBOL(geni_se_config_packing); +EXPORT_SYMBOL_GPL(geni_se_config_packing); static void geni_se_clks_off(struct geni_se *se) { @@ -512,7 +512,7 @@ int geni_se_resources_off(struct geni_se *se) geni_se_clks_off(se); return 0; } -EXPORT_SYMBOL(geni_se_resources_off); +EXPORT_SYMBOL_GPL(geni_se_resources_off); static int geni_se_clks_on(struct geni_se *se) { @@ -553,7 +553,7 @@ int geni_se_resources_on(struct geni_se *se) return ret; } -EXPORT_SYMBOL(geni_se_resources_on); +EXPORT_SYMBOL_GPL(geni_se_resources_on); /** * geni_se_clk_tbl_get() - Get the clock table to program DFS @@ -594,7 +594,7 @@ int geni_se_clk_tbl_get(struct geni_se *se, unsigned long **tbl) *tbl = se->clk_perf_tbl; return se->num_clk_levels; } -EXPORT_SYMBOL(geni_se_clk_tbl_get); +EXPORT_SYMBOL_GPL(geni_se_clk_tbl_get); /** * geni_se_clk_freq_match() - Get the matching or closest SE clock frequency @@ -656,7 +656,7 @@ int geni_se_clk_freq_match(struct geni_se *se, unsigned long req_freq, return 0; } -EXPORT_SYMBOL(geni_se_clk_freq_match); +EXPORT_SYMBOL_GPL(geni_se_clk_freq_match); #define GENI_SE_DMA_DONE_EN BIT(0) #define GENI_SE_DMA_EOT_EN BIT(1) @@ -684,7 +684,7 @@ void geni_se_tx_init_dma(struct geni_se *se, dma_addr_t iova, size_t len) writel_relaxed(GENI_SE_DMA_EOT_BUF, se->base + SE_DMA_TX_ATTR); writel(len, se->base + SE_DMA_TX_LEN); } -EXPORT_SYMBOL(geni_se_tx_init_dma); +EXPORT_SYMBOL_GPL(geni_se_tx_init_dma); /** * geni_se_tx_dma_prep() - Prepare the serial engine for TX DMA transfer @@ -712,7 +712,7 @@ int geni_se_tx_dma_prep(struct geni_se *se, void *buf, size_t len, geni_se_tx_init_dma(se, *iova, len); return 0; } -EXPORT_SYMBOL(geni_se_tx_dma_prep); +EXPORT_SYMBOL_GPL(geni_se_tx_dma_prep); /** * geni_se_rx_init_dma() - Initiate RX DMA transfer on the serial engine @@ -736,7 +736,7 @@ void geni_se_rx_init_dma(struct geni_se *se, dma_addr_t iova, size_t len) writel_relaxed(0, se->base + SE_DMA_RX_ATTR); writel(len, se->base + SE_DMA_RX_LEN); } -EXPORT_SYMBOL(geni_se_rx_init_dma); +EXPORT_SYMBOL_GPL(geni_se_rx_init_dma); /** * geni_se_rx_dma_prep() - Prepare the serial engine for RX DMA transfer @@ -764,7 +764,7 @@ int geni_se_rx_dma_prep(struct geni_se *se, void *buf, size_t len, geni_se_rx_init_dma(se, *iova, len); return 0; } -EXPORT_SYMBOL(geni_se_rx_dma_prep); +EXPORT_SYMBOL_GPL(geni_se_rx_dma_prep); /** * geni_se_tx_dma_unprep() - Unprepare the serial engine after TX DMA transfer @@ -781,7 +781,7 @@ void geni_se_tx_dma_unprep(struct geni_se *se, dma_addr_t iova, size_t len) if (!dma_mapping_error(wrapper->dev, iova)) dma_unmap_single(wrapper->dev, iova, len, DMA_TO_DEVICE); } -EXPORT_SYMBOL(geni_se_tx_dma_unprep); +EXPORT_SYMBOL_GPL(geni_se_tx_dma_unprep); /** * geni_se_rx_dma_unprep() - Unprepare the serial engine after RX DMA transfer @@ -798,7 +798,7 @@ void geni_se_rx_dma_unprep(struct geni_se *se, dma_addr_t iova, size_t len) if (!dma_mapping_error(wrapper->dev, iova)) dma_unmap_single(wrapper->dev, iova, len, DMA_FROM_DEVICE); } -EXPORT_SYMBOL(geni_se_rx_dma_unprep); +EXPORT_SYMBOL_GPL(geni_se_rx_dma_unprep); int geni_icc_get(struct geni_se *se, const char *icc_ddr) { @@ -827,7 +827,7 @@ err: return err; } -EXPORT_SYMBOL(geni_icc_get); +EXPORT_SYMBOL_GPL(geni_icc_get); int geni_icc_set_bw(struct geni_se *se) { @@ -845,7 +845,7 @@ int geni_icc_set_bw(struct geni_se *se) return 0; } -EXPORT_SYMBOL(geni_icc_set_bw); +EXPORT_SYMBOL_GPL(geni_icc_set_bw); void geni_icc_set_tag(struct geni_se *se, u32 tag) { @@ -854,7 +854,7 @@ void geni_icc_set_tag(struct geni_se *se, u32 tag) for (i = 0; i < ARRAY_SIZE(se->icc_paths); i++) icc_set_tag(se->icc_paths[i].path, tag); } -EXPORT_SYMBOL(geni_icc_set_tag); +EXPORT_SYMBOL_GPL(geni_icc_set_tag); /* To do: Replace this by icc_bulk_enable once it's implemented in ICC core */ int geni_icc_enable(struct geni_se *se) @@ -872,7 +872,7 @@ int geni_icc_enable(struct geni_se *se) return 0; } -EXPORT_SYMBOL(geni_icc_enable); +EXPORT_SYMBOL_GPL(geni_icc_enable); int geni_icc_disable(struct geni_se *se) { @@ -889,7 +889,7 @@ int geni_icc_disable(struct geni_se *se) return 0; } -EXPORT_SYMBOL(geni_icc_disable); +EXPORT_SYMBOL_GPL(geni_icc_disable); static int geni_se_probe(struct platform_device *pdev) { diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c index 77f0cf126629..35d35f763eb2 100644 --- a/drivers/soc/qcom/qcom_aoss.c +++ b/drivers/soc/qcom/qcom_aoss.c @@ -260,7 +260,7 @@ int qmp_send(struct qmp *qmp, const char *fmt, ...) return ret; } -EXPORT_SYMBOL(qmp_send); +EXPORT_SYMBOL_GPL(qmp_send); static int qmp_qdss_clk_prepare(struct clk_hw *hw) { @@ -458,7 +458,7 @@ struct qmp *qmp_get(struct device *dev) } return qmp; } -EXPORT_SYMBOL(qmp_get); +EXPORT_SYMBOL_GPL(qmp_get); /** * qmp_put() - release a qmp handle @@ -473,7 +473,7 @@ void qmp_put(struct qmp *qmp) if (!IS_ERR_OR_NULL(qmp)) put_device(qmp->dev); } -EXPORT_SYMBOL(qmp_put); +EXPORT_SYMBOL_GPL(qmp_put); static int qmp_probe(struct platform_device *pdev) { diff --git a/drivers/soc/qcom/qmi_encdec.c b/drivers/soc/qcom/qmi_encdec.c index 5c7161b18b72..bb09eff85cff 100644 --- a/drivers/soc/qcom/qmi_encdec.c +++ b/drivers/soc/qcom/qmi_encdec.c @@ -754,7 +754,7 @@ void *qmi_encode_message(int type, unsigned int msg_id, size_t *len, return msg; } -EXPORT_SYMBOL(qmi_encode_message); +EXPORT_SYMBOL_GPL(qmi_encode_message); /** * qmi_decode_message() - Decode QMI encoded message to C structure @@ -778,7 +778,7 @@ int qmi_decode_message(const void *buf, size_t len, return qmi_decode(ei, c_struct, buf + sizeof(struct qmi_header), len - sizeof(struct qmi_header), 1); } -EXPORT_SYMBOL(qmi_decode_message); +EXPORT_SYMBOL_GPL(qmi_decode_message); /* Common header in all QMI responses */ const struct qmi_elem_info qmi_response_type_v01_ei[] = { @@ -810,7 +810,7 @@ const struct qmi_elem_info qmi_response_type_v01_ei[] = { .ei_array = NULL, }, }; -EXPORT_SYMBOL(qmi_response_type_v01_ei); +EXPORT_SYMBOL_GPL(qmi_response_type_v01_ei); MODULE_DESCRIPTION("QMI encoder/decoder helper"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/qmi_interface.c b/drivers/soc/qcom/qmi_interface.c index 78d7361fdcf2..bb98b06e87f8 100644 --- a/drivers/soc/qcom/qmi_interface.c +++ b/drivers/soc/qcom/qmi_interface.c @@ -223,7 +223,7 @@ int qmi_add_lookup(struct qmi_handle *qmi, unsigned int service, return 0; } -EXPORT_SYMBOL(qmi_add_lookup); +EXPORT_SYMBOL_GPL(qmi_add_lookup); static void qmi_send_new_server(struct qmi_handle *qmi, struct qmi_service *svc) { @@ -287,7 +287,7 @@ int qmi_add_server(struct qmi_handle *qmi, unsigned int service, return 0; } -EXPORT_SYMBOL(qmi_add_server); +EXPORT_SYMBOL_GPL(qmi_add_server); /** * qmi_txn_init() - allocate transaction id within the given QMI handle @@ -328,7 +328,7 @@ int qmi_txn_init(struct qmi_handle *qmi, struct qmi_txn *txn, return ret; } -EXPORT_SYMBOL(qmi_txn_init); +EXPORT_SYMBOL_GPL(qmi_txn_init); /** * qmi_txn_wait() - wait for a response on a transaction @@ -359,7 +359,7 @@ int qmi_txn_wait(struct qmi_txn *txn, unsigned long timeout) else return txn->result; } -EXPORT_SYMBOL(qmi_txn_wait); +EXPORT_SYMBOL_GPL(qmi_txn_wait); /** * qmi_txn_cancel() - cancel an ongoing transaction @@ -375,7 +375,7 @@ void qmi_txn_cancel(struct qmi_txn *txn) mutex_unlock(&txn->lock); mutex_unlock(&qmi->txn_lock); } -EXPORT_SYMBOL(qmi_txn_cancel); +EXPORT_SYMBOL_GPL(qmi_txn_cancel); /** * qmi_invoke_handler() - find and invoke a handler for a message @@ -676,7 +676,7 @@ err_free_recv_buf: return ret; } -EXPORT_SYMBOL(qmi_handle_init); +EXPORT_SYMBOL_GPL(qmi_handle_init); /** * qmi_handle_release() - release the QMI client handle @@ -717,7 +717,7 @@ void qmi_handle_release(struct qmi_handle *qmi) kfree(svc); } } -EXPORT_SYMBOL(qmi_handle_release); +EXPORT_SYMBOL_GPL(qmi_handle_release); /** * qmi_send_message() - send a QMI message @@ -796,7 +796,7 @@ ssize_t qmi_send_request(struct qmi_handle *qmi, struct sockaddr_qrtr *sq, return qmi_send_message(qmi, sq, txn, QMI_REQUEST, msg_id, len, ei, c_struct); } -EXPORT_SYMBOL(qmi_send_request); +EXPORT_SYMBOL_GPL(qmi_send_request); /** * qmi_send_response() - send a response QMI message @@ -817,7 +817,7 @@ ssize_t qmi_send_response(struct qmi_handle *qmi, struct sockaddr_qrtr *sq, return qmi_send_message(qmi, sq, txn, QMI_RESPONSE, msg_id, len, ei, c_struct); } -EXPORT_SYMBOL(qmi_send_response); +EXPORT_SYMBOL_GPL(qmi_send_response); /** * qmi_send_indication() - send an indication QMI message @@ -851,4 +851,4 @@ ssize_t qmi_send_indication(struct qmi_handle *qmi, struct sockaddr_qrtr *sq, return rval; } -EXPORT_SYMBOL(qmi_send_indication); +EXPORT_SYMBOL_GPL(qmi_send_indication); diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index 08e09642d7f5..9f26d7f9b9dc 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -239,7 +239,7 @@ int rpmh_write_async(const struct device *dev, enum rpmh_state state, return __rpmh_write(dev, state, rpm_msg); } -EXPORT_SYMBOL(rpmh_write_async); +EXPORT_SYMBOL_GPL(rpmh_write_async); /** * rpmh_write: Write a set of RPMH commands and block until response @@ -270,7 +270,7 @@ int rpmh_write(const struct device *dev, enum rpmh_state state, WARN_ON(!ret); return (ret > 0) ? 0 : -ETIMEDOUT; } -EXPORT_SYMBOL(rpmh_write); +EXPORT_SYMBOL_GPL(rpmh_write); static void cache_batch(struct rpmh_ctrlr *ctrlr, struct batch_cache_req *req) { @@ -395,7 +395,7 @@ exit: return ret; } -EXPORT_SYMBOL(rpmh_write_batch); +EXPORT_SYMBOL_GPL(rpmh_write_batch); static int is_req_valid(struct cache_req *req) { @@ -500,4 +500,4 @@ void rpmh_invalidate(const struct device *dev) ctrlr->dirty = true; spin_unlock_irqrestore(&ctrlr->cache_lock, flags); } -EXPORT_SYMBOL(rpmh_invalidate); +EXPORT_SYMBOL_GPL(rpmh_invalidate); diff --git a/drivers/soc/qcom/smd-rpm.c b/drivers/soc/qcom/smd-rpm.c index f9fd6177118c..b7056aed4c7d 100644 --- a/drivers/soc/qcom/smd-rpm.c +++ b/drivers/soc/qcom/smd-rpm.c @@ -142,7 +142,7 @@ out: mutex_unlock(&rpm->lock); return ret; } -EXPORT_SYMBOL(qcom_rpm_smd_write); +EXPORT_SYMBOL_GPL(qcom_rpm_smd_write); static int qcom_smd_rpm_callback(struct rpmsg_device *rpdev, void *data, diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index 4ce9cf1477ac..0e644806f897 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -368,7 +368,7 @@ bool qcom_smem_is_available(void) { return !!__smem; } -EXPORT_SYMBOL(qcom_smem_is_available); +EXPORT_SYMBOL_GPL(qcom_smem_is_available); static int qcom_smem_alloc_private(struct qcom_smem *smem, struct smem_partition *part, diff --git a/drivers/soc/qcom/wcnss_ctrl.c b/drivers/soc/qcom/wcnss_ctrl.c index 2894e214cacf..148bcbac332d 100644 --- a/drivers/soc/qcom/wcnss_ctrl.c +++ b/drivers/soc/qcom/wcnss_ctrl.c @@ -287,7 +287,7 @@ struct rpmsg_endpoint *qcom_wcnss_open_channel(void *wcnss, const char *name, rp return rpmsg_create_ept(_wcnss->channel->rpdev, cb, priv, chinfo); } -EXPORT_SYMBOL(qcom_wcnss_open_channel); +EXPORT_SYMBOL_GPL(qcom_wcnss_open_channel); static void wcnss_async_probe(struct work_struct *work) { -- cgit v1.2.3 From f1a1bc8775b26345aba2be278118999e7f661d3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 26 Sep 2023 10:32:29 +0200 Subject: soc: qcom: llcc: Handle a second device without data corruption MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Usually there is only one llcc device. But if there were a second, even a failed probe call would modify the global drv_data pointer. So check if drv_data is valid before overwriting it. Signed-off-by: Uwe Kleine-König Fixes: a3134fb09e0b ("drivers: soc: Add LLCC driver") Link: https://lore.kernel.org/r/20230926083229.2073890-1-u.kleine-koenig@pengutronix.de Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/llcc-qcom.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index feb21637ac20..e3f262a50e4a 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -1120,6 +1120,9 @@ static int qcom_llcc_probe(struct platform_device *pdev) u32 version; struct regmap *regmap; + if (!IS_ERR(drv_data)) + return -EBUSY; + drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL); if (!drv_data) { ret = -ENOMEM; -- cgit v1.2.3 From 3337a6fea25370d3d244ec6bb38c71ee86fcf837 Mon Sep 17 00:00:00 2001 From: Kathiravan Thirumoorthy Date: Mon, 25 Sep 2023 13:59:22 +0530 Subject: firmware: qcom_scm: use 64-bit calling convention only when client is 64-bit Per the "SMC calling convention specification", the 64-bit calling convention can only be used when the client is 64-bit. Whereas the 32-bit calling convention can be used by either a 32-bit or a 64-bit client. Currently during SCM probe, irrespective of the client, 64-bit calling convention is made, which is incorrect and may lead to the undefined behaviour when the client is 32-bit. Let's fix it. Cc: stable@vger.kernel.org Fixes: 9a434cee773a ("firmware: qcom_scm: Dynamically support SMCCC and legacy conventions") Reviewed-By: Elliot Berman Signed-off-by: Kathiravan Thirumoorthy Link: https://lore.kernel.org/r/20230925-scm-v3-1-8790dff6a749@quicinc.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom_scm.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index c2c7fafef34b..520de9b5633a 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -215,6 +215,12 @@ static enum qcom_scm_convention __get_convention(void) if (likely(qcom_scm_convention != SMC_CONVENTION_UNKNOWN)) return qcom_scm_convention; + /* + * Per the "SMC calling convention specification", the 64-bit calling + * convention can only be used when the client is 64-bit, otherwise + * system will encounter the undefined behaviour. + */ +#if IS_ENABLED(CONFIG_ARM64) /* * Device isn't required as there is only one argument - no device * needed to dma_map_single to secure world @@ -235,6 +241,7 @@ static enum qcom_scm_convention __get_convention(void) forced = true; goto found; } +#endif probed_convention = SMC_CONVENTION_ARM_32; ret = __scm_smc_call(NULL, &desc, probed_convention, &res, true); -- cgit v1.2.3 From 387bb6fdd0af1f853deeb1cdd8331c07b6a66b32 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 25 Sep 2023 16:26:21 -0500 Subject: dt-bindings: memory-controllers: Make "additionalProperties: true" explicit Make it explicit that child nodes have additional properties and the child node schema is not complete. The complete schemas are applied separately based the compatible strings. Signed-off-by: Rob Herring Reviewed-by: Roger Quadros Link: https://lore.kernel.org/r/20230925212626.1974676-1-robh@kernel.org Signed-off-by: Krzysztof Kozlowski --- Documentation/devicetree/bindings/memory-controllers/ingenic,nemc.yaml | 1 + .../devicetree/bindings/memory-controllers/renesas,rpc-if.yaml | 2 ++ Documentation/devicetree/bindings/memory-controllers/ti,gpmc.yaml | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/memory-controllers/ingenic,nemc.yaml b/Documentation/devicetree/bindings/memory-controllers/ingenic,nemc.yaml index b40cec0eb651..ee74a362f4ca 100644 --- a/Documentation/devicetree/bindings/memory-controllers/ingenic,nemc.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/ingenic,nemc.yaml @@ -40,6 +40,7 @@ patternProperties: ".*@[0-9]+$": type: object $ref: mc-peripheral-props.yaml# + additionalProperties: true required: - compatible diff --git a/Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.yaml b/Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.yaml index 56e62cd0b36a..25f3bb9890ae 100644 --- a/Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.yaml @@ -80,6 +80,8 @@ properties: patternProperties: "flash@[0-9a-f]+$": type: object + additionalProperties: true + properties: compatible: contains: diff --git a/Documentation/devicetree/bindings/memory-controllers/ti,gpmc.yaml b/Documentation/devicetree/bindings/memory-controllers/ti,gpmc.yaml index b049837ee669..c7a8a041da50 100644 --- a/Documentation/devicetree/bindings/memory-controllers/ti,gpmc.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/ti,gpmc.yaml @@ -130,7 +130,7 @@ patternProperties: bus. The device can be a NAND chip, SRAM device, NOR device or an ASIC. $ref: ti,gpmc-child.yaml - + additionalProperties: true required: - compatible -- cgit v1.2.3 From 20bd1191830478cfdc9be766a735923c5fbe5350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:21 +0200 Subject: soc/rockchip: io-domain: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230925095532.1984344-31-u.kleine-koenig@pengutronix.de Signed-off-by: Heiko Stuebner --- drivers/soc/rockchip/io-domain.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/rockchip/io-domain.c b/drivers/soc/rockchip/io-domain.c index 6619256c2d11..18f809c160a7 100644 --- a/drivers/soc/rockchip/io-domain.c +++ b/drivers/soc/rockchip/io-domain.c @@ -687,7 +687,7 @@ unreg_notify: return ret; } -static int rockchip_iodomain_remove(struct platform_device *pdev) +static void rockchip_iodomain_remove(struct platform_device *pdev) { struct rockchip_iodomain *iod = platform_get_drvdata(pdev); int i; @@ -699,13 +699,11 @@ static int rockchip_iodomain_remove(struct platform_device *pdev) regulator_unregister_notifier(io_supply->reg, &io_supply->nb); } - - return 0; } static struct platform_driver rockchip_iodomain_driver = { .probe = rockchip_iodomain_probe, - .remove = rockchip_iodomain_remove, + .remove_new = rockchip_iodomain_remove, .driver = { .name = "rockchip-iodomain", .of_match_table = rockchip_iodomain_match, -- cgit v1.2.3 From 7b7a224b1ba1703583b25a3641ad9798f34d832a Mon Sep 17 00:00:00 2001 From: Dhruva Gole Date: Thu, 21 Sep 2023 14:40:26 +0530 Subject: firmware: ti_sci: Mark driver as non removable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TI-SCI message protocol provides a way to communicate between various compute processors with a central system controller entity. It provides the fundamental device management capability and clock control in the SOCs that it's used in. The remove function failed to do all the necessary cleanup if there are registered users. Some things are freed however which likely results in an oops later on. Ensure that the driver isn't unbound by suppressing its bind and unbind sysfs attributes. As the driver is built-in there is no way to remove device once bound. We can also remove the ti_sci_remove call along with the ti_sci_debugfs_destroy as there are no callers for it any longer. Fixes: aa276781a64a ("firmware: Add basic support for TI System Control Interface (TI-SCI) protocol") Reported-by: Uwe Kleine-König Closes: https://lore.kernel.org/linux-arm-kernel/20230216083908.mvmydic5lpi3ogo7@pengutronix.de/ Suggested-by: Uwe Kleine-König Acked-by: Uwe Kleine-König Signed-off-by: Dhruva Gole Link: https://lore.kernel.org/r/20230921091025.133130-1-d-gole@ti.com Signed-off-by: Nishanth Menon --- drivers/firmware/ti_sci.c | 46 +--------------------------------------------- 1 file changed, 1 insertion(+), 45 deletions(-) diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index c288ae49f91c..ffca636cc13f 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -190,19 +190,6 @@ static int ti_sci_debugfs_create(struct platform_device *pdev, return 0; } -/** - * ti_sci_debugfs_destroy() - clean up log debug file - * @pdev: platform device pointer - * @info: Pointer to SCI entity information - */ -static void ti_sci_debugfs_destroy(struct platform_device *pdev, - struct ti_sci_info *info) -{ - if (IS_ERR(info->debug_region)) - return; - - debugfs_remove(info->d); -} #else /* CONFIG_DEBUG_FS */ static inline int ti_sci_debugfs_create(struct platform_device *dev, struct ti_sci_info *info) @@ -3445,43 +3432,12 @@ out: return ret; } -static int ti_sci_remove(struct platform_device *pdev) -{ - struct ti_sci_info *info; - struct device *dev = &pdev->dev; - int ret = 0; - - of_platform_depopulate(dev); - - info = platform_get_drvdata(pdev); - - if (info->nb.notifier_call) - unregister_restart_handler(&info->nb); - - mutex_lock(&ti_sci_list_mutex); - if (info->users) - ret = -EBUSY; - else - list_del(&info->node); - mutex_unlock(&ti_sci_list_mutex); - - if (!ret) { - ti_sci_debugfs_destroy(pdev, info); - - /* Safe to free channels since no more users */ - mbox_free_channel(info->chan_tx); - mbox_free_channel(info->chan_rx); - } - - return ret; -} - static struct platform_driver ti_sci_driver = { .probe = ti_sci_probe, - .remove = ti_sci_remove, .driver = { .name = "ti-sci", .of_match_table = of_match_ptr(ti_sci_of_match), + .suppress_bind_attrs = true, }, }; module_platform_driver(ti_sci_driver); -- cgit v1.2.3 From f34b902c5ba67841902cd7f0e24e64bb82f69cb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:24 +0200 Subject: soc/ti: k3-ringacc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Konrad Dybcio # qcom Link: https://lore.kernel.org/r/20230925095532.1984344-34-u.kleine-koenig@pengutronix.de Signed-off-by: Nishanth Menon --- drivers/soc/ti/k3-ringacc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/soc/ti/k3-ringacc.c b/drivers/soc/ti/k3-ringacc.c index 148f54d9691d..fd4251d75935 100644 --- a/drivers/soc/ti/k3-ringacc.c +++ b/drivers/soc/ti/k3-ringacc.c @@ -1551,19 +1551,18 @@ static int k3_ringacc_probe(struct platform_device *pdev) return 0; } -static int k3_ringacc_remove(struct platform_device *pdev) +static void k3_ringacc_remove(struct platform_device *pdev) { struct k3_ringacc *ringacc = dev_get_drvdata(&pdev->dev); mutex_lock(&k3_ringacc_list_lock); list_del(&ringacc->list); mutex_unlock(&k3_ringacc_list_lock); - return 0; } static struct platform_driver k3_ringacc_driver = { .probe = k3_ringacc_probe, - .remove = k3_ringacc_remove, + .remove_new = k3_ringacc_remove, .driver = { .name = "k3-ringacc", .of_match_table = k3_ringacc_of_match, -- cgit v1.2.3 From 3af4ec7c7dd39a2c4618f6536b2e7b73a19be169 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:25 +0200 Subject: soc/ti: knav_dma: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Konrad Dybcio # qcom Link: https://lore.kernel.org/r/20230925095532.1984344-35-u.kleine-koenig@pengutronix.de Signed-off-by: Nishanth Menon --- drivers/soc/ti/knav_dma.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/ti/knav_dma.c b/drivers/soc/ti/knav_dma.c index 0fbc37cd5123..6023006685fc 100644 --- a/drivers/soc/ti/knav_dma.c +++ b/drivers/soc/ti/knav_dma.c @@ -773,7 +773,7 @@ err_pm_disable: return ret; } -static int knav_dma_remove(struct platform_device *pdev) +static void knav_dma_remove(struct platform_device *pdev) { struct knav_dma_device *dma; @@ -784,8 +784,6 @@ static int knav_dma_remove(struct platform_device *pdev) pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - - return 0; } static struct of_device_id of_match[] = { @@ -797,7 +795,7 @@ MODULE_DEVICE_TABLE(of, of_match); static struct platform_driver knav_dma_driver = { .probe = knav_dma_probe, - .remove = knav_dma_remove, + .remove_new = knav_dma_remove, .driver = { .name = "keystone-navigator-dma", .of_match_table = of_match, -- cgit v1.2.3 From af97160a0c5f1908c6f2830023fb93baac4451d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:26 +0200 Subject: soc/ti: knav_qmss_queue: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Konrad Dybcio # qcom Link: https://lore.kernel.org/r/20230925095532.1984344-36-u.kleine-koenig@pengutronix.de Signed-off-by: Nishanth Menon --- drivers/soc/ti/knav_qmss_queue.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/soc/ti/knav_qmss_queue.c b/drivers/soc/ti/knav_qmss_queue.c index 0f252c2549ba..cea791717957 100644 --- a/drivers/soc/ti/knav_qmss_queue.c +++ b/drivers/soc/ti/knav_qmss_queue.c @@ -1884,17 +1884,16 @@ err: return ret; } -static int knav_queue_remove(struct platform_device *pdev) +static void knav_queue_remove(struct platform_device *pdev) { /* TODO: Free resources */ pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - return 0; } static struct platform_driver keystone_qmss_driver = { .probe = knav_queue_probe, - .remove = knav_queue_remove, + .remove_new = knav_queue_remove, .driver = { .name = "keystone-navigator-qmss", .of_match_table = keystone_qmss_of_match, -- cgit v1.2.3 From 9eb950e9fffc5337bfe1798cf89ce4d97a4f1221 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:27 +0200 Subject: soc/ti: pm33xx: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Konrad Dybcio # qcom Link: https://lore.kernel.org/r/20230925095532.1984344-37-u.kleine-koenig@pengutronix.de Signed-off-by: Nishanth Menon --- drivers/soc/ti/pm33xx.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/soc/ti/pm33xx.c b/drivers/soc/ti/pm33xx.c index f04c21157904..8e983c3c4e03 100644 --- a/drivers/soc/ti/pm33xx.c +++ b/drivers/soc/ti/pm33xx.c @@ -583,7 +583,7 @@ err_wkup_m3_ipc_put: return ret; } -static int am33xx_pm_remove(struct platform_device *pdev) +static void am33xx_pm_remove(struct platform_device *pdev) { pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); @@ -594,7 +594,6 @@ static int am33xx_pm_remove(struct platform_device *pdev) am33xx_pm_free_sram(); iounmap(rtc_base_virt); clk_put(rtc_fck); - return 0; } static struct platform_driver am33xx_pm_driver = { @@ -602,7 +601,7 @@ static struct platform_driver am33xx_pm_driver = { .name = "pm33xx", }, .probe = am33xx_pm_probe, - .remove = am33xx_pm_remove, + .remove_new = am33xx_pm_remove, }; module_platform_driver(am33xx_pm_driver); -- cgit v1.2.3 From d183b20d340b7c098f44cb5c02f4ced01cfd0b16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:28 +0200 Subject: soc/ti: pruss: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Konrad Dybcio # qcom Link: https://lore.kernel.org/r/20230925095532.1984344-38-u.kleine-koenig@pengutronix.de Signed-off-by: Nishanth Menon --- drivers/soc/ti/pruss.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/ti/pruss.c b/drivers/soc/ti/pruss.c index f49f8492dde5..24a42e0b645c 100644 --- a/drivers/soc/ti/pruss.c +++ b/drivers/soc/ti/pruss.c @@ -565,7 +565,7 @@ rpm_disable: return ret; } -static int pruss_remove(struct platform_device *pdev) +static void pruss_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -573,8 +573,6 @@ static int pruss_remove(struct platform_device *pdev) pm_runtime_put_sync(dev); pm_runtime_disable(dev); - - return 0; } /* instance-specific driver private data */ @@ -610,7 +608,7 @@ static struct platform_driver pruss_driver = { .of_match_table = pruss_of_match, }, .probe = pruss_probe, - .remove = pruss_remove, + .remove_new = pruss_remove, }; module_platform_driver(pruss_driver); -- cgit v1.2.3 From ba03aab9bfb4c9d456419da3891375d45c6bfe15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:29 +0200 Subject: soc/ti: smartreflex: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Konrad Dybcio # qcom Link: https://lore.kernel.org/r/20230925095532.1984344-39-u.kleine-koenig@pengutronix.de Signed-off-by: Nishanth Menon --- drivers/soc/ti/smartreflex.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/soc/ti/smartreflex.c b/drivers/soc/ti/smartreflex.c index 62b2f1464e46..d6219060b616 100644 --- a/drivers/soc/ti/smartreflex.c +++ b/drivers/soc/ti/smartreflex.c @@ -933,7 +933,7 @@ err_list_del: return ret; } -static int omap_sr_remove(struct platform_device *pdev) +static void omap_sr_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct omap_sr *sr_info = platform_get_drvdata(pdev); @@ -945,7 +945,6 @@ static int omap_sr_remove(struct platform_device *pdev) pm_runtime_disable(dev); clk_unprepare(sr_info->fck); list_del(&sr_info->node); - return 0; } static void omap_sr_shutdown(struct platform_device *pdev) @@ -970,7 +969,7 @@ MODULE_DEVICE_TABLE(of, omap_sr_match); static struct platform_driver smartreflex_driver = { .probe = omap_sr_probe, - .remove = omap_sr_remove, + .remove_new = omap_sr_remove, .shutdown = omap_sr_shutdown, .driver = { .name = DRIVER_NAME, -- cgit v1.2.3 From 82e83cb51c87b5bf3ab83f7c7b150c19400056c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:30 +0200 Subject: soc/ti: wkup_m3_ipc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Konrad Dybcio # qcom Link: https://lore.kernel.org/r/20230925095532.1984344-40-u.kleine-koenig@pengutronix.de Signed-off-by: Nishanth Menon --- drivers/soc/ti/wkup_m3_ipc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/ti/wkup_m3_ipc.c b/drivers/soc/ti/wkup_m3_ipc.c index 3aff106fc11a..6a1c6b34c414 100644 --- a/drivers/soc/ti/wkup_m3_ipc.c +++ b/drivers/soc/ti/wkup_m3_ipc.c @@ -713,7 +713,7 @@ err_free_mbox: return ret; } -static int wkup_m3_ipc_remove(struct platform_device *pdev) +static void wkup_m3_ipc_remove(struct platform_device *pdev) { wkup_m3_ipc_dbg_destroy(m3_ipc_state); @@ -723,8 +723,6 @@ static int wkup_m3_ipc_remove(struct platform_device *pdev) rproc_put(m3_ipc_state->rproc); m3_ipc_state = NULL; - - return 0; } static int __maybe_unused wkup_m3_ipc_suspend(struct device *dev) @@ -760,7 +758,7 @@ MODULE_DEVICE_TABLE(of, wkup_m3_ipc_of_match); static struct platform_driver wkup_m3_ipc_driver = { .probe = wkup_m3_ipc_probe, - .remove = wkup_m3_ipc_remove, + .remove_new = wkup_m3_ipc_remove, .driver = { .name = "wkup_m3_ipc", .of_match_table = wkup_m3_ipc_of_match, -- cgit v1.2.3 From dd714c568ed4e6f79017be45077de71e9908af03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:09 +0200 Subject: soc: qcom: icc-bwmon: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Krzysztof Kozlowski Acked-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230925095532.1984344-19-u.kleine-koenig@pengutronix.de Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/icc-bwmon.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/qcom/icc-bwmon.c b/drivers/soc/qcom/icc-bwmon.c index adf2d523f103..656706259353 100644 --- a/drivers/soc/qcom/icc-bwmon.c +++ b/drivers/soc/qcom/icc-bwmon.c @@ -793,13 +793,11 @@ static int bwmon_probe(struct platform_device *pdev) return 0; } -static int bwmon_remove(struct platform_device *pdev) +static void bwmon_remove(struct platform_device *pdev) { struct icc_bwmon *bwmon = platform_get_drvdata(pdev); bwmon_disable(bwmon); - - return 0; } static const struct icc_bwmon_data msm8998_bwmon_data = { @@ -862,7 +860,7 @@ MODULE_DEVICE_TABLE(of, bwmon_of_match); static struct platform_driver bwmon_driver = { .probe = bwmon_probe, - .remove = bwmon_remove, + .remove_new = bwmon_remove, .driver = { .name = "qcom-bwmon", .of_match_table = bwmon_of_match, -- cgit v1.2.3 From d85a9d18a58156fc8b5ab185e00e078adaaeefde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:10 +0200 Subject: soc: qcom: llcc-qcom: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230925095532.1984344-20-u.kleine-koenig@pengutronix.de Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/llcc-qcom.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index e3f262a50e4a..674abd0d6700 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -1080,11 +1080,10 @@ static int qcom_llcc_get_cfg_index(struct platform_device *pdev, u8 *cfg_index, return ret; } -static int qcom_llcc_remove(struct platform_device *pdev) +static void qcom_llcc_remove(struct platform_device *pdev) { /* Set the global pointer to a error code to avoid referencing it */ drv_data = ERR_PTR(-ENODEV); - return 0; } static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, u8 index, @@ -1260,7 +1259,7 @@ static struct platform_driver qcom_llcc_driver = { .of_match_table = qcom_llcc_of_match, }, .probe = qcom_llcc_probe, - .remove = qcom_llcc_remove, + .remove_new = qcom_llcc_remove, }; module_platform_driver(qcom_llcc_driver); -- cgit v1.2.3 From 0b742c498bcd7d215501b10fe9df72a16237735a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:11 +0200 Subject: soc: qcom: ocmem: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230925095532.1984344-21-u.kleine-koenig@pengutronix.de Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ocmem.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/qcom/ocmem.c b/drivers/soc/qcom/ocmem.c index 3cf8ab892a70..e8841d247953 100644 --- a/drivers/soc/qcom/ocmem.c +++ b/drivers/soc/qcom/ocmem.c @@ -416,14 +416,12 @@ err_clk_disable: return ret; } -static int ocmem_dev_remove(struct platform_device *pdev) +static void ocmem_dev_remove(struct platform_device *pdev) { struct ocmem *ocmem = platform_get_drvdata(pdev); clk_disable_unprepare(ocmem->core_clk); clk_disable_unprepare(ocmem->iface_clk); - - return 0; } static const struct ocmem_config ocmem_8226_config = { @@ -446,7 +444,7 @@ MODULE_DEVICE_TABLE(of, ocmem_of_match); static struct platform_driver ocmem_driver = { .probe = ocmem_dev_probe, - .remove = ocmem_dev_remove, + .remove_new = ocmem_dev_remove, .driver = { .name = "ocmem", .of_match_table = ocmem_of_match, -- cgit v1.2.3 From 4b3373e42dc2caa34394ac090c8c70bed49badd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:12 +0200 Subject: soc: qcom: pmic_glink: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Konrad Dybcio # qcom Link: https://lore.kernel.org/r/20230925095532.1984344-22-u.kleine-koenig@pengutronix.de Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/pmic_glink.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/qcom/pmic_glink.c b/drivers/soc/qcom/pmic_glink.c index 61c89ddfc75b..914057331afd 100644 --- a/drivers/soc/qcom/pmic_glink.c +++ b/drivers/soc/qcom/pmic_glink.c @@ -318,7 +318,7 @@ out_release_ucsi_aux: return ret; } -static int pmic_glink_remove(struct platform_device *pdev) +static void pmic_glink_remove(struct platform_device *pdev) { struct pmic_glink *pg = dev_get_drvdata(&pdev->dev); @@ -334,8 +334,6 @@ static int pmic_glink_remove(struct platform_device *pdev) mutex_lock(&__pmic_glink_lock); __pmic_glink = NULL; mutex_unlock(&__pmic_glink_lock); - - return 0; } static const unsigned long pmic_glink_sm8450_client_mask = BIT(PMIC_GLINK_CLIENT_BATT) | @@ -352,7 +350,7 @@ MODULE_DEVICE_TABLE(of, pmic_glink_of_match); static struct platform_driver pmic_glink_driver = { .probe = pmic_glink_probe, - .remove = pmic_glink_remove, + .remove_new = pmic_glink_remove, .driver = { .name = "qcom_pmic_glink", .of_match_table = pmic_glink_of_match, -- cgit v1.2.3 From ffbe84a514f863a46a85c1e47b2b6d930b1b463e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:13 +0200 Subject: soc: qcom: qcom_aoss: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Konrad Dybcio # qcom Link: https://lore.kernel.org/r/20230925095532.1984344-23-u.kleine-koenig@pengutronix.de Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/qcom_aoss.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c index 35d35f763eb2..aff0cfb71482 100644 --- a/drivers/soc/qcom/qcom_aoss.c +++ b/drivers/soc/qcom/qcom_aoss.c @@ -533,7 +533,7 @@ err_free_mbox: return ret; } -static int qmp_remove(struct platform_device *pdev) +static void qmp_remove(struct platform_device *pdev) { struct qmp *qmp = platform_get_drvdata(pdev); @@ -542,8 +542,6 @@ static int qmp_remove(struct platform_device *pdev) qmp_close(qmp); mbox_free_channel(qmp->mbox_chan); - - return 0; } static const struct of_device_id qmp_dt_match[] = { @@ -565,7 +563,7 @@ static struct platform_driver qmp_driver = { .suppress_bind_attrs = true, }, .probe = qmp_probe, - .remove = qmp_remove, + .remove_new = qmp_remove, }; module_platform_driver(qmp_driver); -- cgit v1.2.3 From 57b31729bd2c72b00d400106e18db91e9d95d3c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:14 +0200 Subject: soc: qcom: qcom_gsbi: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Konrad Dybcio # qcom Link: https://lore.kernel.org/r/20230925095532.1984344-24-u.kleine-koenig@pengutronix.de Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/qcom_gsbi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/qcom/qcom_gsbi.c b/drivers/soc/qcom/qcom_gsbi.c index df7907a83aa8..f04b9a324ea9 100644 --- a/drivers/soc/qcom/qcom_gsbi.c +++ b/drivers/soc/qcom/qcom_gsbi.c @@ -212,13 +212,11 @@ static int gsbi_probe(struct platform_device *pdev) return of_platform_populate(node, NULL, NULL, &pdev->dev); } -static int gsbi_remove(struct platform_device *pdev) +static void gsbi_remove(struct platform_device *pdev) { struct gsbi_info *gsbi = platform_get_drvdata(pdev); clk_disable_unprepare(gsbi->hclk); - - return 0; } static const struct of_device_id gsbi_dt_match[] = { @@ -234,7 +232,7 @@ static struct platform_driver gsbi_driver = { .of_match_table = gsbi_dt_match, }, .probe = gsbi_probe, - .remove = gsbi_remove, + .remove_new = gsbi_remove, }; module_platform_driver(gsbi_driver); -- cgit v1.2.3 From a47ff90bf2f93ce4ca99858948a74a0c10a2bc45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:15 +0200 Subject: soc: qcom: qcom_stats: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Konrad Dybcio # qcom Link: https://lore.kernel.org/r/20230925095532.1984344-25-u.kleine-koenig@pengutronix.de Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/qcom_stats.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/qcom/qcom_stats.c b/drivers/soc/qcom/qcom_stats.c index c207bb96c523..0216fc24f2ca 100644 --- a/drivers/soc/qcom/qcom_stats.c +++ b/drivers/soc/qcom/qcom_stats.c @@ -216,13 +216,11 @@ static int qcom_stats_probe(struct platform_device *pdev) return 0; } -static int qcom_stats_remove(struct platform_device *pdev) +static void qcom_stats_remove(struct platform_device *pdev) { struct dentry *root = platform_get_drvdata(pdev); debugfs_remove_recursive(root); - - return 0; } static const struct stats_config rpm_data = { @@ -272,7 +270,7 @@ MODULE_DEVICE_TABLE(of, qcom_stats_table); static struct platform_driver qcom_stats = { .probe = qcom_stats_probe, - .remove = qcom_stats_remove, + .remove_new = qcom_stats_remove, .driver = { .name = "qcom_stats", .of_match_table = qcom_stats_table, -- cgit v1.2.3 From 7c93da5b8b69d4e4e7270c33ba3206af43930e1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:16 +0200 Subject: soc: qcom: rmtfs_mem: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Konrad Dybcio # qcom Link: https://lore.kernel.org/r/20230925095532.1984344-26-u.kleine-koenig@pengutronix.de Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rmtfs_mem.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/qcom/rmtfs_mem.c b/drivers/soc/qcom/rmtfs_mem.c index 9c937d24ba42..df850d073102 100644 --- a/drivers/soc/qcom/rmtfs_mem.c +++ b/drivers/soc/qcom/rmtfs_mem.c @@ -290,7 +290,7 @@ put_device: return ret; } -static int qcom_rmtfs_mem_remove(struct platform_device *pdev) +static void qcom_rmtfs_mem_remove(struct platform_device *pdev) { struct qcom_rmtfs_mem *rmtfs_mem = dev_get_drvdata(&pdev->dev); struct qcom_scm_vmperm perm; @@ -305,8 +305,6 @@ static int qcom_rmtfs_mem_remove(struct platform_device *pdev) cdev_device_del(&rmtfs_mem->cdev, &rmtfs_mem->dev); put_device(&rmtfs_mem->dev); - - return 0; } static const struct of_device_id qcom_rmtfs_mem_of_match[] = { @@ -317,7 +315,7 @@ MODULE_DEVICE_TABLE(of, qcom_rmtfs_mem_of_match); static struct platform_driver qcom_rmtfs_mem_driver = { .probe = qcom_rmtfs_mem_probe, - .remove = qcom_rmtfs_mem_remove, + .remove_new = qcom_rmtfs_mem_remove, .driver = { .name = "qcom_rmtfs_mem", .of_match_table = qcom_rmtfs_mem_of_match, -- cgit v1.2.3 From 4b8dee9a34d51a61f60add996fae6a7140a20ae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:17 +0200 Subject: soc: qcom: smem: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Konrad Dybcio # qcom Link: https://lore.kernel.org/r/20230925095532.1984344-27-u.kleine-koenig@pengutronix.de Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/smem.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index 0e644806f897..690afc9a12f4 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -1187,14 +1187,12 @@ static int qcom_smem_probe(struct platform_device *pdev) return 0; } -static int qcom_smem_remove(struct platform_device *pdev) +static void qcom_smem_remove(struct platform_device *pdev) { platform_device_unregister(__smem->socinfo); hwspin_lock_free(__smem->hwlock); __smem = NULL; - - return 0; } static const struct of_device_id qcom_smem_of_match[] = { @@ -1205,7 +1203,7 @@ MODULE_DEVICE_TABLE(of, qcom_smem_of_match); static struct platform_driver qcom_smem_driver = { .probe = qcom_smem_probe, - .remove = qcom_smem_remove, + .remove_new = qcom_smem_remove, .driver = { .name = "qcom-smem", .of_match_table = qcom_smem_of_match, -- cgit v1.2.3 From 1cd966c2dc19654ed08c843e5c933db8c1349636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:18 +0200 Subject: soc: qcom: smp2p: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Konrad Dybcio # qcom Link: https://lore.kernel.org/r/20230925095532.1984344-28-u.kleine-koenig@pengutronix.de Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/smp2p.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c index e9c8030d50ee..914b2246148f 100644 --- a/drivers/soc/qcom/smp2p.c +++ b/drivers/soc/qcom/smp2p.c @@ -660,7 +660,7 @@ report_read_failure: return -EINVAL; } -static int qcom_smp2p_remove(struct platform_device *pdev) +static void qcom_smp2p_remove(struct platform_device *pdev) { struct qcom_smp2p *smp2p = platform_get_drvdata(pdev); struct smp2p_entry *entry; @@ -676,8 +676,6 @@ static int qcom_smp2p_remove(struct platform_device *pdev) mbox_free_channel(smp2p->mbox_chan); smp2p->out->valid_entries = 0; - - return 0; } static const struct of_device_id qcom_smp2p_of_match[] = { @@ -688,7 +686,7 @@ MODULE_DEVICE_TABLE(of, qcom_smp2p_of_match); static struct platform_driver qcom_smp2p_driver = { .probe = qcom_smp2p_probe, - .remove = qcom_smp2p_remove, + .remove_new = qcom_smp2p_remove, .driver = { .name = "qcom_smp2p", .of_match_table = qcom_smp2p_of_match, -- cgit v1.2.3 From bdd7cc62cf69fe989557445d65d6c8cb2f956518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:19 +0200 Subject: soc: qcom: smsm: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Konrad Dybcio # qcom Link: https://lore.kernel.org/r/20230925095532.1984344-29-u.kleine-koenig@pengutronix.de Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/smsm.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/qcom/smsm.c b/drivers/soc/qcom/smsm.c index c58cfff64856..e7c7e9a640a6 100644 --- a/drivers/soc/qcom/smsm.c +++ b/drivers/soc/qcom/smsm.c @@ -613,7 +613,7 @@ out_put: return ret; } -static int qcom_smsm_remove(struct platform_device *pdev) +static void qcom_smsm_remove(struct platform_device *pdev) { struct qcom_smsm *smsm = platform_get_drvdata(pdev); unsigned id; @@ -623,8 +623,6 @@ static int qcom_smsm_remove(struct platform_device *pdev) irq_domain_remove(smsm->entries[id].domain); qcom_smem_state_unregister(smsm->state); - - return 0; } static const struct of_device_id qcom_smsm_of_match[] = { @@ -635,7 +633,7 @@ MODULE_DEVICE_TABLE(of, qcom_smsm_of_match); static struct platform_driver qcom_smsm_driver = { .probe = qcom_smsm_probe, - .remove = qcom_smsm_remove, + .remove_new = qcom_smsm_remove, .driver = { .name = "qcom-smsm", .of_match_table = qcom_smsm_of_match, -- cgit v1.2.3 From c0989f7d1264b2b1885345a28a32fd5e1e61f9c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:20 +0200 Subject: soc: qcom: socinfo: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Konrad Dybcio # qcom Link: https://lore.kernel.org/r/20230925095532.1984344-30-u.kleine-koenig@pengutronix.de Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/socinfo.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 2a15983d9f60..51e05bec5bfc 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -787,20 +787,18 @@ static int qcom_socinfo_probe(struct platform_device *pdev) return 0; } -static int qcom_socinfo_remove(struct platform_device *pdev) +static void qcom_socinfo_remove(struct platform_device *pdev) { struct qcom_socinfo *qs = platform_get_drvdata(pdev); soc_device_unregister(qs->soc_dev); socinfo_debugfs_exit(qs); - - return 0; } static struct platform_driver qcom_socinfo_driver = { .probe = qcom_socinfo_probe, - .remove = qcom_socinfo_remove, + .remove_new = qcom_socinfo_remove, .driver = { .name = "qcom-socinfo", }, -- cgit v1.2.3 From 6b841f34d51c9e0e69751e023130992687c9fd37 Mon Sep 17 00:00:00 2001 From: Mark Tseng Date: Thu, 21 Sep 2023 13:26:35 +0800 Subject: dt-bindings: soc: mediatek: add mt8188 svs dt-bindings Add mt8188 svs compatible in dt-bindings. Signed-off-by: Mark Tseng Acked-by: Krzysztof Kozlowski Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20230921052637.30444-2-chun-jen.tseng@mediatek.com --- Documentation/devicetree/bindings/soc/mediatek/mtk-svs.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/soc/mediatek/mtk-svs.yaml b/Documentation/devicetree/bindings/soc/mediatek/mtk-svs.yaml index f21eb907ee90..7eda63d5682f 100644 --- a/Documentation/devicetree/bindings/soc/mediatek/mtk-svs.yaml +++ b/Documentation/devicetree/bindings/soc/mediatek/mtk-svs.yaml @@ -22,6 +22,7 @@ properties: compatible: enum: - mediatek,mt8183-svs + - mediatek,mt8188-svs - mediatek,mt8192-svs reg: -- cgit v1.2.3 From 1f6c9cbc49bb07b25c2f4b998ebe1248691e082d Mon Sep 17 00:00:00 2001 From: Mark Tseng Date: Thu, 21 Sep 2023 13:26:36 +0800 Subject: soc: mediatek: svs: Add support for MT8188 SoC MT8188 svs gpu uses 2-line high bank and low bank to optimize the voltage of opp table for higher and lower frequency respectively. Signed-off-by: Mark Tseng Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20230921052637.30444-3-chun-jen.tseng@mediatek.com --- drivers/soc/mediatek/mtk-svs.c | 127 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/drivers/soc/mediatek/mtk-svs.c b/drivers/soc/mediatek/mtk-svs.c index 3a2f97cd5272..d2ae0b0cf95a 100644 --- a/drivers/soc/mediatek/mtk-svs.c +++ b/drivers/soc/mediatek/mtk-svs.c @@ -1808,6 +1808,66 @@ static bool svs_mt8192_efuse_parsing(struct svs_platform *svsp) return true; } +static bool svs_mt8188_efuse_parsing(struct svs_platform *svsp) +{ + struct svs_bank *svsb; + u32 idx, i, golden_temp; + int ret; + + for (i = 0; i < svsp->efuse_max; i++) + if (svsp->efuse[i]) + dev_info(svsp->dev, "M_HW_RES%d: 0x%08x\n", + i, svsp->efuse[i]); + + if (!svsp->efuse[5]) { + dev_notice(svsp->dev, "svs_efuse[5] = 0x0?\n"); + return false; + } + + /* Svs efuse parsing */ + for (idx = 0; idx < svsp->bank_max; idx++) { + svsb = &svsp->banks[idx]; + + if (svsb->type == SVSB_LOW) { + svsb->mtdes = svsp->efuse[5] & GENMASK(7, 0); + svsb->bdes = (svsp->efuse[5] >> 16) & GENMASK(7, 0); + svsb->mdes = (svsp->efuse[5] >> 24) & GENMASK(7, 0); + svsb->dcbdet = (svsp->efuse[15] >> 16) & GENMASK(7, 0); + svsb->dcmdet = (svsp->efuse[15] >> 24) & GENMASK(7, 0); + } else if (svsb->type == SVSB_HIGH) { + svsb->mtdes = svsp->efuse[4] & GENMASK(7, 0); + svsb->bdes = (svsp->efuse[4] >> 16) & GENMASK(7, 0); + svsb->mdes = (svsp->efuse[4] >> 24) & GENMASK(7, 0); + svsb->dcbdet = svsp->efuse[14] & GENMASK(7, 0); + svsb->dcmdet = (svsp->efuse[14] >> 8) & GENMASK(7, 0); + } + + svsb->vmax += svsb->dvt_fixed; + } + + ret = svs_get_efuse_data(svsp, "t-calibration-data", + &svsp->tefuse, &svsp->tefuse_max); + if (ret) + return false; + + for (i = 0; i < svsp->tefuse_max; i++) + if (svsp->tefuse[i] != 0) + break; + + if (i == svsp->tefuse_max) + golden_temp = 50; /* All thermal efuse data are 0 */ + else + golden_temp = (svsp->tefuse[0] >> 24) & GENMASK(7, 0); + + for (idx = 0; idx < svsp->bank_max; idx++) { + svsb = &svsp->banks[idx]; + svsb->mts = 500; + svsb->bts = (((500 * golden_temp + 250460) / 1000) - 25) * 4; + } + + return true; +} + static bool svs_mt8183_efuse_parsing(struct svs_platform *svsp) { struct svs_bank *svsb; @@ -2173,6 +2233,61 @@ static struct svs_bank svs_mt8192_banks[] = { }, }; +static struct svs_bank svs_mt8188_banks[] = { + { + .sw_id = SVSB_GPU, + .type = SVSB_LOW, + .set_freq_pct = svs_set_bank_freq_pct_v3, + .get_volts = svs_get_bank_volts_v3, + .volt_flags = SVSB_REMOVE_DVTFIXED_VOLT, + .mode_support = SVSB_MODE_INIT02, + .opp_count = MAX_OPP_ENTRIES, + .freq_base = 640000000, + .turn_freq_base = 640000000, + .volt_step = 6250, + .volt_base = 400000, + .vmax = 0x38, + .vmin = 0x1c, + .age_config = 0x555555, + .dc_config = 0x555555, + .dvt_fixed = 0x1, + .vco = 0x10, + .chk_shift = 0x87, + .core_sel = 0x0fff0000, + .int_st = BIT(0), + .ctl0 = 0x00100003, + }, + { + .sw_id = SVSB_GPU, + .type = SVSB_HIGH, + .set_freq_pct = svs_set_bank_freq_pct_v3, + .get_volts = svs_get_bank_volts_v3, + .tzone_name = "gpu1", + .volt_flags = SVSB_REMOVE_DVTFIXED_VOLT | + SVSB_MON_VOLT_IGNORE, + .mode_support = SVSB_MODE_INIT02 | SVSB_MODE_MON, + .opp_count = MAX_OPP_ENTRIES, + .freq_base = 880000000, + .turn_freq_base = 640000000, + .volt_step = 6250, + .volt_base = 400000, + .vmax = 0x38, + .vmin = 0x1c, + .age_config = 0x555555, + .dc_config = 0x555555, + .dvt_fixed = 0x4, + .vco = 0x10, + .chk_shift = 0x87, + .core_sel = 0x0fff0001, + .int_st = BIT(1), + .ctl0 = 0x00100003, + .tzone_htemp = 85000, + .tzone_htemp_voffset = 0, + .tzone_ltemp = 25000, + .tzone_ltemp_voffset = 7, + }, +}; + static struct svs_bank svs_mt8183_banks[] = { { .sw_id = SVSB_CPU_LITTLE, @@ -2286,6 +2401,15 @@ static const struct svs_platform_data svs_mt8192_platform_data = { .bank_max = ARRAY_SIZE(svs_mt8192_banks), }; +static const struct svs_platform_data svs_mt8188_platform_data = { + .name = "mt8188-svs", + .banks = svs_mt8188_banks, + .efuse_parsing = svs_mt8188_efuse_parsing, + .probe = svs_mt8192_platform_probe, + .regs = svs_regs_v2, + .bank_max = ARRAY_SIZE(svs_mt8188_banks), +}; + static const struct svs_platform_data svs_mt8183_platform_data = { .name = "mt8183-svs", .banks = svs_mt8183_banks, @@ -2299,6 +2423,9 @@ static const struct of_device_id svs_of_match[] = { { .compatible = "mediatek,mt8192-svs", .data = &svs_mt8192_platform_data, + }, { + .compatible = "mediatek,mt8188-svs", + .data = &svs_mt8188_platform_data, }, { .compatible = "mediatek,mt8183-svs", .data = &svs_mt8183_platform_data, -- cgit v1.2.3 From fa095fe0ab3589109dc5add3f34e16a053d90260 Mon Sep 17 00:00:00 2001 From: Mark Tseng Date: Thu, 21 Sep 2023 13:26:37 +0800 Subject: soc: mediatek: svs: Add support for voltage bins Add support voltage bins turn point Signed-off-by: Mark Tseng Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20230921052637.30444-4-chun-jen.tseng@mediatek.com --- drivers/soc/mediatek/mtk-svs.c | 57 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/drivers/soc/mediatek/mtk-svs.c b/drivers/soc/mediatek/mtk-svs.c index d2ae0b0cf95a..f31e3bedff50 100644 --- a/drivers/soc/mediatek/mtk-svs.c +++ b/drivers/soc/mediatek/mtk-svs.c @@ -407,6 +407,7 @@ struct svs_platform_data { * @dcbdet: svs efuse data * @dcmdet: svs efuse data * @turn_pt: 2-line turn point tells which opp_volt calculated by high/low bank + * @vbin_turn_pt: voltage bin turn point helps know which svsb_volt should be overridden * @type: bank type to represent it is 2-line (high/low) bank or 1-line bank * * Svs bank will generate suitalbe voltages by below general math equation @@ -469,6 +470,7 @@ struct svs_bank { u32 dcbdet; u32 dcmdet; u32 turn_pt; + u32 vbin_turn_pt; u32 type; }; @@ -751,11 +753,12 @@ static int svs_status_debug_show(struct seq_file *m, void *v) ret = thermal_zone_get_temp(svsb->tzd, &tzone_temp); if (ret) - seq_printf(m, "%s: temperature ignore, turn_pt = %u\n", - svsb->name, svsb->turn_pt); + seq_printf(m, "%s: temperature ignore, vbin_turn_pt = %u, turn_pt = %u\n", + svsb->name, svsb->vbin_turn_pt, svsb->turn_pt); else - seq_printf(m, "%s: temperature = %d, turn_pt = %u\n", - svsb->name, tzone_temp, svsb->turn_pt); + seq_printf(m, "%s: temperature = %d, vbin_turn_pt = %u, turn_pt = %u\n", + svsb->name, tzone_temp, svsb->vbin_turn_pt, + svsb->turn_pt); for (i = 0; i < svsb->opp_count; i++) { opp = dev_pm_opp_find_freq_exact(svsb->opp_dev, @@ -952,6 +955,29 @@ static void svs_get_bank_volts_v3(struct svs_platform *svsp) for (i = opp_start; i < opp_stop; i++) if (svsb->volt_flags & SVSB_REMOVE_DVTFIXED_VOLT) svsb->volt[i] -= svsb->dvt_fixed; + + /* For voltage bin support */ + if (svsb->opp_dfreq[0] > svsb->freq_base) { + svsb->volt[0] = svs_opp_volt_to_bank_volt(svsb->opp_dvolt[0], + svsb->volt_step, + svsb->volt_base); + + /* Find voltage bin turn point */ + for (i = 0; i < svsb->opp_count; i++) { + if (svsb->opp_dfreq[i] <= svsb->freq_base) { + svsb->vbin_turn_pt = i; + break; + } + } + + /* Override svs bank voltages */ + for (i = 1; i < svsb->vbin_turn_pt; i++) + svsb->volt[i] = interpolate(svsb->freq_pct[0], + svsb->freq_pct[svsb->vbin_turn_pt], + svsb->volt[0], + svsb->volt[svsb->vbin_turn_pt], + svsb->freq_pct[i]); + } } static void svs_set_bank_freq_pct_v3(struct svs_platform *svsp) @@ -1069,6 +1095,29 @@ static void svs_get_bank_volts_v2(struct svs_platform *svsp) for (i = 0; i < svsb->opp_count; i++) svsb->volt[i] += svsb->volt_od; + + /* For voltage bin support */ + if (svsb->opp_dfreq[0] > svsb->freq_base) { + svsb->volt[0] = svs_opp_volt_to_bank_volt(svsb->opp_dvolt[0], + svsb->volt_step, + svsb->volt_base); + + /* Find voltage bin turn point */ + for (i = 0; i < svsb->opp_count; i++) { + if (svsb->opp_dfreq[i] <= svsb->freq_base) { + svsb->vbin_turn_pt = i; + break; + } + } + + /* Override svs bank voltages */ + for (i = 1; i < svsb->vbin_turn_pt; i++) + svsb->volt[i] = interpolate(svsb->freq_pct[0], + svsb->freq_pct[svsb->vbin_turn_pt], + svsb->volt[0], + svsb->volt[svsb->vbin_turn_pt], + svsb->freq_pct[i]); + } } static void svs_set_bank_freq_pct_v2(struct svs_platform *svsp) -- cgit v1.2.3 From 033ca4de129646e9969a6838b44cca0fac38e219 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 25 Sep 2023 15:17:13 +0200 Subject: firmware: arm_scmi: Simplify error path in scmi_dvfs_device_opps_add() Let's simplify the code in scmi_dvfs_device_opps_add() by using dev_pm_opp_remove_all_dynamic() in the error path. Signed-off-by: Ulf Hansson Link: https://lore.kernel.org/r/20230925131715.138411-8-ulf.hansson@linaro.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/perf.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index 9eb58df9124d..733c5ebeb555 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -787,30 +787,22 @@ static int scmi_dvfs_device_opps_add(const struct scmi_protocol_handle *ph, { int idx, ret; unsigned long freq; - struct scmi_opp *opp; struct perf_dom_info *dom; dom = scmi_perf_domain_lookup(ph, domain); if (IS_ERR(dom)) return PTR_ERR(dom); - for (opp = dom->opp, idx = 0; idx < dom->opp_count; idx++, opp++) { + for (idx = 0; idx < dom->opp_count; idx++) { if (!dom->level_indexing_mode) - freq = opp->perf * dom->mult_factor; + freq = dom->opp[idx].perf * dom->mult_factor; else - freq = opp->indicative_freq * 1000; + freq = dom->opp[idx].indicative_freq * 1000; ret = dev_pm_opp_add(dev, freq, 0); if (ret) { dev_warn(dev, "failed to add opp %luHz\n", freq); - - while (idx-- > 0) { - if (!dom->level_indexing_mode) - freq = (--opp)->perf * dom->mult_factor; - else - freq = (--opp)->indicative_freq * 1000; - dev_pm_opp_remove(dev, freq); - } + dev_pm_opp_remove_all_dynamic(dev); return ret; } -- cgit v1.2.3 From 5a6a104193520dc3b66ad2c7d823e00b50734ab6 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 25 Sep 2023 15:17:14 +0200 Subject: firmware: arm_scmi: Specify the performance level when adding an OPP To enable the performance level to be used for OPPs, let's convert into using the dev_pm_opp_add_dynamic() API when creating them. This will be particularly useful for the SCMI performance domain, as shown through subsequent changes. Signed-off-by: Ulf Hansson Link: https://lore.kernel.org/r/20230925131715.138411-9-ulf.hansson@linaro.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/perf.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index 733c5ebeb555..c6a1332164c1 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -787,6 +787,7 @@ static int scmi_dvfs_device_opps_add(const struct scmi_protocol_handle *ph, { int idx, ret; unsigned long freq; + struct dev_pm_opp_data data = {}; struct perf_dom_info *dom; dom = scmi_perf_domain_lookup(ph, domain); @@ -799,7 +800,10 @@ static int scmi_dvfs_device_opps_add(const struct scmi_protocol_handle *ph, else freq = dom->opp[idx].indicative_freq * 1000; - ret = dev_pm_opp_add(dev, freq, 0); + data.level = dom->opp[idx].perf; + data.freq = freq; + + ret = dev_pm_opp_add_dynamic(dev, &data); if (ret) { dev_warn(dev, "failed to add opp %luHz\n", freq); dev_pm_opp_remove_all_dynamic(dev); -- cgit v1.2.3 From 0a81b0eb81ca94f2be9712b85c2d1c32542a17b2 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 25 Sep 2023 15:17:15 +0200 Subject: firmware: arm_scmi: Add generic OPP support to the SCMI performance domain To allow a consumer driver to use the OPP library to scale the performance for its device, let's dynamically add the OPP table when the device gets attached to its SCMI performance domain. Signed-off-by: Ulf Hansson Link: https://lore.kernel.org/r/20230925131715.138411-10-ulf.hansson@linaro.org Signed-off-by: Sudeep Holla --- drivers/pmdomain/arm/scmi_perf_domain.c | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/pmdomain/arm/scmi_perf_domain.c b/drivers/pmdomain/arm/scmi_perf_domain.c index aa100270500f..bc3f78abb6da 100644 --- a/drivers/pmdomain/arm/scmi_perf_domain.c +++ b/drivers/pmdomain/arm/scmi_perf_domain.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -42,6 +43,37 @@ scmi_pd_set_perf_state(struct generic_pm_domain *genpd, unsigned int state) return ret; } +static int +scmi_pd_attach_dev(struct generic_pm_domain *genpd, struct device *dev) +{ + struct scmi_perf_domain *pd = to_scmi_pd(genpd); + int ret; + + /* + * Allow the device to be attached, but don't add the OPP table unless + * the performance level can be changed. + */ + if (!pd->info->set_perf) + return 0; + + ret = pd->perf_ops->device_opps_add(pd->ph, dev, pd->domain_id); + if (ret) + dev_warn(dev, "failed to add OPPs for the device\n"); + + return ret; +} + +static void +scmi_pd_detach_dev(struct generic_pm_domain *genpd, struct device *dev) +{ + struct scmi_perf_domain *pd = to_scmi_pd(genpd); + + if (!pd->info->set_perf) + return; + + dev_pm_opp_remove_all_dynamic(dev); +} + static int scmi_perf_domain_probe(struct scmi_device *sdev) { struct device *dev = &sdev->dev; @@ -95,6 +127,8 @@ static int scmi_perf_domain_probe(struct scmi_device *sdev) scmi_pd->genpd.flags = GENPD_FLAG_ALWAYS_ON | GENPD_FLAG_OPP_TABLE_FW; scmi_pd->genpd.set_performance_state = scmi_pd_set_perf_state; + scmi_pd->genpd.attach_dev = scmi_pd_attach_dev; + scmi_pd->genpd.detach_dev = scmi_pd_detach_dev; ret = pm_genpd_init(&scmi_pd->genpd, NULL, false); if (ret) -- cgit v1.2.3 From 7d0bc6360f17ea323ab25939a34857123d7d87e5 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 3 Oct 2023 09:59:32 +0100 Subject: firmware: arm_ffa: Assign the missing IDR allocation ID to the FFA device Commit 19b8766459c4 ("firmware: arm_ffa: Fix FFA device names for logical partitions") added an ID to the FFA device using ida_alloc() and append the same to "arm-ffa" to make up a unique device name. However it missed to stash the id value in ffa_dev to help freeing the ID later when the device is destroyed. Due to the missing/unassigned ID in FFA device, we get the following warning when the FF-A device is unregistered. | ida_free called for id=0 which is not allocated. | WARNING: CPU: 7 PID: 1 at lib/idr.c:525 ida_free+0x114/0x164 | CPU: 7 PID: 1 Comm: swapper/0 Not tainted 6.6.0-rc4 #209 | pstate: 61400009 (nZCv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--) | pc : ida_free+0x114/0x164 | lr : ida_free+0x114/0x164 | Call trace: | ida_free+0x114/0x164 | ffa_release_device+0x24/0x3c | device_release+0x34/0x8c | kobject_put+0x94/0xf8 | put_device+0x18/0x24 | klist_devices_put+0x14/0x20 | klist_next+0xc8/0x114 | bus_for_each_dev+0xd8/0x144 | arm_ffa_bus_exit+0x30/0x54 | ffa_init+0x68/0x330 | do_one_initcall+0xdc/0x250 | do_initcall_level+0x8c/0xac | do_initcalls+0x54/0x94 | do_basic_setup+0x1c/0x28 | kernel_init_freeable+0x104/0x170 | kernel_init+0x20/0x1a0 | ret_from_fork+0x10/0x20 Fix the same by actually assigning the ID in the FFA device this time for real. Fixes: 19b8766459c4 ("firmware: arm_ffa: Fix FFA device names for logical partitions") Link: https://lore.kernel.org/r/20231003085932.3553985-1-sudeep.holla@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/bus.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/firmware/arm_ffa/bus.c b/drivers/firmware/arm_ffa/bus.c index 2b8bfcd010f5..7865438b3696 100644 --- a/drivers/firmware/arm_ffa/bus.c +++ b/drivers/firmware/arm_ffa/bus.c @@ -193,6 +193,7 @@ struct ffa_device *ffa_device_register(const uuid_t *uuid, int vm_id, dev->release = ffa_release_device; dev_set_name(&ffa_dev->dev, "arm-ffa-%d", id); + ffa_dev->id = id; ffa_dev->vm_id = vm_id; ffa_dev->ops = ops; uuid_copy(&ffa_dev->uuid, uuid); -- cgit v1.2.3 From 2d698e8b4fd22374dac0a2d5150ab24d57a222ab Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 5 Oct 2023 15:28:23 +0100 Subject: firmware: arm_ffa: Allow the FF-A drivers to use 32bit mode of messaging An FF-A ABI could support both the SMC32 and SMC64 conventions. A callee that runs in the AArch64 execution state and implements such an ABI must implement both SMC32 and SMC64 conventions of the ABI. So the FF-A drivers will need the option to choose the mode irrespective of FF-A version and the partition execution mode flag in the partition information. Let us remove the check on the FF-A version for allowing the selection of 32bit mode of messaging. The driver will continue to set the 32-bit mode if the partition execution mode flag specified that the partition supports only 32-bit execution. Fixes: 106b11b1ccd5 ("firmware: arm_ffa: Set up 32bit execution mode flag using partiion property") Link: https://lore.kernel.org/r/20231005142823.278121-1-sudeep.holla@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 121f4fc903cd..7cd6b1564e80 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -587,17 +587,9 @@ static int ffa_partition_info_get(const char *uuid_str, return 0; } -static void _ffa_mode_32bit_set(struct ffa_device *dev) -{ - dev->mode_32bit = true; -} - static void ffa_mode_32bit_set(struct ffa_device *dev) { - if (drv_info->version > FFA_VERSION_1_0) - return; - - _ffa_mode_32bit_set(dev); + dev->mode_32bit = true; } static int ffa_sync_send_receive(struct ffa_device *dev, @@ -706,7 +698,7 @@ static void ffa_setup_partitions(void) if (drv_info->version > FFA_VERSION_1_0 && !(tpbuf->properties & FFA_PARTITION_AARCH64_EXEC)) - _ffa_mode_32bit_set(ffa_dev); + ffa_mode_32bit_set(ffa_dev); } kfree(pbuf); } -- cgit v1.2.3 From 22779149e93d956e9d266f545b26e548bf5b132d Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 5 Oct 2023 18:56:40 +0100 Subject: firmware: arm_ffa: Emit modalias for FF-A devices In order to enable libkmod lookups for FF-A device objects to their corresponding module, add 'modalias' to the base attribute of FF-A devices. Tested-by: Abdellatif El Khlifi Link: https://lore.kernel.org/r/20231005175640.379631-1-sudeep.holla@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/bus.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/arm_ffa/bus.c b/drivers/firmware/arm_ffa/bus.c index 7865438b3696..1c7940ba5539 100644 --- a/drivers/firmware/arm_ffa/bus.c +++ b/drivers/firmware/arm_ffa/bus.c @@ -15,6 +15,8 @@ #include "common.h" +#define SCMI_UEVENT_MODALIAS_FMT "arm_ffa:%04x:%pUb" + static DEFINE_IDA(ffa_bus_id); static int ffa_device_match(struct device *dev, struct device_driver *drv) @@ -63,10 +65,20 @@ static int ffa_device_uevent(const struct device *dev, struct kobj_uevent_env *e { const struct ffa_device *ffa_dev = to_ffa_dev(dev); - return add_uevent_var(env, "MODALIAS=arm_ffa:%04x:%pUb", + return add_uevent_var(env, "MODALIAS=" SCMI_UEVENT_MODALIAS_FMT, ffa_dev->vm_id, &ffa_dev->uuid); } +static ssize_t modalias_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ffa_device *ffa_dev = to_ffa_dev(dev); + + return sysfs_emit(buf, SCMI_UEVENT_MODALIAS_FMT, ffa_dev->vm_id, + &ffa_dev->uuid); +} +static DEVICE_ATTR_RO(modalias); + static ssize_t partition_id_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -88,6 +100,7 @@ static DEVICE_ATTR_RO(uuid); static struct attribute *ffa_device_attributes_attrs[] = { &dev_attr_partition_id.attr, &dev_attr_uuid.attr, + &dev_attr_modalias.attr, NULL, }; ATTRIBUTE_GROUPS(ffa_device_attributes); -- cgit v1.2.3 From 1609626c32c4538439f6333d0b6c912af9f13b77 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 5 Oct 2023 15:44:54 +0100 Subject: firmware: arm_ffa: Update the FF-A command list with v1.1 additions Arm Firmware Framework for A-profile(FFA) v1.1 introduces notifications and indirect messaging based upon notifications support and extends some of the memory interfaces. Let us add all the newly supported FF-A function IDs in the spec. Also update to the error values and associated handling. Link: https://lore.kernel.org/r/20231005-ffa_v1-1_notif-v4-1-cddd3237809c@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 1 + include/linux/arm_ffa.h | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 7cd6b1564e80..a64512388ea5 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -64,6 +64,7 @@ static const int ffa_linux_errmap[] = { -EACCES, /* FFA_RET_DENIED */ -EAGAIN, /* FFA_RET_RETRY */ -ECANCELED, /* FFA_RET_ABORTED */ + -ENODATA, /* FFA_RET_NO_DATA */ }; static inline int ffa_to_linux_errno(int errno) diff --git a/include/linux/arm_ffa.h b/include/linux/arm_ffa.h index cc060da51bec..2ea1717a0825 100644 --- a/include/linux/arm_ffa.h +++ b/include/linux/arm_ffa.h @@ -20,6 +20,7 @@ #define FFA_ERROR FFA_SMC_32(0x60) #define FFA_SUCCESS FFA_SMC_32(0x61) +#define FFA_FN64_SUCCESS FFA_SMC_64(0x61) #define FFA_INTERRUPT FFA_SMC_32(0x62) #define FFA_VERSION FFA_SMC_32(0x63) #define FFA_FEATURES FFA_SMC_32(0x64) @@ -54,6 +55,23 @@ #define FFA_MEM_FRAG_RX FFA_SMC_32(0x7A) #define FFA_MEM_FRAG_TX FFA_SMC_32(0x7B) #define FFA_NORMAL_WORLD_RESUME FFA_SMC_32(0x7C) +#define FFA_NOTIFICATION_BITMAP_CREATE FFA_SMC_32(0x7D) +#define FFA_NOTIFICATION_BITMAP_DESTROY FFA_SMC_32(0x7E) +#define FFA_NOTIFICATION_BIND FFA_SMC_32(0x7F) +#define FFA_NOTIFICATION_UNBIND FFA_SMC_32(0x80) +#define FFA_NOTIFICATION_SET FFA_SMC_32(0x81) +#define FFA_NOTIFICATION_GET FFA_SMC_32(0x82) +#define FFA_NOTIFICATION_INFO_GET FFA_SMC_32(0x83) +#define FFA_FN64_NOTIFICATION_INFO_GET FFA_SMC_64(0x83) +#define FFA_RX_ACQUIRE FFA_SMC_32(0x84) +#define FFA_SPM_ID_GET FFA_SMC_32(0x85) +#define FFA_MSG_SEND2 FFA_SMC_32(0x86) +#define FFA_SECONDARY_EP_REGISTER FFA_SMC_32(0x87) +#define FFA_FN64_SECONDARY_EP_REGISTER FFA_SMC_64(0x87) +#define FFA_MEM_PERM_GET FFA_SMC_32(0x88) +#define FFA_FN64_MEM_PERM_GET FFA_SMC_64(0x88) +#define FFA_MEM_PERM_SET FFA_SMC_32(0x89) +#define FFA_FN64_MEM_PERM_SET FFA_SMC_64(0x89) /* * For some calls it is necessary to use SMC64 to pass or return 64-bit values. @@ -76,6 +94,7 @@ #define FFA_RET_DENIED (-6) #define FFA_RET_RETRY (-7) #define FFA_RET_ABORTED (-8) +#define FFA_RET_NO_DATA (-9) /* FFA version encoding */ #define FFA_MAJOR_VERSION_MASK GENMASK(30, 16) @@ -86,6 +105,7 @@ (FIELD_PREP(FFA_MAJOR_VERSION_MASK, (major)) | \ FIELD_PREP(FFA_MINOR_VERSION_MASK, (minor))) #define FFA_VERSION_1_0 FFA_PACK_VERSION_INFO(1, 0) +#define FFA_VERSION_1_1 FFA_PACK_VERSION_INFO(1, 1) /** * FF-A specification mentions explicitly about '4K pages'. This should -- cgit v1.2.3 From 192e88cfea8ce2d85ab70fa1c8d768042e00b7f1 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 5 Oct 2023 15:44:55 +0100 Subject: firmware: arm_ffa: Implement notification bitmap create and destroy interfaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On systems without a hypervisor the responsibility of requesting the creation of the notification bitmaps in the SPM falls to the FF-A driver. We use FFA features to determine if the ABI is supported, if it is not we can assume there is a hypervisor present and will take care of ensure the relevant notifications bitmaps are created on this partitions behalf. An endpoint’s notification bitmaps needs to be setup before it configures its notifications and before other endpoints and partition managers can start signaling these notifications. Add interface to create and destroy the notification bitmaps and use the same to do the necessary setup during the initialisation and cleanup during the module exit. Link: https://lore.kernel.org/r/20231005-ffa_v1-1_notif-v4-2-cddd3237809c@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 63 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index a64512388ea5..c2ab6f4cf296 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -84,6 +84,7 @@ struct ffa_drv_info { void *rx_buffer; void *tx_buffer; bool mem_ops_native; + bool bitmap_created; }; static struct ffa_drv_info *drv_info; @@ -555,6 +556,37 @@ static int ffa_features(u32 func_feat_id, u32 input_props, return 0; } +static int ffa_notification_bitmap_create(void) +{ + ffa_value_t ret; + u16 vcpu_count = nr_cpu_ids; + + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_NOTIFICATION_BITMAP_CREATE, + .a1 = drv_info->vm_id, .a2 = vcpu_count, + }, &ret); + + if (ret.a0 == FFA_ERROR) + return ffa_to_linux_errno((int)ret.a2); + + return 0; +} + +static int ffa_notification_bitmap_destroy(void) +{ + ffa_value_t ret; + + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_NOTIFICATION_BITMAP_DESTROY, + .a1 = drv_info->vm_id, + }, &ret); + + if (ret.a0 == FFA_ERROR) + return ffa_to_linux_errno((int)ret.a2); + + return 0; +} + static void ffa_set_up_mem_ops_native_flag(void) { if (!ffa_features(FFA_FN_NATIVE(MEM_LEND), 0, NULL, NULL) || @@ -704,6 +736,34 @@ static void ffa_setup_partitions(void) kfree(pbuf); } +static int ffa_notifications_setup(void) +{ + int ret; + + ret = ffa_features(FFA_NOTIFICATION_BITMAP_CREATE, 0, NULL, NULL); + if (ret) { + pr_err("Notifications not supported, continuing with it ..\n"); + return 0; + } + + ret = ffa_notification_bitmap_create(); + if (ret) { + pr_err("notification_bitmap_create error %d\n", ret); + return ret; + } + drv_info->bitmap_created = true; + + return 0; +} + +static void ffa_notifications_cleanup(void) +{ + if (drv_info->bitmap_created) { + ffa_notification_bitmap_destroy(); + drv_info->bitmap_created = false; + } +} + static int __init ffa_init(void) { int ret; @@ -759,7 +819,7 @@ static int __init ffa_init(void) ffa_set_up_mem_ops_native_flag(); - return 0; + return ffa_notifications_setup(); free_pages: if (drv_info->tx_buffer) free_pages_exact(drv_info->tx_buffer, RXTX_BUFFER_SIZE); @@ -774,6 +834,7 @@ subsys_initcall(ffa_init); static void __exit ffa_exit(void) { + ffa_notifications_cleanup(); ffa_rxtx_unmap(drv_info->vm_id); free_pages_exact(drv_info->tx_buffer, RXTX_BUFFER_SIZE); free_pages_exact(drv_info->rx_buffer, RXTX_BUFFER_SIZE); -- cgit v1.2.3 From 933db703e8ce3e30bb9f282afe73f7316338036b Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 5 Oct 2023 15:44:56 +0100 Subject: firmware: arm_ffa: Implement the notification bind and unbind interface A receiver endpoint must bind a notification to any sender endpoint before the latter can signal the notification to the former. The receiver assigns one or more doorbells to a specific sender. Only the sender can ring these doorbells. A receiver uses the FFA_NOTIFICATION_BIND interface to bind one or more notifications to the sender. A receiver un-binds a notification from a sender endpoint to stop the notification from being signaled. It uses the FFA_NOTIFICATION_UNBIND interface to do this. Allow the FF-A driver to be able to bind and unbind a given notification ID to a specific partition ID. This will be used to register and unregister notification callbacks from the FF-A client drivers. Link: https://lore.kernel.org/r/20231005-ffa_v1-1_notif-v4-3-cddd3237809c@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index c2ab6f4cf296..20f8f4ca8e89 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -587,6 +587,35 @@ static int ffa_notification_bitmap_destroy(void) return 0; } +#define NOTIFICATION_LOW_MASK GENMASK(31, 0) +#define NOTIFICATION_HIGH_MASK GENMASK(63, 32) +#define NOTIFICATION_BITMAP_HIGH(x) \ + ((u32)(FIELD_GET(NOTIFICATION_HIGH_MASK, (x)))) +#define NOTIFICATION_BITMAP_LOW(x) \ + ((u32)(FIELD_GET(NOTIFICATION_LOW_MASK, (x)))) + +static int ffa_notification_bind_common(u16 dst_id, u64 bitmap, + u32 flags, bool is_bind) +{ + ffa_value_t ret; + u32 func, src_dst_ids = PACK_TARGET_INFO(dst_id, drv_info->vm_id); + + func = is_bind ? FFA_NOTIFICATION_BIND : FFA_NOTIFICATION_UNBIND; + + invoke_ffa_fn((ffa_value_t){ + .a0 = func, .a1 = src_dst_ids, .a2 = flags, + .a3 = NOTIFICATION_BITMAP_LOW(bitmap), + .a4 = NOTIFICATION_BITMAP_HIGH(bitmap), + }, &ret); + + if (ret.a0 == FFA_ERROR) + return ffa_to_linux_errno((int)ret.a2); + else if (ret.a0 != FFA_SUCCESS) + return -EINVAL; + + return 0; +} + static void ffa_set_up_mem_ops_native_flag(void) { if (!ffa_features(FFA_FN_NATIVE(MEM_LEND), 0, NULL, NULL) || -- cgit v1.2.3 From fe2ddb6b42358ad25a6aed30512fb284522335f3 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 5 Oct 2023 15:44:57 +0100 Subject: firmware: arm_ffa: Implement the FFA_RUN interface FFA_RUN is used by a scheduler to allocate CPU cycles to a target endpoint execution context specified in the target information parameter. If the endpoint execution context is in the waiting/blocked state, it transitions to the running state. Expose the ability to call FFA_RUN in order to give any partition in the system cpu cycles to perform IMPDEF functionality. Link: https://lore.kernel.org/r/20231005-ffa_v1-1_notif-v4-4-cddd3237809c@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 22 ++++++++++++++++++++++ include/linux/arm_ffa.h | 5 +++++ 2 files changed, 27 insertions(+) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 20f8f4ca8e89..82e54d231074 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -616,6 +616,23 @@ static int ffa_notification_bind_common(u16 dst_id, u64 bitmap, return 0; } +static int ffa_run(struct ffa_device *dev, u16 vcpu) +{ + ffa_value_t ret; + u32 target = dev->vm_id << 16 | vcpu; + + invoke_ffa_fn((ffa_value_t){ .a0 = FFA_RUN, .a1 = target, }, &ret); + + while (ret.a0 == FFA_INTERRUPT) + invoke_ffa_fn((ffa_value_t){ .a0 = FFA_RUN, .a1 = ret.a1, }, + &ret); + + if (ret.a0 == FFA_ERROR) + return ffa_to_linux_errno((int)ret.a2); + + return 0; +} + static void ffa_set_up_mem_ops_native_flag(void) { if (!ffa_features(FFA_FN_NATIVE(MEM_LEND), 0, NULL, NULL) || @@ -700,10 +717,15 @@ static const struct ffa_mem_ops ffa_drv_mem_ops = { .memory_lend = ffa_memory_lend, }; +static const struct ffa_cpu_ops ffa_drv_cpu_ops = { + .run = ffa_run, +}; + static const struct ffa_ops ffa_drv_ops = { .info_ops = &ffa_drv_info_ops, .msg_ops = &ffa_drv_msg_ops, .mem_ops = &ffa_drv_mem_ops, + .cpu_ops = &ffa_drv_cpu_ops, }; void ffa_device_match_uuid(struct ffa_device *ffa_dev, const uuid_t *uuid) diff --git a/include/linux/arm_ffa.h b/include/linux/arm_ffa.h index 2ea1717a0825..12fd134bf670 100644 --- a/include/linux/arm_ffa.h +++ b/include/linux/arm_ffa.h @@ -387,10 +387,15 @@ struct ffa_mem_ops { int (*memory_lend)(struct ffa_mem_ops_args *args); }; +struct ffa_cpu_ops { + int (*run)(struct ffa_device *dev, u16 vcpu); +}; + struct ffa_ops { const struct ffa_info_ops *info_ops; const struct ffa_msg_ops *msg_ops; const struct ffa_mem_ops *mem_ops; + const struct ffa_cpu_ops *cpu_ops; }; #endif /* _LINUX_ARM_FFA_H */ -- cgit v1.2.3 From 47561777d69400356aa5b3d4e14411eb9e5784ea Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 5 Oct 2023 15:44:58 +0100 Subject: firmware: arm_ffa: Implement the FFA_NOTIFICATION_SET interface The framework provides an interface to the sender to specify the notification to signal to the receiver. A sender signals a notification by requesting its partition manager to set the corresponding bit in the notifications bitmap of the receiver invoking FFA_NOTIFICATION_SET. Implement the FFA_NOTIFICATION_SET to enable the caller(sender) to send the notifications for any other partitions in the system. Link: https://lore.kernel.org/r/20231005-ffa_v1-1_notif-v4-5-cddd3237809c@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 82e54d231074..4edb3cb48014 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -616,6 +616,26 @@ static int ffa_notification_bind_common(u16 dst_id, u64 bitmap, return 0; } +static +int ffa_notification_set(u16 src_id, u16 dst_id, u32 flags, u64 bitmap) +{ + ffa_value_t ret; + u32 src_dst_ids = PACK_TARGET_INFO(dst_id, src_id); + + invoke_ffa_fn((ffa_value_t) { + .a0 = FFA_NOTIFICATION_SET, .a1 = src_dst_ids, .a2 = flags, + .a3 = NOTIFICATION_BITMAP_LOW(bitmap), + .a4 = NOTIFICATION_BITMAP_HIGH(bitmap), + }, &ret); + + if (ret.a0 == FFA_ERROR) + return ffa_to_linux_errno((int)ret.a2); + else if (ret.a0 != FFA_SUCCESS) + return -EINVAL; + + return 0; +} + static int ffa_run(struct ffa_device *dev, u16 vcpu) { ffa_value_t ret; -- cgit v1.2.3 From faa19623e3e1c8a69b1878132187a171d7a51f98 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 5 Oct 2023 15:44:59 +0100 Subject: firmware: arm_ffa: Implement the FFA_NOTIFICATION_GET interface The framework provides an interface to the receiver to determine the identity of the notification. A receiver endpoint must use the FFA_NOTIFICATION_GET interface to retrieve its pending notifications and handle them. Add the support for FFA_NOTIFICATION_GET to allow the caller(receiver) to fetch its pending notifications from other partitions in the system. Link: https://lore.kernel.org/r/20231005-ffa_v1-1_notif-v4-6-cddd3237809c@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 4edb3cb48014..7a0ac4f496bb 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -593,6 +593,14 @@ static int ffa_notification_bitmap_destroy(void) ((u32)(FIELD_GET(NOTIFICATION_HIGH_MASK, (x)))) #define NOTIFICATION_BITMAP_LOW(x) \ ((u32)(FIELD_GET(NOTIFICATION_LOW_MASK, (x)))) +#define PACK_NOTIFICATION_BITMAP(low, high) \ + (FIELD_PREP(NOTIFICATION_LOW_MASK, (low)) | \ + FIELD_PREP(NOTIFICATION_HIGH_MASK, (high))) + +#define RECEIVER_VCPU_MASK GENMASK(31, 16) +#define PACK_NOTIFICATION_GET_RECEIVER_INFO(vcpu_r, r) \ + (FIELD_PREP(RECEIVER_VCPU_MASK, (vcpu_r)) | \ + FIELD_PREP(RECEIVER_ID_MASK, (r))) static int ffa_notification_bind_common(u16 dst_id, u64 bitmap, u32 flags, bool is_bind) @@ -636,6 +644,35 @@ int ffa_notification_set(u16 src_id, u16 dst_id, u32 flags, u64 bitmap) return 0; } +struct ffa_notify_bitmaps { + u64 sp_map; + u64 vm_map; + u64 arch_map; +}; + +static int ffa_notification_get(u32 flags, struct ffa_notify_bitmaps *notify) +{ + ffa_value_t ret; + u16 src_id = drv_info->vm_id; + u16 cpu_id = smp_processor_id(); + u32 rec_vcpu_ids = PACK_NOTIFICATION_GET_RECEIVER_INFO(cpu_id, src_id); + + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_NOTIFICATION_GET, .a1 = rec_vcpu_ids, .a2 = flags, + }, &ret); + + if (ret.a0 == FFA_ERROR) + return ffa_to_linux_errno((int)ret.a2); + else if (ret.a0 != FFA_SUCCESS) + return -EINVAL; /* Something else went wrong. */ + + notify->sp_map = PACK_NOTIFICATION_BITMAP(ret.a2, ret.a3); + notify->vm_map = PACK_NOTIFICATION_BITMAP(ret.a4, ret.a5); + notify->arch_map = PACK_NOTIFICATION_BITMAP(ret.a6, ret.a7); + + return 0; +} + static int ffa_run(struct ffa_device *dev, u16 vcpu) { ffa_value_t ret; -- cgit v1.2.3 From 3522be48d82b58821442f666eca8b4e547255810 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 5 Oct 2023 15:45:00 +0100 Subject: firmware: arm_ffa: Implement the NOTIFICATION_INFO_GET interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The receiver’s scheduler uses the FFA_NOTIFICATION_INFO_GET interface to retrieve the list of endpoints that have pending notifications and must be run. A notification could be signaled by a sender in the secure world to a VM. The Hypervisor needs to determine which VM and vCPU (in case a per-vCPU notification is signaled) has a pending notification in this scenario. It must obtain this information through an invocation of the FFA_NOTIFICATION_INFO_GET. Add the implementation of the NOTIFICATION_INFO_GET interface and prepare to use this to handle the schedule receiver interrupt. Implementation of handling notifications will be added later. Link: https://lore.kernel.org/r/20231005-ffa_v1-1_notif-v4-7-cddd3237809c@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 73 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 7a0ac4f496bb..b1dd8539ab0d 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -602,6 +602,13 @@ static int ffa_notification_bitmap_destroy(void) (FIELD_PREP(RECEIVER_VCPU_MASK, (vcpu_r)) | \ FIELD_PREP(RECEIVER_ID_MASK, (r))) +#define NOTIFICATION_INFO_GET_MORE_PEND_MASK BIT(0) +#define NOTIFICATION_INFO_GET_ID_COUNT GENMASK(11, 7) +#define ID_LIST_MASK_64 GENMASK(51, 12) +#define ID_LIST_MASK_32 GENMASK(31, 12) +#define MAX_IDS_64 20 +#define MAX_IDS_32 10 + static int ffa_notification_bind_common(u16 dst_id, u64 bitmap, u32 flags, bool is_bind) { @@ -673,6 +680,72 @@ static int ffa_notification_get(u32 flags, struct ffa_notify_bitmaps *notify) return 0; } +static void __do_sched_recv_cb(u16 partition_id, u16 vcpu, bool is_per_vcpu) +{ + pr_err("Callback for partition 0x%x failed.\n", partition_id); +} + +static void ffa_notification_info_get(void) +{ + int idx, list, max_ids, lists_cnt, ids_processed, ids_count[MAX_IDS_64]; + bool is_64b_resp; + ffa_value_t ret; + u64 id_list; + + do { + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_FN_NATIVE(NOTIFICATION_INFO_GET), + }, &ret); + + if (ret.a0 != FFA_FN_NATIVE(SUCCESS) && ret.a0 != FFA_SUCCESS) { + if (ret.a2 != FFA_RET_NO_DATA) + pr_err("Notification Info fetch failed: 0x%lx (0x%lx)", + ret.a0, ret.a2); + return; + } + + is_64b_resp = (ret.a0 == FFA_FN64_SUCCESS); + + ids_processed = 0; + lists_cnt = FIELD_GET(NOTIFICATION_INFO_GET_ID_COUNT, ret.a2); + if (is_64b_resp) { + max_ids = MAX_IDS_64; + id_list = FIELD_GET(ID_LIST_MASK_64, ret.a2); + } else { + max_ids = MAX_IDS_32; + id_list = FIELD_GET(ID_LIST_MASK_32, ret.a2); + } + + for (idx = 0; idx < lists_cnt; idx++, id_list >>= 2) + ids_count[idx] = (id_list & 0x3) + 1; + + /* Process IDs */ + for (list = 0; list < lists_cnt; list++) { + u16 vcpu_id, part_id, *packed_id_list = (u16 *)&ret.a3; + + if (ids_processed >= max_ids - 1) + break; + + part_id = packed_id_list[++ids_processed]; + + if (!ids_count[list]) { /* Global Notification */ + __do_sched_recv_cb(part_id, 0, false); + continue; + } + + /* Per vCPU Notification */ + for (idx = 0; idx < ids_count[list]; idx++) { + if (ids_processed >= max_ids - 1) + break; + + vcpu_id = packed_id_list[++ids_processed]; + + __do_sched_recv_cb(part_id, vcpu_id, true); + } + } + } while (ret.a2 & NOTIFICATION_INFO_GET_MORE_PEND_MASK); +} + static int ffa_run(struct ffa_device *dev, u16 vcpu) { ffa_value_t ret; -- cgit v1.2.3 From 897e9e60c0168ae46f168290548ea24bbbc4005e Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 5 Oct 2023 15:45:01 +0100 Subject: firmware: arm_ffa: Initial support for scheduler receiver interrupt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Framework uses the schedule receiver interrupt to inform the receiver’s scheduler that the receiver must be run to handle a pending notification. A receiver’s scheduler can obtain the description of the schedule receiver interrupt by invoking the FFA_FEATURES interface. The delivery of the physical schedule receiver interrupt from the secure state to the non-secure state depends upon the state of the interrupt controller as configured by the hypervisor. The schedule seceiver interrupt is assumed to be a SGI. The Arm GIC specification defines 16 SGIs. It recommends that they are equally divided between the non-secure and secure states. OS like Linux kernel in the non-secure state typically do not have SGIs to spare. The usage of SGIs in the secure state is however limited. It is more likely that software in the Secure world does not use all the SGIs allocated to it. It is recommended that the secure world software donates an unused SGI to the normal world for use as the schedule receiver interrupt. This implies that secure world software must configure the SGI in the GIC as a non-secure interrupt before presenting it to the normal world. Link: https://lore.kernel.org/r/20231005-ffa_v1-1_notif-v4-8-cddd3237809c@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 186 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 176 insertions(+), 10 deletions(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index b1dd8539ab0d..f8d01840f5ec 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -22,15 +22,20 @@ #define DRIVER_NAME "ARM FF-A" #define pr_fmt(fmt) DRIVER_NAME ": " fmt +#include #include #include +#include #include +#include #include #include #include #include +#include #include #include +#include #include #include "common.h" @@ -76,6 +81,10 @@ static inline int ffa_to_linux_errno(int errno) return -EINVAL; } +struct ffa_pcpu_irq { + struct ffa_drv_info *info; +}; + struct ffa_drv_info { u32 version; u16 vm_id; @@ -85,6 +94,11 @@ struct ffa_drv_info { void *tx_buffer; bool mem_ops_native; bool bitmap_created; + unsigned int sched_recv_irq; + unsigned int cpuhp_state; + struct ffa_pcpu_irq __percpu *irq_pcpu; + struct workqueue_struct *notif_pcpu_wq; + struct work_struct irq_work; }; static struct ffa_drv_info *drv_info; @@ -917,34 +931,182 @@ static void ffa_setup_partitions(void) kfree(pbuf); } -static int ffa_notifications_setup(void) +/* FFA FEATURE IDs */ +#define FFA_FEAT_NOTIFICATION_PENDING_INT (1) +#define FFA_FEAT_SCHEDULE_RECEIVER_INT (2) +#define FFA_FEAT_MANAGED_EXIT_INT (3) + +static irqreturn_t irq_handler(int irq, void *irq_data) { - int ret; + struct ffa_pcpu_irq *pcpu = irq_data; + struct ffa_drv_info *info = pcpu->info; - ret = ffa_features(FFA_NOTIFICATION_BITMAP_CREATE, 0, NULL, NULL); - if (ret) { - pr_err("Notifications not supported, continuing with it ..\n"); - return 0; + queue_work(info->notif_pcpu_wq, &info->irq_work); + + return IRQ_HANDLED; +} + +static void ffa_sched_recv_irq_work_fn(struct work_struct *work) +{ + ffa_notification_info_get(); +} + +static int ffa_sched_recv_irq_map(void) +{ + int ret, irq, sr_intid; + + /* The returned sr_intid is assumed to be SGI donated to NS world */ + ret = ffa_features(FFA_FEAT_SCHEDULE_RECEIVER_INT, 0, &sr_intid, NULL); + if (ret < 0) { + if (ret != -EOPNOTSUPP) + pr_err("Failed to retrieve scheduler Rx interrupt\n"); + return ret; } - ret = ffa_notification_bitmap_create(); + if (acpi_disabled) { + struct of_phandle_args oirq = {}; + struct device_node *gic; + + /* Only GICv3 supported currently with the device tree */ + gic = of_find_compatible_node(NULL, NULL, "arm,gic-v3"); + if (!gic) + return -ENXIO; + + oirq.np = gic; + oirq.args_count = 1; + oirq.args[0] = sr_intid; + irq = irq_create_of_mapping(&oirq); + of_node_put(gic); +#ifdef CONFIG_ACPI + } else { + irq = acpi_register_gsi(NULL, sr_intid, ACPI_EDGE_SENSITIVE, + ACPI_ACTIVE_HIGH); +#endif + } + + if (irq <= 0) { + pr_err("Failed to create IRQ mapping!\n"); + return -ENODATA; + } + + return irq; +} + +static void ffa_sched_recv_irq_unmap(void) +{ + if (drv_info->sched_recv_irq) + irq_dispose_mapping(drv_info->sched_recv_irq); +} + +static int ffa_cpuhp_pcpu_irq_enable(unsigned int cpu) +{ + enable_percpu_irq(drv_info->sched_recv_irq, IRQ_TYPE_NONE); + return 0; +} + +static int ffa_cpuhp_pcpu_irq_disable(unsigned int cpu) +{ + disable_percpu_irq(drv_info->sched_recv_irq); + return 0; +} + +static void ffa_uninit_pcpu_irq(void) +{ + if (drv_info->cpuhp_state) + cpuhp_remove_state(drv_info->cpuhp_state); + + if (drv_info->notif_pcpu_wq) + destroy_workqueue(drv_info->notif_pcpu_wq); + + if (drv_info->sched_recv_irq) + free_percpu_irq(drv_info->sched_recv_irq, drv_info->irq_pcpu); + + if (drv_info->irq_pcpu) + free_percpu(drv_info->irq_pcpu); +} + +static int ffa_init_pcpu_irq(unsigned int irq) +{ + struct ffa_pcpu_irq __percpu *irq_pcpu; + int ret, cpu; + + irq_pcpu = alloc_percpu(struct ffa_pcpu_irq); + if (!irq_pcpu) + return -ENOMEM; + + for_each_present_cpu(cpu) + per_cpu_ptr(irq_pcpu, cpu)->info = drv_info; + + drv_info->irq_pcpu = irq_pcpu; + + ret = request_percpu_irq(irq, irq_handler, "ARM-FFA", irq_pcpu); if (ret) { - pr_err("notification_bitmap_create error %d\n", ret); + pr_err("Error registering notification IRQ %d: %d\n", irq, ret); return ret; } - drv_info->bitmap_created = true; + INIT_WORK(&drv_info->irq_work, ffa_sched_recv_irq_work_fn); + drv_info->notif_pcpu_wq = create_workqueue("ffa_pcpu_irq_notification"); + if (!drv_info->notif_pcpu_wq) + return -EINVAL; + + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "ffa/pcpu-irq:starting", + ffa_cpuhp_pcpu_irq_enable, + ffa_cpuhp_pcpu_irq_disable); + + if (ret < 0) + return ret; + + drv_info->cpuhp_state = ret; return 0; } static void ffa_notifications_cleanup(void) { + ffa_uninit_pcpu_irq(); + ffa_sched_recv_irq_unmap(); + if (drv_info->bitmap_created) { ffa_notification_bitmap_destroy(); drv_info->bitmap_created = false; } } +static int ffa_notifications_setup(void) +{ + int ret, irq; + + ret = ffa_features(FFA_NOTIFICATION_BITMAP_CREATE, 0, NULL, NULL); + if (ret) { + pr_err("Notifications not supported, continuing with it ..\n"); + return 0; + } + + ret = ffa_notification_bitmap_create(); + if (ret) { + pr_err("notification_bitmap_create error %d\n", ret); + return ret; + } + drv_info->bitmap_created = true; + + irq = ffa_sched_recv_irq_map(); + if (irq <= 0) { + ret = irq; + goto cleanup; + } + + drv_info->sched_recv_irq = irq; + + ret = ffa_init_pcpu_irq(irq); + if (ret) + goto cleanup; + + return 0; +cleanup: + ffa_notifications_cleanup(); + return ret; +} + static int __init ffa_init(void) { int ret; @@ -1000,7 +1162,11 @@ static int __init ffa_init(void) ffa_set_up_mem_ops_native_flag(); - return ffa_notifications_setup(); + ret = ffa_notifications_setup(); + if (ret) + goto free_pages; + + return 0; free_pages: if (drv_info->tx_buffer) free_pages_exact(drv_info->tx_buffer, RXTX_BUFFER_SIZE); -- cgit v1.2.3 From 0184450b8b1e7734110472616c4758839e1aff96 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 5 Oct 2023 15:45:02 +0100 Subject: firmware: arm_ffa: Add schedule receiver callback mechanism Enable client drivers to register a callback function that will be called when one or more notifications are pending for a target partition as part of schedule receiver interrupt handling. Link: https://lore.kernel.org/r/20231005-ffa_v1-1_notif-v4-9-cddd3237809c@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 103 ++++++++++++++++++++++++++++++++++++-- include/linux/arm_ffa.h | 8 +++ 2 files changed, 108 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index f8d01840f5ec..04cdb49cc78b 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "common.h" @@ -99,6 +100,8 @@ struct ffa_drv_info { struct ffa_pcpu_irq __percpu *irq_pcpu; struct workqueue_struct *notif_pcpu_wq; struct work_struct irq_work; + struct xarray partition_info; + unsigned int partition_count; }; static struct ffa_drv_info *drv_info; @@ -694,9 +697,26 @@ static int ffa_notification_get(u32 flags, struct ffa_notify_bitmaps *notify) return 0; } -static void __do_sched_recv_cb(u16 partition_id, u16 vcpu, bool is_per_vcpu) +struct ffa_dev_part_info { + ffa_sched_recv_cb callback; + void *cb_data; + rwlock_t rw_lock; +}; + +static void __do_sched_recv_cb(u16 part_id, u16 vcpu, bool is_per_vcpu) { - pr_err("Callback for partition 0x%x failed.\n", partition_id); + struct ffa_dev_part_info *partition; + ffa_sched_recv_cb callback; + void *cb_data; + + partition = xa_load(&drv_info->partition_info, part_id); + read_lock(&partition->rw_lock); + callback = partition->callback; + cb_data = partition->cb_data; + read_unlock(&partition->rw_lock); + + if (callback) + callback(vcpu, is_per_vcpu, cb_data); } static void ffa_notification_info_get(void) @@ -845,6 +865,39 @@ static int ffa_memory_lend(struct ffa_mem_ops_args *args) return ffa_memory_ops(FFA_MEM_LEND, args); } +static int ffa_sched_recv_cb_update(u16 part_id, ffa_sched_recv_cb callback, + void *cb_data, bool is_registration) +{ + struct ffa_dev_part_info *partition; + bool cb_valid; + + partition = xa_load(&drv_info->partition_info, part_id); + write_lock(&partition->rw_lock); + + cb_valid = !!partition->callback; + if (!(is_registration ^ cb_valid)) { + write_unlock(&partition->rw_lock); + return -EINVAL; + } + + partition->callback = callback; + partition->cb_data = cb_data; + + write_unlock(&partition->rw_lock); + return 0; +} + +static int ffa_sched_recv_cb_register(struct ffa_device *dev, + ffa_sched_recv_cb cb, void *cb_data) +{ + return ffa_sched_recv_cb_update(dev->vm_id, cb, cb_data, true); +} + +static int ffa_sched_recv_cb_unregister(struct ffa_device *dev) +{ + return ffa_sched_recv_cb_update(dev->vm_id, NULL, NULL, false); +} + static const struct ffa_info_ops ffa_drv_info_ops = { .api_version_get = ffa_api_version_get, .partition_info_get = ffa_partition_info_get, @@ -865,11 +918,17 @@ static const struct ffa_cpu_ops ffa_drv_cpu_ops = { .run = ffa_run, }; +static const struct ffa_notifier_ops ffa_drv_notifier_ops = { + .sched_recv_cb_register = ffa_sched_recv_cb_register, + .sched_recv_cb_unregister = ffa_sched_recv_cb_unregister, +}; + static const struct ffa_ops ffa_drv_ops = { .info_ops = &ffa_drv_info_ops, .msg_ops = &ffa_drv_msg_ops, .mem_ops = &ffa_drv_mem_ops, .cpu_ops = &ffa_drv_cpu_ops, + .notifier_ops = &ffa_drv_notifier_ops, }; void ffa_device_match_uuid(struct ffa_device *ffa_dev, const uuid_t *uuid) @@ -900,6 +959,7 @@ static void ffa_setup_partitions(void) int count, idx; uuid_t uuid; struct ffa_device *ffa_dev; + struct ffa_dev_part_info *info; struct ffa_partition_info *pbuf, *tpbuf; count = ffa_partition_probe(&uuid_null, &pbuf); @@ -908,6 +968,7 @@ static void ffa_setup_partitions(void) return; } + xa_init(&drv_info->partition_info); for (idx = 0, tpbuf = pbuf; idx < count; idx++, tpbuf++) { import_uuid(&uuid, (u8 *)tpbuf->uuid); @@ -927,10 +988,42 @@ static void ffa_setup_partitions(void) if (drv_info->version > FFA_VERSION_1_0 && !(tpbuf->properties & FFA_PARTITION_AARCH64_EXEC)) ffa_mode_32bit_set(ffa_dev); + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ffa_device_unregister(ffa_dev); + continue; + } + xa_store(&drv_info->partition_info, tpbuf->id, info, GFP_KERNEL); } + drv_info->partition_count = count; + kfree(pbuf); } +static void ffa_partitions_cleanup(void) +{ + struct ffa_dev_part_info **info; + int idx, count = drv_info->partition_count; + + if (!count) + return; + + info = kcalloc(count, sizeof(**info), GFP_KERNEL); + if (!info) + return; + + xa_extract(&drv_info->partition_info, (void **)info, 0, VM_ID_MASK, + count, XA_PRESENT); + + for (idx = 0; idx < count; idx++) + kfree(info[idx]); + kfree(info); + + drv_info->partition_count = 0; + xa_destroy(&drv_info->partition_info); +} + /* FFA FEATURE IDs */ #define FFA_FEAT_NOTIFICATION_PENDING_INT (1) #define FFA_FEAT_SCHEDULE_RECEIVER_INT (2) @@ -1164,9 +1257,11 @@ static int __init ffa_init(void) ret = ffa_notifications_setup(); if (ret) - goto free_pages; + goto partitions_cleanup; return 0; +partitions_cleanup: + ffa_partitions_cleanup(); free_pages: if (drv_info->tx_buffer) free_pages_exact(drv_info->tx_buffer, RXTX_BUFFER_SIZE); @@ -1182,9 +1277,11 @@ subsys_initcall(ffa_init); static void __exit ffa_exit(void) { ffa_notifications_cleanup(); + ffa_partitions_cleanup(); ffa_rxtx_unmap(drv_info->vm_id); free_pages_exact(drv_info->tx_buffer, RXTX_BUFFER_SIZE); free_pages_exact(drv_info->rx_buffer, RXTX_BUFFER_SIZE); + xa_destroy(&drv_info->partition_info); kfree(drv_info); arm_ffa_bus_exit(); } diff --git a/include/linux/arm_ffa.h b/include/linux/arm_ffa.h index 12fd134bf670..f9cf6114ef82 100644 --- a/include/linux/arm_ffa.h +++ b/include/linux/arm_ffa.h @@ -391,11 +391,19 @@ struct ffa_cpu_ops { int (*run)(struct ffa_device *dev, u16 vcpu); }; +typedef void (*ffa_sched_recv_cb)(u16 vcpu, bool is_per_vcpu, void *cb_data); +struct ffa_notifier_ops { + int (*sched_recv_cb_register)(struct ffa_device *dev, + ffa_sched_recv_cb cb, void *cb_data); + int (*sched_recv_cb_unregister)(struct ffa_device *dev); +}; + struct ffa_ops { const struct ffa_info_ops *info_ops; const struct ffa_msg_ops *msg_ops; const struct ffa_mem_ops *mem_ops; const struct ffa_cpu_ops *cpu_ops; + const struct ffa_notifier_ops *notifier_ops; }; #endif /* _LINUX_ARM_FFA_H */ -- cgit v1.2.3 From e0573444edbf4ee7e3c191d3d08a4ccbd26628be Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 5 Oct 2023 15:45:03 +0100 Subject: firmware: arm_ffa: Add interfaces to request notification callbacks Add interface to the FFA driver to allow for client drivers to request and relinquish a notification as well as provide a callback for the notification. Link: https://lore.kernel.org/r/20231005-ffa_v1-1_notif-v4-10-cddd3237809c@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 145 ++++++++++++++++++++++++++++++++++++++ include/linux/arm_ffa.h | 5 ++ 2 files changed, 150 insertions(+) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 04cdb49cc78b..1c3b34eb06e4 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -27,11 +27,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -57,6 +59,8 @@ */ #define RXTX_BUFFER_SIZE SZ_4K +#define FFA_MAX_NOTIFICATIONS 64 + static ffa_fn *invoke_ffa_fn; static const int ffa_linux_errmap[] = { @@ -102,6 +106,8 @@ struct ffa_drv_info { struct work_struct irq_work; struct xarray partition_info; unsigned int partition_count; + DECLARE_HASHTABLE(notifier_hash, ilog2(FFA_MAX_NOTIFICATIONS)); + struct mutex notify_lock; /* lock to protect notifier hashtable */ }; static struct ffa_drv_info *drv_info; @@ -626,6 +632,8 @@ static int ffa_notification_bitmap_destroy(void) #define MAX_IDS_64 20 #define MAX_IDS_32 10 +#define PER_VCPU_NOTIFICATION_FLAG BIT(0) + static int ffa_notification_bind_common(u16 dst_id, u64 bitmap, u32 flags, bool is_bind) { @@ -865,6 +873,21 @@ static int ffa_memory_lend(struct ffa_mem_ops_args *args) return ffa_memory_ops(FFA_MEM_LEND, args); } +#define FFA_SECURE_PARTITION_ID_FLAG BIT(15) + +enum notify_type { + NON_SECURE_VM, + SECURE_PARTITION, + FRAMEWORK, +}; + +struct notifier_cb_info { + struct hlist_node hnode; + ffa_notifier_cb cb; + void *cb_data; + enum notify_type type; +}; + static int ffa_sched_recv_cb_update(u16 part_id, ffa_sched_recv_cb callback, void *cb_data, bool is_registration) { @@ -898,6 +921,123 @@ static int ffa_sched_recv_cb_unregister(struct ffa_device *dev) return ffa_sched_recv_cb_update(dev->vm_id, NULL, NULL, false); } +static int ffa_notification_bind(u16 dst_id, u64 bitmap, u32 flags) +{ + return ffa_notification_bind_common(dst_id, bitmap, flags, true); +} + +static int ffa_notification_unbind(u16 dst_id, u64 bitmap) +{ + return ffa_notification_bind_common(dst_id, bitmap, 0, false); +} + +/* Should be called while the notify_lock is taken */ +static struct notifier_cb_info * +notifier_hash_node_get(u16 notify_id, enum notify_type type) +{ + struct notifier_cb_info *node; + + hash_for_each_possible(drv_info->notifier_hash, node, hnode, notify_id) + if (type == node->type) + return node; + + return NULL; +} + +static int +update_notifier_cb(int notify_id, enum notify_type type, ffa_notifier_cb cb, + void *cb_data, bool is_registration) +{ + struct notifier_cb_info *cb_info = NULL; + bool cb_found; + + cb_info = notifier_hash_node_get(notify_id, type); + cb_found = !!cb_info; + + if (!(is_registration ^ cb_found)) + return -EINVAL; + + if (is_registration) { + cb_info = kzalloc(sizeof(*cb_info), GFP_KERNEL); + if (!cb_info) + return -ENOMEM; + + cb_info->type = type; + cb_info->cb = cb; + cb_info->cb_data = cb_data; + + hash_add(drv_info->notifier_hash, &cb_info->hnode, notify_id); + } else { + hash_del(&cb_info->hnode); + } + + return 0; +} + +static enum notify_type ffa_notify_type_get(u16 vm_id) +{ + if (vm_id & FFA_SECURE_PARTITION_ID_FLAG) + return SECURE_PARTITION; + else + return NON_SECURE_VM; +} + +static int ffa_notify_relinquish(struct ffa_device *dev, int notify_id) +{ + int rc; + enum notify_type type = ffa_notify_type_get(dev->vm_id); + + if (notify_id >= FFA_MAX_NOTIFICATIONS) + return -EINVAL; + + mutex_lock(&drv_info->notify_lock); + + rc = update_notifier_cb(notify_id, type, NULL, NULL, false); + if (rc) { + pr_err("Could not unregister notification callback\n"); + mutex_unlock(&drv_info->notify_lock); + return rc; + } + + rc = ffa_notification_unbind(dev->vm_id, BIT(notify_id)); + + mutex_unlock(&drv_info->notify_lock); + + return rc; +} + +static int ffa_notify_request(struct ffa_device *dev, bool is_per_vcpu, + ffa_notifier_cb cb, void *cb_data, int notify_id) +{ + int rc; + u32 flags = 0; + enum notify_type type = ffa_notify_type_get(dev->vm_id); + + if (notify_id >= FFA_MAX_NOTIFICATIONS) + return -EINVAL; + + mutex_lock(&drv_info->notify_lock); + + if (is_per_vcpu) + flags = PER_VCPU_NOTIFICATION_FLAG; + + rc = ffa_notification_bind(dev->vm_id, BIT(notify_id), flags); + if (rc) { + mutex_unlock(&drv_info->notify_lock); + return rc; + } + + rc = update_notifier_cb(notify_id, type, cb, cb_data, true); + if (rc) { + pr_err("Failed to register callback for %d - %d\n", + notify_id, rc); + ffa_notification_unbind(dev->vm_id, BIT(notify_id)); + } + mutex_unlock(&drv_info->notify_lock); + + return rc; +} + static const struct ffa_info_ops ffa_drv_info_ops = { .api_version_get = ffa_api_version_get, .partition_info_get = ffa_partition_info_get, @@ -921,6 +1061,8 @@ static const struct ffa_cpu_ops ffa_drv_cpu_ops = { static const struct ffa_notifier_ops ffa_drv_notifier_ops = { .sched_recv_cb_register = ffa_sched_recv_cb_register, .sched_recv_cb_unregister = ffa_sched_recv_cb_unregister, + .notify_request = ffa_notify_request, + .notify_relinquish = ffa_notify_relinquish, }; static const struct ffa_ops ffa_drv_ops = { @@ -1194,6 +1336,9 @@ static int ffa_notifications_setup(void) if (ret) goto cleanup; + hash_init(drv_info->notifier_hash); + mutex_init(&drv_info->notify_lock); + return 0; cleanup: ffa_notifications_cleanup(); diff --git a/include/linux/arm_ffa.h b/include/linux/arm_ffa.h index f9cf6114ef82..fb6f388a3737 100644 --- a/include/linux/arm_ffa.h +++ b/include/linux/arm_ffa.h @@ -392,10 +392,15 @@ struct ffa_cpu_ops { }; typedef void (*ffa_sched_recv_cb)(u16 vcpu, bool is_per_vcpu, void *cb_data); +typedef void (*ffa_notifier_cb)(int notify_id, void *cb_data); + struct ffa_notifier_ops { int (*sched_recv_cb_register)(struct ffa_device *dev, ffa_sched_recv_cb cb, void *cb_data); int (*sched_recv_cb_unregister)(struct ffa_device *dev); + int (*notify_request)(struct ffa_device *dev, bool per_vcpu, + ffa_notifier_cb cb, void *cb_data, int notify_id); + int (*notify_relinquish)(struct ffa_device *dev, int notify_id); }; struct ffa_ops { -- cgit v1.2.3 From e5adb3b20e39dbf18651322a9bc24eb55050188f Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 5 Oct 2023 15:45:04 +0100 Subject: firmware: arm_ffa: Add interface to send a notification to a given partition The framework provides an interface to the sender endpoint to specify the notification to signal to the receiver endpoint. A sender signals a notification by requesting its partition manager to set the corresponding bit in the notifications bitmap of the receiver. Expose the ability to send a notification to another partition. Link: https://lore.kernel.org/r/20231005-ffa_v1-1_notif-v4-11-cddd3237809c@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 13 +++++++++++++ include/linux/arm_ffa.h | 2 ++ 2 files changed, 15 insertions(+) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 1c3b34eb06e4..590005234073 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -1038,6 +1038,18 @@ static int ffa_notify_request(struct ffa_device *dev, bool is_per_vcpu, return rc; } +static int ffa_notify_send(struct ffa_device *dev, int notify_id, + bool is_per_vcpu, u16 vcpu) +{ + u32 flags = 0; + + if (is_per_vcpu) + flags |= (PER_VCPU_NOTIFICATION_FLAG | vcpu << 16); + + return ffa_notification_set(dev->vm_id, drv_info->vm_id, flags, + BIT(notify_id)); +} + static const struct ffa_info_ops ffa_drv_info_ops = { .api_version_get = ffa_api_version_get, .partition_info_get = ffa_partition_info_get, @@ -1063,6 +1075,7 @@ static const struct ffa_notifier_ops ffa_drv_notifier_ops = { .sched_recv_cb_unregister = ffa_sched_recv_cb_unregister, .notify_request = ffa_notify_request, .notify_relinquish = ffa_notify_relinquish, + .notify_send = ffa_notify_send, }; static const struct ffa_ops ffa_drv_ops = { diff --git a/include/linux/arm_ffa.h b/include/linux/arm_ffa.h index fb6f388a3737..f6df81f14b6d 100644 --- a/include/linux/arm_ffa.h +++ b/include/linux/arm_ffa.h @@ -401,6 +401,8 @@ struct ffa_notifier_ops { int (*notify_request)(struct ffa_device *dev, bool per_vcpu, ffa_notifier_cb cb, void *cb_data, int notify_id); int (*notify_relinquish)(struct ffa_device *dev, int notify_id); + int (*notify_send)(struct ffa_device *dev, int notify_id, bool per_vcpu, + u16 vcpu); }; struct ffa_ops { -- cgit v1.2.3 From 1b6bf41b7a65cf2ce4485f740a2764529635153c Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 5 Oct 2023 15:45:05 +0100 Subject: firmware: arm_ffa: Add notification handling mechanism With all the necessary plumbing in place, let us add handling the notifications as part of schedule receiver interrupt handler. In order to do so, we need to just register scheduling callback on behalf of the driver partition. Link: https://lore.kernel.org/r/20231005-ffa_v1-1_notif-v4-12-cddd3237809c@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 67 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 590005234073..6f688b4a4620 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -103,6 +103,7 @@ struct ffa_drv_info { unsigned int cpuhp_state; struct ffa_pcpu_irq __percpu *irq_pcpu; struct workqueue_struct *notif_pcpu_wq; + struct work_struct notif_pcpu_work; struct work_struct irq_work; struct xarray partition_info; unsigned int partition_count; @@ -633,6 +634,10 @@ static int ffa_notification_bitmap_destroy(void) #define MAX_IDS_32 10 #define PER_VCPU_NOTIFICATION_FLAG BIT(0) +#define SECURE_PARTITION_BITMAP BIT(0) +#define NON_SECURE_VM_BITMAP BIT(1) +#define SPM_FRAMEWORK_BITMAP BIT(2) +#define NS_HYP_FRAMEWORK_BITMAP BIT(3) static int ffa_notification_bind_common(u16 dst_id, u64 bitmap, u32 flags, bool is_bind) @@ -1050,6 +1055,54 @@ static int ffa_notify_send(struct ffa_device *dev, int notify_id, BIT(notify_id)); } +static void handle_notif_callbacks(u64 bitmap, enum notify_type type) +{ + int notify_id; + struct notifier_cb_info *cb_info = NULL; + + for (notify_id = 0; notify_id <= FFA_MAX_NOTIFICATIONS && bitmap; + notify_id++, bitmap >>= 1) { + if (!(bitmap & 1)) + continue; + + mutex_lock(&drv_info->notify_lock); + cb_info = notifier_hash_node_get(notify_id, type); + mutex_unlock(&drv_info->notify_lock); + + if (cb_info && cb_info->cb) + cb_info->cb(notify_id, cb_info->cb_data); + } +} + +static void notif_pcpu_irq_work_fn(struct work_struct *work) +{ + int rc; + struct ffa_notify_bitmaps bitmaps; + + rc = ffa_notification_get(SECURE_PARTITION_BITMAP | + SPM_FRAMEWORK_BITMAP, &bitmaps); + if (rc) { + pr_err("Failed to retrieve notifications with %d!\n", rc); + return; + } + + handle_notif_callbacks(bitmaps.vm_map, NON_SECURE_VM); + handle_notif_callbacks(bitmaps.sp_map, SECURE_PARTITION); + handle_notif_callbacks(bitmaps.arch_map, FRAMEWORK); +} + +static void +ffa_self_notif_handle(u16 vcpu, bool is_per_vcpu, void *cb_data) +{ + struct ffa_drv_info *info = cb_data; + + if (!is_per_vcpu) + notif_pcpu_irq_work_fn(&info->notif_pcpu_work); + else + queue_work_on(vcpu, info->notif_pcpu_wq, + &info->notif_pcpu_work); +} + static const struct ffa_info_ops ffa_drv_info_ops = { .api_version_get = ffa_api_version_get, .partition_info_get = ffa_partition_info_get, @@ -1154,6 +1207,13 @@ static void ffa_setup_partitions(void) drv_info->partition_count = count; kfree(pbuf); + + /* Allocate for the host */ + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return; + xa_store(&drv_info->partition_info, drv_info->vm_id, info, GFP_KERNEL); + drv_info->partition_count++; } static void ffa_partitions_cleanup(void) @@ -1294,6 +1354,7 @@ static int ffa_init_pcpu_irq(unsigned int irq) } INIT_WORK(&drv_info->irq_work, ffa_sched_recv_irq_work_fn); + INIT_WORK(&drv_info->notif_pcpu_work, notif_pcpu_irq_work_fn); drv_info->notif_pcpu_wq = create_workqueue("ffa_pcpu_irq_notification"); if (!drv_info->notif_pcpu_wq) return -EINVAL; @@ -1352,7 +1413,11 @@ static int ffa_notifications_setup(void) hash_init(drv_info->notifier_hash); mutex_init(&drv_info->notify_lock); - return 0; + /* Register internal scheduling callback */ + ret = ffa_sched_recv_cb_update(drv_info->vm_id, ffa_self_notif_handle, + drv_info, true); + if (!ret) + return ret; cleanup: ffa_notifications_cleanup(); return ret; -- cgit v1.2.3 From c9b21ef0d0a87695d7bfeee9a04b89760b49ccf5 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 5 Oct 2023 15:45:06 +0100 Subject: firmware: arm_ffa: Simplify the computation of transmit and fragment length The computation of endpoint memory access descriptor's composite memory region descriptor offset is using COMPOSITE_CONSTITUENTS_OFFSET which is unnecessary complicated. Composite memory region descriptor always follow the endpoint memory access descriptor array and hence it is computed accordingly. COMPOSITE_CONSTITUENTS_OFFSET is useless and wrong for any input other than endpoint memory access descriptor count. Let us drop the usage of COMPOSITE_CONSTITUENTS_OFFSET to simplify the computation of total transmit and fragment length in the memory transactions. Link: https://lore.kernel.org/r/20231005-ffa_v1-1_notif-v4-13-cddd3237809c@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 14 ++++++++------ include/linux/arm_ffa.h | 2 -- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 6f688b4a4620..49fcbeb63eaa 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -442,23 +442,25 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize, { int rc = 0; bool first = true; + u32 composite_offset; phys_addr_t addr = 0; + struct ffa_mem_region *mem_region = buffer; struct ffa_composite_mem_region *composite; struct ffa_mem_region_addr_range *constituents; struct ffa_mem_region_attributes *ep_mem_access; - struct ffa_mem_region *mem_region = buffer; u32 idx, frag_len, length, buf_sz = 0, num_entries = sg_nents(args->sg); mem_region->tag = args->tag; mem_region->flags = args->flags; mem_region->sender_id = drv_info->vm_id; mem_region->attributes = ffa_memory_attributes_get(func_id); - ep_mem_access = &mem_region->ep_mem_access[0]; + ep_mem_access = buffer + COMPOSITE_OFFSET(0); + composite_offset = COMPOSITE_OFFSET(args->nattrs); for (idx = 0; idx < args->nattrs; idx++, ep_mem_access++) { ep_mem_access->receiver = args->attrs[idx].receiver; ep_mem_access->attrs = args->attrs[idx].attrs; - ep_mem_access->composite_off = COMPOSITE_OFFSET(args->nattrs); + ep_mem_access->composite_off = composite_offset; ep_mem_access->flag = 0; ep_mem_access->reserved = 0; } @@ -467,13 +469,13 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize, mem_region->reserved_1 = 0; mem_region->ep_count = args->nattrs; - composite = buffer + COMPOSITE_OFFSET(args->nattrs); + composite = buffer + composite_offset; composite->total_pg_cnt = ffa_get_num_pages_sg(args->sg); composite->addr_range_cnt = num_entries; composite->reserved = 0; - length = COMPOSITE_CONSTITUENTS_OFFSET(args->nattrs, num_entries); - frag_len = COMPOSITE_CONSTITUENTS_OFFSET(args->nattrs, 0); + length = composite_offset + CONSTITUENTS_OFFSET(num_entries); + frag_len = composite_offset + CONSTITUENTS_OFFSET(0); if (frag_len > max_fragsize) return -ENXIO; diff --git a/include/linux/arm_ffa.h b/include/linux/arm_ffa.h index f6df81f14b6d..748d0a83a4bc 100644 --- a/include/linux/arm_ffa.h +++ b/include/linux/arm_ffa.h @@ -356,8 +356,6 @@ struct ffa_mem_region { (offsetof(struct ffa_mem_region, ep_mem_access[x])) #define CONSTITUENTS_OFFSET(x) \ (offsetof(struct ffa_composite_mem_region, constituents[x])) -#define COMPOSITE_CONSTITUENTS_OFFSET(x, y) \ - (COMPOSITE_OFFSET(x) + CONSTITUENTS_OFFSET(y)) struct ffa_mem_ops_args { bool use_txbuf; -- cgit v1.2.3 From 908af696c513bdd028fb16499f12febc0dc48acd Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 6 Oct 2023 17:46:50 -0500 Subject: firmware: arm_scpi: Use device_get_match_data() Use preferred device_get_match_data() instead of of_match_device() to get the driver match data. With this, adjust the includes to explicitly include the correct headers. Signed-off-by: Rob Herring Link: https://lore.kernel.org/r/20231006224650.445424-1-robh@kernel.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scpi.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c index 435d0e2658a4..3f123f592cb4 100644 --- a/drivers/firmware/arm_scpi.c +++ b/drivers/firmware/arm_scpi.c @@ -26,9 +26,12 @@ #include #include #include +#include #include #include +#include #include +#include #include #include #include @@ -894,11 +897,6 @@ static int scpi_alloc_xfer_list(struct device *dev, struct scpi_chan *ch) return 0; } -static const struct of_device_id legacy_scpi_of_match[] = { - {.compatible = "arm,scpi-pre-1.0"}, - {}, -}; - static const struct of_device_id shmem_of_match[] __maybe_unused = { { .compatible = "amlogic,meson-gxbb-scp-shmem", }, { .compatible = "amlogic,meson-axg-scp-shmem", }, @@ -919,8 +917,7 @@ static int scpi_probe(struct platform_device *pdev) if (!scpi_drvinfo) return -ENOMEM; - if (of_match_device(legacy_scpi_of_match, &pdev->dev)) - scpi_drvinfo->is_legacy = true; + scpi_drvinfo->is_legacy = !!device_get_match_data(dev); count = of_count_phandle_with_args(np, "mboxes", "#mbox-cells"); if (count < 0) { @@ -1038,7 +1035,7 @@ static int scpi_probe(struct platform_device *pdev) static const struct of_device_id scpi_of_match[] = { {.compatible = "arm,scpi"}, - {.compatible = "arm,scpi-pre-1.0"}, + {.compatible = "arm,scpi-pre-1.0", .data = (void *)1UL }, {}, }; -- cgit v1.2.3 From 3537a75e73f3420614a358d0c8b390ea483cc87d Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Wed, 4 Oct 2023 20:36:00 +0100 Subject: clk: scmi: Free scmi_clk allocated when the clocks with invalid info are skipped Add the missing devm_kfree() when we skip the clocks with invalid or missing information from the firmware. Cc: Cristian Marussi Cc: Michael Turquette Cc: Stephen Boyd Cc: linux-clk@vger.kernel.org Fixes: 6d6a1d82eaef ("clk: add support for clocks provided by SCMI") Link: https://lore.kernel.org/r/20231004193600.66232-1-sudeep.holla@arm.com Signed-off-by: Sudeep Holla --- drivers/clk/clk-scmi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c index 2e1337b511eb..3f525bed9794 100644 --- a/drivers/clk/clk-scmi.c +++ b/drivers/clk/clk-scmi.c @@ -232,6 +232,7 @@ static int scmi_clocks_probe(struct scmi_device *sdev) sclk->info = scmi_proto_clk_ops->info_get(ph, idx); if (!sclk->info) { dev_dbg(dev, "invalid clock info for idx %d\n", idx); + devm_kfree(dev, sclk); continue; } -- cgit v1.2.3 From 77bbfe607b1d306c88bf96fed00c030f6bf462f1 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 4 Oct 2023 07:42:23 +0800 Subject: firmware: arm_scmi: Add support for clock parents SCMI v3.2 spec introduces CLOCK_POSSIBLE_PARENTS_GET, CLOCK_PARENT_SET and CLOCK_PARENT_GET. Add support for these to enable clock parents and use them in the clock driver. Reviewed-by: Cristian Marussi Signed-off-by: Peng Fan Link: https://lore.kernel.org/r/20231004-scmi-clock-v3-v5-1-1b8a1435673e@nxp.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 181 ++++++++++++++++++++++++++++++++++++-- include/linux/scmi_protocol.h | 6 ++ 2 files changed, 181 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 9c0e33c1efab..42b81c181d68 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -22,6 +22,9 @@ enum scmi_clock_protocol_cmd { CLOCK_RATE_NOTIFY = 0x9, CLOCK_RATE_CHANGE_REQUESTED_NOTIFY = 0xA, CLOCK_CONFIG_GET = 0xB, + CLOCK_POSSIBLE_PARENTS_GET = 0xC, + CLOCK_PARENT_SET = 0xD, + CLOCK_PARENT_GET = 0xE, }; enum clk_state { @@ -42,10 +45,28 @@ struct scmi_msg_resp_clock_attributes { #define SUPPORTS_RATE_CHANGED_NOTIF(x) ((x) & BIT(31)) #define SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(x) ((x) & BIT(30)) #define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(29)) +#define SUPPORTS_PARENT_CLOCK(x) ((x) & BIT(28)) u8 name[SCMI_SHORT_NAME_MAX_SIZE]; __le32 clock_enable_latency; }; +struct scmi_msg_clock_possible_parents { + __le32 id; + __le32 skip_parents; +}; + +struct scmi_msg_resp_clock_possible_parents { + __le32 num_parent_flags; +#define NUM_PARENTS_RETURNED(x) ((x) & 0xff) +#define NUM_PARENTS_REMAINING(x) ((x) >> 24) + __le32 possible_parents[]; +}; + +struct scmi_msg_clock_set_parent { + __le32 id; + __le32 parent_id; +}; + struct scmi_msg_clock_config_set { __le32 id; __le32 attributes; @@ -168,6 +189,98 @@ scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph, return ret; } +struct scmi_clk_ipriv { + struct device *dev; + u32 clk_id; + struct scmi_clock_info *clk; +}; + +static void iter_clk_possible_parents_prepare_message(void *message, unsigned int desc_index, + const void *priv) +{ + struct scmi_msg_clock_possible_parents *msg = message; + const struct scmi_clk_ipriv *p = priv; + + msg->id = cpu_to_le32(p->clk_id); + /* Set the number of OPPs to be skipped/already read */ + msg->skip_parents = cpu_to_le32(desc_index); +} + +static int iter_clk_possible_parents_update_state(struct scmi_iterator_state *st, + const void *response, void *priv) +{ + const struct scmi_msg_resp_clock_possible_parents *r = response; + struct scmi_clk_ipriv *p = priv; + struct device *dev = ((struct scmi_clk_ipriv *)p)->dev; + u32 flags; + + flags = le32_to_cpu(r->num_parent_flags); + st->num_returned = NUM_PARENTS_RETURNED(flags); + st->num_remaining = NUM_PARENTS_REMAINING(flags); + + /* + * num parents is not declared previously anywhere so we + * assume it's returned+remaining on first call. + */ + if (!st->max_resources) { + p->clk->num_parents = st->num_returned + st->num_remaining; + p->clk->parents = devm_kcalloc(dev, p->clk->num_parents, + sizeof(*p->clk->parents), + GFP_KERNEL); + if (!p->clk->parents) { + p->clk->num_parents = 0; + return -ENOMEM; + } + st->max_resources = st->num_returned + st->num_remaining; + } + + return 0; +} + +static int iter_clk_possible_parents_process_response(const struct scmi_protocol_handle *ph, + const void *response, + struct scmi_iterator_state *st, + void *priv) +{ + const struct scmi_msg_resp_clock_possible_parents *r = response; + struct scmi_clk_ipriv *p = priv; + + u32 *parent = &p->clk->parents[st->desc_index + st->loop_idx]; + + *parent = le32_to_cpu(r->possible_parents[st->loop_idx]); + + return 0; +} + +static int scmi_clock_possible_parents(const struct scmi_protocol_handle *ph, u32 clk_id, + struct scmi_clock_info *clk) +{ + struct scmi_iterator_ops ops = { + .prepare_message = iter_clk_possible_parents_prepare_message, + .update_state = iter_clk_possible_parents_update_state, + .process_response = iter_clk_possible_parents_process_response, + }; + + struct scmi_clk_ipriv ppriv = { + .clk_id = clk_id, + .clk = clk, + .dev = ph->dev, + }; + void *iter; + int ret; + + iter = ph->hops->iter_response_init(ph, &ops, 0, + CLOCK_POSSIBLE_PARENTS_GET, + sizeof(struct scmi_msg_clock_possible_parents), + &ppriv); + if (IS_ERR(iter)) + return PTR_ERR(iter); + + ret = ph->hops->iter_response_run(iter); + + return ret; +} + static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph, u32 clk_id, struct scmi_clock_info *clk, u32 version) @@ -212,6 +325,8 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph, clk->rate_changed_notifications = true; if (SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(attributes)) clk->rate_change_requested_notifications = true; + if (SUPPORTS_PARENT_CLOCK(attributes)) + scmi_clock_possible_parents(ph, clk_id, clk); } return ret; @@ -229,12 +344,6 @@ static int rate_cmp_func(const void *_r1, const void *_r2) return 1; } -struct scmi_clk_ipriv { - struct device *dev; - u32 clk_id; - struct scmi_clock_info *clk; -}; - static void iter_clk_describe_prepare_message(void *message, const unsigned int desc_index, const void *priv) @@ -458,6 +567,64 @@ scmi_clock_config_set(const struct scmi_protocol_handle *ph, u32 clk_id, return ret; } +static int +scmi_clock_set_parent(const struct scmi_protocol_handle *ph, u32 clk_id, + u32 parent_id) +{ + int ret; + struct scmi_xfer *t; + struct scmi_msg_clock_set_parent *cfg; + struct clock_info *ci = ph->get_priv(ph); + struct scmi_clock_info *clk; + + if (clk_id >= ci->num_clocks) + return -EINVAL; + + clk = ci->clk + clk_id; + + if (parent_id >= clk->num_parents) + return -EINVAL; + + ret = ph->xops->xfer_get_init(ph, CLOCK_PARENT_SET, + sizeof(*cfg), 0, &t); + if (ret) + return ret; + + t->hdr.poll_completion = false; + + cfg = t->tx.buf; + cfg->id = cpu_to_le32(clk_id); + cfg->parent_id = cpu_to_le32(clk->parents[parent_id]); + + ret = ph->xops->do_xfer(ph, t); + + ph->xops->xfer_put(ph, t); + + return ret; +} + +static int +scmi_clock_get_parent(const struct scmi_protocol_handle *ph, u32 clk_id, + u32 *parent_id) +{ + int ret; + struct scmi_xfer *t; + + ret = ph->xops->xfer_get_init(ph, CLOCK_PARENT_GET, + sizeof(__le32), sizeof(u32), &t); + if (ret) + return ret; + + put_unaligned_le32(clk_id, t->tx.buf); + + ret = ph->xops->do_xfer(ph, t); + if (!ret) + *parent_id = get_unaligned_le32(t->rx.buf); + + ph->xops->xfer_put(ph, t); + return ret; +} + /* For SCMI clock v2.1 and onwards */ static int scmi_clock_config_set_v2(const struct scmi_protocol_handle *ph, u32 clk_id, @@ -650,6 +817,8 @@ static const struct scmi_clk_proto_ops clk_proto_ops = { .state_get = scmi_clock_state_get, .config_oem_get = scmi_clock_config_oem_get, .config_oem_set = scmi_clock_config_oem_set, + .parent_set = scmi_clock_set_parent, + .parent_get = scmi_clock_get_parent, }; static int scmi_clk_rate_notify(const struct scmi_protocol_handle *ph, diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 27bfa5a65b45..f2f05fb42d28 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -58,6 +58,8 @@ struct scmi_clock_info { u64 step_size; } range; }; + int num_parents; + u32 *parents; }; enum scmi_power_scale { @@ -83,6 +85,8 @@ struct scmi_protocol_handle; * @state_get: get the status of the specified clock * @config_oem_get: get the value of an OEM specific clock config * @config_oem_set: set the value of an OEM specific clock config + * @parent_get: get the parent id of a clk + * @parent_set: set the parent of a clock */ struct scmi_clk_proto_ops { int (*count_get)(const struct scmi_protocol_handle *ph); @@ -104,6 +108,8 @@ struct scmi_clk_proto_ops { bool atomic); int (*config_oem_set)(const struct scmi_protocol_handle *ph, u32 clk_id, u8 oem_type, u32 oem_val, bool atomic); + int (*parent_get)(const struct scmi_protocol_handle *ph, u32 clk_id, u32 *parent_id); + int (*parent_set)(const struct scmi_protocol_handle *ph, u32 clk_id, u32 parent_id); }; struct scmi_perf_domain_info { -- cgit v1.2.3 From 65a8a3dd3b95f0310da76f529f86287fefed6a28 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 4 Oct 2023 07:42:24 +0800 Subject: clk: scmi: Add support for clock {set,get}_parent SCMI v3.2 adds set/get parent clock commands, so update the SCMI clock driver to support them. Signed-off-by: Peng Fan Link: https://lore.kernel.org/r/20231004-scmi-clock-v3-v5-2-1b8a1435673e@nxp.com Signed-off-by: Sudeep Holla --- drivers/clk/clk-scmi.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c index 3f525bed9794..8cbe24789c24 100644 --- a/drivers/clk/clk-scmi.c +++ b/drivers/clk/clk-scmi.c @@ -24,6 +24,7 @@ struct scmi_clk { struct clk_hw hw; const struct scmi_clock_info *info; const struct scmi_protocol_handle *ph; + struct clk_parent_data *parent_data; }; #define to_scmi_clk(clk) container_of(clk, struct scmi_clk, hw) @@ -78,6 +79,43 @@ static int scmi_clk_set_rate(struct clk_hw *hw, unsigned long rate, return scmi_proto_clk_ops->rate_set(clk->ph, clk->id, rate); } +static int scmi_clk_set_parent(struct clk_hw *hw, u8 parent_index) +{ + struct scmi_clk *clk = to_scmi_clk(hw); + + return scmi_proto_clk_ops->parent_set(clk->ph, clk->id, parent_index); +} + +static u8 scmi_clk_get_parent(struct clk_hw *hw) +{ + struct scmi_clk *clk = to_scmi_clk(hw); + u32 parent_id, p_idx; + int ret; + + ret = scmi_proto_clk_ops->parent_get(clk->ph, clk->id, &parent_id); + if (ret) + return 0; + + for (p_idx = 0; p_idx < clk->info->num_parents; p_idx++) { + if (clk->parent_data[p_idx].index == parent_id) + break; + } + + if (p_idx == clk->info->num_parents) + return 0; + + return p_idx; +} + +static int scmi_clk_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) +{ + /* + * Suppose all the requested rates are supported, and let firmware + * to handle the left work. + */ + return 0; +} + static int scmi_clk_enable(struct clk_hw *hw) { struct scmi_clk *clk = to_scmi_clk(hw); @@ -139,6 +177,9 @@ static const struct clk_ops scmi_clk_ops = { .set_rate = scmi_clk_set_rate, .prepare = scmi_clk_enable, .unprepare = scmi_clk_disable, + .set_parent = scmi_clk_set_parent, + .get_parent = scmi_clk_get_parent, + .determine_rate = scmi_clk_determine_rate, }; static const struct clk_ops scmi_atomic_clk_ops = { @@ -148,6 +189,9 @@ static const struct clk_ops scmi_atomic_clk_ops = { .enable = scmi_clk_atomic_enable, .disable = scmi_clk_atomic_disable, .is_enabled = scmi_clk_atomic_is_enabled, + .set_parent = scmi_clk_set_parent, + .get_parent = scmi_clk_get_parent, + .determine_rate = scmi_clk_determine_rate, }; static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk, @@ -158,9 +202,10 @@ static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk, struct clk_init_data init = { .flags = CLK_GET_RATE_NOCACHE, - .num_parents = 0, + .num_parents = sclk->info->num_parents, .ops = scmi_ops, .name = sclk->info->name, + .parent_data = sclk->parent_data, }; sclk->hw.init = &init; @@ -251,9 +296,23 @@ static int scmi_clocks_probe(struct scmi_device *sdev) else scmi_ops = &scmi_clk_ops; + /* Initialize clock parent data. */ + if (sclk->info->num_parents > 0) { + sclk->parent_data = devm_kcalloc(dev, sclk->info->num_parents, + sizeof(*sclk->parent_data), GFP_KERNEL); + if (!sclk->parent_data) + return -ENOMEM; + + for (int i = 0; i < sclk->info->num_parents; i++) { + sclk->parent_data[i].index = sclk->info->parents[i]; + sclk->parent_data[i].hw = hws[sclk->info->parents[i]]; + } + } + err = scmi_clk_ops_init(dev, sclk, scmi_ops); if (err) { dev_err(dev, "failed to register clock %d\n", idx); + devm_kfree(dev, sclk->parent_data); devm_kfree(dev, sclk); hws[idx] = NULL; } else { -- cgit v1.2.3 From 76cf932c95b9e7c07b065b5c71e56957e2826ae2 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 5 Oct 2023 15:45:07 +0100 Subject: KVM: arm64: FFA: Remove access of endpoint memory access descriptor array FF-A v1.1 removes the fixed location of endpoint memory access descriptor array within the memory transaction descriptor structure. In preparation to remove the ep_mem_access member from the ffa_mem_region structure, provide the accessor to fetch the offset and use the same in FF-A proxy implementation. The accessor take the FF-A version as the argument from which the memory access descriptor format can be determined. v1.0 uses the old format while v1.1 onwards use the new format specified in the v1.1 specification. Cc: Oliver Upton Cc: Will Deacon Cc: Quentin Perret Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20231005-ffa_v1-1_notif-v4-14-cddd3237809c@arm.com Signed-off-by: Sudeep Holla --- arch/arm64/kvm/hyp/nvhe/ffa.c | 10 ++++++++-- include/linux/arm_ffa.h | 6 ++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/hyp/nvhe/ffa.c b/arch/arm64/kvm/hyp/nvhe/ffa.c index ab4f5d160c58..5c7b345c2cd5 100644 --- a/arch/arm64/kvm/hyp/nvhe/ffa.c +++ b/arch/arm64/kvm/hyp/nvhe/ffa.c @@ -423,6 +423,7 @@ static __always_inline void do_ffa_mem_xfer(const u64 func_id, DECLARE_REG(u32, fraglen, ctxt, 2); DECLARE_REG(u64, addr_mbz, ctxt, 3); DECLARE_REG(u32, npages_mbz, ctxt, 4); + struct ffa_mem_region_attributes *ep_mem_access; struct ffa_composite_mem_region *reg; struct ffa_mem_region *buf; u32 offset, nr_ranges; @@ -452,7 +453,9 @@ static __always_inline void do_ffa_mem_xfer(const u64 func_id, buf = hyp_buffers.tx; memcpy(buf, host_buffers.tx, fraglen); - offset = buf->ep_mem_access[0].composite_off; + ep_mem_access = (void *)buf + + ffa_mem_desc_offset(buf, 0, FFA_VERSION_1_0); + offset = ep_mem_access->composite_off; if (!offset || buf->ep_count != 1 || buf->sender_id != HOST_FFA_ID) { ret = FFA_RET_INVALID_PARAMETERS; goto out_unlock; @@ -504,6 +507,7 @@ static void do_ffa_mem_reclaim(struct arm_smccc_res *res, DECLARE_REG(u32, handle_lo, ctxt, 1); DECLARE_REG(u32, handle_hi, ctxt, 2); DECLARE_REG(u32, flags, ctxt, 3); + struct ffa_mem_region_attributes *ep_mem_access; struct ffa_composite_mem_region *reg; u32 offset, len, fraglen, fragoff; struct ffa_mem_region *buf; @@ -528,7 +532,9 @@ static void do_ffa_mem_reclaim(struct arm_smccc_res *res, len = res->a1; fraglen = res->a2; - offset = buf->ep_mem_access[0].composite_off; + ep_mem_access = (void *)buf + + ffa_mem_desc_offset(buf, 0, FFA_VERSION_1_0); + offset = ep_mem_access->composite_off; /* * We can trust the SPMD to get this right, but let's at least * check that we end up with something that doesn't look _completely_ diff --git a/include/linux/arm_ffa.h b/include/linux/arm_ffa.h index 748d0a83a4bc..2444d596b703 100644 --- a/include/linux/arm_ffa.h +++ b/include/linux/arm_ffa.h @@ -357,6 +357,12 @@ struct ffa_mem_region { #define CONSTITUENTS_OFFSET(x) \ (offsetof(struct ffa_composite_mem_region, constituents[x])) +static inline u32 +ffa_mem_desc_offset(struct ffa_mem_region *buf, int count, u32 ffa_version) +{ + return COMPOSITE_OFFSET(0); +} + struct ffa_mem_ops_args { bool use_txbuf; u32 nattrs; -- cgit v1.2.3 From e4607b84c6819bb59c48063ed41eee1fc7f736a0 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 5 Oct 2023 15:45:08 +0100 Subject: firmware: arm_ffa: Switch to using ffa_mem_desc_offset() accessor In preparation to add support to the new memory transaction descriptor, the ep_mem_access member needs to be removed and hence even the macro COMPOSITE_OFFSET(). Let us switch to using the new ffa_mem_desc_offset() accessor in ffa_setup_and_transmit(). This will enable adding the support for new format transparently without any changes here again. Link: https://lore.kernel.org/r/20231005-ffa_v1-1_notif-v4-15-cddd3237809c@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 49fcbeb63eaa..c79067201487 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -454,8 +454,10 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize, mem_region->flags = args->flags; mem_region->sender_id = drv_info->vm_id; mem_region->attributes = ffa_memory_attributes_get(func_id); - ep_mem_access = buffer + COMPOSITE_OFFSET(0); - composite_offset = COMPOSITE_OFFSET(args->nattrs); + ep_mem_access = buffer + + ffa_mem_desc_offset(buffer, 0, drv_info->version); + composite_offset = ffa_mem_desc_offset(buffer, args->nattrs, + drv_info->version); for (idx = 0; idx < args->nattrs; idx++, ep_mem_access++) { ep_mem_access->receiver = args->attrs[idx].receiver; -- cgit v1.2.3 From 113580530ee7dc61e668b641d657920734533b9f Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 5 Oct 2023 15:45:09 +0100 Subject: firmware: arm_ffa: Update memory descriptor to support v1.1 format Update memory transaction descriptor structure to accommodate couple of new entries in v1.1 which were previously marked reserved and MBZ(must be zero). It also removes the flexible array member ep_mem_access in the memory transaction descriptor structure as it need not be at fixed offset. Also update ffa_mem_desc_offset() accessor to handle both old and new formats of memory transaction descriptors. The updated ffa_mem_region structure aligns with new format in v1.1 and hence the driver/user must take care not to use members beyond and including ep_mem_offset when using the old format. Link: https://lore.kernel.org/r/20231005-ffa_v1-1_notif-v4-16-cddd3237809c@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 11 ++++++++--- include/linux/arm_ffa.h | 33 ++++++++++++++++++++++----------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index c79067201487..6c5c7926b8ee 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -423,7 +423,7 @@ static u32 ffa_get_num_pages_sg(struct scatterlist *sg) return num_pages; } -static u8 ffa_memory_attributes_get(u32 func_id) +static u16 ffa_memory_attributes_get(u32 func_id) { /* * For the memory lend or donate operation, if the receiver is a PE or @@ -467,9 +467,14 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize, ep_mem_access->reserved = 0; } mem_region->handle = 0; - mem_region->reserved_0 = 0; - mem_region->reserved_1 = 0; mem_region->ep_count = args->nattrs; + if (drv_info->version <= FFA_VERSION_1_0) { + mem_region->ep_mem_size = 0; + } else { + mem_region->ep_mem_size = sizeof(*ep_mem_access); + mem_region->ep_mem_offset = sizeof(*mem_region); + memset(mem_region->reserved, 0, 12); + } composite = buffer + composite_offset; composite->total_pg_cnt = ffa_get_num_pages_sg(args->sg); diff --git a/include/linux/arm_ffa.h b/include/linux/arm_ffa.h index 2444d596b703..1abedb5b2e48 100644 --- a/include/linux/arm_ffa.h +++ b/include/linux/arm_ffa.h @@ -6,6 +6,7 @@ #ifndef _LINUX_ARM_FFA_H #define _LINUX_ARM_FFA_H +#include #include #include #include @@ -298,8 +299,8 @@ struct ffa_mem_region { #define FFA_MEM_NON_SHAREABLE (0) #define FFA_MEM_OUTER_SHAREABLE (2) #define FFA_MEM_INNER_SHAREABLE (3) - u8 attributes; - u8 reserved_0; + /* Memory region attributes, upper byte MBZ pre v1.1 */ + u16 attributes; /* * Clear memory region contents after unmapping it from the sender and * before mapping it for any receiver. @@ -337,30 +338,40 @@ struct ffa_mem_region { * memory region. */ u64 tag; - u32 reserved_1; + /* Size of each endpoint memory access descriptor, MBZ pre v1.1 */ + u32 ep_mem_size; /* * The number of `ffa_mem_region_attributes` entries included in this * transaction. */ u32 ep_count; /* - * An array of endpoint memory access descriptors. - * Each one specifies a memory region offset, an endpoint and the - * attributes with which this memory region should be mapped in that - * endpoint's page table. + * 16-byte aligned offset from the base address of this descriptor + * to the first element of the endpoint memory access descriptor array + * Valid only from v1.1 */ - struct ffa_mem_region_attributes ep_mem_access[]; + u32 ep_mem_offset; + /* MBZ, valid only from v1.1 */ + u32 reserved[3]; }; -#define COMPOSITE_OFFSET(x) \ - (offsetof(struct ffa_mem_region, ep_mem_access[x])) #define CONSTITUENTS_OFFSET(x) \ (offsetof(struct ffa_composite_mem_region, constituents[x])) static inline u32 ffa_mem_desc_offset(struct ffa_mem_region *buf, int count, u32 ffa_version) { - return COMPOSITE_OFFSET(0); + u32 offset = count * sizeof(struct ffa_mem_region_attributes); + /* + * Earlier to v1.1, the endpoint memory descriptor array started at + * offset 32(i.e. offset of ep_mem_offset in the current structure) + */ + if (ffa_version <= FFA_VERSION_1_0) + offset += offsetof(struct ffa_mem_region, ep_mem_offset); + else + offset += sizeof(struct ffa_mem_region); + + return offset; } struct ffa_mem_ops_args { -- cgit v1.2.3 From bcefd1bf63b1ec9bb08067021cf47f0fad96f395 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 5 Oct 2023 15:45:10 +0100 Subject: firmware: arm_ffa: Upgrade the driver version to v1.1 With quite a few v1.1 features supported, we can bump the driver version to v1.1 now. Link: https://lore.kernel.org/r/20231005-ffa_v1-1_notif-v4-17-cddd3237809c@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 6c5c7926b8ee..07b72c679247 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -43,7 +43,7 @@ #include "common.h" -#define FFA_DRIVER_VERSION FFA_VERSION_1_0 +#define FFA_DRIVER_VERSION FFA_VERSION_1_1 #define FFA_MIN_VERSION FFA_VERSION_1_0 #define SENDER_ID_MASK GENMASK(31, 16) -- cgit v1.2.3 From 5d59b6a49bb8f6841acf53affc0c4720fe7450e5 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 6 Oct 2023 17:46:44 -0500 Subject: firmware: meson: Use device_get_match_data() Use preferred device_get_match_data() instead of of_match_device() to get the driver match data. With this, adjust the includes to explicitly include the correct headers. Signed-off-by: Rob Herring Reviewed-by: Neil Armstrong Link: https://lore.kernel.org/r/20231006224644.445295-1-robh@kernel.org Signed-off-by: Neil Armstrong --- drivers/firmware/meson/meson_sm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/meson/meson_sm.c b/drivers/firmware/meson/meson_sm.c index 53bf56e18743..ed60f1103053 100644 --- a/drivers/firmware/meson/meson_sm.c +++ b/drivers/firmware/meson/meson_sm.c @@ -13,9 +13,10 @@ #include #include #include -#include +#include #include #include +#include #include #include #include @@ -297,7 +298,7 @@ static int __init meson_sm_probe(struct platform_device *pdev) if (!fw) return -ENOMEM; - chip = of_match_device(meson_sm_ids, dev)->data; + chip = device_get_match_data(dev); if (!chip) return -EINVAL; -- cgit v1.2.3 From a21ecc5262835604d280a36c8b2224f8e1f0199f Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Mon, 25 Sep 2023 16:18:11 +0100 Subject: firmware: raspberrypi: Fix devm_rpi_firmware_get documentation The function documentation for devm_rpi_firmware_get() was missing a description for the dev parameter. Provide it and clear the warning produced here. Fixes: f663204c9a1f ("firmware: raspberrypi: Introduce devm_rpi_firmware_get()") Signed-off-by: Kieran Bingham Link: https://lore.kernel.org/r/20230925151812.142480-1-kieran.bingham+renesas@ideasonboard.com Signed-off-by: Florian Fainelli --- drivers/firmware/raspberrypi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/firmware/raspberrypi.c b/drivers/firmware/raspberrypi.c index f66efaa5196d..4cd290a60fba 100644 --- a/drivers/firmware/raspberrypi.c +++ b/drivers/firmware/raspberrypi.c @@ -378,6 +378,7 @@ EXPORT_SYMBOL_GPL(rpi_firmware_get); /** * devm_rpi_firmware_get - Get pointer to rpi_firmware structure. + * @dev: The firmware device structure * @firmware_node: Pointer to the firmware Device Tree node. * * Returns NULL is the firmware device is not ready. -- cgit v1.2.3 From 089d08d7c3a36c56da6f23c8022f8f47aeab06a5 Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Mon, 9 Oct 2023 14:58:17 +0100 Subject: soc: bcm: brcmstb: depend on ARCH_BRCMSTB over arm arches The SOC_BRCMSTB is better depending on ARCH_BRCMSTB as it's defined for both arm and aarch64 but is more specific to the actual SoC's dependencies, it'll still get compile tested. Signed-off-by: Peter Robinson Link: https://lore.kernel.org/r/20231009135833.17880-2-pbrobinson@gmail.com Signed-off-by: Florian Fainelli --- drivers/soc/bcm/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/bcm/Kconfig b/drivers/soc/bcm/Kconfig index f96906795fa6..ffca9e1d8677 100644 --- a/drivers/soc/bcm/Kconfig +++ b/drivers/soc/bcm/Kconfig @@ -33,7 +33,7 @@ config SOC_BCM63XX config SOC_BRCMSTB bool "Broadcom STB SoC drivers" - depends on ARM || ARM64 || BMIPS_GENERIC || COMPILE_TEST + depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST select SOC_BUS help Enables drivers for the Broadcom Set-Top Box (STB) series of chips. -- cgit v1.2.3 From 112cd2f9614d55f5806bf9bf55e3a9052511c894 Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Mon, 9 Oct 2023 14:58:19 +0100 Subject: bus: brcmstb_gisb: Depend on SoC specifics over generic arm Depend on the explicit SoC defines rather than generic architectures like most of the rest of the HW drivers do. Signed-off-by: Peter Robinson Link: https://lore.kernel.org/r/20231009135833.17880-4-pbrobinson@gmail.com Signed-off-by: Florian Fainelli --- drivers/bus/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index c98dd6ca2629..e6742998f372 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -31,7 +31,7 @@ config ARM_INTEGRATOR_LM config BRCMSTB_GISB_ARB tristate "Broadcom STB GISB bus arbiter" - depends on ARM || ARM64 || MIPS + depends on ARCH_BRCMSTB || BMIPS_GENERIC default ARCH_BRCMSTB || BMIPS_GENERIC help Driver for the Broadcom Set Top Box System-on-a-chip internal bus -- cgit v1.2.3 From 1f17395124a53a8edbb6c51e37b98b49a3523b9b Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 9 Oct 2023 16:20:49 +0100 Subject: firmware: arm_scmi: Convert u32 to unsigned long to align with arm_smccc_1_1_invoke() All the parameters to arm_smccc_1_1_invoke() are unsigned long which aligns well on both 32-bit and 64-bit Arm based platforms. Let us store all the members in the structure scmi_smc used as the parameters to the arm_smccc_1_1_invoke() call as unsigned long. Cc: Cristian Marussi Reviewed-by: Cristian Marussi Link: https://lore.kernel.org/r/20231009152049.1428872-1-sudeep.holla@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/smc.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/firmware/arm_scmi/smc.c b/drivers/firmware/arm_scmi/smc.c index c193516a254d..8eba60a41975 100644 --- a/drivers/firmware/arm_scmi/smc.c +++ b/drivers/firmware/arm_scmi/smc.c @@ -60,9 +60,9 @@ struct scmi_smc { struct mutex shmem_lock; #define INFLIGHT_NONE MSG_TOKEN_MAX atomic_t inflight; - u32 func_id; - u32 param_page; - u32 param_offset; + unsigned long func_id; + unsigned long param_page; + unsigned long param_offset; }; static irqreturn_t smc_msg_done_isr(int irq, void *data) @@ -211,8 +211,6 @@ static int smc_send_message(struct scmi_chan_info *cinfo, { struct scmi_smc *scmi_info = cinfo->transport_info; struct arm_smccc_res res; - unsigned long page = scmi_info->param_page; - unsigned long offset = scmi_info->param_offset; /* * Channel will be released only once response has been @@ -222,8 +220,8 @@ static int smc_send_message(struct scmi_chan_info *cinfo, shmem_tx_prepare(scmi_info->shmem, xfer, cinfo); - arm_smccc_1_1_invoke(scmi_info->func_id, page, offset, 0, 0, 0, 0, 0, - &res); + arm_smccc_1_1_invoke(scmi_info->func_id, scmi_info->param_page, + scmi_info->param_offset, 0, 0, 0, 0, 0, &res); /* Only SMCCC_RET_NOT_SUPPORTED is valid error code */ if (res.a0) { -- cgit v1.2.3 From 6979f88f5a8e6ab1cdbfeb842abb7ce9daecbb8d Mon Sep 17 00:00:00 2001 From: Nikunj Kela Date: Mon, 9 Oct 2023 12:14:36 -0700 Subject: dt-bindings: arm: Add new compatible for smc/hvc transport for SCMI Introduce compatible "qcom,scmi-smc" for SCMI smc/hvc transport channel for Qualcomm virtual platforms. This compatible mandates populating an additional parameter 'capability-id' from the last 8 bytes of the shmem channel. Signed-off-by: Nikunj Kela Reviewed-by: Brian Masney Link: https://lore.kernel.org/r/20231009191437.27926-2-quic_nkela@quicinc.com Signed-off-by: Sudeep Holla --- Documentation/devicetree/bindings/firmware/arm,scmi.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml index 563a87dfb31a..4591523b51a0 100644 --- a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml +++ b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml @@ -38,6 +38,9 @@ properties: with shmem address(4KB-page, offset) as parameters items: - const: arm,scmi-smc-param + - description: SCMI compliant firmware with Qualcomm SMC/HVC transport + items: + - const: qcom,scmi-smc - description: SCMI compliant firmware with SCMI Virtio transport. The virtio transport only supports a single device. items: @@ -313,6 +316,7 @@ else: enum: - arm,scmi-smc - arm,scmi-smc-param + - qcom,scmi-smc then: required: - arm,smc-id -- cgit v1.2.3 From da405477e7670782241234ea6b4309f3224ecb63 Mon Sep 17 00:00:00 2001 From: Nikunj Kela Date: Mon, 9 Oct 2023 12:14:37 -0700 Subject: firmware: arm_scmi: Add qcom smc/hvc transport support This change adds the support for SCMI message exchange on Qualcomm virtual platforms. The hypervisor associates an object-id also known as capability-id with each smc/hvc doorbell object. The capability-id is used to identify the doorbell from the VM's capability namespace, similar to a file-descriptor. The hypervisor, in addition to the function-id, expects the capability-id to be passed in x1 register when SMC/HVC call is invoked. The capability-id is allocated by the hypervisor on bootup and is stored in the shmem region by the firmware before starting Linux. Signed-off-by: Nikunj Kela Reviewed-by: Brian Masney Link: https://lore.kernel.org/r/20231009191437.27926-3-quic_nkela@quicinc.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 1 + drivers/firmware/arm_scmi/smc.c | 27 +++++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 87383c05424b..09371f40d61f 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -2915,6 +2915,7 @@ static const struct of_device_id scmi_of_match[] = { #ifdef CONFIG_ARM_SCMI_TRANSPORT_SMC { .compatible = "arm,scmi-smc", .data = &scmi_smc_desc}, { .compatible = "arm,scmi-smc-param", .data = &scmi_smc_desc}, + { .compatible = "qcom,scmi-smc", .data = &scmi_smc_desc}, #endif #ifdef CONFIG_ARM_SCMI_TRANSPORT_VIRTIO { .compatible = "arm,scmi-virtio", .data = &scmi_virtio_desc}, diff --git a/drivers/firmware/arm_scmi/smc.c b/drivers/firmware/arm_scmi/smc.c index 8eba60a41975..7611e9665038 100644 --- a/drivers/firmware/arm_scmi/smc.c +++ b/drivers/firmware/arm_scmi/smc.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -50,6 +51,8 @@ * @func_id: smc/hvc call function id * @param_page: 4K page number of the shmem channel * @param_offset: Offset within the 4K page of the shmem channel + * @cap_id: smc/hvc doorbell's capability id to be used on Qualcomm virtual + * platforms */ struct scmi_smc { @@ -63,6 +66,7 @@ struct scmi_smc { unsigned long func_id; unsigned long param_page; unsigned long param_offset; + unsigned long cap_id; }; static irqreturn_t smc_msg_done_isr(int irq, void *data) @@ -124,6 +128,7 @@ static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, bool tx) { struct device *cdev = cinfo->dev; + unsigned long cap_id = ULONG_MAX; struct scmi_smc *scmi_info; resource_size_t size; struct resource res; @@ -162,6 +167,18 @@ static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, if (ret < 0) return ret; + if (of_device_is_compatible(dev->of_node, "qcom,scmi-smc")) { + void __iomem *ptr = (void __iomem *)scmi_info->shmem + size - 8; + /* The capability-id is kept in last 8 bytes of shmem. + * +-------+ <-- 0 + * | shmem | + * +-------+ <-- size - 8 + * | capId | + * +-------+ <-- size + */ + memcpy_fromio(&cap_id, ptr, sizeof(cap_id)); + } + if (of_device_is_compatible(dev->of_node, "arm,scmi-smc-param")) { scmi_info->param_page = SHMEM_PAGE(res.start); scmi_info->param_offset = SHMEM_OFFSET(res.start); @@ -184,6 +201,7 @@ static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, } scmi_info->func_id = func_id; + scmi_info->cap_id = cap_id; scmi_info->cinfo = cinfo; smc_channel_lock_init(scmi_info); cinfo->transport_info = scmi_info; @@ -220,8 +238,13 @@ static int smc_send_message(struct scmi_chan_info *cinfo, shmem_tx_prepare(scmi_info->shmem, xfer, cinfo); - arm_smccc_1_1_invoke(scmi_info->func_id, scmi_info->param_page, - scmi_info->param_offset, 0, 0, 0, 0, 0, &res); + if (scmi_info->cap_id != ULONG_MAX) + arm_smccc_1_1_invoke(scmi_info->func_id, scmi_info->cap_id, 0, + 0, 0, 0, 0, 0, &res); + else + arm_smccc_1_1_invoke(scmi_info->func_id, scmi_info->param_page, + scmi_info->param_offset, 0, 0, 0, 0, 0, + &res); /* Only SMCCC_RET_NOT_SUPPORTED is valid error code */ if (res.a0) { -- cgit v1.2.3 From bde3ce7257ca68120f0d8db96b3df5bc14f8245d Mon Sep 17 00:00:00 2001 From: Deming Wang Date: Tue, 12 Sep 2023 08:10:30 -0400 Subject: firmware: tegra: Fix a typo successfully, not 'succesfully' Signed-off-by: Deming Wang Signed-off-by: Thierry Reding --- include/soc/tegra/bpmp-abi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/soc/tegra/bpmp-abi.h b/include/soc/tegra/bpmp-abi.h index ecefcaec7e66..6b995a8f0f6d 100644 --- a/include/soc/tegra/bpmp-abi.h +++ b/include/soc/tegra/bpmp-abi.h @@ -1194,7 +1194,7 @@ struct cmd_clk_is_enabled_request { */ struct cmd_clk_is_enabled_response { /** - * @brief The state of the clock that has been succesfully + * @brief The state of the clock that has been successfully * requested with CMD_CLK_ENABLE or CMD_CLK_DISABLE by the * master invoking the command earlier. * -- cgit v1.2.3 From d3f785d74b3d066b17da57d25290b41471b4ebeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:23 +0200 Subject: soc/tegra: cbb: tegra194-cbb: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/soc/tegra/cbb/tegra194-cbb.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/tegra/cbb/tegra194-cbb.c b/drivers/soc/tegra/cbb/tegra194-cbb.c index cf6886f362d3..9cbc562ae7d3 100644 --- a/drivers/soc/tegra/cbb/tegra194-cbb.c +++ b/drivers/soc/tegra/cbb/tegra194-cbb.c @@ -2293,7 +2293,7 @@ static int tegra194_cbb_probe(struct platform_device *pdev) return tegra_cbb_register(&cbb->base); } -static int tegra194_cbb_remove(struct platform_device *pdev) +static void tegra194_cbb_remove(struct platform_device *pdev) { struct tegra194_cbb *cbb = platform_get_drvdata(pdev); struct tegra_cbb *noc, *tmp; @@ -2311,8 +2311,6 @@ static int tegra194_cbb_remove(struct platform_device *pdev) } spin_unlock_irqrestore(&cbb_lock, flags); - - return 0; } static int __maybe_unused tegra194_cbb_resume_noirq(struct device *dev) @@ -2332,7 +2330,7 @@ static const struct dev_pm_ops tegra194_cbb_pm = { static struct platform_driver tegra194_cbb_driver = { .probe = tegra194_cbb_probe, - .remove = tegra194_cbb_remove, + .remove_new = tegra194_cbb_remove, .driver = { .name = "tegra194-cbb", .of_match_table = of_match_ptr(tegra194_cbb_match), -- cgit v1.2.3 From 9def28f3b8634e4f1fa92a77ccb65fbd2d03af34 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Thu, 12 Oct 2023 11:49:09 +0100 Subject: memory: tegra: Add Tegra234 clients for RCE and VI Add the Tegra234 memory client entries for the Real-time Camera Engine (RCE) and Video Input (VI) devices. Signed-off-by: Jon Hunter Acked-by: Thierry Reding Link: https://lore.kernel.org/r/20231012104909.48518-1-jonathanh@nvidia.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/tegra/tegra234.c | 60 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/drivers/memory/tegra/tegra234.c b/drivers/memory/tegra/tegra234.c index 9e5b5dbd9c8d..42c79f9a70af 100644 --- a/drivers/memory/tegra/tegra234.c +++ b/drivers/memory/tegra/tegra234.c @@ -449,6 +449,18 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { .security = 0x38c, }, }, + }, { + .id = TEGRA234_MEMORY_CLIENT_VIW, + .name = "viw", + .bpmp_id = TEGRA_ICC_BPMP_VI, + .type = TEGRA_ICC_ISO_VI, + .sid = TEGRA234_SID_ISO_VI, + .regs = { + .sid = { + .override = 0x390, + .security = 0x394, + }, + }, }, { .id = TEGRA234_MEMORY_CLIENT_NVDECSRD, .name = "nvdecsrd", @@ -621,6 +633,30 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { .security = 0x50c, }, }, + }, { + .id = TEGRA234_MEMORY_CLIENT_VIFALR, + .name = "vifalr", + .bpmp_id = TEGRA_ICC_BPMP_VIFAL, + .type = TEGRA_ICC_ISO_VIFAL, + .sid = TEGRA234_SID_ISO_VIFALC, + .regs = { + .sid = { + .override = 0x5e0, + .security = 0x5e4, + }, + }, + }, { + .id = TEGRA234_MEMORY_CLIENT_VIFALW, + .name = "vifalw", + .bpmp_id = TEGRA_ICC_BPMP_VIFAL, + .type = TEGRA_ICC_ISO_VIFAL, + .sid = TEGRA234_SID_ISO_VIFALC, + .regs = { + .sid = { + .override = 0x5e8, + .security = 0x5ec, + }, + }, }, { .id = TEGRA234_MEMORY_CLIENT_DLA0RDA, .name = "dla0rda", @@ -701,6 +737,30 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { .security = 0x62c, }, }, + }, { + .id = TEGRA234_MEMORY_CLIENT_RCER, + .name = "rcer", + .bpmp_id = TEGRA_ICC_BPMP_RCE, + .type = TEGRA_ICC_NISO, + .sid = TEGRA234_SID_RCE, + .regs = { + .sid = { + .override = 0x690, + .security = 0x694, + }, + }, + }, { + .id = TEGRA234_MEMORY_CLIENT_RCEW, + .name = "rcew", + .bpmp_id = TEGRA_ICC_BPMP_RCE, + .type = TEGRA_ICC_NISO, + .sid = TEGRA234_SID_RCE, + .regs = { + .sid = { + .override = 0x698, + .security = 0x69c, + }, + }, }, { .id = TEGRA234_MEMORY_CLIENT_PCIE0R, .name = "pcie0r", -- cgit v1.2.3 From 09de3691daab15ae125cbf32f9f72fc90eada49d Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 6 Oct 2023 17:44:01 -0500 Subject: memory: Use device_get_match_data() Use preferred device_get_match_data() instead of of_match_device() to get the driver match data. With this, adjust the includes to explicitly include the correct headers. Signed-off-by: Rob Herring Reviewed-by: Florian Fainelli Link: https://lore.kernel.org/r/20231006224402.442078-1-robh@kernel.org Signed-off-by: Krzysztof Kozlowski --- drivers/memory/atmel-ebi.c | 14 +++++++------- drivers/memory/brcmstb_memc.c | 9 +++------ drivers/memory/fsl-corenet-cf.c | 11 ++--------- 3 files changed, 12 insertions(+), 22 deletions(-) diff --git a/drivers/memory/atmel-ebi.c b/drivers/memory/atmel-ebi.c index ec6100bc94c5..e8bb5f37f5cb 100644 --- a/drivers/memory/atmel-ebi.c +++ b/drivers/memory/atmel-ebi.c @@ -12,7 +12,10 @@ #include #include #include -#include +#include +#include +#include +#include #include #include @@ -515,16 +518,11 @@ static int atmel_ebi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *child, *np = dev->of_node, *smc_np; - const struct of_device_id *match; struct atmel_ebi *ebi; int ret, reg_cells; struct clk *clk; u32 val; - match = of_match_device(atmel_ebi_id_table, dev); - if (!match || !match->data) - return -EINVAL; - ebi = devm_kzalloc(dev, sizeof(*ebi), GFP_KERNEL); if (!ebi) return -ENOMEM; @@ -532,7 +530,9 @@ static int atmel_ebi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ebi); INIT_LIST_HEAD(&ebi->devs); - ebi->caps = match->data; + ebi->caps = device_get_match_data(dev); + if (!ebi->caps) + return -EINVAL; ebi->dev = dev; clk = devm_clk_get(dev, NULL); diff --git a/drivers/memory/brcmstb_memc.c b/drivers/memory/brcmstb_memc.c index 233a53f5bce1..a6ea51996522 100644 --- a/drivers/memory/brcmstb_memc.c +++ b/drivers/memory/brcmstb_memc.c @@ -8,8 +8,9 @@ #include #include #include -#include +#include #include +#include #define REG_MEMC_CNTRLR_CONFIG 0x00 #define CNTRLR_CONFIG_LPDDR4_SHIFT 5 @@ -121,12 +122,9 @@ static struct attribute_group dev_attr_group = { .attrs = dev_attrs, }; -static const struct of_device_id brcmstb_memc_of_match[]; - static int brcmstb_memc_probe(struct platform_device *pdev) { const struct brcmstb_memc_data *memc_data; - const struct of_device_id *of_id; struct device *dev = &pdev->dev; struct brcmstb_memc *memc; int ret; @@ -137,8 +135,7 @@ static int brcmstb_memc_probe(struct platform_device *pdev) dev_set_drvdata(dev, memc); - of_id = of_match_device(brcmstb_memc_of_match, dev); - memc_data = of_id->data; + memc_data = device_get_match_data(dev); memc->srpd_offset = memc_data->srpd_offset; memc->ddr_ctrl = devm_platform_ioremap_resource(pdev, 0); diff --git a/drivers/memory/fsl-corenet-cf.c b/drivers/memory/fsl-corenet-cf.c index 7fc9f57ae278..8096c4f33303 100644 --- a/drivers/memory/fsl-corenet-cf.c +++ b/drivers/memory/fsl-corenet-cf.c @@ -10,10 +10,8 @@ #include #include #include -#include -#include -#include #include +#include enum ccf_version { CCF1, @@ -172,14 +170,9 @@ out: static int ccf_probe(struct platform_device *pdev) { struct ccf_private *ccf; - const struct of_device_id *match; u32 errinten; int ret, irq; - match = of_match_device(ccf_matches, &pdev->dev); - if (WARN_ON(!match)) - return -ENODEV; - ccf = devm_kzalloc(&pdev->dev, sizeof(*ccf), GFP_KERNEL); if (!ccf) return -ENOMEM; @@ -189,7 +182,7 @@ static int ccf_probe(struct platform_device *pdev) return PTR_ERR(ccf->regs); ccf->dev = &pdev->dev; - ccf->info = match->data; + ccf->info = device_get_match_data(&pdev->dev); ccf->err_regs = ccf->regs + ccf->info->err_reg_offs; if (ccf->info->has_brr) { -- cgit v1.2.3 From 0af9e89106c359e531791c3dc2422a16bd40d17f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:54:52 +0200 Subject: soc/aspeed: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Andrew Jeffery Link: https://lore.kernel.org/r/20230925095532.1984344-2-u.kleine-koenig@pengutronix.de Link: https://lore.kernel.org/r/20230925095532.1984344-3-u.kleine-koenig@pengutronix.de Link: https://lore.kernel.org/r/20230925095532.1984344-4-u.kleine-koenig@pengutronix.de Link: https://lore.kernel.org/r/20230925095532.1984344-5-u.kleine-koenig@pengutronix.de Signed-off-by: Joel Stanley --- drivers/soc/aspeed/aspeed-lpc-ctrl.c | 6 ++---- drivers/soc/aspeed/aspeed-lpc-snoop.c | 6 ++---- drivers/soc/aspeed/aspeed-p2a-ctrl.c | 6 ++---- drivers/soc/aspeed/aspeed-uart-routing.c | 6 ++---- 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/drivers/soc/aspeed/aspeed-lpc-ctrl.c b/drivers/soc/aspeed/aspeed-lpc-ctrl.c index 258894ed234b..e87038009d1b 100644 --- a/drivers/soc/aspeed/aspeed-lpc-ctrl.c +++ b/drivers/soc/aspeed/aspeed-lpc-ctrl.c @@ -332,14 +332,12 @@ err: return rc; } -static int aspeed_lpc_ctrl_remove(struct platform_device *pdev) +static void aspeed_lpc_ctrl_remove(struct platform_device *pdev) { struct aspeed_lpc_ctrl *lpc_ctrl = dev_get_drvdata(&pdev->dev); misc_deregister(&lpc_ctrl->miscdev); clk_disable_unprepare(lpc_ctrl->clk); - - return 0; } static const struct of_device_id aspeed_lpc_ctrl_match[] = { @@ -355,7 +353,7 @@ static struct platform_driver aspeed_lpc_ctrl_driver = { .of_match_table = aspeed_lpc_ctrl_match, }, .probe = aspeed_lpc_ctrl_probe, - .remove = aspeed_lpc_ctrl_remove, + .remove_new = aspeed_lpc_ctrl_remove, }; module_platform_driver(aspeed_lpc_ctrl_driver); diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c index 773dbcbc03a6..888b5840c015 100644 --- a/drivers/soc/aspeed/aspeed-lpc-snoop.c +++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c @@ -331,7 +331,7 @@ err: return rc; } -static int aspeed_lpc_snoop_remove(struct platform_device *pdev) +static void aspeed_lpc_snoop_remove(struct platform_device *pdev) { struct aspeed_lpc_snoop *lpc_snoop = dev_get_drvdata(&pdev->dev); @@ -340,8 +340,6 @@ static int aspeed_lpc_snoop_remove(struct platform_device *pdev) aspeed_lpc_disable_snoop(lpc_snoop, 1); clk_disable_unprepare(lpc_snoop->clk); - - return 0; } static const struct aspeed_lpc_snoop_model_data ast2400_model_data = { @@ -368,7 +366,7 @@ static struct platform_driver aspeed_lpc_snoop_driver = { .of_match_table = aspeed_lpc_snoop_match, }, .probe = aspeed_lpc_snoop_probe, - .remove = aspeed_lpc_snoop_remove, + .remove_new = aspeed_lpc_snoop_remove, }; module_platform_driver(aspeed_lpc_snoop_driver); diff --git a/drivers/soc/aspeed/aspeed-p2a-ctrl.c b/drivers/soc/aspeed/aspeed-p2a-ctrl.c index 548f44da28a9..8610ddacc7bc 100644 --- a/drivers/soc/aspeed/aspeed-p2a-ctrl.c +++ b/drivers/soc/aspeed/aspeed-p2a-ctrl.c @@ -383,13 +383,11 @@ static int aspeed_p2a_ctrl_probe(struct platform_device *pdev) return rc; } -static int aspeed_p2a_ctrl_remove(struct platform_device *pdev) +static void aspeed_p2a_ctrl_remove(struct platform_device *pdev) { struct aspeed_p2a_ctrl *p2a_ctrl = dev_get_drvdata(&pdev->dev); misc_deregister(&p2a_ctrl->miscdev); - - return 0; } #define SCU2C_DRAM BIT(25) @@ -433,7 +431,7 @@ static struct platform_driver aspeed_p2a_ctrl_driver = { .of_match_table = aspeed_p2a_ctrl_match, }, .probe = aspeed_p2a_ctrl_probe, - .remove = aspeed_p2a_ctrl_remove, + .remove_new = aspeed_p2a_ctrl_remove, }; module_platform_driver(aspeed_p2a_ctrl_driver); diff --git a/drivers/soc/aspeed/aspeed-uart-routing.c b/drivers/soc/aspeed/aspeed-uart-routing.c index 3a4c1f28cb34..a2195f062e01 100644 --- a/drivers/soc/aspeed/aspeed-uart-routing.c +++ b/drivers/soc/aspeed/aspeed-uart-routing.c @@ -565,14 +565,12 @@ static int aspeed_uart_routing_probe(struct platform_device *pdev) return 0; } -static int aspeed_uart_routing_remove(struct platform_device *pdev) +static void aspeed_uart_routing_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct aspeed_uart_routing *uart_routing = platform_get_drvdata(pdev); sysfs_remove_group(&dev->kobj, uart_routing->attr_grp); - - return 0; } static const struct of_device_id aspeed_uart_routing_table[] = { @@ -591,7 +589,7 @@ static struct platform_driver aspeed_uart_routing_driver = { .of_match_table = aspeed_uart_routing_table, }, .probe = aspeed_uart_routing_probe, - .remove = aspeed_uart_routing_remove, + .remove_new = aspeed_uart_routing_remove, }; module_platform_driver(aspeed_uart_routing_driver); -- cgit v1.2.3 From cda263907a6f88c75fb97cf7adecffaafb6237ec Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 12 Oct 2023 17:35:36 +0200 Subject: soc/tegra: pmc: Drop the ->opp_to_performance_state() callback Since commit 7c41cdcd3bbe ("OPP: Simplify the over-designed pstate <-> level dance"), there is no longer any need for genpd providers to assign the ->opp_to_performance_state(), hence let's drop it. Cc: Thierry Reding Cc: Jonathan Hunter Cc: linux-tegra@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 162f52456f65..f432aa022ace 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -1393,13 +1393,6 @@ tegra_pmc_core_pd_set_performance_state(struct generic_pm_domain *genpd, return 0; } -static unsigned int -tegra_pmc_core_pd_opp_to_performance_state(struct generic_pm_domain *genpd, - struct dev_pm_opp *opp) -{ - return dev_pm_opp_get_level(opp); -} - static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np) { struct generic_pm_domain *genpd; @@ -1412,7 +1405,6 @@ static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np) genpd->name = "core"; genpd->set_performance_state = tegra_pmc_core_pd_set_performance_state; - genpd->opp_to_performance_state = tegra_pmc_core_pd_opp_to_performance_state; err = devm_pm_opp_set_regulators(pmc->dev, rname); if (err) -- cgit v1.2.3 From ea608a01d4ee66f8b51070e623f9adb8684c0dd4 Mon Sep 17 00:00:00 2001 From: Sumit Gupta Date: Mon, 9 Oct 2023 15:35:56 +0530 Subject: firmware: tegra: Add suspend hook and reset BPMP IPC early on resume Add suspend hook and a 'suspended' field in the 'struct tegra_bpmp' to mark if BPMP is suspended. Also, add a 'flags' field in the 'struct tegra_bpmp_message' whose 'TEGRA_BPMP_MESSAGE_RESET' bit can be set from the Tegra MC driver to signal that the reset of BPMP IPC channels is required before sending MRQ to the BPMP FW. Together both the fields allow us to handle any requests that might be sent too soon as they can cause hang during system resume. One case where we see BPMP requests being sent before the BPMP driver has resumed is the memory bandwidth requests which are triggered by onlining the CPUs during system resume. The CPUs are onlined before the BPMP has resumed and we need to reset the BPMP IPC channels to handle these requests. The additional check for 'flags' is done to avoid any un-intended BPMP IPC reset if the tegra_bpmp_transfer*() API gets called during suspend sequence after the BPMP driver is suspended. Fixes: f41e1442ac5b ("cpufreq: tegra194: add OPP support and set bandwidth") Co-developed-by: Thierry Reding Signed-off-by: Sumit Gupta Acked-by: Thierry Reding Signed-off-by: Thierry Reding --- drivers/firmware/tegra/bpmp.c | 30 ++++++++++++++++++++++++++++++ include/soc/tegra/bpmp.h | 6 ++++++ 2 files changed, 36 insertions(+) diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c index 51d062e0c3f1..c1590d3aa9cb 100644 --- a/drivers/firmware/tegra/bpmp.c +++ b/drivers/firmware/tegra/bpmp.c @@ -313,6 +313,8 @@ static ssize_t tegra_bpmp_channel_write(struct tegra_bpmp_channel *channel, return __tegra_bpmp_channel_write(channel, mrq, flags, data, size); } +static int __maybe_unused tegra_bpmp_resume(struct device *dev); + int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp, struct tegra_bpmp_message *msg) { @@ -325,6 +327,14 @@ int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp, if (!tegra_bpmp_message_valid(msg)) return -EINVAL; + if (bpmp->suspended) { + /* Reset BPMP IPC channels during resume based on flags passed */ + if (msg->flags & TEGRA_BPMP_MESSAGE_RESET) + tegra_bpmp_resume(bpmp->dev); + else + return -EAGAIN; + } + channel = bpmp->tx_channel; spin_lock(&bpmp->atomic_tx_lock); @@ -364,6 +374,14 @@ int tegra_bpmp_transfer(struct tegra_bpmp *bpmp, if (!tegra_bpmp_message_valid(msg)) return -EINVAL; + if (bpmp->suspended) { + /* Reset BPMP IPC channels during resume based on flags passed */ + if (msg->flags & TEGRA_BPMP_MESSAGE_RESET) + tegra_bpmp_resume(bpmp->dev); + else + return -EAGAIN; + } + channel = tegra_bpmp_write_threaded(bpmp, msg->mrq, msg->tx.data, msg->tx.size); if (IS_ERR(channel)) @@ -796,10 +814,21 @@ deinit: return err; } +static int __maybe_unused tegra_bpmp_suspend(struct device *dev) +{ + struct tegra_bpmp *bpmp = dev_get_drvdata(dev); + + bpmp->suspended = true; + + return 0; +} + static int __maybe_unused tegra_bpmp_resume(struct device *dev) { struct tegra_bpmp *bpmp = dev_get_drvdata(dev); + bpmp->suspended = false; + if (bpmp->soc->ops->resume) return bpmp->soc->ops->resume(bpmp); else @@ -807,6 +836,7 @@ static int __maybe_unused tegra_bpmp_resume(struct device *dev) } static const struct dev_pm_ops tegra_bpmp_pm_ops = { + .suspend_noirq = tegra_bpmp_suspend, .resume_noirq = tegra_bpmp_resume, }; diff --git a/include/soc/tegra/bpmp.h b/include/soc/tegra/bpmp.h index 5842e38bb288..f5e4ac5b8cce 100644 --- a/include/soc/tegra/bpmp.h +++ b/include/soc/tegra/bpmp.h @@ -102,8 +102,12 @@ struct tegra_bpmp { #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_mirror; #endif + + bool suspended; }; +#define TEGRA_BPMP_MESSAGE_RESET BIT(0) + struct tegra_bpmp_message { unsigned int mrq; @@ -117,6 +121,8 @@ struct tegra_bpmp_message { size_t size; int ret; } rx; + + unsigned long flags; }; #if IS_ENABLED(CONFIG_TEGRA_BPMP) -- cgit v1.2.3 From f344675a34383ae26a8230f4b1cd99cbd0defebd Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 9 Oct 2023 15:35:57 +0530 Subject: memory: tegra: Set BPMP msg flags to reset IPC channels Set the 'TEGRA_BPMP_MESSAGE_RESET' bit in newly added 'flags' field of 'struct tegra_bpmp_message' to request for the reset of BPMP IPC channels. This is used along with the 'suspended' check in BPMP driver for handling early bandwidth requests due to the hotplug of CPU's during system resume before the driver gets resumed. Fixes: f41e1442ac5b ("cpufreq: tegra194: add OPP support and set bandwidth") Co-developed-by: Sumit Gupta Signed-off-by: Sumit Gupta Acked-by: Krzysztof Kozlowski Signed-off-by: Thierry Reding --- drivers/memory/tegra/tegra234.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/memory/tegra/tegra234.c b/drivers/memory/tegra/tegra234.c index 9e5b5dbd9c8d..2845041f32d6 100644 --- a/drivers/memory/tegra/tegra234.c +++ b/drivers/memory/tegra/tegra234.c @@ -986,6 +986,10 @@ static int tegra234_mc_icc_set(struct icc_node *src, struct icc_node *dst) msg.rx.data = &bwmgr_resp; msg.rx.size = sizeof(bwmgr_resp); + if (pclient->bpmp_id >= TEGRA_ICC_BPMP_CPU_CLUSTER0 && + pclient->bpmp_id <= TEGRA_ICC_BPMP_CPU_CLUSTER2) + msg.flags = TEGRA_BPMP_MESSAGE_RESET; + ret = tegra_bpmp_transfer(mc->bpmp, &msg); if (ret < 0) { dev_err(mc->dev, "BPMP transfer failed: %d\n", ret); -- cgit v1.2.3 From e5f98e9f6a42b1055c83dd1e0f92320b28f3ebad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:54:56 +0200 Subject: soc/fsl: dpaa2-console: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Link: https://lore.kernel.org/r/20230925095532.1984344-6-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König --- drivers/soc/fsl/dpaa2-console.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/fsl/dpaa2-console.c b/drivers/soc/fsl/dpaa2-console.c index 1dca693b6b38..6dbc77db7718 100644 --- a/drivers/soc/fsl/dpaa2-console.c +++ b/drivers/soc/fsl/dpaa2-console.c @@ -300,12 +300,10 @@ err_register_mc: return error; } -static int dpaa2_console_remove(struct platform_device *pdev) +static void dpaa2_console_remove(struct platform_device *pdev) { misc_deregister(&dpaa2_mc_console_dev); misc_deregister(&dpaa2_aiop_console_dev); - - return 0; } static const struct of_device_id dpaa2_console_match_table[] = { @@ -322,7 +320,7 @@ static struct platform_driver dpaa2_console_driver = { .of_match_table = dpaa2_console_match_table, }, .probe = dpaa2_console_probe, - .remove = dpaa2_console_remove, + .remove_new = dpaa2_console_remove, }; module_platform_driver(dpaa2_console_driver); -- cgit v1.2.3 From 14914a115e807aa2f8025e451133627a64120ac3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:54:57 +0200 Subject: soc/fsl: cpm: qmc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Acked-by: Herve Codina Link: https://lore.kernel.org/r/20230925095532.1984344-7-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König --- drivers/soc/fsl/qe/qmc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/fsl/qe/qmc.c b/drivers/soc/fsl/qe/qmc.c index b3c292c9a14e..92ec76c03965 100644 --- a/drivers/soc/fsl/qe/qmc.c +++ b/drivers/soc/fsl/qe/qmc.c @@ -1415,7 +1415,7 @@ err_tsa_serial_disconnect: return ret; } -static int qmc_remove(struct platform_device *pdev) +static void qmc_remove(struct platform_device *pdev) { struct qmc *qmc = platform_get_drvdata(pdev); @@ -1427,8 +1427,6 @@ static int qmc_remove(struct platform_device *pdev) /* Disconnect the serial from TSA */ tsa_serial_disconnect(qmc->tsa_serial); - - return 0; } static const struct of_device_id qmc_id_table[] = { @@ -1443,7 +1441,7 @@ static struct platform_driver qmc_driver = { .of_match_table = of_match_ptr(qmc_id_table), }, .probe = qmc_probe, - .remove = qmc_remove, + .remove_new = qmc_remove, }; module_platform_driver(qmc_driver); -- cgit v1.2.3 From ea32155a0383c937d5b057b3093a3a4c4ace7360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:54:58 +0200 Subject: soc/fsl: cpm: tsa: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Acked-by: Herve Codina Link: https://lore.kernel.org/r/20230925095532.1984344-8-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König --- drivers/soc/fsl/qe/tsa.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/soc/fsl/qe/tsa.c b/drivers/soc/fsl/qe/tsa.c index 3646153117b3..3f9981335590 100644 --- a/drivers/soc/fsl/qe/tsa.c +++ b/drivers/soc/fsl/qe/tsa.c @@ -706,7 +706,7 @@ static int tsa_probe(struct platform_device *pdev) return 0; } -static int tsa_remove(struct platform_device *pdev) +static void tsa_remove(struct platform_device *pdev) { struct tsa *tsa = platform_get_drvdata(pdev); int i; @@ -729,7 +729,6 @@ static int tsa_remove(struct platform_device *pdev) clk_put(tsa->tdm[i].l1rclk_clk); } } - return 0; } static const struct of_device_id tsa_id_table[] = { @@ -744,7 +743,7 @@ static struct platform_driver tsa_driver = { .of_match_table = of_match_ptr(tsa_id_table), }, .probe = tsa_probe, - .remove = tsa_remove, + .remove_new = tsa_remove, }; module_platform_driver(tsa_driver); -- cgit v1.2.3 From 9a9b332db032ea17537ba6f5d6e6ddd3c0268d66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:54:59 +0200 Subject: soc/fujitsu: a64fx-diag: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Link: https://lore.kernel.org/r/20230925095532.1984344-9-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König --- drivers/soc/fujitsu/a64fx-diag.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/fujitsu/a64fx-diag.c b/drivers/soc/fujitsu/a64fx-diag.c index 524fbfeb94e3..330901893577 100644 --- a/drivers/soc/fujitsu/a64fx-diag.c +++ b/drivers/soc/fujitsu/a64fx-diag.c @@ -116,7 +116,7 @@ static int a64fx_diag_probe(struct platform_device *pdev) return 0; } -static int a64fx_diag_remove(struct platform_device *pdev) +static void a64fx_diag_remove(struct platform_device *pdev) { struct a64fx_diag_priv *priv = platform_get_drvdata(pdev); @@ -127,8 +127,6 @@ static int a64fx_diag_remove(struct platform_device *pdev) free_nmi(priv->irq, NULL); else free_irq(priv->irq, NULL); - - return 0; } static const struct acpi_device_id a64fx_diag_acpi_match[] = { @@ -144,7 +142,7 @@ static struct platform_driver a64fx_diag_driver = { .acpi_match_table = ACPI_PTR(a64fx_diag_acpi_match), }, .probe = a64fx_diag_probe, - .remove = a64fx_diag_remove, + .remove_new = a64fx_diag_remove, }; module_platform_driver(a64fx_diag_driver); -- cgit v1.2.3 From 3f40c999de39626f67aa36858801ec2906740125 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:00 +0200 Subject: soc/hisilicon: kunpeng_hccs: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Link: https://lore.kernel.org/r/20230925095532.1984344-10-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König --- drivers/soc/hisilicon/kunpeng_hccs.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c index f3810d9d1caa..01aec0df98ec 100644 --- a/drivers/soc/hisilicon/kunpeng_hccs.c +++ b/drivers/soc/hisilicon/kunpeng_hccs.c @@ -1244,14 +1244,12 @@ unregister_pcc_chan: return rc; } -static int hccs_remove(struct platform_device *pdev) +static void hccs_remove(struct platform_device *pdev) { struct hccs_dev *hdev = platform_get_drvdata(pdev); hccs_remove_topo_dirs(hdev); hccs_unregister_pcc_channel(hdev); - - return 0; } static const struct acpi_device_id hccs_acpi_match[] = { @@ -1262,7 +1260,7 @@ MODULE_DEVICE_TABLE(acpi, hccs_acpi_match); static struct platform_driver hccs_driver = { .probe = hccs_probe, - .remove = hccs_remove, + .remove_new = hccs_remove, .driver = { .name = "kunpeng_hccs", .acpi_match_table = hccs_acpi_match, -- cgit v1.2.3 From a25e745c752a0aa197296c0a6405255f19813b4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:01 +0200 Subject: soc/ixp4xx: ixp4xx-npe: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Link: https://lore.kernel.org/r/20230925095532.1984344-11-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König --- drivers/soc/ixp4xx/ixp4xx-npe.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/ixp4xx/ixp4xx-npe.c b/drivers/soc/ixp4xx/ixp4xx-npe.c index 5be9988f30ce..35825ee95dff 100644 --- a/drivers/soc/ixp4xx/ixp4xx-npe.c +++ b/drivers/soc/ixp4xx/ixp4xx-npe.c @@ -736,7 +736,7 @@ static int ixp4xx_npe_probe(struct platform_device *pdev) return 0; } -static int ixp4xx_npe_remove(struct platform_device *pdev) +static void ixp4xx_npe_remove(struct platform_device *pdev) { int i; @@ -744,8 +744,6 @@ static int ixp4xx_npe_remove(struct platform_device *pdev) if (npe_tab[i].regs) { npe_reset(&npe_tab[i]); } - - return 0; } static const struct of_device_id ixp4xx_npe_of_match[] = { @@ -761,7 +759,7 @@ static struct platform_driver ixp4xx_npe_driver = { .of_match_table = ixp4xx_npe_of_match, }, .probe = ixp4xx_npe_probe, - .remove = ixp4xx_npe_remove, + .remove_new = ixp4xx_npe_remove, }; module_platform_driver(ixp4xx_npe_driver); -- cgit v1.2.3 From 48544eee87a5df20f469fbcd44776235db3bb0b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:02 +0200 Subject: soc/ixp4xx: ixp4xx-qmgr: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Link: https://lore.kernel.org/r/20230925095532.1984344-12-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König --- drivers/soc/ixp4xx/ixp4xx-qmgr.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/soc/ixp4xx/ixp4xx-qmgr.c b/drivers/soc/ixp4xx/ixp4xx-qmgr.c index 291086bb9313..244ad8d7e80b 100644 --- a/drivers/soc/ixp4xx/ixp4xx-qmgr.c +++ b/drivers/soc/ixp4xx/ixp4xx-qmgr.c @@ -442,11 +442,10 @@ static int ixp4xx_qmgr_probe(struct platform_device *pdev) return 0; } -static int ixp4xx_qmgr_remove(struct platform_device *pdev) +static void ixp4xx_qmgr_remove(struct platform_device *pdev) { synchronize_irq(qmgr_irq_1); synchronize_irq(qmgr_irq_2); - return 0; } static const struct of_device_id ixp4xx_qmgr_of_match[] = { @@ -462,7 +461,7 @@ static struct platform_driver ixp4xx_qmgr_driver = { .of_match_table = ixp4xx_qmgr_of_match, }, .probe = ixp4xx_qmgr_probe, - .remove = ixp4xx_qmgr_remove, + .remove_new = ixp4xx_qmgr_remove, }; module_platform_driver(ixp4xx_qmgr_driver); -- cgit v1.2.3 From 95589c0349829c9f4de823075e727af8f2016ebf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:03 +0200 Subject: soc/litex: litex_soc_ctrl: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Acked-by: Gabriel Somlo Link: https://lore.kernel.org/r/20230925095532.1984344-13-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König --- drivers/soc/litex/litex_soc_ctrl.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/soc/litex/litex_soc_ctrl.c b/drivers/soc/litex/litex_soc_ctrl.c index f75790091d38..10813299aa10 100644 --- a/drivers/soc/litex/litex_soc_ctrl.c +++ b/drivers/soc/litex/litex_soc_ctrl.c @@ -120,12 +120,11 @@ static int litex_soc_ctrl_probe(struct platform_device *pdev) return 0; } -static int litex_soc_ctrl_remove(struct platform_device *pdev) +static void litex_soc_ctrl_remove(struct platform_device *pdev) { struct litex_soc_ctrl_device *soc_ctrl_dev = platform_get_drvdata(pdev); unregister_restart_handler(&soc_ctrl_dev->reset_nb); - return 0; } static struct platform_driver litex_soc_ctrl_driver = { @@ -134,7 +133,7 @@ static struct platform_driver litex_soc_ctrl_driver = { .of_match_table = of_match_ptr(litex_soc_ctrl_of_match) }, .probe = litex_soc_ctrl_probe, - .remove = litex_soc_ctrl_remove, + .remove_new = litex_soc_ctrl_remove, }; module_platform_driver(litex_soc_ctrl_driver); -- cgit v1.2.3 From db758b73f83fad5a12463fd41942fd1b4fc3080d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:04 +0200 Subject: soc/loongson: loongson2_guts: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Link: https://lore.kernel.org/r/20230925095532.1984344-14-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König --- drivers/soc/loongson/loongson2_guts.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/loongson/loongson2_guts.c b/drivers/soc/loongson/loongson2_guts.c index bace4bc8e03b..48f8382888a7 100644 --- a/drivers/soc/loongson/loongson2_guts.c +++ b/drivers/soc/loongson/loongson2_guts.c @@ -150,11 +150,9 @@ static int loongson2_guts_probe(struct platform_device *pdev) return 0; } -static int loongson2_guts_remove(struct platform_device *dev) +static void loongson2_guts_remove(struct platform_device *dev) { soc_device_unregister(soc_dev); - - return 0; } /* @@ -173,7 +171,7 @@ static struct platform_driver loongson2_guts_driver = { .of_match_table = loongson2_guts_of_match, }, .probe = loongson2_guts_probe, - .remove = loongson2_guts_remove, + .remove_new = loongson2_guts_remove, }; static int __init loongson2_guts_init(void) -- cgit v1.2.3 From a129ac3555c0dca6f04ae404dc0f0790656587fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:05 +0200 Subject: soc/mediatek: mtk-devapc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20230925095532.1984344-15-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König --- drivers/soc/mediatek/mtk-devapc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/mediatek/mtk-devapc.c b/drivers/soc/mediatek/mtk-devapc.c index b28feb967540..56cc345552a4 100644 --- a/drivers/soc/mediatek/mtk-devapc.c +++ b/drivers/soc/mediatek/mtk-devapc.c @@ -292,18 +292,16 @@ static int mtk_devapc_probe(struct platform_device *pdev) return 0; } -static int mtk_devapc_remove(struct platform_device *pdev) +static void mtk_devapc_remove(struct platform_device *pdev) { struct mtk_devapc_context *ctx = platform_get_drvdata(pdev); stop_devapc(ctx); - - return 0; } static struct platform_driver mtk_devapc_driver = { .probe = mtk_devapc_probe, - .remove = mtk_devapc_remove, + .remove_new = mtk_devapc_remove, .driver = { .name = "mtk-devapc", .of_match_table = mtk_devapc_dt_match, -- cgit v1.2.3 From 630cc5e1e6b37df7725142b4fed872dea9a9a36b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:06 +0200 Subject: soc/mediatek: mtk-mmsys: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20230925095532.1984344-16-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König --- drivers/soc/mediatek/mtk-mmsys.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/mediatek/mtk-mmsys.c b/drivers/soc/mediatek/mtk-mmsys.c index ffb75711a1da..88209102ff3b 100644 --- a/drivers/soc/mediatek/mtk-mmsys.c +++ b/drivers/soc/mediatek/mtk-mmsys.c @@ -410,14 +410,12 @@ out_probe_done: return 0; } -static int mtk_mmsys_remove(struct platform_device *pdev) +static void mtk_mmsys_remove(struct platform_device *pdev) { struct mtk_mmsys *mmsys = platform_get_drvdata(pdev); platform_device_unregister(mmsys->drm_pdev); platform_device_unregister(mmsys->clks_pdev); - - return 0; } static const struct of_device_id of_match_mtk_mmsys[] = { @@ -449,7 +447,7 @@ static struct platform_driver mtk_mmsys_drv = { .of_match_table = of_match_mtk_mmsys, }, .probe = mtk_mmsys_probe, - .remove = mtk_mmsys_remove, + .remove_new = mtk_mmsys_remove, }; module_platform_driver(mtk_mmsys_drv); -- cgit v1.2.3 From e77e6e3e909d33361c58af848a96e1f7f71ba7e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Sep 2023 11:55:08 +0200 Subject: soc/pxa: ssp: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Link: https://lore.kernel.org/r/20230925095532.1984344-18-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König --- drivers/soc/pxa/ssp.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/pxa/ssp.c b/drivers/soc/pxa/ssp.c index bd029e838241..a1e8a07f7275 100644 --- a/drivers/soc/pxa/ssp.c +++ b/drivers/soc/pxa/ssp.c @@ -176,15 +176,13 @@ static int pxa_ssp_probe(struct platform_device *pdev) return 0; } -static int pxa_ssp_remove(struct platform_device *pdev) +static void pxa_ssp_remove(struct platform_device *pdev) { struct ssp_device *ssp = platform_get_drvdata(pdev); mutex_lock(&ssp_lock); list_del(&ssp->node); mutex_unlock(&ssp_lock); - - return 0; } static const struct platform_device_id ssp_id_table[] = { @@ -199,7 +197,7 @@ static const struct platform_device_id ssp_id_table[] = { static struct platform_driver pxa_ssp_driver = { .probe = pxa_ssp_probe, - .remove = pxa_ssp_remove, + .remove_new = pxa_ssp_remove, .driver = { .name = "pxa2xx-ssp", .of_match_table = of_match_ptr(pxa_ssp_of_ids), -- cgit v1.2.3 From ada1682d60ac6017037305166d02eb0cd5ee50fa Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 4 Oct 2023 20:57:32 +0200 Subject: firmware: qcom: qseecom: add missing include guards The qseecom header does not contain ifdef guards against multiple inclusion. Add them. Fixes: 00b1248606ba ("firmware: qcom_scm: Add support for Qualcomm Secure Execution Environment SCM interface") Signed-off-by: Bartosz Golaszewski Reviewed-by: Maximilian Luz Link: https://lore.kernel.org/r/20231004185732.98621-1-brgl@bgdev.pl Signed-off-by: Bjorn Andersson --- include/linux/firmware/qcom/qcom_qseecom.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/linux/firmware/qcom/qcom_qseecom.h b/include/linux/firmware/qcom/qcom_qseecom.h index b531547e1dc9..5c28298a98be 100644 --- a/include/linux/firmware/qcom/qcom_qseecom.h +++ b/include/linux/firmware/qcom/qcom_qseecom.h @@ -5,6 +5,10 @@ * * Copyright (C) 2023 Maximilian Luz */ + +#ifndef __QCOM_QSEECOM_H +#define __QCOM_QSEECOM_H + #include #include @@ -44,3 +48,5 @@ static inline int qcom_qseecom_app_send(struct qseecom_client *client, void *req { return qcom_scm_qseecom_app_send(client->app_id, req, req_size, rsp, rsp_size); } + +#endif /* __QCOM_QSEECOM_H */ -- cgit v1.2.3 From 05e5f732850632f24310b677d16787136733f5d4 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 6 Oct 2023 17:46:38 -0500 Subject: firmware: ti_sci: Use device_get_match_data() Use preferred device_get_match_data() instead of of_match_device() to get the driver match data. With this, adjust the includes to explicitly include the correct headers. Signed-off-by: Rob Herring Link: https://lore.kernel.org/r/20231006224638.445168-1-robh@kernel.org Signed-off-by: Nishanth Menon --- drivers/firmware/ti_sci.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index ffca636cc13f..7041befc756a 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -16,7 +16,10 @@ #include #include #include -#include +#include +#include +#include +#include #include #include #include @@ -3293,7 +3296,6 @@ MODULE_DEVICE_TABLE(of, ti_sci_of_match); static int ti_sci_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - const struct of_device_id *of_id; const struct ti_sci_desc *desc; struct ti_sci_xfer *xfer; struct ti_sci_info *info = NULL; @@ -3304,12 +3306,7 @@ static int ti_sci_probe(struct platform_device *pdev) int reboot = 0; u32 h_id; - of_id = of_match_device(ti_sci_of_match, dev); - if (!of_id) { - dev_err(dev, "OF data missing\n"); - return -EINVAL; - } - desc = of_id->data; + desc = device_get_match_data(dev); info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); if (!info) -- cgit v1.2.3 From 50c01a942b287451ed7bec290ac09e07b91c6253 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 9 Oct 2023 16:13:43 -0500 Subject: soc: ti: knav_qmss_queue: Use device_get_match_data() Use preferred device_get_match_data() instead of of_match_device() to get the driver match data. With this, adjust the includes to explicitly include the correct headers. Signed-off-by: Rob Herring Link: https://lore.kernel.org/r/20231009211356.3242037-13-robh@kernel.org Signed-off-by: Nishanth Menon --- drivers/soc/ti/knav_qmss_queue.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/soc/ti/knav_qmss_queue.c b/drivers/soc/ti/knav_qmss_queue.c index cea791717957..06fb5505c22c 100644 --- a/drivers/soc/ti/knav_qmss_queue.c +++ b/drivers/soc/ti/knav_qmss_queue.c @@ -14,10 +14,12 @@ #include #include #include +#include #include -#include #include +#include #include +#include #include #include @@ -1754,7 +1756,6 @@ static int knav_queue_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; struct device_node *qmgrs, *queue_pools, *regions, *pdsps; - const struct of_device_id *match; struct device *dev = &pdev->dev; u32 temp[2]; int ret; @@ -1770,8 +1771,7 @@ static int knav_queue_probe(struct platform_device *pdev) return -ENOMEM; } - match = of_match_device(of_match_ptr(keystone_qmss_of_match), dev); - if (match && match->data) + if (device_get_match_data(dev)) kdev->version = QMSS_66AK2G; platform_set_drvdata(pdev, kdev); -- cgit v1.2.3 From 8dec342ead710dace27dc82096144bf7a1011827 Mon Sep 17 00:00:00 2001 From: Neha Malcom Francis Date: Mon, 16 Oct 2023 15:46:06 +0530 Subject: soc: ti: k3-socinfo: Fix typo in bitfield documentation Fix documentation that indicates wrong bit. Signed-off-by: Neha Malcom Francis Link: https://lore.kernel.org/r/20231016101608.993921-2-n-francis@ti.com Signed-off-by: Nishanth Menon --- drivers/soc/ti/k3-socinfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/ti/k3-socinfo.c b/drivers/soc/ti/k3-socinfo.c index 6ea9b8c7d335..d45f5cb955a6 100644 --- a/drivers/soc/ti/k3-socinfo.c +++ b/drivers/soc/ti/k3-socinfo.c @@ -20,7 +20,7 @@ * 31-28 VARIANT Device variant * 27-12 PARTNO Part number * 11-1 MFG Indicates TI as manufacturer (0x17) - * 1 Always 1 + * 0 Always 1 */ #define CTRLMMR_WKUP_JTAGID_VARIANT_SHIFT (28) #define CTRLMMR_WKUP_JTAGID_VARIANT_MASK GENMASK(31, 28) -- cgit v1.2.3 From 3aeb0d3694e16b5066db82aa1152884f2e6aace0 Mon Sep 17 00:00:00 2001 From: Neha Malcom Francis Date: Mon, 16 Oct 2023 15:46:07 +0530 Subject: soc: ti: k3-socinfo: Avoid overriding return value Avoid overriding the return value and make sure the right error code is reflected. If the part is none of the identified list present in k3_soc_ids[], return -ENODEV. Signed-off-by: Neha Malcom Francis Link: https://lore.kernel.org/r/20231016101608.993921-3-n-francis@ti.com Signed-off-by: Nishanth Menon --- drivers/soc/ti/k3-socinfo.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/soc/ti/k3-socinfo.c b/drivers/soc/ti/k3-socinfo.c index d45f5cb955a6..7fc3548e084c 100644 --- a/drivers/soc/ti/k3-socinfo.c +++ b/drivers/soc/ti/k3-socinfo.c @@ -60,7 +60,7 @@ k3_chipinfo_partno_to_names(unsigned int partno, return 0; } - return -EINVAL; + return -ENODEV; } static int k3_chipinfo_probe(struct platform_device *pdev) @@ -111,8 +111,7 @@ static int k3_chipinfo_probe(struct platform_device *pdev) ret = k3_chipinfo_partno_to_names(partno_id, soc_dev_attr); if (ret) { - dev_err(dev, "Unknown SoC JTAGID[0x%08X]\n", jtag_id); - ret = -ENODEV; + dev_err(dev, "Unknown SoC JTAGID[0x%08X]: %d\n", jtag_id, ret); goto err_free_rev; } -- cgit v1.2.3 From f86955f2b1ff9fbc7ae4f6595112b2f896885366 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 11 Oct 2023 01:52:29 +0300 Subject: soc: qcom: pmic_glink: fix connector type to be DisplayPort As it was pointed out by Simon Ser, the DRM_MODE_CONNECTOR_USB connector is reserved for the GUD devices. Other drivers (i915, amdgpu) use DRM_MODE_CONNECTOR_DisplayPort even if the DP stream is handled by the USB-C altmode. While we are still working on implementing the proper way to let userspace know that the DP is wrapped into USB-C, change connector type to be DRM_MODE_CONNECTOR_DisplayPort. Fixes: 080b4e24852b ("soc: qcom: pmic_glink: Introduce altmode support") Cc: Simon Ser Signed-off-by: Dmitry Baryshkov Reviewed-by: Neil Armstrong Acked-by: Simon Ser Link: https://lore.kernel.org/r/20231010225229.77027-1-dmitry.baryshkov@linaro.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/pmic_glink_altmode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/qcom/pmic_glink_altmode.c b/drivers/soc/qcom/pmic_glink_altmode.c index 9569d999391d..6f8b2f7ae3cc 100644 --- a/drivers/soc/qcom/pmic_glink_altmode.c +++ b/drivers/soc/qcom/pmic_glink_altmode.c @@ -467,7 +467,7 @@ static int pmic_glink_altmode_probe(struct auxiliary_device *adev, alt_port->bridge.funcs = &pmic_glink_altmode_bridge_funcs; alt_port->bridge.of_node = to_of_node(fwnode); alt_port->bridge.ops = DRM_BRIDGE_OP_HPD; - alt_port->bridge.type = DRM_MODE_CONNECTOR_USB; + alt_port->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; ret = devm_drm_bridge_add(dev, &alt_port->bridge); if (ret) { -- cgit v1.2.3 From ba21d6367cc2cca8f25fff3bc94b1b8df55fc261 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 9 Oct 2023 15:30:17 -0600 Subject: soc: qcom: apr: Add __counted_by for struct apr_rx_buf and use struct_size() Prepare for the coming implementation by GCC and Clang of the __counted_by attribute. Flexible array members annotated with __counted_by can have their accesses bounds-checked at run-time via CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for strcpy/memcpy-family functions). While there, use struct_size() helper, instead of the open-coded version, to calculate the size for the allocation of the whole flexible structure, including of course, the flexible-array member. This code was found with the help of Coccinelle, and audited and fixed manually. Signed-off-by: "Gustavo A. R. Silva" Reviewed-by: Justin Stitt Reviewed-by: Kees Cook Link: https://lore.kernel.org/r/ZSRw6RNi3Crhd32H@work Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/apr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soc/qcom/apr.c b/drivers/soc/qcom/apr.c index 30f81d6d9d9d..1f8b315576a4 100644 --- a/drivers/soc/qcom/apr.c +++ b/drivers/soc/qcom/apr.c @@ -41,7 +41,7 @@ struct packet_router { struct apr_rx_buf { struct list_head node; int len; - uint8_t buf[]; + uint8_t buf[] __counted_by(len); }; /** @@ -171,7 +171,7 @@ static int apr_callback(struct rpmsg_device *rpdev, void *buf, return -EINVAL; } - abuf = kzalloc(sizeof(*abuf) + len, GFP_ATOMIC); + abuf = kzalloc(struct_size(abuf, buf, len), GFP_ATOMIC); if (!abuf) return -ENOMEM; -- cgit v1.2.3 From 55fa358ca89f2e0b6fe0a4fa2472a7448e59021a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 16 Oct 2023 09:29:11 +0200 Subject: soc: samsung: exynos-chipid: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230925095532.1984344-32-u.kleine-koenig@pengutronix.de Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20231016072911.27148-2-krzysztof.kozlowski@linaro.org Signed-off-by: Arnd Bergmann --- drivers/soc/samsung/exynos-chipid.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/samsung/exynos-chipid.c b/drivers/soc/samsung/exynos-chipid.c index 7ba45c4aff97..3fd0f2b84dd3 100644 --- a/drivers/soc/samsung/exynos-chipid.c +++ b/drivers/soc/samsung/exynos-chipid.c @@ -158,13 +158,11 @@ err: return ret; } -static int exynos_chipid_remove(struct platform_device *pdev) +static void exynos_chipid_remove(struct platform_device *pdev) { struct soc_device *soc_dev = platform_get_drvdata(pdev); soc_device_unregister(soc_dev); - - return 0; } static const struct exynos_chipid_variant exynos4210_chipid_drv_data = { @@ -197,7 +195,7 @@ static struct platform_driver exynos_chipid_driver = { .of_match_table = exynos_chipid_of_device_ids, }, .probe = exynos_chipid_probe, - .remove = exynos_chipid_remove, + .remove_new = exynos_chipid_remove, }; module_platform_driver(exynos_chipid_driver); -- cgit v1.2.3 From bdac188ec3c71800dd8419620224ee74ef37732a Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 17 Oct 2023 11:27:18 +0200 Subject: firmware: qcom: move Qualcomm code into its own directory We're getting more and more qcom specific .c files in drivers/firmware/ and about to get even more. Create a separate directory for Qualcomm firmware drivers and move existing sources in there. Signed-off-by: Bartosz Golaszewski Acked-by: Elliot Berman Reviewed-by: Krzysztof Kozlowski Reviewed-by: Maximilian Luz Tested-by: Andrew Halaney # sc8280xp-lenovo-thinkpad-x13s Link: https://lore.kernel.org/r/20231017092732.19983-2-brgl@bgdev.pl Signed-off-by: Bjorn Andersson --- MAINTAINERS | 4 +- drivers/firmware/Kconfig | 48 +- drivers/firmware/Makefile | 5 +- drivers/firmware/qcom/Kconfig | 56 + drivers/firmware/qcom/Makefile | 9 + drivers/firmware/qcom/qcom_qseecom.c | 120 ++ drivers/firmware/qcom/qcom_qseecom_uefisecapp.c | 871 ++++++++++ drivers/firmware/qcom/qcom_scm-legacy.c | 246 +++ drivers/firmware/qcom/qcom_scm-smc.c | 225 +++ drivers/firmware/qcom/qcom_scm.c | 1943 +++++++++++++++++++++++ drivers/firmware/qcom/qcom_scm.h | 168 ++ drivers/firmware/qcom_qseecom.c | 120 -- drivers/firmware/qcom_qseecom_uefisecapp.c | 871 ---------- drivers/firmware/qcom_scm-legacy.c | 246 --- drivers/firmware/qcom_scm-smc.c | 225 --- drivers/firmware/qcom_scm.c | 1943 ----------------------- drivers/firmware/qcom_scm.h | 168 -- 17 files changed, 3642 insertions(+), 3626 deletions(-) create mode 100644 drivers/firmware/qcom/Kconfig create mode 100644 drivers/firmware/qcom/Makefile create mode 100644 drivers/firmware/qcom/qcom_qseecom.c create mode 100644 drivers/firmware/qcom/qcom_qseecom_uefisecapp.c create mode 100644 drivers/firmware/qcom/qcom_scm-legacy.c create mode 100644 drivers/firmware/qcom/qcom_scm-smc.c create mode 100644 drivers/firmware/qcom/qcom_scm.c create mode 100644 drivers/firmware/qcom/qcom_scm.h delete mode 100644 drivers/firmware/qcom_qseecom.c delete mode 100644 drivers/firmware/qcom_qseecom_uefisecapp.c delete mode 100644 drivers/firmware/qcom_scm-legacy.c delete mode 100644 drivers/firmware/qcom_scm-smc.c delete mode 100644 drivers/firmware/qcom_scm.c delete mode 100644 drivers/firmware/qcom_scm.h diff --git a/MAINTAINERS b/MAINTAINERS index 01dde0acebd3..2e28b5efc7fa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17804,13 +17804,13 @@ QUALCOMM QSEECOM DRIVER M: Maximilian Luz L: linux-arm-msm@vger.kernel.org S: Maintained -F: drivers/firmware/qcom_qseecom.c +F: drivers/firmware/qcom/qcom_qseecom.c QUALCOMM QSEECOM UEFISECAPP DRIVER M: Maximilian Luz L: linux-arm-msm@vger.kernel.org S: Maintained -F: drivers/firmware/qcom_qseecom_uefisecapp.c +F: drivers/firmware/qcom/qcom_qseecom_uefisecapp.c QUALCOMM RMNET DRIVER M: Subash Abhinov Kasiviswanathan diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 8c608be30060..46949e9b748a 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -212,53 +212,6 @@ config MTK_ADSP_IPC ADSP exists on some mtk processors. Client might use shared memory to exchange information with ADSP. -config QCOM_SCM - tristate - -config QCOM_SCM_DOWNLOAD_MODE_DEFAULT - bool "Qualcomm download mode enabled by default" - depends on QCOM_SCM - help - A device with "download mode" enabled will upon an unexpected - warm-restart enter a special debug mode that allows the user to - "download" memory content over USB for offline postmortem analysis. - The feature can be enabled/disabled on the kernel command line. - - Say Y here to enable "download mode" by default. - -config QCOM_QSEECOM - bool "Qualcomm QSEECOM interface driver" - depends on QCOM_SCM=y - select AUXILIARY_BUS - help - Various Qualcomm SoCs have a Secure Execution Environment (SEE) running - in the Trust Zone. This module provides an interface to that via the - QSEECOM mechanism, using SCM calls. - - The QSEECOM interface allows, among other things, access to applications - running in the SEE. An example of such an application is 'uefisecapp', - which is required to access UEFI variables on certain systems. If - selected, the interface will also attempt to detect and register client - devices for supported applications. - - Select Y here to enable the QSEECOM interface driver. - -config QCOM_QSEECOM_UEFISECAPP - bool "Qualcomm SEE UEFI Secure App client driver" - depends on QCOM_QSEECOM - depends on EFI - help - Various Qualcomm SoCs do not allow direct access to EFI variables. - Instead, these need to be accessed via the UEFI Secure Application - (uefisecapp), residing in the Secure Execution Environment (SEE). - - This module provides a client driver for uefisecapp, installing efivar - operations to allow the kernel accessing EFI variables, and via that also - provide user-space with access to EFI variables via efivarfs. - - Select Y here to provide access to EFI variables on the aforementioned - platforms. - config SYSFB bool select BOOT_VESA_SUPPORT @@ -344,6 +297,7 @@ source "drivers/firmware/efi/Kconfig" source "drivers/firmware/imx/Kconfig" source "drivers/firmware/meson/Kconfig" source "drivers/firmware/psci/Kconfig" +source "drivers/firmware/qcom/Kconfig" source "drivers/firmware/smccc/Kconfig" source "drivers/firmware/tegra/Kconfig" source "drivers/firmware/xilinx/Kconfig" diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index d41b094a5e58..ec454e952168 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -18,10 +18,6 @@ obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o obj-$(CONFIG_MTK_ADSP_IPC) += mtk-adsp-ipc.o obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o -obj-$(CONFIG_QCOM_SCM) += qcom-scm.o -qcom-scm-objs += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o -obj-$(CONFIG_QCOM_QSEECOM) += qcom_qseecom.o -obj-$(CONFIG_QCOM_QSEECOM_UEFISECAPP) += qcom_qseecom_uefisecapp.o obj-$(CONFIG_SYSFB) += sysfb.o obj-$(CONFIG_SYSFB_SIMPLEFB) += sysfb_simplefb.o obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o @@ -37,6 +33,7 @@ obj-$(CONFIG_GOOGLE_FIRMWARE) += google/ obj-y += efi/ obj-y += imx/ obj-y += psci/ +obj-y += qcom/ obj-y += smccc/ obj-y += tegra/ obj-y += xilinx/ diff --git a/drivers/firmware/qcom/Kconfig b/drivers/firmware/qcom/Kconfig new file mode 100644 index 000000000000..3f05d9854ddf --- /dev/null +++ b/drivers/firmware/qcom/Kconfig @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# For a description of the syntax of this configuration file, +# see Documentation/kbuild/kconfig-language.rst. +# + +menu "Qualcomm firmware drivers" + +config QCOM_SCM + tristate + +config QCOM_SCM_DOWNLOAD_MODE_DEFAULT + bool "Qualcomm download mode enabled by default" + depends on QCOM_SCM + help + A device with "download mode" enabled will upon an unexpected + warm-restart enter a special debug mode that allows the user to + "download" memory content over USB for offline postmortem analysis. + The feature can be enabled/disabled on the kernel command line. + + Say Y here to enable "download mode" by default. + +config QCOM_QSEECOM + bool "Qualcomm QSEECOM interface driver" + depends on QCOM_SCM=y + select AUXILIARY_BUS + help + Various Qualcomm SoCs have a Secure Execution Environment (SEE) running + in the Trust Zone. This module provides an interface to that via the + QSEECOM mechanism, using SCM calls. + + The QSEECOM interface allows, among other things, access to applications + running in the SEE. An example of such an application is 'uefisecapp', + which is required to access UEFI variables on certain systems. If + selected, the interface will also attempt to detect and register client + devices for supported applications. + + Select Y here to enable the QSEECOM interface driver. + +config QCOM_QSEECOM_UEFISECAPP + bool "Qualcomm SEE UEFI Secure App client driver" + depends on QCOM_QSEECOM + depends on EFI + help + Various Qualcomm SoCs do not allow direct access to EFI variables. + Instead, these need to be accessed via the UEFI Secure Application + (uefisecapp), residing in the Secure Execution Environment (SEE). + + This module provides a client driver for uefisecapp, installing efivar + operations to allow the kernel accessing EFI variables, and via that also + provide user-space with access to EFI variables via efivarfs. + + Select Y here to provide access to EFI variables on the aforementioned + platforms. + +endmenu diff --git a/drivers/firmware/qcom/Makefile b/drivers/firmware/qcom/Makefile new file mode 100644 index 000000000000..c9f12ee8224a --- /dev/null +++ b/drivers/firmware/qcom/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the linux kernel. +# + +obj-$(CONFIG_QCOM_SCM) += qcom-scm.o +qcom-scm-objs += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o +obj-$(CONFIG_QCOM_QSEECOM) += qcom_qseecom.o +obj-$(CONFIG_QCOM_QSEECOM_UEFISECAPP) += qcom_qseecom_uefisecapp.o diff --git a/drivers/firmware/qcom/qcom_qseecom.c b/drivers/firmware/qcom/qcom_qseecom.c new file mode 100644 index 000000000000..731e6d5719f9 --- /dev/null +++ b/drivers/firmware/qcom/qcom_qseecom.c @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Qualcomm Secure Execution Environment (SEE) interface (QSEECOM). + * Responsible for setting up and managing QSEECOM client devices. + * + * Copyright (C) 2023 Maximilian Luz + */ +#include +#include +#include +#include +#include + +#include +#include + +struct qseecom_app_desc { + const char *app_name; + const char *dev_name; +}; + +static void qseecom_client_release(struct device *dev) +{ + struct qseecom_client *client; + + client = container_of(dev, struct qseecom_client, aux_dev.dev); + kfree(client); +} + +static void qseecom_client_remove(void *data) +{ + struct qseecom_client *client = data; + + auxiliary_device_delete(&client->aux_dev); + auxiliary_device_uninit(&client->aux_dev); +} + +static int qseecom_client_register(struct platform_device *qseecom_dev, + const struct qseecom_app_desc *desc) +{ + struct qseecom_client *client; + u32 app_id; + int ret; + + /* Try to find the app ID, skip device if not found */ + ret = qcom_scm_qseecom_app_get_id(desc->app_name, &app_id); + if (ret) + return ret == -ENOENT ? 0 : ret; + + dev_info(&qseecom_dev->dev, "setting up client for %s\n", desc->app_name); + + /* Allocate and set-up the client device */ + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) + return -ENOMEM; + + client->aux_dev.name = desc->dev_name; + client->aux_dev.dev.parent = &qseecom_dev->dev; + client->aux_dev.dev.release = qseecom_client_release; + client->app_id = app_id; + + ret = auxiliary_device_init(&client->aux_dev); + if (ret) { + kfree(client); + return ret; + } + + ret = auxiliary_device_add(&client->aux_dev); + if (ret) { + auxiliary_device_uninit(&client->aux_dev); + return ret; + } + + ret = devm_add_action_or_reset(&qseecom_dev->dev, qseecom_client_remove, client); + if (ret) + return ret; + + return 0; +} + +/* + * List of supported applications. One client device will be created per entry, + * assuming the app has already been loaded (usually by firmware bootloaders) + * and its ID can be queried successfully. + */ +static const struct qseecom_app_desc qcom_qseecom_apps[] = { + { "qcom.tz.uefisecapp", "uefisecapp" }, +}; + +static int qcom_qseecom_probe(struct platform_device *qseecom_dev) +{ + int ret; + int i; + + /* Set up client devices for each base application */ + for (i = 0; i < ARRAY_SIZE(qcom_qseecom_apps); i++) { + ret = qseecom_client_register(qseecom_dev, &qcom_qseecom_apps[i]); + if (ret) + return ret; + } + + return 0; +} + +static struct platform_driver qcom_qseecom_driver = { + .driver = { + .name = "qcom_qseecom", + }, + .probe = qcom_qseecom_probe, +}; + +static int __init qcom_qseecom_init(void) +{ + return platform_driver_register(&qcom_qseecom_driver); +} +subsys_initcall(qcom_qseecom_init); + +MODULE_AUTHOR("Maximilian Luz "); +MODULE_DESCRIPTION("Driver for the Qualcomm SEE (QSEECOM) interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/qcom/qcom_qseecom_uefisecapp.c b/drivers/firmware/qcom/qcom_qseecom_uefisecapp.c new file mode 100644 index 000000000000..a33acdaf7b78 --- /dev/null +++ b/drivers/firmware/qcom/qcom_qseecom_uefisecapp.c @@ -0,0 +1,871 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Client driver for Qualcomm UEFI Secure Application (qcom.tz.uefisecapp). + * Provides access to UEFI variables on platforms where they are secured by the + * aforementioned Secure Execution Environment (SEE) application. + * + * Copyright (C) 2023 Maximilian Luz + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* -- Qualcomm "uefisecapp" interface definitions. -------------------------- */ + +/* Maximum length of name string with null-terminator */ +#define QSEE_MAX_NAME_LEN 1024 + +#define QSEE_CMD_UEFI(x) (0x8000 | (x)) +#define QSEE_CMD_UEFI_GET_VARIABLE QSEE_CMD_UEFI(0) +#define QSEE_CMD_UEFI_SET_VARIABLE QSEE_CMD_UEFI(1) +#define QSEE_CMD_UEFI_GET_NEXT_VARIABLE QSEE_CMD_UEFI(2) +#define QSEE_CMD_UEFI_QUERY_VARIABLE_INFO QSEE_CMD_UEFI(3) + +/** + * struct qsee_req_uefi_get_variable - Request for GetVariable command. + * @command_id: The ID of the command. Must be %QSEE_CMD_UEFI_GET_VARIABLE. + * @length: Length of the request in bytes, including this struct and any + * parameters (name, GUID) stored after it as well as any padding + * thereof for alignment. + * @name_offset: Offset from the start of this struct to where the variable + * name is stored (as utf-16 string), in bytes. + * @name_size: Size of the name parameter in bytes, including null-terminator. + * @guid_offset: Offset from the start of this struct to where the GUID + * parameter is stored, in bytes. + * @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t). + * @data_size: Size of the output buffer, in bytes. + */ +struct qsee_req_uefi_get_variable { + u32 command_id; + u32 length; + u32 name_offset; + u32 name_size; + u32 guid_offset; + u32 guid_size; + u32 data_size; +} __packed; + +/** + * struct qsee_rsp_uefi_get_variable - Response for GetVariable command. + * @command_id: The ID of the command. Should be %QSEE_CMD_UEFI_GET_VARIABLE. + * @length: Length of the response in bytes, including this struct and the + * returned data. + * @status: Status of this command. + * @attributes: EFI variable attributes. + * @data_offset: Offset from the start of this struct to where the data is + * stored, in bytes. + * @data_size: Size of the returned data, in bytes. In case status indicates + * that the buffer is too small, this will be the size required + * to store the EFI variable data. + */ +struct qsee_rsp_uefi_get_variable { + u32 command_id; + u32 length; + u32 status; + u32 attributes; + u32 data_offset; + u32 data_size; +} __packed; + +/** + * struct qsee_req_uefi_set_variable - Request for the SetVariable command. + * @command_id: The ID of the command. Must be %QSEE_CMD_UEFI_SET_VARIABLE. + * @length: Length of the request in bytes, including this struct and any + * parameters (name, GUID, data) stored after it as well as any + * padding thereof required for alignment. + * @name_offset: Offset from the start of this struct to where the variable + * name is stored (as utf-16 string), in bytes. + * @name_size: Size of the name parameter in bytes, including null-terminator. + * @guid_offset: Offset from the start of this struct to where the GUID + * parameter is stored, in bytes. + * @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t). + * @attributes: The EFI variable attributes to set for this variable. + * @data_offset: Offset from the start of this struct to where the EFI variable + * data is stored, in bytes. + * @data_size: Size of EFI variable data, in bytes. + * + */ +struct qsee_req_uefi_set_variable { + u32 command_id; + u32 length; + u32 name_offset; + u32 name_size; + u32 guid_offset; + u32 guid_size; + u32 attributes; + u32 data_offset; + u32 data_size; +} __packed; + +/** + * struct qsee_rsp_uefi_set_variable - Response for the SetVariable command. + * @command_id: The ID of the command. Should be %QSEE_CMD_UEFI_SET_VARIABLE. + * @length: The length of this response, i.e. the size of this struct in + * bytes. + * @status: Status of this command. + * @_unknown1: Unknown response field. + * @_unknown2: Unknown response field. + */ +struct qsee_rsp_uefi_set_variable { + u32 command_id; + u32 length; + u32 status; + u32 _unknown1; + u32 _unknown2; +} __packed; + +/** + * struct qsee_req_uefi_get_next_variable - Request for the + * GetNextVariableName command. + * @command_id: The ID of the command. Must be + * %QSEE_CMD_UEFI_GET_NEXT_VARIABLE. + * @length: Length of the request in bytes, including this struct and any + * parameters (name, GUID) stored after it as well as any padding + * thereof for alignment. + * @guid_offset: Offset from the start of this struct to where the GUID + * parameter is stored, in bytes. + * @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t). + * @name_offset: Offset from the start of this struct to where the variable + * name is stored (as utf-16 string), in bytes. + * @name_size: Size of the name parameter in bytes, including null-terminator. + */ +struct qsee_req_uefi_get_next_variable { + u32 command_id; + u32 length; + u32 guid_offset; + u32 guid_size; + u32 name_offset; + u32 name_size; +} __packed; + +/** + * struct qsee_rsp_uefi_get_next_variable - Response for the + * GetNextVariableName command. + * @command_id: The ID of the command. Should be + * %QSEE_CMD_UEFI_GET_NEXT_VARIABLE. + * @length: Length of the response in bytes, including this struct and any + * parameters (name, GUID) stored after it as well as any padding + * thereof for alignment. + * @status: Status of this command. + * @guid_offset: Offset from the start of this struct to where the GUID + * parameter is stored, in bytes. + * @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t). + * @name_offset: Offset from the start of this struct to where the variable + * name is stored (as utf-16 string), in bytes. + * @name_size: Size of the name parameter in bytes, including null-terminator. + */ +struct qsee_rsp_uefi_get_next_variable { + u32 command_id; + u32 length; + u32 status; + u32 guid_offset; + u32 guid_size; + u32 name_offset; + u32 name_size; +} __packed; + +/** + * struct qsee_req_uefi_query_variable_info - Response for the + * GetNextVariableName command. + * @command_id: The ID of the command. Must be + * %QSEE_CMD_UEFI_QUERY_VARIABLE_INFO. + * @length: The length of this request, i.e. the size of this struct in + * bytes. + * @attributes: The storage attributes to query the info for. + */ +struct qsee_req_uefi_query_variable_info { + u32 command_id; + u32 length; + u32 attributes; +} __packed; + +/** + * struct qsee_rsp_uefi_query_variable_info - Response for the + * GetNextVariableName command. + * @command_id: The ID of the command. Must be + * %QSEE_CMD_UEFI_QUERY_VARIABLE_INFO. + * @length: The length of this response, i.e. the size of this + * struct in bytes. + * @status: Status of this command. + * @_pad: Padding. + * @storage_space: Full storage space size, in bytes. + * @remaining_space: Free storage space available, in bytes. + * @max_variable_size: Maximum variable data size, in bytes. + */ +struct qsee_rsp_uefi_query_variable_info { + u32 command_id; + u32 length; + u32 status; + u32 _pad; + u64 storage_space; + u64 remaining_space; + u64 max_variable_size; +} __packed; + +/* -- Alignment helpers ----------------------------------------------------- */ + +/* + * Helper macro to ensure proper alignment of types (fields and arrays) when + * stored in some (contiguous) buffer. + * + * Note: The driver from which this one has been reverse-engineered expects an + * alignment of 8 bytes (64 bits) for GUIDs. Our definition of efi_guid_t, + * however, has an alignment of 4 byte (32 bits). So far, this seems to work + * fine here. See also the comment on the typedef of efi_guid_t. + */ +#define qcuefi_buf_align_fields(fields...) \ + ({ \ + size_t __len = 0; \ + fields \ + __len; \ + }) + +#define __field_impl(size, align, offset) \ + ({ \ + size_t *__offset = (offset); \ + size_t __aligned; \ + \ + __aligned = ALIGN(__len, align); \ + __len = __aligned + (size); \ + \ + if (__offset) \ + *__offset = __aligned; \ + }); + +#define __array_offs(type, count, offset) \ + __field_impl(sizeof(type) * (count), __alignof__(type), offset) + +#define __array(type, count) __array_offs(type, count, NULL) +#define __field_offs(type, offset) __array_offs(type, 1, offset) +#define __field(type) __array_offs(type, 1, NULL) + +/* -- UEFI app interface. --------------------------------------------------- */ + +struct qcuefi_client { + struct qseecom_client *client; + struct efivars efivars; +}; + +static struct device *qcuefi_dev(struct qcuefi_client *qcuefi) +{ + return &qcuefi->client->aux_dev.dev; +} + +static efi_status_t qsee_uefi_status_to_efi(u32 status) +{ + u64 category = status & 0xf0000000; + u64 code = status & 0x0fffffff; + + return category << (BITS_PER_LONG - 32) | code; +} + +static efi_status_t qsee_uefi_get_variable(struct qcuefi_client *qcuefi, const efi_char16_t *name, + const efi_guid_t *guid, u32 *attributes, + unsigned long *data_size, void *data) +{ + struct qsee_req_uefi_get_variable *req_data; + struct qsee_rsp_uefi_get_variable *rsp_data; + unsigned long buffer_size = *data_size; + efi_status_t efi_status = EFI_SUCCESS; + unsigned long name_length; + size_t guid_offs; + size_t name_offs; + size_t req_size; + size_t rsp_size; + ssize_t status; + + if (!name || !guid) + return EFI_INVALID_PARAMETER; + + name_length = ucs2_strnlen(name, QSEE_MAX_NAME_LEN) + 1; + if (name_length > QSEE_MAX_NAME_LEN) + return EFI_INVALID_PARAMETER; + + if (buffer_size && !data) + return EFI_INVALID_PARAMETER; + + req_size = qcuefi_buf_align_fields( + __field(*req_data) + __array_offs(*name, name_length, &name_offs) + __field_offs(*guid, &guid_offs) + ); + + rsp_size = qcuefi_buf_align_fields( + __field(*rsp_data) + __array(u8, buffer_size) + ); + + req_data = kzalloc(req_size, GFP_KERNEL); + if (!req_data) { + efi_status = EFI_OUT_OF_RESOURCES; + goto out; + } + + rsp_data = kzalloc(rsp_size, GFP_KERNEL); + if (!rsp_data) { + efi_status = EFI_OUT_OF_RESOURCES; + goto out_free_req; + } + + req_data->command_id = QSEE_CMD_UEFI_GET_VARIABLE; + req_data->data_size = buffer_size; + req_data->name_offset = name_offs; + req_data->name_size = name_length * sizeof(*name); + req_data->guid_offset = guid_offs; + req_data->guid_size = sizeof(*guid); + req_data->length = req_size; + + status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name, name_length); + if (status < 0) + return EFI_INVALID_PARAMETER; + + memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size); + + status = qcom_qseecom_app_send(qcuefi->client, req_data, req_size, rsp_data, rsp_size); + if (status) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->command_id != QSEE_CMD_UEFI_GET_VARIABLE) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->length < sizeof(*rsp_data)) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->status) { + dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n", + __func__, rsp_data->status); + efi_status = qsee_uefi_status_to_efi(rsp_data->status); + + /* Update size and attributes in case buffer is too small. */ + if (efi_status == EFI_BUFFER_TOO_SMALL) { + *data_size = rsp_data->data_size; + if (attributes) + *attributes = rsp_data->attributes; + } + + goto out_free; + } + + if (rsp_data->length > rsp_size) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->data_offset + rsp_data->data_size > rsp_data->length) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + /* + * Note: We need to set attributes and data size even if the buffer is + * too small and we won't copy any data. This is described in spec, so + * that callers can either allocate a buffer properly (with two calls + * to this function) or just read back attributes withouth having to + * deal with that. + * + * Specifically: + * - If we have a buffer size of zero and no buffer, just return the + * attributes, required size, and indicate success. + * - If the buffer size is nonzero but too small, indicate that as an + * error. + * - Otherwise, we are good to copy the data. + * + * Note that we have already ensured above that the buffer pointer is + * non-NULL if its size is nonzero. + */ + *data_size = rsp_data->data_size; + if (attributes) + *attributes = rsp_data->attributes; + + if (buffer_size == 0 && !data) { + efi_status = EFI_SUCCESS; + goto out_free; + } + + if (buffer_size < rsp_data->data_size) { + efi_status = EFI_BUFFER_TOO_SMALL; + goto out_free; + } + + memcpy(data, ((void *)rsp_data) + rsp_data->data_offset, rsp_data->data_size); + +out_free: + kfree(rsp_data); +out_free_req: + kfree(req_data); +out: + return efi_status; +} + +static efi_status_t qsee_uefi_set_variable(struct qcuefi_client *qcuefi, const efi_char16_t *name, + const efi_guid_t *guid, u32 attributes, + unsigned long data_size, const void *data) +{ + struct qsee_req_uefi_set_variable *req_data; + struct qsee_rsp_uefi_set_variable *rsp_data; + efi_status_t efi_status = EFI_SUCCESS; + unsigned long name_length; + size_t name_offs; + size_t guid_offs; + size_t data_offs; + size_t req_size; + ssize_t status; + + if (!name || !guid) + return EFI_INVALID_PARAMETER; + + name_length = ucs2_strnlen(name, QSEE_MAX_NAME_LEN) + 1; + if (name_length > QSEE_MAX_NAME_LEN) + return EFI_INVALID_PARAMETER; + + /* + * Make sure we have some data if data_size is nonzero. Note that using + * a size of zero is a valid use-case described in spec and deletes the + * variable. + */ + if (data_size && !data) + return EFI_INVALID_PARAMETER; + + req_size = qcuefi_buf_align_fields( + __field(*req_data) + __array_offs(*name, name_length, &name_offs) + __field_offs(*guid, &guid_offs) + __array_offs(u8, data_size, &data_offs) + ); + + req_data = kzalloc(req_size, GFP_KERNEL); + if (!req_data) { + efi_status = EFI_OUT_OF_RESOURCES; + goto out; + } + + rsp_data = kzalloc(sizeof(*rsp_data), GFP_KERNEL); + if (!rsp_data) { + efi_status = EFI_OUT_OF_RESOURCES; + goto out_free_req; + } + + req_data->command_id = QSEE_CMD_UEFI_SET_VARIABLE; + req_data->attributes = attributes; + req_data->name_offset = name_offs; + req_data->name_size = name_length * sizeof(*name); + req_data->guid_offset = guid_offs; + req_data->guid_size = sizeof(*guid); + req_data->data_offset = data_offs; + req_data->data_size = data_size; + req_data->length = req_size; + + status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name, name_length); + if (status < 0) + return EFI_INVALID_PARAMETER; + + memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size); + + if (data_size) + memcpy(((void *)req_data) + req_data->data_offset, data, req_data->data_size); + + status = qcom_qseecom_app_send(qcuefi->client, req_data, req_size, rsp_data, + sizeof(*rsp_data)); + if (status) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->command_id != QSEE_CMD_UEFI_SET_VARIABLE) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->length != sizeof(*rsp_data)) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->status) { + dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n", + __func__, rsp_data->status); + efi_status = qsee_uefi_status_to_efi(rsp_data->status); + } + +out_free: + kfree(rsp_data); +out_free_req: + kfree(req_data); +out: + return efi_status; +} + +static efi_status_t qsee_uefi_get_next_variable(struct qcuefi_client *qcuefi, + unsigned long *name_size, efi_char16_t *name, + efi_guid_t *guid) +{ + struct qsee_req_uefi_get_next_variable *req_data; + struct qsee_rsp_uefi_get_next_variable *rsp_data; + efi_status_t efi_status = EFI_SUCCESS; + size_t guid_offs; + size_t name_offs; + size_t req_size; + size_t rsp_size; + ssize_t status; + + if (!name_size || !name || !guid) + return EFI_INVALID_PARAMETER; + + if (*name_size == 0) + return EFI_INVALID_PARAMETER; + + req_size = qcuefi_buf_align_fields( + __field(*req_data) + __field_offs(*guid, &guid_offs) + __array_offs(*name, *name_size / sizeof(*name), &name_offs) + ); + + rsp_size = qcuefi_buf_align_fields( + __field(*rsp_data) + __field(*guid) + __array(*name, *name_size / sizeof(*name)) + ); + + req_data = kzalloc(req_size, GFP_KERNEL); + if (!req_data) { + efi_status = EFI_OUT_OF_RESOURCES; + goto out; + } + + rsp_data = kzalloc(rsp_size, GFP_KERNEL); + if (!rsp_data) { + efi_status = EFI_OUT_OF_RESOURCES; + goto out_free_req; + } + + req_data->command_id = QSEE_CMD_UEFI_GET_NEXT_VARIABLE; + req_data->guid_offset = guid_offs; + req_data->guid_size = sizeof(*guid); + req_data->name_offset = name_offs; + req_data->name_size = *name_size; + req_data->length = req_size; + + memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size); + status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name, + *name_size / sizeof(*name)); + if (status < 0) + return EFI_INVALID_PARAMETER; + + status = qcom_qseecom_app_send(qcuefi->client, req_data, req_size, rsp_data, rsp_size); + if (status) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->command_id != QSEE_CMD_UEFI_GET_NEXT_VARIABLE) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->length < sizeof(*rsp_data)) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->status) { + dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n", + __func__, rsp_data->status); + efi_status = qsee_uefi_status_to_efi(rsp_data->status); + + /* + * If the buffer to hold the name is too small, update the + * name_size with the required size, so that callers can + * reallocate it accordingly. + */ + if (efi_status == EFI_BUFFER_TOO_SMALL) + *name_size = rsp_data->name_size; + + goto out_free; + } + + if (rsp_data->length > rsp_size) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->name_offset + rsp_data->name_size > rsp_data->length) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->guid_offset + rsp_data->guid_size > rsp_data->length) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->name_size > *name_size) { + *name_size = rsp_data->name_size; + efi_status = EFI_BUFFER_TOO_SMALL; + goto out_free; + } + + if (rsp_data->guid_size != sizeof(*guid)) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + memcpy(guid, ((void *)rsp_data) + rsp_data->guid_offset, rsp_data->guid_size); + status = ucs2_strscpy(name, ((void *)rsp_data) + rsp_data->name_offset, + rsp_data->name_size / sizeof(*name)); + *name_size = rsp_data->name_size; + + if (status < 0) { + /* + * Return EFI_DEVICE_ERROR here because the buffer size should + * have already been validated above, causing this function to + * bail with EFI_BUFFER_TOO_SMALL. + */ + return EFI_DEVICE_ERROR; + } + +out_free: + kfree(rsp_data); +out_free_req: + kfree(req_data); +out: + return efi_status; +} + +static efi_status_t qsee_uefi_query_variable_info(struct qcuefi_client *qcuefi, u32 attr, + u64 *storage_space, u64 *remaining_space, + u64 *max_variable_size) +{ + struct qsee_req_uefi_query_variable_info *req_data; + struct qsee_rsp_uefi_query_variable_info *rsp_data; + efi_status_t efi_status = EFI_SUCCESS; + int status; + + req_data = kzalloc(sizeof(*req_data), GFP_KERNEL); + if (!req_data) { + efi_status = EFI_OUT_OF_RESOURCES; + goto out; + } + + rsp_data = kzalloc(sizeof(*rsp_data), GFP_KERNEL); + if (!rsp_data) { + efi_status = EFI_OUT_OF_RESOURCES; + goto out_free_req; + } + + req_data->command_id = QSEE_CMD_UEFI_QUERY_VARIABLE_INFO; + req_data->attributes = attr; + req_data->length = sizeof(*req_data); + + status = qcom_qseecom_app_send(qcuefi->client, req_data, sizeof(*req_data), rsp_data, + sizeof(*rsp_data)); + if (status) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->command_id != QSEE_CMD_UEFI_QUERY_VARIABLE_INFO) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->length != sizeof(*rsp_data)) { + efi_status = EFI_DEVICE_ERROR; + goto out_free; + } + + if (rsp_data->status) { + dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n", + __func__, rsp_data->status); + efi_status = qsee_uefi_status_to_efi(rsp_data->status); + goto out_free; + } + + if (storage_space) + *storage_space = rsp_data->storage_space; + + if (remaining_space) + *remaining_space = rsp_data->remaining_space; + + if (max_variable_size) + *max_variable_size = rsp_data->max_variable_size; + +out_free: + kfree(rsp_data); +out_free_req: + kfree(req_data); +out: + return efi_status; +} + +/* -- Global efivar interface. ---------------------------------------------- */ + +static struct qcuefi_client *__qcuefi; +static DEFINE_MUTEX(__qcuefi_lock); + +static int qcuefi_set_reference(struct qcuefi_client *qcuefi) +{ + mutex_lock(&__qcuefi_lock); + + if (qcuefi && __qcuefi) { + mutex_unlock(&__qcuefi_lock); + return -EEXIST; + } + + __qcuefi = qcuefi; + + mutex_unlock(&__qcuefi_lock); + return 0; +} + +static struct qcuefi_client *qcuefi_acquire(void) +{ + mutex_lock(&__qcuefi_lock); + return __qcuefi; +} + +static void qcuefi_release(void) +{ + mutex_unlock(&__qcuefi_lock); +} + +static efi_status_t qcuefi_get_variable(efi_char16_t *name, efi_guid_t *vendor, u32 *attr, + unsigned long *data_size, void *data) +{ + struct qcuefi_client *qcuefi; + efi_status_t status; + + qcuefi = qcuefi_acquire(); + if (!qcuefi) + return EFI_NOT_READY; + + status = qsee_uefi_get_variable(qcuefi, name, vendor, attr, data_size, data); + + qcuefi_release(); + return status; +} + +static efi_status_t qcuefi_set_variable(efi_char16_t *name, efi_guid_t *vendor, + u32 attr, unsigned long data_size, void *data) +{ + struct qcuefi_client *qcuefi; + efi_status_t status; + + qcuefi = qcuefi_acquire(); + if (!qcuefi) + return EFI_NOT_READY; + + status = qsee_uefi_set_variable(qcuefi, name, vendor, attr, data_size, data); + + qcuefi_release(); + return status; +} + +static efi_status_t qcuefi_get_next_variable(unsigned long *name_size, efi_char16_t *name, + efi_guid_t *vendor) +{ + struct qcuefi_client *qcuefi; + efi_status_t status; + + qcuefi = qcuefi_acquire(); + if (!qcuefi) + return EFI_NOT_READY; + + status = qsee_uefi_get_next_variable(qcuefi, name_size, name, vendor); + + qcuefi_release(); + return status; +} + +static efi_status_t qcuefi_query_variable_info(u32 attr, u64 *storage_space, u64 *remaining_space, + u64 *max_variable_size) +{ + struct qcuefi_client *qcuefi; + efi_status_t status; + + qcuefi = qcuefi_acquire(); + if (!qcuefi) + return EFI_NOT_READY; + + status = qsee_uefi_query_variable_info(qcuefi, attr, storage_space, remaining_space, + max_variable_size); + + qcuefi_release(); + return status; +} + +static const struct efivar_operations qcom_efivar_ops = { + .get_variable = qcuefi_get_variable, + .set_variable = qcuefi_set_variable, + .get_next_variable = qcuefi_get_next_variable, + .query_variable_info = qcuefi_query_variable_info, +}; + +/* -- Driver setup. --------------------------------------------------------- */ + +static int qcom_uefisecapp_probe(struct auxiliary_device *aux_dev, + const struct auxiliary_device_id *aux_dev_id) +{ + struct qcuefi_client *qcuefi; + int status; + + qcuefi = devm_kzalloc(&aux_dev->dev, sizeof(*qcuefi), GFP_KERNEL); + if (!qcuefi) + return -ENOMEM; + + qcuefi->client = container_of(aux_dev, struct qseecom_client, aux_dev); + + auxiliary_set_drvdata(aux_dev, qcuefi); + status = qcuefi_set_reference(qcuefi); + if (status) + return status; + + status = efivars_register(&qcuefi->efivars, &qcom_efivar_ops); + if (status) + qcuefi_set_reference(NULL); + + return status; +} + +static void qcom_uefisecapp_remove(struct auxiliary_device *aux_dev) +{ + struct qcuefi_client *qcuefi = auxiliary_get_drvdata(aux_dev); + + efivars_unregister(&qcuefi->efivars); + qcuefi_set_reference(NULL); +} + +static const struct auxiliary_device_id qcom_uefisecapp_id_table[] = { + { .name = "qcom_qseecom.uefisecapp" }, + {} +}; +MODULE_DEVICE_TABLE(auxiliary, qcom_uefisecapp_id_table); + +static struct auxiliary_driver qcom_uefisecapp_driver = { + .probe = qcom_uefisecapp_probe, + .remove = qcom_uefisecapp_remove, + .id_table = qcom_uefisecapp_id_table, + .driver = { + .name = "qcom_qseecom_uefisecapp", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; +module_auxiliary_driver(qcom_uefisecapp_driver); + +MODULE_AUTHOR("Maximilian Luz "); +MODULE_DESCRIPTION("Client driver for Qualcomm SEE UEFI Secure App"); +MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/qcom/qcom_scm-legacy.c b/drivers/firmware/qcom/qcom_scm-legacy.c new file mode 100644 index 000000000000..029e6d117cb8 --- /dev/null +++ b/drivers/firmware/qcom/qcom_scm-legacy.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2010,2015,2019 The Linux Foundation. All rights reserved. + * Copyright (C) 2015 Linaro Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qcom_scm.h" + +static DEFINE_MUTEX(qcom_scm_lock); + + +/** + * struct arm_smccc_args + * @args: The array of values used in registers in smc instruction + */ +struct arm_smccc_args { + unsigned long args[8]; +}; + + +/** + * struct scm_legacy_command - one SCM command buffer + * @len: total available memory for command and response + * @buf_offset: start of command buffer + * @resp_hdr_offset: start of response buffer + * @id: command to be executed + * @buf: buffer returned from scm_legacy_get_command_buffer() + * + * An SCM command is laid out in memory as follows: + * + * ------------------- <--- struct scm_legacy_command + * | command header | + * ------------------- <--- scm_legacy_get_command_buffer() + * | command buffer | + * ------------------- <--- struct scm_legacy_response and + * | response header | scm_legacy_command_to_response() + * ------------------- <--- scm_legacy_get_response_buffer() + * | response buffer | + * ------------------- + * + * There can be arbitrary padding between the headers and buffers so + * you should always use the appropriate scm_legacy_get_*_buffer() routines + * to access the buffers in a safe manner. + */ +struct scm_legacy_command { + __le32 len; + __le32 buf_offset; + __le32 resp_hdr_offset; + __le32 id; + __le32 buf[]; +}; + +/** + * struct scm_legacy_response - one SCM response buffer + * @len: total available memory for response + * @buf_offset: start of response data relative to start of scm_legacy_response + * @is_complete: indicates if the command has finished processing + */ +struct scm_legacy_response { + __le32 len; + __le32 buf_offset; + __le32 is_complete; +}; + +/** + * scm_legacy_command_to_response() - Get a pointer to a scm_legacy_response + * @cmd: command + * + * Returns a pointer to a response for a command. + */ +static inline struct scm_legacy_response *scm_legacy_command_to_response( + const struct scm_legacy_command *cmd) +{ + return (void *)cmd + le32_to_cpu(cmd->resp_hdr_offset); +} + +/** + * scm_legacy_get_command_buffer() - Get a pointer to a command buffer + * @cmd: command + * + * Returns a pointer to the command buffer of a command. + */ +static inline void *scm_legacy_get_command_buffer( + const struct scm_legacy_command *cmd) +{ + return (void *)cmd->buf; +} + +/** + * scm_legacy_get_response_buffer() - Get a pointer to a response buffer + * @rsp: response + * + * Returns a pointer to a response buffer of a response. + */ +static inline void *scm_legacy_get_response_buffer( + const struct scm_legacy_response *rsp) +{ + return (void *)rsp + le32_to_cpu(rsp->buf_offset); +} + +static void __scm_legacy_do(const struct arm_smccc_args *smc, + struct arm_smccc_res *res) +{ + do { + arm_smccc_smc(smc->args[0], smc->args[1], smc->args[2], + smc->args[3], smc->args[4], smc->args[5], + smc->args[6], smc->args[7], res); + } while (res->a0 == QCOM_SCM_INTERRUPTED); +} + +/** + * scm_legacy_call() - Sends a command to the SCM and waits for the command to + * finish processing. + * @dev: device + * @desc: descriptor structure containing arguments and return values + * @res: results from SMC call + * + * A note on cache maintenance: + * Note that any buffers that are expected to be accessed by the secure world + * must be flushed before invoking qcom_scm_call and invalidated in the cache + * immediately after qcom_scm_call returns. Cache maintenance on the command + * and response buffers is taken care of by qcom_scm_call; however, callers are + * responsible for any other cached buffers passed over to the secure world. + */ +int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc, + struct qcom_scm_res *res) +{ + u8 arglen = desc->arginfo & 0xf; + int ret = 0, context_id; + unsigned int i; + struct scm_legacy_command *cmd; + struct scm_legacy_response *rsp; + struct arm_smccc_args smc = {0}; + struct arm_smccc_res smc_res; + const size_t cmd_len = arglen * sizeof(__le32); + const size_t resp_len = MAX_QCOM_SCM_RETS * sizeof(__le32); + size_t alloc_len = sizeof(*cmd) + cmd_len + sizeof(*rsp) + resp_len; + dma_addr_t cmd_phys; + __le32 *arg_buf; + const __le32 *res_buf; + + cmd = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->len = cpu_to_le32(alloc_len); + cmd->buf_offset = cpu_to_le32(sizeof(*cmd)); + cmd->resp_hdr_offset = cpu_to_le32(sizeof(*cmd) + cmd_len); + cmd->id = cpu_to_le32(SCM_LEGACY_FNID(desc->svc, desc->cmd)); + + arg_buf = scm_legacy_get_command_buffer(cmd); + for (i = 0; i < arglen; i++) + arg_buf[i] = cpu_to_le32(desc->args[i]); + + rsp = scm_legacy_command_to_response(cmd); + + cmd_phys = dma_map_single(dev, cmd, alloc_len, DMA_TO_DEVICE); + if (dma_mapping_error(dev, cmd_phys)) { + kfree(cmd); + return -ENOMEM; + } + + smc.args[0] = 1; + smc.args[1] = (unsigned long)&context_id; + smc.args[2] = cmd_phys; + + mutex_lock(&qcom_scm_lock); + __scm_legacy_do(&smc, &smc_res); + if (smc_res.a0) + ret = qcom_scm_remap_error(smc_res.a0); + mutex_unlock(&qcom_scm_lock); + if (ret) + goto out; + + do { + dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len, + sizeof(*rsp), DMA_FROM_DEVICE); + } while (!rsp->is_complete); + + dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len + + le32_to_cpu(rsp->buf_offset), + resp_len, DMA_FROM_DEVICE); + + if (res) { + res_buf = scm_legacy_get_response_buffer(rsp); + for (i = 0; i < MAX_QCOM_SCM_RETS; i++) + res->result[i] = le32_to_cpu(res_buf[i]); + } +out: + dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE); + kfree(cmd); + return ret; +} + +#define SCM_LEGACY_ATOMIC_N_REG_ARGS 5 +#define SCM_LEGACY_ATOMIC_FIRST_REG_IDX 2 +#define SCM_LEGACY_CLASS_REGISTER (0x2 << 8) +#define SCM_LEGACY_MASK_IRQS BIT(5) +#define SCM_LEGACY_ATOMIC_ID(svc, cmd, n) \ + ((SCM_LEGACY_FNID(svc, cmd) << 12) | \ + SCM_LEGACY_CLASS_REGISTER | \ + SCM_LEGACY_MASK_IRQS | \ + (n & 0xf)) + +/** + * scm_legacy_call_atomic() - Send an atomic SCM command with up to 5 arguments + * and 3 return values + * @unused: device, legacy argument, not used, can be NULL + * @desc: SCM call descriptor containing arguments + * @res: SCM call return values + * + * This shall only be used with commands that are guaranteed to be + * uninterruptable, atomic and SMP safe. + */ +int scm_legacy_call_atomic(struct device *unused, + const struct qcom_scm_desc *desc, + struct qcom_scm_res *res) +{ + int context_id; + struct arm_smccc_res smc_res; + size_t arglen = desc->arginfo & 0xf; + + BUG_ON(arglen > SCM_LEGACY_ATOMIC_N_REG_ARGS); + + arm_smccc_smc(SCM_LEGACY_ATOMIC_ID(desc->svc, desc->cmd, arglen), + (unsigned long)&context_id, + desc->args[0], desc->args[1], desc->args[2], + desc->args[3], desc->args[4], 0, &smc_res); + + if (res) { + res->result[0] = smc_res.a1; + res->result[1] = smc_res.a2; + res->result[2] = smc_res.a3; + } + + return smc_res.a0; +} diff --git a/drivers/firmware/qcom/qcom_scm-smc.c b/drivers/firmware/qcom/qcom_scm-smc.c new file mode 100644 index 000000000000..16cf88acfa8e --- /dev/null +++ b/drivers/firmware/qcom/qcom_scm-smc.c @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2015,2019 The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qcom_scm.h" + +/** + * struct arm_smccc_args + * @args: The array of values used in registers in smc instruction + */ +struct arm_smccc_args { + unsigned long args[8]; +}; + +static DEFINE_MUTEX(qcom_scm_lock); + +#define QCOM_SCM_EBUSY_WAIT_MS 30 +#define QCOM_SCM_EBUSY_MAX_RETRY 20 + +#define SCM_SMC_N_REG_ARGS 4 +#define SCM_SMC_FIRST_EXT_IDX (SCM_SMC_N_REG_ARGS - 1) +#define SCM_SMC_N_EXT_ARGS (MAX_QCOM_SCM_ARGS - SCM_SMC_N_REG_ARGS + 1) +#define SCM_SMC_FIRST_REG_IDX 2 +#define SCM_SMC_LAST_REG_IDX (SCM_SMC_FIRST_REG_IDX + SCM_SMC_N_REG_ARGS - 1) + +static void __scm_smc_do_quirk(const struct arm_smccc_args *smc, + struct arm_smccc_res *res) +{ + unsigned long a0 = smc->args[0]; + struct arm_smccc_quirk quirk = { .id = ARM_SMCCC_QUIRK_QCOM_A6 }; + + quirk.state.a6 = 0; + + do { + arm_smccc_smc_quirk(a0, smc->args[1], smc->args[2], + smc->args[3], smc->args[4], smc->args[5], + quirk.state.a6, smc->args[7], res, &quirk); + + if (res->a0 == QCOM_SCM_INTERRUPTED) + a0 = res->a0; + + } while (res->a0 == QCOM_SCM_INTERRUPTED); +} + +static void fill_wq_resume_args(struct arm_smccc_args *resume, u32 smc_call_ctx) +{ + memset(resume->args, 0, sizeof(resume->args[0]) * ARRAY_SIZE(resume->args)); + + resume->args[0] = ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, + ARM_SMCCC_SMC_64, ARM_SMCCC_OWNER_SIP, + SCM_SMC_FNID(QCOM_SCM_SVC_WAITQ, QCOM_SCM_WAITQ_RESUME)); + + resume->args[1] = QCOM_SCM_ARGS(1); + + resume->args[2] = smc_call_ctx; +} + +int scm_get_wq_ctx(u32 *wq_ctx, u32 *flags, u32 *more_pending) +{ + int ret; + struct arm_smccc_res get_wq_res; + struct arm_smccc_args get_wq_ctx = {0}; + + get_wq_ctx.args[0] = ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, + ARM_SMCCC_SMC_64, ARM_SMCCC_OWNER_SIP, + SCM_SMC_FNID(QCOM_SCM_SVC_WAITQ, QCOM_SCM_WAITQ_GET_WQ_CTX)); + + /* Guaranteed to return only success or error, no WAITQ_* */ + __scm_smc_do_quirk(&get_wq_ctx, &get_wq_res); + ret = get_wq_res.a0; + if (ret) + return ret; + + *wq_ctx = get_wq_res.a1; + *flags = get_wq_res.a2; + *more_pending = get_wq_res.a3; + + return 0; +} + +static int __scm_smc_do_quirk_handle_waitq(struct device *dev, struct arm_smccc_args *waitq, + struct arm_smccc_res *res) +{ + int ret; + u32 wq_ctx, smc_call_ctx; + struct arm_smccc_args resume; + struct arm_smccc_args *smc = waitq; + + do { + __scm_smc_do_quirk(smc, res); + + if (res->a0 == QCOM_SCM_WAITQ_SLEEP) { + wq_ctx = res->a1; + smc_call_ctx = res->a2; + + ret = qcom_scm_wait_for_wq_completion(wq_ctx); + if (ret) + return ret; + + fill_wq_resume_args(&resume, smc_call_ctx); + smc = &resume; + } + } while (res->a0 == QCOM_SCM_WAITQ_SLEEP); + + return 0; +} + +static int __scm_smc_do(struct device *dev, struct arm_smccc_args *smc, + struct arm_smccc_res *res, bool atomic) +{ + int ret, retry_count = 0; + + if (atomic) { + __scm_smc_do_quirk(smc, res); + return 0; + } + + do { + mutex_lock(&qcom_scm_lock); + + ret = __scm_smc_do_quirk_handle_waitq(dev, smc, res); + + mutex_unlock(&qcom_scm_lock); + + if (ret) + return ret; + + if (res->a0 == QCOM_SCM_V2_EBUSY) { + if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY) + break; + msleep(QCOM_SCM_EBUSY_WAIT_MS); + } + } while (res->a0 == QCOM_SCM_V2_EBUSY); + + return 0; +} + + +int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc, + enum qcom_scm_convention qcom_convention, + struct qcom_scm_res *res, bool atomic) +{ + int arglen = desc->arginfo & 0xf; + int i, ret; + dma_addr_t args_phys = 0; + void *args_virt = NULL; + size_t alloc_len; + gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL; + u32 smccc_call_type = atomic ? ARM_SMCCC_FAST_CALL : ARM_SMCCC_STD_CALL; + u32 qcom_smccc_convention = (qcom_convention == SMC_CONVENTION_ARM_32) ? + ARM_SMCCC_SMC_32 : ARM_SMCCC_SMC_64; + struct arm_smccc_res smc_res; + struct arm_smccc_args smc = {0}; + + smc.args[0] = ARM_SMCCC_CALL_VAL( + smccc_call_type, + qcom_smccc_convention, + desc->owner, + SCM_SMC_FNID(desc->svc, desc->cmd)); + smc.args[1] = desc->arginfo; + for (i = 0; i < SCM_SMC_N_REG_ARGS; i++) + smc.args[i + SCM_SMC_FIRST_REG_IDX] = desc->args[i]; + + if (unlikely(arglen > SCM_SMC_N_REG_ARGS)) { + alloc_len = SCM_SMC_N_EXT_ARGS * sizeof(u64); + args_virt = kzalloc(PAGE_ALIGN(alloc_len), flag); + + if (!args_virt) + return -ENOMEM; + + if (qcom_smccc_convention == ARM_SMCCC_SMC_32) { + __le32 *args = args_virt; + + for (i = 0; i < SCM_SMC_N_EXT_ARGS; i++) + args[i] = cpu_to_le32(desc->args[i + + SCM_SMC_FIRST_EXT_IDX]); + } else { + __le64 *args = args_virt; + + for (i = 0; i < SCM_SMC_N_EXT_ARGS; i++) + args[i] = cpu_to_le64(desc->args[i + + SCM_SMC_FIRST_EXT_IDX]); + } + + args_phys = dma_map_single(dev, args_virt, alloc_len, + DMA_TO_DEVICE); + + if (dma_mapping_error(dev, args_phys)) { + kfree(args_virt); + return -ENOMEM; + } + + smc.args[SCM_SMC_LAST_REG_IDX] = args_phys; + } + + /* ret error check follows after args_virt cleanup*/ + ret = __scm_smc_do(dev, &smc, &smc_res, atomic); + + if (args_virt) { + dma_unmap_single(dev, args_phys, alloc_len, DMA_TO_DEVICE); + kfree(args_virt); + } + + if (ret) + return ret; + + if (res) { + res->result[0] = smc_res.a1; + res->result[1] = smc_res.a2; + res->result[2] = smc_res.a3; + } + + return (long)smc_res.a0 ? qcom_scm_remap_error(smc_res.a0) : 0; + +} diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c new file mode 100644 index 000000000000..520de9b5633a --- /dev/null +++ b/drivers/firmware/qcom/qcom_scm.c @@ -0,0 +1,1943 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2010,2015,2019 The Linux Foundation. All rights reserved. + * Copyright (C) 2015 Linaro Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qcom_scm.h" + +static bool download_mode = IS_ENABLED(CONFIG_QCOM_SCM_DOWNLOAD_MODE_DEFAULT); +module_param(download_mode, bool, 0); + +struct qcom_scm { + struct device *dev; + struct clk *core_clk; + struct clk *iface_clk; + struct clk *bus_clk; + struct icc_path *path; + struct completion waitq_comp; + struct reset_controller_dev reset; + + /* control access to the interconnect path */ + struct mutex scm_bw_lock; + int scm_vote_count; + + u64 dload_mode_addr; +}; + +struct qcom_scm_current_perm_info { + __le32 vmid; + __le32 perm; + __le64 ctx; + __le32 ctx_size; + __le32 unused; +}; + +struct qcom_scm_mem_map_info { + __le64 mem_addr; + __le64 mem_size; +}; + +/** + * struct qcom_scm_qseecom_resp - QSEECOM SCM call response. + * @result: Result or status of the SCM call. See &enum qcom_scm_qseecom_result. + * @resp_type: Type of the response. See &enum qcom_scm_qseecom_resp_type. + * @data: Response data. The type of this data is given in @resp_type. + */ +struct qcom_scm_qseecom_resp { + u64 result; + u64 resp_type; + u64 data; +}; + +enum qcom_scm_qseecom_result { + QSEECOM_RESULT_SUCCESS = 0, + QSEECOM_RESULT_INCOMPLETE = 1, + QSEECOM_RESULT_BLOCKED_ON_LISTENER = 2, + QSEECOM_RESULT_FAILURE = 0xFFFFFFFF, +}; + +enum qcom_scm_qseecom_resp_type { + QSEECOM_SCM_RES_APP_ID = 0xEE01, + QSEECOM_SCM_RES_QSEOS_LISTENER_ID = 0xEE02, +}; + +enum qcom_scm_qseecom_tz_owner { + QSEECOM_TZ_OWNER_SIP = 2, + QSEECOM_TZ_OWNER_TZ_APPS = 48, + QSEECOM_TZ_OWNER_QSEE_OS = 50 +}; + +enum qcom_scm_qseecom_tz_svc { + QSEECOM_TZ_SVC_APP_ID_PLACEHOLDER = 0, + QSEECOM_TZ_SVC_APP_MGR = 1, + QSEECOM_TZ_SVC_INFO = 6, +}; + +enum qcom_scm_qseecom_tz_cmd_app { + QSEECOM_TZ_CMD_APP_SEND = 1, + QSEECOM_TZ_CMD_APP_LOOKUP = 3, +}; + +enum qcom_scm_qseecom_tz_cmd_info { + QSEECOM_TZ_CMD_INFO_VERSION = 3, +}; + +#define QSEECOM_MAX_APP_NAME_SIZE 64 + +/* Each bit configures cold/warm boot address for one of the 4 CPUs */ +static const u8 qcom_scm_cpu_cold_bits[QCOM_SCM_BOOT_MAX_CPUS] = { + 0, BIT(0), BIT(3), BIT(5) +}; +static const u8 qcom_scm_cpu_warm_bits[QCOM_SCM_BOOT_MAX_CPUS] = { + BIT(2), BIT(1), BIT(4), BIT(6) +}; + +#define QCOM_SMC_WAITQ_FLAG_WAKE_ONE BIT(0) +#define QCOM_SMC_WAITQ_FLAG_WAKE_ALL BIT(1) + +static const char * const qcom_scm_convention_names[] = { + [SMC_CONVENTION_UNKNOWN] = "unknown", + [SMC_CONVENTION_ARM_32] = "smc arm 32", + [SMC_CONVENTION_ARM_64] = "smc arm 64", + [SMC_CONVENTION_LEGACY] = "smc legacy", +}; + +static struct qcom_scm *__scm; + +static int qcom_scm_clk_enable(void) +{ + int ret; + + ret = clk_prepare_enable(__scm->core_clk); + if (ret) + goto bail; + + ret = clk_prepare_enable(__scm->iface_clk); + if (ret) + goto disable_core; + + ret = clk_prepare_enable(__scm->bus_clk); + if (ret) + goto disable_iface; + + return 0; + +disable_iface: + clk_disable_unprepare(__scm->iface_clk); +disable_core: + clk_disable_unprepare(__scm->core_clk); +bail: + return ret; +} + +static void qcom_scm_clk_disable(void) +{ + clk_disable_unprepare(__scm->core_clk); + clk_disable_unprepare(__scm->iface_clk); + clk_disable_unprepare(__scm->bus_clk); +} + +static int qcom_scm_bw_enable(void) +{ + int ret = 0; + + if (!__scm->path) + return 0; + + if (IS_ERR(__scm->path)) + return -EINVAL; + + mutex_lock(&__scm->scm_bw_lock); + if (!__scm->scm_vote_count) { + ret = icc_set_bw(__scm->path, 0, UINT_MAX); + if (ret < 0) { + dev_err(__scm->dev, "failed to set bandwidth request\n"); + goto err_bw; + } + } + __scm->scm_vote_count++; +err_bw: + mutex_unlock(&__scm->scm_bw_lock); + + return ret; +} + +static void qcom_scm_bw_disable(void) +{ + if (IS_ERR_OR_NULL(__scm->path)) + return; + + mutex_lock(&__scm->scm_bw_lock); + if (__scm->scm_vote_count-- == 1) + icc_set_bw(__scm->path, 0, 0); + mutex_unlock(&__scm->scm_bw_lock); +} + +enum qcom_scm_convention qcom_scm_convention = SMC_CONVENTION_UNKNOWN; +static DEFINE_SPINLOCK(scm_query_lock); + +static enum qcom_scm_convention __get_convention(void) +{ + unsigned long flags; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_INFO, + .cmd = QCOM_SCM_INFO_IS_CALL_AVAIL, + .args[0] = SCM_SMC_FNID(QCOM_SCM_SVC_INFO, + QCOM_SCM_INFO_IS_CALL_AVAIL) | + (ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT), + .arginfo = QCOM_SCM_ARGS(1), + .owner = ARM_SMCCC_OWNER_SIP, + }; + struct qcom_scm_res res; + enum qcom_scm_convention probed_convention; + int ret; + bool forced = false; + + if (likely(qcom_scm_convention != SMC_CONVENTION_UNKNOWN)) + return qcom_scm_convention; + + /* + * Per the "SMC calling convention specification", the 64-bit calling + * convention can only be used when the client is 64-bit, otherwise + * system will encounter the undefined behaviour. + */ +#if IS_ENABLED(CONFIG_ARM64) + /* + * Device isn't required as there is only one argument - no device + * needed to dma_map_single to secure world + */ + probed_convention = SMC_CONVENTION_ARM_64; + ret = __scm_smc_call(NULL, &desc, probed_convention, &res, true); + if (!ret && res.result[0] == 1) + goto found; + + /* + * Some SC7180 firmwares didn't implement the + * QCOM_SCM_INFO_IS_CALL_AVAIL call, so we fallback to forcing ARM_64 + * calling conventions on these firmwares. Luckily we don't make any + * early calls into the firmware on these SoCs so the device pointer + * will be valid here to check if the compatible matches. + */ + if (of_device_is_compatible(__scm ? __scm->dev->of_node : NULL, "qcom,scm-sc7180")) { + forced = true; + goto found; + } +#endif + + probed_convention = SMC_CONVENTION_ARM_32; + ret = __scm_smc_call(NULL, &desc, probed_convention, &res, true); + if (!ret && res.result[0] == 1) + goto found; + + probed_convention = SMC_CONVENTION_LEGACY; +found: + spin_lock_irqsave(&scm_query_lock, flags); + if (probed_convention != qcom_scm_convention) { + qcom_scm_convention = probed_convention; + pr_info("qcom_scm: convention: %s%s\n", + qcom_scm_convention_names[qcom_scm_convention], + forced ? " (forced)" : ""); + } + spin_unlock_irqrestore(&scm_query_lock, flags); + + return qcom_scm_convention; +} + +/** + * qcom_scm_call() - Invoke a syscall in the secure world + * @dev: device + * @desc: Descriptor structure containing arguments and return values + * @res: Structure containing results from SMC/HVC call + * + * Sends a command to the SCM and waits for the command to finish processing. + * This should *only* be called in pre-emptible context. + */ +static int qcom_scm_call(struct device *dev, const struct qcom_scm_desc *desc, + struct qcom_scm_res *res) +{ + might_sleep(); + switch (__get_convention()) { + case SMC_CONVENTION_ARM_32: + case SMC_CONVENTION_ARM_64: + return scm_smc_call(dev, desc, res, false); + case SMC_CONVENTION_LEGACY: + return scm_legacy_call(dev, desc, res); + default: + pr_err("Unknown current SCM calling convention.\n"); + return -EINVAL; + } +} + +/** + * qcom_scm_call_atomic() - atomic variation of qcom_scm_call() + * @dev: device + * @desc: Descriptor structure containing arguments and return values + * @res: Structure containing results from SMC/HVC call + * + * Sends a command to the SCM and waits for the command to finish processing. + * This can be called in atomic context. + */ +static int qcom_scm_call_atomic(struct device *dev, + const struct qcom_scm_desc *desc, + struct qcom_scm_res *res) +{ + switch (__get_convention()) { + case SMC_CONVENTION_ARM_32: + case SMC_CONVENTION_ARM_64: + return scm_smc_call(dev, desc, res, true); + case SMC_CONVENTION_LEGACY: + return scm_legacy_call_atomic(dev, desc, res); + default: + pr_err("Unknown current SCM calling convention.\n"); + return -EINVAL; + } +} + +static bool __qcom_scm_is_call_available(struct device *dev, u32 svc_id, + u32 cmd_id) +{ + int ret; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_INFO, + .cmd = QCOM_SCM_INFO_IS_CALL_AVAIL, + .owner = ARM_SMCCC_OWNER_SIP, + }; + struct qcom_scm_res res; + + desc.arginfo = QCOM_SCM_ARGS(1); + switch (__get_convention()) { + case SMC_CONVENTION_ARM_32: + case SMC_CONVENTION_ARM_64: + desc.args[0] = SCM_SMC_FNID(svc_id, cmd_id) | + (ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT); + break; + case SMC_CONVENTION_LEGACY: + desc.args[0] = SCM_LEGACY_FNID(svc_id, cmd_id); + break; + default: + pr_err("Unknown SMC convention being used\n"); + return false; + } + + ret = qcom_scm_call(dev, &desc, &res); + + return ret ? false : !!res.result[0]; +} + +static int qcom_scm_set_boot_addr(void *entry, const u8 *cpu_bits) +{ + int cpu; + unsigned int flags = 0; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_BOOT, + .cmd = QCOM_SCM_BOOT_SET_ADDR, + .arginfo = QCOM_SCM_ARGS(2), + .owner = ARM_SMCCC_OWNER_SIP, + }; + + for_each_present_cpu(cpu) { + if (cpu >= QCOM_SCM_BOOT_MAX_CPUS) + return -EINVAL; + flags |= cpu_bits[cpu]; + } + + desc.args[0] = flags; + desc.args[1] = virt_to_phys(entry); + + return qcom_scm_call_atomic(__scm ? __scm->dev : NULL, &desc, NULL); +} + +static int qcom_scm_set_boot_addr_mc(void *entry, unsigned int flags) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_BOOT, + .cmd = QCOM_SCM_BOOT_SET_ADDR_MC, + .owner = ARM_SMCCC_OWNER_SIP, + .arginfo = QCOM_SCM_ARGS(6), + .args = { + virt_to_phys(entry), + /* Apply to all CPUs in all affinity levels */ + ~0ULL, ~0ULL, ~0ULL, ~0ULL, + flags, + }, + }; + + /* Need a device for DMA of the additional arguments */ + if (!__scm || __get_convention() == SMC_CONVENTION_LEGACY) + return -EOPNOTSUPP; + + return qcom_scm_call(__scm->dev, &desc, NULL); +} + +/** + * qcom_scm_set_warm_boot_addr() - Set the warm boot address for all cpus + * @entry: Entry point function for the cpus + * + * Set the Linux entry point for the SCM to transfer control to when coming + * out of a power down. CPU power down may be executed on cpuidle or hotplug. + */ +int qcom_scm_set_warm_boot_addr(void *entry) +{ + if (qcom_scm_set_boot_addr_mc(entry, QCOM_SCM_BOOT_MC_FLAG_WARMBOOT)) + /* Fallback to old SCM call */ + return qcom_scm_set_boot_addr(entry, qcom_scm_cpu_warm_bits); + return 0; +} +EXPORT_SYMBOL_GPL(qcom_scm_set_warm_boot_addr); + +/** + * qcom_scm_set_cold_boot_addr() - Set the cold boot address for all cpus + * @entry: Entry point function for the cpus + */ +int qcom_scm_set_cold_boot_addr(void *entry) +{ + if (qcom_scm_set_boot_addr_mc(entry, QCOM_SCM_BOOT_MC_FLAG_COLDBOOT)) + /* Fallback to old SCM call */ + return qcom_scm_set_boot_addr(entry, qcom_scm_cpu_cold_bits); + return 0; +} +EXPORT_SYMBOL_GPL(qcom_scm_set_cold_boot_addr); + +/** + * qcom_scm_cpu_power_down() - Power down the cpu + * @flags: Flags to flush cache + * + * This is an end point to power down cpu. If there was a pending interrupt, + * the control would return from this function, otherwise, the cpu jumps to the + * warm boot entry point set for this cpu upon reset. + */ +void qcom_scm_cpu_power_down(u32 flags) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_BOOT, + .cmd = QCOM_SCM_BOOT_TERMINATE_PC, + .args[0] = flags & QCOM_SCM_FLUSH_FLAG_MASK, + .arginfo = QCOM_SCM_ARGS(1), + .owner = ARM_SMCCC_OWNER_SIP, + }; + + qcom_scm_call_atomic(__scm ? __scm->dev : NULL, &desc, NULL); +} +EXPORT_SYMBOL_GPL(qcom_scm_cpu_power_down); + +int qcom_scm_set_remote_state(u32 state, u32 id) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_BOOT, + .cmd = QCOM_SCM_BOOT_SET_REMOTE_STATE, + .arginfo = QCOM_SCM_ARGS(2), + .args[0] = state, + .args[1] = id, + .owner = ARM_SMCCC_OWNER_SIP, + }; + struct qcom_scm_res res; + int ret; + + ret = qcom_scm_call(__scm->dev, &desc, &res); + + return ret ? : res.result[0]; +} +EXPORT_SYMBOL_GPL(qcom_scm_set_remote_state); + +static int qcom_scm_disable_sdi(void) +{ + int ret; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_BOOT, + .cmd = QCOM_SCM_BOOT_SDI_CONFIG, + .args[0] = 1, /* Disable watchdog debug */ + .args[1] = 0, /* Disable SDI */ + .arginfo = QCOM_SCM_ARGS(2), + .owner = ARM_SMCCC_OWNER_SIP, + }; + struct qcom_scm_res res; + + ret = qcom_scm_clk_enable(); + if (ret) + return ret; + ret = qcom_scm_call(__scm->dev, &desc, &res); + + qcom_scm_clk_disable(); + + return ret ? : res.result[0]; +} + +static int __qcom_scm_set_dload_mode(struct device *dev, bool enable) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_BOOT, + .cmd = QCOM_SCM_BOOT_SET_DLOAD_MODE, + .arginfo = QCOM_SCM_ARGS(2), + .args[0] = QCOM_SCM_BOOT_SET_DLOAD_MODE, + .owner = ARM_SMCCC_OWNER_SIP, + }; + + desc.args[1] = enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0; + + return qcom_scm_call_atomic(__scm->dev, &desc, NULL); +} + +static void qcom_scm_set_download_mode(bool enable) +{ + bool avail; + int ret = 0; + + avail = __qcom_scm_is_call_available(__scm->dev, + QCOM_SCM_SVC_BOOT, + QCOM_SCM_BOOT_SET_DLOAD_MODE); + if (avail) { + ret = __qcom_scm_set_dload_mode(__scm->dev, enable); + } else if (__scm->dload_mode_addr) { + ret = qcom_scm_io_writel(__scm->dload_mode_addr, + enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0); + } else { + dev_err(__scm->dev, + "No available mechanism for setting download mode\n"); + } + + if (ret) + dev_err(__scm->dev, "failed to set download mode: %d\n", ret); +} + +/** + * qcom_scm_pas_init_image() - Initialize peripheral authentication service + * state machine for a given peripheral, using the + * metadata + * @peripheral: peripheral id + * @metadata: pointer to memory containing ELF header, program header table + * and optional blob of data used for authenticating the metadata + * and the rest of the firmware + * @size: size of the metadata + * @ctx: optional metadata context + * + * Return: 0 on success. + * + * Upon successful return, the PAS metadata context (@ctx) will be used to + * track the metadata allocation, this needs to be released by invoking + * qcom_scm_pas_metadata_release() by the caller. + */ +int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size, + struct qcom_scm_pas_metadata *ctx) +{ + dma_addr_t mdata_phys; + void *mdata_buf; + int ret; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_PIL, + .cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE, + .arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW), + .args[0] = peripheral, + .owner = ARM_SMCCC_OWNER_SIP, + }; + struct qcom_scm_res res; + + /* + * During the scm call memory protection will be enabled for the meta + * data blob, so make sure it's physically contiguous, 4K aligned and + * non-cachable to avoid XPU violations. + */ + mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys, + GFP_KERNEL); + if (!mdata_buf) { + dev_err(__scm->dev, "Allocation of metadata buffer failed.\n"); + return -ENOMEM; + } + memcpy(mdata_buf, metadata, size); + + ret = qcom_scm_clk_enable(); + if (ret) + goto out; + + ret = qcom_scm_bw_enable(); + if (ret) + return ret; + + desc.args[1] = mdata_phys; + + ret = qcom_scm_call(__scm->dev, &desc, &res); + + qcom_scm_bw_disable(); + qcom_scm_clk_disable(); + +out: + if (ret < 0 || !ctx) { + dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys); + } else if (ctx) { + ctx->ptr = mdata_buf; + ctx->phys = mdata_phys; + ctx->size = size; + } + + return ret ? : res.result[0]; +} +EXPORT_SYMBOL_GPL(qcom_scm_pas_init_image); + +/** + * qcom_scm_pas_metadata_release() - release metadata context + * @ctx: metadata context + */ +void qcom_scm_pas_metadata_release(struct qcom_scm_pas_metadata *ctx) +{ + if (!ctx->ptr) + return; + + dma_free_coherent(__scm->dev, ctx->size, ctx->ptr, ctx->phys); + + ctx->ptr = NULL; + ctx->phys = 0; + ctx->size = 0; +} +EXPORT_SYMBOL_GPL(qcom_scm_pas_metadata_release); + +/** + * qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral + * for firmware loading + * @peripheral: peripheral id + * @addr: start address of memory area to prepare + * @size: size of the memory area to prepare + * + * Returns 0 on success. + */ +int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size) +{ + int ret; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_PIL, + .cmd = QCOM_SCM_PIL_PAS_MEM_SETUP, + .arginfo = QCOM_SCM_ARGS(3), + .args[0] = peripheral, + .args[1] = addr, + .args[2] = size, + .owner = ARM_SMCCC_OWNER_SIP, + }; + struct qcom_scm_res res; + + ret = qcom_scm_clk_enable(); + if (ret) + return ret; + + ret = qcom_scm_bw_enable(); + if (ret) + return ret; + + ret = qcom_scm_call(__scm->dev, &desc, &res); + qcom_scm_bw_disable(); + qcom_scm_clk_disable(); + + return ret ? : res.result[0]; +} +EXPORT_SYMBOL_GPL(qcom_scm_pas_mem_setup); + +/** + * qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware + * and reset the remote processor + * @peripheral: peripheral id + * + * Return 0 on success. + */ +int qcom_scm_pas_auth_and_reset(u32 peripheral) +{ + int ret; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_PIL, + .cmd = QCOM_SCM_PIL_PAS_AUTH_AND_RESET, + .arginfo = QCOM_SCM_ARGS(1), + .args[0] = peripheral, + .owner = ARM_SMCCC_OWNER_SIP, + }; + struct qcom_scm_res res; + + ret = qcom_scm_clk_enable(); + if (ret) + return ret; + + ret = qcom_scm_bw_enable(); + if (ret) + return ret; + + ret = qcom_scm_call(__scm->dev, &desc, &res); + qcom_scm_bw_disable(); + qcom_scm_clk_disable(); + + return ret ? : res.result[0]; +} +EXPORT_SYMBOL_GPL(qcom_scm_pas_auth_and_reset); + +/** + * qcom_scm_pas_shutdown() - Shut down the remote processor + * @peripheral: peripheral id + * + * Returns 0 on success. + */ +int qcom_scm_pas_shutdown(u32 peripheral) +{ + int ret; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_PIL, + .cmd = QCOM_SCM_PIL_PAS_SHUTDOWN, + .arginfo = QCOM_SCM_ARGS(1), + .args[0] = peripheral, + .owner = ARM_SMCCC_OWNER_SIP, + }; + struct qcom_scm_res res; + + ret = qcom_scm_clk_enable(); + if (ret) + return ret; + + ret = qcom_scm_bw_enable(); + if (ret) + return ret; + + ret = qcom_scm_call(__scm->dev, &desc, &res); + + qcom_scm_bw_disable(); + qcom_scm_clk_disable(); + + return ret ? : res.result[0]; +} +EXPORT_SYMBOL_GPL(qcom_scm_pas_shutdown); + +/** + * qcom_scm_pas_supported() - Check if the peripheral authentication service is + * available for the given peripherial + * @peripheral: peripheral id + * + * Returns true if PAS is supported for this peripheral, otherwise false. + */ +bool qcom_scm_pas_supported(u32 peripheral) +{ + int ret; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_PIL, + .cmd = QCOM_SCM_PIL_PAS_IS_SUPPORTED, + .arginfo = QCOM_SCM_ARGS(1), + .args[0] = peripheral, + .owner = ARM_SMCCC_OWNER_SIP, + }; + struct qcom_scm_res res; + + if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL, + QCOM_SCM_PIL_PAS_IS_SUPPORTED)) + return false; + + ret = qcom_scm_call(__scm->dev, &desc, &res); + + return ret ? false : !!res.result[0]; +} +EXPORT_SYMBOL_GPL(qcom_scm_pas_supported); + +static int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_PIL, + .cmd = QCOM_SCM_PIL_PAS_MSS_RESET, + .arginfo = QCOM_SCM_ARGS(2), + .args[0] = reset, + .args[1] = 0, + .owner = ARM_SMCCC_OWNER_SIP, + }; + struct qcom_scm_res res; + int ret; + + ret = qcom_scm_call(__scm->dev, &desc, &res); + + return ret ? : res.result[0]; +} + +static int qcom_scm_pas_reset_assert(struct reset_controller_dev *rcdev, + unsigned long idx) +{ + if (idx != 0) + return -EINVAL; + + return __qcom_scm_pas_mss_reset(__scm->dev, 1); +} + +static int qcom_scm_pas_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long idx) +{ + if (idx != 0) + return -EINVAL; + + return __qcom_scm_pas_mss_reset(__scm->dev, 0); +} + +static const struct reset_control_ops qcom_scm_pas_reset_ops = { + .assert = qcom_scm_pas_reset_assert, + .deassert = qcom_scm_pas_reset_deassert, +}; + +int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_IO, + .cmd = QCOM_SCM_IO_READ, + .arginfo = QCOM_SCM_ARGS(1), + .args[0] = addr, + .owner = ARM_SMCCC_OWNER_SIP, + }; + struct qcom_scm_res res; + int ret; + + + ret = qcom_scm_call_atomic(__scm->dev, &desc, &res); + if (ret >= 0) + *val = res.result[0]; + + return ret < 0 ? ret : 0; +} +EXPORT_SYMBOL_GPL(qcom_scm_io_readl); + +int qcom_scm_io_writel(phys_addr_t addr, unsigned int val) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_IO, + .cmd = QCOM_SCM_IO_WRITE, + .arginfo = QCOM_SCM_ARGS(2), + .args[0] = addr, + .args[1] = val, + .owner = ARM_SMCCC_OWNER_SIP, + }; + + return qcom_scm_call_atomic(__scm->dev, &desc, NULL); +} +EXPORT_SYMBOL_GPL(qcom_scm_io_writel); + +/** + * qcom_scm_restore_sec_cfg_available() - Check if secure environment + * supports restore security config interface. + * + * Return true if restore-cfg interface is supported, false if not. + */ +bool qcom_scm_restore_sec_cfg_available(void) +{ + return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_MP, + QCOM_SCM_MP_RESTORE_SEC_CFG); +} +EXPORT_SYMBOL_GPL(qcom_scm_restore_sec_cfg_available); + +int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_MP, + .cmd = QCOM_SCM_MP_RESTORE_SEC_CFG, + .arginfo = QCOM_SCM_ARGS(2), + .args[0] = device_id, + .args[1] = spare, + .owner = ARM_SMCCC_OWNER_SIP, + }; + struct qcom_scm_res res; + int ret; + + ret = qcom_scm_call(__scm->dev, &desc, &res); + + return ret ? : res.result[0]; +} +EXPORT_SYMBOL_GPL(qcom_scm_restore_sec_cfg); + +int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_MP, + .cmd = QCOM_SCM_MP_IOMMU_SECURE_PTBL_SIZE, + .arginfo = QCOM_SCM_ARGS(1), + .args[0] = spare, + .owner = ARM_SMCCC_OWNER_SIP, + }; + struct qcom_scm_res res; + int ret; + + ret = qcom_scm_call(__scm->dev, &desc, &res); + + if (size) + *size = res.result[0]; + + return ret ? : res.result[1]; +} +EXPORT_SYMBOL_GPL(qcom_scm_iommu_secure_ptbl_size); + +int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_MP, + .cmd = QCOM_SCM_MP_IOMMU_SECURE_PTBL_INIT, + .arginfo = QCOM_SCM_ARGS(3, QCOM_SCM_RW, QCOM_SCM_VAL, + QCOM_SCM_VAL), + .args[0] = addr, + .args[1] = size, + .args[2] = spare, + .owner = ARM_SMCCC_OWNER_SIP, + }; + int ret; + + ret = qcom_scm_call(__scm->dev, &desc, NULL); + + /* the pg table has been initialized already, ignore the error */ + if (ret == -EPERM) + ret = 0; + + return ret; +} +EXPORT_SYMBOL_GPL(qcom_scm_iommu_secure_ptbl_init); + +int qcom_scm_iommu_set_cp_pool_size(u32 spare, u32 size) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_MP, + .cmd = QCOM_SCM_MP_IOMMU_SET_CP_POOL_SIZE, + .arginfo = QCOM_SCM_ARGS(2), + .args[0] = size, + .args[1] = spare, + .owner = ARM_SMCCC_OWNER_SIP, + }; + + return qcom_scm_call(__scm->dev, &desc, NULL); +} +EXPORT_SYMBOL_GPL(qcom_scm_iommu_set_cp_pool_size); + +int qcom_scm_mem_protect_video_var(u32 cp_start, u32 cp_size, + u32 cp_nonpixel_start, + u32 cp_nonpixel_size) +{ + int ret; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_MP, + .cmd = QCOM_SCM_MP_VIDEO_VAR, + .arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_VAL, QCOM_SCM_VAL, + QCOM_SCM_VAL, QCOM_SCM_VAL), + .args[0] = cp_start, + .args[1] = cp_size, + .args[2] = cp_nonpixel_start, + .args[3] = cp_nonpixel_size, + .owner = ARM_SMCCC_OWNER_SIP, + }; + struct qcom_scm_res res; + + ret = qcom_scm_call(__scm->dev, &desc, &res); + + return ret ? : res.result[0]; +} +EXPORT_SYMBOL_GPL(qcom_scm_mem_protect_video_var); + +static int __qcom_scm_assign_mem(struct device *dev, phys_addr_t mem_region, + size_t mem_sz, phys_addr_t src, size_t src_sz, + phys_addr_t dest, size_t dest_sz) +{ + int ret; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_MP, + .cmd = QCOM_SCM_MP_ASSIGN, + .arginfo = QCOM_SCM_ARGS(7, QCOM_SCM_RO, QCOM_SCM_VAL, + QCOM_SCM_RO, QCOM_SCM_VAL, QCOM_SCM_RO, + QCOM_SCM_VAL, QCOM_SCM_VAL), + .args[0] = mem_region, + .args[1] = mem_sz, + .args[2] = src, + .args[3] = src_sz, + .args[4] = dest, + .args[5] = dest_sz, + .args[6] = 0, + .owner = ARM_SMCCC_OWNER_SIP, + }; + struct qcom_scm_res res; + + ret = qcom_scm_call(dev, &desc, &res); + + return ret ? : res.result[0]; +} + +/** + * qcom_scm_assign_mem() - Make a secure call to reassign memory ownership + * @mem_addr: mem region whose ownership need to be reassigned + * @mem_sz: size of the region. + * @srcvm: vmid for current set of owners, each set bit in + * flag indicate a unique owner + * @newvm: array having new owners and corresponding permission + * flags + * @dest_cnt: number of owners in next set. + * + * Return negative errno on failure or 0 on success with @srcvm updated. + */ +int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, + u64 *srcvm, + const struct qcom_scm_vmperm *newvm, + unsigned int dest_cnt) +{ + struct qcom_scm_current_perm_info *destvm; + struct qcom_scm_mem_map_info *mem_to_map; + phys_addr_t mem_to_map_phys; + phys_addr_t dest_phys; + dma_addr_t ptr_phys; + size_t mem_to_map_sz; + size_t dest_sz; + size_t src_sz; + size_t ptr_sz; + int next_vm; + __le32 *src; + void *ptr; + int ret, i, b; + u64 srcvm_bits = *srcvm; + + src_sz = hweight64(srcvm_bits) * sizeof(*src); + mem_to_map_sz = sizeof(*mem_to_map); + dest_sz = dest_cnt * sizeof(*destvm); + ptr_sz = ALIGN(src_sz, SZ_64) + ALIGN(mem_to_map_sz, SZ_64) + + ALIGN(dest_sz, SZ_64); + + ptr = dma_alloc_coherent(__scm->dev, ptr_sz, &ptr_phys, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + /* Fill source vmid detail */ + src = ptr; + i = 0; + for (b = 0; b < BITS_PER_TYPE(u64); b++) { + if (srcvm_bits & BIT(b)) + src[i++] = cpu_to_le32(b); + } + + /* Fill details of mem buff to map */ + mem_to_map = ptr + ALIGN(src_sz, SZ_64); + mem_to_map_phys = ptr_phys + ALIGN(src_sz, SZ_64); + mem_to_map->mem_addr = cpu_to_le64(mem_addr); + mem_to_map->mem_size = cpu_to_le64(mem_sz); + + next_vm = 0; + /* Fill details of next vmid detail */ + destvm = ptr + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(src_sz, SZ_64); + dest_phys = ptr_phys + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(src_sz, SZ_64); + for (i = 0; i < dest_cnt; i++, destvm++, newvm++) { + destvm->vmid = cpu_to_le32(newvm->vmid); + destvm->perm = cpu_to_le32(newvm->perm); + destvm->ctx = 0; + destvm->ctx_size = 0; + next_vm |= BIT(newvm->vmid); + } + + ret = __qcom_scm_assign_mem(__scm->dev, mem_to_map_phys, mem_to_map_sz, + ptr_phys, src_sz, dest_phys, dest_sz); + dma_free_coherent(__scm->dev, ptr_sz, ptr, ptr_phys); + if (ret) { + dev_err(__scm->dev, + "Assign memory protection call failed %d\n", ret); + return -EINVAL; + } + + *srcvm = next_vm; + return 0; +} +EXPORT_SYMBOL_GPL(qcom_scm_assign_mem); + +/** + * qcom_scm_ocmem_lock_available() - is OCMEM lock/unlock interface available + */ +bool qcom_scm_ocmem_lock_available(void) +{ + return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_OCMEM, + QCOM_SCM_OCMEM_LOCK_CMD); +} +EXPORT_SYMBOL_GPL(qcom_scm_ocmem_lock_available); + +/** + * qcom_scm_ocmem_lock() - call OCMEM lock interface to assign an OCMEM + * region to the specified initiator + * + * @id: tz initiator id + * @offset: OCMEM offset + * @size: OCMEM size + * @mode: access mode (WIDE/NARROW) + */ +int qcom_scm_ocmem_lock(enum qcom_scm_ocmem_client id, u32 offset, u32 size, + u32 mode) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_OCMEM, + .cmd = QCOM_SCM_OCMEM_LOCK_CMD, + .args[0] = id, + .args[1] = offset, + .args[2] = size, + .args[3] = mode, + .arginfo = QCOM_SCM_ARGS(4), + }; + + return qcom_scm_call(__scm->dev, &desc, NULL); +} +EXPORT_SYMBOL_GPL(qcom_scm_ocmem_lock); + +/** + * qcom_scm_ocmem_unlock() - call OCMEM unlock interface to release an OCMEM + * region from the specified initiator + * + * @id: tz initiator id + * @offset: OCMEM offset + * @size: OCMEM size + */ +int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset, u32 size) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_OCMEM, + .cmd = QCOM_SCM_OCMEM_UNLOCK_CMD, + .args[0] = id, + .args[1] = offset, + .args[2] = size, + .arginfo = QCOM_SCM_ARGS(3), + }; + + return qcom_scm_call(__scm->dev, &desc, NULL); +} +EXPORT_SYMBOL_GPL(qcom_scm_ocmem_unlock); + +/** + * qcom_scm_ice_available() - Is the ICE key programming interface available? + * + * Return: true iff the SCM calls wrapped by qcom_scm_ice_invalidate_key() and + * qcom_scm_ice_set_key() are available. + */ +bool qcom_scm_ice_available(void) +{ + return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_ES, + QCOM_SCM_ES_INVALIDATE_ICE_KEY) && + __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_ES, + QCOM_SCM_ES_CONFIG_SET_ICE_KEY); +} +EXPORT_SYMBOL_GPL(qcom_scm_ice_available); + +/** + * qcom_scm_ice_invalidate_key() - Invalidate an inline encryption key + * @index: the keyslot to invalidate + * + * The UFSHCI and eMMC standards define a standard way to do this, but it + * doesn't work on these SoCs; only this SCM call does. + * + * It is assumed that the SoC has only one ICE instance being used, as this SCM + * call doesn't specify which ICE instance the keyslot belongs to. + * + * Return: 0 on success; -errno on failure. + */ +int qcom_scm_ice_invalidate_key(u32 index) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_ES, + .cmd = QCOM_SCM_ES_INVALIDATE_ICE_KEY, + .arginfo = QCOM_SCM_ARGS(1), + .args[0] = index, + .owner = ARM_SMCCC_OWNER_SIP, + }; + + return qcom_scm_call(__scm->dev, &desc, NULL); +} +EXPORT_SYMBOL_GPL(qcom_scm_ice_invalidate_key); + +/** + * qcom_scm_ice_set_key() - Set an inline encryption key + * @index: the keyslot into which to set the key + * @key: the key to program + * @key_size: the size of the key in bytes + * @cipher: the encryption algorithm the key is for + * @data_unit_size: the encryption data unit size, i.e. the size of each + * individual plaintext and ciphertext. Given in 512-byte + * units, e.g. 1 = 512 bytes, 8 = 4096 bytes, etc. + * + * Program a key into a keyslot of Qualcomm ICE (Inline Crypto Engine), where it + * can then be used to encrypt/decrypt UFS or eMMC I/O requests inline. + * + * The UFSHCI and eMMC standards define a standard way to do this, but it + * doesn't work on these SoCs; only this SCM call does. + * + * It is assumed that the SoC has only one ICE instance being used, as this SCM + * call doesn't specify which ICE instance the keyslot belongs to. + * + * Return: 0 on success; -errno on failure. + */ +int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size, + enum qcom_scm_ice_cipher cipher, u32 data_unit_size) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_ES, + .cmd = QCOM_SCM_ES_CONFIG_SET_ICE_KEY, + .arginfo = QCOM_SCM_ARGS(5, QCOM_SCM_VAL, QCOM_SCM_RW, + QCOM_SCM_VAL, QCOM_SCM_VAL, + QCOM_SCM_VAL), + .args[0] = index, + .args[2] = key_size, + .args[3] = cipher, + .args[4] = data_unit_size, + .owner = ARM_SMCCC_OWNER_SIP, + }; + void *keybuf; + dma_addr_t key_phys; + int ret; + + /* + * 'key' may point to vmalloc()'ed memory, but we need to pass a + * physical address that's been properly flushed. The sanctioned way to + * do this is by using the DMA API. But as is best practice for crypto + * keys, we also must wipe the key after use. This makes kmemdup() + + * dma_map_single() not clearly correct, since the DMA API can use + * bounce buffers. Instead, just use dma_alloc_coherent(). Programming + * keys is normally rare and thus not performance-critical. + */ + + keybuf = dma_alloc_coherent(__scm->dev, key_size, &key_phys, + GFP_KERNEL); + if (!keybuf) + return -ENOMEM; + memcpy(keybuf, key, key_size); + desc.args[1] = key_phys; + + ret = qcom_scm_call(__scm->dev, &desc, NULL); + + memzero_explicit(keybuf, key_size); + + dma_free_coherent(__scm->dev, key_size, keybuf, key_phys); + return ret; +} +EXPORT_SYMBOL_GPL(qcom_scm_ice_set_key); + +/** + * qcom_scm_hdcp_available() - Check if secure environment supports HDCP. + * + * Return true if HDCP is supported, false if not. + */ +bool qcom_scm_hdcp_available(void) +{ + bool avail; + int ret = qcom_scm_clk_enable(); + + if (ret) + return ret; + + avail = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_HDCP, + QCOM_SCM_HDCP_INVOKE); + + qcom_scm_clk_disable(); + + return avail; +} +EXPORT_SYMBOL_GPL(qcom_scm_hdcp_available); + +/** + * qcom_scm_hdcp_req() - Send HDCP request. + * @req: HDCP request array + * @req_cnt: HDCP request array count + * @resp: response buffer passed to SCM + * + * Write HDCP register(s) through SCM. + */ +int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) +{ + int ret; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_HDCP, + .cmd = QCOM_SCM_HDCP_INVOKE, + .arginfo = QCOM_SCM_ARGS(10), + .args = { + req[0].addr, + req[0].val, + req[1].addr, + req[1].val, + req[2].addr, + req[2].val, + req[3].addr, + req[3].val, + req[4].addr, + req[4].val + }, + .owner = ARM_SMCCC_OWNER_SIP, + }; + struct qcom_scm_res res; + + if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT) + return -ERANGE; + + ret = qcom_scm_clk_enable(); + if (ret) + return ret; + + ret = qcom_scm_call(__scm->dev, &desc, &res); + *resp = res.result[0]; + + qcom_scm_clk_disable(); + + return ret; +} +EXPORT_SYMBOL_GPL(qcom_scm_hdcp_req); + +int qcom_scm_iommu_set_pt_format(u32 sec_id, u32 ctx_num, u32 pt_fmt) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_SMMU_PROGRAM, + .cmd = QCOM_SCM_SMMU_PT_FORMAT, + .arginfo = QCOM_SCM_ARGS(3), + .args[0] = sec_id, + .args[1] = ctx_num, + .args[2] = pt_fmt, /* 0: LPAE AArch32 - 1: AArch64 */ + .owner = ARM_SMCCC_OWNER_SIP, + }; + + return qcom_scm_call(__scm->dev, &desc, NULL); +} +EXPORT_SYMBOL_GPL(qcom_scm_iommu_set_pt_format); + +int qcom_scm_qsmmu500_wait_safe_toggle(bool en) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_SMMU_PROGRAM, + .cmd = QCOM_SCM_SMMU_CONFIG_ERRATA1, + .arginfo = QCOM_SCM_ARGS(2), + .args[0] = QCOM_SCM_SMMU_CONFIG_ERRATA1_CLIENT_ALL, + .args[1] = en, + .owner = ARM_SMCCC_OWNER_SIP, + }; + + + return qcom_scm_call_atomic(__scm->dev, &desc, NULL); +} +EXPORT_SYMBOL_GPL(qcom_scm_qsmmu500_wait_safe_toggle); + +bool qcom_scm_lmh_dcvsh_available(void) +{ + return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_LMH, QCOM_SCM_LMH_LIMIT_DCVSH); +} +EXPORT_SYMBOL_GPL(qcom_scm_lmh_dcvsh_available); + +int qcom_scm_lmh_profile_change(u32 profile_id) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_LMH, + .cmd = QCOM_SCM_LMH_LIMIT_PROFILE_CHANGE, + .arginfo = QCOM_SCM_ARGS(1, QCOM_SCM_VAL), + .args[0] = profile_id, + .owner = ARM_SMCCC_OWNER_SIP, + }; + + return qcom_scm_call(__scm->dev, &desc, NULL); +} +EXPORT_SYMBOL_GPL(qcom_scm_lmh_profile_change); + +int qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val, + u64 limit_node, u32 node_id, u64 version) +{ + dma_addr_t payload_phys; + u32 *payload_buf; + int ret, payload_size = 5 * sizeof(u32); + + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_LMH, + .cmd = QCOM_SCM_LMH_LIMIT_DCVSH, + .arginfo = QCOM_SCM_ARGS(5, QCOM_SCM_RO, QCOM_SCM_VAL, QCOM_SCM_VAL, + QCOM_SCM_VAL, QCOM_SCM_VAL), + .args[1] = payload_size, + .args[2] = limit_node, + .args[3] = node_id, + .args[4] = version, + .owner = ARM_SMCCC_OWNER_SIP, + }; + + payload_buf = dma_alloc_coherent(__scm->dev, payload_size, &payload_phys, GFP_KERNEL); + if (!payload_buf) + return -ENOMEM; + + payload_buf[0] = payload_fn; + payload_buf[1] = 0; + payload_buf[2] = payload_reg; + payload_buf[3] = 1; + payload_buf[4] = payload_val; + + desc.args[0] = payload_phys; + + ret = qcom_scm_call(__scm->dev, &desc, NULL); + + dma_free_coherent(__scm->dev, payload_size, payload_buf, payload_phys); + return ret; +} +EXPORT_SYMBOL_GPL(qcom_scm_lmh_dcvsh); + +static int qcom_scm_find_dload_address(struct device *dev, u64 *addr) +{ + struct device_node *tcsr; + struct device_node *np = dev->of_node; + struct resource res; + u32 offset; + int ret; + + tcsr = of_parse_phandle(np, "qcom,dload-mode", 0); + if (!tcsr) + return 0; + + ret = of_address_to_resource(tcsr, 0, &res); + of_node_put(tcsr); + if (ret) + return ret; + + ret = of_property_read_u32_index(np, "qcom,dload-mode", 1, &offset); + if (ret < 0) + return ret; + + *addr = res.start + offset; + + return 0; +} + +#ifdef CONFIG_QCOM_QSEECOM + +/* Lock for QSEECOM SCM call executions */ +static DEFINE_MUTEX(qcom_scm_qseecom_call_lock); + +static int __qcom_scm_qseecom_call(const struct qcom_scm_desc *desc, + struct qcom_scm_qseecom_resp *res) +{ + struct qcom_scm_res scm_res = {}; + int status; + + /* + * QSEECOM SCM calls should not be executed concurrently. Therefore, we + * require the respective call lock to be held. + */ + lockdep_assert_held(&qcom_scm_qseecom_call_lock); + + status = qcom_scm_call(__scm->dev, desc, &scm_res); + + res->result = scm_res.result[0]; + res->resp_type = scm_res.result[1]; + res->data = scm_res.result[2]; + + if (status) + return status; + + return 0; +} + +/** + * qcom_scm_qseecom_call() - Perform a QSEECOM SCM call. + * @desc: SCM call descriptor. + * @res: SCM call response (output). + * + * Performs the QSEECOM SCM call described by @desc, returning the response in + * @rsp. + * + * Return: Zero on success, nonzero on failure. + */ +static int qcom_scm_qseecom_call(const struct qcom_scm_desc *desc, + struct qcom_scm_qseecom_resp *res) +{ + int status; + + /* + * Note: Multiple QSEECOM SCM calls should not be executed same time, + * so lock things here. This needs to be extended to callback/listener + * handling when support for that is implemented. + */ + + mutex_lock(&qcom_scm_qseecom_call_lock); + status = __qcom_scm_qseecom_call(desc, res); + mutex_unlock(&qcom_scm_qseecom_call_lock); + + dev_dbg(__scm->dev, "%s: owner=%x, svc=%x, cmd=%x, result=%lld, type=%llx, data=%llx\n", + __func__, desc->owner, desc->svc, desc->cmd, res->result, + res->resp_type, res->data); + + if (status) { + dev_err(__scm->dev, "qseecom: scm call failed with error %d\n", status); + return status; + } + + /* + * TODO: Handle incomplete and blocked calls: + * + * Incomplete and blocked calls are not supported yet. Some devices + * and/or commands require those, some don't. Let's warn about them + * prominently in case someone attempts to try these commands with a + * device/command combination that isn't supported yet. + */ + WARN_ON(res->result == QSEECOM_RESULT_INCOMPLETE); + WARN_ON(res->result == QSEECOM_RESULT_BLOCKED_ON_LISTENER); + + return 0; +} + +/** + * qcom_scm_qseecom_get_version() - Query the QSEECOM version. + * @version: Pointer where the QSEECOM version will be stored. + * + * Performs the QSEECOM SCM querying the QSEECOM version currently running in + * the TrustZone. + * + * Return: Zero on success, nonzero on failure. + */ +static int qcom_scm_qseecom_get_version(u32 *version) +{ + struct qcom_scm_desc desc = {}; + struct qcom_scm_qseecom_resp res = {}; + u32 feature = 10; + int ret; + + desc.owner = QSEECOM_TZ_OWNER_SIP; + desc.svc = QSEECOM_TZ_SVC_INFO; + desc.cmd = QSEECOM_TZ_CMD_INFO_VERSION; + desc.arginfo = QCOM_SCM_ARGS(1, QCOM_SCM_VAL); + desc.args[0] = feature; + + ret = qcom_scm_qseecom_call(&desc, &res); + if (ret) + return ret; + + *version = res.result; + return 0; +} + +/** + * qcom_scm_qseecom_app_get_id() - Query the app ID for a given QSEE app name. + * @app_name: The name of the app. + * @app_id: The returned app ID. + * + * Query and return the application ID of the SEE app identified by the given + * name. This returned ID is the unique identifier of the app required for + * subsequent communication. + * + * Return: Zero on success, nonzero on failure, -ENOENT if the app has not been + * loaded or could not be found. + */ +int qcom_scm_qseecom_app_get_id(const char *app_name, u32 *app_id) +{ + unsigned long name_buf_size = QSEECOM_MAX_APP_NAME_SIZE; + unsigned long app_name_len = strlen(app_name); + struct qcom_scm_desc desc = {}; + struct qcom_scm_qseecom_resp res = {}; + dma_addr_t name_buf_phys; + char *name_buf; + int status; + + if (app_name_len >= name_buf_size) + return -EINVAL; + + name_buf = kzalloc(name_buf_size, GFP_KERNEL); + if (!name_buf) + return -ENOMEM; + + memcpy(name_buf, app_name, app_name_len); + + name_buf_phys = dma_map_single(__scm->dev, name_buf, name_buf_size, DMA_TO_DEVICE); + status = dma_mapping_error(__scm->dev, name_buf_phys); + if (status) { + kfree(name_buf); + dev_err(__scm->dev, "qseecom: failed to map dma address\n"); + return status; + } + + desc.owner = QSEECOM_TZ_OWNER_QSEE_OS; + desc.svc = QSEECOM_TZ_SVC_APP_MGR; + desc.cmd = QSEECOM_TZ_CMD_APP_LOOKUP; + desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_RW, QCOM_SCM_VAL); + desc.args[0] = name_buf_phys; + desc.args[1] = app_name_len; + + status = qcom_scm_qseecom_call(&desc, &res); + dma_unmap_single(__scm->dev, name_buf_phys, name_buf_size, DMA_TO_DEVICE); + kfree(name_buf); + + if (status) + return status; + + if (res.result == QSEECOM_RESULT_FAILURE) + return -ENOENT; + + if (res.result != QSEECOM_RESULT_SUCCESS) + return -EINVAL; + + if (res.resp_type != QSEECOM_SCM_RES_APP_ID) + return -EINVAL; + + *app_id = res.data; + return 0; +} +EXPORT_SYMBOL_GPL(qcom_scm_qseecom_app_get_id); + +/** + * qcom_scm_qseecom_app_send() - Send to and receive data from a given QSEE app. + * @app_id: The ID of the target app. + * @req: Request buffer sent to the app (must be DMA-mappable). + * @req_size: Size of the request buffer. + * @rsp: Response buffer, written to by the app (must be DMA-mappable). + * @rsp_size: Size of the response buffer. + * + * Sends a request to the QSEE app associated with the given ID and read back + * its response. The caller must provide two DMA memory regions, one for the + * request and one for the response, and fill out the @req region with the + * respective (app-specific) request data. The QSEE app reads this and returns + * its response in the @rsp region. + * + * Return: Zero on success, nonzero on failure. + */ +int qcom_scm_qseecom_app_send(u32 app_id, void *req, size_t req_size, void *rsp, + size_t rsp_size) +{ + struct qcom_scm_qseecom_resp res = {}; + struct qcom_scm_desc desc = {}; + dma_addr_t req_phys; + dma_addr_t rsp_phys; + int status; + + /* Map request buffer */ + req_phys = dma_map_single(__scm->dev, req, req_size, DMA_TO_DEVICE); + status = dma_mapping_error(__scm->dev, req_phys); + if (status) { + dev_err(__scm->dev, "qseecom: failed to map request buffer\n"); + return status; + } + + /* Map response buffer */ + rsp_phys = dma_map_single(__scm->dev, rsp, rsp_size, DMA_FROM_DEVICE); + status = dma_mapping_error(__scm->dev, rsp_phys); + if (status) { + dma_unmap_single(__scm->dev, req_phys, req_size, DMA_TO_DEVICE); + dev_err(__scm->dev, "qseecom: failed to map response buffer\n"); + return status; + } + + /* Set up SCM call data */ + desc.owner = QSEECOM_TZ_OWNER_TZ_APPS; + desc.svc = QSEECOM_TZ_SVC_APP_ID_PLACEHOLDER; + desc.cmd = QSEECOM_TZ_CMD_APP_SEND; + desc.arginfo = QCOM_SCM_ARGS(5, QCOM_SCM_VAL, + QCOM_SCM_RW, QCOM_SCM_VAL, + QCOM_SCM_RW, QCOM_SCM_VAL); + desc.args[0] = app_id; + desc.args[1] = req_phys; + desc.args[2] = req_size; + desc.args[3] = rsp_phys; + desc.args[4] = rsp_size; + + /* Perform call */ + status = qcom_scm_qseecom_call(&desc, &res); + + /* Unmap buffers */ + dma_unmap_single(__scm->dev, rsp_phys, rsp_size, DMA_FROM_DEVICE); + dma_unmap_single(__scm->dev, req_phys, req_size, DMA_TO_DEVICE); + + if (status) + return status; + + if (res.result != QSEECOM_RESULT_SUCCESS) + return -EIO; + + return 0; +} +EXPORT_SYMBOL_GPL(qcom_scm_qseecom_app_send); + +/* + * We do not yet support re-entrant calls via the qseecom interface. To prevent + + any potential issues with this, only allow validated machines for now. + */ +static const struct of_device_id qcom_scm_qseecom_allowlist[] = { + { .compatible = "lenovo,thinkpad-x13s", }, + { } +}; + +static bool qcom_scm_qseecom_machine_is_allowed(void) +{ + struct device_node *np; + bool match; + + np = of_find_node_by_path("/"); + if (!np) + return false; + + match = of_match_node(qcom_scm_qseecom_allowlist, np); + of_node_put(np); + + return match; +} + +static void qcom_scm_qseecom_free(void *data) +{ + struct platform_device *qseecom_dev = data; + + platform_device_del(qseecom_dev); + platform_device_put(qseecom_dev); +} + +static int qcom_scm_qseecom_init(struct qcom_scm *scm) +{ + struct platform_device *qseecom_dev; + u32 version; + int ret; + + /* + * Note: We do two steps of validation here: First, we try to query the + * QSEECOM version as a check to see if the interface exists on this + * device. Second, we check against known good devices due to current + * driver limitations (see comment in qcom_scm_qseecom_allowlist). + * + * Note that we deliberately do the machine check after the version + * check so that we can log potentially supported devices. This should + * be safe as downstream sources indicate that the version query is + * neither blocking nor reentrant. + */ + ret = qcom_scm_qseecom_get_version(&version); + if (ret) + return 0; + + dev_info(scm->dev, "qseecom: found qseecom with version 0x%x\n", version); + + if (!qcom_scm_qseecom_machine_is_allowed()) { + dev_info(scm->dev, "qseecom: untested machine, skipping\n"); + return 0; + } + + /* + * Set up QSEECOM interface device. All application clients will be + * set up and managed by the corresponding driver for it. + */ + qseecom_dev = platform_device_alloc("qcom_qseecom", -1); + if (!qseecom_dev) + return -ENOMEM; + + qseecom_dev->dev.parent = scm->dev; + + ret = platform_device_add(qseecom_dev); + if (ret) { + platform_device_put(qseecom_dev); + return ret; + } + + return devm_add_action_or_reset(scm->dev, qcom_scm_qseecom_free, qseecom_dev); +} + +#else /* CONFIG_QCOM_QSEECOM */ + +static int qcom_scm_qseecom_init(struct qcom_scm *scm) +{ + return 0; +} + +#endif /* CONFIG_QCOM_QSEECOM */ + +/** + * qcom_scm_is_available() - Checks if SCM is available + */ +bool qcom_scm_is_available(void) +{ + return !!__scm; +} +EXPORT_SYMBOL_GPL(qcom_scm_is_available); + +static int qcom_scm_assert_valid_wq_ctx(u32 wq_ctx) +{ + /* FW currently only supports a single wq_ctx (zero). + * TODO: Update this logic to include dynamic allocation and lookup of + * completion structs when FW supports more wq_ctx values. + */ + if (wq_ctx != 0) { + dev_err(__scm->dev, "Firmware unexpectedly passed non-zero wq_ctx\n"); + return -EINVAL; + } + + return 0; +} + +int qcom_scm_wait_for_wq_completion(u32 wq_ctx) +{ + int ret; + + ret = qcom_scm_assert_valid_wq_ctx(wq_ctx); + if (ret) + return ret; + + wait_for_completion(&__scm->waitq_comp); + + return 0; +} + +static int qcom_scm_waitq_wakeup(struct qcom_scm *scm, unsigned int wq_ctx) +{ + int ret; + + ret = qcom_scm_assert_valid_wq_ctx(wq_ctx); + if (ret) + return ret; + + complete(&__scm->waitq_comp); + + return 0; +} + +static irqreturn_t qcom_scm_irq_handler(int irq, void *data) +{ + int ret; + struct qcom_scm *scm = data; + u32 wq_ctx, flags, more_pending = 0; + + do { + ret = scm_get_wq_ctx(&wq_ctx, &flags, &more_pending); + if (ret) { + dev_err(scm->dev, "GET_WQ_CTX SMC call failed: %d\n", ret); + goto out; + } + + if (flags != QCOM_SMC_WAITQ_FLAG_WAKE_ONE && + flags != QCOM_SMC_WAITQ_FLAG_WAKE_ALL) { + dev_err(scm->dev, "Invalid flags found for wq_ctx: %u\n", flags); + goto out; + } + + ret = qcom_scm_waitq_wakeup(scm, wq_ctx); + if (ret) + goto out; + } while (more_pending); + +out: + return IRQ_HANDLED; +} + +static int qcom_scm_probe(struct platform_device *pdev) +{ + struct qcom_scm *scm; + int irq, ret; + + scm = devm_kzalloc(&pdev->dev, sizeof(*scm), GFP_KERNEL); + if (!scm) + return -ENOMEM; + + ret = qcom_scm_find_dload_address(&pdev->dev, &scm->dload_mode_addr); + if (ret < 0) + return ret; + + mutex_init(&scm->scm_bw_lock); + + scm->path = devm_of_icc_get(&pdev->dev, NULL); + if (IS_ERR(scm->path)) + return dev_err_probe(&pdev->dev, PTR_ERR(scm->path), + "failed to acquire interconnect path\n"); + + scm->core_clk = devm_clk_get_optional(&pdev->dev, "core"); + if (IS_ERR(scm->core_clk)) + return PTR_ERR(scm->core_clk); + + scm->iface_clk = devm_clk_get_optional(&pdev->dev, "iface"); + if (IS_ERR(scm->iface_clk)) + return PTR_ERR(scm->iface_clk); + + scm->bus_clk = devm_clk_get_optional(&pdev->dev, "bus"); + if (IS_ERR(scm->bus_clk)) + return PTR_ERR(scm->bus_clk); + + scm->reset.ops = &qcom_scm_pas_reset_ops; + scm->reset.nr_resets = 1; + scm->reset.of_node = pdev->dev.of_node; + ret = devm_reset_controller_register(&pdev->dev, &scm->reset); + if (ret) + return ret; + + /* vote for max clk rate for highest performance */ + ret = clk_set_rate(scm->core_clk, INT_MAX); + if (ret) + return ret; + + __scm = scm; + __scm->dev = &pdev->dev; + + init_completion(&__scm->waitq_comp); + + irq = platform_get_irq_optional(pdev, 0); + if (irq < 0) { + if (irq != -ENXIO) + return irq; + } else { + ret = devm_request_threaded_irq(__scm->dev, irq, NULL, qcom_scm_irq_handler, + IRQF_ONESHOT, "qcom-scm", __scm); + if (ret < 0) + return dev_err_probe(scm->dev, ret, "Failed to request qcom-scm irq\n"); + } + + __get_convention(); + + /* + * If requested enable "download mode", from this point on warmboot + * will cause the boot stages to enter download mode, unless + * disabled below by a clean shutdown/reboot. + */ + if (download_mode) + qcom_scm_set_download_mode(true); + + + /* + * Disable SDI if indicated by DT that it is enabled by default. + */ + if (of_property_read_bool(pdev->dev.of_node, "qcom,sdi-enabled")) + qcom_scm_disable_sdi(); + + /* + * Initialize the QSEECOM interface. + * + * Note: QSEECOM is fairly self-contained and this only adds the + * interface device (the driver of which does most of the heavy + * lifting). So any errors returned here should be either -ENOMEM or + * -EINVAL (with the latter only in case there's a bug in our code). + * This means that there is no need to bring down the whole SCM driver. + * Just log the error instead and let SCM live. + */ + ret = qcom_scm_qseecom_init(scm); + WARN(ret < 0, "failed to initialize qseecom: %d\n", ret); + + return 0; +} + +static void qcom_scm_shutdown(struct platform_device *pdev) +{ + /* Clean shutdown, disable download mode to allow normal restart */ + qcom_scm_set_download_mode(false); +} + +static const struct of_device_id qcom_scm_dt_match[] = { + { .compatible = "qcom,scm" }, + + /* Legacy entries kept for backwards compatibility */ + { .compatible = "qcom,scm-apq8064" }, + { .compatible = "qcom,scm-apq8084" }, + { .compatible = "qcom,scm-ipq4019" }, + { .compatible = "qcom,scm-msm8953" }, + { .compatible = "qcom,scm-msm8974" }, + { .compatible = "qcom,scm-msm8996" }, + {} +}; +MODULE_DEVICE_TABLE(of, qcom_scm_dt_match); + +static struct platform_driver qcom_scm_driver = { + .driver = { + .name = "qcom_scm", + .of_match_table = qcom_scm_dt_match, + .suppress_bind_attrs = true, + }, + .probe = qcom_scm_probe, + .shutdown = qcom_scm_shutdown, +}; + +static int __init qcom_scm_init(void) +{ + return platform_driver_register(&qcom_scm_driver); +} +subsys_initcall(qcom_scm_init); + +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. SCM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/firmware/qcom/qcom_scm.h b/drivers/firmware/qcom/qcom_scm.h new file mode 100644 index 000000000000..7b68fa820495 --- /dev/null +++ b/drivers/firmware/qcom/qcom_scm.h @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2010-2015,2019 The Linux Foundation. All rights reserved. + */ +#ifndef __QCOM_SCM_INT_H +#define __QCOM_SCM_INT_H + +enum qcom_scm_convention { + SMC_CONVENTION_UNKNOWN, + SMC_CONVENTION_LEGACY, + SMC_CONVENTION_ARM_32, + SMC_CONVENTION_ARM_64, +}; + +extern enum qcom_scm_convention qcom_scm_convention; + +#define MAX_QCOM_SCM_ARGS 10 +#define MAX_QCOM_SCM_RETS 3 + +enum qcom_scm_arg_types { + QCOM_SCM_VAL, + QCOM_SCM_RO, + QCOM_SCM_RW, + QCOM_SCM_BUFVAL, +}; + +#define QCOM_SCM_ARGS_IMPL(num, a, b, c, d, e, f, g, h, i, j, ...) (\ + (((a) & 0x3) << 4) | \ + (((b) & 0x3) << 6) | \ + (((c) & 0x3) << 8) | \ + (((d) & 0x3) << 10) | \ + (((e) & 0x3) << 12) | \ + (((f) & 0x3) << 14) | \ + (((g) & 0x3) << 16) | \ + (((h) & 0x3) << 18) | \ + (((i) & 0x3) << 20) | \ + (((j) & 0x3) << 22) | \ + ((num) & 0xf)) + +#define QCOM_SCM_ARGS(...) QCOM_SCM_ARGS_IMPL(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + + +/** + * struct qcom_scm_desc + * @arginfo: Metadata describing the arguments in args[] + * @args: The array of arguments for the secure syscall + */ +struct qcom_scm_desc { + u32 svc; + u32 cmd; + u32 arginfo; + u64 args[MAX_QCOM_SCM_ARGS]; + u32 owner; +}; + +/** + * struct qcom_scm_res + * @result: The values returned by the secure syscall + */ +struct qcom_scm_res { + u64 result[MAX_QCOM_SCM_RETS]; +}; + +int qcom_scm_wait_for_wq_completion(u32 wq_ctx); +int scm_get_wq_ctx(u32 *wq_ctx, u32 *flags, u32 *more_pending); + +#define SCM_SMC_FNID(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF)) +extern int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc, + enum qcom_scm_convention qcom_convention, + struct qcom_scm_res *res, bool atomic); +#define scm_smc_call(dev, desc, res, atomic) \ + __scm_smc_call((dev), (desc), qcom_scm_convention, (res), (atomic)) + +#define SCM_LEGACY_FNID(s, c) (((s) << 10) | ((c) & 0x3ff)) +extern int scm_legacy_call_atomic(struct device *dev, + const struct qcom_scm_desc *desc, + struct qcom_scm_res *res); +extern int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc, + struct qcom_scm_res *res); + +#define QCOM_SCM_SVC_BOOT 0x01 +#define QCOM_SCM_BOOT_SET_ADDR 0x01 +#define QCOM_SCM_BOOT_TERMINATE_PC 0x02 +#define QCOM_SCM_BOOT_SDI_CONFIG 0x09 +#define QCOM_SCM_BOOT_SET_DLOAD_MODE 0x10 +#define QCOM_SCM_BOOT_SET_ADDR_MC 0x11 +#define QCOM_SCM_BOOT_SET_REMOTE_STATE 0x0a +#define QCOM_SCM_FLUSH_FLAG_MASK 0x3 +#define QCOM_SCM_BOOT_MAX_CPUS 4 +#define QCOM_SCM_BOOT_MC_FLAG_AARCH64 BIT(0) +#define QCOM_SCM_BOOT_MC_FLAG_COLDBOOT BIT(1) +#define QCOM_SCM_BOOT_MC_FLAG_WARMBOOT BIT(2) + +#define QCOM_SCM_SVC_PIL 0x02 +#define QCOM_SCM_PIL_PAS_INIT_IMAGE 0x01 +#define QCOM_SCM_PIL_PAS_MEM_SETUP 0x02 +#define QCOM_SCM_PIL_PAS_AUTH_AND_RESET 0x05 +#define QCOM_SCM_PIL_PAS_SHUTDOWN 0x06 +#define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07 +#define QCOM_SCM_PIL_PAS_MSS_RESET 0x0a + +#define QCOM_SCM_SVC_IO 0x05 +#define QCOM_SCM_IO_READ 0x01 +#define QCOM_SCM_IO_WRITE 0x02 + +#define QCOM_SCM_SVC_INFO 0x06 +#define QCOM_SCM_INFO_IS_CALL_AVAIL 0x01 + +#define QCOM_SCM_SVC_MP 0x0c +#define QCOM_SCM_MP_RESTORE_SEC_CFG 0x02 +#define QCOM_SCM_MP_IOMMU_SECURE_PTBL_SIZE 0x03 +#define QCOM_SCM_MP_IOMMU_SECURE_PTBL_INIT 0x04 +#define QCOM_SCM_MP_IOMMU_SET_CP_POOL_SIZE 0x05 +#define QCOM_SCM_MP_VIDEO_VAR 0x08 +#define QCOM_SCM_MP_ASSIGN 0x16 + +#define QCOM_SCM_SVC_OCMEM 0x0f +#define QCOM_SCM_OCMEM_LOCK_CMD 0x01 +#define QCOM_SCM_OCMEM_UNLOCK_CMD 0x02 + +#define QCOM_SCM_SVC_ES 0x10 /* Enterprise Security */ +#define QCOM_SCM_ES_INVALIDATE_ICE_KEY 0x03 +#define QCOM_SCM_ES_CONFIG_SET_ICE_KEY 0x04 + +#define QCOM_SCM_SVC_HDCP 0x11 +#define QCOM_SCM_HDCP_INVOKE 0x01 + +#define QCOM_SCM_SVC_LMH 0x13 +#define QCOM_SCM_LMH_LIMIT_PROFILE_CHANGE 0x01 +#define QCOM_SCM_LMH_LIMIT_DCVSH 0x10 + +#define QCOM_SCM_SVC_SMMU_PROGRAM 0x15 +#define QCOM_SCM_SMMU_PT_FORMAT 0x01 +#define QCOM_SCM_SMMU_CONFIG_ERRATA1 0x03 +#define QCOM_SCM_SMMU_CONFIG_ERRATA1_CLIENT_ALL 0x02 + +#define QCOM_SCM_SVC_WAITQ 0x24 +#define QCOM_SCM_WAITQ_RESUME 0x02 +#define QCOM_SCM_WAITQ_GET_WQ_CTX 0x03 + +/* common error codes */ +#define QCOM_SCM_V2_EBUSY -12 +#define QCOM_SCM_ENOMEM -5 +#define QCOM_SCM_EOPNOTSUPP -4 +#define QCOM_SCM_EINVAL_ADDR -3 +#define QCOM_SCM_EINVAL_ARG -2 +#define QCOM_SCM_ERROR -1 +#define QCOM_SCM_INTERRUPTED 1 +#define QCOM_SCM_WAITQ_SLEEP 2 + +static inline int qcom_scm_remap_error(int err) +{ + switch (err) { + case QCOM_SCM_ERROR: + return -EIO; + case QCOM_SCM_EINVAL_ADDR: + case QCOM_SCM_EINVAL_ARG: + return -EINVAL; + case QCOM_SCM_EOPNOTSUPP: + return -EOPNOTSUPP; + case QCOM_SCM_ENOMEM: + return -ENOMEM; + case QCOM_SCM_V2_EBUSY: + return -EBUSY; + } + return -EINVAL; +} + +#endif diff --git a/drivers/firmware/qcom_qseecom.c b/drivers/firmware/qcom_qseecom.c deleted file mode 100644 index 731e6d5719f9..000000000000 --- a/drivers/firmware/qcom_qseecom.c +++ /dev/null @@ -1,120 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Driver for Qualcomm Secure Execution Environment (SEE) interface (QSEECOM). - * Responsible for setting up and managing QSEECOM client devices. - * - * Copyright (C) 2023 Maximilian Luz - */ -#include -#include -#include -#include -#include - -#include -#include - -struct qseecom_app_desc { - const char *app_name; - const char *dev_name; -}; - -static void qseecom_client_release(struct device *dev) -{ - struct qseecom_client *client; - - client = container_of(dev, struct qseecom_client, aux_dev.dev); - kfree(client); -} - -static void qseecom_client_remove(void *data) -{ - struct qseecom_client *client = data; - - auxiliary_device_delete(&client->aux_dev); - auxiliary_device_uninit(&client->aux_dev); -} - -static int qseecom_client_register(struct platform_device *qseecom_dev, - const struct qseecom_app_desc *desc) -{ - struct qseecom_client *client; - u32 app_id; - int ret; - - /* Try to find the app ID, skip device if not found */ - ret = qcom_scm_qseecom_app_get_id(desc->app_name, &app_id); - if (ret) - return ret == -ENOENT ? 0 : ret; - - dev_info(&qseecom_dev->dev, "setting up client for %s\n", desc->app_name); - - /* Allocate and set-up the client device */ - client = kzalloc(sizeof(*client), GFP_KERNEL); - if (!client) - return -ENOMEM; - - client->aux_dev.name = desc->dev_name; - client->aux_dev.dev.parent = &qseecom_dev->dev; - client->aux_dev.dev.release = qseecom_client_release; - client->app_id = app_id; - - ret = auxiliary_device_init(&client->aux_dev); - if (ret) { - kfree(client); - return ret; - } - - ret = auxiliary_device_add(&client->aux_dev); - if (ret) { - auxiliary_device_uninit(&client->aux_dev); - return ret; - } - - ret = devm_add_action_or_reset(&qseecom_dev->dev, qseecom_client_remove, client); - if (ret) - return ret; - - return 0; -} - -/* - * List of supported applications. One client device will be created per entry, - * assuming the app has already been loaded (usually by firmware bootloaders) - * and its ID can be queried successfully. - */ -static const struct qseecom_app_desc qcom_qseecom_apps[] = { - { "qcom.tz.uefisecapp", "uefisecapp" }, -}; - -static int qcom_qseecom_probe(struct platform_device *qseecom_dev) -{ - int ret; - int i; - - /* Set up client devices for each base application */ - for (i = 0; i < ARRAY_SIZE(qcom_qseecom_apps); i++) { - ret = qseecom_client_register(qseecom_dev, &qcom_qseecom_apps[i]); - if (ret) - return ret; - } - - return 0; -} - -static struct platform_driver qcom_qseecom_driver = { - .driver = { - .name = "qcom_qseecom", - }, - .probe = qcom_qseecom_probe, -}; - -static int __init qcom_qseecom_init(void) -{ - return platform_driver_register(&qcom_qseecom_driver); -} -subsys_initcall(qcom_qseecom_init); - -MODULE_AUTHOR("Maximilian Luz "); -MODULE_DESCRIPTION("Driver for the Qualcomm SEE (QSEECOM) interface"); -MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/qcom_qseecom_uefisecapp.c b/drivers/firmware/qcom_qseecom_uefisecapp.c deleted file mode 100644 index a33acdaf7b78..000000000000 --- a/drivers/firmware/qcom_qseecom_uefisecapp.c +++ /dev/null @@ -1,871 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Client driver for Qualcomm UEFI Secure Application (qcom.tz.uefisecapp). - * Provides access to UEFI variables on platforms where they are secured by the - * aforementioned Secure Execution Environment (SEE) application. - * - * Copyright (C) 2023 Maximilian Luz - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* -- Qualcomm "uefisecapp" interface definitions. -------------------------- */ - -/* Maximum length of name string with null-terminator */ -#define QSEE_MAX_NAME_LEN 1024 - -#define QSEE_CMD_UEFI(x) (0x8000 | (x)) -#define QSEE_CMD_UEFI_GET_VARIABLE QSEE_CMD_UEFI(0) -#define QSEE_CMD_UEFI_SET_VARIABLE QSEE_CMD_UEFI(1) -#define QSEE_CMD_UEFI_GET_NEXT_VARIABLE QSEE_CMD_UEFI(2) -#define QSEE_CMD_UEFI_QUERY_VARIABLE_INFO QSEE_CMD_UEFI(3) - -/** - * struct qsee_req_uefi_get_variable - Request for GetVariable command. - * @command_id: The ID of the command. Must be %QSEE_CMD_UEFI_GET_VARIABLE. - * @length: Length of the request in bytes, including this struct and any - * parameters (name, GUID) stored after it as well as any padding - * thereof for alignment. - * @name_offset: Offset from the start of this struct to where the variable - * name is stored (as utf-16 string), in bytes. - * @name_size: Size of the name parameter in bytes, including null-terminator. - * @guid_offset: Offset from the start of this struct to where the GUID - * parameter is stored, in bytes. - * @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t). - * @data_size: Size of the output buffer, in bytes. - */ -struct qsee_req_uefi_get_variable { - u32 command_id; - u32 length; - u32 name_offset; - u32 name_size; - u32 guid_offset; - u32 guid_size; - u32 data_size; -} __packed; - -/** - * struct qsee_rsp_uefi_get_variable - Response for GetVariable command. - * @command_id: The ID of the command. Should be %QSEE_CMD_UEFI_GET_VARIABLE. - * @length: Length of the response in bytes, including this struct and the - * returned data. - * @status: Status of this command. - * @attributes: EFI variable attributes. - * @data_offset: Offset from the start of this struct to where the data is - * stored, in bytes. - * @data_size: Size of the returned data, in bytes. In case status indicates - * that the buffer is too small, this will be the size required - * to store the EFI variable data. - */ -struct qsee_rsp_uefi_get_variable { - u32 command_id; - u32 length; - u32 status; - u32 attributes; - u32 data_offset; - u32 data_size; -} __packed; - -/** - * struct qsee_req_uefi_set_variable - Request for the SetVariable command. - * @command_id: The ID of the command. Must be %QSEE_CMD_UEFI_SET_VARIABLE. - * @length: Length of the request in bytes, including this struct and any - * parameters (name, GUID, data) stored after it as well as any - * padding thereof required for alignment. - * @name_offset: Offset from the start of this struct to where the variable - * name is stored (as utf-16 string), in bytes. - * @name_size: Size of the name parameter in bytes, including null-terminator. - * @guid_offset: Offset from the start of this struct to where the GUID - * parameter is stored, in bytes. - * @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t). - * @attributes: The EFI variable attributes to set for this variable. - * @data_offset: Offset from the start of this struct to where the EFI variable - * data is stored, in bytes. - * @data_size: Size of EFI variable data, in bytes. - * - */ -struct qsee_req_uefi_set_variable { - u32 command_id; - u32 length; - u32 name_offset; - u32 name_size; - u32 guid_offset; - u32 guid_size; - u32 attributes; - u32 data_offset; - u32 data_size; -} __packed; - -/** - * struct qsee_rsp_uefi_set_variable - Response for the SetVariable command. - * @command_id: The ID of the command. Should be %QSEE_CMD_UEFI_SET_VARIABLE. - * @length: The length of this response, i.e. the size of this struct in - * bytes. - * @status: Status of this command. - * @_unknown1: Unknown response field. - * @_unknown2: Unknown response field. - */ -struct qsee_rsp_uefi_set_variable { - u32 command_id; - u32 length; - u32 status; - u32 _unknown1; - u32 _unknown2; -} __packed; - -/** - * struct qsee_req_uefi_get_next_variable - Request for the - * GetNextVariableName command. - * @command_id: The ID of the command. Must be - * %QSEE_CMD_UEFI_GET_NEXT_VARIABLE. - * @length: Length of the request in bytes, including this struct and any - * parameters (name, GUID) stored after it as well as any padding - * thereof for alignment. - * @guid_offset: Offset from the start of this struct to where the GUID - * parameter is stored, in bytes. - * @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t). - * @name_offset: Offset from the start of this struct to where the variable - * name is stored (as utf-16 string), in bytes. - * @name_size: Size of the name parameter in bytes, including null-terminator. - */ -struct qsee_req_uefi_get_next_variable { - u32 command_id; - u32 length; - u32 guid_offset; - u32 guid_size; - u32 name_offset; - u32 name_size; -} __packed; - -/** - * struct qsee_rsp_uefi_get_next_variable - Response for the - * GetNextVariableName command. - * @command_id: The ID of the command. Should be - * %QSEE_CMD_UEFI_GET_NEXT_VARIABLE. - * @length: Length of the response in bytes, including this struct and any - * parameters (name, GUID) stored after it as well as any padding - * thereof for alignment. - * @status: Status of this command. - * @guid_offset: Offset from the start of this struct to where the GUID - * parameter is stored, in bytes. - * @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t). - * @name_offset: Offset from the start of this struct to where the variable - * name is stored (as utf-16 string), in bytes. - * @name_size: Size of the name parameter in bytes, including null-terminator. - */ -struct qsee_rsp_uefi_get_next_variable { - u32 command_id; - u32 length; - u32 status; - u32 guid_offset; - u32 guid_size; - u32 name_offset; - u32 name_size; -} __packed; - -/** - * struct qsee_req_uefi_query_variable_info - Response for the - * GetNextVariableName command. - * @command_id: The ID of the command. Must be - * %QSEE_CMD_UEFI_QUERY_VARIABLE_INFO. - * @length: The length of this request, i.e. the size of this struct in - * bytes. - * @attributes: The storage attributes to query the info for. - */ -struct qsee_req_uefi_query_variable_info { - u32 command_id; - u32 length; - u32 attributes; -} __packed; - -/** - * struct qsee_rsp_uefi_query_variable_info - Response for the - * GetNextVariableName command. - * @command_id: The ID of the command. Must be - * %QSEE_CMD_UEFI_QUERY_VARIABLE_INFO. - * @length: The length of this response, i.e. the size of this - * struct in bytes. - * @status: Status of this command. - * @_pad: Padding. - * @storage_space: Full storage space size, in bytes. - * @remaining_space: Free storage space available, in bytes. - * @max_variable_size: Maximum variable data size, in bytes. - */ -struct qsee_rsp_uefi_query_variable_info { - u32 command_id; - u32 length; - u32 status; - u32 _pad; - u64 storage_space; - u64 remaining_space; - u64 max_variable_size; -} __packed; - -/* -- Alignment helpers ----------------------------------------------------- */ - -/* - * Helper macro to ensure proper alignment of types (fields and arrays) when - * stored in some (contiguous) buffer. - * - * Note: The driver from which this one has been reverse-engineered expects an - * alignment of 8 bytes (64 bits) for GUIDs. Our definition of efi_guid_t, - * however, has an alignment of 4 byte (32 bits). So far, this seems to work - * fine here. See also the comment on the typedef of efi_guid_t. - */ -#define qcuefi_buf_align_fields(fields...) \ - ({ \ - size_t __len = 0; \ - fields \ - __len; \ - }) - -#define __field_impl(size, align, offset) \ - ({ \ - size_t *__offset = (offset); \ - size_t __aligned; \ - \ - __aligned = ALIGN(__len, align); \ - __len = __aligned + (size); \ - \ - if (__offset) \ - *__offset = __aligned; \ - }); - -#define __array_offs(type, count, offset) \ - __field_impl(sizeof(type) * (count), __alignof__(type), offset) - -#define __array(type, count) __array_offs(type, count, NULL) -#define __field_offs(type, offset) __array_offs(type, 1, offset) -#define __field(type) __array_offs(type, 1, NULL) - -/* -- UEFI app interface. --------------------------------------------------- */ - -struct qcuefi_client { - struct qseecom_client *client; - struct efivars efivars; -}; - -static struct device *qcuefi_dev(struct qcuefi_client *qcuefi) -{ - return &qcuefi->client->aux_dev.dev; -} - -static efi_status_t qsee_uefi_status_to_efi(u32 status) -{ - u64 category = status & 0xf0000000; - u64 code = status & 0x0fffffff; - - return category << (BITS_PER_LONG - 32) | code; -} - -static efi_status_t qsee_uefi_get_variable(struct qcuefi_client *qcuefi, const efi_char16_t *name, - const efi_guid_t *guid, u32 *attributes, - unsigned long *data_size, void *data) -{ - struct qsee_req_uefi_get_variable *req_data; - struct qsee_rsp_uefi_get_variable *rsp_data; - unsigned long buffer_size = *data_size; - efi_status_t efi_status = EFI_SUCCESS; - unsigned long name_length; - size_t guid_offs; - size_t name_offs; - size_t req_size; - size_t rsp_size; - ssize_t status; - - if (!name || !guid) - return EFI_INVALID_PARAMETER; - - name_length = ucs2_strnlen(name, QSEE_MAX_NAME_LEN) + 1; - if (name_length > QSEE_MAX_NAME_LEN) - return EFI_INVALID_PARAMETER; - - if (buffer_size && !data) - return EFI_INVALID_PARAMETER; - - req_size = qcuefi_buf_align_fields( - __field(*req_data) - __array_offs(*name, name_length, &name_offs) - __field_offs(*guid, &guid_offs) - ); - - rsp_size = qcuefi_buf_align_fields( - __field(*rsp_data) - __array(u8, buffer_size) - ); - - req_data = kzalloc(req_size, GFP_KERNEL); - if (!req_data) { - efi_status = EFI_OUT_OF_RESOURCES; - goto out; - } - - rsp_data = kzalloc(rsp_size, GFP_KERNEL); - if (!rsp_data) { - efi_status = EFI_OUT_OF_RESOURCES; - goto out_free_req; - } - - req_data->command_id = QSEE_CMD_UEFI_GET_VARIABLE; - req_data->data_size = buffer_size; - req_data->name_offset = name_offs; - req_data->name_size = name_length * sizeof(*name); - req_data->guid_offset = guid_offs; - req_data->guid_size = sizeof(*guid); - req_data->length = req_size; - - status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name, name_length); - if (status < 0) - return EFI_INVALID_PARAMETER; - - memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size); - - status = qcom_qseecom_app_send(qcuefi->client, req_data, req_size, rsp_data, rsp_size); - if (status) { - efi_status = EFI_DEVICE_ERROR; - goto out_free; - } - - if (rsp_data->command_id != QSEE_CMD_UEFI_GET_VARIABLE) { - efi_status = EFI_DEVICE_ERROR; - goto out_free; - } - - if (rsp_data->length < sizeof(*rsp_data)) { - efi_status = EFI_DEVICE_ERROR; - goto out_free; - } - - if (rsp_data->status) { - dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n", - __func__, rsp_data->status); - efi_status = qsee_uefi_status_to_efi(rsp_data->status); - - /* Update size and attributes in case buffer is too small. */ - if (efi_status == EFI_BUFFER_TOO_SMALL) { - *data_size = rsp_data->data_size; - if (attributes) - *attributes = rsp_data->attributes; - } - - goto out_free; - } - - if (rsp_data->length > rsp_size) { - efi_status = EFI_DEVICE_ERROR; - goto out_free; - } - - if (rsp_data->data_offset + rsp_data->data_size > rsp_data->length) { - efi_status = EFI_DEVICE_ERROR; - goto out_free; - } - - /* - * Note: We need to set attributes and data size even if the buffer is - * too small and we won't copy any data. This is described in spec, so - * that callers can either allocate a buffer properly (with two calls - * to this function) or just read back attributes withouth having to - * deal with that. - * - * Specifically: - * - If we have a buffer size of zero and no buffer, just return the - * attributes, required size, and indicate success. - * - If the buffer size is nonzero but too small, indicate that as an - * error. - * - Otherwise, we are good to copy the data. - * - * Note that we have already ensured above that the buffer pointer is - * non-NULL if its size is nonzero. - */ - *data_size = rsp_data->data_size; - if (attributes) - *attributes = rsp_data->attributes; - - if (buffer_size == 0 && !data) { - efi_status = EFI_SUCCESS; - goto out_free; - } - - if (buffer_size < rsp_data->data_size) { - efi_status = EFI_BUFFER_TOO_SMALL; - goto out_free; - } - - memcpy(data, ((void *)rsp_data) + rsp_data->data_offset, rsp_data->data_size); - -out_free: - kfree(rsp_data); -out_free_req: - kfree(req_data); -out: - return efi_status; -} - -static efi_status_t qsee_uefi_set_variable(struct qcuefi_client *qcuefi, const efi_char16_t *name, - const efi_guid_t *guid, u32 attributes, - unsigned long data_size, const void *data) -{ - struct qsee_req_uefi_set_variable *req_data; - struct qsee_rsp_uefi_set_variable *rsp_data; - efi_status_t efi_status = EFI_SUCCESS; - unsigned long name_length; - size_t name_offs; - size_t guid_offs; - size_t data_offs; - size_t req_size; - ssize_t status; - - if (!name || !guid) - return EFI_INVALID_PARAMETER; - - name_length = ucs2_strnlen(name, QSEE_MAX_NAME_LEN) + 1; - if (name_length > QSEE_MAX_NAME_LEN) - return EFI_INVALID_PARAMETER; - - /* - * Make sure we have some data if data_size is nonzero. Note that using - * a size of zero is a valid use-case described in spec and deletes the - * variable. - */ - if (data_size && !data) - return EFI_INVALID_PARAMETER; - - req_size = qcuefi_buf_align_fields( - __field(*req_data) - __array_offs(*name, name_length, &name_offs) - __field_offs(*guid, &guid_offs) - __array_offs(u8, data_size, &data_offs) - ); - - req_data = kzalloc(req_size, GFP_KERNEL); - if (!req_data) { - efi_status = EFI_OUT_OF_RESOURCES; - goto out; - } - - rsp_data = kzalloc(sizeof(*rsp_data), GFP_KERNEL); - if (!rsp_data) { - efi_status = EFI_OUT_OF_RESOURCES; - goto out_free_req; - } - - req_data->command_id = QSEE_CMD_UEFI_SET_VARIABLE; - req_data->attributes = attributes; - req_data->name_offset = name_offs; - req_data->name_size = name_length * sizeof(*name); - req_data->guid_offset = guid_offs; - req_data->guid_size = sizeof(*guid); - req_data->data_offset = data_offs; - req_data->data_size = data_size; - req_data->length = req_size; - - status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name, name_length); - if (status < 0) - return EFI_INVALID_PARAMETER; - - memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size); - - if (data_size) - memcpy(((void *)req_data) + req_data->data_offset, data, req_data->data_size); - - status = qcom_qseecom_app_send(qcuefi->client, req_data, req_size, rsp_data, - sizeof(*rsp_data)); - if (status) { - efi_status = EFI_DEVICE_ERROR; - goto out_free; - } - - if (rsp_data->command_id != QSEE_CMD_UEFI_SET_VARIABLE) { - efi_status = EFI_DEVICE_ERROR; - goto out_free; - } - - if (rsp_data->length != sizeof(*rsp_data)) { - efi_status = EFI_DEVICE_ERROR; - goto out_free; - } - - if (rsp_data->status) { - dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n", - __func__, rsp_data->status); - efi_status = qsee_uefi_status_to_efi(rsp_data->status); - } - -out_free: - kfree(rsp_data); -out_free_req: - kfree(req_data); -out: - return efi_status; -} - -static efi_status_t qsee_uefi_get_next_variable(struct qcuefi_client *qcuefi, - unsigned long *name_size, efi_char16_t *name, - efi_guid_t *guid) -{ - struct qsee_req_uefi_get_next_variable *req_data; - struct qsee_rsp_uefi_get_next_variable *rsp_data; - efi_status_t efi_status = EFI_SUCCESS; - size_t guid_offs; - size_t name_offs; - size_t req_size; - size_t rsp_size; - ssize_t status; - - if (!name_size || !name || !guid) - return EFI_INVALID_PARAMETER; - - if (*name_size == 0) - return EFI_INVALID_PARAMETER; - - req_size = qcuefi_buf_align_fields( - __field(*req_data) - __field_offs(*guid, &guid_offs) - __array_offs(*name, *name_size / sizeof(*name), &name_offs) - ); - - rsp_size = qcuefi_buf_align_fields( - __field(*rsp_data) - __field(*guid) - __array(*name, *name_size / sizeof(*name)) - ); - - req_data = kzalloc(req_size, GFP_KERNEL); - if (!req_data) { - efi_status = EFI_OUT_OF_RESOURCES; - goto out; - } - - rsp_data = kzalloc(rsp_size, GFP_KERNEL); - if (!rsp_data) { - efi_status = EFI_OUT_OF_RESOURCES; - goto out_free_req; - } - - req_data->command_id = QSEE_CMD_UEFI_GET_NEXT_VARIABLE; - req_data->guid_offset = guid_offs; - req_data->guid_size = sizeof(*guid); - req_data->name_offset = name_offs; - req_data->name_size = *name_size; - req_data->length = req_size; - - memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size); - status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name, - *name_size / sizeof(*name)); - if (status < 0) - return EFI_INVALID_PARAMETER; - - status = qcom_qseecom_app_send(qcuefi->client, req_data, req_size, rsp_data, rsp_size); - if (status) { - efi_status = EFI_DEVICE_ERROR; - goto out_free; - } - - if (rsp_data->command_id != QSEE_CMD_UEFI_GET_NEXT_VARIABLE) { - efi_status = EFI_DEVICE_ERROR; - goto out_free; - } - - if (rsp_data->length < sizeof(*rsp_data)) { - efi_status = EFI_DEVICE_ERROR; - goto out_free; - } - - if (rsp_data->status) { - dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n", - __func__, rsp_data->status); - efi_status = qsee_uefi_status_to_efi(rsp_data->status); - - /* - * If the buffer to hold the name is too small, update the - * name_size with the required size, so that callers can - * reallocate it accordingly. - */ - if (efi_status == EFI_BUFFER_TOO_SMALL) - *name_size = rsp_data->name_size; - - goto out_free; - } - - if (rsp_data->length > rsp_size) { - efi_status = EFI_DEVICE_ERROR; - goto out_free; - } - - if (rsp_data->name_offset + rsp_data->name_size > rsp_data->length) { - efi_status = EFI_DEVICE_ERROR; - goto out_free; - } - - if (rsp_data->guid_offset + rsp_data->guid_size > rsp_data->length) { - efi_status = EFI_DEVICE_ERROR; - goto out_free; - } - - if (rsp_data->name_size > *name_size) { - *name_size = rsp_data->name_size; - efi_status = EFI_BUFFER_TOO_SMALL; - goto out_free; - } - - if (rsp_data->guid_size != sizeof(*guid)) { - efi_status = EFI_DEVICE_ERROR; - goto out_free; - } - - memcpy(guid, ((void *)rsp_data) + rsp_data->guid_offset, rsp_data->guid_size); - status = ucs2_strscpy(name, ((void *)rsp_data) + rsp_data->name_offset, - rsp_data->name_size / sizeof(*name)); - *name_size = rsp_data->name_size; - - if (status < 0) { - /* - * Return EFI_DEVICE_ERROR here because the buffer size should - * have already been validated above, causing this function to - * bail with EFI_BUFFER_TOO_SMALL. - */ - return EFI_DEVICE_ERROR; - } - -out_free: - kfree(rsp_data); -out_free_req: - kfree(req_data); -out: - return efi_status; -} - -static efi_status_t qsee_uefi_query_variable_info(struct qcuefi_client *qcuefi, u32 attr, - u64 *storage_space, u64 *remaining_space, - u64 *max_variable_size) -{ - struct qsee_req_uefi_query_variable_info *req_data; - struct qsee_rsp_uefi_query_variable_info *rsp_data; - efi_status_t efi_status = EFI_SUCCESS; - int status; - - req_data = kzalloc(sizeof(*req_data), GFP_KERNEL); - if (!req_data) { - efi_status = EFI_OUT_OF_RESOURCES; - goto out; - } - - rsp_data = kzalloc(sizeof(*rsp_data), GFP_KERNEL); - if (!rsp_data) { - efi_status = EFI_OUT_OF_RESOURCES; - goto out_free_req; - } - - req_data->command_id = QSEE_CMD_UEFI_QUERY_VARIABLE_INFO; - req_data->attributes = attr; - req_data->length = sizeof(*req_data); - - status = qcom_qseecom_app_send(qcuefi->client, req_data, sizeof(*req_data), rsp_data, - sizeof(*rsp_data)); - if (status) { - efi_status = EFI_DEVICE_ERROR; - goto out_free; - } - - if (rsp_data->command_id != QSEE_CMD_UEFI_QUERY_VARIABLE_INFO) { - efi_status = EFI_DEVICE_ERROR; - goto out_free; - } - - if (rsp_data->length != sizeof(*rsp_data)) { - efi_status = EFI_DEVICE_ERROR; - goto out_free; - } - - if (rsp_data->status) { - dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n", - __func__, rsp_data->status); - efi_status = qsee_uefi_status_to_efi(rsp_data->status); - goto out_free; - } - - if (storage_space) - *storage_space = rsp_data->storage_space; - - if (remaining_space) - *remaining_space = rsp_data->remaining_space; - - if (max_variable_size) - *max_variable_size = rsp_data->max_variable_size; - -out_free: - kfree(rsp_data); -out_free_req: - kfree(req_data); -out: - return efi_status; -} - -/* -- Global efivar interface. ---------------------------------------------- */ - -static struct qcuefi_client *__qcuefi; -static DEFINE_MUTEX(__qcuefi_lock); - -static int qcuefi_set_reference(struct qcuefi_client *qcuefi) -{ - mutex_lock(&__qcuefi_lock); - - if (qcuefi && __qcuefi) { - mutex_unlock(&__qcuefi_lock); - return -EEXIST; - } - - __qcuefi = qcuefi; - - mutex_unlock(&__qcuefi_lock); - return 0; -} - -static struct qcuefi_client *qcuefi_acquire(void) -{ - mutex_lock(&__qcuefi_lock); - return __qcuefi; -} - -static void qcuefi_release(void) -{ - mutex_unlock(&__qcuefi_lock); -} - -static efi_status_t qcuefi_get_variable(efi_char16_t *name, efi_guid_t *vendor, u32 *attr, - unsigned long *data_size, void *data) -{ - struct qcuefi_client *qcuefi; - efi_status_t status; - - qcuefi = qcuefi_acquire(); - if (!qcuefi) - return EFI_NOT_READY; - - status = qsee_uefi_get_variable(qcuefi, name, vendor, attr, data_size, data); - - qcuefi_release(); - return status; -} - -static efi_status_t qcuefi_set_variable(efi_char16_t *name, efi_guid_t *vendor, - u32 attr, unsigned long data_size, void *data) -{ - struct qcuefi_client *qcuefi; - efi_status_t status; - - qcuefi = qcuefi_acquire(); - if (!qcuefi) - return EFI_NOT_READY; - - status = qsee_uefi_set_variable(qcuefi, name, vendor, attr, data_size, data); - - qcuefi_release(); - return status; -} - -static efi_status_t qcuefi_get_next_variable(unsigned long *name_size, efi_char16_t *name, - efi_guid_t *vendor) -{ - struct qcuefi_client *qcuefi; - efi_status_t status; - - qcuefi = qcuefi_acquire(); - if (!qcuefi) - return EFI_NOT_READY; - - status = qsee_uefi_get_next_variable(qcuefi, name_size, name, vendor); - - qcuefi_release(); - return status; -} - -static efi_status_t qcuefi_query_variable_info(u32 attr, u64 *storage_space, u64 *remaining_space, - u64 *max_variable_size) -{ - struct qcuefi_client *qcuefi; - efi_status_t status; - - qcuefi = qcuefi_acquire(); - if (!qcuefi) - return EFI_NOT_READY; - - status = qsee_uefi_query_variable_info(qcuefi, attr, storage_space, remaining_space, - max_variable_size); - - qcuefi_release(); - return status; -} - -static const struct efivar_operations qcom_efivar_ops = { - .get_variable = qcuefi_get_variable, - .set_variable = qcuefi_set_variable, - .get_next_variable = qcuefi_get_next_variable, - .query_variable_info = qcuefi_query_variable_info, -}; - -/* -- Driver setup. --------------------------------------------------------- */ - -static int qcom_uefisecapp_probe(struct auxiliary_device *aux_dev, - const struct auxiliary_device_id *aux_dev_id) -{ - struct qcuefi_client *qcuefi; - int status; - - qcuefi = devm_kzalloc(&aux_dev->dev, sizeof(*qcuefi), GFP_KERNEL); - if (!qcuefi) - return -ENOMEM; - - qcuefi->client = container_of(aux_dev, struct qseecom_client, aux_dev); - - auxiliary_set_drvdata(aux_dev, qcuefi); - status = qcuefi_set_reference(qcuefi); - if (status) - return status; - - status = efivars_register(&qcuefi->efivars, &qcom_efivar_ops); - if (status) - qcuefi_set_reference(NULL); - - return status; -} - -static void qcom_uefisecapp_remove(struct auxiliary_device *aux_dev) -{ - struct qcuefi_client *qcuefi = auxiliary_get_drvdata(aux_dev); - - efivars_unregister(&qcuefi->efivars); - qcuefi_set_reference(NULL); -} - -static const struct auxiliary_device_id qcom_uefisecapp_id_table[] = { - { .name = "qcom_qseecom.uefisecapp" }, - {} -}; -MODULE_DEVICE_TABLE(auxiliary, qcom_uefisecapp_id_table); - -static struct auxiliary_driver qcom_uefisecapp_driver = { - .probe = qcom_uefisecapp_probe, - .remove = qcom_uefisecapp_remove, - .id_table = qcom_uefisecapp_id_table, - .driver = { - .name = "qcom_qseecom_uefisecapp", - .probe_type = PROBE_PREFER_ASYNCHRONOUS, - }, -}; -module_auxiliary_driver(qcom_uefisecapp_driver); - -MODULE_AUTHOR("Maximilian Luz "); -MODULE_DESCRIPTION("Client driver for Qualcomm SEE UEFI Secure App"); -MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/qcom_scm-legacy.c b/drivers/firmware/qcom_scm-legacy.c deleted file mode 100644 index 029e6d117cb8..000000000000 --- a/drivers/firmware/qcom_scm-legacy.c +++ /dev/null @@ -1,246 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2010,2015,2019 The Linux Foundation. All rights reserved. - * Copyright (C) 2015 Linaro Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "qcom_scm.h" - -static DEFINE_MUTEX(qcom_scm_lock); - - -/** - * struct arm_smccc_args - * @args: The array of values used in registers in smc instruction - */ -struct arm_smccc_args { - unsigned long args[8]; -}; - - -/** - * struct scm_legacy_command - one SCM command buffer - * @len: total available memory for command and response - * @buf_offset: start of command buffer - * @resp_hdr_offset: start of response buffer - * @id: command to be executed - * @buf: buffer returned from scm_legacy_get_command_buffer() - * - * An SCM command is laid out in memory as follows: - * - * ------------------- <--- struct scm_legacy_command - * | command header | - * ------------------- <--- scm_legacy_get_command_buffer() - * | command buffer | - * ------------------- <--- struct scm_legacy_response and - * | response header | scm_legacy_command_to_response() - * ------------------- <--- scm_legacy_get_response_buffer() - * | response buffer | - * ------------------- - * - * There can be arbitrary padding between the headers and buffers so - * you should always use the appropriate scm_legacy_get_*_buffer() routines - * to access the buffers in a safe manner. - */ -struct scm_legacy_command { - __le32 len; - __le32 buf_offset; - __le32 resp_hdr_offset; - __le32 id; - __le32 buf[]; -}; - -/** - * struct scm_legacy_response - one SCM response buffer - * @len: total available memory for response - * @buf_offset: start of response data relative to start of scm_legacy_response - * @is_complete: indicates if the command has finished processing - */ -struct scm_legacy_response { - __le32 len; - __le32 buf_offset; - __le32 is_complete; -}; - -/** - * scm_legacy_command_to_response() - Get a pointer to a scm_legacy_response - * @cmd: command - * - * Returns a pointer to a response for a command. - */ -static inline struct scm_legacy_response *scm_legacy_command_to_response( - const struct scm_legacy_command *cmd) -{ - return (void *)cmd + le32_to_cpu(cmd->resp_hdr_offset); -} - -/** - * scm_legacy_get_command_buffer() - Get a pointer to a command buffer - * @cmd: command - * - * Returns a pointer to the command buffer of a command. - */ -static inline void *scm_legacy_get_command_buffer( - const struct scm_legacy_command *cmd) -{ - return (void *)cmd->buf; -} - -/** - * scm_legacy_get_response_buffer() - Get a pointer to a response buffer - * @rsp: response - * - * Returns a pointer to a response buffer of a response. - */ -static inline void *scm_legacy_get_response_buffer( - const struct scm_legacy_response *rsp) -{ - return (void *)rsp + le32_to_cpu(rsp->buf_offset); -} - -static void __scm_legacy_do(const struct arm_smccc_args *smc, - struct arm_smccc_res *res) -{ - do { - arm_smccc_smc(smc->args[0], smc->args[1], smc->args[2], - smc->args[3], smc->args[4], smc->args[5], - smc->args[6], smc->args[7], res); - } while (res->a0 == QCOM_SCM_INTERRUPTED); -} - -/** - * scm_legacy_call() - Sends a command to the SCM and waits for the command to - * finish processing. - * @dev: device - * @desc: descriptor structure containing arguments and return values - * @res: results from SMC call - * - * A note on cache maintenance: - * Note that any buffers that are expected to be accessed by the secure world - * must be flushed before invoking qcom_scm_call and invalidated in the cache - * immediately after qcom_scm_call returns. Cache maintenance on the command - * and response buffers is taken care of by qcom_scm_call; however, callers are - * responsible for any other cached buffers passed over to the secure world. - */ -int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc, - struct qcom_scm_res *res) -{ - u8 arglen = desc->arginfo & 0xf; - int ret = 0, context_id; - unsigned int i; - struct scm_legacy_command *cmd; - struct scm_legacy_response *rsp; - struct arm_smccc_args smc = {0}; - struct arm_smccc_res smc_res; - const size_t cmd_len = arglen * sizeof(__le32); - const size_t resp_len = MAX_QCOM_SCM_RETS * sizeof(__le32); - size_t alloc_len = sizeof(*cmd) + cmd_len + sizeof(*rsp) + resp_len; - dma_addr_t cmd_phys; - __le32 *arg_buf; - const __le32 *res_buf; - - cmd = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL); - if (!cmd) - return -ENOMEM; - - cmd->len = cpu_to_le32(alloc_len); - cmd->buf_offset = cpu_to_le32(sizeof(*cmd)); - cmd->resp_hdr_offset = cpu_to_le32(sizeof(*cmd) + cmd_len); - cmd->id = cpu_to_le32(SCM_LEGACY_FNID(desc->svc, desc->cmd)); - - arg_buf = scm_legacy_get_command_buffer(cmd); - for (i = 0; i < arglen; i++) - arg_buf[i] = cpu_to_le32(desc->args[i]); - - rsp = scm_legacy_command_to_response(cmd); - - cmd_phys = dma_map_single(dev, cmd, alloc_len, DMA_TO_DEVICE); - if (dma_mapping_error(dev, cmd_phys)) { - kfree(cmd); - return -ENOMEM; - } - - smc.args[0] = 1; - smc.args[1] = (unsigned long)&context_id; - smc.args[2] = cmd_phys; - - mutex_lock(&qcom_scm_lock); - __scm_legacy_do(&smc, &smc_res); - if (smc_res.a0) - ret = qcom_scm_remap_error(smc_res.a0); - mutex_unlock(&qcom_scm_lock); - if (ret) - goto out; - - do { - dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len, - sizeof(*rsp), DMA_FROM_DEVICE); - } while (!rsp->is_complete); - - dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len + - le32_to_cpu(rsp->buf_offset), - resp_len, DMA_FROM_DEVICE); - - if (res) { - res_buf = scm_legacy_get_response_buffer(rsp); - for (i = 0; i < MAX_QCOM_SCM_RETS; i++) - res->result[i] = le32_to_cpu(res_buf[i]); - } -out: - dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE); - kfree(cmd); - return ret; -} - -#define SCM_LEGACY_ATOMIC_N_REG_ARGS 5 -#define SCM_LEGACY_ATOMIC_FIRST_REG_IDX 2 -#define SCM_LEGACY_CLASS_REGISTER (0x2 << 8) -#define SCM_LEGACY_MASK_IRQS BIT(5) -#define SCM_LEGACY_ATOMIC_ID(svc, cmd, n) \ - ((SCM_LEGACY_FNID(svc, cmd) << 12) | \ - SCM_LEGACY_CLASS_REGISTER | \ - SCM_LEGACY_MASK_IRQS | \ - (n & 0xf)) - -/** - * scm_legacy_call_atomic() - Send an atomic SCM command with up to 5 arguments - * and 3 return values - * @unused: device, legacy argument, not used, can be NULL - * @desc: SCM call descriptor containing arguments - * @res: SCM call return values - * - * This shall only be used with commands that are guaranteed to be - * uninterruptable, atomic and SMP safe. - */ -int scm_legacy_call_atomic(struct device *unused, - const struct qcom_scm_desc *desc, - struct qcom_scm_res *res) -{ - int context_id; - struct arm_smccc_res smc_res; - size_t arglen = desc->arginfo & 0xf; - - BUG_ON(arglen > SCM_LEGACY_ATOMIC_N_REG_ARGS); - - arm_smccc_smc(SCM_LEGACY_ATOMIC_ID(desc->svc, desc->cmd, arglen), - (unsigned long)&context_id, - desc->args[0], desc->args[1], desc->args[2], - desc->args[3], desc->args[4], 0, &smc_res); - - if (res) { - res->result[0] = smc_res.a1; - res->result[1] = smc_res.a2; - res->result[2] = smc_res.a3; - } - - return smc_res.a0; -} diff --git a/drivers/firmware/qcom_scm-smc.c b/drivers/firmware/qcom_scm-smc.c deleted file mode 100644 index 16cf88acfa8e..000000000000 --- a/drivers/firmware/qcom_scm-smc.c +++ /dev/null @@ -1,225 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2015,2019 The Linux Foundation. All rights reserved. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "qcom_scm.h" - -/** - * struct arm_smccc_args - * @args: The array of values used in registers in smc instruction - */ -struct arm_smccc_args { - unsigned long args[8]; -}; - -static DEFINE_MUTEX(qcom_scm_lock); - -#define QCOM_SCM_EBUSY_WAIT_MS 30 -#define QCOM_SCM_EBUSY_MAX_RETRY 20 - -#define SCM_SMC_N_REG_ARGS 4 -#define SCM_SMC_FIRST_EXT_IDX (SCM_SMC_N_REG_ARGS - 1) -#define SCM_SMC_N_EXT_ARGS (MAX_QCOM_SCM_ARGS - SCM_SMC_N_REG_ARGS + 1) -#define SCM_SMC_FIRST_REG_IDX 2 -#define SCM_SMC_LAST_REG_IDX (SCM_SMC_FIRST_REG_IDX + SCM_SMC_N_REG_ARGS - 1) - -static void __scm_smc_do_quirk(const struct arm_smccc_args *smc, - struct arm_smccc_res *res) -{ - unsigned long a0 = smc->args[0]; - struct arm_smccc_quirk quirk = { .id = ARM_SMCCC_QUIRK_QCOM_A6 }; - - quirk.state.a6 = 0; - - do { - arm_smccc_smc_quirk(a0, smc->args[1], smc->args[2], - smc->args[3], smc->args[4], smc->args[5], - quirk.state.a6, smc->args[7], res, &quirk); - - if (res->a0 == QCOM_SCM_INTERRUPTED) - a0 = res->a0; - - } while (res->a0 == QCOM_SCM_INTERRUPTED); -} - -static void fill_wq_resume_args(struct arm_smccc_args *resume, u32 smc_call_ctx) -{ - memset(resume->args, 0, sizeof(resume->args[0]) * ARRAY_SIZE(resume->args)); - - resume->args[0] = ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, - ARM_SMCCC_SMC_64, ARM_SMCCC_OWNER_SIP, - SCM_SMC_FNID(QCOM_SCM_SVC_WAITQ, QCOM_SCM_WAITQ_RESUME)); - - resume->args[1] = QCOM_SCM_ARGS(1); - - resume->args[2] = smc_call_ctx; -} - -int scm_get_wq_ctx(u32 *wq_ctx, u32 *flags, u32 *more_pending) -{ - int ret; - struct arm_smccc_res get_wq_res; - struct arm_smccc_args get_wq_ctx = {0}; - - get_wq_ctx.args[0] = ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, - ARM_SMCCC_SMC_64, ARM_SMCCC_OWNER_SIP, - SCM_SMC_FNID(QCOM_SCM_SVC_WAITQ, QCOM_SCM_WAITQ_GET_WQ_CTX)); - - /* Guaranteed to return only success or error, no WAITQ_* */ - __scm_smc_do_quirk(&get_wq_ctx, &get_wq_res); - ret = get_wq_res.a0; - if (ret) - return ret; - - *wq_ctx = get_wq_res.a1; - *flags = get_wq_res.a2; - *more_pending = get_wq_res.a3; - - return 0; -} - -static int __scm_smc_do_quirk_handle_waitq(struct device *dev, struct arm_smccc_args *waitq, - struct arm_smccc_res *res) -{ - int ret; - u32 wq_ctx, smc_call_ctx; - struct arm_smccc_args resume; - struct arm_smccc_args *smc = waitq; - - do { - __scm_smc_do_quirk(smc, res); - - if (res->a0 == QCOM_SCM_WAITQ_SLEEP) { - wq_ctx = res->a1; - smc_call_ctx = res->a2; - - ret = qcom_scm_wait_for_wq_completion(wq_ctx); - if (ret) - return ret; - - fill_wq_resume_args(&resume, smc_call_ctx); - smc = &resume; - } - } while (res->a0 == QCOM_SCM_WAITQ_SLEEP); - - return 0; -} - -static int __scm_smc_do(struct device *dev, struct arm_smccc_args *smc, - struct arm_smccc_res *res, bool atomic) -{ - int ret, retry_count = 0; - - if (atomic) { - __scm_smc_do_quirk(smc, res); - return 0; - } - - do { - mutex_lock(&qcom_scm_lock); - - ret = __scm_smc_do_quirk_handle_waitq(dev, smc, res); - - mutex_unlock(&qcom_scm_lock); - - if (ret) - return ret; - - if (res->a0 == QCOM_SCM_V2_EBUSY) { - if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY) - break; - msleep(QCOM_SCM_EBUSY_WAIT_MS); - } - } while (res->a0 == QCOM_SCM_V2_EBUSY); - - return 0; -} - - -int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc, - enum qcom_scm_convention qcom_convention, - struct qcom_scm_res *res, bool atomic) -{ - int arglen = desc->arginfo & 0xf; - int i, ret; - dma_addr_t args_phys = 0; - void *args_virt = NULL; - size_t alloc_len; - gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL; - u32 smccc_call_type = atomic ? ARM_SMCCC_FAST_CALL : ARM_SMCCC_STD_CALL; - u32 qcom_smccc_convention = (qcom_convention == SMC_CONVENTION_ARM_32) ? - ARM_SMCCC_SMC_32 : ARM_SMCCC_SMC_64; - struct arm_smccc_res smc_res; - struct arm_smccc_args smc = {0}; - - smc.args[0] = ARM_SMCCC_CALL_VAL( - smccc_call_type, - qcom_smccc_convention, - desc->owner, - SCM_SMC_FNID(desc->svc, desc->cmd)); - smc.args[1] = desc->arginfo; - for (i = 0; i < SCM_SMC_N_REG_ARGS; i++) - smc.args[i + SCM_SMC_FIRST_REG_IDX] = desc->args[i]; - - if (unlikely(arglen > SCM_SMC_N_REG_ARGS)) { - alloc_len = SCM_SMC_N_EXT_ARGS * sizeof(u64); - args_virt = kzalloc(PAGE_ALIGN(alloc_len), flag); - - if (!args_virt) - return -ENOMEM; - - if (qcom_smccc_convention == ARM_SMCCC_SMC_32) { - __le32 *args = args_virt; - - for (i = 0; i < SCM_SMC_N_EXT_ARGS; i++) - args[i] = cpu_to_le32(desc->args[i + - SCM_SMC_FIRST_EXT_IDX]); - } else { - __le64 *args = args_virt; - - for (i = 0; i < SCM_SMC_N_EXT_ARGS; i++) - args[i] = cpu_to_le64(desc->args[i + - SCM_SMC_FIRST_EXT_IDX]); - } - - args_phys = dma_map_single(dev, args_virt, alloc_len, - DMA_TO_DEVICE); - - if (dma_mapping_error(dev, args_phys)) { - kfree(args_virt); - return -ENOMEM; - } - - smc.args[SCM_SMC_LAST_REG_IDX] = args_phys; - } - - /* ret error check follows after args_virt cleanup*/ - ret = __scm_smc_do(dev, &smc, &smc_res, atomic); - - if (args_virt) { - dma_unmap_single(dev, args_phys, alloc_len, DMA_TO_DEVICE); - kfree(args_virt); - } - - if (ret) - return ret; - - if (res) { - res->result[0] = smc_res.a1; - res->result[1] = smc_res.a2; - res->result[2] = smc_res.a3; - } - - return (long)smc_res.a0 ? qcom_scm_remap_error(smc_res.a0) : 0; - -} diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c deleted file mode 100644 index 520de9b5633a..000000000000 --- a/drivers/firmware/qcom_scm.c +++ /dev/null @@ -1,1943 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2010,2015,2019 The Linux Foundation. All rights reserved. - * Copyright (C) 2015 Linaro Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "qcom_scm.h" - -static bool download_mode = IS_ENABLED(CONFIG_QCOM_SCM_DOWNLOAD_MODE_DEFAULT); -module_param(download_mode, bool, 0); - -struct qcom_scm { - struct device *dev; - struct clk *core_clk; - struct clk *iface_clk; - struct clk *bus_clk; - struct icc_path *path; - struct completion waitq_comp; - struct reset_controller_dev reset; - - /* control access to the interconnect path */ - struct mutex scm_bw_lock; - int scm_vote_count; - - u64 dload_mode_addr; -}; - -struct qcom_scm_current_perm_info { - __le32 vmid; - __le32 perm; - __le64 ctx; - __le32 ctx_size; - __le32 unused; -}; - -struct qcom_scm_mem_map_info { - __le64 mem_addr; - __le64 mem_size; -}; - -/** - * struct qcom_scm_qseecom_resp - QSEECOM SCM call response. - * @result: Result or status of the SCM call. See &enum qcom_scm_qseecom_result. - * @resp_type: Type of the response. See &enum qcom_scm_qseecom_resp_type. - * @data: Response data. The type of this data is given in @resp_type. - */ -struct qcom_scm_qseecom_resp { - u64 result; - u64 resp_type; - u64 data; -}; - -enum qcom_scm_qseecom_result { - QSEECOM_RESULT_SUCCESS = 0, - QSEECOM_RESULT_INCOMPLETE = 1, - QSEECOM_RESULT_BLOCKED_ON_LISTENER = 2, - QSEECOM_RESULT_FAILURE = 0xFFFFFFFF, -}; - -enum qcom_scm_qseecom_resp_type { - QSEECOM_SCM_RES_APP_ID = 0xEE01, - QSEECOM_SCM_RES_QSEOS_LISTENER_ID = 0xEE02, -}; - -enum qcom_scm_qseecom_tz_owner { - QSEECOM_TZ_OWNER_SIP = 2, - QSEECOM_TZ_OWNER_TZ_APPS = 48, - QSEECOM_TZ_OWNER_QSEE_OS = 50 -}; - -enum qcom_scm_qseecom_tz_svc { - QSEECOM_TZ_SVC_APP_ID_PLACEHOLDER = 0, - QSEECOM_TZ_SVC_APP_MGR = 1, - QSEECOM_TZ_SVC_INFO = 6, -}; - -enum qcom_scm_qseecom_tz_cmd_app { - QSEECOM_TZ_CMD_APP_SEND = 1, - QSEECOM_TZ_CMD_APP_LOOKUP = 3, -}; - -enum qcom_scm_qseecom_tz_cmd_info { - QSEECOM_TZ_CMD_INFO_VERSION = 3, -}; - -#define QSEECOM_MAX_APP_NAME_SIZE 64 - -/* Each bit configures cold/warm boot address for one of the 4 CPUs */ -static const u8 qcom_scm_cpu_cold_bits[QCOM_SCM_BOOT_MAX_CPUS] = { - 0, BIT(0), BIT(3), BIT(5) -}; -static const u8 qcom_scm_cpu_warm_bits[QCOM_SCM_BOOT_MAX_CPUS] = { - BIT(2), BIT(1), BIT(4), BIT(6) -}; - -#define QCOM_SMC_WAITQ_FLAG_WAKE_ONE BIT(0) -#define QCOM_SMC_WAITQ_FLAG_WAKE_ALL BIT(1) - -static const char * const qcom_scm_convention_names[] = { - [SMC_CONVENTION_UNKNOWN] = "unknown", - [SMC_CONVENTION_ARM_32] = "smc arm 32", - [SMC_CONVENTION_ARM_64] = "smc arm 64", - [SMC_CONVENTION_LEGACY] = "smc legacy", -}; - -static struct qcom_scm *__scm; - -static int qcom_scm_clk_enable(void) -{ - int ret; - - ret = clk_prepare_enable(__scm->core_clk); - if (ret) - goto bail; - - ret = clk_prepare_enable(__scm->iface_clk); - if (ret) - goto disable_core; - - ret = clk_prepare_enable(__scm->bus_clk); - if (ret) - goto disable_iface; - - return 0; - -disable_iface: - clk_disable_unprepare(__scm->iface_clk); -disable_core: - clk_disable_unprepare(__scm->core_clk); -bail: - return ret; -} - -static void qcom_scm_clk_disable(void) -{ - clk_disable_unprepare(__scm->core_clk); - clk_disable_unprepare(__scm->iface_clk); - clk_disable_unprepare(__scm->bus_clk); -} - -static int qcom_scm_bw_enable(void) -{ - int ret = 0; - - if (!__scm->path) - return 0; - - if (IS_ERR(__scm->path)) - return -EINVAL; - - mutex_lock(&__scm->scm_bw_lock); - if (!__scm->scm_vote_count) { - ret = icc_set_bw(__scm->path, 0, UINT_MAX); - if (ret < 0) { - dev_err(__scm->dev, "failed to set bandwidth request\n"); - goto err_bw; - } - } - __scm->scm_vote_count++; -err_bw: - mutex_unlock(&__scm->scm_bw_lock); - - return ret; -} - -static void qcom_scm_bw_disable(void) -{ - if (IS_ERR_OR_NULL(__scm->path)) - return; - - mutex_lock(&__scm->scm_bw_lock); - if (__scm->scm_vote_count-- == 1) - icc_set_bw(__scm->path, 0, 0); - mutex_unlock(&__scm->scm_bw_lock); -} - -enum qcom_scm_convention qcom_scm_convention = SMC_CONVENTION_UNKNOWN; -static DEFINE_SPINLOCK(scm_query_lock); - -static enum qcom_scm_convention __get_convention(void) -{ - unsigned long flags; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_INFO, - .cmd = QCOM_SCM_INFO_IS_CALL_AVAIL, - .args[0] = SCM_SMC_FNID(QCOM_SCM_SVC_INFO, - QCOM_SCM_INFO_IS_CALL_AVAIL) | - (ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT), - .arginfo = QCOM_SCM_ARGS(1), - .owner = ARM_SMCCC_OWNER_SIP, - }; - struct qcom_scm_res res; - enum qcom_scm_convention probed_convention; - int ret; - bool forced = false; - - if (likely(qcom_scm_convention != SMC_CONVENTION_UNKNOWN)) - return qcom_scm_convention; - - /* - * Per the "SMC calling convention specification", the 64-bit calling - * convention can only be used when the client is 64-bit, otherwise - * system will encounter the undefined behaviour. - */ -#if IS_ENABLED(CONFIG_ARM64) - /* - * Device isn't required as there is only one argument - no device - * needed to dma_map_single to secure world - */ - probed_convention = SMC_CONVENTION_ARM_64; - ret = __scm_smc_call(NULL, &desc, probed_convention, &res, true); - if (!ret && res.result[0] == 1) - goto found; - - /* - * Some SC7180 firmwares didn't implement the - * QCOM_SCM_INFO_IS_CALL_AVAIL call, so we fallback to forcing ARM_64 - * calling conventions on these firmwares. Luckily we don't make any - * early calls into the firmware on these SoCs so the device pointer - * will be valid here to check if the compatible matches. - */ - if (of_device_is_compatible(__scm ? __scm->dev->of_node : NULL, "qcom,scm-sc7180")) { - forced = true; - goto found; - } -#endif - - probed_convention = SMC_CONVENTION_ARM_32; - ret = __scm_smc_call(NULL, &desc, probed_convention, &res, true); - if (!ret && res.result[0] == 1) - goto found; - - probed_convention = SMC_CONVENTION_LEGACY; -found: - spin_lock_irqsave(&scm_query_lock, flags); - if (probed_convention != qcom_scm_convention) { - qcom_scm_convention = probed_convention; - pr_info("qcom_scm: convention: %s%s\n", - qcom_scm_convention_names[qcom_scm_convention], - forced ? " (forced)" : ""); - } - spin_unlock_irqrestore(&scm_query_lock, flags); - - return qcom_scm_convention; -} - -/** - * qcom_scm_call() - Invoke a syscall in the secure world - * @dev: device - * @desc: Descriptor structure containing arguments and return values - * @res: Structure containing results from SMC/HVC call - * - * Sends a command to the SCM and waits for the command to finish processing. - * This should *only* be called in pre-emptible context. - */ -static int qcom_scm_call(struct device *dev, const struct qcom_scm_desc *desc, - struct qcom_scm_res *res) -{ - might_sleep(); - switch (__get_convention()) { - case SMC_CONVENTION_ARM_32: - case SMC_CONVENTION_ARM_64: - return scm_smc_call(dev, desc, res, false); - case SMC_CONVENTION_LEGACY: - return scm_legacy_call(dev, desc, res); - default: - pr_err("Unknown current SCM calling convention.\n"); - return -EINVAL; - } -} - -/** - * qcom_scm_call_atomic() - atomic variation of qcom_scm_call() - * @dev: device - * @desc: Descriptor structure containing arguments and return values - * @res: Structure containing results from SMC/HVC call - * - * Sends a command to the SCM and waits for the command to finish processing. - * This can be called in atomic context. - */ -static int qcom_scm_call_atomic(struct device *dev, - const struct qcom_scm_desc *desc, - struct qcom_scm_res *res) -{ - switch (__get_convention()) { - case SMC_CONVENTION_ARM_32: - case SMC_CONVENTION_ARM_64: - return scm_smc_call(dev, desc, res, true); - case SMC_CONVENTION_LEGACY: - return scm_legacy_call_atomic(dev, desc, res); - default: - pr_err("Unknown current SCM calling convention.\n"); - return -EINVAL; - } -} - -static bool __qcom_scm_is_call_available(struct device *dev, u32 svc_id, - u32 cmd_id) -{ - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_INFO, - .cmd = QCOM_SCM_INFO_IS_CALL_AVAIL, - .owner = ARM_SMCCC_OWNER_SIP, - }; - struct qcom_scm_res res; - - desc.arginfo = QCOM_SCM_ARGS(1); - switch (__get_convention()) { - case SMC_CONVENTION_ARM_32: - case SMC_CONVENTION_ARM_64: - desc.args[0] = SCM_SMC_FNID(svc_id, cmd_id) | - (ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT); - break; - case SMC_CONVENTION_LEGACY: - desc.args[0] = SCM_LEGACY_FNID(svc_id, cmd_id); - break; - default: - pr_err("Unknown SMC convention being used\n"); - return false; - } - - ret = qcom_scm_call(dev, &desc, &res); - - return ret ? false : !!res.result[0]; -} - -static int qcom_scm_set_boot_addr(void *entry, const u8 *cpu_bits) -{ - int cpu; - unsigned int flags = 0; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_BOOT, - .cmd = QCOM_SCM_BOOT_SET_ADDR, - .arginfo = QCOM_SCM_ARGS(2), - .owner = ARM_SMCCC_OWNER_SIP, - }; - - for_each_present_cpu(cpu) { - if (cpu >= QCOM_SCM_BOOT_MAX_CPUS) - return -EINVAL; - flags |= cpu_bits[cpu]; - } - - desc.args[0] = flags; - desc.args[1] = virt_to_phys(entry); - - return qcom_scm_call_atomic(__scm ? __scm->dev : NULL, &desc, NULL); -} - -static int qcom_scm_set_boot_addr_mc(void *entry, unsigned int flags) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_BOOT, - .cmd = QCOM_SCM_BOOT_SET_ADDR_MC, - .owner = ARM_SMCCC_OWNER_SIP, - .arginfo = QCOM_SCM_ARGS(6), - .args = { - virt_to_phys(entry), - /* Apply to all CPUs in all affinity levels */ - ~0ULL, ~0ULL, ~0ULL, ~0ULL, - flags, - }, - }; - - /* Need a device for DMA of the additional arguments */ - if (!__scm || __get_convention() == SMC_CONVENTION_LEGACY) - return -EOPNOTSUPP; - - return qcom_scm_call(__scm->dev, &desc, NULL); -} - -/** - * qcom_scm_set_warm_boot_addr() - Set the warm boot address for all cpus - * @entry: Entry point function for the cpus - * - * Set the Linux entry point for the SCM to transfer control to when coming - * out of a power down. CPU power down may be executed on cpuidle or hotplug. - */ -int qcom_scm_set_warm_boot_addr(void *entry) -{ - if (qcom_scm_set_boot_addr_mc(entry, QCOM_SCM_BOOT_MC_FLAG_WARMBOOT)) - /* Fallback to old SCM call */ - return qcom_scm_set_boot_addr(entry, qcom_scm_cpu_warm_bits); - return 0; -} -EXPORT_SYMBOL_GPL(qcom_scm_set_warm_boot_addr); - -/** - * qcom_scm_set_cold_boot_addr() - Set the cold boot address for all cpus - * @entry: Entry point function for the cpus - */ -int qcom_scm_set_cold_boot_addr(void *entry) -{ - if (qcom_scm_set_boot_addr_mc(entry, QCOM_SCM_BOOT_MC_FLAG_COLDBOOT)) - /* Fallback to old SCM call */ - return qcom_scm_set_boot_addr(entry, qcom_scm_cpu_cold_bits); - return 0; -} -EXPORT_SYMBOL_GPL(qcom_scm_set_cold_boot_addr); - -/** - * qcom_scm_cpu_power_down() - Power down the cpu - * @flags: Flags to flush cache - * - * This is an end point to power down cpu. If there was a pending interrupt, - * the control would return from this function, otherwise, the cpu jumps to the - * warm boot entry point set for this cpu upon reset. - */ -void qcom_scm_cpu_power_down(u32 flags) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_BOOT, - .cmd = QCOM_SCM_BOOT_TERMINATE_PC, - .args[0] = flags & QCOM_SCM_FLUSH_FLAG_MASK, - .arginfo = QCOM_SCM_ARGS(1), - .owner = ARM_SMCCC_OWNER_SIP, - }; - - qcom_scm_call_atomic(__scm ? __scm->dev : NULL, &desc, NULL); -} -EXPORT_SYMBOL_GPL(qcom_scm_cpu_power_down); - -int qcom_scm_set_remote_state(u32 state, u32 id) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_BOOT, - .cmd = QCOM_SCM_BOOT_SET_REMOTE_STATE, - .arginfo = QCOM_SCM_ARGS(2), - .args[0] = state, - .args[1] = id, - .owner = ARM_SMCCC_OWNER_SIP, - }; - struct qcom_scm_res res; - int ret; - - ret = qcom_scm_call(__scm->dev, &desc, &res); - - return ret ? : res.result[0]; -} -EXPORT_SYMBOL_GPL(qcom_scm_set_remote_state); - -static int qcom_scm_disable_sdi(void) -{ - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_BOOT, - .cmd = QCOM_SCM_BOOT_SDI_CONFIG, - .args[0] = 1, /* Disable watchdog debug */ - .args[1] = 0, /* Disable SDI */ - .arginfo = QCOM_SCM_ARGS(2), - .owner = ARM_SMCCC_OWNER_SIP, - }; - struct qcom_scm_res res; - - ret = qcom_scm_clk_enable(); - if (ret) - return ret; - ret = qcom_scm_call(__scm->dev, &desc, &res); - - qcom_scm_clk_disable(); - - return ret ? : res.result[0]; -} - -static int __qcom_scm_set_dload_mode(struct device *dev, bool enable) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_BOOT, - .cmd = QCOM_SCM_BOOT_SET_DLOAD_MODE, - .arginfo = QCOM_SCM_ARGS(2), - .args[0] = QCOM_SCM_BOOT_SET_DLOAD_MODE, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - desc.args[1] = enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0; - - return qcom_scm_call_atomic(__scm->dev, &desc, NULL); -} - -static void qcom_scm_set_download_mode(bool enable) -{ - bool avail; - int ret = 0; - - avail = __qcom_scm_is_call_available(__scm->dev, - QCOM_SCM_SVC_BOOT, - QCOM_SCM_BOOT_SET_DLOAD_MODE); - if (avail) { - ret = __qcom_scm_set_dload_mode(__scm->dev, enable); - } else if (__scm->dload_mode_addr) { - ret = qcom_scm_io_writel(__scm->dload_mode_addr, - enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0); - } else { - dev_err(__scm->dev, - "No available mechanism for setting download mode\n"); - } - - if (ret) - dev_err(__scm->dev, "failed to set download mode: %d\n", ret); -} - -/** - * qcom_scm_pas_init_image() - Initialize peripheral authentication service - * state machine for a given peripheral, using the - * metadata - * @peripheral: peripheral id - * @metadata: pointer to memory containing ELF header, program header table - * and optional blob of data used for authenticating the metadata - * and the rest of the firmware - * @size: size of the metadata - * @ctx: optional metadata context - * - * Return: 0 on success. - * - * Upon successful return, the PAS metadata context (@ctx) will be used to - * track the metadata allocation, this needs to be released by invoking - * qcom_scm_pas_metadata_release() by the caller. - */ -int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size, - struct qcom_scm_pas_metadata *ctx) -{ - dma_addr_t mdata_phys; - void *mdata_buf; - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_PIL, - .cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE, - .arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW), - .args[0] = peripheral, - .owner = ARM_SMCCC_OWNER_SIP, - }; - struct qcom_scm_res res; - - /* - * During the scm call memory protection will be enabled for the meta - * data blob, so make sure it's physically contiguous, 4K aligned and - * non-cachable to avoid XPU violations. - */ - mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys, - GFP_KERNEL); - if (!mdata_buf) { - dev_err(__scm->dev, "Allocation of metadata buffer failed.\n"); - return -ENOMEM; - } - memcpy(mdata_buf, metadata, size); - - ret = qcom_scm_clk_enable(); - if (ret) - goto out; - - ret = qcom_scm_bw_enable(); - if (ret) - return ret; - - desc.args[1] = mdata_phys; - - ret = qcom_scm_call(__scm->dev, &desc, &res); - - qcom_scm_bw_disable(); - qcom_scm_clk_disable(); - -out: - if (ret < 0 || !ctx) { - dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys); - } else if (ctx) { - ctx->ptr = mdata_buf; - ctx->phys = mdata_phys; - ctx->size = size; - } - - return ret ? : res.result[0]; -} -EXPORT_SYMBOL_GPL(qcom_scm_pas_init_image); - -/** - * qcom_scm_pas_metadata_release() - release metadata context - * @ctx: metadata context - */ -void qcom_scm_pas_metadata_release(struct qcom_scm_pas_metadata *ctx) -{ - if (!ctx->ptr) - return; - - dma_free_coherent(__scm->dev, ctx->size, ctx->ptr, ctx->phys); - - ctx->ptr = NULL; - ctx->phys = 0; - ctx->size = 0; -} -EXPORT_SYMBOL_GPL(qcom_scm_pas_metadata_release); - -/** - * qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral - * for firmware loading - * @peripheral: peripheral id - * @addr: start address of memory area to prepare - * @size: size of the memory area to prepare - * - * Returns 0 on success. - */ -int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size) -{ - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_PIL, - .cmd = QCOM_SCM_PIL_PAS_MEM_SETUP, - .arginfo = QCOM_SCM_ARGS(3), - .args[0] = peripheral, - .args[1] = addr, - .args[2] = size, - .owner = ARM_SMCCC_OWNER_SIP, - }; - struct qcom_scm_res res; - - ret = qcom_scm_clk_enable(); - if (ret) - return ret; - - ret = qcom_scm_bw_enable(); - if (ret) - return ret; - - ret = qcom_scm_call(__scm->dev, &desc, &res); - qcom_scm_bw_disable(); - qcom_scm_clk_disable(); - - return ret ? : res.result[0]; -} -EXPORT_SYMBOL_GPL(qcom_scm_pas_mem_setup); - -/** - * qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware - * and reset the remote processor - * @peripheral: peripheral id - * - * Return 0 on success. - */ -int qcom_scm_pas_auth_and_reset(u32 peripheral) -{ - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_PIL, - .cmd = QCOM_SCM_PIL_PAS_AUTH_AND_RESET, - .arginfo = QCOM_SCM_ARGS(1), - .args[0] = peripheral, - .owner = ARM_SMCCC_OWNER_SIP, - }; - struct qcom_scm_res res; - - ret = qcom_scm_clk_enable(); - if (ret) - return ret; - - ret = qcom_scm_bw_enable(); - if (ret) - return ret; - - ret = qcom_scm_call(__scm->dev, &desc, &res); - qcom_scm_bw_disable(); - qcom_scm_clk_disable(); - - return ret ? : res.result[0]; -} -EXPORT_SYMBOL_GPL(qcom_scm_pas_auth_and_reset); - -/** - * qcom_scm_pas_shutdown() - Shut down the remote processor - * @peripheral: peripheral id - * - * Returns 0 on success. - */ -int qcom_scm_pas_shutdown(u32 peripheral) -{ - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_PIL, - .cmd = QCOM_SCM_PIL_PAS_SHUTDOWN, - .arginfo = QCOM_SCM_ARGS(1), - .args[0] = peripheral, - .owner = ARM_SMCCC_OWNER_SIP, - }; - struct qcom_scm_res res; - - ret = qcom_scm_clk_enable(); - if (ret) - return ret; - - ret = qcom_scm_bw_enable(); - if (ret) - return ret; - - ret = qcom_scm_call(__scm->dev, &desc, &res); - - qcom_scm_bw_disable(); - qcom_scm_clk_disable(); - - return ret ? : res.result[0]; -} -EXPORT_SYMBOL_GPL(qcom_scm_pas_shutdown); - -/** - * qcom_scm_pas_supported() - Check if the peripheral authentication service is - * available for the given peripherial - * @peripheral: peripheral id - * - * Returns true if PAS is supported for this peripheral, otherwise false. - */ -bool qcom_scm_pas_supported(u32 peripheral) -{ - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_PIL, - .cmd = QCOM_SCM_PIL_PAS_IS_SUPPORTED, - .arginfo = QCOM_SCM_ARGS(1), - .args[0] = peripheral, - .owner = ARM_SMCCC_OWNER_SIP, - }; - struct qcom_scm_res res; - - if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL, - QCOM_SCM_PIL_PAS_IS_SUPPORTED)) - return false; - - ret = qcom_scm_call(__scm->dev, &desc, &res); - - return ret ? false : !!res.result[0]; -} -EXPORT_SYMBOL_GPL(qcom_scm_pas_supported); - -static int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_PIL, - .cmd = QCOM_SCM_PIL_PAS_MSS_RESET, - .arginfo = QCOM_SCM_ARGS(2), - .args[0] = reset, - .args[1] = 0, - .owner = ARM_SMCCC_OWNER_SIP, - }; - struct qcom_scm_res res; - int ret; - - ret = qcom_scm_call(__scm->dev, &desc, &res); - - return ret ? : res.result[0]; -} - -static int qcom_scm_pas_reset_assert(struct reset_controller_dev *rcdev, - unsigned long idx) -{ - if (idx != 0) - return -EINVAL; - - return __qcom_scm_pas_mss_reset(__scm->dev, 1); -} - -static int qcom_scm_pas_reset_deassert(struct reset_controller_dev *rcdev, - unsigned long idx) -{ - if (idx != 0) - return -EINVAL; - - return __qcom_scm_pas_mss_reset(__scm->dev, 0); -} - -static const struct reset_control_ops qcom_scm_pas_reset_ops = { - .assert = qcom_scm_pas_reset_assert, - .deassert = qcom_scm_pas_reset_deassert, -}; - -int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_IO, - .cmd = QCOM_SCM_IO_READ, - .arginfo = QCOM_SCM_ARGS(1), - .args[0] = addr, - .owner = ARM_SMCCC_OWNER_SIP, - }; - struct qcom_scm_res res; - int ret; - - - ret = qcom_scm_call_atomic(__scm->dev, &desc, &res); - if (ret >= 0) - *val = res.result[0]; - - return ret < 0 ? ret : 0; -} -EXPORT_SYMBOL_GPL(qcom_scm_io_readl); - -int qcom_scm_io_writel(phys_addr_t addr, unsigned int val) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_IO, - .cmd = QCOM_SCM_IO_WRITE, - .arginfo = QCOM_SCM_ARGS(2), - .args[0] = addr, - .args[1] = val, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - return qcom_scm_call_atomic(__scm->dev, &desc, NULL); -} -EXPORT_SYMBOL_GPL(qcom_scm_io_writel); - -/** - * qcom_scm_restore_sec_cfg_available() - Check if secure environment - * supports restore security config interface. - * - * Return true if restore-cfg interface is supported, false if not. - */ -bool qcom_scm_restore_sec_cfg_available(void) -{ - return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_MP, - QCOM_SCM_MP_RESTORE_SEC_CFG); -} -EXPORT_SYMBOL_GPL(qcom_scm_restore_sec_cfg_available); - -int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_MP, - .cmd = QCOM_SCM_MP_RESTORE_SEC_CFG, - .arginfo = QCOM_SCM_ARGS(2), - .args[0] = device_id, - .args[1] = spare, - .owner = ARM_SMCCC_OWNER_SIP, - }; - struct qcom_scm_res res; - int ret; - - ret = qcom_scm_call(__scm->dev, &desc, &res); - - return ret ? : res.result[0]; -} -EXPORT_SYMBOL_GPL(qcom_scm_restore_sec_cfg); - -int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_MP, - .cmd = QCOM_SCM_MP_IOMMU_SECURE_PTBL_SIZE, - .arginfo = QCOM_SCM_ARGS(1), - .args[0] = spare, - .owner = ARM_SMCCC_OWNER_SIP, - }; - struct qcom_scm_res res; - int ret; - - ret = qcom_scm_call(__scm->dev, &desc, &res); - - if (size) - *size = res.result[0]; - - return ret ? : res.result[1]; -} -EXPORT_SYMBOL_GPL(qcom_scm_iommu_secure_ptbl_size); - -int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_MP, - .cmd = QCOM_SCM_MP_IOMMU_SECURE_PTBL_INIT, - .arginfo = QCOM_SCM_ARGS(3, QCOM_SCM_RW, QCOM_SCM_VAL, - QCOM_SCM_VAL), - .args[0] = addr, - .args[1] = size, - .args[2] = spare, - .owner = ARM_SMCCC_OWNER_SIP, - }; - int ret; - - ret = qcom_scm_call(__scm->dev, &desc, NULL); - - /* the pg table has been initialized already, ignore the error */ - if (ret == -EPERM) - ret = 0; - - return ret; -} -EXPORT_SYMBOL_GPL(qcom_scm_iommu_secure_ptbl_init); - -int qcom_scm_iommu_set_cp_pool_size(u32 spare, u32 size) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_MP, - .cmd = QCOM_SCM_MP_IOMMU_SET_CP_POOL_SIZE, - .arginfo = QCOM_SCM_ARGS(2), - .args[0] = size, - .args[1] = spare, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - return qcom_scm_call(__scm->dev, &desc, NULL); -} -EXPORT_SYMBOL_GPL(qcom_scm_iommu_set_cp_pool_size); - -int qcom_scm_mem_protect_video_var(u32 cp_start, u32 cp_size, - u32 cp_nonpixel_start, - u32 cp_nonpixel_size) -{ - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_MP, - .cmd = QCOM_SCM_MP_VIDEO_VAR, - .arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_VAL, QCOM_SCM_VAL, - QCOM_SCM_VAL, QCOM_SCM_VAL), - .args[0] = cp_start, - .args[1] = cp_size, - .args[2] = cp_nonpixel_start, - .args[3] = cp_nonpixel_size, - .owner = ARM_SMCCC_OWNER_SIP, - }; - struct qcom_scm_res res; - - ret = qcom_scm_call(__scm->dev, &desc, &res); - - return ret ? : res.result[0]; -} -EXPORT_SYMBOL_GPL(qcom_scm_mem_protect_video_var); - -static int __qcom_scm_assign_mem(struct device *dev, phys_addr_t mem_region, - size_t mem_sz, phys_addr_t src, size_t src_sz, - phys_addr_t dest, size_t dest_sz) -{ - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_MP, - .cmd = QCOM_SCM_MP_ASSIGN, - .arginfo = QCOM_SCM_ARGS(7, QCOM_SCM_RO, QCOM_SCM_VAL, - QCOM_SCM_RO, QCOM_SCM_VAL, QCOM_SCM_RO, - QCOM_SCM_VAL, QCOM_SCM_VAL), - .args[0] = mem_region, - .args[1] = mem_sz, - .args[2] = src, - .args[3] = src_sz, - .args[4] = dest, - .args[5] = dest_sz, - .args[6] = 0, - .owner = ARM_SMCCC_OWNER_SIP, - }; - struct qcom_scm_res res; - - ret = qcom_scm_call(dev, &desc, &res); - - return ret ? : res.result[0]; -} - -/** - * qcom_scm_assign_mem() - Make a secure call to reassign memory ownership - * @mem_addr: mem region whose ownership need to be reassigned - * @mem_sz: size of the region. - * @srcvm: vmid for current set of owners, each set bit in - * flag indicate a unique owner - * @newvm: array having new owners and corresponding permission - * flags - * @dest_cnt: number of owners in next set. - * - * Return negative errno on failure or 0 on success with @srcvm updated. - */ -int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, - u64 *srcvm, - const struct qcom_scm_vmperm *newvm, - unsigned int dest_cnt) -{ - struct qcom_scm_current_perm_info *destvm; - struct qcom_scm_mem_map_info *mem_to_map; - phys_addr_t mem_to_map_phys; - phys_addr_t dest_phys; - dma_addr_t ptr_phys; - size_t mem_to_map_sz; - size_t dest_sz; - size_t src_sz; - size_t ptr_sz; - int next_vm; - __le32 *src; - void *ptr; - int ret, i, b; - u64 srcvm_bits = *srcvm; - - src_sz = hweight64(srcvm_bits) * sizeof(*src); - mem_to_map_sz = sizeof(*mem_to_map); - dest_sz = dest_cnt * sizeof(*destvm); - ptr_sz = ALIGN(src_sz, SZ_64) + ALIGN(mem_to_map_sz, SZ_64) + - ALIGN(dest_sz, SZ_64); - - ptr = dma_alloc_coherent(__scm->dev, ptr_sz, &ptr_phys, GFP_KERNEL); - if (!ptr) - return -ENOMEM; - - /* Fill source vmid detail */ - src = ptr; - i = 0; - for (b = 0; b < BITS_PER_TYPE(u64); b++) { - if (srcvm_bits & BIT(b)) - src[i++] = cpu_to_le32(b); - } - - /* Fill details of mem buff to map */ - mem_to_map = ptr + ALIGN(src_sz, SZ_64); - mem_to_map_phys = ptr_phys + ALIGN(src_sz, SZ_64); - mem_to_map->mem_addr = cpu_to_le64(mem_addr); - mem_to_map->mem_size = cpu_to_le64(mem_sz); - - next_vm = 0; - /* Fill details of next vmid detail */ - destvm = ptr + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(src_sz, SZ_64); - dest_phys = ptr_phys + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(src_sz, SZ_64); - for (i = 0; i < dest_cnt; i++, destvm++, newvm++) { - destvm->vmid = cpu_to_le32(newvm->vmid); - destvm->perm = cpu_to_le32(newvm->perm); - destvm->ctx = 0; - destvm->ctx_size = 0; - next_vm |= BIT(newvm->vmid); - } - - ret = __qcom_scm_assign_mem(__scm->dev, mem_to_map_phys, mem_to_map_sz, - ptr_phys, src_sz, dest_phys, dest_sz); - dma_free_coherent(__scm->dev, ptr_sz, ptr, ptr_phys); - if (ret) { - dev_err(__scm->dev, - "Assign memory protection call failed %d\n", ret); - return -EINVAL; - } - - *srcvm = next_vm; - return 0; -} -EXPORT_SYMBOL_GPL(qcom_scm_assign_mem); - -/** - * qcom_scm_ocmem_lock_available() - is OCMEM lock/unlock interface available - */ -bool qcom_scm_ocmem_lock_available(void) -{ - return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_OCMEM, - QCOM_SCM_OCMEM_LOCK_CMD); -} -EXPORT_SYMBOL_GPL(qcom_scm_ocmem_lock_available); - -/** - * qcom_scm_ocmem_lock() - call OCMEM lock interface to assign an OCMEM - * region to the specified initiator - * - * @id: tz initiator id - * @offset: OCMEM offset - * @size: OCMEM size - * @mode: access mode (WIDE/NARROW) - */ -int qcom_scm_ocmem_lock(enum qcom_scm_ocmem_client id, u32 offset, u32 size, - u32 mode) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_OCMEM, - .cmd = QCOM_SCM_OCMEM_LOCK_CMD, - .args[0] = id, - .args[1] = offset, - .args[2] = size, - .args[3] = mode, - .arginfo = QCOM_SCM_ARGS(4), - }; - - return qcom_scm_call(__scm->dev, &desc, NULL); -} -EXPORT_SYMBOL_GPL(qcom_scm_ocmem_lock); - -/** - * qcom_scm_ocmem_unlock() - call OCMEM unlock interface to release an OCMEM - * region from the specified initiator - * - * @id: tz initiator id - * @offset: OCMEM offset - * @size: OCMEM size - */ -int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset, u32 size) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_OCMEM, - .cmd = QCOM_SCM_OCMEM_UNLOCK_CMD, - .args[0] = id, - .args[1] = offset, - .args[2] = size, - .arginfo = QCOM_SCM_ARGS(3), - }; - - return qcom_scm_call(__scm->dev, &desc, NULL); -} -EXPORT_SYMBOL_GPL(qcom_scm_ocmem_unlock); - -/** - * qcom_scm_ice_available() - Is the ICE key programming interface available? - * - * Return: true iff the SCM calls wrapped by qcom_scm_ice_invalidate_key() and - * qcom_scm_ice_set_key() are available. - */ -bool qcom_scm_ice_available(void) -{ - return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_ES, - QCOM_SCM_ES_INVALIDATE_ICE_KEY) && - __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_ES, - QCOM_SCM_ES_CONFIG_SET_ICE_KEY); -} -EXPORT_SYMBOL_GPL(qcom_scm_ice_available); - -/** - * qcom_scm_ice_invalidate_key() - Invalidate an inline encryption key - * @index: the keyslot to invalidate - * - * The UFSHCI and eMMC standards define a standard way to do this, but it - * doesn't work on these SoCs; only this SCM call does. - * - * It is assumed that the SoC has only one ICE instance being used, as this SCM - * call doesn't specify which ICE instance the keyslot belongs to. - * - * Return: 0 on success; -errno on failure. - */ -int qcom_scm_ice_invalidate_key(u32 index) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_ES, - .cmd = QCOM_SCM_ES_INVALIDATE_ICE_KEY, - .arginfo = QCOM_SCM_ARGS(1), - .args[0] = index, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - return qcom_scm_call(__scm->dev, &desc, NULL); -} -EXPORT_SYMBOL_GPL(qcom_scm_ice_invalidate_key); - -/** - * qcom_scm_ice_set_key() - Set an inline encryption key - * @index: the keyslot into which to set the key - * @key: the key to program - * @key_size: the size of the key in bytes - * @cipher: the encryption algorithm the key is for - * @data_unit_size: the encryption data unit size, i.e. the size of each - * individual plaintext and ciphertext. Given in 512-byte - * units, e.g. 1 = 512 bytes, 8 = 4096 bytes, etc. - * - * Program a key into a keyslot of Qualcomm ICE (Inline Crypto Engine), where it - * can then be used to encrypt/decrypt UFS or eMMC I/O requests inline. - * - * The UFSHCI and eMMC standards define a standard way to do this, but it - * doesn't work on these SoCs; only this SCM call does. - * - * It is assumed that the SoC has only one ICE instance being used, as this SCM - * call doesn't specify which ICE instance the keyslot belongs to. - * - * Return: 0 on success; -errno on failure. - */ -int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size, - enum qcom_scm_ice_cipher cipher, u32 data_unit_size) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_ES, - .cmd = QCOM_SCM_ES_CONFIG_SET_ICE_KEY, - .arginfo = QCOM_SCM_ARGS(5, QCOM_SCM_VAL, QCOM_SCM_RW, - QCOM_SCM_VAL, QCOM_SCM_VAL, - QCOM_SCM_VAL), - .args[0] = index, - .args[2] = key_size, - .args[3] = cipher, - .args[4] = data_unit_size, - .owner = ARM_SMCCC_OWNER_SIP, - }; - void *keybuf; - dma_addr_t key_phys; - int ret; - - /* - * 'key' may point to vmalloc()'ed memory, but we need to pass a - * physical address that's been properly flushed. The sanctioned way to - * do this is by using the DMA API. But as is best practice for crypto - * keys, we also must wipe the key after use. This makes kmemdup() + - * dma_map_single() not clearly correct, since the DMA API can use - * bounce buffers. Instead, just use dma_alloc_coherent(). Programming - * keys is normally rare and thus not performance-critical. - */ - - keybuf = dma_alloc_coherent(__scm->dev, key_size, &key_phys, - GFP_KERNEL); - if (!keybuf) - return -ENOMEM; - memcpy(keybuf, key, key_size); - desc.args[1] = key_phys; - - ret = qcom_scm_call(__scm->dev, &desc, NULL); - - memzero_explicit(keybuf, key_size); - - dma_free_coherent(__scm->dev, key_size, keybuf, key_phys); - return ret; -} -EXPORT_SYMBOL_GPL(qcom_scm_ice_set_key); - -/** - * qcom_scm_hdcp_available() - Check if secure environment supports HDCP. - * - * Return true if HDCP is supported, false if not. - */ -bool qcom_scm_hdcp_available(void) -{ - bool avail; - int ret = qcom_scm_clk_enable(); - - if (ret) - return ret; - - avail = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_HDCP, - QCOM_SCM_HDCP_INVOKE); - - qcom_scm_clk_disable(); - - return avail; -} -EXPORT_SYMBOL_GPL(qcom_scm_hdcp_available); - -/** - * qcom_scm_hdcp_req() - Send HDCP request. - * @req: HDCP request array - * @req_cnt: HDCP request array count - * @resp: response buffer passed to SCM - * - * Write HDCP register(s) through SCM. - */ -int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) -{ - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_HDCP, - .cmd = QCOM_SCM_HDCP_INVOKE, - .arginfo = QCOM_SCM_ARGS(10), - .args = { - req[0].addr, - req[0].val, - req[1].addr, - req[1].val, - req[2].addr, - req[2].val, - req[3].addr, - req[3].val, - req[4].addr, - req[4].val - }, - .owner = ARM_SMCCC_OWNER_SIP, - }; - struct qcom_scm_res res; - - if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT) - return -ERANGE; - - ret = qcom_scm_clk_enable(); - if (ret) - return ret; - - ret = qcom_scm_call(__scm->dev, &desc, &res); - *resp = res.result[0]; - - qcom_scm_clk_disable(); - - return ret; -} -EXPORT_SYMBOL_GPL(qcom_scm_hdcp_req); - -int qcom_scm_iommu_set_pt_format(u32 sec_id, u32 ctx_num, u32 pt_fmt) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_SMMU_PROGRAM, - .cmd = QCOM_SCM_SMMU_PT_FORMAT, - .arginfo = QCOM_SCM_ARGS(3), - .args[0] = sec_id, - .args[1] = ctx_num, - .args[2] = pt_fmt, /* 0: LPAE AArch32 - 1: AArch64 */ - .owner = ARM_SMCCC_OWNER_SIP, - }; - - return qcom_scm_call(__scm->dev, &desc, NULL); -} -EXPORT_SYMBOL_GPL(qcom_scm_iommu_set_pt_format); - -int qcom_scm_qsmmu500_wait_safe_toggle(bool en) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_SMMU_PROGRAM, - .cmd = QCOM_SCM_SMMU_CONFIG_ERRATA1, - .arginfo = QCOM_SCM_ARGS(2), - .args[0] = QCOM_SCM_SMMU_CONFIG_ERRATA1_CLIENT_ALL, - .args[1] = en, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - - return qcom_scm_call_atomic(__scm->dev, &desc, NULL); -} -EXPORT_SYMBOL_GPL(qcom_scm_qsmmu500_wait_safe_toggle); - -bool qcom_scm_lmh_dcvsh_available(void) -{ - return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_LMH, QCOM_SCM_LMH_LIMIT_DCVSH); -} -EXPORT_SYMBOL_GPL(qcom_scm_lmh_dcvsh_available); - -int qcom_scm_lmh_profile_change(u32 profile_id) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_LMH, - .cmd = QCOM_SCM_LMH_LIMIT_PROFILE_CHANGE, - .arginfo = QCOM_SCM_ARGS(1, QCOM_SCM_VAL), - .args[0] = profile_id, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - return qcom_scm_call(__scm->dev, &desc, NULL); -} -EXPORT_SYMBOL_GPL(qcom_scm_lmh_profile_change); - -int qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val, - u64 limit_node, u32 node_id, u64 version) -{ - dma_addr_t payload_phys; - u32 *payload_buf; - int ret, payload_size = 5 * sizeof(u32); - - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_LMH, - .cmd = QCOM_SCM_LMH_LIMIT_DCVSH, - .arginfo = QCOM_SCM_ARGS(5, QCOM_SCM_RO, QCOM_SCM_VAL, QCOM_SCM_VAL, - QCOM_SCM_VAL, QCOM_SCM_VAL), - .args[1] = payload_size, - .args[2] = limit_node, - .args[3] = node_id, - .args[4] = version, - .owner = ARM_SMCCC_OWNER_SIP, - }; - - payload_buf = dma_alloc_coherent(__scm->dev, payload_size, &payload_phys, GFP_KERNEL); - if (!payload_buf) - return -ENOMEM; - - payload_buf[0] = payload_fn; - payload_buf[1] = 0; - payload_buf[2] = payload_reg; - payload_buf[3] = 1; - payload_buf[4] = payload_val; - - desc.args[0] = payload_phys; - - ret = qcom_scm_call(__scm->dev, &desc, NULL); - - dma_free_coherent(__scm->dev, payload_size, payload_buf, payload_phys); - return ret; -} -EXPORT_SYMBOL_GPL(qcom_scm_lmh_dcvsh); - -static int qcom_scm_find_dload_address(struct device *dev, u64 *addr) -{ - struct device_node *tcsr; - struct device_node *np = dev->of_node; - struct resource res; - u32 offset; - int ret; - - tcsr = of_parse_phandle(np, "qcom,dload-mode", 0); - if (!tcsr) - return 0; - - ret = of_address_to_resource(tcsr, 0, &res); - of_node_put(tcsr); - if (ret) - return ret; - - ret = of_property_read_u32_index(np, "qcom,dload-mode", 1, &offset); - if (ret < 0) - return ret; - - *addr = res.start + offset; - - return 0; -} - -#ifdef CONFIG_QCOM_QSEECOM - -/* Lock for QSEECOM SCM call executions */ -static DEFINE_MUTEX(qcom_scm_qseecom_call_lock); - -static int __qcom_scm_qseecom_call(const struct qcom_scm_desc *desc, - struct qcom_scm_qseecom_resp *res) -{ - struct qcom_scm_res scm_res = {}; - int status; - - /* - * QSEECOM SCM calls should not be executed concurrently. Therefore, we - * require the respective call lock to be held. - */ - lockdep_assert_held(&qcom_scm_qseecom_call_lock); - - status = qcom_scm_call(__scm->dev, desc, &scm_res); - - res->result = scm_res.result[0]; - res->resp_type = scm_res.result[1]; - res->data = scm_res.result[2]; - - if (status) - return status; - - return 0; -} - -/** - * qcom_scm_qseecom_call() - Perform a QSEECOM SCM call. - * @desc: SCM call descriptor. - * @res: SCM call response (output). - * - * Performs the QSEECOM SCM call described by @desc, returning the response in - * @rsp. - * - * Return: Zero on success, nonzero on failure. - */ -static int qcom_scm_qseecom_call(const struct qcom_scm_desc *desc, - struct qcom_scm_qseecom_resp *res) -{ - int status; - - /* - * Note: Multiple QSEECOM SCM calls should not be executed same time, - * so lock things here. This needs to be extended to callback/listener - * handling when support for that is implemented. - */ - - mutex_lock(&qcom_scm_qseecom_call_lock); - status = __qcom_scm_qseecom_call(desc, res); - mutex_unlock(&qcom_scm_qseecom_call_lock); - - dev_dbg(__scm->dev, "%s: owner=%x, svc=%x, cmd=%x, result=%lld, type=%llx, data=%llx\n", - __func__, desc->owner, desc->svc, desc->cmd, res->result, - res->resp_type, res->data); - - if (status) { - dev_err(__scm->dev, "qseecom: scm call failed with error %d\n", status); - return status; - } - - /* - * TODO: Handle incomplete and blocked calls: - * - * Incomplete and blocked calls are not supported yet. Some devices - * and/or commands require those, some don't. Let's warn about them - * prominently in case someone attempts to try these commands with a - * device/command combination that isn't supported yet. - */ - WARN_ON(res->result == QSEECOM_RESULT_INCOMPLETE); - WARN_ON(res->result == QSEECOM_RESULT_BLOCKED_ON_LISTENER); - - return 0; -} - -/** - * qcom_scm_qseecom_get_version() - Query the QSEECOM version. - * @version: Pointer where the QSEECOM version will be stored. - * - * Performs the QSEECOM SCM querying the QSEECOM version currently running in - * the TrustZone. - * - * Return: Zero on success, nonzero on failure. - */ -static int qcom_scm_qseecom_get_version(u32 *version) -{ - struct qcom_scm_desc desc = {}; - struct qcom_scm_qseecom_resp res = {}; - u32 feature = 10; - int ret; - - desc.owner = QSEECOM_TZ_OWNER_SIP; - desc.svc = QSEECOM_TZ_SVC_INFO; - desc.cmd = QSEECOM_TZ_CMD_INFO_VERSION; - desc.arginfo = QCOM_SCM_ARGS(1, QCOM_SCM_VAL); - desc.args[0] = feature; - - ret = qcom_scm_qseecom_call(&desc, &res); - if (ret) - return ret; - - *version = res.result; - return 0; -} - -/** - * qcom_scm_qseecom_app_get_id() - Query the app ID for a given QSEE app name. - * @app_name: The name of the app. - * @app_id: The returned app ID. - * - * Query and return the application ID of the SEE app identified by the given - * name. This returned ID is the unique identifier of the app required for - * subsequent communication. - * - * Return: Zero on success, nonzero on failure, -ENOENT if the app has not been - * loaded or could not be found. - */ -int qcom_scm_qseecom_app_get_id(const char *app_name, u32 *app_id) -{ - unsigned long name_buf_size = QSEECOM_MAX_APP_NAME_SIZE; - unsigned long app_name_len = strlen(app_name); - struct qcom_scm_desc desc = {}; - struct qcom_scm_qseecom_resp res = {}; - dma_addr_t name_buf_phys; - char *name_buf; - int status; - - if (app_name_len >= name_buf_size) - return -EINVAL; - - name_buf = kzalloc(name_buf_size, GFP_KERNEL); - if (!name_buf) - return -ENOMEM; - - memcpy(name_buf, app_name, app_name_len); - - name_buf_phys = dma_map_single(__scm->dev, name_buf, name_buf_size, DMA_TO_DEVICE); - status = dma_mapping_error(__scm->dev, name_buf_phys); - if (status) { - kfree(name_buf); - dev_err(__scm->dev, "qseecom: failed to map dma address\n"); - return status; - } - - desc.owner = QSEECOM_TZ_OWNER_QSEE_OS; - desc.svc = QSEECOM_TZ_SVC_APP_MGR; - desc.cmd = QSEECOM_TZ_CMD_APP_LOOKUP; - desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_RW, QCOM_SCM_VAL); - desc.args[0] = name_buf_phys; - desc.args[1] = app_name_len; - - status = qcom_scm_qseecom_call(&desc, &res); - dma_unmap_single(__scm->dev, name_buf_phys, name_buf_size, DMA_TO_DEVICE); - kfree(name_buf); - - if (status) - return status; - - if (res.result == QSEECOM_RESULT_FAILURE) - return -ENOENT; - - if (res.result != QSEECOM_RESULT_SUCCESS) - return -EINVAL; - - if (res.resp_type != QSEECOM_SCM_RES_APP_ID) - return -EINVAL; - - *app_id = res.data; - return 0; -} -EXPORT_SYMBOL_GPL(qcom_scm_qseecom_app_get_id); - -/** - * qcom_scm_qseecom_app_send() - Send to and receive data from a given QSEE app. - * @app_id: The ID of the target app. - * @req: Request buffer sent to the app (must be DMA-mappable). - * @req_size: Size of the request buffer. - * @rsp: Response buffer, written to by the app (must be DMA-mappable). - * @rsp_size: Size of the response buffer. - * - * Sends a request to the QSEE app associated with the given ID and read back - * its response. The caller must provide two DMA memory regions, one for the - * request and one for the response, and fill out the @req region with the - * respective (app-specific) request data. The QSEE app reads this and returns - * its response in the @rsp region. - * - * Return: Zero on success, nonzero on failure. - */ -int qcom_scm_qseecom_app_send(u32 app_id, void *req, size_t req_size, void *rsp, - size_t rsp_size) -{ - struct qcom_scm_qseecom_resp res = {}; - struct qcom_scm_desc desc = {}; - dma_addr_t req_phys; - dma_addr_t rsp_phys; - int status; - - /* Map request buffer */ - req_phys = dma_map_single(__scm->dev, req, req_size, DMA_TO_DEVICE); - status = dma_mapping_error(__scm->dev, req_phys); - if (status) { - dev_err(__scm->dev, "qseecom: failed to map request buffer\n"); - return status; - } - - /* Map response buffer */ - rsp_phys = dma_map_single(__scm->dev, rsp, rsp_size, DMA_FROM_DEVICE); - status = dma_mapping_error(__scm->dev, rsp_phys); - if (status) { - dma_unmap_single(__scm->dev, req_phys, req_size, DMA_TO_DEVICE); - dev_err(__scm->dev, "qseecom: failed to map response buffer\n"); - return status; - } - - /* Set up SCM call data */ - desc.owner = QSEECOM_TZ_OWNER_TZ_APPS; - desc.svc = QSEECOM_TZ_SVC_APP_ID_PLACEHOLDER; - desc.cmd = QSEECOM_TZ_CMD_APP_SEND; - desc.arginfo = QCOM_SCM_ARGS(5, QCOM_SCM_VAL, - QCOM_SCM_RW, QCOM_SCM_VAL, - QCOM_SCM_RW, QCOM_SCM_VAL); - desc.args[0] = app_id; - desc.args[1] = req_phys; - desc.args[2] = req_size; - desc.args[3] = rsp_phys; - desc.args[4] = rsp_size; - - /* Perform call */ - status = qcom_scm_qseecom_call(&desc, &res); - - /* Unmap buffers */ - dma_unmap_single(__scm->dev, rsp_phys, rsp_size, DMA_FROM_DEVICE); - dma_unmap_single(__scm->dev, req_phys, req_size, DMA_TO_DEVICE); - - if (status) - return status; - - if (res.result != QSEECOM_RESULT_SUCCESS) - return -EIO; - - return 0; -} -EXPORT_SYMBOL_GPL(qcom_scm_qseecom_app_send); - -/* - * We do not yet support re-entrant calls via the qseecom interface. To prevent - + any potential issues with this, only allow validated machines for now. - */ -static const struct of_device_id qcom_scm_qseecom_allowlist[] = { - { .compatible = "lenovo,thinkpad-x13s", }, - { } -}; - -static bool qcom_scm_qseecom_machine_is_allowed(void) -{ - struct device_node *np; - bool match; - - np = of_find_node_by_path("/"); - if (!np) - return false; - - match = of_match_node(qcom_scm_qseecom_allowlist, np); - of_node_put(np); - - return match; -} - -static void qcom_scm_qseecom_free(void *data) -{ - struct platform_device *qseecom_dev = data; - - platform_device_del(qseecom_dev); - platform_device_put(qseecom_dev); -} - -static int qcom_scm_qseecom_init(struct qcom_scm *scm) -{ - struct platform_device *qseecom_dev; - u32 version; - int ret; - - /* - * Note: We do two steps of validation here: First, we try to query the - * QSEECOM version as a check to see if the interface exists on this - * device. Second, we check against known good devices due to current - * driver limitations (see comment in qcom_scm_qseecom_allowlist). - * - * Note that we deliberately do the machine check after the version - * check so that we can log potentially supported devices. This should - * be safe as downstream sources indicate that the version query is - * neither blocking nor reentrant. - */ - ret = qcom_scm_qseecom_get_version(&version); - if (ret) - return 0; - - dev_info(scm->dev, "qseecom: found qseecom with version 0x%x\n", version); - - if (!qcom_scm_qseecom_machine_is_allowed()) { - dev_info(scm->dev, "qseecom: untested machine, skipping\n"); - return 0; - } - - /* - * Set up QSEECOM interface device. All application clients will be - * set up and managed by the corresponding driver for it. - */ - qseecom_dev = platform_device_alloc("qcom_qseecom", -1); - if (!qseecom_dev) - return -ENOMEM; - - qseecom_dev->dev.parent = scm->dev; - - ret = platform_device_add(qseecom_dev); - if (ret) { - platform_device_put(qseecom_dev); - return ret; - } - - return devm_add_action_or_reset(scm->dev, qcom_scm_qseecom_free, qseecom_dev); -} - -#else /* CONFIG_QCOM_QSEECOM */ - -static int qcom_scm_qseecom_init(struct qcom_scm *scm) -{ - return 0; -} - -#endif /* CONFIG_QCOM_QSEECOM */ - -/** - * qcom_scm_is_available() - Checks if SCM is available - */ -bool qcom_scm_is_available(void) -{ - return !!__scm; -} -EXPORT_SYMBOL_GPL(qcom_scm_is_available); - -static int qcom_scm_assert_valid_wq_ctx(u32 wq_ctx) -{ - /* FW currently only supports a single wq_ctx (zero). - * TODO: Update this logic to include dynamic allocation and lookup of - * completion structs when FW supports more wq_ctx values. - */ - if (wq_ctx != 0) { - dev_err(__scm->dev, "Firmware unexpectedly passed non-zero wq_ctx\n"); - return -EINVAL; - } - - return 0; -} - -int qcom_scm_wait_for_wq_completion(u32 wq_ctx) -{ - int ret; - - ret = qcom_scm_assert_valid_wq_ctx(wq_ctx); - if (ret) - return ret; - - wait_for_completion(&__scm->waitq_comp); - - return 0; -} - -static int qcom_scm_waitq_wakeup(struct qcom_scm *scm, unsigned int wq_ctx) -{ - int ret; - - ret = qcom_scm_assert_valid_wq_ctx(wq_ctx); - if (ret) - return ret; - - complete(&__scm->waitq_comp); - - return 0; -} - -static irqreturn_t qcom_scm_irq_handler(int irq, void *data) -{ - int ret; - struct qcom_scm *scm = data; - u32 wq_ctx, flags, more_pending = 0; - - do { - ret = scm_get_wq_ctx(&wq_ctx, &flags, &more_pending); - if (ret) { - dev_err(scm->dev, "GET_WQ_CTX SMC call failed: %d\n", ret); - goto out; - } - - if (flags != QCOM_SMC_WAITQ_FLAG_WAKE_ONE && - flags != QCOM_SMC_WAITQ_FLAG_WAKE_ALL) { - dev_err(scm->dev, "Invalid flags found for wq_ctx: %u\n", flags); - goto out; - } - - ret = qcom_scm_waitq_wakeup(scm, wq_ctx); - if (ret) - goto out; - } while (more_pending); - -out: - return IRQ_HANDLED; -} - -static int qcom_scm_probe(struct platform_device *pdev) -{ - struct qcom_scm *scm; - int irq, ret; - - scm = devm_kzalloc(&pdev->dev, sizeof(*scm), GFP_KERNEL); - if (!scm) - return -ENOMEM; - - ret = qcom_scm_find_dload_address(&pdev->dev, &scm->dload_mode_addr); - if (ret < 0) - return ret; - - mutex_init(&scm->scm_bw_lock); - - scm->path = devm_of_icc_get(&pdev->dev, NULL); - if (IS_ERR(scm->path)) - return dev_err_probe(&pdev->dev, PTR_ERR(scm->path), - "failed to acquire interconnect path\n"); - - scm->core_clk = devm_clk_get_optional(&pdev->dev, "core"); - if (IS_ERR(scm->core_clk)) - return PTR_ERR(scm->core_clk); - - scm->iface_clk = devm_clk_get_optional(&pdev->dev, "iface"); - if (IS_ERR(scm->iface_clk)) - return PTR_ERR(scm->iface_clk); - - scm->bus_clk = devm_clk_get_optional(&pdev->dev, "bus"); - if (IS_ERR(scm->bus_clk)) - return PTR_ERR(scm->bus_clk); - - scm->reset.ops = &qcom_scm_pas_reset_ops; - scm->reset.nr_resets = 1; - scm->reset.of_node = pdev->dev.of_node; - ret = devm_reset_controller_register(&pdev->dev, &scm->reset); - if (ret) - return ret; - - /* vote for max clk rate for highest performance */ - ret = clk_set_rate(scm->core_clk, INT_MAX); - if (ret) - return ret; - - __scm = scm; - __scm->dev = &pdev->dev; - - init_completion(&__scm->waitq_comp); - - irq = platform_get_irq_optional(pdev, 0); - if (irq < 0) { - if (irq != -ENXIO) - return irq; - } else { - ret = devm_request_threaded_irq(__scm->dev, irq, NULL, qcom_scm_irq_handler, - IRQF_ONESHOT, "qcom-scm", __scm); - if (ret < 0) - return dev_err_probe(scm->dev, ret, "Failed to request qcom-scm irq\n"); - } - - __get_convention(); - - /* - * If requested enable "download mode", from this point on warmboot - * will cause the boot stages to enter download mode, unless - * disabled below by a clean shutdown/reboot. - */ - if (download_mode) - qcom_scm_set_download_mode(true); - - - /* - * Disable SDI if indicated by DT that it is enabled by default. - */ - if (of_property_read_bool(pdev->dev.of_node, "qcom,sdi-enabled")) - qcom_scm_disable_sdi(); - - /* - * Initialize the QSEECOM interface. - * - * Note: QSEECOM is fairly self-contained and this only adds the - * interface device (the driver of which does most of the heavy - * lifting). So any errors returned here should be either -ENOMEM or - * -EINVAL (with the latter only in case there's a bug in our code). - * This means that there is no need to bring down the whole SCM driver. - * Just log the error instead and let SCM live. - */ - ret = qcom_scm_qseecom_init(scm); - WARN(ret < 0, "failed to initialize qseecom: %d\n", ret); - - return 0; -} - -static void qcom_scm_shutdown(struct platform_device *pdev) -{ - /* Clean shutdown, disable download mode to allow normal restart */ - qcom_scm_set_download_mode(false); -} - -static const struct of_device_id qcom_scm_dt_match[] = { - { .compatible = "qcom,scm" }, - - /* Legacy entries kept for backwards compatibility */ - { .compatible = "qcom,scm-apq8064" }, - { .compatible = "qcom,scm-apq8084" }, - { .compatible = "qcom,scm-ipq4019" }, - { .compatible = "qcom,scm-msm8953" }, - { .compatible = "qcom,scm-msm8974" }, - { .compatible = "qcom,scm-msm8996" }, - {} -}; -MODULE_DEVICE_TABLE(of, qcom_scm_dt_match); - -static struct platform_driver qcom_scm_driver = { - .driver = { - .name = "qcom_scm", - .of_match_table = qcom_scm_dt_match, - .suppress_bind_attrs = true, - }, - .probe = qcom_scm_probe, - .shutdown = qcom_scm_shutdown, -}; - -static int __init qcom_scm_init(void) -{ - return platform_driver_register(&qcom_scm_driver); -} -subsys_initcall(qcom_scm_init); - -MODULE_DESCRIPTION("Qualcomm Technologies, Inc. SCM driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h deleted file mode 100644 index 7b68fa820495..000000000000 --- a/drivers/firmware/qcom_scm.h +++ /dev/null @@ -1,168 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* Copyright (c) 2010-2015,2019 The Linux Foundation. All rights reserved. - */ -#ifndef __QCOM_SCM_INT_H -#define __QCOM_SCM_INT_H - -enum qcom_scm_convention { - SMC_CONVENTION_UNKNOWN, - SMC_CONVENTION_LEGACY, - SMC_CONVENTION_ARM_32, - SMC_CONVENTION_ARM_64, -}; - -extern enum qcom_scm_convention qcom_scm_convention; - -#define MAX_QCOM_SCM_ARGS 10 -#define MAX_QCOM_SCM_RETS 3 - -enum qcom_scm_arg_types { - QCOM_SCM_VAL, - QCOM_SCM_RO, - QCOM_SCM_RW, - QCOM_SCM_BUFVAL, -}; - -#define QCOM_SCM_ARGS_IMPL(num, a, b, c, d, e, f, g, h, i, j, ...) (\ - (((a) & 0x3) << 4) | \ - (((b) & 0x3) << 6) | \ - (((c) & 0x3) << 8) | \ - (((d) & 0x3) << 10) | \ - (((e) & 0x3) << 12) | \ - (((f) & 0x3) << 14) | \ - (((g) & 0x3) << 16) | \ - (((h) & 0x3) << 18) | \ - (((i) & 0x3) << 20) | \ - (((j) & 0x3) << 22) | \ - ((num) & 0xf)) - -#define QCOM_SCM_ARGS(...) QCOM_SCM_ARGS_IMPL(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - - -/** - * struct qcom_scm_desc - * @arginfo: Metadata describing the arguments in args[] - * @args: The array of arguments for the secure syscall - */ -struct qcom_scm_desc { - u32 svc; - u32 cmd; - u32 arginfo; - u64 args[MAX_QCOM_SCM_ARGS]; - u32 owner; -}; - -/** - * struct qcom_scm_res - * @result: The values returned by the secure syscall - */ -struct qcom_scm_res { - u64 result[MAX_QCOM_SCM_RETS]; -}; - -int qcom_scm_wait_for_wq_completion(u32 wq_ctx); -int scm_get_wq_ctx(u32 *wq_ctx, u32 *flags, u32 *more_pending); - -#define SCM_SMC_FNID(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF)) -extern int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc, - enum qcom_scm_convention qcom_convention, - struct qcom_scm_res *res, bool atomic); -#define scm_smc_call(dev, desc, res, atomic) \ - __scm_smc_call((dev), (desc), qcom_scm_convention, (res), (atomic)) - -#define SCM_LEGACY_FNID(s, c) (((s) << 10) | ((c) & 0x3ff)) -extern int scm_legacy_call_atomic(struct device *dev, - const struct qcom_scm_desc *desc, - struct qcom_scm_res *res); -extern int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc, - struct qcom_scm_res *res); - -#define QCOM_SCM_SVC_BOOT 0x01 -#define QCOM_SCM_BOOT_SET_ADDR 0x01 -#define QCOM_SCM_BOOT_TERMINATE_PC 0x02 -#define QCOM_SCM_BOOT_SDI_CONFIG 0x09 -#define QCOM_SCM_BOOT_SET_DLOAD_MODE 0x10 -#define QCOM_SCM_BOOT_SET_ADDR_MC 0x11 -#define QCOM_SCM_BOOT_SET_REMOTE_STATE 0x0a -#define QCOM_SCM_FLUSH_FLAG_MASK 0x3 -#define QCOM_SCM_BOOT_MAX_CPUS 4 -#define QCOM_SCM_BOOT_MC_FLAG_AARCH64 BIT(0) -#define QCOM_SCM_BOOT_MC_FLAG_COLDBOOT BIT(1) -#define QCOM_SCM_BOOT_MC_FLAG_WARMBOOT BIT(2) - -#define QCOM_SCM_SVC_PIL 0x02 -#define QCOM_SCM_PIL_PAS_INIT_IMAGE 0x01 -#define QCOM_SCM_PIL_PAS_MEM_SETUP 0x02 -#define QCOM_SCM_PIL_PAS_AUTH_AND_RESET 0x05 -#define QCOM_SCM_PIL_PAS_SHUTDOWN 0x06 -#define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07 -#define QCOM_SCM_PIL_PAS_MSS_RESET 0x0a - -#define QCOM_SCM_SVC_IO 0x05 -#define QCOM_SCM_IO_READ 0x01 -#define QCOM_SCM_IO_WRITE 0x02 - -#define QCOM_SCM_SVC_INFO 0x06 -#define QCOM_SCM_INFO_IS_CALL_AVAIL 0x01 - -#define QCOM_SCM_SVC_MP 0x0c -#define QCOM_SCM_MP_RESTORE_SEC_CFG 0x02 -#define QCOM_SCM_MP_IOMMU_SECURE_PTBL_SIZE 0x03 -#define QCOM_SCM_MP_IOMMU_SECURE_PTBL_INIT 0x04 -#define QCOM_SCM_MP_IOMMU_SET_CP_POOL_SIZE 0x05 -#define QCOM_SCM_MP_VIDEO_VAR 0x08 -#define QCOM_SCM_MP_ASSIGN 0x16 - -#define QCOM_SCM_SVC_OCMEM 0x0f -#define QCOM_SCM_OCMEM_LOCK_CMD 0x01 -#define QCOM_SCM_OCMEM_UNLOCK_CMD 0x02 - -#define QCOM_SCM_SVC_ES 0x10 /* Enterprise Security */ -#define QCOM_SCM_ES_INVALIDATE_ICE_KEY 0x03 -#define QCOM_SCM_ES_CONFIG_SET_ICE_KEY 0x04 - -#define QCOM_SCM_SVC_HDCP 0x11 -#define QCOM_SCM_HDCP_INVOKE 0x01 - -#define QCOM_SCM_SVC_LMH 0x13 -#define QCOM_SCM_LMH_LIMIT_PROFILE_CHANGE 0x01 -#define QCOM_SCM_LMH_LIMIT_DCVSH 0x10 - -#define QCOM_SCM_SVC_SMMU_PROGRAM 0x15 -#define QCOM_SCM_SMMU_PT_FORMAT 0x01 -#define QCOM_SCM_SMMU_CONFIG_ERRATA1 0x03 -#define QCOM_SCM_SMMU_CONFIG_ERRATA1_CLIENT_ALL 0x02 - -#define QCOM_SCM_SVC_WAITQ 0x24 -#define QCOM_SCM_WAITQ_RESUME 0x02 -#define QCOM_SCM_WAITQ_GET_WQ_CTX 0x03 - -/* common error codes */ -#define QCOM_SCM_V2_EBUSY -12 -#define QCOM_SCM_ENOMEM -5 -#define QCOM_SCM_EOPNOTSUPP -4 -#define QCOM_SCM_EINVAL_ADDR -3 -#define QCOM_SCM_EINVAL_ARG -2 -#define QCOM_SCM_ERROR -1 -#define QCOM_SCM_INTERRUPTED 1 -#define QCOM_SCM_WAITQ_SLEEP 2 - -static inline int qcom_scm_remap_error(int err) -{ - switch (err) { - case QCOM_SCM_ERROR: - return -EIO; - case QCOM_SCM_EINVAL_ADDR: - case QCOM_SCM_EINVAL_ARG: - return -EINVAL; - case QCOM_SCM_EOPNOTSUPP: - return -EOPNOTSUPP; - case QCOM_SCM_ENOMEM: - return -ENOMEM; - case QCOM_SCM_V2_EBUSY: - return -EBUSY; - } - return -EINVAL; -} - -#endif -- cgit v1.2.3 From 3294d01f7a5ddacb796d7f7bf31eb100a8f6d0e0 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 17 Oct 2023 11:27:19 +0200 Subject: firmware: qcom: scm: add a missing forward declaration for struct device We reference struct device in the private scm header but we neither include linux/device.h nor forward declare it. Fix it. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andrew Halaney Tested-by: Andrew Halaney # sc8280xp-lenovo-thinkpad-x13s Link: https://lore.kernel.org/r/20231017092732.19983-3-brgl@bgdev.pl Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/firmware/qcom/qcom_scm.h b/drivers/firmware/qcom/qcom_scm.h index 7b68fa820495..c88e29051d20 100644 --- a/drivers/firmware/qcom/qcom_scm.h +++ b/drivers/firmware/qcom/qcom_scm.h @@ -4,6 +4,8 @@ #ifndef __QCOM_SCM_INT_H #define __QCOM_SCM_INT_H +struct device; + enum qcom_scm_convention { SMC_CONVENTION_UNKNOWN, SMC_CONVENTION_LEGACY, -- cgit v1.2.3 From d79a27ea8e35a2dfc02096d2937267912bcca022 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 17 Oct 2023 11:27:20 +0200 Subject: firmware: qcom: scm: remove unneeded 'extern' specifiers 'extern' specifiers do nothing for function declarations. Remove them from the private qcom-scm header. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andrew Halaney Tested-by: Andrew Halaney # sc8280xp-lenovo-thinkpad-x13s Link: https://lore.kernel.org/r/20231017092732.19983-4-brgl@bgdev.pl Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.h | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/firmware/qcom/qcom_scm.h b/drivers/firmware/qcom/qcom_scm.h index c88e29051d20..4532907e8489 100644 --- a/drivers/firmware/qcom/qcom_scm.h +++ b/drivers/firmware/qcom/qcom_scm.h @@ -66,18 +66,17 @@ int qcom_scm_wait_for_wq_completion(u32 wq_ctx); int scm_get_wq_ctx(u32 *wq_ctx, u32 *flags, u32 *more_pending); #define SCM_SMC_FNID(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF)) -extern int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc, - enum qcom_scm_convention qcom_convention, - struct qcom_scm_res *res, bool atomic); +int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc, + enum qcom_scm_convention qcom_convention, + struct qcom_scm_res *res, bool atomic); #define scm_smc_call(dev, desc, res, atomic) \ __scm_smc_call((dev), (desc), qcom_scm_convention, (res), (atomic)) #define SCM_LEGACY_FNID(s, c) (((s) << 10) | ((c) & 0x3ff)) -extern int scm_legacy_call_atomic(struct device *dev, - const struct qcom_scm_desc *desc, - struct qcom_scm_res *res); -extern int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc, +int scm_legacy_call_atomic(struct device *dev, const struct qcom_scm_desc *desc, struct qcom_scm_res *res); +int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc, + struct qcom_scm_res *res); #define QCOM_SCM_SVC_BOOT 0x01 #define QCOM_SCM_BOOT_SET_ADDR 0x01 -- cgit v1.2.3 From 723d346173e748c4fdb145b84f572b871ab4011a Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Tue, 17 Oct 2023 10:00:23 +0200 Subject: soc: qcom: pmic_glink_altmode: Print return value on error It can be useful to know with which return value for example the typec_retimer_set call failed, so include this info in the dev_err prints. Signed-off-by: Luca Weiss Link: https://lore.kernel.org/r/20231017-glink-altmode-ret-v2-1-921aa7cfc381@fairphone.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/pmic_glink_altmode.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/soc/qcom/pmic_glink_altmode.c b/drivers/soc/qcom/pmic_glink_altmode.c index 6f8b2f7ae3cc..b78279e2f54c 100644 --- a/drivers/soc/qcom/pmic_glink_altmode.c +++ b/drivers/soc/qcom/pmic_glink_altmode.c @@ -160,7 +160,7 @@ static void pmic_glink_altmode_enable_dp(struct pmic_glink_altmode *altmode, ret = typec_mux_set(port->typec_mux, &port->state); if (ret) - dev_err(altmode->dev, "failed to switch mux to DP\n"); + dev_err(altmode->dev, "failed to switch mux to DP: %d\n", ret); port->retimer_state.alt = &port->dp_alt; port->retimer_state.data = &dp_data; @@ -168,7 +168,7 @@ static void pmic_glink_altmode_enable_dp(struct pmic_glink_altmode *altmode, ret = typec_retimer_set(port->typec_retimer, &port->retimer_state); if (ret) - dev_err(altmode->dev, "failed to setup retimer to DP\n"); + dev_err(altmode->dev, "failed to setup retimer to DP: %d\n", ret); } static void pmic_glink_altmode_enable_usb(struct pmic_glink_altmode *altmode, @@ -182,7 +182,7 @@ static void pmic_glink_altmode_enable_usb(struct pmic_glink_altmode *altmode, ret = typec_mux_set(port->typec_mux, &port->state); if (ret) - dev_err(altmode->dev, "failed to switch mux to USB\n"); + dev_err(altmode->dev, "failed to switch mux to USB: %d\n", ret); port->retimer_state.alt = NULL; port->retimer_state.data = NULL; @@ -190,7 +190,7 @@ static void pmic_glink_altmode_enable_usb(struct pmic_glink_altmode *altmode, ret = typec_retimer_set(port->typec_retimer, &port->retimer_state); if (ret) - dev_err(altmode->dev, "failed to setup retimer to USB\n"); + dev_err(altmode->dev, "failed to setup retimer to USB: %d\n", ret); } static void pmic_glink_altmode_safe(struct pmic_glink_altmode *altmode, @@ -204,7 +204,7 @@ static void pmic_glink_altmode_safe(struct pmic_glink_altmode *altmode, ret = typec_mux_set(port->typec_mux, &port->state); if (ret) - dev_err(altmode->dev, "failed to switch mux to safe mode\n"); + dev_err(altmode->dev, "failed to switch mux to safe mode: %d\n", ret); port->retimer_state.alt = NULL; port->retimer_state.data = NULL; @@ -212,7 +212,7 @@ static void pmic_glink_altmode_safe(struct pmic_glink_altmode *altmode, ret = typec_retimer_set(port->typec_retimer, &port->retimer_state); if (ret) - dev_err(altmode->dev, "failed to setup retimer to USB\n"); + dev_err(altmode->dev, "failed to setup retimer to USB: %d\n", ret); } static void pmic_glink_altmode_worker(struct work_struct *work) @@ -397,7 +397,7 @@ static void pmic_glink_altmode_enable_worker(struct work_struct *work) ret = pmic_glink_altmode_request(altmode, ALTMODE_PAN_EN, 0); if (ret) - dev_err(altmode->dev, "failed to request altmode notifications\n"); + dev_err(altmode->dev, "failed to request altmode notifications: %d\n", ret); } static void pmic_glink_altmode_pdr_notify(void *priv, int state) -- cgit v1.2.3