input: mpu3050: add enable/disable sysfs attribute

allow sensor HAL control the power state of gyro sensor, so that sensor is
disabled when not used.

Change-Id: Ib9f8e9761ea518534ffcd06f30de4b8f1f198687
Signed-off-by: Orkhan Karimov <okarimov@codeaurora.org>
diff --git a/drivers/input/misc/mpu3050.c b/drivers/input/misc/mpu3050.c
index 6c64a57..5d6a19a 100644
--- a/drivers/input/misc/mpu3050.c
+++ b/drivers/input/misc/mpu3050.c
@@ -124,6 +124,7 @@
 	u32    use_poll;
 	u32    poll_interval;
 	u32    dlpf_index;
+	atomic_t enabled;
 };
 
 struct sensor_regulator {
@@ -154,6 +155,10 @@
 	{0, 256, 8},
 };
 
+static void mpu3050_set_power_mode(struct i2c_client *client, u8 val);
+static int mpu3050_start(struct mpu3050_sensor *sensor);
+static void mpu3050_stop(struct mpu3050_sensor *sensor);
+
 static u8 interval_to_dlpf_cfg(u32 interval)
 {
 	u32 sample_rate = 1000 / interval;
@@ -293,13 +298,45 @@
 	return size;
 }
 
+
+static ssize_t mpu3050_attr_get_enable(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct mpu3050_sensor *sensor = dev_get_drvdata(dev);
+	int val = atomic_read(&sensor->enabled);
+	return snprintf(buf, sizeof(val) + 2, "%d\n", val);
+}
+
+
+static ssize_t mpu3050_attr_set_enable(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t size)
+{
+	struct mpu3050_sensor *sensor = dev_get_drvdata(dev);
+	unsigned long val;
+
+	if (kstrtoul(buf, 10, &val))
+		return -EINVAL;
+
+	if (val)
+		mpu3050_start(sensor);
+	else
+		mpu3050_stop(sensor);
+
+	return size;
+}
+
 static struct device_attribute attributes[] = {
 
 	__ATTR(pollrate_ms, 0664,
 		mpu3050_attr_get_polling_rate,
 		mpu3050_attr_set_polling_rate),
+	__ATTR(enable, 0664,
+		mpu3050_attr_get_enable,
+		mpu3050_attr_set_enable),
 };
 
+
 static int create_sysfs_interfaces(struct device *dev)
 {
 	int i;
@@ -409,16 +446,15 @@
 }
 
 /**
- *	mpu3050_input_open	-	called on input event open
- *	@input: input dev of opened device
+ *	mpu3050_start	-	called when sensor is enabled via sysfs
+ *	@sensor: the sensor
  *
- *	The input layer calls this function when input event is opened. The
- *	function will push the device to resume. Then, the device is ready
- *	to provide data.
+ *	The function gets called when the sensor is enabled via sysfs.
+ *      Interrupts will be enabled and the device will be ready to provide data.
+ *
  */
-static int mpu3050_input_open(struct input_dev *input)
+static int mpu3050_start(struct mpu3050_sensor *sensor)
 {
-	struct mpu3050_sensor *sensor = input_get_drvdata(input);
 	int error;
 
 	pm_runtime_get_sync(sensor->dev);
@@ -440,16 +476,15 @@
 }
 
 /**
- *	mpu3050_input_close	-	called on input event close
- *	@input: input dev of closed device
+ *	mpu3050_stop	-	called when sensor is disabled via sysfs
+ *	@sensor: the sensor
  *
- *	The input layer calls this function when input event is closed. The
- *	function will push the device to suspend.
+ *	The function gets called when the sensor is disabled via sysfs.
+ *      Device will be pushed to suspend mode.
+ *
  */
-static void mpu3050_input_close(struct input_dev *input)
+static void mpu3050_stop(struct mpu3050_sensor *sensor)
 {
-	struct mpu3050_sensor *sensor = input_get_drvdata(input);
-
 	if (sensor->use_poll)
 		cancel_delayed_work_sync(&sensor->input_work);
 
@@ -595,6 +630,7 @@
 	}
 
 	mpu3050_set_power_mode(client, 1);
+	atomic_set(&sensor->enabled, 1);
 	msleep(10);
 
 	ret = i2c_smbus_read_byte_data(client, MPU3050_CHIP_ID_REG);
@@ -614,9 +650,6 @@
 	idev->id.bustype = BUS_I2C;
 	idev->dev.parent = &client->dev;
 
-	idev->open = mpu3050_input_open;
-	idev->close = mpu3050_input_close;
-
 	__set_bit(EV_ABS, idev->evbit);
 	input_set_abs_params(idev, ABS_X,
 			     MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);
@@ -739,8 +772,10 @@
 static int mpu3050_suspend(struct device *dev)
 {
 	struct i2c_client *client = to_i2c_client(dev);
-
-	mpu3050_set_power_mode(client, 0);
+	struct mpu3050_sensor *sensor = i2c_get_clientdata(client);
+	if (atomic_cmpxchg(&sensor->enabled, 1, 0)) {
+		mpu3050_set_power_mode (sensor->client, 0);
+	}
 
 	return 0;
 }
@@ -754,8 +789,10 @@
 static int mpu3050_resume(struct device *dev)
 {
 	struct i2c_client *client = to_i2c_client(dev);
-
-	mpu3050_set_power_mode(client, 1);
+	struct mpu3050_sensor *sensor = i2c_get_clientdata(client);
+	if (!atomic_cmpxchg(&sensor->enabled, 0, 1)) {
+		mpu3050_set_power_mode (sensor->client, 1);
+	}
 	msleep(100);  /* wait for gyro chip resume */
 
 	return 0;