From 47c169eadc837c7183b885fb27c7da7c1fcd03c4 Mon Sep 17 00:00:00 2001
From: patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>
Date: Fri, 2 Apr 2010 00:45:28 +0000
Subject: [PATCH] DMA updates

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@2568 42af7a65-404d-4744-a932-0658087f49c3
---
 arch/arm/src/sam3u/sam3u_dmac.c     | 909 ++++++++++++++++++++++++----
 arch/arm/src/sam3u/sam3u_dmac.h     |  12 +-
 arch/arm/src/sam3u/sam3u_hsmci.c    |  13 +-
 arch/arm/src/sam3u/sam3u_internal.h |  33 +-
 4 files changed, 820 insertions(+), 147 deletions(-)

diff --git a/arch/arm/src/sam3u/sam3u_dmac.c b/arch/arm/src/sam3u/sam3u_dmac.c
index b5ed3e8878..2a5253c8fe 100755
--- a/arch/arm/src/sam3u/sam3u_dmac.c
+++ b/arch/arm/src/sam3u/sam3u_dmac.c
@@ -1,7 +1,7 @@
 /****************************************************************************
  * arch/arm/src/sam3u-ek/sam3u_dmac.c
  *
- *   Copyright (C) 2009 Gregory Nutt. All rights reserved.
+ *   Copyright (C) 2010 Gregory Nutt. All rights reserved.
  *   Author: Gregory Nutt <spudmonkey@racsa.co.cr>
  *
  * Redistribution and use in source and binary forms, with or without
@@ -60,6 +60,31 @@
  * Pre-processor Definitions
  ****************************************************************************/
 
+/* Configuration ************************************************************/
+
+/* Condition out the whole file unless DMA is selected in the configuration */
+
+#ifdef CONFIG_SAM3U_DMA
+
+/* Check the number of link list descriptors to allocate */
+
+#ifndef CONFIG_SAM3U_NLLDESC
+#  define CONFIG_SAM3U_NLLDESC CONFIG_SAM3U_NDMACHAN
+#endif
+
+#if CONFIG_SAM3U_NLLDESC < CONFIG_SAM3U_NDMACHAN
+#  warning "At least CONFIG_SAM3U_NDMACHAN descriptors must be allocated */
+
+#  undef CONFIG_SAM3U_NLLDESC
+#  define CONFIG_SAM3U_NLLDESC CONFIG_SAM3U_NDMACHAN
+#endif
+
+/* Register values **********************************************************/
+
+#define DMACHAN_CTRLB_BOTHDSCR \
+  (DMACHAN_CTRLB_SRCDSCR | DMACHAN_CTRLB_DSTDSCR)
+
+
 /****************************************************************************
  * Private Types
  ****************************************************************************/
@@ -68,24 +93,27 @@
 
 struct sam3u_dma_s
 {
-  uint8_t           chan;       /* DMA channel number (0-6) */
-  bool              inuse;      /* TRUE: The DMA channel is in use */
-  uint32_t          flags;      /* DMA channel flags */
-  uint32_t          base;       /* DMA register channel base address */
-  dma_callback_t    callback;   /* Callback invoked when the DMA completes */
-  void             *arg;        /* Argument passed to callback function */
-  uint16_t          bufsize;    /* Transfer buffer size in bytes */
-  volatile uint16_t remaining;  /* Total number of bytes remaining to be transferred */
-  int               result;     /* Transfer result (OK or negated errno) */
+  uint8_t                chan;       /* DMA channel number (0-6) */
+  bool                   inuse;      /* TRUE: The DMA channel is in use */
+  uint32_t               flags;      /* DMA channel flags */
+  uint32_t               base;       /* DMA register channel base address */
+  dma_callback_t         callback;   /* Callback invoked when the DMA completes */
+  void                  *arg;        /* Argument passed to callback function */
+  uint16_t               bufsize;    /* Transfer buffer size in bytes */
+  struct dma_linklist_s *llhead;     /* DMA link list head */
+  struct dma_linklist_s *lltail;     /* DMA link list head */
+  volatile uint16_t      remaining;  /* Total number of bytes remaining to be transferred */
+  int                    result;     /* Transfer result (OK or negated errno) */
 };
 
 /****************************************************************************
  * Private Data
  ****************************************************************************/
 
-/* This semaphore protects the DMA channel table */
+/* These semaphores protect the DMA channel and descriptor tables */
 
-static sem_t g_dmasem;
+static sem_t g_chsem;
+static sem_t g_dsem;
 
 /* CTRLA field lookups */
 
@@ -103,6 +131,17 @@ static const uint32_t g_destwidth[3] =
   DMACHAN_CTRLA_DSTWIDTH_WORD
 };
 
+static const uint32_t g_fifocfg[3] =
+{
+  DMACHAN_CFG_FIFOCFG_LARGEST,
+  DMACHAN_CFG_FIFOCFG_HALF,
+  DMACHAN_CFG_FIFOCFG_SINGLE
+};
+
+/* This array describes the available link list descriptors */
+
+static struct dma_linklist_s g_linklist[CONFIG_SAM3U_NLLDESC];
+
 /* This array describes the state of each DMA */
 
 static struct sam3u_dma_s g_dma[CONFIG_SAM3U_NDMACHAN] =
@@ -145,40 +184,67 @@ static struct sam3u_dma_s g_dma[CONFIG_SAM3U_NDMACHAN] =
  * Private Functions
  ****************************************************************************/
 
-/************************************************************************************
- * Name: sam3u_dmatake() and sam3u_dmagive()
+/****************************************************************************
+ * Name: sam3u_takechsem() and sam3u_givechsem()
  *
  * Description:
- *   Used to get exclusive access to a DMA channel.
+ *   Used to get exclusive access to the DMA channel table
  *
- ************************************************************************************/
+ ****************************************************************************/
 
-static void sam3u_dmatake(void)
+static void sam3u_takechsem(void)
 {
   /* Take the semaphore (perhaps waiting) */
 
-  while (sem_wait(&g_dmasem) != 0)
+  while (sem_wait(&g_chsem) != 0)
     {
-      /* The only case that an error should occur here is if the wait was awakened
-       * by a signal.
+      /* The only case that an error should occur here is if the wait was
+       * awakened by a signal.
        */
 
       ASSERT(errno == EINTR);
     }
 }
 
-static inline void sam3u_dmagive(void)
+static inline void sam3u_givechsem(void)
 {
-  (void)sem_post(&g_dmasem);
+  (void)sem_post(&g_chsem);
 }
 
-/************************************************************************************
+****************************************************************************
+ * Name: sam3u_takedsem() and sam3u_givedsem()
+ *
+ * Description:
+ *   Used to wait for availability of descriptors in the descriptor table.
+ *
+ ****************************************************************************/
+
+static void sam3u_takedsem(void)
+{
+  /* Take the semaphore (perhaps waiting) */
+
+  while (sem_wait(&g_dsem) != 0)
+    {
+      /* The only case that an error should occur here is if the wait was
+       * awakened by a signal.
+       */
+
+      ASSERT(errno == EINTR);
+    }
+}
+
+static inline void sam3u_givedsem(void)
+{
+  (void)sem_post(&g_dsem);
+}
+
+/****************************************************************************
  * Name: sam3u_fifosize
  *
  * Description:
  *  Decode the FIFO size from the flags
  *
- ************************************************************************************/
+ ****************************************************************************/
 
 static unsigned int sam3u_fifosize(uint8_t dmach_flags)
 {
@@ -193,38 +259,52 @@ static unsigned int sam3u_fifosize(uint8_t dmach_flags)
     }
 }
 
