[Linux服务器] [Linux]学习笔记系列 -- [drivers][I2C]busses

485 0
Honkers 2026-2-2 18:19:49 | 显示全部楼层 |阅读模式

title: busses
categories:

  • linux
  • drivers
  • I2C
    tags:
  • linux
  • drivers
  • I2C
    abbrlink: 3b770652
    date: 2026-01-12 14:42:46

https://github.com/wdfk-prog/linux-study


Linux I2C 控制器驱动目录解析

[drivers/i2c/busses] [I2C 总线控制器(Adapter)驱动集合] [为不同 SoC/控制器实现 I2C 主控/从控能力,并向 i2c-core 注册 i2c_adapter]

介绍

drivers/i2c/busses/ 是 I2C 控制器(bus controller / host controller)驱动的集中目录:

  • 每个文件(或一组文件)通常对应一种硬件 I2C 控制器 IP(例如某 SoC 内置 I2C、某 PCI/ACPI I2C 控制器、某外设桥接控制器)。
  • 这些驱动的共同目标是:把硬件能力抽象成一个 i2c_adapter,并通过 i2c-core 让上层设备驱动(i2c_client 驱动)能用统一接口读写从设备。

历史与背景

为了解决什么问题而诞生

Linux 需要把“不同厂家的 I2C 控制器寄存器/时序/中断/DMA差异”屏蔽掉,让上层设备驱动只关心“在某条 I2C 总线上读写某地址的寄存器”。因此形成了:

  • i2c-core:统一框架与设备模型;
  • busses:控制器驱动实现底层传输与错误恢复。

重要演进脉络(按技术方向)

  • 平台描述方式演进:从板级硬编码(boardinfo)到 Device Tree/ACPI 为主;
  • 从“纯轮询”到“中断/DMA”:提升吞吐与降低 CPU 占用;
  • 复用与通用化:常见通用模块(如 bit-banging 算法、mux、regmap、PM/runtime、clk/reset)被广泛使用,减少重复代码。

社区与主流应用

这是内核里非常“常用且长期维护”的目录:几乎每个嵌入式/PC 平台都依赖其中至少一个控制器驱动才能让 I2C 外设工作。


核心原理与设计

核心工作原理(控制器驱动的标准骨架)

一个典型 busses 下的 I2C 控制器驱动,结构大体一致:

  1. probe(初始化)
  • 解析平台资源:MMIO、IRQ、时钟/复位、DMA(可选)

  • 初始化控制器寄存器:时钟分频、FIFO/中断、超时、过滤等

  • 组装 struct i2c_adapter:

    • algo 或 master_xfer/master_xfer_atomic
    • functionality
    • dev.parent、nr、name 等
  • i2c_add_adapter() 或 i2c_add_numbered_adapter() 注册到 i2c-core

  1. 传输路径(最关键)
    上层调用最终会落到控制器驱动的传输实现,常见两种风格:
  • 基于 master_xfer() 的自实现:驱动直接实现 i2c_adapter 的传输回调,支持 repeated-start、多 msg;
  • 基于通用算法层 i2c_algorithm:驱动提供底层 bit 操作或简单发送接口,交由算法层组织。

典型需要处理:

  • START/STOP、地址相位、ACK/NACK
  • repeated-start(组合事务:写寄存器地址后紧接读)
  • FIFO 装填/取出,中断触发点
  • 超时、仲裁丢失、总线错误(SCL/SDA 异常)
  1. 并发与上下文
  • I2C 传输通常会睡眠(等待中断/完成),所以多数控制器驱动在进程上下文工作;
  • 有的控制器支持 atomic 传输或用于紧急日志路径,会实现 *_atomic 变体(但要求硬件/实现可在原子上下文完成)。
  1. 错误恢复与总线“卡死”处理
    常见机制:
  • 控制器软复位/清 FIFO
  • 发送 STOP 强制释放
  • SCL “钟控解锁”(部分硬件/平台需要)
  • 结合 i2c_bus_recovery_info(如果驱动实现了恢复钩子)

主要优势(目录层面体现)

  • 硬件差异封装:上层设备驱动与控制器解耦
  • 性能可扩展:从简单轮询到 IRQ/DMA/FIFO 都能覆盖
  • 生态完整:DT/ACPI、PM、runtime PM、clk/reset、DMA 等都能集成

局限性与不适用点(常见“坑位”)

  • 时序/电气问题不在软件能完全解决:上拉、电平转换、总线长度、噪声会表现为 NACK/超时/卡死
  • 控制器差异导致语义不一致:例如 repeated-start 支持不完整、10-bit 地址、SMBus PEC、clock-stretch 处理差异
  • 调试成本:传输失败时需要同时考虑硬件、设备树参数、驱动状态机

使用场景

什么时候它是首选

  • 你要让某个平台的 I2C 外设工作(RTC、EEPROM、sensor、PMIC、codec 等),必须先有对应控制器驱动提供 adapter。
  • 你需要更高性能/更低 CPU:选支持 IRQ/FIFO/DMA 的控制器驱动路径更合适。

不推荐的场景(以及原因)

  • 如果硬件只有 GPIO 且没有 I2C 控制器:可以用 bit-banging(通常不在 busses 的“控制器 IP 驱动”路径里解决所有问题),但性能与稳定性更受限。
  • 对强实时、超高频 I/O:I2C 总线本身速率有限且共享仲裁,可能不合适,应考虑 SPI、并口、MIPI/I3C 等(取决于器件支持)。

对比分析(实现方式/开销/资源/隔离/启动)

这里对比三类常见实现形态(都可能出现在相关目录/组合中):

1) 硬件控制器驱动(本目录主角) vs GPIO bit-banging

  • 实现方式:硬件控制器用寄存器/FIFO/IRQ;bit-banging 由软件翻转 GPIO 时序
  • 性能开销:硬件控制器通常更低 CPU;bit-banging CPU 占用更高
  • 资源占用:硬件控制器需要对应 IP;bit-banging 占 GPIO
  • 隔离级别:硬件控制器对时序更稳定;bit-banging 对抢占/延迟更敏感
  • 启动速度:都快;bit-banging 更依赖 GPIO 配置正确

2) 轮询模式 vs 中断模式 vs DMA 模式(同一控制器内的差异)

  • 实现方式:轮询读写状态寄存器;中断驱动状态机;DMA 通过 DMA engine 搬运
  • 性能开销:轮询 CPU 占用高;中断较均衡;DMA 在大块/高频更省 CPU
  • 资源占用:DMA 需要通道与描述符;中断需要 IRQ;轮询最少资源
  • 隔离级别:DMA/中断更能抵御长事务;轮询容易受调度抖动影响
  • 启动速度:轮询最简单;DMA 初始化稍复杂

3) 纯 I2C vs SMBus 特性依赖

  • 实现方式:I2C 是通用消息;SMBus 有额外语义(PEC、Alert、Host Notify 等)
  • 性能/资源:SMBus 特性可能需要额外中断线/从模式支持
  • 隔离:SMBus Alert 这类共享机制隔离更弱,需要更严格的错误处理
  • 启动:SMBus 扩展可能引入额外注册/探测路径

总结

关键特性

  • drivers/i2c/busses/ 提供“把硬件 I2C 控制器变成 i2c_adapter”的驱动实现集合
  • 核心关注点:传输状态机(START/STOP/repeated-start)、并发/睡眠、中断/DMA、错误恢复、DT/ACPI 资源管理
  • 不同文件的差异主要来自:控制器能力(FIFO/DMA/从模式/恢复机制)与平台集成方式(DT/ACPI/PCI)

drivers/i2c/busses/i2c-stm32f4.c

stm32f4_i2c_hw_config / stm32f4_i2c_set_periph_clk_freq / stm32f4_i2c_set_rise_time / stm32f4_i2c_set_speed_mode:I2C 外设时钟与时序寄存器配置(初始化路径)


stm32f4_i2c_set_bits / stm32f4_i2c_clr_bits / stm32f4_i2c_disable_irq:寄存器位操作与中断屏蔽辅助

  1. #include <linux/clk.h>
  2. #include <linux/delay.h>
  3. #include <linux/err.h>
  4. #include <linux/i2c.h>
  5. #include <linux/interrupt.h>
  6. #include <linux/io.h>
  7. #include <linux/iopoll.h>
  8. #include <linux/module.h>
  9. #include <linux/of_address.h>
  10. #include <linux/of_irq.h>
  11. #include <linux/of.h>
  12. #include <linux/platform_device.h>
  13. #include <linux/reset.h>
  14. #include "i2c-stm32.h"
  15. #define STM32F4_I2C_CR1 0x00
  16. #define STM32F4_I2C_CR2 0x04
  17. #define STM32F4_I2C_DR 0x10
  18. #define STM32F4_I2C_SR1 0x14
  19. #define STM32F4_I2C_SR2 0x18
  20. #define STM32F4_I2C_CCR 0x1C
  21. #define STM32F4_I2C_TRISE 0x20
  22. #define STM32F4_I2C_FLTR 0x24
  23. #define STM32F4_I2C_CR1_POS BIT(11)
  24. #define STM32F4_I2C_CR1_ACK BIT(10)
  25. #define STM32F4_I2C_CR1_STOP BIT(9)
  26. #define STM32F4_I2C_CR1_START BIT(8)
  27. #define STM32F4_I2C_CR1_PE BIT(0)
  28. #define STM32F4_I2C_CR2_FREQ_MASK GENMASK(5, 0)
  29. #define STM32F4_I2C_CR2_FREQ(n) ((n) & STM32F4_I2C_CR2_FREQ_MASK)
  30. #define STM32F4_I2C_CR2_ITBUFEN BIT(10)
  31. #define STM32F4_I2C_CR2_ITEVTEN BIT(9)
  32. #define STM32F4_I2C_CR2_ITERREN BIT(8)
  33. #define STM32F4_I2C_CR2_IRQ_MASK (STM32F4_I2C_CR2_ITBUFEN | \
  34. STM32F4_I2C_CR2_ITEVTEN | \
  35. STM32F4_I2C_CR2_ITERREN)
  36. #define STM32F4_I2C_CCR_CCR_MASK GENMASK(11, 0)
  37. #define STM32F4_I2C_CCR_CCR(n) ((n) & STM32F4_I2C_CCR_CCR_MASK)
  38. #define STM32F4_I2C_CCR_FS BIT(15)
  39. #define STM32F4_I2C_CCR_DUTY BIT(14)
  40. #define STM32F4_I2C_TRISE_VALUE_MASK GENMASK(5, 0)
  41. #define STM32F4_I2C_TRISE_VALUE(n) ((n) & STM32F4_I2C_TRISE_VALUE_MASK)
  42. #define STM32F4_I2C_MIN_STANDARD_FREQ 2U
  43. #define STM32F4_I2C_MIN_FAST_FREQ 6U
  44. #define STM32F4_I2C_MAX_FREQ 46U
  45. #define HZ_TO_MHZ 1000000
  46. struct stm32f4_i2c_msg {
  47. u8 addr;
  48. u32 count;
  49. u8 *buf;
  50. int result;
  51. bool stop;
  52. };
  53. struct stm32f4_i2c_dev {
  54. struct i2c_adapter adap;
  55. struct device *dev;
  56. void __iomem *base;
  57. struct completion complete;
  58. struct clk *clk;
  59. int speed;
  60. int parent_rate;
  61. struct stm32f4_i2c_msg msg;
  62. };
  63. /**
  64. * @brief 置位寄存器中的 mask 位;使用 relaxed 访问以减少不必要的屏障开销。
  65. * @note I2C 外设寄存器通常为强顺序外设区;relaxed 语义依赖体系结构对 MMIO 的基本有序性保证。
  66. */
  67. static inline void stm32f4_i2c_set_bits(void __iomem *reg, u32 mask)
  68. {
  69. writel_relaxed(readl_relaxed(reg) | mask, reg);
  70. }
  71. /**
  72. * @brief 清零寄存器中的 mask 位;使用 read-modify-write。
  73. */
  74. static inline void stm32f4_i2c_clr_bits(void __iomem *reg, u32 mask)
  75. {
  76. writel_relaxed(readl_relaxed(reg) & ~mask, reg);
  77. }
  78. /**
  79. * @brief 关闭该控制器在 CR2 中使能的三类 IRQ:BUF/EVT/ERR。
  80. * @note 这里仅做寄存器位清除,不负责处理中断控制器侧的屏蔽。
  81. */
  82. static void stm32f4_i2c_disable_irq(struct stm32f4_i2c_dev *i2c_dev)
  83. {
  84. void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR2;
  85. stm32f4_i2c_clr_bits(reg, STM32F4_I2C_CR2_IRQ_MASK);
  86. }
复制代码

stm32f4_i2c_set_periph_clk_freq:配置 CR2.FREQ(单位 MHz)并校验硬件允许区间

  1. /**
  2. * @brief 配置 CR2.FREQ:将父时钟频率写入硬件要求的 MHz 字段。
  3. *
  4. * 关键点:
  5. * - clk_get_rate() 返回 Hz;
  6. * - CR2.FREQ 的语义是 MHz(整数),因此用 DIV_ROUND_UP 做向上取整;
  7. * - Standard/Fast 模式下硬件对可接受的 MHz 区间不同,超出则拒绝配置。
  8. */
  9. static int stm32f4_i2c_set_periph_clk_freq(struct stm32f4_i2c_dev *i2c_dev)
  10. {
  11. u32 freq;
  12. u32 cr2 = 0;
  13. i2c_dev->parent_rate = clk_get_rate(i2c_dev->clk);
  14. freq = DIV_ROUND_UP(i2c_dev->parent_rate, HZ_TO_MHZ);
  15. if (i2c_dev->speed == STM32_I2C_SPEED_STANDARD) {
  16. if (freq < STM32F4_I2C_MIN_STANDARD_FREQ ||
  17. freq > STM32F4_I2C_MAX_FREQ) {
  18. dev_err(i2c_dev->dev,
  19. "bad parent clk freq for standard mode\n");
  20. return -EINVAL;
  21. }
  22. } else {
  23. if (freq < STM32F4_I2C_MIN_FAST_FREQ ||
  24. freq > STM32F4_I2C_MAX_FREQ) {
  25. dev_err(i2c_dev->dev,
  26. "bad parent clk freq for fast mode\n");
  27. return -EINVAL;
  28. }
  29. }
  30. cr2 |= STM32F4_I2C_CR2_FREQ(freq);
  31. writel_relaxed(cr2, i2c_dev->base + STM32F4_I2C_CR2);
  32. return 0;
  33. }
复制代码

stm32f4_i2c_set_rise_time:配置 TRISE(按 I2C 规范最大上升沿时间离散化)

  1. /**
  2. * @brief 配置 TRISE:将 I2C 规范的最大 SCL 上升沿时间换算为寄存器值。
  3. *
  4. * 不直观点:
  5. * - TRISE 字段表达“最大上升沿时间对应的时钟周期数 + 1”;
  6. * - freq 取 CR2.FREQ 的 MHz 值(由 parent_rate 向上取整得到);
  7. * - Standard:t_rise_max=1000ns => TRISE = freq + 1;
  8. * - Fast:t_rise_max=300ns => TRISE = freq * 300/1000 + 1,即 freq*3/10 + 1。
  9. */
  10. static void stm32f4_i2c_set_rise_time(struct stm32f4_i2c_dev *i2c_dev)
  11. {
  12. u32 freq = DIV_ROUND_UP(i2c_dev->parent_rate, HZ_TO_MHZ);
  13. u32 trise;
  14. if (i2c_dev->speed == STM32_I2C_SPEED_STANDARD)
  15. trise = freq + 1;
  16. else
  17. trise = freq * 3 / 10 + 1;
  18. writel_relaxed(STM32F4_I2C_TRISE_VALUE(trise),
  19. i2c_dev->base + STM32F4_I2C_TRISE);
  20. }
复制代码

stm32f4_i2c_set_speed_mode:配置 CCR(SCL 周期分频)与 FS(Standard/Fast 选择)

  1. /**
  2. * @brief 配置 CCR 以逼近目标 SCL 频率,并在 Fast 模式下置位 FS。
  3. *
  4. * 不直观点:
  5. * - Standard 模式:高/低电平对称,近似 SCL = parent_rate / (2*CCR);
  6. * - Fast 模式:此实现选择 duty=0(低电平约为高电平两倍),近似 SCL = parent_rate / (3*CCR);
  7. * - Fast 模式使用 DIV_ROUND_UP,避免 CCR 取整导致 SCL 过高或边界时序不满足。
  8. */
  9. static void stm32f4_i2c_set_speed_mode(struct stm32f4_i2c_dev *i2c_dev)
  10. {
  11. u32 val;
  12. u32 ccr = 0;
  13. if (i2c_dev->speed == STM32_I2C_SPEED_STANDARD) {
  14. val = i2c_dev->parent_rate / (I2C_MAX_STANDARD_MODE_FREQ * 2);
  15. } else {
  16. val = DIV_ROUND_UP(i2c_dev->parent_rate,
  17. I2C_MAX_FAST_MODE_FREQ * 3);
  18. ccr |= STM32F4_I2C_CCR_FS;
  19. }
  20. ccr |= STM32F4_I2C_CCR_CCR(val);
  21. writel_relaxed(ccr, i2c_dev->base + STM32F4_I2C_CCR);
  22. }
复制代码

stm32f4_i2c_hw_config:按 CR2→TRISE→CCR→CR1 使能的顺序完成硬件初始化

  1. /**
  2. * @brief 配置 I2C 外设工作参数并使能外设。
  3. *
  4. * 顺序要点:
  5. * - 先写 CR2.FREQ(为 TRISE/CCR 计算与硬件内部时序提供基准);
  6. * - 再写 TRISE 与 CCR;
  7. * - 最后置位 CR1.PE 使能外设。
  8. */
  9. static int stm32f4_i2c_hw_config(struct stm32f4_i2c_dev *i2c_dev)
  10. {
  11. int ret;
  12. ret = stm32f4_i2c_set_periph_clk_freq(i2c_dev);
  13. if (ret)
  14. return ret;
  15. stm32f4_i2c_set_rise_time(i2c_dev);
  16. stm32f4_i2c_set_speed_mode(i2c_dev);
  17. writel_relaxed(STM32F4_I2C_CR1_PE, i2c_dev->base + STM32F4_I2C_CR1);
  18. return 0;
  19. }
复制代码

stm32f4_i2c_wait_free_bus:轮询 SR2.BUSY 等待总线空闲(提交 START 前的门禁)

  1. #define STM32F4_I2C_SR2_BUSY BIT(1)
  2. /**
  3. * @brief 轮询等待 BUSY 清零,避免在总线仍忙时发起新的传输序列。
  4. * @note readl_relaxed_poll_timeout 的时间单位由调用者给定(此处为 us 级参数)。
  5. */
  6. static int stm32f4_i2c_wait_free_bus(struct stm32f4_i2c_dev *i2c_dev)
  7. {
  8. u32 status;
  9. int ret;
  10. ret = readl_relaxed_poll_timeout(i2c_dev->base + STM32F4_I2C_SR2,
  11. status,
  12. !(status & STM32F4_I2C_SR2_BUSY),
  13. 10, 1000);
  14. if (ret) {
  15. dev_dbg(i2c_dev->dev, "bus not free\n");
  16. ret = -EBUSY;
  17. }
  18. return ret;
  19. }
复制代码

stm32f4_i2c_isr_event / stm32f4_i2c_isr_error / stm32f4_i2c_xfer_msg:中断驱动的 I2C 传输状态机(收发细节与 ACK/POS/BTF 处理)


stm32f4_i2c_write_byte / stm32f4_i2c_write_msg / stm32f4_i2c_read_msg:对 DR 的单字节收发封装

  1. /**
  2. * @brief 向数据寄存器 DR 写入 1 字节(用于发送地址或数据)。
  3. */
  4. static void stm32f4_i2c_write_byte(struct stm32f4_i2c_dev *i2c_dev, u8 byte)
  5. {
  6. writel_relaxed(byte, i2c_dev->base + STM32F4_I2C_DR);
  7. }
  8. /**
  9. * @brief 写方向:把当前 msg->buf 指向的 1 字节写入 DR,并推进指针与剩余计数。
  10. * @note 这里依赖硬件在 TXE/BTF 事件下按时取走 DR 数据。
  11. */
  12. static void stm32f4_i2c_write_msg(struct stm32f4_i2c_dev *i2c_dev)
  13. {
  14. struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
  15. stm32f4_i2c_write_byte(i2c_dev, *msg->buf++); /**< 推进 buf 指针,表示该字节已被提交到硬件。 */
  16. msg->count--; /**< 逻辑剩余字节数减少。 */
  17. }
  18. /**
  19. * @brief 读方向:从 DR 读取 1 字节写入 msg->buf,并推进指针与剩余计数。
  20. * @note DR 通常为 32 位读出,这里仅使用低 8 位;驱动用 u32 承接以匹配 readl 接口。
  21. */
  22. static void stm32f4_i2c_read_msg(struct stm32f4_i2c_dev *i2c_dev)
  23. {
  24. struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
  25. u32 rbuf;
  26. rbuf = readl_relaxed(i2c_dev->base + STM32F4_I2C_DR);
  27. *msg->buf++ = rbuf; /**< 仅取低 8 位写入缓冲区。 */
  28. msg->count--;
  29. }
复制代码

stm32f4_i2c_terminate_xfer:结束一次消息(STOP 或重复 START)并唤醒等待者

  1. /**
  2. * @brief 结束当前消息的硬件动作,并通过 completion 通知线程侧退出等待。
  3. *
  4. * 不直观点:
  5. * - 对“最后一条消息”生成 STOP;
  6. * - 对“非最后一条消息”生成重复 START(用于 combined message)。
  7. */
  8. static void stm32f4_i2c_terminate_xfer(struct stm32f4_i2c_dev *i2c_dev)
  9. {
  10. struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
  11. void __iomem *reg;
  12. stm32f4_i2c_disable_irq(i2c_dev); /**< 终止时先关中断,避免状态机继续推进造成重复 complete。 */
  13. reg = i2c_dev->base + STM32F4_I2C_CR1;
  14. if (msg->stop)
  15. stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_STOP); /**< 最后一条:请求生成 STOP。 */
  16. else
  17. stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_START); /**< 非最后:请求重复 START,交给下一条消息续接。 */
  18. complete(&i2c_dev->complete); /**< 唤醒 stm32f4_i2c_xfer_msg() 中等待的线程。 */
  19. }
复制代码

stm32f4_i2c_handle_write:写方向在 TXE/BTF 驱动下逐字节灌入 DR,并在结束时 terminate

  1. /**
  2. * @brief 写方向处理:在 TXE 或 BTF 事件下推进发送。
  3. *
  4. * 要点:
  5. * - 当 count 归零时,关闭 ITBUFEN(避免继续因 TXE 触发抢占);
  6. * - 若已无数据可发,进入 terminate_xfer(),由其决定 STOP/重复 START。
  7. */
  8. static void stm32f4_i2c_handle_write(struct stm32f4_i2c_dev *i2c_dev)
  9. {
  10. struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
  11. void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR2;
  12. if (msg->count) {
  13. stm32f4_i2c_write_msg(i2c_dev);
  14. if (!msg->count) {
  15. stm32f4_i2c_clr_bits(reg, STM32F4_I2C_CR2_ITBUFEN); /**< 无剩余数据:关闭 BUF 中断。 */
  16. }
  17. } else {
  18. stm32f4_i2c_terminate_xfer(i2c_dev);
  19. }
  20. }
复制代码

stm32f4_i2c_handle_read / stm32f4_i2c_handle_rx_done / stm32f4_i2c_handle_rx_addr:读方向的边界条件(1/2/3 字节)与 ACK/POS 控制

  • STM32F4 I2C 接收时涉及 移位寄存器与 DR 的装载时序

    • RXNE:DR 非空(可读)
    • BTF:字节传输完成;接收时常意味着“移位寄存器已有新字节,但 DR 里的旧字节未读走”等组合情况
  • 2 字节接收:必须在读最后两字节之前先置 STOP/重复 START,否则会错过总线条件生成时机。

  • 3 字节接收:必须在读 N-2 之前配置 NACK(通过清 ACK),让最后字节能以 NACK 结束。

  1. /**
  2. * @brief RXNE 事件处理:根据剩余 count 决定“立即读 DR”或“等待 BTF 再读”。
  3. *
  4. * 不直观点:
  5. * - count==2/3 时,不在 RXNE 里读 DR,而是等待 BTF,以满足 STOP/NACK 的时序要求。
  6. */
  7. static void stm32f4_i2c_handle_read(struct stm32f4_i2c_dev *i2c_dev)
  8. {
  9. struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
  10. void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR2;
  11. switch (msg->count) {
  12. case 1:
  13. stm32f4_i2c_disable_irq(i2c_dev); /**< 最后 1 字节:直接关中断,读出并完成。 */
  14. stm32f4_i2c_read_msg(i2c_dev);
  15. complete(&i2c_dev->complete);
  16. break;
  17. case 2:
  18. case 3:
  19. stm32f4_i2c_clr_bits(reg, STM32F4_I2C_CR2_ITBUFEN); /**< 关闭 BUF 中断,避免 RXNE 反复打断;后续由 BTF 收尾。 */
  20. break;
  21. default:
  22. stm32f4_i2c_read_msg(i2c_dev); /**< N>3:可持续在 RXNE 读取,直到剩余进入 3/2 的边界区。 */
  23. }
  24. }
复制代码
  1. /**
  2. * @brief BTF 事件处理(读方向):在“必须等待 BTF”的阶段完成关键时序动作与最后字节读取。
  3. */
  4. static void stm32f4_i2c_handle_rx_done(struct stm32f4_i2c_dev *i2c_dev)
  5. {
  6. struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
  7. void __iomem *reg;
  8. u32 mask;
  9. int i;
  10. switch (msg->count) {
  11. case 2:
  12. reg = i2c_dev->base + STM32F4_I2C_CR1;
  13. /**
  14. * @brief 2 字节接收的关键点:
  15. * 在读取最后两字节(N-1, N)之前先置 STOP/重复 START,
  16. * 否则硬件可能无法在正确窗口发出相应总线条件。
  17. */
  18. if (msg->stop)
  19. stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_STOP);
  20. else
  21. stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_START);
  22. for (i = 2; i > 0; i--)
  23. stm32f4_i2c_read_msg(i2c_dev); /**< 依次读出最后两字节。 */
  24. reg = i2c_dev->base + STM32F4_I2C_CR2;
  25. mask = STM32F4_I2C_CR2_ITEVTEN | STM32F4_I2C_CR2_ITERREN;
  26. stm32f4_i2c_clr_bits(reg, mask); /**< 收尾:关闭事件/错误中断,避免重复进入状态机。 */
  27. complete(&i2c_dev->complete);
  28. break;
  29. case 3:
  30. /**
  31. * @brief 3 字节接收的关键点:
  32. * 在读取 N-2 之前清 ACK,确保最后 1 字节以 NACK 结束,从而正确终止读序列。
  33. */
  34. reg = i2c_dev->base + STM32F4_I2C_CR1;
  35. stm32f4_i2c_clr_bits(reg, STM32F4_I2C_CR1_ACK);
  36. stm32f4_i2c_read_msg(i2c_dev); /**< 读取 N-2,之后流程会进入剩余 2 字节路径。 */
  37. break;
  38. default:
  39. stm32f4_i2c_read_msg(i2c_dev);
  40. }
  41. }
复制代码
  1. /**
  2. * @brief ADDR 事件处理(读方向):在地址阶段根据剩余字节数配置 ACK/POS/STOP/START,并清除 ADDR。
  3. *
  4. * 不直观点(硬件要求):
  5. * - 清 ADDR 需完成 SR1->SR2 的读序列;SR1 已在 isr_event 读取,此处读 SR2 即可完成清除。
  6. * - POS 位用于控制 NACK/ACK 作用于“当前字节还是下一字节”,是 2 字节接收的关键。
  7. */
  8. static void stm32f4_i2c_handle_rx_addr(struct stm32f4_i2c_dev *i2c_dev)
  9. {
  10. struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
  11. u32 cr1;
  12. switch (msg->count) {
  13. case 0:
  14. stm32f4_i2c_terminate_xfer(i2c_dev);
  15. readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2); /**< 清 ADDR。 */
  16. break;
  17. case 1:
  18. /**
  19. * @brief 1 字节接收:
  20. * 关闭 ACK,并清 POS,使 NACK/STOP(或重复 START)在接收该字节后立即生效。
  21. */
  22. cr1 = readl_relaxed(i2c_dev->base + STM32F4_I2C_CR1);
  23. cr1 &= ~(STM32F4_I2C_CR1_ACK | STM32F4_I2C_CR1_POS);
  24. writel_relaxed(cr1, i2c_dev->base + STM32F4_I2C_CR1);
  25. readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2); /**< 清 ADDR。 */
  26. if (msg->stop)
  27. cr1 |= STM32F4_I2C_CR1_STOP;
  28. else
  29. cr1 |= STM32F4_I2C_CR1_START;
  30. writel_relaxed(cr1, i2c_dev->base + STM32F4_I2C_CR1);
  31. break;
  32. case 2:
  33. /**
  34. * @brief 2 字节接收:
  35. * 清 ACK 并置 POS,使 NACK 对“下一字节”生效,这是两字节接收的硬件约束用法。
  36. */
  37. cr1 = readl_relaxed(i2c_dev->base + STM32F4_I2C_CR1);
  38. cr1 &= ~STM32F4_I2C_CR1_ACK;
  39. cr1 |= STM32F4_I2C_CR1_POS;
  40. writel_relaxed(cr1, i2c_dev->base + STM32F4_I2C_CR1);
  41. readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2); /**< 清 ADDR。 */
  42. break;
  43. default:
  44. /**
  45. * @brief N>2 接收:
  46. * 使能 ACK,清 POS,使硬件按常规逐字节 ACK,直到进入 3/2 字节边界区再切换策略。
  47. */
  48. cr1 = readl_relaxed(i2c_dev->base + STM32F4_I2C_CR1);
  49. cr1 |= STM32F4_I2C_CR1_ACK;
  50. cr1 &= ~STM32F4_I2C_CR1_POS;
  51. writel_relaxed(cr1, i2c_dev->base + STM32F4_I2C_CR1);
  52. readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2); /**< 清 ADDR。 */
  53. break;
  54. }
  55. }
复制代码

