pin是什么意思(守时atom Linux serial)
admin
2023-10-19 18:40:35

1)实验平台:正点atom Linux开发板

2)摘自 《正点原子I.MX6U嵌入式Linux驱动开发指南

关注官方微信号微信官方账号,获取更多信息:守时原子。

第四十五章pinctrl和gpio子系统实验

上一章我们写了基于设备树的LED驱动,但是驱动的本质没有变。都是配置LED灯使用的GPIO寄存器,驱动开发方法基本和裸机一样。Linux是一个庞大而完善的系统,尤其是驱动框架。不可能使用& quot原创& quot裸机驱动开发方法对于GPIO这种最基础的驱动来说,不然就相当于买了车天天推着上班。Linux内核为gpio驱动程序提供了pinctrl和gpio子系统。在本章中,我们将学习如何使用pinctrl和GPIO子系统简化GPIO驱动程序的开发。

45.1pinctrl子系统

45.1.1pinctrl子系统简介

Linux驱动注意驱动分离和分层。pinctrl和gpio子系统是驱动分离和分层的产物。驱动分离分层实际上是按照面向对象编程的设计思想设计的设备驱动框架。我们稍后将讨论驱动程序分离和分层。本来pinctrl和gpio子系统应该在驱动分离分层这一章后面说明,但是不管什么外设驱动,gpio驱动基本都是必须的,pinctrl和gpio子系统是GPIO驱动必须的,所以把pintrcl和GPIO子系统这一章提前了。

让我们回顾一下上一章中如何初始化LED灯使用的GPIO。步骤如下:

修改设备树,添加相应的节点。该节点的关键点是设置reg属性,包括GPIO相关寄存器。

