summaryrefslogtreecommitdiff
path: root/i386/pc/i16/i16_a20.c
blob: 5e91f86c44868b9e95effeb95bd74345e3340880 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/* 
 * Copyright (c) 1995-1994 The University of Utah and
 * the Computer Systems Laboratory at the University of Utah (CSL).
 * All rights reserved.
 *
 * Permission to use, copy, modify and distribute this software is hereby
 * granted provided that (1) source code retains these copyright, permission,
 * and disclaimer notices, and (2) redistributions including binaries
 * reproduce the notices in supporting documentation, and (3) all advertising
 * materials mentioning features or use of this software display the following
 * acknowledgement: ``This product includes software developed by the
 * Computer Systems Laboratory at the University of Utah.''
 *
 * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
 * IS" CONDITION.  THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
 * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * CSL requests users of this software to return to csl-dist@cs.utah.edu any
 * improvements that they make and grant CSL redistribution rights.
 *
 *      Author: Bryan Ford, University of Utah CSL
 */


#include <mach/machine/pio.h>
#include <mach/machine/code16.h>

#include "i16_a20.h"


/* Keyboard stuff for turning on the A20 address line (gak!).  */
#define K_RDWR 		0x60		/* keyboard data & cmds (read/write) */
#define K_STATUS 	0x64		/* keyboard status (read-only) */
#define K_CMD	 	0x64		/* keybd ctlr command (write-only) */

#define K_OBUF_FUL 	0x01		/* output buffer full */
#define K_IBUF_FUL 	0x02		/* input buffer full */

#define KC_CMD_WIN	0xd0		/* read  output port */
#define KC_CMD_WOUT	0xd1		/* write output port */

#define KB_ENABLE_A20	0xdf	/* Linux and my BIOS uses this,
				   and I trust them more than Mach 3.0,
				   but I'd like to know what the difference is
				   and if it matters.  */
			/*0x9f*/	/* enable A20,
					   enable output buffer full interrupt
					   enable data line
					   disable clock line */
#define KB_DISABLE_A20	0xdd


CODE16


/*
   This routine ensures that the keyboard command queue is empty
   (after emptying the output buffers)

   No timeout is used - if this hangs there is something wrong with
   the machine, and we probably couldn't proceed anyway.
   XXX should at least die properly
*/
static void i16_empty_8042(void)
{
	int status;

retry:
	i16_nanodelay(1000);
	status = i16_inb(K_STATUS);

	if (status & K_OBUF_FUL)
	{
		i16_nanodelay(1000);
		i16_inb(K_RDWR);
		goto retry;
	}

	if (status & K_IBUF_FUL)
		goto retry;
}

int i16_raw_test_a20(void);

/* Enable the A20 address line.  */
void i16_raw_enable_a20(void)
{
	int v;

	/* XXX try int 15h function 24h */

	if (i16_raw_test_a20())
		return;

	/* PS/2 */
	v = i16_inb(0x92);
	i16_nanodelay(1000);
	i16_outb(0x92,v | 2);

	if (i16_raw_test_a20())
		return;

	/* AT */
	i16_empty_8042();
	i16_outb(K_CMD, KC_CMD_WOUT);
	i16_empty_8042();
	i16_outb(K_RDWR, KB_ENABLE_A20);
	i16_empty_8042();

	/* Wait until the a20 line gets enabled.  */
	while (!i16_raw_test_a20());
}

/* Disable the A20 address line.  */
void i16_raw_disable_a20(void)
{
	int v;

	if (!i16_raw_test_a20())
		return;

	/* PS/2 */
	v = i16_inb(0x92);
	i16_nanodelay(1000);
	i16_outb(0x92, v & ~2);

	if (!i16_raw_test_a20())
		return;

	/* AT */
	i16_empty_8042();
	i16_outb(K_CMD, KC_CMD_WOUT);
	i16_empty_8042();
	i16_outb(K_RDWR, KB_DISABLE_A20);
	i16_empty_8042();

	/* Wait until the a20 line gets disabled.  */
	while (i16_raw_test_a20());
}


void (*i16_enable_a20)(void) = i16_raw_enable_a20;
void (*i16_disable_a20)(void) = i16_raw_disable_a20;