Linux Input/HID development
 help / color / mirror / Atom feed
* Re: [PATCH] Input: egalax_ts - Provide a .remove function
From: Dmitry Torokhov @ 2013-07-24 21:08 UTC (permalink / raw)
  To: Fabio Estevam; +Cc: shawn.guo, linux-input
In-Reply-To: <1374698769-25664-1-git-send-email-fabio.estevam@freescale.com>

Hi Fabio,

On Wednesday, July 24, 2013 05:46:09 PM Fabio Estevam wrote:
> Provide a .remove function so that we can unregister the input device.
> 
> Signed-off-by: Fabio Estevam <fabio.estevam@freescale.com>
> ---
>  drivers/input/touchscreen/egalax_ts.c | 11 +++++++++++
>  1 file changed, 11 insertions(+)
> 
> diff --git a/drivers/input/touchscreen/egalax_ts.c
> b/drivers/input/touchscreen/egalax_ts.c index ef5fcb0..e46be61 100644
> --- a/drivers/input/touchscreen/egalax_ts.c
> +++ b/drivers/input/touchscreen/egalax_ts.c
> @@ -234,6 +234,16 @@ static int egalax_ts_probe(struct i2c_client *client,
>  	return 0;
>  }
> 
> +static int egalax_ts_remove(struct i2c_client *client)
> +{
> +	struct egalax_ts *ts = i2c_get_clientdata(client);
> +
> +	input_unregister_device(ts->input_dev);
> +	kfree(ts);


The egalax_ts driver has been converted to devm* infrastructure, using kfree() 
to free memory not only unnecessary, but wrong.

Thanks.

-- 
Dmitry

^ permalink raw reply

* [RESEND PATCH v5] Input: sysrq - DT binding for key sequence
From: mathieu.poirier @ 2013-07-24 22:16 UTC (permalink / raw)
  To: grant.likely, dmitry.torokhov
  Cc: devicetree-discuss, john.stultz, linux-input, kernel-team,
	mathieu.poirier

I haven't heard back from anyone after making the last modifications
a week ago.  If it's all good with you Rob, please ack it so that 
Dmitry can pick it up on his side.

Thanks,
Mathieu.

>From 37318cb3c6339747f4e96e09f07f732a395a5ae1 Mon Sep 17 00:00:00 2001
From: "Mathieu J. Poirier" <mathieu.poirier@linaro.org>
Date: Wed, 10 Jul 2013 09:53:35 -0600
Subject: [RESEND PATCH v5] Input: sysrq - DT binding for key sequence

Adding a simple device tree binding for the specification of key sequences.
Definition of the keys found in the sequence are located in
'include/uapi/linux/input.h'.

For the sysrq driver, holding the sequence of keys down for a specific amount of time
will reset the system.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
changes for v5:
  - Corrected error in binding definition.
  - Using helper macro to fetch the key sequence.
  - Removing white space.
---
 .../devicetree/bindings/input/input-reset.txt      | 34 ++++++++++++++++++++
 drivers/tty/sysrq.c                                | 37 ++++++++++++++++++++++
 2 files changed, 71 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/input-reset.txt

diff --git a/Documentation/devicetree/bindings/input/input-reset.txt b/Documentation/devicetree/bindings/input/input-reset.txt
new file mode 100644
index 0000000..c69390c
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/input-reset.txt
@@ -0,0 +1,34 @@
+Input: sysrq reset sequence
+
+A simple binding to represent a set of keys as described in
+include/uapi/linux/input.h.  This is to communicate a
+sequence of keys to the sysrq driver.  Upon holding the keys
+for a specified amount of time (if specified) the system is
+sync'ed and reset.
+
+Key sequences are global to the system but all the keys in a
+set must be coming from the same input device.
+
+The /chosen node should contain a 'linux,sysrq-reset-seq' child
+node to define a set of keys.
+
+Required property:
+sysrq-reset-seq: array of keycodes
+
+Optional property:
+timeout-ms: duration keys must be pressed together in milliseconds
+before generating a sysrq
+
+Example:
+
+ chosen {
+                linux,sysrq-reset-seq {
+                        keyset = <0x03
+                                  0x04
+                                  0x0a>;
+                        timeout-ms = <3000>;
+                };
+         };
+
+Would represent KEY_2, KEY_3 and KEY_9.
+
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index b51c154..ed8f00f 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -44,6 +44,7 @@
 #include <linux/uaccess.h>
 #include <linux/moduleparam.h>
 #include <linux/jiffies.h>
+#include <linux/of.h>
 
 #include <asm/ptrace.h>
 #include <asm/irq_regs.h>
@@ -671,6 +672,34 @@ static void sysrq_detect_reset_sequence(struct sysrq_state *state,
 	}
 }
 
+static void sysrq_of_get_keyreset_config(void)
+{
+	u32 key;
+	struct device_node *np;
+	struct property *prop;
+	const __be32 *p;
+
+	np = of_find_node_by_path("/chosen/linux,sysrq-reset-seq");
+	if (!np) {
+		pr_debug("No sysrq node found");
+		return;
+	}
+
+	/* reset in case a __weak definition was present */
+	sysrq_reset_seq_len = 0;
+
+	of_property_for_each_u32(np, "keyset", prop, p, key) {
+		if ((key == KEY_RESERVED || key > KEY_MAX) ||
+			(sysrq_reset_seq_len == SYSRQ_KEY_RESET_MAX))
+			break;
+
+		sysrq_reset_seq[sysrq_reset_seq_len++] = (unsigned short)key;
+	}
+
+	/* get reset timeout if any */
+	of_property_read_u32(np, "timeout-ms", &sysrq_reset_downtime_ms);
+}
+
 static void sysrq_reinject_alt_sysrq(struct work_struct *work)
 {
 	struct sysrq_state *sysrq =
@@ -903,6 +932,7 @@ static inline void sysrq_register_handler(void)
 	int error;
 	int i;
 
+	/* first check if a __weak interface was instantiated */
 	for (i = 0; i < ARRAY_SIZE(sysrq_reset_seq); i++) {
 		key = platform_sysrq_reset_seq[i];
 		if (key == KEY_RESERVED || key > KEY_MAX)
@@ -911,6 +941,13 @@ static inline void sysrq_register_handler(void)
 		sysrq_reset_seq[sysrq_reset_seq_len++] = key;
 	}
 
+	/*
+	 * DT configuration takes precedence over anything
+	 * that would have been defined via the __weak
+	 * interface
+	 */
+	sysrq_of_get_keyreset_config();
+
 	error = input_register_handler(&sysrq_handler);
 	if (error)
 		pr_err("Failed to register input handler, error %d", error);
-- 
1.8.1.2


^ permalink raw reply related

* [RESEND PATCH v5] Input: sysrq - DT binding for key sequence
From: mathieu.poirier @ 2013-07-24 22:35 UTC (permalink / raw)
  To: robherring2, grant.likely, dmitry.torokhov
  Cc: devicetree, john.stultz, linux-input, kernel-team,
	mathieu.poirier

Sorry about the noise - device tree mailing has changed.

I haven't heard back from anyone after making the last modifications
a week ago.  If it's all good with you Rob, please ack it so that 
Dmitry can pick it up on his side.

Thanks,
Mathieu.

>From 37318cb3c6339747f4e96e09f07f732a395a5ae1 Mon Sep 17 00:00:00 2001
From: "Mathieu J. Poirier" <mathieu.poirier@linaro.org>
Date: Wed, 10 Jul 2013 09:53:35 -0600
Subject: [RESEND PATCH v5] Input: sysrq - DT binding for key sequence

Adding a simple device tree binding for the specification of key sequences.
Definition of the keys found in the sequence are located in
'include/uapi/linux/input.h'.

For the sysrq driver, holding the sequence of keys down for a specific amount of time
will reset the system.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
changes for v5:
  - Corrected error in binding definition.
  - Using helper macro to fetch the key sequence.
  - Removing white space.
---
 .../devicetree/bindings/input/input-reset.txt      | 34 ++++++++++++++++++++
 drivers/tty/sysrq.c                                | 37 ++++++++++++++++++++++
 2 files changed, 71 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/input-reset.txt

diff --git a/Documentation/devicetree/bindings/input/input-reset.txt b/Documentation/devicetree/bindings/input/input-reset.txt
new file mode 100644
index 0000000..c69390c
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/input-reset.txt
@@ -0,0 +1,34 @@
+Input: sysrq reset sequence
+
+A simple binding to represent a set of keys as described in
+include/uapi/linux/input.h.  This is to communicate a
+sequence of keys to the sysrq driver.  Upon holding the keys
+for a specified amount of time (if specified) the system is
+sync'ed and reset.
+
+Key sequences are global to the system but all the keys in a
+set must be coming from the same input device.
+
+The /chosen node should contain a 'linux,sysrq-reset-seq' child
+node to define a set of keys.
+
+Required property:
+sysrq-reset-seq: array of keycodes
+
+Optional property:
+timeout-ms: duration keys must be pressed together in milliseconds
+before generating a sysrq
+
+Example:
+
+ chosen {
+                linux,sysrq-reset-seq {
+                        keyset = <0x03
+                                  0x04
+                                  0x0a>;
+                        timeout-ms = <3000>;
+                };
+         };
+
+Would represent KEY_2, KEY_3 and KEY_9.
+
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index b51c154..ed8f00f 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -44,6 +44,7 @@
 #include <linux/uaccess.h>
 #include <linux/moduleparam.h>
 #include <linux/jiffies.h>
+#include <linux/of.h>
 
 #include <asm/ptrace.h>
 #include <asm/irq_regs.h>
@@ -671,6 +672,34 @@ static void sysrq_detect_reset_sequence(struct sysrq_state *state,
 	}
 }
 
+static void sysrq_of_get_keyreset_config(void)
+{
+	u32 key;
+	struct device_node *np;
+	struct property *prop;
+	const __be32 *p;
+
+	np = of_find_node_by_path("/chosen/linux,sysrq-reset-seq");
+	if (!np) {
+		pr_debug("No sysrq node found");
+		return;
+	}
+
+	/* reset in case a __weak definition was present */
+	sysrq_reset_seq_len = 0;
+
+	of_property_for_each_u32(np, "keyset", prop, p, key) {
+		if ((key == KEY_RESERVED || key > KEY_MAX) ||
+			(sysrq_reset_seq_len == SYSRQ_KEY_RESET_MAX))
+			break;
+
+		sysrq_reset_seq[sysrq_reset_seq_len++] = (unsigned short)key;
+	}
+
+	/* get reset timeout if any */
+	of_property_read_u32(np, "timeout-ms", &sysrq_reset_downtime_ms);
+}
+
 static void sysrq_reinject_alt_sysrq(struct work_struct *work)
 {
 	struct sysrq_state *sysrq =
@@ -903,6 +932,7 @@ static inline void sysrq_register_handler(void)
 	int error;
 	int i;
 
+	/* first check if a __weak interface was instantiated */
 	for (i = 0; i < ARRAY_SIZE(sysrq_reset_seq); i++) {
 		key = platform_sysrq_reset_seq[i];
 		if (key == KEY_RESERVED || key > KEY_MAX)
@@ -911,6 +941,13 @@ static inline void sysrq_register_handler(void)
 		sysrq_reset_seq[sysrq_reset_seq_len++] = key;
 	}
 
+	/*
+	 * DT configuration takes precedence over anything
+	 * that would have been defined via the __weak
+	 * interface
+	 */
+	sysrq_of_get_keyreset_config();
+
 	error = input_register_handler(&sysrq_handler);
 	if (error)
 		pr_err("Failed to register input handler, error %d", error);
-- 
1.8.1.2


^ permalink raw reply related

* [PATCH]  Input: max11801_ts - convert to devm
From: Fabio Estevam @ 2013-07-24 23:05 UTC (permalink / raw)
  To: dmitry.torokhov; +Cc: linux-input, Fabio Estevam

From: Fabio Estevam <fabio.estevam@freescale.com>

Converting to devm functions can make the code smaller and cleaner.

Signed-off-by: Fabio Estevam <fabio.estevam@freescale.com>
---
 drivers/input/touchscreen/max11801_ts.c | 37 ++++++++-------------------------
 1 file changed, 9 insertions(+), 28 deletions(-)

diff --git a/drivers/input/touchscreen/max11801_ts.c b/drivers/input/touchscreen/max11801_ts.c
index 00bc6ca..9f84fcd 100644
--- a/drivers/input/touchscreen/max11801_ts.c
+++ b/drivers/input/touchscreen/max11801_ts.c
@@ -181,12 +181,11 @@ static int max11801_ts_probe(struct i2c_client *client,
 	struct input_dev *input_dev;
 	int error;
 
-	data = kzalloc(sizeof(struct max11801_data), GFP_KERNEL);
-	input_dev = input_allocate_device();
+	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+	input_dev = devm_input_allocate_device(&client->dev);
 	if (!data || !input_dev) {
 		dev_err(&client->dev, "Failed to allocate memory\n");
-		error = -ENOMEM;
-		goto err_free_mem;
+		return -ENOMEM;
 	}
 
 	data->client = client;
@@ -205,38 +204,21 @@ static int max11801_ts_probe(struct i2c_client *client,
 
 	max11801_ts_phy_init(data);
 
-	error = request_threaded_irq(client->irq, NULL, max11801_ts_interrupt,
-				     IRQF_TRIGGER_LOW | IRQF_ONESHOT,
-				     "max11801_ts", data);
+	error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+					  max11801_ts_interrupt,
+					  IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+					  "max11801_ts", data);
 	if (error) {
 		dev_err(&client->dev, "Failed to register interrupt\n");
-		goto err_free_mem;
+		return error;
 	}
 
 	error = input_register_device(data->input_dev);
 	if (error)
-		goto err_free_irq;
+		return error;
 
 	i2c_set_clientdata(client, data);
 	return 0;
-
-err_free_irq:
-	free_irq(client->irq, data);
-err_free_mem:
-	input_free_device(input_dev);
-	kfree(data);
-	return error;
-}
-
-static int max11801_ts_remove(struct i2c_client *client)
-{
-	struct max11801_data *data = i2c_get_clientdata(client);
-
-	free_irq(client->irq, data);
-	input_unregister_device(data->input_dev);
-	kfree(data);
-
-	return 0;
 }
 
 static const struct i2c_device_id max11801_ts_id[] = {
@@ -252,7 +234,6 @@ static struct i2c_driver max11801_ts_driver = {
 	},
 	.id_table	= max11801_ts_id,
 	.probe		= max11801_ts_probe,
-	.remove		= max11801_ts_remove,
 };
 
 module_i2c_driver(max11801_ts_driver);
-- 
1.8.1.2


^ permalink raw reply related

* [RFC ebeam PATCH 0/2] new USB eBeam input driver
From: Yann Cantin @ 2013-07-24 23:52 UTC (permalink / raw)
  To: linux-input, linux-usb
  Cc: linux-kernel, linux-doc, dmitry.torokhov, jkosina, rob, gregkh

Hi,

New USB input driver for eBeam devices.

Currently supported (tested) :
- Luidia eBeam classic projection and edge projection models
- Nec "interactive solution" NP01Wi1 & NP01Wi2 accessories.

>From basic usb point of view, all these devices are
indistinguishable : they have the same usb ids and (blank) hid
report descriptors. There's other re-branded hardware that need to be
test, but i bet on same ids.

Patch 1 to blacklist the devices for hid generic-usb.

Patch 2 is the actual driver.

Notable stuff :
- use div64_s64 for portable 64/64-bits divisions.
- 13 sysfs custom files : 9 values for the transformation matrix,
  4 for xy ranges and a calibration trigger.

The module run fine since 3.3.6 kernel, both x86_32 and 64, actually
run in production on a 3.8.13 and compile without errors on
today's linus tree.

Side note :
- This driver was first submit and discuss last year.

Thanks for your help.


^ permalink raw reply

* [RFC ebeam PATCH 1/2] hid: Blacklist eBeam devices
From: Yann Cantin @ 2013-07-24 23:52 UTC (permalink / raw)
  To: linux-input, linux-usb
  Cc: linux-kernel, linux-doc, dmitry.torokhov, jkosina, rob, gregkh,
	Yann Cantin
In-Reply-To: <1374709940-607-1-git-send-email-yann.cantin@laposte.net>


Signed-off-by: Yann Cantin <yann.cantin@laposte.net>
---
 drivers/hid/hid-core.c | 3 +++
 drivers/hid/hid-ids.h  | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 36668d1..da5dfa0 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1985,6 +1985,9 @@ static const struct hid_device_id hid_ignore_list[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x0004) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x000a) },
