input: mpu3050: make sample rate configurable, and use interrupt

Required by 8x30 platform

Change-Id: I51b39e1ee5e367fd72151854e3d421538074d6d6
Signed-off-by: Wentao Xu <wentaox@codeaurora.org>
diff --git a/arch/arm/mach-msm/board-8930-gpiomux.c b/arch/arm/mach-msm/board-8930-gpiomux.c
index e0f012a..ff0a61f 100644
--- a/arch/arm/mach-msm/board-8930-gpiomux.c
+++ b/arch/arm/mach-msm/board-8930-gpiomux.c
@@ -688,6 +688,22 @@
 	},
 };
 
+static struct gpiomux_setting gyro_int_line = {
+	.func = GPIOMUX_FUNC_GPIO,
+	.drv = GPIOMUX_DRV_2MA,
+	.pull = GPIOMUX_PULL_NONE,
+};
+
+static struct msm_gpiomux_config msm8930_gyro_int_config[] __initdata = {
+	{
+		.gpio = 69,	/* Gyro Interrupt Line */
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &gyro_int_line,
+			[GPIOMUX_ACTIVE] = &gyro_int_line,
+		},
+	},
+};
+
 int __init msm8930_init_gpiomux(void)
 {
 	int rc = msm_gpiomux_init(NR_GPIO_IRQS);
@@ -758,5 +774,9 @@
 	msm_gpiomux_install(msm8930_sd_det_config,
 			ARRAY_SIZE(msm8930_sd_det_config));
 
+	if (machine_is_msm8930_fluid() || machine_is_msm8930_mtp())
+		msm_gpiomux_install(msm8930_gyro_int_config,
+			ARRAY_SIZE(msm8930_gyro_int_config));
+
 	return 0;
 }
diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c
index b80d62d..4fce029 100644
--- a/arch/arm/mach-msm/board-8930.c
+++ b/arch/arm/mach-msm/board-8930.c
@@ -2431,6 +2431,7 @@
 static struct i2c_board_info __initdata mpu3050_i2c_boardinfo[] = {
 	{
 		I2C_BOARD_INFO("mpu3050", 0x68),
+		.irq = MSM_GPIO_TO_INT(MPU3050_INT_GPIO),
 		.platform_data = &mpu3050_gyro,
 	},
 };
diff --git a/drivers/input/misc/mpu3050.c b/drivers/input/misc/mpu3050.c
index 8b6e172..d3da652 100644
--- a/drivers/input/misc/mpu3050.c
+++ b/drivers/input/misc/mpu3050.c
@@ -40,6 +40,7 @@
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/pm_runtime.h>
+#include <linux/gpio.h>
 #include <linux/input/mpu3050.h>
 #include <linux/regulator/consumer.h>
 
@@ -50,6 +51,8 @@
 #define MPU3050_MIN_VALUE	-32768
 #define MPU3050_MAX_VALUE	32767
 
+#define MPU3050_MIN_POLL_INTERVAL	1
+#define MPU3050_MAX_POLL_INTERVAL	250
 #define MPU3050_DEFAULT_POLL_INTERVAL	200
 #define MPU3050_DEFAULT_FS_RANGE	3
 
@@ -90,8 +93,10 @@
 #define MPU3050_DLPF_CFG_MASK		0x07
 /* INT_CFG */
 #define MPU3050_RAW_RDY_EN		0x01
-#define MPU3050_MPU_RDY_EN		0x02
-#define MPU3050_LATCH_INT_EN		0x04
+#define MPU3050_MPU_RDY_EN		0x04
+#define MPU3050_LATCH_INT_EN		0x20
+#define MPU3050_OPEN_DRAIN		0x40
+#define MPU3050_ACTIVE_LOW		0x80
 /* PWR_MGM */
 #define MPU3050_PWR_MGM_PLL_X		0x01
 #define MPU3050_PWR_MGM_PLL_Y		0x02
@@ -117,6 +122,7 @@
 	struct mpu3050_gyro_platform_data *platform_data;
 	struct delayed_work input_work;
 	u32    use_poll;
+	u32    poll_interval;
 };
 
 struct sensor_regulator {
@@ -190,6 +196,78 @@
 }
 
 /**
+ *	mpu3050_attr_get_polling_rate	-	get the sampling rate
+ */
+static ssize_t mpu3050_attr_get_polling_rate(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	int val;
+	struct mpu3050_sensor *sensor = dev_get_drvdata(dev);
+	val = sensor ? sensor->poll_interval : 0;
+	return snprintf(buf, 8, "%d\n", val);
+}
+
+/**
+ *	mpu3050_attr_set_polling_rate	-	set the sampling rate
+ */
+static ssize_t mpu3050_attr_set_polling_rate(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	struct mpu3050_sensor *sensor = dev_get_drvdata(dev);
+	unsigned long interval_ms;
+
+	if (kstrtoul(buf, 10, &interval_ms))
+		return -EINVAL;
+	if ((interval_ms < MPU3050_MIN_POLL_INTERVAL) ||
+		(interval_ms > MPU3050_MAX_POLL_INTERVAL))
+		return -EINVAL;
+
+	if (sensor)
+		sensor->poll_interval = interval_ms;
+
+	/* Output frequency divider. The poll interval */
+	i2c_smbus_write_byte_data(sensor->client, MPU3050_SMPLRT_DIV,
+					interval_ms - 1);
+
+	return size;
+}
+
+static struct device_attribute attributes[] = {
+
+	__ATTR(pollrate_ms, 0666,
+		mpu3050_attr_get_polling_rate,
+		mpu3050_attr_set_polling_rate),
+};
+
+static int create_sysfs_interfaces(struct device *dev)
+{
+	int i;
+	int err;
+	for (i = 0; i < ARRAY_SIZE(attributes); i++) {
+		err = device_create_file(dev, attributes + i);
+		if (err)
+			goto error;
+	}
+	return 0;
+
+error:
+	for ( ; i >= 0; i--)
+		device_remove_file(dev, attributes + i);
+	dev_err(dev, "%s:Unable to create interface\n", __func__);
+	return err;
+}
+
+static int remove_sysfs_interfaces(struct device *dev)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(attributes); i++)
+		device_remove_file(dev, attributes + i);
+	return 0;
+}
+
+/**
  *	mpu3050_xyz_read_reg	-	read the axes values
  *	@buffer: provide register addr and get register
  *	@length: length of register
@@ -284,20 +362,20 @@
 	struct mpu3050_sensor *sensor = input_get_drvdata(input);
 	int error;
 
-	pm_runtime_get(sensor->dev);
+	pm_runtime_get_sync(sensor->dev);
 
 	/* Enable interrupts */
 	error = i2c_smbus_write_byte_data(sensor->client, MPU3050_INT_CFG,
-					  MPU3050_LATCH_INT_EN |
-					  MPU3050_RAW_RDY_EN |
-					  MPU3050_MPU_RDY_EN);
+					MPU3050_ACTIVE_LOW |
+					MPU3050_OPEN_DRAIN |
+					MPU3050_RAW_RDY_EN);
 	if (error < 0) {
 		pm_runtime_put(sensor->dev);
 		return error;
 	}
 	if (sensor->use_poll)
 		schedule_delayed_work(&sensor->input_work,
-			msecs_to_jiffies(MPU3050_DEFAULT_POLL_INTERVAL));
+			msecs_to_jiffies(sensor->poll_interval));
 
 	return 0;
 }
@@ -366,7 +444,7 @@
 
 	if (sensor->use_poll)
 		schedule_delayed_work(&sensor->input_work,
-			msecs_to_jiffies(MPU3050_DEFAULT_POLL_INTERVAL));
+			msecs_to_jiffies(sensor->poll_interval));
 }
 
 /**
@@ -399,7 +477,7 @@
 
 	/* Output frequency divider. The poll interval */
 	ret = i2c_smbus_write_byte_data(client, MPU3050_SMPLRT_DIV,
-					MPU3050_DEFAULT_POLL_INTERVAL - 1);
+					sensor->poll_interval - 1);
 	if (ret < 0)
 		return ret;
 
