Hello,
This is an update for my proposal for device tree integration for Contiguous Memory Allocator. The code is quite straightforward, but expect again that the device tree bindings will trigger some discussion.
Just a few words for those who see this code for the first time:
The proposed bindings allows to define contiguous memory regions of specified base address and size. Then, the defined regions can be assigned to the given device(s) by adding a property with a phanle to the defined contiguous memory region. From the device tree perspective that's all. Once the bindings are added, all the memory allocations from dma-mapping subsystem will be served from the defined contiguous memory regions.
Contiguous Memory Allocator is a framework, which lets to provide a large contiguous memory buffers for (usually a multimedia) devices. The contiguous memory is reserved during early boot and then shared with kernel, which is allowed to allocate it for movable pages. Then, when device driver requests a contigouous buffer, the framework migrates movable pages out of contiguous region and gives it to the driver. When device driver frees the buffer, it is added to kernel memory pool again. For more information, please refer to commit c64be2bb1c6eb43c838b2c6d57 ("drivers: add Contiguous Memory Allocator") and d484864dd96e1830e76895 (CMA merge commit).
Why we need device tree bindings for CMA at all?
Older ARM kernels used so called board-based initialization. Those board files contained a definition of all hardware blocks available on the target system and particular kernel and driver software configuration selected by the board maintainer.
In the new approach the board files will be removed completely and Device Tree approach is used to describe all hardware blocks available on the target system. By definition, the bindings should be software independent, so at least in theory it should be possible to use those bindings with other operating systems than Linux kernel.
However we also need to pass somehow the information about kernel and device driver software-only configuration data, which were earlier encoded in the board file. For such data I propose to use /chosen node, where kernel command line has been already stored. Future bootloaders will allow to modify or replace particular nodes and one will be able to use custom /chosen node to configure his system. The proposed patches introduce /chosen/contiguous-memory node and related bindings, to avoid complicated encoding of CMA related configuration to kernel command line.
Best regards Marek Szyprowski Samsung Poland R&D Center
Changelog:
v2: - moved contiguous-memory bindings from /memory to /chosen/contiguous-memory/ node to avoid spreading Linux specific parameters over the whole device tree definitions - added support for autoconfigured regions (use zero base) - fixes minor bugs
v1: http://thread.gmane.org/gmane.linux.drivers.devicetree/30111/ - initial proposal
Patch summary:
Marek Szyprowski (2): drivers: dma-contiguous: clean source code and prepare for device tree drivers: dma-contiguous: add initialization from device tree
Documentation/devicetree/bindings/memory.txt | 101 ++++++++++ arch/arm/boot/dts/skeleton.dtsi | 7 +- drivers/base/dma-contiguous.c | 278 +++++++++++++++++++------- include/asm-generic/dma-contiguous.h | 4 +- include/linux/dma-contiguous.h | 32 ++- 5 files changed, 338 insertions(+), 84 deletions(-) create mode 100644 Documentation/devicetree/bindings/memory.txt
This patch cleans the initialization of dma contiguous framework. The all-in-one dma_declare_contiguous() function is now separated into dma_contiguous_reserve_area() which only steals the the memory from memblock allocator and dma_contiguous_add_device() function, which assigns given device to the specified reserved memory area. This improves the flexibility in defining contiguous memory areas and assigning device to them, because now it is possible to assign more than one device to the given contiguous memory area. This split in initialization is also required for upcoming device tree support.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/base/dma-contiguous.c | 211 ++++++++++++++++------------------ include/asm-generic/dma-contiguous.h | 2 - include/linux/dma-contiguous.h | 32 +++++- 3 files changed, 129 insertions(+), 116 deletions(-)
diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c index 0ca5442..01fe743 100644 --- a/drivers/base/dma-contiguous.c +++ b/drivers/base/dma-contiguous.c @@ -39,8 +39,15 @@ struct cma { unsigned long *bitmap; };
+static DEFINE_MUTEX(cma_mutex); + +static struct cma cma_areas[MAX_CMA_AREAS]; +static unsigned cma_area_count; + struct cma *dma_contiguous_default_area;
+/*****************************************************************************/ + #ifdef CONFIG_CMA_SIZE_MBYTES #define CMA_SIZE_MBYTES CONFIG_CMA_SIZE_MBYTES #else @@ -95,51 +102,20 @@ static inline __maybe_unused phys_addr_t cma_early_percent_memory(void)
#endif
-/** - * dma_contiguous_reserve() - reserve area for contiguous memory handling - * @limit: End address of the reserved memory (optional, 0 for any). - * - * This function reserves memory from early allocator. It should be - * called by arch specific code once the early allocator (memblock or bootmem) - * has been activated and all other subsystems have already allocated/reserved - * memory. - */ -void __init dma_contiguous_reserve(phys_addr_t limit) -{ - phys_addr_t selected_size = 0; - - pr_debug("%s(limit %08lx)\n", __func__, (unsigned long)limit); - - if (size_cmdline != -1) { - selected_size = size_cmdline; - } else { -#ifdef CONFIG_CMA_SIZE_SEL_MBYTES - selected_size = size_bytes; -#elif defined(CONFIG_CMA_SIZE_SEL_PERCENTAGE) - selected_size = cma_early_percent_memory(); -#elif defined(CONFIG_CMA_SIZE_SEL_MIN) - selected_size = min(size_bytes, cma_early_percent_memory()); -#elif defined(CONFIG_CMA_SIZE_SEL_MAX) - selected_size = max(size_bytes, cma_early_percent_memory()); -#endif - } - - if (selected_size) { - pr_debug("%s: reserving %ld MiB for global area\n", __func__, - (unsigned long)selected_size / SZ_1M); - - dma_declare_contiguous(NULL, selected_size, 0, limit); - } -}; - -static DEFINE_MUTEX(cma_mutex); +/*************************************************************************/
-static __init int cma_activate_area(unsigned long base_pfn, unsigned long count) +static __init int cma_activate_area(struct cma *cma) { - unsigned long pfn = base_pfn; - unsigned i = count >> pageblock_order; + int bitmap_size = BITS_TO_LONGS(cma->count) * sizeof(long); + unsigned long base_pfn = cma->base_pfn, pfn = base_pfn; + unsigned i = cma->count >> pageblock_order; struct zone *zone;
+ cma->bitmap = kzalloc(bitmap_size, GFP_KERNEL); + + if (!cma->bitmap) + return -ENOMEM; + WARN_ON_ONCE(!pfn_valid(pfn)); zone = page_zone(pfn_to_page(pfn));
@@ -153,92 +129,74 @@ static __init int cma_activate_area(unsigned long base_pfn, unsigned long count) } init_cma_reserved_pageblock(pfn_to_page(base_pfn)); } while (--i); + return 0; }
-static __init struct cma *cma_create_area(unsigned long base_pfn, - unsigned long count) +/** + * dma_contiguous_reserve() - reserve area(s) for contiguous memory handling + * @limit: End address of the reserved memory (optional, 0 for any). + * + * This function reserves memory from early allocator. It should be + * called by arch specific code once the early allocator (memblock or bootmem) + * has been activated and all other subsystems have already allocated/reserved + * memory. It reserves contiguous areas for global, device independent + * allocations and (optionally) all areas defined in device tree structures. + */ +void __init dma_contiguous_reserve(phys_addr_t limit) { - int bitmap_size = BITS_TO_LONGS(count) * sizeof(long); - struct cma *cma; - int ret = -ENOMEM; - - pr_debug("%s(base %08lx, count %lx)\n", __func__, base_pfn, count); - - cma = kmalloc(sizeof *cma, GFP_KERNEL); - if (!cma) - return ERR_PTR(-ENOMEM); + phys_addr_t sel_size = 0;
- cma->base_pfn = base_pfn; - cma->count = count; - cma->bitmap = kzalloc(bitmap_size, GFP_KERNEL); - - if (!cma->bitmap) - goto no_mem; - - ret = cma_activate_area(base_pfn, count); - if (ret) - goto error; - - pr_debug("%s: returned %p\n", __func__, (void *)cma); - return cma; - -error: - kfree(cma->bitmap); -no_mem: - kfree(cma); - return ERR_PTR(ret); -} - -static struct cma_reserved { - phys_addr_t start; - unsigned long size; - struct device *dev; -} cma_reserved[MAX_CMA_AREAS] __initdata; -static unsigned cma_reserved_count __initdata; + pr_debug("%s(limit %08lx)\n", __func__, (unsigned long)limit);
-static int __init cma_init_reserved_areas(void) -{ - struct cma_reserved *r = cma_reserved; - unsigned i = cma_reserved_count; + if (size_cmdline != -1) { + sel_size = size_cmdline; + } else { +#ifdef CONFIG_CMA_SIZE_SEL_MBYTES + sel_size = size_bytes; +#elif defined(CONFIG_CMA_SIZE_SEL_PERCENTAGE) + sel_size = cma_early_percent_memory(); +#elif defined(CONFIG_CMA_SIZE_SEL_MIN) + sel_size = min(size_bytes, cma_early_percent_memory()); +#elif defined(CONFIG_CMA_SIZE_SEL_MAX) + sel_size = max(size_bytes, cma_early_percent_memory()); +#endif + }
- pr_debug("%s()\n", __func__); + if (sel_size && !dma_contiguous_default_area) { + pr_debug("%s: reserving %ld MiB for global area\n", __func__, + (unsigned long)sel_size / SZ_1M);
- for (; i; --i, ++r) { - struct cma *cma; - cma = cma_create_area(PFN_DOWN(r->start), - r->size >> PAGE_SHIFT); - if (!IS_ERR(cma)) - dev_set_cma_area(r->dev, cma); + dma_contiguous_reserve_area(sel_size, 0, limit, + &dma_contiguous_default_area); } - return 0; -} -core_initcall(cma_init_reserved_areas); +};
/** - * dma_declare_contiguous() - reserve area for contiguous memory handling - * for particular device - * @dev: Pointer to device structure. - * @size: Size of the reserved memory. - * @base: Start address of the reserved memory (optional, 0 for any). + * dma_contiguous_reserve_area() - reserve custom contiguous area + * @size: Size of the reserved area (in bytes), + * @base: Base address of the reserved area optional, use 0 for any * @limit: End address of the reserved memory (optional, 0 for any). + * @res_cma: Pointer to store the created cma region. * - * This function reserves memory for specified device. It should be - * called by board specific code when early allocator (memblock or bootmem) - * is still activate. + * This function reserves memory from early allocator. It should be + * called by arch specific code once the early allocator (memblock or bootmem) + * has been activated and all other subsystems have already allocated/reserved + * memory. This function allows to create custom reserved areas for specific + * devices. */ -int __init dma_declare_contiguous(struct device *dev, phys_addr_t size, - phys_addr_t base, phys_addr_t limit) +int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, + phys_addr_t limit, struct cma **res_cma) { - struct cma_reserved *r = &cma_reserved[cma_reserved_count]; phys_addr_t alignment; + int ret = 0;
pr_debug("%s(size %lx, base %08lx, limit %08lx)\n", __func__, (unsigned long)size, (unsigned long)base, (unsigned long)limit);
/* Sanity checks */ - if (cma_reserved_count == ARRAY_SIZE(cma_reserved)) { + if (cma_area_count == ARRAY_SIZE(cma_areas)) { pr_err("Not enough slots for CMA reserved regions!\n"); return -ENOSPC; } @@ -256,7 +214,7 @@ int __init dma_declare_contiguous(struct device *dev, phys_addr_t size, if (base) { if (memblock_is_region_reserved(base, size) || memblock_reserve(base, size) < 0) { - base = -EBUSY; + ret = -EBUSY; goto err; } } else { @@ -266,7 +224,7 @@ int __init dma_declare_contiguous(struct device *dev, phys_addr_t size, */ phys_addr_t addr = __memblock_alloc_base(size, alignment, limit); if (!addr) { - base = -ENOMEM; + ret = -ENOMEM; goto err; } else { base = addr; @@ -277,10 +235,11 @@ int __init dma_declare_contiguous(struct device *dev, phys_addr_t size, * Each reserved area must be initialised later, when more kernel * subsystems (like slab allocator) are available. */ - r->start = base; - r->size = size; - r->dev = dev; - cma_reserved_count++; + cma_areas[cma_area_count].base_pfn = PFN_DOWN(base); + cma_areas[cma_area_count].count = size >> PAGE_SHIFT; + *res_cma = &cma_areas[cma_area_count]; + cma_area_count++; + pr_info("CMA: reserved %ld MiB at %08lx\n", (unsigned long)size / SZ_1M, (unsigned long)base);
@@ -289,10 +248,38 @@ int __init dma_declare_contiguous(struct device *dev, phys_addr_t size, return 0; err: pr_err("CMA: failed to reserve %ld MiB\n", (unsigned long)size / SZ_1M); - return base; + return ret; }
/** + * dma_contiguous_add_device() - add device to custom contiguous reserved area + * @dev: Pointer to device structure. + * @cma: Pointer to the cma structure for the reserved area + * + * This function assigns the given device to the contiguous memory area + * reserved earlier by dma_contiguous_reserve_area() function. + */ +int __init dma_contiguous_add_device(struct device *dev, struct cma *cma) +{ + dev_set_cma_area(dev, cma); + return 0; +} + +static int __init cma_init_reserved_areas(void) +{ + int i; + + for (i = 0; i < cma_area_count; i++) { + int ret = cma_activate_area(&cma_areas[i]); + if (ret) + return ret; + } + + return 0; +} +core_initcall(cma_init_reserved_areas); + +/** * dma_alloc_from_contiguous() - allocate pages from contiguous area * @dev: Pointer to device for which the allocation is performed. * @count: Requested number of pages. diff --git a/include/asm-generic/dma-contiguous.h b/include/asm-generic/dma-contiguous.h index 294b1e7..8979f86 100644 --- a/include/asm-generic/dma-contiguous.h +++ b/include/asm-generic/dma-contiguous.h @@ -18,8 +18,6 @@ static inline void dev_set_cma_area(struct device *dev, struct cma *cma) { if (dev) dev->cma_area = cma; - if (!dev && !dma_contiguous_default_area) - dma_contiguous_default_area = cma; }
#endif diff --git a/include/linux/dma-contiguous.h b/include/linux/dma-contiguous.h index 01b5c84..28c31fe 100644 --- a/include/linux/dma-contiguous.h +++ b/include/linux/dma-contiguous.h @@ -68,8 +68,36 @@ struct device; extern struct cma *dma_contiguous_default_area;
void dma_contiguous_reserve(phys_addr_t addr_limit); -int dma_declare_contiguous(struct device *dev, phys_addr_t size, - phys_addr_t base, phys_addr_t limit); + +int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, + phys_addr_t limit, struct cma **res_cma); + +int dma_contiguous_add_device(struct device *dev, struct cma *cma); + +/** + * dma_declare_contiguous() - reserve area for contiguous memory handling + * for particular device + * @dev: Pointer to device structure. + * @size: Size of the reserved memory. + * @base: Start address of the reserved memory (optional, 0 for any). + * @limit: End address of the reserved memory (optional, 0 for any). + * + * This function reserves memory for specified device. It should be + * called by board specific code when early allocator (memblock or bootmem) + * is still activate. + */ + +static inline int dma_declare_contiguous(struct device *dev, phys_addr_t size, + phys_addr_t base, phys_addr_t limit) +{ + struct cma *cma; + int ret; + ret = dma_contiguous_reserve_area(size, base, limit, &cma); + if (ret == 0) + dma_contiguous_add_device(dev, cma); + + return ret; +}
struct page *dma_alloc_from_contiguous(struct device *dev, int count, unsigned int order);
Add device tree support for contiguous memory regions defined in device tree. Initialization is done in 2 steps. First, the contiguous memory is reserved, what happens very early, when only flattened device tree is available. Then on device initialization the corresponding cma regions are assigned to device structures.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com --- .../devicetree/bindings/contiguous-memory.txt | 69 +++++++++++ arch/arm/boot/dts/skeleton.dtsi | 8 +- drivers/base/dma-contiguous.c | 124 ++++++++++++++++++++ 3 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/contiguous-memory.txt
diff --git a/Documentation/devicetree/bindings/contiguous-memory.txt b/Documentation/devicetree/bindings/contiguous-memory.txt new file mode 100644 index 0000000..fa598bf --- /dev/null +++ b/Documentation/devicetree/bindings/contiguous-memory.txt @@ -0,0 +1,69 @@ +* Contiguous memory binding + +The /chosen/contiguous-memory node provides runtime configuration of +contiguous memory regions for Linux kernel. One can create such regions +for special usage by various device drivers. A good example are +contiguous memory allocations or memory sharing with other operating +system on the same hardware board. Those special memory regions might +depend on the board configuration and devices used on the target system. + +Parameters for each memory region can be encoded into the device tree +wit the following convention: + +contiguous-memory { + + (name): region@(base-address) { + reg = <(baseaddr) (size)>; + (linux,default-contiguous-region); + device = <&device_0 &device_1 ...> + }; +}; + +name: an name given to the defined region. +base-address: the base address of the defined region. +size: the size of the memory region (bytes). +linux,default-contiguous-region: property indicating that the region + is the default region for all contiguous memory + allocations, Linux specific (optional) +device: array of phandles to the client device nodes, which use + the given contiguous region + +* Example: + +This example defines a memory configuration containing 2 contiguous +regions for Linux kernel, one default of all device drivers (named +contig_mem, placed at 0x72000000, 64MiB) and one dedicated to the +framebuffer device (named display_mem, placed at 0x78000000, 16MiB). The +display_mem region is then assigned to fb@12300000 device for contiguous +memory allocation with Linux kernel drivers. + +The reason for creating a separate region for framebuffer device is to +match the framebuffer address of from configuration done by bootloader, +so once Linux kernel drivers starts, no glitches on the displayed boot +logo appears. + +/ { + /* ... */ + + chosen { + bootargs = /* ... */ + + contiguous-memory { + contig_mem: region@72000000 { + reg = <0x72000000 0x4000000>; + linux,default-contiguous-region; + }; + + display_mem: region@78000000 { + reg = <0x78000000 0x1000000>; + device = <&fb0>; + }; + }; + }; + + /* ... */ + + fb0: fb@12300000 { + status = "okay"; + }; +}; diff --git a/arch/arm/boot/dts/skeleton.dtsi b/arch/arm/boot/dts/skeleton.dtsi index b41d241..538a868 100644 --- a/arch/arm/boot/dts/skeleton.dtsi +++ b/arch/arm/boot/dts/skeleton.dtsi @@ -7,7 +7,13 @@ / { #address-cells = <1>; #size-cells = <1>; - chosen { }; + chosen { + contiguous-memory { + #address-cells = <1>; + #size-cells = <1>; + }; + }; aliases { }; memory { device_type = "memory"; reg = <0 0>; }; }; + diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c index 01fe743..6a8abab 100644 --- a/drivers/base/dma-contiguous.c +++ b/drivers/base/dma-contiguous.c @@ -24,6 +24,9 @@
#include <linux/memblock.h> #include <linux/err.h> +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/of_platform.h> #include <linux/mm.h> #include <linux/mutex.h> #include <linux/page-isolation.h> @@ -37,6 +40,7 @@ struct cma { unsigned long base_pfn; unsigned long count; unsigned long *bitmap; + char full_name[32]; };
static DEFINE_MUTEX(cma_mutex); @@ -133,6 +137,53 @@ static __init int cma_activate_area(struct cma *cma) return 0; }
+/*****************************************************************************/ + +#ifdef CONFIG_OF +int __init cma_fdt_scan(unsigned long node, const char *uname, + int depth, void *data) +{ + static int level; + phys_addr_t base, size; + unsigned long len; + struct cma *cma; + __be32 *prop; + + if (depth == 1 && strcmp(uname, "chosen") == 0) { + level = depth; + return 0; + } + + if (depth == 2 && strcmp(uname, "contiguous-memory") == 0) { + level = depth; + return 0; + } + + if (level != 2 || depth != 3 || strncmp(uname, "region@", 7) != 0) + return 0; + + prop = of_get_flat_dt_prop(node, "reg", &len); + if (!prop || (len != 2 * sizeof(unsigned long))) + return 0; + + base = be32_to_cpu(prop[0]); + size = be32_to_cpu(prop[1]); + + pr_info("Found %s, memory base %lx, size %ld MiB\n", uname, + (unsigned long)base, (unsigned long)size / SZ_1M); + + dma_contiguous_reserve_area(size, base, 0, &cma); + + strcpy(cma->full_name, uname); + + if (of_get_flat_dt_prop(node, "linux,default-contiguous-region", NULL)) { + + dma_contiguous_default_area = cma; + } + return 0; +} +#endif + /** * dma_contiguous_reserve() - reserve area(s) for contiguous memory handling * @limit: End address of the reserved memory (optional, 0 for any). @@ -149,6 +200,10 @@ void __init dma_contiguous_reserve(phys_addr_t limit)
pr_debug("%s(limit %08lx)\n", __func__, (unsigned long)limit);
+#ifdef CONFIG_OF + of_scan_flat_dt(cma_fdt_scan, NULL); +#endif + if (size_cmdline != -1) { sel_size = size_cmdline; } else { @@ -265,6 +320,71 @@ int __init dma_contiguous_add_device(struct device *dev, struct cma *cma) return 0; }
+#ifdef CONFIG_OF +static struct cma_map { + struct cma *cma; + struct device_node *node; +} cma_maps[MAX_CMA_AREAS]; +static unsigned cma_map_count; + +static void cma_assign_device_from_dt(struct device *dev) +{ + int i; + for (i=0; i<cma_map_count; i++) { + if (cma_maps[i].node == dev->of_node) { + dev_set_cma_area(dev, cma_maps[i].cma); + pr_info("Assigned CMA %s to %s device\n", + cma_maps[i].cma->full_name, + dev_name(dev)); + } + } +} + +static int cma_device_init_notifier_call(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct device *dev = data; + if (event == BUS_NOTIFY_ADD_DEVICE && dev->of_node) + cma_assign_device_from_dt(dev); + return NOTIFY_DONE; +} + +static struct notifier_block cma_dev_init_nb = { + .notifier_call = cma_device_init_notifier_call, +}; + +void scan_cma_nodes(void) +{ + struct device_node *parent = of_find_node_by_path("/chosen/contiguous-memory"); + struct device_node *child; + + if (!parent) + return; + + for_each_child_of_node(parent, child) { + struct cma *cma = NULL; + int i; + + for (i=0; i<cma_area_count; i++) + if (strstr(child->full_name, cma_areas[i].full_name)) + cma = &cma_areas[i]; + if (!cma) + continue; + + for (i=0;; i++) { + struct device_node *node; + node = of_parse_phandle(child, "device", i); + if (!node) + break; + + cma_maps[cma_map_count].cma = cma; + cma_maps[cma_map_count].node = node; + cma_map_count++; + } + } +} +#endif + static int __init cma_init_reserved_areas(void) { int i; @@ -275,6 +395,10 @@ static int __init cma_init_reserved_areas(void) return ret; }
+#ifdef CONFIG_OF + scan_cma_nodes(); + bus_register_notifier(&platform_bus_type, &cma_dev_init_nb); +#endif return 0; } core_initcall(cma_init_reserved_areas);
Hi,
On 4/11/2013 4:22 AM, Marek Szyprowski wrote: ...
diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c index 01fe743..6a8abab 100644 --- a/drivers/base/dma-contiguous.c +++ b/drivers/base/dma-contiguous.c @@ -24,6 +24,9 @@
#include <linux/memblock.h> #include <linux/err.h> +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/of_platform.h> #include <linux/mm.h> #include <linux/mutex.h> #include <linux/page-isolation.h> @@ -37,6 +40,7 @@ struct cma { unsigned long base_pfn; unsigned long count; unsigned long *bitmap;
char full_name[32]; };
static DEFINE_MUTEX(cma_mutex);
@@ -133,6 +137,53 @@ static __init int cma_activate_area(struct cma *cma) return 0; }
+/*****************************************************************************/
+#ifdef CONFIG_OF +int __init cma_fdt_scan(unsigned long node, const char *uname,
int depth, void *data)
+{
- static int level;
- phys_addr_t base, size;
- unsigned long len;
- struct cma *cma;
- __be32 *prop;
- if (depth == 1 && strcmp(uname, "chosen") == 0) {
level = depth;
return 0;
- }
- if (depth == 2 && strcmp(uname, "contiguous-memory") == 0) {
level = depth;
return 0;
- }
- if (level != 2 || depth != 3 || strncmp(uname, "region@", 7) != 0)
return 0;
Requiring the region@ label does not work if you want two dynamically placed regions (i.e. two region@0). The devicetree will take the last region@0 entry and ignore all the other ones
- prop = of_get_flat_dt_prop(node, "reg", &len);
- if (!prop || (len != 2 * sizeof(unsigned long)))
return 0;
- base = be32_to_cpu(prop[0]);
- size = be32_to_cpu(prop[1]);
- pr_info("Found %s, memory base %lx, size %ld MiB\n", uname,
(unsigned long)base, (unsigned long)size / SZ_1M);
- dma_contiguous_reserve_area(size, base, 0, &cma);
Need to check the return of dma_contiguous_reserve_area, else there is an abort when trying to access cma->name on an error
- strcpy(cma->full_name, uname);
- if (of_get_flat_dt_prop(node, "linux,default-contiguous-region", NULL)) {
dma_contiguous_default_area = cma;
- }
- return 0;
+} +#endif
- /**
- dma_contiguous_reserve() - reserve area(s) for contiguous memory handling
- @limit: End address of the reserved memory (optional, 0 for any).
if the contiguous region isn't set via devicetree, default_area->full_name needs to be set in dma_contiguous_reserve, else we get wrong associations in scan_cma_node.
@@ -149,6 +200,10 @@ void __init dma_contiguous_reserve(phys_addr_t limit)
pr_debug("%s(limit %08lx)\n", __func__, (unsigned long)limit);
+#ifdef CONFIG_OF
- of_scan_flat_dt(cma_fdt_scan, NULL);
+#endif
- if (size_cmdline != -1) { sel_size = size_cmdline; } else {
@@ -265,6 +320,71 @@ int __init dma_contiguous_add_device(struct device *dev, struct cma *cma) return 0; }
+#ifdef CONFIG_OF +static struct cma_map {
- struct cma *cma;
- struct device_node *node;
+} cma_maps[MAX_CMA_AREAS]; +static unsigned cma_map_count;
+static void cma_assign_device_from_dt(struct device *dev) +{
- int i;
- for (i=0; i<cma_map_count; i++) {
if (cma_maps[i].node == dev->of_node) {
dev_set_cma_area(dev, cma_maps[i].cma);
pr_info("Assigned CMA %s to %s device\n",
cma_maps[i].cma->full_name,
dev_name(dev));
}
- }
+}
+static int cma_device_init_notifier_call(struct notifier_block *nb,
unsigned long event, void *data)
+{
- struct device *dev = data;
- if (event == BUS_NOTIFY_ADD_DEVICE && dev->of_node)
cma_assign_device_from_dt(dev);
- return NOTIFY_DONE;
+}
+static struct notifier_block cma_dev_init_nb = {
- .notifier_call = cma_device_init_notifier_call,
+};
+void scan_cma_nodes(void) +{
- struct device_node *parent = of_find_node_by_path("/chosen/contiguous-memory");
- struct device_node *child;
- if (!parent)
return;
- for_each_child_of_node(parent, child) {
struct cma *cma = NULL;
int i;
for (i=0; i<cma_area_count; i++)
if (strstr(child->full_name, cma_areas[i].full_name))
cma = &cma_areas[i];
break out of the loop once the area is found?
Also, how will the code deal with region names that are substrings of each other e.g.
region@1000000 region@10000000
if (!cma)
continue;
for (i=0;; i++) {
struct device_node *node;
node = of_parse_phandle(child, "device", i);
if (!node)
break;
cma_maps[cma_map_count].cma = cma;
cma_maps[cma_map_count].node = node;
cma_map_count++;
Bounds check cma_map_count against MAX_CMA_AREAS?
}
- }
+} +#endif
- static int __init cma_init_reserved_areas(void) { int i;
@@ -275,6 +395,10 @@ static int __init cma_init_reserved_areas(void) return ret; }
+#ifdef CONFIG_OF
- scan_cma_nodes();
- bus_register_notifier(&platform_bus_type, &cma_dev_init_nb);
+#endif return 0; } core_initcall(cma_init_reserved_areas);
Thanks, Laura
Hi Laura,
Thanks for your thorough review! I will fix all the pointed issues once the main point of this patch set (using /chosen/contiguous-memory for CMA DT bindings) will be agreed and accepted.
On 4/11/2013 7:56 PM, Laura Abbott wrote:
Hi,
On 4/11/2013 4:22 AM, Marek Szyprowski wrote: ...
diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c index 01fe743..6a8abab 100644 --- a/drivers/base/dma-contiguous.c +++ b/drivers/base/dma-contiguous.c @@ -24,6 +24,9 @@
#include <linux/memblock.h> #include <linux/err.h> +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/of_platform.h> #include <linux/mm.h> #include <linux/mutex.h> #include <linux/page-isolation.h> @@ -37,6 +40,7 @@ struct cma { unsigned long base_pfn; unsigned long count; unsigned long *bitmap;
char full_name[32]; };
static DEFINE_MUTEX(cma_mutex);
@@ -133,6 +137,53 @@ static __init int cma_activate_area(struct cma *cma) return 0; }
+/*****************************************************************************/
+#ifdef CONFIG_OF +int __init cma_fdt_scan(unsigned long node, const char *uname,
int depth, void *data)
+{
- static int level;
- phys_addr_t base, size;
- unsigned long len;
- struct cma *cma;
- __be32 *prop;
- if (depth == 1 && strcmp(uname, "chosen") == 0) {
level = depth;
return 0;
- }
- if (depth == 2 && strcmp(uname, "contiguous-memory") == 0) {
level = depth;
return 0;
- }
- if (level != 2 || depth != 3 || strncmp(uname, "region@", 7) != 0)
return 0;
Requiring the region@ label does not work if you want two dynamically placed regions (i.e. two region@0). The devicetree will take the last region@0 entry and ignore all the other ones
Hmm. You are right, I need different solution here to get it working with autoconfigured base address.
- prop = of_get_flat_dt_prop(node, "reg", &len);
- if (!prop || (len != 2 * sizeof(unsigned long)))
return 0;
- base = be32_to_cpu(prop[0]);
- size = be32_to_cpu(prop[1]);
- pr_info("Found %s, memory base %lx, size %ld MiB\n", uname,
(unsigned long)base, (unsigned long)size / SZ_1M);
- dma_contiguous_reserve_area(size, base, 0, &cma);
Need to check the return of dma_contiguous_reserve_area, else there is an abort when trying to access cma->name on an error
- strcpy(cma->full_name, uname);
- if (of_get_flat_dt_prop(node, "linux,default-contiguous-region",
NULL)) {
dma_contiguous_default_area = cma;
- }
- return 0;
+} +#endif
- /**
- dma_contiguous_reserve() - reserve area(s) for contiguous memory
handling
- @limit: End address of the reserved memory (optional, 0 for any).
if the contiguous region isn't set via devicetree, default_area->full_name needs to be set in dma_contiguous_reserve, else we get wrong associations in scan_cma_node.
@@ -149,6 +200,10 @@ void __init dma_contiguous_reserve(phys_addr_t limit)
pr_debug("%s(limit %08lx)\n", __func__, (unsigned long)limit);
+#ifdef CONFIG_OF
- of_scan_flat_dt(cma_fdt_scan, NULL);
+#endif
if (size_cmdline != -1) { sel_size = size_cmdline; } else {
@@ -265,6 +320,71 @@ int __init dma_contiguous_add_device(struct device *dev, struct cma *cma) return 0; }
+#ifdef CONFIG_OF +static struct cma_map {
- struct cma *cma;
- struct device_node *node;
+} cma_maps[MAX_CMA_AREAS]; +static unsigned cma_map_count;
+static void cma_assign_device_from_dt(struct device *dev) +{
- int i;
- for (i=0; i<cma_map_count; i++) {
if (cma_maps[i].node == dev->of_node) {
dev_set_cma_area(dev, cma_maps[i].cma);
pr_info("Assigned CMA %s to %s device\n",
cma_maps[i].cma->full_name,
dev_name(dev));
}
- }
+}
+static int cma_device_init_notifier_call(struct notifier_block *nb,
unsigned long event, void *data)
+{
- struct device *dev = data;
- if (event == BUS_NOTIFY_ADD_DEVICE && dev->of_node)
cma_assign_device_from_dt(dev);
- return NOTIFY_DONE;
+}
+static struct notifier_block cma_dev_init_nb = {
- .notifier_call = cma_device_init_notifier_call,
+};
+void scan_cma_nodes(void) +{
- struct device_node *parent =
of_find_node_by_path("/chosen/contiguous-memory");
- struct device_node *child;
- if (!parent)
return;
- for_each_child_of_node(parent, child) {
struct cma *cma = NULL;
int i;
for (i=0; i<cma_area_count; i++)
if (strstr(child->full_name, cma_areas[i].full_name))
cma = &cma_areas[i];
break out of the loop once the area is found?
Also, how will the code deal with region names that are substrings of each other e.g.
region@1000000 region@10000000
Thanks for pointing this.
if (!cma)
continue;
for (i=0;; i++) {
struct device_node *node;
node = of_parse_phandle(child, "device", i);
if (!node)
break;
cma_maps[cma_map_count].cma = cma;
cma_maps[cma_map_count].node = node;
cma_map_count++;
Bounds check cma_map_count against MAX_CMA_AREAS?
}
- }
+} +#endif
- static int __init cma_init_reserved_areas(void) { int i;
@@ -275,6 +395,10 @@ static int __init cma_init_reserved_areas(void) return ret; }
+#ifdef CONFIG_OF
- scan_cma_nodes();
- bus_register_notifier(&platform_bus_type, &cma_dev_init_nb);
+#endif return 0; } core_initcall(cma_init_reserved_areas);
Thanks, Laura
Best regards
On 04/11/2013 01:22 PM, Marek Szyprowski wrote:
diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c index 01fe743..6a8abab 100644 --- a/drivers/base/dma-contiguous.c +++ b/drivers/base/dma-contiguous.c
[...]
+void scan_cma_nodes(void)
Should be declared static.
+{
- struct device_node *parent = of_find_node_by_path("/chosen/contiguous-memory");
- struct device_node *child;
- if (!parent)
return;
- for_each_child_of_node(parent, child) {
struct cma *cma = NULL;
int i;
for (i=0; i<cma_area_count; i++)
if (strstr(child->full_name, cma_areas[i].full_name))
cma = &cma_areas[i];
if (!cma)
continue;
for (i=0;; i++) {
struct device_node *node;
node = of_parse_phandle(child, "device", i);
if (!node)
break;
cma_maps[cma_map_count].cma = cma;
cma_maps[cma_map_count].node = node;
cma_map_count++;
}
of_parse_phandle() requires of_node_put() to be called when done with the device node. Also, of_node_put() should be called on the parent node as well.
- }
+}
-- Francesco
/**
- dma_contiguous_reserve() - reserve area(s) for contiguous memory handling
- @limit: End address of the reserved memory (optional, 0 for any).
@@ -149,6 +200,10 @@ void __init dma_contiguous_reserve(phys_addr_t limit)
pr_debug("%s(limit %08lx)\n", __func__, (unsigned long)limit);
+#ifdef CONFIG_OF
of_scan_flat_dt(cma_fdt_scan, NULL);
+#endif
What is your expectation with the contention between default region setup via the kernel config (CONFIG_CMA_SIZE_SEL_*) and via the DT? Could the call to 'of_scan_flat_dt()' be done before the setup of the kernel config early regions, followed by a return code check 'fail-over' scheme, or were you intending for the default region setups to be mutually-exclusive?
Thanks, Marc
Hello,
On 4/29/2013 11:21 PM, Marc C wrote:
/**
- dma_contiguous_reserve() - reserve area(s) for contiguous memory handling
- @limit: End address of the reserved memory (optional, 0 for any).
@@ -149,6 +200,10 @@ void __init dma_contiguous_reserve(phys_addr_t limit)
pr_debug("%s(limit %08lx)\n", __func__, (unsigned long)limit);
+#ifdef CONFIG_OF
of_scan_flat_dt(cma_fdt_scan, NULL);
+#endif
What is your expectation with the contention between default region setup via the kernel config (CONFIG_CMA_SIZE_SEL_*) and via the DT? Could the call to 'of_scan_flat_dt()' be done before the setup of the kernel config early regions, followed by a return code check 'fail-over' scheme, or were you intending for the default region setups to be mutually-exclusive?
In the proposed patch the default/global cma region setup from device tree had higher priority than kernel command line parameter and .config saved values, but now as I think of this, it looks that it would make more sense to have the following priority for setting up the default cma region:
1. kernel cmd line - if not available, then use: 2. device tree - if not available, then use: 3. kernel compiled .config
I will update this in the next version of the CMA DT patches.
Best regards
linaro-mm-sig@lists.linaro.org