blob: b49dca4ea14ede24848af12dce6c3858489290a9 [file] [log] [blame]
/*******************************************************************************
* (c) Copyright 2008 Actel Corporation. All rights reserved.
*
* SmartFusion microcontroller subsystem Peripheral DMA bare metal software
* driver implementation.
*
* SVN $Revision: 2110 $
* SVN $Date: 2010-02-05 15:24:19 +0000 (Fri, 05 Feb 2010) $
*/
#include "mss_pdma.h"
#include "../../CMSIS/mss_assert.h"
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__GNUC__)
__attribute__((__interrupt__)) void DMA_IRQHandler( void );
#else
void DMA_IRQHandler( void );
#endif
/***************************************************************************//**
Offset of the posted writes WRITE_ADJ bits in a PDMA channel's configuration
register.
*/
#define CHANNEL_N_POSTED_WRITE_ADJUST_SHIFT 14
/*-------------------------------------------------------------------------*//**
* Look-up table use to derice a channel's control register value from the
* requested source/destination. This table is incexed on the pdma_src_dest_t
* enumeration.
*/
#define CHANNEL_N_CTRL_PDMA_MASK (uint32_t)0x00000001
#define CHANNEL_N_PERIPH_SELECT_SHIFT (uint32_t)23
#define CHANNEL_N_DIRECTION_MASK (uint32_t)0x00000002
const uint32_t src_dest_to_ctrl_reg_lut[] =
{
CHANNEL_N_CTRL_PDMA_MASK, /* PDMA_FROM_UART_0 */
CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)1 << CHANNEL_N_PERIPH_SELECT_SHIFT) | CHANNEL_N_DIRECTION_MASK, /* PDMA_TO_UART_0 */
CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)2 << CHANNEL_N_PERIPH_SELECT_SHIFT), /* PDMA_FROM_UART_1 */
CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)3 << CHANNEL_N_PERIPH_SELECT_SHIFT) | CHANNEL_N_DIRECTION_MASK, /* PDMA_TO_UART_1 */
CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)4 << CHANNEL_N_PERIPH_SELECT_SHIFT), /* PDMA_FROM_SPI_0 */
CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)5 << CHANNEL_N_PERIPH_SELECT_SHIFT) | CHANNEL_N_DIRECTION_MASK, /* PDMA_TO_SPI_0 */
CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)6 << CHANNEL_N_PERIPH_SELECT_SHIFT), /* PDMA_FROM_SPI_1 */
CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)7 << CHANNEL_N_PERIPH_SELECT_SHIFT) | CHANNEL_N_DIRECTION_MASK, /* PDMA_TO_SPI_1 */
CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)8 << CHANNEL_N_PERIPH_SELECT_SHIFT), /* PDMA_FROM_FPGA_1 */
CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)8 << CHANNEL_N_PERIPH_SELECT_SHIFT) | CHANNEL_N_DIRECTION_MASK, /* PDMA_TO_FPGA_1 */
CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)9 << CHANNEL_N_PERIPH_SELECT_SHIFT), /* PDMA_FROM_FPGA_0 */
CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)9 << CHANNEL_N_PERIPH_SELECT_SHIFT) | CHANNEL_N_DIRECTION_MASK, /* PDMA_TO_FPGA_0 */
CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)10 << CHANNEL_N_PERIPH_SELECT_SHIFT) | CHANNEL_N_DIRECTION_MASK, /* PDMA_TO_ACE */
CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)11 << CHANNEL_N_PERIPH_SELECT_SHIFT) /* PDMA_FROM_ACE */
};
/*-------------------------------------------------------------------------*//**
*
*/
#define PDMA_MASTER_ENABLE (uint32_t)0x04
#define PDMA_SOFT_RESET (uint32_t)0x20
/*-------------------------------------------------------------------------*//**
*
*/
#define NB_OF_PDMA_CHANNELS 8
#define NEXT_CHANNEL_A 0U
#define NEXT_CHANNEL_B 1U
#define CHANNEL_STOPPED 0U
#define CHANNEL_STARTED 1U
static uint8_t g_pdma_next_channel[NB_OF_PDMA_CHANNELS];
static uint8_t g_pdma_started_a[NB_OF_PDMA_CHANNELS];
static uint8_t g_pdma_started_b[NB_OF_PDMA_CHANNELS];
static pdma_channel_isr_t g_pdma_isr_table[NB_OF_PDMA_CHANNELS];
static const uint16_t g_pdma_status_mask[NB_OF_PDMA_CHANNELS] =
{
(uint16_t)0x0003, /* PDMA_CHANNEL_0 */
(uint16_t)0x000C, /* PDMA_CHANNEL_1 */
(uint16_t)0x0030, /* PDMA_CHANNEL_2 */
(uint16_t)0x00C0, /* PDMA_CHANNEL_3 */
(uint16_t)0x0300, /* PDMA_CHANNEL_4 */
(uint16_t)0x0C00, /* PDMA_CHANNEL_5 */
(uint16_t)0x3000, /* PDMA_CHANNEL_6 */
(uint16_t)0xC000, /* PDMA_CHANNEL_7 */
};
/***************************************************************************//**
* See mss_pdma.h for description of this function.
*/
void PDMA_init( void )
{
int32_t i;
/* Enable PDMA master access to comms matrix. */
SYSREG->AHB_MATRIX_CR |= PDMA_MASTER_ENABLE;
/* Reset PDMA block. */
SYSREG->SOFT_RST_CR |= PDMA_SOFT_RESET;
/* Clear any previously pended MSS PDMA interrupt */
NVIC_ClearPendingIRQ( DMA_IRQn );
/* Take PDMA controller out of reset*/
SYSREG->SOFT_RST_CR &= ~PDMA_SOFT_RESET;
/* Initialize channels state information. */
for ( i = 0; i < NB_OF_PDMA_CHANNELS; ++i )
{
g_pdma_next_channel[i] = NEXT_CHANNEL_A;
g_pdma_started_a[i] = CHANNEL_STOPPED;
g_pdma_started_b[i] = CHANNEL_STOPPED;
g_pdma_isr_table[i] = 0;
}
}
/***************************************************************************//**
* See mss_pdma.h for description of this function.
*/
#define CHANNEL_RESET_MASK (uint32_t)0x00000020
void PDMA_configure
(
pdma_channel_id_t channel_id,
pdma_src_dest_t src_dest,
uint32_t channel_cfg,
uint8_t write_adjust
)
{
/* Reset the channel. */
PDMA->CHANNEL[channel_id].CRTL |= CHANNEL_RESET_MASK;
PDMA->CHANNEL[channel_id].CRTL &= ~CHANNEL_RESET_MASK;
/* Configure PDMA channel's data source and destination. */
if ( src_dest != PDMA_MEM_TO_MEM )
{
PDMA->CHANNEL[channel_id].CRTL |= src_dest_to_ctrl_reg_lut[src_dest];
}
/* Configure PDMA channel trnasfer size, priority, source and destination address increment. */
PDMA->CHANNEL[channel_id].CRTL |= channel_cfg;
/* Posted write adjust. */
PDMA->CHANNEL[channel_id].CRTL |= ((uint32_t)write_adjust << CHANNEL_N_POSTED_WRITE_ADJUST_SHIFT);
}
/***************************************************************************//**
* See mss_pdma.h for description of this function.
*/
#define PAUSE_MASK (uint32_t)0x00000010
#define BUFFER_B_SELECT_MASK (uint32_t)0x00000004
#define CLEAR_PORT_A_DONE_MASK (uint32_t)0x00000080
#define CLEAR_PORT_B_DONE_MASK (uint32_t)0x00000100
#define PORT_A_COMPLETE_MASK (uint32_t)0x00000001
#define PORT_B_COMPLETE_MASK (uint32_t)0x00000002
void PDMA_start
(
pdma_channel_id_t channel_id,
uint32_t src_addr,
uint32_t dest_addr,
uint16_t transfer_count
)
{
/* Pause transfer. */
PDMA->CHANNEL[channel_id].CRTL |= PAUSE_MASK;
/* Clear complete transfers. */
if ( PDMA->CHANNEL[channel_id].STATUS & PORT_A_COMPLETE_MASK )
{
PDMA->CHANNEL[channel_id].CRTL |= CLEAR_PORT_A_DONE_MASK;
g_pdma_started_a[channel_id] = CHANNEL_STOPPED;
}
if ( PDMA->CHANNEL[channel_id].STATUS & PORT_B_COMPLETE_MASK )
{
PDMA->CHANNEL[channel_id].CRTL |= CLEAR_PORT_B_DONE_MASK;
g_pdma_started_b[channel_id] = CHANNEL_STOPPED;
}
/* Load source, destination and transfer count. */
if ( PDMA->CHANNEL[channel_id].STATUS & BUFFER_B_SELECT_MASK )
{
g_pdma_next_channel[channel_id] = NEXT_CHANNEL_A;
g_pdma_started_b[channel_id] = CHANNEL_STARTED;
PDMA->CHANNEL[channel_id].BUFFER_B_SRC_ADDR = src_addr;
PDMA->CHANNEL[channel_id].BUFFER_B_DEST_ADDR = dest_addr;
PDMA->CHANNEL[channel_id].BUFFER_B_TRANSFER_COUNT = transfer_count;
}
else
{
g_pdma_next_channel[channel_id] = NEXT_CHANNEL_B;
g_pdma_started_a[channel_id] = CHANNEL_STARTED;
PDMA->CHANNEL[channel_id].BUFFER_A_SRC_ADDR = src_addr;
PDMA->CHANNEL[channel_id].BUFFER_A_DEST_ADDR = dest_addr;
PDMA->CHANNEL[channel_id].BUFFER_A_TRANSFER_COUNT = transfer_count;
}
/* Start transfer */
PDMA->CHANNEL[channel_id].CRTL &= ~PAUSE_MASK;
}
/***************************************************************************//**
* See mss_pdma.h for description of this function.
*/
void PDMA_load_next_buffer
(
pdma_channel_id_t channel_id,
uint32_t src_addr,
uint32_t dest_addr,
uint16_t transfer_count
)
{
if ( NEXT_CHANNEL_A == g_pdma_next_channel[channel_id] )
{
/* Wait for channel A current transfer completion. */
if ( CHANNEL_STARTED == g_pdma_started_a[channel_id] )
{
uint32_t completed;
uint32_t channel_mask;
channel_mask = (uint32_t)1 << ((uint32_t)channel_id * 2U);
do {
completed = PDMA->BUFFER_STATUS & channel_mask;
} while( !completed );
PDMA->CHANNEL[channel_id].CRTL |= CLEAR_PORT_A_DONE_MASK;
}
/* Load source, destination and transfer count. */
PDMA->CHANNEL[channel_id].BUFFER_A_SRC_ADDR = src_addr;
PDMA->CHANNEL[channel_id].BUFFER_A_DEST_ADDR = dest_addr;
PDMA->CHANNEL[channel_id].BUFFER_A_TRANSFER_COUNT = transfer_count;
/* Update channel state information. */
g_pdma_next_channel[channel_id] = NEXT_CHANNEL_B;
g_pdma_started_a[channel_id] = CHANNEL_STARTED;
}
else
{
/* Wait for channel B current transfer completion. */
if ( CHANNEL_STARTED == g_pdma_started_b[channel_id] )
{
uint32_t completed;
uint32_t channel_mask;
channel_mask = (uint32_t)1 << (((uint32_t)channel_id * 2U) + 1U);
do {
completed = PDMA->BUFFER_STATUS & channel_mask;
} while( !completed );
PDMA->CHANNEL[channel_id].CRTL |= CLEAR_PORT_B_DONE_MASK;
}
/* Load source, destination and transfer count. */
PDMA->CHANNEL[channel_id].BUFFER_B_SRC_ADDR = src_addr;
PDMA->CHANNEL[channel_id].BUFFER_B_DEST_ADDR = dest_addr;
PDMA->CHANNEL[channel_id].BUFFER_B_TRANSFER_COUNT = transfer_count;
/* Update channel state information. */
g_pdma_next_channel[channel_id] = NEXT_CHANNEL_A;
g_pdma_started_b[channel_id] = CHANNEL_STARTED;
}
}
/***************************************************************************//**
* See mss_pdma.h for description of this function.
*/
uint32_t PDMA_status
(
pdma_channel_id_t channel_id
)
{
uint32_t status;
status = PDMA->CHANNEL[channel_id].STATUS & (PORT_A_COMPLETE_MASK | PORT_B_COMPLETE_MASK);
return status;
}
/***************************************************************************//**
*
*/
#define CHANNEL_0_STATUS_BITS_MASK (uint16_t)0x0003
#define CHANNEL_1_STATUS_BITS_MASK (uint16_t)0x000C
#define CHANNEL_2_STATUS_BITS_MASK (uint16_t)0x0030
#define CHANNEL_3_STATUS_BITS_MASK (uint16_t)0x00C0
#define CHANNEL_4_STATUS_BITS_MASK (uint16_t)0x0300
#define CHANNEL_5_STATUS_BITS_MASK (uint16_t)0x0C00
#define CHANNEL_6_STATUS_BITS_MASK (uint16_t)0x3000
#define CHANNEL_7_STATUS_BITS_MASK (uint16_t)0xC000
static pdma_channel_id_t get_channel_id_from_status
(
uint16_t status
)
{
pdma_channel_id_t channel_id = PDMA_CHANNEL_0;
if ( status & CHANNEL_0_STATUS_BITS_MASK )
{
channel_id = PDMA_CHANNEL_0;
}
else if ( status & CHANNEL_1_STATUS_BITS_MASK )
{
channel_id = PDMA_CHANNEL_1;
}
else if ( status & CHANNEL_2_STATUS_BITS_MASK )
{
channel_id = PDMA_CHANNEL_2;
}
else if ( status & CHANNEL_3_STATUS_BITS_MASK )
{
channel_id = PDMA_CHANNEL_3;
}
else if ( status & CHANNEL_4_STATUS_BITS_MASK )
{
channel_id = PDMA_CHANNEL_4;
}
else if ( status & CHANNEL_5_STATUS_BITS_MASK )
{
channel_id = PDMA_CHANNEL_5;
}
else if ( status & CHANNEL_6_STATUS_BITS_MASK )
{
channel_id = PDMA_CHANNEL_6;
}
else if ( status & CHANNEL_7_STATUS_BITS_MASK )
{
channel_id = PDMA_CHANNEL_7;
}
else
{
ASSERT(0);
}
return channel_id;
}
/***************************************************************************//**
*
*/
#if defined(__GNUC__)
__attribute__((__interrupt__)) void DMA_IRQHandler( void )
#else
void DMA_IRQHandler( void )
#endif
{
uint16_t status;
pdma_channel_id_t channel_id;
status = (uint16_t)PDMA->BUFFER_STATUS;
do {
channel_id = get_channel_id_from_status( status );
status &= (uint16_t)~g_pdma_status_mask[channel_id];
if ( 0 != g_pdma_isr_table[channel_id])
{
g_pdma_isr_table[channel_id]();
}
} while ( 0U != status );
NVIC_ClearPendingIRQ( DMA_IRQn );
}
/***************************************************************************//**
* See mss_pdma.h for description of this function.
*/
void PDMA_set_irq_handler
(
pdma_channel_id_t channel_id,
pdma_channel_isr_t handler
)
{
/* Save address of handler function in PDMA driver ISR lookup table. */
g_pdma_isr_table[channel_id] = handler;
/* Enable PDMA channel's interrupt. */
PDMA->CHANNEL[channel_id].CRTL |= PDMA_IRQ_ENABLE_MASK;
/* Enable PDMA interrupt in Cortex-M3 NVIC. */
NVIC_EnableIRQ( DMA_IRQn );
}
/***************************************************************************//**
* See mss_pdma.h for description of this function.
*/
void PDMA_enable_irq( pdma_channel_id_t channel_id )
{
PDMA->CHANNEL[channel_id].CRTL |= PDMA_IRQ_ENABLE_MASK;
NVIC_EnableIRQ( DMA_IRQn );
}
/***************************************************************************//**
* See mss_pdma.h for description of this function.
*/
void PDMA_clear_irq
(
pdma_channel_id_t channel_id
)
{
/* Clear interrupt in PDMA controller. */
PDMA->CHANNEL[channel_id].CRTL |= CLEAR_PORT_A_DONE_MASK;
PDMA->CHANNEL[channel_id].CRTL |= CLEAR_PORT_B_DONE_MASK;
/* Clear interrupt in Cortex-M3 NVIC. */
NVIC_ClearPendingIRQ( DMA_IRQn );
}
#ifdef __cplusplus
}
#endif