From patchwork Fri May 13 03:46:59 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andy Gross X-Patchwork-Id: 9087611 X-Patchwork-Delegate: agross@codeaurora.org Return-Path: X-Original-To: patchwork-linux-arm-msm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id B2176BF29F for ; Fri, 13 May 2016 03:48:48 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 636F620225 for ; Fri, 13 May 2016 03:48:47 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 6C6BF20204 for ; Fri, 13 May 2016 03:48:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753333AbcEMDsP (ORCPT ); Thu, 12 May 2016 23:48:15 -0400 Received: from mail-oi0-f45.google.com ([209.85.218.45]:35259 "EHLO mail-oi0-f45.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753158AbcEMDr1 (ORCPT ); Thu, 12 May 2016 23:47:27 -0400 Received: by mail-oi0-f45.google.com with SMTP id x19so152000834oix.2 for ; Thu, 12 May 2016 20:47:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=NOASi9XqG557dG1H+suNmyej5Uxb/cJfR/Rr71VUtYs=; b=X25gORHAljfizYynNVkvX8ETC2ezUwDleciLeW6JIufCmORH6USZaRxkxZU7Prz4vT 0y5ROtjoX3xyHQ4/IQoC9TDJyiT7pKPELYE5d/4IzbPEOZKYKwiDZC6rrNRbZNu4Bp7b ldMpIzm9NzJUbJtgcz1eVDPPZ45g+IE34XHTg= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=NOASi9XqG557dG1H+suNmyej5Uxb/cJfR/Rr71VUtYs=; b=aTR7qmLYQErRfSOcpkCgferRBVLNrYvXWUTej2whdSq7VtB90vW+woxcQTEwfAscCB fypkdbEN+S8R20VSZ45SdyYjFPOfp/F5xJ7japLnd40rBWuB5i0LbF/pukVMk3pOwfpj 8nir4Sl5hBu1sZsiq8flYKWzj+0pcQAki3d9+uq+0WLdJDgyDGHV0kJJaES1RVkuHe5a 89hAyNYo6IujQH2aR2id7XTChypCRaExHJyrcL3mrDQCa1LoU1w4EuXJTF3J6qDGbqgu FyLl7w0Cysx/fHlAMeCCwOilMA+mdm1V+64/TnS56J6k+3+7KkSvU8MeomR5N0RAY4wz ZmlQ== X-Gm-Message-State: AOPr4FVI0CiVOdNmsR1UfKHL7kioPNQEW1N8URJJ7A5s/tydMSkJVMv5JkQK2z0I2NhGmB9M X-Received: by 10.202.169.131 with SMTP id s125mr6675910oie.35.1463111246976; Thu, 12 May 2016 20:47:26 -0700 (PDT) Received: from localhost (108-85-129-155.lightspeed.austtx.sbcglobal.net. [108.85.129.155]) by smtp.gmail.com with ESMTPSA id ut10sm4772239obc.19.2016.05.12.20.47.26 (version=TLS1_2 cipher=AES128-SHA bits=128/128); Thu, 12 May 2016 20:47:26 -0700 (PDT) From: Andy Gross To: linux-arm-msm@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Bjorn Andersson , Stephen Boyd , devicetree@vger.kernel.org, jilai wang , Kumar Gala , Andy Gross Subject: [Patch v5 6/8] firmware: qcom: scm: Add support for ARM64 SoCs Date: Thu, 12 May 2016 22:46:59 -0500 Message-Id: <1463111221-6963-7-git-send-email-andy.gross@linaro.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1463111221-6963-1-git-send-email-andy.gross@linaro.org> References: <1463111221-6963-1-git-send-email-andy.gross@linaro.org> Sender: linux-arm-msm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org X-Spam-Status: No, score=-8.2 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,RP_MATCHES_RCVD,T_DKIM_INVALID,UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Kumar Gala Add an implementation of the SCM interface that works on ARM64 SoCs. This is used by things like determine if we have HDCP support or not on the system. Signed-off-by: Kumar Gala Signed-off-by: Andy Gross Reviewed-by: Bjorn Andersson --- drivers/firmware/qcom_scm-32.c | 4 + drivers/firmware/qcom_scm-64.c | 199 +++++++++++++++++++++++++++++++++++++++-- drivers/firmware/qcom_scm.c | 2 + drivers/firmware/qcom_scm.h | 5 ++ 4 files changed, 205 insertions(+), 5 deletions(-) diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c index e92bf7a..4971b55 100644 --- a/drivers/firmware/qcom_scm-32.c +++ b/drivers/firmware/qcom_scm-32.c @@ -420,3 +420,7 @@ int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req, return qcom_scm_call(dev, QCOM_SCM_SVC_HDCP, QCOM_SCM_CMD_HDCP, req, req_cnt * sizeof(*req), resp, sizeof(*resp)); } + +void __qcom_scm_init(void) +{ +} diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c index bb6555f..43ed4de 100644 --- a/drivers/firmware/qcom_scm-64.c +++ b/drivers/firmware/qcom_scm-64.c @@ -12,7 +12,143 @@ #include #include +#include +#include +#include +#include #include +#include +#include + +#include "qcom_scm.h" + +#define QCOM_SCM_FNID(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF)) + +#define MAX_QCOM_SCM_ARGS 10 +#define MAX_QCOM_SCM_RETS 3 + +#define QCOM_SCM_ARGS_IMPL(num, a, b, c, d, e, f, g, h, i, j, ...) (\ + (((a) & 0x3) << 4) | \ + (((b) & 0x3) << 6) | \ + (((c) & 0x3) << 8) | \ + (((d) & 0x3) << 10) | \ + (((e) & 0x3) << 12) | \ + (((f) & 0x3) << 14) | \ + (((g) & 0x3) << 16) | \ + (((h) & 0x3) << 18) | \ + (((i) & 0x3) << 20) | \ + (((j) & 0x3) << 22) | \ + (num & 0xf)) + +#define QCOM_SCM_ARGS(...) QCOM_SCM_ARGS_IMPL(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + +/** + * struct qcom_scm_desc + * @arginfo: Metadata describing the arguments in args[] + * @args: The array of arguments for the secure syscall + * @res: The values returned by the secure syscall + */ +struct qcom_scm_desc { + u32 arginfo; + u64 args[MAX_QCOM_SCM_ARGS]; + struct arm_smccc_res res; +}; + +static u64 qcom_smccc_convention = -1; +static DEFINE_MUTEX(qcom_scm_lock); + +#define QCOM_SCM_EBUSY_WAIT_MS 30 +#define QCOM_SCM_EBUSY_MAX_RETRY 20 + +#define N_EXT_QCOM_SCM_ARGS 7 +#define FIRST_EXT_ARG_IDX 3 +#define N_REGISTER_ARGS (MAX_QCOM_SCM_ARGS - N_EXT_QCOM_SCM_ARGS + 1) + +/** + * qcom_scm_call() - Invoke a syscall in the secure world + * @dev: device + * @svc_id: service identifier + * @cmd_id: command identifier + * @desc: Descriptor structure containing arguments and return values + * + * Sends a command to the SCM and waits for the command to finish processing. + * This should *only* be called in pre-emptible context. +*/ +static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id, + struct qcom_scm_desc *desc) +{ + int arglen = desc->arginfo & 0xf; + int retry_count = 0, i; + u32 fn_id = QCOM_SCM_FNID(svc_id, cmd_id); + u64 cmd, x5 = desc->args[FIRST_EXT_ARG_IDX]; + dma_addr_t args_phys = 0; + void *args_virt = NULL; + size_t alloc_len; + + if (unlikely(arglen > N_REGISTER_ARGS)) { + alloc_len = N_EXT_QCOM_SCM_ARGS * sizeof(u64); + args_virt = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL); + + if (!args_virt) + return qcom_scm_remap_error(-ENOMEM); + + if (qcom_smccc_convention == ARM_SMCCC_SMC_32) { + __le32 *args = args_virt; + + for (i = 0; i < N_EXT_QCOM_SCM_ARGS; i++) + args[i] = cpu_to_le32(desc->args[i + + FIRST_EXT_ARG_IDX]); + } else { + __le64 *args = args_virt; + + for (i = 0; i < N_EXT_QCOM_SCM_ARGS; i++) + args[i] = cpu_to_le64(desc->args[i + + FIRST_EXT_ARG_IDX]); + } + + args_phys = dma_map_single(dev, args_virt, alloc_len, + DMA_TO_DEVICE); + + if (dma_mapping_error(dev, args_phys)) { + kfree(args_virt); + return qcom_scm_remap_error(-ENOMEM); + } + + x5 = args_phys; + } + + do { + mutex_lock(&qcom_scm_lock); + + cmd = ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, + qcom_smccc_convention, + ARM_SMCCC_OWNER_SIP, fn_id); + + do { + arm_smccc_smc(cmd, desc->arginfo, desc->args[0], + desc->args[1], desc->args[2], x5, 0, 0, + &desc->res); + } while (desc->res.a0 == QCOM_SCM_INTERRUPTED); + + mutex_unlock(&qcom_scm_lock); + + if (desc->res.a0 == QCOM_SCM_V2_EBUSY) { + if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY) + break; + msleep(QCOM_SCM_EBUSY_WAIT_MS); + } + } while (desc->res.a0 == QCOM_SCM_V2_EBUSY); + + if (args_virt) { + dma_unmap_single(dev, args_phys, alloc_len, DMA_TO_DEVICE); + kfree(args_virt); + } + + if (desc->res.a0 < 0) + return qcom_scm_remap_error(desc->res.a0); + + return 0; +} /** * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus @@ -29,13 +165,15 @@ int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus) /** * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus + * @dev: Device pointer * @entry: Entry point function for the cpus * @cpus: The cpumask of cpus that will use the entry point * * Set the Linux entry point for the SCM to transfer control to when coming * out of a power down. CPU power down may be executed on cpuidle or hotplug. */ -int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) +int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry, + const cpumask_t *cpus) { return -ENOTSUPP; } @@ -52,12 +190,63 @@ void __qcom_scm_cpu_power_down(u32 flags) { } -int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id) +int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, u32 cmd_id) { - return -ENOTSUPP; + int ret; + struct qcom_scm_desc desc = {0}; + + desc.arginfo = QCOM_SCM_ARGS(1); + desc.args[0] = QCOM_SCM_FNID(svc_id, cmd_id) | + (ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT); + + ret = qcom_scm_call(dev, QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD, + &desc); + + return ret ? : desc.res.a1; } -int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) +int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req, + u32 req_cnt, u32 *resp) { - return -ENOTSUPP; + int ret; + struct qcom_scm_desc desc = {0}; + + if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT) + return -ERANGE; + + desc.args[0] = req[0].addr; + desc.args[1] = req[0].val; + desc.args[2] = req[1].addr; + desc.args[3] = req[1].val; + desc.args[4] = req[2].addr; + desc.args[5] = req[2].val; + desc.args[6] = req[3].addr; + desc.args[7] = req[3].val; + desc.args[8] = req[4].addr; + desc.args[9] = req[4].val; + desc.arginfo = QCOM_SCM_ARGS(10); + + ret = qcom_scm_call(dev, QCOM_SCM_SVC_HDCP, QCOM_SCM_CMD_HDCP, &desc); + *resp = desc.res.a1; + + return ret; +} + +void __qcom_scm_init(void) +{ + u64 cmd; + struct arm_smccc_res res; + u32 function = QCOM_SCM_FNID(QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD); + + /* First try a SMC64 call */ + cmd = ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64, + ARM_SMCCC_OWNER_SIP, function); + + arm_smccc_smc(cmd, QCOM_SCM_ARGS(1), cmd & (~BIT(ARM_SMCCC_TYPE_SHIFT)), + 0, 0, 0, 0, 0, &res); + + if (!res.a0 && res.a1) + qcom_smccc_convention = ARM_SMCCC_SMC_64; + else + qcom_smccc_convention = ARM_SMCCC_SMC_32; } diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 23ccec6..475410b3e 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -189,6 +189,8 @@ static int qcom_scm_probe(struct platform_device *pdev) __scm = scm; __scm->dev = &pdev->dev; + __qcom_scm_init(); + return 0; } diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index afe6676..0ea55d7 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -38,7 +38,10 @@ extern int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, extern int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp); +extern void __qcom_scm_init(void); + /* common error codes */ +#define QCOM_SCM_V2_EBUSY -12 #define QCOM_SCM_ENOMEM -5 #define QCOM_SCM_EOPNOTSUPP -4 #define QCOM_SCM_EINVAL_ADDR -3 @@ -58,6 +61,8 @@ static inline int qcom_scm_remap_error(int err) return -EOPNOTSUPP; case QCOM_SCM_ENOMEM: return -ENOMEM; + case QCOM_SCM_V2_EBUSY: + return -EBUSY; } return -EINVAL; }