[U-Boot] [PATCH 1/5] input: Use finer grain udelays while waitng for the i8042 keyboard buffer to empty

From: Gabe Black gabeblack@chromium.org
On x86, the i8042 keyboard controller driver frequently waits for the keyboard input buffer to be empty to make sure the controller has had a chance to process the data it was given. The way the delay loop was structured, if the controller hadn't cleared the corresponding status bit immediately, it would wait 1ms before checking again. If the keyboard responded quickly but not instantly, the driver would still wait a full 1ms when perhaps 1us would have been sufficient. Because udelay is a busy wait anyway, this change decreases the delay between checks to 1us.
Also, this change gets rid of a hardcoded 250ms delay.
On Stumpy, this saves 100-150ms during boot.
Signed-off-by: Gabe Black gabeblack@chromium.org Signed-off-by: Simon Glass sjg@chromium.org --- drivers/input/i8042.c | 7 ++----- 1 files changed, 2 insertions(+), 5 deletions(-)
diff --git a/drivers/input/i8042.c b/drivers/input/i8042.c index c3bc536..99254e4 100644 --- a/drivers/input/i8042.c +++ b/drivers/input/i8042.c @@ -607,11 +607,10 @@ static void kbd_led_set(void)
static int kbd_input_empty(void) { - int kbdTimeout = KBD_TIMEOUT; + int kbdTimeout = KBD_TIMEOUT * 1000;
- /* wait for input buf empty */ while ((in8(I8042_STATUS_REG) & 0x02) && kbdTimeout--) - udelay(1000); + udelay(1);
return kbdTimeout != -1; } @@ -625,8 +624,6 @@ static int kbd_reset(void)
out8(I8042_DATA_REG, 0xff);
- udelay(250000); - if (kbd_input_empty() == 0) return -1;

From: Gabe Black gabeblack@chromium.org
This change adds a board overridable function which can be used to decide whether or not to initialize the i8042 keyboard controller. On systems where it isn't actually connected to anything, this can save a significant amount of boot time.
On Stumpy, this saves about 200ms on boot.
Signed-off-by: Gabe Black gabeblack@chromium.org Signed-off-by: Simon Glass sjg@chromium.org --- drivers/input/i8042.c | 13 ++++++++++++- 1 files changed, 12 insertions(+), 1 deletions(-)
diff --git a/drivers/input/i8042.c b/drivers/input/i8042.c index 99254e4..cae2d0a 100644 --- a/drivers/input/i8042.c +++ b/drivers/input/i8042.c @@ -320,6 +320,17 @@ static int kbd_controller_present(void) return in8(I8042_STATUS_REG) != 0xff; }
+/* + * Implement a weak default function for boards that optionally + * need to skip the i8042 initialization. + */ +int __board_i8042_skip(void) +{ + /* As default, don't skip */ + return 0; +} +int board_i8042_skip(void) __attribute__((weak, alias("__board_i8042_skip"))); + /******************************************************************************* * * i8042_kbd_init - reset keyboard and init state flags @@ -329,7 +340,7 @@ int i8042_kbd_init(void) int keymap, try; char *penv;
- if (!kbd_controller_present()) + if (!kbd_controller_present() || board_i8042_skip()) return -1;
#ifdef CONFIG_USE_CPCIDVI

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On 10/11/12 18:15, Simon Glass wrote:
From: Gabe Black gabeblack@chromium.org
This change adds a board overridable function which can be used to decide whether or not to initialize the i8042 keyboard controller. On systems where it isn't actually connected to anything, this can save a significant amount of boot time.
On Stumpy, this saves about 200ms on boot.
Signed-off-by: Gabe Black gabeblack@chromium.org Signed-off-by: Simon Glass sjg@chromium.org --- drivers/input/i8042.c | 13 ++++++++++++- 1 files changed, 12 insertions(+), 1 deletions(-)
diff --git a/drivers/input/i8042.c b/drivers/input/i8042.c index 99254e4..cae2d0a 100644 --- a/drivers/input/i8042.c +++ b/drivers/input/i8042.c @@ -320,6 +320,17 @@ static int kbd_controller_present(void) return in8(I8042_STATUS_REG) != 0xff; }
+/* + * Implement a weak default function for boards that optionally + * need to skip the i8042 initialization. + */ +int __board_i8042_skip(void) +{ + /* As default, don't skip */ + return 0; +} +int board_i8042_skip(void) __attribute__((weak, alias("__board_i8042_skip")));
Please add <linux/compiler.h> and use __weak directly, thanks.
- -- Tom

