diff options
| author | Grant Likely <grant.likely@linaro.org> | 2014-03-19 11:01:53 -0400 |
|---|---|---|
| committer | Grant Likely <grant.likely@linaro.org> | 2014-03-19 11:01:53 -0400 |
| commit | ca3992bc0c12e381deb84cd47ec1181a6d34660d (patch) | |
| tree | 162006e862b9d856fdce1b19f0ba117dafb6ca58 | |
| parent | 0829f6d1f69e4f2fae4062987ae6531a9af1a2e3 (diff) | |
| parent | 2040b52768ebab6e7bd73af0dc63703269c62f17 (diff) | |
Merge branch 'devicetree/next-reserved-mem' into devicetree/next
| -rw-r--r-- | Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt | 133 | ||||
| -rw-r--r-- | arch/arm/Kconfig | 1 | ||||
| -rw-r--r-- | arch/arm/mm/init.c | 2 | ||||
| -rw-r--r-- | arch/arm64/Kconfig | 1 | ||||
| -rw-r--r-- | arch/arm64/mm/init.c | 1 | ||||
| -rw-r--r-- | arch/powerpc/Kconfig | 1 | ||||
| -rw-r--r-- | arch/powerpc/kernel/prom.c | 3 | ||||
| -rw-r--r-- | drivers/of/Kconfig | 6 | ||||
| -rw-r--r-- | drivers/of/Makefile | 1 | ||||
| -rw-r--r-- | drivers/of/fdt.c | 143 | ||||
| -rw-r--r-- | drivers/of/of_reserved_mem.c | 217 | ||||
| -rw-r--r-- | include/asm-generic/vmlinux.lds.h | 11 | ||||
| -rw-r--r-- | include/linux/of_fdt.h | 4 | ||||
| -rw-r--r-- | include/linux/of_reserved_mem.h | 53 |
14 files changed, 577 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt b/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt new file mode 100644 index 000000000000..3da0ebdba8d9 --- /dev/null +++ b/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt | |||
| @@ -0,0 +1,133 @@ | |||
| 1 | *** Reserved memory regions *** | ||
| 2 | |||
| 3 | Reserved memory is specified as a node under the /reserved-memory node. | ||
| 4 | The operating system shall exclude reserved memory from normal usage | ||
| 5 | one can create child nodes describing particular reserved (excluded from | ||
| 6 | normal use) memory regions. Such memory regions are usually designed for | ||
| 7 | the special usage by various device drivers. | ||
| 8 | |||
| 9 | Parameters for each memory region can be encoded into the device tree | ||
| 10 | with the following nodes: | ||
| 11 | |||
| 12 | /reserved-memory node | ||
| 13 | --------------------- | ||
| 14 | #address-cells, #size-cells (required) - standard definition | ||
| 15 | - Should use the same values as the root node | ||
| 16 | ranges (required) - standard definition | ||
| 17 | - Should be empty | ||
| 18 | |||
| 19 | /reserved-memory/ child nodes | ||
| 20 | ----------------------------- | ||
| 21 | Each child of the reserved-memory node specifies one or more regions of | ||
| 22 | reserved memory. Each child node may either use a 'reg' property to | ||
| 23 | specify a specific range of reserved memory, or a 'size' property with | ||
| 24 | optional constraints to request a dynamically allocated block of memory. | ||
| 25 | |||
| 26 | Following the generic-names recommended practice, node names should | ||
| 27 | reflect the purpose of the node (ie. "framebuffer" or "dma-pool"). Unit | ||
| 28 | address (@<address>) should be appended to the name if the node is a | ||
| 29 | static allocation. | ||
| 30 | |||
| 31 | Properties: | ||
| 32 | Requires either a) or b) below. | ||
| 33 | a) static allocation | ||
| 34 | reg (required) - standard definition | ||
| 35 | b) dynamic allocation | ||
| 36 | size (required) - length based on parent's #size-cells | ||
| 37 | - Size in bytes of memory to reserve. | ||
| 38 | alignment (optional) - length based on parent's #size-cells | ||
| 39 | - Address boundary for alignment of allocation. | ||
| 40 | alloc-ranges (optional) - prop-encoded-array (address, length pairs). | ||
| 41 | - Specifies regions of memory that are | ||
| 42 | acceptable to allocate from. | ||
| 43 | |||
| 44 | If both reg and size are present, then the reg property takes precedence | ||
| 45 | and size is ignored. | ||
| 46 | |||
| 47 | Additional properties: | ||
| 48 | compatible (optional) - standard definition | ||
| 49 | - may contain the following strings: | ||
| 50 | - shared-dma-pool: This indicates a region of memory meant to be | ||
| 51 | used as a shared pool of DMA buffers for a set of devices. It can | ||
| 52 | be used by an operating system to instanciate the necessary pool | ||
| 53 | management subsystem if necessary. | ||
| 54 | - vendor specific string in the form <vendor>,[<device>-]<usage> | ||
| 55 | no-map (optional) - empty property | ||
| 56 | - Indicates the operating system must not create a virtual mapping | ||
| 57 | of the region as part of its standard mapping of system memory, | ||
| 58 | nor permit speculative access to it under any circumstances other | ||
| 59 | than under the control of the device driver using the region. | ||
| 60 | reusable (optional) - empty property | ||
| 61 | - The operating system can use the memory in this region with the | ||
| 62 | limitation that the device driver(s) owning the region need to be | ||
| 63 | able to reclaim it back. Typically that means that the operating | ||
| 64 | system can use that region to store volatile or cached data that | ||
| 65 | can be otherwise regenerated or migrated elsewhere. | ||
| 66 | |||
| 67 | Linux implementation note: | ||
| 68 | - If a "linux,cma-default" property is present, then Linux will use the | ||
| 69 | region for the default pool of the contiguous memory allocator. | ||
| 70 | |||
| 71 | Device node references to reserved memory | ||
| 72 | ----------------------------------------- | ||
| 73 | Regions in the /reserved-memory node may be referenced by other device | ||
| 74 | nodes by adding a memory-region property to the device node. | ||
| 75 | |||
| 76 | memory-region (optional) - phandle, specifier pairs to children of /reserved-memory | ||
| 77 | |||
| 78 | Example | ||
| 79 | ------- | ||
| 80 | This example defines 3 contiguous regions are defined for Linux kernel: | ||
| 81 | one default of all device drivers (named linux,cma@72000000 and 64MiB in size), | ||
| 82 | one dedicated to the framebuffer device (named framebuffer@78000000, 8MiB), and | ||
| 83 | one for multimedia processing (named multimedia-memory@77000000, 64MiB). | ||
| 84 | |||
| 85 | / { | ||
| 86 | #address-cells = <1>; | ||
| 87 | #size-cells = <1>; | ||
| 88 | |||
| 89 | memory { | ||
| 90 | reg = <0x40000000 0x40000000>; | ||
| 91 | }; | ||
| 92 | |||
| 93 | reserved-memory { | ||
| 94 | #address-cells = <1>; | ||
| 95 | #size-cells = <1>; | ||
| 96 | ranges; | ||
| 97 | |||
| 98 | /* global autoconfigured region for contiguous allocations */ | ||
| 99 | linux,cma { | ||
| 100 | compatible = "shared-dma-pool"; | ||
| 101 | reusable; | ||
| 102 | size = <0x4000000>; | ||
| 103 | alignment = <0x2000>; | ||
| 104 | linux,cma-default; | ||
| 105 | }; | ||
| 106 | |||
| 107 | display_reserved: framebuffer@78000000 { | ||
| 108 | reg = <0x78000000 0x800000>; | ||
| 109 | }; | ||
| 110 | |||
| 111 | multimedia_reserved: multimedia@77000000 { | ||
| 112 | compatible = "acme,multimedia-memory"; | ||
| 113 | reg = <0x77000000 0x4000000>; | ||
| 114 | }; | ||
| 115 | }; | ||
| 116 | |||
| 117 | /* ... */ | ||
| 118 | |||
| 119 | fb0: video@12300000 { | ||
| 120 | memory-region = <&display_reserved>; | ||
| 121 | /* ... */ | ||
| 122 | }; | ||
| 123 | |||
| 124 | scaler: scaler@12500000 { | ||
| 125 | memory-region = <&multimedia_reserved>; | ||
| 126 | /* ... */ | ||
| 127 | }; | ||
| 128 | |||
| 129 | codec: codec@12600000 { | ||
| 130 | memory-region = <&multimedia_reserved>; | ||
| 131 | /* ... */ | ||
| 132 | }; | ||
| 133 | }; | ||
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index e25419817791..d0262bea8020 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig | |||
| @@ -1918,6 +1918,7 @@ config USE_OF | |||
| 1918 | select IRQ_DOMAIN | 1918 | select IRQ_DOMAIN |
| 1919 | select OF | 1919 | select OF |
| 1920 | select OF_EARLY_FLATTREE | 1920 | select OF_EARLY_FLATTREE |
| 1921 | select OF_RESERVED_MEM | ||
| 1921 | help | 1922 | help |
| 1922 | Include support for flattened device tree machine descriptions. | 1923 | Include support for flattened device tree machine descriptions. |
| 1923 | 1924 | ||
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 804d61566a53..2a77ba8796ae 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c | |||
| @@ -323,6 +323,8 @@ void __init arm_memblock_init(struct meminfo *mi, | |||
| 323 | if (mdesc->reserve) | 323 | if (mdesc->reserve) |
| 324 | mdesc->reserve(); | 324 | mdesc->reserve(); |
| 325 | 325 | ||
| 326 | early_init_fdt_scan_reserved_mem(); | ||
| 327 | |||
| 326 | /* | 328 | /* |
| 327 | * reserve memory for DMA contigouos allocations, | 329 | * reserve memory for DMA contigouos allocations, |
| 328 | * must come from DMA area inside low memory | 330 | * must come from DMA area inside low memory |
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 27bbcfc7202a..6abf15407dca 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig | |||
| @@ -43,6 +43,7 @@ config ARM64 | |||
| 43 | select NO_BOOTMEM | 43 | select NO_BOOTMEM |
| 44 | select OF | 44 | select OF |
| 45 | select OF_EARLY_FLATTREE | 45 | select OF_EARLY_FLATTREE |
| 46 | select OF_RESERVED_MEM | ||
| 46 | select PERF_USE_VMALLOC | 47 | select PERF_USE_VMALLOC |
| 47 | select POWER_RESET | 48 | select POWER_RESET |
| 48 | select POWER_SUPPLY | 49 | select POWER_SUPPLY |
diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index d0b4c2efda90..3fb8d50dfdaa 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c | |||
| @@ -160,6 +160,7 @@ void __init arm64_memblock_init(void) | |||
| 160 | memblock_reserve(base, size); | 160 | memblock_reserve(base, size); |
| 161 | } | 161 | } |
| 162 | 162 | ||
| 163 | early_init_fdt_scan_reserved_mem(); | ||
| 163 | dma_contiguous_reserve(0); | 164 | dma_contiguous_reserve(0); |
| 164 | 165 | ||
| 165 | memblock_allow_resize(); | 166 | memblock_allow_resize(); |
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 957bf344c0f5..3b6617fed8fc 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig | |||
| @@ -90,6 +90,7 @@ config PPC | |||
| 90 | select BINFMT_ELF | 90 | select BINFMT_ELF |
| 91 | select OF | 91 | select OF |
| 92 | select OF_EARLY_FLATTREE | 92 | select OF_EARLY_FLATTREE |
| 93 | select OF_RESERVED_MEM | ||
| 93 | select HAVE_FTRACE_MCOUNT_RECORD | 94 | select HAVE_FTRACE_MCOUNT_RECORD |
| 94 | select HAVE_DYNAMIC_FTRACE | 95 | select HAVE_DYNAMIC_FTRACE |
| 95 | select HAVE_FUNCTION_TRACER | 96 | select HAVE_FUNCTION_TRACER |
diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index f58c0d3aaeb4..591986215801 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c | |||
| @@ -33,6 +33,7 @@ | |||
| 33 | #include <linux/irq.h> | 33 | #include <linux/irq.h> |
| 34 | #include <linux/memblock.h> | 34 | #include <linux/memblock.h> |
| 35 | #include <linux/of.h> | 35 | #include <linux/of.h> |
| 36 | #include <linux/of_fdt.h> | ||
| 36 | 37 | ||
| 37 | #include <asm/prom.h> | 38 | #include <asm/prom.h> |
| 38 | #include <asm/rtas.h> | 39 | #include <asm/rtas.h> |
| @@ -588,6 +589,8 @@ static void __init early_reserve_mem_dt(void) | |||
| 588 | memblock_reserve(base, size); | 589 | memblock_reserve(base, size); |
| 589 | } | 590 | } |
| 590 | } | 591 | } |
| 592 | |||
| 593 | early_init_fdt_scan_reserved_mem(); | ||
| 591 | } | 594 | } |
| 592 | 595 | ||
| 593 | static void __init early_reserve_mem(void) | 596 | static void __init early_reserve_mem(void) |
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index a46ac1b00032..889005fa4d04 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig | |||
| @@ -71,4 +71,10 @@ config OF_MTD | |||
| 71 | depends on MTD | 71 | depends on MTD |
| 72 | def_bool y | 72 | def_bool y |
| 73 | 73 | ||
| 74 | config OF_RESERVED_MEM | ||
| 75 | depends on OF_EARLY_FLATTREE | ||
| 76 | bool | ||
| 77 | help | ||
| 78 | Helpers to allow for reservation of memory regions | ||
| 79 | |||
| 74 | endmenu # OF | 80 | endmenu # OF |
diff --git a/drivers/of/Makefile b/drivers/of/Makefile index efd05102c405..ed9660adad77 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile | |||
| @@ -9,3 +9,4 @@ obj-$(CONFIG_OF_MDIO) += of_mdio.o | |||
| 9 | obj-$(CONFIG_OF_PCI) += of_pci.o | 9 | obj-$(CONFIG_OF_PCI) += of_pci.o |
| 10 | obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o | 10 | obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o |
| 11 | obj-$(CONFIG_OF_MTD) += of_mtd.o | 11 | obj-$(CONFIG_OF_MTD) += of_mtd.o |
| 12 | obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o | ||
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 70ccc36513e7..fa16a912a927 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c | |||
| @@ -15,6 +15,8 @@ | |||
| 15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
| 16 | #include <linux/of.h> | 16 | #include <linux/of.h> |
| 17 | #include <linux/of_fdt.h> | 17 | #include <linux/of_fdt.h> |
| 18 | #include <linux/of_reserved_mem.h> | ||
| 19 | #include <linux/sizes.h> | ||
| 18 | #include <linux/string.h> | 20 | #include <linux/string.h> |
| 19 | #include <linux/errno.h> | 21 | #include <linux/errno.h> |
| 20 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
| @@ -440,6 +442,129 @@ struct boot_param_header *initial_boot_params; | |||
| 440 | #ifdef CONFIG_OF_EARLY_FLATTREE | 442 | #ifdef CONFIG_OF_EARLY_FLATTREE |
| 441 | 443 | ||
| 442 | /** | 444 | /** |
| 445 | * res_mem_reserve_reg() - reserve all memory described in 'reg' property | ||
| 446 | */ | ||
| 447 | static int __init __reserved_mem_reserve_reg(unsigned long node, | ||
| 448 | const char *uname) | ||
| 449 | { | ||
| 450 | int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); | ||
| 451 | phys_addr_t base, size; | ||
| 452 | unsigned long len; | ||
| 453 | __be32 *prop; | ||
| 454 | int nomap, first = 1; | ||
| 455 | |||
| 456 | prop = of_get_flat_dt_prop(node, "reg", &len); | ||
| 457 | if (!prop) | ||
| 458 | return -ENOENT; | ||
| 459 | |||
| 460 | if (len && len % t_len != 0) { | ||
| 461 | pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n", | ||
| 462 | uname); | ||
| 463 | return -EINVAL; | ||
| 464 | } | ||
| 465 | |||
| 466 | nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; | ||
| 467 | |||
| 468 | while (len >= t_len) { | ||
| 469 | base = dt_mem_next_cell(dt_root_addr_cells, &prop); | ||
| 470 | size = dt_mem_next_cell(dt_root_size_cells, &prop); | ||
| 471 | |||
| 472 | if (base && size && | ||
| 473 | early_init_dt_reserve_memory_arch(base, size, nomap) == 0) | ||
| 474 | pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %ld MiB\n", | ||
| 475 | uname, &base, (unsigned long)size / SZ_1M); | ||
| 476 | else | ||
| 477 | pr_info("Reserved memory: failed to reserve memory for node '%s': base %pa, size %ld MiB\n", | ||
| 478 | uname, &base, (unsigned long)size / SZ_1M); | ||
| 479 | |||
| 480 | len -= t_len; | ||
| 481 | if (first) { | ||
| 482 | fdt_reserved_mem_save_node(node, uname, base, size); | ||
| 483 | first = 0; | ||
| 484 | } | ||
| 485 | } | ||
| 486 | return 0; | ||
| 487 | } | ||
| 488 | |||
| 489 | /** | ||
| 490 | * __reserved_mem_check_root() - check if #size-cells, #address-cells provided | ||
| 491 | * in /reserved-memory matches the values supported by the current implementation, | ||
| 492 | * also check if ranges property has been provided | ||
| 493 | */ | ||
| 494 | static int __reserved_mem_check_root(unsigned long node) | ||
| 495 | { | ||
| 496 | __be32 *prop; | ||
| 497 | |||
| 498 | prop = of_get_flat_dt_prop(node, "#size-cells", NULL); | ||
| 499 | if (!prop || be32_to_cpup(prop) != dt_root_size_cells) | ||
| 500 | return -EINVAL; | ||
| 501 | |||
| 502 | prop = of_get_flat_dt_prop(node, "#address-cells", NULL); | ||
| 503 | if (!prop || be32_to_cpup(prop) != dt_root_addr_cells) | ||
| 504 | return -EINVAL; | ||
| 505 | |||
| 506 | prop = of_get_flat_dt_prop(node, "ranges", NULL); | ||
| 507 | if (!prop) | ||
| 508 | return -EINVAL; | ||
| 509 | return 0; | ||
| 510 | } | ||
| 511 | |||
| 512 | /** | ||
| 513 | * fdt_scan_reserved_mem() - scan a single FDT node for reserved memory | ||
| 514 | */ | ||
| 515 | static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname, | ||
| 516 | int depth, void *data) | ||
| 517 | { | ||
| 518 | static int found; | ||
| 519 | const char *status; | ||
| 520 | int err; | ||
| 521 | |||
| 522 | if (!found && depth == 1 && strcmp(uname, "reserved-memory") == 0) { | ||
| 523 | if (__reserved_mem_check_root(node) != 0) { | ||
| 524 | pr_err("Reserved memory: unsupported node format, ignoring\n"); | ||
| 525 | /* break scan */ | ||
| 526 | return 1; | ||
| 527 | } | ||
| 528 | found = 1; | ||
| 529 | /* scan next node */ | ||
| 530 | return 0; | ||
| 531 | } else if (!found) { | ||
| 532 | /* scan next node */ | ||
| 533 | return 0; | ||
| 534 | } else if (found && depth < 2) { | ||
| 535 | /* scanning of /reserved-memory has been finished */ | ||
| 536 | return 1; | ||
| 537 | } | ||
| 538 | |||
| 539 | status = of_get_flat_dt_prop(node, "status", NULL); | ||
| 540 | if (status && strcmp(status, "okay") != 0 && strcmp(status, "ok") != 0) | ||
| 541 | return 0; | ||
| 542 | |||
| 543 | err = __reserved_mem_reserve_reg(node, uname); | ||
| 544 | if (err == -ENOENT && of_get_flat_dt_prop(node, "size", NULL)) | ||
| 545 | fdt_reserved_mem_save_node(node, uname, 0, 0); | ||
| 546 | |||
| 547 | /* scan next node */ | ||
| 548 | return 0; | ||
| 549 | } | ||
| 550 | |||
| 551 | /** | ||
| 552 | * early_init_fdt_scan_reserved_mem() - create reserved memory regions | ||
| 553 | * | ||
| 554 | * This function grabs memory from early allocator for device exclusive use | ||
| 555 | * defined in device tree structures. It should be called by arch specific code | ||
| 556 | * once the early allocator (i.e. memblock) has been fully activated. | ||
| 557 | */ | ||
| 558 | void __init early_init_fdt_scan_reserved_mem(void) | ||
| 559 | { | ||
| 560 | if (!initial_boot_params) | ||
| 561 | return; | ||
| 562 | |||
| 563 | of_scan_flat_dt(__fdt_scan_reserved_mem, NULL); | ||
| 564 | fdt_init_reserved_mem(); | ||
| 565 | } | ||
| 566 | |||
| 567 | /** | ||
| 443 | * of_scan_flat_dt - scan flattened tree blob and call callback on each. | 568 | * of_scan_flat_dt - scan flattened tree blob and call callback on each. |
| 444 | * @it: callback function | 569 | * @it: callback function |
| 445 | * @data: context data pointer | 570 | * @data: context data pointer |
| @@ -856,6 +981,16 @@ void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size) | |||
| 856 | memblock_add(base, size); | 981 | memblock_add(base, size); |
| 857 | } | 982 | } |
| 858 | 983 | ||
| 984 | int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base, | ||
| 985 | phys_addr_t size, bool nomap) | ||
| 986 | { | ||
| 987 | if (memblock_is_region_reserved(base, size)) | ||
| 988 | return -EBUSY; | ||
| 989 | if (nomap) | ||
| 990 | return memblock_remove(base, size); | ||
| 991 | return memblock_reserve(base, size); | ||
| 992 | } | ||
| 993 | |||
| 859 | /* | 994 | /* |
| 860 | * called from unflatten_device_tree() to bootstrap devicetree itself | 995 | * called from unflatten_device_tree() to bootstrap devicetree itself |
| 861 | * Architectures can override this definition if memblock isn't used | 996 | * Architectures can override this definition if memblock isn't used |
| @@ -864,6 +999,14 @@ void * __init __weak early_init_dt_alloc_memory_arch(u64 size, u64 align) | |||
| 864 | { | 999 | { |
| 865 | return __va(memblock_alloc(size, align)); | 1000 | return __va(memblock_alloc(size, align)); |
| 866 | } | 1001 | } |
| 1002 | #else | ||
| 1003 | int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base, | ||
| 1004 | phys_addr_t size, bool nomap) | ||
| 1005 | { | ||
| 1006 | pr_err("Reserved memory not supported, ignoring range 0x%llx - 0x%llx%s\n", | ||
| 1007 | base, size, nomap ? " (nomap)" : ""); | ||
| 1008 | return -ENOSYS; | ||
| 1009 | } | ||
| 867 | #endif | 1010 | #endif |
| 868 | 1011 | ||
| 869 | bool __init early_init_dt_scan(void *params) | 1012 | bool __init early_init_dt_scan(void *params) |
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c new file mode 100644 index 000000000000..daaaf935911d --- /dev/null +++ b/drivers/of/of_reserved_mem.c | |||
| @@ -0,0 +1,217 @@ | |||
| 1 | /* | ||
| 2 | * Device tree based initialization code for reserved memory. | ||
| 3 | * | ||
| 4 | * Copyright (c) 2013, The Linux Foundation. All Rights Reserved. | ||
| 5 | * Copyright (c) 2013,2014 Samsung Electronics Co., Ltd. | ||
| 6 | * http://www.samsung.com | ||
| 7 | * Author: Marek Szyprowski <m.szyprowski@samsung.com> | ||
| 8 | * Author: Josh Cartwright <joshc@codeaurora.org> | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or | ||
| 11 | * modify it under the terms of the GNU General Public License as | ||
| 12 | * published by the Free Software Foundation; either version 2 of the | ||
| 13 | * License or (at your optional) any later version of the license. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/err.h> | ||
| 17 | #include <linux/of.h> | ||
| 18 | #include <linux/of_fdt.h> | ||
| 19 | #include <linux/of_platform.h> | ||
| 20 | #include <linux/mm.h> | ||
| 21 | #include <linux/sizes.h> | ||
| 22 | #include <linux/of_reserved_mem.h> | ||
| 23 | |||
| 24 | #define MAX_RESERVED_REGIONS 16 | ||
| 25 | static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS]; | ||
| 26 | static int reserved_mem_count; | ||
| 27 | |||
| 28 | #if defined(CONFIG_HAVE_MEMBLOCK) | ||
| 29 | #include <linux/memblock.h> | ||
| 30 | int __init __weak early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, | ||
| 31 | phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap, | ||
| 32 | phys_addr_t *res_base) | ||
| 33 | { | ||
| 34 | /* | ||
| 35 | * We use __memblock_alloc_base() because memblock_alloc_base() | ||
| 36 | * panic()s on allocation failure. | ||
| 37 | */ | ||
| 38 | phys_addr_t base = __memblock_alloc_base(size, align, end); | ||
| 39 | if (!base) | ||
| 40 | return -ENOMEM; | ||
| 41 | |||
| 42 | /* | ||
| 43 | * Check if the allocated region fits in to start..end window | ||
| 44 | */ | ||
| 45 | if (base < start) { | ||
| 46 | memblock_free(base, size); | ||
| 47 | return -ENOMEM; | ||
| 48 | } | ||
| 49 | |||
| 50 | *res_base = base; | ||
| 51 | if (nomap) | ||
| 52 | return memblock_remove(base, size); | ||
| 53 | return 0; | ||
| 54 | } | ||
| 55 | #else | ||
| 56 | int __init __weak early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, | ||
| 57 | phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap, | ||
| 58 | phys_addr_t *res_base) | ||
| 59 | { | ||
| 60 | pr_err("Reserved memory not supported, ignoring region 0x%llx%s\n", | ||
| 61 | size, nomap ? " (nomap)" : ""); | ||
| 62 | return -ENOSYS; | ||
| 63 | } | ||
| 64 | #endif | ||
| 65 | |||
| 66 | /** | ||
| 67 | * res_mem_save_node() - save fdt node for second pass initialization | ||
| 68 | */ | ||
| 69 | void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname, | ||
| 70 | phys_addr_t base, phys_addr_t size) | ||
| 71 | { | ||
| 72 | struct reserved_mem *rmem = &reserved_mem[reserved_mem_count]; | ||
| 73 | |||
| 74 | if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) { | ||
| 75 | pr_err("Reserved memory: not enough space all defined regions.\n"); | ||
| 76 | return; | ||
| 77 | } | ||
| 78 | |||
| 79 | rmem->fdt_node = node; | ||
| 80 | rmem->name = uname; | ||
| 81 | rmem->base = base; | ||
| 82 | rmem->size = size; | ||
| 83 | |||
| 84 | reserved_mem_count++; | ||
| 85 | return; | ||
| 86 | } | ||
| 87 | |||
| 88 | /** | ||
| 89 | * res_mem_alloc_size() - allocate reserved memory described by 'size', 'align' | ||
| 90 | * and 'alloc-ranges' properties | ||
| 91 | */ | ||
| 92 | static int __init __reserved_mem_alloc_size(unsigned long node, | ||
| 93 | const char *uname, phys_addr_t *res_base, phys_addr_t *res_size) | ||
| 94 | { | ||
| 95 | int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); | ||
| 96 | phys_addr_t start = 0, end = 0; | ||
| 97 | phys_addr_t base = 0, align = 0, size; | ||
| 98 | unsigned long len; | ||
| 99 | __be32 *prop; | ||
| 100 | int nomap; | ||
| 101 | int ret; | ||
| 102 | |||
| 103 | prop = of_get_flat_dt_prop(node, "size", &len); | ||
| 104 | if (!prop) | ||
| 105 | return -EINVAL; | ||
| 106 | |||
| 107 | if (len != dt_root_size_cells * sizeof(__be32)) { | ||
| 108 | pr_err("Reserved memory: invalid size property in '%s' node.\n", | ||
| 109 | uname); | ||
| 110 | return -EINVAL; | ||
| 111 | } | ||
| 112 | size = dt_mem_next_cell(dt_root_size_cells, &prop); | ||
| 113 | |||
| 114 | nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; | ||
| 115 | |||
| 116 | prop = of_get_flat_dt_prop(node, "alignment", &len); | ||
| 117 | if (prop) { | ||
| 118 | if (len != dt_root_addr_cells * sizeof(__be32)) { | ||
| 119 | pr_err("Reserved memory: invalid alignment property in '%s' node.\n", | ||
| 120 | uname); | ||
| 121 | return -EINVAL; | ||
| 122 | } | ||
| 123 | align = dt_mem_next_cell(dt_root_addr_cells, &prop); | ||
| 124 | } | ||
| 125 | |||
| 126 | prop = of_get_flat_dt_prop(node, "alloc-ranges", &len); | ||
| 127 | if (prop) { | ||
| 128 | |||
| 129 | if (len % t_len != 0) { | ||
| 130 | pr_err("Reserved memory: invalid alloc-ranges property in '%s', skipping node.\n", | ||
| 131 | uname); | ||
| 132 | return -EINVAL; | ||
| 133 | } | ||
| 134 | |||
| 135 | base = 0; | ||
| 136 | |||
| 137 | while (len > 0) { | ||
| 138 | start = dt_mem_next_cell(dt_root_addr_cells, &prop); | ||
| 139 | end = start + dt_mem_next_cell(dt_root_size_cells, | ||
| 140 | &prop); | ||
| 141 | |||
| 142 | ret = early_init_dt_alloc_reserved_memory_arch(size, | ||
| 143 | align, start, end, nomap, &base); | ||
| 144 | if (ret == 0) { | ||
| 145 | pr_debug("Reserved memory: allocated memory for '%s' node: base %pa, size %ld MiB\n", | ||
| 146 | uname, &base, | ||
| 147 | (unsigned long)size / SZ_1M); | ||
| 148 | break; | ||
| 149 | } | ||
| 150 | len -= t_len; | ||
| 151 | } | ||
| 152 | |||
| 153 | } else { | ||
| 154 | ret = early_init_dt_alloc_reserved_memory_arch(size, align, | ||
| 155 | 0, 0, nomap, &base); | ||
| 156 | if (ret == 0) | ||
| 157 | pr_debug("Reserved memory: allocated memory for '%s' node: base %pa, size %ld MiB\n", | ||
| 158 | uname, &base, (unsigned long)size / SZ_1M); | ||
| 159 | } | ||
| 160 | |||
| 161 | if (base == 0) { | ||
| 162 | pr_info("Reserved memory: failed to allocate memory for node '%s'\n", | ||
| 163 | uname); | ||
| 164 | return -ENOMEM; | ||
| 165 | } | ||
| 166 | |||
| 167 | *res_base = base; | ||
| 168 | *res_size = size; | ||
| 169 | |||
| 170 | return 0; | ||
| 171 | } | ||
| 172 | |||
| 173 | static const struct of_device_id __rmem_of_table_sentinel | ||
| 174 | __used __section(__reservedmem_of_table_end); | ||
| 175 | |||
| 176 | /** | ||
| 177 | * res_mem_init_node() - call region specific reserved memory init code | ||
| 178 | */ | ||
| 179 | static int __init __reserved_mem_init_node(struct reserved_mem *rmem) | ||
| 180 | { | ||
| 181 | extern const struct of_device_id __reservedmem_of_table[]; | ||
| 182 | const struct of_device_id *i; | ||
| 183 | |||
| 184 | for (i = __reservedmem_of_table; i < &__rmem_of_table_sentinel; i++) { | ||
| 185 | reservedmem_of_init_fn initfn = i->data; | ||
| 186 | const char *compat = i->compatible; | ||
| 187 | |||
| 188 | if (!of_flat_dt_is_compatible(rmem->fdt_node, compat)) | ||
| 189 | continue; | ||
| 190 | |||
| 191 | if (initfn(rmem, rmem->fdt_node, rmem->name) == 0) { | ||
| 192 | pr_info("Reserved memory: initialized node %s, compatible id %s\n", | ||
| 193 | rmem->name, compat); | ||
| 194 | return 0; | ||
| 195 | } | ||
| 196 | } | ||
| 197 | return -ENOENT; | ||
| 198 | } | ||
| 199 | |||
| 200 | /** | ||
| 201 | * fdt_init_reserved_mem - allocate and init all saved reserved memory regions | ||
| 202 | */ | ||
| 203 | void __init fdt_init_reserved_mem(void) | ||
| 204 | { | ||
| 205 | int i; | ||
| 206 | for (i = 0; i < reserved_mem_count; i++) { | ||
| 207 | struct reserved_mem *rmem = &reserved_mem[i]; | ||
| 208 | unsigned long node = rmem->fdt_node; | ||
| 209 | int err = 0; | ||
| 210 | |||
| 211 | if (rmem->size == 0) | ||
| 212 | err = __reserved_mem_alloc_size(node, rmem->name, | ||
| 213 | &rmem->base, &rmem->size); | ||
| 214 | if (err == 0) | ||
| 215 | __reserved_mem_init_node(rmem); | ||
| 216 | } | ||
| 217 | } | ||
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index bc2121fa9132..f10f64fcc815 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h | |||
| @@ -167,6 +167,16 @@ | |||
| 167 | #define CLK_OF_TABLES() | 167 | #define CLK_OF_TABLES() |
| 168 | #endif | 168 | #endif |
| 169 | 169 | ||
| 170 | #ifdef CONFIG_OF_RESERVED_MEM | ||
| 171 | #define RESERVEDMEM_OF_TABLES() \ | ||
| 172 | . = ALIGN(8); \ | ||
| 173 | VMLINUX_SYMBOL(__reservedmem_of_table) = .; \ | ||
| 174 | *(__reservedmem_of_table) \ | ||
| 175 | *(__reservedmem_of_table_end) | ||
| 176 | #else | ||
| 177 | #define RESERVEDMEM_OF_TABLES() | ||
| 178 | #endif | ||
| 179 | |||
| 170 | #define KERNEL_DTB() \ | 180 | #define KERNEL_DTB() \ |
| 171 | STRUCT_ALIGN(); \ | 181 | STRUCT_ALIGN(); \ |
| 172 | VMLINUX_SYMBOL(__dtb_start) = .; \ | 182 | VMLINUX_SYMBOL(__dtb_start) = .; \ |
| @@ -490,6 +500,7 @@ | |||
| 490 | TRACE_SYSCALLS() \ | 500 | TRACE_SYSCALLS() \ |
| 491 | MEM_DISCARD(init.rodata) \ | 501 | MEM_DISCARD(init.rodata) \ |
| 492 | CLK_OF_TABLES() \ | 502 | CLK_OF_TABLES() \ |
| 503 | RESERVEDMEM_OF_TABLES() \ | ||
| 493 | CLKSRC_OF_TABLES() \ | 504 | CLKSRC_OF_TABLES() \ |
| 494 | KERNEL_DTB() \ | 505 | KERNEL_DTB() \ |
| 495 | IRQCHIP_OF_MATCH_TABLE() | 506 | IRQCHIP_OF_MATCH_TABLE() |
diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index 2b77058a7335..ddd7219af8ac 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h | |||
| @@ -98,7 +98,10 @@ extern int early_init_dt_scan_chosen(unsigned long node, const char *uname, | |||
| 98 | int depth, void *data); | 98 | int depth, void *data); |
| 99 | extern int early_init_dt_scan_memory(unsigned long node, const char *uname, | 99 | extern int early_init_dt_scan_memory(unsigned long node, const char *uname, |
| 100 | int depth, void *data); | 100 | int depth, void *data); |
| 101 | extern void early_init_fdt_scan_reserved_mem(void); | ||
| 101 | extern void early_init_dt_add_memory_arch(u64 base, u64 size); | 102 | extern void early_init_dt_add_memory_arch(u64 base, u64 size); |
| 103 | extern int early_init_dt_reserve_memory_arch(phys_addr_t base, phys_addr_t size, | ||
| 104 | bool no_map); | ||
| 102 | extern void * early_init_dt_alloc_memory_arch(u64 size, u64 align); | 105 | extern void * early_init_dt_alloc_memory_arch(u64 size, u64 align); |
| 103 | extern u64 dt_mem_next_cell(int s, __be32 **cellp); | 106 | extern u64 dt_mem_next_cell(int s, __be32 **cellp); |
| 104 | 107 | ||
| @@ -118,6 +121,7 @@ extern void unflatten_and_copy_device_tree(void); | |||
| 118 | extern void early_init_devtree(void *); | 121 | extern void early_init_devtree(void *); |
| 119 | extern void early_get_first_memblock_info(void *, phys_addr_t *); | 122 | extern void early_get_first_memblock_info(void *, phys_addr_t *); |
| 120 | #else /* CONFIG_OF_FLATTREE */ | 123 | #else /* CONFIG_OF_FLATTREE */ |
| 124 | static inline void early_init_fdt_scan_reserved_mem(void) {} | ||
| 121 | static inline const char *of_flat_dt_get_machine_name(void) { return NULL; } | 125 | static inline const char *of_flat_dt_get_machine_name(void) { return NULL; } |
| 122 | static inline void unflatten_device_tree(void) {} | 126 | static inline void unflatten_device_tree(void) {} |
| 123 | static inline void unflatten_and_copy_device_tree(void) {} | 127 | static inline void unflatten_and_copy_device_tree(void) {} |
diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h new file mode 100644 index 000000000000..9b1fbb7f29fc --- /dev/null +++ b/include/linux/of_reserved_mem.h | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | #ifndef __OF_RESERVED_MEM_H | ||
| 2 | #define __OF_RESERVED_MEM_H | ||
| 3 | |||
| 4 | struct device; | ||
| 5 | struct of_phandle_args; | ||
| 6 | struct reserved_mem_ops; | ||
| 7 | |||
| 8 | struct reserved_mem { | ||
| 9 | const char *name; | ||
| 10 | unsigned long fdt_node; | ||
| 11 | const struct reserved_mem_ops *ops; | ||
| 12 | phys_addr_t base; | ||
| 13 | phys_addr_t size; | ||
| 14 | void *priv; | ||
| 15 | }; | ||
| 16 | |||
| 17 | struct reserved_mem_ops { | ||
| 18 | void (*device_init)(struct reserved_mem *rmem, | ||
| 19 | struct device *dev); | ||
| 20 | void (*device_release)(struct reserved_mem *rmem, | ||
| 21 | struct device *dev); | ||
| 22 | }; | ||
| 23 | |||
| 24 | typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem, | ||
| 25 | unsigned long node, const char *uname); | ||
| 26 | |||
| 27 | #ifdef CONFIG_OF_RESERVED_MEM | ||
| 28 | void fdt_init_reserved_mem(void); | ||
| 29 | void fdt_reserved_mem_save_node(unsigned long node, const char *uname, | ||
| 30 | phys_addr_t base, phys_addr_t size); | ||
| 31 | |||
| 32 | #define RESERVEDMEM_OF_DECLARE(name, compat, init) \ | ||
| 33 | static const struct of_device_id __reservedmem_of_table_##name \ | ||
| 34 | __used __section(__reservedmem_of_table) \ | ||
| 35 | = { .compatible = compat, \ | ||
| 36 | .data = (init == (reservedmem_of_init_fn)NULL) ? \ | ||
| 37 | init : init } | ||
| 38 | |||
| 39 | #else | ||
| 40 | static inline void fdt_init_reserved_mem(void) { } | ||
| 41 | static inline void fdt_reserved_mem_save_node(unsigned long node, | ||
| 42 | const char *uname, phys_addr_t base, phys_addr_t size) { } | ||
| 43 | |||
| 44 | #define RESERVEDMEM_OF_DECLARE(name, compat, init) \ | ||
| 45 | static const struct of_device_id __reservedmem_of_table_##name \ | ||
| 46 | __attribute__((unused)) \ | ||
| 47 | = { .compatible = compat, \ | ||
| 48 | .data = (init == (reservedmem_of_init_fn)NULL) ? \ | ||
| 49 | init : init } | ||
| 50 | |||
| 51 | #endif | ||
| 52 | |||
| 53 | #endif /* __OF_RESERVED_MEM_H */ | ||
