Add debug instrumentation to the SAM3U SPI driver
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@4027 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
parent
e596c1a302
commit
8ec0e03628
@ -65,20 +65,23 @@
|
||||
* Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/* Enables debug output from this file (needs CONFIG_DEBUG too) */
|
||||
/* Check if SPI debut is enabled (non-standard.. no support in
|
||||
* include/debug.h
|
||||
*/
|
||||
|
||||
#undef SPI_DEBUG /* Define to enable debug */
|
||||
#undef SPI_VERBOSE /* Define to enable verbose debug */
|
||||
#ifndef CONFIG_DEBUG
|
||||
# undef CONFIG_DEBUG_VERBOSE
|
||||
# undef CONFIG_DEBUG_SPI
|
||||
#endif
|
||||
|
||||
#ifdef SPI_DEBUG
|
||||
# define spidbg lldbg
|
||||
# ifdef SPI_VERBOSE
|
||||
#ifdef CONFIG_DEBUG_SPI
|
||||
# define spidbg lldbg
|
||||
# ifdef CONFIG_DEBUG_VERBOSE
|
||||
# define spivdbg lldbg
|
||||
# else
|
||||
# define spivdbg(x...)
|
||||
# endif
|
||||
#else
|
||||
# undef SPI_VERBOSE
|
||||
# define spidbg(x...)
|
||||
# define spivdbg(x...)
|
||||
#endif
|
||||
@ -114,6 +117,15 @@ struct sam3u_spidev_s
|
||||
/****************************************************************************
|
||||
* Private Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
/* Helpers */
|
||||
|
||||
#if defined(CONFIG_DEBUG_SPI) && defined(CONFIG_DEBUG_VERBOSE)
|
||||
static void spi_dumpregs(FAR const char *msg);
|
||||
#else
|
||||
# define spi_dumpregs(msg)
|
||||
#endif
|
||||
static inline void spi_flush(void);
|
||||
|
||||
/* SPI methods */
|
||||
|
||||
@ -128,10 +140,9 @@ static void spi_setmode(FAR struct spi_dev_s *dev,
|
||||
enum spi_mode_e mode);
|
||||
static void spi_setbits(FAR struct spi_dev_s *dev, int nbits);
|
||||
static uint16_t spi_send(FAR struct spi_dev_s *dev, uint16_t ch);
|
||||
#ifdef CONFIG_SPI_EXCHANGE
|
||||
static void spi_exchange(FAR struct spi_dev_s *dev,
|
||||
FAR const void *txbuffer, FAR void *rxbuffer, size_t nwords);
|
||||
#else
|
||||
#ifndef CONFIG_SPI_EXCHANGE
|
||||
static void spi_sndblock(FAR struct spi_dev_s *dev, FAR const void *buffer, size_t nwords);
|
||||
static void spi_recvblock(FAR struct spi_dev_s *dev, FAR void *buffer, size_t nwords);
|
||||
#endif
|
||||
@ -187,6 +198,65 @@ static const uint32_t g_csraddr[4] =
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: spi_dumpregs
|
||||
*
|
||||
* Description:
|
||||
* Dump the contents of all SPI registers
|
||||
*
|
||||
* Input Parameters:
|
||||
* msg - Message to print before the register data
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#if defined(CONFIG_DEBUG_SPI) && defined(CONFIG_DEBUG_VERBOSE)
|
||||
static void spi_dumpregs(FAR const char *msg)
|
||||
{
|
||||
spivdbg("%s:\n", msg);
|
||||
spivdbg(" MR:%08x SR:%08x IMR:%08x\n",
|
||||
getreg32(SAM3U_SPI_MR), getreg32(SAM3U_SPI_SR),
|
||||
getreg32(SAM3U_SPI_IMR));
|
||||
spivdbg(" CSR0:%08x CSR1:%08x CSR2:%08x CSR3:%08x\n",
|
||||
getreg32(SAM3U_SPI_CSR0), getreg32(SAM3U_SPI_CSR1),
|
||||
getreg32(SAM3U_SPI_CSR2), getreg32(SAM3U_SPI_CSR3));
|
||||
spivdbg(" WPCR:%08x WPSR:%08x IMR:%08x\n",
|
||||
getreg32(SAM3U_SPI_WPCR), getreg32(SAM3U_SPI_WPSR));
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: spi_flush
|
||||
*
|
||||
* Description:
|
||||
* Make sure that there are now dangling SPI transfer in progress
|
||||
*
|
||||
* Input Parameters:
|
||||
* priv - Device-specific state data
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static inline void spi_flush(void)
|
||||
{
|
||||
/* Make sure the no TX activity is in progress... waiting if necessary */
|
||||
|
||||
while ((getreg32(SAM3U_SPI_SR) & SPI_INT_TXEMPTY) == 0);
|
||||
|
||||
/* Then make sure that there is no pending RX data .. reading as
|
||||
* discarding as necessary.
|
||||
*/
|
||||
|
||||
while ((getreg32(SAM3U_SPI_SR) & SPI_INT_RDRF) != 0)
|
||||
{
|
||||
(void)getreg32(SAM3U_SPI_RDR);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: spi_lock
|
||||
*
|
||||
@ -213,6 +283,7 @@ static int spi_lock(FAR struct spi_dev_s *dev, bool lock)
|
||||
{
|
||||
FAR struct sam3u_spidev_s *priv = (FAR struct sam3u_spidev_s *)dev;
|
||||
|
||||
spivdbg("lock=%d\n", lock);
|
||||
if (lock)
|
||||
{
|
||||
/* Take the semaphore (perhaps waiting) */
|
||||
@ -258,6 +329,7 @@ static int spi_lock(FAR struct spi_dev_s *dev, bool lock)
|
||||
|
||||
/* Are we selecting or de-selecting the device? */
|
||||
|
||||
spivdbg("selected=%d\n", selected);
|
||||
if (selected)
|
||||
{
|
||||
/* At this point, we expect no chip selected */
|
||||
@ -267,6 +339,7 @@ static int spi_lock(FAR struct spi_dev_s *dev, bool lock)
|
||||
/* Get the chip select associated with this SPI device */
|
||||
|
||||
priv->cs = sam3u_spiselect(devid);
|
||||
spivdbg("cs=%d\n", priv->cs);
|
||||
DEBUGASSERT(priv->cs >= 0 && priv->cs <= 3);
|
||||
|
||||
/* Before writing the TDR, the PCS field in the SPI_MR register must be set
|
||||
@ -318,6 +391,7 @@ static uint32_t spi_setfrequency(FAR struct spi_dev_s *dev, uint32_t frequency)
|
||||
uint32_t regval;
|
||||
uint32_t regaddr;
|
||||
|
||||
spivdbg("cs=%d frequency=%d\n", priv->cs, frequency);
|
||||
DEBUGASSERT(priv->cs >= 0 && priv->cs <= 3);
|
||||
|
||||
/* Check if the requested frequency is the same as the frequency selection */
|
||||
@ -390,6 +464,7 @@ static uint32_t spi_setfrequency(FAR struct spi_dev_s *dev, uint32_t frequency)
|
||||
/* Calculate the new actual frequency */
|
||||
|
||||
actual = SAM3U_MCK_FREQUENCY / scbr;
|
||||
spivdbg("csr[%08x]=%08x actual=%d\n", regaddr, regval, actual);
|
||||
|
||||
/* Save the frequency setting */
|
||||
|
||||
@ -423,6 +498,7 @@ static void spi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode)
|
||||
uint32_t regval;
|
||||
uint32_t regaddr;
|
||||
|
||||
spivdbg("cs=%d mode=%d\n", priv->cs, mode);
|
||||
DEBUGASSERT(priv->cs >= 0 && priv->cs <= 3);
|
||||
|
||||
/* Has the mode changed? */
|
||||
@ -459,7 +535,8 @@ static void spi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode)
|
||||
return;
|
||||
}
|
||||
|
||||
putreg32(regval, regaddr);
|
||||
putreg32(regval, regaddr);
|
||||
spivdbg("csr[%08x]=%08x\n", regaddr, regval);
|
||||
|
||||
/* Save the mode so that subsequent re-configurations will be faster */
|
||||
|
||||
@ -490,11 +567,19 @@ static void spi_setbits(FAR struct spi_dev_s *dev, int nbits)
|
||||
uint32_t regaddr;
|
||||
uint32_t regval;
|
||||
|
||||
/* Has the number of bits changed? */
|
||||
|
||||
spivdbg("cs=%d nbits=%d\n", priv->cs, nbits);
|
||||
DEBUGASSERT(priv && nbits > 7 && nbits < 17);
|
||||
DEBUGASSERT(priv->cs >= 0 && priv->cs <= 3);
|
||||
|
||||
/* NOTE: The logic in spi_send and in spi_exchange only handles 8-bit
|
||||
* data at the present time. So the following extra assertion is a
|
||||
* reminder that we have to fix that someday.
|
||||
*/
|
||||
|
||||
DEBUGASSERT(nbits == 8); /* Temporary -- FIX ME */
|
||||
|
||||
/* Has the number of bits changed? */
|
||||
|
||||
#ifndef CONFIG_SPI_OWNBUS
|
||||
if (nbits != priv->csstate[priv->cs].nbits)
|
||||
{
|
||||
@ -507,6 +592,8 @@ static void spi_setbits(FAR struct spi_dev_s *dev, int nbits)
|
||||
regval |= SPI_CSR_BITS(nbits);
|
||||
putreg32(regval, regaddr);
|
||||
|
||||
spivdbg("csr[%08x]=%08x\n", regaddr, regval);
|
||||
|
||||
/* Save the selection so the subsequence re-configurations will be faster */
|
||||
|
||||
#ifndef CONFIG_SPI_OWNBUS
|
||||
@ -533,32 +620,19 @@ static void spi_setbits(FAR struct spi_dev_s *dev, int nbits)
|
||||
|
||||
static uint16_t spi_send(FAR struct spi_dev_s *dev, uint16_t wd)
|
||||
{
|
||||
#ifdef CONFIG_SPI_VARSELECT
|
||||
FAR struct sam3u_spidev_s *priv = (FAR struct sam3u_spidev_s *)dev;
|
||||
uint32_t tdr = (uint32_t)priv->cs << SPI_TDR_PCS_SHIFT;
|
||||
#endif
|
||||
uint8_t txbyte;
|
||||
uint8_t rxbyte;
|
||||
|
||||
/* Wait for any previous data written to the TDR to be transferred to the
|
||||
* serializer.
|
||||
/* spi_exchange can do this. Note: right now, this only deals with 8-bit
|
||||
* words. If the SPI interface were configured for words of other sizes,
|
||||
* this would fail.
|
||||
*/
|
||||
|
||||
while ((getreg32(SAM3U_SPI_SR) & SPI_INT_TDRE) == 0);
|
||||
txbyte = (uint8_t)wd;
|
||||
spi_exchange(dev, &txbyte, &rxbyte, 1);
|
||||
|
||||
/* Write the data to transmitted to the Transmit Data Register (TDR) */
|
||||
|
||||
#ifdef CONFIG_SPI_VARSELECT
|
||||
putreg32((uint32_t)wd | tdr | SPI_TDR_LASTXFER, SAM3U_SPI_TDR);
|
||||
#else
|
||||
putreg32((uint32_t)wd, SAM3U_SPI_TDR);
|
||||
#endif
|
||||
|
||||
/* Wait for the read data to be available in the RDR */
|
||||
|
||||
while ((getreg32(SAM3U_SPI_SR) & SPI_INT_RDRF) == 0);
|
||||
|
||||
/* Return the received data */
|
||||
|
||||
return (uint16_t)getreg32(SAM3U_SPI_RDR);
|
||||
spivdbg("Sent %02x received %02x\n", txbyte, rxbyte);
|
||||
return (uint16_t)rxbyte;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -582,7 +656,6 @@ static uint16_t spi_send(FAR struct spi_dev_s *dev, uint16_t wd)
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_SPI_EXCHANGE
|
||||
static void spi_exchange(FAR struct spi_dev_s *dev,
|
||||
FAR const void *txbuffer, FAR void *rxbuffer,
|
||||
size_t nwords)
|
||||
@ -593,42 +666,80 @@ static void spi_exchange(FAR struct spi_dev_s *dev,
|
||||
#endif
|
||||
FAR uint8_t *rxptr = (FAR uint8_t*)rxbuffer;
|
||||
FAR uint8_t *txptr = (FAR uint8_t*)txbuffer;
|
||||
uint8_t data;
|
||||
uint32_t data;
|
||||
|
||||
spidbg("nwords: %d\n", nwords);
|
||||
spivdbg("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords);
|
||||
|
||||
/* Loop, sending each word in the user-provied data buffer */
|
||||
/* Make sure that any previous transfer is flushed from the hardware */
|
||||
|
||||
spi_flush();
|
||||
|
||||
/* Loop, sending each word in the user-provied data buffer.
|
||||
*
|
||||
* Note 1: Right now, this only deals with 8-bit words. If the SPI
|
||||
* interface were configured for words of other sizes, this
|
||||
* would fail.
|
||||
* Note 2: Good SPI performance would require that we implement DMA
|
||||
* transfers!
|
||||
* Note 3: This loop might be made more efficient. Would logic
|
||||
* like the following improve the throughput? Or would it
|
||||
* just add the risk of overruns?
|
||||
*
|
||||
* Get word 1;
|
||||
* Send word 1; Now word 1 is "in flight"
|
||||
* nwords--;
|
||||
* for ( ; nwords > 0; nwords--)
|
||||
* {
|
||||
* Get word N.
|
||||
* Wait for TDRE meaning that word N-1 has moved to the shift
|
||||
* register.
|
||||
* Disable interrupts to keep the following atomic
|
||||
* Send word N. Now both work N-1 and N are "in flight"
|
||||
* Wait for RDRF meaning that word N-1 is available
|
||||
* Read word N-1.
|
||||
* Re-enable interrupts.
|
||||
* Save word N-1.
|
||||
* }
|
||||
* Wait for RDRF meaning that the final word is available
|
||||
* Read the final word.
|
||||
* Save the final word.
|
||||
*/
|
||||
|
||||
for ( ; nwords > 0; nwords--)
|
||||
{
|
||||
/* Get the data to send (0xff if there is no data source) */
|
||||
|
||||
if (rxptr)
|
||||
{
|
||||
data = (uint32_t)*txptr++;
|
||||
}
|
||||
else
|
||||
{
|
||||
data = 0xffff;
|
||||
}
|
||||
|
||||
/* Set the chip select in the value written to the TDR */
|
||||
|
||||
#ifdef CONFIG_SPI_VARSELECT
|
||||
data |= tdr;
|
||||
|
||||
/* Do we need to set the LASTXFER bit in the TDR value too? */
|
||||
|
||||
if (nwords == 1)
|
||||
{
|
||||
data |= SPI_TDR_LASTXFER;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Wait for any previous data written to the TDR to be transferred
|
||||
* to the serializer.
|
||||
*/
|
||||
|
||||
while ((getreg32(SAM3U_SPI_SR) & SPI_INT_TDRE) == 0);
|
||||
|
||||
/* Get the data to send (0xff if there is no data source) */
|
||||
|
||||
if (rxptr)
|
||||
{
|
||||
data = *txptr++;
|
||||
}
|
||||
else
|
||||
{
|
||||
data = 0xff;
|
||||
}
|
||||
|
||||
/* Write the data to transmitted to the Transmit Data Register (TDR) */
|
||||
|
||||
#ifdef CONFIG_SPI_VARSELECT
|
||||
if (nwords == 1)
|
||||
{
|
||||
tdr |= SPI_TDR_LASTXFER;
|
||||
}
|
||||
putreg32((uint32_t)data | tdr, SAM3U_SPI_TDR);
|
||||
#else
|
||||
putreg32((uint32_t)data, SAM3U_SPI_TDR);
|
||||
#endif
|
||||
putreg32(data, SAM3U_SPI_TDR);
|
||||
|
||||
/* Wait for the read data to be available in the RDR */
|
||||
|
||||
@ -636,14 +747,13 @@ static void spi_exchange(FAR struct spi_dev_s *dev,
|
||||
|
||||
/* Read the received data from the SPI Data Register */
|
||||
|
||||
data = (uint8_t)getreg32(SAM3U_SPI_RDR);
|
||||
data = getreg32(SAM3U_SPI_RDR);
|
||||
if (rxptr)
|
||||
{
|
||||
*txptr++ = data;
|
||||
*txptr++ = (uint8_t)data;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************************************************************
|
||||
* Name: spi_sndblock
|
||||
@ -667,38 +777,9 @@ static void spi_exchange(FAR struct spi_dev_s *dev,
|
||||
#ifndef CONFIG_SPI_EXCHANGE
|
||||
static void spi_sndblock(FAR struct spi_dev_s *dev, FAR const void *buffer, size_t nwords)
|
||||
{
|
||||
#ifdef CONFIG_SPI_VARSELECT
|
||||
FAR struct sam3u_spidev_s *priv = (FAR struct sam3u_spidev_s *)dev;
|
||||
uint32_t tdr = (uint32_t)priv->cs << SPI_TDR_PCS_SHIFT;
|
||||
#endif
|
||||
FAR uint8_t *ptr = (FAR uint8_t*)buffer;
|
||||
uint8_t data;
|
||||
/* spi_exchange can do this. */
|
||||
|
||||
spidbg("nwords: %d\n", nwords);
|
||||
|
||||
/* Loop, sending each word in the user-provied data buffer */
|
||||
|
||||
for ( ; nwords > 0; nwords--)
|
||||
{
|
||||
/* Wait for any previous data written to the TDR to be transferred
|
||||
* to the serializer.
|
||||
*/
|
||||
|
||||
while ((getreg32(SAM3U_SPI_SR) & SPI_INT_TDRE) == 0);
|
||||
|
||||
/* Write the data to transmitted to the Transmit Data Register (TDR) */
|
||||
|
||||
data = *ptr++;
|
||||
#ifdef CONFIG_SPI_VARSELECT
|
||||
if (nwords == 1)
|
||||
{
|
||||
tdr |= SPI_TDR_LASTXFER;
|
||||
}
|
||||
putreg32((uint32_t)data | tdr, SAM3U_SPI_TDR);
|
||||
#else
|
||||
putreg32((uint32_t)data, SAM3U_SPI_TDR);
|
||||
#endif
|
||||
}
|
||||
spi_exchange(dev, buffer, NULL, nwords);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -724,45 +805,9 @@ static void spi_sndblock(FAR struct spi_dev_s *dev, FAR const void *buffer, size
|
||||
#ifndef CONFIG_SPI_EXCHANGE
|
||||
static void spi_recvblock(FAR struct spi_dev_s *dev, FAR void *buffer, size_t nwords)
|
||||
{
|
||||
#ifdef CONFIG_SPI_VARSELECT
|
||||
FAR struct sam3u_spidev_s *priv = (FAR struct sam3u_spidev_s *)dev;
|
||||
uint32_t tdr = (uint32_t)priv->cs << SPI_TDR_PCS_SHIFT;
|
||||
#endif
|
||||
FAR uint8_t *ptr = (FAR uint8_t*)buffer;
|
||||
/* spi_exchange can do this. */
|
||||
|
||||
spidbg("nwords: %d\n", nwords);
|
||||
|
||||
/* Loop, receiving each word */
|
||||
|
||||
for ( ; nwords > 0; nwords--)
|
||||
{
|
||||
/* Wait for any previous data written to the TDR to be transferred
|
||||
* to the serializer.
|
||||
*/
|
||||
|
||||
while ((getreg32(SAM3U_SPI_SR) & SPI_INT_TDRE) == 0);
|
||||
|
||||
/* Write the some dummy data the Transmit Data Register (TDR) in order
|
||||
* to clock the read data.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_SPI_VARSELECT
|
||||
if (nwords == 1)
|
||||
{
|
||||
tdr |= SPI_TDR_LASTXFER;
|
||||
}
|
||||
putreg32(0xff | tdr, SAM3U_SPI_TDR);
|
||||
#else
|
||||
putreg32(0xff, SAM3U_SPI_TDR);
|
||||
#endif
|
||||
/* Wait for the read data to be available in the RDR */
|
||||
|
||||
while ((getreg32(SAM3U_SPI_SR) & SPI_INT_RDRF) == 0);
|
||||
|
||||
/* Read the received data from the SPI Data Register */
|
||||
|
||||
*ptr++ = (uint8_t)getreg32(SAM3U_SPI_RDR);
|
||||
}
|
||||
spi_exchange(dev, NULL, buffer, nwords);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -792,6 +837,7 @@ FAR struct spi_dev_s *up_spiinitialize(int port)
|
||||
|
||||
/* The SAM3U has only a single SPI port */
|
||||
|
||||
spivdbg("port=%d\n", port);
|
||||
DEBUGASSERT(port == 0);
|
||||
|
||||
/* Set up the initial state */
|
||||
@ -845,6 +891,7 @@ FAR struct spi_dev_s *up_spiinitialize(int port)
|
||||
#ifndef CONFIG_SPI_OWNBUS
|
||||
sem_init(&priv->exclsem, 0, 1);
|
||||
#endif
|
||||
spi_dumpregs("After initialization");
|
||||
return &priv->spidev;
|
||||
}
|
||||
#endif /* CONFIG_SAM3U_SPI */
|
||||
|
Loading…
Reference in New Issue
Block a user