blob: 33e7c5f1248ef428a0763503761ab768a4f26c49 [file] [log] [blame]
wdenkb983fa22004-01-16 00:30:56 +00001/***********************************************************************
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 */
35static 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
62static int kbd_read_data(void)
63{
64 int val;
65 unsigned char status;
66
Wolfgang Denkdc770c72008-07-14 15:19:07 +020067 val = -1;
wdenkb983fa22004-01-16 00:30:56 +000068 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
77static 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
94static 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
108static 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
115static 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
122static void kbd_send_data(unsigned char data)
123{
124 kbd_write_output_w(data);
125 kbd_wait_for_input();
126}
127
128
129static 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
Wolfgang Denk0ee70772005-09-23 11:05:55 +0200196 /* AMCC powerpc portables need this to use scan-code set 1 -- Cort */
wdenkb983fa22004-01-16 00:30:56 +0000197 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
224static void kbd_interrupt(void *dev_id)
225{
226 handle_kbd_event();
227}
228
229/******************************************************************
230 * Init
231 ******************************************************************/
232
233int 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
250void 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 */