mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	firmware: qcom_scm: Dynamically support SMCCC and legacy conventions
Dynamically support SMCCCC and legacy conventions by detecting which convention to use at runtime. qcom_scm_call_atomic and qcom_scm_call can then be moved in qcom_scm.c and use underlying convention backend as appropriate. Thus, rename qcom_scm-64,-32 to reflect that they are backends for -smc and -legacy, respectively. Also add support for making SCM calls earlier than when SCM driver probes to support use cases such as qcom_scm_set_cold_boot_addr. Support is added by lazily initializing the convention and guarding the query with a spin lock. The limitation of these early SCM calls is that they cannot use DMA, as in the case of >4 arguments for SMC convention and any non-atomic call for legacy convention. Tested-by: Brian Masney <masneyb@onstation.org> # arm32 Tested-by: Stephan Gerhold <stephan@gerhold.net> Signed-off-by: Elliot Berman <eberman@codeaurora.org> Link: https://lore.kernel.org/r/1578431066-19600-18-git-send-email-eberman@codeaurora.org Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
This commit is contained in:
		
							parent
							
								
									57d3b81671
								
							
						
					
					
						commit
						9a434cee77
					
				
					 6 changed files with 176 additions and 135 deletions
				
			
		| 
						 | 
					@ -239,14 +239,6 @@ config QCOM_SCM
 | 
				
			||||||
	depends on ARM || ARM64
 | 
						depends on ARM || ARM64
 | 
				
			||||||
	select RESET_CONTROLLER
 | 
						select RESET_CONTROLLER
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config QCOM_SCM_32
 | 
					 | 
				
			||||||
	def_bool y
 | 
					 | 
				
			||||||
	depends on QCOM_SCM && ARM
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
config QCOM_SCM_64
 | 
					 | 
				
			||||||
	def_bool y
 | 
					 | 
				
			||||||
	depends on QCOM_SCM && ARM64
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
config QCOM_SCM_DOWNLOAD_MODE_DEFAULT
 | 
					config QCOM_SCM_DOWNLOAD_MODE_DEFAULT
 | 
				
			||||||
	bool "Qualcomm download mode enabled by default"
 | 
						bool "Qualcomm download mode enabled by default"
 | 
				
			||||||
	depends on QCOM_SCM
 | 
						depends on QCOM_SCM
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,9 +17,7 @@ obj-$(CONFIG_ISCSI_IBFT)	+= iscsi_ibft.o
 | 
				
			||||||
obj-$(CONFIG_FIRMWARE_MEMMAP)	+= memmap.o
 | 
					obj-$(CONFIG_FIRMWARE_MEMMAP)	+= memmap.o
 | 
				
			||||||
obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o
 | 
					obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o
 | 
				
			||||||
obj-$(CONFIG_FW_CFG_SYSFS)	+= qemu_fw_cfg.o
 | 
					obj-$(CONFIG_FW_CFG_SYSFS)	+= qemu_fw_cfg.o
 | 
				
			||||||
obj-$(CONFIG_QCOM_SCM)		+= qcom_scm.o
 | 
					obj-$(CONFIG_QCOM_SCM)		+= qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o
 | 
				
			||||||
obj-$(CONFIG_QCOM_SCM_64)	+= qcom_scm-64.o
 | 
					 | 
				
			||||||
obj-$(CONFIG_QCOM_SCM_32)	+= qcom_scm-32.o
 | 
					 | 
				
			||||||
obj-$(CONFIG_TI_SCI_PROTOCOL)	+= ti_sci.o
 | 
					obj-$(CONFIG_TI_SCI_PROTOCOL)	+= ti_sci.o
 | 
				
			||||||
obj-$(CONFIG_TRUSTED_FOUNDATIONS) += trusted_foundations.o
 | 
					obj-$(CONFIG_TRUSTED_FOUNDATIONS) += trusted_foundations.o
 | 
				
			||||||
