linux驱动-gpio

news2024/11/14 20:37:08

最近处理es8336声卡问题,最后排查是spk_ctl_gpio和hp_det_gpio这两个gpio导致的,所以恶补了一下gpio相关的知识,现在总结一下。
源代码使用的是飞腾的gitee上开源的内核:https://gitee.com/phytium_embedded/phytium-linux-kernel.git

1. 概述

在这里插入图片描述

  1. 设备驱动层:定义了与硬件无关的GPIO API,包括GPIO的注册、卸载和控制等功能,而实现了某个模块的具体实现,比如led灯、按键等等。

  2. gpiolib抽象层:GPIO框架中的核心抽象层,它的作用是为设备驱动层和控制器层提供一致的接口,该层提供了包括上层设备驱动和下层控制器驱动的API接口。

  3. 控制器层:GPIO控制器的实现和管理,在该层中实现特定GPIO控制器的底层硬件操作和功能实现包括GPIO控制器的初始化、操作和管理等。负责GPIO寄存器的读写操作和GPIO中断的处理等。

其中gpiolib抽象层是GPIO框架中的核心层,也是linux内核自己实现的,一般情况下没有人会修改这部分的代码,控制器层一般是芯片厂家BSP工程师实现的,设备驱动层是驱动工程师根据开发版的实际情况实现的。优秀的BSP工程师和驱动工程师可以把驱动写得与硬件解耦,把硬件的信息填充到设备树中,驱动读取设备树的信息进行各种操作。

2. 控制器

这里以飞腾e2000为例子看看gpio控制器的驱动是怎么样的,在文件drivers/gpio/gpio-phytium-platform.c中,但是gpio的操作函数写在drivers/gpio/gpio-phytium-core.c和drivers/gpio/gpio-phytium-core.h中。

2.1 设备树

		gpio0: gpio@28034000 {
			compatible = "phytium,gpio";
			reg = <0x0 0x28034000 0x0 0x1000>;
			interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>,
				     <GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH>,
				     <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>,
				     <GIC_SPI 111 IRQ_TYPE_LEVEL_HIGH>,
				     <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>,
				     <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>,
				     <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>,
				     <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>,
				     <GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>,
				     <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>,
				     <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>,
				     <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>,
				     <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>,
				     <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>,
				     <GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>,
				     <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>;
			gpio-controller;
			#gpio-cells = <2>;
			#address-cells = <1>;
			#size-cells = <0>;

			porta {
				compatible = "phytium,gpio-port";
				reg = <0>;
				ngpios = <16>;
			};
		};

		gpio3: gpio@28037000 {
			compatible = "phytium,gpio";
			reg = <0x0 0x28037000 0x0 0x1000>;
			interrupts = <GIC_SPI 156 IRQ_TYPE_LEVEL_HIGH>;
			gpio-controller;
			#gpio-cells = <2>;
			#address-cells = <1>;
			#size-cells = <0>;
			status = "disabled";

			porta {
				compatible = "phytium,gpio-port";
				reg = <0>;
				ngpios = <16>;
			};
		};

这里是gpio0和gpio3这个两个gpio模块,其实e2000有0到5,一共6个gpio模块,每个模块都有16个gpio,其中gpio0-2每个gpio都有一个硬件中断号,而gpio3-5则是这个组共享一个硬件中断号。当然,我看其他arm64的gpio都有复用功能,所以gpio的设备树写在pinctrl里面,现在e2000的gpio没有复用到其他模块,所以没有使用pinctrl。

2.2 驱动的数据结构

以我多年的驱动经验来看,所有的gpio控制器驱动做的事情不外乎以下几种:

  1. 分配私有数据结构体,这个结构体会包含struct gpio_chip结构体
  2. 读取设备树信息,填充gpio_chip结构体
  3. 把gpio_chip结构体绑定到平台设备中

实际上gpio的驱动跟中断和pinctrl放在一起的,初始化的时候会把irq_chip和 struct pinctrl_desc 和pinctrl_dev 一起填充的,这里主要讲解gpio,就不多扩展了。我们先看struct gpio_chip结构体:

struct gpio_chip {
	const char		*label;				//gpio控制器的名字
	struct gpio_device	*gpiodev;		//gpio设备描述符
	struct device		*parent;		//gpio的父设备
	struct module		*owner;

	//下面是一系列的操作函数
	int	 (*get_direction)(struct gpio_chip *gc,unsigned int offset);
	int	 (*direction_input)(struct gpio_chip *gc,unsigned int offset);
	int  (*direction_output)(struct gpio_chip *gc,unsigned int offset, int value);
	int	 (*get)(struct gpio_chip *gc,unsigned int offset);
	void (*set)(struct gpio_chip *gc,unsigned int offset, int value);

	int			base;		//gpio引脚基值
	u16			ngpio;		//gpio引脚个数
	const char		*const *names;		//每一个引脚的名字
	bool			can_sleep;			//控制器是否能睡眠

	void __iomem *reg_dat;			//gpio数据寄存器基地址
	void __iomem *reg_set;			//gpio设置寄存器基地址
	void __iomem *reg_clr;			//gpio控制寄存器基地址
	void __iomem *reg_dir_out;		//gpio输出寄存器基地址
	void __iomem *reg_dir_in;		//gpio输入寄存器基地址
};

gpio_chip结构体中包含了对应GPIO端口的硬件基地址、引脚数、GPIO组数、GPIO编号和IRQ号等重要信息。同时,它还包括了访问GPIO寄存器的函数指针,例如读取和写入寄存器等函数,以及描述GPIO的信息和配置的特定标志。其中最重要的是struct gpio_device *gpiodev; 他表示gpio设备描述符。这个结构体存放着gpio控制器的设备信息和每个引脚的描述符结构体,我们来看看struct gpio_device结构体:

struct gpio_device {
	int			id;				//表示这是系统中第几个GPIO控制器
	struct device		dev;
	struct cdev		chrdev;
	struct device		*mockdev;
	struct module		*owner;
	struct gpio_chip	*chip;		//gpio管理硬件的结构体,记录寄存器信息及其操作函数
	struct gpio_desc	*descs;		//引脚的描述符指针,每一个引脚对应一个gpio_desc结构体
	int			base;				//gpio号码基值
	u16			ngpio;				//gpio个数
	const char		*label;			//gpio控制器的名字
	void			*data;
	struct list_head        list;
	struct blocking_notifier_head notifier;
};

gpio_device结构体中包含了与GPIO设备(GPIO控制器)相关的重要信息,例如GPIO控制器ID、GPIO号、GPIO个数、GPIO管理硬件的结构体和gpio引脚描述符结构体。最重要的是GPIO管理硬件的结构体gpio_chip 就是刚刚第一个介绍的数据结构,gpio设备是通过gpio_chip 找到对用的硬件信息和操作方法的;其次是gpio_desc结构体,每一个引脚对应一个gpio_desc结构体,他们通过数组的形式排列,我们看看gpio_desc结构体:

struct gpio_desc {
	struct gpio_device	*gdev;		//属于哪个GPIO控制器
	unsigned long		flags;		//gpio引脚属性,比如是否被使用、是否开漏等等
	
	/* Connection label */
	const char		*label;
	/* Name of the GPIO */
	const char		*name;			//引脚名字
#ifdef CONFIG_OF_DYNAMIC
	struct device_node	*hog;
#endif
#ifdef CONFIG_GPIO_CDEV
	/* debounce period in microseconds */
	unsigned int		debounce_period_us;
#endif
};

gpio_desc结构体主要记录GPIO引脚的硬件信息和状态信息。

在这里插入图片描述
这3个结构体的关系如上图所示,以上这3个结构体就包含了gpio控制器的硬件信息、状态信息和操作方法集合,这就我们的设备驱动提供了底层的基础,我们的驱动就是通过gpiolib抽象层提供的API调用到这个操作方法的。

3. 设备驱动

在控制器驱动准备好的情况下,我们使用gpio是一件很简单的事情,就拿es8336这个网卡驱动举个例子,先看设备树:

	mio14: i2c@28030000 {
...
		codec0:es8336@10 {
			det-gpios = <&gpio2 5 0>;
			sel-gpios = <&gpio2 6 0>;
...
	   };
	};

其中gpio的设备树就两行,其他不重要的就忽略了。
驱动文件在sound/soc/codecs/es8336.c:

	es8336->spk_ctl_gpio = devm_gpiod_get_index_optional(&i2c->dev, "sel", 0,
							GPIOD_OUT_HIGH);
	ret = of_property_read_u8(i2c->dev.of_node, "mic-src", &es8336->mic_src);
	if (ret != 0) {
		dev_dbg(&i2c->dev, "mic1-src return %d", ret);
		es8336->mic_src = 0x20;
	}
	dev_dbg(&i2c->dev, "mic1-src %x", es8336->mic_src);

	if (!es8336->spk_ctl_gpio)
		dev_info(&i2c->dev, "Can not get spk_ctl_gpio\n");
	else
		es8336_enable_spk(es8336, false);

	es8336->hp_det_gpio = devm_gpiod_get_index_optional(&i2c->dev, "det", 0,
							GPIOD_IN);

	if (!es8336->hp_det_gpio) {
		dev_info(&i2c->dev, "Can not get hp_det_gpio\n");
	} else {
		INIT_DELAYED_WORK(&es8336->work, hp_work);
		hp_irq = gpiod_to_irq(es8336->hp_det_gpio);
		ret = devm_request_threaded_irq(&i2c->dev, hp_irq, NULL,
						es8336_irq_handler,
						IRQF_TRIGGER_FALLING |
						IRQF_TRIGGER_RISING |
						IRQF_ONESHOT,
						"es8336_interrupt", es8336);
		if (ret < 0) {
			dev_err(&i2c->dev, "request_irq failed: %d\n", ret);
			return ret;
		}
	}

其实设备驱动都是使用gpiolib提供的API:

  1. gpiod_get_indexed:通过索引号获取GPIO设备引脚。
  2. gpiod_get_optional:尝试获取GPIO设备引脚,如果失败返回NULL,不会导致注册失败。
  3. gpiod_get_optional_indexed:与gpiod_get_optional类似,但通过索引号获取GPIO设备引脚。
  4. gpiod_get_raw:通过GPIO编号获取GPIO设备引脚,不进行方向和值的配置。
  5. devm_gpiod_get:这个函数自动为一个特定的设备申请所需的GPIO,不需要手动释放,适合临时使用的GPIO资源。
  6. devm_gpiod_get_index:这个函数允许为使用多个GPIO的设备申请多个GPIO引脚,返回一个struct gpiod_hanlde数组。
  7. devm_gpiod_get_optional:如果存在,允许驱动程序获取所需的GPIO,而不会阻止设备与其余的GPIO资源一起初始化。
  8. devm_gpiod_get_optional_index:类似于devm_gpiod_get_optional,支持索引GPIO。
  9. gpio_request:向内核申请一个GPIO引脚。
  10. gpio_free:释放一个已经使用的GPIO引脚。
  11. gpio_direction_input:配置GPIO引脚为输入模式。
  12. gpio_direction_output:配置GPIO引脚为输出模式。
  13. gpio_get_value:读取GPIO引脚状态。如果 GPIO 引脚已配置为输出模式,则返回当前输出值。如果 GPIO 引脚未配置或配置为输入模式,则返回实际引脚上的输入值。
  14. gpio_set_value:设置GPIO引脚状态为高或低电平。如果 GPIO 引脚已配置为输出模式,则设置GPIO引脚状态为用户指定电平;如果 GPIO 引脚是输入模式,此函数没有作用。
  15. gpio_to_irq:将 GPIO 引脚转换为专用中断号。
  16. gpio_request_one:请求单个GPIO。
  17. gpio_free_array:释放一组由gpio_request_array()调用请求的GPIO。
  18. gpio_direction_input_array:将一组GPIO方向设置为输入模式。
  19. gpio_direction_output_array:将一组GPIO方向设置为输出模式。
  20. gpio_get_array:将一组GPIO值读入缓冲区中。
  21. gpio_set_array:将一组GPIO指定的值写入用户指定的缓冲区中。
  22. gpio_get_value_cansleep:读取GPIO引脚状态,如果引脚已配置为输出模式,则将与其关联的电平值复制到调用函数的参数变量中,如果引脚已配置为输入模式,则等待GPIO中断或超时发生后将其值复制到调用函数的参数变量中。
  23. gpio_set_value_cansleep:设置GPIO端口电平,如果引脚仍被配置为输入模式,则什么也不做。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

