linux设备树-pinctrl子系统

本文深入探讨了Linux内核的pinctrl子系统,详细介绍了IO概述,包括硬件功能分类、外部中断资源及初始化。重点讲解了pinctrl子系统的功能、重要概念、设备节点结构,以及pin controller device和client device的数据结构。同时阐述了pin controller的注册过程和client device如何使用pinctrl。通过对pin控制子系统的分析,读者可以理解如何在Linux内核中管理和配置SoC的引脚功能。
摘要由CSDN通过智能技术生成

 ----------------------------------------------------------------------------------------------------------------------------

内核版本:linux 5.2.8根文件系统:busybox 1.25.0u-boot:2016.05----------------------------------------------------------------------------------------------------------------------------

一、IO概述

1.1 硬件功能分类

ARM based SoC的datasheet中总有一个章节叫做GPIO controller(或者I/O ports)的章节来描述如何配置、使用SoC的引脚。虽然GPIO controller的硬件描述中充满了大量的寄存器的描述,但是这些寄存器的功能大概分成下面三个类别:

(1) 有些硬件逻辑是和IO port本身的功能设定相关的,我们称这个HW block为pin controller。软件通过设定pin controller这个硬件单元的寄存器可以实现:

  • 引脚功能配置:例如该I/O pin是一个普通的GPIO还是一些特殊功能引脚(例如memeory bank上CS信号);
  • 引脚特性配置:例如pull-up/down电阻的设定,drive-strength的设定等;

(2) 如果一组GPIO被配置成SPI,那么这些pin脚被连接到了SPI controller,如果配置成GPIO,那么控制这些引脚的就是GPIO controller。通过访问GPIO controller的寄存器,软件可以:

  • 配置GPIO的方向;
  • 如果是输出,可以配置high level或者low level;
  • 如果是输入,可以获取GPIO引脚上的电平状态;

(3) 如果一组GPIO有中断控制器的功能,虽然控制寄存器在datasheet中的I/O ports章节描述,但是实际上这些GPIO已经被组织成了一个interrupt controller的硬件block,它更像是一个GPIO类型的中断控制器,通过访问GPIO中断控制寄存器,软件可以:

  • 中断的enable和disable(mask和unmask);
  • 触发方式;
  • 中断状态清除;
1.2 抽象硬件差异

传统的GPIO driver是负责上面三大类的控制,而新的linux kernel中的GPIO subsystem则用三个软件模块来对应上面三类硬件功能:

  • pin control subsystem(或者简称pinctrl subsystem):驱动pin controller硬件的软件子系统;
  • GPIO subsystem:驱动GPIO controller硬件的软件子系统;关于GPIO子系统我们之前已经介绍过了,具体可以参考:linux驱动移植-GPIO子系统
  • GPIO interrupt chip driver:这个模块是作为一个interrupt subsystem中的一个底层硬件驱动模块存在的;

总体来说,pin controller和GPIO controller都是数字输入/输出控制的IP核,但其控制的对象不同,前者控制的引脚可用于GPIO功能、I2C功能等;后者只是把引脚配置为输入、输出等简单的功能。两者的关系是先用pin controller把引脚配置为GPIO,再用GPIO controller把引脚配置为输入或输出。

1.3 外部中断
1.3.1 外部中断资源

s3c2440一共有24个外部中断,分别对应24个GPIO引脚:

  • EINT0~7对应的GPIO是GPF0~7;
  • EINT8~23对应的GPIO是GPG0~15。
1.3.2 外部中断初始化

以GPF7为例,如果将该引脚配置为上升沿外部中断:

  • 首先需要配置GPFCON寄存器GPF7引脚功能复用为EINT;
  • 然后配置EINTMASK寄存器EINT7使能中断;
  • 最后配置INTMSK寄存器使能EINT7对应的主中断EINT4~EINT7;

二、pinctrl子系统

在许多SoC内部都包含有pin controller,通过pin controller的寄存器,我们可以配置一个或者一组引脚的功能和特性。