stm32f4_i2c_isr_event:事件中断总入口(SB/ADDR/TXE/RXNE/BTF 分发)

  1. /**
  2. * @brief I2C 事件中断:根据 SR1 与当前中断使能位推导有效事件集合,并驱动状态机前进。
  3. *
  4. * 不直观点:
  5. * - 只在 ITBUFEN 使能时才把 TXE/RXNE 纳入 possible_status,避免把“未使能的事件”误判为有效;
  6. * - ADDR 事件后会开启 ITBUFEN,使 TXE/RXNE 开始参与推进;
  7. * - 写方向与读方向使用同一套 event 分发,但行为不同(handle_write vs handle_read/rx_done/rx_addr)。
  8. */
  9. static irqreturn_t stm32f4_i2c_isr_event(int irq, void *data)
  10. {
  11. struct stm32f4_i2c_dev *i2c_dev = data;
  12. struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
  13. u32 possible_status = STM32F4_I2C_SR1_ITEVTEN_MASK;
  14. u32 status, ien, event, cr2;
  15. cr2 = readl_relaxed(i2c_dev->base + STM32F4_I2C_CR2);
  16. ien = cr2 & STM32F4_I2C_CR2_IRQ_MASK;
  17. if (ien & STM32F4_I2C_CR2_ITBUFEN)
  18. possible_status |= STM32F4_I2C_SR1_ITBUFEN_MASK; /**< BUF 中断开启时才关心 TXE/RXNE。 */
  19. status = readl_relaxed(i2c_dev->base + STM32F4_I2C_SR1);
  20. event = status & possible_status;
  21. if (!event) {
  22. dev_dbg(i2c_dev->dev,
  23. "spurious evt irq (status=0x%08x, ien=0x%08x)\n",
  24. status, ien);
  25. return IRQ_NONE;
  26. }
  27. if (event & STM32F4_I2C_SR1_SB)
  28. stm32f4_i2c_write_byte(i2c_dev, msg->addr); /**< SB:起始条件完成后写入 8-bit 地址(含 R/W 位)。 */
  29. if (event & STM32F4_I2C_SR1_ADDR) {
  30. if (msg->addr & I2C_M_RD) /**< 这里利用 “R/W 位为 1” 的事实来判断读方向。 */
  31. stm32f4_i2c_handle_rx_addr(i2c_dev);
  32. else
  33. readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2); /**< 写方向:读 SR2 完成清 ADDR 序列。 */
  34. cr2 |= STM32F4_I2C_CR2_ITBUFEN; /**< 地址阶段结束后开启 BUF 中断,使 TXE/RXNE 参与状态机。 */
  35. writel_relaxed(cr2, i2c_dev->base + STM32F4_I2C_CR2);
  36. }
  37. if ((event & STM32F4_I2C_SR1_TXE) && !(msg->addr & I2C_M_RD))
  38. stm32f4_i2c_handle_write(i2c_dev);
  39. if ((event & STM32F4_I2C_SR1_RXNE) && (msg->addr & I2C_M_RD))
  40. stm32f4_i2c_handle_read(i2c_dev);
  41. if (event & STM32F4_I2C_SR1_BTF) {
  42. if (msg->addr & I2C_M_RD)
  43. stm32f4_i2c_handle_rx_done(i2c_dev);
  44. else
  45. stm32f4_i2c_handle_write(i2c_dev);
  46. }
  47. return IRQ_HANDLED;
  48. }
复制代码

stm32f4_i2c_isr_error:错误中断入口(ARLO/AF/BERR)并以 completion 终止传输

  1. /**
  2. * @brief I2C 错误中断:清除错误标志、设置 msg->result,并终止当前等待。
  3. *
  4. * 不直观点:
  5. * - AF(NACK)在发送模式下要求软件生成 STOP;
  6. * - 这里统一 disable_irq + complete,使线程侧从 wait_for_completion_timeout 返回并读取 result。
  7. */
  8. static irqreturn_t stm32f4_i2c_isr_error(int irq, void *data)
  9. {
  10. struct stm32f4_i2c_dev *i2c_dev = data;
  11. struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
  12. void __iomem *reg;
  13. u32 status;
  14. status = readl_relaxed(i2c_dev->base + STM32F4_I2C_SR1);
  15. if (status & STM32F4_I2C_SR1_ARLO) { /**< 仲裁丢失:通常建议上层重试。 */
  16. status &= ~STM32F4_I2C_SR1_ARLO;
  17. writel_relaxed(status, i2c_dev->base + STM32F4_I2C_SR1);
  18. msg->result = -EAGAIN;
  19. }
  20. if (status & STM32F4_I2C_SR1_AF) { /**< ACK failure:从机 NACK。 */
  21. if (!(msg->addr & I2C_M_RD)) {
  22. reg = i2c_dev->base + STM32F4_I2C_CR1;
  23. stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_STOP); /**< 发送模式下必须显式生成 STOP。 */
  24. }
  25. status &= ~STM32F4_I2C_SR1_AF;
  26. writel_relaxed(status, i2c_dev->base + STM32F4_I2C_SR1);
  27. msg->result = -EIO;
  28. }
  29. if (status & STM32F4_I2C_SR1_BERR) { /**< 总线错误:非法 START/STOP 等。 */
  30. status &= ~STM32F4_I2C_SR1_BERR;
  31. writel_relaxed(status, i2c_dev->base + STM32F4_I2C_SR1);
  32. msg->result = -EIO;
  33. }
  34. stm32f4_i2c_disable_irq(i2c_dev);
  35. complete(&i2c_dev->complete);
  36. return IRQ_HANDLED;
  37. }
复制代码

stm32f4_i2c_xfer_msg:线程侧发起单条消息,并等待 ISR 完成(或超时)

  1. /**
  2. * @brief 传输单条 I2C 消息:配置会话状态、使能中断、必要时发起 START,并等待完成。
  3. *
  4. * 不直观点:
  5. * - f4_msg->addr 使用 i2c_8bit_addr_from_msg() 得到“8-bit 地址(含 R/W 位)”,便于在 SB 事件直接写入 DR;
  6. * - is_first 时检查 BUSY 并发起 START;其余消息由 terminate_xfer 触发重复 START 来续接;
  7. * - ret 以 msg->result 为主,超时则覆盖为 -ETIMEDOUT。
  8. */
  9. static int stm32f4_i2c_xfer_msg(struct stm32f4_i2c_dev *i2c_dev,
  10. struct i2c_msg *msg, bool is_first,
  11. bool is_last)
  12. {
  13. struct stm32f4_i2c_msg *f4_msg = &i2c_dev->msg;
  14. void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR1;
  15. unsigned long time_left;
  16. u32 mask;
  17. int ret;
  18. f4_msg->addr = i2c_8bit_addr_from_msg(msg); /**< 8-bit 地址:最低位即 R/W 位。 */
  19. f4_msg->buf = msg->buf;
  20. f4_msg->count = msg->len;
  21. f4_msg->result = 0;
  22. f4_msg->stop = is_last;
  23. reinit_completion(&i2c_dev->complete);
  24. mask = STM32F4_I2C_CR2_ITEVTEN | STM32F4_I2C_CR2_ITERREN;
  25. stm32f4_i2c_set_bits(i2c_dev->base + STM32F4_I2C_CR2, mask); /**< 先开事件/错误中断,后续 ADDR 阶段再开 ITBUFEN。 */
  26. if (is_first) {
  27. ret = stm32f4_i2c_wait_free_bus(i2c_dev);
  28. if (ret)
  29. return ret;
  30. stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_START); /**< 触发起始条件;随后由 SB/ADDR/TXE/RXNE/BTF 推进。 */
  31. }
  32. time_left = wait_for_completion_timeout(&i2c_dev->complete,
  33. i2c_dev->adap.timeout);
  34. ret = f4_msg->result;
  35. if (!time_left)
  36. ret = -ETIMEDOUT;
  37. return ret;
  38. }
复制代码

stm32f4_i2c_xfer / stm32f4_i2c_algo / stm32f4_i2c_probe / stm32f4_i2c_remove:适配器算法挂接与平台驱动生命周期(从 i2c_transfer 到硬件)


stm32f4_i2c_xfer:I2C core 的传输入口(为每个 msg 调用 xfer_msg,并在结束时返回 num)

  1. /**
  2. * @brief I2C core 调用的 xfer 回调:顺序执行 msgs[0..num-1],并返回已完成的消息数。
  3. *
  4. * 不直观点:
  5. * - 这里在每次传输序列开始前 clk_enable(),结束后 clk_disable();
  6. * - 若某条消息失败,立即终止序列并返回错误码;
  7. * - 成功路径返回 num(而不是 0),符合 i2c_algorithm->xfer 的约定。
  8. */
  9. static int stm32f4_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[],
  10. int num)
  11. {
  12. struct stm32f4_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap); /**< 从适配器取回控制器私有数据。 */
  13. int ret, i;
  14. ret = clk_enable(i2c_dev->clk); /**< 传输前使能外设时钟,保证寄存器与状态机可用。 */
  15. if (ret) {
  16. dev_err(i2c_dev->dev, "Failed to enable clock\n");
  17. return ret;
  18. }
  19. for (i = 0; i < num && !ret; i++)
  20. ret = stm32f4_i2c_xfer_msg(i2c_dev, &msgs[i], i == 0,
  21. i == num - 1); /**< 顺序提交:首条生成 START,末条生成 STOP。 */
  22. clk_disable(i2c_dev->clk); /**< 传输结束关闭时钟,降低功耗并避免无谓寄存器活动。 */
  23. return (ret < 0) ? ret : num; /**< 失败返回负 errno;成功返回完成的消息数。 */
  24. }
复制代码

stm32f4_i2c_func / stm32f4_i2c_algo:向 I2C core 宣告能力并提供算法入口

  1. /**
  2. * @brief 向 I2C core 报告该适配器支持的功能集合。
  3. *
  4. * @note I2C_FUNC_SMBUS_EMUL 表示可以通过 I2C 原生传输在软件层模拟部分 SMBus 语义。
  5. */
  6. static u32 stm32f4_i2c_func(struct i2c_adapter *adap)
  7. {
  8. return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
  9. }
  10. /**
  11. * @brief I2C 适配器算法表:I2C core 通过它调用到底层控制器驱动。
  12. */
  13. static const struct i2c_algorithm stm32f4_i2c_algo = {
  14. .xfer = stm32f4_i2c_xfer, /**< 核心传输入口:实现 combined message。 */
  15. .functionality = stm32f4_i2c_func, /**< 能力回调:用于 i2c_check_functionality 等门禁判断。 */
  16. };
复制代码

stm32f4_i2c_probe:资源获取、复位、IRQ 注册、硬件配置,并注册 i2c_adapter

  1. /**
  2. * @brief 平台设备 probe:将设备树描述的 I2C 控制器实例化为一个 i2c_adapter。
  3. *
  4. * 不直观点(调用关系核心):
  5. * - devm_platform_get_and_ioremap_resource:映射寄存器基址到 i2c_dev->base;
  6. * - irq_of_parse_and_map:从设备树取得 event/error IRQ 并映射;
  7. * - devm_request_irq:注册两路中断处理函数(事件与错误);
  8. * - stm32f4_i2c_hw_config:写 CR2/TRISE/CCR/CR1 使能外设;
  9. * - i2c_add_adapter:把适配器注册到 I2C core,使其可被 /dev/i2c-* 或内核客户端使用。
  10. */
  11. static int stm32f4_i2c_probe(struct platform_device *pdev)
  12. {
  13. struct device_node *np = pdev->dev.of_node;
  14. struct stm32f4_i2c_dev *i2c_dev;
  15. struct resource *res;
  16. u32 irq_event, irq_error, clk_rate;
  17. struct i2c_adapter *adap;
  18. struct reset_control *rst;
  19. int ret;
  20. i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL); /**< devm 管理:随设备卸载自动释放。 */
  21. if (!i2c_dev)
  22. return -ENOMEM;
  23. i2c_dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); /**< 映射寄存器资源。 */
  24. if (IS_ERR(i2c_dev->base))
  25. return PTR_ERR(i2c_dev->base);
  26. irq_event = irq_of_parse_and_map(np, 0); /**< event IRQ:驱动 SB/ADDR/TXE/RXNE/BTF 等事件。 */
  27. if (!irq_event) {
  28. dev_err(&pdev->dev, "IRQ event missing or invalid\n");
  29. return -EINVAL;
  30. }
  31. irq_error = irq_of_parse_and_map(np, 1); /**< error IRQ:驱动 ARLO/AF/BERR 等错误。 */
  32. if (!irq_error) {
  33. dev_err(&pdev->dev, "IRQ error missing or invalid\n");
  34. return -EINVAL;
  35. }
  36. i2c_dev->clk = devm_clk_get_enabled(&pdev->dev, NULL); /**< 获取并使能时钟;失败则 probe 终止。 */
  37. if (IS_ERR(i2c_dev->clk)) {
  38. dev_err(&pdev->dev, "Failed to enable clock\n");
  39. return PTR_ERR(i2c_dev->clk);
  40. }
  41. rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); /**< 获取复位控制器,确保外设处于已知初态。 */
  42. if (IS_ERR(rst))
  43. return dev_err_probe(&pdev->dev, PTR_ERR(rst),
  44. "Error: Missing reset ctrl\n");
  45. reset_control_assert(rst);
  46. udelay(2); /**< 给硬件最小复位保持时间,避免亚稳态。 */
  47. reset_control_deassert(rst);
  48. i2c_dev->speed = STM32_I2C_SPEED_STANDARD; /**< 默认 Standard,若设备树频率>=400k 则切换 Fast。 */
  49. ret = of_property_read_u32(np, "clock-frequency", &clk_rate);
  50. if (!ret && clk_rate >= I2C_MAX_FAST_MODE_FREQ)
  51. i2c_dev->speed = STM32_I2C_SPEED_FAST;
  52. i2c_dev->dev = &pdev->dev;
  53. ret = devm_request_irq(&pdev->dev, irq_event, stm32f4_i2c_isr_event, 0,
  54. pdev->name, i2c_dev); /**< 注册事件中断。 */
  55. if (ret) {
  56. dev_err(&pdev->dev, "Failed to request irq event %i\n",
  57. irq_event);
  58. return ret;
  59. }
  60. ret = devm_request_irq(&pdev->dev, irq_error, stm32f4_i2c_isr_error, 0,
  61. pdev->name, i2c_dev); /**< 注册错误中断。 */
  62. if (ret) {
  63. dev_err(&pdev->dev, "Failed to request irq error %i\n",
  64. irq_error);
  65. return ret;
  66. }
  67. ret = stm32f4_i2c_hw_config(i2c_dev); /**< 写寄存器完成外设初始化与使能。 */
  68. if (ret)
  69. return ret;
  70. adap = &i2c_dev->adap;
  71. i2c_set_adapdata(adap, i2c_dev); /**< 建立“adapter→private data”关联,供 xfer 回调取回 i2c_dev。 */
  72. snprintf(adap->name, sizeof(adap->name), "STM32 I2C(%pa)", &res->start);
  73. adap->owner = THIS_MODULE;
  74. adap->timeout = 2 * HZ; /**< 供 xfer_msg 的 wait_for_completion_timeout 使用。 */
  75. adap->retries = 0;
  76. adap->algo = &stm32f4_i2c_algo; /**< 挂接算法表:xfer/functionality。 */
  77. adap->dev.parent = &pdev->dev;
  78. adap->dev.of_node = pdev->dev.of_node;
  79. init_completion(&i2c_dev->complete); /**< 初始化 completion,同步线程与 ISR。 */
  80. ret = i2c_add_adapter(adap); /**< 注册到 I2C core:此后可被 i2c_transfer 等路径调用。 */
  81. if (ret)
  82. return ret;
  83. platform_set_drvdata(pdev, i2c_dev); /**< 平台驱动数据绑定,remove 时可取回。 */
  84. clk_disable(i2c_dev->clk); /**< probe 完成后关闭时钟,等待首次传输再开启。 */
  85. dev_info(i2c_dev->dev, "STM32F4 I2C driver registered\n");
  86. return 0;
  87. }
复制代码

stm32f4_i2c_remove:从 I2C core 注销适配器

  1. /**
  2. * @brief 平台设备移除:注销 i2c_adapter。
  3. *
  4. * @note 资源释放主要由 devm 机制完成;这里只需撤销 I2C core 注册。
  5. */
  6. static void stm32f4_i2c_remove(struct platform_device *pdev)
  7. {
  8. struct stm32f4_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
  9. i2c_del_adapter(&i2c_dev->adap);
  10. }
复制代码

of_match / platform_driver / module_platform_driver:设备树匹配与模块注册(结构性说明)

  1. static const struct of_device_id stm32f4_i2c_match[] = {
  2. { .compatible = "st,stm32f4-i2c", },
  3. {},
  4. };
  5. MODULE_DEVICE_TABLE(of, stm32f4_i2c_match);
  6. static struct platform_driver stm32f4_i2c_driver = {
  7. .driver = {
  8. .name = "stm32f4-i2c",
  9. .of_match_table = stm32f4_i2c_match,
  10. },
  11. .probe = stm32f4_i2c_probe,
  12. .remove = stm32f4_i2c_remove,
  13. };
  14. module_platform_driver(stm32f4_i2c_driver);
复制代码

drivers/i2c/busses/i2c-stm32f7.c

STM32F7 I2C 控制器驱动(i2c-stm32f7)整理

这份驱动相对 STM32F4 版本最大的差异:

  1. 寄存器模型变了(CR2/NBYTES/RELOAD/START/STOP 等)。
  2. 支持更多能力:DMA、SMBus(含 PEC/Alert/Host Notify)、Slave 模式、原子传输(polling)、Runtime PM/系统休眠。
  3. 时序配置从 “CCR/TRISE” 变成 “TIMINGR(PRESC/SCLDEL/SDADEL/SCLH/SCLL)”。

stm32f7_i2c_setup_timing / stm32f7_i2c_compute_timing / stm32f7_get_specs:I2C 总线时序输入解析与 TIMINGR 参数寻优计算

  • 这部分代码的核心意义在于把“目标 I2C 速率 + 时序约束(上升/下降沿、滤波延迟、规范最小高低电平等)”转换为 TIMINGR 可编程字段(PRESC/SCLDEL/SDADEL/SCLH/SCLL)。该过程是确定性的数值搜索,与是否多核无关;单核下的主要差异仅在于计算发生在 probe/配置阶段,不涉及并发同步策略。
  • 无 MMU 不改变该算法的数学与寄存器意义;但在“不可睡眠/原子上下文”场景下不应调用此类计算路径(本段位于配置阶段,通常在进程上下文执行)。

stm32f7_get_specs:按目标速率选择对应 I2C 规范约束集合

  1. static struct stm32f7_i2c_spec *stm32f7_get_specs(u32 rate)
  2. {
  3. int i;
  4. for (i = 0; i < ARRAY_SIZE(stm32f7_i2c_specs); i++)
  5. if (rate <= stm32f7_i2c_specs[i].rate)
  6. return &stm32f7_i2c_specs[i];
  7. return ERR_PTR(-EINVAL);
  8. }
复制代码

stm32f7_i2c_compute_timing:在规范约束与滤波/边沿条件下搜索 TIMINGR 最优解

作用与原理

  • 将 clock_src(Hz) 与 speed_freq(Hz)分别换算为纳秒周期:

    • i2cclk = round(NSEC_PER_SEC / clock_src):I2C 内核时钟周期(ns)
    • i2cbus = round(NSEC_PER_SEC / speed_freq):目标总线周期(ns)
  • 将设备树给出的数字滤波宽度 dnf_dt(ns) 换算为滤波抽头数 dnf(0..15):dnf = round(dnf_dt / i2cclk),并进一步得到 dnf_delay = dnf * i2cclk。

  • 通过 I2C 规范给出的 hddat_min/vddat_max/sudat_min/l_min/h_min 与板级 rise_time/fall_time,联立得到:

    • SDADEL 可行区间 [sdadel_min, sdadel_max]
    • SCLDEL 下界 scldel_min
  • 先枚举 (PRESC,SCLDEL,SDADEL) 的可行组合,再对每组枚举 (SCLL,SCLH),计算得到实际 tscl 并使其落入 [clk_min, clk_max],同时满足 tscl_l >= l_min、tscl_h >= h_min 等约束;最终以 |tscl - i2cbus| 最小作为“最优解”。

  1. #define RATE_MIN(rate) ((rate) * 8 / 10)
  2. static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev,
  3. struct stm32f7_i2c_setup *setup,
  4. struct stm32f7_i2c_timings *output)
  5. {
  6. struct stm32f7_i2c_spec *specs;
  7. u32 p_prev = STM32F7_PRESC_MAX;
  8. u32 i2cclk = DIV_ROUND_CLOSEST(NSEC_PER_SEC,
  9. setup->clock_src); /**< I2C 内核时钟周期(ns),用于把“计数值”映射为时间。 */
  10. u32 i2cbus = DIV_ROUND_CLOSEST(NSEC_PER_SEC,
  11. setup->speed_freq); /**< 目标 SCL 周期(ns),用于误差度量。 */
  12. u32 clk_error_prev = i2cbus; /**< 记录当前最优解的周期误差(|tscl - i2cbus|)。 */
  13. u32 tsync;
  14. u32 af_delay_min, af_delay_max;
  15. u32 dnf_delay;
  16. u32 clk_min, clk_max;
  17. int sdadel_min, sdadel_max;
  18. int scldel_min;
  19. struct stm32f7_i2c_timings *v, *_v, *s;
  20. struct list_head solutions;
  21. u16 p, l, a, h;
  22. int ret = 0;
  23. specs = stm32f7_get_specs(setup->speed_freq);
  24. if (specs == ERR_PTR(-EINVAL)) {
  25. dev_err(i2c_dev->dev, "speed out of bound {%d}\n",
  26. setup->speed_freq);
  27. return -EINVAL;
  28. }
  29. if ((setup->rise_time > specs->rise_max) ||
  30. (setup->fall_time > specs->fall_max)) {
  31. dev_err(i2c_dev->dev,
  32. "timings out of bound Rise{%d>%d}/Fall{%d>%d}\n",
  33. setup->rise_time, specs->rise_max,
  34. setup->fall_time, specs->fall_max);
  35. return -EINVAL;
  36. }
  37. i2c_dev->dnf = DIV_ROUND_CLOSEST(i2c_dev->dnf_dt, i2cclk); /**< 数字滤波宽度(ns)换算为抽头数(dnf)。 */
  38. if (i2c_dev->dnf > STM32F7_I2C_DNF_MAX) {
  39. dev_err(i2c_dev->dev,
  40. "DNF out of bound %d/%d\n",
  41. i2c_dev->dnf * i2cclk, STM32F7_I2C_DNF_MAX * i2cclk);
  42. return -EINVAL;
  43. }
  44. af_delay_min =
  45. (i2c_dev->analog_filter ?
  46. STM32F7_I2C_ANALOG_FILTER_DELAY_MIN : 0); /**< 模拟滤波引入的最小延迟(ns)。 */
  47. af_delay_max =
  48. (i2c_dev->analog_filter ?
  49. STM32F7_I2C_ANALOG_FILTER_DELAY_MAX : 0); /**< 模拟滤波引入的最大延迟(ns)。 */
  50. dnf_delay = i2c_dev->dnf * i2cclk; /**< 数字滤波延迟近似为 dnf 个时钟周期。 */
  51. sdadel_min = specs->hddat_min + setup->fall_time -
  52. af_delay_min - (i2c_dev->dnf + 3) * i2cclk; /**< SDADEL 下界:数据保持时间与滤波/边沿共同约束。 */
  53. sdadel_max = specs->vddat_max - setup->rise_time -
  54. af_delay_max - (i2c_dev->dnf + 4) * i2cclk; /**< SDADEL 上界:数据有效窗口与滤波/边沿共同约束。 */
  55. scldel_min = setup->rise_time + specs->sudat_min; /**< SCLDEL 下界:满足数据建立时间要求。 */
  56. if (sdadel_min < 0)
  57. sdadel_min = 0;
  58. if (sdadel_max < 0)
  59. sdadel_max = 0;
  60. dev_dbg(i2c_dev->dev, "SDADEL(min/max): %i/%i, SCLDEL(Min): %i\n",
  61. sdadel_min, sdadel_max, scldel_min);
  62. INIT_LIST_HEAD(&solutions);
  63. /** 先枚举 PRESC/SCLDEL/SDADEL 的可行集合,缩小后续搜索空间。 */
  64. for (p = 0; p < STM32F7_PRESC_MAX; p++) {
  65. for (l = 0; l < STM32F7_SCLDEL_MAX; l++) {
  66. u32 scldel = (l + 1) * (p + 1) * i2cclk; /**< SCLDEL 寄存器字段对应的实际延迟(ns)。 */
  67. if (scldel < scldel_min)
  68. continue;
  69. for (a = 0; a < STM32F7_SDADEL_MAX; a++) {
  70. u32 sdadel = (a * (p + 1) + 1) * i2cclk; /**< SDADEL 字段对应的实际延迟(ns)。 */
  71. if (((sdadel >= sdadel_min) &&
  72. (sdadel <= sdadel_max)) &&
  73. (p != p_prev)) {
  74. v = kmalloc(sizeof(*v), GFP_KERNEL);
  75. if (!v) {
  76. ret = -ENOMEM;
  77. goto exit;
  78. }
  79. v->presc = p;
  80. v->scldel = l;
  81. v->sdadel = a;
  82. p_prev = p;
  83. list_add_tail(&v->node,
  84. &solutions);
  85. break;
  86. }
  87. }
  88. if (p_prev == p)
  89. break;
  90. }
  91. }
  92. if (list_empty(&solutions)) {
  93. dev_err(i2c_dev->dev, "no Prescaler solution\n");
  94. ret = -EPERM;
  95. goto exit;
  96. }
  97. tsync = af_delay_min + dnf_delay + (2 * i2cclk); /**< 同步项:滤波与内部同步带来的固定时间开销。 */
  98. s = NULL;
  99. clk_max = NSEC_PER_SEC / RATE_MIN(setup->speed_freq); /**< 允许的最大周期(ns):对应最低允许速率(0.8*目标)。 */
  100. clk_min = NSEC_PER_SEC / setup->speed_freq; /**< 允许的最小周期(ns):对应目标速率本身。 */
  101. /** 在可行(PRESC,SCLDEL,SDADEL)集合中继续搜索(SCLL,SCLH),并以周期误差最小选择最优解。 */
  102. list_for_each_entry(v, &solutions, node) {
  103. u32 prescaler = (v->presc + 1) * i2cclk; /**< PRESC 对应的时间量化步进(ns)。 */
  104. for (l = 0; l < STM32F7_SCLL_MAX; l++) {
  105. u32 tscl_l = (l + 1) * prescaler + tsync; /**< SCL 低电平持续时间(ns)。 */
  106. if ((tscl_l < specs->l_min) ||
  107. (i2cclk >=
  108. ((tscl_l - af_delay_min - dnf_delay) / 4))) {
  109. continue;
  110. }
  111. for (h = 0; h < STM32F7_SCLH_MAX; h++) {
  112. u32 tscl_h = (h + 1) * prescaler + tsync; /**< SCL 高电平持续时间(ns)。 */
  113. u32 tscl = tscl_l + tscl_h +
  114. setup->rise_time + setup->fall_time; /**< 近似总周期(ns):含上升/下降沿时间。 */
  115. if ((tscl >= clk_min) && (tscl <= clk_max) &&
  116. (tscl_h >= specs->h_min) &&
  117. (i2cclk < tscl_h)) {
  118. int clk_error = tscl - i2cbus; /**< 与目标周期的偏差,用于择优。 */
  119. if (clk_error < 0)
  120. clk_error = -clk_error;
  121. if (clk_error < clk_error_prev) {
  122. clk_error_prev = clk_error;
  123. v->scll = l;
  124. v->sclh = h;
  125. s = v;
  126. }
  127. }
  128. }
  129. }
  130. }
  131. if (!s) {
  132. dev_err(i2c_dev->dev, "no solution at all\n");
  133. ret = -EPERM;
  134. goto exit;
  135. }
  136. output->presc = s->presc;
  137. output->scldel = s->scldel;
  138. output->sdadel = s->sdadel;
  139. output->scll = s->scll;
  140. output->sclh = s->sclh;
  141. dev_dbg(i2c_dev->dev,
  142. "Presc: %i, scldel: %i, sdadel: %i, scll: %i, sclh: %i\n",
  143. output->presc,
  144. output->scldel, output->sdadel,
  145. output->scll, output->sclh);
  146. exit:
  147. list_for_each_entry_safe(v, _v, &solutions, node) {
  148. list_del(&v->node);
  149. kfree(v);
  150. }
  151. return ret;
  152. }
复制代码

stm32f7_get_lower_rate:计算“降档”的下一个更低速率档位

  1. static u32 stm32f7_get_lower_rate(u32 rate)
  2. {
  3. int i = ARRAY_SIZE(stm32f7_i2c_specs);
  4. while (--i)
  5. if (stm32f7_i2c_specs[i].rate < rate)
  6. break;
  7. return stm32f7_i2c_specs[i].rate;
  8. }
复制代码

