Merge tag 'qcom-drivers-for-6.7' of https://git.kernel.org/pub/scm/linux/kernel/git...
authorArnd Bergmann <arnd@arndb.de>
Wed, 18 Oct 2023 15:17:56 +0000 (17:17 +0200)
committerArnd Bergmann <arnd@arndb.de>
Wed, 18 Oct 2023 15:18:02 +0000 (17:18 +0200)
Qualcomm driver updates for v6.7

This introduces partial support for the Qualcomm Secure Execution
Environment SCM interface, and uses this to implement EFI variable
access on the Windows On Snapdragon devices (for now).

The 32/64-bit calling convention detector of the SCM interface is
updated to not choose 64-bit convention when Linux is 32-bit. The
"extern" specifier is dropped from the interface include file.

The LLCC driver gains support for carrying configuration for multiple
different system/DDR configurations for a given platform, and selecting
between them. Support for Q[DR]U1000 is added to the driver.

All exported symbols are transitioned to EXPORT_SYMBOL_GPL().

The platform_drivers in the Qualcomm SoC are transitioned to the
void-returning remove_new implementation.

The rmtfs memory driver gains support for leaving guard pages around the
used area, to avoid issues if the allocation happens to be placed
adjacent to another protected memory region.

The socinfo driver gains knowledge about IPQ8174, QCM6490, SM7150P and
various PMICs used together with SM8550.

* tag 'qcom-drivers-for-6.7' of https://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux: (44 commits)
  soc: qcom: socinfo: Convert to platform remove callback returning void
  soc: qcom: smsm: Convert to platform remove callback returning void
  soc: qcom: smp2p: Convert to platform remove callback returning void
  soc: qcom: smem: Convert to platform remove callback returning void
  soc: qcom: rmtfs_mem: Convert to platform remove callback returning void
  soc: qcom: qcom_stats: Convert to platform remove callback returning void
  soc: qcom: qcom_gsbi: Convert to platform remove callback returning void
  soc: qcom: qcom_aoss: Convert to platform remove callback returning void
  soc: qcom: pmic_glink: Convert to platform remove callback returning void
  soc: qcom: ocmem: Convert to platform remove callback returning void
  soc: qcom: llcc-qcom: Convert to platform remove callback returning void
  soc: qcom: icc-bwmon: Convert to platform remove callback returning void
  firmware: qcom_scm: use 64-bit calling convention only when client is 64-bit
  soc: qcom: llcc: Handle a second device without data corruption
  soc: qcom: Switch to EXPORT_SYMBOL_GPL()
  soc: qcom: smem: Annotate struct qcom_smem with __counted_by
  soc: qcom: rmtfs: Support discarding guard pages
  dt-bindings: reserved-memory: rmtfs: Allow guard pages
  dt-bindings: firmware: qcom,scm: document IPQ5018 compatible
  firmware: qcom_scm: disable SDI if required
  ...

Link: https://lore.kernel.org/r/20231015204014.855672-1-andersson@kernel.org
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
40 files changed:
Documentation/devicetree/bindings/cache/qcom,llcc.yaml
Documentation/devicetree/bindings/firmware/qcom,scm.yaml
Documentation/devicetree/bindings/reserved-memory/qcom,rmtfs-mem.yaml
Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml
MAINTAINERS
drivers/firmware/Kconfig
drivers/firmware/Makefile
drivers/firmware/qcom_qseecom.c [new file with mode: 0644]
drivers/firmware/qcom_qseecom_uefisecapp.c [new file with mode: 0644]
drivers/firmware/qcom_scm.c
drivers/firmware/qcom_scm.h
drivers/soc/qcom/cmd-db.c
drivers/soc/qcom/icc-bwmon.c
drivers/soc/qcom/kryo-l2-accessors.c
drivers/soc/qcom/llcc-qcom.c
drivers/soc/qcom/ocmem.c
drivers/soc/qcom/pdr_interface.c
drivers/soc/qcom/pmic_glink.c
drivers/soc/qcom/pmic_glink_altmode.c
drivers/soc/qcom/qcom-geni-se.c
drivers/soc/qcom/qcom_aoss.c
drivers/soc/qcom/qcom_gsbi.c
drivers/soc/qcom/qcom_stats.c
drivers/soc/qcom/qmi_encdec.c
drivers/soc/qcom/qmi_interface.c
drivers/soc/qcom/rmtfs_mem.c
drivers/soc/qcom/rpmh.c
drivers/soc/qcom/smd-rpm.c
drivers/soc/qcom/smem.c
drivers/soc/qcom/smp2p.c
drivers/soc/qcom/smsm.c
drivers/soc/qcom/socinfo.c
drivers/soc/qcom/wcnss_ctrl.c
include/dt-bindings/arm/qcom,ids.h
include/linux/firmware/qcom/qcom_qseecom.h [new file with mode: 0644]
include/linux/firmware/qcom/qcom_scm.h
include/linux/nvmem-consumer.h
include/linux/soc/qcom/llcc-qcom.h
include/linux/ucs2_string.h
lib/ucs2_string.c

