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