在平台RK3288上编写键盘驱动

环境介绍
硬件:RK3288、键盘驱动芯片PCA9535(I2C-GPIO)
软件:android 7.1 linux4.4
原理图:

在这里插入图片描述
I2C1_INT:GPIO5_B3
I2C1_RST:GPIO7_B4
配置介绍
1.dts

&i2c1 {
	status = "okay";
	clock-frequency = <400000>;
	pca9535@20 {
		compatible = "nxp,pca9535_keypad";
		reg = <0x20>;
		irq_gpio_number = <&gpio5 11 IRQ_TYPE_LEVEL_LOW>;
		rst_gpio_number = <&gpio7 12 GPIO_ACTIVE_HIGH>;
		status = "okay";
	};
};

2.config

CONFIG_KEYBOARD_PCA953X=y

3.Kconfig


config KEYBOARD_PCA953X
       tristate "nxp PCA953X keyboard Sensor Chip"
       depends on I2C
       help
         Say Y here if you want to use nxp PCA953X keyboard
         Sensor chip as input device.

         To compile this driver as a module, choose M here:
         the module will be called PCA953X

4.Makefile

obj-$(CONFIG_KEYBOARD_PCA953X)		+= pca953x-keypad.o

驱动源码

/*
 *  PCA953x 4/8/16/24/40 bit I/O ports
 *
 *  Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com>
 *  Copyright (C) 2007 Marvell International Ltd.
 *
 *  Derived from drivers/i2c/chips/pca9539.c
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 2 of the License.
 */

#include <linux/clk.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/of_gpio.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <linux/proc_fs.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/regmap.h>


#include <linux/platform_data/pca953x.h>
#include <linux/slab.h>
#ifdef CONFIG_OF_GPIO
#include <linux/of_platform.h>
#endif
#include <linux/acpi.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/timer.h>




#define PCA_INT			0x0100
#define PCA953X_TYPE		0x1000
#define PCA957X_TYPE		0x2000
#define PCA_TYPE_MASK		0xF000

#define PAC9535_NAME  "pac9535"

enum pca9535kbd_cmd  
{  
    PCA9535_INPUT_0     = 0,  
    PCA9535_INPUT_1     = 1,  
    PCA9535_OUTPUT_0    = 2,  
    PCA9535_OUTPUT_1    = 3,  
    PCA9535_INVERT_0    = 4,  
    PCA9535_INVERT_1    = 5,  
    PCA9535_DIRECTION_0 = 6,  
    PCA9535_DIRECTION_1 = 7,  
};  


struct gpio_key_button {  
    u16         gpio_mask;  
    int         keycode;  
}; 
struct gpio_key_button key_buttons[] = {  
    { .gpio_mask = 0x0001, .keycode = KEY_1 },  
    { .gpio_mask = 0x0002, .keycode = KEY_2 },  
    { .gpio_mask = 0x0004, .keycode = KEY_3 },  
    { .gpio_mask = 0x0008, .keycode = KEY_4 },  
    { .gpio_mask = 0x0010, .keycode = KEY_5 }, 
    { .gpio_mask = 0x0020, .keycode = KEY_6 },  
    { .gpio_mask = 0x0040, .keycode = KEY_7 }, 
    { .gpio_mask = 0x0080, .keycode = KEY_8 },
    { .gpio_mask = 0x0100, .keycode = KEY_9 },  
    { .gpio_mask = 0x0200, .keycode = KEY_BACKSPACE },
	{ .gpio_mask = 0x0400, .keycode = KEY_0 },
	{ .gpio_mask = 0x0800, .keycode = KEY_ENTER },
	{ .gpio_mask = 0x1000, .keycode = KEY_UP },
	{ .gpio_mask = 0x2000, .keycode = KEY_DOWN },
	{ .gpio_mask = 0x4000, .keycode = KEY_LEFT },
	{ .gpio_mask = 0x8000, .keycode = KEY_RIGHT },
		
}; 

static const struct i2c_device_id pca953x_id[] = {
	{ "pca9535", 16 | PCA953X_TYPE | PCA_INT, },
	
	{ }
};
MODULE_DEVICE_TABLE(i2c, pca953x_id);

