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