xref: /openbmc/u-boot/drivers/input/i8042.c (revision 6b44ae6b)
1 /*
2  * (C) Copyright 2002 ELTEC Elektronik AG
3  * Frank Gottschling <fgottschling@eltec.de>
4  *
5  * SPDX-License-Identifier:	GPL-2.0+
6  */
7 
8 /* i8042.c - Intel 8042 keyboard driver routines */
9 
10 #include <common.h>
11 #include <i8042.h>
12 #include <input.h>
13 #include <asm/io.h>
14 
15 /* defines */
16 #define in8(p)		inb(p)
17 #define out8(p, v)	outb(v, p)
18 
19 /* locals */
20 static struct input_config config;
21 static bool extended;
22 
23 static unsigned char ext_key_map[] = {
24 	0x1c, /* keypad enter */
25 	0x1d, /* right control */
26 	0x35, /* keypad slash */
27 	0x37, /* print screen */
28 	0x38, /* right alt */
29 	0x46, /* break */
30 	0x47, /* editpad home */
31 	0x48, /* editpad up */
32 	0x49, /* editpad pgup */
33 	0x4b, /* editpad left */
34 	0x4d, /* editpad right */
35 	0x4f, /* editpad end */
36 	0x50, /* editpad dn */
37 	0x51, /* editpad pgdn */
38 	0x52, /* editpad ins */
39 	0x53, /* editpad del */
40 	0x00  /* map end */
41 	};
42 
43 static int kbd_input_empty(void)
44 {
45 	int kbd_timeout = KBD_TIMEOUT * 1000;
46 
47 	while ((in8(I8042_STS_REG) & STATUS_IBF) && kbd_timeout--)
48 		udelay(1);
49 
50 	return kbd_timeout != -1;
51 }
52 
53 static int kbd_output_full(void)
54 {
55 	int kbd_timeout = KBD_TIMEOUT * 1000;
56 
57 	while (((in8(I8042_STS_REG) & STATUS_OBF) == 0) && kbd_timeout--)
58 		udelay(1);
59 
60 	return kbd_timeout != -1;
61 }
62 
63 static void kbd_led_set(int flags)
64 {
65 	kbd_input_empty();
66 	out8(I8042_DATA_REG, CMD_SET_KBD_LED);
67 	kbd_input_empty();
68 	out8(I8042_DATA_REG, flags & 0x7);
69 }
70 
71 static int kbd_write(int reg, int value)
72 {
73 	if (!kbd_input_empty())
74 		return -1;
75 	out8(reg, value);
76 
77 	return 0;
78 }
79 
80 static int kbd_read(int reg)
81 {
82 	if (!kbd_output_full())
83 		return -1;
84 
85 	return in8(reg);
86 }
87 
88 static int kbd_cmd_read(int cmd)
89 {
90 	if (kbd_write(I8042_CMD_REG, cmd))
91 		return -1;
92 
93 	return kbd_read(I8042_DATA_REG);
94 }
95 
96 static int kbd_cmd_write(int cmd, int data)
97 {
98 	if (kbd_write(I8042_CMD_REG, cmd))
99 		return -1;
100 
101 	return kbd_write(I8042_DATA_REG, data);
102 }
103 
104 static int kbd_reset(void)
105 {
106 	int config;
107 
108 	/* controller self test */
109 	if (kbd_cmd_read(CMD_SELF_TEST) != KBC_TEST_OK)
110 		goto err;
111 
112 	/* keyboard reset */
113 	if (kbd_write(I8042_DATA_REG, CMD_RESET_KBD) ||
114 	    kbd_read(I8042_DATA_REG) != KBD_ACK ||
115 	    kbd_read(I8042_DATA_REG) != KBD_POR)
116 		goto err;
117 
118 	/* set AT translation and disable irq */
119 	config = kbd_cmd_read(CMD_RD_CONFIG);
120 	if (config == -1)
121 		goto err;
122 
123 	config |= CONFIG_AT_TRANS;
124 	config &= ~(CONFIG_KIRQ_EN | CONFIG_MIRQ_EN);
125 	if (kbd_cmd_write(CMD_WR_CONFIG, config))
126 		goto err;
127 
128 	/* enable keyboard */
129 	if (kbd_write(I8042_CMD_REG, CMD_KBD_EN) ||
130 	    !kbd_input_empty())
131 		goto err;
132 
133 	return 0;
134 err:
135 	debug("%s: Keyboard failure\n", __func__);
136 	return -1;
137 }
138 
139 static int kbd_controller_present(void)
140 {
141 	return in8(I8042_STS_REG) != 0xff;
142 }
143 
144 /*
145  * Implement a weak default function for boards that optionally
146  * need to skip the i8042 initialization.
147  */
148 int __weak board_i8042_skip(void)
149 {
150 	/* As default, don't skip */
151 	return 0;
152 }
153 
154 void i8042_flush(void)
155 {
156 	int timeout;
157 
158 	/*
159 	 * The delay is to give the keyboard controller some time
160 	 * to fill the next byte.
161 	 */
162 	while (1) {
163 		timeout = 100;	/* wait for no longer than 100us */
164 		while (timeout > 0 && !(in8(I8042_STS_REG) & STATUS_OBF)) {
165 			udelay(1);
166 			timeout--;
167 		}
168 
169 		/* Try to pull next byte if not timeout */
170 		if (in8(I8042_STS_REG) & STATUS_OBF)
171 			in8(I8042_DATA_REG);
172 		else
173 			break;
174 	}
175 }
176 
177 int i8042_disable(void)
178 {
179 	if (kbd_input_empty() == 0)
180 		return -1;
181 
182 	/* Disable keyboard */
183 	out8(I8042_CMD_REG, CMD_KBD_DIS);
184 
185 	if (kbd_input_empty() == 0)
186 		return -1;
187 
188 	return 0;
189 }
190 
191 static int i8042_kbd_check(struct input_config *input)
192 {
193 	if ((in8(I8042_STS_REG) & STATUS_OBF) == 0) {
194 		return 0;
195 	} else {
196 		bool release = false;
197 		int scan_code;
198 		int i;
199 
200 		scan_code = in8(I8042_DATA_REG);
201 		if (scan_code == 0xfa) {
202 			return 0;
203 		} else if (scan_code == 0xe0) {
204 			extended = true;
205 			return 0;
206 		}
207 		if (scan_code & 0x80) {
208 			scan_code &= 0x7f;
209 			release = true;
210 		}
211 		if (extended) {
212 			extended = false;
213 			for (i = 0; ext_key_map[i]; i++) {
214 				if (ext_key_map[i] == scan_code) {
215 					scan_code = 0x60 + i;
216 					break;
217 				}
218 			}
219 			/* not found ? */
220 			if (!ext_key_map[i])
221 				return 0;
222 		}
223 
224 		input_add_keycode(&config, scan_code, release);
225 		return 1;
226 	}
227 }
228 
229 /* i8042_kbd_init - reset keyboard and init state flags */
230 int i8042_kbd_init(void)
231 {
232 	int keymap, try;
233 	char *penv;
234 	int ret;
235 
236 	if (!kbd_controller_present() || board_i8042_skip()) {
237 		debug("i8042 keyboard controller is not present\n");
238 		return -1;
239 	}
240 
241 	/* Init keyboard device (default US layout) */
242 	keymap = KBD_US;
243 	penv = getenv("keymap");
244 	if (penv != NULL) {
245 		if (strncmp(penv, "de", 3) == 0)
246 			keymap = KBD_GER;
247 	}
248 
249 	for (try = 0; kbd_reset() != 0; try++) {
250 		if (try >= KBD_RESET_TRIES)
251 			return -1;
252 	}
253 
254 	ret = input_init(&config, keymap == KBD_GER);
255 	if (ret)
256 		return ret;
257 	config.read_keys = i8042_kbd_check;
258 	input_allow_repeats(&config, true);
259 
260 	kbd_led_set(NORMAL);
261 
262 	return 0;
263 }
264 
265 /**
266  * check_leds() - Check the keyboard LEDs and update them it needed
267  *
268  * @ret:	Value to return
269  * @return value of @ret
270  */
271 static int check_leds(int ret)
272 {
273 	int leds;
274 
275 	leds = input_leds_changed(&config);
276 	if (leds >= 0)
277 		kbd_led_set(leds);
278 
279 	return ret;
280 }
281 
282 /* i8042_tstc - test if keyboard input is available */
283 int i8042_tstc(struct stdio_dev *dev)
284 {
285 	return check_leds(input_tstc(&config));
286 }
287 
288 /* i8042_getc - wait till keyboard input is available */
289 int i8042_getc(struct stdio_dev *dev)
290 {
291 	return check_leds(input_getc(&config));
292 }
293