I2C 驱动

一、Linux I2C 子系统架构

应用(ioctl/read/write)
  ↓
i2c-dev(/dev/i2c-N)→ 用户态驱动
  ↓
i2c-core(i2c.h)
  ↓
i2c_adapter(adapter)→ 总线驱动(如 i2c-imx.c、stm32f7-i2c.c)
  ↓
i2c_client → 设备驱动(如 mysensor.c)
  ↓
I2C 总线硬件

二、关键结构

1. i2c_adapter(总线控制器)

struct i2c_adapter {
    struct module *owner;
    const struct i2c_algorithm *algo;
    struct device dev;
    int nr;
    char name[48];
    // ...
};

struct i2c_algorithm {
    int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
    int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr, unsigned short flags,
                      char read_write, u8 command, int size, union i2c_smbus_data *data);
    u32 (*functionality)(struct i2c_adapter *adap);
};

2. i2c_client(设备实例)

struct i2c_client {
    unsigned short addr;        // 7-bit 地址
    char name[I2C_NAME_SIZE];
    struct i2c_adapter *adapter;
    struct device dev;
    struct i2c_driver *driver;
    // ...
};

3. i2c_driver

struct i2c_driver {
    int (*probe)(struct i2c_client *, const struct i2c_device_id *);
    int (*remove)(struct i2c_client *);
    const struct i2c_device_id *id_table;
    const struct of_device_id *of_match_table;
    struct device_driver driver;
};

4. i2c_msg

struct i2c_msg {
    __u16 addr;       // 7-bit 地址
    __u16 flags;
    #define I2C_M_RD       0x01
    #define I2C_M_TEN      0x10
    #define I2C_M_DMA_SAFE 0x20
    #define I2C_M_RECV_LEN 0x40
    #define I2C_M_NO_RD_ACK 0x0800
    __u16 len;
    __u8 *buf;
};

三、设备驱动示例

1. 设备树

&i2c1 {
    clock-frequency = <100000>;
    pinctrl-0 = <&pinctrl_i2c1>;
    status = "okay";

    eeprom@50 {
        compatible = "atmel,24c02";
        reg = <0x50>;
        pagesize = <8>;
        size = <256>;
        address-width = <8>;
    };

    mysensor@68 {
        compatible = "myvendor,mysensor";
        reg = <0x68>;
        interrupt-parent = <&gpio1>;
        interrupts = <5 IRQ_TYPE_LEVEL_LOW>;
    };
};

2. 驱动代码

#include <linux/i2c.h>
#include <linux/module.h>

struct my_priv {
    struct i2c_client *client;
    struct mutex lock;
};

static int my_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    struct my_priv *priv;
    if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
        return -ENODEV;

    priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv) return -ENOMEM;
    priv->client = client;
    mutex_init(&priv->lock);
    i2c_set_clientdata(client, priv);

    // 探测设备(读 ID 寄存器)
    u8 id;
    int ret = i2c_smbus_read_byte_data(client, 0x00);
    if (ret < 0) return ret;
    id = ret;
    if (id != 0xAB) return -ENODEV;

    dev_info(&client->dev, "found mysensor id=0x%02x\n", id);
    return 0;
}

static int my_remove(struct i2c_client *client)
{
    return 0;
}

static const struct i2c_device_id my_id[] = {
    { "mysensor", 0 },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, my_id);

static const struct of_device_id my_of[] = {
    { .compatible = "myvendor,mysensor" },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_of);

static struct i2c_driver my_driver = {
    .driver = {
        .name           = "mysensor",
        .of_match_table = my_of,
    },
    .probe    = my_probe,
    .remove   = my_remove,
    .id_table = my_id,
};
module_i2c_driver(my_driver);
MODULE_LICENSE("GPL");

四、I2C 通信 API

1. 标准 I2C

int i2c_master_send(struct i2c_client *client, const char *buf, int count);
int i2c_master_recv(struct i2c_client *client, char *buf, int count);

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);

// 多消息示例(带寄存器地址的读)
u8 reg = 0x10, val;
struct i2c_msg msg[2] = {
    { .addr = client->addr, .len = 1, .buf = &reg },          // 写寄存器地址
    { .addr = client->addr, .len = 1, .buf = &val, .flags = I2C_M_RD }, // 读
};
i2c_transfer(client->adapter, msg, 2);

2. SMBus

i2c_smbus_read_byte(client);
i2c_smbus_write_byte(client, value);
i2c_smbus_read_byte_data(client, reg);
i2c_smbus_write_byte_data(client, reg, value);
i2c_smbus_read_word_data(client, reg);
i2c_smbus_read_i2c_block_data(client, reg, count, values);
i2c_smbus_write_i2c_block_data(client, reg, count, values);

3. 内联读写辅助宏

#include <linux/i2c.h>

static inline int i2c_read_reg(struct i2c_client *c, u8 reg, u8 *val)
{
    struct i2c_msg msg[2] = {
        { .addr = c->addr, .len = 1, .buf = &reg },
        { .addr = c->addr, .len = 1, .buf = val, .flags = I2C_M_RD },
    };
    return i2c_transfer(c->adapter, msg, 2) == 2 ? 0 : -EIO;
}

static inline int i2c_write_reg(struct i2c_client *c, u8 reg, u8 val)
{
    u8 buf[2] = { reg, val };
    struct i2c_msg msg = { .addr = c->addr, .len = 2, .buf = buf };
    return i2c_transfer(c->adapter, &msg, 1) == 1 ? 0 : -EIO;
}

五、I2C 适配器(总线驱动)API

总线驱动实现 master_xfer

static int my_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
    // 启动 I2C 控制器,等待完成
    // 实现可能用中断、DMA 等
}

static u32 my_func(struct i2c_adapter *adap)
{
    return I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_I2C_BLOCK;
}

static const struct i2c_algorithm my_algo = {
    .master_xfer = my_xfer,
    .functionality = my_func,
};

static int my_probe(struct platform_device *pdev)
{
    struct i2c_adapter *adap = &my_i2c->adap;
    adap->algo = &my_algo;
    adap->dev.parent = &pdev->dev;
    adap->nr = pdev->id;
    i2c_add_adapter(adap);
    // ...
}

六、调试工具

# 扫描总线
i2cdetect -l                  # 列出适配器
i2cdetect -y 1                # 扫描 /dev/i2c-1
i2cdetect -y -r 1             # 快速扫描

# 读写
i2cget -y 1 0x50 0x10         # 读寄存器
i2cset -y 1 0x50 0x10 0x55    # 写寄存器
i2cdump -y 1 0x50             # dump 所有寄存器
i2ctransfer -y 1 w2@0x50 0x10 0xAB r4

# 内核调试
cat /sys/bus/i2c/devices/1-0050/
dmesg | grep i2c

七、用户态驱动(i2c-dev)

int fd = open("/dev/i2c-1", O_RDWR);
ioctl(fd, I2C_SLAVE, 0x50);            // 从设备地址
i2c_smbus_read_byte_data(fd, 0x10);

适合:原型、调试、不想写内核模块时。

八、踩坑清单

现象 原因
No such device 地址错 / 设备未上电
IO error 总线被锁 / 接线错 / 上拉电阻
数据错 速率太高 / 信号干扰
中断不触发 GPIO 没配中断功能