blob: 1ea11fd67b44a6c3a85ea319fce8c799ea39341d [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
wdenkc6097192002-11-03 00:24:07 +00002/*
3 * (C) Copyright 2002 ELTEC Elektronik AG
4 * Frank Gottschling <fgottschling@eltec.de>
wdenkc6097192002-11-03 00:24:07 +00005 */
6
7/* i8042.c - Intel 8042 keyboard driver routines */
8
wdenkc6097192002-11-03 00:24:07 +00009#include <common.h>
Simon Glassdcbf8252015-11-11 10:05:45 -070010#include <dm.h>
Simon Glass7b51b572019-08-01 09:46:52 -060011#include <env.h>
Simon Glassdcbf8252015-11-11 10:05:45 -070012#include <errno.h>
wdenkc6097192002-11-03 00:24:07 +000013#include <i8042.h>
Simon Glass2ec739d2015-11-11 10:05:41 -070014#include <input.h>
Simon Glassdcbf8252015-11-11 10:05:45 -070015#include <keyboard.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -060016#include <log.h>
Simon Glass2ec739d2015-11-11 10:05:41 -070017#include <asm/io.h>
Simon Glassc05ed002020-05-10 11:40:11 -060018#include <linux/delay.h>
wdenkc6097192002-11-03 00:24:07 +000019
Simon Glass011d89d2015-11-11 10:05:46 -070020DECLARE_GLOBAL_DATA_PTR;
21
wdenkc6097192002-11-03 00:24:07 +000022/* defines */
Bin Meng835dd002015-08-24 01:00:05 -070023#define in8(p) inb(p)
24#define out8(p, v) outb(v, p)
wdenkc6097192002-11-03 00:24:07 +000025
Simon Glass011d89d2015-11-11 10:05:46 -070026enum {
27 QUIRK_DUP_POR = 1 << 0,
28};
29
wdenkc6097192002-11-03 00:24:07 +000030/* locals */
Simon Glassdcbf8252015-11-11 10:05:45 -070031struct i8042_kbd_priv {
32 bool extended; /* true if an extended keycode is expected next */
Simon Glass011d89d2015-11-11 10:05:46 -070033 int quirks; /* quirks that we support */
Simon Glassdcbf8252015-11-11 10:05:45 -070034};
wdenkc6097192002-11-03 00:24:07 +000035
Gabe Blackdd4a5b22011-11-14 19:24:14 +000036static unsigned char ext_key_map[] = {
37 0x1c, /* keypad enter */
38 0x1d, /* right control */
39 0x35, /* keypad slash */
40 0x37, /* print screen */
41 0x38, /* right alt */
42 0x46, /* break */
43 0x47, /* editpad home */
44 0x48, /* editpad up */
45 0x49, /* editpad pgup */
46 0x4b, /* editpad left */
47 0x4d, /* editpad right */
48 0x4f, /* editpad end */
49 0x50, /* editpad dn */
50 0x51, /* editpad pgdn */
51 0x52, /* editpad ins */
52 0x53, /* editpad del */
53 0x00 /* map end */
54 };
wdenkc6097192002-11-03 00:24:07 +000055
Bin Meng3928d662015-08-24 01:00:04 -070056static int kbd_input_empty(void)
57{
Bin Meng835dd002015-08-24 01:00:05 -070058 int kbd_timeout = KBD_TIMEOUT * 1000;
Bin Meng3928d662015-08-24 01:00:04 -070059
Bin Meng835dd002015-08-24 01:00:05 -070060 while ((in8(I8042_STS_REG) & STATUS_IBF) && kbd_timeout--)
Bin Meng3928d662015-08-24 01:00:04 -070061 udelay(1);
62
Bin Meng835dd002015-08-24 01:00:05 -070063 return kbd_timeout != -1;
Bin Meng3928d662015-08-24 01:00:04 -070064}
65
Bin Meng835dd002015-08-24 01:00:05 -070066static int kbd_output_full(void)
Bin Meng3928d662015-08-24 01:00:04 -070067{
Bin Meng835dd002015-08-24 01:00:05 -070068 int kbd_timeout = KBD_TIMEOUT * 1000;
Bin Meng3928d662015-08-24 01:00:04 -070069
Bin Meng835dd002015-08-24 01:00:05 -070070 while (((in8(I8042_STS_REG) & STATUS_OBF) == 0) && kbd_timeout--)
Bin Meng3928d662015-08-24 01:00:04 -070071 udelay(1);
72
Bin Meng835dd002015-08-24 01:00:05 -070073 return kbd_timeout != -1;
Bin Meng3928d662015-08-24 01:00:04 -070074}
75
Simon Glassdcbf8252015-11-11 10:05:45 -070076/**
77 * check_leds() - Check the keyboard LEDs and update them it needed
78 *
79 * @ret: Value to return
80 * @return value of @ret
81 */
82static int i8042_kbd_update_leds(struct udevice *dev, int leds)
Bin Meng3928d662015-08-24 01:00:04 -070083{
84 kbd_input_empty();
Bin Meng835dd002015-08-24 01:00:05 -070085 out8(I8042_DATA_REG, CMD_SET_KBD_LED);
Bin Meng3928d662015-08-24 01:00:04 -070086 kbd_input_empty();
Simon Glassdcbf8252015-11-11 10:05:45 -070087 out8(I8042_DATA_REG, leds & 0x7);
88
89 return 0;
Bin Meng3928d662015-08-24 01:00:04 -070090}
91
Simon Glass31d38ee2015-10-18 21:17:19 -060092static int kbd_write(int reg, int value)
93{
94 if (!kbd_input_empty())
95 return -1;
96 out8(reg, value);
97
98 return 0;
99}
100
101static int kbd_read(int reg)
102{
103 if (!kbd_output_full())
104 return -1;
105
106 return in8(reg);
107}
108
109static int kbd_cmd_read(int cmd)
110{
111 if (kbd_write(I8042_CMD_REG, cmd))
112 return -1;
113
114 return kbd_read(I8042_DATA_REG);
115}
116
117static int kbd_cmd_write(int cmd, int data)
118{
119 if (kbd_write(I8042_CMD_REG, cmd))
120 return -1;
121
122 return kbd_write(I8042_DATA_REG, data);
123}
124
Simon Glass011d89d2015-11-11 10:05:46 -0700125static int kbd_reset(int quirk)
Bin Meng3928d662015-08-24 01:00:04 -0700126{
Simon Glass31d38ee2015-10-18 21:17:19 -0600127 int config;
Bin Meng7d961662015-08-24 01:00:06 -0700128
129 /* controller self test */
Simon Glass31d38ee2015-10-18 21:17:19 -0600130 if (kbd_cmd_read(CMD_SELF_TEST) != KBC_TEST_OK)
Simon Glass4f087ba2015-10-18 21:17:20 -0600131 goto err;
Bin Meng3928d662015-08-24 01:00:04 -0700132
Bin Meng7d961662015-08-24 01:00:06 -0700133 /* keyboard reset */
Simon Glass31d38ee2015-10-18 21:17:19 -0600134 if (kbd_write(I8042_DATA_REG, CMD_RESET_KBD) ||
135 kbd_read(I8042_DATA_REG) != KBD_ACK ||
136 kbd_read(I8042_DATA_REG) != KBD_POR)
Simon Glass4f087ba2015-10-18 21:17:20 -0600137 goto err;
Bin Meng3928d662015-08-24 01:00:04 -0700138
Simon Glass8226a3e2016-03-11 22:06:50 -0700139 if (kbd_write(I8042_DATA_REG, CMD_DRAIN_OUTPUT) ||
140 kbd_read(I8042_DATA_REG) != KBD_ACK)
141 goto err;
142
Bin Meng7d961662015-08-24 01:00:06 -0700143 /* set AT translation and disable irq */
Simon Glass31d38ee2015-10-18 21:17:19 -0600144 config = kbd_cmd_read(CMD_RD_CONFIG);
145 if (config == -1)
Simon Glass4f087ba2015-10-18 21:17:20 -0600146 goto err;
Simon Glass31d38ee2015-10-18 21:17:19 -0600147
Simon Glass011d89d2015-11-11 10:05:46 -0700148 /* Sometimes get a second byte */
149 else if ((quirk & QUIRK_DUP_POR) && config == KBD_POR)
150 config = kbd_cmd_read(CMD_RD_CONFIG);
151
Bin Meng7d961662015-08-24 01:00:06 -0700152 config |= CONFIG_AT_TRANS;
153 config &= ~(CONFIG_KIRQ_EN | CONFIG_MIRQ_EN);
Simon Glass31d38ee2015-10-18 21:17:19 -0600154 if (kbd_cmd_write(CMD_WR_CONFIG, config))
Simon Glass4f087ba2015-10-18 21:17:20 -0600155 goto err;
Bin Meng3928d662015-08-24 01:00:04 -0700156
Bin Meng7d961662015-08-24 01:00:06 -0700157 /* enable keyboard */
Simon Glass31d38ee2015-10-18 21:17:19 -0600158 if (kbd_write(I8042_CMD_REG, CMD_KBD_EN) ||
159 !kbd_input_empty())
Simon Glass4f087ba2015-10-18 21:17:20 -0600160 goto err;
Bin Meng3928d662015-08-24 01:00:04 -0700161
Bin Meng3928d662015-08-24 01:00:04 -0700162 return 0;
Simon Glass4f087ba2015-10-18 21:17:20 -0600163err:
164 debug("%s: Keyboard failure\n", __func__);
165 return -1;
Bin Meng3928d662015-08-24 01:00:04 -0700166}
167
Gabe Black22e0f5a2011-11-14 20:18:12 +0000168static int kbd_controller_present(void)
169{
Bin Meng835dd002015-08-24 01:00:05 -0700170 return in8(I8042_STS_REG) != 0xff;
Gabe Black22e0f5a2011-11-14 20:18:12 +0000171}
172
Simon Glass165be502018-11-23 21:29:38 -0700173/** Flush all buffer from keyboard controller to host*/
174static void i8042_flush(void)
Louis Yung-Chieh Lo45fe6682012-10-11 15:15:51 +0000175{
176 int timeout;
177
178 /*
Bin Meng835dd002015-08-24 01:00:05 -0700179 * The delay is to give the keyboard controller some time
180 * to fill the next byte.
Louis Yung-Chieh Lo45fe6682012-10-11 15:15:51 +0000181 */
182 while (1) {
Bin Meng835dd002015-08-24 01:00:05 -0700183 timeout = 100; /* wait for no longer than 100us */
184 while (timeout > 0 && !(in8(I8042_STS_REG) & STATUS_OBF)) {
Louis Yung-Chieh Lo45fe6682012-10-11 15:15:51 +0000185 udelay(1);
186 timeout--;
187 }
188
Bin Meng835dd002015-08-24 01:00:05 -0700189 /* Try to pull next byte if not timeout */
190 if (in8(I8042_STS_REG) & STATUS_OBF)
Louis Yung-Chieh Lo45fe6682012-10-11 15:15:51 +0000191 in8(I8042_DATA_REG);
192 else
193 break;
194 }
195}
196
Simon Glass165be502018-11-23 21:29:38 -0700197/**
198 * Disables the keyboard so that key strokes no longer generate scancodes to
199 * the host.
200 *
201 * @return 0 if ok, -1 if keyboard input was found while disabling
202 */
203static int i8042_disable(void)
Louis Yung-Chieh Lo45fe6682012-10-11 15:15:51 +0000204{
205 if (kbd_input_empty() == 0)
206 return -1;
207
208 /* Disable keyboard */
Bin Meng835dd002015-08-24 01:00:05 -0700209 out8(I8042_CMD_REG, CMD_KBD_DIS);
Louis Yung-Chieh Lo45fe6682012-10-11 15:15:51 +0000210
211 if (kbd_input_empty() == 0)
212 return -1;
213
214 return 0;
215}
216
Simon Glass2ec739d2015-11-11 10:05:41 -0700217static int i8042_kbd_check(struct input_config *input)
218{
Simon Glassdcbf8252015-11-11 10:05:45 -0700219 struct i8042_kbd_priv *priv = dev_get_priv(input->dev);
220
Simon Glass2ec739d2015-11-11 10:05:41 -0700221 if ((in8(I8042_STS_REG) & STATUS_OBF) == 0) {
222 return 0;
223 } else {
224 bool release = false;
225 int scan_code;
226 int i;
227
228 scan_code = in8(I8042_DATA_REG);
229 if (scan_code == 0xfa) {
230 return 0;
231 } else if (scan_code == 0xe0) {
Simon Glassdcbf8252015-11-11 10:05:45 -0700232 priv->extended = true;
Simon Glass2ec739d2015-11-11 10:05:41 -0700233 return 0;
234 }
235 if (scan_code & 0x80) {
236 scan_code &= 0x7f;
237 release = true;
238 }
Simon Glassdcbf8252015-11-11 10:05:45 -0700239 if (priv->extended) {
240 priv->extended = false;
Simon Glass2ec739d2015-11-11 10:05:41 -0700241 for (i = 0; ext_key_map[i]; i++) {
242 if (ext_key_map[i] == scan_code) {
243 scan_code = 0x60 + i;
244 break;
245 }
246 }
247 /* not found ? */
248 if (!ext_key_map[i])
249 return 0;
250 }
251
Simon Glassdcbf8252015-11-11 10:05:45 -0700252 input_add_keycode(input, scan_code, release);
Simon Glass2ec739d2015-11-11 10:05:41 -0700253 return 1;
254 }
255}
256
Bin Meng835dd002015-08-24 01:00:05 -0700257/* i8042_kbd_init - reset keyboard and init state flags */
Simon Glassdcbf8252015-11-11 10:05:45 -0700258static int i8042_start(struct udevice *dev)
wdenkc6097192002-11-03 00:24:07 +0000259{
Simon Glassdcbf8252015-11-11 10:05:45 -0700260 struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev);
Simon Glass011d89d2015-11-11 10:05:46 -0700261 struct i8042_kbd_priv *priv = dev_get_priv(dev);
Simon Glassdcbf8252015-11-11 10:05:45 -0700262 struct input_config *input = &uc_priv->input;
Gabe Blackdd4a5b22011-11-14 19:24:14 +0000263 int keymap, try;
264 char *penv;
Simon Glass2ec739d2015-11-11 10:05:41 -0700265 int ret;
wdenkc6097192002-11-03 00:24:07 +0000266
Simon Glass165be502018-11-23 21:29:38 -0700267 if (!kbd_controller_present()) {
Bin Meng835dd002015-08-24 01:00:05 -0700268 debug("i8042 keyboard controller is not present\n");
Simon Glassdcbf8252015-11-11 10:05:45 -0700269 return -ENOENT;
Bin Meng835dd002015-08-24 01:00:05 -0700270 }
Gabe Black22e0f5a2011-11-14 20:18:12 +0000271
Gabe Blackdd4a5b22011-11-14 19:24:14 +0000272 /* Init keyboard device (default US layout) */
273 keymap = KBD_US;
Simon Glass00caae62017-08-03 12:22:12 -0600274 penv = env_get("keymap");
Gabe Blackdd4a5b22011-11-14 19:24:14 +0000275 if (penv != NULL) {
276 if (strncmp(penv, "de", 3) == 0)
277 keymap = KBD_GER;
278 }
wdenkc6097192002-11-03 00:24:07 +0000279
Simon Glass011d89d2015-11-11 10:05:46 -0700280 for (try = 0; kbd_reset(priv->quirks) != 0; try++) {
Simon Glassc5d257f2015-10-18 21:17:21 -0600281 if (try >= KBD_RESET_TRIES)
282 return -1;
Gabe Blackdd4a5b22011-11-14 19:24:14 +0000283 }
Bin Meng835dd002015-08-24 01:00:05 -0700284
Simon Glassdcbf8252015-11-11 10:05:45 -0700285 ret = input_add_tables(input, keymap == KBD_GER);
Simon Glass2ec739d2015-11-11 10:05:41 -0700286 if (ret)
287 return ret;
Simon Glass2ec739d2015-11-11 10:05:41 -0700288
Simon Glassdcbf8252015-11-11 10:05:45 -0700289 i8042_kbd_update_leds(dev, NORMAL);
290 debug("%s: started\n", __func__);
Simon Glassc5d257f2015-10-18 21:17:21 -0600291
292 return 0;
wdenkc6097192002-11-03 00:24:07 +0000293}
294
Simon Glass165be502018-11-23 21:29:38 -0700295static int i8042_kbd_remove(struct udevice *dev)
296{
297 if (i8042_disable())
298 log_debug("i8042_disable() failed. fine, continue.\n");
299 i8042_flush();
300
301 return 0;
302}
303
Simon Glass2ec739d2015-11-11 10:05:41 -0700304/**
Simon Glassdcbf8252015-11-11 10:05:45 -0700305 * Set up the i8042 keyboard. This is called by the stdio device handler
Bin Meng835dd002015-08-24 01:00:05 -0700306 *
Simon Glassdcbf8252015-11-11 10:05:45 -0700307 * We want to do this init when the keyboard is actually used rather than
308 * at start-up, since keyboard input may not currently be selected.
309 *
310 * Once the keyboard starts there will be a period during which we must
311 * wait for the keyboard to init. We do this only when a key is first
312 * read - see kbd_wait_for_fifo_init().
313 *
314 * @return 0 if ok, -ve on error
wdenkc6097192002-11-03 00:24:07 +0000315 */
Simon Glassdcbf8252015-11-11 10:05:45 -0700316static int i8042_kbd_probe(struct udevice *dev)
Simon Glass2ec739d2015-11-11 10:05:41 -0700317{
Simon Glassdcbf8252015-11-11 10:05:45 -0700318 struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev);
Simon Glass011d89d2015-11-11 10:05:46 -0700319 struct i8042_kbd_priv *priv = dev_get_priv(dev);
Simon Glassdcbf8252015-11-11 10:05:45 -0700320 struct stdio_dev *sdev = &uc_priv->sdev;
321 struct input_config *input = &uc_priv->input;
322 int ret;
Simon Glass2ec739d2015-11-11 10:05:41 -0700323
Simon Glasse160f7d2017-01-17 16:52:55 -0700324 if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
Simon Glass011d89d2015-11-11 10:05:46 -0700325 "intel,duplicate-por"))
326 priv->quirks |= QUIRK_DUP_POR;
327
Simon Glassdcbf8252015-11-11 10:05:45 -0700328 /* Register the device. i8042_start() will be called soon */
329 input->dev = dev;
330 input->read_keys = i8042_kbd_check;
331 input_allow_repeats(input, true);
332 strcpy(sdev->name, "i8042-kbd");
333 ret = input_stdio_register(sdev);
334 if (ret) {
335 debug("%s: input_stdio_register() failed\n", __func__);
336 return ret;
337 }
338 debug("%s: ready\n", __func__);
Simon Glass2ec739d2015-11-11 10:05:41 -0700339
Simon Glassdcbf8252015-11-11 10:05:45 -0700340 return 0;
Simon Glass2ec739d2015-11-11 10:05:41 -0700341}
342
Simon Glassdcbf8252015-11-11 10:05:45 -0700343static const struct keyboard_ops i8042_kbd_ops = {
344 .start = i8042_start,
345 .update_leds = i8042_kbd_update_leds,
346};
wdenkc6097192002-11-03 00:24:07 +0000347
Simon Glassdcbf8252015-11-11 10:05:45 -0700348static const struct udevice_id i8042_kbd_ids[] = {
349 { .compatible = "intel,i8042-keyboard" },
350 { }
351};
352
353U_BOOT_DRIVER(i8042_kbd) = {
354 .name = "i8042_kbd",
355 .id = UCLASS_KEYBOARD,
356 .of_match = i8042_kbd_ids,
357 .probe = i8042_kbd_probe,
Simon Glass165be502018-11-23 21:29:38 -0700358 .remove = i8042_kbd_remove,
Simon Glassdcbf8252015-11-11 10:05:45 -0700359 .ops = &i8042_kbd_ops,
Simon Glass41575d82020-12-03 16:55:17 -0700360 .priv_auto = sizeof(struct i8042_kbd_priv),
Simon Glassdcbf8252015-11-11 10:05:45 -0700361};