Linux驱动开发———imx6ull的pinctrl子系统源码分析

前言

        最近在配置pinctrl时,配置了引脚复用寄存器的SION位,配置如下图中的所示,0x4001b8b0中的第30位表示SION位
pinctrl
        按照个人理解,imx6ull在设备树中配置的pinctrl节点,后面所带的值应该为配置寄存器的值,而SION位是复用寄存器的第5位
在这里插入图片描述
在这里插入图片描述
        目前猜测应该是驱动部分做了某些判断,使得在设备树配置的0x4001b8b0中的第30位表示复用寄存器的SION位
在这里插入图片描述

一、驱动源文件查找

        分析驱动源码的第一步肯定是要找到对应的驱动源码,我的方法是先查看设备树中的compatible属性,再使用grep命令找到对应的源文件。compatible属性的值是对应驱动文件中of_match_table数组的值,当两者相对应时,驱动和设备树节点就能匹配成功。
        查看compatible属性
在这里插入图片描述
        驱动源码下执行grep命令,其中drivers/pinctrl/freescale/pinctrl-imx6ul.c即为驱动源文件。

grep "fsl,imx6ul-iomuxc" drivers/* -r

在这里插入图片描述

二、驱动分析
1、驱动入口函数

        imx6ull的pinctrl驱动为一个标准的platform驱动,重点应该是platform驱动中的probe函数

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

        imx6ul_pinctrl_driver中包含了probe函数、remove函数和匹配驱动使用的of_match_table。当驱动与设备匹配后,probe函数就会被调用,解析设备的属性并做一些初始化的操作。

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,
};

        imx6ul_pinctrl_of_match的定义

static struct of_device_id imx6ul_pinctrl_of_match[] = {
	{ .compatible = "fsl,imx6ul-iomuxc", },
	{ /* sentinel */ }
};
2、imx6ul_pinctrl_probe函数分析

        在imx6ul_pinctrl_probe函数中调用了imx_pinctrl_probe函数,该函数多了一个imx6ul_pinctrl_info参数,该参数里头存放着与所有引脚的描述信息(包含每个引脚的编号与name),这应该是为每一款imx芯片的引脚做区分,将驱动与设备信息分离

static int imx6ul_pinctrl_probe(struct platform_device *pdev)
{
	return imx_pinctrl_probe(pdev, &imx6ul_pinctrl_info);
}

        imx6ul_pinctrl_infoimx_pinctrl_soc_info结构体,该结构体中包含的信息如下

struct imx_pinctrl_soc_info {
	struct device *dev; 
	const struct pinctrl_pin_desc *pins; // 引脚描述结构体(指向数组)
	unsigned int npins; // 表示有多少个引脚
	struct imx_pin_reg *pin_regs; // 引脚寄存器(指向数组)
	struct imx_pin_group *groups; // 引脚组结构体(设备树中的fsl,pins属性代表一组)
	unsigned int ngroups; // 有多少个组
	struct imx_pmx_func *functions; // 表示iomux节点下有多少个子节点,一个子节点代表一个function
	unsigned int nfunctions; // iomux节点下子节点个数
	unsigned int flags; 
	u32 grp_index;
};
3、imx_pinctrl_probe函数分析

        imx_pinctrl_probe中主要实现的是为info参数中的成员分配空间、创建一个imx_pinctrl_des并调用pinctrl_register注册到内核中去

int imx_pinctrl_probe(struct platform_device *pdev,
		      struct imx_pinctrl_soc_info *info)
{
	......
	struct imx_pinctrl *ipctl;
	struct pinctrl_desc *imx_pinctrl_desc;

	// 根据引脚的个数分配imx_pin_reg数组的空间
	info->pin_regs = devm_kmalloc(&pdev->dev, sizeof(*info->pin_regs) *
				      info->npins, GFP_KERNEL);
	if (!info->pin_regs)
		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;

	// 解析设备树中传入的信息
	ret = imx_pinctrl_probe_dt(pdev, info);
	if (ret) {
		dev_err(&pdev->dev, "fail to probe dt properties\n");
		return ret;
	}
	
	// 调用pinctrl_register进行注册
	ipctl->info = info;
	ipctl->dev = info->dev;
	platform_set_drvdata(pdev, ipctl);
	ipctl->pctl = pinctrl_register(imx_pinctrl_desc, &pdev->dev, ipctl);
	......
}
4、imx_pinctrl_probe_dt函数分析

        imx的pinctrl驱动中将iomux节点分出了functionsgroups的概念。在imx_pinctrl_probe_dt函数中将有所体现。
        function指的是iomux下的子节点,其中imx6ul-evk指的就是一个function
