GPIO系列(1)——MTK平台GPIO的配置,控制及驱动分析_mtk codegen 按键配置-程序员宅基地

技术标签: # GPIO  mtk  android  linux  嵌入式  gpio  

MTK平台GPIO的配置,控制及驱动分析


平台:MT8321,MT8665
Android:5.1
Kernel:3.10

GPIO配置

MTK平台的GPIO配置,通过“DrvGen”工具和“codegen.dws”文件实现,在“preloader”,“lk”,“kernel”里面,都有对应的“DrvGen”工具和“codegen.dws”文件。具体每个部分编译的时候使用的工具和文件的目录,可以参考每一个部分的编译脚本“drvgen.mk”:

DRVGEN_TOOL := $(PWD)/tools/dct/DrvGen
DRVGEN_PATH := drivers/misc/mediatek/mach/$(MTK_PLATFORM)/$(MTK_PROJECT)/dct/$(PRIVATE_CUSTOM_KERNEL_DCT)
DWS_FILE := $(PWD)/$(DRVGEN_PATH)/codegen.dws

以“kernel”为例,
“DrvGen”工具在目录:kernel-3.10\tools\dct
“codegen.dws”文件在目录:kernel-3.10\drivers\misc\mediatek\mach\platform\project\dct\dct

使用“DrvGen”打开“codegen.dws”进行编辑,会出现如下图界面:
在这里插入图片描述
在上面界面可以做各种GPIO的配置,比如是否是中断管脚“EintMode”;对应管脚的功能“M0~M7”,默认“M0”是GPIO功能;管脚方向,是否上拉等。最后有一个“VarName1”,这实际是给GPIO命了一个名字,这里有下拉框,可以选择不同的名字,如下图:
在这里插入图片描述
可以根据管脚的功能,选择对应的名字,如果需要自定义名字,可以修改“DrvGen”工具目录下的“GPIO_YuSu.cmp”文件,新增自定义名字进去,然后在上图的下拉框就可以找到新增的名字了。

这个名字,在编译的时候,解析完“codegen.dws”文件之后,会在“cust_gpio_usage.h”文件生成用这个名字命名的一系列宏,如下:

#define GPIO_CTP_EINT_PIN         (GPIO4 | 0x80000000)
#define GPIO_CTP_EINT_PIN_M_EINT   GPIO_MODE_00
#define GPIO_CTP_EINT_PIN_M_GPIO   GPIO_MODE_00
#define GPIO_CTP_EINT_PIN_M_CLK   GPIO_MODE_01
#define GPIO_CTP_EINT_PIN_CLK     CLK_OUT2
#define GPIO_CTP_EINT_PIN_FREQ    GPIO_CLKSRC_NONE

后面在代码里面控制GPIO就可以使用这些宏,代码需要包含如下头文件:

#include <mach/mt_gpio.h>

上面说的“cust_gpio_usage.h”文件,编译的时候会自动在out目录下生成一份,还有一份在“codegen.dws”文件所在的目录,一般需要在配置完GPIO之后,点击如下按钮手动生成:
在这里插入图片描述
以上就是GPIO的配置部分。

GPIO控制

在代码里面控制GPIO,需要先包含头文件:

#include <mach/mt_gpio.h>

这个头文件里面有下面一些控制函数:

/******************************************************************************
* GPIO Driver interface
******************************************************************************/
/*direction*/
int mt_set_gpio_dir(unsigned long pin, unsigned long dir);
int mt_get_gpio_dir(unsigned long pin);

/*pull enable*/
int mt_set_gpio_pull_enable(unsigned long pin, unsigned long enable);
int mt_get_gpio_pull_enable(unsigned long pin);

/*schmitt trigger*/
int mt_set_gpio_smt(unsigned long pin, unsigned long enable);
int mt_get_gpio_smt(unsigned long pin);

/*IES*/
int mt_set_gpio_ies(unsigned long pin, unsigned long enable);
int mt_get_gpio_ies(unsigned long pin);

/*pull select*/
int mt_set_gpio_pull_select(unsigned long pin, unsigned long select);
int mt_get_gpio_pull_select(unsigned long pin);

/*data inversion*/
int mt_set_gpio_inversion(unsigned long pin, unsigned long enable);
int mt_get_gpio_inversion(unsigned long pin);

