From c8c470271f83bc190144dc368425d05abf45f1ef Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Mon, 10 Aug 2015 10:05:00 -0600 Subject: [PATCH] SAMV7 SPI Slave: Completes implementation basic, no-DMA driver --- arch/arm/src/samv7/chip/sam_spi.h | 4 +- arch/arm/src/samv7/chip/samv71_pinmap.h | 2 + arch/arm/src/samv7/sam_spi_slave.c | 777 +++++++++++++----------- 3 files changed, 430 insertions(+), 353 deletions(-) diff --git a/arch/arm/src/samv7/chip/sam_spi.h b/arch/arm/src/samv7/chip/sam_spi.h index 39150544675..fe738b2e184 100644 --- a/arch/arm/src/samv7/chip/sam_spi.h +++ b/arch/arm/src/samv7/chip/sam_spi.h @@ -171,7 +171,9 @@ #define SPI_INT_NSSR (1 << 8) /* Bit 8: NSS Rising Interrupt */ #define SPI_INT_TXEMPTY (1 << 9) /* Bit 9: Transmission Registers Empty Interrupt */ #define SPI_INT_UNDES (1 << 10) /* Bit 10: Underrun Error Status Interrupt (slave) */ -#define SPI_SR_SPIENS (1 << 16) /* Bit 16: SPI Enable Status (SR only) */ +#define SPI_SR_SPIENS (1 << 16) /* Bit 16: SPI Enable Status (SR only) */ + +#define SPI_INT_ALL (0x0000070f) /* SPI Chip Select Registers 0-3 */ diff --git a/arch/arm/src/samv7/chip/samv71_pinmap.h b/arch/arm/src/samv7/chip/samv71_pinmap.h index 14ba4a8098f..cb9942f2d2a 100644 --- a/arch/arm/src/samv7/chip/samv71_pinmap.h +++ b/arch/arm/src/samv7/chip/samv71_pinmap.h @@ -399,6 +399,7 @@ #define GPIO_SPI0_MOSI (GPIO_PERIPHB | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN21) #define GPIO_SPI0_SPCK (GPIO_PERIPHB | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN22) +#define GPIO_SPI0_NSS (GPIO_PERIPHD | GPIO_CFG_DEFAULT | GPIO_PORT_PIOB | GPIO_PIN2) #define GPIO_SPI0_NPCS0 (GPIO_PERIPHD | GPIO_CFG_DEFAULT | GPIO_PORT_PIOB | GPIO_PIN2) #define GPIO_SPI0_NPCS1_1 (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOA | GPIO_PIN31) #define GPIO_SPI0_NPCS1_2 (GPIO_PERIPHB | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN25) @@ -409,6 +410,7 @@ #define GPIO_SPI1_MOSI (GPIO_PERIPHC | GPIO_CFG_DEFAULT | GPIO_PORT_PIOC | GPIO_PIN27) #define GPIO_SPI1_SPCK (GPIO_PERIPHC | GPIO_CFG_DEFAULT | GPIO_PORT_PIOC | GPIO_PIN24) +#define GPIO_SPI1_NSS (GPIO_PERIPHC | GPIO_CFG_DEFAULT | GPIO_PORT_PIOC | GPIO_PIN25) #define GPIO_SPI1_NPCS0 (GPIO_PERIPHC | GPIO_CFG_DEFAULT | GPIO_PORT_PIOC | GPIO_PIN25) #define GPIO_SPI1_NPCS1_1 (GPIO_PERIPHC | GPIO_CFG_DEFAULT | GPIO_PORT_PIOC | GPIO_PIN28) #define GPIO_SPI1_NPCS1_2 (GPIO_PERIPHC | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN0) diff --git a/arch/arm/src/samv7/sam_spi_slave.c b/arch/arm/src/samv7/sam_spi_slave.c index 8b2353b21e1..c8a0200e67c 100644 --- a/arch/arm/src/samv7/sam_spi_slave.c +++ b/arch/arm/src/samv7/sam_spi_slave.c @@ -73,41 +73,6 @@ # define CONFIG_SAMV7_SPI_SLAVE_QSIZE 8 #endif -/* Pin configurations ******************************************************/ -/* We don't know which ones must be defined until run time */ - -#ifndef GPIO_SPI0_NPCS0 -# define GPIO_SPI0_NPCS0 0 -#endif - -#ifndef GPIO_SPI0_NPCS1 -# define GPIO_SPI0_NPCS1 0 -#endif - -#ifndef GPIO_SPI0_NPCS2 -# define GPIO_SPI0_NPCS2 0 -#endif - -#ifndef GPIO_SPI0_NPCS3 -# define GPIO_SPI0_NPCS3 0 -#endif - -#ifndef GPIO_SPI1_NPCS0 -# define GPIO_SPI1_NPCS0 0 -#endif - -#ifndef GPIO_SPI1_NPCS1 -# define GPIO_SPI1_NPCS1 0 -#endif - -#ifndef GPIO_SPI1_NPCS2 -# define GPIO_SPI1_NPCS2 0 -#endif - -#ifndef GPIO_SPI1_NPCS3 -# define GPIO_SPI1_NPCS3 0 -#endif - /* Debug *******************************************************************/ /* Check if SPI debug is enabled (non-standard.. no support in * include/debug.h @@ -134,20 +99,23 @@ * Private Types ****************************************************************************/ -/* The state of the one SPI chip select */ +/* The overall state of one SPI controller */ -struct sam_spics_s +struct sam_spidev_s { struct spi_sctrlr_s sctrlr; /* Externally visible part of the SPI slave * controller interface */ - FAR struct spi_sdev_s *sdev; /* Bound SPI slave device interface */ - gpio_pinset_t csconfig; /* Chip select pin configuration */ - + struct spi_sdev_s *sdev; /* Bound SPI slave device interface */ + xcpt_t handler; /* SPI interrupt handler */ + uint32_t base; /* SPI controller register base address */ + sem_t spisem; /* Assures mutually exclusive access to SPI */ + uint16_t outval; /* Default shift-out value */ + uint16_t irq; /* SPI IRQ number */ uint8_t mode; /* Mode 0,1,2,3 */ uint8_t nbits; /* Width of word in bits (8 to 16) */ uint8_t spino; /* SPI controller number (0 or 1) */ - uint8_t csno; /* Chip select number */ - uint16_t outval; /* Default shift-out value */ + bool initialized; /* True: Controller has been initialized */ + bool nss; /* True: Chip selected */ /* Output queue */ @@ -155,17 +123,6 @@ struct sam_spics_s uint8_t tail; /* Index of first value */ uint16_t outq[CONFIG_SAMV7_SPI_SLAVE_QSIZE]; -}; - -/* 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 access to SPI */ - bool initialized; /* TRUE: Controller has been initialized */ - int16_t irq; /* SPI IRQ number */ - xcpt_t handler; /* SPI interrupt handler */ /* Debug stuff */ @@ -184,55 +141,53 @@ struct sam_spidev_s /* Helpers */ #ifdef CONFIG_SAMV7_SPI_REGDEBUG -static bool spi_checkreg(struct sam_spidev_s *spidev, bool wr, +static bool spi_checkreg(struct sam_spidev_s *priv, bool wr, uint32_t value, uint32_t address); #else -# define spi_checkreg(spidev,wr,value,address) (false) +# define spi_checkreg(priv,wr,value,address) (false) #endif -static inline uint32_t spi_getreg(struct sam_spidev_s *spidev, +static uint32_t spi_getreg(struct sam_spidev_s *priv, unsigned int offset); -static inline void spi_putreg(struct sam_spidev_s *spidev, uint32_t value, +static void spi_putreg(struct sam_spidev_s *priv, 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(struct sam_spidev_s *spidev, const char *msg); +static void spi_dumpregs(struct sam_spidev_s *priv, const char *msg); #else -# define spi_dumpregs(spidev,msg) +# define spi_dumpregs(priv,msg) #endif -static void spi_semtake(struct sam_spidev_s *spidev); -#define spi_semgive(spidev) (sem_post(&(spidev)->spisem)) +static void spi_semtake(struct sam_spidev_s *priv); +#define spi_semgive(priv) (sem_post(&(priv)->spisem)) /* Interrupt Handling */ -static int spi_interrupt(FAR struct sam_spidev_s *spidev); +static int spi_interrupt(struct sam_spidev_s *priv); #ifdef CONFIG_SAMV7_SPI0_SLAVE -static int spi0_interrupt(int irq, FAR void *context); +static int spi0_interrupt(int irq, void *context); #endif #ifdef CONFIG_SAMV7_SPI1_SLAVE -static int spi1_interrupt(int irq, FAR void *context); +static int spi1_interrupt(int irq, void *context); #endif /* SPI Helpers */ -static inline void spi_flush(struct sam_spidev_s *spidev); -static inline uint32_t spi_cs2pcs(struct sam_spics_s *spics); -static void spi_setmode(FAR struct sam_spics_s *spics, - FAR struct sam_spidev_s *spidev, enum spi_smode_e mode); -static void spi_setbits(FAR struct sam_spics_s *spics, - FAR struct sam_spidev_s *spidev, int nbits); +static uint16_t spi_dequeue(struct sam_spidev_s *priv); +static void spi_setmode(struct sam_spidev_s *priv, + enum spi_smode_e mode); +static void spi_setbits(struct sam_spidev_s *priv, + int nbits); /* SPI slave controller methods */ -static void spi_bind(FAR struct spi_sctrlr_s *sctrlr, - FAR struct spi_sdev_s *sdev, enum spi_smode_e mode, +static void spi_bind(struct spi_sctrlr_s *sctrlr, + struct spi_sdev_s *sdev, enum spi_smode_e mode, int nbits); -static void spi_unbind(FAR struct spi_sctrlr_s *sctrlr); -static int spi_enqueue(FAR struct spi_sctrlr_s *sctrlr, uint16_t data); -static bool spi_qfull(FAR struct spi_sctrlr_s *sctrlr); -static void spi_qflush(FAR struct spi_sctrlr_s *sctrlr); +static void spi_unbind(struct spi_sctrlr_s *sctrlr); +static int spi_enqueue(struct spi_sctrlr_s *sctrlr, uint16_t data); +static bool spi_qfull(struct spi_sctrlr_s *sctrlr); +static void spi_qflush(struct spi_sctrlr_s *sctrlr); /**************************************************************************** * Private Data @@ -246,22 +201,6 @@ static const uint8_t g_csroffset[4] = SAM_SPI_CSR2_OFFSET, SAM_SPI_CSR3_OFFSET }; -/* CS pin configurations */ - -#ifdef CONFIG_SAMV7_SPI0_SLAVE -static gpio_pinset_t g_spi0_csconfig[SAM_SPI_NCS] = -{ - GPIO_SPI0_NPCS0, GPIO_SPI0_NPCS1, GPIO_SPI0_NPCS2, GPIO_SPI0_NPCS3 -}; -#endif - -#ifdef CONFIG_SAMV7_SPI1_SLAVE -static gpio_pinset_t g_spi1_csconfig[SAM_SPI_NCS] = -{ - GPIO_SPI1_NPCS0, GPIO_SPI1_NPCS1, GPIO_SPI1_NPCS2, GPIO_SPI1_NPCS3 -}; -#endif - /* SPI slave controller driver operations */ static const struct spi_sctrlrops_s g_sctrlr_ops = @@ -310,35 +249,35 @@ static struct sam_spidev_s g_spi1_sctrlr; ****************************************************************************/ #ifdef CONFIG_SAMV7_SPI_REGDEBUG -static bool spi_checkreg(struct sam_spidev_s *spidev, bool wr, uint32_t value, +static bool spi_checkreg(struct sam_spidev_s *priv, bool wr, uint32_t value, uint32_t address) { - if (wr == spidev->wrlast && /* Same kind of access? */ - value == spidev->valuelast && /* Same value? */ - address == spidev->addresslast) /* Same address? */ + if (wr == priv->wrlast && /* Same kind of access? */ + value == priv->valuelast && /* Same value? */ + address == priv->addresslast) /* Same address? */ { /* Yes, then just keep a count of the number of times we did this. */ - spidev->ntimes++; + priv->ntimes++; return false; } else { /* Did we do the previous operation more than once? */ - if (spidev->ntimes > 0) + if (priv->ntimes > 0) { /* Yes... show how many times we did it */ - lldbg("...[Repeats %d times]...\n", spidev->ntimes); + lldbg("...[Repeats %d times]...\n", priv->ntimes); } /* Save information about the new access */ - spidev->wrlast = wr; - spidev->valuelast = value; - spidev->addresslast = address; - spidev->ntimes = 0; + priv->wrlast = wr; + priv->valuelast = value; + priv->addresslast = address; + priv->ntimes = 0; } /* Return true if this is the first time that we have done this operation */ @@ -355,14 +294,13 @@ static bool spi_checkreg(struct sam_spidev_s *spidev, bool wr, uint32_t value, * ****************************************************************************/ -static inline uint32_t spi_getreg(struct sam_spidev_s *spidev, - unsigned int offset) +static uint32_t spi_getreg(struct sam_spidev_s *priv, unsigned int offset) { - uint32_t address = spidev->base + offset; + uint32_t address = priv->base + offset; uint32_t value = getreg32(address); #ifdef CONFIG_SAMV7_SPI_REGDEBUG - if (spi_checkreg(spidev, false, value, address)) + if (spi_checkreg(priv, false, value, address)) { lldbg("%08x->%08x\n", address, value); } @@ -379,13 +317,13 @@ static inline uint32_t spi_getreg(struct sam_spidev_s *spidev, * ****************************************************************************/ -static inline void spi_putreg(struct sam_spidev_s *spidev, uint32_t value, - unsigned int offset) +static void spi_putreg(struct sam_spidev_s *priv, uint32_t value, + unsigned int offset) { - uint32_t address = spidev->base + offset; + uint32_t address = priv->base + offset; #ifdef CONFIG_SAMV7_SPI_REGDEBUG - if (spi_checkreg(spidev, true, value, address)) + if (spi_checkreg(priv, true, value, address)) { lldbg("%08x<-%08x\n", address, value); } @@ -401,7 +339,7 @@ static inline void spi_putreg(struct sam_spidev_s *spidev, uint32_t value, * Dump the contents of all SPI registers * * Input Parameters: - * spidev - The SPI controller to dump + * priv - The SPI controller to dump * msg - Message to print before the register data * * Returned Value: @@ -410,44 +348,24 @@ static inline void spi_putreg(struct sam_spidev_s *spidev, uint32_t value, ****************************************************************************/ #if defined(CONFIG_DEBUG_SPI) && defined(CONFIG_DEBUG_VERBOSE) -static void spi_dumpregs(struct sam_spidev_s *spidev, const char *msg) +static void spi_dumpregs(struct sam_spidev_s *priv, const char *msg) { spivdbg("%s:\n", msg); spivdbg(" MR:%08x SR:%08x IMR:%08x\n", - getreg32(spidev->base + SAM_SPI_MR_OFFSET), - getreg32(spidev->base + SAM_SPI_SR_OFFSET), - getreg32(spidev->base + SAM_SPI_IMR_OFFSET)); + getreg32(priv->base + SAM_SPI_MR_OFFSET), + getreg32(priv->base + SAM_SPI_SR_OFFSET), + getreg32(priv->base + SAM_SPI_IMR_OFFSET)); spivdbg(" CSR0:%08x CSR1:%08x CSR2:%08x CSR3:%08x\n", - getreg32(spidev->base + SAM_SPI_CSR0_OFFSET), - getreg32(spidev->base + SAM_SPI_CSR1_OFFSET), - getreg32(spidev->base + SAM_SPI_CSR2_OFFSET), - getreg32(spidev->base + SAM_SPI_CSR3_OFFSET)); + getreg32(priv->base + SAM_SPI_CSR0_OFFSET), + getreg32(priv->base + SAM_SPI_CSR1_OFFSET), + getreg32(priv->base + SAM_SPI_CSR2_OFFSET), + getreg32(priv->base + SAM_SPI_CSR3_OFFSET)); spivdbg(" WPCR:%08x WPSR:%08x\n", - getreg32(spidev->base + SAM_SPI_WPCR_OFFSET), - getreg32(spidev->base + SAM_SPI_WPSR_OFFSET)); + getreg32(priv->base + SAM_SPI_WPCR_OFFSET), + getreg32(priv->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_SAMV7_SPI0_SLAVE) && defined(CONFIG_SAMV7_SPI1_SLAVE) - return spics->spino ? &g_spi1_sctrlr : &g_spi0_sctrlr; -#elif defined(CONFIG_SAMV7_SPI0_SLAVE) - return &g_spi0_sctrlr; -#else - return &g_spi1_sctrlr; -#endif -} - /**************************************************************************** * Name: spi_semtake * @@ -456,14 +374,14 @@ static inline struct sam_spidev_s *spi_device(struct sam_spics_s *spics) * resources, handling any exceptional conditions * * Input Parameters: - * spidev - A reference to the MCAN peripheral state + * priv - A reference to the MCAN peripheral state * * Returned Value: * None * ****************************************************************************/ -static void spi_semtake(struct sam_spidev_s *spidev) +static void spi_semtake(struct sam_spidev_s *priv) { int ret; @@ -474,29 +392,161 @@ static void spi_semtake(struct sam_spidev_s *spidev) do { - ret = sem_wait(&spidev->spisem); + ret = sem_wait(&priv->spisem); DEBUGASSERT(ret == 0 || errno == EINTR); } while (ret < 0); } /**************************************************************************** - * Name: spi_flush + * Name: spi_interrupt * * Description: - * Make sure that there are now dangling SPI transfer in progress + * Common SPI interrupt handler * * Input Parameters: - * spics - SPI controller CS state + * priv - SPI controller CS state * * Returned Value: * Standard interrupt return value. * ****************************************************************************/ -static int spi_interrupt(FAR struct sam_spidev_s *spidev) +static int spi_interrupt(struct sam_spidev_s *priv) { -#warning Missing logic + uint32_t sr; + uint32_t imr; + uint32_t pending; + uint32_t regval; + + /* Get the current set of pending/enabled interrupts */ + + sr = spi_getreg(priv, SAM_SPI_SR_OFFSET); + imr = spi_getreg(priv, SAM_SPI_IMR_OFFSET); + pending = sr & imr; + + /* TThe SPI waits until NSS goes active before receiving the serial clock + * from an external master. When NSS falls, the clock is validated and the + * data is loaded in the SPI_RDR depending on the BITS field configured in + * the SPI_CSR0. These bits are processed following a phase and a polarity + * defined respectively by the NCPHA and CPOL bits in the SPI_CSR0. + * + * When all bits are processed, the received data is transferred in the + * SPI_RDR and the RDRF bit rises. If the SPI_RDR has not been read before + * new data is received, the Overrun Error Status (OVRES) bit in the SPI_SR + * is set. As long as this flag is set, data is loaded in the SPI_RDR. The + * user must read SPI_SR to clear the OVRES bit. + */ + +#ifdef CONFIG_DEBUG_SPI + /* Check the RX data overflow condition */ + + if ((pending & SPI_INT_OVRES) != 0) + { + /* If debug is enabled, report any overrun errors */ + + spidbg("Error: Overrun (OVRES): %08x\n", pending); + + /* OVRES was cleared by the status read. */ + } +#endif + + /* Check for the availability of RX data */ + + if ((pending & SPI_INT_RDRF) != 0) + { + uint16_t data; + + /* We get no indication of the falling edge of NSS. But if we are + * here then it must have fallen. + */ + + if (priv->nss) + { + priv->nss = false; + SPI_SDEV_SELECT(priv->sdev, true); + } + + /* Read the RDR to get the data and to clear the pending RDRF + * interrupt. + */ + + regval = spi_getreg(priv, SAM_SPI_RDR_OFFSET); + data = (uint16_t)((regval & SPI_RDR_RD_MASK) >> SPI_RDR_RD_SHIFT); + + /* Enable TXDR/OVRE interrupts */ + + regval = (SPI_INT_TDRE | SPI_INT_UNDES); + spi_putreg(priv, regval, SAM_SPI_IER_OFFSET); + + /* Report the receipt of data to the SPI device driver */ + + SPI_SDEV_RECEIVE(priv->sdev, data); + } + + /* When a transfer starts, the data shifted out is the data present in the + * Shift register. If no data has been written in the SPI_TDR, the last + * data received is transferred. If no data has been received since the last + * reset, all bits are transmitted low, as the Shift register resets to 0. + * + * When a first data is written in the SPI_TDR, it is transferred immediately + * in the Shift register and the TDRE flag rises. If new data is written, it + * remains in the SPI_TDR until a transfer occurs, i.e., NSS falls and there + * is a valid clock on the SPCK pin. When the transfer occurs, the last data + * written in the SPI_TDR is transferred in the Shift register and the TDRE + * flag rises. This enables frequent updates of critical variables with single + * transfers. + * + * Then, new data is loaded in the Shift register from the SPI_TDR. If no + * character is ready to be transmitted, i.e., no character has been written in + * the SPI_TDR since the last load from the SPI_TDR to the Shift register, the + * SPI_TDR is retransmitted. In this case the Underrun Error Status Flag + * (UNDES) is set in the SPI_SR. + */ + +#ifdef CONFIG_DEBUG_SPI + /* Check the TX data underflow condition */ + + if ((pending & SPI_INT_UNDES) != 0) + { + /* If debug is enabled, report any overrun errors */ + + spidbg("Error: Underrun (UNDEX): %08x\n", pending); + + /* UNDES was cleared by the status read. */ + } +#endif + + /* Output the next TX data */ + + if ((pending & SPI_INT_TDRE) != 0) + { + /* Get the next output value and write it to the TDR + * The TDRE interrupt is cleared by writing to the from RDR. + */ + + regval = spi_dequeue(priv); + spi_putreg(priv, regval, SAM_SPI_TDR_OFFSET); + } + + /* The SPI slave hardware provides only an event when NSS rises + * which may or many not happen at the end of a transfer. NSSR was + * cleared by the status read. + */ + + if ((pending & SPI_INT_NSSR) != 0) + { + /* Disable further TXDR/OVRE interrupts */ + + regval = (SPI_INT_TDRE | SPI_INT_UNDES); + spi_putreg(priv, regval, SAM_SPI_IDR_OFFSET); + + /* Report the state change to the SPI device driver */ + + priv->nss = true; + SPI_SDEV_SELECT(priv->sdev, false); + } + return OK; } @@ -515,7 +565,7 @@ static int spi_interrupt(FAR struct sam_spidev_s *spidev) ****************************************************************************/ #ifdef CONFIG_SAMV7_SPI0_SLAVE -static int spi0_interrupt(int irq, FAR void *context) +static int spi0_interrupt(int irq, void *context) { return spi_interrupt(&g_spi0_sctrlr); } @@ -536,71 +586,77 @@ static int spi0_interrupt(int irq, FAR void *context) ****************************************************************************/ #ifdef CONFIG_SAMV7_SPI1_SLAVE -static int spi1_interrupt(int irq, FAR void *context) +static int spi1_interrupt(int irq, void *context) { return spi_interrupt(&g_spi1_sctrlr); } #endif /**************************************************************************** - * Name: spi_flush + * Name: spi_dequeue * * Description: - * Make sure that there are now dangling SPI transfer in progress + * Return the next queued output value. If nothing is in the output queue, + * then return the last value obtained from getdata(); * * Input Parameters: - * spidev - SPI controller state + * priv - SPI controller CS state * - * Returned Value: - * None + * Assumptions: + * Called only from the SPI interrupt handler so all interrupts are + * disabled. * ****************************************************************************/ -static inline void spi_flush(struct sam_spidev_s *spidev) +static uint16_t spi_dequeue(struct sam_spidev_s *priv) { - /* Make sure the no TX activity is in progress... waiting if necessary */ + uint32_t regval; + uint16_t ret; + int next; - while ((spi_getreg(spidev, SAM_SPI_SR_OFFSET) & SPI_INT_TXEMPTY) == 0); + /* Is the queue empty? */ - /* Then make sure that there is no pending RX data .. reading as - * discarding as necessary. - */ - - while ((spi_getreg(spidev, SAM_SPI_SR_OFFSET) & SPI_INT_RDRF) != 0) + if (priv->head != priv->tail) { - (void)spi_getreg(spidev, SAM_SPI_RDR_OFFSET); + /* No, take the oldest value from the tail of the cicular buffer */ + + ret = priv->outq[priv->tail]; + + /* Update the tail index, handling wraparound */ + + next = priv->tail + 1; + if (next >= CONFIG_SAMV7_SPI_SLAVE_QSIZE) + { + next = 0; + } + + priv->tail = next; + + /* If the queue is empty Disable further TXDR/OVRE interrupts until + * spi_enqueue() is called or until we received another command. We + * do this only for the case where NSS is non-functional (tied to + * ground) and we need to end transfers in some fashion. + */ + + if (priv->head == next) + { + regval = (SPI_INT_TDRE | SPI_INT_UNDES); + spi_putreg(priv, regval, SAM_SPI_IDR_OFFSET); + } } -} + else + { + /* Yes, return the last value we got from the getdata() method */ -/**************************************************************************** - * Name: spi_cs2pcs - * - * Description: - * Map the chip select number to the bit-set PCS field used in the SPI - * registers. A chip select number is used for indexing and identifying - * chip selects. However, the chip select information is represented by - * a bit set in the SPI registers. This function maps those chip select - * numbers to the correct bit set: - * - * CS Returned Spec Effective - * No. PCS Value NPCS - * ---- -------- -------- -------- - * 0 0000 xxx0 1110 - * 1 0001 xx01 1101 - * 2 0011 x011 1011 - * 3 0111 0111 0111 - * - * Input Parameters: - * spics - Device-specific state data - * - * Returned Value: - * None - * - ****************************************************************************/ + ret = priv->outval; -static inline uint32_t spi_cs2pcs(struct sam_spics_s *spics) -{ - return ((uint32_t)1 << (spics->csno)) - 1; + /* Disable further TXDR/OVRE interrupts until spi_enqueue() is called. */ + + regval = (SPI_INT_TDRE | SPI_INT_UNDES); + spi_putreg(priv, regval, SAM_SPI_IDR_OFFSET); + } + + return ret; } /**************************************************************************** @@ -610,8 +666,7 @@ static inline uint32_t spi_cs2pcs(struct sam_spics_s *spics) * Set the SPI mode. See enum spi_smode_e for mode definitions * * Input Parameters: - * spics - Chip select data structure - * spidev - SPI device data structure + * priv - SPI device data structure * mode - The SPI mode requested * * Returned Value: @@ -619,17 +674,15 @@ static inline uint32_t spi_cs2pcs(struct sam_spics_s *spics) * ****************************************************************************/ -static void spi_setmode(FAR struct sam_spics_s *spics, - FAR struct sam_spidev_s *spidev, enum spi_smode_e mode) +static void spi_setmode(struct sam_spidev_s *priv, enum spi_smode_e mode) { uint32_t regval; - unsigned int offset; - spivdbg("csno=%d mode=%d\n", spics->csno, mode); + spivdbg("mode=%d\n", mode); /* Has the mode changed? */ - if (mode != spics->mode) + if (mode != priv->mode) { /* Yes... Set the mode appropriately: * @@ -641,8 +694,7 @@ static void spi_setmode(FAR struct sam_spics_s *spics, * 3 1 0 */ - offset = (unsigned int)g_csroffset[spics->csno]; - regval = spi_getreg(spidev, offset); + regval = spi_getreg(priv, SAM_SPI_CSR0_OFFSET); regval &= ~(SPI_CSR_CPOL | SPI_CSR_NCPHA); switch (mode) @@ -667,12 +719,12 @@ static void spi_setmode(FAR struct sam_spics_s *spics, return; } - spi_putreg(spidev, regval, offset); - spivdbg("csr[offset=%02x]=%08x\n", offset, regval); + spi_putreg(priv, regval, SAM_SPI_CSR0_OFFSET); + spivdbg("csr0=%08x\n", regval); /* Save the mode so that subsequent re-configurations will be faster */ - spics->mode = mode; + priv->mode = mode; } } @@ -683,8 +735,7 @@ static void spi_setmode(FAR struct sam_spics_s *spics, * Set the number if bits per word. * * Input Parameters: - * spics - Chip select data structure - * spidev - SPI device data structure + * priv - SPI device data structure * nbits - The number of bits requests * * Returned Value: @@ -692,32 +743,29 @@ static void spi_setmode(FAR struct sam_spics_s *spics, * ****************************************************************************/ -static void spi_setbits(FAR struct sam_spics_s *spics, - FAR struct sam_spidev_s *spidev, int nbits) +static void spi_setbits(struct sam_spidev_s *priv, int nbits) { uint32_t regval; - unsigned int offset; - spivdbg("csno=%d nbits=%d\n", spics->csno, nbits); - DEBUGASSERT(spics && nbits > 7 && nbits < 17); + spivdbg("nbits=%d\n", nbits); + DEBUGASSERT(priv && nbits > 7 && nbits < 17); /* Has the number of bits changed? */ - if (nbits != spics->nbits) + if (nbits != priv->nbits) { /* Yes... Set number of bits appropriately */ - offset = (unsigned int)g_csroffset[spics->csno]; - regval = spi_getreg(spidev, offset); + regval = spi_getreg(priv, SAM_SPI_CSR0_OFFSET); regval &= ~SPI_CSR_BITS_MASK; regval |= SPI_CSR_BITS(nbits); - spi_putreg(spidev, regval, offset); + spi_putreg(priv, regval, SAM_SPI_CSR0_OFFSET); - spivdbg("csr[offset=%02x]=%08x\n", offset, regval); + spivdbg("csr0=%08x\n", regval); /* Save the selection so the subsequence re-configurations will be faster */ - spics->nbits = nbits; + priv->nbits = nbits; } } @@ -743,56 +791,92 @@ static void spi_setbits(FAR struct sam_spics_s *spics, * ****************************************************************************/ -static void spi_bind(FAR struct spi_sctrlr_s *sctrlr, - FAR struct spi_sdev_s *sdev, enum spi_smode_e mode, +static void spi_bind(struct spi_sctrlr_s *sctrlr, + struct spi_sdev_s *sdev, enum spi_smode_e mode, int nbits) { - FAR struct sam_spics_s *spics = (FAR struct sam_spics_s *)sctrlr; - FAR struct sam_spidev_s *spidev; + struct sam_spidev_s *priv = (struct sam_spidev_s *)sctrlr; + uint32_t regval; spivdbg("sdev=%p mode=%d nbits=%d\n", sdv, mode, nbits); - DEBUGASSERT(spics != NULL && spics->sdev == NULL && sdev != NULL); - spidev = spi_device(spics); + DEBUGASSERT(priv != NULL && priv->sdev == NULL && sdev != NULL); /* Get exclusive access to the SPI device */ - spi_semtake(spidev); + spi_semtake(priv); /* Bind the SPI slave device interface instance to the SPI slave * controller interface. */ - spics->sdev = sdev; + priv->sdev = sdev; - /* Call the slaved device's cmddata() method to indicate the initial - * state of any command/data selection. + /* Call the slaved device's select() and cmddata() methods to indicate + * the initial state of the chip select and command/data discretes. + * + * NOTE: Unless we reconfigure the NSS GPIO pin, it may not be possible + * to read the NSS pin value (I haven't actually tried just reading it). + * And, since the is no interrupt on the falling edge of NSS, we get no + * notification when we are selected... not until the arrival of data. + * + * REVISIT: A board-level interface would be required in order to support + * the Command/Data indication (not yet impklemented). */ + + SPI_SDEV_SELECT(sdev, false); #warning Missing logic SPI_SDEV_CMDDATA(sdev, false); /* Discard any queued data */ - spics->head = 0; - spics->tail = 0; + priv->head = 0; + priv->tail = 0; /* Call the slave device's getdata() method to get the value that will * be shifted out the SPI clock is detected. */ - spics->outval = SPI_SDEV_GETDATA(sdev); + priv->outval = SPI_SDEV_GETDATA(sdev); + spi_putreg(priv, priv->outval, SAM_SPI_TDR_OFFSET); /* Setup to begin normal SPI operation */ - spi_setmode(spics, spidev, mode); - spi_setbits(spics, spidev, nbits); + spi_setmode(priv, mode); + spi_setbits(priv, nbits); -#warning Missing Logic + /* Clear pending interrupts by reading the SPI Status Register */ - /* Enable SPI interrupts (already enabled at the NVIC) */ -#warning Missing Logic + regval = spi_getreg(priv, SAM_SPI_SR_OFFSET); + UNUSED(regval); - spi_semgive(spidev); + /* Enable SPI interrupts (already enabled at the NVIC): + * + * Data Transfer: + * SPI_INT_RDRF - Receive Data Register Full Interrupt + * SPI_INT_TDRE - Transmit Data Register Empty Interrupt + * SPI_INT_NSSR - NSS Rising Interrupt + * + * Transfer Errors (for DEBUG purposes only): + * SPI_INT_OVRES - Overrun Error Interrupt + * SPI_INT_UNDES - Underrun Error Status Interrupt (slave) + * + * Not Used: + * SPI_INT_MODF - Mode Fault Error Interrupt + * SPI_INT_TXEMPTY - Transmission Registers Empty Interrupt + * + * TX interrupts (SPI_INT_TDRE and SPI_INT_UNDES) are not enabled until + * the transfer of data actually starts. + */ + + regval = (SPI_INT_RDRF | SPI_INT_NSSR); +#ifdef CONFIG_DEBUG_SPI + regval |= SPI_INT_OVRES; +#endif + + spi_putreg(priv, regval, SAM_SPI_IER_OFFSET); + + spi_semgive(priv); } /**************************************************************************** @@ -811,32 +895,37 @@ static void spi_bind(FAR struct spi_sctrlr_s *sctrlr, * ****************************************************************************/ -static void spi_unbind(FAR struct spi_sctrlr_s *sctrlr) +static void spi_unbind(struct spi_sctrlr_s *sctrlr) { - FAR struct sam_spics_s *spics = (FAR struct sam_spics_s *)sctrlr; - FAR struct sam_spidev_s *spidev; + struct sam_spidev_s *priv = (struct sam_spidev_s *)sctrlr; - DEBUGASSERT(spics != NULL); - spivdbg("Unbinding %p\n", spics->sdev); + DEBUGASSERT(priv != NULL); + spivdbg("Unbinding %p\n", priv->sdev); - DEBUGASSERT(spics->sdev != NULL); - spidev = spi_device(spics); + DEBUGASSERT(priv->sdev != NULL); /* Get exclusive access to the SPI device */ - spi_semtake(spidev); + spi_semtake(priv); /* Disable SPI interrupts (still enabled at the NVIC) */ -#warning Missing Logic + + spi_putreg(priv, SPI_INT_ALL, SAM_SPI_IDR_OFFSET); /* Unbind the SPI slave interface */ - spics->sdev = NULL; + priv->sdev = NULL; - /* Reset and disable the SPI device */ -#warning Missing Logic + /* Disable the SPI peripheral */ - spi_semgive(spidev); + spi_putreg(priv, SPI_CR_SPIDIS, SAM_SPI_CR_OFFSET); + + /* Execute a software reset of the SPI (twice) */ + + spi_putreg(priv, SPI_CR_SWRST, SAM_SPI_CR_OFFSET); + spi_putreg(priv, SPI_CR_SWRST, SAM_SPI_CR_OFFSET); + + spi_semgive(priv); } /**************************************************************************** @@ -860,22 +949,20 @@ static void spi_unbind(FAR struct spi_sctrlr_s *sctrlr) * ****************************************************************************/ -static int spi_enqueue(FAR struct spi_sctrlr_s *sctrlr, uint16_t data) +static int spi_enqueue(struct spi_sctrlr_s *sctrlr, uint16_t data) { - FAR struct sam_spics_s *spics = (FAR struct sam_spics_s *)sctrlr; - FAR struct sam_spidev_s *spidev; + struct sam_spidev_s *priv = (struct sam_spidev_s *)sctrlr; irqstate_t flags; + uint32_t regval; int next; int ret; spivdbg("data=%04x\n", data); - - DEBUGASSERT(spics != NULL && spics->sdev != NULL); - spidev = spi_device(spics); + DEBUGASSERT(priv != NULL && priv->sdev != NULL); /* Get exclusive access to the SPI device */ - spi_semtake(spidev); + spi_semtake(priv); /* Check if this word would overflow the circular buffer * @@ -883,13 +970,13 @@ static int spi_enqueue(FAR struct spi_sctrlr_s *sctrlr, uint16_t data) */ flags = irqsave(); - next = spics->head + 1; + next = priv->head + 1; if (next >= CONFIG_SAMV7_SPI_SLAVE_QSIZE) { next = 0; } - if (next == spics->tail) + if (next == priv->tail) { ret = -ENOSPC; } @@ -900,13 +987,23 @@ static int spi_enqueue(FAR struct spi_sctrlr_s *sctrlr, uint16_t data) * be overwritten. */ - spics->head = data; - spics->head = next; - ret = OK; + priv->outq[priv->head] = data; + priv->head = next; + ret = OK; + + /* Enable TX interrupts if we have begun the transfer */ + + if (!priv->nss) + { + /* Enable TXDR/OVRE interrupts */ + + regval = (SPI_INT_TDRE | SPI_INT_UNDES); + spi_putreg(priv, regval, SAM_SPI_IER_OFFSET); + } } irqrestore(flags); - spi_semgive(spidev); + spi_semgive(priv); return ret; } @@ -925,20 +1022,18 @@ static int spi_enqueue(FAR struct spi_sctrlr_s *sctrlr, uint16_t data) * ****************************************************************************/ -static bool spi_qfull(FAR struct spi_sctrlr_s *sctrlr) +static bool spi_qfull(struct spi_sctrlr_s *sctrlr) { - FAR struct sam_spics_s *spics = (FAR struct sam_spics_s *)sctrlr; - FAR struct sam_spidev_s *spidev; + struct sam_spidev_s *priv = (struct sam_spidev_s *)sctrlr; irqstate_t flags; int next; bool ret; - DEBUGASSERT(spics != NULL && spics->sdev != NULL); - spidev = spi_device(spics); + DEBUGASSERT(priv != NULL && priv->sdev != NULL); /* Get exclusive access to the SPI device */ - spi_semtake(spidev); + spi_semtake(priv); /* Check if another word would overflow the circular buffer * @@ -946,15 +1041,15 @@ static bool spi_qfull(FAR struct spi_sctrlr_s *sctrlr) */ flags = irqsave(); - next = spics->head + 1; + next = priv->head + 1; if (next >= CONFIG_SAMV7_SPI_SLAVE_QSIZE) { next = 0; } - ret = (next == spics->tail); + ret = (next == priv->tail); irqrestore(flags); - spi_semgive(spidev); + spi_semgive(priv); return ret; } @@ -974,28 +1069,26 @@ static bool spi_qfull(FAR struct spi_sctrlr_s *sctrlr) * ****************************************************************************/ -static void spi_qflush(FAR struct spi_sctrlr_s *sctrlr) +static void spi_qflush(struct spi_sctrlr_s *sctrlr) { - FAR struct sam_spics_s *spics = (FAR struct sam_spics_s *)sctrlr; - FAR struct sam_spidev_s *spidev; + struct sam_spidev_s *priv = (struct sam_spidev_s *)sctrlr; irqstate_t flags; spivdbg("data=%04x\n", data); - DEBUGASSERT(spics != NULL && spics->sdev != NULL); - spidev = spi_device(spics); + DEBUGASSERT(priv != NULL && priv->sdev != NULL); /* Get exclusive access to the SPI device */ - spi_semtake(spidev); + spi_semtake(priv); /* Mark the buffer empty, momentarily disabling interrupts */ flags = irqsave(); - spics->head = 0; - spics->tail = 0; + priv->head = 0; + priv->tail = 0; irqrestore(flags); - spi_semgive(spidev); + spi_semgive(priv); } /**************************************************************************** @@ -1017,20 +1110,16 @@ static void spi_qflush(FAR struct spi_sctrlr_s *sctrlr) * ****************************************************************************/ -FAR struct spi_sctrlr_s *sam_spi_slave_initialize(int port) +struct spi_sctrlr_s *sam_spi_slave_initialize(int port) { - struct sam_spidev_s *spidev; - struct sam_spics_s *spics; - int csno = (port & __SPI_CS_MASK) >> __SPI_CS_SHIFT; + struct sam_spidev_s *priv; int spino = (port & __SPI_SPI_MASK) >> __SPI_SPI_SHIFT; irqstate_t flags; uint32_t regval; - unsigned int offset; /* The support SAM parts have only a single SPI port */ - spivdbg("port: %d csno: %d spino: %d\n", port, csno, spino); - DEBUGASSERT(csno >= 0 && csno <= SAM_SPI_NCS); + spivdbg("port: %d spino: %d\n", port, spino); #if defined(CONFIG_SAMV7_SPI0_SLAVE) && defined(CONFIG_SAMV7_SPI1_SLAVE) DEBUGASSERT(spino >= 0 && spino <= 1); @@ -1045,8 +1134,8 @@ FAR struct spi_sctrlr_s *sam_spi_slave_initialize(int port) * chip select structures. */ - spics = (struct sam_spics_s *)zalloc(sizeof(struct sam_spics_s)); - if (!spics) + priv = (struct sam_spidev_s *)zalloc(sizeof(struct sam_spidev_s)); + if (!priv) { spidbg("ERROR: Failed to allocate a chip select structure\n"); return NULL; @@ -1058,20 +1147,15 @@ FAR struct spi_sctrlr_s *sam_spi_slave_initialize(int port) /* Initialize the SPI operations */ - spics->sctrlr.ops = &g_sctrlr_ops; + priv->sctrlr.ops = &g_sctrlr_ops; - /* Save the chip select and SPI controller numbers */ + /* Save the SPI controller number */ - spics->csno = csno; - spics->spino = spino; - - /* Get the SPI device structure associated with the chip select */ - - spidev = spi_device(spics); + priv->spino = spino; /* Has the SPI hardware been initialized? */ - if (!spidev->initialized) + if (!priv->initialized) { /* Enable clocking to the SPI block */ @@ -1083,25 +1167,20 @@ FAR struct spi_sctrlr_s *sam_spi_slave_initialize(int port) { /* Set the SPI0 register base address and interrupt information */ - spidev->base = SAM_SPI0_BASE, - spidev->irq = SAM_IRQ_SPI0; - spidev->handler = spi0_interrupt; - - /* Chip select pin configuration */ - - spics->csconfig = g_spi0_csconfig[csno]; + priv->base = SAM_SPI0_BASE, + priv->irq = SAM_IRQ_SPI0; + priv->handler = spi0_interrupt; /* Enable peripheral clocking to SPI0 */ sam_spi0_enableclk(); - /* Configure multiplexed pins as connected on the board. Chip - * select pins must be selected by board-specific logic. - */ + /* Configure multiplexed pins as connected on the board. */ - sam_configgpio(GPIO_SPI0_MISO); - sam_configgpio(GPIO_SPI0_MOSI); - sam_configgpio(GPIO_SPI0_SPCK); + sam_configgpio(GPIO_SPI0_MISO); /* Output */ + sam_configgpio(GPIO_SPI0_MOSI); /* Input */ + sam_configgpio(GPIO_SPI0_SPCK); /* Drives slave */ + sam_configgpio(GPIO_SPI0_NSS); /* aka NPCS0 */ } #endif #if defined(CONFIG_SAMV7_SPI0_SLAVE) && defined(CONFIG_SAMV7_SPI1_SLAVE) @@ -1111,83 +1190,77 @@ FAR struct spi_sctrlr_s *sam_spi_slave_initialize(int port) { /* Set the SPI1 register base address and interrupt information */ - spidev->base = SAM_SPI1_BASE, - spidev->irq = SAM_IRQ_SPI1; - spidev->handler = spi1_interrupt; - - /* Chip select pin configuration */ - - spics->csconfig = g_spi1_csconfig[csno]; + priv->base = SAM_SPI1_BASE, + priv->irq = SAM_IRQ_SPI1; + priv->handler = spi1_interrupt; /* Enable peripheral clocking to SPI1 */ sam_spi1_enableclk(); - /* Configure multiplexed pins as connected on the board. Chip - * select pins must be selected by board-specific logic. - */ + /* Configure multiplexed pins as connected on the board. */ - sam_configgpio(GPIO_SPI1_MISO); - sam_configgpio(GPIO_SPI1_MOSI); - sam_configgpio(GPIO_SPI1_SPCK); + sam_configgpio(GPIO_SPI1_MISO); /* Output */ + sam_configgpio(GPIO_SPI1_MOSI); /* Input */ + sam_configgpio(GPIO_SPI1_SPCK); /* Drives slave */ + sam_configgpio(GPIO_SPI0_NSS); /* aka NPCS0 */ } #endif - /* Configure the CS pin */ + /* Disable the SPI peripheral */ - DEBUGASSERT(spics->csconfig != 0); - sam_configgpio(spics->csconfig); - - /* Disable SPI clocking */ - - spi_putreg(spidev, SPI_CR_SPIDIS, SAM_SPI_CR_OFFSET); + spi_putreg(priv, SPI_CR_SPIDIS, SAM_SPI_CR_OFFSET); /* Execute a software reset of the SPI (twice) */ - spi_putreg(spidev, SPI_CR_SWRST, SAM_SPI_CR_OFFSET); - spi_putreg(spidev, SPI_CR_SWRST, SAM_SPI_CR_OFFSET); + spi_putreg(priv, SPI_CR_SWRST, SAM_SPI_CR_OFFSET); + spi_putreg(priv, SPI_CR_SWRST, SAM_SPI_CR_OFFSET); irqrestore(flags); /* Configure the SPI mode register */ - spi_putreg(spidev, SPI_MR_SLAVE | SPI_MR_MODFDIS, SAM_SPI_MR_OFFSET); + spi_putreg(priv, SPI_MR_SLAVE | SPI_MR_MODFDIS, SAM_SPI_MR_OFFSET); /* And enable the SPI */ - spi_putreg(spidev, SPI_CR_SPIEN, SAM_SPI_CR_OFFSET); + spi_putreg(priv, SPI_CR_SPIEN, SAM_SPI_CR_OFFSET); up_mdelay(20); - /* Flush any pending transfers */ + /* Flush any pending interrupts/transfers */ - (void)spi_getreg(spidev, SAM_SPI_SR_OFFSET); - (void)spi_getreg(spidev, SAM_SPI_RDR_OFFSET); + (void)spi_getreg(priv, SAM_SPI_SR_OFFSET); + (void)spi_getreg(priv, SAM_SPI_RDR_OFFSET); /* Initialize the SPI semaphore that enforces mutually exclusive * access to the SPI registers. */ - sem_init(&spidev->spisem, 0, 1); - spidev->initialized = true; + sem_init(&priv->spisem, 0, 1); + priv->nss = true; + priv->initialized = true; + + /* Disable all SPI interrupts at the SPI peripheral */ + + spi_putreg(priv, SPI_INT_ALL, SAM_SPI_IDR_OFFSET); /* Attach and enable interrupts at the NVIC */ - DEBUGVERIFY(irq_attach(spidev->irq, spidev->handler)); - up_enable_irq(spidev->irq); + DEBUGVERIFY(irq_attach(priv->irq, priv->handler)); + up_enable_irq(priv->irq); - spi_dumpregs(spidev, "After initialization"); + spi_dumpregs(priv, "After initialization"); } /* Set to mode=0 and nbits=8 */ - offset = (unsigned int)g_csroffset[csno]; - regval = spi_getreg(spidev, offset); + regval = spi_getreg(priv, SAM_SPI_CSR0_OFFSET); regval &= ~(SPI_CSR_CPOL | SPI_CSR_NCPHA | SPI_CSR_BITS_MASK); regval |= (SPI_CSR_NCPHA | SPI_CSR_BITS(8)); - spi_putreg(spidev, regval, offset); + spi_putreg(priv, regval, SAM_SPI_CSR0_OFFSET); - spics->nbits = 8; + priv->nbits = 8; spivdbg("csr[offset=%02x]=%08x\n", offset, regval); - return &spics->sctrlr; + return &priv->sctrlr; } #endif /* CONFIG_SAMV7_SPI_SLAVE */