← Documents Documentation/arch/arm/stm32/stm32-dma-mdma-chaining.rst GitHub 원문 ↗

Linux 6.18.37 · Architecture

STM32 DMA-MDMA chaining

STM32 DMA와 MDMA를 SRAM 이중 버퍼로 체이닝하는 원리, DMAMUX 매핑, Device Tree 구성, scatter-gather·cyclic 드라이버 절차와 방향별 예외를 설명합니다.

Source pathDocumentation/arch/arm/stm32/stm32-dma-mdma-chaining.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약과 해설

stm32-dma-mdma-chaining.rst:1-415

STM32 DMA는 주변장치와 고속 SRAM 사이의 1단계 전송을 맡고, STM32 MDMA는 SRAM과 DDR 사이의 2단계 전송을 맡습니다. DMA의 Double Buffer Mode와 MDMA linked list를 결합하면 한 SRAM period를 DMA가 사용하는 동안 다른 period를 MDMA가 처리해 CPU 개입과 AXI 버스 부하를 줄일 수 있습니다.

구현의 핵심은 DMAMUX channel ID와 DMA interrupt-clear 정보를 `.peripheral_config`로 MDMA에 전달하고, SRAM period 크기에 맞춘 descriptor 두 세트를 만든 뒤 MDMA를 DMA보다 먼저 시작하는 것입니다. `DMA_MEM_TO_DEV`는 첫 SRAM period를 미리 채워야 하므로 일반적으로 classic DMA가 더 단순합니다.

STM32 DMA-MDMA 체이닝 역할 분담
계층주요 경로동기화
STM32 DMAMUXPeripheral request → DMA channelChannel ID
STM32 DMAPeripheral ↔ SRAM periodTransfer Complete
STM32 MDMASRAM period ↔ DDR bufferDMA TC request
SoftwareDescriptor 구성·제출·종료Callback

