From 9ae2468586a918aa900998e7453ed4d3d1bece1c Mon Sep 17 00:00:00 2001
From: patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>
Date: Sun, 28 Mar 2010 16:10:36 +0000
Subject: [PATCH] A little more DMA logic

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@2558 42af7a65-404d-4744-a932-0658087f49c3
---
 arch/arm/src/sam3u/sam3u_dmac.c     | 179 +++++++++++++++++++---------
 arch/arm/src/sam3u/sam3u_dmac.h     |  12 +-
 arch/arm/src/sam3u/sam3u_hsmci.c    |   4 +-
 arch/arm/src/sam3u/sam3u_internal.h |  61 ++++++----
 4 files changed, 176 insertions(+), 80 deletions(-)

diff --git a/arch/arm/src/sam3u/sam3u_dmac.c b/arch/arm/src/sam3u/sam3u_dmac.c
index e52fed8800..7a686ca153 100755
--- a/arch/arm/src/sam3u/sam3u_dmac.c
+++ b/arch/arm/src/sam3u/sam3u_dmac.c
@@ -69,12 +69,13 @@
 struct sam3u_dma_s
 {
   uint8_t           chan;       /* DMA channel number (0-6) */
-  uint8_t           flags;      /* DMA channel flags */
   bool              inuse;      /* TRUE: The DMA channel is in use */
+  uint16_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 */
-  volatile uint16_t xfrsize;    /* Total transfer size */
+  uint16_t          bufsize;    /* Transfer buffer size in bytes */
+  volatile uint16_t remaining;  /* Total number of bytes remaining to be transferred */
 };
 
 /****************************************************************************
@@ -85,6 +86,22 @@ struct sam3u_dma_s
 
 static sem_t g_dmasem;
 
+/* CTRLA field lookups */
+
+static const uint32_t g_srcwidth[3] =
+{
+  DMACHAN_CTRLA_SRCWIDTH_BYTE,
+  DMACHAN_CTRLA_SRCWIDTH_HWORD,
+  DMACHAN_CTRLA_SRCWIDTH_WORD
+};
+
+static const uint32_t g_destwidth[3] =
+{
+  DMACHAN_CTRLA_DSTWIDTH_BYTE,
+  DMACHAN_CTRLA_DSTWIDTH_HWORD,
+  DMACHAN_CTRLA_DSTWIDTH_WORD
+};
+
 /* This array describes the state of each DMA */
 
 static struct sam3u_dma_s g_dma[CONFIG_SAM3U_NDMACHAN] =
@@ -201,42 +218,23 @@ static inline void
 sam3u_settxctrla(struct sam3u_dma_s *dmach, uint32_t dmasize, uint32_t otherbits)
 {
   uint32_t regval;
-  uint32_t flags;
+  unsigned int ndx;
 
   DEBUGASSERT(dmach && dmasize <= DMACHAN_CTRLA_BTSIZE_MAX);
   regval = (dmasize << DMACHAN_CTRLA_BTSIZE_SHIFT) | otherbits;
 
   /* Since this is a transmit, the source is described by the memeory selections */
 
-  flags  = dmach->flags & DMACH_FLAG_MEMWIDTH_MASK;
-  if (flags == DMACH_FLAG_MEMWIDTH_8BITS)
-    {
-      regval |= DMACHAN_CTRLA_SRCWIDTH_BYTE;
-    }
-  else if (flags == DMACH_FLAG_MEMWIDTH_16BITS)
-    {
-      regval |= DMACHAN_CTRLA_SRCWIDTH_HWORD;
-    }
-  else /* if (flags == DMACH_FLAG_MEMWIDTH_32BITS) */
-    {
-      regval |= DMACHAN_CTRLA_SRCWIDTH_WORD;
-    }
+  ndx = (dmach->flags & DMACH_FLAG_MEMWIDTH_MASK) >> DMACH_FLAG_MEMWIDTH_SHIFT;
+  DEBUGASSERT(ndx < 3);
+  regval |= g_srcwidth[ndx];
+  return regval;
 
   /* Since this is a transmit, the destination is described by the peripheral selections */
 
-  flags = dmach->flags & DMACH_FLAG_PERIPHWIDTH_MASK;
-  if (flags == DMACH_FLAG_PERIPHWIDTH_8BITS)
-    {
-      regval |= DMACHAN_CTRLA_DSTWIDTH_BYTE;
-    }
-  else if (flags == DMACH_FLAG_PERIPHWIDTH_16BITS)
-    {
-      regval |= DMACHAN_CTRLA_DSTWIDTH_HWORD;
-    }
-  else /* if (flags == DMACH_FLAG_PERIPHWIDTH_32BITS) */
-    {
-      regval |= DMACHAN_CTRLA_DSTWIDTH_WORD;
-    }
+  ndx = (dmach->flags & DMACH_FLAG_PERIPHWIDTH_MASK) >> DMACH_FLAG_PERIPHWIDTH_SHIFT;
+  DEBUGASSERT(ndx < 3);
+  regval |= g_destwidth[ndx];
   return regval;
 }
 
