Merge tag 'dmaengine-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul...
[linux-2.6-microblaze.git] / drivers / remoteproc / remoteproc_core.c
index ab15076..626a6b9 100644 (file)
@@ -189,13 +189,13 @@ EXPORT_SYMBOL(rproc_va_to_pa);
  * here the output of the DMA API for the carveouts, which should be more
  * correct.
  */
-void *rproc_da_to_va(struct rproc *rproc, u64 da, size_t len)
+void *rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
 {
        struct rproc_mem_entry *carveout;
        void *ptr = NULL;
 
        if (rproc->ops->da_to_va) {
-               ptr = rproc->ops->da_to_va(rproc, da, len);
+               ptr = rproc->ops->da_to_va(rproc, da, len, is_iomem);
                if (ptr)
                        goto out;
        }
@@ -217,6 +217,9 @@ void *rproc_da_to_va(struct rproc *rproc, u64 da, size_t len)
 
                ptr = carveout->va + offset;
 
+               if (is_iomem)
+                       *is_iomem = carveout->is_iomem;
+
                break;
        }
 
@@ -482,7 +485,7 @@ static int copy_dma_range_map(struct device *to, struct device *from)
 /**
  * rproc_handle_vdev() - handle a vdev fw resource
  * @rproc: the remote processor
- * @rsc: the vring resource descriptor
+ * @ptr: the vring resource descriptor
  * @offset: offset of the resource entry
  * @avail: size of available data (for sanity checking the image)
  *
@@ -507,9 +510,10 @@ static int copy_dma_range_map(struct device *to, struct device *from)
  *
  * Returns 0 on success, or an appropriate error code otherwise
  */
-static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
+static int rproc_handle_vdev(struct rproc *rproc, void *ptr,
                             int offset, int avail)
 {
+       struct fw_rsc_vdev *rsc = ptr;
        struct device *dev = &rproc->dev;
        struct rproc_vdev *rvdev;
        int i, ret;
@@ -627,7 +631,7 @@ void rproc_vdev_release(struct kref *ref)
 /**
  * rproc_handle_trace() - handle a shared trace buffer resource
  * @rproc: the remote processor
- * @rsc: the trace resource descriptor
+ * @ptr: the trace resource descriptor
  * @offset: offset of the resource entry
  * @avail: size of available data (for sanity checking the image)
  *
@@ -641,9 +645,10 @@ void rproc_vdev_release(struct kref *ref)
  *
  * Returns 0 on success, or an appropriate error code otherwise
  */
-static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
+static int rproc_handle_trace(struct rproc *rproc, void *ptr,
                              int offset, int avail)
 {
+       struct fw_rsc_trace *rsc = ptr;
        struct rproc_debug_trace *trace;
        struct device *dev = &rproc->dev;
        char name[15];
@@ -693,7 +698,7 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
 /**
  * rproc_handle_devmem() - handle devmem resource entry
  * @rproc: remote processor handle
- * @rsc: the devmem resource entry
+ * @ptr: the devmem resource entry
  * @offset: offset of the resource entry
  * @avail: size of available data (for sanity checking the image)
  *
@@ -716,9 +721,10 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
  * and not allow firmwares to request access to physical addresses that
  * are outside those ranges.
  */
-static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc,
+static int rproc_handle_devmem(struct rproc *rproc, void *ptr,
                               int offset, int avail)
 {
+       struct fw_rsc_devmem *rsc = ptr;
        struct rproc_mem_entry *mapping;
        struct device *dev = &rproc->dev;
        int ret;
@@ -896,7 +902,7 @@ static int rproc_release_carveout(struct rproc *rproc,
 /**
  * rproc_handle_carveout() - handle phys contig memory allocation requests
  * @rproc: rproc handle
- * @rsc: the resource entry
+ * @ptr: the resource entry
  * @offset: offset of the resource entry
  * @avail: size of available data (for image validation)
  *
@@ -913,9 +919,9 @@ static int rproc_release_carveout(struct rproc *rproc,
  * 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 offset, int avail)
+                                void *ptr, int offset, int avail)
 {
+       struct fw_rsc_carveout *rsc = ptr;
        struct rproc_mem_entry *carveout;
        struct device *dev = &rproc->dev;
 
@@ -1097,10 +1103,10 @@ EXPORT_SYMBOL(rproc_of_parse_firmware);
  * enum fw_resource_type.
  */
 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] = (rproc_handle_resource_t)rproc_handle_vdev,
+       [RSC_CARVEOUT] = rproc_handle_carveout,
+       [RSC_DEVMEM] = rproc_handle_devmem,
+       [RSC_TRACE] = rproc_handle_trace,
+       [RSC_VDEV] = rproc_handle_vdev,
 };
 
 /* handle firmware resource entries before booting the remote processor */
@@ -1416,7 +1422,7 @@ reset_table_ptr:
        return ret;
 }
 
-static int rproc_attach(struct rproc *rproc)
+static int __rproc_attach(struct rproc *rproc)
 {
        struct device *dev = &rproc->dev;
        int ret;
@@ -1444,7 +1450,7 @@ static int rproc_attach(struct rproc *rproc)
                goto stop_rproc;
        }
 
-       rproc->state = RPROC_RUNNING;
+       rproc->state = RPROC_ATTACHED;
 
        dev_info(dev, "remote processor %s is now attached\n", rproc->name);
 
@@ -1537,11 +1543,149 @@ disable_iommu:
        return ret;
 }
 
