# IO-Domain 调试指南

## IO-Domain 概述

在嵌入式系统中，电压域（ Voltage Domain）指的是为某一部分硬件或电路提供特定电压的电源区域。一个系统可能包含多个电压域，每个电压域会根据不同模块的需要提供不同的电压。这些电压域通常通过不同的模块或子系统进行管理与控制

<font color=red> 注意：</font>
- 电压域配置：如果硬件设计外接电压域为 3.3V， IO-Domain 需要配置为 3.3V；如果外接电压域为 1.8V， IO-Domain 需要配置为 1.8V。
- 电压不匹配问题：外接电压域为 3.3V，而 IO-Domain 配置为 1.8V，可能会对芯片造成损伤；如果外接电压域为 1.8V，而 IO-Domain 配置为 3.3V，相关模块可能无法正常工作。

## IO-Domain 特点

在芯片中，电源管理通常按不同的功能域划分，每个功能域根据需求管理不同的电源：

- **AON (Always On) Power Domain**：该电源域负责总是处于活动状态的电源管理，保证即使在设备休眠时也能保持某些基本功能（例如，唤醒和时间跟踪）正常工作。
- **DSP (DSP Subsystem) Power Domain**：负责数字信号处理器（ DSP）子系统的电源管理。 DSP 是处理数字信号（如音频、视频、语音识别等）的专用硬件模块，通常在特定的多媒体应用中使用。
- **SOC (System on Chip) Power Domain**：包括整个芯片的管理，负责与芯片的主要控制器、总线以及其他基本功能模块相关的电源供应。
- **CPU (CPU Subsystem) Power Domain**：该电源域管理 CPU 子系统的电源， CPU 是执行大部分计算任务的核心单元。
- **BPU (BPU Subsystem) Power Domain**： BPU（即神经网络处理单元或智能处理单元）负责处理与人工智能（ AI）、机器学习（ ML）等相关的任务。
- **DDR (DDR Subsystem) Power Domain**：负责动态随机访问内存（ DDR）的电源管理。 DDR 存储器用于系统数据存取，在大部分应用中占用较大功耗。
- **VIDEO (Video Subsystem) Power Domain**：该电源域负责视频处理模块的电源供应，包括视频解码、编码、显示等功能。
- **GPU (GPU Subsystem) Power Domain**：负责图形处理单元（ GPU）的电源管理。 GPU 主要用于图形渲染、图像处理、计算加速等任务，在图形密集型应用中发挥重要作用。
- **ISP (ISP Subsystem) Power Domain**：该电源域管理图像信号处理器（ ISP）的电源供应。 ISP 通常用于处理从相机传感器获取的原始图像数据，执行图像的滤波、降噪、白平衡等处理。

## 功能描述

### IO-Domain 典型应用

在芯片中通常使用设备树来配置 IO-Domain , 但不同引脚使用的 IO-Domain 与硬件原理图强相关，查看原理图， AON GPIO 电源域配置如下：

![io-IO_DOMAIN_AON](_static/_images/12-IO-DOMAIN_Debug_Guide_zh_CN/IO_DOMAIN_AON.png)

设备树中 AON Domain 对应上图中原理图 AON_GPIO_XX 的引脚，配置如下：

```shell
/* AON_GPIO 0-7 */
	aon_gpio_0: aon_gpio_0 {
		horizon,pins = <
			AON_GPIO0_PIN0	INVALID_PINMUX	BIT_OFFSET0		MUX_ALT0	&pconf_input_en_1v8
		>;
	};

	aon_gpio_1: aon_gpio_1 {
		horizon,pins = <
			AON_GPIO0_PIN1	INVALID_PINMUX	BIT_OFFSET0		MUX_ALT0	&pconf_input_en_1v8
		>;
	};

	pconf_input_en_1v8: pconf-input-en-1v8 {
		input-enable;
		power-source = <HORIZON_IO_PAD_VOLTAGE_1V8>;
		drive-strength = <3>;
	};
```

这段代码描述了与 AON_GPIO（ Always On GPIO）复用引脚 的相关配置和使用的电源域：

