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 65775f26a7182942eb74eefc1ec2e8476b8ccc0c
Author: Liam Howatt <[email protected]>
AuthorDate: Mon Sep 14 09:49:21 2026 -0400

    arch/arm/stm32h5: Add OTP support.
    
    Add an API for OTP (one-time programmable) memory on stm32h5.
    There are OTP APIs for non-STM32 platforms.
    There is no other API for STM32 so far with these names.
    
    Implement it in stm32h563xx_flash.c since the progmem abstraction
    also lives there.
    
    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);
    The API allows cross-block reads/writes that don't necessarily
    start/end at block boundaries.
    The type of `data` is uint16_t * to express to the caller that the
    pointer should be 2-aligned. The natural size of OTP words is 16 bits.
    `len` is uint16_t for no strong reason. Preserve author's work.
    
    uint32_t stm32_otp_getlockstatus(void);
    Get a mask of blocks that are locked. A block being locked
    is considered as being one-time programmed.
    
    Co-authored-by: Mykhailo Sopiha <[email protected]>
    Signed-off-by: Liam Howatt <[email protected]>
---
 .../src/stm32h5/hardware/stm32h5xxx_memorymap.h    |   4 +
 arch/arm/src/stm32h5/stm32_flash.h                 |   6 +
 arch/arm/src/stm32h5/stm32h563xx_flash.c           | 494 +++++++++++++++++++++
 3 files changed, 504 insertions(+)

diff --git a/arch/arm/src/stm32h5/hardware/stm32h5xxx_memorymap.h 
b/arch/arm/src/stm32h5/hardware/stm32h5xxx_memorymap.h
index d36327ea3d2..d1e19f693d3 100644
--- a/arch/arm/src/stm32h5/hardware/stm32h5xxx_memorymap.h
+++ b/arch/arm/src/stm32h5/hardware/stm32h5xxx_memorymap.h
@@ -65,6 +65,10 @@
 #define STM32_SYSMEM_FSIZE   0x08FFF80C     /* Size of Flash memory in Kbytes. 
*/
 #define STM32_SYSMEM_PACKAGE 0x08FFF80E     /* Indicates the device's package 
type. */
 
+/* OTP Base Addresses *******************************************************/
+
+#define STM32_OTP_BASE       0x08fff000     /* Base address of OTP Area */
+
 /* Peripheral Base Addresses ************************************************/
 
 #define STM32_APB1_BASE      0x40000000     /* 0x40000000-0x4000fbff: APB1 */
diff --git a/arch/arm/src/stm32h5/stm32_flash.h 
b/arch/arm/src/stm32h5/stm32_flash.h
index 3dd4f10fd4e..fb1e5272d55 100644
--- a/arch/arm/src/stm32h5/stm32_flash.h
+++ b/arch/arm/src/stm32h5/stm32_flash.h
@@ -56,6 +56,12 @@ 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);
+
+uint32_t stm32_otp_getlockstatus(void);
+
 #undef EXTERN
 #if defined(__cplusplus)
 }
diff --git a/arch/arm/src/stm32h5/stm32h563xx_flash.c 
b/arch/arm/src/stm32h5/stm32h563xx_flash.c
index 26c78a2778f..b3160e0db42 100644
--- a/arch/arm/src/stm32h5/stm32h563xx_flash.c
+++ b/arch/arm/src/stm32h5/stm32h563xx_flash.c
@@ -115,6 +115,19 @@
 #define FLASH_ERASEDVALUE_DW  0xffffffffu
 #define FLASH_TIMEOUT_VALUE   5000000   /* 5s */
 
+#define FLASH_OTP_SIZE              2048           /* OTP area size: 2048 
bytes */
+#define FLASH_OTP_BLOCK_SIZE        64             /* 32 words * 2 bytes = 64 
bytes per block */
+#define FLASH_OTP_TOTAL_BLOCKS      32             /* Total OTP blocks (0-31) 
*/
+#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 |  \
+                                FLASH_NSSR_STRBERR | FLASH_NSSR_INCERR | \
+                                FLASH_NSSR_OBKERR | FLASH_NSSR_OBKWERR | \
+                                FLASH_NSSR_OPTCHANGERR )
+
 /****************************************************************************
  * Private Types
  ****************************************************************************/
@@ -339,6 +352,255 @@ static void flash_lock_opt(void)
   modifyreg32(STM32_FLASH_OPTCR, 0, FLASH_OPTCR_OPTLOCK);
 }
 
