GPIO 初始化

某些驱动程序依赖于板级特定的静态 GPIO 引脚或引脚多路复用配置。在这些情况下,通常需要将 GPIO 配置详细信息保留在板级驱动程序中,以确保依赖的驱动程序保持可移植性。为此,板级驱动程序需要将元数据连同初始化步骤列表一起传递给 GPIO 驱动程序。请参阅下面的示例。

在板级驱动程序中配置 GPIO 元数据

假设 SDMMC 驱动程序需要使用特定的备用功能和驱动强度来配置引脚。为此,板级驱动程序会创建一个 fuchsia.hardware.gpioimpl.InitStep 对象列表,用于指定要进行的 GPIO 协议调用以及要进行调用的 GPIO 索引:

#include <fidl/fuchsia.hardware.gpioimpl/cpp/wire.h>

// Helper lambda to simplify the following code.
auto sdmmc_gpio = [&](uint64_t alt_function, uint64_t drive_strength_ua) {
  return fuchsia_hardware_gpioimpl::wire::InitOptions::Builder(arena)
      .alt_function(alt_function)
      .drive_strength_ua(drive_strength_ua)
      .Build();
};

const fuchsia_hardware_gpioimpl::wire::InitStep init_steps[] = {
    {SDMMC_GPIO_0, sdmmc_gpio(SDMMC_GPIO_0_ALT_FUNCTION, 4000)},
    {SDMMC_GPIO_1, sdmmc_gpio(SDMMC_GPIO_1_ALT_FUNCTION, 4000)},
    {SDMMC_GPIO_2, sdmmc_gpio(SDMMC_GPIO_2_ALT_FUNCTION, 4000)},
    {SDMMC_GPIO_3, sdmmc_gpio(SDMMC_GPIO_3_ALT_FUNCTION, 4000)},
    {SDMMC_GPIO_4, sdmmc_gpio(SDMMC_GPIO_4_ALT_FUNCTION, 4000)},
    {SDMMC_GPIO_5, sdmmc_gpio(SDMMC_GPIO_5_ALT_FUNCTION, 4000)},
};

然后,此列表将作为元数据提供给 GPIO 驱动程序:


fuchsia_hardware_gpioimpl::wire::InitMetadata metadata;
metadata.steps = fidl::VectorView<fuchsia_hardware_gpioimpl::wire::InitStep>::FromExternal(
    init_steps, std::size(init_steps));

fit::result encoded = fidl::Persist(metadata);
if (!encoded.is_ok()) {
  return encoded.error_value().status();
}

const std::vector<fpbus::Metadata> gpio_metadata{
    {{
        .type = DEVICE_METADATA_GPIO_INIT_STEPS,
        .data = std::move(encoded.value()),
    }},
    // Other metadata goes here.
};

const fpbus::Node gpio_dev = []() {
  fpbus::Node dev = {};
  dev.name() = "gpio";
  dev.vid() = PDEV_VID_SOME_VENDOR;
  dev.pid() = PDEV_PID_SOME_PRODUCT;
  dev.did() = PDEV_DID_SOME_GPIO_DEVICE;
  dev.mmio() = gpio_mmios;
  dev.irq() = gpio_irqs;
  dev.metadata() = gpio_metadata;
  return dev;
}();

// Add the node here.

使用绑定规则创建对 GPIO 配置的依赖项

现在,GPIO 驱动程序可以正确配置这些引脚,因此必须先让 SDMMC 驱动程序依赖于此配置,然后才能进行绑定。为此,请在其板级驱动程序绑定文件中向 SDMMC 设备添加一个额外的 fragment:

using fuchsia.gpio;

// Other nodes go here.

node "gpio-init" {
  fuchsia.BIND_INIT_STEP == fuchsia.gpio.BIND_INIT_STEP.GPIO;
  fuchsia.BIND_GPIO_CONTROLLER = 1;
}

GPIO 驱动程序会在完成初始化步骤的处理后添加 fuchsia.gpio.BIND_INIT_STEP.GPIO 设备。任意数量的子项都可以绑定到此设备,以确保其 GPIO 在启动之前已配置完毕。

GPIO 驱动程序如何处理配置错误

当 GPIO 驱动程序在处理初始化步骤时遇到错误时,它会继续设置其余的 GPIO,但不会添加 init 设备。 这是为了确保尽可能多的引脚处于已知状态,并且驱动程序不会尝试使用可能配置错误的引脚运行。同样,如果 GPIO 驱动程序没有或无法解析初始化元数据,则不会添加任何 init 设备。

在以下两种情况下,建议使用 GPIO 初始化步骤:

  • 驱动程序需要静态板级或平台级特定的 GPIO 配置。

上拉/下拉、备用功能和驱动强度设置通常是板级或平台级特定的,因此最好使用 GPIO 初始化在板级驱动程序中对其进行配置。如果所有板上的 GPIO 输出值不相同(或不需要),也建议采用这种方式设置 GPIO 输出值。

  • 多个驱动程序依赖于一个 GPIO 的静态配置。

如果多个驱动程序依赖于一个 GPIO 的静态配置,则应通过 GPIO 初始化完成该配置。这样可以防止多个驱动程序必须对单个 GPIO 进行可能冲突的调用。

对于驱动程序在运行时会更改的任何配置,都不应使用 GPIO 初始化步骤。

GPIO 初始化选项

如需查看初始化选项的完整列表及其说明,请参阅 fuchsia.hardware.pinimpl FIDL 规范。