Normally used for management of; carveout, devmem and trace memory. Signed-off-by: Lee Jones <lee.jones@xxxxxxxxxx> --- drivers/remoteproc/remoteproc_core.c | 174 +++++++++++++++++++++++++++++++++-- 1 file changed, 167 insertions(+), 7 deletions(-) diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 03720c0..3d9798c 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -209,6 +209,7 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) { struct rproc *rproc = rvdev->rproc; struct device *dev = &rproc->dev; + struct device *dma_dev; struct rproc_vring *rvring = &rvdev->vring[i]; struct fw_rsc_vdev *rsc; dma_addr_t dma; @@ -222,7 +223,8 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) * Allocate non-cacheable memory for the vring. In the future * this call will also configure the IOMMU for us */ - va = dma_alloc_coherent(dev->parent, size, &dma, GFP_KERNEL); + dma_dev = rproc_subdev_lookup(rproc, "vring"); + va = dma_alloc_coherent(dma_dev, size, &dma, GFP_KERNEL); if (!va) { dev_err(dev->parent, "dma_alloc_coherent failed\n"); return -EINVAL; @@ -236,7 +238,7 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) ret = idr_alloc(&rproc->notifyids, rvring, 0, 0, GFP_KERNEL); if (ret < 0) { dev_err(dev, "idr_alloc failed: %d\n", ret); - dma_free_coherent(dev->parent, size, va, dma); + dma_free_coherent(dma_dev, size, va, dma); return ret; } notifyid = ret; @@ -297,8 +299,10 @@ void rproc_free_vring(struct rproc_vring *rvring) struct rproc *rproc = rvring->rvdev->rproc; int idx = rvring->rvdev->vring - rvring; struct fw_rsc_vdev *rsc; + struct device *dma_dev; - dma_free_coherent(rproc->dev.parent, size, rvring->va, rvring->dma); + dma_dev = rproc_subdev_lookup(rproc, "vring"); + dma_free_coherent(dma_dev, size, rvring->va, rvring->dma); idr_remove(&rproc->notifyids, rvring->notifyid); /* reset resource entry info */ @@ -572,6 +576,7 @@ static int rproc_handle_carveout(struct rproc *rproc, { struct rproc_mem_entry *carveout, *mapping; struct device *dev = &rproc->dev; + struct device *dma_dev; dma_addr_t dma; void *va; int ret; @@ -594,7 +599,8 @@ static int rproc_handle_carveout(struct rproc *rproc, if (!carveout) return -ENOMEM; - va = dma_alloc_coherent(dev->parent, rsc->len, &dma, GFP_KERNEL); + dma_dev = rproc_subdev_lookup(rproc, "carveout"); + va = dma_alloc_coherent(dma_dev, rsc->len, &dma, GFP_KERNEL); if (!va) { dev_err(dev->parent, "dma_alloc_coherent err: %d\n", rsc->len); ret = -ENOMEM; @@ -682,7 +688,7 @@ static int rproc_handle_carveout(struct rproc *rproc, free_mapping: kfree(mapping); dma_free: - dma_free_coherent(dev->parent, rsc->len, va, dma); + dma_free_coherent(dma_dev, rsc->len, va, dma); free_carv: kfree(carveout); return ret; @@ -766,6 +772,7 @@ static void rproc_resource_cleanup(struct rproc *rproc) { struct rproc_mem_entry *entry, *tmp; struct device *dev = &rproc->dev; + struct device *dma_dev; /* clean up debugfs trace entries */ list_for_each_entry_safe(entry, tmp, &rproc->traces, node) { @@ -791,9 +798,9 @@ static void rproc_resource_cleanup(struct rproc *rproc) } /* clean up carveout allocations */ + dma_dev = rproc_subdev_lookup(rproc, "carveout"); list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) { - dma_free_coherent(dev->parent, entry->len, entry->va, - entry->dma); + dma_free_coherent(dma_dev, entry->len, entry->va, entry->dma); list_del(&entry->node); kfree(entry); } @@ -1329,6 +1336,156 @@ struct rproc *rproc_get_by_phandle(phandle phandle) #endif EXPORT_SYMBOL(rproc_get_by_phandle); +/* + * resource structure of rproc_subdev is used for identify the right subdevice + * that has the dma coherent memory. + */ +static int rproc_subdev_match(struct device *dev, void *data) +{ + char *sub_name; + + if (!dev_name(dev)) + return 0; + + sub_name = strpbrk(dev_name(dev), "#"); + if (!sub_name) + return 0; + + return !strcmp(++sub_name, (char *)data); +} + +/* + * find the subdevice child dma coherent memory that match with name region + * the rproc parent is the default device, if there is no match + */ +struct device *rproc_subdev_lookup(struct rproc *rproc, const char *name) +{ + struct device *dev; + + dev = device_find_child(rproc->dev.parent, (void *)name, + rproc_subdev_match); + if (dev) { + /* decrement the matched device's refcount back */ + put_device(dev); + return dev; + } + + return rproc->dev.parent; +} +EXPORT_SYMBOL(rproc_subdev_lookup); + +/** + * rproc_subdev_release() - release the existence of a subdevice + * + * @dev: the subdevice's dev + */ +static void rproc_subdev_release(struct device *dev) +{ + struct rproc_subdev *sub = to_subdevice(dev); + + kfree(sub); +} + +/** + * rproc_subdev_unregister() - unregister sub-device of remote processor + * + * @dev: rproc sub-device + * @data: Not use (just to be compliant with device_for_each_child) + * + * This function is called by device_for_each_child function when unregister + * remote processor. + */ +static int rproc_subdev_unregister(struct device *dev, void *data) +{ + struct rproc_subdev *sub = to_subdevice(dev); + struct rproc *rproc = data; + + if (dev != &(rproc->dev)) + rproc_subdev_del(sub); + return 0; +} + +/** + * rproc_subdev_add() - add a sub-device on remote processor + * + * @rproc: the parent remote processor + * @res: resource allow to define the dma coherent memory of sub-device + * + * This function add a sub-device child on rproc parent. This sub-device allow + * to define a new dma coherent memory area. when the rproc would alloc a + * dma coherent memory it's find the subdevice that match with physical memory + * asked (if there is no children that match, the rproc is the default device) + * + * Returns the sub-device handle on success, and error on failure. + */ +struct rproc_subdev *rproc_subdev_add(struct rproc *rproc, struct resource *res) +{ + struct rproc_subdev *sub; + int ret; + + if (!res || res->flags != IORESOURCE_MEM || res->name == NULL) { + ret = -EINVAL; + goto err; + } + + sub = kzalloc(sizeof(*sub), GFP_KERNEL); + if (!sub) { + ret = -ENOMEM; + goto err; + } + + sub->rproc = rproc; + sub->res = res; + sub->dev.parent = rproc->dev.parent; + sub->dev.release = rproc_subdev_release; + dev_set_name(&sub->dev, "%s#%s", dev_name(sub->dev.parent), res->name); + dev_set_drvdata(&sub->dev, sub); + + ret = device_register(&sub->dev); + if (ret) + goto err_dev; + + if (!devm_request_mem_region(&sub->dev, res->start, + resource_size(res), + dev_name(&sub->dev))) { + dev_err(&rproc->dev, "failed to get memory region\n"); + ret = -EINVAL; + goto err_dev; + } + + ret = dmam_declare_coherent_memory(&sub->dev, + res->start, res->start, + resource_size(res), + DMA_MEMORY_MAP | + DMA_MEMORY_EXCLUSIVE); + if (ret < 0) + goto err_dev; + + return sub; + +err_dev: + put_device(&sub->dev); +err: + dev_err(&rproc->dev, "unable to register subdev %s, err = %d\n", + (res && res->name) ? res->name : "unnamed", ret); + return ERR_PTR(ret); +} +EXPORT_SYMBOL(rproc_subdev_add); + +/** + * rproc_subdev_del() - delete a sub-device of remote processor + * + * @subdev: rproc sub-device + */ +void rproc_subdev_del(struct rproc_subdev *subdev) +{ + if (get_device(&subdev->dev)) { + device_unregister(&subdev->dev); + put_device(&subdev->dev); + } +} +EXPORT_SYMBOL(rproc_subdev_del); + /** * rproc_set_fw_name() - change rproc fw name * @rproc: rproc handle @@ -1618,6 +1775,9 @@ int rproc_del(struct rproc *rproc) kfree(rproc->cached_table); rproc->cached_table = NULL; + device_for_each_child(rproc->dev.parent, rproc, + rproc_subdev_unregister); + /* the rproc is downref'ed as soon as it's removed from the klist */ mutex_lock(&rproc_list_mutex); list_del(&rproc->node); -- 2.8.0 -- To unsubscribe from this list: send the line "unsubscribe linux-remoteproc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html