专栏目录:专栏目录传送门
平台 | 内核 |
---|---|
i.MX8MP | 5.15.71 |
1. pinfunc.h
pinfunc.h中定义了所有的引脚,命名方式是MX8MP_IOMUXC___,例如下面的MX8MP_IOMUXC_GPIO1_IO00__GPIO1_IO00定义了MUX寄存器偏移,PAD配置寄存器偏移,输入选择寄存器偏移,MUX模式,输入寄存器的值。如果是输出引脚,那么输入选择寄存器偏移就为0。
我们在dts中设置的0x184值是设置PAD的电气属性。
pinctrl_gpio1: gpio1grp {
fsl,pins = <
MX8MP_IOMUXC_GPIO1_IO00__GPIO1_IO00 0x184 /* SODIMM 206 */
>;
};
2.iomux驱动
uboot中的pinctl驱动通过imx_pinctrl_probe
来注册iomux功能,主要是IOMUX基地址。
int imx_pinctrl_probe(struct udevice *dev,
struct imx_pinctrl_soc_info *info)
{
struct imx_pinctrl_priv *priv = dev_get_priv(dev);
int node = dev_of_offset(dev), ret;
fdt_addr_t addr;
fdt_size_t size;
priv->dev = dev;
priv->info = info;
addr = devfdt_get_addr_size_index(dev, 0, &size);
info->base = map_sysmem(addr, size);
priv->info = info;
return 0;
}
驱动在device_probe
的时候通过pinctrl_select_state来读取dtb中的pinctl信息,并配置gpio。
device_probe
->pinctrl_select_state
->pinctrl_select_state_full
->pinctrl_config_one
->ops->set_state
->imx_pinctrl_set_state
最后会调用各vendor定义的set_state函数,在这里是imx_pinctrl_set_state
。下面我们就来看看pinctrl_select_state_full
是如何一步步配置gpio的。
3.pinctrl_select_state_full
在了解软件流程之前,我们先以LED为例看看dts中的定义:
gpio-leds {
compatible = "gpio-leds";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpio_led>;
status {
label = "yellow:status";
gpios = <&gpio3 16 GPIO_ACTIVE_HIGH>;
default-state = "on";
};
};
&iomuxc {
pinctrl-names = "default";
....
pinctrl_gpio_led: gpioledgrp {
fsl,pins = <
MX8MP_IOMUXC_NAND_READY_B__GPIO3_IO16 0x19
>;
};
....
}
pinctrl-0
使用的iomux配置是pinctrl_gpio_led
,这个pinctrl_gpio_led
定义在iomuxc节点内,fsl,pins
定义了需要MUX的引脚列表。PAD电气特性设置为了0x19。
dev_read_stringlist_search
会在这个设备的dts节点下搜索名为statename的"pinctrl-names"
,一般这个statename为“default”。如果是“default”,这里的dev_read_prop
使用的propname就是“pinctrl-0”
。根据这个name找到pinctrl_gpio_led
,然后uclass_get_device_by_phandle_id
找到iomuxc节点下的pinctrl_gpio_led
。
最后pinctrl_config_one
中的set_state根据pinctrl_gpio_led
中的配置内容,配置IOMUX寄存器。
static int pinctrl_select_state_full(struct udevice *dev, const char *statename)
{
state = dev_read_stringlist_search(dev, "pinctrl-names", statename);
snprintf(propname, sizeof(propname), "pinctrl-%d", state);
list = dev_read_prop(dev, propname, &size);
size /= sizeof(*list);
for (i = 0; i < size; i++) {
phandle = fdt32_to_cpu(*list++);
ret = uclass_get_device_by_phandle_id(UCLASS_PINCONFIG, phandle,
&config);
ret = pinctrl_config_one(config);
}
return 0;
}
4.imx_pinctrl_set_state
上一节最后提到set_state会配置IOMUX寄存器。对于i.MX平台这里的set_state函数是imx_pinctrl_set_state
。这一节我们重点分析寄存器设置过程。
将pinctrl_gpio_led
定义展开,我们可以看出,实际上fsl,pins
等于<0x014 0x274 0x000 0x0 0x0 0x184>
。
pinctrl_gpio1: gpio1grp {
fsl,pins = <
//MX8MP_IOMUXC_GPIO1_IO00__GPIO1_IO00 0x184
<mux_reg conf_reg input_reg mux_mode input_val pad_conf_val>
0x014 0x274 0x000 0x0 0x0 0x184
>;
};
一起来看看下面的imx_pinctrl_set_state
是如何解析这些定义并设置寄存器的。
-
设置一个gpio配置占用的大小,如果是SCU,那么pin_size为12,如果是共享MUX和配置寄存器的话,一个gpio占用的pin_size为20,正常的gpio占用pin_size为24。这里的占用大小是指
fsl,pins
中一个gpio配置占用的大小,一个数值占用4比特。if (info->flags & IMX8_USE_SCU) pin_size = SHARE_IMX8_PIN_SIZE; else if (info->flags & SHARE_MUX_CONF_REG) pin_size = SHARE_FSL_PIN_SIZE; else pin_size = FSL_PIN_SIZE;//一个gpio占用的大小24
-
获取
"fsl,pins"
占用的总大小,存储到size。prop = fdt_getprop(gd->fdt_blob, node, "fsl,pins", &size);
-
获取
"fsl,pins"
中的内容,存储到pin_data
中,fdtdec_get_int_array(gd->fdt_blob, node, "fsl,pins", pin_data, size >> 2)
-
获取
"fsl,pins"
中引脚的个数npins = size / pin_size;
-
遍历引脚,配置寄存器。
pin_data
中依次存储<mux_reg conf_reg input_reg mux_mode input_val pad_conf_val>
。-
依次获取
mux_reg conf_reg input_reg mux_mode input_val pad_conf_val
的数值,存储到对应名字的变量中。 -
对于输出引脚,只需要设置mux和pad即可。但是有些i.MX芯片MUX和PAD寄存器是做进了一个寄存器,这种情况有一些特殊,我们使用
clrsetbits_le32
来设置一个寄存器中相应的位来写入MUX和PAD。writel(mux_mode, info->base + mux_reg); writel(config_val,info->base + conf_reg);
-
对于输入引脚,需要配置输入选择寄存器。这里又细分为两种情况。①如果选择输入值以0xff开头,它是一个古怪的选择输入,其值应该被解释为如下。
31 23 15 7 0 | 0xff | shift | width | select |
它是用来解决某些引脚的选择输入没有在选择输入寄存器中实现,而是在一些通用寄存器中实现的问题。我们将选择输入值、宽度和位域的移位编码到设备树中引脚功能ID的input_val单元中,然后在这里对它们进行解码,以便在通用寄存器中设置选择输入位。
正常情况下,是直接写入寄存器。
writel(input_val,info->base + input_reg);
-
以上我们就梳理完了IOMUX的核心功能~