Linux驱动学习—pinctl和gpio子系统

2024-01-01 20:36

本文主要是介绍Linux驱动学习—pinctl和gpio子系统,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1、pinctl和gpio子系统(一)

1.1pinctrl 子系统主要工作内容

<1>获取设备树中 pin 信息,管理系统中所有的可以控制的 pin, 在系统初始化的时候, 枚举所有可以控制的 pin, 并标识这些 pin。
<2>根据获取到的 pin 信息来设置 pin 的复用功能,对于 SOC 而言, 其引脚除了配置成普通的 GPIO 之外,若干个引脚还可以组成一个 pin group, 形成特定的功能。
<3>根据获取到的 pin 信息来设置 pin 的电气特性,比如上/下拉、速度、驱动能力等。

对应使用者来说,只需要在设备树里面设置好某个 pin 的相关属性即可,其他的初始化工作均由 pinctrl 子系统来完成。

1.2gpio子系统主要工作内容

当使用 pinctrl 子系统将引脚的复用设置为 GPIO,可以使用 GPIO 子系统来操作GPIO,Linux 内核提供了 pinctrl 子系统和 gpio 子系统用于 GPIO 驱动。

通过 GPIO 子系统功能要实现:

<1>引脚功能的配置(设置为 GPIO,GPIO 的方向, 输入输出模式,读取/设置 GPIO 的值)
<2>实现软硬件的分离(分离出硬件差异, 有厂商提供的底层支持; 软件分层。 驱动只需要调用接口 API 即可操作 GPIO)
<3>iommu 内存管理(直接调用宏即可操作 GPIO)

gpio 子系统的主要目的就是方便驱动开发者使用 gpio,驱动开发者在设备树中添加 gpio 相关信息,然后就可以在驱动程序中使用 gpio 子系统提供的 API函数来操作 GPIO, Linux 内核向驱动开发者屏蔽掉了 GPIO 的设置过程,极大的方便了驱动开发者使用 GPIO。

1.3 不同soc厂家的pin contrller的节点

这些节点都是把某些引脚复用成功能。

1.4 不同soc厂家的pin contrller的节点里面的属性都是什么意思

可以通过在Documentation/devicetree/bindings/下的txt文档查看。

1.5 怎么在代码中使用pin contrller里面定义好的节点?

例1:

pinctrl-names = "default";//设备的状态,可以有多种状态,default为状态0
pinctrl-0 = <&pinctrl_hog_1>;/*第0个状态所对应的引脚配置,也就是default状态对应的引脚在pin controller里面定义好的节点pinctrl_hog_1里面的管脚配置。*/

例2:

pinctrl-names = "default","wake up";//设备的状态,可以有多种状态,default为状态0,wake up为状态1,
pinctrl-0 = <&pinctrl_hog_1>;/*第0个状态所对应的引脚配置,也就是default状态对应的引脚在pin controller里面定义好的节点pinctrl_hog_1里面的管脚配置。*/
pinctrl-1 = <&pinctrl_hog_2>;/*第1个状态所对应的引脚配置,也就是default状态对应的引脚在pin controller里面定义好的节点pinctrl_hog_2里面的管脚配置。*/

例3:

pinctrl-names = "default";//设备的状态,可以有多种状态,default为状态0,wake up为状态1,
pinctrl-0 = <&pinctrl_hog_1   &pinctrl_hog_2>;/*第0个状态所对应的引脚配置,也就是default状态对应的引脚在pin controller里面定义好的节点pinctrl_hog_1和pinctrl_hog_2这两个节点的管脚配置。*/

1.6 总结

总结:之前控制引脚的方法都是操作配置寄存器:

现在可以不用这种方法,linux有现成的框架,这个框架就是pinctl子系统和gpio子系统,可以pinctl子系统设置引脚的复用功能,设置引脚的电气属性。

2、pinctl和gpio子系统(二)