在这里插入图片描述
        驱动源码下来解析function的代码

	// 获取子节点的个数
	nfuncs = of_get_child_count(np);
	if (nfuncs <= 0) {
		dev_err(&pdev->dev, "no functions defined\n");
		return -EINVAL;
	}

	// 为info中的functions分配内存
	info->nfunctions = nfuncs;
	info->functions = devm_kzalloc(&pdev->dev, nfuncs * sizeof(struct imx_pmx_func),
					GFP_KERNEL);

        group是指在iomux中所有function包含子节点的个数,也就是iomux的孙子节点的个数,图片中的pinctrl_pwm5pinctrl_pwm6节点各表示一个groups(个人认为groups是指引脚的集合,根据group来将一个集合中的引脚分出不同的作用功能)
在这里插入图片描述
        group解析的驱动源码

	info->ngroups = 0;
	// 遍历所有子节点,并对子节点下的子节点个数进行统计
	for_each_child_of_node(np, child)
		info->ngroups += of_get_child_count(child);
	// 根据获得的孙子节点的个数给info中的groups成员分配内存空间
	info->groups = devm_kzalloc(&pdev->dev, info->ngroups * sizeof(struct imx_pin_group),
					GFP_KERNEL);

        分配好好functions和groups的内存空间大小后,将对每一个functions进行解析

	// 遍历每一个function,并调用imx_pinctrl_parse_functions来解析
	for_each_child_of_node(np, child)
		imx_pinctrl_parse_functions(child, info, i++);
5、imx_pinctrl_parse_functions函数分析

        imx_pinctrl_parse_functions函数主要是遍历functions中的groups,并调用imx_pinctrl_parse_groups函数来解析每一个groups

	for_each_child_of_node(np, child) {
		func->groups[i] = child->name;
		// 在imx_pinctrl_probe_dt分配创建的groups数组,grp_index用于记录当前groups的位置
		grp = &info->groups[info->grp_index++];
		// 解析每一个groups
		imx_pinctrl_parse_groups(child, grp, info, i++);
	}
6、imx6ull pinctrl中引脚配置分析

        在分析groups的解析函数之前我们需要了解imx6ull在pinctrl中配置的引脚经过转化之后分别对应什么内容,如

MX6UL_PAD_SNVS_TAMPER7__GPIO5_IO07  0x4001b8b0

        MX6UL_PAD_SNVS_TAMPER7__GPIO5_IO07是一个宏定义,在arch/arm/boot/dts/imx6ul-pinfunc.h有定义

#define MX6UL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x0038 0x02C4 0x0000 0x5 0x0

宏定义展开后,配置的引脚就变为:

0x0038 0x02C4 0x0000 0x5 0x0 0x4001b8b0

每一个数值代表一个32位的整数,每个数值表示的含义为:

mux_reg  conf_reg  input_reg  mux_mode  input_val  conf_val
7、imx_pinctrl_parse_groups函数分析

        确定在设备树中使用多少个字节来表示一个引脚,imx6ull使用的是FSL_PIN_SIZE,即使用6个32位整数来表示一个引脚的配置

	if (info->flags & SHARE_MUX_CONF_REG)
		pin_size = SHARE_FSL_PIN_SIZE; // SHARE_FSL_PIN_SIZE=20byte 5个32位整数
	else
		pin_size = FSL_PIN_SIZE; // FSL_PIN_SIZE=24byte 6个32位整数

获取fsl,pins属性的值,list是一个32位的指针,size为传入参数,表示该属性中包含多少个32位整数

	list = of_get_property(np, "fsl,pins", &size);
	if (!list) {
		dev_err(info->dev, "no fsl,pins property in node %s\n", np->full_name);
		return -EINVAL;
	}

根据获取到的pin_sizesize大小,计算引脚数并分配空间

	grp->npins = size / pin_size; 
	grp->pins = devm_kzalloc(info->dev, grp->npins * sizeof(struct imx_pin),
				GFP_KERNEL);
	grp->pin_ids = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int),
				GFP_KERNEL);
	if (!grp->pins || ! grp->pin_ids)
		return -ENOMEM;

解析每一个引脚的配置

for (i = 0; i < grp->npins; i++) {
		// 复用寄存器的值
		u32 mux_reg = be32_to_cpu(*list++);
		
		......

		if (info->flags & SHARE_MUX_CONF_REG) {
			conf_reg = mux_reg;
		} else {
			conf_reg = be32_to_cpu(*list++);
			if (!(info->flags & ZERO_OFFSET_VALID) && !conf_reg)
				conf_reg = -1;
		}
		
		// 相邻引脚的mux_reg之间的偏移量差距为4,根据偏移量算出引脚的id值,引脚的id值pinctrl_imx6ul.c文件中定义
		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++); // 获取配置寄存器的值,即在设备树中由用户指定的值
		// IMX_PAD_SION的值为0x40000000 当我们再设备树中将配置寄存器的第30位配置为1时,在驱动源码中会将复用寄存器的SION位配置成1(果然和前面猜想的一样)
		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);
	}
总结

        驱动源码,yyds!!!一点小记录,供大家参考学习。如果有哪里分析得不对的地方,欢迎关注我的微信公众号来一起交流学习。

在这里插入图片描述

在这里插入图片描述

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值