/*input/output*/
int mt_set_gpio_out(unsigned long pin, unsigned long output);
int mt_get_gpio_out(unsigned long pin);
int mt_get_gpio_in(unsigned long pin);

/*mode control*/
int mt_set_gpio_mode(unsigned long pin, unsigned long mode);
int mt_get_gpio_mode(unsigned long pin);

/*misc functions for protect GPIO*/
/* void mt_gpio_dump(GPIO_REGS *regs,GPIOEXT_REGS *regs_ext); */
void gpio_dump_regs(void);

如下方式可以控制GPIO输出高电平:

mt_set_gpio_mode(GPIO_CTP_EINT_PIN, GPIO_CTP_EINT_PIN_M_GPIO);
mt_set_gpio_dir(GPIO_CTP_EINT_PIN, GPIO_DIR_OUT);
mt_set_gpio_out(GPIO_CTP_EINT_PIN, GPIO_OUT_ONE);

其中“GPIO_CTP_EINT_PIN”和“GPIO_CTP_EINT_PIN_M_GPIO”就是之前配置GPIO的时候,在“cust_gpio_usage.h”文件里面生成的宏。“GPIO_DIR_OUT”和“GPIO_OUT_ONE”是“mt_gpio.h”文件里面定义的宏,这个文件在:kernel-3.10\include\mach

以此类推,可以使用“mt_gpio.h”文件里面的函数,控制GPIO的输入输出,模式切换等。

GPIO驱动分析

简单分析一下控制GPIO的“mt_set_gpio_mode”这些函数的驱动。

“mt_set_gpio_mode”函数的实现,在文件“mt_gpio_core.c”,目录:kernel-3.10\drivers\misc\mediatek\gpio

分析一下这个文件,先通过“platform_driver_register”注册了GPIO的设备驱动:

#ifdef CONFIG_OF
static const struct of_device_id apgpio_of_ids[] = {
    
	{
     .compatible = "mediatek,GPIO", },
	{
    }
};
#endif

static struct platform_driver gpio_driver = {
    
	.probe = mt_gpio_probe,
	.remove = mt_gpio_remove,
#ifdef CONFIG_PM
	.suspend = mtk_gpio_suspend,
	.resume = mtk_gpio_resume,
#endif
	.driver = {
    
		.name = GPIO_DEVICE,
#ifdef CONFIG_OF
		.of_match_table = apgpio_of_ids,
#endif
		},
};

static int __init mt_gpio_init(void)
{
    
	int ret = 0;
	GPIOLOG("version: %s\n", VERSION);

	ret = platform_driver_register(&gpio_driver);
	return ret;
}

根据使用的“of_device_id”里面的“mediatek,GPIO”,查看dts文件如下:

		GPIO@0x10211000 {
    
			compatible = "mediatek,GPIO";
			reg = <0x10211000 0x1000>;
		};

就比较简单记录了GPIO硬件的寄存器地址,接下来看下“mt_gpio_probe”函数:

/*---------------------------------------------------------------------------*/
static struct file_operations mt_gpio_fops = {
    
	.owner = THIS_MODULE,
	.unlocked_ioctl = mt_gpio_ioctl,
	#ifdef CONFIG_COMPAT
	.compat_ioctl   = mt_gpio_ioctl,
	#endif
	.open = mt_gpio_open,
	.release = mt_gpio_release,
};

/*----------------------------------------------------------------------------*/
static struct miscdevice mt_gpio_device = {
    
	.minor = MISC_DYNAMIC_MINOR,
	.name = "mtgpio",
	.fops = &mt_gpio_fops,
};

/*---------------------------------------------------------------------------*/
static int mt_gpio_probe(struct platform_device *dev)
{
    
	int err;
	struct miscdevice *misc = &mt_gpio_device;

#ifdef CONFIG_OF
	if (dev->dev.of_node) {
    
		/* Setup IO addresses */
		get_gpio_vbase(dev->dev.of_node);
	}
	get_io_cfg_vbase();
#endif
	......
	if ((err = misc_register(misc)))
		GPIOERR("register gpio\n");
	......
	return err;
}

这部分代码比较主要的两个地方,一个是使用“get_gpio_vbase”函数设置了GPIO的基地址,就是dts文件里面记录的,这个函数在文件“mt_gpio_base.c”里,目录:kernel-3.10\drivers\misc\mediatek\gpio\platform;

一个是使用“misc_register”函数注册了一个misc设备,名字为“mtgpio”,对应的有open,release,ioctl函数。
主要看下ioctl函数:

static long mt_gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    
	struct mt_gpio_obj_t *obj = mt_gpio;
	long res;
	unsigned long pin;
	
	......
	
	switch (cmd) {
    
	......
	case GPIO_IOCTMODE0:
		{
    
			pin = (unsigned long)arg;
			res =
			    GIO_INVALID_OBJ(obj) ? (-EACCES) : mt_set_gpio_mode(pin, GPIO_MODE_00);
			break;
		}
	......
	return res;
}

这就看到了之前使用的“mt_set_gpio_mode”函数,这里就是允许用户层程序,通过misc设备,来控制GPIO。

int mt_set_gpio_mode(unsigned long pin, unsigned long mode)
{
    
	if (mode >= GPIO_MODE_MAX) {
    
		GPIOERR("Parameter mode error: %d\n", (int)mode);
		return -ERINVAL;
	}
	return MT_GPIO_OPS_SET(pin, set_mode, mode);
}
EXPORT_SYMBOL(mt_set_gpio_mode);

“mt_set_gpio_mode”函数主要就是使用了一个宏“MT_GPIO_OPS_SET”:

#define MT_GPIO_OPS_SET(pin, operation, arg) \
({   unsigned long flags;\
     u32 retval = 0;\
	mt_gpio_pin_decrypt(&pin);\
	spin_lock_irqsave(&mt_gpio_lock, flags);\
	switch (MT_GPIO_PLACE(pin)) {\
		case MT_BASE:\
			if ((mt_gpio->base_ops == NULL) || (mt_gpio->base_ops->operation == NULL)) {\
				GPIOERR("base access error, null point %d\n", (int)pin);\
				retval = -ERACCESS;\
			} else{\
				retval = mt_gpio->base_ops->operation(pin, arg);\
				if (retval < 0) \
					GPIOERR("base operation fail %d\n", (int)retval);\
			} \
			break;\
		case MT_EXT:\
			if ((mt_gpio->ext_ops == NULL) || (mt_gpio->ext_ops->operation == NULL)) {\
				GPIOERR("extention access error, null point %d\n", (int)pin);\
				retval = -ERWRAPPER;\
			} else{\
				retval = mt_gpio->ext_ops->operation(pin, arg);\
				if (retval < 0) \
					GPIOERR("ext operation fail %d\n", (int)retval);\
			} \
			break;\
		default:\
			GPIOERR("Parameter error: %d\n", (int)pin);\
			retval = -ERINVAL;\
			break;\
	} \
	spin_unlock_irqrestore(&mt_gpio_lock, flags);\
	retval; })

这个宏先判断“pin”是属于“MT_BASE”或者“MT_EXT”,对于“mt_set_gpio_mode”函数这个宏展开之后主要就是:

mt_gpio->base_ops->set_mode(pin, mode);//MT_BASE
mt_gpio->ext_ops->set_mode(pin, mode);//MT_EXT

这里使用了一个指针变量“mt_gpio”:

static struct mt_gpio_obj_t *mt_gpio = &mt_gpio_obj;

“mt_gpio”是对变量“mt_gpio_obj”取址:

struct mt_gpio_obj_t {
    
	atomic_t ref;
	dev_t devno;
	struct class *cls;
	struct device *dev;
	struct cdev chrdev;
	/* spinlock_t      lock; */
	struct miscdevice *misc;
	struct mt_gpio_ops *base_ops;
	struct mt_gpio_ops *ext_ops;
};
static struct mt_gpio_obj_t mt_gpio_obj = {
    
	.ref = ATOMIC_INIT(0),
	.cls = NULL,
	.dev = NULL,
	.base_ops = &mt_base_ops,//MT_BASE
	.ext_ops = &mt_ext_ops,//MT_EXT
	/* .lock = __SPIN_LOCK_UNLOCKED(die.lock), */
};

变量“mt_gpio_obj”里面的成员变量“base_ops”和“ext_ops”,又分别是对变量“mt_base_ops”和“mt_ext_ops”取址,这里以“mt_base_ops”为例:

struct mt_gpio_ops {
    
/* char name[MT_GPIO_MAX_NAME]; */
	int (*set_dir) (unsigned long pin, unsigned long dir);
	int (*get_dir) (unsigned long pin);
	int (*set_pull_enable) (unsigned long pin, unsigned long enable);
	int (*get_pull_enable) (unsigned long pin);
	int (*set_smt) (unsigned long pin, unsigned long enable);
	int (*get_smt) (unsigned long pin);
	int (*set_ies) (unsigned long pin, unsigned long enable);
	int (*get_ies) (unsigned long pin);
	int (*set_pull_select) (unsigned long pin, unsigned long select);
	int (*get_pull_select) (unsigned long pin);
	int (*set_inversion) (unsigned long pin, unsigned long enable);
	int (*get_inversion) (unsigned long pin);
	int (*set_out) (unsigned long pin, unsigned long output);
	int (*get_out) (unsigned long pin);
	int (*get_in) (unsigned long pin);
	int (*set_mode) (unsigned long pin, unsigned long mode);
	int (*get_mode) (unsigned long pin);
};
static struct mt_gpio_ops mt_base_ops = {
    
	.set_dir = mt_set_gpio_dir_base,
	.get_dir = mt_get_gpio_dir_base,
	.set_pull_enable = mt_set_gpio_pull_enable_base,
	.get_pull_enable = mt_get_gpio_pull_enable_base,
	.set_smt = mt_set_gpio_smt_base,	
	.get_smt = mt_get_gpio_smt_base,
	.set_ies = mt_set_gpio_ies_base,
	.get_ies = mt_get_gpio_ies_base,
	.set_pull_select = mt_set_gpio_pull_select_base,
	.get_pull_select = mt_get_gpio_pull_select_base,
	.set_inversion = mt_set_gpio_inversion_base,
	.get_inversion = mt_get_gpio_inversion_base,
	.set_out = mt_set_gpio_out_base,
	.get_out = mt_get_gpio_out_base,
	.get_in = mt_get_gpio_in_base,
	.set_mode = mt_set_gpio_mode_base,
	.get_mode = mt_get_gpio_mode_base,
};

变量“mt_base_ops”里面的成员变量“set_mode”,是一个函数指针,指向“mt_set_gpio_mode_base”函数,这就是我们使用“mt_set_gpio_mode”设置GPIO的时候,最终调用到的地方,在文件“mt_gpio_base.c”里,目录:kernel-3.10\drivers\misc\mediatek\gpio\platform

“mt_set_gpio_mode_base”函数就直接通过判断“pin”和“mode”,去写GPIO的寄存器,来实现需要的GPIO控制。

以上就是GPIO驱动的简单分析!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_21059825/article/details/113087944

智能推荐

pheatmap:绘制聚类热图的函数_pheatmap基于kmeans绘制热图-程序员宅基地

文章浏览阅读342次,点赞4次,收藏6次。该函数还允许使用 kmeans 聚类聚合行。如果行数太大,以至于 R 无法再处理其分层聚类,大约超过 1000 行,则建议这样做。与其单独显示所有行,不如提前对行进行聚类,并仅显示聚类中心。可以通过参数kmeans_k调整集群的数量。来源:https://www.rdocumentation.org/packages/pheatmap/versions/1.0.12/topics/pheatmap。一个绘制聚类热图的函数,可以更好地控制一些图形参数,如单元大小等。Examples 例子。_pheatmap基于kmeans绘制热图

html div四边阴影效果-程序员宅基地

文章浏览阅读369次。<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml..._div四周阴影效果

Java 通过反射获取实体类对应的注释_java获取实体类属性注解-程序员宅基地

文章浏览阅读1.7k次。Java 通过反射获取实体类对应的注释 _java获取实体类属性注解

在使用Mybatis的association属性,两张表中存在相同字段名,联表查询时的冲突解决办法_mybatis association绑定相同对象-程序员宅基地

文章浏览阅读2.3k次,点赞5次,收藏10次。在使用MyBatis进行多表联查时,想要获取关联外键的表的数据信息,使用association进行联查,但当外键表的字段名和主表相同时,外键表的数据就会被覆盖。_mybatis association绑定相同对象

湘潭大学2018年上学期程序设计实践模拟考试3 参考题解_在湘大xx奶茶店夏天推出了新的饮料价格为5元。 很多学生都要买饮料,每个学生-程序员宅基地