- `aon_gpio_0` 和 `aon_gpio_1` 是为两个 GPIO 引脚设置的别名。它们分别描述了两个物理 GPIO 引脚， aon_gpio_0 和 aon_gpio_1 ，对应的是 AON_GPIO0_PIN0 和 AON_GPIO0_PIN1 。
- `horizon,pins 是一个属性，定义了每个引脚的配置信息。
- `AON_GPIO0_PIN0` 和 `AON_GPIO0_PIN1`：这些是设备树中定义的物理 GPIO 引脚标识符。
- `INVALID_PINMUX`：表示该引脚的默认引脚复用（ Pin Muxing）配置无效或没有被设置。通常， Pin Muxing 用于设置一个引脚的功能，例如作为输入、输出、或者特定功能（如 UART、 SPI 等）的引脚。
- `BIT_OFFSET0`：这个字段可能是指定该引脚的位偏移量。位偏移用于指示该引脚在寄存器或内存中的位置，通常用于位掩码操作。
- `MUX_ALT0`：表示引脚复用的选择。 MUX_ALT0 指该引脚被配置为某个特定的备用功能。
- `&pconf_input_en_1v8`：这个是一个引用，指向在设备树中定义的另一个节点 pconf-input-en-1v8 ，它表示此引脚的电源配置 1.8V 输入启用。

其中 pinmux-func 中 pconf_input_en_1v8 节点定义了电源配置与驱动能力：

- `input-enable`：这个属性表示启用了输入功能，即该引脚将作为输入引脚工作
- `power-source` = <HORIZON_IO_PAD_VOLTAGE_1V8>：指定该引脚的电源电压为 1.8V，表明该引脚需要一个 1.8V 的电源电压来正常工作。
- `drive-strength` = <3>：表示驱动强度的设置。驱动强度指的是引脚在驱动信号时能够提供的电流，其中高低电平输出电流对应驱动级如下表：

IOL Low Level Output Current @VOL (max) = 0.125*VDDIO

| DS   (Drive Strength) | Typical Current | Unit |
|:---------------------:|:---------------:|------|
|         0b0000        |       3.8       | mA   |
|         0b0001        |       5.7       | mA   |
|         0b0010        |       7.5       | mA   |
|         0b0011        |       9.4       | mA   |
|         0b0100        |      11.3       | mA   |
|         0b0101        |      13.2       | mA   |
|         0b0110        |      15.0       | mA   |
|         0b0111        |      16.9       | mA   |
|         0b1000        |      18.8       | mA   |
|         0b1001        |      20.7       | mA   |
|         0b1010        |      22.6       | mA   |
|         0b1011        |      24.4       | mA   |
|         0b1100        |      26.3       | mA   |
|         0b1101        |      28.2       | mA   |
|         0b1110        |      30.0       | mA   |
|         0b1111        |      31.9       | mA   |

IOH High Level Output Current @VOH (min) = 0.75*VDDIO

| DS   (Drive Strength) | Typical Current | Unit |
|:---------------------:|:---------------:|------|
|         0b0000        |       5.3       | mA   |
|         0b0001        |       7.9       | mA   |
|         0b0010        |      10.6       | mA   |
|         0b0011        |      13.2       | mA   |
|         0b0100        |      15.8       | mA   |
|         0b0101        |      18.5       | mA   |
|         0b0110        |      21.1       | mA   |
|         0b0111        |      23.7       | mA   |
|         0b1000        |      26.3       | mA   |
|         0b1001        |      29.0       | mA   |
|         0b1010        |      31.6       | mA   |
|         0b1011        |      34.2       | mA   |
|         0b1100        |      36.9       | mA   |
|         0b1101        |      39.5       | mA   |
|         0b1110        |      42.1       | mA   |
|         0b1111        |      44.7       | mA   |

### IO-Domain 功能原理

设备树中的 GPIO 复用配置与 IO-DOMAIN 都需要通过 pinctrl 子系统来完成，在 `Pinctrl_select_state` 中 使用 `pinconf_apply_setting` 来完成电气属性的配置， 具体内核调用过程如图：

![io-domain_flow_chart](_static/_images/12-IO-DOMAIN_Debug_Guide_zh_CN/io-domain_work.png)

### IO-Domain 工作方式

在驱动的实现中， IO Domain 的流程可以概括为以下几个步骤

**1. 引脚配置（Pin Configuration）**

- 通过硬件原理图编写设备树，驱动程序能够读取引脚配置。每个引脚都有一组与之相关的配置参数，包括复用功能（如 GPIO、 UART、 SPI 等）、电压配置（如 1.8V、 3.3V）以及其他电气特性。

```shell
horizon,pins = < AON_GPIO0_PIN0 INVALID_PINMUX BIT_OFFSET0 MUX_ALT0 &pconf_input_en_1v8 >;
```
上述设备树配置指定了 AON_GPIO0_PIN0 引脚的复用方式为 MUX_ALT0 （ GPIO 功能），并且指定了该引脚的电压为 1.8V（通过 &pconf_input_en_1v8 ）。

**2. IO Domain 的初始化**

- 在驱动中， horizon_pinctrl_probe 函数会读取设备树的配置，并将相关的引脚和硬件资源进行初始化。此时，驱动会为每个引脚分配资源、配置寄存器以及设置相应的电气参数。

```shell
ret = horizon_pinctrl_parse_gpio_bank(pdev, ipctl);
```

**3.  电源管理和电气设置**

- 驱动程序通过对 IO Domain 控制寄存器的操作，管理引脚的电气特性（例如电压和电流）。 horizon_pin_power_source 函数会根据传入的标志位设置相应的电压源：

```shell
if (flags == HORIZON_IO_PAD_VOLTAGE_IP_CTRL) {
    val |= MS_BIT_CTRL;
}
```

**4.  引脚方向控制（Input/Output Direction Control）**

- 在配置完引脚的电气特性后，驱动程序还会根据需要设置引脚的输入或输出方向。 horizon_gpio_set_direction 用于控制引脚的工作模式：

```shell
ret = horizon_gpio_set_direction(pctldev, pin, true, arg);
```

**5.  引脚多路复用（Pin Multiplexing）**

- 通过设备树和驱动的配置，系统能够将一个引脚分配给不同的外设功能。多路复用配置会根据需求切换引脚的功能。例如，某个引脚可能同时支持 GPIO 和 UART 功能，通过设置 MUX_ALT 复用寄存器来进行切换。

```shell
pinmux_enable_setting(const struct pinctrl_setting *setting)
```

## 驱动代码

### Kernel Space

#### 代码位置
```bash
drivers/pinctrl/hobot/ # pinctrl 驱动代码源文件所在文件夹
include/linux/platform_data/pinctrl-single.h # pinctrl 驱动代码头文件
```

#### IO-Domain 的 DTS

 Pinctrl 功能相关定义位于 BSP 源码包的 kernel 文件夹下的 `arch/arm64/boot/dts/hobot/pinmux-func.dtsi` 文件内。

由于 IO-Domain 在 pinctrl-single 的框架下实现，因此其 DTS 和 pinctrl 的类似，在 IO-Domain 的 DTS 里已经列出了所有模块 1.8V 和 3.3V 的配置组，一般不需要修改，在具体开发时根据实际情况选择使用即可。

```shell
	pconf_bias_disabled_ds2_1v8: pconf-bias-disabled-ds2-1v8 {
		bias-disable;
		power-source = <HORIZON_IO_PAD_VOLTAGE_1V8>;
		drive-strength = <2>;
	};
