处理器:sim8950,msm8953
内核版本:linux4.4
按键芯片:CH452A
SIM8950 CH452A
M_GPIO_19(PIN52) DCLK(PIN27) -》I2C_SCL
M_GPIO_18(PIN51) DIN(PIN26) -》 I2C_SDA
M_GPIO_59(PIN68) DOUT(PIN24) -》 KEYBOARD_INT
NA ADDR -》高,I2C设备地址
NA VCC(PIN23) -》 3.3V
NA ADDR(PIN25) -》 3.3V
1、设备树中添加I2C键盘设备;
kernel/arch/arm64/boot/dts/qcom/msm8953.dtsi
i2c_5: i2c@7af5000 {
compatible = "qcom,i2c-msm-v2";
...
status = "ok";
ch452@60 {
status = "okay";
compatible = "wch,ch452-keypad";
reg = <0x60>; // CH452 ADDR为高,此处地址为0x60
interrupt-parent = <&tlmm>;
interrupts = <59 0x2008>;
wch,i2c-sda-gpios = <&tlmm 18 0x00>; // GPIO模拟I2C,设置SDA GPIO编号
wch,i2c-scl-gpios = <&tlmm 19 0x00>; // GPIO模拟I2C,设置SCL GPIO编号
wch,interrupt-gpios = <&tlmm 59 0x00>; // 中断上报,用于在中断中获取键值
};
};
参考文档:
kernel/documentation/input/input-programming.txt
2、添加CH452按键编译规则
kernel/arch/arm64/configs/msm8953_defconfig
CONFIG_KEYBOARD_CH452=y
kernel/drivers/input/keyboard/Kconfig
config KEYBOARD_CH452
tristate "ch452_keypad support"
depends on I2C
kernel/drivers/input/keyboard/Makefile
obj-$(CONFIG_KEYBOARD_CH452) += ch452_keypad.o
3、CH452内核驱动代码
static const unsigned int g_ch452_keyinfo[][3] = {
// {row, col, val}
{1, 1, KEY_APPSELECT}, // APP
{1, 0, KEY_LEFT}, // left
{0, 2, KEY_UP}, // up
{7, 0, KEY_DOWN}, // down
{0, 0, KEY_RIGHT}, // right
{0, 1, KEY_ENTER}, // ok
...
};
static int ch452_parse_dt(struct device *dev)
{
enum of_gpio_flags gpio_flag;
struct device_node *np = dev->of_node;
struct ch452_data *pData = &g_ch452Data;
#if (CH452_I2C_SIMULATE == 1)
pData->scl_pin = of_get_named_gpio_flags(np, "wch,i2c-scl-gpios", 0, &gpio_flag);
CH452_INFO("%s, pData->scl_pin=%dn", __FUNCTION__, pData->scl_pin);
pData->sda_pin = of_get_named_gpio_flags(np, "wch,i2c-sda-gpios", 0, &gpio_flag);
CH452_INFO("%s, pData->sda_pin=%dn", __FUNCTION__, pData->sda_pin);
#endif
pData->int_pin = of_get_named_gpio_flags(np, "wch,interrupt-gpios", 0, &gpio_flag);
CH452_INFO("%s, pData->int_pin=%dn", __FUNCTION__, pData->int_pin);
return 0;
}
static int ch452_gpio_init(void)
{
struct ch452_data *pData = &g_ch452Data;
#if (CH452_I2C_SIMULATE == 1)
if (gpio_is_valid(pData->sda_pin)) {
gpio_request(pData->sda_pin, "ch452 sda pin");
gpio_direction_output(pData->sda_pin, GPIO_ST_HIGH);
}
if (gpio_is_valid(pData->scl_pin)) {
gpio_request(pData->scl_pin, "ch452 scl pin");
gpio_direction_output(pData->scl_pin, GPIO_ST_HIGH);
}
#endif
if (gpio_is_valid(pData->int_pin)) {
gpio_request(pData->int_pin, "ch452 int pin");
gpio_direction_input(pData->int_pin);
}
return 0;
}
int ch452_get_keycode(int row, int col, int *keyCode)
{
int i;
int keyNum = sizeof(g_ch452_keyinfo) / sizeof(g_ch452_keyinfo[0]);
for (i = 0; i < keyNum; i++) {
if ((row == g_ch452_keyinfo[i][0]) && (col == g_ch452_keyinfo[i][1])) {
*keyCode = g_ch452_keyinfo[i][2];
return 0;
}
}
CH452_ERROR("%s key not found! %d %dn", row, col);
return -1;
}
static void ch452_worker(struct work_struct *work)
{
unsigned char val;
int row, col, keycode;
struct ch452_data *pData = &g_ch452Data;
val = Ch452_Read(CH452_I2C_ADDR1, (unsigned short)CH452_GET_KEY); // 读取扫描行列号
row = val & 0x7;
col = (val >> 3) & 0x7;
if (ch452_get_keycode(row, col, &keycode) == 0) {
input_report_key(pData->input, keycode, -1); // 上报按键按下
input_sync(pData->input);
udelay(1);
input_report_key(pData->input, keycode, 0); // 上报按键抬起
input_sync(pData->input);
}
return;
}
static irqreturn_t ch452_interrupt(int irq, void *dev_id)
{
ch452_worker(NULL);
return IRQ_HANDLED;
}
static int ch452_irq_init(struct i2c_client *client)
{
int ret;
struct ch452_data *pData = &g_ch452Data;
pData->int_irq = gpio_to_irq(pData->int_pin);
CH452_INFO("%s irq num:%dn", __FUNCTION__, pData->int_irq);
if (pData->int_irq) {
// key up: int pin = 0; key down: int pin = 1
ret = request_threaded_irq(pData->int_irq, NULL, ch452_interrupt,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
client->name, pData);
if (ret < 0) {
CH452_ERROR("%s call request_irq failedn", __FUNCTION__);
return ret;
}
}
return ret;
}
static struct input_dev* ch452_input_init(struct i2c_client *client)
{
int i, ret;
struct ch452_data *pData = &g_ch452Data;
struct input_dev *input;
int row,col,keyCode;
int keyNum = sizeof(g_ch452_keyinfo) / sizeof(g_ch452_keyinfo[0]);
CH452_DEBUG("%s entryn", __FUNCTION__);
input = input_allocate_device();
if (input == NULL) {
dev_err(&client->dev, "%s call input_allocate_device failedn", __FUNCTION__);
return NULL;
}
input->name = client->name;
input->id.bustype = BUS_I2C;
input->open = ch452_open;
input->close = ch452_close;
input->evbit[0] = BIT_MASK(EV_KEY); // input处理按键事件
for (i = 0; i < keyNum; i++) {
keyCode = g_ch452_keyinfo[i][2];
row = g_ch452_keyinfo[i][0];
col = g_ch452_keyinfo[i][1];
input->keybit[BIT_WORD(keyCode)] |= BIT_MASK(keyCode); // 注册需要上报的keycode
CH452_INFO("%s i=%d,row=%d,col=%d,keycode=0x%xn", __FUNCTION__, i, row, col, keyCode);
}
ret = input_register_device(input); // 注册input设备,用于上报上层系统
if (ret) {
CH452_ERROR("failed to register input devicen");
return NULL;
}
return input;
}
static int ch452_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret;
struct ch452_data *pData = &g_ch452Data;
CH452_INFO("%s entry, client name=%sn", __FUNCTION__, client->name);
ret = ch452_parse_dt(&client->dev);
if (ret != 0) {
CH452_ERROR("%s call ch452_parse_dt failedn", __FUNCTION__);
return ret;
}
ret = ch452_gpio_init();
if (ret != 0) {
CH452_ERROR("%s call ch452_gpio_init failedn", __FUNCTION__);
return ret;
}
pData->client = client;
pData->input = ch452_input_init(client);
if (pData->input == NULL) {
CH452_ERROR("%s call ch452_input_init failedn", __FUNCTION__);
return -1;
}
ret = ch452_irq_init(client);
if (ret != 0) {
CH452_ERROR("call ch452_irq_init failed!n");
return -1;
}
ch452_hw_init();
i2c_set_clientdata(client, pData);
return 0;
}
static int ch452_remove(struct i2c_client *client)
{
struct ch452_data *pData = &g_ch452Data;
CH452_INFO("%s entryn", __FUNCTION__);
input_unregister_device(pData->input);
return 0;
}
static const struct of_device_id ch452_dt_match[] = {
{.compatible = "wch,ch452-keypad",},
{},
};
static const struct i2c_device_id ch452_i2c_id[] = {
{"ch452-keypad", 0},
{}
};
static struct i2c_driver ch452_i2c_driver = {
.probe = ch452_probe,
.remove = ch452_remove,
.id_table = ch452_i2c_id,
.driver = {
.name = "ch452-keypad",
.owner = THIS_MODULE,
.of_match_table = ch452_dt_match,
},
};
static int __init ch452_init(void)
{
CH452_DEBUG("%s entryn", __FUNCTION__);
return i2c_add_driver(&ch452_i2c_driver);
}
module_init(ch452_init);
static void __exit ch452_exit(void)
{
struct ch452_data *pData = &g_ch452Data;
#if (CH452_I2C_SIMULATE == 1)
gpio_free(pData->scl_pin);
gpio_free(pData->sda_pin);
#endif
gpio_free(pData->int_pin);
i2c_del_driver(&ch452_i2c_driver);
}
module_exit(ch452_exit);
MODULE_AUTHOR("Leo.li");
MODULE_DEscriptION("CH452 Keypad Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:ch452-keypad");
GPIO模拟I2C驱动与参考Linux驱动参考如下:
https://download.csdn.net/download/wulang4220/9938539?ops_request_misc=&request_id=&biz_id=103&utm_term=CH452%20linux%20keyboard%E9%A9%B1%E5%8A%A8&utm_medium=distribute.pc_search_result.none-task-download-2allsobaiduweb~default-0-9938539.first_rank_v2_pc_rank_v29&spm=1018.2226.3001.4187.2
1、驱动加载时无法进入probe入口函数
解决办法:添加id table解决
static const struct i2c_device_id ch452_i2c_id[] = {
{“ch452-keypad”, 0},
{}
};
1、input子系统——kernel中input设备介绍
https://blog.csdn.net/u013604527/article/details/53432623
2、input子系统按键处理实例介绍
https://blog.csdn.net/liyanfei123456/article/details/53197277



