xref: /openbmc/linux/drivers/firmware/qcom_scm.c (revision d37cf9b63113f13d742713881ce691fc615d8b3b)
197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
25443cc5fSElliot Berman /* Copyright (c) 2010,2015,2019 The Linux Foundation. All rights reserved.
32ce76a6aSLina Iyer  * Copyright (C) 2015 Linaro Ltd.
4916f743dSKumar Gala  */
5d0f6fa7bSAndy Gross #include <linux/platform_device.h>
6dea85242SPaul Gortmaker #include <linux/init.h>
76bf32599SGuru Das Srinagesh #include <linux/interrupt.h>
86bf32599SGuru Das Srinagesh #include <linux/completion.h>
9b6a1dfbcSKumar Gala #include <linux/cpumask.h>
10b6a1dfbcSKumar Gala #include <linux/export.h>
11f01e90feSBjorn Andersson #include <linux/dma-mapping.h>
1265b7ebdaSSibi Sankar #include <linux/interconnect.h>
138c1b7dc9SBjorn Andersson #include <linux/module.h>
14b6a1dfbcSKumar Gala #include <linux/types.h>
153bf90ecaSElliot Berman #include <linux/firmware/qcom/qcom_scm.h>
16d0f6fa7bSAndy Gross #include <linux/of.h>
178c1b7dc9SBjorn Andersson #include <linux/of_address.h>
186bf32599SGuru Das Srinagesh #include <linux/of_irq.h>
19d0f6fa7bSAndy Gross #include <linux/of_platform.h>
20d0f6fa7bSAndy Gross #include <linux/clk.h>
21dd4fe5b2SBjorn Andersson #include <linux/reset-controller.h>
2257d3b816SElliot Berman #include <linux/arm-smccc.h>
23916f743dSKumar Gala 
24b6a1dfbcSKumar Gala #include "qcom_scm.h"
25a353e4a0SLina Iyer 
268c1b7dc9SBjorn Andersson static bool download_mode = IS_ENABLED(CONFIG_QCOM_SCM_DOWNLOAD_MODE_DEFAULT);
278c1b7dc9SBjorn Andersson module_param(download_mode, bool, 0);
288c1b7dc9SBjorn Andersson 
29d0f6fa7bSAndy Gross struct qcom_scm {
30d0f6fa7bSAndy Gross 	struct device *dev;
31d0f6fa7bSAndy Gross 	struct clk *core_clk;
32d0f6fa7bSAndy Gross 	struct clk *iface_clk;
33d0f6fa7bSAndy Gross 	struct clk *bus_clk;
3465b7ebdaSSibi Sankar 	struct icc_path *path;
356bf32599SGuru Das Srinagesh 	struct completion waitq_comp;
36dd4fe5b2SBjorn Andersson 	struct reset_controller_dev reset;
378c1b7dc9SBjorn Andersson 
3865b7ebdaSSibi Sankar 	/* control access to the interconnect path */
3965b7ebdaSSibi Sankar 	struct mutex scm_bw_lock;
4065b7ebdaSSibi Sankar 	int scm_vote_count;
4165b7ebdaSSibi Sankar 
428c1b7dc9SBjorn Andersson 	u64 dload_mode_addr;
43d0f6fa7bSAndy Gross };
44d0f6fa7bSAndy Gross 
45d82bd359SAvaneesh Kumar Dwivedi struct qcom_scm_current_perm_info {
46d82bd359SAvaneesh Kumar Dwivedi 	__le32 vmid;
47d82bd359SAvaneesh Kumar Dwivedi 	__le32 perm;
48d82bd359SAvaneesh Kumar Dwivedi 	__le64 ctx;
49d82bd359SAvaneesh Kumar Dwivedi 	__le32 ctx_size;
50d82bd359SAvaneesh Kumar Dwivedi 	__le32 unused;
51d82bd359SAvaneesh Kumar Dwivedi };
52d82bd359SAvaneesh Kumar Dwivedi 
53d82bd359SAvaneesh Kumar Dwivedi struct qcom_scm_mem_map_info {
54d82bd359SAvaneesh Kumar Dwivedi 	__le64 mem_addr;
55d82bd359SAvaneesh Kumar Dwivedi 	__le64 mem_size;
56d82bd359SAvaneesh Kumar Dwivedi };
57d82bd359SAvaneesh Kumar Dwivedi 
587734c4b5SStephan Gerhold /* Each bit configures cold/warm boot address for one of the 4 CPUs */
597734c4b5SStephan Gerhold static const u8 qcom_scm_cpu_cold_bits[QCOM_SCM_BOOT_MAX_CPUS] = {
607734c4b5SStephan Gerhold 	0, BIT(0), BIT(3), BIT(5)
6157d3b816SElliot Berman };
627734c4b5SStephan Gerhold static const u8 qcom_scm_cpu_warm_bits[QCOM_SCM_BOOT_MAX_CPUS] = {
637734c4b5SStephan Gerhold 	BIT(2), BIT(1), BIT(4), BIT(6)
6457d3b816SElliot Berman };
6557d3b816SElliot Berman 
666bf32599SGuru Das Srinagesh #define QCOM_SMC_WAITQ_FLAG_WAKE_ONE	BIT(0)
676bf32599SGuru Das Srinagesh #define QCOM_SMC_WAITQ_FLAG_WAKE_ALL	BIT(1)
686bf32599SGuru Das Srinagesh 
696bc45428SStephen Boyd static const char * const qcom_scm_convention_names[] = {
709a434ceeSElliot Berman 	[SMC_CONVENTION_UNKNOWN] = "unknown",
719a434ceeSElliot Berman 	[SMC_CONVENTION_ARM_32] = "smc arm 32",
729a434ceeSElliot Berman 	[SMC_CONVENTION_ARM_64] = "smc arm 64",
739a434ceeSElliot Berman 	[SMC_CONVENTION_LEGACY] = "smc legacy",
749a434ceeSElliot Berman };
759a434ceeSElliot Berman 
76d0f6fa7bSAndy Gross static struct qcom_scm *__scm;
77d0f6fa7bSAndy Gross 
qcom_scm_clk_enable(void)78d0f6fa7bSAndy Gross static int qcom_scm_clk_enable(void)
79d0f6fa7bSAndy Gross {
80d0f6fa7bSAndy Gross 	int ret;
81d0f6fa7bSAndy Gross 
82d0f6fa7bSAndy Gross 	ret = clk_prepare_enable(__scm->core_clk);
83d0f6fa7bSAndy Gross 	if (ret)
84d0f6fa7bSAndy Gross 		goto bail;
85d0f6fa7bSAndy Gross 
86d0f6fa7bSAndy Gross 	ret = clk_prepare_enable(__scm->iface_clk);
87d0f6fa7bSAndy Gross 	if (ret)
88d0f6fa7bSAndy Gross 		goto disable_core;
89d0f6fa7bSAndy Gross 
90d0f6fa7bSAndy Gross 	ret = clk_prepare_enable(__scm->bus_clk);
91d0f6fa7bSAndy Gross 	if (ret)
92d0f6fa7bSAndy Gross 		goto disable_iface;
93d0f6fa7bSAndy Gross 
94d0f6fa7bSAndy Gross 	return 0;
95d0f6fa7bSAndy Gross 
96d0f6fa7bSAndy Gross disable_iface:
97d0f6fa7bSAndy Gross 	clk_disable_unprepare(__scm->iface_clk);
98d0f6fa7bSAndy Gross disable_core:
99d0f6fa7bSAndy Gross 	clk_disable_unprepare(__scm->core_clk);
100d0f6fa7bSAndy Gross bail:
101d0f6fa7bSAndy Gross 	return ret;
102d0f6fa7bSAndy Gross }
103d0f6fa7bSAndy Gross 
qcom_scm_clk_disable(void)104d0f6fa7bSAndy Gross static void qcom_scm_clk_disable(void)
105d0f6fa7bSAndy Gross {
106d0f6fa7bSAndy Gross 	clk_disable_unprepare(__scm->core_clk);
107d0f6fa7bSAndy Gross 	clk_disable_unprepare(__scm->iface_clk);
108d0f6fa7bSAndy Gross 	clk_disable_unprepare(__scm->bus_clk);
109d0f6fa7bSAndy Gross }
110d0f6fa7bSAndy Gross 
qcom_scm_bw_enable(void)11165b7ebdaSSibi Sankar static int qcom_scm_bw_enable(void)
11265b7ebdaSSibi Sankar {
11365b7ebdaSSibi Sankar 	int ret = 0;
11465b7ebdaSSibi Sankar 
11565b7ebdaSSibi Sankar 	if (!__scm->path)
11665b7ebdaSSibi Sankar 		return 0;
11765b7ebdaSSibi Sankar 
11865b7ebdaSSibi Sankar 	if (IS_ERR(__scm->path))
11965b7ebdaSSibi Sankar 		return -EINVAL;
12065b7ebdaSSibi Sankar 
12165b7ebdaSSibi Sankar 	mutex_lock(&__scm->scm_bw_lock);
12265b7ebdaSSibi Sankar 	if (!__scm->scm_vote_count) {
12365b7ebdaSSibi Sankar 		ret = icc_set_bw(__scm->path, 0, UINT_MAX);
12465b7ebdaSSibi Sankar 		if (ret < 0) {
12565b7ebdaSSibi Sankar 			dev_err(__scm->dev, "failed to set bandwidth request\n");
12665b7ebdaSSibi Sankar 			goto err_bw;
12765b7ebdaSSibi Sankar 		}
12865b7ebdaSSibi Sankar 	}
12965b7ebdaSSibi Sankar 	__scm->scm_vote_count++;
13065b7ebdaSSibi Sankar err_bw:
13165b7ebdaSSibi Sankar 	mutex_unlock(&__scm->scm_bw_lock);
13265b7ebdaSSibi Sankar 
13365b7ebdaSSibi Sankar 	return ret;
13465b7ebdaSSibi Sankar }
13565b7ebdaSSibi Sankar 
qcom_scm_bw_disable(void)13665b7ebdaSSibi Sankar static void qcom_scm_bw_disable(void)
13765b7ebdaSSibi Sankar {
13865b7ebdaSSibi Sankar 	if (IS_ERR_OR_NULL(__scm->path))
13965b7ebdaSSibi Sankar 		return;
14065b7ebdaSSibi Sankar 
14165b7ebdaSSibi Sankar 	mutex_lock(&__scm->scm_bw_lock);
14265b7ebdaSSibi Sankar 	if (__scm->scm_vote_count-- == 1)
14365b7ebdaSSibi Sankar 		icc_set_bw(__scm->path, 0, 0);
14465b7ebdaSSibi Sankar 	mutex_unlock(&__scm->scm_bw_lock);
14565b7ebdaSSibi Sankar }
14665b7ebdaSSibi Sankar 
147f6ea568fSStephen Boyd enum qcom_scm_convention qcom_scm_convention = SMC_CONVENTION_UNKNOWN;
148f6ea568fSStephen Boyd static DEFINE_SPINLOCK(scm_query_lock);
1499a434ceeSElliot Berman 
__get_convention(void)150f6ea568fSStephen Boyd static enum qcom_scm_convention __get_convention(void)
1519a434ceeSElliot Berman {
1529a434ceeSElliot Berman 	unsigned long flags;
1539a434ceeSElliot Berman 	struct qcom_scm_desc desc = {
1549a434ceeSElliot Berman 		.svc = QCOM_SCM_SVC_INFO,
1559a434ceeSElliot Berman 		.cmd = QCOM_SCM_INFO_IS_CALL_AVAIL,
1569a434ceeSElliot Berman 		.args[0] = SCM_SMC_FNID(QCOM_SCM_SVC_INFO,
1579a434ceeSElliot Berman 					   QCOM_SCM_INFO_IS_CALL_AVAIL) |
1589a434ceeSElliot Berman 			   (ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT),
1599a434ceeSElliot Berman 		.arginfo = QCOM_SCM_ARGS(1),
1609a434ceeSElliot Berman 		.owner = ARM_SMCCC_OWNER_SIP,
1619a434ceeSElliot Berman 	};
1629a434ceeSElliot Berman 	struct qcom_scm_res res;
163f6ea568fSStephen Boyd 	enum qcom_scm_convention probed_convention;
1649a434ceeSElliot Berman 	int ret;
165257f2935SStephen Boyd 	bool forced = false;
1669a434ceeSElliot Berman 
167f6ea568fSStephen Boyd 	if (likely(qcom_scm_convention != SMC_CONVENTION_UNKNOWN))
168f6ea568fSStephen Boyd 		return qcom_scm_convention;
1699a434ceeSElliot Berman 
170f6ea568fSStephen Boyd 	/*
171df56691aSKathiravan Thirumoorthy 	 * Per the "SMC calling convention specification", the 64-bit calling
172df56691aSKathiravan Thirumoorthy 	 * convention can only be used when the client is 64-bit, otherwise
173df56691aSKathiravan Thirumoorthy 	 * system will encounter the undefined behaviour.
174df56691aSKathiravan Thirumoorthy 	 */
175df56691aSKathiravan Thirumoorthy #if IS_ENABLED(CONFIG_ARM64)
176df56691aSKathiravan Thirumoorthy 	/*
177f6ea568fSStephen Boyd 	 * Device isn't required as there is only one argument - no device
178f6ea568fSStephen Boyd 	 * needed to dma_map_single to secure world
179f6ea568fSStephen Boyd 	 */
180f6ea568fSStephen Boyd 	probed_convention = SMC_CONVENTION_ARM_64;
181f6ea568fSStephen Boyd 	ret = __scm_smc_call(NULL, &desc, probed_convention, &res, true);
1829a434ceeSElliot Berman 	if (!ret && res.result[0] == 1)
183f6ea568fSStephen Boyd 		goto found;
1849a434ceeSElliot Berman 
185257f2935SStephen Boyd 	/*
186257f2935SStephen Boyd 	 * Some SC7180 firmwares didn't implement the
187257f2935SStephen Boyd 	 * QCOM_SCM_INFO_IS_CALL_AVAIL call, so we fallback to forcing ARM_64
188257f2935SStephen Boyd 	 * calling conventions on these firmwares. Luckily we don't make any
189257f2935SStephen Boyd 	 * early calls into the firmware on these SoCs so the device pointer
190257f2935SStephen Boyd 	 * will be valid here to check if the compatible matches.
191257f2935SStephen Boyd 	 */
192257f2935SStephen Boyd 	if (of_device_is_compatible(__scm ? __scm->dev->of_node : NULL, "qcom,scm-sc7180")) {
193257f2935SStephen Boyd 		forced = true;
194257f2935SStephen Boyd 		goto found;
195257f2935SStephen Boyd 	}
196df56691aSKathiravan Thirumoorthy #endif
197257f2935SStephen Boyd 
198f6ea568fSStephen Boyd 	probed_convention = SMC_CONVENTION_ARM_32;
199f6ea568fSStephen Boyd 	ret = __scm_smc_call(NULL, &desc, probed_convention, &res, true);
2009a434ceeSElliot Berman 	if (!ret && res.result[0] == 1)
201f6ea568fSStephen Boyd 		goto found;
2029a434ceeSElliot Berman 
203f6ea568fSStephen Boyd 	probed_convention = SMC_CONVENTION_LEGACY;
204f6ea568fSStephen Boyd found:
205f6ea568fSStephen Boyd 	spin_lock_irqsave(&scm_query_lock, flags);
206f6ea568fSStephen Boyd 	if (probed_convention != qcom_scm_convention) {
207f6ea568fSStephen Boyd 		qcom_scm_convention = probed_convention;
208257f2935SStephen Boyd 		pr_info("qcom_scm: convention: %s%s\n",
209257f2935SStephen Boyd 			qcom_scm_convention_names[qcom_scm_convention],
210257f2935SStephen Boyd 			forced ? " (forced)" : "");
2119a434ceeSElliot Berman 	}
212f6ea568fSStephen Boyd 	spin_unlock_irqrestore(&scm_query_lock, flags);
2139a434ceeSElliot Berman 
2149a434ceeSElliot Berman 	return qcom_scm_convention;
2159a434ceeSElliot Berman }
2169a434ceeSElliot Berman 
2179a434ceeSElliot Berman /**
2189a434ceeSElliot Berman  * qcom_scm_call() - Invoke a syscall in the secure world
2199a434ceeSElliot Berman  * @dev:	device
2209a434ceeSElliot Berman  * @desc:	Descriptor structure containing arguments and return values
221a5d32f6dSYang Li  * @res:        Structure containing results from SMC/HVC call
2229a434ceeSElliot Berman  *
2239a434ceeSElliot Berman  * Sends a command to the SCM and waits for the command to finish processing.
2249a434ceeSElliot Berman  * This should *only* be called in pre-emptible context.
2259a434ceeSElliot Berman  */
qcom_scm_call(struct device * dev,const struct qcom_scm_desc * desc,struct qcom_scm_res * res)2269a434ceeSElliot Berman static int qcom_scm_call(struct device *dev, const struct qcom_scm_desc *desc,
2279a434ceeSElliot Berman 			 struct qcom_scm_res *res)
2289a434ceeSElliot Berman {
2299a434ceeSElliot Berman 	might_sleep();
2309a434ceeSElliot Berman 	switch (__get_convention()) {
2319a434ceeSElliot Berman 	case SMC_CONVENTION_ARM_32:
2329a434ceeSElliot Berman 	case SMC_CONVENTION_ARM_64:
2339a434ceeSElliot Berman 		return scm_smc_call(dev, desc, res, false);
2349a434ceeSElliot Berman 	case SMC_CONVENTION_LEGACY:
2359a434ceeSElliot Berman 		return scm_legacy_call(dev, desc, res);
2369a434ceeSElliot Berman 	default:
2379a434ceeSElliot Berman 		pr_err("Unknown current SCM calling convention.\n");
2389a434ceeSElliot Berman 		return -EINVAL;
2399a434ceeSElliot Berman 	}
2409a434ceeSElliot Berman }
2419a434ceeSElliot Berman 
2429a434ceeSElliot Berman /**
2439a434ceeSElliot Berman  * qcom_scm_call_atomic() - atomic variation of qcom_scm_call()
2449a434ceeSElliot Berman  * @dev:	device
2459a434ceeSElliot Berman  * @desc:	Descriptor structure containing arguments and return values
2469a434ceeSElliot Berman  * @res:	Structure containing results from SMC/HVC call
2479a434ceeSElliot Berman  *
2489a434ceeSElliot Berman  * Sends a command to the SCM and waits for the command to finish processing.
2499a434ceeSElliot Berman  * This can be called in atomic context.
2509a434ceeSElliot Berman  */
qcom_scm_call_atomic(struct device * dev,const struct qcom_scm_desc * desc,struct qcom_scm_res * res)2519a434ceeSElliot Berman static int qcom_scm_call_atomic(struct device *dev,
2529a434ceeSElliot Berman 				const struct qcom_scm_desc *desc,
2539a434ceeSElliot Berman 				struct qcom_scm_res *res)
2549a434ceeSElliot Berman {
2559a434ceeSElliot Berman 	switch (__get_convention()) {
2569a434ceeSElliot Berman 	case SMC_CONVENTION_ARM_32:
2579a434ceeSElliot Berman 	case SMC_CONVENTION_ARM_64:
2589a434ceeSElliot Berman 		return scm_smc_call(dev, desc, res, true);
2599a434ceeSElliot Berman 	case SMC_CONVENTION_LEGACY:
2609a434ceeSElliot Berman 		return scm_legacy_call_atomic(dev, desc, res);
2619a434ceeSElliot Berman 	default:
2629a434ceeSElliot Berman 		pr_err("Unknown current SCM calling convention.\n");
2639a434ceeSElliot Berman 		return -EINVAL;
2649a434ceeSElliot Berman 	}
2659a434ceeSElliot Berman }
2669a434ceeSElliot Berman 
__qcom_scm_is_call_available(struct device * dev,u32 svc_id,u32 cmd_id)2679d11af8bSStephen Boyd static bool __qcom_scm_is_call_available(struct device *dev, u32 svc_id,
2689a434ceeSElliot Berman 					 u32 cmd_id)
2699a434ceeSElliot Berman {
2709a434ceeSElliot Berman 	int ret;
2719a434ceeSElliot Berman 	struct qcom_scm_desc desc = {
2729a434ceeSElliot Berman 		.svc = QCOM_SCM_SVC_INFO,
2739a434ceeSElliot Berman 		.cmd = QCOM_SCM_INFO_IS_CALL_AVAIL,
2749a434ceeSElliot Berman 		.owner = ARM_SMCCC_OWNER_SIP,
2759a434ceeSElliot Berman 	};
2769a434ceeSElliot Berman 	struct qcom_scm_res res;
2779a434ceeSElliot Berman 
2789a434ceeSElliot Berman 	desc.arginfo = QCOM_SCM_ARGS(1);
2799a434ceeSElliot Berman 	switch (__get_convention()) {
2809a434ceeSElliot Berman 	case SMC_CONVENTION_ARM_32:
2819a434ceeSElliot Berman 	case SMC_CONVENTION_ARM_64:
2829a434ceeSElliot Berman 		desc.args[0] = SCM_SMC_FNID(svc_id, cmd_id) |
2839a434ceeSElliot Berman 				(ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT);
2849a434ceeSElliot Berman 		break;
2859a434ceeSElliot Berman 	case SMC_CONVENTION_LEGACY:
2869a434ceeSElliot Berman 		desc.args[0] = SCM_LEGACY_FNID(svc_id, cmd_id);
2879a434ceeSElliot Berman 		break;
2889a434ceeSElliot Berman 	default:
2899a434ceeSElliot Berman 		pr_err("Unknown SMC convention being used\n");
29038212b2aSGuru Das Srinagesh 		return false;
2919a434ceeSElliot Berman 	}
2929a434ceeSElliot Berman 
2939a434ceeSElliot Berman 	ret = qcom_scm_call(dev, &desc, &res);
2949a434ceeSElliot Berman 
2959d11af8bSStephen Boyd 	return ret ? false : !!res.result[0];
2969a434ceeSElliot Berman }
2979a434ceeSElliot Berman 
qcom_scm_set_boot_addr(void * entry,const u8 * cpu_bits)29852beb1fcSStephan Gerhold static int qcom_scm_set_boot_addr(void *entry, const u8 *cpu_bits)
2997734c4b5SStephan Gerhold {
3007734c4b5SStephan Gerhold 	int cpu;
3017734c4b5SStephan Gerhold 	unsigned int flags = 0;
3027734c4b5SStephan Gerhold 	struct qcom_scm_desc desc = {
3037734c4b5SStephan Gerhold 		.svc = QCOM_SCM_SVC_BOOT,
3047734c4b5SStephan Gerhold 		.cmd = QCOM_SCM_BOOT_SET_ADDR,
3057734c4b5SStephan Gerhold 		.arginfo = QCOM_SCM_ARGS(2),
3067734c4b5SStephan Gerhold 		.owner = ARM_SMCCC_OWNER_SIP,
3077734c4b5SStephan Gerhold 	};
3087734c4b5SStephan Gerhold 
30952beb1fcSStephan Gerhold 	for_each_present_cpu(cpu) {
3107734c4b5SStephan Gerhold 		if (cpu >= QCOM_SCM_BOOT_MAX_CPUS)
3117734c4b5SStephan Gerhold 			return -EINVAL;
3127734c4b5SStephan Gerhold 		flags |= cpu_bits[cpu];
3137734c4b5SStephan Gerhold 	}
3147734c4b5SStephan Gerhold 
3157734c4b5SStephan Gerhold 	desc.args[0] = flags;
3167734c4b5SStephan Gerhold 	desc.args[1] = virt_to_phys(entry);
3177734c4b5SStephan Gerhold 
3187734c4b5SStephan Gerhold 	return qcom_scm_call_atomic(__scm ? __scm->dev : NULL, &desc, NULL);
3197734c4b5SStephan Gerhold }
3207734c4b5SStephan Gerhold 
qcom_scm_set_boot_addr_mc(void * entry,unsigned int flags)321f60a317bSStephan Gerhold static int qcom_scm_set_boot_addr_mc(void *entry, unsigned int flags)
322f60a317bSStephan Gerhold {
323f60a317bSStephan Gerhold 	struct qcom_scm_desc desc = {
324f60a317bSStephan Gerhold 		.svc = QCOM_SCM_SVC_BOOT,
325f60a317bSStephan Gerhold 		.cmd = QCOM_SCM_BOOT_SET_ADDR_MC,
326f60a317bSStephan Gerhold 		.owner = ARM_SMCCC_OWNER_SIP,
327f60a317bSStephan Gerhold 		.arginfo = QCOM_SCM_ARGS(6),
328f60a317bSStephan Gerhold 		.args = {
329f60a317bSStephan Gerhold 			virt_to_phys(entry),
330f60a317bSStephan Gerhold 			/* Apply to all CPUs in all affinity levels */
331f60a317bSStephan Gerhold 			~0ULL, ~0ULL, ~0ULL, ~0ULL,
332f60a317bSStephan Gerhold 			flags,
333f60a317bSStephan Gerhold 		},
334f60a317bSStephan Gerhold 	};
335f60a317bSStephan Gerhold 
336f60a317bSStephan Gerhold 	/* Need a device for DMA of the additional arguments */
337f60a317bSStephan Gerhold 	if (!__scm || __get_convention() == SMC_CONVENTION_LEGACY)
338f60a317bSStephan Gerhold 		return -EOPNOTSUPP;
339f60a317bSStephan Gerhold 
340f60a317bSStephan Gerhold 	return qcom_scm_call(__scm->dev, &desc, NULL);
341f60a317bSStephan Gerhold }
342f60a317bSStephan Gerhold 
3437db2bc92SArnd Bergmann /**
34452beb1fcSStephan Gerhold  * qcom_scm_set_warm_boot_addr() - Set the warm boot address for all cpus
3457db2bc92SArnd Bergmann  * @entry: Entry point function for the cpus
3467db2bc92SArnd Bergmann  *
3477db2bc92SArnd Bergmann  * Set the Linux entry point for the SCM to transfer control to when coming
3487db2bc92SArnd Bergmann  * out of a power down. CPU power down may be executed on cpuidle or hotplug.
3497db2bc92SArnd Bergmann  */
qcom_scm_set_warm_boot_addr(void * entry)35052beb1fcSStephan Gerhold int qcom_scm_set_warm_boot_addr(void *entry)
35165f0c90bSElliot Berman {
352f60a317bSStephan Gerhold 	if (qcom_scm_set_boot_addr_mc(entry, QCOM_SCM_BOOT_MC_FLAG_WARMBOOT))
353f60a317bSStephan Gerhold 		/* Fallback to old SCM call */
35452beb1fcSStephan Gerhold 		return qcom_scm_set_boot_addr(entry, qcom_scm_cpu_warm_bits);
355f60a317bSStephan Gerhold 	return 0;
35665f0c90bSElliot Berman }
3572784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_set_warm_boot_addr);
35865f0c90bSElliot Berman 
35965f0c90bSElliot Berman /**
36052beb1fcSStephan Gerhold  * qcom_scm_set_cold_boot_addr() - Set the cold boot address for all cpus
361a353e4a0SLina Iyer  * @entry: Entry point function for the cpus
362a353e4a0SLina Iyer  */
qcom_scm_set_cold_boot_addr(void * entry)36352beb1fcSStephan Gerhold int qcom_scm_set_cold_boot_addr(void *entry)
364a353e4a0SLina Iyer {
365f60a317bSStephan Gerhold 	if (qcom_scm_set_boot_addr_mc(entry, QCOM_SCM_BOOT_MC_FLAG_COLDBOOT))
366f60a317bSStephan Gerhold 		/* Fallback to old SCM call */
36752beb1fcSStephan Gerhold 		return qcom_scm_set_boot_addr(entry, qcom_scm_cpu_cold_bits);
368f60a317bSStephan Gerhold 	return 0;
369a353e4a0SLina Iyer }
3702784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_set_cold_boot_addr);
3712ce76a6aSLina Iyer 
3722ce76a6aSLina Iyer /**
373767b0235SLina Iyer  * qcom_scm_cpu_power_down() - Power down the cpu
374a5d32f6dSYang Li  * @flags:	Flags to flush cache
375767b0235SLina Iyer  *
376767b0235SLina Iyer  * This is an end point to power down cpu. If there was a pending interrupt,
377767b0235SLina Iyer  * the control would return from this function, otherwise, the cpu jumps to the
378767b0235SLina Iyer  * warm boot entry point set for this cpu upon reset.
379767b0235SLina Iyer  */
qcom_scm_cpu_power_down(u32 flags)380767b0235SLina Iyer void qcom_scm_cpu_power_down(u32 flags)
381767b0235SLina Iyer {
38257d3b816SElliot Berman 	struct qcom_scm_desc desc = {
38357d3b816SElliot Berman 		.svc = QCOM_SCM_SVC_BOOT,
38457d3b816SElliot Berman 		.cmd = QCOM_SCM_BOOT_TERMINATE_PC,
38557d3b816SElliot Berman 		.args[0] = flags & QCOM_SCM_FLUSH_FLAG_MASK,
38657d3b816SElliot Berman 		.arginfo = QCOM_SCM_ARGS(1),
38757d3b816SElliot Berman 		.owner = ARM_SMCCC_OWNER_SIP,
38857d3b816SElliot Berman 	};
38957d3b816SElliot Berman 
39057d3b816SElliot Berman 	qcom_scm_call_atomic(__scm ? __scm->dev : NULL, &desc, NULL);
391767b0235SLina Iyer }
3922784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_cpu_power_down);
3939626b699Sjilai wang 
qcom_scm_set_remote_state(u32 state,u32 id)39465f0c90bSElliot Berman int qcom_scm_set_remote_state(u32 state, u32 id)
3959626b699Sjilai wang {
39657d3b816SElliot Berman 	struct qcom_scm_desc desc = {
39757d3b816SElliot Berman 		.svc = QCOM_SCM_SVC_BOOT,
39857d3b816SElliot Berman 		.cmd = QCOM_SCM_BOOT_SET_REMOTE_STATE,
39957d3b816SElliot Berman 		.arginfo = QCOM_SCM_ARGS(2),
40057d3b816SElliot Berman 		.args[0] = state,
40157d3b816SElliot Berman 		.args[1] = id,
40257d3b816SElliot Berman 		.owner = ARM_SMCCC_OWNER_SIP,
40357d3b816SElliot Berman 	};
40457d3b816SElliot Berman 	struct qcom_scm_res res;
40557d3b816SElliot Berman 	int ret;
40657d3b816SElliot Berman 
40757d3b816SElliot Berman 	ret = qcom_scm_call(__scm->dev, &desc, &res);
40857d3b816SElliot Berman 
40957d3b816SElliot Berman 	return ret ? : res.result[0];
41065f0c90bSElliot Berman }
4112784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_set_remote_state);
41265f0c90bSElliot Berman 
__qcom_scm_set_dload_mode(struct device * dev,bool enable)41357d3b816SElliot Berman static int __qcom_scm_set_dload_mode(struct device *dev, bool enable)
41457d3b816SElliot Berman {
41557d3b816SElliot Berman 	struct qcom_scm_desc desc = {
41657d3b816SElliot Berman 		.svc = QCOM_SCM_SVC_BOOT,
41757d3b816SElliot Berman 		.cmd = QCOM_SCM_BOOT_SET_DLOAD_MODE,
41857d3b816SElliot Berman 		.arginfo = QCOM_SCM_ARGS(2),
41957d3b816SElliot Berman 		.args[0] = QCOM_SCM_BOOT_SET_DLOAD_MODE,
42057d3b816SElliot Berman 		.owner = ARM_SMCCC_OWNER_SIP,
42157d3b816SElliot Berman 	};
42257d3b816SElliot Berman 
42357d3b816SElliot Berman 	desc.args[1] = enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0;
42457d3b816SElliot Berman 
425b88c2828SJonathan McDowell 	return qcom_scm_call_atomic(__scm->dev, &desc, NULL);
42657d3b816SElliot Berman }
42757d3b816SElliot Berman 
qcom_scm_set_download_mode(bool enable)42865f0c90bSElliot Berman static void qcom_scm_set_download_mode(bool enable)
42965f0c90bSElliot Berman {
43065f0c90bSElliot Berman 	bool avail;
43165f0c90bSElliot Berman 	int ret = 0;
43265f0c90bSElliot Berman 
43365f0c90bSElliot Berman 	avail = __qcom_scm_is_call_available(__scm->dev,
43465f0c90bSElliot Berman 					     QCOM_SCM_SVC_BOOT,
43565f0c90bSElliot Berman 					     QCOM_SCM_BOOT_SET_DLOAD_MODE);
43665f0c90bSElliot Berman 	if (avail) {
43765f0c90bSElliot Berman 		ret = __qcom_scm_set_dload_mode(__scm->dev, enable);
43865f0c90bSElliot Berman 	} else if (__scm->dload_mode_addr) {
43957d3b816SElliot Berman 		ret = qcom_scm_io_writel(__scm->dload_mode_addr,
44065f0c90bSElliot Berman 				enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0);
44165f0c90bSElliot Berman 	} else {
44265f0c90bSElliot Berman 		dev_err(__scm->dev,
44365f0c90bSElliot Berman 			"No available mechanism for setting download mode\n");
44465f0c90bSElliot Berman 	}
445d0f6fa7bSAndy Gross 
446d0f6fa7bSAndy Gross 	if (ret)
44765f0c90bSElliot Berman 		dev_err(__scm->dev, "failed to set download mode: %d\n", ret);
4489626b699Sjilai wang }
449b0a1614fSRob Clark 
450b0a1614fSRob Clark /**
451f01e90feSBjorn Andersson  * qcom_scm_pas_init_image() - Initialize peripheral authentication service
452f01e90feSBjorn Andersson  *			       state machine for a given peripheral, using the
453f01e90feSBjorn Andersson  *			       metadata
454f01e90feSBjorn Andersson  * @peripheral: peripheral id
455f01e90feSBjorn Andersson  * @metadata:	pointer to memory containing ELF header, program header table
456f01e90feSBjorn Andersson  *		and optional blob of data used for authenticating the metadata
457f01e90feSBjorn Andersson  *		and the rest of the firmware
458f01e90feSBjorn Andersson  * @size:	size of the metadata
4593a99f121SBjorn Andersson  * @ctx:	optional metadata context
460f01e90feSBjorn Andersson  *
4613a99f121SBjorn Andersson  * Return: 0 on success.
4623a99f121SBjorn Andersson  *
4633a99f121SBjorn Andersson  * Upon successful return, the PAS metadata context (@ctx) will be used to
4643a99f121SBjorn Andersson  * track the metadata allocation, this needs to be released by invoking
4653a99f121SBjorn Andersson  * qcom_scm_pas_metadata_release() by the caller.
466f01e90feSBjorn Andersson  */
qcom_scm_pas_init_image(u32 peripheral,const void * metadata,size_t size,struct qcom_scm_pas_metadata * ctx)4673a99f121SBjorn Andersson int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size,
4683a99f121SBjorn Andersson 			    struct qcom_scm_pas_metadata *ctx)
469f01e90feSBjorn Andersson {
470f01e90feSBjorn Andersson 	dma_addr_t mdata_phys;
471f01e90feSBjorn Andersson 	void *mdata_buf;
472f01e90feSBjorn Andersson 	int ret;
47357d3b816SElliot Berman 	struct qcom_scm_desc desc = {
47457d3b816SElliot Berman 		.svc = QCOM_SCM_SVC_PIL,
47557d3b816SElliot Berman 		.cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE,
47657d3b816SElliot Berman 		.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW),
47757d3b816SElliot Berman 		.args[0] = peripheral,
47857d3b816SElliot Berman 		.owner = ARM_SMCCC_OWNER_SIP,
47957d3b816SElliot Berman 	};
48057d3b816SElliot Berman 	struct qcom_scm_res res;
481f01e90feSBjorn Andersson 
482f01e90feSBjorn Andersson 	/*
483f01e90feSBjorn Andersson 	 * During the scm call memory protection will be enabled for the meta
484f01e90feSBjorn Andersson 	 * data blob, so make sure it's physically contiguous, 4K aligned and
485f01e90feSBjorn Andersson 	 * non-cachable to avoid XPU violations.
486f01e90feSBjorn Andersson 	 */
487f01e90feSBjorn Andersson 	mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys,
488f01e90feSBjorn Andersson 				       GFP_KERNEL);
489f01e90feSBjorn Andersson 	if (!mdata_buf) {
490f01e90feSBjorn Andersson 		dev_err(__scm->dev, "Allocation of metadata buffer failed.\n");
491f01e90feSBjorn Andersson 		return -ENOMEM;
492f01e90feSBjorn Andersson 	}
493f01e90feSBjorn Andersson 	memcpy(mdata_buf, metadata, size);
494f01e90feSBjorn Andersson 
495f01e90feSBjorn Andersson 	ret = qcom_scm_clk_enable();
496f01e90feSBjorn Andersson 	if (ret)
4973a99f121SBjorn Andersson 		goto out;
498f01e90feSBjorn Andersson 
49965b7ebdaSSibi Sankar 	ret = qcom_scm_bw_enable();
50065b7ebdaSSibi Sankar 	if (ret)
501130b4b94SGabor Juhos 		goto disable_clk;
50265b7ebdaSSibi Sankar 
50357d3b816SElliot Berman 	desc.args[1] = mdata_phys;
50457d3b816SElliot Berman 
50557d3b816SElliot Berman 	ret = qcom_scm_call(__scm->dev, &desc, &res);
50665b7ebdaSSibi Sankar 	qcom_scm_bw_disable();
507130b4b94SGabor Juhos 
508130b4b94SGabor Juhos disable_clk:
509f01e90feSBjorn Andersson 	qcom_scm_clk_disable();
510f01e90feSBjorn Andersson 
5113a99f121SBjorn Andersson out:
5123a99f121SBjorn Andersson 	if (ret < 0 || !ctx) {
513f01e90feSBjorn Andersson 		dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys);
5143a99f121SBjorn Andersson 	} else if (ctx) {
5153a99f121SBjorn Andersson 		ctx->ptr = mdata_buf;
5163a99f121SBjorn Andersson 		ctx->phys = mdata_phys;
5173a99f121SBjorn Andersson 		ctx->size = size;
5183a99f121SBjorn Andersson 	}
519f01e90feSBjorn Andersson 
52057d3b816SElliot Berman 	return ret ? : res.result[0];
521f01e90feSBjorn Andersson }
5222784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_pas_init_image);
523f01e90feSBjorn Andersson 
524f01e90feSBjorn Andersson /**
5253a99f121SBjorn Andersson  * qcom_scm_pas_metadata_release() - release metadata context
5263a99f121SBjorn Andersson  * @ctx:	metadata context
5273a99f121SBjorn Andersson  */
qcom_scm_pas_metadata_release(struct qcom_scm_pas_metadata * ctx)5283a99f121SBjorn Andersson void qcom_scm_pas_metadata_release(struct qcom_scm_pas_metadata *ctx)
5293a99f121SBjorn Andersson {
5303a99f121SBjorn Andersson 	if (!ctx->ptr)
5313a99f121SBjorn Andersson 		return;
5323a99f121SBjorn Andersson 
5333a99f121SBjorn Andersson 	dma_free_coherent(__scm->dev, ctx->size, ctx->ptr, ctx->phys);
5343a99f121SBjorn Andersson 
5353a99f121SBjorn Andersson 	ctx->ptr = NULL;
5363a99f121SBjorn Andersson 	ctx->phys = 0;
5373a99f121SBjorn Andersson 	ctx->size = 0;
5383a99f121SBjorn Andersson }
5392784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_pas_metadata_release);
5403a99f121SBjorn Andersson 
5413a99f121SBjorn Andersson /**
542f01e90feSBjorn Andersson  * qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral
543f01e90feSBjorn Andersson  *			      for firmware loading
544f01e90feSBjorn Andersson  * @peripheral:	peripheral id
545f01e90feSBjorn Andersson  * @addr:	start address of memory area to prepare
546f01e90feSBjorn Andersson  * @size:	size of the memory area to prepare
547f01e90feSBjorn Andersson  *
548f01e90feSBjorn Andersson  * Returns 0 on success.
549f01e90feSBjorn Andersson  */
qcom_scm_pas_mem_setup(u32 peripheral,phys_addr_t addr,phys_addr_t size)550f01e90feSBjorn Andersson int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size)
551f01e90feSBjorn Andersson {
552f01e90feSBjorn Andersson 	int ret;
55357d3b816SElliot Berman 	struct qcom_scm_desc desc = {
55457d3b816SElliot Berman 		.svc = QCOM_SCM_SVC_PIL,
55557d3b816SElliot Berman 		.cmd = QCOM_SCM_PIL_PAS_MEM_SETUP,
55657d3b816SElliot Berman 		.arginfo = QCOM_SCM_ARGS(3),
55757d3b816SElliot Berman 		.args[0] = peripheral,
55857d3b816SElliot Berman 		.args[1] = addr,
55957d3b816SElliot Berman 		.args[2] = size,
56057d3b816SElliot Berman 		.owner = ARM_SMCCC_OWNER_SIP,
56157d3b816SElliot Berman 	};
56257d3b816SElliot Berman 	struct qcom_scm_res res;
563f01e90feSBjorn Andersson 
564f01e90feSBjorn Andersson 	ret = qcom_scm_clk_enable();
565f01e90feSBjorn Andersson 	if (ret)
566f01e90feSBjorn Andersson 		return ret;
567f01e90feSBjorn Andersson 
56865b7ebdaSSibi Sankar 	ret = qcom_scm_bw_enable();
56965b7ebdaSSibi Sankar 	if (ret)
570130b4b94SGabor Juhos 		goto disable_clk;
57165b7ebdaSSibi Sankar 
57257d3b816SElliot Berman 	ret = qcom_scm_call(__scm->dev, &desc, &res);
57365b7ebdaSSibi Sankar 	qcom_scm_bw_disable();
574130b4b94SGabor Juhos 
575130b4b94SGabor Juhos disable_clk:
576f01e90feSBjorn Andersson 	qcom_scm_clk_disable();
577f01e90feSBjorn Andersson 
57857d3b816SElliot Berman 	return ret ? : res.result[0];
579f01e90feSBjorn Andersson }
5802784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_pas_mem_setup);
581f01e90feSBjorn Andersson 
582f01e90feSBjorn Andersson /**
583f01e90feSBjorn Andersson  * qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware
584f01e90feSBjorn Andersson  *				   and reset the remote processor
585f01e90feSBjorn Andersson  * @peripheral:	peripheral id
586f01e90feSBjorn Andersson  *
587f01e90feSBjorn Andersson  * Return 0 on success.
588f01e90feSBjorn Andersson  */
qcom_scm_pas_auth_and_reset(u32 peripheral)589f01e90feSBjorn Andersson int qcom_scm_pas_auth_and_reset(u32 peripheral)
590f01e90feSBjorn Andersson {
591f01e90feSBjorn Andersson 	int ret;
59257d3b816SElliot Berman 	struct qcom_scm_desc desc = {
59357d3b816SElliot Berman 		.svc = QCOM_SCM_SVC_PIL,
59457d3b816SElliot Berman 		.cmd = QCOM_SCM_PIL_PAS_AUTH_AND_RESET,
59557d3b816SElliot Berman 		.arginfo = QCOM_SCM_ARGS(1),
59657d3b816SElliot Berman 		.args[0] = peripheral,
59757d3b816SElliot Berman 		.owner = ARM_SMCCC_OWNER_SIP,
59857d3b816SElliot Berman 	};
59957d3b816SElliot Berman 	struct qcom_scm_res res;
600f01e90feSBjorn Andersson 
601f01e90feSBjorn Andersson 	ret = qcom_scm_clk_enable();
602f01e90feSBjorn Andersson 	if (ret)
603f01e90feSBjorn Andersson 		return ret;
604f01e90feSBjorn Andersson 
60565b7ebdaSSibi Sankar 	ret = qcom_scm_bw_enable();
60665b7ebdaSSibi Sankar 	if (ret)
607130b4b94SGabor Juhos 		goto disable_clk;
60865b7ebdaSSibi Sankar 
60957d3b816SElliot Berman 	ret = qcom_scm_call(__scm->dev, &desc, &res);
61065b7ebdaSSibi Sankar 	qcom_scm_bw_disable();
611130b4b94SGabor Juhos 
612130b4b94SGabor Juhos disable_clk:
613f01e90feSBjorn Andersson 	qcom_scm_clk_disable();
614f01e90feSBjorn Andersson 
61557d3b816SElliot Berman 	return ret ? : res.result[0];
616f01e90feSBjorn Andersson }
6172784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_pas_auth_and_reset);
618f01e90feSBjorn Andersson 
619f01e90feSBjorn Andersson /**
620f01e90feSBjorn Andersson  * qcom_scm_pas_shutdown() - Shut down the remote processor
621f01e90feSBjorn Andersson  * @peripheral: peripheral id
622f01e90feSBjorn Andersson  *
623f01e90feSBjorn Andersson  * Returns 0 on success.
624f01e90feSBjorn Andersson  */
qcom_scm_pas_shutdown(u32 peripheral)625f01e90feSBjorn Andersson int qcom_scm_pas_shutdown(u32 peripheral)
626f01e90feSBjorn Andersson {
627f01e90feSBjorn Andersson 	int ret;
62857d3b816SElliot Berman 	struct qcom_scm_desc desc = {
62957d3b816SElliot Berman 		.svc = QCOM_SCM_SVC_PIL,
63057d3b816SElliot Berman 		.cmd = QCOM_SCM_PIL_PAS_SHUTDOWN,
63157d3b816SElliot Berman 		.arginfo = QCOM_SCM_ARGS(1),
63257d3b816SElliot Berman 		.args[0] = peripheral,
63357d3b816SElliot Berman 		.owner = ARM_SMCCC_OWNER_SIP,
63457d3b816SElliot Berman 	};
63557d3b816SElliot Berman 	struct qcom_scm_res res;
636f01e90feSBjorn Andersson 
637f01e90feSBjorn Andersson 	ret = qcom_scm_clk_enable();
638f01e90feSBjorn Andersson 	if (ret)
639f01e90feSBjorn Andersson 		return ret;
640f01e90feSBjorn Andersson 
64165b7ebdaSSibi Sankar 	ret = qcom_scm_bw_enable();
64265b7ebdaSSibi Sankar 	if (ret)
643130b4b94SGabor Juhos 		goto disable_clk;
64465b7ebdaSSibi Sankar 
64557d3b816SElliot Berman 	ret = qcom_scm_call(__scm->dev, &desc, &res);
64665b7ebdaSSibi Sankar 	qcom_scm_bw_disable();
647130b4b94SGabor Juhos 
648130b4b94SGabor Juhos disable_clk:
649f01e90feSBjorn Andersson 	qcom_scm_clk_disable();
650f01e90feSBjorn Andersson 
65157d3b816SElliot Berman 	return ret ? : res.result[0];
652f01e90feSBjorn Andersson }
6532784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_pas_shutdown);
654f01e90feSBjorn Andersson 
65565f0c90bSElliot Berman /**
65665f0c90bSElliot Berman  * qcom_scm_pas_supported() - Check if the peripheral authentication service is
65765f0c90bSElliot Berman  *			      available for the given peripherial
65865f0c90bSElliot Berman  * @peripheral:	peripheral id
65965f0c90bSElliot Berman  *
66065f0c90bSElliot Berman  * Returns true if PAS is supported for this peripheral, otherwise false.
66165f0c90bSElliot Berman  */
qcom_scm_pas_supported(u32 peripheral)66265f0c90bSElliot Berman bool qcom_scm_pas_supported(u32 peripheral)
66365f0c90bSElliot Berman {
66465f0c90bSElliot Berman 	int ret;
66557d3b816SElliot Berman 	struct qcom_scm_desc desc = {
66657d3b816SElliot Berman 		.svc = QCOM_SCM_SVC_PIL,
66757d3b816SElliot Berman 		.cmd = QCOM_SCM_PIL_PAS_IS_SUPPORTED,
66857d3b816SElliot Berman 		.arginfo = QCOM_SCM_ARGS(1),
66957d3b816SElliot Berman 		.args[0] = peripheral,
67057d3b816SElliot Berman 		.owner = ARM_SMCCC_OWNER_SIP,
67157d3b816SElliot Berman 	};
67257d3b816SElliot Berman 	struct qcom_scm_res res;
67365f0c90bSElliot Berman 
6749d11af8bSStephen Boyd 	if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
6759d11af8bSStephen Boyd 					  QCOM_SCM_PIL_PAS_IS_SUPPORTED))
67665f0c90bSElliot Berman 		return false;
67765f0c90bSElliot Berman 
67857d3b816SElliot Berman 	ret = qcom_scm_call(__scm->dev, &desc, &res);
67957d3b816SElliot Berman 
68057d3b816SElliot Berman 	return ret ? false : !!res.result[0];
68165f0c90bSElliot Berman }
6822784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_pas_supported);
68365f0c90bSElliot Berman 
__qcom_scm_pas_mss_reset(struct device * dev,bool reset)68457d3b816SElliot Berman static int __qcom_scm_pas_mss_reset(struct device *dev, bool reset)
68557d3b816SElliot Berman {
68657d3b816SElliot Berman 	struct qcom_scm_desc desc = {
68757d3b816SElliot Berman 		.svc = QCOM_SCM_SVC_PIL,
68857d3b816SElliot Berman 		.cmd = QCOM_SCM_PIL_PAS_MSS_RESET,
68957d3b816SElliot Berman 		.arginfo = QCOM_SCM_ARGS(2),
69057d3b816SElliot Berman 		.args[0] = reset,
69157d3b816SElliot Berman 		.args[1] = 0,
69257d3b816SElliot Berman 		.owner = ARM_SMCCC_OWNER_SIP,
69357d3b816SElliot Berman 	};
69457d3b816SElliot Berman 	struct qcom_scm_res res;
69557d3b816SElliot Berman 	int ret;
69657d3b816SElliot Berman 
69757d3b816SElliot Berman 	ret = qcom_scm_call(__scm->dev, &desc, &res);
69857d3b816SElliot Berman 
69957d3b816SElliot Berman 	return ret ? : res.result[0];
70057d3b816SElliot Berman }
70157d3b816SElliot Berman 
qcom_scm_pas_reset_assert(struct reset_controller_dev * rcdev,unsigned long idx)702dd4fe5b2SBjorn Andersson static int qcom_scm_pas_reset_assert(struct reset_controller_dev *rcdev,
703dd4fe5b2SBjorn Andersson 				     unsigned long idx)
704dd4fe5b2SBjorn Andersson {
705dd4fe5b2SBjorn Andersson 	if (idx != 0)
706dd4fe5b2SBjorn Andersson 		return -EINVAL;
707dd4fe5b2SBjorn Andersson 
708dd4fe5b2SBjorn Andersson 	return __qcom_scm_pas_mss_reset(__scm->dev, 1);
709dd4fe5b2SBjorn Andersson }
710dd4fe5b2SBjorn Andersson 
qcom_scm_pas_reset_deassert(struct reset_controller_dev * rcdev,unsigned long idx)711dd4fe5b2SBjorn Andersson static int qcom_scm_pas_reset_deassert(struct reset_controller_dev *rcdev,
712dd4fe5b2SBjorn Andersson 				       unsigned long idx)
713dd4fe5b2SBjorn Andersson {
714dd4fe5b2SBjorn Andersson 	if (idx != 0)
715dd4fe5b2SBjorn Andersson 		return -EINVAL;
716dd4fe5b2SBjorn Andersson 
717dd4fe5b2SBjorn Andersson 	return __qcom_scm_pas_mss_reset(__scm->dev, 0);
718dd4fe5b2SBjorn Andersson }
719dd4fe5b2SBjorn Andersson 
720dd4fe5b2SBjorn Andersson static const struct reset_control_ops qcom_scm_pas_reset_ops = {
721dd4fe5b2SBjorn Andersson 	.assert = qcom_scm_pas_reset_assert,
722dd4fe5b2SBjorn Andersson 	.deassert = qcom_scm_pas_reset_deassert,
723dd4fe5b2SBjorn Andersson };
724dd4fe5b2SBjorn Andersson 
qcom_scm_io_readl(phys_addr_t addr,unsigned int * val)72565f0c90bSElliot Berman int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val)
72665f0c90bSElliot Berman {
72757d3b816SElliot Berman 	struct qcom_scm_desc desc = {
72857d3b816SElliot Berman 		.svc = QCOM_SCM_SVC_IO,
72957d3b816SElliot Berman 		.cmd = QCOM_SCM_IO_READ,
73057d3b816SElliot Berman 		.arginfo = QCOM_SCM_ARGS(1),
73157d3b816SElliot Berman 		.args[0] = addr,
73257d3b816SElliot Berman 		.owner = ARM_SMCCC_OWNER_SIP,
73357d3b816SElliot Berman 	};
73457d3b816SElliot Berman 	struct qcom_scm_res res;
73557d3b816SElliot Berman 	int ret;
73657d3b816SElliot Berman 
73757d3b816SElliot Berman 
738b88c2828SJonathan McDowell 	ret = qcom_scm_call_atomic(__scm->dev, &desc, &res);
73957d3b816SElliot Berman 	if (ret >= 0)
74057d3b816SElliot Berman 		*val = res.result[0];
74157d3b816SElliot Berman 
74257d3b816SElliot Berman 	return ret < 0 ? ret : 0;
74365f0c90bSElliot Berman }
7442784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_io_readl);
74565f0c90bSElliot Berman 
qcom_scm_io_writel(phys_addr_t addr,unsigned int val)74665f0c90bSElliot Berman int qcom_scm_io_writel(phys_addr_t addr, unsigned int val)
74765f0c90bSElliot Berman {
74857d3b816SElliot Berman 	struct qcom_scm_desc desc = {
74957d3b816SElliot Berman 		.svc = QCOM_SCM_SVC_IO,
75057d3b816SElliot Berman 		.cmd = QCOM_SCM_IO_WRITE,
75157d3b816SElliot Berman 		.arginfo = QCOM_SCM_ARGS(2),
75257d3b816SElliot Berman 		.args[0] = addr,
75357d3b816SElliot Berman 		.args[1] = val,
75457d3b816SElliot Berman 		.owner = ARM_SMCCC_OWNER_SIP,
75557d3b816SElliot Berman 	};
75657d3b816SElliot Berman 
757b88c2828SJonathan McDowell 	return qcom_scm_call_atomic(__scm->dev, &desc, NULL);
75865f0c90bSElliot Berman }
7592784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_io_writel);
76065f0c90bSElliot Berman 
7610434a406SRob Clark /**
7620434a406SRob Clark  * qcom_scm_restore_sec_cfg_available() - Check if secure environment
7630434a406SRob Clark  * supports restore security config interface.
7640434a406SRob Clark  *
7650434a406SRob Clark  * Return true if restore-cfg interface is supported, false if not.
7660434a406SRob Clark  */
qcom_scm_restore_sec_cfg_available(void)7670434a406SRob Clark bool qcom_scm_restore_sec_cfg_available(void)
7680434a406SRob Clark {
7690434a406SRob Clark 	return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_MP,
7705443cc5fSElliot Berman 					    QCOM_SCM_MP_RESTORE_SEC_CFG);
7710434a406SRob Clark }
7722784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_restore_sec_cfg_available);
7730434a406SRob Clark 
qcom_scm_restore_sec_cfg(u32 device_id,u32 spare)774a2c680c6SRob Clark int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare)
775a2c680c6SRob Clark {
77657d3b816SElliot Berman 	struct qcom_scm_desc desc = {
77757d3b816SElliot Berman 		.svc = QCOM_SCM_SVC_MP,
77857d3b816SElliot Berman 		.cmd = QCOM_SCM_MP_RESTORE_SEC_CFG,
77957d3b816SElliot Berman 		.arginfo = QCOM_SCM_ARGS(2),
78057d3b816SElliot Berman 		.args[0] = device_id,
78157d3b816SElliot Berman 		.args[1] = spare,
78257d3b816SElliot Berman 		.owner = ARM_SMCCC_OWNER_SIP,
78357d3b816SElliot Berman 	};
78457d3b816SElliot Berman 	struct qcom_scm_res res;
78557d3b816SElliot Berman 	int ret;
78657d3b816SElliot Berman 
78757d3b816SElliot Berman 	ret = qcom_scm_call(__scm->dev, &desc, &res);
78857d3b816SElliot Berman 
78957d3b816SElliot Berman 	return ret ? : res.result[0];
790a2c680c6SRob Clark }
7912784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_restore_sec_cfg);
792a2c680c6SRob Clark 
qcom_scm_iommu_secure_ptbl_size(u32 spare,size_t * size)793b182cc4dSStanimir Varbanov int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size)
794b182cc4dSStanimir Varbanov {
79557d3b816SElliot Berman 	struct qcom_scm_desc desc = {
79657d3b816SElliot Berman 		.svc = QCOM_SCM_SVC_MP,
79757d3b816SElliot Berman 		.cmd = QCOM_SCM_MP_IOMMU_SECURE_PTBL_SIZE,
79857d3b816SElliot Berman 		.arginfo = QCOM_SCM_ARGS(1),
79957d3b816SElliot Berman 		.args[0] = spare,
80057d3b816SElliot Berman 		.owner = ARM_SMCCC_OWNER_SIP,
80157d3b816SElliot Berman 	};
80257d3b816SElliot Berman 	struct qcom_scm_res res;
80357d3b816SElliot Berman 	int ret;
80457d3b816SElliot Berman 
80557d3b816SElliot Berman 	ret = qcom_scm_call(__scm->dev, &desc, &res);
80657d3b816SElliot Berman 
80757d3b816SElliot Berman 	if (size)
80857d3b816SElliot Berman 		*size = res.result[0];
80957d3b816SElliot Berman 
81057d3b816SElliot Berman 	return ret ? : res.result[1];
811b182cc4dSStanimir Varbanov }
8122784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_iommu_secure_ptbl_size);
813b182cc4dSStanimir Varbanov 
qcom_scm_iommu_secure_ptbl_init(u64 addr,u32 size,u32 spare)814b182cc4dSStanimir Varbanov int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare)
815b182cc4dSStanimir Varbanov {
81657d3b816SElliot Berman 	struct qcom_scm_desc desc = {
81757d3b816SElliot Berman 		.svc = QCOM_SCM_SVC_MP,
81857d3b816SElliot Berman 		.cmd = QCOM_SCM_MP_IOMMU_SECURE_PTBL_INIT,
81957d3b816SElliot Berman 		.arginfo = QCOM_SCM_ARGS(3, QCOM_SCM_RW, QCOM_SCM_VAL,
82057d3b816SElliot Berman 					 QCOM_SCM_VAL),
82157d3b816SElliot Berman 		.args[0] = addr,
82257d3b816SElliot Berman 		.args[1] = size,
82357d3b816SElliot Berman 		.args[2] = spare,
82457d3b816SElliot Berman 		.owner = ARM_SMCCC_OWNER_SIP,
82557d3b816SElliot Berman 	};
82657d3b816SElliot Berman 	int ret;
82757d3b816SElliot Berman 
82857d3b816SElliot Berman 	ret = qcom_scm_call(__scm->dev, &desc, NULL);
82957d3b816SElliot Berman 
83057d3b816SElliot Berman 	/* the pg table has been initialized already, ignore the error */
83157d3b816SElliot Berman 	if (ret == -EPERM)
83257d3b816SElliot Berman 		ret = 0;
83357d3b816SElliot Berman 
83457d3b816SElliot Berman 	return ret;
835b182cc4dSStanimir Varbanov }
8362784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_iommu_secure_ptbl_init);
837b182cc4dSStanimir Varbanov 
qcom_scm_iommu_set_cp_pool_size(u32 spare,u32 size)83894351509SAngeloGioacchino Del Regno int qcom_scm_iommu_set_cp_pool_size(u32 spare, u32 size)
83994351509SAngeloGioacchino Del Regno {
84094351509SAngeloGioacchino Del Regno 	struct qcom_scm_desc desc = {
84194351509SAngeloGioacchino Del Regno 		.svc = QCOM_SCM_SVC_MP,
84294351509SAngeloGioacchino Del Regno 		.cmd = QCOM_SCM_MP_IOMMU_SET_CP_POOL_SIZE,
84394351509SAngeloGioacchino Del Regno 		.arginfo = QCOM_SCM_ARGS(2),
84494351509SAngeloGioacchino Del Regno 		.args[0] = size,
84594351509SAngeloGioacchino Del Regno 		.args[1] = spare,
84694351509SAngeloGioacchino Del Regno 		.owner = ARM_SMCCC_OWNER_SIP,
84794351509SAngeloGioacchino Del Regno 	};
84894351509SAngeloGioacchino Del Regno 
84994351509SAngeloGioacchino Del Regno 	return qcom_scm_call(__scm->dev, &desc, NULL);
85094351509SAngeloGioacchino Del Regno }
8512784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_iommu_set_cp_pool_size);
85294351509SAngeloGioacchino Del Regno 
qcom_scm_mem_protect_video_var(u32 cp_start,u32 cp_size,u32 cp_nonpixel_start,u32 cp_nonpixel_size)8536d885330SStanimir Varbanov int qcom_scm_mem_protect_video_var(u32 cp_start, u32 cp_size,
8546d885330SStanimir Varbanov 				   u32 cp_nonpixel_start,
8556d885330SStanimir Varbanov 				   u32 cp_nonpixel_size)
8566d885330SStanimir Varbanov {
8576d885330SStanimir Varbanov 	int ret;
8586d885330SStanimir Varbanov 	struct qcom_scm_desc desc = {
8596d885330SStanimir Varbanov 		.svc = QCOM_SCM_SVC_MP,
8606d885330SStanimir Varbanov 		.cmd = QCOM_SCM_MP_VIDEO_VAR,
8616d885330SStanimir Varbanov 		.arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_VAL, QCOM_SCM_VAL,
8626d885330SStanimir Varbanov 					 QCOM_SCM_VAL, QCOM_SCM_VAL),
8636d885330SStanimir Varbanov 		.args[0] = cp_start,
8646d885330SStanimir Varbanov 		.args[1] = cp_size,
8656d885330SStanimir Varbanov 		.args[2] = cp_nonpixel_start,
8666d885330SStanimir Varbanov 		.args[3] = cp_nonpixel_size,
8676d885330SStanimir Varbanov 		.owner = ARM_SMCCC_OWNER_SIP,
8686d885330SStanimir Varbanov 	};
8696d885330SStanimir Varbanov 	struct qcom_scm_res res;
8706d885330SStanimir Varbanov 
8716d885330SStanimir Varbanov 	ret = qcom_scm_call(__scm->dev, &desc, &res);
8726d885330SStanimir Varbanov 
8736d885330SStanimir Varbanov 	return ret ? : res.result[0];
8746d885330SStanimir Varbanov }
8752784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_mem_protect_video_var);
8766d885330SStanimir Varbanov 
__qcom_scm_assign_mem(struct device * dev,phys_addr_t mem_region,size_t mem_sz,phys_addr_t src,size_t src_sz,phys_addr_t dest,size_t dest_sz)87757d3b816SElliot Berman static int __qcom_scm_assign_mem(struct device *dev, phys_addr_t mem_region,
87857d3b816SElliot Berman 				 size_t mem_sz, phys_addr_t src, size_t src_sz,
87957d3b816SElliot Berman 				 phys_addr_t dest, size_t dest_sz)
88057d3b816SElliot Berman {
88157d3b816SElliot Berman 	int ret;
88257d3b816SElliot Berman 	struct qcom_scm_desc desc = {
88357d3b816SElliot Berman 		.svc = QCOM_SCM_SVC_MP,
88457d3b816SElliot Berman 		.cmd = QCOM_SCM_MP_ASSIGN,
88557d3b816SElliot Berman 		.arginfo = QCOM_SCM_ARGS(7, QCOM_SCM_RO, QCOM_SCM_VAL,
88657d3b816SElliot Berman 					 QCOM_SCM_RO, QCOM_SCM_VAL, QCOM_SCM_RO,
88757d3b816SElliot Berman 					 QCOM_SCM_VAL, QCOM_SCM_VAL),
88857d3b816SElliot Berman 		.args[0] = mem_region,
88957d3b816SElliot Berman 		.args[1] = mem_sz,
89057d3b816SElliot Berman 		.args[2] = src,
89157d3b816SElliot Berman 		.args[3] = src_sz,
89257d3b816SElliot Berman 		.args[4] = dest,
89357d3b816SElliot Berman 		.args[5] = dest_sz,
89457d3b816SElliot Berman 		.args[6] = 0,
89557d3b816SElliot Berman 		.owner = ARM_SMCCC_OWNER_SIP,
89657d3b816SElliot Berman 	};
89757d3b816SElliot Berman 	struct qcom_scm_res res;
89857d3b816SElliot Berman 
89957d3b816SElliot Berman 	ret = qcom_scm_call(dev, &desc, &res);
90057d3b816SElliot Berman 
90157d3b816SElliot Berman 	return ret ? : res.result[0];
90257d3b816SElliot Berman }
90357d3b816SElliot Berman 
904d82bd359SAvaneesh Kumar Dwivedi /**
905d82bd359SAvaneesh Kumar Dwivedi  * qcom_scm_assign_mem() - Make a secure call to reassign memory ownership
906d82bd359SAvaneesh Kumar Dwivedi  * @mem_addr: mem region whose ownership need to be reassigned
907d82bd359SAvaneesh Kumar Dwivedi  * @mem_sz:   size of the region.
908d82bd359SAvaneesh Kumar Dwivedi  * @srcvm:    vmid for current set of owners, each set bit in
909d82bd359SAvaneesh Kumar Dwivedi  *            flag indicate a unique owner
910c8b08fc0SStephen Boyd  * @newvm:    array having new owners and corresponding permission
911d82bd359SAvaneesh Kumar Dwivedi  *            flags
912d82bd359SAvaneesh Kumar Dwivedi  * @dest_cnt: number of owners in next set.
913d82bd359SAvaneesh Kumar Dwivedi  *
914c8b08fc0SStephen Boyd  * Return negative errno on failure or 0 on success with @srcvm updated.
915d82bd359SAvaneesh Kumar Dwivedi  */
qcom_scm_assign_mem(phys_addr_t mem_addr,size_t mem_sz,u64 * srcvm,const struct qcom_scm_vmperm * newvm,unsigned int dest_cnt)916d82bd359SAvaneesh Kumar Dwivedi int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz,
917968a26a0SElliot Berman 			u64 *srcvm,
918af311ff9SStephen Boyd 			const struct qcom_scm_vmperm *newvm,
919af311ff9SStephen Boyd 			unsigned int dest_cnt)
920d82bd359SAvaneesh Kumar Dwivedi {
921d82bd359SAvaneesh Kumar Dwivedi 	struct qcom_scm_current_perm_info *destvm;
922d82bd359SAvaneesh Kumar Dwivedi 	struct qcom_scm_mem_map_info *mem_to_map;
923d82bd359SAvaneesh Kumar Dwivedi 	phys_addr_t mem_to_map_phys;
924d82bd359SAvaneesh Kumar Dwivedi 	phys_addr_t dest_phys;
925459b1f86SChristoph Hellwig 	dma_addr_t ptr_phys;
926d82bd359SAvaneesh Kumar Dwivedi 	size_t mem_to_map_sz;
927d82bd359SAvaneesh Kumar Dwivedi 	size_t dest_sz;
928d82bd359SAvaneesh Kumar Dwivedi 	size_t src_sz;
929d82bd359SAvaneesh Kumar Dwivedi 	size_t ptr_sz;
930d82bd359SAvaneesh Kumar Dwivedi 	int next_vm;
931d82bd359SAvaneesh Kumar Dwivedi 	__le32 *src;
932d82bd359SAvaneesh Kumar Dwivedi 	void *ptr;
933af311ff9SStephen Boyd 	int ret, i, b;
934968a26a0SElliot Berman 	u64 srcvm_bits = *srcvm;
935d82bd359SAvaneesh Kumar Dwivedi 
936968a26a0SElliot Berman 	src_sz = hweight64(srcvm_bits) * sizeof(*src);
937d82bd359SAvaneesh Kumar Dwivedi 	mem_to_map_sz = sizeof(*mem_to_map);
938d82bd359SAvaneesh Kumar Dwivedi 	dest_sz = dest_cnt * sizeof(*destvm);
939d82bd359SAvaneesh Kumar Dwivedi 	ptr_sz = ALIGN(src_sz, SZ_64) + ALIGN(mem_to_map_sz, SZ_64) +
940d82bd359SAvaneesh Kumar Dwivedi 			ALIGN(dest_sz, SZ_64);
941d82bd359SAvaneesh Kumar Dwivedi 
942459b1f86SChristoph Hellwig 	ptr = dma_alloc_coherent(__scm->dev, ptr_sz, &ptr_phys, GFP_KERNEL);
943d82bd359SAvaneesh Kumar Dwivedi 	if (!ptr)
944d82bd359SAvaneesh Kumar Dwivedi 		return -ENOMEM;
945d82bd359SAvaneesh Kumar Dwivedi 
946d82bd359SAvaneesh Kumar Dwivedi 	/* Fill source vmid detail */
947d82bd359SAvaneesh Kumar Dwivedi 	src = ptr;
948af311ff9SStephen Boyd 	i = 0;
949968a26a0SElliot Berman 	for (b = 0; b < BITS_PER_TYPE(u64); b++) {
950968a26a0SElliot Berman 		if (srcvm_bits & BIT(b))
951af311ff9SStephen Boyd 			src[i++] = cpu_to_le32(b);
952968a26a0SElliot Berman 	}
953d82bd359SAvaneesh Kumar Dwivedi 
954d82bd359SAvaneesh Kumar Dwivedi 	/* Fill details of mem buff to map */
955d82bd359SAvaneesh Kumar Dwivedi 	mem_to_map = ptr + ALIGN(src_sz, SZ_64);
956d82bd359SAvaneesh Kumar Dwivedi 	mem_to_map_phys = ptr_phys + ALIGN(src_sz, SZ_64);
957af311ff9SStephen Boyd 	mem_to_map->mem_addr = cpu_to_le64(mem_addr);
958af311ff9SStephen Boyd 	mem_to_map->mem_size = cpu_to_le64(mem_sz);
959d82bd359SAvaneesh Kumar Dwivedi 
960d82bd359SAvaneesh Kumar Dwivedi 	next_vm = 0;
961d82bd359SAvaneesh Kumar Dwivedi 	/* Fill details of next vmid detail */
962d82bd359SAvaneesh Kumar Dwivedi 	destvm = ptr + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(src_sz, SZ_64);
963d82bd359SAvaneesh Kumar Dwivedi 	dest_phys = ptr_phys + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(src_sz, SZ_64);
964af311ff9SStephen Boyd 	for (i = 0; i < dest_cnt; i++, destvm++, newvm++) {
965af311ff9SStephen Boyd 		destvm->vmid = cpu_to_le32(newvm->vmid);
966af311ff9SStephen Boyd 		destvm->perm = cpu_to_le32(newvm->perm);
967af311ff9SStephen Boyd 		destvm->ctx = 0;
968af311ff9SStephen Boyd 		destvm->ctx_size = 0;
969af311ff9SStephen Boyd 		next_vm |= BIT(newvm->vmid);
970d82bd359SAvaneesh Kumar Dwivedi 	}
971d82bd359SAvaneesh Kumar Dwivedi 
972d82bd359SAvaneesh Kumar Dwivedi 	ret = __qcom_scm_assign_mem(__scm->dev, mem_to_map_phys, mem_to_map_sz,
973d82bd359SAvaneesh Kumar Dwivedi 				    ptr_phys, src_sz, dest_phys, dest_sz);
974459b1f86SChristoph Hellwig 	dma_free_coherent(__scm->dev, ptr_sz, ptr, ptr_phys);
975d82bd359SAvaneesh Kumar Dwivedi 	if (ret) {
976d82bd359SAvaneesh Kumar Dwivedi 		dev_err(__scm->dev,
977c8b08fc0SStephen Boyd 			"Assign memory protection call failed %d\n", ret);
978d82bd359SAvaneesh Kumar Dwivedi 		return -EINVAL;
979d82bd359SAvaneesh Kumar Dwivedi 	}
980d82bd359SAvaneesh Kumar Dwivedi 
981d82bd359SAvaneesh Kumar Dwivedi 	*srcvm = next_vm;
982d82bd359SAvaneesh Kumar Dwivedi 	return 0;
983d82bd359SAvaneesh Kumar Dwivedi }
9842784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_assign_mem);
985d82bd359SAvaneesh Kumar Dwivedi 
98665f0c90bSElliot Berman /**
98765f0c90bSElliot Berman  * qcom_scm_ocmem_lock_available() - is OCMEM lock/unlock interface available
98865f0c90bSElliot Berman  */
qcom_scm_ocmem_lock_available(void)98965f0c90bSElliot Berman bool qcom_scm_ocmem_lock_available(void)
99065f0c90bSElliot Berman {
99165f0c90bSElliot Berman 	return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_OCMEM,
99265f0c90bSElliot Berman 					    QCOM_SCM_OCMEM_LOCK_CMD);
99365f0c90bSElliot Berman }
9942784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_ocmem_lock_available);
99565f0c90bSElliot Berman 
99665f0c90bSElliot Berman /**
99765f0c90bSElliot Berman  * qcom_scm_ocmem_lock() - call OCMEM lock interface to assign an OCMEM
99865f0c90bSElliot Berman  * region to the specified initiator
99965f0c90bSElliot Berman  *
100065f0c90bSElliot Berman  * @id:     tz initiator id
100165f0c90bSElliot Berman  * @offset: OCMEM offset
100265f0c90bSElliot Berman  * @size:   OCMEM size
100365f0c90bSElliot Berman  * @mode:   access mode (WIDE/NARROW)
100465f0c90bSElliot Berman  */
qcom_scm_ocmem_lock(enum qcom_scm_ocmem_client id,u32 offset,u32 size,u32 mode)100565f0c90bSElliot Berman int qcom_scm_ocmem_lock(enum qcom_scm_ocmem_client id, u32 offset, u32 size,
100665f0c90bSElliot Berman 			u32 mode)
100765f0c90bSElliot Berman {
100857d3b816SElliot Berman 	struct qcom_scm_desc desc = {
100957d3b816SElliot Berman 		.svc = QCOM_SCM_SVC_OCMEM,
101057d3b816SElliot Berman 		.cmd = QCOM_SCM_OCMEM_LOCK_CMD,
101157d3b816SElliot Berman 		.args[0] = id,
101257d3b816SElliot Berman 		.args[1] = offset,
101357d3b816SElliot Berman 		.args[2] = size,
101457d3b816SElliot Berman 		.args[3] = mode,
101557d3b816SElliot Berman 		.arginfo = QCOM_SCM_ARGS(4),
101657d3b816SElliot Berman 	};
101757d3b816SElliot Berman 
101857d3b816SElliot Berman 	return qcom_scm_call(__scm->dev, &desc, NULL);
101965f0c90bSElliot Berman }
10202784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_ocmem_lock);
102165f0c90bSElliot Berman 
102265f0c90bSElliot Berman /**
102365f0c90bSElliot Berman  * qcom_scm_ocmem_unlock() - call OCMEM unlock interface to release an OCMEM
102465f0c90bSElliot Berman  * region from the specified initiator
102565f0c90bSElliot Berman  *
102665f0c90bSElliot Berman  * @id:     tz initiator id
102765f0c90bSElliot Berman  * @offset: OCMEM offset
102865f0c90bSElliot Berman  * @size:   OCMEM size
102965f0c90bSElliot Berman  */
qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id,u32 offset,u32 size)103065f0c90bSElliot Berman int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset, u32 size)
103165f0c90bSElliot Berman {
103257d3b816SElliot Berman 	struct qcom_scm_desc desc = {
103357d3b816SElliot Berman 		.svc = QCOM_SCM_SVC_OCMEM,
103457d3b816SElliot Berman 		.cmd = QCOM_SCM_OCMEM_UNLOCK_CMD,
103557d3b816SElliot Berman 		.args[0] = id,
103657d3b816SElliot Berman 		.args[1] = offset,
103757d3b816SElliot Berman 		.args[2] = size,
103857d3b816SElliot Berman 		.arginfo = QCOM_SCM_ARGS(3),
103957d3b816SElliot Berman 	};
104057d3b816SElliot Berman 
104157d3b816SElliot Berman 	return qcom_scm_call(__scm->dev, &desc, NULL);
104265f0c90bSElliot Berman }
10432784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_ocmem_unlock);
104465f0c90bSElliot Berman 
104565f0c90bSElliot Berman /**
10460f206514SEric Biggers  * qcom_scm_ice_available() - Is the ICE key programming interface available?
10470f206514SEric Biggers  *
10480f206514SEric Biggers  * Return: true iff the SCM calls wrapped by qcom_scm_ice_invalidate_key() and
10490f206514SEric Biggers  *	   qcom_scm_ice_set_key() are available.
10500f206514SEric Biggers  */
qcom_scm_ice_available(void)10510f206514SEric Biggers bool qcom_scm_ice_available(void)
10520f206514SEric Biggers {
10530f206514SEric Biggers 	return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_ES,
10540f206514SEric Biggers 					    QCOM_SCM_ES_INVALIDATE_ICE_KEY) &&
10550f206514SEric Biggers 		__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_ES,
10560f206514SEric Biggers 					     QCOM_SCM_ES_CONFIG_SET_ICE_KEY);
10570f206514SEric Biggers }
10582784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_ice_available);
10590f206514SEric Biggers 
10600f206514SEric Biggers /**
10610f206514SEric Biggers  * qcom_scm_ice_invalidate_key() - Invalidate an inline encryption key
10620f206514SEric Biggers  * @index: the keyslot to invalidate
10630f206514SEric Biggers  *
1064433611eaSEric Biggers  * The UFSHCI and eMMC standards define a standard way to do this, but it
1065433611eaSEric Biggers  * doesn't work on these SoCs; only this SCM call does.
1066433611eaSEric Biggers  *
1067433611eaSEric Biggers  * It is assumed that the SoC has only one ICE instance being used, as this SCM
1068433611eaSEric Biggers  * call doesn't specify which ICE instance the keyslot belongs to.
10690f206514SEric Biggers  *
10700f206514SEric Biggers  * Return: 0 on success; -errno on failure.
10710f206514SEric Biggers  */
qcom_scm_ice_invalidate_key(u32 index)10720f206514SEric Biggers int qcom_scm_ice_invalidate_key(u32 index)
10730f206514SEric Biggers {
10740f206514SEric Biggers 	struct qcom_scm_desc desc = {
10750f206514SEric Biggers 		.svc = QCOM_SCM_SVC_ES,
10760f206514SEric Biggers 		.cmd = QCOM_SCM_ES_INVALIDATE_ICE_KEY,
10770f206514SEric Biggers 		.arginfo = QCOM_SCM_ARGS(1),
10780f206514SEric Biggers 		.args[0] = index,
10790f206514SEric Biggers 		.owner = ARM_SMCCC_OWNER_SIP,
10800f206514SEric Biggers 	};
10810f206514SEric Biggers 
10820f206514SEric Biggers 	return qcom_scm_call(__scm->dev, &desc, NULL);
10830f206514SEric Biggers }
10842784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_ice_invalidate_key);
10850f206514SEric Biggers 
10860f206514SEric Biggers /**
10870f206514SEric Biggers  * qcom_scm_ice_set_key() - Set an inline encryption key
10880f206514SEric Biggers  * @index: the keyslot into which to set the key
10890f206514SEric Biggers  * @key: the key to program
10900f206514SEric Biggers  * @key_size: the size of the key in bytes
10910f206514SEric Biggers  * @cipher: the encryption algorithm the key is for
10920f206514SEric Biggers  * @data_unit_size: the encryption data unit size, i.e. the size of each
10930f206514SEric Biggers  *		    individual plaintext and ciphertext.  Given in 512-byte
10940f206514SEric Biggers  *		    units, e.g. 1 = 512 bytes, 8 = 4096 bytes, etc.
10950f206514SEric Biggers  *
10960f206514SEric Biggers  * Program a key into a keyslot of Qualcomm ICE (Inline Crypto Engine), where it
1097433611eaSEric Biggers  * can then be used to encrypt/decrypt UFS or eMMC I/O requests inline.
10980f206514SEric Biggers  *
1099433611eaSEric Biggers  * The UFSHCI and eMMC standards define a standard way to do this, but it
1100433611eaSEric Biggers  * doesn't work on these SoCs; only this SCM call does.
1101433611eaSEric Biggers  *
1102433611eaSEric Biggers  * It is assumed that the SoC has only one ICE instance being used, as this SCM
1103433611eaSEric Biggers  * call doesn't specify which ICE instance the keyslot belongs to.
11040f206514SEric Biggers  *
11050f206514SEric Biggers  * Return: 0 on success; -errno on failure.
11060f206514SEric Biggers  */
qcom_scm_ice_set_key(u32 index,const u8 * key,u32 key_size,enum qcom_scm_ice_cipher cipher,u32 data_unit_size)11070f206514SEric Biggers int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size,
11080f206514SEric Biggers 			 enum qcom_scm_ice_cipher cipher, u32 data_unit_size)
11090f206514SEric Biggers {
11100f206514SEric Biggers 	struct qcom_scm_desc desc = {
11110f206514SEric Biggers 		.svc = QCOM_SCM_SVC_ES,
11120f206514SEric Biggers 		.cmd = QCOM_SCM_ES_CONFIG_SET_ICE_KEY,
11130f206514SEric Biggers 		.arginfo = QCOM_SCM_ARGS(5, QCOM_SCM_VAL, QCOM_SCM_RW,
11140f206514SEric Biggers 					 QCOM_SCM_VAL, QCOM_SCM_VAL,
11150f206514SEric Biggers 					 QCOM_SCM_VAL),
11160f206514SEric Biggers 		.args[0] = index,
11170f206514SEric Biggers 		.args[2] = key_size,
11180f206514SEric Biggers 		.args[3] = cipher,
11190f206514SEric Biggers 		.args[4] = data_unit_size,
11200f206514SEric Biggers 		.owner = ARM_SMCCC_OWNER_SIP,
11210f206514SEric Biggers 	};
11220f206514SEric Biggers 	void *keybuf;
11230f206514SEric Biggers 	dma_addr_t key_phys;
11240f206514SEric Biggers 	int ret;
11250f206514SEric Biggers 
11260f206514SEric Biggers 	/*
11270f206514SEric Biggers 	 * 'key' may point to vmalloc()'ed memory, but we need to pass a
11280f206514SEric Biggers 	 * physical address that's been properly flushed.  The sanctioned way to
11290f206514SEric Biggers 	 * do this is by using the DMA API.  But as is best practice for crypto
11300f206514SEric Biggers 	 * keys, we also must wipe the key after use.  This makes kmemdup() +
11310f206514SEric Biggers 	 * dma_map_single() not clearly correct, since the DMA API can use
11320f206514SEric Biggers 	 * bounce buffers.  Instead, just use dma_alloc_coherent().  Programming
11330f206514SEric Biggers 	 * keys is normally rare and thus not performance-critical.
11340f206514SEric Biggers 	 */
11350f206514SEric Biggers 
11360f206514SEric Biggers 	keybuf = dma_alloc_coherent(__scm->dev, key_size, &key_phys,
11370f206514SEric Biggers 				    GFP_KERNEL);
11380f206514SEric Biggers 	if (!keybuf)
11390f206514SEric Biggers 		return -ENOMEM;
11400f206514SEric Biggers 	memcpy(keybuf, key, key_size);
11410f206514SEric Biggers 	desc.args[1] = key_phys;
11420f206514SEric Biggers 
11430f206514SEric Biggers 	ret = qcom_scm_call(__scm->dev, &desc, NULL);
11440f206514SEric Biggers 
11450f206514SEric Biggers 	memzero_explicit(keybuf, key_size);
11460f206514SEric Biggers 
11470f206514SEric Biggers 	dma_free_coherent(__scm->dev, key_size, keybuf, key_phys);
11480f206514SEric Biggers 	return ret;
11490f206514SEric Biggers }
11502784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_ice_set_key);
11510f206514SEric Biggers 
11520f206514SEric Biggers /**
115365f0c90bSElliot Berman  * qcom_scm_hdcp_available() - Check if secure environment supports HDCP.
115465f0c90bSElliot Berman  *
115565f0c90bSElliot Berman  * Return true if HDCP is supported, false if not.
115665f0c90bSElliot Berman  */
qcom_scm_hdcp_available(void)115765f0c90bSElliot Berman bool qcom_scm_hdcp_available(void)
115865f0c90bSElliot Berman {
11599d11af8bSStephen Boyd 	bool avail;
116065f0c90bSElliot Berman 	int ret = qcom_scm_clk_enable();
116165f0c90bSElliot Berman 
116265f0c90bSElliot Berman 	if (ret)
116365f0c90bSElliot Berman 		return ret;
116465f0c90bSElliot Berman 
11659d11af8bSStephen Boyd 	avail = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_HDCP,
116665f0c90bSElliot Berman 						QCOM_SCM_HDCP_INVOKE);
116765f0c90bSElliot Berman 
116865f0c90bSElliot Berman 	qcom_scm_clk_disable();
116965f0c90bSElliot Berman 
11709d11af8bSStephen Boyd 	return avail;
117165f0c90bSElliot Berman }
11722784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_hdcp_available);
117365f0c90bSElliot Berman 
117465f0c90bSElliot Berman /**
117565f0c90bSElliot Berman  * qcom_scm_hdcp_req() - Send HDCP request.
117665f0c90bSElliot Berman  * @req: HDCP request array
117765f0c90bSElliot Berman  * @req_cnt: HDCP request array count
117865f0c90bSElliot Berman  * @resp: response buffer passed to SCM
117965f0c90bSElliot Berman  *
118065f0c90bSElliot Berman  * Write HDCP register(s) through SCM.
118165f0c90bSElliot Berman  */
qcom_scm_hdcp_req(struct qcom_scm_hdcp_req * req,u32 req_cnt,u32 * resp)118265f0c90bSElliot Berman int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp)
118365f0c90bSElliot Berman {
118457d3b816SElliot Berman 	int ret;
118557d3b816SElliot Berman 	struct qcom_scm_desc desc = {
118657d3b816SElliot Berman 		.svc = QCOM_SCM_SVC_HDCP,
118757d3b816SElliot Berman 		.cmd = QCOM_SCM_HDCP_INVOKE,
118857d3b816SElliot Berman 		.arginfo = QCOM_SCM_ARGS(10),
118957d3b816SElliot Berman 		.args = {
119057d3b816SElliot Berman 			req[0].addr,
119157d3b816SElliot Berman 			req[0].val,
119257d3b816SElliot Berman 			req[1].addr,
119357d3b816SElliot Berman 			req[1].val,
119457d3b816SElliot Berman 			req[2].addr,
119557d3b816SElliot Berman 			req[2].val,
119657d3b816SElliot Berman 			req[3].addr,
119757d3b816SElliot Berman 			req[3].val,
119857d3b816SElliot Berman 			req[4].addr,
119957d3b816SElliot Berman 			req[4].val
120057d3b816SElliot Berman 		},
120157d3b816SElliot Berman 		.owner = ARM_SMCCC_OWNER_SIP,
120257d3b816SElliot Berman 	};
120357d3b816SElliot Berman 	struct qcom_scm_res res;
120465f0c90bSElliot Berman 
120557d3b816SElliot Berman 	if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT)
120657d3b816SElliot Berman 		return -ERANGE;
120757d3b816SElliot Berman 
120857d3b816SElliot Berman 	ret = qcom_scm_clk_enable();
120965f0c90bSElliot Berman 	if (ret)
121065f0c90bSElliot Berman 		return ret;
121165f0c90bSElliot Berman 
121257d3b816SElliot Berman 	ret = qcom_scm_call(__scm->dev, &desc, &res);
121357d3b816SElliot Berman 	*resp = res.result[0];
121457d3b816SElliot Berman 
121565f0c90bSElliot Berman 	qcom_scm_clk_disable();
121657d3b816SElliot Berman 
121765f0c90bSElliot Berman 	return ret;
121865f0c90bSElliot Berman }
12192784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_hdcp_req);
122065f0c90bSElliot Berman 
qcom_scm_iommu_set_pt_format(u32 sec_id,u32 ctx_num,u32 pt_fmt)1221071a1333SAngeloGioacchino Del Regno int qcom_scm_iommu_set_pt_format(u32 sec_id, u32 ctx_num, u32 pt_fmt)
1222071a1333SAngeloGioacchino Del Regno {
1223071a1333SAngeloGioacchino Del Regno 	struct qcom_scm_desc desc = {
1224071a1333SAngeloGioacchino Del Regno 		.svc = QCOM_SCM_SVC_SMMU_PROGRAM,
1225071a1333SAngeloGioacchino Del Regno 		.cmd = QCOM_SCM_SMMU_PT_FORMAT,
1226071a1333SAngeloGioacchino Del Regno 		.arginfo = QCOM_SCM_ARGS(3),
1227071a1333SAngeloGioacchino Del Regno 		.args[0] = sec_id,
1228071a1333SAngeloGioacchino Del Regno 		.args[1] = ctx_num,
1229071a1333SAngeloGioacchino Del Regno 		.args[2] = pt_fmt, /* 0: LPAE AArch32 - 1: AArch64 */
1230071a1333SAngeloGioacchino Del Regno 		.owner = ARM_SMCCC_OWNER_SIP,
1231071a1333SAngeloGioacchino Del Regno 	};
1232071a1333SAngeloGioacchino Del Regno 
1233071a1333SAngeloGioacchino Del Regno 	return qcom_scm_call(__scm->dev, &desc, NULL);
1234071a1333SAngeloGioacchino Del Regno }
12352784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_iommu_set_pt_format);
1236071a1333SAngeloGioacchino Del Regno 
qcom_scm_qsmmu500_wait_safe_toggle(bool en)123765f0c90bSElliot Berman int qcom_scm_qsmmu500_wait_safe_toggle(bool en)
123865f0c90bSElliot Berman {
123957d3b816SElliot Berman 	struct qcom_scm_desc desc = {
124057d3b816SElliot Berman 		.svc = QCOM_SCM_SVC_SMMU_PROGRAM,
124157d3b816SElliot Berman 		.cmd = QCOM_SCM_SMMU_CONFIG_ERRATA1,
124257d3b816SElliot Berman 		.arginfo = QCOM_SCM_ARGS(2),
124357d3b816SElliot Berman 		.args[0] = QCOM_SCM_SMMU_CONFIG_ERRATA1_CLIENT_ALL,
124457d3b816SElliot Berman 		.args[1] = en,
124557d3b816SElliot Berman 		.owner = ARM_SMCCC_OWNER_SIP,
124657d3b816SElliot Berman 	};
124757d3b816SElliot Berman 
124857d3b816SElliot Berman 
124957d3b816SElliot Berman 	return qcom_scm_call_atomic(__scm->dev, &desc, NULL);
125065f0c90bSElliot Berman }
12512784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_qsmmu500_wait_safe_toggle);
125265f0c90bSElliot Berman 
qcom_scm_lmh_dcvsh_available(void)1253de3438c4SThara Gopinath bool qcom_scm_lmh_dcvsh_available(void)
1254de3438c4SThara Gopinath {
1255de3438c4SThara Gopinath 	return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_LMH, QCOM_SCM_LMH_LIMIT_DCVSH);
1256de3438c4SThara Gopinath }
12572784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_lmh_dcvsh_available);
1258de3438c4SThara Gopinath 
qcom_scm_lmh_profile_change(u32 profile_id)1259de3438c4SThara Gopinath int qcom_scm_lmh_profile_change(u32 profile_id)
1260de3438c4SThara Gopinath {
1261de3438c4SThara Gopinath 	struct qcom_scm_desc desc = {
1262de3438c4SThara Gopinath 		.svc = QCOM_SCM_SVC_LMH,
1263de3438c4SThara Gopinath 		.cmd = QCOM_SCM_LMH_LIMIT_PROFILE_CHANGE,
1264de3438c4SThara Gopinath 		.arginfo = QCOM_SCM_ARGS(1, QCOM_SCM_VAL),
1265de3438c4SThara Gopinath 		.args[0] = profile_id,
1266de3438c4SThara Gopinath 		.owner = ARM_SMCCC_OWNER_SIP,
1267de3438c4SThara Gopinath 	};
1268de3438c4SThara Gopinath 
1269de3438c4SThara Gopinath 	return qcom_scm_call(__scm->dev, &desc, NULL);
1270de3438c4SThara Gopinath }
12712784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_lmh_profile_change);
1272de3438c4SThara Gopinath 
qcom_scm_lmh_dcvsh(u32 payload_fn,u32 payload_reg,u32 payload_val,u64 limit_node,u32 node_id,u64 version)1273de3438c4SThara Gopinath int qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val,
1274de3438c4SThara Gopinath 		       u64 limit_node, u32 node_id, u64 version)
1275de3438c4SThara Gopinath {
1276de3438c4SThara Gopinath 	dma_addr_t payload_phys;
1277de3438c4SThara Gopinath 	u32 *payload_buf;
1278de3438c4SThara Gopinath 	int ret, payload_size = 5 * sizeof(u32);
1279de3438c4SThara Gopinath 
1280de3438c4SThara Gopinath 	struct qcom_scm_desc desc = {
1281de3438c4SThara Gopinath 		.svc = QCOM_SCM_SVC_LMH,
1282de3438c4SThara Gopinath 		.cmd = QCOM_SCM_LMH_LIMIT_DCVSH,
1283de3438c4SThara Gopinath 		.arginfo = QCOM_SCM_ARGS(5, QCOM_SCM_RO, QCOM_SCM_VAL, QCOM_SCM_VAL,
1284de3438c4SThara Gopinath 					QCOM_SCM_VAL, QCOM_SCM_VAL),
1285de3438c4SThara Gopinath 		.args[1] = payload_size,
1286de3438c4SThara Gopinath 		.args[2] = limit_node,
1287de3438c4SThara Gopinath 		.args[3] = node_id,
1288de3438c4SThara Gopinath 		.args[4] = version,
1289de3438c4SThara Gopinath 		.owner = ARM_SMCCC_OWNER_SIP,
1290de3438c4SThara Gopinath 	};
1291de3438c4SThara Gopinath 
1292de3438c4SThara Gopinath 	payload_buf = dma_alloc_coherent(__scm->dev, payload_size, &payload_phys, GFP_KERNEL);
1293de3438c4SThara Gopinath 	if (!payload_buf)
1294de3438c4SThara Gopinath 		return -ENOMEM;
1295de3438c4SThara Gopinath 
1296de3438c4SThara Gopinath 	payload_buf[0] = payload_fn;
1297de3438c4SThara Gopinath 	payload_buf[1] = 0;
1298de3438c4SThara Gopinath 	payload_buf[2] = payload_reg;
1299de3438c4SThara Gopinath 	payload_buf[3] = 1;
1300de3438c4SThara Gopinath 	payload_buf[4] = payload_val;
1301de3438c4SThara Gopinath 
1302de3438c4SThara Gopinath 	desc.args[0] = payload_phys;
1303de3438c4SThara Gopinath 
1304de3438c4SThara Gopinath 	ret = qcom_scm_call(__scm->dev, &desc, NULL);
1305de3438c4SThara Gopinath 
1306de3438c4SThara Gopinath 	dma_free_coherent(__scm->dev, payload_size, payload_buf, payload_phys);
1307de3438c4SThara Gopinath 	return ret;
1308de3438c4SThara Gopinath }
13092784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_lmh_dcvsh);
1310de3438c4SThara Gopinath 
qcom_scm_find_dload_address(struct device * dev,u64 * addr)131165f0c90bSElliot Berman static int qcom_scm_find_dload_address(struct device *dev, u64 *addr)
131265f0c90bSElliot Berman {
131365f0c90bSElliot Berman 	struct device_node *tcsr;
131465f0c90bSElliot Berman 	struct device_node *np = dev->of_node;
131565f0c90bSElliot Berman 	struct resource res;
131665f0c90bSElliot Berman 	u32 offset;
131765f0c90bSElliot Berman 	int ret;
131865f0c90bSElliot Berman 
131965f0c90bSElliot Berman 	tcsr = of_parse_phandle(np, "qcom,dload-mode", 0);
132065f0c90bSElliot Berman 	if (!tcsr)
132165f0c90bSElliot Berman 		return 0;
132265f0c90bSElliot Berman 
132365f0c90bSElliot Berman 	ret = of_address_to_resource(tcsr, 0, &res);
132465f0c90bSElliot Berman 	of_node_put(tcsr);
132565f0c90bSElliot Berman 	if (ret)
132665f0c90bSElliot Berman 		return ret;
132765f0c90bSElliot Berman 
132865f0c90bSElliot Berman 	ret = of_property_read_u32_index(np, "qcom,dload-mode", 1, &offset);
132965f0c90bSElliot Berman 	if (ret < 0)
133065f0c90bSElliot Berman 		return ret;
133165f0c90bSElliot Berman 
133265f0c90bSElliot Berman 	*addr = res.start + offset;
133365f0c90bSElliot Berman 
133465f0c90bSElliot Berman 	return 0;
133565f0c90bSElliot Berman }
133665f0c90bSElliot Berman 
133765f0c90bSElliot Berman /**
133865f0c90bSElliot Berman  * qcom_scm_is_available() - Checks if SCM is available
133965f0c90bSElliot Berman  */
qcom_scm_is_available(void)134065f0c90bSElliot Berman bool qcom_scm_is_available(void)
134165f0c90bSElliot Berman {
1342*b11052c8SKrzysztof Kozlowski 	/* Paired with smp_store_release() in qcom_scm_probe */
1343*b11052c8SKrzysztof Kozlowski 	return !!smp_load_acquire(&__scm);
134465f0c90bSElliot Berman }
13452784e3b0SGuru Das Srinagesh EXPORT_SYMBOL_GPL(qcom_scm_is_available);
134665f0c90bSElliot Berman 
qcom_scm_assert_valid_wq_ctx(u32 wq_ctx)13476bf32599SGuru Das Srinagesh static int qcom_scm_assert_valid_wq_ctx(u32 wq_ctx)
13486bf32599SGuru Das Srinagesh {
13496bf32599SGuru Das Srinagesh 	/* FW currently only supports a single wq_ctx (zero).
13506bf32599SGuru Das Srinagesh 	 * TODO: Update this logic to include dynamic allocation and lookup of
13516bf32599SGuru Das Srinagesh 	 * completion structs when FW supports more wq_ctx values.
13526bf32599SGuru Das Srinagesh 	 */
13536bf32599SGuru Das Srinagesh 	if (wq_ctx != 0) {
13546bf32599SGuru Das Srinagesh 		dev_err(__scm->dev, "Firmware unexpectedly passed non-zero wq_ctx\n");
13556bf32599SGuru Das Srinagesh 		return -EINVAL;
13566bf32599SGuru Das Srinagesh 	}
13576bf32599SGuru Das Srinagesh 
13586bf32599SGuru Das Srinagesh 	return 0;
13596bf32599SGuru Das Srinagesh }
13606bf32599SGuru Das Srinagesh 
qcom_scm_wait_for_wq_completion(u32 wq_ctx)13616bf32599SGuru Das Srinagesh int qcom_scm_wait_for_wq_completion(u32 wq_ctx)
13626bf32599SGuru Das Srinagesh {
13636bf32599SGuru Das Srinagesh 	int ret;
13646bf32599SGuru Das Srinagesh 
13656bf32599SGuru Das Srinagesh 	ret = qcom_scm_assert_valid_wq_ctx(wq_ctx);
13666bf32599SGuru Das Srinagesh 	if (ret)
13676bf32599SGuru Das Srinagesh 		return ret;
13686bf32599SGuru Das Srinagesh 
13696bf32599SGuru Das Srinagesh 	wait_for_completion(&__scm->waitq_comp);
13706bf32599SGuru Das Srinagesh 
13716bf32599SGuru Das Srinagesh 	return 0;
13726bf32599SGuru Das Srinagesh }
13736bf32599SGuru Das Srinagesh 
qcom_scm_waitq_wakeup(struct qcom_scm * scm,unsigned int wq_ctx)13746bf32599SGuru Das Srinagesh static int qcom_scm_waitq_wakeup(struct qcom_scm *scm, unsigned int wq_ctx)
13756bf32599SGuru Das Srinagesh {
13766bf32599SGuru Das Srinagesh 	int ret;
13776bf32599SGuru Das Srinagesh 
13786bf32599SGuru Das Srinagesh 	ret = qcom_scm_assert_valid_wq_ctx(wq_ctx);
13796bf32599SGuru Das Srinagesh 	if (ret)
13806bf32599SGuru Das Srinagesh 		return ret;
13816bf32599SGuru Das Srinagesh 
13826bf32599SGuru Das Srinagesh 	complete(&__scm->waitq_comp);
13836bf32599SGuru Das Srinagesh 
13846bf32599SGuru Das Srinagesh 	return 0;
13856bf32599SGuru Das Srinagesh }
13866bf32599SGuru Das Srinagesh 
qcom_scm_irq_handler(int irq,void * data)13876bf32599SGuru Das Srinagesh static irqreturn_t qcom_scm_irq_handler(int irq, void *data)
13886bf32599SGuru Das Srinagesh {
13896bf32599SGuru Das Srinagesh 	int ret;
13906bf32599SGuru Das Srinagesh 	struct qcom_scm *scm = data;
13916bf32599SGuru Das Srinagesh 	u32 wq_ctx, flags, more_pending = 0;
13926bf32599SGuru Das Srinagesh 
13936bf32599SGuru Das Srinagesh 	do {
13946bf32599SGuru Das Srinagesh 		ret = scm_get_wq_ctx(&wq_ctx, &flags, &more_pending);
13956bf32599SGuru Das Srinagesh 		if (ret) {
13966bf32599SGuru Das Srinagesh 			dev_err(scm->dev, "GET_WQ_CTX SMC call failed: %d\n", ret);
13976bf32599SGuru Das Srinagesh 			goto out;
13986bf32599SGuru Das Srinagesh 		}
13996bf32599SGuru Das Srinagesh 
14006bf32599SGuru Das Srinagesh 		if (flags != QCOM_SMC_WAITQ_FLAG_WAKE_ONE &&
14016bf32599SGuru Das Srinagesh 		    flags != QCOM_SMC_WAITQ_FLAG_WAKE_ALL) {
14026bf32599SGuru Das Srinagesh 			dev_err(scm->dev, "Invalid flags found for wq_ctx: %u\n", flags);
14036bf32599SGuru Das Srinagesh 			goto out;
14046bf32599SGuru Das Srinagesh 		}
14056bf32599SGuru Das Srinagesh 
14066bf32599SGuru Das Srinagesh 		ret = qcom_scm_waitq_wakeup(scm, wq_ctx);
14076bf32599SGuru Das Srinagesh 		if (ret)
14086bf32599SGuru Das Srinagesh 			goto out;
14096bf32599SGuru Das Srinagesh 	} while (more_pending);
14106bf32599SGuru Das Srinagesh 
14116bf32599SGuru Das Srinagesh out:
14126bf32599SGuru Das Srinagesh 	return IRQ_HANDLED;
14136bf32599SGuru Das Srinagesh }
14146bf32599SGuru Das Srinagesh 
qcom_scm_probe(struct platform_device * pdev)1415d0f6fa7bSAndy Gross static int qcom_scm_probe(struct platform_device *pdev)
1416d0f6fa7bSAndy Gross {
1417d0f6fa7bSAndy Gross 	struct qcom_scm *scm;
14186bf32599SGuru Das Srinagesh 	int irq, ret;
1419d0f6fa7bSAndy Gross 
1420d0f6fa7bSAndy Gross 	scm = devm_kzalloc(&pdev->dev, sizeof(*scm), GFP_KERNEL);
1421d0f6fa7bSAndy Gross 	if (!scm)
1422d0f6fa7bSAndy Gross 		return -ENOMEM;
1423d0f6fa7bSAndy Gross 
14240cac3934SMukesh Ojha 	scm->dev = &pdev->dev;
14258c1b7dc9SBjorn Andersson 	ret = qcom_scm_find_dload_address(&pdev->dev, &scm->dload_mode_addr);
14268c1b7dc9SBjorn Andersson 	if (ret < 0)
14278c1b7dc9SBjorn Andersson 		return ret;
14288c1b7dc9SBjorn Andersson 
14290cac3934SMukesh Ojha 	init_completion(&scm->waitq_comp);
143065b7ebdaSSibi Sankar 	mutex_init(&scm->scm_bw_lock);
143165b7ebdaSSibi Sankar 
143265b7ebdaSSibi Sankar 	scm->path = devm_of_icc_get(&pdev->dev, NULL);
143365b7ebdaSSibi Sankar 	if (IS_ERR(scm->path))
143465b7ebdaSSibi Sankar 		return dev_err_probe(&pdev->dev, PTR_ERR(scm->path),
143565b7ebdaSSibi Sankar 				     "failed to acquire interconnect path\n");
143665b7ebdaSSibi Sankar 
14375130464aSKonrad Dybcio 	scm->core_clk = devm_clk_get_optional(&pdev->dev, "core");
1438ae76fd3fSKonrad Dybcio 	if (IS_ERR(scm->core_clk))
143960cd420cSBjorn Andersson 		return PTR_ERR(scm->core_clk);
144060cd420cSBjorn Andersson 
14415130464aSKonrad Dybcio 	scm->iface_clk = devm_clk_get_optional(&pdev->dev, "iface");
1442ae76fd3fSKonrad Dybcio 	if (IS_ERR(scm->iface_clk))
144360cd420cSBjorn Andersson 		return PTR_ERR(scm->iface_clk);
144460cd420cSBjorn Andersson 
14455130464aSKonrad Dybcio 	scm->bus_clk = devm_clk_get_optional(&pdev->dev, "bus");
1446ae76fd3fSKonrad Dybcio 	if (IS_ERR(scm->bus_clk))
144760cd420cSBjorn Andersson 		return PTR_ERR(scm->bus_clk);
1448d0f6fa7bSAndy Gross 
1449dd4fe5b2SBjorn Andersson 	scm->reset.ops = &qcom_scm_pas_reset_ops;
1450dd4fe5b2SBjorn Andersson 	scm->reset.nr_resets = 1;
1451dd4fe5b2SBjorn Andersson 	scm->reset.of_node = pdev->dev.of_node;
1452bd4760caSWei Yongjun 	ret = devm_reset_controller_register(&pdev->dev, &scm->reset);
1453bd4760caSWei Yongjun 	if (ret)
1454bd4760caSWei Yongjun 		return ret;
1455dd4fe5b2SBjorn Andersson 
1456d0f6fa7bSAndy Gross 	/* vote for max clk rate for highest performance */
1457d0f6fa7bSAndy Gross 	ret = clk_set_rate(scm->core_clk, INT_MAX);
1458d0f6fa7bSAndy Gross 	if (ret)
1459d0f6fa7bSAndy Gross 		return ret;
1460d0f6fa7bSAndy Gross 
1461*b11052c8SKrzysztof Kozlowski 	/* Paired with smp_load_acquire() in qcom_scm_is_available(). */
14620cac3934SMukesh Ojha 	smp_store_release(&__scm, scm);
14636bf32599SGuru Das Srinagesh 
1464f3d0fbadSJohan Hovold 	irq = platform_get_irq_optional(pdev, 0);
14656bf32599SGuru Das Srinagesh 	if (irq < 0) {
14666bf32599SGuru Das Srinagesh 		if (irq != -ENXIO)
14676bf32599SGuru Das Srinagesh 			return irq;
14686bf32599SGuru Das Srinagesh 	} else {
14696bf32599SGuru Das Srinagesh 		ret = devm_request_threaded_irq(__scm->dev, irq, NULL, qcom_scm_irq_handler,
14706bf32599SGuru Das Srinagesh 						IRQF_ONESHOT, "qcom-scm", __scm);
14716bf32599SGuru Das Srinagesh 		if (ret < 0)
14726bf32599SGuru Das Srinagesh 			return dev_err_probe(scm->dev, ret, "Failed to request qcom-scm irq\n");
14736bf32599SGuru Das Srinagesh 	}
14746bf32599SGuru Das Srinagesh 
1475f6ea568fSStephen Boyd 	__get_convention();
14766b1751a8SKumar Gala 
14778c1b7dc9SBjorn Andersson 	/*
14788c1b7dc9SBjorn Andersson 	 * If requested enable "download mode", from this point on warmboot
1479c19698a9SJiang Jian 	 * will cause the boot stages to enter download mode, unless
14808c1b7dc9SBjorn Andersson 	 * disabled below by a clean shutdown/reboot.
14818c1b7dc9SBjorn Andersson 	 */
14828c1b7dc9SBjorn Andersson 	if (download_mode)
14838c1b7dc9SBjorn Andersson 		qcom_scm_set_download_mode(true);
14848c1b7dc9SBjorn Andersson 
1485d0f6fa7bSAndy Gross 	return 0;
1486d0f6fa7bSAndy Gross }
1487d0f6fa7bSAndy Gross 
qcom_scm_shutdown(struct platform_device * pdev)14888c1b7dc9SBjorn Andersson static void qcom_scm_shutdown(struct platform_device *pdev)
14898c1b7dc9SBjorn Andersson {
14908c1b7dc9SBjorn Andersson 	/* Clean shutdown, disable download mode to allow normal restart */
14918c1b7dc9SBjorn Andersson 	qcom_scm_set_download_mode(false);
14928c1b7dc9SBjorn Andersson }
14938c1b7dc9SBjorn Andersson 
1494d0f6fa7bSAndy Gross static const struct of_device_id qcom_scm_dt_match[] = {
1495626237dbSKonrad Dybcio 	{ .compatible = "qcom,scm" },
1496626237dbSKonrad Dybcio 
1497626237dbSKonrad Dybcio 	/* Legacy entries kept for backwards compatibility */
14985130464aSKonrad Dybcio 	{ .compatible = "qcom,scm-apq8064" },
14995130464aSKonrad Dybcio 	{ .compatible = "qcom,scm-apq8084" },
150060cd420cSBjorn Andersson 	{ .compatible = "qcom,scm-ipq4019" },
15015130464aSKonrad Dybcio 	{ .compatible = "qcom,scm-msm8953" },
15025130464aSKonrad Dybcio 	{ .compatible = "qcom,scm-msm8974" },
150360cd420cSBjorn Andersson 	{ .compatible = "qcom,scm-msm8996" },
1504d0f6fa7bSAndy Gross 	{}
1505d0f6fa7bSAndy Gross };
1506b42000e4SJohn Stultz MODULE_DEVICE_TABLE(of, qcom_scm_dt_match);
1507d0f6fa7bSAndy Gross 
1508d0f6fa7bSAndy Gross static struct platform_driver qcom_scm_driver = {
1509d0f6fa7bSAndy Gross 	.driver = {
1510d0f6fa7bSAndy Gross 		.name	= "qcom_scm",
1511d0f6fa7bSAndy Gross 		.of_match_table = qcom_scm_dt_match,
151287abf2baSStephen Boyd 		.suppress_bind_attrs = true,
1513d0f6fa7bSAndy Gross 	},
1514d0f6fa7bSAndy Gross 	.probe = qcom_scm_probe,
15158c1b7dc9SBjorn Andersson 	.shutdown = qcom_scm_shutdown,
1516d0f6fa7bSAndy Gross };
1517d0f6fa7bSAndy Gross 
qcom_scm_init(void)1518d0f6fa7bSAndy Gross static int __init qcom_scm_init(void)
1519d0f6fa7bSAndy Gross {
1520d0f6fa7bSAndy Gross 	return platform_driver_register(&qcom_scm_driver);
1521d0f6fa7bSAndy Gross }
15226c8e99d8SAndy Gross subsys_initcall(qcom_scm_init);
1523b42000e4SJohn Stultz 
1524b42000e4SJohn Stultz MODULE_DESCRIPTION("Qualcomm Technologies, Inc. SCM driver");
1525b42000e4SJohn Stultz MODULE_LICENSE("GPL v2");
1526