]> git.sur5r.net Git - freertos/blob
f43eadb4cedf70d7360307136e263ca2067dd6f0
[freertos] /
1 /*******************************************************************************\r
2  * (c) Copyright 2010-2013 Microsemi SoC Products Group.  All rights reserved.\r
3  * \r
4  * SmartFusion2 MSS HPDMA bare metal driver implementation.\r
5  *\r
6  * SVN $Revision: 5148 $\r
7  * SVN $Date: 2013-02-27 16:42:01 +0000 (Wed, 27 Feb 2013) $\r
8  */\r
9 #include "mss_hpdma.h"\r
10 \r
11 /*==============================================================================\r
12  * Design Notes\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
23  */\r
24 \r
25 #ifdef __cplusplus\r
26 extern "C" {\r
27 #endif \r
28 \r
29 /*-------------------------------------------------------------------------*//**\r
30  * Constants\r
31  */\r
32 #define HPDMA_ENABLE    1u\r
33 #define HPDMA_DISABLE   0u\r
34 #define NULL_HANDLER    ((mss_hpdma_handler_t)0)\r
35 \r
36 /* 64 K DMA Transfer */\r
37 #define MAX_SIZE_PER_DMA_XFR   0x10000u\r
38 \r
39 #define DESC0       0u\r
40 \r
41 /*-------------------------------------------------------------------------*//**\r
42  * Mask Constants\r
43  */\r
44 #define HPDMA_SOFT_RESET            0x00020000u\r
45 #define HPDMA_XFR_SIZE_MASK         0xFFFF0000u\r
46 \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
50 \r
51 #define HPDMACR_ALL_IRQ_ENABLE_MASK (HPDMACR_XFR_CMP_INT_MASK | HPDMACR_XFR_ERR_INT_MASK | HPDMACR_NON_WORD_INT_MASK)\r
52 \r
53 #define HPDMAICR_CLEAR_ALL_IRQ      0x000000FFu\r
54 \r
55 #define HPDMAEDR_DCP_ERR_MASK           0x00000100u\r
56 \r
57 #define HPDMAEDR_NON_WORD_ERR_MASK      0x00001000u\r
58 \r
59 /*-------------------------------------------------------------------------*//**\r
60   Current transfer state information.\r
61  */\r
62 typedef struct hpdma_xfer_info\r
63 {\r
64     hpdma_status_t          state;\r
65     mss_hpdma_handler_t     completion_handler;\r
66     uint32_t                src_addr;\r
67     uint32_t                des_addr;\r
68     uint32_t                xfr_size;\r
69     uint8_t                 xfr_dir;\r
70 } hpdma_xfer_info_t;\r
71 \r
72 /*-------------------------------------------------------------------------*//**\r
73   Current transfer state information.\r
74  */\r
75 static hpdma_xfer_info_t g_transfer;\r
76 \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
81  */\r
82 #if defined(__GNUC__)\r
83 __attribute__((__interrupt__)) void HPDMA_Complete_IRQHandler(void);\r
84 #else\r
85 void HPDMA_Complete_IRQHandler(void);\r
86 #endif \r
87 \r
88 #if defined(__GNUC__)\r
89 __attribute__((__interrupt__)) void HPDMA_Error_IRQHandler(void);\r
90 #else\r
91 void HPDMA_Error_IRQHandler(void);\r
92 #endif \r
93 \r
94 /*-------------------------------------------------------------------------*//**\r
95  * See "mss_hpdma.h" for details of how to use this function.\r
96  */\r
97 void\r
98 MSS_HPDMA_init\r
99 (\r
100     void\r
101 )\r
102 {\r
103     /* Reset HPDMA block. */\r
104     SYSREG->SOFT_RST_CR |= HPDMA_SOFT_RESET;\r
105 \r
106     /* Take HPDMA controller out of reset. */\r
107     SYSREG->SOFT_RST_CR &= ~HPDMA_SOFT_RESET;\r
108 \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
113     \r
114     /* Disable Interrupt. */\r
115     HPDMA->Descriptor[DESC0].HPDMACR_REG &= ~HPDMACR_ALL_IRQ_ENABLE_MASK;\r
116 \r
117     /* Clear any previously pended MSS HPDMA interrupt. */\r
118     NVIC_ClearPendingIRQ(HPDMA_Error_IRQn);\r
119     NVIC_ClearPendingIRQ(HPDMA_Complete_IRQn);\r
120     \r
121     /* Clear all interrupts. */\r
122     HPDMA->HPDMAICR_REG = HPDMAICR_CLEAR_ALL_IRQ;\r
123 }\r
124 \r
125 /*-------------------------------------------------------------------------*//**\r
126  * See "mss_hpdma.h" for details of how to use this function.\r
127  */\r
128 void\r
129 MSS_HPDMA_start\r
130 (\r
131     uint32_t src_addr,\r
132     uint32_t dest_addr,\r
133     uint32_t transfer_size,\r
134     uint8_t transfer_dir\r
135 )\r
136 {\r
137     /* Pause transfer. */\r
138     HPDMA_BITBAND->Descriptor[DESC0].HPDMACR_DCP_PAUSE = HPDMA_ENABLE;\r
139     \r
140     /*\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
144      */\r
145     HPDMA->Descriptor[DESC0].HPDMACR_REG &= ~HPDMACR_ALL_IRQ_ENABLE_MASK;\r
146     \r
147     /* Set descriptor transfer direction */\r
148     HPDMA_BITBAND->Descriptor[DESC0].HPDMACR_DCP_XFR_DIR = transfer_dir;\r
149 \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
153 \r
154     /* Set the transfer size to 64K */\r
155     HPDMA->Descriptor[DESC0].HPDMACR_REG &= HPDMA_XFR_SIZE_MASK;\r
156 \r
157     g_transfer.state = HPDMA_IN_PROGRESS;\r
158     if(transfer_size <= MAX_SIZE_PER_DMA_XFR)\r
159     {\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
163     }\r
164     else\r
165     {\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
170     }\r
171 \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
176     \r
177     /* Set valid descriptor to start transfer */\r
178     HPDMA_BITBAND->Descriptor[DESC0].HPDMACR_DCP_VALID = HPDMA_ENABLE;\r
179 \r
180     /* Start transfer */\r
181     HPDMA_BITBAND->Descriptor[DESC0].HPDMACR_DCP_PAUSE = HPDMA_DISABLE;\r
182 }\r
183 \r
184 /*-------------------------------------------------------------------------*//**\r
185  * See "mss_hpdma.h" for details of how to use this function.\r
186  */\r
187 void\r
188 MSS_HPDMA_pause\r
189 (\r
190     void\r
191 )\r
192 {\r
193     /* Pause transfer */\r
194     HPDMA_BITBAND->Descriptor[DESC0].HPDMACR_DCP_PAUSE = HPDMA_ENABLE;\r
195 }\r
196 \r
197 /*-------------------------------------------------------------------------*//**\r
198  * See "mss_hpdma.h" for details of how to use this function.\r
199  */\r
200 void\r
201 MSS_HPDMA_resume\r
202 (\r
203     void\r
204 )\r
205 {\r
206     /* Resume transfer */\r
207     HPDMA_BITBAND->Descriptor[DESC0].HPDMACR_DCP_PAUSE = HPDMA_DISABLE;\r
208 }\r
209 \r
210 /*-------------------------------------------------------------------------*//**\r
211  * See "mss_hpdma.h" for details of how to use this function.\r
212  */\r
213 void\r
214 MSS_HPDMA_abort\r
215 (\r
216     void\r
217 )\r
218 {\r
219     HPDMA_BITBAND->Descriptor[DESC0].HPDMACR_DCP_CLR = HPDMA_ENABLE;\r
220 }\r
221 \r
222 /*-------------------------------------------------------------------------*//**\r
223  * See "mss_hpdma.h" for details of how to use this function.\r
224  */\r
225 hpdma_status_t\r
226 MSS_HPDMA_get_transfer_status\r
227 (\r
228     void\r
229 )\r
230 {\r
231     return g_transfer.state;\r
232 }\r
233 \r
234 /*-------------------------------------------------------------------------*//**\r
235  * See "mss_hpdma.h" for details of how to use this function.\r
236  */\r
237 void MSS_HPDMA_set_handler\r
238 (\r
239     mss_hpdma_handler_t handler\r
240 )\r
241 {\r
242     if(handler == NULL_HANDLER)\r
243     {\r
244         g_transfer.completion_handler = NULL_HANDLER;\r
245     }\r
246     else\r
247     {\r
248         g_transfer.completion_handler = handler;\r
249     }\r
250 }\r
251 \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
256  */\r
257 #if defined(__GNUC__)\r
258 __attribute__((__interrupt__)) void HPDMA_Complete_IRQHandler(void)\r
259 #else\r
260 void HPDMA_Complete_IRQHandler(void)\r
261 #endif \r
262 {\r
263     /* Clear interrupt */\r
264     HPDMA_BITBAND->HPDMAICR_CLR_XFR_INT[DESC0] = HPDMA_ENABLE;\r
265 \r
266     if(g_transfer.xfr_size > 0u)\r
267     {\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
272     }\r
273     else\r
274     {\r
275         g_transfer.state = HPDMA_COMPLETED;\r
276         if(g_transfer.completion_handler != NULL_HANDLER)\r
277         {\r
278             (*(g_transfer.completion_handler))(HPDMA_COMPLETED);\r
279         }\r
280     }\r
281 }\r
282 \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
287  */\r
288 #if defined(__GNUC__)\r
289 __attribute__((__interrupt__)) void HPDMA_Error_IRQHandler(void)\r
290 #else\r
291 void HPDMA_Error_IRQHandler(void)\r
292 #endif \r
293 {\r
294     uint32_t error_bits;\r
295 \r
296     /*\r
297      * Handle source/destination errors.\r
298      */\r
299     error_bits = HPDMA->HPDMAEDR_REG & HPDMAEDR_DCP_ERR_MASK;\r
300     if(error_bits != 0u)\r
301     {\r
302         if(g_transfer.completion_handler != NULL_HANDLER)\r
303         {\r
304             if(HPDMA_BITBAND->Descriptor[DESC0].HPDMASR_DCP_SERR)\r
305             {\r
306                 g_transfer.state = HPDMA_SOURCE_ERROR;\r
307                 (*(g_transfer.completion_handler))(HPDMA_SOURCE_ERROR);\r
308             }\r
309             \r
310             if(HPDMA_BITBAND->Descriptor[DESC0].HPDMASR_DCP_DERR)\r
311             {\r
312                 g_transfer.state = HPDMA_DESTINATION_ERROR;\r
313                 (*(g_transfer.completion_handler))(HPDMA_DESTINATION_ERROR);\r
314             }\r
315         }\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
320     }\r
321     \r
322     /*\r
323      * Handle non word aligned errors.\r
324      */\r
325     error_bits = HPDMA->HPDMAEDR_REG & HPDMAEDR_NON_WORD_ERR_MASK;\r
326     if(error_bits != 0u)\r
327     {\r
328         g_transfer.state = HPDMA_WORD_ALIGN_ERROR;\r
329         /* Call handler. */\r
330         if(g_transfer.completion_handler != NULL_HANDLER)\r
331         {\r
332             (*(g_transfer.completion_handler))(HPDMA_WORD_ALIGN_ERROR);\r
333         }\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
338     }\r
339 }\r
340 \r
341 #ifdef __cplusplus\r
342 }\r
343 #endif\r
344 \r