SAMV7 SPI slave: Add output queuing and interrupt handling

This commit is contained in:
Gregory Nutt 2015-08-09 15:42:32 -06:00
parent 5f4224115a
commit c7eaa00ed4
2 changed files with 340 additions and 27 deletions

View file

@ -608,6 +608,18 @@ config SAMV7_SPI_DMADEBUG
endif # SAMV7_SPI_MASTER
if SAMV7_SPI_SLAVE
comment "SPI Master Configuration"
config SAMV7_SPI_SLAVE_QSIZE
int "Output queue size"
default 8
---help---
The number of words that an be retained in the controller driver's
output queue.
endif # SAMV7_SPI_SLAVE
config SAMV7_SPI_REGDEBUG
bool "SPI Register level debug"
depends on DEBUG

View file

@ -48,6 +48,7 @@
#include <nuttx/arch.h>
#include <nuttx/kmalloc.h>
#include <nuttx/irq.h>
#include <nuttx/spi/slave.h>
#include "up_arch.h"
@ -68,6 +69,45 @@
****************************************************************************/
/* Configuration ************************************************************/
#ifndef CONFIG_SAMV7_SPI_SLAVE_QSIZE
# 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
@ -101,11 +141,20 @@ struct sam_spics_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 */
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 cs; /* Chip select number */
uint8_t csno; /* Chip select number */
uint16_t outval; /* Default shift-out value */
/* Output queue */
uint8_t head; /* Location of next value */
uint8_t tail; /* Index of first value */
uint16_t outq[CONFIG_SAMV7_SPI_SLAVE_QSIZE];
};
/* The overall state of one SPI controller */
@ -115,6 +164,8 @@ 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 */
@ -154,6 +205,18 @@ static void spi_dumpregs(struct sam_spidev_s *spidev, const char *msg);
static void spi_semtake(struct sam_spidev_s *spidev);
#define spi_semgive(spidev) (sem_post(&(spidev)->spisem))
/* Interrupt Handling */
static int spi_interrupt(FAR struct sam_spidev_s *spidev);
#ifdef CONFIG_SAMV7_SPI0_SLAVE
static int spi0_interrupt(int irq, FAR void *context);
#endif
#ifdef CONFIG_SAMV7_SPI1_SLAVE
static int spi1_interrupt(int irq, FAR 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,
@ -163,11 +226,13 @@ static void spi_setbits(FAR struct sam_spics_s *spics,
/* SPI slave controller methods */
static void spi_bind(FAR struct spi_sctrlr_s *sctrlr,
static void spi_bind(FAR struct spi_sctrlr_s *sctrlr,
FAR struct spi_sdev_s *sdev, enum spi_smode_e mode,
int nbits);
static void spi_unbind(FAR struct spi_sctrlr_s *sctrlr);
static void spi_setdata(FAR struct spi_sctrlr_s *sctrlr, uint16_t data);
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);
/****************************************************************************
* Private Data
@ -181,17 +246,34 @@ 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 =
{
.bind = spi_bind,
.unbind = spi_unbind,
.setdata = spi_setdata,
.enqueue = spi_enqueue,
.qfull = spi_qfull,
.qflush = spi_qflush,
};
#ifdef CONFIG_SAMV7_SPI0_SLAVE
/* This is the overall state of the SPI0 controller */
static struct sam_spidev_s g_spi0_sctrlr;
@ -398,6 +480,68 @@ static void spi_semtake(struct sam_spidev_s *spidev)
while (ret < 0);
}
/****************************************************************************
* Name: spi_flush
*
* Description:
* Make sure that there are now dangling SPI transfer in progress
*
* Input Parameters:
* spics - SPI controller CS state
*
* Returned Value:
* Standard interrupt return value.
*
****************************************************************************/
static int spi_interrupt(FAR struct sam_spidev_s *spidev)
{
#warning Missing logic
return OK;
}
/****************************************************************************
* Name: spi0_interrupt
*
* Description:
* SPI0 interrupt handler
*
* Input Parameters:
* Standard interrupt input parameters
*
* Returned Value:
* Standard interrupt return value.
*
****************************************************************************/
#ifdef CONFIG_SAMV7_SPI0_SLAVE
static int spi0_interrupt(int irq, FAR void *context)
{
return spi_interrupt(&g_spi0_sctrlr);
}
#endif
/****************************************************************************
* Name: spi1_interrupt
*
* Description:
* SPI1 interrupt handler
*
* Input Parameters:
* Standard interrupt input parameters
*
* Returned Value:
* Standard interrupt return value.
*
****************************************************************************/
#ifdef CONFIG_SAMV7_SPI1_SLAVE
static int spi1_interrupt(int irq, FAR void *context)
{
return spi_interrupt(&g_spi1_sctrlr);
}
#endif
/****************************************************************************
* Name: spi_flush
*
@ -456,7 +600,7 @@ static inline void spi_flush(struct sam_spidev_s *spidev)
static inline uint32_t spi_cs2pcs(struct sam_spics_s *spics)
{
return ((uint32_t)1 << (spics->cs)) - 1;
return ((uint32_t)1 << (spics->csno)) - 1;
}
/****************************************************************************
@ -481,7 +625,7 @@ static void spi_setmode(FAR struct sam_spics_s *spics,
uint32_t regval;
unsigned int offset;
spivdbg("cs=%d mode=%d\n", spics->cs, mode);
spivdbg("csno=%d mode=%d\n", spics->csno, mode);
/* Has the mode changed? */
@ -497,7 +641,7 @@ static void spi_setmode(FAR struct sam_spics_s *spics,
* 3 1 0
*/
offset = (unsigned int)g_csroffset[spics->cs];
offset = (unsigned int)g_csroffset[spics->csno];
regval = spi_getreg(spidev, offset);
regval &= ~(SPI_CSR_CPOL | SPI_CSR_NCPHA);
@ -554,7 +698,7 @@ static void spi_setbits(FAR struct sam_spics_s *spics,
uint32_t regval;
unsigned int offset;
spivdbg("cs=%d nbits=%d\n", spics->cs, nbits);
spivdbg("csno=%d nbits=%d\n", spics->csno, nbits);
DEBUGASSERT(spics && nbits > 7 && nbits < 17);
/* Has the number of bits changed? */
@ -563,7 +707,7 @@ static void spi_setbits(FAR struct sam_spics_s *spics,
{
/* Yes... Set number of bits appropriately */
offset = (unsigned int)g_csroffset[spics->cs];
offset = (unsigned int)g_csroffset[spics->csno];
regval = spi_getreg(spidev, offset);
regval &= ~SPI_CSR_BITS_MASK;
regval |= SPI_CSR_BITS(nbits);
@ -608,7 +752,7 @@ static void spi_bind(FAR struct spi_sctrlr_s *sctrlr,
spivdbg("sdev=%p mode=%d nbits=%d\n", sdv, mode, nbits);
DEBUGASSERT(spics != NULL && spics->sdev == NULL);
DEBUGASSERT(spics != NULL && spics->sdev == NULL && sdev != NULL);
spidev = spi_device(spics);
/* Get exclusive access to the SPI device */
@ -621,6 +765,23 @@ static void spi_bind(FAR struct spi_sctrlr_s *sctrlr,
spics->sdev = sdev;
/* Call the slaved device's cmddata() method to indicate the initial
* state of any command/data selection.
*/
#warning Missing logic
SPI_SDEV_CMDDATA(sdev, false);
/* Discard any queued data */
spics->head = 0;
spics->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);
/* Setup to begin normal SPI operation */
spi_setmode(spics, spidev, mode);
@ -669,12 +830,12 @@ static void spi_unbind(FAR struct spi_sctrlr_s *sctrlr)
}
/****************************************************************************
* Name: spi_setdata
* Name: spi_enqueue
*
* Description:
* Set the next value to be shifted out from the interface. This primes
* the controller driver for the next transfer but has no effect on any
* in-process or currently "committed" transfers
* Enqueue the next value to be shifted out from the interface. This adds
* the word the controller driver for a subsequent transfer but has no
* effect on anyin-process or currently "committed" transfers
*
* Input Parameters:
* sctrlr - SPI slave controller interface instance
@ -683,14 +844,19 @@ static void spi_unbind(FAR struct spi_sctrlr_s *sctrlr)
* provided to the bind() methods.
*
* Returned Value:
* none
* Zero if the word was successfully queue; A negated errno valid is
* returned on any failure to enqueue the word (such as if the queue is
* full).
*
****************************************************************************/
static void spi_setdata(FAR struct spi_sctrlr_s *sctrlr, uint16_t data)
static int spi_enqueue(FAR 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;
irqstate_t flags;
int next;
int ret;
spivdbg("data=%04x\n", data);
@ -701,12 +867,124 @@ static void spi_setdata(FAR struct spi_sctrlr_s *sctrlr, uint16_t data)
spi_semtake(spidev);
/* Save this new word as the next word to shifted out. The current word
* written to the TX data registers is "committed" and will not be
* overwritten.
/* Check if this word would overflow the circular buffer
*
* Interrupts are disabled briefly.
*/
#warning Missing Logic
flags = irqsave();
next = spics->head + 1;
if (next >= CONFIG_SAMV7_SPI_SLAVE_QSIZE)
{
next = 0;
}
if (next == spics->tail)
{
ret = -ENOSPC;
}
else
{
/* Save this new word as the next word to shifted out. The current
* word written to the TX data registers is "committed" and will not
* be overwritten.
*/
spics->head = data;
spics->head = next;
ret = OK;
}
irqrestore(flags);
spi_semgive(spidev);
return ret;
}
/****************************************************************************
* Name: spi_qfull
*
* Description:
* Return true if the queue is full or false if there is space to add an
* additional word to the queue.
*
* Input Parameters:
* sctrlr - SPI slave controller interface instance
*
* Returned Value:
* true if the output wueue is full
*
****************************************************************************/
static bool spi_qfull(FAR struct spi_sctrlr_s *sctrlr)
{
FAR struct sam_spics_s *spics = (FAR struct sam_spics_s *)sctrlr;
FAR struct sam_spidev_s *spidev;
irqstate_t flags;
int next;
bool ret;
DEBUGASSERT(spics != NULL && spics->sdev != NULL);
spidev = spi_device(spics);
/* Get exclusive access to the SPI device */
spi_semtake(spidev);
/* Check if another word would overflow the circular buffer
*
* Interrupts are disabled briefly.
*/
flags = irqsave();
next = spics->head + 1;
if (next >= CONFIG_SAMV7_SPI_SLAVE_QSIZE)
{
next = 0;
}
ret = (next == spics->tail);
irqrestore(flags);
spi_semgive(spidev);
return ret;
}
/****************************************************************************
* Name: spi_qflush
*
* Description:
* Discard all saved values in the output queue. On return from this
* function the output queue will be empty. Any in-progress or otherwise
* "committed" output values may not be flushed.
*
* Input Parameters:
* sctrlr - SPI slave controller interface instance
*
* Returned Value:
* None
*
****************************************************************************/
static void spi_qflush(FAR struct spi_sctrlr_s *sctrlr)
{
FAR struct sam_spics_s *spics = (FAR struct sam_spics_s *)sctrlr;
FAR struct sam_spidev_s *spidev;
irqstate_t flags;
spivdbg("data=%04x\n", data);
DEBUGASSERT(spics != NULL && spics->sdev != NULL);
spidev = spi_device(spics);
/* Get exclusive access to the SPI device */
spi_semtake(spidev);
/* Mark the buffer empty, momentarily disabling interrupts */
flags = irqsave();
spics->head = 0;
spics->tail = 0;
irqrestore(flags);
spi_semgive(spidev);
}
@ -721,7 +999,8 @@ static void spi_setdata(FAR struct spi_sctrlr_s *sctrlr, uint16_t data)
* Initialize the selected SPI port in slave mode.
*
* Input Parameter:
* cs - Chip select number (identifying the "logical" SPI port)
* port - Chip select number identifying the "logical" SPI port. Includes
* incoded port and chip select inforomation.
*
* Returned Value:
* Valid SPI device structure reference on success; a NULL on failure
@ -773,7 +1052,7 @@ struct spi_sctrlr_s *up_spi_slave_initialize(int port)
/* Save the chip select and SPI controller numbers */
spics->cs = csno;
spics->csno = csno;
spics->spino = spino;
/* Get the SPI device structure associated with the chip select */
@ -792,9 +1071,15 @@ struct spi_sctrlr_s *up_spi_slave_initialize(int port)
#endif
#if defined(CONFIG_SAMV7_SPI0_SLAVE)
{
/* Set the SPI0 register base address */
/* Set the SPI0 register base address and interrupt information */
spidev->base = SAM_SPI0_BASE,
spidev->base = SAM_SPI0_BASE,
spidev->irq = SAM_IRQ_SPI0;
spidev->handler = spi0_interrupt;
/* Chip select pin configuration */
spics->csconfig = g_spi0_csconfig[csno];
/* Enable peripheral clocking to SPI0 */
@ -814,9 +1099,15 @@ struct spi_sctrlr_s *up_spi_slave_initialize(int port)
#endif
#if defined(CONFIG_SAMV7_SPI1_SLAVE)
{
/* Set the SPI1 register base address */
/* Set the SPI1 register base address and interrupt information */
spidev->base = SAM_SPI1_BASE,
spidev->base = SAM_SPI1_BASE,
spidev->irq = SAM_IRQ_SPI1;
spidev->handler = spi1_interrupt;
/* Chip select pin configuration */
spics->csconfig = g_spi1_csconfig[csno];
/* Enable peripheral clocking to SPI1 */
@ -832,6 +1123,11 @@ struct spi_sctrlr_s *up_spi_slave_initialize(int port)
}
#endif
/* Configure the CS pin */
DEBUGASSERT(spics->csconfig != 0);
sam_configgpio(spics->csconfig);
/* Disable SPI clocking */
spi_putreg(spidev, SPI_CR_SPIDIS, SAM_SPI_CR_OFFSET);
@ -863,6 +1159,11 @@ struct spi_sctrlr_s *up_spi_slave_initialize(int port)
sem_init(&spidev->spisem, 0, 1);
spidev->initialized = true;
/* Attach and enable interrupts at the NVIC */
DEBUGVERIFY(irq_attach(spidev->irq, spidev->handler));
up_enable_irq(spidev->irq);
spi_dumpregs(spidev, "After initialization");
}