Direct Memory Access (DMA)

Overview

Direct Memory Access (Controller) is a commonly provided type of co-processor that can typically offload transferring data to and from peripherals and memory.

The DMA API is not a portable API and really cannot be as each DMA has unique memory requirements, peripheral interactions, and features. The API in effect provides a union of all useful DMA functionality drivers have needed in the tree. It can still be a good abstraction, with care, for peripheral devices for vendors where the DMA IP might be very similar but have slight variances.

Driver Implementation Expectations

Synchronization and Ownership

From an API point of view, a DMA channel is a single-owner object, meaning the drivers should not attempt to wrap a channel with kernel synchronization primitives such as mutexes or semaphores. If DMA channels require mutating shared registers, those register updates should be wrapped in a spin lock.

This enables the entire API to be low-cost and callable from any call context, including ISRs where it may be very useful to start/stop/suspend/resume/reload a channel transfer.

Transfer Descriptor Memory Management

Drivers should not attempt to use heap allocations of any kind. If object pools are needed for transfer descriptors then those should be setup in a way that does not break the promise of ISR-allowable calls. Many drivers choose to create a simple static descriptor array per channel with the size of the descriptor array adjustable using Kconfig.

Channel State Machine Expectations

DMA channels should be viewed as state machines that the DMA API provides transition events for in the form of API calls. Every driver is expected to maintain its own channel state tracking. The busy state of the channel should be inspectable at any time with dma_get_status().

A diagram showing those expectated possible state transitions and their API calls is provided here for reference.

digraph { node [style=rounded]; edge [fontname=Courier]; init [shape=point]; CONFIGURED [label=Configured,shape=box]; RUNNING [label=Running,shape=box]; SUSPENDED [label=Suspended,shape=box]; init -> CONFIGURED [label=dma_config]; CONFIGURED -> RUNNING [label=dma_start]; CONFIGURED -> CONFIGURED [label=dma_stop]; RUNNING -> CONFIGURED [label=dma_stop]; RUNNING -> RUNNING [label=dma_start]; RUNNING -> RUNNING [label=dma_resume, headport=w]; RUNNING -> SUSPENDED [label=dma_suspend]; SUSPENDED -> SUSPENDED [label=dma_suspend]; SUSPENDED -> RUNNING [label=dma_resume]; SUSPENDED -> CONFIGURED [label=dma_stop]; }

DMA state finite state machine

API Reference

group dma_interface

DMA Interface.

Defines

DMA_STATUS_COMPLETE

The DMA callback event has occurred at the completion of a transfer list.

DMA_STATUS_BLOCK

The DMA callback has occurred at the completion of a single transfer block in a transfer list.

DMA_MAGIC

Magic code to identify context content.

DMA_BUF_ADDR_ALIGNMENT(node)

Get the device tree property describing the buffer address alignment.

Useful when statically defining or allocating buffers for DMA usage where memory alignment often matters.

Parameters:
Returns:

alignment Memory byte alignment required for DMA buffers

DMA_BUF_SIZE_ALIGNMENT(node)

Get the device tree property describing the buffer size alignment.

Useful when statically defining or allocating buffers for DMA usage where memory alignment often matters.

Parameters:
Returns:

alignment Memory byte alignment required for DMA buffers

DMA_COPY_ALIGNMENT(node)

Get the device tree property describing the minimal chunk of data possible to be copied.

Parameters:
Returns:

minimal Minimal chunk of data possible to be copied

Typedefs

typedef void (*dma_callback_t)(const struct device *dev, void *user_data, uint32_t channel, int status)

Callback function for DMA transfer completion.

If enabled, callback function will be invoked at transfer or block completion, or when an error happens. In circular mode, status indicates that the DMA device has reached either the end of the buffer (DMA_STATUS_COMPLETE) or a water mark (DMA_STATUS_BLOCK).

Param dev:

Pointer to the DMA device calling the callback.

Param user_data:

A pointer to some user data or NULL

Param channel:

The channel number

Param status:

