LCOV - code coverage report
Current view: top level - drivers/remoteproc - remoteproc_virtio.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 0 172 0.0 %
Date: 2022-04-01 13:59:58 Functions: 0 15 0.0 %
Branches: 0 60 0.0 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0-only
       2                 :            : /*
       3                 :            :  * Remote processor messaging transport (OMAP platform-specific bits)
       4                 :            :  *
       5                 :            :  * Copyright (C) 2011 Texas Instruments, Inc.
       6                 :            :  * Copyright (C) 2011 Google, Inc.
       7                 :            :  *
       8                 :            :  * Ohad Ben-Cohen <ohad@wizery.com>
       9                 :            :  * Brian Swetland <swetland@google.com>
      10                 :            :  */
      11                 :            : 
      12                 :            : #include <linux/dma-mapping.h>
      13                 :            : #include <linux/export.h>
      14                 :            : #include <linux/of_reserved_mem.h>
      15                 :            : #include <linux/remoteproc.h>
      16                 :            : #include <linux/virtio.h>
      17                 :            : #include <linux/virtio_config.h>
      18                 :            : #include <linux/virtio_ids.h>
      19                 :            : #include <linux/virtio_ring.h>
      20                 :            : #include <linux/err.h>
      21                 :            : #include <linux/kref.h>
      22                 :            : #include <linux/slab.h>
      23                 :            : 
      24                 :            : #include "remoteproc_internal.h"
      25                 :            : 
      26                 :            : /* kick the remote processor, and let it know which virtqueue to poke at */
      27                 :          0 : static bool rproc_virtio_notify(struct virtqueue *vq)
      28                 :            : {
      29                 :          0 :         struct rproc_vring *rvring = vq->priv;
      30                 :          0 :         struct rproc *rproc = rvring->rvdev->rproc;
      31                 :          0 :         int notifyid = rvring->notifyid;
      32                 :            : 
      33                 :          0 :         dev_dbg(&rproc->dev, "kicking vq index: %d\n", notifyid);
      34                 :            : 
      35                 :          0 :         rproc->ops->kick(rproc, notifyid);
      36                 :          0 :         return true;
      37                 :            : }
      38                 :            : 
      39                 :            : /**
      40                 :            :  * rproc_vq_interrupt() - tell remoteproc that a virtqueue is interrupted
      41                 :            :  * @rproc: handle to the remote processor
      42                 :            :  * @notifyid: index of the signalled virtqueue (unique per this @rproc)
      43                 :            :  *
      44                 :            :  * This function should be called by the platform-specific rproc driver,
      45                 :            :  * when the remote processor signals that a specific virtqueue has pending
      46                 :            :  * messages available.
      47                 :            :  *
      48                 :            :  * Returns IRQ_NONE if no message was found in the @notifyid virtqueue,
      49                 :            :  * and otherwise returns IRQ_HANDLED.
      50                 :            :  */
      51                 :          0 : irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int notifyid)
      52                 :            : {
      53                 :          0 :         struct rproc_vring *rvring;
      54                 :            : 
      55                 :          0 :         dev_dbg(&rproc->dev, "vq index %d is interrupted\n", notifyid);
      56                 :            : 
      57                 :          0 :         rvring = idr_find(&rproc->notifyids, notifyid);
      58   [ #  #  #  # ]:          0 :         if (!rvring || !rvring->vq)
      59                 :            :                 return IRQ_NONE;
      60                 :            : 
      61                 :          0 :         return vring_interrupt(0, rvring->vq);
      62                 :            : }
      63                 :            : EXPORT_SYMBOL(rproc_vq_interrupt);
      64                 :            : 
      65                 :          0 : static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
      66                 :            :                                     unsigned int id,
      67                 :            :                                     void (*callback)(struct virtqueue *vq),
      68                 :            :                                     const char *name, bool ctx)
      69                 :            : {
      70         [ #  # ]:          0 :         struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
      71         [ #  # ]:          0 :         struct rproc *rproc = vdev_to_rproc(vdev);
      72                 :          0 :         struct device *dev = &rproc->dev;
      73                 :          0 :         struct rproc_mem_entry *mem;
      74                 :          0 :         struct rproc_vring *rvring;
      75                 :          0 :         struct fw_rsc_vdev *rsc;
      76                 :          0 :         struct virtqueue *vq;
      77                 :          0 :         void *addr;
      78                 :          0 :         int len, size;
      79                 :            : 
      80                 :            :         /* we're temporarily limited to two virtqueues per rvdev */
      81         [ #  # ]:          0 :         if (id >= ARRAY_SIZE(rvdev->vring))
      82                 :            :                 return ERR_PTR(-EINVAL);
      83                 :            : 
      84         [ #  # ]:          0 :         if (!name)
      85                 :            :                 return NULL;
      86                 :            : 
      87                 :            :         /* Search allocated memory region by name */
      88                 :          0 :         mem = rproc_find_carveout_by_name(rproc, "vdev%dvring%d", rvdev->index,
      89                 :            :                                           id);
      90   [ #  #  #  # ]:          0 :         if (!mem || !mem->va)
      91                 :            :                 return ERR_PTR(-ENOMEM);
      92                 :            : 
      93                 :          0 :         rvring = &rvdev->vring[id];
      94                 :          0 :         addr = mem->va;
      95                 :          0 :         len = rvring->len;
      96                 :            : 
      97                 :            :         /* zero vring */
      98                 :          0 :         size = vring_size(len, rvring->align);
      99                 :          0 :         memset(addr, 0, size);
     100                 :            : 
     101                 :          0 :         dev_dbg(dev, "vring%d: va %pK qsz %d notifyid %d\n",
     102                 :            :                 id, addr, len, rvring->notifyid);
     103                 :            : 
     104                 :            :         /*
     105                 :            :          * Create the new vq, and tell virtio we're not interested in
     106                 :            :          * the 'weak' smp barriers, since we're talking with a real device.
     107                 :            :          */
     108                 :          0 :         vq = vring_new_virtqueue(id, len, rvring->align, vdev, false, ctx,
     109                 :            :                                  addr, rproc_virtio_notify, callback, name);
     110         [ #  # ]:          0 :         if (!vq) {
     111                 :          0 :                 dev_err(dev, "vring_new_virtqueue %s failed\n", name);
     112                 :          0 :                 rproc_free_vring(rvring);
     113                 :          0 :                 return ERR_PTR(-ENOMEM);
     114                 :            :         }
     115                 :            : 
     116                 :          0 :         rvring->vq = vq;
     117                 :          0 :         vq->priv = rvring;
     118                 :            : 
     119                 :            :         /* Update vring in resource table */
     120                 :          0 :         rsc = (void *)rproc->table_ptr + rvdev->rsc_offset;
     121                 :          0 :         rsc->vring[id].da = mem->da;
     122                 :            : 
     123                 :          0 :         return vq;
     124                 :            : }
     125                 :            : 
     126                 :          0 : static void __rproc_virtio_del_vqs(struct virtio_device *vdev)
     127                 :            : {
     128                 :          0 :         struct virtqueue *vq, *n;
     129                 :          0 :         struct rproc_vring *rvring;
     130                 :            : 
     131   [ #  #  #  # ]:          0 :         list_for_each_entry_safe(vq, n, &vdev->vqs, list) {
     132                 :          0 :                 rvring = vq->priv;
     133                 :          0 :                 rvring->vq = NULL;
     134                 :          0 :                 vring_del_virtqueue(vq);
     135                 :            :         }
     136                 :            : }
     137                 :            : 
     138                 :          0 : static void rproc_virtio_del_vqs(struct virtio_device *vdev)
     139                 :            : {
     140                 :          0 :         __rproc_virtio_del_vqs(vdev);
     141                 :          0 : }
     142                 :            : 
     143                 :          0 : static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned int nvqs,
     144                 :            :                                  struct virtqueue *vqs[],
     145                 :            :                                  vq_callback_t *callbacks[],
     146                 :            :                                  const char * const names[],
     147                 :            :                                  const bool * ctx,
     148                 :            :                                  struct irq_affinity *desc)
     149                 :            : {
     150                 :          0 :         int i, ret, queue_idx = 0;
     151                 :            : 
     152         [ #  # ]:          0 :         for (i = 0; i < nvqs; ++i) {
     153         [ #  # ]:          0 :                 if (!names[i]) {
     154                 :          0 :                         vqs[i] = NULL;
     155                 :          0 :                         continue;
     156                 :            :                 }
     157                 :            : 
     158                 :          0 :                 vqs[i] = rp_find_vq(vdev, queue_idx++, callbacks[i], names[i],
     159   [ #  #  #  # ]:          0 :                                     ctx ? ctx[i] : false);
     160         [ #  # ]:          0 :                 if (IS_ERR(vqs[i])) {
     161                 :          0 :                         ret = PTR_ERR(vqs[i]);
     162                 :          0 :                         goto error;
     163                 :            :                 }
     164                 :            :         }
     165                 :            : 
     166                 :            :         return 0;
     167                 :            : 
     168                 :            : error:
     169                 :          0 :         __rproc_virtio_del_vqs(vdev);
     170                 :            :         return ret;
     171                 :            : }
     172                 :            : 
     173                 :          0 : static u8 rproc_virtio_get_status(struct virtio_device *vdev)
     174                 :            : {
     175                 :          0 :         struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
     176                 :          0 :         struct fw_rsc_vdev *rsc;
     177                 :            : 
     178                 :          0 :         rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
     179                 :            : 
     180                 :          0 :         return rsc->status;
     181                 :            : }
     182                 :            : 
     183                 :          0 : static void rproc_virtio_set_status(struct virtio_device *vdev, u8 status)
     184                 :            : {
     185                 :          0 :         struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
     186                 :          0 :         struct fw_rsc_vdev *rsc;
     187                 :            : 
     188                 :          0 :         rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
     189                 :            : 
     190                 :          0 :         rsc->status = status;
     191                 :          0 :         dev_dbg(&vdev->dev, "status: %d\n", status);
     192                 :          0 : }
     193                 :            : 
     194                 :          0 : static void rproc_virtio_reset(struct virtio_device *vdev)
     195                 :            : {
     196                 :          0 :         struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
     197                 :          0 :         struct fw_rsc_vdev *rsc;
     198                 :            : 
     199                 :          0 :         rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
     200                 :            : 
     201                 :          0 :         rsc->status = 0;
     202                 :          0 :         dev_dbg(&vdev->dev, "reset !\n");
     203                 :          0 : }
     204                 :            : 
     205                 :            : /* provide the vdev features as retrieved from the firmware */
     206                 :          0 : static u64 rproc_virtio_get_features(struct virtio_device *vdev)
     207                 :            : {
     208                 :          0 :         struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
     209                 :          0 :         struct fw_rsc_vdev *rsc;
     210                 :            : 
     211                 :          0 :         rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
     212                 :            : 
     213                 :          0 :         return rsc->dfeatures;
     214                 :            : }
     215                 :            : 
     216                 :          0 : static void rproc_transport_features(struct virtio_device *vdev)
     217                 :            : {
     218                 :            :         /*
     219                 :            :          * Packed ring isn't enabled on remoteproc for now,
     220                 :            :          * because remoteproc uses vring_new_virtqueue() which
     221                 :            :          * creates virtio rings on preallocated memory.
     222                 :            :          */
     223                 :          0 :         __virtio_clear_bit(vdev, VIRTIO_F_RING_PACKED);
     224                 :            : }
     225                 :            : 
     226                 :          0 : static int rproc_virtio_finalize_features(struct virtio_device *vdev)
     227                 :            : {
     228                 :          0 :         struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
     229                 :          0 :         struct fw_rsc_vdev *rsc;
     230                 :            : 
     231                 :          0 :         rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
     232                 :            : 
     233                 :            :         /* Give virtio_ring a chance to accept features */
     234                 :          0 :         vring_transport_features(vdev);
     235                 :            : 
     236                 :            :         /* Give virtio_rproc a chance to accept features. */
     237         [ #  # ]:          0 :         rproc_transport_features(vdev);
     238                 :            : 
     239                 :            :         /* Make sure we don't have any features > 32 bits! */
     240         [ #  # ]:          0 :         BUG_ON((u32)vdev->features != vdev->features);
     241                 :            : 
     242                 :            :         /*
     243                 :            :          * Remember the finalized features of our vdev, and provide it
     244                 :            :          * to the remote processor once it is powered on.
     245                 :            :          */
     246                 :          0 :         rsc->gfeatures = vdev->features;
     247                 :            : 
     248                 :          0 :         return 0;
     249                 :            : }
     250                 :            : 
     251                 :          0 : static void rproc_virtio_get(struct virtio_device *vdev, unsigned int offset,
     252                 :            :                              void *buf, unsigned int len)
     253                 :            : {
     254         [ #  # ]:          0 :         struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
     255                 :          0 :         struct fw_rsc_vdev *rsc;
     256                 :          0 :         void *cfg;
     257                 :            : 
     258                 :          0 :         rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
     259                 :          0 :         cfg = &rsc->vring[rsc->num_of_vrings];
     260                 :            : 
     261   [ #  #  #  # ]:          0 :         if (offset + len > rsc->config_len || offset + len < len) {
     262                 :          0 :                 dev_err(&vdev->dev, "rproc_virtio_get: access out of bounds\n");
     263                 :          0 :                 return;
     264                 :            :         }
     265                 :            : 
     266                 :          0 :         memcpy(buf, cfg + offset, len);
     267                 :            : }
     268                 :            : 
     269                 :          0 : static void rproc_virtio_set(struct virtio_device *vdev, unsigned int offset,
     270                 :            :                              const void *buf, unsigned int len)
     271                 :            : {
     272         [ #  # ]:          0 :         struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
     273                 :          0 :         struct fw_rsc_vdev *rsc;
     274                 :          0 :         void *cfg;
     275                 :            : 
     276                 :          0 :         rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
     277                 :          0 :         cfg = &rsc->vring[rsc->num_of_vrings];
     278                 :            : 
     279   [ #  #  #  # ]:          0 :         if (offset + len > rsc->config_len || offset + len < len) {
     280                 :          0 :                 dev_err(&vdev->dev, "rproc_virtio_set: access out of bounds\n");
     281                 :          0 :                 return;
     282                 :            :         }
     283                 :            : 
     284                 :          0 :         memcpy(cfg + offset, buf, len);
     285                 :            : }
     286                 :            : 
     287                 :            : static const struct virtio_config_ops rproc_virtio_config_ops = {
     288                 :            :         .get_features   = rproc_virtio_get_features,
     289                 :            :         .finalize_features = rproc_virtio_finalize_features,
     290                 :            :         .find_vqs       = rproc_virtio_find_vqs,
     291                 :            :         .del_vqs        = rproc_virtio_del_vqs,
     292                 :            :         .reset          = rproc_virtio_reset,
     293                 :            :         .set_status     = rproc_virtio_set_status,
     294                 :            :         .get_status     = rproc_virtio_get_status,
     295                 :            :         .get            = rproc_virtio_get,
     296                 :            :         .set            = rproc_virtio_set,
     297                 :            : };
     298                 :            : 
     299                 :            : /*
     300                 :            :  * This function is called whenever vdev is released, and is responsible
     301                 :            :  * to decrement the remote processor's refcount which was taken when vdev was
     302                 :            :  * added.
     303                 :            :  *
     304                 :            :  * Never call this function directly; it will be called by the driver
     305                 :            :  * core when needed.
     306                 :            :  */
     307                 :          0 : static void rproc_virtio_dev_release(struct device *dev)
     308                 :            : {
     309                 :          0 :         struct virtio_device *vdev = dev_to_virtio(dev);
     310                 :          0 :         struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
     311                 :          0 :         struct rproc *rproc = vdev_to_rproc(vdev);
     312                 :            : 
     313                 :          0 :         kfree(vdev);
     314                 :            : 
     315                 :          0 :         kref_put(&rvdev->refcount, rproc_vdev_release);
     316                 :            : 
     317                 :          0 :         put_device(&rproc->dev);
     318                 :          0 : }
     319                 :            : 
     320                 :            : /**
     321                 :            :  * rproc_add_virtio_dev() - register an rproc-induced virtio device
     322                 :            :  * @rvdev: the remote vdev
     323                 :            :  *
     324                 :            :  * This function registers a virtio device. This vdev's partent is
     325                 :            :  * the rproc device.
     326                 :            :  *
     327                 :            :  * Returns 0 on success or an appropriate error value otherwise.
     328                 :            :  */
     329                 :          0 : int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
     330                 :            : {
     331                 :          0 :         struct rproc *rproc = rvdev->rproc;
     332                 :          0 :         struct device *dev = &rvdev->dev;
     333                 :          0 :         struct virtio_device *vdev;
     334                 :          0 :         struct rproc_mem_entry *mem;
     335                 :          0 :         int ret;
     336                 :            : 
     337                 :            :         /* Try to find dedicated vdev buffer carveout */
     338                 :          0 :         mem = rproc_find_carveout_by_name(rproc, "vdev%dbuffer", rvdev->index);
     339         [ #  # ]:          0 :         if (mem) {
     340                 :          0 :                 phys_addr_t pa;
     341                 :            : 
     342         [ #  # ]:          0 :                 if (mem->of_resm_idx != -1) {
     343                 :          0 :                         struct device_node *np = rproc->dev.parent->of_node;
     344                 :            : 
     345                 :            :                         /* Associate reserved memory to vdev device */
     346                 :          0 :                         ret = of_reserved_mem_device_init_by_idx(dev, np,
     347                 :            :                                                                  mem->of_resm_idx);
     348                 :          0 :                         if (ret) {
     349                 :          0 :                                 dev_err(dev, "Can't associate reserved memory\n");
     350                 :          0 :                                 goto out;
     351                 :            :                         }
     352                 :            :                 } else {
     353         [ #  # ]:          0 :                         if (mem->va) {
     354                 :          0 :                                 dev_warn(dev, "vdev %d buffer already mapped\n",
     355                 :            :                                          rvdev->index);
     356                 :          0 :                                 pa = rproc_va_to_pa(mem->va);
     357                 :            :                         } else {
     358                 :            :                                 /* Use dma address as carveout no memmapped yet */
     359                 :            :                                 pa = (phys_addr_t)mem->dma;
     360                 :            :                         }
     361                 :            : 
     362                 :            :                         /* Associate vdev buffer memory pool to vdev subdev */
     363                 :          0 :                         ret = dma_declare_coherent_memory(dev, pa,
     364                 :            :                                                            mem->da,
     365                 :            :                                                            mem->len);
     366                 :          0 :                         if (ret < 0) {
     367                 :          0 :                                 dev_err(dev, "Failed to associate buffer\n");
     368                 :          0 :                                 goto out;
     369                 :            :                         }
     370                 :            :                 }
     371                 :            :         }
     372                 :            : 
     373                 :            :         /* Allocate virtio device */
     374                 :          0 :         vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
     375         [ #  # ]:          0 :         if (!vdev) {
     376                 :          0 :                 ret = -ENOMEM;
     377                 :          0 :                 goto out;
     378                 :            :         }
     379                 :          0 :         vdev->id.device      = id,
     380                 :          0 :         vdev->config = &rproc_virtio_config_ops,
     381                 :          0 :         vdev->dev.parent = dev;
     382                 :          0 :         vdev->dev.release = rproc_virtio_dev_release;
     383                 :            : 
     384                 :            :         /*
     385                 :            :          * We're indirectly making a non-temporary copy of the rproc pointer
     386                 :            :          * here, because drivers probed with this vdev will indirectly
     387                 :            :          * access the wrapping rproc.
     388                 :            :          *
     389                 :            :          * Therefore we must increment the rproc refcount here, and decrement
     390                 :            :          * it _only_ when the vdev is released.
     391                 :            :          */
     392                 :          0 :         get_device(&rproc->dev);
     393                 :            : 
     394                 :            :         /* Reference the vdev and vring allocations */
     395                 :          0 :         kref_get(&rvdev->refcount);
     396                 :            : 
     397                 :          0 :         ret = register_virtio_device(vdev);
     398         [ #  # ]:          0 :         if (ret) {
     399                 :          0 :                 put_device(&vdev->dev);
     400                 :          0 :                 dev_err(dev, "failed to register vdev: %d\n", ret);
     401                 :          0 :                 goto out;
     402                 :            :         }
     403                 :            : 
     404         [ #  # ]:          0 :         dev_info(dev, "registered %s (type %d)\n", dev_name(&vdev->dev), id);
     405                 :            : 
     406                 :          0 : out:
     407                 :          0 :         return ret;
     408                 :            : }
     409                 :            : 
     410                 :            : /**
     411                 :            :  * rproc_remove_virtio_dev() - remove an rproc-induced virtio device
     412                 :            :  * @dev: the virtio device
     413                 :            :  * @data: must be null
     414                 :            :  *
     415                 :            :  * This function unregisters an existing virtio device.
     416                 :            :  */
     417                 :          0 : int rproc_remove_virtio_dev(struct device *dev, void *data)
     418                 :            : {
     419                 :          0 :         struct virtio_device *vdev = dev_to_virtio(dev);
     420                 :            : 
     421                 :          0 :         unregister_virtio_device(vdev);
     422                 :          0 :         return 0;
     423                 :            : }

Generated by: LCOV version 1.14