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 id,
  73                                    void (*callback)(struct virtqueue *vq),
  74                                    const char *name)
  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, ret;
  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        ret = rproc_alloc_vring(rvdev, id);
  92        if (ret)
  93                return ERR_PTR(ret);
  94
  95        rvring = &rvdev->vring[id];
  96        addr = rvring->va;
  97        len = rvring->len;
  98
  99        /* zero vring */
 100        size = vring_size(len, rvring->align);
 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        vdev->config->set_status(vdev, 0);
 143
 144        __rproc_virtio_del_vqs(vdev);
 145}
 146
 147static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs,
 148                       struct virtqueue *vqs[],
 149                       vq_callback_t *callbacks[],
 150                       const char * const names[])
 151{
 152        struct rproc *rproc = vdev_to_rproc(vdev);
 153        int i, ret;
 154
 155        for (i = 0; i < nvqs; ++i) {
 156                vqs[i] = rp_find_vq(vdev, i, callbacks[i], names[i]);
 157                if (IS_ERR(vqs[i])) {
 158                        ret = PTR_ERR(vqs[i]);
 159                        goto error;
 160                }
 161        }
 162
 163        /* now that the vqs are all set, boot the remote processor */
 164        ret = rproc_boot(rproc);
 165        if (ret) {
 166                dev_err(&rproc->dev, "rproc_boot() failed %d\n", ret);
 167                goto error;
 168        }
 169
 170        return 0;
 171
 172error:
 173        __rproc_virtio_del_vqs(vdev);
 174        return ret;
 175}
 176
 177static u8 rproc_virtio_get_status(struct virtio_device *vdev)
 178{
 179        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
 180        struct fw_rsc_vdev *rsc;
 181
 182        rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
 183
 184        return rsc->status;
 185}
 186
 187static void rproc_virtio_set_status(struct virtio_device *vdev, u8 status)
 188{
 189        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
 190        struct fw_rsc_vdev *rsc;
 191
 192        rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
 193
 194        rsc->status = status;
 195        dev_dbg(&vdev->dev, "status: %d\n", status);
 196}
 197
 198static void rproc_virtio_reset(struct virtio_device *vdev)
 199{
 200        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
 201        struct fw_rsc_vdev *rsc;
 202
 203        rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
 204
 205        rsc->status = 0;
 206        dev_dbg(&vdev->dev, "reset !\n");
 207}
 208
 209/* provide the vdev features as retrieved from the firmware */
 210static u64 rproc_virtio_get_features(struct virtio_device *vdev)
 211{
 212        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
 213        struct fw_rsc_vdev *rsc;
 214
 215        rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
 216
 217        return rsc->dfeatures;
 218}
 219
 220static int rproc_virtio_finalize_features(struct virtio_device *vdev)
 221{
 222        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
 223        struct fw_rsc_vdev *rsc;
 224
 225        rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
 226
 227        /* Give virtio_ring a chance to accept features */
 228        vring_transport_features(vdev);
 229
 230        /* Make sure we don't have any features > 32 bits! */
 231        BUG_ON((u32)vdev->features != vdev->features);
 232
 233        /*
 234         * Remember the finalized features of our vdev, and provide it
 235         * to the remote processor once it is powered on.
 236         */
 237        rsc->gfeatures = vdev->features;
 238
 239        return 0;
 240}
 241
 242static void rproc_virtio_get(struct virtio_device *vdev, unsigned offset,
 243                                                        void *buf, unsigned len)
 244{
 245        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
 246        struct fw_rsc_vdev *rsc;
 247        void *cfg;
 248
 249        rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
 250        cfg = &rsc->vring[rsc->num_of_vrings];
 251
 252        if (offset + len > rsc->config_len || offset + len < len) {
 253                dev_err(&vdev->dev, "rproc_virtio_get: access out of bounds\n");
 254                return;
 255        }
 256
 257        memcpy(buf, cfg + offset, len);
 258}
 259
 260static void rproc_virtio_set(struct virtio_device *vdev, unsigned offset,
 261                      const void *buf, unsigned len)
 262{
 263        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
 264        struct fw_rsc_vdev *rsc;
 265        void *cfg;
 266
 267        rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
 268        cfg = &rsc->vring[rsc->num_of_vrings];
 269
 270        if (offset + len > rsc->config_len || offset + len < len) {
 271                dev_err(&vdev->dev, "rproc_virtio_set: access out of bounds\n");
 272                return;
 273        }
 274
 275        memcpy(cfg + offset, buf, len);
 276}
 277
 278static const struct virtio_config_ops rproc_virtio_config_ops = {
 279        .get_features   = rproc_virtio_get_features,
 280        .finalize_features = rproc_virtio_finalize_features,
 281        .find_vqs       = rproc_virtio_find_vqs,
 282        .del_vqs        = rproc_virtio_del_vqs,
 283        .reset          = rproc_virtio_reset,
 284        .set_status     = rproc_virtio_set_status,
 285        .get_status     = rproc_virtio_get_status,
 286        .get            = rproc_virtio_get,
 287        .set            = rproc_virtio_set,
 288};
 289
 290/*
 291 * This function is called whenever vdev is released, and is responsible
 292 * to decrement the remote processor's refcount which was taken when vdev was
 293 * added.
 294 *
 295 * Never call this function directly; it will be called by the driver
 296 * core when needed.
 297 */
 298static void rproc_vdev_release(struct device *dev)
 299{
 300        struct virtio_device *vdev = dev_to_virtio(dev);
 301        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
 302        struct rproc *rproc = vdev_to_rproc(vdev);
 303
 304        list_del(&rvdev->node);
 305        kfree(rvdev);
 306
 307        put_device(&rproc->dev);
 308}
 309
 310/**
 311 * rproc_add_virtio_dev() - register an rproc-induced virtio device
 312 * @rvdev: the remote vdev
 313 *
 314 * This function registers a virtio device. This vdev's partent is
 315 * the rproc device.
 316 *
 317 * Returns 0 on success or an appropriate error value otherwise.
 318 */
 319int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
 320{
 321        struct rproc *rproc = rvdev->rproc;
 322        struct device *dev = &rproc->dev;
 323        struct virtio_device *vdev = &rvdev->vdev;
 324        int ret;
 325
 326        vdev->id.device = id,
 327        vdev->config = &rproc_virtio_config_ops,
 328        vdev->dev.parent = dev;
 329        vdev->dev.release = rproc_vdev_release;
 330
 331        /*
 332         * We're indirectly making a non-temporary copy of the rproc pointer
 333         * here, because drivers probed with this vdev will indirectly
 334         * access the wrapping rproc.
 335         *
 336         * Therefore we must increment the rproc refcount here, and decrement
 337         * it _only_ when the vdev is released.
 338         */
 339        get_device(&rproc->dev);
 340
 341        ret = register_virtio_device(vdev);
 342        if (ret) {
 343                put_device(&rproc->dev);
 344                dev_err(dev, "failed to register vdev: %d\n", ret);
 345                goto out;
 346        }
 347
 348        dev_info(dev, "registered %s (type %d)\n", dev_name(&vdev->dev), id);
 349
 350out:
 351        return ret;
 352}
 353
 354/**
 355 * rproc_remove_virtio_dev() - remove an rproc-induced virtio device
 356 * @rvdev: the remote vdev
 357 *
 358 * This function unregisters an existing virtio device.
 359 */
 360void rproc_remove_virtio_dev(struct rproc_vdev *rvdev)
 361{
 362        unregister_virtio_device(&rvdev->vdev);
 363}
 364