DeviceDriver(二):Pinctrl子系统

一:Pinctrl子系统

大多数SOC的pin配置都很繁琐,复用、上拉、下拉、速度、驱动能力等等,对此Linux内核针对PIN的配置引入了pinctrl子系统。

pinctrl子系统主要功能:

1、获取设备树中pin信息。

2、根据获取到的pin信息来设置pin的复用功能。

3、根据获取到的pin信息来设置pin的电气特性,比如上/下拉,速度,驱动能力等。

二:PIN配置信息

在pinctrl子系统中,PIN的配置信息会被写入设备树公用文件“imx6ull.dtsi”中,对应的节点名为“iomuxc”

iomuxc: iomuxc@020e0000 {
	compatible = "fsl,imx6ul-iomuxc";
	reg = <0x020e0000 0x4000>;
};

在特定目标板设备树文件“imx6ull-ull-my-emmc.dts”下会对“iomuxc”节点进行信息补充:

&iomuxc {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_hog_1>;
	imx6ul-evk {
		pinctrl_hog_1: hoggrp-1 {
			fsl,pins = <
				MX6UL_PAD_UART1_RTS_B__GPIO1_IO19	0x17059 /* SD1 CD */
				MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT	0x17059 /* SD1 VSELECT */
				MX6UL_PAD_GPIO1_IO09__GPIO1_IO09        0x17059 /* SD1 RESET */
			>;
		};

		pinctrl_csi1: csi1grp {
			fsl,pins = <
				MX6UL_PAD_CSI_MCLK__CSI_MCLK		0x1b088
				MX6UL_PAD_CSI_PIXCLK__CSI_PIXCLK	0x1b088
				MX6UL_PAD_CSI_VSYNC__CSI_VSYNC		0x1b088

			>;
		};
... ...
}

以“MX6UL_PAD_UART1_RTS_B__GPIO1_IO19    0x17059”为例分析每个部分的作用。

MX6UL_PAD_UART1_RTS_B__GPIO1_IO19:是一个宏定义,展开为

“#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19                         0x0090 0x031C 0x0000 0x5 0x0”

(1)0x0090:mux_reg寄存器偏移地址,表示“IOMUXC_SW_MUX_CTL_PAD_UART1_RTS_B”寄存器地址为0x020e0090=0x020e0000+0x0090;

(2)0x031C:conf_reg寄存器偏移地址,表示“IOMUXC_SW_PAD_CTL_PAD_UART1_RTS_B”寄存器地址为0x020e031C=0x020e0000+0x031C;

(3)0x0000:input_reg寄存器偏移地址。该PIN用作UART1_RTS_B时不需要input_reg寄存器,所以为0;

(4)0x5:mux_reg寄存器的值,相当于该PIN复用为GPIO1_IO19;

(5)0x0:input_reg寄存器的值。

(6)0x17059:conf_reg寄存器的值,作用为设置IO的上/下拉,速度,驱动能力等。

三:PIN驱动程序

根据节点“iomuxc”的“compatible”属性查找引用文件:

static struct of_device_id imx6ul_pinctrl_of_match[] = {
	{ .compatible = "fsl,imx6ul-iomuxc", .data = &imx6ul_pinctrl_info, },
	{ .compatible = "fsl,imx6ull-iomuxc-snvs", .data = &imx6ull_snvs_pinctrl_info, },
	{ /* sentinel */ }
};

static int imx6ul_pinctrl_probe(struct platform_device *pdev)
{
	const struct of_device_id *match;
	struct imx_pinctrl_soc_info *pinctrl_info;

	match = of_match_device(imx6ul_pinctrl_of_match, &pdev->dev);

	if (!match)
		return -ENODEV;

	pinctrl_info = (struct imx_pinctrl_soc_info *) match->data;

	return imx_pinctrl_probe(pdev, pinctrl_info);
}

static struct platform_driver imx6ul_pinctrl_driver = {
	.driver = {
		.name = "imx6ul-pinctrl",
		.owner = THIS_MODULE,
		.of_match_table = of_match_ptr(imx6ul_pinctrl_of_match),
	},
	.probe = imx6ul_pinctrl_probe,
	.remove = imx_pinctrl_remove,
};

static int __init imx6ul_pinctrl_init(void)
{
	return platform_driver_register(&imx6ul_pinctrl_driver);
}

pinctrl子系统挂在platform驱动框架上,当设备和驱动匹配后则会调用probe函数,即imx6ul_pinctrl_probe函数为PIN配置的入口函数。

-->>imx6ul_pinctrl_probe

     -->>imx_pinctrl_probe

         -->>imx_pinctrl_probe_dt

              -->>imx_pinctrl_parse_functions

                   -->>imx_pinctrl_parse_groups

     -->>pinctrl_register

1、imx_pinctrl_parse_groups函数的作用为解析设备树PIN的配置信息

static int imx_pinctrl_parse_groups(struct device_node *np,
				    struct imx_pin_group *grp,
				    struct imx_pinctrl_soc_info *info,
				    u32 index)
{
... ...
	for (i = 0; i < grp->npins; i++) {
		u32 mux_reg = be32_to_cpu(*list++);
		u32 conf_reg;
		unsigned int pin_id;
		struct imx_pin_reg *pin_reg;
		struct imx_pin *pin = &grp->pins[i];
... ...
		pin_id = (mux_reg != -1) ? mux_reg / 4 : conf_reg / 4;
		pin_reg = &info->pin_regs[pin_id];
		pin->pin = pin_id;
		grp->pin_ids[i] = pin_id;
		pin_reg->mux_reg = mux_reg;
		pin_reg->conf_reg = conf_reg;
		pin->input_reg = be32_to_cpu(*list++);
		pin->mux_mode = be32_to_cpu(*list++);
		pin->input_val = be32_to_cpu(*list++);

		/* SION bit is in mux register */
		config = be32_to_cpu(*list++);
		if (config & IMX_PAD_SION)
			pin->mux_mode |= IOMUXC_CONFIG_SION;
		pin->config = config & ~IMX_PAD_SION;

		dev_dbg(info->dev, "%s: 0x%x 0x%08lx", info->pins[pin_id].name,
				pin->mux_mode, pin->config);
	}
... ...
}

2、pinctrl_register函数的作用为向LInux内核注册一个PIN控制器。

struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,
				    struct device *dev, void *driver_data)

