0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

gpio和pinctrl子系统的关系与区别

FPGA之家 来源:CSDN技术社区 作者:CSDN技术社区 2022-03-15 11:40 次阅读

哈喽,我是老吴,我来继续分享我的学习心得啦。

gpio 和 pinctrl 子系统在内核里的使用率非常高,和嵌入式产品的关联非常大。从这两个子系统开始学习驱动开发是个不错的入门选择。

本文目录:

一、gpio与pinctrl
二、内核里如何引用gpio
三、gpio子系统框架
四、应用层如何访问gpio

一、gpio 与 pinctrl

本文主要关注 gpio 子系统,但是老吴认为必要先说明一下 pinctrl 子系统和 gpio 子系统的之间关系。

pinctrl 的作用:

  • 引脚复用,例如某个引脚即可用作为普通的gpio,也可以作为UART的TX;
  • 引脚配置,一般包括上下拉、驱动能力等;

3b067044-9199-11ec-952b-dac502259ad0.png

点击查看大图

gpio 的作用:

  • 作为输入功能时,支持读引脚值;
  • 作为输出功能时,支持输出高低电平;
  • 部分 gpio 还负责接收中断;

gpio 的使用依赖于 pinctrl:

3b1ae998-9199-11ec-952b-dac502259ad0.png

点击查看大图

本文的关注点是 gpio driver --> gpio subsystem core -> gpio consumer 这一路径,读者如果想更深入地了解 pinctrl 子系统,可以参考内核文档:Documentation/driver-api/pinctl.rst。

gpio 子系统内核文档:

Documentation/driver-api/gpio:

文档 简介
index.rst 文档目录和源码清单
intro.rst gpio 简介
driver.rst 描述如何编写 gpio controller driver
consumer.rst 描述 gpio consumer 如何使用 gpio
board.rst 描述设备如何申请 gpio
drivers-on-gpio.rst 列举一些使用了gpio子系统的常见驱动,例如 leds-gpio.c、gpio_keys.c 等
legacy.rst 描述 legacy gpio 接口

注:本文基于 Linux-4.19。

二、内核里如何引用 gpio

2 个步骤:

1) 设备树里添加 gpio mappings

示例:

foo_device {
    compatible = "packt,gpio-descriptor-sample";
    led-gpios = <&gpio2 15 GPIO_ACTIVE_HIGH>, // red 
                <&gpio2 16 GPIO_ACTIVE_HIGH>, // green 

    btn1-gpios = <&gpio2 1 GPIO_ACTIVE_LOW>;
    btn2-gpios = <&gpio2 1 GPIO_ACTIVE_LOW>;
};

要点:

  • 属性 -gpios 里的 由使用者自行决定的, 例如上述例子中的 为 led,在 gpio consumer driver 里可以通过 "led" 这个字符串,配合偏移值来获取这一组 gpio 里的任一 gpio。

  • 至于如何标志是硬件上的哪一个引脚,是由平台相关的 gpio controller driver 的设备树节点里的 #gpio-cells 的值来决定,上述例子中需要 2个参数才能确定硬件引脚,所以 #gpio-cells = 2

2) 在 gpio consumer driver 中引用

目前 gpio subsystem 提供了 2 套接口:

  • legacy API:integer-based GPIO interface,形式为 gpio_xxx(),例如 void gpio_set_value(unsigned gpio, int value),不推荐使用该 API;

  • 推荐 API: descriptor-based GPIO interface,形式为 gpiod_xxx(),例如 void gpiod_set_value(struct gpio_desc *desc, int value),新添加的驱动代码一律采用这套 API。

示例:

staticstructgpio_desc*red,*green,*btn1,*btn2;
staticintirq;