-/************************************************************************************
+/****************************************************************************
  * Name: sam3u_flowcontrol
  *
  * Description:
  *  Decode the FIFO flow control from the flags
  *
- ************************************************************************************/
+ ****************************************************************************/
 
 static inline boolean sam3u_flowcontrol(uint8_t dmach_flags)
 {
   return ((dmach_flags & DMACH_FLAG_FLOWCONTROL) != 0);
 }
 
-/************************************************************************************
+/****************************************************************************
+ * Name: sam3u_flowcontrol
+ *
+ * Description:
+ *  Decode the FIFO config from the flags
+ *
+ ****************************************************************************/
+
+static uint32_t sam3u_fifocfg(struct sam3u_dma_s *dmach)
+{
+  unsigned int ndx = (dmach->flags & DMACH_FLAG_FIFOCFG_MASK) >> DMACH_FLAG_FIFOCFG_SHIFT;
+  DEBUGASSERT(ndx < 3);
+  return sam3u_fifocfg[ndx];
+}
+
+/****************************************************************************
  * Name: sam3u_txctrlabits
  *
  * Description:
- *  Decode the the flags to get the correct CTRLA register bit settings for a transmit
- *  (memory to peripheral) transfer.  These are only the "fixed" CTRLA values and 
- *  need to be updated with the actual transfer size before being written to CTRLA
- *  sam3u_txctrla).
+ *  Decode the the flags to get the correct CTRLA register bit settings for
+ *  a transmit (memory to peripheral) transfer.  These are only the "fixed"
+ *  CTRLA values and  need to be updated with the actual transfer size before
+ *  being written to CTRLA sam3u_txctrla).
  *
- ************************************************************************************/
+ ****************************************************************************/
 
 static inline uint32_t
-sam3u_txctrlabits(struct sam3u_dma_s *dmach, uint32_t otherbits)
+sam3u_txctrlabits(struct sam3u_dma_s *dmach)
 {
   uint32_t regval;
   unsigned int ndx;
 
   DEBUGASSERT(dmach);
-  regval = otherbits;
 
   /* Since this is a transmit, the source is described by the memory selections.
    * Set the source width (memory width).
@@ -232,7 +312,7 @@ sam3u_txctrlabits(struct sam3u_dma_s *dmach, uint32_t otherbits)
 
   ndx = (dmach->flags & DMACH_FLAG_MEMWIDTH_MASK) >> DMACH_FLAG_MEMWIDTH_SHIFT;
   DEBUGASSERT(ndx < 3);
-  regval |= g_srcwidth[ndx];
+  regval = g_srcwidth[ndx];
 
   /* Set the source chuck size (memory chunk size) */
 
@@ -271,21 +351,23 @@ sam3u_txctrlabits(struct sam3u_dma_s *dmach, uint32_t otherbits)
   return regval;
 }
 
-/************************************************************************************
+/****************************************************************************
  * Name: sam3u_txctrla
  *
  * Description:
  *  Or in the variable CTRLA bits
  *
- ************************************************************************************/
+ ****************************************************************************/
 
 static inline uint32_t sam3u_txctrla(uint32_t dmasize, uint32_t txctrlabits)
 {
-  /* Set the buffer transfer size field.  This is the number of transfers to be
-   * performed, that is, the number of source width transfers to perform.
+  /* Set the buffer transfer size field.  This is the number of transfers to
+   * be performed, that is, the number of source width transfers to perform.
    */
 
-  /* Adjust the the source transfer size for the source chunk size (memory chunk size) */
+  /* Adjust the the source transfer size for the source chunk size (memory
+   * chunk size)
+   */
 
   if ((dmach->flags & DMACH_FLAG_MEMCHUNKSIZE) == DMACH_FLAG_MEMCHUNKSIZE_4)
     {
@@ -296,33 +378,32 @@ static inline uint32_t sam3u_txctrla(uint32_t dmasize, uint32_t txctrlabits)
   return (txctrlabits & ~DMACHAN_CTRLA_BTSIZE_MASK) | (dmasize << DMACHAN_CTRLA_BTSIZE_SHIFT);
 }
 
-/************************************************************************************
+/****************************************************************************
  * Name: sam3u_setrxctrla
  *
  * Description:
- *  Decode the the flags to get the correct CTRLA register bit settings for a read
- *  (peripheral to memory) transfer. These are only the "fixed" CTRLA values and 
- *  need to be updated with the actual transfer size before being written to CTRLA
- *  sam3u_rxctrla).
+ *  Decode the the flags to get the correct CTRLA register bit settings for
+ *  a read (peripheral to memory) transfer. These are only the "fixed" CTRLA
+ *  values and need to be updated with the actual transfer size before being
+ *  written to CTRLA sam3u_rxctrla).
  *
- ************************************************************************************/
+ ****************************************************************************/
 
 static inline uint32_t
-sam3u_setrxctrla(struct sam3u_dma_s *dmach, uint32_t otherbits)
+sam3u_setrxctrla(struct sam3u_dma_s *dmach)
 {
   uint32_t     regval;
   unsigned int ndx;
 
   DEBUGASSERT(dmach && dmasize <= DMACHAN_CTRLA_BTSIZE_MAX);
-  regval = otherbits;
 
-  /* Since this is a receive, the source is described by the peripheral selections.
-   * Set the source width (peripheral width).
+  /* Since this is a receive, the source is described by the peripheral
+   * selections. Set the source width (peripheral width).
    */
 
   ndx = (dmach->flags & DMACH_FLAG_PERIPHWIDTH_MASK) >> DMACH_FLAG_PERIPHWIDTH_SHIFT;
   DEBUGASSERT(ndx < 3);
-  regval |= g_srcwidth[ndx];
+  regval = g_srcwidth[ndx];
 
   /* Set the source chuck size (peripheral chunk size) */
 
@@ -361,21 +442,23 @@ sam3u_setrxctrla(struct sam3u_dma_s *dmach, uint32_t otherbits)
   return regval;
 }
 
-/************************************************************************************
+/****************************************************************************
  * Name: sam3u_rxctrla
  *
  * Description:
- *  Or in the variable CTRLA bits
+ *  'OR' in the variable CTRLA bits
  *
- ************************************************************************************/
+ ****************************************************************************/
 
 static inline uint32_t sam3u_rxctrla(uint32_t dmasize, uint32_t txctrlabits)
 {
-  /* Set the buffer transfer size field.  This is the number of transfers to be
-   * performed, that is, the number of source width transfers to perform.
+  /* Set the buffer transfer size field.  This is the number of transfers to
+   * be performed, that is, the number of source width transfers to perform.
    */
 
-  /* Adjust the the source transfer size for the source chunk size (peripheral chunk size) */
+  /* Adjust the the source transfer size for the source chunk size (peripheral
+   * chunk size)
+   */
 
   if ((dmach->flags & DMACH_FLAG_PERIPHCHUNKSIZE) == DMACH_FLAG_PERIPHCHUNKSIZE_4)
     {
@@ -386,79 +469,171 @@ static inline uint32_t sam3u_rxctrla(uint32_t dmasize, uint32_t txctrlabits)
   return (txctrlabits & ~DMACHAN_CTRLA_BTSIZE_MASK) | (dmasize << DMACHAN_CTRLA_BTSIZE_SHIFT);
 }
 
-/************************************************************************************
- * Name: sam3u_srcctrlb
+/****************************************************************************
+ * Name: sam3u_txctrlb
  *
  * Description:
- *  Set source related CTRLB fields
+ *  Decode the the flags to get the correct CTRLB register bit settings for
+ *  a transmit (memory to peripheral) transfer.
  *
- ************************************************************************************/
+ ****************************************************************************/
 
-static void sam3u_srcctrlb(struct sam3u_dma_s *dmach, bool lli, bool autoincr)
+static inline void sam3u_txctrlb(struct sam3u_dma_s *dmach)
 {
   uint32_t regval;
+ 
+  /* Assume that we will not be using the link list and disable the source
+   * and destination descriptors.  The default will be single transfer mode.
+   */
 
-  /* Fetch CTRLB and clear the configurable bits */
+  regval = DMACHAN_CTRLB_BOTHDSCR;
 
-  regval = getreg32(dmach->base + SAM3U_DMACHAN_CTRLB_OFFSET);
-  regval &= ~ (DMACHAN_CTRLB_SRCDSCR | DMACHAN_CTRLB_SRCINCR_MASK | 1<<31);
+  /* Select flow control (even if the channel doesn't support it).  The
+   * naming convention from TX is memory to peripheral, but that is really be
+   * determined by bits in the DMA flags.
+   */
+
+  /* Is the memory source really a peripheral? */
+
+  if ((dmach->flags & DMACH_FLAG_MEMISPERIPH) != 0)
+    {
+      /* Yes.. is the peripheral destination also a peripheral? */
 
-  /* Disable the source descriptor if we are not using the LLI transfer mode */
+      if ((dmach->flags & DMACH_FLAG_PERIPHISPERIPH) != 0)
+        {
+          /* Yes.. Use peripheral-to-peripheral flow control */
 
-  if (lli)
+          regval |= DMACHAN_CTRLB_FC_P2P;
+        }
+      else
+        {
+          /* No.. Use peripheral-to-memory flow control */
+
+          regval |= DMACHAN_CTRLB_FC_P2M;
+        }
+    }
+  else
     {
-      regval |= DMACHAN_CTRLB_SRCDSCR;
+      /* No, the source is memory.  Is the peripheral destination a
+       * peripheral
+       */
+
+      if ((dmach->flags & DMACH_FLAG_PERIPHISPERIPH) != 0)
+        {
+          /* Yes.. Use memory-to-peripheral flow control */
+
+          regval |= DMACHAN_CTRLB_FC_M2P;
+        }
+      else
+        {
+          /* No.. Use memory-to-memory flow control */
+
+          regval |= DMACHAN_CTRLB_FC_M2M;
+        }
     }
 
-  /* Select address incrementing */
+  /* Select source address incrementing */
 
-  regval |= autoincr ? DMACHAN_CTRLB_SRCINCR_INCR ? DMACHAN_CTRLB_SRCINCR_FIXED;
+  if ((dmach->flags & DMACH_FLAG_MEMINCREMENT) == 0)
+    {
+      regval |= DMACHAN_CTRLB_SRCINCR_FIXED;
+    }
 
-  /* Save the updated CTRLB value */
+  /* Select destination address incrementing */
 
-  putreg32(regval, dmach->base + SAM3U_DMACHAN_CTRLB_OFFSET)
+  if ((dmach->flags & DMACH_FLAG_PERIPHINCREMENT) == 0)
+    {
+      regval |= DMACHAN_CTRLB_DESTINCR_FIXED;
+    }
+  return regval;
 }
 
-/************************************************************************************
- * Name: sam3u_destctrlb
+/****************************************************************************
+ * Name: sam3u_rxctrlb
  *
  * Description:
- *  Set destination related CTRLB fields
+ *  Decode the the flags to get the correct CTRLB register bit settings for
+ *  a receive (peripheral to memory) transfer.
  *
- ************************************************************************************/
+ ****************************************************************************/
 
-static void sam3u_destctrlb(struct sam3u_dma_s *dmach, bool lli, bool autoincr)
+static inline void sam3u_rxctrlb(struct sam3u_dma_s *dmach)
 {
   uint32_t regval;
+ 
+  /* Assume that we will not be using the link list and disable the source and
+   * destination descriptors.  The default will be single transfer mode.
+   */
 
-  /* Fetch CTRLB and clear the configurable bits */
+  regval = DMACHAN_CTRLB_BOTHDSCR;
 
-  regval = getreg32(dmach->base + SAM3U_DMACHAN_CTRLB_OFFSET);
-  regval &= ~ (DMACHAN_CTRLB_DSTDSCR | DMACHAN_CTRLB_DSTINCR_MASK);
+  /* Select flow control (even if the channel doesn't support it).  The
+   * naming convention from RX is peripheral to memory, but that is really be
+   * determined by bits in the DMA flags.
+   */
 
-  /* Disable the source descriptor if we are not using the LLI transfer mode */
+  /* Is the peripheral source really a peripheral? */
 
-  if (lli)
+  if ((dmach->flags & DMACH_FLAG_PERIPHISPERIPH) != 0)
     {
-      regval |= DMACHAN_CTRLB_DSTDSCR;
+      /* Yes.. is the memory destination also a peripheral? */
+
+      if ((dmach->flags & DMACH_FLAG_MEMISPERIPH) != 0)
+        {
+          /* Yes.. Use peripheral-to-peripheral flow control */
+
+          regval |= DMACHAN_CTRLB_FC_P2P;
+        }
+      else
+        {
+          /* No.. Use peripheral-to-memory flow control */
+
+          regval |= DMACHAN_CTRLB_FC_P2M;
+        }
     }
+  else
+    {
+      /* No, the peripheral source is memory.  Is the memory destination
+       * a peripheral
+       */
 
-  /* Select address incrementing */
+      if ((dmach->flags & DMACH_FLAG_MEMISPERIPH) != 0)
+        {
+          /* Yes.. Use memory-to-peripheral flow control */
 
-  regval |= autoincr ? DMACHAN_CTRLB_DESTINCR_INCR ? DMACHAN_CTRLB_DESTINCR_FIXED;
-        
-  /* Save the updated CTRLB value */
+          regval |= DMACHAN_CTRLB_FC_M2P;
+        }
+      else
+        {
+          /* No.. Use memory-to-memory flow control */
 
-  putreg32(regval, dmach->base + SAM3U_DMACHAN_CTRLB_OFFSET)
+          regval |= DMACHAN_CTRLB_FC_M2M;
+        }
+    }
+
+  /* Select source address incrementing */
+
+  if (dmach->flags & DMACH_FLAG_PERIPHINCREMENT) == 0)
+    {
+      regval |= DMACHAN_CTRLB_SRCINCR_FIXED;
+    }
+
+  /* Select address incrementing */
+
+  if (dmach->flags & DMACH_FLAG_MEMINCREMENT) 0= 0)
+    {
+      regval |= DMACHAN_CTRLB_DESTINCR_FIXED;
+    }
+  return regval;
 }
 
