Pinctrl概念:
无论是哪种芯片,都有类似图 16.1 的结构:
要想让 pinA 、 B 用于 GPIO ,需要设置 IOMUX 让它们连接到 GPIO 模块;
要想让 pinA 、 B 用于 I2C ,需要设置 IOMUX 让它们连接到 I2C 模块。
为了将人们从繁杂的寄存器设置工作中解放出来,就把引脚的复用、配置抽出来,做成 Pinctrl 子系统,给 GPIO 、 I2C 等模块使用。
从设备树开始学习 Pintrl 会比较容易。
这会涉及 2 个对象: pin controller 、 client device 。
前者提供服务:可以用它来复用引脚、配置引脚。
后者使用服务:声明自己要使用哪些引脚的哪些功能,怎么配置它们。
pin controller:
在芯片手册里找不到 pin controller ,它是一个软件上的概念,你可以认为它对应 IOMUX ──用来复用引脚,还可以配置引脚 ( 比如上下拉电阻等 ) 。
注意, pin controller 和 GPIO Controller 不是一回事,前者控制的引 脚可用于 GPIO 功能、 I2C 功能;后者只是把引脚配置为输入、输出等简单的功能。即先用 pin controller 把引脚配置为 GPIO ,再用 GPIO Controler 把引 脚配置为输入或输出。
client device:
“客户设备”,谁的客户? Pinctrl 系统的客户,那就是使用 Pinctrl 系统 的设备,使用引脚的设备。它在设备树里会被定义为一个节点,在节点里声明要用哪些引脚。
a) pin state:
对于一个“ client device ”来说,比如对于一个 UART 设备,它有多个“状 态”:default 、 sleep 等,那对应的引脚也有这些状态。
怎么理解?
比如默认状态下, UART 设备是工作的,那么所用的引脚就要复用为 UART 功 能。
在休眠状态下,为了省电,可以把这些引脚复用为 GPIO 功能;或者直接把 它们配置输出高电平。
b) groups 和 function:
一个设备会用到一个或多个引脚,这些引脚就可以归为一组 (group) ;
这些引脚可以复用为某个功能: function。( 复用功能的宏定义在arch/arm/boot/dts/imx6ul-pinfunc.h中 )
当然:一个设备可以用到多组引脚,比如 A1 、 A2 两组引脚, A1 组复用为 F1 功能,A2 组复用为 F2 功能。
c) Generic pin multiplexing node 和 Generic pin configuration node
在上图左边的 pin controller 节点中,有子节点或孙节点,它们是给 client device 使用的。
◼ 可 以 用 来 描 述 复 用 信 息 : 哪 组 (group) 引 脚 复 用 为 哪 个 功 能 (function);
◼ 可以用来描述配置信息:哪组 (group) 引脚配置为哪个设置功能 (setting),比如上拉、下拉等。
注意 : pin controller 节点的格式, 没有统一的标准, 每家芯片都不一样。 甚至上面的 group 、 function 关键字也不一定有,但是概念是有的。
GPIO子系统概念:
要操作 GPIO 引脚,先把所用引脚配置为 GPIO 功能,这通过 Pinctrl 子系统来实现。
然后就可以根据设置引脚方向 ( 输入还是输出 ) 、读值──获得电平状态,写值──输出高低电平。
以前我们通过寄存器来操作 GPIO 引脚,即使 LED 驱动程序,对于不同的板子它的代码也完全不同。 当 BSP 工程师实现了 GPIO 子系统后,我们就可以:
⚫ 在设备树里指定 GPIO 引脚
⚫ 在驱动代码中:使用 GPIO 子系统的标准函数获得 GPIO 、设置 GPIO 方向、读取/ 设置 GPIO 值。
这样的驱动代码,将是单板无关的。
在设备树中指定引脚:
在几乎所有 ARM 芯片中, GPIO 都分为几组,每组中有若干个引脚。所以在 使用 GPIO 子系统之前,就要先确定:它是哪组的?组里的哪一个?
在设备树中,“ GPIO 组”就是一个 GPIO Controller ,这通常都由芯片厂家设置好。我们要做的是找到它名字,比如“gpio1” ,然后指定要用它里面的哪个 引脚,比如<&gpio1 0> 。
有代码更直观,下图是一些芯片的 GPIO 控制器节点,它们一般都是厂家定义好,在 xxx.dtsi 文件中:
我们暂时只需要关心里面的这 2 个属性:
gpio-controller;
#gpio-cells = <2>;
“ gpio-controller ”表示这个节点是一个 GPIO Controller ,它下面有很 多引脚。
“ #gpio-cells = <2> ”表示这个控制器下每一个引脚要用 2 个 32 位的数 (cell)来描述。 为什么要用 2 个数?
其实使用多个 cell 来描述一个引脚,这是 GPIO Controller 自己决定的。比如可以用其中一个 cell 来表示那是哪一个引脚,用另一个 cell 来表示它是高电平有效还是低电平有效,甚至还可以用更多的 cell 来示其他特性。 普遍的用法是,用第 1 个 cell 来表示哪一个引脚,用第 2 个 cell 来表示 有效电平:
GPIO_ACTIVE_HIGH : 高电平有效
GPIO_ACTIVE_LOW : 低电平有效
定义 GPIO Controller 是芯片厂家的事,我们怎么引用某个引脚呢?在自 己的设备节点中使用属性"[<name>-]gpios" ,示例如下:
上图中,可以使用 gpios 属性,也可以使用 name-gpios 属性。
在驱动代码中调用 GPIO 子系统 :
在设备树中指定了 GPIO 引脚,在驱动代码中如何使用? 也就是 GPIO 子系统的接口函数是什么?
GPIO 子系统有两套接口:基于描述符的 (descriptor-based) 、老的 (legacy)。前者的函数都有前缀“ gpiod_ ”,它使用 gpio_desc 结构体来表示 一个引脚;后者的函数都有前缀“gpio_ ”,它使用一个整数来表示一个引脚。要操作一个引脚,首先要 get 引脚,然后设置方向,读值、写值。
当pinctrl子系统配置引脚为GPIO模式后,才能用gpio子系统控制引脚。gpio 子系统是基于 pinctrl 子系统的,gpio 的 API 接口的实现很多都是基于 pinctrl 子系统的函数。
参考: https://www.bilibili.com/read/cv13931424/from=search&spm_id_from=333.337.0.0 出处:bilibili