Freescale i.MX53 GPIO 按键驱动
硬件平台:IMX53-QSB
内核版本:LINUX-2.6.35.3
系统版本:ANDROID 2.3.4
一、GPIO
的使用
按键的处理需要读取相应
IO 引脚的值,阅读 IMX53 处理器芯片手册,得知将 GPIO 读模式大的步骤如下:
1. 通过设置 IOMUX 将相应引脚配置为 GPIO 模式,控制的寄存器是
IOMUXC_SW_MUX_CTL_PAD_XXX
2. 配置 GPIO 的方向为输入,控制的寄存器是 GPIOx_GDIR ,0表示输入,1表示输出
3. 读取相应 GPIO 引脚的值,读取的寄存器为 GPIOx_PSR
第1步,配置为 gpio 模式(以 MX53_PAD_GPIO_2__GPIO1_2 为例)
在文件 arch/arm/mach-mx5/mx53_loco.c 中
板子初始化函数 mxc_board_init 中,调用了 io 初始化函数 mx53_loco_io_init
:
static void __init mxc_board_init(void)
{
mx53_loco_io_init();
}
mx53_loco_io_init 函数中调用 mxc_iomux_v3_setup_multiple_pads
函数配置引脚:
static void __init mx53_loco_io_init(void)
{
mxc_iomux_v3_setup_multiple_pads(mx53_loco_pads,ARRAY_SIZE(mx53_loco_pads));
}
mx53_loco_pads 为一 u64 类型的数组:
static iomux_v3_cfg_t mx53_loco_pads[] = {
MX53_PAD_GPIO_2__GPIO1_2,
};
MX53_PAD_GPIO_2__GPIO1_2 定义在
arch/arm/plat-mxc/include/mach/iomux-mx53.h
#define MX53_PAD_GPIO_2__GPIO1_2
(_MX53_PAD_GPIO_2__GPIO1_2 |
MUX_PAD_CTRL(NO_PAD_CTRL))
MX53_PAD_GPIO_2__GPIO1_2 由 _MX53_PAD_GPIO_2__GPIO1_2 与
MUX_PAD_CTRL(NO_PAD_CTRL) 按位或得到,
先来看 _MX53_PAD_GPIO_2__GPIO1_2 :
#define _MX53_PAD_GPIO_2__GPIO1_2
IOMUX_PAD(0x6B8, 0x328, 1, 0x0, 0,
0)
IOMUX_PAD 的定义在
arch/arm/plat-mxc/include/mach/iomux-v3.h
#define IOMUX_PAD(_pad_ctrl_ofs, _mux_ctrl_ofs, _mux_mode,
_sel_input_ofs,_sel_input, _pad_ctrl)
\
( ((iomux_v3_cfg_t)(_mux_ctrl_ofs) <<
MUX_CTRL_OFS_SHIFT)|\
((iomux_v3_cfg_t)(_mux_mode) <<
MUX_MODE_SHIFT)|\
((iomux_v3_cfg_t)(_pad_ctrl_ofs) <<
MUX_PAD_CTRL_OFS_SHIFT)|\
((iomux_v3_cfg_t)(_pad_ctrl) <<
MUX_PAD_CTRL_SHIFT)|\
((iomux_v3_cfg_t)(_sel_input_ofs) <<
MUX_SEL_INPUT_OFS_SHIFT)|\
((iomux_v3_cfg_t)(_sel_input) <<
MUX_SEL_INPUT_SHIFT) )
#define MUX_CTRL_OFS_SHIFT 0
#define MUX_PAD_CTRL_OFS_SHIFT 12
#define MUX_SEL_INPUT_OFS_SHIFT 24
#define MUX_MODE_SHIFT 36
#define MUX_PAD_CTRL_SHIFT 41
#define MUX_SEL_INPUT_SHIFT 58
那么 IOMUX_PAD 是这个样子:
__________
|61|60|59|58|
_sel_input
__________________________________________
|57|56|55|54|53|52|51|50|49|48|47|46|45|44|43|42|41|
_pad_ctrl
_____________
|40|39|38|37|36|
_mux_mode
______________________________
|35|34|33|32|31|30|29|28|27|26|25|24|
_sel_input_ofs
______________________________
|23|22|21|20|19|18|17|16|15|14|13|12|
_pad_ctrl_ofs
________________________
|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1|
0|
_mux_ctrl_ofs
这样 _MX53_PAD_GPIO_2__GPIO1_2 即 IOMUX_PAD(0x6B8, 0x328, 1, 0x0, 0,
0) 是这个样子:
__________
|61|60|59|58|
0
__________________________________________
|57|56|55|54|53|52|51|50|49|48|47|46|45|44|43|42|41|
0
____________
|40|39|38|37|36|
1
______________________________
|35|34|33|32|31|30|29|28|27|26|25|24|
0x0
______________________________
|23|22|21|20|19|18|17|16|15|14|13|12|
0x6B8
________________________
|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1|
0|
0x328
再来看看 MUX_PAD_CTRL(NO_PAD_CTRL)
#define MUX_PAD_CTRL(x) ((iomux_v3_cfg_t)(x)
<< MUX_PAD_CTRL_SHIFT)
#define NO_PAD_CTRL ((iomux_v3_cfg_t)1 <<
(MUX_PAD_CTRL_SHIFT + 16))
这 MUX_PAD_CTRL(NO_PAD_CTRL) 将 1 左移了 98 位,代表什么意思?
这样 MX53_PAD_GPIO_2__GPIO1_2 的值就确定了。
mxc_iomux_v3_setup_multiple_pads 定义在 arch/arm/plat-mxc/iomux-v3.c
:
int mxc_iomux_v3_setup_multiple_pads(iomux_v3_cfg_t *pad_list,
unsigned count)
{
iomux_v3_cfg_t *p =
pad_list;
int
i;
for (i = 0;
i < count; i++) {
mxc_iomux_v3_get_pad(p);
p++;
}
return
0;
}
mxc_iomux_v3_setup_pad 函数定义:
int mxc_iomux_v3_setup_pad(iomux_v3_cfg_t pad)
{
u32
mux_ctrl_ofs = (pad & MUX_CTRL_OFS_MASK) >>
MUX_CTRL_OFS_SHIFT; // 0x328
u32 mux_mode
= (pad & MUX_MODE_MASK) >>
MUX_MODE_SHIFT;
u32
sel_input_ofs = (pad & MUX_SEL_INPUT_OFS_MASK) >>
MUX_SEL_INPUT_OFS_SHIFT;
u32
sel_input = (pad & MUX_SEL_INPUT_MASK) >>
MUX_SEL_INPUT_SHIFT;
u32
pad_ctrl_ofs = (pad & MUX_PAD_CTRL_OFS_MASK) >>
MUX_PAD_CTRL_OFS_SHIFT;
u32 pad_ctrl
= (pad & MUX_PAD_CTRL_MASK) >>
MUX_PAD_CTRL_SHIFT;
if
(mux_ctrl_ofs)
__raw_writel(mux_mode, base +
mux_ctrl_ofs);
if
(sel_input_ofs)
__raw_writel(sel_input, base +
sel_input_ofs);
if
(!(pad_ctrl & NO_PAD_CTRL) &&
pad_ctrl_ofs)
__raw_writel(pad_ctrl, base +
pad_ctrl_ofs);
return
0;
}
如果需要将某 IO 引脚 XXX_GPIO_XXX 设置为 GPIO 模式,将 XXX_GPIO_XXX 添加到
mx53_loco_pads 数组即可。
第2步,将 gpio 设置为输入
使用的函数为 int gpio_direction_input(unsigned
gpio)
定义在 drivers/gpio/gpiolib.c
int gpio_direction_input(unsigned gpio)
{
unsigned
longflags;
struct
gpio_chip*chip;
struct
gpio_desc*desc
= &gpio_desc[gpio];
int
status = -EINVAL;
spin_lock_irqsave(&gpio_lock,
flags);
if
(!gpio_is_valid(gpio))
goto
fail;
chip =
desc->chip;
if (!chip ||
!chip->get || !chip->direction_input)
goto fail;
gpio -=
chip->base;
if (gpio
>= chip->ngpio)
goto fail;
status =
gpio_ensure_requested(desc, gpio);
if (status
< 0)
goto fail;
spin_unlock_irqrestore(&gpio_lock,
flags);
might_sleep_if(extra_checks
&& chip->can_sleep);
if (status)
{
status = chip->request(chip,
gpio);
if (status < 0)
{
pr_debug("GPIO-%d: chip request
fail, %d\n",
chip->base + gpio,
status);
goto lose;
}
}
status =
chip->direction_input(chip, gpio);
if (status
== 0)
clear_bit(FLAG_IS_OUT,
&desc->flags);
lose:
return
status;
fail:
spin_unlock_irqrestore(&gpio_lock,
flags);
if
(status)
pr_debug("%s: gpio-%d status
%d\n",
__func__,
gpio, status);
return
status;
}
该函数调用了 chip->direction_input(chip, gpio);
最终调用到的是 arch/arm/plat-mxc/gpio.c 文件中的函数 mxc_gpio_direction_input
,原型如下:
static int mxc_gpio_direction_input(struct gpio_chip *chip,
unsigned offset)
{
_set_gpio_direction(chip,
offset, 0);
return
0;
}
_set_gpio_direction 函数原型:
static void _set_gpio_direction(struct gpio_chip *chip, unsigned
offset,int dir)
{
struct
mxc_gpio_port *port =
container_of(chip, struct
mxc_gpio_port, chip);
u32
l;
unsigned
long flags;
spin_lock_irqsave(&port->lock,
flags);
l =
__raw_readl(port->base + GPIO_GDIR);
if
(dir)
l |= 1 <<
offset;
else
l &= ~(1 <<
offset);
__raw_writel(l, port->base +
GPIO_GDIR);
spin_unlock_irqrestore(&port->lock,
flags);
}
第3步,获取 GPIO 引脚的值
使用的宏为 gpio_get_value
arch/arm/plat-mxc/include/mach/gpio.h
#define gpio_get_value __gpio_get_value
__gpio_get_value 函数原型定义在 drivers/gpio/gpiolib.c
int __gpio_get_value(unsigned gpio)
{
struct
gpio_chip*chip;
chip =
gpio_to_chip(gpio);
WARN_ON(extra_checks &&
chip->can_sleep);
return
chip->get ? chip->get(chip, gpio - chip->base) :
0;
}
__gpio_get_value 调用 chip->get ,实际调用到的是 arch/arm/plat-mxc/gpio.c
文件中 mxc_gpio_get 函数
static int mxc_gpio_get(struct gpio_chip *chip, unsigned
offset)
{
struct
mxc_gpio_port *port =
container_of(chip, struct mxc_gpio_port, chip);
return
(__raw_readl(port->base + GPIO_PSR) >> offset) &
1;
}
二、PLATFORM
DEVICE
GPIO按键作为系统的一种设备,挂载到platform总线上,相应的platform
device定义在arch/arm/mach-mx5/mx53_loco.c:
static struct platform_device loco_button_device =
{
.name =
"gpio-keys",
.id = -1,
.num_resources
= 0,
.dev =
{
.platform_data =
&loco_button_data,
}
};
loco_button_data 信息如下:
static struct gpio_keys_platform_data loco_button_data =
{
.buttons = loco_buttons,
.nbuttons = ARRAY_SIZE(loco_buttons),
};
loco_buttons
定义了5个按键:power、back、home、volumeup、volumedown和menu。
static struct gpio_keys_button loco_buttons[] = {
GPIO_BUTTON(MX53_nONKEY,
KEY_POWER, 1, "power", 0),
GPIO_BUTTON(USER_UI1,
KEY_BACK, 1, "back", 0),
GPIO_BUTTON(USER_UI2,
KEY_HOME, 1, "home", 0),
GPIO_BUTTON(KEY_VOLUP,
KEY_VOLUMEUP, 1, "volumeup",
0),
GPIO_BUTTON(KEY_VOLDOWN,
KEY_VOLUMEDOWN, 1, "volumedown", 0),
GPIO_BUTTON(KEY_SET,
KEY_MENU, 1, "menu", 0),
};
宏 GPIO_BUTTON 的内容为:
#define GPIO_BUTTON(gpio_num, ev_code, act_low, descr, wake)
\
{ \
.gpio = gpio_num,
\
.type = EV_KEY, \
.code = ev_code, \
.active_low = act_low,
\
.desc = "btn " descr,
\
.wakeup = wake, \
}
第一个参数为 gpio 引脚,第二个参数为按键的键值。
loco_buttons 使用的 gpio 定义如下:
#define MX53_nONKEY (0*32 + 8)
#define USER_UI1 (1*32 + 14)
#define USER_UI2 (1*32 + 15)
#define KEY_VOLUP (6*32 +
13)
#define KEY_VOLDOWN (0*32 + 4)
#define KEY_SET (0*32 + 2)
键值定义在 include/linux/input.h 文件中:
#define KEY_HOME 102
#define KEY_VOLUMEDOWN 114
#define KEY_VOLUMEUP 115
#define KEY_POWER 116
#define KEY_MENU 139
#define KEY_BACK 158
在板子初始化函数 static void __init mxc_board_init(void) 中
调用了 loco_add_device_buttons()
函数将loco_button_device加入到系统中,
这样驱动即可匹配 .name 找到我们的设备loco_button_device。
loco_add_device_buttons() 函数定义如下:
static void __init loco_add_device_buttons(void)
{
platform_device_register(&loco_button_device);
}
三、PLATFORM
DRIVER
GPIO按键的驱动文件为:drivers/input/keyboard/gpio_keys.c
通过 module_init ,在总线上注册 name 为 gpio-keys 的驱动,并通过 module_exit
相应的将其注销:
static int __init gpio_keys_init(void)
{
return
platform_driver_register(&gpio_keys_device_driver);
}
static void __exit gpio_keys_exit(void)
{
platform_driver_unregister(&gpio_keys_device_driver);
}
module_init(gpio_keys_init);
module_exit(gpio_keys_exit);
platform driver - gpio_keys_device_driver 如下:
static struct platform_driver gpio_keys_device_driver =
{
.probe =
gpio_keys_probe,
.remove =
__devexit_p(gpio_keys_remove),
.driver = {
.name =
"gpio-keys",
.owner =
THIS_MODULE,
#ifdef CONFIG_PM
.pm =
&gpio_keys_pm_ops,
#endif
}
};
在探测到 name 匹配的 device 之后,gpio_keys_probe 得以执行:
static int __devinit gpio_keys_probe(struct platform_device
*pdev)
{
struct
gpio_keys_platform_data *pdata =
pdev->dev.platform_data;
struct
gpio_keys_drvdata *ddata;
struct
device *dev = &pdev->dev;
struct
input_dev *input;
int i,
error;
int wakeup =
0;
ddata =
kzalloc(sizeof(struct gpio_keys_drvdata) +
pdata->nbuttons *
sizeof(struct gpio_button_data),
GFP_KERNEL);
input =
input_allocate_device();
if (!ddata
|| !input) {
dev_err(dev, "failed to
allocate state\n");
error =
-ENOMEM;
goto fail1;
}
ddata->input =
input;
ddata->n_buttons =
pdata->nbuttons;
mutex_init(&ddata->disable_lock);
platform_set_drvdata(pdev,
ddata);
input->name =
pdev->name;
input->phys =
"gpio-keys/input0";
input->dev.parent =
&pdev->dev;
input->id.bustype =
BUS_HOST;
input->id.vendor
= 0x0001;
input->id.product =
0x0001;
input->id.version =
0x0100;
if
(pdata->rep)
__set_bit(EV_REP,
input->evbit);
for (i = 0;
i < pdata->nbuttons; i++) {
struct gpio_keys_button *button
= &pdata->buttons[i];
struct gpio_button_data *bdata
= &ddata->data[i];
unsigned int type =
button->type ?: EV_KEY;
bdata->input =
input;
bdata->button =
button;
error =
gpio_keys_setup_key(pdev, bdata, button);
if (error)
goto fail2;
if
(button->wakeup)
wakeup = 1;
input_set_capability(input,
type, button->code);
}
error =
sysfs_create_group(&pdev->dev.kobj,
&gpio_keys_attr_group);
if (error)
{
dev_err(dev, "Unable to export
keys/switches, error: %d\n",
error);
goto
fail2;
}
error =
input_register_device(input);
if (error)
{
dev_err(dev, "Unable to
register input device, error: %d\n",
error);
goto fail3;
}
for (i = 0;
i < pdata->nbuttons; i++)
gpio_keys_report_event(&ddata->data[i]);
input_sync(input);
device_init_wakeup(&pdev->dev,
wakeup);
return
0;
fail3:
sysfs_remove_group(&pdev->dev.kobj,
&gpio_keys_attr_group);
fail2:
while (--i
>= 0) {
free_irq(gpio_to_irq(pdata->buttons[i].gpio),
&ddata->data[i]);
if
(pdata->buttons[i].debounce_interval)
del_timer_sync(&ddata->data[i].timer);
cancel_work_sync(&ddata->data[i].work);
gpio_free(pdata->buttons[i].gpio);
}
platform_set_drvdata(pdev,
NULL);
fail1:
input_free_device(input);
kfree(ddata);
return
error;
}
gpio_keys_setup_key 函数内容 :
static int __devinit gpio_keys_setup_key(struct platform_device
*pdev,
struct
gpio_button_data *bdata,
struct
gpio_keys_button *button)
{
char *desc =
button->desc ? button->desc : "gpio_keys";
struct
device *dev = &pdev->dev;
unsigned
long irqflags;
int irq,
error;
setup_timer(&bdata->timer,
gpio_keys_timer, (unsigned long)bdata);
INIT_WORK(&bdata->work,
gpio_keys_work_func);
error =
gpio_request(button->gpio, desc);
if (error
< 0) {
dev_err(dev, "failed to request
GPIO %d, error %d\n",
button->gpio,
error);
goto fail2;
}
error =
gpio_direction_input(button->gpio);
if (error
< 0) {
dev_err(dev, "failed to
configure"
" direction for GPIO %d, error
%d\n",
button->gpio,
error);
goto fail3;
}
irq =
gpio_to_irq(button->gpio);
if (irq <
0) {
error = irq;
dev_err(dev, "Unable to get irq
number for GPIO %d, error %d\n",
button->gpio,
error);
goto fail3;
}
irqflags =
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
if
(!button->can_disable)
irqflags |=
IRQF_SHARED;
error =
request_irq(irq, gpio_keys_isr, irqflags, desc,
bdata);
if (error)
{
dev_err(dev, "Unable to claim
irq %d; error %d\n",
irq, error);
goto
fail3;
}
return
0;
fail3:
gpio_free(button->gpio);
fail2:
return
error;
}
中断处理函数 gpio_keys_isr 定义如下:
static irqreturn_t gpio_keys_isr(int irq, void
*dev_id)
{
struct
gpio_button_data *bdata = dev_id;
struct
gpio_keys_button *button = bdata->button;
BUG_ON(irq
!= gpio_to_irq(button->gpio));
if
(button->debounce_interval)
mod_timer(&bdata->timer,
jiffies +
msecs_to_jiffies(button->debounce_interval));
else
schedule_work(&bdata->work);
return
IRQ_HANDLED;
}
gpio_keys_work_func 的定义:
static void gpio_keys_work_func(struct work_struct
*work)
{
struct
gpio_button_data *bdata =
container_of(work, struct
gpio_button_data, work);
gpio_keys_report_event(bdata);
}
函数 gpio_keys_report_event 内容:
static void gpio_keys_report_event(struct gpio_button_data
*bdata)
{
struct
gpio_keys_button *button = bdata->button;
struct
input_dev *input = bdata->input;
unsigned int
type = button->type ?: EV_KEY;
int state =
(gpio_get_value(button->gpio) ? 1 : 0) ^
button->active_low;
input_event(input, type,
button->code, !!state);
input_sync(input);
}
如果要添加或是修改 android
的按键功能,需保证 device/fsl/imx53_loco/gpio-keys.kl
文件中有相应的键值,可以参考 qwerty.kl 文件修改。
以上内容为个人对 IMX53 GPIO 按键驱动的认识,如有不妥之处,还望批评指正。