]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/firmware/qcom_scm-64.c
Merge tag 'pci-v5.6-fixes-1' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci
[linux.git] / drivers / firmware / qcom_scm-64.c
index 91d5ad7cf58b7dfd74c79315794af604f0daec5c..3c5850350974dc99fe85d3a36583604349c5c099 100644 (file)
@@ -62,32 +62,72 @@ static DEFINE_MUTEX(qcom_scm_lock);
 #define FIRST_EXT_ARG_IDX 3
 #define N_REGISTER_ARGS (MAX_QCOM_SCM_ARGS - N_EXT_QCOM_SCM_ARGS + 1)
 
-/**
- * qcom_scm_call() - Invoke a syscall in the secure world
- * @dev:       device
- * @svc_id:    service identifier
- * @cmd_id:    command identifier
- * @desc:      Descriptor structure containing arguments and return values
- *
- * Sends a command to the SCM and waits for the command to finish processing.
- * This should *only* be called in pre-emptible context.
-*/
-static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id,
-                        const struct qcom_scm_desc *desc,
-                        struct arm_smccc_res *res)
+static void __qcom_scm_call_do(const struct qcom_scm_desc *desc,
+                              struct arm_smccc_res *res, u32 fn_id,
+                              u64 x5, u32 type)
+{
+       u64 cmd;
+       struct arm_smccc_quirk quirk = { .id = ARM_SMCCC_QUIRK_QCOM_A6 };
+
+       cmd = ARM_SMCCC_CALL_VAL(type, qcom_smccc_convention,
+                                ARM_SMCCC_OWNER_SIP, fn_id);
+
+       quirk.state.a6 = 0;
+
+       do {
+               arm_smccc_smc_quirk(cmd, desc->arginfo, desc->args[0],
+                                   desc->args[1], desc->args[2], x5,
+                                   quirk.state.a6, 0, res, &quirk);
+
+               if (res->a0 == QCOM_SCM_INTERRUPTED)
+                       cmd = res->a0;
+
+       } while (res->a0 == QCOM_SCM_INTERRUPTED);
+}
+
+static void qcom_scm_call_do(const struct qcom_scm_desc *desc,
+                            struct arm_smccc_res *res, u32 fn_id,
+                            u64 x5, bool atomic)
+{
+       int retry_count = 0;
+
+       if (atomic) {
+               __qcom_scm_call_do(desc, res, fn_id, x5, ARM_SMCCC_FAST_CALL);
+               return;
+       }
+
+       do {
+               mutex_lock(&qcom_scm_lock);
+
+               __qcom_scm_call_do(desc, res, fn_id, x5,
+                                  ARM_SMCCC_STD_CALL);
+
+               mutex_unlock(&qcom_scm_lock);
+
+               if (res->a0 == QCOM_SCM_V2_EBUSY) {
+                       if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY)
+                               break;
+                       msleep(QCOM_SCM_EBUSY_WAIT_MS);
+               }
+       }  while (res->a0 == QCOM_SCM_V2_EBUSY);
+}
+
+static int ___qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id,
+                           const struct qcom_scm_desc *desc,
+                           struct arm_smccc_res *res, bool atomic)
 {
        int arglen = desc->arginfo & 0xf;
-       int retry_count = 0, i;
+       int i;
        u32 fn_id = QCOM_SCM_FNID(svc_id, cmd_id);
-       u64 cmd, x5 = desc->args[FIRST_EXT_ARG_IDX];
+       u64 x5 = desc->args[FIRST_EXT_ARG_IDX];
        dma_addr_t args_phys = 0;
        void *args_virt = NULL;
        size_t alloc_len;
-       struct arm_smccc_quirk quirk = {.id = ARM_SMCCC_QUIRK_QCOM_A6};
+       gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL;
 
        if (unlikely(arglen > N_REGISTER_ARGS)) {
                alloc_len = N_EXT_QCOM_SCM_ARGS * sizeof(u64);
-               args_virt = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL);
+               args_virt = kzalloc(PAGE_ALIGN(alloc_len), flag);
 
                if (!args_virt)
                        return -ENOMEM;
@@ -117,45 +157,55 @@ static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id,
                x5 = args_phys;
        }
 