Status of the transfer

  • DMA_STATUS_COMPLETE buffer fully consumed

  • DMA_STATUS_BLOCK buffer consumption reached a configured block or water mark

  • A negative errno otherwise

Enums

enum dma_channel_direction

DMA channel direction.

Values:

enumerator MEMORY_TO_MEMORY = 0x0

Memory to memory.

enumerator MEMORY_TO_PERIPHERAL

Memory to peripheral.

enumerator PERIPHERAL_TO_MEMORY

Peripheral to memory.

enumerator PERIPHERAL_TO_PERIPHERAL

Peripheral to peripheral.

enumerator HOST_TO_MEMORY

Host to memory.

enumerator MEMORY_TO_HOST

Memory to host.

enumerator DMA_CHANNEL_DIRECTION_COMMON_COUNT

Number of all common channel directions.

enumerator DMA_CHANNEL_DIRECTION_PRIV_START = DMA_CHANNEL_DIRECTION_COMMON_COUNT

This and higher values are dma controller or soc specific.

Refer to the specified dma driver header file.

enumerator DMA_CHANNEL_DIRECTION_MAX = 0x7

Maximum allowed value (3 bit field!)

enum dma_addr_adj

DMA address adjustment.

Valid values for source_addr_adj and dest_addr_adj

Values:

enumerator DMA_ADDR_ADJ_INCREMENT

Increment the address.

enumerator DMA_ADDR_ADJ_DECREMENT

Decrement the address.

enumerator DMA_ADDR_ADJ_NO_CHANGE

No change the address.

enum dma_channel_filter

DMA channel attributes.

Values:

enumerator DMA_CHANNEL_NORMAL
enumerator DMA_CHANNEL_PERIODIC
enum dma_attribute_type

DMA attributes.

Values:

enumerator DMA_ATTR_BUFFER_ADDRESS_ALIGNMENT
enumerator DMA_ATTR_BUFFER_SIZE_ALIGNMENT
enumerator DMA_ATTR_COPY_ALIGNMENT
enumerator DMA_ATTR_MAX_BLOCK_COUNT

Functions

static inline int dma_config(const struct device *dev, uint32_t channel, struct dma_config *config)

Configure individual channel for DMA transfer.

Parameters:
  • dev – Pointer to the device structure for the driver instance.

  • channel – Numeric identification of the channel to configure

  • config – Data structure containing the intended configuration for the selected channel

Return values:
  • 0 – if successful.

  • Negative – errno code if failure.

static inline int dma_reload(const struct device *dev, uint32_t channel, uint32_t src, uint32_t dst, size_t size)

Reload buffer(s) for a DMA channel.

Parameters:
  • dev – Pointer to the device structure for the driver instance.

  • channel – Numeric identification of the channel to configure selected channel

  • src – source address for the DMA transfer

  • dst – destination address for the DMA transfer

  • size – size of DMA transfer

Return values:
  • 0 – if successful.

  • Negative – errno code if failure.

int dma_start(const struct device *dev, uint32_t channel)

Enables DMA channel and starts the transfer, the channel must be configured beforehand.

Implementations must check the validity of the channel ID passed in and return -EINVAL if it is invalid.

Start is allowed on channels that have already been started and must report success.

Function properties (list may not be complete)

isr-ok

Parameters:
  • dev – Pointer to the device structure for the driver instance.

  • channel – Numeric identification of the channel where the transfer will be processed

Return values:
  • 0 – if successful.

  • Negative – errno code if failure.

int dma_stop(const struct device *dev, uint32_t channel)

Stops the DMA transfer and disables the channel.

Implementations must check the validity of the channel ID passed in and return -EINVAL if it is invalid.

Stop is allowed on channels that have already been stopped and must report success.

Function properties (list may not be complete)

isr-ok

Parameters:
  • dev – Pointer to the device structure for the driver instance.

  • channel – Numeric identification of the channel where the transfer was being processed

Return values:
  • 0 – if successful.

  • Negative – errno code if failure.

int dma_suspend(const struct device *dev, uint32_t channel)

