Add support for the Siemens SMN42 board.
[platform/kernel/u-boot.git] / drivers / 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 #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
196         /* AMCC powerpc portables need this to use scan-code set 1 -- Cort */
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 */