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 }