DMA Slave Configuration¶
DMA slave configuration describes how a DMA channel accesses a peripheral endpoint.
Unlike memory-to-memory DMA, slave DMA transfers involve a peripheral such as a UART FIFO, SPI FIFO, I2S data register, or another device-side interface. The DMA controller therefore needs information about the peripheral address, transfer width, burst behavior, and transfer direction in addition to the memory-side transaction information.
Linux DMA Engine separates these responsibilities:
struct dma_slave_configdescribes the peripheral-side configuration of a DMA channel.- DMA preparation APIs describe an individual transaction using that channel.
- The DMA controller driver combines both when preparing controller-specific hardware state.
This separation allows a peripheral configuration to remain associated with a DMA channel while multiple DMA transactions are prepared over its lifetime.
Slave DMA Configuration Model¶
A slave DMA transfer combines channel configuration with transaction-specific information.
The two inputs describe different parts of the transfer:
| Information | Purpose | Typical Source |
|---|---|---|
| Peripheral address | Identifies the peripheral FIFO or register endpoint | struct dma_slave_config |
| Peripheral transfer width | Defines the peripheral-side transfer beat width | struct dma_slave_config |
| Peripheral burst size | Defines the requested peripheral-side burst behavior | struct dma_slave_config |
| Memory DMA address | Identifies the memory-side endpoint | DMA preparation API |
| Transfer length | Defines the amount of data to transfer | DMA preparation API |
| Direction | Selects memory-to-device or device-to-memory behavior | DMA preparation API |
Conceptually:
Peripheral Configuration
+
Transaction Information
│
▼
DMA Controller Driver
│
▼
Controller-Specific Hardware Descriptor
The DMA controller driver translates the generic DMA Engine configuration into the format required by its hardware.
DMA Slave¶
In DMA Engine terminology, a slave transfer is a DMA transaction between memory and a peripheral endpoint.
Typical examples include:
UART RX FIFO ──DMA──> Memory
Memory ──DMA──> UART TX FIFO
SPI RX FIFO ──DMA──> Memory
Memory ──DMA──> SPI TX FIFO
The peripheral driver is the DMA client.
The DMA controller driver is the DMA provider.
The DMA Engine framework provides the common interface between them.
This gives the following responsibility boundary:
Peripheral Driver
│
│ DMA Engine Client API
▼
DMA Engine Framework
│
│ Controller Operations
▼
DMA Controller Driver
│
▼
DMA Hardware
The peripheral driver understands the peripheral interface.
The DMA controller driver understands how to program the DMA hardware.
struct dma_slave_config¶
struct dma_slave_config carries the generic configuration required for slave DMA operation.
Important fields include:
| Field | Purpose |
|---|---|
src_addr |
Peripheral source address used when the device is the source |
dst_addr |
Peripheral destination address used when the device is the destination |
src_addr_width |
Transfer width of the peripheral source |
dst_addr_width |
Transfer width of the peripheral destination |
src_maxburst |
Maximum requested source-side burst |
dst_maxburst |
Maximum requested destination-side burst |
device_fc |
Indicates whether the peripheral performs flow control |
Not every DMA client needs to configure every field.
The required fields depend on:
- Transfer direction.
- Peripheral interface requirements.
- DMA controller capabilities.
- Platform integration.
Peripheral Address¶
The src_addr and dst_addr fields identify the peripheral endpoint used by DMA.
Typical endpoints include:
- UART RX or TX FIFO.
- SPI data FIFO.
- I2S data register.
- ADC data register.
- Other DMA-accessible peripheral registers.
For a memory-to-device transfer:
For a device-to-memory transfer:
The address describes the peripheral endpoint, not the memory buffer.
Peripheral Address and CPU MMIO Address¶
A peripheral driver may maintain multiple address representations for the same hardware resource.
For example:
Peripheral Resource
│
├───────────────┐
│ │
▼ ▼
CPU MMIO Mapping Peripheral Resource Address
│ │
▼ ▼
readl()/writel() DMA Slave Configuration
The CPU normally accesses MMIO registers through an ioremap()-derived virtual address.
DMA hardware does not use that CPU virtual address.
A DMA controller may have a different address view because of:
- Bus address translation.
- DMA address translation.
- Interconnect topology.
- Platform-specific DMA mappings.
Therefore, a CPU MMIO virtual address must not be treated as a DMA peripheral address simply because both refer to the same hardware register.
The exact address translation depends on the platform and DMA controller.
Transfer Direction¶
The DMA transfer direction determines which side of struct dma_slave_config represents the peripheral endpoint.
Memory to Device¶
For:
the peripheral is the destination.
The relevant configuration is normally:
Device to Memory¶
For:
the peripheral is the source.
The relevant configuration is normally:
This can be summarized as:
| Direction | Peripheral Role | Relevant Configuration |
|---|---|---|
DMA_MEM_TO_DEV |
Destination | dst_* |
DMA_DEV_TO_MEM |
Source | src_* |
The DMA channel name itself does not define the transaction direction.
A controller or client implementation may use the same DMA channel in more than one direction when the hardware supports that usage.
Address Width¶
The address width fields describe the width of each peripheral-side DMA transfer beat.
For example:
means that the peripheral destination is accessed using a 4-byte transfer width.
It does not mean:
- The DMA address is 32 bits wide.
- The DMA buffer contains only 4 bytes.
- The complete transaction transfers only one word.
Conceptually:
The DMA controller driver translates this generic width into the encoding required by the hardware.
Burst Configuration¶
The maximum burst fields describe the requested burst behavior on the peripheral side.
The client expresses its peripheral requirements through the generic DMA Engine configuration.
The DMA controller driver then determines how those requirements map to controller-specific burst settings.
Conceptually:
Peripheral Driver
│
│ requested maxburst
▼
struct dma_slave_config
│
▼
DMA Controller Driver
│
│ controller-specific encoding
▼
DMA Hardware
The requested burst size must still be compatible with the DMA controller and peripheral.
Peripheral Flow Control¶
The device_fc field describes whether the peripheral performs flow control for the DMA transfer.
Its relevance depends on the DMA controller and peripheral interface.
Not all DMA clients or controllers require explicit use of this field.
As with burst configuration, the client provides generic intent while the controller driver determines how that intent maps to hardware behavior.
dmaengine_slave_config()¶
The DMA client applies slave configuration with:
Conceptually:
DMA Client
│
│ dmaengine_slave_config()
▼
struct dma_chan
│
▼
struct dma_device
│
│ device_config()
▼
DMA Controller Driver
The generic API dispatches the configuration to the DMA controller driver's device_config() operation.
Calling dmaengine_slave_config() does not imply that a complete DMA transaction has been created or started.
The controller driver may validate and save the configuration for later use.
Slave Configuration vs DMA Descriptor¶
Slave configuration and DMA descriptors have different lifetimes and responsibilities.
Slave Configuration¶
Slave configuration describes the peripheral side of the DMA channel.
Examples:
Peripheral FIFO address
Peripheral transfer width
Peripheral burst configuration
Peripheral flow-control behavior
DMA Descriptor¶
A DMA descriptor describes a particular transaction.
Examples:
Therefore:
dma_slave_config
=
Peripheral-Side Channel Configuration
DMA Descriptor
=
Individual DMA Transaction
A descriptor can use previously configured channel state instead of repeating the peripheral configuration for every transaction.
Example: SPI TX¶
A simplified SPI TX configuration may look like:
struct dma_slave_config config = { };
config.dst_addr = spi_fifo_addr;
config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
config.dst_maxburst = 4;
ret = dmaengine_slave_config(tx_chan, &config);
if (ret)
return ret;
This describes the peripheral side:
A transaction is prepared separately:
desc = dmaengine_prep_slave_single(tx_chan,
tx_dma_addr,
tx_len,
DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT);
if (!desc)
return -ENOMEM;
The two pieces combine conceptually as:
Memory Side Peripheral Side
tx_dma_addr SPI FIFO
tx_len 4-byte width
DMA_MEM_TO_DEV maxburst
│ │
└────────────┬──────────────┘
▼
DMA Transaction
Memory Increment and Peripheral No-Increment¶
Peripheral FIFOs are commonly accessed through a fixed register address.
The memory address progresses through the DMA buffer, while the peripheral FIFO address remains fixed.
For memory-to-device DMA:
Memory Peripheral FIFO
0x1000 ────────────────┐
0x1004 ────────────────┤
0x1008 ────────────────┼──> Same FIFO Register
0x100C ────────────────┘
Memory:
INC
Peripheral:
NOINC
For device-to-memory DMA:
Peripheral FIFO Memory
Same FIFO ────────────────────> 0x2000
Same FIFO ────────────────────> 0x2004
Same FIFO ────────────────────> 0x2008
Same FIFO ────────────────────> 0x200C
Peripheral:
NOINC
Memory:
INC
The DMA controller driver normally derives these controller-specific settings from the transfer direction and descriptor configuration.
Cyclic DMA Integration¶
Slave configuration and cyclic DMA describe different aspects of the same transfer.
For example, a cyclic peripheral RX transfer may use:
dmaengine_slave_config()
│
▼
Peripheral Configuration
- FIFO address
- transfer width
- burst configuration
dmaengine_prep_dma_cyclic()
│
▼
Transaction Configuration
- memory DMA address
- buffer length
- period length
- DMA_DEV_TO_MEM
The DMA controller driver combines both when preparing the hardware transaction.
Therefore, cyclic DMA does not replace slave configuration.
A cyclic descriptor describes the repeating memory transaction, while dma_slave_config describes how that transaction accesses the peripheral.
DMA Client and Provider Responsibilities¶
The DMA client and DMA provider have distinct responsibilities.
DMA Client¶
The peripheral driver typically knows:
- Which DMA channel or request it needs.
- Which peripheral FIFO or register participates in DMA.
- The required peripheral transfer width.
- The desired burst behavior.
- The transaction direction.
- The memory buffer and transfer length.
DMA Controller Driver¶
The controller driver knows:
- How generic DMA configuration maps to hardware fields.
- How source and destination addresses are programmed.
- How transfer widths are encoded.
- How burst sizes are encoded.
- How address increment behavior is programmed.
- How hardware descriptors are allocated and linked.
- How the DMA controller is started and stopped.
The DMA Engine framework provides the abstraction boundary between these two responsibilities.
Linux Source Trace¶
The DMA slave configuration path can be observed in both DMA client and DMA provider drivers.
DMA Client Example¶
The BCM2835 SPI driver provides a clear example of DMA client configuration.
Relevant source:
Important functions include:
The driver obtains DMA channels with:
It then configures peripheral endpoints through struct dma_slave_config and calls:
The peripheral register resource and the CPU MMIO mapping are maintained separately.
This demonstrates that DMA peripheral configuration is distinct from normal CPU register access.
RP1 DMA Provider¶
On Raspberry Pi 5, RP1 exposes a DMA controller compatible with:
An RP1 peripheral can reference the DMA provider through Device Tree entries such as:
The DMA request specifier identifies the DMA request/resource relationship.
It does not replace the peripheral address configuration carried through struct dma_slave_config.
Synopsys AXI DMA Configuration¶
The Synopsys AXI DMA driver registers its slave configuration callback as:
The callback stores the generic configuration in controller-specific channel state:
static int dw_axi_dma_chan_slave_config(struct dma_chan *dchan,
struct dma_slave_config *config)
{
struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
memcpy(&chan->config, config, sizeof(*config));
return 0;
}
This is an important lifecycle boundary.
dmaengine_slave_config() does not immediately construct or start the complete hardware transaction in this implementation.
The saved configuration is consumed later during controller-specific descriptor preparation.
Hardware Descriptor Preparation¶
The AXI DMA driver later uses the saved channel configuration when preparing hardware descriptors.
The controller-specific preparation logic uses information such as:
chan->config.src_addr
chan->config.dst_addr
chan->config.src_addr_width
chan->config.dst_addr_width
chan->config.src_maxburst
chan->config.dst_maxburst
Together with:
the driver derives hardware-specific state including:
- Source Address Register (SAR).
- Destination Address Register (DAR).
- Source and destination transfer width.
- Burst size encoding.
- Increment or no-increment behavior.
- Transfer count.
Conceptually:
Saved Slave Configuration
+
Transaction Information
│
▼
Controller-Specific Descriptor Preparation
│
▼
Hardware Descriptor / LLI
This demonstrates the separation between generic DMA Engine configuration and controller-specific hardware programming.
Raspberry Pi 5 and RP1 Addressing¶
Raspberry Pi 5 provides an additional example of why DMA-visible addressing must be treated separately from CPU MMIO addressing.
Many external I/O controllers are located in RP1, which is connected to the BCM2712 SoC through PCIe.
Conceptually:
RP1 Device Tree descriptions include both ranges and dma-ranges mappings.
These mappings describe different address views used by CPU/PCIe accesses and DMA accesses.
As a result, a peripheral resource address should not be assumed to be identical to the final address programmed into a DMA controller.
Controller drivers may perform additional translation before constructing the hardware descriptor.
Key Design Rules¶
When using slave DMA:
- Request the appropriate DMA channel for the peripheral.
- Determine the DMA-visible peripheral endpoint according to the platform and driver model.
- Configure the peripheral-side width and burst requirements.
- Apply the configuration with
dmaengine_slave_config(). - Prepare each DMA transaction separately.
- Use the correct DMA transfer direction.
- Do not treat CPU MMIO virtual addresses as DMA addresses.
- Do not assume that
dmaengine_slave_config()immediately starts or programs the complete DMA transaction. - Respect DMA controller-specific capability limits.
- Terminate and synchronize DMA before releasing resources that may still be referenced by asynchronous DMA activity.
Mental Model¶
The most useful distinction is:
dma_slave_config
│
▼
How does this DMA channel access the peripheral?
DMA Descriptor
│
▼
What does this particular DMA transaction transfer?
The controller driver eventually combines both:
This separation is fundamental to understanding Linux slave DMA.
Related Topics¶
- Linux DMA Engine Framework
- DMA Controller Driver and DMA Channel
- DMA Descriptor
- DMA Completion and Transaction Status
- DMA Termination and Synchronization
- DMA Cyclic
Related API Reference¶
Summary¶
DMA slave configuration describes the peripheral side of a DMA channel.
struct dma_slave_config provides the peripheral address, transfer width, burst configuration, and related channel information, while DMA preparation APIs describe individual transactions using memory addresses, lengths, directions, and transaction-specific behavior.
dmaengine_slave_config() dispatches this configuration to the DMA controller driver through its device_config() operation. The controller may save the configuration rather than immediately programming hardware.
During descriptor preparation, the controller driver combines the saved peripheral configuration with transaction-specific information and translates the result into controller-specific hardware fields such as source and destination addresses, transfer widths, burst settings, increment behavior, and transfer count.
The key distinction is:
Understanding this boundary makes it easier to reason about UART, SPI, I2S, ADC, cyclic DMA, and other peripheral DMA users built on the Linux DMA Engine framework.