1 |
dpavlin |
20 |
/* |
2 |
dpavlin |
34 |
* Copyright (C) 2005-2007 Anders Gavare. All rights reserved. |
3 |
dpavlin |
20 |
* |
4 |
|
|
* Redistribution and use in source and binary forms, with or without |
5 |
|
|
* modification, are permitted provided that the following conditions are met: |
6 |
|
|
* |
7 |
|
|
* 1. Redistributions of source code must retain the above copyright |
8 |
|
|
* notice, this list of conditions and the following disclaimer. |
9 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
10 |
|
|
* notice, this list of conditions and the following disclaimer in the |
11 |
|
|
* documentation and/or other materials provided with the distribution. |
12 |
|
|
* 3. The name of the author may not be used to endorse or promote products |
13 |
|
|
* derived from this software without specific prior written permission. |
14 |
|
|
* |
15 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
16 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
19 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
21 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
22 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
23 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
24 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
25 |
|
|
* SUCH DAMAGE. |
26 |
|
|
* |
27 |
|
|
* |
28 |
dpavlin |
44 |
* $Id: bus_isa.c,v 1.20 2007/08/29 20:36:49 debug Exp $ |
29 |
dpavlin |
20 |
* |
30 |
dpavlin |
42 |
* COMMENT: Generic ISA bus framework |
31 |
|
|
* |
32 |
|
|
* This is not a normal device, but it can be used as a quick way of adding |
33 |
|
|
* most of the common legacy ISA devices to a machine. |
34 |
dpavlin |
20 |
*/ |
35 |
|
|
|
36 |
|
|
#include <stdio.h> |
37 |
|
|
#include <stdlib.h> |
38 |
|
|
#include <string.h> |
39 |
|
|
|
40 |
dpavlin |
22 |
#define BUS_ISA_C |
41 |
|
|
|
42 |
dpavlin |
20 |
#include "bus_isa.h" |
43 |
|
|
#include "device.h" |
44 |
|
|
#include "devices.h" |
45 |
|
|
#include "diskimage.h" |
46 |
dpavlin |
34 |
#include "interrupt.h" |
47 |
dpavlin |
20 |
#include "machine.h" |
48 |
|
|
#include "misc.h" |
49 |
|
|
|
50 |
|
|
|
51 |
|
|
/* |
52 |
dpavlin |
34 |
* isa_interrupt_common(): |
53 |
dpavlin |
22 |
*/ |
54 |
dpavlin |
34 |
void isa_interrupt_common(struct bus_isa_data *d, int old_isa_assert) |
55 |
dpavlin |
22 |
{ |
56 |
dpavlin |
34 |
int new_isa_assert, x; |
57 |
dpavlin |
22 |
|
58 |
dpavlin |
34 |
/* Any interrupt assertions on PIC2 go to irq 2 on PIC1 */ |
59 |
|
|
/* (TODO: don't hardcode this here) */ |
60 |
|
|
if (d->pic2->irr & ~d->pic2->ier) |
61 |
|
|
d->pic1->irr |= 0x04; |
62 |
|
|
else |
63 |
|
|
d->pic1->irr &= ~0x04; |
64 |
|
|
|
65 |
|
|
/* printf("ISA: irr=%02x%02x ier=%02x%02x\n", |
66 |
|
|
d->pic2->irr, d->pic1->irr, d->pic2->ier, d->pic1->ier); */ |
67 |
|
|
|
68 |
|
|
new_isa_assert = d->pic1->irr & ~d->pic1->ier; |
69 |
|
|
|
70 |
|
|
if (old_isa_assert == new_isa_assert) |
71 |
|
|
return; |
72 |
|
|
|
73 |
|
|
if (!new_isa_assert) { |
74 |
|
|
INTERRUPT_DEASSERT(d->irq); |
75 |
|
|
return; |
76 |
|
|
} |
77 |
|
|
|
78 |
|
|
for (x=0; x<16; x++) { |
79 |
|
|
if (x == 2) |
80 |
|
|
continue; |
81 |
|
|
|
82 |
|
|
if (x < 8 && (d->pic1->irr & ~d->pic1->ier & (1 << x))) |
83 |
|
|
break; |
84 |
|
|
|
85 |
|
|
if (x >= 8 && (d->pic2->irr & ~d->pic2->ier & (1 << (x&7)))) |
86 |
|
|
break; |
87 |
|
|
} |
88 |
|
|
|
89 |
|
|
*d->ptr_to_last_int = x; |
90 |
|
|
|
91 |
|
|
INTERRUPT_ASSERT(d->irq); |
92 |
dpavlin |
22 |
} |
93 |
|
|
|
94 |
|
|
|
95 |
|
|
/* |
96 |
dpavlin |
34 |
* isa_interrupt_assert(): |
97 |
|
|
* |
98 |
|
|
* Called whenever an ISA device asserts an interrupt (0..15). |
99 |
|
|
*/ |
100 |
|
|
void isa_interrupt_assert(struct interrupt *interrupt) |
101 |
|
|
{ |
102 |
|
|
struct bus_isa_data *d = interrupt->extra; |
103 |
|
|
int old_isa_assert, line = interrupt->line; |
104 |
|
|
int mask = 1 << (line & 7); |
105 |
|
|
|
106 |
|
|
old_isa_assert = d->pic1->irr & ~d->pic1->ier; |
107 |
|
|
|
108 |
|
|
if (line < 8) |
109 |
|
|
d->pic1->irr |= mask; |
110 |
|
|
else if (d->pic2 != NULL) |
111 |
|
|
d->pic2->irr |= mask; |
112 |
|
|
|
113 |
|
|
isa_interrupt_common(d, old_isa_assert); |
114 |
|
|
} |
115 |
|
|
|
116 |
|
|
|
117 |
|
|
/* |
118 |
|
|
* isa_interrupt_deassert(): |
119 |
|
|
* |
120 |
|
|
* Called whenever an ISA device deasserts an interrupt (0..15). |
121 |
|
|
*/ |
122 |
|
|
void isa_interrupt_deassert(struct interrupt *interrupt) |
123 |
|
|
{ |
124 |
|
|
struct bus_isa_data *d = interrupt->extra; |
125 |
|
|
int line = interrupt->line, mask = 1 << (line & 7); |
126 |
|
|
int old_irr1 = d->pic1->irr, old_isa_assert; |
127 |
|
|
|
128 |
|
|
old_isa_assert = old_irr1 & ~d->pic1->ier; |
129 |
|
|
|
130 |
|
|
if (line < 8) |
131 |
|
|
d->pic1->irr &= ~mask; |
132 |
|
|
else if (d->pic2 != NULL) |
133 |
|
|
d->pic2->irr &= ~mask; |
134 |
|
|
|
135 |
|
|
/* If IRQ 0 has been cleared, then this is a timer interrupt. |
136 |
|
|
Let's ack it here: */ |
137 |
|
|
if (old_irr1 & 1 && !(d->pic1->irr & 1) && |
138 |
|
|
d->ptr_to_pending_timer_interrupts != NULL && |
139 |
|
|
(*d->ptr_to_pending_timer_interrupts) > 0) |
140 |
|
|
(*d->ptr_to_pending_timer_interrupts) --; |
141 |
|
|
|
142 |
|
|
isa_interrupt_common(d, old_isa_assert); |
143 |
|
|
} |
144 |
|
|
|
145 |
|
|
|
146 |
|
|
/* |
147 |
dpavlin |
22 |
* bus_isa_init(): |
148 |
dpavlin |
20 |
* |
149 |
|
|
* Flags are zero or more of the following, ORed together: |
150 |
|
|
* |
151 |
dpavlin |
34 |
* BUS_ISA_EXTERNAL_PIC Don't register/use isa_interrupt_*(). |
152 |
dpavlin |
20 |
* BUS_ISA_IDE0 Include wdc0. |
153 |
|
|
* BUS_ISA_IDE1 Include wdc1. |
154 |
|
|
* BUS_ISA_FDC Include a floppy controller. (Dummy.) |
155 |
|
|
* BUS_ISA_VGA Include old-style (non-PCI) VGA. (*1) |
156 |
|
|
* BUS_ISA_VGA_FORCE Include VGA even when running without X11. (*2) |
157 |
|
|
* BUS_ISA_PCKBC_FORCE_USE Always assume keyboard console, not serial. (*3) |
158 |
|
|
* BUS_ISA_PCKBC_NONPCSTYLE Don't set the pc-style flag for the keyboard. |
159 |
|
|
* BUS_ISA_NO_SECOND_PIC Only useful for 8086 XT (pre-AT) emulation. :-) |
160 |
dpavlin |
22 |
* BUS_ISA_LPTBASE_3BC Set lptbase to 0x3bc instead of 0x378. |
161 |
dpavlin |
20 |
* |
162 |
|
|
* (*1) For machines with a PCI bus, this flag should not be used. Instead, a |
163 |
|
|
* PCI VGA card should be added to the PCI bus. |
164 |
|
|
* |
165 |
|
|
* (*2) For machines where it is easy to select VGA vs serial console during |
166 |
|
|
* boot, this flag should not be used. Machines that "always" boot up |
167 |
|
|
* in VGA console mode should have it set. |
168 |
|
|
* |
169 |
|
|
* (*3) Similar to *2 above; machines that always boot up with VGA console |
170 |
|
|
* should have this flag set, so that the keyboard is always used. |
171 |
dpavlin |
34 |
* |
172 |
|
|
* The interrupt_base_path is the name of the bus, CPU, or controller onto |
173 |
dpavlin |
44 |
* which this ISA bus will be attached, e.g. "machine[0].lca" or |
174 |
|
|
* "machine[0].cpu[0].pic1". |
175 |
dpavlin |
20 |
*/ |
176 |
dpavlin |
22 |
struct bus_isa_data *bus_isa_init(struct machine *machine, |
177 |
dpavlin |
34 |
char *interrupt_base_path, uint32_t bus_isa_flags, |
178 |
|
|
uint64_t isa_portbase, uint64_t isa_membase) |
179 |
dpavlin |
20 |
{ |
180 |
dpavlin |
42 |
struct bus_isa_data *d; |
181 |
dpavlin |
34 |
char tmpstr[300], tmpstr2[300]; |
182 |
dpavlin |
20 |
int wdc0_irq = 14, wdc1_irq = 15; |
183 |
dpavlin |
34 |
int i, tmp_handle, kbd_in_use; |
184 |
dpavlin |
22 |
int lptbase = 0x378; |
185 |
dpavlin |
20 |
|
186 |
dpavlin |
42 |
CHECK_ALLOCATION(d = malloc(sizeof(struct bus_isa_data))); |
187 |
dpavlin |
22 |
memset(d, 0, sizeof(struct bus_isa_data)); |
188 |
dpavlin |
34 |
|
189 |
dpavlin |
22 |
d->isa_portbase = isa_portbase; |
190 |
|
|
d->isa_membase = isa_membase; |
191 |
|
|
|
192 |
dpavlin |
34 |
if (!(bus_isa_flags & BUS_ISA_EXTERNAL_PIC)) { |
193 |
|
|
/* Connect to the interrupt which we're interrupting |
194 |
|
|
at (usually a CPU): */ |
195 |
|
|
INTERRUPT_CONNECT(interrupt_base_path, d->irq); |
196 |
|
|
|
197 |
|
|
/* Register the 16 possible ISA interrupts: */ |
198 |
|
|
for (i=0; i<16; i++) { |
199 |
|
|
struct interrupt template; |
200 |
|
|
char name[300]; |
201 |
|
|
snprintf(name, sizeof(name), |
202 |
|
|
"%s.isa.%i", interrupt_base_path, i); |
203 |
|
|
memset(&template, 0, sizeof(template)); |
204 |
|
|
template.line = i; |
205 |
|
|
template.name = name; |
206 |
|
|
template.extra = d; |
207 |
|
|
template.interrupt_assert = isa_interrupt_assert; |
208 |
|
|
template.interrupt_deassert = isa_interrupt_deassert; |
209 |
|
|
interrupt_handler_register(&template); |
210 |
|
|
} |
211 |
|
|
} |
212 |
|
|
|
213 |
dpavlin |
20 |
kbd_in_use = ((bus_isa_flags & BUS_ISA_PCKBC_FORCE_USE) || |
214 |
dpavlin |
42 |
(machine->x11_md.in_use))? 1 : 0; |
215 |
dpavlin |
20 |
|
216 |
|
|
if (machine->machine_type == MACHINE_PREP) { |
217 |
|
|
/* PReP with obio controller has both WDCs on irq 13! */ |
218 |
|
|
wdc0_irq = wdc1_irq = 13; |
219 |
|
|
} |
220 |
|
|
|
221 |
dpavlin |
34 |
if (!(bus_isa_flags & BUS_ISA_EXTERNAL_PIC)) { |
222 |
|
|
snprintf(tmpstr, sizeof(tmpstr), "8259 irq=%s addr=0x%llx", |
223 |
|
|
interrupt_base_path, (long long)(isa_portbase + 0x20)); |
224 |
|
|
d->pic1 = machine->isa_pic_data.pic1 = |
225 |
|
|
device_add(machine, tmpstr); |
226 |
|
|
d->ptr_to_pending_timer_interrupts = |
227 |
|
|
machine->isa_pic_data.pending_timer_interrupts; |
228 |
|
|
d->ptr_to_last_int = &machine->isa_pic_data.last_int; |
229 |
dpavlin |
20 |
|
230 |
dpavlin |
34 |
if (bus_isa_flags & BUS_ISA_NO_SECOND_PIC) |
231 |
|
|
bus_isa_flags &= ~BUS_ISA_NO_SECOND_PIC; |
232 |
|
|
else { |
233 |
|
|
snprintf(tmpstr, sizeof(tmpstr), |
234 |
|
|
"8259 irq=%s.isa.2 addr=0x%llx", |
235 |
|
|
interrupt_base_path,(long long)(isa_portbase+0xa0)); |
236 |
|
|
d->pic2 = machine->isa_pic_data.pic2 = |
237 |
|
|
device_add(machine, tmpstr); |
238 |
|
|
} |
239 |
|
|
} else { |
240 |
|
|
bus_isa_flags &= ~BUS_ISA_EXTERNAL_PIC; |
241 |
dpavlin |
20 |
} |
242 |
|
|
|
243 |
dpavlin |
34 |
snprintf(tmpstr, sizeof(tmpstr), "8253 irq=%s.isa.%i addr=0x%llx " |
244 |
|
|
"in_use=0", interrupt_base_path, 0, |
245 |
|
|
(long long)(isa_portbase + 0x40)); |
246 |
dpavlin |
20 |
device_add(machine, tmpstr); |
247 |
|
|
|
248 |
dpavlin |
34 |
snprintf(tmpstr, sizeof(tmpstr), "pccmos irq=%s.isa.%i addr=0x%llx", |
249 |
|
|
interrupt_base_path, 8, (long long)(isa_portbase + 0x70)); |
250 |
dpavlin |
20 |
device_add(machine, tmpstr); |
251 |
|
|
|
252 |
dpavlin |
34 |
snprintf(tmpstr, sizeof(tmpstr), "ns16550 irq=%s.isa.%i addr=0x%llx " |
253 |
|
|
"name2=tty0 in_use=%i", interrupt_base_path, 4, |
254 |
|
|
(long long)(isa_portbase + 0x3f8), 1 - kbd_in_use); |
255 |
dpavlin |
20 |
machine->main_console_handle = (size_t)device_add(machine, tmpstr); |
256 |
|
|
|
257 |
dpavlin |
34 |
snprintf(tmpstr, sizeof(tmpstr), "ns16550 irq=%s.isa.%i addr=0x%llx " |
258 |
|
|
"name2=tty1 in_use=0", interrupt_base_path, 3, |
259 |
|
|
(long long)(isa_portbase + 0x2f8)); |
260 |
dpavlin |
20 |
device_add(machine, tmpstr); |
261 |
|
|
|
262 |
dpavlin |
22 |
if (bus_isa_flags & BUS_ISA_LPTBASE_3BC) { |
263 |
|
|
bus_isa_flags &= ~BUS_ISA_LPTBASE_3BC; |
264 |
|
|
lptbase = 0x3bc; |
265 |
|
|
} |
266 |
|
|
|
267 |
dpavlin |
34 |
snprintf(tmpstr, sizeof(tmpstr), "lpt irq=%s.isa.%i addr=0x%llx " |
268 |
|
|
"name2=lpt in_use=0", interrupt_base_path, 7, |
269 |
|
|
(long long)(isa_portbase + lptbase)); |
270 |
dpavlin |
20 |
device_add(machine, tmpstr); |
271 |
|
|
|
272 |
|
|
if (bus_isa_flags & BUS_ISA_IDE0) { |
273 |
|
|
bus_isa_flags &= ~BUS_ISA_IDE0; |
274 |
dpavlin |
34 |
snprintf(tmpstr, sizeof(tmpstr), "wdc irq=%s.isa.%i " |
275 |
|
|
"addr=0x%llx", interrupt_base_path, wdc0_irq, |
276 |
|
|
(long long)(isa_portbase + 0x1f0)); |
277 |
dpavlin |
20 |
if (diskimage_exist(machine, 0, DISKIMAGE_IDE) || |
278 |
|
|
diskimage_exist(machine, 1, DISKIMAGE_IDE)) |
279 |
|
|
device_add(machine, tmpstr); |
280 |
|
|
} |
281 |
|
|
|
282 |
|
|
if (bus_isa_flags & BUS_ISA_IDE1) { |
283 |
|
|
bus_isa_flags &= ~BUS_ISA_IDE1; |
284 |
dpavlin |
34 |
snprintf(tmpstr, sizeof(tmpstr), "wdc irq=%s.isa.%i " |
285 |
|
|
"addr=0x%llx", interrupt_base_path, wdc1_irq, |
286 |
|
|
(long long)(isa_portbase + 0x170)); |
287 |
dpavlin |
20 |
if (diskimage_exist(machine, 2, DISKIMAGE_IDE) || |
288 |
|
|
diskimage_exist(machine, 3, DISKIMAGE_IDE)) |
289 |
|
|
device_add(machine, tmpstr); |
290 |
|
|
} |
291 |
|
|
|
292 |
|
|
if (bus_isa_flags & BUS_ISA_FDC) { |
293 |
|
|
bus_isa_flags &= ~BUS_ISA_FDC; |
294 |
dpavlin |
34 |
snprintf(tmpstr, sizeof(tmpstr), "fdc irq=%s.isa.%i " |
295 |
|
|
"addr=0x%llx", interrupt_base_path, 6, |
296 |
|
|
(long long)(isa_portbase + 0x3f0)); |
297 |
dpavlin |
20 |
device_add(machine, tmpstr); |
298 |
|
|
} |
299 |
|
|
|
300 |
|
|
if (bus_isa_flags & BUS_ISA_VGA) { |
301 |
dpavlin |
42 |
if (machine->x11_md.in_use || bus_isa_flags & BUS_ISA_VGA_FORCE) |
302 |
dpavlin |
20 |
dev_vga_init(machine, machine->memory, |
303 |
|
|
isa_membase + 0xa0000, isa_portbase + 0x3c0, |
304 |
|
|
machine->machine_name); |
305 |
|
|
bus_isa_flags &= ~(BUS_ISA_VGA | BUS_ISA_VGA_FORCE); |
306 |
|
|
} |
307 |
|
|
|
308 |
dpavlin |
34 |
snprintf(tmpstr, sizeof(tmpstr), "%s.isa.1", interrupt_base_path); |
309 |
|
|
snprintf(tmpstr2, sizeof(tmpstr2), "%s.isa.12", interrupt_base_path); |
310 |
dpavlin |
20 |
tmp_handle = dev_pckbc_init(machine, machine->memory, |
311 |
dpavlin |
34 |
isa_portbase + 0x60, PCKBC_8042, tmpstr, tmpstr2, |
312 |
dpavlin |
20 |
kbd_in_use, bus_isa_flags & BUS_ISA_PCKBC_NONPCSTYLE? 0 : 1); |
313 |
|
|
|
314 |
|
|
if (kbd_in_use) |
315 |
|
|
machine->main_console_handle = tmp_handle; |
316 |
|
|
|
317 |
|
|
bus_isa_flags &= ~(BUS_ISA_PCKBC_NONPCSTYLE | BUS_ISA_PCKBC_FORCE_USE); |
318 |
|
|
|
319 |
|
|
if (bus_isa_flags != 0) |
320 |
|
|
fatal("WARNING! bus_isa(): unimplemented bus_isa_flags 0x%x\n", |
321 |
|
|
bus_isa_flags); |
322 |
dpavlin |
22 |
|
323 |
|
|
return d; |
324 |
dpavlin |
20 |
} |
325 |
|
|
|