diff --git a/os/common/startup/ARMCMx/compilers/GCC/ld/STM32H563xI.ld b/os/common/startup/ARMCMx/compilers/GCC/ld/STM32H563xI.ld index b48085d11..0f2e48534 100644 --- a/os/common/startup/ARMCMx/compilers/GCC/ld/STM32H563xI.ld +++ b/os/common/startup/ARMCMx/compilers/GCC/ld/STM32H563xI.ld @@ -81,5 +81,22 @@ REGION_ALIAS("BSS_RAM", ram0); /* RAM region to be used for the default heap.*/ REGION_ALIAS("HEAP_RAM", ram0); +/* RAM region to be used for GPDMA link structures.*/ +REGION_ALIAS("GPDMA_RAM", ram0); + +SECTIONS +{ + /* Special section for GPDMA links, it must not exceed 64kB in size.*/ + .gpdma (NOLOAD) : ALIGN(16) + { + __gpdma_base__ = .; + *(.gpdma) + *(.gpdma.*) + *(.bss.__gpdma_*) + . = ALIGN(4); + __gpdma_end__ = .; + } > GPDMA_RAM +} + /* Generic rules inclusion.*/ INCLUDE rules.ld diff --git a/os/hal/ports/STM32/LLD/GPDMAv1/stm32_gpdma.c b/os/hal/ports/STM32/LLD/GPDMAv1/stm32_gpdma.c index e1e6c7340..aab27622d 100644 --- a/os/hal/ports/STM32/LLD/GPDMAv1/stm32_gpdma.c +++ b/os/hal/ports/STM32/LLD/GPDMAv1/stm32_gpdma.c @@ -572,10 +572,6 @@ void gpdmaChannelFree(const stm32_gpdma_channel_t *dmachp) { * @details The function disables the specified channel and then clears any * pending interrupt. * @note This function can be invoked in both ISR or thread context. - * @note Interrupts enabling flags are set to zero after this call, see - * bug 3607518. - * @pre The channel must have been allocated using @p dmaChannelAlloc(). - * @post After use the channel can be released using @p dmaChannelRelease(). * * @param[in] dmachp pointer to a @p stm32_gpdma_channel_t structure * diff --git a/os/hal/ports/STM32/LLD/GPDMAv1/stm32_gpdma.h b/os/hal/ports/STM32/LLD/GPDMAv1/stm32_gpdma.h index 013db29a6..c0f32a5c3 100644 --- a/os/hal/ports/STM32/LLD/GPDMAv1/stm32_gpdma.h +++ b/os/hal/ports/STM32/LLD/GPDMAv1/stm32_gpdma.h @@ -380,173 +380,6 @@ typedef struct { /* Driver macros. */ /*===========================================================================*/ -/** - * @name Macro Functions - * @{ - */ -/** - * @brief Associates a peripheral data register to a GPDMA channel. - * @note This function can be invoked in both ISR or thread context. - * @pre The channel must have been allocated using @p dmaStreamAlloc(). - * @post After use the channel can be released using @p dmaStreamRelease(). - * - * @param[in] dmachp pointer to a @p stm32_gpdma_channel_t structure - * @param[in] addr value to be written in the CPAR register - * - * @special - */ -#define gpdmaStreamSetPeripheral(dmachp, addr) { \ - (dmachp)->channel->CPAR = (uint32_t)(addr); \ -} - -/** - * @brief Associates a memory destination to a GPDMA channel. - * @note This function can be invoked in both ISR or thread context. - * @pre The channel must have been allocated using @p dmaStreamAlloc(). - * @post After use the channel can be released using @p dmaStreamRelease(). - * - * @param[in] dmachp pointer to a @p stm32_gpdma_channel_t structure - * @param[in] addr value to be written in the CMAR register - * - * @special - */ -#define gpdmaStreamSetMemory0(dmachp, addr) { \ - (dmachp)->channel->CMAR = (uint32_t)(addr); \ -} - -/** - * @brief Sets the number of transfers to be performed. - * @note This function can be invoked in both ISR or thread context. - * @pre The channel must have been allocated using @p dmaStreamAlloc(). - * @post After use the channel can be released using @p dmaStreamRelease(). - * - * @param[in] dmachp pointer to a @p stm32_gpdma_channel_t structure - * @param[in] size value to be written in the CNDTR register - * - * @special - */ -#define gpdmaStreamSetTransactionSize(dmachp, size) { \ - (dmachp)->channel->CNDTR = (uint32_t)(size); \ -} - -/** - * @brief Returns the number of transfers to be performed. - * @note This function can be invoked in both ISR or thread context. - * @pre The channel must have been allocated using @p dmaStreamAlloc(). - * @post After use the channel can be released using @p dmaStreamRelease(). - * - * @param[in] dmachp pointer to a @p stm32_gpdma_channel_t structure - * @return The number of transfers to be performed. - * - * @special - */ -#define gpdmaStreamGetTransactionSize(dmachp) ((size_t)((dmachp)->channel->CNDTR)) - -/** - * @brief Programs the channel mode settings. - * @note This function can be invoked in both ISR or thread context. - * @pre The channel must have been allocated using @p dmaStreamAlloc(). - * @post After use the channel can be released using @p dmaStreamRelease(). - * - * @param[in] dmachp pointer to a @p stm32_gpdma_channel_t structure - * @param[in] mode value to be written in the CCR register - * - * @special - */ -#define gpdmaStreamSetMode(dmachp, mode) { \ - (dmachp)->channel->CCR = (uint32_t)(mode); \ -} - -/** - * @brief GPDMA channel enable. - * @note This function can be invoked in both ISR or thread context. - * @pre The channel must have been allocated using @p dmaStreamAlloc(). - * @post After use the channel can be released using @p dmaStreamRelease(). - * - * @param[in] dmachp pointer to a @p stm32_gpdma_channel_t structure - * - * @special - */ -#define gpdmaStreamEnable(dmachp) { \ - (dmachp)->channel->CCR |= STM32_GPDMA_CR_EN; \ -} - -/** - * @brief GPDMA channel disable. - * @details The function disables the specified channel and then clears any - * pending interrupt. - * @note This function can be invoked in both ISR or thread context. - * @note Interrupts enabling flags are set to zero after this call, see - * bug 3607518. - * @pre The channel must have been allocated using @p dmaStreamAlloc(). - * @post After use the channel can be released using @p dmaStreamRelease(). - * - * @param[in] dmachp pointer to a @p stm32_gpdma_channel_t structure - * - * @special - */ -#define ______gpdmaStreamDisable(dmachp) { \ - (dmachp)->channel->CCR &= ~(STM32_GPDMA_CR_TCIE | STM32_GPDMA_CR_HTIE | \ - STM32_GPDMA_CR_TEIE | STM32_GPDMA_CR_EN); \ - dmaStreamClearInterrupt(dmachp); \ -} - -/** - * @brief GPDMA channel interrupt sources clear. - * @note This function can be invoked in both ISR or thread context. - * @pre The channel must have been allocated using @p dmaStreamAlloc(). - * @post After use the channel can be released using @p dmaStreamRelease(). - * - * @param[in] dmachp pointer to a @p stm32_gpdma_channel_t structure - * - * @special - */ -#define gpdmaStreamClearInterrupt(dmachp) { \ - (dmachp)->dma->IFCR = STM32_GPDMA_ISR_MASK << (dmachp)->shift; \ -} - -/** - * @brief Starts a memory to memory operation using the specified channel. - * @note The default transfer data mode is "byte to byte" but it can be - * changed by specifying extra options in the @p mode parameter. - * @pre The channel must have been allocated using @p dmaStreamAlloc(). - * @post After use the channel can be released using @p dmaStreamRelease(). - * - * @param[in] dmachp pointer to a @p stm32_gpdma_channel_t structure - * @param[in] mode value to be written in the CCR register, this value - * is implicitly ORed with: - * - @p STM32_GPDMA_CR_MINC - * - @p STM32_GPDMA_CR_PINC - * - @p STM32_GPDMA_CR_DIR_M2M - * - @p STM32_GPDMA_CR_EN - * . - * @param[in] src source address - * @param[in] dst destination address - * @param[in] n number of data units to copy - */ -#define gpdmaStartMemCopy(dmachp, mode, src, dst, n) { \ - dmaStreamSetPeripheral(dmachp, src); \ - dmaStreamSetMemory0(dmachp, dst); \ - dmaStreamSetTransactionSize(dmachp, n); \ - dmaStreamSetMode(dmachp, (mode) | \ - STM32_GPDMA_CR_MINC | STM32_GPDMA_CR_PINC | \ - STM32_GPDMA_CR_DIR_M2M | STM32_GPDMA_CR_EN); \ -} - -/** - * @brief Polled wait for GPDMA transfer end. - * @pre The channel must have been allocated using @p dmaStreamAlloc(). - * @post After use the channel can be released using @p dmaStreamRelease(). - * - * @param[in] dmachp pointer to a @p stm32_gpdma_channel_t structure - */ -#define gpdmaWaitCompletion(dmachp) { \ - while ((dmachp)->channel->CNDTR > 0U) \ - ; \ - dmaStreamDisable(dmachp); \ -} -/** @} */ - /*===========================================================================*/ /* External declarations. */ /*===========================================================================*/ @@ -555,6 +388,13 @@ typedef struct { extern const stm32_gpdma_channel_t __stm32_gpdma_channels[STM32_GPDMA_NUM_CHANNELS]; #endif +/* The linker needs to export this symbol if the link mode of GPDMA is required, + the symbol needs to be aligned to a 64k boundary and marks the base of an + area where all symbols with names starting with __gpdma_ are collected. + On devices with data cache this area shall be placed at beginning of a + non-cachable area.*/ +extern uint32_t __gpdma_base__; + #ifdef __cplusplus extern "C" { #endif diff --git a/os/hal/ports/STM32/LLD/SPIv4/hal_spi_v2_lld.c b/os/hal/ports/STM32/LLD/SPIv4/hal_spi_v2_lld.c index 7c893f80b..f727a8ac8 100644 --- a/os/hal/ports/STM32/LLD/SPIv4/hal_spi_v2_lld.c +++ b/os/hal/ports/STM32/LLD/SPIv4/hal_spi_v2_lld.c @@ -68,6 +68,30 @@ SPIDriver SPID6; /* Driver local variables and types. */ /*===========================================================================*/ +#if STM32_SPI_USE_SPI1 || defined(__DOXYGEN__) +static spi_dmabuf_t __gpdma_spi1; +#endif + +#if STM32_SPI_USE_SPI2 || defined(__DOXYGEN__) +static spi_dmabuf_t __gpdma_spi2; +#endif + +#if STM32_SPI_USE_SPI3 || defined(__DOXYGEN__) +static spi_dmabuf_t __gpdma_spi3; +#endif + +#if STM32_SPI_USE_SPI4 || defined(__DOXYGEN__) +static spi_dmabuf_t __gpdma_spi4; +#endif + +#if STM32_SPI_USE_SPI5 || defined(__DOXYGEN__) +static spi_dmabuf_t __gpdma_spi5; +#endif + +#if STM32_SPI_USE_SPI6 || defined(__DOXYGEN__) +static spi_dmabuf_t __gpdma_spi6; +#endif + /*===========================================================================*/ /* Driver local functions. */ /*===========================================================================*/ @@ -455,6 +479,7 @@ void spi_lld_init(void) { SPID1.dreqrx = STM32_GPDMA_REQ_SPI1_RX; SPID1.dreqtx = STM32_GPDMA_REQ_SPI1_TX; SPID1.dprio = STM32_SPI_SPI1_DMA_PRIORITY; + SPID1.dbuf = &__gpdma_spi1; #if !defined(STM32_SPI1_SUPPRESS_ISR) nvicEnableVector(STM32_SPI1_NUMBER, STM32_SPI_SPI1_IRQ_PRIORITY); #endif @@ -468,6 +493,7 @@ void spi_lld_init(void) { SPID2.dreqrx = STM32_GPDMA_REQ_SPI2_RX; SPID2.dreqtx = STM32_GPDMA_REQ_SPI2_TX; SPID2.dprio = STM32_SPI_SPI2_DMA_PRIORITY; + SPID2.dbuf = &__gpdma_spi2; #if !defined(STM32_SPI2_SUPPRESS_ISR) nvicEnableVector(STM32_SPI2_NUMBER, STM32_SPI_SPI2_IRQ_PRIORITY); #endif @@ -481,6 +507,7 @@ void spi_lld_init(void) { SPID3.dreqrx = STM32_GPDMA_REQ_SPI3_RX; SPID3.dreqtx = STM32_GPDMA_REQ_SPI3_TX; SPID3.dprio = STM32_SPI_SPI3_DMA_PRIORITY; + SPID3.dbuf = &__gpdma_spi3; #if !defined(STM32_SPI3_SUPPRESS_ISR) nvicEnableVector(STM32_SPI3_NUMBER, STM32_SPI_SPI3_IRQ_PRIORITY); #endif @@ -494,6 +521,7 @@ void spi_lld_init(void) { SPID4.dreqrx = STM32_GPDMA_REQ_SPI4_RX; SPID4.dreqtx = STM32_GPDMA_REQ_SPI4_TX; SPID4.dprio = STM32_SPI_SPI4_DMA_PRIORITY; + SPID4.dbuf = &__gpdma_spi4; #if !defined(STM32_SPI4_SUPPRESS_ISR) nvicEnableVector(STM32_SPI4_NUMBER, STM32_SPI_SPI4_IRQ_PRIORITY); #endif @@ -507,6 +535,7 @@ void spi_lld_init(void) { SPID5.dreqrx = STM32_GPDMA_REQ_SPI5_RX; SPID5.dreqtx = STM32_GPDMA_REQ_SPI5_TX; SPID5.dprio = STM32_SPI_SPI5_DMA_PRIORITY; + SPID5.dbuf = &__gpdma_spi5; #if !defined(STM32_SPI5_SUPPRESS_ISR) nvicEnableVector(STM32_SPI5_NUMBER, STM32_SPI_SPI5_IRQ_PRIORITY); #endif @@ -520,6 +549,7 @@ void spi_lld_init(void) { SPID6.dreqrx = STM32_GPDMA_REQ_SPI6_RX; SPID6.dreqtx = STM32_GPDMA_REQ_SPI6_TX; SPID6.dprio = STM32_SPI_SPI6_DMA_PRIORITY; + SPID6.dbuf = &__gpdma_spi6; #if !defined(STM32_SPI6_SUPPRESS_ISR) nvicEnableVector(STM32_SPI6_NUMBER, STM32_SPI_SPI6_IRQ_PRIORITY); #endif @@ -538,8 +568,9 @@ msg_t spi_lld_start(SPIDriver *spip) { uint32_t dsize, dmaccr, dmalbar; msg_t msg; - /* Resetting TX pattern source.*/ - spip->txsource = (uint32_t)STM32_SPI_FILLER_PATTERN; + /* Resetting TX pattern source, clearing RX sink.*/ + spip->dbuf->rxsink = 0U; + spip->dbuf->txsource = (uint32_t)STM32_SPI_FILLER_PATTERN; /* If in stopped state then enables the SPI and GPDMA clocks.*/ if (spip->state == SPI_STOP) { @@ -642,16 +673,23 @@ msg_t spi_lld_start(SPIDriver *spip) { } /* Configuration-specific GPDMA setup.*/ - dmalbar = 0U; dmaccr = STM32_GPDMA_CCR_PRIO((uint32_t)spip->dprio) | STM32_GPDMA_CCR_TOIE | STM32_GPDMA_CCR_USEIE | STM32_GPDMA_CCR_ULEIE | STM32_GPDMA_CCR_DTEIE | STM32_GPDMA_CCR_TCIE; -// if (spip->config->circular) { -// dmaccr |= STM32_GPDMA_CCR_HTIE; -// } +#if SPI_SUPPORTS_CIRCULAR + dmalbar = (uint32_t)&__gpdma_base__; + + osalDbgAssert((dmalbar &0xFFFFU) == 0U, "unaligned LBAR"); + + if (spip->config->circular) { + dmaccr |= STM32_GPDMA_CCR_HTIE; + } +#else + dmalbar = 0U; +#endif gpdmaChannelInit(spip->dmarx, dmalbar, dmaccr); gpdmaChannelSetSource(spip->dmarx, &spip->spi->RXDR); gpdmaChannelInit(spip->dmatx, dmalbar, dmaccr); @@ -809,7 +847,7 @@ msg_t spi_lld_ignore(SPIDriver *spip, size_t n) { osalDbgAssert(n <= STM32_GPDMA_MAX_TRANSFER, "unsupported GPDMA transfer size"); /* Setting up RX DMA channel.*/ - gpdmaChannelSetDestination(spip->dmarx, &spip->rxsink); + gpdmaChannelSetDestination(spip->dmarx, &spip->dbuf->rxsink); gpdmaChannelTransactionSize(spip->dmarx, n); gpdmaChannelSetMode(spip->dmarx, (spip->config->dtr1rx | @@ -820,7 +858,7 @@ msg_t spi_lld_ignore(SPIDriver *spip, size_t n) { gpdmaChannelEnable(spip->dmarx); /* Setting up TX DMA channel.*/ - gpdmaChannelSetSource(spip->dmatx, &spip->txsource); + gpdmaChannelSetSource(spip->dmatx, &spip->dbuf->txsource); gpdmaChannelTransactionSize(spip->dmatx, n); gpdmaChannelSetMode(spip->dmatx, (spip->config->dtr1tx | @@ -854,11 +892,33 @@ msg_t spi_lld_ignore(SPIDriver *spip, size_t n) { */ msg_t spi_lld_exchange(SPIDriver *spip, size_t n, const void *txbuf, void *rxbuf) { + uint32_t llrrx, llrtx; osalDbgAssert(n <= STM32_GPDMA_CCR_PRIO_POS, "unsupported GPDMA transfer size"); - /* Setting up RX DMA channel.*/ +#if SPI_SUPPORTS_CIRCULAR + if (spip->config->circular) { + /* It is a circular operation, using the linking mechanism to reload + source/destination pointers.*/ + llrrx = STM32_GPDMA_CLLR_UDA | (((uint32_t)&spip->dbuf->rxdar) & 0xFFFFU); + spip->dbuf->rxdar = (uint32_t)rxbuf; + llrtx = STM32_GPDMA_CLLR_USA | (((uint32_t)&spip->dbuf->txsar) & 0xFFFFU); + spip->dbuf->txsar = (uint32_t)txbuf; + } + else { + llrrx = 0U; + llrtx = 0U; + gpdmaChannelSetDestination(spip->dmarx, rxbuf); + gpdmaChannelSetSource(spip->dmatx, txbuf); + } +#else + llrrx = 0U; + llrtx = 0U; gpdmaChannelSetDestination(spip->dmarx, rxbuf); + gpdmaChannelSetSource(spip->dmatx, txbuf); +#endif + + /* Setting up RX DMA channel.*/ gpdmaChannelTransactionSize(spip->dmarx, n); gpdmaChannelSetMode(spip->dmarx, (spip->config->dtr1rx | @@ -866,11 +926,10 @@ msg_t spi_lld_exchange(SPIDriver *spip, size_t n, STM32_GPDMA_CTR1_DINC), (spip->config->dtr2rx | STM32_GPDMA_CTR2_REQSEL(spip->dreqrx)), - 0U); + llrrx); gpdmaChannelEnable(spip->dmarx); /* Setting up TX DMA channel.*/ - gpdmaChannelSetSource(spip->dmatx, txbuf); gpdmaChannelTransactionSize(spip->dmatx, n); gpdmaChannelSetMode(spip->dmatx, (spip->config->dtr1tx | @@ -879,7 +938,7 @@ msg_t spi_lld_exchange(SPIDriver *spip, size_t n, (spip->config->dtr2tx | STM32_GPDMA_CTR2_REQSEL(spip->dreqtx) | STM32_GPDMA_CTR2_DREQ), - 0U); + llrtx); gpdmaChannelEnable(spip->dmatx); spi_lld_resume(spip); @@ -906,7 +965,7 @@ msg_t spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf) { osalDbgAssert(n <= STM32_GPDMA_CCR_PRIO_POS, "unsupported GPDMA transfer size"); /* Setting up RX DMA channel.*/ - gpdmaChannelSetDestination(spip->dmarx, &spip->rxsink); + gpdmaChannelSetDestination(spip->dmarx, &spip->dbuf->rxsink); gpdmaChannelTransactionSize(spip->dmarx, n); gpdmaChannelSetMode(spip->dmarx, (spip->config->dtr1rx | @@ -966,7 +1025,7 @@ msg_t spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf) { gpdmaChannelEnable(spip->dmarx); /* Setting up TX DMA channel.*/ - gpdmaChannelSetSource(spip->dmatx, &spip->txsource); + gpdmaChannelSetSource(spip->dmatx, &spip->dbuf->txsource); gpdmaChannelTransactionSize(spip->dmatx, n); gpdmaChannelSetMode(spip->dmatx, (spip->config->dtr1tx | diff --git a/os/hal/ports/STM32/LLD/SPIv4/hal_spi_v2_lld.h b/os/hal/ports/STM32/LLD/SPIv4/hal_spi_v2_lld.h index e97bf3a4a..78c3baa0b 100644 --- a/os/hal/ports/STM32/LLD/SPIv4/hal_spi_v2_lld.h +++ b/os/hal/ports/STM32/LLD/SPIv4/hal_spi_v2_lld.h @@ -31,15 +31,23 @@ /* Driver constants. */ /*===========================================================================*/ +#if !defined(__SPI_DISABLE_CIRCULAR) /** * @brief Circular mode support flag. */ +#define SPI_SUPPORTS_CIRCULAR TRUE +#else #define SPI_SUPPORTS_CIRCULAR FALSE +#endif +#if !defined(__SPI_DISABLE_SLAVE) /** * @brief Slave mode support flag. */ #define SPI_SUPPORTS_SLAVE_MODE TRUE +#else +#define SPI_SUPPORTS_SLAVE_MODE FALSE +#endif /** * @name Register helpers not found in ST headers @@ -449,6 +457,30 @@ /* Driver data structures and types. */ /*===========================================================================*/ +/** + * @brief Type of a structure containin DMA-accessible driver fields. + */ +typedef struct spi_dmabuf { + /** + * @brief Sink for discarded data. + */ + uint32_t rxsink; + /** + * @brief Source for default TX pattern. + */ + uint32_t txsource; +#if SPI_SUPPORTS_CIRCULAR || defined(__DOXYGEN__) + /** + * @brief GPDMA link structure for circular mode RX channel. + */ + uint32_t rxdar; + /** + * @brief GPDMA link structure for circular mode TX channel. + */ + uint32_t txsar; +#endif +} spi_dmabuf_t; + /*===========================================================================*/ /* Driver macros. */ /*===========================================================================*/ @@ -470,10 +502,8 @@ uint32_t dtr1rx; \ /* DMA TX settings.*/ \ uint32_t dtr1tx; \ - /* Sink for discarded data.*/ \ - uint32_t rxsink; \ - /* Source for default TX pattern.*/ \ - uint32_t txsource + /* DMA buffers.*/ \ + spi_dmabuf_t *dbuf /** * @brief Low level fields of the SPI configuration structure.