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配图