각 계층이 담당하는 주소 영역, 트리거, 동시 실행 조건을 한눈에 정리합니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 =======================
4 STM32 DMA-MDMA chaining
5 =======================
6
7
8 Introduction
9 ------------
10
11 This document describes the STM32 DMA-MDMA chaining feature. But before going
12 further, let's introduce the peripherals involved.
13
14 To offload data transfers from the CPU, STM32 microprocessors (MPUs) embed
15 direct memory access controllers (DMA).
16
17 STM32MP1 SoCs embed both STM32 DMA and STM32 MDMA controllers. STM32 DMA
18 request routing capabilities are enhanced by a DMA request multiplexer
19 (STM32 DMAMUX).
20
21 **STM32 DMAMUX**
22
23 STM32 DMAMUX routes any DMA request from a given peripheral to any STM32 DMA
24 controller (STM32MP1 counts two STM32 DMA controllers) channels.
25
26 **STM32 DMA**
27
28 STM32 DMA is mainly used to implement central data buffer storage (usually in
29 the system SRAM) for different peripheral. It can access external RAMs but
30 without the ability to generate convenient burst transfer ensuring the best
31 load of the AXI.
32
33 **STM32 MDMA**
34
35 STM32 MDMA (Master DMA) is mainly used to manage direct data transfers between
36 RAM data buffers without CPU intervention. It can also be used in a
37 hierarchical structure that uses STM32 DMA as first level data buffer
38 interfaces for AHB peripherals, while the STM32 MDMA acts as a second level
39 DMA with better performance. As a AXI/AHB master, STM32 MDMA can take control
40 of the AXI/AHB bus.
41
42
43 Principles
44 ----------
45
46 STM32 DMA-MDMA chaining feature relies on the strengths of STM32 DMA and
47 STM32 MDMA controllers.
48
49 STM32 DMA has a circular Double Buffer Mode (DBM). At each end of transaction
50 (when DMA data counter - DMA_SxNDTR - reaches 0), the memory pointers
51 (configured with DMA_SxSM0AR and DMA_SxM1AR) are swapped and the DMA data
52 counter is automatically reloaded. This allows the SW or the STM32 MDMA to
53 process one memory area while the second memory area is being filled/used by
54 the STM32 DMA transfer.
55
56 With STM32 MDMA linked-list mode, a single request initiates the data array
57 (collection of nodes) to be transferred until the linked-list pointer for the
58 channel is null. The channel transfer complete of the last node is the end of
59 transfer, unless first and last nodes are linked to each other, in such a
60 case, the linked-list loops on to create a circular MDMA transfer.
61
62 STM32 MDMA has direct connections with STM32 DMA. This enables autonomous
63 communication and synchronization between peripherals, thus saving CPU
64 resources and bus congestion. Transfer Complete signal of STM32 DMA channel
65 can triggers STM32 MDMA transfer. STM32 MDMA can clear the request generated
66 by the STM32 DMA by writing to its Interrupt Clear register (whose address is
67 stored in MDMA_CxMAR, and bit mask in MDMA_CxMDR).
68
69 .. table:: STM32 MDMA interconnect table with STM32 DMA
70
71 +--------------+----------------+-----------+------------+
72 | STM32 DMAMUX | STM32 DMA | STM32 DMA | STM32 MDMA |
73 | channels | channels | Transfer | request |
74 | | | complete | |
75 | | | signal | |
76 +==============+================+===========+============+
77 | Channel *0* | DMA1 channel 0 | dma1_tcf0 | *0x00* |
78 +--------------+----------------+-----------+------------+
79 | Channel *1* | DMA1 channel 1 | dma1_tcf1 | *0x01* |
80 +--------------+----------------+-----------+------------+
81 | Channel *2* | DMA1 channel 2 | dma1_tcf2 | *0x02* |
82 +--------------+----------------+-----------+------------+
83 | Channel *3* | DMA1 channel 3 | dma1_tcf3 | *0x03* |
84 +--------------+----------------+-----------+------------+
85 | Channel *4* | DMA1 channel 4 | dma1_tcf4 | *0x04* |
86 +--------------+----------------+-----------+------------+
87 | Channel *5* | DMA1 channel 5 | dma1_tcf5 | *0x05* |
88 +--------------+----------------+-----------+------------+
89 | Channel *6* | DMA1 channel 6 | dma1_tcf6 | *0x06* |
90 +--------------+----------------+-----------+------------+
91 | Channel *7* | DMA1 channel 7 | dma1_tcf7 | *0x07* |
92 +--------------+----------------+-----------+------------+
93 | Channel *8* | DMA2 channel 0 | dma2_tcf0 | *0x08* |
94 +--------------+----------------+-----------+------------+
95 | Channel *9* | DMA2 channel 1 | dma2_tcf1 | *0x09* |
96 +--------------+----------------+-----------+------------+
97 | Channel *10* | DMA2 channel 2 | dma2_tcf2 | *0x0A* |
98 +--------------+----------------+-----------+------------+
99 | Channel *11* | DMA2 channel 3 | dma2_tcf3 | *0x0B* |
100 +--------------+----------------+-----------+------------+
101 | Channel *12* | DMA2 channel 4 | dma2_tcf4 | *0x0C* |
102 +--------------+----------------+-----------+------------+
103 | Channel *13* | DMA2 channel 5 | dma2_tcf5 | *0x0D* |
104 +--------------+----------------+-----------+------------+
105 | Channel *14* | DMA2 channel 6 | dma2_tcf6 | *0x0E* |
106 +--------------+----------------+-----------+------------+
107 | Channel *15* | DMA2 channel 7 | dma2_tcf7 | *0x0F* |
108 +--------------+----------------+-----------+------------+
109
110 STM32 DMA-MDMA chaining feature then uses a SRAM buffer. STM32MP1 SoCs embed
111 three fast access static internal RAMs of various size, used for data storage.
112 Due to STM32 DMA legacy (within microcontrollers), STM32 DMA performances are
113 bad with DDR, while they are optimal with SRAM. Hence the SRAM buffer used
114 between STM32 DMA and STM32 MDMA. This buffer is split in two equal periods
115 and STM32 DMA uses one period while STM32 MDMA uses the other period
116 simultaneously.
117 ::
118
119 dma[1:2]-tcf[0:7]
120 .----------------.
121 ____________ ' _________ V____________
122 | STM32 DMA | / __|>_ \ | STM32 MDMA |
123 |------------| | / \ | |------------|
124 | DMA_SxM0AR |<=>| | SRAM | |<=>| []-[]...[] |
125 | DMA_SxM1AR | | \_____/ | | |
126 |____________| \___<|____/ |____________|
127
128 STM32 DMA-MDMA chaining uses (struct dma_slave_config).peripheral_config to
129 exchange the parameters needed to configure MDMA. These parameters are
130 gathered into a u32 array with three values:
131
132 * the STM32 MDMA request (which is actually the DMAMUX channel ID),
133 * the address of the STM32 DMA register to clear the Transfer Complete
134 interrupt flag,
135 * the mask of the Transfer Complete interrupt flag of the STM32 DMA channel.
136
137 Device Tree updates for STM32 DMA-MDMA chaining support
138 -------------------------------------------------------
139
140 **1. Allocate a SRAM buffer**
141
142 SRAM device tree node is defined in SoC device tree. You can refer to it in
143 your board device tree to define your SRAM pool.
144 ::
145
146 &sram {
147 my_foo_device_dma_pool: dma-sram@0 {
148 reg = <0x0 0x1000>;
149 };
150 };
151
152 Be careful of the start index, in case there are other SRAM consumers.
153 Define your pool size strategically: to optimise chaining, the idea is that
154 STM32 DMA and STM32 MDMA can work simultaneously, on each buffer of the
155 SRAM.
156 If the SRAM period is greater than the expected DMA transfer, then STM32 DMA
157 and STM32 MDMA will work sequentially instead of simultaneously. It is not a
158 functional issue but it is not optimal.
159
160 Don't forget to refer to your SRAM pool in your device node. You need to
161 define a new property.
162 ::
163
164 &my_foo_device {
165 ...
166 my_dma_pool = &my_foo_device_dma_pool;
167 };
168
169 Then get this SRAM pool in your foo driver and allocate your SRAM buffer.
170
171 **2. Allocate a STM32 DMA channel and a STM32 MDMA channel**
172
173 You need to define an extra channel in your device tree node, in addition to
174 the one you should already have for "classic" DMA operation.
175
176 This new channel must be taken from STM32 MDMA channels, so, the phandle of
177 the DMA controller to use is the MDMA controller's one.
178 ::
179
180 &my_foo_device {
181 [...]
182 my_dma_pool = &my_foo_device_dma_pool;
183 dmas = <&dmamux1 ...>, // STM32 DMA channel
184 <&mdma1 0 0x3 0x1200000a 0 0>; // + STM32 MDMA channel
185 };
186
187 Concerning STM32 MDMA bindings:
188
189 1. The request line number : whatever the value here, it will be overwritten
190 by MDMA driver with the STM32 DMAMUX channel ID passed through
191 (struct dma_slave_config).peripheral_config
192
193 2. The priority level : choose Very High (0x3) so that your channel will
194 take priority other the other during request arbitration
195
196 3. A 32bit mask specifying the DMA channel configuration : source and
197 destination address increment, block transfer with 128 bytes per single
198 transfer
199
200 4. The 32bit value specifying the register to be used to acknowledge the
201 request: it will be overwritten by MDMA driver, with the DMA channel
202 interrupt flag clear register address passed through
203 (struct dma_slave_config).peripheral_config
204
205 5. The 32bit mask specifying the value to be written to acknowledge the
206 request: it will be overwritten by MDMA driver, with the DMA channel
207 Transfer Complete flag passed through
208 (struct dma_slave_config).peripheral_config
209
210 Driver updates for STM32 DMA-MDMA chaining support in foo driver
211 ----------------------------------------------------------------
212
213 **0. (optional) Refactor the original sg_table if dmaengine_prep_slave_sg()**
214
215 In case of dmaengine_prep_slave_sg(), the original sg_table can't be used as
216 is. Two new sg_tables must be created from the original one. One for
217 STM32 DMA transfer (where memory address targets now the SRAM buffer instead
218 of DDR buffer) and one for STM32 MDMA transfer (where memory address targets
219 the DDR buffer).
220
221 The new sg_list items must fit SRAM period length. Here is an example for
222 DMA_DEV_TO_MEM:
223 ::
224
225 /*
226 * Assuming sgl and nents, respectively the initial scatterlist and its
227 * length.
228 * Assuming sram_dma_buf and sram_period, respectively the memory
229 * allocated from the pool for DMA usage, and the length of the period,
230 * which is half of the sram_buf size.
231 */
232 struct sg_table new_dma_sgt, new_mdma_sgt;
233 struct scatterlist *s, *_sgl;
234 dma_addr_t ddr_dma_buf;
235 u32 new_nents = 0, len;
236 int i;
237
238 /* Count the number of entries needed */
239 for_each_sg(sgl, s, nents, i)
240 if (sg_dma_len(s) > sram_period)
241 new_nents += DIV_ROUND_UP(sg_dma_len(s), sram_period);
242 else
243 new_nents++;
244
245 /* Create sg table for STM32 DMA channel */
246 ret = sg_alloc_table(&new_dma_sgt, new_nents, GFP_ATOMIC);
247 if (ret)
248 dev_err(dev, "DMA sg table alloc failed\n");
249
250 for_each_sg(new_dma_sgt.sgl, s, new_dma_sgt.nents, i) {
251 _sgl = sgl;
252 sg_dma_len(s) = min(sg_dma_len(_sgl), sram_period);
253 /* Targets the beginning = first half of the sram_buf */
254 s->dma_address = sram_buf;
255 /*
256 * Targets the second half of the sram_buf
257 * for odd indexes of the item of the sg_list
258 */
259 if (i & 1)
260 s->dma_address += sram_period;
261 }
262
263 /* Create sg table for STM32 MDMA channel */
264 ret = sg_alloc_table(&new_mdma_sgt, new_nents, GFP_ATOMIC);
265 if (ret)
266 dev_err(dev, "MDMA sg_table alloc failed\n");
267
268 _sgl = sgl;
269 len = sg_dma_len(sgl);
270 ddr_dma_buf = sg_dma_address(sgl);
271 for_each_sg(mdma_sgt.sgl, s, mdma_sgt.nents, i) {
272 size_t bytes = min_t(size_t, len, sram_period);
273
274 sg_dma_len(s) = bytes;
275 sg_dma_address(s) = ddr_dma_buf;
276 len -= bytes;
277
278 if (!len && sg_next(_sgl)) {
279 _sgl = sg_next(_sgl);
280 len = sg_dma_len(_sgl);
281 ddr_dma_buf = sg_dma_address(_sgl);
282 } else {
283 ddr_dma_buf += bytes;
284 }
285 }
286
287 Don't forget to release these new sg_tables after getting the descriptors
288 with dmaengine_prep_slave_sg().
289
290 **1. Set controller specific parameters**
291
292 First, use dmaengine_slave_config() with a struct dma_slave_config to
293 configure STM32 DMA channel. You just have to take care of DMA addresses,
294 the memory address (depending on the transfer direction) must point on your
295 SRAM buffer, and set (struct dma_slave_config).peripheral_size != 0.
296
297 STM32 DMA driver will check (struct dma_slave_config).peripheral_size to
298 determine if chaining is being used or not. If it is used, then STM32 DMA
299 driver fills (struct dma_slave_config).peripheral_config with an array of
300 three u32 : the first one containing STM32 DMAMUX channel ID, the second one
301 the channel interrupt flag clear register address, and the third one the
302 channel Transfer Complete flag mask.
303
304 Then, use dmaengine_slave_config with another struct dma_slave_config to
305 configure STM32 MDMA channel. Take care of DMA addresses, the device address
306 (depending on the transfer direction) must point on your SRAM buffer, and
307 the memory address must point to the buffer originally used for "classic"
308 DMA operation. Use the previous (struct dma_slave_config).peripheral_size
309 and .peripheral_config that have been updated by STM32 DMA driver, to set
310 (struct dma_slave_config).peripheral_size and .peripheral_config of the
311 struct dma_slave_config to configure STM32 MDMA channel.
312 ::
313
314 struct dma_slave_config dma_conf;
315 struct dma_slave_config mdma_conf;
316
317 memset(&dma_conf, 0, sizeof(dma_conf));
318 [...]
319 config.direction = DMA_DEV_TO_MEM;
320 config.dst_addr = sram_dma_buf; // SRAM buffer
321 config.peripheral_size = 1; // peripheral_size != 0 => chaining
322
323 dmaengine_slave_config(dma_chan, &dma_config);
324
325 memset(&mdma_conf, 0, sizeof(mdma_conf));
326 config.direction = DMA_DEV_TO_MEM;
327 mdma_conf.src_addr = sram_dma_buf; // SRAM buffer
328 mdma_conf.dst_addr = rx_dma_buf; // original memory buffer
329 mdma_conf.peripheral_size = dma_conf.peripheral_size; // <- dma_conf
330 mdma_conf.peripheral_config = dma_config.peripheral_config; // <- dma_conf
331
332 dmaengine_slave_config(mdma_chan, &mdma_conf);
333
334 **2. Get a descriptor for STM32 DMA channel transaction**
335
336 In the same way you get your descriptor for your "classic" DMA operation,
337 you just have to replace the original sg_list (in case of
338 dmaengine_prep_slave_sg()) with the new sg_list using SRAM buffer, or to
339 replace the original buffer address, length and period (in case of
340 dmaengine_prep_dma_cyclic()) with the new SRAM buffer.
341
342 **3. Get a descriptor for STM32 MDMA channel transaction**
343
344 If you previously get descriptor (for STM32 DMA) with
345
346 * dmaengine_prep_slave_sg(), then use dmaengine_prep_slave_sg() for
347 STM32 MDMA;
348 * dmaengine_prep_dma_cyclic(), then use dmaengine_prep_dma_cyclic() for
349 STM32 MDMA.
350
351 Use the new sg_list using SRAM buffer (in case of dmaengine_prep_slave_sg())
352 or, depending on the transfer direction, either the original DDR buffer (in
353 case of DMA_DEV_TO_MEM) or the SRAM buffer (in case of DMA_MEM_TO_DEV), the
354 source address being previously set with dmaengine_slave_config().
355
356 **4. Submit both transactions**
357
358 Before submitting your transactions, you may need to define on which
359 descriptor you want a callback to be called at the end of the transfer
360 (dmaengine_prep_slave_sg()) or the period (dmaengine_prep_dma_cyclic()).
361 Depending on the direction, set the callback on the descriptor that finishes
362 the overall transfer:
363
364 * DMA_DEV_TO_MEM: set the callback on the "MDMA" descriptor
365 * DMA_MEM_TO_DEV: set the callback on the "DMA" descriptor
366
367 Then, submit the descriptors whatever the order, with dmaengine_tx_submit().
368
369 **5. Issue pending requests (and wait for callback notification)**
370
371 As STM32 MDMA channel transfer is triggered by STM32 DMA, you must issue
372 STM32 MDMA channel before STM32 DMA channel.
373
374 If any, your callback will be called to warn you about the end of the overall
375 transfer or the period completion.
376
377 Don't forget to terminate both channels. STM32 DMA channel is configured in
378 cyclic Double-Buffer mode so it won't be disabled by HW, you need to terminate
379 it. STM32 MDMA channel will be stopped by HW in case of sg transfer, but not
380 in case of cyclic transfer. You can terminate it whatever the kind of transfer.
381
382 **STM32 DMA-MDMA chaining DMA_MEM_TO_DEV special case**
383
384 STM32 DMA-MDMA chaining in DMA_MEM_TO_DEV is a special case. Indeed, the
385 STM32 MDMA feeds the SRAM buffer with the DDR data, and the STM32 DMA reads
386 data from SRAM buffer. So some data (the first period) have to be copied in
387 SRAM buffer when the STM32 DMA starts to read.
388
389 A trick could be pausing the STM32 DMA channel (that will raise a Transfer
390 Complete signal, triggering the STM32 MDMA channel), but the first data read
391 by the STM32 DMA could be "wrong". The proper way is to prepare the first SRAM
392 period with dmaengine_prep_dma_memcpy(). Then this first period should be
393 "removed" from the sg or the cyclic transfer.
394
395 Due to this complexity, rather use the STM32 DMA-MDMA chaining for
396 DMA_DEV_TO_MEM and keep the "classic" DMA usage for DMA_MEM_TO_DEV, unless
397 you're not afraid.
398
399 Resources
400 ---------
401
402 Application note, datasheet and reference manual are available on ST website
403 (STM32MP1_).
404
405 Dedicated focus on three application notes (AN5224_, AN4031_ & AN5001_)
406 dealing with STM32 DMAMUX, STM32 DMA and STM32 MDMA.
407
408 .. _STM32MP1: https://www.st.com/en/microcontrollers-microprocessors/stm32mp1-series.html
409 .. _AN5224: https://www.st.com/resource/en/application_note/an5224-stm32-dmamux-the-dma-request-router-stmicroelectronics.pdf
410 .. _AN4031: https://www.st.com/resource/en/application_note/dm00046011-using-the-stm32f2-stm32f4-and-stm32f7-series-dma-controller-stmicroelectronics.pdf
411 .. _AN5001: https://www.st.com/resource/en/application_note/an5001-stm32cube-expansion-package-for-stm32h7-series-mdma-stmicroelectronics.pdf
412
413 :Authors:
414
415 - Amelie Delaunay <amelie.delaunay@foss.st.com>

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

STM32 DMA-MDMA 체이닝

1-7

이 문서는 `GPL-2.0` SPDX 라이선스 식별자를 사용하는 STM32 DMA-MDMA chaining 설명서입니다.

소개

8-20

이 문서는 STM32 DMA-MDMA 체이닝 기능을 설명합니다. 먼저 이 기능에 관여하는 주변장치를 소개합니다.

STM32 마이크로프로세서(MPU)는 CPU에서 데이터 전송 작업을 덜어 내기 위해 직접 메모리 접근 컨트롤러(DMA)를 내장합니다.

STM32MP1 SoC에는 STM32 DMA와 STM32 MDMA 컨트롤러가 모두 들어 있습니다. STM32 DMA의 요청 라우팅 기능은 DMA request multiplexer인 STM32 DMAMUX가 확장합니다.

STM32 DMAMUX

21-25

STM32 DMAMUX는 특정 주변장치에서 발생한 임의의 DMA 요청을 STM32 DMA 컨트롤러의 임의 채널로 라우팅합니다. STM32MP1에는 STM32 DMA 컨트롤러가 두 개 있습니다.

STM32 DMA

26-32

STM32 DMA는 주로 여러 주변장치의 중앙 데이터 버퍼 저장소를 구현하는 데 사용하며, 이 버퍼는 보통 시스템 SRAM에 둡니다. 외부 RAM에도 접근할 수 있지만 AXI 부하를 최적화하는 적절한 burst transfer를 생성하는 능력은 없습니다.

