diff options
Diffstat (limited to 'drivers/remoteproc/remoteproc_virtio.c')
-rw-r--r-- | drivers/remoteproc/remoteproc_virtio.c | 140 |
1 files changed, 65 insertions, 75 deletions
diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c index 78d8527a8fec..07004106c954 100644 --- a/drivers/remoteproc/remoteproc_virtio.c +++ b/drivers/remoteproc/remoteproc_virtio.c @@ -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); } EXPORT_SYMBOL(rproc_vq_interrupt); @@ -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; - -free_rpvq: - kfree(rpvq); - 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 */ rproc_shutdown(rproc); 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); - kfree(rpvq); } } @@ -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 @@ error: /* * 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 */ vring_transport_features(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 * 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->vdev.id.device = VIRTIO_ID_RPMSG, - rvdev->vdev.config = &rproc_virtio_config_ops, - rvdev->vdev.dev.parent = dev; - rvdev->vdev.dev.release = 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) */ kref_get(&rproc->refcount); - 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); + +out: 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; - unregister_virtio_device(&rvdev->vdev); } |