]> rtime.felk.cvut.cz Git - linux-imx.git/commitdiff
Merge tag 'remoteproc-3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 7 May 2013 21:04:56 +0000 (14:04 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 7 May 2013 21:04:56 +0000 (14:04 -0700)
Pull remoteproc update from Ohad Ben-Cohen:

 - Some refactoring, cleanups and small improvements from Sjur
   Brændeland.  The improvements are mainly about better supporting
   varios virtio properties (such as virtio's config space, status and
   features).  I now see that I messed up while commiting one of Sjur's
   patches and erroneously put myself as the author, as well as letting
   a nasty typo sneak in.  I will not fix this in order to avoid
   rebasing the patches.  Sjur - sorry!

 - A new remoteproc driver for OMAP-L13x (technically a DaVinci
   platform) from Robert Tivy.

 - Extend OMAP support to OMAP5 as well, from Vincent StehlĂ©.

 - Fix Kconfig VIRTUALIZATION dependency, from Suman Anna (a
   non-critical fix which arrived late during the rc cycle).

* tag 'remoteproc-3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad/remoteproc:
  remoteproc: fix kconfig dependencies for VIRTIO
  remoteproc/davinci: add a remoteproc driver for OMAP-L13x DSP
  remoteproc: support default firmware name in rproc_alloc()
  remoteproc/omap: support OMAP5 too
  remoteproc: set vring addresses in resource table
  remoteproc: support virtio config space.
  remoteproc: perserve resource table data
  remoteproc: calculate max_notifyid by counting vrings
  remoteproc: code cleanup of resource parsing
  remoteproc: parse STE-firmware and find resource table address
  remoteproc: add find_loaded_rsc_table firmware ops
  remoteproc: refactor rproc_elf_find_rsc_table()

1  2 
drivers/remoteproc/Kconfig
drivers/remoteproc/remoteproc_core.c
drivers/remoteproc/ste_modem_rproc.c

index c6d77e20622c45f54e17e7063e3e498f781739c6,1ceb4867c97ae8a9b5700b5b6cfd4ac5d4a33f27..d4d377c40ec96585126f474238040d47c9e79c2e
@@@ -4,13 -4,15 +4,15 @@@ menu "Remoteproc drivers
  config REMOTEPROC
        tristate
        depends on HAS_DMA
 -      select FW_CONFIG
+       select CRC32
 +      select FW_LOADER
        select VIRTIO
+       select VIRTUALIZATION
  
  config OMAP_REMOTEPROC
        tristate "OMAP remoteproc support"
        depends on HAS_DMA
-       depends on ARCH_OMAP4
+       depends on ARCH_OMAP4 || SOC_OMAP5
        depends on OMAP_IOMMU
        depends on OMAP_MBOX_FWK
        select REMOTEPROC
@@@ -38,4 -40,27 +40,27 @@@ config STE_MODEM_RPRO
          This can be either built-in or a loadable module.
          If unsure say N.
  
+ config DA8XX_REMOTEPROC
+       tristate "DA8xx/OMAP-L13x remoteproc support"
+       depends on ARCH_DAVINCI_DA8XX
+       select CMA
+       select REMOTEPROC
+       select RPMSG
+       help
+         Say y here to support DA8xx/OMAP-L13x remote processors via the
+         remote processor framework.
+         You want to say y here in order to enable AMP
+         use-cases to run on your platform (multimedia codecs are
+         offloaded to remote DSP processors using this framework).
+         This module controls the name of the firmware file that gets
+         loaded on the DSP.  This file must reside in the /lib/firmware
+         directory.  It can be specified via the module parameter
+         da8xx_fw_name=<filename>, and if not specified will default to
+         "rproc-dsp-fw".
+         It's safe to say n here if you're not interested in multimedia
+         offloading.
  endmenu
index 814af5ab8a72d2d5e979855bbffc86b487fe41f6,56a0f8d6855b11699fc86c63bd7dcb4421cf3332..022dc635d01e4935ee84c0378e0d606e94028a39
@@@ -37,6 -37,7 +37,7 @@@
  #include <linux/iommu.h>
  #include <linux/idr.h>
  #include <linux/elf.h>
