]> 4ch.mooo.com Git - 16.git/blob - src/lib/doslib/hw/pci/pci.c
added a bunch of things~ and midi stuff~
[16.git] / src / lib / doslib / hw / pci / pci.c
1
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. */
10
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
13  * home desktop PCs)
14  *
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. */
19
20 #include <stdio.h>
21 #include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <assert.h>
25 #include <fcntl.h>
26 #include <dos.h>
27
28 #include <hw/pci/pci.h>
29 #include <hw/cpu/cpu.h>
30
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;
38
39 /* external assembly language functions */
40 int                             try_pci_bios2();
41 int                             try_pci_bios1();
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);
45 #endif
46
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)));
56 }
57
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));
61         outp(0xCFA,bus);
62 }
63
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);
66         switch (size) {
67                 case 2: return inpd(0xCFC);
68                 case 1: return inpw(0xCFC+(reg&3));
69                 case 0: return inp(0xCFC+(reg&3));
70         }
71
72         return ~0UL;
73 }
74
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);
79         switch (size) {
80                 case 2: return inpd(pt);
81                 case 1: return inpw(pt);
82                 case 0: return inp(pt);
83         }
84
85         return ~0UL;
86 }
87
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};
90         union REGS regs;
91         
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,
98                         /* DI=reg */
99                         reg);
100                 return ~0UL;
101         }
102 #endif
103
104         regs.w.ax = 0xB108 + size;
105         regs.w.bx = (bus << 8) | (card << 3) | func;
106         regs.w.di = reg;
107 #if TARGET_MSDOS == 32
108         int386(0x1A,&regs,&regs);
109 #else
110         int86(0x1A,&regs,&regs);
111 #endif
112         if (regs.w.cflag & 1) /* carry flag set on error */
113                 return ~0UL;
114         if (regs.h.ah != 0x00) /* AH=0x00 if success */
115                 return ~0UL;
116
117 #if TARGET_MSDOS == 32
118         return regs.x.ecx & msks[size];
119 #else
120         return regs.w.cx & msks[size];
121 #endif
122 }
123
124 uint32_t pci_read_cfg_NOTIMPL(uint8_t bus,uint8_t card,uint8_t func,uint8_t reg,uint8_t size) {
125         return ~0UL;
126 }
127
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);
130         switch (size) {
131                 case 2: outpd(0xCFC,data);
132                 case 1: outpw(0xCFC+(reg&3),data);
133                 case 0: outp(0xCFC+(reg&3),data);
134         }
135 }
136
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);
141         switch (size) {
142                 case 2: outpd(pt,data);
143                 case 1: outpw(pt,data);
144                 case 0: outp(pt,data);
145         }
146 }
147
148 void pci_write_cfg_BIOS(uint8_t bus,uint8_t card,uint8_t func,uint8_t reg,uint32_t data,uint8_t size) {
149         union REGS regs;
150         
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,
157                         /* DI=reg */
158                         reg,
159                         /* data */
160                         data);
161                 return;
162         }
163 #endif
164
165         regs.w.ax = 0xB10B + size;
166         regs.w.bx = (bus << 8) | (card << 3) | func;
167         regs.w.di = reg;
168 #if TARGET_MSDOS == 32
169         regs.x.ecx = data;
170         int386(0x1A,&regs,&regs);
171 #else
172         regs.w.cx = (uint16_t)data;
173         int86(0x1A,&regs,&regs);
174 #endif
175 }
176
177 void pci_write_cfg_NOTIMPL(uint8_t bus,uint8_t card,uint8_t func,uint8_t reg,uint32_t data,uint8_t size) {
178 }
179
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
185          * times */
186         uint32_t id[16],bar[16],sub[16];
187         uint8_t bus;
188
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);
193         }
194
195         /* check for cheap PCI busses that don't decode the BUS field of the PCI configuration register */
196         bus = 3;
197         pci_bus_decode_bits = bus + 1;
198         do {
199                 uint8_t pm = 1 << bus;
200                 if (id[pm] == id[0] && bar[pm] == bar[0] && sub[pm] == sub[0]) {
201                         uint8_t ii;
202
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;
207                         }
208                 }
209                 else {
210                         /* not a match, so it's probably not wise to check further */
211                         break;
212                 }
213         } while (bus-- != 0);
214
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;
218 }
219
220 int try_pci_type2() {
221         int ret = 0;
222         outp(0xCF8,0);
223         outp(0xCFA,0);
224         if (inp(0xCF8) == 0 && inp(0xCFA) == 0) ret = 1;
225         return ret;
226 }
227
228 int try_pci_type1() {
229         int ret = 0;
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 */
233         outpd(0xCF8,tmp);
234         return ret;
235 }
236
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) {
239         if (pci_cfg_probed)
240                 return pci_cfg;
241
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 */
246 #endif
247
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;
254
255         switch (preference) {
256                 case PCI_CFG_TYPE1:
257                         if (try_pci_type1()) pci_cfg = PCI_CFG_TYPE1;
258                         break;
259                 case PCI_CFG_BIOS:
260                         if (try_pci_bios2()) pci_cfg = PCI_CFG_BIOS;
261                         break;
262                 case PCI_CFG_TYPE2:
263                         if (try_pci_type2()) pci_cfg = PCI_CFG_TYPE2;
264                         break;
265                 case PCI_CFG_BIOS1:
266                         if (try_pci_bios1()) pci_cfg = PCI_CFG_BIOS1;
267                         break;
268                 default:
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;
271                         }
272                         if (pci_cfg == PCI_CFG_NONE) {
273                                 if (try_pci_bios2()) pci_cfg = PCI_CFG_BIOS;
274                         }
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;
277                         }
278                         if (pci_cfg == PCI_CFG_NONE) {
279                                 if (try_pci_bios1()) pci_cfg = PCI_CFG_BIOS1;
280                         }
281                         break;
282         }
283
284         pci_cfg_probed = 1;
285         return pci_cfg;
286 }
287
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 */
294 };
295
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 */
302 };
303
304 uint8_t pci_probe_device_functions(uint8_t bus,uint8_t dev) {
305         uint8_t funcs=8,f;
306         uint32_t id[8],bar[8],sub[8];
307
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. */
310
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;
316         }
317
318         while (funcs > 1) {
319                 f = funcs >> 1;
320                 if (id[0] == id[f] && bar[0] == bar[f] && sub[0] == sub[f])
321                         funcs = f;
322                 else
323                         break;
324         }
325
326         return funcs;
327 }
328