diff options
-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 */ | ||