stm32f7_i2c_setup_timing:解析固件时序输入并调用 compute_timing 生成最终寄存器字段

  1. static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev,
  2. struct stm32f7_i2c_setup *setup)
  3. {
  4. struct i2c_timings timings, *t = &timings;
  5. int ret = 0;
  6. t->bus_freq_hz = I2C_MAX_STANDARD_MODE_FREQ;
  7. t->scl_rise_ns = i2c_dev->setup.rise_time;
  8. t->scl_fall_ns = i2c_dev->setup.fall_time;
  9. i2c_parse_fw_timings(i2c_dev->dev, t, false); /**< 从设备树/固件读取 bus_freq 与 rise/fall/dnf 等时序参数。 */
  10. if (t->bus_freq_hz > I2C_MAX_FAST_MODE_PLUS_FREQ) {
  11. dev_err(i2c_dev->dev, "Invalid bus speed (%i>%i)\n",
  12. t->bus_freq_hz, I2C_MAX_FAST_MODE_PLUS_FREQ);
  13. return -EINVAL;
  14. }
  15. setup->speed_freq = t->bus_freq_hz;
  16. i2c_dev->setup.rise_time = t->scl_rise_ns;
  17. i2c_dev->setup.fall_time = t->scl_fall_ns;
  18. i2c_dev->dnf_dt = t->digital_filter_width_ns; /**< 数字滤波宽度(ns),后续会换算为抽头数 dnf。 */
  19. setup->clock_src = clk_get_rate(i2c_dev->clk); /**< I2C 外设时钟源频率(Hz),用于换算寄存器字段对应的时间量。 */
  20. if (!setup->clock_src) {
  21. dev_err(i2c_dev->dev, "clock rate is 0\n");
  22. return -EINVAL;
  23. }
  24. if (!of_property_read_bool(i2c_dev->dev->of_node, "i2c-digital-filter"))
  25. i2c_dev->dnf_dt = STM32F7_I2C_DNF_DEFAULT; /**< 未启用数字滤波时,将宽度视为 0。 */
  26. do {
  27. ret = stm32f7_i2c_compute_timing(i2c_dev, setup,
  28. &i2c_dev->timing);
  29. if (ret) {
  30. dev_err(i2c_dev->dev,
  31. "failed to compute I2C timings.\n");
  32. if (setup->speed_freq <= I2C_MAX_STANDARD_MODE_FREQ)
  33. break;
  34. setup->speed_freq =
  35. stm32f7_get_lower_rate(setup->speed_freq); /**< 计算失败时按档位降速重试,避免直接失败。 */
  36. dev_warn(i2c_dev->dev,
  37. "downgrade I2C Speed Freq to (%i)\n",
  38. setup->speed_freq);
  39. }
  40. } while (ret);
  41. if (ret) {
  42. dev_err(i2c_dev->dev, "Impossible to compute I2C timings.\n");
  43. return ret;
  44. }
  45. i2c_dev->analog_filter = of_property_read_bool(i2c_dev->dev->of_node,
  46. "i2c-analog-filter");
  47. dev_dbg(i2c_dev->dev, "I2C Speed(%i), Clk Source(%i)\n",
  48. setup->speed_freq, setup->clock_src);
  49. dev_dbg(i2c_dev->dev, "I2C Rise(%i) and Fall(%i) Time\n",
  50. setup->rise_time, setup->fall_time);
  51. dev_dbg(i2c_dev->dev, "I2C Analog Filter(%s), DNF(%i)\n",
  52. str_on_off(i2c_dev->analog_filter), i2c_dev->dnf);
  53. i2c_dev->bus_rate = setup->speed_freq;
  54. return 0;
  55. }
复制代码

stm32f7_i2c_hw_config / stm32f7_i2c_xfer_core / stm32f7_i2c_xfer_msg:将计算得到的时序写入外设并发起一次主机传输

  • 这三段代码的关键点不在“代码如何顺序执行”,而在于:TIMINGR 字段组合的寄存器语义CR1/CR2 的事务编排语义、以及 atomic 传输时“禁止中断并改用轮询驱动状态机”的策略。这些约束在单核下同样成立。

stm32f7_i2c_hw_config:把 timing/滤波配置固化到寄存器并使能外设

作用与原理

  • 将 compute_timing() 计算出的 presc/scldel/sdadel/sclh/scll 映射为 TIMINGR 的位域组合值并写入。
  • 按 analog_filter 决定是否关闭模拟滤波(通过 ANFOFF 位的“反向语义”实现)。
  • 先清空 DNF 位域再设置 DNF(n),避免残留位影响。
  • 最后置位 PE 使能外设;这一步对寄存器写入的生效顺序具有控制意义。
  1. /**
  2. * @brief 将已计算的 I2C 时序/滤波参数写入硬件寄存器并使能外设。
  3. *
  4. * @param i2c_dev 控制器私有数据,包含 timing/dnf/analog_filter/base 等。
  5. */
  6. static void stm32f7_i2c_hw_config(struct stm32f7_i2c_dev *i2c_dev)
  7. {
  8. struct stm32f7_i2c_timings *t = &i2c_dev->timing; /**< timing:由算法求得的离散字段解,直接对应 TIMINGR 各位域。 */
  9. u32 timing = 0; /**< timing:TIMINGR 最终要写入的位域组合值。 */
  10. /** TIMINGR 的字段组合必须一次性形成最终值写入,避免逐字段 read-modify-write 带来的中间态。 */
  11. timing |= STM32F7_I2C_TIMINGR_PRESC(t->presc); /**< PRESC:决定内部时间量化步进。 */
  12. timing |= STM32F7_I2C_TIMINGR_SCLDEL(t->scldel); /**< SCLDEL:数据建立相关延迟字段。 */
  13. timing |= STM32F7_I2C_TIMINGR_SDADEL(t->sdadel); /**< SDADEL:数据保持相关延迟字段。 */
  14. timing |= STM32F7_I2C_TIMINGR_SCLH(t->sclh); /**< SCLH:SCL 高电平计数。 */
  15. timing |= STM32F7_I2C_TIMINGR_SCLL(t->scll); /**< SCLL:SCL 低电平计数。 */
  16. writel_relaxed(timing, i2c_dev->base + STM32F7_I2C_TIMINGR);
  17. /** ANFOFF 语义为“置位=关闭模拟滤波”,因此 enable 模拟滤波时需要清位。 */
  18. if (i2c_dev->analog_filter)
  19. stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1,
  20. STM32F7_I2C_CR1_ANFOFF);
  21. else
  22. stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1,
  23. STM32F7_I2C_CR1_ANFOFF);
  24. /** DNF 是位域,必须先清 mask 再写入新值,避免旧值残留。 */
  25. stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1,
  26. STM32F7_I2C_CR1_DNF_MASK);
  27. stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1,
  28. STM32F7_I2C_CR1_DNF(i2c_dev->dnf));
  29. /** PE:外设使能位。通常在关键配置完成后再置位,避免配置窗口内进入工作态。 */
  30. stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1,
  31. STM32F7_I2C_CR1_PE);
  32. }
复制代码

stm32f7_i2c_xfer_core:I2C 主机传输的公共核心(支持普通与 atomic 两种驱动方式)

作用与原理

  • 负责一次 master_xfer 的高层编排:运行时电源管理、等待总线空闲、发起首个消息、等待完成、错误清理与超时处理。
  • atomic 为真时不依赖中断完成量 complete,而是通过轮询函数驱动状态机(避免不可睡眠上下文等待/调度)。
  1. /**
  2. * @brief I2C 主机传输公共核心:组织消息队列并等待完成。
  3. *
  4. * @param i2c_adap 适配器对象。
  5. * @param msgs 消息数组。
  6. * @param num 消息数量。
  7. *
  8. * @return 成功返回 num;失败返回负 errno。
  9. */
  10. static int stm32f7_i2c_xfer_core(struct i2c_adapter *i2c_adap,
  11. struct i2c_msg msgs[], int num)
  12. {
  13. struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap);
  14. struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; /**< f7_msg:驱动内部状态机使用的“当前传输上下文”。 */
  15. struct stm32_i2c_dma *dma = i2c_dev->dma;
  16. unsigned long time_left;
  17. int ret;
  18. i2c_dev->msg = msgs;
  19. i2c_dev->msg_num = num;
  20. i2c_dev->msg_id = 0;
  21. f7_msg->smbus = false;
  22. ret = pm_runtime_resume_and_get(i2c_dev->dev); /**< 传输前确保时钟/电源就绪,避免访问寄存器无效。 */
  23. if (ret < 0)
  24. return ret;
  25. ret = stm32f7_i2c_wait_free_bus(i2c_dev); /**< 关键前置条件:BUSY 需为 0,否则本次传输不应启动。 */
  26. if (ret)
  27. goto pm_free;
  28. stm32f7_i2c_xfer_msg(i2c_dev, msgs); /**< 启动第 0 个消息:写 CR1/CR2 并置 START。 */
  29. /** 非 atomic:依赖中断/线程化中断在结束时 complete;atomic:禁止中断后改为轮询驱动状态机。 */
  30. if (!i2c_dev->atomic)
  31. time_left = wait_for_completion_timeout(&i2c_dev->complete,
  32. i2c_dev->adap.timeout);
  33. else
  34. time_left = stm32f7_i2c_wait_polling(i2c_dev);
  35. ret = f7_msg->result;
  36. if (ret) {
  37. if (i2c_dev->use_dma)
  38. dmaengine_synchronize(dma->chan_using); /**< 错误路径:确保 DMA 停止并完成清理语义一致。 */
  39. /**
  40. * 错误场景下 TXDR 可能残留“已写入但未实际发送”的字节。
  41. * 通过置 TXE/flush 手段避免下一次事务误发送旧数据。
  42. */
  43. writel_relaxed(STM32F7_I2C_ISR_TXE,
  44. i2c_dev->base + STM32F7_I2C_ISR);
  45. goto pm_free;
  46. }
  47. if (!time_left) {
  48. dev_dbg(i2c_dev->dev, "Access to slave 0x%x timed out\n",
  49. i2c_dev->msg->addr);
  50. if (i2c_dev->use_dma)
  51. dmaengine_terminate_sync(dma->chan_using); /**< 超时:同步终止 DMA,防止后台继续写寄存器/内存。 */
  52. stm32f7_i2c_wait_free_bus(i2c_dev); /**< 超时后尝试恢复/释放总线,避免 BUSY 长期卡死。 */
  53. ret = -ETIMEDOUT;
  54. }
  55. pm_free:
  56. pm_runtime_put_autosuspend(i2c_dev->dev); /**< 传输结束后允许自动挂起,降低空闲功耗。 */
  57. return (ret < 0) ? ret : num;
  58. }
复制代码

stm32f7_i2c_xfer_msg:为单条 i2c_msg 配置 CR1/CR2,并选择 DMA 或 IRQ 驱动数据通道

作用与原理

  • 把 i2c_msg 映射为 I2C 外设事务参数:

    • RD_WRN:读/写方向
    • SADD7/SADD10 + ADD10:7 位或 10 位地址格式
    • NBYTES + RELOAD:分段传输机制(单段最多 0xFF 字节)
    • START:启动/重复起始
  • 依据 atomic 决定是否允许中断:atomic 模式下 主动清掉所有中断使能位,后续由轮询驱动事件推进。

  • 依据 DMA 条件决定数据通道:

    • 仅在 i2c_dev->dma 存在且 !atomic 时尝试 DMA
    • 通过 i2c_get_dma_safe_msg_buf() 获取满足对齐/缓存一致性要求的安全缓冲区;失败则回退中断方式
    • 成功则置 RXDMAEN/TXDMAEN,否则置 RXIE/TXIE
  1. /**
  2. * @brief 配置并启动单条 I2C 消息传输(写 CR1/CR2 并置 START)。
  3. *
  4. * @param i2c_dev 控制器私有数据。
  5. * @param msg Linux I2C 层消息描述。
  6. */
  7. static void stm32f7_i2c_xfer_msg(struct stm32f7_i2c_dev *i2c_dev,
  8. struct i2c_msg *msg)
  9. {
  10. struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
  11. void __iomem *base = i2c_dev->base;
  12. u8 *dma_buf;
  13. u32 cr1, cr2;
  14. int ret;
  15. f7_msg->addr = msg->addr;
  16. f7_msg->buf = msg->buf;
  17. f7_msg->count = msg->len;
  18. f7_msg->result = 0;
  19. f7_msg->stop = (i2c_dev->msg_id >= i2c_dev->msg_num - 1); /**< 仅最后一条消息生成 STOP,其余通过 TC 触发下一条/重复起始。 */
  20. reinit_completion(&i2c_dev->complete); /**< 为本条消息重新初始化完成量,由 ISR 在结束/错误时 complete。 */
  21. cr1 = readl_relaxed(base + STM32F7_I2C_CR1);
  22. cr2 = readl_relaxed(base + STM32F7_I2C_CR2);
  23. cr2 &= ~STM32F7_I2C_CR2_RD_WRN;
  24. if (msg->flags & I2C_M_RD)
  25. cr2 |= STM32F7_I2C_CR2_RD_WRN; /**< RD_WRN=1 表示读事务,硬件将从 RXDR 出数。 */
  26. cr2 &= ~(STM32F7_I2C_CR2_HEAD10R | STM32F7_I2C_CR2_ADD10);
  27. if (msg->flags & I2C_M_TEN) {
  28. cr2 &= ~STM32F7_I2C_CR2_SADD10_MASK;
  29. cr2 |= STM32F7_I2C_CR2_SADD10(f7_msg->addr);
  30. cr2 |= STM32F7_I2C_CR2_ADD10; /**< 10 位地址模式:由 ADD10 指示并写入 SADD10。 */
  31. } else {
  32. cr2 &= ~STM32F7_I2C_CR2_SADD7_MASK;
  33. cr2 |= STM32F7_I2C_CR2_SADD7(f7_msg->addr); /**< 7 位地址需左移到位域位置(bit[7:1]),bit0 为方向位由硬件管理。 */
  34. }
  35. cr2 &= ~(STM32F7_I2C_CR2_NBYTES_MASK | STM32F7_I2C_CR2_RELOAD);
  36. if (f7_msg->count > STM32F7_I2C_MAX_LEN) {
  37. cr2 |= STM32F7_I2C_CR2_NBYTES(STM32F7_I2C_MAX_LEN);
  38. cr2 |= STM32F7_I2C_CR2_RELOAD; /**< RELOAD:硬件发送完 NBYTES 后置 TCR,驱动需重装下一段 NBYTES。 */
  39. } else {
  40. cr2 |= STM32F7_I2C_CR2_NBYTES(f7_msg->count);
  41. }
  42. cr1 |= STM32F7_I2C_CR1_ERRIE | STM32F7_I2C_CR1_TCIE |
  43. STM32F7_I2C_CR1_STOPIE | STM32F7_I2C_CR1_NACKIE; /**< 事件推进依赖 TC/TCR/STOP/NACK;错误依赖 ERR。 */
  44. cr1 &= ~(STM32F7_I2C_CR1_RXIE | STM32F7_I2C_CR1_TXIE |
  45. STM32F7_I2C_CR1_RXDMAEN | STM32F7_I2C_CR1_TXDMAEN); /**< 数据通道只能二选一:IRQ 或 DMA;先清再按策略设置。 */
  46. i2c_dev->use_dma = false;
  47. if (i2c_dev->dma && !i2c_dev->atomic) {
  48. /**
  49. * DMA 需要满足对齐/缓存一致性等约束,安全缓冲区由 I2C core 提供。
  50. * STM32F7_I2C_DMA_LEN_MIN 是“值得启用 DMA 的最小长度阈值”,避免短包 DMA 开销反而更大。
  51. */
  52. dma_buf = i2c_get_dma_safe_msg_buf(msg, STM32F7_I2C_DMA_LEN_MIN);
  53. if (dma_buf) {
  54. f7_msg->buf = dma_buf;
  55. ret = stm32_i2c_prep_dma_xfer(i2c_dev->dev, i2c_dev->dma,
  56. msg->flags & I2C_M_RD,
  57. f7_msg->count, f7_msg->buf,
  58. stm32f7_i2c_dma_callback,
  59. i2c_dev);
  60. if (ret) {
  61. dev_warn(i2c_dev->dev, "can't use DMA\n");
  62. i2c_put_dma_safe_msg_buf(f7_msg->buf, msg, false);
  63. f7_msg->buf = msg->buf; /**< DMA 准备失败:回退到原始缓冲区并采用中断传输。 */
  64. } else {
  65. i2c_dev->use_dma = true;
  66. }
  67. }
  68. }
  69. if (!i2c_dev->use_dma) {
  70. if (msg->flags & I2C_M_RD)
  71. cr1 |= STM32F7_I2C_CR1_RXIE; /**< 中断收数:RXNE 触发读取 RXDR。 */
  72. else
  73. cr1 |= STM32F7_I2C_CR1_TXIE; /**< 中断发数:TXIS 触发写入 TXDR。 */
  74. } else {
  75. if (msg->flags & I2C_M_RD)
  76. cr1 |= STM32F7_I2C_CR1_RXDMAEN; /**< DMA 收数:由 DMA 搬运 RXDR。 */
  77. else
  78. cr1 |= STM32F7_I2C_CR1_TXDMAEN; /**< DMA 发数:由 DMA 搬运 TXDR。 */
  79. }
  80. if (i2c_dev->atomic)
  81. cr1 &= ~STM32F7_I2C_ALL_IRQ_MASK; /**< atomic 模式:不使用中断推进状态机,后续由轮询路径驱动。 */
  82. cr2 |= STM32F7_I2C_CR2_START; /**< START/重复起始:写 CR2 后硬件开始发送地址与启动传输。 */
  83. i2c_dev->master_mode = true; /**< 该标志影响 ISR 分流:主机/从机/错误处理路径不同。 */
  84. writel_relaxed(cr1, base + STM32F7_I2C_CR1);
  85. writel_relaxed(cr2, base + STM32F7_I2C_CR2);
  86. }
