aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/mpu3050/mpu-dev.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/mpu3050/mpu-dev.c')
-rwxr-xr-xdrivers/misc/mpu3050/mpu-dev.c2280
1 files changed, 2280 insertions, 0 deletions
diff --git a/drivers/misc/mpu3050/mpu-dev.c b/drivers/misc/mpu3050/mpu-dev.c
new file mode 100755
index 00000000000..ef04ed7a96c
--- /dev/null
+++ b/drivers/misc/mpu3050/mpu-dev.c
@@ -0,0 +1,2280 @@
+/*
+ mpu-dev.c - mpu3050 char device interface
+
+ Copyright (C) 1995-97 Simon G. Vogl
+ Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl>
+ Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
+ Copyright (C) 2010 InvenSense Corporation, All Rights Reserved.
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+/* Code inside mpudev_ioctl_rdrw is copied from i2c-dev.c
+ */
+
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/stat.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/signal.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/pm.h>
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include "mpuirq.h"
+#include "slaveirq.h"
+#include "mlsl.h"
+#include "mlos.h"
+#include "mpu-i2c.h"
+#include "mldl_cfg.h"
+#include "mpu-accel.h"
+
+#include "mpu.h"
+
+#define ACCEL_VENDOR_NAME "KIONIX"
+#define ACCEL_CHIP_NAME "KXTF9"
+
+#define GYRO_VENDOR_NAME "INVENSENSE"
+#define GYRO_CHIP_NAME "MPU-3050"
+
+#define MPU3050_EARLY_SUSPEND_IN_DRIVER 1
+
+#define CALIBRATION_FILE_PATH "/efs/calibration_data"
+#define CALIBRATION_DATA_AMOUNT 100
+
+struct acc_data cal_data;
+
+/* Platform data for the MPU */
+struct mpu_private_data {
+ struct mldl_cfg mldl_cfg;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+};
+
+static int is_lis3dh;
+
+#define IDEAL_X 0
+#define IDEAL_Y 0
+#define IDEAL_Z 1024
+
+static int pid;
+
+static struct i2c_client *this_client;
+
+int read_accel_raw_xyz(struct acc_data *acc)
+{
+ unsigned char acc_data[6];
+ s32 temp;
+ struct mldl_cfg *mldl_cfg;
+
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(this_client);
+
+ if (!mpu) {
+ pr_info("%s : mpu data is NULL, mpu3050 Init error", __func__);
+ return 0;
+ }
+
+ mldl_cfg = &mpu->mldl_cfg;
+
+ if (mldl_cfg->accel_is_suspended == 1 ||
+ (mldl_cfg->dmp_is_running == 0
+ && mldl_cfg->accel_is_suspended == 0)) {
+ if (is_lis3dh) {
+ if (mldl_cfg->accel_is_suspended == 1) {
+ sensor_i2c_write_register(this_client->adapter,
+ 0x19, 0x20, 0x67);
+ MLOSSleep(1);
+ }
+ sensor_i2c_read(this_client->adapter,
+ 0x19, 0x28 | 0x80, 6, acc_data);
+ if (mldl_cfg->accel_is_suspended == 1) {
+ sensor_i2c_write_register(this_client->adapter,
+ 0x19, 0x20, 0x18);
+ MLOSSleep(1);
+ }
+ } else
+ sensor_i2c_read(this_client->adapter,
+ 0x0F, 0x06, 6, acc_data);
+ } else if (mldl_cfg->dmp_is_running &&
+ mldl_cfg->accel_is_suspended == 0) {
+ if (sensor_i2c_read(this_client->adapter,
+ DEFAULT_MPU_SLAVEADDR,
+ 0x23, 6, acc_data) != 0)
+ return -1;
+ } else
+ return -1;
+
+ if (is_lis3dh) {
+ acc->x = ((acc_data[0] << 8) | acc_data[1]);
+ acc->x = (acc->x >> 4);
+ acc->y = ((acc_data[2] << 8) | acc_data[3]);
+ acc->y = (acc->y >> 4);
+ acc->z = ((acc_data[4] << 8) | acc_data[5]);
+ acc->z = (acc->z >> 4);
+ } else {
+ temp = ((acc_data[1] << 4) | (acc_data[0] >> 4));
+ if (temp < 2048)
+ acc->x = (s16) (-temp);
+ else
+ acc->x = (s16) (4096 - temp);
+
+ temp = ((acc_data[3] << 4) | (acc_data[2] >> 4));
+ if (temp < 2048)
+ acc->y = (s16) (-temp);
+ else
+ acc->y = (s16) (4096 - temp);
+
+ temp = ((acc_data[5] << 4) | (acc_data[4] >> 4));
+ if (temp < 2048)
+ acc->z = (s16) (1024 - temp);
+ else
+ acc->z = (s16) (3072 - temp);
+ }
+ return 0;
+}
+
+static int accel_open_calibration(void)
+{
+ struct file *cal_filp = NULL;
+ int err = 0;
+ mm_segment_t old_fs;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ cal_filp = filp_open(CALIBRATION_FILE_PATH, O_RDONLY, 0666);
+ if (IS_ERR(cal_filp)) {
+ pr_err("%s: Can't open calibration file\n", __func__);
+ set_fs(old_fs);
+ err = PTR_ERR(cal_filp);
+
+ cal_data.x = 0;
+ cal_data.y = 0;
+ cal_data.z = 0;
+
+ return err;
+ }
+
+ err = cal_filp->f_op->read(cal_filp,
+ (char *)&cal_data, 3 * sizeof(s16),
+ &cal_filp->f_pos);
+ if (err != 3 * sizeof(s16)) {
+ pr_err("%s: Can't read the cal data from file\n", __func__);
+ err = -EIO;
+ }
+
+ pr_info("%s : (%u,%u,%u)\n", __func__,
+ cal_data.x, cal_data.y, cal_data.z);
+
+ filp_close(cal_filp, current->files);
+ set_fs(old_fs);
+
+ return err;
+}
+
+static int accel_do_calibrate(bool do_calib)
+{
+ struct acc_data data = { 0, };
+ struct file *cal_filp = NULL;
+ int sum[3] = { 0, };
+ int err = 0;
+ int i;
+ mm_segment_t old_fs;
+
+ if (do_calib) {
+ for (i = 0; i < CALIBRATION_DATA_AMOUNT; i++) {
+ err = read_accel_raw_xyz(&data);
+ if (err < 0) {
+ pr_err("%s: accel_read_accel_raw_xyz() "
+ "failed in the %dth loop\n",
+ __func__, i);
+ return err;
+ }
+
+ sum[0] += data.x;
+ sum[1] += data.y;
+ sum[2] += data.z;
+ }
+
+
+ if (is_lis3dh) {
+ cal_data.x = IDEAL_X - cal_data.x;
+ cal_data.y = IDEAL_Y - cal_data.y;
+ cal_data.z = IDEAL_Z - cal_data.z;
+ } else {
+ cal_data.x = sum[0] / CALIBRATION_DATA_AMOUNT;
+ cal_data.y = sum[1] / CALIBRATION_DATA_AMOUNT;
+ cal_data.z = sum[2] / CALIBRATION_DATA_AMOUNT;
+ }
+ } else {
+ cal_data.x = 0;
+ cal_data.y = 0;
+ cal_data.z = 0;
+ }
+
+ pr_info("%s: cal data (%d,%d,%d)\n", __func__,
+ cal_data.x, cal_data.y, cal_data.z);
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ cal_filp = filp_open(CALIBRATION_FILE_PATH,
+ O_CREAT | O_TRUNC | O_WRONLY, 0666);
+ if (IS_ERR(cal_filp)) {
+ pr_err("%s: Can't open calibration file\n", __func__);
+ set_fs(old_fs);
+ err = PTR_ERR(cal_filp);
+ return err;
+ }
+
+ err = cal_filp->f_op->write(cal_filp,
+ (char *)&cal_data, 3 * sizeof(s16),
+ &cal_filp->f_pos);
+ if (err != 3 * sizeof(s16)) {
+ pr_err("%s: Can't write the cal data to file\n", __func__);
+ err = -EIO;
+ }
+
+ filp_close(cal_filp, current->files);
+ set_fs(old_fs);
+
+ return err;
+}
+
+static int mpu_open(struct inode *inode, struct file *file)
+{
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(this_client);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+
+ accel_open_calibration();
+
+ pr_info("%s", __func__);
+ dev_dbg(&this_client->adapter->dev, "mpu_open\n");
+ dev_dbg(&this_client->adapter->dev, "current->pid %d\n", current->pid);
+ pid = current->pid;
+ file->private_data = this_client;
+
+ /* we could do some checking on the flags supplied by "open" */
+ /* i.e. O_NONBLOCK */
+ /* -> set some flag to disable interruptible_sleep_on in mpu_read */
+
+ /* Reset the sensors to the default */
+ mldl_cfg->requested_sensors = ML_THREE_AXIS_GYRO;
+ if (mldl_cfg->accel && mldl_cfg->accel->resume)
+ mldl_cfg->requested_sensors |= ML_THREE_AXIS_ACCEL;
+
+ if (mldl_cfg->compass && mldl_cfg->compass->resume)
+ mldl_cfg->requested_sensors |= ML_THREE_AXIS_COMPASS;
+
+ if (mldl_cfg->pressure && mldl_cfg->pressure->resume)
+ mldl_cfg->requested_sensors |= ML_THREE_AXIS_PRESSURE;
+
+ return 0;
+}
+
+/* close function - called when the "file" /dev/mpu is closed in userspace */
+static int mpu_release(struct inode *inode, struct file *file)
+{
+ struct i2c_client *client = (struct i2c_client *)file->private_data;
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct i2c_adapter *accel_adapter;
+ struct i2c_adapter *compass_adapter;
+ struct i2c_adapter *pressure_adapter;
+ int result = 0;
+
+ pid = 0;
+
+ accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
+ compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+ pressure_adapter = i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num);
+ result = mpu3050_suspend(mldl_cfg, client->adapter,
+ accel_adapter, compass_adapter,
+ pressure_adapter, TRUE, TRUE, TRUE, TRUE);
+ pr_info("%s", __func__);
+ dev_dbg(&this_client->adapter->dev, "mpu_release\n");
+ return result;
+}
+
+static noinline int mpudev_ioctl_rdrw(struct i2c_client *client,
+ unsigned long arg)
+{
+ struct i2c_rdwr_ioctl_data rdwr_arg;
+ struct i2c_msg *rdwr_pa;
+ u8 __user **data_ptrs;
+ int i, res;
+
+ if (copy_from_user(&rdwr_arg,
+ (struct i2c_rdwr_ioctl_data __user *)arg,
+ sizeof(rdwr_arg)))
+ return -EFAULT;
+
+ /* Put an arbitrary limit on the number of messages that can
+ * be sent at once */
+ if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)
+ return -EINVAL;
+
+ rdwr_pa = (struct i2c_msg *)
+ kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), GFP_KERNEL);
+ if (!rdwr_pa)
+ return -ENOMEM;
+
+ if (copy_from_user(rdwr_pa, rdwr_arg.msgs,
+ rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {
+ kfree(rdwr_pa);
+ return -EFAULT;
+ }
+
+ data_ptrs = kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL);
+ if (data_ptrs == NULL) {
+ kfree(rdwr_pa);
+ return -ENOMEM;
+ }
+
+ res = 0;
+ for (i = 0; i < rdwr_arg.nmsgs; i++) {
+ /* Limit the size of the message to a sane amount;
+ * and don't let length change either. */
+ if ((rdwr_pa[i].len > 8192) ||
+ (rdwr_pa[i].flags & I2C_M_RECV_LEN)) {
+ res = -EINVAL;
+ break;
+ }
+ data_ptrs[i] = (u8 __user *) rdwr_pa[i].buf;
+ rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL);
+ if (rdwr_pa[i].buf == NULL) {
+ res = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(rdwr_pa[i].buf, data_ptrs[i],
+ rdwr_pa[i].len)) {
+ ++i; /* Needs to be kfreed too */
+ res = -EFAULT;
+ break;
+ }
+ }
+ if (res < 0) {
+ int j;
+ for (j = 0; j < i; ++j)
+ kfree(rdwr_pa[j].buf);
+ kfree(data_ptrs);
+ kfree(rdwr_pa);
+ return res;
+ }
+
+ res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);
+ while (i-- > 0) {
+ if (res >= 0 && (rdwr_pa[i].flags & I2C_M_RD)) {
+ if (copy_to_user(data_ptrs[i], rdwr_pa[i].buf,
+ rdwr_pa[i].len))
+ res = -EFAULT;
+ }
+ kfree(rdwr_pa[i].buf);
+ }
+ kfree(data_ptrs);
+ kfree(rdwr_pa);
+ return res;
+}
+
+/* read function called when from /dev/mpu is read. Read from the FIFO */
+static ssize_t mpu_read(struct file *file,
+ char __user *buf, size_t count, loff_t *offset)
+{
+ char *tmp;
+ int ret;
+
+ struct i2c_client *client = (struct i2c_client *)file->private_data;
+
+ if (count > 8192)
+ count = 8192;
+
+ tmp = kmalloc(count, GFP_KERNEL);
+ if (tmp == NULL)
+ return -ENOMEM;
+
+ pr_info("%s: i2c-dev: i2c-%d reading %zu bytes.\n", __func__,
+ iminor(file->f_path.dentry->d_inode), count);
+
+/* @todo fix this to do a i2c trasnfer from the FIFO */
+ ret = i2c_master_recv(client, tmp, count);
+ if (ret >= 0) {
+ ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret;
+ if (ret)
+ ret = -EFAULT;
+ }
+ kfree(tmp);
+ return ret;
+}
+
+static int mpu_ioctl_set_mpu_pdata(struct i2c_client *client, unsigned long arg)
+{
+ int ii;
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct mpu3050_platform_data *pdata = mpu->mldl_cfg.pdata;
+ struct mpu3050_platform_data local_pdata;
+
+ if (copy_from_user(&local_pdata, (unsigned char __user *)arg,
+ sizeof(local_pdata)))
+ return -EFAULT;
+
+ pdata->int_config = local_pdata.int_config;
+ for (ii = 0; ii < DIM(pdata->orientation); ii++)
+ pdata->orientation[ii] = local_pdata.orientation[ii];
+ pdata->level_shifter = local_pdata.level_shifter;
+
+ pdata->accel.address = local_pdata.accel.address;
+ for (ii = 0; ii < DIM(pdata->accel.orientation); ii++)
+ pdata->accel.orientation[ii] =
+ local_pdata.accel.orientation[ii];
+
+ pdata->compass.address = local_pdata.compass.address;
+ for (ii = 0; ii < DIM(pdata->compass.orientation); ii++)
+ pdata->compass.orientation[ii] =
+ local_pdata.compass.orientation[ii];
+
+ pdata->pressure.address = local_pdata.pressure.address;
+ for (ii = 0; ii < DIM(pdata->pressure.orientation); ii++)
+ pdata->pressure.orientation[ii] =
+ local_pdata.pressure.orientation[ii];
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ return ML_SUCCESS;
+}
+
+static int
+mpu_ioctl_set_mpu_config(struct i2c_client *client, unsigned long arg)
+{
+ int ii;
+ int result = ML_SUCCESS;
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct mldl_cfg *temp_mldl_cfg;
+
+ dev_dbg(&this_client->adapter->dev, "%s\n", __func__);
+
+ temp_mldl_cfg = kzalloc(sizeof(struct mldl_cfg), GFP_KERNEL);
+ if (NULL == temp_mldl_cfg)
+ return -ENOMEM;
+
+ /*
+ * User space is not allowed to modify accel compass pressure or
+ * pdata structs, as well as silicon_revision product_id or trim
+ */
+ if (copy_from_user(temp_mldl_cfg, (struct mldl_cfg __user *)arg,
+ offsetof(struct mldl_cfg, silicon_revision))) {
+ result = -EFAULT;
+ goto out;
+ }
+
+ if (mldl_cfg->gyro_is_suspended) {
+ if (mldl_cfg->addr != temp_mldl_cfg->addr)
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ if (mldl_cfg->int_config != temp_mldl_cfg->int_config)
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ if (mldl_cfg->ext_sync != temp_mldl_cfg->ext_sync)
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ if (mldl_cfg->full_scale != temp_mldl_cfg->full_scale)
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ if (mldl_cfg->lpf != temp_mldl_cfg->lpf)
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ if (mldl_cfg->clk_src != temp_mldl_cfg->clk_src)
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ if (mldl_cfg->divider != temp_mldl_cfg->divider)
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ if (mldl_cfg->dmp_enable != temp_mldl_cfg->dmp_enable)
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ if (mldl_cfg->fifo_enable != temp_mldl_cfg->fifo_enable)
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ if (mldl_cfg->dmp_cfg1 != temp_mldl_cfg->dmp_cfg1)
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ if (mldl_cfg->dmp_cfg2 != temp_mldl_cfg->dmp_cfg2)
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ if (mldl_cfg->gyro_power != temp_mldl_cfg->gyro_power)
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ for (ii = 0; ii < MPU_NUM_AXES; ii++)
+ if (mldl_cfg->offset_tc[ii] !=
+ temp_mldl_cfg->offset_tc[ii])
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ for (ii = 0; ii < MPU_NUM_AXES; ii++)
+ if (mldl_cfg->offset[ii] != temp_mldl_cfg->offset[ii])
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ if (memcmp(mldl_cfg->ram, temp_mldl_cfg->ram,
+ MPU_MEM_NUM_RAM_BANKS * MPU_MEM_BANK_SIZE *
+ sizeof(unsigned char)))
+ mldl_cfg->gyro_needs_reset = TRUE;
+ }
+
+ memcpy(mldl_cfg, temp_mldl_cfg,
+ offsetof(struct mldl_cfg, silicon_revision));
+
+out:
+ kfree(temp_mldl_cfg);
+ return result;
+}
+
+static int
+mpu_ioctl_get_mpu_config(struct i2c_client *client, unsigned long arg)
+{
+ /* Have to be careful as there are 3 pointers in the mldl_cfg
+ * structure */
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct mldl_cfg *local_mldl_cfg;
+ int retval = 0;
+
+ local_mldl_cfg = kzalloc(sizeof(struct mldl_cfg), GFP_KERNEL);
+ if (NULL == local_mldl_cfg)
+ return -ENOMEM;
+
+ retval =
+ copy_from_user(local_mldl_cfg, (struct mldl_cfg __user *)arg,
+ sizeof(struct mldl_cfg));
+ if (retval) {
+ dev_err(&this_client->adapter->dev,
+ "%s|%s:%d: EFAULT on arg\n",
+ __FILE__, __func__, __LINE__);
+ retval = -EFAULT;
+ goto out;
+ }
+
+ /* Fill in the accel, compass, pressure and pdata pointers */
+ if (mldl_cfg->accel) {
+ retval = copy_to_user((void __user *)local_mldl_cfg->accel,
+ mldl_cfg->accel,
+ sizeof(*mldl_cfg->accel));
+ if (retval) {
+ dev_err(&this_client->adapter->dev,
+ "%s|%s:%d: EFAULT on accel\n",
+ __FILE__, __func__, __LINE__);
+ retval = -EFAULT;
+ goto out;
+ }
+ }
+
+ if (mldl_cfg->compass) {
+ retval = copy_to_user((void __user *)local_mldl_cfg->compass,
+ mldl_cfg->compass,
+ sizeof(*mldl_cfg->compass));
+ if (retval) {
+ dev_err(&this_client->adapter->dev,
+ "%s|%s:%d: EFAULT on compass\n",
+ __FILE__, __func__, __LINE__);
+ retval = -EFAULT;
+ goto out;
+ }
+ }
+
+ if (mldl_cfg->pressure) {
+ retval = copy_to_user((void __user *)local_mldl_cfg->pressure,
+ mldl_cfg->pressure,
+ sizeof(*mldl_cfg->pressure));
+ if (retval) {
+ dev_err(&this_client->adapter->dev,
+ "%s|%s:%d: EFAULT on pressure\n",
+ __FILE__, __func__, __LINE__);
+ retval = -EFAULT;
+ goto out;
+ }
+ }
+
+ if (mldl_cfg->pdata) {
+ retval = copy_to_user((void __user *)local_mldl_cfg->pdata,
+ mldl_cfg->pdata,
+ sizeof(*mldl_cfg->pdata));
+ if (retval) {
+ dev_err(&this_client->adapter->dev,
+ "%s|%s:%d: EFAULT on pdata\n",
+ __FILE__, __func__, __LINE__);
+ retval = -EFAULT;
+ goto out;
+ }
+ }
+
+ /* Do not modify the accel, compass, pressure and pdata pointers */
+ retval = copy_to_user((struct mldl_cfg __user *)arg,
+ mldl_cfg, offsetof(struct mldl_cfg, accel));
+
+ if (retval)
+ retval = -EFAULT;
+out:
+ kfree(local_mldl_cfg);
+ return retval;
+}
+
+/**
+ * Pass a requested slave configuration to the slave sensor
+ *
+ * @param adapter the adaptor to use to communicate with the slave
+ * @param mldl_cfg the mldl configuration structuer
+ * @param slave pointer to the slave descriptor
+ * @param usr_config The configuration to pass to the slave sensor
+ *
+ * @return 0 or non-zero error code
+ */
+static int slave_config(void *adapter,
+ struct mldl_cfg *mldl_cfg,
+ struct ext_slave_descr *slave,
+ struct ext_slave_config __user *usr_config)
+{
+ int retval = ML_SUCCESS;
+ if ((slave) && (slave->config)) {
+ struct ext_slave_config config;
+ retval = copy_from_user(&config, usr_config, sizeof(config));
+ if (retval)
+ return -EFAULT;
+
+ if (config.len && config.data) {
+ int *data;
+ data = kzalloc(config.len, GFP_KERNEL);
+ if (!data)
+ return ML_ERROR_MEMORY_EXAUSTED;
+
+ retval = copy_from_user(data,
+ (void __user *)config.data,
+ config.len);
+ if (retval) {
+ retval = -EFAULT;
+ kfree(data);
+ return retval;
+ }
+ config.data = data;
+ }
+ retval = slave->config(adapter,
+ slave, &mldl_cfg->pdata->accel, &config);
+ kfree(config.data);
+ }
+ return retval;
+}
+
+/**
+ * Get a requested slave configuration from the slave sensor
+ *
+ * @param adapter the adaptor to use to communicate with the slave
+ * @param mldl_cfg the mldl configuration structuer
+ * @param slave pointer to the slave descriptor
+ * @param usr_config The configuration for the slave to fill out
+ *
+ * @return 0 or non-zero error code
+ */
+static int slave_get_config(void *adapter,
+ struct mldl_cfg *mldl_cfg,
+ struct ext_slave_descr *slave,
+ struct ext_slave_config __user *usr_config)
+{
+ int retval = ML_SUCCESS;
+ if ((slave) && (slave->get_config)) {
+ struct ext_slave_config config;
+ void *user_data;
+ retval = copy_from_user(&config, usr_config, sizeof(config));
+ if (retval)
+ return -EFAULT;
+
+ user_data = config.data;
+ if (config.len && config.data) {
+ int *data;
+ data = kzalloc(config.len, GFP_KERNEL);
+ if (!data)
+ return ML_ERROR_MEMORY_EXAUSTED;
+
+ retval = copy_from_user(data,
+ (void __user *)config.data,
+ config.len);
+ if (retval) {
+ retval = -EFAULT;
+ kfree(data);
+ return retval;
+ }
+ config.data = data;
+ }
+ retval = slave->get_config(adapter,
+ slave,
+ &mldl_cfg->pdata->accel, &config);
+ if (retval) {
+ kfree(config.data);
+ return retval;
+ }
+ retval = copy_to_user((unsigned char __user *)user_data,
+ config.data, config.len);
+ kfree(config.data);
+ }
+ return retval;
+}
+
+/* ioctl - I/O control */
+static long mpu_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct i2c_client *client = (struct i2c_client *)file->private_data;
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ int retval = 0;
+ struct i2c_adapter *accel_adapter;
+ struct i2c_adapter *compass_adapter;
+ struct i2c_adapter *pressure_adapter;
+
+ accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
+ compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+ pressure_adapter = i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num);
+
+ switch (cmd) {
+ case I2C_RDWR:
+ mpudev_ioctl_rdrw(client, arg);
+ break;
+ case I2C_SLAVE:
+ if ((arg & 0x7E) != (client->addr & 0x7E)) {
+ dev_err(&this_client->adapter->dev,
+ "%s: Invalid I2C_SLAVE arg %lu\n",
+ __func__, arg);
+ }
+ break;
+ case MPU_SET_MPU_CONFIG:
+ retval = mpu_ioctl_set_mpu_config(client, arg);
+ break;
+ case MPU_SET_INT_CONFIG:
+ mldl_cfg->int_config = (unsigned char)arg;
+ break;
+ case MPU_SET_EXT_SYNC:
+ mldl_cfg->ext_sync = (enum mpu_ext_sync)arg;
+ break;
+ case MPU_SET_FULL_SCALE:
+ mldl_cfg->full_scale = (enum mpu_fullscale)arg;
+ break;
+ case MPU_SET_LPF:
+ mldl_cfg->lpf = (enum mpu_filter)arg;
+ break;
+ case MPU_SET_CLK_SRC:
+ mldl_cfg->clk_src = (enum mpu_clock_sel)arg;
+ break;
+ case MPU_SET_DIVIDER:
+ mldl_cfg->divider = (unsigned char)arg;
+ break;
+ case MPU_SET_LEVEL_SHIFTER:
+ mldl_cfg->pdata->level_shifter = (unsigned char)arg;
+ break;
+ case MPU_SET_DMP_ENABLE:
+ mldl_cfg->dmp_enable = (unsigned char)arg;
+ break;
+ case MPU_SET_FIFO_ENABLE:
+ mldl_cfg->fifo_enable = (unsigned char)arg;
+ break;
+ case MPU_SET_DMP_CFG1:
+ mldl_cfg->dmp_cfg1 = (unsigned char)arg;
+ break;
+ case MPU_SET_DMP_CFG2:
+ mldl_cfg->dmp_cfg2 = (unsigned char)arg;
+ break;
+ case MPU_SET_OFFSET_TC:
+ retval = copy_from_user(mldl_cfg->offset_tc,
+ (unsigned char __user *)arg,
+ sizeof(mldl_cfg->offset_tc));
+ if (retval)
+ retval = -EFAULT;
+ break;
+ case MPU_SET_RAM:
+ retval = copy_from_user(mldl_cfg->ram,
+ (unsigned char __user *)arg,
+ sizeof(mldl_cfg->ram));
+ if (retval)
+ retval = -EFAULT;
+ break;
+ case MPU_SET_PLATFORM_DATA:
+ retval = mpu_ioctl_set_mpu_pdata(client, arg);
+ break;
+ case MPU_GET_MPU_CONFIG:
+ retval = mpu_ioctl_get_mpu_config(client, arg);
+ break;
+ case MPU_GET_INT_CONFIG:
+ retval = put_user(mldl_cfg->int_config,
+ (unsigned char __user *)arg);
+ break;
+ case MPU_GET_EXT_SYNC:
+ retval = put_user(mldl_cfg->ext_sync,
+ (unsigned char __user *)arg);
+ break;
+ case MPU_GET_FULL_SCALE:
+ retval = put_user(mldl_cfg->full_scale,
+ (unsigned char __user *)arg);
+ break;
+ case MPU_GET_LPF:
+ retval = put_user(mldl_cfg->lpf, (unsigned char __user *)arg);
+ break;
+ case MPU_GET_CLK_SRC:
+ retval = put_user(mldl_cfg->clk_src,
+ (unsigned char __user *)arg);
+ break;
+ case MPU_GET_DIVIDER:
+ retval = put_user(mldl_cfg->divider,
+ (unsigned char __user *)arg);
+ break;
+ case MPU_GET_LEVEL_SHIFTER:
+ retval = put_user(mldl_cfg->pdata->level_shifter,
+ (unsigned char __user *)arg);
+ break;
+ case MPU_GET_DMP_ENABLE:
+ retval = put_user(mldl_cfg->dmp_enable,
+ (unsigned char __user *)arg);
+ break;
+ case MPU_GET_FIFO_ENABLE:
+ retval = put_user(mldl_cfg->fifo_enable,
+ (unsigned char __user *)arg);
+ break;
+ case MPU_GET_DMP_CFG1:
+ retval = put_user(mldl_cfg->dmp_cfg1,
+ (unsigned char __user *)arg);
+ break;
+ case MPU_GET_DMP_CFG2:
+ retval = put_user(mldl_cfg->dmp_cfg2,
+ (unsigned char __user *)arg);
+ break;
+ case MPU_GET_OFFSET_TC:
+ retval = copy_to_user((unsigned char __user *)arg,
+ mldl_cfg->offset_tc,
+ sizeof(mldl_cfg->offset_tc));
+ if (retval)
+ retval = -EFAULT;
+ break;
+ case MPU_GET_RAM:
+ retval = copy_to_user((unsigned char __user *)arg,
+ mldl_cfg->ram, sizeof(mldl_cfg->ram));
+ if (retval)
+ retval = -EFAULT;
+ break;
+ case MPU_CONFIG_ACCEL:
+ retval = slave_config(accel_adapter, mldl_cfg,
+ mldl_cfg->accel,
+ (struct ext_slave_config __user *)arg);
+ break;
+ case MPU_CONFIG_COMPASS:
+ retval = slave_config(compass_adapter, mldl_cfg,
+ mldl_cfg->compass,
+ (struct ext_slave_config __user *)arg);
+ break;
+ case MPU_CONFIG_PRESSURE:
+ retval = slave_config(pressure_adapter, mldl_cfg,
+ mldl_cfg->pressure,
+ (struct ext_slave_config __user *)arg);
+ break;
+ case MPU_GET_CONFIG_ACCEL:
+ retval = slave_get_config(accel_adapter, mldl_cfg,
+ mldl_cfg->accel,
+ (struct ext_slave_config __user *)
+ arg);
+ break;
+ case MPU_GET_CONFIG_COMPASS:
+ retval = slave_get_config(compass_adapter, mldl_cfg,
+ mldl_cfg->compass,
+ (struct ext_slave_config __user *)
+ arg);
+ break;
+ case MPU_GET_CONFIG_PRESSURE:
+ retval = slave_get_config(pressure_adapter, mldl_cfg,
+ mldl_cfg->pressure,
+ (struct ext_slave_config __user *)
+ arg);
+ break;
+ case MPU_SUSPEND:
+ {
+ unsigned long sensors;
+ sensors = ~(mldl_cfg->requested_sensors);
+ retval = mpu3050_suspend(mldl_cfg,
+ client->adapter,
+ accel_adapter,
+ compass_adapter,
+ pressure_adapter,
+ ((sensors & ML_THREE_AXIS_GYRO)
+ == ML_THREE_AXIS_GYRO),
+ ((sensors &
+ ML_THREE_AXIS_ACCEL)
+ == ML_THREE_AXIS_ACCEL),
+ ((sensors &
+ ML_THREE_AXIS_COMPASS)
+ == ML_THREE_AXIS_COMPASS),
+ ((sensors &
+ ML_THREE_AXIS_PRESSURE)
+ == ML_THREE_AXIS_PRESSURE));
+ }
+ break;
+ case MPU_RESUME:
+ {
+ unsigned long sensors;
+ sensors = mldl_cfg->requested_sensors;
+ retval = mpu3050_resume(mldl_cfg,
+ client->adapter,
+ accel_adapter,
+ compass_adapter,
+ pressure_adapter,
+ sensors & ML_THREE_AXIS_GYRO,
+ sensors & ML_THREE_AXIS_ACCEL,
+ sensors & ML_THREE_AXIS_COMPASS,
+ sensors &
+ ML_THREE_AXIS_PRESSURE);
+ }
+ break;
+ case MPU_READ_ACCEL:
+ {
+ unsigned char data[6];
+ retval = mpu3050_read_accel(mldl_cfg, client->adapter,
+ data);
+
+ if ((ML_SUCCESS == retval) &&
+ (copy_to_user((unsigned char __user *)arg,
+ data, sizeof(data))))
+ retval = -EFAULT;
+ }
+ break;
+ case MPU_READ_COMPASS:
+ {
+ unsigned char data[6];
+ struct i2c_adapter *compass_adapt =
+ i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+ retval = mpu3050_read_compass(mldl_cfg, compass_adapt,
+ data);
+ if ((ML_SUCCESS == retval) &&
+ (copy_to_user((unsigned char *)arg,
+ data, sizeof(data))))
+ retval = -EFAULT;
+ }
+ break;
+ case MPU_READ_PRESSURE:
+ {
+ unsigned char data[3];
+ struct i2c_adapter *pressure_adapt =
+ i2c_get_adapter(mldl_cfg->pdata->pressure.
+ adapt_num);
+ retval =
+ mpu3050_read_pressure(mldl_cfg, pressure_adapt,
+ data);
+ if ((ML_SUCCESS == retval)
+ &&
+ (copy_to_user
+ ((unsigned char __user *)arg, data, sizeof(data))))
+ retval = -EFAULT;
+ }
+ break;
+ case MPU_READ_MEMORY:
+ case MPU_WRITE_MEMORY:
+ default:
+ dev_err(&this_client->adapter->dev,
+ "%s: Unknown cmd %d, arg %lu\n", __func__, cmd, arg);
+ retval = -EINVAL;
+ }
+
+ return retval;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+void mpu3050_early_suspend(struct early_suspend *h)
+{
+ struct mpu_private_data *mpu = container_of(h,
+ struct mpu_private_data,
+ early_suspend);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct i2c_adapter *accel_adapter;
+ struct i2c_adapter *compass_adapter;
+ struct i2c_adapter *pressure_adapter;
+ pr_info("%s", __func__);
+
+ accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
+ compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+ pressure_adapter = i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num);
+
+ dev_dbg(&this_client->adapter->dev, "%s: %d, %d\n", __func__,
+ h->level, mpu->mldl_cfg.gyro_is_suspended);
+ if (MPU3050_EARLY_SUSPEND_IN_DRIVER)
+ (void)mpu3050_suspend(mldl_cfg, this_client->adapter,
+ accel_adapter, compass_adapter,
+ pressure_adapter, TRUE, TRUE, TRUE, TRUE);
+}
+
+void mpu3050_early_resume(struct early_suspend *h)
+{
+ struct mpu_private_data *mpu = container_of(h,
+ struct mpu_private_data,
+ early_suspend);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct i2c_adapter *accel_adapter;
+ struct i2c_adapter *compass_adapter;
+ struct i2c_adapter *pressure_adapter;
+
+ accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
+ compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+ pressure_adapter = i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num);
+
+ if (MPU3050_EARLY_SUSPEND_IN_DRIVER) {
+ if (pid) {
+ unsigned long sensors = mldl_cfg->requested_sensors;
+ (void)mpu3050_resume(mldl_cfg,
+ this_client->adapter,
+ accel_adapter,
+ compass_adapter,
+ pressure_adapter,
+ sensors & ML_THREE_AXIS_GYRO,
+ sensors & ML_THREE_AXIS_ACCEL,
+ sensors & ML_THREE_AXIS_COMPASS,
+ sensors & ML_THREE_AXIS_PRESSURE);
+ dev_dbg(&this_client->adapter->dev,
+ "%s for pid %d\n", __func__, pid);
+ }
+ }
+ dev_dbg(&this_client->adapter->dev, "%s: %d\n", __func__, h->level);
+ pr_info("%s: h->level = %d\n", __func__, h->level);
+}
+#endif
+
+void mpu_shutdown(struct i2c_client *client)
+{
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct i2c_adapter *accel_adapter;
+ struct i2c_adapter *compass_adapter;
+ struct i2c_adapter *pressure_adapter;
+ pr_info("%s", __func__);
+ accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
+ compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+ pressure_adapter = i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num);
+
+ (void)mpu3050_suspend(mldl_cfg, this_client->adapter,
+ accel_adapter, compass_adapter, pressure_adapter,
+ TRUE, TRUE, TRUE, TRUE);
+ dev_dbg(&this_client->adapter->dev, "%s\n", __func__);
+}
+
+int mpu_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct i2c_adapter *accel_adapter;
+ struct i2c_adapter *compass_adapter;
+ struct i2c_adapter *pressure_adapter;
+
+ accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
+ compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+ pressure_adapter = i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num);
+ pr_info("%s", __func__);
+ if (!mpu->mldl_cfg.gyro_is_suspended) {
+ dev_dbg(&this_client->adapter->dev,
+ "%s: suspending on event %d\n", __func__, mesg.event);
+ (void)mpu3050_suspend(mldl_cfg, this_client->adapter,
+ accel_adapter, compass_adapter,
+ pressure_adapter, TRUE, TRUE, TRUE, TRUE);
+ } else {
+ dev_dbg(&this_client->adapter->dev,
+ "%s: Already suspended %d\n", __func__, mesg.event);
+ }
+ return 0;
+}
+
+int mpu_resume(struct i2c_client *client)
+{
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct i2c_adapter *accel_adapter;
+ struct i2c_adapter *compass_adapter;
+ struct i2c_adapter *pressure_adapter;
+
+ accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
+ pr_info("%s: accel_adapter = %p\n", __func__, accel_adapter);
+
+ compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+ pressure_adapter = i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num);
+
+ if (pid) {
+ unsigned long sensors = mldl_cfg->requested_sensors;
+ (void)mpu3050_resume(mldl_cfg, this_client->adapter,
+ accel_adapter,
+ compass_adapter,
+ pressure_adapter,
+ sensors & ML_THREE_AXIS_GYRO,
+ sensors & ML_THREE_AXIS_ACCEL,
+ sensors & ML_THREE_AXIS_COMPASS,
+ sensors & ML_THREE_AXIS_PRESSURE);
+ dev_dbg(&this_client->adapter->dev,
+ "%s for pid %d\n", __func__, pid);
+ }
+
+ pr_info("%s: pid = %d\n", __func__, pid);
+ return 0;
+}
+
+/* define which file operations are supported */
+static const struct file_operations mpu_fops = {
+ .owner = THIS_MODULE,
+ .read = mpu_read,
+#if HAVE_COMPAT_IOCTL
+ .compat_ioctl = mpu_ioctl,
+#endif
+#if HAVE_UNLOCKED_IOCTL
+ .unlocked_ioctl = mpu_ioctl,
+#endif
+ .open = mpu_open,
+ .release = mpu_release,
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static struct miscdevice i2c_mpu_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "mpu", /* Same for both 3050 and 6000 */
+ .fops = &mpu_fops,
+};
+
+#define FACTORY_TEST
+#ifdef FACTORY_TEST
+
+static ssize_t mpu3050_power_on(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int count = 0;
+
+ pr_info("%s : this_client = %d\n", __func__, (int)this_client);
+ count = sprintf(buf, "%d\n", (this_client != NULL ? 1 : 0));
+
+ return count;
+}
+
+static int mpu3050_factory_on(struct i2c_client *client)
+{
+ struct mpu_private_data *mpu = i2c_get_clientdata(client);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct i2c_adapter *accel_adapter;
+ struct i2c_adapter *compass_adapter;
+ struct i2c_adapter *pressure_adapter;
+ int prev_gyro_suspended;
+ pr_info("%s", __func__);
+ accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
+ compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+ pressure_adapter = i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num);
+
+ prev_gyro_suspended = mldl_cfg->gyro_is_suspended;
+ if (prev_gyro_suspended) {
+ unsigned long sensors = mldl_cfg->requested_sensors;
+ (void)mpu3050_resume(mldl_cfg,
+ client->adapter,
+ accel_adapter,
+ compass_adapter,
+ pressure_adapter,
+ sensors & ML_THREE_AXIS_GYRO,
+ sensors & ML_THREE_AXIS_ACCEL,
+ sensors & ML_THREE_AXIS_COMPASS,
+ sensors & ML_THREE_AXIS_PRESSURE);
+ }
+
+ return prev_gyro_suspended;
+}
+
+static void mpu3050_factory_off(struct i2c_client *client)
+{
+ struct mpu_private_data *mpu = i2c_get_clientdata(client);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct i2c_adapter *accel_adapter;
+ struct i2c_adapter *compass_adapter;
+ struct i2c_adapter *pressure_adapter;
+ pr_info("%s", __func__);
+ accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
+ compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+ pressure_adapter = i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num);
+
+ (void)mpu3050_suspend(mldl_cfg,
+ client->adapter,
+ accel_adapter,
+ compass_adapter,
+ pressure_adapter, TRUE, TRUE, TRUE, TRUE);
+}
+
+static ssize_t mpu3050_get_temp(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int count = 0;
+ short int temperature = 0;
+ unsigned char data[2];
+ int prev_gyro_suspended;
+ pr_info("%s", __func__);
+ prev_gyro_suspended = mpu3050_factory_on(this_client);
+
+ /*MPUREG_TEMP_OUT_H, 27 0x1b */
+ /*MPUREG_TEMP_OUT_L, 28 0x1c */
+ /* TEMP_OUT_H/L: 16-bit temperature data (2's complement data format) */
+ sensor_i2c_read(this_client->adapter, DEFAULT_MPU_SLAVEADDR,
+ MPUREG_TEMP_OUT_H, 2, data);
+ temperature = (short)(((data[0]) << 8) | data[1]);
+ temperature = (((temperature + 13200) / 280) + 35);
+ pr_info("%s :read temperature = %d\n", __func__, temperature);
+
+ count = sprintf(buf, "%d\n", temperature);
+
+ if (prev_gyro_suspended)
+ mpu3050_factory_off(this_client);
+
+ return count;
+}
+
+/*
+ Defines
+*/
+
+#define DEBUG_OUT 1
+
+#define DEF_GYRO_FULLSCALE (2000) /* gyro full scale dps */
+#define DEF_GYRO_SENS (32768.f/DEF_GYRO_FULLSCALE)
+ /* gyro sensitivity LSB/dps */
+#define DEF_PACKET_THRESH (75) /* 600 ms / 8ms / sample */
+#define DEF_TIMING_TOL (.05f) /* 5% */
+#define DEF_BIAS_THRESH (40*DEF_GYRO_SENS)
+ /* 40 dps in LSBs */
+#define DEF_RMS_THRESH_SQ (0.4f*0.4f*DEF_GYRO_SENS*DEF_GYRO_SENS)
+ /* (.2 dps in LSBs ) ^ 2 */
+#define DEF_TEST_TIME_PER_AXIS (600) /* ms of time spent collecting
+ data for each axis,
+ multiple of 600ms */
+
+/*
+ Macros
+*/
+
+#define CHECK_TEST_ERROR(x) \
+ if (x) { \
+ pr_info("error %d @ %s|%d\n", x, __func__, __LINE__); \
+ return -1; \
+ }
+
+#define SHORT_TO_TEMP_C(shrt) (((shrt+13200)/280)+35)
+#define CHARS_TO_SHORT(d) ((((short)(d)[0])<<8)+(d)[1])
+#define fabs(x) (((x) < 0) ? -(x) : (x))
+
+void mpu3050_usleep(unsigned long t)
+{
+ unsigned long start = MLOSGetTickCount();
+ while (MLOSGetTickCount() - start < t / 1000) {
+ }
+}
+
+#define X (0)
+#define Y (1)
+#define Z (2)
+
+static short mpu3050_selftest_gyro_avg[3];
+static int mpu3050_selftest_result;
+static int mpu3050_selftest_bias[3];
+static int mpu3050_selftest_rms[3];
+
+int mpu3050_test_gyro(struct i2c_client *client, short gyro_biases[3],
+ short *temp_avg)
+{
+ void *mlsl_handle = client->adapter;
+ int retVal = 0;
+ tMLError result;
+
+ int total_count = 0;
+ int total_count_axis[3] = { 0, 0, 0 };
+ int packet_count;
+ unsigned char regs[7];
+
+ char a_name[3][2] = { "X", "Y", "Z" };
+ int temperature;
+ int Avg[3];
+ int RMS[3];
+ int i, j, tmp;
+ unsigned char dataout[20];
+
+ short *x, *y, *z;
+
+ x = kzalloc(sizeof(*x) * (DEF_TEST_TIME_PER_AXIS / 8 * 4), GFP_KERNEL);
+ y = kzalloc(sizeof(*y) * (DEF_TEST_TIME_PER_AXIS / 8 * 4), GFP_KERNEL);
+ z = kzalloc(sizeof(*z) * (DEF_TEST_TIME_PER_AXIS / 8 * 4), GFP_KERNEL);
+
+ temperature = 0;
+
+ /* sample rate = 8ms */
+ result = MLSLSerialWriteSingle(mlsl_handle, client->addr,
+ MPUREG_SMPLRT_DIV, 0x07);
+
+ if (result)
+ goto out_i2c_faild;
+
+ regs[0] = 0x03; /* filter = 42Hz, analog_sample rate = 1 KHz */
+ switch (DEF_GYRO_FULLSCALE) {
+ case 2000:
+ regs[0] |= 0x18;
+ break;
+ case 1000:
+ regs[0] |= 0x10;
+ break;
+ case 500:
+ regs[0] |= 0x08;
+ break;
+ case 250:
+ default:
+ regs[0] |= 0x00;
+ break;
+ }
+ result = MLSLSerialWriteSingle(mlsl_handle, client->addr,
+ MPUREG_DLPF_FS_SYNC, regs[0]);
+ if (result)
+ goto out_i2c_faild;
+
+ result = MLSLSerialWriteSingle(mlsl_handle, client->addr,
+ MPUREG_INT_CFG, 0x00);
+
+ /* 1st, timing test */
+ for (j = 0; j < 3; j++) {
+
+ pr_info("%s :Collecting gyro data from %s gyro PLL\n",
+ __func__, a_name[j]);
+
+ /* turn on all gyros, use gyro X for clocking
+ Set to Y and Z for 2nd and 3rd iteration */
+ result = MLSLSerialWriteSingle(mlsl_handle, client->addr,
+ MPUREG_PWR_MGM, j + 1);
+ if (result)
+ goto out_i2c_faild;
+
+ /* wait for 2 ms after switching clock source */
+ mpu3050_usleep(2000);
+
+ /* we will enable XYZ gyro in FIFO and nothing else */
+ result = MLSLSerialWriteSingle(mlsl_handle, client->addr,
+ MPUREG_FIFO_EN2, 0x00);
+ if (result)
+ goto out_i2c_faild;
+ /* enable/reset FIFO */
+ result = MLSLSerialWriteSingle(mlsl_handle, client->addr,
+ MPUREG_USER_CTRL, 0x42);
+
+ tmp = (int)(DEF_TEST_TIME_PER_AXIS / 600);
+
+ while (tmp-- > 0) {
+ /* enable XYZ gyro in FIFO and nothing else */
+ result =
+ MLSLSerialWriteSingle(mlsl_handle, client->addr,
+ MPUREG_FIFO_EN1, 0x70);
+ if (result)
+ goto out_i2c_faild;
+
+ /* wait for 600 ms for data */
+ mpu3050_usleep(600000);
+
+ /* stop storing gyro in the FIFO */
+ result =
+ MLSLSerialWriteSingle(mlsl_handle, client->addr,
+ MPUREG_FIFO_EN1, 0x00);
+ if (result)
+ goto out_i2c_faild;
+
+ /* Getting number of bytes in FIFO */
+ result = MLSLSerialRead(mlsl_handle, client->addr,
+ MPUREG_FIFO_COUNTH, 2, dataout);
+ if (result)
+ goto out_i2c_faild;
+ /* number of 6 B packets in the FIFO */
+ packet_count = CHARS_TO_SHORT(dataout) / 6;
+ pr_info("%s :Packet Count: %d - ",
+ __func__, packet_count);
+
+ if (abs(packet_count - DEF_PACKET_THRESH)
+ <= /* Within +-5% range */
+ (int)(DEF_TIMING_TOL * DEF_PACKET_THRESH + .5)) {
+ for (i = 0; i < packet_count; i++) {
+ /* getting FIFO data */
+ result =
+ MLSLSerialReadFifo(mlsl_handle,
+ client->addr, 6,
+ dataout);
+ if (result)
+ goto out_i2c_faild;
+
+ x[total_count + i] =
+ CHARS_TO_SHORT(&dataout[0]);
+ y[total_count + i] =
+ CHARS_TO_SHORT(&dataout[2]);
+ z[total_count + i] =
+ CHARS_TO_SHORT(&dataout[4]);
+ if (DEBUG_OUT && 0) {
+ pr_info("%s :Gyros %-4d " \
+ ": %+13d %+13d %+13d\n",
+ __func__, total_count + i,
+ x[total_count + i],
+ y[total_count + i],
+ z[total_count + i]);
+ }
+ }
+ total_count += packet_count;
+ total_count_axis[j] += packet_count;
+ pr_info("%s :OK\n", __func__);
+ } else {
+ retVal |= 1 << j;
+ pr_info("%s :NOK - samples ignored\n",
+ __func__);
+ }
+ }
+
+ /* remove gyros from FIFO */
+ result = MLSLSerialWriteSingle(mlsl_handle, client->addr,
+ MPUREG_FIFO_EN1, 0x00);
+ if (result)
+ goto out_i2c_faild;
+
+ /* Read Temperature */
+ result = MLSLSerialRead(mlsl_handle, client->addr,
+ MPUREG_TEMP_OUT_H, 2, dataout);
+ temperature += (short)CHARS_TO_SHORT(dataout);
+ }
+
+ pr_info("%s :\nTotal %d samples\n\n", __func__, total_count);
+
+ /* 2nd, check bias from X and Y PLL clock source */
+ tmp = total_count != 0 ? total_count : 1;
+ for (i = 0, Avg[X] = .0f, Avg[Y] = .0f, Avg[Z] = .0f;
+ i < total_count; i++) {
+ Avg[X] += x[i];
+ Avg[Y] += y[i];
+ Avg[Z] += z[i];
+ }
+
+ Avg[X] /= tmp;
+ Avg[Y] /= tmp;
+ Avg[Z] /= tmp;
+
+ pr_info("%s :bias : %+13d %+13d %+13d (LSB)\n",
+ __func__, Avg[X], Avg[Y], Avg[Z]);
+ if (DEBUG_OUT) {
+ pr_info("%s : : %+13d %+13d %+13d (dps)\n",
+ __func__, Avg[X] / 131, Avg[Y] / 131, Avg[Z] / 131);
+ }
+ for (j = 0; j < 3; j++) {
+ if (abs(Avg[j]) > (int)DEF_BIAS_THRESH) {
+ pr_info("%s :%s-Gyro bias (%.0d) exceeded threshold "
+ "(threshold = %f)\n", __func__,
+ a_name[j], Avg[j], DEF_BIAS_THRESH);
+ retVal |= 1 << (3 + j);
+ }
+ }
+
+ /* 3rd and finally, check RMS */
+ for (i = 0, RMS[X] = 0.f, RMS[Y] = 0.f, RMS[Z] = 0.f;
+ i < total_count; i++) {
+ RMS[X] += (x[i] - Avg[X]) * (x[i] - Avg[X]);
+ RMS[Y] += (y[i] - Avg[Y]) * (y[i] - Avg[Y]);
+ RMS[Z] += (z[i] - Avg[Z]) * (z[i] - Avg[Z]);
+ }
+
+ for (j = 0; j < 3; j++) {
+ if (RMS[j] > (int)DEF_RMS_THRESH_SQ * total_count) {
+ pr_info
+ ("%s :%s-Gyro RMS (%d) exceeded threshold (%.4f)\n",
+ __func__, a_name[j], RMS[j] / total_count,
+ DEF_RMS_THRESH_SQ);
+ retVal |= 1 << (6 + j);
+ }
+ }
+
+ pr_info("%s :RMS^2 : %+13d %+13d %+13d (LSB-rms)\n",
+ __func__,
+ (RMS[X] / total_count),
+ (RMS[Y] / total_count), (RMS[Z] / total_count));
+ if (RMS[X] == 0 || RMS[Y] == 0 || RMS[Z] == 0) {
+ /*If any of the RMS noise value returns zero,
+ then we might have dead gyro or FIFO/register failure,
+ or the part is sleeping */
+ retVal |= 1 << 9;
+ }
+
+ temperature /= 3;
+ if (DEBUG_OUT)
+ pr_info("%s :Temperature : %+13d %13s %13s (deg. C)\n",
+ __func__, SHORT_TO_TEMP_C(temperature), "", "");
+
+ /* load into final storage */
+ *temp_avg = (short)temperature;
+ gyro_biases[X] = (short)Avg[X];
+ gyro_biases[Y] = (short)Avg[Y];
+ gyro_biases[Z] = (short)Avg[Z];
+
+ mpu3050_selftest_bias[X] = (int)Avg[X];
+ mpu3050_selftest_bias[Y] = (int)Avg[Y];
+ mpu3050_selftest_bias[Z] = (int)Avg[Z];
+
+ mpu3050_selftest_rms[X] = RMS[X] / total_count;
+ mpu3050_selftest_rms[Y] = RMS[Y] / total_count;
+ mpu3050_selftest_rms[Z] = RMS[Z] / total_count;
+
+out_i2c_faild:
+ if (result)
+ pr_info("%s : error %d", __func__, result);
+
+ kfree(x);
+ kfree(y);
+ kfree(z);
+
+ return retVal;
+}
+
+int mpu3050_self_test_once(struct i2c_client *client)
+{
+ void *mlsl_handle = client->adapter;
+ int result = 0;
+
+ short temp_avg;
+
+ unsigned char regs[5];
+ unsigned long testStart = MLOSGetTickCount();
+
+ pr_info("%s :Collecting %d groups of 600 ms samples for each axis\n\n",
+ __func__, DEF_TEST_TIME_PER_AXIS / 600);
+
+ result = MLSLSerialRead(mlsl_handle, client->addr,
+ MPUREG_PWR_MGM, 1, regs);
+ CHECK_TEST_ERROR(result);
+ /* reset */
+ result = MLSLSerialWriteSingle(mlsl_handle, client->addr,
+ MPUREG_PWR_MGM, regs[0] | 0x80);
+ CHECK_TEST_ERROR(result);
+ MLOSSleep(5);
+ /* wake up */
+ if (regs[0] & 0x40) {
+ result = MLSLSerialWriteSingle(mlsl_handle, client->addr,
+ MPUREG_PWR_MGM, 0x00);
+ CHECK_TEST_ERROR(result);
+ }
+ MLOSSleep(60);
+
+ /* collect gyro and temperature data */
+ mpu3050_selftest_result = mpu3050_test_gyro(client,
+ mpu3050_selftest_gyro_avg,
+ &temp_avg);
+
+ pr_info("%s :\nTest time : %ld ms\n",
+ __func__, MLOSGetTickCount() - testStart);
+
+ return mpu3050_selftest_result;
+}
+
+static ssize_t mpu3050_self_test(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char gyro_data[6];
+ short int raw[3];
+ int count = 0;
+ int res = 0;
+ int prev_gyro_suspended;
+
+/*MPUREG_GYRO_XOUT_H, 29 0x1d */
+/*MPUREG_GYRO_XOUT_L, 30 0x1e */
+/*MPUREG_GYRO_YOUT_H, 31 0x1f */
+/*MPUREG_GYRO_YOUT_L, 32 0x20 */
+/*MPUREG_GYRO_ZOUT_H, 33 0x21 */
+/*MPUREG_GYRO_ZOUT_L, 34 0x22 */
+
+/* GYRO_XOUT_H/L: 16-bit X gyro output data (2's complement data format) */
+/* GYRO_YOUT_H/L: 16-bit Y gyro output data (2's complement data format) */
+/* GYRO_ZOUT_H/L: 16-bit Z gyro output data (2's complement data format) */
+
+ prev_gyro_suspended = mpu3050_factory_on(this_client);
+
+ mpu3050_self_test_once(this_client);
+
+ res = sensor_i2c_read(this_client->adapter, DEFAULT_MPU_SLAVEADDR,
+ MPUREG_GYRO_XOUT_H, 6, gyro_data);
+
+ if (res)
+ return 0;
+
+ raw[0] = (short)(((gyro_data[0]) << 8) | gyro_data[1]);
+ raw[1] = (short)(((gyro_data[2]) << 8) | gyro_data[3]);
+ raw[2] = (short)(((gyro_data[4]) << 8) | gyro_data[5]);
+
+ pr_info("%s: %s %s, %d, %d, %d, %d, %d, %d\n", __func__, buf,
+ (!mpu3050_selftest_result ? "OK" : "NG"),
+ raw[0], raw[1], raw[2],
+ mpu3050_selftest_gyro_avg[0],
+ mpu3050_selftest_gyro_avg[1], mpu3050_selftest_gyro_avg[2]);
+
+ count = sprintf(buf, "%s, %d, %d, %d, %d, %d, %d\n",
+ (!mpu3050_selftest_result ? "OK" : "NG"),
+ mpu3050_selftest_bias[0], mpu3050_selftest_bias[1],
+ mpu3050_selftest_bias[2],
+ mpu3050_selftest_rms[0],
+ mpu3050_selftest_rms[1], mpu3050_selftest_rms[2]);
+
+ if (prev_gyro_suspended)
+ mpu3050_factory_off(this_client);
+
+ return count;
+}
+
+static ssize_t mpu3050_acc_read(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char acc_data[6];
+ s16 x, y, z;
+ s32 temp;
+
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(this_client);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+
+ if (mldl_cfg->accel_is_suspended == 1 ||
+ (mldl_cfg->dmp_is_running == 0
+ && mldl_cfg->accel_is_suspended == 0)) {
+ if (is_lis3dh) {
+ if (mldl_cfg->accel_is_suspended == 1) {
+ sensor_i2c_write_register(this_client->adapter,
+ 0x19, 0x20, 0x67);
+ MLOSSleep(1);
+ }
+ sensor_i2c_read(this_client->adapter,
+ 0x19, 0x28 | 0x80, 6, acc_data);
+
+ if (mldl_cfg->accel_is_suspended == 1) {
+ sensor_i2c_write_register(this_client->adapter,
+ 0x19, 0x20, 0x18);
+ MLOSSleep(1);
+ }
+ } else
+ sensor_i2c_read(this_client->adapter,
+ 0x0F, 0x06, 6, acc_data);
+ } else if (mldl_cfg->dmp_is_running &&
+ mldl_cfg->accel_is_suspended == 0) {
+ sensor_i2c_read(this_client->adapter,
+ DEFAULT_MPU_SLAVEADDR, 0x23, 6, acc_data);
+ }
+
+ if (is_lis3dh) {
+ x = ((acc_data[0] << 8) | acc_data[1]);
+ x = (x >> 4) + cal_data.x;
+ y = ((acc_data[2] << 8) | acc_data[3]);
+ y = (y >> 4) + cal_data.y;
+ z = ((acc_data[4] << 8) | acc_data[5]);
+ z = (z >> 4) + cal_data.z;
+ } else {
+ temp = (s16) ((acc_data[1] << 4) | (acc_data[0] >> 4))
+ + cal_data.x;
+ if (temp < 2048)
+ x = (s16) (temp);
+ else
+ x = (s16) ((4096 - temp)) * (-1);
+
+ temp = (s16) ((acc_data[3] << 4) | (acc_data[2] >> 4))
+ + cal_data.y;
+ if (temp < 2048)
+ y = (s16) (temp);
+ else
+ y = (s16) ((4096 - temp)) * (-1);
+
+ temp = (s16) ((acc_data[5] << 4) | (acc_data[4] >> 4))
+ + cal_data.z;
+ if (temp < 2048)
+ z = (s16) (temp);
+ else
+ z = (s16) ((4096 - temp)) * (-1);
+ }
+
+#if defined(CONFIG_MACH_P8)
+ /* x *= (-1); */
+ /* y *= (-1); */
+ z *= (-1);
+ return sprintf(buf, "%d, %d, %d\n", y, x, z);
+
+#elif defined(CONFIG_MACH_P8LTE)
+ x *= (-1);
+ /* y *= (-1); */
+ /* z *= (-1); */
+ return sprintf(buf, "%d, %d, %d\n", y, x, z);
+
+#elif defined(CONFIG_MACH_P2)
+ /* x *= (-1); */
+ /* y *= (-1); */
+ z *= (-1);
+ return sprintf(buf, "%d, %d, %d\n", y, x, z);
+
+#else
+ x *= (-1);
+ y *= (-1);
+ z *= (-1);
+ return sprintf(buf, "%d, %d, %d\n", y, x, z);
+
+#endif
+
+}
+
+static ssize_t accel_calibration_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int err;
+
+ err = accel_open_calibration();
+ if (err < 0)
+ pr_err("%s: accel_open_calibration() failed\n", __func__);
+
+ pr_info("accel_calibration_show :%d %d %d\n",
+ cal_data.x, cal_data.y, cal_data.z);
+
+ if (err < 0)
+ err = 0;
+ else
+ err = 1;
+
+ if (cal_data.x == 0 && cal_data.y == 0 && cal_data.z == 0)
+ err = 0;
+
+ return sprintf(buf, "%d %d %d %d\n",
+ err, cal_data.x, cal_data.y, cal_data.z);
+}
+
+static ssize_t accel_calibration_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ bool do_calib;
+ int err;
+ int count = 0;
+ char str[11];
+
+ if (sysfs_streq(buf, "1"))
+ do_calib = true;
+ else if (sysfs_streq(buf, "0"))
+ do_calib = false;
+ else {
+ pr_debug("%s: invalid value %d\n", __func__, *buf);
+ return -EINVAL;
+ }
+
+ err = accel_do_calibrate(do_calib);
+ if (err < 0)
+ pr_err("%s: accel_do_calibrate() failed\n", __func__);
+
+ pr_info("accel_calibration_show :%d %d %d\n",
+ cal_data.x, cal_data.y, cal_data.z);
+ if (err > 0)
+ err = 0;
+ count = sprintf(str, "%d\n", err);
+
+ strcpy(str, buf);
+ return count;
+}
+
+static ssize_t get_accel_vendor_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", ACCEL_VENDOR_NAME);
+}
+
+static ssize_t get_accel_chip_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", ACCEL_CHIP_NAME);
+}
+
+static ssize_t get_gyro_vendor_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", GYRO_VENDOR_NAME);
+}
+
+static ssize_t get_gyro_chip_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", GYRO_CHIP_NAME);
+}
+
+static struct device_attribute dev_attr_accel_vendor =
+ __ATTR(vendor, S_IRUGO, get_accel_vendor_name, NULL);
+static struct device_attribute dev_attr_accel_chip =
+ __ATTR(name, S_IRUGO, get_accel_chip_name, NULL);
+
+static struct device_attribute dev_attr_gyro_vendor =
+ __ATTR(vendor, S_IRUGO, get_gyro_vendor_name, NULL);
+static struct device_attribute dev_attr_gyro_chip =
+ __ATTR(name, S_IRUGO, get_gyro_chip_name, NULL);
+
+static DEVICE_ATTR(calibration, 0664,
+ accel_calibration_show, accel_calibration_store);
+static DEVICE_ATTR(raw_data, S_IRUGO, mpu3050_acc_read, NULL);
+
+static DEVICE_ATTR(power_on, S_IRUGO | S_IWUSR, mpu3050_power_on, NULL);
+static DEVICE_ATTR(temperature, S_IRUGO | S_IWUSR, mpu3050_get_temp, NULL);
+static DEVICE_ATTR(selftest, S_IRUGO | S_IWUSR, mpu3050_self_test, NULL);
+
+static DEVICE_ATTR(gyro_power_on, S_IRUGO | S_IWUSR, mpu3050_power_on, NULL);
+static DEVICE_ATTR(gyro_get_temp, S_IRUGO | S_IWUSR, mpu3050_get_temp, NULL);
+static DEVICE_ATTR(gyro_selftest, S_IRUGO | S_IWUSR, mpu3050_self_test, NULL);
+
+
+static struct device_attribute *accel_sensor_attrs[] = {
+ &dev_attr_raw_data,
+ &dev_attr_calibration,
+ &dev_attr_accel_vendor,
+ &dev_attr_accel_chip,
+ NULL,
+};
+
+static struct device_attribute *gyro_sensor_attrs[] = {
+ &dev_attr_power_on,
+ &dev_attr_temperature,
+ &dev_attr_selftest,
+ &dev_attr_gyro_vendor,
+ &dev_attr_gyro_chip,
+ NULL,
+};
+
+extern struct class *sec_class;
+extern struct class *sensors_class;
+
+static struct device *sec_mpu3050_dev;
+static struct device *accel_sensor_device;
+static struct device *gyro_sensor_device;
+
+extern int sensors_register(struct device *dev, void *drvdata,
+ struct device_attribute *attributes[], char *name);
+#endif
+#define FEATURE_MPU_AUTO_PROBING
+
+#if defined(CONFIG_MPU_SENSORS_KXTF9_LIS3DH)
+
+static int mpu350_auto_probe_accel(struct mpu3050_platform_data *pdata)
+{
+ int ret = ML_SUCCESS;
+ unsigned char reg = 0;
+ struct mpu_private_data *mpu;
+ struct mldl_cfg *mldl_cfg;
+ struct i2c_adapter *accel_adapter = NULL;
+ struct ext_slave_descr *slave_descr = NULL;
+ accel_adapter = i2c_get_adapter(pdata->accel.adapt_num);
+
+ slave_descr = lis3dh_get_slave_descr();
+ ret = MLSLSerialRead(accel_adapter, 0x19, 0x0, 1, &reg);
+ if (ret == 0) {
+ pdata->accel.get_slave_descr = lis3dh_get_slave_descr;
+ pdata->accel.address = 0x19;
+
+ mpu = (struct mpu_private_data *)
+ i2c_get_clientdata(this_client);
+ mldl_cfg = &mpu->mldl_cfg;
+ mldl_cfg->accel = slave_descr;
+/*
+ printk("auto probe : found %s\n",
+ pdata->accel.get_slave_descr()->name);
+*/
+ is_lis3dh = 1;
+ }
+ return ret;
+}
+
+#endif
+
+int mpu3050_probe(struct i2c_client *client, const struct i2c_device_id *devid)
+{
+ struct mpu3050_platform_data *pdata;
+ struct mpu_private_data *mpu;
+ struct mldl_cfg *mldl_cfg;
+ int res = 0;
+ int retry = 5;
+
+ struct i2c_adapter *accel_adapter = NULL;
+ struct i2c_adapter *compass_adapter = NULL;
+ struct i2c_adapter *pressure_adapter = NULL;
+
+ pr_info("================%s===============\n", __func__);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ res = -ENODEV;
+ goto out_check_functionality_failed;
+ }
+#ifdef FACTORY_TEST
+ res = sensors_register(accel_sensor_device, NULL,
+ accel_sensor_attrs, "accelerometer_sensor");
+ if (res)
+ pr_err("%s: cound not register accelerometer "\
+ "sensor device(%d).\n", __func__, res);
+
+ res = sensors_register(gyro_sensor_device, NULL,
+ gyro_sensor_attrs, "gyro_sensor");
+ if (res)
+ pr_err("%s: cound not register gyro "\
+ "sensor device(%d).\n", __func__, res);
+
+ sec_mpu3050_dev = device_create(sec_class, NULL, 0, NULL,
+ "sec_mpu3050");
+ if (IS_ERR(sec_mpu3050_dev))
+ pr_info("%s :Failed to create device!", __func__);
+
+ if (device_create_file(sec_mpu3050_dev, &dev_attr_gyro_power_on) < 0) {
+ pr_info("%s :Failed to create device file(%s)!\n", __func__,
+ dev_attr_gyro_power_on.attr.name);
+ return -1;
+ }
+ if (device_create_file(sec_mpu3050_dev, &dev_attr_gyro_get_temp) < 0) {
+ pr_info("%s :Failed to create device file(%s)!\n", __func__,
+ dev_attr_gyro_get_temp.attr.name);
+ device_remove_file(sec_mpu3050_dev, &dev_attr_gyro_power_on);
+ return -1;
+ }
+ if (device_create_file(sec_mpu3050_dev, &dev_attr_gyro_selftest) < 0) {
+ pr_info("%s :Failed to create device file(%s)!\n", __func__,
+ dev_attr_gyro_selftest.attr.name);
+ device_remove_file(sec_mpu3050_dev, &dev_attr_gyro_power_on);
+ device_remove_file(sec_mpu3050_dev, &dev_attr_gyro_get_temp);
+ return -1;
+ }
+#endif
+ mpu = kzalloc(sizeof(struct mpu_private_data), GFP_KERNEL);
+ if (!mpu) {
+ res = -ENOMEM;
+ goto out_alloc_data_failed;
+ }
+
+ i2c_set_clientdata(client, mpu);
+ this_client = client;
+ mldl_cfg = &mpu->mldl_cfg;
+ pdata = (struct mpu3050_platform_data *)client->dev.platform_data;
+ if (!pdata) {
+ dev_warn(&this_client->adapter->dev,
+ "Warning no platform data for mpu3050\n");
+ } else {
+ mldl_cfg->pdata = pdata;
+
+ pdata->accel.get_slave_descr = get_accel_slave_descr;
+ pdata->compass.get_slave_descr = get_compass_slave_descr;
+ pdata->pressure.get_slave_descr = get_pressure_slave_descr;
+
+ is_lis3dh = 0;
+#if defined(CONFIG_MPU_SENSORS_KXTF9_LIS3DH)
+ mpu350_auto_probe_accel(pdata);
+ if (pdata->accel.get_slave_descr && !is_lis3dh) {
+ mldl_cfg->accel = pdata->accel.get_slave_descr();
+ dev_info(&this_client->adapter->dev,
+ "%s: +%s\n", MPU_NAME, mldl_cfg->accel->name);
+ accel_adapter = i2c_get_adapter(pdata->accel.adapt_num);
+
+ if (!accel_adapter) {
+ pr_info("%s : accel_adapter i2c get fail",
+ __func__);
+ goto out_accel_failed;
+ }
+
+ if (pdata->accel.irq > 0) {
+ dev_info(&this_client->adapter->dev,
+ "Installing Accel irq using %d\n",
+ pdata->accel.irq);
+ res = slaveirq_init(accel_adapter,
+ &pdata->accel, "accelirq");
+ if (res)
+ goto out_accelirq_failed;
+ } else {
+ dev_warn(&this_client->adapter->dev,
+ "WARNING: Accel irq not assigned\n");
+ }
+ } else
+#else
+ if (pdata->accel.get_slave_descr) {
+ mldl_cfg->accel = pdata->accel.get_slave_descr();
+ dev_info(&this_client->adapter->dev,
+ "%s: +%s\n", MPU_NAME, mldl_cfg->accel->name);
+ accel_adapter = i2c_get_adapter(pdata->accel.adapt_num);
+
+ if (!accel_adapter) {
+ pr_info("%s : accel_adapter i2c get fail",
+ __func__);
+ goto out_accel_failed;
+ }
+
+ if (pdata->accel.irq > 0) {
+ dev_info(&this_client->adapter->dev,
+ "Installing Accel irq using %d\n",
+ pdata->accel.irq);
+ res = slaveirq_init(accel_adapter,
+ &pdata->accel, "accelirq");
+ if (res)
+ goto out_accelirq_failed;
+ } else {
+ dev_warn(&this_client->adapter->dev,
+ "WARNING: Accel irq not assigned\n");
+ }
+ } else
+#endif
+ {
+ dev_warn(&this_client->adapter->dev,
+ "%s: No Accel Present\n", MPU_NAME);
+ }
+
+ if (pdata->compass.get_slave_descr) {
+ mldl_cfg->compass = pdata->compass.get_slave_descr();
+ dev_info(&this_client->adapter->dev,
+ "%s: +%s\n", MPU_NAME,
+ mldl_cfg->compass->name);
+ compass_adapter =
+ i2c_get_adapter(pdata->compass.adapt_num);
+
+ if (!compass_adapter) {
+ pr_info("%s : compass_adapter i2c get fail",
+ __func__);
+ goto out_compass_failed;
+ }
+
+ if (pdata->compass.irq > 0) {
+ dev_info(&this_client->adapter->dev,
+ "Installing Compass irq using %d\n",
+ pdata->compass.irq);
+ res = slaveirq_init(compass_adapter,
+ &pdata->compass,
+ "compassirq");
+ if (res)
+ goto out_compassirq_failed;
+ } else {
+ dev_warn(&this_client->adapter->dev,
+ "WARNING: Compass irq not assigned\n");
+ }
+ } else {
+ dev_warn(&this_client->adapter->dev,
+ "%s: No Compass Present\n", MPU_NAME);
+ }
+
+ if (pdata->pressure.get_slave_descr) {
+ mldl_cfg->pressure = pdata->pressure.get_slave_descr();
+ dev_info(&this_client->adapter->dev,
+ "%s: +%s\n", MPU_NAME,
+ mldl_cfg->pressure->name);
+ pressure_adapter =
+ i2c_get_adapter(pdata->pressure.adapt_num);
+
+ if (!pressure_adapter) {
+ pr_info("%s : pressure_adapter i2c get fail",
+ __func__);
+ goto out_pressure_failed;
+ }
+
+ if (pdata->pressure.irq > 0) {
+ dev_info(&this_client->adapter->dev,
+ "Installing Pressure irq using %d\n",
+ pdata->pressure.irq);
+ res = slaveirq_init(pressure_adapter,
+ &pdata->pressure,
+ "pressureirq");
+ if (res)
+ goto out_pressureirq_failed;
+ } else {
+ dev_warn(&this_client->adapter->dev,
+ "WARNING: Pressure irq not assigned\n");
+ }
+ } else {
+ dev_warn(&this_client->adapter->dev,
+ "%s: No Pressure Present\n", MPU_NAME);
+ }
+ }
+
+ mldl_cfg->addr = client->addr;
+
+ do {
+ res = mpu3050_open(&mpu->mldl_cfg, client->adapter,
+ accel_adapter, compass_adapter,
+ pressure_adapter);
+ if (res) {
+ dev_err(&this_client->adapter->dev,
+ "%s i2c Init Error, ret = %d\n", MPU_NAME, res);
+ mpu3050_usleep(5000);
+ }
+ } while (retry-- && res);
+
+ if (res) {
+ dev_err(&this_client->adapter->dev,
+ "Unable to open %s %d\n", MPU_NAME, res);
+ res = -ENODEV;
+ goto out_whoami_failed;
+ }
+
+ res = misc_register(&i2c_mpu_device);
+ if (res < 0) {
+ dev_err(&this_client->adapter->dev,
+ "ERROR: misc_register returned %d\n", res);
+ goto out_misc_register_failed;
+ }
+
+ if (this_client->irq > 0) {
+ dev_info(&this_client->adapter->dev,
+ "Installing irq using %d\n", this_client->irq);
+ res = mpuirq_init(this_client);
+ if (res)
+ goto out_mpuirq_failed;
+ } else {
+ dev_warn(&this_client->adapter->dev,
+ "WARNING: %s irq not assigned\n", MPU_NAME);
+ }
+
+ mpu_accel_init(&mpu->mldl_cfg, client->adapter);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ mpu->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ mpu->early_suspend.suspend = mpu3050_early_suspend;
+ mpu->early_suspend.resume = mpu3050_early_resume;
+ register_early_suspend(&mpu->early_suspend);
+#endif
+ return res;
+
+out_mpuirq_failed:
+ misc_deregister(&i2c_mpu_device);
+out_misc_register_failed:
+ mpu3050_close(&mpu->mldl_cfg, client->adapter,
+ accel_adapter, compass_adapter, pressure_adapter);
+out_whoami_failed:
+ if (pdata && pdata->pressure.get_slave_descr && pdata->pressure.irq)
+ slaveirq_exit(&pdata->pressure);
+out_pressureirq_failed:
+out_pressure_failed:
+ if (pdata && pdata->compass.get_slave_descr && pdata->compass.irq)
+ slaveirq_exit(&pdata->compass);
+out_compassirq_failed:
+out_compass_failed:
+ if (pdata && pdata->accel.get_slave_descr && pdata->accel.irq)
+ slaveirq_exit(&pdata->accel);
+out_accelirq_failed:
+out_accel_failed:
+ kfree(mpu);
+out_alloc_data_failed:
+out_check_functionality_failed:
+ dev_err(&this_client->adapter->dev, "%s failed %d\n", __func__, res);
+ return res;
+}
+
+static int mpu3050_remove(struct i2c_client *client)
+{
+ struct mpu_private_data *mpu = i2c_get_clientdata(client);
+ struct i2c_adapter *accel_adapter;
+ struct i2c_adapter *compass_adapter;
+ struct i2c_adapter *pressure_adapter;
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct mpu3050_platform_data *pdata = mldl_cfg->pdata;
+
+ accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
+ compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+ pressure_adapter = i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&mpu->early_suspend);
+#endif
+ mpu3050_close(mldl_cfg, client->adapter,
+ accel_adapter, compass_adapter, pressure_adapter);
+
+ if (client->irq)
+ mpuirq_exit();
+
+ if (pdata && pdata->pressure.get_slave_descr && pdata->pressure.irq)
+ slaveirq_exit(&pdata->pressure);
+
+ if (pdata && pdata->compass.get_slave_descr && pdata->compass.irq)
+ slaveirq_exit(&pdata->compass);
+
+ if (pdata && pdata->accel.get_slave_descr && pdata->accel.irq)
+ slaveirq_exit(&pdata->accel);
+
+ misc_deregister(&i2c_mpu_device);
+ kfree(mpu);
+
+ mpu_accel_exit(mldl_cfg);
+
+ return 0;
+}
+
+static const struct i2c_device_id mpu3050_id[] = {
+ {MPU_NAME, 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, mpu3050_id);
+
+static struct i2c_driver mpu3050_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = mpu3050_probe,
+ .remove = mpu3050_remove,
+ .id_table = mpu3050_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = MPU_NAME,
+ },
+ .address_list = normal_i2c,
+ .shutdown = mpu_shutdown, /* optional */
+ .suspend = mpu_suspend, /* optional */
+ .resume = mpu_resume, /* optional */
+};
+
+static int __init mpu_init(void)
+{
+ int res = i2c_add_driver(&mpu3050_driver);
+ pid = 0;
+
+ pr_info("%s res=%d\n", __func__, res);
+ if (res)
+ dev_err(&this_client->adapter->dev, "%s failed\n", __func__);
+ return res;
+}
+
+static void __exit mpu_exit(void)
+{
+ pr_info(KERN_DEBUG "%s\n", __func__);
+ i2c_del_driver(&mpu3050_driver);
+}
+
+module_init(mpu_init);
+module_exit(mpu_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("User space character device interface for MPU3050");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS(MPU_NAME);