.获取reg属性中两个寄存器(iomuxc _ SW _ mux _ CTL _ pad _ GPIO1_IO03和(iomuxc _ SW _ pad _ CTL _ pad _ gpio 1 _ IO03)的地址,初始化这两个寄存器,用于设置gpio 1 _ IO03引脚的复用功能、下拉和速度。

在中,GPIO1_IO03的引脚被重用为GPIO功能,因此需要设置GPIO1_IO03,即GPIO1_DR和GPIO1_GDIR寄存器。

综上所述,在中,完成了GPIO1_IO03引脚的初始化,设置了该引脚的复用功能、上拉和下拉。例如,GPIO_IO03的引脚被设置为GPIO功能。完成GPIO的初始化,并将GPIO设置为输入/输出。如果你用过STM32,应该记得STM32也是需要先设置一个管脚的复用功能、速度、上拉下拉,然后再设置该管脚对应的GPIO。其实对于大部分32位SOC来说,管脚设置基本就是这两个方面,所以Linux内核引入了pinctrl子系统进行管脚配置,gpio子系统进行GPIO配置。在本节中,我们将学习pinctrl子系统,然后在下一节中学习gpio子系统。

大多数SOC引脚都支持多路复用,比如I.MX6ULL的GPIO1_IO03,可以作为通用GPIO,也可以作为I2C1的SDA,等等。此外,我们还需要配置pin的电气特性,如上/下、速度、驱动能力等。传统的配置pin的方式是直接操作对应的寄存器,但是这种配置方式比较繁琐,容易出现问题(比如pin功能冲突)。为解决这一问题,引入了Pinctrl子系统。pinctrl子系统的主要工作如下:

获取设备树中的pin信息。

根据获取的pin信息设置pin的复用功能。

根据获取的引脚信息设置引脚的电气特性,如上/下、速度、驱动能力等。

对于我们的用户来说,只需要在设备树中设置一个引脚的相关属性,其他初始化工作由pinctrl子系统完成。pinctrl子系统的源代码目录是drivers/pinctrl。

45.1.2I.MX6ULL的pinctrl子系统驱动

1、PIN配置信息详解

要使用pinctrl子系统,我们需要在设备树中设置PIN的配置信息。毕竟pinctrl子系统要根据你提供的信息来配置PIN功能,一般会在设备树中创建一个节点来描述PIN的配置信息。打开imx6ull.dtsi文件,找到一个名为iomuxc的节点,如下图所示:

示例代码45.1.2.1iomuxc节点内容1

756 iomuxc:iomuxc @ 020 e 0000{

757兼容=' fsl,imx6ul-io muxc ';

=0x 020 e 00000 x 4000;

p>759};

iomuxc节点就是I.MX6ULL的IOMUXC外设对应的节点,看起来内容很少,没看出什么跟PIN的配置有关的内容啊,别急!打开imx6ull-alientek-emmc.dts,找到如下所示内容:

示例代码45.1.2.2iomuxc节点内容2

311&iomuxc{

312pinctrl-names="default";

313pinctrl-0=<&pinctrl_hog_1>;

314imx6ul-evk{

315pinctrl_hog_1:hoggrp-1{

316fsl,pins=<

317MX6UL_PAD_UART1_RTS_B__GPIO1_IO190x17059

318MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT0x17059

319MX6UL_PAD_GPIO1_IO09__GPIO1_IO090x17059

320MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID0x13058

321>;

322};

......

371pinctrl_flexcan1:flexcan1grp{

372fsl,pins=<

373MX6UL_PAD_UART3_RTS_B__FLEXCAN1_RX0x1b020

374MX6UL_PAD_UART3_CTS_B__FLEXCAN1_TX0x1b020

375>;

376};

......

587pinctrl_wdog:wdoggrp{

588fsl,pins=<

589MX6UL_PAD_LCD_RESET__WDOG1_WDOG_ANY0x30b0

590>;

591};

592};

593};

示例代码45.1.2.2就是向iomuxc节点追加数据,不同的外设使用的PIN不同、其配置也不同,因此一个萝卜一个坑,将某个外设所使用的所有PIN都组织在一个子节点里面。示例代码45.1.2.2中pinctrl_hog_1子节点就是和热插拔有关的PIN集合,比如USBOTG的ID引脚。pinctrl_flexcan1子节点是flexcan1这个外设所使用的PIN,pinctrl_wdog子节点是wdog外设所使用的PIN。如果需要在iomuxc中添加我们自定义外设的PIN,那么需要新建一个子节点,然后将这个自定义外设的所有PIN配置信息都放到这个子节点中。

将其与示例代码45.1.2.1结合起来就可以得到完成的iomuxc节点,如下所示:

示例代码45.1.2.3完整的iomuxc节点

1iomuxc:iomuxc@020e0000{

2compatible="fsl,imx6ul-iomuxc";

3reg=<0x020e00000x4000>;

4pinctrl-names="default";

5pinctrl-0=<&pinctrl_hog_1>;

6imx6ul-evk{

7pinctrl_hog_1:hoggrp-1{

8fsl,pins=<

9MX6UL_PAD_UART1_RTS_B__GPIO1_IO190x17059

10MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT0x17059

11MX6UL_PAD_GPIO1_IO09__GPIO1_IO090x17059

12MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID0x13058

13>;

......

16};

17};

18};

第2行,compatible属性值为“fsl,imx6ul-iomuxc”,前面讲解设备树的时候说过,Linux内核会根据compatbile属性值来查找对应的驱动文件,所以我们在Linux内核源码中全局搜索字符串“fsl,imx6ul-iomuxc”就会找到I.MX6ULL这颗SOC的pinctrl驱动文件。稍后我们会讲解这个pinctrl驱动文件。

第9~12行,pinctrl_hog_1子节点所使用的PIN配置信息,我们就以第9行的UART1_RTS_B这个PIN为例,讲解一下如何添加PIN的配置信息,UART1_RTS_B的配置信息如下:

MX6UL_PAD_UART1_RTS_B__GPIO1_IO190x17059

首先说明一下,UART1_RTS_B这个PIN是作为SD卡的检测引脚,也就是通过此PIN就可以检测到SD卡是否有插入。UART1_RTS_B的配置信息分为两部分:MX6UL_PAD_UART1_RTS_B__GPIO1_IO19和0x17059

我们重点来看一下这两部分是什么含义,前面说了,对于一个PIN的配置主要包括两方面,一个是设置这个PIN的复用功能,另一个就是设置这个PIN的电气特性。所以我们可以大胆的猜测UART1_RTS_B的这两部分配置信息一个是设置UART1_RTS_B的复用功能,一个是用来设置UART1_RTS_B的电气特性。

首先来看一下MX6UL_PAD_UART1_RTS_B__GPIO1_IO19,这是一个宏定义,定义在文件arch/arm/boot/dts/imx6ul-pinfunc.h中,imx6ull.dtsi会引用imx6ull-pinfunc.h这个头文件,而imx6ull-pinfunc.h又会引用imx6ul-pinfunc.h这个头文件(绕啊绕!)。从这里可以看出,可以在设备树中引用C语言中.h文件中的内容。MX6UL_PAD_UART1_RTS_B__GPIO1_IO19的宏定义内容如下:

示例代码45.1.2.4UART1_RTS_B引脚定义

190#defineMX6UL_PAD_UART1_RTS_B__UART1_DCE_RTS0x00900x031C0x06200x00x3

191#defineMX6UL_PAD_UART1_RTS_B__UART1_DTE_CTS0x00900x031C0x00000x00x0

192#defineMX6UL_PAD_UART1_RTS_B__ENET1_TX_ER0x00900x031C0x00000x10x0

193#defineMX6UL_PAD_UART1_RTS_B__USDHC1_CD_B0x00900x031C0x06680x20x1

194#defineMX6UL_PAD_UART1_RTS_B__CSI_DATA050x00900x031C0x04CC0x30x1

195#defineMX6UL_PAD_UART1_RTS_B__ENET2_1588_EVENT1_OUT0x00900x031C0x00000x40x0

196#defineMX6UL_PAD_UART1_RTS_B__GPIO1_IO190x00900x031C0x00000x50x0

197#defineMX6UL_PAD_UART1_RTS_B__USDHC2_CD_B0x00900x031C0x06740x80x2

示例代码45.1.2.4中一共有8个以“MX6UL_PAD_UART1_RTS_B”开头的宏定义,大家仔细观察应该就能发现,这8个宏定义分别对应UART1_RTS_B这个PIN的8个复用IO。查阅《I.MX6ULL参考手册》可以知UART1_RTS_B的可选复用IO如图45.1.2.1所示:


图45.1.2.1UART1_RTS_B引脚复用


示例代码196行的宏定义MX6UL_PAD_UART1_RTS_B__GPIO1_IO19表示将UART1_RTS_B这个IO复用为GPIO1_IO19。此宏定义后面跟着5个数字,也就是这个宏定义的具体值,如下所示:

0x00900x031C0x00000x50x0

这5个值的含义如下所示:

综上所述可知:

0x0090:mux_reg寄存器偏移地址,设备树中的iomuxc节点就是IOMUXC外设对应的节点,根据其reg属性可知IOMUXC外设寄存器起始地址为0x020e0000。因此0x020e0000+0x0090=0x020e0090,IOMUXC_SW_MUX_CTL_PAD_UART1_RTS_B寄存器地址正好是0x020e0090,大家可以在《IMX6ULL参考手册》中找到IOMUXC_SW_MUX_CTL_PAD_UART1_RTS_B这个寄存器的位域图,如图45.1.2.2所示:


图45.1.2.2寄存器位域图


因此可知,0x020e0000+mux_reg就是PIN的复用寄存器地址。

0x031C:conf_reg寄存器偏移地址,和mux_reg一样,0x020e0000+0x031c=0x020e031c,这个就是寄存器IOMUXC_SW_PAD_CTL_PAD_UART1_RTS_B的地址。

0x0000:input_reg寄存器偏移地址,有些外设有input_reg寄存器,有input_reg寄存器的外设需要配置input_reg寄存器。没有的话就不需要设置,UART1_RTS_B这个PIN在做GPIO1_IO19的时候是没有input_reg寄存器,因此这里intput_reg是无效的。

0x5:mux_reg寄存器值,在这里就相当于设置IOMUXC_SW_MUX_CTL_PAD_UART1_RTS_B寄存器为0x5,也即是设置UART1_RTS_B这个PIN复用为GPIO1_IO19。

0x0:input_reg寄存器值,在这里无效。

这就是宏MX6UL_PAD_UART1_RTS_B__GPIO1_IO19的含义,看的比较仔细的同学应该会发现并没有conf_reg寄存器的值,config_reg寄存器是设置一个PIN的电气特性的,这么重要的寄存器怎么没有值呢?回到示例代码45.1.2.3中,第9行的内容如下所示:

MX6UL_PAD_UART1_RTS_B__GPIO1_IO190x17059

MX6UL_PAD_UART1_RTS_B__GPIO1_IO19我们上面已经分析了,就剩下了一个0x17059,反应快的同学应该已经猜出来了,0x17059就是conf_reg寄存器值!此值由用户自行设置,通过此值来设置一个IO的上/下拉、驱动能力和速度等。在这里就相当于设置寄存器IOMUXC_SW_PAD_CTL_PAD_UART1_RTS_B的值为0x17059。

2、PIN驱动程序讲解

本小节会涉及到Linux驱动分层与分离、平台设备驱动等还未讲解的知识,所以本小节教程可以不用看,不会影响后续的实验。如果对Linux内核的pinctrl子系统实现原理感兴趣的话可以看本小节。

所有的东西都已经准备好了,包括寄存器地址和寄存器值,Linux内核相应的驱动文件就会根据这些值来做相应的初始化。接下来就找一下哪个驱动文件来做这一件事情,iomuxc节点中compatible属性的值为“fsl,imx6ul-iomuxc”,在Linux内核中全局搜索“fsl,imx6ul-iomuxc”字符串就会找到对应的驱动文件。在文件drivers/pinctrl/freescale/pinctrl-imx6ul.c中有如下内容:

示例代码45.1.2.5pinctrl-imx6ul.c文件代码段

326staticstructof_device_idimx6ul_pinctrl_of_match[]={

327{.compatible="fsl,imx6ul-iomuxc",.data=&imx6ul_pinctrl_info,},

328{.compatible="fsl,imx6ull-iomuxc-snvs",.data=&imx6ull_snvs_pinctrl_info,},

329{}

330};

331

332staticintimx6ul_pinctrl_probe(structplatform_device*pdev)

333{

334conststructof_device_id*match;

335structimx_pinctrl_soc_info*pinctrl_info;

336

337match=of_match_device(imx6ul_pinctrl_of_match,&pdev->dev);

338

339if(!match)

340return-ENODEV;

341

342pinctrl_info=(structimx_pinctrl_soc_info*)match->data;

343

344returnimx_pinctrl_probe(pdev,pinctrl_info);

345}

346

347staticstructplatform_driverimx6ul_pinctrl_driver={

348.driver={

349.name="imx6ul-pinctrl",

350.owner=THIS_MODULE,

351.of_match_table=of_match_ptr(imx6ul_pinctrl_of_match),

352},

353.probe=imx6ul_pinctrl_probe,

354.remove=imx_pinctrl_remove,

355};

第326~330行,of_device_id结构体数组,第四十三章讲解设备树的时候说过了,of_device_id里面保存着这个驱动文件的兼容性值,设备树中的compatible属性值会和of_device_id中的所有兼容性字符串比较,查看是否可以使用此驱动。imx6ul_pinctrl_of_match结构体数组一共有两个兼容性字符串,分比为“fsl,imx6ul-iomuxc”和“fsl,imx6ull-iomuxc-snvs”,因此iomuxc节点与此驱动匹配,所以pinctrl-imx6ul.c会完成I.MX6ULL的PIN配置工作。

第347~355行,platform_driver是平台设备驱动,这个是我们后面章节要讲解的内容,platform_driver是个结构体,有个probe成员变量。在这里大家只需要知道,当设备和驱动匹配成功以后platform_driver的probe成员变量所代表的函数就会执行,在353行设置probe成员变量为imx6ul_pinctrl_probe函数,因此在本章实验中imx6ul_pinctrl_probe这个函数就会执行,可以认为imx6ul_pinctrl_probe函数就是I.MX6ULL这个SOC的PIN配置入口函数。以此为入口,有如图45.1.2.3所示的函数调用路径:


图45.1.2.3imx6ul_pinctrl_probe函数执行流程


在图45.1.2.3中函数imx_pinctrl_parse_groups负责获取设备树中关于PIN的配置信息,也就是我们前面分析的那6个u32类型的值。处理过程如下所示:

示例代码45.1.2.6imx_pinctrl_parse_groups函数代码段

488

492#defineFSL_PIN_SIZE24

493#defineSHARE_FSL_PIN_SIZE20

494

495staticintimx_pinctrl_parse_groups(structdevice_node*np,

496structimx_pin_group*grp,

497structimx_pinctrl_soc_info*info,

498u32index)

499{

500intsize,pin_size;

501const__be32*list;

502inti;

503u32config;

......

537

538for(i=0;i<grp->npins;i++){

539u32mux_reg=be32_to_cpu(*list++);

540u32conf_reg;

541unsignedintpin_id;

542structimx_pin_reg*pin_reg;

543structimx_pin*pin=&grp->pins[i];

544

......

555

556pin_id=(mux_reg!=-1)?mux_reg/4:conf_reg/4;

557pin_reg=&info->pin_regs[pin_id];

558pin->pin=pin_id;

559grp->pin_ids[i]=pin_id;

560pin_reg->mux_reg=mux_reg;

561pin_reg->conf_reg=conf_reg;

562pin->input_reg=be32_to_cpu(*list++);

563pin->mux_mode=be32_to_cpu(*list++);

564pin->input_val=be32_to_cpu(*list++);

565

566

567config=be32_to_cpu(*list++);

568if(config&IMX_PAD_SION)

569pin->mux_mode|=IOMUXC_CONFIG_SION;

570pin->config=config&~IMX_PAD_SION;

......

574}

575

576return0;

577}

第496和497行,设备树中的mux_reg和conf_reg值会保存在info参数中,input_reg、mux_mode、input_val和config值会保存在grp参数中。

第560~564行,获取mux_reg、conf_reg、input_reg、mux_mode和input_val值。

第570行,获取config值。

接下来看一下函数pinctrl_register,此函数用于向Linux内核注册一个PIN控制器,此函数原型如下:

structpinctrl_dev*pinctrl_register(structpinctrl_desc*pctldesc,

structdevice*dev,

void*driver_data)

参数pctldesc非常重要,因为此参数就是要注册的PIN控制器,PIN控制器用于配置SOC的PIN复用功能和电气特性。参数pctldesc是pinctrl_desc结构体类型指针,pinctrl_desc结构体如下所示:

示例代码45.1.2.7pinctrl_desc结构体

128structpinctrl_desc{

129constchar*name;

130structpinctrl_pin_descconst*pins;

131unsignedintnpins;

132conststructpinctrl_ops*pctlops;

133conststructpinmux_ops*pmxops;

134conststructpinconf_ops*confops;

135structmodule*owner;

136#ifdefCONFIG_GENERIC_PINCONF

137unsignedintnum_custom_params;

138conststructpinconf_generic_params*custom_params;

139conststructpin_config_item*custom_conf_items;

140#endif

141};

第132~124行,这三个“_ops”结构体指针非常重要!!!因为这三个结构体就是PIN控制器的“工具”,这三个结构体里面包含了很多操作函数,通过这些操作函数就可以完成对某一个PIN的配置。pinctrl_desc结构体需要由用户提供,结构体里面的成员变量也是用户提供的。但是这个用户并不是我们这些使用芯片的程序员,而是半导体厂商,半导体厂商发布的Linux内核源码中已经把这些工作做完了。比如在imx_pinctrl_probe函数中可以找到如下所示代码:

示例代码45.1.2.8imx_pinctrl_probe函数代码段

648intimx_pinctrl_probe(structplatform_device*pdev,

649structimx_pinctrl_soc_info*info)

650{

651structdevice_node*dev_np=pdev->dev.of_node;

652structdevice_node*np;

653structimx_pinctrl*ipctl;

654structresource*res;

655structpinctrl_desc*imx_pinctrl_desc;

......

663

664imx_pinctrl_desc=devm_kzalloc(&pdev->dev,sizeof(*imx_pinctrl_desc),

665GFP_KERNEL);

666if(!imx_pinctrl_desc)

667return-ENOMEM;

......

705

706imx_pinctrl_desc->name=dev_name(&pdev->dev);

707imx_pinctrl_desc->pins=info->pins;

708imx_pinctrl_desc->npins=info->npins;

709imx_pinctrl_desc->pctlops=&imx_pctrl_ops;

710imx_pinctrl_desc->pmxops=&imx_pmx_ops;

711imx_pinctrl_desc->confops=&imx_pinconf_ops;

712imx_pinctrl_desc->owner=THIS_MODULE;

......

723ipctl->pctl=pinctrl_register(imx_pinctrl_desc,&pdev->dev,ipctl);

......

732}

第655行,定义结构体指针变量imx_pinctrl_desc。

第664行,向指针变量imx_pinctrl_desc分配内存。

第706~712行,初始化imx_pinctrl_desc结构体指针变量,重点是pctlops、pmxops和confops这三个成员变量,分别对应imx_pctrl_ops、imx_pmx_ops和imx_pinconf_ops这三个结构体。

第723行,调用函数pinctrl_register向Linux内核注册imx_pinctrl_desc,注册以后Linux内核就有了对I.MX6ULL的PIN进行配置的工具。

imx_pctrl_ops、imx_pmx_ops和imx_pinconf_ops这三个结构体定义如下:

示例代码45.1.2.9imx_pctrl_ops、imx_pmx_ops和imx_pinconf_ops结构体

174staticconststructpinctrl_opsimx_pctrl_ops={

175.get_groups_count=imx_get_groups_count,

176.get_group_name=imx_get_group_name,

177.get_group_pins=imx_get_group_pins,

178.pin_dbg_show=imx_pin_dbg_show,

179.dt_node_to_map=imx_dt_node_to_map,

180.dt_free_map=imx_dt_free_map,

181

182};

......

374staticconststructpinmux_opsimx_pmx_ops={

375.get_functions_count=imx_pmx_get_funcs_count,

376.get_function_name=imx_pmx_get_func_name,

377.get_function_groups=imx_pmx_get_groups,

378.set_mux=imx_pmx_set,

379.gpio_request_enable=imx_pmx_gpio_request_enable,

380.gpio_set_direction=imx_pmx_gpio_set_direction,

381};

......

481staticconststructpinconf_opsimx_pinconf_ops={

482.pin_config_get=imx_pinconf_get,

483.pin_config_set=imx_pinconf_set,

484.pin_config_dbg_show=imx_pinconf_dbg_show,

485.pin_config_group_dbg_show=imx_pinconf_group_dbg_show,

486};

示例代码45.1.2.9中这三个结构体下的所有函数就是I.MX6ULL的PIN配置函数,我们就此打住,不在去分析这些函数了,否则本章就没完没了了,有兴趣的可以去看一下。

45.1.3设备树中添加pinctrl节点模板

我们已经对pinctrl有了比较深入的了解,接下来我们学习一下如何在设备树中添加某个外设的PIN信息。关于I.MX系列SOC的pinctrl设备树绑定信息可以参考文档Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt。这里我们虚拟一个名为“test”的设备,test使用了GPIO1_IO00这个PIN的GPIO功能,pinctrl节点添加过程如下:

1、创建对应的节点

同一个外设的PIN都放到一个节点里面,打开imx6ull-alientek-emmc.dts,在iomuxc节点中的“imx6ul-evk”子节点下添加“pinctrl_test”节点,注意!节点前缀一定要为“pinctrl_”。添加完成以后如下所示:

示例代码45.1.2.10test设备pinctrl节点

1pinctrl_test:testgrp{

2

3};

2、添加“fsl,pins”属性

设备树是通过属性来保存信息的,因此我们需要添加一个属性,属性名字一定要为“fsl,pins”,因为对于I.MX系列SOC而言,pinctrl驱动程序是通过读取“fsl,pins”属性值来获取PIN的配置信息,完成以后如下所示:

示例代码45.1.2.11添加"fsl,pins"属性

1pinctrl_test:testgrp{

2fsl,pins=<

3

4>;

5};

3、在“fsl,pins”属性中添加PIN配置信息

最后在“fsl,pins”属性中添加具体的PIN配置信息,完成以后如下所示:

示例代码45.1.2.13完整的test设备pinctrl子节点

1pinctrl_test:testgrp{

2fsl,pins=<

3MX6UL_PAD_GPIO1_IO00__GPIO1_IO00config

4>;

5};

至此,我们已经在imx6ull-alientek-emmc.dts文件中添加好了test设备所使用的PIN配置信息。

45.2gpio子系统

45.2.1gpio子系统简介

上一小节讲解了pinctrl子系统,pinctrl子系统重点是设置PIN(有的SOC叫做PAD)的复用和电气属性,如果pinctrl子系统将一个PIN复用为GPIO的话,那么接下来就要用到gpio子系统了。gpio子系统顾名思义,就是用于初始化GPIO并且提供提供相应的API函数,比如设置GPIO为输入输出,读取GPIO的值等。gpio子系统的主要目的就是方便驱动开发者使用gpio,驱动开发者在设备树中添加gpio相关信息,然后就可以在驱动程序中使用gpio子系统提供的API函数来操作GPIO,Linux内核向驱动开发者屏蔽掉了GPIO的设置过程,极大的方便了驱动开发者使用GPIO。

45.2.2I.MX6ULL的gpio子系统驱动

1、设备树中的gpio信息

I.MX6ULL-ALPHA开发板上的UART1_RTS_B做为SD卡的检测引脚,UART1_RTS_B复用为GPIO1_IO19,通过读取这个GPIO的高低电平就可以知道SD卡有没有插入。首先肯定是将UART1_RTS_B这个PIN复用为GPIO1_IO19,并且设置电气属性,也就是上一小节讲的pinctrl节点。打开imx6ull-alientek-emmc.dts,UART1_RTS_B这个PIN的pincrtl设置如下:

示例代码45.2.2.1SD卡CD引脚PIN配置参数

316pinctrl_hog_1:hoggrp-1{

317fsl,pins=<

318MX6UL_PAD_UART1_RTS_B__GPIO1_IO190x17059

......

322>;

323};

第318行,设置UART1_RTS_B这个PIN为GPIO1_IO19。

pinctrl配置好以后就是设置gpio了,SD卡驱动程序通过读取GPIO1_IO19的值来判断SD卡有没有插入,但是SD卡驱动程序怎么知道CD引脚连接的GPIO1_IO19呢?肯定是需要设备树告诉驱动啊!在设备树中SD卡节点下添加一个属性来描述SD卡的CD引脚不就行了,SD卡驱动直接读取这个属性值就知道SD卡的CD引脚使用的哪个GPIO了。SD卡连接在I.MX6ULL的usdhc1接口上,在imx6ull-alientek-emmc.dts中找到名为“usdhc1”的节点,这个节点就是SD卡设备节点,如下所示:

示例代码45.2.2.2设备树中SD卡节点

760&usdhc1{

761pinctrl-names="default","state_100mhz","state_200mhz";

762pinctrl-0=<&pinctrl_usdhc1>;

763pinctrl-1=<&pinctrl_usdhc1_100mhz>;

764pinctrl-2=<&pinctrl_usdhc1_200mhz>;

765

766cd-gpios=<&gpio119GPIO_ACTIVE_LOW>;

767keep-power-in-suspend;

768enable-sdio-wakeup;

769vmmc-supply=<&reg_sd1_vmmc>;

770status="okay";

771};

第765行,此行本来没有,是作者添加的,usdhc1节点作为SD卡设备总节点,usdhc1节点需要描述SD卡所有的信息,因为驱动要使用。本行就是描述SD卡的CD引脚pinctrl信息所在的子节点,因为SD卡驱动需要根据pincrtl节点信息来设置CD引脚的复用功能等。762~764行的pinctrl-0~2都是SD卡其他PIN的pincrtl节点信息。但是大家会发现,其实在usdhc1节点中并没有“pinctrl-3=<&pinctrl_hog_1>”这一行,也就是说并没有指定CD引脚的pinctrl信息,那么SD卡驱动就没法设置CD引脚的复用功能啊?这个不用担心,因为在“iomuxc”节点下引用了pinctrl_hog_1这个节点,所以Linux内核中的iomuxc驱动就会自动初始化pinctrl_hog_1节点下的所有PIN。

第766行,属性“cd-gpios”描述了SD卡的CD引脚使用的哪个IO。属性值一共有三个,我们来看一下这三个属性值的含义,“&gpio1”表示CD引脚所使用的IO属于GPIO1组,“19”表示GPIO1组的第19号IO,通过这两个值SD卡驱动程序就知道CD引脚使用了GPIO1_IO19这GPIO。“GPIO_ACTIVE_LOW”表示低电平有效,如果改为“GPIO_ACTIVE_HIGH”就表示高电平有效。

根据上面这些信息,SD卡驱动程序就可以使用GPIO1_IO19来检测SD卡的CD信号了,打开imx6ull.dtsi,在里面找到如下所示内容:

示例代码45.2.2.2gpio1节点

504gpio1:gpio@0209c000{

505compatible="fsl,imx6ul-gpio","fsl,imx35-gpio";

506reg=<0x0209c0000x4000>;

507interrupts=<GIC_SPI66IRQ_TYPE_LEVEL_HIGH>,

508<GIC_SPI67IRQ_TYPE_LEVEL_HIGH>;

509gpio-controller;

510#gpio-cells=<2>;

511interrupt-controller;

512#interrupt-cells=<2>;

513};

gpio1节点信息描述了GPIO1控制器的所有信息,重点就是GPIO1外设寄存器基地址以及兼容属性。关于I.MX系列SOC的GPIO控制器绑定信息请查看文档Documentation/devicetree/bindings/gpio/fsl-imx-gpio.txt。

第505行,设置gpio1节点的compatible属性有两个,分别为“fsl,imx6ul-gpio”和“fsl,imx35-gpio”,在Linux内核中搜索这两个字符串就可以找到I.MX6UL的GPIO驱动程序。

第506行,的reg属性设置了GPIO1控制器的寄存器基地址为0X0209C000,大家可以打开《I.MX6ULL参考手册》找到“Chapter28:GeneralPurposeInput/Output(GPIO)”章节第28.5小节,有如图45.2.2.1所示的寄存器地址表:


图45.2.2.1GPIO1寄存器表


从图45.2.2.1可以看出,GPIO1控制器的基地址就是0X0209C000。

第509行,“gpio-controller”表示gpio1节点是个GPIO控制器。

第510行,“#gpio-cells”属性和“#address-cells”类似,#gpio-cells应该为2,表示一共有两个cell,第一个cell为GPIO编号,比如“&gpio13”就表示GPIO1_IO03。第二个cell表示GPIO极性如果为0的话表示高电平有效,如果为1的话表示低电平有效。

2、GPIO驱动程序简介

本小节会涉及到Linux驱动分层与分离、平台设备驱动等还未讲解的知识,所以本小节教程可以不用看,不会影响后续的实验。如果对Linux内核的GPIO子系统实现原理感兴趣的话可以看本小节。

gpio1节点的compatible属性描述了兼容性,在Linux内核中搜索“fsl,imx6ul-gpio”和“fsl,imx35-gpio”这两个字符串,查找GPIO驱动文件。drivers/gpio/gpio-mxc.c就是I.MX6ULL的GPIO驱动文件,在此文件中有如下所示of_device_id匹配表:

示例代码45.2.2.3mxc_gpio_dt_ids匹配表

152staticconststructof_device_idmxc_gpio_dt_ids[]={

153{.compatible="fsl,imx1-gpio",.data=&mxc_gpio_devtype[IMX1_GPIO],},

154{.compatible="fsl,imx21-gpio",.data=&mxc_gpio_devtype[IMX21_GPIO],},

155{.compatible="fsl,imx31-gpio",.data=&mxc_gpio_devtype[IMX31_GPIO],},

156{.compatible="fsl,imx35-gpio",.data=&mxc_gpio_devtype[IMX35_GPIO],},

157{}

158};

第156行的compatible值为“fsl,imx35-gpio”,和gpio1的compatible属性匹配,因此gpio-mxc.c就是I.MX6ULL的GPIO控制器驱动文件。gpio-mxc.c所在的目录为drivers/gpio,打开这个目录可以看到很多芯片的gpio驱动文件,“gpiolib”开始的文件是gpio驱动的核心文件,如图45.2.2.2所示:


图45.2.2.2gpio核心驱动文件


我们重点来看一下gpio-mxc.c这个文件,在gpio-mxc.c文件中有如下所示内容:

示例代码45.2.2.4mxc_gpio_driver结构体

496staticstructplatform_drivermxc_gpio_driver={

497.driver={

498.name="gpio-mxc",

499.of_match_table=mxc_gpio_dt_ids,

500},

501.probe=mxc_gpio_probe,

502.id_table=mxc_gpio_devtype,

503};

可以看出GPIO驱动也是个平台设备驱动,因此当设备树中的设备节点与驱动的of_device_id匹配以后probe函数就会执行,在这里就是mxc_gpio_probe函数,这个函数就是I.MX6ULL的GPIO驱动入口函数。我们简单来分析一下mxc_gpio_probe这个函数,函数内容如下:

示例代码45.2.2.5mxc_gpio_probe函数

403staticintmxc_gpio_probe(structplatform_device*pdev)

404{

405structdevice_node*np=pdev->dev.of_node;

406structmxc_gpio_port*port;

407structresource*iores;

408intirq_base;

409interr;

410

411mxc_gpio_get_hw(pdev);

412

413port=devm_kzalloc(&pdev->dev,sizeof(*port),GFP_KERNEL);

414if(!port)

415return-ENOMEM;

416

417iores=platform_get_resource(pdev,IORESOURCE_MEM,0);

418port->base=devm_ioremap_resource(&pdev->dev,iores);

419if(IS_ERR(port->base))

420returnPTR_ERR(port->base);

421

422port->irq_high=platform_get_irq(pdev,1);

423port->irq=platform_get_irq(pdev,0);

424if(port->irq<0)

425returnport->irq;

426

427

428writel(0,port->base+GPIO_IMR);

429writel(~0,port->base+GPIO_ISR);

430

431if(mxc_gpio_hwtype==IMX21_GPIO){

432

437irq_set_chained_handler(port->irq,mx2_gpio_irq_handler);

438}else{

439

440irq_set_chained_handler(port->irq,mx3_gpio_irq_handler);

441irq_set_handler_data(port->irq,port);

442if(port->irq_high>0){

443

444irq_set_chained_handler(port->irq_high,

445mx3_gpio_irq_handler);

446irq_set_handler_data(port->irq_high,port);

447}

448}

449

450err=bgpio_init(&port->bgc,&pdev->dev,4,

451port->base+GPIO_PSR,

452port->base+GPIO_DR,NULL,

453port->base+GPIO_GDIR,NULL,0);

454if(err)

455gotoout_bgio;

456

457port->bgc.gc.to_irq=mxc_gpio_to_irq;

458port->bgc.gc.base=(pdev->id<0)?of_alias_get_id(np,"gpio")

459*32:pdev->id*32;

460

461err=gpiochip_add(&port->bgc.gc);

462if(err)

463gotoout_bgpio_remove;

464

465irq_base=irq_alloc_descs(-1,0,32,numa_node_id());

466if(irq_base<0){

467err=irq_base;

468gotoout_gpiochip_remove;

469}

470

471port->domain=irq_domain_add_legacy(np,32,irq_base,0,

472&irq_domain_simple_ops,NULL);

473if(!port->domain){

474err=-ENODEV;

475gotoout_irqdesc_free;

476}

477

478

479mxc_gpio_init_gc(port,irq_base);

480

481list_add_tail(&port->node,&mxc_gpio_ports);

482

483return0;

......

494}

第405行,设备树节点指针。

第406行,定义一个结构体指针port,结构体类型为mxc_gpio_port。gpio-mxc.c的重点工作就是维护mxc_gpio_port,mxc_gpio_port就是对I.MX6ULLGPIO的抽象。mxc_gpio_port结构体定义如下:

示例代码45.2.2.6mxc_gpio_port结构体

61structmxc_gpio_port{

62structlist_headnode;

63void__iomem*base;

64intirq;

65intirq_high;

66structirq_domain*domain;

67structbgpio_chipbgc;

68u32both_edges;

69};

mxc_gpio_port的bgc成员变量很重要,因为稍后的重点就是初始化bgc。

继续回到mxc_gpio_probe函数函数,第411行调用mxc_gpio_get_hw函数获取gpio的硬件相关数据,其实就是gpio的寄存器组,函数mxc_gpio_get_hw里面有如下代码:

示例代码45.2.2.7mxc_gpio_get_hw函数

364staticvoidmxc_gpio_get_hw(structplatform_device*pdev)

365{

366conststructof_device_id*of_id=

367of_match_device(mxc_gpio_dt_ids,&pdev->dev);

368enummxc_gpio_hwtypehwtype;

......

383

384if(hwtype==IMX35_GPIO)

385mxc_gpio_hwdata=&imx35_gpio_hwdata;

386elseif(hwtype==IMX31_GPIO)

387mxc_gpio_hwdata=&imx31_gpio_hwdata;

388else

389mxc_gpio_hwdata=&imx1_imx21_gpio_hwdata;

390

391mxc_gpio_hwtype=hwtype;

392}

注意第385行,mxc_gpio_hwdata是个全局变量,如果硬件类型是IMX35_GPIO的话设置mxc_gpio_hwdat为imx35_gpio_hwdata。对于I.MX6ULL而言,硬件类型就是IMX35_GPIO,imx35_gpio_hwdata是个结构体变量,描述了GPIO寄存器组,内容如下:

示例代码45.2.2.8imx35_gpio_hwdata结构体

101staticstructmxc_gpio_hwdataimx35_gpio_hwdata={

102.dr_reg=0x00,

103.gdir_reg=0x04,

104.psr_reg=0x08,

105.icr1_reg=0x0c,

106.icr2_reg=0x10,

107.imr_reg=0x14,

108.isr_reg=0x18,

109.edge_sel_reg=0x1c,

110.low_level=0x00,

111.high_level=0x01,

112.rise_edge=0x02,

113.fall_edge=0x03,

114};

相关内容

热门资讯

金花创建房间/微信金花房卡怎么... 1.微信渠道:(荣耀联盟)大厅介绍:咨询房/卡添加微信:88355042 2.微信游戏中心:打开微...
金花房间卡/金花房卡如何购买/... 金花房间卡/金花房卡如何购买/新超圣金花房卡正版如何购买新超圣是一款非常受欢迎的游戏,咨询房/卡添加...
牛牛创建房间/金花房卡批发/神... 微信游戏中心:神牛大厅房卡在哪里买打开微信,添加客服【88355042】,进入游戏中心或相关小程序,...
链接牛牛/牛牛房卡游戏代理/鸿... 鸿运大厅房卡更多详情添加微:33549083、 2、在商城页面中选择房卡选项。 3、根...
科技实测!牛牛房卡怎么获得/乐... 微信游戏中心:乐酷大厅房卡在哪里买打开微信,添加客服【88355042】,进入游戏中心或相关小程序,...