You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by pk...@apache.org on 2022/07/16 12:39:27 UTC

[incubator-nuttx] 02/03: samv7: add DMA API for circular buffers

This is an automated email from the ASF dual-hosted git repository.

pkarashchenko pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git

commit e3e282bd8ab8f61f393a321659e31a9cbf3a76a6
Author: Michal Lenc <mi...@seznam.cz>
AuthorDate: Thu Jul 14 22:01:04 2022 +0200

    samv7: add DMA API for circular buffers
    
    This commit adds functions sam_dmarxsetup_circular() and
    sam_dmarxstart_circular() that create API for operating with two or more
    circular buffers. This can be used for DMA operation with serial driver
    or ADC where ping pong buffers are required to successfully transfer the
    data at high speed.
    
    Signed-off-by: Michal Lenc <mi...@seznam.cz>
---
 arch/arm/src/samv7/sam_xdmac.c | 160 ++++++++++++++++++++++++++++++++++++++++-
 arch/arm/src/samv7/sam_xdmac.h |  53 ++++++++++++++
 2 files changed, 212 insertions(+), 1 deletion(-)

diff --git a/arch/arm/src/samv7/sam_xdmac.c b/arch/arm/src/samv7/sam_xdmac.c
index 3749500d49..d1f9dd6d19 100644
--- a/arch/arm/src/samv7/sam_xdmac.c
+++ b/arch/arm/src/samv7/sam_xdmac.c
@@ -95,6 +95,7 @@ struct sam_xdmach_s
   uint8_t chan;                   /* DMA channel number (0-15) */
   bool inuse;                     /* TRUE: The DMA channel is in use */
   bool rx;                        /* TRUE: Peripheral to memory transfer */
+  bool circular;                  /* TRUE: Circular buffer is used */
   uint32_t flags;                 /* DMA channel flags */
   uint32_t base;                  /* DMA register channel base address */
   uint32_t cc;                    /* Pre-calculated CC register for transfer */
@@ -1527,7 +1528,17 @@ static int sam_xdmac_interrupt(int irq, void *context, void *arg)
             {
               /* Yes.. Terminate the transfer with success */
 
-              sam_dmaterminate(xdmach, OK);
+              if (!xdmach->circular)
+                {
+                  sam_dmaterminate(xdmach, OK);
+                }
+              else
+                {
+                  if (xdmach->callback)
+                    {
+                      xdmach->callback((DMA_HANDLE)xdmach, xdmach->arg, OK);
+                    }
+                }
             }
 
           /* Else what? */
@@ -1784,6 +1795,8 @@ int sam_dmatxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr,
   DEBUGASSERT(xdmach);
   dmainfo("llhead: %p lltail: %p\n", xdmach->llhead, xdmach->lltail);
 
+  xdmach->circular = false;
+
   /* The maximum transfer size in bytes depends upon the maximum number of
    * transfers and the number of bytes per transfer.
    */
@@ -1863,6 +1876,8 @@ int sam_dmarxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr,
   DEBUGASSERT(xdmach);
   dmainfo("llhead: %p lltail: %p\n", xdmach->llhead, xdmach->lltail);
 
+  xdmach->circular = false;
+
   /* The maximum transfer size in bytes depends upon the maximum number of
    * transfers and the number of bytes per transfer.
    */
@@ -1921,6 +1936,130 @@ int sam_dmarxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr,
   return ret;
 }
 
+/****************************************************************************
+ * Name: sam_dmarxsetup_circular
+ *
+ * Description:
+ *   Configure DMA for receipt of two circular buffers for peripheral to
+ *   memory transfer. Function sam_dmastart_circular() needs to be called
+ *   to start the transfer. Only peripheral to memory transfer is currently
+ *   supported.
+ *
+ * Input Parameters:
+ *   handle - DMA handler
+ *   descr - array with DMA descriptors
+ *   maddr - array of memory addresses (i.e. destination addresses)
+ *   paddr - peripheral address (i.e. source address)
+ *   nbytes - number of bytes to transfer
+ *   ndescrs - number of descriptors (i.e. the lenght of descr array)
+ *
+ ****************************************************************************/
+
+int sam_dmarxsetup_circular(DMA_HANDLE handle,
+                            struct chnext_view1_s *descr[],
+                            uint32_t maddr[],
+                            uint32_t paddr,
+                            size_t nbytes,
+                            uint8_t ndescrs)
+{
+  struct sam_xdmach_s *xdmach = (struct sam_xdmach_s *)handle;
+  uint32_t cubc;
+  uint8_t nextdescr;
+  int i;
+
+  /* Set circular as true */
+
+  xdmach->circular = true;
+
+  xdmach->cc = sam_rxcc(xdmach);
+
+  /* Calculate the number of transfers for CUBC */
+
+  cubc  = sam_cubc(xdmach, nbytes);
+  cubc |= (CHNEXT_UBC_NDE | CHNEXT_UBC_NVIEW_1 | CHNEXT_UBC_NDEN |
+           CHNEXT_UBC_NSEN);
+
+  nextdescr = 0;
+
+  for (i = ndescrs - 1; i >= 0; i--)
+    {
+      descr[i]->cnda  = (uint32_t)descr[nextdescr];   /* Next Descriptor Address */
+      descr[i]->cubc  = cubc;                         /* Channel Microblock Control Register */
+      descr[i]->csa   = paddr;                        /* Source address */
+      descr[i]->cda   = maddr[i];                     /* Destination address */
+
+      /* Clean data cache */
+
+      up_clean_dcache((uintptr_t)descr[i],
+                      (uintptr_t)descr[i] +
+                       sizeof(struct chnext_view1_s));
+      up_clean_dcache((uintptr_t)maddr[i], (uintptr_t)maddr[i] + nbytes);
+      nextdescr = i;
+    }
+
+  /* Settup of llhead and lltail does not really matter in this case */
+
+  xdmach->llhead = descr[0];
+  xdmach->lltail = descr[0];
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: sam_dmastart_circular
+ *
+ * Description:
+ *   Start the DMA transfer with circular buffers.
+ *
+ ****************************************************************************/
+
+int sam_dmastart_circular(DMA_HANDLE handle, dma_callback_t callback,
+                          void *arg)
+{
+  struct sam_xdmach_s *xdmach = (struct sam_xdmach_s *)handle;
+  struct sam_xdmac_s *xdmac = sam_controller(xdmach);
+  struct chnext_view1_s *llhead = xdmach->llhead;
+  uintptr_t paddr;
+  uint32_t regval;
+
+  /* Save the callback info.  This will be invoked when the DMA
+   * completes
+   */
+
+  xdmach->callback = callback;
+  xdmach->arg      = arg;
+
+  /* Clear pending interrupts */
+
+  sam_getdmach(xdmach, SAM_XDMACH_CIS_OFFSET);
+
+  sam_putdmach(xdmach, xdmach->cc, SAM_XDMACH_CC_OFFSET);
+
+  /* Setup next descriptor */
+
+  regval = (XDMACH_CNDC_NDE | XDMACH_CNDC_NDVIEW_NDV1 |
+            XDMACH_CNDC_NDDUP | XDMACH_CNDC_NDSUP);
+  sam_putdmach(xdmach, regval, SAM_XDMACH_CNDC_OFFSET);
+
+  /* Use llhead (first descriptor) as initial buffer */
+
+  paddr = sam_physramaddr((uintptr_t)llhead);
+  sam_putdmach(xdmach, (uint32_t)paddr, SAM_XDMACH_CNDA_OFFSET);
+
+  /* Enable end of block interrupt */
+
+  sam_putdmach(xdmach, XDMAC_CHINT_BI | XDMAC_CHINT_ERRORS,
+               SAM_XDMACH_CIE_OFFSET);
+
+  /* Enable channel */
+
+  sam_putdmac(xdmac, XDMAC_CHAN(xdmach->chan), SAM_XDMAC_GIE_OFFSET);
+
+  sam_putdmac(xdmac, XDMAC_CHAN(xdmach->chan), SAM_XDMAC_GE_OFFSET);
+
+  return OK;
+}
+
 /****************************************************************************
  * Name: sam_dmastart
  *
@@ -1998,6 +2137,25 @@ void sam_dmastop(DMA_HANDLE handle)
   leave_critical_section(flags);
 }
 
+/****************************************************************************
+ * Name: sam_destaddr
+ *
+ * Description:
+ *   Returns the pointer to the destionation address, i.e the last address
+ *   data were written by DMA.
+ *
+ * Assumptions:
+ *   - DMA handle allocated by sam_dmachannel()
+ *
+ ****************************************************************************/
+
+size_t sam_destaddr(DMA_HANDLE handle)
+{
+  struct sam_xdmach_s *xdmach = (struct sam_xdmach_s *)handle;
+
+  return sam_getdmach(xdmach, SAM_XDMACH_CDA_OFFSET);
+}
+
 /****************************************************************************
  * Name: sam_dmasample
  *
diff --git a/arch/arm/src/samv7/sam_xdmac.h b/arch/arm/src/samv7/sam_xdmac.h
index eff35dd5e0..f77a78eb63 100644
--- a/arch/arm/src/samv7/sam_xdmac.h
+++ b/arch/arm/src/samv7/sam_xdmac.h
@@ -31,6 +31,8 @@
 
 #include "chip.h"
 
+#include "hardware/sam_xdmac.h"
+
 /****************************************************************************
  * Pre-processor Definitions
  ****************************************************************************/
@@ -222,6 +224,20 @@ extern "C"
  * Public Function Prototypes
  ****************************************************************************/
 
+/****************************************************************************
+ * Name: sam_destaddr
+ *
+ * Description:
+ *   Returns the pointer to the destionation address, i.e the last address
+ *   data were written by DMA.
+ *
+ * Assumptions:
+ *   - DMA handle allocated by sam_dmachannel()
+ *
+ ****************************************************************************/
+
+size_t sam_destaddr(DMA_HANDLE handle);
+
 /****************************************************************************
  * Name: sam_dmachannel
  *
@@ -304,6 +320,43 @@ int sam_dmatxsetup(DMA_HANDLE handle, uint32_t paddr,
 int sam_dmarxsetup(DMA_HANDLE handle, uint32_t paddr,
                    uint32_t maddr, size_t nbytes);
 
+/****************************************************************************
+ * Name: sam_dmarxsetup_circular
+ *
+ * Description:
+ *   Configure DMA for receipt of two circular buffers for peripheral to
+ *   memory transfer. Function sam_dmastart_circular() needs to be called
+ *   to start the transfer. Only peripheral to memory transfer is currently
+ *   supported.
+ *
+ * Input Parameters:
+ *   handle - DMA handler
+ *   descr - array with DMA descriptors
+ *   maddr - array of memory addresses (i.e. destination addresses)
+ *   paddr - peripheral address (i.e. source address)
+ *   nbytes - number of bytes to transfer
+ *   ndescrs - number of descriptors (i.e. the lenght of descr array)
+ *
+ ****************************************************************************/
+
+int sam_dmarxsetup_circular(DMA_HANDLE handle,
+                            struct chnext_view1_s *descr[],
+                            uint32_t maddr[],
+                            uint32_t paddr,
+                            size_t nbytes,
+                            uint8_t ndescrs);
+
+/****************************************************************************
+ * Name: sam_dmastart_circular
+ *
+ * Description:
+ *   Start the DMA transfer with circular buffers.
+ *
+ ****************************************************************************/
+
+int sam_dmastart_circular(DMA_HANDLE handle, dma_callback_t callback,
+                          void *arg);
+
 /****************************************************************************
  * Name: sam_dmastart
  *