Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /* Copyright (c) 2015,2019 The Linux Foundation. All rights reserved.
0003  */
0004 
0005 #include <linux/io.h>
0006 #include <linux/errno.h>
0007 #include <linux/delay.h>
0008 #include <linux/mutex.h>
0009 #include <linux/slab.h>
0010 #include <linux/types.h>
0011 #include <linux/qcom_scm.h>
0012 #include <linux/arm-smccc.h>
0013 #include <linux/dma-mapping.h>
0014 
0015 #include "qcom_scm.h"
0016 
0017 /**
0018  * struct arm_smccc_args
0019  * @args:   The array of values used in registers in smc instruction
0020  */
0021 struct arm_smccc_args {
0022     unsigned long args[8];
0023 };
0024 
0025 static DEFINE_MUTEX(qcom_scm_lock);
0026 
0027 #define QCOM_SCM_EBUSY_WAIT_MS 30
0028 #define QCOM_SCM_EBUSY_MAX_RETRY 20
0029 
0030 #define SCM_SMC_N_REG_ARGS  4
0031 #define SCM_SMC_FIRST_EXT_IDX   (SCM_SMC_N_REG_ARGS - 1)
0032 #define SCM_SMC_N_EXT_ARGS  (MAX_QCOM_SCM_ARGS - SCM_SMC_N_REG_ARGS + 1)
0033 #define SCM_SMC_FIRST_REG_IDX   2
0034 #define SCM_SMC_LAST_REG_IDX    (SCM_SMC_FIRST_REG_IDX + SCM_SMC_N_REG_ARGS - 1)
0035 
0036 static void __scm_smc_do_quirk(const struct arm_smccc_args *smc,
0037                    struct arm_smccc_res *res)
0038 {
0039     unsigned long a0 = smc->args[0];
0040     struct arm_smccc_quirk quirk = { .id = ARM_SMCCC_QUIRK_QCOM_A6 };
0041 
0042     quirk.state.a6 = 0;
0043 
0044     do {
0045         arm_smccc_smc_quirk(a0, smc->args[1], smc->args[2],
0046                     smc->args[3], smc->args[4], smc->args[5],
0047                     quirk.state.a6, smc->args[7], res, &quirk);
0048 
0049         if (res->a0 == QCOM_SCM_INTERRUPTED)
0050             a0 = res->a0;
0051 
0052     } while (res->a0 == QCOM_SCM_INTERRUPTED);
0053 }
0054 
0055 static void __scm_smc_do(const struct arm_smccc_args *smc,
0056              struct arm_smccc_res *res, bool atomic)
0057 {
0058     int retry_count = 0;
0059 
0060     if (atomic) {
0061         __scm_smc_do_quirk(smc, res);
0062         return;
0063     }
0064 
0065     do {
0066         mutex_lock(&qcom_scm_lock);
0067 
0068         __scm_smc_do_quirk(smc, res);
0069 
0070         mutex_unlock(&qcom_scm_lock);
0071 
0072         if (res->a0 == QCOM_SCM_V2_EBUSY) {
0073             if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY)
0074                 break;
0075             msleep(QCOM_SCM_EBUSY_WAIT_MS);
0076         }
0077     }  while (res->a0 == QCOM_SCM_V2_EBUSY);
0078 }
0079 
0080 
0081 int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
0082            enum qcom_scm_convention qcom_convention,
0083            struct qcom_scm_res *res, bool atomic)
0084 {
0085     int arglen = desc->arginfo & 0xf;
0086     int i;
0087     dma_addr_t args_phys = 0;
0088     void *args_virt = NULL;
0089     size_t alloc_len;
0090     gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL;
0091     u32 smccc_call_type = atomic ? ARM_SMCCC_FAST_CALL : ARM_SMCCC_STD_CALL;
0092     u32 qcom_smccc_convention = (qcom_convention == SMC_CONVENTION_ARM_32) ?
0093                     ARM_SMCCC_SMC_32 : ARM_SMCCC_SMC_64;
0094     struct arm_smccc_res smc_res;
0095     struct arm_smccc_args smc = {0};
0096 
0097     smc.args[0] = ARM_SMCCC_CALL_VAL(
0098         smccc_call_type,
0099         qcom_smccc_convention,
0100         desc->owner,
0101         SCM_SMC_FNID(desc->svc, desc->cmd));
0102     smc.args[1] = desc->arginfo;
0103     for (i = 0; i < SCM_SMC_N_REG_ARGS; i++)
0104         smc.args[i + SCM_SMC_FIRST_REG_IDX] = desc->args[i];
0105 
0106     if (unlikely(arglen > SCM_SMC_N_REG_ARGS)) {
0107         alloc_len = SCM_SMC_N_EXT_ARGS * sizeof(u64);
0108         args_virt = kzalloc(PAGE_ALIGN(alloc_len), flag);
0109 
0110         if (!args_virt)
0111             return -ENOMEM;
0112 
0113         if (qcom_smccc_convention == ARM_SMCCC_SMC_32) {
0114             __le32 *args = args_virt;
0115 
0116             for (i = 0; i < SCM_SMC_N_EXT_ARGS; i++)
0117                 args[i] = cpu_to_le32(desc->args[i +
0118                               SCM_SMC_FIRST_EXT_IDX]);
0119         } else {
0120             __le64 *args = args_virt;
0121 
0122             for (i = 0; i < SCM_SMC_N_EXT_ARGS; i++)
0123                 args[i] = cpu_to_le64(desc->args[i +
0124                               SCM_SMC_FIRST_EXT_IDX]);
0125         }
0126 
0127         args_phys = dma_map_single(dev, args_virt, alloc_len,
0128                        DMA_TO_DEVICE);
0129 
0130         if (dma_mapping_error(dev, args_phys)) {
0131             kfree(args_virt);
0132             return -ENOMEM;
0133         }
0134 
0135         smc.args[SCM_SMC_LAST_REG_IDX] = args_phys;
0136     }
0137 
0138     __scm_smc_do(&smc, &smc_res, atomic);
0139 
0140     if (args_virt) {
0141         dma_unmap_single(dev, args_phys, alloc_len, DMA_TO_DEVICE);
0142         kfree(args_virt);
0143     }
0144 
0145     if (res) {
0146         res->result[0] = smc_res.a1;
0147         res->result[1] = smc_res.a2;
0148         res->result[2] = smc_res.a3;
0149     }
0150 
0151     return (long)smc_res.a0 ? qcom_scm_remap_error(smc_res.a0) : 0;
0152 
0153 }