mirror of
https://github.com/armbian/linux-cix.git
synced 2026-01-06 12:30:45 -08:00
Merge tag 'dmaengine-6.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine
Pull dmaengine updates from Vinod Koul:
"New Support:
- MT6795 SoC dma controller (AngeloGioacchino Del Regno)
- qcom-adm controller yaml binding (Christian Marangi)
- Renesas r8a779g0 dma controller yaml binding (Geert Uytterhoeven)
- Qualcomm SM6350 GPI dma controller (Luca Weiss)
Updates:
- STM32 DMA-MDMA chaining support (Amelie Delaunay)
- make hsu driver use managed resources (Andy Shevchenko)
- the usual round of idxd driver updates (Dave Jiang & Jerry
Snitselaar)
- apple dma driver iommu and pd properties and remove use
of devres for irqs (Janne Grunau & Martin Povišer)
- device_synchronize support for Xilinx zynqmp driver (Swati
Agarwal)"
* tag 'dmaengine-6.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine: (60 commits)
dmaengine: ioat: remove unused declarations in dma.h
dmaengine: ti: k3-udma: Respond TX done if DMA_PREP_INTERRUPT is not requested
dmaengine: zynqmp_dma: Add device_synchronize support
dt-bindings: dma: add additional pbus reset to qcom,adm
dt-bindings: dma: rework qcom,adm Documentation to yaml schema
dt-bindings: dma: apple,admac: Add iommus and power-domains properties
dmaengine: dw-edma: Remove runtime PM support
dmaengine: idxd: add configuration for concurrent batch descriptor processing
dmaengine: idxd: add configuration for concurrent work descriptor processing
dmaengine: idxd: add WQ operation cap restriction support
dmanegine: idxd: reformat opcap output to match bitmap_parse() input
dmaengine: idxd: convert ats_dis to a wq flag
dmaengine: ioat: stop mod_timer from resurrecting deleted timer in __cleanup()
dmaengine: qcom-adm: fix wrong calling convention for prep_slave_sg
dmaengine: qcom-adm: fix wrong sizeof config in slave_config
dmaengine: ti: k3-psil: add additional TX threads for j721e
dmaengine: ti: k3-psil: add additional TX threads for j7200
dmaengine: apple-admac: Trigger shared reset
dmaengine: apple-admac: Do not use devres for IRQs
dmaengine: ti: edma: Remove some unused functions
...
This commit is contained in:
@@ -227,6 +227,17 @@ Contact: dmaengine@vger.kernel.org
|
||||
Description: Indicate the number of retires for an enqcmds submission on a sharedwq.
|
||||
A max value to set attribute is capped at 64.
|
||||
|
||||
What: /sys/bus/dsa/devices/wq<m>.<n>/op_config
|
||||
Date: Sept 14, 2022
|
||||
KernelVersion: 6.0.0
|
||||
Contact: dmaengine@vger.kernel.org
|
||||
Description: Shows the operation capability bits displayed in bitmap format
|
||||
presented by %*pb printk() output format specifier.
|
||||
The attribute can be configured when the WQ is disabled in
|
||||
order to configure the WQ to accept specific bits that
|
||||
correlates to the operations allowed. It's visible only
|
||||
on platforms that support the capability.
|
||||
|
||||
What: /sys/bus/dsa/devices/engine<m>.<n>/group_id
|
||||
Date: Oct 25, 2019
|
||||
KernelVersion: 5.6.0
|
||||
@@ -255,3 +266,27 @@ Contact: dmaengine@vger.kernel.org
|
||||
Description: Indicates the number of Read Buffers reserved for the use of
|
||||
engines in the group. See DSA spec v1.2 9.2.18 GRPCFG Read Buffers
|
||||
Reserved.
|
||||
|
||||
What: /sys/bus/dsa/devices/group<m>.<n>/desc_progress_limit
|
||||
Date: Sept 14, 2022
|
||||
KernelVersion: 6.0.0
|
||||
Contact: dmaengine@vger.kernel.org
|
||||
Description: Allows control of the number of work descriptors that can be
|
||||
concurrently processed by an engine in the group as a fraction
|
||||
of the Maximum Work Descriptors in Progress value specified in
|
||||
the ENGCAP register. The acceptable values are 0 (default),
|
||||
1 (1/2 of max value), 2 (1/4 of the max value), and 3 (1/8 of
|
||||
the max value). It's visible only on platforms that support
|
||||
the capability.
|
||||
|
||||
What: /sys/bus/dsa/devices/group<m>.<n>/batch_progress_limit
|
||||
Date: Sept 14, 2022
|
||||
KernelVersion: 6.0.0
|
||||
Contact: dmaengine@vger.kernel.org
|
||||
Description: Allows control of the number of batch descriptors that can be
|
||||
concurrently processed by an engine in the group as a fraction
|
||||
of the Maximum Batch Descriptors in Progress value specified in
|
||||
the ENGCAP register. The acceptable values are 0 (default),
|
||||
1 (1/2 of max value), 2 (1/4 of the max value), and 3 (1/8 of
|
||||
the max value). It's visible only on platforms that support
|
||||
the capability.
|
||||
|
||||
@@ -59,6 +59,7 @@ SoC-specific documents
|
||||
stm32/stm32f429-overview
|
||||
stm32/stm32mp13-overview
|
||||
stm32/stm32mp157-overview
|
||||
stm32/stm32-dma-mdma-chaining
|
||||
|
||||
sunxi
|
||||
|
||||
|
||||
415
Documentation/arm/stm32/stm32-dma-mdma-chaining.rst
Normal file
415
Documentation/arm/stm32/stm32-dma-mdma-chaining.rst
Normal file
@@ -0,0 +1,415 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
=======================
|
||||
STM32 DMA-MDMA chaining
|
||||
=======================
|
||||
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
This document describes the STM32 DMA-MDMA chaining feature. But before going
|
||||
further, let's introduce the peripherals involved.
|
||||
|
||||
To offload data transfers from the CPU, STM32 microprocessors (MPUs) embed
|
||||
direct memory access controllers (DMA).
|
||||
|
||||
STM32MP1 SoCs embed both STM32 DMA and STM32 MDMA controllers. STM32 DMA
|
||||
request routing capabilities are enhanced by a DMA request multiplexer
|
||||
(STM32 DMAMUX).
|
||||
|
||||
**STM32 DMAMUX**
|
||||
|
||||
STM32 DMAMUX routes any DMA request from a given peripheral to any STM32 DMA
|
||||
controller (STM32MP1 counts two STM32 DMA controllers) channels.
|
||||
|
||||
**STM32 DMA**
|
||||
|
||||
STM32 DMA is mainly used to implement central data buffer storage (usually in
|
||||
the system SRAM) for different peripheral. It can access external RAMs but
|
||||
without the ability to generate convenient burst transfer ensuring the best
|
||||
load of the AXI.
|
||||
|
||||
**STM32 MDMA**
|
||||
|
||||
STM32 MDMA (Master DMA) is mainly used to manage direct data transfers between
|
||||
RAM data buffers without CPU intervention. It can also be used in a
|
||||
hierarchical structure that uses STM32 DMA as first level data buffer
|
||||
interfaces for AHB peripherals, while the STM32 MDMA acts as a second level
|
||||
DMA with better performance. As a AXI/AHB master, STM32 MDMA can take control
|
||||
of the AXI/AHB bus.
|
||||
|
||||
|
||||
Principles
|
||||
----------
|
||||
|
||||
STM32 DMA-MDMA chaining feature relies on the strengths of STM32 DMA and
|
||||
STM32 MDMA controllers.
|
||||
|
||||
STM32 DMA has a circular Double Buffer Mode (DBM). At each end of transaction
|
||||
(when DMA data counter - DMA_SxNDTR - reaches 0), the memory pointers
|
||||
(configured with DMA_SxSM0AR and DMA_SxM1AR) are swapped and the DMA data
|
||||
counter is automatically reloaded. This allows the SW or the STM32 MDMA to
|
||||
process one memory area while the second memory area is being filled/used by
|
||||
the STM32 DMA transfer.
|
||||
|
||||
With STM32 MDMA linked-list mode, a single request initiates the data array
|
||||
(collection of nodes) to be transferred until the linked-list pointer for the
|
||||
channel is null. The channel transfer complete of the last node is the end of
|
||||
transfer, unless first and last nodes are linked to each other, in such a
|
||||
case, the linked-list loops on to create a circular MDMA transfer.
|
||||
|
||||
STM32 MDMA has direct connections with STM32 DMA. This enables autonomous
|
||||
communication and synchronization between peripherals, thus saving CPU
|
||||
resources and bus congestion. Transfer Complete signal of STM32 DMA channel
|
||||
can triggers STM32 MDMA transfer. STM32 MDMA can clear the request generated
|
||||
by the STM32 DMA by writing to its Interrupt Clear register (whose address is
|
||||
stored in MDMA_CxMAR, and bit mask in MDMA_CxMDR).
|
||||
|
||||
.. table:: STM32 MDMA interconnect table with STM32 DMA
|
||||
|
||||
+--------------+----------------+-----------+------------+
|
||||
| STM32 DMAMUX | STM32 DMA | STM32 DMA | STM32 MDMA |
|
||||
| channels | channels | Transfer | request |
|
||||
| | | complete | |
|
||||
| | | signal | |
|
||||
+==============+================+===========+============+
|
||||
| Channel *0* | DMA1 channel 0 | dma1_tcf0 | *0x00* |
|
||||
+--------------+----------------+-----------+------------+
|
||||
| Channel *1* | DMA1 channel 1 | dma1_tcf1 | *0x01* |
|
||||
+--------------+----------------+-----------+------------+
|
||||
| Channel *2* | DMA1 channel 2 | dma1_tcf2 | *0x02* |
|
||||
+--------------+----------------+-----------+------------+
|
||||
| Channel *3* | DMA1 channel 3 | dma1_tcf3 | *0x03* |
|
||||
+--------------+----------------+-----------+------------+
|
||||
| Channel *4* | DMA1 channel 4 | dma1_tcf4 | *0x04* |
|
||||
+--------------+----------------+-----------+------------+
|
||||
| Channel *5* | DMA1 channel 5 | dma1_tcf5 | *0x05* |
|
||||
+--------------+----------------+-----------+------------+
|
||||
| Channel *6* | DMA1 channel 6 | dma1_tcf6 | *0x06* |
|
||||
+--------------+----------------+-----------+------------+
|
||||
| Channel *7* | DMA1 channel 7 | dma1_tcf7 | *0x07* |
|
||||
+--------------+----------------+-----------+------------+
|
||||
| Channel *8* | DMA2 channel 0 | dma2_tcf0 | *0x08* |
|
||||
+--------------+----------------+-----------+------------+
|
||||
| Channel *9* | DMA2 channel 1 | dma2_tcf1 | *0x09* |
|
||||
+--------------+----------------+-----------+------------+
|
||||
| Channel *10* | DMA2 channel 2 | dma2_tcf2 | *0x0A* |
|
||||
+--------------+----------------+-----------+------------+
|
||||
| Channel *11* | DMA2 channel 3 | dma2_tcf3 | *0x0B* |
|
||||
+--------------+----------------+-----------+------------+
|
||||
| Channel *12* | DMA2 channel 4 | dma2_tcf4 | *0x0C* |
|
||||
+--------------+----------------+-----------+------------+
|
||||
| Channel *13* | DMA2 channel 5 | dma2_tcf5 | *0x0D* |
|
||||
+--------------+----------------+-----------+------------+
|
||||
| Channel *14* | DMA2 channel 6 | dma2_tcf6 | *0x0E* |
|
||||
+--------------+----------------+-----------+------------+
|
||||
| Channel *15* | DMA2 channel 7 | dma2_tcf7 | *0x0F* |
|
||||
+--------------+----------------+-----------+------------+
|
||||
|
||||
STM32 DMA-MDMA chaining feature then uses a SRAM buffer. STM32MP1 SoCs embed
|
||||
three fast access static internal RAMs of various size, used for data storage.
|
||||
Due to STM32 DMA legacy (within microcontrollers), STM32 DMA performances are
|
||||
bad with DDR, while they are optimal with SRAM. Hence the SRAM buffer used
|
||||
between STM32 DMA and STM32 MDMA. This buffer is split in two equal periods
|
||||
and STM32 DMA uses one period while STM32 MDMA uses the other period
|
||||
simultaneously.
|
||||
::
|
||||
|
||||
dma[1:2]-tcf[0:7]
|
||||
.----------------.
|
||||
____________ ' _________ V____________
|
||||
| STM32 DMA | / __|>_ \ | STM32 MDMA |
|
||||
|------------| | / \ | |------------|
|
||||
| DMA_SxM0AR |<=>| | SRAM | |<=>| []-[]...[] |
|
||||
| DMA_SxM1AR | | \_____/ | | |
|
||||
|____________| \___<|____/ |____________|
|
||||
|
||||
STM32 DMA-MDMA chaining uses (struct dma_slave_config).peripheral_config to
|
||||
exchange the parameters needed to configure MDMA. These parameters are
|
||||
gathered into a u32 array with three values:
|
||||
|
||||
* the STM32 MDMA request (which is actually the DMAMUX channel ID),
|
||||
* the address of the STM32 DMA register to clear the Transfer Complete
|
||||
interrupt flag,
|
||||
* the mask of the Transfer Complete interrupt flag of the STM32 DMA channel.
|
||||
|
||||
Device Tree updates for STM32 DMA-MDMA chaining support
|
||||
-------------------------------------------------------
|
||||
|
||||
**1. Allocate a SRAM buffer**
|
||||
|
||||
SRAM device tree node is defined in SoC device tree. You can refer to it in
|
||||
your board device tree to define your SRAM pool.
|
||||
::
|
||||
|
||||
&sram {
|
||||
my_foo_device_dma_pool: dma-sram@0 {
|
||||
reg = <0x0 0x1000>;
|
||||
};
|
||||
};
|
||||
|
||||
Be careful of the start index, in case there are other SRAM consumers.
|
||||
Define your pool size strategically: to optimise chaining, the idea is that
|
||||
STM32 DMA and STM32 MDMA can work simultaneously, on each buffer of the
|
||||
SRAM.
|
||||
If the SRAM period is greater than the expected DMA transfer, then STM32 DMA
|
||||
and STM32 MDMA will work sequentially instead of simultaneously. It is not a
|
||||
functional issue but it is not optimal.
|
||||
|
||||
Don't forget to refer to your SRAM pool in your device node. You need to
|
||||
define a new property.
|
||||
::
|
||||
|
||||
&my_foo_device {
|
||||
...
|
||||
my_dma_pool = &my_foo_device_dma_pool;
|
||||
};
|
||||
|
||||
Then get this SRAM pool in your foo driver and allocate your SRAM buffer.
|
||||
|
||||
**2. Allocate a STM32 DMA channel and a STM32 MDMA channel**
|
||||
|
||||
You need to define an extra channel in your device tree node, in addition to
|
||||
the one you should already have for "classic" DMA operation.
|
||||
|
||||
This new channel must be taken from STM32 MDMA channels, so, the phandle of
|
||||
the DMA controller to use is the MDMA controller's one.
|
||||
::
|
||||
|
||||
&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
|
||||
};
|
||||
|
||||
Concerning STM32 MDMA bindings:
|
||||
|
||||
1. The request line number : whatever the value here, it will be overwritten
|
||||
by MDMA driver with the STM32 DMAMUX channel ID passed through
|
||||
(struct dma_slave_config).peripheral_config
|
||||
|
||||
2. The priority level : choose Very High (0x3) so that your channel will
|
||||
take priority other the other during request arbitration
|
||||
|
||||
3. A 32bit mask specifying the DMA channel configuration : source and
|
||||
destination address increment, block transfer with 128 bytes per single
|
||||
transfer
|
||||
|
||||
4. The 32bit value specifying the register to be used to acknowledge the
|
||||
request: it will be overwritten by MDMA driver, with the DMA channel
|
||||
interrupt flag clear register address passed through
|
||||
(struct dma_slave_config).peripheral_config
|
||||
|
||||
5. The 32bit mask specifying the value to be written to acknowledge the
|
||||
request: it will be overwritten by MDMA driver, with the DMA channel
|
||||
Transfer Complete flag passed through
|
||||
(struct dma_slave_config).peripheral_config
|
||||
|
||||
Driver updates for STM32 DMA-MDMA chaining support in foo driver
|
||||
----------------------------------------------------------------
|
||||
|
||||
**0. (optional) Refactor the original sg_table if dmaengine_prep_slave_sg()**
|
||||
|
||||
In case of dmaengine_prep_slave_sg(), the original sg_table can't be used as
|
||||
is. Two new sg_tables must be created from the original one. One for
|
||||
STM32 DMA transfer (where memory address targets now the SRAM buffer instead
|
||||
of DDR buffer) and one for STM32 MDMA transfer (where memory address targets
|
||||
the DDR buffer).
|
||||
|
||||
The new sg_list items must fit SRAM period length. Here is an example for
|
||||
DMA_DEV_TO_MEM:
|
||||
::
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
Don't forget to release these new sg_tables after getting the descriptors
|
||||
with dmaengine_prep_slave_sg().
|
||||
|
||||
**1. Set controller specific parameters**
|
||||
|
||||
First, use dmaengine_slave_config() with a struct dma_slave_config to
|
||||
configure STM32 DMA channel. You just have to take care of DMA addresses,
|
||||
the memory address (depending on the transfer direction) must point on your
|
||||
SRAM buffer, and set (struct dma_slave_config).peripheral_size != 0.
|
||||
|
||||
STM32 DMA driver will check (struct dma_slave_config).peripheral_size to
|
||||
determine if chaining is being used or not. If it is used, then STM32 DMA
|
||||
driver fills (struct dma_slave_config).peripheral_config with an array of
|
||||
three u32 : the first one containing STM32 DMAMUX channel ID, the second one
|
||||
the channel interrupt flag clear register address, and the third one the
|
||||
channel Transfer Complete flag mask.
|
||||
|
||||
Then, use dmaengine_slave_config with another struct dma_slave_config to
|
||||
configure STM32 MDMA channel. Take care of DMA addresses, the device address
|
||||
(depending on the transfer direction) must point on your SRAM buffer, and
|
||||
the memory address must point to the buffer originally used for "classic"
|
||||
DMA operation. Use the previous (struct dma_slave_config).peripheral_size
|
||||
and .peripheral_config that have been updated by STM32 DMA driver, to set
|
||||
(struct dma_slave_config).peripheral_size and .peripheral_config of the
|
||||
struct dma_slave_config to configure STM32 MDMA channel.
|
||||
::
|
||||
|
||||
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);
|
||||
|
||||
**2. Get a descriptor for STM32 DMA channel transaction**
|
||||
|
||||
In the same way you get your descriptor for your "classic" DMA operation,
|
||||
you just have to replace the original sg_list (in case of
|
||||
dmaengine_prep_slave_sg()) with the new sg_list using SRAM buffer, or to
|
||||
replace the original buffer address, length and period (in case of
|
||||
dmaengine_prep_dma_cyclic()) with the new SRAM buffer.
|
||||
|
||||
**3. Get a descriptor for STM32 MDMA channel transaction**
|
||||
|
||||
If you previously get descriptor (for STM32 DMA) with
|
||||
|
||||
* dmaengine_prep_slave_sg(), then use dmaengine_prep_slave_sg() for
|
||||
STM32 MDMA;
|
||||
* dmaengine_prep_dma_cyclic(), then use dmaengine_prep_dma_cyclic() for
|
||||
STM32 MDMA.
|
||||
|
||||
Use the new sg_list using SRAM buffer (in case of dmaengine_prep_slave_sg())
|
||||
or, depending on the transfer direction, either the original DDR buffer (in
|
||||
case of DMA_DEV_TO_MEM) or the SRAM buffer (in case of DMA_MEM_TO_DEV), the
|
||||
source address being previously set with dmaengine_slave_config().
|
||||
|
||||
**4. Submit both transactions**
|
||||
|
||||
Before submitting your transactions, you may need to define on which
|
||||
descriptor you want a callback to be called at the end of the transfer
|
||||
(dmaengine_prep_slave_sg()) or the period (dmaengine_prep_dma_cyclic()).
|
||||
Depending on the direction, set the callback on the descriptor that finishes
|
||||
the overal transfer:
|
||||
|
||||
* DMA_DEV_TO_MEM: set the callback on the "MDMA" descriptor
|
||||
* DMA_MEM_TO_DEV: set the callback on the "DMA" descriptor
|
||||
|
||||
Then, submit the descriptors whatever the order, with dmaengine_tx_submit().
|
||||
|
||||
**5. Issue pending requests (and wait for callback notification)**
|
||||
|
||||
As STM32 MDMA channel transfer is triggered by STM32 DMA, you must issue
|
||||
STM32 MDMA channel before STM32 DMA channel.
|
||||
|
||||
If any, your callback will be called to warn you about the end of the overal
|
||||
transfer or the period completion.
|
||||
|
||||
Don't forget to terminate both channels. STM32 DMA channel is configured in
|
||||
cyclic Double-Buffer mode so it won't be disabled by HW, you need to terminate
|
||||
it. STM32 MDMA channel will be stopped by HW in case of sg transfer, but not
|
||||
in case of cyclic transfer. You can terminate it whatever the kind of transfer.
|
||||
|
||||
**STM32 DMA-MDMA chaining DMA_MEM_TO_DEV special case**
|
||||
|
||||
STM32 DMA-MDMA chaining in DMA_MEM_TO_DEV is a special case. Indeed, the
|
||||
STM32 MDMA feeds the SRAM buffer with the DDR data, and the STM32 DMA reads
|
||||
data from SRAM buffer. So some data (the first period) have to be copied in
|
||||
SRAM buffer when the STM32 DMA starts to read.
|
||||
|
||||
A trick could be pausing the STM32 DMA channel (that will raise a Transfer
|
||||
Complete signal, triggering the STM32 MDMA channel), but the first data read
|
||||
by the STM32 DMA could be "wrong". The proper way is to prepare the first SRAM
|
||||
period with dmaengine_prep_dma_memcpy(). Then this first period should be
|
||||
"removed" from the sg or the cyclic transfer.
|
||||
|
||||
Due to this complexity, rather use the STM32 DMA-MDMA chaining for
|
||||
DMA_DEV_TO_MEM and keep the "classic" DMA usage for DMA_MEM_TO_DEV, unless
|
||||
you're not afraid.
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
Application note, datasheet and reference manual are available on ST website
|
||||
(STM32MP1_).
|
||||
|
||||
Dedicated focus on three application notes (AN5224_, AN4031_ & AN5001_)
|
||||
dealing with STM32 DMAMUX, STM32 DMA and STM32 MDMA.
|
||||
|
||||
.. _STM32MP1: https://www.st.com/en/microcontrollers-microprocessors/stm32mp1-series.html
|
||||
.. _AN5224: https://www.st.com/resource/en/application_note/an5224-stm32-dmamux-the-dma-request-router-stmicroelectronics.pdf
|
||||
.. _AN4031: https://www.st.com/resource/en/application_note/dm00046011-using-the-stm32f2-stm32f4-and-stm32f7-series-dma-controller-stmicroelectronics.pdf
|
||||
.. _AN5001: https://www.st.com/resource/en/application_note/an5001-stm32cube-expansion-package-for-stm32h7-series-mdma-stmicroelectronics.pdf
|
||||
|
||||
:Authors:
|
||||
|
||||
- Amelie Delaunay <amelie.delaunay@foss.st.com>
|
||||
@@ -49,6 +49,13 @@ properties:
|
||||
in an interrupts-extended list the disconnected positions will contain
|
||||
an empty phandle reference <0>.
|
||||
|
||||
iommus:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
@@ -55,6 +55,12 @@ properties:
|
||||
|
||||
dma-coherent: true
|
||||
|
||||
iommus:
|
||||
minItems: 1
|
||||
maxItems: 9
|
||||
description: Up to 1 IOMMU entry per DMA channel for writes and 1
|
||||
IOMMU entry for reads.
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ properties:
|
||||
- items:
|
||||
- enum:
|
||||
- mediatek,mt2712-uart-dma
|
||||
- mediatek,mt6795-uart-dma
|
||||
- mediatek,mt8365-uart-dma
|
||||
- mediatek,mt8516-uart-dma
|
||||
- const: mediatek,mt6577-uart-dma
|
||||
|
||||
99
Documentation/devicetree/bindings/dma/qcom,adm.yaml
Normal file
99
Documentation/devicetree/bindings/dma/qcom,adm.yaml
Normal file
@@ -0,0 +1,99 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/dma/qcom,adm.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm ADM DMA Controller
|
||||
|
||||
maintainers:
|
||||
- Christian Marangi <ansuelsmth@gmail.com>
|
||||
- Bjorn Andersson <bjorn.andersson@linaro.org>
|
||||
|
||||
description: |
|
||||
QCOM ADM DMA controller provides DMA capabilities for
|
||||
peripheral buses such as NAND and SPI.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,adm
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
"#dma-cells":
|
||||
const: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: phandle to the core clock
|
||||
- description: phandle to the iface clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: core
|
||||
- const: iface
|
||||
|
||||
resets:
|
||||
items:
|
||||
- description: phandle to the clk reset
|
||||
- description: phandle to the pbus reset
|
||||
- description: phandle to the c0 reset
|
||||
- description: phandle to the c1 reset
|
||||
- description: phandle to the c2 reset
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: clk
|
||||
- const: pbus
|
||||
- const: c0
|
||||
- const: c1
|
||||
- const: c2
|
||||
|
||||
qcom,ee:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: indicates the security domain identifier used in the secure world.
|
||||
minimum: 0
|
||||
maximum: 255
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- "#dma-cells"
|
||||
- clocks
|
||||
- clock-names
|
||||
- resets
|
||||
- reset-names
|
||||
- qcom,ee
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/qcom,gcc-ipq806x.h>
|
||||
#include <dt-bindings/reset/qcom,gcc-ipq806x.h>
|
||||
|
||||
adm_dma: dma-controller@18300000 {
|
||||
compatible = "qcom,adm";
|
||||
reg = <0x18300000 0x100000>;
|
||||
interrupts = <0 170 0>;
|
||||
#dma-cells = <1>;
|
||||
|
||||
clocks = <&gcc ADM0_CLK>,
|
||||
<&gcc ADM0_PBUS_CLK>;
|
||||
clock-names = "core", "iface";
|
||||
|
||||
resets = <&gcc ADM0_RESET>,
|
||||
<&gcc ADM0_PBUS_RESET>,
|
||||
<&gcc ADM0_C0_RESET>,
|
||||
<&gcc ADM0_C1_RESET>,
|
||||
<&gcc ADM0_C2_RESET>;
|
||||
reset-names = "clk", "pbus", "c0", "c1", "c2";
|
||||
qcom,ee = <0>;
|
||||
};
|
||||
|
||||
...
|
||||
@@ -8,7 +8,7 @@ title: Qualcomm Technologies Inc BAM DMA controller
|
||||
|
||||
maintainers:
|
||||
- Andy Gross <agross@kernel.org>
|
||||
- Bjorn Andersson <bjorn.andersson@linaro.org>
|
||||
- Bjorn Andersson <andersson@kernel.org>
|
||||
|
||||
allOf:
|
||||
- $ref: "dma-controller.yaml#"
|
||||
@@ -20,7 +20,7 @@ properties:
|
||||
- qcom,bam-v1.3.0
|
||||
# MSM8974, APQ8074 and APQ8084
|
||||
- qcom,bam-v1.4.0
|
||||
# MSM8916
|
||||
# MSM8916 and SDM845
|
||||
- qcom,bam-v1.7.0
|
||||
|
||||
clocks:
|
||||
@@ -90,8 +90,8 @@ examples:
|
||||
|
||||
dma-controller@f9944000 {
|
||||
compatible = "qcom,bam-v1.4.0";
|
||||
reg = <0xf9944000 0x15000>;
|
||||
interrupts = <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>;
|
||||
reg = <0xf9944000 0x19000>;
|
||||
interrupts = <GIC_SPI 239 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&gcc GCC_BLSP2_AHB_CLK>;
|
||||
clock-names = "bam_clk";
|
||||
#dma-cells = <1>;
|
||||
|
||||
@@ -21,6 +21,7 @@ properties:
|
||||
enum:
|
||||
- qcom,sc7280-gpi-dma
|
||||
- qcom,sdm845-gpi-dma
|
||||
- qcom,sm6350-gpi-dma
|
||||
- qcom,sm8150-gpi-dma
|
||||
- qcom,sm8250-gpi-dma
|
||||
- qcom,sm8350-gpi-dma
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
QCOM ADM DMA Controller
|
||||
|
||||
Required properties:
|
||||
- compatible: must contain "qcom,adm" for IPQ/APQ8064 and MSM8960
|
||||
- reg: Address range for DMA registers
|
||||
- interrupts: Should contain one interrupt shared by all channels
|
||||
- #dma-cells: must be <2>. First cell denotes the channel number. Second cell
|
||||
denotes CRCI (client rate control interface) flow control assignment.
|
||||
- clocks: Should contain the core clock and interface clock.
|
||||
- clock-names: Must contain "core" for the core clock and "iface" for the
|
||||
interface clock.
|
||||
- resets: Must contain an entry for each entry in reset names.
|
||||
- reset-names: Must include the following entries:
|
||||
- clk
|
||||
- c0
|
||||
- c1
|
||||
- c2
|
||||
- qcom,ee: indicates the security domain identifier used in the secure world.
|
||||
|
||||
Example:
|
||||
adm_dma: dma@18300000 {
|
||||
compatible = "qcom,adm";
|
||||
reg = <0x18300000 0x100000>;
|
||||
interrupts = <0 170 0>;
|
||||
#dma-cells = <2>;
|
||||
|
||||
clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>;
|
||||
clock-names = "core", "iface";
|
||||
|
||||
resets = <&gcc ADM0_RESET>,
|
||||
<&gcc ADM0_C0_RESET>,
|
||||
<&gcc ADM0_C1_RESET>,
|
||||
<&gcc ADM0_C2_RESET>;
|
||||
reset-names = "clk", "c0", "c1", "c2";
|
||||
qcom,ee = <0>;
|
||||
};
|
||||
|
||||
DMA clients must use the format descripted in the dma.txt file, using a three
|
||||
cell specifier for each channel.
|
||||
|
||||
Each dmas request consists of 3 cells:
|
||||
1. phandle pointing to the DMA controller
|
||||
2. channel number
|
||||
3. CRCI assignment, if applicable. If no CRCI flow control is required, use 0.
|
||||
The CRCI is used for flow control. It identifies the peripheral device that
|
||||
is the source/destination for the transferred data.
|
||||
|
||||
Example:
|
||||
|
||||
spi4: spi@1a280000 {
|
||||
spi-max-frequency = <50000000>;
|
||||
|
||||
pinctrl-0 = <&spi_pins>;
|
||||
pinctrl-names = "default";
|
||||
|
||||
cs-gpios = <&qcom_pinmux 20 0>;
|
||||
|
||||
dmas = <&adm_dma 6 9>,
|
||||
<&adm_dma 5 10>;
|
||||
dma-names = "rx", "tx";
|
||||
};
|
||||
@@ -45,6 +45,7 @@ properties:
|
||||
- enum:
|
||||
- renesas,dmac-r8a779a0 # R-Car V3U
|
||||
- renesas,dmac-r8a779f0 # R-Car S4-8
|
||||
- renesas,dmac-r8a779g0 # R-Car V4H
|
||||
- const: renesas,rcar-gen4-dmac # R-Car Gen4
|
||||
|
||||
reg: true
|
||||
|
||||
@@ -4,7 +4,7 @@ Required properties:
|
||||
- compatible: "ti,dra7-dma-crossbar" for DRA7xx DMA crossbar
|
||||
"ti,am335x-edma-crossbar" for AM335x and AM437x
|
||||
- reg: Memory map for accessing module
|
||||
- #dma-cells: Should be set to to match with the DMA controller's dma-cells
|
||||
- #dma-cells: Should be set to match with the DMA controller's dma-cells
|
||||
for ti,dra7-dma-crossbar and <3> for ti,am335x-edma-crossbar.
|
||||
- dma-requests: Number of DMA requests the crossbar can receive
|
||||
- dma-masters: phandle pointing to the DMA controller
|
||||
|
||||
@@ -9157,6 +9157,7 @@ F: net/dsa/tag_hellcreek.c
|
||||
|
||||
HISILICON DMA DRIVER
|
||||
M: Zhou Wang <wangzhou1@hisilicon.com>
|
||||
M: Jie Hai <haijie1@hisilicon.com>
|
||||
L: dmaengine@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/dma/hisi_dma.c
|
||||
|
||||
@@ -180,7 +180,7 @@ config DMA_SUN6I
|
||||
|
||||
config DW_AXI_DMAC
|
||||
tristate "Synopsys DesignWare AXI DMA support"
|
||||
depends on OF || COMPILE_TEST
|
||||
depends on OF
|
||||
depends on HAS_IOMEM
|
||||
select DMA_ENGINE
|
||||
select DMA_VIRTUAL_CHANNELS
|
||||
|
||||
@@ -2367,7 +2367,7 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
|
||||
INIT_LIST_HEAD(&dmadev->channels);
|
||||
|
||||
/*
|
||||
* Register as many many memcpy as we have physical channels,
|
||||
* Register as many memcpy as we have physical channels,
|
||||
* we won't always be able to use all but the code will have
|
||||
* to cope with that situation.
|
||||
*/
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_dma.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include "dmaengine.h"
|
||||
|
||||
@@ -95,7 +96,9 @@ struct admac_data {
|
||||
struct dma_device dma;
|
||||
struct device *dev;
|
||||
__iomem void *base;
|
||||
struct reset_control *rstc;
|
||||
|
||||
int irq;
|
||||
int irq_index;
|
||||
int nchannels;
|
||||
struct admac_chan channels[];
|
||||
@@ -724,18 +727,17 @@ static int admac_probe(struct platform_device *pdev)
|
||||
|
||||
if (irq < 0)
|
||||
return dev_err_probe(&pdev->dev, irq, "no usable interrupt\n");
|
||||
|
||||
err = devm_request_irq(&pdev->dev, irq, admac_interrupt,
|
||||
0, dev_name(&pdev->dev), ad);
|
||||
if (err)
|
||||
return dev_err_probe(&pdev->dev, err,
|
||||
"unable to register interrupt\n");
|
||||
ad->irq = irq;
|
||||
|
||||
ad->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(ad->base))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(ad->base),
|
||||
"unable to obtain MMIO resource\n");
|
||||
|
||||
ad->rstc = devm_reset_control_get_optional_shared(&pdev->dev, NULL);
|
||||
if (IS_ERR(ad->rstc))
|
||||
return PTR_ERR(ad->rstc);
|
||||
|
||||
dma = &ad->dma;
|
||||
|
||||
dma_cap_set(DMA_PRIVATE, dma->cap_mask);
|
||||
@@ -774,17 +776,38 @@ static int admac_probe(struct platform_device *pdev)
|
||||
tasklet_setup(&adchan->tasklet, admac_chan_tasklet);
|
||||
}
|
||||
|
||||
err = dma_async_device_register(&ad->dma);
|
||||
err = reset_control_reset(ad->rstc);
|
||||
if (err)
|
||||
return dev_err_probe(&pdev->dev, err, "failed to register DMA device\n");
|
||||
return dev_err_probe(&pdev->dev, err,
|
||||
"unable to trigger reset\n");
|
||||
|
||||
err = request_irq(irq, admac_interrupt, 0, dev_name(&pdev->dev), ad);
|
||||
if (err) {
|
||||
dev_err_probe(&pdev->dev, err,
|
||||
"unable to register interrupt\n");
|
||||
goto free_reset;
|
||||
}
|
||||
|
||||
err = dma_async_device_register(&ad->dma);
|
||||
if (err) {
|
||||
dev_err_probe(&pdev->dev, err, "failed to register DMA device\n");
|
||||
goto free_irq;
|
||||
}
|
||||
|
||||
err = of_dma_controller_register(pdev->dev.of_node, admac_dma_of_xlate, ad);
|
||||
if (err) {
|
||||
dma_async_device_unregister(&ad->dma);
|
||||
return dev_err_probe(&pdev->dev, err, "failed to register with OF\n");
|
||||
dev_err_probe(&pdev->dev, err, "failed to register with OF\n");
|
||||
goto free_irq;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
free_irq:
|
||||
free_irq(ad->irq, ad);
|
||||
free_reset:
|
||||
reset_control_rearm(ad->rstc);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int admac_remove(struct platform_device *pdev)
|
||||
@@ -793,6 +816,8 @@ static int admac_remove(struct platform_device *pdev)
|
||||
|
||||
of_dma_controller_free(pdev->dev.of_node);
|
||||
dma_async_device_unregister(&ad->dma);
|
||||
free_irq(ad->irq, ad);
|
||||
reset_control_rearm(ad->rstc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1470,10 +1470,7 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
|
||||
bool initd;
|
||||
|
||||
ret = dma_cookie_status(chan, cookie, txstate);
|
||||
if (ret == DMA_COMPLETE)
|
||||
return ret;
|
||||
|
||||
if (!txstate)
|
||||
if (ret == DMA_COMPLETE || !txstate)
|
||||
return ret;
|
||||
|
||||
spin_lock_irqsave(&atchan->lock, flags);
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
@@ -682,15 +681,12 @@ static int dw_edma_alloc_chan_resources(struct dma_chan *dchan)
|
||||
if (chan->status != EDMA_ST_IDLE)
|
||||
return -EBUSY;
|
||||
|
||||
pm_runtime_get(chan->dw->chip->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dw_edma_free_chan_resources(struct dma_chan *dchan)
|
||||
{
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(5000);
|
||||
struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
|
||||
int ret;
|
||||
|
||||
while (time_before(jiffies, timeout)) {
|
||||
@@ -703,8 +699,6 @@ static void dw_edma_free_chan_resources(struct dma_chan *dchan)
|
||||
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
pm_runtime_put(chan->dw->chip->dev);
|
||||
}
|
||||
|
||||
static int dw_edma_channel_setup(struct dw_edma *dw, bool write,
|
||||
@@ -977,9 +971,6 @@ int dw_edma_probe(struct dw_edma_chip *chip)
|
||||
if (err)
|
||||
goto err_irq_free;
|
||||
|
||||
/* Power management */
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
/* Turn debugfs on */
|
||||
dw_edma_v0_core_debugfs_on(dw);
|
||||
|
||||
@@ -1009,9 +1000,6 @@ int dw_edma_remove(struct dw_edma_chip *chip)
|
||||
for (i = (dw->nr_irqs - 1); i >= 0; i--)
|
||||
free_irq(chip->ops->irq_vector(dev, i), &dw->irq[i]);
|
||||
|
||||
/* Power management */
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
/* Deregister eDMA device */
|
||||
dma_async_device_unregister(&dw->wr_edma);
|
||||
list_for_each_entry_safe(chan, _chan, &dw->wr_edma.channels,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,12 +16,20 @@
|
||||
* port 3, and so on.
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/percpu-defs.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include "hsu.h"
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user