-       do {
-               mutex_lock(&qcom_scm_lock);
-
-               cmd = ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL,
-                                        qcom_smccc_convention,
-                                        ARM_SMCCC_OWNER_SIP, fn_id);
-
-               quirk.state.a6 = 0;
-
-               do {
-                       arm_smccc_smc_quirk(cmd, desc->arginfo, desc->args[0],
-                                     desc->args[1], desc->args[2], x5,
-                                     quirk.state.a6, 0, res, &quirk);
-
-                       if (res->a0 == QCOM_SCM_INTERRUPTED)
-                               cmd = res->a0;
-
-               } while (res->a0 == QCOM_SCM_INTERRUPTED);
-
-               mutex_unlock(&qcom_scm_lock);
-
-               if (res->a0 == QCOM_SCM_V2_EBUSY) {
-                       if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY)
-                               break;
-                       msleep(QCOM_SCM_EBUSY_WAIT_MS);
-               }
-       }  while (res->a0 == QCOM_SCM_V2_EBUSY);
+       qcom_scm_call_do(desc, res, fn_id, x5, atomic);
 
        if (args_virt) {
                dma_unmap_single(dev, args_phys, alloc_len, DMA_TO_DEVICE);
                kfree(args_virt);
        }
 
-       if (res->a0 < 0)
+       if ((long)res->a0 < 0)
                return qcom_scm_remap_error(res->a0);
 
        return 0;
 }
 
+/**
+ * qcom_scm_call() - Invoke a syscall in the secure world
+ * @dev:       device
+ * @svc_id:    service identifier
+ * @cmd_id:    command identifier
+ * @desc:      Descriptor structure containing arguments and return values
+ *
+ * Sends a command to the SCM and waits for the command to finish processing.
+ * This should *only* be called in pre-emptible context.
+ */
+static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id,
+                        const struct qcom_scm_desc *desc,
+                        struct arm_smccc_res *res)
+{
+       might_sleep();
+       return ___qcom_scm_call(dev, svc_id, cmd_id, desc, res, false);
+}
+
+/**
+ * qcom_scm_call_atomic() - atomic variation of qcom_scm_call()
+ * @dev:       device
+ * @svc_id:    service identifier
+ * @cmd_id:    command identifier
+ * @desc:      Descriptor structure containing arguments and return values
+ * @res:       Structure containing results from SMC/HVC call
+ *
+ * Sends a command to the SCM and waits for the command to finish processing.
+ * This can be called in atomic context.
+ */
+static int qcom_scm_call_atomic(struct device *dev, u32 svc_id, u32 cmd_id,
+                               const struct qcom_scm_desc *desc,
+                               struct arm_smccc_res *res)
+{
+       return ___qcom_scm_call(dev, svc_id, cmd_id, desc, res, true);
+}
+
 /**
  * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus
  * @entry: Entry point function for the cpus
@@ -241,6 +291,18 @@ int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req,
        return ret;
 }
 
+int __qcom_scm_ocmem_lock(struct device *dev, uint32_t id, uint32_t offset,
+                         uint32_t size, uint32_t mode)
+{
+       return -ENOTSUPP;
+}
+
+int __qcom_scm_ocmem_unlock(struct device *dev, uint32_t id, uint32_t offset,
+                           uint32_t size)
+{
+       return -ENOTSUPP;
+}
+
 void __qcom_scm_init(void)
 {
        u64 cmd;
@@ -502,3 +564,16 @@ int __qcom_scm_io_writel(struct device *dev, phys_addr_t addr, unsigned int val)
        return qcom_scm_call(dev, QCOM_SCM_SVC_IO, QCOM_SCM_IO_WRITE,
                             &desc, &res);
 }
+
+int __qcom_scm_qsmmu500_wait_safe_toggle(struct device *dev, bool en)
+{
+       struct qcom_scm_desc desc = {0};
+       struct arm_smccc_res res;
+
+       desc.args[0] = QCOM_SCM_CONFIG_ERRATA1_CLIENT_ALL;
+       desc.args[1] = en;
+       desc.arginfo = QCOM_SCM_ARGS(2);
+
+       return qcom_scm_call_atomic(dev, QCOM_SCM_SVC_SMMU_PROGRAM,
+                                   QCOM_SCM_CONFIG_ERRATA1, &desc, &res);
+}