• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Winner Microelectronics Co., Ltd. All rights reserved.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 /**
17  * @file    wm_dma.c
18  *
19  * @brief   DMA Driver Module
20  *
21  * @author  dave
22  *
23  * Copyright (c) 2014 Winner Microelectronics Co., Ltd.
24  */
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
28 
29 #include "wm_regs.h"
30 #include "wm_irq.h"
31 #include "wm_osal.h"
32 #include "core_804.h"
33 #include "wm_pmu.h"
34 #include "wm_dma.h"
35 
36 static u16 dma_used_bit = 0;
37 struct tls_dma_channels {
38     unsigned char    channels[8];    /* list of channels */
39 };
40 
41 typedef void (*dma_irq_callback)(void *p);
42 
43 struct dma_irq_context {
44     u8 flags;
45     dma_irq_callback burst_done_pf;
46     void *burst_done_priv;
47     dma_irq_callback transfer_done_pf;
48     void *transfer_done_priv;
49 };
50 
51 static struct dma_irq_context dma_context[8];
52 static struct tls_dma_channels channels;
53 
54 extern void wm_delay_ticks(uint32_t ticks);
55 extern void tls_irq_priority(u8 vec_no, u32 prio);
56 
dma_irq_proc(void * p)57 static void dma_irq_proc(void *p)
58 {
59     unsigned char ch;
60     unsigned int int_src;
61 
62     ch = (unsigned char)(unsigned long)p;
63     int_src = tls_reg_read32(HR_DMA_INT_SRC);
64 
65     if (ch > 3) {
66         for (ch = 4; ch < 8; ch++) {
67             if (int_src & (TLS_DMA_IRQ_BOTH_DONE << (ch * 2))) { // 2:byte alignment
68                 break;
69             }
70         }
71 
72         if (ch == 8) {
73             return;
74         }
75     }
76 
77     if (DMA_CTRL_REG(ch) & 0x01) {
78         uint32_t temp = 0, cur_len = 0;
79         static uint32_t len[8] = {0, 0, 0, 0, 0, 0, 0, 0};
80 
81         temp = DMA_CTRL_REG(ch);
82         if (len[ch] == 0) {
83             len[ch] = (temp & 0xFFFF00) >> 8;
84         }
85         cur_len = (temp & 0xFFFF00) >> 8;
86         if ((cur_len + len[ch]) > 0xFFFF) {
87             cur_len = 0;
88             DMA_CHNLCTRL_REG(ch) |= (1 << 1);
89             while (DMA_CHNLCTRL_REG(ch) & (1 << 0)) {
90             }
91             DMA_CHNLCTRL_REG(ch) |= (1 << 0);
92         }
93 
94         temp &= ~(0xFFFF << 8);
95         temp |= ((cur_len + len[ch]) << 8);
96         DMA_CTRL_REG(ch) = temp;
97     }
98 
99     if ((int_src & (TLS_DMA_IRQ_BOTH_DONE << (ch * 2))) &&
100         (TLS_DMA_IRQ_BOTH_DONE == dma_context[ch].flags)) {
101         tls_dma_irq_clr(ch, TLS_DMA_IRQ_BOTH_DONE);
102         if (dma_context[ch].burst_done_pf) {
103             dma_context[ch].burst_done_pf(dma_context[ch].burst_done_priv);
104         }
105     } else if ((int_src & (TLS_DMA_IRQ_BURST_DONE << (ch * 2))) &&
106              (TLS_DMA_IRQ_BURST_DONE == dma_context[ch].flags)) {
107         tls_dma_irq_clr(ch, TLS_DMA_IRQ_BOTH_DONE);
108         if (dma_context[ch].burst_done_pf) {
109             dma_context[ch].burst_done_pf(dma_context[ch].burst_done_priv);
110         }
111     } else if ((int_src & (TLS_DMA_IRQ_TRANSFER_DONE << (ch * 2))) &&
112              (TLS_DMA_IRQ_TRANSFER_DONE == dma_context[ch].flags)) {
113         tls_dma_irq_clr(ch, TLS_DMA_IRQ_BOTH_DONE);
114         if (dma_context[ch].transfer_done_pf) {
115             dma_context[ch].transfer_done_pf(dma_context[ch].transfer_done_priv);
116         }
117     }
118     return;
119 }
120 
DMA_Channel0_IRQHandler(void)121 ATTRIBUTE_ISR void DMA_Channel0_IRQHandler(void)
122 {
123     csi_kernel_intrpt_enter();
124     dma_irq_proc((void *)0);
125     csi_kernel_intrpt_exit();
126 }
DMA_Channel1_IRQHandler(void)127 ATTRIBUTE_ISR void DMA_Channel1_IRQHandler(void)
128 {
129     csi_kernel_intrpt_enter();
130     dma_irq_proc((void *)1);
131     csi_kernel_intrpt_exit();
132 }
DMA_Channel2_IRQHandler(void)133 ATTRIBUTE_ISR void DMA_Channel2_IRQHandler(void)
134 {
135     csi_kernel_intrpt_enter();
136     dma_irq_proc((void *)2);
137     csi_kernel_intrpt_exit();
138 }
DMA_Channel3_IRQHandler(void)139 ATTRIBUTE_ISR void DMA_Channel3_IRQHandler(void)
140 {
141     csi_kernel_intrpt_enter();
142     dma_irq_proc((void *)3);
143     csi_kernel_intrpt_exit();
144 }
DMA_Channel4_7_IRQHandler(void)145 ATTRIBUTE_ISR void DMA_Channel4_7_IRQHandler(void)
146 {
147     csi_kernel_intrpt_enter();
148     dma_irq_proc((void *)4);
149     csi_kernel_intrpt_exit();
150 }
151 
152 /**
153  * @brief              This function is used to clear dma interrupt flag.
154  *
155  * @param[in]         ch      Channel no.[0~7]
156  * @param[in]         flags   Flags setted to TLS_DMA_IRQ_BURST_DONE, TLS_DMA_IRQ_TRANSFER_DONE, TLS_DMA_IRQ_BOTH_DONE.
157  *
158  * @return             None
159  *
160  * @note               None
161  */
tls_dma_irq_clr(unsigned char ch,unsigned char flags)162 void tls_dma_irq_clr(unsigned char ch, unsigned char flags)
163 {
164     unsigned int int_src = 0;
165 
166     int_src |= (flags << (2 * ch)); // 2:byte alignment
167 
168     tls_reg_write32(HR_DMA_INT_SRC, int_src);
169 
170     return;
171 }
172 
173 /**
174  * @brief              This function is used to register dma interrupt callback function.
175  *
176  * @param[in]    ch            Channel no.[0~7]
177  * @param[in]    callback    is the dma interrupt call back function.
178  * @param[in]    arg            the param of the callback function.
179  * @param[in]    flags        Flags setted to TLS_DMA_IRQ_BURST_DONE, TLS_DMA_IRQ_TRANSFER_DONE, TLS_DMA_IRQ_BOTH_DONE.
180  *
181  * @return             None
182  *
183  * @note               None
tls_dma_irq_register(unsigned char ch,void (* callback)(void * p),void * arg,unsigned char flags)184  */void tls_dma_irq_register(unsigned char ch, void (*callback)(void *p), void *arg, unsigned char flags)
185 {
186     unsigned int mask;
187     unsigned char Channel = ch;
188 
189     mask  = tls_reg_read32(HR_DMA_INT_MASK);
190     mask |= (TLS_DMA_IRQ_BOTH_DONE << (2 * Channel));
191     mask &= ~(flags << (2 * Channel)); // 2:byte alignment
192     tls_reg_write32(HR_DMA_INT_MASK, mask);
193 
194     dma_context[Channel].flags = flags;
195     if (flags & TLS_DMA_IRQ_BURST_DONE) {
196         dma_context[Channel].burst_done_pf   = callback;
197         dma_context[Channel].burst_done_priv = arg;
198     }
199     if (flags & TLS_DMA_IRQ_TRANSFER_DONE) {
200         dma_context[Channel].transfer_done_pf   = callback;
201         dma_context[Channel].transfer_done_priv = arg;
202     }
203     if (Channel > 3) {
204         Channel = 4; // 4:byte alignment
205     }
206     tls_irq_priority(DMA_Channel0_IRQn + Channel, Channel / 2); // 2:byte alignment
207     tls_irq_enable(DMA_Channel0_IRQn + Channel);
208 }
209 
210 /**
211  * @brief          This function is used to Wait until DMA operation completes
212  *
213  * @param[in]      ch    channel no
214  *
215  * @retval         0     completed
216  * @retval        -1     failed
217  *
218  * @note           None
219  */
tls_dma_wait_complt(unsigned char ch)220 int tls_dma_wait_complt(unsigned char ch)
221 {
222     unsigned long timeout = 0;
223 
224     while (DMA_CHNLCTRL_REG(ch) & DMA_CHNL_CTRL_CHNL_ON) {
225         tls_os_time_delay(1);
226         timeout ++;
227         if (timeout > 500)
228             return -1;
229     }
230     return 0;
231 }
232 
233 /**
234  * @brief          This function is used to Start the DMA controller by Wrap
235  *
236  * @param[in]      ch             channel no
237  * @param[in]      dma_desc       pointer to DMA channel descriptor structure
238  * @param[in]      auto_reload    does restart when current transfer complete
239  * @param[in]      src_zize       dource address size
240  * @param[in]      dest_zize      destination address size
241  *
242  * @retval         1     success
243  * @retval         0     failed
244  *
245  * @note
246  *     DMA Descriptor: +--------------------------------------------------------------+
247  *                     |Vld[31] |                    RSV                              |
248  *                     +--------------------------------------------------------------+
249  *                     |                  RSV           |         Dma_Ctrl[16:0]      |
250  *                     +--------------------------------------------------------------+
251  *                     |                         Src_Addr[31:0]                       |
252  *                     +--------------------------------------------------------------+
253  *                     |                         Dest_Addr[31:0]                      |
254  *                     +--------------------------------------------------------------+
255  *                     |                       Next_Desc_Add[31:0]                    |
256  *                     +--------------------------------------------------------------+
257  */
tls_dma_start_by_wrap(unsigned char ch,struct tls_dma_descriptor * dma_desc,unsigned char auto_reload,unsigned short src_zize,unsigned short dest_zize)258 unsigned char tls_dma_start_by_wrap(unsigned char ch, struct tls_dma_descriptor *dma_desc,
259                                     unsigned char auto_reload,
260                                     unsigned short src_zize,
261                                     unsigned short dest_zize)
262 {
263     if ((ch > 7) && !dma_desc) { // 7:byte alignment
264         return 1;
265     }
266 
267     DMA_SRCWRAPADDR_REG(ch) = dma_desc->src_addr;
268     DMA_DESTWRAPADDR_REG(ch) = dma_desc->dest_addr;
269     DMA_WRAPSIZE_REG(ch) = (dest_zize << 16) | src_zize;
270     DMA_CTRL_REG(ch) = ((dma_desc->dma_ctrl & 0x1ffff) << 1) | (auto_reload ? 0x1: 0x0);
271     DMA_CHNLCTRL_REG(ch) |= DMA_CHNL_CTRL_CHNL_ON;
272 
273     return 0;
274 }
275 
276 /**
277  * @brief          This function is used to Start the DMA controller
278  *
279  * @param[in]      ch             channel no
280  * @param[in]      dma_desc       pointer to DMA channel descriptor structure
281  * @param[in]      auto_reload    does restart when current transfer complete
282  *
283  * @retval         1     success
284  * @retval         0     failed
285  *
286  * @note
287  *     DMA Descriptor: +--------------------------------------------------------------+
288  *                     |Vld[31] |                    RSV                              |
289  *                     +--------------------------------------------------------------+
290  *                     |                  RSV           |         Dma_Ctrl[16:0]      |
291  *                     +--------------------------------------------------------------+
292  *                     |                         Src_Addr[31:0]                       |
293  *                     +--------------------------------------------------------------+
294  *                     |                         Dest_Addr[31:0]                      |
295  *                     +--------------------------------------------------------------+
296  *                     |                       Next_Desc_Add[31:0]                    |
297  *                     +--------------------------------------------------------------+
298  */
tls_dma_start(unsigned char ch,struct tls_dma_descriptor * dma_desc,unsigned char auto_reload)299 unsigned char tls_dma_start(unsigned char ch, struct tls_dma_descriptor *dma_desc, unsigned char auto_reload)
300 {
301     if ((ch > 7) && !dma_desc) { // 7:byte alignment
302         return 1;
303     }
304 
305     if ((dma_used_bit &(1<<ch)) == 0) {
306         dma_used_bit |= (1<<ch);
307     }
308     DMA_SRCADDR_REG(ch) = dma_desc->src_addr;
309     DMA_DESTADDR_REG(ch) = dma_desc->dest_addr;
310     DMA_CTRL_REG(ch) = ((dma_desc->dma_ctrl & 0x7fffff) << 1) | (auto_reload ? 0x1: 0x0);
311     DMA_CHNLCTRL_REG(ch) |= DMA_CHNL_CTRL_CHNL_ON;
312 
313     return 0;
314 }
315 
316 /**
317  * @brief          This function is used to To stop current DMA channel transfer
318  *
319  * @param[in]      ch    channel no. to be stopped
320  *
321  * @retval         0     success
322  * @retval         1     failed
323  *
324  * @note
325  * If channel stop, DMA_CHNL_CTRL_CHNL_ON bit in DMA_CHNLCTRL_REG is cleared.
326  */
tls_dma_stop(unsigned char ch)327 unsigned char tls_dma_stop(unsigned char ch)
328 {
329     if (ch > 7) { // 7:byte alignment
330         return 1;
331     }
332     if (DMA_CHNLCTRL_REG(ch) & DMA_CHNL_CTRL_CHNL_ON) {
333         DMA_CHNLCTRL_REG(ch) |= DMA_CHNL_CTRL_CHNL_OFF;
334 
335         while (DMA_CHNLCTRL_REG(ch) & DMA_CHNL_CTRL_CHNL_ON);
336     }
337 
338     return 0;
339 }
340 
341 /**
342  * @brief          This function is used to Request a free dma channel
343  *
344  * @param[in]      ch       specified channel when ch is valid and not used.
345  * @param[in]      flags    flags setted to selected channel
346  *
347  * @return         Real DMA Channel No.
348  *
349  * @note
350  * If ch is invalid or valid but used, the function will select a random free channel.
351  * else return the selected channel no.
352  */
tls_dma_request(unsigned char ch,unsigned char flags)353 unsigned char tls_dma_request(unsigned char ch, unsigned char flags)
354 {
355     unsigned char freeCh = 0xFF;
356 
357     /* If channel is valid, try to use specified DMA channel! */
358     if ((ch < 8)) {
359         if (!(channels.channels[ch] & TLS_DMA_FLAGS_CHANNEL_VALID)) {
360             freeCh = ch;
361         }
362     }
363 
364     /* If ch is not valid, or ch has been used, try to select another free channel for the caller */
365     if (freeCh == 0xFF) {
366         int i = 0;
367         for (i = 0; i < 8; i++) {
368             if (!(channels.channels[i] & TLS_DMA_FLAGS_CHANNEL_VALID)) {
369                 freeCh = i;
370                 break;
371             }
372         }
373 
374         if (i == 8) {
375             printf("!!!There is no free DMA channel.!!!\n");
376         }
377     }
378 
379     if ((freeCh < 8)) {
380         if (dma_used_bit == 0) {
381             tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_DMA);
382         }
383         dma_used_bit |= (1<<freeCh);
384 
385         channels.channels[freeCh] = flags | TLS_DMA_FLAGS_CHANNEL_VALID;
386         DMA_MODE_REG(freeCh) = flags;
387     }
388 
389     return freeCh;
390 }
391 
392 /**
393  * @brief          This function is used to Free the DMA channel when not use
394  *
395  * @param[in]      ch    channel no. that is ready to free
396  *
397  * @return         None
398  *
399  * @note           None
400  */
tls_dma_free(unsigned char ch)401 void tls_dma_free(unsigned char ch)
402 {
403     if (ch < 8) {
404         tls_dma_stop(ch);
405 
406         DMA_SRCADDR_REG(ch) = 0;
407         DMA_DESTADDR_REG(ch) = 0;
408         DMA_MODE_REG(ch) = 0;
409         DMA_CTRL_REG(ch) = 0;
410         DMA_INTSRC_REG |= 0x03<<(ch*2);
411 
412         channels.channels[ch] = 0x00;
413         dma_used_bit &= ~(1<<ch);
414         if (dma_used_bit == 0) {
415             tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_DMA);
416         }
417     }
418 }
419 
420 /**
421  * @brief          This function is used to Initialize DMA Control
422  *
423  * @param[in]      None
424  *
425  * @return         None
426  *
427  * @note           None
428  */
tls_dma_init(void)429 void tls_dma_init(void)
430 {
431     u32 i = 0;
432     u32 value = 0;
433     for (i = 0; i < 8; i++) {
434         if (!(dma_used_bit & (1<<i))) {
435             value |= 3<<(i*2);
436         }
437     }
438 
439     DMA_INTMASK_REG = value;
440     DMA_INTSRC_REG  = value;
441 }