This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit febf61119826cdd0d93ffcc5f72c17f793877f39 Author: raiden00pl <[email protected]> AuthorDate: Fri Aug 21 09:02:12 2026 +0200 arch/arm/stm32h7: add XSPI driver Add a driver for the STM32H7R/S XSPI controller. The driver implements the NuttX QSPI interface and supports the memory-mapped mode used to execute code from the external flash. Signed-off-by: raiden00pl <[email protected]> Assisted-by: OpenAI Codex:gpt-5 --- arch/arm/src/common/stm32/Kconfig.have | 3 + arch/arm/src/stm32h7/CMakeLists.txt | 4 + arch/arm/src/stm32h7/Kconfig | 7 + arch/arm/src/stm32h7/Make.defs | 4 + arch/arm/src/stm32h7/hardware/stm32h7rsxx_xspi.h | 406 +++++++ arch/arm/src/stm32h7/stm32_xspi.c | 1230 ++++++++++++++++++++++ arch/arm/src/stm32h7/stm32_xspi.h | 163 +++ 7 files changed, 1817 insertions(+) diff --git a/arch/arm/src/common/stm32/Kconfig.have b/arch/arm/src/common/stm32/Kconfig.have index 9ede5dc2247..2eb65ed31e4 100644 --- a/arch/arm/src/common/stm32/Kconfig.have +++ b/arch/arm/src/common/stm32/Kconfig.have @@ -639,6 +639,9 @@ config STM32_HAVE_OCTOSPI2 config STM32_HAVE_QSPI bool +config STM32_HAVE_XSPI + bool + # CRC capabilities config STM32_HAVE_CRC diff --git a/arch/arm/src/stm32h7/CMakeLists.txt b/arch/arm/src/stm32h7/CMakeLists.txt index 79651690179..ecc0880d253 100644 --- a/arch/arm/src/stm32h7/CMakeLists.txt +++ b/arch/arm/src/stm32h7/CMakeLists.txt @@ -116,6 +116,10 @@ if(CONFIG_STM32_QSPI) list(APPEND SRCS stm32_qspi.c) endif() +if(CONFIG_STM32_XSPI) + list(APPEND SRCS stm32_xspi.c) +endif() + if(CONFIG_STM32_RTC) list(APPEND SRCS stm32_rtc.c) if(CONFIG_RTC_ALARM) diff --git a/arch/arm/src/stm32h7/Kconfig b/arch/arm/src/stm32h7/Kconfig index 70112131708..d114eceb2b5 100644 --- a/arch/arm/src/stm32h7/Kconfig +++ b/arch/arm/src/stm32h7/Kconfig @@ -804,6 +804,13 @@ config STM32_BYPASS_CLOCKCONFIG Bypass clock configuration. For dual core chips only one core should configure clocks +config STM32_XSPI + bool "XSPI support" + depends on STM32_HAVE_XSPI + default n + ---help--- + Enable support for the STM32H7R/S XSPI controller. + config STM32_SRAM4EXCLUDE bool "Exclude SRAM4 from the heap" default y if RPTUN diff --git a/arch/arm/src/stm32h7/Make.defs b/arch/arm/src/stm32h7/Make.defs index bb3817171c7..abcb35307f0 100644 --- a/arch/arm/src/stm32h7/Make.defs +++ b/arch/arm/src/stm32h7/Make.defs @@ -119,6 +119,10 @@ ifeq ($(CONFIG_STM32_QSPI),y) CHIP_CSRCS += stm32_qspi.c endif +ifeq ($(CONFIG_STM32_XSPI),y) +CHIP_CSRCS += stm32_xspi.c +endif + ifeq ($(CONFIG_STM32_RTC),y) CHIP_CSRCS += stm32_rtc.c ifeq ($(CONFIG_RTC_ALARM),y) diff --git a/arch/arm/src/stm32h7/hardware/stm32h7rsxx_xspi.h b/arch/arm/src/stm32h7/hardware/stm32h7rsxx_xspi.h new file mode 100644 index 00000000000..fd136678a5f --- /dev/null +++ b/arch/arm/src/stm32h7/hardware/stm32h7rsxx_xspi.h @@ -0,0 +1,406 @@ +/**************************************************************************** + * arch/arm/src/stm32h7/hardware/stm32h7rsxx_xspi.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32H7_HARDWARE_STM32H7RSXX_XSPI_H +#define __ARCH_ARM_SRC_STM32H7_HARDWARE_STM32H7RSXX_XSPI_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "stm32_memorymap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Register Offsets *********************************************************/ + +#define STM32_XSPI_CR_OFFSET 0x0000 /* Control register */ +#define STM32_XSPI_DCR1_OFFSET 0x0008 /* Device configuration 1 */ +#define STM32_XSPI_DCR2_OFFSET 0x000c /* Device configuration 2 */ +#define STM32_XSPI_DCR3_OFFSET 0x0010 /* Device configuration 3 */ +#define STM32_XSPI_DCR4_OFFSET 0x0014 /* Device configuration 4 */ +#define STM32_XSPI_SR_OFFSET 0x0020 /* Status register */ +#define STM32_XSPI_FCR_OFFSET 0x0024 /* Flag clear register */ +#define STM32_XSPI_DLR_OFFSET 0x0040 /* Data length register */ +#define STM32_XSPI_AR_OFFSET 0x0048 /* Address register */ +#define STM32_XSPI_DR_OFFSET 0x0050 /* Data register */ +#define STM32_XSPI_PSMKR_OFFSET 0x0080 /* Polling status mask */ +#define STM32_XSPI_PSMAR_OFFSET 0x0088 /* Polling status match */ +#define STM32_XSPI_PIR_OFFSET 0x0090 /* Polling interval */ +#define STM32_XSPI_CCR_OFFSET 0x0100 /* Communication configuration */ +#define STM32_XSPI_TCR_OFFSET 0x0108 /* Timing configuration */ +#define STM32_XSPI_IR_OFFSET 0x0110 /* Instruction register */ +#define STM32_XSPI_ABR_OFFSET 0x0120 /* Alternate bytes register */ +#define STM32_XSPI_LPTR_OFFSET 0x0130 /* Low-power timeout */ +#define STM32_XSPI_WPCCR_OFFSET 0x0140 /* Wrap communication config */ +#define STM32_XSPI_WPTCR_OFFSET 0x0148 /* Wrap timing config */ +#define STM32_XSPI_WPIR_OFFSET 0x0150 /* Wrap instruction */ +#define STM32_XSPI_WPABR_OFFSET 0x0160 /* Wrap alternate bytes */ +#define STM32_XSPI_WCCR_OFFSET 0x0180 /* Write communication config */ +#define STM32_XSPI_WTCR_OFFSET 0x0188 /* Write timing config */ +#define STM32_XSPI_WIR_OFFSET 0x0190 /* Write instruction */ +#define STM32_XSPI_WABR_OFFSET 0x01a0 /* Write alternate bytes */ +#define STM32_XSPI_HLCR_OFFSET 0x0200 /* HyperBus latency config */ +#define STM32_XSPI_CALFCR_OFFSET 0x0210 /* Full-cycle calibration */ +#define STM32_XSPI_CALMR_OFFSET 0x0218 /* Master calibration */ +#define STM32_XSPI_CALSOR_OFFSET 0x0220 /* Output calibration */ +#define STM32_XSPI_CALSIR_OFFSET 0x0228 /* Input calibration */ + +#define STM32_XSPIM_CR_OFFSET 0x0000 /* XSPIM control */ + +/* Register Addresses *******************************************************/ + +#define STM32_XSPI1_CR (STM32_XSPI1_R_BASE + STM32_XSPI_CR_OFFSET) +#define STM32_XSPI1_DCR1 (STM32_XSPI1_R_BASE + STM32_XSPI_DCR1_OFFSET) +#define STM32_XSPI1_DCR2 (STM32_XSPI1_R_BASE + STM32_XSPI_DCR2_OFFSET) +#define STM32_XSPI1_DCR3 (STM32_XSPI1_R_BASE + STM32_XSPI_DCR3_OFFSET) +#define STM32_XSPI1_DCR4 (STM32_XSPI1_R_BASE + STM32_XSPI_DCR4_OFFSET) +#define STM32_XSPI1_SR (STM32_XSPI1_R_BASE + STM32_XSPI_SR_OFFSET) +#define STM32_XSPI1_FCR (STM32_XSPI1_R_BASE + STM32_XSPI_FCR_OFFSET) +#define STM32_XSPI1_DLR (STM32_XSPI1_R_BASE + STM32_XSPI_DLR_OFFSET) +#define STM32_XSPI1_AR (STM32_XSPI1_R_BASE + STM32_XSPI_AR_OFFSET) +#define STM32_XSPI1_DR (STM32_XSPI1_R_BASE + STM32_XSPI_DR_OFFSET) +#define STM32_XSPI1_PSMKR (STM32_XSPI1_R_BASE + STM32_XSPI_PSMKR_OFFSET) +#define STM32_XSPI1_PSMAR (STM32_XSPI1_R_BASE + STM32_XSPI_PSMAR_OFFSET) +#define STM32_XSPI1_PIR (STM32_XSPI1_R_BASE + STM32_XSPI_PIR_OFFSET) +#define STM32_XSPI1_CCR (STM32_XSPI1_R_BASE + STM32_XSPI_CCR_OFFSET) +#define STM32_XSPI1_TCR (STM32_XSPI1_R_BASE + STM32_XSPI_TCR_OFFSET) +#define STM32_XSPI1_IR (STM32_XSPI1_R_BASE + STM32_XSPI_IR_OFFSET) +#define STM32_XSPI1_ABR (STM32_XSPI1_R_BASE + STM32_XSPI_ABR_OFFSET) +#define STM32_XSPI1_LPTR (STM32_XSPI1_R_BASE + STM32_XSPI_LPTR_OFFSET) +#define STM32_XSPI1_WPCCR (STM32_XSPI1_R_BASE + STM32_XSPI_WPCCR_OFFSET) +#define STM32_XSPI1_WPTCR (STM32_XSPI1_R_BASE + STM32_XSPI_WPTCR_OFFSET) +#define STM32_XSPI1_WPIR (STM32_XSPI1_R_BASE + STM32_XSPI_WPIR_OFFSET) +#define STM32_XSPI1_WPABR (STM32_XSPI1_R_BASE + STM32_XSPI_WPABR_OFFSET) +#define STM32_XSPI1_WCCR (STM32_XSPI1_R_BASE + STM32_XSPI_WCCR_OFFSET) +#define STM32_XSPI1_WTCR (STM32_XSPI1_R_BASE + STM32_XSPI_WTCR_OFFSET) +#define STM32_XSPI1_WIR (STM32_XSPI1_R_BASE + STM32_XSPI_WIR_OFFSET) +#define STM32_XSPI1_WABR (STM32_XSPI1_R_BASE + STM32_XSPI_WABR_OFFSET) +#define STM32_XSPI1_HLCR (STM32_XSPI1_R_BASE + STM32_XSPI_HLCR_OFFSET) +#define STM32_XSPI1_CALFCR (STM32_XSPI1_R_BASE + STM32_XSPI_CALFCR_OFFSET) +#define STM32_XSPI1_CALMR (STM32_XSPI1_R_BASE + STM32_XSPI_CALMR_OFFSET) +#define STM32_XSPI1_CALSOR (STM32_XSPI1_R_BASE + STM32_XSPI_CALSOR_OFFSET) +#define STM32_XSPI1_CALSIR (STM32_XSPI1_R_BASE + STM32_XSPI_CALSIR_OFFSET) + +#define STM32_XSPI2_CR (STM32_XSPI2_R_BASE + STM32_XSPI_CR_OFFSET) +#define STM32_XSPI2_DCR1 (STM32_XSPI2_R_BASE + STM32_XSPI_DCR1_OFFSET) +#define STM32_XSPI2_DCR2 (STM32_XSPI2_R_BASE + STM32_XSPI_DCR2_OFFSET) +#define STM32_XSPI2_DCR3 (STM32_XSPI2_R_BASE + STM32_XSPI_DCR3_OFFSET) +#define STM32_XSPI2_DCR4 (STM32_XSPI2_R_BASE + STM32_XSPI_DCR4_OFFSET) +#define STM32_XSPI2_SR (STM32_XSPI2_R_BASE + STM32_XSPI_SR_OFFSET) +#define STM32_XSPI2_FCR (STM32_XSPI2_R_BASE + STM32_XSPI_FCR_OFFSET) +#define STM32_XSPI2_DLR (STM32_XSPI2_R_BASE + STM32_XSPI_DLR_OFFSET) +#define STM32_XSPI2_AR (STM32_XSPI2_R_BASE + STM32_XSPI_AR_OFFSET) +#define STM32_XSPI2_DR (STM32_XSPI2_R_BASE + STM32_XSPI_DR_OFFSET) +#define STM32_XSPI2_PSMKR (STM32_XSPI2_R_BASE + STM32_XSPI_PSMKR_OFFSET) +#define STM32_XSPI2_PSMAR (STM32_XSPI2_R_BASE + STM32_XSPI_PSMAR_OFFSET) +#define STM32_XSPI2_PIR (STM32_XSPI2_R_BASE + STM32_XSPI_PIR_OFFSET) +#define STM32_XSPI2_CCR (STM32_XSPI2_R_BASE + STM32_XSPI_CCR_OFFSET) +#define STM32_XSPI2_TCR (STM32_XSPI2_R_BASE + STM32_XSPI_TCR_OFFSET) +#define STM32_XSPI2_IR (STM32_XSPI2_R_BASE + STM32_XSPI_IR_OFFSET) +#define STM32_XSPI2_ABR (STM32_XSPI2_R_BASE + STM32_XSPI_ABR_OFFSET) +#define STM32_XSPI2_LPTR (STM32_XSPI2_R_BASE + STM32_XSPI_LPTR_OFFSET) +#define STM32_XSPI2_WPCCR (STM32_XSPI2_R_BASE + STM32_XSPI_WPCCR_OFFSET) +#define STM32_XSPI2_WPTCR (STM32_XSPI2_R_BASE + STM32_XSPI_WPTCR_OFFSET) +#define STM32_XSPI2_WPIR (STM32_XSPI2_R_BASE + STM32_XSPI_WPIR_OFFSET) +#define STM32_XSPI2_WPABR (STM32_XSPI2_R_BASE + STM32_XSPI_WPABR_OFFSET) +#define STM32_XSPI2_WCCR (STM32_XSPI2_R_BASE + STM32_XSPI_WCCR_OFFSET) +#define STM32_XSPI2_WTCR (STM32_XSPI2_R_BASE + STM32_XSPI_WTCR_OFFSET) +#define STM32_XSPI2_WIR (STM32_XSPI2_R_BASE + STM32_XSPI_WIR_OFFSET) +#define STM32_XSPI2_WABR (STM32_XSPI2_R_BASE + STM32_XSPI_WABR_OFFSET) +#define STM32_XSPI2_HLCR (STM32_XSPI2_R_BASE + STM32_XSPI_HLCR_OFFSET) +#define STM32_XSPI2_CALFCR (STM32_XSPI2_R_BASE + STM32_XSPI_CALFCR_OFFSET) +#define STM32_XSPI2_CALMR (STM32_XSPI2_R_BASE + STM32_XSPI_CALMR_OFFSET) +#define STM32_XSPI2_CALSOR (STM32_XSPI2_R_BASE + STM32_XSPI_CALSOR_OFFSET) +#define STM32_XSPI2_CALSIR (STM32_XSPI2_R_BASE + STM32_XSPI_CALSIR_OFFSET) + +#define STM32_XSPIM_CR (STM32_XSPIM_BASE + STM32_XSPIM_CR_OFFSET) + +/* Register Bitfield Definitions ********************************************/ + +/* Control register *********************************************************/ + +#define XSPI_CR_EN (1 << 0) /* XSPI enable */ +#define XSPI_CR_ABORT (1 << 1) /* Abort request */ +#define XSPI_CR_DMAEN (1 << 2) /* DMA enable */ +#define XSPI_CR_TCEN (1 << 3) /* Timeout enable */ +#define XSPI_CR_DMM (1 << 6) /* Dual-memory mode */ +#define XSPI_CR_FTHRES_SHIFT (8) /* FIFO threshold level */ +#define XSPI_CR_FTHRES_MASK (31 << XSPI_CR_FTHRES_SHIFT) +# define XSPI_CR_FTHRES(n) (((n) - 1) << XSPI_CR_FTHRES_SHIFT) +#define XSPI_CR_TEIE (1 << 16) /* Transfer-error interrupt */ +#define XSPI_CR_TCIE (1 << 17) /* Transfer-complete interrupt */ +#define XSPI_CR_FTIE (1 << 18) /* FIFO-threshold interrupt */ +#define XSPI_CR_SMIE (1 << 19) /* Status-match interrupt */ +#define XSPI_CR_TOIE (1 << 20) /* Timeout interrupt */ +#define XSPI_CR_APMS (1 << 22) /* Automatic-polling stop */ +#define XSPI_CR_PMM (1 << 23) /* Polling match mode */ +#define XSPI_CR_CSSEL (1 << 24) /* Chip-select selection */ +#define XSPI_CR_FMODE_SHIFT (28) /* Functional mode */ +#define XSPI_CR_FMODE_MASK (3 << XSPI_CR_FMODE_SHIFT) +# define XSPI_CR_FMODE_WRITE (0 << XSPI_CR_FMODE_SHIFT) +# define XSPI_CR_FMODE_READ (1 << XSPI_CR_FMODE_SHIFT) +# define XSPI_CR_FMODE_POLLING (2 << XSPI_CR_FMODE_SHIFT) +# define XSPI_CR_FMODE_MEMORY (3 << XSPI_CR_FMODE_SHIFT) +#define XSPI_CR_MSEL_SHIFT (30) /* Memory select */ +#define XSPI_CR_MSEL_MASK (3 << XSPI_CR_MSEL_SHIFT) + +/* Device configuration registers *******************************************/ + +#define XSPI_DCR1_CKMODE (1 << 0) /* Clock mode 3 */ +#define XSPI_DCR1_FRCK (1 << 1) /* Free-running clock */ +#define XSPI_DCR1_CSHT_SHIFT (8) /* Chip-select high time */ +#define XSPI_DCR1_CSHT_MASK (0x3f << XSPI_DCR1_CSHT_SHIFT) +# define XSPI_DCR1_CSHT(n) (((n) - 1) << XSPI_DCR1_CSHT_SHIFT) +#define XSPI_DCR1_DEVSIZE_SHIFT (16) /* Device size */ +#define XSPI_DCR1_DEVSIZE_MASK (31 << XSPI_DCR1_DEVSIZE_SHIFT) +# define XSPI_DCR1_DEVSIZE(n) ((n) << XSPI_DCR1_DEVSIZE_SHIFT) +#define XSPI_DCR1_MTYP_SHIFT (24) /* Memory type */ +#define XSPI_DCR1_MTYP_MASK (7 << XSPI_DCR1_MTYP_SHIFT) +# define XSPI_DCR1_MTYP(n) ((n) << XSPI_DCR1_MTYP_SHIFT) + +#define XSPI_DCR2_PRESCALER_SHIFT (0) /* Clock prescaler */ +#define XSPI_DCR2_PRESCALER_MASK (0xff << XSPI_DCR2_PRESCALER_SHIFT) +# define XSPI_DCR2_PRESCALER(n) ((n) << XSPI_DCR2_PRESCALER_SHIFT) +#define XSPI_DCR2_WRAPSIZE_SHIFT (16) /* Wrap size */ +#define XSPI_DCR2_WRAPSIZE_MASK (7 << XSPI_DCR2_WRAPSIZE_SHIFT) +# define XSPI_DCR2_WRAPSIZE(n) ((n) << XSPI_DCR2_WRAPSIZE_SHIFT) + +#define XSPI_DCR3_MAXTRAN_SHIFT (0) /* Maximum transfer */ +#define XSPI_DCR3_MAXTRAN_MASK (0xff << XSPI_DCR3_MAXTRAN_SHIFT) +# define XSPI_DCR3_MAXTRAN(n) ((n) << XSPI_DCR3_MAXTRAN_SHIFT) +#define XSPI_DCR3_CSBOUND_SHIFT (16) /* CS boundary */ +#define XSPI_DCR3_CSBOUND_MASK (31 << XSPI_DCR3_CSBOUND_SHIFT) +# define XSPI_DCR3_CSBOUND(n) ((n) << XSPI_DCR3_CSBOUND_SHIFT) + +#define XSPI_DCR4_REFRESH_SHIFT (0) /* Refresh rate */ +#define XSPI_DCR4_REFRESH_MASK (0xffffffff << XSPI_DCR4_REFRESH_SHIFT) +# define XSPI_DCR4_REFRESH(n) ((n) << XSPI_DCR4_REFRESH_SHIFT) + +/* Status and flag clear registers ******************************************/ + +#define XSPI_SR_TEF (1 << 0) /* Transfer error */ +#define XSPI_SR_TCF (1 << 1) /* Transfer complete */ +#define XSPI_SR_FTF (1 << 2) /* FIFO threshold */ +#define XSPI_SR_SMF (1 << 3) /* Status match */ +#define XSPI_SR_TOF (1 << 4) /* Timeout */ +#define XSPI_SR_BUSY (1 << 5) /* Busy */ +#define XSPI_SR_FLEVEL_SHIFT (8) /* FIFO level */ +#define XSPI_SR_FLEVEL_MASK (0x3f << XSPI_SR_FLEVEL_SHIFT) + +#define XSPI_FCR_CTEF (1 << 0) /* Clear transfer error */ +#define XSPI_FCR_CTCF (1 << 1) /* Clear transfer complete */ +#define XSPI_FCR_CSMF (1 << 3) /* Clear status match */ +#define XSPI_FCR_CTOF (1 << 4) /* Clear timeout */ +#define XSPI_FCR_ALL (XSPI_FCR_CTEF | XSPI_FCR_CTCF | XSPI_FCR_CSMF | XSPI_FCR_CTOF) + +/* Data, address and polling registers **************************************/ + +#define XSPI_DLR_DL_SHIFT (0) /* Data length */ +#define XSPI_DLR_DL_MASK (0xffffffff << XSPI_DLR_DL_SHIFT) +#define XSPI_AR_ADDRESS_SHIFT (0) /* Address */ +#define XSPI_AR_ADDRESS_MASK (0xffffffff << XSPI_AR_ADDRESS_SHIFT) +#define XSPI_DR_DATA_SHIFT (0) /* Data */ +#define XSPI_DR_DATA_MASK (0xffffffff << XSPI_DR_DATA_SHIFT) +#define XSPI_PSMKR_MASK_SHIFT (0) /* Status mask */ +#define XSPI_PSMKR_MASK_MASK (0xffffffff << XSPI_PSMKR_MASK_SHIFT) +#define XSPI_PSMAR_MATCH_SHIFT (0) /* Status match */ +#define XSPI_PSMAR_MATCH_MASK (0xffffffff << XSPI_PSMAR_MATCH_SHIFT) +#define XSPI_PIR_INTERVAL_SHIFT (0) /* Polling interval */ +#define XSPI_PIR_INTERVAL_MASK (0xffff << XSPI_PIR_INTERVAL_SHIFT) +# define XSPI_PIR_INTERVAL(n) ((n) << XSPI_PIR_INTERVAL_SHIFT) + +/* Communication and timing registers ***************************************/ + +#define XSPI_CCR_IMODE_SHIFT (0) /* Instruction mode */ +#define XSPI_CCR_IMODE_MASK (7 << XSPI_CCR_IMODE_SHIFT) +# define XSPI_CCR_IMODE(n) ((n) << XSPI_CCR_IMODE_SHIFT) +#define XSPI_CCR_IDTR (1 << 3) /* Instruction DTR */ +#define XSPI_CCR_ISIZE_SHIFT (4) /* Instruction size */ +#define XSPI_CCR_ISIZE_MASK (3 << XSPI_CCR_ISIZE_SHIFT) +# define XSPI_CCR_ISIZE(n) ((n) << XSPI_CCR_ISIZE_SHIFT) +#define XSPI_CCR_ADMODE_SHIFT (8) /* Address mode */ +#define XSPI_CCR_ADMODE_MASK (7 << XSPI_CCR_ADMODE_SHIFT) +# define XSPI_CCR_ADMODE(n) ((n) << XSPI_CCR_ADMODE_SHIFT) +#define XSPI_CCR_ADDTR (1 << 11) /* Address DTR */ +#define XSPI_CCR_ADSIZE_SHIFT (12) /* Address size */ +#define XSPI_CCR_ADSIZE_MASK (3 << XSPI_CCR_ADSIZE_SHIFT) +# define XSPI_CCR_ADSIZE(n) ((n) << XSPI_CCR_ADSIZE_SHIFT) +#define XSPI_CCR_ABMODE_SHIFT (16) /* Alternate bytes mode */ +#define XSPI_CCR_ABMODE_MASK (7 << XSPI_CCR_ABMODE_SHIFT) +# define XSPI_CCR_ABMODE(n) ((n) << XSPI_CCR_ABMODE_SHIFT) +#define XSPI_CCR_ABDTR (1 << 19) /* Alternate bytes DTR */ +#define XSPI_CCR_ABSIZE_SHIFT (20) /* Alternate bytes size */ +#define XSPI_CCR_ABSIZE_MASK (3 << XSPI_CCR_ABSIZE_SHIFT) +# define XSPI_CCR_ABSIZE(n) ((n) << XSPI_CCR_ABSIZE_SHIFT) +#define XSPI_CCR_DMODE_SHIFT (24) /* Data mode */ +#define XSPI_CCR_DMODE_MASK (7 << XSPI_CCR_DMODE_SHIFT) +# define XSPI_CCR_DMODE(n) ((n) << XSPI_CCR_DMODE_SHIFT) +#define XSPI_CCR_DDTR (1 << 27) /* Data DTR */ +#define XSPI_CCR_DQSE (1 << 29) /* DQS enable */ + +#define XSPI_TCR_DCYC_SHIFT (0) /* Dummy cycles */ +#define XSPI_TCR_DCYC_MASK (31 << XSPI_TCR_DCYC_SHIFT) +# define XSPI_TCR_DCYC(n) ((n) << XSPI_TCR_DCYC_SHIFT) +#define XSPI_TCR_DHQC (1 << 28) /* Delay hold quarter cycle */ +#define XSPI_TCR_SSHIFT (1 << 30) /* Sample shift */ + +#define XSPI_IR_INSTRUCTION_SHIFT (0) /* Instruction */ +#define XSPI_IR_INSTRUCTION_MASK (0xffffffff << XSPI_IR_INSTRUCTION_SHIFT) +#define XSPI_ABR_ALTERNATE_SHIFT (0) /* Alternate bytes */ +#define XSPI_ABR_ALTERNATE_MASK (0xffffffff << XSPI_ABR_ALTERNATE_SHIFT) +#define XSPI_LPTR_TIMEOUT_SHIFT (0) /* Timeout period */ +#define XSPI_LPTR_TIMEOUT_MASK (0xffff << XSPI_LPTR_TIMEOUT_SHIFT) +# define XSPI_LPTR_TIMEOUT(n) ((n) << XSPI_LPTR_TIMEOUT_SHIFT) + +/* Wrap communication and timing registers **********************************/ + +#define XSPI_WPCCR_IMODE_SHIFT (0) /* Instruction mode */ +#define XSPI_WPCCR_IMODE_MASK (7 << XSPI_WPCCR_IMODE_SHIFT) +# define XSPI_WPCCR_IMODE(n) ((n) << XSPI_WPCCR_IMODE_SHIFT) +#define XSPI_WPCCR_IDTR (1 << 3) /* Instruction DTR */ +#define XSPI_WPCCR_ISIZE_SHIFT (4) /* Instruction size */ +#define XSPI_WPCCR_ISIZE_MASK (3 << XSPI_WPCCR_ISIZE_SHIFT) +# define XSPI_WPCCR_ISIZE(n) ((n) << XSPI_WPCCR_ISIZE_SHIFT) +#define XSPI_WPCCR_ADMODE_SHIFT (8) /* Address mode */ +#define XSPI_WPCCR_ADMODE_MASK (7 << XSPI_WPCCR_ADMODE_SHIFT) +# define XSPI_WPCCR_ADMODE(n) ((n) << XSPI_WPCCR_ADMODE_SHIFT) +#define XSPI_WPCCR_ADDTR (1 << 11) /* Address DTR */ +#define XSPI_WPCCR_ADSIZE_SHIFT (12) /* Address size */ +#define XSPI_WPCCR_ADSIZE_MASK (3 << XSPI_WPCCR_ADSIZE_SHIFT) +# define XSPI_WPCCR_ADSIZE(n) ((n) << XSPI_WPCCR_ADSIZE_SHIFT) +#define XSPI_WPCCR_ABMODE_SHIFT (16) /* Alternate bytes mode */ +#define XSPI_WPCCR_ABMODE_MASK (7 << XSPI_WPCCR_ABMODE_SHIFT) +# define XSPI_WPCCR_ABMODE(n) ((n) << XSPI_WPCCR_ABMODE_SHIFT) +#define XSPI_WPCCR_ABDTR (1 << 19) /* Alternate bytes DTR */ +#define XSPI_WPCCR_ABSIZE_SHIFT (20) /* Alternate bytes size */ +#define XSPI_WPCCR_ABSIZE_MASK (3 << XSPI_WPCCR_ABSIZE_SHIFT) +# define XSPI_WPCCR_ABSIZE(n) ((n) << XSPI_WPCCR_ABSIZE_SHIFT) +#define XSPI_WPCCR_DMODE_SHIFT (24) /* Data mode */ +#define XSPI_WPCCR_DMODE_MASK (7 << XSPI_WPCCR_DMODE_SHIFT) +# define XSPI_WPCCR_DMODE(n) ((n) << XSPI_WPCCR_DMODE_SHIFT) +#define XSPI_WPCCR_DDTR (1 << 27) /* Data DTR */ +#define XSPI_WPCCR_DQSE (1 << 29) /* DQS enable */ + +#define XSPI_WPTCR_DCYC_SHIFT (0) /* Dummy cycles */ +#define XSPI_WPTCR_DCYC_MASK (31 << XSPI_WPTCR_DCYC_SHIFT) +# define XSPI_WPTCR_DCYC(n) ((n) << XSPI_WPTCR_DCYC_SHIFT) +#define XSPI_WPTCR_DHQC (1 << 28) /* Delay hold quarter cycle */ +#define XSPI_WPTCR_SSHIFT (1 << 30) /* Sample shift */ + +#define XSPI_WPIR_INSTRUCTION_SHIFT (0) /* Instruction */ +#define XSPI_WPIR_INSTRUCTION_MASK (0xffffffff << XSPI_WPIR_INSTRUCTION_SHIFT) +#define XSPI_WPABR_ALTERNATE_SHIFT (0) /* Alternate bytes */ +#define XSPI_WPABR_ALTERNATE_MASK (0xffffffff << XSPI_WPABR_ALTERNATE_SHIFT) + +/* Write communication and timing registers *********************************/ + +#define XSPI_WCCR_IMODE_SHIFT (0) /* Instruction mode */ +#define XSPI_WCCR_IMODE_MASK (7 << XSPI_WCCR_IMODE_SHIFT) +# define XSPI_WCCR_IMODE(n) ((n) << XSPI_WCCR_IMODE_SHIFT) +#define XSPI_WCCR_IDTR (1 << 3) /* Instruction DTR */ +#define XSPI_WCCR_ISIZE_SHIFT (4) /* Instruction size */ +#define XSPI_WCCR_ISIZE_MASK (3 << XSPI_WCCR_ISIZE_SHIFT) +# define XSPI_WCCR_ISIZE(n) ((n) << XSPI_WCCR_ISIZE_SHIFT) +#define XSPI_WCCR_ADMODE_SHIFT (8) /* Address mode */ +#define XSPI_WCCR_ADMODE_MASK (7 << XSPI_WCCR_ADMODE_SHIFT) +# define XSPI_WCCR_ADMODE(n) ((n) << XSPI_WCCR_ADMODE_SHIFT) +#define XSPI_WCCR_ADDTR (1 << 11) /* Address DTR */ +#define XSPI_WCCR_ADSIZE_SHIFT (12) /* Address size */ +#define XSPI_WCCR_ADSIZE_MASK (3 << XSPI_WCCR_ADSIZE_SHIFT) +# define XSPI_WCCR_ADSIZE(n) ((n) << XSPI_WCCR_ADSIZE_SHIFT) +#define XSPI_WCCR_ABMODE_SHIFT (16) /* Alternate bytes mode */ +#define XSPI_WCCR_ABMODE_MASK (7 << XSPI_WCCR_ABMODE_SHIFT) +# define XSPI_WCCR_ABMODE(n) ((n) << XSPI_WCCR_ABMODE_SHIFT) +#define XSPI_WCCR_ABDTR (1 << 19) /* Alternate bytes DTR */ +#define XSPI_WCCR_ABSIZE_SHIFT (20) /* Alternate bytes size */ +#define XSPI_WCCR_ABSIZE_MASK (3 << XSPI_WCCR_ABSIZE_SHIFT) +# define XSPI_WCCR_ABSIZE(n) ((n) << XSPI_WCCR_ABSIZE_SHIFT) +#define XSPI_WCCR_DMODE_SHIFT (24) /* Data mode */ +#define XSPI_WCCR_DMODE_MASK (7 << XSPI_WCCR_DMODE_SHIFT) +# define XSPI_WCCR_DMODE(n) ((n) << XSPI_WCCR_DMODE_SHIFT) +#define XSPI_WCCR_DDTR (1 << 27) /* Data DTR */ +#define XSPI_WCCR_DQSE (1 << 29) /* DQS enable */ + +#define XSPI_WTCR_DCYC_SHIFT (0) /* Dummy cycles */ +#define XSPI_WTCR_DCYC_MASK (31 << XSPI_WTCR_DCYC_SHIFT) +# define XSPI_WTCR_DCYC(n) ((n) << XSPI_WTCR_DCYC_SHIFT) + +#define XSPI_WIR_INSTRUCTION_SHIFT (0) /* Instruction */ +#define XSPI_WIR_INSTRUCTION_MASK (0xffffffff << XSPI_WIR_INSTRUCTION_SHIFT) +#define XSPI_WABR_ALTERNATE_SHIFT (0) /* Alternate bytes */ +#define XSPI_WABR_ALTERNATE_MASK (0xffffffff << XSPI_WABR_ALTERNATE_SHIFT) + +/* HyperBus latency register ************************************************/ + +#define XSPI_HLCR_LM (1 << 0) /* Latency mode */ +#define XSPI_HLCR_WZL (1 << 1) /* Write zero latency */ +#define XSPI_HLCR_TACC_SHIFT (8) /* Access time */ +#define XSPI_HLCR_TACC_MASK (0xff << XSPI_HLCR_TACC_SHIFT) +#define XSPI_HLCR_TRWR_SHIFT (16) /* Read write recovery time */ +#define XSPI_HLCR_TRWR_MASK (0xff << XSPI_HLCR_TRWR_SHIFT) + +/* Calibration registers ****************************************************/ + +#define XSPI_CALFCR_FINE_SHIFT (0) /* Fine calibration */ +#define XSPI_CALFCR_FINE_MASK (0x7f << XSPI_CALFCR_FINE_SHIFT) +# define XSPI_CALFCR_FINE(n) ((n) << XSPI_CALFCR_FINE_SHIFT) +#define XSPI_CALFCR_COARSE_SHIFT (16) /* Coarse calibration */ +#define XSPI_CALFCR_COARSE_MASK (31 << XSPI_CALFCR_COARSE_SHIFT) +# define XSPI_CALFCR_COARSE(n) ((n) << XSPI_CALFCR_COARSE_SHIFT) +#define XSPI_CALFCR_CALMAX (1 << 31) /* Calibration max value */ + +#define XSPI_CALMR_FINE_SHIFT (0) /* Fine calibration */ +#define XSPI_CALMR_FINE_MASK (0x7f << XSPI_CALMR_FINE_SHIFT) +# define XSPI_CALMR_FINE(n) ((n) << XSPI_CALMR_FINE_SHIFT) +#define XSPI_CALMR_COARSE_SHIFT (16) /* Coarse calibration */ +#define XSPI_CALMR_COARSE_MASK (31 << XSPI_CALMR_COARSE_SHIFT) +# define XSPI_CALMR_COARSE(n) ((n) << XSPI_CALMR_COARSE_SHIFT) + +#define XSPI_CALSOR_FINE_SHIFT (0) /* Fine calibration */ +#define XSPI_CALSOR_FINE_MASK (0x7f << XSPI_CALSOR_FINE_SHIFT) +# define XSPI_CALSOR_FINE(n) ((n) << XSPI_CALSOR_FINE_SHIFT) +#define XSPI_CALSOR_COARSE_SHIFT (16) /* Coarse calibration */ +#define XSPI_CALSOR_COARSE_MASK (31 << XSPI_CALSOR_COARSE_SHIFT) +# define XSPI_CALSOR_COARSE(n) ((n) << XSPI_CALSOR_COARSE_SHIFT) + +#define XSPI_CALSIR_FINE_SHIFT (0) /* Fine calibration */ +#define XSPI_CALSIR_FINE_MASK (0x7f << XSPI_CALSIR_FINE_SHIFT) +# define XSPI_CALSIR_FINE(n) ((n) << XSPI_CALSIR_FINE_SHIFT) +#define XSPI_CALSIR_COARSE_SHIFT (16) /* Coarse calibration */ +#define XSPI_CALSIR_COARSE_MASK (31 << XSPI_CALSIR_COARSE_SHIFT) +# define XSPI_CALSIR_COARSE(n) ((n) << XSPI_CALSIR_COARSE_SHIFT) + +/* XSPI I/O manager control register ****************************************/ + +#define XSPIM_CR_MUXEN (1 << 0) /* Multiplexed mode */ +#define XSPIM_CR_MODE (1 << 1) /* Multiplexing mode */ +#define XSPIM_CR_CSSEL_OVR_EN (1 << 4) /* Chip-select override */ +#define XSPIM_CR_CSSEL_OVR_O1 (1 << 5) /* XSPI1 uses nCS2 */ +#define XSPIM_CR_CSSEL_OVR_O2 (1 << 6) /* XSPI2 uses nCS2 */ +#define XSPIM_CR_REQ2ACK_SHIFT (16) +#define XSPIM_CR_REQ2ACK_MASK (0xff << XSPIM_CR_REQ2ACK_SHIFT) +# define XSPIM_CR_REQ2ACK(n) (((n) - 1) << XSPIM_CR_REQ2ACK_SHIFT) + +#endif /* __ARCH_ARM_SRC_STM32H7_HARDWARE_STM32H7RSXX_XSPI_H */ diff --git a/arch/arm/src/stm32h7/stm32_xspi.c b/arch/arm/src/stm32h7/stm32_xspi.c new file mode 100644 index 00000000000..0edf27e92fb --- /dev/null +++ b/arch/arm/src/stm32h7/stm32_xspi.c @@ -0,0 +1,1230 @@ +/**************************************************************************** + * arch/arm/src/stm32h7/stm32_xspi.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <arch/board/board.h> +#include <nuttx/kmalloc.h> +#include <nuttx/mutex.h> +#include <nuttx/spi/qspi.h> + +#include "arm_internal.h" +#include "stm32_gpio.h" +#include "stm32_xspi.h" +#include "hardware/stm32h7rsxx_pwr.h" +#include "hardware/stm32h7rsxx_rcc.h" +#include "hardware/stm32h7rsxx_sbs.h" +#include "hardware/stm32h7rsxx_xspi.h" + +#ifdef CONFIG_STM32_XSPI + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define XSPI_TIMEOUT 1000000 + +/* MTYP=5 is a reserved encoding in the DCR1 register */ + +#define XSPI_MEMTYPE_RESERVED 5 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct stm32_xspidev_s +{ + struct qspi_dev_s qspi; + uint32_t base; + mutex_t lock; + uint8_t intf; + bool initialized; + bool memmap; + bool sample_shift; +}; + +struct stm32_xspi_cmd_s +{ + uint32_t instruction; + uint32_t address; + void *buffer; + size_t buflen; + uint8_t instruction_lines; + uint8_t address_lines; + uint8_t data_lines; + uint8_t instruction_bits; + uint8_t address_bits; + uint8_t dummy_cycles; + bool dtr; + bool write; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int xspi_lock(struct qspi_dev_s *dev, bool lock); +static uint32_t xspi_setfrequency(struct qspi_dev_s *dev, + uint32_t frequency); +static void xspi_setmode(struct qspi_dev_s *dev, enum qspi_mode_e mode); +static void xspi_setbits(struct qspi_dev_s *dev, int nbits); +static int xspi_command(struct qspi_dev_s *dev, + struct qspi_cmdinfo_s *cmdinfo); +static int xspi_memory(struct qspi_dev_s *dev, + struct qspi_meminfo_s *meminfo); +static void *xspi_alloc(struct qspi_dev_s *dev, size_t buflen); +static void xspi_free(struct qspi_dev_s *dev, void *buffer); +static int xspi_mmap_cmd(struct stm32_xspidev_s *priv, + const struct qspi_meminfo_s *meminfo, + bool write); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct qspi_ops_s g_xspi_ops = +{ + .lock = xspi_lock, + .setfrequency = xspi_setfrequency, + .setmode = xspi_setmode, + .setbits = xspi_setbits, + .command = xspi_command, + .memory = xspi_memory, + .alloc = xspi_alloc, + .free = xspi_free, +}; + +static struct stm32_xspidev_s g_xspi1dev = +{ + .qspi = + { + .ops = &g_xspi_ops, + }, + .base = STM32_XSPI1_R_BASE, + .lock = NXMUTEX_INITIALIZER, + .intf = 1, +}; + +static struct stm32_xspidev_s g_xspi2dev = +{ + .qspi = + { + .ops = &g_xspi_ops, + }, + .base = STM32_XSPI2_R_BASE, + .lock = NXMUTEX_INITIALIZER, + .intf = 2, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: xspi_getreg + * + * Description: + * Read an XSPI register. + * + ****************************************************************************/ + +static inline uint32_t xspi_getreg(struct stm32_xspidev_s *priv, + uint32_t offset) +{ + return getreg32(priv->base + offset); +} + +/**************************************************************************** + * Name: xspi_putreg + * + * Description: + * Write a value to an XSPI register. + * + ****************************************************************************/ + +static inline void xspi_putreg(struct stm32_xspidev_s *priv, + uint32_t offset, uint32_t value) +{ + putreg32(value, priv->base + offset); +} + +/**************************************************************************** + * Name: xspi_device + * + * Description: + * Map an XSPI interface number to its device state structure. + * + ****************************************************************************/ + +static struct stm32_xspidev_s *xspi_device(int intf) +{ + if (intf == 1) + { + return &g_xspi1dev; + } + + if (intf == 2) + { + return &g_xspi2dev; + } + + return NULL; +} + +/**************************************************************************** + * Name: xspi_wait + * + * Description: + * Poll until the masked status register bits equal the given value. + * + ****************************************************************************/ + +static int xspi_wait(struct stm32_xspidev_s *priv, uint32_t mask, + uint32_t value) +{ + int timeout; + + for (timeout = XSPI_TIMEOUT; timeout > 0; timeout--) + { + uint32_t status = xspi_getreg(priv, STM32_XSPI_SR_OFFSET); + + if ((status & XSPI_SR_TEF) != 0) + { + xspi_putreg(priv, STM32_XSPI_FCR_OFFSET, XSPI_FCR_CTEF); + return -EIO; + } + + if ((status & mask) == value) + { + return OK; + } + } + + return -ETIMEDOUT; +} + +/**************************************************************************** + * Name: xspi_wait_any + * + * Description: + * Poll until any of the masked status register bits is set. + * + ****************************************************************************/ + +static int xspi_wait_any(struct stm32_xspidev_s *priv, uint32_t mask) +{ + int timeout; + + for (timeout = XSPI_TIMEOUT; timeout > 0; timeout--) + { + uint32_t status = xspi_getreg(priv, STM32_XSPI_SR_OFFSET); + + if ((status & XSPI_SR_TEF) != 0) + { + xspi_putreg(priv, STM32_XSPI_FCR_OFFSET, XSPI_FCR_CTEF); + return -EIO; + } + + if ((status & mask) != 0) + { + return OK; + } + } + + return -ETIMEDOUT; +} + +/**************************************************************************** + * Name: xspi_size + * + * Description: + * Convert a bit width (8/16/24/32) to the CCR SIZE field encoding. + * + ****************************************************************************/ + +static int xspi_size(uint8_t bits) +{ + if (bits == 8 || bits == 16 || bits == 24 || bits == 32) + { + return (bits >> 3) - 1; + } + + return -EINVAL; +} + +/**************************************************************************** + * Name: xspi_lines + * + * Description: + * Convert a line count (0/1/2/4/8) to the CCR MODE field encoding. + * + ****************************************************************************/ + +static int xspi_lines(uint8_t lines) +{ + switch (lines) + { + case 0: + return 0; + case 1: + return 1; + case 2: + return 2; + case 4: + return 3; + case 8: + return 4; + default: + return -EINVAL; + } +} + +/**************************************************************************** + * Name: xspi_ccr + * + * Description: + * Build the CCR register value for a transfer command. + * + ****************************************************************************/ + +static int xspi_ccr(const struct stm32_xspi_cmd_s *cmd, uint32_t *ccr) +{ + uint32_t reg = 0; + int lines; + int size; + + lines = xspi_lines(cmd->instruction_lines); + if (lines < 0) + { + return lines; + } + + if (lines != 0) + { + size = xspi_size(cmd->instruction_bits); + if (size < 0) + { + return size; + } + + reg |= XSPI_CCR_IMODE(lines) | XSPI_CCR_ISIZE(size); + if (cmd->dtr) + { + reg |= XSPI_CCR_IDTR; + } + } + + lines = xspi_lines(cmd->address_lines); + if (lines < 0) + { + return lines; + } + + if (lines != 0) + { + size = xspi_size(cmd->address_bits); + if (size < 0) + { + return size; + } + + reg |= XSPI_CCR_ADMODE(lines) | XSPI_CCR_ADSIZE(size); + if (cmd->dtr) + { + reg |= XSPI_CCR_ADDTR; + } + } + + lines = xspi_lines(cmd->data_lines); + if (lines < 0) + { + return lines; + } + + reg |= XSPI_CCR_DMODE(lines); + if (cmd->dtr && lines != 0) + { + reg |= XSPI_CCR_DDTR; + } + + if (cmd->dummy_cycles > 31) + { + return -EINVAL; + } + + *ccr = reg; + return OK; +} + +/**************************************************************************** + * Name: xspi_iom_config + * + * Description: + * Configure the XSPI I/O manager port and chip select routing. + * + ****************************************************************************/ + +static void xspi_iom_config(struct stm32_xspidev_s *priv, + const struct stm32_xspi_config_s *config) +{ + enum stm32_xspi_iomport_e port1; + enum stm32_xspi_iomport_e port2; + enum stm32_xspi_cs_e cs1; + enum stm32_xspi_cs_e cs2; + uint32_t xspi1cr; + uint32_t xspi2cr; + uint32_t reg; + + reg = getreg32(STM32_XSPIM_CR); + port1 = (reg & XSPIM_CR_MODE) != 0 ? STM32_XSPI_IOM_PORT2 : + STM32_XSPI_IOM_PORT1; + port2 = ((reg & XSPIM_CR_MUXEN) != 0) != + ((reg & XSPIM_CR_MODE) != 0) ? STM32_XSPI_IOM_PORT1 : + STM32_XSPI_IOM_PORT2; + + if ((reg & XSPIM_CR_CSSEL_OVR_EN) == 0) + { + cs1 = STM32_XSPI_CS_DISABLED; + cs2 = STM32_XSPI_CS_DISABLED; + } + else + { + cs1 = (reg & XSPIM_CR_CSSEL_OVR_O1) != 0 ? STM32_XSPI_CS_NCS2 : + STM32_XSPI_CS_NCS1; + cs2 = (reg & XSPIM_CR_CSSEL_OVR_O2) != 0 ? STM32_XSPI_CS_NCS2 : + STM32_XSPI_CS_NCS1; + } + + if (priv->intf == 1) + { + port1 = config->iomport; + cs1 = config->cs_override; + } + else + { + port2 = config->iomport; + cs2 = config->cs_override; + } + + reg = XSPIM_CR_REQ2ACK(config->req2ack); + if (port1 == port2) + { + reg |= XSPIM_CR_MUXEN; + } + + if (port1 == STM32_XSPI_IOM_PORT2) + { + reg |= XSPIM_CR_MODE; + } + + if (cs1 != STM32_XSPI_CS_DISABLED || cs2 != STM32_XSPI_CS_DISABLED) + { + reg |= XSPIM_CR_CSSEL_OVR_EN; + } + + if (cs1 == STM32_XSPI_CS_NCS2) + { + reg |= XSPIM_CR_CSSEL_OVR_O1; + } + + if (cs2 == STM32_XSPI_CS_NCS2) + { + reg |= XSPIM_CR_CSSEL_OVR_O2; + } + + xspi1cr = getreg32(STM32_XSPI1_CR); + xspi2cr = getreg32(STM32_XSPI2_CR); + modifyreg32(STM32_XSPI1_CR, XSPI_CR_EN, 0); + modifyreg32(STM32_XSPI2_CR, XSPI_CR_EN, 0); + putreg32(reg, STM32_XSPIM_CR); + putreg32(xspi1cr, STM32_XSPI1_CR); + putreg32(xspi2cr, STM32_XSPI2_CR); +} + +/**************************************************************************** + * Name: xspi_compensation_enable + * + * Description: + * Enable the XSPI I/O compensation cell and wait until it is ready. + * + ****************************************************************************/ + +static int xspi_compensation_enable(struct stm32_xspidev_s *priv) +{ + uint32_t enable; + uint32_t ready; + int timeout; + + modifyreg32(STM32_RCC_APB4ENR, 0, RCC_APB4ENR_SBSEN); + modifyreg32(STM32_RCC_CR, 0, RCC_CR_CSION); + + for (timeout = XSPI_TIMEOUT; timeout > 0; timeout--) + { + if ((getreg32(STM32_RCC_CR) & RCC_CR_CSIRDY) != 0) + { + break; + } + } + + if (timeout == 0) + { + return -ETIMEDOUT; + } + + if (priv->intf == 1) + { + enable = SBS_CCCSR_XSPI1_COMP_EN | SBS_CCCSR_XSPI1_IOHSLV; + ready = SBS_CCCSR_XSPI1_COMP_RDY; + } + else + { + enable = SBS_CCCSR_XSPI2_COMP_EN | SBS_CCCSR_XSPI2_IOHSLV; + ready = SBS_CCCSR_XSPI2_COMP_RDY; + } + + modifyreg32(STM32_SBS_CCCSR, 0, enable); + for (timeout = XSPI_TIMEOUT; timeout > 0; timeout--) + { + if ((getreg32(STM32_SBS_CCCSR) & ready) != 0) + { + return OK; + } + } + + return -ETIMEDOUT; +} + +/**************************************************************************** + * Name: xspi_hw_initialize + * + * Description: + * One-time hardware initialization: power, clocking and GPIO pins. + * + ****************************************************************************/ + +static int xspi_hw_initialize(struct stm32_xspidev_s *priv) +{ + if (priv->intf == 1) + { + modifyreg32(STM32_PWR_CSR2, 0, PWR_CSR2_EN_XSPIM1); + modifyreg32(STM32_RCC_CCIPR1, RCC_CCIPR1_XSPI1SEL_MASK, + RCC_CCIPR1_XSPI1SEL_HCLK); + modifyreg32(STM32_RCC_AHB5ENR, 0, RCC_AHB5ENR_XSPI1EN | + RCC_AHB5ENR_XSPIMEN); + +#ifdef GPIO_XSPI1_CLK + stm32_configgpio(GPIO_XSPI1_CLK); +#endif +#ifdef GPIO_XSPI1_DQS + stm32_configgpio(GPIO_XSPI1_DQS); +#endif +#ifdef GPIO_XSPI1_NCS + stm32_configgpio(GPIO_XSPI1_NCS); +#endif +#ifdef GPIO_XSPI1_IO0 + stm32_configgpio(GPIO_XSPI1_IO0); +#endif +#ifdef GPIO_XSPI1_IO1 + stm32_configgpio(GPIO_XSPI1_IO1); +#endif +#ifdef GPIO_XSPI1_IO2 + stm32_configgpio(GPIO_XSPI1_IO2); +#endif +#ifdef GPIO_XSPI1_IO3 + stm32_configgpio(GPIO_XSPI1_IO3); +#endif +#ifdef GPIO_XSPI1_IO4 + stm32_configgpio(GPIO_XSPI1_IO4); +#endif +#ifdef GPIO_XSPI1_IO5 + stm32_configgpio(GPIO_XSPI1_IO5); +#endif +#ifdef GPIO_XSPI1_IO6 + stm32_configgpio(GPIO_XSPI1_IO6); +#endif +#ifdef GPIO_XSPI1_IO7 + stm32_configgpio(GPIO_XSPI1_IO7); +#endif + } + else + { + modifyreg32(STM32_PWR_CSR2, 0, PWR_CSR2_EN_XSPIM2); + modifyreg32(STM32_RCC_CCIPR1, RCC_CCIPR1_XSPI2SEL_MASK, + RCC_CCIPR1_XSPI2SEL_HCLK); + modifyreg32(STM32_RCC_AHB5ENR, 0, RCC_AHB5ENR_XSPI2EN | + RCC_AHB5ENR_XSPIMEN); + +#ifdef GPIO_XSPI2_CLK + stm32_configgpio(GPIO_XSPI2_CLK); +#endif +#ifdef GPIO_XSPI2_DQS + stm32_configgpio(GPIO_XSPI2_DQS); +#endif +#ifdef GPIO_XSPI2_NCS + stm32_configgpio(GPIO_XSPI2_NCS); +#endif +#ifdef GPIO_XSPI2_IO0 + stm32_configgpio(GPIO_XSPI2_IO0); +#endif +#ifdef GPIO_XSPI2_IO1 + stm32_configgpio(GPIO_XSPI2_IO1); +#endif +#ifdef GPIO_XSPI2_IO2 + stm32_configgpio(GPIO_XSPI2_IO2); +#endif +#ifdef GPIO_XSPI2_IO3 + stm32_configgpio(GPIO_XSPI2_IO3); +#endif +#ifdef GPIO_XSPI2_IO4 + stm32_configgpio(GPIO_XSPI2_IO4); +#endif +#ifdef GPIO_XSPI2_IO5 + stm32_configgpio(GPIO_XSPI2_IO5); +#endif +#ifdef GPIO_XSPI2_IO6 + stm32_configgpio(GPIO_XSPI2_IO6); +#endif +#ifdef GPIO_XSPI2_IO7 + stm32_configgpio(GPIO_XSPI2_IO7); +#endif + } + + return xspi_compensation_enable(priv); +} + +/**************************************************************************** + * Name: xspi_lock + * + * Description: + * Lock or unlock the XSPI bus for exclusive access. + * + ****************************************************************************/ + +static int xspi_lock(struct qspi_dev_s *dev, bool lock) +{ + struct stm32_xspidev_s *priv = (struct stm32_xspidev_s *)dev; + + return lock ? nxmutex_lock(&priv->lock) : nxmutex_unlock(&priv->lock); +} + +/**************************************************************************** + * Name: xspi_setfrequency + * + * Description: + * Set the XSPI frequency and return the actual frequency selected. + * + ****************************************************************************/ + +static uint32_t xspi_setfrequency(struct qspi_dev_s *dev, + uint32_t frequency) +{ + struct stm32_xspidev_s *priv = (struct stm32_xspidev_s *)dev; + uint32_t prescaler; + + if (frequency == 0) + { + return 0; + } + + prescaler = (STM32_HCLK_FREQUENCY + frequency - 1) / frequency; + if (prescaler < 1) + { + prescaler = 1; + } + else if (prescaler > 256) + { + prescaler = 256; + } + + modifyreg32(priv->base + STM32_XSPI_DCR2_OFFSET, + XSPI_DCR2_PRESCALER_MASK, + XSPI_DCR2_PRESCALER(prescaler - 1)); + return STM32_HCLK_FREQUENCY / prescaler; +} + +/**************************************************************************** + * Name: xspi_setmode + * + * Description: + * Set the XSPI clock mode (mode 0 or mode 3). + * + ****************************************************************************/ + +static void xspi_setmode(struct qspi_dev_s *dev, enum qspi_mode_e mode) +{ + struct stm32_xspidev_s *priv = (struct stm32_xspidev_s *)dev; + + DEBUGASSERT(mode == QSPIDEV_MODE0 || mode == QSPIDEV_MODE3); + modifyreg32(priv->base + STM32_XSPI_DCR1_OFFSET, XSPI_DCR1_CKMODE, + mode == QSPIDEV_MODE3 ? XSPI_DCR1_CKMODE : 0); +} + +/**************************************************************************** + * Name: xspi_setbits + * + * Description: + * Set the number of bits per word (only 8 bits are supported). + * + ****************************************************************************/ + +static void xspi_setbits(struct qspi_dev_s *dev, int nbits) +{ + UNUSED(dev); + DEBUGASSERT(nbits == 8); +} + +/**************************************************************************** + * Name: xspi_configure + * + * Description: + * Validate the configuration and program the XSPI device registers. + * + ****************************************************************************/ + +static int xspi_configure(struct stm32_xspidev_s *priv, + const struct stm32_xspi_config_s *config) +{ + uint32_t dcr1; + uint32_t cr; + int ret; + + if (config == NULL || config->memtype == XSPI_MEMTYPE_RESERVED || + config->memtype > STM32_XSPI_MEMTYPE_APMEM_16BIT || + config->iomport > STM32_XSPI_IOM_PORT2 || + config->cs_override > STM32_XSPI_CS_NCS2 || config->devsize > 31 || + config->csht < 1 || config->csht > 64 || config->fthres < 1 || + config->fthres > 32 || config->wrapsize > 7 || + config->csbound > 31 || config->req2ack < 1 || + config->req2ack > 256) + { + return -EINVAL; + } + + ret = xspi_wait(priv, XSPI_SR_BUSY, 0); + if (ret < 0) + { + return ret; + } + + cr = xspi_getreg(priv, STM32_XSPI_CR_OFFSET); + xspi_putreg(priv, STM32_XSPI_CR_OFFSET, cr & ~XSPI_CR_EN); + xspi_iom_config(priv, config); + + dcr1 = XSPI_DCR1_CSHT(config->csht) | + XSPI_DCR1_DEVSIZE(config->devsize) | + XSPI_DCR1_MTYP(config->memtype); + if (config->clock_mode3) + { + dcr1 |= XSPI_DCR1_CKMODE; + } + + if (config->free_running) + { + dcr1 |= XSPI_DCR1_FRCK; + } + + xspi_putreg(priv, STM32_XSPI_DCR1_OFFSET, dcr1); + xspi_putreg(priv, STM32_XSPI_DCR2_OFFSET, + XSPI_DCR2_PRESCALER(config->prescaler) | + XSPI_DCR2_WRAPSIZE(config->wrapsize)); + xspi_putreg(priv, STM32_XSPI_DCR3_OFFSET, + XSPI_DCR3_MAXTRAN(config->maxtran) | + XSPI_DCR3_CSBOUND(config->csbound)); + xspi_putreg(priv, STM32_XSPI_DCR4_OFFSET, config->refresh); + + priv->sample_shift = config->sample_shift; + cr &= ~(XSPI_CR_FTHRES_MASK | XSPI_CR_FMODE_MASK); + cr |= XSPI_CR_FTHRES(config->fthres) | XSPI_CR_EN; + xspi_putreg(priv, STM32_XSPI_CR_OFFSET, cr); + return OK; +} + +/**************************************************************************** + * Name: xspi_transfer + * + * Description: + * Execute a single polled indirect-mode transfer. + * + ****************************************************************************/ + +static int xspi_transfer(struct stm32_xspidev_s *priv, + const struct stm32_xspi_cmd_s *cmd) +{ + uint8_t *buffer; + uint32_t ccr; + uint32_t tcr; + uint32_t fmode; + size_t remaining; + int ret; + + if (cmd == NULL || (cmd->buflen > 0 && cmd->buffer == NULL) || + (cmd->buflen > 0 && cmd->data_lines == 0) || + (cmd->buflen == 0 && cmd->data_lines != 0)) + { + return -EINVAL; + } + + ret = xspi_ccr(cmd, &ccr); + if (ret < 0) + { + return ret; + } + + ret = xspi_wait(priv, XSPI_SR_BUSY, 0); + if (ret < 0) + { + return ret; + } + + xspi_putreg(priv, STM32_XSPI_FCR_OFFSET, XSPI_FCR_ALL); + if (cmd->buflen > 0) + { + xspi_putreg(priv, STM32_XSPI_DLR_OFFSET, cmd->buflen - 1); + } + + tcr = XSPI_TCR_DCYC(cmd->dummy_cycles); + if (priv->sample_shift && !cmd->dtr) + { + tcr |= XSPI_TCR_SSHIFT; + } + + xspi_putreg(priv, STM32_XSPI_CCR_OFFSET, ccr); + xspi_putreg(priv, STM32_XSPI_TCR_OFFSET, tcr); + fmode = cmd->write ? XSPI_CR_FMODE_WRITE : XSPI_CR_FMODE_READ; + modifyreg32(priv->base + STM32_XSPI_CR_OFFSET, XSPI_CR_FMODE_MASK, + fmode); + + xspi_putreg(priv, STM32_XSPI_IR_OFFSET, cmd->instruction); + if (cmd->address_lines != 0) + { + xspi_putreg(priv, STM32_XSPI_AR_OFFSET, cmd->address); + } + + if (cmd->buflen == 0) + { + ret = xspi_wait(priv, XSPI_SR_BUSY, 0); + xspi_putreg(priv, STM32_XSPI_FCR_OFFSET, XSPI_FCR_CTCF); + return ret; + } + + buffer = cmd->buffer; + remaining = cmd->buflen; + while (remaining > 0) + { + uint32_t flags = cmd->write ? XSPI_SR_FTF : + (XSPI_SR_FTF | XSPI_SR_TCF); + + ret = xspi_wait_any(priv, flags); + if (ret < 0) + { + break; + } + + if (cmd->write) + { + putreg8(*buffer, priv->base + STM32_XSPI_DR_OFFSET); + } + else + { + *buffer = getreg8(priv->base + STM32_XSPI_DR_OFFSET); + } + + buffer++; + remaining--; + } + + if (ret == OK) + { + ret = xspi_wait(priv, XSPI_SR_TCF, XSPI_SR_TCF); + } + + xspi_putreg(priv, STM32_XSPI_FCR_OFFSET, + XSPI_FCR_CTEF | XSPI_FCR_CTCF); + return ret; +} + +/**************************************************************************** + * Name: xspi_instruction_lines + * + * Description: + * Get the instruction line count from command or memory transfer flags. + * + ****************************************************************************/ + +static uint8_t xspi_instruction_lines(uint16_t flags, bool memory) +{ + if (memory ? QSPIMEM_ISIOCTAL(flags) : QSPICMD_ISIOCTAL(flags)) + { + return 8; + } + + if (memory ? QSPIMEM_ISIQUAD(flags) : QSPICMD_ISIQUAD(flags)) + { + return 4; + } + + if (memory ? QSPIMEM_ISIDUAL(flags) : QSPICMD_ISIDUAL(flags)) + { + return 2; + } + + return 1; +} + +/**************************************************************************** + * Name: xspi_command + * + * Description: + * Perform one QSPI command transaction. + * + ****************************************************************************/ + +static int xspi_command(struct qspi_dev_s *dev, + struct qspi_cmdinfo_s *cmdinfo) +{ + struct stm32_xspidev_s *priv = (struct stm32_xspidev_s *)dev; + struct stm32_xspi_cmd_s cmd; + + if (cmdinfo == NULL || cmdinfo->addrlen > 4 || + (QSPICMD_ISDATA(cmdinfo->flags) && cmdinfo->buffer == NULL)) + { + return -EINVAL; + } + + cmd.instruction = cmdinfo->cmd; + cmd.address = cmdinfo->addr; + cmd.buffer = cmdinfo->buffer; + cmd.buflen = QSPICMD_ISDATA(cmdinfo->flags) ? + cmdinfo->buflen : 0; + cmd.instruction_lines = xspi_instruction_lines(cmdinfo->flags, false); + cmd.address_lines = QSPICMD_ISADDRESS(cmdinfo->flags) ? + (QSPICMD_ISOCTALIO(cmdinfo->flags) ? 8 : 1) : 0; + cmd.data_lines = QSPICMD_ISDATA(cmdinfo->flags) ? + (QSPICMD_ISOCTALIO(cmdinfo->flags) ? 8 : 1) : 0; + cmd.instruction_bits = QSPICMD_ISIOCTAL(cmdinfo->flags) ? 16 : 8; + cmd.address_bits = cmdinfo->addrlen * 8; + cmd.dummy_cycles = 0; + cmd.dtr = QSPICMD_ISDTR(cmdinfo->flags); + cmd.write = !QSPICMD_ISREAD(cmdinfo->flags); + return xspi_transfer(priv, &cmd); +} + +/**************************************************************************** + * Name: xspi_memory + * + * Description: + * Perform one QSPI memory transfer. + * + ****************************************************************************/ + +static int xspi_memory(struct qspi_dev_s *dev, + struct qspi_meminfo_s *meminfo) +{ + struct stm32_xspidev_s *priv = (struct stm32_xspidev_s *)dev; + struct stm32_xspi_cmd_s cmd; + + if (meminfo == NULL || meminfo->addrlen > 4 || + meminfo->buflen == 0 || meminfo->buffer == NULL || + meminfo->dummies > 31) + { + return -EINVAL; + } + + cmd.instruction = meminfo->cmd; + cmd.address = meminfo->addr; + cmd.buffer = meminfo->buffer; + cmd.buflen = meminfo->buflen; + cmd.instruction_lines = xspi_instruction_lines(meminfo->flags, true); + cmd.address_lines = meminfo->addrlen == 0 ? 0 : + QSPIMEM_ISOCTALIO(meminfo->flags) ? 8 : + QSPIMEM_ISQUADIO(meminfo->flags) ? 4 : + QSPIMEM_ISDUALIO(meminfo->flags) ? 2 : 1; + cmd.data_lines = QSPIMEM_ISOCTALIO(meminfo->flags) ? 8 : + (QSPIMEM_ISQUADIO(meminfo->flags) || + QSPIMEM_ISQUADDATA(meminfo->flags)) ? 4 : + QSPIMEM_ISDUALIO(meminfo->flags) ? 2 : 1; + cmd.instruction_bits = QSPIMEM_ISIOCTAL(meminfo->flags) ? 16 : 8; + cmd.address_bits = meminfo->addrlen * 8; + cmd.dummy_cycles = meminfo->dummies; + cmd.dtr = QSPIMEM_ISDTR(meminfo->flags); + cmd.write = QSPIMEM_ISWRITE(meminfo->flags); + return xspi_transfer(priv, &cmd); +} + +/**************************************************************************** + * Name: xspi_alloc + * + * Description: + * Allocate a buffer suitable for XSPI transfers. + * + ****************************************************************************/ + +static void *xspi_alloc(struct qspi_dev_s *dev, size_t buflen) +{ + UNUSED(dev); + return kmm_malloc(buflen); +} + +/**************************************************************************** + * Name: xspi_free + * + * Description: + * Free a buffer allocated by xspi_alloc. + * + ****************************************************************************/ + +static void xspi_free(struct qspi_dev_s *dev, void *buffer) +{ + UNUSED(dev); + kmm_free(buffer); +} + +/**************************************************************************** + * Name: xspi_mmap_cmd + * + * Description: + * Program the read or write command registers for memory-mapped mode. + * + ****************************************************************************/ + +static int xspi_mmap_cmd(struct stm32_xspidev_s *priv, + const struct qspi_meminfo_s *meminfo, + bool write) +{ + struct stm32_xspi_cmd_s cmd; + uint32_t ccr_offset = write ? STM32_XSPI_WCCR_OFFSET : + STM32_XSPI_CCR_OFFSET; + uint32_t tcr_offset = write ? STM32_XSPI_WTCR_OFFSET : + STM32_XSPI_TCR_OFFSET; + uint32_t ir_offset = write ? STM32_XSPI_WIR_OFFSET : + STM32_XSPI_IR_OFFSET; + uint32_t ccr; + uint32_t tcr; + int ret; + + if (meminfo == NULL || meminfo->addrlen == 0 || meminfo->addrlen > 4 || + meminfo->dummies > 31) + { + return -EINVAL; + } + + cmd.instruction = meminfo->cmd; + cmd.address = 0; + cmd.buffer = NULL; + cmd.buflen = 0; + cmd.instruction_lines = xspi_instruction_lines(meminfo->flags, true); + cmd.address_lines = QSPIMEM_ISOCTALIO(meminfo->flags) ? 8 : + QSPIMEM_ISQUADIO(meminfo->flags) ? 4 : + QSPIMEM_ISDUALIO(meminfo->flags) ? 2 : 1; + cmd.data_lines = QSPIMEM_ISOCTALIO(meminfo->flags) ? 8 : + (QSPIMEM_ISQUADIO(meminfo->flags) || + QSPIMEM_ISQUADDATA(meminfo->flags)) ? 4 : + QSPIMEM_ISDUALIO(meminfo->flags) ? 2 : 1; + cmd.instruction_bits = QSPIMEM_ISIOCTAL(meminfo->flags) ? 16 : 8; + cmd.address_bits = meminfo->addrlen * 8; + cmd.dummy_cycles = meminfo->dummies; + cmd.dtr = QSPIMEM_ISDTR(meminfo->flags); + cmd.write = write; + + ret = xspi_ccr(&cmd, &ccr); + if (ret < 0) + { + return ret; + } + + tcr = XSPI_TCR_DCYC(cmd.dummy_cycles); + if (priv->sample_shift && !cmd.dtr) + { + tcr |= XSPI_TCR_SSHIFT; + } + + xspi_putreg(priv, ccr_offset, ccr); + xspi_putreg(priv, tcr_offset, tcr); + xspi_putreg(priv, ir_offset, cmd.instruction); + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_xspi_initialize + * + * Description: + * Initialize an XSPI controller. + * + * Input Parameters: + * intf - XSPI controller number (1 or 2) + * config - XSPI controller configuration + * + * Returned Value: + * Valid QSPI device structure on success; NULL on failure. + * + ****************************************************************************/ + +struct qspi_dev_s * +stm32_xspi_initialize(int intf, const struct stm32_xspi_config_s *config) +{ + struct stm32_xspidev_s *priv = xspi_device(intf); + int ret; + + if (priv == NULL || config == NULL) + { + return NULL; + } + + if (!priv->initialized) + { + ret = xspi_hw_initialize(priv); + if (ret < 0) + { + return NULL; + } + + priv->initialized = true; + } + + if (xspi_configure(priv, config) < 0) + { + return NULL; + } + + return &priv->qspi; +} + +/**************************************************************************** + * Name: stm32_xspi_enter_memorymapped + * + * Description: + * Configure the XSPI controller for memory-mapped access. + * + * Input Parameters: + * dev - QSPI device + * readinfo - Memory transfer parameters used for reading + * writeinfo - Memory transfer parameters used for writing; may be NULL + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int stm32_xspi_enter_memorymapped( + struct qspi_dev_s *dev, + const struct qspi_meminfo_s *readinfo, + const struct qspi_meminfo_s *writeinfo) +{ + struct stm32_xspidev_s *priv = (struct stm32_xspidev_s *)dev; + uint32_t cr; + int ret; + + if (dev == NULL || dev->ops != &g_xspi_ops) + { + return -EINVAL; + } + + ret = xspi_lock(dev, true); + if (ret < 0) + { + return ret; + } + + if (priv->memmap) + { + xspi_lock(dev, false); + return OK; + } + + ret = xspi_wait(priv, XSPI_SR_BUSY, 0); + if (ret < 0) + { + goto errout; + } + + cr = xspi_getreg(priv, STM32_XSPI_CR_OFFSET); + xspi_putreg(priv, STM32_XSPI_CR_OFFSET, cr & ~XSPI_CR_EN); + + ret = xspi_mmap_cmd(priv, readinfo, false); + if (ret == OK && writeinfo != NULL) + { + ret = xspi_mmap_cmd(priv, writeinfo, true); + } + + if (ret < 0) + { + xspi_putreg(priv, STM32_XSPI_CR_OFFSET, cr); + goto errout; + } + + cr &= ~XSPI_CR_FMODE_MASK; + cr |= XSPI_CR_EN | XSPI_CR_FMODE_MEMORY; + xspi_putreg(priv, STM32_XSPI_CR_OFFSET, cr); + priv->memmap = true; + xspi_lock(dev, false); + return OK; + +errout: + xspi_lock(dev, false); + return ret; +} + +/**************************************************************************** + * Name: stm32_xspi_exit_memorymapped + * + * Description: + * Take the XSPI device out of memory-mapped mode. + * + * Input Parameters: + * dev - QSPI device + * + * Returned Value: + * None + * + ****************************************************************************/ + +void stm32_xspi_exit_memorymapped(struct qspi_dev_s *dev) +{ + struct stm32_xspidev_s *priv = (struct stm32_xspidev_s *)dev; + + if (dev == NULL || dev->ops != &g_xspi_ops) + { + return; + } + + xspi_lock(dev, true); + modifyreg32(priv->base + STM32_XSPI_CR_OFFSET, 0, XSPI_CR_ABORT); + xspi_wait(priv, XSPI_SR_BUSY, 0); + modifyreg32(priv->base + STM32_XSPI_CR_OFFSET, + XSPI_CR_ABORT | XSPI_CR_FMODE_MASK, + XSPI_CR_FMODE_WRITE); + priv->memmap = false; + xspi_lock(dev, false); +} + +#endif /* CONFIG_STM32_XSPI */ diff --git a/arch/arm/src/stm32h7/stm32_xspi.h b/arch/arm/src/stm32h7/stm32_xspi.h new file mode 100644 index 00000000000..de30b098f24 --- /dev/null +++ b/arch/arm/src/stm32h7/stm32_xspi.h @@ -0,0 +1,163 @@ +/**************************************************************************** + * arch/arm/src/stm32h7/stm32_xspi.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32H7_STM32_XSPI_H +#define __ARCH_ARM_SRC_STM32H7_STM32_XSPI_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#include <nuttx/spi/qspi.h> + +#ifdef CONFIG_STM32_XSPI + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +enum stm32_xspi_memtype_e +{ + STM32_XSPI_MEMTYPE_MICRON = 0, + STM32_XSPI_MEMTYPE_MACRONIX = 1, + STM32_XSPI_MEMTYPE_APMEM = 2, + STM32_XSPI_MEMTYPE_MACRONIX_RAM = 3, + STM32_XSPI_MEMTYPE_HYPERBUS = 4, + STM32_XSPI_MEMTYPE_APMEM_16BIT = 6 +}; + +enum stm32_xspi_iomport_e +{ + STM32_XSPI_IOM_PORT1 = 0, + STM32_XSPI_IOM_PORT2 = 1 +}; + +enum stm32_xspi_cs_e +{ + STM32_XSPI_CS_DISABLED = 0, + STM32_XSPI_CS_NCS1, + STM32_XSPI_CS_NCS2 +}; + +struct stm32_xspi_config_s +{ + enum stm32_xspi_memtype_e memtype; + enum stm32_xspi_iomport_e iomport; + enum stm32_xspi_cs_e cs_override; + uint8_t devsize; + uint8_t csht; + uint8_t prescaler; + uint8_t fthres; + uint8_t wrapsize; + uint8_t maxtran; + uint8_t csbound; + uint16_t req2ack; + uint32_t refresh; + bool clock_mode3; + bool free_running; + bool sample_shift; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: stm32_xspi_initialize + * + * Description: + * Initialize an XSPI controller. + * + * Input Parameters: + * intf - XSPI controller number (1 or 2) + * config - XSPI controller configuration + * + * Returned Value: + * Valid QSPI device structure on success; NULL on failure. + * + ****************************************************************************/ + +struct qspi_dev_s * +stm32_xspi_initialize(int intf, const struct stm32_xspi_config_s *config); + +/**************************************************************************** + * Name: stm32_xspi_enter_memorymapped + * + * Description: + * Configure the XSPI controller for memory-mapped access. + * + * Input Parameters: + * dev - QSPI device + * readinfo - Memory transfer parameters used for reading + * writeinfo - Memory transfer parameters used for writing; may be NULL + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int stm32_xspi_enter_memorymapped( + struct qspi_dev_s *dev, + const struct qspi_meminfo_s *readinfo, + const struct qspi_meminfo_s *writeinfo); + +/**************************************************************************** + * Name: stm32_xspi_exit_memorymapped + * + * Description: + * Take the XSPI device out of memory-mapped mode. + * + * Input Parameters: + * dev - QSPI device + * + * Returned Value: + * None + * + ****************************************************************************/ + +void stm32_xspi_exit_memorymapped(struct qspi_dev_s *dev); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_STM32_XSPI */ +#endif /* __ARCH_ARM_SRC_STM32H7_STM32_XSPI_H */