index 44892aa589fd00215fc284077b95b3d0ed5a7e42..580f9a97ddf7824887a369c7c030dfbbe84c1d0a 100644 (file)
@@ -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:
index 4233ea839bfca764631c7bff57050f27f3992b64..0613a37a851af4f049320d46465e89c3a4eb50f1 100644 (file)
@@ -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:
index bab982f004852e631f40a90b7e665e6988fc3489..46407e9c1d4fa42c256f17bc9251d7d27788d636 100644 (file)
@@ -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: >
index 8a4b7ba3aaf62e9ddb810cf7fc7fac1ed58bb5bf..7b031ef09669f74eb33a41fd7db88da7260384ae 100644 (file)
@@ -52,6 +52,8 @@ properties:
   iommus:
     maxItems: 1
 
+  dma-coherent: true
+
 required:
   - compatible
   - reg
index 6dcf2926109a67875700844e8ec7855ea1b0499b..75e05a055090ef2100a2d311884403412dec4992 100644 (file)
@@ -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>
index b59e3041fd627585c4662f35c1fc34103df9730d..8c608be300601f63eeaf6ccf26d5d63b37c43155 100644 (file)
@@ -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
index 28fcddcd688fc2fd0f977df39126bf251808b970..d41b094a5e580e92fb3be65bcc7668d98b3dfb60 100644 (file)
@@ -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 (file)
index 0000000..731e6d5
--- /dev/null
@@ -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 (file)
index 0000000..a33acda
--- /dev/null
@@ -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");
index 06fe8aca870d7bf5e34c592b69af1fe4dd1e3888..520de9b5633abc5aab075400b7feb6b97dcef97c 100644 (file)
@@ -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)
@@ -167,6 +215,12 @@ static enum qcom_scm_convention __get_convention(void)
        if (likely(qcom_scm_convention != SMC_CONVENTION_UNKNOWN))
                return qcom_scm_convention;
 
