Re: [PATCH] Input: mpu3050: set IRQF_ONESHOT when requesting the interrupt

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, May 09, 2012 at 01:35:23PM -0600, Stephen Warren wrote:
> On 05/08/2012 11:23 PM, Dmitry Torokhov wrote:
> > Hi Stephen,
> > 
> > On Tue, May 01, 2012 at 11:46:46AM -0600, Stephen Warren wrote:
> >> From: Stephen Warren <swarren@xxxxxxxxxx>
> >>
> >> Commit 1c6c695 "genirq: Reject bogus threaded irq requests" requires
> >> that request_threaded_irq() either be passed an explicit handler, or
> >> that IRQF_ONESHOT be set. Set this flag.
> >>
> >> Signed-off-by: Stephen Warren <swarren@xxxxxxxxxx>
> > 
> > Applied, thank you.
> > 
> > BTW, since it appears you have the hardware any chance you could try the
> > patch below? I had it in my queue for a while but Alan disappeared and
> > I didn't have anyone to test it.
> 
> I tested the patch with an interrupt supplied, and still see periodic
> output from evbug that appears to correlate with when I move the board.
> 
> I'm not sure how to test it in polled mode. What application would I use
> to trigger the polling? evtest just seems to print out the initial
> values, but not poll.

Then there is a problem with the patch; once someone (i.e. evtest) opens
input device kernel should start polling at givein interval...

I think I know what might be wrong. We need to make sure we are calling
runtime PM APIs instead of trying to wake up chip directly because
parent might be sleeping as well.

Does the version below work any better?

Thanks!

-- 
Dmitry


Input: mpu3050 - Support for polled input device

From: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx>

The driver does not need to depend on interrupts.

Signed-off-by: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx>
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@xxxxxxxxxxxxxxx>
Signed-off-by: Alan Cox <alan@xxxxxxxxxxxxxxx>
Signed-off-by: Dmitry Torokhov <dtor@xxxxxxx>
---

 drivers/input/misc/Kconfig   |    7 +
 drivers/input/misc/mpu3050.c |  267 +++++++++++++++++++++++++++++++++---------
 2 files changed, 218 insertions(+), 56 deletions(-)


diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 7faf4a7..8d41bdc 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -177,6 +177,13 @@ config INPUT_MPU3050
 	  To compile this driver as a module, choose M here: the
 	  module will be called mpu3050.
 
+config INPUT_MPU3050_POLLED_MODE
+	bool "Enable polling mode support"
+	depends on INPUT_MPU3050
+	select INPUT_POLLDEV
+	help
+	  Say Y here if you need gyroscope to work in polling mode.
+
 config INPUT_APANEL
 	tristate "Fujitsu Lifebook Application Panel buttons"
 	depends on X86 && I2C && LEDS_CLASS
diff --git a/drivers/input/misc/mpu3050.c b/drivers/input/misc/mpu3050.c
index 306f84c..58926cb 100644
--- a/drivers/input/misc/mpu3050.c
+++ b/drivers/input/misc/mpu3050.c
@@ -37,6 +37,7 @@
 #include <linux/err.h>
 #include <linux/i2c.h>
 #include <linux/input.h>
+#include <linux/input-polldev.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/pm_runtime.h>
@@ -102,6 +103,8 @@
 #define MPU3050_PWR_MGM_RESET		0x80
 #define MPU3050_PWR_MGM_MASK		0x40
 
