18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * i8042 keyboard and mouse controller driver for Linux 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 1999-2004 Vojtech Pavlik 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/types.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/ioport.h> 168c2ecf20Sopenharmony_ci#include <linux/init.h> 178c2ecf20Sopenharmony_ci#include <linux/serio.h> 188c2ecf20Sopenharmony_ci#include <linux/err.h> 198c2ecf20Sopenharmony_ci#include <linux/rcupdate.h> 208c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 218c2ecf20Sopenharmony_ci#include <linux/i8042.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/suspend.h> 248c2ecf20Sopenharmony_ci#include <linux/property.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <asm/io.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); 298c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("i8042 keyboard and mouse controller driver"); 308c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic bool i8042_nokbd; 338c2ecf20Sopenharmony_cimodule_param_named(nokbd, i8042_nokbd, bool, 0); 348c2ecf20Sopenharmony_ciMODULE_PARM_DESC(nokbd, "Do not probe or use KBD port."); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic bool i8042_noaux; 378c2ecf20Sopenharmony_cimodule_param_named(noaux, i8042_noaux, bool, 0); 388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(noaux, "Do not probe or use AUX (mouse) port."); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic bool i8042_nomux; 418c2ecf20Sopenharmony_cimodule_param_named(nomux, i8042_nomux, bool, 0); 428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(nomux, "Do not check whether an active multiplexing controller is present."); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic bool i8042_unlock; 458c2ecf20Sopenharmony_cimodule_param_named(unlock, i8042_unlock, bool, 0); 468c2ecf20Sopenharmony_ciMODULE_PARM_DESC(unlock, "Ignore keyboard lock."); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic bool i8042_probe_defer; 498c2ecf20Sopenharmony_cimodule_param_named(probe_defer, i8042_probe_defer, bool, 0); 508c2ecf20Sopenharmony_ciMODULE_PARM_DESC(probe_defer, "Allow deferred probing."); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cienum i8042_controller_reset_mode { 538c2ecf20Sopenharmony_ci I8042_RESET_NEVER, 548c2ecf20Sopenharmony_ci I8042_RESET_ALWAYS, 558c2ecf20Sopenharmony_ci I8042_RESET_ON_S2RAM, 568c2ecf20Sopenharmony_ci#define I8042_RESET_DEFAULT I8042_RESET_ON_S2RAM 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_cistatic enum i8042_controller_reset_mode i8042_reset = I8042_RESET_DEFAULT; 598c2ecf20Sopenharmony_cistatic int i8042_set_reset(const char *val, const struct kernel_param *kp) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci enum i8042_controller_reset_mode *arg = kp->arg; 628c2ecf20Sopenharmony_ci int error; 638c2ecf20Sopenharmony_ci bool reset; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (val) { 668c2ecf20Sopenharmony_ci error = kstrtobool(val, &reset); 678c2ecf20Sopenharmony_ci if (error) 688c2ecf20Sopenharmony_ci return error; 698c2ecf20Sopenharmony_ci } else { 708c2ecf20Sopenharmony_ci reset = true; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci *arg = reset ? I8042_RESET_ALWAYS : I8042_RESET_NEVER; 748c2ecf20Sopenharmony_ci return 0; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic const struct kernel_param_ops param_ops_reset_param = { 788c2ecf20Sopenharmony_ci .flags = KERNEL_PARAM_OPS_FL_NOARG, 798c2ecf20Sopenharmony_ci .set = i8042_set_reset, 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci#define param_check_reset_param(name, p) \ 828c2ecf20Sopenharmony_ci __param_check(name, p, enum i8042_controller_reset_mode) 838c2ecf20Sopenharmony_cimodule_param_named(reset, i8042_reset, reset_param, 0); 848c2ecf20Sopenharmony_ciMODULE_PARM_DESC(reset, "Reset controller on resume, cleanup or both"); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic bool i8042_direct; 878c2ecf20Sopenharmony_cimodule_param_named(direct, i8042_direct, bool, 0); 888c2ecf20Sopenharmony_ciMODULE_PARM_DESC(direct, "Put keyboard port into non-translated mode."); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic bool i8042_dumbkbd; 918c2ecf20Sopenharmony_cimodule_param_named(dumbkbd, i8042_dumbkbd, bool, 0); 928c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dumbkbd, "Pretend that controller can only read data from keyboard"); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic bool i8042_noloop; 958c2ecf20Sopenharmony_cimodule_param_named(noloop, i8042_noloop, bool, 0); 968c2ecf20Sopenharmony_ciMODULE_PARM_DESC(noloop, "Disable the AUX Loopback command while probing for the AUX port"); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic bool i8042_notimeout; 998c2ecf20Sopenharmony_cimodule_param_named(notimeout, i8042_notimeout, bool, 0); 1008c2ecf20Sopenharmony_ciMODULE_PARM_DESC(notimeout, "Ignore timeouts signalled by i8042"); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic bool i8042_kbdreset; 1038c2ecf20Sopenharmony_cimodule_param_named(kbdreset, i8042_kbdreset, bool, 0); 1048c2ecf20Sopenharmony_ciMODULE_PARM_DESC(kbdreset, "Reset device connected to KBD port"); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 1078c2ecf20Sopenharmony_cistatic bool i8042_dritek; 1088c2ecf20Sopenharmony_cimodule_param_named(dritek, i8042_dritek, bool, 0); 1098c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dritek, "Force enable the Dritek keyboard extension"); 1108c2ecf20Sopenharmony_ci#endif 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 1138c2ecf20Sopenharmony_cistatic bool i8042_nopnp; 1148c2ecf20Sopenharmony_cimodule_param_named(nopnp, i8042_nopnp, bool, 0); 1158c2ecf20Sopenharmony_ciMODULE_PARM_DESC(nopnp, "Do not use PNP to detect controller settings"); 1168c2ecf20Sopenharmony_ci#endif 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci#define DEBUG 1198c2ecf20Sopenharmony_ci#ifdef DEBUG 1208c2ecf20Sopenharmony_cistatic bool i8042_debug; 1218c2ecf20Sopenharmony_cimodule_param_named(debug, i8042_debug, bool, 0600); 1228c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Turn i8042 debugging mode on and off"); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic bool i8042_unmask_kbd_data; 1258c2ecf20Sopenharmony_cimodule_param_named(unmask_kbd_data, i8042_unmask_kbd_data, bool, 0600); 1268c2ecf20Sopenharmony_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]"); 1278c2ecf20Sopenharmony_ci#endif 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic bool i8042_present; 1308c2ecf20Sopenharmony_cistatic bool i8042_bypass_aux_irq_test; 1318c2ecf20Sopenharmony_cistatic char i8042_kbd_firmware_id[128]; 1328c2ecf20Sopenharmony_cistatic char i8042_aux_firmware_id[128]; 1338c2ecf20Sopenharmony_cistatic struct fwnode_handle *i8042_kbd_fwnode; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci#include "i8042.h" 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci/* 1388c2ecf20Sopenharmony_ci * i8042_lock protects serialization between i8042_command and 1398c2ecf20Sopenharmony_ci * the interrupt handler. 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(i8042_lock); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/* 1448c2ecf20Sopenharmony_ci * Writers to AUX and KBD ports as well as users issuing i8042_command 1458c2ecf20Sopenharmony_ci * directly should acquire i8042_mutex (by means of calling 1468c2ecf20Sopenharmony_ci * i8042_lock_chip() and i8042_unlock_ship() helpers) to ensure that 1478c2ecf20Sopenharmony_ci * they do not disturb each other (unfortunately in many i8042 1488c2ecf20Sopenharmony_ci * implementations write to one of the ports will immediately abort 1498c2ecf20Sopenharmony_ci * command that is being processed by another port). 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(i8042_mutex); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistruct i8042_port { 1548c2ecf20Sopenharmony_ci struct serio *serio; 1558c2ecf20Sopenharmony_ci int irq; 1568c2ecf20Sopenharmony_ci bool exists; 1578c2ecf20Sopenharmony_ci bool driver_bound; 1588c2ecf20Sopenharmony_ci signed char mux; 1598c2ecf20Sopenharmony_ci}; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci#define I8042_KBD_PORT_NO 0 1628c2ecf20Sopenharmony_ci#define I8042_AUX_PORT_NO 1 1638c2ecf20Sopenharmony_ci#define I8042_MUX_PORT_NO 2 1648c2ecf20Sopenharmony_ci#define I8042_NUM_PORTS (I8042_NUM_MUX_PORTS + 2) 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic struct i8042_port i8042_ports[I8042_NUM_PORTS]; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic unsigned char i8042_initial_ctr; 1698c2ecf20Sopenharmony_cistatic unsigned char i8042_ctr; 1708c2ecf20Sopenharmony_cistatic bool i8042_mux_present; 1718c2ecf20Sopenharmony_cistatic bool i8042_kbd_irq_registered; 1728c2ecf20Sopenharmony_cistatic bool i8042_aux_irq_registered; 1738c2ecf20Sopenharmony_cistatic unsigned char i8042_suppress_kbd_ack; 1748c2ecf20Sopenharmony_cistatic struct platform_device *i8042_platform_device; 1758c2ecf20Sopenharmony_cistatic struct notifier_block i8042_kbd_bind_notifier_block; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic irqreturn_t i8042_interrupt(int irq, void *dev_id); 1788c2ecf20Sopenharmony_cistatic bool (*i8042_platform_filter)(unsigned char data, unsigned char str, 1798c2ecf20Sopenharmony_ci struct serio *serio); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_civoid i8042_lock_chip(void) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci mutex_lock(&i8042_mutex); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(i8042_lock_chip); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_civoid i8042_unlock_chip(void) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci mutex_unlock(&i8042_mutex); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(i8042_unlock_chip); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ciint i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str, 1948c2ecf20Sopenharmony_ci struct serio *serio)) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci unsigned long flags; 1978c2ecf20Sopenharmony_ci int ret = 0; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci spin_lock_irqsave(&i8042_lock, flags); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (i8042_platform_filter) { 2028c2ecf20Sopenharmony_ci ret = -EBUSY; 2038c2ecf20Sopenharmony_ci goto out; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci i8042_platform_filter = filter; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ciout: 2098c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i8042_lock, flags); 2108c2ecf20Sopenharmony_ci return ret; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(i8042_install_filter); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ciint i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str, 2158c2ecf20Sopenharmony_ci struct serio *port)) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci unsigned long flags; 2188c2ecf20Sopenharmony_ci int ret = 0; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci spin_lock_irqsave(&i8042_lock, flags); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (i8042_platform_filter != filter) { 2238c2ecf20Sopenharmony_ci ret = -EINVAL; 2248c2ecf20Sopenharmony_ci goto out; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci i8042_platform_filter = NULL; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ciout: 2308c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i8042_lock, flags); 2318c2ecf20Sopenharmony_ci return ret; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(i8042_remove_filter); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/* 2368c2ecf20Sopenharmony_ci * The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to 2378c2ecf20Sopenharmony_ci * be ready for reading values from it / writing values to it. 2388c2ecf20Sopenharmony_ci * Called always with i8042_lock held. 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic int i8042_wait_read(void) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci int i = 0; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci while ((~i8042_read_status() & I8042_STR_OBF) && (i < I8042_CTL_TIMEOUT)) { 2468c2ecf20Sopenharmony_ci udelay(50); 2478c2ecf20Sopenharmony_ci i++; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci return -(i == I8042_CTL_TIMEOUT); 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic int i8042_wait_write(void) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci int i = 0; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci while ((i8042_read_status() & I8042_STR_IBF) && (i < I8042_CTL_TIMEOUT)) { 2578c2ecf20Sopenharmony_ci udelay(50); 2588c2ecf20Sopenharmony_ci i++; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci return -(i == I8042_CTL_TIMEOUT); 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci/* 2648c2ecf20Sopenharmony_ci * i8042_flush() flushes all data that may be in the keyboard and mouse buffers 2658c2ecf20Sopenharmony_ci * of the i8042 down the toilet. 2668c2ecf20Sopenharmony_ci */ 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic int i8042_flush(void) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci unsigned long flags; 2718c2ecf20Sopenharmony_ci unsigned char data, str; 2728c2ecf20Sopenharmony_ci int count = 0; 2738c2ecf20Sopenharmony_ci int retval = 0; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci spin_lock_irqsave(&i8042_lock, flags); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci while ((str = i8042_read_status()) & I8042_STR_OBF) { 2788c2ecf20Sopenharmony_ci if (count++ < I8042_BUFFER_SIZE) { 2798c2ecf20Sopenharmony_ci udelay(50); 2808c2ecf20Sopenharmony_ci data = i8042_read_data(); 2818c2ecf20Sopenharmony_ci dbg("%02x <- i8042 (flush, %s)\n", 2828c2ecf20Sopenharmony_ci data, str & I8042_STR_AUXDATA ? "aux" : "kbd"); 2838c2ecf20Sopenharmony_ci } else { 2848c2ecf20Sopenharmony_ci retval = -EIO; 2858c2ecf20Sopenharmony_ci break; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i8042_lock, flags); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci return retval; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci/* 2958c2ecf20Sopenharmony_ci * i8042_command() executes a command on the i8042. It also sends the input 2968c2ecf20Sopenharmony_ci * parameter(s) of the commands to it, and receives the output value(s). The 2978c2ecf20Sopenharmony_ci * parameters are to be stored in the param array, and the output is placed 2988c2ecf20Sopenharmony_ci * into the same array. The number of the parameters and output values is 2998c2ecf20Sopenharmony_ci * encoded in bits 8-11 of the command number. 3008c2ecf20Sopenharmony_ci */ 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic int __i8042_command(unsigned char *param, int command) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci int i, error; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (i8042_noloop && command == I8042_CMD_AUX_LOOP) 3078c2ecf20Sopenharmony_ci return -1; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci error = i8042_wait_write(); 3108c2ecf20Sopenharmony_ci if (error) 3118c2ecf20Sopenharmony_ci return error; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci dbg("%02x -> i8042 (command)\n", command & 0xff); 3148c2ecf20Sopenharmony_ci i8042_write_command(command & 0xff); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci for (i = 0; i < ((command >> 12) & 0xf); i++) { 3178c2ecf20Sopenharmony_ci error = i8042_wait_write(); 3188c2ecf20Sopenharmony_ci if (error) { 3198c2ecf20Sopenharmony_ci dbg(" -- i8042 (wait write timeout)\n"); 3208c2ecf20Sopenharmony_ci return error; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci dbg("%02x -> i8042 (parameter)\n", param[i]); 3238c2ecf20Sopenharmony_ci i8042_write_data(param[i]); 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci for (i = 0; i < ((command >> 8) & 0xf); i++) { 3278c2ecf20Sopenharmony_ci error = i8042_wait_read(); 3288c2ecf20Sopenharmony_ci if (error) { 3298c2ecf20Sopenharmony_ci dbg(" -- i8042 (wait read timeout)\n"); 3308c2ecf20Sopenharmony_ci return error; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (command == I8042_CMD_AUX_LOOP && 3348c2ecf20Sopenharmony_ci !(i8042_read_status() & I8042_STR_AUXDATA)) { 3358c2ecf20Sopenharmony_ci dbg(" -- i8042 (auxerr)\n"); 3368c2ecf20Sopenharmony_ci return -1; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci param[i] = i8042_read_data(); 3408c2ecf20Sopenharmony_ci dbg("%02x <- i8042 (return)\n", param[i]); 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci return 0; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ciint i8042_command(unsigned char *param, int command) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci unsigned long flags; 3498c2ecf20Sopenharmony_ci int retval; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (!i8042_present) 3528c2ecf20Sopenharmony_ci return -1; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci spin_lock_irqsave(&i8042_lock, flags); 3558c2ecf20Sopenharmony_ci retval = __i8042_command(param, command); 3568c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i8042_lock, flags); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci return retval; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(i8042_command); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci/* 3638c2ecf20Sopenharmony_ci * i8042_kbd_write() sends a byte out through the keyboard interface. 3648c2ecf20Sopenharmony_ci */ 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic int i8042_kbd_write(struct serio *port, unsigned char c) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci unsigned long flags; 3698c2ecf20Sopenharmony_ci int retval = 0; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci spin_lock_irqsave(&i8042_lock, flags); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (!(retval = i8042_wait_write())) { 3748c2ecf20Sopenharmony_ci dbg("%02x -> i8042 (kbd-data)\n", c); 3758c2ecf20Sopenharmony_ci i8042_write_data(c); 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i8042_lock, flags); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci return retval; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci/* 3848c2ecf20Sopenharmony_ci * i8042_aux_write() sends a byte out through the aux interface. 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic int i8042_aux_write(struct serio *serio, unsigned char c) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci struct i8042_port *port = serio->port_data; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci return i8042_command(&c, port->mux == -1 ? 3928c2ecf20Sopenharmony_ci I8042_CMD_AUX_SEND : 3938c2ecf20Sopenharmony_ci I8042_CMD_MUX_SEND + port->mux); 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci/* 3988c2ecf20Sopenharmony_ci * i8042_port_close attempts to clear AUX or KBD port state by disabling 3998c2ecf20Sopenharmony_ci * and then re-enabling it. 4008c2ecf20Sopenharmony_ci */ 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic void i8042_port_close(struct serio *serio) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci int irq_bit; 4058c2ecf20Sopenharmony_ci int disable_bit; 4068c2ecf20Sopenharmony_ci const char *port_name; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (serio == i8042_ports[I8042_AUX_PORT_NO].serio) { 4098c2ecf20Sopenharmony_ci irq_bit = I8042_CTR_AUXINT; 4108c2ecf20Sopenharmony_ci disable_bit = I8042_CTR_AUXDIS; 4118c2ecf20Sopenharmony_ci port_name = "AUX"; 4128c2ecf20Sopenharmony_ci } else { 4138c2ecf20Sopenharmony_ci irq_bit = I8042_CTR_KBDINT; 4148c2ecf20Sopenharmony_ci disable_bit = I8042_CTR_KBDDIS; 4158c2ecf20Sopenharmony_ci port_name = "KBD"; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci i8042_ctr &= ~irq_bit; 4198c2ecf20Sopenharmony_ci if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) 4208c2ecf20Sopenharmony_ci pr_warn("Can't write CTR while closing %s port\n", port_name); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci udelay(50); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci i8042_ctr &= ~disable_bit; 4258c2ecf20Sopenharmony_ci i8042_ctr |= irq_bit; 4268c2ecf20Sopenharmony_ci if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) 4278c2ecf20Sopenharmony_ci pr_err("Can't reactivate %s port\n", port_name); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* 4308c2ecf20Sopenharmony_ci * See if there is any data appeared while we were messing with 4318c2ecf20Sopenharmony_ci * port state. 4328c2ecf20Sopenharmony_ci */ 4338c2ecf20Sopenharmony_ci i8042_interrupt(0, NULL); 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci/* 4378c2ecf20Sopenharmony_ci * i8042_start() is called by serio core when port is about to finish 4388c2ecf20Sopenharmony_ci * registering. It will mark port as existing so i8042_interrupt can 4398c2ecf20Sopenharmony_ci * start sending data through it. 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_cistatic int i8042_start(struct serio *serio) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci struct i8042_port *port = serio->port_data; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci device_set_wakeup_capable(&serio->dev, true); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci /* 4488c2ecf20Sopenharmony_ci * On platforms using suspend-to-idle, allow the keyboard to 4498c2ecf20Sopenharmony_ci * wake up the system from sleep by enabling keyboard wakeups 4508c2ecf20Sopenharmony_ci * by default. This is consistent with keyboard wakeup 4518c2ecf20Sopenharmony_ci * behavior on many platforms using suspend-to-RAM (ACPI S3) 4528c2ecf20Sopenharmony_ci * by default. 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_ci if (pm_suspend_default_s2idle() && 4558c2ecf20Sopenharmony_ci serio == i8042_ports[I8042_KBD_PORT_NO].serio) { 4568c2ecf20Sopenharmony_ci device_set_wakeup_enable(&serio->dev, true); 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci spin_lock_irq(&i8042_lock); 4608c2ecf20Sopenharmony_ci port->exists = true; 4618c2ecf20Sopenharmony_ci spin_unlock_irq(&i8042_lock); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci return 0; 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci/* 4678c2ecf20Sopenharmony_ci * i8042_stop() marks serio port as non-existing so i8042_interrupt 4688c2ecf20Sopenharmony_ci * will not try to send data to the port that is about to go away. 4698c2ecf20Sopenharmony_ci * The function is called by serio core as part of unregister procedure. 4708c2ecf20Sopenharmony_ci */ 4718c2ecf20Sopenharmony_cistatic void i8042_stop(struct serio *serio) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci struct i8042_port *port = serio->port_data; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci spin_lock_irq(&i8042_lock); 4768c2ecf20Sopenharmony_ci port->exists = false; 4778c2ecf20Sopenharmony_ci port->serio = NULL; 4788c2ecf20Sopenharmony_ci spin_unlock_irq(&i8042_lock); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci /* 4818c2ecf20Sopenharmony_ci * We need to make sure that interrupt handler finishes using 4828c2ecf20Sopenharmony_ci * our serio port before we return from this function. 4838c2ecf20Sopenharmony_ci * We synchronize with both AUX and KBD IRQs because there is 4848c2ecf20Sopenharmony_ci * a (very unlikely) chance that AUX IRQ is raised for KBD port 4858c2ecf20Sopenharmony_ci * and vice versa. 4868c2ecf20Sopenharmony_ci */ 4878c2ecf20Sopenharmony_ci synchronize_irq(I8042_AUX_IRQ); 4888c2ecf20Sopenharmony_ci synchronize_irq(I8042_KBD_IRQ); 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci/* 4928c2ecf20Sopenharmony_ci * i8042_filter() filters out unwanted bytes from the input data stream. 4938c2ecf20Sopenharmony_ci * It is called from i8042_interrupt and thus is running with interrupts 4948c2ecf20Sopenharmony_ci * off and i8042_lock held. 4958c2ecf20Sopenharmony_ci */ 4968c2ecf20Sopenharmony_cistatic bool i8042_filter(unsigned char data, unsigned char str, 4978c2ecf20Sopenharmony_ci struct serio *serio) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci if (unlikely(i8042_suppress_kbd_ack)) { 5008c2ecf20Sopenharmony_ci if ((~str & I8042_STR_AUXDATA) && 5018c2ecf20Sopenharmony_ci (data == 0xfa || data == 0xfe)) { 5028c2ecf20Sopenharmony_ci i8042_suppress_kbd_ack--; 5038c2ecf20Sopenharmony_ci dbg("Extra keyboard ACK - filtered out\n"); 5048c2ecf20Sopenharmony_ci return true; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (i8042_platform_filter && i8042_platform_filter(data, str, serio)) { 5098c2ecf20Sopenharmony_ci dbg("Filtered out by platform filter\n"); 5108c2ecf20Sopenharmony_ci return true; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci return false; 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci/* 5178c2ecf20Sopenharmony_ci * i8042_interrupt() is the most important function in this driver - 5188c2ecf20Sopenharmony_ci * it handles the interrupts from the i8042, and sends incoming bytes 5198c2ecf20Sopenharmony_ci * to the upper layers. 5208c2ecf20Sopenharmony_ci */ 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic irqreturn_t i8042_interrupt(int irq, void *dev_id) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci struct i8042_port *port; 5258c2ecf20Sopenharmony_ci struct serio *serio; 5268c2ecf20Sopenharmony_ci unsigned long flags; 5278c2ecf20Sopenharmony_ci unsigned char str, data; 5288c2ecf20Sopenharmony_ci unsigned int dfl; 5298c2ecf20Sopenharmony_ci unsigned int port_no; 5308c2ecf20Sopenharmony_ci bool filtered; 5318c2ecf20Sopenharmony_ci int ret = 1; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci spin_lock_irqsave(&i8042_lock, flags); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci str = i8042_read_status(); 5368c2ecf20Sopenharmony_ci if (unlikely(~str & I8042_STR_OBF)) { 5378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i8042_lock, flags); 5388c2ecf20Sopenharmony_ci if (irq) 5398c2ecf20Sopenharmony_ci dbg("Interrupt %d, without any data\n", irq); 5408c2ecf20Sopenharmony_ci ret = 0; 5418c2ecf20Sopenharmony_ci goto out; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci data = i8042_read_data(); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (i8042_mux_present && (str & I8042_STR_AUXDATA)) { 5478c2ecf20Sopenharmony_ci static unsigned long last_transmit; 5488c2ecf20Sopenharmony_ci static unsigned char last_str; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci dfl = 0; 5518c2ecf20Sopenharmony_ci if (str & I8042_STR_MUXERR) { 5528c2ecf20Sopenharmony_ci dbg("MUX error, status is %02x, data is %02x\n", 5538c2ecf20Sopenharmony_ci str, data); 5548c2ecf20Sopenharmony_ci/* 5558c2ecf20Sopenharmony_ci * When MUXERR condition is signalled the data register can only contain 5568c2ecf20Sopenharmony_ci * 0xfd, 0xfe or 0xff if implementation follows the spec. Unfortunately 5578c2ecf20Sopenharmony_ci * it is not always the case. Some KBCs also report 0xfc when there is 5588c2ecf20Sopenharmony_ci * nothing connected to the port while others sometimes get confused which 5598c2ecf20Sopenharmony_ci * port the data came from and signal error leaving the data intact. They 5608c2ecf20Sopenharmony_ci * _do not_ revert to legacy mode (actually I've never seen KBC reverting 5618c2ecf20Sopenharmony_ci * to legacy mode yet, when we see one we'll add proper handling). 5628c2ecf20Sopenharmony_ci * Anyway, we process 0xfc, 0xfd, 0xfe and 0xff as timeouts, and for the 5638c2ecf20Sopenharmony_ci * rest assume that the data came from the same serio last byte 5648c2ecf20Sopenharmony_ci * was transmitted (if transmission happened not too long ago). 5658c2ecf20Sopenharmony_ci */ 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci switch (data) { 5688c2ecf20Sopenharmony_ci default: 5698c2ecf20Sopenharmony_ci if (time_before(jiffies, last_transmit + HZ/10)) { 5708c2ecf20Sopenharmony_ci str = last_str; 5718c2ecf20Sopenharmony_ci break; 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci fallthrough; /* report timeout */ 5748c2ecf20Sopenharmony_ci case 0xfc: 5758c2ecf20Sopenharmony_ci case 0xfd: 5768c2ecf20Sopenharmony_ci case 0xfe: dfl = SERIO_TIMEOUT; data = 0xfe; break; 5778c2ecf20Sopenharmony_ci case 0xff: dfl = SERIO_PARITY; data = 0xfe; break; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci port_no = I8042_MUX_PORT_NO + ((str >> 6) & 3); 5828c2ecf20Sopenharmony_ci last_str = str; 5838c2ecf20Sopenharmony_ci last_transmit = jiffies; 5848c2ecf20Sopenharmony_ci } else { 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) | 5878c2ecf20Sopenharmony_ci ((str & I8042_STR_TIMEOUT && !i8042_notimeout) ? SERIO_TIMEOUT : 0); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci port_no = (str & I8042_STR_AUXDATA) ? 5908c2ecf20Sopenharmony_ci I8042_AUX_PORT_NO : I8042_KBD_PORT_NO; 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci port = &i8042_ports[port_no]; 5948c2ecf20Sopenharmony_ci serio = port->exists ? port->serio : NULL; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci filter_dbg(port->driver_bound, data, "<- i8042 (interrupt, %d, %d%s%s)\n", 5978c2ecf20Sopenharmony_ci port_no, irq, 5988c2ecf20Sopenharmony_ci dfl & SERIO_PARITY ? ", bad parity" : "", 5998c2ecf20Sopenharmony_ci dfl & SERIO_TIMEOUT ? ", timeout" : ""); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci filtered = i8042_filter(data, str, serio); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i8042_lock, flags); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (likely(serio && !filtered)) 6068c2ecf20Sopenharmony_ci serio_interrupt(serio, data, dfl); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci out: 6098c2ecf20Sopenharmony_ci return IRQ_RETVAL(ret); 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci/* 6138c2ecf20Sopenharmony_ci * i8042_enable_kbd_port enables keyboard port on chip 6148c2ecf20Sopenharmony_ci */ 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_cistatic int i8042_enable_kbd_port(void) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci i8042_ctr &= ~I8042_CTR_KBDDIS; 6198c2ecf20Sopenharmony_ci i8042_ctr |= I8042_CTR_KBDINT; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { 6228c2ecf20Sopenharmony_ci i8042_ctr &= ~I8042_CTR_KBDINT; 6238c2ecf20Sopenharmony_ci i8042_ctr |= I8042_CTR_KBDDIS; 6248c2ecf20Sopenharmony_ci pr_err("Failed to enable KBD port\n"); 6258c2ecf20Sopenharmony_ci return -EIO; 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci return 0; 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci/* 6328c2ecf20Sopenharmony_ci * i8042_enable_aux_port enables AUX (mouse) port on chip 6338c2ecf20Sopenharmony_ci */ 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_cistatic int i8042_enable_aux_port(void) 6368c2ecf20Sopenharmony_ci{ 6378c2ecf20Sopenharmony_ci i8042_ctr &= ~I8042_CTR_AUXDIS; 6388c2ecf20Sopenharmony_ci i8042_ctr |= I8042_CTR_AUXINT; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { 6418c2ecf20Sopenharmony_ci i8042_ctr &= ~I8042_CTR_AUXINT; 6428c2ecf20Sopenharmony_ci i8042_ctr |= I8042_CTR_AUXDIS; 6438c2ecf20Sopenharmony_ci pr_err("Failed to enable AUX port\n"); 6448c2ecf20Sopenharmony_ci return -EIO; 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci return 0; 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci/* 6518c2ecf20Sopenharmony_ci * i8042_enable_mux_ports enables 4 individual AUX ports after 6528c2ecf20Sopenharmony_ci * the controller has been switched into Multiplexed mode 6538c2ecf20Sopenharmony_ci */ 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cistatic int i8042_enable_mux_ports(void) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci unsigned char param; 6588c2ecf20Sopenharmony_ci int i; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci for (i = 0; i < I8042_NUM_MUX_PORTS; i++) { 6618c2ecf20Sopenharmony_ci i8042_command(¶m, I8042_CMD_MUX_PFX + i); 6628c2ecf20Sopenharmony_ci i8042_command(¶m, I8042_CMD_AUX_ENABLE); 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci return i8042_enable_aux_port(); 6668c2ecf20Sopenharmony_ci} 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci/* 6698c2ecf20Sopenharmony_ci * i8042_set_mux_mode checks whether the controller has an 6708c2ecf20Sopenharmony_ci * active multiplexor and puts the chip into Multiplexed (true) 6718c2ecf20Sopenharmony_ci * or Legacy (false) mode. 6728c2ecf20Sopenharmony_ci */ 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cistatic int i8042_set_mux_mode(bool multiplex, unsigned char *mux_version) 6758c2ecf20Sopenharmony_ci{ 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci unsigned char param, val; 6788c2ecf20Sopenharmony_ci/* 6798c2ecf20Sopenharmony_ci * Get rid of bytes in the queue. 6808c2ecf20Sopenharmony_ci */ 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci i8042_flush(); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci/* 6858c2ecf20Sopenharmony_ci * Internal loopback test - send three bytes, they should come back from the 6868c2ecf20Sopenharmony_ci * mouse interface, the last should be version. 6878c2ecf20Sopenharmony_ci */ 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci param = val = 0xf0; 6908c2ecf20Sopenharmony_ci if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != val) 6918c2ecf20Sopenharmony_ci return -1; 6928c2ecf20Sopenharmony_ci param = val = multiplex ? 0x56 : 0xf6; 6938c2ecf20Sopenharmony_ci if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != val) 6948c2ecf20Sopenharmony_ci return -1; 6958c2ecf20Sopenharmony_ci param = val = multiplex ? 0xa4 : 0xa5; 6968c2ecf20Sopenharmony_ci if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param == val) 6978c2ecf20Sopenharmony_ci return -1; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci/* 7008c2ecf20Sopenharmony_ci * Workaround for interference with USB Legacy emulation 7018c2ecf20Sopenharmony_ci * that causes a v10.12 MUX to be found. 7028c2ecf20Sopenharmony_ci */ 7038c2ecf20Sopenharmony_ci if (param == 0xac) 7048c2ecf20Sopenharmony_ci return -1; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci if (mux_version) 7078c2ecf20Sopenharmony_ci *mux_version = param; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci return 0; 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci/* 7138c2ecf20Sopenharmony_ci * i8042_check_mux() checks whether the controller supports the PS/2 Active 7148c2ecf20Sopenharmony_ci * Multiplexing specification by Synaptics, Phoenix, Insyde and 7158c2ecf20Sopenharmony_ci * LCS/Telegraphics. 7168c2ecf20Sopenharmony_ci */ 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_cistatic int i8042_check_mux(void) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci unsigned char mux_version; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci if (i8042_set_mux_mode(true, &mux_version)) 7238c2ecf20Sopenharmony_ci return -1; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci pr_info("Detected active multiplexing controller, rev %d.%d\n", 7268c2ecf20Sopenharmony_ci (mux_version >> 4) & 0xf, mux_version & 0xf); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci/* 7298c2ecf20Sopenharmony_ci * Disable all muxed ports by disabling AUX. 7308c2ecf20Sopenharmony_ci */ 7318c2ecf20Sopenharmony_ci i8042_ctr |= I8042_CTR_AUXDIS; 7328c2ecf20Sopenharmony_ci i8042_ctr &= ~I8042_CTR_AUXINT; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { 7358c2ecf20Sopenharmony_ci pr_err("Failed to disable AUX port, can't use MUX\n"); 7368c2ecf20Sopenharmony_ci return -EIO; 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci i8042_mux_present = true; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci return 0; 7428c2ecf20Sopenharmony_ci} 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci/* 7458c2ecf20Sopenharmony_ci * The following is used to test AUX IRQ delivery. 7468c2ecf20Sopenharmony_ci */ 7478c2ecf20Sopenharmony_cistatic struct completion i8042_aux_irq_delivered; 7488c2ecf20Sopenharmony_cistatic bool i8042_irq_being_tested; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_cistatic irqreturn_t i8042_aux_test_irq(int irq, void *dev_id) 7518c2ecf20Sopenharmony_ci{ 7528c2ecf20Sopenharmony_ci unsigned long flags; 7538c2ecf20Sopenharmony_ci unsigned char str, data; 7548c2ecf20Sopenharmony_ci int ret = 0; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci spin_lock_irqsave(&i8042_lock, flags); 7578c2ecf20Sopenharmony_ci str = i8042_read_status(); 7588c2ecf20Sopenharmony_ci if (str & I8042_STR_OBF) { 7598c2ecf20Sopenharmony_ci data = i8042_read_data(); 7608c2ecf20Sopenharmony_ci dbg("%02x <- i8042 (aux_test_irq, %s)\n", 7618c2ecf20Sopenharmony_ci data, str & I8042_STR_AUXDATA ? "aux" : "kbd"); 7628c2ecf20Sopenharmony_ci if (i8042_irq_being_tested && 7638c2ecf20Sopenharmony_ci data == 0xa5 && (str & I8042_STR_AUXDATA)) 7648c2ecf20Sopenharmony_ci complete(&i8042_aux_irq_delivered); 7658c2ecf20Sopenharmony_ci ret = 1; 7668c2ecf20Sopenharmony_ci } 7678c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i8042_lock, flags); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci return IRQ_RETVAL(ret); 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci/* 7738c2ecf20Sopenharmony_ci * i8042_toggle_aux - enables or disables AUX port on i8042 via command and 7748c2ecf20Sopenharmony_ci * verifies success by readinng CTR. Used when testing for presence of AUX 7758c2ecf20Sopenharmony_ci * port. 7768c2ecf20Sopenharmony_ci */ 7778c2ecf20Sopenharmony_cistatic int i8042_toggle_aux(bool on) 7788c2ecf20Sopenharmony_ci{ 7798c2ecf20Sopenharmony_ci unsigned char param; 7808c2ecf20Sopenharmony_ci int i; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci if (i8042_command(¶m, 7838c2ecf20Sopenharmony_ci on ? I8042_CMD_AUX_ENABLE : I8042_CMD_AUX_DISABLE)) 7848c2ecf20Sopenharmony_ci return -1; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci /* some chips need some time to set the I8042_CTR_AUXDIS bit */ 7878c2ecf20Sopenharmony_ci for (i = 0; i < 100; i++) { 7888c2ecf20Sopenharmony_ci udelay(50); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci if (i8042_command(¶m, I8042_CMD_CTL_RCTR)) 7918c2ecf20Sopenharmony_ci return -1; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci if (!(param & I8042_CTR_AUXDIS) == on) 7948c2ecf20Sopenharmony_ci return 0; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci return -1; 7988c2ecf20Sopenharmony_ci} 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci/* 8018c2ecf20Sopenharmony_ci * i8042_check_aux() applies as much paranoia as it can at detecting 8028c2ecf20Sopenharmony_ci * the presence of an AUX interface. 8038c2ecf20Sopenharmony_ci */ 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_cistatic int i8042_check_aux(void) 8068c2ecf20Sopenharmony_ci{ 8078c2ecf20Sopenharmony_ci int retval = -1; 8088c2ecf20Sopenharmony_ci bool irq_registered = false; 8098c2ecf20Sopenharmony_ci bool aux_loop_broken = false; 8108c2ecf20Sopenharmony_ci unsigned long flags; 8118c2ecf20Sopenharmony_ci unsigned char param; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci/* 8148c2ecf20Sopenharmony_ci * Get rid of bytes in the queue. 8158c2ecf20Sopenharmony_ci */ 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci i8042_flush(); 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci/* 8208c2ecf20Sopenharmony_ci * Internal loopback test - filters out AT-type i8042's. Unfortunately 8218c2ecf20Sopenharmony_ci * SiS screwed up and their 5597 doesn't support the LOOP command even 8228c2ecf20Sopenharmony_ci * though it has an AUX port. 8238c2ecf20Sopenharmony_ci */ 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci param = 0x5a; 8268c2ecf20Sopenharmony_ci retval = i8042_command(¶m, I8042_CMD_AUX_LOOP); 8278c2ecf20Sopenharmony_ci if (retval || param != 0x5a) { 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci/* 8308c2ecf20Sopenharmony_ci * External connection test - filters out AT-soldered PS/2 i8042's 8318c2ecf20Sopenharmony_ci * 0x00 - no error, 0x01-0x03 - clock/data stuck, 0xff - general error 8328c2ecf20Sopenharmony_ci * 0xfa - no error on some notebooks which ignore the spec 8338c2ecf20Sopenharmony_ci * Because it's common for chipsets to return error on perfectly functioning 8348c2ecf20Sopenharmony_ci * AUX ports, we test for this only when the LOOP command failed. 8358c2ecf20Sopenharmony_ci */ 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci if (i8042_command(¶m, I8042_CMD_AUX_TEST) || 8388c2ecf20Sopenharmony_ci (param && param != 0xfa && param != 0xff)) 8398c2ecf20Sopenharmony_ci return -1; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci/* 8428c2ecf20Sopenharmony_ci * If AUX_LOOP completed without error but returned unexpected data 8438c2ecf20Sopenharmony_ci * mark it as broken 8448c2ecf20Sopenharmony_ci */ 8458c2ecf20Sopenharmony_ci if (!retval) 8468c2ecf20Sopenharmony_ci aux_loop_broken = true; 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci/* 8508c2ecf20Sopenharmony_ci * Bit assignment test - filters out PS/2 i8042's in AT mode 8518c2ecf20Sopenharmony_ci */ 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if (i8042_toggle_aux(false)) { 8548c2ecf20Sopenharmony_ci pr_warn("Failed to disable AUX port, but continuing anyway... Is this a SiS?\n"); 8558c2ecf20Sopenharmony_ci pr_warn("If AUX port is really absent please use the 'i8042.noaux' option\n"); 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci if (i8042_toggle_aux(true)) 8598c2ecf20Sopenharmony_ci return -1; 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci/* 8628c2ecf20Sopenharmony_ci * Reset keyboard (needed on some laptops to successfully detect 8638c2ecf20Sopenharmony_ci * touchpad, e.g., some Gigabyte laptop models with Elantech 8648c2ecf20Sopenharmony_ci * touchpads). 8658c2ecf20Sopenharmony_ci */ 8668c2ecf20Sopenharmony_ci if (i8042_kbdreset) { 8678c2ecf20Sopenharmony_ci pr_warn("Attempting to reset device connected to KBD port\n"); 8688c2ecf20Sopenharmony_ci i8042_kbd_write(NULL, (unsigned char) 0xff); 8698c2ecf20Sopenharmony_ci } 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci/* 8728c2ecf20Sopenharmony_ci * Test AUX IRQ delivery to make sure BIOS did not grab the IRQ and 8738c2ecf20Sopenharmony_ci * used it for a PCI card or somethig else. 8748c2ecf20Sopenharmony_ci */ 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci if (i8042_noloop || i8042_bypass_aux_irq_test || aux_loop_broken) { 8778c2ecf20Sopenharmony_ci/* 8788c2ecf20Sopenharmony_ci * Without LOOP command we can't test AUX IRQ delivery. Assume the port 8798c2ecf20Sopenharmony_ci * is working and hope we are right. 8808c2ecf20Sopenharmony_ci */ 8818c2ecf20Sopenharmony_ci retval = 0; 8828c2ecf20Sopenharmony_ci goto out; 8838c2ecf20Sopenharmony_ci } 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci if (request_irq(I8042_AUX_IRQ, i8042_aux_test_irq, IRQF_SHARED, 8868c2ecf20Sopenharmony_ci "i8042", i8042_platform_device)) 8878c2ecf20Sopenharmony_ci goto out; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci irq_registered = true; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci if (i8042_enable_aux_port()) 8928c2ecf20Sopenharmony_ci goto out; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci spin_lock_irqsave(&i8042_lock, flags); 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci init_completion(&i8042_aux_irq_delivered); 8978c2ecf20Sopenharmony_ci i8042_irq_being_tested = true; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci param = 0xa5; 9008c2ecf20Sopenharmony_ci retval = __i8042_command(¶m, I8042_CMD_AUX_LOOP & 0xf0ff); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i8042_lock, flags); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci if (retval) 9058c2ecf20Sopenharmony_ci goto out; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci if (wait_for_completion_timeout(&i8042_aux_irq_delivered, 9088c2ecf20Sopenharmony_ci msecs_to_jiffies(250)) == 0) { 9098c2ecf20Sopenharmony_ci/* 9108c2ecf20Sopenharmony_ci * AUX IRQ was never delivered so we need to flush the controller to 9118c2ecf20Sopenharmony_ci * get rid of the byte we put there; otherwise keyboard may not work. 9128c2ecf20Sopenharmony_ci */ 9138c2ecf20Sopenharmony_ci dbg(" -- i8042 (aux irq test timeout)\n"); 9148c2ecf20Sopenharmony_ci i8042_flush(); 9158c2ecf20Sopenharmony_ci retval = -1; 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci out: 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci/* 9218c2ecf20Sopenharmony_ci * Disable the interface. 9228c2ecf20Sopenharmony_ci */ 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci i8042_ctr |= I8042_CTR_AUXDIS; 9258c2ecf20Sopenharmony_ci i8042_ctr &= ~I8042_CTR_AUXINT; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) 9288c2ecf20Sopenharmony_ci retval = -1; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci if (irq_registered) 9318c2ecf20Sopenharmony_ci free_irq(I8042_AUX_IRQ, i8042_platform_device); 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci return retval; 9348c2ecf20Sopenharmony_ci} 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_cistatic int i8042_controller_check(void) 9378c2ecf20Sopenharmony_ci{ 9388c2ecf20Sopenharmony_ci if (i8042_flush()) { 9398c2ecf20Sopenharmony_ci pr_info("No controller found\n"); 9408c2ecf20Sopenharmony_ci return -ENODEV; 9418c2ecf20Sopenharmony_ci } 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci return 0; 9448c2ecf20Sopenharmony_ci} 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_cistatic int i8042_controller_selftest(void) 9478c2ecf20Sopenharmony_ci{ 9488c2ecf20Sopenharmony_ci unsigned char param; 9498c2ecf20Sopenharmony_ci int i = 0; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci /* 9528c2ecf20Sopenharmony_ci * We try this 5 times; on some really fragile systems this does not 9538c2ecf20Sopenharmony_ci * take the first time... 9548c2ecf20Sopenharmony_ci */ 9558c2ecf20Sopenharmony_ci do { 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci if (i8042_command(¶m, I8042_CMD_CTL_TEST)) { 9588c2ecf20Sopenharmony_ci pr_err("i8042 controller selftest timeout\n"); 9598c2ecf20Sopenharmony_ci return -ENODEV; 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci if (param == I8042_RET_CTL_TEST) 9638c2ecf20Sopenharmony_ci return 0; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci dbg("i8042 controller selftest: %#x != %#x\n", 9668c2ecf20Sopenharmony_ci param, I8042_RET_CTL_TEST); 9678c2ecf20Sopenharmony_ci msleep(50); 9688c2ecf20Sopenharmony_ci } while (i++ < 5); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 9718c2ecf20Sopenharmony_ci /* 9728c2ecf20Sopenharmony_ci * On x86, we don't fail entire i8042 initialization if controller 9738c2ecf20Sopenharmony_ci * reset fails in hopes that keyboard port will still be functional 9748c2ecf20Sopenharmony_ci * and user will still get a working keyboard. This is especially 9758c2ecf20Sopenharmony_ci * important on netbooks. On other arches we trust hardware more. 9768c2ecf20Sopenharmony_ci */ 9778c2ecf20Sopenharmony_ci pr_info("giving up on controller selftest, continuing anyway...\n"); 9788c2ecf20Sopenharmony_ci return 0; 9798c2ecf20Sopenharmony_ci#else 9808c2ecf20Sopenharmony_ci pr_err("i8042 controller selftest failed\n"); 9818c2ecf20Sopenharmony_ci return -EIO; 9828c2ecf20Sopenharmony_ci#endif 9838c2ecf20Sopenharmony_ci} 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci/* 9868c2ecf20Sopenharmony_ci * i8042_controller init initializes the i8042 controller, and, 9878c2ecf20Sopenharmony_ci * most importantly, sets it into non-xlated mode if that's 9888c2ecf20Sopenharmony_ci * desired. 9898c2ecf20Sopenharmony_ci */ 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_cistatic int i8042_controller_init(void) 9928c2ecf20Sopenharmony_ci{ 9938c2ecf20Sopenharmony_ci unsigned long flags; 9948c2ecf20Sopenharmony_ci int n = 0; 9958c2ecf20Sopenharmony_ci unsigned char ctr[2]; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci/* 9988c2ecf20Sopenharmony_ci * Save the CTR for restore on unload / reboot. 9998c2ecf20Sopenharmony_ci */ 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci do { 10028c2ecf20Sopenharmony_ci if (n >= 10) { 10038c2ecf20Sopenharmony_ci pr_err("Unable to get stable CTR read\n"); 10048c2ecf20Sopenharmony_ci return -EIO; 10058c2ecf20Sopenharmony_ci } 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci if (n != 0) 10088c2ecf20Sopenharmony_ci udelay(50); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci if (i8042_command(&ctr[n++ % 2], I8042_CMD_CTL_RCTR)) { 10118c2ecf20Sopenharmony_ci pr_err("Can't read CTR while initializing i8042\n"); 10128c2ecf20Sopenharmony_ci return i8042_probe_defer ? -EPROBE_DEFER : -EIO; 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci } while (n < 2 || ctr[0] != ctr[1]); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci i8042_initial_ctr = i8042_ctr = ctr[0]; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci/* 10208c2ecf20Sopenharmony_ci * Disable the keyboard interface and interrupt. 10218c2ecf20Sopenharmony_ci */ 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci i8042_ctr |= I8042_CTR_KBDDIS; 10248c2ecf20Sopenharmony_ci i8042_ctr &= ~I8042_CTR_KBDINT; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci/* 10278c2ecf20Sopenharmony_ci * Handle keylock. 10288c2ecf20Sopenharmony_ci */ 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci spin_lock_irqsave(&i8042_lock, flags); 10318c2ecf20Sopenharmony_ci if (~i8042_read_status() & I8042_STR_KEYLOCK) { 10328c2ecf20Sopenharmony_ci if (i8042_unlock) 10338c2ecf20Sopenharmony_ci i8042_ctr |= I8042_CTR_IGNKEYLOCK; 10348c2ecf20Sopenharmony_ci else 10358c2ecf20Sopenharmony_ci pr_warn("Warning: Keylock active\n"); 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i8042_lock, flags); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci/* 10408c2ecf20Sopenharmony_ci * If the chip is configured into nontranslated mode by the BIOS, don't 10418c2ecf20Sopenharmony_ci * bother enabling translating and be happy. 10428c2ecf20Sopenharmony_ci */ 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci if (~i8042_ctr & I8042_CTR_XLATE) 10458c2ecf20Sopenharmony_ci i8042_direct = true; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci/* 10488c2ecf20Sopenharmony_ci * Set nontranslated mode for the kbd interface if requested by an option. 10498c2ecf20Sopenharmony_ci * After this the kbd interface becomes a simple serial in/out, like the aux 10508c2ecf20Sopenharmony_ci * interface is. We don't do this by default, since it can confuse notebook 10518c2ecf20Sopenharmony_ci * BIOSes. 10528c2ecf20Sopenharmony_ci */ 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci if (i8042_direct) 10558c2ecf20Sopenharmony_ci i8042_ctr &= ~I8042_CTR_XLATE; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci/* 10588c2ecf20Sopenharmony_ci * Write CTR back. 10598c2ecf20Sopenharmony_ci */ 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { 10628c2ecf20Sopenharmony_ci pr_err("Can't write CTR while initializing i8042\n"); 10638c2ecf20Sopenharmony_ci return -EIO; 10648c2ecf20Sopenharmony_ci } 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci/* 10678c2ecf20Sopenharmony_ci * Flush whatever accumulated while we were disabling keyboard port. 10688c2ecf20Sopenharmony_ci */ 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci i8042_flush(); 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci return 0; 10738c2ecf20Sopenharmony_ci} 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci/* 10778c2ecf20Sopenharmony_ci * Reset the controller and reset CRT to the original value set by BIOS. 10788c2ecf20Sopenharmony_ci */ 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_cistatic void i8042_controller_reset(bool s2r_wants_reset) 10818c2ecf20Sopenharmony_ci{ 10828c2ecf20Sopenharmony_ci i8042_flush(); 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci/* 10858c2ecf20Sopenharmony_ci * Disable both KBD and AUX interfaces so they don't get in the way 10868c2ecf20Sopenharmony_ci */ 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci i8042_ctr |= I8042_CTR_KBDDIS | I8042_CTR_AUXDIS; 10898c2ecf20Sopenharmony_ci i8042_ctr &= ~(I8042_CTR_KBDINT | I8042_CTR_AUXINT); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) 10928c2ecf20Sopenharmony_ci pr_warn("Can't write CTR while resetting\n"); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci/* 10958c2ecf20Sopenharmony_ci * Disable MUX mode if present. 10968c2ecf20Sopenharmony_ci */ 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci if (i8042_mux_present) 10998c2ecf20Sopenharmony_ci i8042_set_mux_mode(false, NULL); 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci/* 11028c2ecf20Sopenharmony_ci * Reset the controller if requested. 11038c2ecf20Sopenharmony_ci */ 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci if (i8042_reset == I8042_RESET_ALWAYS || 11068c2ecf20Sopenharmony_ci (i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) { 11078c2ecf20Sopenharmony_ci i8042_controller_selftest(); 11088c2ecf20Sopenharmony_ci } 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci/* 11118c2ecf20Sopenharmony_ci * Restore the original control register setting. 11128c2ecf20Sopenharmony_ci */ 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci if (i8042_command(&i8042_initial_ctr, I8042_CMD_CTL_WCTR)) 11158c2ecf20Sopenharmony_ci pr_warn("Can't restore CTR\n"); 11168c2ecf20Sopenharmony_ci} 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci/* 11208c2ecf20Sopenharmony_ci * i8042_panic_blink() will turn the keyboard LEDs on or off and is called 11218c2ecf20Sopenharmony_ci * when kernel panics. Flashing LEDs is useful for users running X who may 11228c2ecf20Sopenharmony_ci * not see the console and will help distinguishing panics from "real" 11238c2ecf20Sopenharmony_ci * lockups. 11248c2ecf20Sopenharmony_ci * 11258c2ecf20Sopenharmony_ci * Note that DELAY has a limit of 10ms so we will not get stuck here 11268c2ecf20Sopenharmony_ci * waiting for KBC to free up even if KBD interrupt is off 11278c2ecf20Sopenharmony_ci */ 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci#define DELAY do { mdelay(1); if (++delay > 10) return delay; } while(0) 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_cistatic long i8042_panic_blink(int state) 11328c2ecf20Sopenharmony_ci{ 11338c2ecf20Sopenharmony_ci long delay = 0; 11348c2ecf20Sopenharmony_ci char led; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci led = (state) ? 0x01 | 0x04 : 0; 11378c2ecf20Sopenharmony_ci while (i8042_read_status() & I8042_STR_IBF) 11388c2ecf20Sopenharmony_ci DELAY; 11398c2ecf20Sopenharmony_ci dbg("%02x -> i8042 (panic blink)\n", 0xed); 11408c2ecf20Sopenharmony_ci i8042_suppress_kbd_ack = 2; 11418c2ecf20Sopenharmony_ci i8042_write_data(0xed); /* set leds */ 11428c2ecf20Sopenharmony_ci DELAY; 11438c2ecf20Sopenharmony_ci while (i8042_read_status() & I8042_STR_IBF) 11448c2ecf20Sopenharmony_ci DELAY; 11458c2ecf20Sopenharmony_ci DELAY; 11468c2ecf20Sopenharmony_ci dbg("%02x -> i8042 (panic blink)\n", led); 11478c2ecf20Sopenharmony_ci i8042_write_data(led); 11488c2ecf20Sopenharmony_ci DELAY; 11498c2ecf20Sopenharmony_ci return delay; 11508c2ecf20Sopenharmony_ci} 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci#undef DELAY 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 11558c2ecf20Sopenharmony_cistatic void i8042_dritek_enable(void) 11568c2ecf20Sopenharmony_ci{ 11578c2ecf20Sopenharmony_ci unsigned char param = 0x90; 11588c2ecf20Sopenharmony_ci int error; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci error = i8042_command(¶m, 0x1059); 11618c2ecf20Sopenharmony_ci if (error) 11628c2ecf20Sopenharmony_ci pr_warn("Failed to enable DRITEK extension: %d\n", error); 11638c2ecf20Sopenharmony_ci} 11648c2ecf20Sopenharmony_ci#endif 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci/* 11698c2ecf20Sopenharmony_ci * Here we try to reset everything back to a state we had 11708c2ecf20Sopenharmony_ci * before suspending. 11718c2ecf20Sopenharmony_ci */ 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_cistatic int i8042_controller_resume(bool s2r_wants_reset) 11748c2ecf20Sopenharmony_ci{ 11758c2ecf20Sopenharmony_ci int error; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci error = i8042_controller_check(); 11788c2ecf20Sopenharmony_ci if (error) 11798c2ecf20Sopenharmony_ci return error; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci if (i8042_reset == I8042_RESET_ALWAYS || 11828c2ecf20Sopenharmony_ci (i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) { 11838c2ecf20Sopenharmony_ci error = i8042_controller_selftest(); 11848c2ecf20Sopenharmony_ci if (error) 11858c2ecf20Sopenharmony_ci return error; 11868c2ecf20Sopenharmony_ci } 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci/* 11898c2ecf20Sopenharmony_ci * Restore original CTR value and disable all ports 11908c2ecf20Sopenharmony_ci */ 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci i8042_ctr = i8042_initial_ctr; 11938c2ecf20Sopenharmony_ci if (i8042_direct) 11948c2ecf20Sopenharmony_ci i8042_ctr &= ~I8042_CTR_XLATE; 11958c2ecf20Sopenharmony_ci i8042_ctr |= I8042_CTR_AUXDIS | I8042_CTR_KBDDIS; 11968c2ecf20Sopenharmony_ci i8042_ctr &= ~(I8042_CTR_AUXINT | I8042_CTR_KBDINT); 11978c2ecf20Sopenharmony_ci if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { 11988c2ecf20Sopenharmony_ci pr_warn("Can't write CTR to resume, retrying...\n"); 11998c2ecf20Sopenharmony_ci msleep(50); 12008c2ecf20Sopenharmony_ci if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { 12018c2ecf20Sopenharmony_ci pr_err("CTR write retry failed\n"); 12028c2ecf20Sopenharmony_ci return -EIO; 12038c2ecf20Sopenharmony_ci } 12048c2ecf20Sopenharmony_ci } 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 12088c2ecf20Sopenharmony_ci if (i8042_dritek) 12098c2ecf20Sopenharmony_ci i8042_dritek_enable(); 12108c2ecf20Sopenharmony_ci#endif 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci if (i8042_mux_present) { 12138c2ecf20Sopenharmony_ci if (i8042_set_mux_mode(true, NULL) || i8042_enable_mux_ports()) 12148c2ecf20Sopenharmony_ci pr_warn("failed to resume active multiplexor, mouse won't work\n"); 12158c2ecf20Sopenharmony_ci } else if (i8042_ports[I8042_AUX_PORT_NO].serio) 12168c2ecf20Sopenharmony_ci i8042_enable_aux_port(); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci if (i8042_ports[I8042_KBD_PORT_NO].serio) 12198c2ecf20Sopenharmony_ci i8042_enable_kbd_port(); 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci i8042_interrupt(0, NULL); 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci return 0; 12248c2ecf20Sopenharmony_ci} 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci/* 12278c2ecf20Sopenharmony_ci * Here we try to restore the original BIOS settings to avoid 12288c2ecf20Sopenharmony_ci * upsetting it. 12298c2ecf20Sopenharmony_ci */ 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_cistatic int i8042_pm_suspend(struct device *dev) 12328c2ecf20Sopenharmony_ci{ 12338c2ecf20Sopenharmony_ci int i; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci if (pm_suspend_via_firmware()) 12368c2ecf20Sopenharmony_ci i8042_controller_reset(true); 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci /* Set up serio interrupts for system wakeup. */ 12398c2ecf20Sopenharmony_ci for (i = 0; i < I8042_NUM_PORTS; i++) { 12408c2ecf20Sopenharmony_ci struct serio *serio = i8042_ports[i].serio; 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci if (serio && device_may_wakeup(&serio->dev)) 12438c2ecf20Sopenharmony_ci enable_irq_wake(i8042_ports[i].irq); 12448c2ecf20Sopenharmony_ci } 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci return 0; 12478c2ecf20Sopenharmony_ci} 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_cistatic int i8042_pm_resume_noirq(struct device *dev) 12508c2ecf20Sopenharmony_ci{ 12518c2ecf20Sopenharmony_ci if (!pm_resume_via_firmware()) 12528c2ecf20Sopenharmony_ci i8042_interrupt(0, NULL); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci return 0; 12558c2ecf20Sopenharmony_ci} 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_cistatic int i8042_pm_resume(struct device *dev) 12588c2ecf20Sopenharmony_ci{ 12598c2ecf20Sopenharmony_ci bool want_reset; 12608c2ecf20Sopenharmony_ci int i; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci for (i = 0; i < I8042_NUM_PORTS; i++) { 12638c2ecf20Sopenharmony_ci struct serio *serio = i8042_ports[i].serio; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci if (serio && device_may_wakeup(&serio->dev)) 12668c2ecf20Sopenharmony_ci disable_irq_wake(i8042_ports[i].irq); 12678c2ecf20Sopenharmony_ci } 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci /* 12708c2ecf20Sopenharmony_ci * If platform firmware was not going to be involved in suspend, we did 12718c2ecf20Sopenharmony_ci * not restore the controller state to whatever it had been at boot 12728c2ecf20Sopenharmony_ci * time, so we do not need to do anything. 12738c2ecf20Sopenharmony_ci */ 12748c2ecf20Sopenharmony_ci if (!pm_suspend_via_firmware()) 12758c2ecf20Sopenharmony_ci return 0; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci /* 12788c2ecf20Sopenharmony_ci * We only need to reset the controller if we are resuming after handing 12798c2ecf20Sopenharmony_ci * off control to the platform firmware, otherwise we can simply restore 12808c2ecf20Sopenharmony_ci * the mode. 12818c2ecf20Sopenharmony_ci */ 12828c2ecf20Sopenharmony_ci want_reset = pm_resume_via_firmware(); 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci return i8042_controller_resume(want_reset); 12858c2ecf20Sopenharmony_ci} 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_cistatic int i8042_pm_thaw(struct device *dev) 12888c2ecf20Sopenharmony_ci{ 12898c2ecf20Sopenharmony_ci i8042_interrupt(0, NULL); 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci return 0; 12928c2ecf20Sopenharmony_ci} 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_cistatic int i8042_pm_reset(struct device *dev) 12958c2ecf20Sopenharmony_ci{ 12968c2ecf20Sopenharmony_ci i8042_controller_reset(false); 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci return 0; 12998c2ecf20Sopenharmony_ci} 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_cistatic int i8042_pm_restore(struct device *dev) 13028c2ecf20Sopenharmony_ci{ 13038c2ecf20Sopenharmony_ci return i8042_controller_resume(false); 13048c2ecf20Sopenharmony_ci} 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_cistatic const struct dev_pm_ops i8042_pm_ops = { 13078c2ecf20Sopenharmony_ci .suspend = i8042_pm_suspend, 13088c2ecf20Sopenharmony_ci .resume_noirq = i8042_pm_resume_noirq, 13098c2ecf20Sopenharmony_ci .resume = i8042_pm_resume, 13108c2ecf20Sopenharmony_ci .thaw = i8042_pm_thaw, 13118c2ecf20Sopenharmony_ci .poweroff = i8042_pm_reset, 13128c2ecf20Sopenharmony_ci .restore = i8042_pm_restore, 13138c2ecf20Sopenharmony_ci}; 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci/* 13188c2ecf20Sopenharmony_ci * We need to reset the 8042 back to original mode on system shutdown, 13198c2ecf20Sopenharmony_ci * because otherwise BIOSes will be confused. 13208c2ecf20Sopenharmony_ci */ 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_cistatic void i8042_shutdown(struct platform_device *dev) 13238c2ecf20Sopenharmony_ci{ 13248c2ecf20Sopenharmony_ci i8042_controller_reset(false); 13258c2ecf20Sopenharmony_ci} 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_cistatic int i8042_create_kbd_port(void) 13288c2ecf20Sopenharmony_ci{ 13298c2ecf20Sopenharmony_ci struct serio *serio; 13308c2ecf20Sopenharmony_ci struct i8042_port *port = &i8042_ports[I8042_KBD_PORT_NO]; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci serio = kzalloc(sizeof(struct serio), GFP_KERNEL); 13338c2ecf20Sopenharmony_ci if (!serio) 13348c2ecf20Sopenharmony_ci return -ENOMEM; 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci serio->id.type = i8042_direct ? SERIO_8042 : SERIO_8042_XL; 13378c2ecf20Sopenharmony_ci serio->write = i8042_dumbkbd ? NULL : i8042_kbd_write; 13388c2ecf20Sopenharmony_ci serio->start = i8042_start; 13398c2ecf20Sopenharmony_ci serio->stop = i8042_stop; 13408c2ecf20Sopenharmony_ci serio->close = i8042_port_close; 13418c2ecf20Sopenharmony_ci serio->ps2_cmd_mutex = &i8042_mutex; 13428c2ecf20Sopenharmony_ci serio->port_data = port; 13438c2ecf20Sopenharmony_ci serio->dev.parent = &i8042_platform_device->dev; 13448c2ecf20Sopenharmony_ci strlcpy(serio->name, "i8042 KBD port", sizeof(serio->name)); 13458c2ecf20Sopenharmony_ci strlcpy(serio->phys, I8042_KBD_PHYS_DESC, sizeof(serio->phys)); 13468c2ecf20Sopenharmony_ci strlcpy(serio->firmware_id, i8042_kbd_firmware_id, 13478c2ecf20Sopenharmony_ci sizeof(serio->firmware_id)); 13488c2ecf20Sopenharmony_ci set_primary_fwnode(&serio->dev, i8042_kbd_fwnode); 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci port->serio = serio; 13518c2ecf20Sopenharmony_ci port->irq = I8042_KBD_IRQ; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci return 0; 13548c2ecf20Sopenharmony_ci} 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_cistatic int i8042_create_aux_port(int idx) 13578c2ecf20Sopenharmony_ci{ 13588c2ecf20Sopenharmony_ci struct serio *serio; 13598c2ecf20Sopenharmony_ci int port_no = idx < 0 ? I8042_AUX_PORT_NO : I8042_MUX_PORT_NO + idx; 13608c2ecf20Sopenharmony_ci struct i8042_port *port = &i8042_ports[port_no]; 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci serio = kzalloc(sizeof(struct serio), GFP_KERNEL); 13638c2ecf20Sopenharmony_ci if (!serio) 13648c2ecf20Sopenharmony_ci return -ENOMEM; 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci serio->id.type = SERIO_8042; 13678c2ecf20Sopenharmony_ci serio->write = i8042_aux_write; 13688c2ecf20Sopenharmony_ci serio->start = i8042_start; 13698c2ecf20Sopenharmony_ci serio->stop = i8042_stop; 13708c2ecf20Sopenharmony_ci serio->ps2_cmd_mutex = &i8042_mutex; 13718c2ecf20Sopenharmony_ci serio->port_data = port; 13728c2ecf20Sopenharmony_ci serio->dev.parent = &i8042_platform_device->dev; 13738c2ecf20Sopenharmony_ci if (idx < 0) { 13748c2ecf20Sopenharmony_ci strlcpy(serio->name, "i8042 AUX port", sizeof(serio->name)); 13758c2ecf20Sopenharmony_ci strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys)); 13768c2ecf20Sopenharmony_ci strlcpy(serio->firmware_id, i8042_aux_firmware_id, 13778c2ecf20Sopenharmony_ci sizeof(serio->firmware_id)); 13788c2ecf20Sopenharmony_ci serio->close = i8042_port_close; 13798c2ecf20Sopenharmony_ci } else { 13808c2ecf20Sopenharmony_ci snprintf(serio->name, sizeof(serio->name), "i8042 AUX%d port", idx); 13818c2ecf20Sopenharmony_ci snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, idx + 1); 13828c2ecf20Sopenharmony_ci strlcpy(serio->firmware_id, i8042_aux_firmware_id, 13838c2ecf20Sopenharmony_ci sizeof(serio->firmware_id)); 13848c2ecf20Sopenharmony_ci } 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci port->serio = serio; 13878c2ecf20Sopenharmony_ci port->mux = idx; 13888c2ecf20Sopenharmony_ci port->irq = I8042_AUX_IRQ; 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci return 0; 13918c2ecf20Sopenharmony_ci} 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_cistatic void i8042_free_kbd_port(void) 13948c2ecf20Sopenharmony_ci{ 13958c2ecf20Sopenharmony_ci kfree(i8042_ports[I8042_KBD_PORT_NO].serio); 13968c2ecf20Sopenharmony_ci i8042_ports[I8042_KBD_PORT_NO].serio = NULL; 13978c2ecf20Sopenharmony_ci} 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_cistatic void i8042_free_aux_ports(void) 14008c2ecf20Sopenharmony_ci{ 14018c2ecf20Sopenharmony_ci int i; 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci for (i = I8042_AUX_PORT_NO; i < I8042_NUM_PORTS; i++) { 14048c2ecf20Sopenharmony_ci kfree(i8042_ports[i].serio); 14058c2ecf20Sopenharmony_ci i8042_ports[i].serio = NULL; 14068c2ecf20Sopenharmony_ci } 14078c2ecf20Sopenharmony_ci} 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_cistatic void i8042_register_ports(void) 14108c2ecf20Sopenharmony_ci{ 14118c2ecf20Sopenharmony_ci int i; 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci for (i = 0; i < I8042_NUM_PORTS; i++) { 14148c2ecf20Sopenharmony_ci struct serio *serio = i8042_ports[i].serio; 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci if (!serio) 14178c2ecf20Sopenharmony_ci continue; 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci printk(KERN_INFO "serio: %s at %#lx,%#lx irq %d\n", 14208c2ecf20Sopenharmony_ci serio->name, 14218c2ecf20Sopenharmony_ci (unsigned long) I8042_DATA_REG, 14228c2ecf20Sopenharmony_ci (unsigned long) I8042_COMMAND_REG, 14238c2ecf20Sopenharmony_ci i8042_ports[i].irq); 14248c2ecf20Sopenharmony_ci serio_register_port(serio); 14258c2ecf20Sopenharmony_ci } 14268c2ecf20Sopenharmony_ci} 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_cistatic void i8042_unregister_ports(void) 14298c2ecf20Sopenharmony_ci{ 14308c2ecf20Sopenharmony_ci int i; 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci for (i = 0; i < I8042_NUM_PORTS; i++) { 14338c2ecf20Sopenharmony_ci if (i8042_ports[i].serio) { 14348c2ecf20Sopenharmony_ci serio_unregister_port(i8042_ports[i].serio); 14358c2ecf20Sopenharmony_ci i8042_ports[i].serio = NULL; 14368c2ecf20Sopenharmony_ci } 14378c2ecf20Sopenharmony_ci } 14388c2ecf20Sopenharmony_ci} 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_cistatic void i8042_free_irqs(void) 14418c2ecf20Sopenharmony_ci{ 14428c2ecf20Sopenharmony_ci if (i8042_aux_irq_registered) 14438c2ecf20Sopenharmony_ci free_irq(I8042_AUX_IRQ, i8042_platform_device); 14448c2ecf20Sopenharmony_ci if (i8042_kbd_irq_registered) 14458c2ecf20Sopenharmony_ci free_irq(I8042_KBD_IRQ, i8042_platform_device); 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci i8042_aux_irq_registered = i8042_kbd_irq_registered = false; 14488c2ecf20Sopenharmony_ci} 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_cistatic int i8042_setup_aux(void) 14518c2ecf20Sopenharmony_ci{ 14528c2ecf20Sopenharmony_ci int (*aux_enable)(void); 14538c2ecf20Sopenharmony_ci int error; 14548c2ecf20Sopenharmony_ci int i; 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci if (i8042_check_aux()) 14578c2ecf20Sopenharmony_ci return -ENODEV; 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci if (i8042_nomux || i8042_check_mux()) { 14608c2ecf20Sopenharmony_ci error = i8042_create_aux_port(-1); 14618c2ecf20Sopenharmony_ci if (error) 14628c2ecf20Sopenharmony_ci goto err_free_ports; 14638c2ecf20Sopenharmony_ci aux_enable = i8042_enable_aux_port; 14648c2ecf20Sopenharmony_ci } else { 14658c2ecf20Sopenharmony_ci for (i = 0; i < I8042_NUM_MUX_PORTS; i++) { 14668c2ecf20Sopenharmony_ci error = i8042_create_aux_port(i); 14678c2ecf20Sopenharmony_ci if (error) 14688c2ecf20Sopenharmony_ci goto err_free_ports; 14698c2ecf20Sopenharmony_ci } 14708c2ecf20Sopenharmony_ci aux_enable = i8042_enable_mux_ports; 14718c2ecf20Sopenharmony_ci } 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci error = request_irq(I8042_AUX_IRQ, i8042_interrupt, IRQF_SHARED, 14748c2ecf20Sopenharmony_ci "i8042", i8042_platform_device); 14758c2ecf20Sopenharmony_ci if (error) 14768c2ecf20Sopenharmony_ci goto err_free_ports; 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci error = aux_enable(); 14798c2ecf20Sopenharmony_ci if (error) 14808c2ecf20Sopenharmony_ci goto err_free_irq; 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci i8042_aux_irq_registered = true; 14838c2ecf20Sopenharmony_ci return 0; 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci err_free_irq: 14868c2ecf20Sopenharmony_ci free_irq(I8042_AUX_IRQ, i8042_platform_device); 14878c2ecf20Sopenharmony_ci err_free_ports: 14888c2ecf20Sopenharmony_ci i8042_free_aux_ports(); 14898c2ecf20Sopenharmony_ci return error; 14908c2ecf20Sopenharmony_ci} 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_cistatic int i8042_setup_kbd(void) 14938c2ecf20Sopenharmony_ci{ 14948c2ecf20Sopenharmony_ci int error; 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci error = i8042_create_kbd_port(); 14978c2ecf20Sopenharmony_ci if (error) 14988c2ecf20Sopenharmony_ci return error; 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci error = request_irq(I8042_KBD_IRQ, i8042_interrupt, IRQF_SHARED, 15018c2ecf20Sopenharmony_ci "i8042", i8042_platform_device); 15028c2ecf20Sopenharmony_ci if (error) 15038c2ecf20Sopenharmony_ci goto err_free_port; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci error = i8042_enable_kbd_port(); 15068c2ecf20Sopenharmony_ci if (error) 15078c2ecf20Sopenharmony_ci goto err_free_irq; 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci i8042_kbd_irq_registered = true; 15108c2ecf20Sopenharmony_ci return 0; 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci err_free_irq: 15138c2ecf20Sopenharmony_ci free_irq(I8042_KBD_IRQ, i8042_platform_device); 15148c2ecf20Sopenharmony_ci err_free_port: 15158c2ecf20Sopenharmony_ci i8042_free_kbd_port(); 15168c2ecf20Sopenharmony_ci return error; 15178c2ecf20Sopenharmony_ci} 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_cistatic int i8042_kbd_bind_notifier(struct notifier_block *nb, 15208c2ecf20Sopenharmony_ci unsigned long action, void *data) 15218c2ecf20Sopenharmony_ci{ 15228c2ecf20Sopenharmony_ci struct device *dev = data; 15238c2ecf20Sopenharmony_ci struct serio *serio = to_serio_port(dev); 15248c2ecf20Sopenharmony_ci struct i8042_port *port = serio->port_data; 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci if (serio != i8042_ports[I8042_KBD_PORT_NO].serio) 15278c2ecf20Sopenharmony_ci return 0; 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci switch (action) { 15308c2ecf20Sopenharmony_ci case BUS_NOTIFY_BOUND_DRIVER: 15318c2ecf20Sopenharmony_ci port->driver_bound = true; 15328c2ecf20Sopenharmony_ci break; 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci case BUS_NOTIFY_UNBIND_DRIVER: 15358c2ecf20Sopenharmony_ci port->driver_bound = false; 15368c2ecf20Sopenharmony_ci break; 15378c2ecf20Sopenharmony_ci } 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci return 0; 15408c2ecf20Sopenharmony_ci} 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_cistatic int i8042_probe(struct platform_device *dev) 15438c2ecf20Sopenharmony_ci{ 15448c2ecf20Sopenharmony_ci int error; 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci if (i8042_reset == I8042_RESET_ALWAYS) { 15478c2ecf20Sopenharmony_ci error = i8042_controller_selftest(); 15488c2ecf20Sopenharmony_ci if (error) 15498c2ecf20Sopenharmony_ci return error; 15508c2ecf20Sopenharmony_ci } 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci error = i8042_controller_init(); 15538c2ecf20Sopenharmony_ci if (error) 15548c2ecf20Sopenharmony_ci return error; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 15578c2ecf20Sopenharmony_ci if (i8042_dritek) 15588c2ecf20Sopenharmony_ci i8042_dritek_enable(); 15598c2ecf20Sopenharmony_ci#endif 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci if (!i8042_noaux) { 15628c2ecf20Sopenharmony_ci error = i8042_setup_aux(); 15638c2ecf20Sopenharmony_ci if (error && error != -ENODEV && error != -EBUSY) 15648c2ecf20Sopenharmony_ci goto out_fail; 15658c2ecf20Sopenharmony_ci } 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci if (!i8042_nokbd) { 15688c2ecf20Sopenharmony_ci error = i8042_setup_kbd(); 15698c2ecf20Sopenharmony_ci if (error) 15708c2ecf20Sopenharmony_ci goto out_fail; 15718c2ecf20Sopenharmony_ci } 15728c2ecf20Sopenharmony_ci/* 15738c2ecf20Sopenharmony_ci * Ok, everything is ready, let's register all serio ports 15748c2ecf20Sopenharmony_ci */ 15758c2ecf20Sopenharmony_ci i8042_register_ports(); 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci return 0; 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci out_fail: 15808c2ecf20Sopenharmony_ci i8042_free_aux_ports(); /* in case KBD failed but AUX not */ 15818c2ecf20Sopenharmony_ci i8042_free_irqs(); 15828c2ecf20Sopenharmony_ci i8042_controller_reset(false); 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci return error; 15858c2ecf20Sopenharmony_ci} 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_cistatic int i8042_remove(struct platform_device *dev) 15888c2ecf20Sopenharmony_ci{ 15898c2ecf20Sopenharmony_ci i8042_unregister_ports(); 15908c2ecf20Sopenharmony_ci i8042_free_irqs(); 15918c2ecf20Sopenharmony_ci i8042_controller_reset(false); 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci return 0; 15948c2ecf20Sopenharmony_ci} 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_cistatic struct platform_driver i8042_driver = { 15978c2ecf20Sopenharmony_ci .driver = { 15988c2ecf20Sopenharmony_ci .name = "i8042", 15998c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 16008c2ecf20Sopenharmony_ci .pm = &i8042_pm_ops, 16018c2ecf20Sopenharmony_ci#endif 16028c2ecf20Sopenharmony_ci }, 16038c2ecf20Sopenharmony_ci .probe = i8042_probe, 16048c2ecf20Sopenharmony_ci .remove = i8042_remove, 16058c2ecf20Sopenharmony_ci .shutdown = i8042_shutdown, 16068c2ecf20Sopenharmony_ci}; 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_cistatic struct notifier_block i8042_kbd_bind_notifier_block = { 16098c2ecf20Sopenharmony_ci .notifier_call = i8042_kbd_bind_notifier, 16108c2ecf20Sopenharmony_ci}; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_cistatic int __init i8042_init(void) 16138c2ecf20Sopenharmony_ci{ 16148c2ecf20Sopenharmony_ci int err; 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci dbg_init(); 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci err = i8042_platform_init(); 16198c2ecf20Sopenharmony_ci if (err) 16208c2ecf20Sopenharmony_ci return (err == -ENODEV) ? 0 : err; 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci err = i8042_controller_check(); 16238c2ecf20Sopenharmony_ci if (err) 16248c2ecf20Sopenharmony_ci goto err_platform_exit; 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci /* Set this before creating the dev to allow i8042_command to work right away */ 16278c2ecf20Sopenharmony_ci i8042_present = true; 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci err = platform_driver_register(&i8042_driver); 16308c2ecf20Sopenharmony_ci if (err) 16318c2ecf20Sopenharmony_ci goto err_platform_exit; 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_ci i8042_platform_device = platform_device_alloc("i8042", -1); 16348c2ecf20Sopenharmony_ci if (!i8042_platform_device) { 16358c2ecf20Sopenharmony_ci err = -ENOMEM; 16368c2ecf20Sopenharmony_ci goto err_unregister_driver; 16378c2ecf20Sopenharmony_ci } 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci err = platform_device_add(i8042_platform_device); 16408c2ecf20Sopenharmony_ci if (err) 16418c2ecf20Sopenharmony_ci goto err_free_device; 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci bus_register_notifier(&serio_bus, &i8042_kbd_bind_notifier_block); 16448c2ecf20Sopenharmony_ci panic_blink = i8042_panic_blink; 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci return 0; 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_cierr_free_device: 16498c2ecf20Sopenharmony_ci platform_device_put(i8042_platform_device); 16508c2ecf20Sopenharmony_cierr_unregister_driver: 16518c2ecf20Sopenharmony_ci platform_driver_unregister(&i8042_driver); 16528c2ecf20Sopenharmony_ci err_platform_exit: 16538c2ecf20Sopenharmony_ci i8042_platform_exit(); 16548c2ecf20Sopenharmony_ci return err; 16558c2ecf20Sopenharmony_ci} 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_cistatic void __exit i8042_exit(void) 16588c2ecf20Sopenharmony_ci{ 16598c2ecf20Sopenharmony_ci if (!i8042_present) 16608c2ecf20Sopenharmony_ci return; 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci platform_device_unregister(i8042_platform_device); 16638c2ecf20Sopenharmony_ci platform_driver_unregister(&i8042_driver); 16648c2ecf20Sopenharmony_ci i8042_platform_exit(); 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci bus_unregister_notifier(&serio_bus, &i8042_kbd_bind_notifier_block); 16678c2ecf20Sopenharmony_ci panic_blink = NULL; 16688c2ecf20Sopenharmony_ci} 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_cimodule_init(i8042_init); 16718c2ecf20Sopenharmony_cimodule_exit(i8042_exit); 1672