From c1e1c5759d05774f1084553c6f8a1d977d2f6ad0 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Mon, 20 Jul 2015 13:54:41 -0600 Subject: [PATCH] STM32 F7: Port the STM32 F7 --- ChangeLog | 5 +- arch/arm/src/stm32f7/Kconfig | 6 + arch/arm/src/stm32f7/Make.defs | 4 + arch/arm/src/stm32f7/stm32_dma.c | 1056 +++++++++++++++++++++++++ arch/arm/src/stm32f7/stm32_dma.h | 8 +- arch/arm/src/stm32f7/stm32_ethernet.c | 2 +- 6 files changed, 1077 insertions(+), 4 deletions(-) create mode 100644 arch/arm/src/stm32f7/stm32_dma.c diff --git a/ChangeLog b/ChangeLog index 48656b96d5..6e2cf5af01 100755 --- a/ChangeLog +++ b/ChangeLog @@ -10744,4 +10744,7 @@ barometer. From Alan Carvalho de Assis (2015-07-20). * configs/stm32f4discovery/: Add support to BMP180 driver on the STM32F4Discovery. From Alan Carvalho de Assis (2015-07-20). - * arch/arm/srch/stm32f7: Add an STM32 F7 Ethernet driver (2015-07-20). + * arch/arm/src/stm32f7: Add an STM32 F7 Ethernet driver (2015-07-20). + * arch/arm/src/stm32f7: Port the STM32 F4 DMA driver. Untested on + initial commit (2015-07-20). + diff --git a/arch/arm/src/stm32f7/Kconfig b/arch/arm/src/stm32f7/Kconfig index f554dadb82..d31b3a6ea5 100644 --- a/arch/arm/src/stm32f7/Kconfig +++ b/arch/arm/src/stm32f7/Kconfig @@ -90,6 +90,10 @@ config STM32F7_DAC bool default n +config STM32F7_DMA + bool + default n + config STM32F7_I2C bool default n @@ -155,11 +159,13 @@ config STM32F7_CRYP config STM32F7_DMA1 bool "DMA1" default n + select STM32F7_DMA select ARCH_DMA config STM32F7_DMA2 bool "DMA2" default n + select STM32F7_DMA select ARCH_DMA config STM32F7_DAC1 diff --git a/arch/arm/src/stm32f7/Make.defs b/arch/arm/src/stm32f7/Make.defs index 521732de92..824175123b 100644 --- a/arch/arm/src/stm32f7/Make.defs +++ b/arch/arm/src/stm32f7/Make.defs @@ -118,6 +118,10 @@ CHIP_CSRCS += stm32_procfs_dtcm.c endif endif +ifeq ($(CONFIG_STM32F7_DMA),y) +CHIP_CSRCS += stm32_dma.c +endif + ifeq ($(CONFIG_STM32_PWR),y) CHIP_CSRCS += stm32_exti_pwr.c endif diff --git a/arch/arm/src/stm32f7/stm32_dma.c b/arch/arm/src/stm32f7/stm32_dma.c new file mode 100644 index 0000000000..798a5965bf --- /dev/null +++ b/arch/arm/src/stm32f7/stm32_dma.c @@ -0,0 +1,1056 @@ +/**************************************************************************** + * arch/arm/src/stm32f7/stm32_dma.c + * + * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "up_arch.h" + +#include "up_internal.h" +#include "sched/sched.h" +#include "stm32_dma.h" + +/* Content of this file requires verification before it is used with other + * families + */ + +#if defined(CONFIG_STM32F7_STM32F74XX) || defined(CONFIG_STM32F7_STM32F75XX) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DMA1_NSTREAMS 8 +#if STM32F7_NDMA > 1 +# define DMA2_NSTREAMS 8 +# define DMA_NSTREAMS (DMA1_NSTREAMS+DMA2_NSTREAMS) +#else +# define DMA_NSTREAMS DMA1_NSTREAMS +#endif + +#ifndef CONFIG_DMA_PRI +# define CONFIG_DMA_PRI NVIC_SYSH_PRIORITY_DEFAULT +#endif + +/* Convert the DMA stream base address to the DMA register block address */ + +#define DMA_BASE(ch) (ch & 0xfffffc00) + +/**************************************************************************** + * Private Types + ****************************************************************************/ +/* This structure describes one DMA channel */ + +struct stm32_dma_s +{ + uint8_t stream; /* DMA stream number (0-7) */ + uint8_t irq; /* DMA stream IRQ number */ + uint8_t shift; /* ISR/IFCR bit shift value */ + uint8_t channel; /* DMA channel number (0-7) */ + sem_t sem; /* Used to wait for DMA channel to become available */ + uint32_t base; /* DMA register channel base address */ + dma_callback_t callback; /* Callback invoked when the DMA completes */ + void *arg; /* Argument passed to callback function */ +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* This array describes the state of each DMA */ + +static struct stm32_dma_s g_dma[DMA_NSTREAMS] = +{ + { + .stream = 0, + .irq = STM32_IRQ_DMA1S0, + .shift = DMA_INT_STREAM0_SHIFT, + .base = STM32_DMA1_BASE + STM32_DMA_OFFSET(0), + }, + { + .stream = 1, + .irq = STM32_IRQ_DMA1S1, + .shift = DMA_INT_STREAM1_SHIFT, + .base = STM32_DMA1_BASE + STM32_DMA_OFFSET(1), + }, + { + .stream = 2, + .irq = STM32_IRQ_DMA1S2, + .shift = DMA_INT_STREAM2_SHIFT, + .base = STM32_DMA1_BASE + STM32_DMA_OFFSET(2), + }, + { + .stream = 3, + .irq = STM32_IRQ_DMA1S3, + .shift = DMA_INT_STREAM3_SHIFT, + .base = STM32_DMA1_BASE + STM32_DMA_OFFSET(3), + }, + { + .stream = 4, + .irq = STM32_IRQ_DMA1S4, + .shift = DMA_INT_STREAM4_SHIFT, + .base = STM32_DMA1_BASE + STM32_DMA_OFFSET(4), + }, + { + .stream = 5, + .irq = STM32_IRQ_DMA1S5, + .shift = DMA_INT_STREAM5_SHIFT, + .base = STM32_DMA1_BASE + STM32_DMA_OFFSET(5), + }, + { + .stream = 6, + .irq = STM32_IRQ_DMA1S6, + .shift = DMA_INT_STREAM6_SHIFT, + .base = STM32_DMA1_BASE + STM32_DMA_OFFSET(6), + }, + { + .stream = 7, + .irq = STM32_IRQ_DMA1S7, + .shift = DMA_INT_STREAM7_SHIFT, + .base = STM32_DMA1_BASE + STM32_DMA_OFFSET(7), + }, +#if STM32F7_NDMA > 1 + { + .stream = 0, + .irq = STM32_IRQ_DMA2S0, + .shift = DMA_INT_STREAM0_SHIFT, + .base = STM32_DMA2_BASE + STM32_DMA_OFFSET(0), + }, + { + .stream = 1, + .irq = STM32_IRQ_DMA2S1, + .shift = DMA_INT_STREAM1_SHIFT, + .base = STM32_DMA2_BASE + STM32_DMA_OFFSET(1), + }, + { + .stream = 2, + .irq = STM32_IRQ_DMA2S2, + .shift = DMA_INT_STREAM2_SHIFT, + .base = STM32_DMA2_BASE + STM32_DMA_OFFSET(2), + }, + { + .stream = 3, + .irq = STM32_IRQ_DMA2S3, + .shift = DMA_INT_STREAM3_SHIFT, + .base = STM32_DMA2_BASE + STM32_DMA_OFFSET(3), + }, + { + .stream = 4, + .irq = STM32_IRQ_DMA2S4, + .base = STM32_DMA2_BASE + STM32_DMA_OFFSET(4), + }, + { + .stream = 5, + .irq = STM32_IRQ_DMA2S5, + .shift = DMA_INT_STREAM5_SHIFT, + .base = STM32_DMA2_BASE + STM32_DMA_OFFSET(5), + }, + { + .stream = 6, + .irq = STM32_IRQ_DMA2S6, + .shift = DMA_INT_STREAM6_SHIFT, + .base = STM32_DMA2_BASE + STM32_DMA_OFFSET(6), + }, + { + .stream = 7, + .irq = STM32_IRQ_DMA2S7, + .shift = DMA_INT_STREAM7_SHIFT, + .base = STM32_DMA2_BASE + STM32_DMA_OFFSET(7), + }, +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * DMA register access functions + ****************************************************************************/ + +/* Get non-channel register from DMA1 or DMA2 */ + +static inline uint32_t dmabase_getreg(struct stm32_dma_s *dmast, uint32_t offset) +{ + return getreg32(DMA_BASE(dmast->base) + offset); +} + +/* Write to non-channel register in DMA1 or DMA2 */ + +static inline void dmabase_putreg(struct stm32_dma_s *dmast, uint32_t offset, uint32_t value) +{ + putreg32(value, DMA_BASE(dmast->base) + offset); +} + +/* Get channel register from DMA1 or DMA2 */ + +static inline uint32_t dmast_getreg(struct stm32_dma_s *dmast, uint32_t offset) +{ + return getreg32(dmast->base + offset); +} + +/* Write to channel register in DMA1 or DMA2 */ + +static inline void dmast_putreg(struct stm32_dma_s *dmast, uint32_t offset, uint32_t value) +{ + putreg32(value, dmast->base + offset); +} + +/************************************************************************************ + * Name: stm32_dmatake() and stm32_dmagive() + * + * Description: + * Used to get exclusive access to a DMA channel. + * + ************************************************************************************/ + +static void stm32_dmatake(FAR struct stm32_dma_s *dmast) +{ + /* Take the semaphore (perhaps waiting) */ + + while (sem_wait(&dmast->sem) != 0) + { + /* The only case that an error should occur here is if the wait was awakened + * by a signal. + */ + + ASSERT(errno == EINTR); + } +} + +static inline void stm32_dmagive(FAR struct stm32_dma_s *dmast) +{ + (void)sem_post(&dmast->sem); +} + +/************************************************************************************ + * Name: stm32_dmastream + * + * Description: + * Get the g_dma table entry associated with a DMA controller and a stream number + * + ************************************************************************************/ + +static inline FAR struct stm32_dma_s *stm32_dmastream(unsigned int stream, + unsigned int controller) +{ + int index; + + DEBUGASSERT(stream < DMA_NSTREAMS && controller < STM32F7_NDMA); + + /* Convert the controller + stream based on the fact that there are 8 streams + * per controller. + */ + +#if STM32F7_NDMA > 1 + index = controller << 3 | stream; +#else + index = stream; +#endif + + /* Then return the stream structure associated with the stream index */ + + return &g_dma[index]; +} + +/************************************************************************************ + * Name: stm32_dmamap + * + * Description: + * Get the g_dma table entry associated with a bit-encoded DMA selection + * + ************************************************************************************/ + +static inline FAR struct stm32_dma_s *stm32_dmamap(unsigned long dmamap) +{ + /* Extract the DMA controller number from the bit encoded value */ + + unsigned int controller = STM32_DMA_CONTROLLER(dmamap); + + /* Extact the stream number from the bit encoded value */ + + unsigned int stream = STM32_DMA_STREAM(dmamap); + + /* Return the table entry associated with the controller + stream */ + + return stm32_dmastream(stream, controller); +} + +/************************************************************************************ + * Name: stm32_dmastreamdisable + * + * Description: + * Disable the DMA stream + * + ************************************************************************************/ + +static void stm32_dmastreamdisable(struct stm32_dma_s *dmast) +{ + uint32_t regoffset; + uint32_t regval; + + /* Disable all interrupts at the DMA controller */ + + regval = dmast_getreg(dmast, STM32_DMA_SCR_OFFSET); + regval &= ~DMA_SCR_ALLINTS; + + /* Disable the DMA stream */ + + regval &= ~DMA_SCR_EN; + dmast_putreg(dmast, STM32_DMA_SCR_OFFSET, regval); + + /* Clear pending stream interrupts by setting bits in the upper or lower IFCR + * register + */ + + if (dmast->stream < 4) + { + regoffset = STM32_DMA_LIFCR_OFFSET; + } + else + { + regoffset = STM32_DMA_HIFCR_OFFSET; + } + + dmabase_putreg(dmast, regoffset, (DMA_STREAM_MASK << dmast->shift)); +} + +/************************************************************************************ + * Name: stm32_dmainterrupt + * + * Description: + * DMA interrupt handler + * + ************************************************************************************/ + +static int stm32_dmainterrupt(int irq, void *context) +{ + struct stm32_dma_s *dmast; + uint32_t status; + uint32_t regoffset = 0; + unsigned int stream = 0; + unsigned int controller = 0; + + /* Get the stream and the controller that generated the interrupt */ + + if (irq >= STM32_IRQ_DMA1S0 && irq <= STM32_IRQ_DMA1S6) + { + stream = irq - STM32_IRQ_DMA1S0; + controller = DMA1; + } + else if (irq == STM32_IRQ_DMA1S7) + { + stream = 7; + controller = DMA1; + } + else +#if STM32F7_NDMA > 1 + if (irq >= STM32_IRQ_DMA2S0 && irq <= STM32_IRQ_DMA2S4) + { + stream = irq - STM32_IRQ_DMA2S0; + controller = DMA2; + } + else if (irq >= STM32_IRQ_DMA2S5 && irq <= STM32_IRQ_DMA2S7) + { + stream = irq - STM32_IRQ_DMA2S5 + 5; + controller = DMA2; + } + else +#endif + { + PANIC(); + } + + /* Get the stream structure from the stream and controller numbers */ + + dmast = stm32_dmastream(stream, controller); + + /* Select the interrupt status register (either the LISR or HISR) + * based on the stream number that caused the interrupt. + */ + + if (stream < 4) + { + regoffset = STM32_DMA_LISR_OFFSET; + } + else + { + regoffset = STM32_DMA_HISR_OFFSET; + } + + /* Get the interrupt status for this stream */ + + status = (dmabase_getreg(dmast, regoffset) >> dmast->shift) & DMA_STREAM_MASK; + + /* Clear fetched stream interrupts by setting bits in the upper or lower IFCR + * register + */ + + if (stream < 4) + { + regoffset = STM32_DMA_LIFCR_OFFSET; + } + else + { + regoffset = STM32_DMA_HIFCR_OFFSET; + } + + dmabase_putreg(dmast, regoffset, (status << dmast->shift)); + + /* Invoke the callback */ + + if (dmast->callback) + { + dmast->callback(dmast, status, dmast->arg); + } + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_dmainitialize + * + * Description: + * Initialize the DMA subsystem + * + * Returned Value: + * None + * + ****************************************************************************/ + +void weak_function up_dmainitialize(void) +{ + struct stm32_dma_s *dmast; + int stream; + + /* Initialize each DMA stream */ + + for (stream = 0; stream < DMA_NSTREAMS; stream++) + { + dmast = &g_dma[stream]; + sem_init(&dmast->sem, 0, 1); + + /* Attach DMA interrupt vectors */ + + (void)irq_attach(dmast->irq, stm32_dmainterrupt); + + /* Disable the DMA stream */ + + stm32_dmastreamdisable(dmast); + + /* Enable the IRQ at the NVIC (still disabled at the DMA controller) */ + + up_enable_irq(dmast->irq); + +#ifdef CONFIG_ARCH_IRQPRIO + /* Set the interrupt priority */ + + up_prioritize_irq(dmast->irq, CONFIG_DMA_PRI); +#endif + } +} + +/**************************************************************************** + * Name: stm32_dmachannel + * + * Description: + * Allocate a DMA channel. This function gives the caller mutually + * exclusive access to the DMA channel specified by the 'dmamap' argument. + * DMA channels are shared on the STM32: Devices sharing the same DMA + * channel cannot do DMA concurrently! See the DMACHAN_* definitions in + * stm32_dma.h. + * + * If the DMA channel is not available, then stm32_dmachannel() will wait + * until the holder of the channel relinquishes the channel by calling + * stm32_dmafree(). WARNING: If you have two devices sharing a DMA + * channel and the code never releases the channel, the stm32_dmachannel + * call for the other will hang forever in this function! Don't let your + * design do that! + * + * Hmm.. I suppose this interface could be extended to make a non-blocking + * version. Feel free to do that if that is what you need. + * + * Input parameter: + * dmamap - Identifies the stream/channel resource. For the STM32 F4, this + * is a bit-encoded value as provided by the DMAMAP_* definitions + * in chip/stm32f40xxx_dma.h + * + * Returned Value: + * Provided that 'dmamap' is valid, this function ALWAYS returns a non-NULL, + * void* DMA channel handle. (If 'dmamap' is invalid, the function will + * assert if debug is enabled or do something ignorant otherwise). + * + * Assumptions: + * - The caller does not hold he DMA channel. + * - The caller can wait for the DMA channel to be freed if it is no + * available. + * + ****************************************************************************/ + +DMA_HANDLE stm32_dmachannel(unsigned int dmamap) +{ + FAR struct stm32_dma_s *dmast; + + /* Get the stream index from the bit-encoded channel value */ + + dmast = stm32_dmamap(dmamap); + DEBUGASSERT(dmast != NULL); + + /* Get exclusive access to the DMA channel -- OR wait until the channel + * is available if it is currently being used by another driver + */ + + stm32_dmatake(dmast); + + /* The caller now has exclusive use of the DMA channel. Assign the + * channel to the stream and return an opaque reference to the stream + * structure. + */ + + dmast->channel = STM32_DMA_CHANNEL(dmamap); + return (DMA_HANDLE)dmast; +} + +/**************************************************************************** + * Name: stm32_dmafree + * + * Description: + * Release a DMA channel. If another thread is waiting for this DMA channel + * in a call to stm32_dmachannel, then this function will re-assign the + * DMA channel to that thread and wake it up. NOTE: The 'handle' used + * in this argument must NEVER be used again until stm32_dmachannel() is + * called again to re-gain access to the channel. + * + * Returned Value: + * None + * + * Assumptions: + * - The caller holds the DMA channel. + * - There is no DMA in progress + * + ****************************************************************************/ + +void stm32_dmafree(DMA_HANDLE handle) +{ + struct stm32_dma_s *dmast = (struct stm32_dma_s *)handle; + + DEBUGASSERT(handle != NULL); + + /* Release the channel */ + + stm32_dmagive(dmast); +} + +/**************************************************************************** + * Name: stm32_dmasetup + * + * Description: + * Configure DMA before using + * + ****************************************************************************/ + +void stm32_dmasetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr, + size_t ntransfers, uint32_t scr) +{ + struct stm32_dma_s *dmast = (struct stm32_dma_s *)handle; + uint32_t regoffset; + uint32_t regval; + + dmadbg("paddr: %08x maddr: %08x ntransfers: %d scr: %08x\n", + paddr, maddr, ntransfers, scr); + +#ifdef CONFIG_STM32_DMACAPABLE + DEBUGASSERT(stm32_dmacapable(maddr, ntransfers, scr)); +#endif + + /* "If the stream is enabled, disable it by resetting the EN bit in the + * DMA_SxCR register, then read this bit in order to confirm that there is no + * ongoing stream operation. Writing this bit to 0 is not immediately + * effective since it is actually written to 0 once all the current transfers + * have finished. When the EN bit is read as 0, this means that the stream is + * ready to be configured. It is therefore necessary to wait for the EN bit + * to be cleared before starting any stream configuration. ..." + */ + + while ((dmast_getreg(dmast, STM32_DMA_SCR_OFFSET) & DMA_SCR_EN) != 0); + + /* "... All the stream dedicated bits set in the status register (DMA_LISR + * and DMA_HISR) from the previous data block DMA transfer should be cleared + * before the stream can be re-enabled." + * + * Clear pending stream interrupts by setting bits in the upper or lower IFCR + * register + */ + + if (dmast->stream < 4) + { + regoffset = STM32_DMA_LIFCR_OFFSET; + } + else + { + regoffset = STM32_DMA_HIFCR_OFFSET; + } + + dmabase_putreg(dmast, regoffset, (DMA_STREAM_MASK << dmast->shift)); + + /* "Set the peripheral register address in the DMA_SPARx register. The data + * will be moved from/to this address to/from the memory after the + * peripheral event. + */ + + dmast_putreg(dmast, STM32_DMA_SPAR_OFFSET, paddr); + + /* "Set the memory address in the DMA_SM0ARx ... register. The data will be + * written to or read from this memory after the peripheral event." + * + * Note that in double-buffered mode it is explicitly assumed that the second + * buffer immediately follows the first. + */ + + dmast_putreg(dmast, STM32_DMA_SM0AR_OFFSET, maddr); + if (scr & DMA_SCR_DBM) + { + dmast_putreg(dmast, STM32_DMA_SM1AR_OFFSET, maddr + ntransfers); + } + + /* "Configure the total number of data items to be transferred in the + * DMA_SNDTRx register. After each peripheral event, this value will be + * decremented." + * + * "When the peripheral flow controller is used for a given stream, the value + * written into the DMA_SxNDTR has no effect on the DMA transfer. Actually, + * whatever the value written, it will be forced by hardware to 0xFFFF as soon + * as the stream is enabled..." + */ + + dmast_putreg(dmast, STM32_DMA_SNDTR_OFFSET, ntransfers); + + /* "Select the DMA channel (request) using CHSEL[2:0] in the DMA_SxCR register." + * + * "Configure the stream priority using the PL[1:0] bits in the DMA_SCRx" + * register." + */ + + regval = dmast_getreg(dmast, STM32_DMA_SCR_OFFSET); + regval &= ~(DMA_SCR_PL_MASK|DMA_SCR_CHSEL_MASK); + regval |= scr & DMA_SCR_PL_MASK; + regval |= (uint32_t)dmast->channel << DMA_SCR_CHSEL_SHIFT; + dmast_putreg(dmast, STM32_DMA_SCR_OFFSET, regval); + + /* "Configure the FIFO usage (enable or disable, threshold in transmission and + * reception)" + * + * "Caution is required when choosing the FIFO threshold (bits FTH[1:0] of the + * DMA_SxFCR register) and the size of the memory burst (MBURST[1:0] of the + * DMA_SxCR register): The content pointed by the FIFO threshold must exactly + * match to an integer number of memory burst transfers. If this is not in the + * case, a FIFO error (flag FEIFx of the DMA_HISR or DMA_LISR register) will be + * generated when the stream is enabled, then the stream will be automatically + * disabled." + * + * The FIFO is disabled in circular mode when transferring data from a + * peripheral to memory, as in this case it is usually desirable to know that + * every byte from the peripheral is transferred immediately to memory. It is + * not practical to flush the DMA FIFO, as this requires disabling the channel + * which triggers the transfer-complete interrupt. + * + * NOTE: The FEIFx error interrupt is not enabled because the FEIFx seems to + * be reported spuriously causing good transfers to be marked as failures. + */ + + regval = dmast_getreg(dmast, STM32_DMA_SFCR_OFFSET); + regval &= ~(DMA_SFCR_FTH_MASK | DMA_SFCR_FS_MASK | DMA_SFCR_FEIE); + if (!((scr & (DMA_SCR_CIRC | DMA_SCR_DIR_MASK)) == (DMA_SCR_CIRC | DMA_SCR_DIR_P2M))) + { + regval |= (DMA_SFCR_FTH_FULL | DMA_SFCR_DMDIS); + } + + dmast_putreg(dmast, STM32_DMA_SFCR_OFFSET, regval); + + /* "Configure data transfer direction, circular mode, peripheral & memory + * incremented mode, peripheral & memory data size, and interrupt after + * half and/or full transfer in the DMA_CCRx register." + * + * Note: The CT bit is always reset. + */ + + regval = dmast_getreg(dmast, STM32_DMA_SCR_OFFSET); + regval &= ~(DMA_SCR_PFCTRL | DMA_SCR_DIR_MASK | DMA_SCR_PINC | DMA_SCR_MINC | + DMA_SCR_PSIZE_MASK | DMA_SCR_MSIZE_MASK | DMA_SCR_PINCOS | + DMA_SCR_CIRC | DMA_SCR_DBM | DMA_SCR_CT | + DMA_SCR_PBURST_MASK | DMA_SCR_MBURST_MASK); + scr &= (DMA_SCR_PFCTRL | DMA_SCR_DIR_MASK | DMA_SCR_PINC | DMA_SCR_MINC | + DMA_SCR_PSIZE_MASK | DMA_SCR_MSIZE_MASK | DMA_SCR_PINCOS | + DMA_SCR_DBM | DMA_SCR_CIRC | + DMA_SCR_PBURST_MASK | DMA_SCR_MBURST_MASK); + regval |= scr; + dmast_putreg(dmast, STM32_DMA_SCR_OFFSET, regval); +} + +/**************************************************************************** + * Name: stm32_dmastart + * + * Description: + * Start the DMA transfer + * + * Assumptions: + * - DMA handle allocated by stm32_dmachannel() + * - No DMA in progress + * + ****************************************************************************/ + +void stm32_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg, bool half) +{ + struct stm32_dma_s *dmast = (struct stm32_dma_s *)handle; + uint32_t scr; + + DEBUGASSERT(handle != NULL); + + /* Save the callback info. This will be invoked when the DMA completes */ + + dmast->callback = callback; + dmast->arg = arg; + + /* Activate the stream by setting the ENABLE bit in the DMA_SCRx register. + * As soon as the stream is enabled, it can serve any DMA request from the + * peripheral connected on the stream. + */ + + scr = dmast_getreg(dmast, STM32_DMA_SCR_OFFSET); + scr |= DMA_SCR_EN; + + /* In normal mode, interrupt at either half or full completion. In circular + * and double-buffered modes, always interrupt on buffer wrap, and optionally + * interrupt at the halfway point. + */ + + if ((scr & (DMA_SCR_DBM|DMA_SCR_CIRC)) == 0) + { + /* Once half of the bytes are transferred, the half-transfer flag (HTIF) is + * set and an interrupt is generated if the Half-Transfer Interrupt Enable + * bit (HTIE) is set. At the end of the transfer, the Transfer Complete Flag + * (TCIF) is set and an interrupt is generated if the Transfer Complete + * Interrupt Enable bit (TCIE) is set. + */ + + scr |= (half ? (DMA_SCR_HTIE|DMA_SCR_TEIE) : (DMA_SCR_TCIE|DMA_SCR_TEIE)); + } + else + { + /* In non-stop modes, when the transfer completes it immediately resets + * and starts again. The transfer-complete interrupt is thus always + * enabled, and the half-complete interrupt can be used in circular + * mode to determine when the buffer is half-full, or in double-buffered + * mode to determine when one of the two buffers is full. + */ + + scr |= (half ? DMA_SCR_HTIE : 0) | DMA_SCR_TCIE | DMA_SCR_TEIE; + } + + dmast_putreg(dmast, STM32_DMA_SCR_OFFSET, scr); +} + +/**************************************************************************** + * Name: stm32_dmastop + * + * Description: + * Cancel the DMA. After stm32_dmastop() is called, the DMA channel is + * reset and stm32_dmasetup() must be called before stm32_dmastart() can be + * called again + * + * Assumptions: + * - DMA handle allocated by stm32_dmachannel() + * + ****************************************************************************/ + +void stm32_dmastop(DMA_HANDLE handle) +{ + struct stm32_dma_s *dmast = (struct stm32_dma_s *)handle; + stm32_dmastreamdisable(dmast); +} + +/**************************************************************************** + * Name: stm32_dmaresidual + * + * Description: + * Read the DMA bytes-remaining register. + * + * Assumptions: + * - DMA handle allocated by stm32_dmachannel() + * + ****************************************************************************/ + +size_t stm32_dmaresidual(DMA_HANDLE handle) +{ + struct stm32_dma_s *dmast = (struct stm32_dma_s *)handle; + uint32_t residual; + + /* Fetch the count of bytes remaining to be transferred. + * + * If the FIFO is enabled, this count may be inaccurate. ST don't + * appear to document whether this counts the peripheral or the memory + * side of the channel, and they don't make the memory pointer + * available either. + * + * For reception in circular mode the FIFO is disabled in order that + * this value can be useful. + */ + + residual = dmast_getreg(dmast, STM32_DMA_SNDTR_OFFSET); + + return (size_t)residual; +} + +/**************************************************************************** + * Name: stm32_dmacapable + * + * Description: + * Check if the DMA controller can transfer data to/from given memory + * address. This depends on the internal connections in the ARM bus matrix + * of the processor. Note that this only applies to memory addresses, it + * will return false for any peripheral address. + * + * Returned value: + * True, if transfer is possible. + * + ****************************************************************************/ + +#ifdef CONFIG_STM32_DMACAPABLE +bool stm32_dmacapable(uint32_t maddr, uint32_t count, uint32_t ccr) +{ + uint32_t transfer_size, burst_length; + uint32_t mend; + + dmavdbg("stm32_dmacapable: 0x%08x/%u 0x%08x\n", maddr, count, ccr); + + /* Verify that the address conforms to the memory transfer size. + * Transfers to/from memory performed by the DMA controller are + * required to be aligned to their size. + * + * See ST RM0090 rev4, section 9.3.11 + * + * Compute mend inline to avoid a possible non-constant integer + * multiply. + */ + + switch (ccr & DMA_SCR_MSIZE_MASK) + { + case DMA_SCR_MSIZE_8BITS: + transfer_size = 1; + mend = maddr + count - 1; + break; + + case DMA_SCR_MSIZE_16BITS: + transfer_size = 2; + mend = maddr + (count << 1) - 1; + break; + + case DMA_SCR_MSIZE_32BITS: + transfer_size = 4; + mend = maddr + (count << 2) - 1; + break; + + default: + dmavdbg("stm32_dmacapable: bad transfer size in CCR\n"); + return false; + } + + if ((maddr & (transfer_size - 1)) != 0) + { + dmavdbg("stm32_dmacapable: transfer unaligned\n"); + return false; + } + + /* Verify that burst transfers do not cross a 1KiB boundary. */ + + if ((maddr / 1024) != (mend / 1024)) + { + /* The transfer as a whole crosses a 1KiB boundary. + * Verify that no burst does by asserting that the address + * is aligned to the burst length. + */ + + switch (ccr & DMA_SCR_MBURST_MASK) + { + case DMA_SCR_MBURST_SINGLE: + burst_length = transfer_size; + break; + + case DMA_SCR_MBURST_INCR4: + burst_length = transfer_size << 2; + break; + + case DMA_SCR_MBURST_INCR8: + burst_length = transfer_size << 3; + break; + + case DMA_SCR_MBURST_INCR16: + burst_length = transfer_size << 4; + break; + + default: + dmavdbg("stm32_dmacapable: bad burst size in CCR\n"); + return false; + } + + if ((maddr & (burst_length - 1)) != 0) + { + dmavdbg("stm32_dmacapable: burst crosses 1KiB\n"); + return false; + } + } + + /* Verify that the transfer is to a memory region that supports DMA. */ + + if ((maddr & STM32_REGION_MASK) != (mend & STM32_REGION_MASK)) + { + dmavdbg("stm32_dmacapable: transfer crosses memory region\n"); + return false; + } + + switch (maddr & STM32_REGION_MASK) + { + case STM32_FSMC_BANK1: + case STM32_FSMC_BANK2: + case STM32_FSMC_BANK3: + case STM32_FSMC_BANK4: + case STM32_SRAM_BASE: + /* All RAM is supported */ + + break; + + case STM32_CODE_BASE: + /* Everything except the CCM ram is supported */ + + if (maddr >= STM32_CCMRAM_BASE && + (maddr - STM32_CCMRAM_BASE) < 65536) + { + dmavdbg("stm32_dmacapable: transfer targets CCMRAM\n"); + return false; + } + break; + + default: + /* Everything else is unsupported by DMA */ + + dmavdbg("stm32_dmacapable: transfer targets unknown/unsupported region\n"); + return false; + } + + dmavdbg("stm32_dmacapable: transfer OK\n"); + return true; +} +#endif + +/**************************************************************************** + * Name: stm32_dmasample + * + * Description: + * Sample DMA register contents + * + * Assumptions: + * - DMA handle allocated by stm32_dmachannel() + * + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_DMA +void stm32_dmasample(DMA_HANDLE handle, struct stm32_dmaregs_s *regs) +{ + struct stm32_dma_s *dmast = (struct stm32_dma_s *)handle; + irqstate_t flags; + + flags = irqsave(); + regs->lisr = dmabase_getreg(dmast, STM32_DMA_LISR_OFFSET); + regs->hisr = dmabase_getreg(dmast, STM32_DMA_HISR_OFFSET); + regs->scr = dmast_getreg(dmast, STM32_DMA_SCR_OFFSET); + regs->sndtr = dmast_getreg(dmast, STM32_DMA_SNDTR_OFFSET); + regs->spar = dmast_getreg(dmast, STM32_DMA_SPAR_OFFSET); + regs->sm0ar = dmast_getreg(dmast, STM32_DMA_SM0AR_OFFSET); + regs->sm1ar = dmast_getreg(dmast, STM32_DMA_SM1AR_OFFSET); + regs->sfcr = dmast_getreg(dmast, STM32_DMA_SFCR_OFFSET); + irqrestore(flags); +} +#endif + +/**************************************************************************** + * Name: stm32_dmadump + * + * Description: + * Dump previously sampled DMA register contents + * + * Assumptions: + * - DMA handle allocated by stm32_dmachannel() + * + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_DMA +void stm32_dmadump(DMA_HANDLE handle, const struct stm32_dmaregs_s *regs, + const char *msg) +{ + struct stm32_dma_s *dmast = (struct stm32_dma_s *)handle; + uint32_t dmabase = DMA_BASE(dmast->base); + + dmadbg("DMA Registers: %s\n", msg); + dmadbg(" LISR[%08x]: %08x\n", dmabase + STM32_DMA_LISR_OFFSET, regs->lisr); + dmadbg(" HISR[%08x]: %08x\n", dmabase + STM32_DMA_HISR_OFFSET, regs->hisr); + dmadbg(" SCR[%08x]: %08x\n", dmast->base + STM32_DMA_SCR_OFFSET, regs->scr); + dmadbg(" SNDTR[%08x]: %08x\n", dmast->base + STM32_DMA_SNDTR_OFFSET, regs->sndtr); + dmadbg(" SPAR[%08x]: %08x\n", dmast->base + STM32_DMA_SPAR_OFFSET, regs->spar); + dmadbg(" SM0AR[%08x]: %08x\n", dmast->base + STM32_DMA_SM0AR_OFFSET, regs->sm0ar); + dmadbg(" SM1AR[%08x]: %08x\n", dmast->base + STM32_DMA_SM1AR_OFFSET, regs->sm1ar); + dmadbg(" SFCR[%08x]: %08x\n", dmast->base + STM32_DMA_SFCR_OFFSET, regs->sfcr); +} +#endif + +#endif /* CONFIG_STM32F7_STM32F74XX || CONFIG_STM32F7_STM32F75XX */ diff --git a/arch/arm/src/stm32f7/stm32_dma.h b/arch/arm/src/stm32f7/stm32_dma.h index 31ee77da2a..67c9cbf4c8 100644 --- a/arch/arm/src/stm32f7/stm32_dma.h +++ b/arch/arm/src/stm32f7/stm32_dma.h @@ -70,7 +70,9 @@ typedef FAR void *DMA_HANDLE; /* Description: * This is the type of the callback that is used to inform the user of the the - * completion of the DMA. + * completion of the DMA. NOTE: The DMA module does *NOT* perform any cache + * operations. It is the responsibility of the DMA client to invalidate DMA + * buffers after completion of the DMA RX operations. * * Input Parameters: * handle - Refers tot he DMA channel or stream @@ -190,7 +192,9 @@ void stm32_dmasetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr, * Name: stm32_dmastart * * Description: - * Start the DMA transfer + * Start the DMA transfer. NOTE: The DMA module does *NOT* perform any + * cache operations. It is the responsibility of the DMA client to clean + * DMA buffers after staring of the DMA TX operations. * * Assumptions: * - DMA handle allocated by stm32_dmachannel() diff --git a/arch/arm/src/stm32f7/stm32_ethernet.c b/arch/arm/src/stm32f7/stm32_ethernet.c index ae918adf1e..ba4cc109d6 100644 --- a/arch/arm/src/stm32f7/stm32_ethernet.c +++ b/arch/arm/src/stm32f7/stm32_ethernet.c @@ -77,7 +77,7 @@ /* STM32F7_NETHERNET determines the number of physical interfaces that can * be supported by the hardware. CONFIG_STM32F7_ETHMAC will defined if - * any STM32F7 Ethernet support is enabled in the configuratino. + * any STM32F7 Ethernet support is enabled in the configuration. */ #if STM32F7_NETHERNET > 0 && defined(CONFIG_STM32F7_ETHMAC)