struct pinctrl_desc {
	const char *name;
	struct pinctrl_pin_desc const *pins;
	unsigned int npins;
	const struct pinctrl_ops *pctlops;
	const struct pinmux_ops *pmxops;
	const struct pinconf_ops *confops;
	struct module *owner;
#ifdef CONFIG_GENERIC_PINCONF
	unsigned int num_custom_params;
	const struct pinconf_generic_params *custom_params;
	const struct pin_config_item *custom_conf_items;
#endif
};

其中传入的pinctrl_desc结构体中三个ops结构体包含了许多操作函数,通过这些函数就可以完成一个PIN的配置,一般由半导体厂商提供。

int imx_pinctrl_probe(struct platform_device *pdev,
		      struct imx_pinctrl_soc_info *info)
{
    struct pinctrl_desc *imx_pinctrl_desc;
... ...
	imx_pinctrl_desc->pctlops = &imx_pctrl_ops;
	imx_pinctrl_desc->pmxops = &imx_pmx_ops;
	imx_pinctrl_desc->confops = &imx_pinconf_ops;

}

static const struct pinctrl_ops imx_pctrl_ops = {
	.get_groups_count = imx_get_groups_count,
	.get_group_name = imx_get_group_name,
	.get_group_pins = imx_get_group_pins,
	.pin_dbg_show = imx_pin_dbg_show,
	.dt_node_to_map = imx_dt_node_to_map,
	.dt_free_map = imx_dt_free_map,

};

static const struct pinmux_ops imx_pmx_ops = {
	.get_functions_count = imx_pmx_get_funcs_count,
	.get_function_name = imx_pmx_get_func_name,
	.get_function_groups = imx_pmx_get_groups,
	.set_mux = imx_pmx_set,
	.gpio_request_enable = imx_pmx_gpio_request_enable,
	.gpio_set_direction = imx_pmx_gpio_set_direction,
};

static const struct pinconf_ops imx_pinconf_ops = {
	.pin_config_get = imx_pinconf_get,
	.pin_config_set = imx_pinconf_set,
	.pin_config_dbg_show = imx_pinconf_dbg_show,
	.pin_config_group_dbg_show = imx_pinconf_group_dbg_show,
};

四:添加pinctrl节点

创建设备节点“pinctrl_test”,前缀必须为“pinctrl_”,属性名字必须为“fsl,pins”

pinctrl_test: testgrp {
    fsl,pins = <
        MX6UL_PAD_GPIO1_IO0x__GPIO1_IO0x     0xXXXXXX 
	>;
};

猜你喜欢

转载自blog.csdn.net/qq_34968572/article/details/103275579