am335x i2c_max7359-IC按键与旋钮驱动

20160719

I2C驱动开发总结

. I2C设备驱动代码示例

1.1 源代码

1.1.1 max7359_keypad.c

/* max7359_keypad.c - MAX7359 Key Switch Controller Driver */

#include <linux/module.h>

#include <linux/gpio.h>

#include <linux/i2c.h>

#include <linux/slab.h>

#include <linux/interrupt.h>

#include <linux/pm.h>

#include <linux/input.h>

#include <linux/input/matrix_keypad.h>

#include <linux/delay.h>

#define MAX7359_MAX_KEY_ROWS 8

#define MAX7359_MAX_KEY_COLS 8

#define MAX7359_MAX_KEY_NUM (MAX7359_MAX_KEY_ROWS * MAX7359_MAX_KEY_COLS)

#define MAX7359_ROW_SHIFT 3

/*

 * MAX7359 registers

 */

#define MAX7359_REG_KEYFIFO 0x00

#define MAX7359_REG_CONFIG 0x01

#define MAX7359_REG_DEBOUNCE 0x02

#define MAX7359_REG_INTERRUPT 0x03

#define MAX7359_REG_PORTS 0x04

#define MAX7359_REG_KEYREP 0x05

#define MAX7359_REG_SLEEP 0x06

/*

 * Configuration register bits

 */

#define MAX7359_CFG_SLEEP (1 << 7)

#define MAX7359_CFG_INTERRUPT (1 << 5)

#define MAX7359_CFG_KEY_RELEASE (1 << 3)

#define MAX7359_CFG_WAKEUP (1 << 1)

#define MAX7359_CFG_TIMEOUT (1 << 0)

/*

 * Autosleep register values (ms)

 */

#define MAX7359_AUTOSLEEP_8192 0x01

#define MAX7359_AUTOSLEEP_4096 0x02

#define MAX7359_AUTOSLEEP_2048 0x03

#define MAX7359_AUTOSLEEP_1024 0x04

#define MAX7359_AUTOSLEEP_512 0x05

#define MAX7359_AUTOSLEEP_256 0x06

#if 1

#define PRINTK_C(fmt, args...) printk(KERN_INFO#fmt, ##args)

#else

#define PRINTK_C(fmt, args...)

#endif

struct max7359_keypad {

/* matrix key code map */

unsigned short keycodes[MAX7359_MAX_KEY_NUM];

struct input_dev *input_dev;

struct i2c_client *client;

    /* +++ Added by XXXX2013-09-04 +++ */

    unsigned long last_jiffies;

    int bCrA;

    int bCrB;

    int bCwA;

    int bCwB;

    /* --- Added by XXXX2013-09-04 --- */

};

static int max7359_write_reg(struct i2c_client *client, u8 reg, u8 val)

{

int ret = i2c_smbus_write_byte_data(client, reg, val);

if (ret < 0)

dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",

__func__, reg, val, ret);

return ret;

}

static int max7359_read_reg(struct i2c_client *client, int reg)

{

int ret = i2c_smbus_read_byte_data(client, reg);

if (ret < 0)

dev_err(&client->dev, "%s: reg 0x%x, err %d\n",

__func__, reg, ret);

return ret;

}

static void max7359_build_keycode(struct max7359_keypad *keypad,

const struct matrix_keymap_data *keymap_data)

{

struct input_dev *input_dev = keypad->input_dev;

int i;

for (i = 0; i < keymap_data->keymap_size; i++) {

unsigned int key = keymap_data->keymap[i];

unsigned int row = KEY_ROW(key);

unsigned int col = KEY_COL(key);

unsigned int scancode = MATRIX_SCAN_CODE(row, col,

MAX7359_ROW_SHIFT);

unsigned short keycode = KEY_VAL(key);

/*设置扫描码与按键码对应,并标记按键码为下标的数组位为1*/

keypad->keycodes[scancode] = keycode;

__set_bit(keycode, input_dev->keybit);

}

__clear_bit(KEY_RESERVED, input_dev->keybit);

}

/* runs in an IRQ thread -- can (and will!) sleep */

