/******************************************************************************* | |
* (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 | |