xref: /rk3399_rockchip-uboot/drivers/input/i8042.c (revision 00caae6d47645e68d6e5277aceb69592b49381a6)
116b195c8SJean-Christophe PLAGNIOL-VILLARD /*
216b195c8SJean-Christophe PLAGNIOL-VILLARD  * (C) Copyright 2002 ELTEC Elektronik AG
316b195c8SJean-Christophe PLAGNIOL-VILLARD  * Frank Gottschling <fgottschling@eltec.de>
416b195c8SJean-Christophe PLAGNIOL-VILLARD  *
51a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
616b195c8SJean-Christophe PLAGNIOL-VILLARD  */
716b195c8SJean-Christophe PLAGNIOL-VILLARD 
816b195c8SJean-Christophe PLAGNIOL-VILLARD /* i8042.c - Intel 8042 keyboard driver routines */
916b195c8SJean-Christophe PLAGNIOL-VILLARD 
1016b195c8SJean-Christophe PLAGNIOL-VILLARD #include <common.h>
11dcbf8257SSimon Glass #include <dm.h>
12dcbf8257SSimon Glass #include <errno.h>
1316b195c8SJean-Christophe PLAGNIOL-VILLARD #include <i8042.h>
142ec739dbSSimon Glass #include <input.h>
15dcbf8257SSimon Glass #include <keyboard.h>
162ec739dbSSimon Glass #include <asm/io.h>
1716b195c8SJean-Christophe PLAGNIOL-VILLARD 
18011d89d6SSimon Glass DECLARE_GLOBAL_DATA_PTR;
19011d89d6SSimon Glass 
2016b195c8SJean-Christophe PLAGNIOL-VILLARD /* defines */
21835dd000SBin Meng #define in8(p)		inb(p)
22835dd000SBin Meng #define out8(p, v)	outb(v, p)
2316b195c8SJean-Christophe PLAGNIOL-VILLARD 
24011d89d6SSimon Glass enum {
25011d89d6SSimon Glass 	QUIRK_DUP_POR	= 1 << 0,
26011d89d6SSimon Glass };
27011d89d6SSimon Glass 
2816b195c8SJean-Christophe PLAGNIOL-VILLARD /* locals */
29dcbf8257SSimon Glass struct i8042_kbd_priv {
30dcbf8257SSimon Glass 	bool extended;	/* true if an extended keycode is expected next */
31011d89d6SSimon Glass 	int quirks;	/* quirks that we support */
32dcbf8257SSimon Glass };
3316b195c8SJean-Christophe PLAGNIOL-VILLARD 
34dd4a5b22SGabe Black static unsigned char ext_key_map[] = {
3516b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x1c, /* keypad enter */
3616b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x1d, /* right control */
3716b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x35, /* keypad slash */
3816b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x37, /* print screen */
3916b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x38, /* right alt */
4016b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x46, /* break */
4116b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x47, /* editpad home */
4216b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x48, /* editpad up */
4316b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x49, /* editpad pgup */
4416b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x4b, /* editpad left */
4516b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x4d, /* editpad right */
4616b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x4f, /* editpad end */
4716b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x50, /* editpad dn */
4816b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x51, /* editpad pgdn */
4916b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x52, /* editpad ins */
5016b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x53, /* editpad del */
5116b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x00  /* map end */
5216b195c8SJean-Christophe PLAGNIOL-VILLARD 	};
5316b195c8SJean-Christophe PLAGNIOL-VILLARD 
kbd_input_empty(void)543928d66aSBin Meng static int kbd_input_empty(void)
553928d66aSBin Meng {
56835dd000SBin Meng 	int kbd_timeout = KBD_TIMEOUT * 1000;
573928d66aSBin Meng 
58835dd000SBin Meng 	while ((in8(I8042_STS_REG) & STATUS_IBF) && kbd_timeout--)
593928d66aSBin Meng 		udelay(1);
603928d66aSBin Meng 
61835dd000SBin Meng 	return kbd_timeout != -1;
623928d66aSBin Meng }
633928d66aSBin Meng 
kbd_output_full(void)64835dd000SBin Meng static int kbd_output_full(void)
653928d66aSBin Meng {
66835dd000SBin Meng 	int kbd_timeout = KBD_TIMEOUT * 1000;
673928d66aSBin Meng 
68835dd000SBin Meng 	while (((in8(I8042_STS_REG) & STATUS_OBF) == 0) && kbd_timeout--)
693928d66aSBin Meng 		udelay(1);
703928d66aSBin Meng 
71835dd000SBin Meng 	return kbd_timeout != -1;
723928d66aSBin Meng }
733928d66aSBin Meng 
74dcbf8257SSimon Glass /**
75dcbf8257SSimon Glass  * check_leds() - Check the keyboard LEDs and update them it needed
76dcbf8257SSimon Glass  *
77dcbf8257SSimon Glass  * @ret:	Value to return
78dcbf8257SSimon Glass  * @return value of @ret
79dcbf8257SSimon Glass  */
i8042_kbd_update_leds(struct udevice * dev,int leds)80dcbf8257SSimon Glass static int i8042_kbd_update_leds(struct udevice *dev, int leds)
813928d66aSBin Meng {
823928d66aSBin Meng 	kbd_input_empty();
83835dd000SBin Meng 	out8(I8042_DATA_REG, CMD_SET_KBD_LED);
843928d66aSBin Meng 	kbd_input_empty();
85dcbf8257SSimon Glass 	out8(I8042_DATA_REG, leds & 0x7);
86dcbf8257SSimon Glass 
87dcbf8257SSimon Glass 	return 0;
883928d66aSBin Meng }
893928d66aSBin Meng 
kbd_write(int reg,int value)9031d38ee6SSimon Glass static int kbd_write(int reg, int value)
9131d38ee6SSimon Glass {
9231d38ee6SSimon Glass 	if (!kbd_input_empty())
9331d38ee6SSimon Glass 		return -1;
9431d38ee6SSimon Glass 	out8(reg, value);
9531d38ee6SSimon Glass 
9631d38ee6SSimon Glass 	return 0;
9731d38ee6SSimon Glass }
9831d38ee6SSimon Glass 
kbd_read(int reg)9931d38ee6SSimon Glass static int kbd_read(int reg)
10031d38ee6SSimon Glass {
10131d38ee6SSimon Glass 	if (!kbd_output_full())
10231d38ee6SSimon Glass 		return -1;
10331d38ee6SSimon Glass 
10431d38ee6SSimon Glass 	return in8(reg);
10531d38ee6SSimon Glass }
10631d38ee6SSimon Glass 
kbd_cmd_read(int cmd)10731d38ee6SSimon Glass static int kbd_cmd_read(int cmd)
10831d38ee6SSimon Glass {
10931d38ee6SSimon Glass 	if (kbd_write(I8042_CMD_REG, cmd))
11031d38ee6SSimon Glass 		return -1;
11131d38ee6SSimon Glass 
11231d38ee6SSimon Glass 	return kbd_read(I8042_DATA_REG);
11331d38ee6SSimon Glass }
11431d38ee6SSimon Glass 
kbd_cmd_write(int cmd,int data)11531d38ee6SSimon Glass static int kbd_cmd_write(int cmd, int data)
11631d38ee6SSimon Glass {
11731d38ee6SSimon Glass 	if (kbd_write(I8042_CMD_REG, cmd))
11831d38ee6SSimon Glass 		return -1;
11931d38ee6SSimon Glass 
12031d38ee6SSimon Glass 	return kbd_write(I8042_DATA_REG, data);
12131d38ee6SSimon Glass }
12231d38ee6SSimon Glass 
kbd_reset(int quirk)123011d89d6SSimon Glass static int kbd_reset(int quirk)
1243928d66aSBin Meng {
12531d38ee6SSimon Glass 	int config;
1267d96166bSBin Meng 
1277d96166bSBin Meng 	/* controller self test */
12831d38ee6SSimon Glass 	if (kbd_cmd_read(CMD_SELF_TEST) != KBC_TEST_OK)
1294f087bacSSimon Glass 		goto err;
1303928d66aSBin Meng 
1317d96166bSBin Meng 	/* keyboard reset */
13231d38ee6SSimon Glass 	if (kbd_write(I8042_DATA_REG, CMD_RESET_KBD) ||
13331d38ee6SSimon Glass 	    kbd_read(I8042_DATA_REG) != KBD_ACK ||
13431d38ee6SSimon Glass 	    kbd_read(I8042_DATA_REG) != KBD_POR)
1354f087bacSSimon Glass 		goto err;
1363928d66aSBin Meng 
1378226a3e9SSimon Glass 	if (kbd_write(I8042_DATA_REG, CMD_DRAIN_OUTPUT) ||
1388226a3e9SSimon Glass 	    kbd_read(I8042_DATA_REG) != KBD_ACK)
1398226a3e9SSimon Glass 		goto err;
1408226a3e9SSimon Glass 
1417d96166bSBin Meng 	/* set AT translation and disable irq */
14231d38ee6SSimon Glass 	config = kbd_cmd_read(CMD_RD_CONFIG);
14331d38ee6SSimon Glass 	if (config == -1)
1444f087bacSSimon Glass 		goto err;
14531d38ee6SSimon Glass 
146011d89d6SSimon Glass 	/* Sometimes get a second byte */
147011d89d6SSimon Glass 	else if ((quirk & QUIRK_DUP_POR) && config == KBD_POR)
148011d89d6SSimon Glass 		config = kbd_cmd_read(CMD_RD_CONFIG);
149011d89d6SSimon Glass 
1507d96166bSBin Meng 	config |= CONFIG_AT_TRANS;
1517d96166bSBin Meng 	config &= ~(CONFIG_KIRQ_EN | CONFIG_MIRQ_EN);
15231d38ee6SSimon Glass 	if (kbd_cmd_write(CMD_WR_CONFIG, config))
1534f087bacSSimon Glass 		goto err;
1543928d66aSBin Meng 
1557d96166bSBin Meng 	/* enable keyboard */
15631d38ee6SSimon Glass 	if (kbd_write(I8042_CMD_REG, CMD_KBD_EN) ||
15731d38ee6SSimon Glass 	    !kbd_input_empty())
1584f087bacSSimon Glass 		goto err;
1593928d66aSBin Meng 
1603928d66aSBin Meng 	return 0;
1614f087bacSSimon Glass err:
1624f087bacSSimon Glass 	debug("%s: Keyboard failure\n", __func__);
1634f087bacSSimon Glass 	return -1;
1643928d66aSBin Meng }
1653928d66aSBin Meng 
kbd_controller_present(void)16622e0f5a9SGabe Black static int kbd_controller_present(void)
16722e0f5a9SGabe Black {
168835dd000SBin Meng 	return in8(I8042_STS_REG) != 0xff;
16922e0f5a9SGabe Black }
17022e0f5a9SGabe Black 
17148edb304SGabe Black /*
17248edb304SGabe Black  * Implement a weak default function for boards that optionally
17348edb304SGabe Black  * need to skip the i8042 initialization.
174dcbf8257SSimon Glass  *
175dcbf8257SSimon Glass  * TODO(sjg@chromium.org): Use device tree for this?
17648edb304SGabe Black  */
board_i8042_skip(void)17748edb304SGabe Black int __weak board_i8042_skip(void)
17848edb304SGabe Black {
17948edb304SGabe Black 	/* As default, don't skip */
18048edb304SGabe Black 	return 0;
18148edb304SGabe Black }
18248edb304SGabe Black 
i8042_flush(void)18345fe668fSLouis Yung-Chieh Lo void i8042_flush(void)
18445fe668fSLouis Yung-Chieh Lo {
18545fe668fSLouis Yung-Chieh Lo 	int timeout;
18645fe668fSLouis Yung-Chieh Lo 
18745fe668fSLouis Yung-Chieh Lo 	/*
188835dd000SBin Meng 	 * The delay is to give the keyboard controller some time
189835dd000SBin Meng 	 * to fill the next byte.
19045fe668fSLouis Yung-Chieh Lo 	 */
19145fe668fSLouis Yung-Chieh Lo 	while (1) {
19245fe668fSLouis Yung-Chieh Lo 		timeout = 100;	/* wait for no longer than 100us */
193835dd000SBin Meng 		while (timeout > 0 && !(in8(I8042_STS_REG) & STATUS_OBF)) {
19445fe668fSLouis Yung-Chieh Lo 			udelay(1);
19545fe668fSLouis Yung-Chieh Lo 			timeout--;
19645fe668fSLouis Yung-Chieh Lo 		}
19745fe668fSLouis Yung-Chieh Lo 
198835dd000SBin Meng 		/* Try to pull next byte if not timeout */
199835dd000SBin Meng 		if (in8(I8042_STS_REG) & STATUS_OBF)
20045fe668fSLouis Yung-Chieh Lo 			in8(I8042_DATA_REG);
20145fe668fSLouis Yung-Chieh Lo 		else
20245fe668fSLouis Yung-Chieh Lo 			break;
20345fe668fSLouis Yung-Chieh Lo 	}
20445fe668fSLouis Yung-Chieh Lo }
20545fe668fSLouis Yung-Chieh Lo 
i8042_disable(void)20645fe668fSLouis Yung-Chieh Lo int i8042_disable(void)
20745fe668fSLouis Yung-Chieh Lo {
20845fe668fSLouis Yung-Chieh Lo 	if (kbd_input_empty() == 0)
20945fe668fSLouis Yung-Chieh Lo 		return -1;
21045fe668fSLouis Yung-Chieh Lo 
21145fe668fSLouis Yung-Chieh Lo 	/* Disable keyboard */
212835dd000SBin Meng 	out8(I8042_CMD_REG, CMD_KBD_DIS);
21345fe668fSLouis Yung-Chieh Lo 
21445fe668fSLouis Yung-Chieh Lo 	if (kbd_input_empty() == 0)
21545fe668fSLouis Yung-Chieh Lo 		return -1;
21645fe668fSLouis Yung-Chieh Lo 
21745fe668fSLouis Yung-Chieh Lo 	return 0;
21845fe668fSLouis Yung-Chieh Lo }
21945fe668fSLouis Yung-Chieh Lo 
i8042_kbd_check(struct input_config * input)2202ec739dbSSimon Glass static int i8042_kbd_check(struct input_config *input)
2212ec739dbSSimon Glass {
222dcbf8257SSimon Glass 	struct i8042_kbd_priv *priv = dev_get_priv(input->dev);
223dcbf8257SSimon Glass 
2242ec739dbSSimon Glass 	if ((in8(I8042_STS_REG) & STATUS_OBF) == 0) {
2252ec739dbSSimon Glass 		return 0;
2262ec739dbSSimon Glass 	} else {
2272ec739dbSSimon Glass 		bool release = false;
2282ec739dbSSimon Glass 		int scan_code;
2292ec739dbSSimon Glass 		int i;
2302ec739dbSSimon Glass 
2312ec739dbSSimon Glass 		scan_code = in8(I8042_DATA_REG);
2322ec739dbSSimon Glass 		if (scan_code == 0xfa) {
2332ec739dbSSimon Glass 			return 0;
2342ec739dbSSimon Glass 		} else if (scan_code == 0xe0) {
235dcbf8257SSimon Glass 			priv->extended = true;
2362ec739dbSSimon Glass 			return 0;
2372ec739dbSSimon Glass 		}
2382ec739dbSSimon Glass 		if (scan_code & 0x80) {
2392ec739dbSSimon Glass 			scan_code &= 0x7f;
2402ec739dbSSimon Glass 			release = true;
2412ec739dbSSimon Glass 		}
242dcbf8257SSimon Glass 		if (priv->extended) {
243dcbf8257SSimon Glass 			priv->extended = false;
2442ec739dbSSimon Glass 			for (i = 0; ext_key_map[i]; i++) {
2452ec739dbSSimon Glass 				if (ext_key_map[i] == scan_code) {
2462ec739dbSSimon Glass 					scan_code = 0x60 + i;
2472ec739dbSSimon Glass 					break;
2482ec739dbSSimon Glass 				}
2492ec739dbSSimon Glass 			}
2502ec739dbSSimon Glass 			/* not found ? */
2512ec739dbSSimon Glass 			if (!ext_key_map[i])
2522ec739dbSSimon Glass 				return 0;
2532ec739dbSSimon Glass 		}
2542ec739dbSSimon Glass 
255dcbf8257SSimon Glass 		input_add_keycode(input, scan_code, release);
2562ec739dbSSimon Glass 		return 1;
2572ec739dbSSimon Glass 	}
2582ec739dbSSimon Glass }
2592ec739dbSSimon Glass 
260835dd000SBin Meng /* i8042_kbd_init - reset keyboard and init state flags */
i8042_start(struct udevice * dev)261dcbf8257SSimon Glass static int i8042_start(struct udevice *dev)
26216b195c8SJean-Christophe PLAGNIOL-VILLARD {
263dcbf8257SSimon Glass 	struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev);
264011d89d6SSimon Glass 	struct i8042_kbd_priv *priv = dev_get_priv(dev);
265dcbf8257SSimon Glass 	struct input_config *input = &uc_priv->input;
26616b195c8SJean-Christophe PLAGNIOL-VILLARD 	int keymap, try;
26716b195c8SJean-Christophe PLAGNIOL-VILLARD 	char *penv;
2682ec739dbSSimon Glass 	int ret;
26916b195c8SJean-Christophe PLAGNIOL-VILLARD 
270835dd000SBin Meng 	if (!kbd_controller_present() || board_i8042_skip()) {
271835dd000SBin Meng 		debug("i8042 keyboard controller is not present\n");
272dcbf8257SSimon Glass 		return -ENOENT;
273835dd000SBin Meng 	}
27422e0f5a9SGabe Black 
27516b195c8SJean-Christophe PLAGNIOL-VILLARD 	/* Init keyboard device (default US layout) */
27616b195c8SJean-Christophe PLAGNIOL-VILLARD 	keymap = KBD_US;
277*00caae6dSSimon Glass 	penv = env_get("keymap");
278dd4a5b22SGabe Black 	if (penv != NULL) {
27916b195c8SJean-Christophe PLAGNIOL-VILLARD 		if (strncmp(penv, "de", 3) == 0)
28016b195c8SJean-Christophe PLAGNIOL-VILLARD 			keymap = KBD_GER;
28116b195c8SJean-Christophe PLAGNIOL-VILLARD 	}
28216b195c8SJean-Christophe PLAGNIOL-VILLARD 
283011d89d6SSimon Glass 	for (try = 0; kbd_reset(priv->quirks) != 0; try++) {
284c5d257f9SSimon Glass 		if (try >= KBD_RESET_TRIES)
285c5d257f9SSimon Glass 			return -1;
286c5d257f9SSimon Glass 	}
287c5d257f9SSimon Glass 
288dcbf8257SSimon Glass 	ret = input_add_tables(input, keymap == KBD_GER);
2892ec739dbSSimon Glass 	if (ret)
2902ec739dbSSimon Glass 		return ret;
2912ec739dbSSimon Glass 
292dcbf8257SSimon Glass 	i8042_kbd_update_leds(dev, NORMAL);
293dcbf8257SSimon Glass 	debug("%s: started\n", __func__);
294835dd000SBin Meng 
29516b195c8SJean-Christophe PLAGNIOL-VILLARD 	return 0;
29616b195c8SJean-Christophe PLAGNIOL-VILLARD }
29716b195c8SJean-Christophe PLAGNIOL-VILLARD 
2982ec739dbSSimon Glass /**
299dcbf8257SSimon Glass  * Set up the i8042 keyboard. This is called by the stdio device handler
300835dd000SBin Meng  *
301dcbf8257SSimon Glass  * We want to do this init when the keyboard is actually used rather than
302dcbf8257SSimon Glass  * at start-up, since keyboard input may not currently be selected.
303dcbf8257SSimon Glass  *
304dcbf8257SSimon Glass  * Once the keyboard starts there will be a period during which we must
305dcbf8257SSimon Glass  * wait for the keyboard to init. We do this only when a key is first
306dcbf8257SSimon Glass  * read - see kbd_wait_for_fifo_init().
307dcbf8257SSimon Glass  *
308dcbf8257SSimon Glass  * @return 0 if ok, -ve on error
30916b195c8SJean-Christophe PLAGNIOL-VILLARD  */
i8042_kbd_probe(struct udevice * dev)310dcbf8257SSimon Glass static int i8042_kbd_probe(struct udevice *dev)
3112ec739dbSSimon Glass {
312dcbf8257SSimon Glass 	struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev);
313011d89d6SSimon Glass 	struct i8042_kbd_priv *priv = dev_get_priv(dev);
314dcbf8257SSimon Glass 	struct stdio_dev *sdev = &uc_priv->sdev;
315dcbf8257SSimon Glass 	struct input_config *input = &uc_priv->input;
316dcbf8257SSimon Glass 	int ret;
3172ec739dbSSimon Glass 
318e160f7d4SSimon Glass 	if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
319011d89d6SSimon Glass 			    "intel,duplicate-por"))
320011d89d6SSimon Glass 		priv->quirks |= QUIRK_DUP_POR;
321011d89d6SSimon Glass 
322dcbf8257SSimon Glass 	/* Register the device. i8042_start() will be called soon */
323dcbf8257SSimon Glass 	input->dev = dev;
324dcbf8257SSimon Glass 	input->read_keys = i8042_kbd_check;
325dcbf8257SSimon Glass 	input_allow_repeats(input, true);
326dcbf8257SSimon Glass 	strcpy(sdev->name, "i8042-kbd");
327dcbf8257SSimon Glass 	ret = input_stdio_register(sdev);
328dcbf8257SSimon Glass 	if (ret) {
329dcbf8257SSimon Glass 		debug("%s: input_stdio_register() failed\n", __func__);
3302ec739dbSSimon Glass 		return ret;
3312ec739dbSSimon Glass 	}
332dcbf8257SSimon Glass 	debug("%s: ready\n", __func__);
3332ec739dbSSimon Glass 
334dcbf8257SSimon Glass 	return 0;
33516b195c8SJean-Christophe PLAGNIOL-VILLARD }
336835dd000SBin Meng 
337dcbf8257SSimon Glass static const struct keyboard_ops i8042_kbd_ops = {
338dcbf8257SSimon Glass 	.start	= i8042_start,
339dcbf8257SSimon Glass 	.update_leds	= i8042_kbd_update_leds,
340dcbf8257SSimon Glass };
341dcbf8257SSimon Glass 
342dcbf8257SSimon Glass static const struct udevice_id i8042_kbd_ids[] = {
343dcbf8257SSimon Glass 	{ .compatible = "intel,i8042-keyboard" },
344dcbf8257SSimon Glass 	{ }
345dcbf8257SSimon Glass };
346dcbf8257SSimon Glass 
347dcbf8257SSimon Glass U_BOOT_DRIVER(i8042_kbd) = {
348dcbf8257SSimon Glass 	.name	= "i8042_kbd",
349dcbf8257SSimon Glass 	.id	= UCLASS_KEYBOARD,
350dcbf8257SSimon Glass 	.of_match = i8042_kbd_ids,
351dcbf8257SSimon Glass 	.probe = i8042_kbd_probe,
352dcbf8257SSimon Glass 	.ops	= &i8042_kbd_ops,
353dcbf8257SSimon Glass 	.priv_auto_alloc_size = sizeof(struct i8042_kbd_priv),
354dcbf8257SSimon Glass };
355