什么是SVG格式?如何制作?

图像质量对页面非常重要——扭曲和缩放变形的标志、图标或照片会使页面看起来粗糙和不协调&#xff0c;这个问题只会因为响应设计而复杂。 访问者通过桌面机和智能手机查看应用程序&#xff0c;因此无论使用什么设备&#xff0c;图像都应该进行优化。如果有一个数字格式可以让…

基于FPGA+JESD204B 时钟双通道 6.4GSPS 高速数据采集设计(三)连续多段触发存储及传输逻辑设计

本章将完成数据速率为 80MHz 、位宽为 12bits 的 80 路并行采样数据的连续多 段触发存储。首先&#xff0c;给出数据触发存储的整体框架及功能模块划分。然后&#xff0c;简介 MIG 用户接口、设置及读写时序。最后&#xff0c;进行数据跨时钟域模块设计&#xff0c;内存…

XC7VX690T PCIE 硬件设计注意事项

首先参考PG023找到对应封装支持的位置 然后参考UG476找到对应的实际物理位置 XILINX 也有给出对应的推荐位置

gitlab服务器发送邮件配置

1.修改gitlab的配置文件&#xff1a; vim /etc/gitlab/gitlab.rb 这里具体的gitlab.rb文件所在路径需要根据实际的来 找到如下图所示的部分&#xff0c;放开注释&#xff0c;修改配置&#xff0c;此处我用的发件邮箱是QQ邮箱&#xff0c;所以域名配置都是qq.com&#xff0c;…

Springboot application/yaml/yml没有提示解决方案

有下面提示别安装该插件 卸载wl spring assistant plugin就好了,标志是yml图标变成六边形

线程间互斥-mutex互斥锁和lock_guard

要点 锁双重判断的技法 竟态条件&#xff1a;多线程程序执行的结果一致&#xff0c;不会随着CPU对线程不同的调用顺序 线程间安全实例——3个窗口同时卖票 线程不安全的代码如下 int ticketCount 100; // 100张车票 // 模拟10个窗口同时卖票 void sellTicket(int index) …

PostgreSQL11 | 索引

截止到上一篇《PostgreSQL11 | 查询数据》属于pgsql的基础部分就算是都总结完了&#xff0c;从这一篇&#xff08;第9章&#xff09;开始一直到本专栏最后一篇文章&#xff08;第14章&#xff09;都是进阶部分&#xff0c;sql量会减弱&#xff0c;抽象的概念会越来越多&#xf…

数字识别问题

文章目录 6.1 MNIST数据处理6.2.1 训练数据6.2.2 变量管理6.3.1 保存模型6.3.1 加载计算图6.3.1 加载模型6.3.2 导出元图 6.1 MNIST数据处理 在直接在第6章的目录下面创建文件 compat.v1.是tensorflow2.x的语法&#xff0c;全部删掉 删除compat.v1.后的代码 # -*- coding: …

【SCI一区】考虑P2G和碳捕集设备的热电联供综合能源系统优化调度模型(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

学术必备的21个论文网站,建议收藏!

1、综合型论文网站&#xff08;国内&#xff09; &#xff08;1&#xff09;知网 介绍&#xff1a;国内知名度最高的网站&#xff0c;拥有上亿篇各种论文期刊&#xff0c;包含中国学术文献、 外文文献、学位论文、报纸、会议、年鉴、工具书等各类资源统一检索、统一导 航、…

第四章 图像的形态学操作

文章目录 前言一、阈值控制二、腐蚀与膨胀1.腐蚀2.膨胀3.形态学操作 总结 前言 前面讲解了图像基础理论、图像的变换以及图像滤波等操作&#xff0c;本章&#xff0c;将会介绍图像的形态学操作。 图像的形态学指的是一组数学方法和工具&#xff0c;用于图像分析和处理。形态学…

(7)Qt---文件IO

目录 1. QFileDialog 文件选择对话框** 2. QFileInfo 文件信息类** 3. QFile 文件读写类*** 4. UI与耗时操作** 5. QThread 线程类 5.1 复现阻塞 5.2 新建并启动子线程 5.3 异步刷新 5.4 停止线程 1. QFileDialog 文件选择对话框** 操作系统会提供一个统一样式的文件选择对话框…

从本地到云端:豆瓣如何使用 JuiceFS 实现统一的数据存储

豆瓣成立于 2005 年&#xff0c;是中国最早的社交网站之一。在 2009 到 2019 的十年间&#xff0c;豆瓣数据平台经历了几轮变迁&#xff0c;形成了 DPark Mesos MooseFS 的架构。 由机房全面上云的过程中&#xff0c;原有这套架构并不能很好的利用云的特性&#xff0c;豆瓣需…

少林派问题汇总

少林派问题汇总&#xff1a; Q: A:缺少bmodel,没有指定bmodel的路径&#xff0c;测试图片不在同一文件路径下 复制过来就解决了 Q: docker容器下运行./install_lib.sh nntc会rm不到文件怎么回事? A&#xff1a;文件已经被删除 Q: 我将pytorch的模型用export工具转换成.torch…

聚焦丨酷雷曼荣列XRMA联盟成员单位

自“元宇宙”概念兴起之初&#xff0c;酷雷曼VR所属北京同创蓝天云科技有限公司就积极布局、探索和实践。2022年12月&#xff0c;酷雷曼VR成功加入虚拟现实与元宇宙产业联盟&#xff08;XRMA&#xff09;&#xff0c;正式被接纳为联盟成员单位&#xff0c;意味着酷雷曼公司将进…

详细版易学版TypeScript - 元组 枚举详解

一、元组(Tuple) 数组:合并了相同类型的对象 const myArr: Array<number> [1, 2, 3]; 元组(Tuple):合并了不同类型的对象 // 定义元组时就要确定好数据的类型&#xff0c;并一一对应 const tuple: [number, string] [12, "hi"]; // 添加内容时&#xff0c;不…

代码随想录算法训练营day35 | 860.柠檬水找零,406.根据身高重建队列,452. 用最少数量的箭引爆气球

代码随想录算法训练营day35 | 860.柠檬水找零&#xff0c;406.根据身高重建队列&#xff0c;452. 用最少数量的箭引爆气球 860.柠檬水找零406.根据身高重建队列452. 用最少数量的箭引爆气球 860.柠檬水找零 教程视频&#xff1a;https://www.bilibili.com/video/BV12x4y1j7DD/…

3.SpringBoot开发实用篇

SpringBoot开发实用篇 ​ 开发实用篇中因为牵扯到SpringBoot整合各种各样的技术&#xff0c;由于不是每个小伙伴对各种技术都有所掌握&#xff0c;所以在整合每一个技术之前&#xff0c;都会做一个快速的普及&#xff0c;这样的话内容整个开发实用篇所包含的内容就会比较多。各…

推荐系统综述

这里写目录标题 推荐系统架构1、传统推荐方式1.1 基于内容推荐&#xff08;Content-Based recommendation&#xff0c;CB&#xff09;1.2 协同过滤推荐&#xff08;Collaborative Filtering recommendation&#xff0c; CF&#xff09;1.2.0 UserCF举例&#xff1a;1. 2. 1 基于…

window-2016服务器;服务——活动目录

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a;小刘主页 ♥️每天分享云计算网络运维课堂笔记&#xff0c;努力不一定有收获&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️树高千尺&#xff0c;落叶归根人生不易&…