Hi Tom,
On Fri, Oct 12, 2012 at 9:05 AM, Tom Rini trini@ti.com wrote:
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On 10/11/12 18:15, Simon Glass wrote:
From: Gabe Black gabeblack@chromium.org
This change adds a board overridable function which can be used to decide whether or not to initialize the i8042 keyboard controller. On systems where it isn't actually connected to anything, this can save a significant amount of boot time.
On Stumpy, this saves about 200ms on boot.
Signed-off-by: Gabe Black gabeblack@chromium.org Signed-off-by: Simon Glass sjg@chromium.org --- drivers/input/i8042.c | 13 ++++++++++++- 1 files changed, 12 insertions(+), 1 deletions(-)
diff --git a/drivers/input/i8042.c b/drivers/input/i8042.c index 99254e4..cae2d0a 100644 --- a/drivers/input/i8042.c +++ b/drivers/input/i8042.c @@ -320,6 +320,17 @@ static int kbd_controller_present(void) return in8(I8042_STATUS_REG) != 0xff; }
+/* + * Implement a weak default function for boards that optionally + * need to skip the i8042 initialization. + */ +int __board_i8042_skip(void) +{ + /* As default, don't skip */ + return 0; +} +int board_i8042_skip(void) __attribute__((weak, alias("__board_i8042_skip")));
Please add <linux/compiler.h> and use __weak directly, thanks.
Yes will do,
Tom -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://www.enigmail.net/
iQIcBAEBAgAGBQJQeD/QAAoJENk4IS6UOR1WVMsP/RJTlWaTvF56K6H75WbjK7l5 xE7zeAeCS4gbM1CS0fEv1X4296L7oWUUUkw73gwSEAxd2CxOj96HrhJI0WQIqZ/a m6ysfwswzY92oqXJGxnWnLiZzd0JUR/QHZAM7U0tX1yHFsYrycEtuq+JvfMm5BU7 1ozqrN0SoFPa8ZBq0GpnXhDsGdMApucDBkbDv3cgHTYPkOlfh3gLpbz/dkWKxrCv NBwKvZgVMtEPf9hZf5CLuB2ghfWIRyP50Bf99nKhV+gdLGtrEbboHkMMXEc2Z1ex foEMqpmSiMVd9yMRqVEIM3sWwnapqAZVn3wQLoq5Kz+YGS/9e0c9t95ck+CmndiM kJmrx/LZ/XSGzjLrKC3MWLWC/KUS2hHqj4AfYUf1kL5lPuHM24c+oSRfOjogkaqR bMJZ9ii/kYcQJry/dAYTjFyEa120X48y46e2bNcMhWVZh48A6VpJkFILb1Ga4tEo ss+hMBX/tiMNp8eoOR7y1n+A+94r3ABKUrLapjfVKg+qXeufrMyotBVfPY35hWnP qLNVlPpdEF7nP1Wt22OB9s6bBcI6dCrNuL75Ym6wJ3DYnpqU8Y8f3HihwL7cBy7F o1Zpo0bzazNfy722IYphWu0O3hihIQ11fRpAndVemUzjaXP/kKxaVhiDCj28xHup 9zGuTm7SW/liJTXp4cc8 =5tNx -----END PGP SIGNATURE-----
Regards, Simon

