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。它可以帮助您找到各种问题,这些问题在其他情况下可能很难调试。