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 7fc26fd50c2 rp23xx: Add an OTP driver on the efuse interface.
7fc26fd50c2 is described below
commit 7fc26fd50c2b3efed794db696e3821a644eb4bed
Author: Marco Casaroli <[email protected]>
AuthorDate: Sat Jul 25 10:53:03 2026 +0200
rp23xx: Add an OTP driver on the efuse interface.
The rp2350 holds 4096 rows of 24 bit one-time-programmable memory, which
the port did not expose at all. This adds a driver on the NuttX efuse
interface, registered by the common board bringup as /dev/efuse.
The driver uses the ECC interpretation of a row, in which 16 bits carry
data and the remaining 8 carry a Hamming code, so the OTP appears as a
flat space of 4096 * 16 bits for the efuse field descriptors to index: a
descriptor at bit offset N refers to bit N % 16 of row N / 16. That
matches the row numbers already listed in hardware/rp23xx_otp_data.h.
Reads come from the chip's ECC-translated window and have no side
effects. Rows are locked in pages of 64; a page locked against reads
would raise a bus fault, so the lock is checked first and reported as an
error instead.
Programming needs the separate RP23XX_OTP_WRITE option, which defaults to
off; without it a write returns EPERM and no programming code is built at
all. When enabled, a row is programmed as a whole through the bootrom,
since the ECC bits cover the whole row. For the same reason a row that
already holds data cannot be modified, and such a write is rejected
rather than left to corrupt the row's ECC.
Assisted-by: Claude Opus 5 (1M context) <[email protected]>
Signed-off-by: Marco Casaroli <[email protected]>
---
Documentation/platforms/arm/rp23xx/index.rst | 44 ++
arch/arm/src/rp23xx/CMakeLists.txt | 4 +
arch/arm/src/rp23xx/Kconfig | 45 ++
arch/arm/src/rp23xx/Make.defs | 4 +
arch/arm/src/rp23xx/rp23xx_otp.c | 474 +++++++++++++++++++++
arch/arm/src/rp23xx/rp23xx_otp.h | 88 ++++
.../arm/rp23xx/common/src/rp23xx_common_bringup.c | 12 +
7 files changed, 671 insertions(+)
diff --git a/Documentation/platforms/arm/rp23xx/index.rst
b/Documentation/platforms/arm/rp23xx/index.rst
index 2b673aba40e..cf1142c46bc 100644
--- a/Documentation/platforms/arm/rp23xx/index.rst
+++ b/Documentation/platforms/arm/rp23xx/index.rst
@@ -46,6 +46,7 @@ Flash MTD Working Unused flash tail as an MTD
device, answers BIOC_
Timer Working /dev/timerN on TIMER0/TIMER1, 1 us resolution
Tickless Working Optional, RP2350 TIMER via the alarm/oneshot
RTC Working POWMAN always-on timer, backs the system clock
+OTP Working Reads via /dev/efuse; programming is opt-in
============== ============ =====
Installation
@@ -342,6 +343,49 @@ Any MTD-based filesystem can be layered on the device, for
example::
nsh> mksmartfs /dev/rpflash
nsh> mount -t smartfs /dev/rpflash /mnt
+OTP
+===
+
+The rp2350 has 4096 rows of 24 bit one-time-programmable memory. Enabling
+``RP23XX_OTP`` exposes it through the NuttX efuse interface, registered by the
+common board bringup as ``/dev/efuse``.
+
+The driver uses the ECC interpretation of a row, in which 16 bits carry data
+and the remaining 8 carry a Hamming code. The OTP therefore appears as a flat
+space of 4096 * 16 bits, which is what the efuse field descriptors index: a
+descriptor at bit offset N refers to bit ``N % 16`` of row ``N / 16``. The
+row numbers of the predefined fields are in
+``arch/arm/src/rp23xx/hardware/rp23xx_otp_data.h``.
+
+Reads use the chip's ECC-translated window and have no side effects. Single
+bit errors are corrected in hardware. Rows live in pages of 64, each of which
+can be locked; a page locked against reads is reported as ``EPERM`` rather than
+being allowed to raise a bus fault.
+
+To read a field, fill in a ``struct efuse_param_s`` with a NULL terminated list
+of field descriptors and call ``EFUSEIOC_READ_FIELD``. The bits are returned
+packed from bit 0 of the first byte. For example, reading the 64 bit chip
+identifier held in rows 0 to 3::
+
+ static const efuse_desc_t chipid = { 0, 64 }; /* row 0, 4 * 16 bits */
+ static const efuse_desc_t *chipid_field[] = { &chipid, NULL };
+
+ uint8_t data[8];
+ struct efuse_param_s param =
+ {
+ .field = chipid_field, .size = 64, .data = data
+ };
+
+ ioctl(fd, EFUSEIOC_READ_FIELD, (unsigned long)¶m);
+
+Programming requires the separate ``RP23XX_OTP_WRITE`` option, which is off by
+default; without it ``EFUSEIOC_WRITE_FIELD`` fails with ``EPERM``. Programming
+is **irreversible**: a bit can only go from zero to one, and since the ECC bits
+cover the whole row, a row can only be programmed once -- the driver rejects a
+write to a row that already holds data rather than corrupting its ECC. Note
+that some rows control boot and debug behaviour, so an incorrect write can make
+a board unbootable or permanently lock out the debugger.
+
Supported Boards
================
diff --git a/arch/arm/src/rp23xx/CMakeLists.txt
b/arch/arm/src/rp23xx/CMakeLists.txt
index 6bd1eeaf358..90387a8bb8b 100644
--- a/arch/arm/src/rp23xx/CMakeLists.txt
+++ b/arch/arm/src/rp23xx/CMakeLists.txt
@@ -113,4 +113,8 @@ if(CONFIG_RP23XX_RTC)
list(APPEND SRCS rp23xx_rtc.c)
endif()
+if(CONFIG_RP23XX_OTP)
+ list(APPEND SRCS rp23xx_otp.c)
+endif()
+
target_sources(arch PRIVATE ${SRCS})
diff --git a/arch/arm/src/rp23xx/Kconfig b/arch/arm/src/rp23xx/Kconfig
index 7fd81b6f909..39c79b3d08d 100644
--- a/arch/arm/src/rp23xx/Kconfig
+++ b/arch/arm/src/rp23xx/Kconfig
@@ -1254,3 +1254,48 @@ config RP23XX_FLASH_MTD_SAFE_XIP
choice when bringing up a new board.
endif # RP23XX_FLASH_MTD
+
+#####################################################################
+# OTP Configuration
+#####################################################################
+
+config RP23XX_OTP
+ bool "OTP (one-time-programmable memory) driver"
+ default n
+ select EFUSE
+ ---help---
+ Expose the RP2350 OTP through the NuttX efuse interface as a
+ character device, by default /dev/efuse.
+
+ The OTP is an array of 4096 rows of 24 bits. This driver uses
the
+ ECC interpretation of a row, where 16 bits carry data and the
rest
+ carry a Hamming code, so it presents a flat space of 4096 * 16
bits
+ that the efuse field descriptors index into: a field at bit
offset
+ N refers to bit N % 16 of row N / 16.
+
+ Reads come straight from the chip's ECC-translated window, and
are
+ free of side effects. A page locked against reads is reported
as
+ an error rather than being allowed to raise a bus fault.
+
+ Programming is a separate option below, and is off by default.
+
+if RP23XX_OTP
+
+config RP23XX_OTP_WRITE
+ bool "Allow programming the OTP"
+ default n
+ ---help---
+ Build the code that programs OTP rows, through the bootrom.
+
+ Leave this off unless the application really has to burn fuses.
+ Programming is IRREVERSIBLE: a bit can only ever go from zero to
+ one, and because the ECC bits are computed over the whole row, a
+ row can only be programmed once. A mistake here permanently
+ damages the part, and some rows control boot and debug
behaviour,
+ so a wrong write can make a board unbootable or lock out the
+ debugger for good.
+
+ With this option off, EFUSEIOC_WRITE_FIELD fails with EPERM and
+ reads still work normally.
+
+endif # RP23XX_OTP
diff --git a/arch/arm/src/rp23xx/Make.defs b/arch/arm/src/rp23xx/Make.defs
index 75366113e42..47e790bd17b 100644
--- a/arch/arm/src/rp23xx/Make.defs
+++ b/arch/arm/src/rp23xx/Make.defs
@@ -117,3 +117,7 @@ endif
ifeq ($(CONFIG_RP23XX_RTC),y)
CHIP_CSRCS += rp23xx_rtc.c
endif
+
+ifeq ($(CONFIG_RP23XX_OTP),y)
+CHIP_CSRCS += rp23xx_otp.c
+endif
diff --git a/arch/arm/src/rp23xx/rp23xx_otp.c b/arch/arm/src/rp23xx/rp23xx_otp.c
new file mode 100644
index 00000000000..7e9a4dde37a
--- /dev/null
+++ b/arch/arm/src/rp23xx/rp23xx_otp.c
@@ -0,0 +1,474 @@
+/****************************************************************************
+ * arch/arm/src/rp23xx/rp23xx_otp.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 RP2350 one-time-programmable memory exposed through the NuttX efuse
+ * interface.
+ *
+ * The OTP is an array of 4096 rows of 24 bits. Software normally uses the
+ * ECC interpretation, where 16 of those bits carry data and the remaining 8
+ * carry a Hamming code, giving single-bit correction and double-bit
+ * detection. The chip maps that interpretation into the address space at
+ * RP23XX_OTP_DATA_BASE, one halfword per row, so a read is just a halfword
+ * load. This driver uses that window, and therefore presents the OTP as a
+ * flat bit address space of 4096 * 16 bits, which is what the efuse field
+ * descriptors index into.
+ *
+ * Reading is free of side effects. Programming is not: a bit can only ever
+ * go from zero to one, and with ECC in use a row can only be programmed
+ * once, because the Hamming code is computed over the whole 16 bit value.
+ * Programming goes through the bootrom, which owns the programming voltage
+ * and timing, and is compiled out unless CONFIG_RP23XX_OTP_WRITE is set.
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/efuse/efuse.h>
+
+#include "arm_internal.h"
+#include "rp23xx_otp.h"
+#include "hardware/rp23xx_memorymap.h"
+#include "hardware/rp23xx_otp.h"
+
+#ifdef CONFIG_RP23XX_OTP_WRITE
+/* Only the programming path needs the bootrom lookup helpers */
+
+# include "rp23xx_rom.h"
+#endif
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Software lock bits, per page of 64 rows. The two bit field encodes
+ * 0 = read and write, 1 = read only, 2 and 3 = inaccessible. NuttX runs in
+ * the Secure state on this part, so the SEC field is the one that applies.
+ */
+
+#define OTP_SW_LOCK_SEC_SHIFT 0
+#define OTP_SW_LOCK_NO_WRITE 1
+#define OTP_SW_LOCK_NO_READ 2
+
+/* Bootrom otp_access() command word: row in bits 15:0, then the direction
+ * and the ECC translation.
+ */
+
+#define OTP_CMD_ROW_MASK 0x0000ffff
+#define OTP_CMD_IS_WRITE (1 << 16)
+#define OTP_CMD_IS_ECC (1 << 17)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* The bootrom entry point used for programming */
+
+typedef int (*rom_otp_access_fn)(FAR uint8_t *buf, uint32_t buf_len,
+ uint32_t cmd);
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int rp23xx_otp_read_field(FAR struct efuse_lowerhalf_s *lower,
+ FAR const efuse_desc_t *field[],
+ FAR uint8_t *data, size_t bit_size);
+static int rp23xx_otp_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 rp23xx_otp_ioctl(FAR struct efuse_lowerhalf_s *lower, int cmd,
+ unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct efuse_ops_s g_rp23xx_otp_ops =
+{
+ .read_field = rp23xx_otp_read_field,
+ .write_field = rp23xx_otp_write_field,
+ .ioctl = rp23xx_otp_ioctl,
+};
+
+static struct efuse_lowerhalf_s g_rp23xx_otp_lower =
+{
+ .ops = &g_rp23xx_otp_ops,
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rp23xx_otp_page_lock
+ *
+ * Description:
+ * Return the Secure software lock state of the page holding a row.
+ *
+ ****************************************************************************/
+
+static uint32_t rp23xx_otp_page_lock(uint32_t row)
+{
+ uint32_t page = row / RP23XX_OTP_PAGE_ROWS;
+ uint32_t lock = getreg32(RP23XX_OTP_SW_LOCK(page));
+
+ return (lock & RP23XX_OTP_SW_LOCK_SEC_MASK) >> OTP_SW_LOCK_SEC_SHIFT;
+}
+
+/****************************************************************************
+ * Name: rp23xx_otp_read_row
+ *
+ * Description:
+ * Read one row through the ECC window. Reading a page that is locked
+ * against reads raises a bus fault, so the lock is checked first and
+ * reported as an error instead.
+ *
+ * Input Parameters:
+ * row - Row number, 0 to RP23XX_OTP_NUM_ROWS - 1
+ * value - Receives the 16 bits of data held in the row
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int rp23xx_otp_read_row(uint32_t row, FAR uint16_t *value)
+{
+ if (row >= RP23XX_OTP_NUM_ROWS)
+ {
+ return -EINVAL;
+ }
+
+ if (rp23xx_otp_page_lock(row) >= OTP_SW_LOCK_NO_READ)
+ {
+ finfo("OTP row %" PRIu32 " is in a page locked against reads\n", row);
+ return -EPERM;
+ }
+
+ *value = getreg16(RP23XX_OTP_DATA_BASE + row * sizeof(uint16_t));
+ return OK;
+}
+
+/****************************************************************************
+ * Name: rp23xx_otp_field_bits
+ *
+ * Description:
+ * Total number of bits described by a NULL terminated field list.
+ *
+ ****************************************************************************/
+
+static size_t rp23xx_otp_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: rp23xx_otp_check_field
+ *
+ * Description:
+ * Verify that every descriptor lies inside the OTP bit address space.
+ *
+ ****************************************************************************/
+
+static int rp23xx_otp_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 >
+ RP23XX_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: rp23xx_otp_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 rp23xx_otp_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_row = 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 = rp23xx_otp_check_field(field);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ request = rp23xx_otp_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 row = flat / RP23XX_OTP_ROW_BITS;
+
+ if (row != cached_row)
+ {
+ ret = rp23xx_otp_read_row(row, &cached_val);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ cached_row = row;
+ }
+
+ if ((cached_val & (1u << (flat % RP23XX_OTP_ROW_BITS))) != 0)
+ {
+ data[written / 8] |= 1u << (written % 8);
+ }
+ }
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: rp23xx_otp_write_field
+ *
+ * Description:
+ * Program the bits named by the field list, taking the data in the same
+ * packed layout that rp23xx_otp_read_field() produces.
+ *
+ * This is destructive and irreversible. Each affected row is programmed
+ * as a whole through the bootrom, because the ECC bits are computed over
+ * the whole row, which also means a row that has already been programmed
+ * cannot be modified: such a write is rejected here rather than left to
+ * corrupt the row's ECC.
+ *
+ ****************************************************************************/
+
+static int rp23xx_otp_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_RP23XX_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
+ rom_otp_access_fn otp_access;
+ size_t consumed = 0;
+ size_t request;
+ int ret;
+ int i;
+
+ if (field == NULL || data == NULL)
+ {
+ return -EINVAL;
+ }
+
+ ret = rp23xx_otp_check_field(field);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ otp_access = (rom_otp_access_fn)rom_func_lookup(ROM_FUNC_OTP_ACCESS);
+ if (otp_access == NULL)
+ {
+ ferr("ERROR: the bootrom has no OTP access function\n");
+ return -ENOTSUP;
+ }
+
+ request = rp23xx_otp_field_bits(field);
+ if (bit_size != 0 && bit_size < request)
+ {
+ request = bit_size;
+ }
+
+ /* Walk the field list a row at a time, building the value to program */
+
+ for (i = 0; field[i] != NULL && consumed < request; i++)
+ {
+ size_t bit = 0;
+
+ while (bit < field[i]->bit_count && consumed < request)
+ {
+ size_t flat = field[i]->bit_offset + bit;
+ uint32_t row = flat / RP23XX_OTP_ROW_BITS;
+ uint16_t value = 0;
+ uint16_t current;
+
+ /* Collect every bit of this field that falls in this row */
+
+ while (bit < field[i]->bit_count && consumed < request &&
+ (field[i]->bit_offset + bit) / RP23XX_OTP_ROW_BITS == row)
+ {
+ size_t pos = (field[i]->bit_offset + bit) %
+ RP23XX_OTP_ROW_BITS;
+
+ if ((data[consumed / 8] & (1u << (consumed % 8))) != 0)
+ {
+ value |= 1u << pos;
+ }
+
+ bit++;
+ consumed++;
+ }
+
+ if (rp23xx_otp_page_lock(row) >= OTP_SW_LOCK_NO_WRITE)
+ {
+ ferr("ERROR: OTP row %" PRIu32 " is locked against writes\n",
+ row);
+ return -EPERM;
+ }
+
+ ret = rp23xx_otp_read_row(row, ¤t);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if ((uint16_t)(current | value) == current)
+ {
+ /* Every bit requested is already programmed, so there is
+ * nothing to do for this row.
+ */
+
+ continue;
+ }
+
+ /* Anything else asked of a row that already holds data would need
+ * its ECC recomputed, and the array can only set bits, never clear
+ * them. Refuse here rather than handing the bootrom a write that
+ * would leave the row's ECC inconsistent. Note this catches a
+ * field covering the whole row as well as a partial one: any
+ * non-blank row is off limits.
+ */
+
+ if (current != 0)
+ {
+ ferr("ERROR: OTP row %" PRIu32 " already programmed (0x%04x), "
+ "its ECC cannot be recomputed\n", row, current);
+ return -EROFS;
+ }
+
+ ret = otp_access((FAR uint8_t *)&value, sizeof(value),
+ (row & OTP_CMD_ROW_MASK) | OTP_CMD_IS_WRITE |
+ OTP_CMD_IS_ECC);
+ if (ret < 0)
+ {
+ ferr("ERROR: bootrom refused to program row %" PRIu32 ": %d\n",
+ row, ret);
+ return -EIO;
+ }
+ }
+ }
+
+ return OK;
+#endif /* CONFIG_RP23XX_OTP_WRITE */
+}
+
+/****************************************************************************
+ * Name: rp23xx_otp_ioctl
+ ****************************************************************************/
+
+static int rp23xx_otp_ioctl(FAR struct efuse_lowerhalf_s *lower, int cmd,
+ unsigned long arg)
+{
+ return -ENOTTY;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rp23xx_otp_initialize
+ *
+ * Description:
+ * Register the OTP as an efuse character device.
+ *
+ ****************************************************************************/
+
+int rp23xx_otp_initialize(FAR const char *devpath)
+{
+ FAR void *handle;
+
+ handle = efuse_register(devpath, &g_rp23xx_otp_lower);
+ if (handle == NULL)
+ {
+ ferr("ERROR: failed to register the OTP at %s\n", devpath);
+ return -ENODEV;
+ }
+
+ return OK;
+}
diff --git a/arch/arm/src/rp23xx/rp23xx_otp.h b/arch/arm/src/rp23xx/rp23xx_otp.h
new file mode 100644
index 00000000000..1c4edaf20f6
--- /dev/null
+++ b/arch/arm/src/rp23xx/rp23xx_otp.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+ * arch/arm/src/rp23xx/rp23xx_otp.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_RP23XX_RP23XX_OTP_H
+#define __ARCH_ARM_SRC_RP23XX_RP23XX_OTP_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* The OTP holds 4096 rows. Through the ECC translation used by this driver
+ * each row carries 16 bits of data, so the flat bit address space that the
+ * efuse field descriptors index into is:
+ *
+ * bit = row * RP23XX_OTP_ROW_BITS + bit_within_row
+ */
+
+#define RP23XX_OTP_NUM_ROWS 4096
+#define RP23XX_OTP_ROW_BITS 16
+#define RP23XX_OTP_TOTAL_BITS (RP23XX_OTP_NUM_ROWS * RP23XX_OTP_ROW_BITS)
+
+/* Rows are locked in pages of 64 rows */
+
+#define RP23XX_OTP_PAGE_ROWS 64
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Name: rp23xx_otp_initialize
+ *
+ * Description:
+ * Register the OTP as an efuse character device.
+ *
+ * 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 rp23xx_otp_initialize(FAR const char *devpath);
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ARCH_ARM_SRC_RP23XX_RP23XX_OTP_H */
diff --git a/boards/arm/rp23xx/common/src/rp23xx_common_bringup.c
b/boards/arm/rp23xx/common/src/rp23xx_common_bringup.c
index 33645f16490..0130f633b5c 100644
--- a/boards/arm/rp23xx/common/src/rp23xx_common_bringup.c
+++ b/boards/arm/rp23xx/common/src/rp23xx_common_bringup.c
@@ -58,6 +58,10 @@
# include "rp23xx_flash_mtd.h"
#endif
+#ifdef CONFIG_RP23XX_OTP
+# include "rp23xx_otp.h"
+#endif
+
#if defined(CONFIG_RP23XX_ROMFS_ROMDISK_DEVNAME)
# include <rp23xx_romfsimg.h>
#endif
@@ -438,6 +442,14 @@ int rp23xx_common_bringup(void)
}
#endif
+#ifdef CONFIG_RP23XX_OTP
+ ret = rp23xx_otp_initialize("/dev/efuse");
+ if (ret < 0)
+ {
+ syslog(LOG_ERR, "Failed to initialize the OTP: %d\n", ret);
+ }
+#endif
+
#ifdef CONFIG_RP23XX_I2S
ret = board_i2sdev_initialize(0);
if (ret < 0)