This pull request contains:
- Some refactoring, cleanups and small improvements from Sjur Brændeland. The improvements are mainly about better supporting varios virtio properties (such as virtio's config space, status and features). I now see that I messed up while commiting one of Sjur's patches and erroneously put myself as the author, as well as letting a nasty typo sneak in. I will not fix this in order to avoid rebasing the patches. Sjur - sorry! - A new remoteproc driver for OMAP-L13x (technically a DaVinci platform) from Robert Tivy. - Extend OMAP support to OMAP5 as well, from Vincent Stehlé. - Fix Kconfig VIRTUALIZATION dependency, from Suman Anna (a non-critical fix which arrived late during the rc cycle). -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJRiPCwAAoJELLolMlTRIoMRoQP/ivAF3r9jPaMizky82T0mDs0 kUPz6sz0zwb412h/IVtCo0ChJ1Jv4rVgo5u4klzDS5wfOutRiwDFIa8ZQF484nRb gTIFHtqy+xk82aHAR9PWi2G3QWJ9hplZ7m52aaOIG7E6BaY3EfWL7fnt5QGBAb/O vcR3rrj7QNQcB963PQl7cYWSX966ipzfX1g7VxFk/Ah8m9rjQp0xZgg3a5svGmq7 5iQcdxiXm63RAfgN9kbZrxWjX5/7m1N1WOfK5CE1H2jnGObttNdhN5xr7Ky5TXyN sVJvoVhIbylSzDVl/LH6v/V9T/is+VCZOPs+erXVGv2vGctNY5Cs+RAVPF4bz4UC R9/LDRbdZtbvcKo0TiPAjsIN3t+Rg1EDBnjOImi5kN5bYASpfcoRE0hpicL51S01 HzdT5+k3Xo5RS0EWakITc1ecc+7kfMVjLjn59/Im+bWVGhv2Lzq1pCFQ+XAcfvL9 FrQCGYCn8QZFWBHgeDRzg1ysK4hDNPo1UkEbLTTXjFcaMLeMczSSRagCi4Fk2RjL QYmtDzKbm7Tc03Yi6ac7A8I2lgeQRsXNENLxtTtONu9rNKf4O0Of86KCLhtUtDqb 6SUxn2ZvG+maXwyrqbIjak+CKphimOnOEEIxur3viuKLs4sM8yuZCzU+v56bPbdb Pqcdo7VOhqAwTBoW3NUp =IUfO -----END PGP SIGNATURE----- Merge tag 'remoteproc-3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad/remoteproc Pull remoteproc update from Ohad Ben-Cohen: - Some refactoring, cleanups and small improvements from Sjur Brændeland. The improvements are mainly about better supporting varios virtio properties (such as virtio's config space, status and features). I now see that I messed up while commiting one of Sjur's patches and erroneously put myself as the author, as well as letting a nasty typo sneak in. I will not fix this in order to avoid rebasing the patches. Sjur - sorry! - A new remoteproc driver for OMAP-L13x (technically a DaVinci platform) from Robert Tivy. - Extend OMAP support to OMAP5 as well, from Vincent Stehlé. - Fix Kconfig VIRTUALIZATION dependency, from Suman Anna (a non-critical fix which arrived late during the rc cycle). * tag 'remoteproc-3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad/remoteproc: remoteproc: fix kconfig dependencies for VIRTIO remoteproc/davinci: add a remoteproc driver for OMAP-L13x DSP remoteproc: support default firmware name in rproc_alloc() remoteproc/omap: support OMAP5 too remoteproc: set vring addresses in resource table remoteproc: support virtio config space. remoteproc: perserve resource table data remoteproc: calculate max_notifyid by counting vrings remoteproc: code cleanup of resource parsing remoteproc: parse STE-firmware and find resource table address remoteproc: add find_loaded_rsc_table firmware ops remoteproc: refactor rproc_elf_find_rsc_table()
This commit is contained in:
Коммит
de9c9f86be
|
@ -4,13 +4,15 @@ menu "Remoteproc drivers"
|
|||
config REMOTEPROC
|
||||
tristate
|
||||
depends on HAS_DMA
|
||||
select CRC32
|
||||
select FW_LOADER
|
||||
select VIRTIO
|
||||
select VIRTUALIZATION
|
||||
|
||||
config OMAP_REMOTEPROC
|
||||
tristate "OMAP remoteproc support"
|
||||
depends on HAS_DMA
|
||||
depends on ARCH_OMAP4
|
||||
depends on ARCH_OMAP4 || SOC_OMAP5
|
||||
depends on OMAP_IOMMU
|
||||
depends on OMAP_MBOX_FWK
|
||||
select REMOTEPROC
|
||||
|
@ -38,4 +40,27 @@ config STE_MODEM_RPROC
|
|||
This can be either built-in or a loadable module.
|
||||
If unsure say N.
|
||||
|
||||
config DA8XX_REMOTEPROC
|
||||
tristate "DA8xx/OMAP-L13x remoteproc support"
|
||||
depends on ARCH_DAVINCI_DA8XX
|
||||
select CMA
|
||||
select REMOTEPROC
|
||||
select RPMSG
|
||||
help
|
||||
Say y here to support DA8xx/OMAP-L13x remote processors via the
|
||||
remote processor framework.
|
||||
|
||||
You want to say y here in order to enable AMP
|
||||
use-cases to run on your platform (multimedia codecs are
|
||||
offloaded to remote DSP processors using this framework).
|
||||
|
||||
This module controls the name of the firmware file that gets
|
||||
loaded on the DSP. This file must reside in the /lib/firmware
|
||||
directory. It can be specified via the module parameter
|
||||
da8xx_fw_name=<filename>, and if not specified will default to
|
||||
"rproc-dsp-fw".
|
||||
|
||||
It's safe to say n here if you're not interested in multimedia
|
||||
offloading.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -9,3 +9,4 @@ remoteproc-y += remoteproc_virtio.o
|
|||
remoteproc-y += remoteproc_elf_loader.o
|
||||
obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o
|
||||
obj-$(CONFIG_STE_MODEM_RPROC) += ste_modem_rproc.o
|
||||
obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o
|
||||
|
|
|
@ -0,0 +1,324 @@
|
|||
/*
|
||||
* Remote processor machine-specific module for DA8XX
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/remoteproc.h>
|
||||
|
||||
#include <mach/clock.h> /* for davinci_clk_reset_assert/deassert() */
|
||||
|
||||
#include "remoteproc_internal.h"
|
||||
|
||||
static char *da8xx_fw_name;
|
||||
module_param(da8xx_fw_name, charp, S_IRUGO);
|
||||
MODULE_PARM_DESC(da8xx_fw_name,
|
||||
"\n\t\tName of DSP firmware file in /lib/firmware"
|
||||
" (if not specified defaults to 'rproc-dsp-fw')");
|
||||
|
||||
/*
|
||||
* OMAP-L138 Technical References:
|
||||
* http://www.ti.com/product/omap-l138
|
||||
*/
|
||||
#define SYSCFG_CHIPSIG0 BIT(0)
|
||||
#define SYSCFG_CHIPSIG1 BIT(1)
|
||||
#define SYSCFG_CHIPSIG2 BIT(2)
|
||||
#define SYSCFG_CHIPSIG3 BIT(3)
|
||||
#define SYSCFG_CHIPSIG4 BIT(4)
|
||||
|
||||
/**
|
||||
* struct da8xx_rproc - da8xx remote processor instance state
|
||||
* @rproc: rproc handle
|
||||
* @dsp_clk: placeholder for platform's DSP clk
|
||||
* @ack_fxn: chip-specific ack function for ack'ing irq
|
||||
* @irq_data: ack_fxn function parameter
|
||||
* @chipsig: virt ptr to DSP interrupt registers (CHIPSIG & CHIPSIG_CLR)
|
||||
* @bootreg: virt ptr to DSP boot address register (HOST1CFG)
|
||||
* @irq: irq # used by this instance
|
||||
*/
|
||||
struct da8xx_rproc {
|
||||
struct rproc *rproc;
|
||||
struct clk *dsp_clk;
|
||||
void (*ack_fxn)(struct irq_data *data);
|
||||
struct irq_data *irq_data;
|
||||
void __iomem *chipsig;
|
||||
void __iomem *bootreg;
|
||||
int irq;
|
||||
};
|
||||
|
||||
/**
|
||||
* handle_event() - inbound virtqueue message workqueue function
|
||||
*
|
||||
* This function is registered as a kernel thread and is scheduled by the
|
||||
* kernel handler.
|
||||
*/
|
||||
static irqreturn_t handle_event(int irq, void *p)
|
||||
{
|
||||
struct rproc *rproc = (struct rproc *)p;
|
||||
|
||||
/* Process incoming buffers on all our vrings */
|
||||
rproc_vq_interrupt(rproc, 0);
|
||||
rproc_vq_interrupt(rproc, 1);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* da8xx_rproc_callback() - inbound virtqueue message handler
|
||||
*
|
||||
* This handler is invoked directly by the kernel whenever the remote
|
||||
* core (DSP) has modified the state of a virtqueue. There is no
|
||||
* "payload" message indicating the virtqueue index as is the case with
|
||||
* mailbox-based implementations on OMAP4. As such, this handler "polls"
|
||||
* each known virtqueue index for every invocation.
|
||||
*/
|
||||
static irqreturn_t da8xx_rproc_callback(int irq, void *p)
|
||||
{
|
||||
struct rproc *rproc = (struct rproc *)p;
|
||||
struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
|
||||
u32 chipsig;
|
||||
|
||||
chipsig = readl(drproc->chipsig);
|
||||
if (chipsig & SYSCFG_CHIPSIG0) {
|
||||
/* Clear interrupt level source */
|
||||
writel(SYSCFG_CHIPSIG0, drproc->chipsig + 4);
|
||||
|
||||
/*
|
||||
* ACK intr to AINTC.
|
||||
*
|
||||
* It has already been ack'ed by the kernel before calling
|
||||
* this function, but since the ARM<->DSP interrupts in the
|
||||
* CHIPSIG register are "level" instead of "pulse" variety,
|
||||
* we need to ack it after taking down the level else we'll
|
||||
* be called again immediately after returning.
|
||||
*/
|
||||
drproc->ack_fxn(drproc->irq_data);
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int da8xx_rproc_start(struct rproc *rproc)
|
||||
{
|
||||
struct device *dev = rproc->dev.parent;
|
||||
struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
|
||||
struct clk *dsp_clk = drproc->dsp_clk;
|
||||
|
||||
/* hw requires the start (boot) address be on 1KB boundary */
|
||||
if (rproc->bootaddr & 0x3ff) {
|
||||
dev_err(dev, "invalid boot address: must be aligned to 1KB\n");
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel(rproc->bootaddr, drproc->bootreg);
|
||||
|
||||
clk_enable(dsp_clk);
|
||||
davinci_clk_reset_deassert(dsp_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int da8xx_rproc_stop(struct rproc *rproc)
|
||||
{
|
||||
struct da8xx_rproc *drproc = rproc->priv;
|
||||
|
||||
clk_disable(drproc->dsp_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* kick a virtqueue */
|
||||
static void da8xx_rproc_kick(struct rproc *rproc, int vqid)
|
||||
{
|
||||
struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
|
||||
|
||||
/* Interupt remote proc */
|
||||
writel(SYSCFG_CHIPSIG2, drproc->chipsig);
|
||||
}
|
||||
|
||||
static struct rproc_ops da8xx_rproc_ops = {
|
||||
.start = da8xx_rproc_start,
|
||||
.stop = da8xx_rproc_stop,
|
||||
.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_RET(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;
|
||||
struct da8xx_rproc *drproc;
|
||||
struct rproc *rproc;
|
||||
struct irq_data *irq_data;
|
||||
struct resource *bootreg_res;
|
||||
struct resource *chipsig_res;
|
||||
struct clk *dsp_clk;
|
||||
void __iomem *chipsig;
|
||||
void __iomem *bootreg;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "platform_get_irq(pdev, 0) error: %d\n", irq);
|
||||
return irq;
|
||||
}
|
||||
|
||||
irq_data = irq_get_irq_data(irq);
|
||||
if (!irq_data) {
|
||||
dev_err(dev, "irq_get_irq_data(%d): NULL\n", irq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bootreg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!bootreg_res) {
|
||||
dev_err(dev,
|
||||
"platform_get_resource(IORESOURCE_MEM, 0): NULL\n");
|
||||
return -EADDRNOTAVAIL;
|
||||
}
|
||||
|
||||
chipsig_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (!chipsig_res) {
|
||||
dev_err(dev,
|
||||
"platform_get_resource(IORESOURCE_MEM, 1): NULL\n");
|
||||
return -EADDRNOTAVAIL;
|
||||
}
|
||||
|
||||
bootreg = devm_ioremap_resource(dev, bootreg_res);
|
||||
if (IS_ERR(bootreg))
|
||||
return PTR_ERR(bootreg);
|
||||
|
||||
chipsig = devm_ioremap_resource(dev, chipsig_res);
|
||||
if (IS_ERR(chipsig))
|
||||
return PTR_ERR(chipsig);
|
||||
|
||||
dsp_clk = devm_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);
|
||||
}
|
||||
|
||||
rproc = rproc_alloc(dev, "dsp", &da8xx_rproc_ops, da8xx_fw_name,
|
||||
sizeof(*drproc));
|
||||
if (!rproc)
|
||||
return -ENOMEM;
|
||||
|
||||
drproc = rproc->priv;
|
||||
drproc->rproc = rproc;
|
||||
|
||||
platform_set_drvdata(pdev, rproc);
|
||||
|
||||
/* everything the ISR needs is now setup, so hook it up */
|
||||
ret = devm_request_threaded_irq(dev, irq, da8xx_rproc_callback,
|
||||
handle_event, 0, "da8xx-remoteproc",
|
||||
rproc);
|
||||
if (ret) {
|
||||
dev_err(dev, "devm_request_threaded_irq error: %d\n", ret);
|
||||
goto free_rproc;
|
||||
}
|
||||
|
||||
/*
|
||||
* rproc_add() can end up enabling the DSP's clk with the DSP
|
||||
* *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);
|
||||
if (ret)
|
||||
goto free_rproc;
|
||||
|
||||
drproc->chipsig = chipsig;
|
||||
drproc->bootreg = bootreg;
|
||||
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) {
|
||||
dev_err(dev, "rproc_add failed: %d\n", ret);
|
||||
goto free_rproc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
free_rproc:
|
||||
rproc_put(rproc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
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
|
||||
* the device is being removed. This should prevent that.
|
||||
*/
|
||||
disable_irq(drproc->irq);
|
||||
|
||||
devm_clk_put(dev, drproc->dsp_clk);
|
||||
|
||||
rproc_del(rproc);
|
||||
rproc_put(rproc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver da8xx_rproc_driver = {
|
||||
.probe = da8xx_rproc_probe,
|
||||
.remove = da8xx_rproc_remove,
|
||||
.driver = {
|
||||
.name = "davinci-rproc",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(da8xx_rproc_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("DA8XX Remote Processor control driver");
|
|
@ -37,6 +37,7 @@
|
|||
#include <linux/iommu.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/elf.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/virtio_ids.h>
|
||||
#include <linux/virtio_ring.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
@ -45,7 +46,8 @@
|
|||
|
||||
typedef int (*rproc_handle_resources_t)(struct rproc *rproc,
|
||||
struct resource_table *table, int len);
|
||||
typedef int (*rproc_handle_resource_t)(struct rproc *rproc, void *, int avail);
|
||||
typedef int (*rproc_handle_resource_t)(struct rproc *rproc,
|
||||
void *, int offset, int avail);
|
||||
|
||||
/* Unique indices for remoteproc devices */
|
||||
static DEFINE_IDA(rproc_dev_index);
|
||||
|
@ -192,6 +194,7 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
|
|||
struct rproc *rproc = rvdev->rproc;
|
||||
struct device *dev = &rproc->dev;
|
||||
struct rproc_vring *rvring = &rvdev->vring[i];
|
||||
struct fw_rsc_vdev *rsc;
|
||||
dma_addr_t dma;
|
||||
void *va;
|
||||
int ret, size, notifyid;
|
||||
|
@ -202,7 +205,6 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
|
|||
/*
|
||||
* Allocate non-cacheable memory for the vring. In the future
|
||||
* this call will also configure the IOMMU for us
|
||||
* TODO: let the rproc know the da of this vring
|
||||
*/
|
||||
va = dma_alloc_coherent(dev->parent, size, &dma, GFP_KERNEL);
|
||||
if (!va) {
|
||||
|
@ -213,7 +215,6 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
|
|||
/*
|
||||
* Assign an rproc-wide unique index for this vring
|
||||
* TODO: assign a notifyid for rvdev updates as well
|
||||
* TODO: let the rproc know the notifyid of this vring
|
||||
* TODO: support predefined notifyids (via resource table)
|
||||
*/
|
||||
ret = idr_alloc(&rproc->notifyids, rvring, 0, 0, GFP_KERNEL);
|
||||
|
@ -224,9 +225,6 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
|
|||
}
|
||||
notifyid = ret;
|
||||
|
||||
/* Store largest notifyid */
|
||||
rproc->max_notifyid = max(rproc->max_notifyid, notifyid);
|
||||
|
||||
dev_dbg(dev, "vring%d: va %p dma %llx size %x idr %d\n", i, va,
|
||||
(unsigned long long)dma, size, notifyid);
|
||||
|
||||
|
@ -234,6 +232,15 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
|
|||
rvring->dma = dma;
|
||||
rvring->notifyid = notifyid;
|
||||
|
||||
/*
|
||||
* Let the rproc know the notifyid and da of this vring.
|
||||
* Not all platforms use dma_alloc_coherent to automatically
|
||||
* set up the iommu. In this case the device address (da) will
|
||||
* hold the physical address and not the device address.
|
||||
*/
|
||||
rsc = (void *)rproc->table_ptr + rvdev->rsc_offset;
|
||||
rsc->vring[i].da = dma;
|
||||
rsc->vring[i].notifyid = notifyid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -268,25 +275,20 @@ rproc_parse_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int rproc_max_notifyid(int id, void *p, void *data)
|
||||
{
|
||||
int *maxid = data;
|
||||
*maxid = max(*maxid, id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rproc_free_vring(struct rproc_vring *rvring)
|
||||
{
|
||||
int size = PAGE_ALIGN(vring_size(rvring->len, rvring->align));
|
||||
struct rproc *rproc = rvring->rvdev->rproc;
|
||||
int maxid = 0;
|
||||
int idx = rvring->rvdev->vring - rvring;
|
||||
struct fw_rsc_vdev *rsc;
|
||||
|
||||
dma_free_coherent(rproc->dev.parent, size, rvring->va, rvring->dma);
|
||||
idr_remove(&rproc->notifyids, rvring->notifyid);
|
||||
|
||||
/* Find the largest remaining notifyid */
|
||||
idr_for_each(&rproc->notifyids, rproc_max_notifyid, &maxid);
|
||||
rproc->max_notifyid = maxid;
|
||||
/* reset resource entry info */
|
||||
rsc = (void *)rproc->table_ptr + rvring->rvdev->rsc_offset;
|
||||
rsc->vring[idx].da = 0;
|
||||
rsc->vring[idx].notifyid = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -317,7 +319,7 @@ void rproc_free_vring(struct rproc_vring *rvring)
|
|||
* Returns 0 on success, or an appropriate error code otherwise
|
||||
*/
|
||||
static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
|
||||
int avail)
|
||||
int offset, int avail)
|
||||
{
|
||||
struct device *dev = &rproc->dev;
|
||||
struct rproc_vdev *rvdev;
|
||||
|
@ -358,8 +360,8 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
|
|||
goto free_rvdev;
|
||||
}
|
||||
|
||||
/* remember the device features */
|
||||
rvdev->dfeatures = rsc->dfeatures;
|
||||
/* remember the resource offset*/
|
||||
rvdev->rsc_offset = offset;
|
||||
|
||||
list_add_tail(&rvdev->node, &rproc->rvdevs);
|
||||
|
||||
|
@ -394,7 +396,7 @@ free_rvdev:
|
|||
* Returns 0 on success, or an appropriate error code otherwise
|
||||
*/
|
||||
static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
|
||||
int avail)
|
||||
int offset, int avail)
|
||||
{
|
||||
struct rproc_mem_entry *trace;
|
||||
struct device *dev = &rproc->dev;
|
||||
|
@ -476,7 +478,7 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
|
|||
* are outside those ranges.
|
||||
*/
|
||||
static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc,
|
||||
int avail)
|
||||
int offset, int avail)
|
||||
{
|
||||
struct rproc_mem_entry *mapping;
|
||||
struct device *dev = &rproc->dev;
|
||||
|
@ -549,7 +551,9 @@ out:
|
|||
* pressure is important; it may have a substantial impact on performance.
|
||||
*/
|
||||
static int rproc_handle_carveout(struct rproc *rproc,
|
||||
struct fw_rsc_carveout *rsc, int avail)
|
||||
struct fw_rsc_carveout *rsc,
|
||||
int offset, int avail)
|
||||
|
||||
{
|
||||
struct rproc_mem_entry *carveout, *mapping;
|
||||
struct device *dev = &rproc->dev;
|
||||
|
@ -671,28 +675,45 @@ free_carv:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int rproc_count_vrings(struct rproc *rproc, struct fw_rsc_vdev *rsc,
|
||||
int offset, int avail)
|
||||
{
|
||||
/* Summarize the number of notification IDs */
|
||||
rproc->max_notifyid += rsc->num_of_vrings;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* A lookup table for resource handlers. The indices are defined in
|
||||
* enum fw_resource_type.
|
||||
*/
|
||||
static rproc_handle_resource_t rproc_handle_rsc[] = {
|
||||
static rproc_handle_resource_t rproc_loading_handlers[RSC_LAST] = {
|
||||
[RSC_CARVEOUT] = (rproc_handle_resource_t)rproc_handle_carveout,
|
||||
[RSC_DEVMEM] = (rproc_handle_resource_t)rproc_handle_devmem,
|
||||
[RSC_TRACE] = (rproc_handle_resource_t)rproc_handle_trace,
|
||||
[RSC_VDEV] = NULL, /* VDEVs were handled upon registrarion */
|
||||
};
|
||||
|
||||
static rproc_handle_resource_t rproc_vdev_handler[RSC_LAST] = {
|
||||
[RSC_VDEV] = (rproc_handle_resource_t)rproc_handle_vdev,
|
||||
};
|
||||
|
||||
static rproc_handle_resource_t rproc_count_vrings_handler[RSC_LAST] = {
|
||||
[RSC_VDEV] = (rproc_handle_resource_t)rproc_count_vrings,
|
||||
};
|
||||
|
||||
/* handle firmware resource entries before booting the remote processor */
|
||||
static int
|
||||
rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len)
|
||||
static int rproc_handle_resources(struct rproc *rproc, int len,
|
||||
rproc_handle_resource_t handlers[RSC_LAST])
|
||||
{
|
||||
struct device *dev = &rproc->dev;
|
||||
rproc_handle_resource_t handler;
|
||||
int ret = 0, i;
|
||||
|
||||
for (i = 0; i < table->num; i++) {
|
||||
int offset = table->offset[i];
|
||||
struct fw_rsc_hdr *hdr = (void *)table + offset;
|
||||
for (i = 0; i < rproc->table_ptr->num; i++) {
|
||||
int offset = rproc->table_ptr->offset[i];
|
||||
struct fw_rsc_hdr *hdr = (void *)rproc->table_ptr + offset;
|
||||
int avail = len - offset - sizeof(*hdr);
|
||||
void *rsc = (void *)hdr + sizeof(*hdr);
|
||||
|
||||
|
@ -709,45 +730,11 @@ rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len
|
|||
continue;
|
||||
}
|
||||
|
||||
handler = rproc_handle_rsc[hdr->type];
|
||||
handler = handlers[hdr->type];
|
||||
if (!handler)
|
||||
continue;
|
||||
|
||||
ret = handler(rproc, rsc, avail);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* handle firmware resource entries while registering the remote processor */
|
||||
static int
|
||||
rproc_handle_virtio_rsc(struct rproc *rproc, struct resource_table *table, int len)
|
||||
{
|
||||
struct device *dev = &rproc->dev;
|
||||
int ret = 0, i;
|
||||
|
||||
for (i = 0; i < table->num; i++) {
|
||||
int offset = table->offset[i];
|
||||
struct fw_rsc_hdr *hdr = (void *)table + offset;
|
||||
int avail = len - offset - sizeof(*hdr);
|
||||
struct fw_rsc_vdev *vrsc;
|
||||
|
||||
/* make sure table isn't truncated */
|
||||
if (avail < 0) {
|
||||
dev_err(dev, "rsc table is truncated\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "%s: rsc type %d\n", __func__, hdr->type);
|
||||
|
||||
if (hdr->type != RSC_VDEV)
|
||||
continue;
|
||||
|
||||
vrsc = (struct fw_rsc_vdev *)hdr->data;
|
||||
|
||||
ret = rproc_handle_vdev(rproc, vrsc, avail);
|
||||
ret = handler(rproc, rsc, offset + sizeof(*hdr), avail);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
@ -805,9 +792,12 @@ 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;
|
||||
struct resource_table *table, *loaded_table;
|
||||
int ret, tablesz;
|
||||
|
||||
if (!rproc->table_ptr)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = rproc_fw_sanity_check(rproc, fw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -833,8 +823,15 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
|
|||
goto clean_up;
|
||||
}
|
||||
|
||||
/* Verify that resource table in loaded fw is unchanged */
|
||||
if (rproc->table_csum != crc32(0, table, tablesz)) {
|
||||
dev_err(dev, "resource checksum failed, fw changed?\n");
|
||||
ret = -EINVAL;
|
||||
goto clean_up;
|
||||
}
|
||||
|
||||
/* handle fw resources which are required to boot rproc */
|
||||
ret = rproc_handle_boot_rsc(rproc, table, tablesz);
|
||||
ret = rproc_handle_resources(rproc, tablesz, rproc_loading_handlers);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to process resources: %d\n", ret);
|
||||
goto clean_up;
|
||||
|
@ -847,6 +844,19 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
|
|||
goto clean_up;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
loaded_table = rproc_find_loaded_rsc_table(rproc, fw);
|
||||
if (!loaded_table)
|
||||
goto clean_up;
|
||||
|
||||
memcpy(loaded_table, rproc->cached_table, tablesz);
|
||||
|
||||
/* power up the remote processor */
|
||||
ret = rproc->ops->start(rproc);
|
||||
if (ret) {
|
||||
|
@ -854,6 +864,13 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
|
|||
goto clean_up;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update table_ptr so that all subsequent vring allocations and
|
||||
* virtio fields manipulation update the actual loaded resource table
|
||||
* in device memory.
|
||||
*/
|
||||
rproc->table_ptr = loaded_table;
|
||||
|
||||
rproc->state = RPROC_RUNNING;
|
||||
|
||||
dev_info(dev, "remote processor %s is now up\n", rproc->name);
|
||||
|
@ -888,11 +905,30 @@ static void rproc_fw_config_virtio(const struct firmware *fw, void *context)
|
|||
if (!table)
|
||||
goto out;
|
||||
|
||||
/* look for virtio devices and register them */
|
||||
ret = rproc_handle_virtio_rsc(rproc, table, tablesz);
|
||||
rproc->table_csum = crc32(0, table, tablesz);
|
||||
|
||||
/*
|
||||
* Create a copy of the resource table. When a virtio device starts
|
||||
* and calls vring_new_virtqueue() the address of the allocated vring
|
||||
* will be stored in the cached_table. Before the device is started,
|
||||
* cached_table will be copied into devic memory.
|
||||
*/
|
||||
rproc->cached_table = kmalloc(tablesz, GFP_KERNEL);
|
||||
if (!rproc->cached_table)
|
||||
goto out;
|
||||
|
||||
memcpy(rproc->cached_table, table, tablesz);
|
||||
rproc->table_ptr = rproc->cached_table;
|
||||
|
||||
/* count the number of notify-ids */
|
||||
rproc->max_notifyid = -1;
|
||||
ret = rproc_handle_resources(rproc, tablesz, rproc_count_vrings_handler);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* look for virtio devices and register them */
|
||||
ret = rproc_handle_resources(rproc, tablesz, rproc_vdev_handler);
|
||||
|
||||
out:
|
||||
release_firmware(fw);
|
||||
/* allow rproc_del() contexts, if any, to proceed */
|
||||
|
@ -950,6 +986,9 @@ int rproc_trigger_recovery(struct rproc *rproc)
|
|||
/* wait until there is no more rproc users */
|
||||
wait_for_completion(&rproc->crash_comp);
|
||||
|
||||
/* Free the copy of the resource table */
|
||||
kfree(rproc->cached_table);
|
||||
|
||||
return rproc_add_virtio_devices(rproc);
|
||||
}
|
||||
|
||||
|
@ -1105,6 +1144,9 @@ void rproc_shutdown(struct rproc *rproc)
|
|||
|
||||
rproc_disable_iommu(rproc);
|
||||
|
||||
/* Give the next start a clean resource table */
|
||||
rproc->table_ptr = rproc->cached_table;
|
||||
|
||||
/* if in crash state, unlock crash handler */
|
||||
if (rproc->state == RPROC_CRASHED)
|
||||
complete_all(&rproc->crash_comp);
|
||||
|
@ -1196,11 +1238,11 @@ static struct device_type rproc_type = {
|
|||
* @dev: the underlying device
|
||||
* @name: name of this remote processor
|
||||
* @ops: platform-specific handlers (mainly start/stop)
|
||||
* @firmware: name of firmware file to load
|
||||
* @firmware: name of firmware file to load, can be NULL
|
||||
* @len: length of private data needed by the rproc driver (in bytes)
|
||||
*
|
||||
* Allocates a new remote processor handle, but does not register
|
||||
* it yet.
|
||||
* it yet. if @firmware is NULL, a default name is used.
|
||||
*
|
||||
* This function should be used by rproc implementations during initialization
|
||||
* of the remote processor.
|
||||
|
@ -1219,19 +1261,39 @@ 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 = 0;
|
||||
|
||||
if (!dev || !name || !ops)
|
||||
return NULL;
|
||||
|
||||
rproc = kzalloc(sizeof(struct rproc) + len, GFP_KERNEL);
|
||||
if (!firmware)
|
||||
/*
|
||||
* Make room for default firmware name (minus %s plus '\0').
|
||||
* If the caller didn't pass in a firmware name then
|
||||
* construct a default name. We're already glomming 'len'
|
||||
* bytes onto the end of the struct rproc allocation, so do
|
||||
* a few more for the default firmware name (but only if
|
||||
* the caller doesn't pass one).
|
||||
*/
|
||||
name_len = strlen(name) + strlen(template) - 2 + 1;
|
||||
|
||||
rproc = kzalloc(sizeof(struct rproc) + len + name_len, GFP_KERNEL);
|
||||
if (!rproc) {
|
||||
dev_err(dev, "%s: kzalloc failed\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!firmware) {
|
||||
p = (char *)rproc + sizeof(struct rproc) + len;
|
||||
snprintf(p, name_len, template, name);
|
||||
} else {
|
||||
p = (char *)firmware;
|
||||
}
|
||||
|
||||
rproc->firmware = p;
|
||||
rproc->name = name;
|
||||
rproc->ops = ops;
|
||||
rproc->firmware = firmware;
|
||||
rproc->priv = &rproc[1];
|
||||
|
||||
device_initialize(&rproc->dev);
|
||||
|
@ -1315,6 +1377,9 @@ int rproc_del(struct rproc *rproc)
|
|||
list_for_each_entry_safe(rvdev, tmp, &rproc->rvdevs, node)
|
||||
rproc_remove_virtio_dev(rvdev);
|
||||
|
||||
/* Free the copy of the resource table */
|
||||
kfree(rproc->cached_table);
|
||||
|
||||
device_del(&rproc->dev);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -208,41 +208,22 @@ rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* rproc_elf_find_rsc_table() - find the resource table
|
||||
* @rproc: the rproc handle
|
||||
* @fw: the ELF firmware image
|
||||
* @tablesz: place holder for providing back the table size
|
||||
*
|
||||
* This function finds the resource table inside the remote processor's
|
||||
* firmware. It is used both upon the registration of @rproc (in order
|
||||
* to look for and register the supported virito devices), and when the
|
||||
* @rproc is booted.
|
||||
*
|
||||
* Returns the pointer to the resource table if it is found, and write its
|
||||
* size into @tablesz. If a valid table isn't found, NULL is returned
|
||||
* (and @tablesz isn't set).
|
||||
*/
|
||||
static struct resource_table *
|
||||
rproc_elf_find_rsc_table(struct rproc *rproc, const struct firmware *fw,
|
||||
int *tablesz)
|
||||
static struct elf32_shdr *
|
||||
find_table(struct device *dev, struct elf32_hdr *ehdr, size_t fw_size)
|
||||
{
|
||||
struct elf32_hdr *ehdr;
|
||||
struct elf32_shdr *shdr;
|
||||
const char *name_table;
|
||||
struct device *dev = &rproc->dev;
|
||||
struct resource_table *table = NULL;
|
||||
int i;
|
||||
const u8 *elf_data = fw->data;
|
||||
const char *name_table;
|
||||
struct resource_table *table = NULL;
|
||||
const u8 *elf_data = (void *)ehdr;
|
||||
|
||||
ehdr = (struct elf32_hdr *)elf_data;
|
||||
/* look for the resource table and handle it */
|
||||
shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff);
|
||||
name_table = elf_data + shdr[ehdr->e_shstrndx].sh_offset;
|
||||
|
||||
/* look for the resource table and handle it */
|
||||
for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
|
||||
int size = shdr->sh_size;
|
||||
int offset = shdr->sh_offset;
|
||||
u32 size = shdr->sh_size;
|
||||
u32 offset = shdr->sh_offset;
|
||||
|
||||
if (strcmp(name_table + shdr->sh_name, ".resource_table"))
|
||||
continue;
|
||||
|
@ -250,7 +231,7 @@ rproc_elf_find_rsc_table(struct rproc *rproc, const struct firmware *fw,
|
|||
table = (struct resource_table *)(elf_data + offset);
|
||||
|
||||
/* make sure we have the entire table */
|
||||
if (offset + size > fw->size) {
|
||||
if (offset + size > fw_size || offset + size < size) {
|
||||
dev_err(dev, "resource table truncated\n");
|
||||
return NULL;
|
||||
}
|
||||
|
@ -280,16 +261,77 @@ rproc_elf_find_rsc_table(struct rproc *rproc, const struct firmware *fw,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
*tablesz = shdr->sh_size;
|
||||
break;
|
||||
return shdr;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* rproc_elf_find_rsc_table() - find the resource table
|
||||
* @rproc: the rproc handle
|
||||
* @fw: the ELF firmware image
|
||||
* @tablesz: place holder for providing back the table size
|
||||
*
|
||||
* This function finds the resource table inside the remote processor's
|
||||
* firmware. It is used both upon the registration of @rproc (in order
|
||||
* to look for and register the supported virito devices), and when the
|
||||
* @rproc is booted.
|
||||
*
|
||||
* Returns the pointer to the resource table if it is found, and write its
|
||||
* size into @tablesz. If a valid table isn't found, NULL is returned
|
||||
* (and @tablesz isn't set).
|
||||
*/
|
||||
static struct resource_table *
|
||||
rproc_elf_find_rsc_table(struct rproc *rproc, const struct firmware *fw,
|
||||
int *tablesz)
|
||||
{
|
||||
struct elf32_hdr *ehdr;
|
||||
struct elf32_shdr *shdr;
|
||||
struct device *dev = &rproc->dev;
|
||||
struct resource_table *table = NULL;
|
||||
const u8 *elf_data = fw->data;
|
||||
|
||||
ehdr = (struct elf32_hdr *)elf_data;
|
||||
|
||||
shdr = find_table(dev, ehdr, fw->size);
|
||||
if (!shdr)
|
||||
return NULL;
|
||||
|
||||
table = (struct resource_table *)(elf_data + shdr->sh_offset);
|
||||
*tablesz = shdr->sh_size;
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
/**
|
||||
* rproc_elf_find_loaded_rsc_table() - find the loaded resource table
|
||||
* @rproc: the rproc handle
|
||||
* @fw: the ELF firmware image
|
||||
*
|
||||
* This function finds the location of the loaded resource table. Don't
|
||||
* call this function if the table wasn't loaded yet - it's a bug if you do.
|
||||
*
|
||||
* Returns the pointer to the resource table if it is found or NULL otherwise.
|
||||
* If the table wasn't loaded yet the result is unspecified.
|
||||
*/
|
||||
static struct resource_table *
|
||||
rproc_elf_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw)
|
||||
{
|
||||
struct elf32_hdr *ehdr = (struct elf32_hdr *)fw->data;
|
||||
struct elf32_shdr *shdr;
|
||||
|
||||
shdr = find_table(&rproc->dev, ehdr, fw->size);
|
||||
if (!shdr)
|
||||
return NULL;
|
||||
|
||||
return rproc_da_to_va(rproc, shdr->sh_addr, shdr->sh_size);
|
||||
}
|
||||
|
||||
const struct rproc_fw_ops rproc_elf_fw_ops = {
|
||||
.load = rproc_elf_load_segments,
|
||||
.find_rsc_table = rproc_elf_find_rsc_table,
|
||||
.find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table,
|
||||
.sanity_check = rproc_elf_sanity_check,
|
||||
.get_boot_addr = rproc_elf_get_boot_addr
|
||||
};
|
||||
|
|
|
@ -27,7 +27,8 @@ struct rproc;
|
|||
|
||||
/**
|
||||
* struct rproc_fw_ops - firmware format specific operations.
|
||||
* @find_rsc_table: finds the resource table inside the firmware image
|
||||
* @find_rsc_table: find the resource table inside the firmware image
|
||||
* @find_loaded_rsc_table: find the loaded resouce table
|
||||
* @load: load firmeware to memory, where the remote processor
|
||||
* expects to find it
|
||||
* @sanity_check: sanity check the fw image
|
||||
|
@ -37,6 +38,8 @@ struct rproc_fw_ops {
|
|||
struct resource_table *(*find_rsc_table) (struct rproc *rproc,
|
||||
const struct firmware *fw,
|
||||
int *tablesz);
|
||||
struct resource_table *(*find_loaded_rsc_table)(struct rproc *rproc,
|
||||
const struct firmware *fw);
|
||||
int (*load)(struct rproc *rproc, const struct firmware *fw);
|
||||
int (*sanity_check)(struct rproc *rproc, const struct firmware *fw);
|
||||
u32 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);
|
||||
|
@ -102,6 +105,16 @@ struct resource_table *rproc_find_rsc_table(struct rproc *rproc,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static inline
|
||||
struct resource_table *rproc_find_loaded_rsc_table(struct rproc *rproc,
|
||||
const struct firmware *fw)
|
||||
{
|
||||
if (rproc->fw_ops->find_loaded_rsc_table)
|
||||
return rproc->fw_ops->find_loaded_rsc_table(rproc, fw);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
extern const struct rproc_fw_ops rproc_elf_fw_ops;
|
||||
|
||||
#endif /* REMOTEPROC_INTERNAL_H */
|
||||
|
|
|
@ -173,25 +173,35 @@ error:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't support yet real virtio status semantics.
|
||||
*
|
||||
* The plan is to provide this via the VDEV resource entry
|
||||
* which is part of the firmware: this way the remote processor
|
||||
* will be able to access the status values as set by us.
|
||||
*/
|
||||
static u8 rproc_virtio_get_status(struct virtio_device *vdev)
|
||||
{
|
||||
return 0;
|
||||
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
|
||||
struct fw_rsc_vdev *rsc;
|
||||
|
||||
rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
|
||||
|
||||
return rsc->status;
|
||||
}
|
||||
|
||||
static void rproc_virtio_set_status(struct virtio_device *vdev, u8 status)
|
||||
{
|
||||
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
|
||||
struct fw_rsc_vdev *rsc;
|
||||
|
||||
rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
|
||||
|
||||
rsc->status = status;
|
||||
dev_dbg(&vdev->dev, "status: %d\n", status);
|
||||
}
|
||||
|
||||
static void rproc_virtio_reset(struct virtio_device *vdev)
|
||||
{
|
||||
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
|
||||
struct fw_rsc_vdev *rsc;
|
||||
|
||||
rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
|
||||
|
||||
rsc->status = 0;
|
||||
dev_dbg(&vdev->dev, "reset !\n");
|
||||
}
|
||||
|
||||
|
@ -199,13 +209,19 @@ static void rproc_virtio_reset(struct virtio_device *vdev)
|
|||
static u32 rproc_virtio_get_features(struct virtio_device *vdev)
|
||||
{
|
||||
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
|
||||
struct fw_rsc_vdev *rsc;
|
||||
|
||||
return rvdev->dfeatures;
|
||||
rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
|
||||
|
||||
return rsc->dfeatures;
|
||||
}
|
||||
|
||||
static void rproc_virtio_finalize_features(struct virtio_device *vdev)
|
||||
{
|
||||
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
|
||||
struct fw_rsc_vdev *rsc;
|
||||
|
||||
rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
|
||||
|
||||
/* Give virtio_ring a chance to accept features */
|
||||
vring_transport_features(vdev);
|
||||
|
@ -213,13 +229,44 @@ static void rproc_virtio_finalize_features(struct virtio_device *vdev)
|
|||
/*
|
||||
* Remember the finalized features of our vdev, and provide it
|
||||
* to the remote processor once it is powered on.
|
||||
*
|
||||
* Similarly to the status field, we don't expose yet the negotiated
|
||||
* features to the remote processors at this point. This will be
|
||||
* fixed as part of a small resource table overhaul and then an
|
||||
* extension of the virtio resource entries.
|
||||
*/
|
||||
rvdev->gfeatures = vdev->features[0];
|
||||
rsc->gfeatures = vdev->features[0];
|
||||
}
|
||||
|
||||
static void rproc_virtio_get(struct virtio_device *vdev, unsigned offset,
|
||||
void *buf, unsigned len)
|
||||
{
|
||||
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
|
||||
struct fw_rsc_vdev *rsc;
|
||||
void *cfg;
|
||||
|
||||
rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
|
||||
cfg = &rsc->vring[rsc->num_of_vrings];
|
||||
|
||||
if (offset + len > rsc->config_len || offset + len < len) {
|
||||
dev_err(&vdev->dev, "rproc_virtio_get: access out of bounds\n");
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(buf, cfg + offset, len);
|
||||
}
|
||||
|
||||
static void rproc_virtio_set(struct virtio_device *vdev, unsigned offset,
|
||||
const void *buf, unsigned len)
|
||||
{
|
||||
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
|
||||
struct fw_rsc_vdev *rsc;
|
||||
void *cfg;
|
||||
|
||||
rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
|
||||
cfg = &rsc->vring[rsc->num_of_vrings];
|
||||
|
||||
if (offset + len > rsc->config_len || offset + len < len) {
|
||||
dev_err(&vdev->dev, "rproc_virtio_set: access out of bounds\n");
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(cfg + offset, buf, len);
|
||||
}
|
||||
|
||||
static const struct virtio_config_ops rproc_virtio_config_ops = {
|
||||
|
@ -230,6 +277,8 @@ static const struct virtio_config_ops rproc_virtio_config_ops = {
|
|||
.reset = rproc_virtio_reset,
|
||||
.set_status = rproc_virtio_set_status,
|
||||
.get_status = rproc_virtio_get_status,
|
||||
.get = rproc_virtio_get,
|
||||
.set = rproc_virtio_set,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -64,26 +64,18 @@ static int sproc_load_segments(struct rproc *rproc, const struct firmware *fw)
|
|||
}
|
||||
|
||||
/* Find the entry for resource table in the Table of Content */
|
||||
static struct ste_toc_entry *sproc_find_rsc_entry(const struct firmware *fw)
|
||||
static const struct ste_toc_entry *sproc_find_rsc_entry(const void *data)
|
||||
{
|
||||
int i;
|
||||
struct ste_toc *toc;
|
||||
|
||||
if (!fw)
|
||||
return NULL;
|
||||
|
||||
toc = (void *)fw->data;
|
||||
const struct ste_toc *toc;
|
||||
toc = data;
|
||||
|
||||
/* Search the table for the resource table */
|
||||
for (i = 0; i < SPROC_MAX_TOC_ENTRIES &&
|
||||
toc->table[i].start != 0xffffffff; i++) {
|
||||
|
||||
if (!strncmp(toc->table[i].name, SPROC_RESOURCE_NAME,
|
||||
sizeof(toc->table[i].name))) {
|
||||
if (toc->table[i].start > fw->size)
|
||||
return NULL;
|
||||
sizeof(toc->table[i].name)))
|
||||
return &toc->table[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
@ -96,9 +88,12 @@ sproc_find_rsc_table(struct rproc *rproc, const struct firmware *fw,
|
|||
{
|
||||
struct sproc *sproc = rproc->priv;
|
||||
struct resource_table *table;
|
||||
struct ste_toc_entry *entry;
|
||||
const struct ste_toc_entry *entry;
|
||||
|
||||
entry = sproc_find_rsc_entry(fw);
|
||||
if (!fw)
|
||||
return NULL;
|
||||
|
||||
entry = sproc_find_rsc_entry(fw->data);
|
||||
if (!entry) {
|
||||
sproc_err(sproc, "resource table not found in fw\n");
|
||||
return NULL;
|
||||
|
@ -149,10 +144,30 @@ sproc_find_rsc_table(struct rproc *rproc, const struct firmware *fw,
|
|||
return table;
|
||||
}
|
||||
|
||||
/* Find the resource table inside the remote processor's firmware. */
|
||||
static struct resource_table *
|
||||
sproc_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw)
|
||||
{
|
||||
struct sproc *sproc = rproc->priv;
|
||||
const struct ste_toc_entry *entry;
|
||||
|
||||
if (!fw || !sproc->fw_addr)
|
||||
return NULL;
|
||||
|
||||
entry = sproc_find_rsc_entry(sproc->fw_addr);
|
||||
if (!entry) {
|
||||
sproc_err(sproc, "resource table not found in fw\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sproc->fw_addr + entry->start;
|
||||
}
|
||||
|
||||
/* STE modem firmware handler operations */
|
||||
const struct rproc_fw_ops sproc_fw_ops = {
|
||||
.load = sproc_load_segments,
|
||||
.find_rsc_table = sproc_find_rsc_table,
|
||||
.find_loaded_rsc_table = sproc_find_loaded_rsc_table,
|
||||
};
|
||||
|
||||
/* Kick the modem with specified notification id */
|
||||
|
@ -198,7 +213,7 @@ static int sproc_start(struct rproc *rproc)
|
|||
}
|
||||
|
||||
/* Subscribe to notifications */
|
||||
for (i = 0; i < rproc->max_notifyid; i++) {
|
||||
for (i = 0; i <= rproc->max_notifyid; i++) {
|
||||
err = sproc->mdev->ops.kick_subscribe(sproc->mdev, i);
|
||||
if (err) {
|
||||
sproc_err(sproc,
|
||||
|
|
|
@ -401,6 +401,9 @@ enum rproc_crash_type {
|
|||
* @crash_comp: completion used to sync crash handler and the rproc reload
|
||||
* @recovery_disabled: flag that state if recovery was disabled
|
||||
* @max_notifyid: largest allocated notify id.
|
||||
* @table_ptr: pointer to the resource table in effect
|
||||
* @cached_table: copy of the resource table
|
||||
* @table_csum: checksum of the resource table
|
||||
*/
|
||||
struct rproc {
|
||||
struct klist_node node;
|
||||
|
@ -429,9 +432,13 @@ struct rproc {
|
|||
struct completion crash_comp;
|
||||
bool recovery_disabled;
|
||||
int max_notifyid;
|
||||
struct resource_table *table_ptr;
|
||||
struct resource_table *cached_table;
|
||||
u32 table_csum;
|
||||
};
|
||||
|
||||
/* we currently support only two vrings per rvdev */
|
||||
|
||||
#define RVDEV_NUM_VRINGS 2
|
||||
|
||||
/**
|
||||
|
@ -462,16 +469,14 @@ struct rproc_vring {
|
|||
* @rproc: the rproc handle
|
||||
* @vdev: the virio device
|
||||
* @vring: the vrings for this vdev
|
||||
* @dfeatures: virtio device features
|
||||
* @gfeatures: virtio guest features
|
||||
* @rsc_offset: offset of the vdev's resource entry
|
||||
*/
|
||||
struct rproc_vdev {
|
||||
struct list_head node;
|
||||
struct rproc *rproc;
|
||||
struct virtio_device vdev;
|
||||
struct rproc_vring vring[RVDEV_NUM_VRINGS];
|
||||
unsigned long dfeatures;
|
||||
unsigned long gfeatures;
|
||||
u32 rsc_offset;
|
||||
};
|
||||
|
||||
struct rproc *rproc_alloc(struct device *dev, const char *name,
|
||||
|
|
Загрузка…
Ссылка в новой задаче