162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * i8042 keyboard and mouse controller driver for Linux 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 1999-2004 Vojtech Pavlik 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/types.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/ioport.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/serio.h> 1862306a36Sopenharmony_ci#include <linux/err.h> 1962306a36Sopenharmony_ci#include <linux/rcupdate.h> 2062306a36Sopenharmony_ci#include <linux/platform_device.h> 2162306a36Sopenharmony_ci#include <linux/i8042.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/suspend.h> 2462306a36Sopenharmony_ci#include <linux/property.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <asm/io.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ciMODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); 2962306a36Sopenharmony_ciMODULE_DESCRIPTION("i8042 keyboard and mouse controller driver"); 3062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic bool i8042_nokbd; 3362306a36Sopenharmony_cimodule_param_named(nokbd, i8042_nokbd, bool, 0); 3462306a36Sopenharmony_ciMODULE_PARM_DESC(nokbd, "Do not probe or use KBD port."); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic bool i8042_noaux; 3762306a36Sopenharmony_cimodule_param_named(noaux, i8042_noaux, bool, 0); 3862306a36Sopenharmony_ciMODULE_PARM_DESC(noaux, "Do not probe or use AUX (mouse) port."); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic bool i8042_nomux; 4162306a36Sopenharmony_cimodule_param_named(nomux, i8042_nomux, bool, 0); 4262306a36Sopenharmony_ciMODULE_PARM_DESC(nomux, "Do not check whether an active multiplexing controller is present."); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic bool i8042_unlock; 4562306a36Sopenharmony_cimodule_param_named(unlock, i8042_unlock, bool, 0); 4662306a36Sopenharmony_ciMODULE_PARM_DESC(unlock, "Ignore keyboard lock."); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic bool i8042_probe_defer; 4962306a36Sopenharmony_cimodule_param_named(probe_defer, i8042_probe_defer, bool, 0); 5062306a36Sopenharmony_ciMODULE_PARM_DESC(probe_defer, "Allow deferred probing."); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cienum i8042_controller_reset_mode { 5362306a36Sopenharmony_ci I8042_RESET_NEVER, 5462306a36Sopenharmony_ci I8042_RESET_ALWAYS, 5562306a36Sopenharmony_ci I8042_RESET_ON_S2RAM, 5662306a36Sopenharmony_ci#define I8042_RESET_DEFAULT I8042_RESET_ON_S2RAM 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_cistatic enum i8042_controller_reset_mode i8042_reset = I8042_RESET_DEFAULT; 5962306a36Sopenharmony_cistatic int i8042_set_reset(const char *val, const struct kernel_param *kp) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci enum i8042_controller_reset_mode *arg = kp->arg; 6262306a36Sopenharmony_ci int error; 6362306a36Sopenharmony_ci bool reset; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (val) { 6662306a36Sopenharmony_ci error = kstrtobool(val, &reset); 6762306a36Sopenharmony_ci if (error) 6862306a36Sopenharmony_ci return error; 6962306a36Sopenharmony_ci } else { 7062306a36Sopenharmony_ci reset = true; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci *arg = reset ? I8042_RESET_ALWAYS : I8042_RESET_NEVER; 7462306a36Sopenharmony_ci return 0; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic const struct kernel_param_ops param_ops_reset_param = { 7862306a36Sopenharmony_ci .flags = KERNEL_PARAM_OPS_FL_NOARG, 7962306a36Sopenharmony_ci .set = i8042_set_reset, 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci#define param_check_reset_param(name, p) \ 8262306a36Sopenharmony_ci __param_check(name, p, enum i8042_controller_reset_mode) 8362306a36Sopenharmony_cimodule_param_named(reset, i8042_reset, reset_param, 0); 8462306a36Sopenharmony_ciMODULE_PARM_DESC(reset, "Reset controller on resume, cleanup or both"); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic bool i8042_direct; 8762306a36Sopenharmony_cimodule_param_named(direct, i8042_direct, bool, 0); 8862306a36Sopenharmony_ciMODULE_PARM_DESC(direct, "Put keyboard port into non-translated mode."); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic bool i8042_dumbkbd; 9162306a36Sopenharmony_cimodule_param_named(dumbkbd, i8042_dumbkbd, bool, 0); 9262306a36Sopenharmony_ciMODULE_PARM_DESC(dumbkbd, "Pretend that controller can only read data from keyboard"); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic bool i8042_noloop; 9562306a36Sopenharmony_cimodule_param_named(noloop, i8042_noloop, bool, 0); 9662306a36Sopenharmony_ciMODULE_PARM_DESC(noloop, "Disable the AUX Loopback command while probing for the AUX port"); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic bool i8042_notimeout; 9962306a36Sopenharmony_cimodule_param_named(notimeout, i8042_notimeout, bool, 0); 10062306a36Sopenharmony_ciMODULE_PARM_DESC(notimeout, "Ignore timeouts signalled by i8042"); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic bool i8042_kbdreset; 10362306a36Sopenharmony_cimodule_param_named(kbdreset, i8042_kbdreset, bool, 0); 10462306a36Sopenharmony_ciMODULE_PARM_DESC(kbdreset, "Reset device connected to KBD port"); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci#ifdef CONFIG_X86 10762306a36Sopenharmony_cistatic bool i8042_dritek; 10862306a36Sopenharmony_cimodule_param_named(dritek, i8042_dritek, bool, 0); 10962306a36Sopenharmony_ciMODULE_PARM_DESC(dritek, "Force enable the Dritek keyboard extension"); 11062306a36Sopenharmony_ci#endif 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci#ifdef CONFIG_PNP 11362306a36Sopenharmony_cistatic bool i8042_nopnp; 11462306a36Sopenharmony_cimodule_param_named(nopnp, i8042_nopnp, bool, 0); 11562306a36Sopenharmony_ciMODULE_PARM_DESC(nopnp, "Do not use PNP to detect controller settings"); 11662306a36Sopenharmony_ci#endif 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci#define DEBUG 11962306a36Sopenharmony_ci#ifdef DEBUG 12062306a36Sopenharmony_cistatic bool i8042_debug; 12162306a36Sopenharmony_cimodule_param_named(debug, i8042_debug, bool, 0600); 12262306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "Turn i8042 debugging mode on and off"); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic bool i8042_unmask_kbd_data; 12562306a36Sopenharmony_cimodule_param_named(unmask_kbd_data, i8042_unmask_kbd_data, bool, 0600); 12662306a36Sopenharmony_ciMODULE_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]"); 12762306a36Sopenharmony_ci#endif 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic bool i8042_present; 13062306a36Sopenharmony_cistatic bool i8042_bypass_aux_irq_test; 13162306a36Sopenharmony_cistatic char i8042_kbd_firmware_id[128]; 13262306a36Sopenharmony_cistatic char i8042_aux_firmware_id[128]; 13362306a36Sopenharmony_cistatic struct fwnode_handle *i8042_kbd_fwnode; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci#include "i8042.h" 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* 13862306a36Sopenharmony_ci * i8042_lock protects serialization between i8042_command and 13962306a36Sopenharmony_ci * the interrupt handler. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_cistatic DEFINE_SPINLOCK(i8042_lock); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/* 14462306a36Sopenharmony_ci * Writers to AUX and KBD ports as well as users issuing i8042_command 14562306a36Sopenharmony_ci * directly should acquire i8042_mutex (by means of calling 14662306a36Sopenharmony_ci * i8042_lock_chip() and i8042_unlock_chip() helpers) to ensure that 14762306a36Sopenharmony_ci * they do not disturb each other (unfortunately in many i8042 14862306a36Sopenharmony_ci * implementations write to one of the ports will immediately abort 14962306a36Sopenharmony_ci * command that is being processed by another port). 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_cistatic DEFINE_MUTEX(i8042_mutex); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistruct i8042_port { 15462306a36Sopenharmony_ci struct serio *serio; 15562306a36Sopenharmony_ci int irq; 15662306a36Sopenharmony_ci bool exists; 15762306a36Sopenharmony_ci bool driver_bound; 15862306a36Sopenharmony_ci signed char mux; 15962306a36Sopenharmony_ci}; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci#define I8042_KBD_PORT_NO 0 16262306a36Sopenharmony_ci#define I8042_AUX_PORT_NO 1 16362306a36Sopenharmony_ci#define I8042_MUX_PORT_NO 2 16462306a36Sopenharmony_ci#define I8042_NUM_PORTS (I8042_NUM_MUX_PORTS + 2) 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic struct i8042_port i8042_ports[I8042_NUM_PORTS]; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic unsigned char i8042_initial_ctr; 16962306a36Sopenharmony_cistatic unsigned char i8042_ctr; 17062306a36Sopenharmony_cistatic bool i8042_mux_present; 17162306a36Sopenharmony_cistatic bool i8042_kbd_irq_registered; 17262306a36Sopenharmony_cistatic bool i8042_aux_irq_registered; 17362306a36Sopenharmony_cistatic unsigned char i8042_suppress_kbd_ack; 17462306a36Sopenharmony_cistatic struct platform_device *i8042_platform_device; 17562306a36Sopenharmony_cistatic struct notifier_block i8042_kbd_bind_notifier_block; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic irqreturn_t i8042_interrupt(int irq, void *dev_id); 17862306a36Sopenharmony_cistatic bool (*i8042_platform_filter)(unsigned char data, unsigned char str, 17962306a36Sopenharmony_ci struct serio *serio); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_civoid i8042_lock_chip(void) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci mutex_lock(&i8042_mutex); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ciEXPORT_SYMBOL(i8042_lock_chip); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_civoid i8042_unlock_chip(void) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci mutex_unlock(&i8042_mutex); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ciEXPORT_SYMBOL(i8042_unlock_chip); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ciint i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str, 19462306a36Sopenharmony_ci struct serio *serio)) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci unsigned long flags; 19762306a36Sopenharmony_ci int ret = 0; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci spin_lock_irqsave(&i8042_lock, flags); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (i8042_platform_filter) { 20262306a36Sopenharmony_ci ret = -EBUSY; 20362306a36Sopenharmony_ci goto out; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci i8042_platform_filter = filter; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ciout: 20962306a36Sopenharmony_ci spin_unlock_irqrestore(&i8042_lock, flags); 21062306a36Sopenharmony_ci return ret; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ciEXPORT_SYMBOL(i8042_install_filter); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ciint i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str, 21562306a36Sopenharmony_ci struct serio *port)) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci unsigned long flags; 21862306a36Sopenharmony_ci int ret = 0; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci spin_lock_irqsave(&i8042_lock, flags); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (i8042_platform_filter != filter) { 22362306a36Sopenharmony_ci ret = -EINVAL; 22462306a36Sopenharmony_ci goto out; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci i8042_platform_filter = NULL; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ciout: 23062306a36Sopenharmony_ci spin_unlock_irqrestore(&i8042_lock, flags); 23162306a36Sopenharmony_ci return ret; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ciEXPORT_SYMBOL(i8042_remove_filter); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci/* 23662306a36Sopenharmony_ci * The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to 23762306a36Sopenharmony_ci * be ready for reading values from it / writing values to it. 23862306a36Sopenharmony_ci * Called always with i8042_lock held. 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic int i8042_wait_read(void) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci int i = 0; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci while ((~i8042_read_status() & I8042_STR_OBF) && (i < I8042_CTL_TIMEOUT)) { 24662306a36Sopenharmony_ci udelay(50); 24762306a36Sopenharmony_ci i++; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci return -(i == I8042_CTL_TIMEOUT); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic int i8042_wait_write(void) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci int i = 0; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci while ((i8042_read_status() & I8042_STR_IBF) && (i < I8042_CTL_TIMEOUT)) { 25762306a36Sopenharmony_ci udelay(50); 25862306a36Sopenharmony_ci i++; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci return -(i == I8042_CTL_TIMEOUT); 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci/* 26462306a36Sopenharmony_ci * i8042_flush() flushes all data that may be in the keyboard and mouse buffers 26562306a36Sopenharmony_ci * of the i8042 down the toilet. 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic int i8042_flush(void) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci unsigned long flags; 27162306a36Sopenharmony_ci unsigned char data, str; 27262306a36Sopenharmony_ci int count = 0; 27362306a36Sopenharmony_ci int retval = 0; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci spin_lock_irqsave(&i8042_lock, flags); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci while ((str = i8042_read_status()) & I8042_STR_OBF) { 27862306a36Sopenharmony_ci if (count++ < I8042_BUFFER_SIZE) { 27962306a36Sopenharmony_ci udelay(50); 28062306a36Sopenharmony_ci data = i8042_read_data(); 28162306a36Sopenharmony_ci dbg("%02x <- i8042 (flush, %s)\n", 28262306a36Sopenharmony_ci data, str & I8042_STR_AUXDATA ? "aux" : "kbd"); 28362306a36Sopenharmony_ci } else { 28462306a36Sopenharmony_ci retval = -EIO; 28562306a36Sopenharmony_ci break; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci spin_unlock_irqrestore(&i8042_lock, flags); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return retval; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci/* 29562306a36Sopenharmony_ci * i8042_command() executes a command on the i8042. It also sends the input 29662306a36Sopenharmony_ci * parameter(s) of the commands to it, and receives the output value(s). The 29762306a36Sopenharmony_ci * parameters are to be stored in the param array, and the output is placed 29862306a36Sopenharmony_ci * into the same array. The number of the parameters and output values is 29962306a36Sopenharmony_ci * encoded in bits 8-11 of the command number. 30062306a36Sopenharmony_ci */ 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic int __i8042_command(unsigned char *param, int command) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci int i, error; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (i8042_noloop && command == I8042_CMD_AUX_LOOP) 30762306a36Sopenharmony_ci return -1; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci error = i8042_wait_write(); 31062306a36Sopenharmony_ci if (error) 31162306a36Sopenharmony_ci return error; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci dbg("%02x -> i8042 (command)\n", command & 0xff); 31462306a36Sopenharmony_ci i8042_write_command(command & 0xff); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci for (i = 0; i < ((command >> 12) & 0xf); i++) { 31762306a36Sopenharmony_ci error = i8042_wait_write(); 31862306a36Sopenharmony_ci if (error) { 31962306a36Sopenharmony_ci dbg(" -- i8042 (wait write timeout)\n"); 32062306a36Sopenharmony_ci return error; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci dbg("%02x -> i8042 (parameter)\n", param[i]); 32362306a36Sopenharmony_ci i8042_write_data(param[i]); 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci for (i = 0; i < ((command >> 8) & 0xf); i++) { 32762306a36Sopenharmony_ci error = i8042_wait_read(); 32862306a36Sopenharmony_ci if (error) { 32962306a36Sopenharmony_ci dbg(" -- i8042 (wait read timeout)\n"); 33062306a36Sopenharmony_ci return error; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (command == I8042_CMD_AUX_LOOP && 33462306a36Sopenharmony_ci !(i8042_read_status() & I8042_STR_AUXDATA)) { 33562306a36Sopenharmony_ci dbg(" -- i8042 (auxerr)\n"); 33662306a36Sopenharmony_ci return -1; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci param[i] = i8042_read_data(); 34062306a36Sopenharmony_ci dbg("%02x <- i8042 (return)\n", param[i]); 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci return 0; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ciint i8042_command(unsigned char *param, int command) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci unsigned long flags; 34962306a36Sopenharmony_ci int retval; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (!i8042_present) 35262306a36Sopenharmony_ci return -1; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci spin_lock_irqsave(&i8042_lock, flags); 35562306a36Sopenharmony_ci retval = __i8042_command(param, command); 35662306a36Sopenharmony_ci spin_unlock_irqrestore(&i8042_lock, flags); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci return retval; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ciEXPORT_SYMBOL(i8042_command); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci/* 36362306a36Sopenharmony_ci * i8042_kbd_write() sends a byte out through the keyboard interface. 36462306a36Sopenharmony_ci */ 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic int i8042_kbd_write(struct serio *port, unsigned char c) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci unsigned long flags; 36962306a36Sopenharmony_ci int retval = 0; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci spin_lock_irqsave(&i8042_lock, flags); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (!(retval = i8042_wait_write())) { 37462306a36Sopenharmony_ci dbg("%02x -> i8042 (kbd-data)\n", c); 37562306a36Sopenharmony_ci i8042_write_data(c); 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci spin_unlock_irqrestore(&i8042_lock, flags); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci return retval; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci/* 38462306a36Sopenharmony_ci * i8042_aux_write() sends a byte out through the aux interface. 38562306a36Sopenharmony_ci */ 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic int i8042_aux_write(struct serio *serio, unsigned char c) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci struct i8042_port *port = serio->port_data; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci return i8042_command(&c, port->mux == -1 ? 39262306a36Sopenharmony_ci I8042_CMD_AUX_SEND : 39362306a36Sopenharmony_ci I8042_CMD_MUX_SEND + port->mux); 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci/* 39862306a36Sopenharmony_ci * i8042_port_close attempts to clear AUX or KBD port state by disabling 39962306a36Sopenharmony_ci * and then re-enabling it. 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic void i8042_port_close(struct serio *serio) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci int irq_bit; 40562306a36Sopenharmony_ci int disable_bit; 40662306a36Sopenharmony_ci const char *port_name; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (serio == i8042_ports[I8042_AUX_PORT_NO].serio) { 40962306a36Sopenharmony_ci irq_bit = I8042_CTR_AUXINT; 41062306a36Sopenharmony_ci disable_bit = I8042_CTR_AUXDIS; 41162306a36Sopenharmony_ci port_name = "AUX"; 41262306a36Sopenharmony_ci } else { 41362306a36Sopenharmony_ci irq_bit = I8042_CTR_KBDINT; 41462306a36Sopenharmony_ci disable_bit = I8042_CTR_KBDDIS; 41562306a36Sopenharmony_ci port_name = "KBD"; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci i8042_ctr &= ~irq_bit; 41962306a36Sopenharmony_ci if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) 42062306a36Sopenharmony_ci pr_warn("Can't write CTR while closing %s port\n", port_name); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci udelay(50); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci i8042_ctr &= ~disable_bit; 42562306a36Sopenharmony_ci i8042_ctr |= irq_bit; 42662306a36Sopenharmony_ci if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) 42762306a36Sopenharmony_ci pr_err("Can't reactivate %s port\n", port_name); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* 43062306a36Sopenharmony_ci * See if there is any data appeared while we were messing with 43162306a36Sopenharmony_ci * port state. 43262306a36Sopenharmony_ci */ 43362306a36Sopenharmony_ci i8042_interrupt(0, NULL); 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci/* 43762306a36Sopenharmony_ci * i8042_start() is called by serio core when port is about to finish 43862306a36Sopenharmony_ci * registering. It will mark port as existing so i8042_interrupt can 43962306a36Sopenharmony_ci * start sending data through it. 44062306a36Sopenharmony_ci */ 44162306a36Sopenharmony_cistatic int i8042_start(struct serio *serio) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci struct i8042_port *port = serio->port_data; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci device_set_wakeup_capable(&serio->dev, true); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* 44862306a36Sopenharmony_ci * On platforms using suspend-to-idle, allow the keyboard to 44962306a36Sopenharmony_ci * wake up the system from sleep by enabling keyboard wakeups 45062306a36Sopenharmony_ci * by default. This is consistent with keyboard wakeup 45162306a36Sopenharmony_ci * behavior on many platforms using suspend-to-RAM (ACPI S3) 45262306a36Sopenharmony_ci * by default. 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_ci if (pm_suspend_default_s2idle() && 45562306a36Sopenharmony_ci serio == i8042_ports[I8042_KBD_PORT_NO].serio) { 45662306a36Sopenharmony_ci device_set_wakeup_enable(&serio->dev, true); 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci spin_lock_irq(&i8042_lock); 46062306a36Sopenharmony_ci port->exists = true; 46162306a36Sopenharmony_ci spin_unlock_irq(&i8042_lock); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci/* 46762306a36Sopenharmony_ci * i8042_stop() marks serio port as non-existing so i8042_interrupt 46862306a36Sopenharmony_ci * will not try to send data to the port that is about to go away. 46962306a36Sopenharmony_ci * The function is called by serio core as part of unregister procedure. 47062306a36Sopenharmony_ci */ 47162306a36Sopenharmony_cistatic void i8042_stop(struct serio *serio) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci struct i8042_port *port = serio->port_data; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci spin_lock_irq(&i8042_lock); 47662306a36Sopenharmony_ci port->exists = false; 47762306a36Sopenharmony_ci port->serio = NULL; 47862306a36Sopenharmony_ci spin_unlock_irq(&i8042_lock); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* 48162306a36Sopenharmony_ci * We need to make sure that interrupt handler finishes using 48262306a36Sopenharmony_ci * our serio port before we return from this function. 48362306a36Sopenharmony_ci * We synchronize with both AUX and KBD IRQs because there is 48462306a36Sopenharmony_ci * a (very unlikely) chance that AUX IRQ is raised for KBD port 48562306a36Sopenharmony_ci * and vice versa. 48662306a36Sopenharmony_ci */ 48762306a36Sopenharmony_ci synchronize_irq(I8042_AUX_IRQ); 48862306a36Sopenharmony_ci synchronize_irq(I8042_KBD_IRQ); 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci/* 49262306a36Sopenharmony_ci * i8042_filter() filters out unwanted bytes from the input data stream. 49362306a36Sopenharmony_ci * It is called from i8042_interrupt and thus is running with interrupts 49462306a36Sopenharmony_ci * off and i8042_lock held. 49562306a36Sopenharmony_ci */ 49662306a36Sopenharmony_cistatic bool i8042_filter(unsigned char data, unsigned char str, 49762306a36Sopenharmony_ci struct serio *serio) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci if (unlikely(i8042_suppress_kbd_ack)) { 50062306a36Sopenharmony_ci if ((~str & I8042_STR_AUXDATA) && 50162306a36Sopenharmony_ci (data == 0xfa || data == 0xfe)) { 50262306a36Sopenharmony_ci i8042_suppress_kbd_ack--; 50362306a36Sopenharmony_ci dbg("Extra keyboard ACK - filtered out\n"); 50462306a36Sopenharmony_ci return true; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (i8042_platform_filter && i8042_platform_filter(data, str, serio)) { 50962306a36Sopenharmony_ci dbg("Filtered out by platform filter\n"); 51062306a36Sopenharmony_ci return true; 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci return false; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci/* 51762306a36Sopenharmony_ci * i8042_interrupt() is the most important function in this driver - 51862306a36Sopenharmony_ci * it handles the interrupts from the i8042, and sends incoming bytes 51962306a36Sopenharmony_ci * to the upper layers. 52062306a36Sopenharmony_ci */ 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic irqreturn_t i8042_interrupt(int irq, void *dev_id) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci struct i8042_port *port; 52562306a36Sopenharmony_ci struct serio *serio; 52662306a36Sopenharmony_ci unsigned long flags; 52762306a36Sopenharmony_ci unsigned char str, data; 52862306a36Sopenharmony_ci unsigned int dfl; 52962306a36Sopenharmony_ci unsigned int port_no; 53062306a36Sopenharmony_ci bool filtered; 53162306a36Sopenharmony_ci int ret = 1; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci spin_lock_irqsave(&i8042_lock, flags); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci str = i8042_read_status(); 53662306a36Sopenharmony_ci if (unlikely(~str & I8042_STR_OBF)) { 53762306a36Sopenharmony_ci spin_unlock_irqrestore(&i8042_lock, flags); 53862306a36Sopenharmony_ci if (irq) 53962306a36Sopenharmony_ci dbg("Interrupt %d, without any data\n", irq); 54062306a36Sopenharmony_ci ret = 0; 54162306a36Sopenharmony_ci goto out; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci data = i8042_read_data(); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (i8042_mux_present && (str & I8042_STR_AUXDATA)) { 54762306a36Sopenharmony_ci static unsigned long last_transmit; 54862306a36Sopenharmony_ci static unsigned char last_str; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci dfl = 0; 55162306a36Sopenharmony_ci if (str & I8042_STR_MUXERR) { 55262306a36Sopenharmony_ci dbg("MUX error, status is %02x, data is %02x\n", 55362306a36Sopenharmony_ci str, data); 55462306a36Sopenharmony_ci/* 55562306a36Sopenharmony_ci * When MUXERR condition is signalled the data register can only contain 55662306a36Sopenharmony_ci * 0xfd, 0xfe or 0xff if implementation follows the spec. Unfortunately 55762306a36Sopenharmony_ci * it is not always the case. Some KBCs also report 0xfc when there is 55862306a36Sopenharmony_ci * nothing connected to the port while others sometimes get confused which 55962306a36Sopenharmony_ci * port the data came from and signal error leaving the data intact. They 56062306a36Sopenharmony_ci * _do not_ revert to legacy mode (actually I've never seen KBC reverting 56162306a36Sopenharmony_ci * to legacy mode yet, when we see one we'll add proper handling). 56262306a36Sopenharmony_ci * Anyway, we process 0xfc, 0xfd, 0xfe and 0xff as timeouts, and for the 56362306a36Sopenharmony_ci * rest assume that the data came from the same serio last byte 56462306a36Sopenharmony_ci * was transmitted (if transmission happened not too long ago). 56562306a36Sopenharmony_ci */ 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci switch (data) { 56862306a36Sopenharmony_ci default: 56962306a36Sopenharmony_ci if (time_before(jiffies, last_transmit + HZ/10)) { 57062306a36Sopenharmony_ci str = last_str; 57162306a36Sopenharmony_ci break; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci fallthrough; /* report timeout */ 57462306a36Sopenharmony_ci case 0xfc: 57562306a36Sopenharmony_ci case 0xfd: 57662306a36Sopenharmony_ci case 0xfe: dfl = SERIO_TIMEOUT; data = 0xfe; break; 57762306a36Sopenharmony_ci case 0xff: dfl = SERIO_PARITY; data = 0xfe; break; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci port_no = I8042_MUX_PORT_NO + ((str >> 6) & 3); 58262306a36Sopenharmony_ci last_str = str; 58362306a36Sopenharmony_ci last_transmit = jiffies; 58462306a36Sopenharmony_ci } else { 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) | 58762306a36Sopenharmony_ci ((str & I8042_STR_TIMEOUT && !i8042_notimeout) ? SERIO_TIMEOUT : 0); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci port_no = (str & I8042_STR_AUXDATA) ? 59062306a36Sopenharmony_ci I8042_AUX_PORT_NO : I8042_KBD_PORT_NO; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci port = &i8042_ports[port_no]; 59462306a36Sopenharmony_ci serio = port->exists ? port->serio : NULL; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci filter_dbg(port->driver_bound, data, "<- i8042 (interrupt, %d, %d%s%s)\n", 59762306a36Sopenharmony_ci port_no, irq, 59862306a36Sopenharmony_ci dfl & SERIO_PARITY ? ", bad parity" : "", 59962306a36Sopenharmony_ci dfl & SERIO_TIMEOUT ? ", timeout" : ""); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci filtered = i8042_filter(data, str, serio); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci spin_unlock_irqrestore(&i8042_lock, flags); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci if (likely(serio && !filtered)) 60662306a36Sopenharmony_ci serio_interrupt(serio, data, dfl); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci out: 60962306a36Sopenharmony_ci return IRQ_RETVAL(ret); 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci/* 61362306a36Sopenharmony_ci * i8042_enable_kbd_port enables keyboard port on chip 61462306a36Sopenharmony_ci */ 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic int i8042_enable_kbd_port(void) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci i8042_ctr &= ~I8042_CTR_KBDDIS; 61962306a36Sopenharmony_ci i8042_ctr |= I8042_CTR_KBDINT; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { 62262306a36Sopenharmony_ci i8042_ctr &= ~I8042_CTR_KBDINT; 62362306a36Sopenharmony_ci i8042_ctr |= I8042_CTR_KBDDIS; 62462306a36Sopenharmony_ci pr_err("Failed to enable KBD port\n"); 62562306a36Sopenharmony_ci return -EIO; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci return 0; 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci/* 63262306a36Sopenharmony_ci * i8042_enable_aux_port enables AUX (mouse) port on chip 63362306a36Sopenharmony_ci */ 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic int i8042_enable_aux_port(void) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci i8042_ctr &= ~I8042_CTR_AUXDIS; 63862306a36Sopenharmony_ci i8042_ctr |= I8042_CTR_AUXINT; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { 64162306a36Sopenharmony_ci i8042_ctr &= ~I8042_CTR_AUXINT; 64262306a36Sopenharmony_ci i8042_ctr |= I8042_CTR_AUXDIS; 64362306a36Sopenharmony_ci pr_err("Failed to enable AUX port\n"); 64462306a36Sopenharmony_ci return -EIO; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci return 0; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci/* 65162306a36Sopenharmony_ci * i8042_enable_mux_ports enables 4 individual AUX ports after 65262306a36Sopenharmony_ci * the controller has been switched into Multiplexed mode 65362306a36Sopenharmony_ci */ 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cistatic int i8042_enable_mux_ports(void) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci unsigned char param; 65862306a36Sopenharmony_ci int i; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci for (i = 0; i < I8042_NUM_MUX_PORTS; i++) { 66162306a36Sopenharmony_ci i8042_command(¶m, I8042_CMD_MUX_PFX + i); 66262306a36Sopenharmony_ci i8042_command(¶m, I8042_CMD_AUX_ENABLE); 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci return i8042_enable_aux_port(); 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci/* 66962306a36Sopenharmony_ci * i8042_set_mux_mode checks whether the controller has an 67062306a36Sopenharmony_ci * active multiplexor and puts the chip into Multiplexed (true) 67162306a36Sopenharmony_ci * or Legacy (false) mode. 67262306a36Sopenharmony_ci */ 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cistatic int i8042_set_mux_mode(bool multiplex, unsigned char *mux_version) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci unsigned char param, val; 67862306a36Sopenharmony_ci/* 67962306a36Sopenharmony_ci * Get rid of bytes in the queue. 68062306a36Sopenharmony_ci */ 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci i8042_flush(); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci/* 68562306a36Sopenharmony_ci * Internal loopback test - send three bytes, they should come back from the 68662306a36Sopenharmony_ci * mouse interface, the last should be version. 68762306a36Sopenharmony_ci */ 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci param = val = 0xf0; 69062306a36Sopenharmony_ci if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != val) 69162306a36Sopenharmony_ci return -1; 69262306a36Sopenharmony_ci param = val = multiplex ? 0x56 : 0xf6; 69362306a36Sopenharmony_ci if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != val) 69462306a36Sopenharmony_ci return -1; 69562306a36Sopenharmony_ci param = val = multiplex ? 0xa4 : 0xa5; 69662306a36Sopenharmony_ci if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param == val) 69762306a36Sopenharmony_ci return -1; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci/* 70062306a36Sopenharmony_ci * Workaround for interference with USB Legacy emulation 70162306a36Sopenharmony_ci * that causes a v10.12 MUX to be found. 70262306a36Sopenharmony_ci */ 70362306a36Sopenharmony_ci if (param == 0xac) 70462306a36Sopenharmony_ci return -1; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci if (mux_version) 70762306a36Sopenharmony_ci *mux_version = param; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci return 0; 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci/* 71362306a36Sopenharmony_ci * i8042_check_mux() checks whether the controller supports the PS/2 Active 71462306a36Sopenharmony_ci * Multiplexing specification by Synaptics, Phoenix, Insyde and 71562306a36Sopenharmony_ci * LCS/Telegraphics. 71662306a36Sopenharmony_ci */ 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_cistatic int i8042_check_mux(void) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci unsigned char mux_version; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci if (i8042_set_mux_mode(true, &mux_version)) 72362306a36Sopenharmony_ci return -1; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci pr_info("Detected active multiplexing controller, rev %d.%d\n", 72662306a36Sopenharmony_ci (mux_version >> 4) & 0xf, mux_version & 0xf); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci/* 72962306a36Sopenharmony_ci * Disable all muxed ports by disabling AUX. 73062306a36Sopenharmony_ci */ 73162306a36Sopenharmony_ci i8042_ctr |= I8042_CTR_AUXDIS; 73262306a36Sopenharmony_ci i8042_ctr &= ~I8042_CTR_AUXINT; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { 73562306a36Sopenharmony_ci pr_err("Failed to disable AUX port, can't use MUX\n"); 73662306a36Sopenharmony_ci return -EIO; 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci i8042_mux_present = true; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci return 0; 74262306a36Sopenharmony_ci} 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci/* 74562306a36Sopenharmony_ci * The following is used to test AUX IRQ delivery. 74662306a36Sopenharmony_ci */ 74762306a36Sopenharmony_cistatic struct completion i8042_aux_irq_delivered; 74862306a36Sopenharmony_cistatic bool i8042_irq_being_tested; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_cistatic irqreturn_t i8042_aux_test_irq(int irq, void *dev_id) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci unsigned long flags; 75362306a36Sopenharmony_ci unsigned char str, data; 75462306a36Sopenharmony_ci int ret = 0; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci spin_lock_irqsave(&i8042_lock, flags); 75762306a36Sopenharmony_ci str = i8042_read_status(); 75862306a36Sopenharmony_ci if (str & I8042_STR_OBF) { 75962306a36Sopenharmony_ci data = i8042_read_data(); 76062306a36Sopenharmony_ci dbg("%02x <- i8042 (aux_test_irq, %s)\n", 76162306a36Sopenharmony_ci data, str & I8042_STR_AUXDATA ? "aux" : "kbd"); 76262306a36Sopenharmony_ci if (i8042_irq_being_tested && 76362306a36Sopenharmony_ci data == 0xa5 && (str & I8042_STR_AUXDATA)) 76462306a36Sopenharmony_ci complete(&i8042_aux_irq_delivered); 76562306a36Sopenharmony_ci ret = 1; 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci spin_unlock_irqrestore(&i8042_lock, flags); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci return IRQ_RETVAL(ret); 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci/* 77362306a36Sopenharmony_ci * i8042_toggle_aux - enables or disables AUX port on i8042 via command and 77462306a36Sopenharmony_ci * verifies success by readinng CTR. Used when testing for presence of AUX 77562306a36Sopenharmony_ci * port. 77662306a36Sopenharmony_ci */ 77762306a36Sopenharmony_cistatic int i8042_toggle_aux(bool on) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci unsigned char param; 78062306a36Sopenharmony_ci int i; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci if (i8042_command(¶m, 78362306a36Sopenharmony_ci on ? I8042_CMD_AUX_ENABLE : I8042_CMD_AUX_DISABLE)) 78462306a36Sopenharmony_ci return -1; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci /* some chips need some time to set the I8042_CTR_AUXDIS bit */ 78762306a36Sopenharmony_ci for (i = 0; i < 100; i++) { 78862306a36Sopenharmony_ci udelay(50); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (i8042_command(¶m, I8042_CMD_CTL_RCTR)) 79162306a36Sopenharmony_ci return -1; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci if (!(param & I8042_CTR_AUXDIS) == on) 79462306a36Sopenharmony_ci return 0; 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci return -1; 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci/* 80162306a36Sopenharmony_ci * i8042_check_aux() applies as much paranoia as it can at detecting 80262306a36Sopenharmony_ci * the presence of an AUX interface. 80362306a36Sopenharmony_ci */ 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_cistatic int i8042_check_aux(void) 80662306a36Sopenharmony_ci{ 80762306a36Sopenharmony_ci int retval = -1; 80862306a36Sopenharmony_ci bool irq_registered = false; 80962306a36Sopenharmony_ci bool aux_loop_broken = false; 81062306a36Sopenharmony_ci unsigned long flags; 81162306a36Sopenharmony_ci unsigned char param; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci/* 81462306a36Sopenharmony_ci * Get rid of bytes in the queue. 81562306a36Sopenharmony_ci */ 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci i8042_flush(); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci/* 82062306a36Sopenharmony_ci * Internal loopback test - filters out AT-type i8042's. Unfortunately 82162306a36Sopenharmony_ci * SiS screwed up and their 5597 doesn't support the LOOP command even 82262306a36Sopenharmony_ci * though it has an AUX port. 82362306a36Sopenharmony_ci */ 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci param = 0x5a; 82662306a36Sopenharmony_ci retval = i8042_command(¶m, I8042_CMD_AUX_LOOP); 82762306a36Sopenharmony_ci if (retval || param != 0x5a) { 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci/* 83062306a36Sopenharmony_ci * External connection test - filters out AT-soldered PS/2 i8042's 83162306a36Sopenharmony_ci * 0x00 - no error, 0x01-0x03 - clock/data stuck, 0xff - general error 83262306a36Sopenharmony_ci * 0xfa - no error on some notebooks which ignore the spec 83362306a36Sopenharmony_ci * Because it's common for chipsets to return error on perfectly functioning 83462306a36Sopenharmony_ci * AUX ports, we test for this only when the LOOP command failed. 83562306a36Sopenharmony_ci */ 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci if (i8042_command(¶m, I8042_CMD_AUX_TEST) || 83862306a36Sopenharmony_ci (param && param != 0xfa && param != 0xff)) 83962306a36Sopenharmony_ci return -1; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci/* 84262306a36Sopenharmony_ci * If AUX_LOOP completed without error but returned unexpected data 84362306a36Sopenharmony_ci * mark it as broken 84462306a36Sopenharmony_ci */ 84562306a36Sopenharmony_ci if (!retval) 84662306a36Sopenharmony_ci aux_loop_broken = true; 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci/* 85062306a36Sopenharmony_ci * Bit assignment test - filters out PS/2 i8042's in AT mode 85162306a36Sopenharmony_ci */ 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci if (i8042_toggle_aux(false)) { 85462306a36Sopenharmony_ci pr_warn("Failed to disable AUX port, but continuing anyway... Is this a SiS?\n"); 85562306a36Sopenharmony_ci pr_warn("If AUX port is really absent please use the 'i8042.noaux' option\n"); 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci if (i8042_toggle_aux(true)) 85962306a36Sopenharmony_ci return -1; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci/* 86262306a36Sopenharmony_ci * Reset keyboard (needed on some laptops to successfully detect 86362306a36Sopenharmony_ci * touchpad, e.g., some Gigabyte laptop models with Elantech 86462306a36Sopenharmony_ci * touchpads). 86562306a36Sopenharmony_ci */ 86662306a36Sopenharmony_ci if (i8042_kbdreset) { 86762306a36Sopenharmony_ci pr_warn("Attempting to reset device connected to KBD port\n"); 86862306a36Sopenharmony_ci i8042_kbd_write(NULL, (unsigned char) 0xff); 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci/* 87262306a36Sopenharmony_ci * Test AUX IRQ delivery to make sure BIOS did not grab the IRQ and 87362306a36Sopenharmony_ci * used it for a PCI card or somethig else. 87462306a36Sopenharmony_ci */ 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci if (i8042_noloop || i8042_bypass_aux_irq_test || aux_loop_broken) { 87762306a36Sopenharmony_ci/* 87862306a36Sopenharmony_ci * Without LOOP command we can't test AUX IRQ delivery. Assume the port 87962306a36Sopenharmony_ci * is working and hope we are right. 88062306a36Sopenharmony_ci */ 88162306a36Sopenharmony_ci retval = 0; 88262306a36Sopenharmony_ci goto out; 88362306a36Sopenharmony_ci } 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci if (request_irq(I8042_AUX_IRQ, i8042_aux_test_irq, IRQF_SHARED, 88662306a36Sopenharmony_ci "i8042", i8042_platform_device)) 88762306a36Sopenharmony_ci goto out; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci irq_registered = true; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci if (i8042_enable_aux_port()) 89262306a36Sopenharmony_ci goto out; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci spin_lock_irqsave(&i8042_lock, flags); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci init_completion(&i8042_aux_irq_delivered); 89762306a36Sopenharmony_ci i8042_irq_being_tested = true; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci param = 0xa5; 90062306a36Sopenharmony_ci retval = __i8042_command(¶m, I8042_CMD_AUX_LOOP & 0xf0ff); 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci spin_unlock_irqrestore(&i8042_lock, flags); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci if (retval) 90562306a36Sopenharmony_ci goto out; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci if (wait_for_completion_timeout(&i8042_aux_irq_delivered, 90862306a36Sopenharmony_ci msecs_to_jiffies(250)) == 0) { 90962306a36Sopenharmony_ci/* 91062306a36Sopenharmony_ci * AUX IRQ was never delivered so we need to flush the controller to 91162306a36Sopenharmony_ci * get rid of the byte we put there; otherwise keyboard may not work. 91262306a36Sopenharmony_ci */ 91362306a36Sopenharmony_ci dbg(" -- i8042 (aux irq test timeout)\n"); 91462306a36Sopenharmony_ci i8042_flush(); 91562306a36Sopenharmony_ci retval = -1; 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci out: 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci/* 92162306a36Sopenharmony_ci * Disable the interface. 92262306a36Sopenharmony_ci */ 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci i8042_ctr |= I8042_CTR_AUXDIS; 92562306a36Sopenharmony_ci i8042_ctr &= ~I8042_CTR_AUXINT; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) 92862306a36Sopenharmony_ci retval = -1; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci if (irq_registered) 93162306a36Sopenharmony_ci free_irq(I8042_AUX_IRQ, i8042_platform_device); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci return retval; 93462306a36Sopenharmony_ci} 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_cistatic int i8042_controller_check(void) 93762306a36Sopenharmony_ci{ 93862306a36Sopenharmony_ci if (i8042_flush()) { 93962306a36Sopenharmony_ci pr_info("No controller found\n"); 94062306a36Sopenharmony_ci return -ENODEV; 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci return 0; 94462306a36Sopenharmony_ci} 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_cistatic int i8042_controller_selftest(void) 94762306a36Sopenharmony_ci{ 94862306a36Sopenharmony_ci unsigned char param; 94962306a36Sopenharmony_ci int i = 0; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci /* 95262306a36Sopenharmony_ci * We try this 5 times; on some really fragile systems this does not 95362306a36Sopenharmony_ci * take the first time... 95462306a36Sopenharmony_ci */ 95562306a36Sopenharmony_ci do { 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci if (i8042_command(¶m, I8042_CMD_CTL_TEST)) { 95862306a36Sopenharmony_ci pr_err("i8042 controller selftest timeout\n"); 95962306a36Sopenharmony_ci return -ENODEV; 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci if (param == I8042_RET_CTL_TEST) 96362306a36Sopenharmony_ci return 0; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci dbg("i8042 controller selftest: %#x != %#x\n", 96662306a36Sopenharmony_ci param, I8042_RET_CTL_TEST); 96762306a36Sopenharmony_ci msleep(50); 96862306a36Sopenharmony_ci } while (i++ < 5); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci#ifdef CONFIG_X86 97162306a36Sopenharmony_ci /* 97262306a36Sopenharmony_ci * On x86, we don't fail entire i8042 initialization if controller 97362306a36Sopenharmony_ci * reset fails in hopes that keyboard port will still be functional 97462306a36Sopenharmony_ci * and user will still get a working keyboard. This is especially 97562306a36Sopenharmony_ci * important on netbooks. On other arches we trust hardware more. 97662306a36Sopenharmony_ci */ 97762306a36Sopenharmony_ci pr_info("giving up on controller selftest, continuing anyway...\n"); 97862306a36Sopenharmony_ci return 0; 97962306a36Sopenharmony_ci#else 98062306a36Sopenharmony_ci pr_err("i8042 controller selftest failed\n"); 98162306a36Sopenharmony_ci return -EIO; 98262306a36Sopenharmony_ci#endif 98362306a36Sopenharmony_ci} 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci/* 98662306a36Sopenharmony_ci * i8042_controller_init initializes the i8042 controller, and, 98762306a36Sopenharmony_ci * most importantly, sets it into non-xlated mode if that's 98862306a36Sopenharmony_ci * desired. 98962306a36Sopenharmony_ci */ 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_cistatic int i8042_controller_init(void) 99262306a36Sopenharmony_ci{ 99362306a36Sopenharmony_ci unsigned long flags; 99462306a36Sopenharmony_ci int n = 0; 99562306a36Sopenharmony_ci unsigned char ctr[2]; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci/* 99862306a36Sopenharmony_ci * Save the CTR for restore on unload / reboot. 99962306a36Sopenharmony_ci */ 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci do { 100262306a36Sopenharmony_ci if (n >= 10) { 100362306a36Sopenharmony_ci pr_err("Unable to get stable CTR read\n"); 100462306a36Sopenharmony_ci return -EIO; 100562306a36Sopenharmony_ci } 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci if (n != 0) 100862306a36Sopenharmony_ci udelay(50); 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci if (i8042_command(&ctr[n++ % 2], I8042_CMD_CTL_RCTR)) { 101162306a36Sopenharmony_ci pr_err("Can't read CTR while initializing i8042\n"); 101262306a36Sopenharmony_ci return i8042_probe_defer ? -EPROBE_DEFER : -EIO; 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci } while (n < 2 || ctr[0] != ctr[1]); 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci i8042_initial_ctr = i8042_ctr = ctr[0]; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci/* 102062306a36Sopenharmony_ci * Disable the keyboard interface and interrupt. 102162306a36Sopenharmony_ci */ 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci i8042_ctr |= I8042_CTR_KBDDIS; 102462306a36Sopenharmony_ci i8042_ctr &= ~I8042_CTR_KBDINT; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci/* 102762306a36Sopenharmony_ci * Handle keylock. 102862306a36Sopenharmony_ci */ 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci spin_lock_irqsave(&i8042_lock, flags); 103162306a36Sopenharmony_ci if (~i8042_read_status() & I8042_STR_KEYLOCK) { 103262306a36Sopenharmony_ci if (i8042_unlock) 103362306a36Sopenharmony_ci i8042_ctr |= I8042_CTR_IGNKEYLOCK; 103462306a36Sopenharmony_ci else 103562306a36Sopenharmony_ci pr_warn("Warning: Keylock active\n"); 103662306a36Sopenharmony_ci } 103762306a36Sopenharmony_ci spin_unlock_irqrestore(&i8042_lock, flags); 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci/* 104062306a36Sopenharmony_ci * If the chip is configured into nontranslated mode by the BIOS, don't 104162306a36Sopenharmony_ci * bother enabling translating and be happy. 104262306a36Sopenharmony_ci */ 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci if (~i8042_ctr & I8042_CTR_XLATE) 104562306a36Sopenharmony_ci i8042_direct = true; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci/* 104862306a36Sopenharmony_ci * Set nontranslated mode for the kbd interface if requested by an option. 104962306a36Sopenharmony_ci * After this the kbd interface becomes a simple serial in/out, like the aux 105062306a36Sopenharmony_ci * interface is. We don't do this by default, since it can confuse notebook 105162306a36Sopenharmony_ci * BIOSes. 105262306a36Sopenharmony_ci */ 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci if (i8042_direct) 105562306a36Sopenharmony_ci i8042_ctr &= ~I8042_CTR_XLATE; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci/* 105862306a36Sopenharmony_ci * Write CTR back. 105962306a36Sopenharmony_ci */ 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { 106262306a36Sopenharmony_ci pr_err("Can't write CTR while initializing i8042\n"); 106362306a36Sopenharmony_ci return -EIO; 106462306a36Sopenharmony_ci } 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci/* 106762306a36Sopenharmony_ci * Flush whatever accumulated while we were disabling keyboard port. 106862306a36Sopenharmony_ci */ 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci i8042_flush(); 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci return 0; 107362306a36Sopenharmony_ci} 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci/* 107762306a36Sopenharmony_ci * Reset the controller and reset CRT to the original value set by BIOS. 107862306a36Sopenharmony_ci */ 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_cistatic void i8042_controller_reset(bool s2r_wants_reset) 108162306a36Sopenharmony_ci{ 108262306a36Sopenharmony_ci i8042_flush(); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci/* 108562306a36Sopenharmony_ci * Disable both KBD and AUX interfaces so they don't get in the way 108662306a36Sopenharmony_ci */ 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci i8042_ctr |= I8042_CTR_KBDDIS | I8042_CTR_AUXDIS; 108962306a36Sopenharmony_ci i8042_ctr &= ~(I8042_CTR_KBDINT | I8042_CTR_AUXINT); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) 109262306a36Sopenharmony_ci pr_warn("Can't write CTR while resetting\n"); 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci/* 109562306a36Sopenharmony_ci * Disable MUX mode if present. 109662306a36Sopenharmony_ci */ 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci if (i8042_mux_present) 109962306a36Sopenharmony_ci i8042_set_mux_mode(false, NULL); 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci/* 110262306a36Sopenharmony_ci * Reset the controller if requested. 110362306a36Sopenharmony_ci */ 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci if (i8042_reset == I8042_RESET_ALWAYS || 110662306a36Sopenharmony_ci (i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) { 110762306a36Sopenharmony_ci i8042_controller_selftest(); 110862306a36Sopenharmony_ci } 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci/* 111162306a36Sopenharmony_ci * Restore the original control register setting. 111262306a36Sopenharmony_ci */ 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci if (i8042_command(&i8042_initial_ctr, I8042_CMD_CTL_WCTR)) 111562306a36Sopenharmony_ci pr_warn("Can't restore CTR\n"); 111662306a36Sopenharmony_ci} 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci/* 112062306a36Sopenharmony_ci * i8042_panic_blink() will turn the keyboard LEDs on or off and is called 112162306a36Sopenharmony_ci * when kernel panics. Flashing LEDs is useful for users running X who may 112262306a36Sopenharmony_ci * not see the console and will help distinguishing panics from "real" 112362306a36Sopenharmony_ci * lockups. 112462306a36Sopenharmony_ci * 112562306a36Sopenharmony_ci * Note that DELAY has a limit of 10ms so we will not get stuck here 112662306a36Sopenharmony_ci * waiting for KBC to free up even if KBD interrupt is off 112762306a36Sopenharmony_ci */ 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci#define DELAY do { mdelay(1); if (++delay > 10) return delay; } while(0) 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_cistatic long i8042_panic_blink(int state) 113262306a36Sopenharmony_ci{ 113362306a36Sopenharmony_ci long delay = 0; 113462306a36Sopenharmony_ci char led; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci led = (state) ? 0x01 | 0x04 : 0; 113762306a36Sopenharmony_ci while (i8042_read_status() & I8042_STR_IBF) 113862306a36Sopenharmony_ci DELAY; 113962306a36Sopenharmony_ci dbg("%02x -> i8042 (panic blink)\n", 0xed); 114062306a36Sopenharmony_ci i8042_suppress_kbd_ack = 2; 114162306a36Sopenharmony_ci i8042_write_data(0xed); /* set leds */ 114262306a36Sopenharmony_ci DELAY; 114362306a36Sopenharmony_ci while (i8042_read_status() & I8042_STR_IBF) 114462306a36Sopenharmony_ci DELAY; 114562306a36Sopenharmony_ci DELAY; 114662306a36Sopenharmony_ci dbg("%02x -> i8042 (panic blink)\n", led); 114762306a36Sopenharmony_ci i8042_write_data(led); 114862306a36Sopenharmony_ci DELAY; 114962306a36Sopenharmony_ci return delay; 115062306a36Sopenharmony_ci} 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci#undef DELAY 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci#ifdef CONFIG_X86 115562306a36Sopenharmony_cistatic void i8042_dritek_enable(void) 115662306a36Sopenharmony_ci{ 115762306a36Sopenharmony_ci unsigned char param = 0x90; 115862306a36Sopenharmony_ci int error; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci error = i8042_command(¶m, 0x1059); 116162306a36Sopenharmony_ci if (error) 116262306a36Sopenharmony_ci pr_warn("Failed to enable DRITEK extension: %d\n", error); 116362306a36Sopenharmony_ci} 116462306a36Sopenharmony_ci#endif 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci#ifdef CONFIG_PM 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci/* 116962306a36Sopenharmony_ci * Here we try to reset everything back to a state we had 117062306a36Sopenharmony_ci * before suspending. 117162306a36Sopenharmony_ci */ 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_cistatic int i8042_controller_resume(bool s2r_wants_reset) 117462306a36Sopenharmony_ci{ 117562306a36Sopenharmony_ci int error; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci error = i8042_controller_check(); 117862306a36Sopenharmony_ci if (error) 117962306a36Sopenharmony_ci return error; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci if (i8042_reset == I8042_RESET_ALWAYS || 118262306a36Sopenharmony_ci (i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) { 118362306a36Sopenharmony_ci error = i8042_controller_selftest(); 118462306a36Sopenharmony_ci if (error) 118562306a36Sopenharmony_ci return error; 118662306a36Sopenharmony_ci } 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci/* 118962306a36Sopenharmony_ci * Restore original CTR value and disable all ports 119062306a36Sopenharmony_ci */ 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci i8042_ctr = i8042_initial_ctr; 119362306a36Sopenharmony_ci if (i8042_direct) 119462306a36Sopenharmony_ci i8042_ctr &= ~I8042_CTR_XLATE; 119562306a36Sopenharmony_ci i8042_ctr |= I8042_CTR_AUXDIS | I8042_CTR_KBDDIS; 119662306a36Sopenharmony_ci i8042_ctr &= ~(I8042_CTR_AUXINT | I8042_CTR_KBDINT); 119762306a36Sopenharmony_ci if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { 119862306a36Sopenharmony_ci pr_warn("Can't write CTR to resume, retrying...\n"); 119962306a36Sopenharmony_ci msleep(50); 120062306a36Sopenharmony_ci if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { 120162306a36Sopenharmony_ci pr_err("CTR write retry failed\n"); 120262306a36Sopenharmony_ci return -EIO; 120362306a36Sopenharmony_ci } 120462306a36Sopenharmony_ci } 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci#ifdef CONFIG_X86 120862306a36Sopenharmony_ci if (i8042_dritek) 120962306a36Sopenharmony_ci i8042_dritek_enable(); 121062306a36Sopenharmony_ci#endif 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci if (i8042_mux_present) { 121362306a36Sopenharmony_ci if (i8042_set_mux_mode(true, NULL) || i8042_enable_mux_ports()) 121462306a36Sopenharmony_ci pr_warn("failed to resume active multiplexor, mouse won't work\n"); 121562306a36Sopenharmony_ci } else if (i8042_ports[I8042_AUX_PORT_NO].serio) 121662306a36Sopenharmony_ci i8042_enable_aux_port(); 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci if (i8042_ports[I8042_KBD_PORT_NO].serio) 121962306a36Sopenharmony_ci i8042_enable_kbd_port(); 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci i8042_interrupt(0, NULL); 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci return 0; 122462306a36Sopenharmony_ci} 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci/* 122762306a36Sopenharmony_ci * Here we try to restore the original BIOS settings to avoid 122862306a36Sopenharmony_ci * upsetting it. 122962306a36Sopenharmony_ci */ 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_cistatic int i8042_pm_suspend(struct device *dev) 123262306a36Sopenharmony_ci{ 123362306a36Sopenharmony_ci int i; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci if (pm_suspend_via_firmware()) 123662306a36Sopenharmony_ci i8042_controller_reset(true); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci /* Set up serio interrupts for system wakeup. */ 123962306a36Sopenharmony_ci for (i = 0; i < I8042_NUM_PORTS; i++) { 124062306a36Sopenharmony_ci struct serio *serio = i8042_ports[i].serio; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci if (serio && device_may_wakeup(&serio->dev)) 124362306a36Sopenharmony_ci enable_irq_wake(i8042_ports[i].irq); 124462306a36Sopenharmony_ci } 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci return 0; 124762306a36Sopenharmony_ci} 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_cistatic int i8042_pm_resume_noirq(struct device *dev) 125062306a36Sopenharmony_ci{ 125162306a36Sopenharmony_ci if (!pm_resume_via_firmware()) 125262306a36Sopenharmony_ci i8042_interrupt(0, NULL); 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci return 0; 125562306a36Sopenharmony_ci} 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_cistatic int i8042_pm_resume(struct device *dev) 125862306a36Sopenharmony_ci{ 125962306a36Sopenharmony_ci bool want_reset; 126062306a36Sopenharmony_ci int i; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci for (i = 0; i < I8042_NUM_PORTS; i++) { 126362306a36Sopenharmony_ci struct serio *serio = i8042_ports[i].serio; 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci if (serio && device_may_wakeup(&serio->dev)) 126662306a36Sopenharmony_ci disable_irq_wake(i8042_ports[i].irq); 126762306a36Sopenharmony_ci } 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci /* 127062306a36Sopenharmony_ci * If platform firmware was not going to be involved in suspend, we did 127162306a36Sopenharmony_ci * not restore the controller state to whatever it had been at boot 127262306a36Sopenharmony_ci * time, so we do not need to do anything. 127362306a36Sopenharmony_ci */ 127462306a36Sopenharmony_ci if (!pm_suspend_via_firmware()) 127562306a36Sopenharmony_ci return 0; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci /* 127862306a36Sopenharmony_ci * We only need to reset the controller if we are resuming after handing 127962306a36Sopenharmony_ci * off control to the platform firmware, otherwise we can simply restore 128062306a36Sopenharmony_ci * the mode. 128162306a36Sopenharmony_ci */ 128262306a36Sopenharmony_ci want_reset = pm_resume_via_firmware(); 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci return i8042_controller_resume(want_reset); 128562306a36Sopenharmony_ci} 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_cistatic int i8042_pm_thaw(struct device *dev) 128862306a36Sopenharmony_ci{ 128962306a36Sopenharmony_ci i8042_interrupt(0, NULL); 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci return 0; 129262306a36Sopenharmony_ci} 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_cistatic int i8042_pm_reset(struct device *dev) 129562306a36Sopenharmony_ci{ 129662306a36Sopenharmony_ci i8042_controller_reset(false); 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci return 0; 129962306a36Sopenharmony_ci} 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_cistatic int i8042_pm_restore(struct device *dev) 130262306a36Sopenharmony_ci{ 130362306a36Sopenharmony_ci return i8042_controller_resume(false); 130462306a36Sopenharmony_ci} 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_cistatic const struct dev_pm_ops i8042_pm_ops = { 130762306a36Sopenharmony_ci .suspend = i8042_pm_suspend, 130862306a36Sopenharmony_ci .resume_noirq = i8042_pm_resume_noirq, 130962306a36Sopenharmony_ci .resume = i8042_pm_resume, 131062306a36Sopenharmony_ci .thaw = i8042_pm_thaw, 131162306a36Sopenharmony_ci .poweroff = i8042_pm_reset, 131262306a36Sopenharmony_ci .restore = i8042_pm_restore, 131362306a36Sopenharmony_ci}; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci#endif /* CONFIG_PM */ 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci/* 131862306a36Sopenharmony_ci * We need to reset the 8042 back to original mode on system shutdown, 131962306a36Sopenharmony_ci * because otherwise BIOSes will be confused. 132062306a36Sopenharmony_ci */ 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_cistatic void i8042_shutdown(struct platform_device *dev) 132362306a36Sopenharmony_ci{ 132462306a36Sopenharmony_ci i8042_controller_reset(false); 132562306a36Sopenharmony_ci} 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_cistatic int i8042_create_kbd_port(void) 132862306a36Sopenharmony_ci{ 132962306a36Sopenharmony_ci struct serio *serio; 133062306a36Sopenharmony_ci struct i8042_port *port = &i8042_ports[I8042_KBD_PORT_NO]; 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci serio = kzalloc(sizeof(struct serio), GFP_KERNEL); 133362306a36Sopenharmony_ci if (!serio) 133462306a36Sopenharmony_ci return -ENOMEM; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci serio->id.type = i8042_direct ? SERIO_8042 : SERIO_8042_XL; 133762306a36Sopenharmony_ci serio->write = i8042_dumbkbd ? NULL : i8042_kbd_write; 133862306a36Sopenharmony_ci serio->start = i8042_start; 133962306a36Sopenharmony_ci serio->stop = i8042_stop; 134062306a36Sopenharmony_ci serio->close = i8042_port_close; 134162306a36Sopenharmony_ci serio->ps2_cmd_mutex = &i8042_mutex; 134262306a36Sopenharmony_ci serio->port_data = port; 134362306a36Sopenharmony_ci serio->dev.parent = &i8042_platform_device->dev; 134462306a36Sopenharmony_ci strscpy(serio->name, "i8042 KBD port", sizeof(serio->name)); 134562306a36Sopenharmony_ci strscpy(serio->phys, I8042_KBD_PHYS_DESC, sizeof(serio->phys)); 134662306a36Sopenharmony_ci strscpy(serio->firmware_id, i8042_kbd_firmware_id, 134762306a36Sopenharmony_ci sizeof(serio->firmware_id)); 134862306a36Sopenharmony_ci set_primary_fwnode(&serio->dev, i8042_kbd_fwnode); 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci port->serio = serio; 135162306a36Sopenharmony_ci port->irq = I8042_KBD_IRQ; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci return 0; 135462306a36Sopenharmony_ci} 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_cistatic int i8042_create_aux_port(int idx) 135762306a36Sopenharmony_ci{ 135862306a36Sopenharmony_ci struct serio *serio; 135962306a36Sopenharmony_ci int port_no = idx < 0 ? I8042_AUX_PORT_NO : I8042_MUX_PORT_NO + idx; 136062306a36Sopenharmony_ci struct i8042_port *port = &i8042_ports[port_no]; 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci serio = kzalloc(sizeof(struct serio), GFP_KERNEL); 136362306a36Sopenharmony_ci if (!serio) 136462306a36Sopenharmony_ci return -ENOMEM; 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci serio->id.type = SERIO_8042; 136762306a36Sopenharmony_ci serio->write = i8042_aux_write; 136862306a36Sopenharmony_ci serio->start = i8042_start; 136962306a36Sopenharmony_ci serio->stop = i8042_stop; 137062306a36Sopenharmony_ci serio->ps2_cmd_mutex = &i8042_mutex; 137162306a36Sopenharmony_ci serio->port_data = port; 137262306a36Sopenharmony_ci serio->dev.parent = &i8042_platform_device->dev; 137362306a36Sopenharmony_ci if (idx < 0) { 137462306a36Sopenharmony_ci strscpy(serio->name, "i8042 AUX port", sizeof(serio->name)); 137562306a36Sopenharmony_ci strscpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys)); 137662306a36Sopenharmony_ci strscpy(serio->firmware_id, i8042_aux_firmware_id, 137762306a36Sopenharmony_ci sizeof(serio->firmware_id)); 137862306a36Sopenharmony_ci serio->close = i8042_port_close; 137962306a36Sopenharmony_ci } else { 138062306a36Sopenharmony_ci snprintf(serio->name, sizeof(serio->name), "i8042 AUX%d port", idx); 138162306a36Sopenharmony_ci snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, idx + 1); 138262306a36Sopenharmony_ci strscpy(serio->firmware_id, i8042_aux_firmware_id, 138362306a36Sopenharmony_ci sizeof(serio->firmware_id)); 138462306a36Sopenharmony_ci } 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci port->serio = serio; 138762306a36Sopenharmony_ci port->mux = idx; 138862306a36Sopenharmony_ci port->irq = I8042_AUX_IRQ; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci return 0; 139162306a36Sopenharmony_ci} 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_cistatic void i8042_free_kbd_port(void) 139462306a36Sopenharmony_ci{ 139562306a36Sopenharmony_ci kfree(i8042_ports[I8042_KBD_PORT_NO].serio); 139662306a36Sopenharmony_ci i8042_ports[I8042_KBD_PORT_NO].serio = NULL; 139762306a36Sopenharmony_ci} 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_cistatic void i8042_free_aux_ports(void) 140062306a36Sopenharmony_ci{ 140162306a36Sopenharmony_ci int i; 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci for (i = I8042_AUX_PORT_NO; i < I8042_NUM_PORTS; i++) { 140462306a36Sopenharmony_ci kfree(i8042_ports[i].serio); 140562306a36Sopenharmony_ci i8042_ports[i].serio = NULL; 140662306a36Sopenharmony_ci } 140762306a36Sopenharmony_ci} 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_cistatic void i8042_register_ports(void) 141062306a36Sopenharmony_ci{ 141162306a36Sopenharmony_ci int i; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci for (i = 0; i < I8042_NUM_PORTS; i++) { 141462306a36Sopenharmony_ci struct serio *serio = i8042_ports[i].serio; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci if (!serio) 141762306a36Sopenharmony_ci continue; 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci printk(KERN_INFO "serio: %s at %#lx,%#lx irq %d\n", 142062306a36Sopenharmony_ci serio->name, 142162306a36Sopenharmony_ci (unsigned long) I8042_DATA_REG, 142262306a36Sopenharmony_ci (unsigned long) I8042_COMMAND_REG, 142362306a36Sopenharmony_ci i8042_ports[i].irq); 142462306a36Sopenharmony_ci serio_register_port(serio); 142562306a36Sopenharmony_ci } 142662306a36Sopenharmony_ci} 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_cistatic void i8042_unregister_ports(void) 142962306a36Sopenharmony_ci{ 143062306a36Sopenharmony_ci int i; 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci for (i = 0; i < I8042_NUM_PORTS; i++) { 143362306a36Sopenharmony_ci if (i8042_ports[i].serio) { 143462306a36Sopenharmony_ci serio_unregister_port(i8042_ports[i].serio); 143562306a36Sopenharmony_ci i8042_ports[i].serio = NULL; 143662306a36Sopenharmony_ci } 143762306a36Sopenharmony_ci } 143862306a36Sopenharmony_ci} 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_cistatic void i8042_free_irqs(void) 144162306a36Sopenharmony_ci{ 144262306a36Sopenharmony_ci if (i8042_aux_irq_registered) 144362306a36Sopenharmony_ci free_irq(I8042_AUX_IRQ, i8042_platform_device); 144462306a36Sopenharmony_ci if (i8042_kbd_irq_registered) 144562306a36Sopenharmony_ci free_irq(I8042_KBD_IRQ, i8042_platform_device); 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci i8042_aux_irq_registered = i8042_kbd_irq_registered = false; 144862306a36Sopenharmony_ci} 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_cistatic int i8042_setup_aux(void) 145162306a36Sopenharmony_ci{ 145262306a36Sopenharmony_ci int (*aux_enable)(void); 145362306a36Sopenharmony_ci int error; 145462306a36Sopenharmony_ci int i; 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci if (i8042_check_aux()) 145762306a36Sopenharmony_ci return -ENODEV; 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci if (i8042_nomux || i8042_check_mux()) { 146062306a36Sopenharmony_ci error = i8042_create_aux_port(-1); 146162306a36Sopenharmony_ci if (error) 146262306a36Sopenharmony_ci goto err_free_ports; 146362306a36Sopenharmony_ci aux_enable = i8042_enable_aux_port; 146462306a36Sopenharmony_ci } else { 146562306a36Sopenharmony_ci for (i = 0; i < I8042_NUM_MUX_PORTS; i++) { 146662306a36Sopenharmony_ci error = i8042_create_aux_port(i); 146762306a36Sopenharmony_ci if (error) 146862306a36Sopenharmony_ci goto err_free_ports; 146962306a36Sopenharmony_ci } 147062306a36Sopenharmony_ci aux_enable = i8042_enable_mux_ports; 147162306a36Sopenharmony_ci } 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci error = request_irq(I8042_AUX_IRQ, i8042_interrupt, IRQF_SHARED, 147462306a36Sopenharmony_ci "i8042", i8042_platform_device); 147562306a36Sopenharmony_ci if (error) 147662306a36Sopenharmony_ci goto err_free_ports; 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci error = aux_enable(); 147962306a36Sopenharmony_ci if (error) 148062306a36Sopenharmony_ci goto err_free_irq; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci i8042_aux_irq_registered = true; 148362306a36Sopenharmony_ci return 0; 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci err_free_irq: 148662306a36Sopenharmony_ci free_irq(I8042_AUX_IRQ, i8042_platform_device); 148762306a36Sopenharmony_ci err_free_ports: 148862306a36Sopenharmony_ci i8042_free_aux_ports(); 148962306a36Sopenharmony_ci return error; 149062306a36Sopenharmony_ci} 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_cistatic int i8042_setup_kbd(void) 149362306a36Sopenharmony_ci{ 149462306a36Sopenharmony_ci int error; 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci error = i8042_create_kbd_port(); 149762306a36Sopenharmony_ci if (error) 149862306a36Sopenharmony_ci return error; 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci error = request_irq(I8042_KBD_IRQ, i8042_interrupt, IRQF_SHARED, 150162306a36Sopenharmony_ci "i8042", i8042_platform_device); 150262306a36Sopenharmony_ci if (error) 150362306a36Sopenharmony_ci goto err_free_port; 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci error = i8042_enable_kbd_port(); 150662306a36Sopenharmony_ci if (error) 150762306a36Sopenharmony_ci goto err_free_irq; 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci i8042_kbd_irq_registered = true; 151062306a36Sopenharmony_ci return 0; 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci err_free_irq: 151362306a36Sopenharmony_ci free_irq(I8042_KBD_IRQ, i8042_platform_device); 151462306a36Sopenharmony_ci err_free_port: 151562306a36Sopenharmony_ci i8042_free_kbd_port(); 151662306a36Sopenharmony_ci return error; 151762306a36Sopenharmony_ci} 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_cistatic int i8042_kbd_bind_notifier(struct notifier_block *nb, 152062306a36Sopenharmony_ci unsigned long action, void *data) 152162306a36Sopenharmony_ci{ 152262306a36Sopenharmony_ci struct device *dev = data; 152362306a36Sopenharmony_ci struct serio *serio = to_serio_port(dev); 152462306a36Sopenharmony_ci struct i8042_port *port = serio->port_data; 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci if (serio != i8042_ports[I8042_KBD_PORT_NO].serio) 152762306a36Sopenharmony_ci return 0; 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci switch (action) { 153062306a36Sopenharmony_ci case BUS_NOTIFY_BOUND_DRIVER: 153162306a36Sopenharmony_ci port->driver_bound = true; 153262306a36Sopenharmony_ci break; 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci case BUS_NOTIFY_UNBIND_DRIVER: 153562306a36Sopenharmony_ci port->driver_bound = false; 153662306a36Sopenharmony_ci break; 153762306a36Sopenharmony_ci } 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci return 0; 154062306a36Sopenharmony_ci} 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_cistatic int i8042_probe(struct platform_device *dev) 154362306a36Sopenharmony_ci{ 154462306a36Sopenharmony_ci int error; 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci if (i8042_reset == I8042_RESET_ALWAYS) { 154762306a36Sopenharmony_ci error = i8042_controller_selftest(); 154862306a36Sopenharmony_ci if (error) 154962306a36Sopenharmony_ci return error; 155062306a36Sopenharmony_ci } 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci error = i8042_controller_init(); 155362306a36Sopenharmony_ci if (error) 155462306a36Sopenharmony_ci return error; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci#ifdef CONFIG_X86 155762306a36Sopenharmony_ci if (i8042_dritek) 155862306a36Sopenharmony_ci i8042_dritek_enable(); 155962306a36Sopenharmony_ci#endif 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci if (!i8042_noaux) { 156262306a36Sopenharmony_ci error = i8042_setup_aux(); 156362306a36Sopenharmony_ci if (error && error != -ENODEV && error != -EBUSY) 156462306a36Sopenharmony_ci goto out_fail; 156562306a36Sopenharmony_ci } 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci if (!i8042_nokbd) { 156862306a36Sopenharmony_ci error = i8042_setup_kbd(); 156962306a36Sopenharmony_ci if (error) 157062306a36Sopenharmony_ci goto out_fail; 157162306a36Sopenharmony_ci } 157262306a36Sopenharmony_ci/* 157362306a36Sopenharmony_ci * Ok, everything is ready, let's register all serio ports 157462306a36Sopenharmony_ci */ 157562306a36Sopenharmony_ci i8042_register_ports(); 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci return 0; 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci out_fail: 158062306a36Sopenharmony_ci i8042_free_aux_ports(); /* in case KBD failed but AUX not */ 158162306a36Sopenharmony_ci i8042_free_irqs(); 158262306a36Sopenharmony_ci i8042_controller_reset(false); 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci return error; 158562306a36Sopenharmony_ci} 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_cistatic int i8042_remove(struct platform_device *dev) 158862306a36Sopenharmony_ci{ 158962306a36Sopenharmony_ci i8042_unregister_ports(); 159062306a36Sopenharmony_ci i8042_free_irqs(); 159162306a36Sopenharmony_ci i8042_controller_reset(false); 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci return 0; 159462306a36Sopenharmony_ci} 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_cistatic struct platform_driver i8042_driver = { 159762306a36Sopenharmony_ci .driver = { 159862306a36Sopenharmony_ci .name = "i8042", 159962306a36Sopenharmony_ci#ifdef CONFIG_PM 160062306a36Sopenharmony_ci .pm = &i8042_pm_ops, 160162306a36Sopenharmony_ci#endif 160262306a36Sopenharmony_ci }, 160362306a36Sopenharmony_ci .probe = i8042_probe, 160462306a36Sopenharmony_ci .remove = i8042_remove, 160562306a36Sopenharmony_ci .shutdown = i8042_shutdown, 160662306a36Sopenharmony_ci}; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_cistatic struct notifier_block i8042_kbd_bind_notifier_block = { 160962306a36Sopenharmony_ci .notifier_call = i8042_kbd_bind_notifier, 161062306a36Sopenharmony_ci}; 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_cistatic int __init i8042_init(void) 161362306a36Sopenharmony_ci{ 161462306a36Sopenharmony_ci int err; 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ci dbg_init(); 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci err = i8042_platform_init(); 161962306a36Sopenharmony_ci if (err) 162062306a36Sopenharmony_ci return (err == -ENODEV) ? 0 : err; 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci err = i8042_controller_check(); 162362306a36Sopenharmony_ci if (err) 162462306a36Sopenharmony_ci goto err_platform_exit; 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci /* Set this before creating the dev to allow i8042_command to work right away */ 162762306a36Sopenharmony_ci i8042_present = true; 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci err = platform_driver_register(&i8042_driver); 163062306a36Sopenharmony_ci if (err) 163162306a36Sopenharmony_ci goto err_platform_exit; 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci i8042_platform_device = platform_device_alloc("i8042", -1); 163462306a36Sopenharmony_ci if (!i8042_platform_device) { 163562306a36Sopenharmony_ci err = -ENOMEM; 163662306a36Sopenharmony_ci goto err_unregister_driver; 163762306a36Sopenharmony_ci } 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci err = platform_device_add(i8042_platform_device); 164062306a36Sopenharmony_ci if (err) 164162306a36Sopenharmony_ci goto err_free_device; 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci bus_register_notifier(&serio_bus, &i8042_kbd_bind_notifier_block); 164462306a36Sopenharmony_ci panic_blink = i8042_panic_blink; 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci return 0; 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_cierr_free_device: 164962306a36Sopenharmony_ci platform_device_put(i8042_platform_device); 165062306a36Sopenharmony_cierr_unregister_driver: 165162306a36Sopenharmony_ci platform_driver_unregister(&i8042_driver); 165262306a36Sopenharmony_ci err_platform_exit: 165362306a36Sopenharmony_ci i8042_platform_exit(); 165462306a36Sopenharmony_ci return err; 165562306a36Sopenharmony_ci} 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_cistatic void __exit i8042_exit(void) 165862306a36Sopenharmony_ci{ 165962306a36Sopenharmony_ci if (!i8042_present) 166062306a36Sopenharmony_ci return; 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci platform_device_unregister(i8042_platform_device); 166362306a36Sopenharmony_ci platform_driver_unregister(&i8042_driver); 166462306a36Sopenharmony_ci i8042_platform_exit(); 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci bus_unregister_notifier(&serio_bus, &i8042_kbd_bind_notifier_block); 166762306a36Sopenharmony_ci panic_blink = NULL; 166862306a36Sopenharmony_ci} 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_cimodule_init(i8042_init); 167162306a36Sopenharmony_cimodule_exit(i8042_exit); 1672