252 lines
5.4 KiB
C
252 lines
5.4 KiB
C
|
/***********************************************************************
|
||
|
*
|
||
|
* (C) Copyright 2004
|
||
|
* DENX Software Engineering
|
||
|
* Wolfgang Denk, wd@denx.de
|
||
|
*
|
||
|
* PS/2 keyboard driver
|
||
|
*
|
||
|
* Originally from linux source (drivers/char/pc_keyb.c)
|
||
|
*
|
||
|
***********************************************************************/
|
||
|
|
||
|
#include <common.h>
|
||
|
|
||
|
#include <keyboard.h>
|
||
|
#include <pc_keyb.h>
|
||
|
|
||
|
#undef KBG_DEBUG
|
||
|
|
||
|
#ifdef KBG_DEBUG
|
||
|
#define PRINTF(fmt,args...) printf (fmt ,##args)
|
||
|
#else
|
||
|
#define PRINTF(fmt,args...)
|
||
|
#endif
|
||
|
|
||
|
|
||
|
/*
|
||
|
* This reads the keyboard status port, and does the
|
||
|
* appropriate action.
|
||
|
*
|
||
|
*/
|
||
|
static unsigned char handle_kbd_event(void)
|
||
|
{
|
||
|
unsigned char status = kbd_read_status();
|
||
|
unsigned int work = 10000;
|
||
|
|
||
|
while ((--work > 0) && (status & KBD_STAT_OBF)) {
|
||
|
unsigned char scancode;
|
||
|
|
||
|
scancode = kbd_read_input();
|
||
|
|
||
|
/* Error bytes must be ignored to make the
|
||
|
Synaptics touchpads compaq use work */
|
||
|
/* Ignore error bytes */
|
||
|
if (!(status & (KBD_STAT_GTO | KBD_STAT_PERR))) {
|
||
|
if (status & KBD_STAT_MOUSE_OBF)
|
||
|
; /* not supported: handle_mouse_event(scancode); */
|
||
|
else
|
||
|
handle_scancode(scancode);
|
||
|
}
|
||
|
status = kbd_read_status();
|
||
|
}
|
||
|
if (!work)
|
||
|
PRINTF("pc_keyb: controller jammed (0x%02X).\n", status);
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int kbd_read_data(void)
|
||
|
{
|
||
|
int val;
|
||
|
unsigned char status;
|
||
|
|
||
|
val = -1;
|
||
|
status = kbd_read_status();
|
||
|
if (status & KBD_STAT_OBF) {
|
||
|
val = kbd_read_input();
|
||
|
if (status & (KBD_STAT_GTO | KBD_STAT_PERR))
|
||
|
val = -2;
|
||
|
}
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
static int kbd_wait_for_input(void)
|
||
|
{
|
||
|
unsigned long timeout;
|
||
|
int val;
|
||
|
|
||
|
timeout = KBD_TIMEOUT;
|
||
|
val=kbd_read_data();
|
||
|
while(val < 0) {
|
||
|
if(timeout--==0)
|
||
|
return -1;
|
||
|
udelay(1000);
|
||
|
val=kbd_read_data();
|
||
|
}
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int kb_wait(void)
|
||
|
{
|
||
|
unsigned long timeout = KBC_TIMEOUT * 10;
|
||
|
|
||
|
do {
|
||
|
unsigned char status = handle_kbd_event();
|
||
|
if (!(status & KBD_STAT_IBF))
|
||
|
return 0; /* ok */
|
||
|
udelay(1000);
|
||
|
timeout--;
|
||
|
} while (timeout);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static void kbd_write_command_w(int data)
|
||
|
{
|
||
|
if(kb_wait())
|
||
|
PRINTF("timeout in kbd_write_command_w\n");
|
||
|
kbd_write_command(data);
|
||
|
}
|
||
|
|
||
|
static void kbd_write_output_w(int data)
|
||
|
{
|
||
|
if(kb_wait())
|
||
|
PRINTF("timeout in kbd_write_output_w\n");
|
||
|
kbd_write_output(data);
|
||
|
}
|
||
|
|
||
|
static void kbd_send_data(unsigned char data)
|
||
|
{
|
||
|
kbd_write_output_w(data);
|
||
|
kbd_wait_for_input();
|
||
|
}
|
||
|
|
||
|
|
||
|
static char * kbd_initialize(void)
|
||
|
{
|
||
|
int status;
|
||
|
|
||
|
/*
|
||
|
* Test the keyboard interface.
|
||
|
* This seems to be the only way to get it going.
|
||
|
* If the test is successful a x55 is placed in the input buffer.
|
||
|
*/
|
||
|
kbd_write_command_w(KBD_CCMD_SELF_TEST);
|
||
|
if (kbd_wait_for_input() != 0x55)
|
||
|
return "Kbd: failed self test";
|
||
|
/*
|
||
|
* Perform a keyboard interface test. This causes the controller
|
||
|
* to test the keyboard clock and data lines. The results of the
|
||
|
* test are placed in the input buffer.
|
||
|
*/
|
||
|
kbd_write_command_w(KBD_CCMD_KBD_TEST);
|
||
|
if (kbd_wait_for_input() != 0x00)
|
||
|
return "Kbd: interface failed self test";
|
||
|
/*
|
||
|
* Enable the keyboard by allowing the keyboard clock to run.
|
||
|
*/
|
||
|
kbd_write_command_w(KBD_CCMD_KBD_ENABLE);
|
||
|
|
||
|
/*
|
||
|
* Reset keyboard. If the read times out
|
||
|
* then the assumption is that no keyboard is
|
||
|
* plugged into the machine.
|
||
|
* This defaults the keyboard to scan-code set 2.
|
||
|
*
|
||
|
* Set up to try again if the keyboard asks for RESEND.
|
||
|
*/
|
||
|
do {
|
||
|
kbd_write_output_w(KBD_CMD_RESET);
|
||
|
status = kbd_wait_for_input();
|
||
|
if (status == KBD_REPLY_ACK)
|
||
|
break;
|
||
|
if (status != KBD_REPLY_RESEND) {
|
||
|
PRINTF("status: %X\n",status);
|
||
|
return "Kbd: reset failed, no ACK";
|
||
|
}
|
||
|
} while (1);
|
||
|
if (kbd_wait_for_input() != KBD_REPLY_POR)
|
||
|
return "Kbd: reset failed, no POR";
|
||
|
|
||
|
/*
|
||
|
* Set keyboard controller mode. During this, the keyboard should be
|
||
|
* in the disabled state.
|
||
|
*
|
||
|
* Set up to try again if the keyboard asks for RESEND.
|
||
|
*/
|
||
|
do {
|
||
|
kbd_write_output_w(KBD_CMD_DISABLE);
|
||
|
status = kbd_wait_for_input();
|
||
|
if (status == KBD_REPLY_ACK)
|
||
|
break;
|
||
|
if (status != KBD_REPLY_RESEND)
|
||
|
return "Kbd: disable keyboard: no ACK";
|
||
|
} while (1);
|
||
|
|
||
|
kbd_write_command_w(KBD_CCMD_WRITE_MODE);
|
||
|
kbd_write_output_w(KBD_MODE_KBD_INT
|
||
|
| KBD_MODE_SYS
|
||
|
| KBD_MODE_DISABLE_MOUSE
|
||
|
| KBD_MODE_KCC);
|
||
|
|
||
|
/* AMCC powerpc portables need this to use scan-code set 1 -- Cort */
|
||
|
kbd_write_command_w(KBD_CCMD_READ_MODE);
|
||
|
if (!(kbd_wait_for_input() & KBD_MODE_KCC)) {
|
||
|
/*
|
||
|
* If the controller does not support conversion,
|
||
|
* Set the keyboard to scan-code set 1.
|
||
|
*/
|
||
|
kbd_write_output_w(0xF0);
|
||
|
kbd_wait_for_input();
|
||
|
kbd_write_output_w(0x01);
|
||
|
kbd_wait_for_input();
|
||
|
}
|
||
|
kbd_write_output_w(KBD_CMD_ENABLE);
|
||
|
if (kbd_wait_for_input() != KBD_REPLY_ACK)
|
||
|
return "Kbd: enable keyboard: no ACK";
|
||
|
|
||
|
/*
|
||
|
* Finally, set the typematic rate to maximum.
|
||
|
*/
|
||
|
kbd_write_output_w(KBD_CMD_SET_RATE);
|
||
|
if (kbd_wait_for_input() != KBD_REPLY_ACK)
|
||
|
return "Kbd: Set rate: no ACK";
|
||
|
kbd_write_output_w(0x00);
|
||
|
if (kbd_wait_for_input() != KBD_REPLY_ACK)
|
||
|
return "Kbd: Set rate: no ACK";
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static void kbd_interrupt(void *dev_id)
|
||
|
{
|
||
|
handle_kbd_event();
|
||
|
}
|
||
|
|
||
|
/******************************************************************
|
||
|
* Init
|
||
|
******************************************************************/
|
||
|
|
||
|
int kbd_init_hw(void)
|
||
|
{
|
||
|
char* result;
|
||
|
|
||
|
kbd_request_region();
|
||
|
|
||
|
result=kbd_initialize();
|
||
|
if (result==NULL) {
|
||
|
PRINTF("AT Keyboard initialized\n");
|
||
|
kbd_request_irq(kbd_interrupt);
|
||
|
return (1);
|
||
|
} else {
|
||
|
printf("%s\n",result);
|
||
|
return (-1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void pckbd_leds(unsigned char leds)
|
||
|
{
|
||
|
kbd_send_data(KBD_CMD_SET_LEDS);
|
||
|
kbd_send_data(leds);
|
||
|
}
|