Linux驱动开发(十一):pinctrl子系统和GPIO子系统

pinctrl子系统和GPIO子系统

  • 简介
  • pinctrl子系统
    • 概况
    • 属性
      • 恩智浦IMX6
      • 三星4412
      • 调用pinctrl
    • 配置流程
  • GPIO子系统
    • 概括
    • 系统框图
    • 与pinctrl比较
    • 关系
  • 总结

简介

配置寄存器来控制IO的方式太过于原始,Linux内核提供了pinctrl子系统和gpio子系统用于GPIO驱动,当然pinctrl子系统负责的就不仅仅是GPIO的驱动了而是所有pin脚的配置。
pinctrl子系统是随着设备树的加入而加入的,依赖于设备树。GPIO子系统在之前的内核中也是存在的,但是pinctrl子系统的加入GPIO子系统也是有很大的改变,之前的GPIO子系统需要芯片厂商提供的mach文件,而加入设备术后,GPIO子系统使用设备树来实现。
接下来我们就分别来看一下pinctrl子系统和GPIO子系统

pinctrl子系统

概况

大多数SOC的PIN都是支持复用的,所以在配置时要考虑复用的设置,此外还要配置PIN的电气特性,比如上下拉、速度、驱动等
pinctrl子系统的主要工作内容:

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

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

属性

各厂家SOC的属性不一定完全相同,要根据手册来查看语法相关的信息

恩智浦IMX6

手册参考\Documentation\devicetree\bindings\pinctrl\fsl,imx-pinctrl.txt
它的语法是这样的

