new file mode 100644
@@ -0,0 +1,152 @@
+*** 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 folllowing node:
+
+memory {
+ device_type = "memory";
+ reg = <(baseaddr1) (size1)
+ (baseaddr2) (size2)
+ ...
+ (baseaddrN) (sizeN)>;
+};
+
+baseaddrX: the 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 additional 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
+wit the following convention:
+
+[(label):] (name)@(address) {
+ compatible = "contiguous-memory-region", "reserved-memory-region";
+ reg = <(address) (size)>;
+ (linux,default-contiguous-region);
+};
+
+label: label given to the defined region (optional)
+name: an name given to the defined region
+address: the base address of the defined region
+size: the size of the memory region
+
+compatible: "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), alternatively if
+ "reserved-memory-region" - compatibility is defined, given
+ region is assigned for exclusive usage for DMA transfers
+
+linux,default-contiguous-region: property indicating that the region
+ is the default region for all contiguous memory
+ allocations, Linux specific (optional)
+
+Each defined region must use unique name. It is optional to specify the
+base address, so if one wants to use autoconfiguration of the base
+address, he must specify the '0' as base address in the 'reg' property
+and assign ann unique name to such regions.
+
+
+*** Device node's properties ***
+
+Once the regions in the /memory/reserved-memory node are defined, they
+can be assigned to device nodes to enable drivers for their special use.
+The following properties are defined:
+
+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.
+
+/ {
+ /* ... */
+ 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 = "contiguous-memory-region";
+ reg = <0x0 0x4000000>;
+ linux,default-contiguous-region;
+ };
+
+ /*
+ * special region for framebuffer
+ */
+ display_mem: region@78000000 {
+ compatible = "contiguous-memory-region", "reserved-memory-region";
+ reg = <0x78000000 0x800000>;
+ };
+
+ /*
+ * special region for multimedia processing devices
+ */
+ multimedia_mem: region@77000000 {
+ compatible = "contiguous-memory-region";
+ reg = <0x77000000 0x4000000>;
+ };
+ };
+ };
+
+ /* ... */
+
+ fb0: fb@12300000 {
+ status = "okay";
+ memory-region = <&display_mem>;
+ };
+
+ scaler: scaler@12500000 {
+ status = "okay";
+ memory-region = <&multimedia_mem>;
+ };
+
+ codec: codec@12600000 {
+ status = "okay";
+ memory-region = <&multimedia_mem>;
+ };
+};
@@ -80,4 +80,10 @@ config OF_MTD
depends on MTD
def_bool y
+config OF_RESERVED_MEM
+ depends on CMA || (HAVE_GENERIC_DMA_COHERENT && HAVE_MEMBLOCK)
+ def_bool y
+ help
+ Initialization code for DMA reserved memory
+
endmenu # OF
@@ -10,3 +10,4 @@ obj-$(CONFIG_OF_MDIO) += of_mdio.o
obj-$(CONFIG_OF_PCI) += of_pci.o
obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o
obj-$(CONFIG_OF_MTD) += of_mtd.o
+obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
new file mode 100644
@@ -0,0 +1,197 @@
+/*
+ * Device tree based initialization code for reserved memory.
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ * Author: Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License or (at your optional) any later version of the license.
+ */
+
+#include <asm/dma-contiguous.h>
+
+#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 reserved_mem_fdt_scan(unsigned long node, const char *uname,
+ int depth, void *data)
+{
+ static int size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT;
+ static int addr_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT;
+ static int my_depth;
+ phys_addr_t base, size;
+ int is_cma, is_reserved;
+ unsigned long len;
+ __be32 *prop;
+
+ if (depth == 1 && my_depth == 0 && strcmp(uname, "memory") == 0) {
+ my_depth = depth;
+ /* scan next node */
+ return 0;
+ } else if (depth == 2 && my_depth == 1 &&
+ strcmp(uname, "reserved-memory") == 0) {
+ prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
+ if (prop)
+ size_cells = be32_to_cpup(prop);
+
+ prop = of_get_flat_dt_prop(node, "#address-cells", NULL);
+ if (prop)
+ addr_cells = be32_to_cpup(prop);
+
+ my_depth = depth;
+ /* scan next node */
+ return 0;
+ } else if (depth != 3 && my_depth != 2) {
+ /* scan next node */
+ return 0;
+ } else if (depth < my_depth) {
+ /* break scan now */
+ return 1;
+ }
+
+ /* now we are scanning a /memory/reserved-memory child */
+
+ is_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 && IS_ENABLED(CONFIG_CMA))) {
+ /* ignore node and scan next one */
+ return 0;
+ }
+
+ prop = of_get_flat_dt_prop(node, "reg", &len);
+ if (!prop || (len != (size_cells + 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(addr_cells, &prop);
+ size = dt_mem_next_cell(size_cells, &prop);
+
+ if (!size) {
+ /* ignore node and scan next one */
+ return 0;
+ }
+
+ 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;
+
+ reserved_mem[reserved_mem_count].base = base;
+ reserved_mem[reserved_mem_count].size = size;
+ strlcpy(reserved_mem[reserved_mem_count].name, uname,
+ sizeof(reserved_mem[reserved_mem_count].name));
+
+ if (IS_ENABLED(CONFIG_CMA) && is_cma) {
+ struct cma *cma;
+ if (dma_contiguous_reserve_area(size, base, 0, &cma) == 0) {
+ reserved_mem[reserved_mem_count].cma = cma;
+ reserved_mem_count++;
+
+ if (of_get_flat_dt_prop(node,
+ "linux,default-contiguous-region",
+ NULL))
+ dma_contiguous_default_area = 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;
+}
+
+static struct reserved_mem *get_dma_memory_region(struct device *dev)
+{
+ struct device_node *node;
+ const char *name;
+ int i;
+
+ node = of_parse_phandle(dev->of_node, "memory-region", 0);
+ if (!node)
+ return 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;
+}
+
+/**
+ * of_reserved_mem_device_init() - assign reserved memory region to given device
+ *
+ * This function assign memory region pointed by "dma-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;
+
+ 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));
+ }
+}
+
+/**
+ * of_reserved_mem_device_release() - release reserved memory device structures
+ *
+ * This function releases structures allocated for memory region handling for
+ * the given device.
+ */
+void of_reserved_mem_device_release(struct device *dev)
+{
+ struct reserved_mem *region = get_dma_memory_region(dev);
+ if (!region && !region->cma)
+ dma_release_declared_memory(dev);
+}
+
+/**
+ * dma_reserved_mem_reserve() - grab memory reserved for device exclusive use
+ *
+ * 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.
+ */
+void __init dma_reserved_mem_of_reserve(void)
+{
+ if (initial_boot_params)
+ of_scan_flat_dt(reserved_mem_fdt_scan, NULL);
+}
@@ -21,6 +21,7 @@
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
+#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
const struct of_device_id of_default_bus_match_table[] = {
@@ -196,6 +197,7 @@ EXPORT_SYMBOL(of_device_alloc);
* Returns pointer to created platform device, or NULL if a device was not
* registered. Unavailable devices will not get registered.
*/
+
struct platform_device *of_platform_device_create_pdata(
struct device_node *np,
const char *bus_id,
@@ -218,6 +220,8 @@ struct platform_device *of_platform_device_create_pdata(
dev->dev.bus = &platform_bus_type;
dev->dev.platform_data = platform_data;
+ of_reserved_mem_device_init(&dev->dev);
+
/* We do not fill the DMA ops for platform devices by default.
* This is currently the responsibility of the platform code
* to do such, possibly using a device notifier
@@ -225,6 +229,7 @@ struct platform_device *of_platform_device_create_pdata(
if (of_device_add(dev) != 0) {
platform_device_put(dev);
+ of_reserved_mem_device_release(&dev->dev);
return NULL;
}
new file mode 100644
@@ -0,0 +1,14 @@
+#ifndef __OF_RESERVED_MEM_H
+#define __OF_RESERVED_MEM_H
+
+#ifdef CONFIG_OF_RESERVED_MEM
+void of_reserved_mem_device_init(struct device *dev);
+void of_reserved_mem_device_release(struct device *dev);
+void __init dma_reserved_mem_of_reserve(void);
+#else
+#define of_reserved_mem_device_init(dev) (void)0
+#define of_reserved_mem_device_release(dev) (void)0
+#define dma_reserved_mem_of_reserve() (void)0
+#endif
+
+#endif /* __OF_RESERVED_MEM_H */