Ported bnx2 driver from Etherboot 5.4.
[gpxe.git] / src / arch / i386 / firmware / pcbios / gateA20.c
1 #include "realmode.h"
2 #include "timer.h"
3 #include "latch.h"
4 #include "bios.h"
5
6 #define K_RDWR          0x60            /* keyboard data & cmds (read/write) */
7 #define K_STATUS        0x64            /* keyboard status */
8 #define K_CMD           0x64            /* keybd ctlr command (write-only) */
9
10 #define K_OBUF_FUL      0x01            /* output buffer full */
11 #define K_IBUF_FUL      0x02            /* input buffer full */
12
13 #define KC_CMD_WIN      0xd0            /* read  output port */
14 #define KC_CMD_WOUT     0xd1            /* write output port */
15 #define KB_SET_A20      0xdf            /* enable A20,
16                                            enable output buffer full interrupt
17                                            enable data line
18                                            disable clock line */
19 #define KB_UNSET_A20    0xdd            /* enable A20,
20                                            enable output buffer full interrupt
21                                            enable data line
22                                            disable clock line */
23
24 enum { Disable_A20 = 0x2400, Enable_A20 = 0x2401, Query_A20_Status = 0x2402,
25         Query_A20_Support = 0x2403 };
26
27 #define CF ( 1 << 0 )
28
29 #ifndef IBM_L40
30 static void empty_8042 ( void ) {
31         unsigned long time;
32
33         time = currticks() + TICKS_PER_SEC;     /* max wait of 1 second */
34         while ( ( inb ( K_CMD ) & K_IBUF_FUL ) &&
35                 currticks() < time ) {
36                 /* Do nothing.  In particular, do *not* read from
37                  * K_RDWR, because that will drain the keyboard buffer
38                  * and lose keypresses.
39                  */
40         }
41 }
42 #endif  /* IBM_L40 */
43
44 /*
45  * Gate A20 for high memory
46  *
47  * Note that this function gets called as part of the return path from
48  * librm's real_call, which is used to make the int15 call if librm is
49  * being used.  To avoid an infinite recursion, we make gateA20_set
50  * return immediately if it is already part of the call stack.
51  */
52 void gateA20_set ( void ) {
53         static char reentry_guard = 0;
54         unsigned int discard_a;
55         unsigned int flags;
56
57         if ( reentry_guard )
58                 return;
59         reentry_guard = 1;
60
61         __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
62                                            "stc\n\t"
63                                            "int $0x15\n\t"
64                                            "pushfw\n\t"
65                                            "popw %w0\n\t"
66                                            "cli\n\t" )
67                                : "=r" ( flags ), "=a" ( discard_a )
68                                : "a" ( Enable_A20 ) );
69
70
71         if ( flags & CF ) {
72                 /* INT 15 method failed, try alternatives */
73 #ifdef  IBM_L40
74                 outb(0x2, 0x92);
75 #else   /* IBM_L40 */
76                 empty_8042();
77                 outb(KC_CMD_WOUT, K_CMD);
78                 empty_8042();
79                 outb(KB_SET_A20, K_RDWR);
80                 empty_8042();
81 #endif  /* IBM_L40 */
82         }
83
84         reentry_guard = 0;
85 }
86
87 void gateA20_unset ( void ) {
88         /* Not currently implemented */
89 }