# I2C Usage Instructions

## Overview

The K230 SoC integrates 5 Synopsys DesignWare I2C controllers, with details as follows:

| I2C Controller | Base Address | Default Clock Frequency | Default State |
|------------|------------|--------------|----------|
| I2C0       | 0x91405000 | 100kHz       | disabled |
| I2C1       | 0x91406000 | 400kHz       | disabled |
| I2C2       | 0x91407000 | 400kHz       | disabled |
| I2C3       | 0x91408000 | 400kHz       | disabled |
| I2C4       | 0x91409000 | 400kHz       | disabled |

> **Note**: All I2C controllers are `disabled` by default and must be enabled in the device tree before use.

## Device Tree Configuration

### Device Tree Alias Configuration

Define I2C bus aliases in the `aliases` node of the device tree, making it easy for the kernel and other device tree files to reference them.

**CanMV-K230 Development Board Example:**

```dts
aliases {
    serial3 = &uart3;
    i2c0 = &i2c4;   // Corresponds to /dev/i2c-0
    i2c1 = &i2c3;   // Corresponds to /dev/i2c-1
    mmc0 = &mmc_sd0;
    mmc1 = &mmc_sd1;
};
```

**Description:**

- The alias names (`i2c0`, `i2c1`) are defined by the kernel or board-level configuration
- The aliases point to specific I2C controller nodes (`&i2c4`, `&i2c3`)
- The kernel creates corresponding numbered I2C buses based on the aliases, and device nodes can reference them via `&i2c0`
- This approach improves the portability of the device tree, eliminating the need to modify all references when board-level configuration changes

### Enabling the I2C Controller

```dts
&i2c2 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&i2c2_pins>;
    clock-frequency = <400000>;  // 400kHz Fast Mode
};
```

### Configuring Pin Multiplexing

```dts
&iomux {
    i2c2_pins: i2c2_pins {
        pins = K230_IO11, K230_IO12;
        function = "alt3";
        bias-pull-up;
    };
};
```

### Adding I2C Device Nodes

Add device nodes under the enabled I2C bus:

```dts
&i2c2 {
    status = "okay";

    // Example: Add 24C02 EEPROM
    eeprom@50 {
        compatible = "atmel,24c02";
        reg = <0x50>;
    };

    // Example: Add TMP102 temperature sensor
    tmp102@48 {
        compatible = "ti,tmp102";
        reg = <0x48>;
    };
};
```

### CanMV-K230 Complete Configuration Example

Reference file: `arch/riscv/boot/dts/canaan/k230-canmv-01studio.dts`

```dts
// Initialize aliases
aliases {
    serial3 = &uart3;
    i2c0 = &i2c4;
    i2c1 = &i2c3;
    mmc0 = &mmc_sd0;
    mmc1 = &mmc_sd1;
};

// Pin configuration
&iomux {
    i2c2_pins: i2c2_pins {
        pins = K230_IO11, K230_IO12;
        function = "alt3";
    };
};

// Enable I2C3 (used for LT9611 HDMI bridge)
&i2c3 {
    status = "okay";

    lt9611: hdmi-bridge@3b {
        compatible = "lontium,lt9611";
        reg = <0x3b>;
        reset-gpios = <&gpio0_ports 22 GPIO_ACTIVE_HIGH>;
        interrupt-parent = <&gpio0_ports>;
        interrupts = <23 IRQ_TYPE_EDGE_FALLING>;
    };
};

// Enable I2C4
&i2c4 {
    status = "okay";
};
```

## Userspace Operations

### List I2C Buses

```bash
i2cdetect -l
```

Example output:

```bash
[root@canaan ~]# i2cdetect -l
i2c-1 i2c        Synopsys DesignWare I2C adapter  I2C adapter
i2c-2 i2c        Synopsys DesignWare I2C adapter  I2C adapter
i2c-0 i2c        Synopsys DesignWare I2C adapter  I2C adapter
```

### Scan I2C Devices

```bash
i2cdetect -y -r -a 2
```

Example output:

```bash
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: 50 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
```

### Read Register

```bash
# Read a single byte: from the device at address 0x50 on I2C bus 2, read data at offset 0x00
i2cget -y 2 0x50 0x00

# Read byte data from the specified register (b = byte)
i2cget -y 2 0x50 0x01 b

# Read word data (w = word, 16-bit)
i2cget -y 2 0x50 0x00 w
```

### Write Register

```bash
# Write a single byte
i2cset -y 2 0x50 0x00 0x55

# Write word data (w = word)
i2cset -y 2 0x50 0x00 0x1234 w
```

### Device File Interface

I2C devices appear in `/dev/` as follows:

```bash
ls -l /dev/i2c-*
```

Example output:

```bash
crw-rw---- 1 root i2c 89, 0 Jun 10 10:00 /dev/i2c-0
crw-rw---- 1 root i2c 89, 1 Jun 10 10:00 /dev/i2c-1
```

## Programming Examples

### C Language Example

```c
#include <linux/i2c-dev.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>

void print_usage(const char *prog)
{
    printf("Usage: %s [I2C_BUS]\n", prog);
    printf("  -h, --help    Show this help message\n");
    printf("  I2C_BUS       I2C bus device (default: /dev/i2c-0)\n");
}

int main(int argc, char *argv[])
{
    // 解析 -h 参数
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
            print_usage(argv[0]);
            return 0;
        }
    }

    int file;
    char *filename = "/dev/i2c-0";

    if (argc > 1) {
        filename = argv[1];
    }

    // 打开 I2C 设备
    file = open(filename, O_RDWR);
    if (file < 0) {
        perror("Failed to open i2c device");
        return -1;
    }

    printf("Scanning I2C bus: %s\n", filename);
    printf("Address  Device\n");
    printf("-------  ------\n");
    printf("\n7-bit addresses (0x00-0xff):\n");

    // 扫描所有 0x00 到 0xFF 的地址
    for (int addr = 0x00; addr <= 0xFF; addr++) {
        // 设置从设备地址
        if (ioctl(file, I2C_SLAVE, addr) < 0) {
            continue;
        }

        // 尝试读取 1 个字节
        // 此操作是安全的，不会影响设备状态
        unsigned char buf[1];
        int result = read(file, buf, 1);

        // 如果读取成功，说明设备存在
        if (result == 1) {
            printf("  0x%02X   Present\n", addr);
        }
    }

    printf("\nNote: 0x78-0x7F are reserved addresses.\n");

    close(file);
    return 0;
}
```

### Python Example

#### Using Adafruit Blinka busio

```python

import time
import sys
import board
import busio

def main(bus_num=0):
    """Scan I2C devices on specified bus."""
    # SCL and SDA pins for different I2C buses

    print(f"Hello Blinka! Using I2C bus {bus_num}")

    _, scl, sda = board.pin.i2cPorts[bus_num]
    i2c = busio.I2C(scl, sda)

    while not i2c.try_lock():
        pass

    try:
        devices = i2c.scan()
        print(f"I2C devices found on bus {bus_num}: {[hex(i) for i in devices]}")
    finally:
        i2c.unlock()


if __name__ == "__main__":
    bus = 0
    if len(sys.argv) > 1:
        if sys.argv[1] in ["-h", "--help"]:
            print("Usage: python pi_busio_i2c.py [bus_num]")
            print("  bus_num  I2C bus number (default: 0)")
            print("           0 - I2C0 (SCL/SDA)")
            print("           1 - I2C1 (SCL1/SDA1)")
            print("           2 - I2C2 (SCL2/SDA2)")
            sys.exit(0)
        bus = int(sys.argv[1])

    main(bus)

```

#### Scanning Devices Without Libraries

```python
#!/usr/bin/env python3
import fcntl
import sys

I2C_SLAVE = 0x0703

def i2c_scan(bus_num=0):
    with open(f"/dev/i2c-{bus_num}", "r+b", buffering=0) as f:
        print(f"Scanning I2C bus {bus_num}:")
        print("Address  Device")
        print("-------  ------")
        for addr in range(0x00, 0x100):
            try:
                fcntl.ioctl(f.fileno(), I2C_SLAVE, addr)
                f.read(1)  # 尝试读取一个字节
                print(f"  0x{addr:02X}   Present")
            except:
                pass

if __name__ == "__main__":
    if len(sys.argv) > 1 and sys.argv[1] in ["-h", "--help"]:
        print("Usage: python i2c.py [bus_num]")
        print("  bus_num  I2C bus number (default: 0)")
        print("  -h, --help  Show this help message")
        sys.exit(0)

    bus = 0
    if len(sys.argv) > 1:
        bus = int(sys.argv[1])
    i2c_scan(bus)
```

## Related Files

- Device tree file: `arch/riscv/boot/dts/canaan/k230.dtsi`
- Platform driver: `drivers/i2c/busses/i2c-designware-platdrv.c`
- Core driver: `drivers/i2c/busses/i2c-designware-core.h`
- Kconfig: `drivers/i2c/busses/Kconfig`
