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
The following commit(s) were added to refs/heads/master by this push:
new c046c337c17 arch/arm/stm32h5: Add EDATA flash driver
c046c337c17 is described below
commit c046c337c179b55d0e400d7018e35f0528f65b0e
Author: Darryl Ring <[email protected]>
AuthorDate: Thu Sep 24 14:05:37 2026 -0700
arch/arm/stm32h5: Add EDATA flash driver
Add support for high-cycle flash which can be used for EEPROM emulation.
Assisted-by: Claude:claude-opus-5.5
Signed-off-by: Darryl Ring <[email protected]>
---
arch/arm/src/stm32h5/CMakeLists.txt | 6 +-
arch/arm/src/stm32h5/Kconfig | 17 +
arch/arm/src/stm32h5/Make.defs | 6 +-
arch/arm/src/stm32h5/hardware/stm32_sbs.h | 4 +
arch/arm/src/stm32h5/hardware/stm32h5xxx_flash.h | 8 +-
.../src/stm32h5/hardware/stm32h5xxx_memorymap.h | 2 +
arch/arm/src/stm32h5/stm32_edata.c | 369 +++++++++++++++
.../src/stm32h5/{stm32_flash.h => stm32_edata.h} | 52 +-
arch/arm/src/stm32h5/stm32_flash.h | 27 ++
arch/arm/src/stm32h5/stm32h563xx_flash.c | 525 ++++++++++++++++++++-
10 files changed, 987 insertions(+), 29 deletions(-)
diff --git a/arch/arm/src/stm32h5/CMakeLists.txt
b/arch/arm/src/stm32h5/CMakeLists.txt
index 244ca4ab298..654455e0780 100644
--- a/arch/arm/src/stm32h5/CMakeLists.txt
+++ b/arch/arm/src/stm32h5/CMakeLists.txt
@@ -24,10 +24,14 @@ set(SRCS)
# Common ARM and Cortex-M33 files
-if(CONFIG_STM32_PROGMEM)
+if(CONFIG_STM32_PROGMEM OR CONFIG_STM32_EDATA)
list(APPEND SRCS stm32_flash.c)
endif()
+if(CONFIG_STM32_EDATA)
+ list(APPEND SRCS stm32_edata.c)
+endif()
+
# Required STM32H5 files
list(
diff --git a/arch/arm/src/stm32h5/Kconfig b/arch/arm/src/stm32h5/Kconfig
index 672874f8387..d83f93f4d56 100644
--- a/arch/arm/src/stm32h5/Kconfig
+++ b/arch/arm/src/stm32h5/Kconfig
@@ -231,4 +231,21 @@ config STM32H5_IO_CONFIG_A
comment "STM32H5 SRAM2 Options"
+config STM32_SBS
+ bool
+ default n
+
+config STM32_EDATA
+ bool "Flash high-cycle data (EDATA) MTD driver"
+ default n
+ depends on STM32_STM32H563XX
+ select MTD
+ select STM32_SBS
+ ---help---
+ Provides stm32_edata_initialize(), which configures the last
+ sectors of a flash bank as high-cycle data (EDATA) and returns
an
+ MTD device for them. EDATA is rated for many more erase cycles
+ than user flash, which makes it suitable for configuration
storage
+ such as NVS (MTD_CONFIG_NVS).
+
endif # ARCH_CHIP_STM32H5
diff --git a/arch/arm/src/stm32h5/Make.defs b/arch/arm/src/stm32h5/Make.defs
index 7d3fd06b0b3..4dead210dc0 100644
--- a/arch/arm/src/stm32h5/Make.defs
+++ b/arch/arm/src/stm32h5/Make.defs
@@ -30,10 +30,14 @@ HEAD_ASRC =
include armv8-m/Make.defs
include common/stm32/Make.defs
-ifeq ($(CONFIG_STM32_PROGMEM),y)
+ifneq ($(CONFIG_STM32_PROGMEM)$(CONFIG_STM32_EDATA),)
CHIP_CSRCS += stm32_flash.c
endif
+ifeq ($(CONFIG_STM32_EDATA),y)
+CHIP_CSRCS += stm32_edata.c
+endif
+
# Required STM32H5 files
CHIP_CSRCS += stm32_gpio.c stm32_irq.c stm32_lowputc.c stm32_rcc.c
diff --git a/arch/arm/src/stm32h5/hardware/stm32_sbs.h
b/arch/arm/src/stm32h5/hardware/stm32_sbs.h
index 32b7b2942b3..e18ae7046e4 100644
--- a/arch/arm/src/stm32h5/hardware/stm32_sbs.h
+++ b/arch/arm/src/stm32h5/hardware/stm32_sbs.h
@@ -86,6 +86,10 @@
/* Register Bitfield Definitions ********************************************/
+/* ECC NMI mask register (ECCNMIR) */
+
+#define SBS_ECCNMIR_ECCNMI_MASK_EN (1 << 0) /* Bit 0: Flash ECC NMI masked */
+
/* Product mode and configuration register (PMCR) */
#define SBS_PMCR_PB6_FMP (1 << 16) /* Fast-mode Plus on PB6*/
diff --git a/arch/arm/src/stm32h5/hardware/stm32h5xxx_flash.h
b/arch/arm/src/stm32h5/hardware/stm32h5xxx_flash.h
index 78fffb5e695..a2d76231206 100644
--- a/arch/arm/src/stm32h5/hardware/stm32h5xxx_flash.h
+++ b/arch/arm/src/stm32h5/hardware/stm32h5xxx_flash.h
@@ -509,7 +509,7 @@
/* Flash Data Sector Configuration Bank 1 (Current) Register (EDATA1R_CUR) */
#define FLASH_EDATA1R_CUR_EDATA1_STRT_SHIFT (0)
-#define FLASH_EDATA1R_CUR_EDATA1_STRT_MASK (3 <<
FLASH_EDATA1R_CUR_EDATA1_STRT_SHIFT)
+#define FLASH_EDATA1R_CUR_EDATA1_STRT_MASK (7 <<
FLASH_EDATA1R_CUR_EDATA1_STRT_SHIFT)
#define FLASH_EDATA1R_CUR_EDATA1_STRT(n) ((n-1) <<
FLASH_EDATA1R_CUR_EDATA1_STRT_SHIFT) /* n = 1..8 */
#define FLASH_EDATA1R_CUR_EDATA1_EN (1 << 15)
@@ -517,7 +517,7 @@
/* Flash Data Sector Configuration Bank 1 (Program) Register (EDATA1R_PRG) */
#define FLASH_EDATA1R_PRG_EDATA1_STRT_SHIFT (0)
-#define FLASH_EDATA1R_PRG_EDATA1_STRT_MASK (3 <<
FLASH_EDATA1R_PRG_EDATA1_STRT_SHIFT)
+#define FLASH_EDATA1R_PRG_EDATA1_STRT_MASK (7 <<
FLASH_EDATA1R_PRG_EDATA1_STRT_SHIFT)
#define FLASH_EDATA1R_PRG_EDATA1_STRT(n) ((n-1) <<
FLASH_EDATA1R_PRG_EDATA1_STRT_SHIFT) /* n = 1..8 */
#define FLASH_EDATA1R_PRG_EDATA1_EN (1 << 15)
@@ -600,7 +600,7 @@
/* Flash Data Sector Configuration Bank 2 (Current) Register (EDATA2R_CUR) */
#define FLASH_EDATA2R_CUR_EDATA2_STRT_SHIFT (0)
-#define FLASH_EDATA2R_CUR_EDATA2_STRT_MASK (3 <<
FLASH_EDATA2R_CUR_EDATA2_STRT_SHIFT)
+#define FLASH_EDATA2R_CUR_EDATA2_STRT_MASK (7 <<
FLASH_EDATA2R_CUR_EDATA2_STRT_SHIFT)
#define FLASH_EDATA2R_CUR_EDATA2_STRT(n) ((n-1) <<
FLASH_EDATA2R_CUR_EDATA2_STRT_SHIFT) /* n = 1..8 */
#define FLASH_EDATA2R_CUR_EDATA2_EN (1 << 15)
@@ -608,7 +608,7 @@
/* Flash Data Sector Configuration Bank 2 (Program) Register (EDATA2R_PRG) */
#define FLASH_EDATA2R_PRG_EDATA2_STRT_SHIFT (0)
-#define FLASH_EDATA2R_PRG_EDATA2_STRT_MASK (3 <<
FLASH_EDATA2R_PRG_EDATA2_STRT_SHIFT)
+#define FLASH_EDATA2R_PRG_EDATA2_STRT_MASK (7 <<
FLASH_EDATA2R_PRG_EDATA2_STRT_SHIFT)
#define FLASH_EDATA2R_PRG_EDATA2_STRT(n) ((n-1) <<
FLASH_EDATA2R_PRG_EDATA2_STRT_SHIFT) /* n = 1..8 */
#define FLASH_EDATA2R_PRG_EDATA2_EN (1 << 15)
diff --git a/arch/arm/src/stm32h5/hardware/stm32h5xxx_memorymap.h
b/arch/arm/src/stm32h5/hardware/stm32h5xxx_memorymap.h
index d36327ea3d2..33a90712aa0 100644
--- a/arch/arm/src/stm32h5/hardware/stm32h5xxx_memorymap.h
+++ b/arch/arm/src/stm32h5/hardware/stm32h5xxx_memorymap.h
@@ -49,6 +49,8 @@
#define STM32_FLASH_BANK1 0x08000000 /* 0x08000000-0x080fffff: Bank 1
(562/563/573xx devices) */
#define STM32_FLASH_BANK2 0x08100000 /* 0x08100000-0x081fffff: Bank 2
(562/563/573xx devices) */
#define STM32_FLASH_BASE STM32_FLASH_BANK1
+#define STM32_EDATA_BASE 0x09000000 /* 0x09000000-0x09017fff: Flash
high-cycle data (562/563/573xx devices) */
+
/* SRAM1, SRAM2 and SRAM3 are contiguous on all STM32H5 parts, but their
* sizes are family-dependent (STM32_SRAMx_SIZE comes from chip.h).
*/
diff --git a/arch/arm/src/stm32h5/stm32_edata.c
b/arch/arm/src/stm32h5/stm32_edata.c
new file mode 100644
index 00000000000..14eb5a4bdbf
--- /dev/null
+++ b/arch/arm/src/stm32h5/stm32_edata.c
@@ -0,0 +1,369 @@
+/****************************************************************************
+ * arch/arm/src/stm32h5/stm32_edata.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.
+ *
+ ****************************************************************************/
+
+/* MTD driver for the flash high-cycle data (EDATA) area. The low-level
+ * access, including handling of the ECC errors raised when reading blank
+ * EDATA, is in stm32h563xx_flash.c.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <string.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/mtd/mtd.h>
+
+#include "stm32_edata.h"
+#include "stm32_flash.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define EDATA_BLOCK_SIZE 2
+#define EDATA_ERASESTATE 0xff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* struct mtd_dev_s must be first so that the two can be cast freely */
+
+struct stm32_edata_dev_s
+{
+ struct mtd_dev_s mtd;
+
+ int bank; /* Physical flash bank (1 or 2) */
+ unsigned int first; /* First EDATA sector used (0..7) */
+ unsigned int nsectors; /* Number of EDATA sectors */
+ uintptr_t base; /* Address of the first sector */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int edata_erase(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks);
+static ssize_t edata_bread(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks, FAR uint8_t *buf);
+static ssize_t edata_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks, FAR const uint8_t *buf);
+static ssize_t edata_read(FAR struct mtd_dev_s *dev, off_t offset,
+ size_t nbytes, FAR uint8_t *buf);
+#ifdef CONFIG_MTD_BYTE_WRITE
+static ssize_t edata_write(FAR struct mtd_dev_s *dev, off_t offset,
+ size_t nbytes, FAR const uint8_t *buf);
+#endif
+static int edata_ioctl(FAR struct mtd_dev_s *dev, int cmd,
+ unsigned long arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: edata_size
+ ****************************************************************************/
+
+static inline size_t edata_size(FAR struct stm32_edata_dev_s *priv)
+{
+ return priv->nsectors * STM32_EDATA_SECTOR_SIZE;
+}
+
+/****************************************************************************
+ * Name: edata_erase
+ ****************************************************************************/
+
+static int edata_erase(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks)
+{
+ FAR struct stm32_edata_dev_s *priv = (FAR struct stm32_edata_dev_s *)dev;
+ size_t i;
+ int ret;
+
+ if (startblock < 0 || startblock + nblocks > priv->nsectors)
+ {
+ return -EINVAL;
+ }
+
+ for (i = 0; i < nblocks; i++)
+ {
+ ret = stm32_flash_edata_erase(priv->bank,
+ priv->first + startblock + i);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ }
+
+ return nblocks;
+}
+
+/****************************************************************************
+ * Name: edata_bread
+ ****************************************************************************/
+
+static ssize_t edata_bread(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks, FAR uint8_t *buf)
+{
+ FAR struct stm32_edata_dev_s *priv = (FAR struct stm32_edata_dev_s *)dev;
+ off_t offset = startblock * EDATA_BLOCK_SIZE;
+ size_t nbytes = nblocks * EDATA_BLOCK_SIZE;
+ ssize_t ret;
+
+ if (startblock < 0 || offset + nbytes > edata_size(priv))
+ {
+ return -EINVAL;
+ }
+
+ ret = stm32_flash_edata_read(priv->base + offset, buf, nbytes);
+ return ret < 0 ? ret : nblocks;
+}
+
+/****************************************************************************
+ * Name: edata_bwrite
+ ****************************************************************************/
+
+static ssize_t edata_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks, FAR const uint8_t *buf)
+{
+ FAR struct stm32_edata_dev_s *priv = (FAR struct stm32_edata_dev_s *)dev;
+ off_t offset = startblock * EDATA_BLOCK_SIZE;
+ size_t nbytes = nblocks * EDATA_BLOCK_SIZE;
+ ssize_t ret;
+
+ if (startblock < 0 || offset + nbytes > edata_size(priv))
+ {
+ return -EINVAL;
+ }
+
+ ret = stm32_flash_edata_write(priv->base + offset, buf, nbytes);
+ return ret < 0 ? ret : nblocks;
+}
+
+/****************************************************************************
+ * Name: edata_read
+ *
+ * Description:
+ * Byte-oriented read. EDATA can only be read in half-words, so an odd
+ * leading or trailing byte is read through a bounce buffer.
+ *
+ ****************************************************************************/
+
+static ssize_t edata_read(FAR struct mtd_dev_s *dev, off_t offset,
+ size_t nbytes, FAR uint8_t *buf)
+{
+ FAR struct stm32_edata_dev_s *priv = (FAR struct stm32_edata_dev_s *)dev;
+ uint8_t hword[EDATA_BLOCK_SIZE];
+ size_t remaining = nbytes;
+ size_t len;
+ ssize_t ret;
+
+ if (offset < 0 || offset + nbytes > edata_size(priv))
+ {
+ return -EINVAL;
+ }
+
+ while (remaining > 0)
+ {
+ if ((offset & 1) || remaining == 1)
+ {
+ ret = stm32_flash_edata_read(priv->base + (offset & ~1), hword,
+ sizeof(hword));
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ *buf = hword[offset & 1];
+ len = 1;
+ }
+ else
+ {
+ len = remaining & ~1;
+ ret = stm32_flash_edata_read(priv->base + offset, buf, len);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ }
+
+ offset += len;
+ buf += len;
+ remaining -= len;
+ }
+
+ return nbytes;
+}
+
+/****************************************************************************
+ * Name: edata_write
+ *
+ * Description:
+ * Byte-oriented write. EDATA is programmed in half-words, each only once
+ * per erase, so only half-word aligned writes are supported.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MTD_BYTE_WRITE
+static ssize_t edata_write(FAR struct mtd_dev_s *dev, off_t offset,
+ size_t nbytes, FAR const uint8_t *buf)
+{
+ FAR struct stm32_edata_dev_s *priv = (FAR struct stm32_edata_dev_s *)dev;
+
+ if (offset < 0 || offset + nbytes > edata_size(priv) ||
+ ((offset | nbytes) & 1))
+ {
+ return -EINVAL;
+ }
+
+ return stm32_flash_edata_write(priv->base + offset, buf, nbytes);
+}
+#endif
+
+/****************************************************************************
+ * Name: edata_ioctl
+ ****************************************************************************/
+
+static int edata_ioctl(FAR struct mtd_dev_s *dev, int cmd,
+ unsigned long arg)
+{
+ FAR struct stm32_edata_dev_s *priv = (FAR struct stm32_edata_dev_s *)dev;
+ int ret = -EINVAL;
+
+ switch (cmd)
+ {
+ case MTDIOC_GEOMETRY:
+ {
+ FAR struct mtd_geometry_s *geo =
+ (FAR struct mtd_geometry_s *)arg;
+
+ if (geo != NULL)
+ {
+ memset(geo, 0, sizeof(*geo));
+ geo->blocksize = EDATA_BLOCK_SIZE;
+ geo->erasesize = STM32_EDATA_SECTOR_SIZE;
+ geo->neraseblocks = priv->nsectors;
+ strlcpy(geo->model, "stm32h5-edata", sizeof(geo->model));
+ ret = OK;
+ }
+ }
+ break;
+
+ case MTDIOC_BULKERASE:
+ ret = edata_erase(dev, 0, priv->nsectors);
+ if (ret > 0)
+ {
+ ret = OK;
+ }
+ break;
+
+ case MTDIOC_ERASESTATE:
+ {
+ FAR uint8_t *result = (FAR uint8_t *)arg;
+
+ if (result != NULL)
+ {
+ *result = EDATA_ERASESTATE;
+ ret = OK;
+ }
+ }
+ break;
+
+ default:
+ ret = -ENOTTY;
+ break;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_edata_initialize
+ *
+ * Description:
+ * Create an MTD device on the flash high-cycle data (EDATA) area of a
+ * physical flash bank. See stm32_edata.h.
+ *
+ ****************************************************************************/
+
+FAR struct mtd_dev_s *stm32_edata_initialize(int bank,
+ unsigned int nsectors)
+{
+ FAR struct stm32_edata_dev_s *priv;
+ int ret;
+
+ if ((bank != 1 && bank != 2) || nsectors < 1 ||
+ nsectors > STM32_EDATA_BANK_NSECTORS)
+ {
+ ferr("ERROR: Invalid EDATA bank %d or sector count %u\n",
+ bank, nsectors);
+ return NULL;
+ }
+
+ ret = stm32_flash_edata_configure(bank, nsectors);
+ if (ret < 0)
+ {
+ ferr("ERROR: Failed to configure EDATA in bank %d: %d\n", bank, ret);
+ return NULL;
+ }
+
+ priv = kmm_zalloc(sizeof(struct stm32_edata_dev_s));
+ if (priv == NULL)
+ {
+ return NULL;
+ }
+
+ priv->mtd.erase = edata_erase;
+ priv->mtd.bread = edata_bread;
+ priv->mtd.bwrite = edata_bwrite;
+ priv->mtd.read = edata_read;
+#ifdef CONFIG_MTD_BYTE_WRITE
+ priv->mtd.write = edata_write;
+#endif
+ priv->mtd.ioctl = edata_ioctl;
+ priv->mtd.name = "edata";
+
+ priv->bank = bank;
+ priv->first = STM32_EDATA_BANK_NSECTORS - nsectors;
+ priv->nsectors = nsectors;
+ priv->base = stm32_flash_edata_address(bank, priv->first);
+
+ finfo("EDATA bank %d: %u sectors at %08" PRIxPTR "\n",
+ bank, nsectors, priv->base);
+
+ return &priv->mtd;
+}
diff --git a/arch/arm/src/stm32h5/stm32_flash.h
b/arch/arm/src/stm32h5/stm32_edata.h
similarity index 54%
copy from arch/arm/src/stm32h5/stm32_flash.h
copy to arch/arm/src/stm32h5/stm32_edata.h
index fb1e5272d55..6fdceb1e3b9 100644
--- a/arch/arm/src/stm32h5/stm32_flash.h
+++ b/arch/arm/src/stm32h5/stm32_edata.h
@@ -1,5 +1,5 @@
/****************************************************************************
- * arch/arm/src/stm32h5/stm32_flash.h
+ * arch/arm/src/stm32h5/stm32_edata.h
*
* SPDX-License-Identifier: Apache-2.0
*
@@ -20,15 +20,14 @@
*
****************************************************************************/
-#ifndef __ARCH_ARM_SRC_STM32H5_STM32_FLASH_H
-#define __ARCH_ARM_SRC_STM32H5_STM32_FLASH_H
+#ifndef __ARCH_ARM_SRC_STM32H5_STM32_EDATA_H
+#define __ARCH_ARM_SRC_STM32H5_STM32_EDATA_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
-#include "hardware/stm32_flash.h"
/****************************************************************************
* Public Function Prototypes
@@ -45,22 +44,36 @@ extern "C"
#define EXTERN extern
#endif
-void stm32_flash_getopt(uint32_t *opt1, uint32_t *opt2);
+struct mtd_dev_s;
-int stm32_flash_optmodify(uint32_t clear1, uint32_t set1,
- uint32_t clear2, uint32_t set2);
-
-int stm32_flash_swapbanks(void);
-
-void stm32_flash_lock(void);
-
-void stm32_flash_unlock(void);
-
-int stm32_otp_write(const uint16_t *data, uint16_t len, uint32_t offset);
-
-int stm32_otp_read(uint16_t *data, uint16_t len, uint32_t offset);
+/****************************************************************************
+ * Name: stm32_edata_initialize
+ *
+ * Description:
+ * Create an MTD device on the flash high-cycle data (EDATA) area of a
+ * physical flash bank.
+ *
+ * If the bank does not already have nsectors of EDATA, the option bytes
+ * are reprogrammed and the affected sectors are erased. The last
+ * nsectors sectors of the bank are no longer usable as user flash, so the
+ * firmware image and anything else using those sectors (such as a second
+ * image bank) must stay clear of them.
+ *
+ * The MTD device has 6 KiB erase blocks, 2 byte read/write blocks, and an
+ * erase state of 0xff. Each 2 byte block can be written only once after
+ * the erase block that holds it is erased.
+ *
+ * Input Parameters:
+ * bank - The physical flash bank (1 or 2)
+ * nsectors - The number of EDATA sectors (1..8)
+ *
+ * Returned Value:
+ * The MTD device, or NULL on failure.
+ *
+ ****************************************************************************/
-uint32_t stm32_otp_getlockstatus(void);
+FAR struct mtd_dev_s *stm32_edata_initialize(int bank,
+ unsigned int nsectors);
#undef EXTERN
#if defined(__cplusplus)
@@ -68,5 +81,4 @@ uint32_t stm32_otp_getlockstatus(void);
#endif
#endif /* __ASSEMBLY__ */
-
-#endif /* __ARCH_ARM_SRC_STM32H5_STM32_FLASH_H */
+#endif /* __ARCH_ARM_SRC_STM32H5_STM32_EDATA_H */
diff --git a/arch/arm/src/stm32h5/stm32_flash.h
b/arch/arm/src/stm32h5/stm32_flash.h
index fb1e5272d55..ffa548fc0bd 100644
--- a/arch/arm/src/stm32h5/stm32_flash.h
+++ b/arch/arm/src/stm32h5/stm32_flash.h
@@ -28,6 +28,10 @@
****************************************************************************/
#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
#include "hardware/stm32_flash.h"
/****************************************************************************
@@ -62,6 +66,29 @@ int stm32_otp_read(uint16_t *data, uint16_t len, uint32_t
offset);
uint32_t stm32_otp_getlockstatus(void);
+/* Flash high-cycle data (EDATA) low-level access.
+ *
+ * EDATA can be enabled on the last 1..8 sectors of each physical bank.
+ * Each 8 KiB user flash sector becomes a 6 KiB EDATA sector, read and
+ * programmed in 16-bit half-words and mapped at STM32_EDATA_BASE.
+ *
+ * Banks are always physical banks (1 or 2). The EDATA window follows the
+ * SWAP_BANK option in the same way as the user flash, which these functions
+ * account for. Sectors are numbered 0..7 within the EDATA area of a bank,
+ * where 0 is the first of the last eight sectors of the bank.
+ */
+
+#define STM32_EDATA_BANK_NSECTORS 8
+#define STM32_EDATA_SECTOR_SIZE 6144
+
+int stm32_flash_edata_getconfig(int bank);
+int stm32_flash_edata_configure(int bank, unsigned int nsectors);
+uintptr_t stm32_flash_edata_address(int bank, unsigned int sector);
+int stm32_flash_edata_erase(int bank, unsigned int sector);
+ssize_t stm32_flash_edata_read(uintptr_t addr, void *buf, size_t count);
+ssize_t stm32_flash_edata_write(uintptr_t addr, const void *buf,
+ size_t count);
+
#undef EXTERN
#if defined(__cplusplus)
}
diff --git a/arch/arm/src/stm32h5/stm32h563xx_flash.c
b/arch/arm/src/stm32h5/stm32h563xx_flash.c
index b3160e0db42..26fead7b263 100644
--- a/arch/arm/src/stm32h5/stm32h563xx_flash.c
+++ b/arch/arm/src/stm32h5/stm32h563xx_flash.c
@@ -44,11 +44,18 @@
#include <stdbool.h>
#include <assert.h>
+#include <debug.h>
#include <errno.h>
+#include <inttypes.h>
+#include <string.h>
+#include <sys/param.h>
#include "hardware/stm32_flash.h"
#include "hardware/stm32_memorymap.h"
+#include "hardware/stm32_sbs.h"
#include "arm_internal.h"
+#include "stm32_flash.h"
+#include "stm32_icache.h"
/****************************************************************************
* Pre-processor Definitions
@@ -121,13 +128,18 @@
#define FLASH_OTP_WORDS_PER_BLOCK 32 /* 32 words per block */
#define OTP_WORD_SIZE 2 /* 16-bit words as per
manual */
-#define FLASH_NSSR_ALL_ERRORS (FLASH_NSSR_BSY | FLASH_NSSR_WBNE | \
- FLASH_NSSR_DBNE |FLASH_NSSR_EOP | \
- FLASH_NSSR_WRPERR | FLASH_NSSR_PGSERR | \
+#define FLASH_NSSR_ALL_ERRORS (FLASH_NSSR_WRPERR | FLASH_NSSR_PGSERR | \
FLASH_NSSR_STRBERR | FLASH_NSSR_INCERR | \
FLASH_NSSR_OBKERR | FLASH_NSSR_OBKWERR | \
FLASH_NSSR_OPTCHANGERR )
+/* Flash high-cycle data (EDATA) */
+
+#define EDATA_BANK_SIZE (STM32_EDATA_BANK_NSECTORS * \
+ STM32_EDATA_SECTOR_SIZE)
+#define EDATA_ERASEDVALUE 0xffffu
+#define EDATA_ECCD (FLASH_ECCDETR_ECCD | FLASH_ECCDETR_EDATA_ECC)
+
/****************************************************************************
* Private Types
****************************************************************************/
@@ -352,6 +364,139 @@ static void flash_lock_opt(void)
modifyreg32(STM32_FLASH_OPTCR, 0, FLASH_OPTCR_OPTLOCK);
}
+#ifdef CONFIG_STM32_EDATA
+
+/****************************************************************************
+ * Name: edata_logical_bank
+ *
+ * Description:
+ * Returns the logical bank (1 or 2) a physical bank is currently mapped
+ * to. The swap only takes effect at reset, so this is only valid until
+ * the SWAP_BANK option is next changed.
+ *
+ ****************************************************************************/
+
+static int edata_logical_bank(int bank)
+{
+ if (getreg32(STM32_FLASH_OPTSR_CUR) & FLASH_OPTSR_CUR_SWAP_BANK)
+ {
+ return 3 - bank;
+ }
+
+ return bank;
+}
+
+/****************************************************************************
+ * Name: edata_read_hword
+ *
+ * Description:
+ * Read one EDATA half-word. A blank (erased, never programmed) half-word
+ * reads as 0xffff. If the half-word is corrupt, for example because
+ * power was lost while it was being programmed, the raw data is returned.
+ *
+ * EDATA only supports 16 and 32-bit reads, so the ICACHE, which would
+ * fill whole lines, is disabled for the read. The ECC NMI that a blank
+ * half-word would raise is masked in the SBS and handled by checking
+ * ECCDETR instead. Both are restored afterwards.
+ *
+ ****************************************************************************/
+
+static uint16_t edata_read_hword(uintptr_t addr)
+{
+ irqstate_t flags;
+ uint16_t value;
+ uint32_t eccnmir;
+ bool eccerr = false;
+#ifdef CONFIG_STM32_ICACHE
+ bool icache;
+#endif
+
+ flags = up_irq_save();
+
+#ifdef CONFIG_STM32_ICACHE
+ icache = stm32_icache_enabled();
+ if (icache)
+ {
+ stm32_disable_icache();
+ }
+#endif
+
+ eccnmir = getreg32(STM32_SBS_ECCNMIR);
+ putreg32(eccnmir | SBS_ECCNMIR_ECCNMI_MASK_EN, STM32_SBS_ECCNMIR);
+
+ value = getreg16(addr);
+ UP_DSB();
+
+ if ((getreg32(STM32_FLASH_ECCDETR) & EDATA_ECCD) == EDATA_ECCD)
+ {
+ value = getreg32(STM32_FLASH_ECCDR) & FLASH_ECCDR_DATA_ECC_MASK;
+ eccerr = true;
+ putreg32(FLASH_ECCDETR_ECCD, STM32_FLASH_ECCDETR);
+ }
+
+ putreg32(eccnmir, STM32_SBS_ECCNMIR);
+
+#ifdef CONFIG_STM32_ICACHE
+ if (icache)
+ {
+ stm32_enable_icache();
+ }
+#endif
+
+ up_irq_restore(flags);
+
+ if (eccerr && value != EDATA_ERASEDVALUE)
+ {
+ ferr("ERROR: EDATA ECC error at %08" PRIxPTR ": %04x\n", addr, value);
+ }
+
+ return value;
+}
+
+/****************************************************************************
+ * Name: edata_erase
+ *
+ * Description:
+ * Erase one EDATA sector. Must be called with g_lock held.
+ *
+ ****************************************************************************/
+
+static int edata_erase(int bank, unsigned int sector)
+{
+ uint32_t snb = H5_FLASH_BANK_NBLOCKS - STM32_EDATA_BANK_NSECTORS + sector;
+ int ret = OK;
+
+ if (flash_wait_for_operation())
+ {
+ return -EIO;
+ }
+
+ flash_unlock_nscr();
+ modifyreg32(STM32_FLASH_NSCCR, 0, ~0);
+
+ /* BKSEL selects the physical bank */
+
+ modifyreg32(STM32_FLASH_NSCR, FLASH_NSCR_BKSEL | FLASH_NSCR_SNB_MASK,
+ FLASH_NSCR_SER | FLASH_NSCR_SNB(snb) |
+ (bank == 2 ? FLASH_NSCR_BKSEL : 0));
+ modifyreg32(STM32_FLASH_NSCR, 0, FLASH_NSCR_STRT);
+
+ if (flash_wait_for_operation() ||
+ (getreg32(STM32_FLASH_NSSR) & FLASH_NSSR_ALL_ERRORS))
+ {
+ ret = -EIO;
+ }
+
+ modifyreg32(STM32_FLASH_NSCR, FLASH_NSCR_SER | FLASH_NSCR_SNB_MASK |
+ FLASH_NSCR_BKSEL, 0);
+ modifyreg32(STM32_FLASH_NSCCR, 0, ~0);
+ flash_lock_nscr();
+
+ return ret;
+}
+
+#endif /* CONFIG_STM32_EDATA */
+
/****************************************************************************
* Name: stm32h5_otp_is_space_available
*
@@ -987,6 +1132,380 @@ uint32_t stm32_otp_getlockstatus(void)
return getreg32(STM32_FLASH_OTBPBLR_CUR);
}
+#ifdef CONFIG_STM32_EDATA
+
+/****************************************************************************
+ * Name: stm32_flash_edata_getconfig
+ *
+ * Description:
+ * Returns the number of sectors of a physical bank (1 or 2) that are
+ * currently configured as EDATA, 0 if EDATA is disabled in that bank, or
+ * a negated errno value.
+ *
+ ****************************************************************************/
+
+int stm32_flash_edata_getconfig(int bank)
+{
+ uint32_t regval;
+
+ if (bank == 1)
+ {
+ regval = getreg32(STM32_FLASH_EDATA1R_CUR);
+ }
+ else if (bank == 2)
+ {
+ regval = getreg32(STM32_FLASH_EDATA2R_CUR);
+ }
+ else
+ {
+ return -EINVAL;
+ }
+
+ /* The EDATA1R and EDATA2R fields are laid out identically */
+
+ if (!(regval & FLASH_EDATA1R_CUR_EDATA1_EN))
+ {
+ return 0;
+ }
+
+ return ((regval & FLASH_EDATA1R_CUR_EDATA1_STRT_MASK) >>
+ FLASH_EDATA1R_CUR_EDATA1_STRT_SHIFT) + 1;
+}
+
+/****************************************************************************
+ * Name: stm32_flash_edata_configure
+ *
+ * Description:
+ * Program the option bytes so that the last nsectors (0..8) sectors of a
+ * physical bank (1 or 2) are EDATA. Sectors that change between user
+ * flash and EDATA are erased. Nothing is done if the bank is already
+ * configured that way.
+ *
+ * This refuses to convert sectors holding the running image, but the
+ * other bank is not checked.
+ *
+ * Returned Value:
+ * Zero or a negated errno value:
+ *
+ * -EINVAL: Invalid bank or sector count
+ * -EBUSY: The sectors hold the running image
+ * -EIO: Programming the option bytes or erasing failed
+ *
+ ****************************************************************************/
+
+int stm32_flash_edata_configure(int bank, unsigned int nsectors)
+{
+ uintptr_t cur;
+ uintptr_t prg;
+ uintptr_t addr;
+ uint32_t regval;
+ unsigned int oldsectors;
+ unsigned int sector;
+ bool was_locked;
+ int ret;
+
+ if ((bank != 1 && bank != 2) || nsectors > STM32_EDATA_BANK_NSECTORS)
+ {
+ return -EINVAL;
+ }
+
+ ret = stm32_flash_edata_getconfig(bank);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ oldsectors = ret;
+ if (oldsectors == nsectors)
+ {
+ return OK;
+ }
+
+ /* Make sure the running image is not in any sector that changes type */
+
+ addr = STM32_FLASH_BASE +
+ (edata_logical_bank(bank) - 1) * H5_FLASH_BANKSIZE +
+ (H5_FLASH_BANK_NBLOCKS - MAX(oldsectors, nsectors)) *
+ FLASH_BLOCK_SIZE;
+
+ if (addr < (uintptr_t)_eronly + (uintptr_t)(_edata - _sdata))
+ {
+ ferr("ERROR: EDATA sectors overlap the running image\n");
+ return -EBUSY;
+ }
+
+ if (bank == 1)
+ {
+ cur = STM32_FLASH_EDATA1R_CUR;
+ prg = STM32_FLASH_EDATA1R_PRG;
+ }
+ else
+ {
+ cur = STM32_FLASH_EDATA2R_CUR;
+ prg = STM32_FLASH_EDATA2R_PRG;
+ }
+
+ regval = 0;
+ if (nsectors > 0)
+ {
+ regval = FLASH_EDATA1R_PRG_EDATA1_EN |
+ FLASH_EDATA1R_PRG_EDATA1_STRT(nsectors);
+ }
+
+ ret = nxmutex_lock(&g_lock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (flash_wait_for_operation())
+ {
+ ret = -EIO;
+ goto exit_with_lock;
+ }
+
+ was_locked = flash_unlock_opt();
+
+ modifyreg32(prg, FLASH_EDATA1R_PRG_EDATA1_EN |
+ FLASH_EDATA1R_PRG_EDATA1_STRT_MASK, regval);
+ modifyreg32(STM32_FLASH_OPTCR, 0, FLASH_OPTCR_OPTSTRT);
+
+ if (flash_wait_for_operation())
+ {
+ ret = -EIO;
+ }
+
+ if (was_locked)
+ {
+ flash_lock_opt();
+ }
+
+ if (ret == OK &&
+ (getreg32(cur) & (FLASH_EDATA1R_CUR_EDATA1_EN |
+ FLASH_EDATA1R_CUR_EDATA1_STRT_MASK)) != regval)
+ {
+ ferr("ERROR: EDATA%dR option bytes not updated\n", bank);
+ ret = -EIO;
+ }
+
+ /* Erase the sectors that changed type. Their contents are unreadable
+ * with the other ECC layout.
+ */
+
+ for (sector = STM32_EDATA_BANK_NSECTORS - MAX(oldsectors, nsectors);
+ ret == OK &&
+ sector < STM32_EDATA_BANK_NSECTORS - MIN(oldsectors, nsectors);
+ sector++)
+ {
+ ret = edata_erase(bank, sector);
+ }
+
+exit_with_lock:
+ nxmutex_unlock(&g_lock);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: stm32_flash_edata_address
+ *
+ * Description:
+ * Returns the address of an EDATA sector (0..7) of a physical bank (1 or
+ * 2), or 0 if the arguments are invalid. The sector must be enabled with
+ * stm32_flash_edata_configure() before it is accessed.
+ *
+ ****************************************************************************/
+
+uintptr_t stm32_flash_edata_address(int bank, unsigned int sector)
+{
+ if ((bank != 1 && bank != 2) || sector >= STM32_EDATA_BANK_NSECTORS)
+ {
+ return 0;
+ }
+
+ return STM32_EDATA_BASE +
+ (edata_logical_bank(bank) - 1) * EDATA_BANK_SIZE +
+ sector * STM32_EDATA_SECTOR_SIZE;
+}
+
+/****************************************************************************
+ * Name: stm32_flash_edata_erase
+ *
+ * Description:
+ * Erase an EDATA sector (0..7) of a physical bank (1 or 2).
+ *
+ ****************************************************************************/
+
+int stm32_flash_edata_erase(int bank, unsigned int sector)
+{
+ int ret;
+
+ if ((bank != 1 && bank != 2) || sector >= STM32_EDATA_BANK_NSECTORS)
+ {
+ return -EINVAL;
+ }
+
+ ret = nxmutex_lock(&g_lock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = edata_erase(bank, sector);
+
+ nxmutex_unlock(&g_lock);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: stm32_flash_edata_read
+ *
+ * Description:
+ * Read from EDATA. The address and count must be half-word aligned.
+ * Blank half-words read as 0xffff.
+ *
+ * Returned Value:
+ * The number of bytes read or a negated errno value.
+ *
+ ****************************************************************************/
+
+ssize_t stm32_flash_edata_read(uintptr_t addr, void *buf, size_t count)
+{
+ uint8_t *dest = buf;
+ uint16_t value;
+ size_t i;
+ int ret;
+
+ if ((addr | count) & 1)
+ {
+ return -EINVAL;
+ }
+
+ if (addr < STM32_EDATA_BASE ||
+ addr + count > STM32_EDATA_BASE + 2 * EDATA_BANK_SIZE)
+ {
+ return -EFAULT;
+ }
+
+ ret = nxmutex_lock(&g_lock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ for (i = 0; i < count; i += sizeof(value))
+ {
+ value = edata_read_hword(addr + i);
+ memcpy(dest + i, &value, sizeof(value));
+ }
+
+ nxmutex_unlock(&g_lock);
+ return count;
+}
+
+/****************************************************************************
+ * Name: stm32_flash_edata_write
+ *
+ * Description:
+ * Program EDATA. The address and count must be half-word aligned.
+ *
+ * Each half-word can only be programmed once after an erase. Half-words
+ * that already hold the requested value are skipped, so writing 0xffff
+ * leaves a blank half-word blank. Programming a half-word that holds a
+ * different value fails with -EIO.
+ *
+ * Returned Value:
+ * The number of bytes written or a negated errno value.
+ *
+ ****************************************************************************/
+
+ssize_t stm32_flash_edata_write(uintptr_t addr, const void *buf,
+ size_t count)
+{
+ const uint8_t *src = buf;
+ uint16_t value;
+ uint16_t current;
+ size_t i;
+ int ret;
+
+ if ((addr | count) & 1)
+ {
+ return -EINVAL;
+ }
+
+ if (addr < STM32_EDATA_BASE ||
+ addr + count > STM32_EDATA_BASE + 2 * EDATA_BANK_SIZE)
+ {
+ return -EFAULT;
+ }
+
+ ret = nxmutex_lock(&g_lock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (flash_wait_for_operation())
+ {
+ ret = -EIO;
+ goto exit_with_lock;
+ }
+
+ flash_unlock_nscr();
+ modifyreg32(STM32_FLASH_NSCCR, 0, ~0);
+
+ for (i = 0; i < count; i += sizeof(value))
+ {
+ memcpy(&value, src + i, sizeof(value));
+
+ current = edata_read_hword(addr + i);
+ if (current == value)
+ {
+ continue;
+ }
+
+ if (current != EDATA_ERASEDVALUE)
+ {
+ ret = -EIO;
+ break;
+ }
+
+ /* EDATA is programmed one half-word at a time */
+
+ modifyreg32(STM32_FLASH_NSCR, 0, FLASH_NSCR_PG);
+ UP_MB();
+
+ putreg16(value, addr + i);
+ UP_MB();
+
+ if (flash_wait_for_operation() ||
+ (getreg32(STM32_FLASH_NSSR) & FLASH_NSSR_ALL_ERRORS))
+ {
+ ret = -EIO;
+ }
+
+ modifyreg32(STM32_FLASH_NSCR, FLASH_NSCR_PG, 0);
+
+ if (ret == OK && edata_read_hword(addr + i) != value)
+ {
+ ret = -EIO;
+ }
+
+ if (ret < 0)
+ {
+ break;
+ }
+ }
+
+ modifyreg32(STM32_FLASH_NSCCR, 0, ~0);
+ flash_lock_nscr();
+
+exit_with_lock:
+ nxmutex_unlock(&g_lock);
+ return ret < 0 ? ret : count;
+}
+
+#endif /* CONFIG_STM32_EDATA */
+
#ifdef CONFIG_ARCH_HAVE_PROGMEM
/* up_progmem_x functions defined in nuttx/include/nuttx/progmem.h