/* * mxc6225.c - Linux kernel modules for Detection Sensor */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../init-input.h" //#include //#include #undef CONFIG_HAS_EARLYSUSPEND #undef CONFIG_PM #ifdef CONFIG_HAS_EARLYSUSPEND #include #endif #if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_PM) #include #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);