+#if defined(CONFIG_INPUT_EBEAM_USB)
+	{ HID_USB_DEVICE(USB_VENDOR_ID_EFI, USB_DEVICE_ID_EFI_EBEAM) },
+#endif
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC5UH) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC4UM) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index ffe4c7a..abd1c53 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -288,6 +288,9 @@
 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_73F7	0x73f7
 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001	0xa001
 
+#define USB_VENDOR_ID_EFI		0x2650
+#define USB_DEVICE_ID_EFI_EBEAM		0x1311
+
 #define USB_VENDOR_ID_ELECOM		0x056e
 #define USB_DEVICE_ID_ELECOM_BM084	0x0061
 
-- 
1.8.1.5


^ permalink raw reply related

* [RFC ebeam PATCH 2/2] input: misc: New USB eBeam input driver
From: Yann Cantin @ 2013-07-24 23:52 UTC (permalink / raw)
  To: linux-input, linux-usb
  Cc: linux-kernel, linux-doc, dmitry.torokhov, jkosina, rob, gregkh,
	Yann Cantin
In-Reply-To: <1374709940-607-1-git-send-email-yann.cantin@laposte.net>


Signed-off-by: Yann Cantin <yann.cantin@laposte.net>
---
 Documentation/ABI/testing/sysfs-driver-ebeam |  53 ++
 drivers/input/misc/Kconfig                   |  22 +
 drivers/input/misc/Makefile                  |   1 +
 drivers/input/misc/ebeam.c                   | 763 +++++++++++++++++++++++++++
 4 files changed, 839 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-driver-ebeam
 create mode 100644 drivers/input/misc/ebeam.c

diff --git a/Documentation/ABI/testing/sysfs-driver-ebeam b/Documentation/ABI/testing/sysfs-driver-ebeam
new file mode 100644
index 0000000..552a428
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-driver-ebeam
@@ -0,0 +1,53 @@
+What:		/sys/class/input/inputXX/device/min_x
+		/sys/class/input/inputXX/device/min_y
+		/sys/class/input/inputXX/device/max_x
+		/sys/class/input/inputXX/device/max_y
+Date:		Jul 2013
+Kernel Version:	3.11
+Contact:	yann.cantin@laposte.net
+		linux-usb@vger.kernel.org
+Description:
+		Reading from these files return the actually used range values of
+		the reported coordinates.
+		Writing to these files preset these range values.
+		See below for the calibration procedure.
+
+What:		/sys/class/input/inputXX/device/h[1..9]
+Date:		Jul 2013
+Kernel Version:	3.11
+Contact:	yann.cantin@laposte.net
+		linux-usb@vger.kernel.org
+Description:
+		Reading from these files return the 3x3 transformation matrix elements
+		actually used, in row-major.
+		Writing to these files preset these elements values.
+		See below for the calibration procedure.
+
+What:		/sys/class/input/inputXX/device/calibrated
+Date:		Jul 2013
+Kernel Version:	3.11
+Contact:	yann.cantin@laposte.net
+		linux-usb@vger.kernel.org
+Description:
+		Reading from this file :
+		- Return 0 if the driver is in "un-calibrated" mode : it actually send
+		  raw coordinates in the device's internal coordinates system.
+		- Return 1 if the driver is in "calibrated" mode : it send computed coordinates
+		  that (hopefully) matches the screen's coordinates system.
+		Writing 1 to this file enable the "calibrated" mode, 0 reset the driver in
+		"un-calibrated" mode.
+
+Calibration procedure :
+
+When loaded, the driver is in "un-calibrated" mode : it send device's raw coordinates
+in the [0..65535]x[0..65535] range, the transformation matrix is the identity.
+
+A calibration program have to compute a homography transformation matrix that convert
+the device's raw coordinates to the matching screen's ones.
+It then write to the appropriate sysfs files the computed values, pre-setting the
+driver's parameters : xy range, and the matrix's elements.
+When all values are passed, it write 1 to the calibrated sysfs file to enable the "calibrated" mode.
+
+Warning : The parameters aren't used until 1 is writen to the calibrated sysfs file.
+
+Writing 0 to the calibrated sysfs file reset the driver in "un-calibrated" mode.
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 0b541cd..454df2d 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -93,6 +93,28 @@ config INPUT_BMA150
 	  To compile this driver as a module, choose M here: the
 	  module will be called bma150.
 
+config INPUT_EBEAM_USB
+	tristate "USB eBeam driver"
+	depends on USB_ARCH_HAS_HCD
+	select USB
+	help
+	  Say Y here if you have a USB eBeam pointing device and want to
+	  use it without any proprietary user space tools.
+
+	  Have a look at <http://sourceforge.net/projects/ebeam/> for
+	  a usage description and the required user-space tools.
+
+	  Supported devices :
+	    - Luidia eBeam Classic Projection and eBeam Edge Projection
+	    - Nec NP01Wi1 & NP01Wi2 interactive solution
+
+	  Supposed working devices, need test, may lack functionnality :
+	    - Luidia eBeam Edge Whiteboard and eBeam Engage
+	    - Hitachi Starboard FX-63, FX-77, FX-82, FX-77GII
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ebeam.
+
 config INPUT_PCSPKR
 	tristate "PC Speaker support"
 	depends on PCSPKR_PLATFORM
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 829de43..dd2fe33 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_INPUT_COBALT_BTNS)		+= cobalt_btns.o
 obj-$(CONFIG_INPUT_DA9052_ONKEY)	+= da9052_onkey.o
 obj-$(CONFIG_INPUT_DA9055_ONKEY)	+= da9055_onkey.o
 obj-$(CONFIG_INPUT_DM355EVM)		+= dm355evm_keys.o
+obj-$(CONFIG_INPUT_EBEAM_USB)		+= ebeam.o
 obj-$(CONFIG_INPUT_GP2A)		+= gp2ap002a00f.o
 obj-$(CONFIG_INPUT_GPIO_TILT_POLLED)	+= gpio_tilt_polled.o
 obj-$(CONFIG_HP_SDC_RTC)		+= hp_sdc_rtc.o
diff --git a/drivers/input/misc/ebeam.c b/drivers/input/misc/ebeam.c
new file mode 100644
index 0000000..cf551ab
--- /dev/null
+++ b/drivers/input/misc/ebeam.c
@@ -0,0 +1,763 @@
+/******************************************************************************
+ *
+ * eBeam driver
+ *
+ * Copyright (C) 2012 Yann Cantin (yann.cantin@laposte.net)
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation; either version 2 of the
+ *	License, or (at your option) any later version.
+ *
+ *  based on
+ *
+ *	usbtouchscreen.c by Daniel Ritz <daniel.ritz@gmx.ch>
+ *	aiptek.c (sysfs/settings) by Chris Atenasio <chris@crud.net>
+ *				     Bryan W. Headley <bwheadley@earthlink.net>
+ *
+ *****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/math64.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+
+#define DRIVER_AUTHOR	"Yann Cantin <yann.cantin@laposte.net>"
+#define DRIVER_DESC	"USB eBeam Driver"
+
+/* Common values for eBeam devices */
+#define REPT_SIZE	8		/* packet size		  */
+#define MIN_RAW_X	0		/* raw coordinates ranges */
+#define MAX_RAW_X	0xFFFF
+#define MIN_RAW_Y	0
+#define MAX_RAW_Y	0xFFFF
+
+
+#define USB_VENDOR_ID_EFI	0x2650	/* Electronics For Imaging, Inc	*/
+#define USB_DEVICE_ID_EFI_EBEAM	0x1311	/* eBeam hardware :		*/
+					/*	Luidia Classic and Edge	*/
+					/*	Nec NP01Wi1 & NP01Wi2	*/
+
+#define EBEAM_BTN_TIP	0x1      /* tip    */
+#define EBEAM_BTN_LIT	0x2      /* little */
+#define EBEAM_BTN_BIG	0x4      /* big    */
+
+/* ebeam settings */
+struct ebeam_settings {
+	int min_x;	/* computed coordinates ranges for the input layer */
+	int max_x;
+	int min_y;
+	int max_y;
+
+	/* H matrix */
+	s64 h1;
+	s64 h2;
+	s64 h3;
+	s64 h4;
+	s64 h5;
+	s64 h6;
+	s64 h7;
+	s64 h8;
+	s64 h9;
+};
+
+/* ebeam device */
+struct ebeam_device {
+	unsigned char		 *data;
+	dma_addr_t		 data_dma;
+	unsigned char		 *buffer;
+	int			 buf_len;
+	struct urb		 *irq;
+	struct usb_interface	 *interface;
+	struct input_dev	 *input;
+	char			 name[128];
+	char			 phys[64];
+	void			 *priv;
+
+	struct ebeam_settings	 cursetting;	/* device's current settings */
+	struct ebeam_settings	 newsetting;	/* ... and new ones	     */
+
+	bool			 calibrated;	/* false : send raw	     */
+						/* true  : send computed     */
+
+	u16			 raw_x, raw_y;	/* raw coordinates	     */
+	int			 x, y;		/* computed coordinates      */
+	int			 btn_map;	/* internal buttons map      */
+};
+
+/* device types */
+enum {
+	DEVTYPE_EBEAM,
+};
+
+static const struct usb_device_id ebeam_devices[] = {
+	{USB_DEVICE(USB_VENDOR_ID_EFI, USB_DEVICE_ID_EFI_EBEAM),
+			.driver_info = DEVTYPE_EBEAM},
+	{}
+};
+
+static void ebeam_init_settings(struct ebeam_device *ebeam)
+{
+	ebeam->calibrated = false;
+
+	/* Init (x,y) min/max to raw ones */
+	ebeam->cursetting.min_x = ebeam->newsetting.min_x = MIN_RAW_X;
+	ebeam->cursetting.max_x = ebeam->newsetting.max_x = MAX_RAW_X;
+	ebeam->cursetting.min_y = ebeam->newsetting.min_y = MIN_RAW_Y;
+	ebeam->cursetting.max_y = ebeam->newsetting.max_y = MAX_RAW_Y;
+
+	/* Safe values for the H matrix (Identity) */
+	ebeam->cursetting.h1 = ebeam->newsetting.h1 = 1;
+	ebeam->cursetting.h2 = ebeam->newsetting.h2 = 0;
+	ebeam->cursetting.h3 = ebeam->newsetting.h3 = 0;
+
+	ebeam->cursetting.h4 = ebeam->newsetting.h4 = 0;
+	ebeam->cursetting.h5 = ebeam->newsetting.h5 = 1;
+	ebeam->cursetting.h6 = ebeam->newsetting.h6 = 0;
+
+	ebeam->cursetting.h7 = ebeam->newsetting.h7 = 0;
+	ebeam->cursetting.h8 = ebeam->newsetting.h8 = 0;
+	ebeam->cursetting.h9 = ebeam->newsetting.h9 = 1;
+}
+
+static void ebeam_setup_input(struct ebeam_device *ebeam,
+			      struct input_dev *input_dev)
+{
+	unsigned long flags;
+
+	/* Take event lock while modifying parameters */
+	spin_lock_irqsave(&input_dev->event_lock, flags);
+
+	/* Properties */
+	set_bit(INPUT_PROP_DIRECT,  input_dev->propbit);
+
+	/* Events generated */
+	set_bit(EV_KEY, input_dev->evbit);
+	set_bit(EV_ABS, input_dev->evbit);
+
+	/* Keys */
+	set_bit(BTN_LEFT,   input_dev->keybit);
+	set_bit(BTN_MIDDLE, input_dev->keybit);
+	set_bit(BTN_RIGHT,  input_dev->keybit);
+
+	/* Axis */
+	if (!ebeam->calibrated) {
+		ebeam->cursetting.min_x = MIN_RAW_X;
+		ebeam->cursetting.max_x = MAX_RAW_X;
+		ebeam->cursetting.min_y = MIN_RAW_Y;
+		ebeam->cursetting.max_y = MAX_RAW_Y;
+	}
+
+	input_set_abs_params(input_dev, ABS_X,
+			     ebeam->cursetting.min_x, ebeam->cursetting.max_x,
+			     0, 0);
+	input_set_abs_params(input_dev, ABS_Y,
+			     ebeam->cursetting.min_y, ebeam->cursetting.max_y,
+			     0, 0);
+
+	spin_unlock_irqrestore(&input_dev->event_lock, flags);
+}
+
+/*******************************************************************************
+ * sysfs part
+ */
+
+/*
+ * screen min/max and H matrix coefs
+ * _get return *current* value
+ * _set set new value
+ */
+#define DEVICE_MINMAX_ATTR(MM)						       \
+static ssize_t ebeam_##MM##_get(struct device *dev,			       \
+				struct device_attribute *attr,		       \
+				char *buf)				       \
+{									       \
+	struct ebeam_device *ebeam = dev_get_drvdata(dev);		       \
+									       \
+	return snprintf(buf, PAGE_SIZE, "%d\n", ebeam->cursetting.MM);	       \
+}									       \
+static ssize_t ebeam_##MM##_set(struct device *dev,			       \
+				struct device_attribute *attr,		       \
+				const char *buf,			       \
+				size_t count)				       \
+{									       \
+	struct ebeam_device *ebeam = dev_get_drvdata(dev);		       \
+	int err, MM;							       \
+									       \
+	err = kstrtoint(buf, 10, &MM);					       \
+	if (err)							       \
+		return err;						       \
+									       \
+	ebeam->newsetting.MM = MM;					       \
+	return count;							       \
+}									       \
+static DEVICE_ATTR(MM, S_IRUGO | S_IWUGO,				       \
+		   ebeam_##MM##_get,					       \
+		   ebeam_##MM##_set)
+
+DEVICE_MINMAX_ATTR(min_x);
+DEVICE_MINMAX_ATTR(max_x);
+DEVICE_MINMAX_ATTR(min_y);
+DEVICE_MINMAX_ATTR(max_y);
+
+#define DEVICE_H_ATTR(SET_ID)						       \
+static ssize_t ebeam_h##SET_ID##_get(struct device *dev,		       \
+				     struct device_attribute *attr,	       \
+				     char *buf)				       \
+{									       \
+	struct ebeam_device *ebeam = dev_get_drvdata(dev);		       \
+									       \
+	return snprintf(buf, PAGE_SIZE, "%lld\n", ebeam->cursetting.h##SET_ID);\
+}									       \
+static ssize_t ebeam_h##SET_ID##_set(struct device *dev,		       \
+				     struct device_attribute *attr,	       \
+				     const char *buf,			       \
+				     size_t count)			       \
+{									       \
+	struct ebeam_device *ebeam = dev_get_drvdata(dev);		       \
+	int err;							       \
+	u64 h;								       \
+									       \
+	err = kstrtoll(buf, 10, &h);					       \
+	if (err)							       \
+		return err;						       \
+									       \
+	ebeam->newsetting.h##SET_ID = h;				       \
+	return count;							       \
+}									       \
+static DEVICE_ATTR(h##SET_ID, S_IRUGO | S_IWUGO,			       \
+		   ebeam_h##SET_ID##_get,				       \
+		   ebeam_h##SET_ID##_set)
+
+DEVICE_H_ATTR(1);
+DEVICE_H_ATTR(2);
+DEVICE_H_ATTR(3);
+DEVICE_H_ATTR(4);
+DEVICE_H_ATTR(5);
+DEVICE_H_ATTR(6);
+DEVICE_H_ATTR(7);
+DEVICE_H_ATTR(8);
+DEVICE_H_ATTR(9);
+
+/*
+ * sysfs calibrated
+ * Once H matrix coefs are set, writing 1 to this file triggers
+ * coordinates mapping.
+ * Anything else reset the device to un-calibrated mode,
+ * storing cursetting in newsetting.
+*/
+static ssize_t ebeam_calibrated_get(struct device *dev,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	struct ebeam_device *ebeam = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", ebeam->calibrated);
+}
+
+static ssize_t ebeam_calibrated_set(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf,
+				    size_t count)
+{
+	struct ebeam_device *ebeam = dev_get_drvdata(dev);
+	int err, c;
+
+	err = kstrtoint(buf, 10, &c);
+	if (err)
+		return err;
+
+	if (c == 1) {
+		memcpy(&ebeam->cursetting, &ebeam->newsetting,
+		       sizeof(struct ebeam_settings));
+		ebeam->calibrated = true;
+		ebeam_setup_input(ebeam, ebeam->input);
+	} else {
+		memcpy(&ebeam->newsetting, &ebeam->cursetting,
+		       sizeof(struct ebeam_settings));
+		ebeam->calibrated = false;
+		ebeam_setup_input(ebeam, ebeam->input);
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(calibrated, S_IRUGO | S_IWUGO,
+		   ebeam_calibrated_get, ebeam_calibrated_set);
+
+static struct attribute *ebeam_attrs[] = {
+	&dev_attr_min_x.attr,
+	&dev_attr_min_y.attr,
+	&dev_attr_max_x.attr,
+	&dev_attr_max_y.attr,
+	&dev_attr_h1.attr,
+	&dev_attr_h2.attr,
+	&dev_attr_h3.attr,
+	&dev_attr_h4.attr,
+	&dev_attr_h5.attr,
+	&dev_attr_h6.attr,
+	&dev_attr_h7.attr,
+	&dev_attr_h8.attr,
+	&dev_attr_h9.attr,
+	&dev_attr_calibrated.attr,
+	NULL
+};
+
+static const struct attribute_group ebeam_attr_group = {
+	.attrs = ebeam_attrs,
+};
+
+/* IRQ */
+static int ebeam_read_data(struct ebeam_device *ebeam, unsigned char *pkt)
+{
+
+/*
+ * Packet description : 8 bytes
+ *
+ *  nop packet : FF FF FF FF FF FF FF FF
+ *
+ *  pkt[0] : Sensors
+ *	bit 1 : ultrasound signal (guessed)
+ *	bit 2 : IR signal (tested with a remote...) ;
+ *	readings OK : 0x03 (anything else is a show-stopper)
+ *
+ *  pkt[1] : raw_x low
+ *  pkt[2] : raw_x high
+ *
+ *  pkt[3] : raw_y low
+ *  pkt[4] : raw_y high
+ *
+ *  pkt[5] : fiability ?
+ *	often 0xC0
+ *	> 0x80 : OK
+ *
+ *  pkt[6] :
+ *	buttons state (low 4 bits)
+ *		0x1 = no buttons
+ *		bit 0 : tip (WARNING inversed : 0=pressed)
+ *		bit 1 : ? (always 0 during tests)
+ *		bit 2 : little (1=pressed)
+ *		bit 3 : big (1=pressed)
+ *
+ *	pointer ID : (high 4 bits)
+ *		Tested  : 0x6=wand ;
+ *		Guessed : 0x1=red ; 0x2=blue ; 0x3=green ; 0x4=black ;
+ *			  0x5=eraser
+ *		bit 4 : pointer ID
+ *		bit 5 : pointer ID
+ *		bit 6 : pointer ID
+ *		bit 7 : pointer ID
+ *
+ *
+ *  pkt[7] : fiability ?
+ *	often 0xFF
+ *
+ */
+
+	/* Filtering bad/nop packet */
+	if (pkt[0] != 0x03)
+		return 0;
+
+	ebeam->raw_x = (pkt[2] << 8) | pkt[1];
+	ebeam->raw_y = (pkt[4] << 8) | pkt[3];
+
+	ebeam->btn_map = (!(pkt[6] & 0x1))	|
+			 ((pkt[6] & 0x4) >> 1)	|
+			 ((pkt[6] & 0x8) >> 1);
+
+	return 1;
+}
+
+/*
+ * IRQ
+ * compute screen coordinates from raw
+ * Overflow and negative values are ignored
+ */
+static bool ebeam_calculate_xy(struct ebeam_device *ebeam)
+{
+	/* 64-bits divisions handled by div64_s64 */
+
+	s64 scale;
+
+	if (!ebeam->calibrated) {
+		ebeam->x = ebeam->raw_x;
+		ebeam->y = ebeam->raw_y;
+	} else {
+		scale = ebeam->cursetting.h7 * ebeam->raw_x +
+			ebeam->cursetting.h8 * ebeam->raw_y +
+			ebeam->cursetting.h9;
+
+		/* Who want a division by zero in kernel ? */
+		if (scale == 0) {
+			dev_err(&(ebeam->interface)->dev,
+				"%s - Division by zero, wrong calibration.\n",
+				__func__);
+			dev_err(&(ebeam->interface)->dev,
+				"%s - Resetting to un-calibrated mode.\n",
+				__func__);
+			ebeam->calibrated = false;
+			return 0;
+		}
+
+		/*
+		 * We *must* round the result, but can't do (int) (v1/v2 + 0.5)
+		 *
+		 * (int) (v1/v2 + 0.5) <=> (int) ( (2*v1 + v2)/(2*v2) )
+		 */
+		ebeam->x =
+			(int) div64_s64((((ebeam->cursetting.h1 * ebeam->raw_x +
+					   ebeam->cursetting.h2 * ebeam->raw_y +
+					   ebeam->cursetting.h3) << 1) + scale),
+					   (scale << 1));
+		ebeam->y =
+			(int) div64_s64((((ebeam->cursetting.h4 * ebeam->raw_x +
+					   ebeam->cursetting.h5 * ebeam->raw_y +
+					   ebeam->cursetting.h6) << 1) + scale),
+					   (scale << 1));
+
+		/* check range */
+		if ((ebeam->x < ebeam->cursetting.min_x) ||
+		    (ebeam->x > ebeam->cursetting.max_x) ||
+		    (ebeam->y < ebeam->cursetting.min_y) ||
+		    (ebeam->y > ebeam->cursetting.max_y))
+			return 0;
+	}
+
+	return 1;
+}
+
+/* IRQ */
+static void ebeam_report_input(struct ebeam_device *ebeam)
+{
+	input_report_key(ebeam->input, BTN_LEFT,
+			 (ebeam->btn_map & EBEAM_BTN_TIP));
+	input_report_key(ebeam->input, BTN_MIDDLE,
+			 (ebeam->btn_map & EBEAM_BTN_LIT));
+	input_report_key(ebeam->input, BTN_RIGHT,
+			 (ebeam->btn_map & EBEAM_BTN_BIG));
+
+	input_report_abs(ebeam->input, ABS_X, ebeam->x);
+	input_report_abs(ebeam->input, ABS_Y, ebeam->y);
+
+	input_sync(ebeam->input);
+}
+
+/* IRQ */
+static void ebeam_process_pkt(struct ebeam_device *ebeam,
+			      unsigned char *pkt, int len)
+{
+	if (!ebeam_read_data(ebeam, pkt))
+		return;
+
+	if (!ebeam_calculate_xy(ebeam))
+		return;
+
+	ebeam_report_input(ebeam);
+}
+
+/* IRQ
+ * handler */
+static void ebeam_irq(struct urb *urb)
+{
+	struct ebeam_device *ebeam = urb->context;
+	struct device *dev = &ebeam->interface->dev;
+	int retval;
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ETIME:
+		/* this urb is timing out */
+		dev_dbg(dev,
+			"%s - urb timed out - was the device unplugged?\n",
+			__func__);
+		return;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+	case -EPIPE:
+		/* this urb is terminated, clean up */
+		dev_dbg(dev, "%s - urb shutting down with status: %d\n",
+			__func__, urb->status);
+		return;
+	default:
+		dev_dbg(dev, "%s - nonzero urb status received: %d\n",
+			__func__, urb->status);
+		goto exit;
+	}
+
+	ebeam_process_pkt(ebeam, ebeam->data, urb->actual_length);
+
+exit:
+	usb_mark_last_busy(interface_to_usbdev(ebeam->interface));
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval)
+		dev_err(dev, "%s - usb_submit_urb failed with result: %d\n",
+			__func__, retval);
+}
+
+static int ebeam_open(struct input_dev *input)
+{
+	struct ebeam_device *ebeam = input_get_drvdata(input);
+	int r;
+
+	ebeam->irq->dev = interface_to_usbdev(ebeam->interface);
+
+	r = usb_autopm_get_interface(ebeam->interface) ? -EIO : 0;
+	if (r < 0)
+		goto out;
+
+	if (usb_submit_urb(ebeam->irq, GFP_KERNEL)) {
+		r = -EIO;
+		goto out_put;
+	}
+
+	ebeam->interface->needs_remote_wakeup = 1;
+out_put:
+	usb_autopm_put_interface(ebeam->interface);
+out:
+	return r;
+}
+
+static void ebeam_close(struct input_dev *input)
+{
+	struct ebeam_device *ebeam = input_get_drvdata(input);
+	int r;
+
+	usb_kill_urb(ebeam->irq);
+
+	r = usb_autopm_get_interface(ebeam->interface);
+	ebeam->interface->needs_remote_wakeup = 0;
+
+	if (!r)
+		usb_autopm_put_interface(ebeam->interface);
+}
+
+static int ebeam_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct ebeam_device *ebeam = usb_get_intfdata(intf);
+
+	usb_kill_urb(ebeam->irq);
+
+	return 0;
+}
+
+static int ebeam_resume(struct usb_interface *intf)
+{
+	struct ebeam_device *ebeam = usb_get_intfdata(intf);
+	struct input_dev *input = ebeam->input;
+	int result = 0;
+
+	mutex_lock(&input->mutex);
+	if (input->users)
+		result = usb_submit_urb(ebeam->irq, GFP_NOIO);
+	mutex_unlock(&input->mutex);
+
+	return result;
+}
+
+static int ebeam_reset_resume(struct usb_interface *intf)
+{
+	return ebeam_resume(intf);
+}
+
+static void ebeam_free_buffers(struct usb_device *udev,
+			       struct ebeam_device *ebeam)
+{
+	usb_free_coherent(udev, REPT_SIZE,
+			  ebeam->data, ebeam->data_dma);
+	kfree(ebeam->buffer);
+}
+
+static struct usb_endpoint_descriptor *
+ebeam_get_input_endpoint(struct usb_host_interface *interface)
+{
+	int i;
+
+	for (i = 0; i < interface->desc.bNumEndpoints; i++)
+		if (usb_endpoint_dir_in(&interface->endpoint[i].desc))
+			return &interface->endpoint[i].desc;
+
+	return NULL;
+}
+
+static int ebeam_probe(struct usb_interface *intf,
+		       const struct usb_device_id *id)
+{
+	struct ebeam_device *ebeam;
+	struct input_dev *input_dev;
+	struct usb_endpoint_descriptor *endpoint;
+	struct usb_device *udev = interface_to_usbdev(intf);
+	int err = -ENOMEM;
+
+	endpoint = ebeam_get_input_endpoint(intf->cur_altsetting);
+	if (!endpoint)
+		return -ENXIO;
+
+	ebeam = kzalloc(sizeof(struct ebeam_device), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!ebeam || !input_dev)
+		goto out_free;
+
+	ebeam_init_settings(ebeam);
+
+	ebeam->data = usb_alloc_coherent(udev, REPT_SIZE,
+					 GFP_KERNEL, &ebeam->data_dma);
+	if (!ebeam->data)
+		goto out_free;
+
+	ebeam->irq = usb_alloc_urb(0, GFP_KERNEL);
+	if (!ebeam->irq) {
+		dev_dbg(&intf->dev,
+			"%s - usb_alloc_urb failed: ebeam->irq\n", __func__);
+		goto out_free_buffers;
+	}
+
+	ebeam->interface = intf;
+	ebeam->input = input_dev;
+
+	/* setup name */
+	snprintf(ebeam->name, sizeof(ebeam->name),
+		 "USB eBeam %04x:%04x",
+		 le16_to_cpu(udev->descriptor.idVendor),
+		 le16_to_cpu(udev->descriptor.idProduct));
+
+	if (udev->manufacturer || udev->product) {
+		strlcat(ebeam->name,
+			" (",
+			sizeof(ebeam->name));
+
+		if (udev->manufacturer)
+			strlcat(ebeam->name,
+				udev->manufacturer,
+				sizeof(ebeam->name));
+
+		if (udev->product) {
+			if (udev->manufacturer)
+				strlcat(ebeam->name,
+					" ",
+					sizeof(ebeam->name));
+			strlcat(ebeam->name,
+				udev->product,
+				sizeof(ebeam->name));
+		}
+
+		if (strlcat(ebeam->name, ")", sizeof(ebeam->name))
+			>= sizeof(ebeam->name)) {
+			/* overflowed, closing ) anyway */
+			ebeam->name[sizeof(ebeam->name)-2] = ')';
+		}
+	}
+
+	/* usb tree */
+	usb_make_path(udev, ebeam->phys, sizeof(ebeam->phys));
+	strlcat(ebeam->phys, "/input0", sizeof(ebeam->phys));
+
+	/* input setup */
+	input_dev->name = ebeam->name;
+	input_dev->phys = ebeam->phys;
+	usb_to_input_id(udev, &input_dev->id);
+	input_dev->dev.parent = &intf->dev;
+
+	input_set_drvdata(input_dev, ebeam);
+
+	input_dev->open = ebeam_open;
+	input_dev->close = ebeam_close;
+
+	/* usb urb setup */
+	if (usb_endpoint_type(endpoint) == USB_ENDPOINT_XFER_INT)
+		usb_fill_int_urb(ebeam->irq, udev,
+			usb_rcvintpipe(udev, endpoint->bEndpointAddress),
+			ebeam->data, REPT_SIZE,
+			ebeam_irq, ebeam, endpoint->bInterval);
+	else
+		usb_fill_bulk_urb(ebeam->irq, udev,
+			usb_rcvbulkpipe(udev, endpoint->bEndpointAddress),
+			ebeam->data, REPT_SIZE,
+			ebeam_irq, ebeam);
+
+	ebeam->irq->dev = udev;
+	ebeam->irq->transfer_dma = ebeam->data_dma;
+	ebeam->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	/* input final setup */
+	ebeam_setup_input(ebeam, input_dev);
+
+	err = input_register_device(ebeam->input);
+	if (err) {
+		dev_dbg(&intf->dev,
+			"%s - input_register_device failed, err: %d\n",
+			__func__, err);
+		goto out_free_urb;
+	}
+
+	/* usb final setup */
+	usb_set_intfdata(intf, ebeam);
+
+	/* sysfs setup */
+	err = sysfs_create_group(&intf->dev.kobj, &ebeam_attr_group);
+	if (err) {
+		dev_dbg(&intf->dev,
+			"%s - cannot create sysfs group, err: %d\n",
+			__func__, err);
+		goto out_unregister_input;
+	}
+
+	return 0;
+
+out_unregister_input:
+	input_unregister_device(input_dev);
+	input_dev = NULL;
+out_free_urb:
+	usb_free_urb(ebeam->irq);
+out_free_buffers:
+	ebeam_free_buffers(udev, ebeam);
+out_free:
+	input_free_device(input_dev);
+	kfree(ebeam);
+	return err;
+}
+
+static void ebeam_disconnect(struct usb_interface *intf)
+{
+	struct ebeam_device *ebeam = usb_get_intfdata(intf);
+
+	if (!ebeam)
+		return;
+
+	dev_dbg(&intf->dev,
+		"%s - ebeam is initialized, cleaning up\n", __func__);
+
+	usb_set_intfdata(intf, NULL);
+	/* this will stop IO via close */
+	input_unregister_device(ebeam->input);
+	sysfs_remove_group(&intf->dev.kobj, &ebeam_attr_group);
+	usb_free_urb(ebeam->irq);
+	ebeam_free_buffers(interface_to_usbdev(intf), ebeam);
+	kfree(ebeam);
+}
+
+MODULE_DEVICE_TABLE(usb, ebeam_devices);
+
+static struct usb_driver ebeam_driver = {
+	.name			= "ebeam",
+	.probe			= ebeam_probe,
+	.disconnect		= ebeam_disconnect,
+	.suspend		= ebeam_suspend,
+	.resume			= ebeam_resume,
+	.reset_resume		= ebeam_reset_resume,
+	.id_table		= ebeam_devices,
+	.supports_autosuspend	= 1,
+};
+
+module_usb_driver(ebeam_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
-- 
1.8.1.5


^ permalink raw reply related

* [PATCH v2] input: don't call input_dev_release_keys() in resume
From: Oskar Andero @ 2013-07-25 12:09 UTC (permalink / raw)
  To: linux-kernel, linux-input
  Cc: Bjorn Andersson, Radovan Lekanovic, Aleksej Makarov,
	Dmitry Torokhov, Oskar Andero

From: Aleksej Makarov <aleksej.makarov@sonymobile.com>

When waking up the platform by pressing a specific key, sending a
release on that key makes it impossible to react on the event in
user-space. This is fixed by moving the input_reset_device() call to
resume instead.

Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Reviewed-by: Radovan Lekanovic <radovan.lekanovic@sonymobile.com>
Signed-off-by: Aleksej Makarov <aleksej.makarov@sonymobile.com>
Signed-off-by: Oskar Andero <oskar.andero@sonymobile.com>
---
 drivers/input/input.c | 11 +----------
 1 file changed, 1 insertion(+), 10 deletions(-)

diff --git a/drivers/input/input.c b/drivers/input/input.c
index c044699..ee3ff16 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -1676,22 +1676,13 @@ static int input_dev_suspend(struct device *dev)
 {
 	struct input_dev *input_dev = to_input_dev(dev);
 
-	mutex_lock(&input_dev->mutex);
-
-	if (input_dev->users)
-		input_dev_toggle(input_dev, false);
-
-	mutex_unlock(&input_dev->mutex);
+	input_reset_device(input_dev);
 
 	return 0;
 }
 
 static int input_dev_resume(struct device *dev)
 {
-	struct input_dev *input_dev = to_input_dev(dev);
-
-	input_reset_device(input_dev);
-
 	return 0;
 }
 
-- 
1.8.1.5


^ permalink raw reply related

* [PATCH] input/serio: disable i8042 PC keyboard ctrl for ARC
From: Mischa Jonker @ 2013-07-25 12:13 UTC (permalink / raw)
  To: Heiko Carstens, Greg Kroah-Hartman, Martin Schwidefsky,
	linux-input, linux-kernel, Vineet.Gupta1
  Cc: Mischa Jonker

It causes crashes when enabled, and we don't have such a peripheral
anyway on ARC platforms.

Signed-off-by: Mischa Jonker <mjonker@synopsys.com>
---
 drivers/input/serio/Kconfig |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index 94c17c2..1e691a3 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -22,7 +22,8 @@ config SERIO_I8042
 	tristate "i8042 PC Keyboard controller" if EXPERT || !X86
 	default y
 	depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && \
-		   (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN && !S390
+		   (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN && !S390 && \
+		   !ARC
 	help
 	  i8042 is the chip over which the standard AT keyboard and PS/2
 	  mouse are connected to the computer. If you use these devices,
-- 
1.7.9.5


^ permalink raw reply related

* Re: [PATCH 35/50] media: usb: cx231xx: spin_lock in complete() cleanup
From: Hans Verkuil @ 2013-07-26 14:28 UTC (permalink / raw)
  To: Ming Lei
  Cc: Greg Kroah-Hartman, linux-usb, Oliver Neukum, Alan Stern,
	linux-input, linux-bluetooth, netdev, linux-wireless, linux-media,
	alsa-devel, Mauro Carvalho Chehab, Hans Verkuil
In-Reply-To: <1373533573-12272-36-git-send-email-ming.lei@canonical.com>



On 07/11/2013 11:05 AM, Ming Lei wrote:
> Complete() will be run with interrupt enabled, so change to
> spin_lock_irqsave().
> 
> Cc: Mauro Carvalho Chehab <mchehab@redhat.com>
> Cc: Hans Verkuil <hans.verkuil@cisco.com>
> Cc: linux-media@vger.kernel.org
> Signed-off-by: Ming Lei <ming.lei@canonical.com>
> ---
>  drivers/media/usb/cx231xx/cx231xx-audio.c |    6 ++++++
>  drivers/media/usb/cx231xx/cx231xx-core.c  |   10 ++++++----
>  drivers/media/usb/cx231xx/cx231xx-vbi.c   |    5 +++--
>  3 files changed, 15 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/media/usb/cx231xx/cx231xx-audio.c b/drivers/media/usb/cx231xx/cx231xx-audio.c
> index 81a1d97..58c1b5c 100644
> --- a/drivers/media/usb/cx231xx/cx231xx-audio.c
> +++ b/drivers/media/usb/cx231xx/cx231xx-audio.c
> @@ -136,6 +136,7 @@ static void cx231xx_audio_isocirq(struct urb *urb)
>  		stride = runtime->frame_bits >> 3;
>  
>  		for (i = 0; i < urb->number_of_packets; i++) {
> +			unsigned long flags;
>  			int length = urb->iso_frame_desc[i].actual_length /
>  				     stride;
>  			cp = (unsigned char *)urb->transfer_buffer +
> @@ -158,6 +159,7 @@ static void cx231xx_audio_isocirq(struct urb *urb)
>  				       length * stride);
>  			}
>  
> +			local_irq_save(flags);
>  			snd_pcm_stream_lock(substream);

Can't you use snd_pcm_stream_lock_irqsave here?

Ditto for the other media drivers where this happens: em28xx and tlg2300.

I've reviewed the media driver changes and they look OK to me, so if
my comment above is fixed, then I can merge them for 3.12. Or are these
changes required for 3.11?

Regards,

	Hans

>  
>  			dev->adev.hwptr_done_capture += length;
> @@ -174,6 +176,7 @@ static void cx231xx_audio_isocirq(struct urb *urb)
>  				period_elapsed = 1;
>  			}
>  			snd_pcm_stream_unlock(substream);
> +			local_irq_restore(flags);
>  		}
>  		if (period_elapsed)
>  			snd_pcm_period_elapsed(substream);
> @@ -224,6 +227,7 @@ static void cx231xx_audio_bulkirq(struct urb *urb)
>  		stride = runtime->frame_bits >> 3;
>  
>  		if (1) {
> +			unsigned long flags;
>  			int length = urb->actual_length /
>  				     stride;
>  			cp = (unsigned char *)urb->transfer_buffer;
> @@ -242,6 +246,7 @@ static void cx231xx_audio_bulkirq(struct urb *urb)
>  				       length * stride);
>  			}
>  
> +			local_irq_save(flags);
>  			snd_pcm_stream_lock(substream);
>  
>  			dev->adev.hwptr_done_capture += length;
> @@ -258,6 +263,7 @@ static void cx231xx_audio_bulkirq(struct urb *urb)
>  				period_elapsed = 1;
>  			}
>  			snd_pcm_stream_unlock(substream);
> +			local_irq_restore(flags);
>  		}
>  		if (period_elapsed)
>  			snd_pcm_period_elapsed(substream);
> diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c
> index 4ba3ce0..593b397 100644
> --- a/drivers/media/usb/cx231xx/cx231xx-core.c
> +++ b/drivers/media/usb/cx231xx/cx231xx-core.c
> @@ -798,6 +798,7 @@ static void cx231xx_isoc_irq_callback(struct urb *urb)
>  	    container_of(dma_q, struct cx231xx_video_mode, vidq);
>  	struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode);
>  	int i;
> +	unsigned long flags;
>  
>  	switch (urb->status) {
>  	case 0:		/* success */
> @@ -813,9 +814,9 @@ static void cx231xx_isoc_irq_callback(struct urb *urb)
>  	}
>  
>  	/* Copy data from URB */
> -	spin_lock(&dev->video_mode.slock);
> +	spin_lock_irqsave(&dev->video_mode.slock, flags);
>  	dev->video_mode.isoc_ctl.isoc_copy(dev, urb);
> -	spin_unlock(&dev->video_mode.slock);
> +	spin_unlock_irqrestore(&dev->video_mode.slock, flags);
>  
>  	/* Reset urb buffers */
>  	for (i = 0; i < urb->number_of_packets; i++) {
> @@ -842,6 +843,7 @@ static void cx231xx_bulk_irq_callback(struct urb *urb)
>  	struct cx231xx_video_mode *vmode =
>  	    container_of(dma_q, struct cx231xx_video_mode, vidq);
>  	struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode);
> +	unsigned long flags;
>  
>  	switch (urb->status) {
>  	case 0:		/* success */
> @@ -857,9 +859,9 @@ static void cx231xx_bulk_irq_callback(struct urb *urb)
>  	}
>  
>  	/* Copy data from URB */
> -	spin_lock(&dev->video_mode.slock);
> +	spin_lock_irqsave(&dev->video_mode.slock, flags);
>  	dev->video_mode.bulk_ctl.bulk_copy(dev, urb);
> -	spin_unlock(&dev->video_mode.slock);
> +	spin_unlock_irqrestore(&dev->video_mode.slock, flags);
>  
>  	/* Reset urb buffers */
>  	urb->status = usb_submit_urb(urb, GFP_ATOMIC);
> diff --git a/drivers/media/usb/cx231xx/cx231xx-vbi.c b/drivers/media/usb/cx231xx/cx231xx-vbi.c
> index c027942..38e78f8 100644
> --- a/drivers/media/usb/cx231xx/cx231xx-vbi.c
> +++ b/drivers/media/usb/cx231xx/cx231xx-vbi.c
> @@ -306,6 +306,7 @@ static void cx231xx_irq_vbi_callback(struct urb *urb)
>  	struct cx231xx_video_mode *vmode =
>  	    container_of(dma_q, struct cx231xx_video_mode, vidq);
>  	struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode);
> +	unsigned long flags;
>  
>  	switch (urb->status) {
>  	case 0:		/* success */
> @@ -322,9 +323,9 @@ static void cx231xx_irq_vbi_callback(struct urb *urb)
>  	}
>  
>  	/* Copy data from URB */
> -	spin_lock(&dev->vbi_mode.slock);
> +	spin_lock_irqsave(&dev->vbi_mode.slock, flags);
>  	dev->vbi_mode.bulk_ctl.bulk_copy(dev, urb);
> -	spin_unlock(&dev->vbi_mode.slock);
> +	spin_unlock_irqrestore(&dev->vbi_mode.slock, flags);
>  
>  	/* Reset status */
>  	urb->status = 0;
> 

