diff --git a/arch/arm/src/stm32l4/stm32l4_qspi.c b/arch/arm/src/stm32l4/stm32l4_qspi.c index 68e3d0ccb3..fcc58c0d55 100644 --- a/arch/arm/src/stm32l4/stm32l4_qspi.c +++ b/arch/arm/src/stm32l4/stm32l4_qspi.c @@ -140,6 +140,12 @@ GPIO_QSPI_IO1 GPIO_QSPI_IO2 GPIO_QSPI_IO3 GPIO_QSPI_SCK in your board.h #endif +#ifdef CONFIG_STM32L4_QSPI_DMA +#if !defined(DMACHAN_QUADSPI) +# error QSPI DMA channel must be specified via DMACHAN_QUADSPI in your board.h +#endif +#endif + #ifndef BOARD_AHB_FREQUENCY # error your board.h needs to define the value of BOARD_AHB_FREQUENCY #endif @@ -176,6 +182,7 @@ struct stm32l4_qspidev_s uint8_t intf; /* QSPI controller number (0) */ bool initialized; /* TRUE: Controller has been initialized */ sem_t exclsem; /* Assures mutually exclusive access to QSPI */ + bool memmap; /* TRUE: Controller is in memory mapped mode */ #ifdef QSPI_USE_INTERRUPTS xcpt_t handler; /* Interrupt handler */ @@ -1101,7 +1108,11 @@ static int qspi0_interrupt(int irq, void *context) regval &= ~(QSPI_CR_TEIE | QSPI_CR_TCIE | QSPI_CR_FTIE | QSPI_CR_SMIE | QSPI_CR_TOIE); qspi_putreg(&g_qspi0dev, regval, STM32L4_QUADSPI_CR_OFFSET); - /* Set error status */ + /* Set error status; 'transfer error' means that, in 'indirect mode', + * an invalid address is attempted to be accessed. 'Invalid' is + * presumably relative to the FSIZE field in CCR; the manual is not + * explicit, but what else could it be? + */ g_qspi0dev.xctn->disposition = - EIO; @@ -1110,7 +1121,7 @@ static int qspi0_interrupt(int irq, void *context) sem_post(&g_qspi0dev.op_sem); } - /* Is it 'Timeout'? (: */ + /* Is it 'Timeout'? */ if ((status & QSPI_SR_TOF) && (cr & QSPI_CR_TOIE)) { @@ -1118,13 +1129,17 @@ static int qspi0_interrupt(int irq, void *context) qspi_putreg(&g_qspi0dev, QSPI_FCR_CTOF, STM32L4_QUADSPI_FCR); - /* Set error status */ - - g_qspi0dev.xctn->disposition = - ETIMEDOUT; - - /* Signal complete */ - - sem_post(&g_qspi0dev.op_sem); + /* XXX this interrupt simply means that, in 'memory mapped mode', + * the QSPI memory has not been accessed for a while, and the + * IP block was configured to automatically de-assert CS after + * a timeout. And now we're being informed that has happened. + * + * But who cares? If someone does, perhaps a user callback is + * appropriate, or some signal? Either way, realize the xctn + * member is /not/ valid, so you can't set the disposition + * field. Also, note signaling completion has no meaning here + * because in memory mapped mode no one holds the semaphore. + */ } return OK; @@ -1340,6 +1355,14 @@ static uint32_t qspi_setfrequency(struct qspi_dev_s *dev, uint32_t frequency) uint32_t prescaler; uint32_t regval; + if (priv->memmap) + { + /* XXX we have no better return here, but the caller will find out + * in their subsequent calls. + */ + return 0; + } + qspivdbg("frequency=%d\n", frequency); DEBUGASSERT(priv); @@ -1424,6 +1447,14 @@ static void qspi_setmode(struct qspi_dev_s *dev, enum qspi_mode_e mode) struct stm32l4_qspidev_s *priv = (struct stm32l4_qspidev_s *)dev; uint32_t regval; + if (priv->memmap) + { + /* XXX we have no better return here, but the caller will find out + * in their subsequent calls. + */ + return; + } + qspivdbg("mode=%d\n", mode); /* Has the mode changed? */ @@ -1518,6 +1549,16 @@ static int qspi_command(struct qspi_dev_s *dev, struct qspi_xctnspec_s xctn; int ret; + /* Reject commands issued while in memory mapped mode, which will + * automatically cancel the memory mapping. You must exit the + * memory mapped mode first. + */ + + if (priv->memmap) + { + return -EBUSY; + } + /* Set up the transaction descriptor as per command info */ ret = qspi_setupxctnfromcmd(&xctn, cmdinfo); @@ -1689,6 +1730,16 @@ static int qspi_memory(struct qspi_dev_s *dev, struct qspi_xctnspec_s xctn; int ret; + /* Reject commands issued while in memory mapped mode, which will + * automatically cancel the memory mapping. You must exit the + * memory mapped mode first. + */ + + if (priv->memmap) + { + return -EBUSY; + } + /* Set up the transaction descriptor as per command info */ ret = qspi_setupxctnfrommem(&xctn, meminfo); @@ -2080,6 +2131,7 @@ struct qspi_dev_s *stm32l4_qspi_initialize(int intf) /* Enable interrupts at the NVIC */ priv->initialized = true; + priv->memmap = false; #ifdef QSPI_USE_INTERRUPTS up_enable_irq(priv->irq); #endif @@ -2100,4 +2152,122 @@ errout_with_dmadog: sem_destroy(&priv->exclsem); return NULL; } + +/**************************************************************************** + * Name: stm32l4_qspi_enter_memorymapped + * + * Description: + * Put the QSPI device into memory mapped mode + * + * Input Parameter: + * dev - QSPI device + * meminfo - parameters like for a memory transfer used for reading + * + * Returned Value: + * None + * + ****************************************************************************/ + +void stm32l4_qspi_enter_memorymapped(struct qspi_dev_s* dev, + const struct qspi_meminfo_s *meminfo, + uint32_t lpto) +{ + struct stm32l4_qspidev_s *priv = (struct stm32l4_qspidev_s *)dev; + uint32_t regval; + struct qspi_xctnspec_s xctn; + + /* lock during this mode change */ + + qspi_lock(dev, true); + + if (priv->memmap) + { + qspi_lock(dev, false); + return; + } + + /* Abort anything in-progress */ + + qspi_abort(priv); + + /* Wait till BUSY flag reset */ + + qspi_waitstatusflags(priv, QSPI_SR_BUSY, 0); + + /* if we want the 'low-power timeout counter' */ + + if (lpto > 0) + { + /* Set the Low Power Timeout value (automatically de-assert + * CS if memory is not accessed for a while) + */ + + qspi_putreg(priv, lpto, STM32L4_QUADSPI_LPTR_OFFSET); + + /* Clear Timeout interrupt */ + + qspi_putreg(&g_qspi0dev, QSPI_FCR_CTOF, STM32L4_QUADSPI_FCR); + +#ifdef QSPI_USE_INTERRUPTS + /* Enable Timeout interrupt */ + + regval = qspi_getreg(priv, STM32L4_QUADSPI_CR_OFFSET); + regval |= (QSPI_CR_TCEN | QSPI_CR_TOIE); + qspi_putreg(priv, regval, STM32L4_QUADSPI_CR_OFFSET); +#endif + } + else + { + regval = qspi_getreg(priv, STM32L4_QUADSPI_CR_OFFSET); + regval &= ~QSPI_CR_TCEN; + qspi_putreg(priv, regval, STM32L4_QUADSPI_CR_OFFSET); + } + + /* create a transaction object */ + + qspi_setupxctnfrommem(&xctn, meminfo); + priv->xctn = NULL; + + /* set it into the ccr */ + + qspi_ccrconfig(priv, &xctn, CCR_FMODE_MEMMAP); + priv->memmap = true; + + /* we should be in memory mapped mode now */ + + qspi_dumpregs(priv, "After memory mapped:"); + + /* finished this mode change */ + + qspi_lock(dev, false); +} + +/**************************************************************************** + * Name: stm32l4_qspi_exit_memorymapped + * + * Description: + * Take the QSPI device out of memory mapped mode + * + * Input Parameter: + * dev - QSPI device + * + * Returned Value: + * None + * + ****************************************************************************/ + +void stm32l4_qspi_exit_memorymapped(struct qspi_dev_s* dev) +{ + struct stm32l4_qspidev_s *priv = (struct stm32l4_qspidev_s *)dev; + + qspi_lock(dev, true); + + /* A simple abort is sufficient */ + + qspi_abort(priv); + priv->memmap = false; + + qspi_lock(dev, false); +} + #endif /* CONFIG_STM32L4_QSPI */ diff --git a/arch/arm/src/stm32l4/stm32l4_qspi.h b/arch/arm/src/stm32l4/stm32l4_qspi.h index a62fb7635c..ed65850fc5 100644 --- a/arch/arm/src/stm32l4/stm32l4_qspi.h +++ b/arch/arm/src/stm32l4/stm32l4_qspi.h @@ -97,6 +97,42 @@ extern "C" struct qspi_dev_s; FAR struct qspi_dev_s *stm32l4_qspi_initialize(int intf); +/**************************************************************************** + * Name: stm32l4_qspi_enter_memorymapped + * + * Description: + * Put the QSPI device into memory mapped mode + * + * Input Parameter: + * dev - QSPI device + * meminfo - parameters like for a memory transfer used for reading + * lpto - number of cycles to wait to automatically de-assert CS + * + * Returned Value: + * None + * + ****************************************************************************/ + +void stm32l4_qspi_enter_memorymapped(struct qspi_dev_s* dev, + const struct qspi_meminfo_s *meminfo, + uint32_t lpto); + +/**************************************************************************** + * Name: stm32l4_qspi_exit_memorymapped + * + * Description: + * Take the QSPI device out of memory mapped mode + * + * Input Parameter: + * dev - QSPI device + * + * Returned Value: + * None + * + ****************************************************************************/ + +void stm32l4_qspi_exit_memorymapped(struct qspi_dev_s* dev); + #undef EXTERN #if defined(__cplusplus) }