Commit 9d8eab7af79cb4ce2de5de39f82c455b1f796963

Authored by Marek Szyprowski
1 parent 57d74bcf30

drivers: of: add initialization code for dma reserved memory

This patch adds device tree support for contiguous and reserved memory
regions defined in device tree.

Large memory blocks can be reliably reserved only during early boot.
This must happen before the whole memory management subsystem is
initialized, because we need to ensure that the given contiguous blocks
are not yet allocated by kernel. Also it must happen before kernel
mappings for the whole low memory are created, to ensure that there will
be no mappings (for reserved blocks) or mapping with special properties
can be created (for CMA blocks). This all happens before device tree
structures are unflattened, so we need to get reserved memory layout
directly from fdt.

Later, those reserved memory regions are assigned to devices on each
device structure initialization.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Acked-by: Tomasz Figa <t.figa@samsung.com>
Acked-by: Stephen Warren <swarren@nvidia.com>
Reviewed-by: Rob Herring <rob.herring@calxeda.com>

Showing 6 changed files with 368 additions and 0 deletions Side-by-side Diff

Documentation/devicetree/bindings/memory.txt
  1 +*** Memory binding ***
  2 +
  3 +The /memory node provides basic information about the address and size
  4 +of the physical memory. This node is usually filled or updated by the
  5 +bootloader, depending on the actual memory configuration of the given
  6 +hardware.
  7 +
  8 +The memory layout is described by the following node:
  9 +
  10 +/ {
  11 + #address-cells = <(n)>;
  12 + #size-cells = <(m)>;
  13 + memory {
  14 + device_type = "memory";
  15 + reg = <(baseaddr1) (size1)
  16 + (baseaddr2) (size2)
  17 + ...
  18 + (baseaddrN) (sizeN)>;
  19 + };
  20 + ...
  21 +};
  22 +
  23 +A memory node follows the typical device tree rules for "reg" property:
  24 +n: number of cells used to store base address value
  25 +m: number of cells used to store size value
  26 +baseaddrX: defines a base address of the defined memory bank
  27 +sizeX: the size of the defined memory bank
  28 +
  29 +
  30 +More than one memory bank can be defined.
  31 +
  32 +
  33 +*** Reserved memory regions ***
  34 +
  35 +In /memory/reserved-memory node one can create child nodes describing
  36 +particular reserved (excluded from normal use) memory regions. Such
  37 +memory regions are usually designed for the special usage by various
  38 +device drivers. A good example are contiguous memory allocations or
  39 +memory sharing with other operating system on the same hardware board.
  40 +Those special memory regions might depend on the board configuration and
  41 +devices used on the target system.
  42 +
  43 +Parameters for each memory region can be encoded into the device tree
  44 +with the following convention:
  45 +
  46 +[(label):] (name) {
  47 + compatible = "linux,contiguous-memory-region", "reserved-memory-region";
  48 + reg = <(address) (size)>;
  49 + (linux,default-contiguous-region);
  50 +};
  51 +
  52 +compatible: one or more of:
  53 + - "linux,contiguous-memory-region" - enables binding of this
  54 + region to Contiguous Memory Allocator (special region for
  55 + contiguous memory allocations, shared with movable system
  56 + memory, Linux kernel-specific).
  57 + - "reserved-memory-region" - compatibility is defined, given
  58 + region is assigned for exclusive usage for by the respective
  59 + devices.
  60 +
  61 +reg: standard property defining the base address and size of
  62 + the memory region
  63 +
  64 +linux,default-contiguous-region: property indicating that the region
  65 + is the default region for all contiguous memory
  66 + allocations, Linux specific (optional)
  67 +
  68 +It is optional to specify the base address, so if one wants to use
  69 +autoconfiguration of the base address, '0' can be specified as a base
  70 +address in the 'reg' property.
  71 +
  72 +The /memory/reserved-memory node must contain the same #address-cells
  73 +and #size-cells value as the root node.
  74 +
  75 +
  76 +*** Device node's properties ***
  77 +
  78 +Once regions in the /memory/reserved-memory node have been defined, they
  79 +may be referenced by other device nodes. Bindings that wish to reference
  80 +memory regions should explicitly document their use of the following
  81 +property:
  82 +
  83 +memory-region = <&phandle_to_defined_region>;
  84 +
  85 +This property indicates that the device driver should use the memory
  86 +region pointed by the given phandle.
  87 +
  88 +
  89 +*** Example ***
  90 +
  91 +This example defines a memory consisting of 4 memory banks. 3 contiguous
  92 +regions are defined for Linux kernel, one default of all device drivers
  93 +(named contig_mem, placed at 0x72000000, 64MiB), one dedicated to the
  94 +framebuffer device (labelled display_mem, placed at 0x78000000, 8MiB)
  95 +and one for multimedia processing (labelled multimedia_mem, placed at
  96 +0x77000000, 64MiB). 'display_mem' region is then assigned to fb@12300000
  97 +device for DMA memory allocations (Linux kernel drivers will use CMA is
  98 +available or dma-exclusive usage otherwise). 'multimedia_mem' is
  99 +assigned to scaler@12500000 and codec@12600000 devices for contiguous
  100 +memory allocations when CMA driver is enabled.
  101 +
  102 +The reason for creating a separate region for framebuffer device is to
  103 +match the framebuffer base address to the one configured by bootloader,
  104 +so once Linux kernel drivers starts no glitches on the displayed boot
  105 +logo appears. Scaller and codec drivers should share the memory
  106 +allocations.
  107 +
  108 +/ {
  109 + #address-cells = <1>;
  110 + #size-cells = <1>;
  111 +
  112 + /* ... */
  113 +
  114 + memory {
  115 + reg = <0x40000000 0x10000000
  116 + 0x50000000 0x10000000
  117 + 0x60000000 0x10000000
  118 + 0x70000000 0x10000000>;
  119 +
  120 + reserved-memory {
  121 + #address-cells = <1>;
  122 + #size-cells = <1>;
  123 +
  124 + /*
  125 + * global autoconfigured region for contiguous allocations
  126 + * (used only with Contiguous Memory Allocator)
  127 + */
  128 + contig_region@0 {
  129 + compatible = "linux,contiguous-memory-region";
  130 + reg = <0x0 0x4000000>;
  131 + linux,default-contiguous-region;
  132 + };
  133 +
  134 + /*
  135 + * special region for framebuffer
  136 + */
  137 + display_region: region@78000000 {
  138 + compatible = "linux,contiguous-memory-region", "reserved-memory-region";
  139 + reg = <0x78000000 0x800000>;
  140 + };
  141 +
  142 + /*
  143 + * special region for multimedia processing devices
  144 + */
  145 + multimedia_region: region@77000000 {
  146 + compatible = "linux,contiguous-memory-region";
  147 + reg = <0x77000000 0x4000000>;
  148 + };
  149 + };
  150 + };
  151 +
  152 + /* ... */
  153 +
  154 + fb0: fb@12300000 {
  155 + status = "okay";
  156 + memory-region = <&display_region>;
  157 + };
  158 +
  159 + scaler: scaler@12500000 {
  160 + status = "okay";
  161 + memory-region = <&multimedia_region>;
  162 + };
  163 +
  164 + codec: codec@12600000 {
  165 + status = "okay";
  166 + memory-region = <&multimedia_region>;
  167 + };
  168 +};