```

```shell
	pinctrl_enet: enetgrp {
		horizon,pins = <
			HSIO_ENET_MDC		HSIO_PINMUX_1	BIT_OFFSET30	MUX_ALT0	&pconf_bias_disabled_ds2_1v8
			HSIO_ENET_MDIO		HSIO_PINMUX_1	BIT_OFFSET28	MUX_ALT0	&pconf_bias_disabled_ds2_1v8
			HSIO_ENET_TXD_0		HSIO_PINMUX_1	BIT_OFFSET26	MUX_ALT0	&pconf_bias_disabled_ds2_1v8
			HSIO_ENET_TXD_1		HSIO_PINMUX_1	BIT_OFFSET24	MUX_ALT0	&pconf_bias_disabled_ds2_1v8
			HSIO_ENET_TXD_2		HSIO_PINMUX_1	BIT_OFFSET22	MUX_ALT0	&pconf_bias_disabled_ds2_1v8
			HSIO_ENET_TXD_3		HSIO_PINMUX_1	BIT_OFFSET20	MUX_ALT0	&pconf_bias_disabled_ds2_1v8
			HSIO_ENET_TXEN		HSIO_PINMUX_1	BIT_OFFSET18	MUX_ALT0	&pconf_bias_disabled_ds2_1v8
			HSIO_ENET_TX_CLK	HSIO_PINMUX_1	BIT_OFFSET16	MUX_ALT0	&pconf_bias_disabled_ds2_1v8
			HSIO_ENET_RX_CLK	HSIO_PINMUX_1	BIT_OFFSET14	MUX_ALT0	&pconf_bias_disabled_ds2_1v8
			HSIO_ENET_RXD_0		HSIO_PINMUX_1	BIT_OFFSET12	MUX_ALT0	&pconf_bias_disabled_ds2_1v8
			HSIO_ENET_RXD_1		HSIO_PINMUX_1	BIT_OFFSET10	MUX_ALT0	&pconf_bias_disabled_ds2_1v8
			HSIO_ENET_RXD_2		HSIO_PINMUX_1	BIT_OFFSET8	MUX_ALT0	&pconf_bias_disabled_ds2_1v8
			HSIO_ENET_RXD_3		HSIO_PINMUX_1	BIT_OFFSET6	MUX_ALT0	&pconf_bias_disabled_ds2_1v8
			HSIO_ENET_RXDV		HSIO_PINMUX_1	BIT_OFFSET4	MUX_ALT0	&pconf_bias_disabled_ds2_1v8
			HSIO_ENET_PHY_CLK	HSIO_PINMUX_1	BIT_OFFSET2	MUX_ALT0	&pconf_bias_disabled_ds2_1v8
		>;
	};
