pinctl和gpio子系统
1.什么是pinctrl和gpio子系统?
pinctrl子系统是用来设置引脚的复用关系和电气属性的, gpio子系统是当pinctrl子系统把引脚的复用关系设置为gpio功能以后就可以使用gpio子系统来操作引脚了, 比如引脚的输入输出,高低电平等
2.Linux Pinctrl子系统提供的功能是什么?
(1)管理系统中所有的可以控制的pin,在系统初始化的时候,枚举所有可以控制的pin,并标识这些pin。
(2)管理这些pin的复用(Multiplexing)。对于SOC而言,其引脚除了配置成普通的GPIO之外,若干个引脚还可以组成一个pin group,形成特定的功能。
(3)配置这些pin的特性。例如使能或关闭引脚上的pull-up、pull-down电阻,配置引脚的driver strength。
3.不同soc厂家的pin controller的节点
这些节点里都是把某些引脚复用成某些功能。
NXP 的 pinctrl 子系统如下所示 :
fsl,pins 属性主要用于描述设备的引脚和引脚控制信息。它是一个通用的属性,可以用于描述各种类型的设备。在具体的设备树节点中,它通常包含一个复杂的数据结构,用于描述设备的引脚控制信息。
三星pinctrl 子系统如下所示:
瑞星微pinctrl 子系统如下所示:
4. 不同soc厂家的pin controller的节点里面的属性都是什么意思?
可以通过Linux源码目录Documentation/devicetree/bindings下的txt文档查看。
5. 怎么在代码里面使用pin controller里面定义好的节点?
例1:
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog_1>;
含义:
<1>pinctrl-names = "default";
设备的状态,可以有多个状态,default为状态0
<2>pinctrl-0 = <&pinctrl_hog_1>;
第0个状态所对应的引脚配置,也就是default状态对应的引脚在pin controller里面定义好的节点pinctrl_hog_1里面的管脚配置
例2:
pinctrl-names = "default","wake up";
pinctrl-0 = <&pinctrl_hog_1>;
pinctrl-1 = <&pinctrl_hog_2>;
含义
<1>pinctrl-names = "default","wake up";
设备的状态,可以有多个状态,default为状态0,wake up为状态1
<2>pinctrl-0 = <&pinctrl_hog_1>;
第0个状态所对应的引脚配置,也就是default状态对应的引脚在pin controller里面定义好的节点pinctrl_hog_1里面的管脚配置
<3>pinctrl-1 = <&pinctrl_hog_2>;
第1个状态所对应的引脚配置,也就是wake up状态对应的引脚在pin controller里面定义好的节点pinctrl_hog_2里面的管脚配置
例3:
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog_1
&pinctrl_hog_2>;
含义
<1>pinctrl-names = "default";
设备的状态,可以有多个状态,default为状态0
<2>pinctrl-0 = <&pinctrl_hog_1 &pinctrl_hog_2>;
第0个状态所对应的引脚配置,也就是default状态对应的引脚在pin controller里面定义好的节点,对应pinctrl_hog_1和pinctrl_hog_2这俩个节点的管脚配置
Linux 内核提供了 pinctrl 子系统和 gpio 子系统用于 GPIO 驱动,当然 pinctrl 子系统负责的就不仅仅是 GPIO 的驱动了而是所有 pin 脚的配置。pinctrl 子系统是随着设备树的加入而加入的,依赖于设备树。GPIO 子系统在之前的内核中也是存在的,但是 pinctrl 子系统的加入 GPIO 子系统也是有很大的改变。
在以前的内核版本中,如果要配置 GPIO 的话一般要使用 SOC 厂家实现的配置函数,例如三星的配置函s3c_gpio_cfgpin等,这样带来的问题就是各家有各家的接口函数与实现方式,不但内核的代码复用率低而且开发者很难记住这么多的函数,如果要使用多种平台的话背函数都是很麻烦的,所以在引入设备树后对 GPIO 子系统进行了大的改造,使用设备树来实现并提供统一的接口。通过 GPIO 子系统功能主要实现引脚功能的配置,如设置为 GPIO,特殊功能,GPIO 的方向,设置为中断等。
那么我们先来看一下怎么在设备树中pinctrl和gpio子系统描述一个gpio。
<1>设备树使用pinctrl和gpio子系统描述一个gpio。
test1:test{
#address-cells = <1>;
#size-cells = <1>;
compatible = "test";
reg = <0x020ac000 0x00000004>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_beep>;
beep-gpio = <&gpio5 1 GPIO_ACTIVE_LOW>;
};
<2>常用gpio子系统提供的api函数
goio头文件:
#include <linux/gpio.h>
#include <linux/of_gpio.h>
1 、gpio_request 函数
作用:
gpio_request 函数用于申请一个 GPIO 管脚
函数原型:
int gpio_request(unsigned gpio, const char *label);
参数:
gpio:要申请的 gpio 标号,使用 of_get_named_gpio 函数从设备树获取指定 GPIO 属性信息,此函数会返回这个 GPIO 的标号。
label:给 gpio 设置个名字。
返回值:
0,申请成功;其他值,申请失败。
2 、gpio_free函数
作用:如果不使用某个 GPIO 了,那么就可以调用 gpio_free 函数进行释放。
函数原型:
void gpio_free(unsigned gpio);
参数:
gpio:要释放的 gpio 标号。
返回值:无。
3 、gpio_direction_input 函数
作用:
此函数用于设置某个 GPIO 为输入
函数原型:
int gpio_direction_input(unsigned gpio);
参数:
gpio:要设置为输入的 GPIO 标号。
返回值:设置成功返回0;设置失败返回负值
4 、gpio_direction_output 函数
作用:此函数用于设置某个 GPIO 为输出,并且设置默认输出值
函数原型:
int gpio_direction_output(unsigned gpio, int value);
参数:
gpio:要设置为输出的 GPIO 标号。
value:GPIO 默认输出值。
返回值:设置成功返回0;设置失败返回负值
5 、gpio_get_value 函数
作用:
此函数用于获取某个 GPIO 的值(0 或 1)
函数原型:
int __gpio_get_value(unsigned gpio);
参数:
gpio:要获取的 GPIO 标号。
返回值:成功返回GPIO值,失败返回负值。
6 、gpio_set_value 函数
作用:
此函数用于设置某个 GPIO 的值
函数原型:
void __gpio_set_value(unsigned gpio, int value);
参数:
gpio:要设置的 GPIO 标号。
value:要设置的值。
返回值:无
7 、 of_get_named_gpio 函数
作用:
此函数获取 GPIO 编号,因为 Linux 内核中关于 GPIO 的 API 函数都要使用 GPIO 编号,此函数会将设备树中类似<&gpio1 3 GPIO_ACTIVE_LOW>的属性信息转换为对应的 GPIO 编号,函数原型如下:
int of_get_named_gpio(struct device_node *np,const char *propname,int index);
参数:
np:设备节点。
propname:包含要获取 GPIO 信息的属性名。
index:因为一个属性里面可能包含多个 GPIO,此参数指定要获取哪个 GPIO的编号,如果只有一个 GPIO 信息的话此参数为 0。
返回值:
成功返回到的 GPIO 编号,失败返回一个负数。
Pinctrl 和 GPIO 子系统实验
driver.c
#include <linux/init.h> //包含宏定义的头文件
#include <linux/module.h> //包含初始化加载模块的头文件
#include <linux/platform_device.h> //平台设备所需要的头文件
#include <linux/of.h> //of函数
#include <linux/of_address.h> //of_iomap函数
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
struct device_node *test_device_node; //节点
u32 out_values[2] = {0}; //读取到的数组值
unsigned int *vir_gpio5_dr; //地址映射
int beep_gpio = 0; //gpio标号
int misc_open(struct inode *inode, struct file *file)
{
printk("hello misc_open\n");
return 0;
}
int misc_release(struct inode *inode, struct file *file)
{
printk("hello mise_release bye bye\n");
return 0;
}
ssize_t misc_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff_t)
{
char kbuf[64] = "heheh";
if (copy_to_user(ubuf, kbuf, strlen(kbuf) + 1) != 0)
{
printk("copy_to_user error\n");
return -1;
}
printk("hello misc_read bye bye\n");
return 0;
}
ssize_t misc_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
{
char kbuf[64] = {0};
if (copy_from_user(kbuf, ubuf, size) != 0)
{
printk("copy_from_user error\n");
return -1;
}
printk("hello misc_write bye bye\n");
if(kbuf[0] == 1) //打开蜂鸣器
{
__gpio_set_value(beep_gpio, 1);
}else if(kbuf[0] == 0)//关闭蜂鸣器
{
__gpio_set_value(beep_gpio, 0);
}
return 0;
}
/* 文件操作集 */
struct file_operations misc_fops = {
.owner = THIS_MODULE, // 当前模块
.open = misc_open,
.release = misc_release,
.write = misc_write,
.read = misc_read,
};
/* 杂项设备结构体 */
struct miscdevice misc_dev = {
.minor = MISC_DYNAMIC_MINOR, // 动态分配次设备号
.name = "hello_mise", // 设备节点的名字
.fops = &misc_fops // 文件操作集
};
int beep_probe(struct platform_device *pdev)
{
int ret = 0;
printk("beep_probe\n");
test_device_node = of_find_node_by_path("/test"); //查找根节点下的test节点
if(test_device_node == NULL)
{
printk("of_find_node_by_path is error\n");
return -1;
}
printk("test_device_node name is %s\n", test_device_node->name);
beep_gpio = of_get_named_gpio(test_device_node, "beep-gpio", 0); //获取GPIO标号
if(beep_gpio < 0)
{
printk("of_get_named_gpio is error\n");
return -1;
}
printk("beep_gpio is %d\n", beep_gpio);
ret = gpio_request(beep_gpio, "beep"); //申请一个 GPIO 管脚
if(ret != 0)
{
printk("gpio_request is error\n");
return -1;
}
printk("gpio_request is ok\n");
ret = gpio_direction_output(beep_gpio, 0); //设置GPIO为输出,并且设置默认输出值
if(ret < 0)
{
printk("gpio_direction_output is error\n");
return -1;
}
printk("gpio_direction_output is ok\n");
ret = misc_register(&misc_dev); // 注册杂项设备
if (ret < 0)
{
printk("misc register is error!\n");
return -1;
}
printk("mise register is ok!\n");
vir_gpio5_dr = of_iomap(test_device_node, 0); //直接内存映射
if(vir_gpio5_dr == NULL)
{
printk("of_iomap is error\n");
return -1;
}
printk("of_iomap is ok!\n");
return 0;
}
int beep_remove(struct platform_device *platform_device)
{
printk("beep_remove\n");
return 0;
}
struct platform_device_id beep_id_table = {
.name = "123"
};
struct of_device_id of_match_table_test[] = { //与设备树的 compatible 匹配
{
.compatible = "test1234"
},
{
}
};
/* platform 驱动结构体 */
struct platform_driver beep_platform_driver = {
.probe = beep_probe,
.remove = beep_remove,
.driver = {
.owner = THIS_MODULE,
.name = "beep_test", //匹配优先级3
.of_match_table = of_match_table_test, //中的.compatible匹配优先级1
},
.id_table = &beep_id_table //中的.name匹配优先级2
};
static int beep_driver_init(void)
{
int ret = 0;
printk("hello world\n");
ret = platform_driver_register(&beep_platform_driver); //注册平台驱动
if(ret < 0)
{
printk("platform_driver_register is error\n");
return ret;
}
return 0;
}
static void beep_driver_exit(void)
{
printk("byby\n"); // 内核模块卸载的时候打印"byb byb
gpio_free(beep_gpio); //释放GPIO
platform_driver_unregister(&beep_platform_driver); //卸载 platform 驱动
misc_deregister(&misc_dev); //注销杂项设备
iounmap(vir_gpio5_dr); //释放掉ioremap映射的地址
}
module_init(beep_driver_init); // 驱动模块的入口
module_exit(beep_driver_exit); // 驱动模块的出口
MODULE_LICENSE("GPL"); // 声明模块拥有开源许可证
Makefile
obj-m +=driver.o
KDIR:=/home/mzx/imx6ull/linux-imx-rel_imx_4.1.15_2.1.0_ga
PWD?=$(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules
app.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
int fd;
char buf[64] = {0};
if(argc < 2)
{
printf("Usage: %s <1:beep open / 0:beep close>\n", argv[0]);
return -1;
}
fd = open("/dev/hello_mise", O_RDWR);
if (fd < 0)
{
perror("open error\n");
return fd;
}
buf[0] = atoi(argv[1]); //字符串转int
write(fd, buf, strlen(buf)+1);
close(fd);
return 0;
}
标签:15,引脚,pinctrl,beep,GPIO,gpio,pinctl,printk
From: https://www.cnblogs.com/mzx233/p/18158794