blob: f71f514bd4575ef5fc2092b5fb5d072c8adc6442 [file] [log] [blame]
/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/err.h>
#include <linux/ion.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/memory_alloc.h>
#include <mach/ion.h>
#include <mach/msm_memtypes.h>
#include "../ion_priv.h"
static struct ion_device *idev;
static int num_heaps;
static struct ion_heap **heaps;
struct ion_client *msm_ion_client_create(unsigned int heap_mask,
const char *name)
{
return ion_client_create(idev, heap_mask, name);
}
EXPORT_SYMBOL(msm_ion_client_create);
int msm_ion_secure_heap(int heap_id)
{
return ion_secure_heap(idev, heap_id);
}
EXPORT_SYMBOL(msm_ion_secure_heap);
int msm_ion_unsecure_heap(int heap_id)
{
return ion_unsecure_heap(idev, heap_id);
}
EXPORT_SYMBOL(msm_ion_unsecure_heap);
static unsigned long msm_ion_get_base(unsigned long size, int memory_type,
unsigned int align)
{
switch (memory_type) {
case ION_EBI_TYPE:
return allocate_contiguous_ebi_nomap(size, align);
break;
case ION_SMI_TYPE:
return allocate_contiguous_memory_nomap(size, MEMTYPE_SMI,
align);
break;
default:
pr_err("%s: Unknown memory type %d\n", __func__, memory_type);
return 0;
}
}
static struct ion_platform_heap *find_heap(const struct ion_platform_heap
heap_data[],
unsigned int nr_heaps,
int heap_id)
{
unsigned int i;
for (i = 0; i < nr_heaps; ++i) {
const struct ion_platform_heap *heap = &heap_data[i];
if (heap->id == heap_id)
return (struct ion_platform_heap *) heap;
}
return 0;
}
static void allocate_co_memory(struct ion_platform_heap *heap,
struct ion_platform_heap heap_data[],
unsigned int nr_heaps)
{
struct ion_co_heap_pdata *co_heap_data =
(struct ion_co_heap_pdata *) heap->extra_data;
if (co_heap_data->adjacent_mem_id != INVALID_HEAP_ID) {
struct ion_platform_heap *shared_heap =
find_heap(heap_data, nr_heaps,
co_heap_data->adjacent_mem_id);
if (shared_heap) {
struct ion_cp_heap_pdata *cp_data =
(struct ion_cp_heap_pdata *) shared_heap->extra_data;
heap->base = msm_ion_get_base(
heap->size + shared_heap->size,
shared_heap->memory_type,
co_heap_data->align);
if (heap->base) {
shared_heap->base = heap->base + heap->size;
cp_data->secure_base = heap->base;
cp_data->secure_size =
heap->size + shared_heap->size;
} else {
pr_err("%s: could not get memory for heap %s "
"(id %x)\n", __func__, heap->name, heap->id);
}
}
}
}
/* Fixup heaps in board file to support two heaps being adjacent to each other.
* A flag (adjacent_mem_id) in the platform data tells us that the heap phy
* memory location must be adjacent to the specified heap. We do this by
* carving out memory for both heaps and then splitting up the memory to the
* two heaps. The heap specifying the "adjacent_mem_id" get the base of the
* memory while heap specified in "adjacent_mem_id" get base+size as its
* base address.
* Note: Modifies platform data and allocates memory.
*/
static void msm_ion_heap_fixup(struct ion_platform_heap heap_data[],
unsigned int nr_heaps)
{
unsigned int i;
for (i = 0; i < nr_heaps; i++) {
struct ion_platform_heap *heap = &heap_data[i];
if (!heap->base && heap->type == ION_HEAP_TYPE_CARVEOUT) {
if (heap->extra_data)
allocate_co_memory(heap, heap_data, nr_heaps);
}
}
}
static void msm_ion_allocate(struct ion_platform_heap *heap)
{
if (!heap->base && heap->extra_data) {
unsigned int align = 0;
switch (heap->type) {
case ION_HEAP_TYPE_CARVEOUT:
align =
((struct ion_co_heap_pdata *) heap->extra_data)->align;
break;
case ION_HEAP_TYPE_CP:
align =
((struct ion_cp_heap_pdata *) heap->extra_data)->align;
break;
default:
break;
}
if (align) {
heap->base = msm_ion_get_base(heap->size,
heap->memory_type,
align);
if (!heap->base)
pr_err("%s: could not get memory for heap %s "
"(id %x)\n", __func__, heap->name, heap->id);
}
}
}
static int msm_ion_probe(struct platform_device *pdev)
{
struct ion_platform_data *pdata = pdev->dev.platform_data;
int err;
int i;
num_heaps = pdata->nr;
heaps = kcalloc(pdata->nr, sizeof(struct ion_heap *), GFP_KERNEL);
if (!heaps) {
err = -ENOMEM;
goto out;
}
idev = ion_device_create(NULL);
if (IS_ERR_OR_NULL(idev)) {
err = PTR_ERR(idev);
goto freeheaps;
}
msm_ion_heap_fixup(pdata->heaps, num_heaps);
/* create the heaps as specified in the board file */
for (i = 0; i < num_heaps; i++) {
struct ion_platform_heap *heap_data = &pdata->heaps[i];
msm_ion_allocate(heap_data);
heaps[i] = ion_heap_create(heap_data);
if (IS_ERR_OR_NULL(heaps[i])) {
heaps[i] = 0;
continue;
} else {
if (heap_data->size)
pr_info("ION heap %s created at %lx "
"with size %x\n", heap_data->name,
heap_data->base,
heap_data->size);
else
pr_info("ION heap %s created\n",
heap_data->name);
}
ion_device_add_heap(idev, heaps[i]);
}
platform_set_drvdata(pdev, idev);
return 0;
freeheaps:
kfree(heaps);
out:
return err;
}
static int msm_ion_remove(struct platform_device *pdev)
{
struct ion_device *idev = platform_get_drvdata(pdev);
int i;
for (i = 0; i < num_heaps; i++)
ion_heap_destroy(heaps[i]);
ion_device_destroy(idev);
kfree(heaps);
return 0;
}
static struct platform_driver msm_ion_driver = {
.probe = msm_ion_probe,
.remove = msm_ion_remove,
.driver = { .name = "ion-msm" }
};
static int __init msm_ion_init(void)
{
return platform_driver_register(&msm_ion_driver);
}
static void __exit msm_ion_exit(void)
{
platform_driver_unregister(&msm_ion_driver);
}
subsys_initcall(msm_ion_init);
module_exit(msm_ion_exit);