blob: 9b099e827cb0bf3975066fdef63d86a035d3d604 [file] [log] [blame]
Kevin O'Connor3b897192008-07-20 10:08:59 -04001// Support for handling the PS/2 mouse/keyboard ports.
2//
3// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05004// Several ideas taken from code Copyright (c) 1999-2004 Vojtech Pavlik
Kevin O'Connor3b897192008-07-20 10:08:59 -04005//
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05006// This file may be distributed under the terms of the GNU LGPLv3 license.
Kevin O'Connor3b897192008-07-20 10:08:59 -04007
Kevin O'Connor3693af02012-05-13 12:28:55 -04008#include "biosvar.h" // GET_LOW
Kevin O'Connor2d2fa312013-09-14 21:55:26 -04009#include "output.h" // dprintf
Kevin O'Connoraa7c2342013-07-14 15:07:21 -040010#include "pic.h" // pic_eoi1
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040011#include "ps2port.h" // ps2_kbd_command
Kevin O'Connor41639f82013-09-14 19:37:36 -040012#include "romfile.h" // romfile_loadint
Kevin O'Connor3df600b2013-09-14 19:28:55 -040013#include "stacks.h" // yield
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040014#include "util.h" // udelay
Kevin O'Connor4ade5232013-09-18 21:41:48 -040015#include "x86.h" // inb
Kevin O'Connor3b897192008-07-20 10:08:59 -040016
17
18/****************************************************************
19 * Low level i8042 commands.
20 ****************************************************************/
21
22// Timeout value.
23#define I8042_CTL_TIMEOUT 10000
24
25#define I8042_BUFFER_SIZE 16
26
Kevin O'Connor3b897192008-07-20 10:08:59 -040027static int
28i8042_wait_read(void)
29{
Kevin O'Connordd3588f2008-11-29 12:41:48 -050030 dprintf(7, "i8042_wait_read\n");
Kevin O'Connor3b897192008-07-20 10:08:59 -040031 int i;
32 for (i=0; i<I8042_CTL_TIMEOUT; i++) {
33 u8 status = inb(PORT_PS2_STATUS);
34 if (status & I8042_STR_OBF)
35 return 0;
36 udelay(50);
37 }
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -050038 warn_timeout();
Kevin O'Connor3b897192008-07-20 10:08:59 -040039 return -1;
40}
41
42static int
43i8042_wait_write(void)
44{
Kevin O'Connordd3588f2008-11-29 12:41:48 -050045 dprintf(7, "i8042_wait_write\n");
Kevin O'Connor3b897192008-07-20 10:08:59 -040046 int i;
47 for (i=0; i<I8042_CTL_TIMEOUT; i++) {
48 u8 status = inb(PORT_PS2_STATUS);
49 if (! (status & I8042_STR_IBF))
50 return 0;
51 udelay(50);
52 }
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -050053 warn_timeout();
Kevin O'Connor3b897192008-07-20 10:08:59 -040054 return -1;
55}
56
Kevin O'Connoradaf3732010-09-13 20:22:07 -040057static int
Kevin O'Connor3b897192008-07-20 10:08:59 -040058i8042_flush(void)
59{
Kevin O'Connordd3588f2008-11-29 12:41:48 -050060 dprintf(7, "i8042_flush\n");
Kevin O'Connor3b897192008-07-20 10:08:59 -040061 int i;
62 for (i=0; i<I8042_BUFFER_SIZE; i++) {
63 u8 status = inb(PORT_PS2_STATUS);
Kevin O'Connor10ad7992009-10-24 11:06:08 -040064 if (! (status & I8042_STR_OBF))
Kevin O'Connor3b897192008-07-20 10:08:59 -040065 return 0;
Kevin O'Connor3b897192008-07-20 10:08:59 -040066 udelay(50);
Kevin O'Connor0234cd92009-01-04 12:20:02 -050067 u8 data = inb(PORT_PS2_DATA);
Kevin O'Connor9e91c7b2009-01-17 02:30:21 -050068 dprintf(7, "i8042 flushed %x (status=%x)\n", data, status);
Kevin O'Connor3b897192008-07-20 10:08:59 -040069 }
70
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -050071 warn_timeout();
Kevin O'Connor3b897192008-07-20 10:08:59 -040072 return -1;
73}
74
75static int
76__i8042_command(int command, u8 *param)
77{
78 int receive = (command >> 8) & 0xf;
79 int send = (command >> 12) & 0xf;
80
81 // Send the command.
82 int ret = i8042_wait_write();
83 if (ret)
84 return ret;
85 outb(command, PORT_PS2_STATUS);
86
87 // Send parameters (if any).
88 int i;
89 for (i = 0; i < send; i++) {
90 ret = i8042_wait_write();
91 if (ret)
92 return ret;
93 outb(param[i], PORT_PS2_DATA);
94 }
95
96 // Receive parameters (if any).
97 for (i = 0; i < receive; i++) {
98 ret = i8042_wait_read();
99 if (ret)
100 return ret;
101 param[i] = inb(PORT_PS2_DATA);
Kevin O'Connor0234cd92009-01-04 12:20:02 -0500102 dprintf(7, "i8042 param=%x\n", param[i]);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400103 }
104
105 return 0;
106}
107
Kevin O'Connoradaf3732010-09-13 20:22:07 -0400108static int
Kevin O'Connor3b897192008-07-20 10:08:59 -0400109i8042_command(int command, u8 *param)
110{
Kevin O'Connordd3588f2008-11-29 12:41:48 -0500111 dprintf(7, "i8042_command cmd=%x\n", command);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400112 int ret = __i8042_command(command, param);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400113 if (ret)
114 dprintf(2, "i8042 command %x failed\n", command);
115 return ret;
116}
117
118static int
119i8042_kbd_write(u8 c)
120{
Kevin O'Connordd3588f2008-11-29 12:41:48 -0500121 dprintf(7, "i8042_kbd_write c=%d\n", c);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400122 int ret = i8042_wait_write();
123 if (! ret)
124 outb(c, PORT_PS2_DATA);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400125 return ret;
126}
127
128static int
129i8042_aux_write(u8 c)
130{
131 return i8042_command(I8042_CMD_AUX_SEND, &c);
132}
133
Kevin O'Connoradaf3732010-09-13 20:22:07 -0400134void
135i8042_reboot(void)
136{
Kevin O'Connore216ce82012-01-29 12:25:46 -0500137 if (! CONFIG_PS2PORT)
138 return;
Kevin O'Connoradaf3732010-09-13 20:22:07 -0400139 int i;
140 for (i=0; i<10; i++) {
141 i8042_wait_write();
142 udelay(50);
143 outb(0xfe, PORT_PS2_STATUS); /* pulse reset low */
144 udelay(50);
145 }
146}
147
Kevin O'Connor3b897192008-07-20 10:08:59 -0400148
149/****************************************************************
150 * Device commands.
151 ****************************************************************/
152
153#define PS2_RET_ACK 0xfa
154#define PS2_RET_NAK 0xfe
155
156static int
Kevin O'Connor0234cd92009-01-04 12:20:02 -0500157ps2_recvbyte(int aux, int needack, int timeout)
158{
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400159 u32 end = timer_calc(timeout);
Kevin O'Connor0234cd92009-01-04 12:20:02 -0500160 for (;;) {
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400161 u8 status = inb(PORT_PS2_STATUS);
162 if (status & I8042_STR_OBF) {
163 u8 data = inb(PORT_PS2_DATA);
164 dprintf(7, "ps2 read %x\n", data);
165
166 if (!!(status & I8042_STR_AUXDATA) == aux) {
167 if (!needack)
168 return data;
169 if (data == PS2_RET_ACK)
170 return data;
171 if (data == PS2_RET_NAK) {
Kevin O'Connor6080fe02009-12-02 21:19:30 -0500172 dprintf(1, "Got ps2 nak (status=%x)\n", status);
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400173 return data;
174 }
175 }
176
Kevin O'Connorf6282442010-03-13 21:05:12 -0500177 // This data not part of command - just discard it.
178 dprintf(1, "Discarding ps2 data %02x (status=%02x)\n", data, status);
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400179 }
180
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400181 if (timer_check(end)) {
Kevin O'Connor7bd14802016-08-14 22:07:19 -0400182 warn_timeout();
Kevin O'Connor0234cd92009-01-04 12:20:02 -0500183 return -1;
184 }
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400185 yield();
Kevin O'Connor0234cd92009-01-04 12:20:02 -0500186 }
187}
188
189static int
Kevin O'Connor235df202009-01-18 12:54:51 -0500190ps2_sendbyte(int aux, u8 command, int timeout)
Kevin O'Connor3b897192008-07-20 10:08:59 -0400191{
Kevin O'Connordd3588f2008-11-29 12:41:48 -0500192 dprintf(7, "ps2_sendbyte aux=%d cmd=%x\n", aux, command);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400193 int ret;
194 if (aux)
195 ret = i8042_aux_write(command);
196 else
197 ret = i8042_kbd_write(command);
198 if (ret)
199 return ret;
200
201 // Read ack.
Kevin O'Connorb44a8522009-01-17 23:30:01 -0500202 ret = ps2_recvbyte(aux, 1, timeout);
Kevin O'Connor0234cd92009-01-04 12:20:02 -0500203 if (ret < 0)
Kevin O'Connor3b897192008-07-20 10:08:59 -0400204 return ret;
Kevin O'Connor6080fe02009-12-02 21:19:30 -0500205 if (ret != PS2_RET_ACK)
206 return -1;
Kevin O'Connor3b897192008-07-20 10:08:59 -0400207
208 return 0;
209}
210
Kevin O'Connorf318c072014-11-12 12:15:34 -0500211u8 Ps2ctr VARLOW = I8042_CTR_KBDDIS | I8042_CTR_AUXDIS;
Kevin O'Connor3693af02012-05-13 12:28:55 -0400212
Kevin O'Connor3b897192008-07-20 10:08:59 -0400213static int
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400214__ps2_command(int aux, int command, u8 *param)
Kevin O'Connor3b897192008-07-20 10:08:59 -0400215{
Kevin O'Connor6704cf92010-03-13 18:51:46 -0500216 int ret2;
Kevin O'Connor3b897192008-07-20 10:08:59 -0400217 int receive = (command >> 8) & 0xf;
218 int send = (command >> 12) & 0xf;
219
Kevin O'Connor6704cf92010-03-13 18:51:46 -0500220 // Disable interrupts and keyboard/mouse.
Kevin O'Connor3693af02012-05-13 12:28:55 -0400221 u8 ps2ctr = GET_LOW(Ps2ctr);
Kevin O'Connorf6282442010-03-13 21:05:12 -0500222 u8 newctr = ((ps2ctr | I8042_CTR_AUXDIS | I8042_CTR_KBDDIS)
223 & ~(I8042_CTR_KBDINT|I8042_CTR_AUXINT));
Kevin O'Connor6704cf92010-03-13 18:51:46 -0500224 dprintf(6, "i8042 ctr old=%x new=%x\n", ps2ctr, newctr);
225 int ret = i8042_command(I8042_CMD_CTL_WCTR, &newctr);
226 if (ret)
227 return ret;
Kevin O'Connor3b897192008-07-20 10:08:59 -0400228
Kevin O'Connor67a9eec2010-03-13 19:00:02 -0500229 // Flush any interrupts already pending.
230 yield();
231
Kevin O'Connorf6282442010-03-13 21:05:12 -0500232 // Enable port command is being sent to.
Kevin O'Connorf318c072014-11-12 12:15:34 -0500233 SET_LOW(Ps2ctr, newctr);
Kevin O'Connorf6282442010-03-13 21:05:12 -0500234 if (aux)
235 newctr &= ~I8042_CTR_AUXDIS;
236 else
237 newctr &= ~I8042_CTR_KBDDIS;
238 ret = i8042_command(I8042_CMD_CTL_WCTR, &newctr);
239 if (ret)
240 goto fail;
241
Kevin O'Connor4ce5d202015-07-22 14:32:28 -0400242 if ((u8)command == (u8)ATKBD_CMD_RESET_BAT) {
243 // Reset is special wrt timeouts.
Kevin O'Connor3b897192008-07-20 10:08:59 -0400244
Kevin O'Connor235df202009-01-18 12:54:51 -0500245 // Send command.
246 ret = ps2_sendbyte(aux, command, 1000);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400247 if (ret)
248 goto fail;
Kevin O'Connor235df202009-01-18 12:54:51 -0500249
250 // Receive parameters.
251 ret = ps2_recvbyte(aux, 0, 4000);
252 if (ret < 0)
253 goto fail;
254 param[0] = ret;
Kevin O'Connor4ce5d202015-07-22 14:32:28 -0400255 if (receive > 1) {
256 ret = ps2_recvbyte(aux, 0, 500);
257 if (ret < 0)
258 goto fail;
259 param[1] = ret;
260 }
Kevin O'Connorb9ed5e22010-03-13 19:59:24 -0500261 } else if (command == ATKBD_CMD_GETID) {
262 // Getid is special wrt bytes received.
263
264 // Send command.
265 ret = ps2_sendbyte(aux, command, 200);
266 if (ret)
267 goto fail;
268
269 // Receive parameters.
270 ret = ps2_recvbyte(aux, 0, 500);
271 if (ret < 0)
272 goto fail;
273 param[0] = ret;
274 if (ret == 0xab || ret == 0xac || ret == 0x2b || ret == 0x5d
275 || ret == 0x60 || ret == 0x47) {
276 // These ids (keyboards) return two bytes.
277 ret = ps2_recvbyte(aux, 0, 500);
278 if (ret < 0)
279 goto fail;
280 param[1] = ret;
281 } else {
282 param[1] = 0;
283 }
Kevin O'Connor235df202009-01-18 12:54:51 -0500284 } else {
285 // Send command.
286 ret = ps2_sendbyte(aux, command, 200);
287 if (ret)
288 goto fail;
289
290 // Send parameters (if any).
291 int i;
292 for (i = 0; i < send; i++) {
293 ret = ps2_sendbyte(aux, param[i], 200);
294 if (ret)
295 goto fail;
296 }
297
298 // Receive parameters (if any).
299 for (i = 0; i < receive; i++) {
300 ret = ps2_recvbyte(aux, 0, 500);
301 if (ret < 0)
302 goto fail;
303 param[i] = ret;
304 }
Kevin O'Connor3b897192008-07-20 10:08:59 -0400305 }
306
Kevin O'Connor235df202009-01-18 12:54:51 -0500307 ret = 0;
Kevin O'Connor3b897192008-07-20 10:08:59 -0400308
309fail:
Kevin O'Connor6704cf92010-03-13 18:51:46 -0500310 // Restore interrupts and keyboard/mouse.
Kevin O'Connorf318c072014-11-12 12:15:34 -0500311 SET_LOW(Ps2ctr, ps2ctr);
Kevin O'Connor6704cf92010-03-13 18:51:46 -0500312 ret2 = i8042_command(I8042_CMD_CTL_WCTR, &ps2ctr);
313 if (ret2)
314 return ret2;
Kevin O'Connor3b897192008-07-20 10:08:59 -0400315
316 return ret;
317}
318
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400319static int
320ps2_command(int aux, int command, u8 *param)
Kevin O'Connor3b897192008-07-20 10:08:59 -0400321{
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400322 dprintf(7, "ps2_command aux=%d cmd=%x\n", aux, command);
323 int ret = __ps2_command(aux, command, param);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400324 if (ret)
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400325 dprintf(2, "ps2 command %x failed (aux=%d)\n", command, aux);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400326 return ret;
327}
328
329int
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400330ps2_kbd_command(int command, u8 *param)
Kevin O'Connor3b897192008-07-20 10:08:59 -0400331{
Kevin O'Connore216ce82012-01-29 12:25:46 -0500332 if (! CONFIG_PS2PORT)
333 return -1;
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400334 return ps2_command(0, command, param);
335}
336
337int
338ps2_mouse_command(int command, u8 *param)
339{
Kevin O'Connore216ce82012-01-29 12:25:46 -0500340 if (! CONFIG_PS2PORT)
341 return -1;
342
Kevin O'Connor9ead6fc2011-05-07 14:19:29 -0400343 // Update ps2ctr for mouse enable/disable.
344 if (command == PSMOUSE_CMD_ENABLE || command == PSMOUSE_CMD_DISABLE) {
Kevin O'Connor3693af02012-05-13 12:28:55 -0400345 u8 ps2ctr = GET_LOW(Ps2ctr);
Kevin O'Connor9ead6fc2011-05-07 14:19:29 -0400346 if (command == PSMOUSE_CMD_ENABLE)
Kevin O'Connor5ddce092015-08-10 15:50:53 -0400347 ps2ctr = ((ps2ctr | (CONFIG_HARDWARE_IRQ ? I8042_CTR_AUXINT : 0))
Kevin O'Connorf318c072014-11-12 12:15:34 -0500348 & ~I8042_CTR_AUXDIS);
Kevin O'Connor9ead6fc2011-05-07 14:19:29 -0400349 else
350 ps2ctr = (ps2ctr | I8042_CTR_AUXDIS) & ~I8042_CTR_AUXINT;
Kevin O'Connor3693af02012-05-13 12:28:55 -0400351 SET_LOW(Ps2ctr, ps2ctr);
Kevin O'Connor9ead6fc2011-05-07 14:19:29 -0400352 }
353
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400354 return ps2_command(1, command, param);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400355}
Kevin O'Connor57877482009-12-09 21:00:41 -0500356
357
358/****************************************************************
359 * IRQ handlers
360 ****************************************************************/
361
362// INT74h : PS/2 mouse hardware interrupt
363void VISIBLE16
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400364handle_74(void)
Kevin O'Connor57877482009-12-09 21:00:41 -0500365{
366 if (! CONFIG_PS2PORT)
367 return;
368
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400369 debug_isr(DEBUG_ISR_74);
Kevin O'Connora5d84582010-03-13 20:06:34 -0500370
371 u8 v = inb(PORT_PS2_STATUS);
372 if ((v & (I8042_STR_OBF|I8042_STR_AUXDATA))
373 != (I8042_STR_OBF|I8042_STR_AUXDATA)) {
Kevin O'Connorf6282442010-03-13 21:05:12 -0500374 dprintf(1, "ps2 mouse irq but no mouse data.\n");
Kevin O'Connora5d84582010-03-13 20:06:34 -0500375 goto done;
376 }
377 v = inb(PORT_PS2_DATA);
378
Kevin O'Connor3693af02012-05-13 12:28:55 -0400379 if (!(GET_LOW(Ps2ctr) & I8042_CTR_AUXINT))
Kevin O'Connorf6282442010-03-13 21:05:12 -0500380 // Interrupts not enabled.
381 goto done;
382
Kevin O'Connora5d84582010-03-13 20:06:34 -0500383 process_mouse(v);
384
385done:
Kevin O'Connoraa7c2342013-07-14 15:07:21 -0400386 pic_eoi2();
Kevin O'Connor57877482009-12-09 21:00:41 -0500387}
388
389// INT09h : Keyboard Hardware Service Entry Point
390void VISIBLE16
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400391handle_09(void)
Kevin O'Connor57877482009-12-09 21:00:41 -0500392{
393 if (! CONFIG_PS2PORT)
394 return;
395
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400396 debug_isr(DEBUG_ISR_09);
Kevin O'Connora5d84582010-03-13 20:06:34 -0500397
398 // read key from keyboard controller
399 u8 v = inb(PORT_PS2_STATUS);
Kevin O'Connorf6282442010-03-13 21:05:12 -0500400 if (v & I8042_STR_AUXDATA) {
401 dprintf(1, "ps2 keyboard irq but found mouse data?!\n");
Kevin O'Connora5d84582010-03-13 20:06:34 -0500402 goto done;
403 }
404 v = inb(PORT_PS2_DATA);
405
Kevin O'Connor3693af02012-05-13 12:28:55 -0400406 if (!(GET_LOW(Ps2ctr) & I8042_CTR_KBDINT))
Kevin O'Connorf6282442010-03-13 21:05:12 -0500407 // Interrupts not enabled.
408 goto done;
409
Kevin O'Connora5d84582010-03-13 20:06:34 -0500410 process_key(v);
411
Kevin O'Connor90ce89f2012-03-11 20:45:56 -0400412 // Some old programs expect ISR to turn keyboard back on.
413 i8042_command(I8042_CMD_KBD_ENABLE, NULL);
414
Kevin O'Connora5d84582010-03-13 20:06:34 -0500415done:
Kevin O'Connoraa7c2342013-07-14 15:07:21 -0400416 pic_eoi1();
Kevin O'Connor57877482009-12-09 21:00:41 -0500417}
418
Kevin O'Connor5ddce092015-08-10 15:50:53 -0400419// Check for ps2 activity on machines without hardware irqs
Kevin O'Connorf318c072014-11-12 12:15:34 -0500420void
421ps2_check_event(void)
422{
Kevin O'Connor5ddce092015-08-10 15:50:53 -0400423 if (! CONFIG_PS2PORT || CONFIG_HARDWARE_IRQ)
Kevin O'Connorf318c072014-11-12 12:15:34 -0500424 return;
425 u8 ps2ctr = GET_LOW(Ps2ctr);
426 if ((ps2ctr & (I8042_CTR_KBDDIS|I8042_CTR_AUXDIS))
427 == (I8042_CTR_KBDDIS|I8042_CTR_AUXDIS))
428 return;
429 for (;;) {
430 u8 status = inb(PORT_PS2_STATUS);
431 if (!(status & I8042_STR_OBF))
432 break;
433 u8 data = inb(PORT_PS2_DATA);
434 if (status & I8042_STR_AUXDATA) {
435 if (!(ps2ctr & I8042_CTR_AUXDIS))
436 process_mouse(data);
437 } else {
438 if (!(ps2ctr & I8042_CTR_KBDDIS))
439 process_key(data);
440 }
441 }
442}
443
Kevin O'Connor57877482009-12-09 21:00:41 -0500444
445/****************************************************************
446 * Setup
447 ****************************************************************/
448
449static void
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500450ps2_keyboard_setup(void *data)
Kevin O'Connor57877482009-12-09 21:00:41 -0500451{
Kevin O'Connordbf9dd22017-02-11 12:13:03 -0500452 // flush incoming keys (also verifies port is likely present)
Kevin O'Connor57877482009-12-09 21:00:41 -0500453 int ret = i8042_flush();
454 if (ret)
455 return;
456
Kevin O'Connordbf9dd22017-02-11 12:13:03 -0500457 // Disable keyboard / mouse and drain any input they may have sent
458 ret = i8042_command(I8042_CMD_KBD_DISABLE, NULL);
459 if (ret)
460 return;
461 ret = i8042_command(I8042_CMD_AUX_DISABLE, NULL);
462 if (ret)
463 return;
464 ret = i8042_flush();
465 if (ret)
466 return;
467
Kevin O'Connor57877482009-12-09 21:00:41 -0500468 // Controller self-test.
469 u8 param[2];
470 ret = i8042_command(I8042_CMD_CTL_TEST, param);
471 if (ret)
472 return;
473 if (param[0] != 0x55) {
474 dprintf(1, "i8042 self test failed (got %x not 0x55)\n", param[0]);
475 return;
476 }
477
478 // Controller keyboard test.
479 ret = i8042_command(I8042_CMD_KBD_TEST, param);
480 if (ret)
481 return;
482 if (param[0] != 0x00) {
483 dprintf(1, "i8042 keyboard test failed (got %x not 0x00)\n", param[0]);
484 return;
485 }
486
Kevin O'Connor57877482009-12-09 21:00:41 -0500487
488 /* ------------------- keyboard side ------------------------*/
489 /* reset keyboard and self test (keyboard side) */
Kevin O'Connoraafe4422011-07-05 20:44:00 -0400490 int spinupdelay = romfile_loadint("etc/ps2-keyboard-spinup", 0);
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400491 u32 end = timer_calc(spinupdelay);
Kevin O'Connor299dc132011-05-28 11:00:28 -0400492 for (;;) {
493 ret = ps2_kbd_command(ATKBD_CMD_RESET_BAT, param);
494 if (!ret)
495 break;
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400496 if (timer_check(end)) {
Kevin O'Connoraafe4422011-07-05 20:44:00 -0400497 if (spinupdelay)
Kevin O'Connor299dc132011-05-28 11:00:28 -0400498 warn_timeout();
499 return;
500 }
501 yield();
502 }
Kevin O'Connor57877482009-12-09 21:00:41 -0500503 if (param[0] != 0xaa) {
504 dprintf(1, "keyboard self test failed (got %x not 0xaa)\n", param[0]);
505 return;
506 }
507
Kevin O'Connor7e9db042020-05-21 13:32:22 -0400508 /* Disable keyboard */
509 ret = ps2_kbd_command(ATKBD_CMD_RESET_DIS, NULL);
510 if (ret)
511 return;
512
Kevin O'Connor57877482009-12-09 21:00:41 -0500513 // Set scancode command (mode 2)
514 param[0] = 0x02;
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400515 ret = ps2_kbd_command(ATKBD_CMD_SSCANSET, param);
Kevin O'Connor57877482009-12-09 21:00:41 -0500516 if (ret)
517 return;
518
Kevin O'Connorf6282442010-03-13 21:05:12 -0500519 // Keyboard Mode: disable mouse, scan code convert, enable kbd IRQ
Kevin O'Connorf318c072014-11-12 12:15:34 -0500520 Ps2ctr = (I8042_CTR_AUXDIS | I8042_CTR_XLATE
Kevin O'Connor5ddce092015-08-10 15:50:53 -0400521 | (CONFIG_HARDWARE_IRQ ? I8042_CTR_KBDINT : 0));
Kevin O'Connor57877482009-12-09 21:00:41 -0500522
523 /* Enable keyboard */
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400524 ret = ps2_kbd_command(ATKBD_CMD_ENABLE, NULL);
Kevin O'Connor57877482009-12-09 21:00:41 -0500525 if (ret)
526 return;
527
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500528 dprintf(1, "PS2 keyboard initialized\n");
Kevin O'Connor57877482009-12-09 21:00:41 -0500529}
530
531void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500532ps2port_setup(void)
Kevin O'Connor57877482009-12-09 21:00:41 -0500533{
Kevin O'Connor59c75742010-02-13 18:49:24 -0500534 ASSERT32FLAT();
Kevin O'Connor57877482009-12-09 21:00:41 -0500535 if (! CONFIG_PS2PORT)
536 return;
Gerd Hoffmannea038212020-03-31 14:51:52 +0200537 if (acpi_dsdt_present_eisaid(0x0303) == 0) {
538 dprintf(1, "ACPI: no PS/2 keyboard present\n");
539 return;
540 }
Kevin O'Connor57877482009-12-09 21:00:41 -0500541 dprintf(3, "init ps2port\n");
542
Kevin O'Connor5ddce092015-08-10 15:50:53 -0400543 enable_hwirq(1, FUNC16(entry_09));
544 enable_hwirq(12, FUNC16(entry_74));
Kevin O'Connor57877482009-12-09 21:00:41 -0500545
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500546 run_thread(ps2_keyboard_setup, NULL);
Kevin O'Connor57877482009-12-09 21:00:41 -0500547}