+#define MPU3050_DEFAULT_POLL_INTERVAL	200
+
 struct axis_data {
 	s16 x;
 	s16 y;
@@ -112,6 +115,9 @@ struct mpu3050_sensor {
 	struct i2c_client *client;
 	struct device *dev;
 	struct input_dev *idev;
+#ifdef CONFIG_INPUT_MPU3050_POLLED_MODE
+	struct input_polled_dev *input_polled;
+#endif
 };
 
 /**
@@ -169,6 +175,40 @@ static void mpu3050_read_xyz(struct i2c_client *client,
 }
 
 /**
+ *	mpu3050_report_xyz	-	read and report coordinates
+ *	@sensor: the sensor
+ *
+ *	Reads coordinates from the device and reports them to input
+ *	core.
+ */
+static void mpu3050_report_xyz(struct mpu3050_sensor *sensor)
+{
+	struct axis_data axis;
+
+	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);
+}
+
+/**
+ *	mpu3050_interrupt_thread	-	handle an IRQ
+ *	@irq: interrupt number
+ *	@data: the sensor
+ *
+ *	Called by the kernel single threaded after an interrupt occurs. Read
+ *	the sensor data and generate an input event for it.
+ */
+static irqreturn_t mpu3050_interrupt_thread(int irq, void *data)
+{
+	mpu3050_report_xyz(data);
+
+	return IRQ_HANDLED;
+}
+
+/**
  *	mpu3050_set_power_mode	-	set the power mode
  *	@client: i2c client for the sensor
  *	@val: value to switch on/off of power, 1: normal power, 0: low power
@@ -228,30 +268,153 @@ static void mpu3050_input_close(struct input_dev *input)
 	pm_runtime_put(sensor->dev);
 }
 
+static void __devinit mpu3050_init_idev(struct mpu3050_sensor *sensor,
+						struct input_dev *idev)
+{
+	idev->name = "MPU3050";
+	idev->id.bustype = BUS_I2C;
+	idev->dev.parent = &sensor->client->dev;
+
+	__set_bit(EV_ABS, idev->evbit);
+	input_set_abs_params(idev, ABS_X,
+			     MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);
+	input_set_abs_params(idev, ABS_Y,
+			     MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);
+	input_set_abs_params(idev, ABS_Z,
+			     MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);
+}
+
 /**
- *	mpu3050_interrupt_thread	-	handle an IRQ
- *	@irq: interrupt numner
- *	@data: the sensor
+ *	mpu3050_create_polled_idev - creates input device for the sensor
+ *	@sensor: the sensor
  *
- *	Called by the kernel single threaded after an interrupt occurs. Read
- *	the sensor data and generate an input event for it.
+ *	Called from mpu3050_probe() when setting up device in interrupt-driven
+ *	mode.
  */
-static irqreturn_t mpu3050_interrupt_thread(int irq, void *data)
+static int __devinit mpu3050_create_idev(struct mpu3050_sensor *sensor)
 {
-	struct mpu3050_sensor *sensor = data;
-	struct axis_data axis;
+	struct input_dev *idev;
+	int err;
 
-	mpu3050_read_xyz(sensor->client, &axis);
+	idev = input_allocate_device();
+	if (!idev)
+		return -ENODEV;
 
-	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);
+	mpu3050_init_idev(sensor, idev);
 
-	return IRQ_HANDLED;
+	idev->open = mpu3050_input_open;
+	idev->close = mpu3050_input_close;
+	input_set_drvdata(idev, sensor);
+
+	err = input_register_device(idev);
+	if (err) {
+		input_free_device(idev);
+		return err;
+	}
+
+	sensor->idev = idev;
+
+	return 0;
+}
+
+#ifdef CONFIG_INPUT_MPU3050_POLLED_MODE
+/**
+ *	mpu3050_poll_open	-	called when opening polled device
+ *	@ipoll_dev: polled input device being opened
+ *
+ *	The input layer calls this function when polled device is opened.
+ *	The function will cause the device to resume. Then, the device is
+ *	ready to provide data.
+ */
+static void mpu3050_poll_open(struct input_polled_dev *ipoll_dev)
+{
+	struct mpu3050_sensor *sensor = ipoll_dev->private;
+
+	pm_runtime_get_sync(sensor->dev);
+}
+
+/**
+ *	mpu3050_poll_close	-	called when closing polled device
+ *	@ipoll_dev: polled input device being closed
+ *
+ *	The input layer calls this function when polled device is closed.
+ *	The function will push the device to suspend.
+ */
+static void mpu3050_poll_close(struct input_polled_dev *ipoll_dev)
+{
+	struct mpu3050_sensor *sensor = ipoll_dev->private;
+
+	pm_runtime_put(sensor->dev);
 }
 
 /**
+ *	mpu3050_poll	-	polls the device and reports coordinated
+ *	@dev: device that is being polled
+ *
+ *	Called by polled device core at specified intervals.
+ */
+static void mpu3050_poll(struct input_polled_dev *dev)
+{
+	mpu3050_report_xyz(dev->private);
+}
+
+/**
+ *	mpu3050_create_polled_idev - creates polled input device for the sensor
+ *	@sensor: the sensor
+ *
+ *	Called from mpu3050_probe() when setting up device in polled mode.
+ */
+static int __devinit mpu3050_create_polled_idev(struct mpu3050_sensor *sensor)
+{
+	struct input_polled_dev *ipoll_dev;
+	int err;
+
+	ipoll_dev = input_allocate_polled_device();
+	if (!ipoll_dev)
+		return -ENOMEM;
+
+	sensor->input_polled = ipoll_dev;
+	sensor->idev = ipoll_dev->input;
+
+	ipoll_dev->private = sensor;
+	ipoll_dev->open = mpu3050_poll_open;
+	ipoll_dev->close = mpu3050_poll_close;
+	ipoll_dev->poll = mpu3050_poll;
+	ipoll_dev->poll_interval = MPU3050_DEFAULT_POLL_INTERVAL;
+	ipoll_dev->poll_interval_min = 10;
+	ipoll_dev->poll_interval_max = MPU3050_DEFAULT_POLL_INTERVAL;
+
+	mpu3050_init_idev(sensor, ipoll_dev->input);
+
+	err = input_register_polled_device(ipoll_dev);
+	if (err) {
+		input_free_polled_device(ipoll_dev);
+		return err;
+	}
+
+	return 0;
+}
+
+static void __devexit mpu3050_teardown_polled_idev(struct mpu3050_sensor *sensor)
+{
+	input_unregister_polled_device(sensor->input_polled);
+	input_free_polled_device(sensor->input_polled);
+}
+
+#else
+
+static inline int mpu3050_create_polled_idev(struct mpu3050_sensor *sensor)
+{
+	return -ENOSYS;
+}
+
+static inline void mpu3050_teardown_polled_idev(struct mpu3050_sensor *sensor)
+{
+}
+
+#endif
+
+/**
  *	mpu3050_hw_init	-	initialize hardware
  *	@sensor: the sensor
  *
@@ -310,21 +473,17 @@ static int __devinit mpu3050_probe(struct i2c_client *client,
 				   const struct i2c_device_id *id)
 {
 	struct mpu3050_sensor *sensor;
-	struct input_dev *idev;
 	int ret;
 	int error;
 
 	sensor = kzalloc(sizeof(struct mpu3050_sensor), GFP_KERNEL);
-	idev = input_allocate_device();
-	if (!sensor || !idev) {
+	if (!sensor) {
 		dev_err(&client->dev, "failed to allocate driver data\n");
-		error = -ENOMEM;
-		goto err_free_mem;
+		return -ENOMEM;
 	}
 
 	sensor->client = client;
 	sensor->dev = &client->dev;
-	sensor->idev = idev;
 
 	mpu3050_set_power_mode(client, 1);
 	msleep(10);
@@ -342,43 +501,35 @@ static int __devinit mpu3050_probe(struct i2c_client *client,
 		goto err_free_mem;
 	}
 
-	idev->name = "MPU3050";
-	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);
-	input_set_abs_params(idev, ABS_Y,
-			     MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);
-	input_set_abs_params(idev, ABS_Z,
-			     MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);
-
-	input_set_drvdata(idev, sensor);
-
 	pm_runtime_set_active(&client->dev);
 
 	error = mpu3050_hw_init(sensor);
 	if (error)
 		goto err_pm_set_suspended;
 
-	error = request_threaded_irq(client->irq,
-				     NULL, mpu3050_interrupt_thread,
-				     IRQF_TRIGGER_RISING | IRQF_ONESHOT,
-				     "mpu3050", sensor);
-	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);
-	if (error) {
-		dev_err(&client->dev, "failed to register input device\n");
-		goto err_free_irq;
+	if (client->irq) {
+		error = mpu3050_create_idev(sensor);
+		if (error) {
+			dev_err(&client->dev, "failed to create input device\n");
+			goto err_pm_set_suspended;
+		}
+
+		error = request_threaded_irq(client->irq,
+					     NULL, mpu3050_interrupt_thread,
+					     IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+					     "mpu3050", sensor);
+		if (error) {
+			dev_err(&client->dev, "can't get IRQ %d, error %d\n",
+					client->irq, error);
+			goto err_destroy_input;
+		}
+	} else {
+		error = mpu3050_create_polled_idev(sensor);
+		if (error) {
+			dev_err(&client->dev,
+				"failed to create polled input device");
+			goto err_pm_set_suspended;
+		}
 	}
 
 	pm_runtime_enable(&client->dev);
@@ -386,12 +537,11 @@ static int __devinit mpu3050_probe(struct i2c_client *client,
 
 	return 0;
 
-err_free_irq:
-	free_irq(client->irq, sensor);
+err_destroy_input:
+	input_unregister_device(sensor->idev);
 err_pm_set_suspended:
 	pm_runtime_set_suspended(&client->dev);
 err_free_mem:
-	input_free_device(idev);
 	kfree(sensor);
 	return error;
 }
@@ -409,8 +559,13 @@ static int __devexit mpu3050_remove(struct i2c_client *client)
 	pm_runtime_disable(&client->dev);
 	pm_runtime_set_suspended(&client->dev);
 
-	free_irq(client->irq, sensor);
-	input_unregister_device(sensor->idev);
+	if (client->irq) {
+		free_irq(client->irq, sensor);
+		input_unregister_device(sensor->idev);
+	} else {
+		mpu3050_teardown_polled_idev(sensor);
+	}
+
 	kfree(sensor);
 
 	return 0;
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux