drivers/spi/Kconfig and include/nuttx/spi/spi.h: Extend the HW features supported by SPI. It now supports a deffered DMA trigger hardware configuration. arch/arm/src/stm32/stm32_spi.c: Implements the new deferred DMA trigger feature.

This commit is contained in:
Dave Marples
2018-12-02 07:30:17 -06:00
committed by Gregory Nutt
parent d2b98cc150
commit 8328539534
3 changed files with 137 additions and 4 deletions
+96 -4
View File
@@ -173,6 +173,10 @@ struct stm32_spidev_s
#ifdef CONFIG_STM32_SPI_DMA
volatile uint8_t rxresult; /* Result of the RX DMA */
volatile uint8_t txresult; /* Result of the RX DMA */
#ifdef CONFIG_SPI_TRIGGER
bool defertrig; /* Flag indicating that trigger should be deferred */
bool trigarmed; /* Flag indicating that the trigger is armed */
#endif
uint8_t rxch; /* The RX DMA channel number */
uint8_t txch; /* The TX DMA channel number */
DMA_HANDLE rxdma; /* DMA channel handle for RX transfers */
@@ -277,6 +281,9 @@ static const struct spi_ops_s g_sp1iops =
.sndblock = spi_sndblock,
.recvblock = spi_recvblock,
#endif
#ifdef CONFIG_SPI_TRIGGER
.trigger = spi_trigger,
#endif
#ifdef CONFIG_SPI_CALLBACK
.registercallback = stm32_spi1register, /* Provided externally */
#else
@@ -326,6 +333,9 @@ static const struct spi_ops_s g_sp2iops =
.sndblock = spi_sndblock,
.recvblock = spi_recvblock,
#endif
#ifdef CONFIG_SPI_TRIGGER
.trigger = spi_trigger,
#endif
#ifdef CONFIG_SPI_CALLBACK
.registercallback = stm32_spi2register, /* provided externally */
#else
@@ -375,6 +385,9 @@ static const struct spi_ops_s g_sp3iops =
.sndblock = spi_sndblock,
.recvblock = spi_recvblock,
#endif
#ifdef CONFIG_SPI_TRIGGER
.trigger = spi_trigger,
#endif
#ifdef CONFIG_SPI_CALLBACK
.registercallback = stm32_spi3register, /* provided externally */
#else
@@ -424,6 +437,9 @@ static const struct spi_ops_s g_sp4iops =
.sndblock = spi_sndblock,
.recvblock = spi_recvblock,
#endif
#ifdef CONFIG_SPI_TRIGGER
.trigger = spi_trigger,
#endif
#ifdef CONFIG_SPI_CALLBACK
.registercallback = stm32_spi4register, /* provided externally */
#else
@@ -473,6 +489,9 @@ static const struct spi_ops_s g_sp5iops =
.sndblock = spi_sndblock,
.recvblock = spi_recvblock,
#endif
#ifdef CONFIG_SPI_TRIGGER
.trigger = spi_trigger,
#endif
#ifdef CONFIG_SPI_CALLBACK
.registercallback = stm32_spi5register, /* provided externally */
#else
@@ -522,6 +541,9 @@ static const struct spi_ops_s g_sp6iops =
.sndblock = spi_sndblock,
.recvblock = spi_recvblock,
#endif
#ifdef CONFIG_SPI_TRIGGER
.trigger = spi_trigger,
#endif
#ifdef CONFIG_SPI_CALLBACK
.registercallback = stm32_spi6register, /* provided externally */
#else
@@ -1428,12 +1450,23 @@ static int spi_hwfeatures(FAR struct spi_dev_s *dev, spi_hwfeatures_t features)
spi_modifycr1(priv, setbits, clrbits);
spi_modifycr1(priv, SPI_CR1_SPE, 0);
features &= ~HWFEAT_LSBFIRST;
#endif
#ifdef CONFIG_SPI_TRIGGER
/* Turn deferred trigger mode on or off. Only applicable for DMA mode. If a
* transfer is deferred then the DMA will not actually be triggered until a
* subsequent call to SPI_TRIGGER to set it off. The thread will be waiting
* on the transfer completing as normal.
*/
priv->defertrig = ((features & HWFEAT_TRIGGER) != 0);
features &= ~HWFEAT_TRIGGER;
#endif
/* Other H/W features are not supported */
return ((features & ~HWFEAT_LSBFIRST) == 0) ? OK : -ENOSYS;
#else
return -ENOSYS;
#endif
return (features == 0) ? OK : -ENOSYS;
}
#endif
@@ -1634,19 +1667,78 @@ static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer,
spi_dmarxsetup(priv, rxbuffer, &rxdummy, nwords);
spi_dmatxsetup(priv, txbuffer, &txdummy, nwords);
#ifdef CONFIG_SPI_TRIGGER
/* Is deferred triggering in effect? */
if (!priv->defertrig)
{
/* No.. Start the DMAs */
spi_dmarxstart(priv);
spi_dmatxstart(priv);
}
else
{
/* Yes.. indicated that we are ready to be started */
priv->trigarmed = true;
}
#else
/* Start the DMAs */
spi_dmarxstart(priv);
spi_dmatxstart(priv);
#endif
/* Then wait for each to complete */
spi_dmarxwait(priv);
spi_dmatxwait(priv);
#ifdef CONFIG_SPI_TRIGGER
priv->trigarmed = false;
#endif
}
}
#endif /* CONFIG_STM32_SPI_DMA */
/****************************************************************************
* Name: spi_trigger
*
* Description:
* Trigger a previously configured DMA transfer.
*
* Input Parameters:
* dev - Device-specific state data
*
* Returned Value:
* OK - Trigger was fired
* ENOTSUP - Trigger not fired due to lack of DMA support
* EIO - Trigger not fired because not previously primed
*
****************************************************************************/
#ifdef CONFIG_SPI_TRIGGER
static int spi_trigger(FAR struct spi_dev_s *dev)
{
#ifdef CONFIG_STM32_SPI_DMA
FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)dev;
if (!priv->trigarmed)
{
return -EIO;
}
spi_dmarxstart(priv);
spi_dmatxstart(priv);
return OK;
#else
return -ENOSYS;
#endif
}
#endif
/****************************************************************************
* Name: spi_sndblock
*
+11
View File
@@ -114,6 +114,17 @@ config SPI_CS_DELAY_CONTROL
This option enables the setdelay() interface method.
config SPI_TRIGGER
bool "SPI DMA trigger"
default n
select SPI_HWFEATURES
depends on SPI_EXCHANGE
---help---
Some architectures benefit from delaying the start of DMA from the
DMA setup. If this option is selected, then an SPI_TRIGGER() method
is supported: The DMA is setup with in in SPI_EXCHANGE() but does
not actually begin until SPI_TRIGGER() is called.
config SPI_DRIVER
bool "SPI character driver"
default n
+30
View File
@@ -225,6 +225,10 @@
* condition. (see spi_exchange)
* Bit 4: HWFEAT_LSBFIRST
* Data transferred LSB first (default is MSB first)
* Bit 5: Turn deferred trigger mode on or off. Primarily used for DMA
* mode. If a transfer is deferred then the DMA will not actually
* be triggered until a subsequent call to SPI_TRIGGER to set it
* off.
*/
# ifdef CONFIG_SPI_CRCGENERATION
@@ -243,6 +247,10 @@
# define HWFEAT_LSBFIRST (1 << 4)
# endif
# ifdef CONFIG_SPI_TRIGGER
# define HWFEAT_TRIGGER (1 << 5)
# endif
#else
/* Any attempt to select hardware features with CONFIG_SPI_HWFEATURES
* deselected will return an -ENOSYS error.
@@ -420,6 +428,25 @@
#define SPI_REGISTERCALLBACK(d,c,a) \
((d)->ops->registercallback ? (d)->ops->registercallback(d,c,a) : -ENOSYS)
/****************************************************************************
* Name: SPI_TRIGGER
*
* Description:
* Trigger a previously configured DMA transfer.
*
* Input Parameters:
* dev - Device-specific state data
*
* Returned Value:
* OK - Trigger was fired
* -ENOSYS - Trigger not fired due to lack of DMA or low level support
* -EIO - Trigger not fired because not previously primed
*
****************************************************************************/
# define SPI_TRIGGER(d) \
(((d)->ops->trigger) ? ((d)->ops->trigger(d)) : -ENOSYS)
/* SPI Device Macros ********************************************************/
/* This builds a SPI devid from its type and index */
@@ -549,6 +576,9 @@ struct spi_ops_s
FAR const void *buffer, size_t nwords);
CODE void (*recvblock)(FAR struct spi_dev_s *dev, FAR void *buffer,
size_t nwords);
#endif
#ifdef CONFIG_SPI_TRIGGER
CODE int (*trigger)(FAR struct spi_dev_s *dev);
#endif
CODE int (*registercallback)(FAR struct spi_dev_s *dev,
spi_mediachange_t callback, void *arg);