... ... @@ -80,5 +80,11 @@
80 80 depends on MTD
81 81 def_bool y
82 82  
  83 +config OF_RESERVED_MEM
  84 + depends on OF_FLATTREE && (DMA_CMA || (HAVE_GENERIC_DMA_COHERENT && HAVE_MEMBLOCK))
  85 + def_bool y
  86 + help
  87 + Initialization code for DMA reserved memory
  88 +
83 89 endmenu # OF
... ... @@ -10,4 +10,5 @@
10 10 obj-$(CONFIG_OF_PCI) += of_pci.o
11 11 obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o
12 12 obj-$(CONFIG_OF_MTD) += of_mtd.o
  13 +obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
drivers/of/of_reserved_mem.c
  1 +/*
  2 + * Device tree based initialization code for reserved memory.
  3 + *
  4 + * Copyright (c) 2013 Samsung Electronics Co., Ltd.
  5 + * http://www.samsung.com
  6 + * Author: Marek Szyprowski <m.szyprowski@samsung.com>
  7 + *
  8 + * This program is free software; you can redistribute it and/or
  9 + * modify it under the terms of the GNU General Public License as
  10 + * published by the Free Software Foundation; either version 2 of the
  11 + * License or (at your optional) any later version of the license.
  12 + */
  13 +
  14 +#include <asm/dma-contiguous.h>
  15 +
  16 +#include <linux/memblock.h>
  17 +#include <linux/err.h>
  18 +#include <linux/of.h>
  19 +#include <linux/of_fdt.h>
  20 +#include <linux/of_platform.h>
  21 +#include <linux/mm.h>
  22 +#include <linux/sizes.h>
  23 +#include <linux/mm_types.h>
  24 +#include <linux/dma-contiguous.h>
  25 +#include <linux/dma-mapping.h>
  26 +#include <linux/of_reserved_mem.h>
  27 +
  28 +#define MAX_RESERVED_REGIONS 16
  29 +struct reserved_mem {
  30 + phys_addr_t base;
  31 + unsigned long size;
  32 + struct cma *cma;
  33 + char name[32];
  34 +};
  35 +static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS];
  36 +static int reserved_mem_count;
  37 +
  38 +static int __init fdt_scan_reserved_mem(unsigned long node, const char *uname,
  39 + int depth, void *data)
  40 +{
  41 + struct reserved_mem *rmem = &reserved_mem[reserved_mem_count];
  42 + phys_addr_t base, size;
  43 + int is_cma, is_reserved;
  44 + unsigned long len;
  45 + const char *status;
  46 + __be32 *prop;
  47 +
  48 + is_cma = IS_ENABLED(CONFIG_DMA_CMA) &&
  49 + of_flat_dt_is_compatible(node, "linux,contiguous-memory-region");
  50 + is_reserved = of_flat_dt_is_compatible(node, "reserved-memory-region");
  51 +
  52 + if (!is_reserved && !is_cma) {
  53 + /* ignore node and scan next one */
  54 + return 0;
  55 + }
  56 +
  57 + status = of_get_flat_dt_prop(node, "status", &len);
  58 + if (status && strcmp(status, "okay") != 0) {
  59 + /* ignore disabled node nad scan next one */
  60 + return 0;
  61 + }
  62 +
  63 + prop = of_get_flat_dt_prop(node, "reg", &len);
  64 + if (!prop || (len < (dt_root_size_cells + dt_root_addr_cells) *
  65 + sizeof(__be32))) {
  66 + pr_err("Reserved mem: node %s, incorrect \"reg\" property\n",
  67 + uname);
  68 + /* ignore node and scan next one */
  69 + return 0;
  70 + }
  71 + base = dt_mem_next_cell(dt_root_addr_cells, &prop);
  72 + size = dt_mem_next_cell(dt_root_size_cells, &prop);
  73 +
  74 + if (!size) {
  75 + /* ignore node and scan next one */
  76 + return 0;
  77 + }
  78 +
  79 + pr_info("Reserved mem: found %s, memory base %lx, size %ld MiB\n",
  80 + uname, (unsigned long)base, (unsigned long)size / SZ_1M);
  81 +
  82 + if (reserved_mem_count == ARRAY_SIZE(reserved_mem))
  83 + return -ENOSPC;
  84 +
  85 + rmem->base = base;
  86 + rmem->size = size;
  87 + strlcpy(rmem->name, uname, sizeof(rmem->name));
  88 +
  89 + if (is_cma) {
  90 + struct cma *cma;
  91 + if (dma_contiguous_reserve_area(size, base, 0, &cma) == 0) {
  92 + rmem->cma = cma;
  93 + reserved_mem_count++;
  94 + if (of_get_flat_dt_prop(node,
  95 + "linux,default-contiguous-region",
  96 + NULL))
  97 + dma_contiguous_set_default(cma);
  98 + }
  99 + } else if (is_reserved) {
  100 + if (memblock_remove(base, size) == 0)
  101 + reserved_mem_count++;
  102 + else
  103 + pr_err("Failed to reserve memory for %s\n", uname);
  104 + }
  105 +
  106 + return 0;
  107 +}
  108 +
  109 +static struct reserved_mem *get_dma_memory_region(struct device *dev)
  110 +{
  111 + struct device_node *node;
  112 + const char *name;
  113 + int i;
  114 +
  115 + node = of_parse_phandle(dev->of_node, "memory-region", 0);
  116 + if (!node)
  117 + return NULL;
  118 +
  119 + name = kbasename(node->full_name);
  120 + for (i = 0; i < reserved_mem_count; i++)
  121 + if (strcmp(name, reserved_mem[i].name) == 0)
  122 + return &reserved_mem[i];
  123 + return NULL;
  124 +}
  125 +
  126 +/**
  127 + * of_reserved_mem_device_init() - assign reserved memory region to given device
  128 + *
  129 + * This function assign memory region pointed by "memory-region" device tree
  130 + * property to the given device.
  131 + */
  132 +void of_reserved_mem_device_init(struct device *dev)
  133 +{
  134 + struct reserved_mem *region = get_dma_memory_region(dev);
  135 + if (!region)
  136 + return;
  137 +
  138 + if (region->cma) {
  139 + dev_set_cma_area(dev, region->cma);
  140 + pr_info("Assigned CMA %s to %s device\n", region->name,
  141 + dev_name(dev));
  142 + } else {
  143 + if (dma_declare_coherent_memory(dev, region->base, region->base,
  144 + region->size, DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) != 0)
  145 + pr_info("Declared reserved memory %s to %s device\n",
  146 + region->name, dev_name(dev));
  147 + }
  148 +}
  149 +
  150 +/**
  151 + * of_reserved_mem_device_release() - release reserved memory device structures
  152 + *
  153 + * This function releases structures allocated for memory region handling for
  154 + * the given device.
  155 + */
  156 +void of_reserved_mem_device_release(struct device *dev)
  157 +{
  158 + struct reserved_mem *region = get_dma_memory_region(dev);
  159 + if (!region && !region->cma)
  160 + dma_release_declared_memory(dev);
  161 +}
  162 +
  163 +/**
  164 + * early_init_dt_scan_reserved_mem() - create reserved memory regions
  165 + *
  166 + * This function grabs memory from early allocator for device exclusive use
  167 + * defined in device tree structures. It should be called by arch specific code
  168 + * once the early allocator (memblock) has been activated and all other
  169 + * subsystems have already allocated/reserved memory.
  170 + */
  171 +void __init early_init_dt_scan_reserved_mem(void)
  172 +{
  173 + of_scan_flat_dt_by_path("/memory/reserved-memory",
  174 + fdt_scan_reserved_mem, NULL);
  175 +}