+ #include <linux/crc32.h>
  #include <linux/virtio_ids.h>
  #include <linux/virtio_ring.h>
  #include <asm/byteorder.h>
@@@ -45,7 -46,8 +46,8 @@@
  
  typedef int (*rproc_handle_resources_t)(struct rproc *rproc,
                                struct resource_table *table, int len);
- typedef int (*rproc_handle_resource_t)(struct rproc *rproc, void *, int avail);
+ typedef int (*rproc_handle_resource_t)(struct rproc *rproc,
+                                void *, int offset, int avail);
  
  /* Unique indices for remoteproc devices */
  static DEFINE_IDA(rproc_dev_index);
@@@ -59,7 -61,7 +61,7 @@@ static const char *rproc_crash_to_strin
  {
        if (type < ARRAY_SIZE(rproc_crash_names))
                return rproc_crash_names[type];
 -      return "unkown";
 +      return "unknown";
  }
  
  /*
@@@ -192,6 -194,7 +194,7 @@@ int rproc_alloc_vring(struct rproc_vde
        struct rproc *rproc = rvdev->rproc;
        struct device *dev = &rproc->dev;
        struct rproc_vring *rvring = &rvdev->vring[i];
+       struct fw_rsc_vdev *rsc;
        dma_addr_t dma;
        void *va;
        int ret, size, notifyid;
        /*
         * Allocate non-cacheable memory for the vring. In the future
         * this call will also configure the IOMMU for us
-        * TODO: let the rproc know the da of this vring
         */
        va = dma_alloc_coherent(dev->parent, size, &dma, GFP_KERNEL);
        if (!va) {
        /*
         * Assign an rproc-wide unique index for this vring
         * TODO: assign a notifyid for rvdev updates as well
-        * TODO: let the rproc know the notifyid of this vring
         * TODO: support predefined notifyids (via resource table)
         */
        ret = idr_alloc(&rproc->notifyids, rvring, 0, 0, GFP_KERNEL);
 -      if (ret) {
 +      if (ret < 0) {
                dev_err(dev, "idr_alloc failed: %d\n", ret);
                dma_free_coherent(dev->parent, size, va, dma);
                return ret;
        }
        notifyid = ret;
  
-       /* Store largest notifyid */
-       rproc->max_notifyid = max(rproc->max_notifyid, notifyid);
        dev_dbg(dev, "vring%d: va %p dma %llx size %x idr %d\n", i, va,
                                (unsigned long long)dma, size, notifyid);
  
        rvring->dma = dma;
        rvring->notifyid = notifyid;
  
+       /*
+        * Let the rproc know the notifyid and da of this vring.
+        * Not all platforms use dma_alloc_coherent to automatically
+        * set up the iommu. In this case the device address (da) will
+        * hold the physical address and not the device address.
+        */
+       rsc = (void *)rproc->table_ptr + rvdev->rsc_offset;
+       rsc->vring[i].da = dma;
+       rsc->vring[i].notifyid = notifyid;
        return 0;
  }
  
@@@ -268,25 -275,20 +275,20 @@@ rproc_parse_vring(struct rproc_vdev *rv
        return 0;
  }
  
- static int rproc_max_notifyid(int id, void *p, void *data)
- {
-       int *maxid = data;
-       *maxid = max(*maxid, id);
-       return 0;
- }
  void rproc_free_vring(struct rproc_vring *rvring)
  {
        int size = PAGE_ALIGN(vring_size(rvring->len, rvring->align));
        struct rproc *rproc = rvring->rvdev->rproc;
-       int maxid = 0;
+       int idx = rvring->rvdev->vring - rvring;
+       struct fw_rsc_vdev *rsc;
  
        dma_free_coherent(rproc->dev.parent, size, rvring->va, rvring->dma);
        idr_remove(&rproc->notifyids, rvring->notifyid);
  
-       /* Find the largest remaining notifyid */
-       idr_for_each(&rproc->notifyids, rproc_max_notifyid, &maxid);
-       rproc->max_notifyid = maxid;
+       /* reset resource entry info */
+       rsc = (void *)rproc->table_ptr + rvring->rvdev->rsc_offset;
+       rsc->vring[idx].da = 0;
+       rsc->vring[idx].notifyid = -1;
  }
  
  /**
   * Returns 0 on success, or an appropriate error code otherwise
   */
  static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
-                                                               int avail)
+                                                       int offset, int avail)
  {
        struct device *dev = &rproc->dev;
        struct rproc_vdev *rvdev;
                        goto free_rvdev;
        }
  
