【linux-IMX6ULL-pinctrl和gpio子系统】

1. pinctrl子系统

1.1 pinctrl子系统简介

  pinctrl和gpio子系统就是驱动分离与分层思想下的产物,驱动分离与分层其实就是按照面向对象编程的设计思想而设计的设备驱动框架;
  在我的上篇博客利用设备树点灯中:链接,我点灯的流程就是把控制引脚gpio1_io03的相关初始化需要的寄存器地址放到了设备树reg中,比如gpio的地址信息等。但是我在进行驱动编写时,我假装不知道这些寄存器的地址,然后从设备树中进行获取这些寄存器的地址,获取到地址后进行寄存器配置初始化的一系列的操作,就像在裸机开发中对于寄存器的操作一样,而且我们每使用一个引脚就要进行一系列的初始化和复用,这显然是效率低下的。
  对比图如下:左边是方式1,右边是方式2,这里只是按照gpio的驱动流程进行简要归类,实际上pinctr子系统和gpio的子系统之间的联系是比较紧密的,而且内容也是比较复杂的;

  而且对于引脚控制而言,复用成什么模式是固定的,数据手册已经清楚的列写出来了,也就是我们可以提前把一个引脚的所有的复用模式给列选出来只能进行固定选择,只是把引脚的电气属性的配置权力下放到了开发人员,其他的就是固定模式的选择,基本没啥变化,而且不容易出错,因为这些子系统是芯片厂商BSP工程师都封装好了。

1.2 pinctrl子系统使用

  传统的配置 pin 的方式就是直接操作相应的寄存器,但是这种配置方式比较繁琐、而且容易出问题(比如 pin 功能冲突)。pinctrl 子系统就是为了解决这个问题而引入的,pinctrl 子系统主要工作内容如下:其中使用pinctrl子系统主要是在设备树中进行配置:

  1. 获取设备树中 pin 信息
  2. 根据获取到的 pin 信息来设置 pin 的复用功能
  3. 根据获取到的 pin 信息来设置 pin 的电气特性,比如上/下拉、速度、驱动能力等

  对于我们使用者来讲,只需要在设备树里面设置好某个 pin 的相关属性即可,其他的初始化工作均由 pinctrl 子系统来完成,pinctrl 子系统源码目drivers/pinctrl。

1.2.1 追加pin节点

&esmp; 我们这里控制LED的是引IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03,这里简写成IO3,我们要添加这个IO3的的pin信息,那么如下面代码所示,我们为什么要向iomuxc 中追加fsl,pins信息,而不是在其他的父节点进行追加呢?

//imx6ull.dtsi
iomuxc: iomuxc@020e0000 {
				compatible = "fsl,imx6ul-iomuxc";
				reg = <0x020e0000 0x4000>;
			};
...........................(省略)
//imx6ull-14x14-evk.dts
&iomuxc {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_hog_1>;
	imx6ul-evk {
		pinctrl_gpioled: ledgrp{
			fsl,pins = <
				MX6UL_PAD_GPIO1_IO03__GPIO1_IO03		0x10B0 /* LED-CTRL */
			>;
		};

  这里我们看一下GPIO1_IO03的数据手册:可以看到对于IO3的基地址是0x0x020E0000 ,我们再看一下对于追加的父节点:iomuxc中的reg描述信息:reg = <0x020e0000 0x4000>;其中第一个是不是也有一个0x020e0000,这就是我们为什么要向iomuxc 父节点追加pin节点,对于fsl,pins中的引脚信息,每个引脚都有唯一的偏移量而且已经宏定义好了,当我们使用这个节点时,系统会自动进行基地址+偏移地址然后计算出来IO03的实际地址,然后把后面的0x10B电气属性值赋值给这个寄存器的地址,然后进行一系列的配置

1.2.2 配置引脚信息节点

  当我们追加好了pin节点信息,且配置好了电气属性,接下来就是在设备树中再加上引脚的节点信息,添加引脚节点的信息是为了方便再驱动中进行信息的获取:添加的信息如下,这里添加的主要就是多了一条非常重要的gpio的信息:
led-gpios=<&gpio1 3 GPIO_ACTIVE_LOW>;

	pinctrl-names = "default";
		pinctrl-0 =  <&pinctrl_gpioled>;
		status = "okay";
		led-gpios=<&gpio1 3 GPIO_ACTIVE_LOW>;
	};

  我们上一步已经添加好了pin配置节点,主要目的就是把MX6UL_PAD_GPIO1_IO03引脚复用为:GPIO1_IO03模式,但是我们如何在驱动中把这些配置信息触发生效呢?于是我们就是进行了节点配置,并把所有的配置信息返回映射成一个GPIO号led-gpios,我们在驱动中获取这个gpio号,拿到这个gpio号我们就可以把上面的所有配置信息给正式生效,也就是通过下面的gpio子系统对gpio号进行申请,看看有没有其他设备占用这个gpio,如果没有其他设备占用这个gpio,就可以通过映射的gpio号进行pinctrcl的控制;

2. GPIO子系统

2.1 GPIO子系统简介

  pinctrl子系统的主要功能是设置pin(或PAD)的复用和电气属性,当复用为GPIO的模式时,就要选用GPIO子系统进行调用。gpio 子系统顾名思义,就是用于初始化 GPIO 并且提供相应的 API 函数,比如设置 GPIO为输入输出,读取 GPIO 的值等。gpio 子系统的主要目的就是方便驱动开发者使用 gpio,驱动开发者在设备树中添加 gpio 相关信息,然后就可以在驱动程序中使用 gpio 子系统提供的 API函数来操作 GPIO,Linux 内核向驱动开发者屏蔽掉了 GPIO 的设置过程,极大的方便了驱动开发者使用 GPIO;

2.1 gpio子系统API函数

  对于GPIO子系统的主要作用是通过API函数进行,相关功能的设置和读取,比较重要的API函数如下:

  • int gpio_request(unsigned gpio, const char *label)
  • void gpio_free(unsigned gpio)
  • int gpio_direction_input(unsigned gpio)
  • int gpio_direction_output(unsigned gpio, int value)
  • #define gpio_get_value __gpio_get_value
  • int __gpio_get_value(unsigned gpio)
  • #define gpio_set_value __gpio_set_value
  • void __gpio_set_value(unsigned gpio, int value)

  与gpio相关的of函数如下:

  • int of_gpio_named_count(struct device_node *np, const char *propname)
  • int of_gpio_count(struct device_node *np)
  • int of_get_named_gpio(struct device_node *np,const char *propname,int index)

3. 检查 PIN 是否被其他外设使用

  这一点非常重要,要检测很多初次接触设备树的驱动开发人员很容易因为这个小问题栽了大跟头!因为我们所使用的设备树基本都是在半导体厂商提供的设备树文件基础上修改而来的,而半导体厂商提供的设备树是根据自己官方开发板编写的,很多 PIN 的配置和我们所使用的开发板不一样。比如 A 这个引脚在官方开发板接的是 I2C 的 SDA,而我们所使用的硬件可能将 A 这个引脚接到了其他的外设,比如 LED 灯上,接不同的外设,A 这个引脚的配置就不同。一个引脚一次只能实现一个功能,如果 A 引脚在设备树中配置为了 I2C 的 SDA 信号,那么 A 引脚就不能再配置为 GPIO,否则的话驱动程序在申请 GPIO 的时候就会失败

  • 30
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值