diff --git a/arch/arm/src/stm32/stm32_spi.c b/arch/arm/src/stm32/stm32_spi.c index 08bbba26a9c..7654b630edf 100644 --- a/arch/arm/src/stm32/stm32_spi.c +++ b/arch/arm/src/stm32/stm32_spi.c @@ -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 * diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index a285f342237..50210bf4406 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -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 diff --git a/include/nuttx/spi/spi.h b/include/nuttx/spi/spi.h index f4b64f2cc23..69f9f903c40 100644 --- a/include/nuttx/spi/spi.h +++ b/include/nuttx/spi/spi.h @@ -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);