imx6ul pinctrl 驱动浅析

文章目录

  • pinctrl dev register
  • pinctrl driver register
  • probe 加载探测流程
  • pinctrl 设备的操作接口
  • 结语

本文以imx6ul 的pinctrl为例,分析pinctrl 设备驱动模块的实现与工作机制

pinctrl dev register

在设备树imx6ul.dtsi中有如下节点,通过设备树注册platform设备

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

pinctrl driver register

内核启动时,会调用pinctrl-imx6ul.c 中imx6ul_pinctrl_init 注册pinctrl 设备驱动,如下:

arch_initcall(imx6ul_pinctrl_init);

probe 加载探测流程

imx6ul_pinctrl_probe->imx_pinctrl_probe

int imx_pinctrl_probe(struct platform_device *pdev,struct imx_pinctrl_soc_info *info)
{
struct device_node *dev_np = pdev->dev.of_node;

info->dev = &pdev->dev;
/* Create state holders etc for this driver */
// 分配内存并初始化,用来保存引脚配置及复用信息
ipctl = devm_kzalloc(&pdev->dev, sizeof(*ipctl), GFP_KERNEL);
info->pin_regs = devm_kmalloc(&pdev->dev, sizeof(*info->pin_regs) *
			      info->npins, GFP_KERNEL);
for (i = 0; i < info->npins; i++) {
	info->pin_regs[i].mux_reg = -1;
	info->pin_regs[i].conf_reg = -1;
}
//获取io 资源的虚拟基地址
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ipctl->base = devm_ioremap_resource(&pdev->dev, res);
	of_node_put(np);
}
//填充imx_pinctrl_desc
imx_pinctrl_desc.name = dev_name(&pdev->dev);
imx_pinctrl_desc.pins = info->pins;
imx_pinctrl_desc.npins = info->npins;
//解析dts里定义的pinctrl 配置,并填充info (imx6ul_pinctrl_info)
ret = imx_pinctrl_probe_dt(pdev, info);
if (ret) {
	dev_err(&pdev->dev, "fail to probe dt properties\n");
	return ret;
}
ipctl->info = info;
ipctl->dev = info->dev;
platform_set_drvdata(pdev, ipctl);
ipctl->pctl = pinctrl_register(&imx_pinctrl_desc, &pdev->dev, ipctl);
if (!ipctl->pctl) {
	dev_err(&pdev->dev, "could not register IMX pinctrl driver\n");
	return -EINVAL;
}
return 0;

}

imx_pinctrl_probe 负责准备imx_pinctrl_desc 以及ipctl 结构, 用来注册pinctrl device (通过pinctrl_register函数),其中比较关键的是调用imx_pinctrl_probe_dt 解析pinctrl 配置
接着分析imx_pinctrl_probe_dt

static int imx_pinctrl_probe_dt(struct platform_device *pdev,
				struct imx_pinctrl_soc_info *info)
{
	struct device_node *np = pdev->dev.of_node;
	struct device_node *child;
	u32 nfuncs = 0;
	u32 i = 0;

	if (!np)
		return -ENODEV;

	nfuncs = of_get_child_count(np);
	if (nfuncs <= 0) {
		dev_err(&pdev->dev, "no functions defined\n");
		return -EINVAL;
	}

	info->nfunctions = nfuncs;
	info->functions = devm_kzalloc(&pdev->dev, nfuncs * sizeof(struct imx_pmx_func),
					GFP_KERNEL);
	if (!info->functions)
		return -ENOMEM;

	info->ngroups = 0;
	for_each_child_of_node(np, child)
		info->ngroups += of_get_child_count(child);
	info->groups = devm_kzalloc(&pdev->dev, info->ngroups * sizeof(struct imx_pin_group),
					GFP_KERNEL);
	if (!info->groups)
		return -ENOMEM;

	for_each_child_of_node(np, child)
		imx_pinctrl_parse_functions(child, info, i++);

	return 0;
}

该函数通过逐级分析 parent ->func–>goups不难看出dts 里 pinctrl 配置 被提取转化为imx_pinctrl_soc_info 结构变量,对比dts文件,
info 结构 的functions 对应着iomuxc的子节点,imx6ul-evk.
info 结构 的groups 对应着iomuxc的孙节点,pinctrl_sys_pwrctrl、pinctrl_gpmi_nand_1等