```

**pinctrl_enet: enetgrp**

- **pinctrl_enet** 是一个引脚控制配置组， enetgrp 是该配置组的名称。这个组主要用于定义与以太网（ Ethernet）相关的引脚设置。
- **horizon,pins** = < ... >：
  - **引脚名称**:（例如 HSIO_ENET_MDC、 HSIO_ENET_MDIO、 HSIO_ENET_TXD_0 等）。
  - **复用组**:（例如 HSIO_PINMUX_1 ）表示引脚复用的硬件资源组，决定该引脚的具体用途。
  - **位偏移**:（例如 BIT_OFFSET30 ）引脚控制寄存器的偏移值，指定引脚在寄存器中的位置。
  - **复用模式**:（如 MUX_ALT0 ）指引脚的复用模式，例如使用 MUX_ALT0 来表示该引脚的特定功能。
  - **配置参数引用**:（例如 &pconf_bias_disabled_ds2_1v8 ） 引脚的电气特性配置，通过引用其他的配置结构来设置，比如禁用偏置电流、设定驱动能力等。

这里主要详细介绍电压域在设备树中的解析，`pconf_bias_disabled_ds2_1v8）` 详细信息如下：

```shell
	pconf_bias_disabled_ds2_1v8: pconf-bias-disabled-ds2-1v8 {
		bias-disable;
		power-source = <HORIZON_IO_PAD_VOLTAGE_1V8>;
		drive-strength = <2>;
	};
