remoteproc updates for v4.19
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. -----BEGIN PGP SIGNATURE----- iQJPBAABCAA5FiEEBd4DzF816k8JZtUlCx85Pw2ZrcUFAlt3Pq8bHGJqb3JuLmFu ZGVyc3NvbkBsaW5hcm8ub3JnAAoJEAsfOT8Nma3F7WQP+wT8r4ZAZ5MBSaoQTwCi Ce9+Ixxsn64tcyYMeTnUABC3EsHA4ysilbIGtqiYzpmaJhMr87+MUWKa84SyMILA lomrfzDIWvuAX9Y2NWwOuYH8FI+syq8NuES+O5S7bMfIK7zqOho+lpiI4Q8jHOAD 6y5Ta5FyoZZsy0SmtAwoFDPHKHW2pPb0RU/Fd7UOo4oXkeusuLpeZjDLTOgwoc9/ uSNM8UG+I3mslR4GdJOAwh+C8mRyVJ16YmVrvMrKjxY+MG53D1ODIO4L39j5Vf1Y 5HdIBazXrw4InPMeijOj8OI/NNJS2Ia7TJM6UdKqRTfwQfAXg9ondlxxvXm5ghju sVwx/X/duZm8QJVWaes9iAJ7seN4gooywChqrq5I5GM09jFk2qZ8DqjfesyLjOjm V3+6JCisfUm+UZsToeM1yTjOpCKPtIFrxRRllK7gMxkLvIZ8DQRV7oRT2AD8dKfX wl7apbWXKEBjtfnqV0RrR2M2znIPPC6aGjFFdWCOYo3NTiIKDnF1qeSwVGroCKUM ey+A8dBHN39qA4qfidp/QghwHg216XbyQlUlCEnJ9bqaWGYhsZRQl31W5y3fEPWL UfXkPHjuW8Ulc3yVK8pcHP5xjgg2TF5UbODHDBp1bgUQm/vmeclx+Aa2x7GBqTaF EE2uyhQjro0EJKYFGzjklJi6 =QpCe -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
c54fc8658b
18 changed files with 1189 additions and 362 deletions
|
@ -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)
|
||||
ret = qcom_q6v5_init(&adsp->q6v5, pdev, rproc, desc->crash_reason_smem,
|
||||
qcom_pas_handover);
|
||||
if (ret)
|
||||
goto free_rproc;
|
||||
adsp->wdog_irq = ret;
|
||||
|
||||
ret = adsp_request_irq(adsp, pdev, "fatal", adsp_fatal_interrupt);
|
||||
if (ret < 0)
|
||||
goto free_rproc;
|
||||
adsp->fatal_irq = ret;
|
||||
|
||||
ret = adsp_request_irq(adsp, pdev, "ready", adsp_ready_interrupt);
|
||||
if (ret < 0)
|
||||
goto free_rproc;
|
||||
adsp->ready_irq = ret;
|
||||
|
||||
ret = adsp_request_irq(adsp, pdev, "handover", adsp_handover_interrupt);
|
||||
if (ret < 0)
|
||||
goto free_rproc;
|
||||
adsp->handover_irq = ret;
|
||||
|
||||
ret = adsp_request_irq(adsp, pdev, "stop-ack", adsp_stop_ack_interrupt);
|
||||
if (ret < 0)
|
||||
goto free_rproc;
|
||||
adsp->stop_ack_irq = ret;
|
||||
|
||||
adsp->state = qcom_smem_state_get(&pdev->dev, "stop",
|
||||
&adsp->stop_bit);
|
||||
if (IS_ERR(adsp->state)) {
|
||||
ret = PTR_ERR(adsp->state);
|
||||
goto free_rproc;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
252
drivers/remoteproc/qcom_q6v5.c
Normal file
252
drivers/remoteproc/qcom_q6v5.c
Normal file
|
@ -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");
|
46
drivers/remoteproc/qcom_q6v5.h
Normal file
46
drivers/remoteproc/qcom_q6v5.h
Normal file
|
@ -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
|
|
@ -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)
|
||||
static void qcom_msa_handover(struct qcom_q6v5 *q6v5)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
|
||||
ret = qcom_q6v5_init(&qproc->q6v5, pdev, rproc, MPSS_CRASH_REASON_SMEM,
|
||||
qcom_msa_handover);
|
||||
if (ret)
|
||||
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)
|
||||
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
|
||||
},
|
||||
|
|
601
drivers/remoteproc/qcom_q6v5_wcss.c
Normal file
601
drivers/remoteproc/qcom_q6v5_wcss.c
Normal file
|
@ -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");
|
|
@ -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 (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);
|
||||
|
|
|
@ -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,
|
||||
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 @@ 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
|
||||
|
|
Loading…
Reference in a new issue