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 <ohad@wizery.com>
Cc: Brian Swetland <swetland@google.com>
Cc: Iliyan Malchev <malchev@google.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Grant Likely <grant.likely@secretlab.ca>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Cc: Mark Grosen <mgrosen@ti.com>
Cc: John Williams <john.williams@petalogix.com>
Cc: Michal Simek <monstr@monstr.eu>
Cc: Loic PALLARDY <loic.pallardy@stericsson.com>
Cc: Ludovic BARRE <ludovic.barre@stericsson.com>
Cc: Omar Ramirez Luna <omar.luna@linaro.org>
Cc: Guzman Lugo Fernando <fernando.lugo@ti.com>
Cc: Anna Suman <s-anna@ti.com>
Cc: Clark Rob <rob@ti.com>
Cc: Stephen Boyd <sboyd@codeaurora.org>
Cc: Saravana Kannan <skannan@codeaurora.org>
Cc: David Brown <davidb@codeaurora.org>
Cc: Kieran Bingham <kieranbingham@gmail.com>
Cc: Tony Lindgren <tony@atomide.com>
parent 41a6ee09
...@@ -20,6 +20,11 @@ platform-specific remoteproc drivers only need to provide a few low-level ...@@ -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 handlers, and then all rpmsg drivers will then just work
(for more information about the virtio-based rpmsg bus and its drivers, (for more information about the virtio-based rpmsg bus and its drivers,
please read Documentation/rpmsg.txt). 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
cost.
2. User API 2. User API
...@@ -136,8 +141,6 @@ int dummy_rproc_example(struct rproc *my_rproc) ...@@ -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 If found, those virtio devices will be created and added, so as a result
of registering this remote processor, additional virtio drivers might get of registering this remote processor, additional virtio drivers might get
probed. probed.
Currently, though, we only support a single RPMSG virtio vdev per remote
processor.
int rproc_unregister(struct rproc *rproc) int rproc_unregister(struct rproc *rproc)
- Unregister a remote processor, and decrement its refcount. - Unregister a remote processor, and decrement its refcount.
...@@ -174,7 +177,7 @@ struct rproc_ops { ...@@ -174,7 +177,7 @@ struct rproc_ops {
}; };
Every remoteproc implementation should at least provide the ->start and ->stop 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. should be provided as well.
The ->start() handler takes an rproc handle and should then power on the The ->start() handler takes an rproc handle and should then power on the
......
This diff is collapsed.
...@@ -28,9 +28,9 @@ struct rproc; ...@@ -28,9 +28,9 @@ struct rproc;
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);
/* from remoteproc_rpmsg.c */ /* from remoteproc_virtio.c */
int rproc_add_rpmsg_vdev(struct rproc *); int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id);
void rproc_remove_rpmsg_vdev(struct rproc *rproc); void rproc_remove_virtio_dev(struct rproc_vdev *rvdev);
/* from remoteproc_debugfs.c */ /* from remoteproc_debugfs.c */
void rproc_remove_trace_file(struct dentry *tfile); void rproc_remove_trace_file(struct dentry *tfile);
......
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
#include <linux/export.h> #include <linux/export.h>
#include <linux/remoteproc.h> #include <linux/remoteproc.h>
#include <linux/rpmsg.h>
#include <linux/virtio.h> #include <linux/virtio.h>
#include <linux/virtio_config.h> #include <linux/virtio_config.h>
#include <linux/virtio_ids.h> #include <linux/virtio_ids.h>
...@@ -30,45 +29,41 @@ ...@@ -30,45 +29,41 @@
#include "remoteproc_internal.h" #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 */ /* kick the remote processor, and let it know which virtqueue to poke at */
static void rproc_virtio_notify(struct virtqueue *vq) static void rproc_virtio_notify(struct virtqueue *vq)
{ {
struct rproc_virtio_vq_info *rpvq = vq->priv; struct rproc_vring *rvring = vq->priv;
struct rproc *rproc = rpvq->rproc; 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_vq_interrupt() - tell remoteproc that a virtqueue is interrupted
* @rproc: handle to the remote processor * @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, * This function should be called by the platform-specific rproc driver,
* when the remote processor signals that a specific virtqueue has pending * when the remote processor signals that a specific virtqueue has pending
* messages available. * 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. * 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);
} }
EXPORT_SYMBOL(rproc_vq_interrupt); EXPORT_SYMBOL(rproc_vq_interrupt);
...@@ -77,24 +72,28 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, ...@@ -77,24 +72,28 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
void (*callback)(struct virtqueue *vq), void (*callback)(struct virtqueue *vq),
const char *name) const char *name)
{ {
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
struct rproc *rproc = vdev_to_rproc(vdev); struct rproc *rproc = vdev_to_rproc(vdev);
struct rproc_vdev *rvdev = rproc->rvdev; struct rproc_vring *rvring;
struct rproc_virtio_vq_info *rpvq;
struct virtqueue *vq; struct virtqueue *vq;
void *addr; void *addr;
int ret, len; int len, size;
rpvq = kmalloc(sizeof(*rpvq), GFP_KERNEL); /* we're temporarily limited to two virtqueues per rvdev */
if (!rpvq) if (id >= ARRAY_SIZE(rvdev->vring))
return ERR_PTR(-ENOMEM); return ERR_PTR(-EINVAL);
rvring = &rvdev->vring[id];
rpvq->rproc = rproc; addr = rvring->va;
rpvq->vq_id = id; len = rvring->len;
addr = rvdev->vring[id].va; /* zero vring */
len = rvdev->vring[id].len; 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 * 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, ...@@ -104,32 +103,28 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
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(rproc->dev, "vring_new_virtqueue %s failed\n", name);
ret = -ENOMEM; return ERR_PTR(-ENOMEM);
goto free_rpvq;
} }
rvdev->vq[id] = vq; rvring->vq = vq;
vq->priv = rpvq; vq->priv = rvring;
return vq; return vq;
free_rpvq:
kfree(rpvq);
return ERR_PTR(ret);
} }
static void rproc_virtio_del_vqs(struct virtio_device *vdev) static void rproc_virtio_del_vqs(struct virtio_device *vdev)
{ {
struct virtqueue *vq, *n; struct virtqueue *vq, *n;
struct rproc *rproc = vdev_to_rproc(vdev); struct rproc *rproc = vdev_to_rproc(vdev);
struct rproc_vring *rvring;
/* power down the remote processor before deleting vqs */ /* power down the remote processor before deleting vqs */
rproc_shutdown(rproc); rproc_shutdown(rproc);
list_for_each_entry_safe(vq, n, &vdev->vqs, list) { list_for_each_entry_safe(vq, n, &vdev->vqs, list) {
struct rproc_virtio_vq_info *rpvq = vq->priv; rvring = vq->priv;
rvring->vq = NULL;
vring_del_virtqueue(vq); vring_del_virtqueue(vq);
kfree(rpvq);
} }
} }
...@@ -141,10 +136,6 @@ static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs, ...@@ -141,10 +136,6 @@ static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs,
struct rproc *rproc = vdev_to_rproc(vdev); struct rproc *rproc = vdev_to_rproc(vdev);
int i, ret; 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) { for (i = 0; i < nvqs; ++i) {
vqs[i] = rp_find_vq(vdev, i, callbacks[i], names[i]); vqs[i] = rp_find_vq(vdev, i, callbacks[i], names[i]);
if (IS_ERR(vqs[i])) { if (IS_ERR(vqs[i])) {
...@@ -170,7 +161,7 @@ static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs, ...@@ -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. * 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 * which is part of the firmware: this way the remote processor
* will be able to access the status values as set by us. * 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) ...@@ -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) 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) static void rproc_virtio_reset(struct virtio_device *vdev)
...@@ -192,15 +183,14 @@ 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 */ /* provide the vdev features as retrieved from the firmware */
static u32 rproc_virtio_get_features(struct virtio_device *vdev) 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 rvdev->dfeatures;
return rproc->rvdev->dfeatures;
} }
static void rproc_virtio_finalize_features(struct virtio_device *vdev) 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 */ /* Give virtio_ring a chance to accept features */
vring_transport_features(vdev); vring_transport_features(vdev);
...@@ -214,7 +204,7 @@ static void rproc_virtio_finalize_features(struct virtio_device *vdev) ...@@ -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 * fixed as part of a small resource table overhaul and then an
* extension of the virtio resource entries. * 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 = { static struct virtio_config_ops rproc_virtio_config_ops = {
...@@ -244,26 +234,25 @@ static void rproc_vdev_release(struct device *dev) ...@@ -244,26 +234,25 @@ static void rproc_vdev_release(struct device *dev)
} }
/** /**
* rproc_add_rpmsg_vdev() - create an rpmsg virtio device * rproc_add_virtio_dev() - register an rproc-induced virtio device
* @rproc: the rproc handle * @rvdev: the remote vdev
* *
* This function is called if virtio rpmsg support was found in the * This function registers a virtio device. This vdev's partent is
* firmware of the remote processor. * the rproc device.
* *
* Today we only support creating a single rpmsg vdev (virtio device), * Returns 0 on success or an appropriate error value otherwise.
* but the plan is to remove this limitation. At that point this interface
* will be revised/extended.
*/ */
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 device *dev = rproc->dev;
struct rproc_vdev *rvdev = rproc->rvdev; struct virtio_device *vdev = &rvdev->vdev;
int ret; int ret;
rvdev->vdev.id.device = VIRTIO_ID_RPMSG, vdev->id.device = id,
rvdev->vdev.config = &rproc_virtio_config_ops, vdev->config = &rproc_virtio_config_ops,
rvdev->vdev.dev.parent = dev; vdev->dev.parent = dev;
rvdev->vdev.dev.release = rproc_vdev_release; vdev->dev.release = rproc_vdev_release;
/* /*
* We're indirectly making a non-temporary copy of the rproc pointer * We're indirectly making a non-temporary copy of the rproc pointer
...@@ -275,25 +264,26 @@ int rproc_add_rpmsg_vdev(struct rproc *rproc) ...@@ -275,25 +264,26 @@ int rproc_add_rpmsg_vdev(struct rproc *rproc)
*/ */
kref_get(&rproc->refcount); kref_get(&rproc->refcount);
ret = register_virtio_device(&rvdev->vdev); ret = register_virtio_device(vdev);
if (ret) { if (ret) {
kref_put(&rproc->refcount, rproc_release); kref_put(&rproc->refcount, rproc_release);
dev_err(dev, "failed to register vdev: %d\n", ret); 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);
out:
return ret; return ret;
} }
/** /**
* rproc_remove_rpmsg_vdev() - remove an rpmsg vdev device * rproc_remove_virtio_dev() - remove an rproc-induced virtio device
* @rproc: the rproc handle * @rvdev: the remote vdev
* *
* This function is called whenever @rproc is removed _iff_ an rpmsg * This function unregisters an existing virtio device.
* vdev was created beforehand.
*/ */
void rproc_remove_rpmsg_vdev(struct rproc *rproc) void rproc_remove_virtio_dev(struct rproc_vdev *rvdev)
{ {
struct rproc_vdev *rvdev = rproc->rvdev;
unregister_virtio_device(&rvdev->vdev); unregister_virtio_device(&rvdev->vdev);
} }
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/virtio.h> #include <linux/virtio.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/idr.h>
/* /*
* The alignment between the consumer and producer parts of the vring. * The alignment between the consumer and producer parts of the vring.
...@@ -387,7 +388,8 @@ enum rproc_state { ...@@ -387,7 +388,8 @@ enum rproc_state {
* @mappings: list of iommu mappings we initiated, needed on shutdown * @mappings: list of iommu mappings we initiated, needed on shutdown
* @firmware_loading_complete: marks e/o asynchronous firmware loading * @firmware_loading_complete: marks e/o asynchronous firmware loading
* @bootaddr: address of first instruction to boot rproc with (optional) * @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 rproc {
struct klist_node node; struct klist_node node;
...@@ -408,23 +410,47 @@ struct rproc { ...@@ -408,23 +410,47 @@ struct rproc {
struct list_head mappings; struct list_head mappings;
struct completion firmware_loading_complete; struct completion firmware_loading_complete;
u32 bootaddr; u32 bootaddr;
struct list_head rvdevs;
struct idr notifyids;
};
/* we currently support only two vrings per rvdev */
#define RVDEV_NUM_VRINGS 2
/**
* 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 rproc_vdev *rvdev;
struct virtqueue *vq;
}; };
/** /**
* struct rproc_vdev - remoteproc state for a supported virtio device * struct rproc_vdev - remoteproc state for a supported virtio device
* @node: list node
* @rproc: the rproc handle * @rproc: the rproc handle
* @vdev: the virio device * @vdev: the virio device
* @vq: the virtqueues for this vdev
* @vring: the vrings for this vdev * @vring: the vrings for this vdev
* @dfeatures: virtio device features * @dfeatures: virtio device features
* @gfeatures: virtio guest features * @gfeatures: virtio guest features
*/ */
struct rproc_vdev { struct rproc_vdev {
struct list_head node;
struct rproc *rproc; struct rproc *rproc;
struct virtio_device vdev; struct virtio_device vdev;
struct virtqueue *vq[2]; struct rproc_vring vring[RVDEV_NUM_VRINGS];
struct rproc_mem_entry vring[2];
unsigned long dfeatures; unsigned long dfeatures;
unsigned long gfeatures; unsigned long gfeatures;
}; };
...@@ -442,9 +468,14 @@ int rproc_unregister(struct rproc *rproc); ...@@ -442,9 +468,14 @@ int rproc_unregister(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);
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) 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; 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