From: Louis Yung-Chieh Lo yjlou@chromium.org
The BIOS leaves the keyboard enabled during boot time so that any keystroke would interfere kernel driver initialization.
Add a way to disable the keyboard to make sure no scancode will be generated during the boot time. Note that the keyboard will be re-enabled again after the kernel driver is up.
This code can be called from the board functions. Signed-off-by: Louis Yung-Chieh Lo yjlou@chromium.org
Signed-off-by: Louis Yung-Chieh Lo yjlou@chromium.org Signed-off-by: Simon Glass sjg@chromium.org --- drivers/input/i8042.c | 38 ++++++++++++++++++++++++++++++++++++++ include/i8042.h | 13 +++++++++++++ 2 files changed, 51 insertions(+), 0 deletions(-)
diff --git a/drivers/input/i8042.c b/drivers/input/i8042.c index cae2d0a..6b51eb5 100644 --- a/drivers/input/i8042.c +++ b/drivers/input/i8042.c @@ -331,6 +331,44 @@ int __board_i8042_skip(void) } int board_i8042_skip(void) __attribute__((weak, alias("__board_i8042_skip")));
+void i8042_flush(void) +{ + int timeout; + + /* + * The delay is to give the keyboard controller some time to fill the + * next byte. + */ + while (1) { + timeout = 100; /* wait for no longer than 100us */ + while (timeout > 0 && !(in8(I8042_STATUS_REG) & 0x01)) { + udelay(1); + timeout--; + } + + /* Try to pull next byte if not timeout. */ + if (in8(I8042_STATUS_REG) & 0x01) + in8(I8042_DATA_REG); + else + break; + } +} + +int i8042_disable(void) +{ + if (kbd_input_empty() == 0) + return -1; + + /* Disable keyboard */ + out8(I8042_COMMAND_REG, 0xad); + + if (kbd_input_empty() == 0) + return -1; + + return 0; +} + + /******************************************************************************* * * i8042_kbd_init - reset keyboard and init state flags diff --git a/include/i8042.h b/include/i8042.h index 1395289..c3e4e02 100644 --- a/include/i8042.h +++ b/include/i8042.h @@ -69,6 +69,19 @@
/* exports */
+/** + * Flush all buffer from keyboard controller to host. + */ +void i8042_flush(void); + +/** + * Disables the keyboard so that key strokes no longer generate scancodes to + * the host. + * + * @return 0 if ok, -1 if keyboard input was found while disabling + */ +int i8042_disable(void); + int i8042_kbd_init(void); int i8042_tstc(void); int i8042_getc(void);

From: Marc Jones marc.jones@chromium.org
The i8042 keyboard reset was not checking the results of the output buffer after the reset command. This can jam up some KBC/keyboards. Also, remove a write to the wrong register and the CONFIG setting around the incorrect write.
Signed-off-by: Marc Jones marc.jones@chromium.org Signed-off-by: Simon Glass sjg@chromium.org --- drivers/input/i8042.c | 32 +++++++++++++++++++++++++++----- 1 files changed, 27 insertions(+), 5 deletions(-)
diff --git a/drivers/input/i8042.c b/drivers/input/i8042.c index 6b51eb5..65611ec 100644 --- a/drivers/input/i8042.c +++ b/drivers/input/i8042.c @@ -666,31 +666,53 @@ static int kbd_input_empty(void)
/******************************************************************************/
+static int wait_until_kbd_output_full(void) +{ + int kbdTimeout = KBD_TIMEOUT * 1000; + + while (((in8(I8042_STATUS_REG) & 0x01) == 0) && kbdTimeout--) + udelay(1); + + return kbdTimeout != -1; +} + +/******************************************************************************/ + static int kbd_reset(void) { + /* KB Reset */ if (kbd_input_empty() == 0) return -1;
out8(I8042_DATA_REG, 0xff);
+ if (wait_until_kbd_output_full() == 0) + return -1; + + if (in8(I8042_DATA_REG) != 0xfa) /* ACK */ + return -1; + + if (wait_until_kbd_output_full() == 0) + return -1; + + if (in8(I8042_DATA_REG) != 0xaa) /* Test Pass*/ + return -1; + if (kbd_input_empty() == 0) return -1;
-#ifdef CONFIG_USE_CPCIDVI + /* Set KBC mode */ out8(I8042_COMMAND_REG, 0x60); -#else - out8(I8042_DATA_REG, 0x60); -#endif
if (kbd_input_empty() == 0) return -1;
out8(I8042_DATA_REG, 0x45);
- if (kbd_input_empty() == 0) return -1;
+ /* Enable Keyboard */ out8(I8042_COMMAND_REG, 0xae);
if (kbd_input_empty() == 0)

From: Hung-Te Lin hungte@chromium.org
To support Non-ASCII keys (ex, Fn, PgUp/Dn, arrow keys, ...), we need to translate key code into escape sequence.
(Updated by sjg@chromium.org to move away from a function to store keycodes, so we can easily record how many were sent. We now need to return this from input_send_keycodes() so we know whether keys were generated.)
Signed-off-by: Hung-Te Lin hungte@chromium.org Signed-off-by: Simon Glass sjg@chromium.org --- drivers/input/input.c | 102 +++++++++++++++++++++++++++++++++++++++++------- include/input.h | 2 + 2 files changed, 89 insertions(+), 15 deletions(-)
diff --git a/drivers/input/input.c b/drivers/input/input.c index 5b2b4b0..9800667 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -93,6 +93,22 @@ static unsigned char kbd_ctrl_xlate[] = { '\r', 0xff, 0xff };
+/* + * Scan key code to ANSI 3.64 escape sequence table. This table is + * incomplete in that it does not include all possible extra keys. + */ +static struct { + int kbd_scan_code; + char *escape; +} kbd_to_ansi364[] = { + { KEY_UP, "\033[A"}, + { KEY_DOWN, "\033[B"}, + { KEY_RIGHT, "\033[C"}, + { KEY_LEFT, "\033[D"}, +}; + +/* Maximum number of output characters that an ANSI sequence expands to */ +#define ANSI_CHAR_MAX 3
int input_queue_ascii(struct input_config *config, int ch) { @@ -289,24 +305,67 @@ static int input_check_keycodes(struct input_config *config, }
/** + * Checks and converts a special key code into ANSI 3.64 escape sequence. + * + * @param config Input state + * @param keycode Key code to examine + * @param output_ch Buffer to place output characters into. It should + * be at least ANSI_CHAR_MAX bytes long, to allow for + * an ANSI sequence. + * @param max_chars Maximum number of characters to add to output_ch + * @return number of characters output, if the key was converted, otherwise 0. + * This may be larger than max_chars, in which case the overflow + * characters are not output. + */ +static int input_keycode_to_ansi364(struct input_config *config, + int keycode, char output_ch[], int max_chars) +{ + const char *escape; + int ch_count; + int i; + + for (i = ch_count = 0; i < ARRAY_SIZE(kbd_to_ansi364); i++) { + if (keycode != kbd_to_ansi364[i].kbd_scan_code) + continue; + for (escape = kbd_to_ansi364[i].escape; *escape; escape++) { + if (ch_count < max_chars) + output_ch[ch_count] = *escape; + ch_count++; + } + return ch_count; + } + + return 0; +} + +/** + * Converts and queues a list of key codes in escaped ASCII string form * Convert a list of key codes into ASCII * * You must call input_check_keycodes() before this. It turns the keycode - * list into a list of ASCII characters which are ready to send to the - * input layer. + * list into a list of ASCII characters and sends them to the input layer. * * Characters which were seen last time do not generate fresh ASCII output. + * The output (calls to queue_ascii) may be longer than num_keycodes, if the + * keycode contains special keys that was encoded to longer escaped sequence. * * @param config Input state * @param keycode List of key codes to examine * @param num_keycodes Number of key codes + * @param output_ch Buffer to place output characters into. It should + * be at last ANSI_CHAR_MAX * num_keycodes, to allow for + * ANSI sequences. + * @param max_chars Maximum number of characters to add to output_ch * @param same Number of key codes which are the same + * @return number of characters written into output_ch, or -1 if we would + * exceed max_chars chars. */ static int input_keycodes_to_ascii(struct input_config *config, - int keycode[], int num_keycodes, char output_ch[], int same) + int keycode[], int num_keycodes, char output_ch[], + int max_chars, int same) { struct input_key_xlate *table; - int ch_count; + int ch_count = 0; int i;
table = &config->table[0]; @@ -321,19 +380,31 @@ static int input_keycodes_to_ascii(struct input_config *config, } }
- /* now find normal keys */ - for (i = ch_count = 0; i < num_keycodes; i++) { + /* Start conversion by looking for the first new keycode (by same). */ + for (i = same; i < num_keycodes; i++) { int key = keycode[i]; + int ch = (key < table->num_entries) ? table->xlate[key] : 0xff;
- if (key < table->num_entries && i >= same) { - int ch = table->xlate[key]; - - /* If a normal key with an ASCII value, add it! */ - if (ch != 0xff) - output_ch[ch_count++] = (uchar)ch; + /* + * For a normal key (with an ASCII value), add it; otherwise + * translate special key to escape sequence if possible. + */ + if (ch != 0xff) { + if (ch_count < max_chars) + output_ch[ch_count] = (uchar)ch; + ch_count++; + } else { + ch_count += input_keycode_to_ansi364(config, key, + output_ch, max_chars); } }
+ if (ch_count > max_chars) { + debug("%s: Output char buffer overflow size=%d, need=%d\n", + __func__, max_chars, ch_count); + return -1; + } + /* ok, so return keys */ return ch_count; } @@ -341,7 +412,7 @@ static int input_keycodes_to_ascii(struct input_config *config, int input_send_keycodes(struct input_config *config, int keycode[], int num_keycodes) { - char ch[num_keycodes]; + char ch[num_keycodes * ANSI_CHAR_MAX]; int count, i, same = 0; int is_repeat = 0; unsigned delay_ms; @@ -363,7 +434,7 @@ int input_send_keycodes(struct input_config *config, }
count = input_keycodes_to_ascii(config, keycode, num_keycodes, - ch, is_repeat ? 0 : same); + ch, sizeof(ch), is_repeat ? 0 : same); for (i = 0; i < count; i++) input_queue_ascii(config, ch[i]); delay_ms = is_repeat ? @@ -371,7 +442,8 @@ int input_send_keycodes(struct input_config *config, config->repeat_delay_ms;
config->next_repeat_ms = get_timer(0) + delay_ms; - return 0; + + return count; }
int input_add_table(struct input_config *config, int left_keycode, diff --git a/include/input.h b/include/input.h index 0f4acb2..e90bb0b 100644 --- a/include/input.h +++ b/include/input.h @@ -84,6 +84,8 @@ struct stdio_dev; * @param config Input state * @param keycode List of key codes to examine * @param num_keycodes Number of key codes + * @return number of ascii characters sent, or 0 if none, or -1 for an + * internal error */ int input_send_keycodes(struct input_config *config, int keycode[], int count);

Hi Simon,
On Fri, Oct 12, 2012 at 12:15 PM, Simon Glass sjg@chromium.org wrote:
diff --git a/drivers/input/i8042.c b/drivers/input/i8042.c index c3bc536..99254e4 100644 --- a/drivers/input/i8042.c +++ b/drivers/input/i8042.c @@ -607,11 +607,10 @@ static void kbd_led_set(void)
static int kbd_input_empty(void) {
int kbdTimeout = KBD_TIMEOUT;
int kbdTimeout = KBD_TIMEOUT * 1000;
/* wait for input buf empty */ while ((in8(I8042_STATUS_REG) & 0x02) && kbdTimeout--)
I know this magic number was already here, but later patches introduce even more. Any chance these can be cleaned up as well?
Regards,
Graeme

Hi Graeme,
On Thu, Oct 11, 2012 at 8:55 PM, Graeme Russ graeme.russ@gmail.com wrote:
Hi Simon,
On Fri, Oct 12, 2012 at 12:15 PM, Simon Glass sjg@chromium.org wrote:
diff --git a/drivers/input/i8042.c b/drivers/input/i8042.c index c3bc536..99254e4 100644 --- a/drivers/input/i8042.c +++ b/drivers/input/i8042.c @@ -607,11 +607,10 @@ static void kbd_led_set(void)
static int kbd_input_empty(void) {
int kbdTimeout = KBD_TIMEOUT;
int kbdTimeout = KBD_TIMEOUT * 1000;
/* wait for input buf empty */ while ((in8(I8042_STATUS_REG) & 0x02) && kbdTimeout--)
I know this magic number was already here, but later patches introduce even more. Any chance these can be cleaned up as well?
Yes, will do.
Regards, Simon
Regards,
Graeme

On Thu, Oct 11, 2012 at 06:15:49PM -0700, Simon Glass wrote:
From: Gabe Black gabeblack@chromium.org
On x86, the i8042 keyboard controller driver frequently waits for the keyboard input buffer to be empty to make sure the controller has had a chance to process the data it was given. The way the delay loop was structured, if the controller hadn't cleared the corresponding status bit immediately, it would wait 1ms before checking again. If the keyboard responded quickly but not instantly, the driver would still wait a full 1ms when perhaps 1us would have been sufficient. Because udelay is a busy wait anyway, this change decreases the delay between checks to 1us.
Also, this change gets rid of a hardcoded 250ms delay.
On Stumpy, this saves 100-150ms during boot.
Signed-off-by: Gabe Black gabeblack@chromium.org Signed-off-by: Simon Glass sjg@chromium.org
This, or more precisely, v2 of 1/5 and 2/5 and v1 of 3, 4 and 5/5 have been applied to u-boot/master, thanks!
participants (3)
-
Graeme Russ
-
Simon Glass
-
Tom Rini