]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/i386/pci/pci_pir.c
This commit was generated by cvs2svn to compensate for changes in r67957,
[FreeBSD/FreeBSD.git] / sys / i386 / pci / pci_pir.c
1 /*
2  * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
3  * Copyright (c) 2000, Michael Smith <msmith@freebsd.org>
4  * Copyright (c) 2000, BSDi
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice unmodified, this list of conditions, and the following
12  *    disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  *
30  */
31
32 #include <sys/param.h>          /* XXX trim includes */
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/kernel.h>
36 #include <sys/module.h>
37 #include <sys/malloc.h>
38 #include <vm/vm.h>
39 #include <vm/pmap.h>
40 #include <machine/md_var.h>
41 #include <pci/pcivar.h>
42 #include <pci/pcireg.h>
43 #include <isa/isavar.h>
44 #include <machine/nexusvar.h>
45 #include <machine/pci_cfgreg.h>
46 #include <machine/segments.h>
47 #include <machine/pc/bios.h>
48
49 #include "pcib_if.h"
50
51 static int cfgmech;
52 static int devmax;
53 static int usebios;
54
55 static int      pcibios_cfgread(int bus, int slot, int func, int reg, int bytes);
56 static void     pcibios_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
57 static int      pcibios_cfgopen(void);
58 static int      pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
59 static void     pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
60 static int      pcireg_cfgopen(void);
61
62 static struct PIR_entry *pci_route_table;
63 static int              pci_route_count;
64
65 /* 
66  * Initialise access to PCI configuration space 
67  */
68 int
69 pci_cfgregopen(void)
70 {
71     static int                  opened = 0;
72     u_long                      sigaddr;
73     static struct PIR_table     *pt;
74     u_int8_t                    ck, *cv;
75     int                         i;
76
77     if (opened)
78         return(1);
79
80     if (pcibios_cfgopen() != 0) {
81         usebios = 1;
82     } else if (pcireg_cfgopen() != 0) {
83         usebios = 0;
84     } else {
85         return(0);
86     }
87
88     /*
89      * Look for the interrupt routing table.
90      */
91     /* XXX use PCI BIOS if it's available */
92
93     if ((pt == NULL) && ((sigaddr = bios_sigsearch(0, "$PIR", 4, 16, 0)) != 0)) {
94         pt = (struct PIR_table *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
95         for (cv = (u_int8_t *)pt, ck = 0, i = 0; i < (pt->pt_header.ph_length); i++) {
96             ck += cv[i];
97         }
98         if (ck == 0) {
99             pci_route_table = &pt->pt_entry[0];
100             pci_route_count = (pt->pt_header.ph_length - sizeof(struct PIR_header)) / sizeof(struct PIR_entry);
101             printf("Using $PIR table, %d entries at %p\n", pci_route_count, pci_route_table);
102         }
103     }
104
105     opened = 1;
106     return(1);
107 }
108
109 /* 
110  * Read configuration space register 
111  */
112 u_int32_t
113 pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
114 {
115     return(usebios ? 
116            pcibios_cfgread(bus, slot, func, reg, bytes) : 
117            pcireg_cfgread(bus, slot, func, reg, bytes));
118 }
119
120 /* 
121  * Write configuration space register 
122  */
123 void
124 pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
125 {
126     return(usebios ? 
127            pcibios_cfgwrite(bus, slot, func, reg, data, bytes) : 
128            pcireg_cfgwrite(bus, slot, func, reg, data, bytes));
129 }
130
131 /*
132  * Route a PCI interrupt
133  *
134  * XXX this needs to learn to actually route uninitialised interrupts as well
135  *     as just returning interrupts for stuff that's already initialised.
136  *
137  * XXX we don't do anything "right" with the function number in the PIR table
138  *     (because the consumer isn't currently passing it in).
139  */
140 int
141 pci_cfgintr(int bus, int device, int pin)
142 {
143     struct PIR_entry    *pe;
144     int                 i, irq;
145     struct bios_regs    args;
146     
147     if ((bus < 0) || (bus > 255) || (device < 0) || (device > 255) ||
148       (pin < 1) || (pin > 4))
149         return(255);
150
151     /*
152      * Scan the entry table for a contender
153      */
154     for (i = 0, pe = pci_route_table; i < pci_route_count; i++, pe++) {
155         if ((bus != pe->pe_bus) || (device != pe->pe_device))
156             continue;
157         if (!powerof2(pe->pe_intpin[pin - 1].irqs)) {
158             printf("pci_cfgintr: %d:%d:%c is not routed to a unique interrupt\n",
159                    bus, device, 'A' + pin - 1);
160             break;
161         }
162         irq = ffs(pe->pe_intpin[pin - 1].irqs) - 1;
163         printf("pci_cfgintr: %d:%d:%c routed to irq %d\n", 
164                bus, device, 'A' + pin - 1, irq);
165
166         /*
167          * Ask the BIOS to route the interrupt
168          */
169         args.eax = PCIBIOS_ROUTE_INTERRUPT;
170         args.ebx = (bus << 8) | (device << 3);
171         args.ecx = (irq << 8) | (0xa + pin - 1);        /* pin value is 0xa - 0xd */
172         bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL));
173
174         /* XXX if it fails, we should smack the router hardware directly */
175
176         return(irq);
177     }
178     return(255);
179 }
180
181
182 /*
183  * Config space access using BIOS functions 
184  */
185 static int
186 pcibios_cfgread(int bus, int slot, int func, int reg, int bytes)
187 {
188     struct bios_regs args;
189     u_int mask;
190
191     switch(bytes) {
192     case 1:
193         args.eax = PCIBIOS_READ_CONFIG_BYTE;
194         mask = 0xff;
195         break;
196     case 2:
197         args.eax = PCIBIOS_READ_CONFIG_WORD;
198         mask = 0xffff;
199         break;
200     case 4:
201         args.eax = PCIBIOS_READ_CONFIG_DWORD;
202         mask = 0xffffffff;
203         break;
204     default:
205         return(-1);
206     }
207     args.ebx = (bus << 8) | (slot << 3) | func;
208     args.edi = reg;
209     bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL));
210     /* check call results? */
211     return(args.ecx & mask);
212 }
213
214 static void
215 pcibios_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
216 {
217     struct bios_regs args;
218
219     switch(bytes) {
220     case 1:
221         args.eax = PCIBIOS_WRITE_CONFIG_BYTE;
222         break;
223     case 2:
224         args.eax = PCIBIOS_WRITE_CONFIG_WORD;
225         break;
226     case 4:
227         args.eax = PCIBIOS_WRITE_CONFIG_DWORD;
228         break;
229     default:
230         return;
231     }
232     args.ebx = (bus << 8) | (slot << 3) | func;
233     args.ecx = data;
234     args.edi = reg;
235     bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL));
236 }
237
238 /*
239  * Determine whether there is a PCI BIOS present
240  */
241 static int
242 pcibios_cfgopen(void)
243 {
244     /* check for a found entrypoint */
245     return(PCIbios.entry != 0);
246 }
247
248 /* 
249  * Configuration space access using direct register operations
250  */
251
252 /* enable configuration space accesses and return data port address */
253 static int
254 pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
255 {
256     int dataport = 0;
257
258     if (bus <= PCI_BUSMAX
259         && slot < devmax
260         && func <= PCI_FUNCMAX
261         && reg <= PCI_REGMAX
262         && bytes != 3
263         && (unsigned) bytes <= 4
264         && (reg & (bytes -1)) == 0) {
265         switch (cfgmech) {
266         case 1:
267             outl(CONF1_ADDR_PORT, (1 << 31)
268                  | (bus << 16) | (slot << 11) 
269                  | (func << 8) | (reg & ~0x03));
270             dataport = CONF1_DATA_PORT + (reg & 0x03);
271             break;
272         case 2:
273             outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
274             outb(CONF2_FORWARD_PORT, bus);
275             dataport = 0xc000 | (slot << 8) | reg;
276             break;
277         }
278     }
279     return (dataport);
280 }
281
282 /* disable configuration space accesses */
283 static void
284 pci_cfgdisable(void)
285 {
286     switch (cfgmech) {
287     case 1:
288         outl(CONF1_ADDR_PORT, 0);
289         break;
290     case 2:
291         outb(CONF2_ENABLE_PORT, 0);
292         outb(CONF2_FORWARD_PORT, 0);
293         break;
294     }
295 }
296
297 static int
298 pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
299 {
300     int data = -1;
301     int port;
302
303     port = pci_cfgenable(bus, slot, func, reg, bytes);
304
305     if (port != 0) {
306         switch (bytes) {
307         case 1:
308             data = inb(port);
309             break;
310         case 2:
311             data = inw(port);
312             break;
313         case 4:
314             data = inl(port);
315             break;
316         }
317         pci_cfgdisable();
318     }
319     return (data);
320 }
321
322 static void
323 pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
324 {
325     int port;
326
327     port = pci_cfgenable(bus, slot, func, reg, bytes);
328     if (port != 0) {
329         switch (bytes) {
330         case 1:
331             outb(port, data);
332             break;
333         case 2:
334             outw(port, data);
335             break;
336         case 4:
337             outl(port, data);
338             break;
339         }
340         pci_cfgdisable();
341     }
342 }
343
344 /* check whether the configuration mechanism has been correctly identified */
345 static int
346 pci_cfgcheck(int maxdev)
347 {
348     u_char device;
349
350     if (bootverbose) 
351         printf("pci_cfgcheck:\tdevice ");
352
353     for (device = 0; device < maxdev; device++) {
354         unsigned id, class, header;
355         if (bootverbose) 
356             printf("%d ", device);
357
358         id = inl(pci_cfgenable(0, device, 0, 0, 4));
359         if (id == 0 || id == -1)
360             continue;
361
362         class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8;
363         if (bootverbose)
364             printf("[class=%06x] ", class);
365         if (class == 0 || (class & 0xf870ff) != 0)
366             continue;
367
368         header = inb(pci_cfgenable(0, device, 0, 14, 1));
369         if (bootverbose) 
370             printf("[hdr=%02x] ", header);
371         if ((header & 0x7e) != 0)
372             continue;
373
374         if (bootverbose)
375             printf("is there (id=%08x)\n", id);
376
377         pci_cfgdisable();
378         return (1);
379     }
380     if (bootverbose) 
381         printf("-- nothing found\n");
382
383     pci_cfgdisable();
384     return (0);
385 }
386
387 static int
388 pcireg_cfgopen(void)
389 {
390     unsigned long mode1res,oldval1;
391     unsigned char mode2res,oldval2;
392
393     oldval1 = inl(CONF1_ADDR_PORT);
394
395     if (bootverbose) {
396         printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n",
397                oldval1);
398     }
399
400     if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
401
402         cfgmech = 1;
403         devmax = 32;
404
405         outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
406         outb(CONF1_ADDR_PORT +3, 0);
407         mode1res = inl(CONF1_ADDR_PORT);
408         outl(CONF1_ADDR_PORT, oldval1);
409
410         if (bootverbose)
411             printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n", 
412                    mode1res, CONF1_ENABLE_CHK);
413
414         if (mode1res) {
415             if (pci_cfgcheck(32)) 
416                 return (cfgmech);
417         }
418
419         outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
420         mode1res = inl(CONF1_ADDR_PORT);
421         outl(CONF1_ADDR_PORT, oldval1);
422
423         if (bootverbose)
424             printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n", 
425                    mode1res, CONF1_ENABLE_CHK1);
426
427         if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
428             if (pci_cfgcheck(32)) 
429                 return (cfgmech);
430         }
431     }
432
433     oldval2 = inb(CONF2_ENABLE_PORT);
434
435     if (bootverbose) {
436         printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
437                oldval2);
438     }
439
440     if ((oldval2 & 0xf0) == 0) {
441
442         cfgmech = 2;
443         devmax = 16;
444
445         outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
446         mode2res = inb(CONF2_ENABLE_PORT);
447         outb(CONF2_ENABLE_PORT, oldval2);
448
449         if (bootverbose)
450             printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", 
451                    mode2res, CONF2_ENABLE_CHK);
452
453         if (mode2res == CONF2_ENABLE_RES) {
454             if (bootverbose)
455                 printf("pci_open(2a):\tnow trying mechanism 2\n");
456
457             if (pci_cfgcheck(16)) 
458                 return (cfgmech);
459         }
460     }
461
462     cfgmech = 0;
463     devmax = 0;
464     return (cfgmech);
465 }
466