| /* |
| * Based on work from: |
| * Andrew Andrianov <andrew@ncrmnt.org> |
| * Google |
| * The Linux Foundation |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| */ |
| |
| #include <linux/init.h> |
| #include <linux/platform_device.h> |
| #include <linux/slab.h> |
| #include <linux/of.h> |
| #include <linux/of_platform.h> |
| #include <linux/of_address.h> |
| #include <linux/clk.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/cma.h> |
| #include <linux/dma-contiguous.h> |
| #include <linux/io.h> |
| #include <linux/of_reserved_mem.h> |
| #include "ion.h" |
| #include "ion_priv.h" |
| #include "ion_of.h" |
| |
| static int ion_parse_dt_heap_common(struct device_node *heap_node, |
| struct ion_platform_heap *heap, |
| struct ion_of_heap *compatible) |
| { |
| int i; |
| |
| for (i = 0; compatible[i].name; i++) { |
| if (of_device_is_compatible(heap_node, compatible[i].compat)) |
| break; |
| } |
| |
| if (!compatible[i].name) |
| return -ENODEV; |
| |
| heap->id = compatible[i].heap_id; |
| heap->type = compatible[i].type; |
| heap->name = compatible[i].name; |
| heap->align = compatible[i].align; |
| |
| /* Some kind of callback function pointer? */ |
| |
| pr_info("%s: id %d type %d name %s align %lx\n", __func__, |
| heap->id, heap->type, heap->name, heap->align); |
| return 0; |
| } |
| |
| static int ion_setup_heap_common(struct platform_device *parent, |
| struct device_node *heap_node, |
| struct ion_platform_heap *heap) |
| { |
| int ret = 0; |
| |
| switch (heap->type) { |
| case ION_HEAP_TYPE_CARVEOUT: |
| case ION_HEAP_TYPE_CHUNK: |
| if (heap->base && heap->size) |
| return 0; |
| |
| ret = of_reserved_mem_device_init(heap->priv); |
| break; |
| default: |
| break; |
| } |
| |
| return ret; |
| } |
| |
| struct ion_platform_data *ion_parse_dt(struct platform_device *pdev, |
| struct ion_of_heap *compatible) |
| { |
| int num_heaps, ret; |
| const struct device_node *dt_node = pdev->dev.of_node; |
| struct device_node *node; |
| struct ion_platform_heap *heaps; |
| struct ion_platform_data *data; |
| int i = 0; |
| |
| num_heaps = of_get_available_child_count(dt_node); |
| |
| if (!num_heaps) |
| return ERR_PTR(-EINVAL); |
| |
| heaps = devm_kzalloc(&pdev->dev, |
| sizeof(struct ion_platform_heap) * num_heaps, |
| GFP_KERNEL); |
| if (!heaps) |
| return ERR_PTR(-ENOMEM); |
| |
| data = devm_kzalloc(&pdev->dev, sizeof(struct ion_platform_data), |
| GFP_KERNEL); |
| if (!data) |
| return ERR_PTR(-ENOMEM); |
| |
| for_each_available_child_of_node(dt_node, node) { |
| struct platform_device *heap_pdev; |
| |
| ret = ion_parse_dt_heap_common(node, &heaps[i], compatible); |
| if (ret) |
| return ERR_PTR(ret); |
| |
| heap_pdev = of_platform_device_create(node, heaps[i].name, |
| &pdev->dev); |
| if (!heap_pdev) |
| return ERR_PTR(-ENOMEM); |
| heap_pdev->dev.platform_data = &heaps[i]; |
| |
| heaps[i].priv = &heap_pdev->dev; |
| |
| ret = ion_setup_heap_common(pdev, node, &heaps[i]); |
| if (ret) |
| goto out_err; |
| i++; |
| } |
| |
| data->heaps = heaps; |
| data->nr = num_heaps; |
| return data; |
| |
| out_err: |
| for ( ; i >= 0; i--) |
| if (heaps[i].priv) |
| of_device_unregister(to_platform_device(heaps[i].priv)); |
| |
| return ERR_PTR(ret); |
| } |
| |
| void ion_destroy_platform_data(struct ion_platform_data *data) |
| { |
| int i; |
| |
| for (i = 0; i < data->nr; i++) |
| if (data->heaps[i].priv) |
| of_device_unregister(to_platform_device( |
| data->heaps[i].priv)); |
| } |
| |
| #ifdef CONFIG_OF_RESERVED_MEM |
| #include <linux/of.h> |
| #include <linux/of_fdt.h> |
| #include <linux/of_reserved_mem.h> |
| |
| static int rmem_ion_device_init(struct reserved_mem *rmem, struct device *dev) |
| { |
| struct platform_device *pdev = to_platform_device(dev); |
| struct ion_platform_heap *heap = pdev->dev.platform_data; |
| |
| heap->base = rmem->base; |
| heap->base = rmem->size; |
| pr_debug("%s: heap %s base %pa size %pa dev %p\n", __func__, |
| heap->name, &rmem->base, &rmem->size, dev); |
| return 0; |
| } |
| |
| static void rmem_ion_device_release(struct reserved_mem *rmem, |
| struct device *dev) |
| { |
| return; |
| } |
| |
| static const struct reserved_mem_ops rmem_dma_ops = { |
| .device_init = rmem_ion_device_init, |
| .device_release = rmem_ion_device_release, |
| }; |
| |
| static int __init rmem_ion_setup(struct reserved_mem *rmem) |
| { |
| phys_addr_t size = rmem->size; |
| |
| size = size / 1024; |
| |
| pr_info("Ion memory setup at %pa size %pa MiB\n", |
| &rmem->base, &size); |
| rmem->ops = &rmem_dma_ops; |
| return 0; |
| } |
| |
| RESERVEDMEM_OF_DECLARE(ion, "ion-region", rmem_ion_setup); |
| #endif |