162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* Copyright (c) 2010,2015,2019 The Linux Foundation. All rights reserved.
362306a36Sopenharmony_ci * Copyright (C) 2015 Linaro Ltd.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/platform_device.h>
662306a36Sopenharmony_ci#include <linux/init.h>
762306a36Sopenharmony_ci#include <linux/interrupt.h>
862306a36Sopenharmony_ci#include <linux/completion.h>
962306a36Sopenharmony_ci#include <linux/cpumask.h>
1062306a36Sopenharmony_ci#include <linux/export.h>
1162306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1262306a36Sopenharmony_ci#include <linux/interconnect.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/types.h>
1562306a36Sopenharmony_ci#include <linux/firmware/qcom/qcom_scm.h>
1662306a36Sopenharmony_ci#include <linux/of.h>
1762306a36Sopenharmony_ci#include <linux/of_address.h>
1862306a36Sopenharmony_ci#include <linux/of_irq.h>
1962306a36Sopenharmony_ci#include <linux/of_platform.h>
2062306a36Sopenharmony_ci#include <linux/clk.h>
2162306a36Sopenharmony_ci#include <linux/reset-controller.h>
2262306a36Sopenharmony_ci#include <linux/arm-smccc.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "qcom_scm.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic bool download_mode = IS_ENABLED(CONFIG_QCOM_SCM_DOWNLOAD_MODE_DEFAULT);
2762306a36Sopenharmony_cimodule_param(download_mode, bool, 0);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistruct qcom_scm {
3062306a36Sopenharmony_ci	struct device *dev;
3162306a36Sopenharmony_ci	struct clk *core_clk;
3262306a36Sopenharmony_ci	struct clk *iface_clk;
3362306a36Sopenharmony_ci	struct clk *bus_clk;
3462306a36Sopenharmony_ci	struct icc_path *path;
3562306a36Sopenharmony_ci	struct completion waitq_comp;
3662306a36Sopenharmony_ci	struct reset_controller_dev reset;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	/* control access to the interconnect path */
3962306a36Sopenharmony_ci	struct mutex scm_bw_lock;
4062306a36Sopenharmony_ci	int scm_vote_count;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	u64 dload_mode_addr;
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistruct qcom_scm_current_perm_info {
4662306a36Sopenharmony_ci	__le32 vmid;
4762306a36Sopenharmony_ci	__le32 perm;
4862306a36Sopenharmony_ci	__le64 ctx;
4962306a36Sopenharmony_ci	__le32 ctx_size;
5062306a36Sopenharmony_ci	__le32 unused;
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistruct qcom_scm_mem_map_info {
5462306a36Sopenharmony_ci	__le64 mem_addr;
5562306a36Sopenharmony_ci	__le64 mem_size;
5662306a36Sopenharmony_ci};
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/* Each bit configures cold/warm boot address for one of the 4 CPUs */
5962306a36Sopenharmony_cistatic const u8 qcom_scm_cpu_cold_bits[QCOM_SCM_BOOT_MAX_CPUS] = {
6062306a36Sopenharmony_ci	0, BIT(0), BIT(3), BIT(5)
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_cistatic const u8 qcom_scm_cpu_warm_bits[QCOM_SCM_BOOT_MAX_CPUS] = {
6362306a36Sopenharmony_ci	BIT(2), BIT(1), BIT(4), BIT(6)
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci#define QCOM_SMC_WAITQ_FLAG_WAKE_ONE	BIT(0)
6762306a36Sopenharmony_ci#define QCOM_SMC_WAITQ_FLAG_WAKE_ALL	BIT(1)
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic const char * const qcom_scm_convention_names[] = {
7062306a36Sopenharmony_ci	[SMC_CONVENTION_UNKNOWN] = "unknown",
7162306a36Sopenharmony_ci	[SMC_CONVENTION_ARM_32] = "smc arm 32",
7262306a36Sopenharmony_ci	[SMC_CONVENTION_ARM_64] = "smc arm 64",
7362306a36Sopenharmony_ci	[SMC_CONVENTION_LEGACY] = "smc legacy",
7462306a36Sopenharmony_ci};
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic struct qcom_scm *__scm;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic int qcom_scm_clk_enable(void)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	int ret;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	ret = clk_prepare_enable(__scm->core_clk);
8362306a36Sopenharmony_ci	if (ret)
8462306a36Sopenharmony_ci		goto bail;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	ret = clk_prepare_enable(__scm->iface_clk);
8762306a36Sopenharmony_ci	if (ret)
8862306a36Sopenharmony_ci		goto disable_core;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	ret = clk_prepare_enable(__scm->bus_clk);
9162306a36Sopenharmony_ci	if (ret)
9262306a36Sopenharmony_ci		goto disable_iface;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	return 0;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cidisable_iface:
9762306a36Sopenharmony_ci	clk_disable_unprepare(__scm->iface_clk);
9862306a36Sopenharmony_cidisable_core:
9962306a36Sopenharmony_ci	clk_disable_unprepare(__scm->core_clk);
10062306a36Sopenharmony_cibail:
10162306a36Sopenharmony_ci	return ret;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic void qcom_scm_clk_disable(void)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	clk_disable_unprepare(__scm->core_clk);
10762306a36Sopenharmony_ci	clk_disable_unprepare(__scm->iface_clk);
10862306a36Sopenharmony_ci	clk_disable_unprepare(__scm->bus_clk);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic int qcom_scm_bw_enable(void)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	int ret = 0;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (!__scm->path)
11662306a36Sopenharmony_ci		return 0;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	if (IS_ERR(__scm->path))
11962306a36Sopenharmony_ci		return -EINVAL;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	mutex_lock(&__scm->scm_bw_lock);
12262306a36Sopenharmony_ci	if (!__scm->scm_vote_count) {
12362306a36Sopenharmony_ci		ret = icc_set_bw(__scm->path, 0, UINT_MAX);
12462306a36Sopenharmony_ci		if (ret < 0) {
12562306a36Sopenharmony_ci			dev_err(__scm->dev, "failed to set bandwidth request\n");
12662306a36Sopenharmony_ci			goto err_bw;
12762306a36Sopenharmony_ci		}
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci	__scm->scm_vote_count++;
13062306a36Sopenharmony_cierr_bw:
13162306a36Sopenharmony_ci	mutex_unlock(&__scm->scm_bw_lock);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	return ret;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic void qcom_scm_bw_disable(void)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(__scm->path))
13962306a36Sopenharmony_ci		return;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	mutex_lock(&__scm->scm_bw_lock);
14262306a36Sopenharmony_ci	if (__scm->scm_vote_count-- == 1)
14362306a36Sopenharmony_ci		icc_set_bw(__scm->path, 0, 0);
14462306a36Sopenharmony_ci	mutex_unlock(&__scm->scm_bw_lock);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cienum qcom_scm_convention qcom_scm_convention = SMC_CONVENTION_UNKNOWN;
14862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(scm_query_lock);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic enum qcom_scm_convention __get_convention(void)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	unsigned long flags;
15362306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
15462306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_INFO,
15562306a36Sopenharmony_ci		.cmd = QCOM_SCM_INFO_IS_CALL_AVAIL,
15662306a36Sopenharmony_ci		.args[0] = SCM_SMC_FNID(QCOM_SCM_SVC_INFO,
15762306a36Sopenharmony_ci					   QCOM_SCM_INFO_IS_CALL_AVAIL) |
15862306a36Sopenharmony_ci			   (ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT),
15962306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(1),
16062306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
16162306a36Sopenharmony_ci	};
16262306a36Sopenharmony_ci	struct qcom_scm_res res;
16362306a36Sopenharmony_ci	enum qcom_scm_convention probed_convention;
16462306a36Sopenharmony_ci	int ret;
16562306a36Sopenharmony_ci	bool forced = false;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (likely(qcom_scm_convention != SMC_CONVENTION_UNKNOWN))
16862306a36Sopenharmony_ci		return qcom_scm_convention;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	/*
17162306a36Sopenharmony_ci	 * Per the "SMC calling convention specification", the 64-bit calling
17262306a36Sopenharmony_ci	 * convention can only be used when the client is 64-bit, otherwise
17362306a36Sopenharmony_ci	 * system will encounter the undefined behaviour.
17462306a36Sopenharmony_ci	 */
17562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_ARM64)
17662306a36Sopenharmony_ci	/*
17762306a36Sopenharmony_ci	 * Device isn't required as there is only one argument - no device
17862306a36Sopenharmony_ci	 * needed to dma_map_single to secure world
17962306a36Sopenharmony_ci	 */
18062306a36Sopenharmony_ci	probed_convention = SMC_CONVENTION_ARM_64;
18162306a36Sopenharmony_ci	ret = __scm_smc_call(NULL, &desc, probed_convention, &res, true);
18262306a36Sopenharmony_ci	if (!ret && res.result[0] == 1)
18362306a36Sopenharmony_ci		goto found;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	/*
18662306a36Sopenharmony_ci	 * Some SC7180 firmwares didn't implement the
18762306a36Sopenharmony_ci	 * QCOM_SCM_INFO_IS_CALL_AVAIL call, so we fallback to forcing ARM_64
18862306a36Sopenharmony_ci	 * calling conventions on these firmwares. Luckily we don't make any
18962306a36Sopenharmony_ci	 * early calls into the firmware on these SoCs so the device pointer
19062306a36Sopenharmony_ci	 * will be valid here to check if the compatible matches.
19162306a36Sopenharmony_ci	 */
19262306a36Sopenharmony_ci	if (of_device_is_compatible(__scm ? __scm->dev->of_node : NULL, "qcom,scm-sc7180")) {
19362306a36Sopenharmony_ci		forced = true;
19462306a36Sopenharmony_ci		goto found;
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci#endif
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	probed_convention = SMC_CONVENTION_ARM_32;
19962306a36Sopenharmony_ci	ret = __scm_smc_call(NULL, &desc, probed_convention, &res, true);
20062306a36Sopenharmony_ci	if (!ret && res.result[0] == 1)
20162306a36Sopenharmony_ci		goto found;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	probed_convention = SMC_CONVENTION_LEGACY;
20462306a36Sopenharmony_cifound:
20562306a36Sopenharmony_ci	spin_lock_irqsave(&scm_query_lock, flags);
20662306a36Sopenharmony_ci	if (probed_convention != qcom_scm_convention) {
20762306a36Sopenharmony_ci		qcom_scm_convention = probed_convention;
20862306a36Sopenharmony_ci		pr_info("qcom_scm: convention: %s%s\n",
20962306a36Sopenharmony_ci			qcom_scm_convention_names[qcom_scm_convention],
21062306a36Sopenharmony_ci			forced ? " (forced)" : "");
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci	spin_unlock_irqrestore(&scm_query_lock, flags);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	return qcom_scm_convention;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci/**
21862306a36Sopenharmony_ci * qcom_scm_call() - Invoke a syscall in the secure world
21962306a36Sopenharmony_ci * @dev:	device
22062306a36Sopenharmony_ci * @desc:	Descriptor structure containing arguments and return values
22162306a36Sopenharmony_ci * @res:        Structure containing results from SMC/HVC call
22262306a36Sopenharmony_ci *
22362306a36Sopenharmony_ci * Sends a command to the SCM and waits for the command to finish processing.
22462306a36Sopenharmony_ci * This should *only* be called in pre-emptible context.
22562306a36Sopenharmony_ci */
22662306a36Sopenharmony_cistatic int qcom_scm_call(struct device *dev, const struct qcom_scm_desc *desc,
22762306a36Sopenharmony_ci			 struct qcom_scm_res *res)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	might_sleep();
23062306a36Sopenharmony_ci	switch (__get_convention()) {
23162306a36Sopenharmony_ci	case SMC_CONVENTION_ARM_32:
23262306a36Sopenharmony_ci	case SMC_CONVENTION_ARM_64:
23362306a36Sopenharmony_ci		return scm_smc_call(dev, desc, res, false);
23462306a36Sopenharmony_ci	case SMC_CONVENTION_LEGACY:
23562306a36Sopenharmony_ci		return scm_legacy_call(dev, desc, res);
23662306a36Sopenharmony_ci	default:
23762306a36Sopenharmony_ci		pr_err("Unknown current SCM calling convention.\n");
23862306a36Sopenharmony_ci		return -EINVAL;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci/**
24362306a36Sopenharmony_ci * qcom_scm_call_atomic() - atomic variation of qcom_scm_call()
24462306a36Sopenharmony_ci * @dev:	device
24562306a36Sopenharmony_ci * @desc:	Descriptor structure containing arguments and return values
24662306a36Sopenharmony_ci * @res:	Structure containing results from SMC/HVC call
24762306a36Sopenharmony_ci *
24862306a36Sopenharmony_ci * Sends a command to the SCM and waits for the command to finish processing.
24962306a36Sopenharmony_ci * This can be called in atomic context.
25062306a36Sopenharmony_ci */
25162306a36Sopenharmony_cistatic int qcom_scm_call_atomic(struct device *dev,
25262306a36Sopenharmony_ci				const struct qcom_scm_desc *desc,
25362306a36Sopenharmony_ci				struct qcom_scm_res *res)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	switch (__get_convention()) {
25662306a36Sopenharmony_ci	case SMC_CONVENTION_ARM_32:
25762306a36Sopenharmony_ci	case SMC_CONVENTION_ARM_64:
25862306a36Sopenharmony_ci		return scm_smc_call(dev, desc, res, true);
25962306a36Sopenharmony_ci	case SMC_CONVENTION_LEGACY:
26062306a36Sopenharmony_ci		return scm_legacy_call_atomic(dev, desc, res);
26162306a36Sopenharmony_ci	default:
26262306a36Sopenharmony_ci		pr_err("Unknown current SCM calling convention.\n");
26362306a36Sopenharmony_ci		return -EINVAL;
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic bool __qcom_scm_is_call_available(struct device *dev, u32 svc_id,
26862306a36Sopenharmony_ci					 u32 cmd_id)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	int ret;
27162306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
27262306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_INFO,
27362306a36Sopenharmony_ci		.cmd = QCOM_SCM_INFO_IS_CALL_AVAIL,
27462306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
27562306a36Sopenharmony_ci	};
27662306a36Sopenharmony_ci	struct qcom_scm_res res;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	desc.arginfo = QCOM_SCM_ARGS(1);
27962306a36Sopenharmony_ci	switch (__get_convention()) {
28062306a36Sopenharmony_ci	case SMC_CONVENTION_ARM_32:
28162306a36Sopenharmony_ci	case SMC_CONVENTION_ARM_64:
28262306a36Sopenharmony_ci		desc.args[0] = SCM_SMC_FNID(svc_id, cmd_id) |
28362306a36Sopenharmony_ci				(ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT);
28462306a36Sopenharmony_ci		break;
28562306a36Sopenharmony_ci	case SMC_CONVENTION_LEGACY:
28662306a36Sopenharmony_ci		desc.args[0] = SCM_LEGACY_FNID(svc_id, cmd_id);
28762306a36Sopenharmony_ci		break;
28862306a36Sopenharmony_ci	default:
28962306a36Sopenharmony_ci		pr_err("Unknown SMC convention being used\n");
29062306a36Sopenharmony_ci		return false;
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	ret = qcom_scm_call(dev, &desc, &res);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	return ret ? false : !!res.result[0];
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic int qcom_scm_set_boot_addr(void *entry, const u8 *cpu_bits)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	int cpu;
30162306a36Sopenharmony_ci	unsigned int flags = 0;
30262306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
30362306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_BOOT,
30462306a36Sopenharmony_ci		.cmd = QCOM_SCM_BOOT_SET_ADDR,
30562306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(2),
30662306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
30762306a36Sopenharmony_ci	};
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	for_each_present_cpu(cpu) {
31062306a36Sopenharmony_ci		if (cpu >= QCOM_SCM_BOOT_MAX_CPUS)
31162306a36Sopenharmony_ci			return -EINVAL;
31262306a36Sopenharmony_ci		flags |= cpu_bits[cpu];
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	desc.args[0] = flags;
31662306a36Sopenharmony_ci	desc.args[1] = virt_to_phys(entry);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	return qcom_scm_call_atomic(__scm ? __scm->dev : NULL, &desc, NULL);
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic int qcom_scm_set_boot_addr_mc(void *entry, unsigned int flags)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
32462306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_BOOT,
32562306a36Sopenharmony_ci		.cmd = QCOM_SCM_BOOT_SET_ADDR_MC,
32662306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
32762306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(6),
32862306a36Sopenharmony_ci		.args = {
32962306a36Sopenharmony_ci			virt_to_phys(entry),
33062306a36Sopenharmony_ci			/* Apply to all CPUs in all affinity levels */
33162306a36Sopenharmony_ci			~0ULL, ~0ULL, ~0ULL, ~0ULL,
33262306a36Sopenharmony_ci			flags,
33362306a36Sopenharmony_ci		},
33462306a36Sopenharmony_ci	};
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	/* Need a device for DMA of the additional arguments */
33762306a36Sopenharmony_ci	if (!__scm || __get_convention() == SMC_CONVENTION_LEGACY)
33862306a36Sopenharmony_ci		return -EOPNOTSUPP;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	return qcom_scm_call(__scm->dev, &desc, NULL);
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci/**
34462306a36Sopenharmony_ci * qcom_scm_set_warm_boot_addr() - Set the warm boot address for all cpus
34562306a36Sopenharmony_ci * @entry: Entry point function for the cpus
34662306a36Sopenharmony_ci *
34762306a36Sopenharmony_ci * Set the Linux entry point for the SCM to transfer control to when coming
34862306a36Sopenharmony_ci * out of a power down. CPU power down may be executed on cpuidle or hotplug.
34962306a36Sopenharmony_ci */
35062306a36Sopenharmony_ciint qcom_scm_set_warm_boot_addr(void *entry)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	if (qcom_scm_set_boot_addr_mc(entry, QCOM_SCM_BOOT_MC_FLAG_WARMBOOT))
35362306a36Sopenharmony_ci		/* Fallback to old SCM call */
35462306a36Sopenharmony_ci		return qcom_scm_set_boot_addr(entry, qcom_scm_cpu_warm_bits);
35562306a36Sopenharmony_ci	return 0;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_set_warm_boot_addr);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci/**
36062306a36Sopenharmony_ci * qcom_scm_set_cold_boot_addr() - Set the cold boot address for all cpus
36162306a36Sopenharmony_ci * @entry: Entry point function for the cpus
36262306a36Sopenharmony_ci */
36362306a36Sopenharmony_ciint qcom_scm_set_cold_boot_addr(void *entry)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	if (qcom_scm_set_boot_addr_mc(entry, QCOM_SCM_BOOT_MC_FLAG_COLDBOOT))
36662306a36Sopenharmony_ci		/* Fallback to old SCM call */
36762306a36Sopenharmony_ci		return qcom_scm_set_boot_addr(entry, qcom_scm_cpu_cold_bits);
36862306a36Sopenharmony_ci	return 0;
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_set_cold_boot_addr);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci/**
37362306a36Sopenharmony_ci * qcom_scm_cpu_power_down() - Power down the cpu
37462306a36Sopenharmony_ci * @flags:	Flags to flush cache
37562306a36Sopenharmony_ci *
37662306a36Sopenharmony_ci * This is an end point to power down cpu. If there was a pending interrupt,
37762306a36Sopenharmony_ci * the control would return from this function, otherwise, the cpu jumps to the
37862306a36Sopenharmony_ci * warm boot entry point set for this cpu upon reset.
37962306a36Sopenharmony_ci */
38062306a36Sopenharmony_civoid qcom_scm_cpu_power_down(u32 flags)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
38362306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_BOOT,
38462306a36Sopenharmony_ci		.cmd = QCOM_SCM_BOOT_TERMINATE_PC,
38562306a36Sopenharmony_ci		.args[0] = flags & QCOM_SCM_FLUSH_FLAG_MASK,
38662306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(1),
38762306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
38862306a36Sopenharmony_ci	};
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	qcom_scm_call_atomic(__scm ? __scm->dev : NULL, &desc, NULL);
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_cpu_power_down);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ciint qcom_scm_set_remote_state(u32 state, u32 id)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
39762306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_BOOT,
39862306a36Sopenharmony_ci		.cmd = QCOM_SCM_BOOT_SET_REMOTE_STATE,
39962306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(2),
40062306a36Sopenharmony_ci		.args[0] = state,
40162306a36Sopenharmony_ci		.args[1] = id,
40262306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
40362306a36Sopenharmony_ci	};
40462306a36Sopenharmony_ci	struct qcom_scm_res res;
40562306a36Sopenharmony_ci	int ret;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	ret = qcom_scm_call(__scm->dev, &desc, &res);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	return ret ? : res.result[0];
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_set_remote_state);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic int __qcom_scm_set_dload_mode(struct device *dev, bool enable)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
41662306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_BOOT,
41762306a36Sopenharmony_ci		.cmd = QCOM_SCM_BOOT_SET_DLOAD_MODE,
41862306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(2),
41962306a36Sopenharmony_ci		.args[0] = QCOM_SCM_BOOT_SET_DLOAD_MODE,
42062306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
42162306a36Sopenharmony_ci	};
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	desc.args[1] = enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	return qcom_scm_call_atomic(__scm->dev, &desc, NULL);
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic void qcom_scm_set_download_mode(bool enable)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	bool avail;
43162306a36Sopenharmony_ci	int ret = 0;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	avail = __qcom_scm_is_call_available(__scm->dev,
43462306a36Sopenharmony_ci					     QCOM_SCM_SVC_BOOT,
43562306a36Sopenharmony_ci					     QCOM_SCM_BOOT_SET_DLOAD_MODE);
43662306a36Sopenharmony_ci	if (avail) {
43762306a36Sopenharmony_ci		ret = __qcom_scm_set_dload_mode(__scm->dev, enable);
43862306a36Sopenharmony_ci	} else if (__scm->dload_mode_addr) {
43962306a36Sopenharmony_ci		ret = qcom_scm_io_writel(__scm->dload_mode_addr,
44062306a36Sopenharmony_ci				enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0);
44162306a36Sopenharmony_ci	} else {
44262306a36Sopenharmony_ci		dev_err(__scm->dev,
44362306a36Sopenharmony_ci			"No available mechanism for setting download mode\n");
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	if (ret)
44762306a36Sopenharmony_ci		dev_err(__scm->dev, "failed to set download mode: %d\n", ret);
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci/**
45162306a36Sopenharmony_ci * qcom_scm_pas_init_image() - Initialize peripheral authentication service
45262306a36Sopenharmony_ci *			       state machine for a given peripheral, using the
45362306a36Sopenharmony_ci *			       metadata
45462306a36Sopenharmony_ci * @peripheral: peripheral id
45562306a36Sopenharmony_ci * @metadata:	pointer to memory containing ELF header, program header table
45662306a36Sopenharmony_ci *		and optional blob of data used for authenticating the metadata
45762306a36Sopenharmony_ci *		and the rest of the firmware
45862306a36Sopenharmony_ci * @size:	size of the metadata
45962306a36Sopenharmony_ci * @ctx:	optional metadata context
46062306a36Sopenharmony_ci *
46162306a36Sopenharmony_ci * Return: 0 on success.
46262306a36Sopenharmony_ci *
46362306a36Sopenharmony_ci * Upon successful return, the PAS metadata context (@ctx) will be used to
46462306a36Sopenharmony_ci * track the metadata allocation, this needs to be released by invoking
46562306a36Sopenharmony_ci * qcom_scm_pas_metadata_release() by the caller.
46662306a36Sopenharmony_ci */
46762306a36Sopenharmony_ciint qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size,
46862306a36Sopenharmony_ci			    struct qcom_scm_pas_metadata *ctx)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	dma_addr_t mdata_phys;
47162306a36Sopenharmony_ci	void *mdata_buf;
47262306a36Sopenharmony_ci	int ret;
47362306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
47462306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_PIL,
47562306a36Sopenharmony_ci		.cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE,
47662306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW),
47762306a36Sopenharmony_ci		.args[0] = peripheral,
47862306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
47962306a36Sopenharmony_ci	};
48062306a36Sopenharmony_ci	struct qcom_scm_res res;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	/*
48362306a36Sopenharmony_ci	 * During the scm call memory protection will be enabled for the meta
48462306a36Sopenharmony_ci	 * data blob, so make sure it's physically contiguous, 4K aligned and
48562306a36Sopenharmony_ci	 * non-cachable to avoid XPU violations.
48662306a36Sopenharmony_ci	 */
48762306a36Sopenharmony_ci	mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys,
48862306a36Sopenharmony_ci				       GFP_KERNEL);
48962306a36Sopenharmony_ci	if (!mdata_buf) {
49062306a36Sopenharmony_ci		dev_err(__scm->dev, "Allocation of metadata buffer failed.\n");
49162306a36Sopenharmony_ci		return -ENOMEM;
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci	memcpy(mdata_buf, metadata, size);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	ret = qcom_scm_clk_enable();
49662306a36Sopenharmony_ci	if (ret)
49762306a36Sopenharmony_ci		goto out;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	ret = qcom_scm_bw_enable();
50062306a36Sopenharmony_ci	if (ret)
50162306a36Sopenharmony_ci		return ret;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	desc.args[1] = mdata_phys;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	ret = qcom_scm_call(__scm->dev, &desc, &res);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	qcom_scm_bw_disable();
50862306a36Sopenharmony_ci	qcom_scm_clk_disable();
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ciout:
51162306a36Sopenharmony_ci	if (ret < 0 || !ctx) {
51262306a36Sopenharmony_ci		dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys);
51362306a36Sopenharmony_ci	} else if (ctx) {
51462306a36Sopenharmony_ci		ctx->ptr = mdata_buf;
51562306a36Sopenharmony_ci		ctx->phys = mdata_phys;
51662306a36Sopenharmony_ci		ctx->size = size;
51762306a36Sopenharmony_ci	}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	return ret ? : res.result[0];
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_pas_init_image);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci/**
52462306a36Sopenharmony_ci * qcom_scm_pas_metadata_release() - release metadata context
52562306a36Sopenharmony_ci * @ctx:	metadata context
52662306a36Sopenharmony_ci */
52762306a36Sopenharmony_civoid qcom_scm_pas_metadata_release(struct qcom_scm_pas_metadata *ctx)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	if (!ctx->ptr)
53062306a36Sopenharmony_ci		return;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	dma_free_coherent(__scm->dev, ctx->size, ctx->ptr, ctx->phys);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	ctx->ptr = NULL;
53562306a36Sopenharmony_ci	ctx->phys = 0;
53662306a36Sopenharmony_ci	ctx->size = 0;
53762306a36Sopenharmony_ci}
53862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_pas_metadata_release);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci/**
54162306a36Sopenharmony_ci * qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral
54262306a36Sopenharmony_ci *			      for firmware loading
54362306a36Sopenharmony_ci * @peripheral:	peripheral id
54462306a36Sopenharmony_ci * @addr:	start address of memory area to prepare
54562306a36Sopenharmony_ci * @size:	size of the memory area to prepare
54662306a36Sopenharmony_ci *
54762306a36Sopenharmony_ci * Returns 0 on success.
54862306a36Sopenharmony_ci */
54962306a36Sopenharmony_ciint qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	int ret;
55262306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
55362306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_PIL,
55462306a36Sopenharmony_ci		.cmd = QCOM_SCM_PIL_PAS_MEM_SETUP,
55562306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(3),
55662306a36Sopenharmony_ci		.args[0] = peripheral,
55762306a36Sopenharmony_ci		.args[1] = addr,
55862306a36Sopenharmony_ci		.args[2] = size,
55962306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
56062306a36Sopenharmony_ci	};
56162306a36Sopenharmony_ci	struct qcom_scm_res res;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	ret = qcom_scm_clk_enable();
56462306a36Sopenharmony_ci	if (ret)
56562306a36Sopenharmony_ci		return ret;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	ret = qcom_scm_bw_enable();
56862306a36Sopenharmony_ci	if (ret)
56962306a36Sopenharmony_ci		return ret;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	ret = qcom_scm_call(__scm->dev, &desc, &res);
57262306a36Sopenharmony_ci	qcom_scm_bw_disable();
57362306a36Sopenharmony_ci	qcom_scm_clk_disable();
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	return ret ? : res.result[0];
57662306a36Sopenharmony_ci}
57762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_pas_mem_setup);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci/**
58062306a36Sopenharmony_ci * qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware
58162306a36Sopenharmony_ci *				   and reset the remote processor
58262306a36Sopenharmony_ci * @peripheral:	peripheral id
58362306a36Sopenharmony_ci *
58462306a36Sopenharmony_ci * Return 0 on success.
58562306a36Sopenharmony_ci */
58662306a36Sopenharmony_ciint qcom_scm_pas_auth_and_reset(u32 peripheral)
58762306a36Sopenharmony_ci{
58862306a36Sopenharmony_ci	int ret;
58962306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
59062306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_PIL,
59162306a36Sopenharmony_ci		.cmd = QCOM_SCM_PIL_PAS_AUTH_AND_RESET,
59262306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(1),
59362306a36Sopenharmony_ci		.args[0] = peripheral,
59462306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
59562306a36Sopenharmony_ci	};
59662306a36Sopenharmony_ci	struct qcom_scm_res res;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	ret = qcom_scm_clk_enable();
59962306a36Sopenharmony_ci	if (ret)
60062306a36Sopenharmony_ci		return ret;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	ret = qcom_scm_bw_enable();
60362306a36Sopenharmony_ci	if (ret)
60462306a36Sopenharmony_ci		return ret;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	ret = qcom_scm_call(__scm->dev, &desc, &res);
60762306a36Sopenharmony_ci	qcom_scm_bw_disable();
60862306a36Sopenharmony_ci	qcom_scm_clk_disable();
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	return ret ? : res.result[0];
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_pas_auth_and_reset);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci/**
61562306a36Sopenharmony_ci * qcom_scm_pas_shutdown() - Shut down the remote processor
61662306a36Sopenharmony_ci * @peripheral: peripheral id
61762306a36Sopenharmony_ci *
61862306a36Sopenharmony_ci * Returns 0 on success.
61962306a36Sopenharmony_ci */
62062306a36Sopenharmony_ciint qcom_scm_pas_shutdown(u32 peripheral)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	int ret;
62362306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
62462306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_PIL,
62562306a36Sopenharmony_ci		.cmd = QCOM_SCM_PIL_PAS_SHUTDOWN,
62662306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(1),
62762306a36Sopenharmony_ci		.args[0] = peripheral,
62862306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
62962306a36Sopenharmony_ci	};
63062306a36Sopenharmony_ci	struct qcom_scm_res res;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	ret = qcom_scm_clk_enable();
63362306a36Sopenharmony_ci	if (ret)
63462306a36Sopenharmony_ci		return ret;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	ret = qcom_scm_bw_enable();
63762306a36Sopenharmony_ci	if (ret)
63862306a36Sopenharmony_ci		return ret;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	ret = qcom_scm_call(__scm->dev, &desc, &res);
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	qcom_scm_bw_disable();
64362306a36Sopenharmony_ci	qcom_scm_clk_disable();
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	return ret ? : res.result[0];
64662306a36Sopenharmony_ci}
64762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_pas_shutdown);
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci/**
65062306a36Sopenharmony_ci * qcom_scm_pas_supported() - Check if the peripheral authentication service is
65162306a36Sopenharmony_ci *			      available for the given peripherial
65262306a36Sopenharmony_ci * @peripheral:	peripheral id
65362306a36Sopenharmony_ci *
65462306a36Sopenharmony_ci * Returns true if PAS is supported for this peripheral, otherwise false.
65562306a36Sopenharmony_ci */
65662306a36Sopenharmony_cibool qcom_scm_pas_supported(u32 peripheral)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	int ret;
65962306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
66062306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_PIL,
66162306a36Sopenharmony_ci		.cmd = QCOM_SCM_PIL_PAS_IS_SUPPORTED,
66262306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(1),
66362306a36Sopenharmony_ci		.args[0] = peripheral,
66462306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
66562306a36Sopenharmony_ci	};
66662306a36Sopenharmony_ci	struct qcom_scm_res res;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
66962306a36Sopenharmony_ci					  QCOM_SCM_PIL_PAS_IS_SUPPORTED))
67062306a36Sopenharmony_ci		return false;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	ret = qcom_scm_call(__scm->dev, &desc, &res);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	return ret ? false : !!res.result[0];
67562306a36Sopenharmony_ci}
67662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_pas_supported);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_cistatic int __qcom_scm_pas_mss_reset(struct device *dev, bool reset)
67962306a36Sopenharmony_ci{
68062306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
68162306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_PIL,
68262306a36Sopenharmony_ci		.cmd = QCOM_SCM_PIL_PAS_MSS_RESET,
68362306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(2),
68462306a36Sopenharmony_ci		.args[0] = reset,
68562306a36Sopenharmony_ci		.args[1] = 0,
68662306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
68762306a36Sopenharmony_ci	};
68862306a36Sopenharmony_ci	struct qcom_scm_res res;
68962306a36Sopenharmony_ci	int ret;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	ret = qcom_scm_call(__scm->dev, &desc, &res);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	return ret ? : res.result[0];
69462306a36Sopenharmony_ci}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_cistatic int qcom_scm_pas_reset_assert(struct reset_controller_dev *rcdev,
69762306a36Sopenharmony_ci				     unsigned long idx)
69862306a36Sopenharmony_ci{
69962306a36Sopenharmony_ci	if (idx != 0)
70062306a36Sopenharmony_ci		return -EINVAL;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	return __qcom_scm_pas_mss_reset(__scm->dev, 1);
70362306a36Sopenharmony_ci}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_cistatic int qcom_scm_pas_reset_deassert(struct reset_controller_dev *rcdev,
70662306a36Sopenharmony_ci				       unsigned long idx)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	if (idx != 0)
70962306a36Sopenharmony_ci		return -EINVAL;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	return __qcom_scm_pas_mss_reset(__scm->dev, 0);
71262306a36Sopenharmony_ci}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_cistatic const struct reset_control_ops qcom_scm_pas_reset_ops = {
71562306a36Sopenharmony_ci	.assert = qcom_scm_pas_reset_assert,
71662306a36Sopenharmony_ci	.deassert = qcom_scm_pas_reset_deassert,
71762306a36Sopenharmony_ci};
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ciint qcom_scm_io_readl(phys_addr_t addr, unsigned int *val)
72062306a36Sopenharmony_ci{
72162306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
72262306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_IO,
72362306a36Sopenharmony_ci		.cmd = QCOM_SCM_IO_READ,
72462306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(1),
72562306a36Sopenharmony_ci		.args[0] = addr,
72662306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
72762306a36Sopenharmony_ci	};
72862306a36Sopenharmony_ci	struct qcom_scm_res res;
72962306a36Sopenharmony_ci	int ret;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	ret = qcom_scm_call_atomic(__scm->dev, &desc, &res);
73362306a36Sopenharmony_ci	if (ret >= 0)
73462306a36Sopenharmony_ci		*val = res.result[0];
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	return ret < 0 ? ret : 0;
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_io_readl);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ciint qcom_scm_io_writel(phys_addr_t addr, unsigned int val)
74162306a36Sopenharmony_ci{
74262306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
74362306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_IO,
74462306a36Sopenharmony_ci		.cmd = QCOM_SCM_IO_WRITE,
74562306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(2),
74662306a36Sopenharmony_ci		.args[0] = addr,
74762306a36Sopenharmony_ci		.args[1] = val,
74862306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
74962306a36Sopenharmony_ci	};
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	return qcom_scm_call_atomic(__scm->dev, &desc, NULL);
75262306a36Sopenharmony_ci}
75362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_io_writel);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci/**
75662306a36Sopenharmony_ci * qcom_scm_restore_sec_cfg_available() - Check if secure environment
75762306a36Sopenharmony_ci * supports restore security config interface.
75862306a36Sopenharmony_ci *
75962306a36Sopenharmony_ci * Return true if restore-cfg interface is supported, false if not.
76062306a36Sopenharmony_ci */
76162306a36Sopenharmony_cibool qcom_scm_restore_sec_cfg_available(void)
76262306a36Sopenharmony_ci{
76362306a36Sopenharmony_ci	return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_MP,
76462306a36Sopenharmony_ci					    QCOM_SCM_MP_RESTORE_SEC_CFG);
76562306a36Sopenharmony_ci}
76662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_restore_sec_cfg_available);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ciint qcom_scm_restore_sec_cfg(u32 device_id, u32 spare)
76962306a36Sopenharmony_ci{
77062306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
77162306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_MP,
77262306a36Sopenharmony_ci		.cmd = QCOM_SCM_MP_RESTORE_SEC_CFG,
77362306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(2),
77462306a36Sopenharmony_ci		.args[0] = device_id,
77562306a36Sopenharmony_ci		.args[1] = spare,
77662306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
77762306a36Sopenharmony_ci	};
77862306a36Sopenharmony_ci	struct qcom_scm_res res;
77962306a36Sopenharmony_ci	int ret;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	ret = qcom_scm_call(__scm->dev, &desc, &res);
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	return ret ? : res.result[0];
78462306a36Sopenharmony_ci}
78562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_restore_sec_cfg);
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ciint qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size)
78862306a36Sopenharmony_ci{
78962306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
79062306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_MP,
79162306a36Sopenharmony_ci		.cmd = QCOM_SCM_MP_IOMMU_SECURE_PTBL_SIZE,
79262306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(1),
79362306a36Sopenharmony_ci		.args[0] = spare,
79462306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
79562306a36Sopenharmony_ci	};
79662306a36Sopenharmony_ci	struct qcom_scm_res res;
79762306a36Sopenharmony_ci	int ret;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	ret = qcom_scm_call(__scm->dev, &desc, &res);
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	if (size)
80262306a36Sopenharmony_ci		*size = res.result[0];
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	return ret ? : res.result[1];
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_iommu_secure_ptbl_size);
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ciint qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare)
80962306a36Sopenharmony_ci{
81062306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
81162306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_MP,
81262306a36Sopenharmony_ci		.cmd = QCOM_SCM_MP_IOMMU_SECURE_PTBL_INIT,
81362306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(3, QCOM_SCM_RW, QCOM_SCM_VAL,
81462306a36Sopenharmony_ci					 QCOM_SCM_VAL),
81562306a36Sopenharmony_ci		.args[0] = addr,
81662306a36Sopenharmony_ci		.args[1] = size,
81762306a36Sopenharmony_ci		.args[2] = spare,
81862306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
81962306a36Sopenharmony_ci	};
82062306a36Sopenharmony_ci	int ret;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	ret = qcom_scm_call(__scm->dev, &desc, NULL);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	/* the pg table has been initialized already, ignore the error */
82562306a36Sopenharmony_ci	if (ret == -EPERM)
82662306a36Sopenharmony_ci		ret = 0;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	return ret;
82962306a36Sopenharmony_ci}
83062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_iommu_secure_ptbl_init);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ciint qcom_scm_iommu_set_cp_pool_size(u32 spare, u32 size)
83362306a36Sopenharmony_ci{
83462306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
83562306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_MP,
83662306a36Sopenharmony_ci		.cmd = QCOM_SCM_MP_IOMMU_SET_CP_POOL_SIZE,
83762306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(2),
83862306a36Sopenharmony_ci		.args[0] = size,
83962306a36Sopenharmony_ci		.args[1] = spare,
84062306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
84162306a36Sopenharmony_ci	};
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	return qcom_scm_call(__scm->dev, &desc, NULL);
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_iommu_set_cp_pool_size);
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ciint qcom_scm_mem_protect_video_var(u32 cp_start, u32 cp_size,
84862306a36Sopenharmony_ci				   u32 cp_nonpixel_start,
84962306a36Sopenharmony_ci				   u32 cp_nonpixel_size)
85062306a36Sopenharmony_ci{
85162306a36Sopenharmony_ci	int ret;
85262306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
85362306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_MP,
85462306a36Sopenharmony_ci		.cmd = QCOM_SCM_MP_VIDEO_VAR,
85562306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_VAL, QCOM_SCM_VAL,
85662306a36Sopenharmony_ci					 QCOM_SCM_VAL, QCOM_SCM_VAL),
85762306a36Sopenharmony_ci		.args[0] = cp_start,
85862306a36Sopenharmony_ci		.args[1] = cp_size,
85962306a36Sopenharmony_ci		.args[2] = cp_nonpixel_start,
86062306a36Sopenharmony_ci		.args[3] = cp_nonpixel_size,
86162306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
86262306a36Sopenharmony_ci	};
86362306a36Sopenharmony_ci	struct qcom_scm_res res;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	ret = qcom_scm_call(__scm->dev, &desc, &res);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	return ret ? : res.result[0];
86862306a36Sopenharmony_ci}
86962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_mem_protect_video_var);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_cistatic int __qcom_scm_assign_mem(struct device *dev, phys_addr_t mem_region,
87262306a36Sopenharmony_ci				 size_t mem_sz, phys_addr_t src, size_t src_sz,
87362306a36Sopenharmony_ci				 phys_addr_t dest, size_t dest_sz)
87462306a36Sopenharmony_ci{
87562306a36Sopenharmony_ci	int ret;
87662306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
87762306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_MP,
87862306a36Sopenharmony_ci		.cmd = QCOM_SCM_MP_ASSIGN,
87962306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(7, QCOM_SCM_RO, QCOM_SCM_VAL,
88062306a36Sopenharmony_ci					 QCOM_SCM_RO, QCOM_SCM_VAL, QCOM_SCM_RO,
88162306a36Sopenharmony_ci					 QCOM_SCM_VAL, QCOM_SCM_VAL),
88262306a36Sopenharmony_ci		.args[0] = mem_region,
88362306a36Sopenharmony_ci		.args[1] = mem_sz,
88462306a36Sopenharmony_ci		.args[2] = src,
88562306a36Sopenharmony_ci		.args[3] = src_sz,
88662306a36Sopenharmony_ci		.args[4] = dest,
88762306a36Sopenharmony_ci		.args[5] = dest_sz,
88862306a36Sopenharmony_ci		.args[6] = 0,
88962306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
89062306a36Sopenharmony_ci	};
89162306a36Sopenharmony_ci	struct qcom_scm_res res;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	ret = qcom_scm_call(dev, &desc, &res);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	return ret ? : res.result[0];
89662306a36Sopenharmony_ci}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci/**
89962306a36Sopenharmony_ci * qcom_scm_assign_mem() - Make a secure call to reassign memory ownership
90062306a36Sopenharmony_ci * @mem_addr: mem region whose ownership need to be reassigned
90162306a36Sopenharmony_ci * @mem_sz:   size of the region.
90262306a36Sopenharmony_ci * @srcvm:    vmid for current set of owners, each set bit in
90362306a36Sopenharmony_ci *            flag indicate a unique owner
90462306a36Sopenharmony_ci * @newvm:    array having new owners and corresponding permission
90562306a36Sopenharmony_ci *            flags
90662306a36Sopenharmony_ci * @dest_cnt: number of owners in next set.
90762306a36Sopenharmony_ci *
90862306a36Sopenharmony_ci * Return negative errno on failure or 0 on success with @srcvm updated.
90962306a36Sopenharmony_ci */
91062306a36Sopenharmony_ciint qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz,
91162306a36Sopenharmony_ci			u64 *srcvm,
91262306a36Sopenharmony_ci			const struct qcom_scm_vmperm *newvm,
91362306a36Sopenharmony_ci			unsigned int dest_cnt)
91462306a36Sopenharmony_ci{
91562306a36Sopenharmony_ci	struct qcom_scm_current_perm_info *destvm;
91662306a36Sopenharmony_ci	struct qcom_scm_mem_map_info *mem_to_map;
91762306a36Sopenharmony_ci	phys_addr_t mem_to_map_phys;
91862306a36Sopenharmony_ci	phys_addr_t dest_phys;
91962306a36Sopenharmony_ci	dma_addr_t ptr_phys;
92062306a36Sopenharmony_ci	size_t mem_to_map_sz;
92162306a36Sopenharmony_ci	size_t dest_sz;
92262306a36Sopenharmony_ci	size_t src_sz;
92362306a36Sopenharmony_ci	size_t ptr_sz;
92462306a36Sopenharmony_ci	int next_vm;
92562306a36Sopenharmony_ci	__le32 *src;
92662306a36Sopenharmony_ci	void *ptr;
92762306a36Sopenharmony_ci	int ret, i, b;
92862306a36Sopenharmony_ci	u64 srcvm_bits = *srcvm;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	src_sz = hweight64(srcvm_bits) * sizeof(*src);
93162306a36Sopenharmony_ci	mem_to_map_sz = sizeof(*mem_to_map);
93262306a36Sopenharmony_ci	dest_sz = dest_cnt * sizeof(*destvm);
93362306a36Sopenharmony_ci	ptr_sz = ALIGN(src_sz, SZ_64) + ALIGN(mem_to_map_sz, SZ_64) +
93462306a36Sopenharmony_ci			ALIGN(dest_sz, SZ_64);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	ptr = dma_alloc_coherent(__scm->dev, ptr_sz, &ptr_phys, GFP_KERNEL);
93762306a36Sopenharmony_ci	if (!ptr)
93862306a36Sopenharmony_ci		return -ENOMEM;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	/* Fill source vmid detail */
94162306a36Sopenharmony_ci	src = ptr;
94262306a36Sopenharmony_ci	i = 0;
94362306a36Sopenharmony_ci	for (b = 0; b < BITS_PER_TYPE(u64); b++) {
94462306a36Sopenharmony_ci		if (srcvm_bits & BIT(b))
94562306a36Sopenharmony_ci			src[i++] = cpu_to_le32(b);
94662306a36Sopenharmony_ci	}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	/* Fill details of mem buff to map */
94962306a36Sopenharmony_ci	mem_to_map = ptr + ALIGN(src_sz, SZ_64);
95062306a36Sopenharmony_ci	mem_to_map_phys = ptr_phys + ALIGN(src_sz, SZ_64);
95162306a36Sopenharmony_ci	mem_to_map->mem_addr = cpu_to_le64(mem_addr);
95262306a36Sopenharmony_ci	mem_to_map->mem_size = cpu_to_le64(mem_sz);
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	next_vm = 0;
95562306a36Sopenharmony_ci	/* Fill details of next vmid detail */
95662306a36Sopenharmony_ci	destvm = ptr + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(src_sz, SZ_64);
95762306a36Sopenharmony_ci	dest_phys = ptr_phys + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(src_sz, SZ_64);
95862306a36Sopenharmony_ci	for (i = 0; i < dest_cnt; i++, destvm++, newvm++) {
95962306a36Sopenharmony_ci		destvm->vmid = cpu_to_le32(newvm->vmid);
96062306a36Sopenharmony_ci		destvm->perm = cpu_to_le32(newvm->perm);
96162306a36Sopenharmony_ci		destvm->ctx = 0;
96262306a36Sopenharmony_ci		destvm->ctx_size = 0;
96362306a36Sopenharmony_ci		next_vm |= BIT(newvm->vmid);
96462306a36Sopenharmony_ci	}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	ret = __qcom_scm_assign_mem(__scm->dev, mem_to_map_phys, mem_to_map_sz,
96762306a36Sopenharmony_ci				    ptr_phys, src_sz, dest_phys, dest_sz);
96862306a36Sopenharmony_ci	dma_free_coherent(__scm->dev, ptr_sz, ptr, ptr_phys);
96962306a36Sopenharmony_ci	if (ret) {
97062306a36Sopenharmony_ci		dev_err(__scm->dev,
97162306a36Sopenharmony_ci			"Assign memory protection call failed %d\n", ret);
97262306a36Sopenharmony_ci		return -EINVAL;
97362306a36Sopenharmony_ci	}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	*srcvm = next_vm;
97662306a36Sopenharmony_ci	return 0;
97762306a36Sopenharmony_ci}
97862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_assign_mem);
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci/**
98162306a36Sopenharmony_ci * qcom_scm_ocmem_lock_available() - is OCMEM lock/unlock interface available
98262306a36Sopenharmony_ci */
98362306a36Sopenharmony_cibool qcom_scm_ocmem_lock_available(void)
98462306a36Sopenharmony_ci{
98562306a36Sopenharmony_ci	return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_OCMEM,
98662306a36Sopenharmony_ci					    QCOM_SCM_OCMEM_LOCK_CMD);
98762306a36Sopenharmony_ci}
98862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_ocmem_lock_available);
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci/**
99162306a36Sopenharmony_ci * qcom_scm_ocmem_lock() - call OCMEM lock interface to assign an OCMEM
99262306a36Sopenharmony_ci * region to the specified initiator
99362306a36Sopenharmony_ci *
99462306a36Sopenharmony_ci * @id:     tz initiator id
99562306a36Sopenharmony_ci * @offset: OCMEM offset
99662306a36Sopenharmony_ci * @size:   OCMEM size
99762306a36Sopenharmony_ci * @mode:   access mode (WIDE/NARROW)
99862306a36Sopenharmony_ci */
99962306a36Sopenharmony_ciint qcom_scm_ocmem_lock(enum qcom_scm_ocmem_client id, u32 offset, u32 size,
100062306a36Sopenharmony_ci			u32 mode)
100162306a36Sopenharmony_ci{
100262306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
100362306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_OCMEM,
100462306a36Sopenharmony_ci		.cmd = QCOM_SCM_OCMEM_LOCK_CMD,
100562306a36Sopenharmony_ci		.args[0] = id,
100662306a36Sopenharmony_ci		.args[1] = offset,
100762306a36Sopenharmony_ci		.args[2] = size,
100862306a36Sopenharmony_ci		.args[3] = mode,
100962306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(4),
101062306a36Sopenharmony_ci	};
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	return qcom_scm_call(__scm->dev, &desc, NULL);
101362306a36Sopenharmony_ci}
101462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_ocmem_lock);
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci/**
101762306a36Sopenharmony_ci * qcom_scm_ocmem_unlock() - call OCMEM unlock interface to release an OCMEM
101862306a36Sopenharmony_ci * region from the specified initiator
101962306a36Sopenharmony_ci *
102062306a36Sopenharmony_ci * @id:     tz initiator id
102162306a36Sopenharmony_ci * @offset: OCMEM offset
102262306a36Sopenharmony_ci * @size:   OCMEM size
102362306a36Sopenharmony_ci */
102462306a36Sopenharmony_ciint qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset, u32 size)
102562306a36Sopenharmony_ci{
102662306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
102762306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_OCMEM,
102862306a36Sopenharmony_ci		.cmd = QCOM_SCM_OCMEM_UNLOCK_CMD,
102962306a36Sopenharmony_ci		.args[0] = id,
103062306a36Sopenharmony_ci		.args[1] = offset,
103162306a36Sopenharmony_ci		.args[2] = size,
103262306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(3),
103362306a36Sopenharmony_ci	};
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	return qcom_scm_call(__scm->dev, &desc, NULL);
103662306a36Sopenharmony_ci}
103762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_ocmem_unlock);
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci/**
104062306a36Sopenharmony_ci * qcom_scm_ice_available() - Is the ICE key programming interface available?
104162306a36Sopenharmony_ci *
104262306a36Sopenharmony_ci * Return: true iff the SCM calls wrapped by qcom_scm_ice_invalidate_key() and
104362306a36Sopenharmony_ci *	   qcom_scm_ice_set_key() are available.
104462306a36Sopenharmony_ci */
104562306a36Sopenharmony_cibool qcom_scm_ice_available(void)
104662306a36Sopenharmony_ci{
104762306a36Sopenharmony_ci	return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_ES,
104862306a36Sopenharmony_ci					    QCOM_SCM_ES_INVALIDATE_ICE_KEY) &&
104962306a36Sopenharmony_ci		__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_ES,
105062306a36Sopenharmony_ci					     QCOM_SCM_ES_CONFIG_SET_ICE_KEY);
105162306a36Sopenharmony_ci}
105262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_ice_available);
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci/**
105562306a36Sopenharmony_ci * qcom_scm_ice_invalidate_key() - Invalidate an inline encryption key
105662306a36Sopenharmony_ci * @index: the keyslot to invalidate
105762306a36Sopenharmony_ci *
105862306a36Sopenharmony_ci * The UFSHCI and eMMC standards define a standard way to do this, but it
105962306a36Sopenharmony_ci * doesn't work on these SoCs; only this SCM call does.
106062306a36Sopenharmony_ci *
106162306a36Sopenharmony_ci * It is assumed that the SoC has only one ICE instance being used, as this SCM
106262306a36Sopenharmony_ci * call doesn't specify which ICE instance the keyslot belongs to.
106362306a36Sopenharmony_ci *
106462306a36Sopenharmony_ci * Return: 0 on success; -errno on failure.
106562306a36Sopenharmony_ci */
106662306a36Sopenharmony_ciint qcom_scm_ice_invalidate_key(u32 index)
106762306a36Sopenharmony_ci{
106862306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
106962306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_ES,
107062306a36Sopenharmony_ci		.cmd = QCOM_SCM_ES_INVALIDATE_ICE_KEY,
107162306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(1),
107262306a36Sopenharmony_ci		.args[0] = index,
107362306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
107462306a36Sopenharmony_ci	};
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	return qcom_scm_call(__scm->dev, &desc, NULL);
107762306a36Sopenharmony_ci}
107862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_ice_invalidate_key);
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci/**
108162306a36Sopenharmony_ci * qcom_scm_ice_set_key() - Set an inline encryption key
108262306a36Sopenharmony_ci * @index: the keyslot into which to set the key
108362306a36Sopenharmony_ci * @key: the key to program
108462306a36Sopenharmony_ci * @key_size: the size of the key in bytes
108562306a36Sopenharmony_ci * @cipher: the encryption algorithm the key is for
108662306a36Sopenharmony_ci * @data_unit_size: the encryption data unit size, i.e. the size of each
108762306a36Sopenharmony_ci *		    individual plaintext and ciphertext.  Given in 512-byte
108862306a36Sopenharmony_ci *		    units, e.g. 1 = 512 bytes, 8 = 4096 bytes, etc.
108962306a36Sopenharmony_ci *
109062306a36Sopenharmony_ci * Program a key into a keyslot of Qualcomm ICE (Inline Crypto Engine), where it
109162306a36Sopenharmony_ci * can then be used to encrypt/decrypt UFS or eMMC I/O requests inline.
109262306a36Sopenharmony_ci *
109362306a36Sopenharmony_ci * The UFSHCI and eMMC standards define a standard way to do this, but it
109462306a36Sopenharmony_ci * doesn't work on these SoCs; only this SCM call does.
109562306a36Sopenharmony_ci *
109662306a36Sopenharmony_ci * It is assumed that the SoC has only one ICE instance being used, as this SCM
109762306a36Sopenharmony_ci * call doesn't specify which ICE instance the keyslot belongs to.
109862306a36Sopenharmony_ci *
109962306a36Sopenharmony_ci * Return: 0 on success; -errno on failure.
110062306a36Sopenharmony_ci */
110162306a36Sopenharmony_ciint qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size,
110262306a36Sopenharmony_ci			 enum qcom_scm_ice_cipher cipher, u32 data_unit_size)
110362306a36Sopenharmony_ci{
110462306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
110562306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_ES,
110662306a36Sopenharmony_ci		.cmd = QCOM_SCM_ES_CONFIG_SET_ICE_KEY,
110762306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(5, QCOM_SCM_VAL, QCOM_SCM_RW,
110862306a36Sopenharmony_ci					 QCOM_SCM_VAL, QCOM_SCM_VAL,
110962306a36Sopenharmony_ci					 QCOM_SCM_VAL),
111062306a36Sopenharmony_ci		.args[0] = index,
111162306a36Sopenharmony_ci		.args[2] = key_size,
111262306a36Sopenharmony_ci		.args[3] = cipher,
111362306a36Sopenharmony_ci		.args[4] = data_unit_size,
111462306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
111562306a36Sopenharmony_ci	};
111662306a36Sopenharmony_ci	void *keybuf;
111762306a36Sopenharmony_ci	dma_addr_t key_phys;
111862306a36Sopenharmony_ci	int ret;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	/*
112162306a36Sopenharmony_ci	 * 'key' may point to vmalloc()'ed memory, but we need to pass a
112262306a36Sopenharmony_ci	 * physical address that's been properly flushed.  The sanctioned way to
112362306a36Sopenharmony_ci	 * do this is by using the DMA API.  But as is best practice for crypto
112462306a36Sopenharmony_ci	 * keys, we also must wipe the key after use.  This makes kmemdup() +
112562306a36Sopenharmony_ci	 * dma_map_single() not clearly correct, since the DMA API can use
112662306a36Sopenharmony_ci	 * bounce buffers.  Instead, just use dma_alloc_coherent().  Programming
112762306a36Sopenharmony_ci	 * keys is normally rare and thus not performance-critical.
112862306a36Sopenharmony_ci	 */
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	keybuf = dma_alloc_coherent(__scm->dev, key_size, &key_phys,
113162306a36Sopenharmony_ci				    GFP_KERNEL);
113262306a36Sopenharmony_ci	if (!keybuf)
113362306a36Sopenharmony_ci		return -ENOMEM;
113462306a36Sopenharmony_ci	memcpy(keybuf, key, key_size);
113562306a36Sopenharmony_ci	desc.args[1] = key_phys;
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	ret = qcom_scm_call(__scm->dev, &desc, NULL);
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	memzero_explicit(keybuf, key_size);
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	dma_free_coherent(__scm->dev, key_size, keybuf, key_phys);
114262306a36Sopenharmony_ci	return ret;
114362306a36Sopenharmony_ci}
114462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_ice_set_key);
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci/**
114762306a36Sopenharmony_ci * qcom_scm_hdcp_available() - Check if secure environment supports HDCP.
114862306a36Sopenharmony_ci *
114962306a36Sopenharmony_ci * Return true if HDCP is supported, false if not.
115062306a36Sopenharmony_ci */
115162306a36Sopenharmony_cibool qcom_scm_hdcp_available(void)
115262306a36Sopenharmony_ci{
115362306a36Sopenharmony_ci	bool avail;
115462306a36Sopenharmony_ci	int ret = qcom_scm_clk_enable();
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	if (ret)
115762306a36Sopenharmony_ci		return ret;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	avail = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_HDCP,
116062306a36Sopenharmony_ci						QCOM_SCM_HDCP_INVOKE);
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	qcom_scm_clk_disable();
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	return avail;
116562306a36Sopenharmony_ci}
116662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_hdcp_available);
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci/**
116962306a36Sopenharmony_ci * qcom_scm_hdcp_req() - Send HDCP request.
117062306a36Sopenharmony_ci * @req: HDCP request array
117162306a36Sopenharmony_ci * @req_cnt: HDCP request array count
117262306a36Sopenharmony_ci * @resp: response buffer passed to SCM
117362306a36Sopenharmony_ci *
117462306a36Sopenharmony_ci * Write HDCP register(s) through SCM.
117562306a36Sopenharmony_ci */
117662306a36Sopenharmony_ciint qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp)
117762306a36Sopenharmony_ci{
117862306a36Sopenharmony_ci	int ret;
117962306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
118062306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_HDCP,
118162306a36Sopenharmony_ci		.cmd = QCOM_SCM_HDCP_INVOKE,
118262306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(10),
118362306a36Sopenharmony_ci		.args = {
118462306a36Sopenharmony_ci			req[0].addr,
118562306a36Sopenharmony_ci			req[0].val,
118662306a36Sopenharmony_ci			req[1].addr,
118762306a36Sopenharmony_ci			req[1].val,
118862306a36Sopenharmony_ci			req[2].addr,
118962306a36Sopenharmony_ci			req[2].val,
119062306a36Sopenharmony_ci			req[3].addr,
119162306a36Sopenharmony_ci			req[3].val,
119262306a36Sopenharmony_ci			req[4].addr,
119362306a36Sopenharmony_ci			req[4].val
119462306a36Sopenharmony_ci		},
119562306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
119662306a36Sopenharmony_ci	};
119762306a36Sopenharmony_ci	struct qcom_scm_res res;
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT)
120062306a36Sopenharmony_ci		return -ERANGE;
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	ret = qcom_scm_clk_enable();
120362306a36Sopenharmony_ci	if (ret)
120462306a36Sopenharmony_ci		return ret;
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	ret = qcom_scm_call(__scm->dev, &desc, &res);
120762306a36Sopenharmony_ci	*resp = res.result[0];
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	qcom_scm_clk_disable();
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	return ret;
121262306a36Sopenharmony_ci}
121362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_hdcp_req);
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ciint qcom_scm_iommu_set_pt_format(u32 sec_id, u32 ctx_num, u32 pt_fmt)
121662306a36Sopenharmony_ci{
121762306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
121862306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_SMMU_PROGRAM,
121962306a36Sopenharmony_ci		.cmd = QCOM_SCM_SMMU_PT_FORMAT,
122062306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(3),
122162306a36Sopenharmony_ci		.args[0] = sec_id,
122262306a36Sopenharmony_ci		.args[1] = ctx_num,
122362306a36Sopenharmony_ci		.args[2] = pt_fmt, /* 0: LPAE AArch32 - 1: AArch64 */
122462306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
122562306a36Sopenharmony_ci	};
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	return qcom_scm_call(__scm->dev, &desc, NULL);
122862306a36Sopenharmony_ci}
122962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_iommu_set_pt_format);
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ciint qcom_scm_qsmmu500_wait_safe_toggle(bool en)
123262306a36Sopenharmony_ci{
123362306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
123462306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_SMMU_PROGRAM,
123562306a36Sopenharmony_ci		.cmd = QCOM_SCM_SMMU_CONFIG_ERRATA1,
123662306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(2),
123762306a36Sopenharmony_ci		.args[0] = QCOM_SCM_SMMU_CONFIG_ERRATA1_CLIENT_ALL,
123862306a36Sopenharmony_ci		.args[1] = en,
123962306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
124062306a36Sopenharmony_ci	};
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	return qcom_scm_call_atomic(__scm->dev, &desc, NULL);
124462306a36Sopenharmony_ci}
124562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_qsmmu500_wait_safe_toggle);
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_cibool qcom_scm_lmh_dcvsh_available(void)
124862306a36Sopenharmony_ci{
124962306a36Sopenharmony_ci	return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_LMH, QCOM_SCM_LMH_LIMIT_DCVSH);
125062306a36Sopenharmony_ci}
125162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_lmh_dcvsh_available);
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ciint qcom_scm_lmh_profile_change(u32 profile_id)
125462306a36Sopenharmony_ci{
125562306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
125662306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_LMH,
125762306a36Sopenharmony_ci		.cmd = QCOM_SCM_LMH_LIMIT_PROFILE_CHANGE,
125862306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(1, QCOM_SCM_VAL),
125962306a36Sopenharmony_ci		.args[0] = profile_id,
126062306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
126162306a36Sopenharmony_ci	};
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	return qcom_scm_call(__scm->dev, &desc, NULL);
126462306a36Sopenharmony_ci}
126562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_lmh_profile_change);
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ciint qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val,
126862306a36Sopenharmony_ci		       u64 limit_node, u32 node_id, u64 version)
126962306a36Sopenharmony_ci{
127062306a36Sopenharmony_ci	dma_addr_t payload_phys;
127162306a36Sopenharmony_ci	u32 *payload_buf;
127262306a36Sopenharmony_ci	int ret, payload_size = 5 * sizeof(u32);
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	struct qcom_scm_desc desc = {
127562306a36Sopenharmony_ci		.svc = QCOM_SCM_SVC_LMH,
127662306a36Sopenharmony_ci		.cmd = QCOM_SCM_LMH_LIMIT_DCVSH,
127762306a36Sopenharmony_ci		.arginfo = QCOM_SCM_ARGS(5, QCOM_SCM_RO, QCOM_SCM_VAL, QCOM_SCM_VAL,
127862306a36Sopenharmony_ci					QCOM_SCM_VAL, QCOM_SCM_VAL),
127962306a36Sopenharmony_ci		.args[1] = payload_size,
128062306a36Sopenharmony_ci		.args[2] = limit_node,
128162306a36Sopenharmony_ci		.args[3] = node_id,
128262306a36Sopenharmony_ci		.args[4] = version,
128362306a36Sopenharmony_ci		.owner = ARM_SMCCC_OWNER_SIP,
128462306a36Sopenharmony_ci	};
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	payload_buf = dma_alloc_coherent(__scm->dev, payload_size, &payload_phys, GFP_KERNEL);
128762306a36Sopenharmony_ci	if (!payload_buf)
128862306a36Sopenharmony_ci		return -ENOMEM;
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	payload_buf[0] = payload_fn;
129162306a36Sopenharmony_ci	payload_buf[1] = 0;
129262306a36Sopenharmony_ci	payload_buf[2] = payload_reg;
129362306a36Sopenharmony_ci	payload_buf[3] = 1;
129462306a36Sopenharmony_ci	payload_buf[4] = payload_val;
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	desc.args[0] = payload_phys;
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	ret = qcom_scm_call(__scm->dev, &desc, NULL);
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	dma_free_coherent(__scm->dev, payload_size, payload_buf, payload_phys);
130162306a36Sopenharmony_ci	return ret;
130262306a36Sopenharmony_ci}
130362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_lmh_dcvsh);
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_cistatic int qcom_scm_find_dload_address(struct device *dev, u64 *addr)
130662306a36Sopenharmony_ci{
130762306a36Sopenharmony_ci	struct device_node *tcsr;
130862306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
130962306a36Sopenharmony_ci	struct resource res;
131062306a36Sopenharmony_ci	u32 offset;
131162306a36Sopenharmony_ci	int ret;
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	tcsr = of_parse_phandle(np, "qcom,dload-mode", 0);
131462306a36Sopenharmony_ci	if (!tcsr)
131562306a36Sopenharmony_ci		return 0;
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	ret = of_address_to_resource(tcsr, 0, &res);
131862306a36Sopenharmony_ci	of_node_put(tcsr);
131962306a36Sopenharmony_ci	if (ret)
132062306a36Sopenharmony_ci		return ret;
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	ret = of_property_read_u32_index(np, "qcom,dload-mode", 1, &offset);
132362306a36Sopenharmony_ci	if (ret < 0)
132462306a36Sopenharmony_ci		return ret;
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	*addr = res.start + offset;
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	return 0;
132962306a36Sopenharmony_ci}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci/**
133262306a36Sopenharmony_ci * qcom_scm_is_available() - Checks if SCM is available
133362306a36Sopenharmony_ci */
133462306a36Sopenharmony_cibool qcom_scm_is_available(void)
133562306a36Sopenharmony_ci{
133662306a36Sopenharmony_ci	return !!__scm;
133762306a36Sopenharmony_ci}
133862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_scm_is_available);
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_cistatic int qcom_scm_assert_valid_wq_ctx(u32 wq_ctx)
134162306a36Sopenharmony_ci{
134262306a36Sopenharmony_ci	/* FW currently only supports a single wq_ctx (zero).
134362306a36Sopenharmony_ci	 * TODO: Update this logic to include dynamic allocation and lookup of
134462306a36Sopenharmony_ci	 * completion structs when FW supports more wq_ctx values.
134562306a36Sopenharmony_ci	 */
134662306a36Sopenharmony_ci	if (wq_ctx != 0) {
134762306a36Sopenharmony_ci		dev_err(__scm->dev, "Firmware unexpectedly passed non-zero wq_ctx\n");
134862306a36Sopenharmony_ci		return -EINVAL;
134962306a36Sopenharmony_ci	}
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	return 0;
135262306a36Sopenharmony_ci}
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ciint qcom_scm_wait_for_wq_completion(u32 wq_ctx)
135562306a36Sopenharmony_ci{
135662306a36Sopenharmony_ci	int ret;
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	ret = qcom_scm_assert_valid_wq_ctx(wq_ctx);
135962306a36Sopenharmony_ci	if (ret)
136062306a36Sopenharmony_ci		return ret;
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	wait_for_completion(&__scm->waitq_comp);
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	return 0;
136562306a36Sopenharmony_ci}
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_cistatic int qcom_scm_waitq_wakeup(struct qcom_scm *scm, unsigned int wq_ctx)
136862306a36Sopenharmony_ci{
136962306a36Sopenharmony_ci	int ret;
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	ret = qcom_scm_assert_valid_wq_ctx(wq_ctx);
137262306a36Sopenharmony_ci	if (ret)
137362306a36Sopenharmony_ci		return ret;
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	complete(&__scm->waitq_comp);
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	return 0;
137862306a36Sopenharmony_ci}
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_cistatic irqreturn_t qcom_scm_irq_handler(int irq, void *data)
138162306a36Sopenharmony_ci{
138262306a36Sopenharmony_ci	int ret;
138362306a36Sopenharmony_ci	struct qcom_scm *scm = data;
138462306a36Sopenharmony_ci	u32 wq_ctx, flags, more_pending = 0;
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	do {
138762306a36Sopenharmony_ci		ret = scm_get_wq_ctx(&wq_ctx, &flags, &more_pending);
138862306a36Sopenharmony_ci		if (ret) {
138962306a36Sopenharmony_ci			dev_err(scm->dev, "GET_WQ_CTX SMC call failed: %d\n", ret);
139062306a36Sopenharmony_ci			goto out;
139162306a36Sopenharmony_ci		}
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci		if (flags != QCOM_SMC_WAITQ_FLAG_WAKE_ONE &&
139462306a36Sopenharmony_ci		    flags != QCOM_SMC_WAITQ_FLAG_WAKE_ALL) {
139562306a36Sopenharmony_ci			dev_err(scm->dev, "Invalid flags found for wq_ctx: %u\n", flags);
139662306a36Sopenharmony_ci			goto out;
139762306a36Sopenharmony_ci		}
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci		ret = qcom_scm_waitq_wakeup(scm, wq_ctx);
140062306a36Sopenharmony_ci		if (ret)
140162306a36Sopenharmony_ci			goto out;
140262306a36Sopenharmony_ci	} while (more_pending);
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ciout:
140562306a36Sopenharmony_ci	return IRQ_HANDLED;
140662306a36Sopenharmony_ci}
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_cistatic int qcom_scm_probe(struct platform_device *pdev)
140962306a36Sopenharmony_ci{
141062306a36Sopenharmony_ci	struct qcom_scm *scm;
141162306a36Sopenharmony_ci	int irq, ret;
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	scm = devm_kzalloc(&pdev->dev, sizeof(*scm), GFP_KERNEL);
141462306a36Sopenharmony_ci	if (!scm)
141562306a36Sopenharmony_ci		return -ENOMEM;
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	ret = qcom_scm_find_dload_address(&pdev->dev, &scm->dload_mode_addr);
141862306a36Sopenharmony_ci	if (ret < 0)
141962306a36Sopenharmony_ci		return ret;
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	mutex_init(&scm->scm_bw_lock);
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	scm->path = devm_of_icc_get(&pdev->dev, NULL);
142462306a36Sopenharmony_ci	if (IS_ERR(scm->path))
142562306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, PTR_ERR(scm->path),
142662306a36Sopenharmony_ci				     "failed to acquire interconnect path\n");
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	scm->core_clk = devm_clk_get_optional(&pdev->dev, "core");
142962306a36Sopenharmony_ci	if (IS_ERR(scm->core_clk))
143062306a36Sopenharmony_ci		return PTR_ERR(scm->core_clk);
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	scm->iface_clk = devm_clk_get_optional(&pdev->dev, "iface");
143362306a36Sopenharmony_ci	if (IS_ERR(scm->iface_clk))
143462306a36Sopenharmony_ci		return PTR_ERR(scm->iface_clk);
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	scm->bus_clk = devm_clk_get_optional(&pdev->dev, "bus");
143762306a36Sopenharmony_ci	if (IS_ERR(scm->bus_clk))
143862306a36Sopenharmony_ci		return PTR_ERR(scm->bus_clk);
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	scm->reset.ops = &qcom_scm_pas_reset_ops;
144162306a36Sopenharmony_ci	scm->reset.nr_resets = 1;
144262306a36Sopenharmony_ci	scm->reset.of_node = pdev->dev.of_node;
144362306a36Sopenharmony_ci	ret = devm_reset_controller_register(&pdev->dev, &scm->reset);
144462306a36Sopenharmony_ci	if (ret)
144562306a36Sopenharmony_ci		return ret;
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	/* vote for max clk rate for highest performance */
144862306a36Sopenharmony_ci	ret = clk_set_rate(scm->core_clk, INT_MAX);
144962306a36Sopenharmony_ci	if (ret)
145062306a36Sopenharmony_ci		return ret;
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	__scm = scm;
145362306a36Sopenharmony_ci	__scm->dev = &pdev->dev;
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	init_completion(&__scm->waitq_comp);
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	irq = platform_get_irq_optional(pdev, 0);
145862306a36Sopenharmony_ci	if (irq < 0) {
145962306a36Sopenharmony_ci		if (irq != -ENXIO)
146062306a36Sopenharmony_ci			return irq;
146162306a36Sopenharmony_ci	} else {
146262306a36Sopenharmony_ci		ret = devm_request_threaded_irq(__scm->dev, irq, NULL, qcom_scm_irq_handler,
146362306a36Sopenharmony_ci						IRQF_ONESHOT, "qcom-scm", __scm);
146462306a36Sopenharmony_ci		if (ret < 0)
146562306a36Sopenharmony_ci			return dev_err_probe(scm->dev, ret, "Failed to request qcom-scm irq\n");
146662306a36Sopenharmony_ci	}
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	__get_convention();
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	/*
147162306a36Sopenharmony_ci	 * If requested enable "download mode", from this point on warmboot
147262306a36Sopenharmony_ci	 * will cause the boot stages to enter download mode, unless
147362306a36Sopenharmony_ci	 * disabled below by a clean shutdown/reboot.
147462306a36Sopenharmony_ci	 */
147562306a36Sopenharmony_ci	if (download_mode)
147662306a36Sopenharmony_ci		qcom_scm_set_download_mode(true);
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	return 0;
147962306a36Sopenharmony_ci}
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_cistatic void qcom_scm_shutdown(struct platform_device *pdev)
148262306a36Sopenharmony_ci{
148362306a36Sopenharmony_ci	/* Clean shutdown, disable download mode to allow normal restart */
148462306a36Sopenharmony_ci	qcom_scm_set_download_mode(false);
148562306a36Sopenharmony_ci}
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_cistatic const struct of_device_id qcom_scm_dt_match[] = {
148862306a36Sopenharmony_ci	{ .compatible = "qcom,scm" },
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	/* Legacy entries kept for backwards compatibility */
149162306a36Sopenharmony_ci	{ .compatible = "qcom,scm-apq8064" },
149262306a36Sopenharmony_ci	{ .compatible = "qcom,scm-apq8084" },
149362306a36Sopenharmony_ci	{ .compatible = "qcom,scm-ipq4019" },
149462306a36Sopenharmony_ci	{ .compatible = "qcom,scm-msm8953" },
149562306a36Sopenharmony_ci	{ .compatible = "qcom,scm-msm8974" },
149662306a36Sopenharmony_ci	{ .compatible = "qcom,scm-msm8996" },
149762306a36Sopenharmony_ci	{}
149862306a36Sopenharmony_ci};
149962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, qcom_scm_dt_match);
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_cistatic struct platform_driver qcom_scm_driver = {
150262306a36Sopenharmony_ci	.driver = {
150362306a36Sopenharmony_ci		.name	= "qcom_scm",
150462306a36Sopenharmony_ci		.of_match_table = qcom_scm_dt_match,
150562306a36Sopenharmony_ci		.suppress_bind_attrs = true,
150662306a36Sopenharmony_ci	},
150762306a36Sopenharmony_ci	.probe = qcom_scm_probe,
150862306a36Sopenharmony_ci	.shutdown = qcom_scm_shutdown,
150962306a36Sopenharmony_ci};
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_cistatic int __init qcom_scm_init(void)
151262306a36Sopenharmony_ci{
151362306a36Sopenharmony_ci	return platform_driver_register(&qcom_scm_driver);
151462306a36Sopenharmony_ci}
151562306a36Sopenharmony_cisubsys_initcall(qcom_scm_init);
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm Technologies, Inc. SCM driver");
151862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
1519