+static int rproc_set_rsc_table(struct rproc *rproc)
+{
+       struct resource_table *table_ptr;
+       struct device *dev = &rproc->dev;
+       size_t table_sz;
+       int ret;
+
+       table_ptr = rproc_get_loaded_rsc_table(rproc, &table_sz);
+       if (!table_ptr) {
+               /* Not having a resource table is acceptable */
+               return 0;
+       }
+
+       if (IS_ERR(table_ptr)) {
+               ret = PTR_ERR(table_ptr);
+               dev_err(dev, "can't load resource table: %d\n", ret);
+               return ret;
+       }
+
+       /*
+        * If it is possible to detach the remote processor, keep an untouched
+        * copy of the resource table.  That way we can start fresh again when
+        * the remote processor is re-attached, that is:
+        *
+        *      DETACHED -> ATTACHED -> DETACHED -> ATTACHED
+        *
+        * Free'd in rproc_reset_rsc_table_on_detach() and
+        * rproc_reset_rsc_table_on_stop().
+        */
+       if (rproc->ops->detach) {
+               rproc->clean_table = kmemdup(table_ptr, table_sz, GFP_KERNEL);
+               if (!rproc->clean_table)
+                       return -ENOMEM;
+       } else {
+               rproc->clean_table = NULL;
+       }
+
+       rproc->cached_table = NULL;
+       rproc->table_ptr = table_ptr;
+       rproc->table_sz = table_sz;
+
+       return 0;
+}
+
+static int rproc_reset_rsc_table_on_detach(struct rproc *rproc)
+{
+       struct resource_table *table_ptr;
+
+       /* A resource table was never retrieved, nothing to do here */
+       if (!rproc->table_ptr)
+               return 0;
+
+       /*
+        * If we made it to this point a clean_table _must_ have been
+        * allocated in rproc_set_rsc_table().  If one isn't present
+        * something went really wrong and we must complain.
+        */
+       if (WARN_ON(!rproc->clean_table))
+               return -EINVAL;
+
+       /* Remember where the external entity installed the resource table */
+       table_ptr = rproc->table_ptr;
+
+       /*
+        * If we made it here the remote processor was started by another
+        * entity and a cache table doesn't exist.  As such make a copy of
+        * the resource table currently used by the remote processor and
+        * use that for the rest of the shutdown process.  The memory
+        * allocated here is free'd in rproc_detach().
+        */
+       rproc->cached_table = kmemdup(rproc->table_ptr,
+                                     rproc->table_sz, GFP_KERNEL);
+       if (!rproc->cached_table)
+               return -ENOMEM;
+
+       /*
+        * Use a copy of the resource table for the remainder of the
+        * shutdown process.
+        */
+       rproc->table_ptr = rproc->cached_table;
+
+       /*
+        * Reset the memory area where the firmware loaded the resource table
+        * to its original value.  That way when we re-attach the remote
+        * processor the resource table is clean and ready to be used again.
+        */
+       memcpy(table_ptr, rproc->clean_table, rproc->table_sz);
+
+       /*
+        * The clean resource table is no longer needed.  Allocated in
+        * rproc_set_rsc_table().
+        */
+       kfree(rproc->clean_table);
+
+       return 0;
+}
+
+static int rproc_reset_rsc_table_on_stop(struct rproc *rproc)
+{
+       /* A resource table was never retrieved, nothing to do here */
+       if (!rproc->table_ptr)
+               return 0;
+
+       /*
+        * If a cache table exists the remote processor was started by
+        * the remoteproc core.  That cache table should be used for
+        * the rest of the shutdown process.
+        */
+       if (rproc->cached_table)
+               goto out;
+
+       /*
+        * If we made it here the remote processor was started by another
+        * entity and a cache table doesn't exist.  As such make a copy of
+        * the resource table currently used by the remote processor and
+        * use that for the rest of the shutdown process.  The memory
+        * allocated here is free'd in rproc_shutdown().
+        */
+       rproc->cached_table = kmemdup(rproc->table_ptr,
+                                     rproc->table_sz, GFP_KERNEL);
+       if (!rproc->cached_table)
+               return -ENOMEM;
+
+       /*
+        * Since the remote processor is being switched off the clean table
+        * won't be needed.  Allocated in rproc_set_rsc_table().
+        */
+       kfree(rproc->clean_table);
+
+out:
+       /*
+        * Use a copy of the resource table for the remainder of the
+        * shutdown process.
+        */
+       rproc->table_ptr = rproc->cached_table;
+       return 0;
+}
+
 /*
  * Attach to remote processor - similar to rproc_fw_boot() but without
  * the steps that deal with the firmware image.
  */