```

- **pconf_bias_disabled_ds2_1v8**：这是设备树中的一个节点名称，用于定义与某些硬件引脚相关的电源配置和驱动特性。这个名称可能是一个通用配置，用于多个引脚或设备模块的电气特性设置。
- **bias-disable**：此属性表示禁用该引脚的电气偏置（ bias）。在许多硬件设计中，某些引脚可能需要使用偏置（例如上拉电阻或下拉电阻），用于确保引脚处于稳定状态。禁用偏置意味着该引脚不会使用上拉或下拉电阻，而是保持无偏置的状态，通常用于需要高阻抗或浮空状态的情况。
- **power-source**：此属性指定了该引脚的电源电压。 HORIZON_IO_PAD_VOLTAGE_1V8 表示该引脚的电源电压为 1.8V，
- **drive-strength**：此属性定义了引脚的驱动强度，表示引脚能够提供的电流大小或驱动能力。

<font color=red> 备注：</font>
电气属性的具体配置应参考 **《X5 PIN SW Reg.xlsx》** 文档

| RW | [30]    | hsio_enet_rxd_3_pu | 0x0 | hsio_enet_rxd_3   pull up enable:      1: pull up enable;      0: pull up disable;                                                     |
|----|---------|--------------------|-----|----------------------------------------------------------------------------------------------------------------------------------------|
| RW | [29]    | hsio_enet_rxd_3_pd | 0x0 | hsio_enet_rxd_3   pull down enable:      1: pull down enable;      0: pull down disable;                                               |
| RW | [28:25] | hsio_enet_rxd_3_ds | 0x8 | hsio_enet_rxd_3   driving selector;                                                                                                    |
| RW | [24]    | hsio_enet_rxd_3_st | 0x0 | hsio_enet_rxd_3;   Schmitt trigger enable.       1: enables Schmitt trigger input function;      0: no Schmitt trigger input function; |

该手册提供了不同 GPIO 的硬件寄存器位配置的详细信息。每一行代表寄存器中的一个特定位或一组位，并包含以下信息：

- 访问类型 (RW/RSV)：表示该位是可读写的（ Read/Write）。
- 位宽或位范围 [ 起始 : 结束 ]: 指明了该配置项在寄存器中的具体位置。例如，[6] 表示第 6 位，[4:1] 表示从第 4 位到第 1 位的一组位。
- 配置项名称 : 描述了该位或位组的功能，如 hsio_enet_rx_clk_pu 表示以太网接收时钟线上拉使能。
- 默认值 (Default Value): 显示了在寄存器复位或初始化时该位或位组的值
- 提供了关于该位或位组功能的详细描述，包括它们如何影响硬件的行为，例如，上拉或下拉使能，施密特触发，电流驱动能力等。

#### 驱动调用示例代码

和 pinctrl 调用方法一致，驱动先通过 pinctrl-names 查找对应的 pinctrl state，然后再切换到对应的 state，具体调用流程可看 **功能原理** 章节。

```c
static int hobot_pinctrl_probe(struct platform_device *pdev)
{
    ...
    g_xxx_dev->pinctrl = devm_pinctrl_get(&pdev->dev);
    if (IS_ERR(g_xxx_dev->pinctrl)) {
        dev_warn(&pdev->dev, "pinctrl get none\n");
        g_xxx_dev->pins_voltage = NULL;
    }
    ...
        /* 按照 pinctrl-names lookup state */
        g_xxx_dev->pins_voltage = pinctrl_lookup_state(g_xxx_dev->pinctrl,
                "xxx_voltage_func");
    if (IS_ERR(g_xxx_dev->pins_voltage)) {
        dev_info(&pdev->dev, "xxx_voltage_func get error %ld\n",
                PTR_ERR(g_xxx_dev->pins_voltage));
        g_xxx_dev->pins_voltage = NULL;
    }
    ...
        /* select state */
        if (g_xxx_dev->pins_voltage) {
            ret = pinctrl_select_state(g_xxx_dev->pinctrl, g_xxx_dev->pins_voltage);
            if (ret) {
                dev_info(&pdev->dev, "xxx_voltage_func set error %d\n", ret);
            }
        }
    ...
}
```
## 功能使用

### User Space

根据 **《X5 PIN SW Reg.xlsx》** 文档，举例将 HSIO_ENET_RX_CLK（即 HSIO_GPIO0_PIN08 ）配置为输出 GPIO 的步骤

**1. 设置 HSIO_GPIO0_PIN08 为输出模式：**

- 读取寄存器 GPIO_SWPORTA_DDR（地址 0x35060004 ）的值
- 将寄存器 GPIO_SWPORTA_DDR 的 bit8 设置为 1 ，以将引脚配置为输出模式。

```shell
devmem 0x35060004  # 读取寄存器 0x35060004
devmem 0x35060004 32 0xxxxxxxxx  # 写寄存器 0x35060004 ，将 bit8 设置为 0x1
```

**2. 设置 HSIO_ENET_RX_CLK 输出电平：**

- 读取寄存器 GPIO_SWPORTA_DR（地址 0x35060000 ）的值。
- 将寄存器 GPIO_SWPORTA_DR 的 bit8 设置为 1 ，将输出电平设置为 1 ；或者将其设置为 0 ，将输出电平设置为 0 。

```shell
devmem 0x35060000  # 读取寄存器 0x35060000
devmem 0x35060000 32 0xxxxxxxxx  # 写寄存器 0x35060000 ，将 bit8 设置为所需电平（ 0 或 1 ）
```

**3. 设置 HSIO_ENET_RX_CLK 输出电平：**

- 读取寄存器 hsio_pin_ctrl_1 （地址 0x35050018 ）的值。
- 将寄存器 hsio_pin_ctrl_1 的 bit[4:1] 设置为 0x2 ，以设置驱动强度为 7.5mA/10.6mA（具体驱动强度与硬件文档中的映射有关）。

```shell
devmem 0x35050018  # 读取寄存器 0x35050018
devmem 0x35050018 32 0xxxxxxxxx  # 写寄存器 0x35050018 ，将 bit[4:1] 设置为 0x2
```

**4. 设置引脚为 GPIO 功能模式：：**

- 读取寄存器 hsio_pinmux_ctl_1 （地址 0x3505005c）的值。
- 将寄存器 hsio_pinmux_ctl_1 的 bit[15:14] 设置为 0x2 ，以将引脚设置为 GPIO 功能模式。

```shell
devmem 0x3505005c  # 读取寄存器 0x3505005c
devmem 0x3505005c 32 0xxxxxxxxx  # 写寄存器 0x3505005c，将 bit[15:14] 设置为 0x2
```

### <span id="IO_domain_uboot"/>Uboot Space

Uboot 内已实现了 Pinctrl 驱动，使用方法与内核一致，在设备树内进行配置和使用，这里主要示例如何在 Uboot 下修改设备树和使用命令查看 IO-Domain 寄存器。

#### 代码位置

```bash
/uboot/arch/arm/dts/pinmux-func.dtsi  # uboot 设备树 pinctrl 复用关系位置
/uboot/arch/arm/dts/x5.dtsi  # uboot 设备树引用节点位置
```

#### IO-Domain 的 DTS

配置选项为自定义的“ power-source”宏，如下所示。
```dts
...
	pconf_drv_pu_3v3_max: pconf-dev-pu-max-high {
		bias-pull-up;
		power-source = <HORIZON_IO_PAD_VOLTAGE_3V3>;
		drive-strength = <15>;
	};

	pconf_drv_pu_1v8_max: pconf-dev-pu-max-low {
		bias-pull-up;
		power-source = <HORIZON_IO_PAD_VOLTAGE_1V8>;
		drive-strength = <15>;
	};
...

	pinctrl_uart7: uart7grp {
		horizon,pins = <
			LSIO_UART7_RX	LSIO_PINMUX_3	BIT_OFFSET4		MUX_ALT0 &pconf_drv_pu_1v8_max
			LSIO_UART7_TX	LSIO_PINMUX_3	BIT_OFFSET6		MUX_ALT0 &pconf_drv_pu_1v8_max
			LSIO_UART7_CTS	LSIO_PINMUX_3	BIT_OFFSET8		MUX_ALT0 &pconf_drv_pu_1v8_max
			LSIO_UART7_RTS	LSIO_PINMUX_3	BIT_OFFSET10	MUX_ALT0 &pconf_drv_pu_1v8_max
		>;
	};
...
```


### 驱动调用时 DTS 配置

首先，在 DTS 内定义当前硬件的电压域配置，以配置 i2c 为 1.8V 为例：

```dts
	pinctrl_i2c1: i2c1grp {
		horizon,pins = <
			LSIO_I2C1_SCL  LSIO_PINMUX_2 BIT_OFFSET20  MUX_ALT0 &pconf_drv_pu_1v8_max
			LSIO_I2C1_SDA  LSIO_PINMUX_2 BIT_OFFSET22  MUX_ALT0 &pconf_drv_pu_1v8_max
		>;
	};

```

 UBOOT 中设备树与内核中设备树基本一致， pinctrl 是用于配置引脚复用和电气特性的机制。在设备树中， pinctrl 节点指定了与特定硬件外设相关的引脚配置。在这里， pinctrl_i2c1 节点定义了与 I2C1 外设相关的引脚配置。

- **horizon,pins**：这个属性定义了与 I2C1 相关的引脚配置。这里的每个项都是一个特定引脚的设置。
- **LSIO_I2C1_SCL**：代表 I2C1 的时钟引脚（ SCL）。
- **LSIO_I2C1_SDA**：代表 I2C1 的数据引脚（ SDA）。
- **LSIO_PINMUX_2**：指定了一个特定的引脚复用组，用来配置这些引脚的功能。
- **BIT_OFFSET20** 和 **BIT_OFFSET22**：这些指定了具体引脚在寄存器中的位置偏移。
- **MUX_ALT0**：设置引脚的复用功能为 I2C，即将引脚配置为 I2C 通信。
- **&pconf_drv_pu_1v8_max**：引用外部的配置节点，通常定义了与引脚相关的电气特性，比如上拉电阻和驱动电压。


和 pinctrl 的使用方法类似，驱动在自己的 DTS 中引用需要配置的 IO-Domain ，以 i2c1 为例，配置如下：

```shell
	i2c1: i2c@340c0000 {
	    compatible = "snps,designware-i2c";
		status = "okay";
		reg = <0x340c0000 0x10000>;
		#address-cells = <1>;
		#size-cells = <0>;
		#clock-names = "i2c-clk";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_i2c1>;
	};

