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 bool 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        return true;
  43}
  44
  45/**
  46 * rproc_vq_interrupt() - tell remoteproc that a virtqueue is interrupted
  47 * @rproc: handle to the remote processor
  48 * @notifyid: index of the signalled virtqueue (unique per this @rproc)
  49 *
  50 * This function should be called by the platform-specific rproc driver,
  51 * when the remote processor signals that a specific virtqueue has pending
  52 * messages available.
  53 *
  54 * Returns IRQ_NONE if no message was found in the @notifyid virtqueue,
  55 * and otherwise returns IRQ_HANDLED.
  56 */
  57irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int notifyid)
  58{
  59        struct rproc_vring *rvring;
  60
  61        dev_dbg(&rproc->dev, "vq index %d is interrupted\n", notifyid);
  62
  63        rvring = idr_find(&rproc->notifyids, notifyid);
  64        if (!rvring || !rvring->vq)
  65                return IRQ_NONE;
  66
  67        return vring_interrupt(0, rvring->vq);
  68}
  69EXPORT_SYMBOL(rproc_vq_interrupt);
  70
  71static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
  72                                    unsigned int id,
  73                                    void (*callback)(struct virtqueue *vq),
  74                                    const char *name, bool ctx)
  75{
  76        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
  77        struct rproc *rproc = vdev_to_rproc(vdev);
  78        struct device *dev = &rproc->dev;
  79        struct rproc_vring *rvring;
  80        struct virtqueue *vq;
  81        void *addr;
  82        int len, size;
  83
  84        /* we're temporarily limited to two virtqueues per rvdev */
  85        if (id >= ARRAY_SIZE(rvdev->vring))
  86                return ERR_PTR(-EINVAL);
  87
  88        if (!name)
  89                return NULL;
  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(id, len, rvring->align, vdev, false, ctx,
 107                                 addr, 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_vring *rvring;
 124
 125        list_for_each_entry_safe(vq, n, &vdev->vqs, list) {
 126                rvring = vq->priv;
 127                rvring->vq = NULL;
 128                vring_del_virtqueue(vq);
 129        }
 130}
 131
 132static void rproc_virtio_del_vqs(struct virtio_device *vdev)
 133{
 134        __rproc_virtio_del_vqs(vdev);
 135}
 136
 137static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned int nvqs,
 138                                 struct virtqueue *vqs[],
 139                                 vq_callback_t *callbacks[],
 140                                 const char * const names[],
 141                                 const bool * ctx,
 142                                 struct irq_affinity *desc)
 143{
 144        int i, ret;
 145
 146        for (i = 0; i < nvqs; ++i) {
 147                vqs[i] = rp_find_vq(vdev, i, callbacks[i], names[i],
 148                                    ctx ? ctx[i] : false);
 149                if (IS_ERR(vqs[i])) {
 150                        ret = PTR_ERR(vqs[i]);
 151                        goto error;
 152                }
 153        }
 154
 155        return 0;
 156
 157error:
 158        __rproc_virtio_del_vqs(vdev);
 159        return ret;
 160}
 161
 162static u8 rproc_virtio_get_status(struct virtio_device *vdev)
 163{
 164        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
 165        struct fw_rsc_vdev *rsc;
 166
 167        rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
 168
 169        return rsc->status;
 170}
 171
 172static void rproc_virtio_set_status(struct virtio_device *vdev, u8 status)
 173{
 174        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
 175        struct fw_rsc_vdev *rsc;
 176
 177        rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
 178
 179        rsc->status = status;
 180        dev_dbg(&vdev->dev, "status: %d\n", status);
 181}
 182
 183static void rproc_virtio_reset(struct virtio_device *vdev)
 184{
 185        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
 186        struct fw_rsc_vdev *rsc;
 187
 188        rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
 189
 190        rsc->status = 0;
 191        dev_dbg(&vdev->dev, "reset !\n");
 192}
 193
 194/* provide the vdev features as retrieved from the firmware */
 195static u64 rproc_virtio_get_features(struct virtio_device *vdev)
 196{
 197        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
 198        struct fw_rsc_vdev *rsc;
 199
 200        rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
 201
 202        return rsc->dfeatures;
 203}
 204
 205static int rproc_virtio_finalize_features(struct virtio_device *vdev)
 206{
 207        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
 208        struct fw_rsc_vdev *rsc;
 209
 210        rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
 211
 212        /* Give virtio_ring a chance to accept features */
 213        vring_transport_features(vdev);
 214
 215        /* Make sure we don't have any features > 32 bits! */
 216        BUG_ON((u32)vdev->features != vdev->features);
 217
 218        /*
 219         * Remember the finalized features of our vdev, and provide it
 220         * to the remote processor once it is powered on.
 221         */
 222        rsc->gfeatures = vdev->features;
 223
 224        return 0;
 225}
 226
 227static void rproc_virtio_get(struct virtio_device *vdev, unsigned int offset,
 228                             void *buf, unsigned int len)
 229{
 230        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
 231        struct fw_rsc_vdev *rsc;
 232        void *cfg;
 233
 234        rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
 235        cfg = &rsc->vring[rsc->num_of_vrings];
 236
 237        if (offset + len > rsc->config_len || offset + len < len) {
 238                dev_err(&vdev->dev, "rproc_virtio_get: access out of bounds\n");
 239                return;
 240        }
 241
 242        memcpy(buf, cfg + offset, len);
 243}
 244
 245static void rproc_virtio_set(struct virtio_device *vdev, unsigned int offset,
 246                             const void *buf, unsigned int len)
 247{
 248        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
 249        struct fw_rsc_vdev *rsc;
 250        void *cfg;
 251
 252        rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
 253        cfg = &rsc->vring[rsc->num_of_vrings];
 254
 255        if (offset + len > rsc->config_len || offset + len < len) {
 256                dev_err(&vdev->dev, "rproc_virtio_set: access out of bounds\n");
 257                return;
 258        }
 259
 260        memcpy(cfg + offset, buf, len);
 261}
 262
 263static const struct virtio_config_ops rproc_virtio_config_ops = {
 264        .get_features   = rproc_virtio_get_features,
 265        .finalize_features = rproc_virtio_finalize_features,
 266        .find_vqs       = rproc_virtio_find_vqs,
 267        .del_vqs        = rproc_virtio_del_vqs,
 268        .reset          = rproc_virtio_reset,
 269        .set_status     = rproc_virtio_set_status,
 270        .get_status     = rproc_virtio_get_status,
 271        .get            = rproc_virtio_get,
 272        .set            = rproc_virtio_set,
 273};
 274
 275/*
 276 * This function is called whenever vdev is released, and is responsible
 277 * to decrement the remote processor's refcount which was taken when vdev was
 278 * added.
 279 *
 280 * Never call this function directly; it will be called by the driver
 281 * core when needed.
 282 */
 283static void rproc_virtio_dev_release(struct device *dev)
 284{
 285        struct virtio_device *vdev = dev_to_virtio(dev);
 286        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
 287        struct rproc *rproc = vdev_to_rproc(vdev);
 288
 289        kref_put(&rvdev->refcount, rproc_vdev_release);
 290
 291        put_device(&rproc->dev);
 292}
 293
 294/**
 295 * rproc_add_virtio_dev() - register an rproc-induced virtio device
 296 * @rvdev: the remote vdev
 297 *
 298 * This function registers a virtio device. This vdev's partent is
 299 * the rproc device.
 300 *
 301 * Returns 0 on success or an appropriate error value otherwise.
 302 */
 303int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
 304{
 305        struct rproc *rproc = rvdev->rproc;
 306        struct device *dev = &rproc->dev;
 307        struct virtio_device *vdev = &rvdev->vdev;
 308        int ret;
 309
 310        vdev->id.device = id,
 311        vdev->config = &rproc_virtio_config_ops,
 312        vdev->dev.parent = dev;
 313        vdev->dev.release = rproc_virtio_dev_release;
 314
 315        /*
 316         * We're indirectly making a non-temporary copy of the rproc pointer
 317         * here, because drivers probed with this vdev will indirectly
 318         * access the wrapping rproc.
 319         *
 320         * Therefore we must increment the rproc refcount here, and decrement
 321         * it _only_ when the vdev is released.
 322         */
 323        get_device(&rproc->dev);
 324
 325        /* Reference the vdev and vring allocations */
 326        kref_get(&rvdev->refcount);
 327
 328        ret = register_virtio_device(vdev);
 329        if (ret) {
 330                put_device(&vdev->dev);
 331                dev_err(dev, "failed to register vdev: %d\n", ret);
 332                goto out;
 333        }
 334
 335        dev_info(dev, "registered %s (type %d)\n", dev_name(&vdev->dev), id);
 336
 337out:
 338        return ret;
 339}
 340
 341/**
 342 * rproc_remove_virtio_dev() - remove an rproc-induced virtio device
 343 * @rvdev: the remote vdev
 344 *
 345 * This function unregisters an existing virtio device.
 346 */
 347void rproc_remove_virtio_dev(struct rproc_vdev *rvdev)
 348{
 349        unregister_virtio_device(&rvdev->vdev);
 350}
 351