add DMA support to QSPI; tested. Updated Kconfig to more cleanly present the options and defaults.
This commit is contained in:
parent
0f8dc3e7b4
commit
8d4dccb3b9
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user