-static int rproc_actuate(struct rproc *rproc)
+static int rproc_attach(struct rproc *rproc)
 {
        struct device *dev = &rproc->dev;
        int ret;
@@ -1556,6 +1700,19 @@ static int rproc_actuate(struct rproc *rproc)
                return ret;
        }
 
+       /* Do anything that is needed to boot the remote processor */
+       ret = rproc_prepare_device(rproc);
+       if (ret) {
+               dev_err(dev, "can't prepare rproc %s: %d\n", rproc->name, ret);
+               goto disable_iommu;
+       }
+
+       ret = rproc_set_rsc_table(rproc);
+       if (ret) {
+               dev_err(dev, "can't load resource table: %d\n", ret);
+               goto unprepare_device;
+       }
+
        /* reset max_notifyid */
        rproc->max_notifyid = -1;
 
@@ -1570,7 +1727,7 @@ static int rproc_actuate(struct rproc *rproc)
        ret = rproc_handle_resources(rproc, rproc_loading_handlers);
        if (ret) {
                dev_err(dev, "Failed to process resources: %d\n", ret);
-               goto disable_iommu;
+               goto unprepare_device;
        }
 
        /* Allocate carveout resources associated to rproc */
@@ -1581,7 +1738,7 @@ static int rproc_actuate(struct rproc *rproc)
                goto clean_up_resources;
        }
 
-       ret = rproc_attach(rproc);
+       ret = __rproc_attach(rproc);
        if (ret)
                goto clean_up_resources;
 
@@ -1589,6 +1746,9 @@ static int rproc_actuate(struct rproc *rproc)
 
 clean_up_resources:
        rproc_resource_cleanup(rproc);
+unprepare_device:
+       /* release HW resources if needed */
+       rproc_unprepare_device(rproc);
 disable_iommu:
        rproc_disable_iommu(rproc);
        return ret;
@@ -1642,11 +1802,20 @@ static int rproc_stop(struct rproc *rproc, bool crashed)
        struct device *dev = &rproc->dev;
        int ret;
 
+       /* No need to continue if a stop() operation has not been provided */
+       if (!rproc->ops->stop)
+               return -EINVAL;
+
        /* Stop any subdevices for the remote processor */
        rproc_stop_subdevices(rproc, crashed);
 
        /* the installed resource table is no longer accessible */
-       rproc->table_ptr = rproc->cached_table;
+       ret = rproc_reset_rsc_table_on_stop(rproc);
+       if (ret) {
+               dev_err(dev, "can't reset resource table: %d\n", ret);
+               return ret;
+       }
+
 
        /* power off the remote processor */
        ret = rproc->ops->stop(rproc);
@@ -1659,19 +1828,48 @@ static int rproc_stop(struct rproc *rproc, bool crashed)
 
        rproc->state = RPROC_OFFLINE;
 
-       /*
-        * The remote processor has been stopped and is now offline, which means
-        * that the next time it is brought back online the remoteproc core will
-        * be responsible to load its firmware.  As such it is no longer
-        * autonomous.
-        */
-       rproc->autonomous = false;
-
        dev_info(dev, "stopped remote processor %s\n", rproc->name);
 
        return 0;
 }
 
