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)&param);
+
+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, &current);
+          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)

Reply via email to