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 按键驱动的认识,如有不妥之处,还望批评指正。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