static const struct acpi_device_id pca953x_acpi_ids[] = {
	{ "INT3491", 16 | PCA953X_TYPE | PCA_INT, },
	{ }
};
MODULE_DEVICE_TABLE(acpi, pca953x_acpi_ids);




struct pca953x_priv{
	struct i2c_client *client;
	struct input_dev    *input;  
	struct work_struct  work;
	int irq_gpio_number ;
	int	rst_gpio_number ;
	int irq_det_invert;
	u16 port_state;
	
};
static int device_pca953x_init(struct pca953x_priv *pdata){

	 /* Initialize the PCA9535 to known state */  
	int ret;
    ret = i2c_smbus_write_byte_data(pdata->client, PCA9535_DIRECTION_0, 0xFF);
	if(ret < 0){
		dev_err(&(pdata->client->dev), "fail to i2c_smbus_write_byte_data PCA9535_DIRECTION_0\n");
		goto exit;
	}
    ret = i2c_smbus_write_byte_data(pdata->client, PCA9535_DIRECTION_1, 0xFF);
	if(ret < 0){
		dev_err(&(pdata->client->dev), "fail to i2c_smbus_write_byte_data PCA9535_DIRECTION_1\n");
		goto exit;
	}
    ret = i2c_smbus_write_byte_data(pdata->client, PCA9535_INVERT_0, 0xFF); 
	if(ret < 0){
		dev_err(&(pdata->client->dev), "fail to i2c_smbus_write_byte_data PCA9535_INVERT_0\n");
		goto exit;
	}
    ret = i2c_smbus_write_byte_data(pdata->client, PCA9535_INVERT_1, 0xFF); 
	if(ret < 0){
		dev_err(&(pdata->client->dev), "fail to i2c_smbus_write_byte_data PCA9535_INVERT_1\n");
		goto exit;
	}
	exit:
		return ret;
}
static void pca953x_do_work(struct work_struct *work_data) {
	int i;  
    u16 new_state, pressed, released;  
    struct pca953x_priv *data = container_of(work_data,  struct pca953x_priv, work);  
    new_state = i2c_smbus_read_byte_data(data->client, PCA9535_INPUT_0)   
                | (i2c_smbus_read_byte_data(data->client, PCA9535_INPUT_1) << 8);  
     
	dev_err(&(data->client->dev), "%s:0x%x" ,__func__, new_state);
    if(new_state == data->port_state)  
        return;    
    /* detect buttons which be newly pressed */  
    released = data->port_state & ~new_state;  
    /* detect buttons which just has been released */  
    pressed = new_state & ~data->port_state;  
      
    for(i = 0; i < ARRAY_SIZE(key_buttons); i++) {  
        if(pressed & key_buttons[i].gpio_mask) {  
            input_event(data->input, EV_KEY, key_buttons[i].keycode, 1);  
            input_sync(data->input);  
        }  
        if(released & key_buttons[i].gpio_mask) {  
            input_event(data->input, EV_KEY, key_buttons[i].keycode, 0);  
            input_sync(data->input);  
        }  
    }  
    data->port_state = new_state;  
}
static irqreturn_t pca9535_irq_handler(int irq, void *data)
{
	struct pca953x_priv *pdata = data;

	schedule_work(&pdata->work);

	return IRQ_HANDLED;
}

