IO管脚配置和pinctrl驱动

        分析io管脚配置和初始化流程。

IOMUX

        文件: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 功能。

IO管脚配置和pinctrl驱动_第1张图片

        iopad属性也与寄存器一一对应,主要看输入输出设置,电平拉高拉低和驱动能力设置。默认情况为0一般是开漏输出。这里0x0600004c表示输入拉低。

IO管脚配置和pinctrl驱动_第2张图片

IO管脚配置和pinctrl驱动_第3张图片

PINCTRL

pin controller dts

        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复用时配置对应的功能管脚寄存器:

IO管脚配置和pinctrl驱动_第4张图片

         其他的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";
	};
};

pin controller 驱动

        在系统初始化的时候,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的管脚就初始化完成了。

你可能感兴趣的:(NXP/TI,IMX8)