From 86a67fa3e867a543c38a7a3e77bb172b2e0e2c7f Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Fri, 30 Nov 2018 12:48:46 -0600 Subject: [PATCH] Squashed commit of the following: arch/arm/src/max326xx/max32660/max32660_spim.c: SPI0 master mode support is complete (sans DMA and completely untested). configs/max32660-evsys/src: Add framework for SPI support. arch/arm/src/max326xx/max32660/max32660_spim.c: Fleshes out some of the data transfer logic. More needs to be done. Also removes leveraged DMA support. It will be a long time (if ever) before DMA is supported. No point in dragging all of that bogus logic around. --- arch/arm/src/max326xx/Kconfig | 16 + arch/arm/src/max326xx/hardware/max32660_spi.h | 6 +- .../arm/src/max326xx/max32660/max32660_spim.c | 1328 ++++++++--------- configs/max32660-evsys/src/Makefile | 2 +- configs/max32660-evsys/src/max32660-evsys.h | 12 + configs/max32660-evsys/src/max326_boot.c | 6 + configs/max32660-evsys/src/max326_spi.c | 172 +++ 7 files changed, 857 insertions(+), 685 deletions(-) create mode 100644 configs/max32660-evsys/src/max326_spi.c diff --git a/arch/arm/src/max326xx/Kconfig b/arch/arm/src/max326xx/Kconfig index 60dfb3ce3c..0c8383d8c1 100644 --- a/arch/arm/src/max326xx/Kconfig +++ b/arch/arm/src/max326xx/Kconfig @@ -416,3 +416,19 @@ config MAX326XX_DMA config MAX326XX_GPIOIRQ bool "GPIO Interrupt Support" default n + +menu "SPI Master Configuration" + depends on MAX326XX_HAVE_SPIM + +if MAX326XX_SPIM0 + +config MAX326XX_3WIRE + bool "SP0 3-pin mode" + default n + ---help--- + By default, 4-pin mode is supported(MISO, MOSI, SCK, SS). Selecting + this option enables 3-pin mode (MOSI, SCK, SS). + +endif # MAX326XX_SPIM0 + +endmenu # SPI Master Configuration diff --git a/arch/arm/src/max326xx/hardware/max32660_spi.h b/arch/arm/src/max326xx/hardware/max32660_spi.h index 68b3293194..f305e15238 100644 --- a/arch/arm/src/max326xx/hardware/max32660_spi.h +++ b/arch/arm/src/max326xx/hardware/max32660_spi.h @@ -47,6 +47,8 @@ * Pre-processor Definitions ************************************************************************************/ +#define MAX326_SPI_FIFO_DEPTH 32 + /* Register Offsets *****************************************************************/ #define MAX326_SPI_DATA_OFFSET 0x0000 /* SPI FIFO Data Register */ @@ -147,7 +149,7 @@ #define SPI_DMA_TXFIFOLVL_SHIFT (0) /* Bits 0-4: TX FIFO Threshold Level */ #define SPI_DMA_TXFIFOLVL_MASK (0x1f << SPI_DMA_TXFIFOLVL_SHIFT) -# define SPI_DMA_TXFIFOLVL(n) ((uint32_t)(n) << SPI_DMA_TXFIFOLVL_SHIFT) +# define SPI_DMA_TXFIFOLVL(n) ((uint32_t)((n) & 0x1f) << SPI_DMA_TXFIFOLVL_SHIFT) #define SPI_DMA_TXFIFOEN (1 << 6) /* Bit 6: TX FIFO Enabled */ #define SPI_DMA_TXFIFOCLR (1 << 7) /* Bit 7: Clear the TX FIFO */ #define SPI_DMA_TXFIFOCNT_SHIFT (8) /* Bits 8-13: Number of Bytes in the TX @@ -156,7 +158,7 @@ #define SPI_DMA_TXDMAEN (1 << 15) /* Bit 15: TX DMA Enable */ #define SPI_DMA_RXFIFOLVL_SHIFT (16) /* Bits 16-20: RX FIFO Threshold Level */ #define SPI_DMA_RXFIFOLVL_MASK (0x1f << SPI_DMA_RXFIFOLVL_SHIFT) -# define SPI_DMA_RXFIFOLVL(n) ((uint32_t)(n) << SPI_DMA_RXFIFOLVL_SHIFT) +# define SPI_DMA_RXFIFOLVL(n) ((uint32_t)((n) & 0x1f) << SPI_DMA_RXFIFOLVL_SHIFT) #define SPI_DMA_RXFIFOEN (1 << 22) /* Bit 22: RX FIFO Enabled */ #define SPI_DMA_RXFIFOCLR (1 << 23) /* Bit 23: Clear the RX FIFO */ #define SPI_DMA_RXFIFOCNT_SHIFT (24) /* Bits 24-29: Number of Bytes in the RX diff --git a/arch/arm/src/max326xx/max32660/max32660_spim.c b/arch/arm/src/max326xx/max32660/max32660_spim.c index 47c77fbc26..c81e9c335e 100644 --- a/arch/arm/src/max326xx/max32660/max32660_spim.c +++ b/arch/arm/src/max326xx/max32660/max32660_spim.c @@ -1,4 +1,4 @@ -/************************************************************************************ +/**************************************************************************** * arch/arm/src/max326/max326_spi.c * * Copyright (C) 2018 Gregory Nutt. All rights reserved. @@ -31,39 +31,41 @@ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - ************************************************************************************/ + ****************************************************************************/ -/************************************************************************************ - * The external functions, max326_spi1/2/3select and max326_spi1/2/3status must be - * provided by board-specific logic. They are implementations of the select - * and status methods of the SPI interface defined by struct spi_ops_s (see - * include/nuttx/spi/spi.h). All other methods (including max326_spibus_initialize()) - * are provided by common MAX326 logic. To use this common SPI logic on your - * board: +/**************************************************************************** + * The external functions, max326_spi1/2/3select and max326_spi1/2/3status + * must be provided by board-specific logic. They are implementations of + * the select and status methods of the SPI interface defined by struct + * spi_ops_s (see include/nuttx/spi/spi.h). All other methods (including + * max326_spibus_initialize()) are provided by common MAX326 logic. To use + * this common SPI logic on your board: * - * 1. Provide logic in max326_boardinitialize() to configure SPI chip select - * pins. - * 2. Provide max326_spi1/2/3select() and max326_spi1/2/3status() functions in your - * board-specific logic. These functions will perform chip selection and - * status operations using GPIOs in the way your board is configured. - * 3. Add a calls to max326_spibus_initialize() in your low level application - * initialization logic - * 4. The handle returned by max326_spibus_initialize() may then be used to bind - * the SPI driver to higher level logic (e.g., calling + * 1. Provide logic in max326_boardinitialize() to configure SPI chip + * select pins. + * 2. Provide max326_spi1/2/3select() and max326_spi1/2/3status() functions + * in your board-specific logic. These functions will perform chip + * selection and status operations using GPIOs in the way your board is + * configured. + * 3. Add a calls to max326_spibus_initialize() in your low level + * application initialization logic + * 4. The handle returned by max326_spibus_initialize() may then be used + * to bind the SPI driver to higher level logic (e.g., calling * mmcsd_spislotinitialize(), for example, will bind the SPI driver to * the SPI MMC/SD driver). * - ****************************************************c********************************/ + ****************************************************************************/ -/************************************************************************************ +/**************************************************************************** * Included Files - ************************************************************************************/ + ****************************************************************************/ #include #include #include #include +#include #include #include #include @@ -88,118 +90,114 @@ #if defined(CONFIG_MAX326XX_HAVE_SPIM) -/************************************************************************************ +/**************************************************************************** * Pre-processor Definitions - ************************************************************************************/ + ****************************************************************************/ -/************************************************************************************ +#ifdef CONFIG_MAX326_SPI_DMA +# error DMA not yet supported +#endif + +/**************************************************************************** * Private Types - ************************************************************************************/ + ****************************************************************************/ struct max326_spidev_s { - struct spi_dev_s spidev; /* Externally visible part of the SPI interface */ - uint32_t spibase; /* SPIn base address */ -#ifdef CONFIG_MAX326_SPI_INTERRUPTS - uint8_t spiirq; /* SPI IRQ number */ -#endif -#ifdef CONFIG_MAX326_SPI_DMA - volatile uint8_t rxresult; /* Result of the RX DMA */ - volatile uint8_t txresult; /* Result of the TX DMA */ - uint8_t rxch; /* The RX DMA channel number */ - uint8_t txch; /* The TX DMA channel number */ - DMA_HANDLE rxdma; /* DMA channel handle for RX transfers */ - DMA_HANDLE txdma; /* DMA channel handle for TX transfers */ - sem_t rxsem; /* Wait for RX DMA to complete */ - sem_t txsem; /* Wait for TX DMA to complete */ - uint32_t txccr; /* DMA control register for TX transfers */ - uint32_t rxccr; /* DMA control register for RX transfers */ -#endif - bool initialized; /* Has SPI interface been initialized */ - sem_t exclsem; /* Held while chip is selected for mutual exclusion */ + struct spi_dev_s dev; /* Externally visible part of the SPI interface */ + const void *txbuffer; /* Tx buffer pointer (may be NULL) */ + void *rxbuffer; /* Rx buffer pointer (may be NULL) */ + uint32_t base; /* SPI base address */ uint32_t frequency; /* Requested clock frequency */ uint32_t actual; /* Actual clock frequency */ + sem_t exclsem; /* Held while chip is selected for mutual exclusion */ + uint16_t rxbytes; /* Number of bytes received into rxbuffer */ + uint16_t txbytes; /* Number of bytes sent from txbuffer */ + uint16_t xfrlen; /* Transfer length */ + bool initialized; /* True: SPI interface been initialized */ + bool data16; /* True: 16- vs 8-bit data transfers */ + bool wire3; /* True: 3- vs 4-pin mode */ + bool busy; /* True: Transfer started */ +#ifdef CONFIG_MAX326_SPI_INTERRUPTS + uint8_t irq; /* SPI IRQ number */ +#endif uint8_t nbits; /* Width of word in bits (4 through 16) */ uint8_t mode; /* Mode 0,1,2,3 */ }; -/************************************************************************************ +/**************************************************************************** * Private Function Prototypes - ************************************************************************************/ + ****************************************************************************/ /* Helpers */ static inline uint32_t spi_getreg(struct max326_spidev_s *priv, - unsigned int offset); + unsigned int offset); static inline void spi_putreg(struct max326_spidev_s *priv, - unsigned int offset, uint32_t value); -static inline uint16_t spi_readword(struct max326_spidev_s *priv); -static inline void spi_writeword(struct max326_spidev_s *priv, uint16_t byte); -static inline bool spi_16bitmode(struct max326_spidev_s *priv); + unsigned int offset, uint32_t value); +static void spi_modify_ctrl0(struct max326_spidev_s *priv, uint32_t setbits, + uint32_t clrbits); +static void spi_modify_ctrl1(struct max326_spidev_s *priv, uint32_t setbits, + uint32_t clrbits); +static void spi_modify_ctrl2(struct max326_spidev_s *priv, uint32_t setbits, + uint32_t clrbits); +static void spi_modify_dma(struct max326_spidev_s *priv, uint32_t setbits, + uint32_t clrbits); +#ifdef CONFIG_MAX326_SPI_INTERRUPTS +static void spi_modify_inten(struct max326_spidev_s *priv, uint32_t setbits, + uint32_t clrbits) +#endif /* Interrupt support */ -static void spi_poll(struct max326_spidev_s *priv); +static int spi_poll(struct max326_spidev_s *priv); #ifdef CONFIG_MAX326_SPI_INTERRUPTS -static int spi_interrupt(int irq, void *context, void *arg); -#endif - -/* DMA support */ - -#ifdef CONFIG_MAX326_SPI_DMA -static void spi_dmarxwait(struct max326_spidev_s *priv); -static void spi_dmatxwait(struct max326_spidev_s *priv); -static inline void spi_dmarxwakeup(struct max326_spidev_s *priv); -static inline void spi_dmatxwakeup(struct max326_spidev_s *priv); -static void spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr, void *arg); -static void spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr, void *arg); -static void spi_dmarxsetup(struct max326_spidev_s *priv, void *rxbuffer, - void *rxdummy, size_t nwords); -static void spi_dmatxsetup(struct max326_spidev_s *priv, - const void *txbuffer, const void *txdummy, - size_t nwords); -static inline void spi_dmarxstart(struct max326_spidev_s *priv); -static inline void spi_dmatxstart(struct max326_spidev_s *priv); +static int spi_interrupt(int irq, void *context, void *arg); #endif /* SPI methods */ -static int spi_lock(struct spi_dev_s *dev, bool lock); -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); -#ifdef CONFIG_SPI_HWFEATURES -static int spi_hwfeatures(struct spi_dev_s *dev, - spi_hwfeatures_t features); +static int spi_lock(struct spi_dev_s *dev, bool lock); +#ifdef CONFIG_MAX326XX_SPIM0 +static void spi0_select(struct spi_dev_s *dev, uint32_t devid, + bool selected); #endif -static uint16_t spi_send(struct spi_dev_s *dev, uint16_t wd); -static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer, - 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); +#ifdef CONFIG_SPI_HWFEATURES +static int spi_hwfeatures(struct spi_dev_s *dev, + spi_hwfeatures_t features); +#endif +static uint16_t spi_send(struct spi_dev_s *dev, uint16_t wd); +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(struct spi_dev_s *dev, const void *txbuffer, - size_t nwords); -static void spi_recvblock(struct spi_dev_s *dev, void *rxbuffer, - size_t nwords); +static void spi_sndblock(struct spi_dev_s *dev, const void *txbuffer, + size_t nwords); +static void spi_recvblock(struct spi_dev_s *dev, void *rxbuffer, + size_t nwords); #endif /* Initialization */ static void spi_bus_initialize(struct max326_spidev_s *priv); -/************************************************************************************ +/**************************************************************************** * Private Data - ************************************************************************************/ + ****************************************************************************/ -/* NOTE: This is somewhat over-designed since there is only a single SPI peripheral. - * However, it supports simple group to additional SPI peripherals by extending this - * logic. +/* NOTE: This is somewhat over-designed since there is only a single SPI + * peripheral. However, it supports simple group to additional SPI + * peripherals by extending this logic. */ #ifdef CONFIG_MAX326XX_SPIM0 static const struct spi_ops_s g_sp0iops = { .lock = spi_lock, - .select = max326_spi0select, + .select = spi0_select, .setfrequency = spi_setfrequency, .setmode = spi_setmode, .setbits = spi_setbits, @@ -226,28 +224,22 @@ static const struct spi_ops_s g_sp0iops = static struct max326_spidev_s g_spi0dev = { - .spidev = { &g_sp0iops }, - .spibase = MAX326_SPI0_BASE, + .dev = { &g_sp0iops }, + .base = MAX326_SPI0_BASE, #ifdef CONFIG_MAX326_SPI_INTERRUPTS - .spiirq = MAX326_IRQ_SPI, + .irq = MAX326_IRQ_SPI, #endif -#ifdef CONFIG_MAX326_SPI_DMA -# ifdef CONFIG_MAX326XX_SPIM0_DMA - .rxch = DMACHAN_SPI0_RX, - .txch = DMACHAN_SPI0_TX, -# else - .rxch = 0, - .txch = 0, -# endif +#ifdef CONFIG_MAX326XX_3WIRE + .wire3 = true, #endif }; #endif -/************************************************************************************ +/**************************************************************************** * Private Functions - ************************************************************************************/ + ****************************************************************************/ -/************************************************************************************ +/**************************************************************************** * Name: spi_getreg * * Description: @@ -260,14 +252,15 @@ static struct max326_spidev_s g_spi0dev = * Returned Value: * The contents of the 16-bit register * - ************************************************************************************/ + ****************************************************************************/ -static inline uint32_t spi_getreg(struct max326_spidev_s *priv, unsigned int offset) +static inline uint32_t spi_getreg(struct max326_spidev_s *priv, + unsigned int offset) { - return getreg32(priv->spibase + offset); + return getreg32(priv->base + offset); } -/************************************************************************************ +/**************************************************************************** * Name: spi_putreg * * Description: @@ -281,88 +274,151 @@ static inline uint32_t spi_getreg(struct max326_spidev_s *priv, unsigned int off * Returned Value: * The contents of the 16-bit register * - ************************************************************************************/ + ****************************************************************************/ -static inline void spi_putreg(struct max326_spidev_s *priv, unsigned int offset, - uint32_t value) +static inline void spi_putreg(struct max326_spidev_s *priv, + unsigned int offset, uint32_t value) { - putreg32(value, priv->spibase + offset); + putreg32(value, priv->base + offset); } -/************************************************************************************ - * Name: spi_readword +/**************************************************************************** + * Name: spi_modify_ctrl0 * * Description: - * Read one word from SPI + * Clear and set bits in the CTRL0 register * * Input Parameters: - * priv - Device-specific state data - * - * Returned Value: - * Word as read - * - ************************************************************************************/ - -static inline uint16_t spi_readword(struct max326_spidev_s *priv) -{ - /* Wait until the receive buffer is not empty */ - - while ((spi_getreg(priv, MAX326_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0) - { - } - - /* Then return the received word */ - - return spi_getreg(priv, MAX326_SPI_DR_OFFSET); -} - -/************************************************************************************ - * Name: spi_writeword - * - * Description: - * Write one byte to SPI - * - * Input Parameters: - * priv - Device-specific state data - * byte - Byte to send + * priv - Device-specific state data + * clrbits - The bits to clear + * setbits - The bits to set * * Returned Value: * None * - ************************************************************************************/ + ****************************************************************************/ -static inline void spi_writeword(struct max326_spidev_s *priv, uint16_t word) +static void spi_modify_ctrl0(struct max326_spidev_s *priv, uint32_t setbits, + uint32_t clrbits) { - /* Wait until the transmit buffer is empty */ + uint32_t ctrl0; - while ((spi_getreg(priv, MAX326_SPI_SR_OFFSET) & SPI_SR_TXE) == 0) - { - } - - /* Then send the word */ - - spi_putreg(priv, MAX326_SPI_DR_OFFSET, word); + ctrl0 = spi_getreg(priv, MAX326_SPI_CTRL0_OFFSET); + ctrl0 &= ~clrbits; + ctrl0 |= setbits; + spi_putreg(priv, MAX326_SPI_CTRL0_OFFSET, ctrl0); } -/************************************************************************************ - * Name: spi_16bitmode +/**************************************************************************** + * Name: spi_modify_ctrl1 * * Description: - * Check if the SPI is operating in 16-bit mode + * Clear and set bits in the CTRL1 register * * Input Parameters: - * priv - Device-specific state data + * priv - Device-specific state data + * clrbits - The bits to clear + * setbits - The bits to set * * Returned Value: - * true: 16-bit mode, false: 8-bit mode + * None * - ************************************************************************************/ + ****************************************************************************/ -static inline bool spi_16bitmode(struct max326_spidev_s *priv) +static void spi_modify_ctrl1(struct max326_spidev_s *priv, uint32_t setbits, + uint32_t clrbits) { -#warning Missing logic + uint32_t ctrl1; + + ctrl1 = spi_getreg(priv, MAX326_SPI_CTRL1_OFFSET); + ctrl1 &= ~clrbits; + ctrl1 |= setbits; + spi_putreg(priv, MAX326_SPI_CTRL1_OFFSET, ctrl1); } +/**************************************************************************** + * Name: spi_modify_ctrl2 + * + * Description: + * Clear and set bits in the CTRL1 register + * + * Input Parameters: + * priv - Device-specific state data + * clrbits - The bits to clear + * setbits - The bits to set + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void spi_modify_ctrl2(struct max326_spidev_s *priv, uint32_t setbits, + uint32_t clrbits) +{ + uint32_t ctrl2; + + ctrl2 = spi_getreg(priv, MAX326_SPI_CTRL2_OFFSET); + ctrl2 &= ~clrbits; + ctrl2 |= setbits; + spi_putreg(priv, MAX326_SPI_CTRL2_OFFSET, ctrl2); +} + +/**************************************************************************** + * Name: spi_modify_dma + * + * Description: + * Clear and set bits in the DMA register + * + * Input Parameters: + * priv - Device-specific state data + * clrbits - The bits to clear + * setbits - The bits to set + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void spi_modify_dma(struct max326_spidev_s *priv, uint32_t setbits, + uint32_t clrbits) +{ + uint32_t dma; + + dma = spi_getreg(priv, MAX326_SPI_DMA_OFFSET); + dma &= ~clrbits; + dma |= setbits; + spi_putreg(priv, MAX326_SPI_DMA_OFFSET, dma); +} + +/**************************************************************************** + * Name: spi_modify_inten + * + * Description: + * Clear and set bits in the INTEN register + * + * Input Parameters: + * priv - Device-specific state data + * clrbits - The bits to clear + * setbits - The bits to set + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_MAX326_SPI_INTERRUPTS +static void spi_modify_inten(struct max326_spidev_s *priv, uint32_t setbits, + uint32_t clrbits) +{ + uint32_t inten; + + inten = spi_getreg(priv, MAX326_SPI_INTEN_OFFSET); + inten &= ~clrbits; + inten |= setbits; + spi_putreg(priv, MAX326_SPI_INTEN_OFFSET, inten); +} +#endif + /**************************************************************************** * Name: spi_poll * @@ -370,11 +426,232 @@ static inline bool spi_16bitmode(struct max326_spidev_s *priv) * Handle SPI events. This may be called repeatedly in polled mode or may * be called from spi_interrupt() in interrupt mode. * + * Returned Value: + * Returns OK when the transfer is complete, -EBUSY is the transfer is + * still in progress. Could also return other negated errno values if + * an error is detected. + * ****************************************************************************/ -static void spi_poll(struct max326_spidev_s *priv) +static int spi_poll(struct max326_spidev_s *priv) { -#warning Missing logic + const uint8_t *src; + uint8_t *dest; + uint32_t inten; + uint32_t length; + uint32_t regval; + uint32_t tmp; + unsigned int txavail; + unsigned int rxavail; + int remaining; + + /* Get the transfer size in units of bytes */ + + length = priv->data16 ? priv->xfrlen << 1 : priv->xfrlen; + + /* Is there a Tx source buffer? */ + + inten = 0; + if (priv->txbuffer != NULL) + { + /* Need to know when all bytes are transmitted. */ + + inten |= SPI_INT_TXEMPTY; + + /* Calculate how many bytes we can write to the FIFO */ + + regval = spi_getreg(priv, MAX326_SPI_DMA_OFFSET); + tmp = (regval & SPI_DMA_TXFIFOCNT_MASK) >> SPI_DMA_TXFIFOCNT_SHIFT; + txavail = MAX326_SPI_FIFO_DEPTH - tmp; + + if ((length - priv->txbytes) < txavail) + { + txavail = (length - priv->txbytes); + } + + if (priv->data16) + { + txavail &= ~1; + } + + /* Write Tx buffer data to the FIFO */ + + src = &((const uint8_t *)priv->txbuffer)[priv->txbytes]; + while (txavail) + { + dest = (uint8_t *)(priv->base + MAX326_SPI_DATA_OFFSET); + + if (txavail > 3) + { + *dest++ = *src++; + *dest++ = *src++; + *dest++ = *src++; + *dest = *src++; + + txavail -= 4; + priv->txbytes += 4; + } + else if (txavail > 1) + { + *dest++ = *src++; + *dest = *src++; + + txavail -= 2; + priv->txbytes += 2; + } + else if (!priv->data16) + { + *dest = *src++; + + txavail -= 1; + priv->txbytes += 1; + } + } + } + + remaining = length - priv->txbytes; + + /* Set the TX interrupts */ + + if (remaining > 0) + { + if (remaining > MAX326_SPI_FIFO_DEPTH) + { + /* Set the TX FIFO almost empty interrupt if we have to refill */ + + regval = SPI_DMA_TXFIFOLVL(MAX326_SPI_FIFO_DEPTH); + } + else + { + regval = SPI_DMA_TXFIFOLVL(remaining); + } + + spi_modify_dma(priv, regval, SPI_DMA_TXFIFOLVL_MASK); + inten |= SPI_INT_TXLEVEL; + } + + /* Break out if we've transmitted all the bytes and not receiving */ + + if ((priv->rxbuffer == NULL) && priv->txbytes == length && + (spi_getreg(priv, MAX326_SPI_DMA_OFFSET) & SPI_DMA_TXFIFOCNT_MASK) == 0) + { + goto done; + } + + /* Read the RX FIFO */ + + if (priv->rxbuffer != NULL) + { + /* Wait for there to be data in the RX FIFO */ + + regval = spi_getreg(priv, MAX326_SPI_DMA_OFFSET); + rxavail = (regval & SPI_DMA_RXFIFOCNT_MASK) >> SPI_DMA_RXFIFOCNT_SHIFT; + + if (length - priv->rxbytes < rxavail) + { + rxavail = length - priv->rxbytes; + } + + dest = &((uint8_t *)priv->rxbuffer)[priv->rxbytes]; + if (!priv->data16 || rxavail >= sizeof(uint16_t)) + { + /* Read data from the Rx FIFO */ + + src = (const uint8_t *)(priv->base + MAX326_SPI_DATA_OFFSET); + while (rxavail) + { + if (rxavail > 3) + { + *dest++ = *src++; + *dest++ = *src++; + *dest++ = *src++; + *dest++ = *src; + + rxavail -= 4; + priv->rxbytes += 4; + } + else if (rxavail > 1) + { + *dest++ = *src++; + *dest++ = *src; + + rxavail -= 2; + priv->rxbytes += 2; + } + else + { + *dest++ = *src; + + rxavail -= 1; + priv->rxbytes += 1; + } + + /* Don't read less than 2 bytes if we are using 16-bit data + * transfers. + */ + + if (rxavail < sizeof(uint16_t) && priv->data16) + { + break; + } + } + } + + remaining = length - priv->rxbytes; + if (remaining > 0) + { + if (remaining > MAX326_SPI_FIFO_DEPTH) + { + regval = SPI_DMA_RXFIFOLVL(2); + } + else + { + regval = SPI_DMA_RXFIFOLVL(remaining - 1); + } + + spi_modify_dma(priv, regval, SPI_DMA_RXFIFOLVL_MASK); + inten |= SPI_INT_RXLEVEL; + } + + /* Break out if we've received all the bytes and we're not transmitting */ + + if (priv->txbuffer == NULL && priv->rxbytes == length) + { + goto done; + } + } + + /* Break out once we've transmitted and received all of the data */ + + if (priv->rxbytes == length && priv->txbytes == length) + { + regval = spi_getreg(priv, MAX326_SPI_DMA_OFFSET); + if ((regval & SPI_DMA_TXFIFOCNT_MASK) == 0) + { + goto done; + } + } + +#ifdef CONFIG_MAX326_SPI_INTERRUPTS + /* Enable interrupts for the next phase */ + + spi_putreg(priv, MAX326_SPI_INTEN_OFFSET, inten); +#endif + + /* Return busy to indicate that we are not finished with the transfer */ + + return -EBUSY; + +done: +#ifdef CONFIG_MAX326_SPI_INTERRUPTS + /* We are done.. disable interrupts */ + + spi_putreg(priv, MAX326_SPI_INTEN_OFFSET, 0); +#endif + + /* Return OK to indicate that we are finished with the transfer */ + + return OK; } /**************************************************************************** @@ -394,20 +671,21 @@ static int spi_interrupt(int irq, void *context, void *arg) uint32_t regval; unsigned int rxavail; unsigned int rxlevel; + int ret; - /* Read pending interrupt flags, interrupt enables, and UART status + /* Read pending interrupt flags, interrupt enables, and SPI status * registers. */ - intfl = max326_serialin(priv, MAX326_UART_INTFL_OFFSET); + intfl = spi_getreg(priv, MAX326_SPI_INTFL_OFFSET); /* Disable all interrupts */ - spi_putreg(priv, MAX326_UART_INTEN_OFFSET, 0); + spi_putreg(priv, MAX326_SPI_INTEN_OFFSET, 0); /* Clear pending interrupt flags */ - max326_serialout(priv, MAX326_UART_INTFL_OFFSET, intfl & UART_INT_ALL); + max326_serialout(priv, MAX326_SPI_INTFL_OFFSET, intfl & SPI_INT_ALL); /* Handle any active request */ @@ -415,7 +693,8 @@ static int spi_interrupt(int irq, void *context, void *arg) { do { - spi_poll(priv); + ret = spi_poll(priv); + DEBUGASSERT(ret == OK || ret == -EBUSY); /* Check if there is more Rx data to be read */ @@ -430,325 +709,7 @@ static int spi_interrupt(int irq, void *context, void *arg) } #endif -/************************************************************************************ - * Name: spi_dmarxwait - * - * Description: - * Wait for DMA to complete. - * - ************************************************************************************/ - -#ifdef CONFIG_MAX326_SPI_DMA -static void spi_dmarxwait(struct max326_spidev_s *priv) -{ - int ret; - - /* Take the semaphore (perhaps waiting). If the result is zero, then the DMA - * must not really have completed??? - */ - - do - { - ret = nxsem_wait(&priv->rxsem); - - /* The only case that an error should occur here is if the wait was - * awakened by a signal. - */ - - DEBUGASSERT(ret == OK || ret == -EINTR); - } - while (ret == -EINTR || priv->rxresult == 0); -} -#endif - -/************************************************************************************ - * Name: spi_dmatxwait - * - * Description: - * Wait for DMA to complete. - * - ************************************************************************************/ - -#ifdef CONFIG_MAX326_SPI_DMA -static void spi_dmatxwait(struct max326_spidev_s *priv) -{ - int ret; - - /* Take the semaphore (perhaps waiting). If the result is zero, then the DMA - * must not really have completed??? - */ - - do - { - ret = nxsem_wait(&priv->txsem); - - /* The only case that an error should occur here is if the wait was - * awakened by a signal. - */ - - DEBUGASSERT(ret == OK || ret == -EINTR); - } - while (ret == -EINTR || priv->txresult == 0); -} -#endif - -/************************************************************************************ - * Name: spi_dmarxwakeup - * - * Description: - * Signal that DMA is complete - * - ************************************************************************************/ - -#ifdef CONFIG_MAX326_SPI_DMA -static inline void spi_dmarxwakeup(struct max326_spidev_s *priv) -{ - (void)nxsem_post(&priv->rxsem); -} -#endif - -/************************************************************************************ - * Name: spi_dmatxwakeup - * - * Description: - * Signal that DMA is complete - * - ************************************************************************************/ - -#ifdef CONFIG_MAX326_SPI_DMA -static inline void spi_dmatxwakeup(struct max326_spidev_s *priv) -{ - (void)nxsem_post(&priv->txsem); -} -#endif - -/************************************************************************************ - * Name: spi_dmarxcallback - * - * Description: - * Called when the RX DMA completes - * - ************************************************************************************/ - -#ifdef CONFIG_MAX326_SPI_DMA -static void spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr, void *arg) -{ - struct max326_spidev_s *priv = (struct max326_spidev_s *)arg; - - /* Wake-up the SPI driver */ - - priv->rxresult = isr | 0x080; /* OR'ed with 0x80 to assure non-zero */ - spi_dmarxwakeup(priv); -} -#endif - -/************************************************************************************ - * Name: spi_dmatxcallback - * - * Description: - * Called when the RX DMA completes - * - ************************************************************************************/ - -#ifdef CONFIG_MAX326_SPI_DMA -static void spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr, void *arg) -{ - struct max326_spidev_s *priv = (struct max326_spidev_s *)arg; - - /* Wake-up the SPI driver */ - - priv->txresult = isr | 0x080; /* OR'ed with 0x80 to assure non-zero */ - spi_dmatxwakeup(priv); -} -#endif - -/************************************************************************************ - * Name: spi_dmarxsetup - * - * Description: - * Setup to perform RX DMA - * - ************************************************************************************/ - -#ifdef CONFIG_MAX326_SPI_DMA -static void spi_dmarxsetup(struct max326_spidev_s *priv, void *rxbuffer, - void *rxdummy, size_t nwords) -{ - /* 8- or 16-bit mode? */ - - if (spi_16bitmode(priv)) - { - /* 16-bit mode -- is there a buffer to receive data in? */ - - if (rxbuffer) - { - priv->rxccr = SPI_RXDMA16_CONFIG; - } - else - { - rxbuffer = rxdummy; - priv->rxccr = SPI_RXDMA16NULL_CONFIG; - } - } - else - { - /* 8-bit mode -- is there a buffer to receive data in? */ - - if (rxbuffer) - { - priv->rxccr = SPI_RXDMA8_CONFIG; - } - else - { - rxbuffer = rxdummy; - priv->rxccr = SPI_RXDMA8NULL_CONFIG; - } - } - - /* Configure the RX DMA */ - - max326_dmasetup(priv->rxdma, priv->spibase + MAX326_SPI_DR_OFFSET, - (uint32_t)rxbuffer, nwords, priv->rxccr); -} -#endif - -/************************************************************************************ - * Name: spi_dmatxsetup - * - * Description: - * Setup to perform TX DMA - * - ************************************************************************************/ - -#ifdef CONFIG_MAX326_SPI_DMA -static void spi_dmatxsetup(struct max326_spidev_s *priv,const void *txbuffer, - const void *txdummy, size_t nwords) -{ - /* 8- or 16-bit mode? */ - - if (spi_16bitmode(priv)) - { - /* 16-bit mode -- is there a buffer to transfer data from? */ - - if (txbuffer) - { - priv->txccr = SPI_TXDMA16_CONFIG; - } - else - { - txbuffer = txdummy; - priv->txccr = SPI_TXDMA16NULL_CONFIG; - } - } - else - { - /* 8-bit mode -- is there a buffer to transfer data from? */ - - if (txbuffer) - { - priv->txccr = SPI_TXDMA8_CONFIG; - } - else - { - txbuffer = txdummy; - priv->txccr = SPI_TXDMA8NULL_CONFIG; - } - } - - /* Setup the TX DMA */ - - max326_dmasetup(priv->txdma, priv->spibase + MAX326_SPI_DR_OFFSET, - (uint32_t)txbuffer, nwords, priv->txccr); -} -#endif - -/************************************************************************************ - * Name: spi_dmarxstart - * - * Description: - * Start RX DMA - * - ************************************************************************************/ - -#ifdef CONFIG_MAX326_SPI_DMA -static inline void spi_dmarxstart(struct max326_spidev_s *priv) -{ - priv->rxresult = 0; - max326_dmastart(priv->rxdma, spi_dmarxcallback, priv, false); -} -#endif - -/************************************************************************************ - * Name: spi_dmatxstart - * - * Description: - * Start TX DMA - * - ************************************************************************************/ - -#ifdef CONFIG_MAX326_SPI_DMA -static inline void spi_dmatxstart(struct max326_spidev_s *priv) -{ - priv->txresult = 0; - max326_dmastart(priv->txdma, spi_dmatxcallback, priv, false); -} -#endif - -/************************************************************************************ - * Name: spi_modify_ctrl0 - * - * Description: - * Clear and set bits in the CTRL0 register - * - * Input Parameters: - * priv - Device-specific state data - * clrbits - The bits to clear - * setbits - The bits to set - * - * Returned Value: - * None - * - ************************************************************************************/ - -static void spi_modify_ctrl0(struct max326_spidev_s *priv, uint32_t setbits, - uint32_t clrbits) -{ - uint32_t ctrl0; - - ctrl0 = spi_getreg(priv, MAX326_SPI_CTRL0_OFFSET); - ctrl0 &= ~clrbits; - ctrl0 |= setbits; - spi_putreg(priv, MAX326_SPI_CTRL0_OFFSET, ctrl0); -} - -/************************************************************************************ - * Name: spi_modify_ctrl2 - * - * Description: - * Clear and set bits in the CTRL2 register - * - * Input Parameters: - * priv - Device-specific state data - * clrbits - The bits to clear - * setbits - The bits to set - * - * Returned Value: - * None - * - ************************************************************************************/ - -static void spi_modify_ctrl2(struct max326_spidev_s *priv, uint32_t setbits, - uint32_t clrbits) -{ - uint32_t ctrl2; - - ctrl2 = spi_getreg(priv, MAX326_SPI_CTRL2_OFFSET); - ctrl2 &= ~clrbits; - ctrl2 |= setbits; - spi_putreg(priv, MAX326_SPI_CTRL2_OFFSET, ctrl2); -} - -/************************************************************************************ +/**************************************************************************** * Name: spi_lock * * Description: @@ -767,7 +728,7 @@ static void spi_modify_ctrl2(struct max326_spidev_s *priv, uint32_t setbits, * Returned Value: * None * - ************************************************************************************/ + ****************************************************************************/ static int spi_lock(struct spi_dev_s *dev, bool lock) { @@ -799,7 +760,49 @@ static int spi_lock(struct spi_dev_s *dev, bool lock) return ret; } -/************************************************************************************ +/**************************************************************************** + * Name: SPI_SELECT + * + * Description: + * Enable/disable the SPI chip select. The implementation of this method + * must include handshaking: If a device is selected, it must hold off + * all other attempts to select the device until the device is deselected. + * Required. + * + * Input Parameters: + * dev - Device-specific state data + * devid - Identifies the device to select + * selected - true: slave selected, false: slave de-selected + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_MAX326XX_SPIM0 +static void spi0_select(struct spi_dev_s *dev, uint32_t devid, bool selected) +{ + struct max326_spidev_s *priv; + + /* Forward the call to the board-specific implementation */ + + DEBUGASSERT(dev != NULL); + max326_spi0select(dev, devid, selected); + + /* The hardware requires that the SPI block be disabled and renabled at + * end of each transaction to cancel the on ongoing transaction (while + * chip select is inactive). + */ + + if (!selected) + { + priv = (struct max326_spidev_s *)dev; + spi_modify_ctrl0(priv, SPI_CTRL0_SPIEN, 0); + } +} +#endif + +/**************************************************************************** * Name: spi_setfrequency * * Description: @@ -812,7 +815,7 @@ static int spi_lock(struct spi_dev_s *dev, bool lock) * Returned Value: * Returns the actual frequency selected * - ************************************************************************************/ + ****************************************************************************/ static uint32_t spi_setfrequency(struct spi_dev_s *dev, uint32_t frequency) { @@ -934,7 +937,7 @@ static uint32_t spi_setfrequency(struct spi_dev_s *dev, uint32_t frequency) return priv->actual; } -/************************************************************************************ +/**************************************************************************** * Name: spi_setmode * * Description: @@ -947,7 +950,7 @@ static uint32_t spi_setfrequency(struct spi_dev_s *dev, uint32_t frequency) * Returned Value: * Returns the actual frequency selected * - ************************************************************************************/ + ****************************************************************************/ static void spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode) { @@ -997,7 +1000,7 @@ static void spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode) } } -/************************************************************************************ +/**************************************************************************** * Name: spi_setbits * * Description: @@ -1010,7 +1013,7 @@ static void spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode) * Returned Value: * None * - ************************************************************************************/ + ****************************************************************************/ static void spi_setbits(struct spi_dev_s *dev, int nbits) { @@ -1028,11 +1031,15 @@ static void spi_setbits(struct spi_dev_s *dev, int nbits) spi_modify_ctrl2(priv, SPI_CTRL2_NUMBITS(nbits), SPI_CTRL2_NUMBITS_MASK); + /* Will we have to do 16- or 8-bit data transfers? */ + + priv->data16 = (nbits > 8); + /* Save the selection so the subsequence re-configurations will be * faster */ - priv->nbits = nbits; + priv->nbits = nbits; } } @@ -1059,7 +1066,7 @@ static int spi_hwfeatures(struct spi_dev_s *dev, spi_hwfeatures_t features) } #endif -/************************************************************************************ +/**************************************************************************** * Name: spi_send * * Description: @@ -1073,141 +1080,22 @@ static int spi_hwfeatures(struct spi_dev_s *dev, spi_hwfeatures_t features) * Returned Value: * response * - ************************************************************************************/ + ****************************************************************************/ static uint16_t spi_send(struct spi_dev_s *dev, uint16_t wd) { - struct max326_spidev_s *priv = (struct max326_spidev_s *)dev; - uint32_t regval; uint16_t ret; - DEBUGASSERT(priv && priv->spibase); - - spi_writeword(priv, wd); - ret = spi_readword(priv); - - /* Check and clear any error flags (Reading from the SR clears the error flags) */ - - regval = spi_getreg(priv, MAX326_SPI_SR_OFFSET); - - spiinfo("Sent: %04x Return: %04x Status: %02x\n", wd, ret, regval); - UNUSED(regval); - + spiinfo("wd=%04u\n", wd); + spi_exchange(dev, &wd, &ret, 1); return ret; } -/************************************************************************************ - * Name: spi_exchange (no DMA). aka spi_exchange_nodma - * - * Description: - * Exchange a block of data on SPI without using DMA - * - * REVISIT: This function could be much more efficient by exploiting (1) RX and TX - * FIFOs and (2) the MAX326 F3 data packing. - * - * Input Parameters: - * dev - Device-specific state data - * txbuffer - A pointer to the buffer of data to be sent - * rxbuffer - A pointer to a buffer in which to receive data - * nwords - the length of data to be exchaned in units of words. - * The wordsize is determined by the number of bits-per-word - * selected for the SPI interface. If nbits <= 8, the data is - * packed into uint8_t's; if nbits >8, the data is packed into - * uint16_t's - * - * Returned Value: - * None - * - ************************************************************************************/ - -#if !defined(CONFIG_MAX326_SPI_DMA) -static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer, void *rxbuffer, - size_t nwords) -#else -static void spi_exchange_nodma(struct spi_dev_s *dev, const void *txbuffer, - void *rxbuffer, size_t nwords) -#endif -{ - struct max326_spidev_s *priv = (struct max326_spidev_s *)dev; - DEBUGASSERT(priv && priv->spibase); - - spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords); - - /* 8- or 16-bit mode? */ - - if (spi_16bitmode(priv)) - { - /* 16-bit mode */ - - const uint16_t *src = (const uint16_t *)txbuffer; - uint16_t *dest = (uint16_t *)rxbuffer; - uint16_t word; - - while (nwords-- > 0) - { - /* Get the next word to write. Is there a source buffer? */ - - if (src) - { - word = *src++; - } - else - { - word = 0xffff; - } - - /* Exchange one word */ - - word = spi_send(dev, word); - - /* Is there a buffer to receive the return value? */ - - if (dest) - { - *dest++ = word; - } - } - } - else - { - /* 8-bit mode */ - - const uint8_t *src = (const uint8_t *)txbuffer; - uint8_t *dest = (uint8_t *)rxbuffer; - uint8_t word; - - while (nwords-- > 0) - { - /* Get the next word to write. Is there a source buffer? */ - - if (src) - { - word = *src++; - } - else - { - word = 0xff; - } - - /* Exchange one word */ - - word = (uint8_t)spi_send(dev, (uint16_t)word); - - /* Is there a buffer to receive the return value? */ - - if (dest) - { - *dest++ = word; - } - } - } -} - /**************************************************************************** - * Name: spi_exchange (with DMA capability) + * Name: spi_exchange * * Description: - * Exchange a block of data on SPI using DMA + * Exchange a block of data on SPI * * Input Parameters: * dev - Device-specific state data @@ -1222,35 +1110,139 @@ static void spi_exchange_nodma(struct spi_dev_s *dev, const void *txbuffer, * Returned Value: * None * - ************************************************************************************/ + ****************************************************************************/ -#ifdef CONFIG_MAX326_SPI_DMA static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer, void *rxbuffer, size_t nwords) { struct max326_spidev_s *priv = (struct max326_spidev_s *)dev; - static uint16_t rxdummy = 0xffff; - static const uint16_t txdummy = 0xffff; +#ifndef CONFIG_MAX326_SPI_INTERRUPTS + uint32_t regval; +#endif + int ret; spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords); - DEBUGASSERT(priv && priv->spibase); + DEBUGASSERT(priv != NULL && nwords > 0 && nwords <= UINT16_MAX); + DEBUGASSERT(txbuffer != NULL || txbuffer != NULL); - /* Setup DMAs */ + /* Setup for the transfer */ - spi_dmarxsetup(priv, rxbuffer, &rxdummy, nwords); - spi_dmatxsetup(priv, txbuffer, &txdummy, nwords); + priv->txbuffer = txbuffer; + priv->rxbuffer = rxbuffer; + priv->txbytes = 0; + priv->rxbytes = 0; + priv->xfrlen = nwords; + priv->busy = false; - /* Start the DMAs */ + /* NOTE: The MAX326 slave select pin is not used. It is controlled as a + * GPIO output from the upper half driver. + */ - spi_dmarxstart(priv); - spi_dmatxstart(priv); + /* Setup the Rx number of words to exchange */ - /* Then wait for each to complete */ + if (rxbuffer != NULL) + { + /* Note: If the SPI port is set to operate in 4-wire mode, the RXNUM + * field is ignored and the TXNUM field is used for both the number + * of words to receive or transmit. + */ - spi_dmarxwait(priv); - spi_dmatxwait(priv); + if (priv->wire3) + { + spi_modify_ctrl1(priv, SPI_CTRL1_RXNUMCH(nwords), + SPI_CTRL1_RXNUMCH_MASK); + } + else + { + spi_modify_ctrl1(priv, SPI_CTRL1_TXNUMCH(nwords), + SPI_CTRL1_TXNUMCH_MASK); + } + + /* Enable the DMA Rx FIFO */ + + spi_modify_dma(priv, SPI_DMA_RXFIFOEN, 0); + } + else + { + /* No Rx data sink */ + + spi_modify_ctrl1(priv, 0, SPI_CTRL1_RXNUMCH_MASK); + spi_modify_dma(priv, 0, SPI_DMA_RXFIFOEN); + } + + /* Four wire mode always operates in full duplex. We must have some Tx + * buffer available in all cases. + */ + + if (!priv->wire3 && txbuffer == NULL) + { + size_t nbytes = priv->data16 ? nwords << 1 : nwords; + + /* We will dual-purpose the the Rx buffer, initialized to zero */ + + memset(priv->rxbuffer, 0, nbytes); + priv->txbuffer = priv->rxbuffer; + } + + /* Set up the number of Tx words to exchange */ + + if (priv->txbuffer != NULL) + { + spi_modify_ctrl1(priv, SPI_CTRL1_TXNUMCH(nwords), + SPI_CTRL1_TXNUMCH_MASK); + + /* Enable the DMA Tx FIFO */ + + spi_modify_dma(priv, SPI_DMA_TXFIFOEN, 0); + } + else + { + /* No Tx data sourc */ + + spi_modify_dma(priv, 0, SPI_DMA_TXFIFOEN); + } + + /* Flush the Rx and Tx FIFOs */ + + spi_modify_dma(priv, SPI_DMA_TXFIFOCLR | SPI_DMA_RXFIFOCLR, 0); + + /* Clear pending interrupts */ + + regval = spi_getreg(priv, MAX326_SPI_INTFL_OFFSET); + spi_putreg(priv, MAX326_SPI_INTFL_OFFSET, regval); + + /* Re-enable SPI to start the next transfer */ + + spi_modify_ctrl0(priv, 0, SPI_CTRL0_SPIEN); + + /* Poll one time to setup the transfer */ + + ret = spi_poll(priv); + DEBUGASSERT(ret == OK || ret == -EBUSY); + + /* Initiate data transmission */ + + spi_modify_ctrl0(priv, 0, SPI_CTRL0_START); + priv->busy = true; + +#ifndef CONFIG_MAX326_SPI_INTERRUPTS + /* Poll repeatedly until the transfer is complete */ + + do + { + ret = spi_poll(priv); + } + while (ret != OK); + + /* Then wait for the master done interrupt */ + + do + { + regval = spi_getreg(priv, MAX326_SPI_INTFL_OFFSET); + } + while ((regval & SPI_INT_MDONE) == 0); +#endif } -#endif /* CONFIG_MAX326_SPI_DMA */ /**************************************************************************** * Name: spi_sndblock @@ -1270,17 +1262,17 @@ static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer, void *rxbu * Returned Value: * None * - ************************************************************************************/ + ****************************************************************************/ #ifndef CONFIG_SPI_EXCHANGE static void spi_sndblock(struct spi_dev_s *dev, const void *txbuffer, size_t nwords) { spiinfo("txbuffer=%p nwords=%d\n", txbuffer, nwords); - return spi_exchange(dev, txbuffer, NULL, nwords); + spi_exchange(dev, txbuffer, NULL, nwords); } #endif -/************************************************************************************ +/**************************************************************************** * Name: spi_recvblock * * Description: @@ -1298,17 +1290,17 @@ static void spi_sndblock(struct spi_dev_s *dev, const void *txbuffer, size_t nwo * Returned Value: * None * - ************************************************************************************/ + ****************************************************************************/ #ifndef CONFIG_SPI_EXCHANGE static void spi_recvblock(struct spi_dev_s *dev, void *rxbuffer, size_t nwords) { spiinfo("rxbuffer=%p nwords=%d\n", rxbuffer, nwords); - return spi_exchange(dev, NULL, rxbuffer, nwords); + spi_exchange(dev, NULL, rxbuffer, nwords); } #endif -/************************************************************************************ +/**************************************************************************** * Name: spi_bus_initialize * * Description: @@ -1321,7 +1313,7 @@ static void spi_recvblock(struct spi_dev_s *dev, void *rxbuffer, size_t nwords) * Returned Value: * None * - ************************************************************************************/ + ****************************************************************************/ static void spi_bus_initialize(struct max326_spidev_s *priv) { @@ -1356,46 +1348,18 @@ static void spi_bus_initialize(struct max326_spidev_s *priv) spi_setfrequency((struct spi_dev_s *)priv, 400000); + /* Set up 3- or 4-pin mode */ + + regval = priv->wire3 ? SPI_CTRL2_DATWIDTH_SINGLE : SPI_CTRL2_DATWIDTH_DUAL; + spi_modify_ctrl2(priv, regval, SPI_CTRL2_DATWIDTH_MASK); + /* Initialize the SPI semaphore that enforces mutually exclusive access */ nxsem_init(&priv->exclsem, 0, 1); -#ifdef CONFIG_MAX326_SPI_DMA - /* Initialize the SPI semaphores that is used to wait for DMA completion. - * This semaphore is used for signaling and, hence, should not have - * priority inheritance enabled. - */ - - if (priv->rxch && priv->txch) - { - nxsem_init(&priv->rxsem, 0, 0); - nxsem_init(&priv->txsem, 0, 0); - - nxsem_setprotocol(&priv->rxsem, SEM_PRIO_NONE); - nxsem_setprotocol(&priv->txsem, SEM_PRIO_NONE); - - /* Get DMA channels. NOTE: max326_dmachannel() will always assign the DMA - * channel. If the channel is not available, then max326_dmachannel() will - * block and wait until the channel becomes available. WARNING: If you have - * another device sharing a DMA channel with SPI and the code never releases - * that channel, then the call to max326_dmachannel() will hang forever in - * this function! Don't let your design do that! - */ - - priv->rxdma = max326_dma_channel(); - priv->txdma = max326_dma_channel(); - DEBUGASSERT(priv->rxdma != NULL && priv->txdma != NULL); - } - else - { - priv->rxdma = NULL; - priv->txdma = NULL; - } -#endif - /* Disable all interrupts at the peripheral */ - spi_putreg(0, MAX326_SPI_INTEN_OFFSET, regval); + spi_putreg(0, MAX326_SPI_INTEN_OFFSET, 0); /* Clear pending interrupts */ @@ -1415,11 +1379,11 @@ static void spi_bus_initialize(struct max326_spidev_s *priv) #endif } -/************************************************************************************ +/**************************************************************************** * Public Functions - ************************************************************************************/ + ****************************************************************************/ -/************************************************************************************ +/**************************************************************************** * Name: max326_spibus_initialize * * Description: @@ -1431,7 +1395,7 @@ static void spi_bus_initialize(struct max326_spidev_s *priv) * Returned Value: * Valid SPI device structure reference on success; a NULL on failure * - ************************************************************************************/ + ****************************************************************************/ struct spi_dev_s *max326_spibus_initialize(int bus) { diff --git a/configs/max32660-evsys/src/Makefile b/configs/max32660-evsys/src/Makefile index cb7114bb07..68e4fd8eab 100644 --- a/configs/max32660-evsys/src/Makefile +++ b/configs/max32660-evsys/src/Makefile @@ -54,7 +54,7 @@ ifeq ($(CONFIG_ARCH_BUTTONS),y) CSRCS += max326_button.c endif -ifeq ($(CONFIG_MAX326XX_HAVE_I2CM),y) +ifeq ($(CONFIG_MAX326XX_HAVE_SPIM),y) CSRCS += max326_spi.c endif diff --git a/configs/max32660-evsys/src/max32660-evsys.h b/configs/max32660-evsys/src/max32660-evsys.h index 42e2e907ef..ab232ff4e4 100644 --- a/configs/max32660-evsys/src/max32660-evsys.h +++ b/configs/max32660-evsys/src/max32660-evsys.h @@ -83,4 +83,16 @@ int max326_bringup(void); +/************************************************************************************ + * Name: max326_spidev_initialize + * + * Description: + * Called to configure SPI chip select GPIO pins for the MAX3660-EVSYS board. + * + ************************************************************************************/ + +#ifdef CONFIG_MAX326XX_HAVE_SPIM +void max326_spidev_initialize(void); +#endif + #endif /* __CONFIG_NUCLEO_F303ZE_INCLUDE_BOARD_H */ diff --git a/configs/max32660-evsys/src/max326_boot.c b/configs/max32660-evsys/src/max326_boot.c index a1eeb0909f..1713598e6e 100644 --- a/configs/max32660-evsys/src/max326_boot.c +++ b/configs/max32660-evsys/src/max326_boot.c @@ -69,6 +69,12 @@ void max326_board_initialize(void) { +#ifdef CONFIG_MAX326XX_HAVE_SPIM + /* Configure SPI chip select pins */ + + max326_spidev_initialize(); +#endif + #ifdef CONFIG_ARCH_LEDS /* Configure on-board LEDs if LED support has been selected. */ diff --git a/configs/max32660-evsys/src/max326_spi.c b/configs/max32660-evsys/src/max326_spi.c new file mode 100644 index 0000000000..9fc2c47ec1 --- /dev/null +++ b/configs/max32660-evsys/src/max326_spi.c @@ -0,0 +1,172 @@ +/************************************************************************************ + * configs/max32660-evsys/src/ma326_spi.c + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include + +#include "up_arch.h" +#include "max326_spim.h" + +#include "max32660-evsys.h" + +#ifdef CONFIG_MAX326XX_HAVE_SPIM + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: max326_spidev_initialize + * + * Description: + * Called to configure SPI chip select GPIO pins for the MAX3660-EVSYS board. + * + ************************************************************************************/ + +void max326_spidev_initialize(void) +{ +#ifdef CONFIG_MAX326XX_SPIM0 +#endif +#ifdef CONFIG_MAX326XX_SPIM1 +#endif +} + +/**************************************************************************** + * Name: max326_spi0/1select and max326_spi0/1status + * + * Description: + * The external functions, max326_spi0/1select and max326_spi0/1status must be + * provided by board-specific logic. They are implementations of the select + * and status methods of the SPI interface defined by struct spi_ops_s (see + * include/nuttx/spi/spi.h). All other methods (including max326_spibus_initialize()) + * are provided by common STM32 logic. To use this common SPI logic on your + * board: + * + * 1. Provide logic in max326_boardinitialize() to configure SPI chip select + * pins. + * 2. Provide max326_spi0/1select() and max326_spi0/1status() functions in your + * board-specific logic. These functions will perform chip selection and + * status operations using GPIOs in the way your board is configured. + * 3. Add a calls to max326_spibus_initialize() in your low level application + * initialization logic + * 4. The handle returned by max326_spibus_initialize() may then be used to bind the + * SPI driver to higher level logic (e.g., calling + * mmcsd_spislotinitialize(), for example, will bind the SPI driver to + * the SPI MMC/SD driver). + * + ****************************************************************************/ + +#ifdef CONFIG_MAX326XX_SPIM0 +void max326_spi0select(FAR struct spi_dev_s *dev, uint32_t devid, bool selected) +{ + spiinfo("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert"); +} + +uint8_t max326_spi0status(FAR struct spi_dev_s *dev, uint32_t devid) +{ + return 0; +} +#endif + +#ifdef CONFIG_MAX326XX_SPIM1 +void max326_spi1select(FAR struct spi_dev_s *dev, uint32_t devid, bool selected) +{ + spiinfo("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert"); +} + +uint8_t max326_spi1status(FAR struct spi_dev_s *dev, uint32_t devid) +{ + return 0; +} +#endif + +/**************************************************************************** + * Name: max326_spi0/1cmddata + * + * Description: + * Set or clear the SH1101A A0 or SD1306 D/C n bit to select data (true) + * or command (false). This function must be provided by platform-specific + * logic. This is an implementation of the cmddata method of the SPI + * interface defined by struct spi_ops_s (see include/nuttx/spi/spi.h). + * + * Input Parameters: + * + * spi - SPI device that controls the bus the device that requires the CMD/ + * DATA selection. + * devid - If there are multiple devices on the bus, this selects which one + * to select cmd or data. NOTE: This design restricts, for example, + * one one SPI display per SPI bus. + * cmd - true: select command; false: select data + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_SPI_CMDDATA +#ifdef CONFIG_MAX326XX_SPIM0 +int max326_spi0cmddata(FAR struct spi_dev_s *dev, uint32_t devid, bool cmd) +{ + return -ENODEV; +} +#endif + +#ifdef CONFIG_MAX326XX_SPIM1 +int max326_spi1cmddata(FAR struct spi_dev_s *dev, uint32_t devid, bool cmd) +{ + return -ENODEV; +} +#endif + +#ifdef CONFIG_STM32_SPI3 +int max326_spi3cmddata(FAR struct spi_dev_s *dev, uint32_t devid, bool cmd) +{ + return -ENODEV; +} +#endif +#endif /* CONFIG_SPI_CMDDATA */ + +#endif /* CONFIG_MAX326XX_HAVE_SPIM */