pinctrl子系统和gpio子系统

一、什么是pinctrl子系统和gpio子系统

pinctrl子系统 用于引脚的配置。如复用为那种引脚(GPIO模式I2C模式),电器特性等等。

gpio子系统 用于引脚的控制。如配置输出,输出高低电平等等。

pinctrl子系统配置引脚为GPIO模式后,才能用gpio子系统控制引脚。gpio 子系统是基于 pinctrl 子系统的,gpio 的 API 接口的实现很多都是基于 pinctrl 子系统的函数。

pinctrl子系统和gpio子系统_第1张图片

二、.Linux Pinctrl子系统

2.1、相关概念

2.1.1、pin controller和client device

  • pin controller:实现复用引脚、配置引脚服务,是服务的提供者
  • client device:调用pin controller配置自己所需要的IO,是服务的使用者

note:pin controller 子节点格式是由芯片厂商自定义的,即每家芯片pin controller子节点格式都是不一样的。各个厂商的 pinctrl 使用说明可以参考厂商提供的文档或去内核源码路径 Documentation/devicetree/bindings/pinctrl 目录下找到对应厂商的使用说明。


imx6ull格式

//client端:
@节点名字 {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_自定义名字A>;
    status = "okay";
};

//pincontroller服务端
pinctrl_自定义名字A: 自定义名字B {
    fsl,pins = <
            引脚复用宏定义   PAD(引脚)属性, // 引脚 A
            引脚复用宏定义   PAD(引脚)属性; // 引脚 B
    >;
};

rk3288实例

//client端
@uart0 {
    pinctrl-names = "default";
    pinctrl-0 = <&uart0_xfer &uart0_cts &uart0_rts>; //它使用三个节点来表示三组引脚。
    status = "okay";
};

//pincontroller服务端
gpio4_uart0 {
    // 引脚 A
    uart0_xfer: uart0-xfer {
        rockchip,pins = , ; //使用rockchip,pins来指定使用哪些引脚,就等效于groups
        rockchip,pull = ; //引脚的参数
        rockchip,drive = ; //引脚的参数
    };
    // 引脚 B
    uart0_cts: uart0-cts {
        rockchip,pins = ; 
        rockchip,pull = ;
        rockchip,drive = ;
    };
    // 引脚 C
    uart0_rts: uart0-rts {
        rockchip,pins = ;
        rockchip,pull = ;
        rockchip,drive = ;
    };
    uart0_rts_gpio: uart0-rts-gpio {
        rockchip,pins = ;
        rockchip,drive = ;
    };
};
 

/arch/arm/boot/dts/imx6ul-14x14-evk.dtsi

pinctrl子系统和gpio子系统_第2张图片

 

2.1.2、pin state:

对于一个“client device”来说,它有多个“状态”:default、sleep等,那对应的引脚也有这些状态。

比如默认状态下,UART设备是工作的,那么所用的引脚就要复用为UART功能。

在休眠状态下,为了省电,可以把这些引脚复用为GPIO功能;或者直接把它们配置输出高电平。

pinctrl子系统和gpio子系统_第3张图片

上图中,pinctrl-names里定义了2种状态:default、sleep。

第0种状态用到的引脚在pinctrl-0中定义,它是state_0_node_a,位于pincontroller节点中。

第1种状态用到的引脚在pinctrl-1中定义,它是state_1_node_a,位于pincontroller节点中。

当这个设备处于default状态时,pinctrl子系统会自动根据上述信息把所用引脚复用为uart0功能。

当这这个设备处于sleep状态时,pinctrl子系统会自动根据上述信息把所用引脚配置为高电平。

2.1.3、groups和function:

一个“client device”会用到一个或多个引脚,这些引脚就可以归为一组(group);

这些引脚可以复用为某个功能:function。当然:一个设备可以用到多能引脚,比如A1、A2两组引脚,A1组复用为F1功能,A2组复用为F2功能。 

state_0_node_a 
{ 
    uart0 
    { 
        function = "uart0";
        groups = "u0rxtx", "u0rtscts"; 
    }; 
};


2.2、源码解析

