]> git.sur5r.net Git - freertos/blobdiff - FreeRTOS/Demo/CORTEX_A5_SAMA5D2x_Xplained_IAR/AtmelFiles/drivers/peripherals/xdmad.c
Add SAMA5D2 Xplained IAR demo.
[freertos] / FreeRTOS / Demo / CORTEX_A5_SAMA5D2x_Xplained_IAR / AtmelFiles / drivers / peripherals / xdmad.c
diff --git a/FreeRTOS/Demo/CORTEX_A5_SAMA5D2x_Xplained_IAR/AtmelFiles/drivers/peripherals/xdmad.c b/FreeRTOS/Demo/CORTEX_A5_SAMA5D2x_Xplained_IAR/AtmelFiles/drivers/peripherals/xdmad.c
new file mode 100644 (file)
index 0000000..9d651e8
--- /dev/null
@@ -0,0 +1,420 @@
+/* ----------------------------------------------------------------------------\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