1 /*
2  * Copyright (C) 2017 C-SKY Microsystems Co., Ltd. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 /******************************************************************************
17 * @file     ck_dmac.c
18 * @brief    CSI Source File for DMAC Driver
19 * @version  V1.0
20 * @date     02. June 2017
21 ******************************************************************************/
22 #include <stdbool.h>
23 #include "ck_dmac.h"
24 #include "csi_core.h"
25 #include "drv_dmac.h"
26 #include "soc.h"
27 
28 #define ERR_DMA(errno) (CSI_DRV_ERRNO_DMA_BASE | errno)
29 
30 typedef struct {
31     uint32_t base;
32     uint32_t irq;
33     dma_event_cb_t cb_event;
34     uint8_t ch_num;
35 } ck_dma_priv_t;
36 
37 static ck_dma_priv_t dma_instance[CONFIG_DMAC_NUM];
38 
39 static const dma_capabilities_t dma_capabilities = {
40     .unalign_addr = 1,          ///< support for unalign address transfer when memory is source
41 };
42 
43 static volatile dma_status_e status[CK_DMA_MAXCHANNEL] = {DMA_STATE_FREE, DMA_STATE_FREE};
44 static volatile uint8_t ch_opened[CK_DMA_MAXCHANNEL] = {0, 0};
45 
ck_dma_set_channel(ck_dma_reg_t * addr,uint32_t source,uint32_t dest,uint32_t size)46 static int32_t ck_dma_set_channel(ck_dma_reg_t *addr, uint32_t source, uint32_t dest, uint32_t size)
47 {
48     uint32_t temp = addr->CHCTRLA;
49     temp &= 0xff000fff;
50     temp |= (size << 12);
51     addr->SAR = source;
52     addr->DAR = dest ;
53     addr->CHCTRLA = temp;
54 
55     return 0;
56 }
57 
ck_dma_set_transfertype(ck_dma_reg_t * addr,dma_trans_type_e transtype)58 static int32_t ck_dma_set_transfertype(ck_dma_reg_t *addr, dma_trans_type_e transtype)
59 {
60     uint32_t temp = addr->CHCTRLB;
61     temp &= 0xffffff7f;
62 
63     if (transtype >= DMA_PERH2PERH) {
64         return ERR_DMA(EDRV_PARAMETER);
65     }
66 
67     if (transtype == DMA_MEM2MEM) {
68         temp |= (transtype << 7);
69     } else {
70         temp |= (1 << 7);
71     }
72 
73     addr->CHCTRLB = temp;
74 
75     return 0;
76 }
77 
ck_dma_set_addrinc(ck_dma_reg_t * addr,enum_addr_state_e src_addrinc,enum_addr_state_e dst_addrinc)78 static int32_t ck_dma_set_addrinc(ck_dma_reg_t *addr, enum_addr_state_e src_addrinc, enum_addr_state_e dst_addrinc)
79 {
80     if ((src_addrinc != DMA_ADDR_INCREMENT && src_addrinc != DMA_ADDR_DECREMENT && src_addrinc != DMA_ADDR_NOCHANGE) ||
81         (dst_addrinc != DMA_ADDR_INCREMENT && dst_addrinc != DMA_ADDR_DECREMENT && dst_addrinc != DMA_ADDR_NOCHANGE)) {
82         return ERR_DMA(EDRV_PARAMETER);
83     }
84 
85     uint32_t  temp = addr->CHCTRLA;
86     temp &= 0xffffff0f;
87     temp |= (src_addrinc << 6);
88     temp |= (dst_addrinc << 4);
89     addr->CHCTRLA = temp;
90 
91     return 0;
92 }
93 
ck_dma_set_transferwidth(ck_dma_reg_t * addr,dma_datawidth_e src_width,dma_datawidth_e dst_width)94 static int32_t ck_dma_set_transferwidth(ck_dma_reg_t *addr, dma_datawidth_e src_width, dma_datawidth_e dst_width)
95 {
96     if ((src_width != DMA_DATAWIDTH_SIZE8 && src_width != DMA_DATAWIDTH_SIZE16 && src_width != DMA_DATAWIDTH_SIZE32) ||
97         (dst_width != DMA_DATAWIDTH_SIZE8 && dst_width != DMA_DATAWIDTH_SIZE16 && dst_width != DMA_DATAWIDTH_SIZE32)) {
98         return ERR_DMA(EDRV_PARAMETER);
99     }
100 
101     uint32_t temp = addr->CHCTRLA;
102     temp &= 0xfffffff0;
103     temp |= (src_width - 1) << 2;
104     temp |= dst_width - 1;
105     addr->CHCTRLA = temp;
106 
107     return 0;
108 }
109 
ck_dma_set_burstlength(ck_dma_reg_t * addr,uint8_t burstlength)110 static int32_t ck_dma_set_burstlength(ck_dma_reg_t *addr, uint8_t burstlength)
111 {
112     uint32_t temp = addr->CHCTRLA;
113     temp &= 0xfffff0ff;
114     temp |= (burstlength << 8);
115     addr->CHCTRLA = temp;
116 
117     return 0;
118 
119 }
120 
121 /**
122   \brief       Set software or hardware handshaking.
123   \param[in]   addr pointer to dma register.
124   \return      error code
125 */
ck_dma_set_handshaking(ck_dma_reg_t * addr,dma_handshaking_select_e handshaking)126 static int32_t ck_dma_set_handshaking(ck_dma_reg_t *addr, dma_handshaking_select_e handshaking)
127 {
128     uint32_t  temp = addr->CHCTRLB;
129     temp &= 0xfffffeff;
130     temp |= (handshaking << 8);
131     addr->CHCTRLB = temp;
132 
133     return 0;
134 }
135 
ck_dma_assign_hdhs_interface(ck_dma_reg_t * addr,ckenum_dma_device_e device)136 static int ck_dma_assign_hdhs_interface(ck_dma_reg_t *addr, ckenum_dma_device_e device)
137 {
138     if (device < 0 || device >= CKENUM_DMA_MEMORY) {
139         return ERR_DMA(EDRV_PARAMETER);
140     }
141 
142     addr->CHCTRLB &= 0xffffe1ff;
143     addr->CHCTRLB |= (device << 9);
144 
145     return 0;
146 }
147 
148 
ck_dma_irqhandler(int32_t idx)149 void ck_dma_irqhandler(int32_t idx)
150 {
151     ck_dma_priv_t *dma_priv = &dma_instance[idx];
152     ck_dma_reg_t *addr = (ck_dma_reg_t *)(dma_priv->base);
153 
154     /*
155      * StatusInt_temp contain the information that which types of interrupr are
156      * requested.
157      */
158     int32_t count = 0;
159     uint32_t temp = 0;
160 
161     for (count = 0; count < dma_priv->ch_num; count++) {
162         addr = (ck_dma_reg_t *)(dma_priv->base + count * 0x30);
163 
164         temp = addr->CHINTS;
165 
166         if (temp != 0) {
167             break;
168         }
169     }
170 
171     /* If Tfr interrupt is requested */
172     if (temp == CK_DMA_TFR) {
173         status[count] = DMA_STATE_DONE;
174         addr->CHINTC = temp;
175 
176         if (dma_priv->cb_event) {
177             dma_priv->cb_event(DMA_EVENT_TRANSFER_DONE, count);
178         }
179     }
180 
181     /* If Err interrput is requested */
182     if (temp == CK_DMA_ERR) {
183         status[count] =  DMA_STATE_ERROR;
184         addr->CHINTC = temp;
185 
186         if (dma_priv->cb_event) {
187             dma_priv->cb_event(DMA_EVENT_TRANSFER_ERROR, count);
188         }
189     }
190 }
191 
target_get_dmac_count(void)192 int32_t __attribute__((weak)) target_get_dmac_count(void)
193 {
194     return 0;
195 }
196 
target_get_dmac(uint32_t idx,uint32_t * base,uint32_t * irq)197 int32_t __attribute__((weak)) target_get_dmac(uint32_t idx, uint32_t *base, uint32_t *irq)
198 {
199     return NULL;
200 }
201 
202 /**
203   \brief       get dma instance count.
204   \return      dma instance count
205 */
csi_dma_get_instance_count(void)206 int32_t csi_dma_get_instance_count(void)
207 {
208     return target_get_dmac_count();
209 }
210 
211 /**
212   \brief       Initialize DMA Interface. 1. Initializes the resources needed for the DMA interface 2.registers event callback function
213   \param[in]   idx must not exceed return value of csi_dma_get_instance_count()
214   \return      pointer to dma instances
215 */
csi_dma_initialize(int32_t idx)216 dmac_handle_t csi_dma_initialize(int32_t idx)
217 {
218 
219     if (idx < 0 || idx >= CONFIG_DMAC_NUM) {
220         return NULL;
221     }
222 
223     uint32_t base = 0u;
224     uint32_t irq = 0u;
225 
226     int32_t real_idx = target_get_dmac(idx, &base, &irq);
227 
228     if (real_idx != idx) {
229         return NULL;
230     }
231 
232     ck_dma_priv_t *dma_priv = &dma_instance[idx];
233 
234     dma_priv->base = base;
235     dma_priv->irq  = irq;
236     dma_priv->ch_num = CK_DMA_MAXCHANNEL;
237     drv_nvic_enable_irq(dma_priv->irq);
238     uint8_t count = 0u;
239 
240     for (count = 0; count < dma_priv->ch_num; count++) {
241         ck_dma_reg_t *addr = (ck_dma_reg_t *)(dma_priv->base + count * 0x30);
242         addr->CHINTM = CK_DMA_MASK;
243         addr->CHINTC = CK_DMA_INTC;
244     }
245 
246     return (dmac_handle_t)dma_priv;
247 }
248 
249 /**
250   \brief       De-initialize DMA Interface. stops operation and releases the software resources used by the interface
251   \param[in]   handle damc handle to operate.
252   \return      error code
253 */
csi_dma_uninitialize(dmac_handle_t handle)254 int32_t csi_dma_uninitialize(dmac_handle_t handle)
255 {
256     if (handle == NULL) {
257         return ERR_DMA(EDRV_PARAMETER);
258     }
259 
260     ck_dma_priv_t *dma_priv = handle;
261     ck_dma_reg_t *addr = (ck_dma_reg_t *)(dma_priv->base);
262 
263     uint8_t count;
264 
265     for (count = 0; count < dma_priv->ch_num; count++) {
266         addr = (ck_dma_reg_t *)(dma_priv->base + count * 0x30);
267         addr->CHINTM = CK_DMA_MASK;
268         addr->CHINTC = CK_DMA_INTC;
269     }
270 
271     drv_nvic_disable_irq(dma_priv->irq);
272 
273     return 0;
274 }
275 
276 /**
277   \brief       Get driver capabilities.
278   \param[in]   handle damc handle to operate.
279   \return      \ref dma_capabilities_t
280 */
csi_dma_get_capabilities(dmac_handle_t handle)281 dma_capabilities_t csi_dma_get_capabilities(dmac_handle_t handle)
282 {
283     return dma_capabilities;
284 }
285 
286 /**
287   \brief     get one free dma channel
288   \param[in] handle damc handle to operate.
289   \param[in] ch channel num. if -1 then allocate a free channal in this dma
290   \return    -1 - no channel can be used, other - channel index
291  */
csi_dma_alloc_channel(dmac_handle_t handle,int32_t ch)292 int32_t csi_dma_alloc_channel(dmac_handle_t handle, int32_t ch)
293 {
294     ck_dma_priv_t *dma_priv = handle;
295 
296     if (handle == NULL || ch > dma_priv->ch_num) {
297         return ERR_DMA(EDRV_PARAMETER);
298     }
299 
300     uint8_t ch_num = 0;
301     ck_dma_reg_t *addr = NULL;
302 
303     if (ch == -1) {     // alloc a free channal
304         for (ch_num = 0; ch_num < dma_priv->ch_num; ch_num++) {
305             addr = (ck_dma_reg_t *)(dma_priv->base + ch_num * 0x30);
306 
307             if (ch_opened[ch_num] != 0x1) {
308                 ch_opened[ch_num] = 1;
309                 break;
310             }
311         }
312 
313         if (ch_num >= dma_priv->ch_num) {
314             return -1;
315         }
316     } else {    //alloc a fixed channel
317         addr = (ck_dma_reg_t *)(dma_priv->base + ch * 0x30);
318 
319         if (ch_opened[ch] == 0x1) {
320             return ERR_DMA(EDRV_BUSY);
321         }
322 
323         ch_opened[ch] = 1;
324         ch_num = ch;
325     }
326 
327     addr->CHINTC = CK_DMA_INTC;
328     addr->CHINTM &= ~CK_DMA_MASK;
329     status[ch_num] = DMA_STATE_READY;
330 
331     return ch_num;
332 }
333 
334 /**
335   \brief        release dma channel and related resources
336   \param[in]    handle damc handle to operate.
337   \param[in]    ch  channel num.
338   \return       error code
339  */
csi_dma_release_channel(dmac_handle_t handle,int32_t ch)340 int32_t csi_dma_release_channel(dmac_handle_t handle, int32_t ch)
341 {
342     ck_dma_priv_t *dma_priv = handle;
343 
344     if (handle == NULL || ch >= dma_priv->ch_num || ch < 0) {
345         return ERR_DMA(EDRV_PARAMETER);
346     }
347 
348     ck_dma_reg_t *addr = (ck_dma_reg_t *)(dma_priv->base + ch * 0x30);
349     status[ch] = DMA_STATE_FREE;
350     ch_opened[ch] = 0;
351 
352     addr->CHINTC = CK_DMA_INTC;
353     addr->CHINTM = CK_DMA_MASK;
354     return 0;
355 }
356 
357 /**
358   \brief
359   \param[in]    handle damc handle to operate.
360   \param[in]    ch          channel num. if -1 then allocate a free channal in this dma
361   \param[in]    psrcaddr    dma transfer source address
362   \param[in]    pstdaddr    dma transfer source address
363   \param[in]    length      dma transfer length
364   \param[in]    config      dma transfer configure
365   \param[in]    cb_event    Pointer to \ref dma_event_cb_t
366   \return       error code
367  */
csi_dma_config(dmac_handle_t handle,int32_t ch,void * psrcaddr,void * pstdaddr,uint32_t length,dma_config_t * config,dma_event_cb_t cb_event)368 int32_t csi_dma_config(dmac_handle_t handle, int32_t ch,
369                        void *psrcaddr, void *pstdaddr,
370                        uint32_t length, dma_config_t *config, dma_event_cb_t cb_event)
371 {
372     ck_dma_priv_t *dma_priv = handle;
373 
374     if (handle == NULL || ch >= dma_priv->ch_num || config == NULL) {
375         return ERR_DMA(EDRV_PARAMETER);
376     }
377 
378     if (ch == -1) { //alloc a free channel
379         ch = csi_dma_alloc_channel(handle, -1);
380 
381         if (ch < 0) {
382             return ERR_DMA(EDRV_BUSY);
383         }
384     }
385 
386     dma_priv->cb_event = cb_event;
387 
388     ck_dma_reg_t *addr = (ck_dma_reg_t *)(dma_priv->base + ch * 0x30);
389 
390     /* Initializes corresponding channel registers */
391 
392     if ((length * config->src_tw) % config->dst_tw != 0) {
393         return ERR_DMA(EDRV_PARAMETER);
394     }
395 
396     int32_t ret = ck_dma_set_transferwidth(addr, config->src_tw, config->dst_tw);
397 
398     if (ret) {
399         return ret;
400     }
401 
402     int32_t grouplen = ((length * config->src_tw / config->dst_tw) - 1) % 16;
403     ck_dma_set_burstlength(addr, grouplen);
404 
405     ret = ck_dma_set_transfertype(addr, config->type);
406 
407     if (ret < 0) {
408         return ret;
409     }
410 
411     if (config->type == DMA_MEM2MEM) {
412         ck_dma_set_handshaking(addr, DMA_HANDSHAKING_SOFTWARE);
413         ret = ck_dma_set_addrinc(addr , config->src_inc, config->dst_inc);
414     } else if (config->type == DMA_MEM2PERH) {
415         ck_dma_set_handshaking(addr, DMA_HANDSHAKING_HARDWARE);
416         ret = ck_dma_set_addrinc(addr , config->src_inc, config->dst_inc);
417 
418         if (ret) {
419             return ret;
420         }
421 
422         ret = ck_dma_assign_hdhs_interface(addr, config->hs_if);
423 
424         if (ret) {
425             return ret;
426         }
427 
428     } else if (config->type == DMA_PERH2MEM) {
429         ck_dma_set_handshaking(addr, DMA_HANDSHAKING_HARDWARE);
430         ret = ck_dma_set_addrinc(addr , config->src_inc, config->dst_inc);
431 
432         if (ret) {
433             return ret;
434         }
435 
436         ret = ck_dma_assign_hdhs_interface(addr, config->hs_if);
437 
438         if (ret) {
439             return ret;
440         }
441     }
442 
443     ck_dma_set_channel(addr, (uint32_t)psrcaddr, (uint32_t)pstdaddr, length);
444     status[ch] = DMA_STATE_READY;
445 
446     return 0;
447 }
448 
449 /**
450   \brief       start generate dma signal.
451   \param[in]   handle damc handle to operate.
452   \param[in]   ch  channel num.
453   \return      error code
454 */
csi_dma_start(dmac_handle_t handle,int32_t ch)455 int32_t csi_dma_start(dmac_handle_t handle, int32_t ch)
456 {
457     ck_dma_priv_t *dma_priv = handle;
458 
459     if (handle == NULL || ch >= dma_priv->ch_num || ch < 0) {
460         return ERR_DMA(EDRV_PARAMETER);
461     }
462 
463     status[ch] = DMA_STATE_BUSY;
464     ck_dma_reg_t *addr = (ck_dma_reg_t *)(dma_priv->base + ch * 0x30);
465     addr->CHCTRLB |= CK_DMA_INT_EN;      // interrupt enable
466     addr->CHEN    |= CK_DMA_CH_EN;
467 
468     return 0;
469 }
470 
471 /**
472   \brief       Stop generate dma signal.
473   \param[in]   handle damc handle to operate.
474   \param[in]   ch  channel num.
475   \return      error code
476 */
csi_dma_stop(dmac_handle_t handle,int32_t ch)477 int32_t csi_dma_stop(dmac_handle_t handle, int32_t ch)
478 {
479     if (handle == NULL) {
480         return ERR_DMA(EDRV_PARAMETER);
481     }
482 
483     ck_dma_priv_t *dma_priv = handle;
484 
485     if (ch >= dma_priv->ch_num || ch < 0) {
486         return ERR_DMA(EDRV_PARAMETER);
487     }
488 
489     status[ch] = DMA_STATE_DONE;
490 
491     ck_dma_reg_t *addr = (ck_dma_reg_t *)(dma_priv->base + ch * 0x30);
492     addr->CHCTRLB &= ~CK_DMA_INT_EN;      // interrupt disable
493     addr->CHEN    &= ~CK_DMA_CH_EN;
494     return 0;
495 }
496 
497 /**
498   \brief       Get DMA status.
499   \param[in]   handle damc handle to operate.
500   \param[in]   ch  channel num.
501   \return      DMA status \ref dma_status_t
502 */
csi_dma_get_status(dmac_handle_t handle,int32_t ch)503 dma_status_e csi_dma_get_status(dmac_handle_t handle, int32_t ch)
504 {
505     if (handle == NULL) {
506         return ERR_DMA(EDRV_PARAMETER);
507     }
508 
509     ck_dma_priv_t *dma_priv = handle;
510 
511     if (ch >= dma_priv->ch_num || ch < 0) {
512         return ERR_DMA(EDRV_PARAMETER);
513     }
514 
515     return status[ch];
516 }
517 
518