Back to home page

OSCL-LXR

 
 

    


0001 ==================================================
0002 ARM TCM (Tightly-Coupled Memory) handling in Linux
0003 ==================================================
0004 
0005 Written by Linus Walleij <linus.walleij@stericsson.com>
0006 
0007 Some ARM SoCs have a so-called TCM (Tightly-Coupled Memory).
0008 This is usually just a few (4-64) KiB of RAM inside the ARM
0009 processor.
0010 
0011 Due to being embedded inside the CPU, the TCM has a
0012 Harvard-architecture, so there is an ITCM (instruction TCM)
0013 and a DTCM (data TCM). The DTCM can not contain any
0014 instructions, but the ITCM can actually contain data.
0015 The size of DTCM or ITCM is minimum 4KiB so the typical
0016 minimum configuration is 4KiB ITCM and 4KiB DTCM.
0017 
0018 ARM CPUs have special registers to read out status, physical
0019 location and size of TCM memories. arch/arm/include/asm/cputype.h
0020 defines a CPUID_TCM register that you can read out from the
0021 system control coprocessor. Documentation from ARM can be found
0022 at http://infocenter.arm.com, search for "TCM Status Register"
0023 to see documents for all CPUs. Reading this register you can
0024 determine if ITCM (bits 1-0) and/or DTCM (bit 17-16) is present
0025 in the machine.
0026 
0027 There is further a TCM region register (search for "TCM Region
0028 Registers" at the ARM site) that can report and modify the location
0029 size of TCM memories at runtime. This is used to read out and modify
0030 TCM location and size. Notice that this is not a MMU table: you
0031 actually move the physical location of the TCM around. At the
0032 place you put it, it will mask any underlying RAM from the
0033 CPU so it is usually wise not to overlap any physical RAM with
0034 the TCM.
0035 
0036 The TCM memory can then be remapped to another address again using
0037 the MMU, but notice that the TCM is often used in situations where
0038 the MMU is turned off. To avoid confusion the current Linux
0039 implementation will map the TCM 1 to 1 from physical to virtual
0040 memory in the location specified by the kernel. Currently Linux
0041 will map ITCM to 0xfffe0000 and on, and DTCM to 0xfffe8000 and
0042 on, supporting a maximum of 32KiB of ITCM and 32KiB of DTCM.
0043 
0044 Newer versions of the region registers also support dividing these
0045 TCMs in two separate banks, so for example an 8KiB ITCM is divided
0046 into two 4KiB banks with its own control registers. The idea is to
0047 be able to lock and hide one of the banks for use by the secure
0048 world (TrustZone).
0049 
0050 TCM is used for a few things:
0051 
0052 - FIQ and other interrupt handlers that need deterministic
0053   timing and cannot wait for cache misses.
0054 
0055 - Idle loops where all external RAM is set to self-refresh
0056   retention mode, so only on-chip RAM is accessible by
0057   the CPU and then we hang inside ITCM waiting for an
0058   interrupt.
0059 
0060 - Other operations which implies shutting off or reconfiguring
0061   the external RAM controller.
0062 
0063 There is an interface for using TCM on the ARM architecture
0064 in <asm/tcm.h>. Using this interface it is possible to:
0065 
0066 - Define the physical address and size of ITCM and DTCM.
0067 
0068 - Tag functions to be compiled into ITCM.
0069 
0070 - Tag data and constants to be allocated to DTCM and ITCM.
0071 
0072 - Have the remaining TCM RAM added to a special
0073   allocation pool with gen_pool_create() and gen_pool_add()
0074   and provice tcm_alloc() and tcm_free() for this
0075   memory. Such a heap is great for things like saving
0076   device state when shutting off device power domains.
0077 
0078 A machine that has TCM memory shall select HAVE_TCM from
0079 arch/arm/Kconfig for itself. Code that needs to use TCM shall
0080 #include <asm/tcm.h>
0081 
0082 Functions to go into itcm can be tagged like this:
0083 int __tcmfunc foo(int bar);
0084 
0085 Since these are marked to become long_calls and you may want
0086 to have functions called locally inside the TCM without
0087 wasting space, there is also the __tcmlocalfunc prefix that
0088 will make the call relative.
0089 
0090 Variables to go into dtcm can be tagged like this::
0091 
0092   int __tcmdata foo;
0093 
0094 Constants can be tagged like this::
0095 
0096   int __tcmconst foo;
0097 
0098 To put assembler into TCM just use::
0099 
0100   .section ".tcm.text" or .section ".tcm.data"
0101 
0102 respectively.
0103 
0104 Example code::
0105 
0106   #include <asm/tcm.h>
0107 
0108   /* Uninitialized data */
0109   static u32 __tcmdata tcmvar;
0110   /* Initialized data */
0111   static u32 __tcmdata tcmassigned = 0x2BADBABEU;
0112   /* Constant */
0113   static const u32 __tcmconst tcmconst = 0xCAFEBABEU;
0114 
0115   static void __tcmlocalfunc tcm_to_tcm(void)
0116   {
0117         int i;
0118         for (i = 0; i < 100; i++)
0119                 tcmvar ++;
0120   }
0121 
0122   static void __tcmfunc hello_tcm(void)
0123   {
0124         /* Some abstract code that runs in ITCM */
0125         int i;
0126         for (i = 0; i < 100; i++) {
0127                 tcmvar ++;
0128         }
0129         tcm_to_tcm();
0130   }
0131 
0132   static void __init test_tcm(void)
0133   {
0134         u32 *tcmem;
0135         int i;
0136 
0137         hello_tcm();
0138         printk("Hello TCM executed from ITCM RAM\n");
0139 
0140         printk("TCM variable from testrun: %u @ %p\n", tcmvar, &tcmvar);
0141         tcmvar = 0xDEADBEEFU;
0142         printk("TCM variable: 0x%x @ %p\n", tcmvar, &tcmvar);
0143 
0144         printk("TCM assigned variable: 0x%x @ %p\n", tcmassigned, &tcmassigned);
0145 
0146         printk("TCM constant: 0x%x @ %p\n", tcmconst, &tcmconst);
0147 
0148         /* Allocate some TCM memory from the pool */
0149         tcmem = tcm_alloc(20);
0150         if (tcmem) {
0151                 printk("TCM Allocated 20 bytes of TCM @ %p\n", tcmem);
0152                 tcmem[0] = 0xDEADBEEFU;
0153                 tcmem[1] = 0x2BADBABEU;
0154                 tcmem[2] = 0xCAFEBABEU;
0155                 tcmem[3] = 0xDEADBEEFU;
0156                 tcmem[4] = 0x2BADBABEU;
0157                 for (i = 0; i < 5; i++)
0158                         printk("TCM tcmem[%d] = %08x\n", i, tcmem[i]);
0159                 tcm_free(tcmem, 20);
0160         }
0161   }