Commit 94e8ca6e authored by Linus Torvalds's avatar Linus Torvalds

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
parents e5df1d3e ccf22a48
...@@ -599,7 +599,7 @@ static int imx_dsp_rproc_add_carveout(struct imx_dsp_rproc *priv) ...@@ -599,7 +599,7 @@ static int imx_dsp_rproc_add_carveout(struct imx_dsp_rproc *priv)
} }
/* Register memory region */ /* 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"); att->size, da, NULL, NULL, "dsp_mem");
if (mem) if (mem)
...@@ -635,7 +635,7 @@ static int imx_dsp_rproc_add_carveout(struct imx_dsp_rproc *priv) ...@@ -635,7 +635,7 @@ static int imx_dsp_rproc_add_carveout(struct imx_dsp_rproc *priv)
} }
/* Register memory region */ /* 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); rmem->size, da, NULL, NULL, it.node->name);
if (mem) if (mem)
......
...@@ -646,7 +646,6 @@ static int imx_rproc_xtr_mbox_init(struct rproc *rproc) ...@@ -646,7 +646,6 @@ static int imx_rproc_xtr_mbox_init(struct rproc *rproc)
struct imx_rproc *priv = rproc->priv; struct imx_rproc *priv = rproc->priv;
struct device *dev = priv->dev; struct device *dev = priv->dev;
struct mbox_client *cl; struct mbox_client *cl;
int ret;
if (!of_get_property(dev->of_node, "mbox-names", NULL)) if (!of_get_property(dev->of_node, "mbox-names", NULL))
return 0; return 0;
...@@ -659,18 +658,15 @@ static int imx_rproc_xtr_mbox_init(struct rproc *rproc) ...@@ -659,18 +658,15 @@ static int imx_rproc_xtr_mbox_init(struct rproc *rproc)
cl->rx_callback = imx_rproc_rx_callback; cl->rx_callback = imx_rproc_rx_callback;
priv->tx_ch = mbox_request_channel_byname(cl, "tx"); priv->tx_ch = mbox_request_channel_byname(cl, "tx");
if (IS_ERR(priv->tx_ch)) { if (IS_ERR(priv->tx_ch))
ret = PTR_ERR(priv->tx_ch); return dev_err_probe(cl->dev, PTR_ERR(priv->tx_ch),
return dev_err_probe(cl->dev, ret, "failed to request tx mailbox channel\n");
"failed to request tx mailbox channel: %d\n", ret);
}
priv->rx_ch = mbox_request_channel_byname(cl, "rx"); priv->rx_ch = mbox_request_channel_byname(cl, "rx");
if (IS_ERR(priv->rx_ch)) { if (IS_ERR(priv->rx_ch)) {
mbox_free_channel(priv->tx_ch); mbox_free_channel(priv->tx_ch);
ret = PTR_ERR(priv->rx_ch); return dev_err_probe(cl->dev, PTR_ERR(priv->rx_ch),
return dev_err_probe(cl->dev, ret, "failed to request rx mailbox channel\n");
"failed to request rx mailbox channel: %d\n", ret);
} }
return 0; return 0;
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_reserved_mem.h> #include <linux/of_reserved_mem.h>
#include <linux/of_gpio.h> #include <linux/gpio/consumer.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include <linux/remoteproc.h> #include <linux/remoteproc.h>
...@@ -59,10 +59,10 @@ struct keystone_rproc { ...@@ -59,10 +59,10 @@ struct keystone_rproc {
int num_mems; int num_mems;
struct regmap *dev_ctrl; struct regmap *dev_ctrl;
struct reset_control *reset; struct reset_control *reset;
struct gpio_desc *kick_gpio;
u32 boot_offset; u32 boot_offset;
int irq_ring; int irq_ring;
int irq_fault; int irq_fault;
int kick_gpio;
struct work_struct workqueue; struct work_struct workqueue;
}; };
...@@ -232,10 +232,10 @@ static void keystone_rproc_kick(struct rproc *rproc, int vqid) ...@@ -232,10 +232,10 @@ static void keystone_rproc_kick(struct rproc *rproc, int vqid)
{ {
struct keystone_rproc *ksproc = rproc->priv; struct keystone_rproc *ksproc = rproc->priv;
if (WARN_ON(ksproc->kick_gpio < 0)) if (!ksproc->kick_gpio)
return; 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) ...@@ -432,9 +432,9 @@ static int keystone_rproc_probe(struct platform_device *pdev)
goto disable_clk; goto disable_clk;
} }
ksproc->kick_gpio = of_get_named_gpio_flags(np, "kick-gpios", 0, NULL); ksproc->kick_gpio = gpiod_get(dev, "kick", GPIOD_ASIS);
if (ksproc->kick_gpio < 0) { ret = PTR_ERR_OR_ZERO(ksproc->kick_gpio);
ret = ksproc->kick_gpio; if (ret) {
dev_err(dev, "failed to get gpio for virtio kicks, status = %d\n", dev_err(dev, "failed to get gpio for virtio kicks, status = %d\n",
ret); ret);
goto disable_clk; goto disable_clk;
...@@ -466,6 +466,7 @@ static int keystone_rproc_probe(struct platform_device *pdev) ...@@ -466,6 +466,7 @@ static int keystone_rproc_probe(struct platform_device *pdev)
release_mem: release_mem:
of_reserved_mem_device_release(dev); of_reserved_mem_device_release(dev);
gpiod_put(ksproc->kick_gpio);
disable_clk: disable_clk:
pm_runtime_put_sync(dev); pm_runtime_put_sync(dev);
disable_rpm: disable_rpm:
...@@ -480,6 +481,7 @@ static int keystone_rproc_remove(struct platform_device *pdev) ...@@ -480,6 +481,7 @@ static int keystone_rproc_remove(struct platform_device *pdev)
struct keystone_rproc *ksproc = platform_get_drvdata(pdev); struct keystone_rproc *ksproc = platform_get_drvdata(pdev);
rproc_del(ksproc->rproc); rproc_del(ksproc->rproc);
gpiod_put(ksproc->kick_gpio);
pm_runtime_put_sync(&pdev->dev); pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
rproc_free(ksproc->rproc); rproc_free(ksproc->rproc);
......
...@@ -23,9 +23,7 @@ ...@@ -23,9 +23,7 @@
#include <linux/panic_notifier.h> #include <linux/panic_notifier.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/dma-map-ops.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/dma-direct.h> /* XXX: pokes into bus_dma_range */
#include <linux/firmware.h> #include <linux/firmware.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
...@@ -346,7 +344,7 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) ...@@ -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)) if (rproc_check_carveout_da(rproc, mem, rsc->vring[i].da, size))
return -ENOMEM; return -ENOMEM;
} else { } else {
/* Register carveout in in list */ /* Register carveout in list */
mem = rproc_mem_entry_init(dev, NULL, 0, mem = rproc_mem_entry_init(dev, NULL, 0,
size, rsc->vring[i].da, size, rsc->vring[i].da,
rproc_alloc_carveout, rproc_alloc_carveout,
...@@ -384,7 +382,7 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) ...@@ -384,7 +382,7 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
return 0; return 0;
} }
static int int
rproc_parse_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i) rproc_parse_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i)
{ {
struct rproc *rproc = rvdev->rproc; struct rproc *rproc = rvdev->rproc;
...@@ -435,57 +433,17 @@ void rproc_free_vring(struct rproc_vring *rvring) ...@@ -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); if (rvdev && rproc)
list_add_tail(&rvdev->node, &rproc->rvdevs);
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);
} }
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; if (rvdev)
int num_ranges = 0; list_del(&rvdev->node);
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;
} }
/** /**
* rproc_handle_vdev() - handle a vdev fw resource * rproc_handle_vdev() - handle a vdev fw resource
* @rproc: the remote processor * @rproc: the remote processor
...@@ -520,12 +478,13 @@ static int rproc_handle_vdev(struct rproc *rproc, void *ptr, ...@@ -520,12 +478,13 @@ static int rproc_handle_vdev(struct rproc *rproc, void *ptr,
struct fw_rsc_vdev *rsc = ptr; struct fw_rsc_vdev *rsc = ptr;
struct device *dev = &rproc->dev; struct device *dev = &rproc->dev;
struct rproc_vdev *rvdev; struct rproc_vdev *rvdev;
int i, ret; size_t rsc_size;
char name[16]; struct rproc_vdev_data rvdev_data;
struct platform_device *pdev;
/* make sure resource isn't truncated */ /* make sure resource isn't truncated */
if (struct_size(rsc, vring, rsc->num_of_vrings) + rsc->config_len > rsc_size = struct_size(rsc, vring, rsc->num_of_vrings);
avail) { if (size_add(rsc_size, rsc->config_len) > avail) {
dev_err(dev, "vdev rsc is truncated\n"); dev_err(dev, "vdev rsc is truncated\n");
return -EINVAL; return -EINVAL;
} }
...@@ -545,93 +504,19 @@ static int rproc_handle_vdev(struct rproc *rproc, void *ptr, ...@@ -545,93 +504,19 @@ static int rproc_handle_vdev(struct rproc *rproc, void *ptr,
return -EINVAL; return -EINVAL;
} }
rvdev = kzalloc(sizeof(*rvdev), GFP_KERNEL); rvdev_data.id = rsc->id;
if (!rvdev) rvdev_data.index = rproc->nb_vdev++;
return -ENOMEM; rvdev_data.rsc_offset = offset;
rvdev_data.rsc = rsc;
kref_init(&rvdev->refcount);
rvdev->id = rsc->id;
rvdev->rproc = rproc;
rvdev->index = rproc->nb_vdev++;
/* Initialise vdev subdevice */ pdev = platform_device_register_data(dev, "rproc-virtio", rvdev_data.index, &rvdev_data,
snprintf(name, sizeof(name), "vdev%dbuffer", rvdev->index); sizeof(rvdev_data));
rvdev->dev.parent = &rproc->dev; if (IS_ERR(pdev)) {
rvdev->dev.release = rproc_rvdev_release; dev_err(dev, "failed to create rproc-virtio device\n");
dev_set_name(&rvdev->dev, "%s#%s", dev_name(rvdev->dev.parent), name); return PTR_ERR(pdev);
dev_set_drvdata(&rvdev->dev, rvdev);
ret = device_register(&rvdev->dev);
if (ret) {
put_device(&rvdev->dev);
return ret;
} }
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; 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) ...@@ -1365,7 +1250,7 @@ void rproc_resource_cleanup(struct rproc *rproc)
/* clean up remote vdev entries */ /* clean up remote vdev entries */
list_for_each_entry_safe(rvdev, rvtmp, &rproc->rvdevs, node) 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); rproc_coredump_cleanup(rproc);
} }
...@@ -1885,6 +1770,45 @@ static int __rproc_detach(struct rproc *rproc) ...@@ -1885,6 +1770,45 @@ static int __rproc_detach(struct rproc *rproc)
return 0; 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_trigger_recovery() - recover a remoteproc
* @rproc: the remote processor * @rproc: the remote processor
...@@ -1899,7 +1823,6 @@ static int __rproc_detach(struct rproc *rproc) ...@@ -1899,7 +1823,6 @@ static int __rproc_detach(struct rproc *rproc)
*/ */
int rproc_trigger_recovery(struct rproc *rproc) int rproc_trigger_recovery(struct rproc *rproc)
{ {
const struct firmware *firmware_p;
struct device *dev = &rproc->dev; struct device *dev = &rproc->dev;
int ret; int ret;
...@@ -1913,24 +1836,10 @@ int rproc_trigger_recovery(struct rproc *rproc) ...@@ -1913,24 +1836,10 @@ int rproc_trigger_recovery(struct rproc *rproc)
dev_err(dev, "recovering %s\n", rproc->name); dev_err(dev, "recovering %s\n", rproc->name);
ret = rproc_stop(rproc, true); if (rproc_has_feature(rproc, RPROC_FEAT_ATTACH_ON_RECOVERY))
if (ret) ret = rproc_attach_recovery(rproc);
goto unlock_mutex; else
ret = rproc_boot_recovery(rproc);
/* 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);
unlock_mutex: unlock_mutex:
mutex_unlock(&rproc->lock); mutex_unlock(&rproc->lock);
......
...@@ -24,16 +24,43 @@ struct rproc_debug_trace { ...@@ -24,16 +24,43 @@ struct rproc_debug_trace {
struct rproc_mem_entry trace_mem; 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 */ /* from remoteproc_core.c */
void rproc_release(struct kref *kref); 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, int rproc_of_parse_firmware(struct device *dev, int index,
const char **fw_name); const char **fw_name);
/* from remoteproc_virtio.c */ /* from remoteproc_virtio.c */
int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id); irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id);
int rproc_remove_virtio_dev(struct device *dev, void *data);
/* from remoteproc_debugfs.c */ /* from remoteproc_debugfs.c */
void rproc_remove_trace_file(struct dentry *tfile); void rproc_remove_trace_file(struct dentry *tfile);
...@@ -83,6 +110,7 @@ static inline void rproc_char_device_remove(struct rproc *rproc) ...@@ -83,6 +110,7 @@ static inline void rproc_char_device_remove(struct rproc *rproc)
void rproc_free_vring(struct rproc_vring *rvring); void rproc_free_vring(struct rproc_vring *rvring);
int rproc_alloc_vring(struct rproc_vdev *rvdev, int i); 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); phys_addr_t rproc_va_to_pa(void *cpu_addr);
int rproc_trigger_recovery(struct rproc *rproc); int rproc_trigger_recovery(struct rproc *rproc);
...@@ -95,6 +123,8 @@ struct resource_table *rproc_elf_find_loaded_rsc_table(struct rproc *rproc, ...@@ -95,6 +123,8 @@ struct resource_table *rproc_elf_find_loaded_rsc_table(struct rproc *rproc,
const struct firmware *fw); const struct firmware *fw);
struct rproc_mem_entry * struct rproc_mem_entry *
rproc_find_carveout_by_name(struct rproc *rproc, const char *name, ...); 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) static inline int rproc_prepare_device(struct rproc *rproc)
{ {
......
...@@ -9,9 +9,12 @@ ...@@ -9,9 +9,12 @@
* Brian Swetland <swetland@google.com> * Brian Swetland <swetland@google.com>
*/ */
#include <linux/dma-direct.h>
#include <linux/dma-map-ops.h> #include <linux/dma-map-ops.h>
#include <linux/dma-mapping.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/of_reserved_mem.h> #include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/remoteproc.h> #include <linux/remoteproc.h>
#include <linux/virtio.h> #include <linux/virtio.h>
#include <linux/virtio_config.h> #include <linux/virtio_config.h>
...@@ -23,9 +26,32 @@ ...@@ -23,9 +26,32 @@
#include "remoteproc_internal.h" #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) 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) static struct rproc *vdev_to_rproc(struct virtio_device *vdev)
...@@ -322,13 +348,10 @@ static void rproc_virtio_dev_release(struct device *dev) ...@@ -322,13 +348,10 @@ static void rproc_virtio_dev_release(struct device *dev)
{ {
struct virtio_device *vdev = dev_to_virtio(dev); struct virtio_device *vdev = dev_to_virtio(dev);
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
struct rproc *rproc = vdev_to_rproc(vdev);
kfree(vdev); kfree(vdev);
kref_put(&rvdev->refcount, rproc_vdev_release); put_device(&rvdev->pdev->dev);
put_device(&rproc->dev);
} }
/** /**
...@@ -341,10 +364,10 @@ static void rproc_virtio_dev_release(struct device *dev) ...@@ -341,10 +364,10 @@ static void rproc_virtio_dev_release(struct device *dev)
* *
* Return: 0 on success or an appropriate error value otherwise * 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 rproc *rproc = rvdev->rproc;
struct device *dev = &rvdev->dev; struct device *dev = &rvdev->pdev->dev;
struct virtio_device *vdev; struct virtio_device *vdev;
struct rproc_mem_entry *mem; struct rproc_mem_entry *mem;
int ret; int ret;
...@@ -414,18 +437,8 @@ int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id) ...@@ -414,18 +437,8 @@ int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
vdev->dev.parent = dev; vdev->dev.parent = dev;
vdev->dev.release = rproc_virtio_dev_release; 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 */ /* Reference the vdev and vring allocations */
kref_get(&rvdev->refcount); get_device(dev);
ret = register_virtio_device(vdev); ret = register_virtio_device(vdev);
if (ret) { if (ret) {
...@@ -449,10 +462,142 @@ int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id) ...@@ -449,10 +462,142 @@ int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
* *
* Return: 0 * 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); struct virtio_device *vdev = dev_to_virtio(dev);
unregister_virtio_device(vdev); unregister_virtio_device(vdev);
return 0; 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);
...@@ -489,6 +489,20 @@ struct rproc_dump_segment { ...@@ -489,6 +489,20 @@ struct rproc_dump_segment {
loff_t offset; loff_t offset;
}; };
/**
* enum rproc_features - features supported
*
* @RPROC_FEAT_ATTACH_ON_RECOVERY: The remote processor does not need help
* from Linux to recover, such as firmware
* loading. Linux just needs to attach after
* recovery.
*/
enum rproc_features {
RPROC_FEAT_ATTACH_ON_RECOVERY,
RPROC_MAX_FEATURES,
};
/** /**
* struct rproc - represents a physical remote processor device * struct rproc - represents a physical remote processor device
* @node: list node of this rproc object * @node: list node of this rproc object
...@@ -530,6 +544,7 @@ struct rproc_dump_segment { ...@@ -530,6 +544,7 @@ struct rproc_dump_segment {
* @elf_machine: firmware ELF machine * @elf_machine: firmware ELF machine
* @cdev: character device of the rproc * @cdev: character device of the rproc
* @cdev_put_on_release: flag to indicate if remoteproc should be shutdown on @char_dev release * @cdev_put_on_release: flag to indicate if remoteproc should be shutdown on @char_dev release
* @features: indicate remoteproc features
*/ */
struct rproc { struct rproc {
struct list_head node; struct list_head node;
...@@ -570,6 +585,7 @@ struct rproc { ...@@ -570,6 +585,7 @@ struct rproc {
u16 elf_machine; u16 elf_machine;
struct cdev cdev; struct cdev cdev;
bool cdev_put_on_release; bool cdev_put_on_release;
DECLARE_BITMAP(features, RPROC_MAX_FEATURES);
}; };
/** /**
...@@ -616,9 +632,8 @@ struct rproc_vring { ...@@ -616,9 +632,8 @@ struct rproc_vring {
/** /**
* struct rproc_vdev - remoteproc state for a supported virtio device * struct rproc_vdev - remoteproc state for a supported virtio device
* @refcount: reference counter for the vdev and vring allocations
* @subdev: handle for registering the vdev as a rproc subdevice * @subdev: handle for registering the vdev as a rproc subdevice
* @dev: device struct used for reference count semantics * @pdev: remoteproc virtio platform device
* @id: virtio device id (as in virtio_ids.h) * @id: virtio device id (as in virtio_ids.h)
* @node: list node * @node: list node
* @rproc: the rproc handle * @rproc: the rproc handle
...@@ -627,10 +642,9 @@ struct rproc_vring { ...@@ -627,10 +642,9 @@ struct rproc_vring {
* @index: vdev position versus other vdev declared in resource table * @index: vdev position versus other vdev declared in resource table
*/ */
struct rproc_vdev { struct rproc_vdev {
struct kref refcount;
struct rproc_subdev subdev; struct rproc_subdev subdev;
struct device dev; struct platform_device *pdev;
unsigned int id; unsigned int id;
struct list_head node; struct list_head node;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment