| | 1 | /* |
| | 2 | * This file is part of the coreboot project. |
| | 3 | * |
| | 4 | * Copyright (C) 2008 Advanced Micro Devices, Inc. |
| | 5 | * Copyright (C) ???? Ollie Lo <ollielo@hotmail.com> |
| | 6 | * |
| | 7 | * This program is free software; you can redistribute it and/or modify |
| | 8 | * it under the terms of the GNU General Public License as published by |
| | 9 | * the Free Software Foundation; version 2 of the License. |
| | 10 | * |
| | 11 | * This program is distributed in the hope that it will be useful, |
| | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| | 14 | * GNU General Public License for more details. |
| | 15 | * |
| | 16 | * You should have received a copy of the GNU General Public License |
| | 17 | * along with this program; if not, write to the Free Software |
| | 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| | 19 | */ |
| | 20 | |
| | 21 | |
| 24 | | /* much better keyboard init courtesy ollie@sis.com.tw |
| 25 | | TODO: Typematic Setting, the keyboard is too slow for me */ |
| | 54 | |
| | 55 | static int kbc_cleanup_buffers(void) |
| | 56 | { |
| | 57 | u32 timeout; |
| | 58 | for(timeout = 1000000; timeout && (inb(0x64) & 0x03); timeout--) { |
| | 59 | inb(0x60); |
| | 60 | } |
| | 61 | |
| | 62 | if (!timeout) { |
| | 63 | printk_err("Couldn't cleanup the keyboard controller buffers\n"); |
| | 64 | printk_err("0x64: 0x%x, 0x60: 0x%x\n", inb(0x64), inb(0x60)); |
| | 65 | } |
| | 66 | return !!timeout; |
| | 67 | } |
| | 68 | |
| | 69 | |
| | 70 | static u8 send_keyboard(u8 command) |
| | 71 | { |
| | 72 | u8 regval = 0; |
| | 73 | u8 resend = 10; |
| | 74 | |
| | 75 | do { |
| | 76 | if (!kbc_input_buffer_empty()) return 0; |
| | 77 | outb(command, 0x60); |
| | 78 | if (!kbc_output_buffer_full()) return 0; |
| | 79 | regval = inb(0x60); |
| | 80 | --resend; |
| | 81 | } while (regval == 0xFE && resend > 0); |
| | 82 | |
| | 83 | return regval; |
| | 84 | } |
| | 85 | |
| | 86 | |
| 31 | | /* send cmd = 0xAA, self test 8042 */ |
| 32 | | outb(0xaa, 0x64); |
| 33 | | |
| 34 | | /* empty input buffer or any other command/data will be lost */ |
| 35 | | if (!kbd_empty_input_buffer()) { |
| 36 | | printk_err("Keyboard input buffer would not empty\n"); |
| 37 | | return; |
| 38 | | } |
| 39 | | |
| 40 | | /* empty output buffer or any other command/data will be lost */ |
| 41 | | if (!kbd_empty_output_buffer()) { |
| 42 | | printk_err("Keyboard output buffer would not empty\n"); |
| 43 | | return; |
| 44 | | } |
| 45 | | |
| 46 | | /* read self-test result, 0x55 should be returned form 0x60 */ |
| | 92 | |
| | 93 | /* clean up any junk that might have been in the kbc */ |
| | 94 | if (!kbc_cleanup_buffers()) return; |
| | 95 | |
| | 96 | /* reset/self test 8042 - send cmd 0xAA, */ |
| | 97 | if (!kbc_input_buffer_empty()) return; |
| | 98 | outb(0xAA, 0x64); |
| | 99 | if (!kbc_output_buffer_full()) return; |
| | 100 | |
| | 101 | /* read self-test result, 0x55 is returned in the output buffer (0x60) */ |
| 48 | | printk_err("Keyboard selftest failed\n"); |
| 49 | | return; |
| 50 | | } |
| 51 | | |
| 52 | | /* enable keyboard interface */ |
| 53 | | outb(0x60, 0x64); |
| 54 | | kbd_empty_input_buffer(); |
| 55 | | |
| 56 | | /* send cmd: enable IRQ 1 */ |
| 57 | | outb(0x61, 0x60); |
| 58 | | kbd_empty_input_buffer(); |
| 59 | | |
| 60 | | /* reset kerboard and self test (keyboard side) */ |
| 61 | | outb(0xff, 0x60); |
| 62 | | |
| 63 | | /* empty inut bufferm or any other command/data will be lost */ |
| 64 | | kbd_empty_input_buffer(); |
| 65 | | |
| 66 | | /* empty output buffer or any other command/data will be lost */ |
| 67 | | kbd_empty_output_buffer(); |
| 68 | | |
| 69 | | if ((regval = inb(0x60) != 0xfa)) |
| 70 | | return; |
| 71 | | |
| 72 | | kbd_empty_output_buffer(); |
| 73 | | if ((regval = inb(0x60) != 0xaa)) |
| 74 | | return; |
| 75 | | } |
| | 103 | printk_err("Keyboard Controller selftest failed: 0x%x\n", regval); |
| | 104 | return; |
| | 105 | } |
| | 106 | |
| | 107 | /* Enable keyboard interface - No IRQ*/ |
| | 108 | resend = 10; |
| | 109 | regval = 0; |
| | 110 | do { |
| | 111 | if (!kbc_input_buffer_empty()) return; |
| | 112 | outb(0x60, 0x64); |
| | 113 | if (!kbc_input_buffer_empty()) return; |
| | 114 | outb(0x20, 0x60); /* send cmd: enable keyboard and IRQ 1 */ |
| | 115 | u8 resend = 10; |
| | 116 | if ((inb(0x64) & 0x01)) { |
| | 117 | regval = inb(0x60); |
| | 118 | } |
| | 119 | --resend; |
| | 120 | } while (regval == 0xFE && resend > 0); |
| | 121 | |
| | 122 | /* clean up any junk that might have been in the keyboard */ |
| | 123 | if (!kbc_cleanup_buffers()) return; |
| | 124 | |
| | 125 | /* reset keyboard and self test (keyboard side) */ |
| | 126 | regval = send_keyboard(0xFF); |
| | 127 | if (regval != 0xFA) { |
| | 128 | printk_err("Keyboard selftest failed ACK: 0x%x\n", regval); |
| | 129 | return; |
| | 130 | } |
| | 131 | if (!kbc_output_buffer_full()) return; |
| | 132 | regval = inb(0x60); |
| | 133 | if (regval != 0xAA) { |
| | 134 | printk_err("Keyboard selftest failed: 0x%x\n", regval); |
| | 135 | return; |
| | 136 | } |
| | 137 | |
| | 138 | /* |
| | 139 | * The following set scancode stuff is what normal BIOS do. It could be |
| | 140 | * argued that coreboot shouldn't set the scan code..... |
| | 141 | */ |
| | 142 | |
| | 143 | /* disable the keyboard */ |
| | 144 | regval = send_keyboard(0xF5); |
| | 145 | if (regval != 0xFA) { |
| | 146 | printk_err("Keyboard disable failed ACK: 0x%x\n", regval); |
| | 147 | return; |
| | 148 | } |
| | 149 | |
| | 150 | /* Set scancode command */ |
| | 151 | regval = send_keyboard(0xF0); |
| | 152 | if (regval != 0xFA) { |
| | 153 | printk_err("Keyboard set scancode cmd failed ACK: 0x%x\n", regval); |
| | 154 | return; |
| | 155 | } |
| | 156 | /* Set scancode mode 2 */ |
| | 157 | regval = send_keyboard(0x02); |
| | 158 | if (regval != 0xFA) { |
| | 159 | printk_err("Keyboard set scancode mode failed ACK: 0x%x\n", regval); |
| | 160 | return; |
| | 161 | } |
| | 162 | |
| | 163 | /* enable the keyboard */ |
| | 164 | regval = send_keyboard(0xF4); |
| | 165 | if (regval != 0xFA) { |
| | 166 | printk_err("Keyboard enable failed ACK: 0x%x\n", regval); |
| | 167 | return; |
| | 168 | } |
| | 169 | |
| | 170 | /* All is well - enable keyboard interface */ |
| | 171 | resend = 10; |
| | 172 | regval = 0; |
| | 173 | do { |
| | 174 | if (!kbc_input_buffer_empty()) return; |
| | 175 | outb(0x60, 0x64); |
| | 176 | if (!kbc_input_buffer_empty()) return; |
| | 177 | outb(0x61, 0x60); /* send cmd: enable keyboard and IRQ 1 */ |
| | 178 | if ((inb(0x64) & 0x01)) { |
| | 179 | regval = inb(0x60); |
| | 180 | } |
| | 181 | --resend; |
| | 182 | } while (regval == 0xFE && resend > 0); |
| | 183 | } |
| | 184 | |
| | 192 | |
| | 193 | /* |
| | 194 | * Support PS/2 mode - oddball SIOs(KBC) need this setup |
| | 195 | * Not well documented. Google - 0xcb keyboard controller |
| | 196 | * This is called before pc_keyboard_init(). |
| | 197 | */ |
| | 198 | void set_kbc_ps2_mode() |
| | 199 | { |
| | 200 | /* clean up any junk that might have been in the kbc */ |
| | 201 | if (!kbc_cleanup_buffers()) return; |
| | 202 | |
| | 203 | /* reset/self test 8042 before we can do anything */ |
| | 204 | if (!kbc_input_buffer_empty()) return; |
| | 205 | outb(0xAA, 0x64); |
| | 206 | if (!kbc_output_buffer_full()) return; |
| | 207 | |
| | 208 | /* read self-test result, 0x55 is returned in the output buffer (0x60) */ |
| | 209 | if ((inb(0x60) != 0x55)) { |
| | 210 | printk_err("Keyboard Controller selftest failed\n"); |
| | 211 | return; |
| | 212 | } |
| | 213 | |
| | 214 | /* Support PS/2 mode */ |
| | 215 | if (!kbc_input_buffer_empty()) return; |
| | 216 | outb(0xcb, 0x64); |
| | 217 | if (!kbc_input_buffer_empty()) return; |
| | 218 | outb(0x01, 0x60); |
| | 219 | kbc_cleanup_buffers(); |
| | 220 | } |