static irqreturn_t max7359_interrupt(int irq, void *dev_id)

{

struct max7359_keypad *keypad = dev_id;

struct input_dev *input_dev = keypad->input_dev;

    int val, row, col, release, code, i;

    /* Modified by XXXX2013-08-30 Clear FIFO of MAX7359. */

    i = 16;

    do {

        val = max7359_read_reg(keypad->client, MAX7359_REG_KEYFIFO);

        row = val & 0x7;

        col = (val >> 3) & 0x7;

        release = val & 0x40;

        code = MATRIX_SCAN_CODE(row, col, MAX7359_ROW_SHIFT);

        PRINTK_C("[MAX7359]val:0x%02X row:%d col:%d, release:%d\n", val, row, col, release);

        if ((val < 0) || (0 == (val & 0xC0))) {

            return IRQ_HANDLED;

        }

        input_event(input_dev, EV_MSC, MSC_SCAN, code);

        input_report_key(input_dev, keypad->keycodes[code], !release);

        input_sync(input_dev);

    } while (i--);

return IRQ_HANDLED;

}

#define MAX7359_DEBOUNCE_JIFFIES    msecs_to_jiffies(10)

/* +++ Added by XXXX2013-08-26 +++ */

static irqreturn_t max7359_knob_a_interrupt(int irq, void *dev_id)

{

struct max7359_keypad *keypad = dev_id;

struct matrix_keymap_data *keymap_data = keypad->client->dev.platform_data;

    unsigned long diff;

    int knob_a, knob_b;

    int code = 0;

    /* Use jiffies to debounce. */

    diff = jiffies - keypad->last_jiffies;

    keypad->last_jiffies = jiffies;

    knob_a = gpio_get_value(keymap_data->gpio_a);

    knob_b = gpio_get_value(keymap_data->gpio_b);

    PRINTK_C("[MAX7359]knob_a down, diff:%lu", diff);

    if (diff < MAX7359_DEBOUNCE_JIFFIES) {

        return IRQ_HANDLED;

    }

    if (knob_a)

    {

        /* knob_a is not low, considered as bounce. */

        PRINTK_C("[MAX7359]knob_a ignored:%d", knob_a);

        return IRQ_HANDLED;

    }

    if (knob_b) {

        /* knob_a be low first, contrarotate. */

        keypad->bCrA = 1;

        keypad->bCwA = 0;

    }

    else {

        /* knob_b be low first, clockwise. */

        keypad->bCwA = 1;

        keypad->bCrA = 0;

    }

    if (1 == keypad->bCrA && 1 == keypad->bCrB)

    {

        code = MATRIX_SCAN_CODE(1, 6, MAX7359_ROW_SHIFT);

    }

    if (1 == keypad->bCwA && 1 == keypad->bCwB)

    {

        code = MATRIX_SCAN_CODE(1, 7, MAX7359_ROW_SHIFT);

    }

    if (code) {

        PRINTK_C("[MAX7359]knob_a down, knob_b:%d code:%d", knob_b, code);

        /* Report the event to input sub-system. */

        input_event(keypad->input_dev, EV_MSC, MSC_SCAN, code);

        input_report_key(keypad->input_dev, keypad->keycodes[code], 0);

        input_sync(keypad->input_dev);

        keypad->bCrA = 0;

        keypad->bCwA = 0;

        keypad->bCrB = 0;

        keypad->bCwB = 0;

    }

return IRQ_HANDLED;

}

static irqreturn_t max7359_knob_b_interrupt(int irq, void *dev_id)

