1 /*
2  * \file
3  *
4  * \brief SAM Direct Memory Access Controller Driver
5  *
6  * Copyright (C) 2014-2016 Atmel Corporation. All rights reserved.
7  *
8  * \asf_license_start
9  *
10  * \page License
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions are met:
14  *
15  * 1. Redistributions of source code must retain the above copyright notice,
16  *    this list of conditions and the following disclaimer.
17  *
18  * 2. Redistributions in binary form must reproduce the above copyright notice,
19  *    this list of conditions and the following disclaimer in the documentation
20  *    and/or other materials provided with the distribution.
21  *
22  * 3. The name of Atmel may not be used to endorse or promote products derived
23  *    from this software without specific prior written permission.
24  *
25  * 4. This software may only be redistributed and used in connection with an
26  *    Atmel microcontroller product.
27  *
28  * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
29  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
30  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
31  * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
32  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
36  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
37  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38  * POSSIBILITY OF SUCH DAMAGE.
39  *
40  * \asf_license_stop
41  *
42  */
43 /*
44  * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
45  */
46 
47 #include <string.h>
48 #include "dma.h"
49 #include "clock.h"
50 #include "system_interrupt.h"
51 
52 struct _dma_module {
53 	volatile bool _dma_init;
54 	volatile uint32_t allocated_channels;
55 	uint8_t free_channels;
56 };
57 
58 struct _dma_module _dma_inst = {
59 	._dma_init = false,
60 	.allocated_channels = 0,
61 	.free_channels = CONF_MAX_USED_CHANNEL_NUM,
62 };
63 
64 /** Maximum retry counter for resuming a job transfer. */
65 #define MAX_JOB_RESUME_COUNT    10000
66 
67 /** DMA channel mask. */
68 #define DMA_CHANNEL_MASK   (0x1f)
69 
70 COMPILER_ALIGNED(16)
71 DmacDescriptor descriptor_section[CONF_MAX_USED_CHANNEL_NUM] SECTION_DMAC_DESCRIPTOR;
72 
73 /** Initial write back memory section. */
74 COMPILER_ALIGNED(16)
75 static DmacDescriptor _write_back_section[CONF_MAX_USED_CHANNEL_NUM] SECTION_DMAC_DESCRIPTOR;
76 
77 /** Internal DMA resource pool. */
78 static struct dma_resource* _dma_active_resource[CONF_MAX_USED_CHANNEL_NUM];
79 
80 /* DMA channel interrup flag. */
81 uint8_t g_chan_interrupt_flag[CONF_MAX_USED_CHANNEL_NUM]={0};
82 
83 /**
84  * \brief Find a free channel for a DMA resource.
85  *
86  * Find a channel for the requested DMA resource.
87  *
88  * \return Status of channel allocation.
89  * \retval DMA_INVALID_CHANNEL  No channel available
90  * \retval count          Allocated channel for the DMA resource
91  */
_dma_find_first_free_channel_and_allocate(void)92 static uint8_t _dma_find_first_free_channel_and_allocate(void)
93 {
94 	uint8_t count;
95 	uint32_t tmp;
96 	bool allocated = false;
97 
98 	system_interrupt_enter_critical_section();
99 
100 	tmp = _dma_inst.allocated_channels;
101 
102 	for (count = 0; count < CONF_MAX_USED_CHANNEL_NUM; ++count) {
103 		if (!(tmp & 0x00000001)) {
104 			/* If free channel found, set as allocated and return
105 			 *number */
106 
107 			_dma_inst.allocated_channels |= 1 << count;
108 			_dma_inst.free_channels--;
109 			allocated = true;
110 
111 			break;
112 		}
113 
114 		tmp = tmp >> 1;
115 	}
116 
117 	system_interrupt_leave_critical_section();
118 
119 	if (!allocated) {
120 		return DMA_INVALID_CHANNEL;
121 	} else {
122 		return count;
123 	}
124 }
125 
126 /**
127  * \brief Release an allocated DMA channel.
128  *
129  * \param[in]  channel  Channel id to be released
130  *
131  */
_dma_release_channel(uint8_t channel)132 static void _dma_release_channel(uint8_t channel)
133 {
134 	_dma_inst.allocated_channels &= ~(1 << channel);
135 	_dma_inst.free_channels++;
136 }
137 
138 /**
139  * \brief Configure the DMA resource.
140  *
141  * \param[in]  dma_resource Pointer to a DMA resource instance
142  * \param[out] resource_config Configurations of the DMA resource
143  *
144  */
_dma_set_config(struct dma_resource * resource,struct dma_resource_config * resource_config)145 static void _dma_set_config(struct dma_resource *resource,
146 		struct dma_resource_config *resource_config)
147 {
148 	Assert(resource);
149 	Assert(resource_config);
150 	uint32_t temp_CHCTRLB_reg;
151 	system_interrupt_enter_critical_section();
152 
153 	/** Select the DMA channel and clear software trigger */
154 	DMAC->CHID.reg = DMAC_CHID_ID(resource->channel_id);
155 	DMAC->SWTRIGCTRL.reg &= (uint32_t)(~(1 << resource->channel_id));
156 
157 	temp_CHCTRLB_reg = DMAC_CHCTRLB_LVL(resource_config->priority) | \
158 			DMAC_CHCTRLB_TRIGSRC(resource_config->peripheral_trigger) | \
159 			DMAC_CHCTRLB_TRIGACT(resource_config->trigger_action);
160 
161 
162 	if(resource_config->event_config.input_action){
163 	temp_CHCTRLB_reg |= DMAC_CHCTRLB_EVIE | DMAC_CHCTRLB_EVACT(
164 				resource_config->event_config.input_action);
165 	}
166 
167 	/** Enable event output, the event output selection is configured in
168 	 * each transfer descriptor  */
169 	if (resource_config->event_config.event_output_enable) {
170 		temp_CHCTRLB_reg |= DMAC_CHCTRLB_EVOE;
171 	}
172 
173 	/* Write config to CTRLB register */
174 	DMAC->CHCTRLB.reg = temp_CHCTRLB_reg;
175 
176 
177 
178 	system_interrupt_leave_critical_section();
179 }
180 
181 /**
182  * \brief DMA interrupt service routine.
183  *
184  */
DMAC_Handler(void)185 void DMAC_Handler( void )
186 {
187 	uint8_t active_channel;
188 	struct dma_resource *resource;
189 	uint8_t isr;
190 	uint32_t write_size;
191 	uint32_t total_size;
192 
193 	system_interrupt_enter_critical_section();
194 
195 	/* Get Pending channel */
196 	active_channel =  DMAC->INTPEND.reg & DMAC_INTPEND_ID_Msk;
197 
198 	Assert(_dma_active_resource[active_channel]);
199 
200 	/* Get active DMA resource based on channel */
201 	resource = _dma_active_resource[active_channel];
202 
203 	/* Select the active channel */
204 	DMAC->CHID.reg = DMAC_CHID_ID(resource->channel_id);
205 	isr = DMAC->CHINTFLAG.reg;
206 
207 	/* Calculate block transfer size of the DMA transfer */
208 	total_size = descriptor_section[resource->channel_id].BTCNT.reg;
209 	write_size = _write_back_section[resource->channel_id].BTCNT.reg;
210 	resource->transfered_size = total_size - write_size;
211 
212 	/* DMA channel interrupt handler */
213 	if (isr & DMAC_CHINTENCLR_TERR) {
214 		/* Clear transfer error flag */
215 		DMAC->CHINTFLAG.reg = DMAC_CHINTENCLR_TERR;
216 
217 		/* Set I/O ERROR status */
218 		resource->job_status = STATUS_ERR_IO;
219 
220 		/* Execute the callback function */
221 		if ((resource->callback_enable & (1<<DMA_CALLBACK_TRANSFER_ERROR)) &&
222 				(resource->callback[DMA_CALLBACK_TRANSFER_ERROR])) {
223 			resource->callback[DMA_CALLBACK_TRANSFER_ERROR](resource);
224 		}
225 	} else if (isr & DMAC_CHINTENCLR_TCMPL) {
226 		/* Clear the transfer complete flag */
227 		DMAC->CHINTFLAG.reg = DMAC_CHINTENCLR_TCMPL;
228 
229 		/* Set job status */
230 		resource->job_status = STATUS_OK;
231 
232 		/* Execute the callback function */
233 		if ((resource->callback_enable & (1 << DMA_CALLBACK_TRANSFER_DONE)) &&
234 				(resource->callback[DMA_CALLBACK_TRANSFER_DONE])) {
235 			resource->callback[DMA_CALLBACK_TRANSFER_DONE](resource);
236 		}
237 	} else if (isr & DMAC_CHINTENCLR_SUSP) {
238 		/* Clear channel suspend flag */
239 		DMAC->CHINTFLAG.reg = DMAC_CHINTENCLR_SUSP;
240 
241 		/* Set job status */
242 		resource->job_status = STATUS_SUSPEND;
243 
244 		/* Execute the callback function */
245 		if ((resource->callback_enable & (1 << DMA_CALLBACK_CHANNEL_SUSPEND)) &&
246 			(resource->callback[DMA_CALLBACK_CHANNEL_SUSPEND])){
247 			resource->callback[DMA_CALLBACK_CHANNEL_SUSPEND](resource);
248 		}
249 	}
250 
251 	system_interrupt_leave_critical_section();
252 }
253 
254 /**
255  * \brief Initializes config with predefined default values.
256  *
257  * This function will initialize a given DMA configuration structure to
258  * a set of known default values. This function should be called on
259  * any new instance of the configuration structure before being
260  * modified by the user application.
261  *
262  * The default configuration is as follows:
263  *  \li Software trigger is used as the transfer trigger
264  *  \li Priority level 0
265  *  \li Only software/event trigger
266  *  \li Requires a trigger for each transaction
267  *  \li No event input /output
268  *  \li DMA channel is disabled during sleep mode (if has the feature)
269  * \param[out] config Pointer to the configuration
270  *
271  */
dma_get_config_defaults(struct dma_resource_config * config)272 void dma_get_config_defaults(struct dma_resource_config *config)
273 {
274 	Assert(config);
275 	/* Set as priority 0 */
276 	config->priority = DMA_PRIORITY_LEVEL_0;
277 	/* Only software/event trigger */
278 	config->peripheral_trigger = 0;
279 	/* Transaction trigger */
280 	config->trigger_action = DMA_TRIGGER_ACTION_TRANSACTION;
281 
282 	/* Event configurations, no event input/output */
283 	config->event_config.input_action = DMA_EVENT_INPUT_NOACT;
284 	config->event_config.event_output_enable = false;
285 #ifdef FEATURE_DMA_CHANNEL_STANDBY
286 	config->run_in_standby = false;
287 #endif
288 }
289 
290 /**
291  * \brief Allocate a DMA with configurations.
292  *
293  * This function will allocate a proper channel for a DMA transfer request.
294  *
295  * \param[in,out]  dma_resource Pointer to a DMA resource instance
296  * \param[in] transfer_config Configurations of the DMA transfer
297  *
298  * \return Status of the allocation procedure.
299  *
300  * \retval STATUS_OK The DMA resource was allocated successfully
301  * \retval STATUS_ERR_NOT_FOUND DMA resource allocation failed
302  */
dma_allocate(struct dma_resource * resource,struct dma_resource_config * config)303 enum status_code dma_allocate(struct dma_resource *resource,
304 		struct dma_resource_config *config)
305 {
306 	uint8_t new_channel;
307 
308 	Assert(resource);
309 
310 	system_interrupt_enter_critical_section();
311 
312 	if (!_dma_inst._dma_init) {
313 		/* Initialize clocks for DMA */
314 #if (SAML21) || (SAML22) || (SAMC20) || (SAMC21) || (SAMR30)
315 		system_ahb_clock_set_mask(MCLK_AHBMASK_DMAC);
316 #else
317 		system_ahb_clock_set_mask(PM_AHBMASK_DMAC);
318 		system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBB,
319 				PM_APBBMASK_DMAC);
320 #endif
321 
322 		/* Perform a software reset before enable DMA controller */
323 		DMAC->CTRL.reg &= ~DMAC_CTRL_DMAENABLE;
324 		DMAC->CTRL.reg = DMAC_CTRL_SWRST;
325 
326 		/* Setup descriptor base address and write back section base
327 		 * address */
328 		DMAC->BASEADDR.reg = (uint32_t)descriptor_section;
329 		DMAC->WRBADDR.reg = (uint32_t)_write_back_section;
330 
331 		/* Enable all priority level at the same time */
332 		DMAC->CTRL.reg = DMAC_CTRL_DMAENABLE | DMAC_CTRL_LVLEN(0xf);
333 
334 		_dma_inst._dma_init = true;
335 	}
336 
337 	/* Find the proper channel */
338 	new_channel = _dma_find_first_free_channel_and_allocate();
339 
340 	/* If no channel available, return not found */
341 	if (new_channel == DMA_INVALID_CHANNEL) {
342 		system_interrupt_leave_critical_section();
343 
344 		return STATUS_ERR_NOT_FOUND;
345 	}
346 
347 	/* Set the channel */
348 	resource->channel_id = new_channel;
349 
350 	/** Perform a reset for the allocated channel */
351 	DMAC->CHID.reg = DMAC_CHID_ID(resource->channel_id);
352 	DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE;
353 	DMAC->CHCTRLA.reg = DMAC_CHCTRLA_SWRST;
354 
355 #ifdef FEATURE_DMA_CHANNEL_STANDBY
356 	if(config->run_in_standby){
357 		DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_RUNSTDBY;
358 	}
359 #endif
360 
361 	/** Configure the DMA control,channel registers and descriptors here */
362 	_dma_set_config(resource, config);
363 
364 	resource->descriptor = NULL;
365 
366 	/* Log the DMA resource into the internal DMA resource pool */
367 	_dma_active_resource[resource->channel_id] = resource;
368 
369 	system_interrupt_leave_critical_section();
370 
371 	return STATUS_OK;
372 }
373 
374 /**
375  * \brief Free an allocated DMA resource.
376  *
377  * This function will free an allocated DMA resource.
378  *
379  * \param[in,out] resource Pointer to the DMA resource
380  *
381  * \return Status of the free procedure.
382  *
383  * \retval STATUS_OK The DMA resource was freed successfully
384  * \retval STATUS_BUSY The DMA resource was busy and can't be freed
385  * \retval STATUS_ERR_NOT_INITIALIZED DMA resource was not initialized
386  */
dma_free(struct dma_resource * resource)387 enum status_code dma_free(struct dma_resource *resource)
388 {
389 	Assert(resource);
390 	Assert(resource->channel_id != DMA_INVALID_CHANNEL);
391 
392 	system_interrupt_enter_critical_section();
393 
394 	/* Check if channel is busy */
395 	if (dma_is_busy(resource)) {
396 		system_interrupt_leave_critical_section();
397 		return STATUS_BUSY;
398 	}
399 
400 	/* Check if DMA resource was not allocated */
401 	if (!(_dma_inst.allocated_channels & (1 << resource->channel_id))) {
402 		system_interrupt_leave_critical_section();
403 		return STATUS_ERR_NOT_INITIALIZED;
404 	}
405 
406 	/* Release the DMA resource */
407 	_dma_release_channel(resource->channel_id);
408 
409 	/* Reset the item in the DMA resource pool */
410 	_dma_active_resource[resource->channel_id] = NULL;
411 
412 	system_interrupt_leave_critical_section();
413 
414 	return STATUS_OK;
415 }
416 
417 /**
418  * \brief Start a DMA transfer.
419  *
420  * This function will start a DMA transfer through an allocated DMA resource.
421  *
422  * \param[in,out] resource Pointer to the DMA resource
423  *
424  * \return Status of the transfer start procedure.
425  *
426  * \retval STATUS_OK The transfer was started successfully
427  * \retval STATUS_BUSY The DMA resource was busy and the transfer was not started
428  * \retval STATUS_ERR_INVALID_ARG Transfer size is 0 and transfer was not started
429  */
dma_start_transfer_job(struct dma_resource * resource)430 enum status_code dma_start_transfer_job(struct dma_resource *resource)
431 {
432 	Assert(resource);
433 	Assert(resource->channel_id != DMA_INVALID_CHANNEL);
434 
435 	system_interrupt_enter_critical_section();
436 
437 	/* Check if resource was busy */
438 	if (resource->job_status == STATUS_BUSY) {
439 		system_interrupt_leave_critical_section();
440 		return STATUS_BUSY;
441 	}
442 
443 	/* Check if transfer size is valid */
444 	if (resource->descriptor->BTCNT.reg == 0) {
445 		system_interrupt_leave_critical_section();
446 		return STATUS_ERR_INVALID_ARG;
447 	}
448 
449 	/* Enable DMA interrupt */
450 	system_interrupt_enable(SYSTEM_INTERRUPT_MODULE_DMA);
451 
452 	/* Set the interrupt flag */
453 	DMAC->CHID.reg = DMAC_CHID_ID(resource->channel_id);
454 	DMAC->CHINTENSET.reg = (DMAC_CHINTENSET_MASK & g_chan_interrupt_flag[resource->channel_id]);
455 	/* Set job status */
456 	resource->job_status = STATUS_BUSY;
457 
458 	/* Set channel x descriptor 0 to the descriptor base address */
459 	memcpy(&descriptor_section[resource->channel_id], resource->descriptor,
460 						sizeof(DmacDescriptor));
461 
462 	/* Enable the transfer channel */
463 	DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;
464 
465 	system_interrupt_leave_critical_section();
466 
467 	return STATUS_OK;
468 }
469 
470 /**
471  * \brief Abort a DMA transfer.
472  *
473  * This function will abort a DMA transfer. The DMA channel used for the DMA
474  * resource will be disabled.
475  * The block transfer count will also be calculated and written to the DMA
476  * resource structure.
477  *
478  * \note The DMA resource will not be freed after calling this function.
479  *       The function \ref dma_free() can be used to free an allocated resource.
480  *
481  * \param[in,out] resource Pointer to the DMA resource
482  *
483  */
dma_abort_job(struct dma_resource * resource)484 void dma_abort_job(struct dma_resource *resource)
485 {
486 	uint32_t write_size;
487 	uint32_t total_size;
488 
489 	Assert(resource);
490 	Assert(resource->channel_id != DMA_INVALID_CHANNEL);
491 
492 	system_interrupt_enter_critical_section();
493 
494 	DMAC->CHID.reg = DMAC_CHID_ID(resource->channel_id);
495 	DMAC->CHCTRLA.reg = 0;
496 
497 	system_interrupt_leave_critical_section();
498 
499 	/* Get transferred size */
500 	total_size = descriptor_section[resource->channel_id].BTCNT.reg;
501 	write_size = _write_back_section[resource->channel_id].BTCNT.reg;
502 	resource->transfered_size = total_size - write_size;
503 
504 	resource->job_status = STATUS_ABORTED;
505 }
506 
507 /**
508  * \brief Suspend a DMA transfer.
509  *
510  * This function will request to suspend the transfer of the DMA resource.
511  * The channel is kept enabled, can receive transfer triggers (the transfer
512  * pending bit will be set), but will be removed from the arbitration scheme.
513  * The channel operation can be resumed by calling \ref dma_resume_job().
514  *
515  * \note This function sets the command to suspend the DMA channel
516  * associated with a DMA resource. The channel suspend interrupt flag
517  * indicates whether the transfer is truly suspended.
518  *
519  * \param[in] resource Pointer to the DMA resource
520  *
521  */
dma_suspend_job(struct dma_resource * resource)522 void dma_suspend_job(struct dma_resource *resource)
523 {
524 	Assert(resource);
525 	Assert(resource->channel_id != DMA_INVALID_CHANNEL);
526 
527 	system_interrupt_enter_critical_section();
528 
529 	/* Select the channel */
530 	DMAC->CHID.reg = DMAC_CHID_ID(resource->channel_id);
531 
532 	/* Send the suspend request */
533 	DMAC->CHCTRLB.reg |= DMAC_CHCTRLB_CMD_SUSPEND;
534 
535 	system_interrupt_leave_critical_section();
536 }
537 
538 /**
539  * \brief Resume a suspended DMA transfer.
540  *
541  * This function try to resume a suspended transfer of a DMA resource.
542  *
543  * \param[in] resource Pointer to the DMA resource
544  *
545  */
dma_resume_job(struct dma_resource * resource)546 void dma_resume_job(struct dma_resource *resource)
547 {
548 	uint32_t bitmap_channel;
549 	uint32_t count = 0;
550 
551 	Assert(resource);
552 	Assert(resource->channel_id != DMA_INVALID_CHANNEL);
553 
554 	/* Get bitmap of the allocated DMA channel */
555 	bitmap_channel = (1 << resource->channel_id);
556 
557 	/* Check if channel was suspended */
558 	if (resource->job_status != STATUS_SUSPEND) {
559 		return;
560 	}
561 
562 	system_interrupt_enter_critical_section();
563 
564 	/* Send resume request */
565 	DMAC->CHID.reg = DMAC_CHID_ID(resource->channel_id);
566 	DMAC->CHCTRLB.reg |= DMAC_CHCTRLB_CMD_RESUME;
567 
568 	system_interrupt_leave_critical_section();
569 
570 	/* Check if transfer job resumed */
571 	for (count = 0; count < MAX_JOB_RESUME_COUNT; count++) {
572 		if ((DMAC->BUSYCH.reg & bitmap_channel) == bitmap_channel) {
573 			break;
574 		}
575 	}
576 
577 	if (count < MAX_JOB_RESUME_COUNT) {
578 		/* Job resumed */
579 		resource->job_status = STATUS_BUSY;
580 	} else {
581 		/* Job resume timeout */
582 		resource->job_status = STATUS_ERR_TIMEOUT;
583 	}
584 }
585 
586 /**
587  * \brief Create a DMA transfer descriptor with configurations.
588  *
589  * This function will set the transfer configurations to the DMA transfer
590  * descriptor.
591  *
592  * \param[in] descriptor Pointer to the DMA transfer descriptor
593  * \param[in] config Pointer to the descriptor configuration structure
594  *
595  */
dma_descriptor_create(DmacDescriptor * descriptor,struct dma_descriptor_config * config)596 void dma_descriptor_create(DmacDescriptor* descriptor,
597 	struct dma_descriptor_config *config)
598 {
599 	/* Set block transfer control */
600 	descriptor->BTCTRL.bit.VALID = config->descriptor_valid;
601 	descriptor->BTCTRL.bit.EVOSEL = config->event_output_selection;
602 	descriptor->BTCTRL.bit.BLOCKACT = config->block_action;
603 	descriptor->BTCTRL.bit.BEATSIZE = config->beat_size;
604 	descriptor->BTCTRL.bit.SRCINC = config->src_increment_enable;
605 	descriptor->BTCTRL.bit.DSTINC = config->dst_increment_enable;
606 	descriptor->BTCTRL.bit.STEPSEL = config->step_selection;
607 	descriptor->BTCTRL.bit.STEPSIZE = config->step_size;
608 
609 	/* Set transfer size, source address and destination address */
610 	descriptor->BTCNT.reg = config->block_transfer_count;
611 	descriptor->SRCADDR.reg = config->source_address;
612 	descriptor->DSTADDR.reg = config->destination_address;
613 
614 	/* Set next transfer descriptor address */
615 	descriptor->DESCADDR.reg = config->next_descriptor_address;
616 }
617 
618 /**
619  * \brief Add a DMA transfer descriptor to a DMA resource.
620  *
621  * This function will add a DMA transfer descriptor to a DMA resource.
622  * If there was a transfer descriptor already allocated to the DMA resource,
623  * the descriptor will be linked to the next descriptor address.
624  *
625  * \param[in] resource Pointer to the DMA resource
626  * \param[in] descriptor Pointer to the transfer descriptor
627  *
628  * \retval STATUS_OK The descriptor is added to the DMA resource
629  * \retval STATUS_BUSY The DMA resource was busy and the descriptor is not added
630  */
dma_add_descriptor(struct dma_resource * resource,DmacDescriptor * descriptor)631 enum status_code dma_add_descriptor(struct dma_resource *resource,
632 		DmacDescriptor* descriptor)
633 {
634 	DmacDescriptor* desc = resource->descriptor;
635 
636 	if (resource->job_status == STATUS_BUSY) {
637 		return STATUS_BUSY;
638 	}
639 
640 	/* Look up for an empty space for the descriptor */
641 	if (desc == NULL) {
642 		resource->descriptor = descriptor;
643 	} else {
644 		/* Looking for end of descriptor link */
645 		while(desc->DESCADDR.reg != 0) {
646 			desc = (DmacDescriptor*)(desc->DESCADDR.reg);
647 		}
648 
649 		/* Set to the end of descriptor list */
650 		desc->DESCADDR.reg = (uint32_t)descriptor;
651 	}
652 
653 	return STATUS_OK;
654 }
655