+/****************************************************************************
+ * Name: stm32h5_otp_is_space_available
+ *
+ * Description:
+ *   Validates if the memory region can be written. OTP memory may only be
+ *   written once, and after writing data to a specific block - this block
+ *   should be locked for writing to prevent potential overwrite attempt.
+ *   Even if the 64-bytes block was only used partially, it should be locked
+ *   because there is no mechanism to validate whether the memory was written
+ *   or not, so the entire block is locked after write even a single bit
+ *
+ * Returned Value:
+ *   True if there is enough consecutive bytes in OTP to store the data
+ *   False otherwise
+ *
+ ****************************************************************************/
+
+static bool stm32h5_otp_is_space_available(uint8_t start_block,
+    uint8_t end_block)
+{
+  uint32_t lockbl_cur = getreg32(STM32_FLASH_OTBPBLR_CUR);
+
+  for (uint8_t i = start_block; i <= end_block; i++)
+    {
+      if (lockbl_cur & (1 << i))
+        {
+          return false;
+        }
+    }
+
+  return true;
+}
+
+/****************************************************************************
+ * Name: stm32h5_otp_clear_errors
+ *
+ * Description:
+ *   Clear all OTP error flags from previous operations
+ *
+ * Returned Value:
+ *   Zero on success or negative error value
+ *
+ ****************************************************************************/
+
+static int stm32h5_otp_clear_errors(void)
+{
+  uint32_t error_flags = getreg32(STM32_FLASH_NSSR) & FLASH_NSSR_ALL_ERRORS;
+
+  UP_DSB();
+
+  if (error_flags != 0)
+    {
+      putreg32(error_flags, STM32_FLASH_NSCCR);
+
+      error_flags = getreg32(STM32_FLASH_NSSR) & FLASH_NSSR_ALL_ERRORS;
+      if (error_flags != 0)
+        {
+          return -EAGAIN;
+        }
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32h5_otp_lock_block
+ *
+ * Description:
+ *   Lock the OTP block to prevent further data changes
+ *
+ * Input Parameters:
+ *   block_number - the number of block to lock
+ *
+ * Returned Value:
+ *   Zero on success or negative error value
+ *
+ ****************************************************************************/
+
+static int stm32h5_otp_lock_block(uint8_t block_number)
+{
+  int ret;
+  uint32_t reg;
+  bool was_locked;
+  uint32_t lockbl_cur;
+
+  if (block_number >= FLASH_OTP_TOTAL_BLOCKS)
+    {
+      return -EINVAL;
+    }
+
+  lockbl_cur = getreg32(STM32_FLASH_OTBPBLR_CUR);
+  if (lockbl_cur & (1 << block_number))
+    {
+      /* Block is already locked */
+
+      return -EACCES;
+    }
+
+  /* Wait for any ongoing flash operations */
+
+  ret = flash_wait_for_operation();
+  if (ret != 0)
+    {
+      return -EBUSY;
+    }
+
+  /* Check that data buffer is empty */
+
+  reg = getreg32(STM32_FLASH_NSSR);
+  if (reg & FLASH_NSSR_DBNE)
+    {
+      return -EBUSY;
+    }
+
+  /* Unlock option bytes for programming */
+
+  was_locked = flash_unlock_opt();
+
+  /* Set the bit in the OTP block lock programming register */
+
+  modifyreg32(STM32_FLASH_OTBPBLR_PRG, 0, (1 << block_number));
+
+  /* Start the option bytes programming sequence */
+
+  modifyreg32(STM32_FLASH_OPTCR, 0, FLASH_OPTCR_OPTSTRT);
+
+  /* Wait for programming operation to complete */
+
+  while (getreg32(STM32_FLASH_NSSR) & FLASH_NSSR_BSY)
+    {
+    }
+
+  /* Check for programming errors */
+
+  reg = getreg32(STM32_FLASH_NSSR);
+  if (reg & FLASH_NSSR_ALL_ERRORS)
+    {
+      /* Clear errors and return failure */
+
+      putreg32(reg & FLASH_NSSR_ALL_ERRORS, STM32_FLASH_NSCCR);
+      ret = -EIO;
+    }
+  else
+    {
+      /* Verify the lock was applied */
+
+      lockbl_cur = getreg32(STM32_FLASH_OTBPBLR_CUR);
+      if (!(lockbl_cur & (1 << block_number)))
+        {
+          ret = -EIO;
+        }
+      else
+        {
+          ret = OK;
+        }
+    }
+
+  /* Re-lock option bytes if they were locked before */
+
+  if (was_locked)
+    {
+      flash_lock_opt();
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32h5_otp_write_word
+ *
+ * Description:
+ *   Write OTP word (16 bits total) following the manual sequence
+ *   Follows steps 1-7 from the STM32H5 reference manual. Locking
+ *   written block (as step 8) is done after all data is written
+ *
+ * Input Parameters:
+ *   otp_address - OTP address (must be 4-byte aligned)
+ *   data        - 16-bit data (one 16-bit words)
+ *
+ * Returned Value:
+ *   Zero on success or negative error value
+ *
+ ****************************************************************************/
+
+static int stm32h5_otp_write_word(uint32_t otp_address, const uint16_t *data)
+{
+  volatile uint16_t *otp_addr = (volatile uint16_t *)otp_address;
+  int ret;
+
+  /* Step 1: Check that no memory operations are ongoing */
+
+  ret = flash_wait_for_operation();
+  if (ret != OK)
+    {
+      return ret;
+    }
+
+  /* Verify data buffer is empty (DBNE bit) */
+
+  if (getreg32(STM32_FLASH_NSSR) & FLASH_NSSR_DBNE)
+    {
+      return -EBUSY;
+    }
+
+  /* Step 2: Check and clear all error flags */
+
+  ret = stm32h5_otp_clear_errors();
+  if (ret != OK)
+    {
+      return ret;
+    }
+
+  /* Step 3: Set PG bit in FLASH_NSCR register */
+
+  modifyreg32(STM32_FLASH_NSCR, 0, FLASH_NSCR_PG);
+
+  UP_DSB();
+  UP_ISB();
+
+  /* Step 5: Write OTP word (16 bits total) */
+
+  *otp_addr = *data;
+
+  UP_DSB();
+  UP_ISB();
+
+  /* Step 6: Wait for BSY bit to be cleared */
+
+  ret = flash_wait_for_operation();
+  if (ret != OK)
+    {
+      modifyreg32(STM32_FLASH_NSCR, FLASH_NSCR_PG, 0);
+      return ret;
+    }
+
+  /* Step 7: Clear PG bit */
+
+  modifyreg32(STM32_FLASH_NSCR, FLASH_NSCR_PG, 0);
+
+  /* Verify the write by reading back */
+
+  if (*otp_addr != *data)
+    {
+      return -EIO;
+    }
+
+  return OK;
+}
+
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
@@ -494,6 +756,237 @@ int stm32_flash_swapbanks(void)
   return 0;
 }
 
+/****************************************************************************
+ * Name: stm32_otp_write
+ *
+ * Description:
+ *   Writes data to OTP section starting from the offset.
+ *   The involved blocks will be locked afterward.
+ *
+ * Input Parameters:
+ *   data   - Pointer to data buffer
+ *   len    - Length in bytes of data to write
+ *   offset - 4-aligned offset in bytes within OTP area
+ *            (0 to FLASH_OTP_SIZE-4)
+ *
+ * Returned Value:
+ *   Zero on success or negative error value
+ *
+ ****************************************************************************/
+
+int stm32_otp_write(const uint16_t *data, uint16_t len, uint32_t offset)
+{
+  uint32_t otp_address;
+  uint16_t remaining_bytes;
+  int ret;
+  uint16_t i;
+  uint8_t start_block;
+  uint8_t end_block;
+  uint16_t words_to_write;
+
+  if (data == NULL || len == 0)
+    {
+      return -EINVAL;
+    }
+
+  if (offset + len > FLASH_OTP_SIZE)
+    {
+      return -ENOMEM;
+    }
+
+  /* Ensure 4-byte alignment for writing */
+
+  if (offset % 4 != 0)
+    {
+      return -EINVAL;
+    }
+
+  start_block = offset / FLASH_OTP_BLOCK_SIZE;
+  end_block = (offset + len - 1) / FLASH_OTP_BLOCK_SIZE;
+
+  /* Calculate actual OTP address */
+
+  otp_address = STM32_OTP_BASE + offset;
+
+  /* Calculate number of complete 16-bit words */
+
+  words_to_write = len / OTP_WORD_SIZE;
+  remaining_bytes = len % OTP_WORD_SIZE;
+
+  ret = nxmutex_lock(&g_lock);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  if (!stm32h5_otp_is_space_available(start_block, end_block))
+    {
+      nxmutex_unlock(&g_lock);
+      return -EACCES;
+    }
+
+  /* Unlock flash for programming */
+
+  flash_unlock_nscr();
+
+  /* Write complete 16-bit pairs (two 16-bit words each) */
+
+  for (i = 0; i < words_to_write; i++)
+    {
+      ret = stm32h5_otp_write_word(otp_address + (i * OTP_WORD_SIZE),
+          data + i);
+
+      if (ret != OK)
+        {
+          goto exit_with_unlock;
+        }
+    }
+
+  /* Handle remaining bytes (less than OTP_WORD_SIZE bytes) */
+
+  if (remaining_bytes > 0)
+    {
+      uint16_t write_word = 0xffff; /* Default erased value for unused bits */
+
+      /* Fill the remaining bytes */
+
+      memcpy(&write_word, data + words_to_write, remaining_bytes);
+
+      ret = stm32h5_otp_write_word(
+          otp_address + (words_to_write * OTP_WORD_SIZE), &write_word);
+      if (ret != OK)
+        {
+          goto exit_with_unlock;
+        }
+    }
+
+  for (i = start_block; i <= end_block; i++)
+    {
+      ret = stm32h5_otp_lock_block(i);
+      if (ret != OK)
+        {
+          break;
+        }
+    }
+
+exit_with_unlock:
+  flash_lock_nscr();
+  nxmutex_unlock(&g_lock);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: stm32_otp_read
+ *
+ * Description:
+ *   Reads data from OTP section starting from the offset
+ *
+ * Input Parameters:
+ *   data   - Pointer to data buffer to store read data.
+ *   len    - Length in bytes of data to read
+ *   offset - 4-aligned offset in bytes within OTP area
+ *            (0 to FLASH_OTP_SIZE-4)
+ *
+ * Returned Value:
+ *   Zero on success or negative error value
+ *
+ ****************************************************************************/
+
+int stm32_otp_read(uint16_t *data, uint16_t len, uint32_t offset)
+{
+  uint32_t otp_address;
+  uint16_t remaining_bytes;
+  int ret;
+  uint16_t i;
+  uint16_t words_to_read;
+
+  if (data == NULL || len == 0)
+    {
+      return -EINVAL;
+    }
+
+  if (offset + len > FLASH_OTP_SIZE)
+    {
+      return -ENOMEM;
+    }
+
+  /* Ensure 4-byte alignment for OTP reading */
+
+  if (offset % 4 != 0)
+    {
+      return -EINVAL;
+    }
+
+  /* Calculate actual OTP address */
+
+  otp_address = STM32_OTP_BASE + offset;
+
+  /* Calculate number of complete 16-bit words */
+
+  words_to_read = len / OTP_WORD_SIZE;
+  remaining_bytes = len % OTP_WORD_SIZE;
+
+  ret = nxmutex_lock(&g_lock);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Wait for any ongoing operations to complete */
+
+  ret = flash_wait_for_operation();
+  if (ret != OK)
+    {
+      nxmutex_unlock(&g_lock);
+      return ret;
+    }
+
+  /* Read complete 16-bit words */
+
+  for (i = 0; i < words_to_read; i++)
+    {
+      volatile uint16_t *otp_addr = (volatile uint16_t *)(otp_address +
+          (i * OTP_WORD_SIZE));
+      uint16_t *dest = data + i;
+
+      *dest = *otp_addr;
+    }
+
+  /* Handle remaining bytes (less than OTP_WORD_SIZE bytes) */
+
+  if (remaining_bytes > 0)
+    {
+      volatile uint16_t *otp_addr = (volatile uint16_t *)(otp_address +
+          (words_to_read * OTP_WORD_SIZE));
+      uint16_t read_data = *otp_addr;
+
+      /* Copy only the needed bytes */
+
+      memcpy(data + words_to_read, &read_data,
+          remaining_bytes);
+    }
+
+  nxmutex_unlock(&g_lock);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_otp_getlockstatus
+ *
+ * Description:
+ *   Get the lock status of all OTP blocks
+ *
+ * Returned Value:
+ *   32-bit value representing lock status of blocks 0-31
+ *
+ ****************************************************************************/
+
+uint32_t stm32_otp_getlockstatus(void)
+{
+  return getreg32(STM32_FLASH_OTBPBLR_CUR);
+}
+
 #ifdef CONFIG_ARCH_HAVE_PROGMEM
 
 /* up_progmem_x functions defined in nuttx/include/nuttx/progmem.h
@@ -527,6 +1020,7 @@ ssize_t up_progmem_getpage(size_t addr)
 size_t up_progmem_getaddress(size_t page)
 {
   struct stm32h5_flash_priv_s *priv;
+
   if (page >= H5_FLASH_NPAGES)
     {
       return SIZE_MAX;

Reply via email to