Commit a9197f90 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'remoteproc-for-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad/remoteproc

Pull remoteproc update from Ohad Ben-Cohen:
 - custom binary format support from Sjur Brændeland
 - groundwork for recovery and runtime pm support
 - some cleanups and API simplifications

Fix up conflicts in drivers/remoteproc/remoteproc_core.c due to clashes
with earlier cleanups by Sjur Brændeland (with part of the cleanups
moved into the new remoteproc_elf_loader.c file).

* tag 'remoteproc-for-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad/remoteproc:
  MAINTAINERS: add remoteproc's git
  remoteproc: Support custom firmware handlers
  remoteproc: Move Elf related functions to separate file
  remoteproc: Add function rproc_get_boot_addr
  remoteproc: Pass struct fw to load_segments and find_rsc_table.
  remoteproc: adopt the driver core's alloc/add/del/put naming
  remoteproc: remove the get_by_name/put API
  remoteproc: support non-iommu carveout assignment
  remoteproc: simplify unregister/free interfaces
  remoteproc: remove the now-redundant kref
  remoteproc: maintain a generic child device for each rproc
  remoteproc: allocate vrings on demand, free when not needed
parents e2aed8df 6bb697b6
...@@ -36,8 +36,7 @@ cost. ...@@ -36,8 +36,7 @@ cost.
Note: to use this function you should already have a valid rproc Note: to use this function you should already have a valid rproc
handle. There are several ways to achieve that cleanly (devres, pdata, handle. There are several ways to achieve that cleanly (devres, pdata,
the way remoteproc_rpmsg.c does this, or, if this becomes prevalent, we the way remoteproc_rpmsg.c does this, or, if this becomes prevalent, we
might also consider using dev_archdata for this). See also might also consider using dev_archdata for this).
rproc_get_by_name() below.
void rproc_shutdown(struct rproc *rproc) void rproc_shutdown(struct rproc *rproc)
- Power off a remote processor (previously booted with rproc_boot()). - Power off a remote processor (previously booted with rproc_boot()).
...@@ -51,30 +50,6 @@ cost. ...@@ -51,30 +50,6 @@ cost.
which means that the @rproc handle stays valid even after which means that the @rproc handle stays valid even after
rproc_shutdown() returns, and users can still use it with a subsequent rproc_shutdown() returns, and users can still use it with a subsequent
rproc_boot(), if needed. rproc_boot(), if needed.
- don't call rproc_shutdown() to unroll rproc_get_by_name(), exactly
because rproc_shutdown() _does not_ decrement the refcount of @rproc.
To decrement the refcount of @rproc, use rproc_put() (but _only_ if
you acquired @rproc using rproc_get_by_name()).
struct rproc *rproc_get_by_name(const char *name)
- Find an rproc handle using the remote processor's name, and then
boot it. If it's already powered on, then just immediately return
(successfully). Returns the rproc handle on success, and NULL on failure.
This function increments the remote processor's refcount, so always
use rproc_put() to decrement it back once rproc isn't needed anymore.
Note: currently rproc_get_by_name() and rproc_put() are not used anymore
by the rpmsg bus and its drivers. We need to scrutinize the use cases
that still need them, and see if we can migrate them to use the non
name-based boot/shutdown interface.
void rproc_put(struct rproc *rproc)
- Decrement @rproc's power refcount and shut it down if it reaches zero
(essentially by just calling rproc_shutdown), and then decrement @rproc's
validity refcount too.
After this function returns, @rproc may _not_ be used anymore, and its
handle should be considered invalid.
This function should be called _iff_ the @rproc handle was grabbed by
calling rproc_get_by_name().
3. Typical usage 3. Typical usage
...@@ -115,21 +90,21 @@ int dummy_rproc_example(struct rproc *my_rproc) ...@@ -115,21 +90,21 @@ int dummy_rproc_example(struct rproc *my_rproc)
This function should be used by rproc implementations during This function should be used by rproc implementations during
initialization of the remote processor. initialization of the remote processor.
After creating an rproc handle using this function, and when ready, After creating an rproc handle using this function, and when ready,
implementations should then call rproc_register() to complete implementations should then call rproc_add() to complete
the registration of the remote processor. the registration of the remote processor.
On success, the new rproc is returned, and on failure, NULL. On success, the new rproc is returned, and on failure, NULL.
Note: _never_ directly deallocate @rproc, even if it was not registered Note: _never_ directly deallocate @rproc, even if it was not registered
yet. Instead, if you just need to unroll rproc_alloc(), use rproc_free(). yet. Instead, when you need to unroll rproc_alloc(), use rproc_put().
void rproc_free(struct rproc *rproc) void rproc_put(struct rproc *rproc)
- Free an rproc handle that was allocated by rproc_alloc. - Free an rproc handle that was allocated by rproc_alloc.
This function should _only_ be used if @rproc was only allocated, This function essentially unrolls rproc_alloc(), by decrementing the
but not registered yet. rproc's refcount. It doesn't directly free rproc; that would happen
If @rproc was already successfully registered (by calling only if there are no other references to rproc and its refcount now
rproc_register()), then use rproc_unregister() instead. dropped to zero.
int rproc_register(struct rproc *rproc) int rproc_add(struct rproc *rproc)
- Register @rproc with the remoteproc framework, after it has been - Register @rproc with the remoteproc framework, after it has been
allocated with rproc_alloc(). allocated with rproc_alloc().
This is called by the platform-specific rproc implementation, whenever This is called by the platform-specific rproc implementation, whenever
...@@ -142,20 +117,15 @@ int dummy_rproc_example(struct rproc *my_rproc) ...@@ -142,20 +117,15 @@ int dummy_rproc_example(struct rproc *my_rproc)
of registering this remote processor, additional virtio drivers might get of registering this remote processor, additional virtio drivers might get
probed. probed.
int rproc_unregister(struct rproc *rproc) int rproc_del(struct rproc *rproc)
- Unregister a remote processor, and decrement its refcount. - Unroll rproc_add().
If its refcount drops to zero, then @rproc will be freed. If not,
it will be freed later once the last reference is dropped.
This function should be called when the platform specific rproc This function should be called when the platform specific rproc
implementation decides to remove the rproc device. it should implementation decides to remove the rproc device. it should
_only_ be called if a previous invocation of rproc_register() _only_ be called if a previous invocation of rproc_add()
has completed successfully. has completed successfully.
After rproc_unregister() returns, @rproc is _not_ valid anymore and After rproc_del() returns, @rproc is still valid, and its
it shouldn't be used. More specifically, don't call rproc_free() last refcount should be decremented by calling rproc_put().
or try to directly free @rproc after rproc_unregister() returns;
none of these are needed, and calling them is a bug.
Returns 0 on success and -EINVAL if @rproc isn't valid. Returns 0 on success and -EINVAL if @rproc isn't valid.
......
...@@ -5725,6 +5725,7 @@ F: include/linux/regmap.h ...@@ -5725,6 +5725,7 @@ F: include/linux/regmap.h
REMOTE PROCESSOR (REMOTEPROC) SUBSYSTEM REMOTE PROCESSOR (REMOTEPROC) SUBSYSTEM
M: Ohad Ben-Cohen <ohad@wizery.com> M: Ohad Ben-Cohen <ohad@wizery.com>
T: git git://git.kernel.org/pub/scm/linux/kernel/git/ohad/remoteproc.git
S: Maintained S: Maintained
F: drivers/remoteproc/ F: drivers/remoteproc/
F: Documentation/remoteproc.txt F: Documentation/remoteproc.txt
......
...@@ -6,4 +6,5 @@ obj-$(CONFIG_REMOTEPROC) += remoteproc.o ...@@ -6,4 +6,5 @@ obj-$(CONFIG_REMOTEPROC) += remoteproc.o
remoteproc-y := remoteproc_core.o remoteproc-y := remoteproc_core.o
remoteproc-y += remoteproc_debugfs.o remoteproc-y += remoteproc_debugfs.o
remoteproc-y += remoteproc_virtio.o remoteproc-y += remoteproc_virtio.o
remoteproc-y += remoteproc_elf_loader.o
obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o
...@@ -66,7 +66,7 @@ static int omap_rproc_mbox_callback(struct notifier_block *this, ...@@ -66,7 +66,7 @@ static int omap_rproc_mbox_callback(struct notifier_block *this,
{ {
mbox_msg_t msg = (mbox_msg_t) data; mbox_msg_t msg = (mbox_msg_t) data;
struct omap_rproc *oproc = container_of(this, struct omap_rproc, nb); struct omap_rproc *oproc = container_of(this, struct omap_rproc, nb);
struct device *dev = oproc->rproc->dev; struct device *dev = oproc->rproc->dev.parent;
const char *name = oproc->rproc->name; const char *name = oproc->rproc->name;
dev_dbg(dev, "mbox msg: 0x%x\n", msg); dev_dbg(dev, "mbox msg: 0x%x\n", msg);
...@@ -92,12 +92,13 @@ static int omap_rproc_mbox_callback(struct notifier_block *this, ...@@ -92,12 +92,13 @@ static int omap_rproc_mbox_callback(struct notifier_block *this,
static void omap_rproc_kick(struct rproc *rproc, int vqid) static void omap_rproc_kick(struct rproc *rproc, int vqid)
{ {
struct omap_rproc *oproc = rproc->priv; struct omap_rproc *oproc = rproc->priv;
struct device *dev = rproc->dev.parent;
int ret; int ret;
/* send the index of the triggered virtqueue in the mailbox payload */ /* send the index of the triggered virtqueue in the mailbox payload */
ret = omap_mbox_msg_send(oproc->mbox, vqid); ret = omap_mbox_msg_send(oproc->mbox, vqid);
if (ret) if (ret)
dev_err(rproc->dev, "omap_mbox_msg_send failed: %d\n", ret); dev_err(dev, "omap_mbox_msg_send failed: %d\n", ret);
} }
/* /*
...@@ -110,7 +111,8 @@ static void omap_rproc_kick(struct rproc *rproc, int vqid) ...@@ -110,7 +111,8 @@ static void omap_rproc_kick(struct rproc *rproc, int vqid)
static int omap_rproc_start(struct rproc *rproc) static int omap_rproc_start(struct rproc *rproc)
{ {
struct omap_rproc *oproc = rproc->priv; struct omap_rproc *oproc = rproc->priv;
struct platform_device *pdev = to_platform_device(rproc->dev); struct device *dev = rproc->dev.parent;
struct platform_device *pdev = to_platform_device(dev);
struct omap_rproc_pdata *pdata = pdev->dev.platform_data; struct omap_rproc_pdata *pdata = pdev->dev.platform_data;
int ret; int ret;
...@@ -120,7 +122,7 @@ static int omap_rproc_start(struct rproc *rproc) ...@@ -120,7 +122,7 @@ static int omap_rproc_start(struct rproc *rproc)
oproc->mbox = omap_mbox_get(pdata->mbox_name, &oproc->nb); oproc->mbox = omap_mbox_get(pdata->mbox_name, &oproc->nb);
if (IS_ERR(oproc->mbox)) { if (IS_ERR(oproc->mbox)) {
ret = PTR_ERR(oproc->mbox); ret = PTR_ERR(oproc->mbox);
dev_err(rproc->dev, "omap_mbox_get failed: %d\n", ret); dev_err(dev, "omap_mbox_get failed: %d\n", ret);
return ret; return ret;
} }
...@@ -133,13 +135,13 @@ static int omap_rproc_start(struct rproc *rproc) ...@@ -133,13 +135,13 @@ static int omap_rproc_start(struct rproc *rproc)
*/ */
ret = omap_mbox_msg_send(oproc->mbox, RP_MBOX_ECHO_REQUEST); ret = omap_mbox_msg_send(oproc->mbox, RP_MBOX_ECHO_REQUEST);
if (ret) { if (ret) {
dev_err(rproc->dev, "omap_mbox_get failed: %d\n", ret); dev_err(dev, "omap_mbox_get failed: %d\n", ret);
goto put_mbox; goto put_mbox;
} }
ret = pdata->device_enable(pdev); ret = pdata->device_enable(pdev);
if (ret) { if (ret) {
dev_err(rproc->dev, "omap_device_enable failed: %d\n", ret); dev_err(dev, "omap_device_enable failed: %d\n", ret);
goto put_mbox; goto put_mbox;
} }
...@@ -153,7 +155,8 @@ static int omap_rproc_start(struct rproc *rproc) ...@@ -153,7 +155,8 @@ static int omap_rproc_start(struct rproc *rproc)
/* power off the remote processor */ /* power off the remote processor */
static int omap_rproc_stop(struct rproc *rproc) static int omap_rproc_stop(struct rproc *rproc)
{ {
struct platform_device *pdev = to_platform_device(rproc->dev); struct device *dev = rproc->dev.parent;
struct platform_device *pdev = to_platform_device(dev);
struct omap_rproc_pdata *pdata = pdev->dev.platform_data; struct omap_rproc_pdata *pdata = pdev->dev.platform_data;
struct omap_rproc *oproc = rproc->priv; struct omap_rproc *oproc = rproc->priv;
int ret; int ret;
...@@ -196,14 +199,14 @@ static int __devinit omap_rproc_probe(struct platform_device *pdev) ...@@ -196,14 +199,14 @@ static int __devinit omap_rproc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, rproc); platform_set_drvdata(pdev, rproc);
ret = rproc_register(rproc); ret = rproc_add(rproc);
if (ret) if (ret)
goto free_rproc; goto free_rproc;
return 0; return 0;
free_rproc: free_rproc:
rproc_free(rproc); rproc_put(rproc);
return ret; return ret;
} }
...@@ -211,7 +214,10 @@ static int __devexit omap_rproc_remove(struct platform_device *pdev) ...@@ -211,7 +214,10 @@ static int __devexit omap_rproc_remove(struct platform_device *pdev)
{ {
struct rproc *rproc = platform_get_drvdata(pdev); struct rproc *rproc = platform_get_drvdata(pdev);
return rproc_unregister(rproc); rproc_del(rproc);
rproc_put(rproc);
return 0;
} }
static struct platform_driver omap_rproc_driver = { static struct platform_driver omap_rproc_driver = {
......
This diff is collapsed.
...@@ -124,7 +124,7 @@ struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc, ...@@ -124,7 +124,7 @@ struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc,
tfile = debugfs_create_file(name, 0400, rproc->dbg_dir, tfile = debugfs_create_file(name, 0400, rproc->dbg_dir,
trace, &trace_rproc_ops); trace, &trace_rproc_ops);
if (!tfile) { if (!tfile) {
dev_err(rproc->dev, "failed to create debugfs trace entry\n"); dev_err(&rproc->dev, "failed to create debugfs trace entry\n");
return NULL; return NULL;
} }
...@@ -141,7 +141,7 @@ void rproc_delete_debug_dir(struct rproc *rproc) ...@@ -141,7 +141,7 @@ void rproc_delete_debug_dir(struct rproc *rproc)
void rproc_create_debug_dir(struct rproc *rproc) void rproc_create_debug_dir(struct rproc *rproc)
{ {
struct device *dev = rproc->dev; struct device *dev = &rproc->dev;
if (!rproc_dbg) if (!rproc_dbg)
return; return;
......
/*
* Remote Processor Framework Elf loader
*
* Copyright (C) 2011 Texas Instruments, Inc.
* Copyright (C) 2011 Google, Inc.
*
* Ohad Ben-Cohen <ohad@wizery.com>
* Brian Swetland <swetland@google.com>
* Mark Grosen <mgrosen@ti.com>
* Fernando Guzman Lugo <fernando.lugo@ti.com>
* Suman Anna <s-anna@ti.com>
* Robert Tivy <rtivy@ti.com>
* Armando Uribe De Leon <x0095078@ti.com>
* Sjur Brændeland <sjur.brandeland@stericsson.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.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/remoteproc.h>
#include <linux/elf.h>
#include "remoteproc_internal.h"
/**
* rproc_elf_sanity_check() - Sanity Check ELF firmware image
* @rproc: the remote processor handle
* @fw: the ELF firmware image
*
* Make sure this fw image is sane.
*/
static int
rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw)
{
const char *name = rproc->firmware;
struct device *dev = &rproc->dev;
struct elf32_hdr *ehdr;
char class;
if (!fw) {
dev_err(dev, "failed to load %s\n", name);
return -EINVAL;
}
if (fw->size < sizeof(struct elf32_hdr)) {
dev_err(dev, "Image is too small\n");
return -EINVAL;
}
ehdr = (struct elf32_hdr *)fw->data;
/* We only support ELF32 at this point */
class = ehdr->e_ident[EI_CLASS];
if (class != ELFCLASS32) {
dev_err(dev, "Unsupported class: %d\n", class);
return -EINVAL;
}
/* We assume the firmware has the same endianess as the host */
# ifdef __LITTLE_ENDIAN
if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) {
# else /* BIG ENDIAN */
if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
# endif
dev_err(dev, "Unsupported firmware endianess\n");
return -EINVAL;
}
if (fw->size < ehdr->e_shoff + sizeof(struct elf32_shdr)) {
dev_err(dev, "Image is too small\n");
return -EINVAL;
}
if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
dev_err(dev, "Image is corrupted (bad magic)\n");
return -EINVAL;
}
if (ehdr->e_phnum == 0) {
dev_err(dev, "No loadable segments\n");
return -EINVAL;
}
if (ehdr->e_phoff > fw->size) {
dev_err(dev, "Firmware size is too small\n");
return -EINVAL;
}
return 0;
}
/**
* rproc_elf_get_boot_addr() - Get rproc's boot address.
* @rproc: the remote processor handle
* @fw: the ELF firmware image
*
* This function returns the entry point address of the ELF
* image.
*
* Note that the boot address is not a configurable property of all remote
* processors. Some will always boot at a specific hard-coded address.
*/
static
u32 rproc_elf_get_boot_addr(struct rproc *rproc, const struct firmware *fw)
{
struct elf32_hdr *ehdr = (struct elf32_hdr *)fw->data;
return ehdr->e_entry;
}
/**
* rproc_elf_load_segments() - load firmware segments to memory
* @rproc: remote processor which will be booted using these fw segments
* @fw: the ELF firmware image
*
* This function loads the firmware segments to memory, where the remote
* processor expects them.
*
* Some remote processors will expect their code and data to be placed
* in specific device addresses, and can't have them dynamically assigned.
*
* We currently support only those kind of remote processors, and expect
* the program header's paddr member to contain those addresses. We then go
* through the physically contiguous "carveout" memory regions which we
* allocated (and mapped) earlier on behalf of the remote processor,
* and "translate" device address to kernel addresses, so we can copy the
* segments where they are expected.
*
* Currently we only support remote processors that required carveout
* allocations and got them mapped onto their iommus. Some processors
* might be different: they might not have iommus, and would prefer to
* directly allocate memory for every segment/resource. This is not yet
* supported, though.
*/
static int
rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
{
struct device *dev = &rproc->dev;
struct elf32_hdr *ehdr;
struct elf32_phdr *phdr;
int i, ret = 0;
const u8 *elf_data = fw->data;
ehdr = (struct elf32_hdr *)elf_data;
phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff);
/* go through the available ELF segments */
for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
u32 da = phdr->p_paddr;
u32 memsz = phdr->p_memsz;
u32 filesz = phdr->p_filesz;
u32 offset = phdr->p_offset;
void *ptr;
if (phdr->p_type != PT_LOAD)
continue;
dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n",
phdr->p_type, da, memsz, filesz);
if (filesz > memsz) {
dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n",
filesz, memsz);
ret = -EINVAL;
break;
}
if (offset + filesz > fw->size) {
dev_err(dev, "truncated fw: need 0x%x avail 0x%zx\n",
offset + filesz, fw->size);
ret = -EINVAL;
break;
}
/* grab the kernel address for this device address */
ptr = rproc_da_to_va(rproc, da, memsz);
if (!ptr) {
dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz);
ret = -EINVAL;
break;
}
/* put the segment where the remote processor expects it */
if (phdr->p_filesz)
memcpy(ptr, elf_data + phdr->p_offset, filesz);
/*
* Zero out remaining memory for this segment.
*
* This isn't strictly required since dma_alloc_coherent already
* did this for us. albeit harmless, we may consider removing
* this.
*/
if (memsz > filesz)
memset(ptr + filesz, 0, memsz - filesz);
}
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)
{
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;
ehdr = (struct elf32_hdr *)elf_data;
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;
if (strcmp(name_table + shdr->sh_name, ".resource_table"))
continue;
table = (struct resource_table *)(elf_data + offset);
/* make sure we have the entire table */
if (offset + size > fw->size) {
dev_err(dev, "resource table truncated\n");
return NULL;
}
/* make sure table has at least the header */
if (sizeof(struct resource_table) > size) {
dev_err(dev, "header-less resource table\n");
return NULL;
}
/* we don't support any version beyond the first */
if (table->ver != 1) {
dev_err(dev, "unsupported fw ver: %d\n", table->ver);
return NULL;
}
/* make sure reserved bytes are zeroes */
if (table->reserved[0] || table->reserved[1]) {
dev_err(dev, "non zero reserved bytes\n");
return NULL;
}
/* make sure the offsets array isn't truncated */
if (table->num * sizeof(table->offset[0]) +
sizeof(struct resource_table) > size) {
dev_err(dev, "resource table incomplete\n");
return NULL;
}
*tablesz = shdr->sh_size;
break;
}
return table;
}
const struct rproc_fw_ops rproc_elf_fw_ops = {
.load = rproc_elf_load_segments,
.find_rsc_table = rproc_elf_find_rsc_table,
.sanity_check = rproc_elf_sanity_check,
.get_boot_addr = rproc_elf_get_boot_addr
};
...@@ -21,9 +21,27 @@ ...@@ -21,9 +21,27 @@
#define REMOTEPROC_INTERNAL_H #define REMOTEPROC_INTERNAL_H
#include <linux/irqreturn.h> #include <linux/irqreturn.h>
#include <linux/firmware.h>
struct rproc; struct rproc;
/**
* struct rproc_fw_ops - firmware format specific operations.
* @find_rsc_table: finds the resource table inside the firmware image
* @load: load firmeware to memory, where the remote processor
* expects to find it
* @sanity_check: sanity check the fw image
* @get_boot_addr: get boot address to entry point specified in firmware
*/
struct rproc_fw_ops {
struct resource_table *(*find_rsc_table) (struct rproc *rproc,
const struct firmware *fw,
int *tablesz);
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);
};
/* from remoteproc_core.c */ /* from remoteproc_core.c */
void rproc_release(struct kref *kref); void rproc_release(struct kref *kref);
irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id); irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id);
...@@ -41,4 +59,48 @@ void rproc_create_debug_dir(struct rproc *rproc); ...@@ -41,4 +59,48 @@ void rproc_create_debug_dir(struct rproc *rproc);
void rproc_init_debugfs(void); void rproc_init_debugfs(void);
void rproc_exit_debugfs(void); void rproc_exit_debugfs(void);
void rproc_free_vring(struct rproc_vring *rvring);
int rproc_alloc_vring(struct rproc_vdev *rvdev, int i);
void *rproc_da_to_va(struct rproc *rproc, u64 da, int len);
static inline
int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw)
{
if (rproc->fw_ops->sanity_check)
return rproc->fw_ops->sanity_check(rproc, fw);
return 0;
}
static inline
u32 rproc_get_boot_addr(struct rproc *rproc, const struct firmware *fw)
{
if (rproc->fw_ops->get_boot_addr)
return rproc->fw_ops->get_boot_addr(rproc, fw);
return 0;
}
static inline
int rproc_load_segments(struct rproc *rproc, const struct firmware *fw)
{
if (rproc->fw_ops->load)
return rproc->fw_ops->load(rproc, fw);
return -EINVAL;
}
static inline
struct resource_table *rproc_find_rsc_table(struct rproc *rproc,
const struct firmware *fw, int *tablesz)
{
if (rproc->fw_ops->find_rsc_table)
return rproc->fw_ops->find_rsc_table(rproc, fw, tablesz);
return NULL;
}
extern const struct rproc_fw_ops rproc_elf_fw_ops;
#endif /* REMOTEPROC_INTERNAL_H */ #endif /* REMOTEPROC_INTERNAL_H */
...@@ -36,7 +36,7 @@ static void rproc_virtio_notify(struct virtqueue *vq) ...@@ -36,7 +36,7 @@ static void rproc_virtio_notify(struct virtqueue *vq)
struct rproc *rproc = rvring->rvdev->rproc; struct rproc *rproc = rvring->rvdev->rproc;
int notifyid = rvring->notifyid; int notifyid = rvring->notifyid;
dev_dbg(rproc->dev, "kicking vq index: %d\n", notifyid); dev_dbg(&rproc->dev, "kicking vq index: %d\n", notifyid);
rproc->ops->kick(rproc, notifyid); rproc->ops->kick(rproc, notifyid);
} }
...@@ -57,7 +57,7 @@ irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int notifyid) ...@@ -57,7 +57,7 @@ irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int notifyid)
{ {
struct rproc_vring *rvring; struct rproc_vring *rvring;
dev_dbg(rproc->dev, "vq index %d is interrupted\n", notifyid); dev_dbg(&rproc->dev, "vq index %d is interrupted\n", notifyid);
rvring = idr_find(&rproc->notifyids, notifyid); rvring = idr_find(&rproc->notifyids, notifyid);
if (!rvring || !rvring->vq) if (!rvring || !rvring->vq)
...@@ -74,17 +74,21 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, ...@@ -74,17 +74,21 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
{ {
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
struct rproc *rproc = vdev_to_rproc(vdev); struct rproc *rproc = vdev_to_rproc(vdev);
struct device *dev = &rproc->dev;
struct rproc_vring *rvring; struct rproc_vring *rvring;
struct virtqueue *vq; struct virtqueue *vq;
void *addr; void *addr;
int len, size; int len, size, ret;
/* we're temporarily limited to two virtqueues per rvdev */ /* we're temporarily limited to two virtqueues per rvdev */
if (id >= ARRAY_SIZE(rvdev->vring)) if (id >= ARRAY_SIZE(rvdev->vring))
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
rvring = &rvdev->vring[id]; ret = rproc_alloc_vring(rvdev, id);
if (ret)
return ERR_PTR(ret);
rvring = &rvdev->vring[id];
addr = rvring->va; addr = rvring->va;
len = rvring->len; len = rvring->len;
...@@ -92,7 +96,7 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, ...@@ -92,7 +96,7 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
size = vring_size(len, rvring->align); size = vring_size(len, rvring->align);
memset(addr, 0, size); memset(addr, 0, size);
dev_dbg(rproc->dev, "vring%d: va %p qsz %d notifyid %d\n", dev_dbg(dev, "vring%d: va %p qsz %d notifyid %d\n",
id, addr, len, rvring->notifyid); id, addr, len, rvring->notifyid);
/* /*
...@@ -102,7 +106,8 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, ...@@ -102,7 +106,8 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
vq = vring_new_virtqueue(len, rvring->align, vdev, false, addr, vq = vring_new_virtqueue(len, rvring->align, vdev, false, addr,
rproc_virtio_notify, callback, name); rproc_virtio_notify, callback, name);
if (!vq) { if (!vq) {
dev_err(rproc->dev, "vring_new_virtqueue %s failed\n", name); dev_err(dev, "vring_new_virtqueue %s failed\n", name);
rproc_free_vring(rvring);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
...@@ -125,6 +130,7 @@ static void rproc_virtio_del_vqs(struct virtio_device *vdev) ...@@ -125,6 +130,7 @@ static void rproc_virtio_del_vqs(struct virtio_device *vdev)
rvring = vq->priv; rvring = vq->priv;
rvring->vq = NULL; rvring->vq = NULL;
vring_del_virtqueue(vq); vring_del_virtqueue(vq);
rproc_free_vring(rvring);
} }
} }
...@@ -147,7 +153,7 @@ static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs, ...@@ -147,7 +153,7 @@ static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs,
/* now that the vqs are all set, boot the remote processor */ /* now that the vqs are all set, boot the remote processor */
ret = rproc_boot(rproc); ret = rproc_boot(rproc);
if (ret) { if (ret) {
dev_err(rproc->dev, "rproc_boot() failed %d\n", ret); dev_err(&rproc->dev, "rproc_boot() failed %d\n", ret);
goto error; goto error;
} }
...@@ -219,7 +225,7 @@ static struct virtio_config_ops rproc_virtio_config_ops = { ...@@ -219,7 +225,7 @@ static struct virtio_config_ops rproc_virtio_config_ops = {
/* /*
* This function is called whenever vdev is released, and is responsible * This function is called whenever vdev is released, and is responsible
* to decrement the remote processor's refcount taken when vdev was * to decrement the remote processor's refcount which was taken when vdev was
* added. * added.
* *
* Never call this function directly; it will be called by the driver * Never call this function directly; it will be called by the driver
...@@ -228,9 +234,13 @@ static struct virtio_config_ops rproc_virtio_config_ops = { ...@@ -228,9 +234,13 @@ static struct virtio_config_ops rproc_virtio_config_ops = {
static void rproc_vdev_release(struct device *dev) static void rproc_vdev_release(struct device *dev)
{ {
struct virtio_device *vdev = dev_to_virtio(dev); struct virtio_device *vdev = dev_to_virtio(dev);
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
struct rproc *rproc = vdev_to_rproc(vdev); struct rproc *rproc = vdev_to_rproc(vdev);
kref_put(&rproc->refcount, rproc_release); list_del(&rvdev->node);
kfree(rvdev);
put_device(&rproc->dev);
} }
/** /**
...@@ -245,7 +255,7 @@ static void rproc_vdev_release(struct device *dev) ...@@ -245,7 +255,7 @@ static void rproc_vdev_release(struct device *dev)
int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id) int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
{ {
struct rproc *rproc = rvdev->rproc; struct rproc *rproc = rvdev->rproc;
struct device *dev = rproc->dev; struct device *dev = &rproc->dev;
struct virtio_device *vdev = &rvdev->vdev; struct virtio_device *vdev = &rvdev->vdev;
int ret; int ret;
...@@ -262,11 +272,11 @@ int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id) ...@@ -262,11 +272,11 @@ int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
* Therefore we must increment the rproc refcount here, and decrement * Therefore we must increment the rproc refcount here, and decrement
* it _only_ when the vdev is released. * it _only_ when the vdev is released.
*/ */
kref_get(&rproc->refcount); get_device(&rproc->dev);
ret = register_virtio_device(vdev); ret = register_virtio_device(vdev);
if (ret) { if (ret) {
kref_put(&rproc->refcount, rproc_release); put_device(&rproc->dev);
dev_err(dev, "failed to register vdev: %d\n", ret); dev_err(dev, "failed to register vdev: %d\n", ret);
goto out; goto out;
} }
......
...@@ -956,7 +956,8 @@ static int rpmsg_probe(struct virtio_device *vdev) ...@@ -956,7 +956,8 @@ static int rpmsg_probe(struct virtio_device *vdev)
vrp->svq = vqs[1]; vrp->svq = vqs[1];
/* allocate coherent memory for the buffers */ /* allocate coherent memory for the buffers */
bufs_va = dma_alloc_coherent(vdev->dev.parent, RPMSG_TOTAL_BUF_SPACE, bufs_va = dma_alloc_coherent(vdev->dev.parent->parent,
RPMSG_TOTAL_BUF_SPACE,
&vrp->bufs_dma, GFP_KERNEL); &vrp->bufs_dma, GFP_KERNEL);
if (!bufs_va) if (!bufs_va)
goto vqs_del; goto vqs_del;
......
...@@ -36,7 +36,6 @@ ...@@ -36,7 +36,6 @@
#define REMOTEPROC_H #define REMOTEPROC_H
#include <linux/types.h> #include <linux/types.h>
#include <linux/kref.h>
#include <linux/klist.h> #include <linux/klist.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/virtio.h> #include <linux/virtio.h>
...@@ -369,8 +368,8 @@ enum rproc_state { ...@@ -369,8 +368,8 @@ enum rproc_state {
* @firmware: name of firmware file to be loaded * @firmware: name of firmware file to be loaded
* @priv: private data which belongs to the platform-specific rproc module * @priv: private data which belongs to the platform-specific rproc module
* @ops: platform-specific start/stop rproc handlers * @ops: platform-specific start/stop rproc handlers
* @dev: underlying device * @dev: virtual device for refcounting and common remoteproc behavior
* @refcount: refcount of users that have a valid pointer to this rproc * @fw_ops: firmware-specific handlers
* @power: refcount of users who need this rproc powered up * @power: refcount of users who need this rproc powered up
* @state: state of the device * @state: state of the device
* @lock: lock which protects concurrent manipulations of the rproc * @lock: lock which protects concurrent manipulations of the rproc
...@@ -383,6 +382,7 @@ enum rproc_state { ...@@ -383,6 +382,7 @@ enum rproc_state {
* @bootaddr: address of first instruction to boot rproc with (optional) * @bootaddr: address of first instruction to boot rproc with (optional)
* @rvdevs: list of remote virtio devices * @rvdevs: list of remote virtio devices
* @notifyids: idr for dynamically assigning rproc-wide unique notify ids * @notifyids: idr for dynamically assigning rproc-wide unique notify ids
* @index: index of this rproc device
*/ */
struct rproc { struct rproc {
struct klist_node node; struct klist_node node;
...@@ -391,8 +391,8 @@ struct rproc { ...@@ -391,8 +391,8 @@ struct rproc {
const char *firmware; const char *firmware;
void *priv; void *priv;
const struct rproc_ops *ops; const struct rproc_ops *ops;
struct device *dev; struct device dev;
struct kref refcount; const struct rproc_fw_ops *fw_ops;
atomic_t power; atomic_t power;
unsigned int state; unsigned int state;
struct mutex lock; struct mutex lock;
...@@ -405,6 +405,7 @@ struct rproc { ...@@ -405,6 +405,7 @@ struct rproc {
u32 bootaddr; u32 bootaddr;
struct list_head rvdevs; struct list_head rvdevs;
struct idr notifyids; struct idr notifyids;
int index;
}; };
/* we currently support only two vrings per rvdev */ /* we currently support only two vrings per rvdev */
...@@ -450,15 +451,12 @@ struct rproc_vdev { ...@@ -450,15 +451,12 @@ struct rproc_vdev {
unsigned long gfeatures; unsigned long gfeatures;
}; };
struct rproc *rproc_get_by_name(const char *name);
void rproc_put(struct rproc *rproc);
struct rproc *rproc_alloc(struct device *dev, const char *name, struct rproc *rproc_alloc(struct device *dev, const char *name,
const struct rproc_ops *ops, const struct rproc_ops *ops,
const char *firmware, int len); const char *firmware, int len);
void rproc_free(struct rproc *rproc); void rproc_put(struct rproc *rproc);
int rproc_register(struct rproc *rproc); int rproc_add(struct rproc *rproc);
int rproc_unregister(struct rproc *rproc); int rproc_del(struct rproc *rproc);
int rproc_boot(struct rproc *rproc); int rproc_boot(struct rproc *rproc);
void rproc_shutdown(struct rproc *rproc); void rproc_shutdown(struct rproc *rproc);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment