TimJTi commented on code in PR #7894:
URL: https://github.com/apache/nuttx/pull/7894#discussion_r1050682476


##########
arch/arm/src/sama5/sam_flexcom_spi.c:
##########
@@ -0,0 +1,2116 @@
+/****************************************************************************
+ * arch/arm/src/sama5/sam_flexcom_spi.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <arch/board/board.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/wdog.h>
+#include <nuttx/clock.h>
+#include <nuttx/mutex.h>
+#include <nuttx/spi/spi.h>
+
+#include "arm_internal.h"
+
+#include "chip.h"
+#include "sam_pio.h"
+#include "sam_dmac.h"
+#include "sam_memories.h"
+#include "sam_periphclks.h"
+#include "sam_flexcom_spi.h"
+#include "hardware/sam_pmc.h"
+#include "hardware/sam_flexcom_spi.h"
+#include "hardware/sam_flexcom.h"
+#include "hardware/sam_pinmap.h"
+
+#if defined(CONFIG_SAMA5_FLEXCOM0_SPI) || \
+    defined(CONFIG_SAMA5_FLEXCOM1_SPI) || \
+    defined(CONFIG_SAMA5_FLEXCOM2_SPI) || \
+    defined(CONFIG_SAMA5_FLEXCOM3_SPI) || \
+    defined(CONFIG_SAMA5_FLEXCOM4_SPI)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* When SPI DMA is enabled, small DMA transfers will still be performed by
+ * polling logic.  But we need a threshold value to determine what is small.
+ * That value is provided by CONFIG_SAMA5_SPI_DMATHRESHOLD.
+ */
+
+#  ifndef CONFIG_SAMA5_SPI_DMATHRESHOLD
+#    define CONFIG_SAMA5_SPI_DMATHRESHOLD 4
+#  endif
+
+#  ifndef CONFIG_DEBUG_SPI_INFO
+#    undef CONFIG_SAMA5_SPI_REGDEBUG
+#  endif
+
+#  ifdef CONFIG_SAMA5_SPI_DMA
+
+#  if defined(CONFIG_SAMA5_FLEXCOM0_SPI) && defined(CONFIG_SAMA5_XDMAC0)
+#    define SAMA5_FLEXCOM0_SPI_DMA true
+#  else
+#    define SAMA5_FLEXCOM0_SPI_DMA false
+#  endif
+
+#  if defined(CONFIG_SAMA5_FLEXCOM1_SPI) && defined(CONFIG_SAMA5_XDMAC0)
+#    define SAMA5_FLEXCOM1_SPI_DMA true
+#  else
+#    define SAMA5_FLEXCOM1_SPI_DMA false
+#  endif
+
+#  if defined(CONFIG_SAMA5_FLEXCOM1_SPI) && defined(CONFIG_SAMA5_XDMAC0)
+#    define SAMA5_FLEXCOM2_SPI_DMA true
+#  else
+#    define SAMA5_FLEXCOM2_SPI_DMA false
+#  endif
+
+#  if defined(CONFIG_SAMA5_FLEXCOM3_SPI) && defined(CONFIG_SAMA5_XDMAC0)
+#    define SAMA5_FLEXCOM3_SPI_DMA true
+#  else
+#    define SAMA5_FLEXCOM3_SPI_DMA false
+#  endif
+
+#  if defined(CONFIG_SAMA5_FLEXCOM4_SPI) && defined(CONFIG_SAMA5_XDMAC0)
+#    define SAMA5_FLEXCOM4_SPI_DMA true
+#  else
+#    define SAMA5_FLEXCOM4_SPI_DMA false
+#  endif
+
+#endif
+
+#ifndef CONFIG_SAMA5_SPI_DMA
+#  undef CONFIG_SAMA5_SPI_DMADEBUG
+#endif
+
+/* Clocking *****************************************************************/
+
+/* Select MCU-specific settings
+ *
+ * SPI is driven by the main clock.
+ */
+
+#define SAM_FLEXCOM_SPI_CLOCK  BOARD_MCK_FREQUENCY
+
+/* DMA timeout.  The value is not critical; we just don't want the system to
+ * hang in the event that a DMA does not finish.  This is set to
+ */
+
+#define DMA_TIMEOUT_MS    (800)
+#define DMA_TIMEOUT_TICKS MSEC2TICK(DMA_TIMEOUT_MS)
+
+/* Debug ********************************************************************/
+
+/* Check if SPI debug is enabled */
+
+#ifndef CONFIG_DEBUG_DMA
+#  undef CONFIG_SAMA5_SPI_DMADEBUG
+#endif
+
+#define DMA_INITIAL      0
+#define DMA_AFTER_SETUP  1
+#define DMA_AFTER_START  2
+#define DMA_CALLBACK     3
+#define DMA_TIMEOUT      3
+#define DMA_END_TRANSFER 4
+#define DMA_NSAMPLES     5
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* The state of the one SPI chip select */
+
+struct sam_flex_spics_s
+{
+  struct spi_dev_s flex_spidev;    /* Externally visible part of the
+                                    * SPI interface */
+  uint32_t frequency;              /* Requested clock frequency */
+  uint32_t actual;                 /* Actual clock frequency */
+  uint8_t nbits;                   /* Width of word in bits (8 to 16) */
+  uint8_t mode;                    /* Mode 0,1,2,3 */
+
+#if defined(CONFIG_SAMA5_FLEXCOM0_SPI) || \
+    defined(CONFIG_SAMA5_FLEXCOM1_SPI) || \
+    defined(CONFIG_SAMA5_FLEXCOM2_SPI) || \
+    defined(CONFIG_SAMA5_FLEXCOM3_SPI) || \
+    defined(CONFIG_SAMA5_FLEXCOM4_SPI)
+  uint8_t flex_spino;               /* SPI controller number (0 or 1) */
+#endif
+  uint8_t cs;                  /* Chip select number */
+
+#ifdef CONFIG_SAMA5_SPI_DMA
+  bool candma;                 /* DMA is supported */
+  sem_t dmawait;               /* Used to wait for DMA completion */
+  struct wdog_s dmadog;        /* Watchdog that handles DMA timeouts */
+  int result;                  /* DMA result */
+  DMA_HANDLE rxdma;            /* SPI RX DMA handle */
+  DMA_HANDLE txdma;            /* SPI TX DMA handle */
+#endif
+
+  /* Debug stuff */
+
+#ifdef CONFIG_SAMA5_SPI_DMADEBUG
+  struct sam_dmaregs_s rxdmaregs[DMA_NSAMPLES];
+  struct sam_dmaregs_s txdmaregs[DMA_NSAMPLES];
+#endif
+};
+
+/* Type of board-specific SPI status function */
+
+typedef void (*select_t)(uint32_t devid, bool selected);
+
+/* Chip select register offsetrs */
+
+/* The overall state of one SPI controller */
+
+struct sam_flex_spidev_s
+{
+  uint32_t base;               /* SPI controller register base address */
+  mutex_t spilock;             /* Assures mutually exclusive access to SPI */
+  select_t select;             /* SPI select callout */
+  bool initialized;            /* TRUE: Controller has been initialized */
+#ifdef CONFIG_SAMA5_SPI_DMA
+  uint8_t pid;                 /* Peripheral ID */
+#endif
+
+  /* Debug stuff */
+
+#ifdef CONFIG_SAMA5_SPI_REGDEBUG
+  bool     wrlast;            /* Last was a write */
+  uint32_t addresslast;       /* Last address */
+  uint32_t valuelast;         /* Last value */
+  int      ntimes;            /* Number of times */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Helpers */
+
+#ifdef CONFIG_SAMA5_SPI_REGDEBUG
+static bool flex_spi_checkreg(struct sam_flex_spidev_s *flex_spi,
+                                 bool wr, uint32_t value, uint32_t address);
+#else
+#  define   flex_spi_checkreg(flex_spi,wr,value,address) (false)
+#endif
+
+static inline uint32_t flex_spi_getreg(struct sam_flex_spidev_s *flex_spi,
+                                       unsigned int offset);
+static inline void flex_spi_putreg(struct sam_flex_spidev_s *spi,
+                                   uint32_t value, unsigned int offset);
+static inline struct sam_flex_spidev_s
+                     *flex_spi_dev(struct sam_flex_spics_s *flex_spics);
+
+#ifdef CONFIG_DEBUG_SPI_INFO
+static void     flex_spi_dumpregs(struct sam_flex_spidev_s *flex_spi,
+                                  const char *msg);
+#else
+# define        flex_spi_dumpregs(flex_spi,msg)
+#endif
+
+static inline void flex_spi_flush(struct sam_flex_spidev_s *flex_spi);
+static inline uint32_t flex_spi_cs2pcs(struct sam_flex_spics_s *flex_spics);
+
+/* DMA support */
+
+#ifdef CONFIG_SAMA5_SPI_DMA
+
+#  ifdef CONFIG_SAMA5_SPI_DMADEBUG
+#    define spi_rxdma_sample(s,i) sam_dmasample((s)->rxdma, &(s)->rxdmaregs[i])
+#    define spi_txdma_sample(s,i) sam_dmasample((s)->txdma, &(s)->txdmaregs[i])
+static void     flex_spi_dma_sampleinit(struct sam_flex_spics_s *flex_spics);
+static void     flex_spi_dma_sampledone(struct sam_flex_spics_s *flex_spics);
+
+#  else
+#    define flex_spi_rxdma_sample(s,i)
+#    define flex_spi_txdma_sample(s,i)
+#    define flex_spi_dma_sampleinit(s)
+#    define flex_spi_dma_sampledone(s)
+
+#  endif
+
+static void flex_spi_rxcallback(DMA_HANDLE handle, void *arg, int result);
+static void flex_spi_txcallback(DMA_HANDLE handle, void *arg, int result);
+static inline uintptr_t flex_spi_physregaddr(
+                        struct sam_flex_spics_s *flex_spics,
+                        unsigned int offset);
+#endif
+
+/* SPI methods */
+
+static int      flex_spi_lock(struct spi_dev_s *dev, bool lock);
+static void     flex_spi_select(struct spi_dev_s *dev, uint32_t devid,
+                  bool selected);
+static uint32_t flex_spi_setfrequency(struct spi_dev_s *dev, uint32_t freq);
+static void flex_spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode);
+static void flex_spi_setbits(struct spi_dev_s *dev, int nbits);
+static uint32_t flex_spi_send(struct spi_dev_s *dev, uint32_t wd);
+
+#ifdef CONFIG_SAMA5_SPI_DMA
+static void flex_spi_exchange_nodma(struct spi_dev_s *dev,
+                                    const void *txbuffer, void *rxbuffer,
+                                    size_t nwords);
+#endif
+static void flex_spi_exchange(struct spi_dev_s *dev, const void *txbuffer,
+                              void *rxbuffer, size_t nwords);
+#ifndef CONFIG_SPI_EXCHANGE
+static void flex_spi_sndblock(struct spi_dev_s *dev,
+                              const void *buffer, size_t nwords);
+static void flex_spi_recvblock(struct spi_dev_s *dev, void *buffer,
+                               size_t nwords);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* This array maps chip select numbers (0-1) to CSR register offsets */
+
+static const uint16_t g_csroffset[2] =
+{
+  SAM_FLEXCOM_SPI_CSR0_OFFSET, SAM_FLEXCOM_SPI_CSR1_OFFSET
+};
+
+#ifdef CONFIG_SAMA5_FLEXCOM0_SPI
+/* FLEXCOM0 SPI driver operations */
+
+static const struct spi_ops_s g_flexcom0_spiops =
+{
+  .lock              = flex_spi_lock,
+  .select            = flex_spi_select,
+  .setfrequency      = flex_spi_setfrequency,
+  .setmode           = flex_spi_setmode,
+  .setbits           = flex_spi_setbits,
+#  ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = 0,                 /* Not supported */
+#  endif
+  .status            = sam_flexcom0_spistatus,
+#  ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = sam_flexcom0_spicmddata,
+#  endif
+  .send              = flex_spi_send,
+#  ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = flex_spi_exchange,
+#  else
+  .sndblock          = flex_spi_sndblock,
+  .recvblock         = flex_spi_recvblock,
+#  endif
+  .registercallback  = 0,                 /* Not implemented */
+};
+
+/* This is the overall state of the FLEXCOM0 SPI controller */
+
+static struct sam_flex_spidev_s g_flexcom0dev =
+{
+  .base    = SAM_FLEXCOM0_VBASE,
+  .spilock = NXMUTEX_INITIALIZER,
+  .select  = sam_flexcom0_spiselect,
+#  ifdef SAMA5_FLEXCOM0_SPI_DMA
+  .pid     = SAM_PID_FLEXCOM0,
+#  endif
+};
+#endif /* CONFIG_SAMA5_FLEXCOM0_SPI */
+
+#ifdef CONFIG_SAMA5_FLEXCOM1_SPI
+/* FLEXCOM1 SPI driver operations */
+
+static const struct spi_ops_s g_flexcom1_spiops =
+{
+  .lock              = flex_spi_lock,
+  .select            = flex_spi_select,
+  .setfrequency      = flex_spi_setfrequency,
+  .setmode           = flex_spi_setmode,
+  .setbits           = flex_spi_setbits,
+#  ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = 0,                 /* Not supported */
+#  endif
+  .status            = sam_flexcom1_spistatus,
+#  ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = sam_flexcom1_spicmddata,
+#  endif
+  .send              = flex_spi_send,
+#  ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = flex_spi_exchange,
+#  else
+  .sndblock          = flex_spi_sndblock,
+  .recvblock         = flex_spi_recvblock,
+#  endif
+  .registercallback  = 0,                 /* Not implemented */
+};
+
+/* This is the overall state of the FLEXCOM1 SPI controller */
+
+static struct sam_flex_spidev_s g_flexcom1dev =
+{
+  .base    = SAM_FLEXCOM1_VBASE,
+  .spilock = NXMUTEX_INITIALIZER,
+  .select  = sam_flexcom1_spiselect,
+#  ifdef SAMA5_FLEXCOM1_SPI_DMA
+  .pid     = SAM_PID_FLEXCOM1,
+#  endif
+};
+#endif /* CONFIG_SAMA5_FLEXCOM1_SPI */
+
+#ifdef CONFIG_SAMA5_FLEXCOM2_SPI
+/* FLEXCOM2 SPI driver operations */
+
+static const struct spi_ops_s g_flexcom2_spiops =
+{
+  .lock              = flex_spi_lock,
+  .select            = flex_spi_select,
+  .setfrequency      = flex_spi_setfrequency,
+  .setmode           = flex_spi_setmode,
+  .setbits           = flex_spi_setbits,
+#  ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = 0,                 /* Not supported */
+#  endif
+  .status            = sam_flexcom2_spistatus,
+#  ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = sam_flexcom2_spicmddata,
+#  endif
+  .send              = flex_spi_send,
+#  ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = flex_spi_exchange,
+#  else
+  .sndblock          = flex_spi_sndblock,
+  .recvblock         = flex_spi_recvblock,
+#  endif
+  .registercallback  = 0,                 /* Not implemented */
+};
+
+/* This is the overall state of the FLEXCOM2 SPI controller */
+
+static struct sam_flex_spidev_s g_flexcom2dev =
+{
+  .base    = SAM_FLEXCOM2_VBASE,
+  .spilock = NXMUTEX_INITIALIZER,
+  .select  = sam_flexcom2_spiselect,
+#  ifdef SAMA5_FLEXCOM2_SPI_DMA
+  .pid     = SAM_PID_FLEXCOM2,
+#  endif
+};
+#endif /* CONFIG_SAMA5_FLEXCOM2_SPI */
+
+#ifdef CONFIG_SAMA5_FLEXCOM3_SPI
+/* FLEXCOM3 SPI driver operations */
+
+static const struct spi_ops_s g_flexcom3_spiops =
+{
+  .lock              = flex_spi_lock,
+  .select            = flex_spi_select,
+  .setfrequency      = flex_spi_setfrequency,
+  .setmode           = flex_spi_setmode,
+  .setbits           = flex_spi_setbits,
+#  ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = 0,                 /* Not supported */
+#  endif
+  .status            = sam_flexcom3_spistatus,
+#  ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = sam_flexcom3_spicmddata,
+#  endif
+  .send              = flex_spi_send,
+#  ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = flex_spi_exchange,
+#  else
+  .sndblock          = flex_spi_sndblock,
+  .recvblock         = flex_spi_recvblock,
+#  endif
+  .registercallback  = 0,                 /* Not implemented */
+};
+
+/* This is the overall state of the FLEXCOM3 SPI controller */
+
+static struct sam_flex_spidev_s g_flexcom3dev =
+{
+  .base    = SAM_FLEXCOM3_VBASE,
+  .spilock = NXMUTEX_INITIALIZER,
+  .select  = sam_flexcom3_spiselect,
+#  ifdef SAMA5_FLEXCOM3_SPI_DMA
+  .pid     = SAM_PID_FLEXCOM3,
+#  endif
+};
+#endif /* CONFIG_SAMA5_FLEXCOM3_SPI */
+
+#ifdef CONFIG_SAMA5_FLEXCOM4_SPI
+/* FLEXCOM4 SPI driver operations */
+
+static const struct spi_ops_s g_flexcom4_spiops =
+{
+  .lock              = flex_spi_lock,
+  .select            = flex_spi_select,
+  .setfrequency      = flex_spi_setfrequency,
+  .setmode           = flex_spi_setmode,
+  .setbits           = flex_spi_setbits,
+#  ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = 0,                 /* Not supported */
+#  endif
+  .status            = sam_flexcom4_spistatus,
+#  ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = sam_flexcom4_spicmddata,
+#  endif
+  .send              = flex_spi_send,
+#  ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = flex_spi_exchange,
+#  else
+  .sndblock          = flex_spi_sndblock,
+  .recvblock         = flex_spi_recvblock,
+#  endif
+  .registercallback  = 0,                 /* Not implemented */
+};
+
+/* This is the overall state of the FLEXCOM4 SPI controller */
+
+static struct sam_flex_spidev_s g_flexcom4dev =
+{
+  .base    = SAM_FLEXCOM4_VBASE,
+  .spilock = NXMUTEX_INITIALIZER,
+  .select  = sam_flexcom4_spiselect,
+#  ifdef SAMA5_FLEXCOM4_SPI_DMA
+  .pid     = SAM_PID_FLEXCOM4,
+#  endif
+};
+#endif /* CONFIG_SAMA5_FLEXCOM4_SPI */
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: flex_spi_checkreg
+ *
+ * Description:
+ *   Check if the current register access is a duplicate of the preceding.
+ *
+ * Input Parameters:
+ *   value   - The value to be written
+ *   address - The address of the register to write to
+ *
+ * Returned Value:
+ *   true:  This is the first register access of this type.
+ *   flase: This is the same as the preceding register access.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SAMA5_SPI_REGDEBUG
+static bool flex_spi_checkreg(struct sam_flex_spidev_s *flex_spi, bool wr,
+                              uint32_t value, uint32_t address)
+{
+  if (wr      == flex_spi->wrlast &&     /* Same kind of access? */
+      value   == flex_spi->valuelast &&  /* Same value? */
+      address == flex_spi->addresslast)  /* Same address? */
+    {
+      /* Yes, then just keep a count of the number of times we did this. */
+
+      flex_spi->ntimes++;
+      return false;
+    }
+  else
+    {
+      /* Did we do the previous operation more than once? */
+
+      if (flex_spi->ntimes > 0)
+        {
+          /* Yes... show how many times we did it */
+
+          spiinfo("...[Repeats %d times]...\n", flex_spi->ntimes);
+        }
+
+      /* Save information about the new access */
+
+      flex_spi->wrlast      = wr;
+      flex_spi->valuelast   = value;
+      flex_spi->addresslast = address;
+      flex_spi->ntimes      = 0;
+    }
+
+  /* Return true if this is the first time that we have done this operation */
+
+  return true;
+}
+#endif /* CONFIG_SAMA5_SPI_REGDEBUG */
+
+/****************************************************************************
+ * Name: flex_spi_getreg
+ *
+ * Description:
+ *  Read an SPI register
+ *
+ ****************************************************************************/
+
+static inline uint32_t flex_spi_getreg(struct sam_flex_spidev_s *flex_spi,
+                                  unsigned int offset)
+{
+  uint32_t address = flex_spi->base + offset;
+  uint32_t value = getreg32(address);
+
+#ifdef CONFIG_SAMA5_SPI_REGDEBUG
+  if (flex_spi_checkreg(flex_spi, false, value, address))
+    {
+      spiinfo("%08" PRIx32 "->%08" PRIx32 "\n", address, value);
+    }
+#endif
+
+  return value;
+}
+
+/****************************************************************************
+ * Name: flex_spi_putreg
+ *
+ * Description:
+ *  Write a value to an SPI register
+ *
+ ****************************************************************************/
+
+static inline void flex_spi_putreg(struct sam_flex_spidev_s *flex_spi,
+                                   uint32_t value, unsigned int offset)
+{
+  uint32_t address = flex_spi->base + offset;
+
+#ifdef CONFIG_SAMA5_SPI_REGDEBUG
+  if (flex_spi_checkreg(flex_spi, true, value, address))
+    {
+      spiinfo("%08" PRIx32 "<-%08" PRIx32 "\n", address, value);
+    }
+#endif
+
+  putreg32(value, address);
+}
+
+/****************************************************************************
+ * Name: flex_spi_dumpregs
+ *
+ * Description:
+ *   Dump the contents of all SPI registers
+ *
+ * Input Parameters:
+ *   spi - The SPI controller to dump
+ *   msg - Message to print before the register data
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_SPI_INFO
+static void flex_spi_dumpregs(struct sam_flex_spidev_s *flex_spi,
+                              const char *msg)
+{
+  spiinfo("%s:\n", msg);
+  spiinfo("    MR:%08x   SR:%08x  IMR:%08x\n",
+          getreg32(flex_spi->base + SAM_FLEXCOM_SPI_MR_OFFSET),
+          getreg32(flex_spi->base + SAM_FLEXCOM_SPI_SR_OFFSET),
+          getreg32(flex_spi->base + SAM_FLEXCOM_SPI_IMR_OFFSET));
+  spiinfo("  CSR0:%08x CSR1:%08x \n",
+          getreg32(flex_spi->base + SAM_FLEXCOM_SPI_CSR0_OFFSET),
+          getreg32(flex_spi->base + SAM_FLEXCOM_SPI_CSR1_OFFSET));
+  spiinfo("  WPCR:%08x WPSR:%08x\n",
+          getreg32(flex_spi->base + SAM_FLEXCOM_SPI_WPCR_OFFSET),
+          getreg32(flex_spi->base + SAM_FLEXCOM_SPI_WPSR_OFFSET));
+}
+#endif
+
+/****************************************************************************
+ * Name: flex_spi_dev
+ *
+ * Description:
+ *    Given a chip select instance, return a pointer to the parent SPI
+ *    controller instance.
+ *
+ ****************************************************************************/
+
+static inline struct sam_flex_spidev_s *flex_spi_dev(struct sam_flex_spics_s
+                                                     *flex_spics)
+{
+#ifdef CONFIG_SAMA5_FLEXCOM0_SPI
+  if (flex_spics->flex_spino == 0)
+      return &g_flexcom0dev;
+#endif
+#ifdef CONFIG_SAMA5_FLEXCOM1_SPI
+  if (flex_spics->flex_spino == 1)
+      return &g_flexcom1dev;
+#endif
+#ifdef CONFIG_SAMA5_FLEXCOM2_SPI
+  if (flex_spics->flex_spino == 2)
+      return &g_flexcom2dev;
+#endif
+#ifdef CONFIG_SAMA5_FLEXCOM3_SPI
+  if (flex_spics->flex_spino == 3)
+      return &g_flexcom3dev;
+#endif
+#ifdef CONFIG_SAMA5_FLEXCOM4_SPI
+  if (flex_spics->flex_spino == 4)
+      return &g_flexcom4dev;
+#endif 
+  /* shouldn't get here */
+
+  DEBUGASSERT(false);
+  return NULL;
+}
+
+/****************************************************************************
+ * Name: flex_spi_flush
+ *
+ * Description:
+ *   Make sure that there are now dangling SPI transfer in progress
+ *
+ * Input Parameters:
+ *   spi - SPI controller state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void flex_spi_flush(struct sam_flex_spidev_s *flex_spi)
+{
+  /* Make sure the no TX activity is in progress... waiting if necessary */
+
+  while ((flex_spi_getreg(flex_spi, SAM_FLEXCOM_SPI_SR_OFFSET) &
+          FLEXCOM_SPI_INT_TXEMPTY) == 0);
+
+  /* Then make sure that there is no pending RX data .. reading as
+   * discarding as necessary.
+   */
+
+  while ((flex_spi_getreg(flex_spi, SAM_FLEXCOM_SPI_SR_OFFSET) &
+          FLEXCOM_SPI_INT_RDRF) != 0)
+    {
+       flex_spi_getreg(flex_spi, SAM_FLEXCOM_SPI_RDR_OFFSET);
+    }
+}
+
+/****************************************************************************
+ * Name: flex_spi_cs2pcs
+ *
+ * Description:
+ *   Map the chip select number to the bit-set PCS field used in the SPI
+ *   registers.  A chip select number is used for indexing and identifying
+ *   chip selects.  However, the chip select information is represented by
+ *   a bit set in the SPI registers.  This function maps those chip select
+ *   numbers to the correct bit set:
+ *
+ *    CS  Returned Spec  Effective
+ *    No.   PCS    Value  NPCS
+ *   ---- ------  ------ --------
+ *    0    00      x0     10
+ *    1    01      01     01
+ *
+ * Input Parameters:
+ *   flex_spics - Device-specific state data
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t flex_spi_cs2pcs(struct sam_flex_spics_s *flex_spics)
+{
+  return ((uint32_t)1 << (flex_spics->cs)) - 1;
+}
+
+/****************************************************************************
+ * Name: flex_spi_dma_sampleinit
+ *
+ * Description:
+ *   Initialize sampling of DMA registers (if CONFIG_SAMA5_SPI_DMADEBUG)
+ *
+ * Input Parameters:
+ *   flex_spics - Chip select doing the DMA
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SAMA5_SPI_DMADEBUG
+static void flex_spi_dma_sampleinit(struct sam_flex_spics_s *flex_spics)
+{
+  /* Put contents of register samples into a known state */
+
+  memset(flex_spics->rxdmaregs, 0xff,
+         DMA_NSAMPLES * sizeof(struct sam_dmaregs_s));
+  memset(flex_spics->txdmaregs, 0xff,
+         DMA_NSAMPLES * sizeof(struct sam_dmaregs_s));
+
+  /* Then get the initial samples */
+
+  sam_dmasample(flex_spics->rxdma, &flex_spics->rxdmaregs[DMA_INITIAL]);
+  sam_dmasample(flex_spics->txdma, &flex_spics->txdmaregs[DMA_INITIAL]);
+}
+#endif
+
+/****************************************************************************
+ * Name: flex_spi_dma_sampledone
+ *
+ * Description:
+ *   Dump sampled DMA registers
+ *
+ * Input Parameters:
+ *   flex_spics - Chip select doing the DMA
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SAMA5_SPI_DMADEBUG
+static void flex_spi_dma_sampledone(struct sam_flex_spics_s *flex_spics)
+{
+  /* Sample the final registers */
+
+  sam_dmasample(flex_spics->rxdma, &flex_spics->rxdmaregs[DMA_END_TRANSFER]);
+  sam_dmasample(flex_spics->txdma, &flex_spics->txdmaregs[DMA_END_TRANSFER]);
+
+  /* Then dump the sampled DMA registers */
+
+  /* Initial register values */
+
+  sam_dmadump(flex_spics->txdma, &flex_spics->txdmaregs[DMA_INITIAL],
+              "TX: Initial Registers");
+  sam_dmadump(flex_spics->rxdma, &flex_spics->rxdmaregs[DMA_INITIAL],
+              "RX: Initial Registers");
+
+  /* Register values after DMA setup */
+
+  sam_dmadump(flex_spics->txdma, &flex_spics->txdmaregs[DMA_AFTER_SETUP],
+              "TX: After DMA Setup");
+  sam_dmadump(flex_spics->rxdma, &flex_spics->rxdmaregs[DMA_AFTER_SETUP],
+              "RX: After DMA Setup");
+
+  /* Register values after DMA start */
+
+  sam_dmadump(flex_spics->txdma, &flex_spics->txdmaregs[DMA_AFTER_START],
+              "TX: After DMA Start");
+  sam_dmadump(flex_spics->rxdma, &flex_spics->rxdmaregs[DMA_AFTER_START],
+              "RX: After DMA Start");
+
+  /* Register values at the time of the TX and RX DMA callbacks
+   * -OR- DMA timeout.
+   *
+   * If the DMA timedout, then there will not be any RX DMA
+   * callback samples.  There is probably no TX DMA callback
+   * samples either, but we don't know for sure.
+   */
+
+  sam_dmadump(flex_spics->txdma, &flex_spics->txdmaregs[DMA_CALLBACK],
+              "TX: At DMA callback");
+
+  /* Register values at the end of the DMA */
+
+  if (flex_spics->result == -ETIMEDOUT)
+    {
+      sam_dmadump(flex_spics->rxdma, &flex_spics->rxdmaregs[DMA_TIMEOUT],
+                  "RX: At DMA timeout");
+    }
+  else
+    {
+      sam_dmadump(flex_spics->rxdma, &flex_spics->rxdmaregs[DMA_CALLBACK],
+                  "RX: At DMA callback");
+    }
+
+  sam_dmadump(flex_spics->rxdma, &flex_spics->rxdmaregs[DMA_END_TRANSFER],
+              "RX: At End-of-Transfer");
+  sam_dmadump(flex_spics->txdma, &flex_spics->txdmaregs[DMA_END_TRANSFER],
+              "TX: At End-of-Transfer");
+}
+#endif /* CONFIG_SAMA5_SPI_DMADEBUG */
+
+/****************************************************************************
+ * Name: flex_spi_dmatimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when a has expired without completion of a
+ *   DMA.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SAMA5_SPI_DMA
+static void flex_spi_dmatimeout(wdparm_t arg)
+{
+  struct sam_flex_spics_s *flex_spics = (struct sam_flex_spics_s *)arg;
+  DEBUGASSERT(flex_spics != NULL);
+
+  /* Sample DMA registers at the time of the timeout */
+
+  flex_spi_rxdma_sample(flex_spics, DMA_CALLBACK);
+
+  /* Report timeout result, perhaps overwriting any failure reports from
+   * the TX callback.
+   */
+
+  flex_spics->result = -ETIMEDOUT;
+
+  /* Then wake up the waiting thread */
+
+  nxsem_post(&flex_spics->dmawait);
+}
+#endif
+
+/****************************************************************************
+ * Name: flex_spi_rxcallback
+ *
+ * Description:
+ *   This callback function is invoked at the completion of the SPI RX DMA.
+ *
+ * Input Parameters:
+ *   handle - The DMA handler
+ *   arg - A pointer to the chip select struction
+ *   result - The result of the DMA transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SAMA5_SPI_DMA
+static void flex_spi_rxcallback(DMA_HANDLE handle, void *arg, int result)
+{
+  struct sam_flex_spics_s *flex_spics = (struct sam_flex_spics_s *)arg;
+  DEBUGASSERT(flex_spics != NULL);
+
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&flex_spics->dmadog);
+
+  /* Sample DMA registers at the time of the callback */
+
+  flex_spi_rxdma_sample(flex_spics, DMA_CALLBACK);
+
+  /* Report the result of the transfer only if the TX callback has not
+   * already reported an error.
+   */
+
+  if (flex_spics->result == -EBUSY)
+    {
+      /* Save the result of the transfer if no error was previously
+       * reported
+       */
+
+      flex_spics->result = result;
+    }
+
+  /* Then wake up the waiting thread */
+
+  nxsem_post(&flex_spics->dmawait);
+}
+#endif
+
+/****************************************************************************
+ * Name: flex_spi_txcallback
+ *
+ * Description:
+ *   This callback function is invoked at the completion of the SPI TX DMA.
+ *
+ * Input Parameters:
+ *   handle - The DMA handler
+ *   arg - A pointer to the chip select struction
+ *   result - The result of the DMA transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SAMA5_SPI_DMA
+static void flex_spi_txcallback(DMA_HANDLE handle, void *arg, int result)
+{
+  struct sam_flex_spics_s *flex_spics = (struct sam_spics_s *)arg;
+  DEBUGASSERT(flex_spics != NULL);
+
+  flex_spi_txdma_sample(flex_spics, DMA_CALLBACK);
+
+  /* Do nothing on the TX callback unless an error is reported.  This
+   * callback is not really important because the SPI exchange is not
+   * complete until the RX callback is received.
+   */
+
+  if (result != OK && flex_spics->result == -EBUSY)
+    {
+      /* Save the result of the transfer if an error is reported */
+
+      flex_spics->result = result;
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: flex_spi_physregaddr
+ *
+ * Description:
+ *   Return the physical address of an SPI register
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SAMA5_SPI_DMA
+static inline uintptr_t flex_spi_physregaddr(struct sam_flex_spics_s
+                                            *flex_spics, unsigned int offset)
+{
+  struct sam_flex_spidev_s *flex_spi = flex_spi_dev(flex_spics);
+  return sam_physregaddr(flex_spi->base + offset);
+}
+#endif
+
+/****************************************************************************
+ * Name: flex_spi_lock
+ *
+ * Description:
+ *   On SPI buses where there are multiple devices, it will be necessary to
+ *   lock SPI to have exclusive access to the buses for a sequence of
+ *   transfers.  The bus should be locked before the chip is selected. After
+ *   locking the SPI bus, the caller should then also call the setfrequency,
+ *   setbits, and setmode methods to make sure that the SPI is properly
+ *   configured for the device.  If the SPI bus is being shared, then it
+ *   may have been left in an incompatible state.
+ *
+ * Input Parameters:
+ *   dev  - Device-specific state data
+ *   lock - true: Lock spi bus, false: unlock SPI bus
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int flex_spi_lock(struct spi_dev_s *flex_dev, bool lock)
+{
+  struct sam_flex_spics_s *flex_spics = (struct sam_flex_spics_s *)flex_dev;
+  struct sam_flex_spidev_s *flex_spi = flex_spi_dev(flex_spics);
+  int ret;
+
+  spiinfo("lock=%d\n", lock);
+  if (lock)
+    {
+      ret = nxmutex_lock(&flex_spi->spilock);
+    }
+  else
+    {
+      ret = nxmutex_unlock(&flex_spi->spilock);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: flex_spi_select
+ *
+ * Description:
+ *   This function does not actually set the chip select line.  Rather, it
+ *   simply maps the device ID into a chip select number and retains that
+ *   chip select number for later use.
+ *
+ * Input Parameters:
+ *   dev -       Device-specific state data
+ *   frequency - The SPI frequency requested
+ *
+ * Returned Value:
+ *   Returns the actual frequency selected
+ *
+ ****************************************************************************/
+
+static void flex_spi_select(struct spi_dev_s *dev, uint32_t devid,
+                       bool selected)
+{
+  struct sam_flex_spics_s *flex_spics = (struct sam_flex_spics_s *)dev;
+  struct sam_flex_spidev_s *flex_spi = flex_spi_dev(flex_spics);
+  uint32_t regval;
+
+  /* Are we selecting or de-selecting the device? */
+
+  spiinfo("selected=%d\n", selected);
+  if (selected)
+    {
+      spiinfo("cs=%d\n", flex_spics->cs);
+
+      /* Before writing the TDR, the PCS field in the SPI_MR register must be
+       * set in order to select a slave.
+       */
+
+      regval  = flex_spi_getreg(flex_spi, SAM_FLEXCOM_SPI_MR_OFFSET);
+      regval &= ~FLEXCOM_SPI_MR_PCS_MASK;
+      regval |= (flex_spi_cs2pcs(flex_spics) << FLEXCOM_SPI_MR_PCS_SHIFT);
+      flex_spi_putreg(flex_spi, regval, SAM_FLEXCOM_SPI_MR_OFFSET);
+    }
+
+  /* Perform any board-specific chip select operations. PIO chip select
+   * pins may be programmed by the board specific logic in one of two
+   * different ways.  First, the pins may be programmed as SPI peripherals.
+   * In that case, the pins are completely controlled by the SPI driver.
+   * The sam_spi[0|1]select methods still needs to be provided, but they
+   * may be only stubs.
+   *
+   * An alternative way to program the PIO chip select pins is as normal
+   * PIO outputs.  In that case, the automatic control of the CS pins is
+   * bypassed and this function must provide control of the chip select.
+   * NOTE:  In this case, the PIO output pin does *not* have to be the
+   * same as the NPCS pin normal associated with the chip select number.
+   */
+
+  flex_spi->select(devid, selected);
+}
+
+/****************************************************************************
+ * Name: flex_spi_setfrequency
+ *
+ * Description:
+ *   Set the SPI frequency.
+ *
+ * Input Parameters:
+ *   dev  - Device-specific state data
+ *   freq - The SPI frequency requested
+ *
+ * Returned Value:
+ *   Returns the actual frequency selected
+ *
+ ****************************************************************************/
+
+static uint32_t flex_spi_setfrequency(struct spi_dev_s *dev, uint32_t freq)
+{
+  struct sam_flex_spics_s *flex_spics = (struct sam_flex_spics_s *)dev;
+  struct sam_flex_spidev_s *flex_spi = flex_spi_dev(flex_spics);
+  uint32_t actual;
+  uint32_t scbr;
+  uint32_t dlybs;
+  uint32_t dlybct;
+  uint32_t regval;
+  unsigned int offset;
+
+  spiinfo("cs=%d frequency=%" PRId32 "\n", flex_spics->cs, freq);
+
+  /* Check if the requested frequency is the same as the frequency
+   * selection
+   */
+
+  if (flex_spics->frequency == freq)
+    {
+      /* We are already at this frequency.  Return the actual. */
+
+      return flex_spics->actual;
+    }
+
+  /* Configure SPI to a frequency as close as possible to the requested
+   * frequency.
+   *
+   *   SPCK frequency = SPI_CLK / SCBR, or SCBR = SPI_CLK / frequency
+   */
+
+  scbr = SAM_FLEXCOM_SPI_CLOCK / freq;
+
+  if (scbr < 8)
+    {
+      scbr = 8;
+    }
+  else if (scbr > 254)
+    {
+      scbr = 254;
+    }
+
+  scbr = (scbr + 1) & ~1;
+
+  /* Save the new scbr value */
+
+  offset = (unsigned int)g_csroffset[flex_spics->cs];

Review Comment:
   I see now that sam_spi.c has g_csroffset as unit8_t as the offsets are 
within the range of a byte, but for flexcom they are not so I changed it to 
uint16_t here. "offset" is an unsigned int which is 32 bits on this processor 
so I think the cast may technically be unnecessary as no information is likely 
to be lost without the cast, but isn't it good practice to do this?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscr...@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


Reply via email to