staticirqreturn_tbtn1_pushed_irq_handler(intirq,void*dev_id)
{
intstate;

/*readthebuttonvalueandchangetheledstate*/
state=gpiod_get_value(btn2);
gpiod_set_value(red,state);
gpiod_set_value(green,state);

pr_info("btn1interrupt:Interrupt!btn2stateis%d)
",state);
returnIRQ_HANDLED;
}

staticconststructof_device_idgpiod_dt_ids[]={
{.compatible="gpio-descriptor-sample",},
};


staticintmy_pdrv_probe(structplatform_device*pdev)
{
intretval;
structdevice*dev=&pdev->dev;

//获得gpiodescriptor的同时也将其设置为output,并且输出低电平
red=gpiod_get_index(dev,"led",0,GPIOD_OUT_LOW);
green=gpiod_get_index(dev,"led",1,GPIOD_OUT_LOW);

btn1=gpiod_get(dev,"btn1",GPIOD_IN);
btn2=gpiod_get(dev,"btn2",GPIOD_IN);

//获得中断号
irq=gpiod_to_irq(btn1);

//申请中断
retval=request_threaded_irq(irq,NULL,
btn1_pushed_irq_handler,
IRQF_TRIGGER_LOW|IRQF_ONESHOT,
"gpio-descriptor-sample",NULL);
pr_info("Hello!deviceprobed!
");
return0;
}

staticintmy_pdrv_remove(structplatform_device*pdev)
{
free_irq(irq,NULL);

//释放gpio
gpiod_put(red);
gpiod_put(green);
gpiod_put(btn1);
gpiod_put(btn2);
pr_info("goodbyereader!
");
return0;
}

staticstructplatform_drivermypdrv={
.probe=my_pdrv_probe,
.remove=my_pdrv_remove,
.driver={
.name="gpio_descriptor_sample",
.of_match_table=of_match_ptr(gpiod_dt_ids),
.owner=THIS_MODULE,
},
};
module_platform_driver(mypdrv);

gpiod_xxx() API

在 gpio 子系统中,用 struct gpio_desc 来描述一个 gpio 引脚,gpiod_xxx() 都是围绕着 strcut gpio_desc 进行操作的。

完整的接口定义位于 linux/gpio/consumer.h,大约共有 70个 API。

常用 API:

  • 获得/释放 一个或者一组 gpio:

    • [devm]_gpiod_get*()
    • [devm]_gpiod_put*()
  • 设置/查询 输入或者输出

    • gpiod_direction_input()
    • gpiod_direction_output()
    • gpiod_get_direction()
  • 读写一个 gpio

    • gpiod_get_value()
    • gpiod_set_value()
    • gpiod_get_value_cansleep()
    • gpiod_set_value_cansleep()
  • 读写一组 gpio

    • gpiod_get_array_value()
    • gpiod_set_array_value()
  • 获得 gpio 对应的中断号

    • gpiod_to_irq()

相关要点:

  • 以 _cansleep 为后缀的函数是可能会睡眠的 API,不可以在 hard (non-threaded) IRQ handlers 中使用;

  • gpiod_get_value() 返回的是硬件上的电平值;

  • gpiod_set_value() 设置的值是逻辑值而非电平值,1 表示使能,0 表示不使能,由设备树里的 gpio mappings 里的 GPIO_ACTIVE_XXX 来决定哪个电平值是有效的,总结如下:

Function line property physical line
gpiod_set_raw_value(desc, 0); don't care low
gpiod_set_raw_value(desc, 1); don't care high
gpiod_set_value(desc, 0); default (active high) low
gpiod_set_value(desc, 1); default (active high) high
gpiod_set_value(desc, 0); active low high
gpiod_set_value(desc, 1); active low low
gpiod_set_value(desc, 0); default (active high) low
gpiod_set_value(desc, 1); default (active high) high
gpiod_set_value(desc, 0); open drain low
gpiod_set_value(desc, 1); open drain high impedance
gpiod_set_value(desc, 0); open source high impedance
gpiod_set_value(desc, 1); open source high

三、gpio 子系统框架

1. 整体框架

3b44b3f4-9199-11ec-952b-dac502259ad0.png

点击查看大图

正常情况下,驱动工程师不需要了解 gpio chip driver 和 gpiolib:

  • 驱动工程师负责编写 gpio consumer drvier;

  • 芯片厂商的 bsp 工程师负责编写 gpio chip driver;

  • 开源社区里的大牛负责 gpiolib 的核心实现;

但是当功能和预期的不一样时,为了调试定位出问题,这时就有必要弄清楚 gpio chip driver 和 gpiolib 的工作流程。

2. gpiolib

作用:

  • 向下为 gpio chip driver 提供注册 struct gpio_chip 的接口:gpiochip_xxx();

  • 向上为 gpio consumer 提供引用 gpio 的接口:gpiod_xxx();

  • 实现字符设备的功能;

  • 注册 sysfs;

源码:

$cdlinux-4_19/drivers/gpio
$lsgpiolib*-1X
gpiolib-acpi.c//ACPIhelpersforGPIOAPI
gpiolib.c//GPIOsubsystemcore
gpiolib-devprop.c//DevicepropertyhelpersforGPIOchips.
gpiolib-legacy.c
gpiolib-of.c//OFhelpersfortheGPIOAPI
gpiolib-sysfs.c//sysfshelpers
gpiolib.h

int gpiochip_add(struct gpio_chip *chip)

这是 bsp 工程师比较关心的 api。

在 gpio 子系统中,SoC 上的每一个 gpio bank 都会被认为是一个 gpio controller,每一个 gpio controller 都由一个 struct gpio_chip 来描述,bsp 工程师的核心工作就是填充该结构体。

struct gpio_chip 比较庞大,但是我们只需要关注跟硬件联系比较紧密的成员就好:

  • .set(),输出电平
  • .get(),获得电平
  • .get_direction(),获得方向
  • .direction_input(),设置为输入
  • .direction_output(),设置为输出
  • .to_irq(),获得中断号

3. gpio chip driver

最简单的 demo:

#defineGPIO_NUM16
staticstructgpio_chipchip;

staticintfake_get_value(structgpio_chip*gc,unsignedoffset)
{
return0;
}

staticvoidfake_set_value(structgpio_chip*gc,unsignedoffset,intval)
{
}

staticintfake_direction_output(structgpio_chip*gc,unsignedoffset,intval)
{
return0;
}

staticintfake_direction_input(structgpio_chip*gc,unsignedoffset)
{
return0;
}

staticconststructof_device_idfake_gpiochip_ids[]={
{.compatible="fake-gpio-chip",},
};

staticintmy_pdrv_probe(structplatform_device*pdev)
{
chip.label=pdev->name;
chip.base=-1;
chip.dev=&pdev->dev;
chip.owner=THIS_MODULE;
chip.ngpio=GPIO_NUM;
chip.can_sleep=1;
chip.get=fake_get_value;
chip.set=fake_set_value;
chip.direction_output=fake_direction_output;
chip.direction_input=fake_direction_input;

returngpiochip_add(&chip);
}

staticintmy_pdrv_remove(structplatform_device*pdev)
{
gpiochip_remove(&chip);
return0;
}

staticstructplatform_drivermypdrv={
.probe=my_pdrv_probe,
.remove=my_pdrv_remove,
.driver={
.name="fake-gpio-chip",
.of_match_table=of_match_ptr(fake_gpiochip_ids),
.owner=THIS_MODULE,
},
};
module_platform_driver(mypdrv);

RK3399 实例分析

1) 设备树

gpio0: gpio0@ff720000 {
    compatible = "rockchip,gpio-bank";
    reg = <0x0 0xff720000 0x0 0x100>;
    clocks = <&pmucru PCLK_GPIO0_PMU>;
    interrupts = ;

    gpio-controller;
    #gpio-cells = <0x2>;

    interrupt-controller;
    #interrupt-cells = <0x2>;
};
...
gpio4: gpio4@ff790000 {
    ...
}

一共定义了 5 个 gpio-controller 节点,对应芯片上的 5 个 gpio bank。

里面用于表明寄存器地址 和 clock 等属性会在 gpio chip driver 中被使用。

2) chip driver

这里只关心程序主干,不关心芯片厂商为了适应多款芯片而封装的代码。

gpio_chip 的注册过程:

drivers/pinctrl/pinctrl-rockchip.c

rockchip_pinctrl_probe(){
//rockchip用于管理gpio/pinctrl大管家结构体
structrockchip_pinctrl*info=devm_kzalloc()

//rk3399gpiobank相关的硬件描述信息
structrockchip_pin_ctrl*ctrl=&rk3399_pin_ctrl;

info->ctrl=ctrl;

rockchip_gpiolib_register(pdev,info);{
structgpio_chip*gc;

for(i=0;i< ctrl->nr_banks;++i,++bank){
//初始化gpio_chip
gc=&rockchip_gpiolib_chip;
gc->base=bank->pin_base;
gc->ngpio=bank->nr_pins;

//注册gpio_chip
gpiochip_add_data(gc,bank);
}
}
}

struct gpio_chip 的定义:

staticconststructgpio_chiprockchip_gpiolib_chip={
.request=gpiochip_generic_request,
.free=gpiochip_generic_free,
.set=rockchip_gpio_set,
.get=rockchip_gpio_get,
.get_direction=rockchip_gpio_get_direction,
.direction_input=rockchip_gpio_direction_input,
.direction_output=rockchip_gpio_direction_output,
.set_config=rockchip_gpio_set_config,
.to_irq=rockchip_gpio_to_irq,
.owner=THIS_MODULE,
};

这些函数都是在操作 rk3399 gpio 相关的寄存器,实现一个 gpio chip driver 本质上就是实现上面一系列的硬件操作函数。

四、应用层如何访问 gpio

1. /dev/gpiochipX

直接操作字符设备是比较低效率的,内核里提供了一些 demo:

$cdlinux-4_19/tools/gpio
$ls
Makefile
gpio-event-mon.c
gpio-hammer.c
gpio-utils.c
lsgpio.c
gpio-utils.h
$makeARCH=arm64CROSS_COMPILE=aarch64-linux-

具体的代码请各位自行阅读吧。

2. libgpiod

libgpiod 是一个用 C 语言编写的用于访问 gpio chardev 的库,同时里面包含了一些访问 gpio 的命令行工具,推荐优先采用这个库来访问 gpio。

编译:

$gitclonehttps://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git-bv1.6.x
$./autogen.sh--enable-tools=yes
$make&&makeinstall
$ldconfig

附带的几个命令行工具:

  • gpiodetect – list all gpiochips present on the system, their names, labels and number of GPIO lines

  • gpioinfo – list all lines of specified gpiochips, their names, consumers, direction, active state and additional flags

  • gpioget – read values of specified GPIO lines

  • gpioset – set values of specified GPIO lines, potentially keep the lines exported and wait until timeout, user input or signal

  • gpiofind – find the gpiochip name and line offset given the line name

  • gpiomon – wait for events on GPIO lines, specify which events to watch, how many events to process before exiting or if the events should be reported to the console

$gpiodetect
gpiochip0[gpio0](32lines)
gpiochip1[gpio1](32lines)
gpiochip2[gpio2](32lines)
gpiochip3[gpio3](32lines)
gpiochip4[gpio4](32lines)
$gpioinfogpio0
gpiochip0-32lines:
line0:unnamedunusedinputactive-high
line1:unnamed"vcc_sd"outputactive-high[used]
line2:unnamedunusedinputactive-high
line3:unnamedunusedinputactive-high
line4:unnamed"bt_default_wake_host"inputactive-high[used]
line5:unnamed"GPIOKeyPower"inputactive-low[used]
...
line13:unnamed"status_led"outputactive-high[used]
...
line30:unnamedunusedinputactive-high
line31:unnamedunusedinputactive-high

3. sysfs

过时的接口,不推荐使用,用法如下:

$echo25>/sys/class/gpio/export
$echoout>/sys/class/gpio/gpio25/direction
$echo1>/sys/class/gpio/gpio25/value

五、相关参考

  • Linux-4.19 Documentation

  • Linux Device Drivers Development / GPIO Controller Drivers

原文标题:五、相关参考

文章出处:【微信公众号:FPGA之家】欢迎添加关注!文章转载请注明出处。

审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 嵌入式
    +关注

    关注

    4982

    文章

    18281

    浏览量

    288430
  • 内核
    +关注

    关注

    3

    文章

    1309

    浏览量

    39846
  • GPIO
    +关注

    关注

    16

    文章

    1133

    浏览量

    50570
收藏 人收藏

    评论

    相关推荐

    一文搞懂Linux pinctrl/gpio子系统

    GPIO的寄存器操作。分享给刚刚接触外设bsp的小伙伴们。当然后面有时间还会分享GPIO子系统框架和pinctrl子系统框架,先知道黑盒怎么
    发表于 06-09 09:52 1519次阅读

    「正点原子Linux连载」第四十五章 pinctrlgpio子系统实验(一)

    1)实验平台:正点原子Linux开发板2)摘自《正点原子I.MX6U嵌入式Linux驱动开发指南》关注官方微信号公众号,获取更多资料:正点原子第四十五章 pinctrlgpio子系统实验上一章我们
    发表于 03-19 14:58

    「正点原子Linux连载」第四十五章 pinctrlgpio子系统实验(二)

    =5};第4行,test设备所使用的gpio。关于pinctrl子系统gpio子系统就讲解到这里,接下来就使用
    发表于 03-19 14:59

    基于GPIO子系统的LED驱动程序分享

    Pinctrl 子系统把引脚的复用、配置抽出来,做成 Pinctrl 子系统,给 GPIO、I2C 等模块使用。让我们在使用某个引脚功能时不
    发表于 12-16 07:16

    怎样去使用linux下的pintcrl和gpio子系统

    pinctrlgpio内部的原理是如何实现的?怎样去使用linux下的pintcrl和gpio子系统呢?
    发表于 03-07 13:38

    RK3399开发板的pinctrlgpio子系统相关资料介绍

    1、RK3399—pinctrlgpio子系统  记得以前在学习51单片机时,要控制一个GPIO输出高、低电平,就得根据对应IO寄存器每一bit的作用和含义进行配置,时钟、速率、方向
    发表于 09-16 17:27

    简析RK3399 pinctrl子系统的使用及重要的数据结构

    1、rk3399 pinctrl子系统的使用及重要的数据结构  作用  引脚的枚举与命名  引脚复用  引脚的配置  使用示例  pincontroller设备树信息  pinctrl
    发表于 11-09 18:04

    更新 | 持续开源 迅为RK3568驱动指南第十一篇-pinctrl子系统

    _平台总线 第七期_设备树 第八期_设备树插件 第九期_设备模型 第十期_热插拔 第十一期_pinctrl子系统 第十二期_GPIO子系统 未完待续,持续更新中...
    发表于 10-18 11:12

    嵌入式驱动开发两大子系统的使用

    本文的关注点是 gpio driver --> gpio subsystem core -> gpio consumer 这一路径,读者如果想更深入地了解 pinctrl
    的头像 发表于 03-15 13:41 1545次阅读

    【i.MX6ULL】驱动开发6——GPIO子系统点亮LED

    本篇介绍了使用**Pinctrl子系统GPIO子系统**的方式来点亮LED,与之前的寄存器版点亮LED与设备树版点亮LED的最大区别在于不
    的头像 发表于 05-21 21:50 2849次阅读
    【i.MX6ULL】驱动开发6——<b class='flag-5'>GPIO</b><b class='flag-5'>子系统</b>点亮LED

    使用pinctrlgpio子系统实现LED灯驱动

    前边已经学了两种点灯,本质依然还是通过配置寄存器;在学习STM32的时候除了学习配置一下寄存器,基本都是使用库来开发,那么在i.MX6ULL还使用寄存器开发明显是不太适合,那么i.MX6ULL有更方便的开发呢,这篇就来学习一下使用 pinctrlgpio
    的头像 发表于 04-03 10:17 821次阅读

    RK3568pinctrlgpio 子系统详解

    如果 pinctrl 子系统将 PIN 复用为 GPIO,那么接下来就要配置 gpio 子系统,且 gp
    的头像 发表于 12-20 10:22 2093次阅读
    RK3568<b class='flag-5'>pinctrl</b> 和 <b class='flag-5'>gpio</b> <b class='flag-5'>子系统</b>详解

    gpio子系统API详解

    gpio 子系统 API gpio 子系统中操作一个 GPIO 需要如下几步: 1、of_find_compatible_node2、of_
    的头像 发表于 09-27 17:17 471次阅读
    <b class='flag-5'>gpio</b><b class='flag-5'>子系统</b>API详解

    Linux中pinctrl操作GPIO只需要几步

    pinctrl 子系统 API pinctrl 子系统的 API 有很多,对于驱动工程师来说,pinctrl 操作一个
    的头像 发表于 09-27 17:24 1222次阅读

    瑞芯微RK3568-iomuxc和pinctrl子系统初窥

    pinctrl子系统作用:从设备树中获取PIN的描述信息来设置PIN的复用和电气属性,PIN可复用为I2C、SPI、GPIOgpio子系统
    发表于 12-20 10:10 54次下载