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