^ permalink raw reply

* [BUG?] EV_LED events not seperated from other events by SYN_REPORT
From: Rolf Morel @ 2013-07-26 14:45 UTC (permalink / raw)
  To: dmitry.torokhov; +Cc: linux-input

Hello,

I am having some trouble with how device LED switches are being
presented in the, userspace API, event stream.

If a program sets the LED using the evdev interface, write EV_LED
input_event to device, other programs connected to the device will
have the event in their stream, as expected. The problem is the
received EV_LED events are not separated in time, as in moment in
time. It does not generate a separate SYN_REPORT to make the EV_LED
event a separate event in time.

I have only two devices which have this functionality, set LED state
through evdev, to test and they both are 3Dconnexion devices. They
both exhibit the same behavior.

So my questions are:
Is this considered a bug (/should it be filed as a bug)?
Does the fault lie in the input-subsystem or a more specific driver?

Here is some evtest data which has only button BTN_0 presses and a
separate program setting the LED state by writing an input_event
to the device.

$ evtest /dev/input/event7
Input driver version is 1.0.1
Input device ID: bus 0x3 vendor 0x46d product 0xc626 version 0x110
Input device name: "3Dconnexion SpaceNavigator"
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 256 (BTN_0)
...
  Event type 4 (EV_MSC)
    Event code 4 (MSC_SCAN)
  Event type 17 (EV_LED)
    Event code 8 (LED_MISC)
