寄存器概览

寄存器驱动程序应该用于 可以由多个驱动程序访问。

概念

驱动程序通常通过基于寄存器的接口与硬件通信。 从概念上讲,寄存器是指可读取或 通过地址空间写入。MMIO 寄存器通过 CPU 的 内存地址空间,而 I2C 寄存器是通过 I2C 总线访问的。

大多数硬件仅支持原子寄存器写入。想要 要更改寄存器中的一些位,必须通过读取/修改/写入来实现 操作。例如,对于 32 位寄存器,如果要更改 将 0-1 位都写入 0,则需要读取该寄存器的所有 32 位。 说 0xFFFFFFFF,然后回写到该寄存器 0xFFFFFFFC

同步

读取/修改/写入操作可能会导致出现数据争用。通过 经典交易示例“从银行账户提取 10 美元”为 实际上是读取/修改/写入操作请参阅 示例部分提供了具体图示。

注册为“独家所有”的注册机构司机应承担的责任 将其访问同步至寄存器,从而避免出现数据争用。( 可能性仍然存在,但这不在 注册驱动程序)。

当寄存器“共享”时(可以被多个驱动程序访问),我们需要 全球协调员,避免出现比赛。寄存器驱动程序可以这样操作 全局协调器,当每个驾驶员只需要访问不相交的子集时, 寄存器的位数。全球协调员将提供所需的 寄存器读写之间的同步。

隔离

寄存器驱动程序不仅通过提供同步来阻止竞态, 还能隔离位字段寄存器驱动程序拆分 注册到多个资源中,并确保每个驱动程序 可以访问这些位驱动程序可能不会意外读取或写入 与自身无关的位。

运行理论

板驱动程序中,一个寄存器 由多个驱动程序访问,声明了不同的位字段 它想要公开的对象。寄存器驱动程序为每个 位字段。想要访问这些位字段的驱动程序需要 才能绑定到相应的设备

在寄存器驱动程序中,每个寄存器都有对应的锁,以确保 同时只有一个读/写可以访问寄存器。 寄存器驱动程序由 registers-util.fidl 定义。

使用方法

目前,重置寄存器是仅有的寄存器 改为使用寄存器驱动程序。本部分将使用它们 如何使用寄存器驱动程序。

  1. 板驱动程序变更

    元数据格式在 metadata.fidl 中声明。

    在板驱动程序(即 nelson-registers)中, 更改。

    a. MMIO

    确保寄存器驱动程序有权访问 MMIO。否则, 添加 MMIO 和相应的 MMIO 索引:

      enum MmioMetadataIdx {
        kResetMmio,
    
        kMmioCount,
      };
    
      static const std::vector<fpbus::Mmio> registers_mmios{
          {
            {
              .base = A311D_RESET_BASE,
              .length = A311D_RESET_LENGTH,
            },
          },
      };
    

    请注意,MmioMetadataIndex 必须与 MMIO 的 registers_mmios 中的索引。

    b. 声明位字段

    RegistersMetadataToFidl 帮助程序 函数可用于声明位字段:

      auto metadata_bytes = fidl_metadata::registers::RegistersMetadataToFidl<uint32_t>(kRegisters);
      if (metadata_bytes.is_error()) {
        zxlogf(ERROR, "%s: Failed to FIDL encode registers metadata %s\n", __func__,
               metadata_bytes.status_string());
        return metadata_bytes.error_value();
      }
    
      const std::vector<fpbus::Metadata> registers_metadata{
          {
            {
              .type = DEVICE_METADATA_REGISTERS,
              .data = metadata_bytes.value(),
            },
          },
      };
    

    其中,位字段在 kRegisters 字段中定义:

      static const fidl_metadata::registers::Register<uint32_t> kRegisters[]{
          {
              .bind_id = aml_registers::REGISTER_USB_PHY_V2_RESET,
              .mmio_id = kResetMmio,
              .masks =
                  {
                      {
                          .value = aml_registers::USB_RESET1_REGISTER_UNKNOWN_1_MASK |
                                   aml_registers::USB_RESET1_REGISTER_UNKNOWN_2_MASK,
                          .mmio_offset = A311D_RESET1_REGISTER,
                      },
                      {
                          .value = aml_registers::USB_RESET1_LEVEL_MASK,
                          .mmio_offset = A311D_RESET1_LEVEL,
                      },
                  },
          },
          ...
      };
    
    • bind_id:每个位字段定义的唯一 ID, 用于在绑定期间识别它
    • mmio_id:与该位字段的 MMIO 对应的 ID 指的是什么。
    • masks:描述特定位字段的掩码列表, 应可通过此寄存器设备访问。
      • value:可供访问的位掩码,使用 1 表示可访问,0表示无法访问。例如,对于 32 位寄存器,位掩码可以是 0xFFFF0000,这意味着 可通过该容器访问寄存器的高 16 位 而后 16 位数则不行。
      • mmio_offset:从 MMIO 开头算起的起始偏移量 由这些位字段定义的 mmio_id 标识。
      • count:此位字段掩码的寄存器地址数 适用于以下 mmio_offset。默认为 1
      • overlap_check_on:如果为 true,寄存器驱动程序将 验证这些位字段是否 已定义的位字段否则,系统会跳过该检查。默认值 发送至 true

    寄存器驱动程序将创建一个提供 registers-util.fidl 的设备。 具有绑定属性: {:.devsite-disable-click-to-copy} bind_fuchsia_register::NAME == bind_id

  2. 绑定到寄存器驱动程序

    然后,希望访问这些位字段的驱动程序必须绑定到 上述创建的设备。

  3. 使用寄存器驱动程序接口

    成功连接到寄存器设备后,例如通过 fidl::WireSyncClient<fuchsia_hardware_registers::Device> register, 您可以根据 FIDL 调用相应的 registers-util.fidl 中定义的接口。 例如,对于 32 位寄存器,

    auto result =
        reset_register_->WriteRegister32(RESET1_LEVEL_OFFSET, aml_registers::USB_RESET1_LEVEL_MASK,
                                         aml_registers::USB_RESET1_LEVEL_MASK);
    if ((result.status() != ZX_OK) || result->is_error()) {
      zxlogf(ERROR, "Write failed\n");
      return ZX_ERR_INTERNAL;
    }
    
  4. 享受隔离的同步寄存器!

