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
6 * Author: Linus Walleij <linus.walleij@stericsson.com>
7 * Author: Rickard Andersson <rickard.andersson@stericsson.com>
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 */
16 #include <asm/page.h> /* PAGE_SHIFT */
17 #include <asm/cputype.h>
18 #include <asm/mach/map.h>
19 #include <mach/memory.h>
22 /* Scream and warn about misuse */
23 #if !defined(ITCM_OFFSET) || !defined(ITCM_END) || \
24 !defined(DTCM_OFFSET) || !defined(DTCM_END)
25 #error "TCM support selected but offsets not defined!"
28 static struct gen_pool
*tcm_pool
;
30 /* TCM section definitions from the linker */
31 extern char __itcm_start
, __sitcm_text
, __eitcm_text
;
32 extern char __dtcm_start
, __sdtcm_data
, __edtcm_data
;
35 * TCM memory resources
37 static struct resource dtcm_res
= {
41 .flags
= IORESOURCE_MEM
44 static struct resource itcm_res
= {
48 .flags
= IORESOURCE_MEM
51 static struct map_desc dtcm_iomap
[] __initdata
= {
53 .virtual = DTCM_OFFSET
,
54 .pfn
= __phys_to_pfn(DTCM_OFFSET
),
55 .length
= (DTCM_END
- DTCM_OFFSET
+ 1),
60 static struct map_desc itcm_iomap
[] __initdata
= {
62 .virtual = ITCM_OFFSET
,
63 .pfn
= __phys_to_pfn(ITCM_OFFSET
),
64 .length
= (ITCM_END
- ITCM_OFFSET
+ 1),
70 * Allocate a chunk of TCM memory
72 void *tcm_alloc(size_t len
)
79 vaddr
= gen_pool_alloc(tcm_pool
, len
);
83 return (void *) vaddr
;
85 EXPORT_SYMBOL(tcm_alloc
);
88 * Free a chunk of TCM memory
90 void tcm_free(void *addr
, size_t len
)
92 gen_pool_free(tcm_pool
, (unsigned long) addr
, len
);
94 EXPORT_SYMBOL(tcm_free
);
97 static void __init
setup_tcm_bank(u8 type
, u32 offset
, u32 expected_size
)
99 const int tcm_sizes
[16] = { 0, -1, -1, 4, 8, 16, 32, 64, 128,
100 256, 512, 1024, -1, -1, -1, -1 };
104 /* Read the special TCM region register c9, 0 */
106 asm("mrc p15, 0, %0, c9, c1, 0"
107 : "=r" (tcm_region
));
109 asm("mrc p15, 0, %0, c9, c1, 1"
110 : "=r" (tcm_region
));
112 tcm_size
= tcm_sizes
[(tcm_region
>> 2) & 0x0f];
114 pr_err("CPU: %sTCM of unknown size!\n",
117 pr_info("CPU: found %sTCM %dk @ %08x, %senabled\n",
120 (tcm_region
& 0xfffff000U
),
121 (tcm_region
& 1) ? "" : "not ");
124 if (tcm_size
!= expected_size
) {
125 pr_crit("CPU: %sTCM was detected %dk but expected %dk!\n",
129 /* Adjust to the expected size? what can we do... */
132 /* Force move the TCM bank to where we want it, enable */
133 tcm_region
= offset
| (tcm_region
& 0x00000ffeU
) | 1;
136 asm("mcr p15, 0, %0, c9, c1, 0"
137 : /* No output operands */
140 asm("mcr p15, 0, %0, c9, c1, 1"
141 : /* No output operands */
144 pr_debug("CPU: moved %sTCM %dk to %08x, enabled\n",
147 (tcm_region
& 0xfffff000U
));
151 * This initializes the TCM memory
153 void __init
tcm_init(void)
155 u32 tcm_status
= read_cpuid_tcmstatus();
160 /* Setup DTCM if present */
161 if (tcm_status
& (1 << 16)) {
162 setup_tcm_bank(0, DTCM_OFFSET
,
163 (DTCM_END
- DTCM_OFFSET
+ 1) >> 10);
164 request_resource(&iomem_resource
, &dtcm_res
);
165 iotable_init(dtcm_iomap
, 1);
166 /* Copy data from RAM to DTCM */
167 start
= &__sdtcm_data
;
170 memcpy(start
, ram
, (end
-start
));
171 pr_debug("CPU DTCM: copied data from %p - %p\n", start
, end
);
174 /* Setup ITCM if present */
175 if (tcm_status
& 1) {
176 setup_tcm_bank(1, ITCM_OFFSET
,
177 (ITCM_END
- ITCM_OFFSET
+ 1) >> 10);
178 request_resource(&iomem_resource
, &itcm_res
);
179 iotable_init(itcm_iomap
, 1);
180 /* Copy code from RAM to ITCM */
181 start
= &__sitcm_text
;
184 memcpy(start
, ram
, (end
-start
));
185 pr_debug("CPU ITCM: copied code from %p - %p\n", start
, end
);
190 * This creates the TCM memory pool and has to be done later,
191 * during the core_initicalls, since the allocator is not yet
192 * up and running when the first initialization runs.
194 static int __init
setup_tcm_pool(void)
196 u32 tcm_status
= read_cpuid_tcmstatus();
197 u32 dtcm_pool_start
= (u32
) &__edtcm_data
;
198 u32 itcm_pool_start
= (u32
) &__eitcm_text
;
202 * Set up malloc pool, 2^2 = 4 bytes granularity since
203 * the TCM is sometimes just 4 KiB. NB: pages and cache
204 * line alignments does not matter in TCM!
206 tcm_pool
= gen_pool_create(2, -1);
208 pr_debug("Setting up TCM memory pool\n");
210 /* Add the rest of DTCM to the TCM pool */
211 if (tcm_status
& (1 << 16)) {
212 if (dtcm_pool_start
< DTCM_END
) {
213 ret
= gen_pool_add(tcm_pool
, dtcm_pool_start
,
214 DTCM_END
- dtcm_pool_start
+ 1, -1);
216 pr_err("CPU DTCM: could not add DTCM " \
217 "remainder to pool!\n");
220 pr_debug("CPU DTCM: Added %08x bytes @ %08x to " \
221 "the TCM memory pool\n",
222 DTCM_END
- dtcm_pool_start
+ 1,
227 /* Add the rest of ITCM to the TCM pool */
228 if (tcm_status
& 1) {
229 if (itcm_pool_start
< ITCM_END
) {
230 ret
= gen_pool_add(tcm_pool
, itcm_pool_start
,
231 ITCM_END
- itcm_pool_start
+ 1, -1);
233 pr_err("CPU ITCM: could not add ITCM " \
234 "remainder to pool!\n");
237 pr_debug("CPU ITCM: Added %08x bytes @ %08x to " \
238 "the TCM memory pool\n",
239 ITCM_END
- itcm_pool_start
+ 1,
246 core_initcall(setup_tcm_pool
);