The goal of those patches is to allow ION clients (drivers or userland applications) to use Contiguous Memory Allocator (CMA).
To get more info about CMA: http://lists.linaro.org/pipermail/linaro-mm-sig/2012-February/001328.html
patches version 6: - add private field in ion_platform_heap to pass the device linked with CMA. - rework CMA heap to use private field. - prepare CMA heap for incoming dma_common_get_sgtable function http://lists.linaro.org/pipermail/linaro-mm-sig/2012-June/002109.html - simplify ion-ux500 driver.
patches version 5: - port patches on android kernel 3.4 where ION use dmabuf - add ion_cma_heap_map_dma and ion_cma_heap_unmap_dma functions
patches version 4: - add ION_HEAP_TYPE_DMA heap type in ion_heap_type enum. - CMA heap is now a "native" ION heap. - add ion_heap_create_full function to keep backward compatibilty. - clean up included files in CMA heap - ux500-ion is using ion_heap_create_full instead of ion_heap_create
patches version 3: - add a private field in ion_heap structure instead of expose ion_device structure to all heaps - ion_cma_heap is no more a platform driver - ion_cma_heap use ion_heap private field to store the device pointer and make the link with reserved CMA regions - provide ux500-ion driver and configuration file for snowball board to give an example of how use CMA heaps
patches version 2: - fix comments done by Andy Green
Benjamin Gaignard (4): fix ion_platform_data definition add private field in ion_heap and ion_platform_heap structure add CMA heap add test/example driver for ux500 platform
arch/arm/mach-ux500/board-mop500.c | 64 +++++++++++++ drivers/gpu/ion/Kconfig | 5 + drivers/gpu/ion/Makefile | 5 +- drivers/gpu/ion/ion_cma_heap.c | 179 ++++++++++++++++++++++++++++++++++++ drivers/gpu/ion/ion_heap.c | 11 +++ drivers/gpu/ion/ion_priv.h | 8 ++ drivers/gpu/ion/ux500/Makefile | 1 + drivers/gpu/ion/ux500/ux500_ion.c | 134 +++++++++++++++++++++++++++ include/linux/ion.h | 7 +- 9 files changed, 412 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/ion/ion_cma_heap.c create mode 100644 drivers/gpu/ion/ux500/Makefile create mode 100644 drivers/gpu/ion/ux500/ux500_ion.c
fix ion_platform_heap to make is use an usual way in board configuration file.
Signed-off-by: Benjamin Gaignard benjamin.gaignard@linaro.org --- include/linux/ion.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/linux/ion.h b/include/linux/ion.h index ba09e70..a03b3a3 100644 --- a/include/linux/ion.h +++ b/include/linux/ion.h @@ -85,7 +85,7 @@ struct ion_platform_heap { */ struct ion_platform_data { int nr; - struct ion_platform_heap heaps[]; + struct ion_platform_heap *heaps; };
/**
copy private field from platform configuration to internal heap structure.
Signed-off-by: Benjamin Gaignard benjamin.gaignard@linaro.org --- drivers/gpu/ion/ion_heap.c | 1 + drivers/gpu/ion/ion_priv.h | 2 ++ include/linux/ion.h | 2 ++ 3 files changed, 5 insertions(+)
diff --git a/drivers/gpu/ion/ion_heap.c b/drivers/gpu/ion/ion_heap.c index 8ce3c19..f9ab1df 100644 --- a/drivers/gpu/ion/ion_heap.c +++ b/drivers/gpu/ion/ion_heap.c @@ -47,6 +47,7 @@ struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data)
heap->name = heap_data->name; heap->id = heap_data->id; + heap->priv = heap_data->priv; return heap; }
diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h index cf4a960..58945ab 100644 --- a/drivers/gpu/ion/ion_priv.h +++ b/drivers/gpu/ion/ion_priv.h @@ -99,6 +99,7 @@ struct ion_heap_ops { * allocating. These are specified by platform data and * MUST be unique * @name: used for debugging + * @priv: heap private data * * Represents a pool of memory from which buffers can be made. In some * systems the only heap is regular system memory allocated via vmalloc. @@ -112,6 +113,7 @@ struct ion_heap { struct ion_heap_ops *ops; int id; const char *name; + void *priv; };
/** diff --git a/include/linux/ion.h b/include/linux/ion.h index a03b3a3..b914dbc 100644 --- a/include/linux/ion.h +++ b/include/linux/ion.h @@ -65,6 +65,7 @@ struct ion_buffer; * @name: used for debug purposes * @base: base address of heap in physical memory if applicable * @size: size of the heap in bytes if applicable + * @priv: heap private data * * Provided by the board file. */ @@ -74,6 +75,7 @@ struct ion_platform_heap { const char *name; ion_phys_addr_t base; size_t size; + void *priv; };
/**
New heap type ION_HEAP_TYPE_DMA where allocation is done with dma_alloc_coherent API. device coherent_dma_mask must be set to DMA_BIT_MASK(32). ion_platform_heap private field is used to retrieve the device linked to CMA, if NULL the default CMA area is used. ion_cma_get_sgtable is a copy of dma_common_get_sgtable function which should be in kernel 3.5
Signed-off-by: Benjamin Gaignard benjamin.gaignard@linaro.org --- drivers/gpu/ion/Makefile | 4 +- drivers/gpu/ion/ion_cma_heap.c | 179 ++++++++++++++++++++++++++++++++++++++++ drivers/gpu/ion/ion_heap.c | 10 +++ drivers/gpu/ion/ion_priv.h | 6 ++ include/linux/ion.h | 3 + 5 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/ion/ion_cma_heap.c
diff --git a/drivers/gpu/ion/Makefile b/drivers/gpu/ion/Makefile index 73fe3fa..32d3385 100644 --- a/drivers/gpu/ion/Makefile +++ b/drivers/gpu/ion/Makefile @@ -1,2 +1,4 @@ -obj-$(CONFIG_ION) += ion.o ion_heap.o ion_system_heap.o ion_carveout_heap.o +ion-driver-objs += ion.o ion_heap.o ion_system_heap.o ion_carveout_heap.o +obj-$(CONFIG_ION) += ion-driver.o +obj-$(CONFIG_CMA) += ion_cma_heap.o obj-$(CONFIG_ION_TEGRA) += tegra/ diff --git a/drivers/gpu/ion/ion_cma_heap.c b/drivers/gpu/ion/ion_cma_heap.c new file mode 100644 index 0000000..6c8dd6d --- /dev/null +++ b/drivers/gpu/ion/ion_cma_heap.c @@ -0,0 +1,179 @@ +/* + * drivers/gpu/ion/ion_cma_heap.c + * + * Copyright (C) Linaro 2012 + * Author: benjamin.gaignard@linaro.org for ST-Ericsson. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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/device.h> +#include <linux/ion.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/dma-mapping.h> + +/* for ion_heap_ops structure */ +#include "ion_priv.h" + +#define ION_CMA_ALLOCATE_FAILED -1 + +struct ion_cma_buffer_info { + void *cpu_addr; + dma_addr_t handle; + struct sg_table *table; +}; + +/* + * Create scatter-list for the already allocated DMA buffer. + * This function could be replace by dma_common_get_sgtable + * as soon as it will avalaible. + */ +int ion_cma_get_sgtable(struct device *dev, struct sg_table *sgt, + void *cpu_addr, dma_addr_t handle, size_t size) +{ + struct page *page = virt_to_page(cpu_addr); + int ret; + + ret = sg_alloc_table(sgt, 1, GFP_KERNEL); + if (unlikely(ret)) + return ret; + + sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0); + return 0; +} + +/* ION CMA heap operations functions */ +static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer, + unsigned long len, unsigned long align, + unsigned long flags) +{ + struct device *dev = heap->priv; + struct ion_cma_buffer_info *info; + + dev_dbg(dev, "Request buffer allocation len %ld\n", len); + + info = kzalloc(sizeof(struct ion_cma_buffer_info), GFP_KERNEL); + if (!info) { + dev_err(dev, "Can't allocate buffer info\n"); + return ION_CMA_ALLOCATE_FAILED; + } + + info->cpu_addr = dma_alloc_coherent(dev, len, &(info->handle), 0); + + if (!info->cpu_addr) { + dev_err(dev, "Fail to allocate buffer\n"); + goto err; + } + + info->table = kmalloc(sizeof(struct sg_table), GFP_KERNEL); + if (!info->table) { + dev_err(dev, "Fail to allocate sg table\n"); + goto err; + } + + ion_cma_get_sgtable(dev, + info->table, info->cpu_addr, info->handle, len); + + /* keep this for memory release */ + buffer->priv_virt = info; + dev_dbg(dev, "Allocate buffer %p\n", buffer); + return 0; + +err: + kfree(info); + return ION_CMA_ALLOCATE_FAILED; +} + +static void ion_cma_free(struct ion_buffer *buffer) +{ + struct device *dev = buffer->heap->priv; + struct ion_cma_buffer_info *info = buffer->priv_virt; + + dev_dbg(dev, "Release buffer %p\n", buffer); + /* release memory */ + dma_free_coherent(dev, buffer->size, info->cpu_addr, info->handle); + /* release sg table */ + kfree(info->table); + kfree(info); +} + +/* return physical address in addr */ +static int ion_cma_phys(struct ion_heap *heap, struct ion_buffer *buffer, + ion_phys_addr_t *addr, size_t *len) +{ + struct device *dev = heap->priv; + struct ion_cma_buffer_info *info = buffer->priv_virt; + + dev_dbg(dev, "Return buffer %p physical address 0x%x\n", buffer, + virt_to_phys(info->cpu_addr)); + + *addr = virt_to_phys(info->cpu_addr); + *len = buffer->size; + + return 0; +} + +struct sg_table *ion_cma_heap_map_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + struct ion_cma_buffer_info *info = buffer->priv_virt; + + return info->table; +} + +void ion_cma_heap_unmap_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + return; +} + +static int ion_cma_mmap(struct ion_heap *mapper, struct ion_buffer *buffer, + struct vm_area_struct *vma) +{ + struct device *dev = buffer->heap->priv; + struct ion_cma_buffer_info *info = buffer->priv_virt; + + return dma_mmap_coherent(dev, vma, info->cpu_addr, info->handle, + buffer->size); +} + +static struct ion_heap_ops ion_cma_ops = { + .allocate = ion_cma_allocate, + .free = ion_cma_free, + .map_dma = ion_cma_heap_map_dma, + .unmap_dma = ion_cma_heap_unmap_dma, + .phys = ion_cma_phys, + .map_user = ion_cma_mmap, +}; + +struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *data) +{ + struct ion_heap *heap; + + heap = kzalloc(sizeof(struct ion_heap), GFP_KERNEL); + + if (!heap) + return ERR_PTR(-ENOMEM); + + heap->ops = &ion_cma_ops; + /* set device as private heaps data, later it will be + * used to make the link with reserved CMA memory */ + heap->priv = data->priv; + heap->type = ION_HEAP_TYPE_DMA; + return heap; +} + +void ion_cma_heap_destroy(struct ion_heap *heap) +{ + kfree(heap); +} diff --git a/drivers/gpu/ion/ion_heap.c b/drivers/gpu/ion/ion_heap.c index f9ab1df..8e56f00 100644 --- a/drivers/gpu/ion/ion_heap.c +++ b/drivers/gpu/ion/ion_heap.c @@ -32,6 +32,11 @@ struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data) case ION_HEAP_TYPE_CARVEOUT: heap = ion_carveout_heap_create(heap_data); break; +#ifdef CONFIG_CMA + case ION_HEAP_TYPE_DMA: + heap = ion_cma_heap_create(heap_data); + break; +#endif default: pr_err("%s: Invalid heap type %d\n", __func__, heap_data->type); @@ -66,6 +71,11 @@ void ion_heap_destroy(struct ion_heap *heap) case ION_HEAP_TYPE_CARVEOUT: ion_carveout_heap_destroy(heap); break; +#ifdef CONFIG_CMA + case ION_HEAP_TYPE_DMA: + ion_cma_heap_destroy(heap); + break; +#endif default: pr_err("%s: Invalid heap type %d\n", __func__, heap->type); diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h index 58945ab..727a182 100644 --- a/drivers/gpu/ion/ion_priv.h +++ b/drivers/gpu/ion/ion_priv.h @@ -165,6 +165,12 @@ ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap, unsigned long size, unsigned long align); void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr, unsigned long size); + +#ifdef CONFIG_CMA +struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *); +void ion_cma_heap_destroy(struct ion_heap *); +#endif + /** * The carveout heap returns physical addresses, since 0 may be a valid * physical address, this is used to indicate allocation failed diff --git a/include/linux/ion.h b/include/linux/ion.h index b914dbc..c29f346 100644 --- a/include/linux/ion.h +++ b/include/linux/ion.h @@ -27,6 +27,7 @@ struct ion_handle; * @ION_HEAP_TYPE_CARVEOUT: memory allocated from a prereserved * carveout heap, allocations are physically * contiguous + * @ION_HEAP_TYPE_DMA: memory allocated via DMA API * @ION_NUM_HEAPS: helper for iterating over heaps, a bit mask * is used to identify the heaps, so only 32 * total heap types are supported @@ -35,6 +36,7 @@ enum ion_heap_type { ION_HEAP_TYPE_SYSTEM, ION_HEAP_TYPE_SYSTEM_CONTIG, ION_HEAP_TYPE_CARVEOUT, + ION_HEAP_TYPE_DMA, ION_HEAP_TYPE_CUSTOM, /* must be last so device specific heaps always are at the end of this enum */ ION_NUM_HEAPS = 16, @@ -43,6 +45,7 @@ enum ion_heap_type { #define ION_HEAP_SYSTEM_MASK (1 << ION_HEAP_TYPE_SYSTEM) #define ION_HEAP_SYSTEM_CONTIG_MASK (1 << ION_HEAP_TYPE_SYSTEM_CONTIG) #define ION_HEAP_CARVEOUT_MASK (1 << ION_HEAP_TYPE_CARVEOUT) +#define ION_HEAP_TYPE_DMA_MASK (1 << ION_HEAP_TYPE_DMA)
#ifdef __KERNEL__ struct ion_device;
DO NOT MERGE ux500-ion driver is provided as example. Define 2 CMA heaps, one on a specific CMA region reserved at boot time the other will use the default CMA region.
Signed-off-by: Benjamin Gaignard benjamin.gaignard@linaro.org --- arch/arm/mach-ux500/board-mop500.c | 64 +++++++++++++++++ drivers/gpu/ion/Kconfig | 5 ++ drivers/gpu/ion/Makefile | 1 + drivers/gpu/ion/ux500/Makefile | 1 + drivers/gpu/ion/ux500/ux500_ion.c | 134 ++++++++++++++++++++++++++++++++++++ 5 files changed, 205 insertions(+) create mode 100644 drivers/gpu/ion/ux500/Makefile create mode 100644 drivers/gpu/ion/ux500/ux500_ion.c
diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index 77d03c1..c91cd8f 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -30,6 +30,13 @@ #include <linux/gpio_keys.h> #include <linux/delay.h>
+#ifdef CONFIG_ION +#include <linux/ion.h> +#endif +#ifdef CONFIG_CMA +#include <linux/dma-contiguous.h> +#endif + #include <linux/of.h> #include <linux/of_platform.h>
@@ -54,6 +61,50 @@ #include "board-mop500.h" #include "board-mop500-regulators.h"
+#ifdef CONFIG_CMA +static u64 snowball_dmamask = DMA_BIT_MASK(32); + +static struct platform_device snowball_cma_device = { + .name = "snowball-cma-device", + .id = -1, + .dev = { + .dma_mask = &snowball_dmamask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, + .num_resources = 0, +}; + +#ifdef CONFIG_ION +static struct ion_platform_heap snowball_ion_heaps[] = { + [0] = { + .type = ION_HEAP_TYPE_DMA, + .id = 0, + .name = "ion-cma-heap-1", + .priv = &snowball_cma_device.dev, + }, + [1] = { + .type = ION_HEAP_TYPE_DMA, + .id = 1, + .name = "ion-cma-heap-2", + }, +}; + +static struct ion_platform_data snowball_ion_data = { + .heaps = snowball_ion_heaps, + .nr = ARRAY_SIZE(snowball_ion_heaps), +}; + +static struct platform_device snowball_ion_device = { + .name = "ion-ux500", + .id = -1, + .dev = { + .platform_data = &snowball_ion_data, + }, + .num_resources = 0, +}; +#endif +#endif + static struct gpio_led snowball_led_array[] = { { .name = "user_led", @@ -607,8 +658,18 @@ static struct platform_device *snowball_platform_devs[] __initdata = { &snowball_key_dev, &snowball_sbnet_dev, &ab8500_device, +#ifdef CONFIG_ION + &snowball_ion_device, +#endif };
+#ifdef CONFIG_ION +static void __init mop500_reserve(void) +{ + dma_declare_contiguous(&snowball_cma_device.dev, (16 * SZ_1M), 0, 0); +} +#endif + static void __init mop500_init_machine(void) { struct device *parent = NULL; @@ -741,6 +802,9 @@ MACHINE_START(SNOWBALL, "Calao Systems Snowball platform") .timer = &ux500_timer, .handle_irq = gic_handle_irq, .init_machine = snowball_init_machine, +#ifdef CONFIG_ION + .reserve = mop500_reserve, +#endif MACHINE_END
#ifdef CONFIG_MACH_UX500_DT diff --git a/drivers/gpu/ion/Kconfig b/drivers/gpu/ion/Kconfig index b5bfdb4..bfe572d 100644 --- a/drivers/gpu/ion/Kconfig +++ b/drivers/gpu/ion/Kconfig @@ -11,3 +11,8 @@ config ION_TEGRA help Choose this option if you wish to use ion on an nVidia Tegra.
+config ION_UX500 + tristate "Ion for ux500" + depends on ARCH_U8500 && ION + help + Choose this option if you wish to use ion on ux500 platforms. diff --git a/drivers/gpu/ion/Makefile b/drivers/gpu/ion/Makefile index 32d3385..a7ea570 100644 --- a/drivers/gpu/ion/Makefile +++ b/drivers/gpu/ion/Makefile @@ -2,3 +2,4 @@ ion-driver-objs += ion.o ion_heap.o ion_system_heap.o ion_carveout_heap.o obj-$(CONFIG_ION) += ion-driver.o obj-$(CONFIG_CMA) += ion_cma_heap.o obj-$(CONFIG_ION_TEGRA) += tegra/ +obj-$(CONFIG_ION_UX500) += ux500/ diff --git a/drivers/gpu/ion/ux500/Makefile b/drivers/gpu/ion/ux500/Makefile new file mode 100644 index 0000000..691c600 --- /dev/null +++ b/drivers/gpu/ion/ux500/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_ION_UX500) += ux500_ion.o diff --git a/drivers/gpu/ion/ux500/ux500_ion.c b/drivers/gpu/ion/ux500/ux500_ion.c new file mode 100644 index 0000000..5d5cf77 --- /dev/null +++ b/drivers/gpu/ion/ux500/ux500_ion.c @@ -0,0 +1,134 @@ +/* + * drivers/gpu/ion/ux500/ux500_ion.c + * + * Copyright (C) Linaro 2012 + * Author: benjamin.gaignard@linaro.org for ST-Ericsson. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 "../ion_priv.h" + +struct ion_ux500_internal { + struct ion_device *ion_device; + int num_heaps; + struct ion_heap **heaps; +}; + +int ux500_ion_probe(struct platform_device *pdev) +{ + struct ion_platform_data *pdata = pdev->dev.platform_data; + struct ion_ux500_internal *ion_ux500; + int ret = 0; + int i; + + ion_ux500 = kzalloc(sizeof(struct ion_ux500_internal), GFP_KERNEL); + if (!ion_ux500) { + dev_err(&pdev->dev, "can't allocate memory\n"); + return -ENOMEM; + } + + ion_ux500->num_heaps = pdata->nr; + if (!ion_ux500->num_heaps) { + dev_err(&pdev->dev, "no heap defined\n"); + ret = -ENODEV; + goto release; + } + + ion_ux500->heaps = kzalloc(sizeof(struct ion_heap *) + * ion_ux500->num_heaps, GFP_KERNEL); + if (!ion_ux500->heaps) { + dev_err(&pdev->dev, "no memory for heaps\n"); + ret = -ENOMEM; + goto release; + } + + ion_ux500->ion_device = ion_device_create(NULL); + if (IS_ERR_OR_NULL(ion_ux500->ion_device)) { + dev_err(&pdev->dev, "failed to create ion device\n"); + ret = PTR_ERR(ion_ux500->ion_device); + goto release_heaps; + } + + /* create the heaps as specified in the board file */ + for (i = 0; i < ion_ux500->num_heaps; i++) { + struct ion_platform_heap *heap_data = &pdata->heaps[i]; + + ion_ux500->heaps[i] = ion_heap_create(heap_data); + + if (IS_ERR_OR_NULL(ion_ux500->heaps[i])) { + ret = PTR_ERR(ion_ux500->heaps[i]); + ion_ux500->heaps[i] = NULL; + dev_err(&pdev->dev, + "failed to create heap type %d id %d\n", + heap_data->type, heap_data->id); + goto destroy_heaps; + } + ion_device_add_heap(ion_ux500->ion_device, ion_ux500->heaps[i]); + } + + platform_set_drvdata(pdev, ion_ux500); + + return ret; + +destroy_heaps: + for (i = 0; i < ion_ux500->num_heaps; i++) + if (ion_ux500->heaps[i]) + ion_heap_destroy(ion_ux500->heaps[i]); + + ion_device_destroy(ion_ux500->ion_device); +release_heaps: + kfree(ion_ux500->heaps); +release: + kfree(ion_ux500); + return ret; +} + +int ux500_ion_remove(struct platform_device *pdev) +{ + struct ion_ux500_internal *ion_ux500 = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < ion_ux500->num_heaps; i++) + if (ion_ux500->heaps[i]) + ion_heap_destroy(ion_ux500->heaps[i]); + + ion_device_destroy(ion_ux500->ion_device); + kfree(ion_ux500->heaps); + kfree(ion_ux500); + + return 0; +} + +static struct platform_driver ux500_ion_driver = { + .probe = ux500_ion_probe, + .remove = ux500_ion_remove, + .driver = { + .name = "ion-ux500", + } +}; + +static int __init ux500_ion_init(void) +{ + return platform_driver_register(&ux500_ion_driver); +} + +static void __exit ux500_ion_exit(void) +{ + platform_driver_unregister(&ux500_ion_driver); +} + +module_init(ux500_ion_init); +module_exit(ux500_ion_exit);
linaro-mm-sig@lists.linaro.org