diff --git a/arch/arm/src/stm32f7/stm32_spi.c b/arch/arm/src/stm32f7/stm32_spi.c index 5fa7eb4a74..e776309c57 100644 --- a/arch/arm/src/stm32f7/stm32_spi.c +++ b/arch/arm/src/stm32f7/stm32_spi.c @@ -153,6 +153,10 @@ struct stm32_spidev_s #ifdef CONFIG_STM32F7_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 */ @@ -216,6 +220,9 @@ static int spi_hwfeatures(FAR struct spi_dev_s *dev, static uint16_t spi_send(FAR struct spi_dev_s *dev, uint16_t wd); static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, FAR void *rxbuffer, size_t nwords); +#ifdef CONFIG_SPI_TRIGGER +static int spi_trigger(FAR struct spi_dev_s *dev); +#endif #ifndef CONFIG_SPI_EXCHANGE static void spi_sndblock(FAR struct spi_dev_s *dev, FAR const void *txbuffer, size_t nwords); @@ -260,6 +267,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 @@ -307,6 +317,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 @@ -354,6 +367,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 @@ -401,6 +417,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 @@ -448,6 +467,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 @@ -495,6 +517,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 @@ -1380,8 +1405,11 @@ static void spi_setbits(FAR struct spi_dev_s *dev, int nbits) #ifdef CONFIG_SPI_HWFEATURES static int spi_hwfeatures(FAR struct spi_dev_s *dev, spi_hwfeatures_t features) { -#ifdef CONFIG_SPI_BITORDER +#if defined(CONFIG_SPI_BITORDER) || defined(CONFIG_SPI_TRIGGER) FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)dev; +#endif + +#ifdef CONFIG_SPI_BITORDER uint16_t setbitscr1; uint16_t clrbitscr1; uint16_t setbitscr2; @@ -1407,12 +1435,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 @@ -1644,16 +1683,38 @@ static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, arch_flush_dcache((uintptr_t)txbuffer, (uintptr_t)txbuffer + buflen); } +#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 + /* Force RAM re-read */ if (rxbuffer) @@ -1670,6 +1731,43 @@ static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, } #endif /* CONFIG_STM32F7_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_STM32F7_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/arch/arm/src/stm32h7/stm32_spi.c b/arch/arm/src/stm32h7/stm32_spi.c index f5f9b882f8..413933ee19 100644 --- a/arch/arm/src/stm32h7/stm32_spi.c +++ b/arch/arm/src/stm32h7/stm32_spi.c @@ -196,6 +196,10 @@ struct stm32_spidev_s #ifdef CONFIG_STM32H7_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 */ @@ -262,6 +266,9 @@ static int spi_hwfeatures(FAR struct spi_dev_s *dev, static uint16_t spi_send(FAR struct spi_dev_s *dev, uint16_t wd); static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, FAR void *rxbuffer, size_t nwords); +#ifdef CONFIG_SPI_TRIGGER +static int spi_trigger(FAR struct spi_dev_s *dev); +#endif #ifndef CONFIG_SPI_EXCHANGE static void spi_sndblock(FAR struct spi_dev_s *dev, FAR const void *txbuffer, size_t nwords); @@ -306,6 +313,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 @@ -353,6 +363,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 @@ -400,6 +413,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 @@ -447,6 +463,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 @@ -494,6 +513,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 @@ -541,6 +563,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 @@ -1424,8 +1449,11 @@ static void spi_setbits(FAR struct spi_dev_s *dev, int nbits) #ifdef CONFIG_SPI_HWFEATURES static int spi_hwfeatures(FAR struct spi_dev_s *dev, spi_hwfeatures_t features) { -#ifdef CONFIG_SPI_BITORDER +#if defined(CONFIG_SPI_BITORDER) || defined(CONFIG_SPI_TRIGGER) FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)dev; +#endif + +#ifdef CONFIG_SPI_BITORDER uint32_t setbits = 0; uint32_t clrbits = 0; @@ -1448,12 +1476,23 @@ static int spi_hwfeatures(FAR struct spi_dev_s *dev, spi_hwfeatures_t features) spi_modifyreg(priv, STM32_SPI_CFG2_OFFSET, clrbits, setbits); spi_enable(priv, true); + 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 @@ -1700,16 +1739,38 @@ static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, arch_flush_dcache((uintptr_t)txbuffer, (uintptr_t)txbuffer + buflen); } +#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 + /* Force RAM re-read */ if (rxbuffer) @@ -1726,6 +1787,43 @@ static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, } #endif /* CONFIG_STM32H7_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_STM32H7_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/arch/arm/src/stm32l4/stm32l4_spi.c b/arch/arm/src/stm32l4/stm32l4_spi.c index f5dd7cf373..076466e5f9 100644 --- a/arch/arm/src/stm32l4/stm32l4_spi.c +++ b/arch/arm/src/stm32l4/stm32l4_spi.c @@ -154,6 +154,10 @@ struct stm32l4_spidev_s #ifdef CONFIG_STM32L4_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 uint16_t rxch; /* The RX DMA channel number */ uint16_t txch; /* The TX DMA channel number */ DMA_HANDLE rxdma; /* DMA channel handle for RX transfers */ @@ -217,6 +221,9 @@ static int spi_hwfeatures(FAR struct spi_dev_s *dev, static uint16_t spi_send(FAR struct spi_dev_s *dev, uint16_t wd); static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, FAR void *rxbuffer, size_t nwords); +#ifdef CONFIG_SPI_TRIGGER +static int spi_trigger(FAR struct spi_dev_s *dev); +#endif #ifndef CONFIG_SPI_EXCHANGE static void spi_sndblock(FAR struct spi_dev_s *dev, FAR const void *txbuffer, size_t nwords); @@ -261,6 +268,9 @@ static const struct spi_ops_s g_spi1ops = .sndblock = spi_sndblock, .recvblock = spi_recvblock, #endif +#ifdef CONFIG_SPI_TRIGGER + .trigger = spi_trigger, +#endif #ifdef CONFIG_SPI_CALLBACK .registercallback = stm32l4_spi1register, /* Provided externally */ #else @@ -309,6 +319,9 @@ static const struct spi_ops_s g_spi2ops = .sndblock = spi_sndblock, .recvblock = spi_recvblock, #endif +#ifdef CONFIG_SPI_TRIGGER + .trigger = spi_trigger, +#endif #ifdef CONFIG_SPI_CALLBACK .registercallback = stm32l4_spi2register, /* provided externally */ #else @@ -356,6 +369,9 @@ static const struct spi_ops_s g_spi3ops = .sndblock = spi_sndblock, .recvblock = spi_recvblock, #endif +#ifdef CONFIG_SPI_TRIGGER + .trigger = spi_trigger, +#endif #ifdef CONFIG_SPI_CALLBACK .registercallback = stm32l4_spi3register, /* provided externally */ #else @@ -1185,8 +1201,11 @@ static void spi_setbits(FAR struct spi_dev_s *dev, int nbits) #ifdef CONFIG_SPI_HWFEATURES static int spi_hwfeatures(FAR struct spi_dev_s *dev, spi_hwfeatures_t features) { -#ifdef CONFIG_SPI_BITORDER +#if defined(CONFIG_SPI_BITORDER) || defined(CONFIG_SPI_TRIGGER) FAR struct stm32l4_spidev_s *priv = (FAR struct stm32l4_spidev_s *)dev; +#endif + +#ifdef CONFIG_SPI_BITORDER uint16_t setbits; uint16_t clrbits; @@ -1209,12 +1228,23 @@ static int spi_hwfeatures(FAR struct spi_dev_s *dev, spi_hwfeatures_t features) spi_modifycr(STM32L4_SPI_CR1_OFFSET, priv, setbits, clrbits); spi_modifycr(STM32L4_SPI_CR1_OFFSET, 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 @@ -1430,19 +1460,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_STM32L4_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_STM32L4_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 *