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;
