summaryrefslogtreecommitdiff
path: root/drivers/remoteproc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/remoteproc')
-rw-r--r--drivers/remoteproc/Kconfig16
-rw-r--r--drivers/remoteproc/Makefile2
-rw-r--r--drivers/remoteproc/da8xx_remoteproc.c6
-rw-r--r--drivers/remoteproc/omap_remoteproc.c9
-rw-r--r--drivers/remoteproc/qcom_q6v5_pil.c8
-rw-r--r--drivers/remoteproc/qcom_wcnss.c624
-rw-r--r--drivers/remoteproc/qcom_wcnss.h22
-rw-r--r--drivers/remoteproc/qcom_wcnss_iris.c188
-rw-r--r--drivers/remoteproc/remoteproc_core.c248
-rw-r--r--drivers/remoteproc/remoteproc_debugfs.c20
-rw-r--r--drivers/remoteproc/remoteproc_elf_loader.c6
-rw-r--r--drivers/remoteproc/remoteproc_internal.h15
-rw-r--r--drivers/remoteproc/remoteproc_virtio.c35
-rw-r--r--drivers/remoteproc/st_remoteproc.c4
-rw-r--r--drivers/remoteproc/ste_modem_rproc.c4
-rw-r--r--drivers/remoteproc/wkup_m3_rproc.c6
16 files changed, 1022 insertions, 191 deletions
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 70ec8130622d..f396bfef5d42 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -91,6 +91,22 @@ config QCOM_Q6V5_PIL
Say y here to support the Qualcomm Peripherial Image Loader for the
Hexagon V5 based remote processors.
+config QCOM_WCNSS_IRIS
+ tristate
+ depends on OF && ARCH_QCOM
+
+config QCOM_WCNSS_PIL
+ tristate "Qualcomm WCNSS Peripheral Image Loader"
+ depends on OF && ARCH_QCOM
+ depends on QCOM_SMEM
+ select QCOM_MDT_LOADER
+ select QCOM_SCM
+ select QCOM_WCNSS_IRIS
+ select REMOTEPROC
+ help
+ Say y here to support the Peripheral Image Loader for the Qualcomm
+ Wireless Connectivity Subsystem.
+
config ST_REMOTEPROC
tristate "ST remoteproc support"
depends on ARCH_STI
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 92d3758bd15c..6dfb62ed643f 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -13,4 +13,6 @@ obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o
obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o
obj-$(CONFIG_QCOM_MDT_LOADER) += qcom_mdt_loader.o
obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o
+obj-$(CONFIG_QCOM_WCNSS_IRIS) += qcom_wcnss_iris.o
+obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss.o
obj-$(CONFIG_ST_REMOTEPROC) += st_remoteproc.o
diff --git a/drivers/remoteproc/da8xx_remoteproc.c b/drivers/remoteproc/da8xx_remoteproc.c
index 009e56f67de2..1afac8f31be0 100644
--- a/drivers/remoteproc/da8xx_remoteproc.c
+++ b/drivers/remoteproc/da8xx_remoteproc.c
@@ -147,7 +147,7 @@ static void da8xx_rproc_kick(struct rproc *rproc, int vqid)
{
struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
- /* Interupt remote proc */
+ /* Interrupt remote proc */
writel(SYSCFG_CHIPSIG2, drproc->chipsig);
}
@@ -261,7 +261,7 @@ static int da8xx_rproc_probe(struct platform_device *pdev)
return 0;
free_rproc:
- rproc_put(rproc);
+ rproc_free(rproc);
return ret;
}
@@ -290,7 +290,7 @@ static int da8xx_rproc_remove(struct platform_device *pdev)
disable_irq(drproc->irq);
rproc_del(rproc);
- rproc_put(rproc);
+ rproc_free(rproc);
return 0;
}
diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c
index b74368a91235..fa63bf2eb885 100644
--- a/drivers/remoteproc/omap_remoteproc.c
+++ b/drivers/remoteproc/omap_remoteproc.c
@@ -96,7 +96,8 @@ static void omap_rproc_kick(struct rproc *rproc, int vqid)
/* send the index of the triggered virtqueue in the mailbox payload */
ret = mbox_send_message(oproc->mbox, (void *)vqid);
if (ret < 0)
- dev_err(dev, "omap_mbox_msg_send failed: %d\n", ret);
+ dev_err(dev, "failed to send mailbox message, status = %d\n",
+ ret);
}
/*
@@ -196,7 +197,7 @@ static int omap_rproc_probe(struct platform_device *pdev)
}
rproc = rproc_alloc(&pdev->dev, pdata->name, &omap_rproc_ops,
- pdata->firmware, sizeof(*oproc));
+ pdata->firmware, sizeof(*oproc));
if (!rproc)
return -ENOMEM;
@@ -214,7 +215,7 @@ static int omap_rproc_probe(struct platform_device *pdev)
return 0;
free_rproc:
- rproc_put(rproc);
+ rproc_free(rproc);
return ret;
}
@@ -223,7 +224,7 @@ static int omap_rproc_remove(struct platform_device *pdev)
struct rproc *rproc = platform_get_drvdata(pdev);
rproc_del(rproc);
- rproc_put(rproc);
+ rproc_free(rproc);
return 0;
}
diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c
index 2a1b2c7d8f2c..2e0caaaa766a 100644
--- a/drivers/remoteproc/qcom_q6v5_pil.c
+++ b/drivers/remoteproc/qcom_q6v5_pil.c
@@ -863,8 +863,10 @@ static int q6v5_probe(struct platform_device *pdev)
goto free_rproc;
qproc->state = qcom_smem_state_get(&pdev->dev, "stop", &qproc->stop_bit);
- if (IS_ERR(qproc->state))
+ if (IS_ERR(qproc->state)) {
+ ret = PTR_ERR(qproc->state);
goto free_rproc;
+ }
ret = rproc_add(rproc);
if (ret)
@@ -873,7 +875,7 @@ static int q6v5_probe(struct platform_device *pdev)
return 0;
free_rproc:
- rproc_put(rproc);
+ rproc_free(rproc);
return ret;
}
@@ -883,7 +885,7 @@ static int q6v5_remove(struct platform_device *pdev)
struct q6v5 *qproc = platform_get_drvdata(pdev);
rproc_del(qproc->rproc);
- rproc_put(qproc->rproc);
+ rproc_free(qproc->rproc);
return 0;
}
diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c
new file mode 100644
index 000000000000..f5cedeaafba1
--- /dev/null
+++ b/drivers/remoteproc/qcom_wcnss.c
@@ -0,0 +1,624 @@
+/*
+ * Qualcomm Wireless Connectivity Subsystem Peripheral Image Loader
+ *
+ * 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 <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/qcom_scm.h>
+#include <linux/regulator/consumer.h>
+#include <linux/remoteproc.h>
+#include <linux/soc/qcom/smem.h>
+#include <linux/soc/qcom/smem_state.h>
+
+#include "qcom_mdt_loader.h"
+#include "remoteproc_internal.h"
+#include "qcom_wcnss.h"
+
+#define WCNSS_CRASH_REASON_SMEM 422
+#define WCNSS_FIRMWARE_NAME "wcnss.mdt"
+#define WCNSS_PAS_ID 6
+
+#define WCNSS_SPARE_NVBIN_DLND BIT(25)
+
+#define WCNSS_PMU_IRIS_XO_CFG BIT(3)
+#define WCNSS_PMU_IRIS_XO_EN BIT(4)
+#define WCNSS_PMU_GC_BUS_MUX_SEL_TOP BIT(5)
+#define WCNSS_PMU_IRIS_XO_CFG_STS BIT(6) /* 1: in progress, 0: done */
+
+#define WCNSS_PMU_IRIS_RESET BIT(7)
+#define WCNSS_PMU_IRIS_RESET_STS BIT(8) /* 1: in progress, 0: done */
+#define WCNSS_PMU_IRIS_XO_READ BIT(9)
+#define WCNSS_PMU_IRIS_XO_READ_STS BIT(10)
+
+#define WCNSS_PMU_XO_MODE_MASK GENMASK(2, 1)
+#define WCNSS_PMU_XO_MODE_19p2 0
+#define WCNSS_PMU_XO_MODE_48 3
+
+struct wcnss_data {
+ size_t pmu_offset;
+ size_t spare_offset;
+
+ const struct wcnss_vreg_info *vregs;
+ size_t num_vregs;
+};
+
+struct qcom_wcnss {
+ struct device *dev;
+ struct rproc *rproc;
+
+ void __iomem *pmu_cfg;
+ void __iomem *spare_out;
+
+ bool use_48mhz_xo;
+
+ 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 mutex iris_lock;
+ struct qcom_iris *iris;
+
+ struct regulator_bulk_data *vregs;
+ size_t num_vregs;
+
+ 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 const struct wcnss_data riva_data = {
+ .pmu_offset = 0x28,
+ .spare_offset = 0xb4,
+
+ .vregs = (struct wcnss_vreg_info[]) {
+ { "vddmx", 1050000, 1150000, 0 },
+ { "vddcx", 1050000, 1150000, 0 },
+ { "vddpx", 1800000, 1800000, 0 },
+ },
+ .num_vregs = 3,
+};
+
+static const struct wcnss_data pronto_v1_data = {
+ .pmu_offset = 0x1004,
+ .spare_offset = 0x1088,
+
+ .vregs = (struct wcnss_vreg_info[]) {
+ { "vddmx", 950000, 1150000, 0 },
+ { "vddcx", .super_turbo = true},
+ { "vddpx", 1800000, 1800000, 0 },
+ },
+ .num_vregs = 3,
+};
+
+static const struct wcnss_data pronto_v2_data = {
+ .pmu_offset = 0x1004,
+ .spare_offset = 0x1088,
+
+ .vregs = (struct wcnss_vreg_info[]) {
+ { "vddmx", 1287500, 1287500, 0 },
+ { "vddcx", .super_turbo = true },
+ { "vddpx", 1800000, 1800000, 0 },
+ },
+ .num_vregs = 3,
+};
+
+void qcom_wcnss_assign_iris(struct qcom_wcnss *wcnss,
+ struct qcom_iris *iris,
+ bool use_48mhz_xo)
+{
+ mutex_lock(&wcnss->iris_lock);
+
+ wcnss->iris = iris;
+ wcnss->use_48mhz_xo = use_48mhz_xo;
+
+ mutex_unlock(&wcnss->iris_lock);
+}
+EXPORT_SYMBOL_GPL(qcom_wcnss_assign_iris);
+
+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);
+}
+
+static const struct rproc_fw_ops wcnss_fw_ops = {
+ .find_rsc_table = qcom_mdt_find_rsc_table,
+ .load = wcnss_load,
+};
+
+static void wcnss_indicate_nv_download(struct qcom_wcnss *wcnss)
+{
+ u32 val;
+
+ /* Indicate NV download capability */
+ val = readl(wcnss->spare_out);
+ val |= WCNSS_SPARE_NVBIN_DLND;
+ writel(val, wcnss->spare_out);
+}
+
+static void wcnss_configure_iris(struct qcom_wcnss *wcnss)
+{
+ u32 val;
+
+ /* Clear PMU cfg register */
+ writel(0, wcnss->pmu_cfg);
+
+ val = WCNSS_PMU_GC_BUS_MUX_SEL_TOP | WCNSS_PMU_IRIS_XO_EN;
+ writel(val, wcnss->pmu_cfg);
+
+ /* Clear XO_MODE */
+ val &= ~WCNSS_PMU_XO_MODE_MASK;
+ if (wcnss->use_48mhz_xo)
+ val |= WCNSS_PMU_XO_MODE_48 << 1;
+ else
+ val |= WCNSS_PMU_XO_MODE_19p2 << 1;
+ writel(val, wcnss->pmu_cfg);
+
+ /* Reset IRIS */
+ val |= WCNSS_PMU_IRIS_RESET;
+ writel(val, wcnss->pmu_cfg);
+
+ /* Wait for PMU.iris_reg_reset_sts */
+ while (readl(wcnss->pmu_cfg) & WCNSS_PMU_IRIS_RESET_STS)
+ cpu_relax();
+
+ /* Clear IRIS reset */
+ val &= ~WCNSS_PMU_IRIS_RESET;
+ writel(val, wcnss->pmu_cfg);
+
+ /* Start IRIS XO configuration */
+ val |= WCNSS_PMU_IRIS_XO_CFG;
+ writel(val, wcnss->pmu_cfg);
+
+ /* Wait for XO configuration to finish */
+ while (readl(wcnss->pmu_cfg) & WCNSS_PMU_IRIS_XO_CFG_STS)
+ cpu_relax();
+
+ /* Stop IRIS XO configuration */
+ val &= ~WCNSS_PMU_GC_BUS_MUX_SEL_TOP;
+ val &= ~WCNSS_PMU_IRIS_XO_CFG;
+ writel(val, wcnss->pmu_cfg);
+
+ /* Add some delay for XO to settle */
+ msleep(20);
+}
+
+static int wcnss_start(struct rproc *rproc)
+{
+ struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv;
+ int ret;
+
+ mutex_lock(&wcnss->iris_lock);
+ if (!wcnss->iris) {
+ dev_err(wcnss->dev, "no iris registered\n");
+ ret = -EINVAL;
+ goto release_iris_lock;
+ }
+
+ ret = regulator_bulk_enable(wcnss->num_vregs, wcnss->vregs);
+ if (ret)
+ goto release_iris_lock;
+
+ ret = qcom_iris_enable(wcnss->iris);
+ if (ret)
+ goto disable_regulators;
+
+ wcnss_indicate_nv_download(wcnss);
+ wcnss_configure_iris(wcnss);
+
+ ret = qcom_scm_pas_auth_and_reset(WCNSS_PAS_ID);
+ if (ret) {
+ dev_err(wcnss->dev,
+ "failed to authenticate image and release reset\n");
+ goto disable_iris;
+ }
+
+ ret = wait_for_completion_timeout(&wcnss->start_done,
+ msecs_to_jiffies(5000));
+ if (wcnss->ready_irq > 0 && ret == 0) {
+ /* We have a ready_irq, but it didn't fire in time. */
+ dev_err(wcnss->dev, "start timed out\n");
+ qcom_scm_pas_shutdown(WCNSS_PAS_ID);
+ ret = -ETIMEDOUT;
+ goto disable_iris;
+ }
+
+ ret = 0;
+
+disable_iris:
+ qcom_iris_disable(wcnss->iris);
+disable_regulators:
+ regulator_bulk_disable(wcnss->num_vregs, wcnss->vregs);
+release_iris_lock:
+ mutex_unlock(&wcnss->iris_lock);
+
+ return ret;
+}
+
+static int wcnss_stop(struct rproc *rproc)
+{
+ struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv;
+ int ret;
+
+ if (wcnss->state) {
+ qcom_smem_state_update_bits(wcnss->state,
+ BIT(wcnss->stop_bit),
+ BIT(wcnss->stop_bit));
+
+ ret = wait_for_completion_timeout(&wcnss->stop_done,
+ msecs_to_jiffies(5000));
+ if (ret == 0)
+ dev_err(wcnss->dev, "timed out on wait\n");
+
+ qcom_smem_state_update_bits(wcnss->state,
+ BIT(wcnss->stop_bit),
+ 0);
+ }
+
+ ret = qcom_scm_pas_shutdown(WCNSS_PAS_ID);
+ if (ret)
+ dev_err(wcnss->dev, "failed to shutdown: %d\n", ret);
+
+ return ret;
+}
+
+static void *wcnss_da_to_va(struct rproc *rproc, u64 da, int len)
+{
+ struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv;
+ int offset;
+
+ offset = da - wcnss->mem_reloc;
+ if (offset < 0 || offset + len > wcnss->mem_size)
+ return NULL;
+
+ return wcnss->mem_region + offset;
+}
+
+static const struct rproc_ops wcnss_ops = {
+ .start = wcnss_start,
+ .stop = wcnss_stop,
+ .da_to_va = wcnss_da_to_va,
+};
+
+static irqreturn_t wcnss_wdog_interrupt(int irq, void *dev)
+{
+ struct qcom_wcnss *wcnss = dev;
+
+ rproc_report_crash(wcnss->rproc, RPROC_WATCHDOG);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcnss_fatal_interrupt(int irq, void *dev)
+{
+ struct qcom_wcnss *wcnss = dev;
+ size_t len;
+ char *msg;
+
+ msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, WCNSS_CRASH_REASON_SMEM, &len);
+ if (!IS_ERR(msg) && len > 0 && msg[0])
+ dev_err(wcnss->dev, "fatal error received: %s\n", msg);
+
+ rproc_report_crash(wcnss->rproc, RPROC_FATAL_ERROR);
+
+ if (!IS_ERR(msg))
+ msg[0] = '\0';
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcnss_ready_interrupt(int irq, void *dev)
+{
+ struct qcom_wcnss *wcnss = dev;
+
+ complete(&wcnss->start_done);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcnss_handover_interrupt(int irq, void *dev)
+{
+ /*
+ * XXX: At this point we're supposed to release the resources that we
+ * have been holding on behalf of the WCNSS. Unfortunately this
+ * interrupt comes way before the other side seems to be done.
+ *
+ * So we're currently relying on the ready interrupt firing later then
+ * this and we just disable the resources at the end of wcnss_start().
+ */
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcnss_stop_ack_interrupt(int irq, void *dev)
+{
+ struct qcom_wcnss *wcnss = dev;
+
+ complete(&wcnss->stop_done);
+
+ return IRQ_HANDLED;
+}
+
+static int wcnss_init_regulators(struct qcom_wcnss *wcnss,
+ const struct wcnss_vreg_info *info,
+ int num_vregs)
+{
+ struct regulator_bulk_data *bulk;
+ int ret;
+ int i;
+
+ bulk = devm_kcalloc(wcnss->dev,
+ num_vregs, sizeof(struct regulator_bulk_data),
+ GFP_KERNEL);
+ if (!bulk)
+ return -ENOMEM;
+
+ for (i = 0; i < num_vregs; i++)
+ bulk[i].supply = info[i].name;
+
+ ret = devm_regulator_bulk_get(wcnss->dev, num_vregs, bulk);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < num_vregs; i++) {
+ if (info[i].max_voltage)
+ regulator_set_voltage(bulk[i].consumer,
+ info[i].min_voltage,
+ info[i].max_voltage);
+
+ if (info[i].load_uA)
+ regulator_set_load(bulk[i].consumer, info[i].load_uA);
+ }
+
+ wcnss->vregs = bulk;
+ wcnss->num_vregs = num_vregs;
+
+ return 0;
+}
+
+static int wcnss_request_irq(struct qcom_wcnss *wcnss,
+ struct platform_device *pdev,
+ const char *name,
+ bool optional,
+ irq_handler_t thread_fn)
+{
+ int ret;
+
+ ret = platform_get_irq_byname(pdev, name);
+ if (ret < 0 && optional) {
+ dev_dbg(&pdev->dev, "no %s IRQ defined, ignoring\n", name);
+ return 0;
+ } else 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_TRIGGER_RISING | IRQF_ONESHOT,
+ "wcnss", wcnss);
+ if (ret)
+ dev_err(&pdev->dev, "request %s IRQ failed\n", name);
+
+ return ret;
+}
+
+static int wcnss_alloc_memory_region(struct qcom_wcnss *wcnss)
+{
+ struct device_node *node;
+ struct resource r;
+ int ret;
+
+ node = of_parse_phandle(wcnss->dev->of_node, "memory-region", 0);
+ if (!node) {
+ dev_err(wcnss->dev, "no memory-region specified\n");
+ return -EINVAL;
+ }
+
+ ret = of_address_to_resource(node, 0, &r);
+ if (ret)
+ return ret;
+
+ wcnss->mem_phys = wcnss->mem_reloc = r.start;
+ wcnss->mem_size = resource_size(&r);
+ wcnss->mem_region = devm_ioremap_wc(wcnss->dev, wcnss->mem_phys, wcnss->mem_size);
+ if (!wcnss->mem_region) {
+ dev_err(wcnss->dev, "unable to map memory region: %pa+%zx\n",
+ &r.start, wcnss->mem_size);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int wcnss_probe(struct platform_device *pdev)
+{
+ const struct wcnss_data *data;
+ struct qcom_wcnss *wcnss;
+ struct resource *res;
+ struct rproc *rproc;
+ void __iomem *mmio;
+ int ret;
+
+ data = of_device_get_match_data(&pdev->dev);
+
+ if (!qcom_scm_is_available())
+ return -EPROBE_DEFER;
+
+ if (!qcom_scm_pas_supported(WCNSS_PAS_ID)) {
+ dev_err(&pdev->dev, "PAS is not available for WCNSS\n");
+ return -ENXIO;
+ }
+
+ rproc = rproc_alloc(&pdev->dev, pdev->name, &wcnss_ops,
+ WCNSS_FIRMWARE_NAME, sizeof(*wcnss));
+ if (!rproc) {
+ dev_err(&pdev->dev, "unable to allocate remoteproc\n");
+ return -ENOMEM;
+ }
+
+ rproc->fw_ops = &wcnss_fw_ops;
+
+ wcnss = (struct qcom_wcnss *)rproc->priv;
+ wcnss->dev = &pdev->dev;
+ wcnss->rproc = rproc;
+ platform_set_drvdata(pdev, wcnss);
+
+ init_completion(&wcnss->start_done);
+ init_completion(&wcnss->stop_done);
+
+ mutex_init(&wcnss->iris_lock);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pmu");
+ mmio = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(mmio)) {
+ ret = PTR_ERR(mmio);
+ goto free_rproc;
+ };
+
+ ret = wcnss_alloc_memory_region(wcnss);
+ if (ret)
+ goto free_rproc;
+
+ wcnss->pmu_cfg = mmio + data->pmu_offset;
+ wcnss->spare_out = mmio + data->spare_offset;
+
+ ret = wcnss_init_regulators(wcnss, data->vregs, data->num_vregs);
+ if (ret)
+ goto free_rproc;
+
+ ret = wcnss_request_irq(wcnss, pdev, "wdog", false, wcnss_wdog_interrupt);
+ if (ret < 0)
+ goto free_rproc;
+ wcnss->wdog_irq = ret;
+
+ ret = wcnss_request_irq(wcnss, pdev, "fatal", false, wcnss_fatal_interrupt);
+ if (ret < 0)
+ goto free_rproc;
+ wcnss->fatal_irq = ret;
+
+ ret = wcnss_request_irq(wcnss, pdev, "ready", true, wcnss_ready_interrupt);
+ if (ret < 0)
+ goto free_rproc;
+ wcnss->ready_irq = ret;
+
+ ret = wcnss_request_irq(wcnss, pdev, "handover", true, wcnss_handover_interrupt);
+ if (ret < 0)
+ goto free_rproc;
+ wcnss->handover_irq = ret;
+
+ ret = wcnss_request_irq(wcnss, pdev, "stop-ack", true, wcnss_stop_ack_interrupt);
+ if (ret < 0)
+ goto free_rproc;
+ wcnss->stop_ack_irq = ret;
+
+ if (wcnss->stop_ack_irq) {
+ wcnss->state = qcom_smem_state_get(&pdev->dev, "stop",
+ &wcnss->stop_bit);
+ if (IS_ERR(wcnss->state)) {
+ ret = PTR_ERR(wcnss->state);
+ goto free_rproc;
+ }
+ }
+
+ ret = rproc_add(rproc);
+ if (ret)
+ goto free_rproc;
+
+ return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+
+free_rproc:
+ rproc_free(rproc);
+
+ return ret;
+}
+
+static int wcnss_remove(struct platform_device *pdev)
+{
+ struct qcom_wcnss *wcnss = platform_get_drvdata(pdev);
+
+ of_platform_depopulate(&pdev->dev);
+
+ qcom_smem_state_put(wcnss->state);
+ rproc_del(wcnss->rproc);
+ rproc_free(wcnss->rproc);
+
+ return 0;
+}
+
+static const struct of_device_id wcnss_of_match[] = {
+ { .compatible = "qcom,riva-pil", &riva_data },
+ { .compatible = "qcom,pronto-v1-pil", &pronto_v1_data },
+ { .compatible = "qcom,pronto-v2-pil", &pronto_v2_data },
+ { },
+};
+
+static struct platform_driver wcnss_driver = {
+ .probe = wcnss_probe,
+ .remove = wcnss_remove,
+ .driver = {
+ .name = "qcom-wcnss-pil",
+ .of_match_table = wcnss_of_match,
+ },
+};
+
+module_platform_driver(wcnss_driver);
+MODULE_DESCRIPTION("Qualcomm Peripherial Image Loader for Wireless Subsystem");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/remoteproc/qcom_wcnss.h b/drivers/remoteproc/qcom_wcnss.h
new file mode 100644
index 000000000000..9dc4a9fe41e1
--- /dev/null
+++ b/drivers/remoteproc/qcom_wcnss.h
@@ -0,0 +1,22 @@
+#ifndef __QCOM_WNCSS_H__
+#define __QCOM_WNCSS_H__
+
+struct qcom_iris;
+struct qcom_wcnss;
+
+struct wcnss_vreg_info {
+ const char * const name;
+ int min_voltage;
+ int max_voltage;
+
+ int load_uA;
+
+ bool super_turbo;
+};
+
+int qcom_iris_enable(struct qcom_iris *iris);
+void qcom_iris_disable(struct qcom_iris *iris);
+
+void qcom_wcnss_assign_iris(struct qcom_wcnss *wcnss, struct qcom_iris *iris, bool use_48mhz_xo);
+
+#endif
diff --git a/drivers/remoteproc/qcom_wcnss_iris.c b/drivers/remoteproc/qcom_wcnss_iris.c
new file mode 100644
index 000000000000..f0ca24a8dd0b
--- /dev/null
+++ b/drivers/remoteproc/qcom_wcnss_iris.c
@@ -0,0 +1,188 @@
+/*
+ * Qualcomm Wireless Connectivity Subsystem Iris driver
+ *
+ * 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 <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#include "qcom_wcnss.h"
+
+struct qcom_iris {
+ struct device *dev;
+
+ struct clk *xo_clk;
+
+ struct regulator_bulk_data *vregs;
+ size_t num_vregs;
+};
+
+struct iris_data {
+ const struct wcnss_vreg_info *vregs;
+ size_t num_vregs;
+
+ bool use_48mhz_xo;
+};
+
+static const struct iris_data wcn3620_data = {
+ .vregs = (struct wcnss_vreg_info[]) {
+ { "vddxo", 1800000, 1800000, 10000 },
+ { "vddrfa", 1300000, 1300000, 100000 },
+ { "vddpa", 3300000, 3300000, 515000 },
+ { "vdddig", 1800000, 1800000, 10000 },
+ },
+ .num_vregs = 4,
+ .use_48mhz_xo = false,
+};
+
+static const struct iris_data wcn3660_data = {
+ .vregs = (struct wcnss_vreg_info[]) {
+ { "vddxo", 1800000, 1800000, 10000 },
+ { "vddrfa", 1300000, 1300000, 100000 },
+ { "vddpa", 2900000, 3000000, 515000 },
+ { "vdddig", 1200000, 1225000, 10000 },
+ },
+ .num_vregs = 4,
+ .use_48mhz_xo = true,
+};
+
+static const struct iris_data wcn3680_data = {
+ .vregs = (struct wcnss_vreg_info[]) {
+ { "vddxo", 1800000, 1800000, 10000 },
+ { "vddrfa", 1300000, 1300000, 100000 },
+ { "vddpa", 3300000, 3300000, 515000 },
+ { "vdddig", 1800000, 1800000, 10000 },
+ },
+ .num_vregs = 4,
+ .use_48mhz_xo = true,
+};
+
+int qcom_iris_enable(struct qcom_iris *iris)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(iris->num_vregs, iris->vregs);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(iris->xo_clk);
+ if (ret) {
+ dev_err(iris->dev, "failed to enable xo clk\n");
+ goto disable_regulators;
+ }
+
+ return 0;
+
+disable_regulators:
+ regulator_bulk_disable(iris->num_vregs, iris->vregs);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(qcom_iris_enable);
+
+void qcom_iris_disable(struct qcom_iris *iris)
+{
+ clk_disable_unprepare(iris->xo_clk);
+ regulator_bulk_disable(iris->num_vregs, iris->vregs);
+}
+EXPORT_SYMBOL_GPL(qcom_iris_disable);
+
+static int qcom_iris_probe(struct platform_device *pdev)
+{
+ const struct iris_data *data;
+ struct qcom_wcnss *wcnss;
+ struct qcom_iris *iris;
+ int ret;
+ int i;
+
+ iris = devm_kzalloc(&pdev->dev, sizeof(struct qcom_iris), GFP_KERNEL);
+ if (!iris)
+ return -ENOMEM;
+
+ data = of_device_get_match_data(&pdev->dev);
+ wcnss = dev_get_drvdata(pdev->dev.parent);
+
+ iris->xo_clk = devm_clk_get(&pdev->dev, "xo");
+ if (IS_ERR(iris->xo_clk)) {
+ if (PTR_ERR(iris->xo_clk) != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "failed to acquire xo clk\n");
+ return PTR_ERR(iris->xo_clk);
+ }
+
+ iris->num_vregs = data->num_vregs;
+ iris->vregs = devm_kcalloc(&pdev->dev,
+ iris->num_vregs,
+ sizeof(struct regulator_bulk_data),
+ GFP_KERNEL);
+ if (!iris->vregs)
+ return -ENOMEM;
+
+ for (i = 0; i < iris->num_vregs; i++)
+ iris->vregs[i].supply = data->vregs[i].name;
+
+ ret = devm_regulator_bulk_get(&pdev->dev, iris->num_vregs, iris->vregs);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get regulators\n");
+ return ret;
+ }
+
+ for (i = 0; i < iris->num_vregs; i++) {
+ if (data->vregs[i].max_voltage)
+ regulator_set_voltage(iris->vregs[i].consumer,
+ data->vregs[i].min_voltage,
+ data->vregs[i].max_voltage);
+
+ if (data->vregs[i].load_uA)
+ regulator_set_load(iris->vregs[i].consumer,
+ data->vregs[i].load_uA);
+ }
+
+ qcom_wcnss_assign_iris(wcnss, iris, data->use_48mhz_xo);
+
+ return 0;
+}
+
+static int qcom_iris_remove(struct platform_device *pdev)
+{
+ struct qcom_wcnss *wcnss = dev_get_drvdata(pdev->dev.parent);
+
+ qcom_wcnss_assign_iris(wcnss, NULL, false);
+
+ return 0;
+}
+
+static const struct of_device_id iris_of_match[] = {
+ { .compatible = "qcom,wcn3620", .data = &wcn3620_data },
+ { .compatible = "qcom,wcn3660", .data = &wcn3660_data },
+ { .compatible = "qcom,wcn3680", .data = &wcn3680_data },
+ {}
+};
+
+static struct platform_driver wcnss_driver = {
+ .probe = qcom_iris_probe,
+ .remove = qcom_iris_remove,
+ .driver = {
+ .name = "qcom-iris",
+ .of_match_table = iris_of_match,
+ },
+};
+
+module_platform_driver(wcnss_driver);
+MODULE_DESCRIPTION("Qualcomm Wireless Subsystem Iris driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index fe0539ed9cb5..c6bfb3496684 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -78,7 +78,7 @@ static const char *rproc_crash_to_string(enum rproc_crash_type type)
* will try to access an unmapped device address.
*/
static int rproc_iommu_fault(struct iommu_domain *domain, struct device *dev,
- unsigned long iova, int flags, void *token)
+ unsigned long iova, int flags, void *token)
{
struct rproc *rproc = token;
@@ -236,8 +236,8 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
}
notifyid = ret;
- dev_dbg(dev, "vring%d: va %p dma %llx size %x idr %d\n", i, va,
- (unsigned long long)dma, size, notifyid);
+ dev_dbg(dev, "vring%d: va %p dma %pad size 0x%x idr %d\n",
+ i, va, &dma, size, notifyid);
rvring->va = va;
rvring->dma = dma;
@@ -263,19 +263,13 @@ rproc_parse_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i)
struct fw_rsc_vdev_vring *vring = &rsc->vring[i];
struct rproc_vring *rvring = &rvdev->vring[i];
- dev_dbg(dev, "vdev rsc: vring%d: da %x, qsz %d, align %d\n",
- i, vring->da, vring->num, vring->align);
-
- /* make sure reserved bytes are zeroes */
- if (vring->reserved) {
- dev_err(dev, "vring rsc has non zero reserved bytes\n");
- return -EINVAL;
- }
+ dev_dbg(dev, "vdev rsc: vring%d: da 0x%x, qsz %d, align %d\n",
+ i, vring->da, vring->num, vring->align);
/* verify queue size and vring alignment are sane */
if (!vring->num || !vring->align) {
dev_err(dev, "invalid qsz (%d) or alignment (%d)\n",
- vring->num, vring->align);
+ vring->num, vring->align);
return -EINVAL;
}
@@ -330,7 +324,7 @@ void rproc_free_vring(struct rproc_vring *rvring)
* Returns 0 on success, or an appropriate error code otherwise
*/
static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
- int offset, int avail)
+ int offset, int avail)
{
struct device *dev = &rproc->dev;
struct rproc_vdev *rvdev;
@@ -349,7 +343,7 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
return -EINVAL;
}
- dev_dbg(dev, "vdev rsc: id %d, dfeatures %x, cfg len %d, %d vrings\n",
+ dev_dbg(dev, "vdev rsc: id %d, dfeatures 0x%x, cfg len %d, %d vrings\n",
rsc->id, rsc->dfeatures, rsc->config_len, rsc->num_of_vrings);
/* we currently support only two vrings per rvdev */
@@ -358,7 +352,7 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
return -EINVAL;
}
- rvdev = kzalloc(sizeof(struct rproc_vdev), GFP_KERNEL);
+ rvdev = kzalloc(sizeof(*rvdev), GFP_KERNEL);
if (!rvdev)
return -ENOMEM;
@@ -407,7 +401,7 @@ free_rvdev:
* Returns 0 on success, or an appropriate error code otherwise
*/
static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
- int offset, int avail)
+ int offset, int avail)
{
struct rproc_mem_entry *trace;
struct device *dev = &rproc->dev;
@@ -455,8 +449,8 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
rproc->num_traces++;
- dev_dbg(dev, "%s added: va %p, da 0x%x, len 0x%x\n", name, ptr,
- rsc->da, rsc->len);
+ dev_dbg(dev, "%s added: va %p, da 0x%x, len 0x%x\n",
+ name, ptr, rsc->da, rsc->len);
return 0;
}
@@ -487,7 +481,7 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
* are outside those ranges.
*/
static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc,
- int offset, int avail)
+ int offset, int avail)
{
struct rproc_mem_entry *mapping;
struct device *dev = &rproc->dev;
@@ -530,7 +524,7 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc,
list_add_tail(&mapping->node, &rproc->mappings);
dev_dbg(dev, "mapped devmem pa 0x%x, da 0x%x, len 0x%x\n",
- rsc->pa, rsc->da, rsc->len);
+ rsc->pa, rsc->da, rsc->len);
return 0;
@@ -558,9 +552,8 @@ out:
* pressure is important; it may have a substantial impact on performance.
*/
static int rproc_handle_carveout(struct rproc *rproc,
- struct fw_rsc_carveout *rsc,
- int offset, int avail)
-
+ struct fw_rsc_carveout *rsc,
+ int offset, int avail)
{
struct rproc_mem_entry *carveout, *mapping;
struct device *dev = &rproc->dev;
@@ -579,8 +572,8 @@ static int rproc_handle_carveout(struct rproc *rproc,
return -EINVAL;
}
- dev_dbg(dev, "carveout rsc: da %x, pa %x, len %x, flags %x\n",
- rsc->da, rsc->pa, rsc->len, rsc->flags);
+ dev_dbg(dev, "carveout rsc: name: %s, da 0x%x, pa 0x%x, len 0x%x, flags 0x%x\n",
+ rsc->name, rsc->da, rsc->pa, rsc->len, rsc->flags);
carveout = kzalloc(sizeof(*carveout), GFP_KERNEL);
if (!carveout)
@@ -588,13 +581,14 @@ static int rproc_handle_carveout(struct rproc *rproc,
va = dma_alloc_coherent(dev->parent, rsc->len, &dma, GFP_KERNEL);
if (!va) {
- dev_err(dev->parent, "dma_alloc_coherent err: %d\n", rsc->len);
+ dev_err(dev->parent,
+ "failed to allocate dma memory: len 0x%x\n", rsc->len);
ret = -ENOMEM;
goto free_carv;
}
- dev_dbg(dev, "carveout va %p, dma %llx, len 0x%x\n", va,
- (unsigned long long)dma, rsc->len);
+ dev_dbg(dev, "carveout va %p, dma %pad, len 0x%x\n",
+ va, &dma, rsc->len);
/*
* Ok, this is non-standard.
@@ -616,13 +610,12 @@ static int rproc_handle_carveout(struct rproc *rproc,
if (rproc->domain) {
mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
if (!mapping) {
- dev_err(dev, "kzalloc mapping failed\n");
ret = -ENOMEM;
goto dma_free;
}
ret = iommu_map(rproc->domain, rsc->da, dma, rsc->len,
- rsc->flags);
+ rsc->flags);
if (ret) {
dev_err(dev, "iommu_map failed: %d\n", ret);
goto free_mapping;
@@ -639,8 +632,8 @@ static int rproc_handle_carveout(struct rproc *rproc,
mapping->len = rsc->len;
list_add_tail(&mapping->node, &rproc->mappings);
- dev_dbg(dev, "carveout mapped 0x%x to 0x%llx\n",
- rsc->da, (unsigned long long)dma);
+ dev_dbg(dev, "carveout mapped 0x%x to %pad\n",
+ rsc->da, &dma);
}
/*
@@ -697,17 +690,13 @@ static rproc_handle_resource_t rproc_loading_handlers[RSC_LAST] = {
[RSC_CARVEOUT] = (rproc_handle_resource_t)rproc_handle_carveout,
[RSC_DEVMEM] = (rproc_handle_resource_t)rproc_handle_devmem,
[RSC_TRACE] = (rproc_handle_resource_t)rproc_handle_trace,
- [RSC_VDEV] = NULL, /* VDEVs were handled upon registrarion */
+ [RSC_VDEV] = (rproc_handle_resource_t)rproc_count_vrings,
};
static rproc_handle_resource_t rproc_vdev_handler[RSC_LAST] = {
[RSC_VDEV] = (rproc_handle_resource_t)rproc_handle_vdev,
};
-static rproc_handle_resource_t rproc_count_vrings_handler[RSC_LAST] = {
- [RSC_VDEV] = (rproc_handle_resource_t)rproc_count_vrings,
-};
-
/* handle firmware resource entries before booting the remote processor */
static int rproc_handle_resources(struct rproc *rproc, int len,
rproc_handle_resource_t handlers[RSC_LAST])
@@ -757,6 +746,7 @@ static int rproc_handle_resources(struct rproc *rproc, int len,
static void rproc_resource_cleanup(struct rproc *rproc)
{
struct rproc_mem_entry *entry, *tmp;
+ struct rproc_vdev *rvdev, *rvtmp;
struct device *dev = &rproc->dev;
/* clean up debugfs trace entries */
@@ -775,7 +765,7 @@ static void rproc_resource_cleanup(struct rproc *rproc)
if (unmapped != entry->len) {
/* nothing much to do besides complaining */
dev_err(dev, "failed to unmap %u/%zu\n", entry->len,
- unmapped);
+ unmapped);
}
list_del(&entry->node);
@@ -789,6 +779,10 @@ static void rproc_resource_cleanup(struct rproc *rproc)
list_del(&entry->node);
kfree(entry);
}
+
+ /* clean up remote vdev entries */
+ list_for_each_entry_safe(rvdev, rvtmp, &rproc->rvdevs, node)
+ rproc_remove_virtio_dev(rvdev);
}
/*
@@ -801,9 +795,6 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
struct resource_table *table, *loaded_table;
int ret, tablesz;
- if (!rproc->table_ptr)
- return -ENOMEM;
-
ret = rproc_fw_sanity_check(rproc, fw);
if (ret)
return ret;
@@ -830,9 +821,25 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
goto clean_up;
}
- /* Verify that resource table in loaded fw is unchanged */
- if (rproc->table_csum != crc32(0, table, tablesz)) {
- dev_err(dev, "resource checksum failed, fw changed?\n");
+ /*
+ * Create a copy of the resource table. When a virtio device starts
+ * and calls vring_new_virtqueue() the address of the allocated vring
+ * will be stored in the cached_table. Before the device is started,
+ * cached_table will be copied into device memory.
+ */
+ rproc->cached_table = kmemdup(table, tablesz, GFP_KERNEL);
+ if (!rproc->cached_table)
+ goto clean_up;
+
+ rproc->table_ptr = rproc->cached_table;
+
+ /* reset max_notifyid */
+ rproc->max_notifyid = -1;
+
+ /* look for virtio devices and register them */
+ ret = rproc_handle_resources(rproc, tablesz, rproc_vdev_handler);
+ if (ret) {
+ dev_err(dev, "Failed to handle vdev resources: %d\n", ret);
goto clean_up;
}
@@ -840,49 +847,50 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
ret = rproc_handle_resources(rproc, tablesz, rproc_loading_handlers);
if (ret) {
dev_err(dev, "Failed to process resources: %d\n", ret);
- goto clean_up;
+ goto clean_up_resources;
}
/* load the ELF segments to memory */
ret = rproc_load_segments(rproc, fw);
if (ret) {
dev_err(dev, "Failed to load program segments: %d\n", ret);
- goto clean_up;
+ goto clean_up_resources;
}
/*
* The starting device has been given the rproc->cached_table as the
* resource table. The address of the vring along with the other
* allocated resources (carveouts etc) is stored in cached_table.
- * In order to pass this information to the remote device we must
- * copy this information to device memory.
+ * In order to pass this information to the remote device we must copy
+ * this information to device memory. We also update the table_ptr so
+ * that any subsequent changes will be applied to the loaded version.
*/
loaded_table = rproc_find_loaded_rsc_table(rproc, fw);
- if (loaded_table)
+ if (loaded_table) {
memcpy(loaded_table, rproc->cached_table, tablesz);
+ rproc->table_ptr = loaded_table;
+ }
/* power up the remote processor */
ret = rproc->ops->start(rproc);
if (ret) {
dev_err(dev, "can't start rproc %s: %d\n", rproc->name, ret);
- goto clean_up;
+ goto clean_up_resources;
}
- /*
- * Update table_ptr so that all subsequent vring allocations and
- * virtio fields manipulation update the actual loaded resource table
- * in device memory.
- */
- rproc->table_ptr = loaded_table;
-
rproc->state = RPROC_RUNNING;
dev_info(dev, "remote processor %s is now up\n", rproc->name);
return 0;
-clean_up:
+clean_up_resources:
rproc_resource_cleanup(rproc);
+clean_up:
+ kfree(rproc->cached_table);
+ rproc->cached_table = NULL;
+ rproc->table_ptr = NULL;
+
rproc_disable_iommu(rproc);
return ret;
}
@@ -898,42 +906,11 @@ clean_up:
static void rproc_fw_config_virtio(const struct firmware *fw, void *context)
{
struct rproc *rproc = context;
- struct resource_table *table;
- int ret, tablesz;
-
- if (rproc_fw_sanity_check(rproc, fw) < 0)
- goto out;
-
- /* look for the resource table */
- table = rproc_find_rsc_table(rproc, fw, &tablesz);
- if (!table)
- goto out;
-
- rproc->table_csum = crc32(0, table, tablesz);
-
- /*
- * Create a copy of the resource table. When a virtio device starts
- * and calls vring_new_virtqueue() the address of the allocated vring
- * will be stored in the cached_table. Before the device is started,
- * cached_table will be copied into devic memory.
- */
- rproc->cached_table = kmemdup(table, tablesz, GFP_KERNEL);
- if (!rproc->cached_table)
- goto out;
-
- rproc->table_ptr = rproc->cached_table;
- /* count the number of notify-ids */
- rproc->max_notifyid = -1;
- ret = rproc_handle_resources(rproc, tablesz,
- rproc_count_vrings_handler);
- if (ret)
- goto out;
-
- /* look for virtio devices and register them */
- ret = rproc_handle_resources(rproc, tablesz, rproc_vdev_handler);
+ /* if rproc is marked always-on, request it to boot */
+ if (rproc->auto_boot)
+ rproc_boot_nowait(rproc);
-out:
release_firmware(fw);
/* allow rproc_del() contexts, if any, to proceed */
complete_all(&rproc->firmware_loading_complete);
@@ -969,7 +946,7 @@ static int rproc_add_virtio_devices(struct rproc *rproc)
* rproc_trigger_recovery() - recover a remoteproc
* @rproc: the remote processor
*
- * The recovery is done by reseting all the virtio devices, that way all the
+ * The recovery is done by resetting all the virtio devices, that way all the
* rpmsg drivers will be reseted along with the remote processor making the
* remoteproc functional again.
*
@@ -977,23 +954,23 @@ static int rproc_add_virtio_devices(struct rproc *rproc)
*/
int rproc_trigger_recovery(struct rproc *rproc)
{
- struct rproc_vdev *rvdev, *rvtmp;
-
dev_err(&rproc->dev, "recovering %s\n", rproc->name);
init_completion(&rproc->crash_comp);
- /* clean up remote vdev entries */
- list_for_each_entry_safe(rvdev, rvtmp, &rproc->rvdevs, node)
- rproc_remove_virtio_dev(rvdev);
+ /* shut down the remote */
+ /* TODO: make sure this works with rproc->power > 1 */
+ rproc_shutdown(rproc);
/* wait until there is no more rproc users */
wait_for_completion(&rproc->crash_comp);
- /* Free the copy of the resource table */
- kfree(rproc->cached_table);
+ /*
+ * boot the remote processor up again
+ */
+ rproc_boot(rproc);
- return rproc_add_virtio_devices(rproc);
+ return 0;
}
/**
@@ -1058,20 +1035,6 @@ static int __rproc_boot(struct rproc *rproc, bool wait)
return ret;
}
- /* loading a firmware is required */
- if (!rproc->firmware) {
- dev_err(dev, "%s: no firmware to load\n", __func__);
- ret = -EINVAL;
- goto unlock_mutex;
- }
-
- /* prevent underlying implementation from being removed */
- if (!try_module_get(dev->parent->driver->owner)) {
- dev_err(dev, "%s: can't get owner\n", __func__);
- ret = -EINVAL;
- goto unlock_mutex;
- }
-
/* skip the boot process if rproc is already powered up */
if (atomic_inc_return(&rproc->power) > 1) {
ret = 0;
@@ -1096,10 +1059,8 @@ static int __rproc_boot(struct rproc *rproc, bool wait)
release_firmware(firmware_p);
downref_rproc:
- if (ret) {
- module_put(dev->parent->driver->owner);
+ if (ret)
atomic_dec(&rproc->power);
- }
unlock_mutex:
mutex_unlock(&rproc->lock);
return ret;
@@ -1173,8 +1134,10 @@ void rproc_shutdown(struct rproc *rproc)
rproc_disable_iommu(rproc);
- /* Give the next start a clean resource table */
- rproc->table_ptr = rproc->cached_table;
+ /* Free the copy of the resource table */
+ kfree(rproc->cached_table);
+ rproc->cached_table = NULL;
+ rproc->table_ptr = NULL;
/* if in crash state, unlock crash handler */
if (rproc->state == RPROC_CRASHED)
@@ -1186,8 +1149,6 @@ void rproc_shutdown(struct rproc *rproc)
out:
mutex_unlock(&rproc->lock);
- if (!ret)
- module_put(dev->parent->driver->owner);
}
EXPORT_SYMBOL(rproc_shutdown);
@@ -1216,6 +1177,12 @@ struct rproc *rproc_get_by_phandle(phandle phandle)
mutex_lock(&rproc_list_mutex);
list_for_each_entry(r, &rproc_list, node) {
if (r->dev.parent && r->dev.parent->of_node == np) {
+ /* prevent underlying implementation from being removed */
+ if (!try_module_get(r->dev.parent->driver->owner)) {
+ dev_err(&r->dev, "can't get owner\n");
+ break;
+ }
+
rproc = r;
get_device(&rproc->dev);
break;
@@ -1335,11 +1302,11 @@ static struct device_type rproc_type = {
* On success the new rproc is returned, and on failure, NULL.
*
* Note: _never_ directly deallocate @rproc, even if it was not registered
- * yet. Instead, when you need to unroll rproc_alloc(), use rproc_put().
+ * yet. Instead, when you need to unroll rproc_alloc(), use rproc_free().
*/
struct rproc *rproc_alloc(struct device *dev, const char *name,
- const struct rproc_ops *ops,
- const char *firmware, int len)
+ const struct rproc_ops *ops,
+ const char *firmware, int len)
{
struct rproc *rproc;
char *p, *template = "rproc-%s-fw";
@@ -1359,7 +1326,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
*/
name_len = strlen(name) + strlen(template) - 2 + 1;
- rproc = kzalloc(sizeof(struct rproc) + len + name_len, GFP_KERNEL);
+ rproc = kzalloc(sizeof(*rproc) + len + name_len, GFP_KERNEL);
if (!rproc)
return NULL;
@@ -1374,6 +1341,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
rproc->name = name;
rproc->ops = ops;
rproc->priv = &rproc[1];
+ rproc->auto_boot = true;
device_initialize(&rproc->dev);
rproc->dev.parent = dev;
@@ -1413,7 +1381,22 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
EXPORT_SYMBOL(rproc_alloc);
/**
- * rproc_put() - unroll rproc_alloc()
+ * rproc_free() - unroll rproc_alloc()
+ * @rproc: the remote processor handle
+ *
+ * This function decrements the rproc dev refcount.
+ *
+ * If no one holds any reference to rproc anymore, then its refcount would
+ * now drop to zero, and it would be freed.
+ */
+void rproc_free(struct rproc *rproc)
+{
+ put_device(&rproc->dev);
+}
+EXPORT_SYMBOL(rproc_free);
+
+/**
+ * rproc_put() - release rproc reference
* @rproc: the remote processor handle
*
* This function decrements the rproc dev refcount.
@@ -1423,6 +1406,7 @@ EXPORT_SYMBOL(rproc_alloc);
*/
void rproc_put(struct rproc *rproc)
{
+ module_put(rproc->dev.parent->driver->owner);
put_device(&rproc->dev);
}
EXPORT_SYMBOL(rproc_put);
@@ -1438,7 +1422,7 @@ EXPORT_SYMBOL(rproc_put);
*
* After rproc_del() returns, @rproc isn't freed yet, because
* of the outstanding reference created by rproc_alloc. To decrement that
- * one last refcount, one still needs to call rproc_put().
+ * one last refcount, one still needs to call rproc_free().
*
* Returns 0 on success and -EINVAL if @rproc isn't valid.
*/
@@ -1452,13 +1436,15 @@ int rproc_del(struct rproc *rproc)
/* 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);
+
/* clean up remote vdev entries */
list_for_each_entry_safe(rvdev, tmp, &rproc->rvdevs, node)
rproc_remove_virtio_dev(rvdev);
- /* Free the copy of the resource table */
- kfree(rproc->cached_table);
-
/* the rproc is downref'ed as soon as it's removed from the klist */
mutex_lock(&rproc_list_mutex);
list_del(&rproc->node);
diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c
index 74a120b6e206..374797206c79 100644
--- a/drivers/remoteproc/remoteproc_debugfs.c
+++ b/drivers/remoteproc/remoteproc_debugfs.c
@@ -45,7 +45,7 @@ static struct dentry *rproc_dbg;
* as it provides very early tracing with little to no dependencies at all.
*/
static ssize_t rproc_trace_read(struct file *filp, char __user *userbuf,
- size_t count, loff_t *ppos)
+ size_t count, loff_t *ppos)
{
struct rproc_mem_entry *trace = filp->private_data;
int len = strnlen(trace->va, trace->len);
@@ -73,7 +73,7 @@ static const char * const rproc_state_string[] = {
/* expose the state of the remote processor via debugfs */
static ssize_t rproc_state_read(struct file *filp, char __user *userbuf,
- size_t count, loff_t *ppos)
+ size_t count, loff_t *ppos)
{
struct rproc *rproc = filp->private_data;
unsigned int state;
@@ -83,7 +83,7 @@ static ssize_t rproc_state_read(struct file *filp, char __user *userbuf,
state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state;
i = scnprintf(buf, 30, "%.28s (%d)\n", rproc_state_string[state],
- rproc->state);
+ rproc->state);
return simple_read_from_buffer(userbuf, count, ppos, buf, i);
}
@@ -130,7 +130,7 @@ static const struct file_operations rproc_state_ops = {
/* expose the name of the remote processor via debugfs */
static ssize_t rproc_name_read(struct file *filp, char __user *userbuf,
- size_t count, loff_t *ppos)
+ size_t count, loff_t *ppos)
{
struct rproc *rproc = filp->private_data;
/* need room for the name, a newline and a terminating null */
@@ -230,12 +230,12 @@ void rproc_remove_trace_file(struct dentry *tfile)
}
struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc,
- struct rproc_mem_entry *trace)
+ struct rproc_mem_entry *trace)
{
struct dentry *tfile;
- tfile = debugfs_create_file(name, 0400, rproc->dbg_dir,
- trace, &trace_rproc_ops);
+ tfile = debugfs_create_file(name, 0400, rproc->dbg_dir, trace,
+ &trace_rproc_ops);
if (!tfile) {
dev_err(&rproc->dev, "failed to create debugfs trace entry\n");
return NULL;
@@ -264,11 +264,11 @@ void rproc_create_debug_dir(struct rproc *rproc)
return;
debugfs_create_file("name", 0400, rproc->dbg_dir,
- rproc, &rproc_name_ops);
+ rproc, &rproc_name_ops);
debugfs_create_file("state", 0400, rproc->dbg_dir,
- rproc, &rproc_state_ops);
+ rproc, &rproc_state_ops);
debugfs_create_file("recovery", 0400, rproc->dbg_dir,
- rproc, &rproc_recovery_ops);
+ rproc, &rproc_recovery_ops);
}
void __init rproc_init_debugfs(void)
diff --git a/drivers/remoteproc/remoteproc_elf_loader.c b/drivers/remoteproc/remoteproc_elf_loader.c
index ce283a5b42a1..c523983a4aec 100644
--- a/drivers/remoteproc/remoteproc_elf_loader.c
+++ b/drivers/remoteproc/remoteproc_elf_loader.c
@@ -166,18 +166,18 @@ rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
continue;
dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n",
- phdr->p_type, da, memsz, filesz);
+ phdr->p_type, da, memsz, filesz);
if (filesz > memsz) {
dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n",
- filesz, memsz);
+ filesz, memsz);
ret = -EINVAL;
break;
}
if (offset + filesz > fw->size) {
dev_err(dev, "truncated fw: need 0x%x avail 0x%zx\n",
- offset + filesz, fw->size);
+ offset + filesz, fw->size);
ret = -EINVAL;
break;
}
diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
index 57e1de59bec8..4cf93ca2816e 100644
--- a/drivers/remoteproc/remoteproc_internal.h
+++ b/drivers/remoteproc/remoteproc_internal.h
@@ -36,10 +36,10 @@ struct rproc;
*/
struct rproc_fw_ops {
struct resource_table *(*find_rsc_table)(struct rproc *rproc,
- const struct firmware *fw,
- int *tablesz);
- struct resource_table *(*find_loaded_rsc_table)(struct rproc *rproc,
- const struct firmware *fw);
+ const struct firmware *fw,
+ int *tablesz);
+ struct resource_table *(*find_loaded_rsc_table)(
+ struct rproc *rproc, const struct firmware *fw);
int (*load)(struct rproc *rproc, const struct firmware *fw);
int (*sanity_check)(struct rproc *rproc, const struct firmware *fw);
u32 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);
@@ -57,7 +57,7 @@ void rproc_remove_virtio_dev(struct rproc_vdev *rvdev);
/* from remoteproc_debugfs.c */
void rproc_remove_trace_file(struct dentry *tfile);
struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc,
- struct rproc_mem_entry *trace);
+ struct rproc_mem_entry *trace);
void rproc_delete_debug_dir(struct rproc *rproc);
void rproc_create_debug_dir(struct rproc *rproc);
void rproc_init_debugfs(void);
@@ -98,7 +98,8 @@ int rproc_load_segments(struct rproc *rproc, const struct firmware *fw)
static inline
struct resource_table *rproc_find_rsc_table(struct rproc *rproc,
- const struct firmware *fw, int *tablesz)
+ const struct firmware *fw,
+ int *tablesz)
{
if (rproc->fw_ops->find_rsc_table)
return rproc->fw_ops->find_rsc_table(rproc, fw, tablesz);
@@ -108,7 +109,7 @@ struct resource_table *rproc_find_rsc_table(struct rproc *rproc,
static inline
struct resource_table *rproc_find_loaded_rsc_table(struct rproc *rproc,
- const struct firmware *fw)
+ const struct firmware *fw)
{
if (rproc->fw_ops->find_loaded_rsc_table)
return rproc->fw_ops->find_loaded_rsc_table(rproc, fw);
diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c
index cc91556313e1..01870a16d6d2 100644
--- a/drivers/remoteproc/remoteproc_virtio.c
+++ b/drivers/remoteproc/remoteproc_virtio.c
@@ -69,7 +69,7 @@ irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int notifyid)
EXPORT_SYMBOL(rproc_vq_interrupt);
static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
- unsigned id,
+ unsigned int id,
void (*callback)(struct virtqueue *vq),
const char *name)
{
@@ -101,14 +101,14 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
memset(addr, 0, size);
dev_dbg(dev, "vring%d: va %p qsz %d notifyid %d\n",
- id, addr, len, rvring->notifyid);
+ id, addr, len, rvring->notifyid);
/*
* Create the new vq, and tell virtio we're not interested in
* the 'weak' smp barriers, since we're talking with a real device.
*/
vq = vring_new_virtqueue(id, len, rvring->align, vdev, false, addr,
- rproc_virtio_notify, callback, name);
+ rproc_virtio_notify, callback, name);
if (!vq) {
dev_err(dev, "vring_new_virtqueue %s failed\n", name);
rproc_free_vring(rvring);
@@ -136,20 +136,14 @@ static void __rproc_virtio_del_vqs(struct virtio_device *vdev)
static void rproc_virtio_del_vqs(struct virtio_device *vdev)
{
- struct rproc *rproc = vdev_to_rproc(vdev);
-
- /* power down the remote processor before deleting vqs */
- rproc_shutdown(rproc);
-
__rproc_virtio_del_vqs(vdev);
}
-static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs,
- struct virtqueue *vqs[],
- vq_callback_t *callbacks[],
- const char * const names[])
+static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned int nvqs,
+ struct virtqueue *vqs[],
+ vq_callback_t *callbacks[],
+ const char * const names[])
{
- struct rproc *rproc = vdev_to_rproc(vdev);
int i, ret;
for (i = 0; i < nvqs; ++i) {
@@ -160,13 +154,6 @@ static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs,
}
}
- /* now that the vqs are all set, boot the remote processor */
- ret = rproc_boot_nowait(rproc);
- if (ret) {
- dev_err(&rproc->dev, "rproc_boot() failed %d\n", ret);
- goto error;
- }
-
return 0;
error:
@@ -239,8 +226,8 @@ static int rproc_virtio_finalize_features(struct virtio_device *vdev)
return 0;
}
-static void rproc_virtio_get(struct virtio_device *vdev, unsigned offset,
- void *buf, unsigned len)
+static void rproc_virtio_get(struct virtio_device *vdev, unsigned int offset,
+ void *buf, unsigned int len)
{
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
struct fw_rsc_vdev *rsc;
@@ -257,8 +244,8 @@ static void rproc_virtio_get(struct virtio_device *vdev, unsigned offset,
memcpy(buf, cfg + offset, len);
}
-static void rproc_virtio_set(struct virtio_device *vdev, unsigned offset,
- const void *buf, unsigned len)
+static void rproc_virtio_set(struct virtio_device *vdev, unsigned int offset,
+ const void *buf, unsigned int len)
{
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
struct fw_rsc_vdev *rsc;
diff --git a/drivers/remoteproc/st_remoteproc.c b/drivers/remoteproc/st_remoteproc.c
index 6f056caa8a56..ae8963fcc8c8 100644
--- a/drivers/remoteproc/st_remoteproc.c
+++ b/drivers/remoteproc/st_remoteproc.c
@@ -262,7 +262,7 @@ static int st_rproc_probe(struct platform_device *pdev)
return 0;
free_rproc:
- rproc_put(rproc);
+ rproc_free(rproc);
return ret;
}
@@ -277,7 +277,7 @@ static int st_rproc_remove(struct platform_device *pdev)
of_reserved_mem_device_release(&pdev->dev);
- rproc_put(rproc);
+ rproc_free(rproc);
return 0;
}
diff --git a/drivers/remoteproc/ste_modem_rproc.c b/drivers/remoteproc/ste_modem_rproc.c
index 53dc17bdd54e..03d69a9a3c5b 100644
--- a/drivers/remoteproc/ste_modem_rproc.c
+++ b/drivers/remoteproc/ste_modem_rproc.c
@@ -257,7 +257,7 @@ static int sproc_drv_remove(struct platform_device *pdev)
rproc_del(sproc->rproc);
dma_free_coherent(sproc->rproc->dev.parent, SPROC_FW_SIZE,
sproc->fw_addr, sproc->fw_dma_addr);
- rproc_put(sproc->rproc);
+ rproc_free(sproc->rproc);
mdev->drv_data = NULL;
@@ -325,7 +325,7 @@ free_mem:
free_rproc:
/* Reset device data upon error */
mdev->drv_data = NULL;
- rproc_put(rproc);
+ rproc_free(rproc);
return err;
}
diff --git a/drivers/remoteproc/wkup_m3_rproc.c b/drivers/remoteproc/wkup_m3_rproc.c
index 02d271d101b4..18175d0331fd 100644
--- a/drivers/remoteproc/wkup_m3_rproc.c
+++ b/drivers/remoteproc/wkup_m3_rproc.c
@@ -167,6 +167,8 @@ static int wkup_m3_rproc_probe(struct platform_device *pdev)
goto err;
}
+ rproc->auto_boot = false;
+
wkupm3 = rproc->priv;
wkupm3->rproc = rproc;
wkupm3->pdev = pdev;
@@ -206,7 +208,7 @@ static int wkup_m3_rproc_probe(struct platform_device *pdev)
return 0;
err_put_rproc:
- rproc_put(rproc);
+ rproc_free(rproc);
err:
pm_runtime_put_noidle(dev);
pm_runtime_disable(dev);
@@ -218,7 +220,7 @@ static int wkup_m3_rproc_remove(struct platform_device *pdev)
struct rproc *rproc = platform_get_drvdata(pdev);
rproc_del(rproc);
- rproc_put(rproc);
+ rproc_free(rproc);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);