blob: 25ad3e40650449cfd15928d90da187d1fc4e6dd0 [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
wdenkb983fa22004-01-16 00:30:56 +000016#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 */
33static 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
60static int kbd_read_data(void)
61{
62 int val;
63 unsigned char status;
64
Wolfgang Denkdc770c72008-07-14 15:19:07 +020065 val = -1;
wdenkb983fa22004-01-16 00:30:56 +000066 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
75static 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
92static 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
106static 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
113static 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
120static void kbd_send_data(unsigned char data)
121{
122 kbd_write_output_w(data);
123 kbd_wait_for_input();
124}
125
126
127static 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
Wolfgang Denk0ee70772005-09-23 11:05:55 +0200194 /* AMCC powerpc portables need this to use scan-code set 1 -- Cort */
wdenkb983fa22004-01-16 00:30:56 +0000195 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
222static void kbd_interrupt(void *dev_id)
223{
224 handle_kbd_event();
225}
226
227/******************************************************************
228 * Init
229 ******************************************************************/
230
231int 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
248void pckbd_leds(unsigned char leds)
249{
250 kbd_send_data(KBD_CMD_SET_LEDS);
251 kbd_send_data(leds);
252}