Examples: usdhc@0219c000 { /* uSDHC4 */ non-removable;
	vmmc-supply = <&reg_3p3v>;
	status = "okay";
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_usdhc4_1>; };
iomuxc@020e0000 { compatible = "fsl,imx6q-iomuxc";
	reg = <0x020e0000 0x4000>;
	/* shared pinctrl settings */
	usdhc4 { pinctrl_usdhc4_1: usdhc4grp-1 { 
		fsl,pins = < 
				MX6QDL_PAD_SD4_CMD__SD4_CMD    0x17059
				MX6QDL_PAD_SD4_CLK__SD4_CLK    0x10059 
				MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059
				MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059 
				MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059
				MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059 
				MX6QDL_PAD_SD4_DAT4__SD4_DATA4 0x17059
				MX6QDL_PAD_SD4_DAT5__SD4_DATA5 0x17059 
				MX6QDL_PAD_SD4_DAT6__SD4_DATA6 0x17059
				MX6QDL_PAD_SD4_DAT7__SD4_DATA7 0x17059 
				>; 
			};
	.... };

IMX6的pinctrl写在iomuxc节点下面,它的名字“pinctrl_usdhc4_1“会在别的节点中被调用
对于PIN的配置分为两个部分“MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059”

  • MX6QDL_PAD_SD4_CMD__SD4_CMD这部分配置的是引脚的复用
  • 0x17059 这部分是对引脚的初始化,配置速度、上下拉、驱动能力等

MX6QDL_PAD_SD4_CMD__SD4_CMD,定义在\arch\arm\boot\dts\imx6ul-pinfunc.h
Linux驱动开发(十一):pinctrl子系统和GPIO子系统_第1张图片
文件中的每一个宏定义是某一个引脚的某一个复用功能
IMX6实现IO复用的使用IOMUXC来实现的,我们看两个寄存器
Linux驱动开发(十一):pinctrl子系统和GPIO子系统_第2张图片
我们可以通过配置MUX_MODE来实现IO的复用,一个引脚最多可以配置九种功能
还有一个寄存器用来对IO初始化
Linux驱动开发(十一):pinctrl子系统和GPIO子系统_第3张图片GPIO的框图如下:
Linux驱动开发(十一):pinctrl子系统和GPIO子系统_第4张图片
HYS:迟滞比较器,IO作为输入功能时有效,用于设置输入接收器的施密特触发器是否使能,用于输入波形整型
PUS:设置上下拉电阻
PUE:上下拉还是状态保持,保持就是断电保持以前的状态
PKE:用来使能或者静止上下拉/状态保持器功能
ODE:禁止或使能开路输出
SPEED:速度
DSE:驱动能力
SRE:压摆率,就是IO电平跳变所需要的时间,要过EMC可以使用低压摆率,波形缓和,要进行高速通信可以使用高压摆率

我们以一个为例

#define MX6UL_PAD_GPIO1_IO00__I2C2_SCL                            0x005C 0x02E8 0x05AC 0x0 0x1

它的后面有五个值,这五个值分别是

<mux_reg conf_reg input_reg mux_mode input_val>
  • mux_reg为复用配置寄存器偏移地址
  • conf_reg为初始化配置寄存器的偏移地址
  • input_reg 有的外设有input_reg,有的话需要配置这个偏移地址
  • mux_mode 设置复用模式
  • input_val 输入值

在使用它的时候后面还会再跟一个值

MX6QDL_PAD_SD4_CMD__SD4_CMD    0x17059

后面的值0x17059就是对IO的初始化

通过以上的操作我们就可以完成对一个引脚的复用配置以及初始化

三星4412

三星的配置就与恩智浦的配置不太一样
三星的一般有一个xxx-pinctrl.dtsi文件,专门进行引脚的配置
例子如下:

pinctrl_0: pinctrl@11400000 {
	gpa0: gpa0 {
		gpio-controller;
		#gpio-cells = <2>;

		interrupt-controller;
		#interrupt-cells = <2>;
	};
...
};
uart1_data: uart1-data {
	samsung,pins = "gpa0-4", "gpa0-5";
	samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
	samsung,pin-pud = <EXYNOS_PIN_PULL_NONE>;
	samsung,pin-drv = <EXYNOS4_PIN_DRV_LV1>;
};

Pin mux/config
这个是关键,选择复用(pin function mode)以及初始化(上下拉、驱动能力)

  • property name : “samsung,pins”
  • pin names format: “[pin bank name]-[pin number within the bank]”,例:gpa0-0
  • 管脚复用属性:“samsung,pin-function”
  • 属性配置:
    samsung,pin-val: Initial value of pin output buffer.
    samsung,pin-pud: Pull up/down configuration.
    samsung,pin-drv: Drive strength configuration.
    samsung,pin-pud-pdn: Pull up/down configuration in power down mode.
    samsung,pin-drv-pdn: Drive strength configuration in power down mode.

调用pinctrl

调用pinctrl一般是在设备树中进行的
各种板子的语法是差不多的,例子如下

gpioled {
		#address-cells = <1>; 
		#size-cells = <1>; 
		compatible = "atkalpha-gpioled"; 
		pinctrl-names = "default"; //关注
		pinctrl-0 = <&pinctrl_gpio_leds>; //关注
		led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>; 
		status = "okay";
	};

我们只关注两句,这两句的意思就是引用我们前面配置的pinctrl节点
这样如果你的引脚配置为GPIO的话就可以设置GPIO属性并在驱动中使用了

配置流程

想要配置某个外设而需要配置某一个引脚为GPIO时,一般的配置流程是这样的

  • 在IOMUXC/pinctrl中对某一个引脚进行配置,两个部分(复用、初始化),在外设节点中调用
  • 在驱动中获取设备节点以及GPIO
  • 对GPIO进行配置

GPIO子系统

概括

在以前的内核版本中,如果要配置GPIO的话一般要使用IC厂家实现的GPIO配置函数,例如三星的配置函数为

/*设置为输入*/
s3c_gpio_cfgpin(EXYNOS4_GPC0(3),S3C_GPIO_INPUT);
/*不上拉不下拉*/
s3c_gpio_setpull(EXYNOS4_GPC0(3),S3C_GPIO_PULL_NONE);

这样带来的问题就是各家有各家的接口函数与实现方式,不但内核的代码复用率低而且开发者很难记住这么多的函数,如果要使用多种平台的话背函数都是很麻烦的,所以在引入设备树后对GPIO子系统进行了大的改造,使用设备树来实现并提供统一的接口
通过GPIO子系统功能要实现:

  • 引脚功能的配置(设置为GPIO,特殊功能,GPIO的方向,设置为中断等)
  • 实现软硬件的分离(分离出硬件差异,有厂商提供的底层支持;软件分层。驱动只需要调用接口API即可操作GPIO)
  • iommu内存管理(直接调用宏即可操作GPIO)

经过GPIO子系统,我们可以通过如下的方式来配置GPIO

gpio_request(leddev.led0, "led0");
gpio_direction_output(leddev.led0, 1);
gpio_set_value(leddev.led0, 0);
gpio_direction_input(key.irqkeydesc[0].gpio);
value = gpio_get_value(keydesc->gpio);

系统框图

Linux驱动开发(十一):pinctrl子系统和GPIO子系统_第5张图片
GPIO lib调用下层结构并对顶层的驱动提供结构

与pinctrl比较

结构和功能类似,内部有所差异
Linux驱动开发(十一):pinctrl子系统和GPIO子系统_第6张图片

关系

pinctrl基于设备树,gpio子系统依赖与pinctrl子系统来实现

总结

我这里总结的是比较基本的pinctrl子系统和gpio子系统的应用,他们的好处就是大大简化了驱动的编写,不用去考虑过多的寄存器,考虑不同芯片的差别,大大提高的代码的复用率

你可能感兴趣的:(arm+linux开发)