Commit 8796ac1d authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'rproc-v5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/andersson/remoteproc

Pull remoteproc updates from Bjorn Andersson:
 "This adds support to the remoteproc core for detaching Linux from a
  running remoteproc, e.g. to reboot Linux while leaving the remoteproc
  running, and it enable this support in the stm32 remoteproc driver.

  It also introduces a property for memory carveouts to track if they
  are iomem or system ram, to enable proper handling of the differences.

  The imx_rproc received a number of fixes and improvements, in
  particular support for attaching to already running remote processors
  and i.MX8MQ and i.MX8MM support.

  The Qualcomm wcss driver gained support for starting and stopping the
  wireless subsystem on QCS404, when not using the TrustZone-based
  validator/loader.

  Finally it brings a few fixes to the TI PRU and to the firmware loader
  for the Qualcomm modem subsystem drivers"

* tag 'rproc-v5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/andersson/remoteproc: (53 commits)
  remoteproc: stm32: add capability to detach
  dt-bindings: remoteproc: stm32-rproc: add new mailbox channel for detach
  remoteproc: imx_rproc: support remote cores booted before Linux Kernel
  remoteproc: imx_rproc: move memory parsing to rproc_ops
  remoteproc: imx_rproc: enlarge IMX7D_RPROC_MEM_MAX
  remoteproc: imx_rproc: add missing of_node_put
  remoteproc: imx_rproc: fix build error without CONFIG_MAILBOX
  remoteproc: qcom: wcss: Remove unnecessary PTR_ERR()
  remoteproc: qcom: wcss: Fix wrong pointer passed to PTR_ERR()
  remoteproc: qcom: pas: Add modem support for SDX55
  dt-bindings: remoteproc: qcom: pas: Add binding for SDX55
  remoteproc: qcom: wcss: Fix return value check in q6v5_wcss_init_mmio()
  remoteproc: pru: Fix and cleanup firmware interrupt mapping logic
  remoteproc: pru: Fix wrong success return value for fw events
  remoteproc: pru: Fixup interrupt-parent logic for fw events
  remoteproc: qcom: wcnss: Allow specifying firmware-name
  remoteproc: qcom: wcss: explicitly request exclusive reset control
  remoteproc: qcom: wcss: Add non pas wcss Q6 support for QCS404
  dt-bindings: remoteproc: qcom: Add Q6V5 Modem PIL binding for QCS404
  remoteproc: qcom: wcss: populate hardcoded param using driver data
  ...