各个厂商SoC的pin脚在使用中,都有许多共同的特性,要么配置,要么复用pin脚。所以内核提供了一套代码来管理这些pin,这就是pinctrl subsystem。主要实现的功能:

  • 管理系统中所有的可以控制的pin,在系统初始化的时候,枚举所有可以控制的pin,并标识这些pin;每个pin都有的唯一的ID;
  • 管理这些pin的复用(Multiplexing),对于SoC而言,其引脚除了配置成普通的GPIO之外,若干个引脚还可以组成一个pin group,形成特定的功能, pinctrl subsystem要管理所有的pin group;
  • 配置这些pin的电气特性,例如使能或禁止引脚的上拉、下拉电阻,配置引脚的driver strength;
2.1 重要概念

pinctrl subsystem涉及到了两个对象:

  • pin controller device:其主要目的是提供服务,可以用来复用引脚、配置引脚;
  • client device:使用服务,即使用pinctrl系统的设备。声明自己要使用哪些引脚的哪些功能,怎么配置它们;因此需要在client device设备节点中描述与pin相关的信息。
2.2 设备节点

以arch/arm/boot/dts/s3c2440-pinctrl.dtsi为例,描述了s3c2440 pin controller的dts结构,内容如下:

pinctrl_0: pinctrl@56000000 {
        compatible = "samsung,s3c2440-pinctrl";
        reg = <0x56000000 0x1000>;

        wakeup-interrupt-controller {
                compatible = "samsung,s3c2410-wakeup-eint";
                interrupts = <0 0 0 3>,
                             <0 0 1 3>,
                             <0 0 2 3>,
                             <0 0 3 3>,
                             <0 0 4 4>,
                             <0 0 5 4>;
        };
        
        /*
         * Pin banks
         */

        gpa: gpa {
                gpio-controller;
                #gpio-cells = <2>;
        };

        ......
        gpj: gpj {
                gpio-controller;
                #gpio-cells = <2>;
        };


        /*
         * Pin groups
         */

        uart0_data: uart0-data {
                samsung,pins = "gph-2", "gph-3";
                samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
        };

       .....

};

如上所示:pinctrl@56000000内部定义了一些自己的属性,比如compatible、reg,此外还定义了大量的子节点,这些子节点我们称之为引脚配置(pin configuration)。

定义pin configuration的目的是为了让client device引用。例如串口设备:

  • 无device tree,我们一般会在初始化代码中配置相关的引脚功能为串口功能;
  • 有了device tree,我们可以通过device tree来传递这样的信息;设备节点可以通过自己节点的属性来指向pin controller的某个child node。

在pin controller node中定义的pin configuration可以分为两大类:pin bank和pin group。

2.2.1 bank

所谓的pin bank,个人理解就是一组GPIO端口,这一组GPIO端口同属于一个GPIO控制器。以s3c2440为例,分为了9 个GPIO控制器:

GPIO控制器 GPIO端口名称 GPIO端口数量
GPIOA GPA0~GPA24 25
GPIOB GPB0~GPB10 11
GPIOC GPC0~GPC15 16
GPIOD GPD0~GPD15 16
GPIOE GPE0~GPE15 16
GPIOF GPF0~GPF7 8
GPIOG GPG0~GPG15 16 
GPIOH GPH0~GPH10 11 (这里明明11个,datasheet说的总共9个)
GPIOJ GPJ0~GPJ12 13 

所以在arch/arm/boot/dts/s3c2440-pinctrl.dtsi文件中就把这9组GPIO端口枚举成pin bank,如下:

/*
 * Pin banks
 */
gpa: gpa {
        gpio-controller;
        #gpio-cells = <2>;
};

gpb: gpb {
        gpio-controller;
        #gpio-cells = <2>;
};

gpc: gpc {
        gpio-controller;
        #gpio-cells = <2>;
};

gpd: gpd {
        gpio-controller;
        #gpio-cells = <2>;
};

gpe: gpe {
        gpio-controller;
        #gpio-cells = <2>;
};

gpf: gpf {
        gpio-controller;
        #gpio-cells = <2>;
        interrupt-controller;
        #interrupt-cells = <2>;
};

gpg: gpg {
        gpio-controller;
        #gpio-cells = <2>;
        interrupt-controller;
        #interrupt-cells = <2>;
};

gph: gph {
        gpio-controller;
        #gpio-cells = <2>;
};

gpj: gpj {
        gpio-controller;
        #gpio-cells = <2>;
};
View Code

如gpa: gpa 这个child node就是描述GPIOA这个组,也就是gpa bank.。pin bank中支持如下属性:

  • gpio-controller:表示这是一个GPIO控制器;这样就可以使用GPIO子系统API来操作管脚了;
  • interrupt-controller:表示这是一个中断控制器,有的GPIO控制器也可以是中断控制器,如gpf;
  • #gpio-cells:表示使用这个bank的GPIO时,需要用两个32位数去描述;为什么要用2个数?其实使用多个cell来描述一个引脚,这是GPIO Controller自己决定的。比如可以用其中一个cell来表示那是哪一个引脚,用另一个cell来表示它是高电平有效还是低电平有效,甚至还可以用更多的cell来示其他特性;

gpf、gpg本身也充当一个中断控制器,它的interrupt parent也是interrupt-controller@4a000000,gpf的interrupt cell是2,表示引用gpf的一个中断需要2个参数来描述。

GPIO控制器支持两种类型的外部中断:外部GPIO中断和外部唤醒中断。两者之间的区别在于,外部唤醒中断可以用作系统唤醒事件。

更多信息可以参考: Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt。

2.2.2 group

以功能为依据,具有相同功能的引脚称为一个pin group,比如:

  • 串口0使用的GPH2、GPH3引脚,因此可以将GPH2、GPH3分为一组;
  • I2C使用的GPE14、GPE15引脚,因此可以将GPE14、GPE15分为一组;

所以在arch/arm/boot/dts/s3c2440-pinctrl.dtsi文件中定义到了大量的pin group,如下:

/*
 * Pin groups
 */

uart0_data: uart0-data {
        samsung,pins = "gph-2", "gph-3";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
};

uart1_data: uart1-data {
        samsung,pins = "gph-4", "gph-5";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
};


uart2_data: uart2-data {
        samsung,pins = "gph-6", "gph-7";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
};

uart2_fctl: uart2-fctl {
        samsung,pins = "gph-6", "gph-7";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
};

extuart_clk: extuart-clk {
        samsung,pins = "gph-8";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
};

i2c0_bus: i2c0-bus {
        samsung,pins = "gpe-14", "gpe-15";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
};

spi0_bus: spi0-bus {
        samsung,pins = "gpe-11", "gpe-12", "gpe-13";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
};

sd0_clk: sd0-clk {
        samsung,pins = "gpe-5";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
};

sd0_cmd: sd0-cmd {
        samsung,pins = "gpe-6";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
};

sd0_bus1: sd0-bus1 {
        samsung,pins = "gpe-7";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
};

sd0_bus4: sd0-bus4 {
        samsung,pins = "gpe-8", "gpe-9", "gpe-10";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
};

/*添加Nand Flash所用的管脚*/
nand_pinctrl: nand_pinctrl {
    samsung,pins = "gpa-17", "gpa-18", "gpa-19",
                 "gpa-20", "gpa-22";
    samsung,pin-function = <1>;
};
View Code

其中:

  • samsung,pins:包含要应用特定引脚功能选择或引脚配置(或两者)的引脚列表;该属性至少需要指定一个引脚,并且没有指定引脚数目的上限;引脚使用从SoC硬件手册中派生出来的引脚名称来指定。例如,pin控制器中GPA0组的引脚可以表示为“gpa0-0”、“gpa0-1”、“gpa0-2”等,名称应该采用小写字母。引脚名称的格式应该是(根据硬件手册)“[引脚组名称]-[组内引脚编号]”;
  • samsung,pin-function:指定应用于列在“samsung,pins”属性中的每个引脚上的引脚功能选择,将这些GPIO初始值设置为2,该属性的值应该从所指定的引脚组的SoC硬件手册中选择,具体是什么功能,有datasheet解释;