{

    struct max7359_keypad *keypad = dev_id;

    struct matrix_keymap_data *keymap_data = keypad->client->dev.platform_data;

    unsigned long diff;

    int knob_a, knob_b;

    int code = 0;

    /* Use jiffies to debounce. */

    diff = jiffies - keypad->last_jiffies;

    keypad->last_jiffies = jiffies;

    knob_a = gpio_get_value(keymap_data->gpio_a);

    knob_b = gpio_get_value(keymap_data->gpio_b);

    PRINTK_C("[MAX7359]knob_b rise, diff:%lu", diff);

    if (diff < MAX7359_DEBOUNCE_JIFFIES) {

        return IRQ_HANDLED;

    }

    if (!knob_b)

    {

        /* knob_a is not high, considered as bounce. */

        PRINTK_C("[MAX7359]knob_b ignored:%d", knob_b);

        return IRQ_HANDLED;

    }

    if (knob_a) {

        /* knob_a be low first, contrarotate. */

        keypad->bCrB = 1;

        keypad->bCwB = 0;

    }

    else {

        /* knob_b be low first, clockwise. */

        keypad->bCwB = 1;

        keypad->bCrB = 0;

    }

    if (1 == keypad->bCrA && 1 == keypad->bCrB)

    {

        code = MATRIX_SCAN_CODE(1, 6, MAX7359_ROW_SHIFT);

    }

    if (1 == keypad->bCwA && 1 == keypad->bCwB)

    {

        code = MATRIX_SCAN_CODE(1, 7, MAX7359_ROW_SHIFT);

    }

    if (code) {

        PRINTK_C("[MAX7359]knob_b rise, knob_a:%d code:%d", knob_a, code);

        /* Report the event to input sub-system. */

        input_event(keypad->input_dev, EV_MSC, MSC_SCAN, code);

        input_report_key(keypad->input_dev, keypad->keycodes[code], 0);

        input_sync(keypad->input_dev);

        keypad->bCrA = 0;

        keypad->bCwA = 0;

        keypad->bCrB = 0;

        keypad->bCwB = 0;

    }

    return IRQ_HANDLED;

}

/* --- Added by XXXX2013-08-26 --- */

/* MAX7359休眠设置:如果没有按键按下,在8192ms之后进入休眠模式。在有任何按键按下,max7359进入正常运行模式。*/

static inline void max7359_fall_deepsleep(struct i2c_client *client)

{

max7359_write_reg(client, MAX7359_REG_SLEEP, MAX7359_AUTOSLEEP_8192); 

}

/*MAX7359休眠设置:设置max7359小睡,在256ms之后进入自动休眠。*/

static inline void max7359_take_catnap(struct i2c_client *client)

{

max7359_write_reg(client, MAX7359_REG_SLEEP, MAX7359_AUTOSLEEP_256);

}

static int max7359_open(struct input_dev *dev)

{

struct max7359_keypad *keypad = input_get_drvdata(dev);

max7359_take_catnap(keypad->client);

return 0;

}

static void max7359_close(struct input_dev *dev)

{

struct max7359_keypad *keypad = input_get_drvdata(dev);

max7359_fall_deepsleep(keypad->client);

}

static void max7359_initialize(struct i2c_client *client)

{

    int val, i;

max7359_write_reg(client, MAX7359_REG_CONFIG,

MAX7359_CFG_INTERRUPT |    /*在主机读之中断清除 */

MAX7359_CFG_KEY_RELEASE |  /*按键释放使能 */

MAX7359_CFG_WAKEUP);      /*按键按下唤醒使能 */

/* 按键反转时间设置与GPO使能 */

max7359_write_reg(client, MAX7359_REG_DEBOUNCE, 0x1F);

/*中断周期设置 */

max7359_write_reg(client, MAX7359_REG_INTERRUPT, 0x01);

max7359_fall_deepsleep(client); /*设定自动休眠时间*/

    /* Added by XXXX2013-08-30 Clear FIFO of MAX7359. */

    i = 24;

    do {

        val = max7359_read_reg(client, MAX7359_REG_KEYFIFO);

        if ((val < 0) || (0 == (val & 0xC0))) {

            break;

        }

        dev_info(&client->dev, "Clear FIFO of MAX7359: val:0x%02X", val);

    } while (i--);

}

static int __devinit max7359_probe(struct i2c_client *client,

const struct i2c_device_id *id)