示例

数据争用

假设我们有一个寄存器可供驱动程序 A 和驱动程序 B 访问。 现在,驱动程序 A 想要将 b01 写入寄存器和驱动程序的位 0-1 中 B 想要将 b001 写入寄存器的第 8-10 位。视时间而定 驱动程序 A 可能会读取寄存器的原始值,例如 0xFFFFFFFF, 驱动程序 B 也可以读取相同的值。然后,驱动程序 A 向位 0-1 写入数据 并放入内存 0xFFFFFFFD。然后,驱动程序 B 向内存写入数据 0xFFFFF9FF。在这一事件序列中,寄存器现在存储 0xFFFFF9FF(驱动程序 B 在最后写入的内容)。不过,下次 驱动程序 A 读取该寄存器的位 0-1,因此不会获得 与之前写入的值相同。

AMLogic SoC

本部分通过一些示例来说明寄存器驱动程序应在何处 。注册数据库驱动程序时, 在多个驱动程序之间进行同步, 不一定是 仅用于隔离资源请注意,下方并未列出所有情况 AMLogic SoC 中的共享寄存器列表。

重置寄存器

AMLogic SoC 具有针对各种硬件单元的重置功能 集中在几个 32 位寄存器中。这以 S905D3 中的 RESET1_REGISTER 表 6-186。例如,USB 重置操作 由位 2 控制,SD_EMMC 由位 12-14 控制。硬件设计影响 我们可以在多个驱动程序(包括 EMMC)之间共享 RESET1_REGISTER 驱动程序和 USB 驱动程序。

如需了解针对重置所做的板级文件更改,请参阅 nelson-registers 寄存器和 nelson-usb - 用于添加重置功能的 aml_usb_phy 设备 注册 fragment。AmlUsbPhy::InitPhy()aml-usb-phy 使用次数 FIDL 客户端写入重置寄存器。

功率寄存器

与上述重置寄存器类似,AO_RTI_GEN_PWR_SLEEP0S905D3 表 6-17)和 AO_RTI_GEN_PWR_ISO0S905D3 表) 6-18)由多个驱动程序(包括显示屏、USB 和机器学习)共享, 由寄存器驱动程序管理,因为它们是可写入的,并且需要 协调。此迁移目前正在进行中。

另一方面,虽然 AO_RTI_GEN_PWR_ACK0S905D3 表 6-19) 将由多个驱动程序(PCIE、USB、显示器等)共享,且处于只读状态。 可以同时访问此寄存器中的数据,而不会产生任何竞选风险, 因此无需使用寄存器驱动程序进行同步。如果 我们仍然可以使用寄存器驱动程序来隔离资源。