/* * mma8452.c - Linux kernel modules for 3-Axis Orientation/Motion * Detection Sensor * * Copyright (C) 2009-2010 Freescale Semiconductor Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../init-input.h" //#include //#include //#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 MMA8452_DRV_NAME "mma8452" #define SENSOR_NAME MMA8452_DRV_NAME #define MMA8452_I2C_ADDR 0x1C #define MMA8452_ID 0x2A #define POLL_INTERVAL_MAX 500 #define POLL_INTERVAL 100 #define INPUT_FUZZ 32 #define INPUT_FLAT 32 #define MODE_CHANGE_DELAY_MS 100 #define MMA8452_I2C_ADDR0 0x1C #define MMA8452_I2C_ADDR1 0x1D enum { DEBUG_INIT = 1U << 0, DEBUG_CONTROL_INFO = 1U << 1, DEBUG_DATA_INFO = 1U << 2, DEBUG_SUSPEND = 1U << 3, }; static u32 debug_mask = 0; #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, 0644); /* Addresses to scan */ static const unsigned short normal_i2c[2] = {0x1d,I2C_CLIENT_END}; static const unsigned short i2c_address[2] = {MMA8452_I2C_ADDR0,MMA8452_I2C_ADDR1}; static __u32 twi_id = 0; static int i2c_num = 0; static struct sensor_config_info gsensor_info = { .input_type = GSENSOR_TYPE, }; #ifdef CONFIG_HAS_EARLYSUSPEND struct mma8452_data { struct i2c_client *client; struct early_suspend early_suspend; }; static struct mma8452_data *mma8452_data; #endif /* register enum for mma8452 registers */ enum { MMA8452_STATUS = 0x00, MMA8452_OUT_X_MSB, MMA8452_OUT_X_LSB, MMA8452_OUT_Y_MSB, MMA8452_OUT_Y_LSB, MMA8452_OUT_Z_MSB, MMA8452_OUT_Z_LSB, MMA8452_SYSMOD = 0x0B, MMA8452_INT_SOURCE, MMA8452_WHO_AM_I, MMA8452_XYZ_DATA_CFG, MMA8452_HP_FILTER_CUTOFF, MMA8452_PL_STATUS, MMA8452_PL_CFG, MMA8452_PL_COUNT, MMA8452_PL_BF_ZCOMP, MMA8452_PL_P_L_THS_REG, MMA8452_FF_MT_CFG, MMA8452_FF_MT_SRC, MMA8452_FF_MT_THS, MMA8452_FF_MT_COUNT, MMA8452_TRANSIENT_CFG = 0x1D, MMA8452_TRANSIENT_SRC, MMA8452_TRANSIENT_THS, MMA8452_TRANSIENT_COUNT, MMA8452_PULSE_CFG, MMA8452_PULSE_SRC, MMA8452_PULSE_THSX, MMA8452_PULSE_THSY, MMA8452_PULSE_THSZ, MMA8452_PULSE_TMLT, MMA8452_PULSE_LTCY, MMA8452_PULSE_WIND, MMA8452_ASLP_COUNT, MMA8452_CTRL_REG1, MMA8452_CTRL_REG2, MMA8452_CTRL_REG3, MMA8452_CTRL_REG4, MMA8452_CTRL_REG5, MMA8452_OFF_X, MMA8452_OFF_Y, MMA8452_OFF_Z, MMA8452_REG_END, }; enum { MODE_2G = 0, MODE_4G, MODE_8G, }; /* mma8452 status */ struct mma8452_status { u8 mode; u8 ctl_reg1; }; static struct mma8452_status mma_status = { .mode = 0, .ctl_reg1 = 0 }; static struct input_polled_dev *mma8452_idev; static struct device *hwmon_dev; static struct i2c_client *mma8452_i2c_client; static int enable_id = 0; static struct mutex enable_mutex; static struct mutex init_mutex; static void mma8452_resume_events(struct work_struct *work); static void mma8452_init_events(struct work_struct *work); static struct workqueue_struct *mma8452_resume_wq; static struct workqueue_struct *mma8452_init_wq; static DECLARE_WORK(mma8452_resume_work, mma8452_resume_events); static DECLARE_WORK(mma8452_init_work, mma8452_init_events); /** * gsensor_detect - Device detection callback for automatic device creation * return value: * = 0; success; * < 0; err */ static int gsensor_detect(struct i2c_client *client, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; int ret ; dprintk(DEBUG_INIT, "%s enter \n", __func__); if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; if(twi_id == adapter->nr){ while (i2c_num < 2) { client->addr = i2c_address[i2c_num++]; pr_info("%s: addr= %x\n",__func__,client->addr); ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I); if ((ret&0x00FF) == MMA8452_ID) { printk( "%s: mma8452 equipment is detected!\n",__func__); strlcpy(info->type, SENSOR_NAME, I2C_NAME_SIZE); return 0; } } pr_info("%s: mma8452 equipment is not found!\n",__func__); return -ENODEV; } else { return -ENODEV; } } static ssize_t mma8452_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; } dprintk(DEBUG_CONTROL_INFO, "%s data = %ld \n", __func__, data); if(data) { mutex_lock(&init_mutex); enable_id = 1; mma_status.ctl_reg1 = i2c_smbus_read_byte_data(mma8452_i2c_client, MMA8452_CTRL_REG1); mma_status.ctl_reg1 |= 0x01; error = i2c_smbus_write_byte_data(mma8452_i2c_client, MMA8452_CTRL_REG1, mma_status.ctl_reg1); assert(error==0); mutex_unlock(&init_mutex); if(!error) { mutex_lock(&enable_mutex); if (mma8452_idev->input->users) mma8452_idev->input->open(mma8452_idev->input); mutex_unlock(&enable_mutex); dprintk(DEBUG_CONTROL_INFO, "mma enable setting active \n"); } } else { mutex_lock(&init_mutex); enable_id = 0; mma_status.ctl_reg1 = i2c_smbus_read_byte_data(mma8452_i2c_client, MMA8452_CTRL_REG1); error = i2c_smbus_write_byte_data(mma8452_i2c_client, MMA8452_CTRL_REG1,mma_status.ctl_reg1 & 0xFE); assert(error==0); mutex_unlock(&init_mutex); if(!error) { mutex_lock(&enable_mutex); if (mma8452_idev->input->users) mma8452_idev->input->close(mma8452_idev->input); mutex_unlock(&enable_mutex); dprintk(DEBUG_CONTROL_INFO, "mma enable setting inactive \n"); } } return count; exit: return error; } static ssize_t mma8452_delay_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) return error; if (data > POLL_INTERVAL_MAX) data = POLL_INTERVAL_MAX; mma8452_idev->poll_interval = data; return count; } static DEVICE_ATTR(enable, 0664, NULL, mma8452_enable_store); static DEVICE_ATTR(delay, 0664, NULL, mma8452_delay_store); static struct attribute *mma8452_attributes[] = { &dev_attr_enable.attr, &dev_attr_delay.attr, NULL }; static struct attribute_group mma8452_attribute_group = { .attrs = mma8452_attributes }; /*************************************************************** * * Initialization function */ static int mma8452_init_client(struct i2c_client *client) { int result; dprintk(DEBUG_INIT, "mma8452 device init\n"); mutex_lock(&init_mutex); mma_status.ctl_reg1 = 0x20; result = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1, mma_status.ctl_reg1); assert(result==0); mma_status.mode = MODE_2G; result = i2c_smbus_write_byte_data(client, MMA8452_XYZ_DATA_CFG, mma_status.mode); assert(result==0); mma_status.ctl_reg1 |= 0x01; result = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1, mma_status.ctl_reg1); assert(result==0); mutex_unlock(&init_mutex); msleep(MODE_CHANGE_DELAY_MS); dprintk(DEBUG_INIT, "mma8452 device init end\n"); return result; } /*************************************************************** * * read sensor data from mma8452 * ***************************************************************/ static int mma8452_read_data(short *x, short *y, short *z) { u8 tmp_data[7]; if (i2c_smbus_read_i2c_block_data(mma8452_i2c_client,MMA8452_OUT_X_MSB,7,tmp_data) < 7) { dev_err(&mma8452_i2c_client->dev, "i2c block read failed\n"); return -3; } *x = ((tmp_data[0] << 8) & 0xff00) | tmp_data[1]; *y = ((tmp_data[2] << 8) & 0xff00) | tmp_data[3]; *z = ((tmp_data[4] << 8) & 0xff00) | tmp_data[5]; *x = (short)(*x) >> 4; *y = (short)(*y) >> 4; *z = (short)(*z) >> 4; if (mma_status.mode == MODE_4G){ (*x)=(*x)<<1; (*y)=(*y)<<1; (*z)=(*z)<<1; } else if (mma_status.mode == MODE_8G){ (*x)=(*x)<<2; (*y)=(*y)<<2; (*z)=(*z)<<2; } return 0; } static void report_abs(void) { short x,y,z; int result; result = i2c_smbus_read_byte_data(mma8452_i2c_client, MMA8452_STATUS); if (!(result & 0x08)) { dprintk(DEBUG_DATA_INFO, "mma8452 check new data\n"); return; /* wait for new data */ } if (mma8452_read_data(&x,&y,&z) != 0) { dprintk(DEBUG_DATA_INFO, "mma8452 no data"); return; } dprintk(DEBUG_DATA_INFO, "x= 0x%hx, y = 0x%hx, z = 0x%hx\n", x, y, z); input_report_abs(mma8452_idev->input, ABS_X, x); input_report_abs(mma8452_idev->input, ABS_Y, y); input_report_abs(mma8452_idev->input, ABS_Z, z); input_sync(mma8452_idev->input); } static void mma8452_dev_poll(struct input_polled_dev *dev) { report_abs(); } static void mma8452_resume_events (struct work_struct *work) { int result; mutex_lock(&init_mutex); mma_status.ctl_reg1 = 0x20; result = i2c_smbus_write_byte_data(mma8452_i2c_client, MMA8452_CTRL_REG1, mma_status.ctl_reg1); assert(result==0); mma_status.mode = MODE_2G; result = i2c_smbus_write_byte_data(mma8452_i2c_client, MMA8452_XYZ_DATA_CFG, mma_status.mode); assert(result==0); if (1 == enable_id) { mma_status.ctl_reg1 |= 0x01; result = i2c_smbus_write_byte_data(mma8452_i2c_client, MMA8452_CTRL_REG1, mma_status.ctl_reg1); assert(result==0); } mutex_unlock(&init_mutex); msleep(MODE_CHANGE_DELAY_MS); mutex_lock(&enable_mutex); if (mma8452_idev->input->users) mma8452_idev->input->open(mma8452_idev->input); mutex_unlock(&enable_mutex); } #ifdef CONFIG_HAS_EARLYSUSPEND static void mma8452_early_suspend(struct early_suspend *h) { int result; dprintk(DEBUG_SUSPEND, "mma8452 early suspend"); flush_workqueue(mma8452_resume_wq); mutex_lock(&enable_mutex); if (mma8452_idev->input->users) mma8452_idev->input->close(mma8452_idev->input); mutex_unlock(&enable_mutex); mutex_lock(&init_mutex); mma_status.ctl_reg1 = i2c_smbus_read_byte_data(mma8452_i2c_client, MMA8452_CTRL_REG1); result = i2c_smbus_write_byte_data(mma8452_i2c_client, MMA8452_CTRL_REG1,mma_status.ctl_reg1 & 0xFE); assert(result==0); mutex_unlock(&init_mutex); dprintk(DEBUG_SUSPEND, "mma8452 early suspend end"); return ; } static void mma8452_late_resume(struct early_suspend *h) //(struct i2c_client *client) { int result; dprintk(DEBUG_SUSPEND, "mma8452 late resume"); if (NORMAL_STANDBY == standby_type) { mutex_lock(&init_mutex); if (1 == enable_id) { mma_status.ctl_reg1 = i2c_smbus_read_byte_data(mma8452_i2c_client, MMA8452_CTRL_REG1); mma_status.ctl_reg1 |= 0x01; result = i2c_smbus_write_byte_data(mma8452_i2c_client, MMA8452_CTRL_REG1, mma_status.ctl_reg1); assert(result==0); } mutex_unlock(&init_mutex); mutex_lock(&enable_mutex); if (mma8452_idev->input->users) mma8452_idev->input->open(mma8452_idev->input); mutex_unlock(&enable_mutex); } else if (SUPER_STANDBY == standby_type) { queue_work(mma8452_resume_wq, &mma8452_resume_work); } dprintk(DEBUG_SUSPEND, "mma8452 late resume end"); return ; } #else #ifdef CONFIG_PM static int mma8452_resume(struct i2c_client *client) { int result; dprintk(DEBUG_SUSPEND, "mma8452 resume"); if (NORMAL_STANDBY == standby_type) { mutex_lock(&init_mutex); if (1 == enable_id) { mma_status.ctl_reg1 = i2c_smbus_read_byte_data(mma8452_i2c_client, MMA8452_CTRL_REG1); mma_status.ctl_reg1 |= 0x01; result = i2c_smbus_write_byte_data(mma8452_i2c_client, MMA8452_CTRL_REG1, mma_status.ctl_reg1); assert(result==0); } mutex_unlock(&init_mutex); mutex_lock(&enable_mutex); if (mma8452_idev->input->users) mma8452_idev->input->open(mma8452_idev->input); mutex_unlock(&enable_mutex); } else if (SUPER_STANDBY == standby_type) { queue_work(mma8452_resume_wq, &mma8452_resume_work); } dprintk(DEBUG_SUSPEND, "mma8452 resume end"); return 0; } static int mma8452_suspend(struct i2c_client *client, pm_message_t mesg) { int result; dprintk(DEBUG_SUSPEND, "mma8452 suspend"); flush_workqueue(mma8452_resume_wq); mutex_lock(&enable_mutex); if (mma8452_idev->input->users) mma8452_idev->input->close(mma8452_idev->input); mutex_unlock(&enable_mutex); mutex_lock(&init_mutex); if (1 == enable_id) { mma_status.ctl_reg1 = i2c_smbus_read_byte_data(mma8452_i2c_client, MMA8452_CTRL_REG1); result = i2c_smbus_write_byte_data(mma8452_i2c_client, MMA8452_CTRL_REG1,mma_status.ctl_reg1 & 0xFE); assert(result==0); } mutex_unlock(&init_mutex); dprintk(DEBUG_SUSPEND, "mma8452 suspend end"); return 0; } #endif #endif static void mma8452_init_events (struct work_struct *work) { mma8452_init_client(mma8452_i2c_client); } static int mma8452_probe(struct i2c_client *client, const struct i2c_device_id *id) { int result; struct input_dev *idev; struct i2c_adapter *adapter; dprintk(DEBUG_INIT, "mma8452 probe i2c address is %d \n",i2c_address[i2c_num-1]); client->addr =i2c_address[i2c_num-1]; mma8452_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); mutex_init(&init_mutex); mutex_init(&enable_mutex); mma8452_init_wq = create_singlethread_workqueue("mma8452_init"); if (mma8452_init_wq == NULL) { printk("create mma8452_init_wq fail!\n"); return -ENOMEM; } /* Initialize the MMA8452 chip */ queue_work(mma8452_init_wq, &mma8452_init_work); hwmon_dev = hwmon_device_register(&client->dev); assert(!(IS_ERR(hwmon_dev))); dev_info(&client->dev, "build time %s %s\n", __DATE__, __TIME__); /*input poll device register */ mma8452_idev = input_allocate_polled_device(); if (!mma8452_idev) { dev_err(&client->dev, "alloc poll device failed!\n"); result = -ENOMEM; return result; } mma8452_idev->poll = mma8452_dev_poll; mma8452_idev->poll_interval = POLL_INTERVAL; mma8452_idev->poll_interval_max = POLL_INTERVAL_MAX; idev = mma8452_idev->input; idev->name = MMA8452_DRV_NAME; idev->id.bustype = BUS_I2C; idev->evbit[0] = BIT_MASK(EV_ABS); input_set_abs_params(idev, ABS_X, -8192, 8191, INPUT_FUZZ, INPUT_FLAT); input_set_abs_params(idev, ABS_Y, -8192, 8191, INPUT_FUZZ, INPUT_FLAT); input_set_abs_params(idev, ABS_Z, -8192, 8191, INPUT_FUZZ, INPUT_FLAT); result = input_register_polled_device(mma8452_idev); mma8452_idev->input->close(mma8452_idev->input); if (result) { dev_err(&client->dev, "register poll device failed!\n"); return result; } result = sysfs_create_group(&mma8452_idev->input->dev.kobj, &mma8452_attribute_group); mma8452_resume_wq = create_singlethread_workqueue("mma8452_resume"); if (mma8452_resume_wq == NULL) { printk("create mma8452_resume_wq fail!\n"); return -ENOMEM; } #ifdef CONFIG_HAS_EARLYSUSPEND dprintk(DEBUG_INIT, "==register_early_suspend =\n"); mma8452_data = kzalloc(sizeof(*mma8452_data), GFP_KERNEL); if (mma8452_data == NULL) { result = -ENOMEM; goto err_alloc_data_failed; } mma8452_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 3; mma8452_data->early_suspend.suspend = mma8452_early_suspend; mma8452_data->early_suspend.resume = mma8452_late_resume; register_early_suspend(&mma8452_data->early_suspend); #endif dprintk(DEBUG_INIT, "mma8452 probe end\n"); return result; #ifdef CONFIG_HAS_EARLYSUSPEND err_alloc_data_failed: #endif return result; } static int mma8452_remove(struct i2c_client *client) { int result; mutex_lock(&init_mutex); mma_status.ctl_reg1 = i2c_smbus_read_byte_data(client, MMA8452_CTRL_REG1); result = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1,mma_status.ctl_reg1 & 0xFE); assert(result==0); mutex_unlock(&init_mutex); #ifdef CONFIG_HAS_EARLYSUSPEND unregister_early_suspend(&mma8452_data->early_suspend); #endif cancel_work_sync(&mma8452_resume_work); destroy_workqueue(mma8452_resume_wq); sysfs_remove_group(&mma8452_idev->input->dev.kobj, &mma8452_attribute_group); mma8452_idev->input->close(mma8452_idev->input); input_unregister_polled_device(mma8452_idev); input_free_polled_device(mma8452_idev); hwmon_device_unregister(hwmon_dev); cancel_work_sync(&mma8452_init_work); destroy_workqueue(mma8452_init_wq); i2c_set_clientdata(client, NULL); return result; } static const struct i2c_device_id mma8452_id[] = { { MMA8452_DRV_NAME, 0 }, { } }; MODULE_DEVICE_TABLE(i2c, mma8452_id); static struct i2c_driver mma8452_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = MMA8452_DRV_NAME, .owner = THIS_MODULE, .of_match_table = "allwinner,sun50i-gsensor-para", }, .probe = mma8452_probe, .remove = mma8452_remove, #ifdef CONFIG_HAS_EARLYSUSPEND #else #ifdef CONFIG_PM .suspend = mma8452_suspend, .resume = mma8452_resume, #endif #endif .id_table = mma8452_id, .detect = gsensor_detect, .address_list = normal_i2c, }; static int __init mma8452_init(void) { /* register driver */ int res; dprintk(DEBUG_INIT, "======%s=========. \n", __func__); if (input_fetch_sysconfig_para(&(gsensor_info.input_type))) { printk("%s: err.\n", __func__); return -1; } else{ res = input_init_platform_resource(&(gsensor_info.input_type)); if (0 != res){ printk("%s:ctp_ops.init_platform_resource err. \n", __func__); } } twi_id = gsensor_info.twi_id; dprintk(DEBUG_INIT, "%s i2c twi is %d \n", __func__, twi_id); input_set_power_enable(&(gsensor_info.input_type),1); res = i2c_add_driver(&mma8452_driver); if (res < 0) { printk(KERN_INFO "add mma8452 i2c driver failed\n"); return -ENODEV; } dprintk(DEBUG_INIT, "add mma8452 i2c driver\n"); return (res); } static void __exit mma8452_exit(void) { dprintk(DEBUG_INIT, "remove mma8452 i2c driver.\n"); i2c_del_driver(&mma8452_driver); input_set_power_enable(&(gsensor_info.input_type),0); } MODULE_AUTHOR("Chen Gang "); MODULE_DESCRIPTION("MMA8452 3-Axis Orientation/Motion Detection Sensor driver"); MODULE_LICENSE("GPL"); MODULE_VERSION("1.1"); module_init(mma8452_init); module_exit(mma8452_exit);