obj-$(CONFIG_TURRIS_MOX_RWTM)	+= turris-mox-rwtm.o
 | 
					obj-$(CONFIG_TURRIS_MOX_RWTM)	+= turris-mox-rwtm.o
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,7 +26,6 @@ struct arm_smccc_args {
 | 
				
			||||||
	unsigned long args[8];
 | 
						unsigned long args[8];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define SCM_LEGACY_FNID(s, c)	(((s) << 10) | ((c) & 0x3ff))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * struct scm_legacy_command - one SCM command buffer
 | 
					 * struct scm_legacy_command - one SCM command buffer
 | 
				
			||||||
| 
						 | 
					@ -129,8 +128,8 @@ static void __scm_legacy_do(const struct arm_smccc_args *smc,
 | 
				
			||||||
 * and response buffers is taken care of by qcom_scm_call; however, callers are
 | 
					 * and response buffers is taken care of by qcom_scm_call; however, callers are
 | 
				
			||||||
 * responsible for any other cached buffers passed over to the secure world.
 | 
					 * responsible for any other cached buffers passed over to the secure world.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int qcom_scm_call(struct device *dev, const struct qcom_scm_desc *desc,
 | 
					int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc,
 | 
				
			||||||
			 struct qcom_scm_res *res)
 | 
							    struct qcom_scm_res *res)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	u8 arglen = desc->arginfo & 0xf;
 | 
						u8 arglen = desc->arginfo & 0xf;
 | 
				
			||||||
	int ret = 0, context_id;
 | 
						int ret = 0, context_id;
 | 
				
			||||||
| 
						 | 
					@ -218,9 +217,9 @@ int qcom_scm_call(struct device *dev, const struct qcom_scm_desc *desc,
 | 
				
			||||||
 * This shall only be used with commands that are guaranteed to be
 | 
					 * This shall only be used with commands that are guaranteed to be
 | 
				
			||||||
 * uninterruptable, atomic and SMP safe.
 | 
					 * uninterruptable, atomic and SMP safe.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int qcom_scm_call_atomic(struct device *unused,
 | 
					int scm_legacy_call_atomic(struct device *unused,
 | 
				
			||||||
			 const struct qcom_scm_desc *desc,
 | 
								   const struct qcom_scm_desc *desc,
 | 
				
			||||||
			 struct qcom_scm_res *res)
 | 
								   struct qcom_scm_res *res)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int context_id;
 | 
						int context_id;
 | 
				
			||||||
	struct arm_smccc_res smc_res;
 | 
						struct arm_smccc_res smc_res;
 | 
				
			||||||
| 
						 | 
					@ -241,23 +240,3 @@ int qcom_scm_call_atomic(struct device *unused,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return smc_res.a0;
 | 
						return smc_res.a0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, u32 cmd_id)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int ret;
 | 
					 | 
				
			||||||
	struct qcom_scm_desc desc = {
 | 
					 | 
				
			||||||
		.svc = QCOM_SCM_SVC_INFO,
 | 
					 | 
				
			||||||
		.cmd = QCOM_SCM_INFO_IS_CALL_AVAIL,
 | 
					 | 
				
			||||||
		.args[0] = SCM_LEGACY_FNID(svc_id, cmd_id),
 | 
					 | 
				
			||||||
		.arginfo = QCOM_SCM_ARGS(1),
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
	struct qcom_scm_res res;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ret = qcom_scm_call(dev, &desc, &res);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return ret ? : res.result[0];
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void __qcom_scm_init(void)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -14,8 +14,6 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "qcom_scm.h"
 | 
					#include "qcom_scm.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define SCM_SMC_FNID(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * struct arm_smccc_args
 | 
					 * struct arm_smccc_args
 | 
				
			||||||
 * @args:	The array of values used in registers in smc instruction
 | 
					 * @args:	The array of values used in registers in smc instruction
 | 
				
			||||||
| 
						 | 
					@ -24,7 +22,6 @@ struct arm_smccc_args {
 | 
				
			||||||
	unsigned long args[8];
 | 
						unsigned long args[8];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static u64 qcom_smccc_convention = -1;
 | 
					 | 
				
			||||||
static DEFINE_MUTEX(qcom_scm_lock);
 | 
					static DEFINE_MUTEX(qcom_scm_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define QCOM_SCM_EBUSY_WAIT_MS 30
 | 
					#define QCOM_SCM_EBUSY_WAIT_MS 30
 | 
				
			||||||
| 
						 | 
					@ -80,8 +77,8 @@ static void __scm_smc_do(const struct arm_smccc_args *smc,
 | 
				
			||||||
	}  while (res->a0 == QCOM_SCM_V2_EBUSY);
 | 
						}  while (res->a0 == QCOM_SCM_V2_EBUSY);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
 | 
					int scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
 | 
				
			||||||
			  struct qcom_scm_res *res, bool atomic)
 | 
							 struct qcom_scm_res *res, bool atomic)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int arglen = desc->arginfo & 0xf;
 | 
						int arglen = desc->arginfo & 0xf;
 | 
				
			||||||
	int i;
 | 
						int i;
 | 
				
			||||||
| 
						 | 
					@ -90,6 +87,9 @@ static int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
 | 
				
			||||||
	size_t alloc_len;
 | 
						size_t alloc_len;
 | 
				
			||||||
	gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL;
 | 
						gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL;
 | 
				
			||||||
	u32 smccc_call_type = atomic ? ARM_SMCCC_FAST_CALL : ARM_SMCCC_STD_CALL;
 | 
						u32 smccc_call_type = atomic ? ARM_SMCCC_FAST_CALL : ARM_SMCCC_STD_CALL;
 | 
				
			||||||
 | 
						u32 qcom_smccc_convention =
 | 
				
			||||||
 | 
								(qcom_scm_convention == SMC_CONVENTION_ARM_32) ?
 | 
				
			||||||
 | 
								ARM_SMCCC_SMC_32 : ARM_SMCCC_SMC_64;
 | 
				
			||||||
	struct arm_smccc_res smc_res;
 | 
						struct arm_smccc_res smc_res;
 | 
				
			||||||
	struct arm_smccc_args smc = {0};
 | 
						struct arm_smccc_args smc = {0};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -149,88 +149,3 @@ static int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return (long)smc_res.a0 ? qcom_scm_remap_error(smc_res.a0) : 0;
 | 
						return (long)smc_res.a0 ? qcom_scm_remap_error(smc_res.a0) : 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.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
int qcom_scm_call(struct device *dev, const struct qcom_scm_desc *desc,
 | 
					 | 
				
			||||||
		  struct qcom_scm_res *res)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	might_sleep();
 | 
					 | 
				
			||||||
	return __scm_smc_call(dev, 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.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
int qcom_scm_call_atomic(struct device *dev, const struct qcom_scm_desc *desc,
 | 
					 | 
				
			||||||
			 struct qcom_scm_res *res)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return __scm_smc_call(dev, desc, res, true);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, u32 cmd_id)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int ret;
 | 
					 | 
				
			||||||
	struct qcom_scm_desc desc = {
 | 
					 | 
				
			||||||
		.svc = QCOM_SCM_SVC_INFO,
 | 
					 | 
				
			||||||
		.cmd = QCOM_SCM_INFO_IS_CALL_AVAIL,
 | 
					 | 
				
			||||||
		.owner = ARM_SMCCC_OWNER_SIP,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
	struct qcom_scm_res res;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	desc.arginfo = QCOM_SCM_ARGS(1);
 | 
					 | 
				
			||||||
	desc.args[0] = SCM_SMC_FNID(svc_id, cmd_id) |
 | 
					 | 
				
			||||||
			(ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ret = qcom_scm_call(dev, &desc, &res);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return ret ? : res.result[0];
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void __qcom_scm_init(void)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct qcom_scm_desc desc = {
 | 
					 | 
				
			||||||
		.svc = QCOM_SCM_SVC_INFO,
 | 
					 | 
				
			||||||
		.cmd = QCOM_SCM_INFO_IS_CALL_AVAIL,
 | 
					 | 
				
			||||||
		.args[0] = SCM_SMC_FNID(QCOM_SCM_SVC_INFO,
 | 
					 | 
				
			||||||
					QCOM_SCM_INFO_IS_CALL_AVAIL) |
 | 
					 | 
				
			||||||
			   (ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT),
 | 
					 | 
				
			||||||
		.arginfo = QCOM_SCM_ARGS(1),
 | 
					 | 
				
			||||||
		.owner = ARM_SMCCC_OWNER_SIP,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
	struct qcom_scm_res res;
 | 
					 | 
				
			||||||
	int ret;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	qcom_smccc_convention = ARM_SMCCC_SMC_64;
 | 
					 | 
				
			||||||
	// Device isn't required as there is only one argument - no device
 | 
					 | 
				
			||||||
	// needed to dma_map_single to secure world
 | 
					 | 
				
			||||||
	ret = qcom_scm_call_atomic(NULL, &desc, &res);
 | 
					 | 
				
			||||||
	if (!ret && res.result[0] == 1)
 | 
					 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	qcom_smccc_convention = ARM_SMCCC_SMC_32;
 | 
					 | 
				
			||||||
	ret = qcom_scm_call_atomic(NULL, &desc, &res);
 | 
					 | 
				
			||||||
	if (!ret && res.result[0] == 1)
 | 
					 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	qcom_smccc_convention = -1;
 | 
					 | 
				
			||||||
	BUG();
 | 
					 | 
				
			||||||
out:
 | 
					 | 
				
			||||||
	pr_info("QCOM SCM SMC Convention: %lld\n", qcom_smccc_convention);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -72,6 +72,13 @@ static struct qcom_scm_wb_entry qcom_scm_wb[] = {
 | 
				
			||||||
	{ .flag = QCOM_SCM_FLAG_WARMBOOT_CPU3 },
 | 
						{ .flag = QCOM_SCM_FLAG_WARMBOOT_CPU3 },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *qcom_scm_convention_names[] = {
 | 
				
			||||||
 | 
						[SMC_CONVENTION_UNKNOWN] = "unknown",
 | 
				
			||||||
 | 
						[SMC_CONVENTION_ARM_32] = "smc arm 32",
 | 
				
			||||||
 | 
						[SMC_CONVENTION_ARM_64] = "smc arm 64",
 | 
				
			||||||
 | 
						[SMC_CONVENTION_LEGACY] = "smc legacy",
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct qcom_scm *__scm;
 | 
					static struct qcom_scm *__scm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int qcom_scm_clk_enable(void)
 | 
					static int qcom_scm_clk_enable(void)
 | 
				
			||||||
| 
						 | 
					@ -107,6 +114,143 @@ static void qcom_scm_clk_disable(void)
 | 
				
			||||||
	clk_disable_unprepare(__scm->bus_clk);
 | 
						clk_disable_unprepare(__scm->bus_clk);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int __qcom_scm_is_call_available(struct device *dev, u32 svc_id,
 | 
				
			||||||
 | 
										u32 cmd_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum qcom_scm_convention qcom_scm_convention;
 | 
				
			||||||
 | 
					static bool has_queried __read_mostly;
 | 
				
			||||||
 | 
					static DEFINE_SPINLOCK(query_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void __query_convention(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned long flags;
 | 
				
			||||||
 | 
						struct qcom_scm_desc desc = {
 | 
				
			||||||
 | 
							.svc = QCOM_SCM_SVC_INFO,
 | 
				
			||||||
 | 
							.cmd = QCOM_SCM_INFO_IS_CALL_AVAIL,
 | 
				
			||||||
 | 
							.args[0] = SCM_SMC_FNID(QCOM_SCM_SVC_INFO,
 | 
				
			||||||
 | 
										   QCOM_SCM_INFO_IS_CALL_AVAIL) |
 | 
				
			||||||
 | 
								   (ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT),
 | 
				
			||||||
 | 
							.arginfo = QCOM_SCM_ARGS(1),
 | 
				
			||||||
 | 
							.owner = ARM_SMCCC_OWNER_SIP,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						struct qcom_scm_res res;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spin_lock_irqsave(&query_lock, flags);
 | 
				
			||||||
 | 
						if (has_queried)
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qcom_scm_convention = SMC_CONVENTION_ARM_64;
 | 
				
			||||||
 | 
						// Device isn't required as there is only one argument - no device
 | 
				
			||||||
 | 
						// needed to dma_map_single to secure world
 | 
				
			||||||
 | 
						ret = scm_smc_call(NULL, &desc, &res, true);
 | 
				
			||||||
 | 
						if (!ret && res.result[0] == 1)
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qcom_scm_convention = SMC_CONVENTION_ARM_32;
 | 
				
			||||||
 | 
						ret = scm_smc_call(NULL, &desc, &res, true);
 | 
				
			||||||
 | 
						if (!ret && res.result[0] == 1)
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qcom_scm_convention = SMC_CONVENTION_LEGACY;
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
 | 
						has_queried = true;
 | 
				
			||||||
 | 
						spin_unlock_irqrestore(&query_lock, flags);
 | 
				
			||||||
 | 
						pr_info("qcom_scm: convention: %s\n",
 | 
				
			||||||
 | 
							qcom_scm_convention_names[qcom_scm_convention]);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline enum qcom_scm_convention __get_convention(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (unlikely(!has_queried))
 | 
				
			||||||
 | 
							__query_convention();
 | 
				
			||||||
 | 
						return qcom_scm_convention;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 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, const struct qcom_scm_desc *desc,
 | 
				
			||||||
 | 
								 struct qcom_scm_res *res)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						might_sleep();
 | 
				
			||||||
 | 
						switch (__get_convention()) {
 | 
				
			||||||
 | 
						case SMC_CONVENTION_ARM_32:
 | 
				
			||||||
 | 
						case SMC_CONVENTION_ARM_64:
 | 
				
			||||||
 | 
							return scm_smc_call(dev, desc, res, false);
 | 
				
			||||||
 | 
						case SMC_CONVENTION_LEGACY:
 | 
				
			||||||
 | 
							return scm_legacy_call(dev, desc, res);
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							pr_err("Unknown current SCM calling convention.\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 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,
 | 
				
			||||||
 | 
									const struct qcom_scm_desc *desc,
 | 
				
			||||||
 | 
									struct qcom_scm_res *res)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (__get_convention()) {
 | 
				
			||||||
 | 
						case SMC_CONVENTION_ARM_32:
 | 
				
			||||||
 | 
						case SMC_CONVENTION_ARM_64:
 | 
				
			||||||
 | 
							return scm_smc_call(dev, desc, res, true);
 | 
				
			||||||
 | 
						case SMC_CONVENTION_LEGACY:
 | 
				
			||||||
 | 
							return scm_legacy_call_atomic(dev, desc, res);
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							pr_err("Unknown current SCM calling convention.\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int __qcom_scm_is_call_available(struct device *dev, u32 svc_id,
 | 
				
			||||||
 | 
										u32 cmd_id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
						struct qcom_scm_desc desc = {
 | 
				
			||||||
 | 
							.svc = QCOM_SCM_SVC_INFO,
 | 
				
			||||||
 | 
							.cmd = QCOM_SCM_INFO_IS_CALL_AVAIL,
 | 
				
			||||||
 | 
							.owner = ARM_SMCCC_OWNER_SIP,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						struct qcom_scm_res res;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						desc.arginfo = QCOM_SCM_ARGS(1);
 | 
				
			||||||
 | 
						switch (__get_convention()) {
 | 
				
			||||||
 | 
						case SMC_CONVENTION_ARM_32:
 | 
				
			||||||
 | 
						case SMC_CONVENTION_ARM_64:
 | 
				
			||||||
 | 
							desc.args[0] = SCM_SMC_FNID(svc_id, cmd_id) |
 | 
				
			||||||
 | 
									(ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case SMC_CONVENTION_LEGACY:
 | 
				
			||||||
 | 
							desc.args[0] = SCM_LEGACY_FNID(svc_id, cmd_id);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							pr_err("Unknown SMC convention being used\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = qcom_scm_call(dev, &desc, &res);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret ? : res.result[0];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus
 | 
					 * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus
 | 
				
			||||||
 * @entry: Entry point function for the cpus
 | 
					 * @entry: Entry point function for the cpus
 | 
				
			||||||
| 
						 | 
					@ -971,7 +1115,7 @@ static int qcom_scm_probe(struct platform_device *pdev)
 | 
				
			||||||
	__scm = scm;
 | 
						__scm = scm;
 | 
				
			||||||
	__scm->dev = &pdev->dev;
 | 
						__scm->dev = &pdev->dev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	__qcom_scm_init();
 | 
						__query_convention();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * If requested enable "download mode", from this point on warmboot
 | 
						 * If requested enable "download mode", from this point on warmboot
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,16 @@
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
#ifndef __QCOM_SCM_INT_H
 | 
					#ifndef __QCOM_SCM_INT_H
 | 
				
			||||||
#define __QCOM_SCM_INT_H
 | 
					#define __QCOM_SCM_INT_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum qcom_scm_convention {
 | 
				
			||||||
 | 
						SMC_CONVENTION_UNKNOWN,
 | 
				
			||||||
 | 
						SMC_CONVENTION_LEGACY,
 | 
				
			||||||
 | 
						SMC_CONVENTION_ARM_32,
 | 
				
			||||||
 | 
						SMC_CONVENTION_ARM_64,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern enum qcom_scm_convention qcom_scm_convention;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define MAX_QCOM_SCM_ARGS 10
 | 
					#define MAX_QCOM_SCM_ARGS 10
 | 
				
			||||||
#define MAX_QCOM_SCM_RETS 3
 | 
					#define MAX_QCOM_SCM_RETS 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -50,11 +60,16 @@ struct qcom_scm_res {
 | 
				
			||||||
	u64 result[MAX_QCOM_SCM_RETS];
 | 
						u64 result[MAX_QCOM_SCM_RETS];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern int qcom_scm_call(struct device *dev, const struct qcom_scm_desc *desc,
 | 
					#define SCM_SMC_FNID(s, c)	((((s) & 0xFF) << 8) | ((c) & 0xFF))
 | 
				
			||||||
			 struct qcom_scm_res *res);
 | 
					extern int scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
 | 
				
			||||||
extern int qcom_scm_call_atomic(struct device *dev,
 | 
								struct qcom_scm_res *res, bool atomic);
 | 
				
			||||||
				const struct qcom_scm_desc *desc,
 | 
					
 | 
				
			||||||
				struct qcom_scm_res *res);
 | 
					#define SCM_LEGACY_FNID(s, c)	(((s) << 10) | ((c) & 0x3ff))
 | 
				
			||||||
 | 
					extern int scm_legacy_call_atomic(struct device *dev,
 | 
				
			||||||
 | 
									  const struct qcom_scm_desc *desc,
 | 
				
			||||||
 | 
									  struct qcom_scm_res *res);
 | 
				
			||||||
 | 
					extern int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc,
 | 
				
			||||||
 | 
								   struct qcom_scm_res *res);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define QCOM_SCM_SVC_BOOT		0x01
 | 
					#define QCOM_SCM_SVC_BOOT		0x01
 | 
				
			||||||
#define QCOM_SCM_BOOT_SET_ADDR		0x01
 | 
					#define QCOM_SCM_BOOT_SET_ADDR		0x01
 | 
				
			||||||
| 
						 | 
					@ -77,8 +92,6 @@ extern int qcom_scm_call_atomic(struct device *dev,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define QCOM_SCM_SVC_INFO		0x06
 | 
					#define QCOM_SCM_SVC_INFO		0x06
 | 
				
			||||||
#define QCOM_SCM_INFO_IS_CALL_AVAIL	0x01
 | 
					#define QCOM_SCM_INFO_IS_CALL_AVAIL	0x01
 | 
				
			||||||
extern int __qcom_scm_is_call_available(struct device *dev, u32 svc_id,
 | 
					 | 
				
			||||||
		u32 cmd_id);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define QCOM_SCM_SVC_MP				0x0c
 | 
					#define QCOM_SCM_SVC_MP				0x0c
 | 
				
			||||||
#define QCOM_SCM_MP_RESTORE_SEC_CFG		0x02
 | 
					#define QCOM_SCM_MP_RESTORE_SEC_CFG		0x02
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue