]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge tag 'rproc-v4.11' of git://github.com/andersson/remoteproc
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 23 Feb 2017 17:38:10 +0000 (09:38 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 23 Feb 2017 17:38:10 +0000 (09:38 -0800)
Pull remoteproc updates from Bjorn Andersson:
 "This introduces support for booting the dedicated sensor core in the
  Qualcomm MSM8996, updates the Qualcomm ADSP and Hexagon drivers to
  utilize SMD subdevice helpers for properly handle shutdowns and
  restarts of the remoteproc, add virtio support to the ST remoteproc
  and refactor the Qualcomm Hexagon driver to handle variations between
  platforms.

  The support code for parsing, loading and authenticating Qualcomm
  firmware files (MDT) is refactored and move to drivers/soc/qcom, to
  allow for non-remoteproc drivers to utilize this.

  Finally it brings some cleanups to the remoteproc core"

* tag 'rproc-v4.11' of git://github.com/andersson/remoteproc: (27 commits)
  remoteproc: qcom: mdt_loader: Use signed type for offset
  remoteproc: st: add virtio communication support
  remoteproc: st: correct probe error management
  remoteproc: Modify the function names
  remoteproc: Reduce asynchronous request_firmware to auto-boot only
  remoteproc: Drop qcom_scm_pas_supported() from adsp_probe()
  MAINTAINERS: Add missing rpmsg include path
  remoteproc: qcom: Use common SMD edge handler
  remoteproc: qcom: wcnss: Make SMD handling common
  remoteproc: Move qcom_mdt_loader into drivers/soc/qcom
  remoteproc: qcom: mdt_loader: Refactor MDT loader
  remoteproc: qcom: mdt_loader: Don't overwrite firmware object
  remoteproc: qcom: Extract non-mdt related helper
  remoteproc: qcom: q6v5: Decouple driver from MDT loader
  remoteproc: qcom: q6v5: Remove mss supply from 8916
  remoteproc: qcom: fix initializers for qcom_mss_reg_res array
  remoteproc: Drop firmware_loading_complete
  remoteproc: Add RPROC_DELETED state
  remoteproc: Move rproc_delete_debug_dir() to rproc_del()
  remoteproc: qcom: Add SLPI rproc support to load and boot slpi proc.
  ...

24 files changed:
Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt
Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt
MAINTAINERS
drivers/remoteproc/Kconfig
drivers/remoteproc/Makefile
drivers/remoteproc/da8xx_remoteproc.c
drivers/remoteproc/omap_remoteproc.c
drivers/remoteproc/qcom_adsp_pil.c
drivers/remoteproc/qcom_common.c [new file with mode: 0644]
drivers/remoteproc/qcom_common.h [new file with mode: 0644]
drivers/remoteproc/qcom_mdt_loader.c [deleted file]
drivers/remoteproc/qcom_mdt_loader.h [deleted file]
drivers/remoteproc/qcom_q6v5_pil.c
drivers/remoteproc/qcom_wcnss.c
drivers/remoteproc/remoteproc_core.c
drivers/remoteproc/remoteproc_sysfs.c
drivers/remoteproc/st_remoteproc.c
drivers/remoteproc/st_slim_rproc.c
drivers/remoteproc/wkup_m3_rproc.c
drivers/soc/qcom/Kconfig
drivers/soc/qcom/Makefile
drivers/soc/qcom/mdt_loader.c [new file with mode: 0644]
include/linux/remoteproc.h
include/linux/soc/qcom/mdt_loader.h [new file with mode: 0644]

index b85885a298d83e43b753fbb2c46e06154a3e181f..75ad7b8df0b1d782a2edce8ff80168a25a483556 100644 (file)
@@ -9,6 +9,7 @@ on the Qualcomm ADSP Hexagon core.
        Definition: must be one of:
                    "qcom,msm8974-adsp-pil"
                    "qcom,msm8996-adsp-pil"
+                   "qcom,msm8996-slpi-pil"
 
 - interrupts-extended:
        Usage: required
@@ -24,13 +25,13 @@ on the Qualcomm ADSP Hexagon core.
 - clocks:
        Usage: required
        Value type: <prop-encoded-array>
-       Definition: reference to the xo clock to be held on behalf of the
-                   booting Hexagon core
+       Definition: reference to the xo clock and optionally aggre2 clock to be
+                   held on behalf of the booting Hexagon core
 
 - clock-names:
        Usage: required
        Value type: <stringlist>
-       Definition: must be "xo"
+       Definition: must be "xo" and optionally include "aggre2"
 
 - cx-supply:
        Usage: required
@@ -38,6 +39,12 @@ on the Qualcomm ADSP Hexagon core.
        Definition: reference to the regulator to be held on behalf of the
                    booting Hexagon core
 
+- px-supply:
+       Usage: required
+       Value type: <phandle>
+       Definition: reference to the px regulator to be held on behalf of the
+                   booting Hexagon core
+
 - memory-region:
        Usage: required
        Value type: <phandle>
@@ -96,3 +103,31 @@ ADSP, as it is found on MSM8974 boards.
                        qcom,smd-edge = <1>;
                };
        };
+
+The following example describes the resources needed to boot control the
+SLPI, as it is found on MSM8996 boards.
+
+       slpi {
+               compatible = "qcom,msm8996-slpi-pil";
+               interrupts-extended = <&intc 0 390 IRQ_TYPE_EDGE_RISING>,
+                                     <&slpi_smp2p_in 0 IRQ_TYPE_EDGE_RISING>,
+                                     <&slpi_smp2p_in 1 IRQ_TYPE_EDGE_RISING>,
+                                     <&slpi_smp2p_in 2 IRQ_TYPE_EDGE_RISING>,
+                                     <&slpi_smp2p_in 3 IRQ_TYPE_EDGE_RISING>;
+               interrupt-names = "wdog",
+                                 "fatal",
+                                 "ready",
+                                 "handover",
+                                 "stop-ack";
+
+               clocks = <&rpmcc MSM8996_RPM_SMD_XO_CLK_SRC>,
+                        <&rpmcc MSM8996_RPM_SMD_AGGR2_NOC_CLK>;
+               clock-names = "xo", "aggre2";
+
+               cx-supply = <&pm8994_l26>;
+               px-supply = <&pm8994_lvs2>;
+
+               memory-region = <&slpi_region>;
+               qcom,smem-states = <&slpi_smp2p_out 0>;
+               qcom,smem-state-names = "stop";
+        };
index 57cb49ec55caa0a71c1fbdbc77e74aecc127b656..92347fe6890e2b5d6f1e72f3b8ac43fd245d832a 100644 (file)
@@ -7,7 +7,9 @@ on the Qualcomm Hexagon core.
        Usage: required
        Value type: <string>
        Definition: must be one of:
-                   "qcom,q6v5-pil"
+                   "qcom,q6v5-pil",
+                   "qcom,msm8916-mss-pil",
+                   "qcom,msm8974-mss-pil"
 
 - reg:
        Usage: required
index d461a94d2eb0dbd2a6bfde682a771ef8dfbc9346..5a5fa41ac96145b4d72c09dfba0ed054d9e74202 100644 (file)
@@ -10548,6 +10548,7 @@ S:      Maintained
 F:     drivers/rpmsg/
 F:     Documentation/rpmsg.txt
 F:     include/linux/rpmsg.h
+F:     include/linux/rpmsg/
 
 RENESAS CLOCK DRIVERS
 M:     Geert Uytterhoeven <geert+renesas@glider.be>
index 8f9cf0bc571ccb94ff67729d440be665f90a58ab..65f86bc24c07c7032726700e09a1d9ef3cdfb3c2 100644 (file)
@@ -7,6 +7,9 @@ config REMOTEPROC
        select FW_LOADER
        select VIRTIO
        select VIRTUALIZATION
+       help
+         Support for remote processors (such as DSP coprocessors). These
+         are mainly used on embedded systems.
 
 if REMOTEPROC
 
@@ -25,11 +28,11 @@ config OMAP_REMOTEPROC
 
          Currently only supported on OMAP4.
 
-         Usually you want to say y here, in order to enable multimedia
+         Usually you want to say Y here, in order to enable multimedia
          use-cases to run on your platform (multimedia codecs are
          offloaded to remote DSP processors using this framework).
 
-         It's safe to say n here if you're not interested in multimedia
+         It's safe to say N here if you're not interested in multimedia
          offloading or just want a bare minimum kernel.
 
 config WKUP_M3_RPROC
@@ -73,14 +76,16 @@ config QCOM_ADSP_PIL
        depends on OF && ARCH_QCOM
        depends on REMOTEPROC
        depends on QCOM_SMEM
+       depends on QCOM_SMD || (COMPILE_TEST && QCOM_SMD=n)
        select MFD_SYSCON
        select QCOM_MDT_LOADER
+       select QCOM_RPROC_COMMON
        select QCOM_SCM
        help
          Say y here to support the TrustZone based Peripherial Image Loader
          for the Qualcomm ADSP remote processors.
 
-config QCOM_MDT_LOADER
+config QCOM_RPROC_COMMON
        tristate
 
 config QCOM_Q6V5_PIL
@@ -88,8 +93,9 @@ config QCOM_Q6V5_PIL
        depends on OF && ARCH_QCOM
        depends on QCOM_SMEM
        depends on REMOTEPROC
+       depends on QCOM_SMD || (COMPILE_TEST && QCOM_SMD=n)
        select MFD_SYSCON
-       select QCOM_MDT_LOADER
+       select QCOM_RPROC_COMMON
        select QCOM_SCM
        help
          Say y here to support the Qualcomm Peripherial Image Loader for the
@@ -102,6 +108,7 @@ config QCOM_WCNSS_PIL
        depends on QCOM_SMEM
        depends on REMOTEPROC
        select QCOM_MDT_LOADER
+       select QCOM_RPROC_COMMON
        select QCOM_SCM
        help
          Say y here to support the Peripheral Image Loader for the Qualcomm
@@ -111,6 +118,9 @@ config ST_REMOTEPROC
        tristate "ST remoteproc support"
        depends on ARCH_STI
        depends on REMOTEPROC