@@ -252,44 +250,108 @@ sam3u_settxctrla(struct sam3u_dma_s *dmach, uint32_t dmasize, uint32_t otherbits
 static inline void
 sam3u_setrxctrla(struct sam3u_dma_s *dmach, uint32_t dmasize, uint32_t otherbits)
 {
-  uint32_t regval;
-  uint32_t flags;
+  uint32_t     regval;
+  unsigned int ndx;
 
   DEBUGASSERT(dmach && dmasize <= DMACHAN_CTRLA_BTSIZE_MAX);
   regval = (dmasize << DMACHAN_CTRLA_BTSIZE_SHIFT) | otherbits;
 
   /* Since this is a receive, the source is described by the peripheral selections */
 
-  flags  = dmach->flags & DMACH_FLAG_PERIPHWIDTH_MASK;
-  if (flags == DMACH_FLAG_PERIPHWIDTH_8BITS)
-    {
-      regval |= DMACHAN_CTRLA_SRCWIDTH_BYTE;
-    }
-  else if (flags == DMACH_FLAG_PERIPHWIDTH_16BITS)
-    {
-      regval |= DMACHAN_CTRLA_SRCWIDTH_HWORD;
-    }
-  else /* if (flags == DMACH_FLAG_PERIPHWIDTH_32BITS) */
-    {
-      regval |= DMACHAN_CTRLA_SRCWIDTH_WORD;
-    }
+  ndx = (dmach->flags & DMACH_FLAG_PERIPHWIDTH_MASK) >> DMACH_FLAG_PERIPHWIDTH_SHIFT;
+  DEBUGASSERT(ndx < 3);
+  regval |= g_srcwidth[ndx];
 
   /* Since this is a receive, the destination is described by the memory selections */
 
-  flags = dmach->flags & DMACH_FLAG_MEMWIDTH_MASK;
-  if (flags == DMACH_FLAG_MEMWIDTH_8BITS)
-    {
-      regval |= DMACHAN_CTRLA_DSTWIDTH_BYTE;
-    }
-  else if (flags == DMACH_FLAG_MEMWIDTH_16BITS)
+  ndx = (dmach->flags & DMACH_FLAG_MEMWIDTH_MASK) >> DMACH_FLAG_MEMWIDTH_SHIFT;
+  DEBUGASSERT(ndx < 3);
+  regval |= g_destwidth[ndx];
+  return regval;
+}
+
+/************************************************************************************
+ * Name: sam3u_srcctrlb
+ *
+ * Description:
+ *  Set source related CTRLB fields
+ *
+ ************************************************************************************/
+
+static void sam3u_srcctrlb(struct sam3u_dma_s *dmach, bool lli, bool autoincr)
+{
+  uint32_t regval;
+
+  /* Fetch CTRLB and clear the configurable bits */
+
+  regval = getreg32(dmach->base + SAM3U_DMACHAN_CTRLB_OFFSET);
+  regval &= ~ (DMACHAN_CTRLB_SRCDSCR | DMACHAN_CTRLB_SRCINCR_MASK | 1<<31);
+
+  /* Disable the source descriptor if we are not using the LLI transfer mode */
+
+  if (lli)
     {
-      regval |= DMACHAN_CTRLA_DSTWIDTH_HWORD;
+      regval |= DMACHAN_CTRLB_SRCDSCR;
     }
-  else /* if (flags == DMACH_FLAG_MEMWIDTH_32BITS) */
+
+  /* Select address incrementing */
+
+  regval |= autoincr ? DMACHAN_CTRLB_SRCINCR_INCR ? DMACHAN_CTRLB_SRCINCR_FIXED;
+
+  /* Save the updated CTRLB value */
+
+  putreg32(regval, dmach->base + SAM3U_DMACHAN_CTRLB_OFFSET)
+}
+
+/************************************************************************************
+ * Name: sam3u_destctrlb
+ *
+ * Description:
+ *  Set destination related CTRLB fields
+ *
+ ************************************************************************************/
+
+static void sam3u_destctrlb(struct sam3u_dma_s *dmach, bool lli, bool autoincr)
+{
+  uint32_t regval;
+
+  /* Fetch CTRLB and clear the configurable bits */
+
+  regval = getreg32(dmach->base + SAM3U_DMACHAN_CTRLB_OFFSET);
+  regval &= ~ (DMACHAN_CTRLB_DSTDSCR | DMACHAN_CTRLB_DSTINCR_MASK);
+
+  /* Disable the source descriptor if we are not using the LLI transfer mode */
+
+  if (lli)
     {
-      regval |= DMACHAN_CTRLA_DSTWIDTH_WORD;
+      regval |= DMACHAN_CTRLB_DSTDSCR;
     }
-  return regval;
+
+  /* Select address incrementing */
+
+  regval |= autoincr ? DMACHAN_CTRLB_DESTINCR_INCR ? DMACHAN_CTRLB_DESTINCR_FIXED;
+        
+  /* Save the updated CTRLB value */
+
+  putreg32(regval, dmach->base + SAM3U_DMACHAN_CTRLB_OFFSET)
+}
+
+/************************************************************************************
+ * Name: sam3u_flowcontrol
+ *
+ * Description:
+ *  Select flow control
+ *
+ ************************************************************************************/
+
+static inline void sam3u_flowcontrol(struct sam3u_dma_s *dmach, uint32_t setting)
+{
+    uint32_t regval;
+
+    regval = getreg32(dmach->base + SAM3U_DMACHAN_CTRLB_OFFSET);
+    regval &= ~(DMACHAN_CTRLB_FC_MASK);
+    regval |= setting;
+    putreg(regval, dmach->base + SAM3U_DMACHAN_CTRLB_OFFSET);   
 }
 
 /************************************************************************************
@@ -355,6 +417,11 @@ void weak_function up_dmainitialize(void)
  *   the required FIFO size and flow control capabilities (determined by
  *   dma_flags) then  gives the caller exclusive access to the DMA channel.
  *
+ *   The naming convention in all of the DMA interfaces is that one side is
+ *   the 'peripheral' and the other is 'memory'.  Howerver, the interface
+ *   could still be used if, for example, both sides were memory although
+ *   the naming would be awkward.
+ *
  * Returned Value:
  *   If a DMA channel if the required FIFO size is available, this function
  *   returns a non-NULL, void* DMA channel handle.  NULL is returned on any
@@ -362,7 +429,7 @@ void weak_function up_dmainitialize(void)
  *
  ****************************************************************************/
 
-DMA_HANDLE sam3u_dmachannel(uint8_t dmach_flags)
+DMA_HANDLE sam3u_dmachannel(uint16_t dmach_flags)
 {
   struct sam3u_dma_s *dmach;
   unsigned int chndx;
@@ -406,7 +473,7 @@ DMA_HANDLE sam3u_dmachannel(uint8_t dmach_flags)
 
           /* Initialize the transfer state */
 
-          dmach->xfrsize = 0;
+          dmach->remaining = 0;
           break;
         }
     }
