diff options
| -rw-r--r-- | arch/xtensa/include/asm/sysmem.h | 5 | ||||
| -rw-r--r-- | arch/xtensa/mm/init.c | 103 |
2 files changed, 103 insertions, 5 deletions
diff --git a/arch/xtensa/include/asm/sysmem.h b/arch/xtensa/include/asm/sysmem.h index fe7ad750a158..c015c5c8e3f7 100644 --- a/arch/xtensa/include/asm/sysmem.h +++ b/arch/xtensa/include/asm/sysmem.h | |||
| @@ -18,6 +18,11 @@ struct meminfo { | |||
| 18 | unsigned long end; | 18 | unsigned long end; |
| 19 | }; | 19 | }; |
| 20 | 20 | ||
| 21 | /* | ||
| 22 | * Bank array is sorted by .start. | ||
| 23 | * Banks don't overlap and there's at least one page gap | ||
| 24 | * between adjacent bank entries. | ||
| 25 | */ | ||
| 21 | struct sysmem_info { | 26 | struct sysmem_info { |
| 22 | int nr_banks; | 27 | int nr_banks; |
| 23 | struct meminfo bank[SYSMEM_BANKS_MAX]; | 28 | struct meminfo bank[SYSMEM_BANKS_MAX]; |
diff --git a/arch/xtensa/mm/init.c b/arch/xtensa/mm/init.c index 4f78264e8bd8..79c0c3d52ae3 100644 --- a/arch/xtensa/mm/init.c +++ b/arch/xtensa/mm/init.c | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * for more details. | 8 | * for more details. |
| 9 | * | 9 | * |
| 10 | * Copyright (C) 2001 - 2005 Tensilica Inc. | 10 | * Copyright (C) 2001 - 2005 Tensilica Inc. |
| 11 | * Copyright (C) 2014 Cadence Design Systems Inc. | ||
| 11 | * | 12 | * |
| 12 | * Chris Zankel <chris@zankel.net> | 13 | * Chris Zankel <chris@zankel.net> |
| 13 | * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com> | 14 | * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com> |
| @@ -31,17 +32,109 @@ | |||
| 31 | 32 | ||
| 32 | struct sysmem_info sysmem __initdata; | 33 | struct sysmem_info sysmem __initdata; |
| 33 | 34 | ||
| 35 | /* | ||
| 36 | * Find bank with maximal .start such that bank.start <= start | ||
| 37 | */ | ||
| 38 | static inline struct meminfo * __init find_bank(unsigned long start) | ||
| 39 | { | ||
| 40 | unsigned i; | ||
| 41 | struct meminfo *it = NULL; | ||
| 42 | |||
| 43 | for (i = 0; i < sysmem.nr_banks; ++i) | ||
| 44 | if (sysmem.bank[i].start <= start) | ||
| 45 | it = sysmem.bank + i; | ||
| 46 | else | ||
| 47 | break; | ||
| 48 | return it; | ||
| 49 | } | ||
| 50 | |||
| 51 | /* | ||
| 52 | * Move all memory banks starting at 'from' to a new place at 'to', | ||
| 53 | * adjust nr_banks accordingly. | ||
| 54 | * Both 'from' and 'to' must be inside the sysmem.bank. | ||
| 55 | * | ||
| 56 | * Returns: 0 (success), -ENOMEM (not enough space in the sysmem.bank). | ||
| 57 | */ | ||
| 58 | static int __init move_banks(struct meminfo *to, struct meminfo *from) | ||
| 59 | { | ||
| 60 | unsigned n = sysmem.nr_banks - (from - sysmem.bank); | ||
| 61 | |||
| 62 | if (to > from && to - from + sysmem.nr_banks > SYSMEM_BANKS_MAX) | ||
| 63 | return -ENOMEM; | ||
| 64 | if (to != from) | ||
| 65 | memmove(to, from, n * sizeof(struct meminfo)); | ||
| 66 | sysmem.nr_banks += to - from; | ||
| 67 | return 0; | ||
| 68 | } | ||
| 69 | |||
| 70 | /* | ||
| 71 | * Add new bank to sysmem. Resulting sysmem is the union of bytes of the | ||
| 72 | * original sysmem and the new bank. | ||
| 73 | * | ||
| 74 | * Returns: 0 (success), < 0 (error) | ||
| 75 | */ | ||
| 34 | int __init add_sysmem_bank(unsigned long start, unsigned long end) | 76 | int __init add_sysmem_bank(unsigned long start, unsigned long end) |
| 35 | { | 77 | { |
| 36 | if (sysmem.nr_banks >= SYSMEM_BANKS_MAX) { | 78 | unsigned i; |
| 37 | pr_warn("Ignoring memory bank 0x%08lx size %ldKB\n", | 79 | struct meminfo *it = NULL; |
| 80 | unsigned long sz; | ||
| 81 | unsigned long bank_sz = 0; | ||
| 82 | |||
| 83 | if (start == end || | ||
| 84 | (start < end) != (PAGE_ALIGN(start) < (end & PAGE_MASK))) { | ||
| 85 | pr_warn("Ignoring small memory bank 0x%08lx size: %ld bytes\n", | ||
| 38 | start, end - start); | 86 | start, end - start); |
| 39 | return -EINVAL; | 87 | return -EINVAL; |
| 40 | } | 88 | } |
| 41 | sysmem.bank[sysmem.nr_banks].start = PAGE_ALIGN(start); | ||
| 42 | sysmem.bank[sysmem.nr_banks].end = end & PAGE_MASK; | ||
| 43 | sysmem.nr_banks++; | ||
| 44 | 89 | ||
| 90 | start = PAGE_ALIGN(start); | ||
| 91 | end &= PAGE_MASK; | ||
| 92 | sz = end - start; | ||
| 93 | |||
| 94 | it = find_bank(start); | ||
| 95 | |||
| 96 | if (it) | ||
| 97 | bank_sz = it->end - it->start; | ||
| 98 | |||
| 99 | if (it && bank_sz >= start - it->start) { | ||
| 100 | if (end - it->start > bank_sz) | ||
| 101 | it->end = end; | ||
| 102 | else | ||
| 103 | return 0; | ||
| 104 | } else { | ||
| 105 | if (!it) | ||
| 106 | it = sysmem.bank; | ||
| 107 | else | ||
| 108 | ++it; | ||
| 109 | |||
| 110 | if (it - sysmem.bank < sysmem.nr_banks && | ||
| 111 | it->start - start <= sz) { | ||
| 112 | it->start = start; | ||
| 113 | if (it->end - it->start < sz) | ||
| 114 | it->end = end; | ||
| 115 | else | ||
| 116 | return 0; | ||
| 117 | } else { | ||
| 118 | if (move_banks(it + 1, it) < 0) { | ||
| 119 | pr_warn("Ignoring memory bank 0x%08lx size %ld bytes\n", | ||
| 120 | start, end - start); | ||
| 121 | return -EINVAL; | ||
| 122 | } | ||
| 123 | it->start = start; | ||
| 124 | it->end = end; | ||
| 125 | return 0; | ||
| 126 | } | ||
| 127 | } | ||
| 128 | sz = it->end - it->start; | ||
| 129 | for (i = it + 1 - sysmem.bank; i < sysmem.nr_banks; ++i) | ||
| 130 | if (sysmem.bank[i].start - it->start <= sz) { | ||
| 131 | if (sz < sysmem.bank[i].end - it->start) | ||
| 132 | it->end = sysmem.bank[i].end; | ||
| 133 | } else { | ||
| 134 | break; | ||
| 135 | } | ||
| 136 | |||
| 137 | move_banks(it + 1, sysmem.bank + i); | ||
| 45 | return 0; | 138 | return 0; |
| 46 | } | 139 | } |
| 47 | 140 | ||
