blob: a2411c27d4fe7398be1320eba795a1f49fb3df22 [file] [log] [blame]
/*
* Copyright (C) 2012 Motorola, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Adds ability to program periodic interrupts from user space that
* can wake the phone out of low power modes.
*
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/proc_fs.h>
#include <linux/input.h>
#include <linux/m4sensorhub.h>
#include <linux/m4sensorhub/MemMapGyroSensor.h>
#include <linux/m4sensorhub/MemMapAccelSensor.h>
#include <linux/m4sensorhub/MemMapCompassSensor.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#ifdef CONFIG_DEBUG_FS
#define MPU9150_DEBUG 1
#else
#define MPU9150_DEBUG 0
#endif
#define MPU9150_CLIENT_DRIVER_NAME "m4sensorhub_mpu9150"
#define SENSOR_IRQ_ENABLE 1
#define SENSOR_IRQ_DISABLE 0
struct mpu9150_accel_data {
int x;
int y;
int z;
};
struct mpu9150_gyro_data {
int rx;
int ry;
int rz;
};
struct mpu9150_compass_data {
int cx;
int cy;
int cz;
int ca;
};
enum mpu9150_sensor {
TYPE_GYRO,
TYPE_COMPASS,
TYPE_ACCEL,
NUM_TYPES, /* Leave as last element */
} sensor;
struct mpu9150_client {
struct m4sensorhub_data *m4sensorhub;
struct input_dev *input_dev;
signed short samplerate[NUM_TYPES];
signed short latest_samplerate[NUM_TYPES];
struct mpu9150_accel_data accel_data;
struct mpu9150_gyro_data gyro_data;
struct mpu9150_compass_data compass_data;
struct mutex mutex; /* prevent concurrent thread access */
struct delayed_work mpu9150_work[NUM_TYPES];
signed short fastest_rate[NUM_TYPES];
int calibration_done;
int app_override;
};
struct mpu9150_client *misc_mpu9150_data;
static int mpu9150_client_open(struct inode *inode, struct file *file)
{
int err = 0;
err = nonseekable_open(inode, file);
if (err < 0) {
KDEBUG(M4SH_ERROR, "%s failed\n", __func__);
return err;
}
file->private_data = misc_mpu9150_data;
return 0;
}
static int mpu9150_client_close(struct inode *inode, struct file *file)
{
KDEBUG(M4SH_DEBUG, "mpu9150_client in %s\n", __func__);
return 0;
}
static void m4_report_mpu9150_inputevent(
struct mpu9150_client *mpu9150_client_data,
enum mpu9150_sensor type)
{
switch (type) {
case TYPE_GYRO:
input_report_rel(mpu9150_client_data->input_dev, REL_RX,
mpu9150_client_data->gyro_data.rx);
input_report_rel(mpu9150_client_data->input_dev, REL_RY,
mpu9150_client_data->gyro_data.ry);
input_report_rel(mpu9150_client_data->input_dev, REL_RZ,
mpu9150_client_data->gyro_data.rz);
input_sync(mpu9150_client_data->input_dev);
break;
case TYPE_ACCEL:
input_report_abs(mpu9150_client_data->input_dev, ABS_X,
mpu9150_client_data->accel_data.x);
input_report_abs(mpu9150_client_data->input_dev, ABS_Y,
mpu9150_client_data->accel_data.y);
input_report_abs(mpu9150_client_data->input_dev, ABS_Z,
mpu9150_client_data->accel_data.z);
input_sync(mpu9150_client_data->input_dev);
break;
case TYPE_COMPASS:
input_report_rel(mpu9150_client_data->input_dev, REL_X,
mpu9150_client_data->compass_data.cx);
input_report_rel(mpu9150_client_data->input_dev, REL_Y,
mpu9150_client_data->compass_data.cy);
input_report_rel(mpu9150_client_data->input_dev, REL_Z,
mpu9150_client_data->compass_data.cz);
/* TODO : accuracy needs to be sent out through sysfs*/
input_sync(mpu9150_client_data->input_dev);
break;
default:
break;
}
}
static void m4_queue_delayed_work(struct mpu9150_client *dd,
int delay, enum mpu9150_sensor type)
{
if (type == TYPE_COMPASS) {
/* initial calibration is not done and there is
no app requesting compass data */
if ((!dd->calibration_done) && (!dd->app_override))
/* For current drain saving, m4 is sampling
compass at 40ms while omap is polling
for compass data at 15 secs */
delay = 15000;
}
queue_delayed_work(system_freezable_wq,
&(dd->mpu9150_work[type]),
msecs_to_jiffies(delay));
}
static void m4_set_mpu9150_delay(struct mpu9150_client *mpu9150_client_data,
int delay, enum mpu9150_sensor type)
{
struct mpu9150_client *dd = mpu9150_client_data;
if ((delay >= 0) && (delay <= dd->fastest_rate[type]))
delay = dd->fastest_rate[type];
dd->latest_samplerate[type] = delay;
if (delay != dd->samplerate[type]) {
switch (type) {
case TYPE_GYRO:
m4sensorhub_reg_write(dd->m4sensorhub,
M4SH_REG_GYRO_SAMPLERATE,
(char *)&delay, m4sh_no_mask);
break;
case TYPE_ACCEL:
m4sensorhub_reg_write(dd->m4sensorhub,
M4SH_REG_ACCEL_SAMPLERATE,
(char *)&delay, m4sh_no_mask);
break;
case TYPE_COMPASS:
m4sensorhub_reg_write(dd->m4sensorhub,
M4SH_REG_COMPASS_SAMPLERATE,
(char *)&delay, m4sh_no_mask);
break;
default:
return;
break;
}
KDEBUG(M4SH_DEBUG, "%s() updating samplerate for type %d from"
" %d to %d\n", __func__, type,
mpu9150_client_data->samplerate[type],
delay);
cancel_delayed_work(&(dd->mpu9150_work[type]));
dd->samplerate[type] = delay;
if (dd->samplerate[type] > 0)
m4_queue_delayed_work(dd, delay, type);
}
}
static void m4_read_mpu9150_data(struct mpu9150_client *mpu9150_client_data,
enum mpu9150_sensor type)
{
sCompassData compassdata;
sAccelData acceldata;
sGyroData gyrodata;
struct mpu9150_client *dd = mpu9150_client_data;
switch (type) {
case TYPE_GYRO:
m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
M4SH_REG_GYRO_X, (char *)&gyrodata.x);
m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
M4SH_REG_GYRO_Y, (char *)&gyrodata.y);
m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
M4SH_REG_GYRO_Z, (char *)&gyrodata.z);
mpu9150_client_data->gyro_data.rx = gyrodata.x;
mpu9150_client_data->gyro_data.ry = gyrodata.y;
mpu9150_client_data->gyro_data.rz = gyrodata.z;
break;
case TYPE_ACCEL:
m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
M4SH_REG_ACCEL_X, (char *)&acceldata.x);
m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
M4SH_REG_ACCEL_Y, (char *)&acceldata.y);
m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
M4SH_REG_ACCEL_Z, (char *)&acceldata.z);
mpu9150_client_data->accel_data.x = acceldata.x;
mpu9150_client_data->accel_data.y = acceldata.y;
mpu9150_client_data->accel_data.z = acceldata.z;
break;
case TYPE_COMPASS:
m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
M4SH_REG_COMPASS_X, (char *)&compassdata.x);
m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
M4SH_REG_COMPASS_Y, (char *)&compassdata.y);
m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
M4SH_REG_COMPASS_Z, (char *)&compassdata.z);
m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
M4SH_REG_COMPASS_ACCURACY,
(char *)&compassdata.accuracy);
mpu9150_client_data->compass_data.cx = compassdata.x;
mpu9150_client_data->compass_data.cy = compassdata.y;
mpu9150_client_data->compass_data.cz = compassdata.z;
mpu9150_client_data->compass_data.ca = compassdata.accuracy;
/* Check if calibration is complete */
if ((!(dd->calibration_done)) && (compassdata.accuracy)) {
dd->calibration_done = 1;
KDEBUG(M4SH_INFO, "Calibration complete\n");
/* Stop compass sampling if no app is using the data */
if (dd->app_override == 0) {
m4_set_mpu9150_delay(dd,
-1,
TYPE_COMPASS);
KDEBUG(M4SH_INFO, "Init cal done. Turning off compass");
}
}
break;
default:
break;
}
}
static void m4gyro_work_func(struct work_struct *work)
{
struct mpu9150_client *dd =
container_of(work,
struct mpu9150_client,
mpu9150_work[TYPE_GYRO].work);
signed short rate;
mutex_lock(&(dd->mutex));
m4_read_mpu9150_data(dd, TYPE_GYRO);
m4_report_mpu9150_inputevent(dd, TYPE_GYRO);
rate = dd->samplerate[TYPE_GYRO];
if (rate > 0)
queue_delayed_work(system_freezable_wq,
&(dd->mpu9150_work[TYPE_GYRO]),
msecs_to_jiffies(rate));
mutex_unlock(&(dd->mutex));
}
static void m4accel_work_func(struct work_struct *work)
{
struct mpu9150_client *dd =
container_of(work,
struct mpu9150_client,
mpu9150_work[TYPE_ACCEL].work);
signed short rate;
mutex_lock(&(dd->mutex));
m4_read_mpu9150_data(dd, TYPE_ACCEL);
m4_report_mpu9150_inputevent(dd, TYPE_ACCEL);
rate = dd->samplerate[TYPE_ACCEL];
if (rate > 0)
queue_delayed_work(system_freezable_wq,
&(dd->mpu9150_work[TYPE_ACCEL]),
msecs_to_jiffies(rate));
mutex_unlock(&(dd->mutex));
}
static void m4compass_work_func(struct work_struct *work)
{
struct mpu9150_client *dd =
container_of(work,
struct mpu9150_client,
mpu9150_work[TYPE_COMPASS].work);
signed short rate;
mutex_lock(&(dd->mutex));
m4_read_mpu9150_data(dd, TYPE_COMPASS);
m4_report_mpu9150_inputevent(dd, TYPE_COMPASS);
rate = dd->samplerate[TYPE_COMPASS];
if (rate > 0)
m4_queue_delayed_work(dd, rate, TYPE_COMPASS);
mutex_unlock(&(dd->mutex));
}
static ssize_t m4_mpu9150_write_accel_setdelay(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int scanresult, err;
err = kstrtoint(buf, 10, &scanresult);
if (err < 0) {
KDEBUG(M4SH_ERROR, "%s: conversion failed\n", __func__);
return err;
}
/* Input validation */
if (scanresult < -1) {
KDEBUG(
M4SH_ERROR, "%s() invalid input %d\n",
__func__ , scanresult
);
return -EINVAL;
}
mutex_lock(&(misc_mpu9150_data->mutex));
m4_set_mpu9150_delay(misc_mpu9150_data, scanresult, TYPE_ACCEL);
mutex_unlock(&(misc_mpu9150_data->mutex));
return count;
}
static DEVICE_ATTR(accel_setdelay, S_IWUSR, NULL,
m4_mpu9150_write_accel_setdelay);
static ssize_t m4_mpu9150_write_gyro_setdelay(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int scanresult, err;
err = kstrtoint(buf, 10, &scanresult);
if (err < 0) {
KDEBUG(M4SH_ERROR, "%s: conversion failed\n", __func__);
return err;
}
/* Input validation */
if (scanresult < -1) {
KDEBUG(
M4SH_ERROR, "%s() invalid input %d\n",
__func__ , scanresult
);
return -EINVAL;
}
mutex_lock(&(misc_mpu9150_data->mutex));
m4_set_mpu9150_delay(misc_mpu9150_data, scanresult, TYPE_GYRO);
mutex_unlock(&(misc_mpu9150_data->mutex));
return count;
}
static DEVICE_ATTR(gyro_setdelay, S_IWUSR, NULL,
m4_mpu9150_write_gyro_setdelay);
static ssize_t m4_mpu9150_write_compass_setdelay(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int scanresult, err;
err = kstrtoint(buf, 10, &scanresult);
if (err < 0) {
KDEBUG(M4SH_ERROR, "%s: conversion failed\n", __func__);
return err;
}
/* Input validation */
if (scanresult < -1) {
KDEBUG(
M4SH_ERROR, "%s() invalid input %d\n",
__func__ , scanresult
);
return -EINVAL;
}
mutex_lock(&(misc_mpu9150_data->mutex));
if (misc_mpu9150_data->calibration_done == 0) {
/* If calibration is not complete and app tries to
turn off ignore */
if (scanresult < 0) {
misc_mpu9150_data->app_override = 0;
goto compass_setdelay_exit;
} else {
misc_mpu9150_data->app_override = 1;
}
}
m4_set_mpu9150_delay(misc_mpu9150_data, scanresult, TYPE_COMPASS);
compass_setdelay_exit:
mutex_unlock(&(misc_mpu9150_data->mutex));
return count;
}
static DEVICE_ATTR(compass_setdelay, S_IWUSR, NULL,
m4_mpu9150_write_compass_setdelay);
static struct attribute *mpu9150_control_attributes[] = {
&dev_attr_accel_setdelay.attr,
&dev_attr_gyro_setdelay.attr,
&dev_attr_compass_setdelay.attr,
NULL
};
static const struct attribute_group mpu9150_control_group = {
.attrs = mpu9150_control_attributes,
};
#ifdef MPU9150_DEBUG
static ssize_t m4_mpu9150_x(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
KDEBUG(M4SH_DEBUG, "%s : raw x = %d\n",
__func__, mpu9150_client_data->accel_data.x);
return sprintf(buf, "%d\n", mpu9150_client_data->accel_data.x);
}
static ssize_t m4_mpu9150_y(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
KDEBUG(M4SH_DEBUG, "%s : raw y = %d\n",
__func__, mpu9150_client_data->accel_data.y);
return sprintf(buf, "%d\n", mpu9150_client_data->accel_data.y);
}
static ssize_t m4_mpu9150_z(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
KDEBUG(M4SH_DEBUG, "%s : raw z = %d\n",
__func__, mpu9150_client_data->accel_data.z);
return sprintf(buf, "%d\n", mpu9150_client_data->accel_data.z);
}
static ssize_t m4_mpu9150_cx(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
KDEBUG(M4SH_DEBUG, "%s : compass cx = %d\n",
__func__, mpu9150_client_data->compass_data.cx);
return sprintf(buf, "%d\n", mpu9150_client_data->compass_data.cx);
}
static ssize_t m4_mpu9150_cy(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
KDEBUG(M4SH_DEBUG, "%s : compass cy = %d\n",
__func__, mpu9150_client_data->compass_data.cy);
return sprintf(buf, "%d\n", mpu9150_client_data->compass_data.cy);
}
static ssize_t m4_mpu9150_cz(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
KDEBUG(M4SH_DEBUG, "%s : compass cz = %d\n",
__func__, mpu9150_client_data->compass_data.cz);
return sprintf(buf, "%d\n", mpu9150_client_data->compass_data.cz);
}
static ssize_t m4_mpu9150_ca(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
KDEBUG(M4SH_DEBUG, "%s : compass ca = %d\n",
__func__, mpu9150_client_data->compass_data.ca);
return sprintf(buf, "%d\n", mpu9150_client_data->compass_data.ca);
}
static ssize_t m4_mpu9150_rx(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
KDEBUG(M4SH_DEBUG, "%s : rx = %d\n",
__func__, mpu9150_client_data->gyro_data.rx);
return sprintf(buf, "%d\n", mpu9150_client_data->gyro_data.rx);
}
static ssize_t m4_mpu9150_ry(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
KDEBUG(M4SH_DEBUG, "%s : ry = %d\n",
__func__, mpu9150_client_data->gyro_data.ry);
return sprintf(buf, "%d\n", mpu9150_client_data->gyro_data.ry);
}
static ssize_t m4_mpu9150_rz(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
KDEBUG(M4SH_DEBUG, "%s : rz = %d\n",
__func__, mpu9150_client_data->gyro_data.rz);
return sprintf(buf, "%d\n", mpu9150_client_data->gyro_data.rz);
}
static DEVICE_ATTR(raw_x, 0444, m4_mpu9150_x, NULL);
static DEVICE_ATTR(raw_y, 0444, m4_mpu9150_y, NULL);
static DEVICE_ATTR(raw_z, 0444, m4_mpu9150_z, NULL);
static DEVICE_ATTR(compass_cx, 0444, m4_mpu9150_cx, NULL);
static DEVICE_ATTR(compass_cy, 0444, m4_mpu9150_cy, NULL);
static DEVICE_ATTR(compass_cz, 0444, m4_mpu9150_cz, NULL);
static DEVICE_ATTR(compass_ca, 0444, m4_mpu9150_ca, NULL);
static DEVICE_ATTR(rx, 0444, m4_mpu9150_rx, NULL);
static DEVICE_ATTR(ry, 0444, m4_mpu9150_ry, NULL);
static DEVICE_ATTR(rz, 0444, m4_mpu9150_rz, NULL);
static struct attribute *mpu9150_attributes[] = {
&dev_attr_raw_x.attr,
&dev_attr_raw_y.attr,
&dev_attr_raw_z.attr,
&dev_attr_compass_cx.attr,
&dev_attr_compass_cy.attr,
&dev_attr_compass_cz.attr,
&dev_attr_compass_ca.attr,
&dev_attr_rx.attr,
&dev_attr_ry.attr,
&dev_attr_rz.attr,
NULL
};
static const struct attribute_group mpu9150_group = {
.attrs = mpu9150_attributes,
};
#endif
static const struct file_operations mpu9150_client_fops = {
.owner = THIS_MODULE,
.open = mpu9150_client_open,
.release = mpu9150_client_close,
};
static struct miscdevice mpu9150_client_miscdrv = {
.minor = MISC_DYNAMIC_MINOR,
.name = MPU9150_CLIENT_DRIVER_NAME,
.fops = &mpu9150_client_fops,
};
static void mpu9150_panic_restore(struct m4sensorhub_data *m4sensorhub,
void *data)
{
struct mpu9150_client *dd = (struct mpu9150_client *)data;
int type;
signed short rate;
if (dd == NULL) {
KDEBUG(M4SH_INFO, "%s: Driver data is null,unable to restore\n",
__func__);
return;
}
mutex_lock(&(dd->mutex));
KDEBUG(M4SH_INFO, "Executing mpu9150 panic restore\n");
for (type = 0; type < NUM_TYPES; type++) {
rate = dd->samplerate[type];
m4_set_mpu9150_delay(dd, rate, type);
cancel_delayed_work(&(dd->mpu9150_work[type]));
if (rate > 0)
m4_queue_delayed_work(dd, rate, type);
}
mutex_unlock(&(dd->mutex));
}
static int mpu9150_driver_init(struct init_calldata *p_arg)
{
int ret;
struct mpu9150_client *dd = p_arg->p_data;
mutex_lock(&(dd->mutex));
INIT_DELAYED_WORK(&(dd->mpu9150_work[TYPE_ACCEL]), m4accel_work_func);
INIT_DELAYED_WORK(&(dd->mpu9150_work[TYPE_COMPASS]),
m4compass_work_func);
INIT_DELAYED_WORK(&(dd->mpu9150_work[TYPE_GYRO]),
m4gyro_work_func);
ret = m4sensorhub_panic_register(dd->m4sensorhub,
PANICHDL_MPU9150_RESTORE,
mpu9150_panic_restore,
dd);
if (ret < 0)
KDEBUG(M4SH_ERROR, "HR panic callback register failed\n");
m4_set_mpu9150_delay(dd,
40,
TYPE_COMPASS);
mutex_unlock(&(dd->mutex));
return ret;
}
static int mpu9150_client_probe(struct platform_device *pdev)
{
int ret = -1;
struct mpu9150_client *mpu9150_client_data;
struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata();
if (!m4sensorhub)
return -EFAULT;
mpu9150_client_data = kzalloc(sizeof(*mpu9150_client_data),
GFP_KERNEL);
if (!mpu9150_client_data)
return -ENOMEM;
mpu9150_client_data->m4sensorhub = m4sensorhub;
platform_set_drvdata(pdev, mpu9150_client_data);
mpu9150_client_data->samplerate[TYPE_ACCEL] = -1;
mpu9150_client_data->samplerate[TYPE_GYRO] = -1;
mpu9150_client_data->samplerate[TYPE_COMPASS] = -1;
mpu9150_client_data->latest_samplerate[TYPE_ACCEL] =
mpu9150_client_data->samplerate[TYPE_ACCEL];
mpu9150_client_data->latest_samplerate[TYPE_GYRO] =
mpu9150_client_data->samplerate[TYPE_GYRO];
mpu9150_client_data->latest_samplerate[TYPE_COMPASS] =
mpu9150_client_data->samplerate[TYPE_COMPASS];
mpu9150_client_data->fastest_rate[TYPE_ACCEL] = 40;
mpu9150_client_data->fastest_rate[TYPE_GYRO] = 40;
mpu9150_client_data->fastest_rate[TYPE_COMPASS] = 40;
mpu9150_client_data->calibration_done = 0;
mpu9150_client_data->app_override = 0;
mpu9150_client_data->input_dev = input_allocate_device();
if (!mpu9150_client_data->input_dev) {
ret = -ENOMEM;
KDEBUG(M4SH_ERROR, "%s: input device allocate failed: %d\n",
__func__, ret);
goto free_mem;
}
mpu9150_client_data->input_dev->name = MPU9150_CLIENT_DRIVER_NAME;
set_bit(EV_ABS, mpu9150_client_data->input_dev->evbit);
set_bit(EV_REL, mpu9150_client_data->input_dev->evbit);
input_set_abs_params(mpu9150_client_data->input_dev,
ABS_X, -2147483647, 2147483647, 0, 0);
input_set_abs_params(mpu9150_client_data->input_dev,
ABS_Y, -2147483647, 2147483647, 0, 0);
input_set_abs_params(mpu9150_client_data->input_dev,
ABS_Z, -2147483647, 2147483647, 0, 0);
set_bit(REL_X, mpu9150_client_data->input_dev->relbit);
set_bit(REL_Y, mpu9150_client_data->input_dev->relbit);
set_bit(REL_Z, mpu9150_client_data->input_dev->relbit);
set_bit(REL_RX, mpu9150_client_data->input_dev->relbit);
set_bit(REL_RY, mpu9150_client_data->input_dev->relbit);
set_bit(REL_RZ, mpu9150_client_data->input_dev->relbit);
if (input_register_device(mpu9150_client_data->input_dev)) {
KDEBUG(M4SH_ERROR, "%s: input device register failed\n",
__func__);
input_free_device(mpu9150_client_data->input_dev);
goto free_mem;
}
ret = misc_register(&mpu9150_client_miscdrv);
if (ret < 0) {
KDEBUG(M4SH_ERROR, "Error registering %s driver\n", __func__);
goto unregister_input_device;
}
misc_mpu9150_data = mpu9150_client_data;
mutex_init(&(misc_mpu9150_data->mutex));
ret = m4sensorhub_register_initcall(mpu9150_driver_init,
mpu9150_client_data);
if (ret < 0) {
KDEBUG(M4SH_ERROR, "Unable to register init function"
"for mpu9150 client = %d\n", ret);
goto unregister_misc_device;
}
ret = sysfs_create_group(&pdev->dev.kobj, &mpu9150_control_group);
if (ret)
goto unregister_initcall;
#ifdef MPU9150_DEBUG
ret = sysfs_create_group(&pdev->dev.kobj, &mpu9150_group);
if (ret)
goto unregister_control_group;
#endif
KDEBUG(M4SH_INFO, "Initialized %s driver\n", __func__);
return 0;
#ifdef MPU9150_DEBUG
unregister_control_group:
sysfs_remove_group(&pdev->dev.kobj, &mpu9150_control_group);
#endif
unregister_initcall:
m4sensorhub_unregister_initcall(mpu9150_driver_init);
unregister_misc_device:
mutex_destroy(&(misc_mpu9150_data->mutex));
misc_mpu9150_data = NULL;
misc_deregister(&mpu9150_client_miscdrv);
unregister_input_device:
input_unregister_device(mpu9150_client_data->input_dev);
free_mem:
platform_set_drvdata(pdev, NULL);
mpu9150_client_data->m4sensorhub = NULL;
kfree(mpu9150_client_data);
mpu9150_client_data = NULL;
return ret;
}
static int __exit mpu9150_client_remove(struct platform_device *pdev)
{
struct mpu9150_client *mpu9150_client_data =
platform_get_drvdata(pdev);
if (mpu9150_client_data == NULL)
return 0;
mutex_lock(&(mpu9150_client_data->mutex));
sysfs_remove_group(&pdev->dev.kobj, &mpu9150_control_group);
#ifdef MPU9150_DEBUG
sysfs_remove_group(&pdev->dev.kobj, &mpu9150_group);
#endif
cancel_delayed_work(&(mpu9150_client_data->mpu9150_work[TYPE_COMPASS]));
cancel_delayed_work(&(mpu9150_client_data->mpu9150_work[TYPE_ACCEL]));
cancel_delayed_work(&(mpu9150_client_data->mpu9150_work[TYPE_GYRO]));
m4sensorhub_unregister_initcall(mpu9150_driver_init);
misc_mpu9150_data = NULL;
misc_deregister(&mpu9150_client_miscdrv);
input_unregister_device(mpu9150_client_data->input_dev);
platform_set_drvdata(pdev, NULL);
mpu9150_client_data->m4sensorhub = NULL;
mutex_destroy(&(mpu9150_client_data->mutex));
kfree(mpu9150_client_data);
mpu9150_client_data = NULL;
return 0;
}
static int mpu9150_client_suspend(struct platform_device *pdev,
pm_message_t state)
{
struct mpu9150_client *dd = platform_get_drvdata(pdev);
if (dd == NULL) {
KDEBUG(M4SH_ERROR, "%s: Driver data is NULL--%s.\n",
__func__, "cannot suspend");
return 0;
}
m4_set_mpu9150_delay(dd, dd->latest_samplerate[TYPE_ACCEL], TYPE_ACCEL);
m4_set_mpu9150_delay(dd, dd->latest_samplerate[TYPE_GYRO], TYPE_GYRO);
m4_set_mpu9150_delay(dd, dd->latest_samplerate[TYPE_COMPASS],
TYPE_COMPASS);
return 0;
}
static struct of_device_id m4mpu9150_match_tbl[] = {
{ .compatible = "mot,m4mpu9150" },
{},
};
static struct platform_driver mpu9150_client_driver = {
.probe = mpu9150_client_probe,
.remove = __exit_p(mpu9150_client_remove),
.shutdown = NULL,
.suspend = mpu9150_client_suspend,
.resume = NULL,
.driver = {
.name = MPU9150_CLIENT_DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(m4mpu9150_match_tbl),
},
};
static int __init mpu9150_client_init(void)
{
return platform_driver_register(&mpu9150_client_driver);
}
static void __exit mpu9150_client_exit(void)
{
platform_driver_unregister(&mpu9150_client_driver);
}
module_init(mpu9150_client_init);
module_exit(mpu9150_client_exit);
MODULE_ALIAS("platform:mpu9150_client");
MODULE_DESCRIPTION("M4 Sensor Hub Mpu9150 client driver");
MODULE_AUTHOR("Motorola");
MODULE_LICENSE("GPL");