Properties:
Testing ... (interrupt to exit)
Event: time 1374507104.352812, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90001
Event: time 1374507104.352812, type 1 (EV_KEY), code 256 (BTN_0), value 1
Event: time 1374507104.352812, -------------- SYN_REPORT ------------
Event: time 1374507104.504809, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90001
Event: time 1374507104.504809, type 1 (EV_KEY), code 256 (BTN_0), value 0
Event: time 1374507104.504809, -------------- SYN_REPORT ------------
...
Switch terminal and run program which sets LED using evdev and
wait 5 seconds before pressing the button.
...
Event: time 1374507117.496465, type 17 (EV_LED), code 8 (LED_MISC), value 1
Event: time 1374507117.496465, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90001
Event: time 1374507117.496465, type 1 (EV_KEY), code 256 (BTN_0), value 1
Event: time 1374507117.496465, -------------- SYN_REPORT ------------
Event: time 1374507117.616560, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90001
Event: time 1374507117.616560, type 1 (EV_KEY), code 256 (BTN_0), value 0
Event: time 1374507117.616560, -------------- SYN_REPORT ------------

The EV_LED events even accumelate.
Event: time 1374507610.679128, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90001
Event: time 1374507610.679128, type 1 (EV_KEY), code 256 (BTN_0), value 1
Event: time 1374507610.679128, -------------- SYN_REPORT ------------
Event: time 1374507610.791126, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90001
Event: time 1374507610.791126, type 1 (EV_KEY), code 256 (BTN_0), value 0
Event: time 1374507610.791126, -------------- SYN_REPORT ------------
Event: time 1374507617.247107, type 17 (EV_LED), code 8 (LED_MISC), value 0
Event: time 1374507617.247107, type 17 (EV_LED), code 8 (LED_MISC), value 1
Event: time 1374507617.247107, type 17 (EV_LED), code 8 (LED_MISC), value 0
Event: time 1374507617.247107, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90001
Event: time 1374507617.247107, type 1 (EV_KEY), code 256 (BTN_0), value 1
Event: time 1374507617.247107, -------------- SYN_REPORT ------------
Event: time 1374507617.423105, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90001
Event: time 1374507617.423105, type 1 (EV_KEY), code 256 (BTN_0), value 0
Event: time 1374507617.423105, -------------- SYN_REPORT ------------

Event used to 'write' to device ('int state' is either 0 or 1):
struct input_event input_event;
input_event.type = EV_LED;
input_event.code = LED_MISC;
input_event.value = state;

Relevant part of 'cat /proc/bus/input/devices':
I: Bus=0003 Vendor=046d Product=c626 Version=0110
N: Name="3Dconnexion SpaceNavigator"
P: Phys=usb-0000:00:13.1-1/input0
S: Sysfs=/devices/pci0000:00/0000:00:13.1/usb6/6-1/6-1:1.0/input/input53
U: Uniq=
H: Handlers=event7 js0
B: PROP=0
B: EV=2001b
B: KEY=3 0 0 0 0
B: ABS=3f
B: MSC=10
B: LED=100

Relevant part of 'dmesg':
[124497.173908] usb 6-1: new low-speed USB device number 5 using ohci_hcd
[124497.456966] logitech 0003:046D:C626.0007: fixing up rel/abs in
Logitech report descriptor
[124497.521043] input: 3Dconnexion SpaceNavigator as
/devices/pci0000:00/0000:00:13.1/usb6/6-1/6-1:1.0/input/input54
[124497.521208] logitech 0003:046D:C626.0007: input,hidraw3: USB HID
v1.10 Multi-Axis Controller [3Dconnexion SpaceNavigator] on
usb-0000:00:13.1-1/input0

Tested on two home desktop machines, ArchLinux and XUbuntu.
$ cat /proc/version
Linux version 3.10.2-1-ARCH (tobias@T-POWA-LX) (gcc version 4.8.1
(GCC) ) #1 SMP PREEMPT Mon Jul 22 08:47:24 CEST 2013
$ cat /proc/version
Linux version 3.8.0-26-generic (buildd@panlong) (gcc version 4.7.3
(Ubuntu/Linaro 4.7.3-1ubuntu1) ) #38-Ubuntu SMP Mon Jun 17 21:43:33
UTC 2013

P.S. This is my first time posting to a kernel mailing list,
I hope that this message is adequate and in the right place.

^ permalink raw reply

* [PATCH 0/3] Input: omap-keypad: Wakeup capability and w/a for i689 errata.
From: Illia Smyrnov @ 2013-07-26 16:29 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, linux-kernel, linux-omap

This patchset adds wake up capability for the keypad and
workaround for i689 errata.

Based on top of v3.11-rc2

Depends on:

Input: omap-keypad: Convert to threaded IRQ
https://patchwork.kernel.org/patch/2832920/

Input: omap-keypad: Cleanup - use bitfiled instead of hardcoded values
https://patchwork.kernel.org/patch/2832913/

Axel Haslam (2):
  Input: omap-keypad: errata i689: Correct debounce time
  Input: omap-keypad: Set irq to level instead of edge

Illia Smyrnov (1):
  Input: omap-keypad: Enable wakeup capability for keypad.

 drivers/input/keyboard/omap4-keypad.c |   50 +++++++++++++++++++++++++++++++--
 1 file changed, 47 insertions(+), 3 deletions(-)

-- 
1.7.9.5

^ permalink raw reply

* [PATCH 1/3] Input: omap-keypad: Enable wakeup capability for keypad.
From: Illia Smyrnov @ 2013-07-26 16:29 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, linux-kernel, linux-omap
In-Reply-To: <1374856201-11507-1-git-send-email-illia.smyrnov@ti.com>

Enable/disable IRQ wake in suspend/resume handlers
to make the keypad wakeup capable.

Signed-off-by: Illia Smyrnov <illia.smyrnov@ti.com>
---
 drivers/input/keyboard/omap4-keypad.c |   38 +++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c
index 0244262..5f0a2c7 100644
--- a/drivers/input/keyboard/omap4-keypad.c
+++ b/drivers/input/keyboard/omap4-keypad.c
@@ -74,6 +74,7 @@ struct omap4_keypad {
 	struct input_dev *input;
 
 	void __iomem *base;
+	bool irq_wake_enabled;
 	unsigned int irq;
 
 	unsigned int rows;
@@ -380,6 +381,7 @@ static int omap4_keypad_probe(struct platform_device *pdev)
 		goto err_free_input;
 	}
 
+	device_init_wakeup(&pdev->dev, true);
 	pm_runtime_put_sync(&pdev->dev);
 
 	error = input_register_device(keypad_data->input);
@@ -393,6 +395,7 @@ static int omap4_keypad_probe(struct platform_device *pdev)
 
 err_pm_disable:
 	pm_runtime_disable(&pdev->dev);
+	device_init_wakeup(&pdev->dev, false);
 	free_irq(keypad_data->irq, keypad_data);
 err_free_keymap:
 	kfree(keypad_data->keymap);
@@ -418,6 +421,8 @@ static int omap4_keypad_remove(struct platform_device *pdev)
 
 	pm_runtime_disable(&pdev->dev);
 
+	device_init_wakeup(&pdev->dev, false);
+
 	input_unregister_device(keypad_data->input);
 
 	iounmap(keypad_data->base);
@@ -439,12 +444,45 @@ static const struct of_device_id omap_keypad_dt_match[] = {
 MODULE_DEVICE_TABLE(of, omap_keypad_dt_match);
 #endif
 
+#ifdef CONFIG_PM_SLEEP
+static int omap4_keypad_suspend(struct device *dev)
+{
+	int error;
+
+	if (device_may_wakeup(&pdev->dev)) {
+		error = enable_irq_wake(keypad_data->irq);
+		if (!error)
+			keypad_data->irq_wake_enabled = true;
+	}
+
+	return 0;
+}
+
+static int omap4_keypad_resume(struct device *dev)
+{
+	if (device_may_wakeup(&pdev->dev) && keypad_data->irq_wake_enabled) {
+		disable_irq_wake(keypad_data->irq);
+		keypad_data->irq_wake_enabled = false;
+	}
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(omap4_keypad_pm_ops, omap4_keypad_suspend,
+				omap4_keypad_resume);
+
+#define OMAP4_KEYPAD_PM_OPS (&omap4_keypad_pm_ops)
+#else
+#define OMAP4_KEYPAD_PM_OPS NULL
+#endif
+
 static struct platform_driver omap4_keypad_driver = {
 	.probe		= omap4_keypad_probe,
 	.remove		= omap4_keypad_remove,
 	.driver		= {
 		.name	= "omap4-keypad",
 		.owner	= THIS_MODULE,
+		.pm	= OMAP4_KEYPAD_PM_OPS,
 		.of_match_table = of_match_ptr(omap_keypad_dt_match),
 	},
 };
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 2/3] Input: omap-keypad: errata i689: Correct debounce time
From: Illia Smyrnov @ 2013-07-26 16:30 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, linux-kernel, linux-omap
In-Reply-To: <1374856201-11507-1-git-send-email-illia.smyrnov@ti.com>

From: Axel Haslam <axelhaslam@ti.com>

Set debounce time value to 12ms to workaround the errata.

ERRATA DESCRIPTION:
When a key is released for a time shorter than the debounce time,
in-between 2 key press (KP1 and KP2), the keyboard state machine will go to
idle mode and will never detect the key release (after KP1,and also after KP2),
and thus will never generate a new IRQ indicating the key release.
>From the operating system standpoint, only a key press as been detected,
and the key will keep on being printed on the screen until another or
the same key is pressed again.When the failing state has been reached,
the KBD_STATEMACHINE register will show "idle state" and the KBD_FULLCODE
register won't be empty, this is the signature of the bug.There is no impact
on the power consumption of the system as the state machine goes to IDLE state.

WORKAROUND:
First thing is to program the debounce time correctly:
If X (us) is the maximum time of bounces when a key is pressed or released,
and Y (us) is the minimum time of a key release that is expected to be detected,
then the debounce time should be set to a value inbetween X and Y.

In case it is still possible to get shorter than debounce time key-release
events, then the only solution is to implement a software workaround:

Before printing a second character on the screen, the software must check if
the keyboard has hit the failing condition (cf signature of the bug above)
or if the key is still really pressed and then take the appropriate actions.

Silicon revisions impacted: OMAP4430 ES 1.0,2.0,2.1,2.2,2.3
                            OMAP4460 ES 1.0,1.1
                            OMAP5430 ES 1.0

Signed-off-by: Axel Haslam <axelhaslam@ti.com>
Signed-off-by: Illia Smyrnov <illia.smyrnov@ti.com>
---
 Based on top of v3.11-rc2

 Depends on:
 Input: omap-keypad: Cleanup - use bitfiled instead of hardcoded values
 https://patchwork.kernel.org/patch/2832913/

 drivers/input/keyboard/omap4-keypad.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c
index 5f0a2c7..b13589e 100644
--- a/drivers/input/keyboard/omap4-keypad.c
+++ b/drivers/input/keyboard/omap4-keypad.c
@@ -62,8 +62,10 @@
 
 /* OMAP4 values */
 #define OMAP4_VAL_IRQDISABLE		0x0
-#define OMAP4_VAL_DEBOUNCINGTIME	0x7
-#define OMAP4_VAL_PVT			0x7
+
+/* Errata i689: set debounce time value to 12ms */
+#define OMAP4_VAL_DEBOUNCINGTIME	0x2
+#define OMAP4_VAL_PVT			0x6
 
 enum {
 	KBD_REVISION_OMAP4 = 0,
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 3/3] Input: omap-keypad: Set irq to level instead of edge
From: Illia Smyrnov @ 2013-07-26 16:30 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, linux-kernel, linux-omap
In-Reply-To: <1374856201-11507-1-git-send-email-illia.smyrnov@ti.com>

From: Axel Haslam <axelhaslam@ti.com>

Set mpu irq to level instead of edge, since if mpu is in low power
an edge detection may be lost if the event is a wkup event.

Signed-off-by: Axel Haslam <axelhaslam@ti.com>
Signed-off-by: Illia Smyrnov <illia.smyrnov@ti.com>
---
 Based on top of v3.11-rc2

 Depends on:
 Input: omap-keypad: Convert to threaded IRQ
 https://patchwork.kernel.org/patch/2832920/

 drivers/input/keyboard/omap4-keypad.c |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c
index b13589e..66a41d6 100644
--- a/drivers/input/keyboard/omap4-keypad.c
+++ b/drivers/input/keyboard/omap4-keypad.c
@@ -374,9 +374,13 @@ static int omap4_keypad_probe(struct platform_device *pdev)
 		goto err_free_keymap;
 	}
 
