summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpatacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>2009-10-17 16:11:55 +0000
committerpatacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>2009-10-17 16:11:55 +0000
commit2f3269b1362a9588ed3b34c1044b85ff1e4147e2 (patch)
tree561534c555a9f307eb583949c7304666979377e7
parent1482de823cddb881777b57e76cafb47a9bf4c3b2 (diff)
downloadnuttx-2f3269b1362a9588ed3b34c1044b85ff1e4147e2.tar.gz
nuttx-2f3269b1362a9588ed3b34c1044b85ff1e4147e2.tar.bz2
nuttx-2f3269b1362a9588ed3b34c1044b85ff1e4147e2.zip
SPI DMA design simplification
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@2148 42af7a65-404d-4744-a932-0658087f49c3
-rwxr-xr-xnuttx/arch/arm/src/stm32/stm32_dma.c33
-rwxr-xr-xnuttx/arch/arm/src/stm32/stm32_internal.h37
-rwxr-xr-xnuttx/arch/arm/src/stm32/stm32_spi.c301
3 files changed, 88 insertions, 283 deletions
diff --git a/nuttx/arch/arm/src/stm32/stm32_dma.c b/nuttx/arch/arm/src/stm32/stm32_dma.c
index d76a4373b..adf651236 100755
--- a/nuttx/arch/arm/src/stm32/stm32_dma.c
+++ b/nuttx/arch/arm/src/stm32/stm32_dma.c
@@ -301,10 +301,31 @@ void weak_function stm32_dmainitialize(void)
* Name: stm32_dmachannel
*
* Description:
- * Allocate a DMA channel
+ * Allocate a DMA channel. This function gives the caller mutually
+ * exclusive access to the DMA channel specified by the 'chan' argument.
+ * DMA channels are shared on the STM32: Devices sharing the same DMA
+ * channel cannot do DMA concurrently! See the DMACHAN_* definitions in
+ * stm32_dma.h.
+ *
+ * If the DMA channel is not available, then stm32_dmachannel() will wait
+ * until the holder of the channel relinquishes the channel by calling
+ * stm32_dmafree(). WARNING: If you have two devices sharing a DMA
+ * channel and the code never releases the channel, the stm32_dmachannel
+ * call for the other will hang forever in this function! Don't let your
+ * design do that!
+ *
+ * Hmm.. I suppose this interface could be extended to make a non-blocking
+ * version. Feel free to do that if that is what you need.
*
* Returned Value:
- * On success, a void* DMA channel handle; NULL on failure
+ * Provided that 'chan' is valid, this function ALWAYS returns a non-NULL,
+ * void* DMA channel handle. (If 'chan' is invalid, the function will
+ * assert if debug is enabled or do something ignorant otherwise).
+ *
+ * Assumptions:
+ * - The caller does not hold he DMA channel.
+ * - The caller can wait for the DMA channel to be freed if it is no
+ * available.
*
****************************************************************************/
@@ -326,10 +347,14 @@ DMA_HANDLE stm32_dmachannel(int chan)
}
/****************************************************************************
- * Name: stm32_dmarelease
+ * Name: stm32_dmafree
*
* Description:
- * Release a DMA channel
+ * Release a DMA channel. If another thread is waiting for this DMA channel
+ * in a call to stm32_dmachannel, then this function will re-assign the
+ * DMA channel to that thread and wake it up. NOTE: The 'handle' used
+ * in this argument must NEVER be used again until stm32_dmachannel() is
+ * called again to re-gain access to the channel.
*
* Returned Value:
* None
diff --git a/nuttx/arch/arm/src/stm32/stm32_internal.h b/nuttx/arch/arm/src/stm32/stm32_internal.h
index 7d58d736b..b558ce2a5 100755
--- a/nuttx/arch/arm/src/stm32/stm32_internal.h
+++ b/nuttx/arch/arm/src/stm32/stm32_internal.h
@@ -494,24 +494,53 @@ EXTERN void weak_function stm32_dmainitialize(void);
* Name: stm32_dmachannel
*
* Description:
- * Allocate a DMA channel
+ * Allocate a DMA channel. This function gives the caller mutually
+ * exclusive access to the DMA channel specified by the 'chan' argument.
+ * DMA channels are shared on the STM32: Devices sharing the same DMA
+ * channel cannot do DMA concurrently! See the DMACHAN_* definitions in
+ * stm32_dma.h.
+ *
+ * If the DMA channel is not available, then stm32_dmachannel() will wait
+ * until the holder of the channel relinquishes the channel by calling
+ * stm32_dmafree(). WARNING: If you have two devices sharing a DMA
+ * channel and the code never releases the channel, the stm32_dmachannel
+ * call for the other will hang forever in this function! Don't let your
+ * design do that!
+ *
+ * Hmm.. I suppose this interface could be extended to make a non-blocking
+ * version. Feel free to do that if that is what you need.
*
* Returned Value:
- * On success, a void* DMA channel handle; NULL on failure
+ * Provided that 'chan' is valid, this function ALWAYS returns a non-NULL,
+ * void* DMA channel handle. (If 'chan' is invalid, the function will
+ * assert if debug is enabled or do something ignorant otherwise).
+ *
+ * Assumptions:
+ * - The caller does not hold he DMA channel.
+ * - The caller can wait for the DMA channel to be freed if it is no
+ * available.
*
****************************************************************************/
EXTERN DMA_HANDLE stm32_dmachannel(int chan);
/****************************************************************************
- * Name: stm32_dmarelease
+ * Name: stm32_dmafree
*
* Description:
- * Release a DMA channel
+ * Release a DMA channel. If another thread is waiting for this DMA channel
+ * in a call to stm32_dmachannel, then this function will re-assign the
+ * DMA channel to that thread and wake it up. NOTE: The 'handle' used
+ * in this argument must NEVER be used again until stm32_dmachannel() is
+ * called again to re-gain access to the channel.
*
* Returned Value:
* None
*
+ * Assumptions:
+ * - The caller holds the DMA channel.
+ * - There is no DMA in progress
+ *
****************************************************************************/
EXTERN void stm32_dmafree(DMA_HANDLE handle);
diff --git a/nuttx/arch/arm/src/stm32/stm32_spi.c b/nuttx/arch/arm/src/stm32/stm32_spi.c
index 49341e6ed..68f3f729b 100755
--- a/nuttx/arch/arm/src/stm32/stm32_spi.c
+++ b/nuttx/arch/arm/src/stm32/stm32_spi.c
@@ -163,14 +163,6 @@ static void spi_dmatxsetup(FAR struct stm32_spidev_s *priv,
FAR const void *txbuffer, FAR const void *txdummy, size_t nwords);
static inline void spi_dmarxstart(FAR struct stm32_spidev_s *priv);
static inline void spi_dmatxstart(FAR struct stm32_spidev_s *priv);
-static inline void spi_dmaexchange(FAR struct stm32_spidev_s *priv,
- FAR const void *txbuffer, FAR void *rxbuffer,
- size_t nwords);
-static inline void spi_dmatxexchange(FAR struct stm32_spidev_s *priv,
- FAR const void *txbuffer, FAR void *rxbuffer,
- size_t nwords);
-static inline void spi_dmarxexchange(FAR struct stm32_spidev_s *priv, FAR const void *txbuffer,
- FAR void *rxbuffer, size_t nwords);
#endif
/* SPI methods */
@@ -179,13 +171,8 @@ static uint32 spi_setfrequency(FAR struct spi_dev_s *dev, uint32 frequen
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 spi_send(FAR struct spi_dev_s *dev, uint16 wd);
-#ifdef CONFIG_STM32_SPI_DMA
-static void spi_copyexchange(FAR struct spi_dev_s *dev,
- FAR const void *txbuffer, FAR void *rxbuffer,
- size_t nwords);
-#endif
-static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer,
- FAR void *rxbuffer, size_t nwords);
+static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer,
+ FAR void *rxbuffer, size_t nwords);
#ifndef CONFIG_SPI_EXCHANGE
static void spi_sndblock(FAR struct spi_dev_s *dev, FAR const void *txbuffer,
size_t nwords);
@@ -653,226 +640,6 @@ static inline void spi_dmatxstart(FAR struct stm32_spidev_s *priv)
#endif
/************************************************************************************
- * Name: spi_dmaexchange
- *
- * Description:
- * Perform concurrent RX and TX DMA transfers
- *
- * priv - 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 ubytes; if nbits >8, the data is packed into uint16's
- *
- * Returned Value:
- * None
- *
- ************************************************************************************/
-
-#ifdef CONFIG_STM32_SPI_DMA
-static inline void spi_dmaexchange(FAR struct stm32_spidev_s *priv,
- FAR const void *txbuffer, FAR void *rxbuffer,
- size_t nwords)
-{
- uint16 rxdummy = 0xffff;
- uint16 txdummy;
-
- /* Setup DMAs */
-
- spi_dmarxsetup(priv, rxbuffer, &rxdummy, nwords);
- spi_dmatxsetup(priv, txbuffer, &txdummy, nwords);
-
- /* Start the DMAs */
-
- spi_dmarxstart(priv);
- spi_dmatxstart(priv);
-
- /* Then wait for each to complete */
-
- spi_dmarxwait(priv);
- spi_dmatxwait(priv);
-}
-#endif
-
-/************************************************************************************
- * Name: spi_dmatxexchange
- *
- * Description:
- * Perform SPI exchange using DMA on the TX side only
- *
- * priv - 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 ubytes; if nbits >8, the data is packed into uint16's
- *
- * Returned Value:
- * None
- *
- ************************************************************************************/
-
-#ifdef CONFIG_STM32_SPI_DMA
-static inline void spi_dmatxexchange(FAR struct stm32_spidev_s *priv,
- FAR const void *txbuffer, FAR void *rxbuffer,
- size_t nwords)
-{
- uint16 txdummy;
-
- /* Setup and start the TX DMA */
-
- spi_dmatxsetup(priv, txbuffer, &txdummy, nwords);
- spi_dmatxstart(priv);
-
- /* Then read the RX data via a polling loop */
-
- /* 8- or 16-bit mode? */
-
- if (spi_16bitmode(priv))
- {
- /* 16-bit mode */
-
- uint16 *dest = (uint16*)rxbuffer;
- uint16 word;
-
- while (nwords-- > 0)
- {
- /* Read one word */
-
- word = spi_readword(priv);
-
- /* Is there a buffer to receive the return value? */
-
- if (dest)
- {
- *dest++ = word;
- }
- }
- }
- else
- {
- /* 8-bit mode */
-
- ubyte *dest = (ubyte*)rxbuffer;
- ubyte word;
-
- while (nwords-- > 0)
- {
- /* Read one word */
-
- word = (ubyte)spi_readword(priv);
-
- /* Is there a buffer to receive the return value? */
-
- if (dest)
- {
- *dest++ = word;
- }
- }
- }
-
- /* Then wait for the TX DMA to complete (should already be finished) */
-
- spi_dmatxwait(priv);
-}
-#endif
-
-/************************************************************************************
- * Name: spi_dmarxexchange
- *
- * Description:
- * Perform SPI exchange using DMA on the RX side only
- *
- * priv - 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 ubytes; if nbits >8, the data is packed into uint16's
- *
- * Returned Value:
- * None
- *
- ************************************************************************************/
-
-#ifdef CONFIG_STM32_SPI_DMA
-static inline void spi_dmarxexchange(FAR struct stm32_spidev_s *priv,
- FAR const void *txbuffer, FAR void *rxbuffer,
- size_t nwords)
-{
- uint16 rxdummy = 0xffff;
-
- /* Setup and start the RX DMA */
-
- spi_dmarxsetup(priv, rxbuffer, &rxdummy, nwords);
- spi_dmarxstart(priv);
-
- /* Then send all of the TX data via a copy loop */
-
- /* 8- or 16-bit mode? */
-
- if (spi_16bitmode(priv))
- {
- /* 16-bit mode */
-
- const uint16 *src = (const uint16*)txbuffer;;
- uint16 word;
-
- while (nwords-- > 0)
- {
- /* Get the next word to write. Is there a source buffer? */
-
- if (src)
- {
- word = *src++;
- }
- else
- {
- word = 0xffff;
- }
-
- /* Write one word */
-
- spi_writeword(priv, word);
- }
- }
- else
- {
- /* 8-bit mode */
-
- const ubyte *src = (const ubyte*)txbuffer;;
- ubyte word;
-
- while (nwords-- > 0)
- {
- /* Get the next word to write. Is there a source buffer? */
-
- if (src)
- {
- word = *src++;
- }
- else
- {
- word = 0xff;
- }
-
- /* Write one word */
-
- spi_writeword(priv, (uint16)word);
- }
- }
-
- /* Then wait for the RX DMA to complete */
-
- spi_dmarxwait(priv);
-}
-#endif
-
-/************************************************************************************
* Name: spi_modifycr1
*
* Description:
@@ -1098,10 +865,10 @@ static uint16 spi_send(FAR struct spi_dev_s *dev, uint16 wd)
}
/*************************************************************************
- * Name: spi_exchange (no DMA) or spi_copyexchange (with DMA capability)
+ * Name: spi_exchange (no DMA)
*
* Description:
- * Exchange a block of data on SPI
+ * Exchange a block of data on SPI without using DMA
*
* Input Parameters:
* dev - Device-specific state data
@@ -1117,14 +884,9 @@ static uint16 spi_send(FAR struct spi_dev_s *dev, uint16 wd)
*
************************************************************************************/
-#ifdef CONFIG_STM32_SPI_DMA
-static void spi_copyexchange(FAR struct spi_dev_s *dev,
- FAR const void *txbuffer, FAR void *rxbuffer,
- size_t nwords)
-#else
+#ifndef CONFIG_STM32_SPI_DMA
static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer,
FAR void *rxbuffer, size_t nwords)
-#endif
{
FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)dev;
DEBUGASSERT(priv && priv->spibase);
@@ -1198,12 +960,13 @@ static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer,
}
}
}
+#endif
/*************************************************************************
* Name: spi_exchange (with DMA capability)
*
* Description:
- * Exchange a block of data on SPI
+ * Exchange a block of data on SPI using DMA
*
* Input Parameters:
* dev - Device-specific state data
@@ -1224,42 +987,25 @@ static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer,
FAR void *rxbuffer, size_t nwords)
{
FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)dev;
- DEBUGASSERT(priv && priv->spibase);
-
- /* Do we have a TX dma channel? */
-
- if (priv->txdma)
- {
- /* Yes.. Do we have an RX dma channel too? */
+ uint16 rxdummy = 0xffff;
+ uint16 txdummy;
- if (priv->rxdma)
- {
- /* Yes.. do the full DMA exchange */
+ DEBUGASSERT(priv && priv->spibase);
- spi_dmaexchange(priv, txbuffer, rxbuffer, nwords);
- }
- else
- {
- /* No... do the exchange with only TX DMA */
+ /* Setup DMAs */
- spi_dmatxexchange(priv, txbuffer, rxbuffer, nwords);
- }
- }
+ spi_dmarxsetup(priv, rxbuffer, &rxdummy, nwords);
+ spi_dmatxsetup(priv, txbuffer, &txdummy, nwords);
- /* Do we have an RX dma channel? */
+ /* Start the DMAs */
- else if (priv->rxdma)
- {
- /* Yes... do the exchange with only RX DMA */
+ spi_dmarxstart(priv);
+ spi_dmatxstart(priv);
- spi_dmarxexchange(priv, txbuffer, rxbuffer, nwords);
- }
- else
- {
- /* No... do the exchange with no DMA */
+ /* Then wait for each to complete */
- spi_copyexchange(dev, txbuffer, rxbuffer, nwords);
- }
+ spi_dmarxwait(priv);
+ spi_dmatxwait(priv);
}
#endif
@@ -1366,12 +1112,17 @@ static void spi_portinitialize(FAR struct stm32_spidev_s *priv)
sem_init(&priv->rxsem, 0, 0);
sem_init(&priv->txsem, 0, 0);
- /* Get DMA channels. Note that if we fail to get a DMA channel, we will just
- * fall back to dumb I/O.
+ /* Get DMA channels. NOTE: stm32_dmachannel() will always assign the DMA channel.
+ * if the channel is not available, then stm32_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 stm32_dmachannel() will hang forever in this function! Don't let your
+ * design do that!
*/
priv->rxdma = stm32_dmachannel(priv->rxch);
priv->txdma = stm32_dmachannel(priv->txch);
+ DEBUGASSERT(priv->rxdma && priv->txdma);
#endif
/* Enable spi */