2 /* NTS: It's not required in the DOS environment, but in the unlikely event that
3 * interrupt handlers are mucking around with PCI configuration space you
4 * may want to bracket your PCI operations with CLI/STI. Again for performance
5 * reasons, this code will NOT do it for you */
6 /* NTS: Additional research suggests that on legacy systems with 10-bit decoding
7 * I/O ports 0xCF8-0xCFF are unlikely to collide with 287/387 legacy I/O
8 * ports 0xF0-0xF7 (as I initially thought), therefore probing 0xCF8 on
9 * ancient systems should do no harm and not cause any problems. */
11 /* This library enables programming the PCI bus that may be available to DOS level
12 * programs on Pentium and higher systems (where PCI slots were first common on
15 * This code is designed to compile 16-bit and 32-bit versions. The 16-bit real
16 * mode versions are designed with DOS programs in mind that might want the ability
17 * to fiddle with the PCI bus and program registers while maintaining downlevel
18 * compatability with older hardware, perhaps all the way down to the 8086. */
21 #include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
28 #include <hw/pci/pci.h>
29 #include <hw/cpu/cpu.h>
31 unsigned char pci_cfg_probed = 0;
32 unsigned char pci_cfg = PCI_CFG_NONE;
33 uint32_t pci_bios_protmode_entry_point = 0;
34 uint8_t pci_bios_hw_characteristics = 0;
35 uint16_t pci_bios_interface_level = 0;
36 uint8_t pci_bus_decode_bits = 0; /* which bus bits the hardware actually bothers to compare against */
37 int16_t pci_bios_last_bus = 0;
39 /* external assembly language functions */
42 #if TARGET_MSDOS == 16
43 uint32_t __cdecl pci_bios_read_dword_16(uint16_t bx,uint16_t di);
44 void __cdecl pci_bios_write_dword_16(uint16_t bx,uint16_t di,uint32_t data);
47 /* NTS: Programming experience tells me that depite what this bitfield arrangement
48 * suggests, most PCI devices ignore bits 0-1 of the register number and expect
49 * you to offset the read from the 0xCFC register instead. */
50 void pci_type1_select(uint8_t bus,uint8_t card,uint8_t func,uint8_t reg) {
51 outpd(0xCF8,0x80000000UL |
52 (((uint32_t)bus) << 16UL) |
53 (((uint32_t)card) << 11UL) |
54 (((uint32_t)func) << 8UL) |
55 ((uint32_t)(reg & 0xFC)));
58 void pci_type2_select(uint8_t bus,uint8_t func) {
59 /* FIXME: Is this right? Documentation is sparse and hard to come by... */
60 outp(0xCF8,0x80 | (func << 1));
64 uint32_t pci_read_cfg_TYPE1(uint8_t bus,uint8_t card,uint8_t func,uint8_t reg,uint8_t size) {
65 pci_type1_select(bus,card,func,reg);
67 case 2: return inpd(0xCFC);
68 case 1: return inpw(0xCFC+(reg&3));
69 case 0: return inp(0xCFC+(reg&3));
75 /* WARNING: I have no hardware to verify this works */
76 uint32_t pci_read_cfg_TYPE2(uint8_t bus,uint8_t card,uint8_t func,uint8_t reg,uint8_t size) {
77 const uint16_t pt = 0xC000 + (card << 8) + reg;
78 pci_type2_select(bus,func);
80 case 2: return inpd(pt);
81 case 1: return inpw(pt);
82 case 0: return inp(pt);
88 uint32_t pci_read_cfg_BIOS(uint8_t bus,uint8_t card,uint8_t func,uint8_t reg,uint8_t size) {
89 static const uint32_t msks[3] = {0xFFUL,0xFFFFUL,0xFFFFFFFFUL};
92 if (size > 2) return ~0UL;
93 #if TARGET_MSDOS == 16
94 if (size == 2) { /* 32-bit DWORD read in real mode when Watcom won't let me use 386 style registers or int386() */
95 return pci_bios_read_dword_16(
96 /* BH=bus BL(7-3)=card BL(2-0)=func */
97 (bus << 8) | (card << 3) | func,
104 regs.w.ax = 0xB108 + size;
105 regs.w.bx = (bus << 8) | (card << 3) | func;
107 #if TARGET_MSDOS == 32
108 int386(0x1A,®s,®s);
110 int86(0x1A,®s,®s);
112 if (regs.w.cflag & 1) /* carry flag set on error */
114 if (regs.h.ah != 0x00) /* AH=0x00 if success */
117 #if TARGET_MSDOS == 32
118 return regs.x.ecx & msks[size];
120 return regs.w.cx & msks[size];
124 uint32_t pci_read_cfg_NOTIMPL(uint8_t bus,uint8_t card,uint8_t func,uint8_t reg,uint8_t size) {
128 void pci_write_cfg_TYPE1(uint8_t bus,uint8_t card,uint8_t func,uint8_t reg,uint32_t data,uint8_t size) {
129 pci_type1_select(bus,card,func,reg);
131 case 2: outpd(0xCFC,data);
132 case 1: outpw(0xCFC+(reg&3),data);
133 case 0: outp(0xCFC+(reg&3),data);
137 /* WARNING: I have no hardware to verify this works */
138 void pci_write_cfg_TYPE2(uint8_t bus,uint8_t card,uint8_t func,uint8_t reg,uint32_t data,uint8_t size) {
139 const uint16_t pt = 0xC000 + (card << 8) + reg;
140 pci_type2_select(bus,func);
142 case 2: outpd(pt,data);
143 case 1: outpw(pt,data);
144 case 0: outp(pt,data);
148 void pci_write_cfg_BIOS(uint8_t bus,uint8_t card,uint8_t func,uint8_t reg,uint32_t data,uint8_t size) {
151 if (size > 2) return;
152 #if TARGET_MSDOS == 16
153 if (size == 2) { /* 32-bit DWORD read in real mode when Watcom won't let me use 386 style registers or int386() */
154 pci_bios_write_dword_16(
155 /* BH=bus BL(7-3)=card BL(2-0)=func */
156 (bus << 8) | (card << 3) | func,
165 regs.w.ax = 0xB10B + size;
166 regs.w.bx = (bus << 8) | (card << 3) | func;
168 #if TARGET_MSDOS == 32
170 int386(0x1A,®s,®s);
172 regs.w.cx = (uint16_t)data;
173 int86(0x1A,®s,®s);
177 void pci_write_cfg_NOTIMPL(uint8_t bus,uint8_t card,uint8_t func,uint8_t reg,uint32_t data,uint8_t size) {
180 void pci_probe_for_last_bus() {
181 /* scan backwards until we find a root PCI device that doesn't return 0xFFFF for ID.
182 * but also keep track of the IDs because some PCI busses (especially ancient Pentium
183 * based laptops) don't bother decoding the bus field. If we're not careful, we could
184 * erroneously come up with 16 busses attached, each having the same device list 16
186 uint32_t id[16],bar[16],sub[16];
189 for (bus=0;bus < 16;bus++) {
190 id[bus] = pci_read_cfgl(bus,0,0,0x00);
191 bar[bus] = pci_read_cfgl(bus,0,0,0x10);
192 sub[bus] = pci_read_cfgl(bus,0,0,0x2C);
195 /* check for cheap PCI busses that don't decode the BUS field of the PCI configuration register */
197 pci_bus_decode_bits = bus + 1;
199 uint8_t pm = 1 << bus;
200 if (id[pm] == id[0] && bar[pm] == bar[0] && sub[pm] == sub[0]) {
203 /* looks like a mirror image to me. rub it out */
204 pci_bus_decode_bits = bus;
205 for (ii=pm;ii < (2 << bus);ii++) {
206 id[ii] = bar[ii] = sub[ii] = ~0UL;
210 /* not a match, so it's probably not wise to check further */
213 } while (bus-- != 0);
215 /* now check for the last working root device, and mark that */
216 for (bus=0xF;bus != 0 && id[bus] == ~0UL;) bus--;
217 pci_bios_last_bus = bus;
220 int try_pci_type2() {
224 if (inp(0xCF8) == 0 && inp(0xCFA) == 0) ret = 1;
228 int try_pci_type1() {
230 uint32_t tmp = inpd(0xCF8);
231 outpd(0xCF8,0x80000000);
232 if (inpd(0xCF8) == 0x80000000 && inpd(0xCFC) != 0xFFFFFFFFUL) ret=1; /* might be type 2 reflecting written data */
237 /* WARNING: Uses 32-bit I/O. The caller is expected to have first called the CPU library to determine we are a 386 or higher */
238 int pci_probe(int preference) {
242 #if TARGET_MSDOS == 16
243 /* This code won't even run on pre-386 if compiled 32-bit, so this check is limited only to 16-bit real mode builds that might be run on such hardware */
244 if (cpu_basic_level < 3) /* NTS: I'm aware this variable is initialized to ~0 (255) first */
245 return PCI_CFG_NONE; /* if the programmer is not checking the CPU he either doesn't care about pre-386 systems or he is stupid, so go ahead and do it anyway */
248 pci_bios_protmode_entry_point = 0;
249 pci_bios_hw_characteristics = 0;
250 pci_bios_interface_level = 0;
251 pci_cfg = PCI_CFG_NONE;
252 pci_bios_last_bus = -1; /* -1 means "I don't know" */
253 pci_bus_decode_bits = 4;
255 switch (preference) {
257 if (try_pci_type1()) pci_cfg = PCI_CFG_TYPE1;
260 if (try_pci_bios2()) pci_cfg = PCI_CFG_BIOS;
263 if (try_pci_type2()) pci_cfg = PCI_CFG_TYPE2;
266 if (try_pci_bios1()) pci_cfg = PCI_CFG_BIOS1;
269 if (pci_cfg == PCI_CFG_NONE) { /* Type 1? This is most common, and widely supported */
270 if (try_pci_type1()) pci_cfg = PCI_CFG_TYPE1;
272 if (pci_cfg == PCI_CFG_NONE) {
273 if (try_pci_bios2()) pci_cfg = PCI_CFG_BIOS;
275 if (pci_cfg == PCI_CFG_NONE) { /* Type 2? This is rare, I have no hardware that supports this... */
276 if (try_pci_type2()) pci_cfg = PCI_CFG_TYPE2;
278 if (pci_cfg == PCI_CFG_NONE) {
279 if (try_pci_bios1()) pci_cfg = PCI_CFG_BIOS1;
288 uint32_t (*pci_read_cfg_array[PCI_CFG_MAX])(uint8_t bus,uint8_t card,uint8_t func,uint8_t reg,uint8_t size) = {
289 pci_read_cfg_NOTIMPL, /* NONE */
290 pci_read_cfg_TYPE1, /* TYPE1 */
291 pci_read_cfg_BIOS, /* BIOS */
292 pci_read_cfg_NOTIMPL, /* BIOS1 */
293 pci_read_cfg_TYPE2 /* TYPE2 */
296 void (*pci_write_cfg_array[PCI_CFG_MAX])(uint8_t bus,uint8_t card,uint8_t func,uint8_t reg,uint32_t data,uint8_t size) = {
297 pci_write_cfg_NOTIMPL, /* NONE */
298 pci_write_cfg_TYPE1, /* TYPE1 */
299 pci_write_cfg_BIOS, /* BIOS */
300 pci_write_cfg_NOTIMPL, /* BIOS1 */
301 pci_write_cfg_TYPE2 /* TYPE2 */
304 uint8_t pci_probe_device_functions(uint8_t bus,uint8_t dev) {
306 uint32_t id[8],bar[8],sub[8];
308 /* some laptop PCI chipsets, believe it or not, decode bus and device numbers but ignore the function index.
309 * if we're not careful, we could end up with the misconception that there were 8 of each PCI device present. */
311 for (f=0;f < 8;f++) {
312 id[f] = pci_read_cfgl(bus,dev,f,0x00);
313 bar[f] = pci_read_cfgl(bus,dev,f,0x10);
314 sub[f] = pci_read_cfgl(bus,dev,f,0x2C);
315 if (f == 0 && id[f] == 0xFFFFFFFFUL) return 0;
320 if (id[0] == id[f] && bar[0] == bar[f] && sub[0] == sub[f])