分析io管脚配置和初始化流程。
文件:arch/arm64/boot/dts/freescale/fsl-imx8x-mek.dtsi
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog>;
imx8qxp-mek {
pinctrl_hog: hoggrp {
fsl,pins = <
SC_P_MCLK_OUT0_ADMA_ACM_MCLK_OUT0 0x0600004c
SC_P_COMP_CTL_GPIO_1V8_3V3_GPIORHB_PAD 0x000514a0
>;
};
};
};
文件:include/dt-bindings/pinctrl/pads-imx8qxp.h
#define SC_P_MCLK_OUT0 76 /* ADMA.ACM.MCLK_OUT0, ADMA.ESAI0.TX_HF_CLK, ADMA.LCDIF.CLK, ADMA.SPI2.SDO, LSIO.GPIO0.IO20 */
#define SC_P_MCLK_OUT0_ADMA_ACM_MCLK_OUT0 SC_P_MCLK_OUT0 0
iomux管脚复用,由两部分组成,iomux + iopad,iomux复用管脚功能,iopad复用管脚属性。
其中SC_P_MCLK_OUT0表示管脚名,ADMA_ACM_MCLK_OUT0表示管脚iomux复用功能,0x0600004c表示iopad属性功能。
iomux复用与寄存器手册一致对应, 0就是对应000b - ADMA_ACM_MCLK_OUT0 功能。
iopad属性也与寄存器一一对应,主要看输入输出设置,电平拉高拉低和驱动能力设置。默认情况为0一般是开漏输出。这里0x0600004c表示输入拉低。
pinctrl也是一个controller控制器,也是需要dts节点,也有自己的管脚需要配置:
文件:fsl-imx8dx.dtsi
iomuxc: iomuxc {
compatible = "fsl,imx8qxp-iomuxc";
};
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog>;
imx8qxp-mek {
pinctrl_hog: hoggrp {
fsl,pins = <
SC_P_MCLK_OUT0_ADMA_ACM_MCLK_OUT0 0x0600004c
SC_P_COMP_CTL_GPIO_1V8_3V3_GPIORHB_PAD 0x000514a0
>;
};
};
};
怎么没设置寄存器基地址和长度呢,IOMUXD是通过系统spi调用而不是配置寄存器方式,设置每个GPIO复用时配置对应的功能管脚寄存器:
其他的pinctrl设备配置都是pin controller的子节点,比如uart0:
pinctrl_lpuart0: lpuart0grp {
fsl,pins = <
SC_P_UART0_RX_ADMA_UART0_RX 0x06000020
SC_P_UART0_TX_ADMA_UART0_TX 0x06000020
>;
};
在pin controller node中定义pin configuration其目的是为了让client device引用,所谓 client device 其实就是使用 pin control subsystem 提供服务的那些设备,例如串口设备。
&lpuart0 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_lpuart0>;
status = "okay";
};
pinctrl-names定义了state列表,表示一组pin处于某种状态,state的定义与电源管理关联密切,大多数设备只有一种default状态,state ID从0开始编号,与pingctrl-x的编号对应:
brcmfmac: brcmfmac {
compatible = "cypress,brcmfmac";
pinctrl-names = "init", "idle", "default";
pinctrl-0 = <&pinctrl_wifi_init>;
pinctrl-1 = <&pinctrl_wifi_init>;
pinctrl-2 = <&pinctrl_wifi>;
};
&i2c1 {
#address-cells = <1>;
#size-cells = <0>;
clock-frequency = <100000>;
pinctrl-names = "default", "sleep";
pinctrl-0 = <&pinctrl_lpi2c1>;
pinctrl-1 = <&pinctrl_lpi2c1>;
pinctrl-assert-gpios = <&gpio1 1 GPIO_ACTIVE_HIGH>;
status = "okay";
};
&usdhc2 {
pinctrl-names = "default", "state_100mhz", "state_200mhz";
pinctrl-0 = <&pinctrl_usdhc2>, <&pinctrl_usdhc2_gpio>;
pinctrl-1 = <&pinctrl_usdhc2_100mhz>, <&pinctrl_usdhc2_gpio>;
pinctrl-2 = <&pinctrl_usdhc2_200mhz>, <&pinctrl_usdhc2_gpio>;
bus-width = <4>;
cd-gpios = <&gpio4 22 GPIO_ACTIVE_LOW>;
wp-gpios = <&gpio4 21 GPIO_ACTIVE_HIGH>;
vmmc-supply = <®_usdhc2_vmmc>;
max-frequency = <50000000>;
bus-width = <8>;
keep-power-in-suspend;
enable-sdio-wakeup;
non-removable;
cap-power-off-card;
status = "okay";
sd8xxx: sd8xxx-wlan@2 {
/*for ubloxwifi*/
compatible = "mrvl,sd8xxx";
reg = <2>;
/*interrupt-parent = <&gpio6>;*/
/*interrupts = <17 1>;*/
platform-quirks = <1>;
status = "okay";
};
};
在系统初始化的时候,dts 描述的 device node 会形成一个树状结构,在 machine 初始化的过程中,会 scan device node 的树状结构,将真正的硬件 device node 变成一个个的设备模型中的 device 结构(比如 struct platform_device)并加入到系统中。
文件:drivers/pinctrl/freescale/pinctrl-imx8qxp.c
static struct of_device_id imx8qxp_pinctrl_of_match[] = {
{ .compatible = "fsl,imx8qxp-iomuxc", },
{ /* sentinel */ }
};
static struct platform_driver imx8qxp_pinctrl_driver = {
.driver = {
.name = "imx8qxp-pinctrl",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(imx8qxp_pinctrl_of_match),
},
.probe = imx8qxp_pinctrl_probe,
};
static int __init imx8qxp_pinctrl_init(void)
{
return platform_driver_register(&imx8qxp_pinctrl_driver);
}
arch_initcall(imx8qxp_pinctrl_init);
arch_initcall等级为3, 调用比较靠前,等级顺序:
#define pure_initcall(fn) __define_initcall(fn, 0)
#define core_initcall(fn) __define_initcall(fn, 1)
#define core_initcall_sync(fn) __define_initcall(fn, 1s)
#define postcore_initcall(fn) __define_initcall(fn, 2)
#define postcore_initcall_sync(fn) __define_initcall(fn, 2s)
#define arch_initcall(fn) __define_initcall(fn, 3)
#define arch_initcall_sync(fn) __define_initcall(fn, 3s)
#define subsys_initcall(fn) __define_initcall(fn, 4)
#define subsys_initcall_sync(fn) __define_initcall(fn, 4s)
#define fs_initcall(fn) __define_initcall(fn, 5)
#define fs_initcall_sync(fn) __define_initcall(fn, 5s)
#define rootfs_initcall(fn) __define_initcall(fn, rootfs)
#define device_initcall(fn) __define_initcall(fn, 6)
#define device_initcall_sync(fn) __define_initcall(fn, 6s)
#define late_initcall(fn) __define_initcall(fn, 7)
#define late_initcall_sync(fn) __define_initcall(fn, 7s)
static int imx8qxp_pinctrl_probe(struct platform_device *pdev)
{
return imx_pinctrl_probe(pdev, &imx8qxp_pinctrl_info);
}
imx8qxp_pinctrl_info包含了 所以GPIO管脚pins信息,174个管脚,与手册中iomuxd Memory map对应,设置了flag标志。
static struct imx_pinctrl_soc_info imx8qxp_pinctrl_info = {
.pins = imx8qxp_pinctrl_pads,
.npins = ARRAY_SIZE(imx8qxp_pinctrl_pads),
.flags = IMX8_USE_SCU | SHARE_MUX_CONF_REG
| IMX8_ENABLE_MUX_CONFIG | IMX8_ENABLE_PAD_CONFIG,
};
static const struct pinctrl_pin_desc imx8qxp_pinctrl_pads[] = {
IMX_PINCTRL_PIN(SC_P_PCIE_CTRL0_PERST_B),
IMX_PINCTRL_PIN(SC_P_PCIE_CTRL0_CLKREQ_B),
IMX_PINCTRL_PIN(SC_P_PCIE_CTRL0_WAKE_B),
IMX_PINCTRL_PIN(SC_P_COMP_CTL_GPIO_1V8_3V3_PCIESEP),
IMX_PINCTRL_PIN(SC_P_USB_SS3_TC0),
文件:drivers\pinctrl\freescale\pinctrl-imx.c
int imx_pinctrl_probe(struct platform_device *pdev,
struct imx_pinctrl_soc_info *info)
{
struct regmap_config config = { .name = "gpr" };
struct device_node *dev_np = pdev->dev.of_node;
struct pinctrl_desc *imx_pinctrl_desc;
struct device_node *np;
struct imx_pinctrl *ipctl;
struct resource *res;
struct regmap *gpr;
int ret, i;
if (!info || !info->pins || !info->npins) {
dev_err(&pdev->dev, "wrong pinctrl info\n");
return -EINVAL;
}
info->dev = &pdev->dev;
if (info->gpr_compatible) {
gpr = syscon_regmap_lookup_by_compatible(info->gpr_compatible);
if (!IS_ERR(gpr))
regmap_attach_dev(&pdev->dev, gpr, &config);
}
/* Create state holders etc for this driver */
ipctl = devm_kzalloc(&pdev->dev, sizeof(*ipctl), GFP_KERNEL);
if (!ipctl)
return -ENOMEM;
判断info,pins,npins是否为空,申请imx_pinctrl的内核内存,使用devm_kzalloc方式,内存交给设备自己管理,注销的时候会释放内存,避免的内存使用成对和各种异常退出的情况。
由于IMX8QXP设置了IMX8_USE_SCU,所以不用寄存器直接编程的方式而是使用系统控制api调用方式。imx8qm也是这种方式,其他是寄存器直接寄存器编程方式:
if (!(info->flags & IMX8_USE_SCU)) {
info->pin_regs = devm_kmalloc(&pdev->dev, sizeof(*info->pin_regs) *
info->npins, GFP_KERNEL);
if (!info->pin_regs)
return -ENOMEM;
for (i = 0; i < info->npins; i++) {
info->pin_regs[i].mux_reg = -1;
info->pin_regs[i].conf_reg = -1;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ipctl->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(ipctl->base))
return PTR_ERR(ipctl->base);
if (of_property_read_bool(dev_np, "fsl,input-sel")) {
np = of_parse_phandle(dev_np, "fsl,input-sel", 0);
if (!np) {
dev_err(&pdev->dev, "iomuxc fsl,input-sel property not found\n");
return -EINVAL;
}
ipctl->input_sel_base = of_iomap(np, 0);
of_node_put(np);
if (!ipctl->input_sel_base) {
dev_err(&pdev->dev,
"iomuxc input select base address not found\n");
return -ENOMEM;
}
}
}
申请pinctrl_desc的内核内存,初始化imx_pinctrl_desc结构体成员变量。
imx_pinctrl_desc = devm_kzalloc(&pdev->dev, sizeof(*imx_pinctrl_desc),
GFP_KERNEL);
if (!imx_pinctrl_desc)
return -ENOMEM;
imx_pinctrl_desc->name = dev_name(&pdev->dev);
imx_pinctrl_desc->pins = info->pins;
imx_pinctrl_desc->npins = info->npins;
imx_pinctrl_desc->pctlops = &imx_pctrl_ops;
imx_pinctrl_desc->pmxops = &imx_pmx_ops;
imx_pinctrl_desc->confops = &imx_pinconf_ops;
imx_pinctrl_desc->owner = THIS_MODULE;
/* for generic pinconf */
imx_pinctrl_desc->custom_params = info->custom_params;
imx_pinctrl_desc->num_custom_params = info->num_custom_params;
/* platform specific callback */
imx_pmx_ops.gpio_set_direction = info->gpio_set_direction;
初始化info的锁,ipctl与info通过指针关联起来,
mutex_init(&info->mutex);
ipctl->info = info;
ipctl->dev = info->dev;
platform_set_drvdata(pdev, ipctl);
ret = devm_pinctrl_register_and_init(&pdev->dev,
imx_pinctrl_desc, ipctl,
&ipctl->pctl);
if (ret) {
dev_err(&pdev->dev, "could not register IMX pinctrl driver\n");
goto free;
}
注册CPU下面的所有pins管脚:
drivers\pinctrl\freescale\pinctrl-imx.c
devm_pinctrl_register_and_init(&pdev->dev, imx_pinctrl_desc, ipctl, &ipctl->pctl);
drivers\pinctrl\core.c
pinctrl_register_and_init(pctldesc, dev, driver_data, pctldev);
pinctrl_init_controller(pctldesc, dev, driver_data);
pinctrl_register_pins(pctldev, pctldesc->pins, pctldesc->npins);
pinctrl_register_one_pin(pctldev, &pins[i]);
radix_tree_insert(&pctldev->pin_desc_tree, pin->number, pindesc);
注册和初始化dts pins管脚
ret = imx_pinctrl_probe_dt(pdev, ipctl);
if (ret) {
dev_err(&pdev->dev, "fail to probe dt properties\n");
goto free;
}
dev_info(&pdev->dev, "initialized IMX pinctrl driver\n");
return pinctrl_enable(ipctl->pctl);
这里就只初始化iomux里面子节点的pins,然后通过函数parse_dt_cfg读取配置val进行配置的设置。
drivers\pinctrl\freescale\pinctrl-imx.c
imx_pinctrl_probe_dt(pdev, ipctl);
imx_pinctrl_dt_is_flat_functions 判断dts是否没有子节点是平行设置,显然是很多子节点管理的
radix_tree_insert(&pctl->pin_function_tree, i, function); 注册
i = 0;
for_each_child_of_node(np, child)
imx_pinctrl_parse_functions(child, ipctl, i++);
pinmux_generic_get_function(pctl, index);
radix_tree_insert(&pctl->pin_group_tree,
info->group_index++, grp);
imx_pinctrl_parse_groups(child, grp, ipctl, i++);
imx_pinconf_parse_generic_config(np, ipctl);
imx_pinctrl_parse_pin_scu(info, &grp->pins[i],
pin, list_p, config);
到这里iomux的管脚就初始化完成了。