imx6 项目的按键驱动程序

来源:互联网 发布:java post 提交文件 编辑:程序博客网 时间:2024/05/22 14:19
/**
 * filename:imx6_key_drv.c
 * description: matrix key driver for imx6.
 * author:shell.albert@gmail.com
 * date: September 1,2015.
 *
 * three gpio keys are connected to imx6 through different GPIO.
 * up/down key: GPIO04/SLEEP_WAKE
 * led key:EIM_BCLK/VOL+
 * left/right key:CSI0_DATA_EN/VOL-
 * ok key:NANDF_CS0
 * return key:NANDF_CS1
 *
 * attention here.
 * the mdev rules must be enabled before use this driver.
 * do the following work to make it works.
 *     /etc/init.d/rcS
 *     #mount partitions
 *     mkdir /dev/pts -p
 *     mount -t devpts devpts /dev/pts
 *     mount -a
 *     echo /sbin/mdev > /proc/sys/kernel/hotplug
 *     mdev -s
 *
 *     /etc/mdev.conf
 *     # system all-writable devices
 *      full            0:0     0666
 *      null            0:0     0666
 *          ptmx            0:0     0666
 *      random          0:0     0666
 *        tty             0:0     0666
 *        zero            0:0     0666


 *        # i2c devices
 *        i2c-0           0:0     0666    =i2c/0
 *        i2c-1           0:0     0666    =i2c/1

 *        # frame buffer devices
 *        fb[0-9]         0:0     0666

 *        # input devices
 *        mice            0:0     0660    =input/
 *        mouse.*         0:0     0660    =input/
 *        event.*         0:0     0660    =input/
 *        ts.*            0:0     0660    =input/
 */

/**
Event: time 1441159728.919925, -------------- Report Sync ------------
Event: time 1441159733.423896, type 4 (Misc), code 4 (ScanCode), value 70052
Event: time 1441159733.423896, type 1 (Key), code 103 (Up), value 1
Event: time 1441159733.423896, -------------- Report Sync ------------
Event: time 1441159733.511935, type 4 (Misc), code 4 (ScanCode), value 70052
Event: time 1441159733.511935, type 1 (Key), code 103 (Up), value 0
Event: time 1441159733.511935, -------------- Report Sync ------------
Event: time 1441159735.431922, type 4 (Misc), code 4 (ScanCode), value 70050
Event: time 1441159735.431922, type 1 (Key), code 105 (Left), value 1
Event: time 1441159735.431922, -------------- Report Sync ------------
Event: time 1441159735.503919, type 4 (Misc), code 4 (ScanCode), value 70050
Event: time 1441159735.503919, type 1 (Key), code 105 (Left), value 0
Event: time 1441159735.503919, -------------- Report Sync ------------
Event: time 1441159739.127894, type 4 (Misc), code 4 (ScanCode), value 700e4
Event: time 1441159739.127894, type 1 (Key), code 97 (RightCtrl), value 1
Event: time 1441159739.127894, -------------- Report Sync ------------
Event: time 1441159739.263894, type 4 (Misc), code 4 (ScanCode), value 700e4
Event: time 1441159739.263894, type 1 (Key), code 97 (RightCtrl), value 0
Event: time 1441159739.263894, -------------- Report Sync ------------
Event: time 1441159742.103834, type 4 (Misc), code 4 (ScanCode), value 700e0
Event: time 1441159742.103834, type 1 (Key), code 29 (LeftControl), value 1
Event: time 1441159742.103834, -------------- Report Sync ------------
Event: time 1441159742.207873, type 4 (Misc), code 4 (ScanCode), value 700e0
Event: time 1441159742.207873, type 1 (Key), code 29 (LeftControl), value 0
Event: time 1441159742.207873, -------------- Report Sync ------------
 */

/**
 * PAD Mux registers.
 * related register define here.
 * I/O are multiplex,so we choose the right mode.
 */
