Linux I2C and DMA
1. Introduction
鉴于 I2C 是一种低速总线,通过它传输的大多数消息都很小,因此它不被视为 DMA 访问的主要用户。 截至撰写本文时,只有 10% 的 I2C 总线主驱动程序实现了 DMA 支持。 而且绝大多数事务都很小,因此为其设置 DMA 可能会比普通 PIO 传输增加更多开销。
因此,I2C 消息的缓冲区不强制要求 DMA 安全。 当该功能很少使用时,施加额外的负担似乎并不合理。 但是,如果您的消息大小可能适用于 DMA,建议使用 DMA 安全缓冲区。 大多数驱动程序的阈值约为 8 字节(不过,截至目前,这主要是一个有根据的猜测)。 对于任何 16 字节或更大的消息,这可能是一个非常好的主意。 请注意,您使用的其他子系统可能会增加要求。 例如,如果您的 I2C 总线主驱动程序使用 USB 作为桥接器,那么您始终需要有 DMA 安全缓冲区,因为 USB 需要它。
2. Clients
对于客户端,如果您在 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 标志。
3. Masters
希望实现安全 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。 它可以帮助您发现各种问题,否则调试起来可能会很复杂。