You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by gn...@apache.org on 2020/01/05 13:36:38 UTC
[incubator-nuttx] branch pr39 updated: Implement QSPI for STM32H7
(#39)
This is an automated email from the ASF dual-hosted git repository.
gnutt pushed a commit to branch pr39
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
The following commit(s) were added to refs/heads/pr39 by this push:
new 5889d45 Implement QSPI for STM32H7 (#39)
5889d45 is described below
commit 5889d45897a02643e804976f2071d5fc92fa7a64
Author: Minamiya_Natsuki <yu...@gmail.com>
AuthorDate: Sun Jan 5 21:36:32 2020 +0800
Implement QSPI for STM32H7 (#39)
* clone QSPI code from F7
---
arch/arm/src/stm32f7/hardware/stm32_qspi.h | 2 +-
arch/arm/src/stm32h7/Kconfig | 140 +-
arch/arm/src/stm32h7/Make.defs | 5 +
.../src/{stm32f7 => stm32h7}/hardware/stm32_qspi.h | 17 +-
arch/arm/src/stm32h7/stm32_qspi.c | 2799 ++++++++++++++++++++
arch/arm/src/stm32h7/stm32_qspi.h | 144 +
6 files changed, 3097 insertions(+), 10 deletions(-)
diff --git a/arch/arm/src/stm32f7/hardware/stm32_qspi.h b/arch/arm/src/stm32f7/hardware/stm32_qspi.h
index 61c84d2..190c4c1 100644
--- a/arch/arm/src/stm32f7/hardware/stm32_qspi.h
+++ b/arch/arm/src/stm32f7/hardware/stm32_qspi.h
@@ -236,6 +236,6 @@
* Public Functions
****************************************************************************************/
-#endif /* __ARCH_ARM_SRC_STM32F7_HARDWARE_STM32L4_QSPI_H */
+#endif /* __ARCH_ARM_SRC_STM32F7_HARDWARE_STM32F7_QSPI_H */
diff --git a/arch/arm/src/stm32h7/Kconfig b/arch/arm/src/stm32h7/Kconfig
index 788056a..dcd26f1 100644
--- a/arch/arm/src/stm32h7/Kconfig
+++ b/arch/arm/src/stm32h7/Kconfig
@@ -304,6 +304,10 @@ config STM32H7_OTG_USBREGEN
bool "Enable USB voltage regulator"
default n
+config STM32H7_QUADSPI
+ bool "QuadSPI"
+ default n
+
config STM32H7_USBDEV_REGDEBUG
bool "OTG USBDEV REGDEBUG"
default n
@@ -1154,6 +1158,8 @@ config STM32H7_RTC_LSECLOCK_RUN_DRV_CAPABILITY
endif # STM32H7_RTC_LSECLOCK
+endmenu # RTC Configuration
+
config STM32H7_EXTERNAL_RAM
bool "External RAM on FMC"
default n
@@ -1162,7 +1168,139 @@ config STM32H7_EXTERNAL_RAM
---help---
In addition to internal SDRAM, external RAM may be available through the FMC.
-endmenu # RTC Configuration
+menu "QuadSPI Configuration"
+ depends on STM32H7_QUADSPI
+
+config STM32H7_QSPI_FLASH_SIZE
+ int "Size of attached serial flash, bytes"
+ default 16777216
+ range 1 2147483648
+ ---help---
+ The STM32H7 QSPI peripheral requires the size of the Flash be specified
+
+config STM32H7_QSPI_FIFO_THESHOLD
+ int "Number of bytes before asserting FIFO threshold flag"
+ default 4
+ range 1 16
+ ---help---
+ The STM32H7 QSPI peripheral requires that the FIFO threshold be specified
+ I would leave it at the default value of 4 unless you know what you are doing.
+
+config STM32H7_QSPI_CSHT
+ int "Number of cycles Chip Select must be inactive between transactions"
+ default 1
+ range 1 8
+ ---help---
+ The STM32H7 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 STM32H7_QSPI_DMA
+ ---help---
+ You can choose between using polling, interrupts, or DMA to transfer data
+ over the QSPI interface.
+
+config STM32H7_QSPI_POLLING
+ bool "Polling"
+ ---help---
+ Use conventional register I/O with status polling to transfer data.
+
+config STM32H7_QSPI_INTERRUPTS
+ bool "Interrupts"
+ ---help---
+ User interrupt driven I/O transfers.
+
+config STM32H7_QSPI_DMA
+ bool "DMA"
+ depends on STM32H7_DMA
+ ---help---
+ Use DMA to improve QSPI transfer performance.
+
+endchoice
+
+choice
+ prompt "Bank selection"
+ default STM32H7_QSPI_MODE_BANK1
+ ---help---
+ You can choose between using polling, interrupts, or DMA to transfer data
+ over the QSPI interface.
+
+config STM32H7_QSPI_MODE_BANK1
+ bool "Bank 1"
+
+config STM32H7_QSPI_MODE_BANK2
+ bool "Bank 2"
+
+config STM32H7_QSPI_MODE_DUAL
+ bool "Dual Bank"
+
+endchoice
+
+choice
+ prompt "DMA Priority"
+ default STM32H7_QSPI_DMAPRIORITY_MEDIUM
+ depends on STM32H7_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 STM32H7_QSPI_DMAPRIORITY_VERYHIGH
+ bool "Very High priority"
+ depends on STM32H7_DMA
+ ---help---
+ 'Highest' priority.
+
+config STM32H7_QSPI_DMAPRIORITY_HIGH
+ bool "High priority"
+ depends on STM32H7_DMA
+ ---help---
+ 'High' priority.
+
+config STM32H7_QSPI_DMAPRIORITY_MEDIUM
+ bool "Medium priority"
+ depends on STM32H7_DMA
+ ---help---
+ 'Medium' priority.
+
+config STM32H7_QSPI_DMAPRIORITY_LOW
+ bool "Low priority"
+ depends on STM32H7_DMA
+ ---help---
+ 'Low' priority.
+
+endchoice
+
+config STM32H7_QSPI_DMATHRESHOLD
+ int "QSPI DMA threshold"
+ default 4
+ depends on STM32H7_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 STM32H7_QSPI_DMADEBUG
+ bool "QSPI DMA transfer debug"
+ depends on STM32H7_QSPI_DMA && DEBUG_SPI && 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 STM32H7_QSPI_REGDEBUG
+ bool "QSPI Register level debug"
+ depends on DEBUG_SPI_INFO
+ default n
+ ---help---
+ Output detailed register-level QSPI device debug information.
+ Requires also CONFIG_DEBUG_SPI_INFO.
+
+endmenu
config STM32H7_CUSTOM_CLOCKCONFIG
bool "Custom clock configuration"
diff --git a/arch/arm/src/stm32h7/Make.defs b/arch/arm/src/stm32h7/Make.defs
index 90921db..965f38a 100644
--- a/arch/arm/src/stm32h7/Make.defs
+++ b/arch/arm/src/stm32h7/Make.defs
@@ -161,6 +161,11 @@ ifeq ($(CONFIG_STM32H7_PWR),y)
CHIP_CSRCS += stm32_pwr.c
endif
+
+ifeq ($(CONFIG_STM32H7_QUADSPI),y)
+CHIP_CSRCS += stm32_qspi.c
+endif
+
ifeq ($(CONFIG_STM32H7_RTC),y)
CHIP_CSRCS += stm32_rtc.c
ifeq ($(CONFIG_RTC_ALARM),y)
diff --git a/arch/arm/src/stm32f7/hardware/stm32_qspi.h b/arch/arm/src/stm32h7/hardware/stm32_qspi.h
similarity index 95%
copy from arch/arm/src/stm32f7/hardware/stm32_qspi.h
copy to arch/arm/src/stm32h7/hardware/stm32_qspi.h
index 61c84d2..9b63be1 100644
--- a/arch/arm/src/stm32f7/hardware/stm32_qspi.h
+++ b/arch/arm/src/stm32h7/hardware/stm32_qspi.h
@@ -1,5 +1,5 @@
/****************************************************************************
- * arch/arm/src/stm32f7/hardware/stm32_qspi.h
+ * arch/arm/src/stm32h7/hardware/stm32_qspi.h
*
* Copyright (C) 2016 Gregory Nutt. All rights reserved.
* Author: dev@ziggurat29.com
@@ -33,15 +33,15 @@
*
****************************************************************************/
-#ifndef __ARCH_ARM_SRC_STM32F7_HARDWARE_STM32F7_QSPI_H
-#define __ARCH_ARM_SRC_STM32F7_HARDWARE_STM32F7_QSPI_H
+#ifndef __ARCH_ARM_SRC_STM32H7_HARDWARE_STM32H7_QSPI_H
+#define __ARCH_ARM_SRC_STM32H7_HARDWARE_STM32H7_QSPI_H
/****************************************************************************************
* Included Files
****************************************************************************************/
#include <nuttx/config.h>
-#include <arch/stm32f7/chip.h>
+#include <arch/stm32h7/chip.h>
#include "chip.h"
@@ -51,8 +51,8 @@
/* General Characteristics **************************************************************/
-#define STM32F7_QSPI_MINBITS 8 /* Minimum word width */
-#define STM32F7_QSPI_MAXBITS 32 /* Maximum word width */
+#define STM32H7_QSPI_MINBITS 8 /* Minimum word width */
+#define STM32H7_QSPI_MAXBITS 32 /* Maximum word width */
/* QSPI register offsets ****************************************************************/
@@ -92,7 +92,7 @@
#define QSPI_CR_EN (1 << 0) /* Bit 0: QSPI Enable */
#define QSPI_CR_ABORT (1 << 1) /* Bit 1: Abort request */
-#define QSPI_CR_DMAEN (1 << 2) /* Bit 2: DMA enable */
+// #define QSPI_CR_DMAEN (1 << 2) /* Bit 2: DMA enable */
#define QSPI_CR_TCEN (1 << 3) /* Bit 3: Timeout counter enable */
#define QSPI_CR_SSHIFT (1 << 4) /* Bit 4: Sample shift */
#define QSPI_CR_DFM (1 << 6) /* Bit 6: DFM: Dual-flash mode */
@@ -202,6 +202,7 @@
#define QSPI_CCR_FMODE_MASK (0x3 << QSPI_CCR_FMODE_SHIFT)
# define QSPI_CCR_FMODE(n) ((uint32_t)(n) << QSPI_CCR_FMODE_SHIFT)
#define QSPI_CCR_SIOO (1 << 28) /* Bit 28: Send instruction only once mode */
+#define QSPI_CCR_FRCM (1 << 29) /* Bit 28: Enters Free running clock mode */
#define QSPI_CCR_DDRM (1 << 31) /* Bit 31: Double data rate mode */
/* Address Register */
@@ -236,6 +237,6 @@
* Public Functions
****************************************************************************************/
-#endif /* __ARCH_ARM_SRC_STM32F7_HARDWARE_STM32L4_QSPI_H */
+#endif /* __ARCH_ARM_SRC_STM32H7_HARDWARE_STM32H4_QSPI_H */
diff --git a/arch/arm/src/stm32h7/stm32_qspi.c b/arch/arm/src/stm32h7/stm32_qspi.c
new file mode 100644
index 0000000..81d9c67
--- /dev/null
+++ b/arch/arm/src/stm32h7/stm32_qspi.c
@@ -0,0 +1,2799 @@
+/****************************************************************************
+ * arch/arm/src/stm32h7/stm32_qspi.c
+ *
+ * Copyright (C) 2016-2017, 2019 Gregory Nutt. All rights reserved.
+ * Author: dev@ziggurat29.com
+ *
+ * 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 "stm32_qspi.h"
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <semaphore.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <arch/board/board.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/wdog.h>
+#include <nuttx/clock.h>
+#include <nuttx/cache.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/spi/qspi.h>
+
+#include "up_internal.h"
+#include "up_arch.h"
+#include "barriers.h"
+
+#include "stm32_gpio.h"
+#include "stm32_dma.h"
+#include "stm32_rcc.h"
+#include "hardware/stm32_qspi.h"
+
+#ifdef CONFIG_STM32H7_QUADSPI
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* QSPI memory synchronization */
+
+#define MEMORY_SYNC() do { ARM_DSB(); ARM_ISB(); } while (0)
+
+/* Ensure that the DMA buffers are word-aligned. */
+
+#define ALIGN_SHIFT 2
+#define ALIGN_MASK 3
+#define ALIGN_UP(n) (((n)+ALIGN_MASK) & ~ALIGN_MASK)
+#define IS_ALIGNED(n) (((uint32_t)(n) & ALIGN_MASK) == 0)
+
+/* Debug ********************************************************************/
+
+/* Check if QSPI debug is enabled */
+
+#ifndef CONFIG_DEBUG_DMA
+# undef CONFIG_STM32H7_QSPI_DMADEBUG
+#endif
+
+#define DMA_INITIAL 0
+#define DMA_AFTER_SETUP 1
+#define DMA_AFTER_START 2
+#define DMA_CALLBACK 3
+#define DMA_TIMEOUT 3
+#define DMA_END_TRANSFER 4
+#define DMA_NSAMPLES 5
+
+/* Can't have both interrupt-driven QSPI and DMA QSPI */
+
+#if defined(CONFIG_STM32H7_QSPI_INTERRUPTS) && defined(CONFIG_STM32H7_QSPI_DMA)
+# error "Cannot enable both interrupt mode and DMA mode for QSPI"
+#endif
+
+/* Sanity check that board.h defines requisite QSPI pinmap options for */
+
+#if (!defined(GPIO_QSPI_CS) || !defined(GPIO_QSPI_IO0) || !defined(GPIO_QSPI_IO1) || \
+ !defined(GPIO_QSPI_IO2) || !defined(GPIO_QSPI_IO3) || !defined(GPIO_QSPI_SCK))
+# error you must define QSPI pinmapping options for GPIO_QSPI_CS GPIO_QSPI_IO0 \
+ GPIO_QSPI_IO1 GPIO_QSPI_IO2 GPIO_QSPI_IO3 GPIO_QSPI_SCK in your board.h
+#endif
+
+#ifdef CONFIG_STM32H7_QSPI_DMA
+
+# ifdef DMAMAP_QUADSPI
+
+/* QSPI DMA Channel/Stream selection. There
+ * are multiple DMA stream options that must be dis-ambiguated in the board.h
+ * file.
+ */
+
+# define DMACHAN_QUADSPI DMAMAP_QUADSPI
+# endif
+
+# if defined(CONFIG_STM32H7_QSPI_DMAPRIORITY_LOW)
+# define QSPI_DMA_PRIO DMA_SCR_PRILO
+# elif defined(CONFIG_STM32H7_QSPI_DMAPRIORITY_MEDIUM)
+# define QSPI_DMA_PRIO DMA_SCR_PRIMED
+# elif defined(CONFIG_STM32H7_QSPI_DMAPRIORITY_HIGH)
+# define QSPI_DMA_PRIO DMA_SCR_PRIHI
+# elif defined(CONFIG_STM32H7_QSPI_DMAPRIORITY_VERYHIGH)
+# define QSPI_DMA_PRIO DMA_SCR_PRIVERYHI
+# else
+# define QSPI_DMA_PRIO DMA_SCR_PRIMED
+# endif
+
+#endif /* CONFIG_STM32H7_QSPI_DMA */
+
+#ifndef STM32_SYSCLK_FREQUENCY
+# error your board.h needs to define the value of STM32_SYSCLK_FREQUENCY
+#endif
+
+#if !defined(CONFIG_STM32H7_QSPI_FLASH_SIZE) || 0 == CONFIG_STM32H7_QSPI_FLASH_SIZE
+# error you must specify a positive flash size via CONFIG_STM32H7_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
+ */
+
+#define STL32F7_QSPI_CLOCK STM32_SYSCLK_FREQUENCY /* Frequency of the QSPI clock */
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* The state of the QSPI controller.
+ *
+ * NOTE: the STM32H7 supports only a single QSPI peripheral. Logic here is
+ * designed to support multiple QSPI peripherals.
+ */
+
+struct stm32h7_qspidev_s
+{
+ struct qspi_dev_s qspi; /* Externally visible part of the QSPI interface */
+ uint32_t base; /* QSPI controller register base address */
+ uint32_t frequency; /* Requested clock frequency */
+ uint32_t actual; /* Actual clock frequency */
+ uint8_t mode; /* Mode 0,3 */
+ uint8_t nbits; /* Width of word in bits (8 to 32) */
+ 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 CONFIG_STM32H7_QSPI_INTERRUPTS
+ xcpt_t handler; /* Interrupt handler */
+ uint8_t irq; /* Interrupt number */
+ sem_t op_sem; /* Block until complete */
+ struct qspi_xctnspec_s *xctn; /* context of transaction in progress */
+#endif
+
+#ifdef CONFIG_STM32H7_QSPI_DMA
+ 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 */
+
+#ifdef CONFIG_STM32H7_QSPI_DMADEBUG
+ struct stm32h7_dmaregs_s dmaregs[DMA_NSAMPLES];
+#endif
+
+#ifdef CONFIG_STM32H7_QSPI_REGDEBUG
+ bool wrlast; /* Last was a write */
+ uint32_t addresslast; /* Last address */
+ uint32_t valuelast; /* Last value */
+ int ntimes; /* Number of times */
+#endif
+};
+
+/* The QSPI transaction specification
+ *
+ * This is mostly the values of the CCR and DLR, AR, ABR, broken out into a C
+ * structure since these fields need to be considered at various phases of
+ * thee transaction processing activity.
+ */
+
+struct qspi_xctnspec_s
+{
+ uint8_t instrmode; /* 'instruction mode'; 0=none, 1=single, 2=dual, 3=quad */
+ uint8_t instr; /* the (8-bit) Instruction (if any) */
+
+ uint8_t addrmode; /* 'address mode'; 0=none, 1=single, 2=dual, 3=quad */
+ uint8_t addrsize; /* address size (n - 1); 0, 1, 2, 3 */
+ uint32_t addr; /* the address (if any) (1 to 4 bytes as per addrsize) */
+
+ uint8_t altbytesmode; /* 'alt bytes mode'; 0=none, 1=single, 2=dual, 3=quad */
+ uint8_t altbytessize; /* 'alt bytes' size (n - 1); 0, 1, 2, 3 */
+ uint32_t altbytes; /* the 'alt bytes' (if any) */
+
+ uint8_t dummycycles; /* number of Dummy Cycles; 0 - 32 */
+
+ uint8_t datamode; /* 'data mode'; 0=none, 1=single, 2=dual, 3=quad */
+ uint32_t datasize; /* number of data bytes (0xffffffff == undefined) */
+ FAR void *buffer; /* Data buffer */
+
+ uint8_t isddr; /* true if 'double data rate' */
+ uint8_t issioo; /* true if 'send instruction only once' mode */
+
+#ifdef CONFIG_STM32H7_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 */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Helpers */
+
+#ifdef CONFIG_STM32H7_QSPI_REGDEBUG
+static bool qspi_checkreg(struct stm32h7_qspidev_s *priv, bool wr,
+ uint32_t value, uint32_t address);
+#else
+# define qspi_checkreg(priv,wr,value,address) (false)
+#endif
+
+static inline uint32_t qspi_getreg(struct stm32h7_qspidev_s *priv,
+ unsigned int offset);
+static inline void qspi_putreg(struct stm32h7_qspidev_s *priv, uint32_t value,
+ unsigned int offset);
+
+#ifdef CONFIG_DEBUG_SPI_INFO
+static void qspi_dumpregs(struct stm32h7_qspidev_s *priv,
+ const char *msg);
+#else
+# define qspi_dumpregs(priv,msg)
+#endif
+
+#if defined(CONFIG_DEBUG_SPI_INFO) && defined(CONFIG_DEBUG_GPIO)
+static void qspi_dumpgpioconfig(const char *msg);
+#else
+# define qspi_dumpgpioconfig(msg)
+#endif
+
+/* Interrupts */
+
+#ifdef CONFIG_STM32H7_QSPI_INTERRUPTS
+static int qspi0_interrupt(int irq, void *context, FAR void *arg);
+
+#endif
+
+/* DMA support */
+
+#ifdef CONFIG_STM32H7_QSPI_DMA
+
+# ifdef CONFIG_STM32H7_QSPI_DMADEBUG
+# define qspi_dma_sample(s,i) stm32h7_dmasample((s)->dmach, &(s)->dmaregs[i])
+static void qspi_dma_sampleinit(struct stm32h7_qspidev_s *priv);
+static void qspi_dma_sampledone(struct stm32h7_qspidev_s *priv);
+# else
+# define qspi_dma_sample(s,i)
+# define qspi_dma_sampleinit(s)
+# define qspi_dma_sampledone(s)
+# endif
+
+# ifndef CONFIG_STM32H7_QSPI_DMATHRESHOLD
+# define CONFIG_STM32H7_QSPI_DMATHRESHOLD 4
+# endif
+
+#endif
+
+/* QSPI methods */
+
+static int qspi_lock(struct qspi_dev_s *dev, bool lock);
+static uint32_t qspi_setfrequency(struct qspi_dev_s *dev, uint32_t frequency);
+static void qspi_setmode(struct qspi_dev_s *dev, enum qspi_mode_e mode);
+static void qspi_setbits(struct qspi_dev_s *dev, int nbits);
+static int qspi_command(struct qspi_dev_s *dev,
+ struct qspi_cmdinfo_s *cmdinfo);
+static int qspi_memory(struct qspi_dev_s *dev,
+ struct qspi_meminfo_s *meminfo);
+static FAR void *qspi_alloc(FAR struct qspi_dev_s *dev, size_t buflen);
+static void qspi_free(FAR struct qspi_dev_s *dev, FAR void *buffer);
+
+/* Initialization */
+
+static int qspi_hw_initialize(struct stm32h7_qspidev_s *priv);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* QSPI0 driver operations */
+
+static const struct qspi_ops_s g_qspi0ops =
+{
+ .lock = qspi_lock,
+ .setfrequency = qspi_setfrequency,
+ .setmode = qspi_setmode,
+ .setbits = qspi_setbits,
+ .command = qspi_command,
+ .memory = qspi_memory,
+ .alloc = qspi_alloc,
+ .free = qspi_free,
+};
+
+/* This is the overall state of the QSPI0 controller */
+
+static struct stm32h7_qspidev_s g_qspi0dev =
+{
+ .qspi =
+ {
+ .ops = &g_qspi0ops,
+ },
+ .base = STM32_QUADSPI_BASE,
+#ifdef CONFIG_STM32H7_QSPI_INTERRUPTS
+ .handler = qspi0_interrupt,
+ .irq = STM32_IRQ_QUADSPI,
+#endif
+ .intf = 0,
+#ifdef CONFIG_STM32H7_QSPI_DMA
+ .candma = true,
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: qspi_checkreg
+ *
+ * Description:
+ * Check if the current register access is a duplicate of the preceding.
+ *
+ * Input Parameters:
+ * value - The value to be written
+ * address - The address of the register to write to
+ *
+ * Returned Value:
+ * true: This is the first register access of this type.
+ * false: This is the same as the preceding register access.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32H7_QSPI_REGDEBUG
+static bool qspi_checkreg(struct stm32h7_qspidev_s *priv, bool wr,
+ uint32_t value, uint32_t address)
+{
+ if (wr == priv->wrlast && /* Same kind of access? */
+ value == priv->valuelast && /* Same value? */
+ address == priv->addresslast) /* Same address? */
+ {
+ /* Yes, then just keep a count of the number of times we did this. */
+
+ priv->ntimes++;
+ return false;
+ }
+ else
+ {
+ /* Did we do the previous operation more than once? */
+
+ if (priv->ntimes > 0)
+ {
+ /* Yes... show how many times we did it */
+
+ spiinfo("...[Repeats %d times]...\n", priv->ntimes);
+ }
+
+ /* Save information about the new access */
+
+ priv->wrlast = wr;
+ priv->valuelast = value;
+ priv->addresslast = address;
+ priv->ntimes = 0;
+ }
+
+ /* Return true if this is the first time that we have done this operation */
+
+ return true;
+}
+#endif
+
+/****************************************************************************
+ * Name: qspi_getreg
+ *
+ * Description:
+ * Read an QSPI register
+ *
+ ****************************************************************************/
+
+static inline uint32_t qspi_getreg(struct stm32h7_qspidev_s *priv,
+ unsigned int offset)
+{
+ uint32_t address = priv->base + offset;
+ uint32_t value = getreg32(address);
+
+#ifdef CONFIG_STM32H7_QSPI_REGDEBUG
+ if (qspi_checkreg(priv, false, value, address))
+ {
+ spiinfo("%08x->%08x\n", address, value);
+ }
+#endif
+
+ return value;
+}
+
+/****************************************************************************
+ * Name: qspi_putreg
+ *
+ * Description:
+ * Write a value to an QSPI register
+ *
+ ****************************************************************************/
+
+static inline void qspi_putreg(struct stm32h7_qspidev_s *priv, uint32_t value,
+ unsigned int offset)
+{
+ uint32_t address = priv->base + offset;
+
+#ifdef CONFIG_STM32H7_QSPI_REGDEBUG
+ if (qspi_checkreg(priv, true, value, address))
+ {
+ spiinfo("%08x<-%08x\n", address, value);
+ }
+#endif
+
+ putreg32(value, address);
+}
+
+/****************************************************************************
+ * Name: qspi_dumpregs
+ *
+ * Description:
+ * Dump the contents of all QSPI registers
+ *
+ * Input Parameters:
+ * priv - The QSPI controller to dump
+ * msg - Message to print before the register data
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_SPI_INFO
+static void qspi_dumpregs(struct stm32h7_qspidev_s *priv, const char *msg)
+{
+ uint32_t regval;
+ spiinfo("%s:\n", msg);
+
+#if 0
+ /* this extra verbose output may be helpful in some cases; you'll need
+ * to make sure your syslog is large enough to accommodate the extra output.
+ */
+
+ regval = getreg32(priv->base + STM32_QUADSPI_CR_OFFSET); /* Control Register */
+ spiinfo("CR:%08x\n", regval);
+ spiinfo(" EN:%1d ABORT:%1d DMAEN:%1d TCEN:%1d SSHIFT:%1d\n"
+ " FTHRES: %d\n"
+ " TEIE:%1d TCIE:%1d FTIE:%1d SMIE:%1d TOIE:%1d APMS:%1d PMM:%1d\n"
+ " PRESCALER: %d\n",
+ (regval & QSPI_CR_EN) ? 1 : 0,
+ (regval & QSPI_CR_ABORT) ? 1 : 0,
+ (regval & QSPI_CR_DMAEN) ? 1 : 0,
+ (regval & QSPI_CR_TCEN) ? 1 : 0,
+ (regval & QSPI_CR_SSHIFT) ? 1 : 0,
+ (regval & QSPI_CR_FTHRES_MASK) >> QSPI_CR_FTHRES_SHIFT,
+ (regval & QSPI_CR_TEIE) ? 1 : 0,
+ (regval & QSPI_CR_TCIE) ? 1 : 0,
+ (regval & QSPI_CR_FTIE) ? 1 : 0,
+ (regval & QSPI_CR_SMIE) ? 1 : 0,
+ (regval & QSPI_CR_TOIE) ? 1 : 0,
+ (regval & QSPI_CR_APMS) ? 1 : 0,
+ (regval & QSPI_CR_PMM) ? 1 : 0,
+ (regval & QSPI_CR_PRESCALER_MASK) >> QSPI_CR_PRESCALER_SHIFT);
+
+ regval = getreg32(priv->base + STM32_QUADSPI_DCR_OFFSET); /* Device Configuration Register */
+ spiinfo("DCR:%08x\n", regval);
+ spiinfo(" CKMODE:%1d CSHT:%d FSIZE:%d\n",
+ (regval & QSPI_DCR_CKMODE) ? 1 : 0,
+ (regval & QSPI_DCR_CSHT_MASK) >> QSPI_DCR_CSHT_SHIFT,
+ (regval & QSPI_DCR_FSIZE_MASK) >> QSPI_DCR_FSIZE_SHIFT);
+
+ regval = getreg32(priv->base + STM32_QUADSPI_CCR_OFFSET); /* Communication Configuration Register */
+ spiinfo("CCR:%08x\n", regval);
+ spiinfo(" INST:%02x IMODE:%d ADMODE:%d ADSIZE:%d ABMODE:%d\n"
+ " ABSIZE:%d DCYC:%d DMODE:%d FMODE:%d\n"
+ " SIOO:%1d DDRM:%1d\n",
+ (regval & QSPI_CCR_INSTRUCTION_MASK) >> QSPI_CCR_INSTRUCTION_SHIFT,
+ (regval & QSPI_CCR_IMODE_MASK) >> QSPI_CCR_IMODE_SHIFT,
+ (regval & QSPI_CCR_ADMODE_MASK) >> QSPI_CCR_ADMODE_SHIFT,
+ (regval & QSPI_CCR_ADSIZE_MASK) >> QSPI_CCR_ABSIZE_SHIFT,
+ (regval & QSPI_CCR_ABMODE_MASK) >> QSPI_CCR_ABMODE_SHIFT,
+ (regval & QSPI_CCR_ABSIZE_MASK) >> QSPI_CCR_ABSIZE_SHIFT,
+ (regval & QSPI_CCR_DCYC_MASK) >> QSPI_CCR_DCYC_SHIFT,
+ (regval & QSPI_CCR_DMODE_MASK) >> QSPI_CCR_DMODE_SHIFT,
+ (regval & QSPI_CCR_FMODE_MASK) >> QSPI_CCR_FMODE_SHIFT,
+ (regval & QSPI_CCR_SIOO) ? 1 : 0,
+ (regval & QSPI_CCR_DDRM) ? 1 : 0);
+
+ regval = getreg32(priv->base + STM32_QUADSPI_SR_OFFSET); /* Status Register */
+ spiinfo("SR:%08x\n", regval);
+ spiinfo(" TEF:%1d TCF:%1d FTF:%1d SMF:%1d TOF:%1d BUSY:%1d FLEVEL:%d\n",
+ (regval & QSPI_SR_TEF) ? 1 : 0,
+ (regval & QSPI_SR_TCF) ? 1 : 0,
+ (regval & QSPI_SR_FTF) ? 1 : 0,
+ (regval & QSPI_SR_SMF) ? 1 : 0,
+ (regval & QSPI_SR_TOF) ? 1 : 0,
+ (regval & QSPI_SR_BUSY) ? 1 : 0,
+ (regval & QSPI_SR_FLEVEL_MASK) >> QSPI_SR_FLEVEL_SHIFT);
+
+#else
+ spiinfo(" CR:%08x DCR:%08x CCR:%08x SR:%08x\n",
+ getreg32(priv->base + STM32_QUADSPI_CR_OFFSET), /* Control Register */
+ getreg32(priv->base + STM32_QUADSPI_DCR_OFFSET), /* Device Configuration Register */
+ getreg32(priv->base + STM32_QUADSPI_CCR_OFFSET), /* Communication Configuration Register */
+ getreg32(priv->base + STM32_QUADSPI_SR_OFFSET)); /* Status Register */
+ spiinfo(" DLR:%08x ABR:%08x PSMKR:%08x PSMAR:%08x\n",
+ getreg32(priv->base + STM32_QUADSPI_DLR_OFFSET), /* Data Length Register */
+ getreg32(priv->base + STM32_QUADSPI_ABR_OFFSET), /* Alternate Bytes Register */
+ getreg32(priv->base + STM32_QUADSPI_PSMKR_OFFSET), /* Polling Status mask Register */
+ getreg32(priv->base + STM32_QUADSPI_PSMAR_OFFSET)); /* Polling Status match Register */
+ spiinfo(" PIR:%08x LPTR:%08x\n",
+ getreg32(priv->base + STM32_QUADSPI_PIR_OFFSET), /* Polling Interval Register */
+ getreg32(priv->base + STM32_QUADSPI_LPTR_OFFSET)); /* Low-Power Timeout Register */
+ (void)regval;
+#endif
+}
+#endif
+
+#if defined(CONFIG_DEBUG_SPI_INFO) && defined(CONFIG_DEBUG_GPIO)
+static void qspi_dumpgpioconfig(const char *msg)
+{
+ uint32_t regval;
+ spiinfo("%s:\n", msg);
+
+ /* Port B */
+
+ regval = getreg32(STM32_GPIOB_MODER);
+ spiinfo("B_MODER:%08x\n", regval);
+
+ regval = getreg32(STM32_GPIOB_OTYPER);
+ spiinfo("B_OTYPER:%08x\n", regval);
+
+ regval = getreg32(STM32_GPIOB_OSPEED);
+ spiinfo("B_OSPEED:%08x\n", regval);
+
+ regval = getreg32(STM32_GPIOB_PUPDR);
+ spiinfo("B_PUPDR:%08x\n", regval);
+
+ regval = getreg32(STM32_GPIOB_AFRL);
+ spiinfo("B_AFRL:%08x\n", regval);
+
+ regval = getreg32(STM32_GPIOB_AFRH);
+ spiinfo("B_AFRH:%08x\n", regval);
+
+ /* Port D */
+
+ regval = getreg32(STM32_GPIOD_MODER);
+ spiinfo("D_MODER:%08x\n", regval);
+
+ regval = getreg32(STM32_GPIOD_OTYPER);
+ spiinfo("D_OTYPER:%08x\n", regval);
+
+ regval = getreg32(STM32_GPIOD_OSPEED);
+ spiinfo("D_OSPEED:%08x\n", regval);
+
+ regval = getreg32(STM32_GPIOD_PUPDR);
+ spiinfo("D_PUPDR:%08x\n", regval);
+
+ regval = getreg32(STM32_GPIOD_AFRL);
+ spiinfo("D_AFRL:%08x\n", regval);
+
+ regval = getreg32(STM32_GPIOD_AFRH);
+ spiinfo("D_AFRH:%08x\n", regval);
+
+ /* Port E */
+
+ regval = getreg32(STM32_GPIOE_MODER);
+ spiinfo("E_MODER:%08x\n", regval);
+
+ regval = getreg32(STM32_GPIOE_OTYPER);
+ spiinfo("E_OTYPER:%08x\n", regval);
+
+ regval = getreg32(STM32_GPIOE_OSPEED);
+ spiinfo("E_OSPEED:%08x\n", regval);
+
+ regval = getreg32(STM32_GPIOE_PUPDR);
+ spiinfo("E_PUPDR:%08x\n", regval);
+
+ regval = getreg32(STM32_GPIOE_AFRL);
+ spiinfo("E_AFRL:%08x\n", regval);
+
+ regval = getreg32(STM32_GPIOE_AFRH);
+ spiinfo("E_AFRH:%08x\n", regval);
+}
+#endif
+
+#ifdef CONFIG_STM32H7_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 stm32h7_qspidev_s *priv)
+{
+ /* Put contents of register samples into a known state */
+
+ memset(priv->dmaregs, 0xff,
+ DMA_NSAMPLES * sizeof(struct stm32h7_dmaregs_s));
+
+ /* Then get the initial samples */
+
+ stm32h7_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 stm32h7_qspidev_s *priv)
+{
+ /* Sample the final registers */
+
+ stm32h7_dmasample(priv->dmach, &priv->dmaregs[DMA_END_TRANSFER]);
+
+ /* Then dump the sampled DMA registers */
+
+ /* Initial register values */
+
+ stm32h7_dmadump(priv->dmach, &priv->dmaregs[DMA_INITIAL],
+ "Initial Registers");
+
+ /* Register values after DMA setup */
+
+ stm32h7_dmadump(priv->dmach, &priv->dmaregs[DMA_AFTER_SETUP],
+ "After DMA Setup");
+
+ /* Register values after DMA start */
+
+ stm32h7_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)
+ {
+ stm32h7_dmadump(priv->dmach, &priv->dmaregs[DMA_TIMEOUT],
+ "At DMA timeout");
+ }
+ else
+ {
+ stm32h7_dmadump(priv->dmach, &priv->dmaregs[DMA_CALLBACK],
+ "At DMA callback");
+ }
+
+ stm32h7_dmadump(priv->dmach, &priv->dmaregs[DMA_END_TRANSFER],
+ "At End-of-Transfer");
+}
+#endif
+
+/****************************************************************************
+ * Name: qspi_setupxctnfromcmd
+ *
+ * Description:
+ * Setup our transaction descriptor from a command info structure
+ *
+ * Input Parameters:
+ * xctn - the transaction descriptor we setup
+ * cmdinfo - the command info (originating from the MTD device)
+ *
+ * Returned Value:
+ * OK, or -errno if invalid
+ *
+ ****************************************************************************/
+
+static int qspi_setupxctnfromcmd(struct qspi_xctnspec_s *xctn,
+ const struct qspi_cmdinfo_s *cmdinfo)
+{
+ DEBUGASSERT(xctn != NULL && cmdinfo != NULL);
+
+#ifdef CONFIG_DEBUG_SPI_INFO
+ spiinfo("Transfer:\n");
+ spiinfo(" flags: %02x\n", cmdinfo->flags);
+ spiinfo(" cmd: %04x\n", cmdinfo->cmd);
+
+ if (QSPICMD_ISADDRESS(cmdinfo->flags))
+ {
+ spiinfo(" address/length: %08lx/%d\n",
+ (unsigned long)cmdinfo->addr, cmdinfo->addrlen);
+ }
+
+ if (QSPICMD_ISDATA(cmdinfo->flags))
+ {
+ spiinfo(" %s Data:\n",
+ QSPICMD_ISWRITE(cmdinfo->flags) ? "Write" : "Read");
+ spiinfo(" buffer/length: %p/%d\n",
+ cmdinfo->buffer, cmdinfo->buflen);
+ }
+#endif
+
+ DEBUGASSERT(cmdinfo->cmd < 256);
+
+ /* Specify the instruction as per command info */
+
+ /* XXX III instruction mode, single dual quad option bits */
+
+ xctn->instrmode = CCR_IMODE_SINGLE;
+ xctn->instr = cmdinfo->cmd;
+
+ /* XXX III option bits for 'send instruction only once' */
+
+ xctn->issioo = 0;
+
+ /* XXX III options for alt bytes, dummy cycles */
+
+ xctn->altbytesmode = CCR_ABMODE_NONE;
+ xctn->altbytessize = CCR_ABSIZE_8;
+ xctn->altbytes = 0;
+ xctn->dummycycles = 0;
+
+ /* Specify the address size as needed */
+
+ if (QSPICMD_ISADDRESS(cmdinfo->flags))
+ {
+ /* XXX III address mode mode, single, dual, quad option bits */
+
+ xctn->addrmode = CCR_ADMODE_SINGLE;
+ if (cmdinfo->addrlen == 1)
+ {
+ xctn->addrsize = CCR_ADSIZE_8;
+ }
+ else if (cmdinfo->addrlen == 2)
+ {
+ xctn->addrsize = CCR_ADSIZE_16;
+ }
+ else if (cmdinfo->addrlen == 3)
+ {
+ xctn->addrsize = CCR_ADSIZE_24;
+ }
+ else if (cmdinfo->addrlen == 4)
+ {
+ xctn->addrsize = CCR_ADSIZE_32;
+ }
+ else
+ {
+ return -EINVAL;
+ }
+
+ xctn->addr = cmdinfo->addr;
+ }
+ else
+ {
+ xctn->addrmode = CCR_ADMODE_NONE;
+ xctn->addrsize = 0;
+ xctn->addr = cmdinfo->addr;
+ }
+
+ /* Specify the data as needed */
+
+ xctn->buffer = cmdinfo->buffer;
+ if (QSPICMD_ISDATA(cmdinfo->flags))
+ {
+ /* XXX III data mode mode, single, dual, quad option bits */
+
+ xctn->datamode = CCR_DMODE_SINGLE;
+ xctn->datasize = cmdinfo->buflen;
+
+ /* XXX III double data rate option bits */
+
+ xctn->isddr = 0;
+ }
+ else
+ {
+ xctn->datamode = CCR_DMODE_NONE;
+ xctn->datasize = 0;
+ xctn->isddr = 0;
+ }
+
+#if defined(CONFIG_STM32H7_QSPI_INTERRUPTS)
+ xctn->function = QSPICMD_ISWRITE(cmdinfo->flags) ? CCR_FMODE_INDWR :
+ CCR_FMODE_INDRD;
+ xctn->disposition = - EIO;
+ xctn->idxnow = 0;
+#endif
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: qspi_setupxctnfrommem
+ *
+ * Description:
+ * Setup our transaction descriptor from a memory info structure
+ *
+ * Input Parameters:
+ * xctn - the transaction descriptor we setup
+ * meminfo - the memory info (originating from the MTD device)
+ *
+ * Returned Value:
+ * OK, or -errno if invalid
+ *
+ ****************************************************************************/
+
+static int qspi_setupxctnfrommem(struct qspi_xctnspec_s *xctn,
+ const struct qspi_meminfo_s *meminfo)
+{
+ DEBUGASSERT(xctn != NULL && meminfo != NULL);
+
+#ifdef CONFIG_DEBUG_SPI_INFO
+ spiinfo("Transfer:\n");
+ spiinfo(" flags: %02x\n", meminfo->flags);
+ spiinfo(" cmd: %04x\n", meminfo->cmd);
+ spiinfo(" address/length: %08lx/%d\n",
+ (unsigned long)meminfo->addr, meminfo->addrlen);
+ spiinfo(" %s Data:\n", QSPIMEM_ISWRITE(meminfo->flags) ? "Write" : "Read");
+ spiinfo(" buffer/length: %p/%d\n", meminfo->buffer, meminfo->buflen);
+#endif
+
+ DEBUGASSERT(meminfo->cmd < 256);
+
+ /* Specify the instruction as per command info */
+
+ /* XXX III instruction mode, single dual quad option bits */
+
+ xctn->instrmode = CCR_IMODE_SINGLE;
+ xctn->instr = meminfo->cmd;
+
+ /* XXX III option bits for 'send instruction only once' */
+
+ xctn->issioo = 0;
+
+ /* XXX III options for alt bytes */
+
+ xctn->altbytesmode = CCR_ABMODE_NONE;
+ xctn->altbytessize = CCR_ABSIZE_8;
+ xctn->altbytes = 0;
+
+ xctn->dummycycles = meminfo->dummies;
+
+ /* Specify the address size as needed */
+
+ /* XXX III there should be a separate flags for single/dual/quad for each
+ * of i,a,d
+ */
+
+ if (QSPIMEM_ISDUALIO(meminfo->flags))
+ {
+ xctn->addrmode = CCR_ADMODE_DUAL;
+ }
+ else if (QSPIMEM_ISQUADIO(meminfo->flags))
+ {
+ xctn->addrmode = CCR_ADMODE_QUAD;
+ }
+ else
+ {
+ xctn->addrmode = CCR_ADMODE_SINGLE;
+ }
+
+ if (meminfo->addrlen == 1)
+ {
+ xctn->addrsize = CCR_ADSIZE_8;
+ }
+ else if (meminfo->addrlen == 2)
+ {
+ xctn->addrsize = CCR_ADSIZE_16;
+ }
+ else if (meminfo->addrlen == 3)
+ {
+ xctn->addrsize = CCR_ADSIZE_24;
+ }
+ else if (meminfo->addrlen == 4)
+ {
+ xctn->addrsize = CCR_ADSIZE_32;
+ }
+ else
+ {
+ return -EINVAL;
+ }
+
+ xctn->addr = meminfo->addr;
+
+ /* Specify the data as needed */
+
+ xctn->buffer = meminfo->buffer;
+
+ /* XXX III there should be a separate flags for single/dual/quad for each
+ * of i,a,d
+ */
+
+ if (QSPIMEM_ISDUALIO(meminfo->flags))
+ {
+ xctn->datamode = CCR_DMODE_DUAL;
+ }
+ else if (QSPIMEM_ISQUADIO(meminfo->flags))
+ {
+ xctn->datamode = CCR_DMODE_QUAD;
+ }
+ else
+ {
+ xctn->datamode = CCR_DMODE_SINGLE;
+ }
+
+ xctn->datasize = meminfo->buflen;
+
+ /* XXX III double data rate option bits */
+
+ xctn->isddr = 0;
+
+#if defined(CONFIG_STM32H7_QSPI_INTERRUPTS)
+ xctn->function = QSPIMEM_ISWRITE(meminfo->flags) ? CCR_FMODE_INDWR :
+ CCR_FMODE_INDRD;
+ xctn->disposition = - EIO;
+ xctn->idxnow = 0;
+#endif
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: qspi_waitstatusflags
+ *
+ * Description:
+ * Spin wait for specified status flags to be set as desired
+ *
+ * Input Parameters:
+ * priv - The QSPI controller to dump
+ * mask - bits to check, can be multiple
+ * polarity - true wait if any set, false to wait if all reset
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void qspi_waitstatusflags(struct stm32h7_qspidev_s *priv,
+ uint32_t mask, int polarity)
+{
+ uint32_t regval;
+
+ if (polarity)
+ {
+ while (!((regval = qspi_getreg(priv, STM32_QUADSPI_SR_OFFSET)) & mask));
+ }
+ else
+ {
+ while (((regval = qspi_getreg(priv, STM32_QUADSPI_SR_OFFSET)) & mask));
+ }
+}
+
+/****************************************************************************
+ * Name: qspi_abort
+ *
+ * Description:
+ * Abort any transaction in progress
+ *
+ * Input Parameters:
+ * priv - The QSPI controller to dump
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void qspi_abort(struct stm32h7_qspidev_s *priv)
+{
+ uint32_t regval;
+
+ regval = qspi_getreg(priv, STM32_QUADSPI_CR_OFFSET);
+ regval |= QSPI_CR_ABORT;
+ qspi_putreg(priv, regval, STM32_QUADSPI_CR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: qspi_ccrconfig
+ *
+ * Description:
+ * Do common Communications Configuration Register setup
+ *
+ * Input Parameters:
+ * priv - The QSPI controller to dump
+ * xctn - the transaction descriptor; CCR setup
+ * fctn - 'functional mode'; 0=indwrite, 1=indread, 2=autopoll, 3=memmmap
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void qspi_ccrconfig(struct stm32h7_qspidev_s *priv,
+ struct qspi_xctnspec_s *xctn,
+ uint8_t fctn)
+{
+ uint32_t regval;
+
+ /* If we have data, and it's not memory mapped, write the length */
+
+ if (CCR_DMODE_NONE != xctn->datamode && CCR_FMODE_MEMMAP != fctn)
+ {
+ qspi_putreg(priv, xctn->datasize - 1, STM32_QUADSPI_DLR_OFFSET);
+ }
+
+ /* If we have alternate bytes, stick them in now */
+
+ if (CCR_ABMODE_NONE != xctn->altbytesmode)
+ {
+ qspi_putreg(priv, xctn->altbytes, STM32_QUADSPI_ABR_OFFSET);
+ }
+
+ /* Build the CCR value and set it */
+
+ regval = QSPI_CCR_INST(xctn->instr) |
+ QSPI_CCR_IMODE(xctn->instrmode) |
+ QSPI_CCR_ADMODE(xctn->addrmode) |
+ QSPI_CCR_ADSIZE(xctn->addrsize) |
+ QSPI_CCR_ABMODE(xctn->altbytesmode) |
+ QSPI_CCR_ABSIZE(xctn->altbytessize) |
+ QSPI_CCR_DCYC(xctn->dummycycles) |
+ QSPI_CCR_DMODE(xctn->datamode) |
+ QSPI_CCR_FMODE(fctn) |
+ (xctn->isddr ? QSPI_CCR_SIOO : 0) |
+ (xctn->issioo ? QSPI_CCR_DDRM : 0);
+ qspi_putreg(priv, regval, STM32_QUADSPI_CCR_OFFSET);
+
+ /* If we have and need and address, set that now, too */
+
+ if (CCR_ADMODE_NONE != xctn->addrmode && CCR_FMODE_MEMMAP != fctn)
+ {
+ qspi_putreg(priv, xctn->addr, STM32_QUADSPI_AR_OFFSET);
+ }
+}
+
+#if defined(CONFIG_STM32H7_QSPI_INTERRUPTS)
+/****************************************************************************
+ * Name: qspi0_interrupt
+ *
+ * Description:
+ * Interrupt handler; we handle all QSPI cases -- reads, writes,
+ * automatic status polling, etc.
+ *
+ * Input Parameters:
+ * irq -
+ * context -
+ *
+ * Returned Value:
+ * OK means we handled it
+ *
+ ****************************************************************************/
+
+static int qspi0_interrupt(int irq, void *context, FAR void *arg)
+{
+ uint32_t status;
+ uint32_t cr;
+ uint32_t regval;
+
+ /* Let's find out what is going on */
+
+ status = qspi_getreg(&g_qspi0dev, STM32_QUADSPI_SR_OFFSET);
+ cr = qspi_getreg(&g_qspi0dev, STM32_QUADSPI_CR_OFFSET);
+
+ /* Is it 'FIFO Threshold'? */
+
+ if ((status & QSPI_SR_FTF) && (cr & QSPI_CR_FTIE))
+ {
+ volatile uint32_t *datareg =
+ (volatile uint32_t *)(g_qspi0dev.base + STM32_QUADSPI_DR_OFFSET);
+
+ if (g_qspi0dev.xctn->function == CCR_FMODE_INDWR)
+ {
+ /* Write data until we have no more or have no place to put it */
+
+ while (((regval = qspi_getreg(&g_qspi0dev, STM32_QUADSPI_SR_OFFSET)) &
+ QSPI_SR_FTF) != 0)
+ {
+ if (g_qspi0dev.xctn->idxnow < g_qspi0dev.xctn->datasize)
+ {
+ *(volatile uint8_t *)datareg =
+ ((uint8_t *)g_qspi0dev.xctn->buffer)[g_qspi0dev.xctn->idxnow];
+ ++g_qspi0dev.xctn->idxnow;
+ }
+ else
+ {
+ /* Fresh out of data to write */
+
+ break;
+ }
+ }
+ }
+ else if (g_qspi0dev.xctn->function == CCR_FMODE_INDRD)
+ {
+ /* Read data until we have no more or have no place to put it */
+
+ while (((regval = qspi_getreg(&g_qspi0dev, STM32_QUADSPI_SR_OFFSET)) &
+ QSPI_SR_FTF) != 0)
+ {
+ if (g_qspi0dev.xctn->idxnow < g_qspi0dev.xctn->datasize)
+ {
+ ((uint8_t *)g_qspi0dev.xctn->buffer)[g_qspi0dev.xctn->idxnow] =
+ *(volatile uint8_t *)datareg;
+ ++g_qspi0dev.xctn->idxnow;
+ }
+ else
+ {
+ /* no room at the inn */
+
+ break;
+ }
+ }
+ }
+ }
+
+ /* Is it 'Transfer Complete'? */
+
+ if ((status & QSPI_SR_TCF) && (cr & QSPI_CR_TCIE))
+ {
+ /* Acknowledge interrupt */
+
+ qspi_putreg(&g_qspi0dev, QSPI_FCR_CTCF, STM32_QUADSPI_FCR_OFFSET);
+
+ /* Disable the QSPI FIFO Threshold, Transfer Error and Transfer
+ * complete Interrupts
+ */
+
+ regval = qspi_getreg(&g_qspi0dev, STM32_QUADSPI_CR_OFFSET);
+ regval &= ~(QSPI_CR_TEIE | QSPI_CR_TCIE | QSPI_CR_FTIE);
+ qspi_putreg(&g_qspi0dev, regval, STM32_QUADSPI_CR_OFFSET);
+
+ /* Do the last bit of read if needed */
+
+ if (g_qspi0dev.xctn->function == CCR_FMODE_INDRD)
+ {
+ volatile uint32_t *datareg =
+ (volatile uint32_t *)(g_qspi0dev.base + STM32_QUADSPI_DR_OFFSET);
+
+ /* Read any remaining data */
+
+ while (((regval = qspi_getreg(&g_qspi0dev, STM32_QUADSPI_SR_OFFSET)) &
+ QSPI_SR_FLEVEL_MASK) != 0)
+ {
+ if (g_qspi0dev.xctn->idxnow < g_qspi0dev.xctn->datasize)
+ {
+ ((uint8_t *)g_qspi0dev.xctn->buffer)[g_qspi0dev.xctn->idxnow] =
+ *(volatile uint8_t *)datareg;
+ ++g_qspi0dev.xctn->idxnow;
+ }
+ else
+ {
+ /* No room at the inn */
+
+ break;
+ }
+ }
+ }
+
+ /* Use 'abort' to ditch any stray fifo contents and clear BUSY flag */
+
+ qspi_abort(&g_qspi0dev);
+
+ /* Set success status */
+
+ g_qspi0dev.xctn->disposition = OK;
+
+ /* Signal complete */
+
+ nxsem_post(&g_qspi0dev.op_sem);
+ }
+
+ /* Is it 'Status Match'? */
+
+ if ((status & QSPI_SR_SMF) && (cr & QSPI_CR_SMIE))
+ {
+ /* Acknowledge interrupt */
+
+ qspi_putreg(&g_qspi0dev, QSPI_FCR_CSMF, STM32_QUADSPI_FCR_OFFSET);
+
+ /* If 'automatic poll mode stop' is activated, we're done */
+
+ if (cr & QSPI_CR_APMS)
+ {
+ /* Disable the QSPI Transfer Error and Status Match Interrupts */
+
+ regval = qspi_getreg(&g_qspi0dev, STM32_QUADSPI_CR_OFFSET);
+ regval &= ~(QSPI_CR_TEIE | QSPI_CR_SMIE);
+ qspi_putreg(&g_qspi0dev, regval, STM32_QUADSPI_CR_OFFSET);
+
+ /* Set success status */
+
+ g_qspi0dev.xctn->disposition = OK;
+
+ /* Signal complete */
+
+ nxsem_post(&g_qspi0dev.op_sem);
+ }
+ else
+ {
+ /* XXX if it's NOT auto stop; something needs to happen here; a callback? */
+ }
+ }
+
+ /* Is it' Transfer Error'? :( */
+
+ if ((status & QSPI_SR_TEF) && (cr & QSPI_CR_TEIE))
+ {
+ /* Acknowledge interrupt */
+
+ qspi_putreg(&g_qspi0dev, QSPI_FCR_CTEF, STM32_QUADSPI_FCR_OFFSET);
+
+ /* Disable all the QSPI Interrupts */
+
+ regval = qspi_getreg(&g_qspi0dev, STM32_QUADSPI_CR_OFFSET);
+ regval &= ~(QSPI_CR_TEIE | QSPI_CR_TCIE | QSPI_CR_FTIE |
+ QSPI_CR_SMIE | QSPI_CR_TOIE);
+ qspi_putreg(&g_qspi0dev, regval, STM32_QUADSPI_CR_OFFSET);
+
+ /* 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;
+
+ /* Signal complete */
+
+ nxsem_post(&g_qspi0dev.op_sem);
+ }
+
+ /* Is it 'Timeout'? */
+
+ if ((status & QSPI_SR_TOF) && (cr & QSPI_CR_TOIE))
+ {
+ /* Acknowledge interrupt */
+
+ qspi_putreg(&g_qspi0dev, QSPI_FCR_CTOF, STM32_QUADSPI_FCR_OFFSET);
+
+ /* 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;
+}
+
+#elif defined(CONFIG_STM32H7_QSPI_DMA)
+/****************************************************************************
+ * 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.
+ *
+ ****************************************************************************/
+
+static void qspi_dma_timeout(int argc, uint32_t arg)
+{
+ struct stm32h7_qspidev_s *priv = (struct stm32h7_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 */
+
+ nxsem_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 stm32h7_qspidev_s *priv = (struct stm32h7_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_STREAM_TCIF_BIT)
+ {
+ priv->result = OK;
+ }
+ else if (isr & DMA_STREAM_TEIF_BIT)
+ {
+ priv->result = -EIO;
+ }
+ else
+ {
+ priv->result = OK;
+ }
+ }
+
+ /* Then wake up the waiting thread */
+
+ nxsem_post(&priv->dmawait);
+}
+
+/****************************************************************************
+ * Name: qspi_regaddr
+ *
+ * Description:
+ * Return the address of an QSPI register
+ *
+ ****************************************************************************/
+
+static inline uintptr_t qspi_regaddr(struct stm32h7_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 stm32h7_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_SCR_MSIZE_8BITS |
+ DMA_SCR_PSIZE_8BITS | DMA_SCR_MINC | DMA_SCR_DIR_M2P);
+
+ up_clean_dcache((uintptr_t)meminfo->buffer,
+ (uintptr_t)meminfo->buffer + meminfo->buflen);
+ }
+ else
+ {
+ /* Setup the DMA (peripheral-to-memory) */
+
+ dmaflags = (QSPI_DMA_PRIO | DMA_SCR_MSIZE_8BITS |
+ DMA_SCR_PSIZE_8BITS | DMA_SCR_MINC | DMA_SCR_DIR_P2M);
+ }
+
+ stm32_dmasetup(priv->dmach, qspi_regaddr(priv, STM32_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, STM32_QUADSPI_CR_OFFSET);
+ regval |= QSPI_CR_DMAEN;
+ qspi_putreg(priv, regval, STM32_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;
+ stm32_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 nxsem_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 < 0)
+ {
+ spierr("ERROR: wd_start failed: %d\n", ret);
+ }
+
+ /* Wait for the DMA complete */
+
+ ret = nxsem_wait(&priv->dmawait);
+
+ if (QSPIMEM_ISREAD(meminfo->flags))
+ {
+ up_invalidate_dcache((uintptr_t)meminfo->buffer,
+ (uintptr_t)meminfo->buffer + meminfo->buflen);
+ }
+
+ /* 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.
+ */
+
+ if (ret != -EINTR)
+ {
+ DEBUGPANIC();
+ regval = qspi_getreg(priv, STM32_QUADSPI_CR_OFFSET);
+ regval &= ~QSPI_CR_DMAEN;
+ qspi_putreg(priv, regval, STM32_QUADSPI_CR_OFFSET);
+ return ret;
+ }
+ }
+
+ /* 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).
+ */
+
+ stm32_dmastop(priv->dmach);
+
+ regval = qspi_getreg(priv, STM32_QUADSPI_CR_OFFSET);
+ regval &= ~QSPI_CR_DMAEN;
+ qspi_putreg(priv, regval, STM32_QUADSPI_CR_OFFSET);
+
+ /* Complain if the DMA fails */
+
+ if (priv->result)
+ {
+ spierr("ERROR: DMA failed with result: %d\n", priv->result);
+ }
+
+ return priv->result;
+}
+#endif
+
+#if !defined(CONFIG_STM32H7_QSPI_INTERRUPTS)
+/****************************************************************************
+ * Name: qspi_receive_blocking
+ *
+ * Description:
+ * Do common data receive in a blocking (status polling) way
+ *
+ * Input Parameters:
+ * priv - The QSPI controller to dump
+ * xctn - the transaction descriptor
+ *
+ * Returned Value:
+ * OK, or -errno on error
+ *
+ ****************************************************************************/
+
+static int qspi_receive_blocking(struct stm32h7_qspidev_s *priv,
+ struct qspi_xctnspec_s *xctn)
+{
+ int ret = OK;
+ volatile uint32_t *datareg =
+ (volatile uint32_t *)(priv->base + STM32_QUADSPI_DR_OFFSET);
+ uint8_t *dest = (uint8_t *)xctn->buffer;
+ uint32_t addrval;
+ uint32_t regval;
+
+ addrval = qspi_getreg(priv, STM32_QUADSPI_AR_OFFSET);
+ if (dest != NULL)
+ {
+ /* Counter of remaining data */
+
+ uint32_t remaining = xctn->datasize;
+
+ /* Ensure CCR register specifies indirect read */
+
+ regval = qspi_getreg(priv, STM32_QUADSPI_CCR_OFFSET);
+ regval &= ~QSPI_CCR_FMODE_MASK;
+ regval |= QSPI_CCR_FMODE(CCR_FMODE_INDRD);
+ qspi_putreg(priv, regval, STM32_QUADSPI_CCR_OFFSET);
+
+ /* Start the transfer by re-writing the address in AR register */
+
+ qspi_putreg(priv, addrval, STM32_QUADSPI_AR_OFFSET);
+
+ /* Transfer loop */
+
+ while (remaining > 0)
+ {
+ /* Wait for Fifo Threshold, or Transfer Complete, to read data */
+
+ qspi_waitstatusflags(priv, QSPI_SR_FTF | QSPI_SR_TCF, 1);
+
+ *dest = *(volatile uint8_t *)datareg;
+ dest++;
+ remaining--;
+ }
+
+ if (ret == OK)
+ {
+ /* Wait for transfer complete, then clear it */
+
+ qspi_waitstatusflags(priv, QSPI_SR_TCF, 1);
+ qspi_putreg(priv, QSPI_FCR_CTCF, STM32_QUADSPI_FCR_OFFSET);
+
+ /* Use Abort to clear the busy flag, and ditch any extra bytes in fifo */
+
+ qspi_abort(priv);
+ }
+ }
+ else
+ {
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: qspi_transmit_blocking
+ *
+ * Description:
+ * Do common data transmit in a blocking (status polling) way
+ *
+ * Input Parameters:
+ * priv - The QSPI controller to dump
+ * xctn - the transaction descriptor
+ *
+ * Returned Value:
+ * OK, or -errno on error
+ *
+ ****************************************************************************/
+
+static int qspi_transmit_blocking(struct stm32h7_qspidev_s *priv,
+ struct qspi_xctnspec_s *xctn)
+{
+ int ret = OK;
+ volatile uint32_t *datareg =
+ (volatile uint32_t *)(priv->base + STM32_QUADSPI_DR_OFFSET);
+ uint8_t *src = (uint8_t *)xctn->buffer;
+
+ if (src != NULL)
+ {
+ /* Counter of remaining data */
+
+ uint32_t remaining = xctn->datasize;
+
+ /* Transfer loop */
+
+ while (remaining > 0)
+ {
+ /* Wait for Fifo Threshold to write data */
+
+ qspi_waitstatusflags(priv, QSPI_SR_FTF, 1);
+
+ *(volatile uint8_t *)datareg = *src++;
+ remaining--;
+ }
+
+ if (ret == OK)
+ {
+ /* Wait for transfer complete, then clear it */
+
+ qspi_waitstatusflags(priv, QSPI_SR_TCF, 1);
+ qspi_putreg(priv, QSPI_FCR_CTCF, STM32_QUADSPI_FCR_OFFSET);
+
+ /* Use Abort to clear the Busy flag */
+
+ qspi_abort(priv);
+ }
+ }
+ else
+ {
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+#endif
+
+/****************************************************************************
+ * Name: qspi_lock
+ *
+ * Description:
+ * On QSPI buses where there are multiple devices, it will be necessary to
+ * lock QSPI to have exclusive access to the buses for a sequence of
+ * transfers. The bus should be locked before the chip is selected. After
+ * locking the QSPI bus, the caller should then also call the setfrequency,
+ * setbits, and setmode methods to make sure that the QSPI is properly
+ * configured for the device. If the QSPI bus is being shared, then it
+ * may have been left in an incompatible state.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * lock - true: Lock QSPI bus, false: unlock QSPI bus
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static int qspi_lock(struct qspi_dev_s *dev, bool lock)
+{
+ struct stm32h7_qspidev_s *priv = (struct stm32h7_qspidev_s *)dev;
+ int ret;
+
+ spiinfo("lock=%d\n", lock);
+ if (lock)
+ {
+ /* Take the semaphore (perhaps waiting) */
+
+ do
+ {
+ ret = nxsem_wait(&priv->exclsem);
+
+ /* The only case that an error should occur here is if the wait
+ * was awakened by a signal.
+ */
+
+ DEBUGASSERT(ret == OK || ret == -EINTR);
+ }
+ while (ret == -EINTR);
+ }
+ else
+ {
+ (void)nxsem_post(&priv->exclsem);
+ ret = OK;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: qspi_setfrequency
+ *
+ * Description:
+ * Set the QSPI frequency.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * frequency - The QSPI frequency requested
+ *
+ * Returned Value:
+ * Returns the actual frequency selected
+ *
+ ****************************************************************************/
+
+static uint32_t qspi_setfrequency(struct qspi_dev_s *dev, uint32_t frequency)
+{
+ struct stm32h7_qspidev_s *priv = (struct stm32h7_qspidev_s *)dev;
+ uint32_t actual;
+ 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;
+ }
+
+ spiinfo("frequency=%d\n", frequency);
+ DEBUGASSERT(priv);
+
+ /* Wait till BUSY flag reset */
+
+ qspi_abort(priv);
+ qspi_waitstatusflags(priv, QSPI_SR_BUSY, 0);
+
+ /* Check if the requested frequency is the same as the frequency selection */
+
+ if (priv->frequency == frequency)
+ {
+ /* We are already at this frequency. Return the actual. */
+
+ return priv->actual;
+ }
+
+ /* Configure QSPI to a frequency as close as possible to the requested
+ * frequency.
+ *
+ * QSCK frequency = STL32F7_QSPI_CLOCK / prescaler, or
+ * prescaler = STL32F7_QSPI_CLOCK / frequency
+ *
+ * Where prescaler can have the range 1 to 256 and the
+ * STM32_QUADSPI_CR_OFFSET register field holds prescaler - 1.
+ * NOTE that a "ceiling" type of calculation is performed.
+ * 'frequency' is treated as a not-to-exceed value.
+ */
+
+ prescaler = (frequency + STL32F7_QSPI_CLOCK - 1) / frequency;
+
+ /* Make sure that the divider is within range */
+
+ if (prescaler < 1)
+ {
+ prescaler = 1;
+ }
+ else if (prescaler > 256)
+ {
+ prescaler = 256;
+ }
+
+ /* Save the new prescaler value (minus one) */
+
+ regval = qspi_getreg(priv, STM32_QUADSPI_CR_OFFSET);
+ regval &= ~(QSPI_CR_PRESCALER_MASK);
+ regval |= (prescaler - 1) << QSPI_CR_PRESCALER_SHIFT;
+ qspi_putreg(priv, regval, STM32_QUADSPI_CR_OFFSET);
+
+ /* Calculate the new actual frequency */
+
+ actual = STL32F7_QSPI_CLOCK / prescaler;
+ spiinfo("prescaler=%d actual=%d\n", prescaler, actual);
+
+ /* Save the frequency setting */
+
+ priv->frequency = frequency;
+ priv->actual = actual;
+
+ spiinfo("Frequency %d->%d\n", frequency, actual);
+ return actual;
+}
+
+/****************************************************************************
+ * Name: qspi_setmode
+ *
+ * Description:
+ * Set the QSPI mode. Optional. See enum qspi_mode_e for mode definitions.
+ * NOTE: the STM32H7 QSPI supports only modes 0 and 3.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * mode - The QSPI mode requested
+ *
+ * Returned Value:
+ * none
+ *
+ ****************************************************************************/
+
+static void qspi_setmode(struct qspi_dev_s *dev, enum qspi_mode_e mode)
+{
+ struct stm32h7_qspidev_s *priv = (struct stm32h7_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;
+ }
+
+ spiinfo("mode=%d\n", mode);
+
+ /* Has the mode changed? */
+
+ if (mode != priv->mode)
+ {
+ /* Yes... Set the mode appropriately:
+ *
+ * QSPI CPOL CPHA
+ * MODE
+ * 0 0 0
+ * 1 0 1
+ * 2 1 0
+ * 3 1 1
+ */
+
+ regval = qspi_getreg(priv, STM32_QUADSPI_DCR_OFFSET);
+ regval &= ~(QSPI_DCR_CKMODE);
+
+ switch (mode)
+ {
+ case QSPIDEV_MODE0: /* CPOL=0; CPHA=0 */
+ break;
+
+ case QSPIDEV_MODE3: /* CPOL=1; CPHA=1 */
+ regval |= (QSPI_DCR_CKMODE);
+ break;
+
+ case QSPIDEV_MODE1: /* CPOL=0; CPHA=1 */
+ case QSPIDEV_MODE2: /* CPOL=1; CPHA=0 */
+ spiinfo("unsupported mode=%d\n", mode);
+ default:
+ DEBUGASSERT(FALSE);
+ return;
+ }
+
+ qspi_putreg(priv, regval, STM32_QUADSPI_DCR_OFFSET);
+ spiinfo("DCR=%08x\n", regval);
+
+ /* Save the mode so that subsequent re-configurations will be faster */
+
+ priv->mode = mode;
+ }
+}
+
+/****************************************************************************
+ * Name: qspi_setbits
+ *
+ * Description:
+ * Set the number if bits per word.
+ * NOTE: the STM32H7 QSPI only supports 8 bits, so this does nothing.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * nbits - The number of bits requests
+ *
+ * Returned Value:
+ * none
+ *
+ ****************************************************************************/
+
+static void qspi_setbits(struct qspi_dev_s *dev, int nbits)
+{
+ /* Not meaningful for the STM32H7x6 */
+
+ if (8 != nbits)
+ {
+ spiinfo("unsupported nbits=%d\n", nbits);
+ DEBUGASSERT(FALSE);
+ }
+}
+
+/****************************************************************************
+ * Name: qspi_command
+ *
+ * Description:
+ * Perform one QSPI data transfer
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * cmdinfo - Describes the command transfer to be performed.
+ *
+ * Returned Value:
+ * Zero (OK) on SUCCESS, a negated errno on value of failure
+ *
+ ****************************************************************************/
+
+static int qspi_command(struct qspi_dev_s *dev,
+ struct qspi_cmdinfo_s *cmdinfo)
+{
+ struct stm32h7_qspidev_s *priv = (struct stm32h7_qspidev_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);
+ if (OK != ret)
+ {
+ return ret;
+ }
+
+ /* Prepare for transaction */
+
+ /* Wait 'till non-busy */
+
+ qspi_abort(priv);
+ qspi_waitstatusflags(priv, QSPI_SR_BUSY, 0);
+
+ /* Clear flags */
+
+ qspi_putreg(priv,
+ QSPI_FCR_CTEF | QSPI_FCR_CTCF | QSPI_FCR_CSMF | QSPI_FCR_CTOF,
+ STM32_QUADSPI_FCR_OFFSET);
+
+#ifdef CONFIG_STM32H7_QSPI_INTERRUPTS
+ /* interrupt mode will need access to the transaction context */
+
+ priv->xctn = &xctn;
+
+ if (QSPICMD_ISDATA(cmdinfo->flags))
+ {
+ DEBUGASSERT(cmdinfo->buffer != NULL && cmdinfo->buflen > 0);
+ DEBUGASSERT(IS_ALIGNED(cmdinfo->buffer));
+
+ if (QSPICMD_ISWRITE(cmdinfo->flags))
+ {
+ uint32_t regval;
+
+ /* Set up the Communications Configuration Register as per command
+ * info
+ */
+
+ qspi_ccrconfig(priv, &xctn, CCR_FMODE_INDWR);
+
+ /* Enable 'Transfer Error' 'FIFO Threshhold' and 'Transfer Complete'
+ * interrupts.
+ */
+
+ regval = qspi_getreg(priv, STM32_QUADSPI_CR_OFFSET);
+ regval |= (QSPI_CR_TEIE | QSPI_CR_FTIE | QSPI_CR_TCIE);
+ qspi_putreg(priv, regval, STM32_QUADSPI_CR_OFFSET);
+ }
+ else
+ {
+ uint32_t regval;
+ uint32_t addrval;
+
+ addrval = qspi_getreg(priv, STM32_QUADSPI_AR_OFFSET);
+
+ /* Set up the Communications Configuration Register as per command
+ * info
+ */
+
+ qspi_ccrconfig(priv, &xctn, CCR_FMODE_INDRD);
+
+ /* Start the transfer by re-writing the address in AR register */
+
+ qspi_putreg(priv, addrval, STM32_QUADSPI_AR_OFFSET);
+
+ /* Enable 'Transfer Error' 'FIFO Threshhold' and 'Transfer Complete'
+ * interrupts
+ */
+
+ regval = qspi_getreg(priv, STM32_QUADSPI_CR_OFFSET);
+ regval |= (QSPI_CR_TEIE | QSPI_CR_FTIE | QSPI_CR_TCIE);
+ qspi_putreg(priv, regval, STM32_QUADSPI_CR_OFFSET);
+ }
+ }
+ else
+ {
+ uint32_t regval;
+
+ /* We have no data phase, the command will execute as soon as we emit
+ * the CCR
+ */
+
+ /* Enable 'Transfer Error' and 'Transfer Complete' interrupts */
+
+ regval = qspi_getreg(priv, STM32_QUADSPI_CR_OFFSET);
+ regval |= (QSPI_CR_TEIE | QSPI_CR_TCIE);
+ qspi_putreg(priv, regval, STM32_QUADSPI_CR_OFFSET);
+
+ /* Set up the Communications Configuration Register as per command
+ * info
+ */
+
+ qspi_ccrconfig(priv, &xctn, CCR_FMODE_INDRD);
+ }
+
+ /* Wait for the interrupt routine to finish it's magic */
+
+ (void)nxsem_wait(&priv->op_sem);
+ MEMORY_SYNC();
+
+ /* Convey the result */
+
+ ret = xctn.disposition;
+
+ /* because command transfers are so small, we're not going to use
+ * DMA for them, only interrupts or polling
+ */
+
+#else
+ /* Polling mode */
+
+ /* Set up the Communications Configuration Register as per command info */
+
+ qspi_ccrconfig(priv, &xctn, CCR_FMODE_INDWR);
+
+ /* That may be it, unless there is also data to transfer */
+
+ if (QSPICMD_ISDATA(cmdinfo->flags))
+ {
+ DEBUGASSERT(cmdinfo->buffer != NULL && cmdinfo->buflen > 0);
+ DEBUGASSERT(IS_ALIGNED(cmdinfo->buffer));
+
+ if (QSPICMD_ISWRITE(cmdinfo->flags))
+ {
+ ret = qspi_transmit_blocking(priv, &xctn);
+ }
+ else
+ {
+ ret = qspi_receive_blocking(priv, &xctn);
+ }
+
+ MEMORY_SYNC();
+ }
+ else
+ {
+ ret = OK;
+ }
+
+ /* Wait for Transfer complete, and not busy */
+
+ qspi_waitstatusflags(priv, QSPI_SR_TCF, 1);
+ qspi_waitstatusflags(priv, QSPI_SR_BUSY, 0);
+
+#endif
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: qspi_memory
+ *
+ * Description:
+ * Perform one QSPI memory transfer
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * meminfo - Describes the memory transfer to be performed.
+ *
+ * Returned Value:
+ * Zero (OK) on SUCCESS, a negated errno on value of failure
+ *
+ ****************************************************************************/
+
+static int qspi_memory(struct qspi_dev_s *dev,
+ struct qspi_meminfo_s *meminfo)
+{
+ struct stm32h7_qspidev_s *priv = (struct stm32h7_qspidev_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);
+ if (OK != ret)
+ {
+ return ret;
+ }
+
+ /* Prepare for transaction */
+
+ /* Wait 'till non-busy */
+
+ qspi_abort(priv);
+ qspi_waitstatusflags(priv, QSPI_SR_BUSY, 0);
+
+ /* Clear flags */
+
+ qspi_putreg(priv,
+ QSPI_FCR_CTEF | QSPI_FCR_CTCF | QSPI_FCR_CSMF | QSPI_FCR_CTOF,
+ STM32_QUADSPI_FCR_OFFSET);
+
+#ifdef CONFIG_STM32H7_QSPI_INTERRUPTS
+ /* interrupt mode will need access to the transaction context */
+
+ priv->xctn = &xctn;
+
+ DEBUGASSERT(meminfo->buffer != NULL && meminfo->buflen > 0);
+ DEBUGASSERT(IS_ALIGNED(meminfo->buffer));
+
+ if (QSPIMEM_ISWRITE(meminfo->flags))
+ {
+ uint32_t regval;
+
+ /* Set up the Communications Configuration Register as per command
+ * info
+ */
+
+ qspi_ccrconfig(priv, &xctn, CCR_FMODE_INDWR);
+
+ /* Enable 'Transfer Error' 'FIFO Threshhold' and 'Transfer Complete'
+ * interrupts
+ */
+
+ regval = qspi_getreg(priv, STM32_QUADSPI_CR_OFFSET);
+ regval |= (QSPI_CR_TEIE | QSPI_CR_FTIE | QSPI_CR_TCIE);
+ qspi_putreg(priv, regval, STM32_QUADSPI_CR_OFFSET);
+ }
+ else
+ {
+ uint32_t regval;
+ uint32_t addrval;
+
+ addrval = qspi_getreg(priv, STM32_QUADSPI_AR_OFFSET);
+
+ /* Set up the Communications Configuration Register as per command
+ * info
+ */
+
+ qspi_ccrconfig(priv, &xctn, CCR_FMODE_INDRD);
+
+ /* Start the transfer by re-writing the address in AR register */
+
+ qspi_putreg(priv, addrval, STM32_QUADSPI_AR_OFFSET);
+
+ /* Enable 'Transfer Error' 'FIFO Threshhold' and 'Transfer Complete'
+ * interrupts
+ */
+
+ regval = qspi_getreg(priv, STM32_QUADSPI_CR_OFFSET);
+ regval |= (QSPI_CR_TEIE | QSPI_CR_FTIE | QSPI_CR_TCIE);
+ qspi_putreg(priv, regval, STM32_QUADSPI_CR_OFFSET);
+ }
+
+ /* Wait for the interrupt routine to finish it's magic */
+
+ (void)nxsem_wait(&priv->op_sem);
+ MEMORY_SYNC();
+
+ /* convey the result */
+
+ ret = xctn.disposition;
+
+#elif defined(CONFIG_STM32H7_QSPI_DMA)
+ /* Can we perform DMA? Should we perform DMA? */
+
+ if (priv->candma &&
+ meminfo->buflen > CONFIG_STM32H7_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 */
+
+ /* 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();
+
+#endif
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: qspi_alloc
+ *
+ * Description:
+ * Allocate a buffer suitable for DMA data transfer
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * buflen - Buffer length to allocate in bytes
+ *
+ * Returned Value:
+ * Address of tha allocated memory on success; NULL is returned on any
+ * failure.
+ *
+ ****************************************************************************/
+
+static FAR void *qspi_alloc(FAR struct qspi_dev_s *dev, size_t buflen)
+{
+ /* Here we exploit the carnal knowledge the kmm_malloc() will return memory
+ * aligned to 64-bit addresses. The buffer length must be large enough to
+ * hold the rested buflen in units a 32-bits.
+ */
+
+ return kmm_malloc(ALIGN_UP(buflen));
+}
+
+/****************************************************************************
+ * Name: QSPI_FREE
+ *
+ * Description:
+ * Free memory returned by QSPI_ALLOC
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * buffer - Buffer previously allocated via QSPI_ALLOC
+ *
+ * Returned Value:
+ * None.
+ *
+ ****************************************************************************/
+
+static void qspi_free(FAR struct qspi_dev_s *dev, FAR void *buffer)
+{
+ if (buffer)
+ {
+ kmm_free(buffer);
+ }
+}
+
+/****************************************************************************
+ * Name: qspi_hw_initialize
+ *
+ * Description:
+ * Initialize the QSPI peripheral from hardware reset.
+ *
+ * Input Parameters:
+ * priv - Device state structure.
+ *
+ * Returned Value:
+ * Zero (OK) on SUCCESS, a negated errno on value of failure
+ *
+ ****************************************************************************/
+
+static int qspi_hw_initialize(struct stm32h7_qspidev_s *priv)
+{
+ uint32_t regval;
+
+ /* Disable the QSPI; abort anything happening, disable, wait for not busy */
+
+ qspi_abort(priv);
+
+ regval = 0;
+ regval &= ~(QSPI_CR_EN);
+ qspi_putreg(priv, regval, STM32_QUADSPI_CR_OFFSET);
+
+ /* Wait till BUSY flag reset */
+
+ qspi_waitstatusflags(priv, QSPI_SR_BUSY, 0);
+
+ /* Disable all interrupt sources for starters */
+
+ regval = qspi_getreg(priv, STM32_QUADSPI_CR_OFFSET);
+ regval &= ~(QSPI_CR_TEIE | QSPI_CR_TCIE | QSPI_CR_FTIE | QSPI_CR_SMIE |
+ QSPI_CR_TOIE | QSPI_CR_FSEL | QSPI_CR_DFM);
+
+#if defined(CONFIG_STM32H7_QSPI_MODE_BANK2)
+ regval |= QSPI_CR_FSEL;
+#endif
+
+#if defined(CONFIG_STM32H7_QSPI_MODE_DUAL)
+ regval |= QSPI_CR_DFM;
+#endif
+
+ /* Configure QSPI FIFO Threshold */
+
+ regval &= ~(QSPI_CR_FTHRES_MASK);
+ regval |= ((CONFIG_STM32H7_QSPI_FIFO_THESHOLD - 1) << QSPI_CR_FTHRES_SHIFT);
+ qspi_putreg(priv, regval, STM32_QUADSPI_CR_OFFSET);
+
+ /* Wait till BUSY flag reset */
+
+ qspi_waitstatusflags(priv, QSPI_SR_BUSY, 0);
+
+ /* Configure QSPI Clock Prescaler and Sample Shift */
+
+ regval = qspi_getreg(priv, STM32_QUADSPI_CR_OFFSET);
+ regval &= ~(QSPI_CR_PRESCALER_MASK | QSPI_CR_SSHIFT);
+ regval |= (0x01 << QSPI_CR_PRESCALER_SHIFT);
+ regval |= (0x00);
+ qspi_putreg(priv, regval, STM32_QUADSPI_CR_OFFSET);
+
+ /* Configure QSPI Flash Size, CS High Time and Clock Mode */
+
+ regval = qspi_getreg(priv, STM32_QUADSPI_DCR_OFFSET);
+ regval &= ~(QSPI_DCR_CKMODE | QSPI_DCR_CSHT_MASK | QSPI_DCR_FSIZE_MASK);
+ regval |= (0x00);
+ regval |= ((CONFIG_STM32H7_QSPI_CSHT - 1) << QSPI_DCR_CSHT_SHIFT);
+ if (0 != CONFIG_STM32H7_QSPI_FLASH_SIZE)
+ {
+ unsigned int nsize = CONFIG_STM32H7_QSPI_FLASH_SIZE;
+ int nlog2size = 31;
+
+ while ((nsize & 0x80000000) == 0)
+ {
+ --nlog2size;
+ nsize <<= 1;
+ }
+
+ regval |= ((nlog2size - 1) << QSPI_DCR_FSIZE_SHIFT);
+ }
+
+ qspi_putreg(priv, regval, STM32_QUADSPI_DCR_OFFSET);
+
+ /* Enable QSPI */
+
+ regval = qspi_getreg(priv, STM32_QUADSPI_CR_OFFSET);
+ regval |= QSPI_CR_EN;
+ qspi_putreg(priv, regval, STM32_QUADSPI_CR_OFFSET);
+
+ /* Wait till BUSY flag reset */
+
+ qspi_waitstatusflags(priv, QSPI_SR_BUSY, 0);
+
+ qspi_dumpregs(priv, "After initialization");
+ qspi_dumpgpioconfig("GPIO");
+
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32h7_qspi_initialize
+ *
+ * Description:
+ * Initialize the selected QSPI port in master mode
+ *
+ * Input Parameters:
+ * intf - Interface number(must be zero)
+ *
+ * Returned Value:
+ * Valid QSPI device structure reference on success; a NULL on failure
+ *
+ ****************************************************************************/
+
+struct qspi_dev_s *stm32h7_qspi_initialize(int intf)
+{
+ struct stm32h7_qspidev_s *priv;
+ uint32_t regval;
+ int ret;
+
+ /* The STM32H7 has only a single QSPI port */
+
+ spiinfo("intf: %d\n", intf);
+ DEBUGASSERT(intf == 0);
+
+ /* Select the QSPI interface */
+
+ if (intf == 0)
+ {
+ /* If this function is called multiple times, the following operations
+ * will be performed multiple times.
+ */
+
+ /* Select QSPI0 */
+
+ priv = &g_qspi0dev;
+
+ /* Enable clocking to the QSPI peripheral */
+
+ regval = getreg32(STM32_RCC_AHB3ENR);
+ regval |= RCC_AHB3ENR_QSPIEN;
+ putreg32(regval, STM32_RCC_AHB3ENR);
+
+ /* Reset the QSPI peripheral */
+
+ regval = getreg32(STM32_RCC_AHB3RSTR);
+ regval |= RCC_AHB3RSTR_QSPIRST;
+ putreg32(regval, STM32_RCC_AHB3RSTR);
+ regval &= ~RCC_AHB3RSTR_QSPIRST;
+ putreg32(regval, STM32_RCC_AHB3RSTR);
+
+ /* Configure multiplexed pins as connected on the board. */
+
+ stm32_configgpio(GPIO_QSPI_CS);
+ stm32_configgpio(GPIO_QSPI_IO0);
+ stm32_configgpio(GPIO_QSPI_IO1);
+ stm32_configgpio(GPIO_QSPI_IO2);
+ stm32_configgpio(GPIO_QSPI_IO3);
+ stm32_configgpio(GPIO_QSPI_SCK);
+ }
+ else
+ {
+ spierr("ERROR: QSPI%d not supported\n", intf);
+ return NULL;
+ }
+
+ /* Has the QSPI hardware been initialized? */
+
+ if (!priv->initialized)
+ {
+ /* Now perform one time initialization.
+ *
+ * Initialize the QSPI semaphore that enforces mutually exclusive
+ * access to the QSPI registers.
+ */
+
+ nxsem_init(&priv->exclsem, 0, 1);
+
+#ifdef CONFIG_STM32H7_QSPI_DMA
+ /* Pre-allocate DMA channels. */
+
+ if (priv->candma)
+ {
+ priv->dmach = stm32_dmachannel(DMACHAN_QUADSPI);
+ if (!priv->dmach)
+ {
+ spierr("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. This semaphore is used for
+ * signaling and, hence, should not have priority inheritance enabled.
+ */
+
+ nxsem_init(&priv->dmawait, 0, 0);
+ nxsem_setprotocol(&priv->dmawait, SEM_PRIO_NONE);
+
+ /* Create a watchdog time to catch DMA timeouts */
+
+ priv->dmadog = wd_create();
+ if (priv->dmadog == NULL)
+ {
+ spierr("ERROR: Failed to create wdog\n");
+ goto errout_with_dmahandles;
+ }
+#endif
+
+#ifdef CONFIG_STM32H7_QSPI_INTERRUPTS
+ /* Attach the interrupt handler */
+
+ ret = irq_attach(priv->irq, priv->handler, NULL);
+ if (ret < 0)
+ {
+ spierr("ERROR: Failed to attach irq %d\n", priv->irq);
+ goto errout_with_dmadog;
+ }
+
+ /* Initialize the semaphore that blocks until the operation completes.
+ * This semaphore is used for signaling and, hence, should not have
+ * priority inheritance enabled.
+ */
+
+ nxsem_init(&priv->op_sem, 0, 0);
+ nxsem_setprotocol(&priv->op_sem, SEM_PRIO_NONE);
+#endif
+
+ /* Perform hardware initialization. Puts the QSPI into an active
+ * state.
+ */
+
+ ret = qspi_hw_initialize(priv);
+ if (ret < 0)
+ {
+ spierr("ERROR: Failed to initialize QSPI hardware\n");
+ goto errout_with_irq;
+ }
+
+ /* Enable interrupts at the NVIC */
+
+ priv->initialized = true;
+ priv->memmap = false;
+#ifdef CONFIG_STM32H7_QSPI_INTERRUPTS
+ up_enable_irq(priv->irq);
+#endif
+ }
+
+ return &priv->qspi;
+
+errout_with_irq:
+#ifdef CONFIG_STM32H7_QSPI_INTERRUPTS
+ irq_detach(priv->irq);
+
+errout_with_dmadog:
+#endif
+#ifdef CONFIG_STM32H7_QSPI_DMA
+ wd_delete(priv->dmadog);
+
+errout_with_dmahandles:
+ nxsem_destroy(&priv->dmawait);
+
+ if (priv->dmach)
+ {
+ stm32_dmafree(priv->dmach);
+ priv->dmach = NULL;
+ }
+#endif
+
+ nxsem_destroy(&priv->exclsem);
+ return NULL;
+}
+
+/****************************************************************************
+ * Name: stm32h7_qspi_enter_memorymapped
+ *
+ * Description:
+ * Put the QSPI device into memory mapped mode
+ *
+ * Input Parameters:
+ * dev - QSPI device
+ * meminfo - parameters like for a memory transfer used for reading
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+void stm32h7_qspi_enter_memorymapped(struct qspi_dev_s *dev,
+ const struct qspi_meminfo_s *meminfo,
+ uint32_t lpto)
+{
+ struct stm32h7_qspidev_s *priv = (struct stm32h7_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, STM32_QUADSPI_LPTR_OFFSET);
+
+ /* Clear Timeout interrupt */
+
+ qspi_putreg(&g_qspi0dev, QSPI_FCR_CTOF, STM32_QUADSPI_FCR_OFFSET);
+
+#ifdef CONFIG_STM32H7_QSPI_INTERRUPTS
+ /* Enable Timeout interrupt */
+
+ regval = qspi_getreg(priv, STM32_QUADSPI_CR_OFFSET);
+ regval |= (QSPI_CR_TCEN | QSPI_CR_TOIE);
+ qspi_putreg(priv, regval, STM32_QUADSPI_CR_OFFSET);
+#endif
+ }
+ else
+ {
+ regval = qspi_getreg(priv, STM32_QUADSPI_CR_OFFSET);
+ regval &= ~QSPI_CR_TCEN;
+ qspi_putreg(priv, regval, STM32_QUADSPI_CR_OFFSET);
+ }
+
+ /* create a transaction object */
+
+ qspi_setupxctnfrommem(&xctn, meminfo);
+
+#ifdef CONFIG_STM32H7_QSPI_INTERRUPTS
+ priv->xctn = NULL;
+#endif
+
+ /* 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: stm32h7_qspi_exit_memorymapped
+ *
+ * Description:
+ * Take the QSPI device out of memory mapped mode
+ *
+ * Input Parameters:
+ * dev - QSPI device
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+void stm32h7_qspi_exit_memorymapped(struct qspi_dev_s *dev)
+{
+ struct stm32h7_qspidev_s *priv = (struct stm32h7_qspidev_s *)dev;
+
+ qspi_lock(dev, true);
+
+ /* A simple abort is sufficient */
+
+ qspi_abort(priv);
+ priv->memmap = false;
+
+ qspi_lock(dev, false);
+}
+
+#endif /* CONFIG_STM32H7_QSPI */
diff --git a/arch/arm/src/stm32h7/stm32_qspi.h b/arch/arm/src/stm32h7/stm32_qspi.h
new file mode 100644
index 0000000..bbb774c
--- /dev/null
+++ b/arch/arm/src/stm32h7/stm32_qspi.h
@@ -0,0 +1,144 @@
+/****************************************************************************
+ * arch/arm/src/stm32h7/stm32_qspi.h
+ *
+ * Copyright (C) 2016, 2019 Gregory Nutt. All rights reserved.
+ * Author: dev@ziggurat29.com
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32_STM32H7_QSPI_H
+#define __ARCH_ARM_SRC_STM32_STM32H7_QSPI_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/spi/qspi.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "chip.h"
+
+#ifdef CONFIG_STM32H7_QUADSPI
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Inline Functions
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32l4_qspi_initialize
+ *
+ * Description:
+ * Initialize the selected QSPI port in master mode
+ *
+ * Input Parameters:
+ * intf - Interface number(must be zero)
+ *
+ * Returned Value:
+ * Valid SPI device structure reference on success; a NULL on failure
+ *
+ ****************************************************************************/
+
+struct qspi_dev_s;
+FAR struct qspi_dev_s *stm32h7_qspi_initialize(int intf);
+
+/****************************************************************************
+ * Name: stm32l4_qspi_enter_memorymapped
+ *
+ * Description:
+ * Put the QSPI device into memory mapped mode
+ *
+ * Input Parameters:
+ * 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 stm32h7_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 Parameters:
+ * dev - QSPI device
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+void stm32h7_qspi_exit_memorymapped(struct qspi_dev_s *dev);
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __ASSEMBLY__ */
+#endif /* CONFIG_STM32H7_QSPI */
+#endif /* __ARCH_ARM_SRC_STM32_STM32H7_QSPI_H */