```

这个节点描述了硬件外设 I2C1 本身，提供了外设的基地址、配置和状态信息 :

- **i2c@340c0000**：节点的名称，标识了 I2C1 外设在系统中的位置。地址 0x340c0000 是该外设在内存中的起始地址。
- **compatible = "snps,designware-i2c"**：这个属性指定了与该外设兼容的驱动程序。 snps,designware-i2c 是一个常见的 I2C 控制器 IP 核的兼容标识，表示这个 I2C 外设使用 Synopsys 的设计。
- **status = "okay"**：启用该外设的状态。"okay" 表示启用该 I2C 外设， U-Boot 会为此外设加载相应的驱动程序。
- **reg = <0x340c0000 0x10000>**：指定该外设的物理地址范围。 0x340c0000 是外设的起始地址， 0x10000 是外设的地址范围（ 16KB）。
- **#address-cells = <1>** 和 **#size-cells = <0>**：这两个属性定义了地址和大小单元的数量。#address-cells = <1> 表示外设的地址由一个单元（ 32 位）表示，#size-cells = <0> 表示没有额外的大小信息。
- **#clock-names = "i2c-clk"**：指定与该外设相关的时钟名称，通常用来引用系统时钟。
- **pinctrl-names = "default"**：指定使用默认的引脚控制配置。
- **pinctrl-0 = <&pinctrl_i2c1>**：将前面定义的 pinctrl_i2c1 引脚配置应用于此 I2C 外设，确保该外设的引脚配置为 I2C1 引脚。

### <span id="use-by-io-domain"/> 命令查询与使用方式

进入 `UBOOT` 时输入 `I2C BUS` 命令来查询 uboot 设备树中添加修改的 I2C1 配置是否生效。

```uboot
Hobot>i2c bus
Bus 0:  i2c@340d0000
Bus 1:  i2c@340c0000

```

可以发现在 Uboot 中除了默认的 I2C2 总线外，额外探测到 I2C1 总线，地址为 `0X340C 0000` ，说明 Uboot 中设备树的配置已生效，继续输入 `MD` 命令来查询电压域的寄存器 。

```uboot
Hobot>md 0x34180080 1
34180080: 50555555
```

通过查询 Pin_Mux_List 手册， I2C1 的属性如下，通过 `MD` 输出结果对应电压域的偏移位 bit = 1 ，电压域为 1.8V ，与 Uboot 中设备树配置的电压域一致。

|     PinName             |     Default IO Domain    |     IO Domain Reg    |     Reg Offset    |     Reg Value    |
|-------------------------|--------------------------|----------------------|-------------------|------------------|
|     LSIO_I2C1_SCL       |     3.3v                 |     0x34180080       |     21:20         |     1: 1.8V      |
|     LSIO_I2C1_SDA       |     3.3v                 |     0x34180080       |     23:22         |     0: 3.3V      |


<font color=red> 注意：</font>
- 以上 I2C 电源域修改仅为示例，实际电压域修改需要和实际硬件需求来修改对应的 dts 配置

## FAQ

**1、为什么** pinmux-func.dtsi 中 pinctrl_sd 和 pinctrl_sdio 中的电压域使用的配置为 `pconf_sd_sdio_pu_ds3_ipctrl` , 没有明确指明电压域的具体电压

```shell
	pconf_sd_sdio_pu_ds3_ipctrl: pconf-sd-sdio-pu-ds3-ipctrl {
		bias-pull-up;
		drive-strength = <3>;
		power-source = <HORIZON_IO_PAD_VOLTAGE_IP_CTRL>;
	};
