From patchwork Tue Aug 23 05:57:44 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bjorn Andersson X-Patchwork-Id: 9294991 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id C533D607F0 for ; Tue, 23 Aug 2016 06:00:12 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B500828400 for ; Tue, 23 Aug 2016 06:00:12 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A91B2289C2; Tue, 23 Aug 2016 06:00:12 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.1 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_MED, T_DKIM_INVALID autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id B461E28400 for ; Tue, 23 Aug 2016 06:00:11 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.85_2 #1 (Red Hat Linux)) id 1bc4jE-0002JN-HD; Tue, 23 Aug 2016 05:58:28 +0000 Received: from mail-pa0-x236.google.com ([2607:f8b0:400e:c03::236]) by bombadil.infradead.org with esmtps (Exim 4.85_2 #1 (Red Hat Linux)) id 1bc4ix-0002GF-U5 for linux-arm-kernel@lists.infradead.org; Tue, 23 Aug 2016 05:58:13 +0000 Received: by mail-pa0-x236.google.com with SMTP id pp5so44804159pac.3 for ; Mon, 22 Aug 2016 22:57:51 -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=5ubyT8Ib5T2+y79IHEA9oLmCIcySrox7Zl4RO3/V83w=; b=GRDR/oynfYTVUdvfIOOrt6flp28aekzrv5/Js2tpYUgj1aZnEuNoZd9i2133wMNdDs FkcX2/7Jrb8s5OhwwxQy/VaJmrMvchB54vk2XeMnLNRIv3qGaxS6zFpXw1ikpVc0LUO4 1shMewM//vt7/MYJCsVSB4W+TJ09F/o6P+qoA= 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=5ubyT8Ib5T2+y79IHEA9oLmCIcySrox7Zl4RO3/V83w=; b=b/LRCUphmLJMWAKDAOB50fYkMMAEXNWiP7FIoC2OqVx+6M02eL5NlYA+V+8vjZlgJK eriD2dn2gXclUyhvc9vhZg6NiHyevA8AqZP8jrXpAFtjjPPPLKHUBgXy9mdjh6zIKCpM OPnuYlgKxS9BJzhVx/Pereh+gq5iEJ+V27gQN0JEVyLtIkCjyQCsmXDOlNwD0Th0SEIQ IbiEP8m6DRRG7gMThcLZUNSrr1mQAX39S25hm6R2E8hLsM5neJIQhTxEoBwE+5IvwKFw C2gF75H7HV7e0NrL2KZZimXA0gEw4kdHX+O+tqigpSLRJvJrOrUXB9LA/iM06L4lFqxG 7iBQ== X-Gm-Message-State: AEkoouurnkDxZEqwfinl4gbtTpwo5VFyyuVNCBLX+SJko2dzlhWiMwzvVEucDo3J9LLErEda X-Received: by 10.66.226.201 with SMTP id ru9mr49212464pac.55.1471931870664; Mon, 22 Aug 2016 22:57:50 -0700 (PDT) Received: from localhost.localdomain (ip68-111-223-48.sd.sd.cox.net. [68.111.223.48]) by smtp.gmail.com with ESMTPSA id uc1sm1994173pac.24.2016.08.22.22.57.49 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 22 Aug 2016 22:57:50 -0700 (PDT) From: Bjorn Andersson To: Bjorn Andersson , Ohad Ben-Cohen Subject: [PATCH v2 2/4] remoteproc: Introduce Qualcomm ADSP PIL Date: Mon, 22 Aug 2016 22:57:44 -0700 Message-Id: <1471931866-3107-2-git-send-email-bjorn.andersson@linaro.org> X-Mailer: git-send-email 2.5.0 In-Reply-To: <1471931866-3107-1-git-send-email-bjorn.andersson@linaro.org> References: <1471931866-3107-1-git-send-email-bjorn.andersson@linaro.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20160822_225812_069295_8655B2FF X-CRM114-Status: GOOD ( 23.76 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Mark Rutland , devicetree@vger.kernel.org, linux-arm-msm@vger.kernel.org, linux-remoteproc@vger.kernel.org, linux-kernel@vger.kernel.org, Rob Herring , Bjorn Andersson , Andy Gross , linux-soc@vger.kernel.org, linux-arm-kernel@lists.infradead.org MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP From: Bjorn Andersson The Qualcomm ADSP Peripheral Image Loader is used on a variety of different Qualcomm platforms for loading firmware into and controlling the Hexagon based ADSP. Signed-off-by: Bjorn Andersson Signed-off-by: Bjorn Andersson --- Changes since v1: - Added platform names to compatibility - Removed some incorrect quirks that tried to handle older platforms drivers/remoteproc/Kconfig | 11 + drivers/remoteproc/Makefile | 1 + drivers/remoteproc/qcom_adsp_pil.c | 399 +++++++++++++++++++++++++++++++++++++ 3 files changed, 411 insertions(+) create mode 100644 drivers/remoteproc/qcom_adsp_pil.c diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 1a8bf76a925f..919741674686 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -77,6 +77,17 @@ config DA8XX_REMOTEPROC It's safe to say n here if you're not interested in multimedia offloading. +config QCOM_ADSP_PIL + tristate "Qualcomm ADSP Peripheral Image Loader" + depends on OF && ARCH_QCOM + depends on QCOM_SMEM + select MFD_SYSCON + select QCOM_MDT_LOADER + select REMOTEPROC + help + Say y here to support the TrustZone based Peripherial Image Loader + for the Qualcomm ADSP remote processors. + config QCOM_MDT_LOADER tristate diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 92d3758bd15c..877a4c3b554d 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o obj-$(CONFIG_STE_MODEM_RPROC) += ste_modem_rproc.o obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o +obj-$(CONFIG_QCOM_ADSP_PIL) += qcom_adsp_pil.o obj-$(CONFIG_QCOM_MDT_LOADER) += qcom_mdt_loader.o obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o obj-$(CONFIG_ST_REMOTEPROC) += st_remoteproc.o diff --git a/drivers/remoteproc/qcom_adsp_pil.c b/drivers/remoteproc/qcom_adsp_pil.c new file mode 100644 index 000000000000..914163315091 --- /dev/null +++ b/drivers/remoteproc/qcom_adsp_pil.c @@ -0,0 +1,399 @@ +/* + * Qualcomm ADSP Peripheral Image Loader for MSM8974 and MSM8996 + * + * Copyright (C) 2016 Linaro Ltd + * Copyright (C) 2014 Sony Mobile Communications AB + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qcom_mdt_loader.h" +#include "remoteproc_internal.h" + +#define ADSP_CRASH_REASON_SMEM 423 +#define ADSP_FIRMWARE_NAME "adsp.mdt" +#define ADSP_PAS_ID 1 + +struct qcom_adsp { + struct device *dev; + struct rproc *rproc; + + int wdog_irq; + int fatal_irq; + int ready_irq; + int handover_irq; + int stop_ack_irq; + + struct qcom_smem_state *state; + unsigned stop_bit; + + struct regulator *cx_supply; + + struct completion start_done; + struct completion stop_done; + + phys_addr_t mem_phys; + phys_addr_t mem_reloc; + void *mem_region; + size_t mem_size; +}; + +static int adsp_load(struct rproc *rproc, const struct firmware *fw) +{ + struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; + phys_addr_t fw_addr; + size_t fw_size; + bool relocate; + int ret; + + ret = qcom_scm_pas_init_image(ADSP_PAS_ID, fw->data, fw->size); + if (ret) { + dev_err(&rproc->dev, "invalid firmware metadata\n"); + return ret; + } + + ret = qcom_mdt_parse(fw, &fw_addr, &fw_size, &relocate); + if (ret) { + dev_err(&rproc->dev, "failed to parse mdt header\n"); + return ret; + } + + if (relocate) { + adsp->mem_reloc = fw_addr; + + ret = qcom_scm_pas_mem_setup(ADSP_PAS_ID, adsp->mem_phys, fw_size); + if (ret) { + dev_err(&rproc->dev, "unable to setup memory for image\n"); + return ret; + } + } + + return qcom_mdt_load(rproc, fw, rproc->firmware); +} + +static const struct rproc_fw_ops adsp_fw_ops = { + .find_rsc_table = qcom_mdt_find_rsc_table, + .load = adsp_load, +}; + +static int adsp_start(struct rproc *rproc) +{ + struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; + int ret; + + ret = regulator_enable(adsp->cx_supply); + if (ret) + return ret; + + ret = qcom_scm_pas_auth_and_reset(ADSP_PAS_ID); + if (ret) { + dev_err(adsp->dev, + "failed to authenticate image and release reset\n"); + goto disable_regulators; + } + + ret = wait_for_completion_timeout(&adsp->start_done, + msecs_to_jiffies(5000)); + if (!ret) { + dev_err(adsp->dev, "start timed out\n"); + qcom_scm_pas_shutdown(ADSP_PAS_ID); + ret = -ETIMEDOUT; + goto disable_regulators; + } + + ret = 0; + +disable_regulators: + regulator_disable(adsp->cx_supply); + + return ret; +} + +static int adsp_stop(struct rproc *rproc) +{ + struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; + int ret; + + qcom_smem_state_update_bits(adsp->state, + BIT(adsp->stop_bit), + BIT(adsp->stop_bit)); + + ret = wait_for_completion_timeout(&adsp->stop_done, + msecs_to_jiffies(5000)); + if (ret == 0) + dev_err(adsp->dev, "timed out on wait\n"); + + qcom_smem_state_update_bits(adsp->state, + BIT(adsp->stop_bit), + 0); + + ret = qcom_scm_pas_shutdown(ADSP_PAS_ID); + if (ret) + dev_err(adsp->dev, "failed to shutdown: %d\n", ret); + + return ret; +} + +static void *adsp_da_to_va(struct rproc *rproc, u64 da, int len) +{ + struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; + int offset; + + offset = da - adsp->mem_reloc; + if (offset < 0 || offset + len > adsp->mem_size) + return NULL; + + return adsp->mem_region + offset; +} + +static const struct rproc_ops adsp_ops = { + .start = adsp_start, + .stop = adsp_stop, + .da_to_va = adsp_da_to_va, +}; + +static irqreturn_t adsp_wdog_interrupt(int irq, void *dev) +{ + struct qcom_adsp *adsp = dev; + + rproc_report_crash(adsp->rproc, RPROC_WATCHDOG); + + return IRQ_HANDLED; +} + +static irqreturn_t adsp_fatal_interrupt(int irq, void *dev) +{ + struct qcom_adsp *adsp = dev; + size_t len; + char *msg; + + msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, ADSP_CRASH_REASON_SMEM, &len); + if (!IS_ERR(msg) && len > 0 && msg[0]) + dev_err(adsp->dev, "fatal error received: %s\n", msg); + + rproc_report_crash(adsp->rproc, RPROC_FATAL_ERROR); + + if (!IS_ERR(msg)) + msg[0] = '\0'; + + return IRQ_HANDLED; +} + +static irqreturn_t adsp_ready_interrupt(int irq, void *dev) +{ + return IRQ_HANDLED; +} + +static irqreturn_t adsp_handover_interrupt(int irq, void *dev) +{ + struct qcom_adsp *adsp = dev; + + complete(&adsp->start_done); + + return IRQ_HANDLED; +} + +static irqreturn_t adsp_stop_ack_interrupt(int irq, void *dev) +{ + struct qcom_adsp *adsp = dev; + + complete(&adsp->stop_done); + + return IRQ_HANDLED; +} + +static int adsp_init_regulator(struct qcom_adsp *adsp) +{ + adsp->cx_supply = devm_regulator_get(adsp->dev, "cx"); + if (IS_ERR(adsp->cx_supply)) + return PTR_ERR(adsp->cx_supply); + + regulator_set_load(adsp->cx_supply, 100000); + + return 0; +} + +static int adsp_request_irq(struct qcom_adsp *adsp, + struct platform_device *pdev, + const char *name, + irq_handler_t thread_fn) +{ + int ret; + + ret = platform_get_irq_byname(pdev, name); + if (ret < 0) { + dev_err(&pdev->dev, "no %s IRQ defined\n", name); + return ret; + } + + ret = devm_request_threaded_irq(&pdev->dev, ret, + NULL, thread_fn, + IRQF_ONESHOT, + "adsp", adsp); + if (ret) + dev_err(&pdev->dev, "request %s IRQ failed\n", name); + + return ret; +} + +static int adsp_alloc_memory_region(struct qcom_adsp *adsp) +{ + struct device_node *node; + struct resource r; + int ret; + + node = of_parse_phandle(adsp->dev->of_node, "memory-region", 0); + if (!node) { + dev_err(adsp->dev, "no memory-region specified\n"); + return -EINVAL; + } + + ret = of_address_to_resource(node, 0, &r); + if (ret) + return ret; + + adsp->mem_phys = adsp->mem_reloc = r.start; + adsp->mem_size = resource_size(&r); + adsp->mem_region = devm_ioremap_wc(adsp->dev, adsp->mem_phys, adsp->mem_size); + if (!adsp->mem_region) { + dev_err(adsp->dev, "unable to map memory region: %pa+%zx\n", + &r.start, adsp->mem_size); + return -EBUSY; + } + + return 0; +} + +static int adsp_probe(struct platform_device *pdev) +{ + struct qcom_adsp *adsp; + struct rproc *rproc; + int ret; + + if (!qcom_scm_is_available()) + return -EPROBE_DEFER; + + if (!qcom_scm_pas_supported(ADSP_PAS_ID)) { + dev_err(&pdev->dev, "PAS is not available for ADSP\n"); + return -ENXIO; + } + + rproc = rproc_alloc(&pdev->dev, pdev->name, &adsp_ops, + ADSP_FIRMWARE_NAME, sizeof(*adsp)); + if (!rproc) { + dev_err(&pdev->dev, "unable to allocate remoteproc\n"); + return -ENOMEM; + } + + rproc->fw_ops = &adsp_fw_ops; + + adsp = (struct qcom_adsp *)rproc->priv; + adsp->dev = &pdev->dev; + adsp->rproc = rproc; + platform_set_drvdata(pdev, adsp); + + init_completion(&adsp->start_done); + init_completion(&adsp->stop_done); + + ret = adsp_alloc_memory_region(adsp); + if (ret) + goto free_rproc; + + ret = adsp_init_regulator(adsp); + if (ret) + goto free_rproc; + + ret = adsp_request_irq(adsp, pdev, "wdog", adsp_wdog_interrupt); + if (ret < 0) + goto free_rproc; + adsp->wdog_irq = ret; + + ret = adsp_request_irq(adsp, pdev, "fatal", adsp_fatal_interrupt); + if (ret < 0) + goto free_rproc; + adsp->fatal_irq = ret; + + ret = adsp_request_irq(adsp, pdev, "ready", adsp_ready_interrupt); + if (ret < 0) + goto free_rproc; + adsp->ready_irq = ret; + + ret = adsp_request_irq(adsp, pdev, "handover", adsp_handover_interrupt); + if (ret < 0) + goto free_rproc; + adsp->handover_irq = ret; + + ret = adsp_request_irq(adsp, pdev, "stop-ack", adsp_stop_ack_interrupt); + if (ret < 0) + goto free_rproc; + adsp->stop_ack_irq = ret; + + adsp->state = qcom_smem_state_get(&pdev->dev, "stop", + &adsp->stop_bit); + if (IS_ERR(adsp->state)) { + ret = PTR_ERR(adsp->state); + goto free_rproc; + } + + ret = rproc_add(rproc); + if (ret) + goto free_rproc; + + return 0; + +free_rproc: + rproc_put(rproc); + + return ret; +} + +static int adsp_remove(struct platform_device *pdev) +{ + struct qcom_adsp *adsp = platform_get_drvdata(pdev); + + qcom_smem_state_put(adsp->state); + rproc_del(adsp->rproc); + rproc_put(adsp->rproc); + + return 0; +} + +static const struct of_device_id adsp_of_match[] = { + { .compatible = "qcom,msm8974-adsp-pil" }, + { .compatible = "qcom,msm8996-adsp-pil" }, + { }, +}; + +static struct platform_driver adsp_driver = { + .probe = adsp_probe, + .remove = adsp_remove, + .driver = { + .name = "qcom_adsp_pil", + .of_match_table = adsp_of_match, + }, +}; + +module_platform_driver(adsp_driver); +MODULE_DESCRIPTION("Qualcomm MSM8974/MSM8996 ADSP Peripherial Image Loader"); +MODULE_LICENSE("GPL v2");