diff mbox series

Input: elantech: Retry ETP_FW_VERSION_QUERY on Lenovo Thinkpad E14 Gen2

Message ID 20250125-fix-elantech-firmware-query-v1-1-b507a5361741@mailbox.org
State New
Headers show
Series Input: elantech: Retry ETP_FW_VERSION_QUERY on Lenovo Thinkpad E14 Gen2 | expand

Commit Message

Maurice Hieronymus Jan. 25, 2025, 3:55 p.m. UTC
On the ThinkPad E14 Gen2, the touchpad occasionally fails to respond to
the ETP_FW_VERSION_QUERY during initialization at boot. As a result, the
touchpad is detected as a generic mouse. Reloading the `psmouse` kernel
module after startup resolves the issue, suggesting a timing-related
problem.

This patch retries the ETP_FW_VERSION_QUERY command if it fails. This
allows the touchpad more time to initialize and respond during firmware
version queries.

Signed-off-by: Maurice Hieronymus <mhi@mailbox.org>
---
On the Lenovo ThinkPad E12 Gen2, the touchpad does not respond to scrolling
gestures after startup. The kernel log (`dmesg`) reveals the following:

[1.439036] psmouse serio1: elantech: synaptics_send_cmd query 0x01 failed.
[1.801802] input: PS/2 Logitech Wheel Mouse as /devices/platform/i8042/serio1/input/input6

A command failure causes the touchpad to be recognized as a generic mouse.
Reloading the `psmouse` kernel module resolves the issue:

[158.928793] psmouse serio1: elantech: assuming hardware version 4 (with firmware version 0x5f3001)
[158.941924] psmouse serio1: elantech: Synaptics capabilities query result 0x90, 0x18, 0x0d.
[158.968111] psmouse serio1: elantech: Elan ic body: 0x11, current fw version: 0x4
[159.084746] input: ETPS/2 Elantech Touchpad as /devices/platform/i8042/serio1/input/input15

Enabling debug logging revealed that the driver fails to query the firmware
version of the touchpad during initialization:

[1.435339] libps2:ps2_sliced_command: psmouse serio1: 01 - -5
[1.436523] psmouse:elantech_detect: psmouse serio1: elantech: failed to query firmware version.

The issue appears to be a timing problem, where the touchpad is not fully
initialized during the firmware query. Interestingly, enabling debug
logging reduces the frequency of the issue, giving the touchpad more time
to initialize.

This patch introduces a retry mechanism for PS/2 sliced commands during
firmware queries, similar to the retry logic in `elantech_ps2_command`.
Testing over several weeks confirms that this change resolves the issue
reliably on my hardware.
---
 drivers/input/mouse/elantech.c | 28 ++++++++++++++++++++++++++--
 1 file changed, 26 insertions(+), 2 deletions(-)


---
base-commit: 08bd5b7c9a2401faabdaa1472d45c7de0755fd7e
change-id: 20250101-fix-elantech-firmware-query-07619912f603

Best regards,
diff mbox series

Patch

diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index 79ad98cc1e79..202150443209 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -44,6 +44,30 @@  static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c,
 	return 0;
 }
 
+/*
+ * A retrying version of synaptics_send_cmd
+ */
+static int synaptics_send_cmd_retry(struct psmouse *psmouse, unsigned char c,
+				unsigned char *param)
+{
+	int rc;
+	int tries = ETP_PS2_COMMAND_TRIES;
+
+	do {
+		rc = synaptics_send_cmd(psmouse, c, param);
+		if (rc == 0)
+			break;
+		tries--;
+		psmouse_dbg(psmouse, "%s retrying query 0x%02x (%d).\n", __func__, c, tries);
+		msleep(ETP_PS2_COMMAND_DELAY);
+	} while (tries > 0);
+
+	if (rc)
+		psmouse_err(psmouse, "%s query 0x%02x with retry failed.\n", __func__, c);
+
+	return rc;
+}
+
 /*
  * V3 and later support this fast command
  */
@@ -1432,7 +1456,7 @@  int elantech_detect(struct psmouse *psmouse, bool set_properties)
 	 * value to avoid mis-detection. Logitech mice are known to respond
 	 * to Elantech magic knock and there might be more.
 	 */
-	if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) {
+	if (synaptics_send_cmd_retry(psmouse, ETP_FW_VERSION_QUERY, param)) {
 		psmouse_dbg(psmouse, "failed to query firmware version.\n");
 		return -1;
 	}
@@ -1718,7 +1742,7 @@  static int elantech_query_info(struct psmouse *psmouse,
 	/*
 	 * Do the version query again so we can store the result
 	 */
-	if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) {
+	if (synaptics_send_cmd_retry(psmouse, ETP_FW_VERSION_QUERY, param)) {
 		psmouse_err(psmouse, "failed to query firmware version.\n");
 		return -EINVAL;
 	}