概述
MTK平台GPIO的配置,控制及驱动分析
- GPIO配置
- GPIO控制
- 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.10toolsdct
“codegen.dws”文件在目录:kernel-3.10driversmiscmediatekmachplatformprojectdctdct
使用“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.10includemach
以此类推,可以使用“mt_gpio.h”文件里面的函数,控制GPIO的输入输出,模式切换等。
GPIO驱动分析
简单分析一下控制GPIO的“mt_set_gpio_mode”这些函数的驱动。
“mt_set_gpio_mode”函数的实现,在文件“mt_gpio_core.c”,目录:kernel-3.10driversmiscmediatekgpio
分析一下这个文件,先通过“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: %sn", 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 gpion");
......
return err;
}
这部分代码比较主要的两个地方,一个是使用“get_gpio_vbase”函数设置了GPIO的基地址,就是dts文件里面记录的,这个函数在文件“mt_gpio_base.c”里,目录:kernel-3.10driversmiscmediatekgpioplatform;
一个是使用“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: %dn", (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 %dn", (int)pin);
retval = -ERACCESS;
} else{
retval = mt_gpio->base_ops->operation(pin, arg);
if (retval < 0)
GPIOERR("base operation fail %dn", (int)retval);
}
break;
case MT_EXT:
if ((mt_gpio->ext_ops == NULL) || (mt_gpio->ext_ops->operation == NULL)) {
GPIOERR("extention access error, null point %dn", (int)pin);
retval = -ERWRAPPER;
} else{
retval = mt_gpio->ext_ops->operation(pin, arg);
if (retval < 0)
GPIOERR("ext operation fail %dn", (int)retval);
}
break;
default:
GPIOERR("Parameter error: %dn", (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.10driversmiscmediatekgpioplatform
“mt_set_gpio_mode_base”函数就直接通过判断“pin”和“mode”,去写GPIO的寄存器,来实现需要的GPIO控制。
以上就是GPIO驱动的简单分析!
最后
以上就是默默便当为你收集整理的GPIO系列(1)——MTK平台GPIO的配置,控制及驱动分析GPIO配置GPIO控制GPIO驱动分析的全部内容,希望文章能够帮你解决GPIO系列(1)——MTK平台GPIO的配置,控制及驱动分析GPIO配置GPIO控制GPIO驱动分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复