{

struct matrix_keymap_data *keymap_data = client->dev.platform_data;

struct max7359_keypad *keypad;

struct input_dev *input_dev;

int ret;

int error;

if (!client->irq) {

dev_err(&client->dev, "The irq number should not be zero\n");

return -EINVAL;

}

keypad = kzalloc(sizeof(struct max7359_keypad), GFP_KERNEL);

input_dev = input_allocate_device();

if (!keypad || !input_dev) {

dev_err(&client->dev, "failed to allocate memory\n");

error = -ENOMEM;

goto failed_free_mem;

}

keypad->client = client;

keypad->input_dev = input_dev;

    keypad->last_jiffies = jiffies;

input_dev->name = client->name;

input_dev->id.bustype = BUS_I2C;

input_dev->open = max7359_open;

input_dev->close = max7359_close;

input_dev->dev.parent = &client->dev;

input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);

input_dev->keycodesize = sizeof(keypad->keycodes[0]);

input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes);

input_dev->keycode = keypad->keycodes;

input_set_capability(input_dev, EV_MSC, MSC_SCAN);

input_set_drvdata(input_dev, keypad);

max7359_build_keycode(keypad, keymap_data);

error = request_threaded_irq(client->irq, NULL, max7359_interrupt,

     IRQF_TRIGGER_FALLING | IRQF_ONESHOT,

     client->name, keypad);

    if (error)

    {

dev_err(&client->dev, "failed to register interrupt\n");

goto failed_free_mem;

    }

    /* +++ Added by XXXX2013-09-06 +++ */

    keypad->bCrA = 0;

    keypad->bCwA = 0;

    keypad->bCrB = 0;

    keypad->bCwB = 0;

    error = gpio_request_one(keymap_data->gpio_a, GPIOF_IN, "gpio_knob_a");

if (error) {

dev_err(&client->dev, "failed to request gpio knob a\n");

goto failed_free_mem;

}

    error = gpio_request_one(keymap_data->gpio_b, GPIOF_IN, "gpio_knob_ab");

if (error) {

dev_err(&client->dev, "failed to request gpio knob b\n");

goto failed_free_mem;

}

    gpio_set_debounce(keymap_data->gpio_a, 160);

    gpio_set_debounce(keymap_data->gpio_b, 160);

error = request_irq(keymap_data->irq_a, max7359_knob_a_interrupt,

     IRQF_TRIGGER_FALLING | IRQF_ONESHOT,

     client->name, keypad);

if (error) {

dev_err(&client->dev, "failed to register knob a interrupt\n");

goto failed_free_mem;

}

error = request_irq(keymap_data->irq_b, max7359_knob_b_interrupt,

     IRQF_TRIGGER_RISING | IRQF_ONESHOT,

     client->name, keypad);

if (error) {

dev_err(&client->dev, "failed to register knob b interrupt\n");

goto failed_free_mem;

}

    /* --- Added by XXXX2013-09-06 --- */

/* Register the input device */

error = input_register_device(input_dev);

if (error) {

dev_err(&client->dev, "failed to register input device\n");

goto failed_free_irq;

}

    /* Detect MAX7359: The initial Keys FIFO value is '0x3F' */

    ret = max7359_read_reg(client, MAX7359_REG_KEYFIFO);

    if (ret < 0) {

        dev_err(&client->dev, "failed to detect device\n");

        return -ENODEV;

    }

    else

    {

        dev_dbg(&client->dev, "keys FIFO is 0x%02x\n", ret);

        /* Initialize MAX7359 */

        max7359_initialize(client);

    }

i2c_set_clientdata(client, keypad);

device_init_wakeup(&client->dev, 1);

return 0;

failed_free_irq:

free_irq(client->irq, keypad);

    free_irq(keymap_data->irq_a, keypad);

    free_irq(keymap_data->irq_b, keypad);

failed_free_mem:

input_free_device(input_dev);

kfree(keypad);

return error;

}

static int __devexit max7359_remove(struct i2c_client *client)

{

struct max7359_keypad *keypad = i2c_get_clientdata(client);

const struct matrix_keymap_data *keymap_data = client->dev.platform_data;

free_irq(client->irq, keypad);

    free_irq(keymap_data->irq_a, keypad);

    free_irq(keymap_data->irq_b, keypad);

input_unregister_device(keypad->input_dev);

kfree(keypad);

return 0;

}

#ifdef CONFIG_PM

static int max7359_suspend(struct device *dev)

