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

智能推荐

基于Kepler.gl 和 Google Earth Engine 卫星数据创建拉伸多边形地图-程序员宅基地

文章浏览阅读965次,点赞18次,收藏21次。现在我们有了 2021 年和 2023 年的 NDVI 数据帧,我们需要从 2021 年的值中减去 2023 年的值以捕获 NDVI 的差异。该数据集包括像素级别的植被值,我们将编写一个自定义函数来根据红色和绿色波段的表面反射率计算 NDVI。在我的上一篇文章中,我演示了如何将单个多边形分割/镶嵌为一组大小均匀的六边形。现在我们有了植被损失数据,让我们使用 Kepler.gl 可视化每个六边形的植被损失。将地图保存为 HTML 文件,在浏览器中打开 HTML 以获得更好的视图。现在我们将调用该函数并使用、

Echarts绘制任意数据的正态分布图_echarts正态分布图-程序员宅基地

文章浏览阅读3.3k次,点赞6次,收藏5次。正态分布,又称高斯分布或钟形曲线,是统计学中最为重要和常用的分布之一。_echarts正态分布图

Android中发送短信等普通方法_android bundle.get("pdus");-程序员宅基地

文章浏览阅读217次。首先要在Mainfest.xml中加入所需要的权限:[html] view plain copyprint?uses-permission android:name="android.permission.SEND_SMS"/> uses-permission android:name="android.permission.READ_SMS"/> _android bundle.get("pdus");

2021-07-26 WSL2 的安装和联网_wsl2 联网-程序员宅基地

文章浏览阅读2.6k次。0、说明最近在学习 Data Assimilation Research Testbed (DART) 相关内容,其软件是在 Unix/Linux 操作系统下编译和运行的 ,由于我的电脑是 Windows 10 的,DART 推荐可以使用 Windows Subsystem For Linux (WSL) 来创建一个 Windows 下的 Linux 子系统。以下的内容主要介绍如何安装 WSL2,以及 WSL2 的联网。1、如何在 Windows 10 下安装WSL具体的安装流程可以在 microso_wsl2 联网

DATABASE_LINK 数据库连接_添加 database link重复的数据库链接命-程序员宅基地

文章浏览阅读1k次。DB_LINK 介绍在本机数据库orcl上创建了一个prod_link的publicdblink(使用远程主机的scott用户连接),则用sqlplus连接到本机数据库,执行select * from scott.emp@prod_link即可以将远程数据库上的scott用户下的emp表中的数据获取到。也可以在本地建一个同义词来指向scott.emp@prod_link,这样取值就方便多了..._添加 database link重复的数据库链接命

云-腾讯云-实时音视频:实时音视频(TRTC)-程序员宅基地

文章浏览阅读3.1k次。ylbtech-云-腾讯云-实时音视频:实时音视频(TRTC)支持跨终端、全平台之间互通,从零开始快速搭建实时音视频通信平台1.返回顶部 1、腾讯实时音视频(Tencent Real-Time Communication,TRTC)拥有QQ十几年来在音视频技术上的积累,致力于帮助企业快速搭建低成本、高品质音视频通讯能力的完整解决方案。..._腾讯实时音视频 分享链接

随便推点

用c语言写个日历表_农历库c语言-程序员宅基地

文章浏览阅读534次,点赞10次,收藏8次。编写一个完整的日历表需要处理许多细节,包括公历和农历之间的转换、节气、闰年等。运行程序后,会输出指定年份的日历表。注意,这个程序只是一个简单的示例,还有很多可以改进和扩展的地方,例如添加节气、节日等。_农历库c语言

FL Studio21.1.1.3750中文破解百度网盘下载地址含Crack补丁_fl studio 21 注册机-程序员宅基地

文章浏览阅读1w次,点赞28次,收藏27次。FL Studio21.1.1.3750中文破解版是最优秀、最繁荣的数字音频工作站 (DAW) 之一,日新月异。它是一款录音机和编辑器,可让您不惜一切代价制作精美的音乐作品并保存精彩的活动画廊。为方便用户,FL Studio 21提供三种不同的版本——Fruity 版、Producer 版和签名版。所有这些版本都是独一无二的,同样具有竞争力。用户可以根据自己的需要选择其中任何一种。FL Studio21.1.1.3750中文版可以说是一站式综合音乐制作单位,可以让您录制、作曲、混音和编辑音乐。_fl studio 21 注册机

冯.诺伊曼体系结构的计算机工作原理是,冯 诺依曼型计算机的工作原理是什么...-程序员宅基地

文章浏览阅读1.3k次。冯诺依曼计算机工作原理冯 诺依曼计算机工作原理的核心是 和 程序控制世界上不同型号的计算机,就其工作原理而言,一般都是认为冯 诺依曼提出了什么原理冯 诺依曼原理中,计算机硬件系统由那五大部分组成的 急急急急急急急急急急急急急急急急急急急急急急冯诺依曼结构计算机工作原理的核心冯诺依曼结构和现代计算机结构模型 转载重学计算机组成原理 一 冯 诺依曼体系结构从冯.诺依曼的存储程序工作原理及计算机的组成来..._简述冯诺依曼计算机结构及工作原理

四国军棋引擎开发(2)简单的事件驱动模型下棋-程序员宅基地

文章浏览阅读559次。这次在随机乱下的基础上加上了一些简单的处理,如进营、炸棋、吃子等功能,在和敌方棋子产生碰撞之后会获取敌方棋子大小的一些信息,目前采用的是事件驱动模型,当下完一步棋界面返回结果后会判断是否触发了相关事件,有事件发生则处理相关事件,没有事件发生则仍然是随机下棋。1.事件驱动模型首先定义一个各种事件的枚举变量,目前的事件有工兵吃子,摸暗棋,进营,明确吃子,炸棋。定义如下:enum MoveE..._军棋引擎

STL与泛型编程-第一周笔记-Geekband-程序员宅基地

文章浏览阅读85次。1, 模板观念与函数模板简单模板: template< typename T > T Function( T a, T b) {… }类模板: template struct Object{……….}; 函数模板 template< class T> inline T Function( T a, T b){……} 不可以使用不同型别的..._geekband 讲义

vb.net正则表达式html,VB.Net常用的正则表达式(实例)-程序员宅基地

文章浏览阅读158次。"^\d+$"  //非负整数(正整数 + 0)"^[0-9]*[1-9][0-9]*$"  //正整数"^((-\d+)|(0+))$"  //非正整数(负整数 + 0)"^-[0-9]*[1-9][0-9]*$"  //负整数"^-?\d+$"    //整数"^\d+(\.\d+)?$"  //非负浮点数(正浮点数 + 0)"^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0..._vb.net 正则表达式 取html中的herf