Skip to content

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_config describes 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.

DMA Slave Configuration Flow

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:

Memory ───────────────> Peripheral
                      dst_addr

For a device-to-memory transfer:

Peripheral ───────────> Memory
 src_addr

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:

DMA_MEM_TO_DEV

Memory ───────────────> Peripheral

the peripheral is the destination.

The relevant configuration is normally:

config.dst_addr
config.dst_addr_width
config.dst_maxburst

Device to Memory

For:

DMA_DEV_TO_MEM

Peripheral ───────────> Memory

the peripheral is the source.

The relevant configuration is normally:

config.src_addr
config.src_addr_width
config.src_maxburst

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:

config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;

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:

Peripheral Address Width
Size of One Peripheral-Side Transfer Beat

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.

config.src_maxburst
config.dst_maxburst

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:

dmaengine_slave_config(chan, &config);

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:

Memory DMA address
Transfer length
Direction
Cyclic buffer length
Period length
Callback behavior

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:

SPI FIFO address
4-byte peripheral access
Maximum burst of 4

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:

drivers/spi/spi-bcm2835.c

Important functions include:

bcm2835_spi_probe()
bcm2835_dma_init()

The driver obtains DMA channels with:

dma_request_chan(dev, "tx");
dma_request_chan(dev, "rx");

It then configures peripheral endpoints through struct dma_slave_config and calls:

dmaengine_slave_config()

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:

snps,axi-dma-1.01a

An RP1 peripheral can reference the DMA provider through Device Tree entries such as:

dmas = <&rp1_dma ...>;
dma-names = "tx", "rx";

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:

dw->dma.device_config = dw_axi_dma_chan_slave_config;

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:

Memory DMA address
Transfer length
Transfer direction

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:

BCM2712
    │ PCIe
RP1
    ├── SPI
    ├── UART
    ├── I2C
    └── DMA

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:

  1. Request the appropriate DMA channel for the peripheral.
  2. Determine the DMA-visible peripheral endpoint according to the platform and driver model.
  3. Configure the peripheral-side width and burst requirements.
  4. Apply the configuration with dmaengine_slave_config().
  5. Prepare each DMA transaction separately.
  6. Use the correct DMA transfer direction.
  7. Do not treat CPU MMIO virtual addresses as DMA addresses.
  8. Do not assume that dmaengine_slave_config() immediately starts or programs the complete DMA transaction.
  9. Respect DMA controller-specific capability limits.
  10. 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:

Peripheral Configuration
        +
Transaction Description
Hardware DMA Descriptor

This separation is fundamental to understanding Linux slave DMA.




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:

Slave Configuration
        =
Peripheral-Side Channel Configuration

Descriptor
        =
Individual DMA Transaction

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.