Suspend a DMA channel transfer.

Implementations must check the validity of the channel state and ID passed in and return -EINVAL if either are invalid.

Function properties (list may not be complete)

isr-ok

Parameters:
  • dev – Pointer to the device structure for the driver instance.

  • channel – Numeric identification of the channel to suspend

Return values:
  • 0 – If successful.

  • -ENOSYS – If not implemented.

  • -EINVAL – If invalid channel id or state.

  • -errno – Other negative errno code failure.

int dma_resume(const struct device *dev, uint32_t channel)

Resume a DMA channel transfer.

Implementations must check the validity of the channel state and ID passed in and return -EINVAL if either are invalid.

Function properties (list may not be complete)

isr-ok

Parameters:
  • dev – Pointer to the device structure for the driver instance.

  • channel – Numeric identification of the channel to resume

Return values:
  • 0 – If successful.

  • -ENOSYS – If not implemented

  • -EINVAL – If invalid channel id or state.

  • -errno – Other negative errno code failure.

int dma_request_channel(const struct device *dev, void *filter_param)

request DMA channel.

request DMA channel resources return -EINVAL if there is no valid channel available.

Function properties (list may not be complete)

isr-ok

Parameters:
  • dev – Pointer to the device structure for the driver instance.

  • filter_param – filter function parameter

Return values:
  • dma – channel if successful.

  • Negative – errno code if failure.

void dma_release_channel(const struct device *dev, uint32_t channel)

release DMA channel.

release DMA channel resources

Function properties (list may not be complete)

isr-ok

Parameters:
  • dev – Pointer to the device structure for the driver instance.

  • channel – channel number

int dma_chan_filter(const struct device *dev, int channel, void *filter_param)

DMA channel filter.

filter channel by attribute

Parameters:
  • dev – Pointer to the device structure for the driver instance.

  • channel – channel number

  • filter_param – filter attribute

Return values:

Negative – errno code if not support

static inline int dma_get_status(const struct device *dev, uint32_t channel, struct dma_status *stat)

get current runtime status of DMA transfer

Implementations must check the validity of the channel ID passed in and return -EINVAL if it is invalid or -ENOSYS if not supported.

Function properties (list may not be complete)

isr-ok

Parameters:
  • dev – Pointer to the device structure for the driver instance.

  • channel – Numeric identification of the channel where the transfer was being processed

  • stat – a non-NULL dma_status object for storing DMA status

Return values:
  • non-negative – if successful.

  • Negative – errno code if failure.

static inline int dma_get_attribute(const struct device *dev, uint32_t type, uint32_t *value)

get attribute of a dma controller

This function allows to get a device specific static or runtime attribute like required address and size alignment of a buffer. Implementations must check the validity of the type passed in and return -EINVAL if it is invalid or -ENOSYS if not supported.

Function properties (list may not be complete)

isr-ok

Parameters:
  • dev – Pointer to the device structure for the driver instance.

  • type – Numeric identification of the attribute

  • value – A non-NULL pointer to the variable where the read value is to be placed

Return values:
  • non-negative – if successful.

  • Negative – errno code if failure.

static inline uint32_t dma_width_index(uint32_t size)

Look-up generic width index to be used in registers.

Warning

This look-up works for most controllers, but may not work for yours. Ensure your controller expects the most common register bit values before using this convenience function. If your controller does not support these values, you will have to write your own look-up inside the controller driver.

Parameters:
  • size – width of bus (in bytes)

Return values:

common – DMA index to be placed into registers.

static inline uint32_t dma_burst_index(uint32_t burst)

Look-up generic burst index to be used in registers.

Warning

This look-up works for most controllers, but may not work for yours. Ensure your controller expects the most common register bit values before using this convenience function. If your controller does not support these values, you will have to write your own look-up inside the controller driver.

Parameters:
  • burst – number of bytes to be sent in a single burst

Return values:

common – DMA index to be placed into registers.

struct dma_block_config
#include <dma.h>

DMA block configuration structure.