STM32 MDMA

33-42

STM32 MDMA(Master DMA)는 CPU 개입 없이 RAM 데이터 버퍼 사이를 직접 전송하는 데 주로 사용합니다.

계층형 구조에서는 STM32 DMA가 AHB 주변장치를 위한 1단계 데이터 버퍼 인터페이스를 맡고, 성능이 더 높은 STM32 MDMA가 2단계 DMA를 맡을 수 있습니다. AXI/AHB master인 STM32 MDMA는 AXI/AHB 버스의 제어권을 가져올 수 있습니다.

체이닝 원리

43-68

STM32 DMA-MDMA 체이닝은 두 컨트롤러의 장점을 함께 사용합니다.

STM32 DMA에는 순환 Double Buffer Mode(DBM)가 있습니다. 각 트랜잭션이 끝나 `DMA_SxNDTR` 데이터 카운터가 0에 도달하면 `DMA_SxSM0AR`와 `DMA_SxM1AR`로 구성한 메모리 포인터를 서로 바꾸고 데이터 카운터를 자동으로 다시 적재합니다. 따라서 STM32 DMA가 한 메모리 영역을 채우거나 사용하는 동안 소프트웨어 또는 STM32 MDMA가 다른 영역을 처리할 수 있습니다.

STM32 MDMA linked-list mode에서는 요청 하나가 노드 모음인 데이터 배열의 전송을 시작하고, 채널의 linked-list pointer가 null이 될 때까지 계속합니다. 마지막 노드의 channel transfer complete가 전체 전송의 끝입니다. 첫 노드와 마지막 노드를 서로 연결하면 linked list가 반복되어 순환 MDMA 전송이 됩니다.

