mirror of
https://github.com/apache/nuttx.git
synced 2026-10-07 22:35:22 +00:00
arch/arm/stm32h5: Add OTP/eFuse support
Some checks are pending
Build Documentation / build-html (push) Waiting to run
MemBrowse Memory Report / changes-filter (push) Waiting to run
MemBrowse Memory Report / load-targets (push) Waiting to run
MemBrowse Memory Report / identical (push) Blocked by required conditions
MemBrowse Memory Report / analyze (push) Blocked by required conditions
Some checks are pending
Build Documentation / build-html (push) Waiting to run
MemBrowse Memory Report / changes-filter (push) Waiting to run
MemBrowse Memory Report / load-targets (push) Waiting to run
MemBrowse Memory Report / identical (push) Blocked by required conditions
MemBrowse Memory Report / analyze (push) Blocked by required conditions
This adds support for the OTP flash region in the STM32H5 via both low-level functions and an eFuse lower half driver. Assisted-by: Claude:claude-sonnet-5 Signed-off-by: Darryl Ring <darryl@bluerobotics.com>
This commit is contained in:
parent
04c7eb43e8
commit
16b8a74417
9 changed files with 964 additions and 46 deletions
|
|
@ -114,6 +114,16 @@ STM32H5 parts have a 2 KiB one-time programmable (OTP) area. It's organized
|
|||
into 32 blocks of 32 16-bit words. Each word can be successfully programmed once.
|
||||
Each block may be permanently locked at any point. Written words may be read.
|
||||
|
||||
There are two APIs for it: a block-oriented API with locking, and a lower-level
|
||||
word API. An optional eFuse character device is built on top of the word API.
|
||||
|
||||
Block API
|
||||
~~~~~~~~~
|
||||
|
||||
This API is organized into 32 blocks of 32 16-bit words. Each word can be
|
||||
successfully programmed once. Each block may be permanently locked at any
|
||||
point. Written words may be read.
|
||||
|
||||
Writing the same word more than once is unsupported. Doing so may cause corruption.
|
||||
Reading an unwritten word raises an exception.
|
||||
To simplify the programming model, the OTP API
|
||||
|
|
@ -135,6 +145,53 @@ of the block size and count when partitioning the OTP area for their needs.
|
|||
``len`` is the number of bytes - not words. It has no alignment requirement. ``offset`` is
|
||||
the offset in bytes. It must be a multiple of 4.
|
||||
|
||||
Word API
|
||||
~~~~~~~~
|
||||
|
||||
``CONFIG_STM32H5_OTP_WORD`` builds direct 16- or 32-bit word access to the
|
||||
OTP area, independent of the block API above -- there is no locking, and
|
||||
no relation between a "word" index here and the block API's byte
|
||||
``offset``:
|
||||
|
||||
.. code:: c
|
||||
|
||||
int stm32_otp_word_read16(uint32_t word, uint16_t *value);
|
||||
int stm32_otp_word_read32(uint32_t word, uint32_t *value);
|
||||
|
||||
Unlike the block API, reading a blank (never programmed) word does not
|
||||
raise an exception: it legitimately reads back as
|
||||
``0xffff``/``0xffffffff``, which is returned through ``*value`` either
|
||||
way. Since whether a word has been written is known, that is reported
|
||||
through the return value: ``-ENODATA`` for a blank word, ``OK`` for one
|
||||
that holds real data. ``-EIO`` is returned only when a word's ECC does
|
||||
not check out at all, i.e. it is neither blank nor the value its own
|
||||
program operation wrote.
|
||||
|
||||
With ``CONFIG_STM32H5_OTP_WRITE`` also set:
|
||||
|
||||
.. code:: c
|
||||
|
||||
int stm32_otp_word_write16(uint32_t word, uint16_t value);
|
||||
int stm32_otp_word_write32(uint32_t word, uint32_t value);
|
||||
|
||||
Each word may be programmed once. Writing a word that already holds
|
||||
exactly the value requested is a harmless no-op that returns ``OK``.
|
||||
Writing a word that already holds a different value returns ``-EEXIST``.
|
||||
A hardware programming failure, or a post-write readback mismatch,
|
||||
returns ``-EIO``.
|
||||
|
||||
eFuse Character Device
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``CONFIG_STM32H5_EFUSE`` (which selects ``CONFIG_STM32H5_OTP_WORD``)
|
||||
registers the OTP area as a NuttX efuse character device, by default
|
||||
``/dev/efuse``, built on the word API above. See
|
||||
:doc:`/components/drivers/character/efuse` for the ``EFUSEIOC_READ_FIELD``/
|
||||
``EFUSEIOC_WRITE_FIELD`` ioctl interface. Field bit offsets index into the
|
||||
flat bit space of the OTP area at 16 bits per word, the same as the word
|
||||
API's ``word`` index. Writing a field requires ``CONFIG_STM32H5_OTP_WRITE``;
|
||||
without it, writes are refused with ``-EPERM``.
|
||||
|
||||
Clocks
|
||||
------
|
||||
|
||||
|
|
|
|||
|
|
@ -81,6 +81,10 @@ if(CONFIG_STM32_ICACHE)
|
|||
list(APPEND SRCS stm32_icache.c)
|
||||
endif()
|
||||
|
||||
if(CONFIG_STM32H5_EFUSE)
|
||||
list(APPEND SRCS stm32_efuse.c)
|
||||
endif()
|
||||
|
||||
if(CONFIG_STM32_SPI)
|
||||
list(APPEND SRCS stm32_spi.c)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -229,6 +229,52 @@ config STM32H5_IO_CONFIG_A
|
|||
bool
|
||||
default n
|
||||
|
||||
comment "STM32H5 OTP Options"
|
||||
|
||||
config STM32H5_OTP_WORD
|
||||
bool "OTP word read/write"
|
||||
default n
|
||||
---help---
|
||||
Build stm32_otp_word_read16()/read32() (and, with
|
||||
STM32H5_OTP_WRITE below, write16()/write32()) in
|
||||
stm32h563xx_flash.c: direct 16- or 32-bit word access to the
|
||||
STM32H5 OTP area (RM0481, "OTP area"), independent of the
|
||||
existing block/locking API (stm32_otp_write()/
|
||||
stm32_otp_read()/stm32_otp_getlockstatus()) in that same
|
||||
file, which is always built.
|
||||
|
||||
The OTP area's base address and size are fixed by the
|
||||
silicon, so they are plain constants in
|
||||
arch/arm/src/stm32h5/hardware/stm32h5xxx_memorymap.h rather
|
||||
than configuration here.
|
||||
|
||||
if STM32H5_OTP_WORD
|
||||
|
||||
config STM32H5_OTP_WRITE
|
||||
bool "Allow OTP word programming"
|
||||
default n
|
||||
---help---
|
||||
Build the code that programs OTP words.
|
||||
|
||||
Programming is IRREVERSIBLE: OTP bits can only be cleared, and
|
||||
because ECC is computed over each 16-bit word, a word that
|
||||
already holds a different value can never be reprogrammed to
|
||||
match -- see stm32_otp_word_write16() in
|
||||
arch/arm/src/stm32h5/stm32h563xx_flash.c. Reads work normally
|
||||
either way.
|
||||
|
||||
endif # STM32H5_OTP_WORD
|
||||
|
||||
config STM32H5_EFUSE
|
||||
bool "eFuse character device for the OTP area"
|
||||
default n
|
||||
depends on EFUSE
|
||||
select STM32H5_OTP_WORD
|
||||
---help---
|
||||
Expose the OTP area through the NuttX efuse interface as a
|
||||
character device, by default /dev/efuse, built on
|
||||
stm32_otp_word_read16()/write16() -- see stm32_efuse.c.
|
||||
|
||||
comment "STM32H5 SRAM2 Options"
|
||||
|
||||
config STM32_SBS
|
||||
|
|
|
|||
|
|
@ -76,6 +76,10 @@ ifeq ($(CONFIG_STM32_ICACHE),y)
|
|||
CHIP_CSRCS += stm32_icache.c
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_STM32H5_EFUSE),y)
|
||||
CHIP_CSRCS += stm32_efuse.c
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_STM32_SPI),y)
|
||||
CHIP_CSRCS += stm32_spi.c
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@
|
|||
|
||||
#define STM32_SYSMEM_MEM 0x0bf80000
|
||||
#define STM32_OTP_BASE 0x08FFF000 /* One-Time Programmable (OTP) memory base address */
|
||||
#define STM32_OTP_SIZE 2048
|
||||
#define STM32_SYSMEM_UID 0x08FFF800 /* The 96-bit unique device identifier */
|
||||
#define STM32_SYSMEM_FSIZE 0x08FFF80C /* Size of Flash memory in Kbytes. */
|
||||
#define STM32_SYSMEM_PACKAGE 0x08FFF80E /* Indicates the device's package type. */
|
||||
|
|
|
|||
347
arch/arm/src/stm32h5/stm32_efuse.c
Normal file
347
arch/arm/src/stm32h5/stm32_efuse.c
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
/****************************************************************************
|
||||
* arch/arm/src/stm32h5/stm32_efuse.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.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* The STM32H5 one-time-programmable area exposed through the NuttX efuse
|
||||
* interface.
|
||||
*
|
||||
* All the hardware access -- including the ICACHE/ECC NMI handling a blank
|
||||
* OTP word needs -- lives in stm32_otp_word_read16()/write16()
|
||||
* (stm32h563xx_flash.c). This file is just the efuse_ops_s adapter: it
|
||||
* packs/unpacks the field descriptors' bit ranges into and out of those
|
||||
* two word-level primitives, the same shape as stm32_flash_edata_*() is
|
||||
* to the MTD driver in stm32_edata.c.
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <debug.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <nuttx/efuse/efuse.h>
|
||||
|
||||
#include "stm32_flash.h"
|
||||
#include "stm32_efuse.h"
|
||||
|
||||
#ifdef CONFIG_STM32H5_EFUSE
|
||||
|
||||
/****************************************************************************
|
||||
* Private Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
static int stm32_efuse_read_field(FAR struct efuse_lowerhalf_s *lower,
|
||||
FAR const efuse_desc_t *field[],
|
||||
FAR uint8_t *data, size_t bit_size);
|
||||
static int stm32_efuse_write_field(FAR struct efuse_lowerhalf_s *lower,
|
||||
FAR const efuse_desc_t *field[],
|
||||
FAR const uint8_t *data, size_t bit_size);
|
||||
static int stm32_efuse_ioctl(FAR struct efuse_lowerhalf_s *lower, int cmd,
|
||||
unsigned long arg);
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
static const struct efuse_ops_s g_stm32_efuse_ops =
|
||||
{
|
||||
.read_field = stm32_efuse_read_field,
|
||||
.write_field = stm32_efuse_write_field,
|
||||
.ioctl = stm32_efuse_ioctl,
|
||||
};
|
||||
|
||||
static struct efuse_lowerhalf_s g_stm32_efuse_lower =
|
||||
{
|
||||
.ops = &g_stm32_efuse_ops,
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_efuse_field_bits
|
||||
*
|
||||
* Description:
|
||||
* Total number of bits described by a NULL terminated field list.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static size_t stm32_efuse_field_bits(FAR const efuse_desc_t *field[])
|
||||
{
|
||||
size_t bits = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; field[i] != NULL; i++)
|
||||
{
|
||||
bits += field[i]->bit_count;
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_efuse_check_field
|
||||
*
|
||||
* Description:
|
||||
* Verify that every descriptor lies inside the OTP bit address space.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int stm32_efuse_check_field(FAR const efuse_desc_t *field[])
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; field[i] != NULL; i++)
|
||||
{
|
||||
if ((size_t)field[i]->bit_offset + field[i]->bit_count >
|
||||
STM32_OTP_TOTAL_BITS)
|
||||
{
|
||||
ferr("ERROR: field %d [%u,+%u) is outside the OTP\n", i,
|
||||
field[i]->bit_offset, field[i]->bit_count);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_efuse_read_field
|
||||
*
|
||||
* Description:
|
||||
* Read the bits named by the field list. The bits are packed towards
|
||||
* the start of the caller's buffer: the first bit of the first
|
||||
* descriptor lands in bit 0 of data[0], the next in bit 1, and so on
|
||||
* across descriptor boundaries.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int stm32_efuse_read_field(FAR struct efuse_lowerhalf_s *lower,
|
||||
FAR const efuse_desc_t *field[],
|
||||
FAR uint8_t *data, size_t bit_size)
|
||||
{
|
||||
uint32_t cached_word = UINT32_MAX;
|
||||
uint16_t cached_val = 0;
|
||||
size_t written = 0;
|
||||
size_t request;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (field == NULL || data == NULL)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = stm32_efuse_check_field(field);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
request = stm32_efuse_field_bits(field);
|
||||
if (bit_size != 0 && bit_size < request)
|
||||
{
|
||||
request = bit_size;
|
||||
}
|
||||
|
||||
memset(data, 0, (request + 7) / 8);
|
||||
|
||||
for (i = 0; field[i] != NULL && written < request; i++)
|
||||
{
|
||||
size_t bit;
|
||||
|
||||
for (bit = 0; bit < field[i]->bit_count && written < request;
|
||||
bit++, written++)
|
||||
{
|
||||
size_t flat = field[i]->bit_offset + bit;
|
||||
uint32_t word = flat / STM32_OTP_WORD_BITS;
|
||||
|
||||
if (word != cached_word)
|
||||
{
|
||||
ret = stm32_otp_word_read16(word, &cached_val);
|
||||
if (ret < 0 && ret != -ENODATA)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
cached_word = word;
|
||||
}
|
||||
|
||||
if ((cached_val & (1u << (flat % STM32_OTP_WORD_BITS))) != 0)
|
||||
{
|
||||
data[written / 8] |= 1u << (written % 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_efuse_write_field
|
||||
*
|
||||
* Description:
|
||||
* Program the bits named by the field list, taking the data in the same
|
||||
* packed layout that stm32_efuse_read_field() produces.
|
||||
*
|
||||
* This is destructive and irreversible. Programming happens a whole
|
||||
* 16-bit word at a time, via stm32_otp_word_write16(), which is also
|
||||
* where a word that already holds a conflicting value is rejected.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int stm32_efuse_write_field(FAR struct efuse_lowerhalf_s *lower,
|
||||
FAR const efuse_desc_t *field[],
|
||||
FAR const uint8_t *data, size_t bit_size)
|
||||
{
|
||||
#ifndef CONFIG_STM32H5_OTP_WRITE
|
||||
/* Programming is not built in. Refuse rather than silently doing
|
||||
* nothing, so a caller cannot mistake this for a successful burn.
|
||||
*/
|
||||
|
||||
return -EPERM;
|
||||
#else
|
||||
uint32_t word = UINT32_MAX;
|
||||
uint16_t value = 0;
|
||||
bool dirty = false;
|
||||
size_t consumed = 0;
|
||||
size_t request;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (field == NULL || data == NULL)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = stm32_efuse_check_field(field);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
request = stm32_efuse_field_bits(field);
|
||||
if (bit_size != 0 && bit_size < request)
|
||||
{
|
||||
request = bit_size;
|
||||
}
|
||||
|
||||
for (i = 0; field[i] != NULL && consumed < request; i++)
|
||||
{
|
||||
size_t bit;
|
||||
|
||||
for (bit = 0; bit < field[i]->bit_count && consumed < request;
|
||||
bit++, consumed++)
|
||||
{
|
||||
size_t flat = field[i]->bit_offset + bit;
|
||||
uint32_t new_word = flat / STM32_OTP_WORD_BITS;
|
||||
size_t wordbit = flat % STM32_OTP_WORD_BITS;
|
||||
|
||||
if (new_word != word)
|
||||
{
|
||||
if (dirty)
|
||||
{
|
||||
ret = stm32_otp_word_write16(word, value);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Seed "value" with the word's current contents, so bits
|
||||
* this field does not touch are preserved -- blank reads
|
||||
* back as 0xffff, which is exactly the starting point a
|
||||
* never-written word needs.
|
||||
*/
|
||||
|
||||
ret = stm32_otp_word_read16(new_word, &value);
|
||||
if (ret < 0 && ret != -ENODATA)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
word = new_word;
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
if ((data[consumed / 8] & (1u << (consumed % 8))) == 0)
|
||||
{
|
||||
value &= ~(1u << wordbit);
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dirty)
|
||||
{
|
||||
ret = stm32_otp_word_write16(word, value);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
#endif /* CONFIG_STM32H5_OTP_WRITE */
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_efuse_ioctl
|
||||
****************************************************************************/
|
||||
|
||||
static int stm32_efuse_ioctl(FAR struct efuse_lowerhalf_s *lower, int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_efuse_initialize
|
||||
****************************************************************************/
|
||||
|
||||
int stm32_efuse_initialize(FAR const char *devpath)
|
||||
{
|
||||
FAR void *handle;
|
||||
|
||||
handle = efuse_register(devpath, &g_stm32_efuse_lower);
|
||||
if (handle == NULL)
|
||||
{
|
||||
ferr("ERROR: failed to register the OTP at %s\n", devpath);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_STM32H5_EFUSE */
|
||||
79
arch/arm/src/stm32h5/stm32_efuse.h
Normal file
79
arch/arm/src/stm32h5/stm32_efuse.h
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/****************************************************************************
|
||||
* arch/arm/src/stm32h5/stm32_efuse.h
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __ARCH_ARM_SRC_STM32H5_STM32_EFUSE_H
|
||||
#define __ARCH_ARM_SRC_STM32H5_STM32_EFUSE_H
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#ifdef CONFIG_STM32H5_EFUSE
|
||||
|
||||
/****************************************************************************
|
||||
* Public Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#undef EXTERN
|
||||
#if defined(__cplusplus)
|
||||
#define EXTERN extern "C"
|
||||
extern "C"
|
||||
{
|
||||
#else
|
||||
#define EXTERN extern
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_efuse_initialize
|
||||
*
|
||||
* Description:
|
||||
* Register the OTP area as an efuse character device, built on
|
||||
* stm32_otp_word_read16()/write16() (stm32_flash.h). Those two are a
|
||||
* bare register access and an nxmutex-protected program sequence
|
||||
* respectively, so unlike this function they have no dependency on
|
||||
* driver init order; this one allocates upper-half driver state and
|
||||
* creates an inode, so call it once from board bring-up, after the
|
||||
* usual driver/GPIO initialization has run.
|
||||
*
|
||||
* Input Parameters:
|
||||
* devpath - The path to the device, e.g. "/dev/efuse"
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) on success; a negated errno value on failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int stm32_efuse_initialize(FAR const char *devpath);
|
||||
|
||||
#undef EXTERN
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#endif /* CONFIG_STM32H5_EFUSE */
|
||||
#endif /* __ARCH_ARM_SRC_STM32H5_STM32_EFUSE_H */
|
||||
|
|
@ -33,6 +33,21 @@
|
|||
#include <sys/types.h>
|
||||
|
||||
#include "hardware/stm32_flash.h"
|
||||
#include "hardware/stm32_memorymap.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/* STM32_OTP_BASE/STM32_OTP_SIZE (hardware/stm32h5xxx_memorymap.h) are the
|
||||
* silicon facts from RM0481, "OTP area". These are the same facts recast
|
||||
* as a 16-bit word count, which is what stm32_otp_word_read16()/write16()
|
||||
* and the efuse driver built on them index by.
|
||||
*/
|
||||
|
||||
#define STM32_OTP_NWORDS (STM32_OTP_SIZE / 2)
|
||||
#define STM32_OTP_WORD_BITS 16
|
||||
#define STM32_OTP_TOTAL_BITS (STM32_OTP_NWORDS * STM32_OTP_WORD_BITS)
|
||||
|
||||
/****************************************************************************
|
||||
* Public Function Prototypes
|
||||
|
|
@ -66,6 +81,21 @@ int stm32_otp_read(uint16_t *data, uint16_t len, uint32_t offset);
|
|||
|
||||
uint32_t stm32_otp_getlockstatus(void);
|
||||
|
||||
#ifdef CONFIG_STM32H5_OTP_WORD
|
||||
|
||||
int stm32_otp_word_read16(uint32_t word, FAR uint16_t *value);
|
||||
|
||||
int stm32_otp_word_read32(uint32_t word, FAR uint32_t *value);
|
||||
|
||||
#ifdef CONFIG_STM32H5_OTP_WRITE
|
||||
|
||||
int stm32_otp_word_write16(uint32_t word, uint16_t value);
|
||||
|
||||
int stm32_otp_word_write32(uint32_t word, uint32_t value);
|
||||
|
||||
#endif /* CONFIG_STM32H5_OTP_WRITE */
|
||||
#endif /* CONFIG_STM32H5_OTP_WORD */
|
||||
|
||||
/* Flash high-cycle data (EDATA) low-level access.
|
||||
*
|
||||
* EDATA can be enabled on the last 1..8 sectors of each physical bank.
|
||||
|
|
|
|||
|
|
@ -128,6 +128,10 @@
|
|||
#define FLASH_OTP_WORDS_PER_BLOCK 32 /* 32 words per block */
|
||||
#define OTP_WORD_SIZE 2 /* 16-bit words as per manual */
|
||||
|
||||
#define OTP_ERASEDVALUE16 0xffffu
|
||||
#define OTP_ERASEDVALUE32 0xffffffffu
|
||||
#define OTP_ECCD (FLASH_ECCDETR_ECCD | FLASH_ECCDETR_OTP_ECC)
|
||||
|
||||
#define FLASH_NSSR_ALL_ERRORS (FLASH_NSSR_WRPERR | FLASH_NSSR_PGSERR | \
|
||||
FLASH_NSSR_STRBERR | FLASH_NSSR_INCERR | \
|
||||
FLASH_NSSR_OBKERR | FLASH_NSSR_OBKWERR | \
|
||||
|
|
@ -364,6 +368,79 @@ static void flash_lock_opt(void)
|
|||
modifyreg32(STM32_FLASH_OPTCR, 0, FLASH_OPTCR_OPTLOCK);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_STM32_EDATA) || defined(CONFIG_STM32H5_OTP_WORD)
|
||||
|
||||
/****************************************************************************
|
||||
* Name: flash_read_eccsafe16
|
||||
*
|
||||
* Description:
|
||||
* Read one 16-bit half-word of EDATA or OTP. Both only support 16 and
|
||||
* 32-bit reads, so the ICACHE, which would fill whole lines, is disabled
|
||||
* for the read. Reading a blank (erased, never programmed) half-word
|
||||
* raises the Flash ECC NMI, which is masked in the SBS first and handled
|
||||
* instead by checking ECCDETR afterwards. Both are restored before
|
||||
* returning.
|
||||
*
|
||||
* Input Parameters:
|
||||
* addr - Address of the half-word
|
||||
* eccd - ECCDETR bits that flag an ECC error for this memory
|
||||
* (EDATA_ECCD or OTP_ECCD)
|
||||
* eccerr - Set to true if ECCDETR flagged this read, whether because
|
||||
* the half-word was blank or genuinely corrupt. The returned
|
||||
* value in that case comes from ECCDR, not from the bus, since
|
||||
* a flagged read's data is not to be trusted.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static uint16_t flash_read_eccsafe16(uintptr_t addr, uint32_t eccd,
|
||||
bool *eccerr)
|
||||
{
|
||||
irqstate_t flags;
|
||||
uint16_t value;
|
||||
uint32_t eccnmir;
|
||||
#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();
|
||||
|
||||
*eccerr = (getreg32(STM32_FLASH_ECCDETR) & eccd) == eccd;
|
||||
if (*eccerr)
|
||||
{
|
||||
value = getreg32(STM32_FLASH_ECCDR) & FLASH_ECCDR_DATA_ECC_MASK;
|
||||
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);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_STM32_EDATA || CONFIG_STM32H5_OTP_WORD */
|
||||
|
||||
#ifdef CONFIG_STM32_EDATA
|
||||
|
||||
/****************************************************************************
|
||||
|
|
@ -394,56 +471,12 @@ static int edata_logical_bank(int bank)
|
|||
* 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);
|
||||
bool eccerr;
|
||||
uint16_t value = flash_read_eccsafe16(addr, EDATA_ECCD, &eccerr);
|
||||
|
||||
if (eccerr && value != EDATA_ERASEDVALUE)
|
||||
{
|
||||
|
|
@ -497,6 +530,47 @@ static int edata_erase(int bank, unsigned int sector)
|
|||
|
||||
#endif /* CONFIG_STM32_EDATA */
|
||||
|
||||
#ifdef CONFIG_STM32H5_OTP_WORD
|
||||
|
||||
/****************************************************************************
|
||||
* Name: otp_read_eccsafe16
|
||||
*
|
||||
* Description:
|
||||
* Read one 16-bit OTP word; see flash_read_eccsafe16().
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static uint16_t otp_read_eccsafe16(uintptr_t addr, bool *eccerr)
|
||||
{
|
||||
return flash_read_eccsafe16(addr, OTP_ECCD, eccerr);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: otp_read_eccsafe32
|
||||
*
|
||||
* Description:
|
||||
* 32-bit counterpart of otp_read_eccsafe16(). ECC is computed per
|
||||
* 16-bit word (FLASH_ECCDR only ever holds 16 bits of recovered data),
|
||||
* so a 32-bit read is done as its two halves, each independently
|
||||
* recovered: a single native 32-bit access could only ever recover
|
||||
* whichever half ECCDETR last reported and would have to discard the
|
||||
* other half's real contents.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static uint32_t otp_read_eccsafe32(uintptr_t addr, FAR bool *eccerr)
|
||||
{
|
||||
bool erclo;
|
||||
bool erchi;
|
||||
uint16_t lo = otp_read_eccsafe16(addr, &erclo);
|
||||
uint16_t hi = otp_read_eccsafe16(addr + sizeof(uint16_t), &erchi);
|
||||
|
||||
*eccerr = erclo || erchi;
|
||||
return (uint32_t)lo | ((uint32_t)hi << 16);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_STM32H5_OTP_WORD */
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32h5_otp_is_space_available
|
||||
*
|
||||
|
|
@ -1506,6 +1580,282 @@ exit_with_lock:
|
|||
|
||||
#endif /* CONFIG_STM32_EDATA */
|
||||
|
||||
#ifdef CONFIG_STM32H5_OTP_WORD
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_otp_word_read16
|
||||
*
|
||||
* Description:
|
||||
* Read one 16-bit OTP word. This is independent of, and does not
|
||||
* interact with, the block-oriented stm32_otp_write()/stm32_otp_read()
|
||||
* API above: no locking is involved or required, since reading never
|
||||
* conflicts with anything.
|
||||
*
|
||||
* A blank (never programmed) word reads back as 0xffff. Since whether a
|
||||
* word has been written is known (that is exactly what trips its ECC),
|
||||
* that is reported through the return value as -ENODATA rather than as
|
||||
* OK, even though *value is filled in either way. -EIO is reserved for
|
||||
* a word whose ECC genuinely does not check out: neither blank nor the
|
||||
* value its own program operation wrote.
|
||||
*
|
||||
* Input Parameters:
|
||||
* word - 16-bit word index, 0 to (FLASH_OTP_SIZE / 2) - 1
|
||||
* value - Receives the word's contents
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) on success; a negated errno value otherwise. *value is set
|
||||
* in every case except -EINVAL:
|
||||
*
|
||||
* -EINVAL: value is NULL, or word is out of range
|
||||
* -ENODATA: The word has never been programmed; *value is 0xffff
|
||||
* -EIO: The word's ECC does not check out
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int stm32_otp_word_read16(uint32_t word, FAR uint16_t *value)
|
||||
{
|
||||
bool eccerr;
|
||||
uint16_t raw;
|
||||
|
||||
if (value == NULL || word >= FLASH_OTP_SIZE / OTP_WORD_SIZE)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
raw = otp_read_eccsafe16(STM32_OTP_BASE + word * OTP_WORD_SIZE, &eccerr);
|
||||
*value = raw;
|
||||
|
||||
if (eccerr)
|
||||
{
|
||||
if (raw != OTP_ERASEDVALUE16)
|
||||
{
|
||||
ferr("ERROR: OTP word %" PRIu32 " ECC error: %04x\n", word, raw);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_otp_word_read32
|
||||
*
|
||||
* Description:
|
||||
* 32-bit counterpart of stm32_otp_word_read16(). Note that "word" here
|
||||
* is a 32-bit word index: it does not line up with the index used by
|
||||
* the 16-bit functions, the same as the attached reference driver this
|
||||
* was ported from.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int stm32_otp_word_read32(uint32_t word, FAR uint32_t *value)
|
||||
{
|
||||
bool eccerr;
|
||||
uint32_t raw;
|
||||
|
||||
if (value == NULL || word >= FLASH_OTP_SIZE / sizeof(uint32_t))
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
raw = otp_read_eccsafe32(STM32_OTP_BASE + word * sizeof(uint32_t),
|
||||
&eccerr);
|
||||
*value = raw;
|
||||
|
||||
if (eccerr)
|
||||
{
|
||||
if (raw != OTP_ERASEDVALUE32)
|
||||
{
|
||||
ferr("ERROR: OTP word %" PRIu32 " ECC error: %08" PRIx32 "\n",
|
||||
word, raw);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_STM32H5_OTP_WRITE
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_otp_word_write16
|
||||
*
|
||||
* Description:
|
||||
* Program one 16-bit OTP word. Programming is IRREVERSIBLE: a word
|
||||
* that already holds a value other than the one requested cannot be
|
||||
* reprogrammed, because bits can only move from 1 to 0 and ECC was
|
||||
* already computed over its current contents. Writing a word that
|
||||
* already holds exactly the requested value is a harmless no-op that
|
||||
* returns success, so this is safe to call unconditionally for a value
|
||||
* that may or may not have been written before.
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) on success (including the no-op case above); a negated
|
||||
* errno value on failure:
|
||||
*
|
||||
* -EINVAL: word is out of range
|
||||
* -EEXIST: The word already holds a different value
|
||||
* -EIO: Programming failed, or the post-write readback did not
|
||||
* match
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int stm32_otp_word_write16(uint32_t word, uint16_t value)
|
||||
{
|
||||
uintptr_t addr;
|
||||
uint16_t current;
|
||||
bool eccerr;
|
||||
int ret;
|
||||
|
||||
if (word >= FLASH_OTP_SIZE / OTP_WORD_SIZE)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
addr = STM32_OTP_BASE + word * OTP_WORD_SIZE;
|
||||
|
||||
ret = nxmutex_lock(&g_lock);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
current = otp_read_eccsafe16(addr, &eccerr);
|
||||
if (!eccerr || current != OTP_ERASEDVALUE16)
|
||||
{
|
||||
/* Not blank: either already holds this exact value (success, the
|
||||
* word is already in the requested state) or holds something else
|
||||
* (this word can never be reprogrammed to the new value).
|
||||
*/
|
||||
|
||||
ret = current == value ? OK : -EEXIST;
|
||||
goto exit_with_lock;
|
||||
}
|
||||
|
||||
if (flash_wait_for_operation())
|
||||
{
|
||||
ret = -EIO;
|
||||
goto exit_with_lock;
|
||||
}
|
||||
|
||||
flash_unlock_nscr();
|
||||
modifyreg32(STM32_FLASH_NSCCR, 0, ~0);
|
||||
|
||||
modifyreg32(STM32_FLASH_NSCR, 0, FLASH_NSCR_PG);
|
||||
UP_MB();
|
||||
|
||||
putreg16(value, addr);
|
||||
UP_MB();
|
||||
|
||||
ret = OK;
|
||||
if (flash_wait_for_operation() ||
|
||||
(getreg32(STM32_FLASH_NSSR) & FLASH_NSSR_ALL_ERRORS))
|
||||
{
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
modifyreg32(STM32_FLASH_NSCR, FLASH_NSCR_PG, 0);
|
||||
modifyreg32(STM32_FLASH_NSCCR, 0, ~0);
|
||||
flash_lock_nscr();
|
||||
|
||||
if (ret == OK)
|
||||
{
|
||||
current = otp_read_eccsafe16(addr, &eccerr);
|
||||
if (eccerr || current != value)
|
||||
{
|
||||
ret = -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
exit_with_lock:
|
||||
nxmutex_unlock(&g_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_otp_word_write32
|
||||
*
|
||||
* Description:
|
||||
* 32-bit counterpart of stm32_otp_word_write16(); see there for the
|
||||
* full explanation. As with stm32_otp_word_read32(), "word" is a
|
||||
* 32-bit word index here.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int stm32_otp_word_write32(uint32_t word, uint32_t value)
|
||||
{
|
||||
uintptr_t addr;
|
||||
uint32_t current;
|
||||
bool eccerr;
|
||||
int ret;
|
||||
|
||||
if (word >= FLASH_OTP_SIZE / sizeof(uint32_t))
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
addr = STM32_OTP_BASE + word * sizeof(uint32_t);
|
||||
|
||||
ret = nxmutex_lock(&g_lock);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
current = otp_read_eccsafe32(addr, &eccerr);
|
||||
if (!eccerr || current != OTP_ERASEDVALUE32)
|
||||
{
|
||||
ret = current == value ? OK : -EEXIST;
|
||||
goto exit_with_lock;
|
||||
}
|
||||
|
||||
if (flash_wait_for_operation())
|
||||
{
|
||||
ret = -EIO;
|
||||
goto exit_with_lock;
|
||||
}
|
||||
|
||||
flash_unlock_nscr();
|
||||
modifyreg32(STM32_FLASH_NSCCR, 0, ~0);
|
||||
|
||||
modifyreg32(STM32_FLASH_NSCR, 0, FLASH_NSCR_PG);
|
||||
UP_MB();
|
||||
|
||||
putreg32(value, addr);
|
||||
UP_MB();
|
||||
|
||||
ret = OK;
|
||||
if (flash_wait_for_operation() ||
|
||||
(getreg32(STM32_FLASH_NSSR) & FLASH_NSSR_ALL_ERRORS))
|
||||
{
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
modifyreg32(STM32_FLASH_NSCR, FLASH_NSCR_PG, 0);
|
||||
modifyreg32(STM32_FLASH_NSCCR, 0, ~0);
|
||||
flash_lock_nscr();
|
||||
|
||||
if (ret == OK)
|
||||
{
|
||||
current = otp_read_eccsafe32(addr, &eccerr);
|
||||
if (eccerr || current != value)
|
||||
{
|
||||
ret = -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
exit_with_lock:
|
||||
nxmutex_unlock(&g_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_STM32H5_OTP_WRITE */
|
||||
#endif /* CONFIG_STM32H5_OTP_WORD */
|
||||
|
||||
#ifdef CONFIG_ARCH_HAVE_PROGMEM
|
||||
|
||||
/* up_progmem_x functions defined in nuttx/include/nuttx/progmem.h
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue