blob: 1f246a881d6aedb197b28247d7b95e5ae9808341 [file] [log] [blame]
/*
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/>.
*/
#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 "mpu-i2c.h"
#include "mldl_cfg.h"
#include "mpu.h"
#define MPU3050_EARLY_SUSPEND_IN_DRIVER 0
#define D(x...) printk(KERN_DEBUG "[GYRO][MPU3050] " x)
#define I(x...) printk(KERN_INFO "[GYRO][MPU3050] " x)
#define E(x...) printk(KERN_ERR "[GYRO][MPU3050 ERROR] " x)
struct mpu_private_data {
struct mldl_cfg mldl_cfg;
#ifdef CONFIG_HAS_EARLYSUSPEND
struct early_suspend early_suspend;
#endif
};
static int pid;
static struct i2c_client *this_client;
int mpu_debug_flag;
int mpu_sensors_reset;
int mpu_lpm_flag;
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;
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;
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;
}
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);
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;
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++) {
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;
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;
}
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_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
iminor(file->f_path.dentry->d_inode), count);
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;
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)
{
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;
}
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;
}
}
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;
}
static int slave_config(void *adapter,
struct mldl_cfg *mldl_cfg,
struct ext_slave_descr *slave,
struct ext_slave_platform_data *pdata,
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,
pdata,
&config);
kfree(config.data);
}
return retval;
}
static int slave_get_config(void *adapter,
struct mldl_cfg *mldl_cfg,
struct ext_slave_descr *slave,
struct ext_slave_platform_data *pdata,
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,
pdata,
&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;
}
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,
"[mpu_err]%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,
&mldl_cfg->pdata->accel,
(struct ext_slave_config __user *) arg);
break;
case MPU_CONFIG_COMPASS:
retval = slave_config(compass_adapter, mldl_cfg,
mldl_cfg->compass,
&mldl_cfg->pdata->compass,
(struct ext_slave_config __user *) arg);
break;
case MPU_CONFIG_PRESSURE:
retval = slave_config(pressure_adapter, mldl_cfg,
mldl_cfg->pressure,
&mldl_cfg->pdata->pressure,
(struct ext_slave_config __user *) arg);
break;
case MPU_GET_CONFIG_ACCEL:
retval = slave_get_config(accel_adapter, mldl_cfg,
mldl_cfg->accel,
&mldl_cfg->pdata->accel,
(struct ext_slave_config __user *) arg);
break;
case MPU_GET_CONFIG_COMPASS:
retval = slave_get_config(compass_adapter, mldl_cfg,
mldl_cfg->compass,
&mldl_cfg->pdata->compass,
(struct ext_slave_config __user *) arg);
break;
case MPU_GET_CONFIG_PRESSURE:
retval = slave_get_config(pressure_adapter, mldl_cfg,
mldl_cfg->pressure,
&mldl_cfg->pdata->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;
#ifdef HTC_READ_CAL_DATA
case MPU_READ_CAL_DATA:
{
int index;
unsigned char mpu_gyro_gsensor_kvalue[37];
for (index = 0;
index < sizeof(mpu_gyro_gsensor_kvalue);
index++) {
mpu_gyro_gsensor_kvalue[index] =
gyro_gsensor_kvalue[index];
printk(KERN_DEBUG "gyro_gsensor_kvalue[%d] = 0x%x\n",
index, gyro_gsensor_kvalue[index]);
}
retval =
copy_to_user((unsigned char *) arg,
mpu_gyro_gsensor_kvalue,
sizeof(mpu_gyro_gsensor_kvalue));
}
break;
#endif
case MPU_READ_MEMORY:
case MPU_WRITE_MEMORY:
default:
dev_err(&this_client->adapter->dev,
"[mpu_err]%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;
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);
}
#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;
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);
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);
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);
}
return 0;
}
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",
.fops = &mpu_fops,
};
void *g_handler;
int (*g_sensors_reset)(void);
struct class *mpu3050_class;
struct device *mpu3050_dev;
static ssize_t pwr_reg_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned char b[2] = "";
int result;
unsigned char bma[8] = "";
result = MLSLSerialRead(g_handler, 0x68,
MPUREG_USER_CTRL, 2, b);
result = MLSLSerialRead(g_handler, 0x18,
0x0F, 3, bma);
result = sprintf(buf, "MPUREG_USER_CTRL = 0x%x, MPUREG_PWR_MGM = "
"0x%x.\n"
"BMA register 0x0F = 0x%x, "
"BMA register 0x10 = 0x%x, "
"BMA register 0x11 = 0x%x\n"
"",
b[0], b[1],
bma[0], bma[1], bma[2]);
return result;
}
static ssize_t pwr_reg_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
return count;
}
static DEVICE_ATTR(pwr_reg, 0664, pwr_reg_show, pwr_reg_store);
static ssize_t mpu_debug_flag_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
char *s = buf;
s += sprintf(s, "mpu_debug_flag = 0x%x\n", mpu_debug_flag);
return s - buf;
}
static ssize_t mpu_debug_flag_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
mpu_debug_flag = -1;
sscanf(buf, "%d", &mpu_debug_flag);
D("%s: mpu_debug_flag = %d\n", __func__, mpu_debug_flag);
return count;
}
static DEVICE_ATTR(mpu_debug_flag, 0664, mpu_debug_flag_show, \
mpu_debug_flag_store);
static ssize_t mpu_sensors_reset_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
char *s = buf;
s += sprintf(s, "mpu_sensors_reset = 0x%x\n", mpu_sensors_reset);
return s - buf;
}
static ssize_t mpu_sensors_reset_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int rc = 0;
mpu_sensors_reset = -1;
sscanf(buf, "%d", &mpu_sensors_reset);
D("%s: mpu_sensors_reset = %d\n", __func__, mpu_sensors_reset);
if ((mpu_sensors_reset == 1) && g_sensors_reset) {
rc = g_sensors_reset();
if (rc)
E("G-Sensor, Compass, Gyro reset error\n");
}
return count;
}
static DEVICE_ATTR(mpu_sensors_reset, 0664, mpu_sensors_reset_show, \
mpu_sensors_reset_store);
static ssize_t mpu_lpm_flag_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
char *s = buf;
s += sprintf(s, "%d", mpu_lpm_flag);
return s - buf;
}
#ifdef CONFIG_CIR_ALWAYS_READY
extern int cir_flag;
#endif
static ssize_t mpu_lpm_flag_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct mpu_private_data *mpu =
(struct mpu_private_data *) i2c_get_clientdata(this_client);
struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
mpu_lpm_flag = -1;
sscanf(buf, "%d", &mpu_lpm_flag);
#ifdef CONFIG_CIR_ALWAYS_READY
if ((mpu_lpm_flag == 1) && mldl_cfg->pdata->power_LPM && !cir_flag)
#else
if ((mpu_lpm_flag == 1) && mldl_cfg->pdata->power_LPM)
#endif
mldl_cfg->pdata->power_LPM(1);
else if (mldl_cfg->pdata->power_LPM)
mldl_cfg->pdata->power_LPM(0);
D("%s: mpu_lpm_flag = %d\n", __func__, mpu_lpm_flag);
return count;
}
static DEVICE_ATTR(mpu_lpm_flag, 0664, mpu_lpm_flag_show, \
mpu_lpm_flag_store);
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;
struct i2c_adapter *accel_adapter = NULL;
struct i2c_adapter *compass_adapter = NULL;
struct i2c_adapter *pressure_adapter = NULL;
dev_dbg(&client->adapter->dev, "%s\n", __func__);
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
res = -ENODEV;
goto out_check_functionality_failed;
}
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;
g_sensors_reset = pdata->g_sensors_reset;
#if defined(CONFIG_MPU_SENSORS_MPU3050_MODULE) || \
defined(CONFIG_MPU_SENSORS_MPU6000_MODULE)
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;
#endif
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 (pdata->accel.irq > 0) {
dev_info(&this_client->adapter->dev,
"Installing Accel irq using %d\n",
pdata->accel.irq);
res = slaveirq_init(accel_adapter,
#ifdef CONFIG_CIR_ALWAYS_READY
this_client,
#endif
&pdata->accel,
"accelirq");
if (res)
goto out_accelirq_failed;
} else {
dev_info(&this_client->adapter->dev,
"Accel irq not needed\n");
}
} else {
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 (pdata->compass.irq > 0) {
dev_info(&this_client->adapter->dev,
"Installing Compass irq using %d\n",
pdata->compass.irq);
res = slaveirq_init(compass_adapter,
#ifdef CONFIG_CIR_ALWAYS_READY
NULL,
#endif
&pdata->compass,
"compassirq");
if (res)
goto out_compassirq_failed;
} else {
dev_info(&this_client->adapter->dev,
"Compass irq not needed\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 (pdata->pressure.irq > 0) {
dev_info(&this_client->adapter->dev,
"Installing Pressure irq using %d\n",
pdata->pressure.irq);
res = slaveirq_init(pressure_adapter,
#ifdef CONFIG_CIR_ALWAYS_READY
NULL,
#endif
&pdata->pressure,
"pressureirq");
if (res)
goto out_pressureirq_failed;
} else {
dev_warn(&this_client->adapter->dev,
"WARNING: Pressure irq not assigned\n");
}
} else {
dev_info(&this_client->adapter->dev,
"%s: No Pressure Present\n", MPU_NAME);
}
}
mldl_cfg->addr = client->addr;
res = mpu3050_open(&mpu->mldl_cfg, client->adapter,
accel_adapter, compass_adapter, pressure_adapter);
if (res) {
dev_err(&this_client->adapter->dev,
"[mpu_err] Unable to open %s %d\n", MPU_NAME, res);
res = -ENODEV;
goto out_whoami_failed;
}
g_handler = client->adapter;
res = misc_register(&i2c_mpu_device);
if (res < 0) {
dev_err(&this_client->adapter->dev,
"[mpu_err] 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);
}
#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
mpu3050_class = class_create(THIS_MODULE, "gyro_sensors");
if (IS_ERR(mpu3050_class)) {
res = PTR_ERR(mpu3050_class);
mpu3050_class = NULL;
E("%s, create mpu3050_class fail!\n", __func__);
goto out_mpuirq_failed;
}
mpu3050_dev = device_create(mpu3050_class,
NULL, 0, "%s", "gyro");
if (unlikely(IS_ERR(mpu3050_dev))) {
res = PTR_ERR(mpu3050_dev);
mpu3050_dev = NULL;
E("%s, create mpu3050_dev fail!\n", __func__);
goto err_create_mpu_device;
}
res = device_create_file(mpu3050_dev, &dev_attr_pwr_reg);
if (res) {
E("%s, create mpu3050_device_create_file fail!\n", __func__);
goto err_create_mpu_device_file;
}
res = device_create_file(mpu3050_dev, &dev_attr_mpu_debug_flag);
if (res) {
E("%s, create mpu3050_device_create_file fail!\n", __func__);
goto err_create_mpu_device_mpu_debug_flag_file;
}
res = device_create_file(mpu3050_dev, &dev_attr_mpu_sensors_reset);
if (res) {
E("%s, create mpu3050_device_create_file fail!\n", __func__);
goto err_create_mpu_device_sensors_reset_flag_file;
}
res = device_create_file(mpu3050_dev, &dev_attr_mpu_lpm_flag);
if (res) {
E("%s, create mpu3050_device_create_file fail!\n", __func__);
goto err_create_mpu_device_mpu_lpm_flag_file;
}
mpu_debug_flag = 0;
mpu_sensors_reset = 0;
mpu_lpm_flag = 0;
D("%s: MPU3050 probe success v02-Fix set ODR G-Sensor issue\n",
__func__);
return res;
err_create_mpu_device_mpu_lpm_flag_file:
device_remove_file(mpu3050_dev, &dev_attr_mpu_sensors_reset);
err_create_mpu_device_sensors_reset_flag_file:
device_remove_file(mpu3050_dev, &dev_attr_mpu_debug_flag);
err_create_mpu_device_mpu_debug_flag_file:
device_remove_file(mpu3050_dev, &dev_attr_pwr_reg);
err_create_mpu_device_file:
device_unregister(mpu3050_dev);
err_create_mpu_device:
class_destroy(mpu3050_class);
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:
if (pdata &&
pdata->compass.get_slave_descr &&
pdata->compass.irq)
slaveirq_exit(&pdata->compass);
out_compassirq_failed:
if (pdata &&
pdata->accel.get_slave_descr &&
pdata->accel.irq)
slaveirq_exit(&pdata->accel);
out_accelirq_failed:
kfree(mpu);
out_alloc_data_failed:
out_check_functionality_failed:
dev_err(&this_client->adapter->dev, "[mpu_err]%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);
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,
.suspend = mpu_suspend,
.resume = mpu_resume,
};
static int __init mpu_init(void)
{
int res = i2c_add_driver(&mpu3050_driver);
pid = 0;
printk(KERN_DEBUG "%s\n", __func__);
if (res)
dev_err(&this_client->adapter->dev, "[mpu_err]%s failed\n",
__func__);
return res;
}
static void __exit mpu_exit(void)
{
printk(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);