mbed library sources. Supersedes mbed-src.

Fork of mbed-dev by mbed official

Revision:
15:a81a8d6c1dfe
diff -r d797fbdad187 -r a81a8d6c1dfe targets/hal/TARGET_Atmel/TARGET_SAM_CortexM0P/drivers/dma/dma.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/hal/TARGET_Atmel/TARGET_SAM_CortexM0P/drivers/dma/dma.c	Wed Nov 04 16:30:11 2015 +0000
@@ -0,0 +1,654 @@
+/*
+ * \file
+ *
+ * \brief SAM Direct Memory Access Controller Driver
+ *
+ * Copyright (C) 2014-2015 Atmel Corporation. All rights reserved.
+ *
+ * \asf_license_start
+ *
+ * \page License
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * 3. The name of Atmel may not be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * 4. This software may only be redistributed and used in connection with an
+ *    Atmel microcontroller product.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
+ * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * \asf_license_stop
+ *
+ */
+/*
+ * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
+ */
+
+#include <string.h>
+#include "dma.h"
+#include "clock.h"
+#include "system_interrupt.h"
+
+struct _dma_module {
+    volatile bool _dma_init;
+    volatile uint32_t allocated_channels;
+    uint8_t free_channels;
+};
+
+struct _dma_module _dma_inst = {
+    ._dma_init = false,
+    .allocated_channels = 0,
+    .free_channels = CONF_MAX_USED_CHANNEL_NUM,
+};
+
+/** Maximum retry counter for resuming a job transfer. */
+#define MAX_JOB_RESUME_COUNT    10000
+
+/** DMA channel mask. */
+#define DMA_CHANNEL_MASK   (0x1f)
+
+COMPILER_ALIGNED(16)
+DmacDescriptor descriptor_section[CONF_MAX_USED_CHANNEL_NUM] SECTION_DMAC_DESCRIPTOR;
+
+/** Initial write back memory section. */
+COMPILER_ALIGNED(16)
+static DmacDescriptor _write_back_section[CONF_MAX_USED_CHANNEL_NUM] SECTION_DMAC_DESCRIPTOR;
+
+/** Internal DMA resource pool. */
+static struct dma_resource* _dma_active_resource[CONF_MAX_USED_CHANNEL_NUM];
+
+/* DMA channel interrup flag. */
+uint8_t g_chan_interrupt_flag[CONF_MAX_USED_CHANNEL_NUM]= {0};
+
+/**
+ * \brief Find a free channel for a DMA resource.
+ *
+ * Find a channel for the requested DMA resource.
+ *
+ * \return Status of channel allocation.
+ * \retval DMA_INVALID_CHANNEL  No channel available
+ * \retval count          Allocated channel for the DMA resource
+ */
+static uint8_t _dma_find_first_free_channel_and_allocate(void)
+{
+    uint8_t count;
+    uint32_t tmp;
+    bool allocated = false;
+
+    system_interrupt_enter_critical_section();
+
+    tmp = _dma_inst.allocated_channels;
+
+    for (count = 0; count < CONF_MAX_USED_CHANNEL_NUM; ++count) {
+        if (!(tmp & 0x00000001)) {
+            /* If free channel found, set as allocated and return
+             *number */
+
+            _dma_inst.allocated_channels |= 1 << count;
+            _dma_inst.free_channels--;
+            allocated = true;
+
+            break;
+        }
+
+        tmp = tmp >> 1;
+    }
+
+    system_interrupt_leave_critical_section();
+
+    if (!allocated) {
+        return DMA_INVALID_CHANNEL;
+    } else {
+        return count;
+    }
+}
+
+/**
+ * \brief Release an allocated DMA channel.
+ *
+ * \param[in]  channel  Channel id to be released
+ *
+ */
+static void _dma_release_channel(uint8_t channel)
+{
+    _dma_inst.allocated_channels &= ~(1 << channel);
+    _dma_inst.free_channels++;
+}
+
+/**
+ * \brief Configure the DMA resource.
+ *
+ * \param[in]  dma_resource Pointer to a DMA resource instance
+ * \param[out] resource_config Configurations of the DMA resource
+ *
+ */
+static void _dma_set_config(struct dma_resource *resource,
+                            struct dma_resource_config *resource_config)
+{
+    Assert(resource);
+    Assert(resource_config);
+    uint32_t temp_CHCTRLB_reg;
+    system_interrupt_enter_critical_section();
+
+    /** Select the DMA channel and clear software trigger */
+    DMAC->CHID.reg = DMAC_CHID_ID(resource->channel_id);
+    DMAC->SWTRIGCTRL.reg &= (uint32_t)(~(1 << resource->channel_id));
+
+    temp_CHCTRLB_reg = DMAC_CHCTRLB_LVL(resource_config->priority) | \
+                       DMAC_CHCTRLB_TRIGSRC(resource_config->peripheral_trigger) | \
+                       DMAC_CHCTRLB_TRIGACT(resource_config->trigger_action);
+
+
+    if(resource_config->event_config.input_action) {
+        temp_CHCTRLB_reg |= DMAC_CHCTRLB_EVIE | DMAC_CHCTRLB_EVACT(
+                                resource_config->event_config.input_action);
+    }
+
+    /** Enable event output, the event output selection is configured in
+     * each transfer descriptor  */
+    if (resource_config->event_config.event_output_enable) {
+        temp_CHCTRLB_reg |= DMAC_CHCTRLB_EVOE;
+    }
+
+    /* Write config to CTRLB register */
+    DMAC->CHCTRLB.reg = temp_CHCTRLB_reg;
+
+
+
+    system_interrupt_leave_critical_section();
+}
+
+/**
+ * \brief DMA interrupt service routine.
+ *
+ */
+void DMAC_Handler( void )
+{
+    uint8_t active_channel;
+    struct dma_resource *resource;
+    uint8_t isr;
+    uint32_t write_size;
+    uint32_t total_size;
+
+    system_interrupt_enter_critical_section();
+
+    /* Get Pending channel */
+    active_channel =  DMAC->INTPEND.reg & DMAC_INTPEND_ID_Msk;
+
+    Assert(_dma_active_resource[active_channel]);
+
+    /* Get active DMA resource based on channel */
+    resource = _dma_active_resource[active_channel];
+
+    /* Select the active channel */
+    DMAC->CHID.reg = DMAC_CHID_ID(resource->channel_id);
+    isr = DMAC->CHINTFLAG.reg;
+
+    /* Calculate block transfer size of the DMA transfer */
+    total_size = descriptor_section[resource->channel_id].BTCNT.reg;
+    write_size = _write_back_section[resource->channel_id].BTCNT.reg;
+    resource->transfered_size = total_size - write_size;
+
+    /* DMA channel interrupt handler */
+    if (isr & DMAC_CHINTENCLR_TERR) {
+        /* Clear transfer error flag */
+        DMAC->CHINTFLAG.reg = DMAC_CHINTENCLR_TERR;
+
+        /* Set I/O ERROR status */
+        resource->job_status = STATUS_ERR_IO;
+
+        /* Execute the callback function */
+        if ((resource->callback_enable & (1<<DMA_CALLBACK_TRANSFER_ERROR)) &&
+                (resource->callback[DMA_CALLBACK_TRANSFER_ERROR])) {
+            resource->callback[DMA_CALLBACK_TRANSFER_ERROR](resource);
+        }
+    } else if (isr & DMAC_CHINTENCLR_TCMPL) {
+        /* Clear the transfer complete flag */
+        DMAC->CHINTFLAG.reg = DMAC_CHINTENCLR_TCMPL;
+
+        /* Set job status */
+        resource->job_status = STATUS_OK;
+
+        /* Execute the callback function */
+        if ((resource->callback_enable & (1 << DMA_CALLBACK_TRANSFER_DONE)) &&
+                (resource->callback[DMA_CALLBACK_TRANSFER_DONE])) {
+            resource->callback[DMA_CALLBACK_TRANSFER_DONE](resource);
+        }
+    } else if (isr & DMAC_CHINTENCLR_SUSP) {
+        /* Clear channel suspend flag */
+        DMAC->CHINTFLAG.reg = DMAC_CHINTENCLR_SUSP;
+
+        /* Set job status */
+        resource->job_status = STATUS_SUSPEND;
+
+        /* Execute the callback function */
+        if ((resource->callback_enable & (1 << DMA_CALLBACK_CHANNEL_SUSPEND)) &&
+                (resource->callback[DMA_CALLBACK_CHANNEL_SUSPEND])) {
+            resource->callback[DMA_CALLBACK_CHANNEL_SUSPEND](resource);
+        }
+    }
+
+    system_interrupt_leave_critical_section();
+}
+
+/**
+ * \brief Initializes config with predefined default values.
+ *
+ * This function will initialize a given DMA configuration structure to
+ * a set of known default values. This function should be called on
+ * any new instance of the configuration structure before being
+ * modified by the user application.
+ *
+ * The default configuration is as follows:
+ *  \li Software trigger is used as the transfer trigger
+ *  \li Priority level 0
+ *  \li Only software/event trigger
+ *  \li Requires a trigger for each transaction
+ *  \li No event input /output
+ *  \li DMA channel is disabled during sleep mode (if has the feature)
+ * \param[out] config Pointer to the configuration
+ *
+ */
+void dma_get_config_defaults(struct dma_resource_config *config)
+{
+    Assert(config);
+    /* Set as priority 0 */
+    config->priority = DMA_PRIORITY_LEVEL_0;
+    /* Only software/event trigger */
+    config->peripheral_trigger = 0;
+    /* Transaction trigger */
+    config->trigger_action = DMA_TRIGGER_ACTON_TRANSACTION;
+
+    /* Event configurations, no event input/output */
+    config->event_config.input_action = DMA_EVENT_INPUT_NOACT;
+    config->event_config.event_output_enable = false;
+#ifdef FEATURE_DMA_CHANNEL_STANDBY
+    config->run_in_standby = false;
+#endif
+}
+
+/**
+ * \brief Allocate a DMA with configurations.
+ *
+ * This function will allocate a proper channel for a DMA transfer request.
+ *
+ * \param[in,out]  dma_resource Pointer to a DMA resource instance
+ * \param[in] transfer_config Configurations of the DMA transfer
+ *
+ * \return Status of the allocation procedure.
+ *
+ * \retval STATUS_OK The DMA resource was allocated successfully
+ * \retval STATUS_ERR_NOT_FOUND DMA resource allocation failed
+ */
+enum status_code dma_allocate(struct dma_resource *resource,
+                              struct dma_resource_config *config)
+{
+    uint8_t new_channel;
+
+    Assert(resource);
+
+    system_interrupt_enter_critical_section();
+
+    if (!_dma_inst._dma_init) {
+        /* Initialize clocks for DMA */
+#if (SAML21) || (SAMC20) || (SAMC21)
+        system_ahb_clock_set_mask(MCLK_AHBMASK_DMAC);
+#else
+        system_ahb_clock_set_mask(PM_AHBMASK_DMAC);
+        system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBB,
+                                  PM_APBBMASK_DMAC);
+#endif
+
+        /* Perform a software reset before enable DMA controller */
+        DMAC->CTRL.reg &= ~DMAC_CTRL_DMAENABLE;
+        DMAC->CTRL.reg = DMAC_CTRL_SWRST;
+
+        /* Setup descriptor base address and write back section base
+         * address */
+        DMAC->BASEADDR.reg = (uint32_t)descriptor_section;
+        DMAC->WRBADDR.reg = (uint32_t)_write_back_section;
+
+        /* Enable all priority level at the same time */
+        DMAC->CTRL.reg = DMAC_CTRL_DMAENABLE | DMAC_CTRL_LVLEN(0xf);
+
+        _dma_inst._dma_init = true;
+    }
+
+    /* Find the proper channel */
+    new_channel = _dma_find_first_free_channel_and_allocate();
+
+    /* If no channel available, return not found */
+    if (new_channel == DMA_INVALID_CHANNEL) {
+        system_interrupt_leave_critical_section();
+
+        return STATUS_ERR_NOT_FOUND;
+    }
+
+    /* Set the channel */
+    resource->channel_id = new_channel;
+
+    /** Perform a reset for the allocated channel */
+    DMAC->CHID.reg = DMAC_CHID_ID(resource->channel_id);
+    DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE;
+    DMAC->CHCTRLA.reg = DMAC_CHCTRLA_SWRST;
+
+#ifdef FEATURE_DMA_CHANNEL_STANDBY
+    if(config->run_in_standby) {
+        DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_RUNSTDBY;
+    }
+#endif
+
+    /** Configure the DMA control,channel registers and descriptors here */
+    _dma_set_config(resource, config);
+
+    resource->descriptor = NULL;
+
+    /* Log the DMA resource into the internal DMA resource pool */
+    _dma_active_resource[resource->channel_id] = resource;
+
+    system_interrupt_leave_critical_section();
+
+    return STATUS_OK;
+}
+
+/**
+ * \brief Free an allocated DMA resource.
+ *
+ * This function will free an allocated DMA resource.
+ *
+ * \param[in,out] resource Pointer to the DMA resource
+ *
+ * \return Status of the free procedure.
+ *
+ * \retval STATUS_OK The DMA resource was freed successfully
+ * \retval STATUS_BUSY The DMA resource was busy and can't be freed
+ * \retval STATUS_ERR_NOT_INITIALIZED DMA resource was not initialized
+ */
+enum status_code dma_free(struct dma_resource *resource)
+{
+    Assert(resource);
+    Assert(resource->channel_id != DMA_INVALID_CHANNEL);
+
+    system_interrupt_enter_critical_section();
+
+    /* Check if channel is busy */
+    if (dma_is_busy(resource)) {
+        system_interrupt_leave_critical_section();
+        return STATUS_BUSY;
+    }
+
+    /* Check if DMA resource was not allocated */
+    if (!(_dma_inst.allocated_channels & (1 << resource->channel_id))) {
+        system_interrupt_leave_critical_section();
+        return STATUS_ERR_NOT_INITIALIZED;
+    }
+
+    /* Release the DMA resource */
+    _dma_release_channel(resource->channel_id);
+
+    /* Reset the item in the DMA resource pool */
+    _dma_active_resource[resource->channel_id] = NULL;
+
+    system_interrupt_leave_critical_section();
+
+    return STATUS_OK;
+}
+
+/**
+ * \brief Start a DMA transfer.
+ *
+ * This function will start a DMA transfer through an allocated DMA resource.
+ *
+ * \param[in,out] resource Pointer to the DMA resource
+ *
+ * \return Status of the transfer start procedure.
+ *
+ * \retval STATUS_OK The transfer was started successfully
+ * \retval STATUS_BUSY The DMA resource was busy and the transfer was not started
+ * \retval STATUS_ERR_INVALID_ARG Transfer size is 0 and transfer was not started
+ */
+enum status_code dma_start_transfer_job(struct dma_resource *resource)
+{
+    Assert(resource);
+    Assert(resource->channel_id != DMA_INVALID_CHANNEL);
+
+    system_interrupt_enter_critical_section();
+
+    /* Check if resource was busy */
+    if (resource->job_status == STATUS_BUSY) {
+        system_interrupt_leave_critical_section();
+        return STATUS_BUSY;
+    }
+
+    /* Check if transfer size is valid */
+    if (resource->descriptor->BTCNT.reg == 0) {
+        system_interrupt_leave_critical_section();
+        return STATUS_ERR_INVALID_ARG;
+    }
+
+    /* Enable DMA interrupt */
+    system_interrupt_enable(SYSTEM_INTERRUPT_MODULE_DMA);
+
+    /* Set the interrupt flag */
+    DMAC->CHID.reg = DMAC_CHID_ID(resource->channel_id);
+    DMAC->CHINTENSET.reg = (DMAC_CHINTENSET_MASK & g_chan_interrupt_flag[resource->channel_id]);
+    /* Set job status */
+    resource->job_status = STATUS_BUSY;
+
+    /* Set channel x descriptor 0 to the descriptor base address */
+    memcpy(&descriptor_section[resource->channel_id], resource->descriptor,
+           sizeof(DmacDescriptor));
+
+    /* Enable the transfer channel */
+    DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;
+
+    system_interrupt_leave_critical_section();
+
+    return STATUS_OK;
+}
+
+/**
+ * \brief Abort a DMA transfer.
+ *
+ * This function will abort a DMA transfer. The DMA channel used for the DMA
+ * resource will be disabled.
+ * The block transfer count will be also calculated and written to the DMA
+ * resource structure.
+ *
+ * \note The DMA resource will not be freed after calling this function.
+ *       The function \ref dma_free() can be used to free an allocated resource.
+ *
+ * \param[in,out] resource Pointer to the DMA resource
+ *
+ */
+void dma_abort_job(struct dma_resource *resource)
+{
+    uint32_t write_size;
+    uint32_t total_size;
+
+    Assert(resource);
+    Assert(resource->channel_id != DMA_INVALID_CHANNEL);
+
+    system_interrupt_enter_critical_section();
+
+    DMAC->CHID.reg = DMAC_CHID_ID(resource->channel_id);
+    DMAC->CHCTRLA.reg = 0;
+
+    system_interrupt_leave_critical_section();
+
+    /* Get transferred size */
+    total_size = descriptor_section[resource->channel_id].BTCNT.reg;
+    write_size = _write_back_section[resource->channel_id].BTCNT.reg;
+    resource->transfered_size = total_size - write_size;
+
+    resource->job_status = STATUS_ABORTED;
+}
+
+/**
+ * \brief Suspend a DMA transfer.
+ *
+ * This function will request to suspend the transfer of the DMA resource.
+ * The channel is kept enabled, can receive transfer triggers (the transfer
+ * pending bit will be set), but will be removed from the arbitration scheme.
+ * The channel operation can be resumed by calling \ref dma_resume_job().
+ *
+ * \note This function sets the command to suspend the DMA channel
+ * associated with a DMA resource. The channel suspend interrupt flag
+ * indicates whether the transfer is truly suspended.
+ *
+ * \param[in] resource Pointer to the DMA resource
+ *
+ */
+void dma_suspend_job(struct dma_resource *resource)
+{
+    Assert(resource);
+    Assert(resource->channel_id != DMA_INVALID_CHANNEL);
+
+    system_interrupt_enter_critical_section();
+
+    /* Select the channel */
+    DMAC->CHID.reg = DMAC_CHID_ID(resource->channel_id);
+
+    /* Send the suspend request */
+    DMAC->CHCTRLB.reg |= DMAC_CHCTRLB_CMD_SUSPEND;
+
+    system_interrupt_leave_critical_section();
+}
+
+/**
+ * \brief Resume a suspended DMA transfer.
+ *
+ * This function try to resume a suspended transfer of a DMA resource.
+ *
+ * \param[in] resource Pointer to the DMA resource
+ *
+ */
+void dma_resume_job(struct dma_resource *resource)
+{
+    uint32_t bitmap_channel;
+    uint32_t count = 0;
+
+    Assert(resource);
+    Assert(resource->channel_id != DMA_INVALID_CHANNEL);
+
+    /* Get bitmap of the allocated DMA channel */
+    bitmap_channel = (1 << resource->channel_id);
+
+    /* Check if channel was suspended */
+    if (resource->job_status != STATUS_SUSPEND) {
+        return;
+    }
+
+    system_interrupt_enter_critical_section();
+
+    /* Send resume request */
+    DMAC->CHID.reg = DMAC_CHID_ID(resource->channel_id);
+    DMAC->CHCTRLB.reg |= DMAC_CHCTRLB_CMD_RESUME;
+
+    system_interrupt_leave_critical_section();
+
+    /* Check if transfer job resumed */
+    for (count = 0; count < MAX_JOB_RESUME_COUNT; count++) {
+        if ((DMAC->BUSYCH.reg & bitmap_channel) == bitmap_channel) {
+            break;
+        }
+    }
+
+    if (count < MAX_JOB_RESUME_COUNT) {
+        /* Job resumed */
+        resource->job_status = STATUS_BUSY;
+    } else {
+        /* Job resume timeout */
+        resource->job_status = STATUS_ERR_TIMEOUT;
+    }
+}
+
+/**
+ * \brief Create a DMA transfer descriptor with configurations.
+ *
+ * This function will set the transfer configurations to the DMA transfer
+ * descriptor.
+ *
+ * \param[in] descriptor Pointer to the DMA transfer descriptor
+ * \param[in] config Pointer to the descriptor configuration structure
+ *
+ */
+void dma_descriptor_create(DmacDescriptor* descriptor,
+                           struct dma_descriptor_config *config)
+{
+    /* Set block transfer control */
+    descriptor->BTCTRL.bit.VALID = config->descriptor_valid;
+    descriptor->BTCTRL.bit.EVOSEL = config->event_output_selection;
+    descriptor->BTCTRL.bit.BLOCKACT = config->block_action;
+    descriptor->BTCTRL.bit.BEATSIZE = config->beat_size;
+    descriptor->BTCTRL.bit.SRCINC = config->src_increment_enable;
+    descriptor->BTCTRL.bit.DSTINC = config->dst_increment_enable;
+    descriptor->BTCTRL.bit.STEPSEL = config->step_selection;
+    descriptor->BTCTRL.bit.STEPSIZE = config->step_size;
+
+    /* Set transfer size, source address and destination address */
+    descriptor->BTCNT.reg = config->block_transfer_count;
+    descriptor->SRCADDR.reg = config->source_address;
+    descriptor->DSTADDR.reg = config->destination_address;
+
+    /* Set next transfer descriptor address */
+    descriptor->DESCADDR.reg = config->next_descriptor_address;
+}
+
+/**
+ * \brief Add a DMA transfer descriptor to a DMA resource.
+ *
+ * This function will add a DMA transfer descriptor to a DMA resource.
+ * If there was a transfer descriptor already allocated to the DMA resource,
+ * the descriptor will be linked to the next descriptor address.
+ *
+ * \param[in] resource Pointer to the DMA resource
+ * \param[in] descriptor Pointer to the transfer descriptor
+ *
+ * \retval STATUS_OK The descriptor is added to the DMA resource
+ * \retval STATUS_BUSY The DMA resource was busy and the descriptor is not added
+ */
+enum status_code dma_add_descriptor(struct dma_resource *resource,
+                                    DmacDescriptor* descriptor)
+{
+    DmacDescriptor* desc = resource->descriptor;
+
+    if (resource->job_status == STATUS_BUSY) {
+        return STATUS_BUSY;
+    }
+
+    /* Look up for an empty space for the descriptor */
+    if (desc == NULL) {
+        resource->descriptor = descriptor;
+    } else {
+        /* Looking for end of descriptor link */
+        while(desc->DESCADDR.reg != 0) {
+            desc = (DmacDescriptor*)(desc->DESCADDR.reg);
+        }
+
+        /* Set to the end of descriptor list */
+        desc->DESCADDR.reg = (uint32_t)descriptor;
+    }
+
+    return STATUS_OK;
+}