Aside from source address, destination address, and block size many of these options are hardware and driver dependent.

Public Members

uint32_t source_address

block starting address at source

uint32_t dest_address

block starting address at destination

uint32_t source_gather_interval

Address adjustment at gather boundary.

uint32_t dest_scatter_interval

Address adjustment at scatter boundary.

uint16_t dest_scatter_count

Continuous transfer count between scatter boundaries.

uint16_t source_gather_count

Continuous transfer count between gather boundaries.

uint32_t block_size

Number of bytes to be transferred for this block.

struct dma_block_config *next_block

Pointer to next block in a transfer list.

uint16_t source_gather_en

Enable source gathering when set to 1.

uint16_t dest_scatter_en

Enable destination scattering when set to 1.

uint16_t source_addr_adj

Source address adjustment option.

  • 0b00 increment

  • 0b01 decrement

  • 0b10 no change

uint16_t dest_addr_adj

Destination address adjustment.

  • 0b00 increment

  • 0b01 decrement

  • 0b10 no change

uint16_t source_reload_en

Reload source address at the end of block transfer.

uint16_t dest_reload_en

Reload destination address at the end of block transfer.

uint16_t fifo_mode_control

FIFO fill before starting transfer, HW specific meaning.

uint16_t flow_control_mode

Transfer flow control mode.

  • 0b0 source request service upon data availability

  • 0b1 source request postponed until destination request happens

struct dma_config
#include <dma.h>

DMA configuration structure.

Public Members

uint32_t dma_slot

Which peripheral and direction, HW specific.

uint32_t channel_direction

Direction the transfers are occurring.

  • 0b000 memory to memory,

  • 0b001 memory to peripheral,

  • 0b010 peripheral to memory,

  • 0b011 peripheral to peripheral,

  • 0b100 host to memory

  • 0b101 memory to host

  • others hardware specific

uint32_t complete_callback_en

Completion callback enable.

  • 0b0 callback invoked at transfer list completion only

  • 0b1 callback invoked at completion of each block

uint32_t error_callback_en

Error callback enable.

  • 0b0 error callback enabled

  • 0b1 error callback disabled

uint32_t source_handshake

Source handshake, HW specific.

  • 0b0 HW

  • 0b1 SW

uint32_t dest_handshake

Destination handshake, HW specific.

  • 0b0 HW

  • 0b1 SW

uint32_t channel_priority

Channel priority for arbitration, HW specific.

uint32_t source_chaining_en

Source chaining enable, HW specific.

uint32_t dest_chaining_en

Destination chaining enable, HW specific.

uint32_t linked_channel

Linked channel, HW specific.

uint32_t cyclic

Cyclic transfer list, HW specific.

uint32_t source_data_size

Width of source data (in bytes)

uint32_t dest_data_size

Width of destination data (in bytes)

uint32_t source_burst_length

Source burst length in bytes.

uint32_t dest_burst_length

Destination burst length in bytes.

uint32_t block_count

Number of blocks in transfer list.

struct dma_block_config *head_block

Pointer to the first block in the transfer list.

void *user_data

Optional attached user data for callbacks.

dma_callback_t dma_callback

Optional callback for completion and error events.

struct dma_status
#include <dma.h>

DMA runtime status structure.

Public Members

bool busy

Is the current DMA transfer busy or idle.

enum dma_channel_direction dir

Direction fo the transfer.

uint32_t pending_length

Pending length to be transferred in bytes, HW specific.

uint32_t free

Available buffers space, HW specific.

uint32_t write_position

Write position in circular DMA buffer, HW specific.

uint32_t read_position

Read position in circular DMA buffer, HW specific.

uint64_t total_copied

Total copied, HW specific.

struct dma_context
#include <dma.h>

DMA context structure Note: the dma_context shall be the first member of DMA client driver Data, got by dev->data.

Public Members

int32_t magic

magic code to identify the context

int dma_channels

number of dma channels

atomic_t *atomic

atomic holding bit flags for each channel to mark as used/unused