```

**答**：在 pinmux-func.dtsi 中，只有 pinctrl_sd 和 pinctrl_sdio 节点使用了相关的电压域配置，`HORIZON_IO_PAD_VOLTAGE_IP_CTRL` 代表此模块的电压域由硬件中配置引脚的电压而改变，通常主控在启动时首先以 3.3V 电压与 SDIO 设备进行通信， SDIO 设备会通过主控发出的命令确认其支持的电压，并决定是否切换到 1.8V 电压。

**2、SDIO 设备的电压域是如何进行切换的**

```shell
&sdio_0 {
	status = "okay";
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_sd &hsio_gpio0_26 &hsio_gpio1_1>;
	power-gpios = <&hs_gpio0_porta 26 GPIO_ACTIVE_HIGH>;
	/* Drive voltage gpio to 0 to switch to 1.8V */
	voltage-gpios = <&hs_gpio1_porta 1 GPIO_ACTIVE_LOW>;
};
```
![HSIO_CTRL_DOMAIN](_static/_images/12-IO-DOMAIN_Debug_Guide_zh_CN/sd_vdd.png)
![HSIO_CTRL_DOMAIN](_static/_images/12-IO-DOMAIN_Debug_Guide_zh_CN/sd_vdd1.png)

**答**：在 硬件原理图中 HSIO_SD_XX 引脚相关的电压域由 VDDIO_SD 提供，那么如何实现 3v3 和 1v8 电压间的切换呢，可以发现在设备树中定义了 `power-gpios` 和 `voltage-gpios` ，其配置中的引脚都被复用为 GPIO 功能，当此设备数的节点被内核 SDIO 控制器解析时，`hsio_gpio0_26` 用来控制 SD 卡 VDD 的电压开关，当引脚为高时开关打开 3.3V ，`hsio_gpio1_1` 默认状态的电压域为 3.3v，当引脚被拉低时硬件设计切换为 1.8v。这里列出手册中部分相关参数分别为 **引脚名** ，**默认电压** ，**电源域偏移位** ，当第一位 bit 为 1 则代表 1.8v，为 0 则代表 3.3v。

|  引脚名      | 电压域默认值 | 偏移位 0:1.8v 1:3.3v |
|--------------------|------|---|
|  HSIO_SD_SDCLK     | 3.3V | 1 |
|  HSIO_SD_CMD       | 3.3V | 1 |
|  HSIO_SD_CDN       | 3.3V | 1 |
|  HSIO_SD_DATA_0    | 3.3V | 1 |
|  HSIO_SD_DATA_1    | 3.3V | 1 |
|  HSIO_SD_DATA_2    | 3.3V | 1 |
|  HSIO_SD_DATA_3    | 3.3V | 1 |
|  HSIO_SDIO_WP      | 3.3V | 1 |
|  HSIO_SDIO_SDCLK   | 3.3V | 1 |
|  HSIO_SDIO_CMD     | 3.3V | 1 |
|  HSIO_SDIO_CDN     | 3.3V | 1 |
|  HSIO_SDIO_DATA_0  | 3.3V | 1 |
|  HSIO_SDIO_DATA_1  | 3.3V | 1 |
|  HSIO_SDIO_DATA_2  | 3.3V | 1 |
|  HSIO_SDIO_DATA_3  | 3.3V | 1 |

在内核驱动中 `horizon_pin_power_source` 添加打印，内核日志如下

```c
[    2.236243] horizon-hsio-pinctrl 35050000.hsio_iomuxc: Read value from register: 0xd
[    2.248841] horizon-hsio-pinctrl 35050000.hsio_iomuxc: ms_bits_offset: 0x2
[    2.261723] horizon-hsio-pinctrl 35050000.hsio_iomuxc: flags: 0x2
[    2.277303] horizon-hsio-pinctrl 35050000.hsio_iomuxc: Read value from register: 0xf

[    3.915439] horizon-hsio-pinctrl 35050000.hsio_iomuxc: Modified value for register: 0xf
[    3.930253] horizon-hsio-pinctrl 35050000.hsio_iomuxc: ms_bits_offset: 0x2
[    3.930258] horizon-hsio-pinctrl 35050000.hsio_iomuxc: flags: 0x2
[    3.930262] horizon-hsio-pinctrl 35050000.hsio_iomuxc: Read value from register: 0xf
```

- **Read value from register**: 代表读当前电压域地址的值。
- **ms_bits_offset**：代表电压域地址的偏移。
- **flags**：代表设备树中电压域使用的是 power-source = <HORIZON_IO_PAD_VOLTAGE_IP_CTRL>。
  - HORIZON_IO_PAD_VOLTAGE_3V3 ： 0
  - HORIZON_IO_PAD_VOLTAGE_1V8 ： 1
  - HORIZON_IO_PAD_CTRL_VOLTAGE_1V8 ： 3
  - HORIZON_IO_PAD_CTRL_VOLTAGE_3V3 ： 4
- **Modified value for register**: 代表位操作后电压域地址的值。

通过 dmesg 打印信息可以发现 HSIO_SD_XX 引脚的电压域由 3.3v 切换至了 1.8v。