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        if (!name)
  88                return NULL;
  89
  90        ret = rproc_alloc_vring(rvdev, id);
  91        if (ret)
  92                return ERR_PTR(ret);
  93
  94        rvring = &rvdev->vring[id];
  95        addr = rvring->va;
  96        len = rvring->len;
  97
  98        /* zero vring */
  99        size = vring_size(len, rvring->align);
 100        memset(addr, 0, size);
 101
 102        dev_dbg(dev, "vring%d: va %p qsz %d notifyid %d\n",
 103                                        id, addr, len, rvring->notifyid);
 104
 105        /*
 106         * Create the new vq, and tell virtio we're not interested in
 107         * the 'weak' smp barriers, since we're talking with a real device.
 108         */
 109        vq = vring_new_virtqueue(id, len, rvring->align, vdev, false, addr,
 110                                        rproc_virtio_notify, callback, name);
 111        if (!vq) {
 112                dev_err(dev, "vring_new_virtqueue %s failed\n", name);
 113                rproc_free_vring(rvring);
 114                return ERR_PTR(-ENOMEM);
 115        }
 116
 117        rvring->vq = vq;
 118        vq->priv = rvring;
 119
 120        return vq;
 121}
 122
 123static void __rproc_virtio_del_vqs(struct virtio_device *vdev)
 124{
 125        struct virtqueue *vq, *n;
 126        struct rproc_vring *rvring;
 127
 128        list_for_each_entry_safe(vq, n, &vdev->vqs, list) {
 129                rvring = vq->priv;
 130                rvring->vq = NULL;
 131                vring_del_virtqueue(vq);
 132                rproc_free_vring(rvring);
 133        }
 134}
 135
 136static void rproc_virtio_del_vqs(struct virtio_device *vdev)
 137{
 138        struct rproc *rproc = vdev_to_rproc(vdev);
 139
 140        /* power down the remote processor before deleting vqs */
 141        rproc_shutdown(rproc);
 142
 143        __rproc_virtio_del_vqs(vdev);
 144}
 145
 146static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs,
 147                       struct virtqueue *vqs[],
 148                       vq_callback_t *callbacks[],
 149                       const char *names[])
 150{
 151        struct rproc *rproc = vdev_to_rproc(vdev);
 152        int i, ret;
 153
 154        for (i = 0; i < nvqs; ++i) {
 155                vqs[i] = rp_find_vq(vdev, i, callbacks[i], names[i]);
 156                if (IS_ERR(vqs[i])) {
 157                        ret = PTR_ERR(vqs[i]);
 158                        goto error;
 159                }
 160        }
 161
 162        /* now that the vqs are all set, boot the remote processor */
 163        ret = rproc_boot(rproc);
 164        if (ret) {
 165                dev_err(&rproc->dev, "rproc_boot() failed %d\n", ret);
 166                goto error;
 167        }
 168
 169        return 0;
 170
 171error:
 172        __rproc_virtio_del_vqs(vdev);
 173        return ret;
 174}
 175
 176/*
 177 * We don't support yet real virtio status semantics.
 178 *
 179 * The plan is to provide this via the VDEV resource entry
 180 * which is part of the firmware: this way the remote processor
 181 * will be able to access the status values as set by us.
 182 */
 183static u8 rproc_virtio_get_status(struct virtio_device *vdev)
 184{
 185        return 0;
 186}
 187
 188static void rproc_virtio_set_status(struct virtio_device *vdev, u8 status)
 189{
 190        dev_dbg(&vdev->dev, "status: %d\n", status);
 191}
 192
 193static void rproc_virtio_reset(struct virtio_device *vdev)
 194{
 195        dev_dbg(&vdev->dev, "reset !\n");
 196}
 197
 198/* provide the vdev features as retrieved from the firmware */
 199static u32 rproc_virtio_get_features(struct virtio_device *vdev)
 200{
 201        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
 202
 203        return rvdev->dfeatures;
 204}
 205
 206static void rproc_virtio_finalize_features(struct virtio_device *vdev)
 207{
 208        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
 209
 210        /* Give virtio_ring a chance to accept features */
 211        vring_transport_features(vdev);
 212
 213        /*
 214         * Remember the finalized features of our vdev, and provide it
 215         * to the remote processor once it is powered on.
 216         *
 217         * Similarly to the status field, we don't expose yet the negotiated
 218         * features to the remote processors at this point. This will be
 219         * fixed as part of a small resource table overhaul and then an
 220         * extension of the virtio resource entries.
 221         */
 222        rvdev->gfeatures = vdev->features[0];
 223}
 224
 225static struct virtio_config_ops rproc_virtio_config_ops = {
 226        .get_features   = rproc_virtio_get_features,
 227        .finalize_features = rproc_virtio_finalize_features,
 228        .find_vqs       = rproc_virtio_find_vqs,
 229        .del_vqs        = rproc_virtio_del_vqs,
 230        .reset          = rproc_virtio_reset,
 231        .set_status     = rproc_virtio_set_status,
 232        .get_status     = rproc_virtio_get_status,
 233};
 234
 235/*
 236 * This function is called whenever vdev is released, and is responsible
 237 * to decrement the remote processor's refcount which was taken when vdev was
 238 * added.
 239 *
 240 * Never call this function directly; it will be called by the driver
 241 * core when needed.
 242 */
 243static void rproc_vdev_release(struct device *dev)
 244{
 245        struct virtio_device *vdev = dev_to_virtio(dev);
 246        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
 247        struct rproc *rproc = vdev_to_rproc(vdev);
 248
 249        list_del(&rvdev->node);
 250        kfree(rvdev);
 251
 252        put_device(&rproc->dev);
 253}
 254
 255/**
 256 * rproc_add_virtio_dev() - register an rproc-induced virtio device
 257 * @rvdev: the remote vdev
 258 *
 259 * This function registers a virtio device. This vdev's partent is
 260 * the rproc device.
 261 *
 262 * Returns 0 on success or an appropriate error value otherwise.
 263 */
 264int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
 265{
 266        struct rproc *rproc = rvdev->rproc;
 267        struct device *dev = &rproc->dev;
 268        struct virtio_device *vdev = &rvdev->vdev;
 269        int ret;
 270
 271        vdev->id.device = id,
 272        vdev->config = &rproc_virtio_config_ops,
 273        vdev->dev.parent = dev;
 274        vdev->dev.release = rproc_vdev_release;
 275
 276        /*
 277         * We're indirectly making a non-temporary copy of the rproc pointer
 278         * here, because drivers probed with this vdev will indirectly
 279         * access the wrapping rproc.
 280         *
 281         * Therefore we must increment the rproc refcount here, and decrement
 282         * it _only_ when the vdev is released.
 283         */
 284        get_device(&rproc->dev);
 285
 286        ret = register_virtio_device(vdev);
 287        if (ret) {
 288                put_device(&rproc->dev);
 289                dev_err(dev, "failed to register vdev: %d\n", ret);
 290                goto out;
 291        }
 292
 293        dev_info(dev, "registered %s (type %d)\n", dev_name(&vdev->dev), id);
 294
 295out:
 296        return ret;
 297}
 298
 299/**
 300 * rproc_remove_virtio_dev() - remove an rproc-induced virtio device
 301 * @rvdev: the remote vdev
 302 *
 303 * This function unregisters an existing virtio device.
 304 */
 305void rproc_remove_virtio_dev(struct rproc_vdev *rvdev)
 306{
 307        unregister_virtio_device(&rvdev->vdev);
 308}
 309
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.