1 /*******************************************************************************
\r
2 * (c) Copyright 2010-2013 Microsemi SoC Products Group. All rights reserved.
\r
4 * SmartFusion2 MSS HPDMA bare metal driver implementation.
\r
6 * SVN $Revision: 5148 $
\r
7 * SVN $Date: 2013-02-27 16:42:01 +0000 (Wed, 27 Feb 2013) $
\r
9 #include "mss_hpdma.h"
\r
11 /*==============================================================================
\r
13 *------------------------------------------------------------------------------
\r
14 This SmartFusion2 MSS HPDMA driver only makes use of the first HPDMA
\r
15 descriptor. The rationale for this choice is that the pause and clear bits of
\r
16 each individual descriptor have undocumented side effects to the other
\r
17 descriptors in the HPDMA hardware block. This prevents independent operations
\r
18 of the descriptors in particular when it comes to error handling where error
\r
19 recovery requires clearing a descriptor. An error on one descriptor would
\r
20 result in aborting transfers on the other descriptors. Likewise, it is not
\r
21 possible to pause individual descriptors. Setting the pause bit in one
\r
22 descriptor results in all descriptors being paused.
\r
29 /*-------------------------------------------------------------------------*//**
\r
32 #define HPDMA_ENABLE 1u
\r
33 #define HPDMA_DISABLE 0u
\r
34 #define NULL_HANDLER ((mss_hpdma_handler_t)0)
\r
36 /* 64 K DMA Transfer */
\r
37 #define MAX_SIZE_PER_DMA_XFR 0x10000u
\r
41 /*-------------------------------------------------------------------------*//**
\r
44 #define HPDMA_SOFT_RESET 0x00020000u
\r
45 #define HPDMA_XFR_SIZE_MASK 0xFFFF0000u
\r
47 #define HPDMACR_XFR_CMP_INT_MASK 0x00100000u
\r
48 #define HPDMACR_XFR_ERR_INT_MASK 0x00200000u
\r
49 #define HPDMACR_NON_WORD_INT_MASK 0x00400000u
\r
51 #define HPDMACR_ALL_IRQ_ENABLE_MASK (HPDMACR_XFR_CMP_INT_MASK | HPDMACR_XFR_ERR_INT_MASK | HPDMACR_NON_WORD_INT_MASK)
\r
53 #define HPDMAICR_CLEAR_ALL_IRQ 0x000000FFu
\r
55 #define HPDMAEDR_DCP_ERR_MASK 0x00000100u
\r
57 #define HPDMAEDR_NON_WORD_ERR_MASK 0x00001000u
\r
59 /*-------------------------------------------------------------------------*//**
\r
60 Current transfer state information.
\r
62 typedef struct hpdma_xfer_info
\r
64 hpdma_status_t state;
\r
65 mss_hpdma_handler_t completion_handler;
\r
70 } hpdma_xfer_info_t;
\r
72 /*-------------------------------------------------------------------------*//**
\r
73 Current transfer state information.
\r
75 static hpdma_xfer_info_t g_transfer;
\r
77 /*-------------------------------------------------------------------------*//**
\r
78 Interrupt service routines function prototypes.
\r
79 The name of these interrupt service routines must match the name of the ISRs
\r
80 defined in startup_m2sxxx.s distributed as part of the SmartFusion2 CMSIS.
\r
82 #if defined(__GNUC__)
\r
83 __attribute__((__interrupt__)) void HPDMA_Complete_IRQHandler(void);
\r
85 void HPDMA_Complete_IRQHandler(void);
\r
88 #if defined(__GNUC__)
\r
89 __attribute__((__interrupt__)) void HPDMA_Error_IRQHandler(void);
\r
91 void HPDMA_Error_IRQHandler(void);
\r
94 /*-------------------------------------------------------------------------*//**
\r
95 * See "mss_hpdma.h" for details of how to use this function.
\r
103 /* Reset HPDMA block. */
\r
104 SYSREG->SOFT_RST_CR |= HPDMA_SOFT_RESET;
\r
106 /* Take HPDMA controller out of reset. */
\r
107 SYSREG->SOFT_RST_CR &= ~HPDMA_SOFT_RESET;
\r
109 /* Initialize data structures. */
\r
110 g_transfer.xfr_size = 0u;
\r
111 g_transfer.completion_handler = NULL_HANDLER;
\r
112 g_transfer.state = HPDMA_COMPLETED;
\r
114 /* Disable Interrupt. */
\r
115 HPDMA->Descriptor[DESC0].HPDMACR_REG &= ~HPDMACR_ALL_IRQ_ENABLE_MASK;
\r
117 /* Clear any previously pended MSS HPDMA interrupt. */
\r
118 NVIC_ClearPendingIRQ(HPDMA_Error_IRQn);
\r
119 NVIC_ClearPendingIRQ(HPDMA_Complete_IRQn);
\r
121 /* Clear all interrupts. */
\r
122 HPDMA->HPDMAICR_REG = HPDMAICR_CLEAR_ALL_IRQ;
\r
125 /*-------------------------------------------------------------------------*//**
\r
126 * See "mss_hpdma.h" for details of how to use this function.
\r
132 uint32_t dest_addr,
\r
133 uint32_t transfer_size,
\r
134 uint8_t transfer_dir
\r
137 /* Pause transfer. */
\r
138 HPDMA_BITBAND->Descriptor[DESC0].HPDMACR_DCP_PAUSE = HPDMA_ENABLE;
\r
141 * Disable all interrupts for the current descriptor. Treat this function as a
\r
142 * critical section. Pausing the descriptor transfer is not sufficient in case
\r
143 * of non-aligned errors.
\r
145 HPDMA->Descriptor[DESC0].HPDMACR_REG &= ~HPDMACR_ALL_IRQ_ENABLE_MASK;
\r
147 /* Set descriptor transfer direction */
\r
148 HPDMA_BITBAND->Descriptor[DESC0].HPDMACR_DCP_XFR_DIR = transfer_dir;
\r
150 /* Store Source and destination Address */
\r
151 HPDMA->Descriptor[DESC0].HPDMASAR_REG = src_addr;
\r
152 HPDMA->Descriptor[DESC0].HPDMADAR_REG = dest_addr;
\r
154 /* Set the transfer size to 64K */
\r
155 HPDMA->Descriptor[DESC0].HPDMACR_REG &= HPDMA_XFR_SIZE_MASK;
\r
157 g_transfer.state = HPDMA_IN_PROGRESS;
\r
158 if(transfer_size <= MAX_SIZE_PER_DMA_XFR)
\r
160 /* Set descriptor transfer size */
\r
161 HPDMA->Descriptor[DESC0].HPDMACR_REG |= (uint16_t)transfer_size;
\r
162 g_transfer.xfr_size = 0u;
\r
166 g_transfer.xfr_size = transfer_size - MAX_SIZE_PER_DMA_XFR;
\r
167 g_transfer.des_addr = dest_addr + MAX_SIZE_PER_DMA_XFR;
\r
168 g_transfer.src_addr = src_addr + MAX_SIZE_PER_DMA_XFR;
\r
169 g_transfer.xfr_dir = transfer_dir;
\r
172 /* Enable interrupts for the requested descriptor. */
\r
173 HPDMA->Descriptor[DESC0].HPDMACR_REG |= HPDMACR_ALL_IRQ_ENABLE_MASK;
\r
174 NVIC_EnableIRQ(HPDMA_Complete_IRQn);
\r
175 NVIC_EnableIRQ(HPDMA_Error_IRQn);
\r
177 /* Set valid descriptor to start transfer */
\r
178 HPDMA_BITBAND->Descriptor[DESC0].HPDMACR_DCP_VALID = HPDMA_ENABLE;
\r
180 /* Start transfer */
\r
181 HPDMA_BITBAND->Descriptor[DESC0].HPDMACR_DCP_PAUSE = HPDMA_DISABLE;
\r
184 /*-------------------------------------------------------------------------*//**
\r
185 * See "mss_hpdma.h" for details of how to use this function.
\r
193 /* Pause transfer */
\r
194 HPDMA_BITBAND->Descriptor[DESC0].HPDMACR_DCP_PAUSE = HPDMA_ENABLE;
\r
197 /*-------------------------------------------------------------------------*//**
\r
198 * See "mss_hpdma.h" for details of how to use this function.
\r
206 /* Resume transfer */
\r
207 HPDMA_BITBAND->Descriptor[DESC0].HPDMACR_DCP_PAUSE = HPDMA_DISABLE;
\r
210 /*-------------------------------------------------------------------------*//**
\r
211 * See "mss_hpdma.h" for details of how to use this function.
\r
219 HPDMA_BITBAND->Descriptor[DESC0].HPDMACR_DCP_CLR = HPDMA_ENABLE;
\r
222 /*-------------------------------------------------------------------------*//**
\r
223 * See "mss_hpdma.h" for details of how to use this function.
\r
226 MSS_HPDMA_get_transfer_status
\r
231 return g_transfer.state;
\r
234 /*-------------------------------------------------------------------------*//**
\r
235 * See "mss_hpdma.h" for details of how to use this function.
\r
237 void MSS_HPDMA_set_handler
\r
239 mss_hpdma_handler_t handler
\r
242 if(handler == NULL_HANDLER)
\r
244 g_transfer.completion_handler = NULL_HANDLER;
\r
248 g_transfer.completion_handler = handler;
\r
252 /*------------------------------------------------------------------------------
\r
253 HPDMA Transfer complete service routine.
\r
254 This function will be called as a result of any descriptor transfer
\r
255 is completed by HPDMA controller .
\r
257 #if defined(__GNUC__)
\r
258 __attribute__((__interrupt__)) void HPDMA_Complete_IRQHandler(void)
\r
260 void HPDMA_Complete_IRQHandler(void)
\r
263 /* Clear interrupt */
\r
264 HPDMA_BITBAND->HPDMAICR_CLR_XFR_INT[DESC0] = HPDMA_ENABLE;
\r
266 if(g_transfer.xfr_size > 0u)
\r
268 MSS_HPDMA_start(g_transfer.src_addr,
\r
269 g_transfer.des_addr,
\r
270 g_transfer.xfr_size,
\r
271 g_transfer.xfr_dir);
\r
275 g_transfer.state = HPDMA_COMPLETED;
\r
276 if(g_transfer.completion_handler != NULL_HANDLER)
\r
278 (*(g_transfer.completion_handler))(HPDMA_COMPLETED);
\r
283 /*------------------------------------------------------------------------------
\r
284 HPDMA Transfer Error service routine.
\r
285 This function will be called as a result of any descriptor transfer
\r
286 has an error condition or it there is non word transfer size error.
\r
288 #if defined(__GNUC__)
\r
289 __attribute__((__interrupt__)) void HPDMA_Error_IRQHandler(void)
\r
291 void HPDMA_Error_IRQHandler(void)
\r
294 uint32_t error_bits;
\r
297 * Handle source/destination errors.
\r
299 error_bits = HPDMA->HPDMAEDR_REG & HPDMAEDR_DCP_ERR_MASK;
\r
300 if(error_bits != 0u)
\r
302 if(g_transfer.completion_handler != NULL_HANDLER)
\r
304 if(HPDMA_BITBAND->Descriptor[DESC0].HPDMASR_DCP_SERR)
\r
306 g_transfer.state = HPDMA_SOURCE_ERROR;
\r
307 (*(g_transfer.completion_handler))(HPDMA_SOURCE_ERROR);
\r
310 if(HPDMA_BITBAND->Descriptor[DESC0].HPDMASR_DCP_DERR)
\r
312 g_transfer.state = HPDMA_DESTINATION_ERROR;
\r
313 (*(g_transfer.completion_handler))(HPDMA_DESTINATION_ERROR);
\r
316 /* Abort transfer. */
\r
317 HPDMA_BITBAND->Descriptor[DESC0].HPDMACR_DCP_CLR = HPDMA_ENABLE;
\r
318 /* Clear interrupt. */
\r
319 HPDMA_BITBAND->HPDMAICR_CLR_XFR_INT[DESC0] = HPDMA_ENABLE;
\r
323 * Handle non word aligned errors.
\r
325 error_bits = HPDMA->HPDMAEDR_REG & HPDMAEDR_NON_WORD_ERR_MASK;
\r
326 if(error_bits != 0u)
\r
328 g_transfer.state = HPDMA_WORD_ALIGN_ERROR;
\r
329 /* Call handler. */
\r
330 if(g_transfer.completion_handler != NULL_HANDLER)
\r
332 (*(g_transfer.completion_handler))(HPDMA_WORD_ALIGN_ERROR);
\r
334 /* Abort transfer. */
\r
335 HPDMA_BITBAND->Descriptor[DESC0].HPDMACR_DCP_CLR = HPDMA_ENABLE;
\r
336 /* Clear interrupt. */
\r
337 HPDMA_BITBAND->HPDMAICR_NON_WORD_INT[DESC0] = HPDMA_ENABLE;
\r