+	/*
+	 * Set irq level detection for mpu. Edge event are missed in gic
+	 * if the mpu is in low power and keypad event is a wakeup
+	 */
 	error = request_threaded_irq(keypad_data->irq, omap4_keypad_irq_handler,
 				     omap4_keypad_irq_thread_fn,
-				     IRQF_TRIGGER_RISING,
+				     IRQF_TRIGGER_HIGH,
 				     "omap4-keypad", keypad_data);
 	if (error) {
 		dev_err(&pdev->dev, "failed to register interrupt\n");
-- 
1.7.9.5

^ permalink raw reply related

* Re: [PATCH 1/3] Input: omap-keypad: Enable wakeup capability for keypad.
From: Illia Smyrnov @ 2013-07-26 16:58 UTC (permalink / raw)
  To: Illia Smyrnov; +Cc: Dmitry Torokhov, linux-input, linux-kernel, linux-omap
In-Reply-To: <1374856201-11507-2-git-send-email-illia.smyrnov@ti.com>

Sorry,
this patch is incomplete and was sent by mistake.
I will sent v2

On 07/26/2013 07:29 PM, Illia Smyrnov wrote:
> Enable/disable IRQ wake in suspend/resume handlers
> to make the keypad wakeup capable.
>
> Signed-off-by: Illia Smyrnov <illia.smyrnov@ti.com>
> ---
>   drivers/input/keyboard/omap4-keypad.c |   38 +++++++++++++++++++++++++++++++++
>   1 file changed, 38 insertions(+)
>
> diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c
> index 0244262..5f0a2c7 100644
> --- a/drivers/input/keyboard/omap4-keypad.c
> +++ b/drivers/input/keyboard/omap4-keypad.c
> @@ -74,6 +74,7 @@ struct omap4_keypad {
>   	struct input_dev *input;
>
>   	void __iomem *base;
> +	bool irq_wake_enabled;
>   	unsigned int irq;
>
>   	unsigned int rows;
> @@ -380,6 +381,7 @@ static int omap4_keypad_probe(struct platform_device *pdev)
>   		goto err_free_input;
>   	}
>
> +	device_init_wakeup(&pdev->dev, true);
>   	pm_runtime_put_sync(&pdev->dev);
>
>   	error = input_register_device(keypad_data->input);
> @@ -393,6 +395,7 @@ static int omap4_keypad_probe(struct platform_device *pdev)
>
>   err_pm_disable:
>   	pm_runtime_disable(&pdev->dev);
> +	device_init_wakeup(&pdev->dev, false);
>   	free_irq(keypad_data->irq, keypad_data);
>   err_free_keymap:
>   	kfree(keypad_data->keymap);
> @@ -418,6 +421,8 @@ static int omap4_keypad_remove(struct platform_device *pdev)
>
>   	pm_runtime_disable(&pdev->dev);
>
> +	device_init_wakeup(&pdev->dev, false);
> +
>   	input_unregister_device(keypad_data->input);
>
>   	iounmap(keypad_data->base);
> @@ -439,12 +444,45 @@ static const struct of_device_id omap_keypad_dt_match[] = {
>   MODULE_DEVICE_TABLE(of, omap_keypad_dt_match);
>   #endif
>
> +#ifdef CONFIG_PM_SLEEP
> +static int omap4_keypad_suspend(struct device *dev)
> +{
> +	int error;
> +
> +	if (device_may_wakeup(&pdev->dev)) {
> +		error = enable_irq_wake(keypad_data->irq);
> +		if (!error)
> +			keypad_data->irq_wake_enabled = true;
> +	}
> +
> +	return 0;
> +}
> +
> +static int omap4_keypad_resume(struct device *dev)
> +{
> +	if (device_may_wakeup(&pdev->dev) && keypad_data->irq_wake_enabled) {
> +		disable_irq_wake(keypad_data->irq);
> +		keypad_data->irq_wake_enabled = false;
> +	}
> +
> +	return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(omap4_keypad_pm_ops, omap4_keypad_suspend,
> +				omap4_keypad_resume);
> +
> +#define OMAP4_KEYPAD_PM_OPS (&omap4_keypad_pm_ops)
> +#else
> +#define OMAP4_KEYPAD_PM_OPS NULL
> +#endif
> +
>   static struct platform_driver omap4_keypad_driver = {
>   	.probe		= omap4_keypad_probe,
>   	.remove		= omap4_keypad_remove,
>   	.driver		= {
>   		.name	= "omap4-keypad",
>   		.owner	= THIS_MODULE,
> +		.pm	= OMAP4_KEYPAD_PM_OPS,
>   		.of_match_table = of_match_ptr(omap_keypad_dt_match),
>   	},
>   };
>

^ permalink raw reply

* [PATCH v2 1/3] Input: omap-keypad: Enable wakeup capability for keypad.
From: Illia Smyrnov @ 2013-07-26 17:09 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, linux-kernel, linux-omap
In-Reply-To: <1374858546-25034-1-git-send-email-illia.smyrnov@ti.com>

Enable/disable IRQ wake in suspend/resume handlers
to make the keypad wakeup capable.

Signed-off-by: Illia Smyrnov <illia.smyrnov@ti.com>
---
 drivers/input/keyboard/omap4-keypad.c |   43 +++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c
index 0244262..feab00f 100644
--- a/drivers/input/keyboard/omap4-keypad.c
+++ b/drivers/input/keyboard/omap4-keypad.c
@@ -74,6 +74,7 @@ struct omap4_keypad {
 	struct input_dev *input;
 
 	void __iomem *base;
+	bool irq_wake_enabled;
 	unsigned int irq;
 
 	unsigned int rows;
@@ -380,6 +381,7 @@ static int omap4_keypad_probe(struct platform_device *pdev)
 		goto err_free_input;
 	}
 
+	device_init_wakeup(&pdev->dev, true);
 	pm_runtime_put_sync(&pdev->dev);
 
 	error = input_register_device(keypad_data->input);
@@ -393,6 +395,7 @@ static int omap4_keypad_probe(struct platform_device *pdev)
 
 err_pm_disable:
 	pm_runtime_disable(&pdev->dev);
+	device_init_wakeup(&pdev->dev, false);
 	free_irq(keypad_data->irq, keypad_data);
 err_free_keymap:
 	kfree(keypad_data->keymap);
@@ -418,6 +421,8 @@ static int omap4_keypad_remove(struct platform_device *pdev)
 
 	pm_runtime_disable(&pdev->dev);
 
+	device_init_wakeup(&pdev->dev, false);
+
 	input_unregister_device(keypad_data->input);
 
 	iounmap(keypad_data->base);
@@ -439,12 +444,50 @@ static const struct of_device_id omap_keypad_dt_match[] = {
 MODULE_DEVICE_TABLE(of, omap_keypad_dt_match);
 #endif
 
+#ifdef CONFIG_PM_SLEEP
+static int omap4_keypad_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct omap4_keypad *keypad_data = platform_get_drvdata(pdev);
+	int error;
+
+	if (device_may_wakeup(&pdev->dev)) {
+		error = enable_irq_wake(keypad_data->irq);
+		if (!error)
+			keypad_data->irq_wake_enabled = true;
+	}
+
+	return 0;
+}
+
+static int omap4_keypad_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct omap4_keypad *keypad_data = platform_get_drvdata(pdev);
+
+	if (device_may_wakeup(&pdev->dev) && keypad_data->irq_wake_enabled) {
+		disable_irq_wake(keypad_data->irq);
+		keypad_data->irq_wake_enabled = false;
+	}
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(omap4_keypad_pm_ops, omap4_keypad_suspend,
+				omap4_keypad_resume);
+
+#define OMAP4_KEYPAD_PM_OPS (&omap4_keypad_pm_ops)
+#else
+#define OMAP4_KEYPAD_PM_OPS NULL
+#endif
+
 static struct platform_driver omap4_keypad_driver = {
 	.probe		= omap4_keypad_probe,
 	.remove		= omap4_keypad_remove,
 	.driver		= {
 		.name	= "omap4-keypad",
 		.owner	= THIS_MODULE,
+		.pm	= OMAP4_KEYPAD_PM_OPS,
 		.of_match_table = of_match_ptr(omap_keypad_dt_match),
 	},
 };
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH v2 2/3] Input: omap-keypad: errata i689: Correct debounce time
From: Illia Smyrnov @ 2013-07-26 17:09 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, linux-kernel, linux-omap
In-Reply-To: <1374858546-25034-1-git-send-email-illia.smyrnov@ti.com>

From: Axel Haslam <axelhaslam@ti.com>

Set debounce time value to 12ms to workaround the errata.

ERRATA DESCRIPTION:
When a key is released for a time shorter than the debounce time,
in-between 2 key press (KP1 and KP2), the keyboard state machine will go to
idle mode and will never detect the key release (after KP1,and also after KP2),
and thus will never generate a new IRQ indicating the key release.
>From the operating system standpoint, only a key press as been detected,
and the key will keep on being printed on the screen until another or
the same key is pressed again.When the failing state has been reached,
the KBD_STATEMACHINE register will show "idle state" and the KBD_FULLCODE
register won't be empty, this is the signature of the bug.There is no impact
on the power consumption of the system as the state machine goes to IDLE state.

WORKAROUND:
First thing is to program the debounce time correctly:
If X (us) is the maximum time of bounces when a key is pressed or released,
and Y (us) is the minimum time of a key release that is expected to be detected,
then the debounce time should be set to a value inbetween X and Y.

In case it is still possible to get shorter than debounce time key-release
events, then the only solution is to implement a software workaround:

Before printing a second character on the screen, the software must check if
the keyboard has hit the failing condition (cf signature of the bug above)
or if the key is still really pressed and then take the appropriate actions.

Silicon revisions impacted: OMAP4430 ES 1.0,2.0,2.1,2.2,2.3
                            OMAP4460 ES 1.0,1.1
                            OMAP5430 ES 1.0

Signed-off-by: Axel Haslam <axelhaslam@ti.com>
Signed-off-by: Illia Smyrnov <illia.smyrnov@ti.com>
---
 Based on top of v3.11-rc2

 Depends on:
 Input: omap-keypad: Cleanup - use bitfiled instead of hardcoded values
 https://patchwork.kernel.org/patch/2832913/

 drivers/input/keyboard/omap4-keypad.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c
index feab00f..e8bdc76 100644
--- a/drivers/input/keyboard/omap4-keypad.c
+++ b/drivers/input/keyboard/omap4-keypad.c
@@ -62,8 +62,10 @@
 
 /* OMAP4 values */
 #define OMAP4_VAL_IRQDISABLE		0x0
-#define OMAP4_VAL_DEBOUNCINGTIME	0x7
-#define OMAP4_VAL_PVT			0x7
+
+/* Errata i689: set debounce time value to 12ms */
+#define OMAP4_VAL_DEBOUNCINGTIME	0x2
+#define OMAP4_VAL_PVT			0x6
 
 enum {
 	KBD_REVISION_OMAP4 = 0,
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH v2 0/3] Input: omap-keypad: Wakeup capability and w/a for i689 errata.
From: Illia Smyrnov @ 2013-07-26 17:09 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, linux-kernel, linux-omap

This patchset adds wake up capability for the keypad and
workaround for i689 errata.

Based on top of v3.11-rc2

Depends on:

Input: omap-keypad: Convert to threaded IRQ
https://patchwork.kernel.org/patch/2832920/

Input: omap-keypad: Cleanup - use bitfiled instead of hardcoded values
https://patchwork.kernel.org/patch/2832913/

Axel Haslam (2):
  Input: omap-keypad: errata i689: Correct debounce time
  Input: omap-keypad: Set irq to level instead of edge

Illia Smyrnov (1):
  Input: omap-keypad: Enable wakeup capability for keypad.

 drivers/input/keyboard/omap4-keypad.c |   55 +++++++++++++++++++++++++++++++--
 1 file changed, 52 insertions(+), 3 deletions(-)

-- 
1.7.9.5


^ permalink raw reply

* [PATCH v2 3/3] Input: omap-keypad: Set irq to level instead of edge
From: Illia Smyrnov @ 2013-07-26 17:09 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, linux-kernel, linux-omap
In-Reply-To: <1374858546-25034-1-git-send-email-illia.smyrnov@ti.com>

From: Axel Haslam <axelhaslam@ti.com>

Set mpu irq to level instead of edge, since if mpu is in low power
an edge detection may be lost if the event is a wkup event.

Signed-off-by: Axel Haslam <axelhaslam@ti.com>
Signed-off-by: Illia Smyrnov <illia.smyrnov@ti.com>
---
 Based on top of v3.11-rc2

 Depends on:
 Input: omap-keypad: Convert to threaded IRQ
 https://patchwork.kernel.org/patch/2832920/

 drivers/input/keyboard/omap4-keypad.c |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c
index e8bdc76..7adb53e 100644
--- a/drivers/input/keyboard/omap4-keypad.c
+++ b/drivers/input/keyboard/omap4-keypad.c
@@ -374,9 +374,13 @@ static int omap4_keypad_probe(struct platform_device *pdev)
 		goto err_free_keymap;
 	}
 
+	/*
+	 * Set irq level detection for mpu. Edge event are missed in gic
+	 * if the mpu is in low power and keypad event is a wakeup
+	 */
 	error = request_threaded_irq(keypad_data->irq, omap4_keypad_irq_handler,
 				     omap4_keypad_irq_thread_fn,
-				     IRQF_TRIGGER_RISING,
+				     IRQF_TRIGGER_HIGH,
 				     "omap4-keypad", keypad_data);
 	if (error) {
 		dev_err(&pdev->dev, "failed to register interrupt\n");
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH] mfd: input: arm: dts: doc: ti_am335x: typo fixes (coordiante -> coordinate)
From: Zubair Lutfullah @ 2013-07-26 21:37 UTC (permalink / raw)
  To: linux-doc; +Cc: linux-omap, linux-kernel, linux-arm-kernel, linux-input
In-Reply-To: <1374874630-25457-1-git-send-email-zubair.lutfullah@gmail.com>

Did a grep for 'coordiante' and replaced them all with 'coordinate'

Signed-off-by: Zubair Lutfullah <zubair.lutfullah@gmail.com>
Acked-by: Lee Jones <lee.jones@linaro.org>
---
 .../bindings/input/touchscreen/ti-tsc-adc.txt      |    4 ++--
 arch/arm/boot/dts/am335x-evm.dts                   |    2 +-
 drivers/input/touchscreen/ti_am335x_tsc.c          |    2 +-
 drivers/mfd/ti_am335x_tscadc.c                     |    2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/Documentation/devicetree/bindings/input/touchscreen/ti-tsc-adc.txt b/Documentation/devicetree/bindings/input/touchscreen/ti-tsc-adc.txt
index 491c97b..3e22aec 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/ti-tsc-adc.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/ti-tsc-adc.txt
@@ -6,7 +6,7 @@ Required properties:
 	ti,wires: Wires refer to application modes i.e. 4/5/8 wire touchscreen
 		  support on the platform.
 	ti,x-plate-resistance: X plate resistance
-	ti,coordiante-readouts: The sequencer supports a total of 16
+	ti,coordinate-readouts: The sequencer supports a total of 16
 				programmable steps each step is used to
 				read a single coordinate. A single
                                 readout is enough but multiple reads can
@@ -34,7 +34,7 @@ Example:
 		tsc {
 			ti,wires = <4>;
 			ti,x-plate-resistance = <200>;
-			ti,coordiante-readouts = <5>;
+			ti,coordinate-readouts = <5>;
 			ti,wire-config = <0x00 0x11 0x22 0x33>;
 		};
 
diff --git a/arch/arm/boot/dts/am335x-evm.dts b/arch/arm/boot/dts/am335x-evm.dts
index 0fa4c7f..e50b781 100644
--- a/arch/arm/boot/dts/am335x-evm.dts
+++ b/arch/arm/boot/dts/am335x-evm.dts
@@ -250,7 +250,7 @@
 	tsc {
 		ti,wires = <4>;
 		ti,x-plate-resistance = <200>;
-		ti,coordiante-readouts = <5>;
+		ti,coordinate-readouts = <5>;
 		ti,wire-config = <0x00 0x11 0x22 0x33>;
 	};
 
diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
index 0e9f02a..6422f65 100644
--- a/drivers/input/touchscreen/ti_am335x_tsc.c
+++ b/drivers/input/touchscreen/ti_am335x_tsc.c
@@ -348,7 +348,7 @@ static int titsc_parse_dt(struct platform_device *pdev,
 	if (err < 0)
 		return err;
 
-	err = of_property_read_u32(node, "ti,coordiante-readouts",
+	err = of_property_read_u32(node, "ti,coordinate-readouts",
 			&ts_dev->coordinate_readouts);
 	if (err < 0)
 		return err;
diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c
index cd74d59..1686ae6 100644
--- a/drivers/mfd/ti_am335x_tscadc.c
+++ b/drivers/mfd/ti_am335x_tscadc.c
@@ -106,7 +106,7 @@ static	int ti_tscadc_probe(struct platform_device *pdev)
 
 	node = of_get_child_by_name(pdev->dev.of_node, "tsc");
 	of_property_read_u32(node, "ti,wires", &tsc_wires);
-	of_property_read_u32(node, "ti,coordiante-readouts", &readouts);
+	of_property_read_u32(node, "ti,coordinate-readouts", &readouts);
 
 	node = of_get_child_by_name(pdev->dev.of_node, "adc");
 	of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) {
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH] mfd: input: arm: dts: doc: ti_am335x: typo fix (coordiante -> coordinate)
From: Zubair Lutfullah @ 2013-07-26 21:37 UTC (permalink / raw)
  To: linux-doc; +Cc: linux-kernel, linux-omap, linux-arm-kernel, linux-input

 
Small typo fix. Received ack from mfd already.
Awaiting acks from all the rest.

As Lee from mfd stated, we can remove these during staging now.
or we'll have to leave typos as is later on.

Zubair Lutfullah (1):
  mfd: input: arm: dts: doc: ti_am335x: typo fixes (coordiante ->
    coordinate)

 .../bindings/input/touchscreen/ti-tsc-adc.txt      |    4 ++--
 arch/arm/boot/dts/am335x-evm.dts                   |    2 +-
 drivers/input/touchscreen/ti_am335x_tsc.c          |    2 +-
 drivers/mfd/ti_am335x_tscadc.c                     |    2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

-- 
1.7.9.5


^ permalink raw reply

* [PATCH 1/2] input: ti_tsc: Enable shared IRQ for TSC
From: Zubair Lutfullah @ 2013-07-26 23:51 UTC (permalink / raw)
  To: jic23-KWPb1pKIrIJaa/9Udqfwiw
  Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r, Russ.Dill-l0cyMroinI0
In-Reply-To: <1374882674-18403-1-git-send-email-zubair.lutfullah-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

From: "Patil, Rachna" <rachna-l0cyMroinI0@public.gmane.org>

Touchscreen and ADC share the same IRQ line from parent MFD core.
Previously only Touchscreen was interrupt based.
With continuous mode support added in ADC driver, driver requires
interrupt to process the ADC samples, so enable shared IRQ flag bit for
touchscreen.

Signed-off-by: Patil, Rachna <rachna-l0cyMroinI0@public.gmane.org>
Acked-by: Vaibhav Hiremath <hvaibhav-l0cyMroinI0@public.gmane.org>
Signed-off-by: Zubair Lutfullah <zubair.lutfullah-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Acked-by: Greg Kroah-Hartman <gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org>
---
 drivers/input/touchscreen/ti_am335x_tsc.c |   18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
index e1c5300..68d1250 100644
--- a/drivers/input/touchscreen/ti_am335x_tsc.c
+++ b/drivers/input/touchscreen/ti_am335x_tsc.c
@@ -260,8 +260,18 @@ static irqreturn_t titsc_irq(int irq, void *dev)
 	unsigned int fsm;
 
 	status = titsc_readl(ts_dev, REG_IRQSTATUS);
-	if (status & IRQENB_FIFO0THRES) {
-
+	/*
+	 * ADC and touchscreen share the IRQ line.
+	 * FIFO1 threshold, FIFO1 Overrun and FIFO1 underflow
+	 * interrupts are used by ADC,
+	 * hence return from touchscreen IRQ handler if FIFO1
+	 * related interrupts occurred.
+	 */
+	if ((status & IRQENB_FIFO1THRES) ||
+			(status & IRQENB_FIFO1OVRRUN) ||
+			(status & IRQENB_FIFO1UNDRFLW))
+		return IRQ_NONE;
+	else if (status & IRQENB_FIFO0THRES) {
 		titsc_read_coordinates(ts_dev, &x, &y, &z1, &z2);
 
 		if (ts_dev->pen_down && z1 != 0 && z2 != 0) {
@@ -315,7 +325,7 @@ static irqreturn_t titsc_irq(int irq, void *dev)
 	}
 
 	if (irqclr) {
-		titsc_writel(ts_dev, REG_IRQSTATUS, irqclr);
+		titsc_writel(ts_dev, REG_IRQSTATUS, (status | irqclr));
 		am335x_tsc_se_update(ts_dev->mfd_tscadc);
 		return IRQ_HANDLED;
 	}
@@ -389,7 +399,7 @@ static int titsc_probe(struct platform_device *pdev)
 	}
 
 	err = request_irq(ts_dev->irq, titsc_irq,
-			  0, pdev->dev.driver->name, ts_dev);
+			  IRQF_SHARED, pdev->dev.driver->name, ts_dev);
 	if (err) {
 		dev_err(&pdev->dev, "failed to allocate irq.\n");
 		goto err_free_mem;
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 0/2] iio: input: ti_am335x_adc: Add continuous sampling and trigger support round 3
From: Zubair Lutfullah @ 2013-07-26 23:51 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, linux-kernel, linux-input, gregkh, Russ.Dill

ADC and TSC share an IRQ line. Patch one is simple and adds shared irq support on the TSC side.

The second patch adds continuous sampling support to the am335x_adc driver.

It has been submitted previously. This is round 3.

Previously:
- Submitted as a series of patches and bug fixes.
- The driver would continuously push samples into a buffer exposed to userspace.
- Extra sysfs attributes for selecting continuous mode or one shot mode.
- No trigger functionality.
- Reading from /dev/iio required patching the provided generic_buffer.c iio test application to bypass trigger.
- Only one channel could be read at a time.
- And even then, samples were skipped as the FIFO was read incorrectly.

Now: 
- All bug fixes merged together in one patch.
- Stuck closely to the IIO ABI this time.
- Added trigger support.
- Fixed channel scanning where only one channel could be read into the buffer at a time.
- Now all enabled channels in the scan_elements folder are pushed to the userspace properly without skipping any samples.
- generic_buffer.c test application can read samples without any modification.
- A sysfs trigger starts acquisition.

This has been tested on the Beaglebone Black running the am335x processor.
The patches apply on the iio branch fixes-togreg.

Patil, Rachna (1):
  input: ti_tsc: Enable shared IRQ for TSC

Zubair Lutfullah (1):
  iio: ti_am335x_adc: Add continuous sampling and trigger support

 drivers/iio/adc/ti_am335x_adc.c           |  334 +++++++++++++++++++++++------
 drivers/input/touchscreen/ti_am335x_tsc.c |   18 +-
 include/linux/mfd/ti_am335x_tscadc.h      |   13 +-
 3 files changed, 299 insertions(+), 66 deletions(-)

-- 
1.7.9.5


^ permalink raw reply

* [PATCH 2/2] iio: ti_am335x_adc: Add continuous sampling and trigger support
From: Zubair Lutfullah @ 2013-07-26 23:51 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, linux-kernel, linux-input, gregkh, Russ.Dill
In-Reply-To: <1374882674-18403-1-git-send-email-zubair.lutfullah@gmail.com>

Previously the driver had only one-shot reading functionality.
This patch adds triggered buffer support to the driver.
A buffer of samples can now be read via /dev/iio.

Patil Rachna (TI) laid the ground work for ADC HW register access.
Russ Dill (TI) fixed bugs in the driver relevant to FIFOs and IRQs.

I fixed channel scanning so multiple ADC channels can be read
simultaneously and pushed to userspace.
Restructured the driver to fit IIO ABI.
And added trigger support.

Signed-off-by: Zubair Lutfullah <zubair.lutfullah@gmail.com>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Russ Dill <Russ.Dill@ti.com>
---
 drivers/iio/adc/ti_am335x_adc.c      |  334 +++++++++++++++++++++++++++-------
 include/linux/mfd/ti_am335x_tscadc.h |   13 +-
 2 files changed, 285 insertions(+), 62 deletions(-)

diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
index 3ceac3e..630ce85 100644
--- a/drivers/iio/adc/ti_am335x_adc.c
+++ b/drivers/iio/adc/ti_am335x_adc.c
@@ -26,14 +26,25 @@
 #include <linux/of_device.h>
 #include <linux/iio/machine.h>
 #include <linux/iio/driver.h>
-
 #include <linux/mfd/ti_am335x_tscadc.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
 
 struct tiadc_device {
 	struct ti_tscadc_dev *mfd_tscadc;
 	int channels;
 	u8 channel_line[8];
 	u8 channel_step[8];
+	struct work_struct poll_work;
+	wait_queue_head_t wq_data_avail;
+	bool data_avail;
+	u32 *inputbuffer;
+	int sample_count;
+	int irq;
 };
 
 static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg)
@@ -56,27 +67,28 @@ static u32 get_adc_step_mask(struct tiadc_device *adc_dev)
 	return step_en;
 }
 