&iomuxc {
	imx6ul-evk {       
		pinctrl_sys_pwrctrl: sys_pwrctrl {  
			fsl,pins = <
	            MX6UL_PAD_JTAG_TMS__GPIO1_IO11          0x1f030		/*PWR_CTRL*/			
			>;
		};	
		pinctrl_gpmi_nand_1: gpmi-nand-1 {
			fsl,pins = <
				MX6UL_PAD_NAND_CLE__RAWNAND_CLE         0xb0b1
				MX6UL_PAD_NAND_ALE__RAWNAND_ALE         0xb0b1
				MX6UL_PAD_NAND_READY_B__RAWNAND_READY_B 0xb000
				MX6UL_PAD_NAND_CE0_B__RAWNAND_CE0_B     0xb0b1				
				MX6UL_PAD_NAND_RE_B__RAWNAND_RE_B       0xb0b1
				MX6UL_PAD_NAND_WE_B__RAWNAND_WE_B       0xb0b1
				MX6UL_PAD_NAND_DATA00__RAWNAND_DATA00   0xb0b1
				MX6UL_PAD_NAND_DATA01__RAWNAND_DATA01   0xb0b1
				MX6UL_PAD_NAND_DATA02__RAWNAND_DATA02   0xb0b1
				MX6UL_PAD_NAND_DATA03__RAWNAND_DATA03   0xb0b1
				MX6UL_PAD_NAND_DATA04__RAWNAND_DATA04   0xb0b1
				MX6UL_PAD_NAND_DATA05__RAWNAND_DATA05   0xb0b1
				MX6UL_PAD_NAND_DATA06__RAWNAND_DATA06   0xb0b1
				MX6UL_PAD_NAND_DATA07__RAWNAND_DATA07   0xb0b1
			>;
		};	
		pinctrl_keypad: keypad
		{
			fsl,pins = <
				MX6UL_PAD_UART1_CTS_B__GPIO1_IO18    0x1f030	/*KEY_IN0*/
				MX6UL_PAD_UART1_RTS_B__GPIO1_IO19    0x1f030	/*KEY_IN1*/
				MX6UL_PAD_LCD_ENABLE__GPIO3_IO01    0x1f030	/*KEY_IN2*/
				MX6UL_PAD_LCD_VSYNC__GPIO3_IO03    0x1f030	/*KEY_IN3*/			
				
				MX6UL_PAD_UART2_CTS_B__GPIO1_IO22     0x3030		/*KEY_OUT0*/
				MX6UL_PAD_UART2_RTS_B__GPIO1_IO23     0x3030		/*KEY_OUT1*/
		//		MX6UL_PAD_LCD_DATA18__GPIO3_IO23      0x3030		/*KEY_OUT2*/
				MX6UL_PAD_LCD_DATA19__GPIO3_IO24      0x3030		/*KEY_OUT3*/
				MX6UL_PAD_ENET1_RX_DATA1__GPIO2_IO01  0x3030		/*KEY_OUT4*/	
			>;			
		};
	
			};
};

imx_pinctrl_parse_groups中就是最内里的一层解析,即pin脚的具体配置,看之前的dts配置,再对这个接口,原来imx平台已经把pin 脚对应的六个数值的前五个用宏定义封装了,所以dts 中看上去只有id与一个conf 实际一个Pin对应的配置有六个 
mux_reg conf_reg input_reg mux_val input_val conf_val

fsl,pins: each entry consists of 6 integers and represents the mux
and config setting for one pin. The first 5 integers conf_reg input_reg mux_val input_val> are specified using a
PIN_FUNC_ID macro, which can be found in imx*-pinfunc.h under device
tree source folder. The last integer CONFIG is the pad setting value
like pull-up on this pin. And that’s why fsl,pins entry looks like
in the example below.

pinctrl 设备的操作接口

pinctrl 的各种控制通过imx_pinctrl_desc定义的一系列的操作接口来完成,并通过pinctrl_register注册到pinctrl 核心层,由核心层负责在合适时机调用

结语

pinctrl 系统套用的还是linux 驱动中不变的分层模型,核心层把握好统一流程,而板级的pinctrl 驱动只是把相关的控制和信息装成盒子告知给核心层即可,想了解系统中引脚是如何初始化,以及休眠,唤醒对应的引脚切换,以及gpio 如何申请控制等pinctrl 系统的工作机制,还得去核心层找答案

你可能感兴趣的:(#,DRVIER)