Linux I2C 和 DMA

鉴于 I2C 是一种低速总线,其上传输的大多数消息都很小,因此它不被认为是 DMA 访问的主要用户。 在撰写本文时,只有 10% 的 I2C 总线主驱动程序实现了 DMA 支持。 而且绝大多数事务都很小,因此为其设置 DMA 可能会增加比普通 PIO 传输更多的开销。

因此,*不是*强制要求 I2C 消息的缓冲区是 DMA 安全的。 当该功能很少使用时,施加额外的负担似乎是不合理的。 但是,如果您的消息大小可能适用于 DMA,建议使用 DMA 安全的缓冲区。 大多数驱动程序将此阈值设置在 8 字节左右(截至今天,这主要是一个有根据的猜测)。 对于任何 16 字节或更大的消息,这可能是一个非常好的主意。 请注意,您使用的其他子系统可能会添加要求。 例如,如果您的 I2C 总线主驱动程序使用 USB 作为桥接器,那么您需要始终具有 DMA 安全的缓冲区,因为 USB 需要它。

客户端

对于客户端,如果您在 i2c_msg 中使用 DMA 安全缓冲区,请设置 I2C_M_DMA_SAFE 标志。 然后,I2C 核心和驱动程序就知道它们可以安全地在其上操作 DMA。 请注意,使用此标志是可选的。 未更新为使用此标志的 I2C 主机驱动程序将像以前一样工作。 并且像以前一样,它们有使用不安全 DMA 缓冲区的风险。 为了改善这种情况,在越来越多的客户端和主机驱动程序中使用 I2C_M_DMA_SAFE 是计划中的前进方向。 另请注意,设置此标志仅在内核空间中有意义。 用户空间数据无论如何都会复制到内核空间。 I2C 核心确保内核空间中的目标缓冲区始终具有 DMA 功能。 此外,当核心通过 I2C 模拟 SMBus 事务时,块传输的缓冲区是 DMA 安全的。 i2c_master_send()i2c_master_recv() 函数的用户现在可以使用 DMA 安全变体 (i2c_master_send_dmasafe()i2c_master_recv_dmasafe()),一旦他们知道他们的缓冲区是 DMA 安全的。 i2c_transfer() 的用户必须手动设置 I2C_M_DMA_SAFE 标志。

主机

希望实现安全 DMA 的总线主驱动程序可以使用 I2C 核心中的辅助函数。 一个为您提供给定 i2c_msg 的 DMA 安全缓冲区,只要满足某个阈值即可

dma_buf = i2c_get_dma_safe_msg_buf(msg, threshold_in_byte);

如果返回缓冲区,则对于 I2C_M_DMA_SAFE 情况,它是 msg->buf 或反弹缓冲区。 但是您不需要关心该细节,只需使用返回的缓冲区即可。 如果返回 NULL,则未满足阈值或无法分配反弹缓冲区。 在这种情况下,回退到 PIO。

在任何情况下,都需要释放从上面获得的缓冲区。 另一个辅助函数确保释放潜在使用的反弹缓冲区

i2c_put_dma_safe_msg_buf(dma_buf, msg, xferred);

最后一个参数“xferred”控制缓冲区是否同步回消息。 如果设置 DMA 出错并且没有传输任何数据,则无需同步。

核心的反弹缓冲区处理是通用的且简单的。 它将始终分配一个新的反弹缓冲区。 如果您想要更复杂的处理(例如,重用预先分配的缓冲区),您可以自由地实现自己的处理方式。

另请查看内核文档以获取详细信息。 i2c-sh_mobile 驱动程序可以用作如何使用上述辅助函数的参考示例。

最后的注意事项:如果您计划将 DMA 与 I2C(或与任何其他东西)一起使用,请确保在开发期间启用 CONFIG_DMA_API_DEBUG。 它可以帮助您找到各种问题,否则这些问题可能很难调试。