0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/init.h>
0010 #include <linux/kernel.h>
0011 #include <linux/module.h>
0012 #include <linux/stddef.h>
0013 #include <linux/ioport.h>
0014 #include <linux/genalloc.h>
0015 #include <linux/string.h> /* memcpy */
0016 #include <asm/cputype.h>
0017 #include <asm/mach/map.h>
0018 #include <asm/memory.h>
0019 #include <asm/system_info.h>
0020 #include <asm/traps.h>
0021 #include <asm/tcm.h>
0022
0023 #define TCMTR_FORMAT_MASK 0xe0000000U
0024
0025 static struct gen_pool *tcm_pool;
0026 static bool dtcm_present;
0027 static bool itcm_present;
0028
0029
0030 extern char __itcm_start, __sitcm_text, __eitcm_text;
0031 extern char __dtcm_start, __sdtcm_data, __edtcm_data;
0032
0033
0034 static u32 dtcm_end = DTCM_OFFSET;
0035 static u32 itcm_end = ITCM_OFFSET;
0036
0037
0038
0039
0040 static struct resource dtcm_res = {
0041 .name = "DTCM RAM",
0042 .start = DTCM_OFFSET,
0043 .end = DTCM_OFFSET,
0044 .flags = IORESOURCE_MEM
0045 };
0046
0047 static struct resource itcm_res = {
0048 .name = "ITCM RAM",
0049 .start = ITCM_OFFSET,
0050 .end = ITCM_OFFSET,
0051 .flags = IORESOURCE_MEM
0052 };
0053
0054 static struct map_desc dtcm_iomap[] __initdata = {
0055 {
0056 .virtual = DTCM_OFFSET,
0057 .pfn = __phys_to_pfn(DTCM_OFFSET),
0058 .length = 0,
0059 .type = MT_MEMORY_RW_DTCM
0060 }
0061 };
0062
0063 static struct map_desc itcm_iomap[] __initdata = {
0064 {
0065 .virtual = ITCM_OFFSET,
0066 .pfn = __phys_to_pfn(ITCM_OFFSET),
0067 .length = 0,
0068 .type = MT_MEMORY_RWX_ITCM,
0069 }
0070 };
0071
0072
0073
0074
0075 void *tcm_alloc(size_t len)
0076 {
0077 unsigned long vaddr;
0078
0079 if (!tcm_pool)
0080 return NULL;
0081
0082 vaddr = gen_pool_alloc(tcm_pool, len);
0083 if (!vaddr)
0084 return NULL;
0085
0086 return (void *) vaddr;
0087 }
0088 EXPORT_SYMBOL(tcm_alloc);
0089
0090
0091
0092
0093 void tcm_free(void *addr, size_t len)
0094 {
0095 gen_pool_free(tcm_pool, (unsigned long) addr, len);
0096 }
0097 EXPORT_SYMBOL(tcm_free);
0098
0099 bool tcm_dtcm_present(void)
0100 {
0101 return dtcm_present;
0102 }
0103 EXPORT_SYMBOL(tcm_dtcm_present);
0104
0105 bool tcm_itcm_present(void)
0106 {
0107 return itcm_present;
0108 }
0109 EXPORT_SYMBOL(tcm_itcm_present);
0110
0111 static int __init setup_tcm_bank(u8 type, u8 bank, u8 banks,
0112 u32 *offset)
0113 {
0114 const int tcm_sizes[16] = { 0, -1, -1, 4, 8, 16, 32, 64, 128,
0115 256, 512, 1024, -1, -1, -1, -1 };
0116 u32 tcm_region;
0117 int tcm_size;
0118
0119
0120
0121
0122
0123
0124 if (banks > 1)
0125 asm("mcr p15, 0, %0, c9, c2, 0"
0126 :
0127 : "r" (bank));
0128
0129
0130 if (!type)
0131 asm("mrc p15, 0, %0, c9, c1, 0"
0132 : "=r" (tcm_region));
0133 else
0134 asm("mrc p15, 0, %0, c9, c1, 1"
0135 : "=r" (tcm_region));
0136
0137 tcm_size = tcm_sizes[(tcm_region >> 2) & 0x0f];
0138 if (tcm_size < 0) {
0139 pr_err("CPU: %sTCM%d of unknown size\n",
0140 type ? "I" : "D", bank);
0141 return -EINVAL;
0142 } else if (tcm_size > 32) {
0143 pr_err("CPU: %sTCM%d larger than 32k found\n",
0144 type ? "I" : "D", bank);
0145 return -EINVAL;
0146 } else {
0147 pr_info("CPU: found %sTCM%d %dk @ %08x, %senabled\n",
0148 type ? "I" : "D",
0149 bank,
0150 tcm_size,
0151 (tcm_region & 0xfffff000U),
0152 (tcm_region & 1) ? "" : "not ");
0153 }
0154
0155
0156 if (tcm_size == 0)
0157 return 0;
0158
0159
0160 tcm_region = *offset | (tcm_region & 0x00000ffeU) | 1;
0161
0162 if (!type)
0163 asm("mcr p15, 0, %0, c9, c1, 0"
0164 :
0165 : "r" (tcm_region));
0166 else
0167 asm("mcr p15, 0, %0, c9, c1, 1"
0168 :
0169 : "r" (tcm_region));
0170
0171
0172 *offset += (tcm_size << 10);
0173
0174 pr_info("CPU: moved %sTCM%d %dk to %08x, enabled\n",
0175 type ? "I" : "D",
0176 bank,
0177 tcm_size,
0178 (tcm_region & 0xfffff000U));
0179 return 0;
0180 }
0181
0182
0183
0184
0185
0186
0187
0188
0189
0190
0191
0192
0193
0194
0195
0196
0197
0198
0199
0200
0201
0202
0203
0204
0205
0206
0207
0208
0209
0210
0211
0212
0213
0214
0215
0216
0217
0218
0219
0220
0221
0222
0223
0224
0225
0226
0227
0228
0229
0230
0231
0232
0233 #define TCM_REGION_READ_MASK 0xffff0fdf
0234 #define TCM_REGION_READ_INSTR 0xee190f11
0235 #define DEST_REG_SHIFT 12
0236 #define DEST_REG_MASK 0xf
0237
0238 static int __init tcm_handler(struct pt_regs *regs, unsigned int instr)
0239 {
0240 regs->uregs[(instr >> DEST_REG_SHIFT) & DEST_REG_MASK] = 0;
0241 regs->ARM_pc += 4;
0242 return 0;
0243 }
0244
0245 static struct undef_hook tcm_hook __initdata = {
0246 .instr_mask = TCM_REGION_READ_MASK,
0247 .instr_val = TCM_REGION_READ_INSTR,
0248 .cpsr_mask = MODE_MASK,
0249 .cpsr_val = SVC_MODE,
0250 .fn = tcm_handler
0251 };
0252
0253
0254
0255
0256 void __init tcm_init(void)
0257 {
0258 u32 tcm_status;
0259 u8 dtcm_banks;
0260 u8 itcm_banks;
0261 size_t dtcm_code_sz = &__edtcm_data - &__sdtcm_data;
0262 size_t itcm_code_sz = &__eitcm_text - &__sitcm_text;
0263 char *start;
0264 char *end;
0265 char *ram;
0266 int ret;
0267 int i;
0268
0269
0270
0271
0272
0273 if (cpu_architecture() < CPU_ARCH_ARMv5) {
0274 if (dtcm_code_sz || itcm_code_sz)
0275 pr_info("CPU TCM: %u bytes of DTCM and %u bytes of "
0276 "ITCM code compiled in, but no TCM present "
0277 "in pre-v5 CPU\n", dtcm_code_sz, itcm_code_sz);
0278 return;
0279 }
0280
0281 tcm_status = read_cpuid_tcmstatus();
0282
0283
0284
0285
0286 if (tcm_status & TCMTR_FORMAT_MASK)
0287 return;
0288
0289 dtcm_banks = (tcm_status >> 16) & 0x03;
0290 itcm_banks = (tcm_status & 0x03);
0291
0292 register_undef_hook(&tcm_hook);
0293
0294
0295 if (dtcm_banks > 2)
0296 dtcm_banks = 0;
0297 if (itcm_banks > 2)
0298 itcm_banks = 0;
0299
0300
0301 if (dtcm_banks > 0) {
0302 for (i = 0; i < dtcm_banks; i++) {
0303 ret = setup_tcm_bank(0, i, dtcm_banks, &dtcm_end);
0304 if (ret)
0305 goto unregister;
0306 }
0307
0308 if (dtcm_code_sz > (dtcm_end - DTCM_OFFSET)) {
0309 pr_info("CPU DTCM: %u bytes of code compiled to "
0310 "DTCM but only %lu bytes of DTCM present\n",
0311 dtcm_code_sz, (dtcm_end - DTCM_OFFSET));
0312 goto no_dtcm;
0313 }
0314
0315
0316
0317
0318 if (!(dtcm_end - DTCM_OFFSET))
0319 goto no_dtcm;
0320 dtcm_res.end = dtcm_end - 1;
0321 request_resource(&iomem_resource, &dtcm_res);
0322 dtcm_iomap[0].length = dtcm_end - DTCM_OFFSET;
0323 iotable_init(dtcm_iomap, 1);
0324
0325 start = &__sdtcm_data;
0326 end = &__edtcm_data;
0327 ram = &__dtcm_start;
0328 memcpy(start, ram, dtcm_code_sz);
0329 pr_debug("CPU DTCM: copied data from %p - %p\n",
0330 start, end);
0331 dtcm_present = true;
0332 } else if (dtcm_code_sz) {
0333 pr_info("CPU DTCM: %u bytes of code compiled to DTCM but no "
0334 "DTCM banks present in CPU\n", dtcm_code_sz);
0335 }
0336
0337 no_dtcm:
0338
0339 if (itcm_banks > 0) {
0340 for (i = 0; i < itcm_banks; i++) {
0341 ret = setup_tcm_bank(1, i, itcm_banks, &itcm_end);
0342 if (ret)
0343 goto unregister;
0344 }
0345
0346 if (itcm_code_sz > (itcm_end - ITCM_OFFSET)) {
0347 pr_info("CPU ITCM: %u bytes of code compiled to "
0348 "ITCM but only %lu bytes of ITCM present\n",
0349 itcm_code_sz, (itcm_end - ITCM_OFFSET));
0350 goto unregister;
0351 }
0352
0353
0354
0355
0356 if (!(itcm_end - ITCM_OFFSET))
0357 goto unregister;
0358 itcm_res.end = itcm_end - 1;
0359 request_resource(&iomem_resource, &itcm_res);
0360 itcm_iomap[0].length = itcm_end - ITCM_OFFSET;
0361 iotable_init(itcm_iomap, 1);
0362
0363 start = &__sitcm_text;
0364 end = &__eitcm_text;
0365 ram = &__itcm_start;
0366 memcpy(start, ram, itcm_code_sz);
0367 pr_debug("CPU ITCM: copied code from %p - %p\n",
0368 start, end);
0369 itcm_present = true;
0370 } else if (itcm_code_sz) {
0371 pr_info("CPU ITCM: %u bytes of code compiled to ITCM but no "
0372 "ITCM banks present in CPU\n", itcm_code_sz);
0373 }
0374
0375 unregister:
0376 unregister_undef_hook(&tcm_hook);
0377 }
0378
0379
0380
0381
0382
0383
0384 static int __init setup_tcm_pool(void)
0385 {
0386 u32 dtcm_pool_start = (u32) &__edtcm_data;
0387 u32 itcm_pool_start = (u32) &__eitcm_text;
0388 int ret;
0389
0390
0391
0392
0393
0394
0395 tcm_pool = gen_pool_create(2, -1);
0396
0397 pr_debug("Setting up TCM memory pool\n");
0398
0399
0400 if (dtcm_present) {
0401 if (dtcm_pool_start < dtcm_end) {
0402 ret = gen_pool_add(tcm_pool, dtcm_pool_start,
0403 dtcm_end - dtcm_pool_start, -1);
0404 if (ret) {
0405 pr_err("CPU DTCM: could not add DTCM " \
0406 "remainder to pool!\n");
0407 return ret;
0408 }
0409 pr_debug("CPU DTCM: Added %08x bytes @ %08x to " \
0410 "the TCM memory pool\n",
0411 dtcm_end - dtcm_pool_start,
0412 dtcm_pool_start);
0413 }
0414 }
0415
0416
0417 if (itcm_present) {
0418 if (itcm_pool_start < itcm_end) {
0419 ret = gen_pool_add(tcm_pool, itcm_pool_start,
0420 itcm_end - itcm_pool_start, -1);
0421 if (ret) {
0422 pr_err("CPU ITCM: could not add ITCM " \
0423 "remainder to pool!\n");
0424 return ret;
0425 }
0426 pr_debug("CPU ITCM: Added %08x bytes @ %08x to " \
0427 "the TCM memory pool\n",
0428 itcm_end - itcm_pool_start,
0429 itcm_pool_start);
0430 }
0431 }
0432 return 0;
0433 }
0434
0435 core_initcall(setup_tcm_pool);