186 lines
4.2 KiB
C
186 lines
4.2 KiB
C
/*
|
|
* 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 (!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
|