Commit 617e7481 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull remoteproc updates from Bjorn Andersson:
 "This introduces a new "detached" state for remote processors that are
  deemed to be running at the time Linux boots and the infrastructure
  for "attaching" to these. It then introduces the support for
  performing this operation for the STM32 platform.

  The coredump functionality is moved out from the core file and gains
  support for an optional mode where the recovery phase awaits the
  notification from devcoredump that the dump should be released. This
  allows userspace to grab the coredump in scenarios where vmalloc space
  is too low for creating a complete copy of the coredump before handing
  this to devcoredump.

  A new character device based interface is introduced to allow tying
  the stoppage of a remote processor to the termination of a user space
  process. This is useful in situations when such process provides
  crucial resources/operations for the firmware running on the remote
  processor.

  The Texas Instrument K3 driver gains support for the C66x and C71x
  DSPs.

  Qualcomm remoteprocs gains support for stashing relocation information
  in IMEM, to aid post mortem debugging and the crash notification
  mechanism is generalized to be reusable in cases where loosely coupled
  drivers needs to know about the status of a remote processor. One such
  example is the IPA hardware block, which is jointly owned with the
  modem and migrated to this improved interface.

  It also introduces a number of bug fixes and debug improvements for
  the Qualcomm modem remoteproc driver.

  And it cleans up the inconsistent interface for remoteproc drivers to
  implement power management"

* tag 'rproc-v5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/andersson/remoteproc: (56 commits)
  remoteproc: core: Register the character device interface
  remoteproc: Add remoteproc character device interface
  remoteproc: kill IPA notify code
  net: ipa: new notification infrastructure
  remoteproc: k3-dsp: Add support for C71x DSPs
  dt-bindings: remoteproc: k3-dsp: Update bindings for C71x DSPs
  remoteproc: k3-dsp: Add support for L2RAM loading on C66x DSPs
  remoteproc: k3-dsp: Add a remoteproc driver of K3 C66x DSPs
  dt-bindings: remoteproc: Add bindings for C66x DSPs on TI K3 SoCs
  remoteproc: k3: Add TI-SCI processor control helper functions
  remoteproc: Introduce rproc_of_parse_firmware() helper
  dt-bindings: arm: keystone: Add common TI SCI bindings
  remoteproc: qcom_q6v5_mss: Remove redundant running state
  remoteproc: qcom: q6v5: Update running state before requesting stop
  remoteproc: qcom_q6v5_mss: Add modem debug policy support
  remoteproc: qcom_q6v5_mss: Validate modem blob firmware size before load
  remoteproc: qcom_q6v5_mss: Validate MBA firmware size before load
  rpmsg: update documentation
  remoteproc: qcom_q6v5_mss: Add MBA log extraction support
  remoteproc: Add coredump debugfs entry
  ...
