diff --git a/Documentation/devicetree/bindings/memory.txt b/Documentation/devicetree/bindings/memory.txt index e98ee0588f3c9ecffe829139ed95bfd042ed545a..21299d1525c4bf06941fa636d09acb7cc1891d7e 100644 --- a/Documentation/devicetree/bindings/memory.txt +++ b/Documentation/devicetree/bindings/memory.txt @@ -36,6 +36,7 @@ wit the following convention: reg = <(baseaddr) (size)>; (linux,contiguous-region); (linux,default-contiguous-region); + (linux,reserve-region); label = (unique_name); }; @@ -48,6 +49,9 @@ linux,contiguous-region: property indicating that the defined memory linux,default-contiguous-region: property indicating that the region is the default region for all contiguous memory allocations, Linux specific (optional) +linux,reserve-region: property indicating that the contiguous memory will + not be given back to the system allocator. The memory be + always be available for contiguous use. label: an internal name used for automatically associating the cma region with a given device. The label is optional; if the label is not given the client is responsible for diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c index ebfe0043cb8ddc1ec5f017ab95153daab07c88c8..531c166f1b7b93df5d78775cfdce459a0b1b5d2e 100644 --- a/drivers/base/dma-contiguous.c +++ b/drivers/base/dma-contiguous.c @@ -44,6 +44,7 @@ struct cma { unsigned long base_pfn; unsigned long count; unsigned long *bitmap; + bool in_system; }; static DEFINE_MUTEX(cma_mutex); @@ -56,6 +57,7 @@ static struct cma_area { unsigned long size; struct cma *cma; const char *name; + bool to_system; } cma_areas[MAX_CMA_AREAS]; static unsigned cma_area_count; @@ -166,7 +168,7 @@ static __init int cma_activate_area(unsigned long base_pfn, unsigned long count) } static __init struct cma *cma_create_area(unsigned long base_pfn, - unsigned long count) + unsigned long count, bool system) { int bitmap_size = BITS_TO_LONGS(count) * sizeof(long); struct cma *cma; @@ -180,14 +182,17 @@ static __init struct cma *cma_create_area(unsigned long base_pfn, cma->base_pfn = base_pfn; cma->count = count; + cma->in_system = system; cma->bitmap = kzalloc(bitmap_size, GFP_KERNEL); if (!cma->bitmap) goto no_mem; - ret = cma_activate_area(base_pfn, count); - if (ret) - goto error; + if (cma->in_system) { + ret = cma_activate_area(base_pfn, count); + if (ret) + goto error; + } pr_debug("%s: returned %p\n", __func__, (void *)cma); return cma; @@ -209,6 +214,7 @@ int __init cma_fdt_scan(unsigned long node, const char *uname, unsigned long len; __be32 *prop; char *name; + bool in_system; if (!of_get_flat_dt_prop(node, "linux,contiguous-region", NULL)) return 0; @@ -221,10 +227,13 @@ int __init cma_fdt_scan(unsigned long node, const char *uname, size = be32_to_cpu(prop[1]); name = of_get_flat_dt_prop(node, "label", NULL); + in_system = + of_get_flat_dt_prop(node, "linux,reserve-region", NULL) ? 0 : 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, MEMBLOCK_ALLOC_ANYWHERE, name); + dma_contiguous_reserve_area(size, &base, MEMBLOCK_ALLOC_ANYWHERE, name, + in_system); return 0; } @@ -265,8 +274,8 @@ void __init dma_contiguous_reserve(phys_addr_t limit) pr_debug("%s: reserving %ld MiB for global area\n", __func__, (unsigned long)sel_size / SZ_1M); - if (dma_contiguous_reserve_area(sel_size, &base, limit, NULL) - == 0) + if (dma_contiguous_reserve_area(sel_size, &base, limit, NULL, + true) == 0) dma_contiguous_def_base = base; } #ifdef CONFIG_OF @@ -289,7 +298,8 @@ void __init dma_contiguous_reserve(phys_addr_t limit) * devices. */ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t *res_base, - phys_addr_t limit, const char *name) + phys_addr_t limit, const char *name, + bool to_system) { phys_addr_t base = *res_base; phys_addr_t alignment; @@ -342,6 +352,7 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t *res_base, cma_areas[cma_area_count].base = base; cma_areas[cma_area_count].size = size; cma_areas[cma_area_count].name = name; + cma_areas[cma_area_count].to_system = to_system; cma_area_count++; *res_base = base; @@ -424,8 +435,9 @@ static int __init cma_init_reserved_areas(void) for (i = 0; i < cma_area_count; i++) { phys_addr_t base = PFN_DOWN(cma_areas[i].base); unsigned int count = cma_areas[i].size >> PAGE_SHIFT; + bool system = cma_areas[i].to_system; - cma = cma_create_area(base, count); + cma = cma_create_area(base, count, system); if (!IS_ERR(cma)) cma_areas[i].cma = cma; } @@ -461,7 +473,7 @@ struct page *dma_alloc_from_contiguous(struct device *dev, int count, unsigned long mask, pfn, pageno, start = 0; struct cma *cma = dev_get_cma_area(dev); struct page *page = NULL; - int ret; + int ret = 0; int tries = 0; if (!cma || !cma->count) @@ -487,7 +499,8 @@ struct page *dma_alloc_from_contiguous(struct device *dev, int count, break; pfn = cma->base_pfn + pageno; - ret = alloc_contig_range(pfn, pfn + count, MIGRATE_CMA); + if (cma->in_system) + ret = alloc_contig_range(pfn, pfn + count, MIGRATE_CMA); if (ret == 0) { bitmap_set(cma->bitmap, pageno, count); page = pfn_to_page(pfn); @@ -539,7 +552,8 @@ bool dma_release_from_contiguous(struct device *dev, struct page *pages, mutex_lock(&cma_mutex); bitmap_clear(cma->bitmap, pfn - cma->base_pfn, count); - free_contig_range(pfn, count); + if (cma->in_system) + free_contig_range(pfn, count); mutex_unlock(&cma_mutex); return true; diff --git a/include/linux/dma-contiguous.h b/include/linux/dma-contiguous.h index 8a1b3a1b41bddad77ac52325aaf317e96015b67f..e5ebd7e16ac8bfd791016feadee2b46ac3d466d7 100644 --- a/include/linux/dma-contiguous.h +++ b/include/linux/dma-contiguous.h @@ -70,7 +70,8 @@ extern struct cma *dma_contiguous_def_area; void dma_contiguous_reserve(phys_addr_t addr_limit); int dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t *res_base, - phys_addr_t limit, const char *name); + phys_addr_t limit, const char *name, + bool in_system); int dma_contiguous_add_device(struct device *dev, phys_addr_t base); @@ -91,7 +92,19 @@ static inline int dma_declare_contiguous(struct device *dev, phys_addr_t size, phys_addr_t base, phys_addr_t limit) { int ret; - ret = dma_contiguous_reserve_area(size, &base, limit, NULL); + ret = dma_contiguous_reserve_area(size, &base, limit, NULL, true); + if (ret == 0) + ret = dma_contiguous_add_device(dev, base); + return ret; +} + +static inline int dma_declare_contiguous_reserved(struct device *dev, + phys_addr_t size, + phys_addr_t base, + phys_addr_t limit) +{ + int ret; + ret = dma_contiguous_reserve_area(size, &base, limit, NULL, false); if (ret == 0) ret = dma_contiguous_add_device(dev, base); return ret;