diff options
-rw-r--r-- | nuttx/ChangeLog | 6 | ||||
-rw-r--r-- | nuttx/arch/arm/src/sam34/Kconfig | 43 | ||||
-rw-r--r-- | nuttx/arch/arm/src/sam34/Make.defs | 4 | ||||
-rw-r--r-- | nuttx/arch/arm/src/sam34/sam_spi.c | 1227 | ||||
-rw-r--r-- | nuttx/arch/arm/src/sam34/sam_spi.h | 122 | ||||
-rw-r--r-- | nuttx/arch/arm/src/sama5/Kconfig | 2 | ||||
-rw-r--r-- | nuttx/arch/arm/src/sama5/sam_spi.c | 2 | ||||
-rw-r--r-- | nuttx/arch/arm/src/sama5/sam_spi.h | 28 | ||||
-rw-r--r-- | nuttx/configs/sam3u-ek/src/up_spi.c | 18 | ||||
-rw-r--r-- | nuttx/configs/sam4e-ek/src/Makefile | 6 | ||||
-rw-r--r-- | nuttx/configs/sam4e-ek/src/sam4e-ek.h | 6 | ||||
-rw-r--r-- | nuttx/configs/sam4e-ek/src/sam_spi.c | 26 | ||||
-rw-r--r-- | nuttx/configs/sam4l-xplained/src/sam_spi.c | 22 |
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) |