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 <mach/memory.h> 19 #include "tcm.h" 20 21 static struct gen_pool *tcm_pool; 22 23 /* TCM section definitions from the linker */ 24 extern char __itcm_start, __sitcm_text, __eitcm_text; 25 extern char __dtcm_start, __sdtcm_data, __edtcm_data; 26 27 /* These will be increased as we run */ 28 u32 dtcm_end = DTCM_OFFSET; 29 u32 itcm_end = ITCM_OFFSET; 30 31 /* 32 * TCM memory resources 33 */ 34 static struct resource dtcm_res = { 35 .name = "DTCM RAM", 36 .start = DTCM_OFFSET, 37 .end = DTCM_OFFSET, 38 .flags = IORESOURCE_MEM 39 }; 40 41 static struct resource itcm_res = { 42 .name = "ITCM RAM", 43 .start = ITCM_OFFSET, 44 .end = ITCM_OFFSET, 45 .flags = IORESOURCE_MEM 46 }; 47 48 static struct map_desc dtcm_iomap[] __initdata = { 49 { 50 .virtual = DTCM_OFFSET, 51 .pfn = __phys_to_pfn(DTCM_OFFSET), 52 .length = 0, 53 .type = MT_MEMORY_DTCM 54 } 55 }; 56 57 static struct map_desc itcm_iomap[] __initdata = { 58 { 59 .virtual = ITCM_OFFSET, 60 .pfn = __phys_to_pfn(ITCM_OFFSET), 61 .length = 0, 62 .type = MT_MEMORY_ITCM 63 } 64 }; 65 66 /* 67 * Allocate a chunk of TCM memory 68 */ 69 void *tcm_alloc(size_t len) 70 { 71 unsigned long vaddr; 72 73 if (!tcm_pool) 74 return NULL; 75 76 vaddr = gen_pool_alloc(tcm_pool, len); 77 if (!vaddr) 78 return NULL; 79 80 return (void *) vaddr; 81 } 82 EXPORT_SYMBOL(tcm_alloc); 83 84 /* 85 * Free a chunk of TCM memory 86 */ 87 void tcm_free(void *addr, size_t len) 88 { 89 gen_pool_free(tcm_pool, (unsigned long) addr, len); 90 } 91 EXPORT_SYMBOL(tcm_free); 92 93 static int __init setup_tcm_bank(u8 type, u8 bank, u8 banks, 94 u32 *offset) 95 { 96 const int tcm_sizes[16] = { 0, -1, -1, 4, 8, 16, 32, 64, 128, 97 256, 512, 1024, -1, -1, -1, -1 }; 98 u32 tcm_region; 99 int tcm_size; 100 101 /* 102 * If there are more than one TCM bank of this type, 103 * select the TCM bank to operate on in the TCM selection 104 * register. 105 */ 106 if (banks > 1) 107 asm("mcr p15, 0, %0, c9, c2, 0" 108 : /* No output operands */ 109 : "r" (bank)); 110 111 /* Read the special TCM region register c9, 0 */ 112 if (!type) 113 asm("mrc p15, 0, %0, c9, c1, 0" 114 : "=r" (tcm_region)); 115 else 116 asm("mrc p15, 0, %0, c9, c1, 1" 117 : "=r" (tcm_region)); 118 119 tcm_size = tcm_sizes[(tcm_region >> 2) & 0x0f]; 120 if (tcm_size < 0) { 121 pr_err("CPU: %sTCM%d of unknown size\n", 122 type ? "I" : "D", bank); 123 return -EINVAL; 124 } else if (tcm_size > 32) { 125 pr_err("CPU: %sTCM%d larger than 32k found\n", 126 type ? "I" : "D", bank); 127 return -EINVAL; 128 } else { 129 pr_info("CPU: found %sTCM%d %dk @ %08x, %senabled\n", 130 type ? "I" : "D", 131 bank, 132 tcm_size, 133 (tcm_region & 0xfffff000U), 134 (tcm_region & 1) ? "" : "not "); 135 } 136 137 /* Force move the TCM bank to where we want it, enable */ 138 tcm_region = *offset | (tcm_region & 0x00000ffeU) | 1; 139 140 if (!type) 141 asm("mcr p15, 0, %0, c9, c1, 0" 142 : /* No output operands */ 143 : "r" (tcm_region)); 144 else 145 asm("mcr p15, 0, %0, c9, c1, 1" 146 : /* No output operands */ 147 : "r" (tcm_region)); 148 149 /* Increase offset */ 150 *offset += (tcm_size << 10); 151 152 pr_info("CPU: moved %sTCM%d %dk to %08x, enabled\n", 153 type ? "I" : "D", 154 bank, 155 tcm_size, 156 (tcm_region & 0xfffff000U)); 157 return 0; 158 } 159 160 /* 161 * This initializes the TCM memory 162 */ 163 void __init tcm_init(void) 164 { 165 u32 tcm_status = read_cpuid_tcmstatus(); 166 u8 dtcm_banks = (tcm_status >> 16) & 0x03; 167 u8 itcm_banks = (tcm_status & 0x03); 168 char *start; 169 char *end; 170 char *ram; 171 int ret; 172 int i; 173 174 /* Setup DTCM if present */ 175 if (dtcm_banks > 0) { 176 for (i = 0; i < dtcm_banks; i++) { 177 ret = setup_tcm_bank(0, i, dtcm_banks, &dtcm_end); 178 if (ret) 179 return; 180 } 181 dtcm_res.end = dtcm_end - 1; 182 request_resource(&iomem_resource, &dtcm_res); 183 dtcm_iomap[0].length = dtcm_end - DTCM_OFFSET; 184 iotable_init(dtcm_iomap, 1); 185 /* Copy data from RAM to DTCM */ 186 start = &__sdtcm_data; 187 end = &__edtcm_data; 188 ram = &__dtcm_start; 189 /* This means you compiled more code than fits into DTCM */ 190 BUG_ON((end - start) > (dtcm_end - DTCM_OFFSET)); 191 memcpy(start, ram, (end-start)); 192 pr_debug("CPU DTCM: copied data from %p - %p\n", start, end); 193 } 194 195 /* Setup ITCM if present */ 196 if (itcm_banks > 0) { 197 for (i = 0; i < itcm_banks; i++) { 198 ret = setup_tcm_bank(1, i, itcm_banks, &itcm_end); 199 if (ret) 200 return; 201 } 202 itcm_res.end = itcm_end - 1; 203 request_resource(&iomem_resource, &itcm_res); 204 itcm_iomap[0].length = itcm_end - ITCM_OFFSET; 205 iotable_init(itcm_iomap, 1); 206 /* Copy code from RAM to ITCM */ 207 start = &__sitcm_text; 208 end = &__eitcm_text; 209 ram = &__itcm_start; 210 /* This means you compiled more code than fits into ITCM */ 211 BUG_ON((end - start) > (itcm_end - ITCM_OFFSET)); 212 memcpy(start, ram, (end-start)); 213 pr_debug("CPU ITCM: copied code from %p - %p\n", start, end); 214 } 215 } 216 217 /* 218 * This creates the TCM memory pool and has to be done later, 219 * during the core_initicalls, since the allocator is not yet 220 * up and running when the first initialization runs. 221 */ 222 static int __init setup_tcm_pool(void) 223 { 224 u32 tcm_status = read_cpuid_tcmstatus(); 225 u32 dtcm_pool_start = (u32) &__edtcm_data; 226 u32 itcm_pool_start = (u32) &__eitcm_text; 227 int ret; 228 229 /* 230 * Set up malloc pool, 2^2 = 4 bytes granularity since 231 * the TCM is sometimes just 4 KiB. NB: pages and cache 232 * line alignments does not matter in TCM! 233 */ 234 tcm_pool = gen_pool_create(2, -1); 235 236 pr_debug("Setting up TCM memory pool\n"); 237 238 /* Add the rest of DTCM to the TCM pool */ 239 if (tcm_status & (0x03 << 16)) { 240 if (dtcm_pool_start < dtcm_end) { 241 ret = gen_pool_add(tcm_pool, dtcm_pool_start, 242 dtcm_end - dtcm_pool_start, -1); 243 if (ret) { 244 pr_err("CPU DTCM: could not add DTCM " \ 245 "remainder to pool!\n"); 246 return ret; 247 } 248 pr_debug("CPU DTCM: Added %08x bytes @ %08x to " \ 249 "the TCM memory pool\n", 250 dtcm_end - dtcm_pool_start, 251 dtcm_pool_start); 252 } 253 } 254 255 /* Add the rest of ITCM to the TCM pool */ 256 if (tcm_status & 0x03) { 257 if (itcm_pool_start < itcm_end) { 258 ret = gen_pool_add(tcm_pool, itcm_pool_start, 259 itcm_end - itcm_pool_start, -1); 260 if (ret) { 261 pr_err("CPU ITCM: could not add ITCM " \ 262 "remainder to pool!\n"); 263 return ret; 264 } 265 pr_debug("CPU ITCM: Added %08x bytes @ %08x to " \ 266 "the TCM memory pool\n", 267 itcm_end - itcm_pool_start, 268 itcm_pool_start); 269 } 270 } 271 return 0; 272 } 273 274 core_initcall(setup_tcm_pool); 275