Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 64c35386 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull DMA mapping update from Marek Szyprowski:
 "This contains an addition of Device Tree support for reserved memory
  regions (Contiguous Memory Allocator is one of the drivers for it) and
  changes required by the KVM extensions for PowerPC architectue"

* 'for-v3.12' of git://git.linaro.org/people/mszyprowski/linux-dma-mapping:
  ARM: init: add support for reserved memory defined by device tree
  drivers: of: add initialization code for dma reserved memory
  drivers: of: add function to scan fdt nodes given by path
  drivers: dma-contiguous: clean source code and prepare for device tree
parents d8cacd3a 10bcdfb8
Loading
Loading
Loading
Loading
+168 −0
Original line number Diff line number Diff line
*** Memory binding ***

The /memory node provides basic information about the address and size
of the physical memory. This node is usually filled or updated by the
bootloader, depending on the actual memory configuration of the given
hardware.

The memory layout is described by the following node:

/ {
	#address-cells = <(n)>;
	#size-cells = <(m)>;
	memory {
		device_type = "memory";
		reg =  <(baseaddr1) (size1)
			(baseaddr2) (size2)
			...
			(baseaddrN) (sizeN)>;
	};
	...
};

A memory node follows the typical device tree rules for "reg" property:
n:		number of cells used to store base address value
m:		number of cells used to store size value
baseaddrX:	defines a base address of the defined memory bank
sizeX:		the size of the defined memory bank


More than one memory bank can be defined.


*** Reserved memory regions ***

In /memory/reserved-memory node one can create child nodes describing
particular reserved (excluded from normal use) memory regions. Such
memory regions are usually designed for the 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
with the following convention:

[(label):] (name) {
	compatible = "linux,contiguous-memory-region", "reserved-memory-region";
	reg = <(address) (size)>;
	(linux,default-contiguous-region);
};

compatible:	one or more of:
	- "linux,contiguous-memory-region" - enables binding of this
	  region to Contiguous Memory Allocator (special region for
	  contiguous memory allocations, shared with movable system
	  memory, Linux kernel-specific).
	- "reserved-memory-region" - compatibility is defined, given
	  region is assigned for exclusive usage for by the respective
	  devices.

reg:	standard property defining the base address and size of
	the memory region

linux,default-contiguous-region: property indicating that the region
	is the default region for all contiguous memory
	allocations, Linux specific (optional)

It is optional to specify the base address, so if one wants to use
autoconfiguration of the base address, '0' can be specified as a base
address in the 'reg' property.

The /memory/reserved-memory node must contain the same #address-cells
and #size-cells value as the root node.


*** Device node's properties ***

Once regions in the /memory/reserved-memory node have been defined, they
may be referenced by other device nodes. Bindings that wish to reference
memory regions should explicitly document their use of the following
property:

memory-region = <&phandle_to_defined_region>;

This property indicates that the device driver should use the memory
region pointed by the given phandle.


*** Example ***

This example defines a memory consisting of 4 memory banks. 3 contiguous
regions are defined for Linux kernel, one default of all device drivers
(named contig_mem, placed at 0x72000000, 64MiB), one dedicated to the
framebuffer device (labelled display_mem, placed at 0x78000000, 8MiB)
and one for multimedia processing (labelled multimedia_mem, placed at
0x77000000, 64MiB). 'display_mem' region is then assigned to fb@12300000
device for DMA memory allocations (Linux kernel drivers will use CMA is
available or dma-exclusive usage otherwise). 'multimedia_mem' is
assigned to scaler@12500000 and codec@12600000 devices for contiguous
memory allocations when CMA driver is enabled.

The reason for creating a separate region for framebuffer device is to
match the framebuffer base address to the one configured by bootloader,
so once Linux kernel drivers starts no glitches on the displayed boot
logo appears. Scaller and codec drivers should share the memory
allocations.

/ {
	#address-cells = <1>;
	#size-cells = <1>;

	/* ... */

	memory {
		reg =  <0x40000000 0x10000000
			0x50000000 0x10000000
			0x60000000 0x10000000
			0x70000000 0x10000000>;

		reserved-memory {
			#address-cells = <1>;
			#size-cells = <1>;

			/*
			 * global autoconfigured region for contiguous allocations
			 * (used only with Contiguous Memory Allocator)
			 */
			contig_region@0 {
				compatible = "linux,contiguous-memory-region";
				reg = <0x0 0x4000000>;
				linux,default-contiguous-region;
			};

			/*
			 * special region for framebuffer
			 */
			display_region: region@78000000 {
				compatible = "linux,contiguous-memory-region", "reserved-memory-region";
				reg = <0x78000000 0x800000>;
			};

			/*
			 * special region for multimedia processing devices
			 */
			multimedia_region: region@77000000 {
				compatible = "linux,contiguous-memory-region";
				reg = <0x77000000 0x4000000>;
			};
		};
	};

	/* ... */

	fb0: fb@12300000 {
		status = "okay";
		memory-region = <&display_region>;
	};

	scaler: scaler@12500000 {
		status = "okay";
		memory-region = <&multimedia_region>;
	};

	codec: codec@12600000 {
		status = "okay";
		memory-region = <&multimedia_region>;
	};
};
+0 −1
Original line number Diff line number Diff line
@@ -5,7 +5,6 @@
#ifdef CONFIG_DMA_CMA

#include <linux/types.h>
#include <asm-generic/dma-contiguous.h>

void dma_contiguous_early_fixup(phys_addr_t base, unsigned long size);

+3 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include <linux/nodemask.h>
#include <linux/initrd.h>
#include <linux/of_fdt.h>
#include <linux/of_reserved_mem.h>
#include <linux/highmem.h>
#include <linux/gfp.h>
#include <linux/memblock.h>
@@ -378,6 +379,8 @@ void __init arm_memblock_init(struct meminfo *mi,
	if (mdesc->reserve)
		mdesc->reserve();

	early_init_dt_scan_reserved_mem();

	/*
	 * reserve memory for DMA contigouos allocations,
	 * must come from DMA area inside low memory
+0 −1
Original line number Diff line number Diff line
@@ -4,7 +4,6 @@
#ifdef __KERNEL__

#include <linux/types.h>
#include <asm-generic/dma-contiguous.h>

static inline void
dma_contiguous_early_fixup(phys_addr_t base, unsigned long size) { }
+44 −75
Original line number Diff line number Diff line
@@ -96,7 +96,7 @@ static inline __maybe_unused phys_addr_t cma_early_percent_memory(void)
#endif

/**
 * dma_contiguous_reserve() - reserve area for contiguous memory handling
 * 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
@@ -124,22 +124,29 @@ void __init dma_contiguous_reserve(phys_addr_t limit)
#endif
	}

	if (selected_size) {
	if (selected_size && !dma_contiguous_default_area) {
		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);
		dma_contiguous_reserve_area(selected_size, 0, limit,
					    &dma_contiguous_default_area);
	}
};

static DEFINE_MUTEX(cma_mutex);

static int __init cma_activate_area(unsigned long base_pfn, unsigned long count)
static int __init 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 +160,53 @@ static int __init cma_activate_area(unsigned long base_pfn, unsigned long count)
		}
		init_cma_reserved_pageblock(pfn_to_page(base_pfn));
	} while (--i);
	return 0;
}

static struct cma * __init cma_create_area(unsigned long base_pfn,
				     unsigned long count)
{
	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);

	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);
	return 0;
}

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;
static struct cma cma_areas[MAX_CMA_AREAS];
static unsigned cma_area_count;

static int __init cma_init_reserved_areas(void)
{
	struct cma_reserved *r = cma_reserved;
	unsigned i = cma_reserved_count;

	pr_debug("%s()\n", __func__);
	int i;

	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);
	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_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];
	struct cma *cma = &cma_areas[cma_area_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 +224,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 +234,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 +245,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->base_pfn = PFN_DOWN(base);
	cma->count = size >> PAGE_SHIFT;
	*res_cma = cma;
	cma_area_count++;

	pr_info("CMA: reserved %ld MiB at %08lx\n", (unsigned long)size / SZ_1M,
		(unsigned long)base);

@@ -289,7 +258,7 @@ 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;
}

/**
Loading