Commit c54fc865 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'rproc-v4.19' of git://github.com/andersson/remoteproc

Pull remoteproc updates from Bjorn Andersson:
 "This adds support for pre-start and post-shutdown hooks for remoteproc
  subdevices, refactors the Qualcomm Hexagon support to allow reuse
  between several drivers, makes authentication in the MDT file loader
  optional, migrates a few format strings to use %pK and migrates the
  Davinci driver to use the reset framework"

* tag 'rproc-v4.19' of git://github.com/andersson/remoteproc:
  remoteproc/davinci: use the reset framework
  remoteproc/davinci: Mark error recovery as disabled
  remoteproc: st_slim: replace "%p" with "%pK"
  remoteproc: replace "%p" with "%pK"
  remoteproc: qcom: fix Q6V5_WCSS dependencies
  remoteproc: Reset table_ptr in rproc_start() failure paths
  remoteproc: qcom: q6v5-pil: fix modem hang on SDM845 after axis2 clk unvote
  remoteproc: qcom q6v5: fix modular build
  remoteproc: Introduce prepare and unprepare for subdevices
  remoteproc: rename subdev probe and remove functions
  remoteproc: Make client initialize ops in rproc_subdev
  remoteproc: Make start and stop in subdev optional
  remoteproc: Rename subdev functions to start/stop
  remoteproc: qcom: Introduce Hexagon V5 based WCSS driver
  remoteproc: qcom: q6v5-pil: Use common q6v5 helpers
  remoteproc: qcom: adsp: Use common q6v5 helpers
  remoteproc: q6v5: Extract common resource handling
  remoteproc: qcom: mdt_loader: Make the firmware authentication optional
parents 6eaac34f b2201ee5
......@@ -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:
......
......@@ -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
......
......@@ -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
......
......@@ -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;
......
......@@ -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 @@ static int adsp_start(struct rproc *rproc)
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);
......
......@@ -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);
......
// 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");
/* 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
......@@ -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 @@ static int q6v5_start(struct rproc *rproc)
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
},
......
// 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");
......@@ -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);
......
......@@ -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 (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);
......
......@@ -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);
......
......@@ -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);
/*
......
......@@ -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;
}
......
......@@ -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,
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)
phys_addr_t *reloc_base, bool pas_init)
{
const struct elf32_phdr *phdrs;
const struct elf32_phdr *phdr;
......@@ -121,11 +108,13 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw,
if (!fw_name)
return -ENOMEM;
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++) {
phdr = &phdrs[i];
......@@ -144,11 +133,14 @@ 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 (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;
}
}
/*
* The image is relocatable, so offset each segment based on
......@@ -202,7 +194,52 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw,
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");
......@@ -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);
......
......@@ -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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment