Knowledge Base Article
Why is the HPS I2C SCL clock frequency above 400 kHz in fast mode when using U-Boot?
Description
When the HPS I2C controller is configured for fast mode (clock-frequency = <400000>)
in U-Boot, the measured SCL frequency might exceed the 400 kHz maximum in the I2C
specification (for example, 431 kHz) on SoC FPGA designs.
This happens because the U-Boot DesignWare I2C driver (drivers/i2c/designware_i2c.c)
calculates the SCL high and low counts from assumed SCL rise and fall times. When the
device tree does not specify them, the driver assumes default 300 ns for both in fast mode.
The driver subtracts the assumed rise time from the SCL period. If the board's actual
SCL rise time is shorter than 300 ns, the unused part of that allowance shortens the
period, and SCL runs faster than 400 kHz.
The Linux DesignWare I2C driver uses a different calculation and different device
tree properties, so the same device tree node can give different SCL frequencies
in U-Boot and Linux.
Resolution
To set the SCL timing to match your board, follow these steps:
- Measure the SCL and SDA rise times (30%–70%) and fall times (70%–30%) on
your board with an oscilloscope, in fast mode. - In the U-Boot device tree, add the measured values to the HPS I2C node:
&i2c0 {
clock-frequency = <400000>;
i2c-scl-rising-time-ns = <[measured tr]>;
i2c-scl-falling-time-ns = <[measured tf]>;
i2c-sda-hold-time-ns = <[required hold, default 300]>;
}; - For Linux, add the SDA fall time to the same node. The Linux driver uses
i2c-sda-falling-time-ns for the SCL high count and i2c-scl-falling-time-ns
for the SCL low count, and ignores i2c-scl-rising-time-ns:
i2c-sda-falling-time-ns = <[measured tf]>; - Rebuild U-Boot and the Linux device tree blob, then re-measure SCL in both
U-Boot and Linux to confirm the frequency is at or below 400 kHz and that
tLOW ≥ 1.3 µs and tHIGH ≥ 0.6 µs.
Note these points when tuning:- In U-Boot, enter the actual measured rise time. A larger
i2c-scl-rising-time-ns value makes SCL faster, not slower. - In U-Boot, i2c-scl-falling-time-ns changes only the high/low duty cycle,
not the frequency. - In Linux, larger fall-time values make SCL slower.
- Do not lower clock-frequency below 400000 in U-Boot to slow the bus. Any
value below 400000 selects standard mode (100 kHz timing). - snps,bus-capacitance-pf has no effect in fast mode. U-Boot ignores it, and
Linux uses it only for high-speed mode (3.4 MHz).
- In U-Boot, enter the actual measured rise time. A larger
Additional Information
|
Property |
U-Boot |
Linux |
|---|---|---|
|
clock-frequency |
Sets the target SCL period (default 100 kHz) |
Selects the speed mode only (default 400 kHz) |
|
i2c-scl-rising-time-ns |
Sets the SCL frequency (default 300 ns in fast mode, 1000 ns in standard mode) |
Ignored by the DesignWare driver |
|
i2c-scl-falling-time-ns |
Changes the duty cycle only (default 300 ns) |
Sets the SCL low count (default 300 ns) |
|
i2c-sda-falling-time-ns |
Ignored |
Sets the SCL high count (default 300 ns) |
|
i2c-sda-hold-time-ns |
Sets the SDA hold time (default 300 ns) |
Sets the SDA hold time (default: keeps the existing hardware value) |
|
snps,bus-capacitance-pf |
Ignored |
Used in high-speed mode (3.4 MHz) only |