+       select MAILBOX
+       select STI_MBOX
+       select RPMSG_VIRTIO
        help
          Say y here to support ST's adjunct processors via the remote
          processor framework.
index 0938ea3c41ba617f638e3a4cb4f6834866a2b088..ffc5e430df279a451a7d96313b3fe8107b301a02 100644 (file)
@@ -12,7 +12,7 @@ obj-$(CONFIG_OMAP_REMOTEPROC)         += omap_remoteproc.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_RPROC_COMMON)                += qcom_common.o
 obj-$(CONFIG_QCOM_Q6V5_PIL)            += qcom_q6v5_pil.o
 obj-$(CONFIG_QCOM_WCNSS_PIL)           += qcom_wcnss_pil.o
 qcom_wcnss_pil-y                       += qcom_wcnss.o
index 1afac8f31be07b3738bc622af46f4dac54a988a5..3814de28599c963699ed401c3af135c973027e22 100644 (file)
@@ -151,7 +151,7 @@ static void da8xx_rproc_kick(struct rproc *rproc, int vqid)
        writel(SYSCFG_CHIPSIG2, drproc->chipsig);
 }
 
-static struct rproc_ops da8xx_rproc_ops = {
+static const struct rproc_ops da8xx_rproc_ops = {
        .start = da8xx_rproc_start,
        .stop = da8xx_rproc_stop,
        .kick = da8xx_rproc_kick,
index fa63bf2eb885991b79e8b9a35425091e18460f66..a96ce9083f7faba98f460aa8cfdde8a6f55dd0d3 100644 (file)
@@ -177,7 +177,7 @@ static int omap_rproc_stop(struct rproc *rproc)
        return 0;
 }
 
-static struct rproc_ops omap_rproc_ops = {
+static const struct rproc_ops omap_rproc_ops = {
        .start          = omap_rproc_start,
        .stop           = omap_rproc_stop,
        .kick           = omap_rproc_kick,
index 43a4ed2f346cf6370eab882fcbe07d4b4deda39e..49fe2f807e1d941e804f75df9f276f894079fa7f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Qualcomm ADSP Peripheral Image Loader for MSM8974 and MSM8996
+ * Qualcomm ADSP/SLPI Peripheral Image Loader for MSM8974 and MSM8996
  *
  * Copyright (C) 2016 Linaro Ltd
  * Copyright (C) 2014 Sony Mobile Communications AB
 #include <linux/qcom_scm.h>
 #include <linux/regulator/consumer.h>
 #include <linux/remoteproc.h>
+#include <linux/soc/qcom/mdt_loader.h>
 #include <linux/soc/qcom/smem.h>
 #include <linux/soc/qcom/smem_state.h>
 
-#include "qcom_mdt_loader.h"
+#include "qcom_common.h"
 #include "remoteproc_internal.h"
 
-#define ADSP_CRASH_REASON_SMEM         423
-#define ADSP_FIRMWARE_NAME             "adsp.mdt"
-#define ADSP_PAS_ID                    1
+struct adsp_data {
+       int crash_reason_smem;
+       const char *firmware_name;
+       int pas_id;
+       bool has_aggre2_clk;
+};
 
 struct qcom_adsp {
        struct device *dev;
@@ -50,8 +54,14 @@ struct qcom_adsp {
        unsigned stop_bit;
 
        struct clk *xo;
+       struct clk *aggre2_clk;
 
        struct regulator *cx_supply;
+       struct regulator *px_supply;
+
+       int pas_id;
+       int crash_reason_smem;
+       bool has_aggre2_clk;
 
        struct completion start_done;
        struct completion stop_done;
@@ -60,39 +70,16 @@ struct qcom_adsp {
        phys_addr_t mem_reloc;
        void *mem_region;
        size_t mem_size;
+
+       struct qcom_rproc_subdev smd_subdev;
 };
 
 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);
+       return qcom_mdt_load(adsp->dev, fw, rproc->firmware, adsp->pas_id,
+                            adsp->mem_region, adsp->mem_phys, adsp->mem_size);
 }
 
 static const struct rproc_fw_ops adsp_fw_ops = {
@@ -109,31 +96,43 @@ static int adsp_start(struct rproc *rproc)
        if (ret)
                return ret;
 
+       ret = clk_prepare_enable(adsp->aggre2_clk);
+       if (ret)
+               goto disable_xo_clk;
+
        ret = regulator_enable(adsp->cx_supply);
        if (ret)
-               goto disable_clocks;
+               goto disable_aggre2_clk;
+
+       ret = regulator_enable(adsp->px_supply);
+       if (ret)
+               goto disable_cx_supply;
 
-       ret = qcom_scm_pas_auth_and_reset(ADSP_PAS_ID);
+       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;
+               goto disable_px_supply;
        }
 
        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);
+               qcom_scm_pas_shutdown(adsp->pas_id);
                ret = -ETIMEDOUT;
-               goto disable_regulators;
+               goto disable_px_supply;
        }
 
        ret = 0;
 
-disable_regulators:
+disable_px_supply:
+       regulator_disable(adsp->px_supply);
+disable_cx_supply:
        regulator_disable(adsp->cx_supply);
-disable_clocks:
+disable_aggre2_clk:
+       clk_disable_unprepare(adsp->aggre2_clk);
+disable_xo_clk:
        clk_disable_unprepare(adsp->xo);
 
        return ret;
@@ -157,7 +156,7 @@ static int adsp_stop(struct rproc *rproc)
                                    BIT(adsp->stop_bit),
                                    0);
 
-       ret = qcom_scm_pas_shutdown(ADSP_PAS_ID);
+       ret = qcom_scm_pas_shutdown(adsp->pas_id);
        if (ret)
                dev_err(adsp->dev, "failed to shutdown: %d\n", ret);
 
@@ -197,7 +196,7 @@ static irqreturn_t adsp_fatal_interrupt(int irq, void *dev)
        size_t len;
        char *msg;
 
-       msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, ADSP_CRASH_REASON_SMEM, &len);
+       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);
 
@@ -244,6 +243,17 @@ static int adsp_init_clock(struct qcom_adsp *adsp)
                return ret;
        }
 
+       if (adsp->has_aggre2_clk) {
+               adsp->aggre2_clk = devm_clk_get(adsp->dev, "aggre2");
+               if (IS_ERR(adsp->aggre2_clk)) {
+                       ret = PTR_ERR(adsp->aggre2_clk);
+                       if (ret != -EPROBE_DEFER)
+                               dev_err(adsp->dev,
+                                       "failed to get aggre2 clock");
+                       return ret;
+               }
+       }
+
        return 0;
 }
 
@@ -255,6 +265,10 @@ static int adsp_init_regulator(struct qcom_adsp *adsp)
 
        regulator_set_load(adsp->cx_supply, 100000);
 
+       adsp->px_supply = devm_regulator_get(adsp->dev, "px");
+       if (IS_ERR(adsp->px_supply))
+               return PTR_ERR(adsp->px_supply);
+
        return 0;
 }
 
@@ -311,20 +325,20 @@ static int adsp_alloc_memory_region(struct qcom_adsp *adsp)
 
 static int adsp_probe(struct platform_device *pdev)
 {
+       const struct adsp_data *desc;
        struct qcom_adsp *adsp;
        struct rproc *rproc;
        int ret;
 
+       desc = of_device_get_match_data(&pdev->dev);
+       if (!desc)
+               return -EINVAL;
+
        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));
+                           desc->firmware_name, sizeof(*adsp));
        if (!rproc) {
                dev_err(&pdev->dev, "unable to allocate remoteproc\n");
                return -ENOMEM;
@@ -335,6 +349,9 @@ static int adsp_probe(struct platform_device *pdev)
        adsp = (struct qcom_adsp *)rproc->priv;
        adsp->dev = &pdev->dev;
        adsp->rproc = rproc;
+       adsp->pas_id = desc->pas_id;
+       adsp->crash_reason_smem = desc->crash_reason_smem;
+       adsp->has_aggre2_clk = desc->has_aggre2_clk;
        platform_set_drvdata(pdev, adsp);
 
        init_completion(&adsp->start_done);
@@ -384,6 +401,8 @@ static int adsp_probe(struct platform_device *pdev)
                goto free_rproc;
        }
 
+       qcom_add_smd_subdev(rproc, &adsp->smd_subdev);
+
        ret = rproc_add(rproc);
        if (ret)
                goto free_rproc;
@@ -402,14 +421,31 @@ static int adsp_remove(struct platform_device *pdev)
 
        qcom_smem_state_put(adsp->state);
        rproc_del(adsp->rproc);
+
+       qcom_remove_smd_subdev(adsp->rproc, &adsp->smd_subdev);
        rproc_free(adsp->rproc);
 
        return 0;
 }
 
+static const struct adsp_data adsp_resource_init = {
+               .crash_reason_smem = 423,
+               .firmware_name = "adsp.mdt",
+               .pas_id = 1,
+               .has_aggre2_clk = false,
+};
+
+static const struct adsp_data slpi_resource_init = {
+               .crash_reason_smem = 424,
+               .firmware_name = "slpi.mdt",
+               .pas_id = 12,
+               .has_aggre2_clk = true,
+};
+
 static const struct of_device_id adsp_of_match[] = {
-       { .compatible = "qcom,msm8974-adsp-pil" },
-       { .compatible = "qcom,msm8996-adsp-pil" },
+       { .compatible = "qcom,msm8974-adsp-pil", .data = &adsp_resource_init},
+       { .compatible = "qcom,msm8996-adsp-pil", .data = &adsp_resource_init},
+       { .compatible = "qcom,msm8996-slpi-pil", .data = &slpi_resource_init},
        { },
 };
 MODULE_DEVICE_TABLE(of, adsp_of_match);
diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c
new file mode 100644 (file)
index 0000000..bb90481
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Qualcomm Peripheral Image Loader helpers
+ *
+ * Copyright (C) 2016 Linaro Ltd
+ * Copyright (C) 2015 Sony Mobile Communications Inc
+ * 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 <linux/firmware.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/remoteproc.h>
+#include <linux/rpmsg/qcom_smd.h>
+
+#include "remoteproc_internal.h"
+#include "qcom_common.h"
+
+#define to_smd_subdev(d) container_of(d, struct qcom_rproc_subdev, subdev)
+
+/**
+ * qcom_mdt_find_rsc_table() - provide dummy resource table for remoteproc
+ * @rproc:     remoteproc handle
+ * @fw:                firmware header
+ * @tablesz:   outgoing size of the table
+ *
+ * Returns a dummy table.
+ */
+struct resource_table *qcom_mdt_find_rsc_table(struct rproc *rproc,
+                                              const struct firmware *fw,
+                                              int *tablesz)
+{
+       static struct resource_table table = { .ver = 1, };
+
+       *tablesz = sizeof(table);
+       return &table;
+}
+EXPORT_SYMBOL_GPL(qcom_mdt_find_rsc_table);
+
+static int smd_subdev_probe(struct rproc_subdev *subdev)
+{
+       struct qcom_rproc_subdev *smd = to_smd_subdev(subdev);
+
+       smd->edge = qcom_smd_register_edge(smd->dev, smd->node);
+
+       return IS_ERR(smd->edge) ? PTR_ERR(smd->edge) : 0;
+}
+
+static void smd_subdev_remove(struct rproc_subdev *subdev)
+{
+       struct qcom_rproc_subdev *smd = to_smd_subdev(subdev);
+
+       qcom_smd_unregister_edge(smd->edge);
+       smd->edge = NULL;
+}
+
+/**
+ * qcom_add_smd_subdev() - try to add a SMD subdevice to rproc
+ * @rproc:     rproc handle to parent the subdevice
+ * @smd:       reference to a Qualcomm subdev context
+ */
+void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd)
+{
+       struct device *dev = &rproc->dev;
+
+       smd->node = of_get_child_by_name(dev->parent->of_node, "smd-edge");
+       if (!smd->node)
+               return;
+
+       smd->dev = dev;
+       rproc_add_subdev(rproc, &smd->subdev, smd_subdev_probe, smd_subdev_remove);
+}
+EXPORT_SYMBOL_GPL(qcom_add_smd_subdev);
+
+/**
+ * qcom_remove_smd_subdev() - remove the smd subdevice from rproc
+ * @rproc:     rproc handle
+ * @smd:       the SMD subdevice to remove
+ */
+void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd)
+{
+       rproc_remove_subdev(rproc, &smd->subdev);
+       of_node_put(smd->node);
+}
+EXPORT_SYMBOL_GPL(qcom_remove_smd_subdev);
+
+MODULE_DESCRIPTION("Qualcomm Remoteproc helper driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/remoteproc/qcom_common.h b/drivers/remoteproc/qcom_common.h
new file mode 100644 (file)
index 0000000..db5c826
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef __RPROC_QCOM_COMMON_H__
+#define __RPROC_QCOM_COMMON_H__
+
+#include <linux/remoteproc.h>
+#include "remoteproc_internal.h"
+
+struct qcom_rproc_subdev {
+       struct rproc_subdev subdev;
+
+       struct device *dev;
+       struct device_node *node;
+       struct qcom_smd_edge *edge;
+};
+
+struct resource_table *qcom_mdt_find_rsc_table(struct rproc *rproc,
+                                              const struct firmware *fw,
+                                              int *tablesz);
+
+void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd);
+void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd);
+
+#endif
diff --git a/drivers/remoteproc/qcom_mdt_loader.c b/drivers/remoteproc/qcom_mdt_loader.c
deleted file mode 100644 (file)
index 2ff18cd..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Qualcomm Peripheral Image Loader
- *
- * Copyright (C) 2016 Linaro Ltd
- * Copyright (C) 2015 Sony Mobile Communications Inc
- * 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 <linux/elf.h>
-#include <linux/firmware.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/remoteproc.h>
-#include <linux/sizes.h>
-#include <linux/slab.h>
-
-#include "remoteproc_internal.h"
-#include "qcom_mdt_loader.h"
-
-/**
- * qcom_mdt_find_rsc_table() - provide dummy resource table for remoteproc
- * @rproc:     remoteproc handle
- * @fw:                firmware header
- * @tablesz:   outgoing size of the table
- *
- * Returns a dummy table.
- */
-struct resource_table *qcom_mdt_find_rsc_table(struct rproc *rproc,
-                                              const struct firmware *fw,
-                                              int *tablesz)
-{
-       static struct resource_table table = { .ver = 1, };
-
-       *tablesz = sizeof(table);
-       return &table;
-}
-EXPORT_SYMBOL_GPL(qcom_mdt_find_rsc_table);
-
-/**
- * qcom_mdt_parse() - extract useful parameters from the mdt header
- * @fw:                firmware handle
- * @fw_addr:   optional reference for base of the firmware's memory region
- * @fw_size:   optional reference for size of the firmware's memory region
- * @fw_relocate: optional reference for flagging if the firmware is relocatable
- *
- * Returns 0 on success, negative errno otherwise.
- */
-int qcom_mdt_parse(const struct firmware *fw, phys_addr_t *fw_addr,
-                  size_t *fw_size, bool *fw_relocate)
-{
-       const struct elf32_phdr *phdrs;
-       const struct elf32_phdr *phdr;
-       const struct elf32_hdr *ehdr;
-       phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX;
-       phys_addr_t max_addr = 0;
-       bool relocate = false;
-       int i;
-
-       ehdr = (struct elf32_hdr *)fw->data;
-       phdrs = (struct elf32_phdr *)(ehdr + 1);
-
-       for (i = 0; i < ehdr->e_phnum; i++) {
-               phdr = &phdrs[i];
-
-               if (phdr->p_type != PT_LOAD)
-                       continue;
-
-               if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH)
-                       continue;
-
-               if (!phdr->p_memsz)
-                       continue;
-
-               if (phdr->p_flags & QCOM_MDT_RELOCATABLE)
-                       relocate = true;
-
-               if (phdr->p_paddr < min_addr)
-                       min_addr = phdr->p_paddr;
-
-               if (phdr->p_paddr + phdr->p_memsz > max_addr)
-                       max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K);
-       }
-
-       if (fw_addr)
-               *fw_addr = min_addr;
-       if (fw_size)
-               *fw_size = max_addr - min_addr;
-       if (fw_relocate)
-               *fw_relocate = relocate;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(qcom_mdt_parse);
-
-/**
- * qcom_mdt_load() - load the firmware which header is defined in fw
- * @rproc:     rproc handle
- * @fw:                frimware object for the header
- * @firmware:  filename of the firmware, for building .bXX names
- *
- * Returns 0 on success, negative errno otherwise.
- */
-int qcom_mdt_load(struct rproc *rproc,
-                 const struct firmware *fw,
-                 const char *firmware)
-{
-       const struct elf32_phdr *phdrs;
-       const struct elf32_phdr *phdr;
-       const struct elf32_hdr *ehdr;
-       size_t fw_name_len;
-       char *fw_name;
-       void *ptr;
-       int ret;
-       int i;
-
-       ehdr = (struct elf32_hdr *)fw->data;
-       phdrs = (struct elf32_phdr *)(ehdr + 1);
-
-       fw_name_len = strlen(firmware);
-       if (fw_name_len <= 4)
-               return -EINVAL;
-
-       fw_name = kstrdup(firmware, GFP_KERNEL);
-       if (!fw_name)
-               return -ENOMEM;
-
-       for (i = 0; i < ehdr->e_phnum; i++) {
-               phdr = &phdrs[i];
-
-               if (phdr->p_type != PT_LOAD)
-                       continue;
-
-               if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH)
-                       continue;
-
-               if (!phdr->p_memsz)
-                       continue;
-
-               ptr = rproc_da_to_va(rproc, phdr->p_paddr, phdr->p_memsz);
-               if (!ptr) {
-                       dev_err(&rproc->dev, "segment outside memory range\n");
-                       ret = -EINVAL;
-                       break;
-               }
-
-               if (phdr->p_filesz) {
-                       sprintf(fw_name + fw_name_len - 3, "b%02d", i);
-                       ret = request_firmware(&fw, fw_name, &rproc->dev);
-                       if (ret) {
-                               dev_err(&rproc->dev, "failed to load %s\n",
-                                       fw_name);
-                               break;
-                       }
-
-                       memcpy(ptr, fw->data, fw->size);
-
-                       release_firmware(fw);
-               }
-
-               if (phdr->p_memsz > phdr->p_filesz)
-                       memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz);
-       }
-
-       kfree(fw_name);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(qcom_mdt_load);
-
-MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/remoteproc/qcom_mdt_loader.h b/drivers/remoteproc/qcom_mdt_loader.h
deleted file mode 100644 (file)
index c5d7122..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef __QCOM_MDT_LOADER_H__
-#define __QCOM_MDT_LOADER_H__
-
-#define QCOM_MDT_TYPE_MASK     (7 << 24)
-#define QCOM_MDT_TYPE_HASH     (2 << 24)
-#define QCOM_MDT_RELOCATABLE   BIT(27)
-
-struct resource_table * qcom_mdt_find_rsc_table(struct rproc *rproc, const struct firmware *fw, int *tablesz);
-int qcom_mdt_load(struct rproc *rproc, const struct firmware *fw, const char *fw_name);
-
-int qcom_mdt_parse(const struct firmware *fw, phys_addr_t *fw_addr, size_t *fw_size, bool *fw_relocate);
-
-#endif
index b08989b48df7bad5b4e9fb0ef11f81ee369363b9..8fd697a3cf8f973e8ca616018956abf89883361b 100644 (file)
 #include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/of_address.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 #include <linux/remoteproc.h>
 #include <linux/reset.h>
+#include <linux/soc/qcom/mdt_loader.h>
 #include <linux/soc/qcom/smem.h>
 #include <linux/soc/qcom/smem_state.h>
 
 #include "remoteproc_internal.h"
-#include "qcom_mdt_loader.h"
+#include "qcom_common.h"
 
 #include <linux/qcom_scm.h>
 
-#define MBA_FIRMWARE_NAME              "mba.b00"
-#define MPSS_FIRMWARE_NAME             "modem.mdt"
-
 #define MPSS_CRASH_REASON_SMEM         421
 
 /* RMB Status Register Values */
 #define QDSS_BHS_ON                    BIT(21)
 #define QDSS_LDO_BYP                   BIT(22)
 
+struct reg_info {
+       struct regulator *reg;
+       int uV;
+       int uA;
+};
+
+struct qcom_mss_reg_res {
+       const char *supply;
+       int uV;
+       int uA;
+};
+
+struct rproc_hexagon_res {
+       const char *hexagon_mba_image;
+       struct qcom_mss_reg_res *proxy_supply;
+       struct qcom_mss_reg_res *active_supply;
+       char **proxy_clk_names;
+       char **active_clk_names;
+};
+
 struct q6v5 {
        struct device *dev;
        struct rproc *rproc;
@@ -110,11 +129,15 @@ struct q6v5 {
        struct qcom_smem_state *state;
        unsigned stop_bit;
 
-       struct regulator_bulk_data supply[4];
+       struct clk *active_clks[8];
+       struct clk *proxy_clks[4];
+       int active_clk_count;
+       int proxy_clk_count;
 
-       struct clk *ahb_clk;
-       struct clk *axi_clk;
-       struct clk *rom_clk;
+       struct reg_info active_regs[1];
+       struct reg_info proxy_regs[3];
+       int active_reg_count;
+       int proxy_reg_count;
 
        struct completion start_done;
        struct completion stop_done;
@@ -128,65 +151,141 @@ struct q6v5 {
        phys_addr_t mpss_reloc;
        void *mpss_region;
        size_t mpss_size;
-};
 
-enum {
-       Q6V5_SUPPLY_CX,
-       Q6V5_SUPPLY_MX,
-       Q6V5_SUPPLY_MSS,
-       Q6V5_SUPPLY_PLL,
+       struct qcom_rproc_subdev smd_subdev;
 };
 
-static int q6v5_regulator_init(struct q6v5 *qproc)
+static int q6v5_regulator_init(struct device *dev, struct reg_info *regs,
+                              const struct qcom_mss_reg_res *reg_res)
 {
-       int ret;
+       int rc;
+       int i;
 
-       qproc->supply[Q6V5_SUPPLY_CX].supply = "cx";
-       qproc->supply[Q6V5_SUPPLY_MX].supply = "mx";
-       qproc->supply[Q6V5_SUPPLY_MSS].supply = "mss";
-       qproc->supply[Q6V5_SUPPLY_PLL].supply = "pll";
+       if (!reg_res)
+               return 0;
+
+       for (i = 0; reg_res[i].supply; i++) {
+               regs[i].reg = devm_regulator_get(dev, reg_res[i].supply);
+               if (IS_ERR(regs[i].reg)) {
+                       rc = PTR_ERR(regs[i].reg);
+                       if (rc != -EPROBE_DEFER)
+                               dev_err(dev, "Failed to get %s\n regulator",
+                                       reg_res[i].supply);
+                       return rc;
+               }
 
-       ret = devm_regulator_bulk_get(qproc->dev,
-                                     ARRAY_SIZE(qproc->supply), qproc->supply);
-       if (ret < 0) {
-               dev_err(qproc->dev, "failed to get supplies\n");
-               return ret;
+               regs[i].uV = reg_res[i].uV;
+               regs[i].uA = reg_res[i].uA;
        }
 
-       regulator_set_load(qproc->supply[Q6V5_SUPPLY_CX].consumer, 100000);
-       regulator_set_load(qproc->supply[Q6V5_SUPPLY_MSS].consumer, 100000);
-       regulator_set_load(qproc->supply[Q6V5_SUPPLY_PLL].consumer, 10000);
+       return i;
+}
+
+static int q6v5_regulator_enable(struct q6v5 *qproc,
+                                struct reg_info *regs, int count)
+{
+       int ret;
+       int i;
+
+       for (i = 0; i < count; i++) {
+               if (regs[i].uV > 0) {
+                       ret = regulator_set_voltage(regs[i].reg,
+                                       regs[i].uV, INT_MAX);
+                       if (ret) {
+                               dev_err(qproc->dev,
+                                       "Failed to request voltage for %d.\n",
+                                               i);
+                               goto err;
+                       }
+               }
+
+               if (regs[i].uA > 0) {
+                       ret = regulator_set_load(regs[i].reg,
+                                                regs[i].uA);
+                       if (ret < 0) {
+                               dev_err(qproc->dev,
+                                       "Failed to set regulator mode\n");
+                               goto err;
+                       }
+               }
+
+               ret = regulator_enable(regs[i].reg);
+               if (ret) {
+                       dev_err(qproc->dev, "Regulator enable failed\n");
+                       goto err;
+               }
+       }
 
        return 0;
+err:
+       for (; i >= 0; i--) {
+               if (regs[i].uV > 0)
+                       regulator_set_voltage(regs[i].reg, 0, INT_MAX);
+
+               if (regs[i].uA > 0)
+                       regulator_set_load(regs[i].reg, 0);
+
+               regulator_disable(regs[i].reg);
+       }
+
+       return ret;
 }
 
-static int q6v5_regulator_enable(struct q6v5 *qproc)
+static void q6v5_regulator_disable(struct q6v5 *qproc,
+                                  struct reg_info *regs, int count)
 {
-       struct regulator *mss = qproc->supply[Q6V5_SUPPLY_MSS].consumer;
-       struct regulator *mx = qproc->supply[Q6V5_SUPPLY_MX].consumer;
-       int ret;
+       int i;
 
-       /* TODO: Q6V5_SUPPLY_CX is supposed to be set to super-turbo here */
+       for (i = 0; i < count; i++) {
+               if (regs[i].uV > 0)
+                       regulator_set_voltage(regs[i].reg, 0, INT_MAX);
 
-       ret = regulator_set_voltage(mx, 1050000, INT_MAX);
-       if (ret)
-               return ret;
+               if (regs[i].uA > 0)
+                       regulator_set_load(regs[i].reg, 0);
+
+               regulator_disable(regs[i].reg);
+       }
+}
 
-       regulator_set_voltage(mss, 1000000, 1150000);
+static int q6v5_clk_enable(struct device *dev,
+                          struct clk **clks, int count)
+{
+       int rc;
+       int i;
 
-       return regulator_bulk_enable(ARRAY_SIZE(qproc->supply), qproc->supply);
+       for (i = 0; i < count; i++) {
+               rc = clk_prepare_enable(clks[i]);
+               if (rc) {
+                       dev_err(dev, "Clock enable failed\n");
+                       goto err;
+               }
+       }
+
+       return 0;
+err:
+       for (i--; i >= 0; i--)
+               clk_disable_unprepare(clks[i]);
+
+       return rc;
 }
 
-static void q6v5_regulator_disable(struct q6v5 *qproc)
+static void q6v5_clk_disable(struct device *dev,
+                            struct clk **clks, int count)
 {
-       struct regulator *mss = qproc->supply[Q6V5_SUPPLY_MSS].consumer;
-       struct regulator *mx = qproc->supply[Q6V5_SUPPLY_MX].consumer;
+       int i;
+
+       for (i = 0; i < count; i++)
+               clk_disable_unprepare(clks[i]);
+}
 
-       /* TODO: Q6V5_SUPPLY_CX corner votes should be released */
+static struct resource_table *q6v5_find_rsc_table(struct rproc *rproc,
+                                                 const struct firmware *fw,
+                                                 int *tablesz)
+{
+       static struct resource_table table = { .ver = 1, };
 
-       regulator_bulk_disable(ARRAY_SIZE(qproc->supply), qproc->supply);
-       regulator_set_voltage(mx, 0, INT_MAX);
-       regulator_set_voltage(mss, 0, 1150000);
+       *tablesz = sizeof(table);
+       return &table;
 }
 
 static int q6v5_load(struct rproc *rproc, const struct firmware *fw)
@@ -199,7 +298,7 @@ static int q6v5_load(struct rproc *rproc, const struct firmware *fw)
 }
 
 static const struct rproc_fw_ops q6v5_fw_ops = {
-       .find_rsc_table = qcom_mdt_find_rsc_table,
+       .find_rsc_table = q6v5_find_rsc_table,
        .load = q6v5_load,
 };
 
@@ -376,45 +475,109 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw)
        return ret < 0 ? ret : 0;
 }
 
-static int q6v5_mpss_validate(struct q6v5 *qproc, const struct firmware *fw)
+static bool q6v5_phdr_valid(const struct elf32_phdr *phdr)
+{
+       if (phdr->p_type != PT_LOAD)
+               return false;
+
+       if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH)
+               return false;
+
+       if (!phdr->p_memsz)
+               return false;
+
+       return true;
+}
+
+static int q6v5_mpss_load(struct q6v5 *qproc)
 {
        const struct elf32_phdr *phdrs;
        const struct elf32_phdr *phdr;
+       const struct firmware *seg_fw;
+       const struct firmware *fw;
        struct elf32_hdr *ehdr;
+       phys_addr_t mpss_reloc;
        phys_addr_t boot_addr;
-       phys_addr_t fw_addr;
-       bool relocate;
+       phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX;
+       phys_addr_t max_addr = 0;
+       bool relocate = false;
+       char seg_name[10];
+       ssize_t offset;
        size_t size;
+       void *ptr;
        int ret;
        int i;
 
-       ret = qcom_mdt_parse(fw, &fw_addr, NULL, &relocate);
-       if (ret) {
-               dev_err(qproc->dev, "failed to parse mdt header\n");
+       ret = request_firmware(&fw, "modem.mdt", qproc->dev);
+       if (ret < 0) {
+               dev_err(qproc->dev, "unable to load modem.mdt\n");
                return ret;
        }
 
-       if (relocate)
-               boot_addr = qproc->mpss_phys;
-       else
-               boot_addr = fw_addr;
+       /* Initialize the RMB validator */
+       writel(0, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG);
+
+       ret = q6v5_mpss_init_image(qproc, fw);
+       if (ret)
+               goto release_firmware;
 
        ehdr = (struct elf32_hdr *)fw->data;
        phdrs = (struct elf32_phdr *)(ehdr + 1);
-       for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
+
+       for (i = 0; i < ehdr->e_phnum; i++) {
                phdr = &phdrs[i];
 
-               if (phdr->p_type != PT_LOAD)
+               if (!q6v5_phdr_valid(phdr))
                        continue;
 
-               if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH)
-                       continue;
+               if (phdr->p_flags & QCOM_MDT_RELOCATABLE)
+                       relocate = true;
+
+               if (phdr->p_paddr < min_addr)
+                       min_addr = phdr->p_paddr;
+
+               if (phdr->p_paddr + phdr->p_memsz > max_addr)
+                       max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K);
+       }
+
+       mpss_reloc = relocate ? min_addr : qproc->mpss_phys;
 
-               if (!phdr->p_memsz)
+       for (i = 0; i < ehdr->e_phnum; i++) {
+               phdr = &phdrs[i];
+
+               if (!q6v5_phdr_valid(phdr))
                        continue;
 
+               offset = phdr->p_paddr - mpss_reloc;
+               if (offset < 0 || offset + phdr->p_memsz > qproc->mpss_size) {
+                       dev_err(qproc->dev, "segment outside memory range\n");
+                       ret = -EINVAL;
+                       goto release_firmware;
+               }
+
+               ptr = qproc->mpss_region + offset;
+
+               if (phdr->p_filesz) {
+                       snprintf(seg_name, sizeof(seg_name), "modem.b%02d", i);
+                       ret = request_firmware(&seg_fw, seg_name, qproc->dev);
+                       if (ret) {
+                               dev_err(qproc->dev, "failed to load %s\n", seg_name);
+                               goto release_firmware;
+                       }
+
+                       memcpy(ptr, seg_fw->data, seg_fw->size);
+
+                       release_firmware(seg_fw);
+               }
+
+               if (phdr->p_memsz > phdr->p_filesz) {
+                       memset(ptr + phdr->p_filesz, 0,
+                              phdr->p_memsz - phdr->p_filesz);
+               }
+
                size = readl(qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG);
                if (!size) {
+                       boot_addr = relocate ? qproc->mpss_phys : min_addr;
                        writel(boot_addr, qproc->rmb_base + RMB_PMI_CODE_START_REG);
                        writel(RMB_CMD_LOAD_READY, qproc->rmb_base + RMB_MBA_COMMAND_REG);
                }
@@ -429,44 +592,6 @@ static int q6v5_mpss_validate(struct q6v5 *qproc, const struct firmware *fw)
        else if (ret < 0)
                dev_err(qproc->dev, "MPSS authentication failed: %d\n", ret);
 
-       return ret < 0 ? ret : 0;
-}
-
-static int q6v5_mpss_load(struct q6v5 *qproc)
-{
-       const struct firmware *fw;
-       phys_addr_t fw_addr;
-       bool relocate;
-       int ret;
-
-       ret = request_firmware(&fw, MPSS_FIRMWARE_NAME, qproc->dev);
-       if (ret < 0) {
-               dev_err(qproc->dev, "unable to load " MPSS_FIRMWARE_NAME "\n");
-               return ret;
-       }
-
-       ret = qcom_mdt_parse(fw, &fw_addr, NULL, &relocate);
-       if (ret) {
-               dev_err(qproc->dev, "failed to parse mdt header\n");
-               goto release_firmware;
-       }
-
-       if (relocate)
-               qproc->mpss_reloc = fw_addr;
-
-       /* Initialize the RMB validator */
-       writel(0, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG);
-
-       ret = q6v5_mpss_init_image(qproc, fw);
-       if (ret)
-               goto release_firmware;
-
-       ret = qcom_mdt_load(qproc->rproc, fw, MPSS_FIRMWARE_NAME);
-       if (ret)
-               goto release_firmware;
-
-       ret = q6v5_mpss_validate(qproc, fw);
-
 release_firmware:
        release_firmware(fw);
 
@@ -478,29 +603,38 @@ static int q6v5_start(struct rproc *rproc)
        struct q6v5 *qproc = (struct q6v5 *)rproc->priv;
        int ret;
 
-       ret = q6v5_regulator_enable(qproc);
+       ret = q6v5_regulator_enable(qproc, qproc->proxy_regs,
+                                   qproc->proxy_reg_count);
        if (ret) {
-               dev_err(qproc->dev, "failed to enable supplies\n");
+               dev_err(qproc->dev, "failed to enable proxy supplies\n");
                return ret;
        }
 
+       ret = q6v5_clk_enable(qproc->dev, qproc->proxy_clks,
+                             qproc->proxy_clk_count);
+       if (ret) {
+               dev_err(qproc->dev, "failed to enable proxy clocks\n");
+               goto disable_proxy_reg;
+       }
+
+       ret = q6v5_regulator_enable(qproc, qproc->active_regs,
+                                   qproc->active_reg_count);
+       if (ret) {
+               dev_err(qproc->dev, "failed to enable supplies\n");
+               goto disable_proxy_clk;
+       }
        ret = reset_control_deassert(qproc->mss_restart);
        if (ret) {
                dev_err(qproc->dev, "failed to deassert mss restart\n");
                goto disable_vdd;
        }
 
-       ret = clk_prepare_enable(qproc->ahb_clk);
-       if (ret)
+       ret = q6v5_clk_enable(qproc->dev, qproc->active_clks,
+                             qproc->active_clk_count);
+       if (ret) {
+               dev_err(qproc->dev, "failed to enable clocks\n");
                goto assert_reset;
-
-       ret = clk_prepare_enable(qproc->axi_clk);
-       if (ret)
-               goto disable_ahb_clk;
-
-       ret = clk_prepare_enable(qproc->rom_clk);
-       if (ret)
-               goto disable_axi_clk;
+       }
 
        writel(qproc->mba_phys, qproc->rmb_base + RMB_MBA_IMAGE_REG);
 
@@ -535,7 +669,10 @@ static int q6v5_start(struct rproc *rproc)
 
        qproc->running = true;
 
-       /* TODO: All done, release the handover resources */
+       q6v5_clk_disable(qproc->dev, qproc->proxy_clks,
+                        qproc->proxy_clk_count);
+       q6v5_regulator_disable(qproc, qproc->proxy_regs,
+                              qproc->proxy_reg_count);
 
        return 0;
 
@@ -543,16 +680,19 @@ halt_axi_ports:
        q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6);
        q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem);
        q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc);
-
-       clk_disable_unprepare(qproc->rom_clk);
-disable_axi_clk:
-       clk_disable_unprepare(qproc->axi_clk);
-disable_ahb_clk:
-       clk_disable_unprepare(qproc->ahb_clk);
+       q6v5_clk_disable(qproc->dev, qproc->active_clks,
+                        qproc->active_clk_count);
 assert_reset:
        reset_control_assert(qproc->mss_restart);
 disable_vdd:
-       q6v5_regulator_disable(qproc);
+       q6v5_regulator_disable(qproc, qproc->active_regs,
+                              qproc->active_reg_count);
+disable_proxy_clk:
+       q6v5_clk_disable(qproc->dev, qproc->proxy_clks,
+                        qproc->proxy_clk_count);
+disable_proxy_reg:
+       q6v5_regulator_disable(qproc, qproc->proxy_regs,
+                              qproc->proxy_reg_count);
 
        return ret;
 }
@@ -579,10 +719,10 @@ static int q6v5_stop(struct rproc *rproc)
        q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc);
 
        reset_control_assert(qproc->mss_restart);
-       clk_disable_unprepare(qproc->rom_clk);
-       clk_disable_unprepare(qproc->axi_clk);
-       clk_disable_unprepare(qproc->ahb_clk);
-       q6v5_regulator_disable(qproc);
+       q6v5_clk_disable(qproc->dev, qproc->active_clks,
+                        qproc->active_clk_count);
+       q6v5_regulator_disable(qproc, qproc->active_regs,
+                              qproc->active_reg_count);
 
        return 0;
 }
@@ -702,27 +842,27 @@ static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev)
        return 0;
 }
 
-static int q6v5_init_clocks(struct q6v5 *qproc)
+static int q6v5_init_clocks(struct device *dev, struct clk **clks,
+               char **clk_names)
 {
-       qproc->ahb_clk = devm_clk_get(qproc->dev, "iface");
-       if (IS_ERR(qproc->ahb_clk)) {
-               dev_err(qproc->dev, "failed to get iface clock\n");
-               return PTR_ERR(qproc->ahb_clk);
-       }
+       int i;
 
-       qproc->axi_clk = devm_clk_get(qproc->dev, "bus");
-       if (IS_ERR(qproc->axi_clk)) {
-               dev_err(qproc->dev, "failed to get bus clock\n");
-               return PTR_ERR(qproc->axi_clk);
-       }
+       if (!clk_names)
+               return 0;
 
-       qproc->rom_clk = devm_clk_get(qproc->dev, "mem");
-       if (IS_ERR(qproc->rom_clk)) {
-               dev_err(qproc->dev, "failed to get mem clock\n");
-               return PTR_ERR(qproc->rom_clk);
+       for (i = 0; clk_names[i]; i++) {
+               clks[i] = devm_clk_get(dev, clk_names[i]);
+               if (IS_ERR(clks[i])) {
+                       int rc = PTR_ERR(clks[i]);
+
+                       if (rc != -EPROBE_DEFER)
+                               dev_err(dev, "Failed to get %s clock\n",
+                                       clk_names[i]);
+                       return rc;
+               }
        }
 
-       return 0;
+       return i;
 }
 
 static int q6v5_init_reset(struct q6v5 *qproc)
@@ -805,12 +945,17 @@ static int q6v5_alloc_memory_region(struct q6v5 *qproc)
 
 static int q6v5_probe(struct platform_device *pdev)
 {
+       const struct rproc_hexagon_res *desc;
        struct q6v5 *qproc;
        struct rproc *rproc;
        int ret;
 
+       desc = of_device_get_match_data(&pdev->dev);
+       if (!desc)
+               return -EINVAL;
+
        rproc = rproc_alloc(&pdev->dev, pdev->name, &q6v5_ops,
-                           MBA_FIRMWARE_NAME, sizeof(*qproc));
+                           desc->hexagon_mba_image, sizeof(*qproc));
        if (!rproc) {
                dev_err(&pdev->dev, "failed to allocate rproc\n");
                return -ENOMEM;
@@ -834,13 +979,37 @@ static int q6v5_probe(struct platform_device *pdev)
        if (ret)
                goto free_rproc;
 
-       ret = q6v5_init_clocks(qproc);
-       if (ret)
+       ret = q6v5_init_clocks(&pdev->dev, qproc->proxy_clks,
+                              desc->proxy_clk_names);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to get proxy clocks.\n");
                goto free_rproc;
+       }
+       qproc->proxy_clk_count = ret;
 
-       ret = q6v5_regulator_init(qproc);
-       if (ret)
+       ret = q6v5_init_clocks(&pdev->dev, qproc->active_clks,
+                              desc->active_clk_names);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to get active clocks.\n");
+               goto free_rproc;
+       }
+       qproc->active_clk_count = ret;
+
+       ret = q6v5_regulator_init(&pdev->dev, qproc->proxy_regs,
+                                 desc->proxy_supply);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to get proxy regulators.\n");
+               goto free_rproc;
+       }
+       qproc->proxy_reg_count = ret;
+
+       ret = q6v5_regulator_init(&pdev->dev,  qproc->active_regs,
+                                 desc->active_supply);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to get active regulators.\n");
                goto free_rproc;
+       }
+       qproc->active_reg_count = ret;
 
        ret = q6v5_init_reset(qproc);
        if (ret)
@@ -868,6 +1037,8 @@ static int q6v5_probe(struct platform_device *pdev)
                goto free_rproc;
        }
 
+       qcom_add_smd_subdev(rproc, &qproc->smd_subdev);
+
        ret = rproc_add(rproc);
        if (ret)
                goto free_rproc;
@@ -885,13 +1056,83 @@ static int q6v5_remove(struct platform_device *pdev)
        struct q6v5 *qproc = platform_get_drvdata(pdev);
 
        rproc_del(qproc->rproc);
+
+       qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev);
        rproc_free(qproc->rproc);
 
        return 0;
 }
 
+static const struct rproc_hexagon_res msm8916_mss = {
+       .hexagon_mba_image = "mba.mbn",
+       .proxy_supply = (struct qcom_mss_reg_res[]) {
+               {
+                       .supply = "mx",
+                       .uV = 1050000,
+               },
+               {
+                       .supply = "cx",
+                       .uA = 100000,
+               },
+               {
+                       .supply = "pll",
+                       .uA = 100000,
+               },
+               {}
+       },
+       .proxy_clk_names = (char*[]){
+               "xo",
+               NULL
+       },
+       .active_clk_names = (char*[]){
+               "iface",
+               "bus",
+               "mem",
+               NULL
+       },
+};
+
+static const struct rproc_hexagon_res msm8974_mss = {
+       .hexagon_mba_image = "mba.b00",
+       .proxy_supply = (struct qcom_mss_reg_res[]) {
+               {
+                       .supply = "mx",
+                       .uV = 1050000,
+               },
+               {
+                       .supply = "cx",
+                       .uA = 100000,
+               },
+               {
+                       .supply = "pll",
+                       .uA = 100000,
+               },
+               {}
+       },
+       .active_supply = (struct qcom_mss_reg_res[]) {
+               {
+                       .supply = "mss",
+                       .uV = 1050000,
+                       .uA = 100000,
+               },
+               {}
+       },
+       .proxy_clk_names = (char*[]){
+               "xo",
+               NULL
+       },
+       .active_clk_names = (char*[]){
+               "iface",
+               "bus",
+               "mem",
+               NULL
+       },
+};
+
 static const struct of_device_id q6v5_of_match[] = {
-       { .compatible = "qcom,q6v5-pil", },
+       { .compatible = "qcom,q6v5-pil", .data = &msm8916_mss},
+       { .compatible = "qcom,msm8916-mss-pil", .data = &msm8916_mss},
+       { .compatible = "qcom,msm8974-mss-pil", .data = &msm8974_mss},
        { },
 };
 MODULE_DEVICE_TABLE(of, q6v5_of_match);
index ebd61f5d18bb05562f8a3a687c83d5024a8e13b3..c7686393d5056ee230282308c16d7ce12cba25df 100644 (file)
 #include <linux/qcom_scm.h>
 #include <linux/regulator/consumer.h>
 #include <linux/remoteproc.h>
+#include <linux/soc/qcom/mdt_loader.h>
 #include <linux/soc/qcom/smem.h>
 #include <linux/soc/qcom/smem_state.h>
 #include <linux/rpmsg/qcom_smd.h>
 
-#include "qcom_mdt_loader.h"
+#include "qcom_common.h"
 #include "remoteproc_internal.h"
 #include "qcom_wcnss.h"
 
@@ -96,9 +97,7 @@ struct qcom_wcnss {
        void *mem_region;
        size_t mem_size;
 
-       struct device_node *smd_node;
-       struct qcom_smd_edge *smd_edge;
-       struct rproc_subdev smd_subdev;
+       struct qcom_rproc_subdev smd_subdev;
 };
 
 static const struct wcnss_data riva_data = {
@@ -152,34 +151,9 @@ void qcom_wcnss_assign_iris(struct qcom_wcnss *wcnss,
 static int wcnss_load(struct rproc *rproc, const struct firmware *fw)
 {
        struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv;
-       phys_addr_t fw_addr;
-       size_t fw_size;
-       bool relocate;
-       int ret;
-
-       ret = qcom_scm_pas_init_image(WCNSS_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) {
-               wcnss->mem_reloc = fw_addr;
-
-               ret = qcom_scm_pas_mem_setup(WCNSS_PAS_ID, wcnss->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);
+       return qcom_mdt_load(wcnss->dev, fw, rproc->firmware, WCNSS_PAS_ID,
+                            wcnss->mem_region, wcnss->mem_phys, wcnss->mem_size);
 }
 
 static const struct rproc_fw_ops wcnss_fw_ops = {
@@ -400,23 +374,6 @@ static irqreturn_t wcnss_stop_ack_interrupt(int irq, void *dev)
        return IRQ_HANDLED;
 }
 
-static int wcnss_smd_probe(struct rproc_subdev *subdev)
-{
-       struct qcom_wcnss *wcnss = container_of(subdev, struct qcom_wcnss, smd_subdev);
-
-       wcnss->smd_edge = qcom_smd_register_edge(wcnss->dev, wcnss->smd_node);
-
-       return IS_ERR(wcnss->smd_edge) ? PTR_ERR(wcnss->smd_edge) : 0;
-}
-
-static void wcnss_smd_remove(struct rproc_subdev *subdev)
-{
-       struct qcom_wcnss *wcnss = container_of(subdev, struct qcom_wcnss, smd_subdev);
-
-       qcom_smd_unregister_edge(wcnss->smd_edge);
-       wcnss->smd_edge = NULL;
-}
-
 static int wcnss_init_regulators(struct qcom_wcnss *wcnss,
                                 const struct wcnss_vreg_info *info,
                                 int num_vregs)
@@ -599,9 +556,7 @@ static int wcnss_probe(struct platform_device *pdev)
                }
        }
 
-       wcnss->smd_node = of_get_child_by_name(pdev->dev.of_node, "smd-edge");
-       if (wcnss->smd_node)
-               rproc_add_subdev(rproc, &wcnss->smd_subdev, wcnss_smd_probe, wcnss_smd_remove);
+       qcom_add_smd_subdev(rproc, &wcnss->smd_subdev);
 
        ret = rproc_add(rproc);
        if (ret)
@@ -621,9 +576,10 @@ static int wcnss_remove(struct platform_device *pdev)
 
        of_platform_depopulate(&pdev->dev);
 
-       of_node_put(wcnss->smd_node);
        qcom_smem_state_put(wcnss->state);
        rproc_del(wcnss->rproc);
+
+       qcom_remove_smd_subdev(wcnss->rproc, &wcnss->smd_subdev);
        rproc_free(wcnss->rproc);
 
        return 0;
index 90b05c72186c4f9e9c37a812ec469a738d253e95..3dabb20b8d5d0b0608809e95479703283912a4c9 100644 (file)
@@ -961,48 +961,35 @@ clean_up:
 }
 
 /*
- * take a firmware and look for virtio devices to register.
+ * take a firmware and boot it up.
  *
  * Note: this function is called asynchronously upon registration of the
  * remote processor (so we must wait until it completes before we try
  * to unregister the device. one other option is just to use kref here,
  * that might be cleaner).
  */
-static void rproc_fw_config_virtio(const struct firmware *fw, void *context)
+static void rproc_auto_boot_callback(const struct firmware *fw, void *context)
 {
        struct rproc *rproc = context;
 
-       /* if rproc is marked always-on, request it to boot */
-       if (rproc->auto_boot)
-               rproc_boot(rproc);
+       rproc_boot(rproc);
 
        release_firmware(fw);
-       /* allow rproc_del() contexts, if any, to proceed */
-       complete_all(&rproc->firmware_loading_complete);
 }
 
-static int rproc_add_virtio_devices(struct rproc *rproc)
+static int rproc_trigger_auto_boot(struct rproc *rproc)
 {
        int ret;
 
-       /* rproc_del() calls must wait until async loader completes */
-       init_completion(&rproc->firmware_loading_complete);
-
        /*
-        * We must retrieve early virtio configuration info from
-        * the firmware (e.g. whether to register a virtio device,
-        * what virtio features does it support, ...).
-        *
         * We're initiating an asynchronous firmware loading, so we can
         * be built-in kernel code, without hanging the boot process.
         */
        ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
                                      rproc->firmware, &rproc->dev, GFP_KERNEL,
-                                     rproc, rproc_fw_config_virtio);
-       if (ret < 0) {
+                                     rproc, rproc_auto_boot_callback);
+       if (ret < 0)
                dev_err(&rproc->dev, "request_firmware_nowait err: %d\n", ret);
-               complete_all(&rproc->firmware_loading_complete);
-       }
 
        return ret;
 }
@@ -1099,6 +1086,12 @@ static int __rproc_boot(struct rproc *rproc)
                return ret;
        }
 
