495 lines
12 KiB
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);
|
|
|