{

struct i2c_client *client = to_i2c_client(dev);

max7359_fall_deepsleep(client);

if (device_may_wakeup(&client->dev))

enable_irq_wake(client->irq);

return 0;

}

static int max7359_resume(struct device *dev)

{

struct i2c_client *client = to_i2c_client(dev);

if (device_may_wakeup(&client->dev))

disable_irq_wake(client->irq);

/* Restore the default setting */

max7359_take_catnap(client);

return 0;

}

#endif

static SIMPLE_DEV_PM_OPS(max7359_pm, max7359_suspend, max7359_resume);

static const struct i2c_device_id max7359_ids[] = {

{ "max7359", 0 },

{ }

};

MODULE_DEVICE_TABLE(i2c, max7359_ids);

static struct i2c_driver max7359_i2c_driver = {

.driver = {

.name = "max7359",

.pm   = &max7359_pm,

},

.probe = max7359_probe,

.remove = __devexit_p(max7359_remove),

.id_table = max7359_ids,

};

static int __init max7359_init(void){

return i2c_add_driver(&max7359_i2c_driver);

}

module_init(max7359_init);

static void __exit max7359_exit(void){

i2c_del_driver(&max7359_i2c_driver);

}

module_exit(max7359_exit);

MODULE_AUTHOR("Kim Kyuwon <[email protected]>");

MODULE_DESCRIPTION("MAX7359 Key Switch Controller Driver");

MODULE_LICENSE("GPL v2");

1.1.2 Board-am335xevm.c

上面代码涉及到设备资源如下:

/* Keys mapping */

static const uint32_t am335x_evm_matrix_keys[] = {

KEY(0, 0, KEY_RIGHT),  //sw4

KEY(0, 1, KEY_MENU),  //sw8

KEY(0, 2, KEY_ENTER),  //sw12

KEY(0, 3, KEY_RESERVED),  //sw16

KEY(0, 4, KEY_RESERVED),

KEY(0, 5, KEY_RESERVED),

    /* Modified by XXXX2013-08-27 Used for knob driver. */

KEY(0, 6, KEY_VOLUMEDOWN),

KEY(0, 7, KEY_VOLUMEUP),

KEY(1, 0, KEY_LEFT),  //sw3

KEY(1, 1, KEY_PAGEUP),  //sw7

KEY(1, 2, KEY_RESERVED),  //sw11

KEY(1, 3, KEY_RESERVED),  //sw15

KEY(1, 4, KEY_RESERVED),

KEY(1, 5, KEY_RESERVED),

KEY(1, 6, KEY_RESERVED),

KEY(1, 7, KEY_RESERVED),

KEY(2, 0, KEY_DOWN),  //sw2

KEY(2, 1, KEY_ESC),//sw6

KEY(2, 2, KEY_PAGEDOWN),  //sw10

KEY(2, 3, KEY_RESERVED),  //sw14

KEY(2, 4, KEY_RESERVED),

KEY(2, 5, KEY_RESERVED),

KEY(2, 6, KEY_RESERVED),

KEY(2, 7, KEY_RESERVED),

KEY(3, 0, KEY_UP),  //sw1

KEY(3, 1, KEY_OK),  //sw5

KEY(3, 2, KEY_RESERVED),  //sw9

KEY(3, 3, KEY_RESERVED),  //sw13

KEY(3, 4, KEY_RESERVED),

KEY(3, 5, KEY_RESERVED),

KEY(3, 6, KEY_RESERVED),

KEY(3, 7, KEY_RESERVED),

};

static struct pinmux_config max7359_keypad_pin_mux[] = {

    {"mii1_txd2.gpio0_17", OMAP_MUX_MODE7 | AM33XX_PIN_INPUT},

    {"gpmc_a8.gpio1_24",  OMAP_MUX_MODE7 | AM33XX_PIN_INPUT_PULLUP},

    {"gpmc_a9.gpio1_25",  OMAP_MUX_MODE7 | AM33XX_PIN_INPUT_PULLUP},

    {NULL, 0},

};

