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