@@ -605,6 +605,25 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
return IRQ_RETVAL(ret);
}
+/*
+ * i8042_disable_kbd_port disables keyboard port on chip
+ */
+
+static int i8042_disable_kbd_port(void)
+{
+ i8042_ctr |= I8042_CTR_KBDDIS;
+ i8042_ctr &= ~I8042_CTR_KBDINT;
+
+ if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
+ i8042_ctr |= I8042_CTR_KBDINT;
+ i8042_ctr &= ~I8042_CTR_KBDDIS;
+ pr_err("Failed to disable KBD port\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
/*
* i8042_enable_kbd_port enables keyboard port on chip
*/
@@ -624,6 +643,25 @@ static int i8042_enable_kbd_port(void)
return 0;
}
+/*
+ * i8042_disable_aux_port disables AUX (mouse) port on chip
+ */
+
+static int i8042_disable_aux_port(void)
+{
+ i8042_ctr |= I8042_CTR_AUXDIS;
+ i8042_ctr &= ~I8042_CTR_AUXINT;
+
+ if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
+ i8042_ctr |= I8042_CTR_AUXINT;
+ i8042_ctr &= ~I8042_CTR_AUXDIS;
+ pr_err("Failed to disable AUX port\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
/*
* i8042_enable_aux_port enables AUX (mouse) port on chip
*/
@@ -1227,6 +1265,7 @@ static int i8042_controller_resume(bool s2r_wants_reset)
static int i8042_pm_suspend(struct device *dev)
{
int i;
+ bool wakeup_enabled[I8042_NUM_PORTS] = { false };
if (pm_suspend_via_firmware())
i8042_controller_reset(true);
@@ -1235,8 +1274,21 @@ static int i8042_pm_suspend(struct device *dev)
for (i = 0; i < I8042_NUM_PORTS; i++) {
struct serio *serio = i8042_ports[i].serio;
- if (serio && device_may_wakeup(&serio->dev))
+ if (serio && device_may_wakeup(&serio->dev)) {
enable_irq_wake(i8042_ports[i].irq);
+ wakeup_enabled[i] = true;
+ }
+ }
+
+ /* For KBD and AUX ports, if at least one of them is enabled for wakeup
+ * the system, we need to disable the one which isn't enabled for
+ * wakeup, otherwise this port could impact the wakeup of the other port
+ */
+ if (wakeup_enabled[I8042_KBD_PORT_NO] || wakeup_enabled[I8042_AUX_PORT_NO]) {
+ if (i8042_ports[I8042_KBD_PORT_NO].serio && !wakeup_enabled[I8042_KBD_PORT_NO])
+ i8042_disable_kbd_port();
+ else if (i8042_ports[I8042_AUX_PORT_NO].serio && !wakeup_enabled[I8042_AUX_PORT_NO])
+ i8042_disable_aux_port();
}
return 0;
@@ -1254,12 +1306,22 @@ static int i8042_pm_resume(struct device *dev)
{
bool want_reset;
int i;
+ bool wakeup_enabled[I8042_NUM_PORTS] = { false };
for (i = 0; i < I8042_NUM_PORTS; i++) {
struct serio *serio = i8042_ports[i].serio;
- if (serio && device_may_wakeup(&serio->dev))
+ if (serio && device_may_wakeup(&serio->dev)) {
disable_irq_wake(i8042_ports[i].irq);
+ wakeup_enabled[i] = true;
+ }
+ }
+
+ if (wakeup_enabled[I8042_KBD_PORT_NO] || wakeup_enabled[I8042_AUX_PORT_NO]) {
+ if (i8042_ports[I8042_KBD_PORT_NO].serio && !wakeup_enabled[I8042_KBD_PORT_NO])
+ i8042_enable_kbd_port();
+ else if (i8042_ports[I8042_AUX_PORT_NO].serio && !wakeup_enabled[I8042_AUX_PORT_NO])
+ i8042_enable_aux_port();
}
/*
We found an suspend/resume issue on many Lenovo and Dell laptops, those machines support s2idle and have i8042 kbd, i8042 trackpoint and i8042 touchpad, the current driver will enable the wakeup on kbd if the s2idle is the current mode. If we suspend the system, then press any key on the keyboard, the system will be resumed as expected. But if we press trackpoint or touchpad ahead of kbd, the kbd can't wakeup the system anymore, need to press PWR_BUTTON to resume the system. Similarly, if we enable wakeup on i8042 trackpoint and disable wakeup on i8042 kbd via sysfs interface, and after suspend, we press the kbd ahead of trackpoint, the trackpoint can't wakeup the system too. It is highly possible that this is a defect in the EC firmware, but this issue exists on most of the laptops (maybe all laptops with i8042 kbd, i8042 trackpoint and i8042 touchpad), it is not practical to fix this issue by upgrading the firmware. Let's workaround this issue in the kernel driver. So far, this issue is only reproduced on KBD port + AUX port, there is no issue reported on MUX port yet, we only handle these 2 ports here. If one of them is enabled for wakeup, we temporarily disable the port which is not enabled for wakeup. If both of them are enabled or disabled for wakeup, do nothing. Signed-off-by: Hui Wang <hui.wang@canonical.com> --- drivers/input/serio/i8042.c | 66 +++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-)