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