-/************************************************************************************
+/****************************************************************************
  * Name: sam3u_flowcontrol
  *
  * Description:
  *  Select flow control
  *
- ************************************************************************************/
+ ****************************************************************************/
 
 static inline void sam3u_flowcontrol(struct sam3u_dma_s *dmach, uint32_t setting)
 {
@@ -470,13 +645,396 @@ static inline void sam3u_flowcontrol(struct sam3u_dma_s *dmach, uint32_t setting
     putreg(regval, dmach->base + SAM3U_DMACHAN_CTRLB_OFFSET);   
 }
 
-/************************************************************************************
+/****************************************************************************
+ * Name: sam3u_allocdesc
+ *
+ * Description:
+ *  Allocate and add one descriptor to the DMA channel's link list.
+ *
+ *  NOTE: link list entries are freed by the DMA interrupt handler.  However,
+ *  since the setting/clearing of the 'in use' indication is atomic, no
+ *  special actions need be performed.  It would be a good thing to add logic
+ *  to handle the case where all of the entries are exhausted and we could
+ *  wait for some to be freed by the interrupt handler.
+ *
+ ****************************************************************************/
+
+static struct dma_linklist_s *
+sam3u_allocdesc(struct sam3u_dma_s *dmach, struct dma_linklist_s *prev,
+                uint23_t src, uint32_t dest, uint32_t ctrla, uint32_t ctrlb)
+{
+  struct dma_linklist_s *desc = NULL;
+  int i;
+
+  /* Sanity check -- src == 0 is the indication that the link is unused.
+   * Obviously setting it to zero would break that usage.
+   */
+
+#ifdef CONFIG_DEBUG
+  if (src != 0)
+#endif
+    {
+      /* Table a descriptor table semaphore count.  When we get one, then there
+       * is at least one free descriptor in the table and it is ours.
+       */
+
+      sam3u_takedsem();
+
+      /* Examine each link list entry to find an available one -- i.e., one
+       * with src == 0.  That src field is set to zero by the DMA transfer
+       * complete interrupt handler.  The following should be safe because
+       * that is an atomic operation.
+       */
+
+      sam3u_takechsem();
+      for (i = 0; i < CONFIG_SAM3U_NLLDESC; i++)
+        {
+          if (g_linklist[i].src == 0)
+            {
+              /* We have it.  Initialize the new link list entry */
+
+              desc        = &g_linklist[i];
+              desc->src   = src;    /* Source address */
+              desc->dest  = dest;   /* Destination address */
+              desc->ctrla = ctrla;  /* Control A value */
+              desc->ctrlb = ctrlb;  /* Control B value */
+              desc->desc  = 0;      /* Descriptor address */
+
+              /* And then hook it at the tail of the link list */
+
+              if (!prev)
+                {
+                  /* There is no previous link.  This is the new head of
+                   * the list
+                   */
+
+                  DEBUGASSERT(dmach->llhead == NULL && dmach->lltail == NULL);
+                  dmach->llhead = desc;
+                }
+              else
+                {
+                  DEBUGASSERT(dmach->llhead != NULL && dmach->tail == prev);
+
+                  /* When the second link is added to the list, that is the
+                   * cue that we are going to do the link list transfer.
+                   *
+                   * Enable the source and destination descriptor in the link
+                   * list entry just before this one.  We assume that both
+                   * source and destination buffers are non-continuous, but
+                   * this should work even if that is not the case.
+                   */
+
+                  prev->ctrlb &= ~DMACHAN_CTRLB_BOTHDSCR;
+ 
+                  /* Link the previous tail to the new tail */
+
+                  prev->next = (uint32_t)desc;
+                }
+
+              /* In any event, this is the new tail of the list.  The source
+               * and destination descriptors must be disabled for the last entry
+               * in the link list. */
+
+              desc->ctrlb  |= DMACHAN_CTRLB_BOTHDSCR;
+              dmach->lltail = desc;
+
+              /* Increment the total number of bytes to be transferred */
+
+              dmach->bufsize += nbytes;
+              break;
+            }
+        }
+
+      /* Because we hold a count from the counting semaphore, the above
+       * search loop should always be successful.
+       */
+
+      sam3u_givechsem();
+      DEBUGASSERT(desc != NULL);
+    }
+  return desc;
+}
+
+/****************************************************************************
+ * Name: sam3u_freelinklist
+ *
+ * Description:
+ *  Free all descriptors in the DMA channel's link list.
+ *
+ *  NOTE: Called from the DMA interrupt handler.
+ *
+ ****************************************************************************/
+
+static void sam3u_freelinklist(struct sam3u_dma_s *dmach)
+{
+  struct dma_linklist_s *desc;
+  struct dma_linklist_s *next;
+
+  /* Get the head of the link list and detach the link list from the DMA
+   * channel
+   */
+
+  desc             = dmach->llhead
+  dmach->llhead    = NULL;
+  dmach->lltail    = NULL;
+
+  /* No transfer in progress */
+
+  dmach->remaining = 0;
+  dmach->bufsize   = 0;
+
+  /* Reset each descriptor in the link list (thereby freeing them) */
+
+  while (desc != NULL)
+    {
+      next = (struct dma_linklist_s *)desc->next;
+      DEBUGASSERT(desc->src != 0);
+      memset(desc, 0, sizeof(struct dma_linklist_s));
+      sam3u_givedsem();
+      desc = next;
+    }
+}
+
+/****************************************************************************
+ * Name: sam3u_txbuffer
+ *
+ * Description:
+ *   Configure DMA for transmit of one buffer (memory to peripheral).  This
+ *   function may be called multiple times to handle large and/or dis-
+ *   continuous transfers.
+ *
+ ****************************************************************************/
+
+static int sam3u_txbuffer(struct sam3u_dma_s *dmach, uint32_t paddr,
+                          uint32_t maddr, size_t nbytes)
+{
+  uint32_t regval;
+  uint32_t ctrla;
+  uint32_t ctrlb;
+
+  /* If we are appending a buffer to a linklist, then re-use the CTRLA/B
+   * values.  Otherwise, create them from the properties of the transfer.
+   */
+
+  if (dmach->llhead)
+    {
+      regval = dmach->llhead.ctrla;
+      ctrlb  = dmach->llhead.ctrlb;
+    }
+  else
+    {
+      regval = sam3u_txctrlabits(dmach); 
+      ctrla  = sam3u_txctrlb(dmach);
+    }
+  ctrla  = sam3u_txctrla(regval, nbytes);   
+
+  /* Add the new link list entry */
+
+  if (!sam3u_allocdesc(dmach, dmach->lltail, maddr, paddr, ctrla, ctrlb))
+    {
+      return -ENOMEM;
+    }
+  return OK;
+}
+
+/****************************************************************************
+ * Name: sam3u_rxbuffer
+ *
+ * Description:
+ *   Configure DMA for receipt of one buffer (peripheral to memory).  This
+ *   function may be called multiple times to handle large and/or dis-
+ *   continuous transfers.
+ *
+ ****************************************************************************/
+
+static int sam3u_rxbuffer(struct sam3u_dma_s *dmach, uint32_t paddr,
+                          uint32_t maddr, size_t nbytes)
+{
+  uint32_t regval;
+  uint32_t ctrla;
+  uint32_t ctrlb;
+
+  /* If we are appending a buffer to a linklist, then re-use the CTRLA/B
+   * values.  Otherwise, create them from the properties of the transfer.
+   */
+
+  if (dmach->llhead)
+    {
+      regval = dmach->llhead.ctrla;
+      ctrlb  = dmach->llhead.ctrlb;
+    }
+  else
+    {
+      regval = sam3u_rxctrlabits(dmach); 
+      ctrlb  = sam3u_rxctrlb(dmach);
+    }
+   ctrla  = sam3u_rxctrla(regval, nbytes);   
+
+  /* Add the new link list entry */
+
+  if (!sam3u_allocdesc(dmach, dmach->lltail, maddr, paddr, ctrla, ctrlb))
+    {
+      return -ENOMEM;
+    }
+  return OK;
+}
+
+/****************************************************************************
+ * Name: sam3u_single
+ *
+ * Description:
+ *   Start a single buffer DMA.
+ *
+ ****************************************************************************/
+
+static inline int sam3u_single(struct sam3u_dma_s *dmach)
+{
+  
+Transfer Type                AUTO SRC_REP DST_REP SRC_DSCR DST_DSCR BTSIZE SADDR DADDR Other
+ 1) Single Buffer or Last      0      �       �       1        1       USR   USR   USR   USR
+buffer of a multiple buffer
+transfer
+
+  struct dma_linklist_s *llhead = dmach->llhead;
+
+  /* Clear any pending interrupts from any previous DMAC transfer by reading
+   * the interrupt status register.
+   */
+
+  (void)getreg32(SAM3U_DMAC_EBCISR);
+
+  /* Write the starting source address in the SADDR register */
+
+  DEBUGASSERT(llhead != NULL && llhead->src != 0);
+  putreg32(llhead->src, dmach->base + SAM3U_DMACHAN_SADDR_OFFSET);
+
+  /* Write the starting destination address in the DADDR register */
+
+  putreg32(llhead->dest, dmach->base + SAM3U_DMACHAN_DADDR_OFFSET);
+
+  /* Set up the CTRLA register */
+
+  putreg32(llhead->ctrla, dmach->base + SAM3U_DMACHAN_CTRLA_OFFSET);
+
+  /* Set up the CTRLB register */
+
+  putreg32(llhead->ctrlb, dmach->base + SAM3U_DMACHAN_CTRLA_OFFSET);
+
+  /* Both the DST and SRC DSCR bits should be '1' in CTRLB */
+
+  DEBUGASSERT((llhead->ctrlb & DMACHAN_CTRLB_BOTHDSCR) == DMACHAN_CTRLB_BOTHDSCR);
+
+  /* Set up the CFG register */
+ #error missing logic
+  /* Enable the channel by writing a �1� to the CHER enable bit */
+ #error missing logic
+
+  /* Make sure that bit 0 of the EN register is enabled */
+
+5. Source and destination request single and chunk DMAC transactions to transfer the
+   buffer of data (assuming non-memory peripherals). The DMAC acknowledges at the
+   completion of every transaction (chunk and single) in the buffer and carry out the buffer
+   transfer.
+6. Once the transfer completes, hardware sets the interrupts and disables the channel. At
+   this time you can either respond to the buffer Complete or Transfer Complete interrupts,
+   or poll for the Channel Handler Status Register (DMAC_CHSR.ENABLE[n]) bit until it is
+   cleared by hardware, to detect when the transfer is complete.
+}
+
+
+/****************************************************************************
+ * Name: sam3u_multiple
+ *
+ * Description:
+ *   Start a multiple buffer DMA.
+ *
+ ****************************************************************************/
+
+static inline int sam3u_multiple(struct sam3u_dma_s *dmach)
+{
+Transfer Type                AUTO SRC_REP DST_REP SRC_DSCR DST_DSCR BTSIZE SADDR DADDR Other
+ 1) Single Buffer or Last      0      �       �       1        1       USR   USR   USR   USR
+buffer of a multiple buffer
+transfer
+ 4) Multi Buffer transfer      0      �       �       0        0       LLI   LLI   LLI   LLI
+with LLI support
+
+  struct dma_linklist_s *llhead = dmach->llhead;
+
+  DEBUGASSERT(llhead != NULL && llhead->src != 0);
+
+  /* Check the first and last CTRLB values */
+
+  DEBUGASSERT((llhead->ctrlb & DMACHAN_CTRLB_BOTHDSCR) == 0);
+  DEBUGASSERT((lltail->ctrlb & DMACHAN_CTRLB_BOTHDSCR) == DMACHAN_CTRLB_BOTHDSCR);
+
+  /* Write the channel configuration information into the CFG register */
+
+#error missing logic
+
+  /* Clear any pending interrupts from any previous DMAC transfer by reading the
+   * status register
+   */
+
+  (void)getreg32(SAM3U_DMAC_EBCISR);
+
+  /* Set up the initial CTRLB register (to enable descriptors) */
+
+  putreg32(llhead->ctrlb, dmach->base + SAM3U_DMACHAN_CTRLA_OFFSET);
+
+/* Program the DMAC_CTRLBx, DMAC_CFGx registers according to Row 4 as shown in
+   Table 40-1 on page 1060. */
+ #error missing logic
+
+  /* Program the DSCR register with the pointer to the firstlink list entry. */
+
+  putreg32((uint32_t)dmach->llhead, dmac->base + SAM3U_DMACHAN_DSCR_OFFSET);
+
+ #error missing logic
+
+/* Finally, enable the channel by writing a �1� to the CHER enable */
+ #error missing logic
+
+14. The DMAC fetches the first LLI from the location pointed to by DMAC_DSCRx(0).
+   Note: The LLI.DMAC_SADDRx, LLI. DMAC_DADDRx, LLI.DMAC_DSCRx, LLI.DMAC_CTRLAx and
+   LLI.DMAC_CTRLBx registers are fetched. The DMAC automatically reprograms the
+   DMAC_SADDRx, DMAC_DADDRx, DMAC_DSCRx, DMAC_CTRLBx and DMAC_CTRLAx channel
+   registers from the DMAC_DSCRx(0).
+
+15. Source and destination request single and chunk DMAC transactions to transfer the
+   buffer of data (assuming non-memory peripheral). The DMAC acknowledges at the
+   completion of every transaction (chunk and single) in the buffer and carry out the buffer
+   transfer.
+
+16. Once the buffer of data is transferred, the DMAC_CTRLAx register is written out to system
+   memory at the same location and on the same layer where it was originally
+   fetched, that is, the location of the DMAC_CTRLAx register of the linked list item
+   fetched prior to the start of the buffer transfer. Only DMAC_CTRLAx register is written
+   out because only the DMAC_CTRLAx.BTSIZE and DMAC_CTRLAX.DONE bits have
+   been updated by DMAC hardware. Additionally, the DMAC_CTRLAx.DONE bit is
+   asserted when the buffer transfer has completed.
+   Note: Do not poll the DMAC_CTRLAx.DONE bit in the DMAC memory map. Instead, poll the
+   LLI.DMAC_CTRLAx.DONE bit in the LLI for that buffer. If the poll LLI.DMAC_CTRLAx.DONE bit is
+   asserted, then this buffer transfer has completed. This LLI.DMAC_CTRLAx.DONE bit was cleared
+   at the start of the transfer.
+17. The DMAC does not wait for the buffer interrupt to be cleared, but continues fetching
+   the next LLI from the memory location pointed to by current DMAC_DSCRx register
+   and automatically reprograms the DMAC_SADDRx, DMAC_DADDRx, DMAC_DSCRx,
+   DMAC_CTRLAx and DMAC_CTRLBx channel registers. The DMAC transfer continues
+   until the DMAC determines that the DMAC_CTRLBx and DMAC_DSCRx registers at
+   the end of a buffer transfer match described in Row 1 of Table 40-1 on page 1060. The
+   DMAC then knows that the previous buffer transferred was the last buffer in the DMAC
+   transfer. The DMAC transfer might look like that shown in Figure 40-5 on page 1064
+
+}
+
+/****************************************************************************
  * Name: sam3u_dmainterrupt
  *
  * Description:
  *  DMA interrupt handler
  *
- ************************************************************************************/
+ ****************************************************************************/
 
 static int sam3u_dmainterrupt(int irq, void *context)
 {
@@ -502,7 +1060,7 @@ static int sam3u_dmainterrupt(int irq, void *context)
             {
               /* Subtract the number of bytes transferred so far */
              
-              dmach->remaining -= dmach->bufsize;
+              dmach->remaining -= dmach->bufsize ???????;
 
               /* Is the transfer finished? */
 
@@ -517,11 +1075,15 @@ static int sam3u_dmainterrupt(int irq, void *context)
 
                   putreg32(DMAC_CHDR_DIS(dmach->chan), SAM3U_DMAC_CHDR);
  
+                  /* Free the linklist */
+ 
+                  sam3u_freelinklist(dmach);
+
                   /* Perform the DMA complete callback */
 
                   if (dmach->callback)
                     {
-                      dmach->callback(dmach->arg);
+                      dmach->callback(dmach->arg, dmach->result);
                     }
                 }
               else
@@ -575,7 +1137,12 @@ void weak_function up_dmainitialize(void)
 
   /* Enable the DMA controller */
 
-  putreg32(DMAC_EN_ENABLE, SAM3U_DMAC_EN); 
+  putreg32(DMAC_EN_ENABLE, SAM3U_DMAC_EN);
+
+  /* Initialize semaphores */
+
+  sem_init(g_chsem, 0, 1);
+  sem_init(g_chsem, 0, CONFIG_SAM3U_NDMACHAN);
 }
 
 /****************************************************************************
@@ -614,7 +1181,7 @@ DMA_HANDLE sam3u_dmachannel(uint16_t dmach_flags)
    */
 
   dmach = NULL;
