Rails and OTP¶
How the PF1550 encodes rail voltages, enables and limits, which registers OTP controls, and what that means for the values the driver writes.
All tables come from the PF1550 data sheet, Rev. 7; the
driver's decoders in pf1550_voltage_tables.hpp implement them and check
themselves at compile time.
OTP decides first¶
At start-up (REGS_DISABLE to RUN) the PF1550 copies its OTP into the regulator registers: set points, enables,
sequence, current limits, charger parameters. Register writes over I2C then change the running configuration,
not OTP. The driver reads OTP (ReadOtpByte(), ReadOtpRegion()), but never programs it.
Three consequences for a host:
- An MCU reset does not reset the PF1550. The registers keep what you wrote while the PMIC stays powered. To start again from OTP values, power the PMIC down (remove VBUS and the battery) or send it through REGS_DISABLE.
- An A0EP part starts with nothing useful in OTP. OTP_FLAVOR (0x01) reads 0x00 and the host must configure every rail it needs, which is what the Portenta H7 profiles do.
- Some registers are read-only by design. The next two sections say which.
SW1 and SW2: two code tables¶
OTP_SWx_DVS_SEL selects one of two encodings for SW1 and for SW2 (Table 31). It lives in OTP, so the driver
cannot read it with an ordinary register access: you declare it with SetSwDvsEnabled() and the snapshot decodes
SWx_VOLT accordingly. The default is DVS disabled.
Code (SWx_VOLT[5:0]) |
DVS enabled (OTP_SWx_DVS_SEL = 0) |
DVS disabled (OTP_SWx_DVS_SEL = 1) |
|---|---|---|
| 0 | 0.6000 V | 1.10 V |
| 1 | 0.6125 V | 1.20 V |
| 2 | 0.6250 V | 1.35 V |
| 3 | 0.6375 V | 1.50 V |
| 4 | 0.6500 V | 1.80 V |
| 5 | 0.6625 V | 2.50 V |
| 6 | 0.6750 V | 3.00 V |
| 7 to 63 | 0.6875 V + 12.5 mV per code, up to 1.3875 V | 3.30 V |
With DVS enabled, SWx_VOLT, SWx_STBY_VOLT and SWx_SLP_VOLT can be changed after start-up. With DVS
disabled they are read-only, and the data sheet says they must not be written.
SetSwVoltageMv() and SetSwVoltage() follow these rules: they write only a DVS-enabled SW1 or SW2 (declared
with SetSwDvsEnabled()), always in the DVS encoding, and refuse a DVS-disabled buck and SW3.
SwDvsMillivoltsToCode() gives the code for a millivolt value. A raw WriteRegister() is not checked: on a DVS
buck, code 0x07 is 0.6875 V, not 3.3 V.
SW3¶
SW3 has one table (Table 37): 1.80 V + 100 mV per code, 0 to 15, so 1.80 V to 3.30 V. SW3 does not support DVS:
its three voltage registers are loaded from fuses and read-only. Sw3CodeToMillivolts() decodes them.
LDO1, LDO2, LDO3¶
The LDO voltage registers are loaded from OTP and writable.
| Rail | Codes | Output |
|---|---|---|
| LDO1, LDO3 (Table 41) | 0 to 15 | 0.75 V to 1.50 V in 50 mV steps |
| LDO1, LDO3 (Table 41) | 16 to 31 | 1.80 V to 3.30 V in 100 mV steps |
| LDO2 (Table 43) | 0 to 15 | 1.80 V to 3.30 V in 100 mV steps |
LdoVoltageCode names the three codes the Portenta H7 uses: V1_0 (0x05, LDO1), V1_2 (0x09, LDO3) and
V1_8 (0x00, LDO2). LdoCodeToMillivolts(code, is_ldo2) decodes any code.
Enables¶
The control registers share one layout for the bucks (SWx_CTRL, 0x35 / 0x3B / 0x41) and the LDOs (LDOx_CTRL,
0x4D / 0x50 / 0x53) in their low four bits:
| Bit | SwCtrlBits / LdoCtrlBits |
Set means |
|---|---|---|
| 0 | kEn |
On in RUN |
| 1 | kStbyEn |
On in STANDBY |
| 2 | kOMode |
On in SLEEP |
| 3 | kLpwr |
Low-power mode in STANDBY and SLEEP |
| 4 | kDvsSpeed (SW) / kLsEn (LDO) |
Slow DVS slew / load-switch mode (LDO1, LDO3; can only be set, not cleared) |
| 5 to 7 | kFpwmInDvs, kFpwm, kRdisEnb (SW only) |
Forced PWM during DVS, forced PWM, discharge resistor off |
So 0x0F keeps a rail on in RUN, STANDBY and SLEEP with low-power mode, and 0x01 turns it off as soon as the
PMIC enters STANDBY.
Current limits¶
SWx_ILIM[1:0] (SWx_CTRL1 bits 1:0) |
Typical limit |
|---|---|
00 |
1.0 A |
01 |
1.2 A |
10 |
1.5 A |
11 |
2.0 A |
SwCurrentLimitCodeToMilliamps() decodes the field.
VBUS input current limit¶
VBUS_LIN_ILIM is bits 7:3 of VBUS_INLIM_CNFG (absolute address 0x94); the power-on value is code 13, 400 mA
(Table 58).
| Codes | Limit |
|---|---|
| 0 to 8 | 10 mA to 50 mA in 5 mA steps |
| 9 to 15 | 100, 150, 200, 300, 400, 500, 600 mA |
| 16 to 20 | 700, 800, 900, 1000, 1500 mA |
| 21 to 31 | Reserved |
VbusLimitRegToMilliamps() decodes the register byte and returns 0 for a reserved code; the snapshot's
vbus_in_limit_ma uses it. SetVbusCurrentLimitMa() accepts 10, 100, 500, 1000 and 1500 mA and rejects anything else with
Error::InvalidParam. For another value, write the byte yourself:
WriteRegister(Register::VbusInCurrentLimit, code << 3).
Reading OTP¶
OTP bytes 0x1C to 0x36 (OtpRegion) are read through FMRADDR / FMRDATA after three unlock writes; the sequence
is on the Hardware page. Each byte costs five I2C transfers and a 2 ms wait, so a full
region read takes about 60 ms. Use it at bring-up to see what a programmed part will do at power-up, not in a
control loop.
Next: Register map.