address-cell& size-cell

I2C子设备为啥size-cell为零

在设备树(Device Tree)中,I2C 子设备的 #size-cells 为零,主要是由 I2C 总线的寻址特性决定的。

简单来说,这是因为 I2C 设备在总线上占据的是一个**"点"(单一地址),而不是一个"范围"**(地址区间)。

以下是详细的原因分析:

1. reg 属性的含义

在设备树中,子节点的 reg 属性通常由父节点的两个属性来定义格式:

  • #address-cells :定义 reg 中"地址"字段占用的字数(cell,每个 cell 为 32 位)。
  • #size-cells :定义 reg 中"长度/大小"字段占用的字数。

对于常见的内存映射设备 (如片上外设),它们占据一段连续的内存空间(例如:起始地址 0x40001000,长度 0x100),因此需要 size 来描述范围。

2. I2C 的寻址方式:单一地址 vs 地址范围

  • 非内存映射 :I2C 是一种串行总线,每个从设备(Slave)由一个唯一的 7位或10位地址 标识。
  • 没有"长度"概念:当你访问一个 I2C 设备时,你只需要知道它的从机地址(Slave Address)。这个地址是一个离散的标识符,它不代表一段地址空间,也没有"从地址 A 到地址 B"这种长度的概念。
  • reg 属性的简化 :在 I2C 节点下,reg = <0x50>; 就足够表示这个设备(比如 EEPROM)的地址是 0x50。由于不需要描述"占用多少地址空间",所以 reg 里不需要包含 length 字段。

3. 设置为零的具体作用

在 I2C 控制器(父节点)中设置 #size-cells = <0>;,是为了告诉内核:"我的子设备(I2C 从机)在描述自己的资源(reg)时,只需要写地址,不需要写长度。"

示例对比:

  • 内存总线(需要 Size)

    dts 复制代码
    soc {
        #address-cells = <1>;
        #size-cells = <1>;
        serial@40001000 {
            reg = <0x40001000 0x100>; // 地址是0x40001000,长度是0x100
        };
    };
  • I2C 总线(不需要 Size)

    dts 复制代码
    i2c@40002000 {
        #address-cells = <1>;
        #size-cells = <0>; // 规定子设备不需要长度字段
        eeprom@50 {
            reg = <0x50>;     // 只需要地址,没有第二个数字
        };
    };

总结

I2C 子设备的 size-cell 为零是因为:

  1. I2C 协议基于从机 ID 寻址,每个设备只占用一个逻辑地址。
  2. 没有物理上的"空间长度" 需要被映射。
  3. 同样的逻辑也适用于 SPI 总线 (地址是片选线编号)和 CPU 节点 (地址是内核 ID),它们的 #size-cells 通常也是 0。

为啥内存总线需要 Size

内存总线(Memory-mapped I/O,简称 MMIO)之所以需要 size(即 #size-cells 不为 0),是因为内存地址空间和 I2C 这种串行总线在硬件寻址逻辑操作系统管理方式上有本质的区别。

以下是为什么要定义"长度(Size)"的四个核心原因:

1. 内部寄存器范围(Offset 概念)

一个内存映射设备(如 UART 控制器、网卡、GPU)通常不只有一个寄存器,而是有一堆寄存器。

  • I2C 设备 :你向地址 0x50 发送指令,具体的内部寄存器(如 EEPROM 的存储地址)是在数据包里定义的,不是在总线层面定义的。
  • 内存映射设备 :CPU 直接通过地址访问。比如一个 UART 控制器:
    • 0x40001000 是数据寄存器
    • 0x40001004 是控制寄存器
    • 0x40001008 是状态寄存器
  • 结论 :这个设备占据了从 0x40001000 开始的一整块"领地"。操作系统必须知道这块地有多大,才能给驱动程序提供正确的访问范围。

2. 内存管理单元(MMU)与页表映射

在现代操作系统(如 Linux)中,驱动程序不能直接访问物理地址,必须通过 虚拟地址 访问。

  • 内核在加载驱动时,会调用类似 ioremap(base, size) 的函数。
  • 映射单位 :MMU 是按"页"(通常是 4KB)来映射的。如果 OS 不知道设备的 size,它就不知道该为这个设备在虚拟地址空间里分配多大的区域,也不知道该映射多少个页表项。

3. 防止地址冲突(Resource Allocation)

系统内存地址空间是极其宝贵的"房地产"。

  • 内核有一个资源管理器(在 Linux 里可以通过 cat /proc/iomem 查看)。
  • 为了防止两个设备被分配到重叠的地址区域,内核需要记录每个设备从哪开始 以及到哪结束
  • 如果没有 size,内核只知道 0x40001000 被占用了,但不知道 0x40001004 是不是也被这个设备占用了,这会导致资源管理的混乱。

4. 总线解码器(Hardware Decoder)的工作原理

从硬件设计的角度看:

  • I2C:总线上的所有设备都看着数据流,当地址匹配时,某个设备跳出来响应。
  • 内存总线:CPU 发出一个地址信号。总线控制器(Bus Fabric/Interconnect)需要根据这个地址来决定把信号传给哪个外设。
  • 译码逻辑 :硬件内部的地址译码器就像一个"围墙"。它判断:if (Address >= Base && Address < Base + Size),则激活该设备。这个 Size 是硬件逻辑电路中真实存在的范围。

类比理解

  • I2C 就像"快递员投递"
    你只需要写一个门牌号 (地址 0x50)。快递员顺着街道走,看到门牌号就敲门。他不需要知道你家房子有多大。
  • 内存总线就像"土地规划"
    政府(CPU/OS)要把一块地划拨给工厂(外设)。政府不仅要给工厂一个起始坐标 (Base Address),还必须标明这块地占地几亩(Size)。否则,隔壁的工厂可能会把楼盖到你的地盘上,或者你在自家院里走路(访问寄存器)却走到了别人家里。

总结

特性 I2C 子设备 内存映射设备 (MMIO)
访问方式 串行协议包发送地址 CPU 指令直接读写内存地址
占用空间 逻辑上的一个点 物理上的一个地址区间
映射需求 不需要映射,直接由驱动发包 需要 MMU 映射成虚拟地址
#size-cells 0 通常为 1 或 2
相关推荐
XIAOHEZIcode8 小时前
Ubuntu 终端美化全栈指南:Bash 到 Kitty 踩坑实录
linux·ubuntu·命令行
唐青枫10 小时前
别再只会用 cron:Linux systemd Timer 定时任务实战详解
linux
AlfredZhao2 天前
生产环境里,为什么不建议把普通端口直接暴露到公网?
linux·https·443·80
戴为沐3 天前
Linux内存扩容指南
linux
zylyehuo4 天前
Linux 彻底且安全地删除文件
linux
用户805533698034 天前
主线 U-Boot 上 RK3506:和闭源 rkbin 拔河的三个隐性契约
linux·嵌入式
用户034095297914 天前
linux fcitx 5 雾凇拼音 设置在中文输入法下仍然输入英文标点
linux
Web3探索者6 天前
可视化服务器管理和传统命令行区别是什么?新手教程:Linux 运维到底该用图形界面还是 SSH 命令行?
linux·ssh
zylyehuo6 天前
Linux系统中网线与USB网络共享冲突
linux
Sokach10157 天前
Linux Shell 脚本从零到能用:一个新手的一天学习总结
linux