parents a01d9524 edf696f2
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: "http://devicetree.org/schemas/remoteproc/fsl,imx-rproc.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: NXP i.MX Co-Processor Bindings
description:
This binding provides support for ARM Cortex M4 Co-processor found on some NXP iMX SoCs.
maintainers:
- Peng Fan <peng.fan@nxp.com>
properties:
compatible:
enum:
- fsl,imx8mq-cm4
- fsl,imx8mm-cm4
- fsl,imx7d-cm4
- fsl,imx6sx-cm4
clocks:
maxItems: 1
syscon:
$ref: /schemas/types.yaml#/definitions/phandle
description:
Phandle to syscon block which provide access to System Reset Controller
mbox-names:
items:
- const: tx
- const: rx
- const: rxdb
mboxes:
description:
This property is required only if the rpmsg/virtio functionality is used.
List of <&phandle type channel> - 1 channel for TX, 1 channel for RX, 1 channel for RXDB.
(see mailbox/fsl,mu.yaml)
minItems: 1
maxItems: 3
memory-region:
description:
If present, a phandle for a reserved memory area that used for vdev buffer,
resource table, vring region and others used by remote processor.
minItems: 1
maxItems: 32
required:
- compatible
- clocks
- syscon
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/imx7d-clock.h>
m4_reserved_sysmem1: cm4@80000000 {
reg = <0x80000000 0x80000>;
};
m4_reserved_sysmem2: cm4@81000000 {
reg = <0x81000000 0x80000>;
};
imx7d-cm4 {
compatible = "fsl,imx7d-cm4";
memory-region = <&m4_reserved_sysmem1>, <&m4_reserved_sysmem2>;
syscon = <&src>;
clocks = <&clks IMX7D_ARM_M4_ROOT_CLK>;
};
- |
#include <dt-bindings/clock/imx8mm-clock.h>
imx8mm-cm4 {
compatible = "fsl,imx8mm-cm4";
clocks = <&clk IMX8MM_CLK_M4_DIV>;
mbox-names = "tx", "rx", "rxdb";
mboxes = <&mu 0 1
&mu 1 1
&mu 3 1>;
memory-region = <&vdev0buffer>, <&vdev0vring0>, <&vdev0vring1>, <&rsc_table>;
syscon = <&src>;
};
...
NXP iMX6SX/iMX7D Co-Processor Bindings
----------------------------------------
This binding provides support for ARM Cortex M4 Co-processor found on some
NXP iMX SoCs.
Required properties:
- compatible Should be one of:
"fsl,imx7d-cm4"
"fsl,imx6sx-cm4"
- clocks Clock for co-processor (See: ../clock/clock-bindings.txt)
- syscon Phandle to syscon block which provide access to
System Reset Controller
Optional properties:
- memory-region list of phandels to the reserved memory regions.
(See: ../reserved-memory/reserved-memory.txt)
Example:
m4_reserved_sysmem1: cm4@80000000 {
reg = <0x80000000 0x80000>;
};
m4_reserved_sysmem2: cm4@81000000 {
reg = <0x81000000 0x80000>;
};
imx7d-cm4 {
compatible = "fsl,imx7d-cm4";
memory-region = <&m4_reserved_sysmem1>, <&m4_reserved_sysmem2>;
syscon = <&src>;
clocks = <&clks IMX7D_ARM_M4_ROOT_CLK>;
};
......@@ -18,6 +18,7 @@ on the Qualcomm ADSP Hexagon core.
"qcom,sc7180-mpss-pas"
"qcom,sdm845-adsp-pas"
"qcom,sdm845-cdsp-pas"
"qcom,sdx55-mpss-pas"
"qcom,sm8150-adsp-pas"
"qcom,sm8150-cdsp-pas"
"qcom,sm8150-mpss-pas"
......@@ -61,6 +62,7 @@ on the Qualcomm ADSP Hexagon core.
must be "wdog", "fatal", "ready", "handover", "stop-ack"
qcom,qcs404-wcss-pas:
qcom,sc7180-mpss-pas:
qcom,sdx55-mpss-pas:
qcom,sm8150-mpss-pas:
qcom,sm8350-mpss-pas:
must be "wdog", "fatal", "ready", "handover", "stop-ack",
......@@ -128,6 +130,8 @@ on the Qualcomm ADSP Hexagon core.
qcom,sm8150-mpss-pas:
qcom,sm8350-mpss-pas:
must be "cx", "load_state", "mss"
qcom,sdx55-mpss-pas:
must be "cx", "mss"
qcom,sm8250-adsp-pas:
qcom,sm8350-adsp-pas:
qcom,sm8150-slpi-pas:
......
......@@ -9,6 +9,7 @@ on the Qualcomm Hexagon core.
Definition: must be one of:
"qcom,q6v5-pil",
"qcom,ipq8074-wcss-pil"
"qcom,qcs404-wcss-pil"
"qcom,msm8916-mss-pil",
"qcom,msm8974-mss-pil"
"qcom,msm8996-mss-pil"
......@@ -39,6 +40,7 @@ on the Qualcomm Hexagon core.
string:
qcom,q6v5-pil:
qcom,ipq8074-wcss-pil:
qcom,qcs404-wcss-pil:
qcom,msm8916-mss-pil:
qcom,msm8974-mss-pil:
must be "wdog", "fatal", "ready", "handover", "stop-ack"
......@@ -67,6 +69,11 @@ on the Qualcomm Hexagon core.
Definition: The clocks needed depend on the compatible string:
qcom,ipq8074-wcss-pil:
no clock names required
qcom,qcs404-wcss-pil:
must be "xo", "gcc_abhs_cbcr", "gcc_abhs_cbcr",
"gcc_axim_cbcr", "lcc_ahbfabric_cbc", "tcsr_lcc_cbc",
"lcc_abhs_cbc", "lcc_tcm_slave_cbc", "lcc_abhm_cbc",
"lcc_axim_cbc", "lcc_bcr_sleep"
qcom,q6v5-pil:
qcom,msm8916-mss-pil:
qcom,msm8974-mss-pil:
......@@ -132,6 +139,14 @@ For the compatible string below the following supplies are required:
Definition: reference to the regulators to be held on behalf of the
booting of the Hexagon core
For the compatible string below the following supplies are required:
"qcom,qcs404-wcss-pil"
- cx-supply:
Usage: required
Value type: <phandle>
Definition: reference to the regulators to be held on behalf of the
booting of the Hexagon core
For the compatible string below the following supplies are required:
"qcom,msm8996-mss-pil"
- pll-supply:
......
......@@ -34,6 +34,12 @@ on the Qualcomm WCNSS core.
Definition: should be "wdog", "fatal", optionally followed by "ready",
"handover", "stop-ack"
- firmware-name:
Usage: optional
Value type: <string>
Definition: must list the relative firmware image path for the
WCNSS core. Defaults to "wcnss.mdt".
- vddmx-supply: (deprecated for qcom,pronto-v1/2-pil)
- vddcx-supply: (deprecated for qcom,pronto-v1/2-pil)
- vddpx-supply:
......
......@@ -65,16 +65,23 @@ properties:
Unidirectional channel:
- from local to remote, where ACK from the remote means that it is
ready for shutdown
- description: |
A channel (d) used by the local proc to notify the remote proc that it
has to stop interprocessor communnication.
Unidirectional channel:
- from local to remote, where ACK from the remote means that communnication
as been stopped on the remote side.
minItems: 1
maxItems: 3
maxItems: 4
mbox-names:
items:
- const: vq0
- const: vq1
- const: shutdown
- const: detach
minItems: 1
maxItems: 3
maxItems: 4
memory-region:
description:
......
......@@ -24,11 +24,12 @@ config REMOTEPROC_CDEV
It's safe to say N if you don't want to use this interface.
config IMX_REMOTEPROC
tristate "IMX6/7 remoteproc support"
tristate "i.MX remoteproc support"
depends on ARCH_MXC
select MAILBOX
help
Say y here to support iMX's remote processors (Cortex M4
on iMX7D) via the remote processor framework.
Say y here to support iMX's remote processors via the remote
processor framework.
It's safe to say N here.
......
This diff is collapsed.
......@@ -121,7 +121,7 @@ static void ingenic_rproc_kick(struct rproc *rproc, int vqid)
writel(vqid, vpu->aux_base + REG_CORE_MSG);
}
static void *ingenic_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len)
static void *ingenic_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
{
struct vpu *vpu = rproc->priv;
void __iomem *va = NULL;
......
......@@ -246,7 +246,7 @@ static void keystone_rproc_kick(struct rproc *rproc, int vqid)
* can be used either by the remoteproc core for loading (when using kernel
* remoteproc loader), or by any rpmsg bus drivers.
*/
static void *keystone_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len)
static void *keystone_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
{
struct keystone_rproc *ksproc = rproc->priv;
void __iomem *va = NULL;
......
......@@ -272,7 +272,7 @@ static int scp_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
}
/* grab the kernel address for this device address */
ptr = (void __iomem *)rproc_da_to_va(rproc, da, memsz);
ptr = (void __iomem *)rproc_da_to_va(rproc, da, memsz, NULL);
if (!ptr) {
dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz);
ret = -EINVAL;
......@@ -509,7 +509,7 @@ static void *mt8192_scp_da_to_va(struct mtk_scp *scp, u64 da, size_t len)
return NULL;
}
static void *scp_da_to_va(struct rproc *rproc, u64 da, size_t len)
static void *scp_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
{
struct mtk_scp *scp = (struct mtk_scp *)rproc->priv;
......@@ -627,7 +627,7 @@ void *scp_mapping_dm_addr(struct mtk_scp *scp, u32 mem_addr)
{
void *ptr;
ptr = scp_da_to_va(scp->rproc, mem_addr, 0);
ptr = scp_da_to_va(scp->rproc, mem_addr, 0, NULL);
if (!ptr)
return ERR_PTR(-EINVAL);
......
......@@ -728,7 +728,7 @@ static int omap_rproc_stop(struct rproc *rproc)
* Return: translated virtual address in kernel memory space on success,
* or NULL on failure.
*/
static void *omap_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len)
static void *omap_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
{
struct omap_rproc *oproc = rproc->priv;
int i;
......
......@@ -244,8 +244,8 @@ static int pru_rproc_debug_ss_get(void *data, u64 *val)
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(pru_rproc_debug_ss_fops, pru_rproc_debug_ss_get,
pru_rproc_debug_ss_set, "%llu\n");
DEFINE_DEBUGFS_ATTRIBUTE(pru_rproc_debug_ss_fops, pru_rproc_debug_ss_get,
pru_rproc_debug_ss_set, "%llu\n");
/*
* Create PRU-specific debugfs entries
......@@ -266,12 +266,17 @@ static void pru_rproc_create_debug_entries(struct rproc *rproc)
static void pru_dispose_irq_mapping(struct pru_rproc *pru)
{
while (pru->evt_count--) {
if (!pru->mapped_irq)
return;
while (pru->evt_count) {
pru->evt_count--;
if (pru->mapped_irq[pru->evt_count] > 0)
irq_dispose_mapping(pru->mapped_irq[pru->evt_count]);
}
kfree(pru->mapped_irq);
pru->mapped_irq = NULL;
}
/*
......@@ -284,7 +289,7 @@ static int pru_handle_intrmap(struct rproc *rproc)
struct pru_rproc *pru = rproc->priv;
struct pru_irq_rsc *rsc = pru->pru_interrupt_map;
struct irq_fwspec fwspec;
struct device_node *irq_parent;
struct device_node *parent, *irq_parent;
int i, ret = 0;
/* not having pru_interrupt_map is not an error */
......@@ -307,16 +312,31 @@ static int pru_handle_intrmap(struct rproc *rproc)
pru->evt_count = rsc->num_evts;
pru->mapped_irq = kcalloc(pru->evt_count, sizeof(unsigned int),
GFP_KERNEL);
if (!pru->mapped_irq)
if (!pru->mapped_irq) {
pru->evt_count = 0;
return -ENOMEM;
}
/*
* parse and fill in system event to interrupt channel and
* channel-to-host mapping
* channel-to-host mapping. The interrupt controller to be used
* for these mappings for a given PRU remoteproc is always its
* corresponding sibling PRUSS INTC node.
*/
irq_parent = of_irq_find_parent(pru->dev->of_node);
parent = of_get_parent(dev_of_node(pru->dev));
if (!parent) {
kfree(pru->mapped_irq);
pru->mapped_irq = NULL;
pru->evt_count = 0;
return -ENODEV;
}
irq_parent = of_get_child_by_name(parent, "interrupt-controller");
of_node_put(parent);
if (!irq_parent) {
kfree(pru->mapped_irq);
pru->mapped_irq = NULL;
pru->evt_count = 0;
return -ENODEV;
}
......@@ -332,16 +352,20 @@ static int pru_handle_intrmap(struct rproc *rproc)
pru->mapped_irq[i] = irq_create_fwspec_mapping(&fwspec);
if (!pru->mapped_irq[i]) {
dev_err(dev, "failed to get virq\n");
ret = pru->mapped_irq[i];
dev_err(dev, "failed to get virq for fw mapping %d: event %d chnl %d host %d\n",
i, fwspec.param[0], fwspec.param[1],
fwspec.param[2]);
ret = -EINVAL;
goto map_fail;
}
}
of_node_put(irq_parent);
return ret;
map_fail:
pru_dispose_irq_mapping(pru);
of_node_put(irq_parent);
return ret;
}
......@@ -387,8 +411,7 @@ static int pru_rproc_stop(struct rproc *rproc)
pru_control_write_reg(pru, PRU_CTRL_CTRL, val);
/* dispose irq mapping - new firmware can provide new mapping */
if (pru->mapped_irq)
pru_dispose_irq_mapping(pru);
pru_dispose_irq_mapping(pru);
return 0;
}
......@@ -483,7 +506,7 @@ static void *pru_i_da_to_va(struct pru_rproc *pru, u32 da, size_t len)
* core for any PRU client drivers. The PRU Instruction RAM access is restricted
* only to the PRU loader code.
*/
static void *pru_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len)
static void *pru_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
{
struct pru_rproc *pru = rproc->priv;
......
......@@ -281,7 +281,7 @@ static int adsp_stop(struct rproc *rproc)
return ret;
}
static void *adsp_da_to_va(struct rproc *rproc, u64 da, size_t len)
static void *adsp_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
{
struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
int offset;
......
......@@ -1210,6 +1210,14 @@ static int q6v5_mpss_load(struct q6v5 *qproc)
goto release_firmware;
}
if (phdr->p_filesz > phdr->p_memsz) {
dev_err(qproc->dev,
"refusing to load segment %d with p_filesz > p_memsz\n",
i);
ret = -EINVAL;
goto release_firmware;
}
ptr = memremap(qproc->mpss_phys + offset, phdr->p_memsz, MEMREMAP_WC);
if (!ptr) {
dev_err(qproc->dev,
......@@ -1241,6 +1249,16 @@ static int q6v5_mpss_load(struct q6v5 *qproc)
goto release_firmware;
}
if (seg_fw->size != phdr->p_filesz) {
dev_err(qproc->dev,
"failed to load segment %d from truncated file %s\n",
i, fw_name);
ret = -EINVAL;
release_firmware(seg_fw);
memunmap(ptr);
goto release_firmware;
}
release_firmware(seg_fw);
}
......@@ -1661,8 +1679,10 @@ static int q6v5_probe(struct platform_device *pdev)
mba_image = desc->hexagon_mba_image;
ret = of_property_read_string_index(pdev->dev.of_node, "firmware-name",
0, &mba_image);
if (ret < 0 && ret != -EINVAL)
if (ret < 0 && ret != -EINVAL) {
dev_err(&pdev->dev, "unable to read mba firmware-name\n");
return ret;
}
rproc = rproc_alloc(&pdev->dev, pdev->name, &q6v5_ops,
mba_image, sizeof(*qproc));
......@@ -1680,8 +1700,10 @@ static int q6v5_probe(struct platform_device *pdev)
qproc->hexagon_mdt_image = "modem.mdt";
ret = of_property_read_string_index(pdev->dev.of_node, "firmware-name",
1, &qproc->hexagon_mdt_image);
if (ret < 0 && ret != -EINVAL)
if (ret < 0 && ret != -EINVAL) {
dev_err(&pdev->dev, "unable to read mpss firmware-name\n");
goto free_rproc;
}
platform_set_drvdata(pdev, qproc);
......
......@@ -242,7 +242,7 @@ static int adsp_stop(struct rproc *rproc)
return ret;
}
static void *adsp_da_to_va(struct rproc *rproc, u64 da, size_t len)
static void *adsp_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
{
struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
int offset;
......@@ -785,6 +785,22 @@ static const struct adsp_data wcss_resource_init = {
.ssctl_id = 0x12,
};
static const struct adsp_data sdx55_mpss_resource = {
.crash_reason_smem = 421,
.firmware_name = "modem.mdt",
.pas_id = 4,
.has_aggre2_clk = false,
.auto_boot = true,
.proxy_pd_names = (char*[]){
"cx",
"mss",
NULL
},
.ssr_name = "mpss",
.sysmon_name = "modem",
.ssctl_id = 0x22,
};
static const struct of_device_id adsp_of_match[] = {
{ .compatible = "qcom,msm8974-adsp-pil", .data = &adsp_resource_init},
{ .compatible = "qcom,msm8996-adsp-pil", .data = &adsp_resource_init},
......@@ -797,6 +813,7 @@ static const struct of_device_id adsp_of_match[] = {
{ .compatible = "qcom,sc7180-mpss-pas", .data = &mpss_resource_init},
{ .compatible = "qcom,sdm845-adsp-pas", .data = &adsp_resource_init},
{ .compatible = "qcom,sdm845-cdsp-pas", .data = &cdsp_resource_init},
{ .compatible = "qcom,sdx55-mpss-pas", .data = &sdx55_mpss_resource},
{ .compatible = "qcom,sm8150-adsp-pas", .data = &sm8150_adsp_resource},
{ .compatible = "qcom,sm8150-cdsp-pas", .data = &sm8150_cdsp_resource},
{ .compatible = "qcom,sm8150-mpss-pas", .data = &mpss_resource_init},
......
This diff is collapsed.
......@@ -320,7 +320,7 @@ static int wcnss_stop(struct rproc *rproc)
return ret;
}
static void *wcnss_da_to_va(struct rproc *rproc, u64 da, size_t len)
static void *wcnss_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
{
struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv;
int offset;
......@@ -530,6 +530,7 @@ static int wcnss_alloc_memory_region(struct qcom_wcnss *wcnss)
static int wcnss_probe(struct platform_device *pdev)
{
const char *fw_name = WCNSS_FIRMWARE_NAME;
const struct wcnss_data *data;
struct qcom_wcnss *wcnss;
struct resource *res;
......@@ -547,8 +548,13 @@ static int wcnss_probe(struct platform_device *pdev)
return -ENXIO;
}
ret = of_property_read_string(pdev->dev.of_node, "firmware-name",
&fw_name);
if (ret < 0 && ret != -EINVAL)
return ret;
rproc = rproc_alloc(&pdev->dev, pdev->name, &wcnss_ops,
WCNSS_FIRMWARE_NAME, sizeof(*wcnss));
fw_name, sizeof(*wcnss));
if (!rproc) {
dev_err(&pdev->dev, "unable to allocate remoteproc\n");
return -ENOMEM;
......
......@@ -32,15 +32,22 @@ static ssize_t rproc_cdev_write(struct file *filp, const char __user *buf, size_
return -EFAULT;
if (!strncmp(cmd, "start", len)) {
if (rproc->state == RPROC_RUNNING)
if (rproc->state == RPROC_RUNNING ||
rproc->state == RPROC_ATTACHED)
return -EBUSY;
ret = rproc_boot(rproc);
} else if (!strncmp(cmd, "stop", len)) {
if (rproc->state != RPROC_RUNNING)
if (rproc->state != RPROC_RUNNING &&
rproc->state != RPROC_ATTACHED)
return -EINVAL;
rproc_shutdown(rproc);
} else if (!strncmp(cmd, "detach", len)) {
if (rproc->state != RPROC_ATTACHED)
return -EINVAL;
ret = rproc_detach(rproc);
} else {
dev_err(&rproc->dev, "Unrecognized option\n");
ret = -EINVAL;
......@@ -79,11 +86,17 @@ static long rproc_device_ioctl(struct file *filp, unsigned int ioctl, unsigned l
static int rproc_cdev_release(struct inode *inode, struct file *filp)
{
struct rproc *rproc = container_of(inode->i_cdev, struct rproc, cdev);
int ret = 0;
if (rproc->cdev_put_on_release && rproc->state == RPROC_RUNNING)
if (!rproc->cdev_put_on_release)
return 0;
if (rproc->state == RPROC_RUNNING)
rproc_shutdown(rproc);
else if (rproc->state == RPROC_ATTACHED)
ret = rproc_detach(rproc);
return 0;
return ret;
}
static const struct file_operations rproc_fops = {
......
This diff is collapsed.
......@@ -153,18 +153,22 @@ static void rproc_copy_segment(struct rproc *rproc, void *dest,
size_t offset, size_t size)
{
void *ptr;
bool is_iomem;
if (segment->dump) {
segment->dump(rproc, segment, dest, offset, size);
} else {
ptr = rproc_da_to_va(rproc, segment->da + offset, size);
ptr = rproc_da_to_va(rproc, segment->da + offset, size, &is_iomem);
if (!ptr) {
dev_err(&rproc->dev,
"invalid copy request for segment %pad with offset %zu and size %zu)\n",
&segment->da, offset, size);
memset(dest, 0xff, size);
} else {
memcpy(dest, ptr, size);
if (is_iomem)
memcpy_fromio(dest, ptr, size);
else
memcpy(dest, ptr, size);
}
}
}
......
......@@ -132,7 +132,7 @@ static ssize_t rproc_trace_read(struct file *filp, char __user *userbuf,
char buf[100];
int len;
va = rproc_da_to_va(data->rproc, trace->da, trace->len);
va = rproc_da_to_va(data->rproc, trace->da, trace->len, NULL);
if (!va) {
len = scnprintf(buf, sizeof(buf), "Trace %s not available\n",
......
......@@ -175,6 +175,7 @@ int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
u64 offset = elf_phdr_get_p_offset(class, phdr);
u32 type = elf_phdr_get_p_type(class, phdr);
void *ptr;
bool is_iomem;
if (type != PT_LOAD)
continue;
......@@ -204,7 +205,7 @@ int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
}
/* grab the kernel address for this device address */
ptr = rproc_da_to_va(rproc, da, memsz);
ptr = rproc_da_to_va(rproc, da, memsz, &is_iomem);
if (!ptr) {
dev_err(dev, "bad phdr da 0x%llx mem 0x%llx\n", da,
memsz);
......@@ -213,8 +214,12 @@ int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
}
/* put the segment where the remote processor expects it */
if (filesz)
memcpy(ptr, elf_data + offset, filesz);
if (filesz) {
if (is_iomem)
memcpy_fromio(ptr, (void __iomem *)(elf_data + offset), filesz);
else
memcpy(ptr, elf_data + offset, filesz);
}
/*
* Zero out remaining memory for this segment.
......@@ -223,8 +228,12 @@ int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
* did this for us. albeit harmless, we may consider removing
* this.
*/
if (memsz > filesz)
memset(ptr + filesz, 0, memsz - filesz);
if (memsz > filesz) {
if (is_iomem)
memset_io((void __iomem *)(ptr + filesz), 0, memsz - filesz);
else
memset(ptr + filesz, 0, memsz - filesz);
}
}
return ret;
......@@ -377,6 +386,6 @@ struct resource_table *rproc_elf_find_loaded_rsc_table(struct rproc *rproc,
return NULL;
}
return rproc_da_to_va(rproc, sh_addr, sh_size);
return rproc_da_to_va(rproc, sh_addr, sh_size, NULL);
}
EXPORT_SYMBOL(rproc_elf_find_loaded_rsc_table);
......@@ -84,7 +84,7 @@ static inline void rproc_char_device_remove(struct rproc *rproc)
void rproc_free_vring(struct rproc_vring *rvring);
int rproc_alloc_vring(struct rproc_vdev *rvdev, int i);
void *rproc_da_to_va(struct rproc *rproc, u64 da, size_t len);
void *rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem);
phys_addr_t rproc_va_to_pa(void *cpu_addr);
int rproc_trigger_recovery(struct rproc *rproc);
......@@ -177,6 +177,16 @@ struct resource_table *rproc_find_loaded_rsc_table(struct rproc *rproc,
return NULL;
}
static inline
struct resource_table *rproc_get_loaded_rsc_table(struct rproc *rproc,
size_t *size)
{
if (rproc->ops->get_loaded_rsc_table)
return rproc->ops->get_loaded_rsc_table(rproc, size);
return NULL;
}
static inline
bool rproc_u64_fit_in_size_t(u64 val)
{
......
......@@ -15,7 +15,7 @@ static ssize_t recovery_show(struct device *dev,
{
struct rproc *rproc = to_rproc(dev);
return sprintf(buf, "%s", rproc->recovery_disabled ? "disabled\n" : "enabled\n");
return sysfs_emit(buf, "%s", rproc->recovery_disabled ? "disabled\n" : "enabled\n");
}
/*
......@@ -82,7 +82,7 @@ static ssize_t coredump_show(struct device *dev,
{
struct rproc *rproc = to_rproc(dev);
return sprintf(buf, "%s\n", rproc_coredump_str[rproc->dump_conf]);
return sysfs_emit(buf, "%s\n", rproc_coredump_str[rproc->dump_conf]);
}
/*
......@@ -138,11 +138,8 @@ static ssize_t firmware_show(struct device *dev, struct device_attribute *attr,
* If the remote processor has been started by an external
* entity we have no idea of what image it is running. As such
* simply display a generic string rather then rproc->firmware.
*
* Here we rely on the autonomous flag because a remote processor
* may have been attached to and currently in a running state.
*/
if (rproc->autonomous)
if (rproc->state == RPROC_ATTACHED)
firmware = "unknown";
return sprintf(buf, "%s\n", firmware);
......@@ -172,6 +169,7 @@ static const char * const rproc_state_string[] = {
[RPROC_RUNNING] = "running",
[RPROC_CRASHED] = "crashed",
[RPROC_DELETED] = "deleted",
[RPROC_ATTACHED] = "attached",
[RPROC_DETACHED] = "detached",
[RPROC_LAST] = "invalid",
};
......@@ -196,17 +194,24 @@ static ssize_t state_store(struct device *dev,
int ret = 0;
if (sysfs_streq(buf, "start")) {
if (rproc->state == RPROC_RUNNING)
if (rproc->state == RPROC_RUNNING ||
rproc->state == RPROC_ATTACHED)
return -EBUSY;
ret = rproc_boot(rproc);
if (ret)
dev_err(&rproc->dev, "Boot failed: %d\n", ret);
} else if (sysfs_streq(buf, "stop")) {
if (rproc->state != RPROC_RUNNING)
if (rproc->state != RPROC_RUNNING &&
rproc->state != RPROC_ATTACHED)
return -EINVAL;
rproc_shutdown(rproc);
} else if (sysfs_streq(buf, "detach")) {
if (rproc->state != RPROC_ATTACHED)
return -EINVAL;
ret = rproc_detach(rproc);
} else {
dev_err(&rproc->dev, "Unrecognised option: %s\n", buf);
ret = -EINVAL;
......
......@@ -174,7 +174,7 @@ static int slim_rproc_stop(struct rproc *rproc)
return 0;
}
static void *slim_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len)
static void *slim_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
{
struct st_slim_rproc *slim_rproc = rproc->priv;
void *va = NULL;
......
......@@ -28,7 +28,7 @@
#define RELEASE_BOOT 1
#define MBOX_NB_VQ 2
#define MBOX_NB_MBX 3
#define MBOX_NB_MBX 4
#define STM32_SMC_RCC 0x82001000
#define STM32_SMC_REG_WRITE 0x1
......@@ -38,6 +38,7 @@
#define STM32_MBX_VQ1 "vq1"
#define STM32_MBX_VQ1_ID 1
#define STM32_MBX_SHUTDOWN "shutdown"
#define STM32_MBX_DETACH "detach"
#define RSC_TBL_SIZE 1024
......@@ -207,16 +208,7 @@ static int stm32_rproc_mbox_idx(struct rproc *rproc, const unsigned char *name)
return -EINVAL;
}
static int stm32_rproc_elf_load_rsc_table(struct rproc *rproc,
const struct firmware *fw)
{
if (rproc_elf_load_rsc_table(rproc, fw))
dev_warn(&rproc->dev, "no resource table found for this firmware\n");
return 0;
}
static int stm32_rproc_parse_memory_regions(struct rproc *rproc)
static int stm32_rproc_prepare(struct rproc *rproc)
{
struct device *dev = rproc->dev.parent;
struct device_node *np = dev->of_node;
......@@ -274,12 +266,10 @@ static int stm32_rproc_parse_memory_regions(struct rproc *rproc)
static int stm32_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
{
int ret = stm32_rproc_parse_memory_regions(rproc);
if (ret)
return ret;
if (rproc_elf_load_rsc_table(rproc, fw))
dev_warn(&rproc->dev, "no resource table found for this firmware\n");
return stm32_rproc_elf_load_rsc_table(rproc, fw);
return 0;
}
static irqreturn_t stm32_rproc_wdg(int irq, void *data)
......@@ -347,6 +337,15 @@ static const struct stm32_mbox stm32_rproc_mbox[MBOX_NB_MBX] = {
.tx_done = NULL,
.tx_tout = 500, /* 500 ms time out */
},
},
{
.name = STM32_MBX_DETACH,
.vq_id = -1,
.client = {
.tx_block = true,
.tx_done = NULL,
.tx_tout = 200, /* 200 ms time out to detach should be fair enough */
},
}
};
......@@ -472,6 +471,25 @@ static int stm32_rproc_attach(struct rproc *rproc)
return stm32_rproc_set_hold_boot(rproc, true);
}
static int stm32_rproc_detach(struct rproc *rproc)
{
struct stm32_rproc *ddata = rproc->priv;
int err, dummy_data, idx;
/* Inform the remote processor of the detach */
idx = stm32_rproc_mbox_idx(rproc, STM32_MBX_DETACH);
if (idx >= 0 && ddata->mb[idx].chan) {
/* A dummy data is sent to allow to block on transmit */
err = mbox_send_message(ddata->mb[idx].chan,
&dummy_data);
if (err < 0)
dev_warn(&rproc->dev, "warning: remote FW detach without ack\n");
}
/* Allow remote processor to auto-reboot */
return stm32_rproc_set_hold_boot(rproc, false);
}
static int stm32_rproc_stop(struct rproc *rproc)
{
struct stm32_rproc *ddata = rproc->priv;
......@@ -546,14 +564,89 @@ static void stm32_rproc_kick(struct rproc *rproc, int vqid)
}
}
static int stm32_rproc_da_to_pa(struct rproc *rproc,
u64 da, phys_addr_t *pa)
{
struct stm32_rproc *ddata = rproc->priv;
struct device *dev = rproc->dev.parent;
struct stm32_rproc_mem *p_mem;
unsigned int i;
for (i = 0; i < ddata->nb_rmems; i++) {
p_mem = &ddata->rmems[i];
if (da < p_mem->dev_addr ||
da >= p_mem->dev_addr + p_mem->size)
continue;
*pa = da - p_mem->dev_addr + p_mem->bus_addr;
dev_dbg(dev, "da %llx to pa %#x\n", da, *pa);
return 0;
}
dev_err(dev, "can't translate da %llx\n", da);
return -EINVAL;
}
static struct resource_table *
stm32_rproc_get_loaded_rsc_table(struct rproc *rproc, size_t *table_sz)
{
struct stm32_rproc *ddata = rproc->priv;
struct device *dev = rproc->dev.parent;
phys_addr_t rsc_pa;
u32 rsc_da;
int err;
/* The resource table has already been mapped, nothing to do */
if (ddata->rsc_va)
goto done;
err = regmap_read(ddata->rsctbl.map, ddata->rsctbl.reg, &rsc_da);
if (err) {
dev_err(dev, "failed to read rsc tbl addr\n");
return ERR_PTR(-EINVAL);
}
if (!rsc_da)
/* no rsc table */
return ERR_PTR(-ENOENT);
err = stm32_rproc_da_to_pa(rproc, rsc_da, &rsc_pa);
if (err)
return ERR_PTR(err);
ddata->rsc_va = devm_ioremap_wc(dev, rsc_pa, RSC_TBL_SIZE);
if (IS_ERR_OR_NULL(ddata->rsc_va)) {
dev_err(dev, "Unable to map memory region: %pa+%zx\n",
&rsc_pa, RSC_TBL_SIZE);
ddata->rsc_va = NULL;
return ERR_PTR(-ENOMEM);
}
done:
/*
* Assuming the resource table fits in 1kB is fair.
* Notice for the detach, that this 1 kB memory area has to be reserved in the coprocessor
* firmware for the resource table. On detach, the remoteproc core re-initializes this
* entire area by overwriting it with the initial values stored in rproc->clean_table.
*/
*table_sz = RSC_TBL_SIZE;
return (struct resource_table *)ddata->rsc_va;
}
static const struct rproc_ops st_rproc_ops = {
.prepare = stm32_rproc_prepare,
.start = stm32_rproc_start,
.stop = stm32_rproc_stop,
.attach = stm32_rproc_attach,
.detach = stm32_rproc_detach,
.kick = stm32_rproc_kick,
.load = rproc_elf_load_segments,
.parse_fw = stm32_rproc_parse_fw,
.find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table,
.get_loaded_rsc_table = stm32_rproc_get_loaded_rsc_table,
.sanity_check = rproc_elf_sanity_check,
.get_boot_addr = rproc_elf_get_boot_addr,
};
......@@ -695,75 +788,6 @@ static int stm32_rproc_get_m4_status(struct stm32_rproc *ddata,
return regmap_read(ddata->m4_state.map, ddata->m4_state.reg, state);
}
static int stm32_rproc_da_to_pa(struct platform_device *pdev,
struct stm32_rproc *ddata,
u64 da, phys_addr_t *pa)
{
struct device *dev = &pdev->dev;
struct stm32_rproc_mem *p_mem;
unsigned int i;
for (i = 0; i < ddata->nb_rmems; i++) {
p_mem = &ddata->rmems[i];
if (da < p_mem->dev_addr ||
da >= p_mem->dev_addr + p_mem->size)
continue;
*pa = da - p_mem->dev_addr + p_mem->bus_addr;
dev_dbg(dev, "da %llx to pa %#x\n", da, *pa);
return 0;
}
dev_err(dev, "can't translate da %llx\n", da);
return -EINVAL;
}
static int stm32_rproc_get_loaded_rsc_table(struct platform_device *pdev,
struct rproc *rproc,
struct stm32_rproc *ddata)
{
struct device *dev = &pdev->dev;
phys_addr_t rsc_pa;
u32 rsc_da;
int err;
err = regmap_read(ddata->rsctbl.map, ddata->rsctbl.reg, &rsc_da);
if (err) {
dev_err(dev, "failed to read rsc tbl addr\n");
return err;
}
if (!rsc_da)
/* no rsc table */
return 0;
err = stm32_rproc_da_to_pa(pdev, ddata, rsc_da, &rsc_pa);
if (err)
return err;
ddata->rsc_va = devm_ioremap_wc(dev, rsc_pa, RSC_TBL_SIZE);
if (IS_ERR_OR_NULL(ddata->rsc_va)) {
dev_err(dev, "Unable to map memory region: %pa+%zx\n",
&rsc_pa, RSC_TBL_SIZE);
ddata->rsc_va = NULL;
return -ENOMEM;
}
/*
* The resource table is already loaded in device memory, no need
* to work with a cached table.
*/
rproc->cached_table = NULL;
/* Assuming the resource table fits in 1kB is fair */
rproc->table_sz = RSC_TBL_SIZE;
rproc->table_ptr = (struct resource_table *)ddata->rsc_va;
return 0;
}
static int stm32_rproc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
......@@ -797,18 +821,9 @@ static int stm32_rproc_probe(struct platform_device *pdev)
if (ret)
goto free_rproc;
if (state == M4_STATE_CRUN) {
if (state == M4_STATE_CRUN)
rproc->state = RPROC_DETACHED;
ret = stm32_rproc_parse_memory_regions(rproc);
if (ret)
goto free_resources;
ret = stm32_rproc_get_loaded_rsc_table(pdev, rproc, ddata);
if (ret)
goto free_resources;
}
rproc->has_iommu = false;
ddata->workqueue = create_workqueue(dev_name(dev));
if (!ddata->workqueue) {
......
......@@ -354,7 +354,7 @@ static int k3_dsp_rproc_stop(struct rproc *rproc)
* can be used either by the remoteproc core for loading (when using kernel
* remoteproc loader), or by any rpmsg bus drivers.
*/
static void *k3_dsp_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len)
static void *k3_dsp_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
{
struct k3_dsp_rproc *kproc = rproc->priv;
void __iomem *va = NULL;
......
......@@ -590,7 +590,7 @@ static int k3_r5_rproc_stop(struct rproc *rproc)
* present in a DSP or IPU device). The translated addresses can be used
* either by the remoteproc core for loading, or by any rpmsg bus drivers.
*/
static void *k3_r5_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len)
static void *k3_r5_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
{
struct k3_r5_rproc *kproc = rproc->priv;
struct k3_r5_core *core = kproc->core;
......
......@@ -89,7 +89,7 @@ static int wkup_m3_rproc_stop(struct rproc *rproc)
return error;
}
static void *wkup_m3_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len)
static void *wkup_m3_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
{
struct wkup_m3_rproc *wkupm3 = rproc->priv;
void *va = NULL;
......
......@@ -315,6 +315,7 @@ struct rproc;
/**
* struct rproc_mem_entry - memory entry descriptor
* @va: virtual address
* @is_iomem: io memory
* @dma: dma address
* @len: length, in bytes
* @da: device address
......@@ -329,6 +330,7 @@ struct rproc;
*/
struct rproc_mem_entry {
void *va;
bool is_iomem;
dma_addr_t dma;
size_t len;
u32 da;
......@@ -361,6 +363,7 @@ enum rsc_handling_status {
* @start: power on the device and boot it
* @stop: power off the device
* @attach: attach to a device that his already powered up
* @detach: detach from a device, leaving it powered up
* @kick: kick a virtqueue (virtqueue id given as a parameter)
* @da_to_va: optional platform hook to perform address translations
* @parse_fw: parse firmware to extract information (e.g. resource table)
......@@ -368,7 +371,9 @@ enum rsc_handling_status {
* RSC_HANDLED if resource was handled, RSC_IGNORED if not handled and a
* negative value on error
* @load_rsc_table: load resource table from firmware image
* @find_loaded_rsc_table: find the loaded resouce table
* @find_loaded_rsc_table: find the loaded resource table from firmware image
* @get_loaded_rsc_table: get resource table installed in memory
* by external entity
* @load: load firmware to memory, where the remote processor
* expects to find it
* @sanity_check: sanity check the fw image
......@@ -383,13 +388,16 @@ struct rproc_ops {
int (*start)(struct rproc *rproc);
int (*stop)(struct rproc *rproc);
int (*attach)(struct rproc *rproc);
int (*detach)(struct rproc *rproc);
void (*kick)(struct rproc *rproc, int vqid);
void * (*da_to_va)(struct rproc *rproc, u64 da, size_t len);
void * (*da_to_va)(struct rproc *rproc, u64 da, size_t len, bool *is_iomem);
int (*parse_fw)(struct rproc *rproc, const struct firmware *fw);
int (*handle_rsc)(struct rproc *rproc, u32 rsc_type, void *rsc,
int offset, int avail);
struct resource_table *(*find_loaded_rsc_table)(
struct rproc *rproc, const struct firmware *fw);
struct resource_table *(*get_loaded_rsc_table)(
struct rproc *rproc, size_t *size);
int (*load)(struct rproc *rproc, const struct firmware *fw);
int (*sanity_check)(struct rproc *rproc, const struct firmware *fw);
u64 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);
......@@ -405,6 +413,8 @@ struct rproc_ops {
* @RPROC_RUNNING: device is up and running
* @RPROC_CRASHED: device has crashed; need to start recovery
* @RPROC_DELETED: device is deleted
* @RPROC_ATTACHED: device has been booted by another entity and the core
* has attached to it
* @RPROC_DETACHED: device has been booted by another entity and waiting
* for the core to attach to it
* @RPROC_LAST: just keep this one at the end
......@@ -421,8 +431,9 @@ enum rproc_state {
RPROC_RUNNING = 2,
RPROC_CRASHED = 3,
RPROC_DELETED = 4,
RPROC_DETACHED = 5,
RPROC_LAST = 6,
RPROC_ATTACHED = 5,
RPROC_DETACHED = 6,
RPROC_LAST = 7,
};
/**
......@@ -505,11 +516,12 @@ struct rproc_dump_segment {
* @recovery_disabled: flag that state if recovery was disabled
* @max_notifyid: largest allocated notify id.
* @table_ptr: pointer to the resource table in effect
* @clean_table: copy of the resource table without modifications. Used
* when a remote processor is attached or detached from the core
* @cached_table: copy of the resource table
* @table_sz: size of @cached_table
* @has_iommu: flag to indicate if remote processor is behind an MMU
* @auto_boot: flag to indicate if remote processor should be auto-started
* @autonomous: true if an external entity has booted the remote processor
* @dump_segments: list of segments in the firmware
* @nb_vdev: number of vdev currently handled by rproc
* @char_dev: character device of the rproc
......@@ -542,11 +554,11 @@ struct rproc {
bool recovery_disabled;
int max_notifyid;
struct resource_table *table_ptr;
struct resource_table *clean_table;
struct resource_table *cached_table;
size_t table_sz;
bool has_iommu;
bool auto_boot;
bool autonomous;
struct list_head dump_segments;
int nb_vdev;
u8 elf_class;
......@@ -655,6 +667,7 @@ rproc_of_resm_mem_entry_init(struct device *dev, u32 of_resm_idx, size_t len,
int rproc_boot(struct rproc *rproc);
void rproc_shutdown(struct rproc *rproc);
int rproc_detach(struct rproc *rproc);
int rproc_set_firmware(struct rproc *rproc, const char *fw_name);
void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type);
void rproc_coredump_using_sections(struct rproc *rproc);
......
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