/*===============================================================
* @ halecho@163.com
*
* 文件名称:cp2528_key.c
* 创 建 者 hecong
* 创建日期:2018年03月26日
* 描 述:
*
================================================================*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#include <asm/io.h>
#include <linux/i2c.h>
#include <linux/timer.h>
#include "cp2528.h"
#define CP2528_KEY_ADDR 0x31
#define CP2528_WP_ADDR 0x60
#define CP2528_WP_VALUE 0x0055
#define CP2528_RESET_ADDR 0x01
#define CP2528_RESET_VALUE 0x0080
#define CP2528_SLEEP_ADDR 0x01
#define CP2528_SLEEP_VALUE 0xf800
#define CP2528_KEY_ADDR 0x31
#define CP2528_CHIP_ID 0x2825
#define DBG(fmt...) printk(fmt)
static struct i2c_board_info cp2528_info = {
I2C_BOARD_INFO("cp2528", 0x2c),
};
static struct i2c_client *cp2528_client;
/* Keypad Initialization */
#define KEYPAD_BUTTON(ev_type, ev_code, act_low) \
{ \
.type = ev_type, \
.code = ev_code, \
.active_low = act_low, \
}
#define KEYPAD_BUTTON_LOW(event_code) KEYPAD_BUTTON(EV_KEY, event_code, 0)
static struct cp2528_button cp2528_i2c_keys[] = {
KEYPAD_BUTTON_LOW(0),
KEYPAD_BUTTON_LOW(1),
KEYPAD_BUTTON_LOW(2),
KEYPAD_BUTTON_LOW(3),
KEYPAD_BUTTON_LOW(4),
};
static struct cp2528_keys_platform_data cp2528_keys_info = {
.buttons = cp2528_i2c_keys,
.nbuttons = ARRAY_SIZE(cp2528_i2c_keys),
.rep = 1,
.use_polling = 1,
.pinmask = 0xFF00,
};
struct cp2528_keypad_chip {
uint16_t reg_output;
uint16_t reg_direction;
uint16_t reg_input;
struct i2c_client *client;
struct input_dev *input;
struct delayed_work dwork;
u16 pinmask;
int irqnum;
bool use_polling;
struct cp2528_button buttons[0];
};
struct cp2528_keypad_chip *chip;
static int cp2528_write_reg(struct cp2528_keypad_chip *chip, u16 reg,
u32 val)
{
int error;
error = i2c_smbus_write_word_data(chip->client, reg, val);
if (error < 0) {
dev_err(&chip->client->dev,
"%s failed, reg: %d, val: %d, error: %d\n",
__func__, reg, val, error);
return error;
}
return 0;
}
static int cp2528_read_reg(struct cp2528_keypad_chip *chip, u16 reg,
u16 * val)
{
int retval;
retval = i2c_smbus_read_word_data(chip->client, reg);
if (retval < 0) {
dev_err(&chip->client->dev, "%s failed, reg: %d, error: %d\n",
__func__, reg, retval);
return retval;
}
*val = (u16) retval;
return 0;
}
static void cp2528_keys_scan(struct cp2528_keypad_chip *chip)
{
struct input_dev *input = chip->input;
u16 reg_val, val;
int error, i, pin_index;
error = cp2528_read_reg(chip, CP2528_KEY_ADDR, ®_val);
if (error)
return;
reg_val &= chip->pinmask;
/* Figure out which lines have changed */
val = reg_val ^ chip->reg_input;
chip->reg_input = reg_val;
for (i = 0, pin_index = 0; i < 16; i++) {
if (val & (1 << i)) {
struct cp2528_button *button = &chip->buttons[pin_index];
unsigned int type = button->type ? : EV_KEY;
int state = ((reg_val & (1 << i)) ? 1 : 0)
^ button->active_low;
input_event(input, type, button->code, ! !state);
input_sync(input);
}
if (chip->pinmask & (1 << i))
pin_index++;
}
}
static void cp2528_keys_work_func(struct work_struct *work)
{
struct cp2528_keypad_chip *chip =
container_of(work, struct cp2528_keypad_chip, dwork.work);
cp2528_keys_scan(chip);
schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100));
}
static int cp2528_keys_open(struct input_dev *dev)
{
struct cp2528_keypad_chip *chip = input_get_drvdata(dev);
/* Get initial device state in case it has switches */
cp2528_keys_scan(chip);
if (chip->use_polling)
schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100));
return 0;
}
static void cp2528_keys_close(struct input_dev *dev)
{
struct cp2528_keypad_chip *chip = input_get_drvdata(dev);
if (chip->use_polling)
cancel_delayed_work_sync(&chip->dwork);
}
static int __devinit cp2528_setup_registers(struct cp2528_keypad_chip
*chip)
{
int error;
u16 value;
cp2528_read_reg(chip, 0, &value);
if (value == CP2528_CHIP_ID) {
} else {
return -1;
}
msleep(50);
error = cp2528_write_reg(chip, CP2528_WP_ADDR, CP2528_WP_VALUE);
msleep(50);
error = cp2528_read_reg(chip, CP2528_WP_ADDR, &value);
DBG("read 0x60 0x0055:: %d value:%d\n", error, value);
chip->reg_input &= chip->pinmask;
return 0;
}
static int cp2528_dev_init(void)
{
struct i2c_adapter *i2c_adap; //分配一个适配器的指针
unsigned char id_buf[2] = { 0xff, 0x00 };
unsigned char id_add[1] = { 0xf4 };
unsigned char id_temp = 0;;
int ret = 0;
struct cp2528_keys_platform_data *pdata;
struct input_dev *input;
int error;
int i;
i2c_adap = i2c_get_adapter(1);
cp2528_client = i2c_new_device(i2c_adap, &cp2528_info);
i2c_put_adapter(i2c_adap);
chip = kzalloc(sizeof(struct cp2528_keypad_chip) +
5 * sizeof(struct cp2528_button), GFP_KERNEL);
input = input_allocate_device();
if (!chip || !input) {
error = -ENOMEM;
goto fail;
}
DBG("%d\n", __LINE__);
chip->client = cp2528_client;
cp2528_client->dev.platform_data = &cp2528_keys_info;
pdata = cp2528_client->dev.platform_data;
chip->input = input;
chip->pinmask = pdata->pinmask;
chip->use_polling = pdata->use_polling;
DBG("%d\n", __LINE__);
INIT_DELAYED_WORK(&chip->dwork, cp2528_keys_work_func);
DBG("%d\n", __LINE__);
input->phys = "cp2528-keys/input1";
input->name = cp2528_client->name;
input->dev.parent = &cp2528_client->dev;
input->open = cp2528_keys_open;
input->close = cp2528_keys_close;
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
DBG("%d\n", __LINE__);
/* Enable auto repeat feature of Linux input subsystem */
if (pdata->rep)
__set_bit(EV_REP, input->evbit);
DBG("nbutton:%d\n", pdata->nbuttons);
for (i = 0; i < pdata->nbuttons; i++) {
unsigned int type;
chip->buttons[i] = pdata->buttons[i];
type = (pdata->buttons[i].type) ? : EV_KEY;
input_set_capability(input, type, pdata->buttons[i].code);
}
DBG("%d\n", __LINE__);
input_set_drvdata(input, chip);
/*
* Initialize cached registers from their original values.
* we can't share this chip with another i2c master.
*/
error = cp2528_setup_registers(chip);
if (error)
goto fail;
DBG("%d\n", __LINE__);
error = input_register_device(input);
if (error) {
DBG(&cp2528_client->dev,
"Unable to register input device, error: %d\n", error);
goto fail;
}
DBG("%d\n", __LINE__);
i2c_set_clientdata(cp2528_client, chip);
DBG("%d\n", __LINE__);
return 0;
fail:
input_free_device(input);
kfree(chip);
return error;
return 0;
}
static void cp2528_dev_exit(void)
{
i2c_unregister_device(cp2528_client);
input_unregister_device(chip->input);
kfree(chip);
}
module_init(cp2528_dev_init);
module_exit(cp2528_dev_exit);
MODULE_AUTHOR("qualvision");
MODULE_DESCRIPTION("GT911 Touch Screen Driver");
MODULE_LICENSE("GPL");
国科gk7102s上cp2528值i2c触摸按键驱动实现
最新推荐文章于 2024-08-01 11:01:53 发布