Blame view
drivers/firmware/qcom_scm-smc.c
3.68 KB
97fb5e8d9 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
e0aa15396 firmware: qcom_sc... |
2 |
/* Copyright (c) 2015,2019 The Linux Foundation. All rights reserved. |
50b956f3d firmware: qcom: s... |
3 4 5 6 |
*/ #include <linux/io.h> #include <linux/errno.h> |
6b1751a86 firmware: qcom: s... |
7 8 9 10 |
#include <linux/delay.h> #include <linux/mutex.h> #include <linux/slab.h> #include <linux/types.h> |
50b956f3d firmware: qcom: s... |
11 |
#include <linux/qcom_scm.h> |
6b1751a86 firmware: qcom: s... |
12 13 14 15 |
#include <linux/arm-smccc.h> #include <linux/dma-mapping.h> #include "qcom_scm.h" |
3f951ea62 firmware: qcom_sc... |
16 17 18 19 20 21 22 |
/** * struct arm_smccc_args * @args: The array of values used in registers in smc instruction */ struct arm_smccc_args { unsigned long args[8]; }; |
6b1751a86 firmware: qcom: s... |
23 24 25 26 |
static DEFINE_MUTEX(qcom_scm_lock); #define QCOM_SCM_EBUSY_WAIT_MS 30 #define QCOM_SCM_EBUSY_MAX_RETRY 20 |
4a9f1e13b firmware: qcom_sc... |
27 28 29 |
#define SCM_SMC_N_REG_ARGS 4 #define SCM_SMC_FIRST_EXT_IDX (SCM_SMC_N_REG_ARGS - 1) #define SCM_SMC_N_EXT_ARGS (MAX_QCOM_SCM_ARGS - SCM_SMC_N_REG_ARGS + 1) |
3f951ea62 firmware: qcom_sc... |
30 31 |
#define SCM_SMC_FIRST_REG_IDX 2 #define SCM_SMC_LAST_REG_IDX (SCM_SMC_FIRST_REG_IDX + SCM_SMC_N_REG_ARGS - 1) |
6b1751a86 firmware: qcom: s... |
32 |
|
3f951ea62 firmware: qcom_sc... |
33 34 |
static void __scm_smc_do_quirk(const struct arm_smccc_args *smc, struct arm_smccc_res *res) |
1a5ea3b7a firmware: qcom_sc... |
35 |
{ |
3f951ea62 firmware: qcom_sc... |
36 |
unsigned long a0 = smc->args[0]; |
1a5ea3b7a firmware: qcom_sc... |
37 |
struct arm_smccc_quirk quirk = { .id = ARM_SMCCC_QUIRK_QCOM_A6 }; |
1a5ea3b7a firmware: qcom_sc... |
38 39 40 |
quirk.state.a6 = 0; do { |
3f951ea62 firmware: qcom_sc... |
41 42 43 |
arm_smccc_smc_quirk(a0, smc->args[1], smc->args[2], smc->args[3], smc->args[4], smc->args[5], quirk.state.a6, smc->args[7], res, &quirk); |
1a5ea3b7a firmware: qcom_sc... |
44 45 |
if (res->a0 == QCOM_SCM_INTERRUPTED) |
3f951ea62 firmware: qcom_sc... |
46 |
a0 = res->a0; |
1a5ea3b7a firmware: qcom_sc... |
47 48 49 |
} while (res->a0 == QCOM_SCM_INTERRUPTED); } |
3f951ea62 firmware: qcom_sc... |
50 51 |
static void __scm_smc_do(const struct arm_smccc_args *smc, struct arm_smccc_res *res, bool atomic) |
1a5ea3b7a firmware: qcom_sc... |
52 53 54 55 |
{ int retry_count = 0; if (atomic) { |
3f951ea62 firmware: qcom_sc... |
56 |
__scm_smc_do_quirk(smc, res); |
1a5ea3b7a firmware: qcom_sc... |
57 58 59 60 61 |
return; } do { mutex_lock(&qcom_scm_lock); |
3f951ea62 firmware: qcom_sc... |
62 |
__scm_smc_do_quirk(smc, res); |
1a5ea3b7a firmware: qcom_sc... |
63 64 65 66 67 68 69 70 71 72 |
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); } |
9a434cee7 firmware: qcom_sc... |
73 74 |
int scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc, struct qcom_scm_res *res, bool atomic) |
6b1751a86 firmware: qcom: s... |
75 76 |
{ int arglen = desc->arginfo & 0xf; |
1a5ea3b7a firmware: qcom_sc... |
77 |
int i; |
6b1751a86 firmware: qcom: s... |
78 79 80 |
dma_addr_t args_phys = 0; void *args_virt = NULL; size_t alloc_len; |
1a5ea3b7a firmware: qcom_sc... |
81 |
gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL; |
3f951ea62 firmware: qcom_sc... |
82 |
u32 smccc_call_type = atomic ? ARM_SMCCC_FAST_CALL : ARM_SMCCC_STD_CALL; |
9a434cee7 firmware: qcom_sc... |
83 84 85 |
u32 qcom_smccc_convention = (qcom_scm_convention == SMC_CONVENTION_ARM_32) ? ARM_SMCCC_SMC_32 : ARM_SMCCC_SMC_64; |
1f7166fda firmware: qcom_sc... |
86 |
struct arm_smccc_res smc_res; |
3f951ea62 firmware: qcom_sc... |
87 88 89 90 91 92 93 94 95 96 |
struct arm_smccc_args smc = {0}; smc.args[0] = ARM_SMCCC_CALL_VAL( smccc_call_type, qcom_smccc_convention, desc->owner, SCM_SMC_FNID(desc->svc, desc->cmd)); smc.args[1] = desc->arginfo; for (i = 0; i < SCM_SMC_N_REG_ARGS; i++) smc.args[i + SCM_SMC_FIRST_REG_IDX] = desc->args[i]; |
6b1751a86 firmware: qcom: s... |
97 |
|
e0aa15396 firmware: qcom_sc... |
98 99 |
if (unlikely(arglen > SCM_SMC_N_REG_ARGS)) { alloc_len = SCM_SMC_N_EXT_ARGS * sizeof(u64); |
1a5ea3b7a firmware: qcom_sc... |
100 |
args_virt = kzalloc(PAGE_ALIGN(alloc_len), flag); |
6b1751a86 firmware: qcom: s... |
101 102 103 104 105 106 |
if (!args_virt) return -ENOMEM; if (qcom_smccc_convention == ARM_SMCCC_SMC_32) { __le32 *args = args_virt; |
e0aa15396 firmware: qcom_sc... |
107 |
for (i = 0; i < SCM_SMC_N_EXT_ARGS; i++) |
6b1751a86 firmware: qcom: s... |
108 |
args[i] = cpu_to_le32(desc->args[i + |
e0aa15396 firmware: qcom_sc... |
109 |
SCM_SMC_FIRST_EXT_IDX]); |
6b1751a86 firmware: qcom: s... |
110 111 |
} else { __le64 *args = args_virt; |
e0aa15396 firmware: qcom_sc... |
112 |
for (i = 0; i < SCM_SMC_N_EXT_ARGS; i++) |
6b1751a86 firmware: qcom: s... |
113 |
args[i] = cpu_to_le64(desc->args[i + |
e0aa15396 firmware: qcom_sc... |
114 |
SCM_SMC_FIRST_EXT_IDX]); |
6b1751a86 firmware: qcom: s... |
115 116 117 118 119 120 121 122 123 |
} args_phys = dma_map_single(dev, args_virt, alloc_len, DMA_TO_DEVICE); if (dma_mapping_error(dev, args_phys)) { kfree(args_virt); return -ENOMEM; } |
3f951ea62 firmware: qcom_sc... |
124 |
smc.args[SCM_SMC_LAST_REG_IDX] = args_phys; |
6b1751a86 firmware: qcom: s... |
125 |
} |
3f951ea62 firmware: qcom_sc... |
126 |
__scm_smc_do(&smc, &smc_res, atomic); |
6b1751a86 firmware: qcom: s... |
127 128 129 130 131 |
if (args_virt) { dma_unmap_single(dev, args_phys, alloc_len, DMA_TO_DEVICE); kfree(args_virt); } |
1f7166fda firmware: qcom_sc... |
132 133 134 135 136 |
if (res) { res->result[0] = smc_res.a1; res->result[1] = smc_res.a2; res->result[2] = smc_res.a3; } |
6b1751a86 firmware: qcom: s... |
137 |
|
1f7166fda firmware: qcom_sc... |
138 |
return (long)smc_res.a0 ? qcom_scm_remap_error(smc_res.a0) : 0; |
6b1751a86 firmware: qcom: s... |
139 |
} |