Pinctrl子系统和gpio子系统
- 6.1Pinctrl子系统
- 6.2Pinctrl子系统的编写格式
- 6.2.1**iomuxc节点介绍**
- 6.2.2pinctrl子节点编写模式
- 6.2.3引脚的配置信息
- 6.2.4将led灯引脚添加到pinctrl子系统
- 6.2GPIO子系统
- 6.3在设备树中添加led设备树节点
- 6.4 在驱动中调用GPIO子系统
- 6.5 ioctl接口
现在我们可以通过在驱动程序代码里使用设备树接口,来获取到外设的信息了。但是我们还是要将寄存器操作具体细节体现在驱动中,比如复位操作。
那么,有没有更加通用的方法,可以不涉及到具体的寄存器操作内容呢?对于有些外设,是具备抽象条件的,也就是说我们可以将对这些外设的操作统一起来。
6.1Pinctrl子系统
Pinctrl子系统是驱动分离分层思想下的产物,硬件属性方面放在设备树dts中,其中关于设备所使用的的管脚配置,可以集中使用pinctrl。
pinctrl子系统是由芯片厂商来实现的用于管理芯片的引脚。imx6ull芯片拥有众多的片上外设, 大多数外设需要通过芯片的引脚与外部设备(器件)相连实现相对应的控制。芯片的设计厂商为了提高硬件设计的灵活性, 一个芯片引脚往往可以做为多个片上外设的功能引脚。pinctrl子系统用于帮助我们管理芯片引脚并自动完成引脚的初始化, 而我们要做的只是在设备树中按照规定的格式写出想要的配置参数即可。
参考资料文档:Documentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt
6.2Pinctrl子系统的编写格式
6.2.1iomuxc节点介绍
imx6ull.dtsi这个文件是芯片厂商官方将芯片的通用的部分单独提出来的一些设备树配置。 在iomuxc节点中汇总了所需引脚的配置信息,pinctrl子系统存储使用着iomux节点信息。
我们的设备树主要的配置文件在~kernel/linux-imx/arch/arm/boot/dts/imx6ull-myir-mys-6ulx.dtsi 中,在文件中搜索“&iomuxc”找到设备树中引用“iomuxc”节点的位置如下所示。
&iomuxc {
pinctrl-names = "default"; /*指定PIN的状态列表,默认设置为“default”*/
pinctrl_camera_clock: cameraclockgrp {
fsl,pins = <
MX6UL_PAD_CSI_MCLK__CSI_MCLK 0x1b088 /*将0x1b088写给电气属性寄存器,以确定pin的电器属性*/
>;
};
6.2.2pinctrl子节点编写模式
这里我们需要知道的是每一个芯片厂商的pinctrl子节点的编写格式不同,这不属于设备树的规范,是芯片厂商自己定义的。如果我们需要添加我们自己的pinctl节点,只需要模仿上面的格式编写就行。
pinctrl_自定义名字: 自定义名字 {
fsl,pins = <
引脚复用宏定义 PAD(引脚)属性 /*引脚配置信息*/
引脚复用宏定义 PAD(引脚)属性
>;
};
6.2.3引脚的配置信息
引脚的配置信息是由一个宏定义和一个16进制数组成
引脚定义在“./arch/arm/boot/dts/imx6ul-pinfunc.h”文件中
linxincheng@ubuntu:~/imx6ull/bsp/kernel/linux-imx/arch/arm/boot/dts$ cat imx6ul-pinfunc.h
#ifndef __DTS_IMX6UL_PINFUNC_H
#define __DTS_IMX6UL_PINFUNC_H
/*
* The pin function ID is a tuple of
* <mux_reg conf_reg input_reg mux_mode input_val>
*/
#define MX6UL_PAD_BOOT_MODE0__GPIO5_IO10 0x0014 0x02a0 0x0000 5 0
#define MX6UL_PAD_BOOT_MODE1__GPIO5_IO11 0x0018 0x02a4 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER0__GPIO5_IO00 0x001c 0x02a8 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x0020 0x02ac 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER2__GPIO5_IO02 0x0024 0x02b0 0x0000 5
#define MX6UL_PAD_JTAG_MOD__SJC_MOD 0x0044 0x02d0 0x0000 0 0
#define MX6UL_PAD_JTAG_MOD__GPT2_CLK 0x0044 0x02d0 0x05a0 1 0
#define MX6UL_PAD_JTAG_MOD__SPDIF_OUT 0x0044 0x02d0 0x0000 2 0
#define MX6UL_PAD_JTAG_MOD__ENET1_REF_CLK_25M 0x0044 0x02d0 0x0000 3 0
6.2.4将led灯引脚添加到pinctrl子系统
1.我们先根据原理图找出LED对应的引脚,这里使用GPIO5_IO02
2.根据imx6ul-pinfunc.h查找引脚宏定义GPIO5_IO02
3.设置引脚的属性为0x17059(参考内核设备树xxx.dts,vim打开,通过/GPIO5_IO02,按Enter搜索,按n查找下一个)
4.在igkboard.dts文件中的iomuxc节点下添加pinctrl子节点
6.2GPIO子系统
GPIO子系统就是用于初始化GPIO并且提供相应的API函数,比如设置GPIO位输入输出,读取GPIO的值。GPIO子系统的主要目的是方便驱动开发资使用GPIO
在设备树中添加gpio相关信息,然后就可以在驱动程序使用gpio子系统提供的API函数来操作GPIO。
6.3在设备树中添加led设备树节点
pinctrl配置好后就是配置gpio了,我们写驱动程序通过读取 GPIO5_IO02的值来控制我们的led灯,设备树会告诉驱动我们通过这个值来控led灯。
在设备树添加my_leds{}节点,在节点下添加一个属性来描述这个引脚就好了,驱动通过读取这个属性读取GPIO,并通过GPIO提供的API函数设置GPIO位输入输出,读取GPIO的值。
然后在/bsp/kernel/linux-imx执行如下命令:
make dtbs (只编译设备树)
编译完成后,把igkboard.dts拷贝替换到开发板的/run/media/mmcblk1p1路径,然后sudo reboot
重启开发板
重启以后在开发板的/proc/device-tree路径下看到了我们添加的"my_leds"设备树节点,看到我们设置的gpio子系统的属性:
6.4 在驱动中调用GPIO子系统
在设备树中指定了GPIO引脚,在驱动代码中如何使用呢?GPIO子系统的接口函数是什么?
GPIO子系统有两套接口:
- 一种是基于描述符(descriptor-based)的,相关api函数都是以”gpiod_”为前缀,它使用gpio_desc结构来表示一个引脚。
- 另一种是老(legency)的,相关api函数都是以”gpio_”为前缀,它使用一个整数来表示一个引脚,强烈建议不要使用legacy的接口函数。
要操作一个引脚,首先要 get 引脚,然后设置方向,读值、写值。
驱动的程序中包含头文件:
#include <linux/gpio/consumer.h> // descriptor-based
或
#include <linux/gpio.h> // legacy
下面是一些常用函数:
6.5 ioctl接口
ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。对I/O通道进行管理,就是对设备的一些特性进行控制。
除了读取和写入设备之外,大部分驱动程序还需要另外一种能力,就是通过设备驱动程序执行各种类型的硬件控制。
除简单数据传输之外,大部分设备可以执行其他一些操作,比如,用户空间经常会请求设备锁门、弹出介质、报告错误信息、改变波特率或者执行子破坏等等。
这些操作通常通过ioctl方法来实现
用户空间,ioctl系统调用具有如下原型:
#include <sys/ioctl.h>
int ioctl(int fd,int cmd, ...);
参数:fd 使要操作的文件描述符
cmd:控制命令
...:可选参数arg,一些情况下应用程序需要向驱动程序传参,参数就通过arg来传递。
具体形式依赖于第二个参数cmd,因为有些控制命令需要参数,有些命令不需要参数。
内核驱动程序,ioctl方法原型如下:
#include <linux/ioctl.h>
int (*ioctl)(struct inode *inode,struct file *filp,unsigned int cmd,
unsigned long arg);
参数inode和filp:和open方法的参数一样,表示文件节点和打开的file结构
参数cmd:由用户空间不经修改地传递进来
参数arg:使用户空间传递的整数值;如果用户空间的调用程序没有第三个参数,则驱动的arg参数为未定义状态。