parents dded87af 62b8f9e9
# SPDX-License-Identifier: (GPL-2.0-only or BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/arm/keystone/ti,k3-sci-common.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Common K3 TI-SCI bindings
maintainers:
- Nishanth Menon <nm@ti.com>
description: |
The TI K3 family of SoCs usually have a central System Controller Processor
that is responsible for managing various SoC-level resources like clocks,
resets, interrupts etc. The communication with that processor is performed
through the TI-SCI protocol.
Each specific device management node like a clock controller node, a reset
controller node or an interrupt-controller node should define a common set
of properties that enables them to implement the corresponding functionality
over the TI-SCI protocol. The following are some of the common properties
needed by such individual nodes. The required properties for each device
management node is defined in the respective binding.
properties:
ti,sci:
$ref: /schemas/types.yaml#/definitions/phandle
description:
Should be a phandle to the TI-SCI System Controller node
ti,sci-dev-id:
$ref: /schemas/types.yaml#/definitions/uint32
description: |
Should contain the TI-SCI device id corresponding to the device. Please
refer to the corresponding System Controller documentation for valid
values for the desired device.
ti,sci-proc-ids:
description: Should contain a single tuple of <proc_id host_id>.
$ref: /schemas/types.yaml#/definitions/uint32-array
items:
- description: TI-SCI processor id for the remote processor device
- description: TI-SCI host id to which processor control ownership
should be transferred to
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/remoteproc/qcom,pil-info.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm peripheral image loader relocation info binding
maintainers:
- Bjorn Andersson <bjorn.andersson@linaro.org>
description:
The Qualcomm peripheral image loader relocation memory region, in IMEM, is
used for communicating remoteproc relocation information to post mortem
debugging tools.
properties:
compatible:
const: qcom,pil-reloc-info
reg:
maxItems: 1
required:
- compatible
- reg
examples:
- |
imem@146bf000 {
compatible = "syscon", "simple-mfd";
reg = <0x146bf000 0x1000>;
#address-cells = <1>;
#size-cells = <1>;
ranges = <0 0x146bf000 0x1000>;
pil-reloc@94c {
compatible = "qcom,pil-reloc-info";
reg = <0x94c 0xc8>;
};
};
...
# SPDX-License-Identifier: (GPL-2.0-only or BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/remoteproc/ti,k3-dsp-rproc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TI K3 DSP devices
maintainers:
- Suman Anna <s-anna@ti.com>
description: |
The TI K3 family of SoCs usually have one or more TI DSP Core sub-systems
that are used to offload some of the processor-intensive tasks or algorithms,
for achieving various system level goals.
These processor sub-systems usually contain additional sub-modules like
L1 and/or L2 caches/SRAMs, an Interrupt Controller, an external memory
controller, a dedicated local power/sleep controller etc. The DSP processor
cores in the K3 SoCs are usually either a TMS320C66x CorePac processor or a
TMS320C71x CorePac processor.
Each DSP Core sub-system is represented as a single DT node. Each node has a
number of required or optional properties that enable the OS running on the
host processor (Arm CorePac) to perform the device management of the remote
processor and to communicate with the remote processor.
allOf:
- $ref: /schemas/arm/keystone/ti,k3-sci-common.yaml#
properties:
compatible:
enum:
- ti,j721e-c66-dsp
- ti,j721e-c71-dsp
description:
Use "ti,j721e-c66-dsp" for C66x DSPs on K3 J721E SoCs
Use "ti,j721e-c71-dsp" for C71x DSPs on K3 J721E SoCs
resets:
description: |
Should contain the phandle to the reset controller node managing the
local resets for this device, and a reset specifier.
maxItems: 1
firmware-name:
description: |
Should contain the name of the default firmware image
file located on the firmware search path
mboxes:
description: |
OMAP Mailbox specifier denoting the sub-mailbox, to be used for
communication with the remote processor. This property should match
with the sub-mailbox node used in the firmware image.
maxItems: 1
memory-region:
minItems: 2
maxItems: 8
description: |
phandle to the reserved memory nodes to be associated with the remoteproc
device. There should be at least two reserved memory nodes defined. The
reserved memory nodes should be carveout nodes, and should be defined as
per the bindings in
Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
items:
- description: region used for dynamic DMA allocations like vrings and
vring buffers
- description: region reserved for firmware image sections
additionalItems: true
# Optional properties:
# --------------------
sram:
$ref: /schemas/types.yaml#/definitions/phandle-array
minItems: 1
maxItems: 4
description: |
phandles to one or more reserved on-chip SRAM regions. The regions
should be defined as child nodes of the respective SRAM node, and
should be defined as per the generic bindings in,
Documentation/devicetree/bindings/sram/sram.yaml
if:
properties:
compatible:
enum:
- ti,j721e-c66-dsp
then:
properties:
reg:
items:
- description: Address and Size of the L2 SRAM internal memory region
- description: Address and Size of the L1 PRAM internal memory region
- description: Address and Size of the L1 DRAM internal memory region
reg-names:
items:
- const: l2sram
- const: l1pram
- const: l1dram
else:
if:
properties:
compatible:
enum:
- ti,j721e-c71-dsp
then:
properties:
reg:
items:
- description: Address and Size of the L2 SRAM internal memory region
- description: Address and Size of the L1 DRAM internal memory region
reg-names:
items:
- const: l2sram
- const: l1dram
required:
- compatible
- reg
- reg-names
- ti,sci
- ti,sci-dev-id
- ti,sci-proc-ids
- resets
- firmware-name
- mboxes
- memory-region
unevaluatedProperties: false
examples:
- |
/ {
model = "Texas Instruments K3 J721E SoC";
compatible = "ti,j721e";
#address-cells = <2>;
#size-cells = <2>;
bus@100000 {
compatible = "simple-bus";
#address-cells = <2>;
#size-cells = <2>;
ranges = <0x00 0x00100000 0x00 0x00100000 0x00 0x00020000>, /* ctrl mmr */
<0x00 0x64800000 0x00 0x64800000 0x00 0x00800000>, /* C71_0 */
<0x4d 0x80800000 0x4d 0x80800000 0x00 0x00800000>, /* C66_0 */
<0x4d 0x81800000 0x4d 0x81800000 0x00 0x00800000>; /* C66_1 */
/* J721E C66_0 DSP node */
dsp@4d80800000 {
compatible = "ti,j721e-c66-dsp";
reg = <0x4d 0x80800000 0x00 0x00048000>,
<0x4d 0x80e00000 0x00 0x00008000>,
<0x4d 0x80f00000 0x00 0x00008000>;
reg-names = "l2sram", "l1pram", "l1dram";
ti,sci = <&dmsc>;
ti,sci-dev-id = <142>;
ti,sci-proc-ids = <0x03 0xFF>;
resets = <&k3_reset 142 1>;
firmware-name = "j7-c66_0-fw";
memory-region = <&c66_0_dma_memory_region>,
<&c66_0_memory_region>;
mboxes = <&mailbox0_cluster3 &mbox_c66_0>;
};
/* J721E C71_0 DSP node */
c71_0: dsp@64800000 {
compatible = "ti,j721e-c71-dsp";
reg = <0x00 0x64800000 0x00 0x00080000>,
<0x00 0x64e00000 0x00 0x0000c000>;
reg-names = "l2sram", "l1dram";
ti,sci = <&dmsc>;
ti,sci-dev-id = <15>;
ti,sci-proc-ids = <0x30 0xFF>;
resets = <&k3_reset 15 1>;
firmware-name = "j7-c71_0-fw";
memory-region = <&c71_0_dma_memory_region>,
<&c71_0_memory_region>;
mboxes = <&mailbox0_cluster4 &mbox_c71_0>;
};
};
};
......@@ -192,9 +192,9 @@ Returns 0 on success and an appropriate error value on failure.
::
struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *rpdev,
void (*cb)(struct rpmsg_channel *, void *, int, void *, u32),
void *priv, u32 addr);
struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev,
rpmsg_rx_cb_t cb, void *priv,
struct rpmsg_channel_info chinfo);
every rpmsg address in the system is bound to an rx callback (so when
inbound messages arrive, they are dispatched by the rpmsg bus using the
......
......@@ -339,6 +339,7 @@ Code Seq# Include File Comments
0xB4 00-0F linux/gpio.h <mailto:linux-gpio@vger.kernel.org>
0xB5 00-0F uapi/linux/rpmsg.h <mailto:linux-remoteproc@vger.kernel.org>
0xB6 all linux/fpga-dfl.h
0xB7 all uapi/linux/remoteproc_cdev.h <mailto:linux-remoteproc@vger.kernel.org>
0xC0 00-0F linux/usb/iowarrior.h
0xCA 00-0F uapi/misc/cxl.h
0xCA 10-2F uapi/misc/ocxl.h
......
......@@ -17065,6 +17065,7 @@ M: Tero Kristo <t-kristo@ti.com>
M: Santosh Shilimkar <ssantosh@kernel.org>
L: linux-arm-kernel@lists.infradead.org
S: Maintained
F: Documentation/devicetree/bindings/arm/keystone/ti,k3-sci-common.yaml
F: Documentation/devicetree/bindings/arm/keystone/ti,sci.txt
F: Documentation/devicetree/bindings/clock/ti,sci-clk.txt
F: Documentation/devicetree/bindings/interrupt-controller/ti,sci-inta.txt
......
......@@ -10,6 +10,7 @@
#include <linux/device.h>
#include <linux/notifier.h>
#include <linux/pm_wakeup.h>
#include <linux/notifier.h>
#include "ipa_version.h"
#include "gsi.h"
......@@ -73,6 +74,8 @@ struct ipa {
enum ipa_version version;
struct platform_device *pdev;
struct rproc *modem_rproc;
struct notifier_block nb;
void *notifier;
struct ipa_smp2p *smp2p;
struct ipa_clock *clock;
atomic_t suspend_ref;
......
......@@ -9,7 +9,7 @@
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/if_rmnet.h>
#include <linux/remoteproc/qcom_q6v5_ipa_notify.h>
#include <linux/remoteproc/qcom_rproc.h>
#include "ipa.h"
#include "ipa_data.h"
......@@ -311,43 +311,40 @@ static void ipa_modem_crashed(struct ipa *ipa)
dev_err(dev, "error %d zeroing modem memory regions\n", ret);
}
static void ipa_modem_notify(void *data, enum qcom_rproc_event event)
static int ipa_modem_notify(struct notifier_block *nb, unsigned long action,
void *data)
{
struct ipa *ipa = data;
struct device *dev;
struct ipa *ipa = container_of(nb, struct ipa, nb);
struct qcom_ssr_notify_data *notify_data = data;
struct device *dev = &ipa->pdev->dev;
dev = &ipa->pdev->dev;
switch (event) {
case MODEM_STARTING:
switch (action) {
case QCOM_SSR_BEFORE_POWERUP:
dev_info(dev, "received modem starting event\n");
ipa_smp2p_notify_reset(ipa);
break;
case MODEM_RUNNING:
case QCOM_SSR_AFTER_POWERUP:
dev_info(dev, "received modem running event\n");
break;
case MODEM_STOPPING:
case MODEM_CRASHED:
case QCOM_SSR_BEFORE_SHUTDOWN:
dev_info(dev, "received modem %s event\n",
event == MODEM_STOPPING ? "stopping"
: "crashed");
notify_data->crashed ? "crashed" : "stopping");
if (ipa->setup_complete)
ipa_modem_crashed(ipa);
break;
case MODEM_OFFLINE:
case QCOM_SSR_AFTER_SHUTDOWN:
dev_info(dev, "received modem offline event\n");
break;
case MODEM_REMOVING:
dev_info(dev, "received modem stopping event\n");
break;
default:
dev_err(&ipa->pdev->dev, "unrecognized event %u\n", event);
dev_err(dev, "received unrecognized event %lu\n", action);
break;
}
return NOTIFY_OK;
}
int ipa_modem_init(struct ipa *ipa, bool modem_init)
......@@ -362,13 +359,30 @@ void ipa_modem_exit(struct ipa *ipa)
int ipa_modem_config(struct ipa *ipa)
{
return qcom_register_ipa_notify(ipa->modem_rproc, ipa_modem_notify,
ipa);
void *notifier;
ipa->nb.notifier_call = ipa_modem_notify;
notifier = qcom_register_ssr_notifier("mpss", &ipa->nb);
if (IS_ERR(notifier))
return PTR_ERR(notifier);
ipa->notifier = notifier;
return 0;
}
void ipa_modem_deconfig(struct ipa *ipa)
{
qcom_deregister_ipa_notify(ipa->modem_rproc);
struct device *dev = &ipa->pdev->dev;
int ret;
ret = qcom_unregister_ssr_notifier(ipa->notifier, &ipa->nb);
if (ret)
dev_err(dev, "error %d unregistering notifier", ret);
ipa->notifier = NULL;
memset(&ipa->nb, 0, sizeof(ipa->nb));
}
int ipa_modem_setup(struct ipa *ipa)
......
......@@ -14,6 +14,15 @@ config REMOTEPROC
if REMOTEPROC
config REMOTEPROC_CDEV
bool "Remoteproc character device interface"
help
Say y here to have a character device interface for the remoteproc
framework. Userspace can boot/shutdown remote processors through
this interface.
It's safe to say N if you don't want to use this interface.
config IMX_REMOTEPROC
tristate "IMX6/7 remoteproc support"
depends on ARCH_MXC
......@@ -116,6 +125,9 @@ config KEYSTONE_REMOTEPROC
It's safe to say N here if you're not interested in the Keystone
DSPs or just want to use a bare minimum kernel.
config QCOM_PIL_INFO
tristate
config QCOM_RPROC_COMMON
tristate
......@@ -132,6 +144,7 @@ config QCOM_Q6V5_ADSP
depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n
depends on QCOM_SYSMON || QCOM_SYSMON=n
select MFD_SYSCON
select QCOM_PIL_INFO
select QCOM_MDT_LOADER
select QCOM_Q6V5_COMMON
select QCOM_RPROC_COMMON
......@@ -148,8 +161,8 @@ config QCOM_Q6V5_MSS
depends on QCOM_SYSMON || QCOM_SYSMON=n
select MFD_SYSCON
select QCOM_MDT_LOADER
select QCOM_PIL_INFO
select QCOM_Q6V5_COMMON
select QCOM_Q6V5_IPA_NOTIFY
select QCOM_RPROC_COMMON
select QCOM_SCM
help
......@@ -164,6 +177,7 @@ config QCOM_Q6V5_PAS
depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n
depends on QCOM_SYSMON || QCOM_SYSMON=n
select MFD_SYSCON
select QCOM_PIL_INFO
select QCOM_MDT_LOADER
select QCOM_Q6V5_COMMON
select QCOM_RPROC_COMMON
......@@ -182,6 +196,7 @@ config QCOM_Q6V5_WCSS
depends on QCOM_SYSMON || QCOM_SYSMON=n
select MFD_SYSCON
select QCOM_MDT_LOADER
select QCOM_PIL_INFO
select QCOM_Q6V5_COMMON
select QCOM_RPROC_COMMON
select QCOM_SCM
......@@ -189,9 +204,6 @@ config QCOM_Q6V5_WCSS
Say y here to support the Qualcomm Peripheral Image Loader for the
Hexagon V5 based WCSS remote processors.
config QCOM_Q6V5_IPA_NOTIFY
tristate
config QCOM_SYSMON
tristate "Qualcomm sysmon driver"
depends on RPMSG
......@@ -215,6 +227,7 @@ config QCOM_WCNSS_PIL
depends on QCOM_SMEM
depends on QCOM_SYSMON || QCOM_SYSMON=n
select QCOM_MDT_LOADER
select QCOM_PIL_INFO
select QCOM_RPROC_COMMON
select QCOM_SCM
help
......@@ -249,6 +262,19 @@ config STM32_RPROC
This can be either built-in or a loadable module.
config TI_K3_DSP_REMOTEPROC
tristate "TI K3 DSP remoteproc support"
depends on ARCH_K3
select MAILBOX
select OMAP2PLUS_MBOX
help
Say m here to support TI's C66x and C71x DSP remote processor
subsystems on various TI K3 family of SoCs through the remote
processor framework.
It's safe to say N here if you're not interested in utilizing
the DSP slave processors.
endif # REMOTEPROC
endmenu
......@@ -5,10 +5,12 @@
obj-$(CONFIG_REMOTEPROC) += remoteproc.o
remoteproc-y := remoteproc_core.o
remoteproc-y += remoteproc_coredump.o
remoteproc-y += remoteproc_debugfs.o
remoteproc-y += remoteproc_sysfs.o
remoteproc-y += remoteproc_virtio.o
remoteproc-y += remoteproc_elf_loader.o
obj-$(CONFIG_REMOTEPROC_CDEV) += remoteproc_cdev.o
obj-$(CONFIG_IMX_REMOTEPROC) += imx_rproc.o
obj-$(CONFIG_INGENIC_VPU_RPROC) += ingenic_rproc.o
obj-$(CONFIG_MTK_SCP) += mtk_scp.o mtk_scp_ipi.o
......@@ -16,13 +18,13 @@ obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o
obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o
obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o
obj-$(CONFIG_KEYSTONE_REMOTEPROC) += keystone_remoteproc.o
obj-$(CONFIG_QCOM_PIL_INFO) += qcom_pil_info.o
obj-$(CONFIG_QCOM_RPROC_COMMON) += qcom_common.o
obj-$(CONFIG_QCOM_Q6V5_COMMON) += qcom_q6v5.o
obj-$(CONFIG_QCOM_Q6V5_ADSP) += qcom_q6v5_adsp.o
obj-$(CONFIG_QCOM_Q6V5_MSS) += qcom_q6v5_mss.o
obj-$(CONFIG_QCOM_Q6V5_PAS) += qcom_q6v5_pas.o
obj-$(CONFIG_QCOM_Q6V5_WCSS) += qcom_q6v5_wcss.o
obj-$(CONFIG_QCOM_Q6V5_IPA_NOTIFY) += qcom_q6v5_ipa_notify.o
obj-$(CONFIG_QCOM_SYSMON) += qcom_sysmon.o
obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss_pil.o
qcom_wcnss_pil-y += qcom_wcnss.o
......@@ -30,3 +32,4 @@ qcom_wcnss_pil-y += qcom_wcnss_iris.o
obj-$(CONFIG_ST_REMOTEPROC) += st_remoteproc.o
obj-$(CONFIG_ST_SLIM_REMOTEPROC) += st_slim_rproc.o
obj-$(CONFIG_STM32_RPROC) += stm32_rproc.o
obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o
......@@ -11,7 +11,6 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/remoteproc.h>
#include "remoteproc_internal.h"
......@@ -62,6 +61,28 @@ struct vpu {
struct device *dev;
};
static int ingenic_rproc_prepare(struct rproc *rproc)
{
struct vpu *vpu = rproc->priv;
int ret;
/* The clocks must be enabled for the firmware to be loaded in TCSM */
ret = clk_bulk_prepare_enable(ARRAY_SIZE(vpu->clks), vpu->clks);
if (ret)
dev_err(vpu->dev, "Unable to start clocks: %d\n", ret);
return ret;
}
static int ingenic_rproc_unprepare(struct rproc *rproc)
{
struct vpu *vpu = rproc->priv;
clk_bulk_disable_unprepare(ARRAY_SIZE(vpu->clks), vpu->clks);
return 0;
}
static int ingenic_rproc_start(struct rproc *rproc)
{
struct vpu *vpu = rproc->priv;
......@@ -115,6 +136,8 @@ static void *ingenic_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len)
}
static struct rproc_ops ingenic_rproc_ops = {
.prepare = ingenic_rproc_prepare,
.unprepare = ingenic_rproc_unprepare,
.start = ingenic_rproc_start,
.stop = ingenic_rproc_stop,
.kick = ingenic_rproc_kick,
......@@ -135,16 +158,6 @@ static irqreturn_t vpu_interrupt(int irq, void *data)
return rproc_vq_interrupt(rproc, vring);
}
static void ingenic_rproc_disable_clks(void *data)
{
struct vpu *vpu = data;
pm_runtime_resume(vpu->dev);
pm_runtime_disable(vpu->dev);
clk_bulk_disable_unprepare(ARRAY_SIZE(vpu->clks), vpu->clks);
}
static int ingenic_rproc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
......@@ -206,35 +219,13 @@ static int ingenic_rproc_probe(struct platform_device *pdev)
disable_irq(vpu->irq);
/* The clocks must be enabled for the firmware to be loaded in TCSM */
ret = clk_bulk_prepare_enable(ARRAY_SIZE(vpu->clks), vpu->clks);
if (ret) {
dev_err(dev, "Unable to start clocks\n");
return ret;
}
pm_runtime_irq_safe(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
pm_runtime_use_autosuspend(dev);
ret = devm_add_action_or_reset(dev, ingenic_rproc_disable_clks, vpu);
if (ret) {
dev_err(dev, "Unable to register action\n");
goto out_pm_put;
}
ret = devm_rproc_add(dev, rproc);
if (ret) {
dev_err(dev, "Failed to register remote processor\n");
goto out_pm_put;
return ret;
}
out_pm_put:
pm_runtime_put_autosuspend(dev);
return ret;
return 0;
}
static const struct of_device_id ingenic_rproc_of_matches[] = {
......@@ -243,33 +234,10 @@ static const struct of_device_id ingenic_rproc_of_matches[] = {
};
MODULE_DEVICE_TABLE(of, ingenic_rproc_of_matches);
static int __maybe_unused ingenic_rproc_suspend(struct device *dev)
{
struct vpu *vpu = dev_get_drvdata(dev);
clk_bulk_disable(ARRAY_SIZE(vpu->clks), vpu->clks);
return 0;
}
static int __maybe_unused ingenic_rproc_resume(struct device *dev)
{
struct vpu *vpu = dev_get_drvdata(dev);
return clk_bulk_enable(ARRAY_SIZE(vpu->clks), vpu->clks);
}
static const struct dev_pm_ops __maybe_unused ingenic_rproc_pm = {
SET_RUNTIME_PM_OPS(ingenic_rproc_suspend, ingenic_rproc_resume, NULL)
};
static struct platform_driver ingenic_rproc_driver = {
.probe = ingenic_rproc_probe,
.driver = {
.name = "ingenic-vpu",
#ifdef CONFIG_PM
.pm = &ingenic_rproc_pm,
#endif
.of_match_table = ingenic_rproc_of_matches,
},
};
......
......@@ -12,8 +12,10 @@
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/remoteproc.h>
#include <linux/remoteproc/qcom_rproc.h>
#include <linux/rpmsg/qcom_glink.h>
#include <linux/rpmsg/qcom_smd.h>
#include <linux/slab.h>
#include <linux/soc/qcom/mdt_loader.h>
#include "remoteproc_internal.h"
......@@ -23,7 +25,14 @@
#define to_smd_subdev(d) container_of(d, struct qcom_rproc_subdev, subdev)
#define to_ssr_subdev(d) container_of(d, struct qcom_rproc_ssr, subdev)
static BLOCKING_NOTIFIER_HEAD(ssr_notifiers);
struct qcom_ssr_subsystem {
const char *name;
struct srcu_notifier_head notifier_list;
struct list_head list;
};
static LIST_HEAD(qcom_ssr_subsystem_list);
static DEFINE_MUTEX(qcom_ssr_subsys_lock);
static int glink_subdev_start(struct rproc_subdev *subdev)
{
......@@ -189,37 +198,122 @@ void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd)
}
EXPORT_SYMBOL_GPL(qcom_remove_smd_subdev);
static struct qcom_ssr_subsystem *qcom_ssr_get_subsys(const char *name)
{
struct qcom_ssr_subsystem *info;
mutex_lock(&qcom_ssr_subsys_lock);
/* Match in the global qcom_ssr_subsystem_list with name */
list_for_each_entry(info, &qcom_ssr_subsystem_list, list)
if (!strcmp(info->name, name))
goto out;
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
info = ERR_PTR(-ENOMEM);
goto out;
}
info->name = kstrdup_const(name, GFP_KERNEL);
srcu_init_notifier_head(&info->notifier_list);
/* Add to global notification list */
list_add_tail(&info->list, &qcom_ssr_subsystem_list);
out:
mutex_unlock(&qcom_ssr_subsys_lock);
return info;
}
/**
* qcom_register_ssr_notifier() - register SSR notification handler
* @nb: notifier_block to notify for restart notifications
* @name: Subsystem's SSR name
* @nb: notifier_block to be invoked upon subsystem's state change
*
* Returns 0 on success, negative errno on failure.
* This registers the @nb notifier block as part the notifier chain for a
* remoteproc associated with @name. The notifier block's callback
* will be invoked when the remote processor's SSR events occur
* (pre/post startup and pre/post shutdown).
*
* This register the @notify function as handler for restart notifications. As
* remote processors are stopped this function will be called, with the SSR
* name passed as a parameter.
* Return: a subsystem cookie on success, ERR_PTR on failure.
*/
int qcom_register_ssr_notifier(struct notifier_block *nb)
void *qcom_register_ssr_notifier(const char *name, struct notifier_block *nb)
{
return blocking_notifier_chain_register(&ssr_notifiers, nb);
struct qcom_ssr_subsystem *info;
info = qcom_ssr_get_subsys(name);
if (IS_ERR(info))
return info;
srcu_notifier_chain_register(&info->notifier_list, nb);
return &info->notifier_list;
}
EXPORT_SYMBOL_GPL(qcom_register_ssr_notifier);
/**
* qcom_unregister_ssr_notifier() - unregister SSR notification handler
* @notify: subsystem cookie returned from qcom_register_ssr_notifier
* @nb: notifier_block to unregister
*
* This function will unregister the notifier from the particular notifier
* chain.
*
* Return: 0 on success, %ENOENT otherwise.
*/
void qcom_unregister_ssr_notifier(struct notifier_block *nb)
int qcom_unregister_ssr_notifier(void *notify, struct notifier_block *nb)
{
blocking_notifier_chain_unregister(&ssr_notifiers, nb);
return srcu_notifier_chain_unregister(notify, nb);
}
EXPORT_SYMBOL_GPL(qcom_unregister_ssr_notifier);
static int ssr_notify_prepare(struct rproc_subdev *subdev)
{
struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
struct qcom_ssr_notify_data data = {
.name = ssr->info->name,
.crashed = false,
};
srcu_notifier_call_chain(&ssr->info->notifier_list,
QCOM_SSR_BEFORE_POWERUP, &data);
return 0;
}
static int ssr_notify_start(struct rproc_subdev *subdev)
{
struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
struct qcom_ssr_notify_data data = {
.name = ssr->info->name,
.crashed = false,
};
srcu_notifier_call_chain(&ssr->info->notifier_list,
QCOM_SSR_AFTER_POWERUP, &data);
return 0;
}
static void ssr_notify_stop(struct rproc_subdev *subdev, bool crashed)
{
struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
struct qcom_ssr_notify_data data = {
.name = ssr->info->name,
.crashed = crashed,
};
srcu_notifier_call_chain(&ssr->info->notifier_list,
QCOM_SSR_BEFORE_SHUTDOWN, &data);
}
static void ssr_notify_unprepare(struct rproc_subdev *subdev)
{
struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
struct qcom_ssr_notify_data data = {
.name = ssr->info->name,
.crashed = false,
};
blocking_notifier_call_chain(&ssr_notifiers, 0, (void *)ssr->name);
srcu_notifier_call_chain(&ssr->info->notifier_list,
QCOM_SSR_AFTER_SHUTDOWN, &data);
}
/**
......@@ -229,12 +323,24 @@ static void ssr_notify_unprepare(struct rproc_subdev *subdev)
* @ssr_name: identifier to use for notifications originating from @rproc
*
* As the @ssr is registered with the @rproc SSR events will be sent to all
* registered listeners in the system as the remoteproc is shut down.
* registered listeners for the remoteproc when it's SSR events occur
* (pre/post startup and pre/post shutdown).
*/
void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr,
const char *ssr_name)
{
ssr->name = ssr_name;
struct qcom_ssr_subsystem *info;
info = qcom_ssr_get_subsys(ssr_name);
if (IS_ERR(info)) {
dev_err(&rproc->dev, "Failed to add ssr subdevice\n");
return;
}
ssr->info = info;
ssr->subdev.prepare = ssr_notify_prepare;
ssr->subdev.start = ssr_notify_start;
ssr->subdev.stop = ssr_notify_stop;
ssr->subdev.unprepare = ssr_notify_unprepare;
rproc_add_subdev(rproc, &ssr->subdev);
......@@ -249,6 +355,7 @@ EXPORT_SYMBOL_GPL(qcom_add_ssr_subdev);
void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr)
{
rproc_remove_subdev(rproc, &ssr->subdev);
ssr->info = NULL;
}
EXPORT_SYMBOL_GPL(qcom_remove_ssr_subdev);
......
......@@ -26,10 +26,11 @@ struct qcom_rproc_subdev {
struct qcom_smd_edge *edge;
};
struct qcom_ssr_subsystem;
struct qcom_rproc_ssr {
struct rproc_subdev subdev;
const char *name;
struct qcom_ssr_subsystem *info;
};
void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink,
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2019-2020 Linaro Ltd.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_address.h>
#include "qcom_pil_info.h"
/*
* The PIL relocation information region is used to communicate memory regions
* occupied by co-processor firmware for post mortem crash analysis.
*
* It consists of an array of entries with an 8 byte textual identifier of the
* region followed by a 64 bit base address and 32 bit size, both little
* endian.
*/
#define PIL_RELOC_NAME_LEN 8
#define PIL_RELOC_ENTRY_SIZE (PIL_RELOC_NAME_LEN + sizeof(__le64) + sizeof(__le32))
struct pil_reloc {
void __iomem *base;
size_t num_entries;
};
static struct pil_reloc _reloc __read_mostly;
static DEFINE_MUTEX(pil_reloc_lock);
static int qcom_pil_info_init(void)
{
struct device_node *np;
struct resource imem;
void __iomem *base;
int ret;
/* Already initialized? */
if (_reloc.base)
return 0;
np = of_find_compatible_node(NULL, NULL, "qcom,pil-reloc-info");
if (!np)
return -ENOENT;
ret = of_address_to_resource(np, 0, &imem);
of_node_put(np);
if (ret < 0)
return ret;
base = ioremap(imem.start, resource_size(&imem));
if (!base) {
pr_err("failed to map PIL relocation info region\n");
return -ENOMEM;
}
memset_io(base, 0, resource_size(&imem));
_reloc.base = base;
_reloc.num_entries = resource_size(&imem) / PIL_RELOC_ENTRY_SIZE;
return 0;
}
/**
* qcom_pil_info_store() - store PIL information of image in IMEM
* @image: name of the image
* @base: base address of the loaded image
* @size: size of the loaded image
*
* Return: 0 on success, negative errno on failure
*/
int qcom_pil_info_store(const char *image, phys_addr_t base, size_t size)
{
char buf[PIL_RELOC_NAME_LEN];
void __iomem *entry;
int ret;
int i;
mutex_lock(&pil_reloc_lock);
ret = qcom_pil_info_init();
if (ret < 0) {
mutex_unlock(&pil_reloc_lock);
return ret;
}
for (i = 0; i < _reloc.num_entries; i++) {
entry = _reloc.base + i * PIL_RELOC_ENTRY_SIZE;
memcpy_fromio(buf, entry, PIL_RELOC_NAME_LEN);
/*
* An empty record means we didn't find it, given that the
* records are packed.
*/
if (!buf[0])
goto found_unused;
if (!strncmp(buf, image, PIL_RELOC_NAME_LEN))
goto found_existing;
}
pr_warn("insufficient PIL info slots\n");
mutex_unlock(&pil_reloc_lock);
return -ENOMEM;
found_unused:
memcpy_toio(entry, image, PIL_RELOC_NAME_LEN);
found_existing:
/* Use two writel() as base is only aligned to 4 bytes on odd entries */
writel(base, entry + PIL_RELOC_NAME_LEN);
writel((u64)base >> 32, entry + PIL_RELOC_NAME_LEN + 4);
writel(size, entry + PIL_RELOC_NAME_LEN + sizeof(__le64));
mutex_unlock(&pil_reloc_lock);
return 0;
}
EXPORT_SYMBOL_GPL(qcom_pil_info_store);
static void __exit pil_reloc_exit(void)
{
mutex_lock(&pil_reloc_lock);
iounmap(_reloc.base);
_reloc.base = NULL;
mutex_unlock(&pil_reloc_lock);
}
module_exit(pil_reloc_exit);
MODULE_DESCRIPTION("Qualcomm PIL relocation info");
MODULE_LICENSE("GPL v2");
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __QCOM_PIL_INFO_H__
#define __QCOM_PIL_INFO_H__
#include <linux/types.h>
int qcom_pil_info_store(const char *image, phys_addr_t base, size_t size);
#endif
......@@ -153,6 +153,8 @@ 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));
......
......@@ -26,6 +26,7 @@
#include <linux/soc/qcom/smem_state.h>
#include "qcom_common.h"
#include "qcom_pil_info.h"
#include "qcom_q6v5.h"
#include "remoteproc_internal.h"
......@@ -82,6 +83,7 @@ struct qcom_adsp {
unsigned int halt_lpass;
int crash_reason_smem;
const char *info_name;
struct completion start_done;
struct completion stop_done;
......@@ -164,10 +166,17 @@ static int qcom_adsp_shutdown(struct qcom_adsp *adsp)
static int adsp_load(struct rproc *rproc, const struct firmware *fw)
{
struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
int ret;
ret = qcom_mdt_load_no_init(adsp->dev, fw, rproc->firmware, 0,
adsp->mem_region, adsp->mem_phys,
adsp->mem_size, &adsp->mem_reloc);
if (ret)
return ret;
qcom_pil_info_store(adsp->info_name, adsp->mem_phys, adsp->mem_size);
return qcom_mdt_load_no_init(adsp->dev, fw, rproc->firmware, 0,
adsp->mem_region, adsp->mem_phys, adsp->mem_size,
&adsp->mem_reloc);
return 0;
}
static int adsp_start(struct rproc *rproc)
......@@ -436,6 +445,7 @@ static int adsp_probe(struct platform_device *pdev)
adsp = (struct qcom_adsp *)rproc->priv;
adsp->dev = &pdev->dev;
adsp->rproc = rproc;
adsp->info_name = desc->sysmon_name;
platform_set_drvdata(pdev, adsp);
ret = adsp_alloc_memory_region(adsp);
......
// SPDX-License-Identifier: GPL-2.0
/*
* Qualcomm IPA notification subdev support
*
* Copyright (C) 2019 Linaro Ltd.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/remoteproc.h>
#include <linux/remoteproc/qcom_q6v5_ipa_notify.h>
static void
ipa_notify_common(struct rproc_subdev *subdev, enum qcom_rproc_event event)
{
struct qcom_rproc_ipa_notify *ipa_notify;
qcom_ipa_notify_t notify;
ipa_notify = container_of(subdev, struct qcom_rproc_ipa_notify, subdev);
notify = ipa_notify->notify;
if (notify)
notify(ipa_notify->data, event);
}
static int ipa_notify_prepare(struct rproc_subdev *subdev)
{
ipa_notify_common(subdev, MODEM_STARTING);
return 0;
}
static int ipa_notify_start(struct rproc_subdev *subdev)
{
ipa_notify_common(subdev, MODEM_RUNNING);
return 0;
}
static void ipa_notify_stop(struct rproc_subdev *subdev, bool crashed)
{
ipa_notify_common(subdev, crashed ? MODEM_CRASHED : MODEM_STOPPING);
}
static void ipa_notify_unprepare(struct rproc_subdev *subdev)
{
ipa_notify_common(subdev, MODEM_OFFLINE);
}
static void ipa_notify_removing(struct rproc_subdev *subdev)
{
ipa_notify_common(subdev, MODEM_REMOVING);
}
/* Register the IPA notification subdevice with the Q6V5 MSS remoteproc */
void qcom_add_ipa_notify_subdev(struct rproc *rproc,
struct qcom_rproc_ipa_notify *ipa_notify)
{
ipa_notify->notify = NULL;
ipa_notify->data = NULL;
ipa_notify->subdev.prepare = ipa_notify_prepare;
ipa_notify->subdev.start = ipa_notify_start;
ipa_notify->subdev.stop = ipa_notify_stop;
ipa_notify->subdev.unprepare = ipa_notify_unprepare;
rproc_add_subdev(rproc, &ipa_notify->subdev);
}
EXPORT_SYMBOL_GPL(qcom_add_ipa_notify_subdev);
/* Remove the IPA notification subdevice */
void qcom_remove_ipa_notify_subdev(struct rproc *rproc,
struct qcom_rproc_ipa_notify *ipa_notify)
{
struct rproc_subdev *subdev = &ipa_notify->subdev;
ipa_notify_removing(subdev);
rproc_remove_subdev(rproc, subdev);
ipa_notify->notify = NULL; /* Make it obvious */
}
EXPORT_SYMBOL_GPL(qcom_remove_ipa_notify_subdev);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Qualcomm IPA notification remoteproc subdev");
This diff is collapsed.
......@@ -25,6 +25,7 @@
#include <linux/soc/qcom/smem_state.h>
#include "qcom_common.h"
#include "qcom_pil_info.h"
#include "qcom_q6v5.h"
#include "remoteproc_internal.h"
......@@ -64,6 +65,7 @@ struct qcom_adsp {
int pas_id;
int crash_reason_smem;
bool has_aggre2_clk;
const char *info_name;
struct completion start_done;
struct completion stop_done;
......@@ -117,11 +119,17 @@ static void adsp_pds_disable(struct qcom_adsp *adsp, struct device **pds,
static int adsp_load(struct rproc *rproc, const struct firmware *fw)
{
struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
int ret;
return qcom_mdt_load(adsp->dev, fw, rproc->firmware, adsp->pas_id,
adsp->mem_region, adsp->mem_phys, adsp->mem_size,
&adsp->mem_reloc);
ret = qcom_mdt_load(adsp->dev, fw, rproc->firmware, adsp->pas_id,
adsp->mem_region, adsp->mem_phys, adsp->mem_size,
&adsp->mem_reloc);
if (ret)
return ret;
qcom_pil_info_store(adsp->info_name, adsp->mem_phys, adsp->mem_size);
return 0;
}
static int adsp_start(struct rproc *rproc)
......@@ -405,6 +413,7 @@ static int adsp_probe(struct platform_device *pdev)
adsp->rproc = rproc;
adsp->pas_id = desc->pas_id;
adsp->has_aggre2_clk = desc->has_aggre2_clk;
adsp->info_name = desc->sysmon_name;
platform_set_drvdata(pdev, adsp);
device_wakeup_enable(adsp->dev);
......
......@@ -14,6 +14,7 @@
#include <linux/reset.h>
#include <linux/soc/qcom/mdt_loader.h>
#include "qcom_common.h"
#include "qcom_pil_info.h"
#include "qcom_q6v5.h"
#define WCSS_CRASH_REASON 421
......@@ -424,10 +425,17 @@ static void *q6v5_wcss_da_to_va(struct rproc *rproc, u64 da, size_t len)
static int q6v5_wcss_load(struct rproc *rproc, const struct firmware *fw)
{
struct q6v5_wcss *wcss = rproc->priv;
int ret;
ret = qcom_mdt_load_no_init(wcss->dev, fw, rproc->firmware,
0, wcss->mem_region, wcss->mem_phys,
wcss->mem_size, &wcss->mem_reloc);
if (ret)
return ret;
qcom_pil_info_store("wcnss", wcss->mem_phys, wcss->mem_size);
return qcom_mdt_load_no_init(wcss->dev, fw, rproc->firmware,
0, wcss->mem_region, wcss->mem_phys,
wcss->mem_size, &wcss->mem_reloc);
return ret;
}
static const struct rproc_ops q6v5_wcss_ops = {
......
......@@ -71,7 +71,7 @@ static LIST_HEAD(sysmon_list);
/**
* sysmon_send_event() - send notification of other remote's SSR event
* @sysmon: sysmon context
* @name: other remote's name
* @event: sysmon event context
*/
static void sysmon_send_event(struct qcom_sysmon *sysmon,
const struct sysmon_event *event)
......@@ -343,7 +343,7 @@ static void ssctl_request_shutdown(struct qcom_sysmon *sysmon)
/**
* ssctl_send_event() - send notification of other remote's SSR event
* @sysmon: sysmon context
* @name: other remote's name
* @event: sysmon event context
*/
static void ssctl_send_event(struct qcom_sysmon *sysmon,
const struct sysmon_event *event)
......
......@@ -27,6 +27,7 @@
#include "qcom_common.h"
#include "remoteproc_internal.h"
#include "qcom_pil_info.h"
#include "qcom_wcnss.h"
#define WCNSS_CRASH_REASON_SMEM 422
......@@ -145,10 +146,17 @@ void qcom_wcnss_assign_iris(struct qcom_wcnss *wcnss,
static int wcnss_load(struct rproc *rproc, const struct firmware *fw)
{
struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv;
int ret;
ret = qcom_mdt_load(wcnss->dev, fw, rproc->firmware, WCNSS_PAS_ID,
wcnss->mem_region, wcnss->mem_phys,
wcnss->mem_size, &wcnss->mem_reloc);
if (ret)
return ret;
qcom_pil_info_store("wcnss", wcnss->mem_phys, wcnss->mem_size);
return qcom_mdt_load(wcnss->dev, fw, rproc->firmware, WCNSS_PAS_ID,
wcnss->mem_region, wcnss->mem_phys,
wcnss->mem_size, &wcnss->mem_reloc);
return 0;
}
static void wcnss_indicate_nv_download(struct qcom_wcnss *wcnss)
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Character device interface driver for Remoteproc framework.
*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*/
#include <linux/cdev.h>
#include <linux/compat.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/remoteproc.h>
#include <linux/uaccess.h>
#include <uapi/linux/remoteproc_cdev.h>
#include "remoteproc_internal.h"
#define NUM_RPROC_DEVICES 64
static dev_t rproc_major;
static ssize_t rproc_cdev_write(struct file *filp, const char __user *buf, size_t len, loff_t *pos)
{
struct rproc *rproc = container_of(filp->f_inode->i_cdev, struct rproc, cdev);
int ret = 0;
char cmd[10];
if (!len || len > sizeof(cmd))
return -EINVAL;
ret = copy_from_user(cmd, buf, len);
if (ret)
return -EFAULT;
if (!strncmp(cmd, "start", len)) {
if (rproc->state == RPROC_RUNNING)
return -EBUSY;
ret = rproc_boot(rproc);
} else if (!strncmp(cmd, "stop", len)) {
if (rproc->state != RPROC_RUNNING)
return -EINVAL;
rproc_shutdown(rproc);
} else {
dev_err(&rproc->dev, "Unrecognized option\n");
ret = -EINVAL;
}
return ret ? ret : len;
}
static long rproc_device_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
{
struct rproc *rproc = container_of(filp->f_inode->i_cdev, struct rproc, cdev);
void __user *argp = (void __user *)arg;
s32 param;
switch (ioctl) {
case RPROC_SET_SHUTDOWN_ON_RELEASE:
if (copy_from_user(&param, argp, sizeof(s32)))
return -EFAULT;
rproc->cdev_put_on_release = !!param;
break;
case RPROC_GET_SHUTDOWN_ON_RELEASE:
param = (s32)rproc->cdev_put_on_release;
if (copy_to_user(argp, &param, sizeof(s32)))
return -EFAULT;
break;
default:
dev_err(&rproc->dev, "Unsupported ioctl\n");
return -EINVAL;
}
return 0;
}
static int rproc_cdev_release(struct inode *inode, struct file *filp)
{
struct rproc *rproc = container_of(inode->i_cdev, struct rproc, cdev);
if (rproc->cdev_put_on_release && rproc->state == RPROC_RUNNING)
rproc_shutdown(rproc);
return 0;
}
static const struct file_operations rproc_fops = {
.write = rproc_cdev_write,
.unlocked_ioctl = rproc_device_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.release = rproc_cdev_release,
};
int rproc_char_device_add(struct rproc *rproc)
{
int ret;
cdev_init(&rproc->cdev, &rproc_fops);
rproc->cdev.owner = THIS_MODULE;
rproc->dev.devt = MKDEV(MAJOR(rproc_major), rproc->index);
cdev_set_parent(&rproc->cdev, &rproc->dev.kobj);
ret = cdev_add(&rproc->cdev, rproc->dev.devt, 1);
if (ret < 0)
dev_err(&rproc->dev, "Failed to add char dev for %s\n", rproc->name);
return ret;
}
void rproc_char_device_remove(struct rproc *rproc)
{
__unregister_chrdev(MAJOR(rproc->dev.devt), rproc->index, 1, "remoteproc");
}
void __init rproc_init_cdev(void)
{
int ret;
ret = alloc_chrdev_region(&rproc_major, 0, NUM_RPROC_DEVICES, "remoteproc");
if (ret < 0)
pr_err("Failed to alloc rproc_cdev region, err %d\n", ret);
}
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0-only
/*
* Coredump functionality for Remoteproc framework.
*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*/
#include <linux/completion.h>
#include <linux/devcoredump.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/remoteproc.h>
#include "remoteproc_internal.h"
#include "remoteproc_elf_helpers.h"
struct rproc_coredump_state {
struct rproc *rproc;
void *header;
struct completion dump_done;
};
/**
* rproc_coredump_cleanup() - clean up dump_segments list
* @rproc: the remote processor handle
*/
void rproc_coredump_cleanup(struct rproc *rproc)
{
struct rproc_dump_segment *entry, *tmp;
list_for_each_entry_safe(entry, tmp, &rproc->dump_segments, node) {
list_del(&entry->node);
kfree(entry);
}
}
/**
* rproc_coredump_add_segment() - add segment of device memory to coredump
* @rproc: handle of a remote processor
* @da: device address
* @size: size of segment
*
* Add device memory to the list of segments to be included in a coredump for
* the remoteproc.
*
* Return: 0 on success, negative errno on error.
*/
int rproc_coredump_add_segment(struct rproc *rproc, dma_addr_t da, size_t size)
{
struct rproc_dump_segment *segment;
segment = kzalloc(sizeof(*segment), GFP_KERNEL);
if (!segment)
return -ENOMEM;
segment->da = da;
segment->size = size;
list_add_tail(&segment->node, &rproc->dump_segments);
return 0;
}
EXPORT_SYMBOL(rproc_coredump_add_segment);
/**
* rproc_coredump_add_custom_segment() - add custom coredump segment
* @rproc: handle of a remote processor
* @da: device address
* @size: size of segment
* @dumpfn: custom dump function called for each segment during coredump
* @priv: private data
*
* Add device memory to the list of segments to be included in the coredump
* and associate the segment with the given custom dump function and private
* data.
*
* Return: 0 on success, negative errno on error.
*/
int rproc_coredump_add_custom_segment(struct rproc *rproc,
dma_addr_t da, size_t size,
void (*dumpfn)(struct rproc *rproc,
struct rproc_dump_segment *segment,
void *dest, size_t offset,
size_t size),
void *priv)
{
struct rproc_dump_segment *segment;
segment = kzalloc(sizeof(*segment), GFP_KERNEL);
if (!segment)
return -ENOMEM;
segment->da = da;
segment->size = size;
segment->priv = priv;
segment->dump = dumpfn;
list_add_tail(&segment->node, &rproc->dump_segments);
return 0;
}
EXPORT_SYMBOL(rproc_coredump_add_custom_segment);
/**
* rproc_coredump_set_elf_info() - set coredump elf information
* @rproc: handle of a remote processor
* @class: elf class for coredump elf file
* @machine: elf machine for coredump elf file
*
* Set elf information which will be used for coredump elf file.
*
* Return: 0 on success, negative errno on error.
*/
int rproc_coredump_set_elf_info(struct rproc *rproc, u8 class, u16 machine)
{
if (class != ELFCLASS64 && class != ELFCLASS32)
return -EINVAL;
rproc->elf_class = class;
rproc->elf_machine = machine;
return 0;
}
EXPORT_SYMBOL(rproc_coredump_set_elf_info);
static void rproc_coredump_free(void *data)
{
struct rproc_coredump_state *dump_state = data;
vfree(dump_state->header);
complete(&dump_state->dump_done);
}
static void *rproc_coredump_find_segment(loff_t user_offset,
struct list_head *segments,
size_t *data_left)
{
struct rproc_dump_segment *segment;
list_for_each_entry(segment, segments, node) {
if (user_offset < segment->size) {
*data_left = segment->size - user_offset;
return segment;
}
user_offset -= segment->size;
}
*data_left = 0;
return NULL;
}
static void rproc_copy_segment(struct rproc *rproc, void *dest,
struct rproc_dump_segment *segment,
size_t offset, size_t size)
{
void *ptr;
if (segment->dump) {
segment->dump(rproc, segment, dest, offset, size);
} else {
ptr = rproc_da_to_va(rproc, segment->da + offset, size);
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);
}
}
}
static ssize_t rproc_coredump_read(char *buffer, loff_t offset, size_t count,
void *data, size_t header_sz)
{
size_t seg_data, bytes_left = count;
ssize_t copy_sz;
struct rproc_dump_segment *seg;
struct rproc_coredump_state *dump_state = data;
struct rproc *rproc = dump_state->rproc;
void *elfcore = dump_state->header;
/* Copy the vmalloc'ed header first. */
if (offset < header_sz) {
copy_sz = memory_read_from_buffer(buffer, count, &offset,
elfcore, header_sz);
return copy_sz;
}
/*
* Find out the segment memory chunk to be copied based on offset.
* Keep copying data until count bytes are read.
*/
while (bytes_left) {
seg = rproc_coredump_find_segment(offset - header_sz,
&rproc->dump_segments,
&seg_data);
/* EOF check */
if (!seg) {
dev_info(&rproc->dev, "Ramdump done, %lld bytes read",
offset);
break;
}
copy_sz = min_t(size_t, bytes_left, seg_data);
rproc_copy_segment(rproc, buffer, seg, seg->size - seg_data,
copy_sz);
offset += copy_sz;
buffer += copy_sz;
bytes_left -= copy_sz;
}
return count - bytes_left;
}
/**
* rproc_coredump() - perform coredump
* @rproc: rproc handle
*
* This function will generate an ELF header for the registered segments
* and create a devcoredump device associated with rproc. Based on the
* coredump configuration this function will directly copy the segments
* from device memory to userspace or copy segments from device memory to
* a separate buffer, which can then be read by userspace.
* The first approach avoids using extra vmalloc memory. But it will stall
* recovery flow until dump is read by userspace.
*/
void rproc_coredump(struct rproc *rproc)
{
struct rproc_dump_segment *segment;
void *phdr;
void *ehdr;
size_t data_size;
size_t offset;
void *data;
u8 class = rproc->elf_class;
int phnum = 0;
struct rproc_coredump_state dump_state;
enum rproc_dump_mechanism dump_conf = rproc->dump_conf;
if (list_empty(&rproc->dump_segments) ||
dump_conf == RPROC_COREDUMP_DISABLED)
return;
if (class == ELFCLASSNONE) {
dev_err(&rproc->dev, "Elf class is not set\n");
return;
}
data_size = elf_size_of_hdr(class);
list_for_each_entry(segment, &rproc->dump_segments, node) {
/*
* For default configuration buffer includes headers & segments.
* For inline dump buffer just includes headers as segments are
* directly read from device memory.
*/
data_size += elf_size_of_phdr(class);
if (dump_conf == RPROC_COREDUMP_DEFAULT)
data_size += segment->size;
phnum++;
}
data = vmalloc(data_size);
if (!data)
return;
ehdr = data;
memset(ehdr, 0, elf_size_of_hdr(class));
/* e_ident field is common for both elf32 and elf64 */
elf_hdr_init_ident(ehdr, class);
elf_hdr_set_e_type(class, ehdr, ET_CORE);
elf_hdr_set_e_machine(class, ehdr, rproc->elf_machine);
elf_hdr_set_e_version(class, ehdr, EV_CURRENT);
elf_hdr_set_e_entry(class, ehdr, rproc->bootaddr);
elf_hdr_set_e_phoff(class, ehdr, elf_size_of_hdr(class));
elf_hdr_set_e_ehsize(class, ehdr, elf_size_of_hdr(class));
elf_hdr_set_e_phentsize(class, ehdr, elf_size_of_phdr(class));
elf_hdr_set_e_phnum(class, ehdr, phnum);
phdr = data + elf_hdr_get_e_phoff(class, ehdr);
offset = elf_hdr_get_e_phoff(class, ehdr);
offset += elf_size_of_phdr(class) * elf_hdr_get_e_phnum(class, ehdr);
list_for_each_entry(segment, &rproc->dump_segments, node) {
memset(phdr, 0, elf_size_of_phdr(class));
elf_phdr_set_p_type(class, phdr, PT_LOAD);
elf_phdr_set_p_offset(class, phdr, offset);
elf_phdr_set_p_vaddr(class, phdr, segment->da);
elf_phdr_set_p_paddr(class, phdr, segment->da);
elf_phdr_set_p_filesz(class, phdr, segment->size);
elf_phdr_set_p_memsz(class, phdr, segment->size);
elf_phdr_set_p_flags(class, phdr, PF_R | PF_W | PF_X);
elf_phdr_set_p_align(class, phdr, 0);
if (dump_conf == RPROC_COREDUMP_DEFAULT)
rproc_copy_segment(rproc, data + offset, segment, 0,
segment->size);
offset += elf_phdr_get_p_filesz(class, phdr);
phdr += elf_size_of_phdr(class);
}
if (dump_conf == RPROC_COREDUMP_DEFAULT) {
dev_coredumpv(&rproc->dev, data, data_size, GFP_KERNEL);
return;
}
/* Initialize the dump state struct to be used by rproc_coredump_read */
dump_state.rproc = rproc;
dump_state.header = data;
init_completion(&dump_state.dump_done);
dev_coredumpm(&rproc->dev, NULL, &dump_state, data_size, GFP_KERNEL,
rproc_coredump_read, rproc_coredump_free);
/*
* Wait until the dump is read and free is called. Data is freed
* by devcoredump framework automatically after 5 minutes.
*/
wait_for_completion(&dump_state.dump_done);
}
......@@ -27,6 +27,94 @@
/* remoteproc debugfs parent dir */
static struct dentry *rproc_dbg;
/*
* A coredump-configuration-to-string lookup table, for exposing a
* human readable configuration via debugfs. Always keep in sync with
* enum rproc_coredump_mechanism
*/
static const char * const rproc_coredump_str[] = {
[RPROC_COREDUMP_DEFAULT] = "default",
[RPROC_COREDUMP_INLINE] = "inline",
[RPROC_COREDUMP_DISABLED] = "disabled",
};
/* Expose the current coredump configuration via debugfs */
static ssize_t rproc_coredump_read(struct file *filp, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct rproc *rproc = filp->private_data;
char buf[20];
int len;
len = scnprintf(buf, sizeof(buf), "%s\n",
rproc_coredump_str[rproc->dump_conf]);
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
}
/*
* By writing to the 'coredump' debugfs entry, we control the behavior of the
* coredump mechanism dynamically. The default value of this entry is "default".
*
* The 'coredump' debugfs entry supports these commands:
*
* default: This is the default coredump mechanism. When the remoteproc
* crashes the entire coredump will be copied to a separate buffer
* and exposed to userspace.
*
* inline: The coredump will not be copied to a separate buffer and the
* recovery process will have to wait until data is read by
* userspace. But this avoid usage of extra memory.
*
* disabled: This will disable coredump. Recovery will proceed without
* collecting any dump.
*/
static ssize_t rproc_coredump_write(struct file *filp,
const char __user *user_buf, size_t count,
loff_t *ppos)
{
struct rproc *rproc = filp->private_data;
int ret, err = 0;
char buf[20];
if (count > sizeof(buf))
return -EINVAL;
ret = copy_from_user(buf, user_buf, count);
if (ret)
return -EFAULT;
/* remove end of line */
if (buf[count - 1] == '\n')
buf[count - 1] = '\0';
if (rproc->state == RPROC_CRASHED) {
dev_err(&rproc->dev, "can't change coredump configuration\n");
err = -EBUSY;
goto out;
}
if (!strncmp(buf, "disable", count)) {
rproc->dump_conf = RPROC_COREDUMP_DISABLED;
} else if (!strncmp(buf, "inline", count)) {
rproc->dump_conf = RPROC_COREDUMP_INLINE;
} else if (!strncmp(buf, "default", count)) {
rproc->dump_conf = RPROC_COREDUMP_DEFAULT;
} else {
dev_err(&rproc->dev, "Invalid coredump configuration\n");
err = -EINVAL;
}
out:
return err ? err : count;
}
static const struct file_operations rproc_coredump_fops = {
.read = rproc_coredump_read,
.write = rproc_coredump_write,
.open = simple_open,
.llseek = generic_file_llseek,
};
/*
* Some remote processors may support dumping trace logs into a shared
* memory buffer. We expose this trace buffer using debugfs, so users
......@@ -337,6 +425,8 @@ void rproc_create_debug_dir(struct rproc *rproc)
rproc, &rproc_rsc_table_fops);
debugfs_create_file("carveout_memories", 0400, rproc->dbg_dir,
rproc, &rproc_carveouts_fops);
debugfs_create_file("coredump", 0600, rproc->dbg_dir,
rproc, &rproc_coredump_fops);
}
void __init rproc_init_debugfs(void)
......
......@@ -28,6 +28,8 @@ struct rproc_debug_trace {
void rproc_release(struct kref *kref);
irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id);
void rproc_vdev_release(struct kref *ref);
int rproc_of_parse_firmware(struct device *dev, int index,
const char **fw_name);
/* from remoteproc_virtio.c */
int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id);
......@@ -47,6 +49,38 @@ extern struct class rproc_class;
int rproc_init_sysfs(void);
void rproc_exit_sysfs(void);
/* from remoteproc_coredump.c */
void rproc_coredump_cleanup(struct rproc *rproc);
void rproc_coredump(struct rproc *rproc);
#ifdef CONFIG_REMOTEPROC_CDEV
void rproc_init_cdev(void);
void rproc_exit_cdev(void);
int rproc_char_device_add(struct rproc *rproc);
void rproc_char_device_remove(struct rproc *rproc);
#else
static inline void rproc_init_cdev(void)
{
}
static inline void rproc_exit_cdev(void)
{
}
/*
* The character device interface is an optional feature, if it is not enabled
* the function should not return an error.
*/
static inline int rproc_char_device_add(struct rproc *rproc)
{
return 0;
}
static inline void rproc_char_device_remove(struct rproc *rproc)
{
}
#endif
void rproc_free_vring(struct rproc_vring *rvring);
int rproc_alloc_vring(struct rproc_vdev *rvdev, int i);
......@@ -79,6 +113,14 @@ static inline int rproc_unprepare_device(struct rproc *rproc)
return 0;
}
static inline int rproc_attach_device(struct rproc *rproc)
{
if (rproc->ops->attach)
return rproc->ops->attach(rproc);
return 0;
}
static inline
int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw)
{
......
......@@ -15,8 +15,20 @@ static ssize_t firmware_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct rproc *rproc = to_rproc(dev);
return sprintf(buf, "%s\n", rproc->firmware);
const char *firmware = rproc->firmware;
/*
* 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)
firmware = "unknown";
return sprintf(buf, "%s\n", firmware);
}
/* Change firmware name via sysfs */
......@@ -72,6 +84,7 @@ static const char * const rproc_state_string[] = {
[RPROC_RUNNING] = "running",
[RPROC_CRASHED] = "crashed",
[RPROC_DELETED] = "deleted",
[RPROC_DETACHED] = "detached",
[RPROC_LAST] = "invalid",
};
......
......@@ -39,6 +39,15 @@
#define STM32_MBX_VQ1_ID 1
#define STM32_MBX_SHUTDOWN "shutdown"
#define RSC_TBL_SIZE 1024
#define M4_STATE_OFF 0
#define M4_STATE_INI 1
#define M4_STATE_CRUN 2
#define M4_STATE_CSTOP 3
#define M4_STATE_STANDBY 4
#define M4_STATE_CRASH 5
struct stm32_syscon {
struct regmap *map;
u32 reg;
......@@ -71,12 +80,15 @@ struct stm32_rproc {
struct reset_control *rst;
struct stm32_syscon hold_boot;
struct stm32_syscon pdds;
struct stm32_syscon m4_state;
struct stm32_syscon rsctbl;
int wdg_irq;
u32 nb_rmems;
struct stm32_rproc_mem *rmems;
struct stm32_mbox mb[MBOX_NB_MBX];
struct workqueue_struct *workqueue;
bool secured_soc;
void __iomem *rsc_va;
};
static int stm32_rproc_pa_to_da(struct rproc *rproc, phys_addr_t pa, u64 *da)
......@@ -128,10 +140,10 @@ static int stm32_rproc_mem_release(struct rproc *rproc,
return 0;
}
static int stm32_rproc_of_memory_translations(struct rproc *rproc)
static int stm32_rproc_of_memory_translations(struct platform_device *pdev,
struct stm32_rproc *ddata)
{
struct device *parent, *dev = rproc->dev.parent;
struct stm32_rproc *ddata = rproc->priv;
struct device *parent, *dev = &pdev->dev;
struct device_node *np;
struct stm32_rproc_mem *p_mems;
struct stm32_rproc_mem_ranges *mem_range;
......@@ -204,7 +216,7 @@ static int stm32_rproc_elf_load_rsc_table(struct rproc *rproc,
return 0;
}
static int stm32_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
static int stm32_rproc_parse_memory_regions(struct rproc *rproc)
{
struct device *dev = rproc->dev.parent;
struct device_node *np = dev->of_node;
......@@ -257,12 +269,23 @@ static int stm32_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
index++;
}
return 0;
}
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;
return stm32_rproc_elf_load_rsc_table(rproc, fw);
}
static irqreturn_t stm32_rproc_wdg(int irq, void *data)
{
struct rproc *rproc = data;
struct platform_device *pdev = data;
struct rproc *rproc = platform_get_drvdata(pdev);
rproc_report_crash(rproc, RPROC_WATCHDOG);
......@@ -437,6 +460,13 @@ static int stm32_rproc_start(struct rproc *rproc)
return stm32_rproc_set_hold_boot(rproc, true);
}
static int stm32_rproc_attach(struct rproc *rproc)
{
stm32_rproc_add_coredump_trace(rproc);
return stm32_rproc_set_hold_boot(rproc, true);
}
static int stm32_rproc_stop(struct rproc *rproc)
{
struct stm32_rproc *ddata = rproc->priv;
......@@ -474,6 +504,18 @@ static int stm32_rproc_stop(struct rproc *rproc)
}
}
/* update coprocessor state to OFF if available */
if (ddata->m4_state.map) {
err = regmap_update_bits(ddata->m4_state.map,
ddata->m4_state.reg,
ddata->m4_state.mask,
M4_STATE_OFF);
if (err) {
dev_err(&rproc->dev, "failed to set copro state\n");
return err;
}
}
return 0;
}
......@@ -502,6 +544,7 @@ static void stm32_rproc_kick(struct rproc *rproc, int vqid)
static struct rproc_ops st_rproc_ops = {
.start = stm32_rproc_start,
.stop = stm32_rproc_stop,
.attach = stm32_rproc_attach,
.kick = stm32_rproc_kick,
.load = rproc_elf_load_segments,
.parse_fw = stm32_rproc_parse_fw,
......@@ -538,12 +581,11 @@ static int stm32_rproc_get_syscon(struct device_node *np, const char *prop,
return err;
}
static int stm32_rproc_parse_dt(struct platform_device *pdev)
static int stm32_rproc_parse_dt(struct platform_device *pdev,
struct stm32_rproc *ddata, bool *auto_boot)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct rproc *rproc = platform_get_drvdata(pdev);
struct stm32_rproc *ddata = rproc->priv;
struct stm32_syscon tz;
unsigned int tzen;
int err, irq;
......@@ -554,7 +596,7 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev)
if (irq > 0) {
err = devm_request_irq(dev, irq, stm32_rproc_wdg, 0,
dev_name(dev), rproc);
dev_name(dev), pdev);
if (err) {
dev_err(dev, "failed to request wdg irq\n");
return err;
......@@ -589,7 +631,7 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev)
err = regmap_read(tz.map, tz.reg, &tzen);
if (err) {
dev_err(&rproc->dev, "failed to read tzen\n");
dev_err(dev, "failed to read tzen\n");
return err;
}
ddata->secured_soc = tzen & tz.mask;
......@@ -605,9 +647,118 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev)
if (err)
dev_info(dev, "failed to get pdds\n");
rproc->auto_boot = of_property_read_bool(np, "st,auto-boot");
*auto_boot = of_property_read_bool(np, "st,auto-boot");
return stm32_rproc_of_memory_translations(rproc);
/*
* See if we can check the M4 status, i.e if it was started
* from the boot loader or not.
*/
err = stm32_rproc_get_syscon(np, "st,syscfg-m4-state",
&ddata->m4_state);
if (err) {
/* remember this */
ddata->m4_state.map = NULL;
/* no coprocessor state syscon (optional) */
dev_warn(dev, "m4 state not supported\n");
/* no need to go further */
return 0;
}
/* See if we can get the resource table */
err = stm32_rproc_get_syscon(np, "st,syscfg-rsc-tbl",
&ddata->rsctbl);
if (err) {
/* no rsc table syscon (optional) */
dev_warn(dev, "rsc tbl syscon not supported\n");
}
return 0;
}
static int stm32_rproc_get_m4_status(struct stm32_rproc *ddata,
unsigned int *state)
{
/* See stm32_rproc_parse_dt() */
if (!ddata->m4_state.map) {
/*
* We couldn't get the coprocessor's state, assume
* it is not running.
*/
state = M4_STATE_OFF;
return 0;
}
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)
......@@ -616,6 +767,7 @@ static int stm32_rproc_probe(struct platform_device *pdev)
struct stm32_rproc *ddata;
struct device_node *np = dev->of_node;
struct rproc *rproc;
unsigned int state;
int ret;
ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
......@@ -626,25 +778,47 @@ static int stm32_rproc_probe(struct platform_device *pdev)
if (!rproc)
return -ENOMEM;
ddata = rproc->priv;
rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
ret = stm32_rproc_parse_dt(pdev, ddata, &rproc->auto_boot);
if (ret)
goto free_rproc;
ret = stm32_rproc_of_memory_translations(pdev, ddata);
if (ret)
goto free_rproc;
ret = stm32_rproc_get_m4_status(ddata, &state);
if (ret)
goto free_rproc;
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 = rproc->priv;
ddata->workqueue = create_workqueue(dev_name(dev));
if (!ddata->workqueue) {
dev_err(dev, "cannot create workqueue\n");
ret = -ENOMEM;
goto free_rproc;
goto free_resources;
}
platform_set_drvdata(pdev, rproc);
ret = stm32_rproc_parse_dt(pdev);
if (ret)
goto free_wkq;
ret = stm32_rproc_request_mbox(rproc);
if (ret)
goto free_rproc;
goto free_wkq;
ret = rproc_add(rproc);
if (ret)
......@@ -656,6 +830,8 @@ static int stm32_rproc_probe(struct platform_device *pdev)
stm32_rproc_free_mbox(rproc);
free_wkq:
destroy_workqueue(ddata->workqueue);
free_resources:
rproc_resource_cleanup(rproc);
free_rproc:
if (device_may_wakeup(dev)) {
dev_pm_clear_wake_irq(dev);
......
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Texas Instruments TI-SCI Processor Controller Helper Functions
*
* Copyright (C) 2018-2020 Texas Instruments Incorporated - https://www.ti.com/
* Suman Anna <s-anna@ti.com>
*/
#ifndef REMOTEPROC_TI_SCI_PROC_H
#define REMOTEPROC_TI_SCI_PROC_H
#include <linux/soc/ti/ti_sci_protocol.h>
/**
* struct ti_sci_proc - structure representing a processor control client
* @sci: cached TI-SCI protocol handle
* @ops: cached TI-SCI proc ops
* @dev: cached client device pointer
* @proc_id: processor id for the consumer remoteproc device
* @host_id: host id to pass the control over for this consumer remoteproc
* device
*/
struct ti_sci_proc {
const struct ti_sci_handle *sci;
const struct ti_sci_proc_ops *ops;
struct device *dev;
u8 proc_id;
u8 host_id;
};
static inline int ti_sci_proc_request(struct ti_sci_proc *tsp)
{
int ret;
ret = tsp->ops->request(tsp->sci, tsp->proc_id);
if (ret)
dev_err(tsp->dev, "ti-sci processor request failed: %d\n",
ret);
return ret;
}
static inline int ti_sci_proc_release(struct ti_sci_proc *tsp)
{
int ret;
ret = tsp->ops->release(tsp->sci, tsp->proc_id);
if (ret)
dev_err(tsp->dev, "ti-sci processor release failed: %d\n",
ret);
return ret;
}
static inline int ti_sci_proc_handover(struct ti_sci_proc *tsp)
{
int ret;
ret = tsp->ops->handover(tsp->sci, tsp->proc_id, tsp->host_id);
if (ret)
dev_err(tsp->dev, "ti-sci processor handover of %d to %d failed: %d\n",
tsp->proc_id, tsp->host_id, ret);
return ret;
}
static inline int ti_sci_proc_set_config(struct ti_sci_proc *tsp,
u64 boot_vector,
u32 cfg_set, u32 cfg_clr)
{
int ret;
ret = tsp->ops->set_config(tsp->sci, tsp->proc_id, boot_vector,
cfg_set, cfg_clr);
if (ret)
dev_err(tsp->dev, "ti-sci processor set_config failed: %d\n",
ret);
return ret;
}
static inline int ti_sci_proc_set_control(struct ti_sci_proc *tsp,
u32 ctrl_set, u32 ctrl_clr)
{
int ret;
ret = tsp->ops->set_control(tsp->sci, tsp->proc_id, ctrl_set, ctrl_clr);
if (ret)
dev_err(tsp->dev, "ti-sci processor set_control failed: %d\n",
ret);
return ret;
}
static inline int ti_sci_proc_get_status(struct ti_sci_proc *tsp,
u64 *boot_vector, u32 *cfg_flags,
u32 *ctrl_flags, u32 *status_flags)
{
int ret;
ret = tsp->ops->get_status(tsp->sci, tsp->proc_id, boot_vector,
cfg_flags, ctrl_flags, status_flags);
if (ret)
dev_err(tsp->dev, "ti-sci processor get_status failed: %d\n",
ret);
return ret;
}
#endif /* REMOTEPROC_TI_SCI_PROC_H */
......@@ -38,6 +38,7 @@
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/virtio.h>
#include <linux/cdev.h>
#include <linux/completion.h>
#include <linux/idr.h>
#include <linux/of.h>
......@@ -359,6 +360,7 @@ enum rsc_handling_status {
* @unprepare: unprepare device after stop
* @start: power on the device and boot it
* @stop: power off the device
* @attach: attach to a device that his already 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)
......@@ -379,6 +381,7 @@ struct rproc_ops {
int (*unprepare)(struct rproc *rproc);
int (*start)(struct rproc *rproc);
int (*stop)(struct rproc *rproc);
int (*attach)(struct rproc *rproc);
void (*kick)(struct rproc *rproc, int vqid);
void * (*da_to_va)(struct rproc *rproc, u64 da, size_t len);
int (*parse_fw)(struct rproc *rproc, const struct firmware *fw);
......@@ -400,6 +403,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_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
*
* Please note that the values of these states are used as indices
......@@ -414,7 +419,8 @@ enum rproc_state {
RPROC_RUNNING = 2,
RPROC_CRASHED = 3,
RPROC_DELETED = 4,
RPROC_LAST = 5,
RPROC_DETACHED = 5,
RPROC_LAST = 6,
};
/**
......@@ -434,6 +440,20 @@ enum rproc_crash_type {
RPROC_FATAL_ERROR,
};
/**
* enum rproc_dump_mechanism - Coredump options for core
* @RPROC_COREDUMP_DEFAULT: Copy dump to separate buffer and carry on with
recovery
* @RPROC_COREDUMP_INLINE: Read segments directly from device memory. Stall
recovery until all segments are read
* @RPROC_COREDUMP_DISABLED: Don't perform any dump
*/
enum rproc_dump_mechanism {
RPROC_COREDUMP_DEFAULT,
RPROC_COREDUMP_INLINE,
RPROC_COREDUMP_DISABLED,
};
/**
* struct rproc_dump_segment - segment info from ELF header
* @node: list node related to the rproc segment list
......@@ -451,7 +471,7 @@ struct rproc_dump_segment {
void *priv;
void (*dump)(struct rproc *rproc, struct rproc_dump_segment *segment,
void *dest);
void *dest, size_t offset, size_t size);
loff_t offset;
};
......@@ -466,6 +486,7 @@ struct rproc_dump_segment {
* @dev: virtual device for refcounting and common remoteproc behavior
* @power: refcount of users who need this rproc powered up
* @state: state of the device
* @dump_conf: Currently selected coredump configuration
* @lock: lock which protects concurrent manipulations of the rproc
* @dbg_dir: debugfs directory of this rproc device
* @traces: list of trace buffers
......@@ -486,8 +507,11 @@ struct rproc_dump_segment {
* @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
* @cdev_put_on_release: flag to indicate if remoteproc should be shutdown on @char_dev release
*/
struct rproc {
struct list_head node;
......@@ -499,6 +523,7 @@ struct rproc {
struct device dev;
atomic_t power;
unsigned int state;
enum rproc_dump_mechanism dump_conf;
struct mutex lock;
struct dentry *dbg_dir;
struct list_head traces;
......@@ -519,10 +544,13 @@ struct rproc {
size_t table_sz;
bool has_iommu;
bool auto_boot;
bool autonomous;
struct list_head dump_segments;
int nb_vdev;
u8 elf_class;
u16 elf_machine;
struct cdev cdev;
bool cdev_put_on_release;
};
/**
......@@ -603,6 +631,7 @@ void rproc_put(struct rproc *rproc);
int rproc_add(struct rproc *rproc);
int rproc_del(struct rproc *rproc);
void rproc_free(struct rproc *rproc);
void rproc_resource_cleanup(struct rproc *rproc);
struct rproc *devm_rproc_alloc(struct device *dev, const char *name,
const struct rproc_ops *ops,
......@@ -630,7 +659,8 @@ int rproc_coredump_add_custom_segment(struct rproc *rproc,
dma_addr_t da, size_t size,
void (*dumpfn)(struct rproc *rproc,
struct rproc_dump_segment *segment,
void *dest),
void *dest, size_t offset,
size_t size),
void *priv);
int rproc_coredump_set_elf_info(struct rproc *rproc, u8 class, u16 machine);
......
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2019 Linaro Ltd. */
#ifndef __QCOM_Q6V5_IPA_NOTIFY_H__
#define __QCOM_Q6V5_IPA_NOTIFY_H__
#if IS_ENABLED(CONFIG_QCOM_Q6V5_IPA_NOTIFY)
#include <linux/remoteproc.h>
enum qcom_rproc_event {
MODEM_STARTING = 0, /* Modem is about to be started */
MODEM_RUNNING = 1, /* Startup complete; modem is operational */
MODEM_STOPPING = 2, /* Modem is about to shut down */
MODEM_CRASHED = 3, /* Modem has crashed (implies stopping) */
MODEM_OFFLINE = 4, /* Modem is now offline */
MODEM_REMOVING = 5, /* Modem is about to be removed */
};
typedef void (*qcom_ipa_notify_t)(void *data, enum qcom_rproc_event event);
struct qcom_rproc_ipa_notify {
struct rproc_subdev subdev;
qcom_ipa_notify_t notify;
void *data;
};
/**
* qcom_add_ipa_notify_subdev() - Register IPA notification subdevice
* @rproc: rproc handle
* @ipa_notify: IPA notification subdevice handle
*
* Register the @ipa_notify subdevice with the @rproc so modem events
* can be sent to IPA when they occur.
*
* This is defined in "qcom_q6v5_ipa_notify.c".
*/
void qcom_add_ipa_notify_subdev(struct rproc *rproc,
struct qcom_rproc_ipa_notify *ipa_notify);
/**
* qcom_remove_ipa_notify_subdev() - Remove IPA SSR subdevice
* @rproc: rproc handle
* @ipa_notify: IPA notification subdevice handle
*
* This is defined in "qcom_q6v5_ipa_notify.c".
*/
void qcom_remove_ipa_notify_subdev(struct rproc *rproc,
struct qcom_rproc_ipa_notify *ipa_notify);
/**
* qcom_register_ipa_notify() - Register IPA notification function
* @rproc: Remote processor handle
* @notify: Non-null IPA notification callback function pointer
* @data: Data supplied to IPA notification callback function
*
* @Return: 0 if successful, or a negative error code otherwise
*
* This is defined in "qcom_q6v5_mss.c".
*/
int qcom_register_ipa_notify(struct rproc *rproc, qcom_ipa_notify_t notify,
void *data);
/**
* qcom_deregister_ipa_notify() - Deregister IPA notification function
* @rproc: Remote processor handle
*
* This is defined in "qcom_q6v5_mss.c".
*/
void qcom_deregister_ipa_notify(struct rproc *rproc);
#else /* !IS_ENABLED(CONFIG_QCOM_Q6V5_IPA_NOTIFY) */
struct qcom_rproc_ipa_notify { /* empty */ };
#define qcom_add_ipa_notify_subdev(rproc, ipa_notify) /* no-op */
#define qcom_remove_ipa_notify_subdev(rproc, ipa_notify) /* no-op */
#endif /* !IS_ENABLED(CONFIG_QCOM_Q6V5_IPA_NOTIFY) */
#endif /* !__QCOM_Q6V5_IPA_NOTIFY_H__ */
......@@ -5,17 +5,43 @@ struct notifier_block;
#if IS_ENABLED(CONFIG_QCOM_RPROC_COMMON)
int qcom_register_ssr_notifier(struct notifier_block *nb);
void qcom_unregister_ssr_notifier(struct notifier_block *nb);
/**
* enum qcom_ssr_notify_type - Startup/Shutdown events related to a remoteproc
* processor.
*
* @QCOM_SSR_BEFORE_POWERUP: Remoteproc about to start (prepare stage)
* @QCOM_SSR_AFTER_POWERUP: Remoteproc is running (start stage)
* @QCOM_SSR_BEFORE_SHUTDOWN: Remoteproc crashed or shutting down (stop stage)
* @QCOM_SSR_AFTER_SHUTDOWN: Remoteproc is down (unprepare stage)
*/
enum qcom_ssr_notify_type {
QCOM_SSR_BEFORE_POWERUP,
QCOM_SSR_AFTER_POWERUP,
QCOM_SSR_BEFORE_SHUTDOWN,
QCOM_SSR_AFTER_SHUTDOWN,
};
struct qcom_ssr_notify_data {
const char *name;
bool crashed;
};
void *qcom_register_ssr_notifier(const char *name, struct notifier_block *nb);
int qcom_unregister_ssr_notifier(void *notify, struct notifier_block *nb);
#else
static inline int qcom_register_ssr_notifier(struct notifier_block *nb)
static inline void *qcom_register_ssr_notifier(const char *name,
struct notifier_block *nb)
{
return 0;
return NULL;
}
static inline void qcom_unregister_ssr_notifier(struct notifier_block *nb) {}
static inline int qcom_unregister_ssr_notifier(void *notify,
struct notifier_block *nb)
{
return 0;
}
#endif
......
/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
/*
* IOCTLs for Remoteproc's character device interface.
*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*/
#ifndef _UAPI_REMOTEPROC_CDEV_H_
#define _UAPI_REMOTEPROC_CDEV_H_
#include <linux/ioctl.h>
#include <linux/types.h>
#define RPROC_MAGIC 0xB7
/*
* The RPROC_SET_SHUTDOWN_ON_RELEASE ioctl allows to enable/disable the shutdown of a remote
* processor automatically when the controlling userpsace closes the char device interface.
*
* input parameter: integer
* 0 : disable automatic shutdown
* other : enable automatic shutdown
*/
#define RPROC_SET_SHUTDOWN_ON_RELEASE _IOW(RPROC_MAGIC, 1, __s32)
/*
* The RPROC_GET_SHUTDOWN_ON_RELEASE ioctl gets information about whether the automatic shutdown of
* a remote processor is enabled or disabled when the controlling userspace closes the char device
* interface.
*
* output parameter: integer
* 0 : automatic shutdown disable
* other : automatic shutdown enable
*/
#define RPROC_GET_SHUTDOWN_ON_RELEASE _IOR(RPROC_MAGIC, 2, __s32)
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment