1 /*******************************************************************************
\r
2 * (c) Copyright 2008 Actel Corporation. All rights reserved.
\r
4 * SmartFusion microcontroller subsystem Peripheral DMA bare metal software
\r
5 * driver implementation.
\r
7 * SVN $Revision: 2110 $
\r
8 * SVN $Date: 2010-02-05 15:24:19 +0000 (Fri, 05 Feb 2010) $
\r
10 #include "mss_pdma.h"
\r
11 #include "../../CMSIS/mss_assert.h"
\r
17 #if defined(__GNUC__)
\r
18 __attribute__((__interrupt__)) void DMA_IRQHandler( void );
\r
20 void DMA_IRQHandler( void );
\r
23 /***************************************************************************//**
\r
24 Offset of the posted writes WRITE_ADJ bits in a PDMA channel's configuration
\r
27 #define CHANNEL_N_POSTED_WRITE_ADJUST_SHIFT 14
\r
29 /*-------------------------------------------------------------------------*//**
\r
30 * Look-up table use to derice a channel's control register value from the
\r
31 * requested source/destination. This table is incexed on the pdma_src_dest_t
\r
34 #define CHANNEL_N_CTRL_PDMA_MASK (uint32_t)0x00000001
\r
35 #define CHANNEL_N_PERIPH_SELECT_SHIFT (uint32_t)23
\r
36 #define CHANNEL_N_DIRECTION_MASK (uint32_t)0x00000002
\r
38 const uint32_t src_dest_to_ctrl_reg_lut[] =
\r
40 CHANNEL_N_CTRL_PDMA_MASK, /* PDMA_FROM_UART_0 */
\r
41 CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)1 << CHANNEL_N_PERIPH_SELECT_SHIFT) | CHANNEL_N_DIRECTION_MASK, /* PDMA_TO_UART_0 */
\r
42 CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)2 << CHANNEL_N_PERIPH_SELECT_SHIFT), /* PDMA_FROM_UART_1 */
\r
43 CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)3 << CHANNEL_N_PERIPH_SELECT_SHIFT) | CHANNEL_N_DIRECTION_MASK, /* PDMA_TO_UART_1 */
\r
44 CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)4 << CHANNEL_N_PERIPH_SELECT_SHIFT), /* PDMA_FROM_SPI_0 */
\r
45 CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)5 << CHANNEL_N_PERIPH_SELECT_SHIFT) | CHANNEL_N_DIRECTION_MASK, /* PDMA_TO_SPI_0 */
\r
46 CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)6 << CHANNEL_N_PERIPH_SELECT_SHIFT), /* PDMA_FROM_SPI_1 */
\r
47 CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)7 << CHANNEL_N_PERIPH_SELECT_SHIFT) | CHANNEL_N_DIRECTION_MASK, /* PDMA_TO_SPI_1 */
\r
48 CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)8 << CHANNEL_N_PERIPH_SELECT_SHIFT), /* PDMA_FROM_FPGA_1 */
\r
49 CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)8 << CHANNEL_N_PERIPH_SELECT_SHIFT) | CHANNEL_N_DIRECTION_MASK, /* PDMA_TO_FPGA_1 */
\r
50 CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)9 << CHANNEL_N_PERIPH_SELECT_SHIFT), /* PDMA_FROM_FPGA_0 */
\r
51 CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)9 << CHANNEL_N_PERIPH_SELECT_SHIFT) | CHANNEL_N_DIRECTION_MASK, /* PDMA_TO_FPGA_0 */
\r
52 CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)10 << CHANNEL_N_PERIPH_SELECT_SHIFT) | CHANNEL_N_DIRECTION_MASK, /* PDMA_TO_ACE */
\r
53 CHANNEL_N_CTRL_PDMA_MASK | ( (uint32_t)11 << CHANNEL_N_PERIPH_SELECT_SHIFT) /* PDMA_FROM_ACE */
\r
56 /*-------------------------------------------------------------------------*//**
\r
59 #define PDMA_MASTER_ENABLE (uint32_t)0x04
\r
60 #define PDMA_SOFT_RESET (uint32_t)0x20
\r
62 /*-------------------------------------------------------------------------*//**
\r
65 #define NB_OF_PDMA_CHANNELS 8
\r
67 #define NEXT_CHANNEL_A 0U
\r
68 #define NEXT_CHANNEL_B 1U
\r
70 #define CHANNEL_STOPPED 0U
\r
71 #define CHANNEL_STARTED 1U
\r
73 static uint8_t g_pdma_next_channel[NB_OF_PDMA_CHANNELS];
\r
74 static uint8_t g_pdma_started_a[NB_OF_PDMA_CHANNELS];
\r
75 static uint8_t g_pdma_started_b[NB_OF_PDMA_CHANNELS];
\r
76 static pdma_channel_isr_t g_pdma_isr_table[NB_OF_PDMA_CHANNELS];
\r
77 static const uint16_t g_pdma_status_mask[NB_OF_PDMA_CHANNELS] =
\r
79 (uint16_t)0x0003, /* PDMA_CHANNEL_0 */
\r
80 (uint16_t)0x000C, /* PDMA_CHANNEL_1 */
\r
81 (uint16_t)0x0030, /* PDMA_CHANNEL_2 */
\r
82 (uint16_t)0x00C0, /* PDMA_CHANNEL_3 */
\r
83 (uint16_t)0x0300, /* PDMA_CHANNEL_4 */
\r
84 (uint16_t)0x0C00, /* PDMA_CHANNEL_5 */
\r
85 (uint16_t)0x3000, /* PDMA_CHANNEL_6 */
\r
86 (uint16_t)0xC000, /* PDMA_CHANNEL_7 */
\r
91 /***************************************************************************//**
\r
92 * See mss_pdma.h for description of this function.
\r
94 void PDMA_init( void )
\r
98 /* Enable PDMA master access to comms matrix. */
\r
99 SYSREG->AHB_MATRIX_CR |= PDMA_MASTER_ENABLE;
\r
101 /* Reset PDMA block. */
\r
102 SYSREG->SOFT_RST_CR |= PDMA_SOFT_RESET;
\r
104 /* Clear any previously pended MSS PDMA interrupt */
\r
105 NVIC_ClearPendingIRQ( DMA_IRQn );
\r
107 /* Take PDMA controller out of reset*/
\r
108 SYSREG->SOFT_RST_CR &= ~PDMA_SOFT_RESET;
\r
110 /* Initialize channels state information. */
\r
111 for ( i = 0; i < NB_OF_PDMA_CHANNELS; ++i )
\r
113 g_pdma_next_channel[i] = NEXT_CHANNEL_A;
\r
114 g_pdma_started_a[i] = CHANNEL_STOPPED;
\r
115 g_pdma_started_b[i] = CHANNEL_STOPPED;
\r
116 g_pdma_isr_table[i] = 0;
\r
120 /***************************************************************************//**
\r
121 * See mss_pdma.h for description of this function.
\r
123 #define CHANNEL_RESET_MASK (uint32_t)0x00000020
\r
125 void PDMA_configure
\r
127 pdma_channel_id_t channel_id,
\r
128 pdma_src_dest_t src_dest,
\r
129 uint32_t channel_cfg,
\r
130 uint8_t write_adjust
\r
133 /* Reset the channel. */
\r
134 PDMA->CHANNEL[channel_id].CRTL |= CHANNEL_RESET_MASK;
\r
135 PDMA->CHANNEL[channel_id].CRTL &= ~CHANNEL_RESET_MASK;
\r
137 /* Configure PDMA channel's data source and destination. */
\r
138 if ( src_dest != PDMA_MEM_TO_MEM )
\r
140 PDMA->CHANNEL[channel_id].CRTL |= src_dest_to_ctrl_reg_lut[src_dest];
\r
143 /* Configure PDMA channel trnasfer size, priority, source and destination address increment. */
\r
144 PDMA->CHANNEL[channel_id].CRTL |= channel_cfg;
\r
146 /* Posted write adjust. */
\r
147 PDMA->CHANNEL[channel_id].CRTL |= ((uint32_t)write_adjust << CHANNEL_N_POSTED_WRITE_ADJUST_SHIFT);
\r
150 /***************************************************************************//**
\r
151 * See mss_pdma.h for description of this function.
\r
153 #define PAUSE_MASK (uint32_t)0x00000010
\r
155 #define BUFFER_B_SELECT_MASK (uint32_t)0x00000004
\r
157 #define CLEAR_PORT_A_DONE_MASK (uint32_t)0x00000080
\r
158 #define CLEAR_PORT_B_DONE_MASK (uint32_t)0x00000100
\r
160 #define PORT_A_COMPLETE_MASK (uint32_t)0x00000001
\r
161 #define PORT_B_COMPLETE_MASK (uint32_t)0x00000002
\r
165 pdma_channel_id_t channel_id,
\r
167 uint32_t dest_addr,
\r
168 uint16_t transfer_count
\r
171 /* Pause transfer. */
\r
172 PDMA->CHANNEL[channel_id].CRTL |= PAUSE_MASK;
\r
174 /* Clear complete transfers. */
\r
175 if ( PDMA->CHANNEL[channel_id].STATUS & PORT_A_COMPLETE_MASK )
\r
177 PDMA->CHANNEL[channel_id].CRTL |= CLEAR_PORT_A_DONE_MASK;
\r
178 g_pdma_started_a[channel_id] = CHANNEL_STOPPED;
\r
180 if ( PDMA->CHANNEL[channel_id].STATUS & PORT_B_COMPLETE_MASK )
\r
182 PDMA->CHANNEL[channel_id].CRTL |= CLEAR_PORT_B_DONE_MASK;
\r
183 g_pdma_started_b[channel_id] = CHANNEL_STOPPED;
\r
186 /* Load source, destination and transfer count. */
\r
187 if ( PDMA->CHANNEL[channel_id].STATUS & BUFFER_B_SELECT_MASK )
\r
189 g_pdma_next_channel[channel_id] = NEXT_CHANNEL_A;
\r
190 g_pdma_started_b[channel_id] = CHANNEL_STARTED;
\r
192 PDMA->CHANNEL[channel_id].BUFFER_B_SRC_ADDR = src_addr;
\r
193 PDMA->CHANNEL[channel_id].BUFFER_B_DEST_ADDR = dest_addr;
\r
194 PDMA->CHANNEL[channel_id].BUFFER_B_TRANSFER_COUNT = transfer_count;
\r
198 g_pdma_next_channel[channel_id] = NEXT_CHANNEL_B;
\r
199 g_pdma_started_a[channel_id] = CHANNEL_STARTED;
\r
201 PDMA->CHANNEL[channel_id].BUFFER_A_SRC_ADDR = src_addr;
\r
202 PDMA->CHANNEL[channel_id].BUFFER_A_DEST_ADDR = dest_addr;
\r
203 PDMA->CHANNEL[channel_id].BUFFER_A_TRANSFER_COUNT = transfer_count;
\r
206 /* Start transfer */
\r
207 PDMA->CHANNEL[channel_id].CRTL &= ~PAUSE_MASK;
\r
210 /***************************************************************************//**
\r
211 * See mss_pdma.h for description of this function.
\r
213 void PDMA_load_next_buffer
\r
215 pdma_channel_id_t channel_id,
\r
217 uint32_t dest_addr,
\r
218 uint16_t transfer_count
\r
221 if ( NEXT_CHANNEL_A == g_pdma_next_channel[channel_id] )
\r
223 /* Wait for channel A current transfer completion. */
\r
224 if ( CHANNEL_STARTED == g_pdma_started_a[channel_id] )
\r
226 uint32_t completed;
\r
227 uint32_t channel_mask;
\r
228 channel_mask = (uint32_t)1 << ((uint32_t)channel_id * 2U);
\r
230 completed = PDMA->BUFFER_STATUS & channel_mask;
\r
231 } while( !completed );
\r
232 PDMA->CHANNEL[channel_id].CRTL |= CLEAR_PORT_A_DONE_MASK;
\r
234 /* Load source, destination and transfer count. */
\r
235 PDMA->CHANNEL[channel_id].BUFFER_A_SRC_ADDR = src_addr;
\r
236 PDMA->CHANNEL[channel_id].BUFFER_A_DEST_ADDR = dest_addr;
\r
237 PDMA->CHANNEL[channel_id].BUFFER_A_TRANSFER_COUNT = transfer_count;
\r
239 /* Update channel state information. */
\r
240 g_pdma_next_channel[channel_id] = NEXT_CHANNEL_B;
\r
241 g_pdma_started_a[channel_id] = CHANNEL_STARTED;
\r
245 /* Wait for channel B current transfer completion. */
\r
246 if ( CHANNEL_STARTED == g_pdma_started_b[channel_id] )
\r
248 uint32_t completed;
\r
249 uint32_t channel_mask;
\r
250 channel_mask = (uint32_t)1 << (((uint32_t)channel_id * 2U) + 1U);
\r
252 completed = PDMA->BUFFER_STATUS & channel_mask;
\r
253 } while( !completed );
\r
254 PDMA->CHANNEL[channel_id].CRTL |= CLEAR_PORT_B_DONE_MASK;
\r
256 /* Load source, destination and transfer count. */
\r
257 PDMA->CHANNEL[channel_id].BUFFER_B_SRC_ADDR = src_addr;
\r
258 PDMA->CHANNEL[channel_id].BUFFER_B_DEST_ADDR = dest_addr;
\r
259 PDMA->CHANNEL[channel_id].BUFFER_B_TRANSFER_COUNT = transfer_count;
\r
261 /* Update channel state information. */
\r
262 g_pdma_next_channel[channel_id] = NEXT_CHANNEL_A;
\r
263 g_pdma_started_b[channel_id] = CHANNEL_STARTED;
\r
267 /***************************************************************************//**
\r
268 * See mss_pdma.h for description of this function.
\r
270 uint32_t PDMA_status
\r
272 pdma_channel_id_t channel_id
\r
277 status = PDMA->CHANNEL[channel_id].STATUS & (PORT_A_COMPLETE_MASK | PORT_B_COMPLETE_MASK);
\r
282 /***************************************************************************//**
\r
285 #define CHANNEL_0_STATUS_BITS_MASK (uint16_t)0x0003
\r
286 #define CHANNEL_1_STATUS_BITS_MASK (uint16_t)0x000C
\r
287 #define CHANNEL_2_STATUS_BITS_MASK (uint16_t)0x0030
\r
288 #define CHANNEL_3_STATUS_BITS_MASK (uint16_t)0x00C0
\r
289 #define CHANNEL_4_STATUS_BITS_MASK (uint16_t)0x0300
\r
290 #define CHANNEL_5_STATUS_BITS_MASK (uint16_t)0x0C00
\r
291 #define CHANNEL_6_STATUS_BITS_MASK (uint16_t)0x3000
\r
292 #define CHANNEL_7_STATUS_BITS_MASK (uint16_t)0xC000
\r
294 static pdma_channel_id_t get_channel_id_from_status
\r
299 pdma_channel_id_t channel_id = PDMA_CHANNEL_0;
\r
301 if ( status & CHANNEL_0_STATUS_BITS_MASK )
\r
303 channel_id = PDMA_CHANNEL_0;
\r
305 else if ( status & CHANNEL_1_STATUS_BITS_MASK )
\r
307 channel_id = PDMA_CHANNEL_1;
\r
309 else if ( status & CHANNEL_2_STATUS_BITS_MASK )
\r
311 channel_id = PDMA_CHANNEL_2;
\r
313 else if ( status & CHANNEL_3_STATUS_BITS_MASK )
\r
315 channel_id = PDMA_CHANNEL_3;
\r
317 else if ( status & CHANNEL_4_STATUS_BITS_MASK )
\r
319 channel_id = PDMA_CHANNEL_4;
\r
321 else if ( status & CHANNEL_5_STATUS_BITS_MASK )
\r
323 channel_id = PDMA_CHANNEL_5;
\r
325 else if ( status & CHANNEL_6_STATUS_BITS_MASK )
\r
327 channel_id = PDMA_CHANNEL_6;
\r
329 else if ( status & CHANNEL_7_STATUS_BITS_MASK )
\r
331 channel_id = PDMA_CHANNEL_7;
\r
340 /***************************************************************************//**
\r
343 #if defined(__GNUC__)
\r
344 __attribute__((__interrupt__)) void DMA_IRQHandler( void )
\r
346 void DMA_IRQHandler( void )
\r
350 pdma_channel_id_t channel_id;
\r
352 status = (uint16_t)PDMA->BUFFER_STATUS;
\r
355 channel_id = get_channel_id_from_status( status );
\r
356 status &= (uint16_t)~g_pdma_status_mask[channel_id];
\r
357 if ( 0 != g_pdma_isr_table[channel_id])
\r
359 g_pdma_isr_table[channel_id]();
\r
361 } while ( 0U != status );
\r
363 NVIC_ClearPendingIRQ( DMA_IRQn );
\r
366 /***************************************************************************//**
\r
367 * See mss_pdma.h for description of this function.
\r
369 void PDMA_set_irq_handler
\r
371 pdma_channel_id_t channel_id,
\r
372 pdma_channel_isr_t handler
\r
375 /* Save address of handler function in PDMA driver ISR lookup table. */
\r
376 g_pdma_isr_table[channel_id] = handler;
\r
378 /* Enable PDMA channel's interrupt. */
\r
379 PDMA->CHANNEL[channel_id].CRTL |= PDMA_IRQ_ENABLE_MASK;
\r
381 /* Enable PDMA interrupt in Cortex-M3 NVIC. */
\r
382 NVIC_EnableIRQ( DMA_IRQn );
\r
385 /***************************************************************************//**
\r
386 * See mss_pdma.h for description of this function.
\r
388 void PDMA_enable_irq( pdma_channel_id_t channel_id )
\r
390 PDMA->CHANNEL[channel_id].CRTL |= PDMA_IRQ_ENABLE_MASK;
\r
391 NVIC_EnableIRQ( DMA_IRQn );
\r
394 /***************************************************************************//**
\r
395 * See mss_pdma.h for description of this function.
\r
397 void PDMA_clear_irq
\r
399 pdma_channel_id_t channel_id
\r
402 /* Clear interrupt in PDMA controller. */
\r
403 PDMA->CHANNEL[channel_id].CRTL |= CLEAR_PORT_A_DONE_MASK;
\r
404 PDMA->CHANNEL[channel_id].CRTL |= CLEAR_PORT_B_DONE_MASK;
\r
406 /* Clear interrupt in Cortex-M3 NVIC. */
\r
407 NVIC_ClearPendingIRQ( DMA_IRQn );
\r