Fuse Configuration for extended OTP on TI AM62L — in U-Boot#
Irreversible Operation
Writing to the OTP fuse registers is irreversible and can brick your device. Please always double-check and verify what you are doing.
Fuse Register Documentation
See this table for the fuse locations reserved by KED:
Please also consult the AM62L "Technical Reference Manual" for further information on the interpretation of the register values.
Extended OTP Memory#
The extended OTP memory of the AM62L holds 1024 bit at start offset 0. Writing
and reading use two different granularities: it is programmed in rows of 25 bit
(41 rows) and read back through memory mapped registers of 32 bit (32 MMRs).
Row n covers the physical bits 25n to 25n+24, MMR n the physical bits
32n to 32n+31.
<word> Means Different Things for Reading and Writing
The fuse command takes a <word> argument for both operations, but it is
interpreted in a different index space:
fuse read 0 <word>—<word>is an MMR index (32 bit granularity)fuse prog 0 <word>—<word>is a row index (25 bit granularity)
Row n and MMR n therefore cover different physical bits. Mixing up the
two indices while programming blows the wrong fuses irreversibly.
Two OTP Banks
The AM62L provides two banks for programming:
- Bank
0— general purpose, user defined OTP bits, the extended OTP - Bank
0xff— reduced pin boot-mode related OTP bits
This section describes only the processes for reading from and writing to
bank 0 in U-Boot.
Extended OTP Write#
The AM62L is programmed with a row granularity of 25 bit: the extended OTP
consists of 41 rows of 25 bit each, starting at offset 0 (row 0 to
row 40). A row is always written as a whole, so the width of the value decides
how much has to be done:
- A value of up to 25 bit fits into one row and is written as it is.
- A wider value has to be split into portions of 25 bit first, one portion per row.
The procedure to program the extended OTP on a Kontron AM62L board in U-Boot is:
-
Choose the row range. Row
0to15are reserved for Kontron use and must not be written, see Kontron reserved area on extended OTP Memory. Freely usable rows start at row16. -
Split the value, if it is wider than 25 bit.
-
Burn the OTP with
fuse, lowest row first. The Vpp regulator has to be active while the fuses are burned, otherwise programming fails:# enable the vpp_1v8 regulator before programming => gpio set 2 => regulator dev vpp_1v8 => regulator enable# one command per row, starting with portion 0 in the lowest row => fuse prog 0 <row> <0xvalue># disable the vpp_1v8 regulator afterwards => regulator disable => gpio clear 2
Example: Writing a MAC Address for Ethernet 2 on the Kontron AM62L#
e.g. MAC 2 = 00:11:22:33:44:56
-
Determine the rows. According to the Kontron reserved area on extended OTP Memory, MAC 2 starts at physical bit
50and is 48 bit wide. From these two numbers the rows follow:start = 50 # physical start bit len = 48 # width of a MAC address row = start / 25 = 50 / 25 = 2 # first row bit = start % 25 = 50 % 25 = 0 # offset inside that rowbit = 0means MAC 2 starts at a row boundary. MAC 2 therefore occupies row2and row3. -
Split the value into portions of 25 bit:
value = 0x001122334456 portion 0 = value & 0x1ffffff = 0x0334456 # bits 0–24 → row 2 portion 1 = (value >> 25) & 0x1ffffff = 0x0000891 # bits 25–47 → row 3 -
Program both rows, with the Vpp regulator enabled as shown in step 3 of the procedure above:
=> fuse prog 0 2 0x0334456 => fuse prog 0 3 0x0000891
Extended OTP Read#
Reading uses a different granularity than writing: the extended OTP is read back
through 32 memory mapped registers of 32 bit each (MMR 0 to 31).
fuse read 0 <mmr> <count> returns <count> registers starting at MMR <mmr>,
so here the index is an MMR index and has nothing to do with the row index used
for programming.
Because rows are 25 bit and MMRs 32 bit wide, the two grids do not line up. A value therefore rarely starts at bit 0 of an MMR and usually spreads over two or three of them, which is why it has to be cut out of the registers after reading.
The procedure to read the extended OTP on a Kontron AM62L board in U-Boot is:
-
Determine the MMR range.
startis the physical start bit of the value in the 1024-bit extended OTP, andlenits width:mmr = start >> 5 # first MMR of the value index = start & 31 # offset of the value inside that MMR last = (start + len - 1) >> 5 # last MMR of the value count = last - mmr + 1 # number of MMRs to readBecause an MMR is 32 bit wide, the division is a shift by 5 and the remainder a mask with
31. 2. Read the MMRs withfuse read 0 <mmr> <count>. The output starts at MMR<mmr>and continues upwards, so the first register holds the lowest bits of the value. -
Cut the value out of the MMRs.
mmr = start >> 5 # first MMR of the value index = start & 31 # offset of the value inside that MMR val = MMR[mmr] >> index # first register, shifted down to bit 0 bits = 32 - index # data bits collected so far while bits < len: # as long as bits are still missing mmr = mmr + 1 val = val | (MMR[mmr] << bits) bits = bits + 32 value = val & ((1 << len) - 1) # cut to the width of the value
Example: Reading a MAC Address for Ethernet Port 2 on the AM62L#
-
Determine the MMRs. According to the Kontron reserved area on extended OTP Memory, MAC 2 starts at physical bit
50and is 48 bit wide. From these two numbers the MMRs follow:start = 50 # physical start bit len = 48 # width of a MAC address mmr = start >> 5 = 50 >> 5 = 1 # first MMR index = start & 31 = 50 & 31 = 18 # offset inside that MMR last = (start + len - 1) >> 5 = 97 >> 5 = 3 # last MMRMAC 2 therefore starts at bit 18 of MMR
1and ends at bit 1 of MMR3(97 & 31 = 1), so reading it means collecting three MMRs:Read Bits of the MMR Bits of MAC 2 MMR 118–31 0–13 (the 14 lowest bits) MMR 20–31 14–45 MMR 30–1 46–47 (the 2 highest bits) -
Read these MMRs from the OTP:
# MMR 1 is the first MMR and 3 the number of MMRs to read => fuse read 0 1 3 -
Reassemble the value from the MMRs. Every register involved — including the first one — fits a single formula, using its own absolute MMR index
j(1,2,3, …, not the count of registers):shift(MMR_j) = 32·j − startA negative result means the register's low, out-of-range bits are dropped with a right-shift; a positive result means the register is moved up to its position in the result with a left-shift:
shift(MMR1) = 32·1 − 50 = −18 → negative ⇒ right-shift: MMR1 >> 18 shift(MMR2) = 32·2 − 50 = 14 → positive ⇒ left-shift: MMR2 << 14 shift(MMR3) = 32·3 − 50 = 46 → positive ⇒ left-shift: MMR3 << 46mac2 = ((MMR1 >> 18) | (MMR2 << 14) | (MMR3 << 46)) & mask mask = (1 << len) - 1 # here: (1 << 48) - 1 = 0xffffffffffffThe mask cuts off everything the OR'd-together registers collected beyond bit 47, since the last register read (
MMR3) usually contributes more bits than are actually needed for the value.
Example: Reading the KED SoM Unique ID#
-
Determine the MMRs. According to the Kontron reserved area on extended OTP Memory, the SoM Unique ID starts at physical bit
100and is 32 bit wide. From these two numbers the MMRs follow:start = 100 # physical start bit len = 32 # width of a unique ID mmr = start >> 5 = 100 >> 5 = 3 # first MMR index = start & 31 = 100 & 31 = 4 # offset inside that MMR last = (start + len - 1) >> 5 = 131 >> 5 = 4 # last MMR count = last - mmr + 1 = 4 - 3 + 1 = 2 # number of MMRs to readThe ID therefore starts at bit 4 of MMR
3and ends at bit 3 of MMR4(131 & 31 = 3), so reading it means collecting two MMRs:Read Bits of the MMR Bits of the SoM Unique ID MMR 34–31 0–27 (the 28 lowest bits) MMR 40–3 28–31 (the 4 highest bits) -
Read these MMRs from the OTP:
# MMR 3 is the first MMR and 2 the number of MMRs to read => fuse read 0 3 2 -
Reassemble the value from the MMRs, using the same
shift(MMR_j) = 32·j − startformula as above:shift(MMR3) = 32·3 − 100 = −4 → negative ⇒ right-shift: MMR3 >> 4 shift(MMR4) = 32·4 − 100 = 28 → positive ⇒ left-shift: MMR4 << 28uid = ((MMR3 >> 4) | (MMR4 << 28)) & mask mask = (1 << len) - 1 # here: (1 << 32) - 1 = 0xffffffffAs a rule, the mask always has exactly
lenset bits ((1 << len) - 1), regardless of how many MMRs were combined to build the value.