-       /* remember the device features */
-       rvdev->dfeatures = rsc->dfeatures;
+       /* remember the resource offset*/
+       rvdev->rsc_offset = offset;
  
        list_add_tail(&rvdev->node, &rproc->rvdevs);
  
        /* it is now safe to add the virtio device */
        ret = rproc_add_virtio_dev(rvdev, rsc->id);
        if (ret)
 -              goto free_rvdev;
 +              goto remove_rvdev;
  
        return 0;
  
 +remove_rvdev:
 +      list_del(&rvdev->node);
  free_rvdev:
        kfree(rvdev);
        return ret;
   * Returns 0 on success, or an appropriate error code otherwise
   */
  static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
-                                                               int avail)
+                                                       int offset, int avail)
  {
        struct rproc_mem_entry *trace;
        struct device *dev = &rproc->dev;
   * are outside those ranges.
   */
  static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc,
-                                                               int avail)
+                                                       int offset, int avail)
  {
        struct rproc_mem_entry *mapping;
        struct device *dev = &rproc->dev;
@@@ -549,7 -549,9 +551,9 @@@ out
   * pressure is important; it may have a substantial impact on performance.
   */
  static int rproc_handle_carveout(struct rproc *rproc,
-                               struct fw_rsc_carveout *rsc, int avail)
+                                               struct fw_rsc_carveout *rsc,
+                                               int offset, int avail)
  {
        struct rproc_mem_entry *carveout, *mapping;
        struct device *dev = &rproc->dev;
@@@ -671,28 -673,45 +675,45 @@@ free_carv
        return ret;
  }
  
+ static int rproc_count_vrings(struct rproc *rproc, struct fw_rsc_vdev *rsc,
+                             int offset, int avail)
+ {
+       /* Summarize the number of notification IDs */
+       rproc->max_notifyid += rsc->num_of_vrings;
+       return 0;
+ }
  /*
   * A lookup table for resource handlers. The indices are defined in
   * enum fw_resource_type.
   */
- static rproc_handle_resource_t rproc_handle_rsc[] = {
+ static rproc_handle_resource_t rproc_loading_handlers[RSC_LAST] = {
        [RSC_CARVEOUT] = (rproc_handle_resource_t)rproc_handle_carveout,
        [RSC_DEVMEM] = (rproc_handle_resource_t)rproc_handle_devmem,
        [RSC_TRACE] = (rproc_handle_resource_t)rproc_handle_trace,
        [RSC_VDEV] = NULL, /* VDEVs were handled upon registrarion */
  };
  
+ static rproc_handle_resource_t rproc_vdev_handler[RSC_LAST] = {
+       [RSC_VDEV] = (rproc_handle_resource_t)rproc_handle_vdev,
+ };
+ static rproc_handle_resource_t rproc_count_vrings_handler[RSC_LAST] = {
+       [RSC_VDEV] = (rproc_handle_resource_t)rproc_count_vrings,
+ };
  /* handle firmware resource entries before booting the remote processor */
- static int
rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len)
+ static int rproc_handle_resources(struct rproc *rproc, int len,
                                rproc_handle_resource_t handlers[RSC_LAST])
  {
        struct device *dev = &rproc->dev;
        rproc_handle_resource_t handler;
        int ret = 0, i;
  
-       for (i = 0; i < table->num; i++) {
-               int offset = table->offset[i];
-               struct fw_rsc_hdr *hdr = (void *)table + offset;
+       for (i = 0; i < rproc->table_ptr->num; i++) {
+               int offset = rproc->table_ptr->offset[i];
+               struct fw_rsc_hdr *hdr = (void *)rproc->table_ptr + offset;
                int avail = len - offset - sizeof(*hdr);
                void *rsc = (void *)hdr + sizeof(*hdr);
  
                        continue;
                }
  
-               handler = rproc_handle_rsc[hdr->type];
+               handler = handlers[hdr->type];
                if (!handler)
                        continue;
  
-               ret = handler(rproc, rsc, avail);
-               if (ret)
-                       break;
-       }
-       return ret;
- }
- /* handle firmware resource entries while registering the remote processor */
- static int
- rproc_handle_virtio_rsc(struct rproc *rproc, struct resource_table *table, int len)
- {
-       struct device *dev = &rproc->dev;
-       int ret = 0, i;
-       for (i = 0; i < table->num; i++) {
-               int offset = table->offset[i];
-               struct fw_rsc_hdr *hdr = (void *)table + offset;
-               int avail = len - offset - sizeof(*hdr);
-               struct fw_rsc_vdev *vrsc;
-               /* make sure table isn't truncated */
-               if (avail < 0) {
-                       dev_err(dev, "rsc table is truncated\n");
-                       return -EINVAL;
-               }
-               dev_dbg(dev, "%s: rsc type %d\n", __func__, hdr->type);
-               if (hdr->type != RSC_VDEV)
-                       continue;
-               vrsc = (struct fw_rsc_vdev *)hdr->data;
-               ret = rproc_handle_vdev(rproc, vrsc, avail);
+               ret = handler(rproc, rsc, offset + sizeof(*hdr), avail);
                if (ret)
                        break;
        }
@@@ -805,9 -790,12 +792,12 @@@ static int rproc_fw_boot(struct rproc *
  {
        struct device *dev = &rproc->dev;
        const char *name = rproc->firmware;
-       struct resource_table *table;
+       struct resource_table *table, *loaded_table;
        int ret, tablesz;
  
+       if (!rproc->table_ptr)
+               return -ENOMEM;
        ret = rproc_fw_sanity_check(rproc, fw);
        if (ret)
                return ret;
                goto clean_up;
        }
  
+       /* Verify that resource table in loaded fw is unchanged */
+       if (rproc->table_csum != crc32(0, table, tablesz)) {
+               dev_err(dev, "resource checksum failed, fw changed?\n");
+               ret = -EINVAL;
+               goto clean_up;
+       }
        /* handle fw resources which are required to boot rproc */
-       ret = rproc_handle_boot_rsc(rproc, table, tablesz);
+       ret = rproc_handle_resources(rproc, tablesz, rproc_loading_handlers);
        if (ret) {
                dev_err(dev, "Failed to process resources: %d\n", ret);
                goto clean_up;
                goto clean_up;
        }
  
+       /*
+        * The starting device has been given the rproc->cached_table as the
+        * resource table. The address of the vring along with the other
+        * allocated resources (carveouts etc) is stored in cached_table.
+        * In order to pass this information to the remote device we must
+        * copy this information to device memory.
+        */
+       loaded_table = rproc_find_loaded_rsc_table(rproc, fw);
+       if (!loaded_table)
+               goto clean_up;
+       memcpy(loaded_table, rproc->cached_table, tablesz);
        /* power up the remote processor */
        ret = rproc->ops->start(rproc);
        if (ret) {
                goto clean_up;
        }
  
+       /*
+        * Update table_ptr so that all subsequent vring allocations and
+        * virtio fields manipulation update the actual loaded resource table
+        * in device memory.
+        */
+       rproc->table_ptr = loaded_table;
        rproc->state = RPROC_RUNNING;
  
        dev_info(dev, "remote processor %s is now up\n", rproc->name);
@@@ -888,11 -903,30 +905,30 @@@ static void rproc_fw_config_virtio(cons
        if (!table)
                goto out;
  
-       /* look for virtio devices and register them */
-       ret = rproc_handle_virtio_rsc(rproc, table, tablesz);
+       rproc->table_csum = crc32(0, table, tablesz);
+       /*
+        * Create a copy of the resource table. When a virtio device starts
+        * and calls vring_new_virtqueue() the address of the allocated vring
+        * will be stored in the cached_table. Before the device is started,
+        * cached_table will be copied into devic memory.
+        */
+       rproc->cached_table = kmalloc(tablesz, GFP_KERNEL);
+       if (!rproc->cached_table)
+               goto out;
+       memcpy(rproc->cached_table, table, tablesz);
+       rproc->table_ptr = rproc->cached_table;
+       /* count the number of notify-ids */
+       rproc->max_notifyid = -1;
+       ret = rproc_handle_resources(rproc, tablesz, rproc_count_vrings_handler);
        if (ret)
                goto out;
  
+       /* look for virtio devices and register them */
+       ret = rproc_handle_resources(rproc, tablesz, rproc_vdev_handler);
  out:
        release_firmware(fw);
        /* allow rproc_del() contexts, if any, to proceed */
@@@ -950,6 -984,9 +986,9 @@@ int rproc_trigger_recovery(struct rpro
        /* wait until there is no more rproc users */
        wait_for_completion(&rproc->crash_comp);
  
+       /* Free the copy of the resource table */
+       kfree(rproc->cached_table);
        return rproc_add_virtio_devices(rproc);
  }
  
@@@ -1105,6 -1142,9 +1144,9 @@@ void rproc_shutdown(struct rproc *rproc
  
        rproc_disable_iommu(rproc);
  
+       /* Give the next start a clean resource table */
+       rproc->table_ptr = rproc->cached_table;
        /* if in crash state, unlock crash handler */
        if (rproc->state == RPROC_CRASHED)
                complete_all(&rproc->crash_comp);
@@@ -1196,11 -1236,11 +1238,11 @@@ static struct device_type rproc_type = 
   * @dev: the underlying device
   * @name: name of this remote processor
   * @ops: platform-specific handlers (mainly start/stop)
-  * @firmware: name of firmware file to load
+  * @firmware: name of firmware file to load, can be NULL
   * @len: length of private data needed by the rproc driver (in bytes)
   *
   * Allocates a new remote processor handle, but does not register
-  * it yet.
+  * it yet. if @firmware is NULL, a default name is used.
   *
   * This function should be used by rproc implementations during initialization
   * of the remote processor.
@@@ -1219,19 -1259,39 +1261,39 @@@ struct rproc *rproc_alloc(struct devic
                                const char *firmware, int len)
  {
        struct rproc *rproc;
+       char *p, *template = "rproc-%s-fw";
+       int name_len = 0;
  
        if (!dev || !name || !ops)
                return NULL;
  
-       rproc = kzalloc(sizeof(struct rproc) + len, GFP_KERNEL);
+       if (!firmware)
+               /*
+                * Make room for default firmware name (minus %s plus '\0').
+                * If the caller didn't pass in a firmware name then
+                * construct a default name.  We're already glomming 'len'
+                * bytes onto the end of the struct rproc allocation, so do
+                * a few more for the default firmware name (but only if
+                * the caller doesn't pass one).
+                */
+               name_len = strlen(name) + strlen(template) - 2 + 1;
+       rproc = kzalloc(sizeof(struct rproc) + len + name_len, GFP_KERNEL);
        if (!rproc) {
                dev_err(dev, "%s: kzalloc failed\n", __func__);
                return NULL;
        }
  
+       if (!firmware) {
+               p = (char *)rproc + sizeof(struct rproc) + len;
+               snprintf(p, name_len, template, name);
+       } else {
+               p = (char *)firmware;
+       }
+       rproc->firmware = p;
        rproc->name = name;
        rproc->ops = ops;
-       rproc->firmware = firmware;
        rproc->priv = &rproc[1];
  
        device_initialize(&rproc->dev);
@@@ -1315,6 -1375,9 +1377,9 @@@ int rproc_del(struct rproc *rproc
        list_for_each_entry_safe(rvdev, tmp, &rproc->rvdevs, node)
                rproc_remove_virtio_dev(rvdev);
  
+       /* Free the copy of the resource table */
+       kfree(rproc->cached_table);
        device_del(&rproc->dev);
  
        return 0;
index fb95c4220052decb09117075d3a798dbbb6235b7,6fc2139481461ae1fc450688380fb16055dea247..1ec39a4c0b3edbd9a6c64ef95e22649ab6ca7de7
@@@ -64,26 -64,18 +64,18 @@@ static int sproc_load_segments(struct r
  }
  
  /* Find the entry for resource table in the Table of Content */
- static struct ste_toc_entry *sproc_find_rsc_entry(const struct firmware *fw)
+ static const struct ste_toc_entry *sproc_find_rsc_entry(const void *data)
  {
        int i;
-       struct ste_toc *toc;
-       if (!fw)
-               return NULL;
-       toc = (void *)fw->data;
+       const struct ste_toc *toc;
+       toc = data;
  
        /* Search the table for the resource table */
        for (i = 0; i < SPROC_MAX_TOC_ENTRIES &&
                    toc->table[i].start != 0xffffffff; i++) {
                if (!strncmp(toc->table[i].name, SPROC_RESOURCE_NAME,
-                            sizeof(toc->table[i].name))) {
-                       if (toc->table[i].start > fw->size)
-                               return NULL;
+                            sizeof(toc->table[i].name)))
                        return &toc->table[i];
-               }
        }
  
        return NULL;
@@@ -96,9 -88,12 +88,12 @@@ sproc_find_rsc_table(struct rproc *rpro
  {
        struct sproc *sproc = rproc->priv;
        struct resource_table *table;
-       struct ste_toc_entry *entry;
+       const struct ste_toc_entry *entry;
  
-       entry = sproc_find_rsc_entry(fw);
+       if (!fw)
+               return NULL;
+       entry = sproc_find_rsc_entry(fw->data);
        if (!entry) {
                sproc_err(sproc, "resource table not found in fw\n");
                return NULL;
        return table;
  }
  
+ /* Find the resource table inside the remote processor's firmware. */
+ static struct resource_table *
+ sproc_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw)
+ {
+       struct sproc *sproc = rproc->priv;
+       const struct ste_toc_entry *entry;
+       if (!fw || !sproc->fw_addr)
+               return NULL;
+       entry = sproc_find_rsc_entry(sproc->fw_addr);
+       if (!entry) {
+               sproc_err(sproc, "resource table not found in fw\n");
+               return NULL;
+       }
+       return sproc->fw_addr + entry->start;
+ }
  /* STE modem firmware handler operations */
  const struct rproc_fw_ops sproc_fw_ops = {
        .load = sproc_load_segments,
        .find_rsc_table = sproc_find_rsc_table,
+       .find_loaded_rsc_table = sproc_find_loaded_rsc_table,
  };
  
  /* Kick the modem with specified notification id */
@@@ -198,7 -213,7 +213,7 @@@ static int sproc_start(struct rproc *rp
        }
  
        /* Subscribe to notifications */
-       for (i = 0; i < rproc->max_notifyid; i++) {
+       for (i = 0; i <= rproc->max_notifyid; i++) {
                err = sproc->mdev->ops.kick_subscribe(sproc->mdev, i);
                if (err) {
                        sproc_err(sproc,
@@@ -240,8 -255,6 +255,8 @@@ static int sproc_drv_remove(struct plat
  
        /* Unregister as remoteproc device */
        rproc_del(sproc->rproc);
 +      dma_free_coherent(sproc->rproc->dev.parent, SPROC_FW_SIZE,
 +                        sproc->fw_addr, sproc->fw_dma_addr);
        rproc_put(sproc->rproc);
  
        mdev->drv_data = NULL;
@@@ -299,13 -312,10 +314,13 @@@ static int sproc_probe(struct platform_
        /* Register as a remoteproc device */
        err = rproc_add(rproc);
        if (err)
 -              goto free_rproc;
 +              goto free_mem;
  
        return 0;
  
 +free_mem:
 +      dma_free_coherent(rproc->dev.parent, SPROC_FW_SIZE,
 +                        sproc->fw_addr, sproc->fw_dma_addr);
  free_rproc:
        /* Reset device data upon error */
        mdev->drv_data = NULL;