xref: /rk3399_rockchip-uboot/drivers/input/i8042.c (revision dcbf825714e27a4c39edcc36c7623b37bc40040a)
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>
11*dcbf8257SSimon Glass #include <dm.h>
12*dcbf8257SSimon Glass #include <errno.h>
1316b195c8SJean-Christophe PLAGNIOL-VILLARD #include <i8042.h>
142ec739dbSSimon Glass #include <input.h>
15*dcbf8257SSimon Glass #include <keyboard.h>
162ec739dbSSimon Glass #include <asm/io.h>
1716b195c8SJean-Christophe PLAGNIOL-VILLARD 
1816b195c8SJean-Christophe PLAGNIOL-VILLARD /* defines */
19835dd000SBin Meng #define in8(p)		inb(p)
20835dd000SBin Meng #define out8(p, v)	outb(v, p)
2116b195c8SJean-Christophe PLAGNIOL-VILLARD 
2216b195c8SJean-Christophe PLAGNIOL-VILLARD /* locals */
23*dcbf8257SSimon Glass struct i8042_kbd_priv {
24*dcbf8257SSimon Glass 	bool extended;	/* true if an extended keycode is expected next */
25*dcbf8257SSimon Glass };
2616b195c8SJean-Christophe PLAGNIOL-VILLARD 
27dd4a5b22SGabe Black static unsigned char ext_key_map[] = {
2816b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x1c, /* keypad enter */
2916b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x1d, /* right control */
3016b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x35, /* keypad slash */
3116b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x37, /* print screen */
3216b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x38, /* right alt */
3316b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x46, /* break */
3416b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x47, /* editpad home */
3516b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x48, /* editpad up */
3616b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x49, /* editpad pgup */
3716b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x4b, /* editpad left */
3816b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x4d, /* editpad right */
3916b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x4f, /* editpad end */
4016b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x50, /* editpad dn */
4116b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x51, /* editpad pgdn */
4216b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x52, /* editpad ins */
4316b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x53, /* editpad del */
4416b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x00  /* map end */
4516b195c8SJean-Christophe PLAGNIOL-VILLARD 	};
4616b195c8SJean-Christophe PLAGNIOL-VILLARD 
473928d66aSBin Meng static int kbd_input_empty(void)
483928d66aSBin Meng {
49835dd000SBin Meng 	int kbd_timeout = KBD_TIMEOUT * 1000;
503928d66aSBin Meng 
51835dd000SBin Meng 	while ((in8(I8042_STS_REG) & STATUS_IBF) && kbd_timeout--)
523928d66aSBin Meng 		udelay(1);
533928d66aSBin Meng 
54835dd000SBin Meng 	return kbd_timeout != -1;
553928d66aSBin Meng }
563928d66aSBin Meng 
57835dd000SBin Meng static int kbd_output_full(void)
583928d66aSBin Meng {
59835dd000SBin Meng 	int kbd_timeout = KBD_TIMEOUT * 1000;
603928d66aSBin Meng 
61835dd000SBin Meng 	while (((in8(I8042_STS_REG) & STATUS_OBF) == 0) && kbd_timeout--)
623928d66aSBin Meng 		udelay(1);
633928d66aSBin Meng 
64835dd000SBin Meng 	return kbd_timeout != -1;
653928d66aSBin Meng }
663928d66aSBin Meng 
67*dcbf8257SSimon Glass /**
68*dcbf8257SSimon Glass  * check_leds() - Check the keyboard LEDs and update them it needed
69*dcbf8257SSimon Glass  *
70*dcbf8257SSimon Glass  * @ret:	Value to return
71*dcbf8257SSimon Glass  * @return value of @ret
72*dcbf8257SSimon Glass  */
73*dcbf8257SSimon Glass static int i8042_kbd_update_leds(struct udevice *dev, int leds)
743928d66aSBin Meng {
753928d66aSBin Meng 	kbd_input_empty();
76835dd000SBin Meng 	out8(I8042_DATA_REG, CMD_SET_KBD_LED);
773928d66aSBin Meng 	kbd_input_empty();
78*dcbf8257SSimon Glass 	out8(I8042_DATA_REG, leds & 0x7);
79*dcbf8257SSimon Glass 
80*dcbf8257SSimon Glass 	return 0;
813928d66aSBin Meng }
823928d66aSBin Meng 
8331d38ee6SSimon Glass static int kbd_write(int reg, int value)
8431d38ee6SSimon Glass {
8531d38ee6SSimon Glass 	if (!kbd_input_empty())
8631d38ee6SSimon Glass 		return -1;
8731d38ee6SSimon Glass 	out8(reg, value);
8831d38ee6SSimon Glass 
8931d38ee6SSimon Glass 	return 0;
9031d38ee6SSimon Glass }
9131d38ee6SSimon Glass 
9231d38ee6SSimon Glass static int kbd_read(int reg)
9331d38ee6SSimon Glass {
9431d38ee6SSimon Glass 	if (!kbd_output_full())
9531d38ee6SSimon Glass 		return -1;
9631d38ee6SSimon Glass 
9731d38ee6SSimon Glass 	return in8(reg);
9831d38ee6SSimon Glass }
9931d38ee6SSimon Glass 
10031d38ee6SSimon Glass static int kbd_cmd_read(int cmd)
10131d38ee6SSimon Glass {
10231d38ee6SSimon Glass 	if (kbd_write(I8042_CMD_REG, cmd))
10331d38ee6SSimon Glass 		return -1;
10431d38ee6SSimon Glass 
10531d38ee6SSimon Glass 	return kbd_read(I8042_DATA_REG);
10631d38ee6SSimon Glass }
10731d38ee6SSimon Glass 
10831d38ee6SSimon Glass static int kbd_cmd_write(int cmd, int data)
10931d38ee6SSimon Glass {
11031d38ee6SSimon Glass 	if (kbd_write(I8042_CMD_REG, cmd))
11131d38ee6SSimon Glass 		return -1;
11231d38ee6SSimon Glass 
11331d38ee6SSimon Glass 	return kbd_write(I8042_DATA_REG, data);
11431d38ee6SSimon Glass }
11531d38ee6SSimon Glass 
1163928d66aSBin Meng static int kbd_reset(void)
1173928d66aSBin Meng {
11831d38ee6SSimon Glass 	int config;
1197d96166bSBin Meng 
1207d96166bSBin Meng 	/* controller self test */
12131d38ee6SSimon Glass 	if (kbd_cmd_read(CMD_SELF_TEST) != KBC_TEST_OK)
1224f087bacSSimon Glass 		goto err;
1233928d66aSBin Meng 
1247d96166bSBin Meng 	/* keyboard reset */
12531d38ee6SSimon Glass 	if (kbd_write(I8042_DATA_REG, CMD_RESET_KBD) ||
12631d38ee6SSimon Glass 	    kbd_read(I8042_DATA_REG) != KBD_ACK ||
12731d38ee6SSimon Glass 	    kbd_read(I8042_DATA_REG) != KBD_POR)
1284f087bacSSimon Glass 		goto err;
1293928d66aSBin Meng 
1307d96166bSBin Meng 	/* set AT translation and disable irq */
13131d38ee6SSimon Glass 	config = kbd_cmd_read(CMD_RD_CONFIG);
13231d38ee6SSimon Glass 	if (config == -1)
1334f087bacSSimon Glass 		goto err;
13431d38ee6SSimon Glass 
1357d96166bSBin Meng 	config |= CONFIG_AT_TRANS;
1367d96166bSBin Meng 	config &= ~(CONFIG_KIRQ_EN | CONFIG_MIRQ_EN);
13731d38ee6SSimon Glass 	if (kbd_cmd_write(CMD_WR_CONFIG, config))
1384f087bacSSimon Glass 		goto err;
1393928d66aSBin Meng 
1407d96166bSBin Meng 	/* enable keyboard */
14131d38ee6SSimon Glass 	if (kbd_write(I8042_CMD_REG, CMD_KBD_EN) ||
14231d38ee6SSimon Glass 	    !kbd_input_empty())
1434f087bacSSimon Glass 		goto err;
1443928d66aSBin Meng 
1453928d66aSBin Meng 	return 0;
1464f087bacSSimon Glass err:
1474f087bacSSimon Glass 	debug("%s: Keyboard failure\n", __func__);
1484f087bacSSimon Glass 	return -1;
1493928d66aSBin Meng }
1503928d66aSBin Meng 
15122e0f5a9SGabe Black static int kbd_controller_present(void)
15222e0f5a9SGabe Black {
153835dd000SBin Meng 	return in8(I8042_STS_REG) != 0xff;
15422e0f5a9SGabe Black }
15522e0f5a9SGabe Black 
15648edb304SGabe Black /*
15748edb304SGabe Black  * Implement a weak default function for boards that optionally
15848edb304SGabe Black  * need to skip the i8042 initialization.
159*dcbf8257SSimon Glass  *
160*dcbf8257SSimon Glass  * TODO(sjg@chromium.org): Use device tree for this?
16148edb304SGabe Black  */
16248edb304SGabe Black int __weak board_i8042_skip(void)
16348edb304SGabe Black {
16448edb304SGabe Black 	/* As default, don't skip */
16548edb304SGabe Black 	return 0;
16648edb304SGabe Black }
16748edb304SGabe Black 
16845fe668fSLouis Yung-Chieh Lo void i8042_flush(void)
16945fe668fSLouis Yung-Chieh Lo {
17045fe668fSLouis Yung-Chieh Lo 	int timeout;
17145fe668fSLouis Yung-Chieh Lo 
17245fe668fSLouis Yung-Chieh Lo 	/*
173835dd000SBin Meng 	 * The delay is to give the keyboard controller some time
174835dd000SBin Meng 	 * to fill the next byte.
17545fe668fSLouis Yung-Chieh Lo 	 */
17645fe668fSLouis Yung-Chieh Lo 	while (1) {
17745fe668fSLouis Yung-Chieh Lo 		timeout = 100;	/* wait for no longer than 100us */
178835dd000SBin Meng 		while (timeout > 0 && !(in8(I8042_STS_REG) & STATUS_OBF)) {
17945fe668fSLouis Yung-Chieh Lo 			udelay(1);
18045fe668fSLouis Yung-Chieh Lo 			timeout--;
18145fe668fSLouis Yung-Chieh Lo 		}
18245fe668fSLouis Yung-Chieh Lo 
183835dd000SBin Meng 		/* Try to pull next byte if not timeout */
184835dd000SBin Meng 		if (in8(I8042_STS_REG) & STATUS_OBF)
18545fe668fSLouis Yung-Chieh Lo 			in8(I8042_DATA_REG);
18645fe668fSLouis Yung-Chieh Lo 		else
18745fe668fSLouis Yung-Chieh Lo 			break;
18845fe668fSLouis Yung-Chieh Lo 	}
18945fe668fSLouis Yung-Chieh Lo }
19045fe668fSLouis Yung-Chieh Lo 
19145fe668fSLouis Yung-Chieh Lo int i8042_disable(void)
19245fe668fSLouis Yung-Chieh Lo {
19345fe668fSLouis Yung-Chieh Lo 	if (kbd_input_empty() == 0)
19445fe668fSLouis Yung-Chieh Lo 		return -1;
19545fe668fSLouis Yung-Chieh Lo 
19645fe668fSLouis Yung-Chieh Lo 	/* Disable keyboard */
197835dd000SBin Meng 	out8(I8042_CMD_REG, CMD_KBD_DIS);
19845fe668fSLouis Yung-Chieh Lo 
19945fe668fSLouis Yung-Chieh Lo 	if (kbd_input_empty() == 0)
20045fe668fSLouis Yung-Chieh Lo 		return -1;
20145fe668fSLouis Yung-Chieh Lo 
20245fe668fSLouis Yung-Chieh Lo 	return 0;
20345fe668fSLouis Yung-Chieh Lo }
20445fe668fSLouis Yung-Chieh Lo 
2052ec739dbSSimon Glass static int i8042_kbd_check(struct input_config *input)
2062ec739dbSSimon Glass {
207*dcbf8257SSimon Glass 	struct i8042_kbd_priv *priv = dev_get_priv(input->dev);
208*dcbf8257SSimon Glass 
2092ec739dbSSimon Glass 	if ((in8(I8042_STS_REG) & STATUS_OBF) == 0) {
2102ec739dbSSimon Glass 		return 0;
2112ec739dbSSimon Glass 	} else {
2122ec739dbSSimon Glass 		bool release = false;
2132ec739dbSSimon Glass 		int scan_code;
2142ec739dbSSimon Glass 		int i;
2152ec739dbSSimon Glass 
2162ec739dbSSimon Glass 		scan_code = in8(I8042_DATA_REG);
2172ec739dbSSimon Glass 		if (scan_code == 0xfa) {
2182ec739dbSSimon Glass 			return 0;
2192ec739dbSSimon Glass 		} else if (scan_code == 0xe0) {
220*dcbf8257SSimon Glass 			priv->extended = true;
2212ec739dbSSimon Glass 			return 0;
2222ec739dbSSimon Glass 		}
2232ec739dbSSimon Glass 		if (scan_code & 0x80) {
2242ec739dbSSimon Glass 			scan_code &= 0x7f;
2252ec739dbSSimon Glass 			release = true;
2262ec739dbSSimon Glass 		}
227*dcbf8257SSimon Glass 		if (priv->extended) {
228*dcbf8257SSimon Glass 			priv->extended = false;
2292ec739dbSSimon Glass 			for (i = 0; ext_key_map[i]; i++) {
2302ec739dbSSimon Glass 				if (ext_key_map[i] == scan_code) {
2312ec739dbSSimon Glass 					scan_code = 0x60 + i;
2322ec739dbSSimon Glass 					break;
2332ec739dbSSimon Glass 				}
2342ec739dbSSimon Glass 			}
2352ec739dbSSimon Glass 			/* not found ? */
2362ec739dbSSimon Glass 			if (!ext_key_map[i])
2372ec739dbSSimon Glass 				return 0;
2382ec739dbSSimon Glass 		}
2392ec739dbSSimon Glass 
240*dcbf8257SSimon Glass 		input_add_keycode(input, scan_code, release);
2412ec739dbSSimon Glass 		return 1;
2422ec739dbSSimon Glass 	}
2432ec739dbSSimon Glass }
2442ec739dbSSimon Glass 
245835dd000SBin Meng /* i8042_kbd_init - reset keyboard and init state flags */
246*dcbf8257SSimon Glass static int i8042_start(struct udevice *dev)
24716b195c8SJean-Christophe PLAGNIOL-VILLARD {
248*dcbf8257SSimon Glass 	struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev);
249*dcbf8257SSimon Glass 	struct input_config *input = &uc_priv->input;
25016b195c8SJean-Christophe PLAGNIOL-VILLARD 	int keymap, try;
25116b195c8SJean-Christophe PLAGNIOL-VILLARD 	char *penv;
2522ec739dbSSimon Glass 	int ret;
25316b195c8SJean-Christophe PLAGNIOL-VILLARD 
254835dd000SBin Meng 	if (!kbd_controller_present() || board_i8042_skip()) {
255835dd000SBin Meng 		debug("i8042 keyboard controller is not present\n");
256*dcbf8257SSimon Glass 		return -ENOENT;
257835dd000SBin Meng 	}
25822e0f5a9SGabe Black 
25916b195c8SJean-Christophe PLAGNIOL-VILLARD 	/* Init keyboard device (default US layout) */
26016b195c8SJean-Christophe PLAGNIOL-VILLARD 	keymap = KBD_US;
261dd4a5b22SGabe Black 	penv = getenv("keymap");
262dd4a5b22SGabe Black 	if (penv != NULL) {
26316b195c8SJean-Christophe PLAGNIOL-VILLARD 		if (strncmp(penv, "de", 3) == 0)
26416b195c8SJean-Christophe PLAGNIOL-VILLARD 			keymap = KBD_GER;
26516b195c8SJean-Christophe PLAGNIOL-VILLARD 	}
26616b195c8SJean-Christophe PLAGNIOL-VILLARD 
267c5d257f9SSimon Glass 	for (try = 0; kbd_reset() != 0; try++) {
268c5d257f9SSimon Glass 		if (try >= KBD_RESET_TRIES)
269c5d257f9SSimon Glass 			return -1;
270c5d257f9SSimon Glass 	}
271c5d257f9SSimon Glass 
272*dcbf8257SSimon Glass 	ret = input_add_tables(input, keymap == KBD_GER);
2732ec739dbSSimon Glass 	if (ret)
2742ec739dbSSimon Glass 		return ret;
2752ec739dbSSimon Glass 
276*dcbf8257SSimon Glass 	i8042_kbd_update_leds(dev, NORMAL);
277*dcbf8257SSimon Glass 	debug("%s: started\n", __func__);
278835dd000SBin Meng 
27916b195c8SJean-Christophe PLAGNIOL-VILLARD 	return 0;
28016b195c8SJean-Christophe PLAGNIOL-VILLARD }
28116b195c8SJean-Christophe PLAGNIOL-VILLARD 
2822ec739dbSSimon Glass /**
283*dcbf8257SSimon Glass  * Set up the i8042 keyboard. This is called by the stdio device handler
284835dd000SBin Meng  *
285*dcbf8257SSimon Glass  * We want to do this init when the keyboard is actually used rather than
286*dcbf8257SSimon Glass  * at start-up, since keyboard input may not currently be selected.
287*dcbf8257SSimon Glass  *
288*dcbf8257SSimon Glass  * Once the keyboard starts there will be a period during which we must
289*dcbf8257SSimon Glass  * wait for the keyboard to init. We do this only when a key is first
290*dcbf8257SSimon Glass  * read - see kbd_wait_for_fifo_init().
291*dcbf8257SSimon Glass  *
292*dcbf8257SSimon Glass  * @return 0 if ok, -ve on error
29316b195c8SJean-Christophe PLAGNIOL-VILLARD  */
294*dcbf8257SSimon Glass static int i8042_kbd_probe(struct udevice *dev)
2952ec739dbSSimon Glass {
296*dcbf8257SSimon Glass 	struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev);
297*dcbf8257SSimon Glass 	struct stdio_dev *sdev = &uc_priv->sdev;
298*dcbf8257SSimon Glass 	struct input_config *input = &uc_priv->input;
299*dcbf8257SSimon Glass 	int ret;
3002ec739dbSSimon Glass 
301*dcbf8257SSimon Glass 	/* Register the device. i8042_start() will be called soon */
302*dcbf8257SSimon Glass 	input->dev = dev;
303*dcbf8257SSimon Glass 	input->read_keys = i8042_kbd_check;
304*dcbf8257SSimon Glass 	input_allow_repeats(input, true);
305*dcbf8257SSimon Glass 	strcpy(sdev->name, "i8042-kbd");
306*dcbf8257SSimon Glass 	ret = input_stdio_register(sdev);
307*dcbf8257SSimon Glass 	if (ret) {
308*dcbf8257SSimon Glass 		debug("%s: input_stdio_register() failed\n", __func__);
3092ec739dbSSimon Glass 		return ret;
3102ec739dbSSimon Glass 	}
311*dcbf8257SSimon Glass 	debug("%s: ready\n", __func__);
3122ec739dbSSimon Glass 
313*dcbf8257SSimon Glass 	return 0;
31416b195c8SJean-Christophe PLAGNIOL-VILLARD }
315835dd000SBin Meng 
316*dcbf8257SSimon Glass static const struct keyboard_ops i8042_kbd_ops = {
317*dcbf8257SSimon Glass 	.start	= i8042_start,
318*dcbf8257SSimon Glass 	.update_leds	= i8042_kbd_update_leds,
319*dcbf8257SSimon Glass };
320*dcbf8257SSimon Glass 
321*dcbf8257SSimon Glass static const struct udevice_id i8042_kbd_ids[] = {
322*dcbf8257SSimon Glass 	{ .compatible = "intel,i8042-keyboard" },
323*dcbf8257SSimon Glass 	{ }
324*dcbf8257SSimon Glass };
325*dcbf8257SSimon Glass 
326*dcbf8257SSimon Glass U_BOOT_DRIVER(i8042_kbd) = {
327*dcbf8257SSimon Glass 	.name	= "i8042_kbd",
328*dcbf8257SSimon Glass 	.id	= UCLASS_KEYBOARD,
329*dcbf8257SSimon Glass 	.of_match = i8042_kbd_ids,
330*dcbf8257SSimon Glass 	.probe = i8042_kbd_probe,
331*dcbf8257SSimon Glass 	.ops	= &i8042_kbd_ops,
332*dcbf8257SSimon Glass 	.priv_auto_alloc_size = sizeof(struct i8042_kbd_priv),
333*dcbf8257SSimon Glass };
334