static int pca953x_probe(struct i2c_client *client,
				   const struct i2c_device_id *id)
{

	struct	pca953x_priv *pdata;
	struct device_node *np = client->dev.of_node;
	int ret ;
	int i;
	int pca9535_irq;
	enum of_gpio_flags flags;
	pdata = devm_kzalloc(&client->dev,sizeof(struct pca953x_priv), GFP_KERNEL);
	if (pdata == NULL)
		return -ENOMEM;
	memset(pdata,0,sizeof(struct pca953x_priv));
	pdata->client= client;
	
	
	/*input dev init*/
	pdata->input = input_allocate_device();
	if(!pdata->input)
		goto fail0;
	pdata->input->name = PAC9535_NAME;
	pdata->input->id.bustype = BUS_I2C;	
	pdata->input->id.vendor = 0x0001;  
    pdata->input->id.product = 0x0001;  
    pdata->input->id.version = 0x0100;
	__set_bit(EV_KEY, pdata->input->evbit);
	__set_bit(INPUT_PROP_DIRECT, pdata->input->propbit);
    for(i = 0; i < ARRAY_SIZE(key_buttons); i++) {  
        input_set_capability(pdata->input, EV_KEY, key_buttons[i].keycode);  
    }  
	
	if((ret = input_register_device(pdata->input))) {
		dev_err(&client->dev, "%s: failed to register input device: %s\n", 
			__func__, dev_name(&client->dev));
		goto fail0;
	}
	
	pdata->irq_gpio_number = of_get_named_gpio_flags(np,
						      "irq_gpio_number",
						      0,
						      &flags);
	if (pdata->irq_gpio_number < 0) {
		dev_err(&client->dev, "Can not read property hp_det_gpio\n");
		pdata->irq_gpio_number = -1;
	} else {
		INIT_WORK(&pdata->work, pca953x_do_work);
		pdata->irq_det_invert = !!(flags & OF_GPIO_ACTIVE_LOW);
		ret = devm_gpio_request_one(&client->dev, pdata->irq_gpio_number,
					    GPIOF_IN, "pca9535_irq");
		if (ret < 0)
			return ret;
		pca9535_irq = gpio_to_irq(pdata->irq_gpio_number);
		ret = devm_request_threaded_irq(&client->dev, pca9535_irq, NULL,
						pca9535_irq_handler,
						IRQF_TRIGGER_FALLING |
						IRQF_TRIGGER_RISING |
						IRQF_ONESHOT,
						"pca9535_interrupt", pdata);
		if (ret < 0) {
			dev_err(&client->dev, "request_irq failed: %d\n", ret);
			return ret;
		}
	
	}
	pdata->rst_gpio_number = of_get_named_gpio_flags(np,
						      "rst_gpio_number",
						      0,
						      &flags);
	ret = devm_gpio_request_one(&client->dev, pdata->rst_gpio_number,
					    GPIOF_IN, "pca9535_rst");
	if (ret < 0)
		return ret;

	i2c_set_clientdata(client, pdata);
	ret = device_pca953x_init(pdata);

	return 0;

	fail0:
		return -1;
}
static int pca953x_remove(struct i2c_client *client)
{
	
	struct pca953x_priv *pdata = i2c_get_clientdata(client);
	if(pdata == NULL)
		return 0;
	dev_err(&(client->dev), "%s:%d" ,__func__, __LINE__);
	if(pdata->input){
		input_unregister_device(pdata->input);
		input_free_device(pdata->input);
	}
	dev_err(&(client->dev), "%s:%d" ,__func__, __LINE__);
	i2c_set_clientdata(client, NULL);
	kfree(pdata);
	dev_err(&(client->dev), "%s:%d" ,__func__, __LINE__);
	return 0;
}

static const struct of_device_id pca953x_dt_ids[] = {
	{ .compatible = "nxp,pca9535_keypad", },
	
	{ }
};

MODULE_DEVICE_TABLE(of, pca953x_dt_ids);

static struct i2c_driver pca953x_driver = {
	.driver = {
		.name	= PAC9535_NAME,
		.of_match_table = pca953x_dt_ids,
		.acpi_match_table = ACPI_PTR(pca953x_acpi_ids),
	},
	.probe		= pca953x_probe,
	.remove		= pca953x_remove,
	.id_table	= pca953x_id,
};

static int __init pca953x_init(void)
{
	return i2c_add_driver(&pca953x_driver);
}
/* register after i2c postcore initcall and before
 * subsys initcalls that may rely on these GPIOs
 */
subsys_initcall(pca953x_init);

static void __exit pca953x_exit(void)
{
	i2c_del_driver(&pca953x_driver);
}
module_exit(pca953x_exit);

MODULE_AUTHOR("jerry deng <394419178@qq.com>");
MODULE_DESCRIPTION("keypad driver for PCA953x");
MODULE_LICENSE("GPL");

测试方法
getevent

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值