1 /***********************************************************************
2  *
3  * (C) Copyright 2004
4  * DENX Software Engineering
5  * Wolfgang Denk, wd@denx.de
6  *
7  * PS/2 keyboard driver
8  *
9  * Originally from linux source (drivers/char/pc_keyb.c)
10  *
11  ***********************************************************************/
12 
13 #include <common.h>
14 
15 #include <keyboard.h>
16 #include <pc_keyb.h>
17 
18 #undef KBG_DEBUG
19 
20 #ifdef KBG_DEBUG
21 #define	PRINTF(fmt,args...)	printf (fmt ,##args)
22 #else
23 #define PRINTF(fmt,args...)
24 #endif
25 
26 
27 /*
28  * This reads the keyboard status port, and does the
29  * appropriate action.
30  *
31  */
handle_kbd_event(void)32 static unsigned char handle_kbd_event(void)
33 {
34 	unsigned char status = kbd_read_status();
35 	unsigned int work = 10000;
36 
37 	while ((--work > 0) && (status & KBD_STAT_OBF)) {
38 		unsigned char scancode;
39 
40 		scancode = kbd_read_input();
41 
42 		/* Error bytes must be ignored to make the
43 		   Synaptics touchpads compaq use work */
44 		/* Ignore error bytes */
45 		if (!(status & (KBD_STAT_GTO | KBD_STAT_PERR))) {
46 			if (status & KBD_STAT_MOUSE_OBF)
47 				; /* not supported: handle_mouse_event(scancode); */
48 			else
49 				handle_scancode(scancode);
50 		}
51 		status = kbd_read_status();
52 	}
53 	if (!work)
54 		PRINTF("pc_keyb: controller jammed (0x%02X).\n", status);
55 	return status;
56 }
57 
58 
kbd_read_data(void)59 static int kbd_read_data(void)
60 {
61 	int val;
62 	unsigned char status;
63 
64 	val = -1;
65 	status = kbd_read_status();
66 	if (status & KBD_STAT_OBF) {
67 		val = kbd_read_input();
68 		if (status & (KBD_STAT_GTO | KBD_STAT_PERR))
69 			val = -2;
70 	}
71 	return val;
72 }
73 
kbd_wait_for_input(void)74 static int kbd_wait_for_input(void)
75 {
76 	unsigned long timeout;
77 	int val;
78 
79 	timeout = KBD_TIMEOUT;
80 	val=kbd_read_data();
81 	while(val < 0) {
82 		if(timeout--==0)
83 			return -1;
84 		udelay(1000);
85 		val=kbd_read_data();
86 	}
87 	return val;
88 }
89 
90 
kb_wait(void)91 static int kb_wait(void)
92 {
93 	unsigned long timeout = KBC_TIMEOUT * 10;
94 
95 	do {
96 		unsigned char status = handle_kbd_event();
97 		if (!(status & KBD_STAT_IBF))
98 			return 0; /* ok */
99 		udelay(1000);
100 		timeout--;
101 	} while (timeout);
102 	return 1;
103 }
104 
kbd_write_command_w(int data)105 static void kbd_write_command_w(int data)
106 {
107 	if(kb_wait())
108 		PRINTF("timeout in kbd_write_command_w\n");
109 	kbd_write_command(data);
110 }
111 
kbd_write_output_w(int data)112 static void kbd_write_output_w(int data)
113 {
114 	if(kb_wait())
115 		PRINTF("timeout in kbd_write_output_w\n");
116 	kbd_write_output(data);
117 }
118 
kbd_send_data(unsigned char data)119 static void kbd_send_data(unsigned char data)
120 {
121 	kbd_write_output_w(data);
122 	kbd_wait_for_input();
123 }
124 
125 
kbd_initialize(void)126 static char * kbd_initialize(void)
127 {
128 	int status;
129 
130 	/*
131 	 * Test the keyboard interface.
132 	 * This seems to be the only way to get it going.
133 	 * If the test is successful a x55 is placed in the input buffer.
134 	 */
135 	kbd_write_command_w(KBD_CCMD_SELF_TEST);
136 	if (kbd_wait_for_input() != 0x55)
137 		return "Kbd:   failed self test";
138 	/*
139 	 * Perform a keyboard interface test.  This causes the controller
140 	 * to test the keyboard clock and data lines.  The results of the
141 	 * test are placed in the input buffer.
142 	 */
143 	kbd_write_command_w(KBD_CCMD_KBD_TEST);
144 	if (kbd_wait_for_input() != 0x00)
145 		return "Kbd:   interface failed self test";
146 	/*
147 	 * Enable the keyboard by allowing the keyboard clock to run.
148 	 */
149 	kbd_write_command_w(KBD_CCMD_KBD_ENABLE);
150 
151 	/*
152 	 * Reset keyboard. If the read times out
153 	 * then the assumption is that no keyboard is
154 	 * plugged into the machine.
155 	 * This defaults the keyboard to scan-code set 2.
156 	 *
157 	 * Set up to try again if the keyboard asks for RESEND.
158 	 */
159 	do {
160 		kbd_write_output_w(KBD_CMD_RESET);
161 		status = kbd_wait_for_input();
162 		if (status == KBD_REPLY_ACK)
163 			break;
164 		if (status != KBD_REPLY_RESEND) {
165 			PRINTF("status: %X\n",status);
166 			return "Kbd:   reset failed, no ACK";
167 		}
168 	} while (1);
169 	if (kbd_wait_for_input() != KBD_REPLY_POR)
170 		return "Kbd:   reset failed, no POR";
171 
172 	/*
173 	 * Set keyboard controller mode. During this, the keyboard should be
174 	 * in the disabled state.
175 	 *
176 	 * Set up to try again if the keyboard asks for RESEND.
177 	 */
178 	do {
179 		kbd_write_output_w(KBD_CMD_DISABLE);
180 		status = kbd_wait_for_input();
181 		if (status == KBD_REPLY_ACK)
182 			break;
183 		if (status != KBD_REPLY_RESEND)
184 			return "Kbd:   disable keyboard: no ACK";
185 	} while (1);
186 
187 	kbd_write_command_w(KBD_CCMD_WRITE_MODE);
188 	kbd_write_output_w(KBD_MODE_KBD_INT
189 			      | KBD_MODE_SYS
190 			      | KBD_MODE_DISABLE_MOUSE
191 			      | KBD_MODE_KCC);
192 
193 	/* AMCC powerpc portables need this to use scan-code set 1 -- Cort */
194 	kbd_write_command_w(KBD_CCMD_READ_MODE);
195 	if (!(kbd_wait_for_input() & KBD_MODE_KCC)) {
196 		/*
197 		 * If the controller does not support conversion,
198 		 * Set the keyboard to scan-code set 1.
199 		 */
200 		kbd_write_output_w(0xF0);
201 		kbd_wait_for_input();
202 		kbd_write_output_w(0x01);
203 		kbd_wait_for_input();
204 	}
205 	kbd_write_output_w(KBD_CMD_ENABLE);
206 	if (kbd_wait_for_input() != KBD_REPLY_ACK)
207 		return "Kbd:   enable keyboard: no ACK";
208 
209 	/*
210 	 * Finally, set the typematic rate to maximum.
211 	 */
212 	kbd_write_output_w(KBD_CMD_SET_RATE);
213 	if (kbd_wait_for_input() != KBD_REPLY_ACK)
214 		return "Kbd:   Set rate: no ACK";
215 	kbd_write_output_w(0x00);
216 	if (kbd_wait_for_input() != KBD_REPLY_ACK)
217 		return "Kbd:   Set rate: no ACK";
218 	return NULL;
219 }
220 
kbd_interrupt(void * dev_id)221 static void kbd_interrupt(void *dev_id)
222 {
223 	handle_kbd_event();
224 }
225 
226 /******************************************************************
227  * Init
228  ******************************************************************/
229 
kbd_init_hw(void)230 int kbd_init_hw(void)
231 {
232 	char* result;
233 
234 	kbd_request_region();
235 
236 	result=kbd_initialize();
237 	if (result==NULL) {
238 		PRINTF("AT Keyboard initialized\n");
239 		kbd_request_irq(kbd_interrupt);
240 		return (1);
241 	} else {
242 		printf("%s\n",result);
243 		return (-1);
244 	}
245 }
246 
pckbd_leds(unsigned char leds)247 void pckbd_leds(unsigned char leds)
248 {
249 	kbd_send_data(KBD_CMD_SET_LEDS);
250 	kbd_send_data(leds);
251 }
252