/* For AllWinner android platform. * * mir3da.c - Linux kernel modules for 3-Axis Accelerometer * * Copyright (C) 2011-2013 MiraMEMS Sensing Technology Co., Ltd. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mir3da_core.h" #include "mir3da_cust.h" #if (PLATFORM != ALLWINNER_A23) #endif #if (PLATFORM == ALLWINNER_A23 || PLATFORM == ALLWINNER_OTHER) #include "../init-input.h" #endif #ifdef CONFIG_HAS_EARLYSUSPEND #include #endif #if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_PM) #include #endif #define MIR3DA_DRV_NAME "mir3da" #define MIR3DA_INPUT_DEV_NAME MIR3DA_DRV_NAME #define MIR3DA_MISC_NAME MIR3DA_DRV_NAME #define POLL_INTERVAL_MAX 500 #define POLL_INTERVAL 20 #define INPUT_FUZZ 0 #define INPUT_FLAT 0 int i2c_smbus_read(PLAT_HANDLE handle, u8 addr, u8 *data); int i2c_smbus_write(PLAT_HANDLE handle, u8 addr, u8 data); void msdelay(int ms); /*----------------------------------------------------------------------------*/ #ifdef CONFIG_HAS_EARLYSUSPEND struct early_suspend early_suspend; volatile int suspend_indator; #endif static struct sensor_config_info config_info = { .input_type = GSENSOR_TYPE, }; static struct input_polled_dev *mir3da_idev; static struct device *hwmon_dev; static MIR_HANDLE mir_handle; static unsigned int delayMs = 50; static unsigned char twi_id = 0; extern int Log_level; #define MI_DATA(format, ...) if(DEBUG_DATA&Log_level){printk(KERN_ERR MI_TAG format "\n", ## __VA_ARGS__);} #define MI_MSG(format, ...) if(DEBUG_MSG&Log_level){printk(KERN_ERR MI_TAG format "\n", ## __VA_ARGS__);} #define MI_ERR(format, ...) if(DEBUG_ERR&Log_level){printk(KERN_ERR MI_TAG format "\n", ## __VA_ARGS__);} #define MI_FUN if(DEBUG_FUNC&Log_level){printk(KERN_ERR MI_TAG "%s is called, line: %d\n", __FUNCTION__,__LINE__);} #define MI_ASSERT(expr) \ if (!(expr)) {\ printk(KERN_ERR "Assertion failed! %s,%d,%s,%s\n",\ __FILE__, __LINE__, __func__, #expr);\ } /*----------------------------------------------------------------------------*/ #if MIR3DA_OFFSET_TEMP_SOLUTION static char OffsetFileName[] = "/data/misc/miraGSensorOffset.txt"; static char OffsetFolerName[] = "/data/misc/"; #define OFFSET_STRING_LEN 26 struct work_info { char tst1[20]; char tst2[20]; char buffer[OFFSET_STRING_LEN]; struct workqueue_struct *wq; struct delayed_work read_work; struct delayed_work write_work; struct completion completion; int len; int rst; }; static struct work_info m_work_info = {{0}}; /*----------------------------------------------------------------------------*/ static void sensor_write_work( struct work_struct *work ) { struct work_info* pWorkInfo; struct file *filep; u32 orgfs; int ret; orgfs = get_fs(); set_fs(KERNEL_DS); pWorkInfo = container_of((struct delayed_work*)work, struct work_info, write_work); if (pWorkInfo == NULL){ MI_ERR("get pWorkInfo failed!"); return; } filep = filp_open(OffsetFileName, O_RDWR|O_CREAT, 0600); if (IS_ERR(filep)){ MI_ERR("write, sys_open %s error!!.\n", OffsetFileName); ret = -1; } else { filep->f_op->write(filep, pWorkInfo->buffer, pWorkInfo->len, &filep->f_pos); filp_close(filep, NULL); ret = 0; } set_fs(orgfs); pWorkInfo->rst = ret; complete( &pWorkInfo->completion ); } /*----------------------------------------------------------------------------*/ static void sensor_read_work( struct work_struct *work ) { u32 orgfs; struct file *filep; int ret; struct work_info* pWorkInfo; orgfs = get_fs(); set_fs(KERNEL_DS); pWorkInfo = container_of((struct delayed_work*)work, struct work_info, read_work); if (pWorkInfo == NULL){ MI_ERR("get pWorkInfo failed!"); return; } filep = filp_open(OffsetFileName, O_RDONLY, 0600); if (IS_ERR(filep)){ MI_ERR("read, sys_open %s error!!.\n",OffsetFileName); set_fs(orgfs); ret = -1; } else{ filep->f_op->read(filep, pWorkInfo->buffer, sizeof(pWorkInfo->buffer), &filep->f_pos); filp_close(filep, NULL); set_fs(orgfs); ret = 0; } pWorkInfo->rst = ret; complete( &(pWorkInfo->completion) ); } /*----------------------------------------------------------------------------*/ static int sensor_sync_read(u8* offset) { int err; int off[MIR3DA_OFFSET_LEN] = {0}; struct work_info* pWorkInfo = &m_work_info; init_completion( &pWorkInfo->completion ); queue_delayed_work( pWorkInfo->wq, &pWorkInfo->read_work, msecs_to_jiffies(0) ); err = wait_for_completion_timeout( &pWorkInfo->completion, msecs_to_jiffies( 2000 ) ); if ( err == 0 ){ MI_ERR("wait_for_completion_timeout TIMEOUT"); return -1; } if (pWorkInfo->rst != 0){ MI_ERR("work_info.rst not equal 0"); return pWorkInfo->rst; } sscanf(m_work_info.buffer, "%x,%x,%x,%x,%x,%x,%x,%x,%x", &off[0], &off[1], &off[2], &off[3], &off[4], &off[5],&off[6], &off[7], &off[8]); offset[0] = (u8)off[0]; offset[1] = (u8)off[1]; offset[2] = (u8)off[2]; offset[3] = (u8)off[3]; offset[4] = (u8)off[4]; offset[5] = (u8)off[5]; offset[6] = (u8)off[6]; offset[7] = (u8)off[7]; offset[8] = (u8)off[8]; return 0; } /*----------------------------------------------------------------------------*/ static int sensor_sync_write(u8* off) { int err = 0; struct work_info* pWorkInfo = &m_work_info; init_completion( &pWorkInfo->completion ); sprintf(m_work_info.buffer, "%x,%x,%x,%x,%x,%x,%x,%x,%x\n", off[0],off[1],off[2],off[3],off[4],off[5],off[6],off[7],off[8]); pWorkInfo->len = sizeof(m_work_info.buffer); queue_delayed_work( pWorkInfo->wq, &pWorkInfo->write_work, msecs_to_jiffies(0) ); err = wait_for_completion_timeout( &pWorkInfo->completion, msecs_to_jiffies( 2000 ) ); if ( err == 0 ){ MI_ERR("wait_for_completion_timeout TIMEOUT"); return -1; } if (pWorkInfo->rst != 0){ MI_ERR("work_info.rst not equal 0"); return pWorkInfo->rst; } return 0; } /*----------------------------------------------------------------------------*/ static int check_califolder_exist(void) { u32 orgfs = 0; struct file *filep; orgfs = get_fs(); set_fs(KERNEL_DS); filep = filp_open(OffsetFolerName, O_RDONLY, 0600); if (IS_ERR(filep)) { MI_ERR("%s read, sys_open %s error!!.\n",__func__,OffsetFolerName); set_fs(orgfs); return 0; } filp_close(filep, NULL); set_fs(orgfs); return 1; } /*----------------------------------------------------------------------------*/ static int support_fast_auto_cali(void) { #if MIR3DA_SUPPORT_FAST_AUTO_CALI return 1; #else return 0; #endif } #endif /*----------------------------------------------------------------------------*/ static int get_address(PLAT_HANDLE handle) { if(NULL == handle){ MI_ERR("chip init failed !\n"); return -1; } return ((struct i2c_client *)handle)->addr; } /*----------------------------------------------------------------------------*/ static void report_abs(void) { short x=0,y=0,z=0; MIR_HANDLE handle = mir_handle; if (mir3da_read_data(handle, &x,&y,&z) != 0) { MI_ERR("MIR3DA data read failed!\n"); return; } //MI_ERR("===========report abs %d %d %d\n",x,y,z); input_report_abs(mir3da_idev->input, ABS_X, x); input_report_abs(mir3da_idev->input, ABS_Y, y); input_report_abs(mir3da_idev->input, ABS_Z, z); input_sync(mir3da_idev->input); } /*----------------------------------------------------------------------------*/ static void mir3da_dev_poll(struct input_polled_dev *dev) { dev->poll_interval = delayMs; report_abs(); } /*----------------------------------------------------------------------------*/ static long mir3da_misc_ioctl( struct file *file,unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; int err = 0; int interval = 0; char bEnable = 0; int z_dir = 0; int range = 0; short xyz[3] = {0}; MIR_HANDLE handle = mir_handle; if(_IOC_DIR(cmd) & _IOC_READ) { err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); } else if(_IOC_DIR(cmd) & _IOC_WRITE) { err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); } if(err) { return -EFAULT; } switch (cmd) { case MIR3DA_ACC_IOCTL_GET_DELAY: interval = POLL_INTERVAL; if (copy_to_user(argp, &interval, sizeof(interval))) return -EFAULT; break; case MIR3DA_ACC_IOCTL_SET_DELAY: if (copy_from_user(&interval, argp, sizeof(interval))) return -EFAULT; if (interval < 0 || interval > 1000) return -EINVAL; if((interval <=30)&&(interval > 10)) { interval = 10; } delayMs = interval; break; case MIR3DA_ACC_IOCTL_SET_ENABLE: if (copy_from_user(&bEnable, argp, sizeof(bEnable))) return -EFAULT; err = mir3da_set_enable(handle, bEnable); if (err < 0) return EINVAL; break; case MIR3DA_ACC_IOCTL_GET_ENABLE: err = mir3da_get_enable(handle, &bEnable); if (err < 0){ return -EINVAL; } if (copy_to_user(argp, &bEnable, sizeof(bEnable))) return -EINVAL; break; #if MIR3DA_OFFSET_TEMP_SOLUTION case MIR3DA_ACC_IOCTL_CALIBRATION: if(copy_from_user(&z_dir, argp, sizeof(z_dir))) return -EFAULT; if(mir3da_calibrate(handle, z_dir)) { return -EFAULT; } if(copy_to_user(argp, &z_dir, sizeof(z_dir))) return -EFAULT; break; case MIR3DA_ACC_IOCTL_UPDATE_OFFSET: manual_load_cali_file(handle); break; #endif case MIR3DA_ACC_IOCTL_GET_COOR_XYZ: if(mir3da_read_data(handle, &xyz[0],&xyz[1],&xyz[2])) return -EFAULT; if(copy_to_user((void __user *)arg, xyz, sizeof(xyz))) return -EFAULT; break; default: return -EINVAL; } return 0; } /*----------------------------------------------------------------------------*/ static const struct file_operations mir3da_misc_fops = { .owner = THIS_MODULE, .unlocked_ioctl = mir3da_misc_ioctl, }; static struct miscdevice misc_mir3da = { .minor = MISC_DYNAMIC_MINOR, .name = MIR3DA_MISC_NAME, .fops = &mir3da_misc_fops, }; /*----------------------------------------------------------------------------*/ static ssize_t mir3da_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret; char bEnable; MIR_HANDLE handle = mir_handle; ret = mir3da_get_enable(handle, &bEnable); if (ret < 0){ ret = -EINVAL; } else{ ret = sprintf(buf, "%d\n", bEnable); } return ret; } /*----------------------------------------------------------------------------*/ static ssize_t mir3da_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int ret; char bEnable; unsigned long enable; MIR_HANDLE handle = mir_handle; if (buf == NULL){ return -1; } enable = simple_strtoul(buf, NULL, 10); bEnable = (enable > 0) ? 1 : 0; if(bEnable){ input_set_power_enable(&(config_info.input_type),1); mdelay(5); bLoad = FILE_CHECKING; }else{ input_set_power_enable(&(config_info.input_type),0); } if(bEnable) mir3da_idev->input->open(mir3da_idev->input); else mir3da_idev->input->close(mir3da_idev->input); // MI_ERR("============enable store 0 | %d\n",bEnable); ret = mir3da_set_enable(handle, bEnable); if (ret < 0){ ret = -EINVAL; } else{ ret = count; } //MI_ERR("===============enable store 1"); return ret; } /*----------------------------------------------------------------------------*/ static ssize_t mir3da_delay_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%u\n", delayMs); } /*----------------------------------------------------------------------------*/ static ssize_t mir3da_delay_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int interval = 0; interval = simple_strtoul(buf, NULL, 10); if (interval < 0 || interval > 1000) return -EINVAL; if ((interval <=30)&&(interval > 10)){ interval = 10; } delayMs = interval; return count; } /*----------------------------------------------------------------------------*/ static ssize_t mir3da_axis_data_show(struct device *dev, struct device_attribute *attr, char *buf) { int result; short x,y,z; int count = 0; MIR_HANDLE handle = mir_handle; result = mir3da_read_data(handle, &x, &y, &z); if (result == 0) count += sprintf(buf+count, "x= %d;y=%d;z=%d\n", x,y,z); else count += sprintf(buf+count, "reading failed!"); return count; } /*----------------------------------------------------------------------------*/ static ssize_t mir3da_reg_data_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int addr, data; int result; MIR_HANDLE handle = mir_handle; sscanf(buf, "0x%x, 0x%x\n", &addr, &data); result = mir3da_register_write(handle, addr, data); MI_ASSERT(result==0); return count; } /*----------------------------------------------------------------------------*/ static ssize_t mir3da_reg_data_show(struct device *dev, struct device_attribute *attr, char *buf) { MIR_HANDLE handle = mir_handle; return mir3da_get_reg_data(handle, buf); } /*----------------------------------------------------------------------------*/ static ssize_t mir3da_offset_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t count = 0; if(bLoad==FILE_EXIST) count += sprintf(buf,"%s",m_work_info.buffer); else count += sprintf(buf,"%s","Calibration file not exist!\n"); return count; } /*----------------------------------------------------------------------------*/ #if FILTER_AVERAGE_ENHANCE static ssize_t mir3da_average_enhance_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret = 0; struct mir3da_filter_param_s param = {0}; ret = mir3da_get_filter_param(¶m); ret |= sprintf(buf, "%d %d %d\n", param.filter_param_l, param.filter_param_h, param.filter_threhold); return ret; } /*----------------------------------------------------------------------------*/ static ssize_t mir3da_average_enhance_store(struct device *dev,struct device_attribute *attr,const char *buf, size_t count) { int ret = 0; struct mir3da_filter_param_s param = {0}; sscanf(buf, "%d %d %d\n", ¶m.filter_param_l, ¶m.filter_param_h, ¶m.filter_threhold); ret = mir3da_set_filter_param(¶m); return count; } #endif /*----------------------------------------------------------------------------*/ #if MIR3DA_OFFSET_TEMP_SOLUTION int bCaliResult = -1; static ssize_t mir3da_calibrate_show(struct device *dev,struct device_attribute *attr,char *buf) { int ret; ret = sprintf(buf, "%d\n", bCaliResult); return ret; } /*----------------------------------------------------------------------------*/ static ssize_t mir3da_calibrate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { s8 z_dir = 0; MIR_HANDLE handle = mir_handle; z_dir = simple_strtol(buf, NULL, 10); bCaliResult = mir3da_calibrate(handle, z_dir); return count; } #endif /*----------------------------------------------------------------------------*/ static ssize_t mir3da_log_level_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret; ret = sprintf(buf, "%d\n", Log_level); return ret; } /*----------------------------------------------------------------------------*/ static ssize_t mir3da_log_level_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { Log_level = simple_strtoul(buf, NULL, 10); return count; } /*----------------------------------------------------------------------------*/ static ssize_t mir3da_primary_offset_show(struct device *dev, struct device_attribute *attr, char *buf){ MIR_HANDLE handle = mir_handle; int x=0,y=0,z=0; mir3da_get_primary_offset(handle,&x,&y,&z); return sprintf(buf, "x=%d ,y=%d ,z=%d\n",x,y,z); } /*----------------------------------------------------------------------------*/ static ssize_t mir3da_version_show(struct device *dev, struct device_attribute *attr, char *buf){ return sprintf(buf, "%s_%s\n", DRI_VER, CORE_VER); } /*----------------------------------------------------------------------------*/ static ssize_t mir3da_vendor_show(struct device *dev, struct device_attribute *attr, char *buf){ return sprintf(buf, "%s\n", "MiraMEMS"); } /*----------------------------------------------------------------------------*/ static DEVICE_ATTR(enable, S_IRUGO , mir3da_enable_show, mir3da_enable_store); static DEVICE_ATTR(delay, S_IRUGO , mir3da_delay_show, mir3da_delay_store); static DEVICE_ATTR(axis_data, S_IRUGO , mir3da_axis_data_show, NULL); static DEVICE_ATTR(reg_data, S_IRUGO, mir3da_reg_data_show, mir3da_reg_data_store); static DEVICE_ATTR(log_level, S_IRUGO, mir3da_log_level_show, mir3da_log_level_store); #if MIR3DA_OFFSET_TEMP_SOLUTION static DEVICE_ATTR(offset, S_IRUGO, mir3da_offset_show, NULL); static DEVICE_ATTR(calibrate_miraGSensor, S_IRUGO, mir3da_calibrate_show, mir3da_calibrate_store); #endif #if FILTER_AVERAGE_ENHANCE static DEVICE_ATTR(average_enhance, S_IRUGO, mir3da_average_enhance_show, mir3da_average_enhance_store); #endif static DEVICE_ATTR(primary_offset, S_IRUGO, mir3da_primary_offset_show, NULL); static DEVICE_ATTR(version, S_IRUGO, mir3da_version_show, NULL); static DEVICE_ATTR(vendor, S_IRUGO, mir3da_vendor_show, NULL); /*----------------------------------------------------------------------------*/ static struct attribute *mir3da_attributes[] = { &dev_attr_enable.attr, &dev_attr_delay.attr, &dev_attr_axis_data.attr, &dev_attr_reg_data.attr, &dev_attr_log_level.attr, #if MIR3DA_OFFSET_TEMP_SOLUTION &dev_attr_offset.attr, &dev_attr_calibrate_miraGSensor.attr, #endif #if FILTER_AVERAGE_ENHANCE &dev_attr_average_enhance.attr, #endif /* ! FILTER_AVERAGE_ENHANCE */ &dev_attr_primary_offset.attr, &dev_attr_version.attr, &dev_attr_vendor.attr, NULL }; static const struct attribute_group mir3da_attr_group = { .attrs = mir3da_attributes, }; /*----------------------------------------------------------------------------*/ int i2c_smbus_read(PLAT_HANDLE handle, u8 addr, u8 *data) { int res = 0; struct i2c_client *client = (struct i2c_client*)handle; *data = i2c_smbus_read_byte_data(client, addr); return res; } /*----------------------------------------------------------------------------*/ int i2c_smbus_read_block(PLAT_HANDLE handle, u8 addr, u8 count, u8 *data) { int res = 0; struct i2c_client *client = (struct i2c_client*)handle; res = i2c_smbus_read_i2c_block_data(client, addr, count, data); return res; } /*----------------------------------------------------------------------------*/ int i2c_smbus_write(PLAT_HANDLE handle, u8 addr, u8 data) { int res = 0; struct i2c_client *client = (struct i2c_client*)handle; res = i2c_smbus_write_byte_data(client, addr, data); return res; } /*----------------------------------------------------------------------------*/ void msdelay(int ms) { mdelay(ms); } #if MIR3DA_OFFSET_TEMP_SOLUTION MIR_GENERAL_OPS_DECLARE(ops_handle, i2c_smbus_read, i2c_smbus_read_block, i2c_smbus_write, sensor_sync_write, sensor_sync_read, check_califolder_exist,get_address,support_fast_auto_cali,msdelay, printk, sprintf); #else MIR_GENERAL_OPS_DECLARE(ops_handle, i2c_smbus_read, i2c_smbus_read_block, i2c_smbus_write, NULL, NULL, NULL,get_address,NULL,msdelay, printk, sprintf); #endif /*----------------------------------------------------------------------------*/ static int mir3da_probe(struct i2c_client *client, const struct i2c_device_id *id) { int result; struct input_dev *idev; if(mir3da_install_general_ops(&ops_handle)){ MI_ERR("Install ops failed !\n"); goto err_detach_client; } #if MIR3DA_OFFSET_TEMP_SOLUTION m_work_info.wq = create_singlethread_workqueue( "oo" ); if(NULL==m_work_info.wq) { MI_ERR("Failed to create workqueue !"); goto err_detach_client; } INIT_DELAYED_WORK( &m_work_info.read_work, sensor_read_work ); INIT_DELAYED_WORK( &m_work_info.write_work, sensor_write_work ); #endif /* Initialize the MIR3DA chip */ mir_handle = mir3da_core_init((PLAT_HANDLE)client); if(NULL == mir_handle){ MI_ERR("chip init failed !\n"); goto err_detach_client; } hwmon_dev = hwmon_device_register(&client->dev); MI_ASSERT(!(IS_ERR(hwmon_dev))); /* input poll device register */ mir3da_idev = input_allocate_polled_device(); if (!mir3da_idev) { MI_ERR("alloc poll device failed!\n"); result = -ENOMEM; goto err_hwmon_device_unregister; } mir3da_idev->poll = mir3da_dev_poll; mir3da_idev->poll_interval = POLL_INTERVAL; delayMs = POLL_INTERVAL; mir3da_idev->poll_interval_max = POLL_INTERVAL_MAX; idev = mir3da_idev->input; idev->name = MIR3DA_INPUT_DEV_NAME; idev->id.bustype = BUS_I2C; idev->evbit[0] = BIT_MASK(EV_ABS); input_set_abs_params(idev, ABS_X, -16384, 16383, INPUT_FUZZ, INPUT_FLAT); input_set_abs_params(idev, ABS_Y, -16384, 16383, INPUT_FUZZ, INPUT_FLAT); input_set_abs_params(idev, ABS_Z, -16384, 16383, INPUT_FUZZ, INPUT_FLAT); result = input_register_polled_device(mir3da_idev); if (result) { MI_ERR("register poll device failed!\n"); goto err_free_polled_device; } /* Sys Attribute Register */ result = sysfs_create_group(&idev->dev.kobj, &mir3da_attr_group); if (result) { MI_ERR("create device file failed!\n"); result = -EINVAL; goto err_unregister_polled_device; } /* Misc device interface Register */ result = misc_register(&misc_mir3da); if (result) { MI_ERR("%s: mir3da_dev register failed", __func__); goto err_remove_sysfs_group; } #ifdef CONFIG_HAS_EARLYSUSPEND early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; early_suspend.suspend = mir3da_early_suspend; early_suspend.resume = mir3da_late_resume; register_early_suspend(&early_suspend); suspend_indator=0; #endif return result; err_remove_sysfs_group: sysfs_remove_group(&idev->dev.kobj, &mir3da_attr_group); err_unregister_polled_device: input_unregister_polled_device(mir3da_idev); err_free_polled_device: input_free_polled_device(mir3da_idev); err_hwmon_device_unregister: hwmon_device_unregister(&client->dev); err_detach_client: return result; } /*----------------------------------------------------------------------------*/ static int mir3da_remove(struct i2c_client *client) { MIR_HANDLE handle = mir_handle; mir3da_set_enable(handle, 0); misc_deregister(&misc_mir3da); sysfs_remove_group(&mir3da_idev->input->dev.kobj, &mir3da_attr_group); input_unregister_polled_device(mir3da_idev); input_free_polled_device(mir3da_idev); #ifdef CONFIG_HAS_EARLYSUSPEND unregister_early_suspend(&early_suspend); #endif flush_workqueue(m_work_info.wq); destroy_workqueue(m_work_info.wq); hwmon_device_unregister(hwmon_dev); return 0; } /*----------------------------------------------------------------------------*/ #ifdef CONFIG_HAS_EARLYSUSPEND static void mir3da_early_suspend(struct early_suspend *h) { int result = 0; MIR_HANDLE handle = mir_handle; MI_FUN; // MI_ERR("=================early suspend 0"); result = mir3da_set_enable(handle, 0); if(result) { MI_ERR("%s:set disable fail!!\n",__func__); return; } suspend_indator = 1; // MI_ERR("=================early suspend 1"); return result; } /*----------------------------------------------------------------------------*/ static void mir3da_late_resume(struct early_suspend *h) { int result = 0; MIR_HANDLE handle = mir_handle; MI_FUN; // MI_ERR("=================late resume 0"); result = mir3da_chip_resume(handle); if(result) { MI_ERR("%s:chip resume fail!!\n",__func__); return; } result = mir3da_set_enable(handle, 1); if(result) { MI_ERR("%s:set enable fail!!\n",__func__); return; } suspend_indator = 0; // MI_ERR("=================late resume 1"); return result; } #else static int mir3da_suspend(struct device *dev, pm_message_t mesg) { int result = 0; struct i2c_client *client = to_i2c_client(dev); MIR_HANDLE handle = mir_handle; MI_FUN; // MI_ERR("==========================mir3da_suspend 0"); result = mir3da_set_enable(handle, 0); if(result) { MI_ERR("%s:set disable fail!!\n",__func__); return; } // MI_ERR("==========================mir3da_suspend 1"); mir3da_idev->input->close(mir3da_idev->input); return result; } /*----------------------------------------------------------------------------*/ static int mir3da_resume(struct device *dev) { int result = 0; struct i2c_client *client = to_i2c_client(dev); MIR_HANDLE handle = mir_handle; MI_FUN; // MI_ERR("==========================mir3da_resume 0"); result = mir3da_chip_resume(handle); if(result) { MI_ERR("%s:chip resume fail!!\n",__func__); return; } result = mir3da_set_enable(handle, 1); if(result) { MI_ERR("%s:set enable fail!!\n",__func__); return; } // MI_ERR("==========================mir3da_resume 1"); mir3da_idev->input->open(mir3da_idev->input); return result; } #endif /*----------------------------------------------------------------------------*/ static int mir3da_detect(struct i2c_client *new_client, struct i2c_board_info *info) { struct i2c_adapter *adapter = new_client->adapter; MI_MSG("%s:bus[%d] addr[0x%x]\n",__func__, adapter->nr,new_client->addr); if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; if(twi_id == adapter->nr){ if(mir3da_install_general_ops(&ops_handle)){ MI_ERR("Install ops failed !\n"); return -ENODEV; } if(mir3da_module_detect((PLAT_HANDLE)new_client)) { MI_ERR("Can't find Mir3da gsensor!!"); }else{ MI_ERR("'Find Mir3da gsensor!!"); strlcpy(info->type, MIR3DA_DRV_NAME, I2C_NAME_SIZE); return 0; } } return -ENODEV; } /*----------------------------------------------------------------------------*/ #if (PLATFORM == ALLWINNER_A13) static union{ unsigned short dirty_addr_buf[2]; const unsigned short normal_i2c[2]; }u_i2c_addr = {{0x00},}; static int gsensor_fetch_sysconfig_para(void) { int ret = -1; int device_used = -1; __u32 twi_addr = 0; char name[I2C_NAME_SIZE]; script_parser_value_type_t type = SCIRPT_PARSER_VALUE_TYPE_STRING; MI_FUN; if(SCRIPT_PARSER_OK != (ret = script_parser_fetch("gsensor", "gsensor_used", &device_used, 1))){ MI_ERR("%s: gsensor not used [%d] !!\n",__func__,device_used); goto script_parser_fetch_err; } if(1 == device_used){ if(SCRIPT_PARSER_OK != script_parser_fetch_ex("gsensor", "gsensor_name", (int *)(&name), &type, sizeof(name)/sizeof(int))){ MI_ERR("%s: gsensor has no type !! \n",__func__); goto script_parser_fetch_err; } if(strcmp(MIR3DA_DRV_NAME, name)){ MI_ERR("%s: name %s does not match %s!!\n", __func__, name,MIR3DA_DRV_NAME); goto script_parser_fetch_err; } if(SCRIPT_PARSER_OK != script_parser_fetch("gsensor", "gsensor_twi_addr", &twi_addr, sizeof(twi_addr)/sizeof(__u32))){ MI_ERR("%s: gsensor has no i2c addr [%x] \n",__func__,twi_addr); goto script_parser_fetch_err; } u_i2c_addr.dirty_addr_buf[0] = twi_addr; u_i2c_addr.dirty_addr_buf[1] = I2C_CLIENT_END; if(SCRIPT_PARSER_OK != script_parser_fetch("gsensor", "gsensor_twi_id", &twi_id, 1)){ MI_ERR("%s: gsensor has no i2c bus [%d] \n",__func__,twi_id); goto script_parser_fetch_err; } MI_MSG("%s:used=%d name=%s addr=%x bus=%d\n",device_used,name,twi_addr,twi_id); ret = 0; }else{ ret = -1; } return ret; script_parser_fetch_err: MI_ERR("script_parser_fetch_err!!\n"); return ret; } /*----------------------------------------------------------------------------*/ #elif(PLATFORM == ALLWINNER_A23) static const unsigned short normal_i2c[] = { 0x27, 0x26,I2C_CLIENT_END }; static struct sensor_config_info config_info = { .input_type = GSENSOR_TYPE, }; static int gsensor_fetch_sysconfig_para(void) { if (input_sensor_startup(&(config_info.input_type))) { printk("%s: err.\n", __func__); return -1; } else twi_id = config_info.twi_id; MI_MSG("%s:i2c_bus[%d]\n",__func__,twi_id); return 0; } #elif(PLATFORM == ALLWINNER_A13_42) static const unsigned short normal_i2c[] = { 0x27, 0x26,I2C_CLIENT_END }; static int gsensor_fetch_sysconfig_para(void) { int ret = -1; int device_used = -1; MI_FUN; if(SCRIPT_PARSER_OK != (ret = script_parser_fetch("gsensor", "gsensor_used", &device_used, 1))){ printk("%s: gsensor not used [%d] !!\n",__func__,device_used); goto script_parser_fetch_err; } printk("device_used = %d \n",device_used); if(1 == device_used){ if(SCRIPT_PARSER_OK != script_parser_fetch("gsensor", "gsensor_twi_id", &twi_id, 1)){ printk("%s: gsensor has no i2c bus [%d] \n",__func__,twi_id); goto script_parser_fetch_err; } ret = 0; }else{ ret = -1; } return ret; script_parser_fetch_err: printk("script_parser_fetch_err!!\n"); return ret; } #else static const unsigned short normal_i2c[] = { 0x27, 0x26,I2C_CLIENT_END }; static int gsensor_fetch_sysconfig_para(void) { int ret; if (input_sensor_startup(&(config_info.input_type))){ printk("%s: err.\n", __func__); return -1; }else{ ret = input_sensor_init(&(config_info.input_type)); if (0 != ret){ printk("%s:ctp_ops.init_platform_resource err. \n", __func__); } } twi_id = config_info.twi_id; MI_MSG("%s:i2c_bus[%d]\n",__func__,twi_id); return ret; script_get_err: MI_ERR("script_parser_fetch_err!!\n"); return ret; } #endif /*----------------------------------------------------------------------------*/ static const struct i2c_device_id mir3da_id[] = { { MIR3DA_DRV_NAME, 0 }, { } }; MODULE_DEVICE_TABLE(i2c, mir3da_id); /*----------------------------------------------------------------------------*/ static struct i2c_driver mir3da_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = MIR3DA_DRV_NAME, .owner = THIS_MODULE, #ifndef CONFIG_HAS_EARLYSUSPEND .suspend = mir3da_suspend, .resume = mir3da_resume, #endif }, .probe = mir3da_probe, .detect = mir3da_detect, .remove = mir3da_remove, .id_table = mir3da_id, #if (PLATFORM == ALLWINNER_A13) .address_list = u_i2c_addr.normal_i2c, #else .address_list = normal_i2c, #endif }; /*----------------------------------------------------------------------------*/ static int __init mir3da_init(void) { int res; MI_FUN; if(gsensor_fetch_sysconfig_para()){ MI_ERR("fetch_sysconfig_para failed!\n"); return -1; } input_set_power_enable(&(config_info.input_type),1); res = i2c_add_driver(&mir3da_driver); if (res < 0){ MI_ERR("add mir3da i2c driver failed\n"); return -ENODEV; } return (res); } /*----------------------------------------------------------------------------*/ static void __exit mir3da_exit(void) { MI_FUN; i2c_del_driver(&mir3da_driver); input_sensor_free(&(config_info.input_type)); } /*----------------------------------------------------------------------------*/ MODULE_AUTHOR("MiraMEMS "); MODULE_DESCRIPTION("MIR3DA 3-Axis Accelerometer driver"); MODULE_LICENSE("GPL"); MODULE_VERSION("1.0"); module_init(mir3da_init); module_exit(mir3da_exit);