diff options
-rw-r--r-- | Documentation/arm/tcm.txt | 145 | ||||
-rw-r--r-- | arch/arm/Kconfig | 5 | ||||
-rw-r--r-- | arch/arm/include/asm/cputype.h | 5 | ||||
-rw-r--r-- | arch/arm/include/asm/tcm.h | 31 | ||||
-rw-r--r-- | arch/arm/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/kernel/setup.c | 2 | ||||
-rw-r--r-- | arch/arm/kernel/tcm.c | 246 | ||||
-rw-r--r-- | arch/arm/kernel/tcm.h | 17 | ||||
-rw-r--r-- | arch/arm/kernel/vmlinux.lds.S | 57 | ||||
-rw-r--r-- | arch/arm/mach-u300/include/mach/memory.h | 8 | ||||
-rw-r--r-- | arch/arm/mm/init.c | 8 |
11 files changed, 525 insertions, 0 deletions
diff --git a/Documentation/arm/tcm.txt b/Documentation/arm/tcm.txt new file mode 100644 index 000000000000..074f4be6667f --- /dev/null +++ b/Documentation/arm/tcm.txt | |||
@@ -0,0 +1,145 @@ | |||
1 | ARM TCM (Tightly-Coupled Memory) handling in Linux | ||
2 | ---- | ||
3 | Written by Linus Walleij <linus.walleij@stericsson.com> | ||
4 | |||
5 | Some ARM SoC:s have a so-called TCM (Tightly-Coupled Memory). | ||
6 | This is usually just a few (4-64) KiB of RAM inside the ARM | ||
7 | processor. | ||
8 | |||
9 | Due to being embedded inside the CPU The TCM has a | ||
10 | Harvard-architecture, so there is an ITCM (instruction TCM) | ||
11 | and a DTCM (data TCM). The DTCM can not contain any | ||
12 | instructions, but the ITCM can actually contain data. | ||
13 | The size of DTCM or ITCM is minimum 4KiB so the typical | ||
14 | minimum configuration is 4KiB ITCM and 4KiB DTCM. | ||
15 | |||
16 | ARM CPU:s have special registers to read out status, physical | ||
17 | location and size of TCM memories. arch/arm/include/asm/cputype.h | ||
18 | defines a CPUID_TCM register that you can read out from the | ||
19 | system control coprocessor. Documentation from ARM can be found | ||
20 | at http://infocenter.arm.com, search for "TCM Status Register" | ||
21 | to see documents for all CPUs. Reading this register you can | ||
22 | determine if ITCM (bit 0) and/or DTCM (bit 16) is present in the | ||
23 | machine. | ||
24 | |||
25 | There is further a TCM region register (search for "TCM Region | ||
26 | Registers" at the ARM site) that can report and modify the location | ||
27 | size of TCM memories at runtime. This is used to read out and modify | ||
28 | TCM location and size. Notice that this is not a MMU table: you | ||
29 | actually move the physical location of the TCM around. At the | ||
30 | place you put it, it will mask any underlying RAM from the | ||
31 | CPU so it is usually wise not to overlap any physical RAM with | ||
32 | the TCM. The TCM memory exists totally outside the MMU and will | ||
33 | override any MMU mappings. | ||
34 | |||
35 | Code executing inside the ITCM does not "see" any MMU mappings | ||
36 | and e.g. register accesses must be made to physical addresses. | ||
37 | |||
38 | TCM is used for a few things: | ||
39 | |||
40 | - FIQ and other interrupt handlers that need deterministic | ||
41 | timing and cannot wait for cache misses. | ||
42 | |||
43 | - Idle loops where all external RAM is set to self-refresh | ||
44 | retention mode, so only on-chip RAM is accessible by | ||
45 | the CPU and then we hang inside ITCM waiting for an | ||
46 | interrupt. | ||
47 | |||
48 | - Other operations which implies shutting off or reconfiguring | ||
49 | the external RAM controller. | ||
50 | |||
51 | There is an interface for using TCM on the ARM architecture | ||
52 | in <asm/tcm.h>. Using this interface it is possible to: | ||
53 | |||
54 | - Define the physical address and size of ITCM and DTCM. | ||
55 | |||
56 | - Tag functions to be compiled into ITCM. | ||
57 | |||
58 | - Tag data and constants to be allocated to DTCM and ITCM. | ||
59 | |||
60 | - Have the remaining TCM RAM added to a special | ||
61 | allocation pool with gen_pool_create() and gen_pool_add() | ||
62 | and provice tcm_alloc() and tcm_free() for this | ||
63 | memory. Such a heap is great for things like saving | ||
64 | device state when shutting off device power domains. | ||
65 | |||
66 | A machine that has TCM memory shall select HAVE_TCM in | ||
67 | arch/arm/Kconfig for itself, and then the | ||
68 | rest of the functionality will depend on the physical | ||
69 | location and size of ITCM and DTCM to be defined in | ||
70 | mach/memory.h for the machine. Code that needs to use | ||
71 | TCM shall #include <asm/tcm.h> If the TCM is not located | ||
72 | at the place given in memory.h it will be moved using | ||
73 | the TCM Region registers. | ||
74 | |||
75 | Functions to go into itcm can be tagged like this: | ||
76 | int __tcmfunc foo(int bar); | ||
77 | |||
78 | Variables to go into dtcm can be tagged like this: | ||
79 | int __tcmdata foo; | ||
80 | |||
81 | Constants can be tagged like this: | ||
82 | int __tcmconst foo; | ||
83 | |||
84 | To put assembler into TCM just use | ||
85 | .section ".tcm.text" or .section ".tcm.data" | ||
86 | respectively. | ||
87 | |||
88 | Example code: | ||
89 | |||
90 | #include <asm/tcm.h> | ||
91 | |||
92 | /* Uninitialized data */ | ||
93 | static u32 __tcmdata tcmvar; | ||
94 | /* Initialized data */ | ||
95 | static u32 __tcmdata tcmassigned = 0x2BADBABEU; | ||
96 | /* Constant */ | ||
97 | static const u32 __tcmconst tcmconst = 0xCAFEBABEU; | ||
98 | |||
99 | static void __tcmlocalfunc tcm_to_tcm(void) | ||
100 | { | ||
101 | int i; | ||
102 | for (i = 0; i < 100; i++) | ||
103 | tcmvar ++; | ||
104 | } | ||
105 | |||
106 | static void __tcmfunc hello_tcm(void) | ||
107 | { | ||
108 | /* Some abstract code that runs in ITCM */ | ||
109 | int i; | ||
110 | for (i = 0; i < 100; i++) { | ||
111 | tcmvar ++; | ||
112 | } | ||
113 | tcm_to_tcm(); | ||
114 | } | ||
115 | |||
116 | static void __init test_tcm(void) | ||
117 | { | ||
118 | u32 *tcmem; | ||
119 | int i; | ||
120 | |||
121 | hello_tcm(); | ||
122 | printk("Hello TCM executed from ITCM RAM\n"); | ||
123 | |||
124 | printk("TCM variable from testrun: %u @ %p\n", tcmvar, &tcmvar); | ||
125 | tcmvar = 0xDEADBEEFU; | ||
126 | printk("TCM variable: 0x%x @ %p\n", tcmvar, &tcmvar); | ||
127 | |||
128 | printk("TCM assigned variable: 0x%x @ %p\n", tcmassigned, &tcmassigned); | ||
129 | |||
130 | printk("TCM constant: 0x%x @ %p\n", tcmconst, &tcmconst); | ||
131 | |||
132 | /* Allocate some TCM memory from the pool */ | ||
133 | tcmem = tcm_alloc(20); | ||
134 | if (tcmem) { | ||
135 | printk("TCM Allocated 20 bytes of TCM @ %p\n", tcmem); | ||
136 | tcmem[0] = 0xDEADBEEFU; | ||
137 | tcmem[1] = 0x2BADBABEU; | ||
138 | tcmem[2] = 0xCAFEBABEU; | ||
139 | tcmem[3] = 0xDEADBEEFU; | ||
140 | tcmem[4] = 0x2BADBABEU; | ||
141 | for (i = 0; i < 5; i++) | ||
142 | printk("TCM tcmem[%d] = %08x\n", i, tcmem[i]); | ||
143 | tcm_free(tcmem, 20); | ||
144 | } | ||
145 | } | ||
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index d778a699f577..1c4119c60040 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig | |||
@@ -46,6 +46,10 @@ config GENERIC_CLOCKEVENTS_BROADCAST | |||
46 | depends on GENERIC_CLOCKEVENTS | 46 | depends on GENERIC_CLOCKEVENTS |
47 | default y if SMP && !LOCAL_TIMERS | 47 | default y if SMP && !LOCAL_TIMERS |
48 | 48 | ||
49 | config HAVE_TCM | ||
50 | bool | ||
51 | select GENERIC_ALLOCATOR | ||
52 | |||
49 | config NO_IOPORT | 53 | config NO_IOPORT |
50 | bool | 54 | bool |
51 | 55 | ||
@@ -649,6 +653,7 @@ config ARCH_U300 | |||
649 | bool "ST-Ericsson U300 Series" | 653 | bool "ST-Ericsson U300 Series" |
650 | depends on MMU | 654 | depends on MMU |
651 | select CPU_ARM926T | 655 | select CPU_ARM926T |
656 | select HAVE_TCM | ||
652 | select ARM_AMBA | 657 | select ARM_AMBA |
653 | select ARM_VIC | 658 | select ARM_VIC |
654 | select GENERIC_TIME | 659 | select GENERIC_TIME |
diff --git a/arch/arm/include/asm/cputype.h b/arch/arm/include/asm/cputype.h index b3e656c6fb78..54a1f1d76b14 100644 --- a/arch/arm/include/asm/cputype.h +++ b/arch/arm/include/asm/cputype.h | |||
@@ -63,6 +63,11 @@ static inline unsigned int __attribute_const__ read_cpuid_cachetype(void) | |||
63 | return read_cpuid(CPUID_CACHETYPE); | 63 | return read_cpuid(CPUID_CACHETYPE); |
64 | } | 64 | } |
65 | 65 | ||
66 | static inline unsigned int __attribute_const__ read_cpuid_tcmstatus(void) | ||
67 | { | ||
68 | return read_cpuid(CPUID_TCM); | ||
69 | } | ||
70 | |||
66 | /* | 71 | /* |
67 | * Intel's XScale3 core supports some v6 features (supersections, L2) | 72 | * Intel's XScale3 core supports some v6 features (supersections, L2) |
68 | * but advertises itself as v5 as it does not support the v6 ISA. For | 73 | * but advertises itself as v5 as it does not support the v6 ISA. For |
diff --git a/arch/arm/include/asm/tcm.h b/arch/arm/include/asm/tcm.h new file mode 100644 index 000000000000..5929ef5d927a --- /dev/null +++ b/arch/arm/include/asm/tcm.h | |||
@@ -0,0 +1,31 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Copyright (C) 2008-2009 ST-Ericsson AB | ||
4 | * License terms: GNU General Public License (GPL) version 2 | ||
5 | * | ||
6 | * Author: Rickard Andersson <rickard.andersson@stericsson.com> | ||
7 | * Author: Linus Walleij <linus.walleij@stericsson.com> | ||
8 | * | ||
9 | */ | ||
10 | #ifndef __ASMARM_TCM_H | ||
11 | #define __ASMARM_TCM_H | ||
12 | |||
13 | #ifndef CONFIG_HAVE_TCM | ||
14 | #error "You should not be including tcm.h unless you have a TCM!" | ||
15 | #endif | ||
16 | |||
17 | #include <linux/compiler.h> | ||
18 | |||
19 | /* Tag variables with this */ | ||
20 | #define __tcmdata __section(.tcm.data) | ||
21 | /* Tag constants with this */ | ||
22 | #define __tcmconst __section(.tcm.rodata) | ||
23 | /* Tag functions inside TCM called from outside TCM with this */ | ||
24 | #define __tcmfunc __attribute__((long_call)) __section(.tcm.text) noinline | ||
25 | /* Tag function inside TCM called from inside TCM with this */ | ||
26 | #define __tcmlocalfunc __section(.tcm.text) | ||
27 | |||
28 | void *tcm_alloc(size_t len); | ||
29 | void tcm_free(void *addr, size_t len); | ||
30 | |||
31 | #endif | ||
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 3213c9382b17..6808e01aedcd 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile | |||
@@ -34,6 +34,7 @@ obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o | |||
34 | obj-$(CONFIG_ARM_THUMBEE) += thumbee.o | 34 | obj-$(CONFIG_ARM_THUMBEE) += thumbee.o |
35 | obj-$(CONFIG_KGDB) += kgdb.o | 35 | obj-$(CONFIG_KGDB) += kgdb.o |
36 | obj-$(CONFIG_ARM_UNWIND) += unwind.o | 36 | obj-$(CONFIG_ARM_UNWIND) += unwind.o |
37 | obj-$(CONFIG_HAVE_TCM) += tcm.o | ||
37 | 38 | ||
38 | obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o | 39 | obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o |
39 | AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312 | 40 | AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312 |
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index d4d4f77c91b2..c6c57b640b6b 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c | |||
@@ -45,6 +45,7 @@ | |||
45 | 45 | ||
46 | #include "compat.h" | 46 | #include "compat.h" |
47 | #include "atags.h" | 47 | #include "atags.h" |
48 | #include "tcm.h" | ||
48 | 49 | ||
49 | #ifndef MEM_SIZE | 50 | #ifndef MEM_SIZE |
50 | #define MEM_SIZE (16*1024*1024) | 51 | #define MEM_SIZE (16*1024*1024) |
@@ -749,6 +750,7 @@ void __init setup_arch(char **cmdline_p) | |||
749 | #endif | 750 | #endif |
750 | 751 | ||
751 | cpu_init(); | 752 | cpu_init(); |
753 | tcm_init(); | ||
752 | 754 | ||
753 | /* | 755 | /* |
754 | * Set up various architecture-specific pointers | 756 | * Set up various architecture-specific pointers |
diff --git a/arch/arm/kernel/tcm.c b/arch/arm/kernel/tcm.c new file mode 100644 index 000000000000..e50303868f1b --- /dev/null +++ b/arch/arm/kernel/tcm.c | |||
@@ -0,0 +1,246 @@ | |||
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 */ | ||
16 | #include <asm/page.h> /* PAGE_SHIFT */ | ||
17 | #include <asm/cputype.h> | ||
18 | #include <asm/mach/map.h> | ||
19 | #include <mach/memory.h> | ||
20 | #include "tcm.h" | ||
21 | |||
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!" | ||
26 | #endif | ||
27 | |||
28 | static struct gen_pool *tcm_pool; | ||
29 | |||
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; | ||
33 | |||
34 | /* | ||
35 | * TCM memory resources | ||
36 | */ | ||
37 | static struct resource dtcm_res = { | ||
38 | .name = "DTCM RAM", | ||
39 | .start = DTCM_OFFSET, | ||
40 | .end = DTCM_END, | ||
41 | .flags = IORESOURCE_MEM | ||
42 | }; | ||
43 | |||
44 | static struct resource itcm_res = { | ||
45 | .name = "ITCM RAM", | ||
46 | .start = ITCM_OFFSET, | ||
47 | .end = ITCM_END, | ||
48 | .flags = IORESOURCE_MEM | ||
49 | }; | ||
50 | |||
51 | static struct map_desc dtcm_iomap[] __initdata = { | ||
52 | { | ||
53 | .virtual = DTCM_OFFSET, | ||
54 | .pfn = __phys_to_pfn(DTCM_OFFSET), | ||
55 | .length = (DTCM_END - DTCM_OFFSET + 1), | ||
56 | .type = MT_UNCACHED | ||
57 | } | ||
58 | }; | ||
59 | |||
60 | static struct map_desc itcm_iomap[] __initdata = { | ||
61 | { | ||
62 | .virtual = ITCM_OFFSET, | ||
63 | .pfn = __phys_to_pfn(ITCM_OFFSET), | ||
64 | .length = (ITCM_END - ITCM_OFFSET + 1), | ||
65 | .type = MT_UNCACHED | ||
66 | } | ||
67 | }; | ||
68 | |||
69 | /* | ||
70 | * Allocate a chunk of TCM memory | ||
71 | */ | ||
72 | void *tcm_alloc(size_t len) | ||
73 | { | ||
74 | unsigned long vaddr; | ||
75 | |||
76 | if (!tcm_pool) | ||
77 | return NULL; | ||
78 | |||
79 | vaddr = gen_pool_alloc(tcm_pool, len); | ||
80 | if (!vaddr) | ||
81 | return NULL; | ||
82 | |||
83 | return (void *) vaddr; | ||
84 | } | ||
85 | EXPORT_SYMBOL(tcm_alloc); | ||
86 | |||
87 | /* | ||
88 | * Free a chunk of TCM memory | ||
89 | */ | ||
90 | void tcm_free(void *addr, size_t len) | ||
91 | { | ||
92 | gen_pool_free(tcm_pool, (unsigned long) addr, len); | ||
93 | } | ||
94 | EXPORT_SYMBOL(tcm_free); | ||
95 | |||
96 | |||
97 | static void __init setup_tcm_bank(u8 type, u32 offset, u32 expected_size) | ||
98 | { | ||
99 | const int tcm_sizes[16] = { 0, -1, -1, 4, 8, 16, 32, 64, 128, | ||
100 | 256, 512, 1024, -1, -1, -1, -1 }; | ||
101 | u32 tcm_region; | ||
102 | int tcm_size; | ||
103 | |||
104 | /* Read the special TCM region register c9, 0 */ | ||
105 | if (!type) | ||
106 | asm("mrc p15, 0, %0, c9, c1, 0" | ||
107 | : "=r" (tcm_region)); | ||
108 | else | ||
109 | asm("mrc p15, 0, %0, c9, c1, 1" | ||
110 | : "=r" (tcm_region)); | ||
111 | |||
112 | tcm_size = tcm_sizes[(tcm_region >> 2) & 0x0f]; | ||
113 | if (tcm_size < 0) { | ||
114 | pr_err("CPU: %sTCM of unknown size!\n", | ||
115 | type ? "I" : "D"); | ||
116 | } else { | ||
117 | pr_info("CPU: found %sTCM %dk @ %08x, %senabled\n", | ||
118 | type ? "I" : "D", | ||
119 | tcm_size, | ||
120 | (tcm_region & 0xfffff000U), | ||
121 | (tcm_region & 1) ? "" : "not "); | ||
122 | } | ||
123 | |||
124 | if (tcm_size != expected_size) { | ||
125 | pr_crit("CPU: %sTCM was detected %dk but expected %dk!\n", | ||
126 | type ? "I" : "D", | ||
127 | tcm_size, | ||
128 | expected_size); | ||
129 | /* Adjust to the expected size? what can we do... */ | ||
130 | } | ||
131 | |||
132 | /* Force move the TCM bank to where we want it, enable */ | ||
133 | tcm_region = offset | (tcm_region & 0x00000ffeU) | 1; | ||
134 | |||
135 | if (!type) | ||
136 | asm("mcr p15, 0, %0, c9, c1, 0" | ||
137 | : /* No output operands */ | ||
138 | : "r" (tcm_region)); | ||
139 | else | ||
140 | asm("mcr p15, 0, %0, c9, c1, 1" | ||
141 | : /* No output operands */ | ||
142 | : "r" (tcm_region)); | ||
143 | |||
144 | pr_debug("CPU: moved %sTCM %dk to %08x, enabled\n", | ||
145 | type ? "I" : "D", | ||
146 | tcm_size, | ||
147 | (tcm_region & 0xfffff000U)); | ||
148 | } | ||
149 | |||
150 | /* | ||
151 | * This initializes the TCM memory | ||
152 | */ | ||
153 | void __init tcm_init(void) | ||
154 | { | ||
155 | u32 tcm_status = read_cpuid_tcmstatus(); | ||
156 | char *start; | ||
157 | char *end; | ||
158 | char *ram; | ||
159 | |||
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; | ||
168 | end = &__edtcm_data; | ||
169 | ram = &__dtcm_start; | ||
170 | memcpy(start, ram, (end-start)); | ||
171 | pr_debug("CPU DTCM: copied data from %p - %p\n", start, end); | ||
172 | } | ||
173 | |||
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; | ||
182 | end = &__eitcm_text; | ||
183 | ram = &__itcm_start; | ||
184 | memcpy(start, ram, (end-start)); | ||
185 | pr_debug("CPU ITCM: copied code from %p - %p\n", start, end); | ||
186 | } | ||
187 | } | ||
188 | |||
189 | /* | ||
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. | ||
193 | */ | ||
194 | static int __init setup_tcm_pool(void) | ||
195 | { | ||
196 | u32 tcm_status = read_cpuid_tcmstatus(); | ||
197 | u32 dtcm_pool_start = (u32) &__edtcm_data; | ||
198 | u32 itcm_pool_start = (u32) &__eitcm_text; | ||
199 | int ret; | ||
200 | |||
201 | /* | ||
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! | ||
205 | */ | ||
206 | tcm_pool = gen_pool_create(2, -1); | ||
207 | |||
208 | pr_debug("Setting up TCM memory pool\n"); | ||
209 | |||
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); | ||
215 | if (ret) { | ||
216 | pr_err("CPU DTCM: could not add DTCM " \ | ||
217 | "remainder to pool!\n"); | ||
218 | return ret; | ||
219 | } | ||
220 | pr_debug("CPU DTCM: Added %08x bytes @ %08x to " \ | ||
221 | "the TCM memory pool\n", | ||
222 | DTCM_END - dtcm_pool_start + 1, | ||
223 | dtcm_pool_start); | ||
224 | } | ||
225 | } | ||
226 | |||
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); | ||
232 | if (ret) { | ||
233 | pr_err("CPU ITCM: could not add ITCM " \ | ||
234 | "remainder to pool!\n"); | ||
235 | return ret; | ||
236 | } | ||
237 | pr_debug("CPU ITCM: Added %08x bytes @ %08x to " \ | ||
238 | "the TCM memory pool\n", | ||
239 | ITCM_END - itcm_pool_start + 1, | ||
240 | itcm_pool_start); | ||
241 | } | ||
242 | } | ||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | core_initcall(setup_tcm_pool); | ||
diff --git a/arch/arm/kernel/tcm.h b/arch/arm/kernel/tcm.h new file mode 100644 index 000000000000..8015ad434a40 --- /dev/null +++ b/arch/arm/kernel/tcm.h | |||
@@ -0,0 +1,17 @@ | |||
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 | |||
10 | #ifdef CONFIG_HAVE_TCM | ||
11 | void __init tcm_init(void); | ||
12 | #else | ||
13 | /* No TCM support, just blank inlines to be optimized out */ | ||
14 | inline void tcm_init(void) | ||
15 | { | ||
16 | } | ||
17 | #endif | ||
diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index 69371028a202..39d3ffb9ff2b 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S | |||
@@ -198,6 +198,63 @@ SECTIONS | |||
198 | } | 198 | } |
199 | _edata_loc = __data_loc + SIZEOF(.data); | 199 | _edata_loc = __data_loc + SIZEOF(.data); |
200 | 200 | ||
201 | #ifdef CONFIG_HAVE_TCM | ||
202 | /* | ||
203 | * We align everything to a page boundary so we can | ||
204 | * free it after init has commenced and TCM contents have | ||
205 | * been copied to its destination. | ||
206 | */ | ||
207 | .tcm_start : { | ||
208 | . = ALIGN(PAGE_SIZE); | ||
209 | __tcm_start = .; | ||
210 | __itcm_start = .; | ||
211 | } | ||
212 | |||
213 | /* | ||
214 | * Link these to the ITCM RAM | ||
215 | * Put VMA to the TCM address and LMA to the common RAM | ||
216 | * and we'll upload the contents from RAM to TCM and free | ||
217 | * the used RAM after that. | ||
218 | */ | ||
219 | .text_itcm ITCM_OFFSET : AT(__itcm_start) | ||
220 | { | ||
221 | __sitcm_text = .; | ||
222 | *(.tcm.text) | ||
223 | *(.tcm.rodata) | ||
224 | . = ALIGN(4); | ||
225 | __eitcm_text = .; | ||
226 | } | ||
227 | |||
228 | /* | ||
229 | * Reset the dot pointer, this is needed to create the | ||
230 | * relative __dtcm_start below (to be used as extern in code). | ||
231 | */ | ||
232 | . = ADDR(.tcm_start) + SIZEOF(.tcm_start) + SIZEOF(.text_itcm); | ||
233 | |||
234 | .dtcm_start : { | ||
235 | __dtcm_start = .; | ||
236 | } | ||
237 | |||
238 | /* TODO: add remainder of ITCM as well, that can be used for data! */ | ||
239 | .data_dtcm DTCM_OFFSET : AT(__dtcm_start) | ||
240 | { | ||
241 | . = ALIGN(4); | ||
242 | __sdtcm_data = .; | ||
243 | *(.tcm.data) | ||
244 | . = ALIGN(4); | ||
245 | __edtcm_data = .; | ||
246 | } | ||
247 | |||
248 | /* Reset the dot pointer or the linker gets confused */ | ||
249 | . = ADDR(.dtcm_start) + SIZEOF(.data_dtcm); | ||
250 | |||
251 | /* End marker for freeing TCM copy in linked object */ | ||
252 | .tcm_end : AT(ADDR(.dtcm_start) + SIZEOF(.data_dtcm)){ | ||
253 | . = ALIGN(PAGE_SIZE); | ||
254 | __tcm_end = .; | ||
255 | } | ||
256 | #endif | ||
257 | |||
201 | .bss : { | 258 | .bss : { |
202 | __bss_start = .; /* BSS */ | 259 | __bss_start = .; /* BSS */ |
203 | *(.bss) | 260 | *(.bss) |
diff --git a/arch/arm/mach-u300/include/mach/memory.h b/arch/arm/mach-u300/include/mach/memory.h index bf134bcc129d..ab000df7fc03 100644 --- a/arch/arm/mach-u300/include/mach/memory.h +++ b/arch/arm/mach-u300/include/mach/memory.h | |||
@@ -35,6 +35,14 @@ | |||
35 | #endif | 35 | #endif |
36 | 36 | ||
37 | /* | 37 | /* |
38 | * TCM memory whereabouts | ||
39 | */ | ||
40 | #define ITCM_OFFSET 0xffff2000 | ||
41 | #define ITCM_END 0xffff3fff | ||
42 | #define DTCM_OFFSET 0xffff4000 | ||
43 | #define DTCM_END 0xffff5fff | ||
44 | |||
45 | /* | ||
38 | * We enable a real big DMA buffer if need be. | 46 | * We enable a real big DMA buffer if need be. |
39 | */ | 47 | */ |
40 | #define CONSISTENT_DMA_SIZE SZ_4M | 48 | #define CONSISTENT_DMA_SIZE SZ_4M |
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index ea36186f32c3..764d5dc9af76 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c | |||
@@ -613,6 +613,14 @@ void __init mem_init(void) | |||
613 | 613 | ||
614 | void free_initmem(void) | 614 | void free_initmem(void) |
615 | { | 615 | { |
616 | #ifdef CONFIG_HAVE_TCM | ||
617 | extern char *__tcm_start, *__tcm_end; | ||
618 | |||
619 | totalram_pages += free_area(__phys_to_pfn(__pa(__tcm_start)), | ||
620 | __phys_to_pfn(__pa(__tcm_end)), | ||
621 | "TCM link"); | ||
622 | #endif | ||
623 | |||
616 | if (!machine_is_integrator() && !machine_is_cintegrator()) | 624 | if (!machine_is_integrator() && !machine_is_cintegrator()) |
617 | totalram_pages += free_area(__phys_to_pfn(__pa(__init_begin)), | 625 | totalram_pages += free_area(__phys_to_pfn(__pa(__init_begin)), |
618 | __phys_to_pfn(__pa(__init_end)), | 626 | __phys_to_pfn(__pa(__init_end)), |