mirror of
https://github.com/torvalds/linux.git
synced 2024-12-27 05:11:48 +00:00
remoteproc updates for v4.13
This introduces the Keystone 2 DSP driver and refactors the start/stop code in recovery. The Davinci DSP driver gets a few fixes and the Kconfig gets cleaned up. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJZXXbhAAoJEAsfOT8Nma3FPPQQAKCGONJEcmITLuOxKvQLZa7z 18lfJK7xbXM3S5OYwSoOy2vkUsWnFVxLNq3fJhJ3YjnA4vkDExzM5tBWncZ9JrEj EU8Q0K8ArgA0LaQLlgJegEFR/ZW4vs4FFMeb8+EJUGfY8gR5PtdrCT3Tx5fu++p5 c9irv0tXb8IJxOi8gOTNd3nOEQM3+zXCZnfu3HCwOeVpv/1qH9/L42nwbZh6nY2u 7LvklFnDqouv6qiwxc7T+GfxVDPhUUjnUQB3Tsdz+6aOuaRZjYnFKF4prSlqRQnx +18qi3PLuuCprUvEAsNcsFefMqTflCNPvSGcaPuW2I+lF0mnNoKWAwY2iARVIlAy g5krGlkVIndBWRSdluDWuuYMPTNLqbFgfaYQSpsLlPseYFV30UmXJvEABpa4vY1b bBJaYVBtlcl9GWzng5mY/szVLFMzIbadJ81kg2ddaSpLnDSn0a8+xjxwpqDHQS3y 9SxP0Hynh8cbsfwLyBRQ4RC9n8cZ4KyyoIObcglSQtzrmBxxK2MGJ/RyZ3AulmHx YdnWyNAwBa+fzbtyHENHHtE85zTkFV7iGutOibZPsFO9SRyIINbPaKGKicGUswB5 SZKVNLPPLqI8fwO2MBRM4fIrm40hrUbpMwX5S6sVJ86Fv4yIDfxYvQgLaHSLbyCX nDWWhcBjQINgR+UbUFfm =rz64 -----END PGP SIGNATURE----- Merge tag 'rproc-v4.13' of git://github.com/andersson/remoteproc Pull remoteproc updates from Bjorn Andersson: "This introduces the Keystone 2 DSP driver and refactors the start/stop code in recovery. The Davinci DSP driver gets a few fixes and the Kconfig gets cleaned up" * tag 'rproc-v4.13' of git://github.com/andersson/remoteproc: remoteproc/keystone: Fix circular dependencies for ARM configs remoteproc: Drop redundant REMOTEPROC dependency from driver Kconfigs remoteproc: Drop VIRTUALIZATION dependency from REMOTEPROC remoteproc/keystone: Ensure the DSPs are in reset in probe remoteproc/keystone: Add a remoteproc driver for Keystone 2 DSPs dt-bindings: remoteproc: Add Keystone DSP remoteproc binding remoteproc/davinci: fix unbalanced reset between start and stop ops remoteproc/davinci: simplify the reset function remoteproc/davinci: Update Kconfig to depend on DMA_CMA remoteproc: fix spelling mistake: "Resouce" -> "Resource" remoteproc: Modify recovery path to use rproc_{start,stop}() remoteproc: Introduce rproc_{start,stop}() functions
This commit is contained in:
commit
9a6293c321
@ -0,0 +1,133 @@
|
||||
TI Keystone DSP devices
|
||||
=======================
|
||||
|
||||
The TI Keystone 2 family of SoCs usually have one or more (upto 8) 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 core in
|
||||
Keystone 2 SoCs is usually a TMS320C66x CorePac processor.
|
||||
|
||||
DSP Device Node:
|
||||
================
|
||||
Each DSP Core sub-system is represented as a single DT node, and should also
|
||||
have an alias with the stem 'rproc' defined. 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.
|
||||
|
||||
Required properties:
|
||||
--------------------
|
||||
The following are the mandatory properties:
|
||||
|
||||
- compatible: Should be one of the following,
|
||||
"ti,k2hk-dsp" for DSPs on Keystone 2 66AK2H/K SoCs
|
||||
"ti,k2l-dsp" for DSPs on Keystone 2 66AK2L SoCs
|
||||
"ti,k2e-dsp" for DSPs on Keystone 2 66AK2E SoCs
|
||||
|
||||
- reg: Should contain an entry for each value in 'reg-names'.
|
||||
Each entry should have the memory region's start address
|
||||
and the size of the region, the representation matching
|
||||
the parent node's '#address-cells' and '#size-cells' values.
|
||||
|
||||
- reg-names: Should contain strings with the following names, each
|
||||
representing a specific internal memory region, and
|
||||
should be defined in this order,
|
||||
"l2sram", "l1pram", "l1dram"
|
||||
|
||||
- clocks: Should contain the device's input clock, and should be
|
||||
defined as per the bindings in,
|
||||
Documentation/devicetree/bindings/clock/keystone-gate.txt
|
||||
|
||||
- ti,syscon-dev: Should be a pair of the phandle to the Keystone Device
|
||||
State Control node, and the register offset of the DSP
|
||||
boot address register within that node's address space.
|
||||
|
||||
- resets: Should contain the phandle to the reset controller node
|
||||
managing the resets for this device, and a reset
|
||||
specifier. Please refer to the following reset bindings
|
||||
for the reset argument specifier as per SoC,
|
||||
Documentation/devicetree/bindings/reset/ti-syscon-reset.txt
|
||||
for 66AK2HK/66AK2L/66AK2E SoCs
|
||||
|
||||
- interrupt-parent: Should contain a phandle to the Keystone 2 IRQ controller
|
||||
IP node that is used by the ARM CorePac processor to
|
||||
receive interrupts from the DSP remote processors. See
|
||||
Documentation/devicetree/bindings/interrupt-controller/ti,keystone-irq.txt
|
||||
for details.
|
||||
|
||||
- interrupts: Should contain an entry for each value in 'interrupt-names'.
|
||||
Each entry should have the interrupt source number used by
|
||||
the remote processor to the host processor. The values should
|
||||
follow the interrupt-specifier format as dictated by the
|
||||
'interrupt-parent' node. The purpose of each is as per the
|
||||
description in the 'interrupt-names' property.
|
||||
|
||||
- interrupt-names: Should contain strings with the following names, each
|
||||
representing a specific interrupt,
|
||||
"vring" - interrupt for virtio based IPC
|
||||
"exception" - interrupt for exception notification
|
||||
|
||||
- kick-gpios: Should specify the gpio device needed for the virtio IPC
|
||||
stack. This will be used to interrupt the remote processor.
|
||||
The gpio device to be used is as per the bindings in,
|
||||
Documentation/devicetree/bindings/gpio/gpio-dsp-keystone.txt
|
||||
|
||||
Optional properties:
|
||||
--------------------
|
||||
|
||||
- memory-region: phandle to the reserved memory node to be associated
|
||||
with the remoteproc device. The reserved memory node
|
||||
can be a CMA memory node, and should be defined as
|
||||
per the bindings in
|
||||
Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
|
||||
|
||||
|
||||
Example:
|
||||
--------
|
||||
/* 66AK2H/K DSP aliases */
|
||||
aliases {
|
||||
rproc0 = &dsp0;
|
||||
rproc1 = &dsp1;
|
||||
rproc2 = &dsp2;
|
||||
rproc3 = &dsp3;
|
||||
rproc4 = &dsp4;
|
||||
rproc5 = &dsp5;
|
||||
rproc6 = &dsp6;
|
||||
rproc7 = &dsp7;
|
||||
};
|
||||
|
||||
/* 66AK2H/K DSP memory node */
|
||||
reserved-memory {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
dsp_common_memory: dsp-common-memory@81f800000 {
|
||||
compatible = "shared-dma-pool";
|
||||
reg = <0x00000008 0x1f800000 0x00000000 0x800000>;
|
||||
reusable;
|
||||
};
|
||||
};
|
||||
|
||||
/* 66AK2H/K DSP node */
|
||||
soc {
|
||||
dsp0: dsp@10800000 {
|
||||
compatible = "ti,k2hk-dsp";
|
||||
reg = <0x10800000 0x00100000>,
|
||||
<0x10e00000 0x00008000>,
|
||||
<0x10f00000 0x00008000>;
|
||||
reg-names = "l2sram", "l1pram", "l1dram";
|
||||
clocks = <&clkgem0>;
|
||||
ti,syscon-dev = <&devctrl 0x40>;
|
||||
resets = <&pscrst 0>;
|
||||
interrupt-parent = <&kirq0>;
|
||||
interrupts = <0 8>;
|
||||
interrupt-names = "vring", "exception";
|
||||
kick-gpios = <&dspgpio0 27 0>;
|
||||
memory-region = <&dsp_common_memory>;
|
||||
};
|
||||
|
||||
};
|
@ -6,7 +6,6 @@ config REMOTEPROC
|
||||
select CRC32
|
||||
select FW_LOADER
|
||||
select VIRTIO
|
||||
select VIRTUALIZATION
|
||||
help
|
||||
Support for remote processors (such as DSP coprocessors). These
|
||||
are mainly used on embedded systems.
|
||||
@ -18,7 +17,6 @@ config OMAP_REMOTEPROC
|
||||
depends on HAS_DMA
|
||||
depends on ARCH_OMAP4 || SOC_OMAP5
|
||||
depends on OMAP_IOMMU
|
||||
depends on REMOTEPROC
|
||||
select MAILBOX
|
||||
select OMAP2PLUS_MBOX
|
||||
select RPMSG_VIRTIO
|
||||
@ -38,7 +36,6 @@ config OMAP_REMOTEPROC
|
||||
config WKUP_M3_RPROC
|
||||
tristate "AMx3xx Wakeup M3 remoteproc support"
|
||||
depends on SOC_AM33XX || SOC_AM43XX
|
||||
depends on REMOTEPROC
|
||||
help
|
||||
Say y here to support Wakeup M3 remote processor on TI AM33xx
|
||||
and AM43xx family of SoCs.
|
||||
@ -51,8 +48,7 @@ config WKUP_M3_RPROC
|
||||
config DA8XX_REMOTEPROC
|
||||
tristate "DA8xx/OMAP-L13x remoteproc support"
|
||||
depends on ARCH_DAVINCI_DA8XX
|
||||
depends on REMOTEPROC
|
||||
select CMA if MMU
|
||||
depends on DMA_CMA
|
||||
select RPMSG_VIRTIO
|
||||
help
|
||||
Say y here to support DA8xx/OMAP-L13x remote processors via the
|
||||
@ -71,10 +67,20 @@ config DA8XX_REMOTEPROC
|
||||
It's safe to say n here if you're not interested in multimedia
|
||||
offloading.
|
||||
|
||||
config KEYSTONE_REMOTEPROC
|
||||
tristate "Keystone Remoteproc support"
|
||||
depends on ARCH_KEYSTONE
|
||||
select RPMSG_VIRTIO
|
||||
help
|
||||
Say Y here here to support Keystone remote processors (DSP)
|
||||
via the remote processor framework.
|
||||
|
||||
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_ADSP_PIL
|
||||
tristate "Qualcomm ADSP Peripheral Image Loader"
|
||||
depends on OF && ARCH_QCOM
|
||||
depends on REMOTEPROC
|
||||
depends on QCOM_SMEM
|
||||
depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n)
|
||||
select MFD_SYSCON
|
||||
@ -92,7 +98,6 @@ config QCOM_Q6V5_PIL
|
||||
tristate "Qualcomm Hexagon V5 Peripherial Image Loader"
|
||||
depends on OF && ARCH_QCOM
|
||||
depends on QCOM_SMEM
|
||||
depends on REMOTEPROC
|
||||
depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n)
|
||||
select MFD_SYSCON
|
||||
select QCOM_RPROC_COMMON
|
||||
@ -106,7 +111,6 @@ config QCOM_WCNSS_PIL
|
||||
depends on OF && ARCH_QCOM
|
||||
depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n)
|
||||
depends on QCOM_SMEM
|
||||
depends on REMOTEPROC
|
||||
select QCOM_MDT_LOADER
|
||||
select QCOM_RPROC_COMMON
|
||||
select QCOM_SCM
|
||||
@ -117,7 +121,6 @@ config QCOM_WCNSS_PIL
|
||||
config ST_REMOTEPROC
|
||||
tristate "ST remoteproc support"
|
||||
depends on ARCH_STI
|
||||
depends on REMOTEPROC
|
||||
select MAILBOX
|
||||
select STI_MBOX
|
||||
select RPMSG_VIRTIO
|
||||
@ -128,7 +131,6 @@ config ST_REMOTEPROC
|
||||
|
||||
config ST_SLIM_REMOTEPROC
|
||||
tristate
|
||||
depends on REMOTEPROC
|
||||
|
||||
endif # REMOTEPROC
|
||||
|
||||
|
@ -11,6 +11,7 @@ remoteproc-y += remoteproc_elf_loader.o
|
||||
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_ADSP_PIL) += qcom_adsp_pil.o
|
||||
obj-$(CONFIG_QCOM_RPROC_COMMON) += qcom_common.o
|
||||
obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o
|
||||
|
@ -137,6 +137,7 @@ static int da8xx_rproc_stop(struct rproc *rproc)
|
||||
{
|
||||
struct da8xx_rproc *drproc = rproc->priv;
|
||||
|
||||
davinci_clk_reset_assert(drproc->dsp_clk);
|
||||
clk_disable(drproc->dsp_clk);
|
||||
|
||||
return 0;
|
||||
@ -157,22 +158,6 @@ static const struct rproc_ops da8xx_rproc_ops = {
|
||||
.kick = da8xx_rproc_kick,
|
||||
};
|
||||
|
||||
static int reset_assert(struct device *dev)
|
||||
{
|
||||
struct clk *dsp_clk;
|
||||
|
||||
dsp_clk = clk_get(dev, NULL);
|
||||
if (IS_ERR(dsp_clk)) {
|
||||
dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk));
|
||||
return PTR_ERR(dsp_clk);
|
||||
}
|
||||
|
||||
davinci_clk_reset_assert(dsp_clk);
|
||||
clk_put(dsp_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int da8xx_rproc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@ -223,6 +208,7 @@ static int da8xx_rproc_probe(struct platform_device *pdev)
|
||||
|
||||
drproc = rproc->priv;
|
||||
drproc->rproc = rproc;
|
||||
drproc->dsp_clk = dsp_clk;
|
||||
rproc->has_iommu = false;
|
||||
|
||||
platform_set_drvdata(pdev, rproc);
|
||||
@ -241,7 +227,7 @@ static int da8xx_rproc_probe(struct platform_device *pdev)
|
||||
* *not* in reset, but da8xx_rproc_start() needs the DSP to be
|
||||
* held in reset at the time it is called.
|
||||
*/
|
||||
ret = reset_assert(dev);
|
||||
ret = davinci_clk_reset_assert(drproc->dsp_clk);
|
||||
if (ret)
|
||||
goto free_rproc;
|
||||
|
||||
@ -250,7 +236,6 @@ static int da8xx_rproc_probe(struct platform_device *pdev)
|
||||
drproc->ack_fxn = irq_data->chip->irq_ack;
|
||||
drproc->irq_data = irq_data;
|
||||
drproc->irq = irq;
|
||||
drproc->dsp_clk = dsp_clk;
|
||||
|
||||
ret = rproc_add(rproc);
|
||||
if (ret) {
|
||||
@ -268,20 +253,9 @@ free_rproc:
|
||||
|
||||
static int da8xx_rproc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rproc *rproc = platform_get_drvdata(pdev);
|
||||
struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
|
||||
|
||||
/*
|
||||
* It's important to place the DSP in reset before going away,
|
||||
* since a subsequent insmod of this module may enable the DSP's
|
||||
* clock before its program/boot-address has been loaded and
|
||||
* before this module's probe has had a chance to reset the DSP.
|
||||
* Without the reset, the DSP can lockup permanently when it
|
||||
* begins executing garbage.
|
||||
*/
|
||||
reset_assert(dev);
|
||||
|
||||
/*
|
||||
* The devm subsystem might end up releasing things before
|
||||
* freeing the irq, thus allowing an interrupt to sneak in while
|
||||
|
525
drivers/remoteproc/keystone_remoteproc.c
Normal file
525
drivers/remoteproc/keystone_remoteproc.c
Normal file
@ -0,0 +1,525 @@
|
||||
/*
|
||||
* TI Keystone DSP remoteproc driver
|
||||
*
|
||||
* Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/remoteproc.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include "remoteproc_internal.h"
|
||||
|
||||
#define KEYSTONE_RPROC_LOCAL_ADDRESS_MASK (SZ_16M - 1)
|
||||
|
||||
/**
|
||||
* struct keystone_rproc_mem - internal memory structure
|
||||
* @cpu_addr: MPU virtual address of the memory region
|
||||
* @bus_addr: Bus address used to access the memory region
|
||||
* @dev_addr: Device address of the memory region from DSP view
|
||||
* @size: Size of the memory region
|
||||
*/
|
||||
struct keystone_rproc_mem {
|
||||
void __iomem *cpu_addr;
|
||||
phys_addr_t bus_addr;
|
||||
u32 dev_addr;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct keystone_rproc - keystone remote processor driver structure
|
||||
* @dev: cached device pointer
|
||||
* @rproc: remoteproc device handle
|
||||
* @mem: internal memory regions data
|
||||
* @num_mems: number of internal memory regions
|
||||
* @dev_ctrl: device control regmap handle
|
||||
* @reset: reset control handle
|
||||
* @boot_offset: boot register offset in @dev_ctrl regmap
|
||||
* @irq_ring: irq entry for vring
|
||||
* @irq_fault: irq entry for exception
|
||||
* @kick_gpio: gpio used for virtio kicks
|
||||
* @workqueue: workqueue for processing virtio interrupts
|
||||
*/
|
||||
struct keystone_rproc {
|
||||
struct device *dev;
|
||||
struct rproc *rproc;
|
||||
struct keystone_rproc_mem *mem;
|
||||
int num_mems;
|
||||
struct regmap *dev_ctrl;
|
||||
struct reset_control *reset;
|
||||
u32 boot_offset;
|
||||
int irq_ring;
|
||||
int irq_fault;
|
||||
int kick_gpio;
|
||||
struct work_struct workqueue;
|
||||
};
|
||||
|
||||
/* Put the DSP processor into reset */
|
||||
static void keystone_rproc_dsp_reset(struct keystone_rproc *ksproc)
|
||||
{
|
||||
reset_control_assert(ksproc->reset);
|
||||
}
|
||||
|
||||
/* Configure the boot address and boot the DSP processor */
|
||||
static int keystone_rproc_dsp_boot(struct keystone_rproc *ksproc, u32 boot_addr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (boot_addr & (SZ_1K - 1)) {
|
||||
dev_err(ksproc->dev, "invalid boot address 0x%x, must be aligned on a 1KB boundary\n",
|
||||
boot_addr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = regmap_write(ksproc->dev_ctrl, ksproc->boot_offset, boot_addr);
|
||||
if (ret) {
|
||||
dev_err(ksproc->dev, "regmap_write of boot address failed, status = %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
reset_control_deassert(ksproc->reset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process the remoteproc exceptions
|
||||
*
|
||||
* The exception reporting on Keystone DSP remote processors is very simple
|
||||
* compared to the equivalent processors on the OMAP family, it is notified
|
||||
* through a software-designed specific interrupt source in the IPC interrupt
|
||||
* generation register.
|
||||
*
|
||||
* This function just invokes the rproc_report_crash to report the exception
|
||||
* to the remoteproc driver core, to trigger a recovery.
|
||||
*/
|
||||
static irqreturn_t keystone_rproc_exception_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct keystone_rproc *ksproc = dev_id;
|
||||
|
||||
rproc_report_crash(ksproc->rproc, RPROC_FATAL_ERROR);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Main virtqueue message workqueue function
|
||||
*
|
||||
* This function is executed upon scheduling of the keystone remoteproc
|
||||
* driver's workqueue. The workqueue is scheduled by the vring ISR handler.
|
||||
*
|
||||
* There is no payload message indicating the virtqueue index as is the
|
||||
* case with mailbox-based implementations on OMAP family. As such, this
|
||||
* handler processes both the Tx and Rx virtqueue indices on every invocation.
|
||||
* The rproc_vq_interrupt function can detect if there are new unprocessed
|
||||
* messages or not (returns IRQ_NONE vs IRQ_HANDLED), but there is no need
|
||||
* to check for these return values. The index 0 triggering will process all
|
||||
* pending Rx buffers, and the index 1 triggering will process all newly
|
||||
* available Tx buffers and will wakeup any potentially blocked senders.
|
||||
*
|
||||
* NOTE:
|
||||
* 1. A payload could be added by using some of the source bits in the
|
||||
* IPC interrupt generation registers, but this would need additional
|
||||
* changes to the overall IPC stack, and currently there are no benefits
|
||||
* of adapting that approach.
|
||||
* 2. The current logic is based on an inherent design assumption of supporting
|
||||
* only 2 vrings, but this can be changed if needed.
|
||||
*/
|
||||
static void handle_event(struct work_struct *work)
|
||||
{
|
||||
struct keystone_rproc *ksproc =
|
||||
container_of(work, struct keystone_rproc, workqueue);
|
||||
|
||||
rproc_vq_interrupt(ksproc->rproc, 0);
|
||||
rproc_vq_interrupt(ksproc->rproc, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupt handler for processing vring kicks from remote processor
|
||||
*/
|
||||
static irqreturn_t keystone_rproc_vring_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct keystone_rproc *ksproc = dev_id;
|
||||
|
||||
schedule_work(&ksproc->workqueue);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Power up the DSP remote processor.
|
||||
*
|
||||
* This function will be invoked only after the firmware for this rproc
|
||||
* was loaded, parsed successfully, and all of its resource requirements
|
||||
* were met.
|
||||
*/
|
||||
static int keystone_rproc_start(struct rproc *rproc)
|
||||
{
|
||||
struct keystone_rproc *ksproc = rproc->priv;
|
||||
int ret;
|
||||
|
||||
INIT_WORK(&ksproc->workqueue, handle_event);
|
||||
|
||||
ret = request_irq(ksproc->irq_ring, keystone_rproc_vring_interrupt, 0,
|
||||
dev_name(ksproc->dev), ksproc);
|
||||
if (ret) {
|
||||
dev_err(ksproc->dev, "failed to enable vring interrupt, ret = %d\n",
|
||||
ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = request_irq(ksproc->irq_fault, keystone_rproc_exception_interrupt,
|
||||
0, dev_name(ksproc->dev), ksproc);
|
||||
if (ret) {
|
||||
dev_err(ksproc->dev, "failed to enable exception interrupt, ret = %d\n",
|
||||
ret);
|
||||
goto free_vring_irq;
|
||||
}
|
||||
|
||||
ret = keystone_rproc_dsp_boot(ksproc, rproc->bootaddr);
|
||||
if (ret)
|
||||
goto free_exc_irq;
|
||||
|
||||
return 0;
|
||||
|
||||
free_exc_irq:
|
||||
free_irq(ksproc->irq_fault, ksproc);
|
||||
free_vring_irq:
|
||||
free_irq(ksproc->irq_ring, ksproc);
|
||||
flush_work(&ksproc->workqueue);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Stop the DSP remote processor.
|
||||
*
|
||||
* This function puts the DSP processor into reset, and finishes processing
|
||||
* of any pending messages.
|
||||
*/
|
||||
static int keystone_rproc_stop(struct rproc *rproc)
|
||||
{
|
||||
struct keystone_rproc *ksproc = rproc->priv;
|
||||
|
||||
keystone_rproc_dsp_reset(ksproc);
|
||||
free_irq(ksproc->irq_fault, ksproc);
|
||||
free_irq(ksproc->irq_ring, ksproc);
|
||||
flush_work(&ksproc->workqueue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Kick the remote processor to notify about pending unprocessed messages.
|
||||
* The vqid usage is not used and is inconsequential, as the kick is performed
|
||||
* through a simulated GPIO (a bit in an IPC interrupt-triggering register),
|
||||
* the remote processor is expected to process both its Tx and Rx virtqueues.
|
||||
*/
|
||||
static void keystone_rproc_kick(struct rproc *rproc, int vqid)
|
||||
{
|
||||
struct keystone_rproc *ksproc = rproc->priv;
|
||||
|
||||
if (WARN_ON(ksproc->kick_gpio < 0))
|
||||
return;
|
||||
|
||||
gpio_set_value(ksproc->kick_gpio, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Custom function to translate a DSP device address (internal RAMs only) to a
|
||||
* kernel virtual address. The DSPs can access their RAMs at either an internal
|
||||
* address visible only from a DSP, or at the SoC-level bus address. Both these
|
||||
* addresses need to be looked through for translation. The translated addresses
|
||||
* can be used either by the remoteproc core for loading (when using kernel
|
||||
* remoteproc loader), or by any rpmsg bus drivers.
|
||||
*/
|
||||
static void *keystone_rproc_da_to_va(struct rproc *rproc, u64 da, int len)
|
||||
{
|
||||
struct keystone_rproc *ksproc = rproc->priv;
|
||||
void __iomem *va = NULL;
|
||||
phys_addr_t bus_addr;
|
||||
u32 dev_addr, offset;
|
||||
size_t size;
|
||||
int i;
|
||||
|
||||
if (len <= 0)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < ksproc->num_mems; i++) {
|
||||
bus_addr = ksproc->mem[i].bus_addr;
|
||||
dev_addr = ksproc->mem[i].dev_addr;
|
||||
size = ksproc->mem[i].size;
|
||||
|
||||
if (da < KEYSTONE_RPROC_LOCAL_ADDRESS_MASK) {
|
||||
/* handle DSP-view addresses */
|
||||
if ((da >= dev_addr) &&
|
||||
((da + len) <= (dev_addr + size))) {
|
||||
offset = da - dev_addr;
|
||||
va = ksproc->mem[i].cpu_addr + offset;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* handle SoC-view addresses */
|
||||
if ((da >= bus_addr) &&
|
||||
(da + len) <= (bus_addr + size)) {
|
||||
offset = da - bus_addr;
|
||||
va = ksproc->mem[i].cpu_addr + offset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (__force void *)va;
|
||||
}
|
||||
|
||||
static const struct rproc_ops keystone_rproc_ops = {
|
||||
.start = keystone_rproc_start,
|
||||
.stop = keystone_rproc_stop,
|
||||
.kick = keystone_rproc_kick,
|
||||
.da_to_va = keystone_rproc_da_to_va,
|
||||
};
|
||||
|
||||
static int keystone_rproc_of_get_memories(struct platform_device *pdev,
|
||||
struct keystone_rproc *ksproc)
|
||||
{
|
||||
static const char * const mem_names[] = {"l2sram", "l1pram", "l1dram"};
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
int num_mems = 0;
|
||||
int i;
|
||||
|
||||
num_mems = ARRAY_SIZE(mem_names);
|
||||
ksproc->mem = devm_kcalloc(ksproc->dev, num_mems,
|
||||
sizeof(*ksproc->mem), GFP_KERNEL);
|
||||
if (!ksproc->mem)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < num_mems; i++) {
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
mem_names[i]);
|
||||
ksproc->mem[i].cpu_addr = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(ksproc->mem[i].cpu_addr)) {
|
||||
dev_err(dev, "failed to parse and map %s memory\n",
|
||||
mem_names[i]);
|
||||
return PTR_ERR(ksproc->mem[i].cpu_addr);
|
||||
}
|
||||
ksproc->mem[i].bus_addr = res->start;
|
||||
ksproc->mem[i].dev_addr =
|
||||
res->start & KEYSTONE_RPROC_LOCAL_ADDRESS_MASK;
|
||||
ksproc->mem[i].size = resource_size(res);
|
||||
|
||||
/* zero out memories to start in a pristine state */
|
||||
memset((__force void *)ksproc->mem[i].cpu_addr, 0,
|
||||
ksproc->mem[i].size);
|
||||
}
|
||||
ksproc->num_mems = num_mems;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int keystone_rproc_of_get_dev_syscon(struct platform_device *pdev,
|
||||
struct keystone_rproc *ksproc)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
if (!of_property_read_bool(np, "ti,syscon-dev")) {
|
||||
dev_err(dev, "ti,syscon-dev property is absent\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ksproc->dev_ctrl =
|
||||
syscon_regmap_lookup_by_phandle(np, "ti,syscon-dev");
|
||||
if (IS_ERR(ksproc->dev_ctrl)) {
|
||||
ret = PTR_ERR(ksproc->dev_ctrl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (of_property_read_u32_index(np, "ti,syscon-dev", 1,
|
||||
&ksproc->boot_offset)) {
|
||||
dev_err(dev, "couldn't read the boot register offset\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int keystone_rproc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct keystone_rproc *ksproc;
|
||||
struct rproc *rproc;
|
||||
int dsp_id;
|
||||
char *fw_name = NULL;
|
||||
char *template = "keystone-dsp%d-fw";
|
||||
int name_len = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (!np) {
|
||||
dev_err(dev, "only DT-based devices are supported\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dsp_id = of_alias_get_id(np, "rproc");
|
||||
if (dsp_id < 0) {
|
||||
dev_warn(dev, "device does not have an alias id\n");
|
||||
return dsp_id;
|
||||
}
|
||||
|
||||
/* construct a custom default fw name - subject to change in future */
|
||||
name_len = strlen(template); /* assuming a single digit alias */
|
||||
fw_name = devm_kzalloc(dev, name_len, GFP_KERNEL);
|
||||
if (!fw_name)
|
||||
return -ENOMEM;
|
||||
snprintf(fw_name, name_len, template, dsp_id);
|
||||
|
||||
rproc = rproc_alloc(dev, dev_name(dev), &keystone_rproc_ops, fw_name,
|
||||
sizeof(*ksproc));
|
||||
if (!rproc)
|
||||
return -ENOMEM;
|
||||
|
||||
rproc->has_iommu = false;
|
||||
ksproc = rproc->priv;
|
||||
ksproc->rproc = rproc;
|
||||
ksproc->dev = dev;
|
||||
|
||||
ret = keystone_rproc_of_get_dev_syscon(pdev, ksproc);
|
||||
if (ret)
|
||||
goto free_rproc;
|
||||
|
||||
ksproc->reset = devm_reset_control_get(dev, NULL);
|
||||
if (IS_ERR(ksproc->reset)) {
|
||||
ret = PTR_ERR(ksproc->reset);
|
||||
goto free_rproc;
|
||||
}
|
||||
|
||||
/* enable clock for accessing DSP internal memories */
|
||||
pm_runtime_enable(dev);
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to enable clock, status = %d\n", ret);
|
||||
pm_runtime_put_noidle(dev);
|
||||
goto disable_rpm;
|
||||
}
|
||||
|
||||
ret = keystone_rproc_of_get_memories(pdev, ksproc);
|
||||
if (ret)
|
||||
goto disable_clk;
|
||||
|
||||
ksproc->irq_ring = platform_get_irq_byname(pdev, "vring");
|
||||
if (ksproc->irq_ring < 0) {
|
||||
ret = ksproc->irq_ring;
|
||||
dev_err(dev, "failed to get vring interrupt, status = %d\n",
|
||||
ret);
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
ksproc->irq_fault = platform_get_irq_byname(pdev, "exception");
|
||||
if (ksproc->irq_fault < 0) {
|
||||
ret = ksproc->irq_fault;
|
||||
dev_err(dev, "failed to get exception interrupt, status = %d\n",
|
||||
ret);
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
ksproc->kick_gpio = of_get_named_gpio_flags(np, "kick-gpios", 0, NULL);
|
||||
if (ksproc->kick_gpio < 0) {
|
||||
ret = ksproc->kick_gpio;
|
||||
dev_err(dev, "failed to get gpio for virtio kicks, status = %d\n",
|
||||
ret);
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
if (of_reserved_mem_device_init(dev))
|
||||
dev_warn(dev, "device does not have specific CMA pool\n");
|
||||
|
||||
/* ensure the DSP is in reset before loading firmware */
|
||||
ret = reset_control_status(ksproc->reset);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to get reset status, status = %d\n", ret);
|
||||
goto release_mem;
|
||||
} else if (ret == 0) {
|
||||
WARN(1, "device is not in reset\n");
|
||||
keystone_rproc_dsp_reset(ksproc);
|
||||
}
|
||||
|
||||
ret = rproc_add(rproc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to add register device with remoteproc core, status = %d\n",
|
||||
ret);
|
||||
goto release_mem;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, ksproc);
|
||||
|
||||
return 0;
|
||||
|
||||
release_mem:
|
||||
of_reserved_mem_device_release(dev);
|
||||
disable_clk:
|
||||
pm_runtime_put_sync(dev);
|
||||
disable_rpm:
|
||||
pm_runtime_disable(dev);
|
||||
free_rproc:
|
||||
rproc_free(rproc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int keystone_rproc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct keystone_rproc *ksproc = platform_get_drvdata(pdev);
|
||||
|
||||
rproc_del(ksproc->rproc);
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
rproc_free(ksproc->rproc);
|
||||
of_reserved_mem_device_release(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id keystone_rproc_of_match[] = {
|
||||
{ .compatible = "ti,k2hk-dsp", },
|
||||
{ .compatible = "ti,k2l-dsp", },
|
||||
{ .compatible = "ti,k2e-dsp", },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, keystone_rproc_of_match);
|
||||
|
||||
static struct platform_driver keystone_rproc_driver = {
|
||||
.probe = keystone_rproc_probe,
|
||||
.remove = keystone_rproc_remove,
|
||||
.driver = {
|
||||
.name = "keystone-rproc",
|
||||
.of_match_table = keystone_rproc_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(keystone_rproc_driver);
|
||||
|
||||
MODULE_AUTHOR("Suman Anna <s-anna@ti.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("TI Keystone DSP Remoteproc driver");
|
@ -847,6 +847,63 @@ static void rproc_resource_cleanup(struct rproc *rproc)
|
||||
kref_put(&rvdev->refcount, rproc_vdev_release);
|
||||
}
|
||||
|
||||
static int rproc_start(struct rproc *rproc, const struct firmware *fw)
|
||||
{
|
||||
struct resource_table *table, *loaded_table;
|
||||
struct device *dev = &rproc->dev;
|
||||
int ret, tablesz;
|
||||
|
||||
/* look for the resource table */
|
||||
table = rproc_find_rsc_table(rproc, fw, &tablesz);
|
||||
if (!table) {
|
||||
dev_err(dev, "Resource table look up failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* load the ELF segments to memory */
|
||||
ret = rproc_load_segments(rproc, fw);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to load program segments: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The starting device has been given the rproc->cached_table as the
|
||||
* resource table. The address of the vring along with the other
|
||||
* allocated resources (carveouts etc) is stored in cached_table.
|
||||
* In order to pass this information to the remote device we must copy
|
||||
* this information to device memory. We also update the table_ptr so
|
||||
* that any subsequent changes will be applied to the loaded version.
|
||||
*/
|
||||
loaded_table = rproc_find_loaded_rsc_table(rproc, fw);
|
||||
if (loaded_table) {
|
||||
memcpy(loaded_table, rproc->cached_table, tablesz);
|
||||
rproc->table_ptr = loaded_table;
|
||||
}
|
||||
|
||||
/* power up the remote processor */
|
||||
ret = rproc->ops->start(rproc);
|
||||
if (ret) {
|
||||
dev_err(dev, "can't start rproc %s: %d\n", rproc->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* probe any subdevices for the remote processor */
|
||||
ret = rproc_probe_subdevices(rproc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to probe subdevices for %s: %d\n",
|
||||
rproc->name, ret);
|
||||
rproc->ops->stop(rproc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
rproc->state = RPROC_RUNNING;
|
||||
|
||||
dev_info(dev, "remote processor %s is now up\n", rproc->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* take a firmware and boot a remote processor with it.
|
||||
*/
|
||||
@ -854,7 +911,7 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
|
||||
{
|
||||
struct device *dev = &rproc->dev;
|
||||
const char *name = rproc->firmware;
|
||||
struct resource_table *table, *loaded_table;
|
||||
struct resource_table *table;
|
||||
int ret, tablesz;
|
||||
|
||||
ret = rproc_fw_sanity_check(rproc, fw);
|
||||
@ -905,50 +962,12 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
|
||||
goto clean_up_resources;
|
||||
}
|
||||
|
||||
/* load the ELF segments to memory */
|
||||
ret = rproc_load_segments(rproc, fw);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to load program segments: %d\n", ret);
|
||||
ret = rproc_start(rproc, fw);
|
||||
if (ret)
|
||||
goto clean_up_resources;
|
||||
}
|
||||
|
||||
/*
|
||||
* The starting device has been given the rproc->cached_table as the
|
||||
* resource table. The address of the vring along with the other
|
||||
* allocated resources (carveouts etc) is stored in cached_table.
|
||||
* In order to pass this information to the remote device we must copy
|
||||
* this information to device memory. We also update the table_ptr so
|
||||
* that any subsequent changes will be applied to the loaded version.
|
||||
*/
|
||||
loaded_table = rproc_find_loaded_rsc_table(rproc, fw);
|
||||
if (loaded_table) {
|
||||
memcpy(loaded_table, rproc->cached_table, tablesz);
|
||||
rproc->table_ptr = loaded_table;
|
||||
}
|
||||
|
||||
/* power up the remote processor */
|
||||
ret = rproc->ops->start(rproc);
|
||||
if (ret) {
|
||||
dev_err(dev, "can't start rproc %s: %d\n", rproc->name, ret);
|
||||
goto clean_up_resources;
|
||||
}
|
||||
|
||||
/* probe any subdevices for the remote processor */
|
||||
ret = rproc_probe_subdevices(rproc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to probe subdevices for %s: %d\n",
|
||||
rproc->name, ret);
|
||||
goto stop_rproc;
|
||||
}
|
||||
|
||||
rproc->state = RPROC_RUNNING;
|
||||
|
||||
dev_info(dev, "remote processor %s is now up\n", rproc->name);
|
||||
|
||||
return 0;
|
||||
|
||||
stop_rproc:
|
||||
rproc->ops->stop(rproc);
|
||||
clean_up_resources:
|
||||
rproc_resource_cleanup(rproc);
|
||||
clean_up:
|
||||
@ -994,6 +1013,32 @@ static int rproc_trigger_auto_boot(struct rproc *rproc)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rproc_stop(struct rproc *rproc)
|
||||
{
|
||||
struct device *dev = &rproc->dev;
|
||||
int ret;
|
||||
|
||||
/* remove any subdevices for the remote processor */
|
||||
rproc_remove_subdevices(rproc);
|
||||
|
||||
/* power off the remote processor */
|
||||
ret = rproc->ops->stop(rproc);
|
||||
if (ret) {
|
||||
dev_err(dev, "can't stop rproc: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* if in crash state, unlock crash handler */
|
||||
if (rproc->state == RPROC_CRASHED)
|
||||
complete_all(&rproc->crash_comp);
|
||||
|
||||
rproc->state = RPROC_OFFLINE;
|
||||
|
||||
dev_info(dev, "stopped remote processor %s\n", rproc->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* rproc_trigger_recovery() - recover a remoteproc
|
||||
* @rproc: the remote processor
|
||||
@ -1006,23 +1051,40 @@ static int rproc_trigger_auto_boot(struct rproc *rproc)
|
||||
*/
|
||||
int rproc_trigger_recovery(struct rproc *rproc)
|
||||
{
|
||||
dev_err(&rproc->dev, "recovering %s\n", rproc->name);
|
||||
const struct firmware *firmware_p;
|
||||
struct device *dev = &rproc->dev;
|
||||
int ret;
|
||||
|
||||
dev_err(dev, "recovering %s\n", rproc->name);
|
||||
|
||||
init_completion(&rproc->crash_comp);
|
||||
|
||||
/* shut down the remote */
|
||||
/* TODO: make sure this works with rproc->power > 1 */
|
||||
rproc_shutdown(rproc);
|
||||
ret = mutex_lock_interruptible(&rproc->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rproc_stop(rproc);
|
||||
if (ret)
|
||||
goto unlock_mutex;
|
||||
|
||||
/* wait until there is no more rproc users */
|
||||
wait_for_completion(&rproc->crash_comp);
|
||||
|
||||
/*
|
||||
* boot the remote processor up again
|
||||
*/
|
||||
rproc_boot(rproc);
|
||||
/* load firmware */
|
||||
ret = request_firmware(&firmware_p, rproc->firmware, dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "request_firmware failed: %d\n", ret);
|
||||
goto unlock_mutex;
|
||||
}
|
||||
|
||||
return 0;
|
||||
/* boot the remote processor up again */
|
||||
ret = rproc_start(rproc, firmware_p);
|
||||
|
||||
release_firmware(firmware_p);
|
||||
|
||||
unlock_mutex:
|
||||
mutex_unlock(&rproc->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1163,14 +1225,9 @@ void rproc_shutdown(struct rproc *rproc)
|
||||
if (!atomic_dec_and_test(&rproc->power))
|
||||
goto out;
|
||||
|
||||
/* remove any subdevices for the remote processor */
|
||||
rproc_remove_subdevices(rproc);
|
||||
|
||||
/* power off the remote processor */
|
||||
ret = rproc->ops->stop(rproc);
|
||||
ret = rproc_stop(rproc);
|
||||
if (ret) {
|
||||
atomic_inc(&rproc->power);
|
||||
dev_err(dev, "can't stop rproc: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -1183,15 +1240,6 @@ void rproc_shutdown(struct rproc *rproc)
|
||||
kfree(rproc->cached_table);
|
||||
rproc->cached_table = NULL;
|
||||
rproc->table_ptr = NULL;
|
||||
|
||||
/* if in crash state, unlock crash handler */
|
||||
if (rproc->state == RPROC_CRASHED)
|
||||
complete_all(&rproc->crash_comp);
|
||||
|
||||
rproc->state = RPROC_OFFLINE;
|
||||
|
||||
dev_info(dev, "stopped remote processor %s\n", rproc->name);
|
||||
|
||||
out:
|
||||
mutex_unlock(&rproc->lock);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user