summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2014-03-13 10:34:35 -0600
committerGregory Nutt <gnutt@nuttx.org>2014-03-13 10:34:35 -0600
commitee7c9174c33024148562a1125f27e65dbfe7f4ec (patch)
treea496074b6186c7ed8264308a4f4eb6d75a532866
parent47b6c71e7fbd987410d1361080b3231e94358c5a (diff)
downloadnuttx-ee7c9174c33024148562a1125f27e65dbfe7f4ec.tar.gz
nuttx-ee7c9174c33024148562a1125f27e65dbfe7f4ec.tar.bz2
nuttx-ee7c9174c33024148562a1125f27e65dbfe7f4ec.zip
Backport SPI driver enhancements from SAMA5 to SAM3/4
-rw-r--r--nuttx/ChangeLog6
-rw-r--r--nuttx/arch/arm/src/sam34/Kconfig43
-rw-r--r--nuttx/arch/arm/src/sam34/Make.defs4
-rw-r--r--nuttx/arch/arm/src/sam34/sam_spi.c1227
-rw-r--r--nuttx/arch/arm/src/sam34/sam_spi.h122
-rw-r--r--nuttx/arch/arm/src/sama5/Kconfig2
-rw-r--r--nuttx/arch/arm/src/sama5/sam_spi.c2
-rw-r--r--nuttx/arch/arm/src/sama5/sam_spi.h28
-rw-r--r--nuttx/configs/sam3u-ek/src/up_spi.c18
-rw-r--r--nuttx/configs/sam4e-ek/src/Makefile6
-rw-r--r--nuttx/configs/sam4e-ek/src/sam4e-ek.h6
-rw-r--r--nuttx/configs/sam4e-ek/src/sam_spi.c26
-rw-r--r--nuttx/configs/sam4l-xplained/src/sam_spi.c22
13 files changed, 1250 insertions, 262 deletions
diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog
index 5cf07ed45..fa0465eb9 100644
--- a/nuttx/ChangeLog
+++ b/nuttx/ChangeLog
@@ -6988,3 +6988,9 @@
functional (2014-3-13).
* configs/sam4e-ek/nsh: Networking support is now enabled by default
in the NSH configuration (2014-3-13).
+ * arch/arm/src/sam34/sam_spi.c: Backported the SAMA5 SPI driver to
+ the SAM3/4 architecture. The SAMA5 version supports both multiple
+ SPI peripherals as needed by the SAM3A and SAM3X and also supports
+ DMAC (but not PDC). The initial commit is untested and may very
+ well have (temporarily) broken SPI for the SAM3/4/ family (2014-3-13).
+
diff --git a/nuttx/arch/arm/src/sam34/Kconfig b/nuttx/arch/arm/src/sam34/Kconfig
index fcd08a52b..97b73f576 100644
--- a/nuttx/arch/arm/src/sam34/Kconfig
+++ b/nuttx/arch/arm/src/sam34/Kconfig
@@ -454,11 +454,13 @@ config SAM34_SMC
config SAM34_SPI0
bool "Serial Peripheral Interface 0 (SPI0)"
default n
+ select SPI
config SAM34_SPI1
bool "Serial Peripheral Interface 1 (SPI1)"
default n
depends on ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A
+ select SPI
config SAM34_SSC
bool "Synchronous Serial Controller (SSC)"
@@ -871,6 +873,47 @@ config GPIOF_IRQ
endif # GPIO_IRQ
endmenu # AT91SAM3/4 GPIO Interrupt Configuration
+if SAM34_SPI0 || SAM34_SPI1
+
+menu "SPI device driver options"
+
+config SAM34_SPI_DMA
+ bool "SPI DMA"
+ default n
+ depends on (SAM34_DMAC0 && SAM34_SPI0) || (SAM34_DMAC1 && SAM34_SPI1)
+ ---help---
+ Use DMA to improve SPI transfer performance.
+
+config SAM34_SPI_DMATHRESHOLD
+ int "SPI DMA threshold"
+ default 4
+ depends on SAM34_SPI_DMA
+ ---help---
+ 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 SAM34_SPI_DMATHRESHOLD.
+
+config SAM34_SPI_DMADEBUG
+ bool "SPI DMA transfer debug"
+ depends on SAM34_SPI_DMA && DEBUG && DEBUG_DMA
+ default n
+ ---help---
+ Enable special debug instrumentation analyze SPI DMA data transfers.
+ This logic is as non-invasive as possible: It samples DMA
+ registers at key points in the data transfer and then dumps all of
+ the registers at the end of the transfer.
+
+config SAM34_SPI_REGDEBUG
+ bool "SPI Register level debug"
+ depends on DEBUG
+ default n
+ ---help---
+ Output detailed register-level SPI device debug information.
+ Requires also DEBUG.
+
+endmenu # SPI device driver options
+endif # SAM34_SPI0 || SAM34_SPI1
+
if SAM34_EMAC
menu "AT91SAM3/4 EMAC device driver options"
diff --git a/nuttx/arch/arm/src/sam34/Make.defs b/nuttx/arch/arm/src/sam34/Make.defs
index c645319c3..fcbb0a6f3 100644
--- a/nuttx/arch/arm/src/sam34/Make.defs
+++ b/nuttx/arch/arm/src/sam34/Make.defs
@@ -114,4 +114,8 @@ endif
ifeq ($(CONFIG_SAM34_SPI0),y)
CHIP_CSRCS += sam_spi.c
+else
+ifeq ($(CONFIG_SAM34_SPI1),y)
+CHIP_CSRCS += sam_spi.c
+endif
endif
diff --git a/nuttx/arch/arm/src/sam34/sam_spi.c b/nuttx/arch/arm/src/sam34/sam_spi.c
index dc0647e75..36e3b6aae 100644
--- a/nuttx/arch/arm/src/sam34/sam_spi.c
+++ b/nuttx/arch/arm/src/sam34/sam_spi.c
@@ -1,7 +1,7 @@
/****************************************************************************
* arch/arm/src/sam34/sam_spi.c
*
- * Copyright (C) 2011, 2013 Gregory Nutt. All rights reserved.
+ * Copyright (C) 2011, 2013-2014 Gregory Nutt. All rights reserved.
* Authors: Gregory Nutt <gnutt@nuttx.org>
* Diego Sanchez <dsanchez@nx-engineering.com>
*
@@ -44,12 +44,16 @@
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
+#include <string.h>
#include <semaphore.h>
+#include <wdog.h>
#include <errno.h>
+#include <assert.h>
#include <debug.h>
#include <arch/board/board.h>
#include <nuttx/arch.h>
+#include <nuttx/clock.h>
#include <nuttx/spi/spi.h>
#include "up_internal.h"
@@ -57,8 +61,9 @@
#include "chip.h"
#include "sam_gpio.h"
-#include "sam_spi.h"
+#include "sam_dmac.h"
#include "sam_periphclks.h"
+#include "sam_spi.h"
#include "chip/sam_pmc.h"
#include "chip/sam_spi.h"
#include "chip/sam_pinmap.h"
@@ -69,14 +74,45 @@
* 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_SAM34_SPI_DMATHRESHOLD.
+ */
+
+#ifndef CONFIG_SAM34_SPI_DMATHRESHOLD
+# define CONFIG_SAM34_SPI_DMATHRESHOLD 4
+#endif
+
+#ifdef CONFIG_SAM34_SPI_DMA
+
+# if defined(CONFIG_SAM34_SPI0) && defined(CONFIG_SAM34_DMAC0)
+# define SAM34_SPI0_DMA true
+# else
+# define SAM34_SPI0_DMA false
+# endif
+
+# if defined(CONFIG_SAM34_SPI1) && defined(CONFIG_SAM34_DMAC1)
+# define SAM34_SPI1_DMA true
+# else
+# define SAM34_SPI1_DMA false
+# endif
+#endif
+
+#ifndef CONFIG_SAM34_SPI_DMA
+# undef CONFIG_SAM34_SPI_DMADEBUG
+#endif
+
+/* Clocking *****************************************************************/
/* Select MCU-specific settings
*
- * For the SAM3U, SAM3A, and SAM3X SPI is driven by the main clock.
+ * For the SAM3U, SAM3A, SAM3X, SAM4E and SAM4S SPI is driven by the main
+ * clock.
* For the SAM4L, SPI is driven by CLK_SPI which is the PBB clock.
*/
-#if defined(CONFIG_ARCH_CHIP_SAM3U) || defined(CONFIG_ARCH_CHIP_SAM3A) || \
- defined(CONFIG_ARCH_CHIP_SAM3X)
+#if defined(CONFIG_ARCH_CHIP_SAM3U) || defined(CONFIG_ARCH_CHIP_SAM3X) || \
+ defined(CONFIG_ARCH_CHIP_SAM3A) || defined(CONFIG_ARCH_CHIP_SAM4S) || \
+ defined(CONFIG_ARCH_CHIP_SAM4E)
# define SAM_SPI_CLOCK BOARD_MCK_FREQUENCY /* Frequency of the main clock */
#elif defined(CONFIG_ARCH_CHIP_SAM4L)
# define SAM_SPI_CLOCK BOARD_PBB_FREQUENCY /* PBB frequency */
@@ -84,13 +120,12 @@
# error Unrecognized SAM architecture
#endif
-#ifdef CONFIG_SAM34_SPI1
- /* NOTE: See arch/arm/sama5/sam_spi.c. That is the same SPI IP and that
- * version on the driver has been extended to support both SPI0 and SPI1
- */
+/* 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
+ */
-# error Support for SPI1 has not yet been implemented (see NOTE)
-#endif
+#define DMA_TIMEOUT_MS (800)
+#define DMA_TIMEOUT_TICKS ((DMA_TIMEOUT_MS + (MSEC_PER_TICK-1)) / MSEC_PER_TICK)
/* Debug *******************************************************************/
/* Check if SPI debut is enabled (non-standard.. no support in
@@ -100,6 +135,12 @@
#ifndef CONFIG_DEBUG
# undef CONFIG_DEBUG_VERBOSE
# undef CONFIG_DEBUG_SPI
+# undef CONFIG_SAM34_SPI_DMADEBUG
+# undef CONFIG_SAM34_SPI_REGDEBUG
+#endif
+
+#ifndef CONFIG_DEBUG_DMA
+# undef CONFIG_SAM34_SPI_DMADEBUG
#endif
#ifdef CONFIG_DEBUG_SPI
@@ -114,22 +155,79 @@
# define spivdbg(x...)
#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 chip select */
+/* The state of the one SPI chip select */
-struct sam_spidev_s
+struct sam_spics_s
{
struct spi_dev_s spidev; /* Externally visible part of the SPI interface */
+
#ifndef CONFIG_SPI_OWNBUS
- 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 */
+ 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 */
+#endif
+
+#if defined(CONFIG_SAM34_SPI0) || defined(CONFIG_SAM34_SPI1)
+ uint8_t spino; /* SPI controller number (0 or 1) */
+#endif
+ uint8_t cs; /* Chip select number */
+
+#ifdef CONFIG_SAM34_SPI_DMA
+ bool candma; /* DMA is supported */
+ sem_t dmawait; /* Used to wait for DMA completion */
+ WDOG_ID 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_SAM34_SPI_DMADEBUG
+ struct sam_dmaregs_s rxdmaregs[DMA_NSAMPLES];
+ struct sam_dmaregs_s txdmaregs[DMA_NSAMPLES];
+#endif
+};
+
+/* Type of board-specific SPI status fuction */
+
+typedef void (*select_t)(enum spi_dev_e devid, bool selected);
+
+/* Chip select register offsetrs */
+
+/* The overall state of one SPI controller */
+
+struct sam_spidev_s
+{
+ uint32_t base; /* SPI controller register base address */
+ sem_t spisem; /* Assures mutually exclusive acess to SPI */
+ select_t select; /* SPI select callout */
+ bool initialized; /* TRUE: Controller has been initialized */
+#ifdef CONFIG_SAM34_SPI_DMA
+ uint8_t pid; /* Peripheral ID */
+#endif
+
+ /* Debug stuff */
+
+#ifdef CONFIG_SAM34_SPI_REGDEBUG
+ bool wrlast; /* Last was a write */
+ uint32_t addresslast; /* Last address */
+ uint32_t valuelast; /* Last value */
+ int ntimes; /* Number of times */
#endif
- uint8_t cs; /* Chip select number */
};
/****************************************************************************
@@ -138,42 +236,92 @@ struct sam_spidev_s
/* Helpers */
+#ifdef CONFIG_SAM34_SPI_REGDEBUG
+static bool spi_checkreg(struct sam_spidev_s *spi, bool wr,
+ uint32_t value, uint32_t address);
+#else
+# define spi_checkreg(spi,wr,value,address) (false)
+#endif
+
+static inline uint32_t spi_getreg(struct sam_spidev_s *spi,
+ unsigned int offset);
+static inline void spi_putreg(struct sam_spidev_s *spi, uint32_t value,
+ unsigned int offset);
+static inline struct sam_spidev_s *spi_device(struct sam_spics_s *spics);
+
#if defined(CONFIG_DEBUG_SPI) && defined(CONFIG_DEBUG_VERBOSE)
-static void spi_dumpregs(FAR const char *msg);
+static void spi_dumpregs(struct sam_spidev_s *spi, const char *msg);
#else
-# define spi_dumpregs(msg)
+# define spi_dumpregs(spi,msg)
#endif
-static inline void spi_flush(void);
-static inline uint32_t spi_cs2pcs(FAR struct sam_spidev_s *priv);
+static inline void spi_flush(struct sam_spidev_s *spi);
+static inline uint32_t spi_cs2pcs(struct sam_spics_s *spics);
+
+/* DMA support */
+
+#ifdef CONFIG_SAM34_SPI_DMA
+
+#ifdef CONFIG_SAM34_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 spi_dma_sampleinit(struct sam_spics_s *spics);
+static void spi_dma_sampledone(struct sam_spics_s *spics);
+
+#else
+# define spi_rxdma_sample(s,i)
+# define spi_txdma_sample(s,i)
+# define spi_dma_sampleinit(s)
+# define spi_dma_sampledone(s)
+
+#endif
+
+static void spi_rxcallback(DMA_HANDLE handle, void *arg, int result);
+static void spi_txcallback(DMA_HANDLE handle, void *arg, int result);
+static inline uintptr_t spi_physregaddr(struct sam_spics_s *spics,
+ unsigned int offset);
+#endif
/* SPI methods */
#ifndef CONFIG_SPI_OWNBUS
-static int spi_lock(FAR struct spi_dev_s *dev, bool lock);
+static int spi_lock(struct spi_dev_s *dev, bool lock);
#endif
-static void spi_select(FAR struct spi_dev_s *dev, enum spi_dev_e devid,
+static void spi_select(struct spi_dev_s *dev, enum spi_dev_e devid,
bool selected);
-static uint32_t spi_setfrequency(FAR struct spi_dev_s *dev,
- uint32_t frequency);
-static void spi_setmode(FAR struct spi_dev_s *dev,
- enum spi_mode_e mode);
-static void spi_setbits(FAR struct spi_dev_s *dev, int nbits);
-static uint16_t spi_send(FAR struct spi_dev_s *dev, uint16_t ch);
-static void spi_exchange(FAR struct spi_dev_s *dev,
- FAR const void *txbuffer, FAR void *rxbuffer, size_t nwords);
+static uint32_t spi_setfrequency(struct spi_dev_s *dev, uint32_t frequency);
+static void spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode);
+static void spi_setbits(struct spi_dev_s *dev, int nbits);
+static uint16_t spi_send(struct spi_dev_s *dev, uint16_t ch);
+#ifdef CONFIG_SAM34_SPI_DMA
+static void spi_exchange_nodma(struct spi_dev_s *dev,
+ const void *txbuffer, void *rxbuffer, size_t nwords);
+#endif
+static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer,
+ void *rxbuffer, size_t nwords);
#ifndef CONFIG_SPI_EXCHANGE
-static void spi_sndblock(FAR struct spi_dev_s *dev, FAR const void *buffer, size_t nwords);
-static void spi_recvblock(FAR struct spi_dev_s *dev, FAR void *buffer, size_t nwords);
+static void spi_sndblock(struct spi_dev_s *dev,
+ const void *buffer, size_t nwords);
+static void spi_recvblock(struct spi_dev_s *dev, void *buffer,
+ size_t nwords);
#endif
/****************************************************************************
* Private Data
****************************************************************************/
-/* SPI driver operations */
+/* This array maps chip select numbers (0-3) to CSR register offsets */
+
+static const uint8_t g_csroffset[4] =
+{
+ SAM_SPI_CSR0_OFFSET, SAM_SPI_CSR1_OFFSET,
+ SAM_SPI_CSR2_OFFSET, SAM_SPI_CSR3_OFFSET
+};
+
+#ifdef CONFIG_SAM34_SPI0
+/* SPI0 driver operations */
-static const struct spi_ops_s g_spiops =
+static const struct spi_ops_s g_spi0ops =
{
#ifndef CONFIG_SPI_OWNBUS
.lock = spi_lock,
@@ -182,9 +330,9 @@ static const struct spi_ops_s g_spiops =
.setfrequency = spi_setfrequency,
.setmode = spi_setmode,
.setbits = spi_setbits,
- .status = sam_spistatus,
+ .status = sam_spi0status,
#ifdef CONFIG_SPI_CMDDATA
- .cmddata = sam_spicmddata,
+ .cmddata = sam_spi0cmddata,
#endif
.send = spi_send,
#ifdef CONFIG_SPI_EXCHANGE
@@ -196,24 +344,55 @@ static const struct spi_ops_s g_spiops =
.registercallback = 0, /* Not implemented */
};
-#ifdef CONFIG_SPI_OWNBUS
-/* Single chip select device structure */
+/* This is the overall state of the SPI0 controller */
-static struct sam_spidev_s g_spidev;
+static struct sam_spidev_s g_spi0dev =
+{
+ .base = SAM_SPI0_BASE,
+ .select = sam_spi0select,
+#ifdef CONFIG_SAM34_SPI_DMA
+ .pid = SAM_PID_SPI0,
+#endif
+};
+#endif
-#else
-/* Held while chip is selected for mutual exclusion */
+#ifdef CONFIG_SAM34_SPI1
+/* SPI1 driver operations */
-static sem_t g_spisem;
-static bool g_spinitialized = false;
+static const struct spi_ops_s g_spi1ops =
+{
+#ifndef CONFIG_SPI_OWNBUS
+ .lock = spi_lock,
+#endif
+ .select = spi_select,
+ .setfrequency = spi_setfrequency,
+ .setmode = spi_setmode,
+ .setbits = spi_setbits,
+ .status = sam_spi1status,
+#ifdef CONFIG_SPI_CMDDATA
+ .cmddata = sam_spi1cmddata,
+#endif
+ .send = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+ .exchange = spi_exchange,
+#else
+ .sndblock = spi_sndblock,
+ .recvblock = spi_recvblock,
#endif
+ .registercallback = 0, /* Not implemented */
+};
-/* This array maps chip select numbers (0-3) to CSR register addresses */
+/* This is the overall state of the SPI0 controller */
-static const uint32_t g_csraddr[4] =
+static struct sam_spidev_s g_spi1dev =
{
- SAM_SPI0_CSR0, SAM_SPI0_CSR1, SAM_SPI0_CSR2, SAM_SPI0_CSR3
+ .base = SAM_SPI1_BASE,
+ .select = sam_spi1select,
+#ifdef CONFIG_SAM34_SPI_DMA
+ .pid = SAM_PID_SPI1,
+#endif
};
+#endif
/****************************************************************************
* Public Data
@@ -224,12 +403,114 @@ static const uint32_t g_csraddr[4] =
****************************************************************************/
/****************************************************************************
+ * Name: 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_SAM34_SPI_REGDEBUG
+static bool spi_checkreg(struct sam_spidev_s *spi, bool wr, uint32_t value,
+ uint32_t address)
+{
+ if (wr == spi->wrlast && /* Same kind of access? */
+ value == spi->valuelast && /* Same value? */
+ address == spi->addresslast) /* Same address? */
+ {
+ /* Yes, then just keep a count of the number of times we did this. */
+
+ spi->ntimes++;
+ return false;
+ }
+ else
+ {
+ /* Did we do the previous operation more than once? */
+
+ if (spi->ntimes > 0)
+ {
+ /* Yes... show how many times we did it */
+
+ lldbg("...[Repeats %d times]...\n", spi->ntimes);
+ }
+
+ /* Save information about the new access */
+
+ spi->wrlast = wr;
+ spi->valuelast = value;
+ spi->addresslast = address;
+ spi->ntimes = 0;
+ }
+
+ /* Return true if this is the first time that we have done this operation */
+
+ return true;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_getreg
+ *
+ * Description:
+ * Read an SPI register
+ *
+ ****************************************************************************/
+
+static inline uint32_t spi_getreg(struct sam_spidev_s *spi,
+ unsigned int offset)
+{
+ uint32_t address = spi->base + offset;
+ uint32_t value = getreg32(address);
+
+#ifdef CONFIG_SAM34_SPI_REGDEBUG
+ if (spi_checkreg(spi, false, value, address))
+ {
+ lldbg("%08x->%08x\n", address, value);
+ }
+#endif
+
+ return value;
+}
+
+/****************************************************************************
+ * Name: spi_putreg
+ *
+ * Description:
+ * Write a value to an SPI register
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg(struct sam_spidev_s *spi, uint32_t value,
+ unsigned int offset)
+{
+ uint32_t address = spi->base + offset;
+
+#ifdef CONFIG_SAM34_SPI_REGDEBUG
+ if (spi_checkreg(spi, true, value, address))
+ {
+ lldbg("%08x<-%08x\n", address, value);
+ }
+#endif
+
+ putreg32(value, address);
+}
+
+/****************************************************************************
* Name: 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:
@@ -238,47 +519,71 @@ static const uint32_t g_csraddr[4] =
****************************************************************************/
#if defined(CONFIG_DEBUG_SPI) && defined(CONFIG_DEBUG_VERBOSE)
-static void spi_dumpregs(FAR const char *msg)
+static void spi_dumpregs(struct sam_spidev_s *spi, const char *msg)
{
spivdbg("%s:\n", msg);
spivdbg(" MR:%08x SR:%08x IMR:%08x\n",
- getreg32(SAM_SPI0_MR), getreg32(SAM_SPI0_SR),
- getreg32(SAM_SPI0_IMR));
+ getreg32(spi->base + SAM_SPI_MR_OFFSET),
+ getreg32(spi->base + SAM_SPI_SR_OFFSET),
+ getreg32(spi->base + SAM_SPI_IMR_OFFSET));
spivdbg(" CSR0:%08x CSR1:%08x CSR2:%08x CSR3:%08x\n",
- getreg32(SAM_SPI0_CSR0), getreg32(SAM_SPI0_CSR1),
- getreg32(SAM_SPI0_CSR2), getreg32(SAM_SPI0_CSR3));
+ getreg32(spi->base + SAM_SPI_CSR0_OFFSET),
+ getreg32(spi->base + SAM_SPI_CSR1_OFFSET),
+ getreg32(spi->base + SAM_SPI_CSR2_OFFSET),
+ getreg32(spi->base + SAM_SPI_CSR3_OFFSET));
spivdbg(" WPCR:%08x WPSR:%08x\n",
- getreg32(SAM_SPI0_WPCR), getreg32(SAM_SPI0_WPSR));
+ getreg32(spi->base + SAM_SPI_WPCR_OFFSET),
+ getreg32(spi->base + SAM_SPI_WPSR_OFFSET));
}
#endif
/****************************************************************************
+ * Name: spi_device
+ *
+ * Description:
+ * Given a chip select instance, return a pointer to the parent SPI
+ * controller instance.
+ *
+ ****************************************************************************/
+
+static inline struct sam_spidev_s *spi_device(struct sam_spics_s *spics)
+{
+#if defined(CONFIG_SAM34_SPI0) && defined(CONFIG_SAM34_SPI1)
+ return spics->spino ? &g_spi1dev : &g_spi0dev;
+#elif defined(CONFIG_SAM34_SPI0)
+ return &g_spi0dev;
+#else
+ return &g_spi1dev;
+#endif
+}
+
+/****************************************************************************
* Name: spi_flush
*
* Description:
* Make sure that there are now dangling SPI transfer in progress
*
* Input Parameters:
- * priv - Device-specific state data
+ * spi - SPI controller state
*
* Returned Value:
* None
*
****************************************************************************/
-static inline void spi_flush(void)
+static inline void spi_flush(struct sam_spidev_s *spi)
{
/* Make sure the no TX activity is in progress... waiting if necessary */
- while ((getreg32(SAM_SPI0_SR) & SPI_INT_TXEMPTY) == 0);
+ while ((spi_getreg(spi, SAM_SPI_SR_OFFSET) & SPI_INT_TXEMPTY) == 0);
/* Then make sure that there is no pending RX data .. reading as
* discarding as necessary.
*/
- while ((getreg32(SAM_SPI0_SR) & SPI_INT_RDRF) != 0)
+ while ((spi_getreg(spi, SAM_SPI_SR_OFFSET) & SPI_INT_RDRF) != 0)
{
- (void)getreg32(SAM_SPI0_RDR);
+ (void)spi_getreg(spi, SAM_SPI_RDR_OFFSET);
}
}
@@ -301,17 +606,264 @@ static inline void spi_flush(void)
* 3 0111 0111 0111
*
* Input Parameters:
- * priv - Device-specific state data
+ * spics - Device-specific state data
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static inline uint32_t spi_cs2pcs(struct sam_spics_s *spics)
+{
+ return ((uint32_t)1 << (spics->cs)) - 1;
+}
+
+/****************************************************************************
+ * Name: spi_dma_sampleinit
+ *
+ * Description:
+ * Initialize sampling of DMA registers (if CONFIG_SAM34_SPI_DMADEBUG)
+ *
+ * Input Parameters:
+ * spics - Chip select doing the DMA
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SAM34_SPI_DMADEBUG
+static void spi_dma_sampleinit(struct sam_spics_s *spics)
+{
+ /* Put contents of register samples into a known state */
+
+ memset(spics->rxdmaregs, 0xff, DMA_NSAMPLES * sizeof(struct sam_dmaregs_s));
+ memset(spics->txdmaregs, 0xff, DMA_NSAMPLES * sizeof(struct sam_dmaregs_s));
+
+ /* Then get the initial samples */
+
+ sam_dmasample(spics->rxdma, &spics->rxdmaregs[DMA_INITIAL]);
+ sam_dmasample(spics->txdma, &spics->txdmaregs[DMA_INITIAL]);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dma_sampledone
+ *
+ * Description:
+ * Dump sampled DMA registers
+ *
+ * Input Parameters:
+ * spics - Chip select doing the DMA
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SAM34_SPI_DMADEBUG
+static void spi_dma_sampledone(struct sam_spics_s *spics)
+{
+ /* Sample the final registers */
+
+ sam_dmasample(spics->rxdma, &spics->rxdmaregs[DMA_END_TRANSFER]);
+ sam_dmasample(spics->txdma, &spics->txdmaregs[DMA_END_TRANSFER]);
+
+ /* Then dump the sampled DMA registers */
+ /* Initial register values */
+
+ sam_dmadump(spics->txdma, &spics->txdmaregs[DMA_INITIAL],
+ "TX: Initial Registers");
+ sam_dmadump(spics->rxdma, &spics->rxdmaregs[DMA_INITIAL],
+ "RX: Initial Registers");
+
+ /* Register values after DMA setup */
+
+ sam_dmadump(spics->txdma, &spics->txdmaregs[DMA_AFTER_SETUP],
+ "TX: After DMA Setup");
+ sam_dmadump(spics->rxdma, &spics->rxdmaregs[DMA_AFTER_SETUP],
+ "RX: After DMA Setup");
+
+ /* Register values after DMA start */
+
+ sam_dmadump(spics->txdma, &spics->txdmaregs[DMA_AFTER_START],
+ "TX: After DMA Start");
+ sam_dmadump(spics->rxdma, &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(spics->txdma, &spics->txdmaregs[DMA_CALLBACK],
+ "TX: At DMA callback");
+
+ /* Register values at the end of the DMA */
+
+ if (spics->result == -ETIMEDOUT)
+ {
+ sam_dmadump(spics->rxdma, &spics->rxdmaregs[DMA_TIMEOUT],
+ "RX: At DMA timeout");
+ }
+ else
+ {
+ sam_dmadump(spics->rxdma, &spics->rxdmaregs[DMA_CALLBACK],
+ "RX: At DMA callback");
+ }
+
+ sam_dmadump(spics->rxdma, &spics->rxdmaregs[DMA_END_TRANSFER],
+ "RX: At End-of-Transfer");
+ sam_dmadump(spics->txdma, &spics->txdmaregs[DMA_END_TRANSFER],
+ "TX: At End-of-Transfer");
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatimeout
+ *
+ * Description:
+ * The watchdog timeout setup when a has expired without completion of a
+ * DMA.
+ *
+ * Input Parameters:
+ * argc - The number of arguments (should be 1)
+ * arg - The argument (state structure reference cast to uint32_t)
*
* Returned Value:
* None
*
+ * Assumptions:
+ * Always called from the interrupt level with interrupts disabled.
+ *
****************************************************************************/
-static inline uint32_t spi_cs2pcs(FAR struct sam_spidev_s *priv)
+#ifdef CONFIG_SAM34_SPI_DMA
+static void spi_dmatimeout(int argc, uint32_t arg)
{
- return ((uint32_t)1 << (priv->cs)) - 1;
+ struct sam_spics_s *spics = (struct sam_spics_s *)arg;
+ DEBUGASSERT(spics != NULL);
+
+ /* Sample DMA registers at the time of the timeout */
+
+ spi_rxdma_sample(spics, DMA_CALLBACK);
+
+ /* Report timeout result, perhaps overwriting any failure reports from
+ * the TX callback.
+ */
+
+ spics->result = -ETIMEDOUT;
+
+ /* Then wake up the waiting thread */
+
+ sem_post(&spics->dmawait);
}
+#endif
+
+/****************************************************************************
+ * Name: 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_SAM34_SPI_DMA
+static void spi_rxcallback(DMA_HANDLE handle, void *arg, int result)
+{
+ struct sam_spics_s *spics = (struct sam_spics_s *)arg;
+ DEBUGASSERT(spics != NULL);
+
+ /* Cancel the watchdog timeout */
+
+ (void)wd_cancel(spics->dmadog);
+
+ /* Sample DMA registers at the time of the callback */
+
+ spi_rxdma_sample(spics, DMA_CALLBACK);
+
+ /* Report the result of the transfer only if the TX callback has not already
+ * reported an error.
+ */
+
+ if (spics->result == -EBUSY)
+ {
+ /* Save the result of the transfer if no error was previuosly reported */
+
+ spics->result = result;
+ }
+
+ /* Then wake up the waiting thread */
+
+ sem_post(&spics->dmawait);
+}
+#endif
+
+/****************************************************************************
+ * Name: 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_SAM34_SPI_DMA
+static void spi_txcallback(DMA_HANDLE handle, void *arg, int result)
+{
+ struct sam_spics_s *spics = (struct sam_spics_s *)arg;
+ DEBUGASSERT(spics != NULL);
+
+ spi_txdma_sample(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 && spics->result == -EBUSY)
+ {
+ /* Save the result of the transfer if an error is reported */
+
+ spics->result = result;
+ }
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_physregaddr
+ *
+ * Description:
+ * Return the physical address of an HSMCI register
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SAM34_SPI_DMA
+static inline uintptr_t spi_physregaddr(struct sam_spics_s *spics,
+ unsigned int offset)
+{
+ struct sam_spidev_s *spi = spi_device(spics);
+ return sam_physregaddr(spi->base + offset);
+}
+#endif
/****************************************************************************
* Name: spi_lock
@@ -335,14 +887,17 @@ static inline uint32_t spi_cs2pcs(FAR struct sam_spidev_s *priv)
****************************************************************************/
#ifndef CONFIG_SPI_OWNBUS
-static int spi_lock(FAR struct spi_dev_s *dev, bool lock)
+static int spi_lock(struct spi_dev_s *dev, bool lock)
{
+ struct sam_spics_s *spics = (struct sam_spics_s *)dev;
+ struct sam_spidev_s *spi = spi_device(spics);
+
spivdbg("lock=%d\n", lock);
if (lock)
{
/* Take the semaphore (perhaps waiting) */
- while (sem_wait(&g_spisem) != 0)
+ while (sem_wait(&spi->spisem) != 0)
{
/* The only case that an error should occur here is if the wait was awakened
* by a signal.
@@ -353,7 +908,7 @@ static int spi_lock(FAR struct spi_dev_s *dev, bool lock)
}
else
{
- (void)sem_post(&g_spisem);
+ (void)sem_post(&spi->spisem);
}
return OK;
@@ -377,10 +932,11 @@ static int spi_lock(FAR struct spi_dev_s *dev, bool lock)
*
****************************************************************************/
- static void spi_select(FAR struct spi_dev_s *dev, enum spi_dev_e devid,
+ static void spi_select(struct spi_dev_s *dev, enum spi_dev_e devid,
bool selected)
{
- FAR struct sam_spidev_s *priv = (FAR struct sam_spidev_s *)dev;
+ struct sam_spics_s *spics = (struct sam_spics_s *)dev;
+ struct sam_spidev_s *spi = spi_device(spics);
uint32_t regval;
/* Are we selecting or de-selecting the device? */
@@ -388,33 +944,33 @@ static int spi_lock(FAR struct spi_dev_s *dev, bool lock)
spivdbg("selected=%d\n", selected);
if (selected)
{
- spivdbg("cs=%d\n", priv->cs);
+ spivdbg("cs=%d\n", spics->cs);
/* Before writing the TDR, the PCS field in the SPI_MR register must be set
* in order to select a slave.
*/
- regval = getreg32(SAM_SPI0_MR);
+ regval = spi_getreg(spi, SAM_SPI_MR_OFFSET);
regval &= ~SPI_MR_PCS_MASK;
- regval |= (spi_cs2pcs(priv) << SPI_MR_PCS_SHIFT);
- putreg32(regval, SAM_SPI0_MR);
+ regval |= (spi_cs2pcs(spics) << SPI_MR_PCS_SHIFT);
+ spi_putreg(spi, regval, SAM_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.
- * This sam_spiselect method still needs to be provided, but it may
- * be only a stub.
+ * 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
- * GPIO outputs. In that case, the automatic control of the CS pins is
+ * 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 GPIO output pin does *not* have to be the
+ * 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.
*/
- sam_spiselect(devid, selected);
+ spi->select(devid, selected);
}
/****************************************************************************
@@ -432,26 +988,27 @@ static int spi_lock(FAR struct spi_dev_s *dev, bool lock)
*
****************************************************************************/
-static uint32_t spi_setfrequency(FAR struct spi_dev_s *dev, uint32_t frequency)
+static uint32_t spi_setfrequency(struct spi_dev_s *dev, uint32_t frequency)
{
- FAR struct sam_spidev_s *priv = (FAR struct sam_spidev_s *)dev;
+ struct sam_spics_s *spics = (struct sam_spics_s *)dev;
+ struct sam_spidev_s *spi = spi_device(spics);
uint32_t actual;
uint32_t scbr;
uint32_t dlybs;
uint32_t dlybct;
uint32_t regval;
- uint32_t regaddr;
+ unsigned int offset;
- spivdbg("cs=%d frequency=%d\n", priv->cs, frequency);
+ spivdbg("cs=%d frequency=%d\n", spics->cs, frequency);
/* Check if the requested frequency is the same as the frequency selection */
#ifndef CONFIG_SPI_OWNBUS
- if (priv->frequency == frequency)
+ if (spics->frequency == frequency)
{
/* We are already at this frequency. Return the actual. */
- return priv->actual;
+ return spics->actual;
}
#endif
@@ -475,8 +1032,8 @@ static uint32_t spi_setfrequency(FAR struct spi_dev_s *dev, uint32_t frequency)
/* Save the new scbr value */
- regaddr = g_csraddr[priv->cs];
- regval = getreg32(regaddr);
+ offset = (unsigned int)g_csroffset[spics->cs];
+ regval = spi_getreg(spi, offset);
regval &= ~(SPI_CSR_SCBR_MASK | SPI_CSR_DLYBS_MASK | SPI_CSR_DLYBCT_MASK);
regval |= scbr << SPI_CSR_SCBR_SHIFT;
@@ -509,18 +1066,18 @@ static uint32_t spi_setfrequency(FAR struct spi_dev_s *dev, uint32_t frequency)
dlybct = SAM_SPI_CLOCK / 200000 / 32;
regval |= dlybct << SPI_CSR_DLYBCT_SHIFT;
- putreg32(regval, regaddr);
+ spi_putreg(spi, regval, offset);
/* Calculate the new actual frequency */
actual = SAM_SPI_CLOCK / scbr;
- spivdbg("csr[%08x]=%08x actual=%d\n", regaddr, regval, actual);
+ spivdbg("csr[offset=%02x]=%08x actual=%d\n", offset, regval, actual);
/* Save the frequency setting */
#ifndef CONFIG_SPI_OWNBUS
- priv->frequency = frequency;
- priv->actual = actual;
+ spics->frequency = frequency;
+ spics->actual = actual;
#endif
spidbg("Frequency %d->%d\n", frequency, actual);
@@ -542,18 +1099,19 @@ static uint32_t spi_setfrequency(FAR struct spi_dev_s *dev, uint32_t frequency)
*
****************************************************************************/
-static void spi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode)
+static void spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode)
{
- FAR struct sam_spidev_s *priv = (FAR struct sam_spidev_s *)dev;
+ struct sam_spics_s *spics = (struct sam_spics_s *)dev;
+ struct sam_spidev_s *spi = spi_device(spics);
uint32_t regval;
- uint32_t regaddr;
+ unsigned int offset;
- spivdbg("cs=%d mode=%d\n", priv->cs, mode);
+ spivdbg("cs=%d mode=%d\n", spics->cs, mode);
/* Has the mode changed? */
#ifndef CONFIG_SPI_OWNBUS
- if (mode != priv->mode)
+ if (mode != spics->mode)
{
#endif
/* Yes... Set the mode appropriately:
@@ -566,8 +1124,8 @@ static void spi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode)
* 3 1 0
*/
- regaddr = g_csraddr[priv->cs];
- regval = getreg32(regaddr);
+ offset = (unsigned int)g_csroffset[spics->cs];
+ regval = spi_getreg(spi, offset);
regval &= ~(SPI_CSR_CPOL | SPI_CSR_NCPHA);
switch (mode)
@@ -592,13 +1150,13 @@ static void spi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode)
return;
}
- putreg32(regval, regaddr);
- spivdbg("csr[%08x]=%08x\n", regaddr, regval);
+ spi_putreg(spi, regval, offset);
+ spivdbg("csr[offset=%02x]=%08x\n", offset, regval);
/* Save the mode so that subsequent re-configurations will be faster */
#ifndef CONFIG_SPI_OWNBUS
- priv->mode = mode;
+ spics->mode = mode;
}
#endif
}
@@ -618,14 +1176,15 @@ static void spi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode)
*
****************************************************************************/
-static void spi_setbits(FAR struct spi_dev_s *dev, int nbits)
+static void spi_setbits(struct spi_dev_s *dev, int nbits)
{
- FAR struct sam_spidev_s *priv = (FAR struct sam_spidev_s *)dev;
- uint32_t regaddr;
+ struct sam_spics_s *spics = (struct sam_spics_s *)dev;
+ struct sam_spidev_s *spi = spi_device(spics);
uint32_t regval;
+ unsigned int offset;
- spivdbg("cs=%d nbits=%d\n", priv->cs, nbits);
- DEBUGASSERT(priv && nbits > 7 && nbits < 17);
+ spivdbg("cs=%d nbits=%d\n", spics->cs, nbits);
+ DEBUGASSERT(spics && nbits > 7 && nbits < 17);
/* NOTE: The logic in spi_send and in spi_exchange only handles 8-bit
* data at the present time. So the following extra assertion is a
@@ -637,23 +1196,23 @@ static void spi_setbits(FAR struct spi_dev_s *dev, int nbits)
/* Has the number of bits changed? */
#ifndef CONFIG_SPI_OWNBUS
- if (nbits != priv->nbits)
+ if (nbits != spics->nbits)
{
#endif
/* Yes... Set number of bits appropriately */
- regaddr = g_csraddr[priv->cs];
- regval = getreg32(regaddr);
+ offset = (unsigned int)g_csroffset[spics->cs];
+ regval = spi_getreg(spi, offset);
regval &= ~SPI_CSR_BITS_MASK;
regval |= SPI_CSR_BITS(nbits);
- putreg32(regval, regaddr);
+ spi_putreg(spi, regval, offset);
- spivdbg("csr[%08x]=%08x\n", regaddr, regval);
+ spivdbg("csr[offset=%02x]=%08x\n", offset, regval);
/* Save the selection so the subsequence re-configurations will be faster */
#ifndef CONFIG_SPI_OWNBUS
- priv->nbits = nbits;
+ spics->nbits = nbits;
}
#endif
}
@@ -674,7 +1233,7 @@ static void spi_setbits(FAR struct spi_dev_s *dev, int nbits)
*
****************************************************************************/
-static uint16_t spi_send(FAR struct spi_dev_s *dev, uint16_t wd)
+static uint16_t spi_send(struct spi_dev_s *dev, uint16_t wd)
{
uint8_t txbyte;
uint8_t rxbyte;
@@ -685,6 +1244,7 @@ static uint16_t spi_send(FAR struct spi_dev_s *dev, uint16_t wd)
*/
txbyte = (uint8_t)wd;
+ rxbyte = (uint8_t)0;
spi_exchange(dev, &txbyte, &rxbyte, 1);
spivdbg("Sent %02x received %02x\n", txbyte, rxbyte);
@@ -692,10 +1252,16 @@ static uint16_t spi_send(FAR struct spi_dev_s *dev, uint16_t wd)
}
/****************************************************************************
- * Name: spi_exchange
+ * Name: spi_exchange (and spi_exchange_nodma)
*
* Description:
- * Exahange a block of data from SPI. Required.
+ * Exchange a block of data from SPI. There are two versions of this
+ * function: (1) One that is enabled only when CONFIG_SAM34_SPI_DMA=y
+ * that performs DMA SPI transfers, but only when a larger block of
+ * data is being transferred. And (2) another version that does polled
+ * SPI transfers. When CONFIG_SAM34_SPI_DMA=n the latter is the only
+ * version avaialable; when CONFIG_SAM34_SPI_DMA=y, this version is only
+ * used for short SPI transfers and gets renamed as spi_exchange_nodma).
*
* Input Parameters:
* dev - Device-specific state data
@@ -712,13 +1278,18 @@ static uint16_t spi_send(FAR struct spi_dev_s *dev, uint16_t wd)
*
****************************************************************************/
-static void spi_exchange(FAR struct spi_dev_s *dev,
- FAR const void *txbuffer, FAR void *rxbuffer,
- size_t nwords)
+#ifdef CONFIG_SAM34_SPI_DMA
+static void spi_exchange_nodma(struct spi_dev_s *dev, const void *txbuffer,
+ void *rxbuffer, size_t nwords)
+#else
+static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer,
+ void *rxbuffer, size_t nwords)
+#endif
{
- FAR struct sam_spidev_s *priv = (FAR struct sam_spidev_s *)dev;
- FAR uint8_t *rxptr = (FAR uint8_t*)rxbuffer;
- FAR uint8_t *txptr = (FAR uint8_t*)txbuffer;
+ struct sam_spics_s *spics = (struct sam_spics_s *)dev;
+ struct sam_spidev_s *spi = spi_device(spics);
+ uint8_t *rxptr = (uint8_t*)rxbuffer;
+ uint8_t *txptr = (uint8_t*)txbuffer;
uint32_t pcs;
uint32_t data;
@@ -726,11 +1297,11 @@ static void spi_exchange(FAR struct spi_dev_s *dev,
/* Set up PCS bits */
- pcs = spi_cs2pcs(priv) << SPI_TDR_PCS_SHIFT;
+ pcs = spi_cs2pcs(spics) << SPI_TDR_PCS_SHIFT;
/* Make sure that any previous transfer is flushed from the hardware */
- spi_flush();
+ spi_flush(spi);
/* Loop, sending each word in the user-provied data buffer.
*
@@ -793,24 +1364,24 @@ static void spi_exchange(FAR struct spi_dev_s *dev,
* to the serializer.
*/
- while ((getreg32(SAM_SPI0_SR) & SPI_INT_TDRE) == 0);
+ while ((spi_getreg(spi, SAM_SPI_SR_OFFSET) & SPI_INT_TDRE) == 0);
/* Write the data to transmitted to the Transmit Data Register (TDR) */
- putreg32(data, SAM_SPI0_TDR);
+ spi_putreg(spi, data, SAM_SPI_TDR_OFFSET);
/* Wait for the read data to be available in the RDR.
* TODO: Data transfer rates would be improved using the RX FIFO
* (and also DMA)
*/
- while ((getreg32(SAM_SPI0_SR) & SPI_INT_RDRF) == 0);
+ while ((spi_getreg(spi, SAM_SPI_SR_OFFSET) & SPI_INT_RDRF) == 0);
/* Read the received data from the SPI Data Register..
* TODO: The following only works if nbits <= 8.
*/
- data = getreg32(SAM_SPI0_RDR);
+ data = spi_getreg(spi, SAM_SPI_RDR_OFFSET);
if (rxptr)
{
*rxptr++ = (uint8_t)data;
@@ -818,6 +1389,231 @@ static void spi_exchange(FAR struct spi_dev_s *dev,
}
}
+#ifdef CONFIG_SAM34_SPI_DMA
+static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer,
+ void *rxbuffer, size_t nwords)
+{
+ struct sam_spics_s *spics = (struct sam_spics_s *)dev;
+ struct sam_spidev_s *spi = spi_device(spics);
+ uint32_t rxflags;
+ uint32_t txflags;
+ uint32_t txdummy;
+ uint32_t rxdummy;
+ uint32_t paddr;
+ uint32_t maddr;
+ int ret;
+
+ /* If we cannot do DMA -OR- if this is a small SPI transfer, then let
+ * spi_exchange_nodma() do the work.
+ */
+
+ if (!spics->candma || nwords <= CONFIG_SAM34_SPI_DMATHRESHOLD)
+ {
+ spi_exchange_nodma(dev, txbuffer, rxbuffer, nwords);
+ return;
+ }
+
+ spivdbg("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords);
+
+ spics = (struct sam_spics_s *)dev;
+ spi = spi_device(spics);
+ DEBUGASSERT(spics && spi);
+
+ /* Make sure that any previous transfer is flushed from the hardware */
+
+ spi_flush(spi);
+
+ /* Sample initial DMA registers */
+
+ spi_dma_sampleinit(spics);
+
+ /* Configure the DMA channels. There are four different cases:
+ *
+ * 1) A true exchange with the memory address incrementing on both
+ * RX and TX channels,
+ * 2) A read operation with the memory address incrementing only on
+ * the receive channel,
+ * 3) A write operation where the memory address increments only on
+ * the receive channel, and
+ * 4) A corner case where there the memory address does not increment
+ * on either channel. This case might be used in certain cases
+ * where you want to assure that certain number of clocks are
+ * provided on the SPI bus.
+ */
+
+ rxflags = DMACH_FLAG_FIFOCFG_LARGEST |
+ ((uint32_t)spi->pid << DMACH_FLAG_PERIPHPID_SHIFT) |
+ DMACH_FLAG_PERIPHH2SEL | DMACH_FLAG_PERIPHISPERIPH |
+ DMACH_FLAG_PERIPHAHB_AHB_IF2 | DMACH_FLAG_PERIPHWIDTH_8BITS |
+ DMACH_FLAG_PERIPHCHUNKSIZE_1 |
+ ((uint32_t)(0x3f) << DMACH_FLAG_MEMPID_SHIFT) |
+ DMACH_FLAG_MEMAHB_AHB_IF0 | DMACH_FLAG_MEMWIDTH_8BITS |
+ DMACH_FLAG_MEMCHUNKSIZE_1;
+
+ if (!rxbuffer)
+ {
+ /* No sink data buffer. Point to our dummy buffer and leave
+ * the rxflags so that no address increment is performed.
+ */
+
+ rxbuffer = (void *)&rxdummy;
+ }
+ else
+ {
+ /* A receive buffer is available. Use normal TX memory incrementing. */
+
+ rxflags |= DMACH_FLAG_MEMINCREMENT;
+ }
+
+ txflags = DMACH_FLAG_FIFOCFG_LARGEST |
+ ((uint32_t)spi->pid << DMACH_FLAG_PERIPHPID_SHIFT) |
+ DMACH_FLAG_PERIPHH2SEL | DMACH_FLAG_PERIPHISPERIPH |
+ DMACH_FLAG_PERIPHAHB_AHB_IF2 | DMACH_FLAG_PERIPHWIDTH_8BITS |
+ DMACH_FLAG_PERIPHCHUNKSIZE_1 |
+ ((uint32_t)(0x3f) << DMACH_FLAG_MEMPID_SHIFT) |
+ DMACH_FLAG_MEMAHB_AHB_IF0 | DMACH_FLAG_MEMWIDTH_8BITS |
+ DMACH_FLAG_MEMCHUNKSIZE_1;
+
+ if (!txbuffer)
+ {
+ /* No source data buffer. Point to our dummy buffer and configure
+ * the txflags so that no address increment is performed.
+ */
+
+ txdummy = 0xffffffff;
+ txbuffer = (const void *)&txdummy;
+ }
+ else
+ {
+ /* Source data is available. Use normal TX memory incrementing. */
+
+ txflags |= DMACH_FLAG_MEMINCREMENT;
+ }
+
+ /* Then configure the DMA channels to make it so */
+
+ sam_dmaconfig(spics->rxdma, rxflags);
+ sam_dmaconfig(spics->txdma, txflags);
+
+ /* Configure the exchange transfers */
+
+ paddr = spi_physregaddr(spics, SAM_SPI_RDR_OFFSET);
+ maddr = sam_physramaddr((uintptr_t)rxbuffer);
+
+ ret = sam_dmarxsetup(spics->rxdma, paddr, maddr, nwords);
+ if (ret < 0)
+ {
+ dmadbg("ERROR: sam_dmarxsetup failed: %d\n", ret);
+ return;
+ }
+
+ spi_rxdma_sample(spics, DMA_AFTER_SETUP);
+
+ paddr = spi_physregaddr(spics, SAM_SPI_TDR_OFFSET);
+ maddr = sam_physramaddr((uintptr_t)txbuffer);
+
+ ret = sam_dmatxsetup(spics->txdma, paddr, maddr, nwords);
+ if (ret < 0)
+ {
+ dmadbg("ERROR: sam_dmatxsetup failed: %d\n", ret);
+ return;
+ }
+
+ spi_txdma_sample(spics, DMA_AFTER_SETUP);
+
+ /* Start the DMA transfer */
+
+ spics->result = -EBUSY;
+ ret = sam_dmastart(spics->rxdma, spi_rxcallback, (void *)spics);
+ if (ret < 0)
+ {
+ dmadbg("ERROR: RX sam_dmastart failed: %d\n", ret);
+ return;
+ }
+
+ spi_rxdma_sample(spics, DMA_AFTER_START);
+
+ ret = sam_dmastart(spics->txdma, spi_txcallback, (void *)spics);
+ if (ret < 0)
+ {
+ dmadbg("ERROR: RX sam_dmastart failed: %d\n", ret);
+ sam_dmastop(spics->rxdma);
+ return;
+ }
+
+ spi_txdma_sample(spics, DMA_AFTER_START);
+
+ /* Wait for DMA completion. This is done in a loop becaue there my be
+ * false alarm semaphore counts that cause sam_wait() not fail to wait
+ * or to wake-up prematurely (for example due to the receipt of a signal).
+ * We know that the DMA has completed when the result is anything other
+ * that -EBUSY.
+ */
+
+ do
+ {
+ /* Start (or re-start) the watchdog timeout */
+
+ ret = wd_start(spics->dmadog, DMA_TIMEOUT_TICKS,
+ (wdentry_t)spi_dmatimeout, 1, (uint32_t)spics);
+ if (ret != OK)
+ {
+ spidbg("ERROR: wd_start failed: %d\n", ret);
+ }
+
+ /* Wait for the DMA complete */
+
+ ret = sem_wait(&spics->dmawait);
+
+ /* Cancel the watchdog timeout */
+
+ (void)wd_cancel(spics->dmadog);
+
+ /* Check if we were awakened by an error of some kind */
+
+ if (ret < 0)
+ {
+ /* EINTR is not a failure. That simply means that the wait
+ * was awakened by a signel.
+ */
+
+ int errorcode = errno;
+ if (errorcode != EINTR)
+ {
+ DEBUGPANIC();
+ return;
+ }
+ }
+
+ /* Not that we might be awkened before the wait is over due to
+ * residual counts on the semaphore. So, to handle, that case,
+ * we loop until somthing changes the DMA result to any value other
+ * than -EBUSY.
+ */
+ }
+ while (spics->result == -EBUSY);
+
+ /* Dump the sampled DMA registers */
+
+ spi_dma_sampledone(spics);
+
+ /* Make sure that the DMA is stopped (it will be stopped automatically
+ * on normal transfers, but not necessarily when the transfer terminates
+ * on an error condition).
+ */
+
+ sam_dmastop(spics->rxdma);
+ sam_dmastop(spics->txdma);
+
+ /* All we can do is complain if the DMA fails */
+
+ if (spics->result)
+ {
+ spidbg("ERROR: DMA failed with result: %d\n", spics->result);
+ }
+}
+#endif /* CONFIG_SAM34_SPI_DMA */
+
/***************************************************************************
* Name: spi_sndblock
*
@@ -838,7 +1634,7 @@ static void spi_exchange(FAR struct spi_dev_s *dev,
****************************************************************************/
#ifndef CONFIG_SPI_EXCHANGE
-static void spi_sndblock(FAR struct spi_dev_s *dev, FAR const void *buffer, size_t nwords)
+static void spi_sndblock(struct spi_dev_s *dev, const void *buffer, size_t nwords)
{
/* spi_exchange can do this. */
@@ -866,7 +1662,7 @@ static void spi_sndblock(FAR struct spi_dev_s *dev, FAR const void *buffer, size
****************************************************************************/
#ifndef CONFIG_SPI_EXCHANGE
-static void spi_recvblock(FAR struct spi_dev_s *dev, FAR void *buffer, size_t nwords)
+static void spi_recvblock(struct spi_dev_s *dev, void *buffer, size_t nwords)
{
/* spi_exchange can do this. */
@@ -892,101 +1688,188 @@ static void spi_recvblock(FAR struct spi_dev_s *dev, FAR void *buffer, size_t nw
*
****************************************************************************/
-FAR struct spi_dev_s *up_spiinitialize(int cs)
+struct spi_dev_s *up_spiinitialize(int port)
{
- FAR struct sam_spidev_s *priv;
+ struct sam_spidev_s *spi;
+ struct sam_spics_s *spics;
+ int csno = (port & __SPI_CS_MASK) >> __SPI_CS_SHIFT;
+ int spino = (port & __SPI_SPI_MASK) >> __SPI_SPI_SHIFT;
irqstate_t flags;
#ifndef CONFIG_SPI_OWNBUS
- uint32_t regaddr;
uint32_t regval;
+ unsigned int offset;
#endif
/* The support SAM parts have only a single SPI port */
- spivdbg("cs=%d\n", cs);
- DEBUGASSERT(cs >= 0 && cs <= SAM_SPI_NCS);
-
-#ifdef CONFIG_SPI_OWNBUS
- /* There is only one device on the bus and, therefore, there is only one
- * supported chip select. In this case, use the single, pre-allocated
- * chip select structure.
- */
-
- priv = &g_spidev;
+ spivdbg("port: %d csno: %d spino: %d\n", port, csno, spino);
+ DEBUGASSERT(csno >= 0 && csno <= SAM_SPI_NCS);
+#if defined(CONFIG_SAM34_SPI0) && defined(CONFIG_SAM34_SPI1)
+ DEBUGASSERT(spino >= 0 && spino <= 1);
+#elif defined(CONFIG_SAM34_SPI0)
+ DEBUGASSERT(spino == 0);
#else
+ DEBUGASSERT(spino == 1);
+#endif
+
/* Allocate a new state structure for this chip select. NOTE that there
* is no protection if the same chip select is used in two different
* chip select structures.
*/
- priv = (FAR struct sam_spidev_s *)zalloc(sizeof(struct sam_spidev_s));
- if (!priv)
+ spics = (struct sam_spics_s *)zalloc(sizeof(struct sam_spics_s));
+ if (!spics)
{
- spivdbg("ERROR: Failed to allocate a chip select structure\n", cs);
+ spidbg("ERROR: Failed to allocate a chip select structure\n");
return NULL;
}
-#endif
/* Set up the initial state for this chip select structure. Other fields
* were zeroed by zalloc().
*/
- priv->spidev.ops = &g_spiops;
- priv->cs = cs;
+#ifdef CONFIG_SAM34_SPI_DMA
+ /* Can we do DMA on this peripheral? */
-#ifndef CONFIG_SPI_OWNBUS
- /* Has the SPI hardware been initialized? */
+ spics->candma = spino ? SAM34_SPI1_DMA : SAM34_SPI0_DMA;
+
+ /* Pre-allocate DMA channels. These allocations exploit that fact that
+ * SPI0 is managed by DMAC0 and SPI1 is managed by DMAC1. Hence,
+ * the SPI number (spino) is the same as the DMAC number.
+ */
+
+ if (spics->candma)
+ {
+ spics->rxdma = sam_dmachannel(spino, 0);
+ if (!spics->rxdma)
+ {
+ spidbg("ERROR: Failed to allocate the RX DMA channel\n");
+ spics->candma = false;
+ }
+ }
+
+ if (spics->candma)
+ {
+ spics->txdma = sam_dmachannel(spino, 0);
+ if (!spics->txdma)
+ {
+ spidbg("ERROR: Failed to allocate the TX DMA channel\n");
+ sam_dmafree(spics->rxdma);
+ spics->rxdma = NULL;
+ spics->candma = false;
+ }
+ }
+#endif
+
+ /* Select the SPI operations */
+
+#if defined(CONFIG_SAM34_SPI0) && defined(CONFIG_SAM34_SPI1)
+ spics->spidev.ops = spino ? &g_spi1ops : &g_spi0ops;
+#elif defined(CONFIG_SAM34_SPI0)
+ spics->spidev.ops = &g_spi0ops;
+#else
+ spics->spidev.ops = &g_spi1ops;
+#endif
+
+ /* Save the chip select and SPI controller numbers */
- if (!g_spinitialized)
+ spics->cs = csno;
+#if defined(CONFIG_SAM34_SPI0) || defined(CONFIG_SAM34_SPI1)
+ spics->spino = spino;
#endif
+
+ /* Get the SPI device structure associated with the chip select */
+
+ spi = spi_device(spics);
+
+ /* Has the SPI hardware been initialized? */
+
+ if (!spi->initialized)
{
/* Enable clocking to the SPI block */
flags = irqsave();
- sam_spi0_enableclk();
+#if defined(CONFIG_SAM34_SPI0) && defined(CONFIG_SAM34_SPI1)
+ if (spino == 0)
+#endif
+#if defined(CONFIG_SAM34_SPI0)
+ {
+ sam_spi0_enableclk();
- /* Configure multiplexed pins as connected on the board. Chip select
- * pins must be configured by board-specific logic.
- */
+ /* Configure multiplexed pins as connected on the board. Chip
+ * select pins must be selected by board-specific logic.
+ */
- sam_configgpio(GPIO_SPI0_MISO);
- sam_configgpio(GPIO_SPI0_MOSI);
- sam_configgpio(GPIO_SPI0_SPCK);
+ sam_configgpio(GPIO_SPI0_MISO);
+ sam_configgpio(GPIO_SPI0_MOSI);
+ sam_configgpio(GPIO_SPI0_SPCK);
+ }
+#endif
+#if defined(CONFIG_SAM34_SPI0) && defined(CONFIG_SAM34_SPI1)
+ else
+#endif
+#if defined(CONFIG_SAM34_SPI1)
+ {
+ sam_spi1_enableclk();
+
+ /* Configure multiplexed pins as connected on the board. Chip
+ * select pins must be selected by board-specific logic.
+ */
+
+ sam_configgpio(GPIO_SPI1_MISO);
+ sam_configgpio(GPIO_SPI1_MOSI);
+ sam_configgpio(GPIO_SPI1_SPCK);
+ }
+#endif
/* Disable SPI clocking */
- putreg32(SPI_CR_SPIDIS, SAM_SPI0_CR);
+ spi_putreg(spi, SPI_CR_SPIDIS, SAM_SPI_CR_OFFSET);
/* Execute a software reset of the SPI (twice) */
- putreg32(SPI_CR_SWRST, SAM_SPI0_CR);
- putreg32(SPI_CR_SWRST, SAM_SPI0_CR);
+ spi_putreg(spi, SPI_CR_SWRST, SAM_SPI_CR_OFFSET);
+ spi_putreg(spi, SPI_CR_SWRST, SAM_SPI_CR_OFFSET);
irqrestore(flags);
/* Configure the SPI mode register */
- putreg32(SPI_MR_MSTR | SPI_MR_MODFDIS, SAM_SPI0_MR);
+ spi_putreg(spi, SPI_MR_MSTR | SPI_MR_MODFDIS, SAM_SPI_MR_OFFSET);
/* And enable the SPI */
- putreg32(SPI_CR_SPIEN, SAM_SPI0_CR);
+ spi_putreg(spi, SPI_CR_SPIEN, SAM_SPI_CR_OFFSET);
up_mdelay(20);
/* Flush any pending transfers */
- (void)getreg32(SAM_SPI0_SR);
- (void)getreg32(SAM_SPI0_RDR);
+ (void)spi_getreg(spi, SAM_SPI_SR_OFFSET);
+ (void)spi_getreg(spi, SAM_SPI_RDR_OFFSET);
#ifndef CONFIG_SPI_OWNBUS
/* Initialize the SPI semaphore that enforces mutually exclusive
* access to the SPI registers.
*/
- sem_init(&g_spisem, 0, 1);
- g_spinitialized = true;
+ sem_init(&spi->spisem, 0, 1);
+ spi->initialized = true;
#endif
- spi_dumpregs("After initialization");
+
+#ifdef CONFIG_SAM34_SPI_DMA
+ /* Initialize the SPI semaphore that is used to wake up the waiting
+ * thread when the DMA transfer completes.
+ */
+
+ sem_init(&spics->dmawait, 0, 0);
+
+ /* Create a watchdog time to catch DMA timeouts */
+
+ spics->dmadog = wd_create();
+ DEBUGASSERT(spics->dmadog);
+#endif
+
+ spi_dumpregs(spi, "After initialization");
}
#ifndef CONFIG_SPI_OWNBUS
@@ -995,16 +1878,16 @@ FAR struct spi_dev_s *up_spiinitialize(int cs)
* that case, the SPI will only be reconfigured if there is a change.
*/
- regaddr = g_csraddr[cs];
- regval = getreg32(regaddr);
+ offset = (unsigned int)g_csroffset[csno];
+ regval = spi_getreg(spi, offset);
regval &= ~(SPI_CSR_CPOL | SPI_CSR_NCPHA | SPI_CSR_BITS_MASK);
regval |= (SPI_CSR_NCPHA | SPI_CSR_BITS(8));
- putreg32(regval, regaddr);
+ spi_putreg(spi, regval, offset);
- priv->nbits = 8;
- spivdbg("csr[%08x]=%08x\n", regaddr, regval);
+ spics->nbits = 8;
+ spivdbg("csr[offset=%02x]=%08x\n", offset, regval);
#endif
- return &priv->spidev;
+ return &spics->spidev;
}
-#endif /* CONFIG_SAM34_SPI0 */
+#endif /* CONFIG_SAM34_SPI0 || CONFIG_SAM34_SPI1 */
diff --git a/nuttx/arch/arm/src/sam34/sam_spi.h b/nuttx/arch/arm/src/sam34/sam_spi.h
index 0c0d0d41d..45f4e0408 100644
--- a/nuttx/arch/arm/src/sam34/sam_spi.h
+++ b/nuttx/arch/arm/src/sam34/sam_spi.h
@@ -1,7 +1,7 @@
-/************************************************************************************
+/****************************************************************************
* arch/arm/src/sam34/sam_spi.h
*
- * Copyright (C) 2009-2011, 2013 Gregory Nutt. All rights reserved.
+ * Copyright (C) 2009-2011, 2013-2014 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@@ -31,14 +31,14 @@
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
- ************************************************************************************/
+ ****************************************************************************/
#ifndef __ARCH_ARM_SRC_SAM34_SAM_SPI_H
#define __ARCH_ARM_SRC_SAM34_SAM_SPI_H
-/************************************************************************************
+/****************************************************************************
* Included Files
- ************************************************************************************/
+ ****************************************************************************/
#include <nuttx/config.h>
@@ -47,23 +47,53 @@
#include "chip.h"
-/************************************************************************************
- * Definitions
- ************************************************************************************/
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* The SPI port number used as an input to up_spiinitialize encodes
+ * information about the SPI controller (0 or 1) and the SPI chip select
+ * (0-3).
+ *
+ * NOTE that this is this is backward compatible with older implementations
+ * that support only SPI0 and provide only the chip select number to
+ * up_spiinitialize().
+ */
+
+#define __SPI_CS_SHIFT (0) /* Bits 0-1: SPI chip select number */
+#define __SPI_CS_MASK (3 << __SPI_CS_SHIFT)
+# define __SPI_CS0 (0 << __SPI_CS_SHIFT)
+# define __SPI_CS1 (1 << __SPI_CS_SHIFT)
+# define __SPI_CS2 (2 << __SPI_CS_SHIFT)
+# define __SPI_CS3 (3 << __SPI_CS_SHIFT)
+#define __SPI_SPI_SHIFT (2) /* Bit 2: SPI controller number */
+#define __SPI_SPI_MASK (1 << __SPI_SPI_SHIFT)
+# define __SPI_SPI0 (0 << __SPI_SPI_SHIFT) /* SPI0 */
+# define __SPI_SPI1 (1 << __SPI_SPI_SHIFT) /* SPI1 */
+
+#define SPI0_CS0 (__SPI_SPI0 | __SPI_CS0)
+#define SPI0_CS1 (__SPI_SPI0 | __SPI_CS1)
+#define SPI0_CS2 (__SPI_SPI0 | __SPI_CS2)
+#define SPI0_CS3 (__SPI_SPI0 | __SPI_CS3)
+
+#define SPI1_CS0 (__SPI_SPI1 | __SPI_CS0)
+#define SPI1_CS1 (__SPI_SPI1 | __SPI_CS1)
+#define SPI1_CS2 (__SPI_SPI1 | __SPI_CS2)
+#define SPI1_CS3 (__SPI_SPI1 | __SPI_CS3)
-/************************************************************************************
+/****************************************************************************
* Public Types
- ************************************************************************************/
+ ****************************************************************************/
-/************************************************************************************
+/****************************************************************************
* Inline Functions
- ************************************************************************************/
+ ****************************************************************************/
#ifndef __ASSEMBLY__
-/************************************************************************************
+/****************************************************************************
* Public Data
- ************************************************************************************/
+ ****************************************************************************/
#undef EXTERN
#if defined(__cplusplus)
@@ -74,33 +104,34 @@ extern "C"
#define EXTERN extern
#endif
-/************************************************************************************
+/****************************************************************************
* Public Function Prototypes
- ************************************************************************************/
+ ****************************************************************************/
/****************************************************************************
- * Name: sam_spiselect, sam_spistatus, and sam_spicmddata
+ * Name: sam_spi[0|1]select, sam_spi[0|1]status, and sam_spi[0|1]cmddata
*
* Description:
- * These external functions must be provided by board-specific logic. They
- * include:
- *
- * o sam_spiselect is a functions tomanage the board-specific chip selects
- * o sam_spistatus and sam_spicmddata: Implementations of the status
- * and cmddata methods of the SPI interface defined by struct spi_ops_
- * (see include/nuttx/spi/spi.h). All other methods including
+ * These external functions must be provided by board-specific logic.
+ * They include:
+ *
+ * o sam_spi[0|1]select is a functions tomanage the board-specific chip
+ * selects
+ * o sam_spi[0|1]status and sam_spi[0|1]cmddata: Implementations of the
+ * status and cmddata methods of the SPI interface defined by struct
+ * spi_ops_ (see include/nuttx/spi/spi.h). All other methods including
* up_spiinitialize()) are provided by common SAM3/4 logic.
*
* To use this common SPI logic on your board:
*
* 1. Provide logic in sam_boardinitialize() to configure SPI chip select
* pins.
- * 2. Provide sam_spiselect() and sam_spistatus() functions in your board-
- * specific logic. These functions will perform chip selection and
- * status operations using GPIOs in the way your board is configured.
+ * 2. Provide sam_spi[0|1]select() and sam_spi[0|1]status() functions in
+ * our board-specific logic. These functions will perform chip selection
+ * and status operations using PIOs in the way your board is configured.
* 2. If CONFIG_SPI_CMDDATA is defined in the NuttX configuration, provide
- * sam_spicmddata() functions in your board-specific logic. This
- * function will perform cmd/data selection operations using GPIOs in
+ * sam_spi[0|1]cmddata() functions in your board-specific logic. This
+ * function will perform cmd/data selection operations using PIOs in
* the way your board is configured.
* 3. Add a call to up_spiinitialize() in your low level application
* initialization logic
@@ -116,7 +147,7 @@ struct spi_dev_s;
enum spi_dev_e;
/****************************************************************************
- * Name: sam_spiselect
+ * Name: sam_spi[0|1]select
*
* Description:
* PIO chip select pins may be programmed by the board specific logic in
@@ -126,9 +157,9 @@ enum spi_dev_e;
* a stub.
*
* An alternative way to program the PIO chip select pins is as a normal
- * GPIO output. In that case, the automatic control of the CS pins is
+ * PIO output. 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 GPIO output pin does *not* have to be the
+ * 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.
*
* Input Parameters:
@@ -141,10 +172,15 @@ enum spi_dev_e;
*
****************************************************************************/
-void sam_spiselect(enum spi_dev_e devid, bool selected);
+#ifdef CONFIG_SAM34_SPI0
+void sam_spi0select(enum spi_dev_e devid, bool selected);
+#endif
+#ifdef CONFIG_SAM34_SPI1
+void sam_spi1select(enum spi_dev_e devid, bool selected);
+#endif
/****************************************************************************
- * Name: sam_spistatus
+ * Name: sam_spi[0|1]status
*
* Description:
* Return status information associated with the SPI device.
@@ -158,10 +194,15 @@ void sam_spiselect(enum spi_dev_e devid, bool selected);
*
****************************************************************************/
-uint8_t sam_spistatus(FAR struct spi_dev_s *dev, enum spi_dev_e devid);
+#ifdef CONFIG_SAM34_SPI0
+uint8_t sam_spi0status(FAR struct spi_dev_s *dev, enum spi_dev_e devid);
+#endif
+#ifdef CONFIG_SAM34_SPI1
+uint8_t sam_spi1status(FAR struct spi_dev_s *dev, enum spi_dev_e devid);
+#endif
/****************************************************************************
- * Name: sam_spicmddata
+ * Name: sam_spi[0|1]cmddata
*
* Description:
* Some SPI devices require an additional control to determine if the SPI
@@ -172,7 +213,7 @@ uint8_t sam_spistatus(FAR struct spi_dev_s *dev, enum spi_dev_e devid);
* may be configured to use 9-bit data transfers with the 9th bit
* indicating command or data. That same hardware may be configurable,
* instead, to use 8-bit data but to require an additional, board-
- * specific GPIO control to distinguish command and data. This function
+ * specific PIO control to distinguish command and data. This function
* would be needed in that latter case.
*
* Input Parameters:
@@ -185,7 +226,12 @@ uint8_t sam_spistatus(FAR struct spi_dev_s *dev, enum spi_dev_e devid);
****************************************************************************/
#ifdef CONFIG_SPI_CMDDATA
-int sam_spicmddata(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool cmd);
+#ifdef CONFIG_SAM34_SPI0
+int sam_spi0cmddata(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool cmd);
+#endif
+#ifdef CONFIG_SAM34_SPI1
+int sam_spi1cmddata(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool cmd);
+#endif
#endif
#endif /* CONFIG_SAM34_SPI0 */
diff --git a/nuttx/arch/arm/src/sama5/Kconfig b/nuttx/arch/arm/src/sama5/Kconfig
index 7ac338eca..9a48ecadd 100644
--- a/nuttx/arch/arm/src/sama5/Kconfig
+++ b/nuttx/arch/arm/src/sama5/Kconfig
@@ -1310,7 +1310,7 @@ config SAMA5_CAN_REGDEBUG
Output detailed register-level CAN device debug information.
Requires also DEBUG.
-endmenu # SPI device driver options
+endmenu # CAN device driver options
endif # SAMA5_CAN0 || SAMA5_CAN1
if SAMA5_SPI0 || SAMA5_SPI1
diff --git a/nuttx/arch/arm/src/sama5/sam_spi.c b/nuttx/arch/arm/src/sama5/sam_spi.c
index df32defae..11bde568f 100644
--- a/nuttx/arch/arm/src/sama5/sam_spi.c
+++ b/nuttx/arch/arm/src/sama5/sam_spi.c
@@ -736,6 +736,7 @@ static void spi_dma_sampledone(struct sam_spics_s *spics)
*
****************************************************************************/
+#ifdef CONFIG_SAMA5_SPI_DMA
static void spi_dmatimeout(int argc, uint32_t arg)
{
struct sam_spics_s *spics = (struct sam_spics_s *)arg;
@@ -755,6 +756,7 @@ static void spi_dmatimeout(int argc, uint32_t arg)
sem_post(&spics->dmawait);
}
+#endif
/****************************************************************************
* Name: spi_rxcallback
diff --git a/nuttx/arch/arm/src/sama5/sam_spi.h b/nuttx/arch/arm/src/sama5/sam_spi.h
index bb8c5398a..e17d85fdb 100644
--- a/nuttx/arch/arm/src/sama5/sam_spi.h
+++ b/nuttx/arch/arm/src/sama5/sam_spi.h
@@ -1,4 +1,4 @@
-/************************************************************************************
+/****************************************************************************
* arch/arm/src/sama5/sam_spi.h
*
* Copyright (C) 2013 Gregory Nutt. All rights reserved.
@@ -31,14 +31,14 @@
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
- ************************************************************************************/
+ ****************************************************************************/
#ifndef __ARCH_ARM_SRC_SAMA5_SAM_SPI_H
#define __ARCH_ARM_SRC_SAMA5_SAM_SPI_H
-/************************************************************************************
+/****************************************************************************
* Included Files
- ************************************************************************************/
+ ****************************************************************************/
#include <nuttx/config.h>
@@ -47,9 +47,9 @@
#include "chip.h"
-/************************************************************************************
+/****************************************************************************
* Pre-processor Definitions
- ************************************************************************************/
+ ****************************************************************************/
/* The SPI port number used as an input to up_spiinitialize encodes information
* about the SPI controller (0 or 1) and the SPI chip select (0-3)
@@ -76,19 +76,19 @@
#define SPI1_CS2 (__SPI_SPI1 | __SPI_CS2)
#define SPI1_CS3 (__SPI_SPI1 | __SPI_CS3)
-/************************************************************************************
+/****************************************************************************
* Public Types
- ************************************************************************************/
+ ****************************************************************************/
-/************************************************************************************
+/****************************************************************************
* Inline Functions
- ************************************************************************************/
+ ****************************************************************************/
#ifndef __ASSEMBLY__
-/************************************************************************************
+/****************************************************************************
* Public Data
- ************************************************************************************/
+ ****************************************************************************/
#undef EXTERN
#if defined(__cplusplus)
@@ -99,9 +99,9 @@ extern "C"
#define EXTERN extern
#endif
-/************************************************************************************
+/****************************************************************************
* Public Function Prototypes
- ************************************************************************************/
+ ****************************************************************************/
/****************************************************************************
* Name: sam_spi[0|1]select, sam_spi[0|1]status, and sam_spi[0|1]cmddata
diff --git a/nuttx/configs/sam3u-ek/src/up_spi.c b/nuttx/configs/sam3u-ek/src/up_spi.c
index 20423200d..bdedc91ac 100644
--- a/nuttx/configs/sam3u-ek/src/up_spi.c
+++ b/nuttx/configs/sam3u-ek/src/up_spi.c
@@ -107,14 +107,14 @@ void weak_function sam_spiinitialize(void)
}
/****************************************************************************
- * Name: sam_spiselect, sam_spistatus, and sam_spicmddata
+ * Name: sam_spi0select, sam_spi0status, and sam_spic0mddata
*
* Description:
* These external functions must be provided by board-specific logic. They
* include:
*
- * o sam_spiselect is a functions tomanage the board-specific chip selects
- * o sam_spistatus and sam_spicmddata: Implementations of the status
+ * o sam_spi0select is a functions tomanage the board-specific chip selects
+ * o sam_spi0status and sam_spic0mddata: Implementations of the status
* and cmddata methods of the SPI interface defined by struct spi_ops_
* (see include/nuttx/spi/spi.h). All other methods including
* up_spiinitialize()) are provided by common SAM3/4 logic.
@@ -123,11 +123,11 @@ void weak_function sam_spiinitialize(void)
*
* 1. Provide logic in sam_boardinitialize() to configure SPI chip select
* pins.
- * 2. Provide sam_spiselect() and sam_spistatus() functions in your board-
+ * 2. Provide sam_spi0select() and sam_spi0status() functions in your board-
* specific logic. These functions will perform chip selection and
* status operations using GPIOs in the way your board is configured.
* 2. If CONFIG_SPI_CMDDATA is defined in the NuttX configuration, provide
- * sam_spicmddata() functions in your board-specific logic. This
+ * sam_spic0mddata() functions in your board-specific logic. This
* function will perform cmd/data selection operations using GPIOs in
* the way your board is configured.
* 3. Add a call to up_spiinitialize() in your low level application
@@ -140,7 +140,7 @@ void weak_function sam_spiinitialize(void)
****************************************************************************/
/****************************************************************************
- * Name: sam_spiselect
+ * Name: sam_spi0select
*
* Description:
* PIO chip select pins may be programmed by the board specific logic in
@@ -164,7 +164,7 @@ void weak_function sam_spiinitialize(void)
*
****************************************************************************/
-void sam_spiselect(enum spi_dev_e devid, bool selected)
+void sam_spi0select(enum spi_dev_e devid, bool selected)
{
/* The touchscreen chip select is implemented as a GPIO OUTPUT that must
* be controlled by this function. This is because the ADS7843E driver
@@ -183,7 +183,7 @@ void sam_spiselect(enum spi_dev_e devid, bool selected)
}
/****************************************************************************
- * Name: sam_spistatus
+ * Name: sam_spi0status
*
* Description:
* Return status information associated with the SPI device.
@@ -196,7 +196,7 @@ void sam_spiselect(enum spi_dev_e devid, bool selected)
*
****************************************************************************/
-uint8_t sam_spistatus(FAR struct spi_dev_s *dev, enum spi_dev_e devid)
+uint8_t sam_spi0status(FAR struct spi_dev_s *dev, enum spi_dev_e devid)
{
return 0;
}
diff --git a/nuttx/configs/sam4e-ek/src/Makefile b/nuttx/configs/sam4e-ek/src/Makefile
index 75d9029d3..975269671 100644
--- a/nuttx/configs/sam4e-ek/src/Makefile
+++ b/nuttx/configs/sam4e-ek/src/Makefile
@@ -40,7 +40,7 @@ CFLAGS += -I$(TOPDIR)/sched
ASRCS =
AOBJS = $(ASRCS:.S=$(OBJEXT))
-CSRCS = sam_boot.c sam_leds.c sam_buttons.c sam_spi.c sam_usbdev.c
+CSRCS = sam_boot.c sam_leds.c sam_buttons.c sam_usbdev.c
ifeq ($(CONFIG_HAVE_CXXINITIALIZE),y)
CSRCS += sam_cxxinitialize.c
@@ -58,6 +58,10 @@ ifeq ($(CONFIG_SAM34_HSMCI),y)
CSRCS += sam_mmcsd.c
endif
+ifeq ($(CONFIG_SAM34_SPI0),y)
+CSRCS += sam_spi.c
+endif
+
ifeq ($(CONFIG_USBMSC),y)
CSRCS += sam_usbmsc.c
endif
diff --git a/nuttx/configs/sam4e-ek/src/sam4e-ek.h b/nuttx/configs/sam4e-ek/src/sam4e-ek.h
index f87826829..0e6c29358 100644
--- a/nuttx/configs/sam4e-ek/src/sam4e-ek.h
+++ b/nuttx/configs/sam4e-ek/src/sam4e-ek.h
@@ -268,9 +268,9 @@
* it low throughout the SPI transfer.
*/
-#define GPIO_TSC_NPCS2 (GPIO_OUTPUT | GPIO_CFG_PULLUP | GPIO_OUTPUT_SET | \
- GPIO_PORT_PIOA | GPIO_PIN11)
-#define TSC_CSNUM 0
+#define GPIO_TSC_CS (GPIO_OUTPUT | GPIO_CFG_PULLUP | GPIO_OUTPUT_SET | \
+ GPIO_PORT_PIOA | GPIO_PIN11)
+#define TSC_CSNUM 0
/************************************************************************************
* Public Types
diff --git a/nuttx/configs/sam4e-ek/src/sam_spi.c b/nuttx/configs/sam4e-ek/src/sam_spi.c
index 5338d1ffe..714a643ed 100644
--- a/nuttx/configs/sam4e-ek/src/sam_spi.c
+++ b/nuttx/configs/sam4e-ek/src/sam_spi.c
@@ -53,7 +53,7 @@
#include "sam_spi.h"
#include "sam4e-ek.h"
-#if defined(CONFIG_SAM34_SPI0) || defined(CONFIG_SAM34_SPI1)
+#if defined(CONFIG_SAM34_SPI0)
/************************************************************************************
* Definitions
@@ -99,22 +99,22 @@ void weak_function sam_spiinitialize(void)
* ZigBee support.
*/
- /* The touchscreen connects using NPCS2 (PC14). */
+ /* The touchscreen connects using NPCS0 (PA11). */
#if defined(CONFIG_INPUT) && defined(CONFIG_INPUT_ADS7843E)
- sam_configgpio(GPIO_TSC_NPCS2);
+ sam_configgpio(GPIO_TSC_CS);
#endif
}
/****************************************************************************
- * Name: sam_spiselect, sam_spistatus, and sam_spicmddata
+ * Name: sam_spi0select, sam_spi0status, and sam_spic0mddata
*
* Description:
* These external functions must be provided by board-specific logic. They
* include:
*
- * o sam_spiselect is a functions tomanage the board-specific chip selects
- * o sam_spistatus and sam_spicmddata: Implementations of the status
+ * o sam_spi0select is a functions tomanage the board-specific chip selects
+ * o sam_spi0status and sam_spic0mddata: Implementations of the status
* and cmddata methods of the SPI interface defined by struct spi_ops_
* (see include/nuttx/spi/spi.h). All other methods including
* up_spiinitialize()) are provided by common SAM3/4 logic.
@@ -123,11 +123,11 @@ void weak_function sam_spiinitialize(void)
*
* 1. Provide logic in sam_boardinitialize() to configure SPI chip select
* pins.
- * 2. Provide sam_spiselect() and sam_spistatus() functions in your board-
+ * 2. Provide sam_spi0select() and sam_spi0status() functions in your board-
* specific logic. These functions will perform chip selection and
* status operations using GPIOs in the way your board is configured.
* 2. If CONFIG_SPI_CMDDATA is defined in the NuttX configuration, provide
- * sam_spicmddata() functions in your board-specific logic. This
+ * sam_spic0mddata() functions in your board-specific logic. This
* function will perform cmd/data selection operations using GPIOs in
* the way your board is configured.
* 3. Add a call to up_spiinitialize() in your low level application
@@ -140,7 +140,7 @@ void weak_function sam_spiinitialize(void)
****************************************************************************/
/****************************************************************************
- * Name: sam_spiselect
+ * Name: sam_spi0select
*
* Description:
* PIO chip select pins may be programmed by the board specific logic in
@@ -164,7 +164,7 @@ void weak_function sam_spiinitialize(void)
*
****************************************************************************/
-void sam_spiselect(enum spi_dev_e devid, bool selected)
+void sam_spi0select(enum spi_dev_e devid, bool selected)
{
/* The touchscreen chip select is implemented as a GPIO OUTPUT that must
* be controlled by this function. This is because the ADS7843E driver
@@ -177,13 +177,13 @@ void sam_spiselect(enum spi_dev_e devid, bool selected)
#if defined(CONFIG_INPUT) && defined(CONFIG_INPUT_ADS7843E)
if (devid == SPIDEV_TOUCHSCREEN)
{
- sam_gpiowrite(GPIO_TSC_NPCS2, !selected);
+ sam_gpiowrite(GPIO_TSC_CS, !selected);
}
#endif
}
/****************************************************************************
- * Name: sam_spistatus
+ * Name: sam_spi0status
*
* Description:
* Return status information associated with the SPI device.
@@ -196,7 +196,7 @@ void sam_spiselect(enum spi_dev_e devid, bool selected)
*
****************************************************************************/
-uint8_t sam_spistatus(FAR struct spi_dev_s *dev, enum spi_dev_e devid)
+uint8_t sam_spi0status(FAR struct spi_dev_s *dev, enum spi_dev_e devid)
{
return 0;
}
diff --git a/nuttx/configs/sam4l-xplained/src/sam_spi.c b/nuttx/configs/sam4l-xplained/src/sam_spi.c
index 7fe719320..129e72aec 100644
--- a/nuttx/configs/sam4l-xplained/src/sam_spi.c
+++ b/nuttx/configs/sam4l-xplained/src/sam_spi.c
@@ -115,14 +115,14 @@ void weak_function sam_spiinitialize(void)
}
/****************************************************************************
- * Name: sam_spiselect, sam_spistatus, and sam_spicmddata
+ * Name: sam_spi0select, sam_spi0status, and sam_spic0mddata
*
* Description:
* These external functions must be provided by board-specific logic. They
* include:
*
- * o sam_spiselect is a functions tomanage the board-specific chip selects
- * o sam_spistatus and sam_spicmddata: Implementations of the status
+ * o sam_spi0select is a functions tomanage the board-specific chip selects
+ * o sam_spi0status and sam_spic0mddata: Implementations of the status
* and cmddata methods of the SPI interface defined by struct spi_ops_
* (see include/nuttx/spi/spi.h). All other methods including
* up_spiinitialize()) are provided by common SAM3/4 logic.
@@ -131,11 +131,11 @@ void weak_function sam_spiinitialize(void)
*
* 1. Provide logic in sam_boardinitialize() to configure SPI chip select
* pins.
- * 2. Provide sam_spiselect() and sam_spistatus() functions in your board-
+ * 2. Provide sam_spi0select() and sam_spi0status() functions in your board-
* specific logic. These functions will perform chip selection and
* status operations using GPIOs in the way your board is configured.
* 2. If CONFIG_SPI_CMDDATA is defined in the NuttX configuration, provide
- * sam_spicmddata() functions in your board-specific logic. This
+ * sam_spic0mddata() functions in your board-specific logic. This
* function will perform cmd/data selection operations using GPIOs in
* the way your board is configured.
* 3. Add a call to up_spiinitialize() in your low level application
@@ -148,7 +148,7 @@ void weak_function sam_spiinitialize(void)
****************************************************************************/
/****************************************************************************
- * Name: sam_spiselect
+ * Name: sam_spi0select
*
* Description:
* PIO chip select pins may be programmed by the board specific logic in
@@ -172,7 +172,7 @@ void weak_function sam_spiinitialize(void)
*
****************************************************************************/
-void sam_spiselect(enum spi_dev_e devid, bool selected)
+void sam_spi0select(enum spi_dev_e devid, bool selected)
{
#ifdef CONFIG_SAM4L_XPLAINED_IOMODULE
/* Select/de-select the SD card */
@@ -202,7 +202,7 @@ void sam_spiselect(enum spi_dev_e devid, bool selected)
}
/****************************************************************************
- * Name: sam_spistatus
+ * Name: sam_spi0status
*
* Description:
* Return status information associated with the SPI device.
@@ -215,7 +215,7 @@ void sam_spiselect(enum spi_dev_e devid, bool selected)
*
****************************************************************************/
-uint8_t sam_spistatus(FAR struct spi_dev_s *dev, enum spi_dev_e devid)
+uint8_t sam_spi0status(FAR struct spi_dev_s *dev, enum spi_dev_e devid)
{
uint8_t ret = 0;
@@ -239,7 +239,7 @@ uint8_t sam_spistatus(FAR struct spi_dev_s *dev, enum spi_dev_e devid)
#endif /* CONFIG_SAM34_SPI0 */
/****************************************************************************
- * Name: sam_spicmddata
+ * Name: sam_spic0mddata
*
* Description:
* Some SPI devices require an additional control to determine if the SPI
@@ -263,7 +263,7 @@ uint8_t sam_spistatus(FAR struct spi_dev_s *dev, enum spi_dev_e devid)
****************************************************************************/
#ifdef CONFIG_SPI_CMDDATA
-int sam_spicmddata(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool cmd)
+int sam_spic0mddata(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool cmd)
{
#ifdef CONFIG_SAM4L_XPLAINED_OLED1MODULE
if (devid == SPIDEV_DISPLAY)