#define IOMUXC_SW_MUX_CTL_PAD_GPIO04                0x20E0238 //GPIO1_IO04
#define IOMUXC_SW_MUX_CTL_PAD_EIM_BCLK                0x20E0158 //GPIO6_IO31
#define IOMUXC_SW_MUX_CTL_PAD_CSI0_DATA_EN            0x20E0260 //GPIO5_IO20
#define IOMUXC_SW_MUX_CTL_PAD_NAND_CS0_B            0x20E02E4 //GPIO6_IO11
#define IOMUXC_SW_MUX_CTL_PAD_NAND_CS1_B            0x20E02E8 //GPIO6_IO14
/**
 * PAD Control registers.
 * used to control the pad physical features.
 */
#define IOMUXC_SW_PAD_CTL_PAD_GPIO04                    0x20E0608
#define IOMUXC_SW_PAD_CTL_PAD_EIM_BCLK                    0x20E046C
#define IOMUXC_SW_PAD_CTL_PAD_CSI0_DATA_EN                0x20E0630
#define IOMUXC_SW_PAD_CTL_PAD_NAND_CS0_B                0x20E06CC
#define IOMUXC_SW_PAD_CTL_PAD_NAND_CS1_B                0x20E06D0

//register size,it's always 32-bit,4 bytes in 32-bit CPU.
#define REG_SIZE        0x4

#define MODULE_NAME        "imx6_key_drv"

#define print_debug(fmt,arg...)     printk(KERN_DEBUG fmt,##arg)
#define print_error(fmt,arg...)        printk(KERN_ERR fmt,##arg)

//GPIO related register define here.
//up/down key,GPIO1[4].
#define GPIO1_GDIR        0x209C004
#define GPIO1_PSR        0x209C008
#define GPIO1_IMR        0x209C014
//left/right key,GPIO5[20].
#define GPIO5_GDIR        0x20AC004
#define GPIO5_PSR        0x20AC008
#define GPIO5_IMR        0x20AC014
//led key:GPIO6[31].
//ok key: GPIO6[11].
//return key: GPIO6[14].
#define GPIO6_GDIR        0x20B0004
#define GPIO6_PSR        0x20B0008
#define GPIO6_IMR        0x20B0014

//for module.
#include <linux/module.h>
#include <linux/init.h>
//for IRQ.
#include <linux/irq.h>
#include <linux/interrupt.h>
//for timer.
#include <linux/timer.h>
#include <linux/jiffies.h>

//for input framework.
#include <linux/input.h>

//for ioremap.
#include <linux/io.h>
//for kzalloc.
#include <linux/slab.h>
//for ulong.
#include <linux/types.h>

struct imx6_keys_dev {
    //virtual address after mapping.
    volatile ulong *up_down_vir_addr; //GPIO1[4].
    volatile ulong up_down_status;

    volatile ulong *left_right_vir_addr; //GPIO5[20].
    volatile ulong left_right_status;

    volatile ulong *led_ok_return_vir_addr;//GPIO6[11/14/31].
    volatile ulong led_ok_return_status;
    //scan timer.
    struct timer_list scan_timer;

    //input core.
    struct input_dev *input_device;
};
struct imx6_keys_dev *key_dev;
/**
 * configure the register at initial stage.
 * these registers should only be configured once.
 */
int imx6_config_reg(ulong phy_addr, int bits_mask, int bits_value) {
    volatile ulong *pbase = NULL;
    volatile int ret;
    int i;
    int reg_bits_num = sizeof(ret);
    pbase = (ulong*) ioremap(phy_addr, REG_SIZE);
    if (!pbase) {
        print_error("error accord when ioremap()!\n");
        return -1;
    }
    ret = ioread32(pbase);
    for (i = 0; i < reg_bits_num; i++) {
        if (bits_mask & (0x1 << i)) {
            ret &= (~(0x1 << i));
        }
    }
    ret |= bits_value;
    iowrite32(ret, pbase);
    iounmap(pbase);
    pbase = NULL;
    return 0;
}
/**
 * configure the IOMUX registers to choose the right pin mode.
 */
