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