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/cputype.h> 17 #include <asm/mach/map.h> 18 #include <asm/memory.h> 19 #include <asm/system_info.h> 20 #include "tcm.h" 21 22 static struct gen_pool *tcm_pool; 23 static bool dtcm_present; 24 static bool itcm_present; 25 26 /* TCM section definitions from the linker */ 27 extern char __itcm_start, __sitcm_text, __eitcm_text; 28 extern char __dtcm_start, __sdtcm_data, __edtcm_data; 29 30 /* These will be increased as we run */ 31 u32 dtcm_end = DTCM_OFFSET; 32 u32 itcm_end = ITCM_OFFSET; 33 34 /* 35 * TCM memory resources 36 */ 37 static struct resource dtcm_res = { 38 .name = "DTCM RAM", 39 .start = DTCM_OFFSET, 40 .end = DTCM_OFFSET, 41 .flags = IORESOURCE_MEM 42 }; 43 44 static struct resource itcm_res = { 45 .name = "ITCM RAM", 46 .start = ITCM_OFFSET, 47 .end = ITCM_OFFSET, 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 = 0, 56 .type = MT_MEMORY_DTCM 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 = 0, 65 .type = MT_MEMORY_ITCM 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 bool tcm_dtcm_present(void) 97 { 98 return dtcm_present; 99 } 100 EXPORT_SYMBOL(tcm_dtcm_present); 101 102 bool tcm_itcm_present(void) 103 { 104 return itcm_present; 105 } 106 EXPORT_SYMBOL(tcm_itcm_present); 107 108 static int __init setup_tcm_bank(u8 type, u8 bank, u8 banks, 109 u32 *offset) 110 { 111 const int tcm_sizes[16] = { 0, -1, -1, 4, 8, 16, 32, 64, 128, 112 256, 512, 1024, -1, -1, -1, -1 }; 113 u32 tcm_region; 114 int tcm_size; 115 116 /* 117 * If there are more than one TCM bank of this type, 118 * select the TCM bank to operate on in the TCM selection 119 * register. 120 */ 121 if (banks > 1) 122 asm("mcr p15, 0, %0, c9, c2, 0" 123 : /* No output operands */ 124 : "r" (bank)); 125 126 /* Read the special TCM region register c9, 0 */ 127 if (!type) 128 asm("mrc p15, 0, %0, c9, c1, 0" 129 : "=r" (tcm_region)); 130 else 131 asm("mrc p15, 0, %0, c9, c1, 1" 132 : "=r" (tcm_region)); 133 134 tcm_size = tcm_sizes[(tcm_region >> 2) & 0x0f]; 135 if (tcm_size < 0) { 136 pr_err("CPU: %sTCM%d of unknown size\n", 137 type ? "I" : "D", bank); 138 return -EINVAL; 139 } else if (tcm_size > 32) { 140 pr_err("CPU: %sTCM%d larger than 32k found\n", 141 type ? "I" : "D", bank); 142 return -EINVAL; 143 } else { 144 pr_info("CPU: found %sTCM%d %dk @ %08x, %senabled\n", 145 type ? "I" : "D", 146 bank, 147 tcm_size, 148 (tcm_region & 0xfffff000U), 149 (tcm_region & 1) ? "" : "not "); 150 } 151 152 /* Not much fun you can do with a size 0 bank */ 153 if (tcm_size == 0) 154 return 0; 155 156 /* Force move the TCM bank to where we want it, enable */ 157 tcm_region = *offset | (tcm_region & 0x00000ffeU) | 1; 158 159 if (!type) 160 asm("mcr p15, 0, %0, c9, c1, 0" 161 : /* No output operands */ 162 : "r" (tcm_region)); 163 else 164 asm("mcr p15, 0, %0, c9, c1, 1" 165 : /* No output operands */ 166 : "r" (tcm_region)); 167 168 /* Increase offset */ 169 *offset += (tcm_size << 10); 170 171 pr_info("CPU: moved %sTCM%d %dk to %08x, enabled\n", 172 type ? "I" : "D", 173 bank, 174 tcm_size, 175 (tcm_region & 0xfffff000U)); 176 return 0; 177 } 178 179 /* 180 * This initializes the TCM memory 181 */ 182 void __init tcm_init(void) 183 { 184 u32 tcm_status; 185 u8 dtcm_banks; 186 u8 itcm_banks; 187 size_t dtcm_code_sz = &__edtcm_data - &__sdtcm_data; 188 size_t itcm_code_sz = &__eitcm_text - &__sitcm_text; 189 char *start; 190 char *end; 191 char *ram; 192 int ret; 193 int i; 194 195 /* 196 * Prior to ARMv5 there is no TCM, and trying to read the status 197 * register will hang the processor. 198 */ 199 if (cpu_architecture() < CPU_ARCH_ARMv5) { 200 if (dtcm_code_sz || itcm_code_sz) 201 pr_info("CPU TCM: %u bytes of DTCM and %u bytes of " 202 "ITCM code compiled in, but no TCM present " 203 "in pre-v5 CPU\n", dtcm_code_sz, itcm_code_sz); 204 return; 205 } 206 207 tcm_status = read_cpuid_tcmstatus(); 208 dtcm_banks = (tcm_status >> 16) & 0x03; 209 itcm_banks = (tcm_status & 0x03); 210 211 /* Values greater than 2 for D/ITCM banks are "reserved" */ 212 if (dtcm_banks > 2) 213 dtcm_banks = 0; 214 if (itcm_banks > 2) 215 itcm_banks = 0; 216 217 /* Setup DTCM if present */ 218 if (dtcm_banks > 0) { 219 for (i = 0; i < dtcm_banks; i++) { 220 ret = setup_tcm_bank(0, i, dtcm_banks, &dtcm_end); 221 if (ret) 222 return; 223 } 224 /* This means you compiled more code than fits into DTCM */ 225 if (dtcm_code_sz > (dtcm_end - DTCM_OFFSET)) { 226 pr_info("CPU DTCM: %u bytes of code compiled to " 227 "DTCM but only %lu bytes of DTCM present\n", 228 dtcm_code_sz, (dtcm_end - DTCM_OFFSET)); 229 goto no_dtcm; 230 } 231 dtcm_res.end = dtcm_end - 1; 232 request_resource(&iomem_resource, &dtcm_res); 233 dtcm_iomap[0].length = dtcm_end - DTCM_OFFSET; 234 iotable_init(dtcm_iomap, 1); 235 /* Copy data from RAM to DTCM */ 236 start = &__sdtcm_data; 237 end = &__edtcm_data; 238 ram = &__dtcm_start; 239 memcpy(start, ram, dtcm_code_sz); 240 pr_debug("CPU DTCM: copied data from %p - %p\n", 241 start, end); 242 dtcm_present = true; 243 } else if (dtcm_code_sz) { 244 pr_info("CPU DTCM: %u bytes of code compiled to DTCM but no " 245 "DTCM banks present in CPU\n", dtcm_code_sz); 246 } 247 248 no_dtcm: 249 /* Setup ITCM if present */ 250 if (itcm_banks > 0) { 251 for (i = 0; i < itcm_banks; i++) { 252 ret = setup_tcm_bank(1, i, itcm_banks, &itcm_end); 253 if (ret) 254 return; 255 } 256 /* This means you compiled more code than fits into ITCM */ 257 if (itcm_code_sz > (itcm_end - ITCM_OFFSET)) { 258 pr_info("CPU ITCM: %u bytes of code compiled to " 259 "ITCM but only %lu bytes of ITCM present\n", 260 itcm_code_sz, (itcm_end - ITCM_OFFSET)); 261 return; 262 } 263 itcm_res.end = itcm_end - 1; 264 request_resource(&iomem_resource, &itcm_res); 265 itcm_iomap[0].length = itcm_end - ITCM_OFFSET; 266 iotable_init(itcm_iomap, 1); 267 /* Copy code from RAM to ITCM */ 268 start = &__sitcm_text; 269 end = &__eitcm_text; 270 ram = &__itcm_start; 271 memcpy(start, ram, itcm_code_sz); 272 pr_debug("CPU ITCM: copied code from %p - %p\n", 273 start, end); 274 itcm_present = true; 275 } else if (itcm_code_sz) { 276 pr_info("CPU ITCM: %u bytes of code compiled to ITCM but no " 277 "ITCM banks present in CPU\n", itcm_code_sz); 278 } 279 } 280 281 /* 282 * This creates the TCM memory pool and has to be done later, 283 * during the core_initicalls, since the allocator is not yet 284 * up and running when the first initialization runs. 285 */ 286 static int __init setup_tcm_pool(void) 287 { 288 u32 dtcm_pool_start = (u32) &__edtcm_data; 289 u32 itcm_pool_start = (u32) &__eitcm_text; 290 int ret; 291 292 /* 293 * Set up malloc pool, 2^2 = 4 bytes granularity since 294 * the TCM is sometimes just 4 KiB. NB: pages and cache 295 * line alignments does not matter in TCM! 296 */ 297 tcm_pool = gen_pool_create(2, -1); 298 299 pr_debug("Setting up TCM memory pool\n"); 300 301 /* Add the rest of DTCM to the TCM pool */ 302 if (dtcm_present) { 303 if (dtcm_pool_start < dtcm_end) { 304 ret = gen_pool_add(tcm_pool, dtcm_pool_start, 305 dtcm_end - dtcm_pool_start, -1); 306 if (ret) { 307 pr_err("CPU DTCM: could not add DTCM " \ 308 "remainder to pool!\n"); 309 return ret; 310 } 311 pr_debug("CPU DTCM: Added %08x bytes @ %08x to " \ 312 "the TCM memory pool\n", 313 dtcm_end - dtcm_pool_start, 314 dtcm_pool_start); 315 } 316 } 317 318 /* Add the rest of ITCM to the TCM pool */ 319 if (itcm_present) { 320 if (itcm_pool_start < itcm_end) { 321 ret = gen_pool_add(tcm_pool, itcm_pool_start, 322 itcm_end - itcm_pool_start, -1); 323 if (ret) { 324 pr_err("CPU ITCM: could not add ITCM " \ 325 "remainder to pool!\n"); 326 return ret; 327 } 328 pr_debug("CPU ITCM: Added %08x bytes @ %08x to " \ 329 "the TCM memory pool\n", 330 itcm_end - itcm_pool_start, 331 itcm_pool_start); 332 } 333 } 334 return 0; 335 } 336 337 core_initcall(setup_tcm_pool); 338