int imx6_config_all_iomuxcs(void) {
    //IOMUXC_SW_MUX_CTL_PAD_GPIO04.[2:0]=101=0x5,GPIO1[4].
    //bits_mask=111=0x7,
    if (imx6_config_reg(IOMUXC_SW_MUX_CTL_PAD_GPIO04, 0x7, 0x5) < 0) {
        return -1;
    }
    
    //IOMUXC_SW_MUX_CTL_PAD_EIM_BCLK,[2:0]=101=0x5,GPIO6[31].
    //bits_mask=111=0x7.
    if (imx6_config_reg(IOMUXC_SW_MUX_CTL_PAD_EIM_BCLK, 0x7, 0x5) < 0) {
        return -1;
    }
    
    //IOMUXC_SW_MUX_CTL_PAD_CSI0_DATA_EN,[2:0]=101=0x5,ALT5,GPIO5[20].
    //bits_mask=111=0x7.
    if (imx6_config_reg(IOMUXC_SW_MUX_CTL_PAD_CSI0_DATA_EN, 0x7, 0x5) < 0) {
        return -1;
    }

    //IOMUXC_SW_MUX_CTL_PAD_NAND_CS0_B,[2:0]=101=0x5,ALT5,GPIO6[11].
    //bits_mask=111=0x7.
    if (imx6_config_reg(IOMUXC_SW_MUX_CTL_PAD_NAND_CS0_B, 0x7, 0x5) < 0) {
        return -1;
    }
    
    //IOMUXC_SW_MUX_CTL_PAD_NAND_CS1_B,[2:0]=101=0x5,ALT5,GPIO6[14].
    //bits_mask=111=0x7.
    if (imx6_config_reg(IOMUXC_SW_MUX_CTL_PAD_NAND_CS1_B, 0x7, 0x5) < 0) {
        return -1;
    }
    return 0;
}

//configure pad's physical feature.
int imx6_config_all_pads(void) {
    //[16]=1,Schmitt trigger input.
    //[15:14]=10,100Kohm pull up.
    //[13]=1,pull enabled.
    //[12]=1,pull/keeper enabled.
    //bits_mask= 1,1111,0000,0000,0000=0x1F000.
    //bits_value=1,1011,0000,0000,0000=0x1B000.

    //IOMUXC_SW_PAD_CTL_PAD_GPIO04.
    if (imx6_config_reg(IOMUXC_SW_PAD_CTL_PAD_GPIO04, 0x1F000, 0x1B000) < 0) {
        return -1;
    }
    //IOMUXC_SW_PAD_CTL_PAD_EIM_BCLK.
    if (imx6_config_reg(IOMUXC_SW_PAD_CTL_PAD_EIM_BCLK, 0x1F000, 0x1B000) < 0) {
        return -1;
    }
    //IOMUXC_SW_PAD_CTL_PAD_CSI0_DATA_EN.
    if (imx6_config_reg(IOMUXC_SW_PAD_CTL_PAD_CSI0_DATA_EN, 0x1F000, 0x1B000) < 0) {
        return -1;
    }
    
    //IOMUXC_SW_PAD_CTL_PAD_NAND_CS0_B.
    if (imx6_config_reg(IOMUXC_SW_PAD_CTL_PAD_NAND_CS0_B, 0x1F000, 0x1B000) < 0) {
        return -1;
    }
    
    //IOMUXC_SW_PAD_CTL_PAD_NAND_CS1_B.
    if (imx6_config_reg(IOMUXC_SW_PAD_CTL_PAD_NAND_CS1_B, 0x1F000, 0x1B000) < 0) {
        return -1;
    }
    return 0;
}

//configure GPIO registers.
int imx6_config_all_gpios(void) {
    //up/down key,GPIO1[4].
    //[4]=0,direction:input.
    //bits_mask=0001,0000=0x10.
    //bits_value=0000,0000=0x0.
    if (imx6_config_reg(GPIO1_GDIR, 0x10, 0x0) < 0) {
        return -1;
    }
    //[4]=0,disable interrupt.
    if (imx6_config_reg(GPIO1_IMR, 0x10, 0x0) < 0) {
        return -1;
    }

    //left/right key,GPIO5[20].
    //[20]=0,direction,input.
    //bits_mask=1,0000,0000,0000,0000,0000=0x100000.
    //bits_value=0,0000,0000,0000,0000,0000=0x0.
    if (imx6_config_reg(GPIO5_GDIR, 0x100000, 0x0) < 0) {
        return -1;
    }
    //[4]=0,disable interrupt.
    if (imx6_config_reg(GPIO5_IMR, 0x100000, 0x0) < 0) {
        return -1;
    }

    //led key:GPIO6[31].
    //ok key: GPIO6[11].
    //return key: GPIO6[14].
    //[31]=0,direction input.
    //[11]=0,direction input.
    //[14]=0,direction input.
    //bits_mask=1000,0000,0000,0000,0100,1000,0000,0000=0x80004800.
    //bits_value=0000,0000,0000,0000,0000,0000,0000,0000=0x0.
    if (imx6_config_reg(GPIO6_GDIR, 0x80004800, 0x0) < 0) {
        return -1;
    }
    //[31]=0,disable interrupt.
    //[14]=0,disable interrupt.
    //[11]=0,disable interrupt.
    if (imx6_config_reg(GPIO6_IMR, 0x80004800, 0x0) < 0) {
        return -1;
    }

    //do ioremap for virtual address.
    if (!(key_dev->up_down_vir_addr = (ulong*) ioremap(GPIO1_PSR, REG_SIZE))) {
        return -1;
    }
    if (!(key_dev->left_right_vir_addr = (ulong*) ioremap(GPIO5_PSR, REG_SIZE))) {
        return -1;
    }
    if (!(key_dev->led_ok_return_vir_addr = (ulong*) ioremap(GPIO6_PSR, REG_SIZE))) {
        return -1;
    }
    key_dev->up_down_status=1;
    key_dev->left_right_status=1;
    key_dev->led_ok_return_status=1;
    return 0;
}

//timer function.
//used to poll key status.
void key_status_scan_function(unsigned long arg) {

    struct imx6_keys_dev *pkey_dev = (struct imx6_keys_dev*) (arg);
    volatile int ret;

    //the up/down key,GPIO1[4].
    ret = ioread32(pkey_dev->up_down_vir_addr);
    if(!(ret & (0x1 << 4)) && pkey_dev->up_down_status)
    {
        //key was pressed.
        pkey_dev->up_down_status=0;
        input_report_key(pkey_dev->input_device, KEY_UP, 1);
        input_sync(pkey_dev->input_device);
    }else if((ret&(0x1 << 4)) && !pkey_dev->up_down_status)
    {
        //key was released.
        pkey_dev->up_down_status=1;
        input_report_key(pkey_dev->input_device, KEY_UP, 0);
        input_sync(pkey_dev->input_device);
    }

    //the left/right key,GPIO5[20].
    ret = ioread32(pkey_dev->left_right_vir_addr);
    if(!(ret & (0x1 << 20)) && pkey_dev->left_right_status)
    {
        //key was pressed.
        pkey_dev->left_right_status=0;
        input_report_key(pkey_dev->input_device, KEY_LEFT, 1);
        input_sync(pkey_dev->input_device);
    }else if((ret&(0x1<<20)) && !pkey_dev->left_right_status)
    {
        //key was released.
        pkey_dev->left_right_status=1;
        input_report_key(pkey_dev->input_device, KEY_LEFT, 0);
        input_sync(pkey_dev->input_device);
    }

    //led key:GPIO6[31],open or close led.
    //ok key: GPIO6[11],left ctrl.
    //return key: GPIO6[14],left shift.
    ret = ioread32(pkey_dev->led_ok_return_vir_addr);
    if(!(ret & (0x1 << 11)) && (pkey_dev->led_ok_return_status&(0x1<<11)))
    {
        //key was pressed.
        pkey_dev->led_ok_return_status&=~(0x1<<11);
        input_report_key(pkey_dev->input_device, KEY_LEFTCTRL, 1);
        input_sync(pkey_dev->input_device);
    }else if( (ret&(0x1<<11)) && !(pkey_dev->led_ok_return_status&(0x1<<11)))
    {
        //key was released.
        pkey_dev->led_ok_return_status|=(0x1<<11);
        input_report_key(pkey_dev->input_device, KEY_LEFTCTRL, 0);
        input_sync(pkey_dev->input_device);
    }
    /////////////////////////////////////////////////////////////
    ret = ioread32(pkey_dev->led_ok_return_vir_addr);
    if(!(ret & (0x1 << 14)) && (pkey_dev->led_ok_return_status&(0x1<<14)))
    {
        //key was pressed.
        pkey_dev->led_ok_return_status&=~(0x1<<14);
        input_report_key(pkey_dev->input_device, KEY_LEFTSHIFT, 1);
        input_sync(pkey_dev->input_device);
    }else if( (ret&(0x1<<14)) && !(pkey_dev->led_ok_return_status&(0x1<<14)))
    {
        //key was released.
        pkey_dev->led_ok_return_status|=(0x1<<14);
        input_report_key(pkey_dev->input_device, KEY_LEFTSHIFT, 0);
        input_sync(pkey_dev->input_device);
    }
    //////////////////////////////////////////////////////////////
    ret = ioread32(pkey_dev->led_ok_return_vir_addr);
    if(!(ret & (0x1 << 31)) && (pkey_dev->led_ok_return_status&(0x1<<31)))
    {
        //key was pressed to open led.
        pkey_dev->led_ok_return_status&=~(0x1<<31);
    }else if( (ret&(0x1<<31)) && !(pkey_dev->led_ok_return_status&(0x1<<31)))
    {
        //key was released to close led.
        pkey_dev->led_ok_return_status|=(0x1<<31);
    }

#if 1
    //restart the scan routine.
    pkey_dev->scan_timer.expires = jiffies + HZ / 4;    //500ms.
    add_timer(&pkey_dev->scan_timer);
#endif

}

static int __init imx6_key_drv_init(void)
{
    key_dev=(struct imx6_keys_dev *)kzalloc(sizeof(struct imx6_keys_dev),GFP_KERNEL);
    if(!key_dev)
    {
        print_error("kzalloc() failed!\n");goto error_exit_1;
    }
    key_dev->input_device=input_allocate_device();
    if(!key_dev->input_device)
    {
        print_error(
        "allocate input device failed!\n");goto error_exit_2;
    }

#if 1
    if(imx6_config_all_iomuxcs()<0)
    {
        goto  error_exit_3;
        return -1;
    }
    if(imx6_config_all_pads()<0)
    {
        goto  error_exit_3;
        return -1;
    }
    if(imx6_config_all_gpios()<0)
    {
        goto  error_exit_3;
        return -1;
    }
#endif

    //input device name.
    key_dev->input_device->name=MODULE_NAME;

    //key event.
    set_bit(EV_KEY,key_dev->input_device->evbit);
    //key value.
    set_bit(KEY_UP,key_dev->input_device->keybit);
    set_bit(KEY_LEFT,key_dev->input_device->keybit);
    set_bit(KEY_LEFTCTRL,key_dev->input_device->keybit);
    set_bit(KEY_LEFTSHIFT,key_dev->input_device->keybit);
    if(input_register_device(key_dev->input_device))
    {
        print_error("input register device failed!\n");goto error_exit_3;
    }

    //start timer.
    init_timer(&key_dev->scan_timer);key_dev->scan_timer.data=(ulong)key_dev;
    key_dev->scan_timer.function=key_status_scan_function;
    key_dev->scan_timer.expires=jiffies+HZ/2;//500ms.
    add_timer(&key_dev->scan_timer);

    print_debug("imx6_key_drv load successfully!\n");
return 0;
/**
 * exit flow when errors occured.
 */
error_exit_3:
input_free_device(key_dev->input_device);
error_exit_2:
kfree(key_dev);
error_exit_1:
return -1;
}

static void __exit imx6_key_drv_exit(void)
{
    del_timer_sync(&key_dev->scan_timer);input_unregister_device(key_dev->input_device);
    input_free_device(key_dev->input_device);
    kfree(key_dev);
}

module_init(imx6_key_drv_init);
module_exit(imx6_key_drv_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhangshaoyan <shell.albert@gmail.com>");
MODULE_DESCRIPTION("Keyboard driver for Freescale iMX6 GPIOs");
MODULE_ALIAS("platform:imx6 key driver");
0 0
原创粉丝点击