diff --git a/arch/arm/src/stm32l4/Kconfig b/arch/arm/src/stm32l4/Kconfig index 5bda5de53a8..f982641ed15 100644 --- a/arch/arm/src/stm32l4/Kconfig +++ b/arch/arm/src/stm32l4/Kconfig @@ -79,7 +79,7 @@ config STM32L4_HAVE_LTDC default n # These "hidden" settings are the OR of individual peripheral selections -# indicating that the general capabilitiy is required. +# indicating that the general capability is required. config STM32L4_ADC bool @@ -133,7 +133,6 @@ config STM32L4_DMA2 select STM32L4_DMA select ARCH_DMA - config STM32L4_CRC bool "CRC" default n @@ -209,7 +208,119 @@ config STM32L4_QSPI_CSHT ---help--- The STM32L4 QSPI peripheral requires that it be specified the minimum number of AHB cycles that Chip Select be held inactive between transactions. - + +choice + prompt "Transfer technique" + default STM32L4_QSPI_DMA + ---help--- + You can choose between using polling, interrupts, or DMA to transfer data + over the QSPI interface. + +config STM32L4_QSPI_POLLING + bool "Polling" + ---help--- + Use conventional register I/O with status polling to transfer data. + +config STM32L4_QSPI_INTERRUPTS + bool "Interrupts" + ---help--- + User interrupt driven I/O transfers. + +config STM32L4_QSPI_DMA + bool "DMA" + depends on STM32L4_DMA + ---help--- + Use DMA to improve QSPI transfer performance. + +endchoice + +choice + prompt "DMA Channel" + default STM32L4_QSPI_DMA_CHAN_1_5 + depends on STM32L4_DMA + ---help--- + You can choose between two DMA channels for use with QSPI: + either DMA1 channel 5, or DMA2 channel 7. + If you only see one choice here, it is probably because + you have not also enabled the associated DMA controller. + +config STM32L4_QSPI_DMA_CHAN_1_5 + bool "DMA1 Channel 5" + depends on STM32L4_DMA1 + ---help--- + Use DMA1 channel 5 for QSPI. + +config STM32L4_QSPI_DMA_CHAN_2_7 + bool "DMA2 Channel 7" + depends on STM32L4_DMA2 + ---help--- + Use DMA2 channel 7 for QSPI. + +endchoice + +choice + prompt "DMA Priority" + default STM32L4_QSPI_DMAPRIORITY_MEDIUM + depends on STM32L4_DMA + ---help--- + The DMA controller supports priority levels. You are probably fine + with the default of 'medium' except for special cases. In the event + of contention between to channels at the same priority, the lower + numbered channel has hardware priority over the higher numbered one. + +config STM32L4_QSPI_DMAPRIORITY_VERYHIGH + bool "Very High priority" + depends on STM32L4_DMA + ---help--- + 'Highest' priority. + +config STM32L4_QSPI_DMAPRIORITY_HIGH + bool "High priority" + depends on STM32L4_DMA + ---help--- + 'High' priority. + +config STM32L4_QSPI_DMAPRIORITY_MEDIUM + bool "Medium priority" + depends on STM32L4_DMA + ---help--- + 'Medium' priority. + +config STM32L4_QSPI_DMAPRIORITY_LOW + bool "Low priority" + depends on STM32L4_DMA + ---help--- + 'Low' priority. + +endchoice + +config STM32L4_QSPI_DMATHRESHOLD + int "QSPI DMA threshold" + default 4 + depends on STM32L4_QSPI_DMA + ---help--- + When QSPI DMA is enabled, small DMA transfers will still be performed + by polling logic. This value is the threshold below which transfers + will still be performed by conventional register status polling. + +config STM32L4_QSPI_DMADEBUG + bool "QSPI DMA transfer debug" + depends on STM32L4_QSPI_DMA && DEBUG && DEBUG_DMA + default n + ---help--- + Enable special debug instrumentation to analyze QSPI DMA data transfers. + This logic is as non-invasive as possible: It samples DMA + registers at key points in the data transfer and then dumps all of + the registers at the end of the transfer. + +config STM32L4_QSPI_REGDEBUG + bool "QSPI Register level debug" + depends on DEBUG + default n + ---help--- + Output detailed register-level QSPI device debug information. + Requires also DEBUG. + endif comment "APB1 Peripherals" diff --git a/arch/arm/src/stm32l4/README.txt b/arch/arm/src/stm32l4/README.txt index bf38fb80ae1..59f2eb28d9f 100644 --- a/arch/arm/src/stm32l4/README.txt +++ b/arch/arm/src/stm32l4/README.txt @@ -23,13 +23,13 @@ LSE : works, but TODO autotrim of MSI, etc RCC : All registers defined, peripherals enabled, basic clock working SYSCTL : All registers defined USART : Working in normal mode (no DMA, to be tested, code is written) -DMA : Ported from STM32, code written, to be tested +DMA : works; at least tested with QSPI SRAM2 : Should work with enough MM regions FIREWALL : Code written, to be tested, requires support from ldscript SPI : Code written, to be tested, including DMA I2C : Registers defined RTC : works -QSPI : TODO (port from stm32f7) +QSPI : works in polling, interrupt, DMA, and also memory-mapped modes CAN : TODO OTGFS : TODO Timers : TODO diff --git a/arch/arm/src/stm32l4/stm32l4_qspi.c b/arch/arm/src/stm32l4/stm32l4_qspi.c index d9c9ca58258..57aa85dac05 100644 --- a/arch/arm/src/stm32l4/stm32l4_qspi.c +++ b/arch/arm/src/stm32l4/stm32l4_qspi.c @@ -120,18 +120,12 @@ #define DMA_END_TRANSFER 4 #define DMA_NSAMPLES 5 -#ifdef CONFIG_STM32L4_QSPI_DMA -# error QSPI DMA support not yet implemented +/* Can't have both interrupt-driven QSPI and DMA QSPI */ + +#if defined(STM32L4_QSPI_INTERRUPTS) && defined(CONFIG_STM32L4_QSPI_DMA) +# error "Cannot enable both interrupt mode and DMA mode for QSPI" #endif -/* QSPI dma is not yet implemented */ - -#undef CONFIG_STM32L4_QSPI_DMA - -/* QSPI Interrupt mode is implemented */ - -#define QSPI_USE_INTERRUPTS - /* Sanity check that board.h defines requisite QSPI pinmap options for */ #if (!defined(GPIO_QSPI_CS) || !defined(GPIO_QSPI_IO0) || !defined(GPIO_QSPI_IO1) || \ @@ -141,9 +135,27 @@ #endif #ifdef CONFIG_STM32L4_QSPI_DMA -# if !defined(DMACHAN_QUADSPI) + +# if defined(CONFIG_STM32L4_QSPI_DMA_CHAN_1_5) +# define DMACHAN_QUADSPI DMACHAN_QUADSPI_1 +# elif defined(CONFIG_STM32L4_QSPI_DMA_CHAN_2_7) +# define DMACHAN_QUADSPI DMACHAN_QUADSPI_2 +# else # error QSPI DMA channel must be specified via DMACHAN_QUADSPI in your board.h # endif + +# if defined(CONFIG_STM32L4_QSPI_DMAPRIORITY_LOW) +# define QSPI_DMA_PRIO DMA_CCR_PRILO +# elif defined(CONFIG_STM32L4_QSPI_DMAPRIORITY_MEDIUM) +# define QSPI_DMA_PRIO DMA_CCR_PRIMED +# elif defined(CONFIG_STM32L4_QSPI_DMAPRIORITY_HIGH) +# define QSPI_DMA_PRIO DMA_CCR_PRIHI +# elif defined(CONFIG_STM32L4_QSPI_DMAPRIORITY_VERYHIGH) +# define QSPI_DMA_PRIO DMA_CCR_PRIVERYHI +# else +# define QSPI_DMA_PRIO DMA_CCR_PRIMED +# endif + #endif #ifndef BOARD_AHB_FREQUENCY @@ -154,6 +166,13 @@ # error you must specify a positive flash size via CONFIG_STM32L4_QSPI_FLASH_SIZE #endif +/* DMA timeout. The value is not critical; we just don't want the system to + * hang in the event that a DMA does not finish. + */ + +#define DMA_TIMEOUT_MS (800) +#define DMA_TIMEOUT_TICKS MSEC2TICK(DMA_TIMEOUT_MS) + /* Clocking *****************************************************************/ /* The QSPI bit rate clock is generated by dividing the peripheral clock by * a value between 1 and 255 @@ -184,7 +203,7 @@ struct stm32l4_qspidev_s sem_t exclsem; /* Assures mutually exclusive access to QSPI */ bool memmap; /* TRUE: Controller is in memory mapped mode */ -#ifdef QSPI_USE_INTERRUPTS +#ifdef STM32L4_QSPI_INTERRUPTS xcpt_t handler; /* Interrupt handler */ uint8_t irq; /* Interrupt number */ sem_t op_sem; /* Block until complete */ @@ -192,7 +211,11 @@ struct stm32l4_qspidev_s #endif #ifdef CONFIG_STM32L4_QSPI_DMA - /* XXX III needs implementation */ + bool candma; /* DMA is supported */ + sem_t dmawait; /* Used to wait for DMA completion */ + int result; /* DMA result */ + DMA_HANDLE dmach; /* QSPI DMA handle */ + WDOG_ID dmadog; /* Watchdog that handles DMA timeouts */ #endif /* Debug stuff */ @@ -238,7 +261,7 @@ struct qspi_xctnspec_s uint8_t isddr; /* true if 'double data rate' */ uint8_t issioo; /* true if 'send instruction only once' mode */ -#ifdef QSPI_USE_INTERRUPTS +#ifdef STM32L4_QSPI_INTERRUPTS uint8_t function; /* functional mode; to distinguish a read or write */ int8_t disposition; /* how it all turned out */ uint32_t idxnow; /* index into databuffer of current byte in transfer */ @@ -277,11 +300,37 @@ static void qspi_dumpgpioconfig(const char *msg); /* Interrupts */ -#ifdef QSPI_USE_INTERRUPTS +#ifdef STM32L4_QSPI_INTERRUPTS static int qspi0_interrupt(int irq, void *context); #endif +/* DMA support */ + +#ifdef CONFIG_STM32L4_QSPI_DMA + +# if defined(CONFIG_QSPI_DMAPRIO) +# define QSPI_DMA_PRIO CONFIG_QSPI_DMAPRIO +# else +# define QSPI_DMA_PRIO DMA_CCR_PRIMED +# endif + +# ifdef CONFIG_STM32L4_QSPI_DMADEBUG +# define qspi_dma_sample(s,i) stm32l4_dmasample((s)->dmach, &(s)->dmaregs[i]) +static void qspi_dma_sampleinit(struct stm32l4_qspidev_s *priv); +static void qspi_dma_sampledone(struct stm32l4_qspidev_s *priv); +# else +# define qspi_dma_sample(s,i) +# define qspi_dma_sampleinit(s) +# define qspi_dma_sampledone(s) +# endif + +# ifndef CONFIG_STM32L4_QSPI_DMATHRESHOLD +# define CONFIG_STM32L4_QSPI_DMATHRESHOLD 4 +# endif + +#endif + /* QSPI methods */ static int qspi_lock(struct qspi_dev_s *dev, bool lock); @@ -326,13 +375,13 @@ static struct stm32l4_qspidev_s g_qspi0dev = .ops = &g_qspi0ops, }, .base = STM32L4_QSPI_BASE, -#ifdef QSPI_USE_INTERRUPTS +#ifdef STM32L4_QSPI_INTERRUPTS .handler = qspi0_interrupt, .irq = STM32L4_IRQ_QUADSPI, #endif .intf = 0, #ifdef CONFIG_STM32L4_QSPI_DMA - /* XXX III needs implementation */ + .candma = true, #endif }; @@ -572,6 +621,93 @@ static void qspi_dumpgpioconfig(const char *msg) } #endif +#ifdef CONFIG_STM32L4_QSPI_DMADEBUG +/**************************************************************************** + * Name: qspi_dma_sampleinit + * + * Description: + * Initialize sampling of DMA registers + * + * Input Parameters: + * priv - QSPI driver instance + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void qspi_dma_sampleinit(struct stm32l4_qspidev_s *priv) +{ + /* Put contents of register samples into a known state */ + + memset(priv->dmaregs, 0xff, DMA_NSAMPLES * sizeof(struct stm32l4_dmaregs_s)); + + /* Then get the initial samples */ + + stm32l4_dmasample(priv->dmach, &priv->dmaregs[DMA_INITIAL]); +} + +/**************************************************************************** + * Name: qspi_dma_sampledone + * + * Description: + * Dump sampled DMA registers + * + * Input Parameters: + * priv - QSPI driver instance + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void qspi_dma_sampledone(struct stm32l4_qspidev_s *priv) +{ + /* Sample the final registers */ + + stm32l4_dmasample(priv->dmach, &priv->dmaregs[DMA_END_TRANSFER]); + + /* Then dump the sampled DMA registers */ + /* Initial register values */ + + stm32l4_dmadump(priv->dmach, &priv->dmaregs[DMA_INITIAL], + "Initial Registers"); + + /* Register values after DMA setup */ + + stm32l4_dmadump(priv->dmach, &priv->dmaregs[DMA_AFTER_SETUP], + "After DMA Setup"); + + /* Register values after DMA start */ + + stm32l4_dmadump(priv->dmach, &priv->dmaregs[DMA_AFTER_START], + "After DMA Start"); + + /* Register values at the time of the TX and RX DMA callbacks + * -OR- DMA timeout. + * + * If the DMA timed out, then there will not be any RX DMA + * callback samples. There is probably no TX DMA callback + * samples either, but we don't know for sure. + */ + + if (priv->result == -ETIMEDOUT) + { + stm32l4_dmadump(priv->dmach, &priv->dmaregs[DMA_TIMEOUT], + "At DMA timeout"); + } + else + { + stm32l4_dmadump(priv->dmach, &priv->dmaregs[DMA_CALLBACK], + "At DMA callback"); + } + + stm32l4_dmadump(priv->dmach, &priv->dmaregs[DMA_END_TRANSFER], + "At End-of-Transfer"); +} +#endif + + /**************************************************************************** * Name: qspi_setupxctnfromcmd * @@ -687,7 +823,7 @@ static int qspi_setupxctnfromcmd(struct qspi_xctnspec_s *xctn, xctn->isddr = 0; } -#if defined(QSPI_USE_INTERRUPTS) +#if defined(STM32L4_QSPI_INTERRUPTS) xctn->function = QSPICMD_ISWRITE(cmdinfo->flags) ? CCR_FMODE_INDWR : CCR_FMODE_INDRD; xctn->disposition = - EIO; xctn->idxnow = 0; @@ -816,7 +952,7 @@ static int qspi_setupxctnfrommem(struct qspi_xctnspec_s *xctn, xctn->isddr = 0; -#if defined(QSPI_USE_INTERRUPTS) +#if defined(STM32L4_QSPI_INTERRUPTS) xctn->function = QSPIMEM_ISWRITE(meminfo->flags) ? CCR_FMODE_INDWR : CCR_FMODE_INDRD; xctn->disposition = - EIO; xctn->idxnow = 0; @@ -938,7 +1074,7 @@ static void qspi_ccrconfig(struct stm32l4_qspidev_s *priv, } } -#if defined(QSPI_USE_INTERRUPTS) +#if defined(STM32L4_QSPI_INTERRUPTS) /**************************************************************************** * Name: qspi0_interrupt * @@ -1146,9 +1282,267 @@ static int qspi0_interrupt(int irq, void *context) } #elif defined(CONFIG_STM32L4_QSPI_DMA) - /* XXX III dma mode */ +/**************************************************************************** + * Name: qspi_dma_timeout + * + * Description: + * The watchdog timeout setup when a has expired without completion of a + * DMA. + * + * Input Parameters: + * argc - The number of arguments (should be 1) + * arg - The argument (state structure reference cast to uint32_t) + * + * Returned Value: + * None + * + * Assumptions: + * Always called from the interrupt level with interrupts disabled. + * + ****************************************************************************/ -#else +static void qspi_dma_timeout(int argc, uint32_t arg) +{ + struct stm32l4_qspidev_s *priv = (struct stm32l4_qspidev_s *)arg; + DEBUGASSERT(priv != NULL); + + /* Sample DMA registers at the time of the timeout */ + + qspi_dma_sample(priv, DMA_CALLBACK); + + /* Report timeout result, perhaps overwriting any failure reports from + * the TX callback. + */ + + priv->result = -ETIMEDOUT; + + /* Then wake up the waiting thread */ + + sem_post(&priv->dmawait); +} + +/**************************************************************************** + * Name: qspi_dma_callback + * + * Description: + * This callback function is invoked at the completion of the QSPI DMA. + * + * Input Parameters: + * handle - The DMA handler + * isr - source of the DMA interrupt + * arg - A pointer to the chip select structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void qspi_dma_callback(DMA_HANDLE handle, uint8_t isr, void *arg) +{ + struct stm32l4_qspidev_s *priv = (struct stm32l4_qspidev_s *)arg; + DEBUGASSERT(priv != NULL); + + /* Cancel the watchdog timeout */ + + (void)wd_cancel(priv->dmadog); + + /* Sample DMA registers at the time of the callback */ + + qspi_dma_sample(priv, DMA_CALLBACK); + + /* Report the result of the transfer only if the callback has not already + * reported an error. + */ + + if (priv->result == -EBUSY) + { + /* Save the result of the transfer if no error was previously reported */ + + if ( isr & DMA_CHAN_TCIF_BIT ) + { + priv->result = OK; + } + else if ( isr & DMA_CHAN_TEIF_BIT ) + { + priv->result = -EIO; + } + else + { + priv->result = OK; + } + } + + /* Then wake up the waiting thread */ + + sem_post(&priv->dmawait); +} + +/**************************************************************************** + * Name: qspi_regaddr + * + * Description: + * Return the address of an QSPI register + * + ****************************************************************************/ + +static inline uintptr_t qspi_regaddr(struct stm32l4_qspidev_s *priv, + unsigned int offset) +{ + return priv->base + offset; +} + +/**************************************************************************** + * Name: qspi_memory_dma + * + * Description: + * Perform one QSPI memory transfer using DMA + * + * Input Parameters: + * priv - Device-specific state data + * meminfo - Describes the memory transfer to be performed. + * xctn - Describes the transaction context. + * + * Returned Value: + * Zero (OK) on SUCCESS, a negated errno on value of failure + * + ****************************************************************************/ + +static int qspi_memory_dma(struct stm32l4_qspidev_s *priv, + struct qspi_meminfo_s *meminfo, + struct qspi_xctnspec_s *xctn) +{ + uint32_t dmaflags; + uint32_t regval; + int ret; + + /* Initialize register sampling */ + + qspi_dma_sampleinit(priv); + + /* Determine DMA flags and setup the DMA */ + + if (QSPIMEM_ISWRITE(meminfo->flags)) + { + /* Setup the DMA (memory-to-peripheral) */ + + dmaflags = (QSPI_DMA_PRIO | DMA_CCR_MSIZE_8BITS | DMA_CCR_PSIZE_8BITS | DMA_CCR_MINC | DMA_CCR_DIR); + } + else + { + /* Setup the DMA (peripheral-to-memory) */ + + dmaflags = (QSPI_DMA_PRIO | DMA_CCR_MSIZE_8BITS | DMA_CCR_PSIZE_8BITS | DMA_CCR_MINC ); + } + + stm32l4_dmasetup(priv->dmach, qspi_regaddr(priv, STM32L4_QUADSPI_DR_OFFSET), + (uint32_t)meminfo->buffer, meminfo->buflen, dmaflags); + + qspi_dma_sample(priv, DMA_AFTER_SETUP); + + /* Enable the memory transfer */ + + regval = qspi_getreg(priv, STM32L4_QUADSPI_CR_OFFSET); + regval |= QSPI_CR_DMAEN; + qspi_putreg(priv, regval, STM32L4_QUADSPI_CR_OFFSET); + + /* Set up the Communications Configuration Register as per command info */ + + qspi_ccrconfig(priv, xctn, + QSPIMEM_ISWRITE(meminfo->flags) ? CCR_FMODE_INDWR : CCR_FMODE_INDRD); + + /* Start the DMA */ + + priv->result = -EBUSY; + stm32l4_dmastart(priv->dmach, qspi_dma_callback, priv, false); + + qspi_dma_sample(priv, DMA_AFTER_START); + + /* Wait for DMA completion. This is done in a loop because there may be + * false alarm semaphore counts that cause sem_wait() not fail to wait + * or to wake-up prematurely (for example due to the receipt of a signal). + * We know that the DMA has completed when the result is anything other + * that -EBUSY. + */ + + do + { + /* Start (or re-start) the watchdog timeout */ + + ret = wd_start(priv->dmadog, DMA_TIMEOUT_TICKS, + (wdentry_t)qspi_dma_timeout, 1, (uint32_t)priv); + if (ret != OK) + { + qspidbg("ERROR: wd_start failed: %d\n", ret); + } + + /* Wait for the DMA complete */ + + ret = sem_wait(&priv->dmawait); + + /* Cancel the watchdog timeout */ + + (void)wd_cancel(priv->dmadog); + + /* Check if we were awakened by an error of some kind */ + + if (ret < 0) + { + /* EINTR is not a failure. That simply means that the wait + * was awakened by a signal. + */ + + int errorcode = errno; + if (errorcode != EINTR) + { + DEBUGPANIC(); + regval = qspi_getreg(priv, STM32L4_QUADSPI_CR_OFFSET); + regval &= ~QSPI_CR_DMAEN; + qspi_putreg(priv, regval, STM32L4_QUADSPI_CR_OFFSET); + return -errorcode; + } + } + + /* Note that we might be awakened before the wait is over due to + * residual counts on the semaphore. So, to handle, that case, + * we loop until something changes the DMA result to any value other + * than -EBUSY. + */ + } + while (priv->result == -EBUSY); + + /* Wait for Transfer complete, and not busy */ + + qspi_waitstatusflags(priv, QSPI_SR_TCF,1); + qspi_waitstatusflags(priv, QSPI_SR_BUSY,0); + MEMORY_SYNC(); + + /* Dump the sampled DMA registers */ + + qspi_dma_sampledone(priv); + + /* Make sure that the DMA is stopped (it will be stopped automatically + * on normal transfers, but not necessarily when the transfer terminates + * on an error condition). + */ + + stm32l4_dmastop(priv->dmach); + + regval = qspi_getreg(priv, STM32L4_QUADSPI_CR_OFFSET); + regval &= ~QSPI_CR_DMAEN; + qspi_putreg(priv, regval, STM32L4_QUADSPI_CR_OFFSET); + + /* Complain if the DMA fails */ + + if (priv->result) + { + qspidbg("ERROR: DMA failed with result: %d\n", priv->result); + } + + return priv->result; +} +#endif + +#if !defined(STM32L4_QSPI_INTERRUPTS) /**************************************************************************** * Name: qspi_receive_blocking * @@ -1581,7 +1975,7 @@ static int qspi_command(struct qspi_dev_s *dev, QSPI_FCR_CTEF | QSPI_FCR_CTCF | QSPI_FCR_CSMF | QSPI_FCR_CTOF, STM32L4_QUADSPI_FCR); -#ifdef QSPI_USE_INTERRUPTS +#ifdef STM32L4_QSPI_INTERRUPTS /* interrupt mode will need access to the transaction context */ priv->xctn = &xctn; @@ -1665,9 +2059,9 @@ static int qspi_command(struct qspi_dev_s *dev, ret = xctn.disposition; -#elif defined(CONFIG_STM32L4_QSPI_DMA) - /* XXX III dma mode (and 'autopolling'?) */ - + /* because command transfers are so small, we're not going to use + * DMA for them, only interrupts or polling + */ #else /* Polling mode */ @@ -1762,7 +2156,7 @@ static int qspi_memory(struct qspi_dev_s *dev, QSPI_FCR_CTEF | QSPI_FCR_CTCF | QSPI_FCR_CSMF | QSPI_FCR_CTOF, STM32L4_QUADSPI_FCR); -#ifdef QSPI_USE_INTERRUPTS +#ifdef STM32L4_QSPI_INTERRUPTS /* interrupt mode will need access to the transaction context */ priv->xctn = &xctn; @@ -1824,8 +2218,46 @@ static int qspi_memory(struct qspi_dev_s *dev, ret = xctn.disposition; #elif defined(CONFIG_STM32L4_QSPI_DMA) - /* XXX III dma mode (and 'autopolling'?) */ - + /* Can we perform DMA? Should we perform DMA? */ + + if (priv->candma && + meminfo->buflen > CONFIG_STM32L4_QSPI_DMATHRESHOLD && + IS_ALIGNED((uintptr_t)meminfo->buffer) && + IS_ALIGNED(meminfo->buflen)) + { + ret = qspi_memory_dma(priv, meminfo, &xctn); + } + else + { + /* polling mode */ + + /* Set up the Communications Configuration Register as per command info */ + + qspi_ccrconfig(priv, &xctn, + QSPIMEM_ISWRITE(meminfo->flags) ? CCR_FMODE_INDWR : CCR_FMODE_INDRD); + + /* Transfer data */ + + DEBUGASSERT(meminfo->buffer != NULL && meminfo->buflen > 0); + DEBUGASSERT(IS_ALIGNED(meminfo->buffer)); + + if (QSPIMEM_ISWRITE(meminfo->flags)) + { + ret = qspi_transmit_blocking(priv, &xctn); + } + else + { + ret = qspi_receive_blocking(priv, &xctn); + } + + /* Wait for Transfer complete, and not busy */ + + qspi_waitstatusflags(priv, QSPI_SR_TCF,1); + qspi_waitstatusflags(priv, QSPI_SR_BUSY,0); + + MEMORY_SYNC(); + } + #else /* polling mode */ @@ -1848,31 +2280,13 @@ static int qspi_memory(struct qspi_dev_s *dev, ret = qspi_receive_blocking(priv, &xctn); } - MEMORY_SYNC(); - -#if 0 -#ifdef CONFIG_STM32L4_QSPI_DMA - /* Can we perform DMA? Should we perform DMA? */ - - if (priv->candma && - meminfo->buflen > CONFIG_STM32L4_QSPI_DMATHRESHOLD && - IS_ALIGNED((uintptr_t)meminfo->buffer) && - IS_ALIGNED(meminfo->buflen)) - { - return qspi_memory_dma(priv, meminfo); - } - else -#endif - { - return qspi_memory_nodma(priv, meminfo); - } -#endif - /* Wait for Transfer complete, and not busy */ qspi_waitstatusflags(priv, QSPI_SR_TCF,1); qspi_waitstatusflags(priv, QSPI_SR_BUSY,0); + MEMORY_SYNC(); + #endif return ret; @@ -1949,7 +2363,7 @@ static int qspi_hw_initialize(struct stm32l4_qspidev_s *priv) qspi_abort(priv); - regval = qspi_getreg(priv, STM32L4_QUADSPI_CR_OFFSET); + regval = 0; regval &= ~(QSPI_CR_EN); qspi_putreg(priv, regval, STM32L4_QUADSPI_CR_OFFSET); @@ -2099,10 +2513,35 @@ struct qspi_dev_s *stm32l4_qspi_initialize(int intf) sem_init(&priv->exclsem, 0, 1); #ifdef CONFIG_STM32L4_QSPI_DMA - /* XXX III needs implementation */ + /* Pre-allocate DMA channels. */ + + if (priv->candma) + { + priv->dmach = stm32l4_dmachannel(DMACHAN_QUADSPI); + if (!priv->dmach) + { + qspidbg("ERROR: Failed to allocate the DMA channel\n"); + priv->candma = false; + } + } + + /* Initialize the QSPI semaphore that is used to wake up the waiting + * thread when the DMA transfer completes. + */ + + sem_init(&priv->dmawait, 0, 0); + + /* Create a watchdog time to catch DMA timeouts */ + + priv->dmadog = wd_create(); + if (priv->dmadog == NULL) + { + qspidbg("ERROR: Failed to create wdog\n"); + goto errout_with_dmahandles; + } #endif -#ifdef QSPI_USE_INTERRUPTS +#ifdef STM32L4_QSPI_INTERRUPTS /* Attach the interrupt handler */ ret = irq_attach(priv->irq, priv->handler); @@ -2133,7 +2572,7 @@ struct qspi_dev_s *stm32l4_qspi_initialize(int intf) priv->initialized = true; priv->memmap = false; -#ifdef QSPI_USE_INTERRUPTS +#ifdef STM32L4_QSPI_INTERRUPTS up_enable_irq(priv->irq); #endif } @@ -2141,13 +2580,22 @@ struct qspi_dev_s *stm32l4_qspi_initialize(int intf) return &priv->qspi; errout_with_irq: -#ifdef QSPI_USE_INTERRUPTS +#ifdef STM32L4_QSPI_INTERRUPTS irq_detach(priv->irq); errout_with_dmadog: #endif #ifdef CONFIG_STM32L4_QSPI_DMA - /* XXX III needs implementation */ + wd_delete(priv->dmadog); + +errout_with_dmahandles: + sem_destroy(&priv->dmawait); + + if (priv->dmach) + { + stm32l4_dmafree(priv->dmach); + priv->dmach = NULL; + } #endif sem_destroy(&priv->exclsem); @@ -2209,7 +2657,7 @@ void stm32l4_qspi_enter_memorymapped(struct qspi_dev_s* dev, qspi_putreg(&g_qspi0dev, QSPI_FCR_CTOF, STM32L4_QUADSPI_FCR); -#ifdef QSPI_USE_INTERRUPTS +#ifdef STM32L4_QSPI_INTERRUPTS /* Enable Timeout interrupt */ regval = qspi_getreg(priv, STM32L4_QUADSPI_CR_OFFSET); @@ -2227,7 +2675,10 @@ void stm32l4_qspi_enter_memorymapped(struct qspi_dev_s* dev, /* create a transaction object */ qspi_setupxctnfrommem(&xctn, meminfo); + +#ifdef STM32L4_QSPI_INTERRUPTS priv->xctn = NULL; +#endif /* set it into the ccr */