LAN966X OTP Configuration from Linux userspace
The OTP (One Time Programmable) memory in LAN966X can be programmed from Linux userspace using a tool that is included in the root file system of the device.
The next sections describes how to read and write to OTP and how to protect data from being overwritten.
2. Kernel configurations
Following kernel config options should be enabled to enable the LAN966X OTP driver and to be able to access the OTP from userspace.
-
CONFIG_NVMEM_MICROCHIP_OTP
- None volatile memory driver config option. -
CONFIG_NVMEM_SYSFS
- Config option to enable access to OTP using sysfs.
3. Devicetree Configuration
4. UserSpace
Before using the userspace application to configure the OTP, make sure that the
OTP device is created. If the device is created, the following file should
exist: /sys/bus/nvmem/devices/lan9662-otp0/nvmem
, which can be verified with
use of the ls -l
command:
# ls -l /sys/devices/platform/soc/e0021000.otp/lan9662-otp0/nvmem -rw-r--r-- 1 root root 8192 Jan 1 02:14 /sys/devices/platform/soc/e0021000.otp/lan9662-otp0/nvmem
The userspace application otp
it is used to read/write the OTP. It has the
following help:
# otp --help This tool can be used to access and modify the OTP in the following SoCs: - LAN9662 - LAN9668 - LAN969X Usage: otp field list otp field get [<NAME>] otp field set [--merge] <NAME> (ascii|hex|dec|3ascii-dec) <VALUE> otp addr get <ADDRESS> <BYTE-LENGTH> otp addr set [--merge] <ADDRESS> <BYTE-LENGTH> (ascii|hex|dec) <VALUE> otp tag list-tag-names otp tag print otp tag info otp tag get (<tag-number>|<tag-name>) otp tag del (<tag-number>|<tag-name>) otp tag set (<tag-number>|<tag-name>) (ascii|hex|dec) <VALUE> otp nvcnt get (trusted|nontrusted) otp nvcnt set <VALUE> (trusted|nontrusted) otp import-keys [--no-randomize-huk] <FILE> otp region show otp region write-protect <REGION-NUMBER> otp region non-secure <REGION-NUMBER> otp init pcb <PCB> ethaddr (random-ethaddr|<ETHADDR-ADDRESS>) ethaddr_count <COUNT> Options: -h --help Show this screen. --version Show version. --verbose Enable verbose traces on console -d DEV --device DEV NVRAM device This can also be a normal block device, or even a file. -i ID --chip-id ID Specify OTP layout coresponding to the given chip ID. NOTE: This setting is only allowed on devices where the part-ID at byte address 0x5-0x6 is not programmed, or if the programmed part-ID matches the provided ID. --chip-id-force Force usage of the ID given with --chip-id=ID, even though a different part ID is programmed in the OTP. --no-confirm Do not require the user to confirm write operations. General: The OTP in these products are accessed by various elements including: discrete logic in the chip, TF-A bootrom (BL1), Secure Boot loader (BL2), EL3 Runtime software (BL31/BL32), UBoot, Linux kernel and this user-space application. The OTP has a concept of regions, which can be used to configure access control (read and write protection). The regions and write protect mask is in the OTP it self and need to be provisioned. This tool support 3 different kind of content: Fields: This is fixed-length data at fixed positions in the OTP. The tool has a build in template for each of the supported SoC with name, address, length and description. Non volatile counters: This is counters which can only be incremented. This is used to do rollback protection. This is implemented as a bitfields, and the max value is therefore the number of allocated bits for a given counter. Tagged data: This is semi-one-time-programmable data. The purpose of this is to allow store various data such as: mac-addresses, board-ID, ECO level, uniq default password which may be printed on the device, etc. This is implemented as an array of 64-bits records with the following layout: +--------+--------+--------+----------+ | size:3 | cont:1 | tag:12 | value:48 | +--------+--------+--------+----------+ Where: size: specify the number of valid bytes in value. 0 and 0b111 (7) are invalid. If a record needs to be invalidated, then it is a matter of writing 0b111 in this field. cont: specify that the content continues in the next valid record with the given tag value. tag: This is a number from 0-2047 specifying the type of data (the implementation has a list of named tags which may be used). value: Value associated to the tag. Note: All commands writing ('field set', 'addr set', 'tag del|set') to OTP shall do: - Read out existing content. - If '--merge' flag is not set, confirm that the specified value is possible to set. Abort if not. - Print in hex what is about the be written where - Let use user confirm. Command details: otp help: Print this message otp field list: List all fields recognized by the implementation template. otp field get [<NAME>] Get the content of a specific fields, or all fields if non is provided. NOTE: This shall skip fields in areas being read-protected, but will happily print secrets if not proper protected. otp field set [--merge] <NAME> (ascii|hex|dec) <VALUE> Set the content of a given field. - If the --merge option is provided, then the content is bit-wised OR with what is in the OTP field already. If not, a check is performed to confirm that the desired value can be written as-is. - The value can either be provided in ascii format, or as a hex-string. - A hex string must always provide an equal number of chars (no half bytes). - The length of the value must match what is find in the template. otp addr get <address> <BYTE-LENGTH> Read the raw content in OTP. If the requested area is (partly) read-protected, then return error. otp addr set [--merge] <address> <BYTE-LENGTH> (ascii|hex|dec) <VALUE> Write raw content in the OTP. If the requested area is (partly) write-protected, then return error before any content is written. otp nvcnt (trusted|nontrusted) get otp nvcnt (trusted|nontrusted) set <VALUE> Get/set the value of either the trusted or nontrusted non-volatile-ever-incrementing-counter. This is implemented as a bit-field, and a given implementation will have limited capacity. NOTE: This is used by the secure boot-ROM to implement rollback protection. otp tag list-tag-names List all the tag names and associated numbers know by the implementation. otp tag info Print statistics on tag usages. otp tag print Print all valid tags in the otp. otp tag get (bin|hex) [(<tag-number>|<tag-name>)] Read out the value of a given tag (either by name or by number) or dump all tags if no name/number is provided. If no valid tag with matching name/number was found, then return an error. otp tag [--merge] set (<tag-number>|<tag-name>) (ascii|hex) <VALUE> Set a tagged value. If the --merge flag was set, then the content provided must be of exact same length as the length of existing content in the OTP. If this tag already exists, then it shall be replaced (meaning invalidating the existing tag). otp tag append (<tag-number>|<tag-name>) Append more data to a tag. This will always create a new record and set the 'cont' flag in existing tags. otp tag del (<tag-number>|<tag-name>) Invalidate an existing tag in the OTP. otp import-keys [--no-randomize-huk] <FILE>: This shall read the content from a file (or stdin if '-' is provided as file-name). The content is binary and length must match the platform expected length. It can only be used to program the region with TF-A keys (on LAN966x this is region 4). The region will not be defined when the tool is called, and a per platform hard-coded offset/size will be used. Unless the --no-randomize-huk flag is set, then the OTP_TBBR_HUK field will be set with randomized content using /dev/random. otp region show: Show the start-address, end-address and write-protect bit of all regions. Some regions may be displayed with start- and end-address equal to 0, which means that this region is still not defined. otp region write-protect <REGION-NUMBER>: This shall check if the region <REGION-NUMBER> is defined (in `PROTECT_REGION_ADDR`). If not, the region shall be defined using the per-chip-id defined template. Once the region is defined it shall be marked as write-protected in `PROTECT_OTP_WRITE`. otp init pcb <PCBNO> ethaddr (random-ethaddr|<ETHADDR-ADDRESS>) ethaddr_count <COUNT> NOTE: This shall only be called during board manufacturing! Do not call more than once. This sub-command will do the following: - Do the equivalent of: $ opt tag set pcb dec <PCBNO> - Do the equivalent of: $ opt tag set ethaddr_count dec <COUNT> - Do the equivalent of: $ opt tag set ethaddr ascii <ETHADDR-ADDRESS> - If the ethaddr-address is `random-ethaddr`, then generate a random ETHADDR with the following limitation: - Broadcast bit is 0 - Local-administrated bit is 1 - It must not be all zero and not all ff.
4.1. Example on how to use
Here are some examples on how to use it
Once something is written in the OTP this can’t be erased. So make sure you can write the right command. You can test the command by running the same command on a file. |
If the chip id has not been programmed into the OTP you can provide it on the command line and this use the tool correctly. |
How to initialize the board:
otp -d /sys/bus/nvmem/devices/lan9662-otp0/nvmem init partid 9662 serial <SERIAL_NUMBER> pcb 8291 ethaddr random-ethaddr ethaddr_count 16
This command will do the following:
-
set the field
partid
to9668
-
set the field
serial_number
to<SERIAL_NUMBER>
-
set the tag
pcb
to8290
-
set the tag
ethaddr
to a random generated eth addr where bit 1 in MSB is set to 1 and bit 0 in MSB is set to 0. -
set the tag
ethaddr_count
to16
meaning that it would reserve 16 addresses
Here is a way to read back a field:
otp -d /sys/bus/nvmem/devices/lan9662-otp0/nvmem field get PARTID 2 be25|.%|
The first value of the output represents the number of bytes the field it has, second represents the value in little endian in hex and the last value represents the ascii characters of the value.
To see the available OTP regions you use the following command:
# otp -d /sys/bus/nvmem/devices/lan9662-otp0/nvmem region show Region: 0, start: 0x0000, end: 0x003f, write_protect: 0 Region: 1, start: 0x0040, end: 0x0043, write_protect: 0 Region: 2, start: 0x0044, end: 0x0063, write_protect: 0 Region: 3, start: 0x0064, end: 0x00ff, write_protect: 0 Region: 4, start: 0x0100, end: 0x01ff, write_protect: 0 Region: 5, start: 0x0200, end: 0x023f, write_protect: 0 Region: 6, start: 0x0240, end: 0x07ff, write_protect: 0 Region: 7, start: 0x0800, end: 0x2000, write_protect: 0
The regions are described here: OTP Regions
To write-protect region 4 you can use this command:
otp -d /sys/bus/nvmem/devices/lan9662-otp0/nvmem region write-protect 4
As the operation cannot be reverted, you must only write protect a region after you have written any needed data into it. |
To see the available fields the following command can be used:
otp -d /sys/bus/nvmem/devices/lan9662-otp0/nvmem field list Field: OTP_PRG offset: 0x0000 length: 04 Field: FEAT_IDS offset: 0x0004 length: 01 Field: PARTID offset: 0x0005 length: 02 Field: TST_TRK offset: 0x0007 length: 01 Field: SERIAL_NUMBER offset: 0x0008 length: 08 Field: SECURE_JTAG offset: 0x0010 length: 04 Field: WAFER_JTAG offset: 0x0014 length: 07 Field: JTAG_UUID offset: 0x0020 length: 10 Field: TRIM offset: 0x0030 length: 08 Field: PROTECT_OTP_WRITE offset: 0x0040 length: 04 Field: PROTECT_REGION_ADDR offset: 0x0044 length: 32 Field: OTP_PCIE_FLAGS offset: 0x0064 length: 04 Field: OTP_PCIE_DEV offset: 0x0068 length: 04 Field: OTP_PCIE_ID offset: 0x006c length: 08 Field: OTP_PCIE_BARS offset: 0x0074 length: 40 Field: OTP_TBBR_ROTPK offset: 0x0100 length: 32 Field: OTP_TBBR_HUK offset: 0x0120 length: 32 Field: OTP_TBBR_EK offset: 0x0140 length: 32 Field: OTP_TBBR_SSK offset: 0x0160 length: 32 Field: OTP_SJTAG_SSK offset: 0x0180 length: 32 Field: OTP_STRAP_DISABLE_MASK offset: 0x01a4 length: 02
Here is a way to read back a tag:
otp -d /sys/bus/nvmem/devices/lan9662-otp0/nvmem tag list-tag-names Tag: password, id: 1 Tag: pcb, id: 2 Tag: revision, id: 3 Tag: ethaddr, id: 4 Tag: ethaddr_count, id: 5 Tag: fit_config, id: 6