xref: /rk3399_rockchip-uboot/drivers/input/i8042.c (revision 2ec739db5b8d079ca9e21db79b9015bb5ec6e4de)
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>
1116b195c8SJean-Christophe PLAGNIOL-VILLARD #include <i8042.h>
12*2ec739dbSSimon Glass #include <input.h>
13*2ec739dbSSimon Glass #include <asm/io.h>
1416b195c8SJean-Christophe PLAGNIOL-VILLARD 
1516b195c8SJean-Christophe PLAGNIOL-VILLARD /* defines */
16835dd000SBin Meng #define in8(p)		inb(p)
17835dd000SBin Meng #define out8(p, v)	outb(v, p)
1816b195c8SJean-Christophe PLAGNIOL-VILLARD 
1916b195c8SJean-Christophe PLAGNIOL-VILLARD /* locals */
20*2ec739dbSSimon Glass static struct input_config config;
21*2ec739dbSSimon Glass static bool extended;
2216b195c8SJean-Christophe PLAGNIOL-VILLARD 
23dd4a5b22SGabe Black static unsigned char ext_key_map[] = {
2416b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x1c, /* keypad enter */
2516b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x1d, /* right control */
2616b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x35, /* keypad slash */
2716b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x37, /* print screen */
2816b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x38, /* right alt */
2916b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x46, /* break */
3016b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x47, /* editpad home */
3116b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x48, /* editpad up */
3216b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x49, /* editpad pgup */
3316b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x4b, /* editpad left */
3416b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x4d, /* editpad right */
3516b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x4f, /* editpad end */
3616b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x50, /* editpad dn */
3716b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x51, /* editpad pgdn */
3816b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x52, /* editpad ins */
3916b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x53, /* editpad del */
4016b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x00  /* map end */
4116b195c8SJean-Christophe PLAGNIOL-VILLARD 	};
4216b195c8SJean-Christophe PLAGNIOL-VILLARD 
433928d66aSBin Meng static int kbd_input_empty(void)
443928d66aSBin Meng {
45835dd000SBin Meng 	int kbd_timeout = KBD_TIMEOUT * 1000;
463928d66aSBin Meng 
47835dd000SBin Meng 	while ((in8(I8042_STS_REG) & STATUS_IBF) && kbd_timeout--)
483928d66aSBin Meng 		udelay(1);
493928d66aSBin Meng 
50835dd000SBin Meng 	return kbd_timeout != -1;
513928d66aSBin Meng }
523928d66aSBin Meng 
53835dd000SBin Meng static int kbd_output_full(void)
543928d66aSBin Meng {
55835dd000SBin Meng 	int kbd_timeout = KBD_TIMEOUT * 1000;
563928d66aSBin Meng 
57835dd000SBin Meng 	while (((in8(I8042_STS_REG) & STATUS_OBF) == 0) && kbd_timeout--)
583928d66aSBin Meng 		udelay(1);
593928d66aSBin Meng 
60835dd000SBin Meng 	return kbd_timeout != -1;
613928d66aSBin Meng }
623928d66aSBin Meng 
63*2ec739dbSSimon Glass static void kbd_led_set(int flags)
643928d66aSBin Meng {
653928d66aSBin Meng 	kbd_input_empty();
66835dd000SBin Meng 	out8(I8042_DATA_REG, CMD_SET_KBD_LED);
673928d66aSBin Meng 	kbd_input_empty();
68*2ec739dbSSimon Glass 	out8(I8042_DATA_REG, flags & 0x7);
693928d66aSBin Meng }
703928d66aSBin Meng 
7131d38ee6SSimon Glass static int kbd_write(int reg, int value)
7231d38ee6SSimon Glass {
7331d38ee6SSimon Glass 	if (!kbd_input_empty())
7431d38ee6SSimon Glass 		return -1;
7531d38ee6SSimon Glass 	out8(reg, value);
7631d38ee6SSimon Glass 
7731d38ee6SSimon Glass 	return 0;
7831d38ee6SSimon Glass }
7931d38ee6SSimon Glass 
8031d38ee6SSimon Glass static int kbd_read(int reg)
8131d38ee6SSimon Glass {
8231d38ee6SSimon Glass 	if (!kbd_output_full())
8331d38ee6SSimon Glass 		return -1;
8431d38ee6SSimon Glass 
8531d38ee6SSimon Glass 	return in8(reg);
8631d38ee6SSimon Glass }
8731d38ee6SSimon Glass 
8831d38ee6SSimon Glass static int kbd_cmd_read(int cmd)
8931d38ee6SSimon Glass {
9031d38ee6SSimon Glass 	if (kbd_write(I8042_CMD_REG, cmd))
9131d38ee6SSimon Glass 		return -1;
9231d38ee6SSimon Glass 
9331d38ee6SSimon Glass 	return kbd_read(I8042_DATA_REG);
9431d38ee6SSimon Glass }
9531d38ee6SSimon Glass 
9631d38ee6SSimon Glass static int kbd_cmd_write(int cmd, int data)
9731d38ee6SSimon Glass {
9831d38ee6SSimon Glass 	if (kbd_write(I8042_CMD_REG, cmd))
9931d38ee6SSimon Glass 		return -1;
10031d38ee6SSimon Glass 
10131d38ee6SSimon Glass 	return kbd_write(I8042_DATA_REG, data);
10231d38ee6SSimon Glass }
10331d38ee6SSimon Glass 
1043928d66aSBin Meng static int kbd_reset(void)
1053928d66aSBin Meng {
10631d38ee6SSimon Glass 	int config;
1077d96166bSBin Meng 
1087d96166bSBin Meng 	/* controller self test */
10931d38ee6SSimon Glass 	if (kbd_cmd_read(CMD_SELF_TEST) != KBC_TEST_OK)
1104f087bacSSimon Glass 		goto err;
1113928d66aSBin Meng 
1127d96166bSBin Meng 	/* keyboard reset */
11331d38ee6SSimon Glass 	if (kbd_write(I8042_DATA_REG, CMD_RESET_KBD) ||
11431d38ee6SSimon Glass 	    kbd_read(I8042_DATA_REG) != KBD_ACK ||
11531d38ee6SSimon Glass 	    kbd_read(I8042_DATA_REG) != KBD_POR)
1164f087bacSSimon Glass 		goto err;
1173928d66aSBin Meng 
1187d96166bSBin Meng 	/* set AT translation and disable irq */
11931d38ee6SSimon Glass 	config = kbd_cmd_read(CMD_RD_CONFIG);
12031d38ee6SSimon Glass 	if (config == -1)
1214f087bacSSimon Glass 		goto err;
12231d38ee6SSimon Glass 
1237d96166bSBin Meng 	config |= CONFIG_AT_TRANS;
1247d96166bSBin Meng 	config &= ~(CONFIG_KIRQ_EN | CONFIG_MIRQ_EN);
12531d38ee6SSimon Glass 	if (kbd_cmd_write(CMD_WR_CONFIG, config))
1264f087bacSSimon Glass 		goto err;
1273928d66aSBin Meng 
1287d96166bSBin Meng 	/* enable keyboard */
12931d38ee6SSimon Glass 	if (kbd_write(I8042_CMD_REG, CMD_KBD_EN) ||
13031d38ee6SSimon Glass 	    !kbd_input_empty())
1314f087bacSSimon Glass 		goto err;
1323928d66aSBin Meng 
1333928d66aSBin Meng 	return 0;
1344f087bacSSimon Glass err:
1354f087bacSSimon Glass 	debug("%s: Keyboard failure\n", __func__);
1364f087bacSSimon Glass 	return -1;
1373928d66aSBin Meng }
1383928d66aSBin Meng 
13922e0f5a9SGabe Black static int kbd_controller_present(void)
14022e0f5a9SGabe Black {
141835dd000SBin Meng 	return in8(I8042_STS_REG) != 0xff;
14222e0f5a9SGabe Black }
14322e0f5a9SGabe Black 
14448edb304SGabe Black /*
14548edb304SGabe Black  * Implement a weak default function for boards that optionally
14648edb304SGabe Black  * need to skip the i8042 initialization.
14748edb304SGabe Black  */
14848edb304SGabe Black int __weak board_i8042_skip(void)
14948edb304SGabe Black {
15048edb304SGabe Black 	/* As default, don't skip */
15148edb304SGabe Black 	return 0;
15248edb304SGabe Black }
15348edb304SGabe Black 
15445fe668fSLouis Yung-Chieh Lo void i8042_flush(void)
15545fe668fSLouis Yung-Chieh Lo {
15645fe668fSLouis Yung-Chieh Lo 	int timeout;
15745fe668fSLouis Yung-Chieh Lo 
15845fe668fSLouis Yung-Chieh Lo 	/*
159835dd000SBin Meng 	 * The delay is to give the keyboard controller some time
160835dd000SBin Meng 	 * to fill the next byte.
16145fe668fSLouis Yung-Chieh Lo 	 */
16245fe668fSLouis Yung-Chieh Lo 	while (1) {
16345fe668fSLouis Yung-Chieh Lo 		timeout = 100;	/* wait for no longer than 100us */
164835dd000SBin Meng 		while (timeout > 0 && !(in8(I8042_STS_REG) & STATUS_OBF)) {
16545fe668fSLouis Yung-Chieh Lo 			udelay(1);
16645fe668fSLouis Yung-Chieh Lo 			timeout--;
16745fe668fSLouis Yung-Chieh Lo 		}
16845fe668fSLouis Yung-Chieh Lo 
169835dd000SBin Meng 		/* Try to pull next byte if not timeout */
170835dd000SBin Meng 		if (in8(I8042_STS_REG) & STATUS_OBF)
17145fe668fSLouis Yung-Chieh Lo 			in8(I8042_DATA_REG);
17245fe668fSLouis Yung-Chieh Lo 		else
17345fe668fSLouis Yung-Chieh Lo 			break;
17445fe668fSLouis Yung-Chieh Lo 	}
17545fe668fSLouis Yung-Chieh Lo }
17645fe668fSLouis Yung-Chieh Lo 
17745fe668fSLouis Yung-Chieh Lo int i8042_disable(void)
17845fe668fSLouis Yung-Chieh Lo {
17945fe668fSLouis Yung-Chieh Lo 	if (kbd_input_empty() == 0)
18045fe668fSLouis Yung-Chieh Lo 		return -1;
18145fe668fSLouis Yung-Chieh Lo 
18245fe668fSLouis Yung-Chieh Lo 	/* Disable keyboard */
183835dd000SBin Meng 	out8(I8042_CMD_REG, CMD_KBD_DIS);
18445fe668fSLouis Yung-Chieh Lo 
18545fe668fSLouis Yung-Chieh Lo 	if (kbd_input_empty() == 0)
18645fe668fSLouis Yung-Chieh Lo 		return -1;
18745fe668fSLouis Yung-Chieh Lo 
18845fe668fSLouis Yung-Chieh Lo 	return 0;
18945fe668fSLouis Yung-Chieh Lo }
19045fe668fSLouis Yung-Chieh Lo 
191*2ec739dbSSimon Glass static int i8042_kbd_check(struct input_config *input)
192*2ec739dbSSimon Glass {
193*2ec739dbSSimon Glass 	if ((in8(I8042_STS_REG) & STATUS_OBF) == 0) {
194*2ec739dbSSimon Glass 		return 0;
195*2ec739dbSSimon Glass 	} else {
196*2ec739dbSSimon Glass 		bool release = false;
197*2ec739dbSSimon Glass 		int scan_code;
198*2ec739dbSSimon Glass 		int i;
199*2ec739dbSSimon Glass 
200*2ec739dbSSimon Glass 		scan_code = in8(I8042_DATA_REG);
201*2ec739dbSSimon Glass 		if (scan_code == 0xfa) {
202*2ec739dbSSimon Glass 			return 0;
203*2ec739dbSSimon Glass 		} else if (scan_code == 0xe0) {
204*2ec739dbSSimon Glass 			extended = true;
205*2ec739dbSSimon Glass 			return 0;
206*2ec739dbSSimon Glass 		}
207*2ec739dbSSimon Glass 		if (scan_code & 0x80) {
208*2ec739dbSSimon Glass 			scan_code &= 0x7f;
209*2ec739dbSSimon Glass 			release = true;
210*2ec739dbSSimon Glass 		}
211*2ec739dbSSimon Glass 		if (extended) {
212*2ec739dbSSimon Glass 			extended = false;
213*2ec739dbSSimon Glass 			for (i = 0; ext_key_map[i]; i++) {
214*2ec739dbSSimon Glass 				if (ext_key_map[i] == scan_code) {
215*2ec739dbSSimon Glass 					scan_code = 0x60 + i;
216*2ec739dbSSimon Glass 					break;
217*2ec739dbSSimon Glass 				}
218*2ec739dbSSimon Glass 			}
219*2ec739dbSSimon Glass 			/* not found ? */
220*2ec739dbSSimon Glass 			if (!ext_key_map[i])
221*2ec739dbSSimon Glass 				return 0;
222*2ec739dbSSimon Glass 		}
223*2ec739dbSSimon Glass 
224*2ec739dbSSimon Glass 		input_add_keycode(&config, scan_code, release);
225*2ec739dbSSimon Glass 		return 1;
226*2ec739dbSSimon Glass 	}
227*2ec739dbSSimon Glass }
228*2ec739dbSSimon Glass 
229835dd000SBin Meng /* i8042_kbd_init - reset keyboard and init state flags */
23016b195c8SJean-Christophe PLAGNIOL-VILLARD int i8042_kbd_init(void)
23116b195c8SJean-Christophe PLAGNIOL-VILLARD {
23216b195c8SJean-Christophe PLAGNIOL-VILLARD 	int keymap, try;
23316b195c8SJean-Christophe PLAGNIOL-VILLARD 	char *penv;
234*2ec739dbSSimon Glass 	int ret;
23516b195c8SJean-Christophe PLAGNIOL-VILLARD 
236835dd000SBin Meng 	if (!kbd_controller_present() || board_i8042_skip()) {
237835dd000SBin Meng 		debug("i8042 keyboard controller is not present\n");
23822e0f5a9SGabe Black 		return -1;
239835dd000SBin Meng 	}
24022e0f5a9SGabe Black 
24116b195c8SJean-Christophe PLAGNIOL-VILLARD 	/* Init keyboard device (default US layout) */
24216b195c8SJean-Christophe PLAGNIOL-VILLARD 	keymap = KBD_US;
243dd4a5b22SGabe Black 	penv = getenv("keymap");
244dd4a5b22SGabe Black 	if (penv != NULL) {
24516b195c8SJean-Christophe PLAGNIOL-VILLARD 		if (strncmp(penv, "de", 3) == 0)
24616b195c8SJean-Christophe PLAGNIOL-VILLARD 			keymap = KBD_GER;
24716b195c8SJean-Christophe PLAGNIOL-VILLARD 	}
24816b195c8SJean-Christophe PLAGNIOL-VILLARD 
249c5d257f9SSimon Glass 	for (try = 0; kbd_reset() != 0; try++) {
250c5d257f9SSimon Glass 		if (try >= KBD_RESET_TRIES)
251c5d257f9SSimon Glass 			return -1;
252c5d257f9SSimon Glass 	}
253c5d257f9SSimon Glass 
254*2ec739dbSSimon Glass 	ret = input_init(&config, keymap == KBD_GER);
255*2ec739dbSSimon Glass 	if (ret)
256*2ec739dbSSimon Glass 		return ret;
257*2ec739dbSSimon Glass 	config.read_keys = i8042_kbd_check;
258*2ec739dbSSimon Glass 	input_allow_repeats(&config, true);
259*2ec739dbSSimon Glass 
260*2ec739dbSSimon Glass 	kbd_led_set(NORMAL);
261835dd000SBin Meng 
26216b195c8SJean-Christophe PLAGNIOL-VILLARD 	return 0;
26316b195c8SJean-Christophe PLAGNIOL-VILLARD }
26416b195c8SJean-Christophe PLAGNIOL-VILLARD 
265*2ec739dbSSimon Glass /**
266*2ec739dbSSimon Glass  * check_leds() - Check the keyboard LEDs and update them it needed
267835dd000SBin Meng  *
268*2ec739dbSSimon Glass  * @ret:	Value to return
269*2ec739dbSSimon Glass  * @return value of @ret
27016b195c8SJean-Christophe PLAGNIOL-VILLARD  */
271*2ec739dbSSimon Glass static int check_leds(int ret)
272*2ec739dbSSimon Glass {
273*2ec739dbSSimon Glass 	int leds;
274*2ec739dbSSimon Glass 
275*2ec739dbSSimon Glass 	leds = input_leds_changed(&config);
276*2ec739dbSSimon Glass 	if (leds >= 0)
277*2ec739dbSSimon Glass 		kbd_led_set(leds);
278*2ec739dbSSimon Glass 
279*2ec739dbSSimon Glass 	return ret;
280*2ec739dbSSimon Glass }
281*2ec739dbSSimon Glass 
282*2ec739dbSSimon Glass /* i8042_tstc - test if keyboard input is available */
283709ea543SSimon Glass int i8042_tstc(struct stdio_dev *dev)
28416b195c8SJean-Christophe PLAGNIOL-VILLARD {
285*2ec739dbSSimon Glass 	return check_leds(input_tstc(&config));
28616b195c8SJean-Christophe PLAGNIOL-VILLARD }
287835dd000SBin Meng 
288*2ec739dbSSimon Glass /* i8042_getc - wait till keyboard input is available */
289709ea543SSimon Glass int i8042_getc(struct stdio_dev *dev)
29016b195c8SJean-Christophe PLAGNIOL-VILLARD {
291*2ec739dbSSimon Glass 	return check_leds(input_getc(&config));
29216b195c8SJean-Christophe PLAGNIOL-VILLARD }
293