-  sam3u_dmatake();
+  sam3u_takechsem();
   for (chndx = 0; chndx < CONFIG_SAM3U_NDMACHAN; chndx++)
     {
       struct sam3u_dma_s *candidate = &g_dma[chndx];
@@ -625,16 +1192,21 @@ DMA_HANDLE sam3u_dmachannel(uint16_t dmach_flags)
           dmach         = candidate;
           dmach->inuse  = true;
 
-          /* Read the status register to clear any pending interrupts on the channel */
+          /* Read the status register to clear any pending interrupts on the
+           * channel
+           */
 
           (void)getreg32(SAM3U_DMAC_EBCISR);
 
-          /* Disable the channel by writing one to the write-only channel disable register */
+          /* Disable the channel by writing one to the write-only channel
+           * disable register
+           */
  
           putreg32(DMAC_CHDR_DIS(chndx), SAM3U_DMAC_CHDR);
 
-          /* See the DMA channel flags, retaining the fifo size and flow control
-           * settings which are inherent properties of the FIFO and cannot be changed.
+          /* See the DMA channel flags, retaining the fifo size and flow
+           * control settings which are inherent properties of the FIFO
+           * and cannot be changed.
            */
 
           dmach->flags &= (DMACH_FLAG_FLOWCONTROL | DMACH_FLAG_FIFOSIZE_MASK);
@@ -646,7 +1218,7 @@ DMA_HANDLE sam3u_dmachannel(uint16_t dmach_flags)
           break;
         }
     }
