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/Makefile7
-rw-r--r--drivers/remoteproc/qcom_adsp_pil.c399
-rw-r--r--drivers/remoteproc/qcom_q6v5_pil.c1
-rw-r--r--drivers/remoteproc/qcom_wcnss.c53
-rw-r--r--drivers/remoteproc/qcom_wcnss.h2
-rw-r--r--drivers/remoteproc/qcom_wcnss_iris.c9
-rw-r--r--drivers/remoteproc/remoteproc_core.c222
-rw-r--r--drivers/remoteproc/remoteproc_debugfs.c71
-rw-r--r--drivers/remoteproc/remoteproc_internal.h6
-rw-r--r--drivers/remoteproc/remoteproc_sysfs.c151
-rw-r--r--drivers/remoteproc/remoteproc_virtio.c17
-rw-r--r--drivers/remoteproc/st_remoteproc.c4
13 files changed, 796 insertions, 162 deletions
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 14d5d2d43a38..9c41128750ca 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -78,6 +78,17 @@ config DA8XX_REMOTEPROC
It's safe to say n here if you're not interested in multimedia
offloading.
+config QCOM_ADSP_PIL
+ tristate "Qualcomm ADSP Peripheral Image Loader"
+ depends on OF && ARCH_QCOM
+ depends on QCOM_SMEM
+ select MFD_SYSCON
+ select QCOM_MDT_LOADER
+ select REMOTEPROC
+ help
+ Say y here to support the TrustZone based Peripherial Image Loader
+ for the Qualcomm ADSP remote processors.
+
config QCOM_MDT_LOADER
tristate
@@ -92,10 +103,6 @@ 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
@@ -103,7 +110,6 @@ config QCOM_WCNSS_PIL
depends on REMOTEPROC
select QCOM_MDT_LOADER
select QCOM_SCM
- select QCOM_WCNSS_IRIS
help
Say y here to support the Peripheral Image Loader for the Qualcomm
Wireless Connectivity Subsystem.
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 924f0cb25470..6290c3f72e83 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -5,15 +5,18 @@
obj-$(CONFIG_REMOTEPROC) += remoteproc.o
remoteproc-y := remoteproc_core.o
remoteproc-y += remoteproc_debugfs.o
+remoteproc-y += remoteproc_sysfs.o
remoteproc-y += remoteproc_virtio.o
remoteproc-y += remoteproc_elf_loader.o
obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o
obj-$(CONFIG_STE_MODEM_RPROC) += ste_modem_rproc.o
obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o
obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o
+obj-$(CONFIG_QCOM_ADSP_PIL) += qcom_adsp_pil.o
obj-$(CONFIG_QCOM_MDT_LOADER) += qcom_mdt_loader.o
obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o
-obj-$(CONFIG_QCOM_WCNSS_IRIS) += qcom_wcnss_iris.o
-obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss.o
+obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss_pil.o
+qcom_wcnss_pil-y += qcom_wcnss.o
+qcom_wcnss_pil-y += qcom_wcnss_iris.o
obj-$(CONFIG_ST_REMOTEPROC) += st_remoteproc.o
obj-$(CONFIG_ST_SLIM_REMOTEPROC) += st_slim_rproc.o
diff --git a/drivers/remoteproc/qcom_adsp_pil.c b/drivers/remoteproc/qcom_adsp_pil.c
new file mode 100644
index 000000000000..914163315091
--- /dev/null
+++ b/drivers/remoteproc/qcom_adsp_pil.c
@@ -0,0 +1,399 @@
+/*
+ * Qualcomm ADSP Peripheral Image Loader for MSM8974 and MSM8996
+ *
+ * Copyright (C) 2016 Linaro Ltd
+ * Copyright (C) 2014 Sony Mobile Communications AB
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.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"
+
+#define ADSP_CRASH_REASON_SMEM 423
+#define ADSP_FIRMWARE_NAME "adsp.mdt"
+#define ADSP_PAS_ID 1
+
+struct qcom_adsp {
+ struct device *dev;
+ struct rproc *rproc;
+
+ int wdog_irq;
+ int fatal_irq;
+ int ready_irq;
+ int handover_irq;
+ int stop_ack_irq;
+
+ struct qcom_smem_state *state;
+ unsigned stop_bit;
+
+ struct regulator *cx_supply;
+
+ struct completion start_done;
+ struct completion stop_done;
+
+ phys_addr_t mem_phys;
+ phys_addr_t mem_reloc;
+ void *mem_region;
+ size_t mem_size;
+};
+
+static int adsp_load(struct rproc *rproc, const struct firmware *fw)
+{
+ struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
+ phys_addr_t fw_addr;
+ size_t fw_size;
+ bool relocate;
+ int ret;
+
+ ret = qcom_scm_pas_init_image(ADSP_PAS_ID, fw->data, fw->size);
+ if (ret) {
+ dev_err(&rproc->dev, "invalid firmware metadata\n");
+ return ret;
+ }
+
+ ret = qcom_mdt_parse(fw, &fw_addr, &fw_size, &relocate);
+ if (ret) {
+ dev_err(&rproc->dev, "failed to parse mdt header\n");
+ return ret;
+ }
+
+ if (relocate) {
+ adsp->mem_reloc = fw_addr;
+
+ ret = qcom_scm_pas_mem_setup(ADSP_PAS_ID, adsp->mem_phys, fw_size);
+ if (ret) {
+ dev_err(&rproc->dev, "unable to setup memory for image\n");
+ return ret;
+ }
+ }
+
+ return qcom_mdt_load(rproc, fw, rproc->firmware);
+}
+
+static const struct rproc_fw_ops adsp_fw_ops = {
+ .find_rsc_table = qcom_mdt_find_rsc_table,
+ .load = adsp_load,
+};
+
+static int adsp_start(struct rproc *rproc)
+{
+ struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
+ int ret;
+
+ ret = regulator_enable(adsp->cx_supply);
+ if (ret)
+ return ret;
+
+ ret = qcom_scm_pas_auth_and_reset(ADSP_PAS_ID);
+ if (ret) {
+ dev_err(adsp->dev,
+ "failed to authenticate image and release reset\n");
+ goto disable_regulators;
+ }
+
+ ret = wait_for_completion_timeout(&adsp->start_done,
+ msecs_to_jiffies(5000));
+ if (!ret) {
+ dev_err(adsp->dev, "start timed out\n");
+ qcom_scm_pas_shutdown(ADSP_PAS_ID);
+ ret = -ETIMEDOUT;
+ goto disable_regulators;
+ }
+
+ ret = 0;
+
+disable_regulators:
+ regulator_disable(adsp->cx_supply);
+
+ return ret;
+}
+
+static int adsp_stop(struct rproc *rproc)
+{
+ struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
+ int ret;
+
+ qcom_smem_state_update_bits(adsp->state,
+ BIT(adsp->stop_bit),
+ BIT(adsp->stop_bit));
+
+ ret = wait_for_completion_timeout(&adsp->stop_done,
+ msecs_to_jiffies(5000));
+ if (ret == 0)
+ dev_err(adsp->dev, "timed out on wait\n");
+
+ qcom_smem_state_update_bits(adsp->state,
+ BIT(adsp->stop_bit),
+ 0);
+
+ ret = qcom_scm_pas_shutdown(ADSP_PAS_ID);
+ if (ret)
+ dev_err(adsp->dev, "failed to shutdown: %d\n", ret);
+
+ return ret;
+}
+
+static void *adsp_da_to_va(struct rproc *rproc, u64 da, int len)
+{
+ struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
+ int offset;
+
+ offset = da - adsp->mem_reloc;
+ if (offset < 0 || offset + len > adsp->mem_size)
+ return NULL;
+
+ return adsp->mem_region + offset;
+}
+
+static const struct rproc_ops adsp_ops = {
+ .start = adsp_start,
+ .stop = adsp_stop,
+ .da_to_va = adsp_da_to_va,
+};
+
+static irqreturn_t adsp_wdog_interrupt(int irq, void *dev)
+{
+ struct qcom_adsp *adsp = dev;
+
+ rproc_report_crash(adsp->rproc, RPROC_WATCHDOG);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t adsp_fatal_interrupt(int irq, void *dev)
+{
+ struct qcom_adsp *adsp = dev;
+ size_t len;
+ char *msg;
+
+ msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, ADSP_CRASH_REASON_SMEM, &len);
+ if (!IS_ERR(msg) && len > 0 && msg[0])
+ dev_err(adsp->dev, "fatal error received: %s\n", msg);
+
+ rproc_report_crash(adsp->rproc, RPROC_FATAL_ERROR);
+
+ if (!IS_ERR(msg))
+ msg[0] = '\0';
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t adsp_ready_interrupt(int irq, void *dev)
+{
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t adsp_handover_interrupt(int irq, void *dev)
+{
+ struct qcom_adsp *adsp = dev;
+
+ complete(&adsp->start_done);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t adsp_stop_ack_interrupt(int irq, void *dev)
+{
+ struct qcom_adsp *adsp = dev;
+
+ complete(&adsp->stop_done);
+
+ return IRQ_HANDLED;
+}
+
+static int adsp_init_regulator(struct qcom_adsp *adsp)
+{
+ adsp->cx_supply = devm_regulator_get(adsp->dev, "cx");
+ if (IS_ERR(adsp->cx_supply))
+ return PTR_ERR(adsp->cx_supply);
+
+ regulator_set_load(adsp->cx_supply, 100000);
+
+ return 0;
+}
+
+static int adsp_request_irq(struct qcom_adsp *adsp,
+ struct platform_device *pdev,
+ const char *name,
+ irq_handler_t thread_fn)
+{
+ int ret;
+
+ ret = platform_get_irq_byname(pdev, name);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "no %s IRQ defined\n", name);
+ return ret;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, ret,
+ NULL, thread_fn,
+ IRQF_ONESHOT,
+ "adsp", adsp);
+ if (ret)
+ dev_err(&pdev->dev, "request %s IRQ failed\n", name);
+
+ return ret;
+}
+
+static int adsp_alloc_memory_region(struct qcom_adsp *adsp)
+{
+ struct device_node *node;
+ struct resource r;
+ int ret;
+
+ node = of_parse_phandle(adsp->dev->of_node, "memory-region", 0);
+ if (!node) {
+ dev_err(adsp->dev, "no memory-region specified\n");
+ return -EINVAL;
+ }
+
+ ret = of_address_to_resource(node, 0, &r);
+ if (ret)
+ return ret;
+
+ adsp->mem_phys = adsp->mem_reloc = r.start;
+ adsp->mem_size = resource_size(&r);
+ adsp->mem_region = devm_ioremap_wc(adsp->dev, adsp->mem_phys, adsp->mem_size);
+ if (!adsp->mem_region) {
+ dev_err(adsp->dev, "unable to map memory region: %pa+%zx\n",
+ &r.start, adsp->mem_size);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int adsp_probe(struct platform_device *pdev)
+{
+ struct qcom_adsp *adsp;
+ struct rproc *rproc;
+ int ret;
+
+ if (!qcom_scm_is_available())
+ return -EPROBE_DEFER;
+
+ if (!qcom_scm_pas_supported(ADSP_PAS_ID)) {
+ dev_err(&pdev->dev, "PAS is not available for ADSP\n");
+ return -ENXIO;
+ }
+
+ rproc = rproc_alloc(&pdev->dev, pdev->name, &adsp_ops,
+ ADSP_FIRMWARE_NAME, sizeof(*adsp));
+ if (!rproc) {
+ dev_err(&pdev->dev, "unable to allocate remoteproc\n");
+ return -ENOMEM;
+ }
+
+ rproc->fw_ops = &adsp_fw_ops;
+
+ adsp = (struct qcom_adsp *)rproc->priv;
+ adsp->dev = &pdev->dev;
+ adsp->rproc = rproc;
+ platform_set_drvdata(pdev, adsp);
+
+ init_completion(&adsp->start_done);
+ init_completion(&adsp->stop_done);
+
+ ret = adsp_alloc_memory_region(adsp);
+ if (ret)
+ goto free_rproc;
+
+ ret = adsp_init_regulator(adsp);
+ if (ret)
+ goto free_rproc;
+
+ ret = adsp_request_irq(adsp, pdev, "wdog", adsp_wdog_interrupt);
+ if (ret < 0)
+ goto free_rproc;
+ adsp->wdog_irq = ret;
+
+ ret = adsp_request_irq(adsp, pdev, "fatal", adsp_fatal_interrupt);
+ if (ret < 0)
+ goto free_rproc;
+ adsp->fatal_irq = ret;
+
+ ret = adsp_request_irq(adsp, pdev, "ready", adsp_ready_interrupt);
+ if (ret < 0)
+ goto free_rproc;
+ adsp->ready_irq = ret;
+
+ ret = adsp_request_irq(adsp, pdev, "handover", adsp_handover_interrupt);
+ if (ret < 0)
+ goto free_rproc;
+ adsp->handover_irq = ret;
+
+ ret = adsp_request_irq(adsp, pdev, "stop-ack", adsp_stop_ack_interrupt);
+ if (ret < 0)
+ goto free_rproc;
+ adsp->stop_ack_irq = ret;
+
+ adsp->state = qcom_smem_state_get(&pdev->dev, "stop",
+ &adsp->stop_bit);
+ if (IS_ERR(adsp->state)) {
+ ret = PTR_ERR(adsp->state);
+ goto free_rproc;
+ }
+
+ ret = rproc_add(rproc);
+ if (ret)
+ goto free_rproc;
+
+ return 0;
+
+free_rproc:
+ rproc_put(rproc);
+
+ return ret;
+}
+
+static int adsp_remove(struct platform_device *pdev)
+{
+ struct qcom_adsp *adsp = platform_get_drvdata(pdev);
+
+ qcom_smem_state_put(adsp->state);
+ rproc_del(adsp->rproc);
+ rproc_put(adsp->rproc);
+
+ return 0;
+}
+
+static const struct of_device_id adsp_of_match[] = {
+ { .compatible = "qcom,msm8974-adsp-pil" },
+ { .compatible = "qcom,msm8996-adsp-pil" },
+ { },
+};
+
+static struct platform_driver adsp_driver = {
+ .probe = adsp_probe,
+ .remove = adsp_remove,
+ .driver = {
+ .name = "qcom_adsp_pil",
+ .of_match_table = adsp_of_match,
+ },
+};
+
+module_platform_driver(adsp_driver);
+MODULE_DESCRIPTION("Qualcomm MSM8974/MSM8996 ADSP Peripherial Image Loader");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c
index 2e0caaaa766a..b08989b48df7 100644
--- a/drivers/remoteproc/qcom_q6v5_pil.c
+++ b/drivers/remoteproc/qcom_q6v5_pil.c
@@ -894,6 +894,7 @@ static const struct of_device_id q6v5_of_match[] = {
{ .compatible = "qcom,q6v5-pil", },
{ },
};
+MODULE_DEVICE_TABLE(of, q6v5_of_match);
static struct platform_driver q6v5_driver = {
.probe = q6v5_probe,
diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c
index f5cedeaafba1..ebd61f5d18bb 100644
--- a/drivers/remoteproc/qcom_wcnss.c
+++ b/drivers/remoteproc/qcom_wcnss.c
@@ -30,6 +30,7 @@
#include <linux/remoteproc.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 "remoteproc_internal.h"
@@ -94,6 +95,10 @@ struct qcom_wcnss {
phys_addr_t mem_reloc;
void *mem_region;
size_t mem_size;
+
+ struct device_node *smd_node;
+ struct qcom_smd_edge *smd_edge;
+ struct rproc_subdev smd_subdev;
};
static const struct wcnss_data riva_data = {
@@ -143,7 +148,6 @@ void qcom_wcnss_assign_iris(struct qcom_wcnss *wcnss,
mutex_unlock(&wcnss->iris_lock);
}
-EXPORT_SYMBOL_GPL(qcom_wcnss_assign_iris);
static int wcnss_load(struct rproc *rproc, const struct firmware *fw)
{
@@ -396,6 +400,23 @@ 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)
@@ -578,6 +599,10 @@ 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);
+
ret = rproc_add(rproc);
if (ret)
goto free_rproc;
@@ -596,6 +621,7 @@ 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);
rproc_free(wcnss->rproc);
@@ -609,6 +635,7 @@ static const struct of_device_id wcnss_of_match[] = {
{ .compatible = "qcom,pronto-v2-pil", &pronto_v2_data },
{ },
};
+MODULE_DEVICE_TABLE(of, wcnss_of_match);
static struct platform_driver wcnss_driver = {
.probe = wcnss_probe,
@@ -619,6 +646,28 @@ static struct platform_driver wcnss_driver = {
},
};
-module_platform_driver(wcnss_driver);
+static int __init wcnss_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&wcnss_driver);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&qcom_iris_driver);
+ if (ret)
+ platform_driver_unregister(&wcnss_driver);
+
+ return ret;
+}
+module_init(wcnss_init);
+
+static void __exit wcnss_exit(void)
+{
+ platform_driver_unregister(&qcom_iris_driver);
+ platform_driver_unregister(&wcnss_driver);
+}
+module_exit(wcnss_exit);
+
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
index 9dc4a9fe41e1..25fb7f62a457 100644
--- a/drivers/remoteproc/qcom_wcnss.h
+++ b/drivers/remoteproc/qcom_wcnss.h
@@ -4,6 +4,8 @@
struct qcom_iris;
struct qcom_wcnss;
+extern struct platform_driver qcom_iris_driver;
+
struct wcnss_vreg_info {
const char * const name;
int min_voltage;
diff --git a/drivers/remoteproc/qcom_wcnss_iris.c b/drivers/remoteproc/qcom_wcnss_iris.c
index f0ca24a8dd0b..e842be58e8c7 100644
--- a/drivers/remoteproc/qcom_wcnss_iris.c
+++ b/drivers/remoteproc/qcom_wcnss_iris.c
@@ -94,14 +94,12 @@ disable_regulators:
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)
{
@@ -173,8 +171,9 @@ static const struct of_device_id iris_of_match[] = {
{ .compatible = "qcom,wcn3680", .data = &wcn3680_data },
{}
};
+MODULE_DEVICE_TABLE(of, iris_of_match);
-static struct platform_driver wcnss_driver = {
+struct platform_driver qcom_iris_driver = {
.probe = qcom_iris_probe,
.remove = qcom_iris_remove,
.driver = {
@@ -182,7 +181,3 @@ static struct platform_driver wcnss_driver = {
.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 c6bfb3496684..f0f6ec1ab12b 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -236,6 +236,10 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
}
notifyid = ret;
+ /* Potentially bump max_notifyid */
+ if (notifyid > rproc->max_notifyid)
+ rproc->max_notifyid = notifyid;
+
dev_dbg(dev, "vring%d: va %p dma %pad size 0x%x idr %d\n",
i, va, &dma, size, notifyid);
@@ -296,6 +300,20 @@ void rproc_free_vring(struct rproc_vring *rvring)
rsc->vring[idx].notifyid = -1;
}
+static int rproc_vdev_do_probe(struct rproc_subdev *subdev)
+{
+ struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev);
+
+ return rproc_add_virtio_dev(rvdev, rvdev->id);
+}
+
+static void rproc_vdev_do_remove(struct rproc_subdev *subdev)
+{
+ struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev);
+
+ rproc_remove_virtio_dev(rvdev);
+}
+
/**
* rproc_handle_vdev() - handle a vdev fw resource
* @rproc: the remote processor
@@ -356,6 +374,9 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
if (!rvdev)
return -ENOMEM;
+ kref_init(&rvdev->refcount);
+
+ rvdev->id = rsc->id;
rvdev->rproc = rproc;
/* parse the vrings */
@@ -368,22 +389,51 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
/* remember the resource offset*/
rvdev->rsc_offset = offset;
+ /* allocate the vring resources */
+ for (i = 0; i < rsc->num_of_vrings; i++) {
+ ret = rproc_alloc_vring(rvdev, i);
+ if (ret)
+ goto unwind_vring_allocations;
+ }
+
+ /* track the rvdevs list reference */
+ kref_get(&rvdev->refcount);
+
list_add_tail(&rvdev->node, &rproc->rvdevs);
- /* it is now safe to add the virtio device */
- ret = rproc_add_virtio_dev(rvdev, rsc->id);
- if (ret)
- goto remove_rvdev;
+ rproc_add_subdev(rproc, &rvdev->subdev,
+ rproc_vdev_do_probe, rproc_vdev_do_remove);
return 0;
-remove_rvdev:
- list_del(&rvdev->node);
+unwind_vring_allocations:
+ for (i--; i >= 0; i--)
+ rproc_free_vring(&rvdev->vring[i]);
free_rvdev:
kfree(rvdev);
return ret;
}
+void rproc_vdev_release(struct kref *ref)
+{
+ struct rproc_vdev *rvdev = container_of(ref, struct rproc_vdev, refcount);
+ struct rproc_vring *rvring;
+ struct rproc *rproc = rvdev->rproc;
+ int id;
+
+ for (id = 0; id < ARRAY_SIZE(rvdev->vring); id++) {
+ rvring = &rvdev->vring[id];
+ if (!rvring->va)
+ continue;
+
+ rproc_free_vring(rvring);
+ }
+
+ rproc_remove_subdev(rproc, &rvdev->subdev);
+ list_del(&rvdev->node);
+ kfree(rvdev);
+}
+
/**
* rproc_handle_trace() - handle a shared trace buffer resource
* @rproc: the remote processor
@@ -673,15 +723,6 @@ free_carv:
return ret;
}
-static int rproc_count_vrings(struct rproc *rproc, struct fw_rsc_vdev *rsc,
- int offset, int avail)
-{
- /* Summarize the number of notification IDs */
- rproc->max_notifyid += rsc->num_of_vrings;
-
- return 0;
-}
-
/*
* A lookup table for resource handlers. The indices are defined in
* enum fw_resource_type.
@@ -690,10 +731,6 @@ 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] = (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,
};
@@ -736,6 +773,34 @@ static int rproc_handle_resources(struct rproc *rproc, int len,
return ret;
}
+static int rproc_probe_subdevices(struct rproc *rproc)
+{
+ struct rproc_subdev *subdev;
+ int ret;
+
+ list_for_each_entry(subdev, &rproc->subdevs, node) {
+ ret = subdev->probe(subdev);
+ if (ret)
+ goto unroll_registration;
+ }
+
+ return 0;
+
+unroll_registration:
+ list_for_each_entry_continue_reverse(subdev, &rproc->subdevs, node)
+ subdev->remove(subdev);
+
+ return ret;
+}
+
+static void rproc_remove_subdevices(struct rproc *rproc)
+{
+ struct rproc_subdev *subdev;
+
+ list_for_each_entry(subdev, &rproc->subdevs, node)
+ subdev->remove(subdev);
+}
+
/**
* rproc_resource_cleanup() - clean up and free all acquired resources
* @rproc: rproc handle
@@ -782,7 +847,7 @@ static void rproc_resource_cleanup(struct rproc *rproc)
/* clean up remote vdev entries */
list_for_each_entry_safe(rvdev, rvtmp, &rproc->rvdevs, node)
- rproc_remove_virtio_dev(rvdev);
+ kref_put(&rvdev->refcount, rproc_vdev_release);
}
/*
@@ -824,25 +889,16 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
/*
* 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.
+ * will be stored in the table_ptr. Before the device is started,
+ * table_ptr will be copied into device memory.
*/
- rproc->cached_table = kmemdup(table, tablesz, GFP_KERNEL);
- if (!rproc->cached_table)
+ rproc->table_ptr = kmemdup(table, tablesz, GFP_KERNEL);
+ if (!rproc->table_ptr)
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;
- }
-
/* handle fw resources which are required to boot rproc */
ret = rproc_handle_resources(rproc, tablesz, rproc_loading_handlers);
if (ret) {
@@ -858,18 +914,16 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
}
/*
- * The starting device has been given the rproc->cached_table as the
+ * The starting device has been given the rproc->table_ptr as the
* resource table. The address of the vring along with the other
- * allocated resources (carveouts etc) is stored in cached_table.
+ * allocated resources (carveouts etc) is stored in table_ptr.
* 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) {
- memcpy(loaded_table, rproc->cached_table, tablesz);
- rproc->table_ptr = loaded_table;
- }
+ if (loaded_table)
+ memcpy(loaded_table, rproc->table_ptr, tablesz);
/* power up the remote processor */
ret = rproc->ops->start(rproc);
@@ -878,17 +932,26 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
goto clean_up_resources;
}
+ /* probe any subdevices for the remote processor */
+ ret = rproc_probe_subdevices(rproc);
+ if (ret) {
+ dev_err(dev, "failed to probe subdevices for %s: %d\n",
+ rproc->name, ret);
+ goto stop_rproc;
+ }
+
rproc->state = RPROC_RUNNING;
dev_info(dev, "remote processor %s is now up\n", rproc->name);
return 0;
+stop_rproc:
+ rproc->ops->stop(rproc);
clean_up_resources:
rproc_resource_cleanup(rproc);
clean_up:
- kfree(rproc->cached_table);
- rproc->cached_table = NULL;
+ kfree(rproc->table_ptr);
rproc->table_ptr = NULL;
rproc_disable_iommu(rproc);
@@ -1121,6 +1184,9 @@ void rproc_shutdown(struct rproc *rproc)
if (!atomic_dec_and_test(&rproc->power))
goto out;
+ /* remove any subdevices for the remote processor */
+ rproc_remove_subdevices(rproc);
+
/* power off the remote processor */
ret = rproc->ops->stop(rproc);
if (ret) {
@@ -1135,8 +1201,7 @@ void rproc_shutdown(struct rproc *rproc)
rproc_disable_iommu(rproc);
/* Free the copy of the resource table */
- kfree(rproc->cached_table);
- rproc->cached_table = NULL;
+ kfree(rproc->table_ptr);
rproc->table_ptr = NULL;
/* if in crash state, unlock crash handler */
@@ -1273,6 +1338,7 @@ static void rproc_type_release(struct device *dev)
if (rproc->index >= 0)
ida_simple_remove(&rproc_dev_index, rproc->index);
+ kfree(rproc->firmware);
kfree(rproc);
}
@@ -1310,31 +1376,31 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
{
struct rproc *rproc;
char *p, *template = "rproc-%s-fw";
- int name_len = 0;
+ int name_len;
if (!dev || !name || !ops)
return NULL;
- if (!firmware)
+ if (!firmware) {
/*
- * Make room for default firmware name (minus %s plus '\0').
* If the caller didn't pass in a firmware name then
- * construct a default name. We're already glomming 'len'
- * bytes onto the end of the struct rproc allocation, so do
- * a few more for the default firmware name (but only if
- * the caller doesn't pass one).
+ * construct a default name.
*/
name_len = strlen(name) + strlen(template) - 2 + 1;
-
- rproc = kzalloc(sizeof(*rproc) + len + name_len, GFP_KERNEL);
- if (!rproc)
- return NULL;
-
- if (!firmware) {
- p = (char *)rproc + sizeof(struct rproc) + len;
+ p = kmalloc(name_len, GFP_KERNEL);
+ if (!p)
+ return NULL;
snprintf(p, name_len, template, name);
} else {
- p = (char *)firmware;
+ p = kstrdup(firmware, GFP_KERNEL);
+ if (!p)
+ return NULL;
+ }
+
+ rproc = kzalloc(sizeof(struct rproc) + len, GFP_KERNEL);
+ if (!rproc) {
+ kfree(p);
+ return NULL;
}
rproc->firmware = p;
@@ -1346,6 +1412,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
device_initialize(&rproc->dev);
rproc->dev.parent = dev;
rproc->dev.type = &rproc_type;
+ rproc->dev.class = &rproc_class;
/* Assign a unique device index and name */
rproc->index = ida_simple_get(&rproc_dev_index, 0, 0, GFP_KERNEL);
@@ -1370,6 +1437,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
INIT_LIST_HEAD(&rproc->mappings);
INIT_LIST_HEAD(&rproc->traces);
INIT_LIST_HEAD(&rproc->rvdevs);
+ INIT_LIST_HEAD(&rproc->subdevs);
INIT_WORK(&rproc->crash_handler, rproc_crash_handler_work);
init_completion(&rproc->crash_comp);
@@ -1428,8 +1496,6 @@ EXPORT_SYMBOL(rproc_put);
*/
int rproc_del(struct rproc *rproc)
{
- struct rproc_vdev *rvdev, *tmp;
-
if (!rproc)
return -EINVAL;
@@ -1441,10 +1507,6 @@ int rproc_del(struct rproc *rproc)
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);
-
/* the rproc is downref'ed as soon as it's removed from the klist */
mutex_lock(&rproc_list_mutex);
list_del(&rproc->node);
@@ -1457,6 +1519,36 @@ int rproc_del(struct rproc *rproc)
EXPORT_SYMBOL(rproc_del);
/**
+ * rproc_add_subdev() - add a subdevice to a remoteproc
+ * @rproc: rproc handle to add the subdevice to
+ * @subdev: subdev handle to register
+ * @probe: function to call when the rproc boots
+ * @remove: function to call when the rproc shuts down
+ */
+void rproc_add_subdev(struct rproc *rproc,
+ struct rproc_subdev *subdev,
+ int (*probe)(struct rproc_subdev *subdev),
+ void (*remove)(struct rproc_subdev *subdev))
+{
+ subdev->probe = probe;
+ subdev->remove = remove;
+
+ list_add_tail(&subdev->node, &rproc->subdevs);
+}
+EXPORT_SYMBOL(rproc_add_subdev);
+
+/**
+ * rproc_remove_subdev() - remove a subdevice from a remoteproc
+ * @rproc: rproc handle to remove the subdevice from
+ * @subdev: subdev handle, previously registered with rproc_add_subdev()
+ */
+void rproc_remove_subdev(struct rproc *rproc, struct rproc_subdev *subdev)
+{
+ list_del(&subdev->node);
+}
+EXPORT_SYMBOL(rproc_remove_subdev);
+
+/**
* rproc_report_crash() - rproc crash reporter function
* @rproc: remote processor
* @type: crash type
@@ -1484,6 +1576,7 @@ EXPORT_SYMBOL(rproc_report_crash);
static int __init remoteproc_init(void)
{
+ rproc_init_sysfs();
rproc_init_debugfs();
return 0;
@@ -1495,6 +1588,7 @@ static void __exit remoteproc_exit(void)
ida_destroy(&rproc_dev_index);
rproc_exit_debugfs();
+ rproc_exit_sysfs();
}
module_exit(remoteproc_exit);
diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c
index 374797206c79..1c122e230cec 100644
--- a/drivers/remoteproc/remoteproc_debugfs.c
+++ b/drivers/remoteproc/remoteproc_debugfs.c
@@ -59,75 +59,6 @@ static const struct file_operations trace_rproc_ops = {
.llseek = generic_file_llseek,
};
-/*
- * A state-to-string lookup table, for exposing a human readable state
- * via debugfs. Always keep in sync with enum rproc_state
- */
-static const char * const rproc_state_string[] = {
- "offline",
- "suspended",
- "running",
- "crashed",
- "invalid",
-};
-
-/* 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)
-{
- struct rproc *rproc = filp->private_data;
- unsigned int state;
- char buf[30];
- int i;
-
- state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state;
-
- i = scnprintf(buf, 30, "%.28s (%d)\n", rproc_state_string[state],
- rproc->state);
-
- return simple_read_from_buffer(userbuf, count, ppos, buf, i);
-}
-
-static ssize_t rproc_state_write(struct file *filp, const char __user *userbuf,
- size_t count, loff_t *ppos)
-{
- struct rproc *rproc = filp->private_data;
- char buf[10];
- int ret;
-
- if (count > sizeof(buf) || count <= 0)
- return -EINVAL;
-
- ret = copy_from_user(buf, userbuf, count);
- if (ret)
- return -EFAULT;
-
- if (buf[count - 1] == '\n')
- buf[count - 1] = '\0';
-
- if (!strncmp(buf, "start", count)) {
- ret = rproc_boot(rproc);
- if (ret) {
- dev_err(&rproc->dev, "Boot failed: %d\n", ret);
- return ret;
- }
- } else if (!strncmp(buf, "stop", count)) {
- rproc_shutdown(rproc);
- } else {
- dev_err(&rproc->dev, "Unrecognised option: %s\n", buf);
- return -EINVAL;
- }
-
- return count;
-}
-
-static const struct file_operations rproc_state_ops = {
- .read = rproc_state_read,
- .write = rproc_state_write,
- .open = simple_open,
- .llseek = generic_file_llseek,
-};
-
/* 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)
@@ -265,8 +196,6 @@ void rproc_create_debug_dir(struct rproc *rproc)
debugfs_create_file("name", 0400, rproc->dbg_dir,
rproc, &rproc_name_ops);
- debugfs_create_file("state", 0400, rproc->dbg_dir,
- rproc, &rproc_state_ops);
debugfs_create_file("recovery", 0400, rproc->dbg_dir,
rproc, &rproc_recovery_ops);
}
diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
index 4cf93ca2816e..1e9e5b3f021c 100644
--- a/drivers/remoteproc/remoteproc_internal.h
+++ b/drivers/remoteproc/remoteproc_internal.h
@@ -49,6 +49,7 @@ struct rproc_fw_ops {
void rproc_release(struct kref *kref);
irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id);
int rproc_boot_nowait(struct rproc *rproc);
+void rproc_vdev_release(struct kref *ref);
/* from remoteproc_virtio.c */
int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id);
@@ -63,6 +64,11 @@ void rproc_create_debug_dir(struct rproc *rproc);
void rproc_init_debugfs(void);
void rproc_exit_debugfs(void);
+/* from remoteproc_sysfs.c */
+extern struct class rproc_class;
+int rproc_init_sysfs(void);
+void rproc_exit_sysfs(void);
+
void rproc_free_vring(struct rproc_vring *rvring);
int rproc_alloc_vring(struct rproc_vdev *rvdev, int i);
diff --git a/drivers/remoteproc/remoteproc_sysfs.c b/drivers/remoteproc/remoteproc_sysfs.c
new file mode 100644
index 000000000000..bc5b0e00efb1
--- /dev/null
+++ b/drivers/remoteproc/remoteproc_sysfs.c
@@ -0,0 +1,151 @@
+/*
+ * Remote Processor Framework
+ *
+ * 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/remoteproc.h>
+
+#include "remoteproc_internal.h"
+
+#define to_rproc(d) container_of(d, struct rproc, dev)
+
+/* Expose the loaded / running firmware name via sysfs */
+static ssize_t firmware_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct rproc *rproc = to_rproc(dev);
+
+ return sprintf(buf, "%s\n", rproc->firmware);
+}
+
+/* Change firmware name via sysfs */
+static ssize_t firmware_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rproc *rproc = to_rproc(dev);
+ char *p;
+ int err, len = count;
+
+ err = mutex_lock_interruptible(&rproc->lock);
+ if (err) {
+ dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, err);
+ return -EINVAL;
+ }
+
+ if (rproc->state != RPROC_OFFLINE) {
+ dev_err(dev, "can't change firmware while running\n");
+ err = -EBUSY;
+ goto out;
+ }
+
+ len = strcspn(buf, "\n");
+
+ p = kstrndup(buf, len, GFP_KERNEL);
+ if (!p) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ kfree(rproc->firmware);
+ rproc->firmware = p;
+out:
+ mutex_unlock(&rproc->lock);
+
+ return err ? err : count;
+}
+static DEVICE_ATTR_RW(firmware);
+
+/*
+ * A state-to-string lookup table, for exposing a human readable state
+ * via sysfs. Always keep in sync with enum rproc_state
+ */
+static const char * const rproc_state_string[] = {
+ [RPROC_OFFLINE] = "offline",
+ [RPROC_SUSPENDED] = "suspended",
+ [RPROC_RUNNING] = "running",
+ [RPROC_CRASHED] = "crashed",
+ [RPROC_LAST] = "invalid",
+};
+
+/* Expose the state of the remote processor via sysfs */
+static ssize_t state_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct rproc *rproc = to_rproc(dev);
+ unsigned int state;
+
+ state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state;
+ return sprintf(buf, "%s\n", rproc_state_string[state]);
+}
+
+/* Change remote processor state via sysfs */
+static ssize_t state_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rproc *rproc = to_rproc(dev);
+ int ret = 0;
+
+ if (sysfs_streq(buf, "start")) {
+ if (rproc->state == RPROC_RUNNING)
+ return -EBUSY;
+
+ ret = rproc_boot(rproc);
+ if (ret)
+ dev_err(&rproc->dev, "Boot failed: %d\n", ret);
+ } else if (sysfs_streq(buf, "stop")) {
+ if (rproc->state != RPROC_RUNNING)
+ return -EINVAL;
+
+ rproc_shutdown(rproc);
+ } else {
+ dev_err(&rproc->dev, "Unrecognised option: %s\n", buf);
+ ret = -EINVAL;
+ }
+ return ret ? ret : count;
+}
+static DEVICE_ATTR_RW(state);
+
+static struct attribute *rproc_attrs[] = {
+ &dev_attr_firmware.attr,
+ &dev_attr_state.attr,
+ NULL
+};
+
+static const struct attribute_group rproc_devgroup = {
+ .attrs = rproc_attrs
+};
+
+static const struct attribute_group *rproc_devgroups[] = {
+ &rproc_devgroup,
+ NULL
+};
+
+struct class rproc_class = {
+ .name = "remoteproc",
+ .dev_groups = rproc_devgroups,
+};
+
+int __init rproc_init_sysfs(void)
+{
+ /* create remoteproc device class for sysfs */
+ int err = class_register(&rproc_class);
+
+ if (err)
+ pr_err("remoteproc: unable to register class\n");
+ return err;
+}
+
+void __exit rproc_exit_sysfs(void)
+{
+ class_unregister(&rproc_class);
+}
diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c
index 01870a16d6d2..364411fb7734 100644
--- a/drivers/remoteproc/remoteproc_virtio.c
+++ b/drivers/remoteproc/remoteproc_virtio.c
@@ -79,7 +79,7 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
struct rproc_vring *rvring;
struct virtqueue *vq;
void *addr;
- int len, size, ret;
+ int len, size;
/* we're temporarily limited to two virtqueues per rvdev */
if (id >= ARRAY_SIZE(rvdev->vring))
@@ -88,10 +88,6 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
if (!name)
return NULL;
- ret = rproc_alloc_vring(rvdev, id);
- if (ret)
- return ERR_PTR(ret);
-
rvring = &rvdev->vring[id];
addr = rvring->va;
len = rvring->len;
@@ -130,7 +126,6 @@ static void __rproc_virtio_del_vqs(struct virtio_device *vdev)
rvring = vq->priv;
rvring->vq = NULL;
vring_del_virtqueue(vq);
- rproc_free_vring(rvring);
}
}
@@ -282,14 +277,13 @@ static const struct virtio_config_ops rproc_virtio_config_ops = {
* Never call this function directly; it will be called by the driver
* core when needed.
*/
-static void rproc_vdev_release(struct device *dev)
+static void rproc_virtio_dev_release(struct device *dev)
{
struct virtio_device *vdev = dev_to_virtio(dev);
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
struct rproc *rproc = vdev_to_rproc(vdev);
- list_del(&rvdev->node);
- kfree(rvdev);
+ kref_put(&rvdev->refcount, rproc_vdev_release);
put_device(&rproc->dev);
}
@@ -313,7 +307,7 @@ int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
vdev->id.device = id,
vdev->config = &rproc_virtio_config_ops,
vdev->dev.parent = dev;
- vdev->dev.release = rproc_vdev_release;
+ vdev->dev.release = rproc_virtio_dev_release;
/*
* We're indirectly making a non-temporary copy of the rproc pointer
@@ -325,6 +319,9 @@ int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
*/
get_device(&rproc->dev);
+ /* Reference the vdev and vring allocations */
+ kref_get(&rvdev->refcount);
+
ret = register_virtio_device(vdev);
if (ret) {
put_device(&rproc->dev);
diff --git a/drivers/remoteproc/st_remoteproc.c b/drivers/remoteproc/st_remoteproc.c
index ae8963fcc8c8..da4e152e9733 100644
--- a/drivers/remoteproc/st_remoteproc.c
+++ b/drivers/remoteproc/st_remoteproc.c
@@ -245,8 +245,10 @@ static int st_rproc_probe(struct platform_device *pdev)
goto free_rproc;
enabled = st_rproc_state(pdev);
- if (enabled < 0)
+ if (enabled < 0) {
+ ret = enabled;
goto free_rproc;
+ }
if (enabled) {
atomic_inc(&rproc->power);