diff options
Diffstat (limited to 'drivers/input/serio/i8042.c')
| -rw-r--r-- | drivers/input/serio/i8042.c | 487 |
1 files changed, 251 insertions, 236 deletions
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 95a78ccbd847..c135254665b6 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -1,19 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * i8042 keyboard and mouse controller driver for Linux * * Copyright (c) 1999-2004 Vojtech Pavlik */ -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/types.h> #include <linux/delay.h> +#include <linux/export.h> #include <linux/module.h> #include <linux/interrupt.h> #include <linux/ioport.h> @@ -25,6 +22,7 @@ #include <linux/i8042.h> #include <linux/slab.h> #include <linux/suspend.h> +#include <linux/property.h> #include <asm/io.h> @@ -48,6 +46,10 @@ static bool i8042_unlock; module_param_named(unlock, i8042_unlock, bool, 0); MODULE_PARM_DESC(unlock, "Ignore keyboard lock."); +static bool i8042_probe_defer; +module_param_named(probe_defer, i8042_probe_defer, bool, 0); +MODULE_PARM_DESC(probe_defer, "Allow deferred probing."); + enum i8042_controller_reset_mode { I8042_RESET_NEVER, I8042_RESET_ALWAYS, @@ -114,6 +116,10 @@ module_param_named(nopnp, i8042_nopnp, bool, 0); MODULE_PARM_DESC(nopnp, "Do not use PNP to detect controller settings"); #endif +static bool i8042_forcenorestore; +module_param_named(forcenorestore, i8042_forcenorestore, bool, 0); +MODULE_PARM_DESC(forcenorestore, "Force no restore on s3 resume, copying s2idle behaviour"); + #define DEBUG #ifdef DEBUG static bool i8042_debug; @@ -125,9 +131,11 @@ module_param_named(unmask_kbd_data, i8042_unmask_kbd_data, bool, 0600); MODULE_PARM_DESC(unmask_kbd_data, "Unconditional enable (may reveal sensitive data) of normally sanitize-filtered kbd data traffic debug log [pre-condition: i8042.debug=1 enabled]"); #endif +static bool i8042_present; static bool i8042_bypass_aux_irq_test; static char i8042_kbd_firmware_id[128]; static char i8042_aux_firmware_id[128]; +static struct fwnode_handle *i8042_kbd_fwnode; #include "i8042.h" @@ -140,7 +148,7 @@ static DEFINE_SPINLOCK(i8042_lock); /* * Writers to AUX and KBD ports as well as users issuing i8042_command * directly should acquire i8042_mutex (by means of calling - * i8042_lock_chip() and i8042_unlock_ship() helpers) to ensure that + * i8042_lock_chip() and i8042_unlock_chip() helpers) to ensure that * they do not disturb each other (unfortunately in many i8042 * implementations write to one of the ports will immediately abort * command that is being processed by another port). @@ -171,9 +179,9 @@ static unsigned char i8042_suppress_kbd_ack; static struct platform_device *i8042_platform_device; static struct notifier_block i8042_kbd_bind_notifier_block; -static irqreturn_t i8042_interrupt(int irq, void *dev_id); -static bool (*i8042_platform_filter)(unsigned char data, unsigned char str, - struct serio *serio); +static bool i8042_handle_data(int irq); +static i8042_filter_t i8042_platform_filter; +static void *i8042_platform_filter_context; void i8042_lock_chip(void) { @@ -187,45 +195,29 @@ void i8042_unlock_chip(void) } EXPORT_SYMBOL(i8042_unlock_chip); -int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str, - struct serio *serio)) +int i8042_install_filter(i8042_filter_t filter, void *context) { - unsigned long flags; - int ret = 0; + guard(spinlock_irqsave)(&i8042_lock); - spin_lock_irqsave(&i8042_lock, flags); - - if (i8042_platform_filter) { - ret = -EBUSY; - goto out; - } + if (i8042_platform_filter) + return -EBUSY; i8042_platform_filter = filter; - -out: - spin_unlock_irqrestore(&i8042_lock, flags); - return ret; + i8042_platform_filter_context = context; + return 0; } EXPORT_SYMBOL(i8042_install_filter); -int i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str, - struct serio *port)) +int i8042_remove_filter(i8042_filter_t filter) { - unsigned long flags; - int ret = 0; - - spin_lock_irqsave(&i8042_lock, flags); + guard(spinlock_irqsave)(&i8042_lock); - if (i8042_platform_filter != filter) { - ret = -EINVAL; - goto out; - } + if (i8042_platform_filter != filter) + return -EINVAL; i8042_platform_filter = NULL; - -out: - spin_unlock_irqrestore(&i8042_lock, flags); - return ret; + i8042_platform_filter_context = NULL; + return 0; } EXPORT_SYMBOL(i8042_remove_filter); @@ -264,28 +256,22 @@ static int i8042_wait_write(void) static int i8042_flush(void) { - unsigned long flags; unsigned char data, str; int count = 0; - int retval = 0; - spin_lock_irqsave(&i8042_lock, flags); + guard(spinlock_irqsave)(&i8042_lock); while ((str = i8042_read_status()) & I8042_STR_OBF) { - if (count++ < I8042_BUFFER_SIZE) { - udelay(50); - data = i8042_read_data(); - dbg("%02x <- i8042 (flush, %s)\n", - data, str & I8042_STR_AUXDATA ? "aux" : "kbd"); - } else { - retval = -EIO; - break; - } - } + if (count++ >= I8042_BUFFER_SIZE) + return -EIO; - spin_unlock_irqrestore(&i8042_lock, flags); + udelay(50); + data = i8042_read_data(); + dbg("%02x <- i8042 (flush, %s)\n", + data, str & I8042_STR_AUXDATA ? "aux" : "kbd"); + } - return retval; + return 0; } /* @@ -342,14 +328,12 @@ static int __i8042_command(unsigned char *param, int command) int i8042_command(unsigned char *param, int command) { - unsigned long flags; - int retval; + if (!i8042_present) + return -1; - spin_lock_irqsave(&i8042_lock, flags); - retval = __i8042_command(param, command); - spin_unlock_irqrestore(&i8042_lock, flags); + guard(spinlock_irqsave)(&i8042_lock); - return retval; + return __i8042_command(param, command); } EXPORT_SYMBOL(i8042_command); @@ -359,19 +343,18 @@ EXPORT_SYMBOL(i8042_command); static int i8042_kbd_write(struct serio *port, unsigned char c) { - unsigned long flags; - int retval = 0; + int error; - spin_lock_irqsave(&i8042_lock, flags); + guard(spinlock_irqsave)(&i8042_lock); - if (!(retval = i8042_wait_write())) { - dbg("%02x -> i8042 (kbd-data)\n", c); - i8042_write_data(c); - } + error = i8042_wait_write(); + if (error) + return error; - spin_unlock_irqrestore(&i8042_lock, flags); + dbg("%02x -> i8042 (kbd-data)\n", c); + i8042_write_data(c); - return retval; + return 0; } /* @@ -424,7 +407,7 @@ static void i8042_port_close(struct serio *serio) * See if there is any data appeared while we were messing with * port state. */ - i8042_interrupt(0, NULL); + i8042_handle_data(0); } /* @@ -436,9 +419,22 @@ static int i8042_start(struct serio *serio) { struct i8042_port *port = serio->port_data; - spin_lock_irq(&i8042_lock); + device_set_wakeup_capable(&serio->dev, true); + + /* + * On platforms using suspend-to-idle, allow the keyboard to + * wake up the system from sleep by enabling keyboard wakeups + * by default. This is consistent with keyboard wakeup + * behavior on many platforms using suspend-to-RAM (ACPI S3) + * by default. + */ + if (pm_suspend_default_s2idle() && + serio == i8042_ports[I8042_KBD_PORT_NO].serio) { + device_set_wakeup_enable(&serio->dev, true); + } + + guard(spinlock_irq)(&i8042_lock); port->exists = true; - spin_unlock_irq(&i8042_lock); return 0; } @@ -452,10 +448,10 @@ static void i8042_stop(struct serio *serio) { struct i8042_port *port = serio->port_data; - spin_lock_irq(&i8042_lock); - port->exists = false; - port->serio = NULL; - spin_unlock_irq(&i8042_lock); + scoped_guard(spinlock_irq, &i8042_lock) { + port->exists = false; + port->serio = NULL; + } /* * We need to make sure that interrupt handler finishes using @@ -485,7 +481,10 @@ static bool i8042_filter(unsigned char data, unsigned char str, } } - if (i8042_platform_filter && i8042_platform_filter(data, str, serio)) { + if (!i8042_platform_filter) + return false; + + if (i8042_platform_filter(data, str, serio, i8042_platform_filter_context)) { dbg("Filtered out by platform filter\n"); return true; } @@ -494,44 +493,10 @@ static bool i8042_filter(unsigned char data, unsigned char str, } /* - * i8042_interrupt() is the most important function in this driver - - * it handles the interrupts from the i8042, and sends incoming bytes - * to the upper layers. - */ - -static irqreturn_t i8042_interrupt(int irq, void *dev_id) -{ - struct i8042_port *port; - struct serio *serio; - unsigned long flags; - unsigned char str, data; - unsigned int dfl; - unsigned int port_no; - bool filtered; - int ret = 1; - - spin_lock_irqsave(&i8042_lock, flags); - - str = i8042_read_status(); - if (unlikely(~str & I8042_STR_OBF)) { - spin_unlock_irqrestore(&i8042_lock, flags); - if (irq) - dbg("Interrupt %d, without any data\n", irq); - ret = 0; - goto out; - } - - data = i8042_read_data(); - - if (i8042_mux_present && (str & I8042_STR_AUXDATA)) { - static unsigned long last_transmit; - static unsigned char last_str; - - dfl = 0; - if (str & I8042_STR_MUXERR) { - dbg("MUX error, status is %02x, data is %02x\n", - str, data); -/* + * i8042_handle_mux() handles case when data is coming from one of + * the multiplexed ports. It would be simple if not for quirks with + * handling errors: + * * When MUXERR condition is signalled the data register can only contain * 0xfd, 0xfe or 0xff if implementation follows the spec. Unfortunately * it is not always the case. Some KBCs also report 0xfc when there is @@ -543,53 +508,106 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id) * rest assume that the data came from the same serio last byte * was transmitted (if transmission happened not too long ago). */ - - switch (data) { - default: - if (time_before(jiffies, last_transmit + HZ/10)) { - str = last_str; - break; - } - /* fall through - report timeout */ - case 0xfc: - case 0xfd: - case 0xfe: dfl = SERIO_TIMEOUT; data = 0xfe; break; - case 0xff: dfl = SERIO_PARITY; data = 0xfe; break; +static int i8042_handle_mux(u8 str, u8 *data, unsigned int *dfl) +{ + static unsigned long last_transmit; + static unsigned long last_port; + unsigned int mux_port; + + mux_port = (str >> 6) & 3; + *dfl = 0; + + if (str & I8042_STR_MUXERR) { + dbg("MUX error, status is %02x, data is %02x\n", + str, *data); + + switch (*data) { + default: + if (time_before(jiffies, last_transmit + HZ/10)) { + mux_port = last_port; + break; } + fallthrough; /* report timeout */ + case 0xfc: + case 0xfd: + case 0xfe: + *dfl = SERIO_TIMEOUT; + *data = 0xfe; + break; + case 0xff: + *dfl = SERIO_PARITY; + *data = 0xfe; + break; } + } - port_no = I8042_MUX_PORT_NO + ((str >> 6) & 3); - last_str = str; - last_transmit = jiffies; - } else { + last_port = mux_port; + last_transmit = jiffies; - dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) | - ((str & I8042_STR_TIMEOUT && !i8042_notimeout) ? SERIO_TIMEOUT : 0); + return I8042_MUX_PORT_NO + mux_port; +} - port_no = (str & I8042_STR_AUXDATA) ? - I8042_AUX_PORT_NO : I8042_KBD_PORT_NO; - } +/* + * i8042_handle_data() is the most important function in this driver - + * it reads the data from the i8042, determines its destination serio + * port, and sends received byte to the upper layers. + * + * Returns true if there was data waiting, false otherwise. + */ +static bool i8042_handle_data(int irq) +{ + struct i8042_port *port; + struct serio *serio; + unsigned char str, data; + unsigned int dfl; + unsigned int port_no; + bool filtered; - port = &i8042_ports[port_no]; - serio = port->exists ? port->serio : NULL; + scoped_guard(spinlock_irqsave, &i8042_lock) { + str = i8042_read_status(); + if (unlikely(~str & I8042_STR_OBF)) + return false; - if (irq && serio) - pm_wakeup_event(&serio->dev, 0); + data = i8042_read_data(); - filter_dbg(port->driver_bound, data, "<- i8042 (interrupt, %d, %d%s%s)\n", - port_no, irq, - dfl & SERIO_PARITY ? ", bad parity" : "", - dfl & SERIO_TIMEOUT ? ", timeout" : ""); + if (i8042_mux_present && (str & I8042_STR_AUXDATA)) { + port_no = i8042_handle_mux(str, &data, &dfl); + } else { - filtered = i8042_filter(data, str, serio); + dfl = (str & I8042_STR_PARITY) ? SERIO_PARITY : 0; + if ((str & I8042_STR_TIMEOUT) && !i8042_notimeout) + dfl |= SERIO_TIMEOUT; - spin_unlock_irqrestore(&i8042_lock, flags); + port_no = (str & I8042_STR_AUXDATA) ? + I8042_AUX_PORT_NO : I8042_KBD_PORT_NO; + } + + port = &i8042_ports[port_no]; + serio = port->exists ? port->serio : NULL; + + filter_dbg(port->driver_bound, + data, "<- i8042 (interrupt, %d, %d%s%s)\n", + port_no, irq, + dfl & SERIO_PARITY ? ", bad parity" : "", + dfl & SERIO_TIMEOUT ? ", timeout" : ""); + + filtered = i8042_filter(data, str, serio); + } if (likely(serio && !filtered)) serio_interrupt(serio, data, dfl); - out: - return IRQ_RETVAL(ret); + return true; +} + +static irqreturn_t i8042_interrupt(int irq, void *dev_id) +{ + if (unlikely(!i8042_handle_data(irq))) { + dbg("Interrupt %d, without any data\n", irq); + return IRQ_NONE; + } + + return IRQ_HANDLED; } /* @@ -698,7 +716,7 @@ static int i8042_set_mux_mode(bool multiplex, unsigned char *mux_version) * LCS/Telegraphics. */ -static int __init i8042_check_mux(void) +static int i8042_check_mux(void) { unsigned char mux_version; @@ -727,29 +745,27 @@ static int __init i8042_check_mux(void) /* * The following is used to test AUX IRQ delivery. */ -static struct completion i8042_aux_irq_delivered __initdata; -static bool i8042_irq_being_tested __initdata; +static struct completion i8042_aux_irq_delivered; +static bool i8042_irq_being_tested; -static irqreturn_t __init i8042_aux_test_irq(int irq, void *dev_id) +static irqreturn_t i8042_aux_test_irq(int irq, void *dev_id) { - unsigned long flags; unsigned char str, data; - int ret = 0; - spin_lock_irqsave(&i8042_lock, flags); + guard(spinlock_irqsave)(&i8042_lock); + str = i8042_read_status(); - if (str & I8042_STR_OBF) { - data = i8042_read_data(); - dbg("%02x <- i8042 (aux_test_irq, %s)\n", - data, str & I8042_STR_AUXDATA ? "aux" : "kbd"); - if (i8042_irq_being_tested && - data == 0xa5 && (str & I8042_STR_AUXDATA)) - complete(&i8042_aux_irq_delivered); - ret = 1; - } - spin_unlock_irqrestore(&i8042_lock, flags); + if (!(str & I8042_STR_OBF)) + return IRQ_NONE; - return IRQ_RETVAL(ret); + data = i8042_read_data(); + dbg("%02x <- i8042 (aux_test_irq, %s)\n", + data, str & I8042_STR_AUXDATA ? "aux" : "kbd"); + + if (i8042_irq_being_tested && data == 0xa5 && (str & I8042_STR_AUXDATA)) + complete(&i8042_aux_irq_delivered); + + return IRQ_HANDLED; } /* @@ -757,7 +773,7 @@ static irqreturn_t __init i8042_aux_test_irq(int irq, void *dev_id) * verifies success by readinng CTR. Used when testing for presence of AUX * port. */ -static int __init i8042_toggle_aux(bool on) +static int i8042_toggle_aux(bool on) { unsigned char param; int i; @@ -785,12 +801,11 @@ static int __init i8042_toggle_aux(bool on) * the presence of an AUX interface. */ -static int __init i8042_check_aux(void) +static int i8042_check_aux(void) { int retval = -1; bool irq_registered = false; bool aux_loop_broken = false; - unsigned long flags; unsigned char param; /* @@ -874,18 +889,15 @@ static int __init i8042_check_aux(void) if (i8042_enable_aux_port()) goto out; - spin_lock_irqsave(&i8042_lock, flags); - - init_completion(&i8042_aux_irq_delivered); - i8042_irq_being_tested = true; + scoped_guard(spinlock_irqsave, &i8042_lock) { + init_completion(&i8042_aux_irq_delivered); + i8042_irq_being_tested = true; - param = 0xa5; - retval = __i8042_command(¶m, I8042_CMD_AUX_LOOP & 0xf0ff); - - spin_unlock_irqrestore(&i8042_lock, flags); - - if (retval) - goto out; + param = 0xa5; + retval = __i8042_command(¶m, I8042_CMD_AUX_LOOP & 0xf0ff); + if (retval) + goto out; + } if (wait_for_completion_timeout(&i8042_aux_irq_delivered, msecs_to_jiffies(250)) == 0) { @@ -966,14 +978,13 @@ static int i8042_controller_selftest(void) } /* - * i8042_controller init initializes the i8042 controller, and, + * i8042_controller_init initializes the i8042 controller, and, * most importantly, sets it into non-xlated mode if that's * desired. */ static int i8042_controller_init(void) { - unsigned long flags; int n = 0; unsigned char ctr[2]; @@ -992,7 +1003,7 @@ static int i8042_controller_init(void) if (i8042_command(&ctr[n++ % 2], I8042_CMD_CTL_RCTR)) { pr_err("Can't read CTR while initializing i8042\n"); - return -EIO; + return i8042_probe_defer ? -EPROBE_DEFER : -EIO; } } while (n < 2 || ctr[0] != ctr[1]); @@ -1010,14 +1021,14 @@ static int i8042_controller_init(void) * Handle keylock. */ - spin_lock_irqsave(&i8042_lock, flags); - if (~i8042_read_status() & I8042_STR_KEYLOCK) { - if (i8042_unlock) - i8042_ctr |= I8042_CTR_IGNKEYLOCK; - else - pr_warn("Warning: Keylock active\n"); + scoped_guard(spinlock_irqsave, &i8042_lock) { + if (~i8042_read_status() & I8042_STR_KEYLOCK) { + if (i8042_unlock) + i8042_ctr |= I8042_CTR_IGNKEYLOCK; + else + pr_warn("Warning: Keylock active\n"); + } } - spin_unlock_irqrestore(&i8042_lock, flags); /* * If the chip is configured into nontranslated mode by the BIOS, don't @@ -1195,13 +1206,14 @@ static int i8042_controller_resume(bool s2r_wants_reset) if (i8042_mux_present) { if (i8042_set_mux_mode(true, NULL) || i8042_enable_mux_ports()) pr_warn("failed to resume active multiplexor, mouse won't work\n"); - } else if (i8042_ports[I8042_AUX_PORT_NO].serio) + } else if (i8042_ports[I8042_AUX_PORT_NO].serio) { i8042_enable_aux_port(); + } if (i8042_ports[I8042_KBD_PORT_NO].serio) i8042_enable_kbd_port(); - i8042_interrupt(0, NULL); + i8042_handle_data(0); return 0; } @@ -1215,7 +1227,7 @@ static int i8042_pm_suspend(struct device *dev) { int i; - if (pm_suspend_via_firmware()) + if (!i8042_forcenorestore && pm_suspend_via_firmware()) i8042_controller_reset(true); /* Set up serio interrupts for system wakeup. */ @@ -1231,8 +1243,8 @@ static int i8042_pm_suspend(struct device *dev) static int i8042_pm_resume_noirq(struct device *dev) { - if (!pm_resume_via_firmware()) - i8042_interrupt(0, NULL); + if (i8042_forcenorestore || !pm_resume_via_firmware()) + i8042_handle_data(0); return 0; } @@ -1254,7 +1266,7 @@ static int i8042_pm_resume(struct device *dev) * not restore the controller state to whatever it had been at boot * time, so we do not need to do anything. */ - if (!pm_suspend_via_firmware()) + if (i8042_forcenorestore || !pm_suspend_via_firmware()) return 0; /* @@ -1269,7 +1281,7 @@ static int i8042_pm_resume(struct device *dev) static int i8042_pm_thaw(struct device *dev) { - i8042_interrupt(0, NULL); + i8042_handle_data(0); return 0; } @@ -1307,12 +1319,12 @@ static void i8042_shutdown(struct platform_device *dev) i8042_controller_reset(false); } -static int __init i8042_create_kbd_port(void) +static int i8042_create_kbd_port(void) { struct serio *serio; struct i8042_port *port = &i8042_ports[I8042_KBD_PORT_NO]; - serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + serio = kzalloc(sizeof(*serio), GFP_KERNEL); if (!serio) return -ENOMEM; @@ -1324,10 +1336,11 @@ static int __init i8042_create_kbd_port(void) serio->ps2_cmd_mutex = &i8042_mutex; serio->port_data = port; serio->dev.parent = &i8042_platform_device->dev; - strlcpy(serio->name, "i8042 KBD port", sizeof(serio->name)); - strlcpy(serio->phys, I8042_KBD_PHYS_DESC, sizeof(serio->phys)); - strlcpy(serio->firmware_id, i8042_kbd_firmware_id, + strscpy(serio->name, "i8042 KBD port", sizeof(serio->name)); + strscpy(serio->phys, I8042_KBD_PHYS_DESC, sizeof(serio->phys)); + strscpy(serio->firmware_id, i8042_kbd_firmware_id, sizeof(serio->firmware_id)); + set_primary_fwnode(&serio->dev, i8042_kbd_fwnode); port->serio = serio; port->irq = I8042_KBD_IRQ; @@ -1335,13 +1348,13 @@ static int __init i8042_create_kbd_port(void) return 0; } -static int __init i8042_create_aux_port(int idx) +static int i8042_create_aux_port(int idx) { struct serio *serio; int port_no = idx < 0 ? I8042_AUX_PORT_NO : I8042_MUX_PORT_NO + idx; struct i8042_port *port = &i8042_ports[port_no]; - serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + serio = kzalloc(sizeof(*serio), GFP_KERNEL); if (!serio) return -ENOMEM; @@ -1353,15 +1366,15 @@ static int __init i8042_create_aux_port(int idx) serio->port_data = port; serio->dev.parent = &i8042_platform_device->dev; if (idx < 0) { - strlcpy(serio->name, "i8042 AUX port", sizeof(serio->name)); - strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys)); - strlcpy(serio->firmware_id, i8042_aux_firmware_id, + strscpy(serio->name, "i8042 AUX port", sizeof(serio->name)); + strscpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys)); + strscpy(serio->firmware_id, i8042_aux_firmware_id, sizeof(serio->firmware_id)); serio->close = i8042_port_close; } else { snprintf(serio->name, sizeof(serio->name), "i8042 AUX%d port", idx); snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, idx + 1); - strlcpy(serio->firmware_id, i8042_aux_firmware_id, + strscpy(serio->firmware_id, i8042_aux_firmware_id, sizeof(serio->firmware_id)); } @@ -1372,13 +1385,13 @@ static int __init i8042_create_aux_port(int idx) return 0; } -static void __init i8042_free_kbd_port(void) +static void i8042_free_kbd_port(void) { kfree(i8042_ports[I8042_KBD_PORT_NO].serio); i8042_ports[I8042_KBD_PORT_NO].serio = NULL; } -static void __init i8042_free_aux_ports(void) +static void i8042_free_aux_ports(void) { int i; @@ -1388,7 +1401,7 @@ static void __init i8042_free_aux_ports(void) } } -static void __init i8042_register_ports(void) +static void i8042_register_ports(void) { int i; @@ -1404,17 +1417,6 @@ static void __init i8042_register_ports(void) (unsigned long) I8042_COMMAND_REG, i8042_ports[i].irq); serio_register_port(serio); - device_set_wakeup_capable(&serio->dev, true); - - /* - * On platforms using suspend-to-idle, allow the keyboard to - * wake up the system from sleep by enabling keyboard wakeups - * by default. This is consistent with keyboard wakeup - * behavior on many platforms using suspend-to-RAM (ACPI S3) - * by default. - */ - if (pm_suspend_via_s2idle() && i == I8042_KBD_PORT_NO) - device_set_wakeup_enable(&serio->dev, true); } } @@ -1440,7 +1442,7 @@ static void i8042_free_irqs(void) i8042_aux_irq_registered = i8042_kbd_irq_registered = false; } -static int __init i8042_setup_aux(void) +static int i8042_setup_aux(void) { int (*aux_enable)(void); int error; @@ -1468,7 +1470,8 @@ static int __init i8042_setup_aux(void) if (error) goto err_free_ports; - if (aux_enable()) + error = aux_enable(); + if (error) goto err_free_irq; i8042_aux_irq_registered = true; @@ -1481,7 +1484,7 @@ static int __init i8042_setup_aux(void) return error; } -static int __init i8042_setup_kbd(void) +static int i8042_setup_kbd(void) { int error; @@ -1531,12 +1534,10 @@ static int i8042_kbd_bind_notifier(struct notifier_block *nb, return 0; } -static int __init i8042_probe(struct platform_device *dev) +static int i8042_probe(struct platform_device *dev) { int error; - i8042_platform_device = dev; - if (i8042_reset == I8042_RESET_ALWAYS) { error = i8042_controller_selftest(); if (error) @@ -1574,19 +1575,15 @@ static int __init i8042_probe(struct platform_device *dev) i8042_free_aux_ports(); /* in case KBD failed but AUX not */ i8042_free_irqs(); i8042_controller_reset(false); - i8042_platform_device = NULL; return error; } -static int i8042_remove(struct platform_device *dev) +static void i8042_remove(struct platform_device *dev) { i8042_unregister_ports(); i8042_free_irqs(); i8042_controller_reset(false); - i8042_platform_device = NULL; - - return 0; } static struct platform_driver i8042_driver = { @@ -1596,6 +1593,7 @@ static struct platform_driver i8042_driver = { .pm = &i8042_pm_ops, #endif }, + .probe = i8042_probe, .remove = i8042_remove, .shutdown = i8042_shutdown, }; @@ -1606,30 +1604,44 @@ static struct notifier_block i8042_kbd_bind_notifier_block = { static int __init i8042_init(void) { - struct platform_device *pdev; int err; dbg_init(); err = i8042_platform_init(); if (err) - return err; + return (err == -ENODEV) ? 0 : err; err = i8042_controller_check(); if (err) goto err_platform_exit; - pdev = platform_create_bundle(&i8042_driver, i8042_probe, NULL, 0, NULL, 0); - if (IS_ERR(pdev)) { - err = PTR_ERR(pdev); + /* Set this before creating the dev to allow i8042_command to work right away */ + i8042_present = true; + + err = platform_driver_register(&i8042_driver); + if (err) goto err_platform_exit; + + i8042_platform_device = platform_device_alloc("i8042", -1); + if (!i8042_platform_device) { + err = -ENOMEM; + goto err_unregister_driver; } + err = platform_device_add(i8042_platform_device); + if (err) + goto err_free_device; + bus_register_notifier(&serio_bus, &i8042_kbd_bind_notifier_block); panic_blink = i8042_panic_blink; return 0; +err_free_device: + platform_device_put(i8042_platform_device); +err_unregister_driver: + platform_driver_unregister(&i8042_driver); err_platform_exit: i8042_platform_exit(); return err; @@ -1637,6 +1649,9 @@ static int __init i8042_init(void) static void __exit i8042_exit(void) { + if (!i8042_present) + return; + platform_device_unregister(i8042_platform_device); platform_driver_unregister(&i8042_driver); i8042_platform_exit(); |
