rp23xx: Add an OTP driver on the efuse interface.
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

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) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
This commit is contained in:
Marco Casaroli 2026-07-25 10:53:03 +02:00 committed by Xiang Xiao
parent 7d319ab389
commit 7fc26fd50c
7 changed files with 671 additions and 0 deletions

View file

@ -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
================