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:
Linus Torvalds 2017-07-06 15:40:14 -07:00
commit 9a6293c321
6 changed files with 787 additions and 104 deletions

View File

@ -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>;
};
};

View File

@ -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

View File

@ -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

View File

@ -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

View 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");

View File

@ -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);
}