STM32 MDMA는 STM32 DMA와 직접 연결됩니다. 이 연결은 주변장치 사이의 통신과 동기화를 자율화하여 CPU 자원 사용과 버스 혼잡을 줄입니다. STM32 DMA 채널의 Transfer Complete 신호가 STM32 MDMA 전송을 트리거할 수 있습니다.

STM32 MDMA는 `MDMA_CxMAR`에 저장된 주소의 DMA Interrupt Clear register에 `MDMA_CxMDR`의 bit mask를 기록하여 STM32 DMA가 생성한 요청을 지울 수 있습니다.

STM32 DMA와 MDMA의 인터커넥트

69-109

DMAMUX 채널 0~15는 DMA1·DMA2의 각 채널 Transfer Complete 신호와 MDMA 요청 0x00~0x0F에 일대일로 연결됩니다.

STM32 DMAMUX channelSTM32 DMA channelTransfer complete signalSTM32 MDMA request
Channel 0DMA1 channel 0`dma1_tcf0``0x00`
Channel 1DMA1 channel 1`dma1_tcf1``0x01`
Channel 2DMA1 channel 2`dma1_tcf2``0x02`
Channel 3DMA1 channel 3`dma1_tcf3``0x03`
Channel 4DMA1 channel 4`dma1_tcf4``0x04`
Channel 5DMA1 channel 5`dma1_tcf5``0x05`
Channel 6DMA1 channel 6`dma1_tcf6``0x06`
Channel 7DMA1 channel 7`dma1_tcf7``0x07`
Channel 8DMA2 channel 0`dma2_tcf0``0x08`
Channel 9DMA2 channel 1`dma2_tcf1``0x09`
Channel 10DMA2 channel 2`dma2_tcf2``0x0A`
Channel 11DMA2 channel 3`dma2_tcf3``0x0B`
Channel 12DMA2 channel 4`dma2_tcf4``0x0C`
Channel 13DMA2 channel 5`dma2_tcf5``0x0D`
Channel 14DMA2 channel 6`dma2_tcf6``0x0E`
Channel 15DMA2 channel 7`dma2_tcf7``0x0F`