+       if (rproc->state == RPROC_DELETED) {
+               ret = -ENODEV;
+               dev_err(dev, "can't boot deleted rproc %s\n", rproc->name);
+               goto unlock_mutex;
+       }
+
        /* skip the boot process if rproc is already powered up */
        if (atomic_inc_return(&rproc->power) > 1) {
                ret = 0;
@@ -1287,9 +1280,13 @@ int rproc_add(struct rproc *rproc)
 
        /* create debugfs entries */
        rproc_create_debug_dir(rproc);
-       ret = rproc_add_virtio_devices(rproc);
-       if (ret < 0)
-               return ret;
+
+       /* if rproc is marked always-on, request it to boot */
+       if (rproc->auto_boot) {
+               ret = rproc_trigger_auto_boot(rproc);
+               if (ret < 0)
+                       return ret;
+       }
 
        /* expose to rproc_get_by_phandle users */
        mutex_lock(&rproc_list_mutex);
@@ -1315,8 +1312,6 @@ static void rproc_type_release(struct device *dev)
 
        dev_info(&rproc->dev, "releasing %s\n", rproc->name);
 
-       rproc_delete_debug_dir(rproc);
-
        idr_destroy(&rproc->notifyids);
 
        if (rproc->index >= 0)
@@ -1483,14 +1478,17 @@ int rproc_del(struct rproc *rproc)
        if (!rproc)
                return -EINVAL;
 
-       /* if rproc is just being registered, wait */
-       wait_for_completion(&rproc->firmware_loading_complete);
-
        /* if rproc is marked always-on, rproc_add() booted it */
        /* TODO: make sure this works with rproc->power > 1 */
        if (rproc->auto_boot)
                rproc_shutdown(rproc);
 
+       mutex_lock(&rproc->lock);
+       rproc->state = RPROC_DELETED;
+       mutex_unlock(&rproc->lock);
+
+       rproc_delete_debug_dir(rproc);
+
        /* the rproc is downref'ed as soon as it's removed from the klist */
        mutex_lock(&rproc_list_mutex);
        list_del(&rproc->node);
index bc5b0e00efb15422bcbf57a32798a4ea28a42646..47be411400e56aed1b48f44d4254e0178a640412 100644 (file)
@@ -73,6 +73,7 @@ static const char * const rproc_state_string[] = {
        [RPROC_SUSPENDED]       = "suspended",
        [RPROC_RUNNING]         = "running",
        [RPROC_CRASHED]         = "crashed",
+       [RPROC_DELETED]         = "deleted",
        [RPROC_LAST]            = "invalid",
 };
 
index da4e152e97331fbed605887430dddbe22947c9ff..d534bf23dc560516151d213414fb07b6dae229f1 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/err.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
+#include <linux/mailbox_client.h>
 #include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/remoteproc.h>
 #include <linux/reset.h>
 
+#include "remoteproc_internal.h"
+
+#define ST_RPROC_VQ0           0
+#define ST_RPROC_VQ1           1
+#define ST_RPROC_MAX_VRING     2
+
+#define MBOX_RX                        0
+#define MBOX_TX                        1
+#define MBOX_MAX               2
+
 struct st_rproc_config {
        bool                    sw_reset;
        bool                    pwr_reset;
@@ -39,8 +50,47 @@ struct st_rproc {
        u32                     clk_rate;
        struct regmap           *boot_base;
        u32                     boot_offset;
+       struct mbox_chan        *mbox_chan[ST_RPROC_MAX_VRING * MBOX_MAX];
+       struct mbox_client mbox_client_vq0;
+       struct mbox_client mbox_client_vq1;
 };
 
+static void st_rproc_mbox_callback(struct device *dev, u32 msg)
+{
+       struct rproc *rproc = dev_get_drvdata(dev);
+
+       if (rproc_vq_interrupt(rproc, msg) == IRQ_NONE)
+               dev_dbg(dev, "no message was found in vqid %d\n", msg);
+}
+
+static
+void st_rproc_mbox_callback_vq0(struct mbox_client *mbox_client, void *data)
+{
+       st_rproc_mbox_callback(mbox_client->dev, 0);
+}
+
+static
+void st_rproc_mbox_callback_vq1(struct mbox_client *mbox_client, void *data)
+{
+       st_rproc_mbox_callback(mbox_client->dev, 1);
+}
+
+static void st_rproc_kick(struct rproc *rproc, int vqid)
+{
+       struct st_rproc *ddata = rproc->priv;
+       struct device *dev = rproc->dev.parent;
+       int ret;
+
+       /* send the index of the triggered virtqueue in the mailbox payload */
+       if (WARN_ON(vqid >= ST_RPROC_MAX_VRING))
+               return;
+
+       ret = mbox_send_message(ddata->mbox_chan[vqid * MBOX_MAX + MBOX_TX],
+                               (void *)&vqid);
+       if (ret < 0)
+               dev_err(dev, "failed to send message via mbox: %d\n", ret);
+}
+
 static int st_rproc_start(struct rproc *rproc)
 {
        struct st_rproc *ddata = rproc->priv;
@@ -107,7 +157,8 @@ static int st_rproc_stop(struct rproc *rproc)
        return sw_err ?: pwr_err;
 }
 
-static struct rproc_ops st_rproc_ops = {
+static const struct rproc_ops st_rproc_ops = {
+       .kick           = st_rproc_kick,
        .start          = st_rproc_start,
        .stop           = st_rproc_stop,
 };
@@ -221,8 +272,9 @@ static int st_rproc_probe(struct platform_device *pdev)
        struct st_rproc *ddata;
        struct device_node *np = dev->of_node;
        struct rproc *rproc;
+       struct mbox_chan *chan;
        int enabled;
-       int ret;
+       int ret, i;
 
        match = of_match_device(st_rproc_match, dev);
        if (!match || !match->data) {
@@ -247,7 +299,7 @@ static int st_rproc_probe(struct platform_device *pdev)
        enabled = st_rproc_state(pdev);
        if (enabled < 0) {
                ret = enabled;
-               goto free_rproc;
+               goto free_clk;
        }
 
        if (enabled) {
@@ -257,12 +309,67 @@ static int st_rproc_probe(struct platform_device *pdev)
                clk_set_rate(ddata->clk, ddata->clk_rate);
        }
 
+       if (of_get_property(np, "mbox-names", NULL)) {
+               ddata->mbox_client_vq0.dev              = dev;
+               ddata->mbox_client_vq0.tx_done          = NULL;
+               ddata->mbox_client_vq0.tx_block = false;
+               ddata->mbox_client_vq0.knows_txdone     = false;
+               ddata->mbox_client_vq0.rx_callback      = st_rproc_mbox_callback_vq0;
+
+               ddata->mbox_client_vq1.dev              = dev;
+               ddata->mbox_client_vq1.tx_done          = NULL;
+               ddata->mbox_client_vq1.tx_block = false;
+               ddata->mbox_client_vq1.knows_txdone     = false;
+               ddata->mbox_client_vq1.rx_callback      = st_rproc_mbox_callback_vq1;
+
+               /*
+                * To control a co-processor without IPC mechanism.
+                * This driver can be used without mbox and rpmsg.
+                */
+               chan = mbox_request_channel_byname(&ddata->mbox_client_vq0, "vq0_rx");
+               if (IS_ERR(chan)) {
+                       dev_err(&rproc->dev, "failed to request mbox chan 0\n");
+                       ret = PTR_ERR(chan);
+                       goto free_clk;
+               }
+               ddata->mbox_chan[ST_RPROC_VQ0 * MBOX_MAX + MBOX_RX] = chan;
+
+               chan = mbox_request_channel_byname(&ddata->mbox_client_vq0, "vq0_tx");
+               if (IS_ERR(chan)) {
+                       dev_err(&rproc->dev, "failed to request mbox chan 0\n");
+                       ret = PTR_ERR(chan);
+                       goto free_mbox;
+               }
+               ddata->mbox_chan[ST_RPROC_VQ0 * MBOX_MAX + MBOX_TX] = chan;
+
+               chan = mbox_request_channel_byname(&ddata->mbox_client_vq1, "vq1_rx");
+               if (IS_ERR(chan)) {
+                       dev_err(&rproc->dev, "failed to request mbox chan 1\n");
+                       ret = PTR_ERR(chan);
+                       goto free_mbox;
+               }
+               ddata->mbox_chan[ST_RPROC_VQ1 * MBOX_MAX + MBOX_RX] = chan;
+
+               chan = mbox_request_channel_byname(&ddata->mbox_client_vq1, "vq1_tx");
+               if (IS_ERR(chan)) {
+                       dev_err(&rproc->dev, "failed to request mbox chan 1\n");
+                       ret = PTR_ERR(chan);
+                       goto free_mbox;
+               }
+               ddata->mbox_chan[ST_RPROC_VQ1 * MBOX_MAX + MBOX_TX] = chan;
+       }
+
        ret = rproc_add(rproc);
        if (ret)
-               goto free_rproc;
+               goto free_mbox;
 
        return 0;
 
+free_mbox:
+       for (i = 0; i < ST_RPROC_MAX_VRING * MBOX_MAX; i++)
+               mbox_free_channel(ddata->mbox_chan[i]);
+free_clk:
+       clk_unprepare(ddata->clk);
 free_rproc:
        rproc_free(rproc);
        return ret;
@@ -272,6 +379,7 @@ static int st_rproc_remove(struct platform_device *pdev)
 {
        struct rproc *rproc = platform_get_drvdata(pdev);
        struct st_rproc *ddata = rproc->priv;
+       int i;
 
        rproc_del(rproc);
 
@@ -279,6 +387,9 @@ static int st_rproc_remove(struct platform_device *pdev)
 
        of_reserved_mem_device_release(&pdev->dev);
 
+       for (i = 0; i < ST_RPROC_MAX_VRING * MBOX_MAX; i++)
+               mbox_free_channel(ddata->mbox_chan[i]);
+
        rproc_free(rproc);
 
        return 0;
index 507716c8721fd306f056e1a738e68edff6fdc546..6cfd862f945b48293620145e1bb5380e31ad26a7 100644 (file)
@@ -200,7 +200,7 @@ static void *slim_rproc_da_to_va(struct rproc *rproc, u64 da, int len)
        return va;
 }
 
-static struct rproc_ops slim_rproc_ops = {
+static const struct rproc_ops slim_rproc_ops = {
        .start          = slim_rproc_start,
        .stop           = slim_rproc_stop,
        .da_to_va       = slim_rproc_da_to_va,
index 18175d0331fd7121025ab634694d1359f0852c5a..1ada0e51fef61d26cbcb519443943b7b79b9bcc0 100644 (file)
@@ -111,7 +111,7 @@ static void *wkup_m3_rproc_da_to_va(struct rproc *rproc, u64 da, int len)
        return va;
 }
 
-static struct rproc_ops wkup_m3_rproc_ops = {
+static const struct rproc_ops wkup_m3_rproc_ops = {
        .start          = wkup_m3_rproc_start,
        .stop           = wkup_m3_rproc_stop,
        .da_to_va       = wkup_m3_rproc_da_to_va,
index 461b387d03cce53b658dd762a1587b11dc5aec01..78b1bb7bcf20ab1e3c39d7d3817b3ae3570825be 100644 (file)
@@ -10,6 +10,10 @@ config QCOM_GSBI
           functions for connecting the underlying serial UART, SPI, and I2C
           devices to the output pins.
 
+config QCOM_MDT_LOADER
+       tristate
+       select QCOM_SCM
+
 config QCOM_PM
        bool "Qualcomm Power Management"
        depends on ARCH_QCOM && !ARM64
index fdd664edf0bdf3cdc7e468ccec62eae5770fae3a..1f30260b06b8f39ffa04d51e9b13b41c19c7b988 100644 (file)
@@ -1,4 +1,5 @@
 obj-$(CONFIG_QCOM_GSBI)        +=      qcom_gsbi.o
+obj-$(CONFIG_QCOM_MDT_LOADER)  += mdt_loader.o
 obj-$(CONFIG_QCOM_PM)  +=      spm.o
 obj-$(CONFIG_QCOM_SMD) +=      smd.o
 obj-$(CONFIG_QCOM_SMD_RPM)     += smd-rpm.o
diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c
new file mode 100644 (file)
index 0000000..bd63df0
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * Qualcomm Peripheral Image Loader
+ *
+ * Copyright (C) 2016 Linaro Ltd
+ * Copyright (C) 2015 Sony Mobile Communications Inc
+ * 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 <linux/device.h>
+#include <linux/elf.h>
+#include <linux/firmware.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/qcom_scm.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/soc/qcom/mdt_loader.h>
+
+static bool mdt_phdr_valid(const struct elf32_phdr *phdr)
+{
+       if (phdr->p_type != PT_LOAD)
+               return false;
+
+       if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH)
+               return false;
+
+       if (!phdr->p_memsz)
+               return false;
+
+       return true;
+}
+
+/**
+ * qcom_mdt_get_size() - acquire size of the memory region needed to load mdt
+ * @fw:                firmware object for the mdt file
+ *
+ * Returns size of the loaded firmware blob, or -EINVAL on failure.
+ */
+ssize_t qcom_mdt_get_size(const struct firmware *fw)
+{
+       const struct elf32_phdr *phdrs;
+       const struct elf32_phdr *phdr;
+       const struct elf32_hdr *ehdr;
+       phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX;
+       phys_addr_t max_addr = 0;
+       int i;
+
+       ehdr = (struct elf32_hdr *)fw->data;
+       phdrs = (struct elf32_phdr *)(ehdr + 1);
+
+       for (i = 0; i < ehdr->e_phnum; i++) {
+               phdr = &phdrs[i];
+
+               if (!mdt_phdr_valid(phdr))
+                       continue;
+
+               if (phdr->p_paddr < min_addr)
+                       min_addr = phdr->p_paddr;
+
+               if (phdr->p_paddr + phdr->p_memsz > max_addr)
+                       max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K);
+       }
+
+       return min_addr < max_addr ? max_addr - min_addr : -EINVAL;
+}
+EXPORT_SYMBOL_GPL(qcom_mdt_get_size);
+
+/**
+ * qcom_mdt_load() - load the firmware which header is loaded as fw
+ * @dev:       device handle to associate resources with
+ * @fw:                firmware object for the mdt file
+ * @firmware:  name of the firmware, for construction of segment file names
+ * @pas_id:    PAS identifier
+ * @mem_region:        allocated memory region to load firmware into
+ * @mem_phys:  physical address of allocated memory region
+ * @mem_size:  size of the allocated memory region
+ *
+ * Returns 0 on success, negative errno otherwise.
+ */
+int qcom_mdt_load(struct device *dev, const struct firmware *fw,
+                 const char *firmware, int pas_id, void *mem_region,
+                 phys_addr_t mem_phys, size_t mem_size)
+{
+       const struct elf32_phdr *phdrs;
+       const struct elf32_phdr *phdr;
+       const struct elf32_hdr *ehdr;
+       const struct firmware *seg_fw;
+       phys_addr_t mem_reloc;
+       phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX;
+       phys_addr_t max_addr = 0;
+       size_t fw_name_len;
+       ssize_t offset;
+       char *fw_name;
+       bool relocate = false;
+       void *ptr;
+       int ret;
+       int i;
+
+       if (!fw || !mem_region || !mem_phys || !mem_size)
+               return -EINVAL;
+
+       ehdr = (struct elf32_hdr *)fw->data;
+       phdrs = (struct elf32_phdr *)(ehdr + 1);
+
+       fw_name_len = strlen(firmware);
+       if (fw_name_len <= 4)
+               return -EINVAL;
+
+       fw_name = kstrdup(firmware, GFP_KERNEL);
+       if (!fw_name)
+               return -ENOMEM;
+
+       ret = qcom_scm_pas_init_image(pas_id, fw->data, fw->size);
+       if (ret) {
+               dev_err(dev, "invalid firmware metadata\n");
+               goto out;
+       }
+
+       for (i = 0; i < ehdr->e_phnum; i++) {
+               phdr = &phdrs[i];
+
+               if (!mdt_phdr_valid(phdr))
+                       continue;
+
+               if (phdr->p_flags & QCOM_MDT_RELOCATABLE)
+                       relocate = true;
+
+               if (phdr->p_paddr < min_addr)
+                       min_addr = phdr->p_paddr;
+
+               if (phdr->p_paddr + phdr->p_memsz > max_addr)
+                       max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K);
+       }
+
+       if (relocate) {
+               ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr);
+               if (ret) {
+                       dev_err(dev, "unable to setup relocation\n");
+                       goto out;
+               }
+
+               /*
+                * The image is relocatable, so offset each segment based on
+                * the lowest segment address.
+                */
+               mem_reloc = min_addr;
+       } else {
+               /*
+                * Image is not relocatable, so offset each segment based on
+                * the allocated physical chunk of memory.
+                */
+               mem_reloc = mem_phys;
+       }
+
+       for (i = 0; i < ehdr->e_phnum; i++) {
+               phdr = &phdrs[i];
+
+               if (!mdt_phdr_valid(phdr))
+                       continue;
+
+               offset = phdr->p_paddr - mem_reloc;
+               if (offset < 0 || offset + phdr->p_memsz > mem_size) {
+                       dev_err(dev, "segment outside memory range\n");
+                       ret = -EINVAL;
+                       break;
+               }
+
+               ptr = mem_region + offset;
+
+               if (phdr->p_filesz) {
+                       sprintf(fw_name + fw_name_len - 3, "b%02d", i);
+                       ret = request_firmware(&seg_fw, fw_name, dev);
+                       if (ret) {
+                               dev_err(dev, "failed to load %s\n", fw_name);
+                               break;
+                       }
+
+                       memcpy(ptr, seg_fw->data, seg_fw->size);
+
+                       release_firmware(seg_fw);
+               }
+
+               if (phdr->p_memsz > phdr->p_filesz)
+                       memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz);
+       }
+
+out:
+       kfree(fw_name);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(qcom_mdt_load);
+
+MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format");
+MODULE_LICENSE("GPL v2");
index 8265d351c9f0e6bc7814fd59ca1efff531e4798f..81da49564ff4236b86618187b0f0be5d2f81bea9 100644 (file)
@@ -346,6 +346,7 @@ struct rproc_ops {
  *                     a message.
  * @RPROC_RUNNING:     device is up and running
  * @RPROC_CRASHED:     device has crashed; need to start recovery
+ * @RPROC_DELETED:     device is deleted
  * @RPROC_LAST:                just keep this one at the end
  *
  * Please note that the values of these states are used as indices
@@ -359,7 +360,8 @@ enum rproc_state {
        RPROC_SUSPENDED = 1,
        RPROC_RUNNING   = 2,
        RPROC_CRASHED   = 3,
-       RPROC_LAST      = 4,
+       RPROC_DELETED   = 4,
+       RPROC_LAST      = 5,
 };
 
 /**
@@ -397,7 +399,6 @@ enum rproc_crash_type {
  * @num_traces: number of trace buffers
  * @carveouts: list of physically contiguous memory allocations
  * @mappings: list of iommu mappings we initiated, needed on shutdown
- * @firmware_loading_complete: marks e/o asynchronous firmware loading
  * @bootaddr: address of first instruction to boot rproc with (optional)
  * @rvdevs: list of remote virtio devices
  * @subdevs: list of subdevices, to following the running state
@@ -429,7 +430,6 @@ struct rproc {
        int num_traces;
        struct list_head carveouts;
        struct list_head mappings;
-       struct completion firmware_loading_complete;
        u32 bootaddr;
        struct list_head rvdevs;
        struct list_head subdevs;
diff --git a/include/linux/soc/qcom/mdt_loader.h b/include/linux/soc/qcom/mdt_loader.h
new file mode 100644 (file)
index 0000000..f423001
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef __QCOM_MDT_LOADER_H__
+#define __QCOM_MDT_LOADER_H__
+
+#include <linux/types.h>
+
+#define QCOM_MDT_TYPE_MASK     (7 << 24)
+#define QCOM_MDT_TYPE_HASH     (2 << 24)
+#define QCOM_MDT_RELOCATABLE   BIT(27)
+
+struct device;
+struct firmware;
+
+ssize_t qcom_mdt_get_size(const struct firmware *fw);
+int qcom_mdt_load(struct device *dev, const struct firmware *fw,
+                 const char *fw_name, int pas_id, void *mem_region,
+                 phys_addr_t mem_phys, size_t mem_size);
+
+#endif