I2C Debugging Guide
Preface
The S100 chip provides a standard I2C bus. The I2C bus controller transfers information between devices connected to the bus via a Serial Data Line (SDA) and a Serial Clock Line (SCL). Each device has a unique address (whether it's a microcontroller—MCU, LCD controller, memory, or keyboard interface) and can act as both a transmitter and a receiver (determined by the device's functionality).
The I2C controller supports the following features:
- Supports four speed modes:
- Standard mode (0–100 Kb/s)
- Fast mode (100–400 Kb/s)
- Fast mode plus (400–1000 Kb/s)
- High-speed mode (1000 Kb/s–3.4 Mb/s)
- Supports master/slave mode configuration
- Supports both 7-bit and 10-bit addressing modes
Driver Code
drivers/i2c/i2c-dev.c # I2C character device interface code
drivers/i2c/i2c-core-base.c # I2C framework code
drivers/i2c/busses/i2c-designware-platdrv.c # I2C driver source file
Kernel Configuration Location
/* hobot-drivers/configs/drobot_s100_defconfig */
CONFIG_I2C_CHARDEV=y # I2C driver userspace configuration macro
CONFIG_I2C_DESIGNWARE_PLATFORM=y # DesignWare I2C driver configuration macro
Kernel DTS Node Configuration
/*kernel/arch/arm64/boot/dts/hobot/drobot-s100-soc.dtsi*/
i2c0: i2c@39420000 {
power-domains = <&scmi_smc_pd PD_IDX_LSPERI_TOP>;
#address-cells = <1>;
#size-cells = <0>;
compatible = "snps,designware-i2c";
reg = <0x0 0x39420000 0x0 0x10000>;
clocks = <&scmi_smc_clk CLK_IDX_TOP_PERI_I2C0>;
clock-names = "apb_pclk";
interrupts = <GIC_SPI PERISYS_I2C0_INTR PERISYS_I2C0_INTR_TRIG_TYPE>;
clock-frequency = <400000>;
i2c-sda-hold-time-ns = <50>;
pinctrl-names = "default", "gpio";
pinctrl-0 = <&cam_i2c0>;
pinctrl-1 = <&cam_i2c0_gpio>;
scl-gpios = <&cam_port0 8 GPIO_ACTIVE_HIGH>;
sda-gpios = <&cam_port0 9 GPIO_ACTIVE_HIGH>;
status = "okay";
};
I2C Usage
Detailed instructions on I2C usage can be found in kernel/Documentation/i2c/. This document mainly highlights S100-specific aspects of the I2C driver interface.
Kernel Space
The S100 I2C driver provides an interface in kernel space to configure the I2C transfer frequency. Usage is as follows:
I2C Speed Configuration
The default I2C speed is 400K. The driver supports four speeds: 100K, 400K, 1M, and 3.4M. The speed can be modified by adjusting the clock-frequency property of the corresponding I2C node in the device tree (DTS). The relevant code for actual speed selection is as follows:
kernel/drivers/i2c/busses/i2c-designware-common.c
Supported I2C speeds are defined as:
static const u32 supported_speeds[] = {
I2C_MAX_HIGH_SPEED_MODE_FREQ,
I2C_MAX_FAST_MODE_PLUS_FREQ,
I2C_MAX_FAST_MODE_FREQ,
I2C_MAX_STANDARD_MODE_FREQ,
};
...
kernel/drivers/i2c/busses/i2c-designware-pcidrv.c
The function interface to retrieve clock-frequency from the device tree is:
i2c_parse_fw_timings(&pdev->dev, t, false);
Expanded as:
void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t, bool use_defaults)
{
bool u = use_defaults;
u32 d;
i2c_parse_timing(dev, "clock-frequency", &t->bus_freq_hz,
I2C_MAX_STANDARD_MODE_FREQ, u);
d = t->bus_freq_hz <= I2C_MAX_STANDARD_MODE_FREQ ? 1000 :
t->bus_freq_hz <= I2C_MAX_FAST_MODE_FREQ ? 300 : 120;
i2c_parse_timing(dev, "i2c-scl-rising-time-ns", &t->scl_rise_ns, d, u);
d = t->bus_freq_hz <= I2C_MAX_FAST_MODE_FREQ ? 300 : 120;
i2c_parse_timing(dev, "i2c-scl-falling-time-ns", &t->scl_fall_ns, d, u);
i2c_parse_timing(dev, "i2c-scl-internal-delay-ns",
&t->scl_int_delay_ns, 0, u);
i2c_parse_timing(dev, "i2c-sda-falling-time-ns", &t->sda_fall_ns,
t->scl_fall_ns, u);
i2c_parse_timing(dev, "i2c-sda-hold-time-ns", &t->sda_hold_ns, 0, u);
i2c_parse_timing(dev, "i2c-digital-filter-width-ns",
&t->digital_filter_width_ns, 0, u);
i2c_parse_timing(dev, "i2c-analog-filter-cutoff-frequency",
&t->analog_filter_cutoff_freq_hz, 0, u);
}
The function interface to validate I2C speed is:
i2c_dw_validate_speed(dev);
Expanded as:
int i2c_dw_validate_speed(struct dw_i2c_dev *dev)
{
struct i2c_timings *t = &dev->timings;
unsigned int i;
/*
* Only standard mode at 100kHz, fast mode at 400kHz,
* fast mode plus at 1MHz and high speed mode at 3.4MHz are supported.
*/
for (i = 0; i < ARRAY_SIZE(supported_speeds); i++) {
if (t->bus_freq_hz == supported_speeds[i])
return 0;
}
dev_err(dev->dev,
"%d Hz is unsupported, only 100kHz, 400kHz, 1MHz and 3.4MHz are supported\n",
t->bus_freq_hz);
return -EINVAL;
}
User Space
Typically, I2C devices are controlled by kernel drivers, but all devices on the bus can also be accessed from userspace via the /dev/i2c-%d interface. Detailed documentation is available in Documentation/i2c/dev-interface.rst under the kernel source tree.
Debug Interfaces
Register Information Retrieval
To view I2C register information (using i2c-0 as an example):
root@ubuntu:/# cat /sys/kernel/debug/dw_i2c0/registers
39420000.i2c registers:
=================================
CON: 0x00000065
SAR: 0x00000055
DATA_CMD: 0x00000800
INTR_STAT: 0x00000000
INTR_MASK: 0x00000000
RX_TL: 0x00000000
TX_TL: 0x00000002
STATUS: 0x00000006
TXFLR: 0x00000000
RXFLR: 0x00000000
SDA_HOLD: 0x0001000c
TX_ABRT: 0x00000000
EN_STATUS: 0x00000000
CLR_RESTA: 0x00000000
PARAM: 0x000303ee
VERSION: 0x3230322a
TYPE: 0x44570140
=================================
reldump_en Interface
Real-time dump enable interface. When enabled, real-time I2C transfer data can be viewed via dmesg.
# enable
echo 1 > /sys/kernel/debug/dw_i2c0/reldump_en
# disable
echo 0 > /sys/kernel/debug/dw_i2c0/reldump_en
fifodump_en Interface
FIFO dump enable interface. When enabled, it dumps the most recent I2C read/write transactions. Each channel is configured separately and only supports master mode with 7-bit addressing.
# enable
echo 1 > /sys/kernel/debug/dw_i2c0/fifodump_en
# disable
echo 0 > /sys/kernel/debug/dw_i2c0/fifodump_en
fifodump Interface
Temporarily stores I2C data. When fifodump_en is enabled, the stored data can be printed using cat.
Transmission normal:
root@ubuntu:~# i2cdump -f -y 0 0x28
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: 00 1f 21 00 00 00 00 00 00 00 XX XX XX XX XX XX .?!.......XXXXXX
10: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
20: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
30: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
40: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
50: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
60: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
70: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
80: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
90: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
a0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
b0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
c0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
d0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
e0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
f0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
root@ubuntu:~# cat /sys/kernel/debug/dw_i2c0/fifodump
=b[0]-t[1229.799811]-n[2]-me[0]-ce[1]-ae[0x800008]=
m[0]-a[0x28]-f[0x0]:0x84
m[1]-a[0x28]-f[0x1]:0x00
=b[0]-t[1229.799881]-n[2]-me[0]-ce[1]-ae[0x800008]=
m[0]-a[0x28]-f[0x0]:0x85
m[1]-a[0x28]-f[0x1]:0x00
=b[0]-t[1229.799953]-n[2]-me[0]-ce[1]-ae[0x800008]=
m[0]-a[0x28]-f[0x0]:0x86
m[1]-a[0x28]-f[0x1]:0x00
=b[0]-t[1229.800046]-n[2]-me[0]-ce[1]-ae[0x800008]=
m[0]-a[0x28]-f[0x0]:0x87
m[1]-a[0x28]-f[0x1]:0x00
=b[0]-t[1229.800152]-n[2]-me[0]-ce[1]-ae[0x800008]=
m[0]-a[0x28]-f[0x0]:0x88
m[1]-a[0x28]-f[0x1]:0x00
=b[0]-t[1229.800233]-n[2]-me[0]-ce[1]-ae[0x800008]=
m[0]-a[0x28]-f[0x0]:0x89
m[1]-a[0x28]-f[0x1]:0x00
=b[0]-t[1229.800308]-n[2]-me[0]-ce[1]-ae[0x800008]=
m[0]-a[0x28]-f[0x0]:0x8a
m[1]-a[0x28]-f[0x1]:0x00
=b[0]-t[1229.800384]-n[2]-me[0]-ce[1]-ae[0x800008]=
m[0]-a[0x28]-f[0x0]:0x8b
m[1]-a[0x28]-f[0x1]:0x00
b: bus number.
t: timestamp.
n: msg numbers.
m: msg label.
a: slave addr.
f: flags, 0 is write, 1 is read.
Transmission abnormal:
=b[1]-t[32991.050148]-n[2]-me[0]-ce[1]-ae[0x800001]=
m[0]-a[0x10]-f[0x0]:0xfa
m[1]-a[0x10]-f[0x1]:0x13
=b[1]-t[32991.050257]-n[2]-me[0]-ce[1]-ae[0x800001]=
m[0]-a[0x10]-f[0x0]:0xfb
m[1]-a[0x10]-f[0x1]:0x13
me: msg_err. Normal transmission is equal to "0", address/length mismatch will equal "-EINVAL".
ce: cmd_err. Normal transmission is equal to "0", the error was "DW_IC_ERR_TX_ABRT (0x1u)" transmit termination error.
ae: abort_source.
Whitelist interface
root@ubuntu:~# echo 01 02 > /sys/kernel/debug/dw_i2c0/whitelist
root@ubuntu:~# cat /sys/kernel/debug/dw_i2c0/whitelist
whitelist: 01 02
- The whitelist can be configured using the echo command and displayed using the cat command.
- The whitelist supports address filtering for both reldump and fifodump.
- Input is interpreted as hexadecimal by default; invalid input or addresses exceeding 128 will trigger an error. Writing "0" disables the whitelist.
i2c-tools
i2c-tools is an open-source utility suite. It has already been cross-compiled and included in the S100 system software's rootfs, allowing customers to use it directly:
- i2cdetect --- enumerates I2C buses and all devices on each bus
- i2cdump --- displays all register values of an I2C device
- i2cget --- reads the value of a specific register from an I2C device
- i2cset --- writes a value to a specific register of an I2C device
I2C Testing
Checking i2cdev nodes
Verify whether i2c-dev nodes have been created. All devices listed below are configured as master devices:
root@ubuntu:~# ls /sys/class/i2c-dev/
i2c-0 i2c-1 i2c-2 i2c-3 i2c-4 i2c-5
root@ubuntu:~# cat /sys/class/i2c-dev/i2c-0/name
Synopsys DesignWare I2C adapter
Check whether I2C device nodes have been created:
root@ubuntu:~# ls /sys/class/i2c-dev/i2c-0/device/
delete_device device i2c-dev name new_device of_node power subsystem uevent
Test procedure
- Test command: i2cdetect -y -r 3
- Test example as follows:
root@ubuntu:~# i2cdetect -r -y 3
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --