diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-10-07 11:53:53 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-10-07 11:53:53 -0700 |
commit | 94e8ca6ebd1bc20a193eb552dee4de884a954938 (patch) | |
tree | 14faeabc63f8530836845e5aaec1dd5f47b2e893 /drivers | |
parent | e5df1d3ebed2568caf45564946b4a60aa75f0277 (diff) | |
parent | ccf22a48cc8789a35befea783448c259463a5eef (diff) |
Merge tag 'rproc-v6.1' of git://git.kernel.org/pub/scm/linux/kernel/git/remoteproc/linux
Pull remoteproc updates from Bjorn Andersson:
"Support for remoteprocs that will perform recovery without help from
Linux is introduced.
The virtio integration is transitioned towards remoteproc_virtio.c and
represented by a platform_device, in preparation for instantiating
virtio instances from DeviceTree.
The iMX remoteproc driver has a couple of sparse warnings corrected
and a couple of error message printouts are cleaned up. The keystone
driver is transitioned to use the gpiod API"
* tag 'rproc-v6.1' of git://git.kernel.org/pub/scm/linux/kernel/git/remoteproc/linux:
remoteproc: virtio: Fix warning on bindings by removing the of_match_table
remoteproc: Support attach recovery after rproc crash
remoteproc: Introduce rproc features
remoteproc: virtio: Create platform device for the remoteproc_virtio
remoteproc: Move rproc_vdev management to remoteproc_virtio.c
remoteproc: core: Introduce rproc_add_rvdev function
remoteproc: core: Introduce rproc_rvdev_add_device function
remoteproc: Harden rproc_handle_vdev() against integer overflow
remoteproc/keystone: Switch to using gpiod API
drivers/remoteproc: Fix repeated words in comments
remoteproc: imx_dsp_rproc: fix argument 2 of rproc_mem_entry_init
remoteproc: imx_rproc: Simplify some error message
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/remoteproc/imx_dsp_rproc.c | 4 | ||||
-rw-r--r-- | drivers/remoteproc/imx_rproc.c | 14 | ||||
-rw-r--r-- | drivers/remoteproc/keystone_remoteproc.c | 16 | ||||
-rw-r--r-- | drivers/remoteproc/remoteproc_core.c | 223 | ||||
-rw-r--r-- | drivers/remoteproc/remoteproc_internal.h | 38 | ||||
-rw-r--r-- | drivers/remoteproc/remoteproc_virtio.c | 183 |
6 files changed, 280 insertions, 198 deletions
diff --git a/drivers/remoteproc/imx_dsp_rproc.c b/drivers/remoteproc/imx_dsp_rproc.c index ca0817f8e41e..899aa8dd12f0 100644 --- a/drivers/remoteproc/imx_dsp_rproc.c +++ b/drivers/remoteproc/imx_dsp_rproc.c @@ -599,7 +599,7 @@ static int imx_dsp_rproc_add_carveout(struct imx_dsp_rproc *priv) } /* Register memory region */ - mem = rproc_mem_entry_init(dev, cpu_addr, (dma_addr_t)att->sa, + mem = rproc_mem_entry_init(dev, (void __force *)cpu_addr, (dma_addr_t)att->sa, att->size, da, NULL, NULL, "dsp_mem"); if (mem) @@ -635,7 +635,7 @@ static int imx_dsp_rproc_add_carveout(struct imx_dsp_rproc *priv) } /* Register memory region */ - mem = rproc_mem_entry_init(dev, cpu_addr, (dma_addr_t)rmem->base, + mem = rproc_mem_entry_init(dev, (void __force *)cpu_addr, (dma_addr_t)rmem->base, rmem->size, da, NULL, NULL, it.node->name); if (mem) diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c index 38383e7de3c1..7cc4fd207e2d 100644 --- a/drivers/remoteproc/imx_rproc.c +++ b/drivers/remoteproc/imx_rproc.c @@ -646,7 +646,6 @@ static int imx_rproc_xtr_mbox_init(struct rproc *rproc) struct imx_rproc *priv = rproc->priv; struct device *dev = priv->dev; struct mbox_client *cl; - int ret; if (!of_get_property(dev->of_node, "mbox-names", NULL)) return 0; @@ -659,18 +658,15 @@ static int imx_rproc_xtr_mbox_init(struct rproc *rproc) cl->rx_callback = imx_rproc_rx_callback; priv->tx_ch = mbox_request_channel_byname(cl, "tx"); - if (IS_ERR(priv->tx_ch)) { - ret = PTR_ERR(priv->tx_ch); - return dev_err_probe(cl->dev, ret, - "failed to request tx mailbox channel: %d\n", ret); - } + if (IS_ERR(priv->tx_ch)) + return dev_err_probe(cl->dev, PTR_ERR(priv->tx_ch), + "failed to request tx mailbox channel\n"); priv->rx_ch = mbox_request_channel_byname(cl, "rx"); if (IS_ERR(priv->rx_ch)) { mbox_free_channel(priv->tx_ch); - ret = PTR_ERR(priv->rx_ch); - return dev_err_probe(cl->dev, ret, - "failed to request rx mailbox channel: %d\n", ret); + return dev_err_probe(cl->dev, PTR_ERR(priv->rx_ch), + "failed to request rx mailbox channel\n"); } return 0; diff --git a/drivers/remoteproc/keystone_remoteproc.c b/drivers/remoteproc/keystone_remoteproc.c index 594a9b43b7ae..95b39741925d 100644 --- a/drivers/remoteproc/keystone_remoteproc.c +++ b/drivers/remoteproc/keystone_remoteproc.c @@ -14,7 +14,7 @@ #include <linux/workqueue.h> #include <linux/of_address.h> #include <linux/of_reserved_mem.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> #include <linux/regmap.h> #include <linux/mfd/syscon.h> #include <linux/remoteproc.h> @@ -59,10 +59,10 @@ struct keystone_rproc { int num_mems; struct regmap *dev_ctrl; struct reset_control *reset; + struct gpio_desc *kick_gpio; u32 boot_offset; int irq_ring; int irq_fault; - int kick_gpio; struct work_struct workqueue; }; @@ -232,10 +232,10 @@ static void keystone_rproc_kick(struct rproc *rproc, int vqid) { struct keystone_rproc *ksproc = rproc->priv; - if (WARN_ON(ksproc->kick_gpio < 0)) + if (!ksproc->kick_gpio) return; - gpio_set_value(ksproc->kick_gpio, 1); + gpiod_set_value(ksproc->kick_gpio, 1); } /* @@ -432,9 +432,9 @@ static int keystone_rproc_probe(struct platform_device *pdev) goto disable_clk; } - ksproc->kick_gpio = of_get_named_gpio_flags(np, "kick-gpios", 0, NULL); - if (ksproc->kick_gpio < 0) { - ret = ksproc->kick_gpio; + ksproc->kick_gpio = gpiod_get(dev, "kick", GPIOD_ASIS); + ret = PTR_ERR_OR_ZERO(ksproc->kick_gpio); + if (ret) { dev_err(dev, "failed to get gpio for virtio kicks, status = %d\n", ret); goto disable_clk; @@ -466,6 +466,7 @@ static int keystone_rproc_probe(struct platform_device *pdev) release_mem: of_reserved_mem_device_release(dev); + gpiod_put(ksproc->kick_gpio); disable_clk: pm_runtime_put_sync(dev); disable_rpm: @@ -480,6 +481,7 @@ static int keystone_rproc_remove(struct platform_device *pdev) struct keystone_rproc *ksproc = platform_get_drvdata(pdev); rproc_del(ksproc->rproc); + gpiod_put(ksproc->kick_gpio); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); rproc_free(ksproc->rproc); diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index e5279ed9a8d7..8768cb64f560 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -23,9 +23,7 @@ #include <linux/panic_notifier.h> #include <linux/slab.h> #include <linux/mutex.h> -#include <linux/dma-map-ops.h> #include <linux/dma-mapping.h> -#include <linux/dma-direct.h> /* XXX: pokes into bus_dma_range */ #include <linux/firmware.h> #include <linux/string.h> #include <linux/debugfs.h> @@ -346,7 +344,7 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) if (rproc_check_carveout_da(rproc, mem, rsc->vring[i].da, size)) return -ENOMEM; } else { - /* Register carveout in in list */ + /* Register carveout in list */ mem = rproc_mem_entry_init(dev, NULL, 0, size, rsc->vring[i].da, rproc_alloc_carveout, @@ -384,7 +382,7 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) return 0; } -static int +int rproc_parse_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i) { struct rproc *rproc = rvdev->rproc; @@ -435,57 +433,17 @@ void rproc_free_vring(struct rproc_vring *rvring) } } -static int rproc_vdev_do_start(struct rproc_subdev *subdev) +void rproc_add_rvdev(struct rproc *rproc, struct rproc_vdev *rvdev) { - struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev); - - return rproc_add_virtio_dev(rvdev, rvdev->id); -} - -static void rproc_vdev_do_stop(struct rproc_subdev *subdev, bool crashed) -{ - struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev); - int ret; - - ret = device_for_each_child(&rvdev->dev, NULL, rproc_remove_virtio_dev); - if (ret) - dev_warn(&rvdev->dev, "can't remove vdev child device: %d\n", ret); -} - -/** - * rproc_rvdev_release() - release the existence of a rvdev - * - * @dev: the subdevice's dev - */ -static void rproc_rvdev_release(struct device *dev) -{ - struct rproc_vdev *rvdev = container_of(dev, struct rproc_vdev, dev); - - of_reserved_mem_device_release(dev); - dma_release_coherent_memory(dev); - - kfree(rvdev); + if (rvdev && rproc) + list_add_tail(&rvdev->node, &rproc->rvdevs); } -static int copy_dma_range_map(struct device *to, struct device *from) +void rproc_remove_rvdev(struct rproc_vdev *rvdev) { - const struct bus_dma_region *map = from->dma_range_map, *new_map, *r; - int num_ranges = 0; - - if (!map) - return 0; - - for (r = map; r->size; r++) - num_ranges++; - - new_map = kmemdup(map, array_size(num_ranges + 1, sizeof(*map)), - GFP_KERNEL); - if (!new_map) - return -ENOMEM; - to->dma_range_map = new_map; - return 0; + if (rvdev) + list_del(&rvdev->node); } - /** * rproc_handle_vdev() - handle a vdev fw resource * @rproc: the remote processor @@ -520,12 +478,13 @@ static int rproc_handle_vdev(struct rproc *rproc, void *ptr, struct fw_rsc_vdev *rsc = ptr; struct device *dev = &rproc->dev; struct rproc_vdev *rvdev; - int i, ret; - char name[16]; + size_t rsc_size; + struct rproc_vdev_data rvdev_data; + struct platform_device *pdev; /* make sure resource isn't truncated */ - if (struct_size(rsc, vring, rsc->num_of_vrings) + rsc->config_len > - avail) { + rsc_size = struct_size(rsc, vring, rsc->num_of_vrings); + if (size_add(rsc_size, rsc->config_len) > avail) { dev_err(dev, "vdev rsc is truncated\n"); return -EINVAL; } @@ -545,93 +504,19 @@ static int rproc_handle_vdev(struct rproc *rproc, void *ptr, return -EINVAL; } - rvdev = kzalloc(sizeof(*rvdev), GFP_KERNEL); - if (!rvdev) - return -ENOMEM; - - kref_init(&rvdev->refcount); - - rvdev->id = rsc->id; - rvdev->rproc = rproc; - rvdev->index = rproc->nb_vdev++; + rvdev_data.id = rsc->id; + rvdev_data.index = rproc->nb_vdev++; + rvdev_data.rsc_offset = offset; + rvdev_data.rsc = rsc; - /* Initialise vdev subdevice */ - snprintf(name, sizeof(name), "vdev%dbuffer", rvdev->index); - rvdev->dev.parent = &rproc->dev; - rvdev->dev.release = rproc_rvdev_release; - dev_set_name(&rvdev->dev, "%s#%s", dev_name(rvdev->dev.parent), name); - dev_set_drvdata(&rvdev->dev, rvdev); - - ret = device_register(&rvdev->dev); - if (ret) { - put_device(&rvdev->dev); - return ret; + pdev = platform_device_register_data(dev, "rproc-virtio", rvdev_data.index, &rvdev_data, + sizeof(rvdev_data)); + if (IS_ERR(pdev)) { + dev_err(dev, "failed to create rproc-virtio device\n"); + return PTR_ERR(pdev); } - ret = copy_dma_range_map(&rvdev->dev, rproc->dev.parent); - if (ret) - goto free_rvdev; - - /* Make device dma capable by inheriting from parent's capabilities */ - set_dma_ops(&rvdev->dev, get_dma_ops(rproc->dev.parent)); - - ret = dma_coerce_mask_and_coherent(&rvdev->dev, - dma_get_mask(rproc->dev.parent)); - if (ret) { - dev_warn(dev, - "Failed to set DMA mask %llx. Trying to continue... (%pe)\n", - dma_get_mask(rproc->dev.parent), ERR_PTR(ret)); - } - - /* parse the vrings */ - for (i = 0; i < rsc->num_of_vrings; i++) { - ret = rproc_parse_vring(rvdev, rsc, i); - if (ret) - goto free_rvdev; - } - - /* remember the resource offset*/ - rvdev->rsc_offset = offset; - - /* allocate the vring resources */ - for (i = 0; i < rsc->num_of_vrings; i++) { - ret = rproc_alloc_vring(rvdev, i); - if (ret) - goto unwind_vring_allocations; - } - - list_add_tail(&rvdev->node, &rproc->rvdevs); - - rvdev->subdev.start = rproc_vdev_do_start; - rvdev->subdev.stop = rproc_vdev_do_stop; - - rproc_add_subdev(rproc, &rvdev->subdev); - return 0; - -unwind_vring_allocations: - for (i--; i >= 0; i--) - rproc_free_vring(&rvdev->vring[i]); -free_rvdev: - device_unregister(&rvdev->dev); - return ret; -} - -void rproc_vdev_release(struct kref *ref) -{ - struct rproc_vdev *rvdev = container_of(ref, struct rproc_vdev, refcount); - struct rproc_vring *rvring; - struct rproc *rproc = rvdev->rproc; - int id; - - for (id = 0; id < ARRAY_SIZE(rvdev->vring); id++) { - rvring = &rvdev->vring[id]; - rproc_free_vring(rvring); - } - - rproc_remove_subdev(rproc, &rvdev->subdev); - list_del(&rvdev->node); - device_unregister(&rvdev->dev); } /** @@ -1365,7 +1250,7 @@ void rproc_resource_cleanup(struct rproc *rproc) /* clean up remote vdev entries */ list_for_each_entry_safe(rvdev, rvtmp, &rproc->rvdevs, node) - kref_put(&rvdev->refcount, rproc_vdev_release); + platform_device_unregister(rvdev->pdev); rproc_coredump_cleanup(rproc); } @@ -1885,6 +1770,45 @@ static int __rproc_detach(struct rproc *rproc) return 0; } +static int rproc_attach_recovery(struct rproc *rproc) +{ + int ret; + + ret = __rproc_detach(rproc); + if (ret) + return ret; + + return __rproc_attach(rproc); +} + +static int rproc_boot_recovery(struct rproc *rproc) +{ + const struct firmware *firmware_p; + struct device *dev = &rproc->dev; + int ret; + + ret = rproc_stop(rproc, true); + if (ret) + return ret; + + /* generate coredump */ + rproc->ops->coredump(rproc); + + /* load firmware */ + ret = request_firmware(&firmware_p, rproc->firmware, dev); + if (ret < 0) { + dev_err(dev, "request_firmware failed: %d\n", ret); + return ret; + } + + /* boot the remote processor up again */ + ret = rproc_start(rproc, firmware_p); + + release_firmware(firmware_p); + + return ret; +} + /** * rproc_trigger_recovery() - recover a remoteproc * @rproc: the remote processor @@ -1899,7 +1823,6 @@ static int __rproc_detach(struct rproc *rproc) */ int rproc_trigger_recovery(struct rproc *rproc) { - const struct firmware *firmware_p; struct device *dev = &rproc->dev; int ret; @@ -1913,24 +1836,10 @@ int rproc_trigger_recovery(struct rproc *rproc) dev_err(dev, "recovering %s\n", rproc->name); - ret = rproc_stop(rproc, true); - if (ret) - goto unlock_mutex; - - /* generate coredump */ - rproc->ops->coredump(rproc); - - /* load firmware */ - ret = request_firmware(&firmware_p, rproc->firmware, dev); - if (ret < 0) { - dev_err(dev, "request_firmware failed: %d\n", ret); - goto unlock_mutex; - } - - /* boot the remote processor up again */ - ret = rproc_start(rproc, firmware_p); - - release_firmware(firmware_p); + if (rproc_has_feature(rproc, RPROC_FEAT_ATTACH_ON_RECOVERY)) + ret = rproc_attach_recovery(rproc); + else + ret = rproc_boot_recovery(rproc); unlock_mutex: mutex_unlock(&rproc->lock); diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h index 72d4d3d7d94d..d4dbb8d1d80c 100644 --- a/drivers/remoteproc/remoteproc_internal.h +++ b/drivers/remoteproc/remoteproc_internal.h @@ -24,16 +24,43 @@ struct rproc_debug_trace { struct rproc_mem_entry trace_mem; }; +/** + * struct rproc_vdev_data - remoteproc virtio device data + * @rsc_offset: offset of the vdev's resource entry + * @id: virtio device id (as in virtio_ids.h) + * @index: vdev position versus other vdev declared in resource table + * @rsc: pointer to the vdev resource entry. Valid only during vdev init as + * the resource can be cached by rproc. + */ +struct rproc_vdev_data { + u32 rsc_offset; + unsigned int id; + u32 index; + struct fw_rsc_vdev *rsc; +}; + +static inline bool rproc_has_feature(struct rproc *rproc, unsigned int feature) +{ + return test_bit(feature, rproc->features); +} + +static inline int rproc_set_feature(struct rproc *rproc, unsigned int feature) +{ + if (feature >= RPROC_MAX_FEATURES) + return -EINVAL; + + set_bit(feature, rproc->features); + + return 0; +} + /* from remoteproc_core.c */ void rproc_release(struct kref *kref); -irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id); -void rproc_vdev_release(struct kref *ref); int rproc_of_parse_firmware(struct device *dev, int index, const char **fw_name); /* from remoteproc_virtio.c */ -int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id); -int rproc_remove_virtio_dev(struct device *dev, void *data); +irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id); /* from remoteproc_debugfs.c */ void rproc_remove_trace_file(struct dentry *tfile); @@ -83,6 +110,7 @@ static inline void rproc_char_device_remove(struct rproc *rproc) void rproc_free_vring(struct rproc_vring *rvring); int rproc_alloc_vring(struct rproc_vdev *rvdev, int i); +int rproc_parse_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i); phys_addr_t rproc_va_to_pa(void *cpu_addr); int rproc_trigger_recovery(struct rproc *rproc); @@ -95,6 +123,8 @@ struct resource_table *rproc_elf_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw); struct rproc_mem_entry * rproc_find_carveout_by_name(struct rproc *rproc, const char *name, ...); +void rproc_add_rvdev(struct rproc *rproc, struct rproc_vdev *rvdev); +void rproc_remove_rvdev(struct rproc_vdev *rvdev); static inline int rproc_prepare_device(struct rproc *rproc) { diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c index 0f7706e23eb9..0e95525c1158 100644 --- a/drivers/remoteproc/remoteproc_virtio.c +++ b/drivers/remoteproc/remoteproc_virtio.c @@ -9,9 +9,12 @@ * Brian Swetland <swetland@google.com> */ +#include <linux/dma-direct.h> #include <linux/dma-map-ops.h> +#include <linux/dma-mapping.h> #include <linux/export.h> #include <linux/of_reserved_mem.h> +#include <linux/platform_device.h> #include <linux/remoteproc.h> #include <linux/virtio.h> #include <linux/virtio_config.h> @@ -23,9 +26,32 @@ #include "remoteproc_internal.h" +static int copy_dma_range_map(struct device *to, struct device *from) +{ + const struct bus_dma_region *map = from->dma_range_map, *new_map, *r; + int num_ranges = 0; + + if (!map) + return 0; + + for (r = map; r->size; r++) + num_ranges++; + + new_map = kmemdup(map, array_size(num_ranges + 1, sizeof(*map)), + GFP_KERNEL); + if (!new_map) + return -ENOMEM; + to->dma_range_map = new_map; + return 0; +} + static struct rproc_vdev *vdev_to_rvdev(struct virtio_device *vdev) { - return container_of(vdev->dev.parent, struct rproc_vdev, dev); + struct platform_device *pdev; + + pdev = container_of(vdev->dev.parent, struct platform_device, dev); + + return platform_get_drvdata(pdev); } static struct rproc *vdev_to_rproc(struct virtio_device *vdev) @@ -322,13 +348,10 @@ static void rproc_virtio_dev_release(struct device *dev) { struct virtio_device *vdev = dev_to_virtio(dev); struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); - struct rproc *rproc = vdev_to_rproc(vdev); kfree(vdev); - kref_put(&rvdev->refcount, rproc_vdev_release); - - put_device(&rproc->dev); + put_device(&rvdev->pdev->dev); } /** @@ -341,10 +364,10 @@ static void rproc_virtio_dev_release(struct device *dev) * * Return: 0 on success or an appropriate error value otherwise */ -int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id) +static int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id) { struct rproc *rproc = rvdev->rproc; - struct device *dev = &rvdev->dev; + struct device *dev = &rvdev->pdev->dev; struct virtio_device *vdev; struct rproc_mem_entry *mem; int ret; @@ -414,18 +437,8 @@ int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id) vdev->dev.parent = dev; vdev->dev.release = rproc_virtio_dev_release; - /* - * We're indirectly making a non-temporary copy of the rproc pointer - * here, because drivers probed with this vdev will indirectly - * access the wrapping rproc. - * - * Therefore we must increment the rproc refcount here, and decrement - * it _only_ when the vdev is released. - */ - get_device(&rproc->dev); - /* Reference the vdev and vring allocations */ - kref_get(&rvdev->refcount); + get_device(dev); ret = register_virtio_device(vdev); if (ret) { @@ -449,10 +462,142 @@ out: * * Return: 0 */ -int rproc_remove_virtio_dev(struct device *dev, void *data) +static int rproc_remove_virtio_dev(struct device *dev, void *data) { struct virtio_device *vdev = dev_to_virtio(dev); unregister_virtio_device(vdev); return 0; } + +static int rproc_vdev_do_start(struct rproc_subdev *subdev) +{ + struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev); + + return rproc_add_virtio_dev(rvdev, rvdev->id); +} + +static void rproc_vdev_do_stop(struct rproc_subdev *subdev, bool crashed) +{ + struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev); + struct device *dev = &rvdev->pdev->dev; + int ret; + + ret = device_for_each_child(dev, NULL, rproc_remove_virtio_dev); + if (ret) + dev_warn(dev, "can't remove vdev child device: %d\n", ret); +} + +static int rproc_virtio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rproc_vdev_data *rvdev_data = dev->platform_data; + struct rproc_vdev *rvdev; + struct rproc *rproc = container_of(dev->parent, struct rproc, dev); + struct fw_rsc_vdev *rsc; + int i, ret; + + if (!rvdev_data) + return -EINVAL; + + rvdev = devm_kzalloc(dev, sizeof(*rvdev), GFP_KERNEL); + if (!rvdev) + return -ENOMEM; + + rvdev->id = rvdev_data->id; + rvdev->rproc = rproc; + rvdev->index = rvdev_data->index; + + ret = copy_dma_range_map(dev, rproc->dev.parent); + if (ret) + return ret; + + /* Make device dma capable by inheriting from parent's capabilities */ + set_dma_ops(dev, get_dma_ops(rproc->dev.parent)); + + ret = dma_coerce_mask_and_coherent(dev, dma_get_mask(rproc->dev.parent)); + if (ret) { + dev_warn(dev, "Failed to set DMA mask %llx. Trying to continue... (%pe)\n", + dma_get_mask(rproc->dev.parent), ERR_PTR(ret)); + } + + platform_set_drvdata(pdev, rvdev); + rvdev->pdev = pdev; + + rsc = rvdev_data->rsc; + + /* parse the vrings */ + for (i = 0; i < rsc->num_of_vrings; i++) { + ret = rproc_parse_vring(rvdev, rsc, i); + if (ret) + return ret; + } + + /* remember the resource offset*/ + rvdev->rsc_offset = rvdev_data->rsc_offset; + + /* allocate the vring resources */ + for (i = 0; i < rsc->num_of_vrings; i++) { + ret = rproc_alloc_vring(rvdev, i); + if (ret) + goto unwind_vring_allocations; + } + + rproc_add_rvdev(rproc, rvdev); + + rvdev->subdev.start = rproc_vdev_do_start; + rvdev->subdev.stop = rproc_vdev_do_stop; + + rproc_add_subdev(rproc, &rvdev->subdev); + + /* + * We're indirectly making a non-temporary copy of the rproc pointer + * here, because the platform device or the vdev device will indirectly + * access the wrapping rproc. + * + * Therefore we must increment the rproc refcount here, and decrement + * it _only_ on platform remove. + */ + get_device(&rproc->dev); + + return 0; + +unwind_vring_allocations: + for (i--; i >= 0; i--) + rproc_free_vring(&rvdev->vring[i]); + + return ret; +} + +static int rproc_virtio_remove(struct platform_device *pdev) +{ + struct rproc_vdev *rvdev = dev_get_drvdata(&pdev->dev); + struct rproc *rproc = rvdev->rproc; + struct rproc_vring *rvring; + int id; + + for (id = 0; id < ARRAY_SIZE(rvdev->vring); id++) { + rvring = &rvdev->vring[id]; + rproc_free_vring(rvring); + } + + rproc_remove_subdev(rproc, &rvdev->subdev); + rproc_remove_rvdev(rvdev); + + of_reserved_mem_device_release(&pdev->dev); + dma_release_coherent_memory(&pdev->dev); + + put_device(&rproc->dev); + + return 0; +} + +/* Platform driver */ +static struct platform_driver rproc_virtio_driver = { + .probe = rproc_virtio_probe, + .remove = rproc_virtio_remove, + .driver = { + .name = "rproc-virtio", + }, +}; +builtin_platform_driver(rproc_virtio_driver); |