Linux I2C slave interface description
1. Introduction
Wolfram Sang wsa@sang-engineering.com 于 2014 年第 15 周编辑。
如果使用的 I2C 控制器具有从机功能,Linux 也可以成为 I2C 从机。 为此,需要总线驱动程序中的从属支持以及提供实际功能的独立于硬件的软件后端。 后者的一个示例是从属 eeprom 驱动程序,它充当双内存驱动程序。 虽然总线上的另一个 I2C 主设备可以像常规 EEPROM 一样访问它,但 Linux I2C 从设备可以通过 sysfs 访问内容并根据需要处理数据。 后端驱动程序和 I2C 总线驱动程序通过事件进行通信。 这是一个小图,直观地显示了数据流和数据传输方式。 虚线仅标记一个示例。 后端还可以使用字符设备,仅在内核中,或者完全不同的东西:
e.g. sysfs I2C slave events I/O registers
+-----------+ v +---------+ v +--------+ v +------------+
| Userspace +........+ Backend +-----------+ Driver +-----+ Controller |
+-----------+ +---------+ +--------+ +------------+
| |
----------------------------------------------------------------+-- I2C
--------------------------------------------------------------+---- Bus
注意:从技术上讲,后端和驱动程序之间还有 I2C 内核。 然而,在撰写本文时,该层是透明的。
2. User manual
I2C 从后端的行为类似于标准 I2C 客户端。 因此,您可以按照文档如何实例化 I2C 设备中的描述来实例化它们。 唯一的区别是 i2c 从机后端有自己的地址空间。 因此,您必须将 0x1000 添加到您最初请求的地址中。 从用户空间在总线 1 上的 7 位地址 0x64 处实例化从属 eeprom 驱动程序的示例:
# echo slave-24c02 0x1064 > /sys/bus/i2c/devices/i2c-1/new_device
每个后端都应附带单独的文档来描述其特定行为和设置。
3. Developer manual
首先,将详细描述总线驱动程序和后端使用的事件。 之后,将给出一些扩展总线驱动程序和编写后端的实现提示。
3.1 I2C slave events
总线驱动程序使用以下函数将事件发送到后端:
ret = i2c_slave_event(client, event, &val)
“client”描述了 I2C 从设备。 “事件”是下文描述的特殊事件类型之一。 ‘val’ 保存要读/写的数据字节的 u8 值,因此是双向的。 即使 val 不用于事件,也必须始终提供指向 val 的指针,即此处不要使用 NULL。 ‘ret’ 是后端的返回值。 强制事件必须由公交车司机提供,并且必须由后端司机检查。
Event 类型:
- I2C_SLAVE_WRITE_REQUESTED (强制的)
‘val’: 未使用
‘ret’: 如果后端准备好了,则为 0,否则会出现一些 errno
另一个I2C主机想要向我们写入数据。 一旦检测到我们自己的地址和写入位,就应该发送此事件。 数据尚未到达,因此没有任何内容可以处理或返回。 返回后,总线驱动程序必须始终确认地址阶段。 如果“ret”为零,则后端初始化或唤醒完成,并且可以接收更多数据。 如果“ret”是错误号,则总线驱动程序应确认所有传入字节,直到出现下一个停止条件以强制重试传输。
- I2C_SLAVE_READ_REQUESTED (强制的)
‘val’: 后端返回要发送的第一个字节
‘ret’: 总是 0
另一个I2C主机想要从我们这里读取数据。 一旦检测到我们自己的地址和读取位,就应该发送此事件。 返回后,总线驱动程序应传输第一个字节。
- I2C_SLAVE_WRITE_RECEIVED (强制的)
‘val’: 总线驱动程序发送接收到的字节
‘ret’: 如果应该确认该字节,则为 0;如果应该确认该字节,则为一些 errno
另一个 I2C 主设备已向我们发送了一个需要在“val”中设置的字节。 如果“ret”为零,则总线驱动程序应确认该字节。 如果’ret’是一个errno,那么该字节应该被nacked。
- I2C_SLAVE_READ_PROCESSED (强制的)
‘val’: 后端返回要发送的下一个字节
‘ret’: 总是 0
总线驱动程序请求将下一个字节发送到“val”中的另一个 I2C 主设备。 重要提示:这并不意味着前一个字节已被确认,它仅意味着前一个字节已移出到总线! 为了确保无缝传输,大多数硬件在前一个字节仍被移出时请求下一个字节。 如果主机在当前移出的字节后发送 NACK 并停止读取,则此处请求的该字节永远不会被使用。 不过,它很可能需要在下一个 I2C_SLAVE_READ_REQUEST 时再次发送,具体取决于您的后端。
- I2C_SLAVE_STOP (强制的)
‘val’: 未使用
‘ret’: 总是 0
收到停止条件。 这种情况随时可能发生,后端应重置 I2C 传输的状态机,以便能够接收新请求。
3.2 Software backends
如果你想写一个软件后端:
-
使用标准 i2c_driver 及其匹配机制
-
编写处理上述从属事件的slave_callback(最好使用状态机)
-
通过 i2c_slave_register() 注册此回调
以 i2c-slave-eeprom 驱动程序为例。
3.3 Bus driver support
如果要向总线驱动程序添加从属支持:
-
实现注册/取消注册从属设备的调用,并将它们添加到 struct i2c_algorithm 中。 注册时,您可能需要设置 I2C 从机地址并启用从机特定中断。 如果您使用runtime pm,您应该使用pm_runtime_get_sync(),因为您的设备通常需要始终开机才能检测其从机地址。 注销时,执行与上述相反的操作。
-
捕获从机中断并将适当的 i2c_slave_events 发送到后端。
请注意,大多数硬件支持在同一总线上作为主设备和从设备。 因此,如果您扩展总线驱动程序,请确保该驱动程序也支持它。 在几乎所有情况下,从站支持不需要禁用主站功能。
以 i2c-rcar 驱动程序为例。
4. About ACK/NACK
始终确认地址阶段是良好行为,因此主设备知道设备是否基本上存在或是否神秘消失。 用NACK来表示忙碌很麻烦。 SMBus 要求始终确认地址阶段,而 I2C 规范对此更为宽松。 大多数 I2C 控制器在检测其从地址时也会自动 ACK,因此没有选项对它们进行 NACK。 由于这些原因,该 API 在地址阶段不支持 NACK。
目前,如果主设备在从我们读取数据时对字节进行了 ACK 或 NACK,则没有从设备事件可报告。 如果需要的话,我们可以将其设为可选活动。 但是,这种情况应该非常罕见,因为主设备预计会在此之后发送 STOP,并且我们为此准备了一个事件。 另外,请记住,并非所有 I2C 控制器都有可能报告该事件。
5. About buffers
在开发这个 API 的过程中,出现了使用缓冲区而不仅仅是字节的问题。 这样的扩展可能是可能的,但在撰写本文时其用处尚不清楚。 使用缓冲区时要记住的一些要点:
-
缓冲区应该是选择加入的,并且后端驱动程序始终必须支持基于字节的事务作为最终的后备方案,因为这就是大多数硬件的工作方式。
-
对于模拟硬件寄存器的后端来说,缓冲区很大程度上没有帮助,因为在写入每个字节后,应该立即触发一个动作。 对于读取,如果后端由于内部处理而刚刚更新了寄存器,则保存在缓冲区中的数据可能会过时。
-
主设备可以随时发送 STOP。 对于部分传输的缓冲区,这意味着需要额外的代码来处理此异常。 此类代码往往容易出错。