Skip to main content
Inter-Integrated Circuit (I²C) is a bidirectional, two-wire serial bus for short-distance communication between integrated circuits, where devices communicate using assigned bus addresses.

Overview

Key Characteristics

  • Bidirectional 2-wire serial bus (SDA + SCL)
  • 7-bit or 10-bit target addressing
  • Multi-controller mode support

Operating Modes

ModeSpeed
Standard mode100 kbps
Fast mode400 kbps
Fast mode plus1 Mbps (maximum supported)
High-speed mode3.4 MHz (not supported)

Communication Sequence

Packet structure: Start → Address frame (7/10-bit) → R/W bit → ACK/NACK → Data frames (8-bit) → Stop Signal rules:
  • SCL HIGH: SDA must remain stable
  • SCL LOW: SDA may change
  • Exception: START and STOP sequences may change SDA when SCL is HIGH

Transfer Modes by Subsystem

SubsystemTransfer ModeBus Speeds
LinuxFIFO, CPU DMA, GSI100 kHz, 400 kHz, 1000 kHz
BootFIFO100 kHz, 400 kHz, 1000 kHz
aDSP / Qualcomm TEE / SDCFIFOStandard

Interface Components

Device Tree Sources

PlatformDevice Tree File
Dragonwing IQ-8275arch/arm64/boot/dts/qcom/qcs8300.dtsi

APIs

SubsystemHeader
Linuxinclude/linux/i2c.h, include/linux/i2c-dev.h
Bootboot_images/boot/QcomPkg/Include/i2c_api.h
aDSPadsp_proc/core/api/buses/i2c_api.h
Qualcomm TEEtrustzone_images/core/buses/api/i2c/qupv3/i2c_api.h

Software Device Tree Configuration

Linux Device Tree Example

i2c21: i2c@b80000 {
   compatible = "qcom,geni-i2c";
   reg = <0x0 0xb80000 0x0 0x4000>;
   #address-cells = <1>;
   #size-cells = <0>;
   interrupts = <GIC_SPI 831 IRQ_TYPE_LEVEL_HIGH>;
   clocks = <&gcc GCC_QUPV3_WRAP3_S0_CLK>;
   clock-names = "se";
   pinctrl-0 = <&qup_i2c21_default>;
   pinctrl-names = "default";
   power-domains = <&rpmhpd SA8295P_CX>;
   dmas = <&gpi_dma3 0 0 QCOM_GPI_I2C>,
          <&gpi_dma3 1 0 QCOM_GPI_I2C>;
   dma-names = "tx", "rx";
   status = "disabled";
};

QUPAC Access Control

Ensure the correct protocol is set in QUPAC_Access.c:
{ QUPV3_0_SE0, QUPV3_PROTOCOL_I2C, QUPV3_MODE_FIFO, AC_HLOS, TRUE, TRUE, FALSE },
{ QUPV3_1_SE5, QUPV3_PROTOCOL_I2C, QUPV3_MODE_GSI,  AC_HLOS, FALSE, TRUE, FALSE },

Configuration Steps

1

Enable kernel configurations

Edit kernel_platform/kernel/arch/arm64/configs/qcom_defconfig:
CONFIG_QCOM_GENI_SE=y
CONFIG_I2C_QCOM_GENI=m
CONFIG_I2C_CHARDEV=m
CONFIG_QCOM_GPI_DMA=m
2

Enable I2C node in device tree

+&i2c1 {
+    status = "okay";
+};
3

Compile and flash

Compile the kernel and device tree changes, then load images to the device.

Verification

Check Device Nodes

ls /dev/i2c*
# Expected: /dev/i2c-0  /dev/i2c-1  /dev/i2c-16

Use i2c-tools

# push tools using SCP or similar tools.
scp i2cdetect root@<IP address>:/usr

#Assign permission to execute
chmod 777 i2cdetect								
# Scan bus		  
i2cdetect -y -r 0
# Dump device registers					   
i2cdump -r 0-0xff 0 0x2b b
# Read register			   
i2cget -y 0 0x2b 0x04
# Write register				
i2cset -y 0 0x2b 0x04 0xFF

Debugging

Enable Debug Logs

mount -t debugfs none /sys/kernel/debug
echo -n "file i2c-qcom-geni.c +p" > /sys/kernel/debug/dynamic_debug/control
echo -n "file i2c-core-base.c +p" > /sys/kernel/debug/dynamic_debug/control
echo -n "file gpi.c +p" > /sys/kernel/debug/dynamic_debug/control
dmesg | grep i2c

Troubleshooting

cat /proc/device-tree/soc@0/geniqup@*/i2c*/status
lsmod | grep i2c_qcom_geni
modprobe i2c_qcom_geni
cat /sys/kernel/debug/clk/gcc_qupv3_wrap0_s1_clk/clk_enable_count
Causes: No pull-up resistors, slave not responding, wrong clock frequency, bus stuck low.Add 4.7 kΩ pull-ups on SDA/SCL; adjust clock-frequency in device tree.
Symptom: geni_i2c a94000.i2c: Invalid proto 1Verify QUPAC_Access.c sets QUPV3_PROTOCOL_I2C, AC_HLOS, bAllowFifo=TRUE, bLoad=TRUE.
Verify CONFIG_QCOM_GPI_DMA=m; check dmas and dma-names in device tree.To test without DMA:
&i2c1 {
    /delete-property/ dmas;
    /delete-property/ dma-names;
};

Quick Diagnostic Commands

ls -l /dev/i2c*
i2cdetect -y -r 0
dmesg | grep -i i2c
cat /sys/kernel/debug/clk/clk_summary | grep -i qup
lsmod | grep i2c

Resources