summaryrefslogtreecommitdiff
path: root/drivers/input/sensor/mxc622x_gsensor/mxc622x.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/sensor/mxc622x_gsensor/mxc622x.c')
-rwxr-xr-xdrivers/input/sensor/mxc622x_gsensor/mxc622x.c1151
1 files changed, 1151 insertions, 0 deletions
diff --git a/drivers/input/sensor/mxc622x_gsensor/mxc622x.c b/drivers/input/sensor/mxc622x_gsensor/mxc622x.c
new file mode 100755
index 00000000..9c94b6ed
--- /dev/null
+++ b/drivers/input/sensor/mxc622x_gsensor/mxc622x.c
@@ -0,0 +1,1151 @@
+/******************** (C) COPYRIGHT 2010 STMicroelectronics ********************
+ *
+ * File Name : mxc622x_acc.c
+ * Description : MXC622X accelerometer sensor API
+ *
+ *******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THE PRESENT SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
+ * OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, FOR THE SOLE
+ * PURPOSE TO SUPPORT YOUR APPLICATION DEVELOPMENT.
+ * AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
+ * CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
+ * INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+ *
+ * THIS SOFTWARE IS SPECIFICALLY DESIGNED FOR EXCLUSIVE USE WITH ST PARTS.
+ *
+
+ ******************************************************************************/
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+
+#include <linux/workqueue.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#include <linux/platform_device.h>
+
+#include "mxc622x.h"
+#include "../sensor.h"
+#ifdef CONFIG_ARCH_SC8810
+#include <mach/eic.h>
+#endif
+
+#define G_MAX 16000 /** Maximum polled-device-reported g value */
+#define WHOAMI_MXC622X_ACC 0x25 /* Expctd content for WAI */
+
+/* CONTROL REGISTERS */
+#define WHO_AM_I 0x08 /* WhoAmI register */
+
+#define FUZZ 32
+#define FLAT 32
+#define I2C_RETRY_DELAY 5
+#define I2C_RETRIES 5
+#define I2C_AUTO_INCREMENT 0x80
+
+/* RESUME STATE INDICES */
+
+#define RESUME_ENTRIES 20
+#define DEVICE_INFO "Memsic, MXC622X"
+#define DEVICE_INFO_LEN 32
+
+/* end RESUME STATE INDICES */
+
+#define DEBUG
+//#define MXC622X_DEBUG
+
+#define MAX_INTERVAL 50
+
+#ifdef __KERNEL__
+static struct mxc622x_acc_platform_data mxc622x_plat_data = {
+ .poll_interval = 20,
+ .min_interval = 10,
+};
+#endif
+
+#ifdef I2C_BUS_NUM_STATIC_ALLOC
+static struct i2c_board_info mxc622x_i2c_boardinfo = {
+ I2C_BOARD_INFO(MXC622X_ACC_I2C_NAME, MXC622X_ACC_I2C_ADDR),
+#ifdef __KERNEL__
+ .platform_data = &mxc622x_plat_data
+#endif
+};
+#endif
+
+struct mxc622x_acc_data {
+ struct i2c_client *client;
+ struct mxc622x_acc_platform_data *pdata;
+
+ struct mutex lock;
+ struct delayed_work input_work;
+
+ struct input_dev *input_dev;
+
+ int hw_initialized;
+ /* hw_working=-1 means not tested yet */
+ int hw_working;
+ atomic_t enabled;
+ int on_before_suspend;
+
+ u8 resume_state[RESUME_ENTRIES];
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+};
+
+/*
+ * Because misc devices can not carry a pointer from driver register to
+ * open, we keep this global. This limits the driver to a single instance.
+ */
+struct mxc622x_acc_data *mxc622x_acc_misc_data;
+struct i2c_client *mxc622x_i2c_client = NULL;
+static struct class* l_dev_class = NULL;
+struct i2c_client *this_client = NULL;
+//////////////////////////////////////////////////////
+struct mx622x_sensordata{
+ // for control
+ int int_gpio; //0-3
+ int op;
+ int samp;
+ int xyz_axis[3][3]; // (axis,direction)
+ struct proc_dir_entry* sensor_proc;
+ struct input_dev *input_dev;
+ //struct work_struct work;
+ struct delayed_work work; // for polling
+ struct workqueue_struct *queue;
+ int isdbg;
+ int sensor_samp; //
+ int sensor_enable; // 0 --> disable sensor, 1 --> enable sensor
+ int test_pass;
+ //int offset[3];
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend earlysuspend;
+#endif
+ s16 offset[3+1]; /*+1: for 4-byte alignment*/
+
+
+};
+
+static struct mx622x_sensordata l_sensorconfig = {
+ .op = 0,
+ .int_gpio = 3,
+ .samp = 16,
+ .xyz_axis = {
+ {ABS_X, -1},
+ {ABS_Y, 1},
+ {ABS_Z, -1},
+ },
+ .sensor_proc = NULL,
+ .isdbg = 0,
+ .sensor_samp = 1, // 1 sample/second
+ .sensor_enable = 1, // enable sensor
+ .test_pass = 0, // for test program
+ //.offset={0,0,0},
+};
+
+//////////////////////////////////////////////////////
+
+
+static int mxc622x_acc_i2c_read(struct mxc622x_acc_data *acc, u8 * buf, int len)
+{
+ int err;
+ int tries = 0;
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = acc->client->addr,
+ .flags = acc->client->flags & I2C_M_TEN,
+ .len = 1,
+ .buf = buf, },
+ {
+ .addr = acc->client->addr,
+ .flags = (acc->client->flags & I2C_M_TEN) | I2C_M_RD,
+ .len = len,
+ .buf = buf, },
+ };
+
+ do {
+ err = i2c_transfer(acc->client->adapter, msgs, 2);
+ if (err != 2)
+ msleep_interruptible(I2C_RETRY_DELAY);
+ } while ((err != 2) && (++tries < I2C_RETRIES));
+
+ if (err != 2) {
+ dev_err(&acc->client->dev, "read transfer error\n");
+ err = -EIO;
+ } else {
+ err = 0;
+ }
+
+ return err;
+}
+
+static int mxc622x_acc_i2c_write(struct mxc622x_acc_data *acc, u8 * buf, int len)
+{
+ int err;
+ int tries = 0;
+
+ struct i2c_msg msgs[] = { { .addr = acc->client->addr,
+ .flags = acc->client->flags & I2C_M_TEN,
+ .len = len + 1, .buf = buf, }, };
+ do {
+ err = i2c_transfer(acc->client->adapter, msgs, 1);
+ if (err != 1)
+ msleep_interruptible(I2C_RETRY_DELAY);
+ } while ((err != 1) && (++tries < I2C_RETRIES));
+
+ if (err != 1) {
+ dev_err(&acc->client->dev, "write transfer error\n");
+ err = -EIO;
+ } else {
+ err = 0;
+ }
+
+ return err;
+}
+
+static int mxc622x_acc_hw_init(struct mxc622x_acc_data *acc)
+{
+ int err = -1;
+ u8 buf[7];
+
+ printk(KERN_INFO "%s: hw init start\n", MXC622X_ACC_DEV_NAME);
+
+ buf[0] = WHO_AM_I;
+ err = mxc622x_acc_i2c_read(acc, buf, 1);
+ if (err < 0)
+ goto error_firstread;
+ else
+ acc->hw_working = 1;
+ if ((buf[0] & 0x3F) != WHOAMI_MXC622X_ACC) {
+ err = -1; /* choose the right coded error */
+ goto error_unknown_device;
+ }
+
+ acc->hw_initialized = 1;
+ printk(KERN_INFO "%s: hw init done\n", MXC622X_ACC_DEV_NAME);
+ return 0;
+
+error_firstread:
+ acc->hw_working = 0;
+ dev_warn(&acc->client->dev, "Error reading WHO_AM_I: is device "
+ "available/working?\n");
+ goto error1;
+error_unknown_device:
+ dev_err(&acc->client->dev,
+ "device unknown. Expected: 0x%x,"
+ " Replies: 0x%x\n", WHOAMI_MXC622X_ACC, buf[0]);
+error1:
+ acc->hw_initialized = 0;
+ dev_err(&acc->client->dev, "hw init error 0x%x,0x%x: %d\n", buf[0],
+ buf[1], err);
+ return err;
+}
+
+static void mxc622x_acc_device_power_off(struct mxc622x_acc_data *acc)
+{
+ int err;
+ u8 buf[2] = { MXC622X_REG_CTRL, MXC622X_CTRL_PWRDN };
+
+ err = mxc622x_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ dev_err(&acc->client->dev, "soft power off failed: %d\n", err);
+}
+
+static int mxc622x_acc_device_power_on(struct mxc622x_acc_data *acc)
+{
+ int err = -1;
+ u8 buf[2] = { MXC622X_REG_CTRL, MXC622X_CTRL_PWRON };
+
+ err = mxc622x_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ dev_err(&acc->client->dev, "soft power on failed: %d\n", err);
+
+ if (!acc->hw_initialized) {
+ err = mxc622x_acc_hw_init(acc);
+ if (acc->hw_working == 1 && err < 0) {
+ mxc622x_acc_device_power_off(acc);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+
+/* */
+
+static int mxc622x_acc_register_write(struct mxc622x_acc_data *acc, u8 *buf,
+ u8 reg_address, u8 new_value)
+{
+ int err = -1;
+
+ if (atomic_read(&acc->enabled)) {
+ /* Sets configuration register at reg_address
+ * NOTE: this is a straight overwrite */
+ buf[0] = reg_address;
+ buf[1] = new_value;
+ err = mxc622x_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ return err;
+ }
+ return err;
+}
+
+static int mxc622x_acc_register_read(struct mxc622x_acc_data *acc, u8 *buf,
+ u8 reg_address)
+{
+
+ int err = -1;
+ buf[0] = (reg_address);
+ err = mxc622x_acc_i2c_read(acc, buf, 1);
+ return err;
+}
+
+static int mxc622x_acc_register_update(struct mxc622x_acc_data *acc, u8 *buf,
+ u8 reg_address, u8 mask, u8 new_bit_values)
+{
+ int err = -1;
+ u8 init_val;
+ u8 updated_val;
+ err = mxc622x_acc_register_read(acc, buf, reg_address);
+ if (!(err < 0)) {
+ init_val = buf[1];
+ updated_val = ((mask & new_bit_values) | ((~mask) & init_val));
+ err = mxc622x_acc_register_write(acc, buf, reg_address,
+ updated_val);
+ }
+ return err;
+}
+
+/* */
+
+static int mxc622x_acc_get_acceleration_data(struct mxc622x_acc_data *acc,
+ int *xyz)
+{
+ int err = -1;
+ /* Data bytes from hardware x, y */
+ u8 acc_data[2];
+
+ acc_data[0] = MXC622X_REG_DATA;
+ err = mxc622x_acc_i2c_read(acc, acc_data, 2);
+
+ if (err < 0)
+ {
+ #ifdef DEBUG
+ printk(KERN_INFO "%s I2C read error %d\n", MXC622X_ACC_I2C_NAME, err);
+ #endif
+ return err;
+ }
+
+ xyz[0] = (signed char)acc_data[0];
+ xyz[1] = (signed char)acc_data[1];
+ xyz[2] = 8; //32;
+
+ #ifdef MXC622X_DEBUG
+ printk("x = %d, y = %d\n", xyz[0], xyz[1]);
+ #endif
+
+ #ifdef MXC622X_DEBUG
+
+ printk(KERN_INFO "%s read x=%d, y=%d, z=%d\n",
+ MXC622X_ACC_DEV_NAME, xyz[0], xyz[1], xyz[2]);
+ printk(KERN_INFO "%s poll interval %d\n", MXC622X_ACC_DEV_NAME, acc->pdata->poll_interval);
+
+ #endif
+ return err;
+}
+
+static void mxc622x_acc_report_values(struct mxc622x_acc_data *acc, int *xyz)
+{
+ int txyz,tx,ty,tz = 0;
+ static int suf=0; // To report every merging value
+
+ tx = xyz[l_sensorconfig.xyz_axis[0][0]]*l_sensorconfig.xyz_axis[0][1];
+ ty = xyz[l_sensorconfig.xyz_axis[1][0]]*l_sensorconfig.xyz_axis[1][1];
+ tz = xyz[l_sensorconfig.xyz_axis[2][0]]*l_sensorconfig.xyz_axis[2][1];
+ suf++;
+ if (suf > 5)
+ {
+ suf = 0;
+ }
+
+ // packet the x,y,z
+ txyz = (tx&0x00FF) | ((ty&0xFF)<<8) | ((tz&0xFF)<<16) | ((suf&0xFF)<<24);
+ input_report_abs(acc->input_dev, ABS_X, txyz);
+ /*input_report_abs(acc->input_dev, ABS_Y, xyz[1]);
+ input_report_abs(acc->input_dev, ABS_Z, xyz[2]);*/
+ input_sync(acc->input_dev);
+}
+
+static int mxc622x_acc_enable(struct mxc622x_acc_data *acc)
+{
+ int err;
+
+ if (!atomic_cmpxchg(&acc->enabled, 0, 1)) {
+ err = mxc622x_acc_device_power_on(acc);
+ if (err < 0) {
+ atomic_set(&acc->enabled, 0);
+ return err;
+ }
+
+ schedule_delayed_work(&acc->input_work, msecs_to_jiffies(
+ acc->pdata->poll_interval));
+ }
+
+ return 0;
+}
+
+static int mxc622x_acc_disable(struct mxc622x_acc_data *acc)
+{
+ if (atomic_cmpxchg(&acc->enabled, 1, 0)) {
+ cancel_delayed_work_sync(&acc->input_work);
+ mxc622x_acc_device_power_off(acc);
+ }
+
+ return 0;
+}
+
+static int mxc622x_acc_misc_open(struct inode *inode, struct file *file)
+{
+ int err;
+ err = nonseekable_open(inode, file);
+ if (err < 0)
+ return err;
+
+ file->private_data = mxc622x_acc_misc_data;
+
+ return 0;
+}
+
+static /*int*/long mxc622x_acc_misc_ioctl(/*struct inode *inode, */struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ //u8 buf[4];
+ //u8 mask;
+ //u8 reg_address;
+ //u8 bit_values;
+ int err;
+ int interval;
+ int xyz[3] = {0};
+ struct mxc622x_acc_data *acc = file->private_data;
+ unsigned int uval = 0;
+
+// printk(KERN_INFO "%s: %s call with cmd 0x%x and arg 0x%x\n",
+// MXC622X_ACC_DEV_NAME, __func__, cmd, (unsigned int)arg);
+
+ switch (cmd) {
+ case MXC622X_ACC_IOCTL_GET_DELAY:
+ interval = acc->pdata->poll_interval;
+ if (copy_to_user(argp, &interval, sizeof(interval)))
+ return -EFAULT;
+ break;
+
+ //case MXC622X_ACC_IOCTL_SET_DELAY:
+ case ECS_IOCTL_APP_SET_DELAY:
+ if (copy_from_user(&interval, argp, sizeof(interval)))
+ return -EFAULT;
+ if (interval < 0 || interval > 1000)
+ return -EINVAL;
+ //if(interval > MAX_INTERVAL)
+ //interval = MAX_INTERVAL;
+ acc->pdata->poll_interval = max(interval,
+ acc->pdata->min_interval);
+ break;
+
+ //case MXC622X_ACC_IOCTL_SET_ENABLE:
+ case ECS_IOCTL_APP_SET_AFLAG:
+ if (copy_from_user(&interval, argp, sizeof(interval)))
+ return -EFAULT;
+ if (interval > 1)
+ return -EINVAL;
+ if (interval)
+ err = mxc622x_acc_enable(acc);
+ else
+ err = mxc622x_acc_disable(acc);
+ return err;
+ break;
+
+ case MXC622X_ACC_IOCTL_GET_ENABLE:
+ interval = atomic_read(&acc->enabled);
+ if (copy_to_user(argp, &interval, sizeof(interval)))
+ return -EINVAL;
+ break;
+ case MXC622X_ACC_IOCTL_GET_COOR_XYZ:
+ err = mxc622x_acc_get_acceleration_data(acc, xyz);
+ if (err < 0)
+ return err;
+ #ifdef DEBUG
+ // printk(KERN_ALERT "%s Get coordinate xyz:[%d, %d, %d]\n",
+ // __func__, xyz[0], xyz[1], xyz[2]);
+ #endif
+ if (copy_to_user(argp, xyz, sizeof(xyz))) {
+ printk(KERN_ERR " %s %d error in copy_to_user \n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ break;
+ case MXC622X_ACC_IOCTL_GET_CHIP_ID:
+ {
+ u8 devid = 0;
+ u8 devinfo[DEVICE_INFO_LEN] = {0};
+ err = mxc622x_acc_register_read(acc, &devid, WHO_AM_I);
+ if (err < 0) {
+ printk("%s, error read register WHO_AM_I\n", __func__);
+ return -EAGAIN;
+ }
+ sprintf(devinfo, "%s, %#x", DEVICE_INFO, devid);
+
+ if (copy_to_user(argp, devinfo, sizeof(devinfo))) {
+ printk("%s error in copy_to_user(IOCTL_GET_CHIP_ID)\n", __func__);
+ return -EINVAL;
+ }
+ }
+ break;
+ case WMT_IOCTL_SENSOR_GET_DRVID:
+ uval = MXC622X_DRVID;
+ if (copy_to_user((unsigned int*)arg, &uval, sizeof(unsigned int)))
+ {
+ return -EFAULT;
+ }
+ dbg("mxc622x_driver_id:%d\n",uval);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mxc622x_acc_misc_close(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+
+static const struct file_operations mxc622x_acc_misc_fops = {
+ .owner = THIS_MODULE,
+ .open = mxc622x_acc_misc_open,
+ .unlocked_ioctl = mxc622x_acc_misc_ioctl,
+ .release = mxc622x_acc_misc_close,
+};
+
+static struct miscdevice mxc622x_acc_misc_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = GSENSOR_DEV_NODE,//MXC622X_ACC_DEV_NAME,
+ .fops = &mxc622x_acc_misc_fops,
+};
+
+static void mxc622x_acc_input_work_func(struct work_struct *work)
+{
+ struct mxc622x_acc_data *acc;
+
+ int xyz[3] = { 0 };
+ int err;
+
+ acc = mxc622x_acc_misc_data; /*container_of((struct delayed_work *)work,
+ struct mxc622x_acc_data, input_work); */
+
+ mutex_lock(&acc->lock);
+ err = mxc622x_acc_get_acceleration_data(acc, xyz);
+ if (err < 0)
+ dev_err(&acc->client->dev, "get_acceleration_data failed\n");
+ else
+ mxc622x_acc_report_values(acc, xyz);
+
+ schedule_delayed_work(&acc->input_work, msecs_to_jiffies(
+ acc->pdata->poll_interval));
+ mutex_unlock(&acc->lock);
+}
+
+#ifdef MXC622X_OPEN_ENABLE
+int mxc622x_acc_input_open(struct input_dev *input)
+{
+ struct mxc622x_acc_data *acc = input_get_drvdata(input);
+
+ return mxc622x_acc_enable(acc);
+}
+
+void mxc622x_acc_input_close(struct input_dev *dev)
+{
+ struct mxc622x_acc_data *acc = input_get_drvdata(dev);
+
+ mxc622x_acc_disable(acc);
+}
+#endif
+
+static int mxc622x_acc_validate_pdata(struct mxc622x_acc_data *acc)
+{
+ acc->pdata->poll_interval = max(acc->pdata->poll_interval,
+ acc->pdata->min_interval);
+
+ /* Enforce minimum polling interval */
+ if (acc->pdata->poll_interval < acc->pdata->min_interval) {
+ dev_err(&acc->client->dev, "minimum poll interval violated\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mxc622x_acc_input_init(struct mxc622x_acc_data *acc)
+{
+ int err;
+ // Polling rx data when the interrupt is not used.
+ if (1/*acc->irq1 == 0 && acc->irq1 == 0*/) {
+ INIT_DELAYED_WORK(&acc->input_work, mxc622x_acc_input_work_func);
+ }
+
+ acc->input_dev = input_allocate_device();
+ if (!acc->input_dev) {
+ err = -ENOMEM;
+ dev_err(&acc->client->dev, "input device allocate failed\n");
+ goto err0;
+ }
+
+#ifdef MXC622X_ACC_OPEN_ENABLE
+ acc->input_dev->open = mxc622x_acc_input_open;
+ acc->input_dev->close = mxc622x_acc_input_close;
+#endif
+
+ input_set_drvdata(acc->input_dev, acc);
+
+ set_bit(EV_ABS, acc->input_dev->evbit);
+
+ input_set_abs_params(acc->input_dev, ABS_X, -G_MAX, G_MAX, FUZZ, FLAT);
+ input_set_abs_params(acc->input_dev, ABS_Y, -G_MAX, G_MAX, FUZZ, FLAT);
+ input_set_abs_params(acc->input_dev, ABS_Z, -G_MAX, G_MAX, FUZZ, FLAT);
+
+ acc->input_dev->name = GSENSOR_INPUT_NAME;//MXC622X_ACC_INPUT_NAME;
+
+ err = input_register_device(acc->input_dev);
+ if (err) {
+ dev_err(&acc->client->dev,
+ "unable to register input polled device %s\n",
+ acc->input_dev->name);
+ goto err1;
+ }
+
+ return 0;
+
+err1:
+ input_free_device(acc->input_dev);
+err0:
+ return err;
+}
+
+static void mxc622x_acc_input_cleanup(struct mxc622x_acc_data *acc)
+{
+ input_unregister_device(acc->input_dev);
+ input_free_device(acc->input_dev);
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void mxc622x_early_suspend (struct early_suspend* es);
+static void mxc622x_early_resume (struct early_suspend* es);
+#endif
+
+static int is_mxc622x(struct i2c_client *client)
+{
+ int tempvalue;
+
+ /* read chip id */
+ tempvalue = i2c_smbus_read_word_data(client, WHO_AM_I);
+ if ((tempvalue & 0x003F) == WHOAMI_MXC622X_ACC) {
+ //printk(KERN_INFO "%s I2C driver registered!\n",
+ // MXC622X_ACC_DEV_NAME);
+ return 1;
+ }
+ return 0;
+}
+
+static int mxc622x_acc_probe(struct i2c_client *client)
+{
+
+ struct mxc622x_acc_data *acc;
+
+ int err = -1;
+ int tempvalue;
+
+ pr_info("%s: probe start.\n", MXC622X_ACC_DEV_NAME);
+
+ /*if (client->dev.platform_data == NULL) {
+ dev_err(&client->dev, "platform data is NULL. exiting.\n");
+ err = -ENODEV;
+ goto exit_check_functionality_failed;
+ }*/
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "client not i2c capable\n");
+ err = -ENODEV;
+ goto exit_check_functionality_failed;
+ }
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_err(&client->dev, "client not smb-i2c capable:2\n");
+ err = -EIO;
+ goto exit_check_functionality_failed;
+ }
+
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_I2C_BLOCK)){
+ dev_err(&client->dev, "client not smb-i2c capable:3\n");
+ err = -EIO;
+ goto exit_check_functionality_failed;
+ }
+ /*
+ * OK. From now, we presume we have a valid client. We now create the
+ * client structure, even though we cannot fill it completely yet.
+ */
+
+ acc = kzalloc(sizeof(struct mxc622x_acc_data), GFP_KERNEL);
+ if (acc == NULL) {
+ err = -ENOMEM;
+ dev_err(&client->dev,
+ "failed to allocate memory for module data: "
+ "%d\n", err);
+ goto exit_alloc_data_failed;
+ }
+
+ mutex_init(&acc->lock);
+ mutex_lock(&acc->lock);
+
+ acc->client = client;
+ mxc622x_i2c_client = client;
+ i2c_set_clientdata(client, acc);
+
+ /* read chip id */
+ tempvalue = i2c_smbus_read_word_data(client, WHO_AM_I);
+
+ if ((tempvalue & 0x003F) == WHOAMI_MXC622X_ACC) {
+ printk(KERN_INFO "%s I2C driver registered!\n",
+ MXC622X_ACC_DEV_NAME);
+ } else {
+ acc->client = NULL;
+ printk(KERN_INFO "I2C driver not registered!"
+ " Device unknown 0x%x\n", tempvalue);
+ goto err_mutexunlockfreedata;
+ }
+ acc->pdata = kzalloc(sizeof(struct mxc622x_acc_platform_data), GFP_KERNEL);
+ if (acc->pdata == NULL) {
+ err = -ENOMEM;
+ dev_err(&client->dev,
+ "failed to allocate memory for pdata: %d\n",
+ err);
+ goto exit_kfree_pdata;
+ }
+
+ //memcpy(acc->pdata, client->dev.platform_data, sizeof(*acc->pdata));
+ acc->pdata->poll_interval = 20;
+ acc->pdata->min_interval = 10;
+
+ err = mxc622x_acc_validate_pdata(acc);
+ if (err < 0) {
+ dev_err(&client->dev, "failed to validate platform data\n");
+ goto exit_kfree_pdata;
+ }
+
+ i2c_set_clientdata(client, acc);
+
+
+ /*if (acc->pdata->init) {
+ err = acc->pdata->init();
+ if (err < 0) {
+ dev_err(&client->dev, "init failed: %d\n", err);
+ goto err2;
+ }
+ }*/
+
+ err = mxc622x_acc_device_power_on(acc);
+ if (err < 0) {
+ dev_err(&client->dev, "power on failed: %d\n", err);
+ goto err2;
+ }
+
+ atomic_set(&acc->enabled, 1);
+
+ err = mxc622x_acc_input_init(acc);
+ if (err < 0) {
+ dev_err(&client->dev, "input init failed\n");
+ goto err_power_off;
+ }
+ mxc622x_acc_misc_data = acc;
+
+ err = misc_register(&mxc622x_acc_misc_device);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "misc MXC622X_ACC_DEV_NAME register failed\n");
+ goto err_input_cleanup;
+ }
+
+ mxc622x_acc_device_power_off(acc);
+
+ /* As default, do not report information */
+ atomic_set(&acc->enabled, 0);
+
+ acc->on_before_suspend = 0;
+
+ #ifdef CONFIG_HAS_EARLYSUSPEND
+ acc->early_suspend.suspend = mxc622x_early_suspend;
+ acc->early_suspend.resume = mxc622x_early_resume;
+ acc->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;
+ register_early_suspend(&acc->early_suspend);
+#endif
+
+ mutex_unlock(&acc->lock);
+
+ dev_info(&client->dev, "%s: probed\n", MXC622X_ACC_DEV_NAME);
+
+ return 0;
+
+err_input_cleanup:
+ mxc622x_acc_input_cleanup(acc);
+err_power_off:
+ mxc622x_acc_device_power_off(acc);
+err2:
+ if (acc->pdata->exit) acc->pdata->exit();
+exit_kfree_pdata:
+ kfree(acc->pdata);
+err_mutexunlockfreedata:
+ kfree(acc);
+ mutex_unlock(&acc->lock);
+ i2c_set_clientdata(client, NULL);
+ mxc622x_acc_misc_data = NULL;
+exit_alloc_data_failed:
+exit_check_functionality_failed:
+ printk(KERN_ERR "%s: Driver Init failed\n", MXC622X_ACC_DEV_NAME);
+ return err;
+}
+
+static int __devexit mxc622x_acc_remove(struct i2c_client *client)
+{
+ /* TODO: revisit ordering here once _probe order is finalized */
+ struct mxc622x_acc_data *acc = mxc622x_acc_misc_data;//i2c_get_clientdata(client);
+
+ misc_deregister(&mxc622x_acc_misc_device);
+ mxc622x_acc_input_cleanup(acc);
+ mxc622x_acc_device_power_off(acc);
+ if (acc->pdata->exit)
+ acc->pdata->exit();
+ kfree(acc->pdata);
+ kfree(acc);
+
+ return 0;
+}
+
+static int mxc622x_acc_resume(struct platform_device *pdev)
+{
+ struct mxc622x_acc_data *acc = mxc622x_acc_misc_data;
+
+ if (acc != NULL && acc->on_before_suspend) {
+ acc->on_before_suspend = 0;
+ acc->hw_initialized = 0;
+ return mxc622x_acc_enable(acc);
+ }
+ return 0;
+}
+
+static int mxc622x_acc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct mxc622x_acc_data *acc = mxc622x_acc_misc_data;
+ if (acc != NULL) {
+ if (atomic_read(&acc->enabled)) {
+ acc->on_before_suspend = 1;
+ return mxc622x_acc_disable(acc);
+ }
+ }
+ return 0;
+}
+
+static int mxc622x_probe(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static int mxc622x_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver mxc622x_driver = {
+ .probe = mxc622x_probe,
+ .remove = mxc622x_remove,
+ .suspend = mxc622x_acc_suspend,
+ .resume = mxc622x_acc_resume,
+ .driver = {
+ .name = GSENSOR_I2C_NAME,
+ },
+};
+
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+
+static void mxc622x_early_suspend (struct early_suspend* es)
+{
+ struct mxc622x_acc_data *acc = mxc622x_acc_misc_data; //i2c_get_clientdata(client);
+#ifdef MXC622X_DEBUG
+ printk("%s.\n", __func__);
+#endif
+ if (acc != NULL) {
+ if (atomic_read(&acc->enabled)) {
+ acc->on_before_suspend = 1;
+ return mxc622x_acc_disable(acc);
+ }
+ }
+}
+
+static void mxc622x_early_resume (struct early_suspend* es)
+{
+ struct mxc622x_acc_data *acc = mxc622x_acc_misc_data; //i2c_get_clientdata(client);
+#ifdef MXC622X_DEBUG
+ printk("%s.\n", __func__);
+#endif
+
+ if (acc != NULL && acc->on_before_suspend) {
+ acc->on_before_suspend = 0;
+ acc->hw_initialized = 0;
+ return mxc622x_acc_enable(acc);
+ }
+
+}
+
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+
+static const struct i2c_device_id mxc622x_acc_id[]
+ = { { MXC622X_ACC_DEV_NAME, 0 }, { }, };
+
+MODULE_DEVICE_TABLE(i2c, mxc622x_acc_id);
+
+#if 0
+static struct i2c_driver mxc622x_acc_driver = {
+ .driver = {
+ .name = MXC622X_ACC_I2C_NAME,
+ },
+ .probe = mxc622x_acc_probe,
+ .remove = __devexit_p(mxc622x_acc_remove),
+ .resume = mxc622x_acc_resume,
+ .suspend = mxc622x_acc_suspend,
+ .id_table = mxc622x_acc_id,
+};
+#endif
+
+#ifdef I2C_BUS_NUM_STATIC_ALLOC
+
+int i2c_static_add_device(struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+ int err;
+
+ adapter = i2c_get_adapter(I2C_STATIC_BUS_NUM);
+ if (!adapter) {
+ pr_err("%s: can't get i2c adapter\n", __FUNCTION__);
+ err = -ENODEV;
+ goto i2c_err;
+ }
+
+ client = i2c_new_device(adapter, info);
+ if (!client) {
+ pr_err("%s: can't add i2c device at 0x%x\n",
+ __FUNCTION__, (unsigned int)info->addr);
+ err = -ENODEV;
+ goto i2c_err;
+ }
+
+ i2c_put_adapter(adapter);
+
+ return 0;
+
+i2c_err:
+ return err;
+}
+
+#endif /*I2C_BUS_NUM_STATIC_ALLOC*/
+
+static void mxc622x_platform_release(struct device *device)
+{
+ dbg("...\n");
+ return;
+}
+
+
+static struct platform_device mxc622x_device = {
+ .name = GSENSOR_I2C_NAME,
+ .id = 0,
+ .dev = {
+ .release = mxc622x_platform_release,
+ },
+};
+
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+static int get_axisset(void* param)
+{
+ char varbuf[64];
+ int n;
+ int varlen;
+
+ int tmp_offset[3] = {0};
+ memset(varbuf, 0, sizeof(varbuf));
+ varlen = sizeof(varbuf);
+ if (wmt_getsyspara("wmt.io.mxc622xsensor", varbuf, &varlen)) {
+ errlog("Can't get gsensor config in u-boot!!!!\n");
+ return -1;
+ } else {
+ n = sscanf(varbuf, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d",
+ &l_sensorconfig.op,
+ &l_sensorconfig.int_gpio,
+ &l_sensorconfig.samp,
+ &(l_sensorconfig.xyz_axis[0][0]),
+ &(l_sensorconfig.xyz_axis[0][1]),
+ &(l_sensorconfig.xyz_axis[1][0]),
+ &(l_sensorconfig.xyz_axis[1][1]),
+ &(l_sensorconfig.xyz_axis[2][0]),
+ &(l_sensorconfig.xyz_axis[2][1]),
+ &tmp_offset[0],
+ &tmp_offset[1],
+ &tmp_offset[2]
+ );
+ if (n != 12) {
+ printk(KERN_ERR "gsensor format is error in u-boot!!!\n");
+ return -1;
+ }
+ l_sensorconfig.offset[0] = tmp_offset[0];
+ l_sensorconfig.offset[1] = tmp_offset[1];
+ l_sensorconfig.offset[2] = tmp_offset[2];
+ l_sensorconfig.sensor_samp = l_sensorconfig.samp;
+
+ dbg("get the sensor config: %d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d\n",
+ l_sensorconfig.op,
+ l_sensorconfig.int_gpio,
+ l_sensorconfig.samp,
+ l_sensorconfig.xyz_axis[0][0],
+ l_sensorconfig.xyz_axis[0][1],
+ l_sensorconfig.xyz_axis[1][0],
+ l_sensorconfig.xyz_axis[1][1],
+ l_sensorconfig.xyz_axis[2][0],
+ l_sensorconfig.xyz_axis[2][1],
+ l_sensorconfig.offset[0],
+ l_sensorconfig.offset[1],
+ l_sensorconfig.offset[2]
+ );
+ }
+ return 0;
+}
+
+
+static int __init mxc622x_acc_init(void)
+{
+ int ret = 0;
+
+ printk(KERN_INFO "%s accelerometer driver: init\n",
+ MXC622X_ACC_I2C_NAME);
+ ret = get_axisset(NULL);
+ if (ret < 0)
+ {
+ printk("<<<<<%s user choose to no sensor chip!\n", __func__);
+ return ret;
+ }
+
+ if (!(this_client = sensor_i2c_register_device(0, GSENSOR_I2C_ADDR, GSENSOR_I2C_NAME)))
+ {
+ printk(KERN_EMERG"Can't register gsensor i2c device!\n");
+ return -1;
+ }
+ if(!is_mxc622x(this_client))
+ {
+ dbg("isn't mxc622x gsensor!\n");
+ return -1;
+ }
+ // parse g-sensor u-boot arg
+
+ /*if (ret)
+ {
+ errlog("only for test!\n");
+ return -1;
+ }*/
+#ifdef I2C_BUS_NUM_STATIC_ALLOC
+ ret = i2c_static_add_device(&mxc622x_i2c_boardinfo);
+ if (ret < 0) {
+ pr_err("%s: add i2c device error %d\n", __FUNCTION__, ret);
+ goto init_err;
+ }
+#endif
+ ret = mxc622x_acc_probe(this_client);
+ if (ret)
+ {
+ sensor_i2c_unregister_device(this_client);
+ return -1;
+ }
+ // create the platform device
+ l_dev_class = class_create(THIS_MODULE, GSENSOR_I2C_NAME);
+ if (IS_ERR(l_dev_class)){
+ ret = PTR_ERR(l_dev_class);
+ printk(KERN_ERR "Can't class_create gsensor device !!\n");
+ return ret;
+ }
+ if((ret = platform_device_register(&mxc622x_device)))
+ {
+ klog("Can't register mc3230 platform devcie!!!\n");
+ return ret;
+ }
+ if ((ret = platform_driver_register(&mxc622x_driver)) != 0)
+ {
+ errlog("Can't register mc3230 platform driver!!!\n");
+ return ret;
+ }
+
+ //return i2c_add_driver(&mxc622x_acc_driver);
+
+init_err:
+ return ret;
+}
+
+static void __exit mxc622x_acc_exit(void)
+{
+ //printk(KERN_INFO "%s accelerometer driver exit\n", MXC622X_ACC_DEV_NAME);
+
+ platform_driver_unregister(&mxc622x_driver);
+ platform_device_unregister(&mxc622x_device);
+ class_destroy(l_dev_class);
+ mxc622x_acc_remove(mxc622x_i2c_client);
+ sensor_i2c_unregister_device(this_client);
+ #ifdef I2C_BUS_NUM_STATIC_ALLOC
+ i2c_unregister_device(mxc622x_i2c_client);
+ #endif
+
+ //i2c_del_driver(&mxc622x_acc_driver);
+ return;
+}
+
+module_init(mxc622x_acc_init);
+module_exit(mxc622x_acc_exit);
+
+MODULE_DESCRIPTION("mxc622x accelerometer misc driver");
+MODULE_AUTHOR("Memsic");
+MODULE_LICENSE("GPL");
+