文章浏览阅读2.7k次。体验1: 军神太强啦,1小时屯6题,瞬间AK,接下来的90分钟一直在跟榜体验2: A题原题,循环写得好就不麻烦,不然要写很多行,情况要想全并不难。 B题原题,有了上一场的提示之后,这题就不难了。 C题很简单(小声)。 D题卡掉了O(T*N*K*log(N))的方法,卡掉我5发logN ,不过还是可做。 E题水dp(组合数学)。 F题原题,记忆化搜索。体验3: 被DC两题卡到..._在湘大xx奶茶店夏天推出了新的饮料价格为5元。 很多学生都要买饮料,每个学生

微信小程序预览pdf,页面缓存下载过的pdf_微信浏览器请求pdf文件会缓存吗-程序员宅基地

文章浏览阅读2.8k次。需求:  点击预览图标查看该pdf报告问题:  1、最早是直接将请求倒的url放入到<web-view src="{{realUrl}}"></web-view>中展示,ios可以,安卓显示无法查看。  2、通过微信自身的API实现:wx.downloadFile({})、wx.saveFile({})、wx.openDocument({})  3、对于文件较大的,下载较慢,需要点击过的进行缓存,再次点击无需下载,直接打开。  将点击过的下载的url添加给list的tem_微信浏览器请求pdf文件会缓存吗

随便推点

服务部署之配置网络策略服务(NPS)(基于Windows Server 2022)_windows server 部署网络策略服务-程序员宅基地

文章浏览阅读680次。服务部署之配置网络策略服务(NPS)(基于Windows Server 2022)_windows server 部署网络策略服务

视频异常检测 综述(二)_视频异常检测综述-程序员宅基地

文章浏览阅读4.8k次,点赞6次,收藏29次。基于距离、概率、重构的视频异常检测概述_视频异常检测综述

trajan割点模板-程序员宅基地

文章浏览阅读107次。洛谷P3388#include<bits/stdc++.h>using namespace std;typedef long long ll;typedef unsigned long long ull;const int N=2e4+5;const int mod=1e9+7;vector<int> g[N];set<int> v;int dfn[N],low[N],fa[N];int n,m,tot;void tarjan(int x){

linux如何关闭硬件加速,启用硬件加速是什么意思?如何关闭【详解】-程序员宅基地

文章浏览阅读1k次。导语:小编相信,经常会使用到电脑的朋友们,对于启用硬件加速这个词一定都是不陌生的吧!可是呢,对于一些电脑小白们来说,往往会搞不清楚,这个启用硬件加速到底是个什么意思呢?启用之后,我们的电脑又会发生什么变化呢?也有一些人,在启用之后,却不知道应该如何关闭这个硬件加速,接下来,小编就来为大家介绍一下启用硬件加速是什么意思,以及它应该如何关闭。启用硬件加速是什么意思?简而言之,硬件加速就是利用硬件模块来..._linux 禁用硬件加速合成、图层和素材面板

SHAP: 在我眼里,没有黑箱_python对shap的计算只能针对大数值吗-程序员宅基地

文章浏览阅读8.1k次,点赞13次,收藏191次。1. 写在前面很多高级的机器学习模型(xgboost, lgb, cat)和神经网络模型, 它们相对于普通线性模型在进行预测时往往有更好的精度,但是同时也失去了线性模型的可解释性, 所以这些模型也往往看作是黑箱模型, 在2017年,Lundberg和Lee的论文提出了SHAP值这一广泛适用的方法用来解释各种模型(分类以及回归), 使得前面的黑箱模型变得可解释了,这篇文章主要整理一下SHAP的使用, 这个在特征选择的时候特别好用。这次整理, 主要是在xgboost和lgb等树模型上的使用方式, 并且用一个_python对shap的计算只能针对大数值吗

【操作系统】考研真题攻克与重点知识点剖析 - 第 1 篇:操作系统概述_2021-程序员宅基地

文章浏览阅读625次。这篇文章深入探讨了操作系统的各个方面,以及相关的计算机科学概念。文章的结构包括对操作系统的定义和功能的讨论,涵盖了硬件管理、操作系统特征、启动过程、运行环境等多个方面。作者使用思维导图和具体版本(如哈工大版本、王道版本)作为辅助,系统性地介绍了操作系统的运行机制,包括中断与异常、系统调用等内容。文章还回顾了操作系统的历史发展,按照不同线索(如哈工大版本)进行叙述,涵盖了操作系统的发展与分类、体系结构等方面。最后,文章提到了一些考研真题,强调了对计算机科学相关概念的深入理解。_2021

推荐文章

热门文章

相关标签