All of lore.kernel.org
 help / color / mirror / Atom feed
From: Nathan Royer <nroyer@invensense.com>
To: Nathan Royer <nroyer@invensense.com>
Cc: Andrew Morton <akpm@linux-foundation.org>,
	Greg Kroah-Hartman <gregkh@suse.de>,
	Jonathan Cameron <jic23@cam.ac.uk>, Jiri Kosina <jkosina@suse.cz>,
	Alan Cox <alan@linux.intel.com>,
	Jean Delvare <khali@linux-fr.org>,
	linux-kernel@vger.kernel.org, linux-input@vger.kernel.org
Subject: [PATCH 03/11] misc: mpu3050 /dev/mpu implementation.
Date: Thu, 30 Jun 2011 19:18:19 -0700	[thread overview]
Message-ID: <1309486707-1658-3-git-send-email-nroyer@invensense.com> (raw)
In-Reply-To: <1309486707-1658-1-git-send-email-nroyer@invensense.com>

Implements i2c probing, receives registration of slaves, handles file
operations, and power management.

Signed-off-by: Nathan Royer <nroyer@invensense.com>
---
 drivers/misc/inv_mpu/mpu-dev.c | 1247 ++++++++++++++++++++++++++++++++++++++++
 drivers/misc/inv_mpu/mpu-dev.h |   36 ++
 2 files changed, 1283 insertions(+), 0 deletions(-)
 create mode 100644 drivers/misc/inv_mpu/mpu-dev.c
 create mode 100644 drivers/misc/inv_mpu/mpu-dev.h

diff --git a/drivers/misc/inv_mpu/mpu-dev.c b/drivers/misc/inv_mpu/mpu-dev.c
new file mode 100644
index 0000000..977631d
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu-dev.c
@@ -0,0 +1,1247 @@
+/*
+	$License:
+	Copyright (C) 2011 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>
+#include <linux/mutex.h>
+#include <linux/suspend.h>
+#include <linux/poll.h>
+
+#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 "mldl_cfg.h"
+#include <linux/mpu.h>
+
+
+/* Platform data for the MPU */
+struct mpu_private_data {
+	struct miscdevice dev;
+	struct i2c_client *client;
+
+	/* mldl_cfg data */
+	struct mldl_cfg mldl_cfg;
+	struct mpu_ram		mpu_ram;
+	struct mpu_gyro_cfg	mpu_gyro_cfg;
+	struct mpu_offsets	mpu_offsets;
+	struct mpu_chip_info	mpu_chip_info;
+	struct inv_mpu_cfg	inv_mpu_cfg;
+	struct inv_mpu_state	inv_mpu_state;
+
+	struct mutex mutex;
+	wait_queue_head_t mpu_event_wait;
+	struct completion completion;
+	struct timer_list timeout;
+	struct notifier_block nb;
+	struct mpuirq_data mpu_pm_event;
+	int response_timeout;	/* In seconds */
+	unsigned long event;
+	int pid;
+	struct module *slave_modules[EXT_SLAVE_NUM_TYPES];
+};
+
+struct mpu_private_data *mpu_private_data;
+
+static void mpu_pm_timeout(u_long data)
+{
+	struct mpu_private_data *mpu = (struct mpu_private_data *)data;
+	struct i2c_client *client = mpu->client;
+	dev_dbg(&client->adapter->dev, "%s\n", __func__);
+	complete(&mpu->completion);
+}
+
+static int mpu_pm_notifier_callback(struct notifier_block *nb,
+				    unsigned long event, void *unused)
+{
+	struct mpu_private_data *mpu =
+	    container_of(nb, struct mpu_private_data, nb);
+	struct i2c_client *client = mpu->client;
+	struct timeval event_time;
+	dev_dbg(&client->adapter->dev, "%s: %ld\n", __func__, event);
+
+	/* Prevent the file handle from being closed before we initialize
+	   the completion event */
+	mutex_lock(&mpu->mutex);
+	if (!(mpu->pid) ||
+	    (event != PM_SUSPEND_PREPARE && event != PM_POST_SUSPEND)) {
+		mutex_unlock(&mpu->mutex);
+		return NOTIFY_OK;
+	}
+
+	do_gettimeofday(&event_time);
+	mpu->mpu_pm_event.interruptcount++;
+	mpu->mpu_pm_event.irqtime =
+	    (((long long)event_time.tv_sec) << 32) + event_time.tv_usec;
+	mpu->mpu_pm_event.data_type = MPUIRQ_DATA_TYPE_PM_EVENT;
+	mpu->mpu_pm_event.data = mpu->event;
+
+	if (event == PM_SUSPEND_PREPARE)
+		mpu->event = MPU_PM_EVENT_SUSPEND_PREPARE;
+	if (event == PM_POST_SUSPEND)
+		mpu->event = MPU_PM_EVENT_POST_SUSPEND;
+
+	if (mpu->response_timeout > 0) {
+		mpu->timeout.expires = jiffies + mpu->response_timeout * HZ;
+		add_timer(&mpu->timeout);
+	}
+	INIT_COMPLETION(mpu->completion);
+	mutex_unlock(&mpu->mutex);
+
+	wake_up_interruptible(&mpu->mpu_event_wait);
+	wait_for_completion(&mpu->completion);
+	del_timer_sync(&mpu->timeout);
+	dev_dbg(&client->adapter->dev, "%s: %ld DONE\n", __func__, event);
+	return NOTIFY_OK;
+}
+
+static int mpu_dev_open(struct inode *inode, struct file *file)
+{
+	struct mpu_private_data *mpu =
+	    container_of(file->private_data, struct mpu_private_data, dev);
+	struct i2c_client *client = mpu->client;
+	int result;
+	int ii;
+	dev_dbg(&client->adapter->dev, "%s\n", __func__);
+	dev_dbg(&client->adapter->dev, "current->pid %d\n", current->pid);
+
+	result = mutex_lock_interruptible(&mpu->mutex);
+	if (mpu->pid) {
+		mutex_unlock(&mpu->mutex);
+		return -EBUSY;
+	}
+	mpu->pid = current->pid;
+
+	/* Reset the sensors to the default */
+	if (result) {
+		dev_err(&client->adapter->dev,
+			"%s: mutex_lock_interruptible returned %d\n",
+			__func__, result);
+		return result;
+	}
+
+	for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++)
+		__module_get(mpu->slave_modules[ii]);
+
+	mutex_unlock(&mpu->mutex);
+	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 mpu_private_data *mpu =
+	    container_of(file->private_data, struct mpu_private_data, dev);
+	struct i2c_client *client = mpu->client;
+	struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+	int result = 0;
+	int ii;
+	struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
+	struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
+
+	for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
+		if (!pdata_slave[ii])
+			slave_adapter[ii] = NULL;
+		else
+			slave_adapter[ii] =
+				i2c_get_adapter(pdata_slave[ii]->adapt_num);
+	}
+	slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
+
+	mutex_lock(&mpu->mutex);
+	mldl_cfg->inv_mpu_cfg->requested_sensors = 0;
+	result = inv_mpu_suspend(mldl_cfg,
+				slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+				slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+				slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+				slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+				INV_ALL_SENSORS);
+	mpu->pid = 0;
+	for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++)
+		module_put(mpu->slave_modules[ii]);
+
+	mutex_unlock(&mpu->mutex);
+	complete(&mpu->completion);
+	dev_dbg(&client->adapter->dev, "mpu_release\n");
+
+	return result;
+}
+
+/* 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)
+{
+	struct mpu_private_data *mpu =
+	    container_of(file->private_data, struct mpu_private_data, dev);
+	struct i2c_client *client = mpu->client;
+	size_t len = sizeof(mpu->mpu_pm_event) + sizeof(unsigned long);
+	int err;
+
+	if (!mpu->event && (!(file->f_flags & O_NONBLOCK)))
+		wait_event_interruptible(mpu->mpu_event_wait, mpu->event);
+
+	if (!mpu->event || !buf
+	    || count < sizeof(mpu->mpu_pm_event) + sizeof(unsigned long))
+		return 0;
+
+	err = copy_to_user(buf, &mpu->mpu_pm_event, sizeof(mpu->mpu_pm_event));
+	if (err) {
+		dev_err(&client->adapter->dev,
+			"Copy to user returned %d\n", err);
+		return -EFAULT;
+	}
+	mpu->event = 0;
+	return len;
+}
+
+static unsigned int mpu_poll(struct file *file, struct poll_table_struct *poll)
+{
+	struct mpu_private_data *mpu =
+	    container_of(file->private_data, struct mpu_private_data, dev);
+	int mask = 0;
+
+	poll_wait(file, &mpu->mpu_event_wait, poll);
+	if (mpu->event)
+		mask |= POLLIN | POLLRDNORM;
+	return mask;
+}
+
+static int mpu_dev_ioctl_get_ext_slave_platform_data(
+	struct i2c_client *client,
+	struct ext_slave_platform_data __user *arg)
+{
+	struct mpu_private_data *mpu =
+	    (struct mpu_private_data *)i2c_get_clientdata(client);
+	struct ext_slave_platform_data *pdata_slave;
+	struct ext_slave_platform_data local_pdata_slave;
+
+	if (copy_from_user(&local_pdata_slave, arg, sizeof(local_pdata_slave)))
+		return -EFAULT;
+
+	if (local_pdata_slave.type >= EXT_SLAVE_NUM_TYPES)
+		return -EINVAL;
+
+	pdata_slave = mpu->mldl_cfg.pdata_slave[local_pdata_slave.type];
+	/* All but private data and irq_data */
+	if (!pdata_slave)
+		return -ENODEV;
+	if (copy_to_user(arg, pdata_slave, sizeof(*pdata_slave)))
+		return -EFAULT;
+	return 0;
+}
+
+static int mpu_dev_ioctl_get_mpu_platform_data(
+	struct i2c_client *client,
+	struct mpu_platform_data __user *arg)
+{
+	struct mpu_private_data *mpu =
+	    (struct mpu_private_data *)i2c_get_clientdata(client);
+	struct mpu_platform_data *pdata = mpu->mldl_cfg.pdata;
+
+	if (copy_to_user(arg, pdata, sizeof(pdata)))
+		return -EFAULT;
+	return 0;
+}
+
+static int mpu_dev_ioctl_get_ext_slave_descr(
+	struct i2c_client *client,
+	struct ext_slave_descr __user *arg)
+{
+	struct mpu_private_data *mpu =
+	    (struct mpu_private_data *)i2c_get_clientdata(client);
+	struct ext_slave_descr *slave;
+	struct ext_slave_descr local_slave;
+
+	if (copy_from_user(&local_slave, arg, sizeof(local_slave)))
+		return -EFAULT;
+
+	if (local_slave.type >= EXT_SLAVE_NUM_TYPES)
+		return -EINVAL;
+
+	slave = mpu->mldl_cfg.slave[local_slave.type];
+	/* All but private data and irq_data */
+	if (!slave)
+		return -ENODEV;
+	if (copy_to_user(arg, slave, sizeof(*slave)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/**
+ * slave_config() - Pass a requested slave configuration to the slave sensor
+ *
+ * @adapter the adaptor to use to communicate with the slave
+ * @mldl_cfg the mldl configuration structuer
+ * @slave pointer to the slave descriptor
+ * @usr_config The configuration to pass to the slave sensor
+ *
+ * returns 0 or non-zero error code
+ */
+static int inv_mpu_config(struct mldl_cfg *mldl_cfg,
+			void *gyro_adapter,
+			struct ext_slave_config __user *usr_config)
+{
+	int retval = 0;
+	struct ext_slave_config config;
+
+	retval = copy_from_user(&config, usr_config, sizeof(config));
+	if (retval)
+		return -EFAULT;
+
+	if (config.len && config.data) {
+		void *data;
+		data = kmalloc(config.len, GFP_KERNEL);
+		if (!data)
+			return -ENOMEM;
+
+		retval = copy_from_user(data,
+					(void __user *)config.data, config.len);
+		if (retval) {
+			retval = -EFAULT;
+			kfree(data);
+			return retval;
+		}
+		config.data = data;
+	}
+	retval = gyro_config(gyro_adapter, mldl_cfg, &config);
+	kfree(config.data);
+	return retval;
+}
+
+static int inv_mpu_get_config(struct mldl_cfg *mldl_cfg,
+			    void *gyro_adapter,
+			    struct ext_slave_config __user *usr_config)
+{
+	int retval = 0;
+	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) {
+		void *data;
+		data = kmalloc(config.len, GFP_KERNEL);
+		if (!data)
+			return -ENOMEM;
+
+		retval = copy_from_user(data,
+					(void __user *)config.data, config.len);
+		if (retval) {
+			retval = -EFAULT;
+			kfree(data);
+			return retval;
+		}
+		config.data = data;
+	}
+	retval = gyro_get_config(gyro_adapter, mldl_cfg, &config);
+	if (!retval)
+		retval = copy_to_user((unsigned char __user *)user_data,
+				config.data, config.len);
+	kfree(config.data);
+	return retval;
+}
+
+static int slave_config(struct mldl_cfg *mldl_cfg,
+			void *gyro_adapter,
+			void *slave_adapter,
+			struct ext_slave_descr *slave,
+			struct ext_slave_platform_data *pdata,
+			struct ext_slave_config __user *usr_config)
+{
+	int retval = 0;
+	struct ext_slave_config config;
+	if ((!slave) || (!slave->config))
+		return -ENODEV;
+
+	retval = copy_from_user(&config, usr_config, sizeof(config));
+	if (retval)
+		return -EFAULT;
+
+	if (config.len && config.data) {
+		void *data;
+		data = kmalloc(config.len, GFP_KERNEL);
+		if (!data)
+			return -ENOMEM;
+
+		retval = copy_from_user(data,
+					(void __user *)config.data, config.len);
+		if (retval) {
+			retval = -EFAULT;
+			kfree(data);
+			return retval;
+		}
+		config.data = data;
+	}
+	retval = inv_mpu_slave_config(mldl_cfg, gyro_adapter, slave_adapter,
+				      &config, slave, pdata);
+	kfree(config.data);
+	return retval;
+}
+
+static int slave_get_config(struct mldl_cfg *mldl_cfg,
+			    void *gyro_adapter,
+			    void *slave_adapter,
+			    struct ext_slave_descr *slave,
+			    struct ext_slave_platform_data *pdata,
+			    struct ext_slave_config __user *usr_config)
+{
+	int retval = 0;
+	struct ext_slave_config config;
+	void *user_data;
+	if (!(slave) || !(slave->get_config))
+		return -ENODEV;
+
+	retval = copy_from_user(&config, usr_config, sizeof(config));
+	if (retval)
+		return -EFAULT;
+
+	user_data = config.data;
+	if (config.len && config.data) {
+		void *data;
+		data = kmalloc(config.len, GFP_KERNEL);
+		if (!data)
+			return -ENOMEM;
+
+		retval = copy_from_user(data,
+					(void __user *)config.data, config.len);
+		if (retval) {
+			retval = -EFAULT;
+			kfree(data);
+			return retval;
+		}
+		config.data = data;
+	}
+	retval = inv_mpu_get_slave_config(mldl_cfg, gyro_adapter,
+					  slave_adapter, &config, slave, pdata);
+	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 int inv_slave_read(struct mldl_cfg *mldl_cfg,
+			  void *gyro_adapter,
+			  void *slave_adapter,
+			  struct ext_slave_descr *slave,
+			  struct ext_slave_platform_data *pdata,
+			  void __user *usr_data)
+{
+	int retval;
+	unsigned char *data;
+	data = kzalloc(slave->read_len, GFP_KERNEL);
+	if (!data)
+		return -EFAULT;
+
+	retval = inv_mpu_slave_read(mldl_cfg, gyro_adapter, slave_adapter,
+				    slave, pdata, data);
+
+	if ((!retval) &&
+	    (copy_to_user((unsigned char __user *)usr_data,
+			  data, slave->read_len)))
+		retval = -EFAULT;
+
+	kfree(data);
+	return retval;
+}
+
+static int mpu_handle_mlsl(void *sl_handle,
+			   unsigned char addr,
+			   unsigned int cmd,
+			   struct mpu_read_write __user *usr_msg)
+{
+	int retval = 0;
+	struct mpu_read_write msg;
+	unsigned char *user_data;
+	retval = copy_from_user(&msg, usr_msg, sizeof(msg));
+	if (retval)
+		return -EFAULT;
+
+	user_data = msg.data;
+	if (msg.length && msg.data) {
+		unsigned char *data;
+		data = kmalloc(msg.length, GFP_KERNEL);
+		if (!data)
+			return -ENOMEM;
+
+		retval = copy_from_user(data,
+					(void __user *)msg.data, msg.length);
+		if (retval) {
+			retval = -EFAULT;
+			kfree(data);
+			return retval;
+		}
+		msg.data = data;
+	} else {
+		return -EPERM;
+	}
+
+	switch (cmd) {
+	case MPU_READ:
+		retval = inv_serial_read(sl_handle, addr,
+					 msg.address, msg.length, msg.data);
+		break;
+	case MPU_WRITE:
+		retval = inv_serial_write(sl_handle, addr,
+					  msg.length, msg.data);
+		break;
+	case MPU_READ_MEM:
+		retval = inv_serial_read_mem(sl_handle, addr,
+					     msg.address, msg.length, msg.data);
+		break;
+	case MPU_WRITE_MEM:
+		retval = inv_serial_write_mem(sl_handle, addr,
+					      msg.address, msg.length,
+					      msg.data);
+		break;
+	case MPU_READ_FIFO:
+		retval = inv_serial_read_fifo(sl_handle, addr,
+					      msg.length, msg.data);
+		break;
+	case MPU_WRITE_FIFO:
+		retval = inv_serial_write_fifo(sl_handle, addr,
+					       msg.length, msg.data);
+		break;
+
+	};
+	if (retval) {
+		dev_err(&((struct i2c_adapter *)sl_handle)->dev,
+			"%s: i2c %d error %d\n",
+			__func__, cmd, retval);
+		kfree(msg.data);
+		return retval;
+	}
+	retval = copy_to_user((unsigned char __user *)user_data,
+			      msg.data, msg.length);
+	kfree(msg.data);
+	return retval;
+}
+
+/* ioctl - I/O control */
+static long mpu_dev_ioctl(struct file *file,
+			  unsigned int cmd, unsigned long arg)
+{
+	struct mpu_private_data *mpu =
+	    container_of(file->private_data, struct mpu_private_data, dev);
+	struct i2c_client *client = mpu->client;
+	struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+	int retval = 0;
+	struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
+	struct ext_slave_descr **slave = mldl_cfg->slave;
+	struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
+	int ii;
+
+	for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
+		if (!pdata_slave[ii])
+			slave_adapter[ii] = NULL;
+		else
+			slave_adapter[ii] =
+				i2c_get_adapter(pdata_slave[ii]->adapt_num);
+	}
+	slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
+
+	retval = mutex_lock_interruptible(&mpu->mutex);
+	if (retval) {
+		dev_err(&client->adapter->dev,
+			"%s: mutex_lock_interruptible returned %d\n",
+			__func__, retval);
+		return retval;
+	}
+
+	switch (cmd) {
+	case MPU_GET_EXT_SLAVE_PLATFORM_DATA:
+		retval = mpu_dev_ioctl_get_ext_slave_platform_data(
+			client,
+			(struct ext_slave_platform_data __user *)arg);
+		break;
+	case MPU_GET_MPU_PLATFORM_DATA:
+		retval = mpu_dev_ioctl_get_mpu_platform_data(
+			client,
+			(struct mpu_platform_data __user *)arg);
+		break;
+	case MPU_GET_EXT_SLAVE_DESCR:
+		retval = mpu_dev_ioctl_get_ext_slave_descr(
+			client,
+			(struct ext_slave_descr __user *)arg);
+		break;
+	case MPU_READ:
+	case MPU_WRITE:
+	case MPU_READ_MEM:
+	case MPU_WRITE_MEM:
+	case MPU_READ_FIFO:
+	case MPU_WRITE_FIFO:
+		retval = mpu_handle_mlsl(
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			mldl_cfg->mpu_chip_info->addr, cmd,
+			(struct mpu_read_write __user *)arg);
+		break;
+	case MPU_CONFIG_GYRO:
+		retval = inv_mpu_config(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			(struct ext_slave_config __user *)arg);
+		break;
+	case MPU_CONFIG_ACCEL:
+		retval = slave_config(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+			slave[EXT_SLAVE_TYPE_ACCEL],
+			pdata_slave[EXT_SLAVE_TYPE_ACCEL],
+			(struct ext_slave_config __user *)arg);
+		break;
+	case MPU_CONFIG_COMPASS:
+		retval = slave_config(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+			slave[EXT_SLAVE_TYPE_COMPASS],
+			pdata_slave[EXT_SLAVE_TYPE_COMPASS],
+			(struct ext_slave_config __user *)arg);
+		break;
+	case MPU_CONFIG_PRESSURE:
+		retval = slave_config(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+			slave[EXT_SLAVE_TYPE_PRESSURE],
+			pdata_slave[EXT_SLAVE_TYPE_PRESSURE],
+			(struct ext_slave_config __user *)arg);
+		break;
+	case MPU_GET_CONFIG_GYRO:
+		retval = inv_mpu_get_config(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			(struct ext_slave_config __user *)arg);
+		break;
+	case MPU_GET_CONFIG_ACCEL:
+		retval = slave_get_config(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+			slave[EXT_SLAVE_TYPE_ACCEL],
+			pdata_slave[EXT_SLAVE_TYPE_ACCEL],
+			(struct ext_slave_config __user *)arg);
+		break;
+	case MPU_GET_CONFIG_COMPASS:
+		retval = slave_get_config(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+			slave[EXT_SLAVE_TYPE_COMPASS],
+			pdata_slave[EXT_SLAVE_TYPE_COMPASS],
+			(struct ext_slave_config __user *)arg);
+		break;
+	case MPU_GET_CONFIG_PRESSURE:
+		retval = slave_get_config(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+			slave[EXT_SLAVE_TYPE_PRESSURE],
+			pdata_slave[EXT_SLAVE_TYPE_PRESSURE],
+			(struct ext_slave_config __user *)arg);
+		break;
+	case MPU_SUSPEND:
+		retval = inv_mpu_suspend(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+			slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+			slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+			arg);
+		break;
+	case MPU_RESUME:
+		retval = inv_mpu_resume(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+			slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+			slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+			arg);
+		break;
+	case MPU_PM_EVENT_HANDLED:
+		dev_dbg(&client->adapter->dev, "%s: %d\n", __func__, cmd);
+		complete(&mpu->completion);
+		break;
+	case MPU_READ_ACCEL:
+		retval = inv_slave_read(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+			slave[EXT_SLAVE_TYPE_ACCEL],
+			pdata_slave[EXT_SLAVE_TYPE_ACCEL],
+			(unsigned char __user *)arg);
+		break;
+	case MPU_READ_COMPASS:
+		retval = inv_slave_read(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+			slave[EXT_SLAVE_TYPE_COMPASS],
+			pdata_slave[EXT_SLAVE_TYPE_COMPASS],
+			(unsigned char __user *)arg);
+		break;
+	case MPU_READ_PRESSURE:
+		retval = inv_slave_read(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+			slave[EXT_SLAVE_TYPE_PRESSURE],
+			pdata_slave[EXT_SLAVE_TYPE_PRESSURE],
+			(unsigned char __user *)arg);
+		break;
+	case MPU_GET_REQUESTED_SENSORS:
+		if (copy_to_user(
+			   (__u32 __user *)arg,
+			   &mldl_cfg->inv_mpu_cfg->requested_sensors,
+			   sizeof(mldl_cfg->inv_mpu_cfg->requested_sensors)))
+			retval = -EFAULT;
+		break;
+	case MPU_SET_REQUESTED_SENSORS:
+		mldl_cfg->inv_mpu_cfg->requested_sensors = arg;
+		break;
+	case MPU_GET_IGNORE_SYSTEM_SUSPEND:
+		if (copy_to_user(
+			(unsigned char __user *)arg,
+			&mldl_cfg->inv_mpu_cfg->ignore_system_suspend,
+			sizeof(mldl_cfg->inv_mpu_cfg->ignore_system_suspend)))
+			retval = -EFAULT;
+		break;
+	case MPU_SET_IGNORE_SYSTEM_SUSPEND:
+		mldl_cfg->inv_mpu_cfg->ignore_system_suspend = arg;
+		break;
+	case MPU_GET_MLDL_STATUS:
+		if (copy_to_user(
+			(unsigned char __user *)arg,
+			&mldl_cfg->inv_mpu_state->status,
+			sizeof(mldl_cfg->inv_mpu_state->status)))
+			retval = -EFAULT;
+		break;
+	case MPU_GET_I2C_SLAVES_ENABLED:
+		if (copy_to_user(
+			(unsigned char __user *)arg,
+			&mldl_cfg->inv_mpu_state->i2c_slaves_enabled,
+			sizeof(mldl_cfg->inv_mpu_state->i2c_slaves_enabled)))
+			retval = -EFAULT;
+		break;
+	default:
+		dev_err(&client->adapter->dev,
+			"%s: Unknown cmd %x, arg %lu\n",
+			__func__, cmd, arg);
+		retval = -EINVAL;
+	};
+
+	mutex_unlock(&mpu->mutex);
+	dev_dbg(&client->adapter->dev, "%s: %08x, %08lx, %d\n",
+		__func__, cmd, arg, retval);
+
+	if (retval > 0)
+		retval = -retval;
+
+	return retval;
+}
+
+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 *slave_adapter[EXT_SLAVE_NUM_TYPES];
+	struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
+	int ii;
+
+	for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
+		if (!pdata_slave[ii])
+			slave_adapter[ii] = NULL;
+		else
+			slave_adapter[ii] =
+				i2c_get_adapter(pdata_slave[ii]->adapt_num);
+	}
+	slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
+
+	mutex_lock(&mpu->mutex);
+	(void)inv_mpu_suspend(mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+			slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+			slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+			INV_ALL_SENSORS);
+	mutex_unlock(&mpu->mutex);
+	dev_dbg(&client->adapter->dev, "%s\n", __func__);
+}
+
+int mpu_dev_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 *slave_adapter[EXT_SLAVE_NUM_TYPES];
+	struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
+	int ii;
+
+	for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
+		if (!pdata_slave[ii])
+			slave_adapter[ii] = NULL;
+		else
+			slave_adapter[ii] =
+				i2c_get_adapter(pdata_slave[ii]->adapt_num);
+	}
+	slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
+
+	mutex_lock(&mpu->mutex);
+	if (!mldl_cfg->inv_mpu_cfg->ignore_system_suspend) {
+		dev_dbg(&client->adapter->dev,
+			"%s: suspending on event %d\n", __func__, mesg.event);
+		(void)inv_mpu_suspend(mldl_cfg,
+				slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+				slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+				slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+				slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+				INV_ALL_SENSORS);
+	} else {
+		dev_dbg(&client->adapter->dev,
+			"%s: Already suspended %d\n", __func__, mesg.event);
+	}
+	mutex_unlock(&mpu->mutex);
+	return 0;
+}
+
+int mpu_dev_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 *slave_adapter[EXT_SLAVE_NUM_TYPES];
+	struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
+	int ii;
+
+	for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
+		if (!pdata_slave[ii])
+			slave_adapter[ii] = NULL;
+		else
+			slave_adapter[ii] =
+				i2c_get_adapter(pdata_slave[ii]->adapt_num);
+	}
+	slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
+
+	mutex_lock(&mpu->mutex);
+	if (mpu->pid && !mldl_cfg->inv_mpu_cfg->ignore_system_suspend) {
+		(void)inv_mpu_resume(mldl_cfg,
+				slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+				slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+				slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+				slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+				mldl_cfg->inv_mpu_cfg->requested_sensors);
+		dev_dbg(&client->adapter->dev,
+			"%s for pid %d\n", __func__, mpu->pid);
+	}
+	mutex_unlock(&mpu->mutex);
+	return 0;
+}
+
+/* define which file operations are supported */
+static const struct file_operations mpu_fops = {
+	.owner = THIS_MODULE,
+	.read = mpu_read,
+	.poll = mpu_poll,
+	.unlocked_ioctl = mpu_dev_ioctl,
+	.open = mpu_dev_open,
+	.release = mpu_release,
+};
+
+int inv_mpu_register_slave(struct module *slave_module,
+			struct i2c_client *slave_client,
+			struct ext_slave_platform_data *slave_pdata,
+			struct ext_slave_descr *(*get_slave_descr)(void))
+{
+	struct mpu_private_data *mpu = mpu_private_data;
+	struct mldl_cfg *mldl_cfg;
+	struct ext_slave_descr *slave_descr;
+	struct ext_slave_platform_data **pdata_slave;
+	char *irq_name;
+	int result = 0;
+
+	if (!slave_client || !slave_pdata || !get_slave_descr)
+		return -EINVAL;
+
+	if (!mpu) {
+		dev_err(&slave_client->adapter->dev,
+			"%s: Null mpu_private_data\n", __func__);
+		return -EINVAL;
+	}
+	mldl_cfg = &mpu->mldl_cfg;
+	pdata_slave = mldl_cfg->pdata_slave;
+
+	slave_descr = get_slave_descr();
+	if (!slave_descr) {
+		dev_err(&slave_client->adapter->dev,
+			"%s: Null ext_slave_descr\n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&mpu->mutex);
+	if (mpu->pid) {
+		mutex_unlock(&mpu->mutex);
+		return -EBUSY;
+	}
+
+	mpu->slave_modules[slave_descr->type] = slave_module;
+
+	if (pdata_slave[slave_descr->type]) {
+		result = -EBUSY;
+		goto out_unlock_mutex;
+	}
+
+	pdata_slave[slave_descr->type] = slave_pdata;
+	pdata_slave[slave_descr->type]->address		= slave_client->addr;
+	pdata_slave[slave_descr->type]->irq		= slave_client->irq;
+	pdata_slave[slave_descr->type]->adapt_num	=
+		i2c_adapter_id(slave_client->adapter);
+
+	switch (slave_descr->type) {
+	case EXT_SLAVE_TYPE_ACCEL:
+		irq_name = "accelirq";
+		break;
+	case EXT_SLAVE_TYPE_COMPASS:
+		irq_name = "compassirq";
+		break;
+	case EXT_SLAVE_TYPE_PRESSURE:
+		irq_name = "pressureirq";
+		break;
+	default:
+		irq_name = "none";
+	};
+
+	if (pdata_slave[slave_descr->type]->irq > 0) {
+		dev_info(&slave_client->adapter->dev,
+			"Installing %s irq using %d\n",
+			irq_name,
+			pdata_slave[slave_descr->type]->irq);
+		result = slaveirq_init(slave_client->adapter,
+				pdata_slave[slave_descr->type], irq_name);
+		if (result)
+			goto out_unlock_mutex;
+	} else {
+		dev_WARN(&slave_client->adapter->dev,
+			"Accel irq not assigned\n");
+	}
+
+	if (slave_descr->init) {
+		result = slave_descr->init(slave_client->adapter,
+					slave_descr,
+					pdata_slave[slave_descr->type]);
+		if (result) {
+			dev_err(&slave_client->adapter->dev,
+				"Accel init failed %d\n", result);
+			goto out_slaveirq_exit;
+		}
+	}
+
+	mldl_cfg->slave[slave_descr->type] = slave_descr;
+
+	dev_info(&slave_client->adapter->dev,
+		"%s: +%s Type %d: Addr: %2x IRQ: %2d, Adapt: %2d\n",
+		__func__,
+		mldl_cfg->slave[slave_descr->type]->name,
+		slave_descr->type,
+		pdata_slave[slave_descr->type]->address,
+		pdata_slave[slave_descr->type]->irq,
+		pdata_slave[slave_descr->type]->adapt_num);
+	goto out_unlock_mutex;
+
+out_slaveirq_exit:
+	if (pdata_slave[slave_descr->type]->irq > 0)
+		slaveirq_exit(pdata_slave[slave_descr->type]);
+out_unlock_mutex:
+	mutex_unlock(&mpu->mutex);
+	return result;
+}
+EXPORT_SYMBOL(inv_mpu_register_slave);
+
+void inv_mpu_unregister_slave(struct i2c_client *slave_client,
+			struct ext_slave_platform_data *slave_pdata,
+			struct ext_slave_descr *(*get_slave_descr)(void))
+{
+	struct mpu_private_data *mpu = mpu_private_data;
+	struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+	struct ext_slave_descr *slave_descr;
+	int result;
+
+	dev_info(&slave_client->adapter->dev, "%s\n", __func__);
+
+	if (!slave_client || !slave_pdata || !get_slave_descr)
+		return;
+
+	slave_descr = get_slave_descr();
+	if (!slave_descr)
+		return;
+
+	mutex_lock(&mpu->mutex);
+
+	if (slave_descr->exit) {
+		result = slave_descr->exit(slave_client->adapter,
+					slave_descr,
+					slave_pdata);
+		if (result)
+			dev_err(&slave_client->adapter->dev,
+				"Accel exit failed %d\n", result);
+	}
+
+	if (slave_pdata->irq)
+		slaveirq_exit(slave_pdata);
+
+	mldl_cfg->slave[slave_descr->type] = NULL;
+	mldl_cfg->pdata_slave[slave_descr->type] = NULL;
+	mpu->slave_modules[slave_descr->type] = NULL;
+
+	mutex_unlock(&mpu->mutex);
+}
+EXPORT_SYMBOL(inv_mpu_unregister_slave);
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static const struct i2c_device_id mpu_id[] = {
+	{"mpu3050", 0},
+	{"mpu6050", 0},
+	{"mpu6050_no_accel", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, mpu_id);
+
+int mpu_probe(struct i2c_client *client, const struct i2c_device_id *devid)
+{
+	struct mpu_platform_data *pdata;
+	struct mpu_private_data *mpu;
+	struct mldl_cfg *mldl_cfg;
+	int res = 0;
+	int ii = 0;
+
+	dev_info(&client->adapter->dev, "%s: %d\n", __func__, ii++);
+
+	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;
+	}
+	mldl_cfg = &mpu->mldl_cfg;
+	mldl_cfg->mpu_ram	= &mpu->mpu_ram;
+	mldl_cfg->mpu_gyro_cfg	= &mpu->mpu_gyro_cfg;
+	mldl_cfg->mpu_offsets	= &mpu->mpu_offsets;
+	mldl_cfg->mpu_chip_info	= &mpu->mpu_chip_info;
+	mldl_cfg->inv_mpu_cfg	= &mpu->inv_mpu_cfg;
+	mldl_cfg->inv_mpu_state	= &mpu->inv_mpu_state;
+
+	mldl_cfg->mpu_ram->length = MPU_MEM_NUM_RAM_BANKS * MPU_MEM_BANK_SIZE;
+	mldl_cfg->mpu_ram->ram = kzalloc(mldl_cfg->mpu_ram->length, GFP_KERNEL);
+	if (!mldl_cfg->mpu_ram->ram) {
+		res = -ENOMEM;
+		goto out_alloc_ram_failed;
+	}
+	mpu_private_data = mpu;
+	i2c_set_clientdata(client, mpu);
+	mpu->client = client;
+
+	init_waitqueue_head(&mpu->mpu_event_wait);
+	mutex_init(&mpu->mutex);
+	init_completion(&mpu->completion);
+
+	mpu->response_timeout = 60;	/* Seconds */
+	mpu->timeout.function = mpu_pm_timeout;
+	mpu->timeout.data = (u_long) mpu;
+	init_timer(&mpu->timeout);
+
+	mpu->nb.notifier_call = mpu_pm_notifier_callback;
+	mpu->nb.priority = 0;
+	res = register_pm_notifier(&mpu->nb);
+	if (res) {
+		dev_err(&client->adapter->dev,
+			"Unable to register pm_notifier %d\n", res);
+		goto out_register_pm_notifier_failed;
+	}
+
+	pdata = (struct mpu_platform_data *)client->dev.platform_data;
+	if (!pdata) {
+		dev_WARN(&client->adapter->dev,
+			 "Missing platform data for mpu\n");
+	}
+	mldl_cfg->pdata = pdata;
+
+	mldl_cfg->mpu_chip_info->addr = client->addr;
+	res = inv_mpu_open(&mpu->mldl_cfg, client->adapter, NULL, NULL, NULL);
+
+	if (res) {
+		dev_err(&client->adapter->dev,
+			"Unable to open %s %d\n", MPU_NAME, res);
+		res = -ENODEV;
+		goto out_whoami_failed;
+	}
+
+	mpu->dev.minor = MISC_DYNAMIC_MINOR;
+	mpu->dev.name = "mpu";
+	mpu->dev.fops = &mpu_fops;
+	res = misc_register(&mpu->dev);
+	if (res < 0) {
+		dev_err(&client->adapter->dev,
+			"ERROR: misc_register returned %d\n", res);
+		goto out_misc_register_failed;
+	}
+
+	if (client->irq) {
+		dev_info(&client->adapter->dev,
+			 "Installing irq using %d\n", client->irq);
+		res = mpuirq_init(client, mldl_cfg);
+		if (res)
+			goto out_mpuirq_failed;
+	} else {
+		dev_WARN(&client->adapter->dev,
+			 "Missing %s IRQ\n", MPU_NAME);
+	}
+
+	return res;
+
+out_mpuirq_failed:
+	misc_deregister(&mpu->dev);
+out_misc_register_failed:
+	inv_mpu_close(&mpu->mldl_cfg, client->adapter, NULL, NULL, NULL);
+out_whoami_failed:
+	unregister_pm_notifier(&mpu->nb);
+out_register_pm_notifier_failed:
+	kfree(mldl_cfg->mpu_ram->ram);
+	mpu_private_data = NULL;
+out_alloc_ram_failed:
+	kfree(mpu);
+out_alloc_data_failed:
+out_check_functionality_failed:
+	dev_err(&client->adapter->dev, "%s failed %d\n", __func__, res);
+	return res;
+
+}
+
+static int mpu_remove(struct i2c_client *client)
+{
+	struct mpu_private_data *mpu = i2c_get_clientdata(client);
+	struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
+	struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+	struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
+	int ii;
+
+	for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
+		if (!pdata_slave[ii])
+			slave_adapter[ii] = NULL;
+		else
+			slave_adapter[ii] =
+				i2c_get_adapter(pdata_slave[ii]->adapt_num);
+	}
+
+	slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
+	dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+	inv_mpu_close(mldl_cfg,
+		slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+		slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+		slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+		slave_adapter[EXT_SLAVE_TYPE_PRESSURE]);
+
+	if (client->irq)
+		mpuirq_exit();
+
+	for (ii = EXT_SLAVE_TYPE_ACCEL - 1; ii >= 0; ii--) {
+		if (pdata_slave[ii] &&
+		    pdata_slave[ii]->irq) {
+			slaveirq_exit(pdata_slave[ii]);
+		}
+	}
+
+	unregister_pm_notifier(&mpu->nb);
+
+	misc_deregister(&mpu->dev);
+	kfree(mpu);
+
+	return 0;
+}
+
+static struct i2c_driver mpu_driver = {
+	.class = I2C_CLASS_HWMON,
+	.probe = mpu_probe,
+	.remove = mpu_remove,
+	.id_table = mpu_id,
+	.driver = {
+		   .owner = THIS_MODULE,
+		   .name = MPU_NAME,
+		   },
+	.address_list = normal_i2c,
+	.shutdown = mpu_shutdown,	/* optional */
+	.suspend = mpu_dev_suspend,	/* optional */
+	.resume = mpu_dev_resume,	/* optional */
+
+};
+
+static int __init mpu_init(void)
+{
+	int res = i2c_add_driver(&mpu_driver);
+	pr_info("%s: Probe name %s\n", __func__, MPU_NAME);
+	if (res)
+		pr_err("%s failed\n", __func__);
+	return res;
+}
+
+static void __exit mpu_exit(void)
+{
+	pr_info("%s\n", __func__);
+	i2c_del_driver(&mpu_driver);
+}
+
+module_init(mpu_init);
+module_exit(mpu_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("User space character device interface for MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS(MPU_NAME);
diff --git a/drivers/misc/inv_mpu/mpu-dev.h b/drivers/misc/inv_mpu/mpu-dev.h
new file mode 100644
index 0000000..b6a4fcf
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu-dev.h
@@ -0,0 +1,36 @@
+/*
+	$License:
+	Copyright (C) 2011 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/>.
+	$
+ */
+
+
+#ifndef __MPU_DEV_H__
+#define __MPU_DEV_H__
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mpu.h>
+
+int inv_mpu_register_slave(struct module *slave_module,
+			struct i2c_client *client,
+			struct ext_slave_platform_data *pdata,
+			struct ext_slave_descr *(*slave_descr)(void));
+
+void inv_mpu_unregister_slave(struct i2c_client *client,
+			struct ext_slave_platform_data *pdata,
+			struct ext_slave_descr *(*slave_descr)(void));
+#endif
-- 
1.7.4.1


  parent reply	other threads:[~2011-07-01  2:19 UTC|newest]

Thread overview: 54+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-07-01  2:18 [PATCH 01/11] misc: inv_mpu primary header file and README file Nathan Royer
2011-07-01  2:18 ` [PATCH 02/11] misc: mpu3050 Register definition and Private data Nathan Royer
2011-07-01  2:18 ` Nathan Royer [this message]
2011-07-01  2:18 ` [PATCH 04/11] misc: IRQ handling for MPU3050 and slave devices Nathan Royer
2011-07-01  2:18 ` [PATCH 05/11] misc: MPU3050 and slave device configuration Nathan Royer
2011-07-01 17:55   ` Nathan Royer
2011-07-01  2:18 ` [PATCH 06/11] misc: inv_mpu logging and debugging support Nathan Royer
2011-07-01  2:18 ` [PATCH 07/11] misc: I2C communication with the MPU3050 and slave devices Nathan Royer
2011-07-01  2:18 ` [PATCH 08/11] misc: Kconfig and Makefile changes for inv_mpu driver Nathan Royer
2011-07-01 17:10   ` Randy Dunlap
2011-07-01  2:18 ` [PATCH 09/11] misc: Add slave driver for kxtf9 accelerometer Nathan Royer
2011-07-01  2:18 ` [PATCH 10/11] misc: Add slave driver for ak8975 compass driver Nathan Royer
2011-07-01  2:18 ` [PATCH 11/11] misc: Add slave driver for bma085 pressure sensor Nathan Royer
2011-07-01  7:56   ` Alan Cox
2011-07-01  7:56     ` Alan Cox
2011-07-01  8:47     ` Jean Delvare
2011-07-01  8:47       ` Jean Delvare
2011-07-01 14:28     ` Chris Wolfe
2011-07-01 14:41       ` Alan Cox
2011-07-01 14:41         ` Alan Cox
2011-07-01 15:52         ` Chris Wolfe
2011-07-01 17:00           ` Alan Cox
2011-07-01 17:00             ` Alan Cox
2011-07-01 17:56             ` Nathan Royer
2011-07-01 16:09         ` Jean Delvare
2011-07-01  9:05   ` Jonathan Cameron
2011-07-01 10:35     ` Manuel Stahl
2011-07-01 10:35       ` Manuel Stahl
2011-07-01  3:09 ` [PATCH 01/11] misc: inv_mpu primary header file and README file Greg KH
2011-07-01  7:29   ` Alan Cox
2011-07-01  9:00   ` Jonathan Cameron
2011-07-01  3:59 ` Chris Wolfe
2011-07-05 18:08   ` Nathan Royer
2011-07-01  7:53 ` Alan Cox
2011-07-01  9:08 ` Jonathan Cameron
2011-07-01 16:39   ` Nathan Royer
2011-07-03 11:29     ` Jonathan Cameron
2011-07-04  8:16       ` Alan Cox
2011-07-06  1:49         ` Nathan Royer
2011-07-06  9:07           ` Jonathan Cameron
2011-07-06  9:07             ` Jonathan Cameron
2011-07-06 20:25             ` Nathan Royer
2011-07-06 10:54           ` Alan Cox
2011-07-06 21:27             ` Nathan Royer
2011-07-07  7:40               ` Alan Cox
2011-07-07  7:40                 ` Alan Cox
2011-07-08  1:25                 ` Nathan Royer
2011-07-08  1:25                   ` Nathan Royer
2011-07-08 11:21                   ` Jonathan Cameron
2011-07-08 11:21                     ` Jonathan Cameron
2011-07-08 16:24                     ` Nathan Royer
2011-07-08 16:24                       ` Nathan Royer
2011-07-04 20:06       ` Eric Andersson
2011-07-01 21:04 ` Arnd Bergmann

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1309486707-1658-3-git-send-email-nroyer@invensense.com \
    --to=nroyer@invensense.com \
    --cc=akpm@linux-foundation.org \
    --cc=alan@linux.intel.com \
    --cc=gregkh@suse.de \
    --cc=jic23@cam.ac.uk \
    --cc=jkosina@suse.cz \
    --cc=khali@linux-fr.org \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.