linux/drivers/remoteproc/remoteproc_virtio.c
<<
>>
Prefs
   1/*
   2 * Remote processor messaging transport (OMAP platform-specific bits)
   3 *
   4 * Copyright (C) 2011 Texas Instruments, Inc.
   5 * Copyright (C) 2011 Google, Inc.
   6 *
   7 * Ohad Ben-Cohen <ohad@wizery.com>
   8 * Brian Swetland <swetland@google.com>
   9 *
  10 * This software is licensed under the terms of the GNU General Public
  11 * License version 2, as published by the Free Software Foundation, and
  12 * may be copied, distributed, and modified under those terms.
  13 *
  14 * This program is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17 * GNU General Public License for more details.
  18 */
  19
  20#include <linux/export.h>
  21#include <linux/remoteproc.h>
  22#include <linux/virtio.h>
  23#include <linux/virtio_config.h>
  24#include <linux/virtio_ids.h>
  25#include <linux/virtio_ring.h>
  26#include <linux/err.h>
  27#include <linux/kref.h>
  28#include <linux/slab.h>
  29
  30#include "remoteproc_internal.h"
  31
  32/* kick the remote processor, and let it know which virtqueue to poke at */
  33static void rproc_virtio_notify(struct virtqueue *vq)
  34{
  35        struct rproc_vring *rvring = vq->priv;
  36        struct rproc *rproc = rvring->rvdev->rproc;
  37        int notifyid = rvring->notifyid;
  38
  39        dev_dbg(&rproc->dev, "kicking vq index: %d\n", notifyid);
  40
  41        rproc->ops->kick(rproc, notifyid);
  42}
  43
  44/**
  45 * rproc_vq_interrupt() - tell remoteproc that a virtqueue is interrupted
  46 * @rproc: handle to the remote processor
  47 * @notifyid: index of the signalled virtqueue (unique per this @rproc)
  48 *
  49 * This function should be called by the platform-specific rproc driver,
  50 * when the remote processor signals that a specific virtqueue has pending
  51 * messages available.
  52 *
  53 * Returns IRQ_NONE if no message was found in the @notifyid virtqueue,
  54 * and otherwise returns IRQ_HANDLED.
  55 */
  56irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int notifyid)
  57{
  58        struct rproc_vring *rvring;
  59
  60        dev_dbg(&rproc->dev, "vq index %d is interrupted\n", notifyid);
  61
  62        rvring = idr_find(&rproc->notifyids, notifyid);
  63        if (!rvring || !rvring->vq)
  64                return IRQ_NONE;
  65
  66        return vring_interrupt(0, rvring->vq);
  67}
  68EXPORT_SYMBOL(rproc_vq_interrupt);
  69
  70static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
  71                                    unsigned id,
  72                                    void (*callback)(struct virtqueue *vq),
  73                                    const char *name)
  74{
  75        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
  76        struct rproc *rproc = vdev_to_rproc(vdev);
  77        struct device *dev = &rproc->dev;
  78        struct rproc_vring *rvring;
  79        struct virtqueue *vq;
  80        void *addr;
  81        int len, size, ret;
  82
  83        /* we're temporarily limited to two virtqueues per rvdev */
  84        if (id >= ARRAY_SIZE(rvdev->vring))
  85                return ERR_PTR(-EINVAL);
  86
  87        ret = rproc_alloc_vring(rvdev, id);
  88        if (ret)
  89                return ERR_PTR(ret);
  90
  91        rvring = &rvdev->vring[id];
  92        addr = rvring->va;
  93        len = rvring->len;
  94
  95        /* zero vring */
  96        size = vring_size(len, rvring->align);
  97        memset(addr, 0, size);
  98
  99        dev_dbg(dev, "vring%d: va %p qsz %d notifyid %d\n",
 100                                        id, addr, len, rvring->notifyid);
 101
 102        /*
 103         * Create the new vq, and tell virtio we're not interested in
 104         * the 'weak' smp barriers, since we're talking with a real device.
 105         */
 106        vq = vring_new_virtqueue(len, rvring->align, vdev, false, addr,
 107                                        rproc_virtio_notify, callback, name);
 108        if (!vq) {
 109                dev_err(dev, "vring_new_virtqueue %s failed\n", name);
 110                rproc_free_vring(rvring);
 111                return ERR_PTR(-ENOMEM);
 112        }
 113
 114        rvring->vq = vq;
 115        vq->priv = rvring;
 116
 117        return vq;
 118}
 119
 120static void rproc_virtio_del_vqs(struct virtio_device *vdev)
 121{
 122        struct virtqueue *vq, *n;
 123        struct rproc *rproc = vdev_to_rproc(vdev);
 124        struct rproc_vring *rvring;
 125
 126        /* power down the remote processor before deleting vqs */
 127        rproc_shutdown(rproc);
 128
 129        list_for_each_entry_safe(vq, n, &vdev->vqs, list) {
 130                rvring = vq->priv;
 131                rvring->vq = NULL;
 132                vring_del_virtqueue(vq);
 133                rproc_free_vring(rvring);
 134        }
 135}
 136
 137static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs,
 138                       struct virtqueue *vqs[],
 139                       vq_callback_t *callbacks[],
 140                       const char *names[])
 141{
 142        struct rproc *rproc = vdev_to_rproc(vdev);
 143        int i, ret;
 144
 145        for (i = 0; i < nvqs; ++i) {
 146                vqs[i] = rp_find_vq(vdev, i, callbacks[i], names[i]);
 147                if (IS_ERR(vqs[i])) {
 148                        ret = PTR_ERR(vqs[i]);
 149                        goto error;
 150                }
 151        }
 152
 153        /* now that the vqs are all set, boot the remote processor */
 154        ret = rproc_boot(rproc);
 155        if (ret) {
 156                dev_err(&rproc->dev, "rproc_boot() failed %d\n", ret);
 157                goto error;
 158        }
 159
 160        return 0;
 161
 162error:
 163        rproc_virtio_del_vqs(vdev);
 164        return ret;
 165}
 166
 167/*
 168 * We don't support yet real virtio status semantics.
 169 *
 170 * The plan is to provide this via the VDEV resource entry
 171 * which is part of the firmware: this way the remote processor
 172 * will be able to access the status values as set by us.
 173 */
 174static u8 rproc_virtio_get_status(struct virtio_device *vdev)
 175{
 176        return 0;
 177}
 178
 179static void rproc_virtio_set_status(struct virtio_device *vdev, u8 status)
 180{
 181        dev_dbg(&vdev->dev, "status: %d\n", status);
 182}
 183
 184static void rproc_virtio_reset(struct virtio_device *vdev)
 185{
 186        dev_dbg(&vdev->dev, "reset !\n");
 187}
 188
 189/* provide the vdev features as retrieved from the firmware */
 190static u32 rproc_virtio_get_features(struct virtio_device *vdev)
 191{
 192        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
 193
 194        return rvdev->dfeatures;
 195}
 196
 197static void rproc_virtio_finalize_features(struct virtio_device *vdev)
 198{
 199        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
 200
 201        /* Give virtio_ring a chance to accept features */
 202        vring_transport_features(vdev);
 203
 204        /*
 205         * Remember the finalized features of our vdev, and provide it
 206         * to the remote processor once it is powered on.
 207         *
 208         * Similarly to the status field, we don't expose yet the negotiated
 209         * features to the remote processors at this point. This will be
 210         * fixed as part of a small resource table overhaul and then an
 211         * extension of the virtio resource entries.
 212         */
 213        rvdev->gfeatures = vdev->features[0];
 214}
 215
 216static struct virtio_config_ops rproc_virtio_config_ops = {
 217        .get_features   = rproc_virtio_get_features,
 218        .finalize_features = rproc_virtio_finalize_features,
 219        .find_vqs       = rproc_virtio_find_vqs,
 220        .del_vqs        = rproc_virtio_del_vqs,
 221        .reset          = rproc_virtio_reset,
 222        .set_status     = rproc_virtio_set_status,
 223        .get_status     = rproc_virtio_get_status,
 224};
 225
 226/*
 227 * This function is called whenever vdev is released, and is responsible
 228 * to decrement the remote processor's refcount which was taken when vdev was
 229 * added.
 230 *
 231 * Never call this function directly; it will be called by the driver
 232 * core when needed.
 233 */
 234static void rproc_vdev_release(struct device *dev)
 235{
 236        struct virtio_device *vdev = dev_to_virtio(dev);
 237        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
 238        struct rproc *rproc = vdev_to_rproc(vdev);
 239
 240        list_del(&rvdev->node);
 241        kfree(rvdev);
 242
 243        put_device(&rproc->dev);
 244}
 245
 246/**
 247 * rproc_add_virtio_dev() - register an rproc-induced virtio device
 248 * @rvdev: the remote vdev
 249 *
 250 * This function registers a virtio device. This vdev's partent is
 251 * the rproc device.
 252 *
 253 * Returns 0 on success or an appropriate error value otherwise.
 254 */
 255int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
 256{
 257        struct rproc *rproc = rvdev->rproc;
 258        struct device *dev = &rproc->dev;
 259        struct virtio_device *vdev = &rvdev->vdev;
 260        int ret;
 261
 262        vdev->id.device = id,
 263        vdev->config = &rproc_virtio_config_ops,
 264        vdev->dev.parent = dev;
 265        vdev->dev.release = rproc_vdev_release;
 266
 267        /*
 268         * We're indirectly making a non-temporary copy of the rproc pointer
 269         * here, because drivers probed with this vdev will indirectly
 270         * access the wrapping rproc.
 271         *
 272         * Therefore we must increment the rproc refcount here, and decrement
 273         * it _only_ when the vdev is released.
 274         */
 275        get_device(&rproc->dev);
 276
 277        ret = register_virtio_device(vdev);
 278        if (ret) {
 279                put_device(&rproc->dev);
 280                dev_err(dev, "failed to register vdev: %d\n", ret);
 281                goto out;
 282        }
 283
 284        dev_info(dev, "registered %s (type %d)\n", dev_name(&vdev->dev), id);
 285
 286out:
 287        return ret;
 288}
 289
 290/**
 291 * rproc_remove_virtio_dev() - remove an rproc-induced virtio device
 292 * @rvdev: the remote vdev
 293 *
 294 * This function unregisters an existing virtio device.
 295 */
 296void rproc_remove_virtio_dev(struct rproc_vdev *rvdev)
 297{
 298        unregister_virtio_device(&rvdev->vdev);
 299}
 300
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.