^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) ==================================================
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) ARM TCM (Tightly-Coupled Memory) handling in Linux
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) ==================================================
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) Written by Linus Walleij <linus.walleij@stericsson.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) Some ARM SoCs have a so-called TCM (Tightly-Coupled Memory).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) This is usually just a few (4-64) KiB of RAM inside the ARM
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) processor.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) Due to being embedded inside the CPU, the TCM has a
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) Harvard-architecture, so there is an ITCM (instruction TCM)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) and a DTCM (data TCM). The DTCM can not contain any
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) instructions, but the ITCM can actually contain data.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) The size of DTCM or ITCM is minimum 4KiB so the typical
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) minimum configuration is 4KiB ITCM and 4KiB DTCM.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) ARM CPUs have special registers to read out status, physical
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) location and size of TCM memories. arch/arm/include/asm/cputype.h
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) defines a CPUID_TCM register that you can read out from the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) system control coprocessor. Documentation from ARM can be found
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) at http://infocenter.arm.com, search for "TCM Status Register"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) to see documents for all CPUs. Reading this register you can
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) determine if ITCM (bits 1-0) and/or DTCM (bit 17-16) is present
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) in the machine.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) There is further a TCM region register (search for "TCM Region
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) Registers" at the ARM site) that can report and modify the location
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) size of TCM memories at runtime. This is used to read out and modify
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) TCM location and size. Notice that this is not a MMU table: you
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) actually move the physical location of the TCM around. At the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) place you put it, it will mask any underlying RAM from the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) CPU so it is usually wise not to overlap any physical RAM with
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) the TCM.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) The TCM memory can then be remapped to another address again using
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) the MMU, but notice that the TCM if often used in situations where
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) the MMU is turned off. To avoid confusion the current Linux
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) implementation will map the TCM 1 to 1 from physical to virtual
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) memory in the location specified by the kernel. Currently Linux
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) will map ITCM to 0xfffe0000 and on, and DTCM to 0xfffe8000 and
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) on, supporting a maximum of 32KiB of ITCM and 32KiB of DTCM.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) Newer versions of the region registers also support dividing these
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) TCMs in two separate banks, so for example an 8KiB ITCM is divided
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) into two 4KiB banks with its own control registers. The idea is to
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) be able to lock and hide one of the banks for use by the secure
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) world (TrustZone).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) TCM is used for a few things:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) - FIQ and other interrupt handlers that need deterministic
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) timing and cannot wait for cache misses.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) - Idle loops where all external RAM is set to self-refresh
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) retention mode, so only on-chip RAM is accessible by
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) the CPU and then we hang inside ITCM waiting for an
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) interrupt.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) - Other operations which implies shutting off or reconfiguring
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) the external RAM controller.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) There is an interface for using TCM on the ARM architecture
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) in <asm/tcm.h>. Using this interface it is possible to:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) - Define the physical address and size of ITCM and DTCM.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) - Tag functions to be compiled into ITCM.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) - Tag data and constants to be allocated to DTCM and ITCM.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) - Have the remaining TCM RAM added to a special
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) allocation pool with gen_pool_create() and gen_pool_add()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) and provice tcm_alloc() and tcm_free() for this
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) memory. Such a heap is great for things like saving
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) device state when shutting off device power domains.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) A machine that has TCM memory shall select HAVE_TCM from
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) arch/arm/Kconfig for itself. Code that needs to use TCM shall
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) #include <asm/tcm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) Functions to go into itcm can be tagged like this:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) int __tcmfunc foo(int bar);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) Since these are marked to become long_calls and you may want
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) to have functions called locally inside the TCM without
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) wasting space, there is also the __tcmlocalfunc prefix that
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) will make the call relative.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) Variables to go into dtcm can be tagged like this::
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) int __tcmdata foo;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) Constants can be tagged like this::
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) int __tcmconst foo;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) To put assembler into TCM just use::
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) .section ".tcm.text" or .section ".tcm.data"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) respectively.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) Example code::
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) #include <asm/tcm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) /* Uninitialized data */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) static u32 __tcmdata tcmvar;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) /* Initialized data */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) static u32 __tcmdata tcmassigned = 0x2BADBABEU;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) /* Constant */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) static const u32 __tcmconst tcmconst = 0xCAFEBABEU;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) static void __tcmlocalfunc tcm_to_tcm(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) for (i = 0; i < 100; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) tcmvar ++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) static void __tcmfunc hello_tcm(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) /* Some abstract code that runs in ITCM */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) for (i = 0; i < 100; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) tcmvar ++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) tcm_to_tcm();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) static void __init test_tcm(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) u32 *tcmem;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) hello_tcm();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) printk("Hello TCM executed from ITCM RAM\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) printk("TCM variable from testrun: %u @ %p\n", tcmvar, &tcmvar);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) tcmvar = 0xDEADBEEFU;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) printk("TCM variable: 0x%x @ %p\n", tcmvar, &tcmvar);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) printk("TCM assigned variable: 0x%x @ %p\n", tcmassigned, &tcmassigned);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) printk("TCM constant: 0x%x @ %p\n", tcmconst, &tcmconst);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) /* Allocate some TCM memory from the pool */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) tcmem = tcm_alloc(20);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) if (tcmem) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) printk("TCM Allocated 20 bytes of TCM @ %p\n", tcmem);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) tcmem[0] = 0xDEADBEEFU;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) tcmem[1] = 0x2BADBABEU;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) tcmem[2] = 0xCAFEBABEU;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) tcmem[3] = 0xDEADBEEFU;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) tcmem[4] = 0x2BADBABEU;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) for (i = 0; i < 5; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) printk("TCM tcmem[%d] = %08x\n", i, tcmem[i]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) tcm_free(tcmem, 20);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) }