diff options
author | Linus Torvalds | 2020-06-08 13:01:08 -0700 |
---|---|---|
committer | Linus Torvalds | 2020-06-08 13:01:08 -0700 |
commit | abfbb29297c27e3f101f348dc9e467b0fe70f919 (patch) | |
tree | 9e1c75078a260f31d18c1bb365d703c6225f9270 | |
parent | d26a42a9614083413e778832a6efbdf0038c3bff (diff) | |
parent | 7dcef3988eedbfb40e7e95a821966a029a5a465b (diff) |
Merge tag 'rproc-v5.8' of git://git.kernel.org/pub/scm/linux/kernel/git/andersson/remoteproc
Pull remoteproc updates from Bjorn Andersson:
"This introduces device managed versions of functions used to register
remoteproc devices, add support for remoteproc driver specific
resource control, enables remoteproc drivers to specify ELF class and
machine for coredumps. It integrates pm_runtime in the core for
keeping resources active while the remote is booted and holds a wake
source while recoverying a remote processor after a firmware crash.
It refactors the remoteproc device's allocation path to simplify the
logic, fix a few cleanup bugs and to not clone const strings onto the
heap. Debugfs code is simplifies using the DEFINE_SHOW_ATTRIBUTE and a
zero-length array is replaced with flexible-array.
A new remoteproc driver for the JZ47xx VPU is introduced, the Qualcomm
SM8250 gains support for audio, compute and sensor remoteprocs and the
Qualcomm SC7180 modem support is cleaned up and improved.
The Qualcomm glink subsystem-restart driver is merged into the main
glink driver, the Qualcomm sysmon driver is extended to properly
notify remote processors about all other remote processors' state
transitions"
* tag 'rproc-v5.8' of git://git.kernel.org/pub/scm/linux/kernel/git/andersson/remoteproc: (43 commits)
remoteproc: Fix an error code in devm_rproc_alloc()
MAINTAINERS: Add myself as reviewer for Ingenic rproc driver
remoteproc: ingenic: Added remoteproc driver
remoteproc: Add support for runtime PM
dt-bindings: Document JZ47xx VPU auxiliary processor
remoteproc: wcss: Fix arguments passed to qcom_add_glink_subdev()
remoteproc: Fix and restore the parenting hierarchy for vdev
remoteproc: Fall back to using parent memory pool if no dedicated available
remoteproc: Replace zero-length array with flexible-array
remoteproc: wcss: add support for rpmsg communication
remoteproc: core: Prevent system suspend during remoteproc recovery
remoteproc: qcom_q6v5_mss: Remove unused q6v5_da_to_va function
remoteproc: qcom_q6v5_mss: map/unmap mpss segments before/after use
remoteproc: qcom_q6v5_mss: Drop accesses to MPSS PERPH register space
dt-bindings: remoteproc: qcom: Replace halt-nav with spare-regs
remoteproc: qcom: pas: Add SM8250 PAS remoteprocs
dt-bindings: remoteproc: qcom: pas: Add SM8250 remoteprocs
remoteproc: qcom_q6v5_mss: Extract mba/mpss from memory-region
dt-bindings: remoteproc: qcom: Use memory-region to reference memory
remoteproc: qcom: pas: Add SC7180 Modem support
...
31 files changed, 921 insertions, 277 deletions
diff --git a/Documentation/devicetree/bindings/remoteproc/ingenic,vpu.yaml b/Documentation/devicetree/bindings/remoteproc/ingenic,vpu.yaml new file mode 100644 index 000000000000..c019f9fbe916 --- /dev/null +++ b/Documentation/devicetree/bindings/remoteproc/ingenic,vpu.yaml @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/remoteproc/ingenic,vpu.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Ingenic Video Processing Unit bindings + +description: + Inside the Video Processing Unit (VPU) of the recent JZ47xx SoCs from + Ingenic is a second Xburst MIPS CPU very similar to the main core. + This document describes the devicetree bindings for this auxiliary + processor. + +maintainers: + - Paul Cercueil <paul@crapouillou.net> + +properties: + compatible: + const: ingenic,jz4770-vpu-rproc + + reg: + items: + - description: aux registers + - description: tcsm0 registers + - description: tcsm1 registers + - description: sram registers + + reg-names: + items: + - const: aux + - const: tcsm0 + - const: tcsm1 + - const: sram + + clocks: + items: + - description: aux clock + - description: vpu clock + + clock-names: + items: + - const: aux + - const: vpu + + interrupts: + description: VPU hardware interrupt + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + - interrupts + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/jz4770-cgu.h> + + vpu: video-decoder@132a0000 { + compatible = "ingenic,jz4770-vpu-rproc"; + + reg = <0x132a0000 0x20>, /* AUX */ + <0x132b0000 0x4000>, /* TCSM0 */ + <0x132c0000 0xc000>, /* TCSM1 */ + <0x132f0000 0x7000>; /* SRAM */ + reg-names = "aux", "tcsm0", "tcsm1", "sram"; + + clocks = <&cgu JZ4770_CLK_AUX>, <&cgu JZ4770_CLK_VPU>; + clock-names = "aux", "vpu"; + + interrupt-parent = <&cpuintc>; + interrupts = <3>; + }; diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt b/Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt index 9938918b2fea..54737024da20 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt @@ -15,12 +15,16 @@ on the Qualcomm ADSP Hexagon core. "qcom,qcs404-adsp-pas" "qcom,qcs404-cdsp-pas" "qcom,qcs404-wcss-pas" + "qcom,sc7180-mpss-pas" "qcom,sdm845-adsp-pas" "qcom,sdm845-cdsp-pas" "qcom,sm8150-adsp-pas" "qcom,sm8150-cdsp-pas" "qcom,sm8150-mpss-pas" "qcom,sm8150-slpi-pas" + "qcom,sm8250-adsp-pas" + "qcom,sm8250-cdsp-pas" + "qcom,sm8250-slpi-pas" - interrupts-extended: Usage: required @@ -44,8 +48,12 @@ on the Qualcomm ADSP Hexagon core. qcom,sm8150-adsp-pas: qcom,sm8150-cdsp-pas: qcom,sm8150-slpi-pas: + qcom,sm8250-adsp-pas: + qcom,sm8250-cdsp-pas: + qcom,sm8250-slpi-pas: must be "wdog", "fatal", "ready", "handover", "stop-ack" qcom,qcs404-wcss-pas: + qcom,sc7180-mpss-pas: qcom,sm8150-mpss-pas: must be "wdog", "fatal", "ready", "handover", "stop-ack", "shutdown-ack" @@ -105,10 +113,14 @@ on the Qualcomm ADSP Hexagon core. qcom,sdm845-cdsp-pas: qcom,sm8150-adsp-pas: qcom,sm8150-cdsp-pas: + qcom,sm8250-cdsp-pas: must be "cx", "load_state" + qcom,sc7180-mpss-pas: qcom,sm8150-mpss-pas: must be "cx", "load_state", "mss" + qcom,sm8250-adsp-pas: qcom,sm8150-slpi-pas: + qcom,sm8250-slpi-pas: must be "lcx", "lmx", "load_state" - memory-region: diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt index 130e50aab741..1f9a62e13ebe 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt @@ -79,7 +79,7 @@ on the Qualcomm Hexagon core. "snoc_axi", "mnoc_axi", "qdss" qcom,sc7180-mss-pil: must be "iface", "bus", "xo", "snoc_axi", "mnoc_axi", - "mss_crypto", "mss_nav", "nav" + "nav" qcom,sdm845-mss-pil: must be "iface", "bus", "mem", "xo", "gpll0_mss", "snoc_axi", "mnoc_axi", "prng" @@ -102,6 +102,14 @@ on the Qualcomm Hexagon core. must be "mss_restart", "pdc_reset" for the modem sub-system on SC7180, SDM845 SoCs +For devices where the mba and mpss sub-nodes are not specified, mba/mpss region +should be referenced as follows: +- memory-region: + Usage: required + Value type: <phandle> + Definition: reference to the reserved-memory for the mba region followed + by the mpss region + For the compatible strings below the following supplies are required: "qcom,q6v5-pil" "qcom,msm8916-mss-pil", @@ -173,16 +181,12 @@ For the compatible string below the following supplies are required: For the compatible strings below the following phandle references are required: "qcom,sc7180-mss-pil" -- qcom,halt-nav-regs: +- qcom,spare-regs: Usage: required Value type: <prop-encoded-array> - Definition: reference to a list of 2 phandles with one offset each for - the modem sub-system running on SC7180 SoC. The first - phandle reference is to the mss clock node followed by the - offset within register space for nav halt register. The - second phandle reference is to a syscon representing TCSR - followed by the offset within syscon for conn_box_spare0 - register. + Definition: a phandle reference to a syscon representing TCSR followed + by the offset within syscon for conn_box_spare0 register + used by the modem sub-system running on SC7180 SoC. The Hexagon node must contain iommus property as described in ../iommu/iommu.txt on platforms which do not have TrustZone. diff --git a/MAINTAINERS b/MAINTAINERS index ad076ddac82b..77a3fa5e3edd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8502,6 +8502,7 @@ F: drivers/mtd/nand/raw/ingenic/ F: drivers/pinctrl/pinctrl-ingenic.c F: drivers/power/supply/ingenic-battery.c F: drivers/pwm/pwm-jz4740.c +F: drivers/remoteproc/ingenic_rproc.c F: drivers/rtc/rtc-jz4740.c F: drivers/tty/serial/8250/8250_ingenic.c F: drivers/usb/musb/jz4740.c diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index fbaed079b299..c4d1731295eb 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -23,6 +23,15 @@ config IMX_REMOTEPROC It's safe to say N here. +config INGENIC_VPU_RPROC + tristate "Ingenic JZ47xx VPU remoteproc support" + depends on MIPS || COMPILE_TEST + help + Say y or m here to support the VPU in the JZ47xx SoCs from Ingenic. + + This can be either built-in or a loadable module. + If unsure say N. + config MTK_SCP tristate "Mediatek SCP support" depends on ARCH_MEDIATEK diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 0effd3825035..e8b886e511f0 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -10,6 +10,7 @@ remoteproc-y += remoteproc_sysfs.o remoteproc-y += remoteproc_virtio.o remoteproc-y += remoteproc_elf_loader.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 obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o diff --git a/drivers/remoteproc/ingenic_rproc.c b/drivers/remoteproc/ingenic_rproc.c new file mode 100644 index 000000000000..189020d77b25 --- /dev/null +++ b/drivers/remoteproc/ingenic_rproc.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Ingenic JZ47xx remoteproc driver + * Copyright 2019, Paul Cercueil <paul@crapouillou.net> + */ + +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#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" + +#define REG_AUX_CTRL 0x0 +#define REG_AUX_MSG_ACK 0x10 +#define REG_AUX_MSG 0x14 +#define REG_CORE_MSG_ACK 0x18 +#define REG_CORE_MSG 0x1C + +#define AUX_CTRL_SLEEP BIT(31) +#define AUX_CTRL_MSG_IRQ_EN BIT(3) +#define AUX_CTRL_NMI_RESETS BIT(2) +#define AUX_CTRL_NMI BIT(1) +#define AUX_CTRL_SW_RESET BIT(0) + +struct vpu_mem_map { + const char *name; + unsigned int da; +}; + +struct vpu_mem_info { + const struct vpu_mem_map *map; + unsigned long len; + void __iomem *base; +}; + +static const struct vpu_mem_map vpu_mem_map[] = { + { "tcsm0", 0x132b0000 }, + { "tcsm1", 0xf4000000 }, + { "sram", 0x132f0000 }, +}; + +/** + * struct vpu - Ingenic VPU remoteproc private structure + * @irq: interrupt number + * @clks: pointers to the VPU and AUX clocks + * @aux_base: raw pointer to the AUX interface registers + * @mem_info: array of struct vpu_mem_info, which contain the mapping info of + * each of the external memories + * @dev: private pointer to the device + */ +struct vpu { + int irq; + struct clk_bulk_data clks[2]; + void __iomem *aux_base; + struct vpu_mem_info mem_info[ARRAY_SIZE(vpu_mem_map)]; + struct device *dev; +}; + +static int ingenic_rproc_start(struct rproc *rproc) +{ + struct vpu *vpu = rproc->priv; + u32 ctrl; + + enable_irq(vpu->irq); + + /* Reset the AUX and enable message IRQ */ + ctrl = AUX_CTRL_NMI_RESETS | AUX_CTRL_NMI | AUX_CTRL_MSG_IRQ_EN; + writel(ctrl, vpu->aux_base + REG_AUX_CTRL); + + return 0; +} + +static int ingenic_rproc_stop(struct rproc *rproc) +{ + struct vpu *vpu = rproc->priv; + + disable_irq(vpu->irq); + + /* Keep AUX in reset mode */ + writel(AUX_CTRL_SW_RESET, vpu->aux_base + REG_AUX_CTRL); + + return 0; +} + +static void ingenic_rproc_kick(struct rproc *rproc, int vqid) +{ + struct vpu *vpu = rproc->priv; + + writel(vqid, vpu->aux_base + REG_CORE_MSG); +} + +static void *ingenic_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len) +{ + struct vpu *vpu = rproc->priv; + void __iomem *va = NULL; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(vpu_mem_map); i++) { + const struct vpu_mem_info *info = &vpu->mem_info[i]; + const struct vpu_mem_map *map = info->map; + + if (da >= map->da && (da + len) < (map->da + info->len)) { + va = info->base + (da - map->da); + break; + } + } + + return (__force void *)va; +} + +static struct rproc_ops ingenic_rproc_ops = { + .start = ingenic_rproc_start, + .stop = ingenic_rproc_stop, + .kick = ingenic_rproc_kick, + .da_to_va = ingenic_rproc_da_to_va, +}; + +static irqreturn_t vpu_interrupt(int irq, void *data) +{ + struct rproc *rproc = data; + struct vpu *vpu = rproc->priv; + u32 vring; + + vring = readl(vpu->aux_base + REG_AUX_MSG); + + /* Ack the interrupt */ + writel(0, vpu->aux_base + REG_AUX_MSG_ACK); + + 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; + struct resource *mem; + struct rproc *rproc; + struct vpu *vpu; + unsigned int i; + int ret; + + rproc = devm_rproc_alloc(dev, "ingenic-vpu", + &ingenic_rproc_ops, NULL, sizeof(*vpu)); + if (!rproc) + return -ENOMEM; + + vpu = rproc->priv; + vpu->dev = &pdev->dev; + platform_set_drvdata(pdev, vpu); + + mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aux"); + vpu->aux_base = devm_ioremap_resource(dev, mem); + if (IS_ERR(vpu->aux_base)) { + dev_err(dev, "Failed to ioremap\n"); + return PTR_ERR(vpu->aux_base); + } + + for (i = 0; i < ARRAY_SIZE(vpu_mem_map); i++) { + mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, + vpu_mem_map[i].name); + + vpu->mem_info[i].base = devm_ioremap_resource(dev, mem); + if (IS_ERR(vpu->mem_info[i].base)) { + ret = PTR_ERR(vpu->mem_info[i].base); + dev_err(dev, "Failed to ioremap\n"); + return ret; + } + + vpu->mem_info[i].len = resource_size(mem); + vpu->mem_info[i].map = &vpu_mem_map[i]; + } + + vpu->clks[0].id = "vpu"; + vpu->clks[1].id = "aux"; + + ret = devm_clk_bulk_get(dev, ARRAY_SIZE(vpu->clks), vpu->clks); + if (ret) { + dev_err(dev, "Failed to get clocks\n"); + return ret; + } + + vpu->irq = platform_get_irq(pdev, 0); + if (vpu->irq < 0) + return vpu->irq; + + ret = devm_request_irq(dev, vpu->irq, vpu_interrupt, 0, "VPU", rproc); + if (ret < 0) { + dev_err(dev, "Failed to request IRQ\n"); + return ret; + } + + 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; + } + +out_pm_put: + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static const struct of_device_id ingenic_rproc_of_matches[] = { + { .compatible = "ingenic,jz4770-vpu-rproc", }, + {} +}; +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, + }, +}; +module_platform_driver(ingenic_rproc_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); +MODULE_DESCRIPTION("Ingenic JZ47xx Remote Processor control driver"); diff --git a/drivers/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c index 2bead57c9cf9..ac13e7b046a6 100644 --- a/drivers/remoteproc/mtk_scp.c +++ b/drivers/remoteproc/mtk_scp.c @@ -132,8 +132,8 @@ static int scp_ipi_init(struct mtk_scp *scp) (struct mtk_share_obj __iomem *)(scp->sram_base + recv_offset); scp->send_buf = (struct mtk_share_obj __iomem *)(scp->sram_base + send_offset); - memset_io(scp->recv_buf, 0, sizeof(scp->recv_buf)); - memset_io(scp->send_buf, 0, sizeof(scp->send_buf)); + memset_io(scp->recv_buf, 0, sizeof(*scp->recv_buf)); + memset_io(scp->send_buf, 0, sizeof(*scp->send_buf)); return 0; } diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c index 60650bcc8c67..9028cea2d81e 100644 --- a/drivers/remoteproc/qcom_common.c +++ b/drivers/remoteproc/qcom_common.c @@ -42,12 +42,21 @@ static void glink_subdev_stop(struct rproc_subdev *subdev, bool crashed) glink->edge = NULL; } +static void glink_subdev_unprepare(struct rproc_subdev *subdev) +{ + struct qcom_rproc_glink *glink = to_glink_subdev(subdev); + + qcom_glink_ssr_notify(glink->ssr_name); +} + /** * qcom_add_glink_subdev() - try to add a GLINK subdevice to rproc * @rproc: rproc handle to parent the subdevice * @glink: reference to a GLINK subdev context + * @ssr_name: identifier of the associated remoteproc for ssr notifications */ -void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink) +void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink, + const char *ssr_name) { struct device *dev = &rproc->dev; @@ -55,9 +64,14 @@ void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink) if (!glink->node) return; + glink->ssr_name = kstrdup_const(ssr_name, GFP_KERNEL); + if (!glink->ssr_name) + return; + glink->dev = dev; glink->subdev.start = glink_subdev_start; glink->subdev.stop = glink_subdev_stop; + glink->subdev.unprepare = glink_subdev_unprepare; rproc_add_subdev(rproc, &glink->subdev); } @@ -74,6 +88,7 @@ void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glin return; rproc_remove_subdev(rproc, &glink->subdev); + kfree_const(glink->ssr_name); of_node_put(glink->node); } EXPORT_SYMBOL_GPL(qcom_remove_glink_subdev); diff --git a/drivers/remoteproc/qcom_common.h b/drivers/remoteproc/qcom_common.h index 58de71e4781c..34e5188187dc 100644 --- a/drivers/remoteproc/qcom_common.h +++ b/drivers/remoteproc/qcom_common.h @@ -11,6 +11,8 @@ struct qcom_sysmon; struct qcom_rproc_glink { struct rproc_subdev subdev; + const char *ssr_name; + struct device *dev; struct device_node *node; struct qcom_glink *edge; @@ -30,7 +32,8 @@ struct qcom_rproc_ssr { const char *name; }; -void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink); +void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink, + const char *ssr_name); void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink); int qcom_register_dump_segments(struct rproc *rproc, const struct firmware *fw); diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c index 24a3db961d5e..d2a2574dcf35 100644 --- a/drivers/remoteproc/qcom_q6v5_adsp.c +++ b/drivers/remoteproc/qcom_q6v5_adsp.c @@ -431,6 +431,7 @@ static int adsp_probe(struct platform_device *pdev) dev_err(&pdev->dev, "unable to allocate remoteproc\n"); return -ENOMEM; } + rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE); adsp = (struct qcom_adsp *)rproc->priv; adsp->dev = &pdev->dev; @@ -460,7 +461,7 @@ static int adsp_probe(struct platform_device *pdev) if (ret) goto disable_pm; - qcom_add_glink_subdev(rproc, &adsp->glink_subdev); + qcom_add_glink_subdev(rproc, &adsp->glink_subdev, desc->ssr_name); qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name); adsp->sysmon = qcom_add_sysmon_subdev(rproc, desc->sysmon_name, diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index 5475d4f808a8..feb70283b6a2 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -69,13 +69,9 @@ #define AXI_HALTREQ_REG 0x0 #define AXI_HALTACK_REG 0x4 #define AXI_IDLE_REG 0x8 -#define NAV_AXI_HALTREQ_BIT BIT(0) -#define NAV_AXI_HALTACK_BIT BIT(1) -#define NAV_AXI_IDLE_BIT BIT(2) #define AXI_GATING_VALID_OVERRIDE BIT(0) #define HALT_ACK_TIMEOUT_US 100000 -#define NAV_HALT_ACK_TIMEOUT_US 200 /* QDSP6SS_RESET */ #define Q6SS_STOP_CORE BIT(0) @@ -143,7 +139,7 @@ struct rproc_hexagon_res { int version; bool need_mem_protection; bool has_alt_reset; - bool has_halt_nav; + bool has_spare_reg; }; struct q6v5 { @@ -154,13 +150,11 @@ struct q6v5 { void __iomem *rmb_base; struct regmap *halt_map; - struct regmap *halt_nav_map; struct regmap *conn_map; u32 halt_q6; u32 halt_modem; u32 halt_nc; - u32 halt_nav; u32 conn_box; struct reset_control *mss_restart; @@ -196,7 +190,6 @@ struct q6v5 { phys_addr_t mpss_phys; phys_addr_t mpss_reloc; - void *mpss_region; size_t mpss_size; struct qcom_rproc_glink glink_subdev; @@ -206,7 +199,7 @@ struct q6v5 { struct qcom_sysmon *sysmon; bool need_mem_protection; bool has_alt_reset; - bool has_halt_nav; + bool has_spare_reg; int mpss_perm; int mba_perm; const char *hexagon_mdt_image; @@ -427,21 +420,19 @@ static int q6v5_reset_assert(struct q6v5 *qproc) reset_control_assert(qproc->pdc_reset); ret = reset_control_reset(qproc->mss_restart); reset_control_deassert(qproc->pdc_reset); - } else if (qproc->has_halt_nav) { + } else if (qproc->has_spare_reg) { /* * When the AXI pipeline is being reset with the Q6 modem partly * operational there is possibility of AXI valid signal to * glitch, leading to spurious transactions and Q6 hangs. A work * around is employed by asserting the AXI_GATING_VALID_OVERRIDE - * BIT before triggering Q6 MSS reset. Both the HALTREQ and - * AXI_GATING_VALID_OVERRIDE are withdrawn post MSS assert - * followed by a MSS deassert, while holding the PDC reset. + * BIT before triggering Q6 MSS reset. AXI_GATING_VALID_OVERRIDE + * is withdrawn post MSS assert followed by a MSS deassert, + * while holding the PDC reset. */ reset_control_assert(qproc->pdc_reset); regmap_update_bits(qproc->conn_map, qproc->conn_box, AXI_GATING_VALID_OVERRIDE, 1); - regmap_update_bits(qproc->halt_nav_map, qproc->halt_nav, - NAV_AXI_HALTREQ_BIT, 0); reset_control_assert(qproc->mss_restart); reset_control_deassert(qproc->pdc_reset); regmap_update_bits(qproc->conn_map, qproc->conn_box, @@ -464,7 +455,7 @@ static int q6v5_reset_deassert(struct q6v5 *qproc) ret = reset_control_reset(qproc->mss_restart); writel(0, qproc->rmb_base + RMB_MBA_ALT_RESET); reset_control_deassert(qproc->pdc_reset); - } else if (qproc->has_halt_nav) { + } else if (qproc->has_spare_reg) { ret = reset_control_reset(qproc->mss_restart); } else { ret = reset_control_deassert(qproc->mss_restart); @@ -761,32 +752,6 @@ static void q6v5proc_halt_axi_port(struct q6v5 *qproc, regmap_write(halt_map, offset + AXI_HALTREQ_REG, 0); } -static void q6v5proc_halt_nav_axi_port(struct q6v5 *qproc, - struct regmap *halt_map, - u32 offset) -{ - unsigned int val; - int ret; - - /* Check if we're already idle */ - ret = regmap_read(halt_map, offset, &val); - if (!ret && (val & NAV_AXI_IDLE_BIT)) - return; - - /* Assert halt request */ - regmap_update_bits(halt_map, offset, NAV_AXI_HALTREQ_BIT, - NAV_AXI_HALTREQ_BIT); - - /* Wait for halt ack*/ - regmap_read_poll_timeout(halt_map, offset, val, - (val & NAV_AXI_HALTACK_BIT), - 5, NAV_HALT_ACK_TIMEOUT_US); - - ret = regmap_read(halt_map, offset, &val); - if (ret || !(val & NAV_AXI_IDLE_BIT)) - dev_err(qproc->dev, "port failed halt\n"); -} - static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw) { unsigned long dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS; @@ -951,9 +916,6 @@ static int q6v5_mba_load(struct q6v5 *qproc) halt_axi_ports: q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem); - if (qproc->has_halt_nav) - q6v5proc_halt_nav_axi_port(qproc, qproc->halt_nav_map, - qproc->halt_nav); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc); reclaim_mba: @@ -1001,9 +963,6 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc) q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem); - if (qproc->has_halt_nav) - q6v5proc_halt_nav_axi_port(qproc, qproc->halt_nav_map, - qproc->halt_nav); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc); if (qproc->version == MSS_MSM8996) { /* @@ -1156,7 +1115,13 @@ static int q6v5_mpss_load(struct q6v5 *qproc) goto release_firmware; } - ptr = qproc->mpss_region + offset; + ptr = ioremap_wc(qproc->mpss_phys + offset, phdr->p_memsz); + if (!ptr) { + dev_err(qproc->dev, + "unable to map memory region: %pa+%zx-%x\n", + &qproc->mpss_phys, offset, phdr->p_memsz); + goto release_firmware; + } if (phdr->p_filesz && phdr->p_offset < fw->size) { /* Firmware is large enough to be non-split */ @@ -1165,6 +1130,7 @@ static int q6v5_mpss_load(struct q6v5 *qproc) "failed to load segment %d from truncated file %s\n", i, fw_name); ret = -EINVAL; + iounmap(ptr); goto release_firmware; } @@ -1175,6 +1141,7 @@ static int q6v5_mpss_load(struct q6v5 *qproc) ret = request_firmware(&seg_fw, fw_name, qproc->dev); if (ret) { dev_err(qproc->dev, "failed to load %s\n", fw_name); + iounmap(ptr); goto release_firmware; } @@ -1187,6 +1154,7 @@ static int q6v5_mpss_load(struct q6v5 *qproc) memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz); } + iounmap(ptr); size += phdr->p_memsz; code_length = readl(qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG); @@ -1236,7 +1204,8 @@ static void qcom_q6v5_dump_segment(struct rproc *rproc, int ret = 0; struct q6v5 *qproc = rproc->priv; unsigned long mask = BIT((unsigned long)segment->priv); - void *ptr = rproc_da_to_va(rproc, segment->da, segment->size); + int offset = segment->da - qproc->mpss_reloc; + void *ptr = NULL; /* Unlock mba before copying segments */ if (!qproc->dump_mba_loaded) { @@ -1250,10 +1219,15 @@ static void qcom_q6v5_dump_segment(struct rproc *rproc, } } - if (!ptr || ret) - memset(dest, 0xff, segment->size); - else + if (!ret) + ptr = ioremap_wc(qproc->mpss_phys + offset, segment->size); + + if (ptr) { memcpy(dest, ptr, segment->size); + iounmap(ptr); + } else { + memset(dest, 0xff, segment->size); + } qproc->dump_segment_mask |= mask; @@ -1327,18 +1301,6 @@ static int q6v5_stop(struct rproc *rproc) return 0; } -static void *q6v5_da_to_va(struct rproc *rproc, u64 da, size_t len) -{ - struct q6v5 *qproc = rproc->priv; - int offset; - - offset = da - qproc->mpss_reloc; - if (offset < 0 || offset + len > qproc->mpss_size) - return NULL; - - return qproc->mpss_region + offset; -} - static int qcom_q6v5_register_dump_segments(struct rproc *rproc, const struct firmware *mba_fw) { @@ -1357,6 +1319,8 @@ static int qcom_q6v5_register_dump_segments(struct rproc *rproc, return ret; } + rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE); + ehdr = (struct elf32_hdr *)fw->data; phdrs = (struct elf32_phdr *)(ehdr + 1); qproc->dump_complete_mask = 0; @@ -1384,7 +1348,6 @@ static int qcom_q6v5_register_dump_segments(struct rproc *rproc, static const struct rproc_ops q6v5_ops = { .start = q6v5_start, .stop = q6v5_stop, - .da_to_va = q6v5_da_to_va, .parse_fw = qcom_q6v5_register_dump_segments, .load = q6v5_load, }; @@ -1432,36 +1395,12 @@ static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev) qproc->halt_modem = args.args[1]; qproc->halt_nc = args.args[2]; - if (qproc->has_halt_nav) { - struct platform_device *nav_pdev; - + if (qproc->has_spare_reg) { ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, - "qcom,halt-nav-regs", + "qcom,spare-regs", 1, 0, &args); if (ret < 0) { - dev_err(&pdev->dev, "failed to parse halt-nav-regs\n"); - return -EINVAL; - } - - nav_pdev = of_find_device_by_node(args.np); - of_node_put(args.np); - if (!nav_pdev) { - dev_err(&pdev->dev, "failed to get mss clock device\n"); - return -EPROBE_DEFER; - } - - qproc->halt_nav_map = dev_get_regmap(&nav_pdev->dev, NULL); - if (!qproc->halt_nav_map) { - dev_err(&pdev->dev, "failed to get map from device\n"); - return -EINVAL; - } - qproc->halt_nav = args.args[0]; - - ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, - "qcom,halt-nav-regs", - 1, 1, &args); - if (ret < 0) { - dev_err(&pdev->dev, "failed to parse halt-nav-regs\n"); + dev_err(&pdev->dev, "failed to parse spare-regs\n"); return -EINVAL; } @@ -1547,7 +1486,7 @@ static int q6v5_init_reset(struct q6v5 *qproc) return PTR_ERR(qproc->mss_restart); } - if (qproc->has_alt_reset || qproc->has_halt_nav) { + if (qproc->has_alt_reset || qproc->has_spare_reg) { qproc->pdc_reset = devm_reset_control_get_exclusive(qproc->dev, "pdc_reset"); if (IS_ERR(qproc->pdc_reset)) { @@ -1566,8 +1505,17 @@ static int q6v5_alloc_memory_region(struct q6v5 *qproc) struct resource r; int ret; + /* + * In the absence of mba/mpss sub-child, extract the mba and mpss + * reserved memory regions from device's memory-region property. + */ child = of_get_child_by_name(qproc->dev->of_node, "mba"); - node = of_parse_phandle(child, "memory-region", 0); + if (!child) + node = of_parse_phandle(qproc->dev->of_node, + "memory-region", 0); + else + node = of_parse_phandle(child, "memory-region", 0); + ret = of_address_to_resource(node, 0, &r); if (ret) { dev_err(qproc->dev, "unable to resolve mba region\n"); @@ -1584,8 +1532,14 @@ static int q6v5_alloc_memory_region(struct q6v5 *qproc) return -EBUSY; } - child = of_get_child_by_name(qproc->dev->of_node, "mpss"); - node = of_parse_phandle(child, "memory-region", 0); + if (!child) { + node = of_parse_phandle(qproc->dev->of_node, + "memory-region", 1); + } else { + child = of_get_child_by_name(qproc->dev->of_node, "mpss"); + node = of_parse_phandle(child, "memory-region", 0); + } + ret = of_address_to_resource(node, 0, &r); if (ret) { dev_err(qproc->dev, "unable to resolve mpss region\n"); @@ -1595,12 +1549,6 @@ static int q6v5_alloc_memory_region(struct q6v5 *qproc) qproc->mpss_phys = qproc->mpss_reloc = r.start; qproc->mpss_size = resource_size(&r); - qproc->mpss_region = devm_ioremap_wc(qproc->dev, qproc->mpss_phys, qproc->mpss_size); - if (!qproc->mpss_region) { - dev_err(qproc->dev, "unable to map memory region: %pa+%zx\n", - &r.start, qproc->mpss_size); - return -EBUSY; - } return 0; } @@ -1667,6 +1615,7 @@ static int q6v5_probe(struct platform_device *pdev) } rproc->auto_boot = false; + rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE); qproc = (struct q6v5 *)rproc->priv; qproc->dev = &pdev->dev; @@ -1679,7 +1628,7 @@ static int q6v5_probe(struct platform_device *pdev) platform_set_drvdata(pdev, qproc); - qproc->has_halt_nav = desc->has_halt_nav; + qproc->has_spare_reg = desc->has_spare_reg; ret = q6v5_init_mem(qproc, pdev); if (ret) goto free_rproc; @@ -1759,7 +1708,7 @@ static int q6v5_probe(struct platform_device *pdev) qproc->mpss_perm = BIT(QCOM_SCM_VMID_HLOS); qproc->mba_perm = BIT(QCOM_SCM_VMID_HLOS); - qcom_add_glink_subdev(rproc, &qproc->glink_subdev); + qcom_add_glink_subdev(rproc, &qproc->glink_subdev, "mpss"); qcom_add_smd_subdev(rproc, &qproc->smd_subdev); qcom_add_ssr_subdev(rproc, &qproc->ssr_subdev, "mpss"); qcom_add_ipa_notify_subdev(rproc, &qproc->ipa_notify_subdev); @@ -1828,8 +1777,6 @@ static const struct rproc_hexagon_res sc7180_mss = { .active_clk_names = (char*[]){ "mnoc_axi", "nav", - "mss_nav", - "mss_crypto", NULL }, .active_pd_names = (char*[]){ @@ -1844,7 +1791,7 @@ static const struct rproc_hexagon_res sc7180_mss = { }, .need_mem_protection = true, .has_alt_reset = false, - .has_halt_nav = true, + .has_spare_reg = true, .version = MSS_SC7180, }; @@ -1879,7 +1826,7 @@ static const struct rproc_hexagon_res sdm845_mss = { }, .need_mem_protection = true, .has_alt_reset = true, - .has_halt_nav = false, + .has_spare_reg = false, .version = MSS_SDM845, }; @@ -1906,7 +1853,7 @@ static const struct rproc_hexagon_res msm8998_mss = { }, .need_mem_protection = true, .has_alt_reset = false, - .has_halt_nav = false, + .has_spare_reg = false, .version = MSS_MSM8998, }; @@ -1936,7 +1883,7 @@ static const struct rproc_hexagon_res msm8996_mss = { }, .need_mem_protection = true, .has_alt_reset = false, - .has_halt_nav = false, + .has_spare_reg = false, .version = MSS_MSM8996, }; @@ -1969,7 +1916,7 @@ static const struct rproc_hexagon_res msm8916_mss = { }, .need_mem_protection = false, .has_alt_reset = false, - .has_halt_nav = false, + .has_spare_reg = false, .version = MSS_MSM8916, }; @@ -2010,7 +1957,7 @@ static const struct rproc_hexagon_res msm8974_mss = { }, .need_mem_protection = false, .has_alt_reset = false, - .has_halt_nav = false, + .has_spare_reg = false, .version = MSS_MSM8974, }; diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index 7a63efb85405..61791a03f648 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -398,6 +398,7 @@ static int adsp_probe(struct platform_device *pdev) } rproc->auto_boot = desc->auto_boot; + rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE); adsp = (struct qcom_adsp *)rproc->priv; adsp->dev = &pdev->dev; @@ -406,6 +407,8 @@ static int adsp_probe(struct platform_device *pdev) adsp->has_aggre2_clk = desc->has_aggre2_clk; platform_set_drvdata(pdev, adsp); + device_wakeup_enable(adsp->dev); + ret = adsp_alloc_memory_region(adsp); if (ret) goto free_rproc; @@ -435,7 +438,7 @@ static int adsp_probe(struct platform_device *pdev) if (ret) goto detach_proxy_pds; - qcom_add_glink_subdev(rproc, &adsp->glink_subdev); + qcom_add_glink_subdev(rproc, &adsp->glink_subdev, desc->ssr_name); qcom_add_smd_subdev(rproc, &adsp->smd_subdev); qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name); adsp->sysmon = qcom_add_sysmon_subdev(rproc, @@ -507,6 +510,26 @@ static const struct adsp_data sm8150_adsp_resource = { .ssctl_id = 0x14, }; +static const struct adsp_data sm8250_adsp_resource = { + .crash_reason_smem = 423, + .firmware_name = "adsp.mdt", + .pas_id = 1, + .has_aggre2_clk = false, + .auto_boot = true, + .active_pd_names = (char*[]){ + "load_state", + NULL + }, + .proxy_pd_names = (char*[]){ + "lcx", + "lmx", + NULL + }, + .ssr_name = "lpass", + .sysmon_name = "adsp", + .ssctl_id = 0x14, +}; + static const struct adsp_data msm8998_adsp_resource = { .crash_reason_smem = 423, .firmware_name = "adsp.mdt", @@ -552,6 +575,25 @@ static const struct adsp_data sm8150_cdsp_resource = { .ssctl_id = 0x17, }; +static const struct adsp_data sm8250_cdsp_resource = { + .crash_reason_smem = 601, + .firmware_name = "cdsp.mdt", + .pas_id = 18, + .has_aggre2_clk = false, + .auto_boot = true, + .active_pd_names = (char*[]){ + "load_state", + NULL + }, + .proxy_pd_names = (char*[]){ + "cx", + NULL + }, + .ssr_name = "cdsp", + .sysmon_name = "cdsp", + .ssctl_id = 0x17, +}; + static const struct adsp_data mpss_resource_init = { .crash_reason_smem = 421, .firmware_name = "modem.mdt", @@ -603,6 +645,26 @@ static const struct adsp_data sm8150_slpi_resource = { .ssctl_id = 0x16, }; +static const struct adsp_data sm8250_slpi_resource = { + .crash_reason_smem = 424, + .firmware_name = "slpi.mdt", + .pas_id = 12, + .has_aggre2_clk = false, + .auto_boot = true, + .active_pd_names = (char*[]){ + "load_state", + NULL + }, + .proxy_pd_names = (char*[]){ + "lcx", + "lmx", + NULL + }, + .ssr_name = "dsps", + .sysmon_name = "slpi", + .ssctl_id = 0x16, +}; + static const struct adsp_data msm8998_slpi_resource = { .crash_reason_smem = 424, .firmware_name = "slpi.mdt", @@ -637,12 +699,16 @@ static const struct of_device_id adsp_of_match[] = { { .compatible = "qcom,qcs404-adsp-pas", .data = &adsp_resource_init }, { .compatible = "qcom,qcs404-cdsp-pas", .data = &cdsp_resource_init }, { .compatible = "qcom,qcs404-wcss-pas", .data = &wcss_resource_init }, + { .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,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}, { .compatible = "qcom,sm8150-slpi-pas", .data = &sm8150_slpi_resource}, + { .compatible = "qcom,sm8250-adsp-pas", .data = &sm8250_adsp_resource}, + { .compatible = "qcom,sm8250-cdsp-pas", .data = &sm8250_cdsp_resource}, + { .compatible = "qcom,sm8250-slpi-pas", .data = &sm8250_slpi_resource}, { }, }; MODULE_DEVICE_TABLE(of, adsp_of_match); diff --git a/drivers/remoteproc/qcom_q6v5_wcss.c b/drivers/remoteproc/qcom_q6v5_wcss.c index f1924b740a10..88c76b9417fa 100644 --- a/drivers/remoteproc/qcom_q6v5_wcss.c +++ b/drivers/remoteproc/qcom_q6v5_wcss.c @@ -91,6 +91,9 @@ struct q6v5_wcss { phys_addr_t mem_reloc; void *mem_region; size_t mem_size; + + struct qcom_rproc_glink glink_subdev; + struct qcom_rproc_ssr ssr_subdev; }; static int q6v5_wcss_reset(struct q6v5_wcss *wcss) @@ -557,6 +560,9 @@ static int q6v5_wcss_probe(struct platform_device *pdev) if (ret) goto free_rproc; + qcom_add_glink_subdev(rproc, &wcss->glink_subdev, "q6wcss"); + qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, "q6wcss"); + ret = rproc_add(rproc); if (ret) goto free_rproc; diff --git a/drivers/remoteproc/qcom_sysmon.c b/drivers/remoteproc/qcom_sysmon.c index faf3822d8791..8d8996d714f0 100644 --- a/drivers/remoteproc/qcom_sysmon.c +++ b/drivers/remoteproc/qcom_sysmon.c @@ -46,6 +46,25 @@ struct qcom_sysmon { struct sockaddr_qrtr ssctl; }; +enum { + SSCTL_SSR_EVENT_BEFORE_POWERUP, + SSCTL_SSR_EVENT_AFTER_POWERUP, + SSCTL_SSR_EVENT_BEFORE_SHUTDOWN, + SSCTL_SSR_EVENT_AFTER_SHUTDOWN, +}; + +static const char * const sysmon_state_string[] = { + [SSCTL_SSR_EVENT_BEFORE_POWERUP] = "before_powerup", + [SSCTL_SSR_EVENT_AFTER_POWERUP] = "after_powerup", + [SSCTL_SSR_EVENT_BEFORE_SHUTDOWN] = "before_shutdown", + [SSCTL_SSR_EVENT_AFTER_SHUTDOWN] = "after_shutdown", +}; + +struct sysmon_event { + const char *subsys_name; + u32 ssr_event; +}; + static DEFINE_MUTEX(sysmon_lock); static LIST_HEAD(sysmon_list); @@ -54,13 +73,15 @@ static LIST_HEAD(sysmon_list); * @sysmon: sysmon context * @name: other remote's name */ -static void sysmon_send_event(struct qcom_sysmon *sysmon, const char *name) +static void sysmon_send_event(struct qcom_sysmon *sysmon, + const struct sysmon_event *event) { char req[50]; int len; int ret; - len = snprintf(req, sizeof(req), "ssr:%s:before_shutdown", name); + len = snprintf(req, sizeof(req), "ssr:%s:%s", event->subsys_name, + sysmon_state_string[event->ssr_event]); if (len >= sizeof(req)) return; @@ -149,13 +170,6 @@ static int sysmon_callback(struct rpmsg_device *rpdev, void *data, int count, #define SSCTL_SUBSYS_NAME_LENGTH 15 enum { - SSCTL_SSR_EVENT_BEFORE_POWERUP, - SSCTL_SSR_EVENT_AFTER_POWERUP, - SSCTL_SSR_EVENT_BEFORE_SHUTDOWN, - SSCTL_SSR_EVENT_AFTER_SHUTDOWN, -}; - -enum { SSCTL_SSR_EVENT_FORCED, SSCTL_SSR_EVENT_GRACEFUL, }; @@ -331,7 +345,8 @@ static void ssctl_request_shutdown(struct qcom_sysmon *sysmon) * @sysmon: sysmon context * @name: other remote's name */ -static void ssctl_send_event(struct qcom_sysmon *sysmon, const char *name) +static void ssctl_send_event(struct qcom_sysmon *sysmon, + const struct sysmon_event *event) { struct ssctl_subsys_event_resp resp; struct ssctl_subsys_event_req req; @@ -346,9 +361,9 @@ static void ssctl_send_event(struct qcom_sysmon *sysmon, const char *name) } memset(&req, 0, sizeof(req)); - strlcpy(req.subsys_name, name, sizeof(req.subsys_name)); + strlcpy(req.subsys_name, event->subsys_name, sizeof(req.subsys_name)); req.subsys_name_len = strlen(req.subsys_name); - req.event = SSCTL_SSR_EVENT_BEFORE_SHUTDOWN; + req.event = event->ssr_event; req.evt_driven_valid = true; req.evt_driven = SSCTL_SSR_EVENT_FORCED; @@ -424,16 +439,68 @@ static const struct qmi_ops ssctl_ops = { .del_server = ssctl_del_server, }; +static int sysmon_prepare(struct rproc_subdev *subdev) +{ + struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, + subdev); + struct sysmon_event event = { + .subsys_name = sysmon->name, + .ssr_event = SSCTL_SSR_EVENT_BEFORE_POWERUP + }; + + blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event); + + return 0; +} + +/** + * sysmon_start() - start callback for the sysmon remoteproc subdevice + * @subdev: instance of the sysmon subdevice + * + * Inform all the listners of sysmon notifications that the rproc associated + * to @subdev has booted up. The rproc that booted up also needs to know + * which rprocs are already up and running, so send start notifications + * on behalf of all the online rprocs. + */ static int sysmon_start(struct rproc_subdev *subdev) { + struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, + subdev); + struct qcom_sysmon *target; + struct sysmon_event event = { + .subsys_name = sysmon->name, + .ssr_event = SSCTL_SSR_EVENT_AFTER_POWERUP + }; + + blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event); + + mutex_lock(&sysmon_lock); + list_for_each_entry(target, &sysmon_list, node) { + if (target == sysmon || + target->rproc->state != RPROC_RUNNING) + continue; + + event.subsys_name = target->name; + + if (sysmon->ssctl_version == 2) + ssctl_send_event(sysmon, &event); + else if (sysmon->ept) + sysmon_send_event(sysmon, &event); + } + mutex_unlock(&sysmon_lock); + return 0; } static void sysmon_stop(struct rproc_subdev *subdev, bool crashed) { struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, subdev); + struct sysmon_event event = { + .subsys_name = sysmon->name, + .ssr_event = SSCTL_SSR_EVENT_BEFORE_SHUTDOWN + }; - blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)sysmon->name); + blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event); /* Don't request graceful shutdown if we've crashed */ if (crashed) @@ -445,6 +512,18 @@ static void sysmon_stop(struct rproc_subdev *subdev, bool crashed) sysmon_request_shutdown(sysmon); } +static void sysmon_unprepare(struct rproc_subdev *subdev) +{ + struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, + subdev); + struct sysmon_event event = { + .subsys_name = sysmon->name, + .ssr_event = SSCTL_SSR_EVENT_AFTER_SHUTDOWN + }; + + blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event); +} + /** * sysmon_notify() - notify sysmon target of another's SSR * @nb: notifier_block associated with sysmon instance @@ -456,19 +535,20 @@ static int sysmon_notify(struct notifier_block *nb, unsigned long event, { struct qcom_sysmon *sysmon = container_of(nb, struct qcom_sysmon, nb); struct rproc *rproc = sysmon->rproc; - const char *ssr_name = data; + struct sysmon_event *sysmon_event = data; /* Skip non-running rprocs and the originating instance */ - if (rproc->state != RPROC_RUNNING || !strcmp(data, sysmon->name)) { + if (rproc->state != RPROC_RUNNING || + !strcmp(sysmon_event->subsys_name, sysmon->name)) { dev_dbg(sysmon->dev, "not notifying %s\n", sysmon->name); return NOTIFY_DONE; } /* Only SSCTL version 2 supports SSR events */ if (sysmon->ssctl_version == 2) - ssctl_send_event(sysmon, ssr_name); + ssctl_send_event(sysmon, sysmon_event); else if (sysmon->ept) - sysmon_send_event(sysmon, ssr_name); + sysmon_send_event(sysmon, sysmon_event); return NOTIFY_DONE; } @@ -543,8 +623,10 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, qmi_add_lookup(&sysmon->qmi, 43, 0, 0); + sysmon->subdev.prepare = sysmon_prepare; sysmon->subdev.start = sysmon_start; sysmon->subdev.stop = sysmon_stop; + sysmon->subdev.unprepare = sysmon_unprepare; rproc_add_subdev(rproc, &sysmon->subdev); diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c index 0c7afd038f0d..5d65e1a9329a 100644 --- a/drivers/remoteproc/qcom_wcnss.c +++ b/drivers/remoteproc/qcom_wcnss.c @@ -480,6 +480,7 @@ static int wcnss_probe(struct platform_device *pdev) dev_err(&pdev->dev, "unable to allocate remoteproc\n"); return -ENOMEM; } + rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE); wcnss = (struct qcom_wcnss *)rproc->priv; wcnss->dev = &pdev->dev; diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index e12a54e67588..9f04c30c4aaf 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -29,6 +29,7 @@ #include <linux/devcoredump.h> #include <linux/rculist.h> #include <linux/remoteproc.h> +#include <linux/pm_runtime.h> #include <linux/iommu.h> #include <linux/idr.h> #include <linux/elf.h> @@ -517,7 +518,7 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, /* Initialise vdev subdevice */ snprintf(name, sizeof(name), "vdev%dbuffer", rvdev->index); - rvdev->dev.parent = rproc->dev.parent; + rvdev->dev.parent = &rproc->dev; rvdev->dev.dma_pfn_offset = rproc->dev.parent->dma_pfn_offset; rvdev->dev.release = rproc_rvdev_release; dev_set_name(&rvdev->dev, "%s#%s", dev_name(rvdev->dev.parent), name); @@ -1382,6 +1383,12 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw) if (ret) return ret; + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "pm_runtime_get_sync failed: %d\n", ret); + return ret; + } + dev_info(dev, "Booting fw image %s, size %zd\n", name, fw->size); /* @@ -1391,7 +1398,14 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw) ret = rproc_enable_iommu(rproc); if (ret) { dev_err(dev, "can't enable iommu: %d\n", ret); - return ret; + goto put_pm_runtime; + } + + /* Prepare rproc for firmware loading if needed */ + ret = rproc_prepare_device(rproc); + if (ret) { + dev_err(dev, "can't prepare rproc %s: %d\n", rproc->name, ret); + goto disable_iommu; } rproc->bootaddr = rproc_get_boot_addr(rproc, fw); @@ -1399,7 +1413,7 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw) /* Load resource table, core dump segment list etc from the firmware */ ret = rproc_parse_fw(rproc, fw); if (ret) - goto disable_iommu; + goto unprepare_rproc; /* reset max_notifyid */ rproc->max_notifyid = -1; @@ -1433,8 +1447,13 @@ clean_up_resources: kfree(rproc->cached_table); rproc->cached_table = NULL; rproc->table_ptr = NULL; +unprepare_rproc: + /* release HW resources if needed */ + rproc_unprepare_device(rproc); disable_iommu: rproc_disable_iommu(rproc); +put_pm_runtime: + pm_runtime_put(dev); return ret; } @@ -1566,6 +1585,28 @@ int rproc_coredump_add_custom_segment(struct rproc *rproc, 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); + +/** * rproc_coredump() - perform coredump * @rproc: rproc handle * @@ -1587,6 +1628,11 @@ static void rproc_coredump(struct rproc *rproc) if (list_empty(&rproc->dump_segments)) 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) { data_size += elf_size_of_phdr(class) + segment->size; @@ -1605,7 +1651,7 @@ static void rproc_coredump(struct rproc *rproc) elf_hdr_init_ident(ehdr, class); elf_hdr_set_e_type(class, ehdr, ET_CORE); - elf_hdr_set_e_machine(class, ehdr, EM_NONE); + 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)); @@ -1729,6 +1775,8 @@ static void rproc_crash_handler_work(struct work_struct *work) if (!rproc->recovery_disabled) rproc_trigger_recovery(rproc); + + pm_relax(rproc->dev.parent); } /** @@ -1838,8 +1886,13 @@ void rproc_shutdown(struct rproc *rproc) /* clean up all acquired resources */ rproc_resource_cleanup(rproc); + /* release HW resources if needed */ + rproc_unprepare_device(rproc); + rproc_disable_iommu(rproc); + pm_runtime_put(dev); + /* Free the copy of the resource table */ kfree(rproc->cached_table); rproc->cached_table = NULL; @@ -1949,6 +2002,33 @@ int rproc_add(struct rproc *rproc) } EXPORT_SYMBOL(rproc_add); +static void devm_rproc_remove(void *rproc) +{ + rproc_del(rproc); +} + +/** + * devm_rproc_add() - resource managed rproc_add() + * @dev: the underlying device + * @rproc: the remote processor handle to register + * + * This function performs like rproc_add() but the registered rproc device will + * automatically be removed on driver detach. + * + * Returns: 0 on success, negative errno on failure + */ +int devm_rproc_add(struct device *dev, struct rproc *rproc) +{ + int err; + + err = rproc_add(rproc); + if (err) + return err; + + return devm_add_action_or_reset(dev, devm_rproc_remove, rproc); +} +EXPORT_SYMBOL(devm_rproc_add); + /** * rproc_type_release() - release a remote processor instance * @dev: the rproc's device @@ -1969,7 +2049,8 @@ static void rproc_type_release(struct device *dev) if (rproc->index >= 0) ida_simple_remove(&rproc_dev_index, rproc->index); - kfree(rproc->firmware); + kfree_const(rproc->firmware); + kfree_const(rproc->name); kfree(rproc->ops); kfree(rproc); } @@ -1979,6 +2060,47 @@ static const struct device_type rproc_type = { .release = rproc_type_release, }; +static int rproc_alloc_firmware(struct rproc *rproc, + const char *name, const char *firmware) +{ + const char *p; + + /* + * Allocate a firmware name if the caller gave us one to work + * with. Otherwise construct a new one using a default pattern. + */ + if (firmware) + p = kstrdup_const(firmware, GFP_KERNEL); + else + p = kasprintf(GFP_KERNEL, "rproc-%s-fw", name); + + if (!p) + return -ENOMEM; + + rproc->firmware = p; + + return 0; +} + +static int rproc_alloc_ops(struct rproc *rproc, const struct rproc_ops *ops) +{ + rproc->ops = kmemdup(ops, sizeof(*ops), GFP_KERNEL); + if (!rproc->ops) + return -ENOMEM; + + if (rproc->ops->load) + return 0; + + /* Default to ELF loader if no load function is specified */ + rproc->ops->load = rproc_elf_load_segments; + rproc->ops->parse_fw = rproc_elf_load_rsc_table; + rproc->ops->find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table; + rproc->ops->sanity_check = rproc_elf_sanity_check; + rproc->ops->get_boot_addr = rproc_elf_get_boot_addr; + + return 0; +} + /** * rproc_alloc() - allocate a remote processor handle * @dev: the underlying device @@ -2007,79 +2129,49 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, const char *firmware, int len) { struct rproc *rproc; - char *p, *template = "rproc-%s-fw"; - int name_len; if (!dev || !name || !ops) return NULL; - if (!firmware) { - /* - * If the caller didn't pass in a firmware name then - * construct a default name. - */ - name_len = strlen(name) + strlen(template) - 2 + 1; - p = kmalloc(name_len, GFP_KERNEL); - if (!p) - return NULL; - snprintf(p, name_len, template, name); - } else { - p = kstrdup(firmware, GFP_KERNEL); - if (!p) - return NULL; - } - rproc = kzalloc(sizeof(struct rproc) + len, GFP_KERNEL); - if (!rproc) { - kfree(p); - return NULL; - } - - rproc->ops = kmemdup(ops, sizeof(*ops), GFP_KERNEL); - if (!rproc->ops) { - kfree(p); - kfree(rproc); + if (!rproc) return NULL; - } - rproc->firmware = p; - rproc->name = name; rproc->priv = &rproc[1]; rproc->auto_boot = true; - rproc->elf_class = ELFCLASS32; + rproc->elf_class = ELFCLASSNONE; + rproc->elf_machine = EM_NONE; device_initialize(&rproc->dev); rproc->dev.parent = dev; rproc->dev.type = &rproc_type; rproc->dev.class = &rproc_class; rproc->dev.driver_data = rproc; + idr_init(&rproc->notifyids); + + rproc->name = kstrdup_const(name, GFP_KERNEL); + if (!rproc->name) + goto put_device; + + if (rproc_alloc_firmware(rproc, name, firmware)) + goto put_device; + + if (rproc_alloc_ops(rproc, ops)) + goto put_device; /* Assign a unique device index and name */ rproc->index = ida_simple_get(&rproc_dev_index, 0, 0, GFP_KERNEL); if (rproc->index < 0) { dev_err(dev, "ida_simple_get failed: %d\n", rproc->index); - put_device(&rproc->dev); - return NULL; + goto put_device; } dev_set_name(&rproc->dev, "remoteproc%d", rproc->index); atomic_set(&rproc->power, 0); - /* Default to ELF loader if no load function is specified */ - if (!rproc->ops->load) { - rproc->ops->load = rproc_elf_load_segments; - rproc->ops->parse_fw = rproc_elf_load_rsc_table; - rproc->ops->find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table; - if (!rproc->ops->sanity_check) - rproc->ops->sanity_check = rproc_elf32_sanity_check; - rproc->ops->get_boot_addr = rproc_elf_get_boot_addr; - } - mutex_init(&rproc->lock); - idr_init(&rproc->notifyids); - INIT_LIST_HEAD(&rproc->carveouts); INIT_LIST_HEAD(&rproc->mappings); INIT_LIST_HEAD(&rproc->traces); @@ -2091,7 +2183,14 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, rproc->state = RPROC_OFFLINE; + pm_runtime_no_callbacks(&rproc->dev); + pm_runtime_enable(&rproc->dev); + return rproc; + +put_device: + put_device(&rproc->dev); + return NULL; } EXPORT_SYMBOL(rproc_alloc); @@ -2106,6 +2205,7 @@ EXPORT_SYMBOL(rproc_alloc); */ void rproc_free(struct rproc *rproc) { + pm_runtime_disable(&rproc->dev); put_device(&rproc->dev); } EXPORT_SYMBOL(rproc_free); @@ -2171,6 +2271,46 @@ int rproc_del(struct rproc *rproc) } EXPORT_SYMBOL(rproc_del); +static void devm_rproc_free(struct device *dev, void *res) +{ + rproc_free(*(struct rproc **)res); +} + +/** + * devm_rproc_alloc() - resource managed rproc_alloc() + * @dev: the underlying device + * @name: name of this remote processor + * @ops: platform-specific handlers (mainly start/stop) + * @firmware: name of firmware file to load, can be NULL + * @len: length of private data needed by the rproc driver (in bytes) + * + * This function performs like rproc_alloc() but the acquired rproc device will + * automatically be released on driver detach. + * + * Returns: new rproc instance, or NULL on failure + */ +struct rproc *devm_rproc_alloc(struct device *dev, const char *name, + const struct rproc_ops *ops, + const char *firmware, int len) +{ + struct rproc **ptr, *rproc; + + ptr = devres_alloc(devm_rproc_free, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return NULL; + + rproc = rproc_alloc(dev, name, ops, firmware, len); + if (rproc) { + *ptr = rproc; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return rproc; +} +EXPORT_SYMBOL(devm_rproc_alloc); + /** * rproc_add_subdev() - add a subdevice to a remoteproc * @rproc: rproc handle to add the subdevice to @@ -2230,6 +2370,9 @@ void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type) return; } + /* Prevent suspend while the remoteproc is being recovered */ + pm_stay_awake(rproc->dev.parent); + dev_err(&rproc->dev, "crash detected in %s: type %s\n", rproc->name, rproc_crash_to_string(type)); diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c index d734cadb16e3..732770e92b99 100644 --- a/drivers/remoteproc/remoteproc_debugfs.c +++ b/drivers/remoteproc/remoteproc_debugfs.c @@ -269,17 +269,7 @@ static int rproc_rsc_table_show(struct seq_file *seq, void *p) return 0; } -static int rproc_rsc_table_open(struct inode *inode, struct file *file) -{ - return single_open(file, rproc_rsc_table_show, inode->i_private); -} - -static const struct file_operations rproc_rsc_table_ops = { - .open = rproc_rsc_table_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(rproc_rsc_table); /* Expose carveout content via debugfs */ static int rproc_carveouts_show(struct seq_file *seq, void *p) @@ -299,17 +289,7 @@ static int rproc_carveouts_show(struct seq_file *seq, void *p) return 0; } -static int rproc_carveouts_open(struct inode *inode, struct file *file) -{ - return single_open(file, rproc_carveouts_show, inode->i_private); -} - -static const struct file_operations rproc_carveouts_ops = { - .open = rproc_carveouts_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(rproc_carveouts); void rproc_remove_trace_file(struct dentry *tfile) { @@ -354,9 +334,9 @@ void rproc_create_debug_dir(struct rproc *rproc) debugfs_create_file("crash", 0200, rproc->dbg_dir, rproc, &rproc_crash_ops); debugfs_create_file("resource_table", 0400, rproc->dbg_dir, - rproc, &rproc_rsc_table_ops); + rproc, &rproc_rsc_table_fops); debugfs_create_file("carveout_memories", 0400, rproc->dbg_dir, - rproc, &rproc_carveouts_ops); + rproc, &rproc_carveouts_fops); } void __init rproc_init_debugfs(void) diff --git a/drivers/remoteproc/remoteproc_elf_loader.c b/drivers/remoteproc/remoteproc_elf_loader.c index 16e2c496fd45..df68d87752e4 100644 --- a/drivers/remoteproc/remoteproc_elf_loader.c +++ b/drivers/remoteproc/remoteproc_elf_loader.c @@ -113,27 +113,6 @@ int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw) EXPORT_SYMBOL(rproc_elf_sanity_check); /** - * rproc_elf_sanity_check() - Sanity Check ELF32 firmware image - * @rproc: the remote processor handle - * @fw: the ELF32 firmware image - * - * Make sure this fw image is sane. - */ -int rproc_elf32_sanity_check(struct rproc *rproc, const struct firmware *fw) -{ - int ret = rproc_elf_sanity_check(rproc, fw); - - if (ret) - return ret; - - if (fw_elf_get_class(fw) == ELFCLASS32) - return 0; - - return -EINVAL; -} -EXPORT_SYMBOL(rproc_elf32_sanity_check); - -/** * rproc_elf_get_boot_addr() - Get rproc's boot address. * @rproc: the remote processor handle * @fw: the ELF firmware image @@ -248,9 +227,6 @@ int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw) memset(ptr + filesz, 0, memsz - filesz); } - if (ret == 0) - rproc->elf_class = class; - return ret; } EXPORT_SYMBOL(rproc_elf_load_segments); diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h index b389dc79da81..4ba7cb59d3e8 100644 --- a/drivers/remoteproc/remoteproc_internal.h +++ b/drivers/remoteproc/remoteproc_internal.h @@ -54,7 +54,6 @@ void *rproc_da_to_va(struct rproc *rproc, u64 da, size_t len); phys_addr_t rproc_va_to_pa(void *cpu_addr); int rproc_trigger_recovery(struct rproc *rproc); -int rproc_elf32_sanity_check(struct rproc *rproc, const struct firmware *fw); int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw); u64 rproc_elf_get_boot_addr(struct rproc *rproc, const struct firmware *fw); int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw); @@ -64,6 +63,22 @@ struct resource_table *rproc_elf_find_loaded_rsc_table(struct rproc *rproc, struct rproc_mem_entry * rproc_find_carveout_by_name(struct rproc *rproc, const char *name, ...); +static inline int rproc_prepare_device(struct rproc *rproc) +{ + if (rproc->ops->prepare) + return rproc->ops->prepare(rproc); + + return 0; +} + +static inline int rproc_unprepare_device(struct rproc *rproc) +{ + if (rproc->ops->unprepare) + return rproc->ops->unprepare(rproc); + + return 0; +} + static inline int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw) { diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c index e61d738d9b47..dfd3808c34fd 100644 --- a/drivers/remoteproc/remoteproc_virtio.c +++ b/drivers/remoteproc/remoteproc_virtio.c @@ -337,8 +337,7 @@ int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id) if (rproc->ops->kick == NULL) { ret = -EINVAL; - dev_err(dev, ".kick method not defined for %s", - rproc->name); + dev_err(dev, ".kick method not defined for %s\n", rproc->name); goto out; } @@ -376,6 +375,18 @@ int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id) goto out; } } + } else { + struct device_node *np = rproc->dev.parent->of_node; + + /* + * If we don't have dedicated buffer, just attempt to re-assign + * the reserved memory from our parent. A default memory-region + * at index 0 from the parent's memory-regions is assigned for + * the rvdev dev to allocate from. Failure is non-critical and + * the allocations will fall back to global pools, so don't + * check return value either. + */ + of_reserved_mem_device_init_by_idx(dev, np, 0); } /* Allocate virtio device */ diff --git a/drivers/remoteproc/st_remoteproc.c b/drivers/remoteproc/st_remoteproc.c index a6cbfa452764..a3268d95a50e 100644 --- a/drivers/remoteproc/st_remoteproc.c +++ b/drivers/remoteproc/st_remoteproc.c @@ -233,7 +233,7 @@ static const struct rproc_ops st_rproc_ops = { .parse_fw = st_rproc_parse_fw, .load = rproc_elf_load_segments, .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table, - .sanity_check = rproc_elf32_sanity_check, + .sanity_check = rproc_elf_sanity_check, .get_boot_addr = rproc_elf_get_boot_addr, }; diff --git a/drivers/remoteproc/st_slim_rproc.c b/drivers/remoteproc/st_slim_rproc.c index 3cca8b65a8db..09bcb4d8b9e0 100644 --- a/drivers/remoteproc/st_slim_rproc.c +++ b/drivers/remoteproc/st_slim_rproc.c @@ -203,7 +203,7 @@ static const struct rproc_ops slim_rproc_ops = { .da_to_va = slim_rproc_da_to_va, .get_boot_addr = rproc_elf_get_boot_addr, .load = rproc_elf_load_segments, - .sanity_check = rproc_elf32_sanity_check, + .sanity_check = rproc_elf_sanity_check, }; /** diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c index 0bdd56f02f18..062797a447c6 100644 --- a/drivers/remoteproc/stm32_rproc.c +++ b/drivers/remoteproc/stm32_rproc.c @@ -506,7 +506,7 @@ static struct rproc_ops st_rproc_ops = { .load = rproc_elf_load_segments, .parse_fw = stm32_rproc_parse_fw, .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table, - .sanity_check = rproc_elf32_sanity_check, + .sanity_check = rproc_elf_sanity_check, .get_boot_addr = rproc_elf_get_boot_addr, }; @@ -626,6 +626,7 @@ static int stm32_rproc_probe(struct platform_device *pdev) if (!rproc) return -ENOMEM; + rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE); rproc->has_iommu = false; ddata = rproc->priv; ddata->workqueue = create_workqueue(dev_name(dev)); diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig index a9108ff563dc..f96716893c2a 100644 --- a/drivers/rpmsg/Kconfig +++ b/drivers/rpmsg/Kconfig @@ -24,13 +24,13 @@ config RPMSG_MTK_SCP remote processors in MediaTek platforms. This use IPI and IPC to communicate with remote processors. -config RPMSG_QCOM_GLINK_NATIVE +config RPMSG_QCOM_GLINK tristate select RPMSG config RPMSG_QCOM_GLINK_RPM tristate "Qualcomm RPM Glink driver" - select RPMSG_QCOM_GLINK_NATIVE + select RPMSG_QCOM_GLINK depends on HAS_IOMEM depends on MAILBOX help @@ -40,7 +40,7 @@ config RPMSG_QCOM_GLINK_RPM config RPMSG_QCOM_GLINK_SMEM tristate "Qualcomm SMEM Glink driver" - select RPMSG_QCOM_GLINK_NATIVE + select RPMSG_QCOM_GLINK depends on MAILBOX depends on QCOM_SMEM help diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile index ae92a7fb08f6..ffe932ef6050 100644 --- a/drivers/rpmsg/Makefile +++ b/drivers/rpmsg/Makefile @@ -2,8 +2,9 @@ obj-$(CONFIG_RPMSG) += rpmsg_core.o obj-$(CONFIG_RPMSG_CHAR) += rpmsg_char.o obj-$(CONFIG_RPMSG_MTK_SCP) += mtk_rpmsg.o +qcom_glink-objs := qcom_glink_native.o qcom_glink_ssr.o +obj-$(CONFIG_RPMSG_QCOM_GLINK) += qcom_glink.o obj-$(CONFIG_RPMSG_QCOM_GLINK_RPM) += qcom_glink_rpm.o -obj-$(CONFIG_RPMSG_QCOM_GLINK_NATIVE) += qcom_glink_native.o obj-$(CONFIG_RPMSG_QCOM_GLINK_SMEM) += qcom_glink_smem.o obj-$(CONFIG_RPMSG_QCOM_SMD) += qcom_smd.o obj-$(CONFIG_RPMSG_VIRTIO) += virtio_rpmsg_bus.o diff --git a/drivers/soc/qcom/glink_ssr.c b/drivers/rpmsg/qcom_glink_ssr.c index d7babe3d67bc..dcd1ce616974 100644 --- a/drivers/soc/qcom/glink_ssr.c +++ b/drivers/rpmsg/qcom_glink_ssr.c @@ -54,6 +54,19 @@ struct glink_ssr { struct completion completion; }; +/* Notifier list for all registered glink_ssr instances */ +static BLOCKING_NOTIFIER_HEAD(ssr_notifiers); + +/** + * qcom_glink_ssr_notify() - notify GLINK SSR about stopped remoteproc + * @ssr_name: name of the remoteproc that has been stopped + */ +void qcom_glink_ssr_notify(const char *ssr_name) +{ + blocking_notifier_call_chain(&ssr_notifiers, 0, (void *)ssr_name); +} +EXPORT_SYMBOL_GPL(qcom_glink_ssr_notify); + static int qcom_glink_ssr_callback(struct rpmsg_device *rpdev, void *data, int len, void *priv, u32 addr) { @@ -81,8 +94,9 @@ static int qcom_glink_ssr_callback(struct rpmsg_device *rpdev, return 0; } -static int qcom_glink_ssr_notify(struct notifier_block *nb, unsigned long event, - void *data) +static int qcom_glink_ssr_notifier_call(struct notifier_block *nb, + unsigned long event, + void *data) { struct glink_ssr *ssr = container_of(nb, struct glink_ssr, nb); struct do_cleanup_msg msg; @@ -121,18 +135,18 @@ static int qcom_glink_ssr_probe(struct rpmsg_device *rpdev) ssr->dev = &rpdev->dev; ssr->ept = rpdev->ept; - ssr->nb.notifier_call = qcom_glink_ssr_notify; + ssr->nb.notifier_call = qcom_glink_ssr_notifier_call; dev_set_drvdata(&rpdev->dev, ssr); - return qcom_register_ssr_notifier(&ssr->nb); + return blocking_notifier_chain_register(&ssr_notifiers, &ssr->nb); } static void qcom_glink_ssr_remove(struct rpmsg_device *rpdev) { struct glink_ssr *ssr = dev_get_drvdata(&rpdev->dev); - qcom_unregister_ssr_notifier(&ssr->nb); + blocking_notifier_chain_unregister(&ssr_notifiers, &ssr->nb); } static const struct rpmsg_device_id qcom_glink_ssr_match[] = { @@ -150,7 +164,3 @@ static struct rpmsg_driver qcom_glink_ssr_driver = { }, }; module_rpmsg_driver(qcom_glink_ssr_driver); - -MODULE_ALIAS("rpmsg:glink_ssr"); -MODULE_DESCRIPTION("Qualcomm GLINK SSR notifier"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 250a393f255d..07bb261a63d2 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -35,15 +35,6 @@ config QCOM_GENI_SE driver is also used to manage the common aspects of multiple Serial Engines present in the QUP. -config QCOM_GLINK_SSR - tristate "Qualcomm Glink SSR driver" - depends on RPMSG - depends on QCOM_RPROC_COMMON - help - Say y here to enable GLINK SSR support. The GLINK SSR driver - implements the SSR protocol for notifying the remote processor about - neighboring subsystems going up or down. - config QCOM_GSBI tristate "QCOM General Serial Bus Interface" depends on ARCH_QCOM || COMPILE_TEST diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 92cc4232d72c..7d7e2ecbdce6 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -3,7 +3,6 @@ CFLAGS_rpmh-rsc.o := -I$(src) obj-$(CONFIG_QCOM_AOSS_QMP) += qcom_aoss.o obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o -obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.o obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o obj-$(CONFIG_QCOM_OCMEM) += ocmem.o diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 9c07d7958c53..e7b7bab8b235 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -73,7 +73,7 @@ struct resource_table { u32 ver; u32 num; u32 reserved[2]; - u32 offset[0]; + u32 offset[]; } __packed; /** @@ -87,7 +87,7 @@ struct resource_table { */ struct fw_rsc_hdr { u32 type; - u8 data[0]; + u8 data[]; } __packed; /** @@ -306,7 +306,7 @@ struct fw_rsc_vdev { u8 status; u8 num_of_vrings; u8 reserved[2]; - struct fw_rsc_vdev_vring vring[0]; + struct fw_rsc_vdev_vring vring[]; } __packed; struct rproc; @@ -355,6 +355,8 @@ enum rsc_handling_status { /** * struct rproc_ops - platform-specific device handlers + * @prepare: prepare device for code loading + * @unprepare: unprepare device after stop * @start: power on the device and boot it * @stop: power off the device * @kick: kick a virtqueue (virtqueue id given as a parameter) @@ -373,6 +375,8 @@ enum rsc_handling_status { * panic at least the returned number of milliseconds */ struct rproc_ops { + int (*prepare)(struct rproc *rproc); + int (*unprepare)(struct rproc *rproc); int (*start)(struct rproc *rproc); int (*stop)(struct rproc *rproc); void (*kick)(struct rproc *rproc, int vqid); @@ -489,7 +493,7 @@ struct rproc { struct list_head node; struct iommu_domain *domain; const char *name; - char *firmware; + const char *firmware; void *priv; struct rproc_ops *ops; struct device dev; @@ -518,6 +522,7 @@ struct rproc { struct list_head dump_segments; int nb_vdev; u8 elf_class; + u16 elf_machine; }; /** @@ -599,6 +604,11 @@ int rproc_add(struct rproc *rproc); int rproc_del(struct rproc *rproc); void rproc_free(struct rproc *rproc); +struct rproc *devm_rproc_alloc(struct device *dev, const char *name, + const struct rproc_ops *ops, + const char *firmware, int len); +int devm_rproc_add(struct device *dev, struct rproc *rproc); + void rproc_add_carveout(struct rproc *rproc, struct rproc_mem_entry *mem); struct rproc_mem_entry * @@ -622,6 +632,7 @@ int rproc_coredump_add_custom_segment(struct rproc *rproc, struct rproc_dump_segment *segment, void *dest), void *priv); +int rproc_coredump_set_elf_info(struct rproc *rproc, u8 class, u16 machine); static inline struct rproc_vdev *vdev_to_rvdev(struct virtio_device *vdev) { diff --git a/include/linux/rpmsg/qcom_glink.h b/include/linux/rpmsg/qcom_glink.h index 96e26d94719f..daded9fddf36 100644 --- a/include/linux/rpmsg/qcom_glink.h +++ b/include/linux/rpmsg/qcom_glink.h @@ -12,6 +12,7 @@ struct qcom_glink; struct qcom_glink *qcom_glink_smem_register(struct device *parent, struct device_node *node); void qcom_glink_smem_unregister(struct qcom_glink *glink); +void qcom_glink_ssr_notify(const char *ssr_name); #else @@ -23,7 +24,7 @@ qcom_glink_smem_register(struct device *parent, } static inline void qcom_glink_smem_unregister(struct qcom_glink *glink) {} - +static inline void qcom_glink_ssr_notify(const char *ssr_name) {} #endif #endif |