上一个小节我们学习了pinctrl子系统,Linux内核提供了pinctrl子系统和gpio子系统用于GPIO驱动,当然pinctrl子系统负责不仅仅是GPIO的驱动,而是所有pin脚配置。pinctrl子系统是随着设备树的加入而加入的,依赖设备树。GPIO子系统在之前的内核也是存在的,但是pinctrl子系统的加入使得GPIO子系统有很大的改变。

在以前的内核版本中,如果要配置GPIO的话一般要使用SOC厂家实现的配置函数,例如三星的配置函数s3c_gpio_cfgpin等,这样带来的问题就是各家有个家的接口函数与是实现方式,不但内核的代码复用率低而且开发者很难记住这么多的函数,如果要使用多种平台的话背函数都是很麻烦的,所以在引入设备树后对GPIO子系统进行大的改造,使用设备树来实现并提供统一的接口。通过GPIO子系统功能主要实现引脚功能的配置,如设置为GPIO,特殊功能,GPIO的方向,设置为中断等。

那么我们先来看一下怎么在设备树中pinctrl和gpio子系统描述一个gpio。

2.1 设备树使用pinctrl和gpio子系统描述一个gpio

test1:test{#address-cells = <1>;#size-cells = <1>;compatible = "test";reg = <0x20ac000 0x00000004>;//描述数据寄存器的地址pinctrl-names = "default";pintrl-0 = <&pinctrl_test>;test-gpio = <gpio1 3 GPIO_ACTICE_LOW>;//gpio 表示第一组,3表示第一组第三个引脚,GPIO_ACTICE_LOW表示低电平
};

2.2 常用的gpio子系统提供的api函数

这些函数的定义在include\linux\gpio.h

2.2.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.2.2 gpio_free函数

作用:如果不使用某个GPIO了,那么就可以调用gpio_free函数进行释放。

void gpio_free(unsigned gpio);
参数:
gpio:要释放的gpio标号。
返回值:无
2.2.3 gpio_direction_input函数

作用:此函数用于设置某个GPIO为输入。

int gpio_direction_input(unsigned gpio);
参数:
gpio:要设置为输入的GPIO标号。
返回值:0,设置成功,其他值设置失败。
2.2.4gpio_direction_output函数

作用:此函数用于设置某个GPIO为输出,并且设置默认输出值。

int gpio_direction_output(unsigned gpio, int value);
参数:
gpio:要设置为输出的GPIO标号。
value:GPIO默认输出值。
返回值:0,设置成功,设置失败返回负值。
2.2.5 gpio_get_value函数

作用:此函数用于获取某个GPIO的值(0或1)

int gpio_get_value(unsigned int gpio);
gpio:要获取的gpio标号
返回值:0,成功,失败返回负值。
2.2.5 gpio_set_value函数

作用:此函数用于获取某个GPIO的值(0或1)

void gpio_set_value(unsigned int gpio, int value);
gpio:要设置的gpio标号
value:要设置的值。
返回值:无。

2.3 总结

pinctl子系统的作用就是设置引脚的复用功能和电气属性。gpio子系统就是当pinctl子系统把引脚设置成GPIO功能以后就可以使用gpio子系统来操作我们引脚了,比如说设置输入、输出或者引脚的高低电平等等。

3、pinctl和gpio子系统(三)

pinctrl子系统就是设置引脚的复用关系和电气属性,gpio子系统就是当pinctrl把引脚设置成设置为gpio以后我们使用gpio子系统来操作gpio。

3.1 引脚的宏定义是在哪里找的

在arch/arm/boot/dts/imx6ul-pinfunc.h:

每个宏定义都对应一个管脚的复用功能。一个引脚有怎么多复用功能,但是只能使能一个,所以在设备树下需要检察是否有其他复用功能被使用,有就需要在设备树文件其他使用的地方注释掉:

3.2 实验

Linux驱动学习—设备树及设备树下的platform总线-CSDN博客

