input: change mpu3050 driver to request regulator, add poll support

Needed on 8x30 MTP/MDP platforms, where interrupt line polled up and
does not work with driver's default configuration

Change-Id: I4827c7a1f7df147245336d18523088c013381f1e
Signed-off-by: Wentao Xu <wentaox@codeaurora.org>
diff --git a/drivers/input/misc/mpu3050.c b/drivers/input/misc/mpu3050.c
index 5403c57..8b6e172 100644
--- a/drivers/input/misc/mpu3050.c
+++ b/drivers/input/misc/mpu3050.c
@@ -40,6 +40,8 @@
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/pm_runtime.h>
+#include <linux/input/mpu3050.h>
+#include <linux/regulator/consumer.h>
 
 #define MPU3050_CHIP_ID		0x69
 
@@ -112,8 +114,81 @@
 	struct i2c_client *client;
 	struct device *dev;
 	struct input_dev *idev;
+	struct mpu3050_gyro_platform_data *platform_data;
+	struct delayed_work input_work;
+	u32    use_poll;
 };
 
+struct sensor_regulator {
+	struct regulator *vreg;
+	const char *name;
+	u32	min_uV;
+	u32	max_uV;
+};
+
+struct sensor_regulator mpu_vreg[] = {
+	{NULL, "vdd", 2100000, 3600000},
+	{NULL, "vlogic", 1800000, 1800000},
+};
+
+static int mpu3050_config_regulator(struct i2c_client *client, bool on)
+{
+	int rc = 0, i;
+	int num_reg = sizeof(mpu_vreg) / sizeof(struct sensor_regulator);
+
+	if (on) {
+		for (i = 0; i < num_reg; i++) {
+			mpu_vreg[i].vreg = regulator_get(&client->dev,
+						mpu_vreg[i].name);
+			if (IS_ERR(mpu_vreg[i].vreg)) {
+				rc = PTR_ERR(mpu_vreg[i].vreg);
+				pr_err("%s:regulator get failed rc=%d\n",
+						__func__, rc);
+				goto error_vdd;
+			}
+
+			if (regulator_count_voltages(mpu_vreg[i].vreg) > 0) {
+				rc = regulator_set_voltage(mpu_vreg[i].vreg,
+					mpu_vreg[i].min_uV, mpu_vreg[i].max_uV);
+				if (rc) {
+					pr_err("%s:set_voltage failed rc=%d\n",
+						__func__, rc);
+					regulator_put(mpu_vreg[i].vreg);
+					goto error_vdd;
+				}
+			}
+
+			rc = regulator_enable(mpu_vreg[i].vreg);
+			if (rc) {
+				pr_err("%s: regulator_enable failed rc =%d\n",
+						__func__,
+						rc);
+
+				if (regulator_count_voltages(
+					mpu_vreg[i].vreg) > 0) {
+					regulator_set_voltage(mpu_vreg[i].vreg,
+						0, mpu_vreg[i].max_uV);
+				}
+				regulator_put(mpu_vreg[i].vreg);
+				goto error_vdd;
+			}
+		}
+		return rc;
+	} else {
+		i = num_reg;
+	}
+error_vdd:
+	while (--i >= 0) {
+		if (regulator_count_voltages(mpu_vreg[i].vreg) > 0) {
+			regulator_set_voltage(mpu_vreg[i].vreg, 0,
+						mpu_vreg[i].max_uV);
+		}
+		regulator_disable(mpu_vreg[i].vreg);
+		regulator_put(mpu_vreg[i].vreg);
+	}
+	return rc;
+}
+
 /**
  *	mpu3050_xyz_read_reg	-	read the axes values
  *	@buffer: provide register addr and get register
@@ -179,11 +254,21 @@
 {
 	u8 value;
 
+	if (val) {
+		mpu3050_config_regulator(client, 1);
+		udelay(10);
+	}
+
 	value = i2c_smbus_read_byte_data(client, MPU3050_PWR_MGM);
 	value = (value & ~MPU3050_PWR_MGM_MASK) |
 		(((val << MPU3050_PWR_MGM_POS) & MPU3050_PWR_MGM_MASK) ^
 		 MPU3050_PWR_MGM_MASK);
 	i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM, value);
+
+	if (!val) {
+		udelay(10);
+		mpu3050_config_regulator(client, 0);
+	}
 }
 
 /**
@@ -210,6 +295,9 @@
 		pm_runtime_put(sensor->dev);
 		return error;
 	}
+	if (sensor->use_poll)
+		schedule_delayed_work(&sensor->input_work,
+			msecs_to_jiffies(MPU3050_DEFAULT_POLL_INTERVAL));
 
 	return 0;
 }
@@ -225,6 +313,9 @@
 {
 	struct mpu3050_sensor *sensor = input_get_drvdata(input);
 
+	if (sensor->use_poll)
+		cancel_delayed_work_sync(&sensor->input_work);
+
 	pm_runtime_put(sensor->dev);
 }
 
@@ -252,6 +343,33 @@
 }
 
 /**
+ *	mpu3050_input_work_fn -		polling work
+ *	@work: the work struct
+ *
+ *	Called by the work queue; read sensor data and generate an input
+ *	event
+ */
+static void mpu3050_input_work_fn(struct work_struct *work)
+{
+	struct mpu3050_sensor *sensor;
+	struct axis_data axis;
+
+	sensor = container_of((struct delayed_work *)work,
+				struct mpu3050_sensor, input_work);
+
+	mpu3050_read_xyz(sensor->client, &axis);
+
+	input_report_abs(sensor->idev, ABS_X, axis.x);
+	input_report_abs(sensor->idev, ABS_Y, axis.y);
+	input_report_abs(sensor->idev, ABS_Z, axis.z);
+	input_sync(sensor->idev);
+
+	if (sensor->use_poll)
+		schedule_delayed_work(&sensor->input_work,
+			msecs_to_jiffies(MPU3050_DEFAULT_POLL_INTERVAL));
+}
+
+/**
  *	mpu3050_hw_init	-	initialize hardware
  *	@sensor: the sensor
  *
@@ -325,6 +443,7 @@
 	sensor->client = client;
 	sensor->dev = &client->dev;
 	sensor->idev = idev;
+	sensor->platform_data = client->dev.platform_data;
 
 	mpu3050_set_power_mode(client, 1);
 	msleep(10);
@@ -365,14 +484,22 @@
 	if (error)
 		goto err_pm_set_suspended;
 
-	error = request_threaded_irq(client->irq,
+	if (client->irq == 0) {
+		INIT_DELAYED_WORK(&sensor->input_work, mpu3050_input_work_fn);
+		sensor->use_poll = 1;
+	} else {
+		sensor->use_poll = 0;
+
+		error = request_threaded_irq(client->irq,
 				     NULL, mpu3050_interrupt_thread,
 				     IRQF_TRIGGER_RISING,
 				     "mpu3050", sensor);
-	if (error) {
-		dev_err(&client->dev,
-			"can't get IRQ %d, error %d\n", client->irq, error);
-		goto err_pm_set_suspended;
+		if (error) {
+			dev_err(&client->dev,
+				"can't get IRQ %d, error %d\n",
+				client->irq, error);
+			goto err_pm_set_suspended;
+		}
 	}
 
 	error = input_register_device(idev);
@@ -387,7 +514,8 @@
 	return 0;
 
 err_free_irq:
-	free_irq(client->irq, sensor);
+	if (client->irq > 0)
+		free_irq(client->irq, sensor);
 err_pm_set_suspended:
 	pm_runtime_set_suspended(&client->dev);
 err_free_mem: