Files
rtems/bsps/aarch64/raspberrypi/dma/raspberrypi-dma.c
Shaunak Datar fb08c8cb8d bsp/aarch64/raspberrypi: Add support for DMA
Adds the support for DMA on the Raspberry Pi 4b
2025-08-20 12:40:01 +05:30

300 lines
9.3 KiB
C

/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @ingroup RTEMSBSPsAArch64Raspberrypi4
*
* @brief Raspberry Pi specific DMA definitions.
*/
/*
* Copyright (C) 2025 Shaunak Datar
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <bsp/raspberrypi-dma.h>
#define DMA4_AD_SHIFT( addr ) ( addr >> 5 )
#define DMA4_AD_UNSHIFT( addr ) ( addr << 5 )
#define ADDRESS_LOW( addr ) ( (uintptr_t) ( addr ) & 0xFFFFFFFF )
#define ADDRESS_HIGH( addr ) ( ( (uintptr_t) ( addr ) >> 32 ) & 0xFF )
#define BUS_ADDR( addr ) ( ( ( addr ) & ~0xC0000000 ) | 0xC0000000 )
typedef struct {
uint32_t transfer_info; /**< Control register */
uint32_t source_addr; /**< Source address register */
uint32_t destination_addr; /**< Destination address register */
uint32_t transfer_length; /**< Transfer length register */
uint32_t mode_2d_stride; /**< Stride register */
uint32_t next_cb; /**< Next control block address register */
uint32_t reserved[ 2 ]; /**< Reserved */
} rpi_dma_control_block;
typedef struct {
uint32_t transfer_info; /**< Control register */
uint32_t source_addr; /**< Source address register */
uint32_t destination_addr; /**< Destination address register */
uint32_t transfer_length; /**< Transfer length register */
uint32_t reserved_bit; /**< Reserved */
uint32_t next_cb; /**< Next control block address register */
uint32_t reserved[ 2 ]; /**< Reserved */
} rpi_dma_lite_control_block;
typedef struct {
uint32_t transfer_info; /**< Control register */
uint32_t source_addr; /**< Source address register */
uint32_t source_info; /**< Source information */
uint32_t destination_addr; /**< Destination address register */
uint32_t destination_info; /**< Destination information */
uint32_t transfer_length; /**< Transfer length register */
uint32_t next_cb; /**< Next control block address register */
uint32_t reserved; /**< Reserved */
} rpi_dma4_control_block;
static const uint32_t dma_base_addresses[] = {
BCM2711_DMA0_BASE,
BCM2711_DMA1_BASE,
BCM2711_DMA2_BASE,
BCM2711_DMA3_BASE,
BCM2711_DMA4_BASE,
BCM2711_DMA5_BASE,
BCM2711_DMA6_BASE,
BCM2711_DMA7_BASE,
BCM2711_DMA8_BASE,
BCM2711_DMA9_BASE,
BCM2711_DMA10_BASE,
BCM2711_DMA11_BASE,
BCM2711_DMA12_BASE,
BCM2711_DMA13_BASE,
BCM2711_DMA14_BASE
};
static inline uint32_t get_base_address( rpi_dma_channel channel )
{
if ( channel >= 0 && channel <= DMA4_CHANNEL_14 ) {
return dma_base_addresses[ channel ];
}
return 0;
}
static rpi_dma_control_block *rpi_dma_init_cb(
void *source_address,
void *destination_address,
uint32_t transfer_length
)
{
rpi_dma_control_block *cb = (rpi_dma_control_block *)
rtems_heap_allocate_aligned_with_boundary(
sizeof( rpi_dma_control_block ),
CPU_CACHE_LINE_BYTES,
0
);
if ( cb == NULL ) {
return NULL;
}
cb->source_addr = BUS_ADDR( (uint32_t) (uintptr_t) source_address );
cb->destination_addr = BUS_ADDR( (uint32_t) (uintptr_t) destination_address );
cb->transfer_length = transfer_length;
cb->transfer_info = ( TI_DEST_INC | TI_SRC_INC );
cb->mode_2d_stride = 0;
cb->next_cb = 0;
cb->reserved[ 0 ] = 0;
cb->reserved[ 1 ] = 0;
return cb;
}
static rpi_dma_lite_control_block *rpi_dma_lite_init_cb(
void *source_address,
void *destination_address,
uint32_t transfer_length
)
{
rpi_dma_lite_control_block *cb = (rpi_dma_lite_control_block *)
rtems_heap_allocate_aligned_with_boundary(
sizeof( rpi_dma_control_block ),
CPU_CACHE_LINE_BYTES,
0
);
if ( cb == NULL ) {
return NULL;
}
cb->source_addr = BUS_ADDR( (uint32_t) (uintptr_t) source_address );
cb->destination_addr = BUS_ADDR( (uint32_t) (uintptr_t) destination_address );
cb->transfer_length = transfer_length;
cb->transfer_info = ( TI_DEST_INC | TI_SRC_INC );
cb->next_cb = 0;
cb->reserved_bit = 0;
cb->reserved[ 0 ] = 0;
cb->reserved[ 1 ] = 0;
return cb;
}
static rpi_dma4_control_block *rpi_dma4_init_cb(
void *source_address,
void *destination_address,
uint32_t transfer_length
)
{
rpi_dma4_control_block *cb = (rpi_dma4_control_block *)
rtems_heap_allocate_aligned_with_boundary(
sizeof( rpi_dma4_control_block ),
CPU_CACHE_LINE_BYTES,
0
);
if ( cb == NULL ) {
return NULL;
}
cb->source_addr = (uint32_t) ADDRESS_LOW( source_address );
cb->source_info = SI_SRC_INC | ADDRESS_HIGH( source_address );
cb->destination_addr = (uint32_t) ADDRESS_LOW( destination_address );
cb->destination_info = DI_DEST_INC | ADDRESS_HIGH( destination_address );
cb->transfer_length = transfer_length;
cb->transfer_info = 0;
cb->next_cb = 0;
cb->reserved = 0;
return cb;
}
static inline void rpi_dma_free_control_block(
rpi_dma_channel channel,
uint32_t base_address
)
{
uint32_t cb_ad_reg = BCM2835_REG( base_address + CONBLK_AD_OFFSET );
if ( cb_ad_reg == 0 ) {
return;
}
uintptr_t cb_addr;
if ( channel >= DMA4_CHANNEL_11 && channel <= DMA4_CHANNEL_14 ) {
cb_addr = DMA4_AD_UNSHIFT( cb_ad_reg );
} else {
cb_addr = (uintptr_t) cb_ad_reg;
}
if ( cb_addr != 0 ) {
void *cb = (void *) cb_addr;
free( cb );
BCM2835_REG( base_address + CONBLK_AD_OFFSET ) = 0;
}
}
rtems_status_code rpi_dma_start_transfer( rpi_dma_channel channel )
{
uint32_t base_address = get_base_address( channel );
if ( !base_address ) {
return RTEMS_INVALID_NUMBER;
}
BCM2835_REG( base_address + CS_OFFSET ) = CS_WAIT_FOR_OUTSTANDING_WRITES |
CS_PANIC_PRIORITY_SHIFT |
CS_PRIORITY_SHIFT;
BCM2835_REG( base_address + CS_OFFSET ) |= CS_ACTIVE;
return RTEMS_SUCCESSFUL;
}
rtems_status_code rpi_dma_wait( rpi_dma_channel channel )
{
uint32_t base_address = get_base_address( channel );
if ( !base_address ) {
return RTEMS_INVALID_NUMBER;
}
while (( BCM2835_REG( base_address + CS_OFFSET ) & CS_ACTIVE ));
rpi_dma_free_control_block( channel, base_address );
return RTEMS_SUCCESSFUL;
}
rtems_status_code rpi_dma_mem_to_mem_init(
rpi_dma_channel channel,
void *source_address,
void *destination_address,
uint32_t transfer_length
)
{
uint32_t base_address = get_base_address( channel );
if ( !base_address ) {
return RTEMS_INVALID_NUMBER;
}
if ( ( (uintptr_t) source_address % CPU_CACHE_LINE_BYTES ) != 0 ||
( (uintptr_t) destination_address % CPU_CACHE_LINE_BYTES ) != 0 ) {
return RTEMS_INVALID_ADDRESS;
}
void *control_block = NULL;
size_t cb_size = 0;
if ( channel < DMA_LITE_CHANNEL_7 ) {
control_block = rpi_dma_init_cb(
source_address,
destination_address,
transfer_length
);
cb_size = sizeof( rpi_dma_control_block );
} else if ( channel > DMA_CHANNEL_6 && channel < DMA4_CHANNEL_11 ) {
control_block = rpi_dma_lite_init_cb(
source_address,
destination_address,
transfer_length
);
cb_size = sizeof( rpi_dma_lite_control_block );
} else if ( channel > DMA_LITE_CHANNEL_10 && channel <= DMA4_CHANNEL_14 ) {
control_block = rpi_dma4_init_cb(
source_address,
destination_address,
transfer_length
);
cb_size = sizeof( rpi_dma4_control_block );
} else {
return RTEMS_INVALID_NUMBER;
}
if ( control_block == NULL ) {
return RTEMS_NO_MEMORY;
}
BCM2835_REG( base_address + CS_OFFSET ) = CS_RESET | CS_ABORT;
rtems_cache_flush_multiple_data_lines( control_block, cb_size );
rtems_cache_flush_multiple_data_lines( source_address, transfer_length );
rtems_cache_invalidate_multiple_data_lines(
destination_address,
transfer_length
);
if ( ( channel >= DMA4_CHANNEL_11 ) && ( channel <= DMA4_CHANNEL_14 ) ) {
uint32_t cb_addr = (uint32_t) (uintptr_t) control_block;
uint32_t dma4_cb_addr = DMA4_AD_SHIFT( cb_addr );
BCM2835_REG( base_address + CONBLK_AD_OFFSET ) = dma4_cb_addr;
} else {
BCM2835_REG( base_address + CONBLK_AD_OFFSET ) = (uint32_t) (uintptr_t
) control_block;
}
return RTEMS_SUCCESSFUL;
}