还可以选择性地指定应用于“samsung,pins”属性中列出的所有引脚上的一个或多个引脚配置。支持以下引脚配置属性:

  • samsung,pin-val:引脚输出缓冲区的初始值;
  • samsung,pin-pud:上下拉配置;
  • samsung,pin-drv:驱动器强度配置;
  • samsung,pin-pud-pdn:低功耗模式下的上下拉配置;
  • samsung,pin-drv-pdn:低功耗模式下的驱动器强度配置;

更多信息可以参考: Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt。

2.2.3  client device引用pinctrl

当一个client device想引用某个"引脚配置节点"应该如何进行描述呢?一个典型的device tree中的外设节点定义如下:

device-node-name {  
    .......
    pinctrl-names = "sleep", "active";   
    pinctrl-0 = <pin-config-0-a>; 
    pinctrl-1 = <pin-config-1-a  pin-config-1-b>;         
};

其中pinctrl-names属性和pinctrl-%d属性格外重要,因为它们是内核设定的属性,我们下面来介绍;

(1) pinctrl-names定义了一个state列表,那么什么是state呢?

对于一个client device,比如它是一个串口设备,它有多种状态,比如default、sleep等。那么对应的引脚也有这些状态,比如:

  • 在默认状态下,串口设备是工作的,那么所用的pin都要复用为UART功能;
  • 在休眠状态下,为了省电,可以把这些引脚复用为GPIO功能,或者直接把它们配置输出高电平;

state有两种标识:一种就是pinctrl-names定义的字符串列表,另外一种就是ID。ID从0开始,依次加一。以上面例子为例:

  • state ID等于0(名字是sleep)的state对应pinctrl-0属性;
  • state ID等于1(名字是active)的state对应pinctrl-1属性;

内核自己定义了"default","init","idel","sleep"状态;也可以是其它自己定义的状态, 比如串口的"flow_ctrl"状态(使用流量控制)。

(2) pinctrl-x的定义。pinctrl-x是一个句柄(phandle)列表,每个句柄指向一个"引脚配置节点",有时候,一个state可以用到多组pin,比如A1、A2两组pin,A1组pin复用为F1功能,A2组pin复用为F2功能。

我们以s3c2440串口0设备节点定义为例:

&uart_0 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&uart0_data>;
};

其中:

  • pinctrl-names:这里只定义了一个state就是default,对应pinctrl-0属性定义;
  • pinctrl-0:pinctrl-0是一个句柄(phandle)列表,每个句柄指向一个"引脚配置节点";这里配置为uart0-data。
2.3 pinctrl subsystem框架

下图描述了pinctrl subsystem的模块图:

中间层是pin control core,用于管理系统中的pin controller。pin control core汇总了pin controller的通用操作:

  • 对上pin control core提供的一套统一通用的操作pin controller硬件的软件接口,屏蔽了不同芯片的具体实现;
  • 对下pin control core提供了针对不同芯片操作的一套framework,针对不同芯片,只需要实现pin controller driver ,然后使用pin control core提供的注册函数,将其挂接到pinctrl subsystem上,这样就完成了这一套东西;

基本上这个软件框架图和GPIO subsystem是一样的,其软件抽象的思想也是一样的,当然其内部具体的实现不一样。

2.4 目录结构
2.4.1 源文件

linux内核将pinctrl驱动相关的代码放在drivers/pinctrl目录下,这下面的文件还是比较多的,我们大概了解一下即可。

 其中:

  • core.c、core.h :pinctrl subsystem的core driver;
  • pinctrl-utils.c、pinctrl-utils.h:pinctrl subsystem的一些utility接口函数;
  • pinmux.c pinmux.h:pinctrl subsystem的core driver(pin muxing部分的代码,也称为pinmux driver);
  • pinconf.c、pinconf.h:pinctrl subsystem的core driver(pin config部分的代码,也称为pin config driver);
  • devicetree.c、devicetree.h:pinctrl subsystem的device tree代码;
  • pinctrl-xxxx.c:各种pin controller的low level driver;

在pin controller driver文档中 ,我们以s3c2440的pin controller为例,描述了一个具体的low level的driver,这个driver涉及的文件包括pinctrl-samsung.c,pinctrl-samsung.h和pinctrl-s3c24xx.c。

2.4.2 头文件

pinctrl subsystem会向系统中的其它driver提供接口以便进行该driver的引脚配置和引脚复用功能的设定,下面这些头文件就定义了pinctrl subsystem的外部接口以及相关的数据结构:

  • consumer.h:其它的driver要使用pinctrl subsystem的以下功能:
    • a、设置引脚复用功能;
    • b、配置引脚的电气特性;
  • devinfo.h:这是for linux内核的驱动模型模块(driver model)使用的接口。struct device中包括了一个struct dev_pin_info *pins的成员,这个成员描述了该设备的引脚的初始状态信息,在probe之前,driver model中的core driver在调用driver的probe函数之前会先设定pin state;
  • machine.h:machine模块的接口;

pinctrl subsystem提供给底层pin controller driver的头文件列表如下:

  • pinconf-generic.h:这个接口主要是提供给各种pin controller driver使用的,不是外部接口;
  • pinconf.h:pin configuration 接口;
  • pinctrl-state.h:pin control state状态定义;
  • pinmux.h:pin mux function接口;
2.5 数据结构

学习pin controller driver,首先要了解pinctrl subsystem涉及到的数据结构,知道每个数据结构以及成员的含义之后,再去看源码就容易了。

我们从pin controller device和client device视角去介绍pinctrl subsystem涉及到的数据结构,其中:

  • pin controller device相关的数据结构主要有:pinctrl_desc、pinctrl_ops、pinmux_ops、pinconf_ops、pinctrl_dev;
  • client device相关的数据结构主要有:pinctrl、pinctrl_state、pinctrl_setting、pinctrl_map、pinctrl_dt_map;

三、pin controller device

3.1 struct pinctrl_dev

pin control core使用struct pinctrl_dev抽象一个pin controller device,其定义在drivers/pinctrl/core.h文件,如下:

/**
 * struct pinctrl_dev - pin control class device
 * @node: node to include this pin controller in the global pin controller list
 * @desc: the pin controller descriptor supplied when initializing this pin
 *      controller
 * @pin_desc_tree: each pin descriptor for this pin controller is stored in
 *      this radix tree
 * @pin_group_tree: optionally each pin group can be stored in this radix tree
 * @num_groups: optionally number of groups can be kept here
 * @pin_function_tree: optionally each function can be stored in this radix tree
 * @num_functions: optionally number of functions can be kept here
 * @gpio_ranges: a list of GPIO ranges that is handled by this pin controller,
 *      ranges are added to this list at runtime
 * @dev: the device entry for this pin controller
 * @owner: module providing the pin controller, used for refcounting
 * @driver_data: driver data for drivers registering to the pin controller
 *      subsystem
 * @p: result of pinctrl_get() for this device
 * @hog_default: default state for pins hogged by this device
 * @hog_sleep: sleep state for pins hogged by this device
 * @mutex: mutex taken on each pin controller specific action
 * @device_root: debugfs root for this device
 */
struct pinctrl_dev {
        struct list_head node;
        struct pinctrl_desc *desc;
        struct radix_tree_root pin_desc_tree;
#ifdef CONFIG_GENERIC_PINCTRL_GROUPS
        struct radix_tree_root pin_group_tree;
        unsigned int num_groups;
#endif
#ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS
        struct radix_tree_root pin_function_tree;
        unsigned int num_functions;
#endif
        struct list_head gpio_ranges;
        struct device *dev;
        struct module *owner;
        void *driver_data;
        struct pinctrl *p;
        struct pinctrl_state *hog_default;
        struct pinctrl_state *hog_sleep;
        struct mutex mutex;
#ifdef CONFIG_DEBUG_FS
        struct dentry *device_root;
#endif
};

其中部分参数含义如下:

  • node:用于构建双向链表,将此pinctrldev添加到全局链表pinctrldev_list;
  • desc:初始化此 pin controller时提供的 pin controller描述符;
  • pin_desc_tree:存储此p
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Graceful_scenery

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值