/* * Based on work from: * Andrew Andrianov * 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 #include #include #include #include #include #include #include #include #include #include #include #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 #include #include 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) { } 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