mirror of
https://github.com/apache/nuttx.git
synced 2026-05-19 03:03:37 +08:00
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:
committed by
Gregory Nutt
parent
d2b98cc150
commit
8328539534
@@ -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
|
||||
*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user