xref: /openbmc/linux/drivers/input/serio/i8042.c (revision 6052abf8)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  i8042 keyboard and mouse controller driver for Linux
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  Copyright (c) 1999-2004 Vojtech Pavlik
61da177e4SLinus Torvalds  */
71da177e4SLinus Torvalds 
81da177e4SLinus Torvalds 
94eb3c30bSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
104eb3c30bSJoe Perches 
117e044e05SDmitry Torokhov #include <linux/types.h>
121da177e4SLinus Torvalds #include <linux/delay.h>
131da177e4SLinus Torvalds #include <linux/module.h>
141da177e4SLinus Torvalds #include <linux/interrupt.h>
151da177e4SLinus Torvalds #include <linux/ioport.h>
161da177e4SLinus Torvalds #include <linux/init.h>
171da177e4SLinus Torvalds #include <linux/serio.h>
181da177e4SLinus Torvalds #include <linux/err.h>
191da177e4SLinus Torvalds #include <linux/rcupdate.h>
20d052d1beSRussell King #include <linux/platform_device.h>
21553a05b8SMárton Németh #include <linux/i8042.h>
225a0e3ad6STejun Heo #include <linux/slab.h>
231c5dd134SRafael J. Wysocki #include <linux/suspend.h>
24*6052abf8SRajat Jain #include <linux/property.h>
251da177e4SLinus Torvalds 
261da177e4SLinus Torvalds #include <asm/io.h>
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
291da177e4SLinus Torvalds MODULE_DESCRIPTION("i8042 keyboard and mouse controller driver");
301da177e4SLinus Torvalds MODULE_LICENSE("GPL");
311da177e4SLinus Torvalds 
32386b3849SDmitry Torokhov static bool i8042_nokbd;
33945ef0d4SDmitry Torokhov module_param_named(nokbd, i8042_nokbd, bool, 0);
34945ef0d4SDmitry Torokhov MODULE_PARM_DESC(nokbd, "Do not probe or use KBD port.");
35945ef0d4SDmitry Torokhov 
36386b3849SDmitry Torokhov static bool i8042_noaux;
371da177e4SLinus Torvalds module_param_named(noaux, i8042_noaux, bool, 0);
381da177e4SLinus Torvalds MODULE_PARM_DESC(noaux, "Do not probe or use AUX (mouse) port.");
391da177e4SLinus Torvalds 
40e55a3366SDmitry Torokhov static bool i8042_nomux;
411da177e4SLinus Torvalds module_param_named(nomux, i8042_nomux, bool, 0);
422c860a11SDominik Brodowski MODULE_PARM_DESC(nomux, "Do not check whether an active multiplexing controller is present.");
431da177e4SLinus Torvalds 
44386b3849SDmitry Torokhov static bool i8042_unlock;
451da177e4SLinus Torvalds module_param_named(unlock, i8042_unlock, bool, 0);
461da177e4SLinus Torvalds MODULE_PARM_DESC(unlock, "Ignore keyboard lock.");
471da177e4SLinus Torvalds 
48930e1924SMarcos Paulo de Souza enum i8042_controller_reset_mode {
49930e1924SMarcos Paulo de Souza 	I8042_RESET_NEVER,
50930e1924SMarcos Paulo de Souza 	I8042_RESET_ALWAYS,
51930e1924SMarcos Paulo de Souza 	I8042_RESET_ON_S2RAM,
52930e1924SMarcos Paulo de Souza #define I8042_RESET_DEFAULT	I8042_RESET_ON_S2RAM
53930e1924SMarcos Paulo de Souza };
54930e1924SMarcos Paulo de Souza static enum i8042_controller_reset_mode i8042_reset = I8042_RESET_DEFAULT;
55930e1924SMarcos Paulo de Souza static int i8042_set_reset(const char *val, const struct kernel_param *kp)
56930e1924SMarcos Paulo de Souza {
57930e1924SMarcos Paulo de Souza 	enum i8042_controller_reset_mode *arg = kp->arg;
58930e1924SMarcos Paulo de Souza 	int error;
59930e1924SMarcos Paulo de Souza 	bool reset;
60930e1924SMarcos Paulo de Souza 
61930e1924SMarcos Paulo de Souza 	if (val) {
62930e1924SMarcos Paulo de Souza 		error = kstrtobool(val, &reset);
63930e1924SMarcos Paulo de Souza 		if (error)
64930e1924SMarcos Paulo de Souza 			return error;
65930e1924SMarcos Paulo de Souza 	} else {
66930e1924SMarcos Paulo de Souza 		reset = true;
67930e1924SMarcos Paulo de Souza 	}
68930e1924SMarcos Paulo de Souza 
69930e1924SMarcos Paulo de Souza 	*arg = reset ? I8042_RESET_ALWAYS : I8042_RESET_NEVER;
70930e1924SMarcos Paulo de Souza 	return 0;
71930e1924SMarcos Paulo de Souza }
72930e1924SMarcos Paulo de Souza 
73930e1924SMarcos Paulo de Souza static const struct kernel_param_ops param_ops_reset_param = {
74930e1924SMarcos Paulo de Souza 	.flags = KERNEL_PARAM_OPS_FL_NOARG,
75930e1924SMarcos Paulo de Souza 	.set = i8042_set_reset,
76930e1924SMarcos Paulo de Souza };
77930e1924SMarcos Paulo de Souza #define param_check_reset_param(name, p)	\
78930e1924SMarcos Paulo de Souza 	__param_check(name, p, enum i8042_controller_reset_mode)
79930e1924SMarcos Paulo de Souza module_param_named(reset, i8042_reset, reset_param, 0);
80930e1924SMarcos Paulo de Souza MODULE_PARM_DESC(reset, "Reset controller on resume, cleanup or both");
811da177e4SLinus Torvalds 
82386b3849SDmitry Torokhov static bool i8042_direct;
831da177e4SLinus Torvalds module_param_named(direct, i8042_direct, bool, 0);
841da177e4SLinus Torvalds MODULE_PARM_DESC(direct, "Put keyboard port into non-translated mode.");
851da177e4SLinus Torvalds 
86386b3849SDmitry Torokhov static bool i8042_dumbkbd;
871da177e4SLinus Torvalds module_param_named(dumbkbd, i8042_dumbkbd, bool, 0);
881da177e4SLinus Torvalds MODULE_PARM_DESC(dumbkbd, "Pretend that controller can only read data from keyboard");
891da177e4SLinus Torvalds 
90386b3849SDmitry Torokhov static bool i8042_noloop;
911da177e4SLinus Torvalds module_param_named(noloop, i8042_noloop, bool, 0);
921da177e4SLinus Torvalds MODULE_PARM_DESC(noloop, "Disable the AUX Loopback command while probing for the AUX port");
931da177e4SLinus Torvalds 
94f8313ef1SJiri Kosina static bool i8042_notimeout;
95f8313ef1SJiri Kosina module_param_named(notimeout, i8042_notimeout, bool, 0);
96f8313ef1SJiri Kosina MODULE_PARM_DESC(notimeout, "Ignore timeouts signalled by i8042");
97f8313ef1SJiri Kosina 
98148e9a71SSrihari Vijayaraghavan static bool i8042_kbdreset;
99148e9a71SSrihari Vijayaraghavan module_param_named(kbdreset, i8042_kbdreset, bool, 0);
100148e9a71SSrihari Vijayaraghavan MODULE_PARM_DESC(kbdreset, "Reset device connected to KBD port");
101148e9a71SSrihari Vijayaraghavan 
1028987fec0SCarlos Corbacho #ifdef CONFIG_X86
103386b3849SDmitry Torokhov static bool i8042_dritek;
1048987fec0SCarlos Corbacho module_param_named(dritek, i8042_dritek, bool, 0);
1058987fec0SCarlos Corbacho MODULE_PARM_DESC(dritek, "Force enable the Dritek keyboard extension");
1068987fec0SCarlos Corbacho #endif
1078987fec0SCarlos Corbacho 
1081da177e4SLinus Torvalds #ifdef CONFIG_PNP
109386b3849SDmitry Torokhov static bool i8042_nopnp;
1101da177e4SLinus Torvalds module_param_named(nopnp, i8042_nopnp, bool, 0);
1111da177e4SLinus Torvalds MODULE_PARM_DESC(nopnp, "Do not use PNP to detect controller settings");
1121da177e4SLinus Torvalds #endif
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds #define DEBUG
1151da177e4SLinus Torvalds #ifdef DEBUG
116386b3849SDmitry Torokhov static bool i8042_debug;
1171da177e4SLinus Torvalds module_param_named(debug, i8042_debug, bool, 0600);
1181da177e4SLinus Torvalds MODULE_PARM_DESC(debug, "Turn i8042 debugging mode on and off");
119e1443d28SStephen Chandler Paul 
120e1443d28SStephen Chandler Paul static bool i8042_unmask_kbd_data;
121e1443d28SStephen Chandler Paul module_param_named(unmask_kbd_data, i8042_unmask_kbd_data, bool, 0600);
122e1443d28SStephen Chandler Paul MODULE_PARM_DESC(unmask_kbd_data, "Unconditional enable (may reveal sensitive data) of normally sanitize-filtered kbd data traffic debug log [pre-condition: i8042.debug=1 enabled]");
1231da177e4SLinus Torvalds #endif
1241da177e4SLinus Torvalds 
1251c7827aeSDmitry Torokhov static bool i8042_bypass_aux_irq_test;
126a7c5868cSHans de Goede static char i8042_kbd_firmware_id[128];
127a7c5868cSHans de Goede static char i8042_aux_firmware_id[128];
128*6052abf8SRajat Jain static struct fwnode_handle *i8042_kbd_fwnode;
1291c7827aeSDmitry Torokhov 
1301da177e4SLinus Torvalds #include "i8042.h"
1311da177e4SLinus Torvalds 
132181d683dSDmitry Torokhov /*
133181d683dSDmitry Torokhov  * i8042_lock protects serialization between i8042_command and
134181d683dSDmitry Torokhov  * the interrupt handler.
135181d683dSDmitry Torokhov  */
1361da177e4SLinus Torvalds static DEFINE_SPINLOCK(i8042_lock);
1371da177e4SLinus Torvalds 
138181d683dSDmitry Torokhov /*
139181d683dSDmitry Torokhov  * Writers to AUX and KBD ports as well as users issuing i8042_command
140181d683dSDmitry Torokhov  * directly should acquire i8042_mutex (by means of calling
141181d683dSDmitry Torokhov  * i8042_lock_chip() and i8042_unlock_ship() helpers) to ensure that
142181d683dSDmitry Torokhov  * they do not disturb each other (unfortunately in many i8042
143181d683dSDmitry Torokhov  * implementations write to one of the ports will immediately abort
144181d683dSDmitry Torokhov  * command that is being processed by another port).
145181d683dSDmitry Torokhov  */
146181d683dSDmitry Torokhov static DEFINE_MUTEX(i8042_mutex);
147181d683dSDmitry Torokhov 
1481da177e4SLinus Torvalds struct i8042_port {
1491da177e4SLinus Torvalds 	struct serio *serio;
1501da177e4SLinus Torvalds 	int irq;
151386b3849SDmitry Torokhov 	bool exists;
152e1443d28SStephen Chandler Paul 	bool driver_bound;
1531da177e4SLinus Torvalds 	signed char mux;
1541da177e4SLinus Torvalds };
1551da177e4SLinus Torvalds 
1561da177e4SLinus Torvalds #define I8042_KBD_PORT_NO	0
1571da177e4SLinus Torvalds #define I8042_AUX_PORT_NO	1
1581da177e4SLinus Torvalds #define I8042_MUX_PORT_NO	2
1591da177e4SLinus Torvalds #define I8042_NUM_PORTS		(I8042_NUM_MUX_PORTS + 2)
160de9ce703SDmitry Torokhov 
161de9ce703SDmitry Torokhov static struct i8042_port i8042_ports[I8042_NUM_PORTS];
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds static unsigned char i8042_initial_ctr;
1641da177e4SLinus Torvalds static unsigned char i8042_ctr;
165386b3849SDmitry Torokhov static bool i8042_mux_present;
166386b3849SDmitry Torokhov static bool i8042_kbd_irq_registered;
167386b3849SDmitry Torokhov static bool i8042_aux_irq_registered;
168817e6ba3SDmitry Torokhov static unsigned char i8042_suppress_kbd_ack;
1691da177e4SLinus Torvalds static struct platform_device *i8042_platform_device;
170e1443d28SStephen Chandler Paul static struct notifier_block i8042_kbd_bind_notifier_block;
1711da177e4SLinus Torvalds 
1727d12e780SDavid Howells static irqreturn_t i8042_interrupt(int irq, void *dev_id);
173967c9ef9SMatthew Garrett static bool (*i8042_platform_filter)(unsigned char data, unsigned char str,
174967c9ef9SMatthew Garrett 				     struct serio *serio);
1751da177e4SLinus Torvalds 
176181d683dSDmitry Torokhov void i8042_lock_chip(void)
177181d683dSDmitry Torokhov {
178181d683dSDmitry Torokhov 	mutex_lock(&i8042_mutex);
179181d683dSDmitry Torokhov }
180181d683dSDmitry Torokhov EXPORT_SYMBOL(i8042_lock_chip);
181181d683dSDmitry Torokhov 
182181d683dSDmitry Torokhov void i8042_unlock_chip(void)
183181d683dSDmitry Torokhov {
184181d683dSDmitry Torokhov 	mutex_unlock(&i8042_mutex);
185181d683dSDmitry Torokhov }
186181d683dSDmitry Torokhov EXPORT_SYMBOL(i8042_unlock_chip);
187181d683dSDmitry Torokhov 
188967c9ef9SMatthew Garrett int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str,
189967c9ef9SMatthew Garrett 					struct serio *serio))
190967c9ef9SMatthew Garrett {
191967c9ef9SMatthew Garrett 	unsigned long flags;
192967c9ef9SMatthew Garrett 	int ret = 0;
193967c9ef9SMatthew Garrett 
194967c9ef9SMatthew Garrett 	spin_lock_irqsave(&i8042_lock, flags);
195967c9ef9SMatthew Garrett 
196967c9ef9SMatthew Garrett 	if (i8042_platform_filter) {
197967c9ef9SMatthew Garrett 		ret = -EBUSY;
198967c9ef9SMatthew Garrett 		goto out;
199967c9ef9SMatthew Garrett 	}
200967c9ef9SMatthew Garrett 
201967c9ef9SMatthew Garrett 	i8042_platform_filter = filter;
202967c9ef9SMatthew Garrett 
203967c9ef9SMatthew Garrett out:
204967c9ef9SMatthew Garrett 	spin_unlock_irqrestore(&i8042_lock, flags);
205967c9ef9SMatthew Garrett 	return ret;
206967c9ef9SMatthew Garrett }
207967c9ef9SMatthew Garrett EXPORT_SYMBOL(i8042_install_filter);
208967c9ef9SMatthew Garrett 
209967c9ef9SMatthew Garrett int i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str,
210967c9ef9SMatthew Garrett 				       struct serio *port))
211967c9ef9SMatthew Garrett {
212967c9ef9SMatthew Garrett 	unsigned long flags;
213967c9ef9SMatthew Garrett 	int ret = 0;
214967c9ef9SMatthew Garrett 
215967c9ef9SMatthew Garrett 	spin_lock_irqsave(&i8042_lock, flags);
216967c9ef9SMatthew Garrett 
217967c9ef9SMatthew Garrett 	if (i8042_platform_filter != filter) {
218967c9ef9SMatthew Garrett 		ret = -EINVAL;
219967c9ef9SMatthew Garrett 		goto out;
220967c9ef9SMatthew Garrett 	}
221967c9ef9SMatthew Garrett 
222967c9ef9SMatthew Garrett 	i8042_platform_filter = NULL;
223967c9ef9SMatthew Garrett 
224967c9ef9SMatthew Garrett out:
225967c9ef9SMatthew Garrett 	spin_unlock_irqrestore(&i8042_lock, flags);
226967c9ef9SMatthew Garrett 	return ret;
227967c9ef9SMatthew Garrett }
228967c9ef9SMatthew Garrett EXPORT_SYMBOL(i8042_remove_filter);
229967c9ef9SMatthew Garrett 
2301da177e4SLinus Torvalds /*
2311da177e4SLinus Torvalds  * The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to
2321da177e4SLinus Torvalds  * be ready for reading values from it / writing values to it.
2331da177e4SLinus Torvalds  * Called always with i8042_lock held.
2341da177e4SLinus Torvalds  */
2351da177e4SLinus Torvalds 
2361da177e4SLinus Torvalds static int i8042_wait_read(void)
2371da177e4SLinus Torvalds {
2381da177e4SLinus Torvalds 	int i = 0;
239de9ce703SDmitry Torokhov 
2401da177e4SLinus Torvalds 	while ((~i8042_read_status() & I8042_STR_OBF) && (i < I8042_CTL_TIMEOUT)) {
2411da177e4SLinus Torvalds 		udelay(50);
2421da177e4SLinus Torvalds 		i++;
2431da177e4SLinus Torvalds 	}
2441da177e4SLinus Torvalds 	return -(i == I8042_CTL_TIMEOUT);
2451da177e4SLinus Torvalds }
2461da177e4SLinus Torvalds 
2471da177e4SLinus Torvalds static int i8042_wait_write(void)
2481da177e4SLinus Torvalds {
2491da177e4SLinus Torvalds 	int i = 0;
250de9ce703SDmitry Torokhov 
2511da177e4SLinus Torvalds 	while ((i8042_read_status() & I8042_STR_IBF) && (i < I8042_CTL_TIMEOUT)) {
2521da177e4SLinus Torvalds 		udelay(50);
2531da177e4SLinus Torvalds 		i++;
2541da177e4SLinus Torvalds 	}
2551da177e4SLinus Torvalds 	return -(i == I8042_CTL_TIMEOUT);
2561da177e4SLinus Torvalds }
2571da177e4SLinus Torvalds 
2581da177e4SLinus Torvalds /*
2591da177e4SLinus Torvalds  * i8042_flush() flushes all data that may be in the keyboard and mouse buffers
2601da177e4SLinus Torvalds  * of the i8042 down the toilet.
2611da177e4SLinus Torvalds  */
2621da177e4SLinus Torvalds 
2631da177e4SLinus Torvalds static int i8042_flush(void)
2641da177e4SLinus Torvalds {
2651da177e4SLinus Torvalds 	unsigned long flags;
2661da177e4SLinus Torvalds 	unsigned char data, str;
2672f0d2604SAndrey Moiseev 	int count = 0;
2682f0d2604SAndrey Moiseev 	int retval = 0;
2691da177e4SLinus Torvalds 
2701da177e4SLinus Torvalds 	spin_lock_irqsave(&i8042_lock, flags);
2711da177e4SLinus Torvalds 
2722f0d2604SAndrey Moiseev 	while ((str = i8042_read_status()) & I8042_STR_OBF) {
2732f0d2604SAndrey Moiseev 		if (count++ < I8042_BUFFER_SIZE) {
2741da177e4SLinus Torvalds 			udelay(50);
2751da177e4SLinus Torvalds 			data = i8042_read_data();
2764eb3c30bSJoe Perches 			dbg("%02x <- i8042 (flush, %s)\n",
2774eb3c30bSJoe Perches 			    data, str & I8042_STR_AUXDATA ? "aux" : "kbd");
2782f0d2604SAndrey Moiseev 		} else {
2792f0d2604SAndrey Moiseev 			retval = -EIO;
2802f0d2604SAndrey Moiseev 			break;
2812f0d2604SAndrey Moiseev 		}
2821da177e4SLinus Torvalds 	}
2831da177e4SLinus Torvalds 
2841da177e4SLinus Torvalds 	spin_unlock_irqrestore(&i8042_lock, flags);
2851da177e4SLinus Torvalds 
2862f0d2604SAndrey Moiseev 	return retval;
2871da177e4SLinus Torvalds }
2881da177e4SLinus Torvalds 
2891da177e4SLinus Torvalds /*
2901da177e4SLinus Torvalds  * i8042_command() executes a command on the i8042. It also sends the input
2911da177e4SLinus Torvalds  * parameter(s) of the commands to it, and receives the output value(s). The
2921da177e4SLinus Torvalds  * parameters are to be stored in the param array, and the output is placed
2931da177e4SLinus Torvalds  * into the same array. The number of the parameters and output values is
2941da177e4SLinus Torvalds  * encoded in bits 8-11 of the command number.
2951da177e4SLinus Torvalds  */
2961da177e4SLinus Torvalds 
297de9ce703SDmitry Torokhov static int __i8042_command(unsigned char *param, int command)
2981da177e4SLinus Torvalds {
299de9ce703SDmitry Torokhov 	int i, error;
3001da177e4SLinus Torvalds 
3011da177e4SLinus Torvalds 	if (i8042_noloop && command == I8042_CMD_AUX_LOOP)
3021da177e4SLinus Torvalds 		return -1;
3031da177e4SLinus Torvalds 
304de9ce703SDmitry Torokhov 	error = i8042_wait_write();
305de9ce703SDmitry Torokhov 	if (error)
306de9ce703SDmitry Torokhov 		return error;
307463a4f76SDmitry Torokhov 
3084eb3c30bSJoe Perches 	dbg("%02x -> i8042 (command)\n", command & 0xff);
3091da177e4SLinus Torvalds 	i8042_write_command(command & 0xff);
3101da177e4SLinus Torvalds 
3111da177e4SLinus Torvalds 	for (i = 0; i < ((command >> 12) & 0xf); i++) {
312de9ce703SDmitry Torokhov 		error = i8042_wait_write();
3132ea9c236SMarcos Paulo de Souza 		if (error) {
3142ea9c236SMarcos Paulo de Souza 			dbg("     -- i8042 (wait write timeout)\n");
315de9ce703SDmitry Torokhov 			return error;
3162ea9c236SMarcos Paulo de Souza 		}
3174eb3c30bSJoe Perches 		dbg("%02x -> i8042 (parameter)\n", param[i]);
3181da177e4SLinus Torvalds 		i8042_write_data(param[i]);
3191da177e4SLinus Torvalds 	}
3201da177e4SLinus Torvalds 
3211da177e4SLinus Torvalds 	for (i = 0; i < ((command >> 8) & 0xf); i++) {
322de9ce703SDmitry Torokhov 		error = i8042_wait_read();
323de9ce703SDmitry Torokhov 		if (error) {
3242ea9c236SMarcos Paulo de Souza 			dbg("     -- i8042 (wait read timeout)\n");
325de9ce703SDmitry Torokhov 			return error;
326de9ce703SDmitry Torokhov 		}
327463a4f76SDmitry Torokhov 
328463a4f76SDmitry Torokhov 		if (command == I8042_CMD_AUX_LOOP &&
329463a4f76SDmitry Torokhov 		    !(i8042_read_status() & I8042_STR_AUXDATA)) {
3304eb3c30bSJoe Perches 			dbg("     -- i8042 (auxerr)\n");
331de9ce703SDmitry Torokhov 			return -1;
332463a4f76SDmitry Torokhov 		}
333463a4f76SDmitry Torokhov 
3341da177e4SLinus Torvalds 		param[i] = i8042_read_data();
3354eb3c30bSJoe Perches 		dbg("%02x <- i8042 (return)\n", param[i]);
3361da177e4SLinus Torvalds 	}
3371da177e4SLinus Torvalds 
338de9ce703SDmitry Torokhov 	return 0;
339de9ce703SDmitry Torokhov }
3401da177e4SLinus Torvalds 
341553a05b8SMárton Németh int i8042_command(unsigned char *param, int command)
342de9ce703SDmitry Torokhov {
343de9ce703SDmitry Torokhov 	unsigned long flags;
344de9ce703SDmitry Torokhov 	int retval;
345de9ce703SDmitry Torokhov 
346de9ce703SDmitry Torokhov 	spin_lock_irqsave(&i8042_lock, flags);
347de9ce703SDmitry Torokhov 	retval = __i8042_command(param, command);
348463a4f76SDmitry Torokhov 	spin_unlock_irqrestore(&i8042_lock, flags);
349de9ce703SDmitry Torokhov 
3501da177e4SLinus Torvalds 	return retval;
3511da177e4SLinus Torvalds }
352553a05b8SMárton Németh EXPORT_SYMBOL(i8042_command);
3531da177e4SLinus Torvalds 
3541da177e4SLinus Torvalds /*
3551da177e4SLinus Torvalds  * i8042_kbd_write() sends a byte out through the keyboard interface.
3561da177e4SLinus Torvalds  */
3571da177e4SLinus Torvalds 
3581da177e4SLinus Torvalds static int i8042_kbd_write(struct serio *port, unsigned char c)
3591da177e4SLinus Torvalds {
3601da177e4SLinus Torvalds 	unsigned long flags;
3611da177e4SLinus Torvalds 	int retval = 0;
3621da177e4SLinus Torvalds 
3631da177e4SLinus Torvalds 	spin_lock_irqsave(&i8042_lock, flags);
3641da177e4SLinus Torvalds 
3651da177e4SLinus Torvalds 	if (!(retval = i8042_wait_write())) {
3664eb3c30bSJoe Perches 		dbg("%02x -> i8042 (kbd-data)\n", c);
3671da177e4SLinus Torvalds 		i8042_write_data(c);
3681da177e4SLinus Torvalds 	}
3691da177e4SLinus Torvalds 
3701da177e4SLinus Torvalds 	spin_unlock_irqrestore(&i8042_lock, flags);
3711da177e4SLinus Torvalds 
3721da177e4SLinus Torvalds 	return retval;
3731da177e4SLinus Torvalds }
3741da177e4SLinus Torvalds 
3751da177e4SLinus Torvalds /*
3761da177e4SLinus Torvalds  * i8042_aux_write() sends a byte out through the aux interface.
3771da177e4SLinus Torvalds  */
3781da177e4SLinus Torvalds 
3791da177e4SLinus Torvalds static int i8042_aux_write(struct serio *serio, unsigned char c)
3801da177e4SLinus Torvalds {
3811da177e4SLinus Torvalds 	struct i8042_port *port = serio->port_data;
3821da177e4SLinus Torvalds 
383f4e3c711SDmitry Torokhov 	return i8042_command(&c, port->mux == -1 ?
384f4e3c711SDmitry Torokhov 					I8042_CMD_AUX_SEND :
385f4e3c711SDmitry Torokhov 					I8042_CMD_MUX_SEND + port->mux);
3861da177e4SLinus Torvalds }
3871da177e4SLinus Torvalds 
3885ddbc77cSDmitry Torokhov 
3895ddbc77cSDmitry Torokhov /*
3900e2b4458SMarcos Paulo de Souza  * i8042_port_close attempts to clear AUX or KBD port state by disabling
3915ddbc77cSDmitry Torokhov  * and then re-enabling it.
3925ddbc77cSDmitry Torokhov  */
3935ddbc77cSDmitry Torokhov 
3945ddbc77cSDmitry Torokhov static void i8042_port_close(struct serio *serio)
3955ddbc77cSDmitry Torokhov {
3965ddbc77cSDmitry Torokhov 	int irq_bit;
3975ddbc77cSDmitry Torokhov 	int disable_bit;
3985ddbc77cSDmitry Torokhov 	const char *port_name;
3995ddbc77cSDmitry Torokhov 
4005ddbc77cSDmitry Torokhov 	if (serio == i8042_ports[I8042_AUX_PORT_NO].serio) {
4015ddbc77cSDmitry Torokhov 		irq_bit = I8042_CTR_AUXINT;
4025ddbc77cSDmitry Torokhov 		disable_bit = I8042_CTR_AUXDIS;
4035ddbc77cSDmitry Torokhov 		port_name = "AUX";
4045ddbc77cSDmitry Torokhov 	} else {
4055ddbc77cSDmitry Torokhov 		irq_bit = I8042_CTR_KBDINT;
4065ddbc77cSDmitry Torokhov 		disable_bit = I8042_CTR_KBDDIS;
4075ddbc77cSDmitry Torokhov 		port_name = "KBD";
4085ddbc77cSDmitry Torokhov 	}
4095ddbc77cSDmitry Torokhov 
4105ddbc77cSDmitry Torokhov 	i8042_ctr &= ~irq_bit;
4115ddbc77cSDmitry Torokhov 	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
4124eb3c30bSJoe Perches 		pr_warn("Can't write CTR while closing %s port\n", port_name);
4135ddbc77cSDmitry Torokhov 
4145ddbc77cSDmitry Torokhov 	udelay(50);
4155ddbc77cSDmitry Torokhov 
4165ddbc77cSDmitry Torokhov 	i8042_ctr &= ~disable_bit;
4175ddbc77cSDmitry Torokhov 	i8042_ctr |= irq_bit;
4185ddbc77cSDmitry Torokhov 	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
4194eb3c30bSJoe Perches 		pr_err("Can't reactivate %s port\n", port_name);
4205ddbc77cSDmitry Torokhov 
4215ddbc77cSDmitry Torokhov 	/*
4225ddbc77cSDmitry Torokhov 	 * See if there is any data appeared while we were messing with
4235ddbc77cSDmitry Torokhov 	 * port state.
4245ddbc77cSDmitry Torokhov 	 */
4255ddbc77cSDmitry Torokhov 	i8042_interrupt(0, NULL);
4265ddbc77cSDmitry Torokhov }
4275ddbc77cSDmitry Torokhov 
4281da177e4SLinus Torvalds /*
4291da177e4SLinus Torvalds  * i8042_start() is called by serio core when port is about to finish
4301da177e4SLinus Torvalds  * registering. It will mark port as existing so i8042_interrupt can
4311da177e4SLinus Torvalds  * start sending data through it.
4321da177e4SLinus Torvalds  */
4331da177e4SLinus Torvalds static int i8042_start(struct serio *serio)
4341da177e4SLinus Torvalds {
4351da177e4SLinus Torvalds 	struct i8042_port *port = serio->port_data;
4361da177e4SLinus Torvalds 
437c8a144b2SStephen Boyd 	device_set_wakeup_capable(&serio->dev, true);
438c8a144b2SStephen Boyd 
439c8a144b2SStephen Boyd 	/*
440c8a144b2SStephen Boyd 	 * On platforms using suspend-to-idle, allow the keyboard to
441c8a144b2SStephen Boyd 	 * wake up the system from sleep by enabling keyboard wakeups
442c8a144b2SStephen Boyd 	 * by default.  This is consistent with keyboard wakeup
443c8a144b2SStephen Boyd 	 * behavior on many platforms using suspend-to-RAM (ACPI S3)
444c8a144b2SStephen Boyd 	 * by default.
445c8a144b2SStephen Boyd 	 */
446c8a144b2SStephen Boyd 	if (pm_suspend_default_s2idle() &&
447c8a144b2SStephen Boyd 	    serio == i8042_ports[I8042_KBD_PORT_NO].serio) {
448c8a144b2SStephen Boyd 		device_set_wakeup_enable(&serio->dev, true);
449c8a144b2SStephen Boyd 	}
450c8a144b2SStephen Boyd 
451340d394aSChen Hong 	spin_lock_irq(&i8042_lock);
452386b3849SDmitry Torokhov 	port->exists = true;
453340d394aSChen Hong 	spin_unlock_irq(&i8042_lock);
454340d394aSChen Hong 
4551da177e4SLinus Torvalds 	return 0;
4561da177e4SLinus Torvalds }
4571da177e4SLinus Torvalds 
4581da177e4SLinus Torvalds /*
4591da177e4SLinus Torvalds  * i8042_stop() marks serio port as non-existing so i8042_interrupt
4601da177e4SLinus Torvalds  * will not try to send data to the port that is about to go away.
4611da177e4SLinus Torvalds  * The function is called by serio core as part of unregister procedure.
4621da177e4SLinus Torvalds  */
4631da177e4SLinus Torvalds static void i8042_stop(struct serio *serio)
4641da177e4SLinus Torvalds {
4651da177e4SLinus Torvalds 	struct i8042_port *port = serio->port_data;
4661da177e4SLinus Torvalds 
467340d394aSChen Hong 	spin_lock_irq(&i8042_lock);
468386b3849SDmitry Torokhov 	port->exists = false;
469340d394aSChen Hong 	port->serio = NULL;
470340d394aSChen Hong 	spin_unlock_irq(&i8042_lock);
471a8399c51SDmitry Torokhov 
472a8399c51SDmitry Torokhov 	/*
473340d394aSChen Hong 	 * We need to make sure that interrupt handler finishes using
474340d394aSChen Hong 	 * our serio port before we return from this function.
475a8399c51SDmitry Torokhov 	 * We synchronize with both AUX and KBD IRQs because there is
476a8399c51SDmitry Torokhov 	 * a (very unlikely) chance that AUX IRQ is raised for KBD port
477a8399c51SDmitry Torokhov 	 * and vice versa.
478a8399c51SDmitry Torokhov 	 */
479a8399c51SDmitry Torokhov 	synchronize_irq(I8042_AUX_IRQ);
480a8399c51SDmitry Torokhov 	synchronize_irq(I8042_KBD_IRQ);
4811da177e4SLinus Torvalds }
4821da177e4SLinus Torvalds 
4831da177e4SLinus Torvalds /*
4844e8d340dSDmitry Torokhov  * i8042_filter() filters out unwanted bytes from the input data stream.
4854e8d340dSDmitry Torokhov  * It is called from i8042_interrupt and thus is running with interrupts
4864e8d340dSDmitry Torokhov  * off and i8042_lock held.
4874e8d340dSDmitry Torokhov  */
488967c9ef9SMatthew Garrett static bool i8042_filter(unsigned char data, unsigned char str,
489967c9ef9SMatthew Garrett 			 struct serio *serio)
4904e8d340dSDmitry Torokhov {
4914e8d340dSDmitry Torokhov 	if (unlikely(i8042_suppress_kbd_ack)) {
4924e8d340dSDmitry Torokhov 		if ((~str & I8042_STR_AUXDATA) &&
4934e8d340dSDmitry Torokhov 		    (data == 0xfa || data == 0xfe)) {
4944e8d340dSDmitry Torokhov 			i8042_suppress_kbd_ack--;
4954e8d340dSDmitry Torokhov 			dbg("Extra keyboard ACK - filtered out\n");
4964e8d340dSDmitry Torokhov 			return true;
4974e8d340dSDmitry Torokhov 		}
4984e8d340dSDmitry Torokhov 	}
4994e8d340dSDmitry Torokhov 
500967c9ef9SMatthew Garrett 	if (i8042_platform_filter && i8042_platform_filter(data, str, serio)) {
5010747e3bcSStefan Weil 		dbg("Filtered out by platform filter\n");
502967c9ef9SMatthew Garrett 		return true;
503967c9ef9SMatthew Garrett 	}
504967c9ef9SMatthew Garrett 
5054e8d340dSDmitry Torokhov 	return false;
5064e8d340dSDmitry Torokhov }
5074e8d340dSDmitry Torokhov 
5084e8d340dSDmitry Torokhov /*
5091da177e4SLinus Torvalds  * i8042_interrupt() is the most important function in this driver -
5101da177e4SLinus Torvalds  * it handles the interrupts from the i8042, and sends incoming bytes
5111da177e4SLinus Torvalds  * to the upper layers.
5121da177e4SLinus Torvalds  */
5131da177e4SLinus Torvalds 
5147d12e780SDavid Howells static irqreturn_t i8042_interrupt(int irq, void *dev_id)
5151da177e4SLinus Torvalds {
5161da177e4SLinus Torvalds 	struct i8042_port *port;
517967c9ef9SMatthew Garrett 	struct serio *serio;
5181da177e4SLinus Torvalds 	unsigned long flags;
5191da177e4SLinus Torvalds 	unsigned char str, data;
5201da177e4SLinus Torvalds 	unsigned int dfl;
5211da177e4SLinus Torvalds 	unsigned int port_no;
5224e8d340dSDmitry Torokhov 	bool filtered;
523817e6ba3SDmitry Torokhov 	int ret = 1;
5241da177e4SLinus Torvalds 
5251da177e4SLinus Torvalds 	spin_lock_irqsave(&i8042_lock, flags);
5264e8d340dSDmitry Torokhov 
5271da177e4SLinus Torvalds 	str = i8042_read_status();
5281da177e4SLinus Torvalds 	if (unlikely(~str & I8042_STR_OBF)) {
5291da177e4SLinus Torvalds 		spin_unlock_irqrestore(&i8042_lock, flags);
5304eb3c30bSJoe Perches 		if (irq)
5314eb3c30bSJoe Perches 			dbg("Interrupt %d, without any data\n", irq);
5321da177e4SLinus Torvalds 		ret = 0;
5331da177e4SLinus Torvalds 		goto out;
5341da177e4SLinus Torvalds 	}
5354e8d340dSDmitry Torokhov 
5361da177e4SLinus Torvalds 	data = i8042_read_data();
5371da177e4SLinus Torvalds 
5381da177e4SLinus Torvalds 	if (i8042_mux_present && (str & I8042_STR_AUXDATA)) {
5391da177e4SLinus Torvalds 		static unsigned long last_transmit;
5401da177e4SLinus Torvalds 		static unsigned char last_str;
5411da177e4SLinus Torvalds 
5421da177e4SLinus Torvalds 		dfl = 0;
5431da177e4SLinus Torvalds 		if (str & I8042_STR_MUXERR) {
5444eb3c30bSJoe Perches 			dbg("MUX error, status is %02x, data is %02x\n",
5454eb3c30bSJoe Perches 			    str, data);
5461da177e4SLinus Torvalds /*
5471da177e4SLinus Torvalds  * When MUXERR condition is signalled the data register can only contain
5481da177e4SLinus Torvalds  * 0xfd, 0xfe or 0xff if implementation follows the spec. Unfortunately
549a216a4b6SDmitry Torokhov  * it is not always the case. Some KBCs also report 0xfc when there is
550a216a4b6SDmitry Torokhov  * nothing connected to the port while others sometimes get confused which
551a216a4b6SDmitry Torokhov  * port the data came from and signal error leaving the data intact. They
552a216a4b6SDmitry Torokhov  * _do not_ revert to legacy mode (actually I've never seen KBC reverting
553a216a4b6SDmitry Torokhov  * to legacy mode yet, when we see one we'll add proper handling).
554a216a4b6SDmitry Torokhov  * Anyway, we process 0xfc, 0xfd, 0xfe and 0xff as timeouts, and for the
555a216a4b6SDmitry Torokhov  * rest assume that the data came from the same serio last byte
5561da177e4SLinus Torvalds  * was transmitted (if transmission happened not too long ago).
5571da177e4SLinus Torvalds  */
558a216a4b6SDmitry Torokhov 
559a216a4b6SDmitry Torokhov 			switch (data) {
560a216a4b6SDmitry Torokhov 				default:
5611da177e4SLinus Torvalds 					if (time_before(jiffies, last_transmit + HZ/10)) {
5621da177e4SLinus Torvalds 						str = last_str;
5631da177e4SLinus Torvalds 						break;
5641da177e4SLinus Torvalds 					}
5651da177e4SLinus Torvalds 					/* fall through - report timeout */
566a216a4b6SDmitry Torokhov 				case 0xfc:
5671da177e4SLinus Torvalds 				case 0xfd:
5681da177e4SLinus Torvalds 				case 0xfe: dfl = SERIO_TIMEOUT; data = 0xfe; break;
5691da177e4SLinus Torvalds 				case 0xff: dfl = SERIO_PARITY;  data = 0xfe; break;
5701da177e4SLinus Torvalds 			}
5711da177e4SLinus Torvalds 		}
5721da177e4SLinus Torvalds 
5731da177e4SLinus Torvalds 		port_no = I8042_MUX_PORT_NO + ((str >> 6) & 3);
5741da177e4SLinus Torvalds 		last_str = str;
5751da177e4SLinus Torvalds 		last_transmit = jiffies;
5761da177e4SLinus Torvalds 	} else {
5771da177e4SLinus Torvalds 
5781da177e4SLinus Torvalds 		dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) |
579f8313ef1SJiri Kosina 		      ((str & I8042_STR_TIMEOUT && !i8042_notimeout) ? SERIO_TIMEOUT : 0);
5801da177e4SLinus Torvalds 
5811da177e4SLinus Torvalds 		port_no = (str & I8042_STR_AUXDATA) ?
5821da177e4SLinus Torvalds 				I8042_AUX_PORT_NO : I8042_KBD_PORT_NO;
5831da177e4SLinus Torvalds 	}
5841da177e4SLinus Torvalds 
5851da177e4SLinus Torvalds 	port = &i8042_ports[port_no];
586967c9ef9SMatthew Garrett 	serio = port->exists ? port->serio : NULL;
5871da177e4SLinus Torvalds 
588e1443d28SStephen Chandler Paul 	filter_dbg(port->driver_bound, data, "<- i8042 (interrupt, %d, %d%s%s)\n",
589e1443d28SStephen Chandler Paul 		   port_no, irq,
5901da177e4SLinus Torvalds 		   dfl & SERIO_PARITY ? ", bad parity" : "",
5911da177e4SLinus Torvalds 		   dfl & SERIO_TIMEOUT ? ", timeout" : "");
5921da177e4SLinus Torvalds 
593967c9ef9SMatthew Garrett 	filtered = i8042_filter(data, str, serio);
594817e6ba3SDmitry Torokhov 
5954e8d340dSDmitry Torokhov 	spin_unlock_irqrestore(&i8042_lock, flags);
5964e8d340dSDmitry Torokhov 
597340d394aSChen Hong 	if (likely(serio && !filtered))
598967c9ef9SMatthew Garrett 		serio_interrupt(serio, data, dfl);
5991da177e4SLinus Torvalds 
6001da177e4SLinus Torvalds  out:
6011da177e4SLinus Torvalds 	return IRQ_RETVAL(ret);
6021da177e4SLinus Torvalds }
6031da177e4SLinus Torvalds 
6041da177e4SLinus Torvalds /*
6055ddbc77cSDmitry Torokhov  * i8042_enable_kbd_port enables keyboard port on chip
606de9ce703SDmitry Torokhov  */
607de9ce703SDmitry Torokhov 
608de9ce703SDmitry Torokhov static int i8042_enable_kbd_port(void)
609de9ce703SDmitry Torokhov {
610de9ce703SDmitry Torokhov 	i8042_ctr &= ~I8042_CTR_KBDDIS;
611de9ce703SDmitry Torokhov 	i8042_ctr |= I8042_CTR_KBDINT;
612de9ce703SDmitry Torokhov 
613de9ce703SDmitry Torokhov 	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
614018db6bbSMarkus Armbruster 		i8042_ctr &= ~I8042_CTR_KBDINT;
615018db6bbSMarkus Armbruster 		i8042_ctr |= I8042_CTR_KBDDIS;
6164eb3c30bSJoe Perches 		pr_err("Failed to enable KBD port\n");
617de9ce703SDmitry Torokhov 		return -EIO;
618de9ce703SDmitry Torokhov 	}
619de9ce703SDmitry Torokhov 
620de9ce703SDmitry Torokhov 	return 0;
621de9ce703SDmitry Torokhov }
622de9ce703SDmitry Torokhov 
623de9ce703SDmitry Torokhov /*
624de9ce703SDmitry Torokhov  * i8042_enable_aux_port enables AUX (mouse) port on chip
625de9ce703SDmitry Torokhov  */
626de9ce703SDmitry Torokhov 
627de9ce703SDmitry Torokhov static int i8042_enable_aux_port(void)
628de9ce703SDmitry Torokhov {
629de9ce703SDmitry Torokhov 	i8042_ctr &= ~I8042_CTR_AUXDIS;
630de9ce703SDmitry Torokhov 	i8042_ctr |= I8042_CTR_AUXINT;
631de9ce703SDmitry Torokhov 
632de9ce703SDmitry Torokhov 	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
633018db6bbSMarkus Armbruster 		i8042_ctr &= ~I8042_CTR_AUXINT;
634018db6bbSMarkus Armbruster 		i8042_ctr |= I8042_CTR_AUXDIS;
6354eb3c30bSJoe Perches 		pr_err("Failed to enable AUX port\n");
636de9ce703SDmitry Torokhov 		return -EIO;
637de9ce703SDmitry Torokhov 	}
638de9ce703SDmitry Torokhov 
639de9ce703SDmitry Torokhov 	return 0;
640de9ce703SDmitry Torokhov }
641de9ce703SDmitry Torokhov 
642de9ce703SDmitry Torokhov /*
643de9ce703SDmitry Torokhov  * i8042_enable_mux_ports enables 4 individual AUX ports after
644de9ce703SDmitry Torokhov  * the controller has been switched into Multiplexed mode
645de9ce703SDmitry Torokhov  */
646de9ce703SDmitry Torokhov 
647de9ce703SDmitry Torokhov static int i8042_enable_mux_ports(void)
648de9ce703SDmitry Torokhov {
649de9ce703SDmitry Torokhov 	unsigned char param;
650de9ce703SDmitry Torokhov 	int i;
651de9ce703SDmitry Torokhov 
652de9ce703SDmitry Torokhov 	for (i = 0; i < I8042_NUM_MUX_PORTS; i++) {
653de9ce703SDmitry Torokhov 		i8042_command(&param, I8042_CMD_MUX_PFX + i);
654de9ce703SDmitry Torokhov 		i8042_command(&param, I8042_CMD_AUX_ENABLE);
655de9ce703SDmitry Torokhov 	}
656de9ce703SDmitry Torokhov 
657de9ce703SDmitry Torokhov 	return i8042_enable_aux_port();
658de9ce703SDmitry Torokhov }
659de9ce703SDmitry Torokhov 
660de9ce703SDmitry Torokhov /*
661386b3849SDmitry Torokhov  * i8042_set_mux_mode checks whether the controller has an
662386b3849SDmitry Torokhov  * active multiplexor and puts the chip into Multiplexed (true)
663386b3849SDmitry Torokhov  * or Legacy (false) mode.
6641da177e4SLinus Torvalds  */
6651da177e4SLinus Torvalds 
666386b3849SDmitry Torokhov static int i8042_set_mux_mode(bool multiplex, unsigned char *mux_version)
6671da177e4SLinus Torvalds {
6681da177e4SLinus Torvalds 
669386b3849SDmitry Torokhov 	unsigned char param, val;
6701da177e4SLinus Torvalds /*
6711da177e4SLinus Torvalds  * Get rid of bytes in the queue.
6721da177e4SLinus Torvalds  */
6731da177e4SLinus Torvalds 
6741da177e4SLinus Torvalds 	i8042_flush();
6751da177e4SLinus Torvalds 
6761da177e4SLinus Torvalds /*
6771da177e4SLinus Torvalds  * Internal loopback test - send three bytes, they should come back from the
678de9ce703SDmitry Torokhov  * mouse interface, the last should be version.
6791da177e4SLinus Torvalds  */
6801da177e4SLinus Torvalds 
681386b3849SDmitry Torokhov 	param = val = 0xf0;
682386b3849SDmitry Torokhov 	if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param != val)
6831da177e4SLinus Torvalds 		return -1;
684386b3849SDmitry Torokhov 	param = val = multiplex ? 0x56 : 0xf6;
685386b3849SDmitry Torokhov 	if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param != val)
6861da177e4SLinus Torvalds 		return -1;
687386b3849SDmitry Torokhov 	param = val = multiplex ? 0xa4 : 0xa5;
688386b3849SDmitry Torokhov 	if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param == val)
689386b3849SDmitry Torokhov 		return -1;
690386b3849SDmitry Torokhov 
691386b3849SDmitry Torokhov /*
692386b3849SDmitry Torokhov  * Workaround for interference with USB Legacy emulation
693386b3849SDmitry Torokhov  * that causes a v10.12 MUX to be found.
694386b3849SDmitry Torokhov  */
695386b3849SDmitry Torokhov 	if (param == 0xac)
6961da177e4SLinus Torvalds 		return -1;
6971da177e4SLinus Torvalds 
6981da177e4SLinus Torvalds 	if (mux_version)
699463a4f76SDmitry Torokhov 		*mux_version = param;
7001da177e4SLinus Torvalds 
7011da177e4SLinus Torvalds 	return 0;
7021da177e4SLinus Torvalds }
7031da177e4SLinus Torvalds 
7041da177e4SLinus Torvalds /*
7051da177e4SLinus Torvalds  * i8042_check_mux() checks whether the controller supports the PS/2 Active
7061da177e4SLinus Torvalds  * Multiplexing specification by Synaptics, Phoenix, Insyde and
7071da177e4SLinus Torvalds  * LCS/Telegraphics.
7081da177e4SLinus Torvalds  */
7091da177e4SLinus Torvalds 
710f8113416SDmitry Torokhov static int __init i8042_check_mux(void)
7111da177e4SLinus Torvalds {
7121da177e4SLinus Torvalds 	unsigned char mux_version;
7131da177e4SLinus Torvalds 
714386b3849SDmitry Torokhov 	if (i8042_set_mux_mode(true, &mux_version))
7151da177e4SLinus Torvalds 		return -1;
7161da177e4SLinus Torvalds 
7174eb3c30bSJoe Perches 	pr_info("Detected active multiplexing controller, rev %d.%d\n",
7181da177e4SLinus Torvalds 		(mux_version >> 4) & 0xf, mux_version & 0xf);
7191da177e4SLinus Torvalds 
720de9ce703SDmitry Torokhov /*
721de9ce703SDmitry Torokhov  * Disable all muxed ports by disabling AUX.
722de9ce703SDmitry Torokhov  */
723de9ce703SDmitry Torokhov 	i8042_ctr |= I8042_CTR_AUXDIS;
724de9ce703SDmitry Torokhov 	i8042_ctr &= ~I8042_CTR_AUXINT;
725de9ce703SDmitry Torokhov 
726de9ce703SDmitry Torokhov 	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
7274eb3c30bSJoe Perches 		pr_err("Failed to disable AUX port, can't use MUX\n");
728de9ce703SDmitry Torokhov 		return -EIO;
729de9ce703SDmitry Torokhov 	}
7301da177e4SLinus Torvalds 
731386b3849SDmitry Torokhov 	i8042_mux_present = true;
732de9ce703SDmitry Torokhov 
7331da177e4SLinus Torvalds 	return 0;
7341da177e4SLinus Torvalds }
7351da177e4SLinus Torvalds 
736de9ce703SDmitry Torokhov /*
737de9ce703SDmitry Torokhov  * The following is used to test AUX IRQ delivery.
738de9ce703SDmitry Torokhov  */
739f8113416SDmitry Torokhov static struct completion i8042_aux_irq_delivered __initdata;
740f8113416SDmitry Torokhov static bool i8042_irq_being_tested __initdata;
741de9ce703SDmitry Torokhov 
742f8113416SDmitry Torokhov static irqreturn_t __init i8042_aux_test_irq(int irq, void *dev_id)
743de9ce703SDmitry Torokhov {
744de9ce703SDmitry Torokhov 	unsigned long flags;
745de9ce703SDmitry Torokhov 	unsigned char str, data;
746e3758b2aSFernando Luis Vázquez Cao 	int ret = 0;
747de9ce703SDmitry Torokhov 
748de9ce703SDmitry Torokhov 	spin_lock_irqsave(&i8042_lock, flags);
749de9ce703SDmitry Torokhov 	str = i8042_read_status();
750de9ce703SDmitry Torokhov 	if (str & I8042_STR_OBF) {
751de9ce703SDmitry Torokhov 		data = i8042_read_data();
7524eb3c30bSJoe Perches 		dbg("%02x <- i8042 (aux_test_irq, %s)\n",
753d3d2dfe2SDmitry Torokhov 		    data, str & I8042_STR_AUXDATA ? "aux" : "kbd");
754de9ce703SDmitry Torokhov 		if (i8042_irq_being_tested &&
755de9ce703SDmitry Torokhov 		    data == 0xa5 && (str & I8042_STR_AUXDATA))
756de9ce703SDmitry Torokhov 			complete(&i8042_aux_irq_delivered);
757e3758b2aSFernando Luis Vázquez Cao 		ret = 1;
758de9ce703SDmitry Torokhov 	}
759de9ce703SDmitry Torokhov 	spin_unlock_irqrestore(&i8042_lock, flags);
760de9ce703SDmitry Torokhov 
761e3758b2aSFernando Luis Vázquez Cao 	return IRQ_RETVAL(ret);
762de9ce703SDmitry Torokhov }
763de9ce703SDmitry Torokhov 
764d2ada559SRoland Scheidegger /*
765d2ada559SRoland Scheidegger  * i8042_toggle_aux - enables or disables AUX port on i8042 via command and
766d2ada559SRoland Scheidegger  * verifies success by readinng CTR. Used when testing for presence of AUX
767d2ada559SRoland Scheidegger  * port.
768d2ada559SRoland Scheidegger  */
769f8113416SDmitry Torokhov static int __init i8042_toggle_aux(bool on)
770d2ada559SRoland Scheidegger {
771d2ada559SRoland Scheidegger 	unsigned char param;
772d2ada559SRoland Scheidegger 	int i;
773d2ada559SRoland Scheidegger 
774d2ada559SRoland Scheidegger 	if (i8042_command(&param,
775d2ada559SRoland Scheidegger 			on ? I8042_CMD_AUX_ENABLE : I8042_CMD_AUX_DISABLE))
776d2ada559SRoland Scheidegger 		return -1;
777d2ada559SRoland Scheidegger 
778d2ada559SRoland Scheidegger 	/* some chips need some time to set the I8042_CTR_AUXDIS bit */
779d2ada559SRoland Scheidegger 	for (i = 0; i < 100; i++) {
780d2ada559SRoland Scheidegger 		udelay(50);
781d2ada559SRoland Scheidegger 
782d2ada559SRoland Scheidegger 		if (i8042_command(&param, I8042_CMD_CTL_RCTR))
783d2ada559SRoland Scheidegger 			return -1;
784d2ada559SRoland Scheidegger 
785d2ada559SRoland Scheidegger 		if (!(param & I8042_CTR_AUXDIS) == on)
786d2ada559SRoland Scheidegger 			return 0;
787d2ada559SRoland Scheidegger 	}
788d2ada559SRoland Scheidegger 
789d2ada559SRoland Scheidegger 	return -1;
790d2ada559SRoland Scheidegger }
7911da177e4SLinus Torvalds 
7921da177e4SLinus Torvalds /*
7931da177e4SLinus Torvalds  * i8042_check_aux() applies as much paranoia as it can at detecting
7941da177e4SLinus Torvalds  * the presence of an AUX interface.
7951da177e4SLinus Torvalds  */
7961da177e4SLinus Torvalds 
797f8113416SDmitry Torokhov static int __init i8042_check_aux(void)
7981da177e4SLinus Torvalds {
799de9ce703SDmitry Torokhov 	int retval = -1;
800386b3849SDmitry Torokhov 	bool irq_registered = false;
801386b3849SDmitry Torokhov 	bool aux_loop_broken = false;
802de9ce703SDmitry Torokhov 	unsigned long flags;
8031da177e4SLinus Torvalds 	unsigned char param;
8041da177e4SLinus Torvalds 
8051da177e4SLinus Torvalds /*
8061da177e4SLinus Torvalds  * Get rid of bytes in the queue.
8071da177e4SLinus Torvalds  */
8081da177e4SLinus Torvalds 
8091da177e4SLinus Torvalds 	i8042_flush();
8101da177e4SLinus Torvalds 
8111da177e4SLinus Torvalds /*
8121da177e4SLinus Torvalds  * Internal loopback test - filters out AT-type i8042's. Unfortunately
8131da177e4SLinus Torvalds  * SiS screwed up and their 5597 doesn't support the LOOP command even
8141da177e4SLinus Torvalds  * though it has an AUX port.
8151da177e4SLinus Torvalds  */
8161da177e4SLinus Torvalds 
8171da177e4SLinus Torvalds 	param = 0x5a;
8183ca5de6dSDmitry Torokhov 	retval = i8042_command(&param, I8042_CMD_AUX_LOOP);
8193ca5de6dSDmitry Torokhov 	if (retval || param != 0x5a) {
8201da177e4SLinus Torvalds 
8211da177e4SLinus Torvalds /*
8221da177e4SLinus Torvalds  * External connection test - filters out AT-soldered PS/2 i8042's
8231da177e4SLinus Torvalds  * 0x00 - no error, 0x01-0x03 - clock/data stuck, 0xff - general error
8241da177e4SLinus Torvalds  * 0xfa - no error on some notebooks which ignore the spec
8251da177e4SLinus Torvalds  * Because it's common for chipsets to return error on perfectly functioning
8261da177e4SLinus Torvalds  * AUX ports, we test for this only when the LOOP command failed.
8271da177e4SLinus Torvalds  */
8281da177e4SLinus Torvalds 
829de9ce703SDmitry Torokhov 		if (i8042_command(&param, I8042_CMD_AUX_TEST) ||
830de9ce703SDmitry Torokhov 		    (param && param != 0xfa && param != 0xff))
8311da177e4SLinus Torvalds 			return -1;
8321e4865f8SDmitry Torokhov 
8333ca5de6dSDmitry Torokhov /*
8343ca5de6dSDmitry Torokhov  * If AUX_LOOP completed without error but returned unexpected data
8353ca5de6dSDmitry Torokhov  * mark it as broken
8363ca5de6dSDmitry Torokhov  */
8373ca5de6dSDmitry Torokhov 		if (!retval)
838386b3849SDmitry Torokhov 			aux_loop_broken = true;
8391da177e4SLinus Torvalds 	}
8401da177e4SLinus Torvalds 
8411da177e4SLinus Torvalds /*
8421da177e4SLinus Torvalds  * Bit assignment test - filters out PS/2 i8042's in AT mode
8431da177e4SLinus Torvalds  */
8441da177e4SLinus Torvalds 
845386b3849SDmitry Torokhov 	if (i8042_toggle_aux(false)) {
8464eb3c30bSJoe Perches 		pr_warn("Failed to disable AUX port, but continuing anyway... Is this a SiS?\n");
8474eb3c30bSJoe Perches 		pr_warn("If AUX port is really absent please use the 'i8042.noaux' option\n");
8481da177e4SLinus Torvalds 	}
8491da177e4SLinus Torvalds 
850386b3849SDmitry Torokhov 	if (i8042_toggle_aux(true))
8511da177e4SLinus Torvalds 		return -1;
8521da177e4SLinus Torvalds 
8531da177e4SLinus Torvalds /*
854148e9a71SSrihari Vijayaraghavan  * Reset keyboard (needed on some laptops to successfully detect
855148e9a71SSrihari Vijayaraghavan  * touchpad, e.g., some Gigabyte laptop models with Elantech
856148e9a71SSrihari Vijayaraghavan  * touchpads).
857148e9a71SSrihari Vijayaraghavan  */
858148e9a71SSrihari Vijayaraghavan 	if (i8042_kbdreset) {
859148e9a71SSrihari Vijayaraghavan 		pr_warn("Attempting to reset device connected to KBD port\n");
860148e9a71SSrihari Vijayaraghavan 		i8042_kbd_write(NULL, (unsigned char) 0xff);
861148e9a71SSrihari Vijayaraghavan 	}
862148e9a71SSrihari Vijayaraghavan 
863148e9a71SSrihari Vijayaraghavan /*
864de9ce703SDmitry Torokhov  * Test AUX IRQ delivery to make sure BIOS did not grab the IRQ and
865de9ce703SDmitry Torokhov  * used it for a PCI card or somethig else.
866de9ce703SDmitry Torokhov  */
867de9ce703SDmitry Torokhov 
8681c7827aeSDmitry Torokhov 	if (i8042_noloop || i8042_bypass_aux_irq_test || aux_loop_broken) {
869de9ce703SDmitry Torokhov /*
870de9ce703SDmitry Torokhov  * Without LOOP command we can't test AUX IRQ delivery. Assume the port
871de9ce703SDmitry Torokhov  * is working and hope we are right.
872de9ce703SDmitry Torokhov  */
873de9ce703SDmitry Torokhov 		retval = 0;
874de9ce703SDmitry Torokhov 		goto out;
875de9ce703SDmitry Torokhov 	}
876de9ce703SDmitry Torokhov 
877de9ce703SDmitry Torokhov 	if (request_irq(I8042_AUX_IRQ, i8042_aux_test_irq, IRQF_SHARED,
878de9ce703SDmitry Torokhov 			"i8042", i8042_platform_device))
879de9ce703SDmitry Torokhov 		goto out;
880de9ce703SDmitry Torokhov 
881386b3849SDmitry Torokhov 	irq_registered = true;
882de9ce703SDmitry Torokhov 
883de9ce703SDmitry Torokhov 	if (i8042_enable_aux_port())
884de9ce703SDmitry Torokhov 		goto out;
885de9ce703SDmitry Torokhov 
886de9ce703SDmitry Torokhov 	spin_lock_irqsave(&i8042_lock, flags);
887de9ce703SDmitry Torokhov 
888de9ce703SDmitry Torokhov 	init_completion(&i8042_aux_irq_delivered);
889386b3849SDmitry Torokhov 	i8042_irq_being_tested = true;
890de9ce703SDmitry Torokhov 
891de9ce703SDmitry Torokhov 	param = 0xa5;
892de9ce703SDmitry Torokhov 	retval = __i8042_command(&param, I8042_CMD_AUX_LOOP & 0xf0ff);
893de9ce703SDmitry Torokhov 
894de9ce703SDmitry Torokhov 	spin_unlock_irqrestore(&i8042_lock, flags);
895de9ce703SDmitry Torokhov 
896de9ce703SDmitry Torokhov 	if (retval)
897de9ce703SDmitry Torokhov 		goto out;
898de9ce703SDmitry Torokhov 
899de9ce703SDmitry Torokhov 	if (wait_for_completion_timeout(&i8042_aux_irq_delivered,
900de9ce703SDmitry Torokhov 					msecs_to_jiffies(250)) == 0) {
901de9ce703SDmitry Torokhov /*
902de9ce703SDmitry Torokhov  * AUX IRQ was never delivered so we need to flush the controller to
903de9ce703SDmitry Torokhov  * get rid of the byte we put there; otherwise keyboard may not work.
904de9ce703SDmitry Torokhov  */
9054eb3c30bSJoe Perches 		dbg("     -- i8042 (aux irq test timeout)\n");
906de9ce703SDmitry Torokhov 		i8042_flush();
907de9ce703SDmitry Torokhov 		retval = -1;
908de9ce703SDmitry Torokhov 	}
909de9ce703SDmitry Torokhov 
910de9ce703SDmitry Torokhov  out:
911de9ce703SDmitry Torokhov 
912de9ce703SDmitry Torokhov /*
9131da177e4SLinus Torvalds  * Disable the interface.
9141da177e4SLinus Torvalds  */
9151da177e4SLinus Torvalds 
9161da177e4SLinus Torvalds 	i8042_ctr |= I8042_CTR_AUXDIS;
9171da177e4SLinus Torvalds 	i8042_ctr &= ~I8042_CTR_AUXINT;
9181da177e4SLinus Torvalds 
9191da177e4SLinus Torvalds 	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
920de9ce703SDmitry Torokhov 		retval = -1;
921de9ce703SDmitry Torokhov 
922de9ce703SDmitry Torokhov 	if (irq_registered)
923de9ce703SDmitry Torokhov 		free_irq(I8042_AUX_IRQ, i8042_platform_device);
924de9ce703SDmitry Torokhov 
925de9ce703SDmitry Torokhov 	return retval;
926de9ce703SDmitry Torokhov }
927de9ce703SDmitry Torokhov 
928de9ce703SDmitry Torokhov static int i8042_controller_check(void)
929de9ce703SDmitry Torokhov {
9302f0d2604SAndrey Moiseev 	if (i8042_flush()) {
931f5d75341STakashi Iwai 		pr_info("No controller found\n");
932de9ce703SDmitry Torokhov 		return -ENODEV;
933de9ce703SDmitry Torokhov 	}
9341da177e4SLinus Torvalds 
9351da177e4SLinus Torvalds 	return 0;
9361da177e4SLinus Torvalds }
9371da177e4SLinus Torvalds 
938de9ce703SDmitry Torokhov static int i8042_controller_selftest(void)
9392673c836SVojtech Pavlik {
9402673c836SVojtech Pavlik 	unsigned char param;
9415ea2fc64SArjan van de Ven 	int i = 0;
9422673c836SVojtech Pavlik 
9435ea2fc64SArjan van de Ven 	/*
9445ea2fc64SArjan van de Ven 	 * We try this 5 times; on some really fragile systems this does not
9455ea2fc64SArjan van de Ven 	 * take the first time...
9465ea2fc64SArjan van de Ven 	 */
9475ea2fc64SArjan van de Ven 	do {
9485ea2fc64SArjan van de Ven 
9492673c836SVojtech Pavlik 		if (i8042_command(&param, I8042_CMD_CTL_TEST)) {
9504eb3c30bSJoe Perches 			pr_err("i8042 controller selftest timeout\n");
951de9ce703SDmitry Torokhov 			return -ENODEV;
9522673c836SVojtech Pavlik 		}
9532673c836SVojtech Pavlik 
9545ea2fc64SArjan van de Ven 		if (param == I8042_RET_CTL_TEST)
9555ea2fc64SArjan van de Ven 			return 0;
9565ea2fc64SArjan van de Ven 
957a2a94e73SPaul Bolle 		dbg("i8042 controller selftest: %#x != %#x\n",
9582673c836SVojtech Pavlik 		    param, I8042_RET_CTL_TEST);
9595ea2fc64SArjan van de Ven 		msleep(50);
9605ea2fc64SArjan van de Ven 	} while (i++ < 5);
9612673c836SVojtech Pavlik 
9625ea2fc64SArjan van de Ven #ifdef CONFIG_X86
9635ea2fc64SArjan van de Ven 	/*
9645ea2fc64SArjan van de Ven 	 * On x86, we don't fail entire i8042 initialization if controller
9655ea2fc64SArjan van de Ven 	 * reset fails in hopes that keyboard port will still be functional
9665ea2fc64SArjan van de Ven 	 * and user will still get a working keyboard. This is especially
9675ea2fc64SArjan van de Ven 	 * important on netbooks. On other arches we trust hardware more.
9685ea2fc64SArjan van de Ven 	 */
9694eb3c30bSJoe Perches 	pr_info("giving up on controller selftest, continuing anyway...\n");
9702673c836SVojtech Pavlik 	return 0;
9715ea2fc64SArjan van de Ven #else
972a2a94e73SPaul Bolle 	pr_err("i8042 controller selftest failed\n");
9735ea2fc64SArjan van de Ven 	return -EIO;
9745ea2fc64SArjan van de Ven #endif
9752673c836SVojtech Pavlik }
9761da177e4SLinus Torvalds 
9771da177e4SLinus Torvalds /*
9781da177e4SLinus Torvalds  * i8042_controller init initializes the i8042 controller, and,
9791da177e4SLinus Torvalds  * most importantly, sets it into non-xlated mode if that's
9801da177e4SLinus Torvalds  * desired.
9811da177e4SLinus Torvalds  */
9821da177e4SLinus Torvalds 
9831da177e4SLinus Torvalds static int i8042_controller_init(void)
9841da177e4SLinus Torvalds {
9851da177e4SLinus Torvalds 	unsigned long flags;
986ee1e82ceSDmitry Torokhov 	int n = 0;
987ee1e82ceSDmitry Torokhov 	unsigned char ctr[2];
9881da177e4SLinus Torvalds 
9891da177e4SLinus Torvalds /*
990ee1e82ceSDmitry Torokhov  * Save the CTR for restore on unload / reboot.
9911da177e4SLinus Torvalds  */
9921da177e4SLinus Torvalds 
993ee1e82ceSDmitry Torokhov 	do {
994ee1e82ceSDmitry Torokhov 		if (n >= 10) {
9954eb3c30bSJoe Perches 			pr_err("Unable to get stable CTR read\n");
996de9ce703SDmitry Torokhov 			return -EIO;
9971da177e4SLinus Torvalds 		}
9981da177e4SLinus Torvalds 
999ee1e82ceSDmitry Torokhov 		if (n != 0)
1000ee1e82ceSDmitry Torokhov 			udelay(50);
1001ee1e82ceSDmitry Torokhov 
1002ee1e82ceSDmitry Torokhov 		if (i8042_command(&ctr[n++ % 2], I8042_CMD_CTL_RCTR)) {
10034eb3c30bSJoe Perches 			pr_err("Can't read CTR while initializing i8042\n");
1004ee1e82ceSDmitry Torokhov 			return -EIO;
1005ee1e82ceSDmitry Torokhov 		}
1006ee1e82ceSDmitry Torokhov 
1007ee1e82ceSDmitry Torokhov 	} while (n < 2 || ctr[0] != ctr[1]);
1008ee1e82ceSDmitry Torokhov 
1009ee1e82ceSDmitry Torokhov 	i8042_initial_ctr = i8042_ctr = ctr[0];
10101da177e4SLinus Torvalds 
10111da177e4SLinus Torvalds /*
10121da177e4SLinus Torvalds  * Disable the keyboard interface and interrupt.
10131da177e4SLinus Torvalds  */
10141da177e4SLinus Torvalds 
10151da177e4SLinus Torvalds 	i8042_ctr |= I8042_CTR_KBDDIS;
10161da177e4SLinus Torvalds 	i8042_ctr &= ~I8042_CTR_KBDINT;
10171da177e4SLinus Torvalds 
10181da177e4SLinus Torvalds /*
10191da177e4SLinus Torvalds  * Handle keylock.
10201da177e4SLinus Torvalds  */
10211da177e4SLinus Torvalds 
10221da177e4SLinus Torvalds 	spin_lock_irqsave(&i8042_lock, flags);
10231da177e4SLinus Torvalds 	if (~i8042_read_status() & I8042_STR_KEYLOCK) {
10241da177e4SLinus Torvalds 		if (i8042_unlock)
10251da177e4SLinus Torvalds 			i8042_ctr |= I8042_CTR_IGNKEYLOCK;
10261da177e4SLinus Torvalds 		else
10274eb3c30bSJoe Perches 			pr_warn("Warning: Keylock active\n");
10281da177e4SLinus Torvalds 	}
10291da177e4SLinus Torvalds 	spin_unlock_irqrestore(&i8042_lock, flags);
10301da177e4SLinus Torvalds 
10311da177e4SLinus Torvalds /*
10321da177e4SLinus Torvalds  * If the chip is configured into nontranslated mode by the BIOS, don't
10331da177e4SLinus Torvalds  * bother enabling translating and be happy.
10341da177e4SLinus Torvalds  */
10351da177e4SLinus Torvalds 
10361da177e4SLinus Torvalds 	if (~i8042_ctr & I8042_CTR_XLATE)
1037386b3849SDmitry Torokhov 		i8042_direct = true;
10381da177e4SLinus Torvalds 
10391da177e4SLinus Torvalds /*
10401da177e4SLinus Torvalds  * Set nontranslated mode for the kbd interface if requested by an option.
10411da177e4SLinus Torvalds  * After this the kbd interface becomes a simple serial in/out, like the aux
10421da177e4SLinus Torvalds  * interface is. We don't do this by default, since it can confuse notebook
10431da177e4SLinus Torvalds  * BIOSes.
10441da177e4SLinus Torvalds  */
10451da177e4SLinus Torvalds 
10461da177e4SLinus Torvalds 	if (i8042_direct)
10471da177e4SLinus Torvalds 		i8042_ctr &= ~I8042_CTR_XLATE;
10481da177e4SLinus Torvalds 
10491da177e4SLinus Torvalds /*
10501da177e4SLinus Torvalds  * Write CTR back.
10511da177e4SLinus Torvalds  */
10521da177e4SLinus Torvalds 
10531da177e4SLinus Torvalds 	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
10544eb3c30bSJoe Perches 		pr_err("Can't write CTR while initializing i8042\n");
1055de9ce703SDmitry Torokhov 		return -EIO;
10561da177e4SLinus Torvalds 	}
10571da177e4SLinus Torvalds 
1058ee1e82ceSDmitry Torokhov /*
1059ee1e82ceSDmitry Torokhov  * Flush whatever accumulated while we were disabling keyboard port.
1060ee1e82ceSDmitry Torokhov  */
1061ee1e82ceSDmitry Torokhov 
1062ee1e82ceSDmitry Torokhov 	i8042_flush();
1063ee1e82ceSDmitry Torokhov 
10641da177e4SLinus Torvalds 	return 0;
10651da177e4SLinus Torvalds }
10661da177e4SLinus Torvalds 
10671da177e4SLinus Torvalds 
10681da177e4SLinus Torvalds /*
1069de9ce703SDmitry Torokhov  * Reset the controller and reset CRT to the original value set by BIOS.
10701da177e4SLinus Torvalds  */
10711da177e4SLinus Torvalds 
1072930e1924SMarcos Paulo de Souza static void i8042_controller_reset(bool s2r_wants_reset)
1073de9ce703SDmitry Torokhov {
1074de9ce703SDmitry Torokhov 	i8042_flush();
10751da177e4SLinus Torvalds 
10761da177e4SLinus Torvalds /*
10778d04ddb6SDmitry Torokhov  * Disable both KBD and AUX interfaces so they don't get in the way
10788d04ddb6SDmitry Torokhov  */
10798d04ddb6SDmitry Torokhov 
10808d04ddb6SDmitry Torokhov 	i8042_ctr |= I8042_CTR_KBDDIS | I8042_CTR_AUXDIS;
10818d04ddb6SDmitry Torokhov 	i8042_ctr &= ~(I8042_CTR_KBDINT | I8042_CTR_AUXINT);
10828d04ddb6SDmitry Torokhov 
1083ee1e82ceSDmitry Torokhov 	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
10844eb3c30bSJoe Perches 		pr_warn("Can't write CTR while resetting\n");
10855ddbc77cSDmitry Torokhov 
10868d04ddb6SDmitry Torokhov /*
10871da177e4SLinus Torvalds  * Disable MUX mode if present.
10881da177e4SLinus Torvalds  */
10891da177e4SLinus Torvalds 
10901da177e4SLinus Torvalds 	if (i8042_mux_present)
1091386b3849SDmitry Torokhov 		i8042_set_mux_mode(false, NULL);
10921da177e4SLinus Torvalds 
10931da177e4SLinus Torvalds /*
1094de9ce703SDmitry Torokhov  * Reset the controller if requested.
1095de9ce703SDmitry Torokhov  */
1096de9ce703SDmitry Torokhov 
1097930e1924SMarcos Paulo de Souza 	if (i8042_reset == I8042_RESET_ALWAYS ||
1098930e1924SMarcos Paulo de Souza 	    (i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) {
1099de9ce703SDmitry Torokhov 		i8042_controller_selftest();
1100930e1924SMarcos Paulo de Souza 	}
1101de9ce703SDmitry Torokhov 
1102de9ce703SDmitry Torokhov /*
11031da177e4SLinus Torvalds  * Restore the original control register setting.
11041da177e4SLinus Torvalds  */
11051da177e4SLinus Torvalds 
1106de9ce703SDmitry Torokhov 	if (i8042_command(&i8042_initial_ctr, I8042_CMD_CTL_WCTR))
11074eb3c30bSJoe Perches 		pr_warn("Can't restore CTR\n");
11081da177e4SLinus Torvalds }
11091da177e4SLinus Torvalds 
11101da177e4SLinus Torvalds 
11111da177e4SLinus Torvalds /*
1112c7ff0d9cSTAMUKI Shoichi  * i8042_panic_blink() will turn the keyboard LEDs on or off and is called
1113c7ff0d9cSTAMUKI Shoichi  * when kernel panics. Flashing LEDs is useful for users running X who may
1114aa5e5dc2SMichael Opdenacker  * not see the console and will help distinguishing panics from "real"
11151da177e4SLinus Torvalds  * lockups.
11161da177e4SLinus Torvalds  *
11171da177e4SLinus Torvalds  * Note that DELAY has a limit of 10ms so we will not get stuck here
11181da177e4SLinus Torvalds  * waiting for KBC to free up even if KBD interrupt is off
11191da177e4SLinus Torvalds  */
11201da177e4SLinus Torvalds 
11211da177e4SLinus Torvalds #define DELAY do { mdelay(1); if (++delay > 10) return delay; } while(0)
11221da177e4SLinus Torvalds 
1123c7ff0d9cSTAMUKI Shoichi static long i8042_panic_blink(int state)
11241da177e4SLinus Torvalds {
11251da177e4SLinus Torvalds 	long delay = 0;
1126c7ff0d9cSTAMUKI Shoichi 	char led;
11271da177e4SLinus Torvalds 
1128c7ff0d9cSTAMUKI Shoichi 	led = (state) ? 0x01 | 0x04 : 0;
11291da177e4SLinus Torvalds 	while (i8042_read_status() & I8042_STR_IBF)
11301da177e4SLinus Torvalds 		DELAY;
11314eb3c30bSJoe Perches 	dbg("%02x -> i8042 (panic blink)\n", 0xed);
113219f3c3e3SDmitry Torokhov 	i8042_suppress_kbd_ack = 2;
11331da177e4SLinus Torvalds 	i8042_write_data(0xed); /* set leds */
11341da177e4SLinus Torvalds 	DELAY;
11351da177e4SLinus Torvalds 	while (i8042_read_status() & I8042_STR_IBF)
11361da177e4SLinus Torvalds 		DELAY;
11371da177e4SLinus Torvalds 	DELAY;
11384eb3c30bSJoe Perches 	dbg("%02x -> i8042 (panic blink)\n", led);
11391da177e4SLinus Torvalds 	i8042_write_data(led);
11401da177e4SLinus Torvalds 	DELAY;
11411da177e4SLinus Torvalds 	return delay;
11421da177e4SLinus Torvalds }
11431da177e4SLinus Torvalds 
11441da177e4SLinus Torvalds #undef DELAY
11451da177e4SLinus Torvalds 
1146d35895dbSBruno Prémont #ifdef CONFIG_X86
1147d35895dbSBruno Prémont static void i8042_dritek_enable(void)
1148d35895dbSBruno Prémont {
1149594d6363SChristoph Fritz 	unsigned char param = 0x90;
1150d35895dbSBruno Prémont 	int error;
1151d35895dbSBruno Prémont 
1152d35895dbSBruno Prémont 	error = i8042_command(&param, 0x1059);
1153d35895dbSBruno Prémont 	if (error)
11544eb3c30bSJoe Perches 		pr_warn("Failed to enable DRITEK extension: %d\n", error);
1155d35895dbSBruno Prémont }
1156d35895dbSBruno Prémont #endif
1157d35895dbSBruno Prémont 
115882dd9effSDmitry Torokhov #ifdef CONFIG_PM
11597e044e05SDmitry Torokhov 
11601da177e4SLinus Torvalds /*
1161ebd7768dSDmitry Torokhov  * Here we try to reset everything back to a state we had
1162ebd7768dSDmitry Torokhov  * before suspending.
11631da177e4SLinus Torvalds  */
11641da177e4SLinus Torvalds 
1165930e1924SMarcos Paulo de Souza static int i8042_controller_resume(bool s2r_wants_reset)
11661da177e4SLinus Torvalds {
1167de9ce703SDmitry Torokhov 	int error;
11681da177e4SLinus Torvalds 
1169de9ce703SDmitry Torokhov 	error = i8042_controller_check();
1170de9ce703SDmitry Torokhov 	if (error)
1171de9ce703SDmitry Torokhov 		return error;
11722673c836SVojtech Pavlik 
1173930e1924SMarcos Paulo de Souza 	if (i8042_reset == I8042_RESET_ALWAYS ||
1174930e1924SMarcos Paulo de Souza 	    (i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) {
1175de9ce703SDmitry Torokhov 		error = i8042_controller_selftest();
1176de9ce703SDmitry Torokhov 		if (error)
1177de9ce703SDmitry Torokhov 			return error;
11781ca56e51SDmitry Torokhov 	}
1179de9ce703SDmitry Torokhov 
1180de9ce703SDmitry Torokhov /*
118182dd9effSDmitry Torokhov  * Restore original CTR value and disable all ports
1182de9ce703SDmitry Torokhov  */
1183de9ce703SDmitry Torokhov 
118482dd9effSDmitry Torokhov 	i8042_ctr = i8042_initial_ctr;
118582dd9effSDmitry Torokhov 	if (i8042_direct)
118682dd9effSDmitry Torokhov 		i8042_ctr &= ~I8042_CTR_XLATE;
1187de9ce703SDmitry Torokhov 	i8042_ctr |= I8042_CTR_AUXDIS | I8042_CTR_KBDDIS;
1188de9ce703SDmitry Torokhov 	i8042_ctr &= ~(I8042_CTR_AUXINT | I8042_CTR_KBDINT);
11892673c836SVojtech Pavlik 	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
11904eb3c30bSJoe Perches 		pr_warn("Can't write CTR to resume, retrying...\n");
11912f6a77d5SJiri Kosina 		msleep(50);
11922f6a77d5SJiri Kosina 		if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
11934eb3c30bSJoe Perches 			pr_err("CTR write retry failed\n");
1194de9ce703SDmitry Torokhov 			return -EIO;
11951da177e4SLinus Torvalds 		}
11962f6a77d5SJiri Kosina 	}
11971da177e4SLinus Torvalds 
1198d35895dbSBruno Prémont 
1199d35895dbSBruno Prémont #ifdef CONFIG_X86
1200d35895dbSBruno Prémont 	if (i8042_dritek)
1201d35895dbSBruno Prémont 		i8042_dritek_enable();
1202d35895dbSBruno Prémont #endif
1203d35895dbSBruno Prémont 
1204de9ce703SDmitry Torokhov 	if (i8042_mux_present) {
1205386b3849SDmitry Torokhov 		if (i8042_set_mux_mode(true, NULL) || i8042_enable_mux_ports())
12064eb3c30bSJoe Perches 			pr_warn("failed to resume active multiplexor, mouse won't work\n");
1207de9ce703SDmitry Torokhov 	} else if (i8042_ports[I8042_AUX_PORT_NO].serio)
1208de9ce703SDmitry Torokhov 		i8042_enable_aux_port();
12091da177e4SLinus Torvalds 
1210de9ce703SDmitry Torokhov 	if (i8042_ports[I8042_KBD_PORT_NO].serio)
1211de9ce703SDmitry Torokhov 		i8042_enable_kbd_port();
12121da177e4SLinus Torvalds 
12137d12e780SDavid Howells 	i8042_interrupt(0, NULL);
12141da177e4SLinus Torvalds 
12151da177e4SLinus Torvalds 	return 0;
12161da177e4SLinus Torvalds }
1217ebd7768dSDmitry Torokhov 
12181ca56e51SDmitry Torokhov /*
12191ca56e51SDmitry Torokhov  * Here we try to restore the original BIOS settings to avoid
12201ca56e51SDmitry Torokhov  * upsetting it.
12211ca56e51SDmitry Torokhov  */
12221ca56e51SDmitry Torokhov 
12231729ad1fSDmitry Torokhov static int i8042_pm_suspend(struct device *dev)
12241ca56e51SDmitry Torokhov {
1225f13b2065SRafael J. Wysocki 	int i;
1226f13b2065SRafael J. Wysocki 
12271c5dd134SRafael J. Wysocki 	if (pm_suspend_via_firmware())
12281729ad1fSDmitry Torokhov 		i8042_controller_reset(true);
12291ca56e51SDmitry Torokhov 
1230f13b2065SRafael J. Wysocki 	/* Set up serio interrupts for system wakeup. */
1231f13b2065SRafael J. Wysocki 	for (i = 0; i < I8042_NUM_PORTS; i++) {
1232f13b2065SRafael J. Wysocki 		struct serio *serio = i8042_ports[i].serio;
1233f13b2065SRafael J. Wysocki 
1234f13b2065SRafael J. Wysocki 		if (serio && device_may_wakeup(&serio->dev))
1235f13b2065SRafael J. Wysocki 			enable_irq_wake(i8042_ports[i].irq);
1236f13b2065SRafael J. Wysocki 	}
1237f13b2065SRafael J. Wysocki 
12381ca56e51SDmitry Torokhov 	return 0;
12391ca56e51SDmitry Torokhov }
12401ca56e51SDmitry Torokhov 
12411c5dd134SRafael J. Wysocki static int i8042_pm_resume_noirq(struct device *dev)
12421c5dd134SRafael J. Wysocki {
12431c5dd134SRafael J. Wysocki 	if (!pm_resume_via_firmware())
12441c5dd134SRafael J. Wysocki 		i8042_interrupt(0, NULL);
12451c5dd134SRafael J. Wysocki 
12461c5dd134SRafael J. Wysocki 	return 0;
12471c5dd134SRafael J. Wysocki }
12481c5dd134SRafael J. Wysocki 
12491ca56e51SDmitry Torokhov static int i8042_pm_resume(struct device *dev)
12501ca56e51SDmitry Torokhov {
1251930e1924SMarcos Paulo de Souza 	bool want_reset;
1252f13b2065SRafael J. Wysocki 	int i;
1253f13b2065SRafael J. Wysocki 
1254f13b2065SRafael J. Wysocki 	for (i = 0; i < I8042_NUM_PORTS; i++) {
1255f13b2065SRafael J. Wysocki 		struct serio *serio = i8042_ports[i].serio;
1256f13b2065SRafael J. Wysocki 
1257f13b2065SRafael J. Wysocki 		if (serio && device_may_wakeup(&serio->dev))
1258f13b2065SRafael J. Wysocki 			disable_irq_wake(i8042_ports[i].irq);
1259f13b2065SRafael J. Wysocki 	}
1260f13b2065SRafael J. Wysocki 
12611ca56e51SDmitry Torokhov 	/*
12621c5dd134SRafael J. Wysocki 	 * If platform firmware was not going to be involved in suspend, we did
12631c5dd134SRafael J. Wysocki 	 * not restore the controller state to whatever it had been at boot
12641c5dd134SRafael J. Wysocki 	 * time, so we do not need to do anything.
12651ca56e51SDmitry Torokhov 	 */
12661c5dd134SRafael J. Wysocki 	if (!pm_suspend_via_firmware())
12671c5dd134SRafael J. Wysocki 		return 0;
12681c5dd134SRafael J. Wysocki 
12691c5dd134SRafael J. Wysocki 	/*
12701c5dd134SRafael J. Wysocki 	 * We only need to reset the controller if we are resuming after handing
12711c5dd134SRafael J. Wysocki 	 * off control to the platform firmware, otherwise we can simply restore
12721c5dd134SRafael J. Wysocki 	 * the mode.
12731c5dd134SRafael J. Wysocki 	 */
1274930e1924SMarcos Paulo de Souza 	want_reset = pm_resume_via_firmware();
12751c5dd134SRafael J. Wysocki 
1276930e1924SMarcos Paulo de Souza 	return i8042_controller_resume(want_reset);
12771ca56e51SDmitry Torokhov }
12781ca56e51SDmitry Torokhov 
1279c2d1a2a1SAlan Jenkins static int i8042_pm_thaw(struct device *dev)
1280c2d1a2a1SAlan Jenkins {
1281c2d1a2a1SAlan Jenkins 	i8042_interrupt(0, NULL);
1282c2d1a2a1SAlan Jenkins 
1283c2d1a2a1SAlan Jenkins 	return 0;
1284c2d1a2a1SAlan Jenkins }
1285c2d1a2a1SAlan Jenkins 
12861729ad1fSDmitry Torokhov static int i8042_pm_reset(struct device *dev)
12871729ad1fSDmitry Torokhov {
12881729ad1fSDmitry Torokhov 	i8042_controller_reset(false);
12891729ad1fSDmitry Torokhov 
12901729ad1fSDmitry Torokhov 	return 0;
12911729ad1fSDmitry Torokhov }
12921729ad1fSDmitry Torokhov 
12931ca56e51SDmitry Torokhov static int i8042_pm_restore(struct device *dev)
12941ca56e51SDmitry Torokhov {
12951ca56e51SDmitry Torokhov 	return i8042_controller_resume(false);
12961ca56e51SDmitry Torokhov }
12971ca56e51SDmitry Torokhov 
1298ebd7768dSDmitry Torokhov static const struct dev_pm_ops i8042_pm_ops = {
12991729ad1fSDmitry Torokhov 	.suspend	= i8042_pm_suspend,
13001c5dd134SRafael J. Wysocki 	.resume_noirq	= i8042_pm_resume_noirq,
13011ca56e51SDmitry Torokhov 	.resume		= i8042_pm_resume,
1302c2d1a2a1SAlan Jenkins 	.thaw		= i8042_pm_thaw,
1303ebd7768dSDmitry Torokhov 	.poweroff	= i8042_pm_reset,
1304ebd7768dSDmitry Torokhov 	.restore	= i8042_pm_restore,
1305ebd7768dSDmitry Torokhov };
1306ebd7768dSDmitry Torokhov 
130782dd9effSDmitry Torokhov #endif /* CONFIG_PM */
13081da177e4SLinus Torvalds 
13091da177e4SLinus Torvalds /*
13101da177e4SLinus Torvalds  * We need to reset the 8042 back to original mode on system shutdown,
13111da177e4SLinus Torvalds  * because otherwise BIOSes will be confused.
13121da177e4SLinus Torvalds  */
13131da177e4SLinus Torvalds 
13143ae5eaecSRussell King static void i8042_shutdown(struct platform_device *dev)
13151da177e4SLinus Torvalds {
13161729ad1fSDmitry Torokhov 	i8042_controller_reset(false);
13171da177e4SLinus Torvalds }
13181da177e4SLinus Torvalds 
1319f8113416SDmitry Torokhov static int __init i8042_create_kbd_port(void)
13201da177e4SLinus Torvalds {
13211da177e4SLinus Torvalds 	struct serio *serio;
13221da177e4SLinus Torvalds 	struct i8042_port *port = &i8042_ports[I8042_KBD_PORT_NO];
13231da177e4SLinus Torvalds 
1324d39969deSDmitry Torokhov 	serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
13250854e52dSDmitry Torokhov 	if (!serio)
13260854e52dSDmitry Torokhov 		return -ENOMEM;
13270854e52dSDmitry Torokhov 
13281da177e4SLinus Torvalds 	serio->id.type		= i8042_direct ? SERIO_8042 : SERIO_8042_XL;
13291da177e4SLinus Torvalds 	serio->write		= i8042_dumbkbd ? NULL : i8042_kbd_write;
13301da177e4SLinus Torvalds 	serio->start		= i8042_start;
13311da177e4SLinus Torvalds 	serio->stop		= i8042_stop;
13325ddbc77cSDmitry Torokhov 	serio->close		= i8042_port_close;
133340974618SDmitry Torokhov 	serio->ps2_cmd_mutex	= &i8042_mutex;
13341da177e4SLinus Torvalds 	serio->port_data	= port;
13351da177e4SLinus Torvalds 	serio->dev.parent	= &i8042_platform_device->dev;
1336de9ce703SDmitry Torokhov 	strlcpy(serio->name, "i8042 KBD port", sizeof(serio->name));
13371da177e4SLinus Torvalds 	strlcpy(serio->phys, I8042_KBD_PHYS_DESC, sizeof(serio->phys));
1338a7c5868cSHans de Goede 	strlcpy(serio->firmware_id, i8042_kbd_firmware_id,
1339a7c5868cSHans de Goede 		sizeof(serio->firmware_id));
1340*6052abf8SRajat Jain 	set_primary_fwnode(&serio->dev, i8042_kbd_fwnode);
13411da177e4SLinus Torvalds 
13421da177e4SLinus Torvalds 	port->serio = serio;
1343de9ce703SDmitry Torokhov 	port->irq = I8042_KBD_IRQ;
13440854e52dSDmitry Torokhov 
1345de9ce703SDmitry Torokhov 	return 0;
13461da177e4SLinus Torvalds }
13471da177e4SLinus Torvalds 
1348f8113416SDmitry Torokhov static int __init i8042_create_aux_port(int idx)
13491da177e4SLinus Torvalds {
13501da177e4SLinus Torvalds 	struct serio *serio;
1351de9ce703SDmitry Torokhov 	int port_no = idx < 0 ? I8042_AUX_PORT_NO : I8042_MUX_PORT_NO + idx;
1352de9ce703SDmitry Torokhov 	struct i8042_port *port = &i8042_ports[port_no];
13531da177e4SLinus Torvalds 
1354d39969deSDmitry Torokhov 	serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
13550854e52dSDmitry Torokhov 	if (!serio)
13560854e52dSDmitry Torokhov 		return -ENOMEM;
13570854e52dSDmitry Torokhov 
13581da177e4SLinus Torvalds 	serio->id.type		= SERIO_8042;
13591da177e4SLinus Torvalds 	serio->write		= i8042_aux_write;
13601da177e4SLinus Torvalds 	serio->start		= i8042_start;
13611da177e4SLinus Torvalds 	serio->stop		= i8042_stop;
136247af45d6SDmitry Torokhov 	serio->ps2_cmd_mutex	= &i8042_mutex;
13631da177e4SLinus Torvalds 	serio->port_data	= port;
13641da177e4SLinus Torvalds 	serio->dev.parent	= &i8042_platform_device->dev;
1365de9ce703SDmitry Torokhov 	if (idx < 0) {
1366de9ce703SDmitry Torokhov 		strlcpy(serio->name, "i8042 AUX port", sizeof(serio->name));
13671da177e4SLinus Torvalds 		strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys));
1368a7c5868cSHans de Goede 		strlcpy(serio->firmware_id, i8042_aux_firmware_id,
1369a7c5868cSHans de Goede 			sizeof(serio->firmware_id));
13705ddbc77cSDmitry Torokhov 		serio->close = i8042_port_close;
1371de9ce703SDmitry Torokhov 	} else {
1372de9ce703SDmitry Torokhov 		snprintf(serio->name, sizeof(serio->name), "i8042 AUX%d port", idx);
1373de9ce703SDmitry Torokhov 		snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, idx + 1);
1374266e43c4SHans de Goede 		strlcpy(serio->firmware_id, i8042_aux_firmware_id,
1375266e43c4SHans de Goede 			sizeof(serio->firmware_id));
13761da177e4SLinus Torvalds 	}
13771da177e4SLinus Torvalds 
13781da177e4SLinus Torvalds 	port->serio = serio;
1379de9ce703SDmitry Torokhov 	port->mux = idx;
1380de9ce703SDmitry Torokhov 	port->irq = I8042_AUX_IRQ;
13810854e52dSDmitry Torokhov 
1382de9ce703SDmitry Torokhov 	return 0;
1383de9ce703SDmitry Torokhov }
1384de9ce703SDmitry Torokhov 
1385f8113416SDmitry Torokhov static void __init i8042_free_kbd_port(void)
1386de9ce703SDmitry Torokhov {
1387de9ce703SDmitry Torokhov 	kfree(i8042_ports[I8042_KBD_PORT_NO].serio);
1388de9ce703SDmitry Torokhov 	i8042_ports[I8042_KBD_PORT_NO].serio = NULL;
1389de9ce703SDmitry Torokhov }
1390de9ce703SDmitry Torokhov 
1391f8113416SDmitry Torokhov static void __init i8042_free_aux_ports(void)
1392de9ce703SDmitry Torokhov {
1393de9ce703SDmitry Torokhov 	int i;
1394de9ce703SDmitry Torokhov 
1395de9ce703SDmitry Torokhov 	for (i = I8042_AUX_PORT_NO; i < I8042_NUM_PORTS; i++) {
1396de9ce703SDmitry Torokhov 		kfree(i8042_ports[i].serio);
1397de9ce703SDmitry Torokhov 		i8042_ports[i].serio = NULL;
1398de9ce703SDmitry Torokhov 	}
1399de9ce703SDmitry Torokhov }
1400de9ce703SDmitry Torokhov 
1401f8113416SDmitry Torokhov static void __init i8042_register_ports(void)
1402de9ce703SDmitry Torokhov {
1403de9ce703SDmitry Torokhov 	int i;
1404de9ce703SDmitry Torokhov 
1405de9ce703SDmitry Torokhov 	for (i = 0; i < I8042_NUM_PORTS; i++) {
1406f13b2065SRafael J. Wysocki 		struct serio *serio = i8042_ports[i].serio;
1407f13b2065SRafael J. Wysocki 
1408684bec10SDaniel Drake 		if (!serio)
1409684bec10SDaniel Drake 			continue;
1410684bec10SDaniel Drake 
1411de9ce703SDmitry Torokhov 		printk(KERN_INFO "serio: %s at %#lx,%#lx irq %d\n",
1412f13b2065SRafael J. Wysocki 			serio->name,
1413de9ce703SDmitry Torokhov 			(unsigned long) I8042_DATA_REG,
1414de9ce703SDmitry Torokhov 			(unsigned long) I8042_COMMAND_REG,
1415de9ce703SDmitry Torokhov 			i8042_ports[i].irq);
1416f13b2065SRafael J. Wysocki 		serio_register_port(serio);
1417de9ce703SDmitry Torokhov 	}
1418de9ce703SDmitry Torokhov }
1419de9ce703SDmitry Torokhov 
1420e2619cf7SBill Pemberton static void i8042_unregister_ports(void)
1421de9ce703SDmitry Torokhov {
1422de9ce703SDmitry Torokhov 	int i;
1423de9ce703SDmitry Torokhov 
1424de9ce703SDmitry Torokhov 	for (i = 0; i < I8042_NUM_PORTS; i++) {
1425de9ce703SDmitry Torokhov 		if (i8042_ports[i].serio) {
1426de9ce703SDmitry Torokhov 			serio_unregister_port(i8042_ports[i].serio);
1427de9ce703SDmitry Torokhov 			i8042_ports[i].serio = NULL;
1428de9ce703SDmitry Torokhov 		}
1429de9ce703SDmitry Torokhov 	}
1430de9ce703SDmitry Torokhov }
1431de9ce703SDmitry Torokhov 
1432de9ce703SDmitry Torokhov static void i8042_free_irqs(void)
1433de9ce703SDmitry Torokhov {
1434de9ce703SDmitry Torokhov 	if (i8042_aux_irq_registered)
1435de9ce703SDmitry Torokhov 		free_irq(I8042_AUX_IRQ, i8042_platform_device);
1436de9ce703SDmitry Torokhov 	if (i8042_kbd_irq_registered)
1437de9ce703SDmitry Torokhov 		free_irq(I8042_KBD_IRQ, i8042_platform_device);
1438de9ce703SDmitry Torokhov 
1439386b3849SDmitry Torokhov 	i8042_aux_irq_registered = i8042_kbd_irq_registered = false;
1440de9ce703SDmitry Torokhov }
1441de9ce703SDmitry Torokhov 
1442f8113416SDmitry Torokhov static int __init i8042_setup_aux(void)
1443de9ce703SDmitry Torokhov {
1444de9ce703SDmitry Torokhov 	int (*aux_enable)(void);
1445de9ce703SDmitry Torokhov 	int error;
1446de9ce703SDmitry Torokhov 	int i;
1447de9ce703SDmitry Torokhov 
1448de9ce703SDmitry Torokhov 	if (i8042_check_aux())
1449de9ce703SDmitry Torokhov 		return -ENODEV;
1450de9ce703SDmitry Torokhov 
1451de9ce703SDmitry Torokhov 	if (i8042_nomux || i8042_check_mux()) {
1452de9ce703SDmitry Torokhov 		error = i8042_create_aux_port(-1);
1453de9ce703SDmitry Torokhov 		if (error)
1454de9ce703SDmitry Torokhov 			goto err_free_ports;
1455de9ce703SDmitry Torokhov 		aux_enable = i8042_enable_aux_port;
1456de9ce703SDmitry Torokhov 	} else {
1457de9ce703SDmitry Torokhov 		for (i = 0; i < I8042_NUM_MUX_PORTS; i++) {
1458de9ce703SDmitry Torokhov 			error = i8042_create_aux_port(i);
1459de9ce703SDmitry Torokhov 			if (error)
1460de9ce703SDmitry Torokhov 				goto err_free_ports;
1461de9ce703SDmitry Torokhov 		}
1462de9ce703SDmitry Torokhov 		aux_enable = i8042_enable_mux_ports;
1463de9ce703SDmitry Torokhov 	}
1464de9ce703SDmitry Torokhov 
1465de9ce703SDmitry Torokhov 	error = request_irq(I8042_AUX_IRQ, i8042_interrupt, IRQF_SHARED,
1466de9ce703SDmitry Torokhov 			    "i8042", i8042_platform_device);
1467de9ce703SDmitry Torokhov 	if (error)
1468de9ce703SDmitry Torokhov 		goto err_free_ports;
1469de9ce703SDmitry Torokhov 
1470de9ce703SDmitry Torokhov 	if (aux_enable())
1471de9ce703SDmitry Torokhov 		goto err_free_irq;
1472de9ce703SDmitry Torokhov 
1473386b3849SDmitry Torokhov 	i8042_aux_irq_registered = true;
1474de9ce703SDmitry Torokhov 	return 0;
1475de9ce703SDmitry Torokhov 
1476de9ce703SDmitry Torokhov  err_free_irq:
1477de9ce703SDmitry Torokhov 	free_irq(I8042_AUX_IRQ, i8042_platform_device);
1478de9ce703SDmitry Torokhov  err_free_ports:
1479de9ce703SDmitry Torokhov 	i8042_free_aux_ports();
1480de9ce703SDmitry Torokhov 	return error;
1481de9ce703SDmitry Torokhov }
1482de9ce703SDmitry Torokhov 
1483f8113416SDmitry Torokhov static int __init i8042_setup_kbd(void)
1484de9ce703SDmitry Torokhov {
1485de9ce703SDmitry Torokhov 	int error;
1486de9ce703SDmitry Torokhov 
1487de9ce703SDmitry Torokhov 	error = i8042_create_kbd_port();
1488de9ce703SDmitry Torokhov 	if (error)
1489de9ce703SDmitry Torokhov 		return error;
1490de9ce703SDmitry Torokhov 
1491de9ce703SDmitry Torokhov 	error = request_irq(I8042_KBD_IRQ, i8042_interrupt, IRQF_SHARED,
1492de9ce703SDmitry Torokhov 			    "i8042", i8042_platform_device);
1493de9ce703SDmitry Torokhov 	if (error)
1494de9ce703SDmitry Torokhov 		goto err_free_port;
1495de9ce703SDmitry Torokhov 
1496de9ce703SDmitry Torokhov 	error = i8042_enable_kbd_port();
1497de9ce703SDmitry Torokhov 	if (error)
1498de9ce703SDmitry Torokhov 		goto err_free_irq;
1499de9ce703SDmitry Torokhov 
1500386b3849SDmitry Torokhov 	i8042_kbd_irq_registered = true;
1501de9ce703SDmitry Torokhov 	return 0;
1502de9ce703SDmitry Torokhov 
1503de9ce703SDmitry Torokhov  err_free_irq:
1504de9ce703SDmitry Torokhov 	free_irq(I8042_KBD_IRQ, i8042_platform_device);
1505de9ce703SDmitry Torokhov  err_free_port:
1506de9ce703SDmitry Torokhov 	i8042_free_kbd_port();
1507de9ce703SDmitry Torokhov 	return error;
15081da177e4SLinus Torvalds }
15091da177e4SLinus Torvalds 
1510e1443d28SStephen Chandler Paul static int i8042_kbd_bind_notifier(struct notifier_block *nb,
1511e1443d28SStephen Chandler Paul 				   unsigned long action, void *data)
1512e1443d28SStephen Chandler Paul {
1513e1443d28SStephen Chandler Paul 	struct device *dev = data;
1514e1443d28SStephen Chandler Paul 	struct serio *serio = to_serio_port(dev);
1515e1443d28SStephen Chandler Paul 	struct i8042_port *port = serio->port_data;
1516e1443d28SStephen Chandler Paul 
1517e1443d28SStephen Chandler Paul 	if (serio != i8042_ports[I8042_KBD_PORT_NO].serio)
1518e1443d28SStephen Chandler Paul 		return 0;
1519e1443d28SStephen Chandler Paul 
1520e1443d28SStephen Chandler Paul 	switch (action) {
1521e1443d28SStephen Chandler Paul 	case BUS_NOTIFY_BOUND_DRIVER:
1522e1443d28SStephen Chandler Paul 		port->driver_bound = true;
1523e1443d28SStephen Chandler Paul 		break;
1524e1443d28SStephen Chandler Paul 
1525e1443d28SStephen Chandler Paul 	case BUS_NOTIFY_UNBIND_DRIVER:
1526e1443d28SStephen Chandler Paul 		port->driver_bound = false;
1527e1443d28SStephen Chandler Paul 		break;
1528e1443d28SStephen Chandler Paul 	}
1529e1443d28SStephen Chandler Paul 
1530e1443d28SStephen Chandler Paul 	return 0;
1531e1443d28SStephen Chandler Paul }
1532e1443d28SStephen Chandler Paul 
1533f8113416SDmitry Torokhov static int __init i8042_probe(struct platform_device *dev)
15341da177e4SLinus Torvalds {
1535de9ce703SDmitry Torokhov 	int error;
15361da177e4SLinus Torvalds 
1537ec62e1c8SDmitry Torokhov 	i8042_platform_device = dev;
1538ec62e1c8SDmitry Torokhov 
1539930e1924SMarcos Paulo de Souza 	if (i8042_reset == I8042_RESET_ALWAYS) {
1540de9ce703SDmitry Torokhov 		error = i8042_controller_selftest();
1541de9ce703SDmitry Torokhov 		if (error)
1542de9ce703SDmitry Torokhov 			return error;
15431ca56e51SDmitry Torokhov 	}
15441da177e4SLinus Torvalds 
1545de9ce703SDmitry Torokhov 	error = i8042_controller_init();
1546de9ce703SDmitry Torokhov 	if (error)
1547de9ce703SDmitry Torokhov 		return error;
15481da177e4SLinus Torvalds 
1549d35895dbSBruno Prémont #ifdef CONFIG_X86
1550d35895dbSBruno Prémont 	if (i8042_dritek)
1551d35895dbSBruno Prémont 		i8042_dritek_enable();
1552d35895dbSBruno Prémont #endif
1553d35895dbSBruno Prémont 
1554de9ce703SDmitry Torokhov 	if (!i8042_noaux) {
1555de9ce703SDmitry Torokhov 		error = i8042_setup_aux();
1556de9ce703SDmitry Torokhov 		if (error && error != -ENODEV && error != -EBUSY)
1557de9ce703SDmitry Torokhov 			goto out_fail;
15581da177e4SLinus Torvalds 	}
15591da177e4SLinus Torvalds 
1560945ef0d4SDmitry Torokhov 	if (!i8042_nokbd) {
1561de9ce703SDmitry Torokhov 		error = i8042_setup_kbd();
1562de9ce703SDmitry Torokhov 		if (error)
1563de9ce703SDmitry Torokhov 			goto out_fail;
1564945ef0d4SDmitry Torokhov 	}
1565de9ce703SDmitry Torokhov /*
1566de9ce703SDmitry Torokhov  * Ok, everything is ready, let's register all serio ports
1567de9ce703SDmitry Torokhov  */
1568de9ce703SDmitry Torokhov 	i8042_register_ports();
15691da177e4SLinus Torvalds 
15701da177e4SLinus Torvalds 	return 0;
15710854e52dSDmitry Torokhov 
1572de9ce703SDmitry Torokhov  out_fail:
1573de9ce703SDmitry Torokhov 	i8042_free_aux_ports();	/* in case KBD failed but AUX not */
1574de9ce703SDmitry Torokhov 	i8042_free_irqs();
15751729ad1fSDmitry Torokhov 	i8042_controller_reset(false);
1576ec62e1c8SDmitry Torokhov 	i8042_platform_device = NULL;
15770854e52dSDmitry Torokhov 
1578de9ce703SDmitry Torokhov 	return error;
15791da177e4SLinus Torvalds }
15801da177e4SLinus Torvalds 
1581e2619cf7SBill Pemberton static int i8042_remove(struct platform_device *dev)
15821da177e4SLinus Torvalds {
1583de9ce703SDmitry Torokhov 	i8042_unregister_ports();
1584de9ce703SDmitry Torokhov 	i8042_free_irqs();
15851729ad1fSDmitry Torokhov 	i8042_controller_reset(false);
1586ec62e1c8SDmitry Torokhov 	i8042_platform_device = NULL;
15871da177e4SLinus Torvalds 
158887fd6318SDmitry Torokhov 	return 0;
158987fd6318SDmitry Torokhov }
159087fd6318SDmitry Torokhov 
159187fd6318SDmitry Torokhov static struct platform_driver i8042_driver = {
159287fd6318SDmitry Torokhov 	.driver		= {
159387fd6318SDmitry Torokhov 		.name	= "i8042",
1594ebd7768dSDmitry Torokhov #ifdef CONFIG_PM
1595ebd7768dSDmitry Torokhov 		.pm	= &i8042_pm_ops,
1596ebd7768dSDmitry Torokhov #endif
159787fd6318SDmitry Torokhov 	},
15981cb0aa88SBill Pemberton 	.remove		= i8042_remove,
159982dd9effSDmitry Torokhov 	.shutdown	= i8042_shutdown,
160087fd6318SDmitry Torokhov };
160187fd6318SDmitry Torokhov 
1602e1443d28SStephen Chandler Paul static struct notifier_block i8042_kbd_bind_notifier_block = {
1603e1443d28SStephen Chandler Paul 	.notifier_call = i8042_kbd_bind_notifier,
1604e1443d28SStephen Chandler Paul };
1605e1443d28SStephen Chandler Paul 
160687fd6318SDmitry Torokhov static int __init i8042_init(void)
160787fd6318SDmitry Torokhov {
1608ec62e1c8SDmitry Torokhov 	struct platform_device *pdev;
160987fd6318SDmitry Torokhov 	int err;
161087fd6318SDmitry Torokhov 
161187fd6318SDmitry Torokhov 	dbg_init();
161287fd6318SDmitry Torokhov 
161387fd6318SDmitry Torokhov 	err = i8042_platform_init();
161487fd6318SDmitry Torokhov 	if (err)
161587fd6318SDmitry Torokhov 		return err;
161687fd6318SDmitry Torokhov 
1617de9ce703SDmitry Torokhov 	err = i8042_controller_check();
1618de9ce703SDmitry Torokhov 	if (err)
1619de9ce703SDmitry Torokhov 		goto err_platform_exit;
162087fd6318SDmitry Torokhov 
1621ec62e1c8SDmitry Torokhov 	pdev = platform_create_bundle(&i8042_driver, i8042_probe, NULL, 0, NULL, 0);
1622ec62e1c8SDmitry Torokhov 	if (IS_ERR(pdev)) {
1623ec62e1c8SDmitry Torokhov 		err = PTR_ERR(pdev);
1624f8113416SDmitry Torokhov 		goto err_platform_exit;
162587fd6318SDmitry Torokhov 	}
162687fd6318SDmitry Torokhov 
1627e1443d28SStephen Chandler Paul 	bus_register_notifier(&serio_bus, &i8042_kbd_bind_notifier_block);
1628de9ce703SDmitry Torokhov 	panic_blink = i8042_panic_blink;
1629de9ce703SDmitry Torokhov 
163087fd6318SDmitry Torokhov 	return 0;
163187fd6318SDmitry Torokhov 
163287fd6318SDmitry Torokhov  err_platform_exit:
163387fd6318SDmitry Torokhov 	i8042_platform_exit();
163487fd6318SDmitry Torokhov 	return err;
163587fd6318SDmitry Torokhov }
163687fd6318SDmitry Torokhov 
163787fd6318SDmitry Torokhov static void __exit i8042_exit(void)
163887fd6318SDmitry Torokhov {
1639f8113416SDmitry Torokhov 	platform_device_unregister(i8042_platform_device);
1640af045b86SDmitry Torokhov 	platform_driver_unregister(&i8042_driver);
16411da177e4SLinus Torvalds 	i8042_platform_exit();
16421da177e4SLinus Torvalds 
1643e1443d28SStephen Chandler Paul 	bus_unregister_notifier(&serio_bus, &i8042_kbd_bind_notifier_block);
16441da177e4SLinus Torvalds 	panic_blink = NULL;
16451da177e4SLinus Torvalds }
16461da177e4SLinus Torvalds 
16471da177e4SLinus Torvalds module_init(i8042_init);
16481da177e4SLinus Torvalds module_exit(i8042_exit);
1649