diff options
40 files changed, 2090 insertions, 261 deletions
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: diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml index 4233ea839bfc..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 @@ -56,6 +57,7 @@ properties: - qcom,scm-sm6125 - qcom,scm-sm6350 - qcom,scm-sm6375 + - qcom,scm-sm7150 - qcom,scm-sm8150 - qcom,scm-sm8250 - qcom,scm-sm8350 @@ -89,6 +91,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: 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: > 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 diff --git a/MAINTAINERS b/MAINTAINERS index 6dcf2926109a..75e05a055090 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17801,6 +17801,18 @@ S: Maintained F: Documentation/devicetree/bindings/mtd/qcom,nandc.yaml F: drivers/mtd/nand/raw/qcom_nandc.c +QUALCOMM QSEECOM DRIVER +M: Maximilian Luz <luzmaximilian@gmail.com> +L: linux-arm-msm@vger.kernel.org +S: Maintained +F: drivers/firmware/qcom_qseecom.c + +QUALCOMM QSEECOM UEFISECAPP DRIVER +M: Maximilian Luz <luzmaximilian@gmail.com> +L: linux-arm-msm@vger.kernel.org +S: Maintained +F: drivers/firmware/qcom_qseecom_uefisecapp.c + QUALCOMM RMNET DRIVER M: Subash Abhinov Kasiviswanathan <quic_subashab@quicinc.com> M: Sean Tranchetti <quic_stranche@quicinc.com> diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index b59e3041fd62..8c608be30060 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -226,6 +226,39 @@ 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 + 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 diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 28fcddcd688f..d41b094a5e58 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -20,6 +20,8 @@ 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 diff --git a/drivers/firmware/qcom_qseecom.c b/drivers/firmware/qcom_qseecom.c new file mode 100644 index 000000000000..731e6d5719f9 --- /dev/null +++ b/drivers/firmware/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 <luzmaximilian@gmail.com> + */ +#include <linux/auxiliary_bus.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include <linux/firmware/qcom/qcom_qseecom.h> +#include <linux/firmware/qcom/qcom_scm.h> + +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 <luzmaximilian@gmail.com>"); +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 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 <luzmaximilian@gmail.com> + */ + +#include <linux/efi.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/ucs2_string.h> + +#include <linux/firmware/qcom/qcom_qseecom.h> + +/* -- 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 <luzmaximilian@gmail.com>"); +MODULE_DESCRIPTION("Client driver for Qualcomm SEE UEFI Secure App"); +MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 06fe8aca870d..520de9b5633a 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 <linux/platform_device.h> -#include <linux/init.h> -#include <linux/interrupt.h> + +#include <linux/arm-smccc.h> +#include <linux/clk.h> #include <linux/completion.h> #include <linux/cpumask.h> -#include <linux/export.h> #include <linux/dma-mapping.h> +#include <linux/export.h> +#include <linux/firmware/qcom/qcom_scm.h> +#include <linux/init.h> #include <linux/interconnect.h> +#include <linux/interrupt.h> #include <linux/module.h> -#include <linux/types.h> -#include <linux/firmware/qcom/qcom_scm.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/of_platform.h> -#include <linux/clk.h> +#include <linux/platform_device.h> #include <linux/reset-controller.h> -#include <linux/arm-smccc.h> +#include <linux/types.h> #include "qcom_scm.h" @@ -55,6 +56,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) @@ -168,6 +216,12 @@ static enum qcom_scm_convention __get_convention(void) 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 */ @@ -187,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); @@ -403,6 +458,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 = { @@ -1321,6 +1399,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 +1880,26 @@ 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. + * + * 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/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 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/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, 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/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index e32a4161a8d0..674abd0d6700 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -12,6 +12,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/nvmem-consumer.h> #include <linux/of.h> #include <linux/regmap.h> #include <linux/sizes.h> @@ -126,6 +127,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, @@ -185,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 }, @@ -356,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, @@ -422,101 +458,221 @@ 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 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, + .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 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 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), }; -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_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; @@ -906,11 +1062,28 @@ static int qcom_llcc_cfg_program(struct platform_device *pdev, return ret; } -static int qcom_llcc_remove(struct platform_device *pdev) +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 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, @@ -938,12 +1111,17 @@ 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; + u8 cfg_index; 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; @@ -957,7 +1135,15 @@ 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; + } + 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) @@ -1050,18 +1236,19 @@ 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,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 }, + { .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); @@ -1072,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); diff --git a/drivers/soc/qcom/ocmem.c b/drivers/soc/qcom/ocmem.c index 20f5461d46b9..e8841d247953 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) { @@ -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, 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/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, 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, 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..aff0cfb71482 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) { @@ -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); 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); 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, 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/rmtfs_mem.c b/drivers/soc/qcom/rmtfs_mem.c index f83811f51175..df850d073102 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; @@ -281,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; @@ -296,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[] = { @@ -308,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, 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 d4a89d2bb43b..690afc9a12f4 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 * @@ -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, @@ -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, 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, 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, diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 497cfb720fcb..51e05bec5bfc 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 { @@ -349,6 +355,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) }, @@ -359,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) }, @@ -389,6 +399,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) }, @@ -776,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", }, diff --git a/drivers/soc/qcom/wcnss_ctrl.c b/drivers/soc/qcom/wcnss_ctrl.c index ad9942412c58..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) { @@ -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, }, }; diff --git a/include/dt-bindings/arm/qcom,ids.h b/include/dt-bindings/arm/qcom,ids.h index be12e1dd1f38..f7248348a459 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 @@ -203,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 @@ -233,6 +237,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 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 <luzmaximilian@gmail.com> + */ +#include <linux/auxiliary_bus.h> +#include <linux/types.h> + +#include <linux/firmware/qcom/qcom_scm.h> + +/** + * 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..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,54 +72,69 @@ 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 + +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 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) { 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 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) { |