| /*********************************************************************** | 
 |  * | 
 |  * (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); | 
 | } |