在 Linux 内核源码中,pinctrl 子系统的代码大都在 kernel/drivers/pinctrl/...,不同平台有不同的文件夹。gpio 子系统的代码大都在 kernel/drivers/gpio/...目录下。

分析源码可以在线查看 Linux 内核源码,在线网址跳转函数和查找结构体也很方便:

https://elixir.bootlin.com/linux/latest/source

2.2.1、重要的结构体

1、pinctrl_desc:这里包含了pinctrl 子系统三个最重要的结构体,有三个操作函数集,pinctrl_ops 包含了对 PIN 的操作函数集,pinmux_ops 包含了对 PIN 的复用函数集,pinconf_ops 包含了对 PIN 的配置函数,大家可以在自己平台中点进去看看自己平台实现了哪个函数,如何实现的。

2、pinctrl 结构体:这里包含了 PIN 控制器所控制 PIN 的状态 state,state 里面包含了 setting,这个 setting 就是在设备树中对PIN的设置,大家点进去看相关数据结构就可以看到自己在设备树中用到的字符串。

3、gpio 相关的结构体,我们说过 pinctrl 子系统和 gpio 子系统是耦合的,我们从结构体就可以看得出来,它包含了最重要的结构体 gpio_chip。

我们已经阐述了pinctrl 子系统主要的数据结构,后面讲述函数调用关系。

2.2.2、总体框架:


在内核中,用platform_driver来描述一个结构体。当设备和驱动进行匹配以后,里面得probe函数就会执行。在probe函数中,完成:
1)、从设备树中,获取对应设备信息,其实就是电气属性和复用信息。
2)、向内核注册一个pin控制器。在内核中,每个pin控制器都抽象成结构体pinctrl_desc。注册控制器,其实就是注册这个结构体。
 

2.2.3、probe入口:

在文件 drivers/pinctrl/freescale/pinctrl-imx6ul.c 中有如下内容:

static const struct imx_pinctrl_soc_info imx6ul_pinctrl_info = {
	.pins = imx6ul_pinctrl_pads,
	.npins = ARRAY_SIZE(imx6ul_pinctrl_pads),
	.gpr_compatible = "fsl,imx6ul-iomuxc-gpr",
};

static const struct imx_pinctrl_soc_info imx6ull_snvs_pinctrl_info = {
	.pins = imx6ull_snvs_pinctrl_pads,
	.npins = ARRAY_SIZE(imx6ull_snvs_pinctrl_pads),
	.flags = ZERO_OFFSET_VALID,
};

static const 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 imx_pinctrl_soc_info *pinctrl_info;
    //匹配结构体,根据 compatible 和设备树的compatible 字段进行匹配
	pinctrl_info = of_device_get_match_data(&pdev->dev);
	if (!pinctrl_info)
		return -ENODEV;
    //匹配成功执行这个 probe 函数。
	return imx_pinctrl_probe(pdev, pinctrl_info);
}

static struct platform_driver imx6ul_pinctrl_driver = {
	.driver = {
		.name = "imx6ul-pinctrl",
		.of_match_table = imx6ul_pinctrl_of_match,
		.suppress_bind_attrs = true,
	},
	.probe = imx6ul_pinctrl_probe,
};

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

pinctrl 子系统的驱动也是一个标准的 platform 驱动框架,分为:驱动、设备、总线。

 当设备和驱动匹配的时候,probe 函数会执行,只是 pinctrl 子系统采用的 arch_initcall 去声明,而不是 module_init(device_initcall),所以在系统起来的时候它会先加载(Linux 驱动挂载顺序分析-51CTO.COM)。

platform 虚拟总线会按照 of_device_id 结构体中的 compatible 属性去匹配 pinctrl 驱动和设备。

pinctrl子系统和gpio子系统_第4张图片

2.2.4、获取设备树中的信息:

设备树中信息配置如下:

pinctrl_led: ledgrp{
	fsl, pins = ;
};

宏定义的位置:/arch/arm/boot/dts/imx6ul-pinfunc.h

其中:MX6UL_PAD_UART1_RTS_B__GPIO1_IO19其本质是个宏,他表示将UART1_RTS_B复用为GPIO1_IO19,

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

将管脚的配置展开即:

pinctrl_led: ledgrp{
	fsl, pins = <0x0090 0x031C 0x0000 0x5 0x 0x10B0 >;
};

这六个值有什么含义呢?内核是怎么解析的呢?我们可以有两种途经知道这些配置项的含义:

1、厂家给出的说明文件

/Documentation/devicetree/bindings/pinctrl/fsl,imx6ul-pinctrl.txt

* Freescale i.MX6 UltraLite IOMUX Controller

Please refer to fsl,imx-pinctrl.txt in this directory for common binding part
and usage.

Required properties:
- compatible: "fsl,imx6ul-iomuxc" for main IOMUX controller or
  "fsl,imx6ull-iomuxc-snvs" for i.MX 6ULL's SNVS IOMUX controller.
- fsl,pins: each entry consists of 6 integers and represents the mux and config
  setting for one pin.  The first 5 integers  are specified using a PIN_FUNC_ID macro, which can be found in
  imx6ul-pinfunc.h under device tree source folder.  The last integer CONFIG is
  the pad setting value like pull-up on this pin.  Please refer to i.MX6 UltraLite
  Reference Manual for detailed CONFIG settings.

2、阅读源码

读取dts文件的文件为:drivers/pinctrl/freescale/pinctrl-imx.c,实现函数名为:static int imx_pinctrl_parse_groups

这段代码中list = of_get_property(np, "fsl,pins", &size);实现了读取dts文件中的fsl,pin属性值,并保存在了list指针变量中。紧接着,分别将list中的值mux_reg、conf_reg、input_reg、mux_mode、input_val、config六个变量中,config的值说白了就是对寄存器配置(上拉电阻、频率等等)的值,就是pad_ctrl的值。

pinctrl子系统和gpio子系统_第5张图片

因此对应关系如下:

      0x0090 |   0x031C |  0x0000  |  0x5   |   0x0     |   0x10B0
---------------------------------------------------------------------------------------------------------
mux_ctrl_ofs  |  pad_ctrl_ofs |  sel_input_ofs |  mux_mode   | sel_input   |  pad_ctrl

结论:

#define   MX6UL_PAD_UART1_RTS_B__GPIO1_IO19   0x0090  0x031C  0x0000  0x5  0x0
/******************************/

mux_reg 代表mux寄存器(复用寄存器)的偏移地址  ->设备树下对应的设备节点的reg属性代表外示该设备的外设寄存器的起始地址。
conf_reg 代表conf寄存器(引脚属性控制)的偏移地址
input_reg 代表input寄存器(输入选择寄存器)的偏移地址(有些外设没有)
mux_reg 代表mux寄存器的值
input_reg 代表input寄存器的值。

前面三个均为 寄存器地址偏移值,后面两个为对应要写入寄存器的值。
对应规则:
mux_reg <-- mux_mode
conf_reg <-- 引脚属性值 (宏后面的参数)
因为引脚属性配置比较多样灵活,所以由用户决定该值,便提出到宏外。
input_reg <-- input_val

0x10B0就表示conf_reg寄存器的值,他用来配置一个pin的电气属性。通过此值,来设置一个IO的上/下拉,驱动能力和速度等……

三、GPIO子系统

3.1

ref

linux内核中的GPIO系统之(4):pinctrl驱动的理解和总结

Pinctrl 和 GPIO 使用手册 — 矽昌通信

【i.MX6ULL】驱动开发6——Pinctrl子系统与GPIO子系统点亮LED - 哔哩哔哩

【深度】韦东山:GPIO和Pinctrl子系统的使用 (附免费视频) - 哔哩哔哩

【linux】驱动-10-pinctrl子系统 - 李柱明 - 博客园

一篇带给你Pinctrl子系统的深入分析-51CTO.COM

L2. pinctrl子系统 - 简书

pinctrl和GPIO子系统 - 蘑菇王国大聪明 - 博客园

【linux】驱动-11-gpio子系统 - 李柱明 - 博客园

你可能感兴趣的:(嵌入式linux驱动,单片机,stm32,c语言)