SmartAudio/lichee/linux-4.9/drivers/input/sensor/mxc622x.c

495 lines
12 KiB
C

/*
* mxc6225.c - Linux kernel modules for Detection Sensor
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/input-polldev.h>
#include <linux/device.h>
#include "../init-input.h"
//#include <mach/system.h>
//#include <mach/hardware.h>
#undef CONFIG_HAS_EARLYSUSPEND
#undef CONFIG_PM
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
#if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_PM)
#include <linux/pm.h>
#endif
/*
* Defines
*/
#define assert(expr)\
if (!(expr)) {\
printk(KERN_ERR "Assertion failed! %s,%d,%s,%s\n",\
__FILE__, __LINE__, __func__, #expr);\
}
#define MXC622X_DRV_NAME "mxc622x"
#define SENSOR_NAME MXC622X_DRV_NAME
#define POLL_INTERVAL_MAX 500
#define POLL_INTERVAL 50
#define INPUT_FUZZ 2
#define INPUT_FLAT 2
#define MXC622X_REG_CTRL 0x04
#define MXC622X_REG_DATA 0x00
/* MXC622X control bit */
#define MXC622X_CTRL_PWRON 0x00 /* power on */
#define MXC622X_CTRL_PWRDN 0x80 /* power donw */
#define MODE_CHANGE_DELAY_MS 100
static struct device *hwmon_dev;
static struct i2c_client *mxc622x_i2c_client;
static struct input_polled_dev *mxc622x_idev;
static struct sensor_config_info gsensor_info = {
.input_type = GSENSOR_TYPE,
};
struct mxc622x_data_s {
atomic_t enable;
struct i2c_client *client;
struct input_polled_dev *pollDev;
struct mutex interval_mutex;
#ifdef CONFIG_HAS_EARLYSUSPEND
struct early_suspend early_suspend;
volatile int suspend_indator;
#endif
} mxc622x_data;
static int filter_flag;
/* Addresses to scan */
static const unsigned short normal_i2c[] = {0x15,I2C_CLIENT_END};
enum {
DEBUG_INIT = 1U << 0,
DEBUG_CONTROL_INFO = 1U << 1,
DEBUG_DATA_INFO = 1U << 2,
DEBUG_SUSPEND = 1U << 3,
};
static u32 debug_mask = 0x0;
#define dprintk(level_mask, fmt, arg...) if (unlikely(debug_mask & level_mask)) \
printk(KERN_DEBUG fmt , ## arg)
module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
#ifdef CONFIG_HAS_EARLYSUSPEND
static void mxc622x_early_suspend(struct early_suspend *h);
static void mxc622x_late_resume(struct early_suspend *h);
#endif
#if 0
//Function as i2c_master_send, and return 1 if operation is successful.
static int i2c_write_bytes(struct i2c_client *client, uint8_t *data, uint16_t len)
{
struct i2c_msg msg;
int ret=-1;
msg.flags = !I2C_M_RD;
msg.addr = client->addr;
msg.len = len;
msg.buf = data;
ret=i2c_transfer(client->adapter, &msg,1);
return ret;
}
static bool gsensor_i2c_test(struct i2c_client * client)
{
int ret, retry;
uint8_t test_data[1] = { 0 }; //only write a data address.
for(retry=0; retry < 2; retry++)
{
ret =i2c_write_bytes(client, test_data, 1); //Test i2c.
if (ret == 1)
break;
msleep(5);
}
return ret==1 ? true : false;
}
#endif
/**
* gsensor_detect - Device detection callback for automatic device creation
* return value:
* = 0; success;
* < 0; err
*/
int gsensor_detect(struct i2c_client *client, struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
if(gsensor_info.twi_id == adapter->nr){
dprintk(DEBUG_INIT, "%s: addr= %x\n",__func__,client->addr);
/*
ret = gsensor_i2c_test(client);
if(!ret){
pr_info("%s:I2C connection might be something wrong or maybe the other gsensor equipment! \n",__func__);
return -ENODEV;
}else
*/
{
dprintk(DEBUG_INIT, "I2C connection sucess!\n");
strlcpy(info->type, SENSOR_NAME, I2C_NAME_SIZE);
return 0;
}
}else{
return -ENODEV;
}
}
static ssize_t mxc622x_delay_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = mxc622x_i2c_client;
struct mxc622x_data_s *mxc622x = NULL;
mxc622x = i2c_get_clientdata(client);
return sprintf(buf, "%d\n", mxc622x->pollDev->poll_interval);
}
static ssize_t mxc622x_delay_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long data;
int error;
struct i2c_client *client = mxc622x_i2c_client;
struct mxc622x_data_s *mxc622x = NULL;
mxc622x = i2c_get_clientdata(client);
dprintk(DEBUG_INIT, "delay store %d\n", __LINE__);
error = strict_strtoul(buf, 10, &data);
if (error)
return error;
if (data > POLL_INTERVAL_MAX)
data = POLL_INTERVAL_MAX;
mutex_lock(&mxc622x->interval_mutex);
mxc622x->pollDev->poll_interval = data;
mutex_unlock(&mxc622x->interval_mutex);
return count;
}
static ssize_t mxc622x_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = mxc622x_i2c_client;
struct mxc622x_data_s *mxc622x = NULL;
mxc622x = i2c_get_clientdata(client);
return sprintf(buf, "%d\n",atomic_read(&mxc622x_data.enable));
}
static ssize_t mxc622x_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long data;
int error;
error = strict_strtoul(buf, 10, &data);
if(error) {
pr_err("%s strict_strtoul error\n", __FUNCTION__);
goto exit;
}
if(data) {
error = i2c_smbus_write_byte_data(mxc622x_i2c_client, MXC622X_REG_CTRL,MXC622X_CTRL_PWRON);
atomic_set(&mxc622x_data.enable, 1);
assert(error==0);
filter_flag = 0;
mxc622x_idev->input->open(mxc622x_idev->input);
} else {
mxc622x_idev->input->close(mxc622x_idev->input);
atomic_set(&mxc622x_data.enable, 0);
error = i2c_smbus_write_byte_data(mxc622x_i2c_client, MXC622X_REG_CTRL,MXC622X_CTRL_PWRDN);
assert(error==0);
}
return count;
exit:
return error;
}
static DEVICE_ATTR(enable, S_IRUSR|S_IRGRP|S_IWUSR|S_IWGRP,
mxc622x_enable_show, mxc622x_enable_store);
static DEVICE_ATTR(delay, S_IRUSR|S_IRGRP|S_IWUSR|S_IWGRP,
mxc622x_delay_show, mxc622x_delay_store);
static struct attribute *mxc622x_attributes[] = {
&dev_attr_enable.attr,
&dev_attr_delay.attr,
NULL
};
static struct attribute_group mxc622x_attribute_group = {
.attrs = mxc622x_attributes
};
static int report_abs(void)
{
s8 xyz[2] = {0x00};
s16 x, y, z = 0;
int ret;
ret = i2c_smbus_write_byte_data(mxc622x_i2c_client, 0x04, 0x02);
assert(ret==0);
memset(&xyz,0,sizeof(xyz));
ret=i2c_smbus_read_i2c_block_data(mxc622x_i2c_client, 0x00, 2,xyz);
if(ret < 2)
{
printk("read data error!\n");
}
x = ((xyz[0] ) << 8) >> 8;
y = ((xyz[1] ) << 8) >> 8;
if ((x == 0) || (y == 0)) {
return 0;
}
/* To filter some wrong data */
if (filter_flag < 3) {
filter_flag++;
return 0;
}
dprintk(DEBUG_DATA_INFO, "x= 0x%hx, y = 0x%hx, z = 0x%hx\n", x, y, z);
input_report_abs(mxc622x_idev->input, ABS_X, x);
input_report_abs(mxc622x_idev->input, ABS_Y, y);
/*input_report_abs(mxc622x_idev->input, ABS_Z, z);*/
input_sync(mxc622x_idev->input);
return 1;
}
static void mxc622x_dev_poll(struct input_polled_dev *dev)
{
#ifdef CONFIG_HAS_EARLYSUSPEND
if(0 == mxc622x_data.suspend_indator){
report_abs();
}
#else
report_abs();
#endif
}
/*
* I2C init/probing/exit functions
*/
static int mxc622x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int result;
struct i2c_adapter *adapter;
struct mxc622x_data_s* data = &mxc622x_data;
dprintk(DEBUG_INIT, "mxc622x probe\n");
mxc622x_i2c_client = client;
adapter = to_i2c_adapter(client->dev.parent);
result = i2c_check_functionality(adapter,
I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA);
assert(result);
hwmon_dev = hwmon_device_register(&client->dev);
assert(!(IS_ERR(hwmon_dev)));
dprintk(DEBUG_INIT, "build time %s %s\n", __DATE__, __TIME__);
/*input poll device register */
mxc622x_idev = input_allocate_polled_device();
if (!mxc622x_idev) {
dev_err(&client->dev, "alloc poll device failed!\n");
result = -ENOMEM;
return result;
}
mxc622x_idev->poll = mxc622x_dev_poll;
mxc622x_idev->poll_interval = POLL_INTERVAL;
mxc622x_idev->poll_interval_max = POLL_INTERVAL_MAX;
mxc622x_idev->input->name = MXC622X_DRV_NAME;
mxc622x_idev->input->id.bustype = BUS_I2C;
mxc622x_idev->input->evbit[0] = BIT_MASK(EV_ABS);
mutex_init(&data->interval_mutex);
input_set_abs_params(mxc622x_idev->input, ABS_X, -128, 128, 0, 0);
input_set_abs_params(mxc622x_idev->input, ABS_Y, -128, 128, 0, 0);
input_set_abs_params(mxc622x_idev->input, ABS_Z, -128, 128, 0, 0);
result = input_register_polled_device(mxc622x_idev);
if (result) {
dev_err(&client->dev, "register poll device failed!\n");
return result;
}
result = sysfs_create_group(&mxc622x_idev->input->dev.kobj, &mxc622x_attribute_group);
if(result) {
dev_err(&client->dev, "create sys failed\n");
}
data->client = client;
data->pollDev = mxc622x_idev;
i2c_set_clientdata(client, data);
#ifdef CONFIG_HAS_EARLYSUSPEND
mxc622x_data.early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
mxc622x_data.early_suspend.suspend = mxc622x_early_suspend;
mxc622x_data.early_suspend.resume = mxc622x_late_resume;
register_early_suspend(&mxc622x_data.early_suspend);
mxc622x_data.suspend_indator = 0;
#endif
return result;
}
static int mxc622x_remove(struct i2c_client *client)
{
int result;
result = i2c_smbus_write_byte_data(mxc622x_i2c_client, MXC622X_REG_CTRL, MXC622X_CTRL_PWRDN);
assert(result==0);
hwmon_device_unregister(hwmon_dev);
#ifdef CONFIG_HAS_EARLYSUSPEND
unregister_early_suspend(&mxc622x_data.early_suspend);
#endif
sysfs_remove_group(&mxc622x_idev->input->dev.kobj, &mxc622x_attribute_group);
input_unregister_polled_device(mxc622x_idev);
input_free_polled_device(mxc622x_idev);
i2c_set_clientdata(mxc622x_i2c_client, NULL);
return result;
}
#ifdef CONFIG_HAS_EARLYSUSPEND
static void mxc622x_early_suspend(struct early_suspend *h)
{
int result;
dprintk(DEBUG_SUSPEND, "mxc6225 early suspend\n");
mxc622x_data.suspend_indator = 1;
result = i2c_smbus_write_byte_data(mxc622x_i2c_client, MXC622X_REG_CTRL, MXC622X_CTRL_PWRDN);
assert(result==0);
return;
}
static void mxc622x_late_resume(struct early_suspend *h)
{
int result;
dprintk(DEBUG_SUSPEND, "mxc6225 late resume\n");
mxc622x_data.suspend_indator = 0;
result = i2c_smbus_write_byte_data(mxc622x_i2c_client, MXC622X_REG_CTRL, MXC622X_CTRL_PWRON);
assert(result==0);
return;
}
#endif /* CONFIG_HAS_EARLYSUSPEND */
static const struct i2c_device_id mxc622x_id[] = {
{ MXC622X_DRV_NAME, 1 },
{ }
};
MODULE_DEVICE_TABLE(i2c, mxc622x_id);
static struct i2c_driver mxc622x_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = MXC622X_DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = "allwinner,mxc622x",
},
.probe = mxc622x_probe,
.remove = mxc622x_remove,
.id_table = mxc622x_id,
.address_list = normal_i2c,
};
static int __init mxc622x_init(void)
{
int ret = -1;
dprintk(DEBUG_INIT, "======%s=========. \n", __func__);
if(input_fetch_sysconfig_para(&(gsensor_info.input_type))){
printk("%s: err.\n", __func__);
return -1;
}else{
ret = input_init_platform_resource(&(gsensor_info.input_type));
if (0 != ret){
printk("%s:ctp_ops.init_platform_resource err. \n", __func__);
}
}
if(gsensor_info.sensor_used == 0){
printk("*** used set to 0 !\n");
printk("*** if use sensor,please put the sys_config.fex gsensor_used set to 1. \n");
return 0;
}
input_set_power_enable(&(gsensor_info.input_type),1);
mxc622x_driver.detect = gsensor_detect;
ret = i2c_add_driver(&mxc622x_driver);
if (ret < 0) {
printk(KERN_INFO "add mxc622x i2c driver failed\n");
return -ENODEV;
}
dprintk(DEBUG_INIT, "add mxc622x i2c driver\n");
return ret;
}
static void __exit mxc622x_exit(void)
{
printk(KERN_INFO "remove mxc622x i2c driver.\n");
i2c_del_driver(&mxc622x_driver);
input_set_power_enable(&(gsensor_info.input_type),0);
}
MODULE_AUTHOR("yin");
MODULE_DESCRIPTION("mxc622x Sensor driver");
MODULE_LICENSE("GPL");
MODULE_VERSION("1.1");
module_init(mxc622x_init);
module_exit(mxc622x_exit);