【问题标题】:why repeated start based i2c operation are not supported in linux?为什么 linux 不支持基于重复启动的 i2c 操作?
【发布时间】:2016-05-06 19:27:56
【问题描述】:

我想从需要多次启动操作来读取其寄存器值的 i2c 从机读取。

在某种程度上,我在 Linux 内核 3.18.21 中跟踪了 I2C 驱动程序,我发现它不支持多启动操作,并且我无法从这个 I2C 从设备(以太网供电管理器 PD69104B1)中读取。

如果需要此 i2c 从站或其他任何需要,我仍在寻找可以扩展驱动程序的方法。

我使用 i2c-tools 3.2.1。 我试试

$ i2cdump -y 0 0x20 

但我可以看到相同的值,这意味着它每次都读取第一个寄存器。

$ i2cget -y 0 0x20 0x12

或任何其他寄存器地址返回与第一个寄存器相同的值。

这个slave支持两个读操作:

  • 字节读写地址得到它的值,但这需要多开始
  • 块读取 - 开始读取,i2c 从机将按顺序给出寄存器值,如 0x00 0x01....(第一个寄存器,第二个,第三个,第四个...等)

我尝试了所有可能的方法:

  • i2c_smbus_access()
  • i2c_smbus_write_byte()
  • i2c_smbus_read_block_data()
  • write()
  • read()

但随后大部分时间 i2c 总线进入超时错误和挂起情况。

有人知道如何在 Linux 中实现这一点吗?

更新0:

此 I2C 从机需要唯一的读取周期:

  • 改变方向:S Addr Wr [A] RegAddress [A] S Addr Rd [A] [RegValue] P

  • 短读:S Addr Rd [A] [RegValue] P

这里从 i2c 从站返回的最后一个值不要期望 ACK。

我尝试使用 I2C_M_NO_RD_ACK 但没有太多帮助。我读了一些值,然后得到 FF。

这个 POE I2C 从设备在 SCL 上的 i2c 时间超过 14 毫秒,这有点令人怀疑。这看起来像 i2c 非标准,因为 i2c 可以在 0HZ 上工作,即 SCL 可以由 master 拉伸,只要它想要。 Linux 绝对不是实时操作系统,因此无法保证实现此超时,并且可能会发生 i2c 从 SCL 超时重置。这就是我目前的结论!

使用的 I2C 消息符号来自: https://www.kernel.org/doc/Documentation/i2c/i2c-protocol

【问题讨论】:

  • 几件事:您使用的是哪种 SoC/HW?某些 i2c 主机控制器可能不支持重复启动(这就是他们的驱动程序可能没有重复启动代码的原因)。查看支持REPEAT_STARTTegra-I2C driver,因为Tegra I2C 主机控制器支持重复启动模式。另外像i2cset/i2cget这样的用户空间工具相当简单,可能不支持i2c操作的重复启动模式。
  • @TheCodeArtist 看起来 OP 只是使用不正确的 API 来发送重复启动。我在回答中添加了关于用户空间方法的注释。
  • @TheCodeArtist 它的联发科 MT7621A SoC。已经,我还在检查 i2c 主机控制器或其驱动程序是否不支持重复启动。
  • @SamProtsenko 我也在尝试使用这个 ioctl I2C_RDWR 但 I2C_M_NOSTART 看起来有些新。我会尝试的。谢谢

标签: linux linux-kernel driver device i2c


【解决方案1】:

为什么 linux 不支持基于重复启动的 i2c 操作?

事实上,它们是受支持的。

如果您正在寻找一种在用户空间中执行重复启动条件的方法,您可能需要使用I2C_RDWR 请求执行ioctl(),就像它所描述的here 一样(见最后原始问题中的代码 sn-p)和here(有问题的代码)。

下面描述了在内核空间中执行重复启动的方式。


在 Linux 内核中,repeated start condition 的 I2C 读取操作默认为组合(写入/读取)消息执行。

这是一个如何执行组合 I2C 传输的示例:

/**
 * Read set of registers via I2C using "repeated start" condition.
 *
 * Two I2C messages are being sent by this function:
 *   1. I2C write operation (write register address) with no STOP bit in the end
 *   2. I2C read operation
 *
 * @client: I2C client structure
 * @reg: register address (subaddress)
 * @len: bytes count to read
 * @buf: buffer which will contain read data
 *
 * Returns 0 on success or negative value on error.
 */
static int i2c_read_regs(struct i2c_client *client, u8 reg, u8 len, u8 *buf)
{
    int ret;
    struct i2c_msg msg[2] = {
        {
            .addr = client->addr,
            .len = 1,
            .buf = &reg,
        },
        {
            .addr = client->addr,
            .flags = I2C_M_RD,
            .len = len,
            .buf = buf,
        }
    };

    ret = i2c_transfer(client->adapter, msg, 2);
    if (ret < 0) {
        dev_err(&client->dev, "I2C read failed\n");
        return ret;
    }

    return 0;
}

要仅读取 1 个字节(单个寄存器值),您可以使用下一个辅助函数:

/**
 * Read one register via I2C using "repeated start" condition.
 *
 * @client: I2C client structure
 * @reg: register address (subaddress)
 * @val: variable to store read value
 *
 * Returns 0 on success or negative value on error.
 */
static int i2c_read_reg(struct i2c_client *client, u8 reg, u8 *val)
{
    return i2c_read_regs(client, reg, 1, val);
}

下面是i2c_read_regs(client, reg, 1, val)调用的图示:

  • 设备地址为client-&gt;addr
  • 注册地址为reg
  • 1 表示我们要读取 1 个字节的数据(图片上的粉色矩形)
  • 读取的数据将驻留在val


注意:如果您的 I2C 控制器(或其驱动程序)不支持组合消息中的重复启动,您仍然可以使用 I2C 的 bit-bang 实现,即 i2c-gpio 驱动程序。


如果没有任何效果,您可以尝试下一步作为最后的手段。出于某种原因我不太记得了,为了重复开始工作,我需要将I2C_M_NOSTART 添加到第一条消息的.flags,如下所示:

struct i2c_msg msg[2] = {
    {
        .addr = client->addr,
        .flags = I2C_M_NOSTART,
        .len = 1,
        .buf = &reg,
    },
    {
        .addr = client->addr,
        .flags = I2C_M_RD,
        .len = len,
        .buf = buf,
    }
};

Documentation/i2c/i2c-protocol中所述:

如果您为第一部分消息设置了I2C_M_NOSTART 变量, 我们不会生成Addr,但会生成起始位S


参考资料:

[1]I2C on STLinux

【讨论】:

  • 我用逻辑分析仪检查并知道这每条消息都有自己的开始和结束,这样的读取限制为 16 。您对如何生成 UPDATE0 中提到的上述消息格式有任何意见吗?
  • 不太清楚在说什么。您在哪个代码上使用逻辑分析仪?哪条消息有自己的开始和结束?你的意思是开始和停止可能?您是从内核还是用户空间执行读取?您指的是哪种消息格式?请详细说明。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-18
  • 1970-01-01
  • 2022-12-07
  • 2013-06-05
  • 1970-01-01
  • 2019-03-26
  • 1970-01-01
相关资源
最近更新 更多