社区编辑申请
注册/登录
GPIO到底该如何控制 原创
系统 Linux
希望大家通过本文,可以了解内核中GPIO的机制并掌握其操作方法,但需要说明的是在内核之前的阶段也会进行引脚的配置,比如我们使用串口来打印bootloader阶段的日志,这种情况下串口引脚一定是在内核之前的阶段进行配置的。

作者 | 赵青窕

随着Linux内核代码的逐步完善,其GPIO口的操作接口也在不断完善。内核中存在多种GPIO API接口,我们该如何使用这些API接口呢?我们又该如何在设备树中配置GPIO呢?

目前的内核中提供了三种版本的API接口供我们使用,分别是Pinctrl子系统对应的API接口和GPIO子系统对应的API接口(GPIO子系统提供了两种类型的API接口),在本文中我将会通过内核中GPIO架构的角度来说明这三类API接口;我们对GPIO的控制,除了合适的API接口外,还需要通过设备树对GPIO进行配置,虽然不同架构中设备树的配置方式不同,但本文中也会举例说明设备树如何配置,对应的代码中如何使用API接口;最后说明我们可以用到的调试手段。

1.内核GPIO架构

下图是内核中,GPIO架构中的核心部分框架图(暂时不考虑GPIO架构对应的sysfs和debugfs):

图 1:GPIO架构

从上图中的上半部分可以看出以下关键信息:

  • 内核提供了两种控制引脚的方式,一种是采用Pinctrl子系统,一种是采用GPIO子系统(该系统又有两种方式,后面小节中会进行说明),用户编写驱动时可以调用这两个子系统提供的API接口来达到控制GPIO口的目的;
  • GPIO子系统的功能是通过Pinctrl子系统来实现的。虽然从图中看出,Pinctrl子系统和GPIO子系统并存,但内核需要对所有的GPIO口进行管理,因此就需要一个统一的管理接口或者模块,Pinctrl就完成这种统一管理的目标,比如我们采用GPIO子系统的API接口gpio_request来申请GPIO口时,内核需要记录哪些GPIO口已经申请过了,若Pinctrl子系统和GPIO子系统各维护一套GPIO管理策略,那就可能导致Pinctrl子系统和GPIO子系统同时操作同一个GPIO口的情况,这种显然是不可行的,且从高通平台内核代码中可以看出gpio_request有如下的调用关系:

gpio_request--->gpiod_request----> gpiod_request_commit---->chip->request(系统启动时设置为gpopchip_generic_request) ---->pinctrl_gpio_request

该调用关系从GPIO子系统的API函数gpio_request最后调用了Pinctrl子系统的函数pinctrl_gpio_request,这种调用关系,也证实了Pinctrl和GPIO子系统的关系。对于其他接口,如gpio_direction_input,gpio_set_value等函数的调用,同gpio_request相似,其最后均是调用到对应的chip->direction_input,chip->set,进而调用pinctrl_gpio_direction_input,等函数,由于这类API函数比较多,在此就不展示其调用关系了;

  • 内核采用结构体struct pinctrl_dev来表示一个控制器,所有的pinctrl_dev会形成链表,链表头就是pinctrldev_list,在函数pinctrl_gpio_request内部,会调用函数pinctrl_get_device_gpio_range来根据GPIO号,遍历链表pinctrldev_list来查找该GPIO口对应的pinctrl_dev,当然这部分工作均是由系统来维护,我们只需知道整个框架,并如何使用GPIO的整个子系统即可。

注意:目前从我看到的代码中发现,有些厂家在实现GPIO子系统时,并非所有的功能均通过Pinctrl子系统,但gpio_request是会通过Pinctrl子系统,因为Pinctrl子系统中会标记哪些GPIO已经request了,这样后续模块采用request继续申请该资源时就会失败;不过大家放心,即使其部分功能不通过Pinctrl子系统,但其对驱动模块提供的API接口不受其影响。

2.用户驱动中控制GPIO

在编写驱动时,可以采用以下两种方式来设置:

  • Pinctrl方式,该方式最终是采用Pinctrl子系统来实现各项功能的;
  • 采用GPIO子系统接口的方式,该方式其实有两种,分别是legacy和gpio description的方式。但目前的内核中,legacy内部会调用gpio description的方式,后面内容中,我会以legacy的方式来说明使用方法。

Pinctrl控制引脚方式

下面,我以高通平台Pinctrl的方式来说明其代码和设备树的配置。

我们先来看一下设备树:

&tlmm{
client1_state1: client1_state1 {
mux {
pins = "gpio0";
function = "gpio";
};
config {
pins = "gpio0";
bias-disable;
drive-strength =<2> ;
input-enable;
};
};
client1_state2: client1_state2 {
mux {
pins = "gpio0";
function = "gpio";
};
config {
pins = "gpio0";
bias-disable;
drive-strength = <2>;
output-high;
};
};
};

&soc {
client1 {
pinctrl-names = "state1", "state2";
pinctrl-0 = <&client1_state1 >;
pinctrl-1 = <&client1_state2>;
};
};

上面是典型的设备树的配置,其中包含两个节点,分别是tlmm中的引脚配置和我们的设备client1。

tlmm中定义了两种GPIO状态,分别是作为输入功能的GPIO0和作为输出功能的GPIO0。不同平台的设备树中的配置方法不同,但均需要像上面设备树那样,配置两部分:

引脚功能配置,是作为普通的GPIO口,还是复用为其他的功能;

引脚驱动配置,包括引脚内部的上拉或者下拉,驱动能力等。