+       /*
+        * Per the "SMC calling convention specification", the 64-bit calling
+        * convention can only be used when the client is 64-bit, otherwise
+        * system will encounter the undefined behaviour.
+        */
+#if IS_ENABLED(CONFIG_ARM64)
        /*
         * Device isn't required as there is only one argument - no device
         * needed to dma_map_single to secure world
@@ -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;
 }
 
index e6e512bd57d1b95d77735400c669a9832def8768..7b68fa820495c0c916b0942dd3319c0bf6812963 100644 (file)
@@ -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
index 34c40368d5b5eca960eddf394735702acbed977a..a5fd68411bed587a9796615b65071558bde6fd75 100644 (file)
@@ -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)
index adf2d523f103c48c5e2a098920c0f54a75deb2b6..656706259353390b1b511ee9b12745a7bb2fd2aa 100644 (file)
@@ -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,
index 7886af4fd7267dd9de579ce489200024a2057fba..50cd710c5e824ef9f44bc0cfecffac8899807b37 100644 (file)
@@ -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);
index e32a4161a8d025e5a6004330520de29ce73e65bb..674abd0d6700798a8cea9966fed3387bc97f910e 100644 (file)
@@ -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);
 
index 20f5461d46b91bdcaeb93f776a0295eff43af539..e8841d2479539ed2dbc44aa8797b84c9513c9790 100644 (file)
@@ -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,
index 0034af927b4887f1ed42383d629c7d82dce4f619..a1b6a4081dea72744b64d008154bc52766d8252c 100644 (file)
@@ -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");
index 61c89ddfc75b855d4b680c6411b3ed6a35832da7..914057331afd7f94fc7da7b2625e2743beca9bb8 100644 (file)
@@ -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,
index d05e0d6edf4930f52cb15f4eeea737a38f4a4c30..9569d999391d1858bae98e8d11fa3de6ee8653ac 100644 (file)
@@ -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,
index ba788762835fad7738ad968276c9fd5b7b767941..bdcf44b85b2f2704a0cdfff6c5f370ab9ba32c4a 100644 (file)
@@ -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)
 {
index 77f0cf126629e463af6771472804a8f12da46229..aff0cfb71482026e116a9b70812034da35d520c8 100644 (file)
@@ -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);
 
index df7907a83aa8611bee25bd33d35c1eea84e09367..f04b9a324ea9ce1d91413b6076a2b040ba913490 100644 (file)
@@ -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);
index c207bb96c523abc307a766013ed4e0427ee3e63c..0216fc24f2caba4df6cd352ae7ca13d107fedeeb 100644 (file)
@@ -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,
index 5c7161b18b7240056c1ab053d17eb8e961ad9925..bb09eff85cff3bf32447cbd2060e38d1dd510bb1 100644 (file)
@@ -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");
index 78d7361fdcf2d71f8896d85119412d846f1fa778..bb98b06e87f88e58f5231c09e0c65c9810918d9d 100644 (file)
@@ -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);
index f83811f51175352777ccc95886df4d39d4ffd8cb..df850d0731022d04bf8f969404b7db3b9f102514 100644 (file)
@@ -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,
index 08e09642d7f551308e3fe7bd37b2e20d32f7d51f..9f26d7f9b9dc46a4e4910c1037e18bab374cfd28 100644 (file)
@@ -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);
index f9fd6177118cac50f8e1b87ae4209e0e33f4dbbb..b7056aed4c7d10ae766f8e72310cf0d684f746c1 100644 (file)
@@ -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,
index d4a89d2bb43bb4090a4c0101d9c4298098cb344b..690afc9a12f4ca37fb245cb7e43e8f66d9508f0f 100644 (file)
@@ -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,
index e9c8030d50ee5f11e150d22817c2e8c13705cad2..914b2246148fcb4c44ec3e267bb41868761e2b9c 100644 (file)
@@ -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,
index c58cfff64856e2b8f27af204ab971820429818cb..e7c7e9a640a6697dd302490a5506031b709d9e9b 100644 (file)
@@ -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,
index 497cfb720fcb04214daff3f307ad4aca03f784b6..51e05bec5bfcea540b74bfec6699f1b19d62a868 100644 (file)
@@ -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",
        },
index ad9942412c589c78da652643558aa6f5ff2930ec..148bcbac332dd3b478d5ea86e175c57404eed87f 100644 (file)
@@ -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,
        },
 };
index be12e1dd1f38fad7680f9e5aa485123795a3024b..f7248348a45946596a5d73fa40a2598f5dae9a54 100644 (file)
 #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
 #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
 #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 (file)
index 0000000..b531547
--- /dev/null
@@ -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);
+}
index 0c091a3f6d49f28122b5f305f1142b8a9ab4e614..ccaf288460546a8617a9c7efc5132de8d5fca4e7 100644 (file)
@@ -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
index 4523e4e8331970d3f6e48cb408f825329f407d8f..6ec4b9743e25d4463fafe5d44d51ab89eacc316d 100644 (file)
@@ -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)
 {
index 93417ba1ead4a534c3f77064913472eff3dc4c04..1a886666bbb62597eb41439093d70fa405d188a5 100644 (file)
@@ -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
index cf3ada3e820ed21e794f1f5ab911706e62c068a2..c499ae809c7d991cca9e9ed652167f4d89e8e700 100644 (file)
@@ -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);
index 0a559a42359b82182c37d5f9f1520e01cbe52f4f..9308bcfb2ad50c71b11f502002f68a347d660346 100644 (file)
@@ -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)
 {