-  sam3u_dmagive();
+  sam3u_givechsem();
   return (DMA_HANDLE)dmach;
 }
 
@@ -680,30 +1252,113 @@ void sam3u_dmafree(DMA_HANDLE handle)
  * Name: sam3u_dmatxsetup
  *
  * Description:
- *   Configure DMA for transmit (memory to peripheral) before using
+ *   Configure DMA for transmit of one buffer (memory to peripheral).  This
+ *   function may be called multiple times to handle large and/or dis-
+ *   continuous transfers.  Calls to sam3u_dmatxsetup() and sam3u_dmatxsetup()
+ *   must not be intermixed on the same transfer, however.
  *
  ****************************************************************************/
 
-void sam3u_dmarxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr, size_t nbytes)
+int sam3u_dmarxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr, size_t nbytes)
 {
   struct sam3u_dma_s *dmach = (struct sam3u_dma_s *)handle;
   uint32_t regval;
-# warning "Missing logic"
+  int ret = OK;
+
+  DEBUGASSERT(dmach && dmach->llhead != NULL && dmach->lltail != 0);
+  DEBUGASSERT(dmach->remaining == 0);
+
+  /* If this is a large transfer, break it up into smaller buffers */
+
+  while (nbytes > DMACHAN_CTRLA_BTSIZE_MAX)
+    {
+      /* Set up the maximum size transfer */
+
+      ret = sam3u_txbuffer(dmach, paddr, maddr, DMACHAN_CTRLA_BTSIZE_MAX);
+      if (ret == OK);
+        {
+          /* Decrement the number of bytes left to transfer */
+
+          nbytes -= DMACHAN_CTRLA_BTSIZE_MAX;
+
+          /* Increment the memory & peripheral address (if it is appropriate to
+           * do do).
+           */
+ 
+          if (dmach->flags & DMACH_FLAG_PERIPHINCREMENT) != 0)
+            {
+              paddr += DMACHAN_CTRLA_BTSIZE_MAX;
+            }
+
+          if (dmach->flags & DMACH_FLAG_MEMINCREMENT) != 0)
+            {
+              maddr += DMACHAN_CTRLA_BTSIZE_MAX;
+            }
+        }
+    }
+
+  /* Then set up the final buffer transfer */
+
+  if (ret == OK && nbytes > 0)
+    {
+      ret = sam3u_txbuffer(dmach, paddr, maddr, nbytes);
+    }
 }
 
 /****************************************************************************
  * Name: sam3u_dmarxsetup
  *
  * Description:
- *   Configure DMA for receuve (peripheral to memory) before using
+ *   Configure DMA for receipt of one buffer (peripheral to memory).  This
+ *   function may be called multiple times to handle large and/or dis-
+ *   continuous transfers.  Calls to sam3u_dmatxsetup() and sam3u_dmatxsetup()
+ *   must not be intermixed on the same transfer, however.
  *
  ****************************************************************************/
 
-void sam3u_dmarxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr, size_t nbytes)
+int sam3u_dmarxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr, size_t nbytes)
 {
   struct sam3u_dma_s *dmach = (struct sam3u_dma_s *)handle;
   uint32_t regval;
-# warning "Missing logic"
+  int ret = OK;
+
+  DEBUGASSERT(dmach && dmach->llhead != NULL && dmach->lltail != 0);
+
+  /* If this is a large transfer, break it up into smaller buffers */
+
+  while (nbytes > DMACHAN_CTRLA_BTSIZE_MAX)
+    {
+      /* Set up the maximum size transfer */
+
+      ret = sam3u_rxbuffer(dmach, paddr, maddr, DMACHAN_CTRLA_BTSIZE_MAX);
+      if (ret == OK);
+        {
+          /* Decrement the number of bytes left to transfer */
+ 
+          nbytes -= DMACHAN_CTRLA_BTSIZE_MAX;
+
+          /* Increment the memory & peripheral address (if it is appropriate to
+           * do do).
+           */
+ 
+          if (dmach->flags & DMACH_FLAG_PERIPHINCREMENT) != 0)
+            {
+              paddr += DMACHAN_CTRLA_BTSIZE_MAX;
+            }
+
+          if (dmach->flags & DMACH_FLAG_MEMINCREMENT) != 0)
+            {
+              maddr += DMACHAN_CTRLA_BTSIZE_MAX;
+            }
+        }
+    }
+
+  /* Then set up the final buffer transfer */
+
+  if (ret == OK && nbytes > 0)
+    {
+      ret = sam3u_rxbuffer(dmach, paddr, maddr, nbytes);
+    }
 }
 
 /****************************************************************************
@@ -712,23 +1367,37 @@ void sam3u_dmarxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr, size_t
  * Description:
  *   Start the DMA transfer
  *
- * Assumptions:
- *   - DMA handle allocated by sam3u_dmachannel()
- *   - No DMA in progress
- *
  ****************************************************************************/
 
