]>
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 multiplexer driver | |
8 | * | |
9 | * Originally from linux source (drivers/char/ps2mult.c) | |
10 | * | |
11 | * Uses simple serial driver (ps2ser.c) to access the multiplexer | |
12 | * Used by PS/2 keyboard driver (pc_keyb.c) | |
13 | * | |
14 | ***********************************************************************/ | |
15 | ||
16 | #include <common.h> | |
17 | ||
1c43771b WD |
18 | #include <pc_keyb.h> |
19 | #include <asm/atomic.h> | |
20 | #include <ps2mult.h> | |
21 | ||
22 | /* #define DEBUG_MULT */ | |
23 | /* #define DEBUG_KEYB */ | |
24 | ||
25 | #define KBD_STAT_DEFAULT (KBD_STAT_SELFTEST | KBD_STAT_UNLOCKED) | |
26 | ||
27 | #define PRINTF(format, args...) printf("ps2mult.c: " format, ## args) | |
28 | ||
29 | #ifdef DEBUG_MULT | |
30 | #define PRINTF_MULT(format, args...) printf("PS2MULT: " format, ## args) | |
31 | #else | |
32 | #define PRINTF_MULT(format, args...) | |
33 | #endif | |
34 | ||
35 | #ifdef DEBUG_KEYB | |
36 | #define PRINTF_KEYB(format, args...) printf("KEYB: " format, ## args) | |
37 | #else | |
38 | #define PRINTF_KEYB(format, args...) | |
39 | #endif | |
40 | ||
41 | ||
c837dcb1 | 42 | static ulong start_time; |
1c43771b WD |
43 | static int init_done = 0; |
44 | ||
45 | static int received_escape = 0; | |
46 | static int received_bsync = 0; | |
47 | static int received_selector = 0; | |
48 | ||
49 | static int kbd_command_active = 0; | |
50 | static int mouse_command_active = 0; | |
51 | static int ctl_command_active = 0; | |
52 | ||
53 | static u_char command_byte = 0; | |
54 | ||
55 | static void (*keyb_handler)(void *dev_id); | |
56 | ||
57 | static u_char ps2mult_buf [PS2BUF_SIZE]; | |
58 | static atomic_t ps2mult_buf_cnt; | |
59 | static int ps2mult_buf_in_idx; | |
60 | static int ps2mult_buf_out_idx; | |
61 | ||
62 | static u_char ps2mult_buf_status [PS2BUF_SIZE]; | |
63 | ||
c837dcb1 WD |
64 | #ifndef CONFIG_BOARD_EARLY_INIT_R |
65 | #error #define CONFIG_BOARD_EARLY_INIT_R and call ps2mult_early_init() in board_early_init_r() | |
66 | #endif | |
67 | void ps2mult_early_init (void) | |
68 | { | |
69 | start_time = get_timer(0); | |
70 | } | |
1c43771b WD |
71 | |
72 | static void ps2mult_send_byte(u_char byte, u_char sel) | |
73 | { | |
74 | ps2ser_putc(sel); | |
75 | ||
76 | if (sel == PS2MULT_KB_SELECTOR) { | |
77 | PRINTF_MULT("0x%02x send KEYBOARD\n", byte); | |
78 | kbd_command_active = 1; | |
79 | } else { | |
80 | PRINTF_MULT("0x%02x send MOUSE\n", byte); | |
81 | mouse_command_active = 1; | |
82 | } | |
83 | ||
84 | switch (byte) { | |
85 | case PS2MULT_ESCAPE: | |
86 | case PS2MULT_BSYNC: | |
87 | case PS2MULT_KB_SELECTOR: | |
88 | case PS2MULT_MS_SELECTOR: | |
89 | case PS2MULT_SESSION_START: | |
90 | case PS2MULT_SESSION_END: | |
91 | ps2ser_putc(PS2MULT_ESCAPE); | |
92 | break; | |
93 | default: | |
94 | break; | |
95 | } | |
96 | ||
97 | ps2ser_putc(byte); | |
98 | } | |
99 | ||
100 | static void ps2mult_receive_byte(u_char byte, u_char sel) | |
101 | { | |
102 | u_char status = KBD_STAT_DEFAULT; | |
103 | ||
104 | #if 1 /* Ignore mouse in U-Boot */ | |
105 | if (sel == PS2MULT_MS_SELECTOR) return; | |
106 | #endif | |
107 | ||
108 | if (sel == PS2MULT_KB_SELECTOR) { | |
109 | if (kbd_command_active) { | |
110 | if (!received_bsync) { | |
111 | PRINTF_MULT("0x%02x lost KEYBOARD !!!\n", byte); | |
112 | return; | |
113 | } else { | |
114 | kbd_command_active = 0; | |
115 | received_bsync = 0; | |
116 | } | |
117 | } | |
118 | PRINTF_MULT("0x%02x receive KEYBOARD\n", byte); | |
119 | status |= KBD_STAT_IBF | KBD_STAT_OBF; | |
120 | } else { | |
121 | if (mouse_command_active) { | |
122 | if (!received_bsync) { | |
123 | PRINTF_MULT("0x%02x lost MOUSE !!!\n", byte); | |
124 | return; | |
125 | } else { | |
126 | mouse_command_active = 0; | |
127 | received_bsync = 0; | |
128 | } | |
129 | } | |
130 | PRINTF_MULT("0x%02x receive MOUSE\n", byte); | |
131 | status |= KBD_STAT_IBF | KBD_STAT_OBF | KBD_STAT_MOUSE_OBF; | |
132 | } | |
133 | ||
134 | if (atomic_read(&ps2mult_buf_cnt) < PS2BUF_SIZE) { | |
135 | ps2mult_buf_status[ps2mult_buf_in_idx] = status; | |
136 | ps2mult_buf[ps2mult_buf_in_idx++] = byte; | |
137 | ps2mult_buf_in_idx &= (PS2BUF_SIZE - 1); | |
138 | atomic_inc(&ps2mult_buf_cnt); | |
139 | } else { | |
140 | PRINTF("buffer overflow\n"); | |
141 | } | |
142 | ||
143 | if (received_bsync) { | |
144 | PRINTF("unexpected BSYNC\n"); | |
145 | received_bsync = 0; | |
146 | } | |
147 | } | |
148 | ||
149 | void ps2mult_callback (int in_cnt) | |
150 | { | |
151 | int i; | |
152 | u_char byte; | |
153 | static int keyb_handler_active = 0; | |
154 | ||
155 | if (!init_done) { | |
156 | return; | |
157 | } | |
158 | ||
159 | for (i = 0; i < in_cnt; i ++) { | |
160 | byte = ps2ser_getc(); | |
161 | ||
162 | if (received_escape) { | |
163 | ps2mult_receive_byte(byte, received_selector); | |
164 | received_escape = 0; | |
165 | } else switch (byte) { | |
166 | case PS2MULT_ESCAPE: | |
167 | PRINTF_MULT("ESCAPE receive\n"); | |
168 | received_escape = 1; | |
169 | break; | |
170 | ||
171 | case PS2MULT_BSYNC: | |
172 | PRINTF_MULT("BSYNC receive\n"); | |
173 | received_bsync = 1; | |
174 | break; | |
175 | ||
176 | case PS2MULT_KB_SELECTOR: | |
177 | case PS2MULT_MS_SELECTOR: | |
178 | PRINTF_MULT("%s receive\n", | |
179 | byte == PS2MULT_KB_SELECTOR ? "KB_SEL" : "MS_SEL"); | |
180 | received_selector = byte; | |
181 | break; | |
182 | ||
183 | case PS2MULT_SESSION_START: | |
184 | case PS2MULT_SESSION_END: | |
185 | PRINTF_MULT("%s receive\n", | |
186 | byte == PS2MULT_SESSION_START ? | |
187 | "SESSION_START" : "SESSION_END"); | |
188 | break; | |
189 | ||
190 | default: | |
191 | ps2mult_receive_byte(byte, received_selector); | |
192 | } | |
193 | } | |
194 | ||
195 | if (keyb_handler && !keyb_handler_active && | |
196 | atomic_read(&ps2mult_buf_cnt)) { | |
197 | keyb_handler_active = 1; | |
198 | keyb_handler(NULL); | |
199 | keyb_handler_active = 0; | |
200 | } | |
201 | } | |
202 | ||
203 | u_char ps2mult_read_status(void) | |
204 | { | |
205 | u_char byte; | |
206 | ||
207 | if (atomic_read(&ps2mult_buf_cnt) == 0) { | |
208 | ps2ser_check(); | |
209 | } | |
210 | ||
211 | if (atomic_read(&ps2mult_buf_cnt)) { | |
212 | byte = ps2mult_buf_status[ps2mult_buf_out_idx]; | |
213 | } else { | |
214 | byte = KBD_STAT_DEFAULT; | |
215 | } | |
216 | PRINTF_KEYB("read_status()=0x%02x\n", byte); | |
217 | return byte; | |
218 | } | |
219 | ||
220 | u_char ps2mult_read_input(void) | |
221 | { | |
222 | u_char byte = 0; | |
223 | ||
224 | if (atomic_read(&ps2mult_buf_cnt) == 0) { | |
225 | ps2ser_check(); | |
226 | } | |
227 | ||
228 | if (atomic_read(&ps2mult_buf_cnt)) { | |
229 | byte = ps2mult_buf[ps2mult_buf_out_idx++]; | |
230 | ps2mult_buf_out_idx &= (PS2BUF_SIZE - 1); | |
231 | atomic_dec(&ps2mult_buf_cnt); | |
232 | } | |
233 | PRINTF_KEYB("read_input()=0x%02x\n", byte); | |
234 | return byte; | |
235 | } | |
236 | ||
237 | void ps2mult_write_output(u_char val) | |
238 | { | |
239 | int i; | |
240 | ||
241 | PRINTF_KEYB("write_output(0x%02x)\n", val); | |
242 | ||
243 | for (i = 0; i < KBD_TIMEOUT; i++) { | |
244 | if (!kbd_command_active && !mouse_command_active) { | |
245 | break; | |
246 | } | |
247 | udelay(1000); | |
248 | ps2ser_check(); | |
249 | } | |
250 | ||
251 | if (kbd_command_active) { | |
252 | PRINTF("keyboard command not acknoledged\n"); | |
253 | kbd_command_active = 0; | |
254 | } | |
255 | ||
256 | if (mouse_command_active) { | |
257 | PRINTF("mouse command not acknoledged\n"); | |
258 | mouse_command_active = 0; | |
259 | } | |
260 | ||
261 | if (ctl_command_active) { | |
262 | switch (ctl_command_active) { | |
263 | case KBD_CCMD_WRITE_MODE: | |
264 | /* Scan code conversion not supported */ | |
265 | command_byte = val & ~KBD_MODE_KCC; | |
266 | break; | |
267 | ||
268 | case KBD_CCMD_WRITE_AUX_OBUF: | |
269 | ps2mult_receive_byte(val, PS2MULT_MS_SELECTOR); | |
270 | break; | |
271 | ||
272 | case KBD_CCMD_WRITE_MOUSE: | |
273 | ps2mult_send_byte(val, PS2MULT_MS_SELECTOR); | |
274 | break; | |
275 | ||
276 | default: | |
277 | PRINTF("invalid controller command\n"); | |
278 | break; | |
279 | } | |
280 | ||
281 | ctl_command_active = 0; | |
282 | return; | |
283 | } | |
284 | ||
285 | ps2mult_send_byte(val, PS2MULT_KB_SELECTOR); | |
286 | } | |
287 | ||
288 | void ps2mult_write_command(u_char val) | |
289 | { | |
290 | ctl_command_active = 0; | |
291 | ||
292 | PRINTF_KEYB("write_command(0x%02x)\n", val); | |
293 | ||
294 | switch (val) { | |
295 | case KBD_CCMD_READ_MODE: | |
296 | ps2mult_receive_byte(command_byte, PS2MULT_KB_SELECTOR); | |
297 | break; | |
298 | ||
299 | case KBD_CCMD_WRITE_MODE: | |
300 | ctl_command_active = val; | |
301 | break; | |
302 | ||
303 | case KBD_CCMD_MOUSE_DISABLE: | |
304 | break; | |
305 | ||
306 | case KBD_CCMD_MOUSE_ENABLE: | |
307 | break; | |
308 | ||
309 | case KBD_CCMD_SELF_TEST: | |
310 | ps2mult_receive_byte(0x55, PS2MULT_KB_SELECTOR); | |
311 | break; | |
312 | ||
313 | case KBD_CCMD_KBD_TEST: | |
314 | ps2mult_receive_byte(0x00, PS2MULT_KB_SELECTOR); | |
315 | break; | |
316 | ||
317 | case KBD_CCMD_KBD_DISABLE: | |
318 | break; | |
319 | ||
320 | case KBD_CCMD_KBD_ENABLE: | |
321 | break; | |
322 | ||
323 | case KBD_CCMD_WRITE_AUX_OBUF: | |
324 | ctl_command_active = val; | |
325 | break; | |
326 | ||
327 | case KBD_CCMD_WRITE_MOUSE: | |
328 | ctl_command_active = val; | |
329 | break; | |
330 | ||
331 | default: | |
332 | PRINTF("invalid controller command\n"); | |
333 | break; | |
334 | } | |
335 | } | |
336 | ||
337 | static int ps2mult_getc_w (void) | |
338 | { | |
339 | int res = -1; | |
340 | int i; | |
341 | ||
342 | for (i = 0; i < KBD_TIMEOUT; i++) { | |
343 | if (ps2ser_check()) { | |
344 | res = ps2ser_getc(); | |
345 | break; | |
346 | } | |
347 | udelay(1000); | |
348 | } | |
349 | ||
350 | switch (res) { | |
351 | case PS2MULT_KB_SELECTOR: | |
352 | case PS2MULT_MS_SELECTOR: | |
353 | received_selector = res; | |
354 | break; | |
355 | default: | |
356 | break; | |
357 | } | |
358 | ||
359 | return res; | |
360 | } | |
361 | ||
362 | int ps2mult_init (void) | |
363 | { | |
364 | int byte; | |
365 | int kbd_found = 0; | |
366 | int mouse_found = 0; | |
367 | ||
c837dcb1 WD |
368 | while (get_timer(start_time) < CONFIG_PS2MULT_DELAY); |
369 | ||
1c43771b WD |
370 | ps2ser_init(); |
371 | ||
372 | ps2ser_putc(PS2MULT_SESSION_START); | |
373 | ||
374 | ps2ser_putc(PS2MULT_KB_SELECTOR); | |
375 | ps2ser_putc(KBD_CMD_RESET); | |
376 | ||
377 | do { | |
378 | byte = ps2mult_getc_w(); | |
379 | } while (byte >= 0 && byte != KBD_REPLY_ACK); | |
380 | ||
381 | if (byte == KBD_REPLY_ACK) { | |
382 | byte = ps2mult_getc_w(); | |
383 | if (byte == 0xaa) { | |
384 | kbd_found = 1; | |
385 | puts("keyboard"); | |
386 | } | |
387 | } | |
388 | ||
389 | if (!kbd_found) { | |
390 | while (byte >= 0) { | |
391 | byte = ps2mult_getc_w(); | |
392 | } | |
393 | } | |
394 | ||
395 | #if 1 /* detect mouse */ | |
396 | ps2ser_putc(PS2MULT_MS_SELECTOR); | |
397 | ps2ser_putc(AUX_RESET); | |
398 | ||
399 | do { | |
400 | byte = ps2mult_getc_w(); | |
401 | } while (byte >= 0 && byte != AUX_ACK); | |
402 | ||
403 | if (byte == AUX_ACK) { | |
404 | byte = ps2mult_getc_w(); | |
405 | if (byte == 0xaa) { | |
406 | byte = ps2mult_getc_w(); | |
407 | if (byte == 0x00) { | |
408 | mouse_found = 1; | |
409 | puts(", mouse"); | |
410 | } | |
411 | } | |
412 | } | |
413 | ||
414 | if (!mouse_found) { | |
415 | while (byte >= 0) { | |
416 | byte = ps2mult_getc_w(); | |
417 | } | |
418 | } | |
419 | #endif | |
420 | ||
421 | if (mouse_found || kbd_found) { | |
422 | if (!received_selector) { | |
423 | if (mouse_found) { | |
424 | received_selector = PS2MULT_MS_SELECTOR; | |
425 | } else { | |
426 | received_selector = PS2MULT_KB_SELECTOR; | |
427 | } | |
428 | } | |
429 | ||
430 | init_done = 1; | |
431 | } else { | |
432 | puts("No device found"); | |
433 | } | |
434 | ||
435 | puts("\n"); | |
436 | ||
437 | #if 0 /* for testing */ | |
438 | { | |
439 | int i; | |
440 | u_char key[] = { | |
441 | 0x1f, 0x12, 0x14, 0x12, 0x31, 0x2f, 0x39, /* setenv */ | |
442 | 0x1f, 0x14, 0x20, 0x17, 0x31, 0x39, /* stdin */ | |
443 | 0x1f, 0x12, 0x13, 0x17, 0x1e, 0x26, 0x1c, /* serial */ | |
444 | }; | |
445 | ||
446 | for (i = 0; i < sizeof (key); i++) { | |
447 | ps2mult_receive_byte (key[i], PS2MULT_KB_SELECTOR); | |
448 | ps2mult_receive_byte (key[i] | 0x80, PS2MULT_KB_SELECTOR); | |
449 | } | |
450 | } | |
451 | #endif | |
452 | ||
453 | return init_done ? 0 : -1; | |
454 | } | |
455 | ||
456 | int ps2mult_request_irq(void (*handler)(void *)) | |
457 | { | |
458 | keyb_handler = handler; | |
459 | ||
460 | return 0; | |
461 | } |