Commit | Line | Data |
---|---|---|
bc581770 LW |
1 | /* |
2 | * Copyright (C) 2008-2009 ST-Ericsson AB | |
3 | * License terms: GNU General Public License (GPL) version 2 | |
4 | * TCM memory handling for ARM systems | |
5 | * | |
6 | * Author: Linus Walleij <linus.walleij@stericsson.com> | |
7 | * Author: Rickard Andersson <rickard.andersson@stericsson.com> | |
8 | */ | |
9 | #include <linux/init.h> | |
10 | #include <linux/kernel.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/stddef.h> | |
13 | #include <linux/ioport.h> | |
14 | #include <linux/genalloc.h> | |
15 | #include <linux/string.h> /* memcpy */ | |
bc581770 LW |
16 | #include <asm/cputype.h> |
17 | #include <asm/mach/map.h> | |
f4117ac9 | 18 | #include <asm/memory.h> |
bc581770 LW |
19 | #include "tcm.h" |
20 | ||
bc581770 LW |
21 | static struct gen_pool *tcm_pool; |
22 | ||
23 | /* TCM section definitions from the linker */ | |
24 | extern char __itcm_start, __sitcm_text, __eitcm_text; | |
25 | extern char __dtcm_start, __sdtcm_data, __edtcm_data; | |
26 | ||
1dbd30e9 LW |
27 | /* These will be increased as we run */ |
28 | u32 dtcm_end = DTCM_OFFSET; | |
29 | u32 itcm_end = ITCM_OFFSET; | |
30 | ||
bc581770 LW |
31 | /* |
32 | * TCM memory resources | |
33 | */ | |
34 | static struct resource dtcm_res = { | |
35 | .name = "DTCM RAM", | |
36 | .start = DTCM_OFFSET, | |
1dbd30e9 | 37 | .end = DTCM_OFFSET, |
bc581770 LW |
38 | .flags = IORESOURCE_MEM |
39 | }; | |
40 | ||
41 | static struct resource itcm_res = { | |
42 | .name = "ITCM RAM", | |
43 | .start = ITCM_OFFSET, | |
1dbd30e9 | 44 | .end = ITCM_OFFSET, |
bc581770 LW |
45 | .flags = IORESOURCE_MEM |
46 | }; | |
47 | ||
48 | static struct map_desc dtcm_iomap[] __initdata = { | |
49 | { | |
50 | .virtual = DTCM_OFFSET, | |
51 | .pfn = __phys_to_pfn(DTCM_OFFSET), | |
1dbd30e9 | 52 | .length = 0, |
cb9d7707 | 53 | .type = MT_MEMORY_DTCM |
bc581770 LW |
54 | } |
55 | }; | |
56 | ||
57 | static struct map_desc itcm_iomap[] __initdata = { | |
58 | { | |
59 | .virtual = ITCM_OFFSET, | |
60 | .pfn = __phys_to_pfn(ITCM_OFFSET), | |
1dbd30e9 | 61 | .length = 0, |
cb9d7707 | 62 | .type = MT_MEMORY_ITCM |
bc581770 LW |
63 | } |
64 | }; | |
65 | ||
66 | /* | |
67 | * Allocate a chunk of TCM memory | |
68 | */ | |
69 | void *tcm_alloc(size_t len) | |
70 | { | |
71 | unsigned long vaddr; | |
72 | ||
73 | if (!tcm_pool) | |
74 | return NULL; | |
75 | ||
76 | vaddr = gen_pool_alloc(tcm_pool, len); | |
77 | if (!vaddr) | |
78 | return NULL; | |
79 | ||
80 | return (void *) vaddr; | |
81 | } | |
82 | EXPORT_SYMBOL(tcm_alloc); | |
83 | ||
84 | /* | |
85 | * Free a chunk of TCM memory | |
86 | */ | |
87 | void tcm_free(void *addr, size_t len) | |
88 | { | |
89 | gen_pool_free(tcm_pool, (unsigned long) addr, len); | |
90 | } | |
91 | EXPORT_SYMBOL(tcm_free); | |
92 | ||
1dbd30e9 LW |
93 | static int __init setup_tcm_bank(u8 type, u8 bank, u8 banks, |
94 | u32 *offset) | |
bc581770 LW |
95 | { |
96 | const int tcm_sizes[16] = { 0, -1, -1, 4, 8, 16, 32, 64, 128, | |
97 | 256, 512, 1024, -1, -1, -1, -1 }; | |
98 | u32 tcm_region; | |
99 | int tcm_size; | |
100 | ||
59850977 LW |
101 | /* |
102 | * If there are more than one TCM bank of this type, | |
103 | * select the TCM bank to operate on in the TCM selection | |
104 | * register. | |
105 | */ | |
106 | if (banks > 1) | |
107 | asm("mcr p15, 0, %0, c9, c2, 0" | |
108 | : /* No output operands */ | |
109 | : "r" (bank)); | |
110 | ||
bc581770 LW |
111 | /* Read the special TCM region register c9, 0 */ |
112 | if (!type) | |
113 | asm("mrc p15, 0, %0, c9, c1, 0" | |
114 | : "=r" (tcm_region)); | |
115 | else | |
116 | asm("mrc p15, 0, %0, c9, c1, 1" | |
117 | : "=r" (tcm_region)); | |
118 | ||
119 | tcm_size = tcm_sizes[(tcm_region >> 2) & 0x0f]; | |
120 | if (tcm_size < 0) { | |
1dbd30e9 | 121 | pr_err("CPU: %sTCM%d of unknown size\n", |
59850977 | 122 | type ? "I" : "D", bank); |
1dbd30e9 LW |
123 | return -EINVAL; |
124 | } else if (tcm_size > 32) { | |
125 | pr_err("CPU: %sTCM%d larger than 32k found\n", | |
126 | type ? "I" : "D", bank); | |
127 | return -EINVAL; | |
bc581770 | 128 | } else { |
59850977 | 129 | pr_info("CPU: found %sTCM%d %dk @ %08x, %senabled\n", |
bc581770 | 130 | type ? "I" : "D", |
59850977 | 131 | bank, |
bc581770 LW |
132 | tcm_size, |
133 | (tcm_region & 0xfffff000U), | |
134 | (tcm_region & 1) ? "" : "not "); | |
135 | } | |
136 | ||
bc581770 | 137 | /* Force move the TCM bank to where we want it, enable */ |
1dbd30e9 | 138 | tcm_region = *offset | (tcm_region & 0x00000ffeU) | 1; |
bc581770 LW |
139 | |
140 | if (!type) | |
141 | asm("mcr p15, 0, %0, c9, c1, 0" | |
142 | : /* No output operands */ | |
143 | : "r" (tcm_region)); | |
144 | else | |
145 | asm("mcr p15, 0, %0, c9, c1, 1" | |
146 | : /* No output operands */ | |
147 | : "r" (tcm_region)); | |
148 | ||
1dbd30e9 LW |
149 | /* Increase offset */ |
150 | *offset += (tcm_size << 10); | |
151 | ||
59850977 LW |
152 | pr_info("CPU: moved %sTCM%d %dk to %08x, enabled\n", |
153 | type ? "I" : "D", | |
154 | bank, | |
155 | tcm_size, | |
156 | (tcm_region & 0xfffff000U)); | |
1dbd30e9 | 157 | return 0; |
bc581770 LW |
158 | } |
159 | ||
160 | /* | |
161 | * This initializes the TCM memory | |
162 | */ | |
163 | void __init tcm_init(void) | |
164 | { | |
165 | u32 tcm_status = read_cpuid_tcmstatus(); | |
59850977 | 166 | u8 dtcm_banks = (tcm_status >> 16) & 0x03; |
59850977 | 167 | u8 itcm_banks = (tcm_status & 0x03); |
bc581770 LW |
168 | char *start; |
169 | char *end; | |
170 | char *ram; | |
1dbd30e9 | 171 | int ret; |
59850977 | 172 | int i; |
bc581770 LW |
173 | |
174 | /* Setup DTCM if present */ | |
1dbd30e9 LW |
175 | if (dtcm_banks > 0) { |
176 | for (i = 0; i < dtcm_banks; i++) { | |
177 | ret = setup_tcm_bank(0, i, dtcm_banks, &dtcm_end); | |
178 | if (ret) | |
179 | return; | |
180 | } | |
181 | dtcm_res.end = dtcm_end - 1; | |
bc581770 | 182 | request_resource(&iomem_resource, &dtcm_res); |
1dbd30e9 | 183 | dtcm_iomap[0].length = dtcm_end - DTCM_OFFSET; |
bc581770 LW |
184 | iotable_init(dtcm_iomap, 1); |
185 | /* Copy data from RAM to DTCM */ | |
186 | start = &__sdtcm_data; | |
187 | end = &__edtcm_data; | |
188 | ram = &__dtcm_start; | |
1dbd30e9 LW |
189 | /* This means you compiled more code than fits into DTCM */ |
190 | BUG_ON((end - start) > (dtcm_end - DTCM_OFFSET)); | |
bc581770 LW |
191 | memcpy(start, ram, (end-start)); |
192 | pr_debug("CPU DTCM: copied data from %p - %p\n", start, end); | |
193 | } | |
194 | ||
195 | /* Setup ITCM if present */ | |
1dbd30e9 LW |
196 | if (itcm_banks > 0) { |
197 | for (i = 0; i < itcm_banks; i++) { | |
198 | ret = setup_tcm_bank(1, i, itcm_banks, &itcm_end); | |
199 | if (ret) | |
200 | return; | |
201 | } | |
202 | itcm_res.end = itcm_end - 1; | |
bc581770 | 203 | request_resource(&iomem_resource, &itcm_res); |
1dbd30e9 | 204 | itcm_iomap[0].length = itcm_end - ITCM_OFFSET; |
bc581770 LW |
205 | iotable_init(itcm_iomap, 1); |
206 | /* Copy code from RAM to ITCM */ | |
207 | start = &__sitcm_text; | |
208 | end = &__eitcm_text; | |
209 | ram = &__itcm_start; | |
1dbd30e9 LW |
210 | /* This means you compiled more code than fits into ITCM */ |
211 | BUG_ON((end - start) > (itcm_end - ITCM_OFFSET)); | |
bc581770 LW |
212 | memcpy(start, ram, (end-start)); |
213 | pr_debug("CPU ITCM: copied code from %p - %p\n", start, end); | |
214 | } | |
215 | } | |
216 | ||
217 | /* | |
218 | * This creates the TCM memory pool and has to be done later, | |
219 | * during the core_initicalls, since the allocator is not yet | |
220 | * up and running when the first initialization runs. | |
221 | */ | |
222 | static int __init setup_tcm_pool(void) | |
223 | { | |
224 | u32 tcm_status = read_cpuid_tcmstatus(); | |
225 | u32 dtcm_pool_start = (u32) &__edtcm_data; | |
226 | u32 itcm_pool_start = (u32) &__eitcm_text; | |
227 | int ret; | |
228 | ||
229 | /* | |
230 | * Set up malloc pool, 2^2 = 4 bytes granularity since | |
231 | * the TCM is sometimes just 4 KiB. NB: pages and cache | |
232 | * line alignments does not matter in TCM! | |
233 | */ | |
234 | tcm_pool = gen_pool_create(2, -1); | |
235 | ||
236 | pr_debug("Setting up TCM memory pool\n"); | |
237 | ||
238 | /* Add the rest of DTCM to the TCM pool */ | |
59850977 | 239 | if (tcm_status & (0x03 << 16)) { |
1dbd30e9 | 240 | if (dtcm_pool_start < dtcm_end) { |
bc581770 | 241 | ret = gen_pool_add(tcm_pool, dtcm_pool_start, |
1dbd30e9 | 242 | dtcm_end - dtcm_pool_start, -1); |
bc581770 LW |
243 | if (ret) { |
244 | pr_err("CPU DTCM: could not add DTCM " \ | |
245 | "remainder to pool!\n"); | |
246 | return ret; | |
247 | } | |
248 | pr_debug("CPU DTCM: Added %08x bytes @ %08x to " \ | |
249 | "the TCM memory pool\n", | |
1dbd30e9 | 250 | dtcm_end - dtcm_pool_start, |
bc581770 LW |
251 | dtcm_pool_start); |
252 | } | |
253 | } | |
254 | ||
255 | /* Add the rest of ITCM to the TCM pool */ | |
59850977 | 256 | if (tcm_status & 0x03) { |
1dbd30e9 | 257 | if (itcm_pool_start < itcm_end) { |
bc581770 | 258 | ret = gen_pool_add(tcm_pool, itcm_pool_start, |
1dbd30e9 | 259 | itcm_end - itcm_pool_start, -1); |
bc581770 LW |
260 | if (ret) { |
261 | pr_err("CPU ITCM: could not add ITCM " \ | |
262 | "remainder to pool!\n"); | |
263 | return ret; | |
264 | } | |
265 | pr_debug("CPU ITCM: Added %08x bytes @ %08x to " \ | |
266 | "the TCM memory pool\n", | |
1dbd30e9 | 267 | itcm_end - itcm_pool_start, |
bc581770 LW |
268 | itcm_pool_start); |
269 | } | |
270 | } | |
271 | return 0; | |
272 | } | |
273 | ||
274 | core_initcall(setup_tcm_pool); |