本文以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 <mux_reg
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
<PIN_FUNC_ID CONFIG> in the example below.
pinctrl 设备的操作接口
pinctrl 的各种控制通过imx_pinctrl_desc定义的一系列的操作接口来完成,并通过pinctrl_register注册到pinctrl 核心层,由核心层负责在合适时机调用
结语
pinctrl 系统套用的还是linux 驱动中不变的分层模型,核心层把握好统一流程,而板级的pinctrl 驱动只是把相关的控制和信息装成盒子告知给核心层即可,想了解系统中引脚是如何初始化,以及休眠,唤醒对应的引脚切换,以及gpio 如何申请控制等pinctrl 系统的工作机制,还得去核心层找答案