复制代码

stm32f7_i2c_isr_event / stm32f7_i2c_isr_event_thread / stm32f7_i2c_handle_isr_errs:中断顶半部快速分流与线程化状态机推进

平台关注:单核、无 MMU、ARMv7-M(STM32H750)

  • 这组函数体现的关键机制是 线程化中断:顶半部只做“快速判定与少量不可阻塞工作”,把需要较长处理、可能睡眠/等待的逻辑放到线程化 handler 中完成。
  • atomic 传输模式下会关闭本外设的中断使能位,状态推进改由轮询路径调用 stm32f7_i2c_isr_event() 完成,因此 ISR 顶半部的逻辑需要保持“可被轮询复用”的性质。

stm32f7_i2c_write_tx_data:向 TXDR 写入 1 字节并递减剩余计数

  1. /**
  2. * @brief 发送路径写入 1 字节到 TXDR。
  3. *
  4. * @param i2c_dev 控制器私有数据,内部使用 f7_msg 的 buf/count。
  5. */
  6. static void stm32f7_i2c_write_tx_data(struct stm32f7_i2c_dev *i2c_dev)
  7. {
  8. struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; /**< 驱动内部消息上下文:buf 指向待发送数据,count 为剩余字节数。 */
  9. void __iomem *base = i2c_dev->base;
  10. if (f7_msg->count) { /**< 仅当还有待发送数据时才写 TXDR,避免写入无效字节。 */
  11. writeb_relaxed(*f7_msg->buf++, base + STM32F7_I2C_TXDR); /**< 写 8bit 数据寄存器;buf 自增以指向下一字节。 */
  12. f7_msg->count--; /**< 发送 1 字节后,剩余字节数递减。 */
  13. }
  14. }
复制代码

stm32f7_i2c_read_rx_data:从 RXDR 读取 1 字节或执行接收 FIFO 刷新

  1. /**
  2. * @brief 接收路径读取 1 字节到缓冲区;若不期望数据仍读取一次以清空 RXDR。
  3. *
  4. * @param i2c_dev 控制器私有数据。
  5. */
  6. static void stm32f7_i2c_read_rx_data(struct stm32f7_i2c_dev *i2c_dev)
  7. {
  8. struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; /**< buf 指向存放接收数据的位置;count 为剩余待接收字节数。 */
  9. void __iomem *base = i2c_dev->base;
  10. if (f7_msg->count) {
  11. *f7_msg->buf++ = readb_relaxed(base + STM32F7_I2C_RXDR); /**< 读出 1 字节并写入 buf,随后 buf 自增。 */
  12. f7_msg->count--; /**< 接收 1 字节后剩余计数递减。 */
  13. } else {
  14. readb_relaxed(base + STM32F7_I2C_RXDR); /**< 读 RXDR 以清除硬件“RXNE”状态,避免遗留数据影响后续事务。 */
  15. }
  16. }
复制代码

stm32f7_i2c_reload:处理 RELOAD 分段传输时重装 NBYTES

  1. /**
  2. * @brief RELOAD 分段传输重装 NBYTES。
  3. *
  4. * @param i2c_dev 控制器私有数据。
  5. */
  6. static void stm32f7_i2c_reload(struct stm32f7_i2c_dev *i2c_dev)
  7. {
  8. struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
  9. u32 cr2;
  10. if (i2c_dev->use_dma)
  11. f7_msg->count -= STM32F7_I2C_MAX_LEN; /**< DMA 分段场景:每次装载固定段长后,需要扣除已覆盖的段长度。 */
  12. cr2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_CR2);
  13. cr2 &= ~STM32F7_I2C_CR2_NBYTES_MASK;
  14. if (f7_msg->count > STM32F7_I2C_MAX_LEN) {
  15. cr2 |= STM32F7_I2C_CR2_NBYTES(STM32F7_I2C_MAX_LEN); /**< 仍超过单段上限:继续维持 RELOAD 并装载下一段最大值。 */
  16. } else {
  17. cr2 &= ~STM32F7_I2C_CR2_RELOAD; /**< 最后一段:清 RELOAD,使硬件在段结束后进入 TC 而非 TCR。 */
  18. cr2 |= STM32F7_I2C_CR2_NBYTES(f7_msg->count); /**< 装载最后一段的实际剩余字节数。 */
  19. }
  20. writel_relaxed(cr2, i2c_dev->base + STM32F7_I2C_CR2);
  21. }
复制代码

stm32f7_i2c_smbus_reload:SMBus 块读类协议按“首字节长度”重装 NBYTES

  1. /**
  2. * @brief SMBus 块读/块过程调用的 RELOAD:先接收长度字节,再按长度更新 NBYTES。
  3. *
  4. * @param i2c_dev 控制器私有数据。
  5. */
  6. static void stm32f7_i2c_smbus_reload(struct stm32f7_i2c_dev *i2c_dev)
  7. {
  8. struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
  9. u32 cr2;
  10. u8 *val;
  11. stm32f7_i2c_read_rx_data(i2c_dev); /**< SMBus 块读:首字节为 count,需要先把它接收下来。 */
  12. val = f7_msg->buf - sizeof(u8); /**< buf 已自增,回退 1 字节定位到刚接收的 count 值。 */
  13. f7_msg->count = *val; /**< 将接收长度作为后续需要接收的数据字节数。 */
  14. cr2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_CR2);
  15. cr2 &= ~(STM32F7_I2C_CR2_NBYTES_MASK | STM32F7_I2C_CR2_RELOAD); /**< SMBus 块读在得知长度后,通常不再需要继续 RELOAD。 */
  16. cr2 |= STM32F7_I2C_CR2_NBYTES(f7_msg->count);
  17. writel_relaxed(cr2, i2c_dev->base + STM32F7_I2C_CR2);
  18. }
复制代码

stm32f7_i2c_handle_isr_errs:错误/异常标志处理与收尾

  1. /**
  2. * @brief 处理 I2C 错误类中断标志,设置传输结果并结束本次事务。
  3. *
  4. * @param i2c_dev 控制器私有数据。
  5. * @param status ISR 读出的状态寄存器值。
  6. *
  7. * @return IRQ_HANDLED
  8. */
  9. static irqreturn_t stm32f7_i2c_handle_isr_errs(struct stm32f7_i2c_dev *i2c_dev, u32 status)
  10. {
  11. struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
  12. u16 addr = f7_msg->addr;
  13. void __iomem *base = i2c_dev->base;
  14. struct device *dev = i2c_dev->dev;
  15. if (status & STM32F7_I2C_ISR_BERR) {
  16. dev_err(dev, "Bus error accessing addr 0x%x\n", addr);
  17. writel_relaxed(STM32F7_I2C_ICR_BERRCF, base + STM32F7_I2C_ICR); /**< 写 ICR 清 BERR,防止错误标志粘连。 */
  18. stm32f7_i2c_release_bus(&i2c_dev->adap); /**< 通过禁用/重配恢复控制器,缓解 BUSY 卡死或异常状态。 */
  19. f7_msg->result = -EIO;
  20. }
  21. if (status & STM32F7_I2C_ISR_ARLO) {
  22. dev_dbg(dev, "Arbitration loss accessing addr 0x%x\n", addr);
  23. writel_relaxed(STM32F7_I2C_ICR_ARLOCF, base + STM32F7_I2C_ICR);
  24. f7_msg->result = -EAGAIN; /**< 仲裁丢失通常建议上层重试。 */
  25. }
  26. if (status & STM32F7_I2C_ISR_PECERR) {
  27. dev_err(dev, "PEC error in reception accessing addr 0x%x\n", addr);
  28. writel_relaxed(STM32F7_I2C_ICR_PECCF, base + STM32F7_I2C_ICR);
  29. f7_msg->result = -EINVAL;
  30. }
  31. if (status & STM32F7_I2C_ISR_ALERT) {
  32. dev_dbg(dev, "SMBus alert received\n");
  33. writel_relaxed(STM32F7_I2C_ICR_ALERTCF, base + STM32F7_I2C_ICR);
  34. i2c_handle_smbus_alert(i2c_dev->alert->ara); /**< ARA 设备负责完成 SMBus Alert 的响应流程。 */
  35. return IRQ_HANDLED;
  36. }
  37. if (!i2c_dev->slave_running) {
  38. u32 mask;
  39. if (stm32f7_i2c_is_slave_registered(i2c_dev))
  40. mask = STM32F7_I2C_XFER_IRQ_MASK;
  41. else
  42. mask = STM32F7_I2C_ALL_IRQ_MASK;
  43. stm32f7_i2c_disable_irq(i2c_dev, mask); /**< 错误收尾:关闭本次事务相关中断源,防止重复进入。 */
  44. }
  45. if (i2c_dev->use_dma)
  46. stm32f7_i2c_dma_callback(i2c_dev); /**< 错误收尾:停止 DMA、解除映射并触发 dma_complete。 */
  47. i2c_dev->master_mode = false;
  48. complete(&i2c_dev->complete); /**< 通知等待方本次事务结束(成功或失败)。 */
  49. return IRQ_HANDLED;
  50. }
复制代码

stm32f7_i2c_isr_event:顶半部快速处理与线程唤醒判定

  1. #define STM32F7_ERR_EVENTS (STM32F7_I2C_ISR_BERR | STM32F7_I2C_ISR_ARLO |\
  2. STM32F7_I2C_ISR_PECERR | STM32F7_I2C_ISR_ALERT)
  3. /**
  4. * @brief I2C 事件中断顶半部:做最小化处理并决定是否唤醒线程化 handler。
  5. *
  6. * @param irq 中断号。
  7. * @param data 控制器私有数据指针。
  8. *
  9. * @return IRQ_HANDLED 或 IRQ_WAKE_THREAD
  10. */
  11. static irqreturn_t stm32f7_i2c_isr_event(int irq, void *data)
  12. {
  13. struct stm32f7_i2c_dev *i2c_dev = data;
  14. u32 status;
  15. status = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR);
  16. /**
  17. * 非主机模式:说明当前在从机事务或待从机处理;
  18. * 单 IT 线模式:错误与事件共线,遇到错误标志应交由线程化路径统一处理。
  19. */
  20. if (!i2c_dev->master_mode ||
  21. (i2c_dev->setup.single_it_line && (status & STM32F7_ERR_EVENTS)))
  22. return IRQ_WAKE_THREAD;
  23. if (status & STM32F7_I2C_ISR_TXIS)
  24. stm32f7_i2c_write_tx_data(i2c_dev); /**< TXIS 代表 TXDR 可写,顶半部直接写 1 字节以减少线程切换开销。 */
  25. if (status & STM32F7_I2C_ISR_RXNE)
  26. stm32f7_i2c_read_rx_data(i2c_dev); /**< RXNE 代表 RXDR 有数据,顶半部直接读 1 字节。 */
  27. /**
  28. * 其余需要“状态机推进”的标志由线程化 handler 处理:
  29. * NACKF/STOPF/TC/TCR 涉及收尾、分段重装、发 STOP 或启动下一消息。
  30. */
  31. if (status &
  32. (STM32F7_I2C_ISR_NACKF | STM32F7_I2C_ISR_STOPF |
  33. STM32F7_I2C_ISR_TC | STM32F7_I2C_ISR_TCR))
  34. return IRQ_WAKE_THREAD;
  35. return IRQ_HANDLED;
  36. }
复制代码

stm32f7_i2c_isr_event_thread:线程化事件处理,推进 RELOAD/消息链/STOP/NACK

  1. /**
  2. * @brief I2C 事件线程化 handler:处理需要完整状态机推进的事件与收尾。
  3. *
  4. * @param irq 中断号。
  5. * @param data 控制器私有数据。
  6. *
  7. * @return IRQ_HANDLED
  8. */
  9. static irqreturn_t stm32f7_i2c_isr_event_thread(int irq, void *data)
  10. {
  11. struct stm32f7_i2c_dev *i2c_dev = data;
  12. struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
  13. void __iomem *base = i2c_dev->base;
  14. u32 status, mask;
  15. int ret;
  16. status = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR);
  17. if (!i2c_dev->master_mode)
  18. return stm32f7_i2c_slave_isr_event(i2c_dev, status); /**< 从机模式完整处理在专用路径完成。 */
  19. if (i2c_dev->setup.single_it_line && (status & STM32F7_ERR_EVENTS))
  20. return stm32f7_i2c_handle_isr_errs(i2c_dev, status); /**< 单 IT 线:错误交由统一错误处理。 */
  21. if (status & STM32F7_I2C_ISR_NACKF) {
  22. dev_dbg(i2c_dev->dev, "<%s>: Receive NACK (addr %x)\n",
  23. __func__, f7_msg->addr);
  24. writel_relaxed(STM32F7_I2C_ICR_NACKCF, base + STM32F7_I2C_ICR);
  25. if (i2c_dev->use_dma)
  26. stm32f7_i2c_dma_callback(i2c_dev); /**< NACK 发生时停止 DMA,避免继续搬运无意义数据。 */
  27. f7_msg->result = -ENXIO; /**< NACK 通常代表目标地址/阶段无响应。 */
  28. }
  29. if (status & STM32F7_I2C_ISR_TCR) {
  30. if (f7_msg->smbus)
  31. stm32f7_i2c_smbus_reload(i2c_dev); /**< SMBus 块读:按首字节长度更新 NBYTES。 */
  32. else
  33. stm32f7_i2c_reload(i2c_dev); /**< I2C 普通分段:继续装载下一段 NBYTES。 */
  34. }
  35. if (status & STM32F7_I2C_ISR_TC) {
  36. if (i2c_dev->use_dma && !f7_msg->result) {
  37. ret = wait_for_completion_timeout(&i2c_dev->dma->dma_complete, HZ);
  38. if (!ret) {
  39. dev_dbg(i2c_dev->dev, "<%s>: Timed out\n", __func__);
  40. stm32f7_i2c_dma_callback(i2c_dev); /**< DMA 未在合理时间内完成,强制终止并标记超时。 */
  41. f7_msg->result = -ETIMEDOUT;
  42. }
  43. }
  44. if (f7_msg->stop) {
  45. mask = STM32F7_I2C_CR2_STOP;
  46. stm32f7_i2c_set_bits(base + STM32F7_I2C_CR2, mask); /**< 最后一条消息:置 STOP 收尾。 */
  47. } else if (f7_msg->smbus) {
  48. stm32f7_i2c_smbus_rep_start(i2c_dev); /**< SMBus 需要重复起始的协议:进入 rep-start 专用配置。 */
  49. } else {
  50. i2c_dev->msg_id++;
  51. i2c_dev->msg++;
  52. stm32f7_i2c_xfer_msg(i2c_dev, i2c_dev->msg); /**< 多消息链:在 TC 点启动下一条消息(重复起始语义由硬件 START 位实现)。 */
  53. }
  54. }
  55. if (status & STM32F7_I2C_ISR_STOPF) {
  56. if (stm32f7_i2c_is_slave_registered(i2c_dev))
  57. mask = STM32F7_I2C_XFER_IRQ_MASK;
  58. else
  59. mask = STM32F7_I2C_ALL_IRQ_MASK;
  60. stm32f7_i2c_disable_irq(i2c_dev, mask); /**< STOP 收到后关闭中断源,防止重复触发。 */
  61. writel_relaxed(STM32F7_I2C_ICR_STOPCF, base + STM32F7_I2C_ICR); /**< 清 STOPF。 */
  62. i2c_dev->master_mode = false;
  63. complete(&i2c_dev->complete); /**< 事务结束:唤醒等待方。 */
  64. }
  65. return IRQ_HANDLED;
  66. }
