coreboot-v2/src/pc80/keyboard.c
<<
>>
Prefs
   1/*
   2 * This file is part of the coreboot project.
   3 *
   4 * Copyright (C) 2009 coresystems GmbH
   5 * Copyright (C) 2008 Advanced Micro Devices, Inc.
   6 * Copyright (C) 2003 Ollie Lo <ollielo@hotmail.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; version 2 of the License.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  20 */
  21
  22
  23#include <console/console.h>
  24#include <pc80/keyboard.h>
  25#include <device/device.h>
  26#include <arch/io.h>
  27#include <delay.h>
  28
  29#define KBD_DATA        0x60
  30#define KBD_COMMAND     0x64
  31#define KBD_STATUS      0x64
  32#define   KBD_IBF       (1 << 1) // 1: input buffer full (data ready for ec)
  33#define   KBD_OBF       (1 << 0) // 1: output buffer full (data ready for host)
  34
  35// Keyboard Controller Commands
  36#define KBC_CMD_READ_COMMAND    0x20 // Read command byte
  37#define KBC_CMD_WRITE_COMMAND   0x60 // Write command byte
  38#define KBC_CMD_SELF_TEST       0xAA // Controller self-test
  39
  40/* The Keyboard controller command byte
  41 *  BIT | Description
  42 *  ----+-------------------------------------------------------
  43 *   7  | reserved, must be zero
  44 *   6  | XT Translation, (1 = on, 0 = off)
  45 *   5  | Disable Mouse Port (1 = disable, 0 = enable)
  46 *   4  | Disable Keyboard Port (1 = disable, 0 = enable)
  47 *   3  | reserved, must be zero
  48 *   2  | System Flag (1 = self-test passed. DO NOT SET TO ZERO)
  49 *   1  | Mouse Port Interrupts (1 = enable, 0 = disable)
  50 *   0  | Keyboard Port Interrupts (1 = enable, 0 = disable)
  51 */
  52
  53// Keyboard Controller Replies
  54#define KBC_REPLY_SELFTEST_OK   0x55 // controller self-test succeeded
  55
  56//
  57// Keyboard Replies
  58//
  59#define KBD_REPLY_POR           0xAA    // Power on reset
  60#define KBD_REPLY_ACK           0xFA    // Command ACK
  61#define KBD_REPLY_RESEND        0xFE    // Command NACK, send command again
  62
  63/* Wait 200ms for keyboard controller answers */
  64#define KBC_TIMEOUT_IN_MS 200
  65
  66static int kbc_input_buffer_empty(void)
  67{
  68        u32 timeout;
  69        for(timeout = KBC_TIMEOUT_IN_MS; timeout && (inb(KBD_STATUS) & KBD_IBF); timeout--) {
  70                mdelay(1);
  71        }
  72
  73        if (!timeout) {
  74                printk_warning("Unexpected Keyboard controller input buffer full\n");
  75        }
  76        return !!timeout;
  77}
  78
  79
  80static int kbc_output_buffer_full(void)
  81{
  82        u32 timeout;
  83        for(timeout = KBC_TIMEOUT_IN_MS; timeout && ((inb(KBD_STATUS) & KBD_OBF) == 0); timeout--) {
  84                mdelay(1);
  85        }
  86
  87        if (!timeout) {
  88                printk_warning("Keyboard controller output buffer result timeout\n");
  89        }
  90        return !!timeout;
  91}
  92
  93
  94static int kbc_cleanup_buffers(void)
  95{
  96        u32 timeout;
  97        for(timeout = KBC_TIMEOUT_IN_MS; timeout && (inb(KBD_STATUS) & (KBD_OBF | KBD_IBF)); timeout--) {
  98                mdelay(1);
  99                inb(KBD_DATA);
 100        }
 101
 102        if (!timeout) {
 103                printk_err("Couldn't cleanup the keyboard controller buffers\n");
 104                printk_err("Status (0x%x): 0x%x, Buffer (0x%x): 0x%x\n",
 105                                KBD_STATUS, inb(KBD_STATUS), KBD_DATA, inb(KBD_DATA));
 106        }
 107
 108        return !!timeout;
 109}
 110
 111static int kbc_self_test(void)
 112{
 113        u8 self_test;
 114
 115        /* Clean up any junk that might have been in the KBC.
 116         * Both input and output buffers must be empty.
 117         */
 118        if (!kbc_cleanup_buffers())
 119                return 0;
 120
 121        /* reset/self test 8042 - send cmd 0xAA */
 122        outb(KBC_CMD_SELF_TEST, KBD_COMMAND);
 123
 124        if (!kbc_output_buffer_full()) {
 125                /* There probably is no keyboard controller. */
 126                printk_err("Could not reset keyboard controller.\n");
 127                return 0;
 128        }
 129
 130        /* read self-test result, 0x55 is returned in the output buffer */
 131        self_test = inb(KBD_DATA);
 132
 133        if (self_test != 0x55) {
 134                printk_err("Keyboard Controller self-test failed: 0x%x\n",
 135                                self_test);
 136                return 0;
 137        }
 138
 139        return 1;
 140}
 141
 142static u8 send_keyboard(u8 command)
 143{
 144        u8 regval = 0;
 145        u8 resend = 10;
 146
 147        do {
 148                if (!kbc_input_buffer_empty()) return 0;
 149                outb(command, KBD_DATA);
 150                if (!kbc_output_buffer_full()) {
 151                        printk_err("Could not send keyboard command %02x\n",
 152                                        command);
 153                        return 0;
 154                }
 155                regval = inb(KBD_DATA);
 156                --resend;
 157        } while (regval == 0xFE && resend > 0);
 158
 159        return regval;
 160}
 161
 162static void pc_keyboard_init(struct pc_keyboard *keyboard)
 163{
 164        u8 regval;
 165        printk_debug("Keyboard init...\n");
 166
 167        /* Run a keyboard controller self-test */
 168        if (!kbc_self_test())
 169                return;
 170
 171        /* Enable keyboard interface - No IRQ */
 172        if (!kbc_input_buffer_empty()) return;
 173        outb(0x60, KBD_COMMAND);
 174        if (!kbc_input_buffer_empty()) return;
 175        outb(0x20, KBD_DATA);   /* send cmd: enable keyboard */
 176        if (!kbc_input_buffer_empty()) {
 177                printk_info("Timeout while enabling keyboard\n");
 178                return;
 179        }
 180
 181        /* clean up any junk that might have been in the keyboard */
 182        if (!kbc_cleanup_buffers()) return;
 183
 184        /* reset keyboard and self test (keyboard side) */
 185        regval = send_keyboard(0xFF);
 186        if (regval != 0xFA) {
 187                printk_err("Keyboard selftest failed ACK: 0x%x\n", regval);
 188                return;
 189        }
 190
 191        if (!kbc_output_buffer_full()) {
 192                printk_err("Timeout waiting for keyboard after reset.\n");
 193                return;
 194        }
 195
 196        regval = inb(KBD_DATA);
 197        if (regval != 0xAA) {
 198                printk_err("Keyboard selftest failed: 0x%x\n", regval);
 199                return;
 200        }
 201
 202        /*
 203         * The following set scancode stuff is what normal BIOS do. It could be
 204         * argued that coreboot shouldn't set the scan code.....
 205         */
 206
 207        /* disable the keyboard */
 208        regval = send_keyboard(0xF5);
 209        if (regval != 0xFA) {
 210                printk_err("Keyboard disable failed ACK: 0x%x\n", regval);
 211                return;
 212        }
 213
 214        /* Set scancode command */
 215        regval = send_keyboard(0xF0);
 216        if (regval != 0xFA) {
 217                printk_err("Keyboard set scancode cmd failed ACK: 0x%x\n", regval);
 218                return;
 219        }
 220        /* Set scancode mode 2 */
 221        regval = send_keyboard(0x02);
 222        if (regval != 0xFA) {
 223                printk_err("Keyboard set scancode mode failed ACK: 0x%x\n", regval);
 224                return;
 225        }
 226
 227        /* enable the keyboard */
 228        regval = send_keyboard(0xF4);
 229        if (regval != 0xFA) {
 230                printk_err("Keyboard enable failed ACK: 0x%x\n", regval);
 231                return;
 232        }
 233
 234        /* All is well - enable keyboard interface */
 235        if (!kbc_input_buffer_empty()) return;
 236        outb(0x60, KBD_COMMAND);
 237        if (!kbc_input_buffer_empty()) return;
 238        outb(0x61, KBD_DATA);   /* send cmd: enable keyboard and IRQ 1 */
 239        if (kbc_output_buffer_full()) {
 240                printk_err("Timeout during final keyboard enable\n");
 241                return;
 242        }
 243}
 244
 245
 246void init_pc_keyboard(unsigned port0, unsigned port1, struct pc_keyboard *kbd)
 247{
 248        if ((port0 == 0x60) && (port1 == 0x64)) {
 249                pc_keyboard_init(kbd);
 250        } else {
 251                printk_warning("Unsupported keyboard controller.\n");
 252        }
 253}
 254
 255/*
 256 * Support PS/2 mode -  oddball SIOs(KBC) need this setup
 257 * Not well documented. Google - 0xcb keyboard controller
 258 * This is called before pc_keyboard_init().
 259 */
 260void set_kbc_ps2_mode(void)
 261{
 262        /* Run a keyboard controller self-test */
 263        if (!kbc_self_test())
 264                return;
 265
 266        /* Support PS/2 mode */
 267        if (!kbc_input_buffer_empty()) return;
 268        outb(0xcb, KBD_COMMAND);
 269
 270        if (!kbc_input_buffer_empty()) return;
 271        outb(0x01, KBD_DATA);
 272
 273        kbc_cleanup_buffers();
 274}
 275
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.