diff options
author | H. Peter Anvin <hpa@linux.intel.com> | 2009-02-02 17:52:00 -0500 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2009-02-02 17:52:00 -0500 |
commit | 3bd323a1da42525317e2ce6c93b97b5ba653bc9d (patch) | |
tree | 8139baab8ed22cba923b73cca095f12706a3935b /arch/x86/boot/a20.c | |
parent | 042cbaf88ab48e11afb725541e3c2cbf5b483680 (diff) |
x86 setup: a20: early timeout for a nonexistent keyboard controller
When probing the keyboard controller to enable A20, if we get FF back
(which is *possible* as a valid status word, but is extremely
unlikely) then bail after much fewer iterations than we otherwise
would, and abort the attempt to access the KBC.
This hopefully should make it work a lot better for embedded platforms
which don't have a KBC and where the BIOS doesn't implement
INT 15h AX=2401h (and doesn't boot with A20 already enabled.)
If this works, it will be the one remaining use of CONFIG_X86_ELAN as
anything other than a processor type optimization option.
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Diffstat (limited to 'arch/x86/boot/a20.c')
-rw-r--r-- | arch/x86/boot/a20.c | 75 |
1 files changed, 41 insertions, 34 deletions
diff --git a/arch/x86/boot/a20.c b/arch/x86/boot/a20.c index 4063d630deff..fba8e9c6a504 100644 --- a/arch/x86/boot/a20.c +++ b/arch/x86/boot/a20.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * | 2 | * |
3 | * Copyright (C) 1991, 1992 Linus Torvalds | 3 | * Copyright (C) 1991, 1992 Linus Torvalds |
4 | * Copyright 2007-2008 rPath, Inc. - All Rights Reserved | 4 | * Copyright 2007-2008 rPath, Inc. - All Rights Reserved |
5 | * Copyright 2009 Intel Corporation | ||
5 | * | 6 | * |
6 | * This file is part of the Linux kernel, and is made available under | 7 | * This file is part of the Linux kernel, and is made available under |
7 | * the terms of the GNU General Public License version 2. | 8 | * the terms of the GNU General Public License version 2. |
@@ -15,16 +16,23 @@ | |||
15 | #include "boot.h" | 16 | #include "boot.h" |
16 | 17 | ||
17 | #define MAX_8042_LOOPS 100000 | 18 | #define MAX_8042_LOOPS 100000 |
19 | #define MAX_8042_FF 32 | ||
18 | 20 | ||
19 | static int empty_8042(void) | 21 | static int empty_8042(void) |
20 | { | 22 | { |
21 | u8 status; | 23 | u8 status; |
22 | int loops = MAX_8042_LOOPS; | 24 | int loops = MAX_8042_LOOPS; |
25 | int ffs = MAX_8042_FF; | ||
23 | 26 | ||
24 | while (loops--) { | 27 | while (loops--) { |
25 | io_delay(); | 28 | io_delay(); |
26 | 29 | ||
27 | status = inb(0x64); | 30 | status = inb(0x64); |
31 | if (status == 0xff) { | ||
32 | /* FF is a plausible, but very unlikely status */ | ||
33 | if (!--ffs) | ||
34 | return -1; /* Assume no KBC present */ | ||
35 | } | ||
28 | if (status & 1) { | 36 | if (status & 1) { |
29 | /* Read and discard input data */ | 37 | /* Read and discard input data */ |
30 | io_delay(); | 38 | io_delay(); |
@@ -118,44 +126,43 @@ static void enable_a20_fast(void) | |||
118 | 126 | ||
119 | int enable_a20(void) | 127 | int enable_a20(void) |
120 | { | 128 | { |
121 | #if defined(CONFIG_X86_ELAN) | 129 | #ifdef CONFIG_X86_VOYAGER |
122 | /* Elan croaks if we try to touch the KBC */ | ||
123 | enable_a20_fast(); | ||
124 | while (!a20_test_long()) | ||
125 | ; | ||
126 | return 0; | ||
127 | #elif defined(CONFIG_X86_VOYAGER) | ||
128 | /* On Voyager, a20_test() is unsafe? */ | 130 | /* On Voyager, a20_test() is unsafe? */ |
129 | enable_a20_kbc(); | 131 | enable_a20_kbc(); |
130 | return 0; | 132 | return 0; |
131 | #else | 133 | #else |
132 | int loops = A20_ENABLE_LOOPS; | 134 | int loops = A20_ENABLE_LOOPS; |
133 | while (loops--) { | 135 | int kbc_err; |
134 | /* First, check to see if A20 is already enabled | 136 | |
135 | (legacy free, etc.) */ | 137 | while (loops--) { |
136 | if (a20_test_short()) | 138 | /* First, check to see if A20 is already enabled |
137 | return 0; | 139 | (legacy free, etc.) */ |
138 | 140 | if (a20_test_short()) | |
139 | /* Next, try the BIOS (INT 0x15, AX=0x2401) */ | 141 | return 0; |
140 | enable_a20_bios(); | 142 | |
141 | if (a20_test_short()) | 143 | /* Next, try the BIOS (INT 0x15, AX=0x2401) */ |
142 | return 0; | 144 | enable_a20_bios(); |
143 | 145 | if (a20_test_short()) | |
144 | /* Try enabling A20 through the keyboard controller */ | 146 | return 0; |
145 | empty_8042(); | 147 | |
146 | if (a20_test_short()) | 148 | /* Try enabling A20 through the keyboard controller */ |
147 | return 0; /* BIOS worked, but with delayed reaction */ | 149 | kbc_err = empty_8042(); |
148 | 150 | ||
149 | enable_a20_kbc(); | 151 | if (a20_test_short()) |
150 | if (a20_test_long()) | 152 | return 0; /* BIOS worked, but with delayed reaction */ |
151 | return 0; | 153 | |
152 | 154 | if (!kbc_err) { | |
153 | /* Finally, try enabling the "fast A20 gate" */ | 155 | enable_a20_kbc(); |
154 | enable_a20_fast(); | 156 | if (a20_test_long()) |
155 | if (a20_test_long()) | 157 | return 0; |
156 | return 0; | 158 | } |
157 | } | 159 | |
158 | 160 | /* Finally, try enabling the "fast A20 gate" */ | |
159 | return -1; | 161 | enable_a20_fast(); |
162 | if (a20_test_long()) | ||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | return -1; | ||
160 | #endif | 167 | #endif |
161 | } | 168 | } |