[Linux 基础] -- gpio 子系统和 pinctrl 子系统(上)

前言

随着内核的发展,linux驱动框架在不断的变化。很早很早以前,出现了gpio子系统,后来又出现了pinctrl子系统。在网上很难看到一篇讲解这类子系统的文章。就拿gpio操作来说吧,很多时候都是简单的调用gpio子系统提供的api,然后根据sdk说明文档写明的gpio号传参数,至于里面的工作过程对于驱动工程师而言就像个黑盒子。当我们自己设计的板子和demo板有很大变动时,问题就出现了。首先遇到的是怎么配置pin(是基于设备树还是不基于设备树,基于设备树的话,怎么修改设备树关于pinctrl部分的内容,里面各个字段什么意思,怎么改),然后是在哪里配置pin(内核部分有哪些需要相应修改,还是不需要一点修改呢),接着就是怎么调试等等。我想只有清楚了尽量多的gpio子系统和pinctrl子系统细节,才会更快更好的完成这些工作。有些平台的实现没有使用内核提供的pinctrl子系统,而是继续采用在内核提供pinctrl子系统前自己实现的那套机制来pinmux操作,如omap,有些平台则基于pinctrl子系统来实现pinmux、pinconf的控制。本文以gpio子系统为入口慢慢深入,最后分析pinctrl子系统。

gpio 子系统

gpio 子系统帮助我们管理整个系统 gpio 的使用情况,同时通过 sys 文件系统导出了调试信息和应用层控制接口。它内部实现主要提供了两类接口,一类给 bsp 工程师,用于注册 gpio chip(也就是所谓的 gpio 控制器驱动),另一部分给驱动工程师使用,为驱动工程师屏蔽了不同 gpio chip 之间的区别,驱动工程师调用的 api 的最终操作流程会导向 gpio 对应的 gpio chip 的控制代码,也就是 bsp 的代码。

gpio 子系统核心实现分析

gpio 子系统的内容在 drivers/gpio 文件夹下,主要文件有:

devres.c
gpiolib.c
gpiolib-of.c
gpiolib-acpi.c
gpiolib-xxx.c

devres.c 是针对 gpio api 增加的 devres 机制的支持,gpiolib.c 是 gpio 子系统的核心实现, gpiolib-of.c 是对设备树的支持,gpiolib-acpi.c 和 acpi 相关,不分析,最后情景分析的时候,会找一个平台的gpio-xxx.c 来分析。

从驱动工程师使用的 api 开始分析吧!也分两代,legacy 的 api 主要会用到的接口有(现在推荐采用新的,基于描述符的 api):

gpio_request、gpio_free
gpio_direction_input、gpio_direction_output
gpio_to_irq
gpio_export

一般的流程:

//请求一个/一组 gpio
gpio_request / devm_gpio_request、gpio_request_one / devm_gpio_request_one、gpio_request_array ----------------- <1>

//设置 gpio 方向为输入 /  输出
gpio_direction_input 或者 gpio_direction_output ---------------- <2>

//将该 gpio 通过 sys 文件系统导出,应用层可以通过文件操作 gpio
gpio_export                                                                ---------------- <3>

//如果 gpio 为输入,获取 gpio 值,如果 gpio 为输出,可以设置 gpio 高低电平
gpio_get_value、gpio_set_value                           ---------------- <4>

//将 gpio 转为对应的 irq,然后注册该 irq 的中断 handler
request_irq(gpio_to_irq(gpio_num) . . .)                 ---------------- <5>

//释放请求的一个或者一组 gpio
gpio_free / devm_gpio_free、gpio_free_array     ---------------- <6>

下面一个个来分析吧!

<1> 以 gpio_request 为例,gpio_request_one、gpio_request_array 是它的扩展,devm_ 为前缀的是 gpio devres 机制的实现。

int gpio_request(unsigned gpio, const char *label)

参数为 gpio 号和为该 gpio 指定的表签名,具体 gpio 号是多少,可以通过 sdk 开发包说明文档查看,或者查看设备树文件,再或者基于 bank 数量推算(当然,这样可能不准),实在没办法的话,瞄一眼 gpio chip 驱动的代码吧!gpio_request 主要做了以下动作:
    1、检查是否已经被申请,没有的话,标记为已申请
    2、填充 label 到该 pin 数据结构,用于 debug
    3、如果 chip driver 提供了 request 回调,调用它
    4、如果 chip driver 提供了 get_direction 回调,调用它,通过它更新 pin 数据结构,标明 gpio 方向

