Commit 7a186941 authored by Ohad Ben-Cohen's avatar Ohad Ben-Cohen

remoteproc: remove the single rpmsg vdev limitation

Now that the resource table supports publishing a virtio device
in a single resource entry, firmware images can start supporting
more than a single vdev.

This patch removes the single vdev limitation of the remoteproc
framework so multi-vdev firmwares can be leveraged: VDEV resource
entries are parsed when the rproc is registered, and as a result
their vrings are set up and the virtio devices are registered
(and they go away when the rproc goes away).

Moreover, we no longer only support VIRTIO_ID_RPMSG vdevs; any
virtio device type goes now. As a result, there's no more any
rpmsg-specific APIs or code in remoteproc: it all becomes generic
virtio handling.
Signed-off-by: default avatarOhad Ben-Cohen <>
Cc: Brian Swetland <>
Cc: Iliyan Malchev <>
Cc: Arnd Bergmann <>
Cc: Grant Likely <>
Cc: Rusty Russell <>
Cc: Mark Grosen <>
Cc: John Williams <>
Cc: Michal Simek <>
Cc: Loic PALLARDY <>
Cc: Ludovic BARRE <>
Cc: Omar Ramirez Luna <>
Cc: Guzman Lugo Fernando <>
Cc: Anna Suman <>
Cc: Clark Rob <>
Cc: Stephen Boyd <>
Cc: Saravana Kannan <>
Cc: David Brown <>
Cc: Kieran Bingham <>
Cc: Tony Lindgren <>
parent 41a6ee09
......@@ -20,6 +20,11 @@ platform-specific remoteproc drivers only need to provide a few low-level
handlers, and then all rpmsg drivers will then just work
(for more information about the virtio-based rpmsg bus and its drivers,
please read Documentation/rpmsg.txt).
Registration of other types of virtio devices is now also possible. Firmwares
just need to publish what kind of virtio devices do they support, and then
remoteproc will add those devices. This makes it possible to reuse the
existing virtio drivers with remote processor backends at a minimal development
2. User API
......@@ -136,8 +141,6 @@ int dummy_rproc_example(struct rproc *my_rproc)
If found, those virtio devices will be created and added, so as a result
of registering this remote processor, additional virtio drivers might get
Currently, though, we only support a single RPMSG virtio vdev per remote
int rproc_unregister(struct rproc *rproc)
- Unregister a remote processor, and decrement its refcount.
......@@ -174,7 +177,7 @@ struct rproc_ops {
Every remoteproc implementation should at least provide the ->start and ->stop
handlers. If rpmsg functionality is also desired, then the ->kick handler
handlers. If rpmsg/virtio functionality is also desired, then the ->kick handler
should be provided as well.
The ->start() handler takes an rproc handle and should then power on the
This diff is collapsed.
......@@ -28,9 +28,9 @@ struct rproc;
void rproc_release(struct kref *kref);
irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id);
/* from remoteproc_rpmsg.c */
int rproc_add_rpmsg_vdev(struct rproc *);
void rproc_remove_rpmsg_vdev(struct rproc *rproc);
/* from remoteproc_virtio.c */
int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id);
void rproc_remove_virtio_dev(struct rproc_vdev *rvdev);
/* from remoteproc_debugfs.c */
void rproc_remove_trace_file(struct dentry *tfile);
......@@ -19,7 +19,6 @@
#include <linux/export.h>
#include <linux/remoteproc.h>
#include <linux/rpmsg.h>
#include <linux/virtio.h>
#include <linux/virtio_config.h>
#include <linux/virtio_ids.h>
......@@ -30,45 +29,41 @@
#include "remoteproc_internal.h"
* struct rproc_virtio_vq_info - virtqueue state
* @vq_id: a unique index of this virtqueue (unique for this @rproc)
* @rproc: handle to the remote processor
* Such a struct will be maintained for every virtqueue we're
* using to communicate with the remote processor
struct rproc_virtio_vq_info {
__u16 vq_id;
struct rproc *rproc;
/* kick the remote processor, and let it know which virtqueue to poke at */
static void rproc_virtio_notify(struct virtqueue *vq)
struct rproc_virtio_vq_info *rpvq = vq->priv;
struct rproc *rproc = rpvq->rproc;
struct rproc_vring *rvring = vq->priv;
struct rproc *rproc = rvring->rvdev->rproc;
int notifyid = rvring->notifyid;
dev_dbg(rproc->dev, "kicking vq id: %d\n", rpvq->vq_id);
dev_dbg(rproc->dev, "kicking vq index: %d\n", notifyid);
rproc->ops->kick(rproc, rpvq->vq_id);
rproc->ops->kick(rproc, notifyid);
* rproc_vq_interrupt() - tell remoteproc that a virtqueue is interrupted
* @rproc: handle to the remote processor
* @vq_id: index of the signalled virtqueue
* @notifyid: index of the signalled virtqueue (unique per this @rproc)
* This function should be called by the platform-specific rproc driver,
* when the remote processor signals that a specific virtqueue has pending
* messages available.
* Returns IRQ_NONE if no message was found in the @vq_id virtqueue,
* Returns IRQ_NONE if no message was found in the @notifyid virtqueue,
* and otherwise returns IRQ_HANDLED.
irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id)
irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int notifyid)
return vring_interrupt(0, rproc->rvdev->vq[vq_id]);
struct rproc_vring *rvring;
dev_dbg(rproc->dev, "vq index %d is interrupted\n", notifyid);
rvring = idr_find(&rproc->notifyids, notifyid);
if (!rvring || !rvring->vq)
return IRQ_NONE;
return vring_interrupt(0, rvring->vq);
......@@ -77,24 +72,28 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
void (*callback)(struct virtqueue *vq),
const char *name)
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
struct rproc *rproc = vdev_to_rproc(vdev);
struct rproc_vdev *rvdev = rproc->rvdev;
struct rproc_virtio_vq_info *rpvq;
struct rproc_vring *rvring;
struct virtqueue *vq;
void *addr;
int ret, len;
int len, size;
rpvq = kmalloc(sizeof(*rpvq), GFP_KERNEL);
if (!rpvq)
return ERR_PTR(-ENOMEM);
/* we're temporarily limited to two virtqueues per rvdev */
if (id >= ARRAY_SIZE(rvdev->vring))
return ERR_PTR(-EINVAL);
rvring = &rvdev->vring[id];
rpvq->rproc = rproc;
rpvq->vq_id = id;
addr = rvring->va;
len = rvring->len;
addr = rvdev->vring[id].va;
len = rvdev->vring[id].len;
/* zero vring */
size = vring_size(len, rvring->align);
memset(addr, 0, size);
dev_dbg(rproc->dev, "vring%d: va %p qsz %d\n", id, addr, len);
dev_dbg(rproc->dev, "vring%d: va %p qsz %d notifyid %d\n",
id, addr, len, rvring->notifyid);
* Create the new vq, and tell virtio we're not interested in
......@@ -104,32 +103,28 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
rproc_virtio_notify, callback, name);
if (!vq) {
dev_err(rproc->dev, "vring_new_virtqueue %s failed\n", name);
ret = -ENOMEM;
goto free_rpvq;
return ERR_PTR(-ENOMEM);
rvdev->vq[id] = vq;
vq->priv = rpvq;
rvring->vq = vq;
vq->priv = rvring;
return vq;
return ERR_PTR(ret);
static void rproc_virtio_del_vqs(struct virtio_device *vdev)
struct virtqueue *vq, *n;
struct rproc *rproc = vdev_to_rproc(vdev);
struct rproc_vring *rvring;
/* power down the remote processor before deleting vqs */
list_for_each_entry_safe(vq, n, &vdev->vqs, list) {
struct rproc_virtio_vq_info *rpvq = vq->priv;
rvring = vq->priv;
rvring->vq = NULL;
......@@ -141,10 +136,6 @@ static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs,
struct rproc *rproc = vdev_to_rproc(vdev);
int i, ret;
/* we maintain two virtqueues per remote processor (for RX and TX) */
if (nvqs != 2)
return -EINVAL;
for (i = 0; i < nvqs; ++i) {
vqs[i] = rp_find_vq(vdev, i, callbacks[i], names[i]);
if (IS_ERR(vqs[i])) {
......@@ -170,7 +161,7 @@ static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs,
* We don't support yet real virtio status semantics.
* The plan is to provide this via the VIRTIO HDR resource entry
* 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.
......@@ -181,7 +172,7 @@ static u8 rproc_virtio_get_status(struct virtio_device *vdev)
static void rproc_virtio_set_status(struct virtio_device *vdev, u8 status)
dev_dbg(&vdev->dev, "new status: %d\n", status);
dev_dbg(&vdev->dev, "status: %d\n", status);
static void rproc_virtio_reset(struct virtio_device *vdev)
......@@ -192,15 +183,14 @@ static void rproc_virtio_reset(struct virtio_device *vdev)
/* provide the vdev features as retrieved from the firmware */
static u32 rproc_virtio_get_features(struct virtio_device *vdev)
struct rproc *rproc = vdev_to_rproc(vdev);
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
/* we only support a single vdev device for now */
return rproc->rvdev->dfeatures;
return rvdev->dfeatures;
static void rproc_virtio_finalize_features(struct virtio_device *vdev)
struct rproc *rproc = vdev_to_rproc(vdev);
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
/* Give virtio_ring a chance to accept features */
......@@ -214,7 +204,7 @@ static void rproc_virtio_finalize_features(struct virtio_device *vdev)
* fixed as part of a small resource table overhaul and then an
* extension of the virtio resource entries.
rproc->rvdev->gfeatures = vdev->features[0];
rvdev->gfeatures = vdev->features[0];
static struct virtio_config_ops rproc_virtio_config_ops = {
......@@ -244,26 +234,25 @@ static void rproc_vdev_release(struct device *dev)
* rproc_add_rpmsg_vdev() - create an rpmsg virtio device
* @rproc: the rproc handle
* rproc_add_virtio_dev() - register an rproc-induced virtio device
* @rvdev: the remote vdev
* This function is called if virtio rpmsg support was found in the
* firmware of the remote processor.
* This function registers a virtio device. This vdev's partent is
* the rproc device.
* Today we only support creating a single rpmsg vdev (virtio device),
* but the plan is to remove this limitation. At that point this interface
* will be revised/extended.
* Returns 0 on success or an appropriate error value otherwise.
int rproc_add_rpmsg_vdev(struct rproc *rproc)
int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
struct rproc *rproc = rvdev->rproc;
struct device *dev = rproc->dev;
struct rproc_vdev *rvdev = rproc->rvdev;
struct virtio_device *vdev = &rvdev->vdev;
int ret;
rvdev-> = VIRTIO_ID_RPMSG,
rvdev->vdev.config = &rproc_virtio_config_ops,
rvdev-> = dev;
rvdev-> = rproc_vdev_release;
vdev->id.device = id,
vdev->config = &rproc_virtio_config_ops,
vdev->dev.parent = dev;
vdev->dev.release = rproc_vdev_release;
* We're indirectly making a non-temporary copy of the rproc pointer
......@@ -275,25 +264,26 @@ int rproc_add_rpmsg_vdev(struct rproc *rproc)
ret = register_virtio_device(&rvdev->vdev);
ret = register_virtio_device(vdev);
if (ret) {
kref_put(&rproc->refcount, rproc_release);
dev_err(dev, "failed to register vdev: %d\n", ret);
goto out;
dev_info(dev, "registered %s (type %d)\n", dev_name(&vdev->dev), id);
return ret;
* rproc_remove_rpmsg_vdev() - remove an rpmsg vdev device
* @rproc: the rproc handle
* rproc_remove_virtio_dev() - remove an rproc-induced virtio device
* @rvdev: the remote vdev
* This function is called whenever @rproc is removed _iff_ an rpmsg
* vdev was created beforehand.
* This function unregisters an existing virtio device.
void rproc_remove_rpmsg_vdev(struct rproc *rproc)
void rproc_remove_virtio_dev(struct rproc_vdev *rvdev)
struct rproc_vdev *rvdev = rproc->rvdev;
......@@ -41,6 +41,7 @@
#include <linux/mutex.h>
#include <linux/virtio.h>
#include <linux/completion.h>
#include <linux/idr.h>
* The alignment between the consumer and producer parts of the vring.
......@@ -387,7 +388,8 @@ enum rproc_state {
* @mappings: list of iommu mappings we initiated, needed on shutdown
* @firmware_loading_complete: marks e/o asynchronous firmware loading
* @bootaddr: address of first instruction to boot rproc with (optional)
* @rvdev: virtio device (we only support a single rpmsg virtio device for now)
* @rvdevs: list of remote virtio devices
* @notifyids: idr for dynamically assigning rproc-wide unique notify ids
struct rproc {
struct klist_node node;
......@@ -408,23 +410,47 @@ struct rproc {
struct list_head mappings;
struct completion firmware_loading_complete;
u32 bootaddr;
struct list_head rvdevs;
struct idr notifyids;
/* we currently support only two vrings per rvdev */
* struct rproc_vring - remoteproc vring state
* @va: virtual address
* @dma: dma address
* @len: length, in bytes
* @da: device address
* @notifyid: rproc-specific unique vring index
* @rvdev: remote vdev
* @vq: the virtqueue of this vring
struct rproc_vring {
void *va;
dma_addr_t dma;
int len;
u32 da;
int notifyid;
struct rproc_vdev *rvdev;
struct virtqueue *vq;
* struct rproc_vdev - remoteproc state for a supported virtio device
* @node: list node
* @rproc: the rproc handle
* @vdev: the virio device
* @vq: the virtqueues for this vdev
* @vring: the vrings for this vdev
* @dfeatures: virtio device features
* @gfeatures: virtio guest features
struct rproc_vdev {
struct list_head node;
struct rproc *rproc;
struct virtio_device vdev;
struct virtqueue *vq[2];
struct rproc_mem_entry vring[2];
struct rproc_vring vring[RVDEV_NUM_VRINGS];
unsigned long dfeatures;
unsigned long gfeatures;
......@@ -442,9 +468,14 @@ int rproc_unregister(struct rproc *rproc);
int rproc_boot(struct rproc *rproc);
void rproc_shutdown(struct rproc *rproc);
static inline struct rproc_vdev *vdev_to_rvdev(struct virtio_device *vdev)
return container_of(vdev, struct rproc_vdev, vdev);
static inline struct rproc *vdev_to_rproc(struct virtio_device *vdev)
struct rproc_vdev *rvdev = container_of(vdev, struct rproc_vdev, vdev);
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
return rvdev->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