+/*
+ * __rproc_detach(): Does the opposite of __rproc_attach()
+ */
+static int __rproc_detach(struct rproc *rproc)
+{
+       struct device *dev = &rproc->dev;
+       int ret;
+
+       /* No need to continue if a detach() operation has not been provided */
+       if (!rproc->ops->detach)
+               return -EINVAL;
+
+       /* Stop any subdevices for the remote processor */
+       rproc_stop_subdevices(rproc, false);
+
+       /* the installed resource table is no longer accessible */
+       ret = rproc_reset_rsc_table_on_detach(rproc);
+       if (ret) {
+               dev_err(dev, "can't reset resource table: %d\n", ret);
+               return ret;
+       }
+
+       /* Tell the remote processor the core isn't available anymore */
+       ret = rproc->ops->detach(rproc);
+       if (ret) {
+               dev_err(dev, "can't detach from rproc: %d\n", ret);
+               return ret;
+       }
+
+       rproc_unprepare_subdevices(rproc);
+
+       rproc->state = RPROC_DETACHED;
+
+       dev_info(dev, "detached remote processor %s\n", rproc->name);
+
+       return 0;
+}
 
 /**
  * rproc_trigger_recovery() - recover a remoteproc
@@ -1802,7 +2000,7 @@ int rproc_boot(struct rproc *rproc)
        if (rproc->state == RPROC_DETACHED) {
                dev_info(dev, "attaching to %s\n", rproc->name);
 
-               ret = rproc_actuate(rproc);
+               ret = rproc_attach(rproc);
        } else {
                dev_info(dev, "powering up %s\n", rproc->name);
 
@@ -1884,6 +2082,65 @@ out:
 }
 EXPORT_SYMBOL(rproc_shutdown);
 
+/**
+ * rproc_detach() - Detach the remote processor from the
+ * remoteproc core
+ *
+ * @rproc: the remote processor
+ *
+ * Detach a remote processor (previously attached to with rproc_attach()).
+ *
+ * In case @rproc is still being used by an additional user(s), then
+ * this function will just decrement the power refcount and exit,
+ * without disconnecting the device.
+ *
+ * Function rproc_detach() calls __rproc_detach() in order to let a remote
+ * processor know that services provided by the application processor are
+ * no longer available.  From there it should be possible to remove the
+ * platform driver and even power cycle the application processor (if the HW
+ * supports it) without needing to switch off the remote processor.
+ */
+int rproc_detach(struct rproc *rproc)
+{
+       struct device *dev = &rproc->dev;
+       int ret;
+
+       ret = mutex_lock_interruptible(&rproc->lock);
+       if (ret) {
+               dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, ret);
+               return ret;
+       }
+
+       /* if the remote proc is still needed, bail out */
+       if (!atomic_dec_and_test(&rproc->power)) {
+               ret = 0;
+               goto out;
+       }
+
+       ret = __rproc_detach(rproc);
+       if (ret) {
+               atomic_inc(&rproc->power);
+               goto out;
+       }
+
+       /* clean up all acquired resources */
+       rproc_resource_cleanup(rproc);
+
+       /* release HW resources if needed */
+       rproc_unprepare_device(rproc);
+
+       rproc_disable_iommu(rproc);
+
+       /* Free the copy of the resource table */
+       kfree(rproc->cached_table);
+       rproc->cached_table = NULL;
+       rproc->table_ptr = NULL;
+out:
+       mutex_unlock(&rproc->lock);
+       return ret;
+}
+EXPORT_SYMBOL(rproc_detach);
+
 /**
  * rproc_get_by_phandle() - find a remote processor by phandle
  * @phandle: phandle to the rproc
@@ -2077,16 +2334,6 @@ int rproc_add(struct rproc *rproc)
        if (ret < 0)
                return ret;
 
-       /*
-        * Remind ourselves the remote processor has been attached to rather
-        * than booted by the remoteproc core.  This is important because the
-        * RPROC_DETACHED state will be lost as soon as the remote processor
-        * has been attached to.  Used in firmware_show() and reset in
-        * rproc_stop().
-        */
-       if (rproc->state == RPROC_DETACHED)
-               rproc->autonomous = true;
-
        /* if rproc is marked always-on, request it to boot */
        if (rproc->auto_boot) {
                ret = rproc_trigger_auto_boot(rproc);
@@ -2347,10 +2594,8 @@ int rproc_del(struct rproc *rproc)
        if (!rproc)
                return -EINVAL;
 
-       /* if rproc is marked always-on, rproc_add() booted it */
        /* TODO: make sure this works with rproc->power > 1 */
-       if (rproc->auto_boot)
-               rproc_shutdown(rproc);
+       rproc_shutdown(rproc);
 
        mutex_lock(&rproc->lock);
        rproc->state = RPROC_DELETED;
@@ -2492,7 +2737,11 @@ static int rproc_panic_handler(struct notifier_block *nb, unsigned long event,
 
        rcu_read_lock();
        list_for_each_entry_rcu(rproc, &rproc_list, node) {
-               if (!rproc->ops->panic || rproc->state != RPROC_RUNNING)
+               if (!rproc->ops->panic)
+                       continue;
+
+               if (rproc->state != RPROC_RUNNING &&
+                   rproc->state != RPROC_ATTACHED)
                        continue;
 
                d = rproc->ops->panic(rproc);