const struct matrix_keymap_data am335x_evm_keymap_data = {

    .keymap      = am335x_evm_matrix_keys/*按键图*/

    .keymap_size  = ARRAY_SIZE(am335x_evm_matrix_keys),

    .gpio_a      = AM335XEVM_KNOB_A_IRQ_GPIO,

    .gpio_b      = AM335XEVM_KNOB_B_IRQ_GPIO,

    .irq_a       = OMAP_GPIO_IRQ(AM335XEVM_KNOB_A_IRQ_GPIO),

    .irq_b       = OMAP_GPIO_IRQ(AM335XEVM_KNOB_B_IRQ_GPIO),

};

static struct pinmux_config i2c1_pin_mux[] = {

{"uart1_rxd.i2c1_sda",  OMAP_MUX_MODE3 | AM33XX_SLEWCTRL_SLOW|

AM33XX_PULL_ENBL | AM33XX_INPUT_EN},

    {"uart1_txd.i2c1_scl",   OMAP_MUX_MODE3 | AM33XX_SLEWCTRL_SLOW |

                 AM33XX_PULL_ENBL | AM33XX_INPUT_EN},

    {NULL, 0},

};

static struct i2c_board_info am335x_i2c1_boardinfo[] = {

    {

     I2C_BOARD_INFO("max7359", 0x38), /*匹配驱动名,设备ID*/

        .irq = OMAP_GPIO_IRQ(AM335XEVM_KEY_IRQ_GPIO), /*设置中断号*/

        .platform_data = (void*)&am335x_evm_keymap_data,/*平台数据:按键资源*/

    },

};

static void i2c1_init(int evm_id, int profile)

{

    setup_pin_mux(max7359_keypad_pin_mux); /*初始化按键中断与旋钮IO*/

setup_pin_mux(i2c1_pin_mux); /*初始化按键I2C接口*/

omap_register_i2c_bus(2, 100, am335x_i2c1_boardinfo,

         ARRAY_SIZE(am335x_i2c1_boardinfo));

return;

}

1.1.3 Mux33xx.c

/* AM33XX pin mux super set */

static struct omap_mux __initdata am33xx_muxmodes[] = {

… …

_AM33XX_MUXENTRY(GPMC_A8, 0,

"gpmc_a8", "mii2_rxd3", "rgmii2_rd3", "mmc2_dat6",

NULL, NULL, "mcasp0_aclkx", "gpio1_24"),

_AM33XX_MUXENTRY(GPMC_A9, 0,

"gpmc_a9", "mii2_rxd2", "rgmii2_rd2", "mmc2_dat7",

NULL, NULL, "mcasp0_fsx", "gpio1_25"),

_AM33XX_MUXENTRY(MII1_TXD2, 0,

"mii1_txd2", NULL, "rgmii1_td2", NULL,

"mcasp1_axr0", "mmc2_dat2", "mcasp0_ahclkx", "gpio0_17"),

_AM33XX_MUXENTRY(UART1_RXD, 0,

"uart1_rxd", "mmc1_sdwp", NULL, "i2c1_sda",

NULL, "pr1_uart0_rxd_mux1", NULL, "gpio0_14"),

_AM33XX_MUXENTRY(UART1_TXD, 0,

"uart1_txd", "mmc2_sdwp", NULL, "i2c1_scl",

NULL, "pr1_uart0_txd_mux1", NULL, "gpio0_15"),

… …

}

1.2 源码实现功能

max7359芯片为按键扫描芯片,最大可支持64个按键扫描,通过I2C接口与主控芯片连接通信。

Ti_am3358单板I2C总线驱动(适配器)加载之后,才能调用I2C设备驱动(max7359驱动)。调用max7359设备驱动首先初始化max7359设备,并进行后续的访问。

需要注意:1.max7359驱动中XXX开发人员加入了旋钮的驱动检测,两个GPIO口直接连接检测高低电平,从而实现了旋钮功能。旋钮事件是如何上报给应用层的呢?2.可以阅读原理图和7359/旋钮的数据手册,理解整个代码。


附件一:

1.内核menuconfig配图

 


发布了41 篇原创文章 · 获赞 13 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/wade_510/article/details/72084651
今日推荐