-static void tiadc_step_config(struct tiadc_device *adc_dev)
+static void tiadc_step_config(struct iio_dev *indio_dev)
 {
+	struct tiadc_device *adc_dev = iio_priv(indio_dev);
 	unsigned int stepconfig;
-	int i, steps;
+	int i, steps, chan;
 
 	/*
 	 * There are 16 configurable steps and 8 analog input
 	 * lines available which are shared between Touchscreen and ADC.
-	 *
 	 * Steps backwards i.e. from 16 towards 0 are used by ADC
 	 * depending on number of input lines needed.
 	 * Channel would represent which analog input
 	 * needs to be given to ADC to digitalize data.
 	 */
-
 	steps = TOTAL_STEPS - adc_dev->channels;
-	stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1;
+	if (iio_buffer_enabled(indio_dev))
+		stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1
+					| STEPCONFIG_MODE_SWCNT;
+	else
+		stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1;
 
 	for (i = 0; i < adc_dev->channels; i++) {
-		int chan;
-
 		chan = adc_dev->channel_line[i];
 		tiadc_writel(adc_dev, REG_STEPCONFIG(steps),
 				stepconfig | STEPCONFIG_INP(chan));
@@ -85,7 +97,190 @@ static void tiadc_step_config(struct tiadc_device *adc_dev)
 		adc_dev->channel_step[i] = steps;
 		steps++;
 	}
+}
+
+static irqreturn_t tiadc_irq(int irq, void *private)
+{
+	struct iio_dev *idev = private;
+	struct tiadc_device *adc_dev = iio_priv(idev);
+	unsigned int status, config;
+	status = tiadc_readl(adc_dev, REG_IRQSTATUS);
+
+	/* FIFO Overrun. Clear flag. Disable/Enable ADC to recover */
+	if (status & IRQENB_FIFO1OVRRUN) {
+		config = tiadc_readl(adc_dev, REG_CTRL);
+		config &= ~(CNTRLREG_TSCSSENB);
+		tiadc_writel(adc_dev, REG_CTRL, config);
+		tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1OVRRUN |
+				IRQENB_FIFO1UNDRFLW | IRQENB_FIFO1THRES);
+		tiadc_writel(adc_dev, REG_CTRL, (config | CNTRLREG_TSCSSENB));
+		return IRQ_HANDLED;
+	} else if (status & IRQENB_FIFO1THRES) {
+		/* Wake adc_work that pushes FIFO data to iio buffer */
+		tiadc_writel(adc_dev, REG_IRQCLR, IRQENB_FIFO1THRES);
+		adc_dev->data_avail = 1;
+		wake_up_interruptible(&adc_dev->wq_data_avail);
+		return IRQ_HANDLED;
+	} else
+		return IRQ_NONE;
+}
+
+static irqreturn_t tiadc_trigger_h(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct tiadc_device *adc_dev = iio_priv(indio_dev);
+	unsigned int config;
+
+	schedule_work(&adc_dev->poll_work);
+	config = tiadc_readl(adc_dev, REG_CTRL);
+	tiadc_writel(adc_dev, REG_CTRL,	config & ~CNTRLREG_TSCSSENB);
+	tiadc_writel(adc_dev, REG_CTRL,	config |  CNTRLREG_TSCSSENB);
+
+	tiadc_writel(adc_dev,  REG_IRQSTATUS, IRQENB_FIFO1THRES |
+			 IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW);
+	tiadc_writel(adc_dev,  REG_IRQENABLE, IRQENB_FIFO1THRES
+				| IRQENB_FIFO1OVRRUN);
+
+	iio_trigger_notify_done(indio_dev->trig);
+	return IRQ_HANDLED;
+}
+
+static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
+{
+	return iio_sw_buffer_preenable(indio_dev);
+}
+
+static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
+{
+	struct tiadc_device *adc_dev = iio_priv(indio_dev);
+	struct iio_buffer *buffer = indio_dev->buffer;
+	unsigned int enb, stepnum;
+	u8 bit;
+
+	tiadc_step_config(indio_dev);
+	tiadc_writel(adc_dev, REG_SE, 0x00);
+	for_each_set_bit(bit, buffer->scan_mask,
+			adc_dev->channels) {
+		struct iio_chan_spec const *chan = indio_dev->channels + bit;
+		/*
+		 * There are a total of 16 steps available
+		 * that are shared between ADC and touchscreen.
+		 * We start configuring from step 16 to 0 incase of
+		 * ADC. Hence the relation between input channel
+		 * and step for ADC would be as below.
+		 */
+		stepnum = chan->channel + 9;
+		enb = tiadc_readl(adc_dev, REG_SE);
+		enb |= (1 << stepnum);
+		tiadc_writel(adc_dev, REG_SE, enb);
+	}
+
+	return iio_triggered_buffer_postenable(indio_dev);
+}
+
+static int tiadc_buffer_predisable(struct iio_dev *indio_dev)
+{
+	struct tiadc_device *adc_dev = iio_priv(indio_dev);
+	int fifo1count, i, read, config;
+
+	config = tiadc_readl(adc_dev, REG_CTRL);
+	config &= ~(CNTRLREG_TSCSSENB);
+	tiadc_writel(adc_dev, REG_CTRL, config);
+	tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
+				IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW));
+	tiadc_writel(adc_dev, REG_SE, STPENB_STEPENB_TC);
+
+	/* Flush FIFO of any leftover data */
+	fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
+	for (i = 0; i < fifo1count; i++)
+		read = tiadc_readl(adc_dev, REG_FIFO1);
+
+	return iio_triggered_buffer_predisable(indio_dev);
+}
+
+static int tiadc_buffer_postdisable(struct iio_dev *indio_dev)
+{
+	struct tiadc_device *adc_dev = iio_priv(indio_dev);
+	int config;
+
+	tiadc_step_config(indio_dev);
+	config = tiadc_readl(adc_dev, REG_CTRL);
+	tiadc_writel(adc_dev, REG_CTRL, (config | CNTRLREG_TSCSSENB));
+
+	return 0;
+}
+
+static const struct iio_buffer_setup_ops tiadc_buffer_setup_ops = {
+	.preenable = &tiadc_buffer_preenable,
+	.postenable = &tiadc_buffer_postenable,
+	.predisable = &tiadc_buffer_predisable,
+	.postdisable = &tiadc_buffer_postdisable,
+};
+
+static void tiadc_adc_work(struct work_struct *work_s)
+{
+	struct tiadc_device *adc_dev =
+		container_of(work_s, struct tiadc_device, poll_work);
+	struct iio_dev *indio_dev = iio_priv_to_dev(adc_dev);
+	struct iio_buffer *buffer = indio_dev->buffer;
+	int i, j, k, fifo1count, read;
+	unsigned int config;
+	int size_to_acquire = buffer->access->get_length(buffer);
+	int sample_count = 0;
+	u32 *data;
+
+	adc_dev->data_avail = 0;
+	data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+	if (data == NULL)
+		goto out;
+
+	while (sample_count < size_to_acquire) {
+		tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES);
+		tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES);
+
+		wait_event_interruptible(adc_dev->wq_data_avail,
+					(adc_dev->data_avail == 1));
+		adc_dev->data_avail = 0;
+
+		fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
+		if (fifo1count * sizeof(u32) <
+				buffer->access->get_bytes_per_datum(buffer))
+			continue;
+
+		sample_count = sample_count + fifo1count;
+		for (k = 0; k < fifo1count; k = k + i) {
+			for (i = 0, j = 0; i < (indio_dev->scan_bytes)/4; i++) {
+				read = tiadc_readl(adc_dev, REG_FIFO1);
+				data[i] = read & FIFOREAD_DATA_MASK;
+			}
+			iio_push_to_buffers(indio_dev, (u8 *) data);
+		}
+	}
+out:
+	tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
+				IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW));
+	config = tiadc_readl(adc_dev, REG_CTRL);
+	tiadc_writel(adc_dev, REG_CTRL,	config & ~CNTRLREG_TSCSSENB);
+}
 
