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