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