]>
Commit | Line | Data |
---|---|---|
1c43771b WD |
1 | /*********************************************************************** |
2 | * | |
3 | * (C) Copyright 2004 | |
4 | * DENX Software Engineering | |
5 | * Wolfgang Denk, wd@denx.de | |
1c43771b WD |
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 | ||
1c43771b WD |
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 | */ | |
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 | ||
59 | static int kbd_read_data(void) | |
60 | { | |
61 | int val; | |
62 | unsigned char status; | |
63 | ||
d0ff51ba | 64 | val = -1; |
1c43771b WD |
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
119 | static void kbd_send_data(unsigned char data) | |
120 | { | |
121 | kbd_write_output_w(data); | |
122 | kbd_wait_for_input(); | |
123 | } | |
124 | ||
125 | ||
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 | ||
0c8721a4 | 193 | /* AMCC powerpc portables need this to use scan-code set 1 -- Cort */ |
1c43771b WD |
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 | ||
221 | static void kbd_interrupt(void *dev_id) | |
222 | { | |
223 | handle_kbd_event(); | |
224 | } | |
225 | ||
226 | /****************************************************************** | |
227 | * Init | |
228 | ******************************************************************/ | |
229 | ||
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 | ||
247 | void pckbd_leds(unsigned char leds) | |
248 | { | |
249 | kbd_send_data(KBD_CMD_SET_LEDS); | |
250 | kbd_send_data(leds); | |
251 | } |