diff options
Diffstat (limited to 'drivers/of/of_reserved_mem.c')
| -rw-r--r-- | drivers/of/of_reserved_mem.c | 282 | 
1 files changed, 163 insertions, 119 deletions
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 0fe40c7d690..632aae86137 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -1,9 +1,11 @@  /*   * Device tree based initialization code for reserved memory.   * - * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Copyright (c) 2013, The Linux Foundation. All Rights Reserved. + * Copyright (c) 2013,2014 Samsung Electronics Co., Ltd.   *		http://www.samsung.com   * Author: Marek Szyprowski <m.szyprowski@samsung.com> + * Author: Josh Cartwright <joshc@codeaurora.org>   *   * This program is free software; you can redistribute it and/or   * modify it under the terms of the GNU General Public License as @@ -11,163 +13,205 @@   * License or (at your optional) any later version of the license.   */ -#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/sizes.h> -#include <linux/mm_types.h> -#include <linux/dma-contiguous.h> -#include <linux/dma-mapping.h>  #include <linux/of_reserved_mem.h>  #define MAX_RESERVED_REGIONS	16 -struct reserved_mem { -	phys_addr_t		base; -	unsigned long		size; -	struct cma		*cma; -	char			name[32]; -};  static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS];  static int reserved_mem_count; -static int __init fdt_scan_reserved_mem(unsigned long node, const char *uname, -					int depth, void *data) +#if defined(CONFIG_HAVE_MEMBLOCK) +#include <linux/memblock.h> +int __init __weak early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, +	phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap, +	phys_addr_t *res_base)  { -	struct reserved_mem *rmem = &reserved_mem[reserved_mem_count]; -	phys_addr_t base, size; -	int is_cma, is_reserved; -	unsigned long len; -	const char *status; -	__be32 *prop; - -	is_cma = IS_ENABLED(CONFIG_DMA_CMA) && -	       of_flat_dt_is_compatible(node, "linux,contiguous-memory-region"); -	is_reserved = of_flat_dt_is_compatible(node, "reserved-memory-region"); - -	if (!is_reserved && !is_cma) { -		/* ignore node and scan next one */ -		return 0; +	/* +	 * We use __memblock_alloc_base() because memblock_alloc_base() +	 * panic()s on allocation failure. +	 */ +	phys_addr_t base = __memblock_alloc_base(size, align, end); +	if (!base) +		return -ENOMEM; + +	/* +	 * Check if the allocated region fits in to start..end window +	 */ +	if (base < start) { +		memblock_free(base, size); +		return -ENOMEM;  	} -	status = of_get_flat_dt_prop(node, "status", &len); -	if (status && strcmp(status, "okay") != 0) { -		/* ignore disabled node nad scan next one */ -		return 0; -	} +	*res_base = base; +	if (nomap) +		return memblock_remove(base, size); +	return 0; +} +#else +int __init __weak early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, +	phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap, +	phys_addr_t *res_base) +{ +	pr_err("Reserved memory not supported, ignoring region 0x%llx%s\n", +		  size, nomap ? " (nomap)" : ""); +	return -ENOSYS; +} +#endif -	prop = of_get_flat_dt_prop(node, "reg", &len); -	if (!prop || (len < (dt_root_size_cells + dt_root_addr_cells) * -			     sizeof(__be32))) { -		pr_err("Reserved mem: node %s, incorrect \"reg\" property\n", -		       uname); -		/* ignore node and scan next one */ -		return 0; -	} -	base = dt_mem_next_cell(dt_root_addr_cells, &prop); -	size = dt_mem_next_cell(dt_root_size_cells, &prop); +/** + * res_mem_save_node() - save fdt node for second pass initialization + */ +void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname, +				      phys_addr_t base, phys_addr_t size) +{ +	struct reserved_mem *rmem = &reserved_mem[reserved_mem_count]; -	if (!size) { -		/* ignore node and scan next one */ -		return 0; +	if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) { +		pr_err("Reserved memory: not enough space all defined regions.\n"); +		return;  	} -	pr_info("Reserved mem: found %s, memory base %lx, size %ld MiB\n", -		uname, (unsigned long)base, (unsigned long)size / SZ_1M); - -	if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) -		return -ENOSPC; - +	rmem->fdt_node = node; +	rmem->name = uname;  	rmem->base = base;  	rmem->size = size; -	strlcpy(rmem->name, uname, sizeof(rmem->name)); - -	if (is_cma) { -		struct cma *cma; -		if (dma_contiguous_reserve_area(size, base, 0, &cma) == 0) { -			rmem->cma = cma; -			reserved_mem_count++; -			if (of_get_flat_dt_prop(node, -						"linux,default-contiguous-region", -						NULL)) -				dma_contiguous_set_default(cma); -		} -	} else if (is_reserved) { -		if (memblock_remove(base, size) == 0) -			reserved_mem_count++; -		else -			pr_err("Failed to reserve memory for %s\n", uname); -	} -	return 0; +	reserved_mem_count++; +	return;  } -static struct reserved_mem *get_dma_memory_region(struct device *dev) +/** + * res_mem_alloc_size() - allocate reserved memory described by 'size', 'align' + *			  and 'alloc-ranges' properties + */ +static int __init __reserved_mem_alloc_size(unsigned long node, +	const char *uname, phys_addr_t *res_base, phys_addr_t *res_size)  { -	struct device_node *node; -	const char *name; -	int i; +	int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); +	phys_addr_t start = 0, end = 0; +	phys_addr_t base = 0, align = 0, size; +	int len; +	const __be32 *prop; +	int nomap; +	int ret; + +	prop = of_get_flat_dt_prop(node, "size", &len); +	if (!prop) +		return -EINVAL; + +	if (len != dt_root_size_cells * sizeof(__be32)) { +		pr_err("Reserved memory: invalid size property in '%s' node.\n", +				uname); +		return -EINVAL; +	} +	size = dt_mem_next_cell(dt_root_size_cells, &prop); -	node = of_parse_phandle(dev->of_node, "memory-region", 0); -	if (!node) -		return NULL; +	nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; -	name = kbasename(node->full_name); -	for (i = 0; i < reserved_mem_count; i++) -		if (strcmp(name, reserved_mem[i].name) == 0) -			return &reserved_mem[i]; -	return NULL; -} +	prop = of_get_flat_dt_prop(node, "alignment", &len); +	if (prop) { +		if (len != dt_root_addr_cells * sizeof(__be32)) { +			pr_err("Reserved memory: invalid alignment property in '%s' node.\n", +				uname); +			return -EINVAL; +		} +		align = dt_mem_next_cell(dt_root_addr_cells, &prop); +	} -/** - * of_reserved_mem_device_init() - assign reserved memory region to given device - * - * This function assign memory region pointed by "memory-region" device tree - * property to the given device. - */ -void of_reserved_mem_device_init(struct device *dev) -{ -	struct reserved_mem *region = get_dma_memory_region(dev); -	if (!region) -		return; +	prop = of_get_flat_dt_prop(node, "alloc-ranges", &len); +	if (prop) { + +		if (len % t_len != 0) { +			pr_err("Reserved memory: invalid alloc-ranges property in '%s', skipping node.\n", +			       uname); +			return -EINVAL; +		} + +		base = 0; + +		while (len > 0) { +			start = dt_mem_next_cell(dt_root_addr_cells, &prop); +			end = start + dt_mem_next_cell(dt_root_size_cells, +						       &prop); + +			ret = early_init_dt_alloc_reserved_memory_arch(size, +					align, start, end, nomap, &base); +			if (ret == 0) { +				pr_debug("Reserved memory: allocated memory for '%s' node: base %pa, size %ld MiB\n", +					uname, &base, +					(unsigned long)size / SZ_1M); +				break; +			} +			len -= t_len; +		} -	if (region->cma) { -		dev_set_cma_area(dev, region->cma); -		pr_info("Assigned CMA %s to %s device\n", region->name, -			dev_name(dev));  	} else { -		if (dma_declare_coherent_memory(dev, region->base, region->base, -		    region->size, DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) != 0) -			pr_info("Declared reserved memory %s to %s device\n", -				region->name, dev_name(dev)); +		ret = early_init_dt_alloc_reserved_memory_arch(size, align, +							0, 0, nomap, &base); +		if (ret == 0) +			pr_debug("Reserved memory: allocated memory for '%s' node: base %pa, size %ld MiB\n", +				uname, &base, (unsigned long)size / SZ_1M); +	} + +	if (base == 0) { +		pr_info("Reserved memory: failed to allocate memory for node '%s'\n", +			uname); +		return -ENOMEM;  	} + +	*res_base = base; +	*res_size = size; + +	return 0;  } +static const struct of_device_id __rmem_of_table_sentinel +	__used __section(__reservedmem_of_table_end); +  /** - * of_reserved_mem_device_release() - release reserved memory device structures - * - * This function releases structures allocated for memory region handling for - * the given device. + * res_mem_init_node() - call region specific reserved memory init code   */ -void of_reserved_mem_device_release(struct device *dev) +static int __init __reserved_mem_init_node(struct reserved_mem *rmem)  { -	struct reserved_mem *region = get_dma_memory_region(dev); -	if (!region && !region->cma) -		dma_release_declared_memory(dev); +	extern const struct of_device_id __reservedmem_of_table[]; +	const struct of_device_id *i; + +	for (i = __reservedmem_of_table; i < &__rmem_of_table_sentinel; i++) { +		reservedmem_of_init_fn initfn = i->data; +		const char *compat = i->compatible; + +		if (!of_flat_dt_is_compatible(rmem->fdt_node, compat)) +			continue; + +		if (initfn(rmem) == 0) { +			pr_info("Reserved memory: initialized node %s, compatible id %s\n", +				rmem->name, compat); +			return 0; +		} +	} +	return -ENOENT;  }  /** - * early_init_dt_scan_reserved_mem() - create reserved memory regions - * - * This function grabs memory from early allocator for device exclusive use - * defined in device tree structures. It should be called by arch specific code - * once the early allocator (memblock) has been activated and all other - * subsystems have already allocated/reserved memory. + * fdt_init_reserved_mem - allocate and init all saved reserved memory regions   */ -void __init early_init_dt_scan_reserved_mem(void) +void __init fdt_init_reserved_mem(void)  { -	of_scan_flat_dt_by_path("/memory/reserved-memory", -				fdt_scan_reserved_mem, NULL); +	int i; +	for (i = 0; i < reserved_mem_count; i++) { +		struct reserved_mem *rmem = &reserved_mem[i]; +		unsigned long node = rmem->fdt_node; +		int err = 0; + +		if (rmem->size == 0) +			err = __reserved_mem_alloc_size(node, rmem->name, +						 &rmem->base, &rmem->size); +		if (err == 0) +			__reserved_mem_init_node(rmem); +	}  }  | 