diff --git a/arch/arm/src/sam3u/sam3u_dmac.h b/arch/arm/src/sam3u/sam3u_dmac.h
index 817b3934c4..c92c45edf3 100755
--- a/arch/arm/src/sam3u/sam3u_dmac.h
+++ b/arch/arm/src/sam3u/sam3u_dmac.h
@@ -360,8 +360,8 @@
 
 /* DMAC Channel n [n = 0..3] Control B Register */
 
-#define DMACHAN_CTRLB_SRCDSCR         (1 << 16) /* Bit 16: Source uffer Descriptor Fetch operation disabled */
-#define DMACHAN_CTRLB_DSTDSCR         (1 << 20) /* Bit 20: Dest Buffer Descriptor Fetch operation disabled */
+#define DMACHAN_CTRLB_SRCDSCR         (1 << 16) /* Bit 16: Source buffer descriptor fetch operation disabled */
+#define DMACHAN_CTRLB_DSTDSCR         (1 << 20) /* Bit 20: Dest buffer descriptor fetch operation disabled */
 #define DMACHAN_CTRLB_FC_SHIFT        (21)      /* Bits 21-22:  Flow controller  */
 #define DMACHAN_CTRLB_FC_MASK         (3 << DMACHAN_CTRLB_FC_SHIFT)
 #  define DMACHAN_CTRLB_FC_M2M        (0 << DMACHAN_CTRLB_FC_SHIFT) /* Memory-to-Memory  */