@@ -444,6 +522,18 @@
 	sensor->dev = &client->dev;
 	sensor->idev = idev;
 	sensor->platform_data = client->dev.platform_data;
+	i2c_set_clientdata(client, sensor);
+	if (sensor->platform_data) {
+		u32 interval = sensor->platform_data->poll_interval;
+
+		if ((interval < MPU3050_MIN_POLL_INTERVAL) ||
+			(interval > MPU3050_MAX_POLL_INTERVAL))
+			sensor->poll_interval = MPU3050_DEFAULT_POLL_INTERVAL;
+		else
+			sensor->poll_interval = interval;
+	} else {
+		sensor->poll_interval = MPU3050_DEFAULT_POLL_INTERVAL;
+	}
 
 	mpu3050_set_power_mode(client, 1);
 	msleep(10);
@@ -485,14 +575,34 @@
 		goto err_pm_set_suspended;
 
 	if (client->irq == 0) {
-		INIT_DELAYED_WORK(&sensor->input_work, mpu3050_input_work_fn);
 		sensor->use_poll = 1;
+		INIT_DELAYED_WORK(&sensor->input_work, mpu3050_input_work_fn);
 	} else {
 		sensor->use_poll = 0;
 
+		if (gpio_is_valid(sensor->platform_data->gpio_int)) {
+			/* configure interrupt gpio */
+			ret = gpio_request(sensor->platform_data->gpio_int,
+								"gyro_gpio_int");
+			if (ret) {
+				pr_err("%s: unable to request interrupt gpio %d\n",
+					__func__,
+					sensor->platform_data->gpio_int);
+				goto err_pm_set_suspended;
+			}
+
+			ret = gpio_direction_input(
+				sensor->platform_data->gpio_int);
+			if (ret) {
+				pr_err("%s: unable to set direction for gpio %d\n",
+				__func__, sensor->platform_data->gpio_int);
+				goto err_free_gpio;
+			}
+		}
+
 		error = request_threaded_irq(client->irq,
 				     NULL, mpu3050_interrupt_thread,
-				     IRQF_TRIGGER_RISING,
+				     IRQF_TRIGGER_FALLING,
 				     "mpu3050", sensor);
 		if (error) {
 			dev_err(&client->dev,
@@ -508,14 +618,26 @@
 		goto err_free_irq;
 	}
 
+	error = create_sysfs_interfaces(&client->dev);
+	if (error < 0) {
+		dev_err(&client->dev, "failed to create sysfs\n");
+		goto err_input_cleanup;
+	}
+
 	pm_runtime_enable(&client->dev);
 	pm_runtime_set_autosuspend_delay(&client->dev, MPU3050_AUTO_DELAY);
 
 	return 0;
 
+err_input_cleanup:
+	input_unregister_device(idev);
 err_free_irq:
 	if (client->irq > 0)
 		free_irq(client->irq, sensor);
+err_free_gpio:
+	if ((client->irq > 0) &&
+		(gpio_is_valid(sensor->platform_data->gpio_int)))
+		gpio_free(sensor->platform_data->gpio_int);
 err_pm_set_suspended:
 	pm_runtime_set_suspended(&client->dev);
 err_free_mem:
@@ -537,8 +659,12 @@
 	pm_runtime_disable(&client->dev);
 	pm_runtime_set_suspended(&client->dev);
 
-	free_irq(client->irq, sensor);
+	if (client->irq)
+		free_irq(client->irq, sensor);
+
+	remove_sysfs_interfaces(&client->dev);
 	input_unregister_device(sensor->idev);
+
 	kfree(sensor);
 
 	return 0;