SRAM 이중 버퍼 파이프라인

110-127

STM32 DMA-MDMA 체이닝은 SRAM 버퍼를 사용합니다. STM32MP1 SoC는 데이터 저장용으로 크기가 서로 다른 고속 내부 정적 RAM 세 개를 내장합니다.

마이크로컨트롤러에서 유래한 STM32 DMA는 DDR 성능이 좋지 않고 SRAM에서는 최적 성능을 냅니다. 이 때문에 STM32 DMA와 STM32 MDMA 사이에 SRAM 버퍼를 둡니다. 버퍼를 같은 크기의 period 두 개로 나누어 STM32 DMA와 STM32 MDMA가 서로 다른 period를 동시에 사용합니다.

STM32 DMA-MDMA 이중 버퍼 체이닝
PeripheralSTM32 DMAMUXSTM32 DMA (DMA_SxM0AR / DMA_SxM1AR)SRAM period ASTM32 MDMA linked listDDR
STM32 DMA dma[1:2]-tcf[0:7]MDMA requestSRAM period BSTM32 MDMA

DMA Transfer Complete 신호가 MDMA를 트리거하며 두 컨트롤러는 SRAM의 반대쪽 period를 번갈아 처리합니다.

MDMA 구성 매개변수 전달

128-136

STM32 DMA-MDMA 체이닝은 `(struct dma_slave_config).peripheral_config`를 사용해 MDMA 구성에 필요한 매개변수를 교환합니다. 매개변수는 다음 세 값을 담은 `u32` 배열로 묶습니다.

  • STM32 MDMA request. 실제 값은 DMAMUX channel ID입니다.
  • Transfer Complete interrupt flag를 지울 STM32 DMA register의 주소입니다.
  • STM32 DMA channel의 Transfer Complete interrupt flag mask입니다.

Device Tree에서 SRAM 버퍼 할당

137-170

SoC Device Tree에 정의된 SRAM 노드를 보드 Device Tree에서 참조하여 전용 SRAM pool을 정의합니다.

&sram {
        my_foo_device_dma_pool: dma-sram@0 {
                reg = <0x0 0x1000>;
        };
};

다른 SRAM 소비자가 있을 수 있으므로 시작 인덱스를 주의해야 합니다. STM32 DMA와 STM32 MDMA가 SRAM의 각 버퍼에서 동시에 작업할 수 있도록 pool 크기를 전략적으로 정하십시오.

SRAM period가 예상 DMA 전송보다 크면 두 컨트롤러가 동시에 움직이지 않고 순차적으로 동작합니다. 기능상 문제는 아니지만 최적 상태는 아닙니다.

장치 노드에서 SRAM pool을 참조하는 새 속성도 정의해야 합니다.

&my_foo_device {
        ...
        my_dma_pool = &my_foo_device_dma_pool;
};

그런 다음 foo 드라이버에서 이 SRAM pool을 얻어 DMA용 SRAM 버퍼를 할당합니다.

Device Tree에서 DMA와 MDMA 채널 할당

171-209

기존의 "classic" DMA 동작용 채널에 더해 장치 노드에 채널 하나를 추가해야 합니다. 새 채널은 STM32 MDMA 채널에서 가져오므로 사용할 DMA controller phandle은 MDMA 컨트롤러의 phandle입니다.

&my_foo_device {
        [...]
        my_dma_pool = &my_foo_device_dma_pool;
        dmas = <&dmamux1 ...>,                // STM32 DMA channel
               <&mdma1 0 0x3 0x1200000a 0 0>; // + STM32 MDMA channel
};

