diff options
-rw-r--r-- | Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt | 7 | ||||
-rw-r--r-- | drivers/remoteproc/Kconfig | 23 | ||||
-rw-r--r-- | drivers/remoteproc/Makefile | 2 | ||||
-rw-r--r-- | drivers/remoteproc/da8xx_remoteproc.c | 37 | ||||
-rw-r--r-- | drivers/remoteproc/qcom_adsp_pil.c | 156 | ||||
-rw-r--r-- | drivers/remoteproc/qcom_common.c | 26 | ||||
-rw-r--r-- | drivers/remoteproc/qcom_q6v5.c | 252 | ||||
-rw-r--r-- | drivers/remoteproc/qcom_q6v5.h | 46 | ||||
-rw-r--r-- | drivers/remoteproc/qcom_q6v5_pil.c | 158 | ||||
-rw-r--r-- | drivers/remoteproc/qcom_q6v5_wcss.c | 601 | ||||
-rw-r--r-- | drivers/remoteproc/qcom_sysmon.c | 5 | ||||
-rw-r--r-- | drivers/remoteproc/remoteproc_core.c | 117 | ||||
-rw-r--r-- | drivers/remoteproc/remoteproc_debugfs.c | 4 | ||||
-rw-r--r-- | drivers/remoteproc/remoteproc_virtio.c | 2 | ||||
-rw-r--r-- | drivers/remoteproc/st_slim_rproc.c | 3 | ||||
-rw-r--r-- | drivers/soc/qcom/mdt_loader.c | 87 | ||||
-rw-r--r-- | include/linux/remoteproc.h | 19 | ||||
-rw-r--r-- | include/linux/soc/qcom/mdt_loader.h | 4 |
18 files changed, 1188 insertions, 361 deletions
diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt index d90182425450..601dd9f389aa 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt @@ -8,6 +8,7 @@ on the Qualcomm Hexagon core. Value type: <string> Definition: must be one of: "qcom,q6v5-pil", + "qcom,ipq8074-wcss-pil" "qcom,msm8916-mss-pil", "qcom,msm8974-mss-pil" "qcom,msm8996-mss-pil" @@ -50,11 +51,15 @@ on the Qualcomm Hexagon core. Usage: required Value type: <phandle> Definition: reference to the reset-controller for the modem sub-system + reference to the list of 3 reset-controllers for the + wcss sub-system - reset-names: Usage: required Value type: <stringlist> - Definition: must be "mss_restart" + Definition: must be "mss_restart" for the modem sub-system + Definition: must be "wcss_aon_reset", "wcss_reset", "wcss_q6_reset" + for the wcss syb-system - cx-supply: - mss-supply: diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index cd1c168fd188..052d4dd347f9 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -93,6 +93,7 @@ config QCOM_ADSP_PIL depends on QCOM_SYSMON || QCOM_SYSMON=n select MFD_SYSCON select QCOM_MDT_LOADER + select QCOM_Q6V5_COMMON select QCOM_RPROC_COMMON select QCOM_SCM help @@ -102,6 +103,11 @@ config QCOM_ADSP_PIL config QCOM_RPROC_COMMON tristate +config QCOM_Q6V5_COMMON + tristate + depends on ARCH_QCOM + depends on QCOM_SMEM + config QCOM_Q6V5_PIL tristate "Qualcomm Hexagon V5 Peripherial Image Loader" depends on OF && ARCH_QCOM @@ -110,12 +116,29 @@ config QCOM_Q6V5_PIL depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n depends on QCOM_SYSMON || QCOM_SYSMON=n select MFD_SYSCON + select QCOM_Q6V5_COMMON select QCOM_RPROC_COMMON select QCOM_SCM help Say y here to support the Qualcomm Peripherial Image Loader for the Hexagon V5 based remote processors. +config QCOM_Q6V5_WCSS + tristate "Qualcomm Hexagon based WCSS Peripheral Image Loader" + depends on OF && ARCH_QCOM + depends on QCOM_SMEM + depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n) + depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n + depends on QCOM_SYSMON || QCOM_SYSMON=n + select MFD_SYSCON + select QCOM_MDT_LOADER + select QCOM_Q6V5_COMMON + select QCOM_RPROC_COMMON + select QCOM_SCM + help + Say y here to support the Qualcomm Peripheral Image Loader for the + Hexagon V5 based WCSS remote processors. + config QCOM_SYSMON tristate "Qualcomm sysmon driver" depends on RPMSG diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 02627ede8d4a..03332fa7e2ee 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -16,7 +16,9 @@ obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o obj-$(CONFIG_KEYSTONE_REMOTEPROC) += keystone_remoteproc.o obj-$(CONFIG_QCOM_ADSP_PIL) += qcom_adsp_pil.o obj-$(CONFIG_QCOM_RPROC_COMMON) += qcom_common.o +obj-$(CONFIG_QCOM_Q6V5_COMMON) += qcom_q6v5.o obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o +obj-$(CONFIG_QCOM_Q6V5_WCSS) += qcom_q6v5_wcss.o obj-$(CONFIG_QCOM_SYSMON) += qcom_sysmon.o obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss_pil.o qcom_wcnss_pil-y += qcom_wcnss.o diff --git a/drivers/remoteproc/da8xx_remoteproc.c b/drivers/remoteproc/da8xx_remoteproc.c index b668e32996e2..e230bef71be1 100644 --- a/drivers/remoteproc/da8xx_remoteproc.c +++ b/drivers/remoteproc/da8xx_remoteproc.c @@ -10,6 +10,7 @@ #include <linux/bitops.h> #include <linux/clk.h> +#include <linux/reset.h> #include <linux/err.h> #include <linux/interrupt.h> #include <linux/io.h> @@ -20,8 +21,6 @@ #include <linux/platform_device.h> #include <linux/remoteproc.h> -#include <mach/clock.h> /* for davinci_clk_reset_assert/deassert() */ - #include "remoteproc_internal.h" static char *da8xx_fw_name; @@ -72,6 +71,7 @@ struct da8xx_rproc { struct da8xx_rproc_mem *mem; int num_mems; struct clk *dsp_clk; + struct reset_control *dsp_reset; void (*ack_fxn)(struct irq_data *data); struct irq_data *irq_data; void __iomem *chipsig; @@ -138,6 +138,7 @@ static int da8xx_rproc_start(struct rproc *rproc) struct device *dev = rproc->dev.parent; struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv; struct clk *dsp_clk = drproc->dsp_clk; + struct reset_control *dsp_reset = drproc->dsp_reset; int ret; /* hw requires the start (boot) address be on 1KB boundary */ @@ -155,7 +156,12 @@ static int da8xx_rproc_start(struct rproc *rproc) return ret; } - davinci_clk_reset_deassert(dsp_clk); + ret = reset_control_deassert(dsp_reset); + if (ret) { + dev_err(dev, "reset_control_deassert() failed: %d\n", ret); + clk_disable_unprepare(dsp_clk); + return ret; + } return 0; } @@ -163,8 +169,15 @@ static int da8xx_rproc_start(struct rproc *rproc) static int da8xx_rproc_stop(struct rproc *rproc) { struct da8xx_rproc *drproc = rproc->priv; + struct device *dev = rproc->dev.parent; + int ret; + + ret = reset_control_assert(drproc->dsp_reset); + if (ret) { + dev_err(dev, "reset_control_assert() failed: %d\n", ret); + return ret; + } - davinci_clk_reset_assert(drproc->dsp_clk); clk_disable_unprepare(drproc->dsp_clk); return 0; @@ -232,6 +245,7 @@ static int da8xx_rproc_probe(struct platform_device *pdev) struct resource *bootreg_res; struct resource *chipsig_res; struct clk *dsp_clk; + struct reset_control *dsp_reset; void __iomem *chipsig; void __iomem *bootreg; int irq; @@ -268,6 +282,15 @@ static int da8xx_rproc_probe(struct platform_device *pdev) return PTR_ERR(dsp_clk); } + dsp_reset = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(dsp_reset)) { + if (PTR_ERR(dsp_reset) != -EPROBE_DEFER) + dev_err(dev, "unable to get reset control: %ld\n", + PTR_ERR(dsp_reset)); + + return PTR_ERR(dsp_reset); + } + if (dev->of_node) { ret = of_reserved_mem_device_init(dev); if (ret) { @@ -284,9 +307,13 @@ static int da8xx_rproc_probe(struct platform_device *pdev) goto free_mem; } + /* error recovery is not supported at present */ + rproc->recovery_disabled = true; + drproc = rproc->priv; drproc->rproc = rproc; drproc->dsp_clk = dsp_clk; + drproc->dsp_reset = dsp_reset; rproc->has_iommu = false; ret = da8xx_rproc_get_internal_memories(pdev, drproc); @@ -309,7 +336,7 @@ static int da8xx_rproc_probe(struct platform_device *pdev) * *not* in reset, but da8xx_rproc_start() needs the DSP to be * held in reset at the time it is called. */ - ret = davinci_clk_reset_assert(drproc->dsp_clk); + ret = reset_control_assert(dsp_reset); if (ret) goto free_rproc; diff --git a/drivers/remoteproc/qcom_adsp_pil.c b/drivers/remoteproc/qcom_adsp_pil.c index 89a86ce07f99..d4339a6da616 100644 --- a/drivers/remoteproc/qcom_adsp_pil.c +++ b/drivers/remoteproc/qcom_adsp_pil.c @@ -31,6 +31,7 @@ #include <linux/soc/qcom/smem_state.h> #include "qcom_common.h" +#include "qcom_q6v5.h" #include "remoteproc_internal.h" struct adsp_data { @@ -48,14 +49,7 @@ 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 qcom_q6v5 q6v5; struct clk *xo; struct clk *aggre2_clk; @@ -96,6 +90,8 @@ static int adsp_start(struct rproc *rproc) struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; int ret; + qcom_q6v5_prepare(&adsp->q6v5); + ret = clk_prepare_enable(adsp->xo); if (ret) return ret; @@ -119,16 +115,14 @@ static int adsp_start(struct rproc *rproc) goto disable_px_supply; } - ret = wait_for_completion_timeout(&adsp->start_done, - msecs_to_jiffies(5000)); - if (!ret) { + ret = qcom_q6v5_wait_for_start(&adsp->q6v5, msecs_to_jiffies(5000)); + if (ret == -ETIMEDOUT) { dev_err(adsp->dev, "start timed out\n"); qcom_scm_pas_shutdown(adsp->pas_id); - ret = -ETIMEDOUT; goto disable_px_supply; } - ret = 0; + return 0; disable_px_supply: regulator_disable(adsp->px_supply); @@ -142,28 +136,34 @@ disable_xo_clk: return ret; } +static void qcom_pas_handover(struct qcom_q6v5 *q6v5) +{ + struct qcom_adsp *adsp = container_of(q6v5, struct qcom_adsp, q6v5); + + regulator_disable(adsp->px_supply); + regulator_disable(adsp->cx_supply); + clk_disable_unprepare(adsp->aggre2_clk); + clk_disable_unprepare(adsp->xo); +} + static int adsp_stop(struct rproc *rproc) { struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; + int handover; 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) + ret = qcom_q6v5_request_stop(&adsp->q6v5); + if (ret == -ETIMEDOUT) 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); + handover = qcom_q6v5_unprepare(&adsp->q6v5); + if (handover) + qcom_pas_handover(&adsp->q6v5); + return ret; } @@ -187,53 +187,6 @@ static const struct rproc_ops adsp_ops = { .load = adsp_load, }; -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); - - 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_clock(struct qcom_adsp *adsp) { int ret; @@ -272,29 +225,6 @@ static int adsp_init_regulator(struct qcom_adsp *adsp) return PTR_ERR_OR_ZERO(adsp->px_supply); } -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; @@ -348,13 +278,9 @@ static int adsp_probe(struct platform_device *pdev) adsp->dev = &pdev->dev; adsp->rproc = rproc; adsp->pas_id = desc->pas_id; - adsp->crash_reason_smem = desc->crash_reason_smem; adsp->has_aggre2_clk = desc->has_aggre2_clk; platform_set_drvdata(pdev, adsp); - init_completion(&adsp->start_done); - init_completion(&adsp->stop_done); - ret = adsp_alloc_memory_region(adsp); if (ret) goto free_rproc; @@ -367,37 +293,10 @@ static int adsp_probe(struct platform_device *pdev) 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); + ret = qcom_q6v5_init(&adsp->q6v5, pdev, rproc, desc->crash_reason_smem, + qcom_pas_handover); + if (ret) goto free_rproc; - } qcom_add_glink_subdev(rproc, &adsp->glink_subdev); qcom_add_smd_subdev(rproc, &adsp->smd_subdev); @@ -422,7 +321,6 @@ 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); qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev); diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c index acfc99f82fb8..6f77840140bf 100644 --- a/drivers/remoteproc/qcom_common.c +++ b/drivers/remoteproc/qcom_common.c @@ -33,7 +33,7 @@ static BLOCKING_NOTIFIER_HEAD(ssr_notifiers); -static int glink_subdev_probe(struct rproc_subdev *subdev) +static int glink_subdev_start(struct rproc_subdev *subdev) { struct qcom_rproc_glink *glink = to_glink_subdev(subdev); @@ -42,7 +42,7 @@ static int glink_subdev_probe(struct rproc_subdev *subdev) return PTR_ERR_OR_ZERO(glink->edge); } -static void glink_subdev_remove(struct rproc_subdev *subdev, bool crashed) +static void glink_subdev_stop(struct rproc_subdev *subdev, bool crashed) { struct qcom_rproc_glink *glink = to_glink_subdev(subdev); @@ -64,7 +64,10 @@ void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink) return; glink->dev = dev; - rproc_add_subdev(rproc, &glink->subdev, glink_subdev_probe, glink_subdev_remove); + glink->subdev.start = glink_subdev_start; + glink->subdev.stop = glink_subdev_stop; + + rproc_add_subdev(rproc, &glink->subdev); } EXPORT_SYMBOL_GPL(qcom_add_glink_subdev); @@ -126,7 +129,7 @@ int qcom_register_dump_segments(struct rproc *rproc, } EXPORT_SYMBOL_GPL(qcom_register_dump_segments); -static int smd_subdev_probe(struct rproc_subdev *subdev) +static int smd_subdev_start(struct rproc_subdev *subdev) { struct qcom_rproc_subdev *smd = to_smd_subdev(subdev); @@ -135,7 +138,7 @@ static int smd_subdev_probe(struct rproc_subdev *subdev) return PTR_ERR_OR_ZERO(smd->edge); } -static void smd_subdev_remove(struct rproc_subdev *subdev, bool crashed) +static void smd_subdev_stop(struct rproc_subdev *subdev, bool crashed) { struct qcom_rproc_subdev *smd = to_smd_subdev(subdev); @@ -157,7 +160,10 @@ void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd) return; smd->dev = dev; - rproc_add_subdev(rproc, &smd->subdev, smd_subdev_probe, smd_subdev_remove); + smd->subdev.start = smd_subdev_start; + smd->subdev.stop = smd_subdev_stop; + + rproc_add_subdev(rproc, &smd->subdev); } EXPORT_SYMBOL_GPL(qcom_add_smd_subdev); @@ -202,11 +208,6 @@ void qcom_unregister_ssr_notifier(struct notifier_block *nb) } EXPORT_SYMBOL_GPL(qcom_unregister_ssr_notifier); -static int ssr_notify_start(struct rproc_subdev *subdev) -{ - return 0; -} - static void ssr_notify_stop(struct rproc_subdev *subdev, bool crashed) { struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev); @@ -227,8 +228,9 @@ void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr, const char *ssr_name) { ssr->name = ssr_name; + ssr->subdev.stop = ssr_notify_stop; - rproc_add_subdev(rproc, &ssr->subdev, ssr_notify_start, ssr_notify_stop); + rproc_add_subdev(rproc, &ssr->subdev); } EXPORT_SYMBOL_GPL(qcom_add_ssr_subdev); diff --git a/drivers/remoteproc/qcom_q6v5.c b/drivers/remoteproc/qcom_q6v5.c new file mode 100644 index 000000000000..61a760ee4aac --- /dev/null +++ b/drivers/remoteproc/qcom_q6v5.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Qualcomm Peripheral Image Loader for Q6V5 + * + * Copyright (C) 2016-2018 Linaro Ltd. + * Copyright (C) 2014 Sony Mobile Communications AB + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + */ +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/soc/qcom/smem.h> +#include <linux/soc/qcom/smem_state.h> +#include <linux/remoteproc.h> +#include "qcom_q6v5.h" + +/** + * qcom_q6v5_prepare() - reinitialize the qcom_q6v5 context before start + * @q6v5: reference to qcom_q6v5 context to be reinitialized + * + * Return: 0 on success, negative errno on failure + */ +int qcom_q6v5_prepare(struct qcom_q6v5 *q6v5) +{ + reinit_completion(&q6v5->start_done); + reinit_completion(&q6v5->stop_done); + + q6v5->running = true; + q6v5->handover_issued = false; + + enable_irq(q6v5->handover_irq); + + return 0; +} +EXPORT_SYMBOL_GPL(qcom_q6v5_prepare); + +/** + * qcom_q6v5_unprepare() - unprepare the qcom_q6v5 context after stop + * @q6v5: reference to qcom_q6v5 context to be unprepared + * + * Return: 0 on success, 1 if handover hasn't yet been called + */ +int qcom_q6v5_unprepare(struct qcom_q6v5 *q6v5) +{ + disable_irq(q6v5->handover_irq); + + return !q6v5->handover_issued; +} +EXPORT_SYMBOL_GPL(qcom_q6v5_unprepare); + +static irqreturn_t q6v5_wdog_interrupt(int irq, void *data) +{ + struct qcom_q6v5 *q6v5 = data; + size_t len; + char *msg; + + /* Sometimes the stop triggers a watchdog rather than a stop-ack */ + if (!q6v5->running) { + complete(&q6v5->stop_done); + return IRQ_HANDLED; + } + + msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, q6v5->crash_reason, &len); + if (!IS_ERR(msg) && len > 0 && msg[0]) + dev_err(q6v5->dev, "watchdog received: %s\n", msg); + else + dev_err(q6v5->dev, "watchdog without message\n"); + + rproc_report_crash(q6v5->rproc, RPROC_WATCHDOG); + + return IRQ_HANDLED; +} + +static irqreturn_t q6v5_fatal_interrupt(int irq, void *data) +{ + struct qcom_q6v5 *q6v5 = data; + size_t len; + char *msg; + + msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, q6v5->crash_reason, &len); + if (!IS_ERR(msg) && len > 0 && msg[0]) + dev_err(q6v5->dev, "fatal error received: %s\n", msg); + else + dev_err(q6v5->dev, "fatal error without message\n"); + + rproc_report_crash(q6v5->rproc, RPROC_FATAL_ERROR); + + return IRQ_HANDLED; +} + +static irqreturn_t q6v5_ready_interrupt(int irq, void *data) +{ + struct qcom_q6v5 *q6v5 = data; + + complete(&q6v5->start_done); + + return IRQ_HANDLED; +} + +/** + * qcom_q6v5_wait_for_start() - wait for remote processor start signal + * @q6v5: reference to qcom_q6v5 context + * @timeout: timeout to wait for the event, in jiffies + * + * qcom_q6v5_unprepare() should not be called when this function fails. + * + * Return: 0 on success, -ETIMEDOUT on timeout + */ +int qcom_q6v5_wait_for_start(struct qcom_q6v5 *q6v5, int timeout) +{ + int ret; + + ret = wait_for_completion_timeout(&q6v5->start_done, timeout); + if (!ret) + disable_irq(q6v5->handover_irq); + + return !ret ? -ETIMEDOUT : 0; +} +EXPORT_SYMBOL_GPL(qcom_q6v5_wait_for_start); + +static irqreturn_t q6v5_handover_interrupt(int irq, void *data) +{ + struct qcom_q6v5 *q6v5 = data; + + if (q6v5->handover) + q6v5->handover(q6v5); + + q6v5->handover_issued = true; + + return IRQ_HANDLED; +} + +static irqreturn_t q6v5_stop_interrupt(int irq, void *data) +{ + struct qcom_q6v5 *q6v5 = data; + + complete(&q6v5->stop_done); + + return IRQ_HANDLED; +} + +/** + * qcom_q6v5_request_stop() - request the remote processor to stop + * @q6v5: reference to qcom_q6v5 context + * + * Return: 0 on success, negative errno on failure + */ +int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5) +{ + int ret; + + q6v5->running = false; + + qcom_smem_state_update_bits(q6v5->state, + BIT(q6v5->stop_bit), BIT(q6v5->stop_bit)); + + ret = wait_for_completion_timeout(&q6v5->stop_done, 5 * HZ); + + qcom_smem_state_update_bits(q6v5->state, BIT(q6v5->stop_bit), 0); + + return ret == 0 ? -ETIMEDOUT : 0; +} +EXPORT_SYMBOL_GPL(qcom_q6v5_request_stop); + +/** + * qcom_q6v5_init() - initializer of the q6v5 common struct + * @q6v5: handle to be initialized + * @pdev: platform_device reference for acquiring resources + * @rproc: associated remoteproc instance + * @crash_reason: SMEM id for crash reason string, or 0 if none + * @handover: function to be called when proxy resources should be released + * + * Return: 0 on success, negative errno on failure + */ +int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev, + struct rproc *rproc, int crash_reason, + void (*handover)(struct qcom_q6v5 *q6v5)) +{ + int ret; + + q6v5->rproc = rproc; + q6v5->dev = &pdev->dev; + q6v5->crash_reason = crash_reason; + q6v5->handover = handover; + + init_completion(&q6v5->start_done); + init_completion(&q6v5->stop_done); + + q6v5->wdog_irq = platform_get_irq_byname(pdev, "wdog"); + ret = devm_request_threaded_irq(&pdev->dev, q6v5->wdog_irq, + NULL, q6v5_wdog_interrupt, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "q6v5 wdog", q6v5); + if (ret) { + dev_err(&pdev->dev, "failed to acquire wdog IRQ\n"); + return ret; + } + + q6v5->fatal_irq = platform_get_irq_byname(pdev, "fatal"); + ret = devm_request_threaded_irq(&pdev->dev, q6v5->fatal_irq, + NULL, q6v5_fatal_interrupt, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "q6v5 fatal", q6v5); + if (ret) { + dev_err(&pdev->dev, "failed to acquire fatal IRQ\n"); + return ret; + } + + q6v5->ready_irq = platform_get_irq_byname(pdev, "ready"); + ret = devm_request_threaded_irq(&pdev->dev, q6v5->ready_irq, + NULL, q6v5_ready_interrupt, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "q6v5 ready", q6v5); + if (ret) { + dev_err(&pdev->dev, "failed to acquire ready IRQ\n"); + return ret; + } + + q6v5->handover_irq = platform_get_irq_byname(pdev, "handover"); + ret = devm_request_threaded_irq(&pdev->dev, q6v5->handover_irq, + NULL, q6v5_handover_interrupt, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "q6v5 handover", q6v5); + if (ret) { + dev_err(&pdev->dev, "failed to acquire handover IRQ\n"); + return ret; + } + disable_irq(q6v5->handover_irq); + + q6v5->stop_irq = platform_get_irq_byname(pdev, "stop-ack"); + ret = devm_request_threaded_irq(&pdev->dev, q6v5->stop_irq, + NULL, q6v5_stop_interrupt, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "q6v5 stop", q6v5); + if (ret) { + dev_err(&pdev->dev, "failed to acquire stop-ack IRQ\n"); + return ret; + } + + q6v5->state = qcom_smem_state_get(&pdev->dev, "stop", &q6v5->stop_bit); + if (IS_ERR(q6v5->state)) { + dev_err(&pdev->dev, "failed to acquire stop state\n"); + return PTR_ERR(q6v5->state); + } + + return 0; +} +EXPORT_SYMBOL_GPL(qcom_q6v5_init); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Qualcomm Peripheral Image Loader for Q6V5"); diff --git a/drivers/remoteproc/qcom_q6v5.h b/drivers/remoteproc/qcom_q6v5.h new file mode 100644 index 000000000000..7ac92c1e0f49 --- /dev/null +++ b/drivers/remoteproc/qcom_q6v5.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __QCOM_Q6V5_H__ +#define __QCOM_Q6V5_H__ + +#include <linux/kernel.h> +#include <linux/completion.h> + +struct rproc; +struct qcom_smem_state; + +struct qcom_q6v5 { + struct device *dev; + struct rproc *rproc; + + struct qcom_smem_state *state; + unsigned stop_bit; + + int wdog_irq; + int fatal_irq; + int ready_irq; + int handover_irq; + int stop_irq; + + bool handover_issued; + + struct completion start_done; + struct completion stop_done; + + int crash_reason; + + bool running; + + void (*handover)(struct qcom_q6v5 *q6v5); +}; + +int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev, + struct rproc *rproc, int crash_reason, + void (*handover)(struct qcom_q6v5 *q6v5)); + +int qcom_q6v5_prepare(struct qcom_q6v5 *q6v5); +int qcom_q6v5_unprepare(struct qcom_q6v5 *q6v5); +int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5); +int qcom_q6v5_wait_for_start(struct qcom_q6v5 *q6v5, int timeout); + +#endif diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c index 2bf8e7c49f2a..d7a4b9eca5d2 100644 --- a/drivers/remoteproc/qcom_q6v5_pil.c +++ b/drivers/remoteproc/qcom_q6v5_pil.c @@ -30,12 +30,11 @@ #include <linux/remoteproc.h> #include <linux/reset.h> #include <linux/soc/qcom/mdt_loader.h> -#include <linux/soc/qcom/smem.h> -#include <linux/soc/qcom/smem_state.h> #include <linux/iopoll.h> #include "remoteproc_internal.h" #include "qcom_common.h" +#include "qcom_q6v5.h" #include <linux/qcom_scm.h> @@ -151,12 +150,7 @@ struct q6v5 { struct reset_control *mss_restart; - struct qcom_smem_state *state; - unsigned stop_bit; - - int handover_irq; - - bool proxy_unvoted; + struct qcom_q6v5 q6v5; struct clk *active_clks[8]; struct clk *reset_clks[4]; @@ -170,8 +164,6 @@ struct q6v5 { int active_reg_count; int proxy_reg_count; - struct completion start_done; - struct completion stop_done; bool running; phys_addr_t mba_phys; @@ -798,9 +790,7 @@ static int q6v5_start(struct rproc *rproc) int xfermemop_ret; int ret; - qproc->proxy_unvoted = false; - - enable_irq(qproc->handover_irq); + qcom_q6v5_prepare(&qproc->q6v5); ret = q6v5_regulator_enable(qproc, qproc->proxy_regs, qproc->proxy_reg_count); @@ -875,11 +865,9 @@ static int q6v5_start(struct rproc *rproc) if (ret) goto reclaim_mpss; - ret = wait_for_completion_timeout(&qproc->start_done, - msecs_to_jiffies(5000)); - if (ret == 0) { + ret = qcom_q6v5_wait_for_start(&qproc->q6v5, msecs_to_jiffies(5000)); + if (ret == -ETIMEDOUT) { dev_err(qproc->dev, "start timed out\n"); - ret = -ETIMEDOUT; goto reclaim_mpss; } @@ -933,7 +921,7 @@ disable_proxy_reg: qproc->proxy_reg_count); disable_irqs: - disable_irq(qproc->handover_irq); + qcom_q6v5_unprepare(&qproc->q6v5); return ret; } @@ -946,16 +934,10 @@ static int q6v5_stop(struct rproc *rproc) qproc->running = false; - qcom_smem_state_update_bits(qproc->state, - BIT(qproc->stop_bit), BIT(qproc->stop_bit)); - - ret = wait_for_completion_timeout(&qproc->stop_done, - msecs_to_jiffies(5000)); - if (ret == 0) + ret = qcom_q6v5_request_stop(&qproc->q6v5); + if (ret == -ETIMEDOUT) dev_err(qproc->dev, "timed out on wait\n"); - qcom_smem_state_update_bits(qproc->state, BIT(qproc->stop_bit), 0); - q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc); @@ -976,9 +958,8 @@ static int q6v5_stop(struct rproc *rproc) q6v5_reset_assert(qproc); - disable_irq(qproc->handover_irq); - - if (!qproc->proxy_unvoted) { + ret = qcom_q6v5_unprepare(&qproc->q6v5); + if (ret) { q6v5_clk_disable(qproc->dev, qproc->proxy_clks, qproc->proxy_clk_count); q6v5_regulator_disable(qproc, qproc->proxy_regs, @@ -1014,74 +995,14 @@ static const struct rproc_ops q6v5_ops = { .load = q6v5_load, }; -static irqreturn_t q6v5_wdog_interrupt(int irq, void *dev) -{ - struct q6v5 *qproc = dev; - size_t len; - char *msg; - - /* Sometimes the stop triggers a watchdog rather than a stop-ack */ - if (!qproc->running) { - complete(&qproc->stop_done); - return IRQ_HANDLED; - } - - msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, MPSS_CRASH_REASON_SMEM, &len); - if (!IS_ERR(msg) && len > 0 && msg[0]) - dev_err(qproc->dev, "watchdog received: %s\n", msg); - else - dev_err(qproc->dev, "watchdog without message\n"); - - rproc_report_crash(qproc->rproc, RPROC_WATCHDOG); - - return IRQ_HANDLED; -} - -static irqreturn_t q6v5_fatal_interrupt(int irq, void *dev) -{ - struct q6v5 *qproc = dev; - size_t len; - char *msg; - - msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, MPSS_CRASH_REASON_SMEM, &len); - if (!IS_ERR(msg) && len > 0 && msg[0]) - dev_err(qproc->dev, "fatal error received: %s\n", msg); - else - dev_err(qproc->dev, "fatal error without message\n"); - - rproc_report_crash(qproc->rproc, RPROC_FATAL_ERROR); - - return IRQ_HANDLED; -} - -static irqreturn_t q6v5_ready_interrupt(int irq, void *dev) -{ - struct q6v5 *qproc = dev; - - complete(&qproc->start_done); - return IRQ_HANDLED; -} - -static irqreturn_t q6v5_handover_interrupt(int irq, void *dev) +static void qcom_msa_handover(struct qcom_q6v5 *q6v5) { - struct q6v5 *qproc = dev; + struct q6v5 *qproc = container_of(q6v5, struct q6v5, q6v5); q6v5_clk_disable(qproc->dev, qproc->proxy_clks, qproc->proxy_clk_count); q6v5_regulator_disable(qproc, qproc->proxy_regs, qproc->proxy_reg_count); - - qproc->proxy_unvoted = true; - - return IRQ_HANDLED; -} - -static irqreturn_t q6v5_stop_ack_interrupt(int irq, void *dev) -{ - struct q6v5 *qproc = dev; - - complete(&qproc->stop_done); - return IRQ_HANDLED; } static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev) @@ -1154,30 +1075,6 @@ static int q6v5_init_reset(struct q6v5 *qproc) return 0; } -static int q6v5_request_irq(struct q6v5 *qproc, - struct platform_device *pdev, - const char *name, - irq_handler_t thread_fn) -{ - int irq; - int ret; - - irq = platform_get_irq_byname(pdev, name); - if (irq < 0) { - dev_err(&pdev->dev, "no %s IRQ defined\n", name); - return irq; - } - - ret = devm_request_threaded_irq(&pdev->dev, irq, - NULL, thread_fn, - IRQF_TRIGGER_RISING | IRQF_ONESHOT, - "q6v5", qproc); - if (ret) - dev_err(&pdev->dev, "request %s IRQ failed\n", name); - - return ret ? : irq; -} - static int q6v5_alloc_memory_region(struct q6v5 *qproc) { struct device_node *child; @@ -1247,9 +1144,6 @@ static int q6v5_probe(struct platform_device *pdev) qproc->rproc = rproc; platform_set_drvdata(pdev, qproc); - init_completion(&qproc->start_done); - init_completion(&qproc->stop_done); - ret = q6v5_init_mem(qproc, pdev); if (ret) goto free_rproc; @@ -1305,33 +1199,12 @@ static int q6v5_probe(struct platform_device *pdev) qproc->version = desc->version; qproc->has_alt_reset = desc->has_alt_reset; qproc->need_mem_protection = desc->need_mem_protection; - ret = q6v5_request_irq(qproc, pdev, "wdog", q6v5_wdog_interrupt); - if (ret < 0) - goto free_rproc; - ret = q6v5_request_irq(qproc, pdev, "fatal", q6v5_fatal_interrupt); - if (ret < 0) - goto free_rproc; - - ret = q6v5_request_irq(qproc, pdev, "ready", q6v5_ready_interrupt); - if (ret < 0) - goto free_rproc; - - ret = q6v5_request_irq(qproc, pdev, "handover", q6v5_handover_interrupt); - if (ret < 0) - goto free_rproc; - qproc->handover_irq = ret; - disable_irq(qproc->handover_irq); - - ret = q6v5_request_irq(qproc, pdev, "stop-ack", q6v5_stop_ack_interrupt); - if (ret < 0) + ret = qcom_q6v5_init(&qproc->q6v5, pdev, rproc, MPSS_CRASH_REASON_SMEM, + qcom_msa_handover); + if (ret) goto free_rproc; - qproc->state = qcom_smem_state_get(&pdev->dev, "stop", &qproc->stop_bit); - if (IS_ERR(qproc->state)) { - ret = PTR_ERR(qproc->state); - goto free_rproc; - } qproc->mpss_perm = BIT(QCOM_SCM_VMID_HLOS); qproc->mba_perm = BIT(QCOM_SCM_VMID_HLOS); qcom_add_glink_subdev(rproc, &qproc->glink_subdev); @@ -1370,7 +1243,6 @@ static const struct rproc_hexagon_res sdm845_mss = { .hexagon_mba_image = "mba.mbn", .proxy_clk_names = (char*[]){ "xo", - "axis2", "prng", NULL }, diff --git a/drivers/remoteproc/qcom_q6v5_wcss.c b/drivers/remoteproc/qcom_q6v5_wcss.c new file mode 100644 index 000000000000..f93e1e4a1cc0 --- /dev/null +++ b/drivers/remoteproc/qcom_q6v5_wcss.c @@ -0,0 +1,601 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016-2018 Linaro Ltd. + * Copyright (C) 2014 Sony Mobile Communications AB + * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. + */ +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_reserved_mem.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <linux/soc/qcom/mdt_loader.h> +#include "qcom_common.h" +#include "qcom_q6v5.h" + +#define WCSS_CRASH_REASON 421 + +/* Q6SS Register Offsets */ +#define Q6SS_RESET_REG 0x014 +#define Q6SS_GFMUX_CTL_REG 0x020 +#define Q6SS_PWR_CTL_REG 0x030 +#define Q6SS_MEM_PWR_CTL 0x0B0 + +/* AXI Halt Register Offsets */ +#define AXI_HALTREQ_REG 0x0 +#define AXI_HALTACK_REG 0x4 +#define AXI_IDLE_REG 0x8 + +#define HALT_ACK_TIMEOUT_MS 100 + +/* Q6SS_RESET */ +#define Q6SS_STOP_CORE BIT(0) +#define Q6SS_CORE_ARES BIT(1) +#define Q6SS_BUS_ARES_ENABLE BIT(2) + +/* Q6SS_GFMUX_CTL */ +#define Q6SS_CLK_ENABLE BIT(1) + +/* Q6SS_PWR_CTL */ +#define Q6SS_L2DATA_STBY_N BIT(18) +#define Q6SS_SLP_RET_N BIT(19) +#define Q6SS_CLAMP_IO BIT(20) +#define QDSS_BHS_ON BIT(21) + +/* Q6SS parameters */ +#define Q6SS_LDO_BYP BIT(25) +#define Q6SS_BHS_ON BIT(24) +#define Q6SS_CLAMP_WL BIT(21) +#define Q6SS_CLAMP_QMC_MEM BIT(22) +#define HALT_CHECK_MAX_LOOPS 200 +#define Q6SS_XO_CBCR GENMASK(5, 3) + +/* Q6SS config/status registers */ +#define TCSR_GLOBAL_CFG0 0x0 +#define TCSR_GLOBAL_CFG1 0x4 +#define SSCAON_CONFIG 0x8 +#define SSCAON_STATUS 0xc +#define Q6SS_BHS_STATUS 0x78 +#define Q6SS_RST_EVB 0x10 + +#define BHS_EN_REST_ACK BIT(0) +#define SSCAON_ENABLE BIT(13) +#define SSCAON_BUS_EN BIT(15) +#define SSCAON_BUS_MUX_MASK GENMASK(18, 16) + +#define MEM_BANKS 19 +#define TCSR_WCSS_CLK_MASK 0x1F +#define TCSR_WCSS_CLK_ENABLE 0x14 + +struct q6v5_wcss { + struct device *dev; + + void __iomem *reg_base; + void __iomem *rmb_base; + + struct regmap *halt_map; + u32 halt_q6; + u32 halt_wcss; + u32 halt_nc; + + struct reset_control *wcss_aon_reset; + struct reset_control *wcss_reset; + struct reset_control *wcss_q6_reset; + + struct qcom_q6v5 q6v5; + + phys_addr_t mem_phys; + phys_addr_t mem_reloc; + void *mem_region; + size_t mem_size; +}; + +static int q6v5_wcss_reset(struct q6v5_wcss *wcss) +{ + int ret; + u32 val; + int i; + + /* Assert resets, stop core */ + val = readl(wcss->reg_base + Q6SS_RESET_REG); + val |= Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE; + writel(val, wcss->reg_base + Q6SS_RESET_REG); + + /* BHS require xo cbcr to be enabled */ + val = readl(wcss->reg_base + Q6SS_XO_CBCR); + val |= 0x1; + writel(val, wcss->reg_base + Q6SS_XO_CBCR); + + /* Read CLKOFF bit to go low indicating CLK is enabled */ + ret = readl_poll_timeout(wcss->reg_base + Q6SS_XO_CBCR, + val, !(val & BIT(31)), 1, + HALT_CHECK_MAX_LOOPS); + if (ret) { + dev_err(wcss->dev, + "xo cbcr enabling timed out (rc:%d)\n", ret); + return ret; + } + /* Enable power block headswitch and wait for it to stabilize */ + val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG); + val |= Q6SS_BHS_ON; + writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); + udelay(1); + + /* Put LDO in bypass mode */ + val |= Q6SS_LDO_BYP; + writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); + + /* Deassert Q6 compiler memory clamp */ + val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG); + val &= ~Q6SS_CLAMP_QMC_MEM; + writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); + + /* Deassert memory peripheral sleep and L2 memory standby */ + val |= Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N; + writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); + + /* Turn on L1, L2, ETB and JU memories 1 at a time */ + val = readl(wcss->reg_base + Q6SS_MEM_PWR_CTL); + for (i = MEM_BANKS; i >= 0; i--) { + val |= BIT(i); + writel(val, wcss->reg_base + Q6SS_MEM_PWR_CTL); + /* + * Read back value to ensure the write is done then + * wait for 1us for both memory peripheral and data + * array to turn on. + */ + val |= readl(wcss->reg_base + Q6SS_MEM_PWR_CTL); + udelay(1); + } + /* Remove word line clamp */ + val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG); + val &= ~Q6SS_CLAMP_WL; + writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); + + /* Remove IO clamp */ + val &= ~Q6SS_CLAMP_IO; + writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); + + /* Bring core out of reset */ + val = readl(wcss->reg_base + Q6SS_RESET_REG); + val &= ~Q6SS_CORE_ARES; + writel(val, wcss->reg_base + Q6SS_RESET_REG); + + /* Turn on core clock */ + val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG); + val |= Q6SS_CLK_ENABLE; + writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG); + + /* Start core execution */ + val = readl(wcss->reg_base + Q6SS_RESET_REG); + val &= ~Q6SS_STOP_CORE; + writel(val, wcss->reg_base + Q6SS_RESET_REG); + + return 0; +} + +static int q6v5_wcss_start(struct rproc *rproc) +{ + struct q6v5_wcss *wcss = rproc->priv; + int ret; + + qcom_q6v5_prepare(&wcss->q6v5); + + /* Release Q6 and WCSS reset */ + ret = reset_control_deassert(wcss->wcss_reset); + if (ret) { + dev_err(wcss->dev, "wcss_reset failed\n"); + return ret; + } + + ret = reset_control_deassert(wcss->wcss_q6_reset); + if (ret) { + dev_err(wcss->dev, "wcss_q6_reset failed\n"); + goto wcss_reset; + } + + /* Lithium configuration - clock gating and bus arbitration */ + ret = regmap_update_bits(wcss->halt_map, + wcss->halt_nc + TCSR_GLOBAL_CFG0, + TCSR_WCSS_CLK_MASK, + TCSR_WCSS_CLK_ENABLE); + if (ret) + goto wcss_q6_reset; + + ret = regmap_update_bits(wcss->halt_map, + wcss->halt_nc + TCSR_GLOBAL_CFG1, + 1, 0); + if (ret) + goto wcss_q6_reset; + + /* Write bootaddr to EVB so that Q6WCSS will jump there after reset */ + writel(rproc->bootaddr >> 4, wcss->reg_base + Q6SS_RST_EVB); + + ret = q6v5_wcss_reset(wcss); + if (ret) + goto wcss_q6_reset; + + ret = qcom_q6v5_wait_for_start(&wcss->q6v5, 5 * HZ); + if (ret == -ETIMEDOUT) + dev_err(wcss->dev, "start timed out\n"); + + return ret; + +wcss_q6_reset: + reset_control_assert(wcss->wcss_q6_reset); + +wcss_reset: + reset_control_assert(wcss->wcss_reset); + + return ret; +} + +static void q6v5_wcss_halt_axi_port(struct q6v5_wcss *wcss, + struct regmap *halt_map, + u32 offset) +{ + unsigned long timeout; + unsigned int val; + int ret; + + /* Check if we're already idle */ + ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val); + if (!ret && val) + return; + + /* Assert halt request */ + regmap_write(halt_map, offset + AXI_HALTREQ_REG, 1); + + /* Wait for halt */ + timeout = jiffies + msecs_to_jiffies(HALT_ACK_TIMEOUT_MS); + for (;;) { + ret = regmap_read(halt_map, offset + AXI_HALTACK_REG, &val); + if (ret || val || time_after(jiffies, timeout)) + break; + + msleep(1); + } + + ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val); + if (ret || !val) + dev_err(wcss->dev, "port failed halt\n"); + + /* Clear halt request (port will remain halted until reset) */ + regmap_write(halt_map, offset + AXI_HALTREQ_REG, 0); +} + +static int q6v5_wcss_powerdown(struct q6v5_wcss *wcss) +{ + int ret; + u32 val; + + /* 1 - Assert WCSS/Q6 HALTREQ */ + q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_wcss); + + /* 2 - Enable WCSSAON_CONFIG */ + val = readl(wcss->rmb_base + SSCAON_CONFIG); + val |= SSCAON_ENABLE; + writel(val, wcss->rmb_base + SSCAON_CONFIG); + + /* 3 - Set SSCAON_CONFIG */ + val |= SSCAON_BUS_EN; + val &= ~SSCAON_BUS_MUX_MASK; + writel(val, wcss->rmb_base + SSCAON_CONFIG); + + /* 4 - SSCAON_CONFIG 1 */ + val |= BIT(1); + writel(val, wcss->rmb_base + SSCAON_CONFIG); + + /* 5 - wait for SSCAON_STATUS */ + ret = readl_poll_timeout(wcss->rmb_base + SSCAON_STATUS, + val, (val & 0xffff) == 0x400, 1000, + HALT_CHECK_MAX_LOOPS); + if (ret) { + dev_err(wcss->dev, + "can't get SSCAON_STATUS rc:%d)\n", ret); + return ret; + } + + /* 6 - De-assert WCSS_AON reset */ + reset_control_assert(wcss->wcss_aon_reset); + + /* 7 - Disable WCSSAON_CONFIG 13 */ + val = readl(wcss->rmb_base + SSCAON_CONFIG); + val &= ~SSCAON_ENABLE; + writel(val, wcss->rmb_base + SSCAON_CONFIG); + + /* 8 - De-assert WCSS/Q6 HALTREQ */ + reset_control_assert(wcss->wcss_reset); + + return 0; +} + +static int q6v5_q6_powerdown(struct q6v5_wcss *wcss) +{ + int ret; + u32 val; + int i; + + /* 1 - Halt Q6 bus interface */ + q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_q6); + + /* 2 - Disable Q6 Core clock */ + val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG); + val &= ~Q6SS_CLK_ENABLE; + writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG); + + /* 3 - Clamp I/O */ + val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG); + val |= Q6SS_CLAMP_IO; + writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); + + /* 4 - Clamp WL */ + val |= QDSS_BHS_ON; + writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); + + /* 5 - Clear Erase standby */ + val &= ~Q6SS_L2DATA_STBY_N; + writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); + + /* 6 - Clear Sleep RTN */ + val &= ~Q6SS_SLP_RET_N; + writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); + + /* 7 - turn off Q6 memory foot/head switch one bank at a time */ + for (i = 0; i < 20; i++) { + val = readl(wcss->reg_base + Q6SS_MEM_PWR_CTL); + val &= ~BIT(i); + writel(val, wcss->reg_base + Q6SS_MEM_PWR_CTL); + mdelay(1); + } + + /* 8 - Assert QMC memory RTN */ + val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG); + val |= Q6SS_CLAMP_QMC_MEM; + writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); + + /* 9 - Turn off BHS */ + val &= ~Q6SS_BHS_ON; + writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); + udelay(1); + + /* 10 - Wait till BHS Reset is done */ + ret = readl_poll_timeout(wcss->reg_base + Q6SS_BHS_STATUS, + val, !(val & BHS_EN_REST_ACK), 1000, + HALT_CHECK_MAX_LOOPS); + if (ret) { + dev_err(wcss->dev, "BHS_STATUS not OFF (rc:%d)\n", ret); + return ret; + } + + /* 11 - Assert WCSS reset */ + reset_control_assert(wcss->wcss_reset); + + /* 12 - Assert Q6 reset */ + reset_control_assert(wcss->wcss_q6_reset); + + return 0; +} + +static int q6v5_wcss_stop(struct rproc *rproc) +{ + struct q6v5_wcss *wcss = rproc->priv; + int ret; + + /* WCSS powerdown */ + ret = qcom_q6v5_request_stop(&wcss->q6v5); + if (ret == -ETIMEDOUT) { + dev_err(wcss->dev, "timed out on wait\n"); + return ret; + } + + ret = q6v5_wcss_powerdown(wcss); + if (ret) + return ret; + + /* Q6 Power down */ + ret = q6v5_q6_powerdown(wcss); + if (ret) + return ret; + + qcom_q6v5_unprepare(&wcss->q6v5); + + return 0; +} + +static void *q6v5_wcss_da_to_va(struct rproc *rproc, u64 da, int len) +{ + struct q6v5_wcss *wcss = rproc->priv; + int offset; + + offset = da - wcss->mem_reloc; + if (offset < 0 || offset + len > wcss->mem_size) + return NULL; + + return wcss->mem_region + offset; +} + +static int q6v5_wcss_load(struct rproc *rproc, const struct firmware *fw) +{ + struct q6v5_wcss *wcss = rproc->priv; + + return qcom_mdt_load_no_init(wcss->dev, fw, rproc->firmware, + 0, wcss->mem_region, wcss->mem_phys, + wcss->mem_size, &wcss->mem_reloc); +} + +static const struct rproc_ops q6v5_wcss_ops = { + .start = q6v5_wcss_start, + .stop = q6v5_wcss_stop, + .da_to_va = q6v5_wcss_da_to_va, + .load = q6v5_wcss_load, + .get_boot_addr = rproc_elf_get_boot_addr, +}; + +static int q6v5_wcss_init_reset(struct q6v5_wcss *wcss) +{ + struct device *dev = wcss->dev; + + wcss->wcss_aon_reset = devm_reset_control_get(dev, "wcss_aon_reset"); + if (IS_ERR(wcss->wcss_aon_reset)) { + dev_err(wcss->dev, "unable to acquire wcss_aon_reset\n"); + return PTR_ERR(wcss->wcss_aon_reset); + } + + wcss->wcss_reset = devm_reset_control_get(dev, "wcss_reset"); + if (IS_ERR(wcss->wcss_reset)) { + dev_err(wcss->dev, "unable to acquire wcss_reset\n"); + return PTR_ERR(wcss->wcss_reset); + } + + wcss->wcss_q6_reset = devm_reset_control_get(dev, "wcss_q6_reset"); + if (IS_ERR(wcss->wcss_q6_reset)) { + dev_err(wcss->dev, "unable to acquire wcss_q6_reset\n"); + return PTR_ERR(wcss->wcss_q6_reset); + } + + return 0; +} + +static int q6v5_wcss_init_mmio(struct q6v5_wcss *wcss, + struct platform_device *pdev) +{ + struct of_phandle_args args; + struct resource *res; + int ret; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6"); + wcss->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(wcss->reg_base)) + return PTR_ERR(wcss->reg_base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rmb"); + wcss->rmb_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(wcss->rmb_base)) + return PTR_ERR(wcss->rmb_base); + + ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, + "qcom,halt-regs", 3, 0, &args); + if (ret < 0) { + dev_err(&pdev->dev, "failed to parse qcom,halt-regs\n"); + return -EINVAL; + } + + wcss->halt_map = syscon_node_to_regmap(args.np); + of_node_put(args.np); + if (IS_ERR(wcss->halt_map)) + return PTR_ERR(wcss->halt_map); + + wcss->halt_q6 = args.args[0]; + wcss->halt_wcss = args.args[1]; + wcss->halt_nc = args.args[2]; + + return 0; +} + +static int q6v5_alloc_memory_region(struct q6v5_wcss *wcss) +{ + struct reserved_mem *rmem = NULL; + struct device_node *node; + struct device *dev = wcss->dev; + + node = of_parse_phandle(dev->of_node, "memory-region", 0); + if (node) + rmem = of_reserved_mem_lookup(node); + of_node_put(node); + + if (!rmem) { + dev_err(dev, "unable to acquire memory-region\n"); + return -EINVAL; + } + + wcss->mem_phys = rmem->base; + wcss->mem_reloc = rmem->base; + wcss->mem_size = rmem->size; + wcss->mem_region = devm_ioremap_wc(dev, wcss->mem_phys, wcss->mem_size); + if (!wcss->mem_region) { + dev_err(dev, "unable to map memory region: %pa+%pa\n", + &rmem->base, &rmem->size); + return -EBUSY; + } + + return 0; +} + +static int q6v5_wcss_probe(struct platform_device *pdev) +{ + struct q6v5_wcss *wcss; + struct rproc *rproc; + int ret; + + rproc = rproc_alloc(&pdev->dev, pdev->name, &q6v5_wcss_ops, + "IPQ8074/q6_fw.mdt", sizeof(*wcss)); + if (!rproc) { + dev_err(&pdev->dev, "failed to allocate rproc\n"); + return -ENOMEM; + } + + wcss = rproc->priv; + wcss->dev = &pdev->dev; + + ret = q6v5_wcss_init_mmio(wcss, pdev); + if (ret) + goto free_rproc; + + ret = q6v5_alloc_memory_region(wcss); + if (ret) + goto free_rproc; + + ret = q6v5_wcss_init_reset(wcss); + if (ret) + goto free_rproc; + + ret = qcom_q6v5_init(&wcss->q6v5, pdev, rproc, WCSS_CRASH_REASON, NULL); + if (ret) + goto free_rproc; + + ret = rproc_add(rproc); + if (ret) + goto free_rproc; + + platform_set_drvdata(pdev, rproc); + + return 0; + +free_rproc: + rproc_free(rproc); + + return ret; +} + +static int q6v5_wcss_remove(struct platform_device *pdev) +{ + struct rproc *rproc = platform_get_drvdata(pdev); + + rproc_del(rproc); + rproc_free(rproc); + + return 0; +} + +static const struct of_device_id q6v5_wcss_of_match[] = { + { .compatible = "qcom,ipq8074-wcss-pil" }, + { }, +}; +MODULE_DEVICE_TABLE(of, q6v5_wcss_of_match); + +static struct platform_driver q6v5_wcss_driver = { + .probe = q6v5_wcss_probe, + .remove = q6v5_wcss_remove, + .driver = { + .name = "qcom-q6v5-wcss-pil", + .of_match_table = q6v5_wcss_of_match, + }, +}; +module_platform_driver(q6v5_wcss_driver); + +MODULE_DESCRIPTION("Hexagon WCSS Peripheral Image Loader"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/remoteproc/qcom_sysmon.c b/drivers/remoteproc/qcom_sysmon.c index f085545d7da5..e976a602b015 100644 --- a/drivers/remoteproc/qcom_sysmon.c +++ b/drivers/remoteproc/qcom_sysmon.c @@ -469,7 +469,10 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, qmi_add_lookup(&sysmon->qmi, 43, 0, 0); - rproc_add_subdev(rproc, &sysmon->subdev, sysmon_start, sysmon_stop); + sysmon->subdev.start = sysmon_start; + sysmon->subdev.stop = sysmon_stop; + + rproc_add_subdev(rproc, &sysmon->subdev); sysmon->nb.notifier_call = sysmon_notify; blocking_notifier_chain_register(&sysmon_notifiers, &sysmon->nb); diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index a9609d971f7f..aa6206706fe3 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -241,7 +241,7 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) if (notifyid > rproc->max_notifyid) rproc->max_notifyid = notifyid; - dev_dbg(dev, "vring%d: va %p dma %pad size 0x%x idr %d\n", + dev_dbg(dev, "vring%d: va %pK dma %pad size 0x%x idr %d\n", i, va, &dma, size, notifyid); rvring->va = va; @@ -301,14 +301,14 @@ void rproc_free_vring(struct rproc_vring *rvring) rsc->vring[idx].notifyid = -1; } -static int rproc_vdev_do_probe(struct rproc_subdev *subdev) +static int rproc_vdev_do_start(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, bool crashed) +static void rproc_vdev_do_stop(struct rproc_subdev *subdev, bool crashed) { struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev); @@ -399,8 +399,10 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, list_add_tail(&rvdev->node, &rproc->rvdevs); - rproc_add_subdev(rproc, &rvdev->subdev, - rproc_vdev_do_probe, rproc_vdev_do_remove); + rvdev->subdev.start = rproc_vdev_do_start; + rvdev->subdev.stop = rproc_vdev_do_stop; + + rproc_add_subdev(rproc, &rvdev->subdev); return 0; @@ -497,7 +499,7 @@ 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", + dev_dbg(dev, "%s added: va %pK, da 0x%x, len 0x%x\n", name, ptr, rsc->da, rsc->len); return 0; @@ -635,7 +637,7 @@ static int rproc_handle_carveout(struct rproc *rproc, goto free_carv; } - dev_dbg(dev, "carveout va %p, dma %pad, len 0x%x\n", + dev_dbg(dev, "carveout va %pK, dma %pad, len 0x%x\n", va, &dma, rsc->len); /* @@ -774,32 +776,72 @@ static int rproc_handle_resources(struct rproc *rproc, return ret; } -static int rproc_probe_subdevices(struct rproc *rproc) +static int rproc_prepare_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; + if (subdev->prepare) { + ret = subdev->prepare(subdev); + if (ret) + goto unroll_preparation; + } + } + + return 0; + +unroll_preparation: + list_for_each_entry_continue_reverse(subdev, &rproc->subdevs, node) { + if (subdev->unprepare) + subdev->unprepare(subdev); + } + + return ret; +} + +static int rproc_start_subdevices(struct rproc *rproc) +{ + struct rproc_subdev *subdev; + int ret; + + list_for_each_entry(subdev, &rproc->subdevs, node) { + if (subdev->start) { + ret = subdev->start(subdev); + if (ret) + goto unroll_registration; + } } return 0; unroll_registration: - list_for_each_entry_continue_reverse(subdev, &rproc->subdevs, node) - subdev->remove(subdev, true); + list_for_each_entry_continue_reverse(subdev, &rproc->subdevs, node) { + if (subdev->stop) + subdev->stop(subdev, true); + } return ret; } -static void rproc_remove_subdevices(struct rproc *rproc, bool crashed) +static void rproc_stop_subdevices(struct rproc *rproc, bool crashed) { struct rproc_subdev *subdev; - list_for_each_entry_reverse(subdev, &rproc->subdevs, node) - subdev->remove(subdev, crashed); + list_for_each_entry_reverse(subdev, &rproc->subdevs, node) { + if (subdev->stop) + subdev->stop(subdev, crashed); + } +} + +static void rproc_unprepare_subdevices(struct rproc *rproc) +{ + struct rproc_subdev *subdev; + + list_for_each_entry_reverse(subdev, &rproc->subdevs, node) { + if (subdev->unprepare) + subdev->unprepare(subdev); + } } /** @@ -894,20 +936,26 @@ static int rproc_start(struct rproc *rproc, const struct firmware *fw) rproc->table_ptr = loaded_table; } + ret = rproc_prepare_subdevices(rproc); + if (ret) { + dev_err(dev, "failed to prepare subdevices for %s: %d\n", + rproc->name, ret); + goto reset_table_ptr; + } + /* 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); - return ret; + goto unprepare_subdevices; } - /* probe any subdevices for the remote processor */ - ret = rproc_probe_subdevices(rproc); + /* Start any subdevices for the remote processor */ + ret = rproc_start_subdevices(rproc); if (ret) { dev_err(dev, "failed to probe subdevices for %s: %d\n", rproc->name, ret); - rproc->ops->stop(rproc); - return ret; + goto stop_rproc; } rproc->state = RPROC_RUNNING; @@ -915,6 +963,15 @@ static int rproc_start(struct rproc *rproc, const struct firmware *fw) dev_info(dev, "remote processor %s is now up\n", rproc->name); return 0; + +stop_rproc: + rproc->ops->stop(rproc); +unprepare_subdevices: + rproc_unprepare_subdevices(rproc); +reset_table_ptr: + rproc->table_ptr = rproc->cached_table; + + return ret; } /* @@ -1014,8 +1071,8 @@ static int rproc_stop(struct rproc *rproc, bool crashed) struct device *dev = &rproc->dev; int ret; - /* remove any subdevices for the remote processor */ - rproc_remove_subdevices(rproc, crashed); + /* Stop any subdevices for the remote processor */ + rproc_stop_subdevices(rproc, crashed); /* the installed resource table is no longer accessible */ rproc->table_ptr = rproc->cached_table; @@ -1027,6 +1084,8 @@ static int rproc_stop(struct rproc *rproc, bool crashed) return ret; } + rproc_unprepare_subdevices(rproc); + rproc->state = RPROC_OFFLINE; dev_info(dev, "stopped remote processor %s\n", rproc->name); @@ -1657,17 +1716,11 @@ 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 + * + * Caller is responsible for populating optional subdevice function pointers. */ -void rproc_add_subdev(struct rproc *rproc, - struct rproc_subdev *subdev, - int (*probe)(struct rproc_subdev *subdev), - void (*remove)(struct rproc_subdev *subdev, bool crashed)) +void rproc_add_subdev(struct rproc *rproc, struct rproc_subdev *subdev) { - subdev->probe = probe; - subdev->remove = remove; - list_add_tail(&subdev->node, &rproc->subdevs); } EXPORT_SYMBOL(rproc_add_subdev); diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c index a20488336aa0..a5c29f2764a3 100644 --- a/drivers/remoteproc/remoteproc_debugfs.c +++ b/drivers/remoteproc/remoteproc_debugfs.c @@ -231,7 +231,7 @@ static int rproc_rsc_table_show(struct seq_file *seq, void *p) } break; default: - seq_printf(seq, "Unknown resource type found: %d [hdr: %p]\n", + seq_printf(seq, "Unknown resource type found: %d [hdr: %pK]\n", hdr->type, hdr); break; } @@ -260,7 +260,7 @@ static int rproc_carveouts_show(struct seq_file *seq, void *p) list_for_each_entry(carveout, &rproc->carveouts, node) { seq_puts(seq, "Carveout memory entry:\n"); - seq_printf(seq, "\tVirtual address: %p\n", carveout->va); + seq_printf(seq, "\tVirtual address: %pK\n", carveout->va); seq_printf(seq, "\tDMA address: %pad\n", &carveout->dma); seq_printf(seq, "\tDevice address: 0x%x\n", carveout->da); seq_printf(seq, "\tLength: 0x%x Bytes\n\n", carveout->len); diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c index b0633fd4c041..bbecd44df7e8 100644 --- a/drivers/remoteproc/remoteproc_virtio.c +++ b/drivers/remoteproc/remoteproc_virtio.c @@ -96,7 +96,7 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, size = vring_size(len, rvring->align); memset(addr, 0, size); - dev_dbg(dev, "vring%d: va %p qsz %d notifyid %d\n", + dev_dbg(dev, "vring%d: va %pK qsz %d notifyid %d\n", id, addr, len, rvring->notifyid); /* diff --git a/drivers/remoteproc/st_slim_rproc.c b/drivers/remoteproc/st_slim_rproc.c index 1ffb1f0c43d6..d711d9430a4f 100644 --- a/drivers/remoteproc/st_slim_rproc.c +++ b/drivers/remoteproc/st_slim_rproc.c @@ -195,7 +195,8 @@ static void *slim_rproc_da_to_va(struct rproc *rproc, u64 da, int len) } } - dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%x va = 0x%p\n", da, len, va); + dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%x va = 0x%pK\n", + da, len, va); return va; } diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c index dc09d7ac905f..1c488024c698 100644 --- a/drivers/soc/qcom/mdt_loader.c +++ b/drivers/soc/qcom/mdt_loader.c @@ -74,23 +74,10 @@ ssize_t qcom_mdt_get_size(const struct firmware *fw) } EXPORT_SYMBOL_GPL(qcom_mdt_get_size); -/** - * qcom_mdt_load() - load the firmware which header is loaded as fw - * @dev: device handle to associate resources with - * @fw: firmware object for the mdt file - * @firmware: name of the firmware, for construction of segment file names - * @pas_id: PAS identifier - * @mem_region: allocated memory region to load firmware into - * @mem_phys: physical address of allocated memory region - * @mem_size: size of the allocated memory region - * @reloc_base: adjusted physical address after relocation - * - * Returns 0 on success, negative errno otherwise. - */ -int qcom_mdt_load(struct device *dev, const struct firmware *fw, - const char *firmware, int pas_id, void *mem_region, - phys_addr_t mem_phys, size_t mem_size, - phys_addr_t *reloc_base) +static int __qcom_mdt_load(struct device *dev, const struct firmware *fw, + const char *firmware, int pas_id, void *mem_region, + phys_addr_t mem_phys, size_t mem_size, + phys_addr_t *reloc_base, bool pas_init) { const struct elf32_phdr *phdrs; const struct elf32_phdr *phdr; @@ -121,10 +108,12 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw, if (!fw_name) return -ENOMEM; - ret = qcom_scm_pas_init_image(pas_id, fw->data, fw->size); - if (ret) { - dev_err(dev, "invalid firmware metadata\n"); - goto out; + if (pas_init) { + ret = qcom_scm_pas_init_image(pas_id, fw->data, fw->size); + if (ret) { + dev_err(dev, "invalid firmware metadata\n"); + goto out; + } } for (i = 0; i < ehdr->e_phnum; i++) { @@ -144,10 +133,13 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw, } if (relocate) { - ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr); - if (ret) { - dev_err(dev, "unable to setup relocation\n"); - goto out; + if (pas_init) { + ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, + max_addr - min_addr); + if (ret) { + dev_err(dev, "unable to setup relocation\n"); + goto out; + } } /* @@ -202,7 +194,52 @@ out: return ret; } + +/** + * qcom_mdt_load() - load the firmware which header is loaded as fw + * @dev: device handle to associate resources with + * @fw: firmware object for the mdt file + * @firmware: name of the firmware, for construction of segment file names + * @pas_id: PAS identifier + * @mem_region: allocated memory region to load firmware into + * @mem_phys: physical address of allocated memory region + * @mem_size: size of the allocated memory region + * @reloc_base: adjusted physical address after relocation + * + * Returns 0 on success, negative errno otherwise. + */ +int qcom_mdt_load(struct device *dev, const struct firmware *fw, + const char *firmware, int pas_id, void *mem_region, + phys_addr_t mem_phys, size_t mem_size, + phys_addr_t *reloc_base) +{ + return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys, + mem_size, reloc_base, true); +} EXPORT_SYMBOL_GPL(qcom_mdt_load); +/** + * qcom_mdt_load_no_init() - load the firmware which header is loaded as fw + * @dev: device handle to associate resources with + * @fw: firmware object for the mdt file + * @firmware: name of the firmware, for construction of segment file names + * @pas_id: PAS identifier + * @mem_region: allocated memory region to load firmware into + * @mem_phys: physical address of allocated memory region + * @mem_size: size of the allocated memory region + * @reloc_base: adjusted physical address after relocation + * + * Returns 0 on success, negative errno otherwise. + */ +int qcom_mdt_load_no_init(struct device *dev, const struct firmware *fw, + const char *firmware, int pas_id, + void *mem_region, phys_addr_t mem_phys, + size_t mem_size, phys_addr_t *reloc_base) +{ + return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys, + mem_size, reloc_base, false); +} +EXPORT_SYMBOL_GPL(qcom_mdt_load_no_init); + MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format"); MODULE_LICENSE("GPL v2"); diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index dfdaede9139e..e3c5d856b6da 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -477,15 +477,19 @@ struct rproc { /** * struct rproc_subdev - subdevice tied to a remoteproc * @node: list node related to the rproc subdevs list - * @probe: probe function, called as the rproc is started - * @remove: remove function, called as the rproc is being stopped, the @crashed - * parameter indicates if this originates from the a recovery + * @prepare: prepare function, called before the rproc is started + * @start: start function, called after the rproc has been started + * @stop: stop function, called before the rproc is stopped; the @crashed + * parameter indicates if this originates from a recovery + * @unprepare: unprepare function, called after the rproc has been stopped */ struct rproc_subdev { struct list_head node; - int (*probe)(struct rproc_subdev *subdev); - void (*remove)(struct rproc_subdev *subdev, bool crashed); + int (*prepare)(struct rproc_subdev *subdev); + int (*start)(struct rproc_subdev *subdev); + void (*stop)(struct rproc_subdev *subdev, bool crashed); + void (*unprepare)(struct rproc_subdev *subdev); }; /* we currently support only two vrings per rvdev */ @@ -566,10 +570,7 @@ static inline struct rproc *vdev_to_rproc(struct virtio_device *vdev) return rvdev->rproc; } -void rproc_add_subdev(struct rproc *rproc, - struct rproc_subdev *subdev, - int (*probe)(struct rproc_subdev *subdev), - void (*remove)(struct rproc_subdev *subdev, bool crashed)); +void rproc_add_subdev(struct rproc *rproc, struct rproc_subdev *subdev); void rproc_remove_subdev(struct rproc *rproc, struct rproc_subdev *subdev); diff --git a/include/linux/soc/qcom/mdt_loader.h b/include/linux/soc/qcom/mdt_loader.h index 5b98bbdabc25..944b06aefb0f 100644 --- a/include/linux/soc/qcom/mdt_loader.h +++ b/include/linux/soc/qcom/mdt_loader.h @@ -17,4 +17,8 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw, phys_addr_t mem_phys, size_t mem_size, phys_addr_t *reloc_base); +int qcom_mdt_load_no_init(struct device *dev, const struct firmware *fw, + const char *fw_name, int pas_id, void *mem_region, + phys_addr_t mem_phys, size_t mem_size, + phys_addr_t *reloc_base); #endif |