aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/cache/qcom,llcc.yaml10
-rw-r--r--Documentation/devicetree/bindings/firmware/qcom,scm.yaml10
-rw-r--r--Documentation/devicetree/bindings/reserved-memory/qcom,rmtfs-mem.yaml11
-rw-r--r--Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml2
-rw-r--r--MAINTAINERS12
-rw-r--r--drivers/firmware/Kconfig33
-rw-r--r--drivers/firmware/Makefile2
-rw-r--r--drivers/firmware/qcom_qseecom.c120
-rw-r--r--drivers/firmware/qcom_qseecom_uefisecapp.c871
-rw-r--r--drivers/firmware/qcom_scm.c448
-rw-r--r--drivers/firmware/qcom_scm.h1
-rw-r--r--drivers/soc/qcom/cmd-db.c8
-rw-r--r--drivers/soc/qcom/icc-bwmon.c6
-rw-r--r--drivers/soc/qcom/kryo-l2-accessors.c4
-rw-r--r--drivers/soc/qcom/llcc-qcom.c367
-rw-r--r--drivers/soc/qcom/ocmem.c12
-rw-r--r--drivers/soc/qcom/pdr_interface.c8
-rw-r--r--drivers/soc/qcom/pmic_glink.c6
-rw-r--r--drivers/soc/qcom/pmic_glink_altmode.c30
-rw-r--r--drivers/soc/qcom/qcom-geni-se.c38
-rw-r--r--drivers/soc/qcom/qcom_aoss.c12
-rw-r--r--drivers/soc/qcom/qcom_gsbi.c6
-rw-r--r--drivers/soc/qcom/qcom_stats.c6
-rw-r--r--drivers/soc/qcom/qmi_encdec.c6
-rw-r--r--drivers/soc/qcom/qmi_interface.c20
-rw-r--r--drivers/soc/qcom/rmtfs_mem.c15
-rw-r--r--drivers/soc/qcom/rpmh.c8
-rw-r--r--drivers/soc/qcom/smd-rpm.c2
-rw-r--r--drivers/soc/qcom/smem.c10
-rw-r--r--drivers/soc/qcom/smp2p.c6
-rw-r--r--drivers/soc/qcom/smsm.c6
-rw-r--r--drivers/soc/qcom/socinfo.c17
-rw-r--r--drivers/soc/qcom/wcnss_ctrl.c3
-rw-r--r--include/dt-bindings/arm/qcom,ids.h5
-rw-r--r--include/linux/firmware/qcom/qcom_qseecom.h46
-rw-r--r--include/linux/firmware/qcom/qcom_scm.h123
-rw-r--r--include/linux/nvmem-consumer.h6
-rw-r--r--include/linux/soc/qcom/llcc-qcom.h2
-rw-r--r--include/linux/ucs2_string.h1
-rw-r--r--lib/ucs2_string.c52
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)
{