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

Reply via email to