aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/kernel/tcm.c
diff options
context:
space:
mode:
authorLinus Walleij <linus.walleij@stericsson.com>2009-09-15 12:30:37 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2009-09-15 17:11:05 -0400
commitbc581770cfdd8c17ea17d324dc05e2f9c599e7ca (patch)
treec6d2c2eb82d8a6c6851de0ecc7d8c0e63e026266 /arch/arm/kernel/tcm.c
parent18240904960a39e582ced8ba8ececb10b8c22dd3 (diff)
ARM: 5580/2: ARM TCM (Tightly-Coupled Memory) support v3
This adds the TCM interface to Linux, when active, it will detect and report TCM memories and sizes early in boot if present, introduce generic TCM memory handling, provide a generic TCM memory pool and select TCM memory for the U300 platform. See the Documentation/arm/tcm.txt for documentation. Signed-off-by: Linus Walleij <linus.walleij@stericsson.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/kernel/tcm.c')
-rw-r--r--arch/arm/kernel/tcm.c246
1 files changed, 246 insertions, 0 deletions
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
28static struct gen_pool *tcm_pool;
29
30/* TCM section definitions from the linker */
31extern char __itcm_start, __sitcm_text, __eitcm_text;
32extern char __dtcm_start, __sdtcm_data, __edtcm_data;
33
34/*
35 * TCM memory resources
36 */
37static struct resource dtcm_res = {
38 .name = "DTCM RAM",
39 .start = DTCM_OFFSET,
40 .end = DTCM_END,
41 .flags = IORESOURCE_MEM
42};
43
44static struct resource itcm_res = {
45 .name = "ITCM RAM",
46 .start = ITCM_OFFSET,
47 .end = ITCM_END,
48 .flags = IORESOURCE_MEM
49};
50
51static 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
60static 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 */
72void *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}
85EXPORT_SYMBOL(tcm_alloc);
86
87/*
88 * Free a chunk of TCM memory
89 */
90void tcm_free(void *addr, size_t len)
91{
92 gen_pool_free(tcm_pool, (unsigned long) addr, len);
93}
94EXPORT_SYMBOL(tcm_free);
95
96
97static 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 */
153void __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 */
194static 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
246core_initcall(setup_tcm_pool);