STM32 MDMA binding의 셀은 다음 의미를 갖습니다.

  • 1. Request line number: 여기 지정한 값과 관계없이 MDMA 드라이버가 `(struct dma_slave_config).peripheral_config`로 전달된 STM32 DMAMUX channel ID로 덮어씁니다.
  • 2. Priority level: 요청 중재에서 다른 채널보다 우선하도록 Very High(`0x3`)를 선택합니다.
  • 3. DMA channel configuration을 지정하는 32-bit mask: source·destination address increment와 단일 전송당 128 bytes인 block transfer를 지정합니다.
  • 4. Request acknowledge에 사용할 register를 지정하는 32-bit 값: MDMA 드라이버가 `.peripheral_config`로 받은 DMA channel interrupt flag clear register 주소로 덮어씁니다.
  • 5. Request acknowledge를 위해 기록할 값을 지정하는 32-bit mask: MDMA 드라이버가 `.peripheral_config`로 받은 DMA channel Transfer Complete flag로 덮어씁니다.

드라이버의 scatter-gather 표 재구성

210-289

`dmaengine_prep_slave_sg()`를 사용한다면 원래 `sg_table`을 그대로 사용할 수 없습니다. 원본에서 새 `sg_table` 두 개를 만들어야 합니다. STM32 DMA용 표의 메모리 주소는 DDR 대신 SRAM 버퍼를 가리키고, STM32 MDMA용 표의 메모리 주소는 DDR 버퍼를 가리킵니다.

새 `sg_list` 항목은 SRAM period 길이에 맞아야 합니다. 다음은 `DMA_DEV_TO_MEM` 방향의 예제입니다. `sgl`과 `nents`는 원래 scatterlist와 그 길이이고, `sram_dma_buf`와 `sram_period`는 DMA용 pool에서 할당한 메모리 및 전체 `sram_buf`의 절반인 period 길이라고 가정합니다.

/*
  * Assuming sgl and nents, respectively the initial scatterlist and its
  * length.
  * Assuming sram_dma_buf and sram_period, respectively the memory
  * allocated from the pool for DMA usage, and the length of the period,
  * which is half of the sram_buf size.
  */
struct sg_table new_dma_sgt, new_mdma_sgt;
struct scatterlist *s, *_sgl;
dma_addr_t ddr_dma_buf;
u32 new_nents = 0, len;
int i;

/* Count the number of entries needed */
for_each_sg(sgl, s, nents, i)
        if (sg_dma_len(s) > sram_period)
                new_nents += DIV_ROUND_UP(sg_dma_len(s), sram_period);
        else
                new_nents++;

/* Create sg table for STM32 DMA channel */
ret = sg_alloc_table(&new_dma_sgt, new_nents, GFP_ATOMIC);
if (ret)
        dev_err(dev, "DMA sg table alloc failed\n");

for_each_sg(new_dma_sgt.sgl, s, new_dma_sgt.nents, i) {
        _sgl = sgl;
        sg_dma_len(s) = min(sg_dma_len(_sgl), sram_period);
        /* Targets the beginning = first half of the sram_buf */
        s->dma_address = sram_buf;
        /*
          * Targets the second half of the sram_buf
          * for odd indexes of the item of the sg_list
          */
        if (i & 1)
                s->dma_address += sram_period;
}

/* Create sg table for STM32 MDMA channel */
ret = sg_alloc_table(&new_mdma_sgt, new_nents, GFP_ATOMIC);
if (ret)
        dev_err(dev, "MDMA sg_table alloc failed\n");

_sgl = sgl;
len = sg_dma_len(sgl);
ddr_dma_buf = sg_dma_address(sgl);
for_each_sg(mdma_sgt.sgl, s, mdma_sgt.nents, i) {
        size_t bytes = min_t(size_t, len, sram_period);

        sg_dma_len(s) = bytes;
        sg_dma_address(s) = ddr_dma_buf;
        len -= bytes;

        if (!len && sg_next(_sgl)) {
                _sgl = sg_next(_sgl);
                len = sg_dma_len(_sgl);
                ddr_dma_buf = sg_dma_address(_sgl);
        } else {
                ddr_dma_buf += bytes;
        }
}

`dmaengine_prep_slave_sg()`로 descriptor를 얻은 뒤에는 새로 만든 두 `sg_table`을 반드시 해제해야 합니다.

컨트롤러별 매개변수 구성

290-333

먼저 `struct dma_slave_config`와 `dmaengine_slave_config()`로 STM32 DMA 채널을 구성합니다. 전송 방향에 따른 memory address가 SRAM 버퍼를 가리키게 하고 `(struct dma_slave_config).peripheral_size != 0`으로 설정합니다.

STM32 DMA 드라이버는 `.peripheral_size`를 확인해 체이닝 사용 여부를 판단합니다. 체이닝이면 `.peripheral_config`에 `u32` 세 개짜리 배열을 채웁니다. 순서대로 STM32 DMAMUX channel ID, channel interrupt flag clear register 주소, channel Transfer Complete flag mask입니다.

그 다음 별도의 `struct dma_slave_config`로 STM32 MDMA 채널을 구성합니다. 전송 방향에 따른 device address는 SRAM 버퍼를, memory address는 기존 "classic" DMA에서 사용하던 원본 버퍼를 가리켜야 합니다.

STM32 DMA 드라이버가 갱신한 이전 구성의 `.peripheral_size`와 `.peripheral_config`를 STM32 MDMA용 `struct dma_slave_config`의 같은 필드에 전달합니다. 아래 예제의 변수명과 필드는 원문 그대로 보존했습니다.

struct dma_slave_config dma_conf;
struct dma_slave_config mdma_conf;