接下来,一起看下代码中如何进行操作,其操作需要按照下面的顺序:

  • 先调用devm_pinctrl_get或者pinctrl_get函数获取对应的struct pinctrl*;
  • 紧接着调用pinctrl_lookup_state(struct pinctrl *p, const char *name)来根据name获取对应的配置;
  • 最后我们采用函数pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state)来进行状态的选择。选择某一状态,就是设置了对应的引脚,且引脚的request操作是在该函数内部完成的,该函数内部会调用pin_request来进行申请,且调用该函数后,引脚的状态就是设备树中设备的状态,如上面设备树中,client1_state1对应的引脚使用了其GPIO功能,且配置为输入(设备树中是通过input-enable进行配置的),client1_state2对应的引脚也使用了其GPIO的功能,且配置为输出高(设备树中是通过out-high进行配置的)。

采用GPIO子系统


&soc {
client1 {
qcom,gpio-client1 = <&tlmm 100 0>; //100就是GPIO号
}

这种方式在设备树的配置方式上比较简单,其代码操作如下:

  • 使用函数of_get_named_gpio(node, " qcom,gpio-client1", 0)获取GPIO号。
  • 接下来是最重要的一步,调用函数gpio_request来进行GPIO的request操作。该函数最后会通过Pinctrl接口间接调用pin_request来进行引脚的申请操作。之前有一次工作中粗心,忘记request操作,但发现该GPIO可能也会正常操作,但会有工作不稳定的情况发生;
  • 接下来可以调用函数gpio_direction_input或者gpio_direction_output来进行GPIO输入或者输出模式的配置,gpio_direction_output调用的同时,可以设置输出高或者输出低;
  • 如果配置位输入模式,则可以使用函数gpio_get_value来获取GPIO口的状态。

如果需要把引脚配置为中断功能,则我们需要使用函数irq = gpio_to_irq(gpio)来获得irq号,根据irq号来进行适当的中断配置。

通过上面两种方式,我们可以发现Pinctrl的方式,其设备树复杂,但API接口简单,只需要通过函数pinctrl_select_state选择设备树中某一配置即可。GPIO子系统的方式是设备树简单,但代码复杂,设备树中只配置了GPIO号,其余的如GPIO的方向及输出信号均需要通过代码来进行设置,这正是不同API接口及其设备树的优缺点。

3.GPIO调试

不同平台的调试方式可能会存在一些差异,比如MTK的不同平台间都会有差异,在此我就介绍一种常见的需要debugfs支持的方式。

我们在编译内核时,需要配置相应的debugfs宏来打开该功能,只有配置相应宏后,我们就可以进入机器的/sys/class/gpio下对GPIO口进行操作,下面是对应的操作顺序:

  • cd /sys/class/gpio/
  • echo 99 > export(此处99代表引脚号,确切地说echo时,应采用对应引脚gpio_request获得到的数值)
  • cd gpio99
  • echo in/out > direction //设置GPIO输入或输出
  • cat direction //获取GPIO输入输出状态
  • echo 0/1 > value //拉低或者拉高对应的GPIO口
  • cat value //查看GPIO口的高低状态

4.总结

希望大家通过本文,可以了解内核中GPIO的机制并掌握其操作方法,但需要说明的是在内核之前的阶段也会进行引脚的配置,比如我们使用串口来打印bootloader阶段的日志,这种情况下串口引脚一定是在内核之前的阶段进行配置的。

作者介绍

赵青窕,51CTO社区编辑,从事多年驱动开发。研究兴趣包含安全OS和网络安全领域,发表过网络相关专利。

责任编辑:武晓燕 来源: 51CTO技术栈
相关推荐

2020-10-14 09:41:02

Hi3861GPIO点灯

2022-03-15 15:00:59

Hi3861Pin接口鸿蒙

2020-11-23 13:09:42

HI3861

2021-11-12 11:28:01

2021-10-28 08:51:53

2020-11-03 11:39:22

wifi小车

2022-06-24 14:07:06

机器人供应链人工智能

2020-10-28 10:03:43

Hi3861 GPIO点灯按键

2021-12-15 08:49:21

2022-05-24 12:42:24

物联网

2022-03-01 15:54:38

智能开发鸿蒙创造性TV

2022-05-19 15:47:24

碰一碰连接设备开发鸿蒙

2022-06-23 12:03:00

网络安全网络安全事故

2022-06-13 08:18:02

操作系统CPU保护模式

2022-06-18 09:02:00

I2C 子系统通信协议

2021-06-21 10:26:09

2022-06-21 14:30:16

Vim自定义Linux

2022-06-20 12:07:32

SAP双碳碳足迹

2022-06-13 06:33:04

浏览器浏览器插件

2022-05-26 07:41:24

Ftp服务器

同话题下的热门内容

用这些开源工具在 Linux 上编辑 PDF 文件Docker Compose:搭建开发环境的好方式在 Linux 上玩电子游戏的三种方式为什么命令行在 Linux 生态系统中如此重要在 Linux 上使用 WineZGUI 运行 Windows 应用和游戏Linux 网络性能的 15 个优化建议如何构建自己的可引导 Linux Live CD用 Curtail GUI 应用轻松压缩 Linux 中的图像

编辑推荐

Linux系统下安装MySQL的步骤详解CentOS与Ubuntu有什么不同?Linux下如何使用minicom USB串口为什么你可能想要略过Ubuntu 17.04?Linux中7个判断文件系统类型的方法
我收藏的内容
点赞
收藏

51CTO技术栈公众号