@@ -380,7 +380,7 @@
 
 /* DMAC Channel n [n = 0..3] Configuration Register */
 
-#define DMACHAN_CFG_SRCPER_SHIFT      (0)       /* Bits 0-3:  Chanel source associated with peripheral ID */
+#define DMACHAN_CFG_SRCPER_SHIFT      (0)       /* Bits 0-3:  Channel source associated with peripheral ID */
 #define DMACHAN_CFG_SRCPER_MASK       (15 << DMACHAN_CFG_SRCPER_SHIFT)
 #define DMACHAN_CFG_DSTPER_SHIFT      (4)       /* Bits 4-7:  Channel dest associated with peripheral ID */
 #define DMACHAN_CFG_DSTPER_MASK       (15 << DMACHAN_CFG_DSTPER_SHIFT)
@@ -401,6 +401,12 @@
 #  define DMACHAN_CFG_FIFOCFG_HALF    (1 << DMACHAN_CFG_FIFOCFG_SHIFT) /* Half FIFO size */
 #  define DMACHAN_CFG_FIFOCFG_SINGLE  (2 << DMACHAN_CFG_FIFOCFG_SHIFT) /* Single AHB access */
 
+/* DMA Peripheral IDs *******************************************************************/
+
+#define DMACHAN_PID_MCI0               0
+#define DMACHAN_PID_SSC                3
+#define DMACHAN_PID_MCI1               13
+
 /****************************************************************************************
  * Public Types
  ****************************************************************************************/
diff --git a/arch/arm/src/sam3u/sam3u_hsmci.c b/arch/arm/src/sam3u/sam3u_hsmci.c
index 144a6026a4..b47290debb 100755
--- a/arch/arm/src/sam3u/sam3u_hsmci.c
+++ b/arch/arm/src/sam3u/sam3u_hsmci.c
@@ -120,7 +120,9 @@
 /* DMA configuration flags */
 
 #define DMA_FLAGS \
-  (DMACH_FLAG_FIFO_8BYTES|DMACH_FLAG_SRCWIDTH_32BITS|DMACH_FLAG_DESTWIDTH_32BITS|DMACH_FLAG_MEMINCREMENT)
+  ((DMACHAN_PID_MCI0 << DMACH_FLAG_PERIPHPID_SHIFT) | \
+   DMACH_FLAG_PERIPHH2SEL | DMACH_FLAG_PERIPHLLIMODE | DMACH_FLAG_PERIPHWIDTH_32BITS | \
+   DMACH_FLAG_MEMLLIMODE | DMACH_FLAG_MEMWIDTH_32BITS | DMACH_FLAG_MEMINCREMENT)
 
 /* FIFO sizes */
 
diff --git a/arch/arm/src/sam3u/sam3u_internal.h b/arch/arm/src/sam3u/sam3u_internal.h
index 41e0585064..d3fdd26a5d 100755
--- a/arch/arm/src/sam3u/sam3u_internal.h
+++ b/arch/arm/src/sam3u/sam3u_internal.h
@@ -297,23 +297,39 @@
  * be used if, for example, both sides were memory although the naming would be awkward)
  */
 
-#define DMACH_FLAG_FLOWCONTROL          (1 << 0)  /* Bit 0: Channel supports flow control */
-#define DMACH_FLAG_FIFOSIZE_SHIFT       (1)       /* Bit 1: Size of DMA FIFO */
-#define DMACH_FLAG_FIFOSIZE_MASK        (1 << DMACH_FLAG_FIFOSIZE_SHIFT)
-#  define DMACH_FLAG_FIFO_8BYTES        (0 << DMACH_FLAG_FIFOSIZE_SHIFT) /* 8 bytes */
-#  define DMACH_FLAG_FIFO_32BYTES       (1 << DMACH_FLAG_FIFOSIZE_SHIFT) /* 32 bytes */
-#define DMACH_FLAG_PERIPHWIDTH_SHIFT    (2)       /* Bits 2-3: 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) /* 16 bits */
-#define DMACH_FLAG_PERIPHINCREMENT      (1 << 4)  /* Bit 4: Autoincrement peripheral address */
-#define DMACH_FLAG_MEMWIDTH_SHIFT       (5)       /* Bits 5-6: 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) /* 16 bits */
-#define DMACH_FLAG_MEMINCREMENT         (1 << 7)  /* Bit 7: Autoincrement memory address */
+/* Unchange-able properties of the channel */
+
+#define DMACH_FLAG_FLOWCONTROL                (1 << 0)  /* Bit 0: Channel supports flow control */
+#define DMACH_FLAG_FIFOSIZE_SHIFT             (1)       /* Bit 1: Size of DMA FIFO */
+#define DMACH_FLAG_FIFOSIZE_MASK              (1 << DMACH_FLAG_FIFOSIZE_SHIFT)
+#  define DMACH_FLAG_FIFO_8BYTES              (0 << DMACH_FLAG_FIFOSIZE_SHIFT) /* 8 bytes */
+#  define DMACH_FLAG_FIFO_32BYTES             (1 << DMACH_FLAG_FIFOSIZE_SHIFT) /* 32 bytes */
+
+/* Peripheral endpoint characteristics */
+
+#define DMACH_FLAG_PERIPHPID_SHIFT            (2)       /* Bits 2-5: Peripheral PID */
+#define DMACH_FLAG_PERIPHPID_MASK             (15 << DMACH_FLAG_PERIPHPID_SHIFT)
+#define DMACH_FLAG_PERIPHH2SEL                (1 << 6)  /* Bits 6: HW handshaking */
+#define DMACH_FLAG_PERIPHWIDTH_SHIFT          (7)       /* Bits 7-8: 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) /* 16 bits */
+#define DMACH_FLAG_PERIPHINCREMENT            (1 << 9)  /* Bit 9: Autoincrement peripheral address */
+#define DMACH_FLAG_PERIPHLLIMODE              (1 << 10) /* Bit 10: Use link list descriptors */
+
+/* Memory endpoint characteristics */
+
+#define DMACH_FLAG_MEMPID_SHIFT               (11)      /* Bits 11-14: Memory PID */
+#define DMACH_FLAG_MEMPID_MASK                (15 << DMACH_FLAG_PERIPHPID_SHIFT)
+#define DMACH_FLAG_MEMH2SEL                   (1 << 15) /* Bits 15: HW handshaking */
+#define DMACH_FLAG_MEMWIDTH_SHIFT             (16)      /* Bits 16-17: 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) /* 16 bits */
+#define DMACH_FLAG_MEMINCREMENT               (1 << 18) /* Bit 18: Autoincrement memory address */
+#define DMACH_FLAG_MEMLLIMODE                 (1 << 19) /* Bit 19: Use link list descriptors */
 
 /************************************************************************************
  * Public Types
@@ -492,6 +508,11 @@ EXTERN void sam3u_gpioirqdisable(int irq);
  *   the required FIFO size and flow control capabilities (determined by
  *   dma_flags) then  gives the caller exclusive access to the DMA channel.
  *
+ *   The naming convention in all of the DMA interfaces is that one side is
+ *   the 'peripheral' and the other is 'memory'.  Howerver, the interface
+ *   could still be used if, for example, both sides were memory although
+ *   the naming would be awkward.
+ *
  * Returned Value:
  *   If a DMA channel if the required FIFO size is available, this function
  *   returns a non-NULL, void* DMA channel handle.  NULL is returned on any
@@ -499,7 +520,7 @@ EXTERN void sam3u_gpioirqdisable(int irq);
  *
  ****************************************************************************/
 
-EXTERN DMA_HANDLE sam3u_dmachannel(uint8_t dmach_flags);
+EXTERN DMA_HANDLE sam3u_dmachannel(uint32_t dmach_flags);
 
 /****************************************************************************
  * Name: sam3u_dmafree
@@ -520,7 +541,7 @@ EXTERN void sam3u_dmafree(DMA_HANDLE handle);
  * Name: sam3u_dmatxsetup
  *
  * Description:
- *   Configure DMA for transmit (memory to periphal) before using
+ *   Configure DMA for transmit (memory to periphal).
  *
  ****************************************************************************/
 
@@ -531,7 +552,7 @@ EXTERN void sam3u_dmatxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr,
  * Name: sam3u_dmarxsetup
  *
  * Description:
- *   Configure DMA for receive (peripheral to memory) before using
+ *   Configure DMA for receive (peripheral to memory).
  *
  ****************************************************************************/
 
-- 
GitLab