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