gpio_request_one 多一个 flags 参数,通过该参数,可以指定 GPIOF_OPEN_DRAIN、GPIOF_OPEN_SOURCE、GPIOF_DIR_IN、GPIOF_EXPORT 等标志,如果指定了 GPIOF_DIR_IN,那么后面就不需要自己再额外调用 gpio_direction_input 或者 gpio_direction_output 了,如果制定了 GPIOF_EXPORT,后面就不需要自己调用 gpio_export 了。

gpio_request_array 是对 gpio_request_one 的封装,用于处理同时申请多个 gpio 的情形。

<2> gpio_direction_input 或者 gpio_direction_output 用来设置该 gpio 为输入还是输出,它们主要是回调 gpio chip driver 提供的 direction_input 或者 direction_output 来设置该 gpio寄存器为输入、输出。

<3> gpio_export 主要用于调试,它会将该 gpio 的信息通过 sys 文件系统导出,这样应用层可以直接查看状态、设置状态等。

<4> gpio_get_value 或者 gpio_set_value 和 input、output 类似,如果为输入,获取该 gpio 的值,如果为输出,设置该 gpio 的值,内部也是调用 gpio chip driver 提供的get、set。

<5> gpio_to_irq 用于获取该 gpio 对应的中断号,这个需要设备树里的该 gpio 节点描述使用哪个中断号(斌通过不是所有的 gpio 都可以触发中断的)。它里面的实现就是回调 gpio chip driver 提供的 to_irq。

<6> gpio_free 就不用说啦,gpio_request 的逆操作。

要使用以上的接口,需要 #include <linux/gpio.h>,且还有一组用于允许睡眠场景的 api 没有给出,更多相关的说明可以参考 Documentation/gpio/gpio-legacy.txt

上面的分析没有深入的代码层,之所以没有深入分析,是因为打算放在后面分析基于描述符 api 时啦!其实,legacy gpio 大部分 api 就是基于描述符 api 来实现的。最新的基于描述符的一般的流程:

//请求第一个 / 指定某一个 gpio desc, 该返回值用于后面的操作
gpiod_get / devm_gpiod_get、gpiod_get_index / devm_gpiod_get_index  ---------------- <1>

//设置 gpio 方向为输入 / 输出
gpiod_direction_input 或者 gpiod_get_direction                                           ---------------- <2>

//将该 gpio 通过 sys 文件系统导出,应用层可以通过文件操作 gpio
gpio_export                                                                                                   ---------------- <3>

//如果 gpio 为输入,获取 gpio 值,如果 gpio 为输出,可以设置 gpio 高低电平
gpiod_get_value 或者 gpiod_set_value                                                        ---------------- <4>

//将 gpio 转为对应的 irq,然后注册该 irq 的中断 handler
request_irq(gpiod_to_irq(gpio_desc) . . .)                                                     ---------------- <5>

//释放请求的一个或一组 gpio
gpiod_put / devm_gpiod_put                                                                         ---------------- <6>

还是下面一个个来分析吧!

<1> gpiod_get 内部的处理和 gpiod_request 一样,不过输入参数变为 char *con_id,这个需要从设备树文件里查看到,除此之外,我们还可以在设备树文件里添加参数(GPIO_ACTIVE_LOW、GPIO_OPEN_DRAIN、GPIO_OPEN_SOURCE)来触发该接口内部设置 gpio,具体的参数格式和具体的 gpio chip driver 有关,一般可以在 /Documentation/devicetree/bindings/gpio 里找到对应平台的。举个例子:

 row-gpios = <&gpio1 25 GPIO_ACTIVE_HIGH     /* Bank1, pin25 */          
                 &gpio1 26 GPIO_ACTIVE_HIGH     /* Bank1, pin26 */              
                 &gpio1 27 GPIO_ACTIVE_HIGH>;   /* Bank1, pin27 */ 
struct gpio_desc *__must_check gpiod_get(struct device *dev, const char *con_id)

对应上面的设备树描述,con_id 就是 row 了,获取的也会是第一个设置,即 gpio1 25 GPIO_ACTIVE_HIGH,如果想要获取第二个设置,我们得通过 gpiod_get_index,并将输入参数 idx 设置为1。

<2> gpiod_direction_input 和 gpio_direction_input 功能一样,实际上 gpio_direction_input 仅仅简单封装了 gpiod_direction_input,不再描述。

<3> gpiod_exprot 同上

<4> gpiod_get_value 同上

<5> gpiod_to_irq 同上

<6> gpiod_put 同上

要使用以上接口,需要 #include <linux/gpio/consumer.h>,且还有一组用于允许睡眠场景的 api 没有给出,更多相关的说明可以参考 Documentation/gpio/consumer.txt。

1> gpio chip driver 的初始化

gpio 子系统提供了两层接口,一层给上层驱动工程师调用,一层给下层 bsp 工程师调用。下层使用前,当然先得 bsp 工程师完成对应的动作。先看一张网上的截图:

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值