配置

硬件周边件和 CPU 通过总线绑定在一起,例如 PCI 总线。

当启动时, BIOS (或者同等平台启动软件)发现绑定在 PCI 总线上的所有周边件。 每一个周边件被分配好对应资源(尤其是中断向量和配置寄存器的地址范围)。

这样做的影响在于分配给每个外设的实际资源在重启时可能是不同的。 当操作系统软件启动时,它枚举了总线和对所有支持的设备启动相关驱动。 接下来驱动调用 PCI 函数来获取关于他们的设备配置信息,这样就可以映射寄存器并绑定中断。

基础地址寄存器

基础地址寄存器(BAR)是在每一个 PCI 设备上都存在的配置寄存器。 这是 BIOS 存储关于设备信息的地方,例如分配的中断向量和控制寄存器地址。另外设备特有信息也存放在这里。

调用pci_map_bar()来使 BAR 寄存器映射到驱动主机的地址空间内:

  1. zx_status_t pci_map_bar(const pci_protocol_t* pci, uint32_t bar_id,
  2. uint32_t cache_policy, void** vaddr, size_t* size,
  3. zx_handle_t* out_handle);

第一个参数pci是 PCI 协议的指针。通常情况下,你可以通过device_get_protocol()在你的bind()函数内获取到。

第二个参数bar_id,是从0开始的 BAR 寄存器数量。

第三个参数cache_policy决定了访问的缓存策略,你可以设置为以下值:

cache_policy value Meaning
ZX_CACHE_POLICY_CACHED use hardware caching
ZX_CACHE_POLICY_UNCACHED disable caching
ZX_CACHE_POLICY_UNCACHED_DEVICE disable caching, and treat as device memory
ZX_CACHE_POLICY_WRITE_COMBINING uncached with write combining

注意ZX_CACHE_POLICY_UNCACHED_DEVICE是依赖架构的,并且在某些架构中它是等同于 ZX_CACHE_POLICY_UNCACHED

接下来的三个参数则为返回值。 vaddrsize返回一个寄存器区域的指针(和长度),out_handle则存储了创建的VMO句柄。

读和写内存

pci_map_bar()函数返回了有效值后,你就可以通过简单的指针操作访问 BAR ,例如:

  1. volatile uint32_t* base;
  2. ...
  3. zx_status_t rc;
  4. rc = pci_map_bar(dev->pci, 0, ZX_CACHE_POLICY_UNCACHED_DEVICE, &base, &size, &handle);
  5. if (rc == ZX_OK) {
  6. base[REGISTER_X] = 0x1234; // configure register X for deep sleep mode
  7. }

base声明为volatile类型是很重要的—这告诉了编译器不要对base所指向的数据内容做任何的假设。例如:

  1. int timeout = 1000;
  2. while (timeout-- > 0 && !(base[REGISTER_READY] & READY_BIT)) ;

这是一种典型(有界的)的轮询,试图达到很短的轮询序列。 在定义中没有使用volatile的关键字,编译器则没有理由相信base[REGISTER_READY]的值会产生变化,所以这将导致它只会被读取一次。