实现设备树学习中的7.3未实现的部分,即在probe函数注册一个杂项设备驱动用于对蜂鸣器的操作。这里对引脚的操作不是相之前一样对地址寄存器的操作实现gpio电平值的改变,而是通过gpio子系统的api函数是实现。

3.2.1 设备树文件修改

3.2.2 实验代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h> 
#include <linux/of.h>
#include <linux/of_address.h>
​
struct device_node *test_device_node;
struct property *test_node_property;
int size;
u32 out_values[2]={0};
const char *str=NULL;
unsigned int *vir_gpio_dr;
int beep_gpio = 0;
​
static const of_device_id of_match_table_test[] = {//匹配表{.compatible = "test1234"},
};
​
static const platform_device_id beep_id_table ={.name = "beep_test",
};
​
int misc_open(struct inode *inode, struct file *file)
{printk("misc_open\n");return 0;
}
​
int misc_release(struct inode *inode, struct file *file)
{printk("misc_relese\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)) != 0) {printk("copy_to_user error\n");return -1;}return 0;
}
​
ssize_t misc_wirie(struct file *file,char __user *ubuf, size_t size, loff_t *loff_t)
{char kbuf[64] = {0};if(copy_form_user(kbuf, ubuf, strlen(kbuf)) != 0) {printk("copy_form_user error\n");return -1;}printk("kbuf is %s\n",kbuf);if(kbuf[0] == 1)get_set_value(beep_gpio, 1);else if(kbuf[0] == 0)get_set_value(beep_gpio, 0);return 0;
}
​
struct file_operations misc_fops = {.owner      = THIS_MODULE,.open       = misc_open,.release    = misc_release,.write      = misc_wirie,.read       = misc_read
};
​
struct miscdevice misc_dev = {.minor = MISC_DYNAMIC_MINOR,.name  = hello_misc,.fops  = &misc_fops
};
​
/*设备树节点compatible属性与of_match_table_test的compatible相匹配就会进入该函数,pdev是匹配成功后传入的设备树节点*/
int beep_probe(struct platform_device *pdev)
{int ret = 0;printk("beep_probe\n");//查找要查找的节点test_device_node = of_find_node_by_path("/test");if (test_device_node == NULL) {printk("test_device_node find error\n");return -1;}printk("test_device_node name is %s\n",test_device_node->name);//testbeep_gpio = of_get_named_gpio(test_device_node, "beep-gpio", 0);if (beep_gpio < 0) {printk("of_get_named_gpio error\n");return -1;}printk("beep_gpio name is %d\n",beep_gpio);ret = gpio_request(beep_gpio, "beep");if (ret < 0) {printk("gpio_request error\n");return -1;}ret = misc_register(&misc_dev);if (ret < 0) {printk("misc_register error\n");return -1;}return 0;
}
​
int beep_remove(struct platform_device *pdev)
{pritnk("beep_remove \n");return 0;
}
​
strcut platform_driver beep_device = {.probe = beep_probe,.remove = beep_remove,.driver = {.owner = THIS_MODULE,.name  = "123",.of_match_table = of_match_table_test,//匹配表 },.id_table = &beep_id_table,
};
​
static int beep_driver_init(void)
{int ret = -1;ret = platform_driver_register(&beep_device);if(ret < 0) {printk("platform_driver_register error \n");}printk("platform_driver_register ok\n");return 0;
}
​
static void  beep_driver_exit(void)
{platform_driver_unregister(&beep_device);printk("beep_driver_exit \n");
}
​
module_init(beep_driver_init);
module_exit(beep_driver_exit);
MODULE_LICENSE("GPL");

加载驱动,可以看到杂项设备节点生成,对这个设备节点写入1就表示引脚电平设置为高,,对这个设备节点写入0就表示引脚电平设置为低,

echo 1 > /dev/hello_misc
echo 0 > /dev/hello_misc

这篇关于Linux驱动学习—pinctl和gpio子系统的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/560445

相关文章

重新对Java的类加载器的学习方式

《重新对Java的类加载器的学习方式》:本文主要介绍重新对Java的类加载器的学习方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍1.1、简介1.2、符号引用和直接引用1、符号引用2、直接引用3、符号转直接的过程2、加载流程3、类加载的分类3.1、显示

如何在Ubuntu上安装NVIDIA显卡驱动? Ubuntu安装英伟达显卡驱动教程

《如何在Ubuntu上安装NVIDIA显卡驱动?Ubuntu安装英伟达显卡驱动教程》Windows系统不同,Linux系统通常不会自动安装专有显卡驱动,今天我们就来看看Ubuntu系统安装英伟达显卡... 对于使用NVIDIA显卡的Ubuntu用户来说,正确安装显卡驱动是获得最佳图形性能的关键。与Windo

ubuntu16.04如何部署dify? 在Linux上安装部署Dify的技巧

《ubuntu16.04如何部署dify?在Linux上安装部署Dify的技巧》随着云计算和容器技术的快速发展,Docker已经成为现代软件开发和部署的重要工具之一,Dify作为一款优秀的云原生应用... Dify 是一个基于 docker 的工作流管理工具,旨在简化机器学习和数据科学领域的多步骤工作流。它

Linux高并发场景下的网络参数调优实战指南

《Linux高并发场景下的网络参数调优实战指南》在高并发网络服务场景中,Linux内核的默认网络参数往往无法满足需求,导致性能瓶颈、连接超时甚至服务崩溃,本文基于真实案例分析,从参数解读、问题诊断到优... 目录一、问题背景:当并发连接遇上性能瓶颈1.1 案例环境1.2 初始参数分析二、深度诊断:连接状态与

Linux系统调试之ltrace工具使用与调试过程

《Linux系统调试之ltrace工具使用与调试过程》:本文主要介绍Linux系统调试之ltrace工具使用与调试过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、ltrace 定义与作用二、ltrace 工作原理1. 劫持进程的 PLT/GOT 表2. 重定

Linux区分SSD和机械硬盘的方法总结

《Linux区分SSD和机械硬盘的方法总结》在Linux系统管理中,了解存储设备的类型和特性是至关重要的,不同的存储介质(如固态硬盘SSD和机械硬盘HDD)在性能、可靠性和适用场景上有着显著差异,本文... 目录一、lsblk 命令简介基本用法二、识别磁盘类型的关键参数:ROTA查询 ROTA 参数ROTA

嵌入式Linux之使用设备树驱动GPIO的实现方式

《嵌入式Linux之使用设备树驱动GPIO的实现方式》:本文主要介绍嵌入式Linux之使用设备树驱动GPIO的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、设备树配置1.1 添加 pinctrl 节点1.2 添加 LED 设备节点二、编写驱动程序2.1

嵌入式Linux驱动中的异步通知机制详解

《嵌入式Linux驱动中的异步通知机制详解》:本文主要介绍嵌入式Linux驱动中的异步通知机制,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、异步通知的核心概念1. 什么是异步通知2. 异步通知的关键组件二、异步通知的实现原理三、代码示例分析1. 设备结构

Linux搭建单机MySQL8.0.26版本的操作方法

《Linux搭建单机MySQL8.0.26版本的操作方法》:本文主要介绍Linux搭建单机MySQL8.0.26版本的操作方法,本文通过图文并茂的形式给大家讲解的非常详细,感兴趣的朋友一起看看吧... 目录概述环境信息数据库服务安装步骤下载前置依赖服务下载方式一:进入官网下载,并上传到宿主机中,适合离线环境

windows和Linux使用命令行计算文件的MD5值

《windows和Linux使用命令行计算文件的MD5值》在Windows和Linux系统中,您可以使用命令行(终端或命令提示符)来计算文件的MD5值,文章介绍了在Windows和Linux/macO... 目录在Windows上:在linux或MACOS上:总结在Windows上:可以使用certuti