-void sam3u_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg, bool half)
+int sam3u_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg)
 {
   struct sam3u_dma_s *dmach = (struct sam3u_dma_s *)handle;
+  int ret = -EINVAL;
+
+  /* Verify that the DMA has been setup (i.e., at least one entry in the
+   * link list).
+   */
 
-  DEBUGASSERT(handle != NULL);
+  DEBUGASSERT(dmach != NULL);
+  if (dmach->llhead)
+    {
+      /* Save the callback info.  This will be invoked whent the DMA commpletes */
+
+      dmach->callback = callback;
+      dmach->arg      = arg;
 
-  /* Save the callback info.  This will be invoked whent the DMA commpletes */
+      /* Is this a single block transfer?  Or a multiple block tranfer? */
 
-  dmach->callback = callback;
-  dmach->arg      = arg;
-# warning "Missing logic"
+      if (dmach->llhead == dmach->lltail)
+        {
+          ret = sam3u_single(dmach);
+        }
+      else
+        {
+          ret = sam3u_multiple(dmach);
+        }
+    }
+  return ret;
 }
 
 /****************************************************************************
@@ -797,7 +1466,7 @@ void sam3u_dmasample(DMA_HANDLE handle, struct sam3u_dmaregs_s *regs)
   regs->cfg    = getreg32(dmach->base + SAM3U_DMACHAN_CFG_OFFSET);
   irqrestore(flags);
 }
-#endif
+#endif /* CONFIG_DEBUG_DMA */
 
 /****************************************************************************
  * Name: sam3u_dmadump
@@ -834,5 +1503,5 @@ void sam3u_dmadump(DMA_HANDLE handle, const struct sam3u_dmaregs_s *regs,
   dmadbg("     CTRLB[%08x]: %08x\n", dmach->base + SAM3U_DMACHAN_CTRLB_OFFSET, regs->ctrlb);
   dmadbg("       CFG[%08x]: %08x\n", dmach->base + SAM3U_DMACHAN_CFG_OFFSET, regs->cfg);
 }
-#endif
-
+#endif /* CONFIG_DEBUG_DMA */
+#endif /* CONFIG_SAM3U_DMA */
\ No newline at end of file
diff --git a/arch/arm/src/sam3u/sam3u_dmac.h b/arch/arm/src/sam3u/sam3u_dmac.h
index 736e689b0d..8a87584811 100755
--- a/arch/arm/src/sam3u/sam3u_dmac.h
+++ b/arch/arm/src/sam3u/sam3u_dmac.h
@@ -374,12 +374,12 @@
 #  define DMACHAN_CTRLB_FC_P2P        (3 << DMACHAN_CTRLB_FC_SHIFT) /* Peripheral-to-Peripheral */
 #define DMACHAN_CTRLB_SRCINCR_SHIFT   (24)      /* Bits 24-25 */
 #define DMACHAN_CTRLB_SRCINCR_MASK    (3 << DMACHAN_CTRLB_SRCINCR_SHIFT)
-#  define DMACHAN_CTRLB_SRCINCR_INCR  (0 << DMACHAN_CTRLB_SRCINCR_SHIFT) /* INCREMENTING */
-#  define DMACHAN_CTRLB_SRCINCR_FIXED (2 << DMACHAN_CTRLB_SRCINCR_SHIFT) /* FIXED */
+#  define DMACHAN_CTRLB_SRCINCR_INCR  (0 << DMACHAN_CTRLB_SRCINCR_SHIFT) /* Incrementing address */
+#  define DMACHAN_CTRLB_SRCINCR_FIXED (2 << DMACHAN_CTRLB_SRCINCR_SHIFT) /* Fixed address */
 #define DMACHAN_CTRLB_DSTINCR_SHIFT   (28)      /* Bits 28-29 */  
 #define DMACHAN_CTRLB_DSTINCR_MASK    (3 << DMACHAN_CTRLB_DSTINCR_SHIFT)
-#  define DMACHAN_CTRLB_DSTINCR_INCR  (0 << DMACHAN_CTRLB_DSTINCR_SHIFT) /* INCREMENTING */
-#  define DMACHAN_CTRLB_DSTINCR_FIXED (2 << DMACHAN_CTRLB_DSTINCR_SHIFT) /* FIXED */
+#  define DMACHAN_CTRLB_DSTINCR_INCR  (0 << DMACHAN_CTRLB_DSTINCR_SHIFT) /* Incrementing address */
+#  define DMACHAN_CTRLB_DSTINCR_FIXED (2 << DMACHAN_CTRLB_DSTINCR_SHIFT) /* Fixed address */
 #define DMACHAN_CTRLB_IEN             (1 << 30)  /* Bit 30:  Clear sets BTC[n] flag in EBCISR */
 
 /* DMAC Channel n [n = 0..3] Configuration Register */
@@ -422,8 +422,8 @@ struct dma_linklist_s
   uint32_t src;    /* Source address */
   uint32_t dest;   /* Destination address */
   uint32_t ctrla;  /* Control A value */
-  uint32_t ctrlb;  /* Congtrol B value */
-  uint32_t desc;   /* Descriptor address */
+  uint32_t ctrlb;  /* Control B value */
+  uint32_t next;   /* Next descriptor address */
 };
 
 /****************************************************************************************
diff --git a/arch/arm/src/sam3u/sam3u_hsmci.c b/arch/arm/src/sam3u/sam3u_hsmci.c
index 552d554c75..f0b26169d1 100755
--- a/arch/arm/src/sam3u/sam3u_hsmci.c
+++ b/arch/arm/src/sam3u/sam3u_hsmci.c
@@ -122,10 +122,9 @@
 #define DMA_FLAGS \
   (DMACH_FLAG_FIFO_8BYTES | DMACH_FLAG_FIFOCFG_LARGEST | \
   (DMACHAN_PID_MCI0 << DMACH_FLAG_PERIPHPID_SHIFT) | \
-   DMACH_FLAG_PERIPHH2SEL | DMACH_FLAG_PERIPHLLIMODE | \
+   DMACH_FLAG_PERIPHH2SEL | DMACH_FLAG_PERIPHISPERIPH |  \
    DMACH_FLAG_PERIPHWIDTH_32BITS | DMACH_FLAG_PERIPHCHUNKSIZE_1 | \
-   DMACH_FLAG_MEMLLIMODE | DMACH_FLAG_MEMWIDTH_32BITS | \
-   DMACH_FLAG_MEMINCREMENT | DMACH_FLAG_MEMCHUNKSIZE_4)
+   DMACH_FLAG_MEMWIDTH_32BITS | DMACH_FLAG_MEMINCREMENT | DMACH_FLAG_MEMCHUNKSIZE_4)
 
 /* FIFO sizes */
 
@@ -328,7 +327,7 @@ static void sam3u_dumpsamples(struct sam3u_dev_s *priv);
 #  define   sam3u_dumpsamples(priv)
 #endif
 
-static void sam3u_dmacallback(DMA_HANDLE handle, uint8_t isr, void *arg);
+static void sam3u_dmacallback(DMA_HANDLE handle, void *arg, int result);
 
 /* Data Transfer Helpers ****************************************************/
 
@@ -791,7 +790,7 @@ static void  sam3u_dumpsamples(struct sam3u_dev_s *priv)
  *
  ****************************************************************************/
 
-static void sam3u_dmacallback(DMA_HANDLE handle, uint8_t isr, void *arg)
+static void sam3u_dmacallback(DMA_HANDLE handle, void *arg, int result)
 {
   /* FAR struct sam3u_spidev_s *priv = (FAR struct sam3u_spidev_s *)arg; */
 
@@ -2354,7 +2353,7 @@ static int sam3u_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
      /* Start the DMA */
 
       sam3u_sample(priv, SAMPLENDX_BEFORE_ENABLE);
-      sam3u_dmastart(priv->dma, sam3u_dmacallback, priv, false);
+      sam3u_dmastart(priv->dma, sam3u_dmacallback, priv);
       sam3u_sample(priv, SAMPLENDX_AFTER_SETUP);
       ret = OK;
     }
