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