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:
patacongo 2011-10-06 18:32:53 +00:00
parent e596c1a302
commit 8ec0e03628

View File

@ -65,20 +65,23 @@
* Definitions * 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 */ #ifndef CONFIG_DEBUG
#undef SPI_VERBOSE /* Define to enable verbose debug */ # undef CONFIG_DEBUG_VERBOSE
# undef CONFIG_DEBUG_SPI
#endif
#ifdef SPI_DEBUG #ifdef CONFIG_DEBUG_SPI
# define spidbg lldbg # define spidbg lldbg
# ifdef SPI_VERBOSE # ifdef CONFIG_DEBUG_VERBOSE
# define spivdbg lldbg # define spivdbg lldbg
# else # else
# define spivdbg(x...) # define spivdbg(x...)
# endif # endif
#else #else
# undef SPI_VERBOSE
# define spidbg(x...) # define spidbg(x...)
# define spivdbg(x...) # define spivdbg(x...)
#endif #endif
@ -114,6 +117,15 @@ struct sam3u_spidev_s
/**************************************************************************** /****************************************************************************
* Private Function Prototypes * 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 */ /* SPI methods */
@ -128,10 +140,9 @@ static void spi_setmode(FAR struct spi_dev_s *dev,
enum spi_mode_e mode); enum spi_mode_e mode);
static void spi_setbits(FAR struct spi_dev_s *dev, int nbits); static void spi_setbits(FAR struct spi_dev_s *dev, int nbits);
static uint16_t spi_send(FAR struct spi_dev_s *dev, uint16_t ch); static 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, static void spi_exchange(FAR struct spi_dev_s *dev,
FAR const void *txbuffer, FAR void *rxbuffer, size_t nwords); 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_sndblock(FAR struct spi_dev_s *dev, FAR const void *buffer, size_t nwords);
static void spi_recvblock(FAR struct spi_dev_s *dev, FAR void *buffer, size_t nwords); static void spi_recvblock(FAR struct spi_dev_s *dev, FAR void *buffer, size_t nwords);
#endif #endif
@ -187,6 +198,65 @@ static const uint32_t g_csraddr[4] =
* Private Functions * 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 * 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; FAR struct sam3u_spidev_s *priv = (FAR struct sam3u_spidev_s *)dev;
spivdbg("lock=%d\n", lock);
if (lock) if (lock)
{ {
/* Take the semaphore (perhaps waiting) */ /* 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? */ /* Are we selecting or de-selecting the device? */
spivdbg("selected=%d\n", selected);
if (selected) if (selected)
{ {
/* At this point, we expect no chip 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 */ /* Get the chip select associated with this SPI device */
priv->cs = sam3u_spiselect(devid); priv->cs = sam3u_spiselect(devid);
spivdbg("cs=%d\n", priv->cs);
DEBUGASSERT(priv->cs >= 0 && priv->cs <= 3); DEBUGASSERT(priv->cs >= 0 && priv->cs <= 3);
/* Before writing the TDR, the PCS field in the SPI_MR register must be set /* 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 regval;
uint32_t regaddr; uint32_t regaddr;
spivdbg("cs=%d frequency=%d\n", priv->cs, frequency);
DEBUGASSERT(priv->cs >= 0 && priv->cs <= 3); DEBUGASSERT(priv->cs >= 0 && priv->cs <= 3);
/* Check if the requested frequency is the same as the frequency selection */ /* 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 */ /* Calculate the new actual frequency */
actual = SAM3U_MCK_FREQUENCY / scbr; actual = SAM3U_MCK_FREQUENCY / scbr;
spivdbg("csr[%08x]=%08x actual=%d\n", regaddr, regval, actual);
/* Save the frequency setting */ /* 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 regval;
uint32_t regaddr; uint32_t regaddr;
spivdbg("cs=%d mode=%d\n", priv->cs, mode);
DEBUGASSERT(priv->cs >= 0 && priv->cs <= 3); DEBUGASSERT(priv->cs >= 0 && priv->cs <= 3);
/* Has the mode changed? */ /* Has the mode changed? */
@ -459,7 +535,8 @@ static void spi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode)
return; 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 */ /* 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 regaddr;
uint32_t regval; 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 && nbits > 7 && nbits < 17);
DEBUGASSERT(priv->cs >= 0 && priv->cs <= 3); 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 #ifndef CONFIG_SPI_OWNBUS
if (nbits != priv->csstate[priv->cs].nbits) 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); regval |= SPI_CSR_BITS(nbits);
putreg32(regval, regaddr); putreg32(regval, regaddr);
spivdbg("csr[%08x]=%08x\n", regaddr, regval);
/* Save the selection so the subsequence re-configurations will be faster */ /* Save the selection so the subsequence re-configurations will be faster */
#ifndef CONFIG_SPI_OWNBUS #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) static uint16_t spi_send(FAR struct spi_dev_s *dev, uint16_t wd)
{ {
#ifdef CONFIG_SPI_VARSELECT uint8_t txbyte;
FAR struct sam3u_spidev_s *priv = (FAR struct sam3u_spidev_s *)dev; uint8_t rxbyte;
uint32_t tdr = (uint32_t)priv->cs << SPI_TDR_PCS_SHIFT;
#endif
/* Wait for any previous data written to the TDR to be transferred to the /* spi_exchange can do this. Note: right now, this only deals with 8-bit
* serializer. * 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) */ spivdbg("Sent %02x received %02x\n", txbyte, rxbyte);
return (uint16_t)rxbyte;
#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);
} }
/**************************************************************************** /****************************************************************************
@ -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, static void spi_exchange(FAR struct spi_dev_s *dev,
FAR const void *txbuffer, FAR void *rxbuffer, FAR const void *txbuffer, FAR void *rxbuffer,
size_t nwords) size_t nwords)
@ -593,42 +666,80 @@ static void spi_exchange(FAR struct spi_dev_s *dev,
#endif #endif
FAR uint8_t *rxptr = (FAR uint8_t*)rxbuffer; FAR uint8_t *rxptr = (FAR uint8_t*)rxbuffer;
FAR uint8_t *txptr = (FAR uint8_t*)txbuffer; 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--) 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 /* Wait for any previous data written to the TDR to be transferred
* to the serializer. * to the serializer.
*/ */
while ((getreg32(SAM3U_SPI_SR) & SPI_INT_TDRE) == 0); 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) */ /* Write the data to transmitted to the Transmit Data Register (TDR) */
#ifdef CONFIG_SPI_VARSELECT putreg32(data, SAM3U_SPI_TDR);
if (nwords == 1)
{
tdr |= SPI_TDR_LASTXFER;
}
putreg32((uint32_t)data | tdr, SAM3U_SPI_TDR);
#else
putreg32((uint32_t)data, SAM3U_SPI_TDR);
#endif
/* Wait for the read data to be available in the RDR */ /* 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 */ /* Read the received data from the SPI Data Register */
data = (uint8_t)getreg32(SAM3U_SPI_RDR); data = getreg32(SAM3U_SPI_RDR);
if (rxptr) if (rxptr)
{ {
*txptr++ = data; *txptr++ = (uint8_t)data;
} }
} }
} }
#endif
/*************************************************************************** /***************************************************************************
* Name: spi_sndblock * Name: spi_sndblock
@ -667,38 +777,9 @@ static void spi_exchange(FAR struct spi_dev_s *dev,
#ifndef CONFIG_SPI_EXCHANGE #ifndef CONFIG_SPI_EXCHANGE
static void spi_sndblock(FAR struct spi_dev_s *dev, FAR const void *buffer, size_t nwords) static void spi_sndblock(FAR struct spi_dev_s *dev, FAR const void *buffer, size_t nwords)
{ {
#ifdef CONFIG_SPI_VARSELECT /* spi_exchange can do this. */
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;
spidbg("nwords: %d\n", nwords); spi_exchange(dev, buffer, NULL, 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
}
} }
#endif #endif
@ -724,45 +805,9 @@ static void spi_sndblock(FAR struct spi_dev_s *dev, FAR const void *buffer, size
#ifndef CONFIG_SPI_EXCHANGE #ifndef CONFIG_SPI_EXCHANGE
static void spi_recvblock(FAR struct spi_dev_s *dev, FAR void *buffer, size_t nwords) static void spi_recvblock(FAR struct spi_dev_s *dev, FAR void *buffer, size_t nwords)
{ {
#ifdef CONFIG_SPI_VARSELECT /* spi_exchange can do this. */
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;
spidbg("nwords: %d\n", nwords); spi_exchange(dev, NULL, buffer, 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);
}
} }
#endif #endif
@ -792,6 +837,7 @@ FAR struct spi_dev_s *up_spiinitialize(int port)
/* The SAM3U has only a single SPI port */ /* The SAM3U has only a single SPI port */
spivdbg("port=%d\n", port);
DEBUGASSERT(port == 0); DEBUGASSERT(port == 0);
/* Set up the initial state */ /* Set up the initial state */
@ -845,6 +891,7 @@ FAR struct spi_dev_s *up_spiinitialize(int port)
#ifndef CONFIG_SPI_OWNBUS #ifndef CONFIG_SPI_OWNBUS
sem_init(&priv->exclsem, 0, 1); sem_init(&priv->exclsem, 0, 1);
#endif #endif
spi_dumpregs("After initialization");
return &priv->spidev; return &priv->spidev;
} }
#endif /* CONFIG_SAM3U_SPI */ #endif /* CONFIG_SAM3U_SPI */