The STM STM32MP13 CPU provides a set of one-time programmable bits (OTPs) structured as 3072 effective bits on 96 words (from 0 to 95).

OTP words are organized in regions:

  • lower OTP (OTP0 to OTP31), for configuration options and public key

  • upper OTP (OTP32 to OTP95), for secrets (ECC protected)

The OTPs are accessed through 32-bit words.

Kernel configuration

You can manage the OTP driver support through the kernel configuration option:

  • STM32 factory-programmed memory support (CONFIG_NVMEM_STM32_ROMEM)

This option is enabled as built-in on the ConnectCore MP13 default kernel configuration file.

Kernel driver

The memory driver is located at:

File Description

drivers/nvmem/stm32-romem.c

STM32 factory-programmed memory support

Device tree bindings

The STM32MP13 OTP memory driver device tree binding are documented at

File Description

Documentation/devicetree/bindings/nvmem/st,stm32-romem.yaml

STM32 BSEC device tree binding

Documentation/devicetree/bindings/nvmem/nvmem.yaml

Generic NVMEM device tree bindings

Documentation/devicetree/bindings/nvmem/nvmem-consumer.yaml

Generic device tree bindings

The STM32MP13 device tree include file defines the bsec node and inside it, a number of relevant OTP words.

STM32MP13 device tree
bsec: efuse@5c005000 {
	compatible = "st,stm32mp13-bsec";
	reg = <0x5c005000 0x400>;
	#address-cells = <1>;
	#size-cells = <1>;
	part_number_otp: part_number_otp@4 {
		reg = <0x4 0x1>;
	};
	vrefint: calib@52 {
		reg = <0x52 0x2>;
	};
	ts_cal1: calib@5c {
		reg = <0x5c 0x2>;
	};
	ts_cal2: calib@5e {
		reg = <0x5e 0x2>;
	};
	ethernet_mac_address: mac@e4 {
		reg = <0xe4 0x6>;
	};
	ethernet_mac2_address: mac2@ea {
		reg = <0xea 0x6>;
	};
};

The ConnectCore MP13 defines OTP words used by the SOM, such as Digi hardware ID. It also sets the OTP words as read-only by default, to prevent accidental programming:

ConnectCore MP13 device tree
&bsec {
	/* By default, make the OTP bits read-only */
	read-only;

	/* Digi hardware ID (HWID) stored in OTP59, OTP60, OTP61 */
	hwid: hwid@ec {
		reg = <0xec 0xc>;
	};
};

OTP user space usage

The OTP words are accessible through the sysfs at /sys/bus/nvmem/devices/stm32-romem0/nvmem.

Read the OTP bits

To dump the values of all OTP words:

# hexdump -e '"%08_ax: " 4/4 "%08x " "\n"' /sys/bus/nvmem/devices/stm32-romem0/nvmem
00000000: 00000017 00008000 a0200000 00000000
00000010: 00000000 00000000 00000000 00000000
*
00000030: 7cf5f917 00360035 32315117 32393532
00000040: 139685e7 20eb0868 7c3f0140 06be143d
00000050: 5da5003b 00000000 00000000 3f992fe9
00000060: 00000000 00000000 00000000 00000000
Certain OTP words, such as security keys, cannot be read so they show as 00000000.

To dump only a certain OTP word use the dd command, skip the offset of the word, and pipe the result to hexdump. For instance, to dump OTP word 12, use:

# dd status=none if=/sys/bus/nvmem/devices/stm32-romem0/nvmem of=/dev/stdout bs=4 count=1 skip=12 | hexdump -v -e '4/4 "%08x " "\n"'
7cf5f917

Write the OTP bits

OTP bits can be blown just once. Programming them is an irreversible operation.

OTP bits on ECC-protected regions should only be written once. Writing a second time may lead to errors.

OTP access is managed by the secure world. To have write access to OTP words, those words must be declared as non-secure using boolean properties st,non-secure-otp or st,non-secure-otp-provisioning in the OP-TEE device tree.

By default, OTP bits access is configured as read-only on Linux device tree. To enable write permissions, add the following to your Linux device tree:

&bsec {
	/delete-property/read-only;
};

Recompile the Linux device tree and deploy it to your device.

OTP bits access is done in 32-bit words. To write a 32-bit value to a specific word, use printf and pipe the output to dd:

# printf <value> | dd of=/sys/bus/nvmem/devices/stm32-romem0/nvmem bs=4 count=1 seek=<offset> conv=notrunc

where:

  • <value> is the 32-bit value to write. Specify the value in little-endian byte order.

  • bs=4 sets the block size in 4 bytes (a full 32-bit word).

  • count=1 sets the blocks (words) to write (1 = 4 bytes).

  • seek=<offset> sets the offset (in words) where to write.

  • conv=notrunc avoids truncating the output.

For example, to write the word 0x12345678 to word N, swap the bytes to present them in little-endian to the printf command:

# printf '\x78\x56\x34\x12' | dd of=/sys/bus/nvmem/devices/stm32-romem0/nvmem bs=4 count=1 seek=N conv=notrunc
Programming of the OTP bits happens without user confirmation. Make sure you double check the value and the word offset you want to program before running a command like this.