drivers/of/platform.c
... ... @@ -21,6 +21,7 @@
21 21 #include <linux/of_device.h>
22 22 #include <linux/of_irq.h>
23 23 #include <linux/of_platform.h>
  24 +#include <linux/of_reserved_mem.h>
24 25 #include <linux/platform_device.h>
25 26  
26 27 const struct of_device_id of_default_bus_match_table[] = {
... ... @@ -218,6 +219,8 @@
218 219 dev->dev.bus = &platform_bus_type;
219 220 dev->dev.platform_data = platform_data;
220 221  
  222 + of_reserved_mem_device_init(&dev->dev);
  223 +
221 224 /* We do not fill the DMA ops for platform devices by default.
222 225 * This is currently the responsibility of the platform code
223 226 * to do such, possibly using a device notifier
... ... @@ -225,6 +228,7 @@
225 228  
226 229 if (of_device_add(dev) != 0) {
227 230 platform_device_put(dev);
  231 + of_reserved_mem_device_release(&dev->dev);
228 232 return NULL;
229 233 }
230 234  
include/linux/of_reserved_mem.h
  1 +#ifndef __OF_RESERVED_MEM_H
  2 +#define __OF_RESERVED_MEM_H
  3 +
  4 +#ifdef CONFIG_OF_RESERVED_MEM
  5 +void of_reserved_mem_device_init(struct device *dev);
  6 +void of_reserved_mem_device_release(struct device *dev);
  7 +void early_init_dt_scan_reserved_mem(void);
  8 +#else
  9 +static inline void of_reserved_mem_device_init(struct device *dev) { }
  10 +static inline void of_reserved_mem_device_release(struct device *dev) { }
  11 +static inline void early_init_dt_scan_reserved_mem(void) { }
  12 +#endif
  13 +
  14 +#endif /* __OF_RESERVED_MEM_H */