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;