2008-09-26 21:15:38 +02:00
|
|
|
/*
|
|
|
|
* This file is part of the coreboot project.
|
|
|
|
*
|
2009-07-21 23:24:22 +02:00
|
|
|
* Copyright (C) 2009 coresystems GmbH
|
2008-09-26 21:15:38 +02:00
|
|
|
* Copyright (C) 2008 Advanced Micro Devices, Inc.
|
2009-07-21 23:24:22 +02:00
|
|
|
* Copyright (C) 2003 Ollie Lo <ollielo@hotmail.com>
|
2008-09-26 21:15:38 +02:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; version 2 of the License.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2004-03-11 16:01:31 +01:00
|
|
|
#include <console/console.h>
|
|
|
|
#include <pc80/keyboard.h>
|
|
|
|
#include <device/device.h>
|
2003-09-26 17:24:54 +02:00
|
|
|
#include <arch/io.h>
|
2009-07-21 23:24:22 +02:00
|
|
|
#include <delay.h>
|
|
|
|
|
|
|
|
/* Wait 200ms for keyboard controller answers */
|
|
|
|
#define KBC_TIMEOUT_IN_MS 200
|
2003-09-26 18:12:23 +02:00
|
|
|
|
2008-09-26 21:15:38 +02:00
|
|
|
static int kbc_input_buffer_empty(void)
|
2004-11-11 12:56:00 +01:00
|
|
|
{
|
2008-09-26 21:15:38 +02:00
|
|
|
u32 timeout;
|
2009-07-21 23:24:22 +02:00
|
|
|
for(timeout = KBC_TIMEOUT_IN_MS; timeout && (inb(0x64) & 0x02); timeout--) {
|
|
|
|
mdelay(1);
|
2008-09-26 21:15:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!timeout) {
|
2009-07-21 23:24:22 +02:00
|
|
|
printk_warning("Unexpected Keyboard controller input buffer full\n");
|
2004-11-11 12:56:00 +01:00
|
|
|
}
|
|
|
|
return !!timeout;
|
|
|
|
}
|
|
|
|
|
2008-09-26 21:15:38 +02:00
|
|
|
|
|
|
|
static int kbc_output_buffer_full(void)
|
2004-11-11 12:56:00 +01:00
|
|
|
{
|
2008-09-26 21:15:38 +02:00
|
|
|
u32 timeout;
|
2009-07-21 23:24:22 +02:00
|
|
|
for(timeout = KBC_TIMEOUT_IN_MS; timeout && ((inb(0x64) & 0x01) == 0); timeout--) {
|
|
|
|
mdelay(1);
|
2008-09-26 21:15:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!timeout) {
|
2009-07-21 23:24:22 +02:00
|
|
|
printk_warning("Keyboard controller output buffer result timeout\n");
|
2004-11-11 12:56:00 +01:00
|
|
|
}
|
|
|
|
return !!timeout;
|
|
|
|
}
|
|
|
|
|
2008-09-26 21:15:38 +02:00
|
|
|
|
|
|
|
static int kbc_cleanup_buffers(void)
|
|
|
|
{
|
|
|
|
u32 timeout;
|
2009-07-21 23:24:22 +02:00
|
|
|
for(timeout = KBC_TIMEOUT_IN_MS; timeout && (inb(0x64) & 0x03); timeout--) {
|
|
|
|
mdelay(1);
|
2008-09-26 21:15:38 +02:00
|
|
|
inb(0x60);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!timeout) {
|
|
|
|
printk_err("Couldn't cleanup the keyboard controller buffers\n");
|
|
|
|
printk_err("0x64: 0x%x, 0x60: 0x%x\n", inb(0x64), inb(0x60));
|
|
|
|
}
|
|
|
|
return !!timeout;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static u8 send_keyboard(u8 command)
|
2003-09-26 17:24:54 +02:00
|
|
|
{
|
2008-09-26 21:15:38 +02:00
|
|
|
u8 regval = 0;
|
|
|
|
u8 resend = 10;
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (!kbc_input_buffer_empty()) return 0;
|
|
|
|
outb(command, 0x60);
|
2009-07-21 23:24:22 +02:00
|
|
|
if (!kbc_output_buffer_full()) {
|
|
|
|
printk_err("Could not send keyboard command %02x\n",
|
|
|
|
command);
|
|
|
|
return 0;
|
|
|
|
}
|
2008-09-26 21:15:38 +02:00
|
|
|
regval = inb(0x60);
|
|
|
|
--resend;
|
|
|
|
} while (regval == 0xFE && resend > 0);
|
2003-09-26 17:24:54 +02:00
|
|
|
|
2008-09-26 21:15:38 +02:00
|
|
|
return regval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void pc_keyboard_init(struct pc_keyboard *keyboard)
|
|
|
|
{
|
|
|
|
u8 regval;
|
|
|
|
u8 resend;
|
2007-10-18 00:34:40 +02:00
|
|
|
printk_debug("Keyboard init...\n");
|
2003-09-26 17:24:54 +02:00
|
|
|
|
2008-09-26 21:15:38 +02:00
|
|
|
/* clean up any junk that might have been in the kbc */
|
|
|
|
if (!kbc_cleanup_buffers()) return;
|
2004-11-11 12:56:00 +01:00
|
|
|
|
2009-07-21 23:24:22 +02:00
|
|
|
/* reset/self test 8042 - send cmd 0xAA */
|
2008-09-26 21:15:38 +02:00
|
|
|
if (!kbc_input_buffer_empty()) return;
|
|
|
|
outb(0xAA, 0x64);
|
2009-07-21 23:24:22 +02:00
|
|
|
if (!kbc_output_buffer_full()) {
|
|
|
|
printk_err("Could not reset keyboard controller.\n");
|
|
|
|
return;
|
|
|
|
}
|
2003-09-26 17:24:54 +02:00
|
|
|
|
2008-09-26 21:15:38 +02:00
|
|
|
/* read self-test result, 0x55 is returned in the output buffer (0x60) */
|
2007-05-24 15:55:45 +02:00
|
|
|
if ((regval = inb(0x60) != 0x55)) {
|
2009-07-21 23:24:22 +02:00
|
|
|
printk_err("Keyboard Controller self-test failed: 0x%x\n", regval);
|
2003-09-26 17:24:54 +02:00
|
|
|
return;
|
2007-05-24 15:55:45 +02:00
|
|
|
}
|
2003-09-26 17:24:54 +02:00
|
|
|
|
2009-07-21 23:24:22 +02:00
|
|
|
/* Enable keyboard interface - No IRQ */
|
2008-09-26 21:15:38 +02:00
|
|
|
resend = 10;
|
|
|
|
regval = 0;
|
|
|
|
do {
|
|
|
|
if (!kbc_input_buffer_empty()) return;
|
|
|
|
outb(0x60, 0x64);
|
|
|
|
if (!kbc_input_buffer_empty()) return;
|
2009-05-24 00:00:58 +02:00
|
|
|
outb(0x20, 0x60); /* send cmd: enable keyboard */
|
2009-07-21 23:24:22 +02:00
|
|
|
if (kbc_output_buffer_full()) {
|
2008-09-26 21:15:38 +02:00
|
|
|
regval = inb(0x60);
|
2009-07-21 23:24:22 +02:00
|
|
|
} else {
|
|
|
|
printk_info("Timeout while enabling keyboard. (No keyboard present?)\n");
|
|
|
|
regval = inb(0x60); /* Better than 0 ? */
|
2008-09-26 21:15:38 +02:00
|
|
|
}
|
2009-05-01 04:24:40 +02:00
|
|
|
--resend;
|
2008-09-26 21:15:38 +02:00
|
|
|
} while (regval == 0xFE && resend > 0);
|
2003-09-26 17:24:54 +02:00
|
|
|
|
2008-09-26 21:15:38 +02:00
|
|
|
/* clean up any junk that might have been in the keyboard */
|
|
|
|
if (!kbc_cleanup_buffers()) return;
|
2003-09-26 17:24:54 +02:00
|
|
|
|
2008-09-26 21:15:38 +02:00
|
|
|
/* reset keyboard and self test (keyboard side) */
|
|
|
|
regval = send_keyboard(0xFF);
|
|
|
|
if (regval != 0xFA) {
|
|
|
|
printk_err("Keyboard selftest failed ACK: 0x%x\n", regval);
|
|
|
|
return;
|
|
|
|
}
|
2009-07-21 23:24:22 +02:00
|
|
|
if (!kbc_output_buffer_full()) {
|
|
|
|
printk_err("Timeout waiting for keyboard after reset.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-09-26 21:15:38 +02:00
|
|
|
regval = inb(0x60);
|
|
|
|
if (regval != 0xAA) {
|
|
|
|
printk_err("Keyboard selftest failed: 0x%x\n", regval);
|
|
|
|
return;
|
|
|
|
}
|
2003-09-26 17:24:54 +02:00
|
|
|
|
2008-09-26 21:15:38 +02:00
|
|
|
/*
|
|
|
|
* The following set scancode stuff is what normal BIOS do. It could be
|
|
|
|
* argued that coreboot shouldn't set the scan code.....
|
|
|
|
*/
|
2004-11-11 12:56:00 +01:00
|
|
|
|
2008-09-26 21:15:38 +02:00
|
|
|
/* disable the keyboard */
|
|
|
|
regval = send_keyboard(0xF5);
|
|
|
|
if (regval != 0xFA) {
|
|
|
|
printk_err("Keyboard disable failed ACK: 0x%x\n", regval);
|
|
|
|
return;
|
|
|
|
}
|
2003-09-26 17:24:54 +02:00
|
|
|
|
2008-09-26 21:15:38 +02:00
|
|
|
/* Set scancode command */
|
|
|
|
regval = send_keyboard(0xF0);
|
|
|
|
if (regval != 0xFA) {
|
|
|
|
printk_err("Keyboard set scancode cmd failed ACK: 0x%x\n", regval);
|
2003-09-26 17:24:54 +02:00
|
|
|
return;
|
2008-09-26 21:15:38 +02:00
|
|
|
}
|
|
|
|
/* Set scancode mode 2 */
|
|
|
|
regval = send_keyboard(0x02);
|
|
|
|
if (regval != 0xFA) {
|
|
|
|
printk_err("Keyboard set scancode mode failed ACK: 0x%x\n", regval);
|
|
|
|
return;
|
|
|
|
}
|
2003-09-26 17:24:54 +02:00
|
|
|
|
2008-09-26 21:15:38 +02:00
|
|
|
/* enable the keyboard */
|
|
|
|
regval = send_keyboard(0xF4);
|
|
|
|
if (regval != 0xFA) {
|
|
|
|
printk_err("Keyboard enable failed ACK: 0x%x\n", regval);
|
2003-09-26 17:24:54 +02:00
|
|
|
return;
|
2008-09-26 21:15:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* All is well - enable keyboard interface */
|
|
|
|
resend = 10;
|
|
|
|
regval = 0;
|
|
|
|
do {
|
|
|
|
if (!kbc_input_buffer_empty()) return;
|
|
|
|
outb(0x60, 0x64);
|
|
|
|
if (!kbc_input_buffer_empty()) return;
|
|
|
|
outb(0x61, 0x60); /* send cmd: enable keyboard and IRQ 1 */
|
2009-07-21 23:24:22 +02:00
|
|
|
if (kbc_output_buffer_full()) {
|
2008-09-26 21:15:38 +02:00
|
|
|
regval = inb(0x60);
|
|
|
|
}
|
|
|
|
--resend;
|
|
|
|
} while (regval == 0xFE && resend > 0);
|
2003-09-26 17:24:54 +02:00
|
|
|
}
|
|
|
|
|
2008-09-26 21:15:38 +02:00
|
|
|
|
2004-03-11 16:01:31 +01:00
|
|
|
void init_pc_keyboard(unsigned port0, unsigned port1, struct pc_keyboard *kbd)
|
|
|
|
{
|
|
|
|
if ((port0 == 0x60) && (port1 == 0x64)) {
|
|
|
|
pc_keyboard_init(kbd);
|
|
|
|
}
|
|
|
|
}
|
2008-09-26 21:15:38 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Support PS/2 mode - oddball SIOs(KBC) need this setup
|
|
|
|
* Not well documented. Google - 0xcb keyboard controller
|
|
|
|
* This is called before pc_keyboard_init().
|
|
|
|
*/
|
2009-05-24 00:02:31 +02:00
|
|
|
void set_kbc_ps2_mode(void)
|
2008-09-26 21:15:38 +02:00
|
|
|
{
|
|
|
|
/* clean up any junk that might have been in the kbc */
|
|
|
|
if (!kbc_cleanup_buffers()) return;
|
|
|
|
|
|
|
|
/* reset/self test 8042 before we can do anything */
|
|
|
|
if (!kbc_input_buffer_empty()) return;
|
|
|
|
outb(0xAA, 0x64);
|
|
|
|
if (!kbc_output_buffer_full()) return;
|
|
|
|
|
|
|
|
/* read self-test result, 0x55 is returned in the output buffer (0x60) */
|
|
|
|
if ((inb(0x60) != 0x55)) {
|
|
|
|
printk_err("Keyboard Controller selftest failed\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Support PS/2 mode */
|
|
|
|
if (!kbc_input_buffer_empty()) return;
|
|
|
|
outb(0xcb, 0x64);
|
|
|
|
if (!kbc_input_buffer_empty()) return;
|
|
|
|
outb(0x01, 0x60);
|
|
|
|
kbc_cleanup_buffers();
|
|
|
|
}
|