blob: 270805be60fd1d135f100e58c92fed4036fe42f4 [file] [log] [blame]
wdenkc6097192002-11-03 00:24:07 +00001/*
2 * (C) Copyright 2002 ELTEC Elektronik AG
3 * Frank Gottschling <fgottschling@eltec.de>
4 *
Wolfgang Denk3765b3e2013-10-07 13:07:26 +02005 * SPDX-License-Identifier: GPL-2.0+
wdenkc6097192002-11-03 00:24:07 +00006 */
7
8/* i8042.c - Intel 8042 keyboard driver routines */
9
wdenkc6097192002-11-03 00:24:07 +000010#include <common.h>
wdenkc6097192002-11-03 00:24:07 +000011#include <i8042.h>
Simon Glass2ec739d2015-11-11 10:05:41 -070012#include <input.h>
13#include <asm/io.h>
wdenkc6097192002-11-03 00:24:07 +000014
15/* defines */
Bin Meng835dd002015-08-24 01:00:05 -070016#define in8(p) inb(p)
17#define out8(p, v) outb(v, p)
wdenkc6097192002-11-03 00:24:07 +000018
wdenkc6097192002-11-03 00:24:07 +000019/* locals */
Simon Glass2ec739d2015-11-11 10:05:41 -070020static struct input_config config;
21static bool extended;
wdenkc6097192002-11-03 00:24:07 +000022
Gabe Blackdd4a5b22011-11-14 19:24:14 +000023static 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 };
wdenkc6097192002-11-03 00:24:07 +000042
Bin Meng3928d662015-08-24 01:00:04 -070043static int kbd_input_empty(void)
44{
Bin Meng835dd002015-08-24 01:00:05 -070045 int kbd_timeout = KBD_TIMEOUT * 1000;
Bin Meng3928d662015-08-24 01:00:04 -070046
Bin Meng835dd002015-08-24 01:00:05 -070047 while ((in8(I8042_STS_REG) & STATUS_IBF) && kbd_timeout--)
Bin Meng3928d662015-08-24 01:00:04 -070048 udelay(1);
49
Bin Meng835dd002015-08-24 01:00:05 -070050 return kbd_timeout != -1;
Bin Meng3928d662015-08-24 01:00:04 -070051}
52
Bin Meng835dd002015-08-24 01:00:05 -070053static int kbd_output_full(void)
Bin Meng3928d662015-08-24 01:00:04 -070054{
Bin Meng835dd002015-08-24 01:00:05 -070055 int kbd_timeout = KBD_TIMEOUT * 1000;
Bin Meng3928d662015-08-24 01:00:04 -070056
Bin Meng835dd002015-08-24 01:00:05 -070057 while (((in8(I8042_STS_REG) & STATUS_OBF) == 0) && kbd_timeout--)
Bin Meng3928d662015-08-24 01:00:04 -070058 udelay(1);
59
Bin Meng835dd002015-08-24 01:00:05 -070060 return kbd_timeout != -1;
Bin Meng3928d662015-08-24 01:00:04 -070061}
62
Simon Glass2ec739d2015-11-11 10:05:41 -070063static void kbd_led_set(int flags)
Bin Meng3928d662015-08-24 01:00:04 -070064{
65 kbd_input_empty();
Bin Meng835dd002015-08-24 01:00:05 -070066 out8(I8042_DATA_REG, CMD_SET_KBD_LED);
Bin Meng3928d662015-08-24 01:00:04 -070067 kbd_input_empty();
Simon Glass2ec739d2015-11-11 10:05:41 -070068 out8(I8042_DATA_REG, flags & 0x7);
Bin Meng3928d662015-08-24 01:00:04 -070069}
70
Simon Glass31d38ee2015-10-18 21:17:19 -060071static 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
80static int kbd_read(int reg)
81{
82 if (!kbd_output_full())
83 return -1;
84
85 return in8(reg);
86}
87
88static 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
96static 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
Bin Meng3928d662015-08-24 01:00:04 -0700104static int kbd_reset(void)
105{
Simon Glass31d38ee2015-10-18 21:17:19 -0600106 int config;
Bin Meng7d961662015-08-24 01:00:06 -0700107
108 /* controller self test */
Simon Glass31d38ee2015-10-18 21:17:19 -0600109 if (kbd_cmd_read(CMD_SELF_TEST) != KBC_TEST_OK)
Simon Glass4f087ba2015-10-18 21:17:20 -0600110 goto err;
Bin Meng3928d662015-08-24 01:00:04 -0700111
Bin Meng7d961662015-08-24 01:00:06 -0700112 /* keyboard reset */
Simon Glass31d38ee2015-10-18 21:17:19 -0600113 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)
Simon Glass4f087ba2015-10-18 21:17:20 -0600116 goto err;
Bin Meng3928d662015-08-24 01:00:04 -0700117
Bin Meng7d961662015-08-24 01:00:06 -0700118 /* set AT translation and disable irq */
Simon Glass31d38ee2015-10-18 21:17:19 -0600119 config = kbd_cmd_read(CMD_RD_CONFIG);
120 if (config == -1)
Simon Glass4f087ba2015-10-18 21:17:20 -0600121 goto err;
Simon Glass31d38ee2015-10-18 21:17:19 -0600122
Bin Meng7d961662015-08-24 01:00:06 -0700123 config |= CONFIG_AT_TRANS;
124 config &= ~(CONFIG_KIRQ_EN | CONFIG_MIRQ_EN);
Simon Glass31d38ee2015-10-18 21:17:19 -0600125 if (kbd_cmd_write(CMD_WR_CONFIG, config))
Simon Glass4f087ba2015-10-18 21:17:20 -0600126 goto err;
Bin Meng3928d662015-08-24 01:00:04 -0700127
Bin Meng7d961662015-08-24 01:00:06 -0700128 /* enable keyboard */
Simon Glass31d38ee2015-10-18 21:17:19 -0600129 if (kbd_write(I8042_CMD_REG, CMD_KBD_EN) ||
130 !kbd_input_empty())
Simon Glass4f087ba2015-10-18 21:17:20 -0600131 goto err;
Bin Meng3928d662015-08-24 01:00:04 -0700132
Bin Meng3928d662015-08-24 01:00:04 -0700133 return 0;
Simon Glass4f087ba2015-10-18 21:17:20 -0600134err:
135 debug("%s: Keyboard failure\n", __func__);
136 return -1;
Bin Meng3928d662015-08-24 01:00:04 -0700137}
138
Gabe Black22e0f5a2011-11-14 20:18:12 +0000139static int kbd_controller_present(void)
140{
Bin Meng835dd002015-08-24 01:00:05 -0700141 return in8(I8042_STS_REG) != 0xff;
Gabe Black22e0f5a2011-11-14 20:18:12 +0000142}
143
Gabe Black48edb302012-10-12 14:02:02 +0000144/*
145 * Implement a weak default function for boards that optionally
146 * need to skip the i8042 initialization.
147 */
148int __weak board_i8042_skip(void)
149{
150 /* As default, don't skip */
151 return 0;
152}
153
Louis Yung-Chieh Lo45fe6682012-10-11 15:15:51 +0000154void i8042_flush(void)
155{
156 int timeout;
157
158 /*
Bin Meng835dd002015-08-24 01:00:05 -0700159 * The delay is to give the keyboard controller some time
160 * to fill the next byte.
Louis Yung-Chieh Lo45fe6682012-10-11 15:15:51 +0000161 */
162 while (1) {
Bin Meng835dd002015-08-24 01:00:05 -0700163 timeout = 100; /* wait for no longer than 100us */
164 while (timeout > 0 && !(in8(I8042_STS_REG) & STATUS_OBF)) {
Louis Yung-Chieh Lo45fe6682012-10-11 15:15:51 +0000165 udelay(1);
166 timeout--;
167 }
168
Bin Meng835dd002015-08-24 01:00:05 -0700169 /* Try to pull next byte if not timeout */
170 if (in8(I8042_STS_REG) & STATUS_OBF)
Louis Yung-Chieh Lo45fe6682012-10-11 15:15:51 +0000171 in8(I8042_DATA_REG);
172 else
173 break;
174 }
175}
176
177int i8042_disable(void)
178{
179 if (kbd_input_empty() == 0)
180 return -1;
181
182 /* Disable keyboard */
Bin Meng835dd002015-08-24 01:00:05 -0700183 out8(I8042_CMD_REG, CMD_KBD_DIS);
Louis Yung-Chieh Lo45fe6682012-10-11 15:15:51 +0000184
185 if (kbd_input_empty() == 0)
186 return -1;
187
188 return 0;
189}
190
Simon Glass2ec739d2015-11-11 10:05:41 -0700191static 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
Bin Meng835dd002015-08-24 01:00:05 -0700229/* i8042_kbd_init - reset keyboard and init state flags */
Gabe Blackdd4a5b22011-11-14 19:24:14 +0000230int i8042_kbd_init(void)
wdenkc6097192002-11-03 00:24:07 +0000231{
Gabe Blackdd4a5b22011-11-14 19:24:14 +0000232 int keymap, try;
233 char *penv;
Simon Glass2ec739d2015-11-11 10:05:41 -0700234 int ret;
wdenkc6097192002-11-03 00:24:07 +0000235
Bin Meng835dd002015-08-24 01:00:05 -0700236 if (!kbd_controller_present() || board_i8042_skip()) {
237 debug("i8042 keyboard controller is not present\n");
Gabe Black22e0f5a2011-11-14 20:18:12 +0000238 return -1;
Bin Meng835dd002015-08-24 01:00:05 -0700239 }
Gabe Black22e0f5a2011-11-14 20:18:12 +0000240
Gabe Blackdd4a5b22011-11-14 19:24:14 +0000241 /* 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 }
wdenkc6097192002-11-03 00:24:07 +0000248
Simon Glassc5d257f2015-10-18 21:17:21 -0600249 for (try = 0; kbd_reset() != 0; try++) {
250 if (try >= KBD_RESET_TRIES)
251 return -1;
Gabe Blackdd4a5b22011-11-14 19:24:14 +0000252 }
Bin Meng835dd002015-08-24 01:00:05 -0700253
Simon Glass2ec739d2015-11-11 10:05:41 -0700254 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);
Simon Glassc5d257f2015-10-18 21:17:21 -0600261
262 return 0;
wdenkc6097192002-11-03 00:24:07 +0000263}
264
Simon Glass2ec739d2015-11-11 10:05:41 -0700265/**
266 * check_leds() - Check the keyboard LEDs and update them it needed
Bin Meng835dd002015-08-24 01:00:05 -0700267 *
Simon Glass2ec739d2015-11-11 10:05:41 -0700268 * @ret: Value to return
269 * @return value of @ret
wdenkc6097192002-11-03 00:24:07 +0000270 */
Simon Glass2ec739d2015-11-11 10:05:41 -0700271static 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 */
Simon Glass709ea542014-07-23 06:54:59 -0600283int i8042_tstc(struct stdio_dev *dev)
wdenkc6097192002-11-03 00:24:07 +0000284{
Simon Glass2ec739d2015-11-11 10:05:41 -0700285 return check_leds(input_tstc(&config));
wdenkc6097192002-11-03 00:24:07 +0000286}
287
Simon Glass2ec739d2015-11-11 10:05:41 -0700288/* i8042_getc - wait till keyboard input is available */
Simon Glass709ea542014-07-23 06:54:59 -0600289int i8042_getc(struct stdio_dev *dev)
wdenkc6097192002-11-03 00:24:07 +0000290{
Simon Glass2ec739d2015-11-11 10:05:41 -0700291 return check_leds(input_getc(&config));
Gabe Blackdd4a5b22011-11-14 19:24:14 +0000292}