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