memset(&dma_conf, 0, sizeof(dma_conf));
[...]
config.direction = DMA_DEV_TO_MEM;
config.dst_addr = sram_dma_buf;        // SRAM buffer
config.peripheral_size = 1;            // peripheral_size != 0 => chaining

dmaengine_slave_config(dma_chan, &dma_config);

memset(&mdma_conf, 0, sizeof(mdma_conf));
config.direction = DMA_DEV_TO_MEM;
mdma_conf.src_addr = sram_dma_buf;     // SRAM buffer
mdma_conf.dst_addr = rx_dma_buf;       // original memory buffer
mdma_conf.peripheral_size = dma_conf.peripheral_size;       // <- dma_conf
mdma_conf.peripheral_config = dma_config.peripheral_config; // <- dma_conf

dmaengine_slave_config(mdma_chan, &mdma_conf);

STM32 DMA 트랜잭션 descriptor

334-341

기존 "classic" DMA 동작과 같은 방식으로 STM32 DMA descriptor를 얻되 SRAM 버퍼를 사용하도록 입력을 바꿉니다. `dmaengine_prep_slave_sg()`에서는 원래 `sg_list`를 새 SRAM용 `sg_list`로 교체하고, `dmaengine_prep_dma_cyclic()`에서는 원래 buffer address, length, period를 새 SRAM 버퍼에 맞게 교체합니다.

STM32 MDMA 트랜잭션 descriptor

342-355

STM32 DMA descriptor를 `dmaengine_prep_slave_sg()`로 준비했다면 STM32 MDMA에도 `dmaengine_prep_slave_sg()`를 사용하고, `dmaengine_prep_dma_cyclic()`을 사용했다면 MDMA에도 같은 cyclic API를 사용합니다.

Scatter-gather 방식에서는 SRAM 버퍼를 사용하는 새 `sg_list`를 사용합니다. Cyclic 방식은 전송 방향에 따라 `DMA_DEV_TO_MEM`이면 원래 DDR 버퍼를, `DMA_MEM_TO_DEV`이면 SRAM 버퍼를 사용합니다. Source address는 앞서 `dmaengine_slave_config()`로 설정합니다.

두 트랜잭션 제출과 callback

356-368

트랜잭션을 제출하기 전에 전체 전송(`dmaengine_prep_slave_sg()`) 또는 period(`dmaengine_prep_dma_cyclic()`)가 끝날 때 어느 descriptor의 callback을 호출할지 정해야 할 수 있습니다. 전체 전송을 마지막으로 완료하는 descriptor에 callback을 둡니다.

  • `DMA_DEV_TO_MEM`: "MDMA" descriptor에 callback을 설정합니다.
  • `DMA_MEM_TO_DEV`: "DMA" descriptor에 callback을 설정합니다.

그 다음 순서와 관계없이 `dmaengine_tx_submit()`으로 두 descriptor를 제출합니다.

Pending 요청 시작과 종료 처리

369-381

STM32 MDMA 채널 전송은 STM32 DMA가 트리거하므로 STM32 DMA 채널보다 STM32 MDMA 채널을 먼저 issue해야 합니다.

Callback이 있다면 전체 전송 종료 또는 period 완료 시 호출됩니다.

두 채널 모두 종료하는 것을 잊지 마십시오. STM32 DMA 채널은 순환 Double-Buffer mode이므로 하드웨어가 비활성화하지 않아 드라이버가 종료해야 합니다. STM32 MDMA 채널은 scatter-gather 전송이면 하드웨어가 멈추지만 cyclic 전송이면 멈추지 않습니다. 전송 종류와 관계없이 명시적으로 종료해도 됩니다.

DMA_MEM_TO_DEV 특수 사례

382-398

`DMA_MEM_TO_DEV` 방향의 STM32 DMA-MDMA 체이닝은 특수 사례입니다. STM32 MDMA가 DDR 데이터를 SRAM 버퍼에 공급하고 STM32 DMA가 SRAM에서 읽기 때문에 STM32 DMA가 읽기 시작할 때 첫 period 데이터가 이미 SRAM에 있어야 합니다.

STM32 DMA 채널을 일시 정지하여 Transfer Complete 신호를 만들고 MDMA를 트리거하는 방법은 첫 DMA 읽기 데이터가 잘못될 수 있습니다. 올바른 방법은 `dmaengine_prep_dma_memcpy()`로 첫 SRAM period를 미리 준비한 뒤 이 첫 period를 scatter-gather 또는 cyclic 전송 범위에서 제거하는 것입니다.

이 복잡성 때문에 특별히 감수할 이유가 없다면 STM32 DMA-MDMA 체이닝은 `DMA_DEV_TO_MEM`에 사용하고 `DMA_MEM_TO_DEV`에는 "classic" DMA를 유지하는 편이 좋습니다.

자료와 작성자

399-415

Application note, datasheet, reference manual은 ST의 STM32MP1 웹사이트에서 제공합니다. AN5224, AN4031, AN5001은 각각 STM32 DMAMUX, STM32 DMA, STM32 MDMA를 집중적으로 다룹니다.

작성자: Amelie Delaunay <amelie.delaunay@foss.st.com>