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