--- /dev/null
+/* ----------------------------------------------------------------------------\r
+ * SAM Software Package License\r
+ * ----------------------------------------------------------------------------\r
+ * Copyright (c) 2015, Atmel Corporation\r
+ *\r
+ * All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ *\r
+ * - Redistributions of source code must retain the above copyright notice,\r
+ * this list of conditions and the disclaimer below.\r
+ *\r
+ * Atmel's name may not be used to endorse or promote products derived from\r
+ * this software without specific prior written permission.\r
+ *\r
+ * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR\r
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE\r
+ * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,\r
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,\r
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\r
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\r
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\r
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * ----------------------------------------------------------------------------\r
+ */\r
+\r
+/** \addtogroup xdmad_module\r
+ *\r
+ * \section Xdma xDma Configuration Usage\r
+ *\r
+ * To configure a XDMA channel, the user has to follow these few steps :\r
+ * <ul>\r
+ * <li> Initialize a XDMA driver instance by XDMAD_Initialize().</li>\r
+ * <li> choose an available (disabled) channel using XDMAD_AllocateChannel().</li>\r
+ * <li> After the XDMAC selected channel has been programmed, XDMAD_PrepareChannel() is to enable\r
+ * clock and dma peripheral of the DMA, and set Configuration register to set up the transfer type\r
+ * (memory or non-memory peripheral for source and destination) and flow control device.</li>\r
+ * <li> Invoke XDMAD_StartTransfer() to start DMA transfer or XDMAD_StopTransfer() to force stop DMA transfer.</li>\r
+ * <li> Once the buffer of data is transferred, XDMAD_IsTransferDone() checks if DMA transfer is finished.</li>\r
+ * <li> XDMAD_Handler() handles XDMA interrupt, and invoking XDMAD_SetCallback() if provided.</li>\r
+ * </ul>\r
+ *\r
+ * Related files:\n\r
+ * \ref xdmad.h\n\r
+ * \ref xdmad.c\n\r
+ */\r
+\r
+/** \file */\r
+\r
+/** \addtogroup dmad_functions\r
+ @{*/\r
+\r
+/*----------------------------------------------------------------------------\r
+ * Includes\r
+ *----------------------------------------------------------------------------*/\r
+\r
+#include "peripherals/aic.h"\r
+#include "peripherals/pmc.h"\r
+#include "peripherals/xdmad.h"\r
+\r
+#include <assert.h>\r
+#include "compiler.h"\r
+\r
+/*----------------------------------------------------------------------------\r
+ * Local definitions\r
+ *----------------------------------------------------------------------------*/\r
+\r
+#define XDMAD_CHANNELS (XDMAC_CONTROLLERS * XDMAC_CHANNELS)\r
+\r
+/** DMA state for channel */\r
+enum {\r
+ XDMAD_STATE_FREE = 0, /**< Free channel */\r
+ XDMAD_STATE_ALLOCATED, /**< Allocated to some peripheral */\r
+ XDMAD_STATE_STARTED, /**< DMA started */\r
+ XDMAD_STATE_DONE, /**< DMA transfer done */\r
+};\r
+\r
+/** DMA driver channel */\r
+struct _xdmad_channel\r
+{\r
+ Xdmac *xdmac; /**< XDMAC instance */\r
+ uint32_t id; /**< Channel ID */\r
+ xdmad_callback_t callback; /**< Callback */\r
+ void *user_arg; /**< Callback argument */\r
+ uint8_t src_txif; /**< Source TX Interface ID */\r
+ uint8_t src_rxif; /**< Source RX Interface ID */\r
+ uint8_t dest_txif; /**< Destination TX Interface ID */\r
+ uint8_t dest_rxif; /**< Destination RX Interface ID */\r
+ volatile uint8_t state; /**< Channel State */\r
+};\r
+\r
+/** DMA driver instance */\r
+struct _xdmad {\r
+ struct _xdmad_channel channels[XDMAD_CHANNELS];\r
+ bool polling;\r
+ uint8_t polling_timeout;\r
+};\r
+\r
+static struct _xdmad _xdmad;\r
+\r
+/*----------------------------------------------------------------------------\r
+ * Local functions\r
+ *----------------------------------------------------------------------------*/\r
+\r
+static inline struct _xdmad_channel *_xdmad_channel(uint32_t controller, uint32_t channel)\r
+{\r
+ return &_xdmad.channels[controller * XDMAC_CHANNELS + channel];\r
+}\r
+\r
+/**\r
+ * \brief xDMA interrupt handler\r
+ * \param pXdmad Pointer to DMA driver instance.\r
+ */\r
+static void xdmad_handler(void)\r
+{\r
+ uint32_t cont;\r
+\r
+ for (cont= 0; cont< XDMAC_CONTROLLERS; cont++) {\r
+ uint32_t chan, gis, gcs;\r
+\r
+ Xdmac *xdmac = xdmac_get_instance(cont);\r
+\r
+ gis = xdmac_get_global_isr(xdmac);\r
+ if ((gis & 0xFFFF) == 0)\r
+ continue;\r
+\r
+ gcs = xdmac_get_global_channel_status(xdmac);\r
+ for (chan = 0; chan < XDMAC_CHANNELS; chan++) {\r
+ struct _xdmad_channel *channel;\r
+ bool exec = false;\r
+\r
+ if (!(gis & (1 << chan)))\r
+ continue;\r
+\r
+ channel = _xdmad_channel(cont, chan);\r
+ if (channel->state == XDMAD_STATE_FREE)\r
+ continue;\r
+\r
+ if (!(gcs & (1 << chan))) {\r
+ uint32_t cis = xdmac_get_channel_isr(xdmac, chan);\r
+\r
+ if (cis & XDMAC_CIS_BIS) {\r
+ if (!(xdmac_get_channel_it_mask(xdmac, chan) & XDMAC_CIM_LIM)) {\r
+ channel->state = XDMAD_STATE_DONE;\r
+ exec = 1;\r
+ }\r
+ }\r
+\r
+ if (cis & XDMAC_CIS_LIS) {\r
+ channel->state = XDMAD_STATE_DONE;\r
+ exec = 1;\r
+ }\r
+\r
+ if (cis & XDMAC_CIS_DIS) {\r
+ channel->state = XDMAD_STATE_DONE;\r
+ exec = 1;\r
+ }\r
+ }\r
+\r
+ /* Execute callback */\r
+ if (exec && channel->callback) {\r
+ channel->callback(channel, channel->user_arg);\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+\r
+/*----------------------------------------------------------------------------\r
+ * Exported functions\r
+ *----------------------------------------------------------------------------*/\r
+\r
+void xdmad_initialize(bool polling)\r
+{\r
+ uint32_t cont, chan;\r
+\r
+ _xdmad.polling = polling;\r
+\r
+ for (cont = 0; cont < XDMAC_CONTROLLERS; cont++) {\r
+ Xdmac* xdmac = xdmac_get_instance(cont);\r
+ for (chan = 0; chan < XDMAC_CHANNELS; chan++) {\r
+ xdmac_get_channel_isr(xdmac, chan);\r
+ struct _xdmad_channel *channel = _xdmad_channel(cont, chan);\r
+ channel->xdmac = xdmac;\r
+ channel->id = chan;\r
+ channel->callback = 0;\r
+ channel->user_arg = 0;\r
+ channel->src_txif = 0;\r
+ channel->src_rxif = 0;\r
+ channel->dest_txif = 0;\r
+ channel->dest_rxif = 0;\r
+ channel->state = XDMAD_STATE_FREE;\r
+ }\r
+\r
+ if (!polling) {\r
+ uint32_t pid = xdmac_get_periph_id(xdmac);\r
+ /* enable interrupts */\r
+ aic_set_source_vector(pid, xdmad_handler);\r
+ aic_enable(pid);\r
+ }\r
+ }\r
+}\r
+\r
+void xdmad_poll(void)\r
+{\r
+ if (_xdmad.polling)\r
+ xdmad_handler();\r
+}\r
+\r
+struct _xdmad_channel *xdmad_allocate_channel(uint8_t src, uint8_t dest)\r
+{\r
+ uint32_t i;\r
+\r
+ /* Reject peripheral to peripheral transfers */\r
+ if (src != XDMAD_PERIPH_MEMORY && dest != XDMAD_PERIPH_MEMORY) {\r
+ return NULL;\r
+ }\r
+\r
+ for (i = 0; i < XDMAD_CHANNELS; i++) {\r
+ struct _xdmad_channel *channel = &_xdmad.channels[i];\r
+ Xdmac *xdmac = channel->xdmac;\r
+\r
+ if (channel->state == XDMAD_STATE_FREE) {\r
+ /* Check if source peripheral matches this channel controller */\r
+ if (src != XDMAD_PERIPH_MEMORY)\r
+ if (!is_peripheral_on_xdma_controller(src, xdmac))\r
+ continue;\r
+\r
+ /* Check if destination peripheral matches this channel controller */\r
+ if (dest != XDMAD_PERIPH_MEMORY)\r
+ if (!is_peripheral_on_xdma_controller(dest, xdmac))\r
+ continue;\r
+\r
+ /* Allocate the channel */\r
+ channel->state = XDMAD_STATE_ALLOCATED;\r
+ channel->src_txif = get_peripheral_xdma_channel(src, xdmac, true);\r
+ channel->src_rxif = get_peripheral_xdma_channel(src, xdmac, false);\r
+ channel->dest_txif = get_peripheral_xdma_channel(dest, xdmac, true);\r
+ channel->dest_rxif = get_peripheral_xdma_channel(dest, xdmac, false);\r
+\r
+ return channel;\r
+ }\r
+ }\r
+ return NULL;\r
+}\r
+\r
+uint32_t xdmad_free_channel(struct _xdmad_channel *channel)\r
+{\r
+ switch (channel->state) {\r
+ case XDMAD_STATE_STARTED:\r
+ return XDMAD_BUSY;\r
+ case XDMAD_STATE_ALLOCATED:\r
+ case XDMAD_STATE_DONE:\r
+ channel->state = XDMAD_STATE_FREE;\r
+ break;\r
+ }\r
+ return XDMAD_OK;\r
+}\r
+\r
+uint32_t xdmad_set_callback(struct _xdmad_channel *channel,\r
+ xdmad_callback_t callback, void *user_arg)\r
+{\r
+ if (channel->state == XDMAD_STATE_FREE)\r
+ return XDMAD_ERROR;\r
+ else if (channel->state == XDMAD_STATE_STARTED)\r
+ return XDMAD_BUSY;\r
+\r
+ channel->callback = callback;\r
+ channel->user_arg = user_arg;\r
+\r
+ return XDMAD_OK;\r
+}\r
+\r
+uint32_t xdmad_prepare_channel(struct _xdmad_channel *channel)\r
+{\r
+ Xdmac *xdmac = channel->xdmac;\r
+\r
+ if (channel->state == XDMAD_STATE_FREE)\r
+ return XDMAD_ERROR;\r
+ else if (channel->state == XDMAD_STATE_STARTED)\r
+ return XDMAD_BUSY;\r
+\r
+ /* Clear status */\r
+ xdmac_get_global_channel_status(xdmac);\r
+ xdmac_get_global_isr(xdmac);\r
+\r
+ /* Enable clock of the DMA peripheral */\r
+ pmc_enable_peripheral(xdmac_get_periph_id(xdmac));\r
+\r
+ /* Clear status */\r
+ xdmac_get_channel_isr(xdmac, channel->id);\r
+\r
+ /* Disables XDMAC interrupt for the given channel */\r
+ xdmac_disable_global_it(xdmac, -1);\r
+ xdmac_disable_channel_it(xdmac, channel->id, -1);\r
+\r
+ /* Disable the given dma channel */\r
+ xdmac_disable_channel(xdmac, channel->id);\r
+ xdmac_set_src_addr(xdmac, channel->id, 0);\r
+ xdmac_set_dest_addr(xdmac, channel->id, 0);\r
+ xdmac_set_block_control(xdmac, channel->id, 0);\r
+ xdmac_set_channel_config(xdmac, channel->id, XDMAC_CC_PROT_UNSEC);\r
+ xdmac_set_descriptor_addr(xdmac, channel->id, 0, 0);\r
+ xdmac_set_descriptor_control(xdmac, channel->id, 0);\r
+\r
+ return XDMAD_OK;\r
+}\r
+\r
+bool xdmad_is_transfer_done(struct _xdmad_channel *channel)\r
+{\r
+ return channel->state != XDMAD_STATE_STARTED;\r
+}\r
+\r
+uint32_t xdmad_configure_transfer(struct _xdmad_channel *channel,\r
+ struct _xdmad_cfg *cfg,\r
+ uint32_t desc_cntrl,\r
+ void *desc_addr)\r
+{\r
+ if (channel->state == XDMAD_STATE_FREE)\r
+ return XDMAD_ERROR;\r
+ else if (channel->state == XDMAD_STATE_STARTED)\r
+ return XDMAD_BUSY;\r
+\r
+ Xdmac *xdmac = channel->xdmac;\r
+\r
+ if (cfg->cfg.bitfield.dsync == XDMAC_CC_DSYNC_PER2MEM) {\r
+ cfg->cfg.bitfield.perid = channel->src_rxif;\r
+ } else {\r
+ cfg->cfg.bitfield.perid = channel->dest_txif;\r
+ }\r
+\r
+ /* Clear status */\r
+ xdmac_get_global_isr(xdmac);\r
+ xdmac_get_channel_isr(xdmac, channel->id);\r
+\r
+ if ((desc_cntrl & XDMAC_CNDC_NDE) == XDMAC_CNDC_NDE_DSCR_FETCH_EN) {\r
+ /* Linked List is enabled */\r
+ if ((desc_cntrl & XDMAC_CNDC_NDVIEW_Msk)\r
+ == XDMAC_CNDC_NDVIEW_NDV0) {\r
+ xdmac_set_channel_config(xdmac, channel->id,\r
+ cfg->cfg.uint32_value);\r
+ xdmac_set_src_addr(xdmac, channel->id, cfg->src_addr);\r
+ xdmac_set_dest_addr(xdmac, channel->id, cfg->dest_addr);\r
+ }\r
+ else if ((desc_cntrl & XDMAC_CNDC_NDVIEW_Msk)\r
+ == XDMAC_CNDC_NDVIEW_NDV1) {\r
+ xdmac_set_channel_config(xdmac, channel->id,\r
+ cfg->cfg.uint32_value);\r
+ }\r
+ xdmac_set_descriptor_addr(xdmac, channel->id, desc_addr, 0);\r
+ xdmac_set_descriptor_control(xdmac, channel->id, desc_cntrl);\r
+ xdmac_disable_channel_it(xdmac, channel->id, -1);\r
+ xdmac_enable_channel_it(xdmac, channel->id, XDMAC_CIE_LIE);\r
+ } else {\r
+ /* Linked List is disabled. */\r
+ xdmac_set_src_addr(xdmac, channel->id, cfg->src_addr);\r
+ xdmac_set_dest_addr(xdmac, channel->id, cfg->dest_addr);\r
+ xdmac_set_microblock_control(xdmac, channel->id, cfg->ublock_size);\r
+ xdmac_set_block_control(xdmac, channel->id,\r
+ cfg->block_size > 1 ? cfg->block_size : 0);\r
+ xdmac_set_data_stride_mem_pattern(xdmac, channel->id,\r
+ cfg->data_stride);\r
+ xdmac_set_src_microblock_stride(xdmac, channel->id,\r
+ cfg->src_ublock_stride);\r
+ xdmac_set_dest_microblock_stride(xdmac, channel->id,\r
+ cfg->dest_ublock_stride);\r
+ xdmac_set_channel_config(xdmac, channel->id, cfg->cfg.uint32_value);\r
+ xdmac_set_descriptor_addr(xdmac, channel->id, 0, 0);\r
+ xdmac_set_descriptor_control(xdmac, channel->id, 0);\r
+ xdmac_enable_channel_it(xdmac, channel->id,\r
+ XDMAC_CIE_BIE | XDMAC_CIE_DIE |\r
+ XDMAC_CIE_FIE | XDMAC_CIE_RBIE |\r
+ XDMAC_CIE_WBIE | XDMAC_CIE_ROIE);\r
+ }\r
+ return XDMAD_OK;\r
+}\r
+\r
+uint32_t xdmad_start_transfer(struct _xdmad_channel *channel)\r
+{\r
+ if (channel->state == XDMAD_STATE_FREE)\r
+ return XDMAD_ERROR;\r
+ else if (channel->state == XDMAD_STATE_STARTED)\r
+ return XDMAD_BUSY;\r
+\r
+ /* Change state to 'started' */\r
+ channel->state = XDMAD_STATE_STARTED;\r
+\r
+ /* Start DMA transfer */\r
+ xdmac_enable_channel(channel->xdmac, channel->id);\r
+ if (!_xdmad.polling) {\r
+ xdmac_enable_global_it(channel->xdmac, 1 << channel->id);\r
+ }\r
+\r
+ return XDMAD_OK;\r
+}\r
+\r
+uint32_t xdmad_stop_transfer(struct _xdmad_channel *channel)\r
+{\r
+ Xdmac *xdmac = channel->xdmac;\r
+\r
+ /* Disable channel */\r
+ xdmac_disable_channel(xdmac, channel->id);\r
+\r
+ /* Disable interrupts */\r
+ xdmac_disable_channel_it(xdmac, channel->id, -1);\r
+\r
+ /* Clear pending status */\r
+ xdmac_get_channel_isr(xdmac, channel->id);\r
+ xdmac_get_global_channel_status(xdmac);\r
+\r
+ /* Change state to 'allocated' */\r
+ channel->state = XDMAD_STATE_ALLOCATED;\r
+\r
+ return XDMAD_OK;\r
+}\r
+\r
+/**@}*/\r