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