复制代码

stm32f7_i2c_isr_error_thread:独立 error IRQ 线程化处理入口

  1. /**
  2. * @brief 独立 error IRQ 的线程化 handler:读取 ISR 后交由统一错误处理。
  3. */
  4. static irqreturn_t stm32f7_i2c_isr_error_thread(int irq, void *data)
  5. {
  6. struct stm32f7_i2c_dev *i2c_dev = data;
  7. u32 status;
  8. status = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR);
  9. return stm32f7_i2c_handle_isr_errs(i2c_dev, status);
  10. }
复制代码

stm32f7_i2c_wait_polling / stm32f7_i2c_dma_callback / stm32f7_i2c_wait_free_bus:atomic 轮询推进与 DMA 完成收尾、总线恢复

stm32f7_i2c_disable_dma_req:关闭外设侧 DMA 请求位

  1. /**
  2. * @brief 关闭 I2C 外设的 RX/TX DMA 请求。
  3. *
  4. * @param i2c_dev 控制器私有数据。
  5. */
  6. static void stm32f7_i2c_disable_dma_req(struct stm32f7_i2c_dev *i2c_dev)
  7. {
  8. void __iomem *base = i2c_dev->base;
  9. u32 mask = STM32F7_I2C_CR1_RXDMAEN | STM32F7_I2C_CR1_TXDMAEN; /**< DMA 请求使能位,需同时清除以避免残留请求。 */
  10. stm32f7_i2c_clr_bits(base + STM32F7_I2C_CR1, mask);
  11. }
复制代码

stm32f7_i2c_dma_callback:DMA 结束/中止时的统一收尾(成功与错误路径复用)

  1. /**
  2. * @brief DMA 传输结束或被迫终止时的收尾回调。
  3. *
  4. * @param arg 回调参数,实际为 struct stm32f7_i2c_dev*。
  5. *
  6. * 该回调负责:
  7. * 1) 关闭外设侧 DMA 请求,防止继续触发 DMA;
  8. * 2) 终止 DMA 通道(异步),避免后台继续搬运;
  9. * 3) 解除 dma_map_single 的映射;
  10. * 4) 若使用了 DMA-safe 缓冲区,将其内容回收/拷回;
  11. * 5) 通过 dma_complete 通知等待方(线程化 ISR 的 TC 分支会等待它)。
  12. */
  13. static void stm32f7_i2c_dma_callback(void *arg)
  14. {
  15. struct stm32f7_i2c_dev *i2c_dev = arg;
  16. struct stm32_i2c_dma *dma = i2c_dev->dma;
  17. struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
  18. stm32f7_i2c_disable_dma_req(i2c_dev); /**< 必须先停外设请求,否则即便 DMA 通道停了仍可能反复产生请求/标志。 */
  19. dmaengine_terminate_async(dma->chan_using); /**< 终止 DMA 通道:用于错误/超时场景快速止血。 */
  20. dma_unmap_single(i2c_dev->dev, dma->dma_buf, dma->dma_len,
  21. dma->dma_data_dir); /**< 解除映射,保证缓存一致性语义闭合。 */
  22. if (!f7_msg->smbus)
  23. i2c_put_dma_safe_msg_buf(f7_msg->buf, i2c_dev->msg, true); /**< 归还 DMA-safe 缓冲区;必要时把数据同步回原 msg->buf。 */
  24. complete(&dma->dma_complete); /**< 通知“DMA 已结束”,线程化 ISR 在 TC 分支会等待该完成量。 */
  25. }
复制代码

stm32f7_i2c_wait_polling:atomic 模式下用轮询驱动 ISR 顶半部推进状态机

  1. /**
  2. * @brief atomic 模式轮询等待:周期性调用事件处理逻辑推进传输。
  3. *
  4. * @param i2c_dev 控制器私有数据。
  5. *
  6. * @return 若在超时前完成,返回 1;否则返回 0。
  7. */
  8. static int stm32f7_i2c_wait_polling(struct stm32f7_i2c_dev *i2c_dev)
  9. {
  10. ktime_t timeout = ktime_add_ms(ktime_get(), i2c_dev->adap.timeout); /**< 使用适配器 timeout 构造绝对超时时刻。 */
  11. while (ktime_compare(ktime_get(), timeout) < 0) {
  12. udelay(5); /**< 轮询间隔:避免纯忙等占满总线/CPU,但仍保持足够响应性。 */
  13. stm32f7_i2c_isr_event(0, i2c_dev); /**< 复用 ISR 顶半部逻辑:读取 ISR 并处理 TXIS/RXNE 等事件。 */
  14. if (completion_done(&i2c_dev->complete)) /**< 线程化 handler 不参与时,由轮询过程中触发的逻辑最终 complete。 */
  15. return 1;
  16. }
  17. return 0;
  18. }
复制代码

stm32f7_i2c_release_bus:通过禁用再重新配置外设以恢复可能卡死的总线状态

  1. /**
  2. * @brief 释放/恢复 I2C 总线:关闭外设后重新写入配置并重新使能。
  3. *
  4. * @param i2c_adap I2C 适配器。
  5. */
  6. static void stm32f7_i2c_release_bus(struct i2c_adapter *i2c_adap)
  7. {
  8. struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap);
  9. stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1,
  10. STM32F7_I2C_CR1_PE); /**< 关闭外设,等价于把内部状态机复位到禁用态。 */
  11. stm32f7_i2c_hw_config(i2c_dev); /**< 重新写 TIMINGR/滤波/DNF 并置 PE,尽量把控制器恢复到已知工作态。 */
  12. }
复制代码

stm32f7_i2c_wait_free_bus:等待 BUSY 清零,超时则尝试 release_bus 后返回忙

  1. /**
  2. * @brief 等待 I2C 总线空闲(BUSY=0)。
  3. *
  4. * @param i2c_dev 控制器私有数据。
  5. *
  6. * @return BUSY 在超时前清零返回 0;否则尝试恢复控制器并返回 -EBUSY。
  7. */
  8. static int stm32f7_i2c_wait_free_bus(struct stm32f7_i2c_dev *i2c_dev)
  9. {
  10. u32 status;
  11. int ret;
  12. ret = readl_relaxed_poll_timeout(i2c_dev->base + STM32F7_I2C_ISR,
  13. status,
  14. !(status & STM32F7_I2C_ISR_BUSY),
  15. 10, 1000); /**< 轮询 ISR.BUSY,10us 间隔,最大 1000us。 */
  16. if (!ret)
  17. return 0;
  18. stm32f7_i2c_release_bus(&i2c_dev->adap); /**< BUSY 卡住:尝试通过禁用/重配恢复控制器。 */
  19. return -EBUSY;
  20. }
复制代码

stm32f7_i2c_smbus_xfer_msg / stm32f7_i2c_smbus_rep_start / stm32f7_i2c_smbus_check_pec — SMBus 事务编排(首阶段/重复起始/PEC 校验)

stm32f7_i2c_smbus_xfer_msg:SMBus 首阶段(command + 可选数据)配置并启动 START

  1. /**
  2. * @brief 配置并启动 SMBus 事务的首阶段(通常是写 command,必要时附带写数据)。
  3. *
  4. * @param i2c_dev 控制器私有数据。
  5. * @param flags 客户端标志(例如 I2C_CLIENT_PEC)。
  6. * @param command SMBus command 字节。
  7. * @param data SMBus 数据联合体(按 size/read_write 决定是否使用)。
  8. *
  9. * @return 0 成功;负 errno 表示不支持或参数非法。
  10. */
  11. static int stm32f7_i2c_smbus_xfer_msg(struct stm32f7_i2c_dev *i2c_dev,
  12. unsigned short flags, u8 command,
  13. union i2c_smbus_data *data)
  14. {
  15. struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
  16. struct device *dev = i2c_dev->dev;
  17. void __iomem *base = i2c_dev->base;
  18. u32 cr1, cr2;
  19. int i, ret;
  20. f7_msg->result = 0;
  21. reinit_completion(&i2c_dev->complete);
  22. cr2 = readl_relaxed(base + STM32F7_I2C_CR2);
  23. cr1 = readl_relaxed(base + STM32F7_I2C_CR1);
  24. cr2 &= ~STM32F7_I2C_CR2_RD_WRN;
  25. if (f7_msg->read_write)
  26. cr2 |= STM32F7_I2C_CR2_RD_WRN; /* 难点:有些“读协议”的首阶段仍需要先写 command,本函数会在 switch 中改回写方向 */
  27. cr2 &= ~(STM32F7_I2C_CR2_ADD10 | STM32F7_I2C_CR2_SADD7_MASK);
  28. cr2 |= STM32F7_I2C_CR2_SADD7(f7_msg->addr);
  29. f7_msg->smbus_buf[0] = command; /* 难点:SMBus 将 command 作为第一个发送字节,后续阶段可能 repeated-start 切读 */
  30. switch (f7_msg->size) {
  31. case I2C_SMBUS_QUICK:
  32. f7_msg->stop = true;
  33. f7_msg->count = 0;
  34. break;
  35. case I2C_SMBUS_BYTE:
  36. f7_msg->stop = true;
  37. f7_msg->count = 1;
  38. break;
  39. case I2C_SMBUS_BYTE_DATA:
  40. if (f7_msg->read_write) {
  41. f7_msg->stop = false; /* 难点:读 BYTE_DATA 需分两段:先写 command,不发 STOP,后续 repeated-start 再读数据 */
  42. f7_msg->count = 1;
  43. cr2 &= ~STM32F7_I2C_CR2_RD_WRN; /* 首阶段强制为写方向(发送 command) */
  44. } else {
  45. f7_msg->stop = true;
  46. f7_msg->count = 2;
  47. f7_msg->smbus_buf[1] = data->byte;
  48. }
  49. break;
  50. case I2C_SMBUS_WORD_DATA:
  51. if (f7_msg->read_write) {
  52. f7_msg->stop = false;
  53. f7_msg->count = 1;
  54. cr2 &= ~STM32F7_I2C_CR2_RD_WRN;
  55. } else {
  56. f7_msg->stop = true;
  57. f7_msg->count = 3;
  58. f7_msg->smbus_buf[1] = data->word & 0xff;
  59. f7_msg->smbus_buf[2] = data->word >> 8;
  60. }
  61. break;
  62. case I2C_SMBUS_BLOCK_DATA:
  63. if (f7_msg->read_write) {
  64. f7_msg->stop = false;
  65. f7_msg->count = 1;
  66. cr2 &= ~STM32F7_I2C_CR2_RD_WRN; /* 首阶段仍是写 command,长度字节将在后续读阶段由从机给出 */
  67. } else {
  68. f7_msg->stop = true;
  69. if (data->block[0] > I2C_SMBUS_BLOCK_MAX || !data->block[0]) {
  70. dev_err(dev, "Invalid block write size %d\n", data->block[0]);
  71. return -EINVAL;
  72. }
  73. f7_msg->count = data->block[0] + 2; /* 难点:block write = command + count + payload */
  74. for (i = 1; i < f7_msg->count; i++)
  75. f7_msg->smbus_buf[i] = data->block[i - 1];
  76. }
  77. break;
  78. case I2C_SMBUS_PROC_CALL:
  79. f7_msg->stop = false; /* 难点:过程调用必须 repeated-start 读回结果 */
  80. f7_msg->count = 3; /* command + 2 字节数据 */
  81. f7_msg->smbus_buf[1] = data->word & 0xff;
  82. f7_msg->smbus_buf[2] = data->word >> 8;
  83. cr2 &= ~STM32F7_I2C_CR2_RD_WRN; /* 首阶段写 */
  84. f7_msg->read_write = I2C_SMBUS_READ; /* 为后续阶段标记读方向 */
  85. break;
  86. case I2C_SMBUS_BLOCK_PROC_CALL:
  87. f7_msg->stop = false;
  88. if (data->block[0] > I2C_SMBUS_BLOCK_MAX - 1) {
  89. dev_err(dev, "Invalid block write size %d\n", data->block[0]);
  90. return -EINVAL;
  91. }
  92. f7_msg->count = data->block[0] + 2;
  93. for (i = 1; i < f7_msg->count; i++)
  94. f7_msg->smbus_buf[i] = data->block[i - 1];
  95. cr2 &= ~STM32F7_I2C_CR2_RD_WRN;
  96. f7_msg->read_write = I2C_SMBUS_READ;
  97. break;
  98. case I2C_SMBUS_I2C_BLOCK_DATA:
  99. return -EOPNOTSUPP;
  100. default:
  101. dev_err(dev, "Unsupported smbus protocol %d\n", f7_msg->size);
  102. return -EOPNOTSUPP;
  103. }
  104. f7_msg->buf = f7_msg->smbus_buf;
  105. if ((flags & I2C_CLIENT_PEC) && f7_msg->size != I2C_SMBUS_QUICK) {
  106. cr1 |= STM32F7_I2C_CR1_PECEN; /* 难点:PEC 由硬件计算,最终值在 PECR 提供 */
  107. if (!f7_msg->read_write) {
  108. cr2 |= STM32F7_I2C_CR2_PECBYTE; /* 难点:写方向时可要求硬件自动追加 PEC 字节 */
  109. f7_msg->count++; /* NBYTES 必须包含 PEC 字节,否则硬件阶段不匹配 */
  110. }
  111. } else {
  112. cr1 &= ~STM32F7_I2C_CR1_PECEN;
  113. cr2 &= ~STM32F7_I2C_CR2_PECBYTE;
  114. }
  115. cr2 &= ~(STM32F7_I2C_CR2_NBYTES_MASK | STM32F7_I2C_CR2_RELOAD);
  116. cr2 |= STM32F7_I2C_CR2_NBYTES(f7_msg->count);
  117. cr1 |= STM32F7_I2C_CR1_ERRIE | STM32F7_I2C_CR1_TCIE |
  118. STM32F7_I2C_CR1_STOPIE | STM32F7_I2C_CR1_NACKIE;
  119. cr1 &= ~(STM32F7_I2C_CR1_RXIE | STM32F7_I2C_CR1_TXIE |
  120. STM32F7_I2C_CR1_RXDMAEN | STM32F7_I2C_CR1_TXDMAEN);
  121. i2c_dev->use_dma = false;
  122. if (i2c_dev->dma && f7_msg->count >= STM32F7_I2C_DMA_LEN_MIN) {
  123. ret = stm32_i2c_prep_dma_xfer(i2c_dev->dev, i2c_dev->dma,
  124. cr2 & STM32F7_I2C_CR2_RD_WRN,
  125. f7_msg->count, f7_msg->buf,
  126. stm32f7_i2c_dma_callback,
  127. i2c_dev);
  128. if (!ret)
  129. i2c_dev->use_dma = true;
  130. else
  131. dev_warn(i2c_dev->dev, "can't use DMA\n");
  132. }
  133. if (!i2c_dev->use_dma) {
  134. if (cr2 & STM32F7_I2C_CR2_RD_WRN)
  135. cr1 |= STM32F7_I2C_CR1_RXIE;
  136. else
  137. cr1 |= STM32F7_I2C_CR1_TXIE;
  138. } else {
  139. if (cr2 & STM32F7_I2C_CR2_RD_WRN)
  140. cr1 |= STM32F7_I2C_CR1_RXDMAEN;
  141. else
  142. cr1 |= STM32F7_I2C_CR1_TXDMAEN;
  143. }
  144. cr2 |= STM32F7_I2C_CR2_START;
  145. i2c_dev->master_mode = true;
  146. writel_relaxed(cr1, base + STM32F7_I2C_CR1);
  147. writel_relaxed(cr2, base + STM32F7_I2C_CR2);
  148. return 0;
  149. }
复制代码

stm32f7_i2c_smbus_rep_start:SMBus 第二阶段(Repeated-START 切换到读)重新配置

  1. /**
  2. * @brief SMBus 需要重复起始的协议:配置第二阶段为读方向并发起 Repeated-START。
  3. *
  4. * @param i2c_dev 控制器私有数据。
  5. */
  6. static void stm32f7_i2c_smbus_rep_start(struct stm32f7_i2c_dev *i2c_dev)
  7. {
  8. struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
  9. void __iomem *base = i2c_dev->base;
  10. u32 cr1, cr2;
  11. int ret;
  12. cr2 = readl_relaxed(base + STM32F7_I2C_CR2);
  13. cr1 = readl_relaxed(base + STM32F7_I2C_CR1);
  14. cr2 |= STM32F7_I2C_CR2_RD_WRN; /* 难点:第二阶段强制切读,形成 repeated-start 后的读事务 */
  15. switch (f7_msg->size) {
  16. case I2C_SMBUS_BYTE_DATA:
  17. f7_msg->count = 1;
  18. break;
  19. case I2C_SMBUS_WORD_DATA:
  20. case I2C_SMBUS_PROC_CALL:
  21. f7_msg->count = 2;
  22. break;
  23. case I2C_SMBUS_BLOCK_DATA:
  24. case I2C_SMBUS_BLOCK_PROC_CALL:
  25. f7_msg->count = 1; /* 难点:块读先读长度字节,随后在 TCR 时由 smbus_reload 更新 NBYTES */
  26. cr2 |= STM32F7_I2C_CR2_RELOAD; /* 让硬件在读完 1 字节后进入 TCR,驱动再装载后续长度 */
  27. break;
  28. }
  29. f7_msg->buf = f7_msg->smbus_buf;
  30. f7_msg->stop = true;
  31. if (cr1 & STM32F7_I2C_CR1_PECEN) {
  32. cr2 |= STM32F7_I2C_CR2_PECBYTE; /* 难点:读阶段 PECBYTE 仍可能影响 NBYTES 计数与最后一个字节的位置 */
  33. f7_msg->count++;
  34. }
  35. cr2 &= ~(STM32F7_I2C_CR2_NBYTES_MASK);
  36. cr2 |= STM32F7_I2C_CR2_NBYTES(f7_msg->count);
  37. cr1 &= ~(STM32F7_I2C_CR1_RXIE | STM32F7_I2C_CR1_TXIE);
  38. cr1 |= STM32F7_I2C_CR1_RXIE;
  39. cr1 &= ~(STM32F7_I2C_CR1_RXIE | STM32F7_I2C_CR1_TXIE |
  40. STM32F7_I2C_CR1_RXDMAEN | STM32F7_I2C_CR1_TXDMAEN);
  41. i2c_dev->use_dma = false;
  42. if (i2c_dev->dma && f7_msg->count >= STM32F7_I2C_DMA_LEN_MIN &&
  43. f7_msg->size != I2C_SMBUS_BLOCK_DATA &&
  44. f7_msg->size != I2C_SMBUS_BLOCK_PROC_CALL) {
  45. ret = stm32_i2c_prep_dma_xfer(i2c_dev->dev, i2c_dev->dma,
  46. cr2 & STM32F7_I2C_CR2_RD_WRN,
  47. f7_msg->count, f7_msg->buf,
  48. stm32f7_i2c_dma_callback,
  49. i2c_dev);
  50. if (!ret)
  51. i2c_dev->use_dma = true;
  52. else
  53. dev_warn(i2c_dev->dev, "can't use DMA\n");
  54. }
  55. if (!i2c_dev->use_dma)
  56. cr1 |= STM32F7_I2C_CR1_RXIE;
  57. else
  58. cr1 |= STM32F7_I2C_CR1_RXDMAEN;
  59. cr2 |= STM32F7_I2C_CR2_START; /* 难点:再次置 START 生成 repeated-start 序列 */
  60. writel_relaxed(cr1, base + STM32F7_I2C_CR1);
  61. writel_relaxed(cr2, base + STM32F7_I2C_CR2);
  62. }
复制代码

stm32f7_i2c_smbus_check_pec:读取硬件计算 PEC 与接收 PEC 字节比较

  1. /**
  2. * @brief SMBus PEC 校验:比较硬件 PECR 与接收缓冲区中的 PEC 字节。
  3. *
  4. * @param i2c_dev 控制器私有数据。
  5. *
  6. * @return 0 表示 PEC 正确;负 errno 表示协议不支持或 PEC 不一致。
  7. */
  8. static int stm32f7_i2c_smbus_check_pec(struct stm32f7_i2c_dev *i2c_dev)
  9. {
  10. struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
  11. u8 count, internal_pec, received_pec;
  12. internal_pec = readl_relaxed(i2c_dev->base + STM32F7_I2C_PECR); /* 难点:PECR 是硬件按 SMBus PEC 规则计算的结果 */
  13. switch (f7_msg->size) {
  14. case I2C_SMBUS_BYTE:
  15. case I2C_SMBUS_BYTE_DATA:
  16. received_pec = f7_msg->smbus_buf[1]; /* 难点:索引取决于该协议的“数据长度与布局” */
  17. break;
  18. case I2C_SMBUS_WORD_DATA:
  19. case I2C_SMBUS_PROC_CALL:
  20. received_pec = f7_msg->smbus_buf[2];
  21. break;
  22. case I2C_SMBUS_BLOCK_DATA:
  23. case I2C_SMBUS_BLOCK_PROC_CALL:
  24. count = f7_msg->smbus_buf[0]; /* 块协议首字节为 count */
  25. received_pec = f7_msg->smbus_buf[count]; /* 难点:PEC 位于块数据末尾,位置由 count 决定 */
  26. break;
  27. default:
  28. dev_err(i2c_dev->dev, "Unsupported smbus protocol for PEC\n");
  29. return -EINVAL;
  30. }
  31. if (internal_pec != received_pec) {
  32. dev_err(i2c_dev->dev, "Bad PEC 0x%02x vs. 0x%02x\n",
  33. internal_pec, received_pec);
  34. return -EBADMSG;
  35. }
  36. return 0;
  37. }
复制代码

stm32f7_i2c_smbus_xfer_msg / stm32f7_i2c_smbus_rep_start / stm32f7_i2c_smbus_check_pec:SMBus 事务编排(首阶段 / 重复起始 / PEC 校验)

stm32f7_i2c_smbus_xfer_msg:SMBus 首阶段(command + 可选数据)配置并启动 START(续)

  1. if (!i2c_dev->use_dma) {
  2. if (cr2 & STM32F7_I2C_CR2_RD_WRN)
  3. cr1 |= STM32F7_I2C_CR1_RXIE;
  4. else
  5. cr1 |= STM32F7_I2C_CR1_TXIE;
  6. } else {
  7. if (cr2 & STM32F7_I2C_CR2_RD_WRN)
  8. cr1 |= STM32F7_I2C_CR1_RXDMAEN;
  9. else
  10. cr1 |= STM32F7_I2C_CR1_TXDMAEN;
  11. }
  12. cr2 |= STM32F7_I2C_CR2_START; /* 置 START 触发硬件产生起始条件并锁定 CR2 中的方向/地址/NBYTES 等配置 */
  13. i2c_dev->master_mode = true; /* 用于 ISR 路径区分主/从状态机 */
  14. writel_relaxed(cr1, base + STM32F7_I2C_CR1);
  15. writel_relaxed(cr2, base + STM32F7_I2C_CR2);
  16. return 0;
  17. }
复制代码

stm32f7_i2c_smbus_rep_start:SMBus 第二阶段(Repeated-START 切换到读)重新配置并触发

  1. /**
  2. * @brief 对需要“先写 command 再读数据”的 SMBus 协议,配置第二阶段为读并触发 Repeated-START。
  3. *
  4. * @param i2c_dev 控制器私有数据。
  5. *
  6. * 关键点:
  7. * - 对块读类协议,不知道后续长度,先读 1 字节(长度字节),并启用 RELOAD 让硬件产生 TCR 以便软件更新 NBYTES。
  8. * - PEC 使能时,读阶段也需要把 PEC 字节计入 NBYTES,使硬件状态机与软件缓冲区布局一致。
  9. */
  10. static void stm32f7_i2c_smbus_rep_start(struct stm32f7_i2c_dev *i2c_dev)
  11. {
  12. struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
  13. void __iomem *base = i2c_dev->base;
  14. u32 cr1, cr2;
  15. int ret;
  16. cr2 = readl_relaxed(base + STM32F7_I2C_CR2);
  17. cr1 = readl_relaxed(base + STM32F7_I2C_CR1);
  18. cr2 |= STM32F7_I2C_CR2_RD_WRN; /* 第二阶段固定为读方向 */
  19. switch (f7_msg->size) {
  20. case I2C_SMBUS_BYTE_DATA:
  21. f7_msg->count = 1;
  22. break;
  23. case I2C_SMBUS_WORD_DATA:
  24. case I2C_SMBUS_PROC_CALL:
  25. f7_msg->count = 2;
  26. break;
  27. case I2C_SMBUS_BLOCK_DATA:
  28. case I2C_SMBUS_BLOCK_PROC_CALL:
  29. f7_msg->count = 1; /* 先读长度字节 */
  30. cr2 |= STM32F7_I2C_CR2_RELOAD; /* 读完 1 字节后触发 TCR,驱动再装载真实长度 */
  31. break;
  32. }
  33. f7_msg->buf = f7_msg->smbus_buf;
  34. f7_msg->stop = true;
  35. if (cr1 & STM32F7_I2C_CR1_PECEN) {
  36. cr2 |= STM32F7_I2C_CR2_PECBYTE; /* 使硬件在本阶段按 PEC 规则处理最后一个字节位置 */
  37. f7_msg->count++;
  38. }
  39. cr2 &= ~(STM32F7_I2C_CR2_NBYTES_MASK);
  40. cr2 |= STM32F7_I2C_CR2_NBYTES(f7_msg->count);
  41. cr1 &= ~(STM32F7_I2C_CR1_RXIE | STM32F7_I2C_CR1_TXIE);
  42. cr1 |= STM32F7_I2C_CR1_RXIE;
  43. cr1 &= ~(STM32F7_I2C_CR1_RXIE | STM32F7_I2C_CR1_TXIE |
  44. STM32F7_I2C_CR1_RXDMAEN | STM32F7_I2C_CR1_TXDMAEN);
  45. i2c_dev->use_dma = false;
  46. if (i2c_dev->dma && f7_msg->count >= STM32F7_I2C_DMA_LEN_MIN &&
  47. f7_msg->size != I2C_SMBUS_BLOCK_DATA &&
  48. f7_msg->size != I2C_SMBUS_BLOCK_PROC_CALL) {
  49. ret = stm32_i2c_prep_dma_xfer(i2c_dev->dev, i2c_dev->dma,
  50. cr2 & STM32F7_I2C_CR2_RD_WRN,
  51. f7_msg->count, f7_msg->buf,
  52. stm32f7_i2c_dma_callback,
  53. i2c_dev);
  54. if (!ret)
  55. i2c_dev->use_dma = true;
  56. else
  57. dev_warn(i2c_dev->dev, "can't use DMA\n");
  58. }
  59. if (!i2c_dev->use_dma)
  60. cr1 |= STM32F7_I2C_CR1_RXIE;
  61. else
  62. cr1 |= STM32F7_I2C_CR1_RXDMAEN;
  63. cr2 |= STM32F7_I2C_CR2_START; /* 再次置 START,硬件产生 Repeated-START 并以读方向继续 */
  64. writel_relaxed(cr1, base + STM32F7_I2C_CR1);
  65. writel_relaxed(cr2, base + STM32F7_I2C_CR2);
  66. }
复制代码

stm32f7_i2c_smbus_check_pec:PEC 校验(硬件 PECR vs 接收缓冲区 PEC 字节)

  1. /**
  2. * @brief SMBus PEC 校验:比较硬件计算值与接收数据末尾的 PEC 字节。
  3. *
  4. * @param i2c_dev 控制器私有数据。
  5. *
  6. * @return 0 校验通过;-EBADMSG 表示 PEC 不一致;-EINVAL 表示当前协议不支持 PEC 校验布局。
  7. */
  8. static int stm32f7_i2c_smbus_check_pec(struct stm32f7_i2c_dev *i2c_dev)
  9. {
  10. struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
  11. u8 count, internal_pec, received_pec;
  12. internal_pec = readl_relaxed(i2c_dev->base + STM32F7_I2C_PECR); /* 硬件按 SMBus PEC 规则累计得到的校验结果 */
  13. switch (f7_msg->size) {
  14. case I2C_SMBUS_BYTE:
  15. case I2C_SMBUS_BYTE_DATA:
  16. received_pec = f7_msg->smbus_buf[1]; /* BYTE/ BYTE_DATA:数据区后 1 字节为 PEC */
  17. break;
  18. case I2C_SMBUS_WORD_DATA:
  19. case I2C_SMBUS_PROC_CALL:
  20. received_pec = f7_msg->smbus_buf[2]; /* WORD/PROC_CALL:2 字节数据后为 PEC */
  21. break;
  22. case I2C_SMBUS_BLOCK_DATA:
  23. case I2C_SMBUS_BLOCK_PROC_CALL:
  24. count = f7_msg->smbus_buf[0]; /* 块读:第 0 字节为长度 */
  25. received_pec = f7_msg->smbus_buf[count]; /* PEC 在块尾部:索引由长度决定(依赖上层对缓冲区布局的一致性约束) */
  26. break;
  27. default:
  28. dev_err(i2c_dev->dev, "Unsupported smbus protocol for PEC\n");
  29. return -EINVAL;
  30. }
  31. if (internal_pec != received_pec) {
  32. dev_err(i2c_dev->dev, "Bad PEC 0x%02x vs. 0x%02x\n",
  33. internal_pec, received_pec);
  34. return -EBADMSG;
  35. }
  36. return 0;
  37. }
复制代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2026 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行