@@ -2421,7 +2420,7 @@ static int sam3u_dmasendsetup(FAR struct sdio_dev_s *dev,
 
       /* Start the DMA */
 
-      sam3u_dmastart(priv->dma, sam3u_dmacallback, priv, false);
+      sam3u_dmastart(priv->dma, sam3u_dmacallback, priv);
       sam3u_sample(priv, SAMPLENDX_AFTER_SETUP);
 
       /* Enable TX interrrupts */
diff --git a/arch/arm/src/sam3u/sam3u_internal.h b/arch/arm/src/sam3u/sam3u_internal.h
index 91cfc7fa15..67c1bafca9 100755
--- a/arch/arm/src/sam3u/sam3u_internal.h
+++ b/arch/arm/src/sam3u/sam3u_internal.h
@@ -322,13 +322,13 @@
 #define DMACH_FLAG_PERIPHPID_SHIFT            (4)       /* Bits 4-7: Peripheral PID */
 #define DMACH_FLAG_PERIPHPID_MASK             (15 << DMACH_FLAG_PERIPHPID_SHIFT)
 #define DMACH_FLAG_PERIPHH2SEL                (1 << 8)  /* Bits 8: HW handshaking */
-#define DMACH_FLAG_PERIPHWIDTH_SHIFT          (9)       /* Bits 9-10: Peripheral width */
+#define DMACH_FLAG_PERIPHISPERIPH             (1 << 9)  /* Bits 9: 0=memory; 1=peripheral */
+#define DMACH_FLAG_PERIPHWIDTH_SHIFT          (10)      /* Bits 10-11: Peripheral width */
 #define DMACH_FLAG_PERIPHWIDTH_MASK           (3 << DMACH_FLAG_PERIPHWIDTH_SHIFT)
 #  define DMACH_FLAG_PERIPHWIDTH_8BITS        (0 << DMACH_FLAG_PERIPHWIDTH_SHIFT) /* 8 bits */
 #  define DMACH_FLAG_PERIPHWIDTH_16BITS       (1 << DMACH_FLAG_PERIPHWIDTH_SHIFT) /* 16 bits */
 #  define DMACH_FLAG_PERIPHWIDTH_32BITS       (2 << DMACH_FLAG_PERIPHWIDTH_SHIFT) /* 32 bits */
-#define DMACH_FLAG_PERIPHINCREMENT            (1 << 11) /* Bit 11: Autoincrement peripheral address */
-#define DMACH_FLAG_PERIPHLLIMODE              (1 << 12) /* Bit 12: Use link list descriptors */
+#define DMACH_FLAG_PERIPHINCREMENT            (1 << 12) /* Bit 12: Autoincrement peripheral address */
 #define DMACH_FLAG_PERIPHCHUNKSIZE            (1 << 13) /* Bit 13: Peripheral chunk size */
 #  define DMACH_FLAG_PERIPHCHUNKSIZE_1        (0)                        /* Peripheral chunksize = 1 */
 #  define DMACH_FLAG_PERIPHCHUNKSIZE_4        DMACH_FLAG_PERIPHCHUNKSIZE /* Peripheral chunksize = 4 */
@@ -338,14 +338,14 @@
 #define DMACH_FLAG_MEMPID_SHIFT               (14)      /* Bits 14-17: Memory PID */
 #define DMACH_FLAG_MEMPID_MASK                (15 << DMACH_FLAG_PERIPHPID_SHIFT)
 #define DMACH_FLAG_MEMH2SEL                   (1 << 18) /* Bits 18: HW handshaking */
-#define DMACH_FLAG_MEMWIDTH_SHIFT             (19)      /* Bits 19-20: Memory width */
+#define DMACH_FLAG_MEMISPERIPH                (1 << 19) /* Bits 19: 0=memory; 1=peripheral */
+#define DMACH_FLAG_MEMWIDTH_SHIFT             (20)      /* Bits 20-21: Memory width */
 #define DMACH_FLAG_MEMWIDTH_MASK              (3 << DMACH_FLAG_MEMWIDTH_SHIFT)
 #  define DMACH_FLAG_MEMWIDTH_8BITS           (0 << DMACH_FLAG_MEMWIDTH_SHIFT) /* 8 bits */
 #  define DMACH_FLAG_MEMWIDTH_16BITS          (1 << DMACH_FLAG_MEMWIDTH_SHIFT) /* 16 bits */
 #  define DMACH_FLAG_MEMWIDTH_32BITS          (2 << DMACH_FLAG_MEMWIDTH_SHIFT) /* 32 bits */
-#define DMACH_FLAG_MEMINCREMENT               (1 << 21) /* Bit 21: Autoincrement memory address */
-#define DMACH_FLAG_MEMLLIMODE                 (1 << 22) /* Bit 22: Use link list descriptors */
-#define DMACH_FLAG_MEMCHUNKSIZE               (1 << 23) /* Bit 23: Memory chunk size */
+#define DMACH_FLAG_MEMINCREMENT               (1 << 22) /* Bit 22: Autoincrement memory address */
+#define DMACH_FLAG_MEMCHUNKSIZE               (1 << 22) /* Bit 23: Memory chunk size */
 #  define DMACH_FLAG_MEMCHUNKSIZE_1           (0)                     /* Memory chunksize = 1 */
 #  define DMACH_FLAG_MEMCHUNKSIZE_4           DMACH_FLAG_MEMCHUNKSIZE /* Memory chunksize = 4 */
 
@@ -354,7 +354,7 @@
  ************************************************************************************/
 
 typedef FAR void *DMA_HANDLE;
-typedef void (*dma_callback_t)(DMA_HANDLE handle, uint8_t isr, void *arg);
+typedef void (*dma_callback_t)(DMA_HANDLE handle, void *arg, int result);
 
 /* The following is used for sampling DMA registers when CONFIG DEBUG_DMA is selected */
 
@@ -559,7 +559,10 @@ EXTERN void sam3u_dmafree(DMA_HANDLE handle);
  * Name: sam3u_dmatxsetup
  *
  * Description:
- *   Configure DMA for transmit (memory to periphal).
+ *   Configure DMA for transmit of one buffer (memory to peripheral).  This
+ *   function may be called multiple times to handle large and/or dis-
+ *   continuous transfers.  Calls to sam3u_dmatxsetup() and sam3u_dmatxsetup()
+ *   must not be intermixed on the same transfer, however.
  *
  ****************************************************************************/
 
@@ -570,12 +573,15 @@ EXTERN void sam3u_dmatxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr,
  * Name: sam3u_dmarxsetup
  *
  * Description:
- *   Configure DMA for receive (peripheral to memory).
+ *   Configure DMA for receipt of one buffer (peripheral to memory).  This
+ *   function may be called multiple times to handle large and/or dis-
+ *   continuous transfers.  Calls to sam3u_dmatxsetup() and sam3u_dmatxsetup()
+ *   must not be intermixed on the same transfer, however.
  *
  ****************************************************************************/
 
-EXTERN void sam3u_dmarxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr,
-                             size_t nbytes);
+EXTERN int sam3u_dmarxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr,
+                            size_t nbytes);
 
 /****************************************************************************
  * Name: sam3u_dmastart
@@ -585,8 +591,7 @@ EXTERN void sam3u_dmarxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr,
  *
  ****************************************************************************/
 
-EXTERN void sam3u_dmastart(DMA_HANDLE handle, dma_callback_t callback,
-                           void *arg, bool half);
+EXTERN int sam3u_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg);
 
 /****************************************************************************
  * Name: sam3u_dmastop
-- 
GitLab