GPIO系列(1)——MTK平台GPIO的配置,控制及驱动分析

本文详细介绍了MT8321和MT8665平台在Android 5.1和Kernel 3.10环境下GPIO的配置、控制和驱动分析。GPIO配置通过DrvGen工具和codegen.dws文件实现,控制使用mt_gpio.h头文件提供的函数,驱动分析中展示了GPIO模式设置的底层实现路径,涉及mt_gpio_probe、mt_gpio_ioctl等关键函数。
摘要由CSDN通过智能技术生成

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驱动的简单分析!

  • 6
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值