+irqreturn_t tiadc_iio_pollfunc(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct tiadc_device *adc_dev = iio_priv(indio_dev);
+	int i, fifo1count, read;
+
+	tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
+				IRQENB_FIFO1OVRRUN |
+				IRQENB_FIFO1UNDRFLW));
+
+	/* Flush FIFO before trigger */
+	fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
+	for (i = 0; i < fifo1count; i++)
+		read = tiadc_readl(adc_dev, REG_FIFO1);
+
+	return IRQ_WAKE_THREAD;
 }
 
 static const char * const chan_name_ain[] = {
@@ -120,13 +315,13 @@ static int tiadc_channel_init(struct iio_dev *indio_dev, int channels)
 		chan->channel = adc_dev->channel_line[i];
 		chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
 		chan->datasheet_name = chan_name_ain[chan->channel];
+		chan->scan_index = i;
 		chan->scan_type.sign = 'u';
 		chan->scan_type.realbits = 12;
 		chan->scan_type.storagebits = 32;
 	}
 
 	indio_dev->channels = chan_array;
-
 	return 0;
 }
 
@@ -141,58 +336,48 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,
 {
 	struct tiadc_device *adc_dev = iio_priv(indio_dev);
 	int i, map_val;
-	unsigned int fifo1count, read, stepid;
-	u32 step = UINT_MAX;
-	bool found = false;
-	u32 step_en;
-	unsigned long timeout = jiffies + usecs_to_jiffies
-				(IDLE_TIMEOUT * adc_dev->channels);
-	step_en = get_adc_step_mask(adc_dev);
-	am335x_tsc_se_set(adc_dev->mfd_tscadc, step_en);
-
-	/* Wait for ADC sequencer to complete sampling */
-	while (tiadc_readl(adc_dev, REG_ADCFSM) & SEQ_STATUS) {
-		if (time_after(jiffies, timeout))
-			return -EAGAIN;
-		}
-	map_val = chan->channel + TOTAL_CHANNELS;
-
-	/*
-	 * When the sub-system is first enabled,
-	 * the sequencer will always start with the
-	 * lowest step (1) and continue until step (16).
-	 * For ex: If we have enabled 4 ADC channels and
-	 * currently use only 1 out of them, the
-	 * sequencer still configures all the 4 steps,
-	 * leading to 3 unwanted data.
-	 * Hence we need to flush out this data.
-	 */
+	unsigned int fifo1count, read, stepid, step_en;
 
-	for (i = 0; i < ARRAY_SIZE(adc_dev->channel_step); i++) {
-		if (chan->channel == adc_dev->channel_line[i]) {
-			step = adc_dev->channel_step[i];
-			break;
-		}
-	}
-	if (WARN_ON_ONCE(step == UINT_MAX))
-		return -EINVAL;
-
-	fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
-	for (i = 0; i < fifo1count; i++) {
-		read = tiadc_readl(adc_dev, REG_FIFO1);
-		stepid = read & FIFOREAD_CHNLID_MASK;
-		stepid = stepid >> 0x10;
-
-		if (stepid == map_val) {
-			read = read & FIFOREAD_DATA_MASK;
-			found = true;
-			*val = read;
+	if (iio_buffer_enabled(indio_dev))
+		return -EBUSY;
+	else {
+		unsigned long timeout = jiffies + usecs_to_jiffies
+					(IDLE_TIMEOUT * adc_dev->channels);
+		step_en = get_adc_step_mask(adc_dev);
+		am335x_tsc_se_set(adc_dev->mfd_tscadc, step_en);
+
+		/* Wait for ADC sequencer to complete sampling */
+		while (tiadc_readl(adc_dev, REG_ADCFSM) & SEQ_STATUS) {
+			if (time_after(jiffies, timeout))
+				return -EAGAIN;
+			}
+		map_val = chan->channel + TOTAL_CHANNELS;
+
+		/*
+		 * When the sub-system is first enabled,
+		 * the sequencer will always start with the
+		 * lowest step (1) and continue until step (16).
+		 * For ex: If we have enabled 4 ADC channels and
+		 * currently use only 1 out of them, the
+		 * sequencer still configures all the 4 steps,
+		 * leading to 3 unwanted data.
+		 * Hence we need to flush out this data.
+		 */
+
+		fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
+		for (i = 0; i < fifo1count; i++) {
+			read = tiadc_readl(adc_dev, REG_FIFO1);
+			stepid = read & FIFOREAD_CHNLID_MASK;
+			stepid = stepid >> 0x10;
+
+			if (stepid == map_val) {
+				read = read & FIFOREAD_DATA_MASK;
+				*val = read;
+				return IIO_VAL_INT;
+			}
 		}
+		return -EAGAIN;
 	}
-
-	if (found == false)
-		return -EBUSY;
-	return IIO_VAL_INT;
 }
 
 static const struct iio_info tiadc_info = {
@@ -231,18 +416,33 @@ static int tiadc_probe(struct platform_device *pdev)
 		channels++;
 	}
 	adc_dev->channels = channels;
+	adc_dev->irq = adc_dev->mfd_tscadc->irq;
 
 	indio_dev->dev.parent = &pdev->dev;
 	indio_dev->name = dev_name(&pdev->dev);
 	indio_dev->modes = INDIO_DIRECT_MODE;
 	indio_dev->info = &tiadc_info;
 
-	tiadc_step_config(adc_dev);
+	tiadc_step_config(indio_dev);
+	tiadc_writel(adc_dev, REG_FIFO1THR, FIFO1_THRESHOLD);
 
 	err = tiadc_channel_init(indio_dev, adc_dev->channels);
 	if (err < 0)
 		goto err_free_device;
 
+	INIT_WORK(&adc_dev->poll_work, &tiadc_adc_work);
+	init_waitqueue_head(&adc_dev->wq_data_avail);
+
+	err = request_irq(adc_dev->irq, tiadc_irq, IRQF_SHARED,
+		indio_dev->name, indio_dev);
+	if (err)
+		goto err_free_irq;
+
+	err = iio_triggered_buffer_setup(indio_dev, &tiadc_iio_pollfunc,
+			&tiadc_trigger_h, &tiadc_buffer_setup_ops);
+	if (err)
+		goto err_unregister;
+
 	err = iio_device_register(indio_dev);
 	if (err)
 		goto err_free_channels;
@@ -251,6 +451,10 @@ static int tiadc_probe(struct platform_device *pdev)
 
 	return 0;
 
+err_unregister:
+	iio_buffer_unregister(indio_dev);
+err_free_irq:
+	free_irq(adc_dev->irq, indio_dev);
 err_free_channels:
 	tiadc_channels_remove(indio_dev);
 err_free_device:
@@ -265,7 +469,9 @@ static int tiadc_remove(struct platform_device *pdev)
 	struct tiadc_device *adc_dev = iio_priv(indio_dev);
 	u32 step_en;
 
+	free_irq(adc_dev->irq, indio_dev);
 	iio_device_unregister(indio_dev);
+	iio_buffer_unregister(indio_dev);
 	tiadc_channels_remove(indio_dev);
 
 	step_en = get_adc_step_mask(adc_dev);
@@ -303,10 +509,16 @@ static int tiadc_resume(struct device *dev)
 
 	/* Make sure ADC is powered up */
 	restore = tiadc_readl(adc_dev, REG_CTRL);
-	restore &= ~(CNTRLREG_POWERDOWN);
+	restore &= ~(CNTRLREG_TSCSSENB);
 	tiadc_writel(adc_dev, REG_CTRL, restore);
 
-	tiadc_step_config(adc_dev);
+	tiadc_writel(adc_dev, REG_FIFO1THR, FIFO1_THRESHOLD);
+	tiadc_step_config(indio_dev);
+
+	/* Make sure ADC is powered up */
+	restore &= ~(CNTRLREG_POWERDOWN);
+	restore |= CNTRLREG_TSCSSENB;
+	tiadc_writel(adc_dev, REG_CTRL, restore);
 
 	return 0;
 }
diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
index db1791b..cb61027 100644
--- a/include/linux/mfd/ti_am335x_tscadc.h
+++ b/include/linux/mfd/ti_am335x_tscadc.h
@@ -46,17 +46,23 @@
 /* Step Enable */
 #define STEPENB_MASK		(0x1FFFF << 0)
 #define STEPENB(val)		((val) << 0)
+#define ENB(val)			(1 << (val))
+#define STPENB_STEPENB		STEPENB(0x1FFFF)
+#define STPENB_STEPENB_TC	STEPENB(0x1FFF)
 
 /* IRQ enable */
 #define IRQENB_HW_PEN		BIT(0)
 #define IRQENB_FIFO0THRES	BIT(2)
 #define IRQENB_FIFO1THRES	BIT(5)
 #define IRQENB_PENUP		BIT(9)
+#define IRQENB_FIFO1OVRRUN	BIT(6)
+#define IRQENB_FIFO1UNDRFLW	BIT(7)
 
 /* Step Configuration */
 #define STEPCONFIG_MODE_MASK	(3 << 0)
 #define STEPCONFIG_MODE(val)	((val) << 0)
 #define STEPCONFIG_MODE_HWSYNC	STEPCONFIG_MODE(2)
+#define STEPCONFIG_MODE_SWCNT	STEPCONFIG_MODE(1)
 #define STEPCONFIG_AVG_MASK	(7 << 2)
 #define STEPCONFIG_AVG(val)	((val) << 2)
 #define STEPCONFIG_AVG_16	STEPCONFIG_AVG(4)
@@ -124,7 +130,8 @@
 #define	MAX_CLK_DIV		7
 #define TOTAL_STEPS		16
 #define TOTAL_CHANNELS		8
-
+#define FIFO1_THRESHOLD		19
+#define FIFO_SIZE			64
 /*
 * ADC runs at 3MHz, and it takes
 * 15 cycles to latch one data output.
@@ -153,6 +160,10 @@ struct ti_tscadc_dev {
 
 	/* adc device */
 	struct adc_device *adc;
+
+	/* Context save */
+	unsigned int irqstat;
+	unsigned int ctrl;
 };
 
 static inline struct ti_tscadc_dev *ti_tscadc_dev_get(struct platform_device *p)
-- 
1.7.9.5


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox