1 /* $NetBSD: pcibios.c,v 1.35 2007/12/25 18:33:33 perry Exp $ */
4 * Copyright (c) 1999 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9 * NASA Ames Research Center.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
34 * Copyright (c) 1999, by UCHIYAMA Yasushi
35 * All rights reserved.
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
40 * 1. Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * 2. The name of the developer may NOT be used to endorse or promote products
43 * derived from this software without specific prior written permission.
45 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 * Interface to the PCI BIOS and PCI Interrupt Routing table.
62 #include <sys/cdefs.h>
63 __KERNEL_RCSID(0, "$NetBSD: pcibios.c,v 1.35 2007/12/25 18:33:33 perry Exp $");
65 #include "opt_pcibios.h"
66 #include "opt_pcifixup.h"
68 #include <sys/param.h>
69 #include <sys/systm.h>
70 #include <sys/device.h>
71 #include <sys/malloc.h>
73 #include <dev/isa/isareg.h>
74 #include <machine/isa_machdep.h>
76 #include <dev/pci/pcireg.h>
77 #include <dev/pci/pcivar.h>
78 #include <dev/pci/pcidevs.h>
80 #include <i386/pci/pcibios.h>
82 #if defined(PCIBIOS_INTR_FIXUP) || defined(PCIBIOS_ADDR_FIXUP) || \
83 defined(PCIBIOS_BUS_FIXUP)
84 #error The options PCIBIOS_INTR_FIXUP, PCIBIOS_ADDR_FIXUP, and PCIBIOS_BUS_FIXUP have been obsoleted by PCI_INTR_FIXUP, PCI_ADDR_FIXUP, and PCI_BUS_FIXUP. Please adjust your kernel configuration file.
88 #include <i386/pci/pci_intr_fixup.h>
91 #include <machine/bios32.h>
94 int pcibiosverbose
= 1;
99 struct pcibios_pir_header pcibios_pir_header
;
100 struct pcibios_intr_routing
*pcibios_pir_table
;
101 int pcibios_pir_table_nentries
;
104 struct bios32_entry pcibios_entry
;
106 void pcibios_pir_init(void);
108 int pcibios_get_status(uint32_t *, uint32_t *, uint32_t *,
109 uint32_t *, uint32_t *, uint32_t *, uint32_t *);
110 int pcibios_get_intr_routing(struct pcibios_intr_routing
*,
113 int pcibios_return_code(uint16_t, const char *);
115 void pcibios_print_exclirq(void);
117 #ifdef PCIBIOS_LIBRETTO_FIXUP
118 /* for Libretto L2/L3 hack */
119 static void pcibios_fixup_pir_table(void);
120 static void pcibios_fixup_pir_table_mask(struct pcibios_linkmap
*);
122 struct pcibios_linkmap pir_mask
[] = {
130 #ifdef PCIBIOS_SHARP_MM20_FIXUP
131 static void pcibios_mm20_fixup(void);
135 void pcibios_print_pir_table(void);
138 #define PCI_IRQ_TABLE_START 0xf0000
139 #define PCI_IRQ_TABLE_END 0xfffff
144 struct bios32_entry_info ei
;
145 uint32_t rev_maj
, rev_min
, mech1
, mech2
, scmech1
, scmech2
;
147 if (bios32_service(BIOS32_MAKESIG('$', 'P', 'C', 'I'),
148 &pcibios_entry
, &ei
) == 0) {
150 * No PCI BIOS found; will fall back on old
157 * We've located the PCI BIOS service; get some information
160 if (pcibios_get_status(&rev_maj
, &rev_min
, &mech1
, &mech2
,
161 &scmech1
, &scmech2
, &pcibios_max_bus
) != PCIBIOS_SUCCESS
) {
163 * We can't use the PCI BIOS; will fall back on old
169 aprint_normal("PCI BIOS rev. %d.%d found at 0x%lx\n",
170 rev_maj
, rev_min
>> 4, ei
.bei_entry
);
171 aprint_verbose("pcibios: config mechanism %s%s, special cycles %s%s, "
173 mech1
? "[1]" : "[x]",
174 mech2
? "[2]" : "[x]",
175 scmech1
? "[1]" : "[x]",
176 scmech2
? "[2]" : "[x]",
180 * The PCI BIOS tells us the config mechanism; fill it in now
181 * so that pci_mode_detect() doesn't have to look for it.
183 pci_mode
= mech1
? 1 : 2;
188 * Find the PCI IRQ Routing table.
192 #ifdef PCI_INTR_FIXUP
193 if (pcibios_pir_table
!= NULL
) {
198 * Fixup interrupt routing.
200 rv
= pci_intr_fixup(NULL
, X86_BUS_SPACE_IO
, &pciirq
);
203 /* Non-fatal error. */
204 aprint_error("Warning: unable to fix up PCI interrupt "
210 panic("pcibios_init: interrupt fixup failed");
215 * XXX Clear `pciirq' from the ISA interrupt allocation
223 pcibios_pir_init(void)
230 uint8_t rev_maj
, rev_min
;
233 for (pa
= PCI_IRQ_TABLE_START
; pa
< PCI_IRQ_TABLE_END
; pa
+= 16) {
234 p
= (void *)ISA_HOLE_VADDR(pa
);
235 if (*(int *)p
!= BIOS32_MAKESIG('$', 'P', 'I', 'R')) {
237 * XXX: Some laptops (Toshiba/Libretto L series)
238 * use _PIR instead of $PIR. So we try that too.
240 if (*(int *)p
!= BIOS32_MAKESIG('_', 'P', 'I', 'R'))
246 tablesize
= *(uint16_t *)(p
+ 6);
249 for (i
= 0; i
< tablesize
; i
++)
250 cksum
+= *(unsigned char *)(p
+ i
);
253 "PCI IRQ Routing Table rev. %d.%d found at 0x%lx, "
254 "size %d bytes (%d entries)\n", rev_maj
, rev_min
, pa
,
255 tablesize
, (tablesize
- 32) / 16);
258 aprint_error("pcibios_pir_init: bad IRQ table checksum\n");
262 if (tablesize
< 32 || (tablesize
% 16) != 0) {
263 aprint_error("pcibios_pir_init: bad IRQ table size\n");
267 if (rev_maj
!= 1 || rev_min
!= 0) {
268 aprint_error("pcibios_pir_init: unsupported IRQ table "
274 * We can handle this table! Make a copy of it.
276 memcpy(&pcibios_pir_header
, p
, 32);
277 pcibios_pir_table
= malloc(tablesize
- 32, M_DEVBUF
,
279 if (pcibios_pir_table
== NULL
) {
280 aprint_error("pcibios_pir_init: no memory for $PIR\n");
283 memcpy(pcibios_pir_table
, p
+ 32, tablesize
- 32);
284 pcibios_pir_table_nentries
= (tablesize
- 32) / 16;
286 aprint_verbose("PCI Interrupt Router at %03d:%02d:%01d",
287 pcibios_pir_header
.router_bus
,
288 PIR_DEVFUNC_DEVICE(pcibios_pir_header
.router_devfunc
),
289 PIR_DEVFUNC_FUNCTION(pcibios_pir_header
.router_devfunc
));
290 if (pcibios_pir_header
.compat_router
!= 0) {
291 devinfo
= malloc(256, M_DEVBUF
, M_NOWAIT
);
293 pci_devinfo(pcibios_pir_header
.compat_router
,
295 aprint_verbose(" (%s compatible)", devinfo
);
296 free(devinfo
, M_DEVBUF
);
299 aprint_verbose("\n");
300 pcibios_print_exclirq();
302 #ifdef PCIBIOS_LIBRETTO_FIXUP
303 /* for Libretto L2/L3 hack */
304 pcibios_fixup_pir_table();
306 #ifdef PCIBIOS_SHARP_MM20_FIXUP
307 pcibios_mm20_fixup();
310 pcibios_print_pir_table();
316 * If there was no PIR table found, try using the PCI BIOS
317 * Get Interrupt Routing call.
319 * XXX The interface to this call sucks; just allocate enough
320 * XXX room for 32 entries.
322 pcibios_pir_table_nentries
= 32;
323 pcibios_pir_table
= malloc(pcibios_pir_table_nentries
*
324 sizeof(*pcibios_pir_table
), M_DEVBUF
, M_NOWAIT
);
325 if (pcibios_pir_table
== NULL
) {
326 aprint_error("pcibios_pir_init: no memory for $PIR\n");
329 if (pcibios_get_intr_routing(pcibios_pir_table
,
330 &pcibios_pir_table_nentries
,
331 &pcibios_pir_header
.exclusive_irq
) != PCIBIOS_SUCCESS
) {
332 aprint_normal("No PCI IRQ Routing information available.\n");
333 free(pcibios_pir_table
, M_DEVBUF
);
334 pcibios_pir_table
= NULL
;
335 pcibios_pir_table_nentries
= 0;
338 aprint_verbose("PCI BIOS has %d Interrupt Routing table entries\n",
339 pcibios_pir_table_nentries
);
340 pcibios_print_exclirq();
342 #ifdef PCIBIOS_LIBRETTO_FIXUP
343 /* for Libretto L2/L3 hack */
344 pcibios_fixup_pir_table();
346 #ifdef PCIBIOS_SHARP_MM20_FIXUP
347 pcibios_mm20_fixup();
350 pcibios_print_pir_table();
355 pcibios_get_status(uint32_t *rev_maj
, uint32_t *rev_min
,
356 uint32_t *mech1
, uint32_t *mech2
, uint32_t *scmech1
, uint32_t *scmech2
,
363 __asm
volatile("lcall *(%%edi) ; \
367 : "=a" (ax
), "=b" (bx
), "=c" (cx
), "=d" (edx
)
368 : "0" (0xb101), "D" (&pcibios_entry
));
370 rv
= pcibios_return_code(ax
, "pcibios_get_status");
371 if (rv
!= PCIBIOS_SUCCESS
)
374 if (edx
!= BIOS32_MAKESIG('P', 'C', 'I', ' '))
375 return (PCIBIOS_SERVICE_NOT_PRESENT
); /* XXX */
378 * Fill in the various pieces if info we're looking for.
381 *mech2
= ax
& (1 << 1);
382 *scmech1
= ax
& (1 << 4);
383 *scmech2
= ax
& (1 << 5);
384 *rev_maj
= (bx
>> 8) & 0xff;
385 *rev_min
= bx
& 0xff;
388 return (PCIBIOS_SUCCESS
);
392 pcibios_get_intr_routing(struct pcibios_intr_routing
*table
,
393 int *nentries
, uint16_t *exclirq
)
403 args
.size
= *nentries
* sizeof(*table
);
404 args
.offset
= (void *)table
;
405 args
.segment
= GSEL(GDATA_SEL
, SEL_KPL
);
407 memset(table
, 0, args
.size
);
409 __asm
volatile("lcall *(%%esi) ; \
412 1: movw %w2, %%ds ; \
414 : "=a" (ax
), "=b" (bx
)
415 : "r" GSEL(GDATA_SEL
, SEL_KPL
), "0" (0xb10e), "1" (0),
416 "D" (&args
), "S" (&pcibios_entry
));
418 rv
= pcibios_return_code(ax
, "pcibios_get_intr_routing");
419 if (rv
!= PCIBIOS_SUCCESS
)
422 *nentries
= args
.size
/ sizeof(*table
);
425 return (PCIBIOS_SUCCESS
);
429 pcibios_return_code(uint16_t ax
, const char *func
)
435 case PCIBIOS_SUCCESS
:
436 return (PCIBIOS_SUCCESS
);
438 case PCIBIOS_SERVICE_NOT_PRESENT
:
439 errstr
= "service not present";
442 case PCIBIOS_FUNCTION_NOT_SUPPORTED
:
443 errstr
= "function not supported";
446 case PCIBIOS_BAD_VENDOR_ID
:
447 errstr
= "bad vendor ID";
450 case PCIBIOS_DEVICE_NOT_FOUND
:
451 errstr
= "device not found";
454 case PCIBIOS_BAD_REGISTER_NUMBER
:
455 errstr
= "bad register number";
458 case PCIBIOS_SET_FAILED
:
459 errstr
= "set failed";
462 case PCIBIOS_BUFFER_TOO_SMALL
:
463 errstr
= "buffer too small";
467 aprint_error("%s: unknown return code 0x%x\n", func
, rv
);
471 aprint_error("%s: %s\n", func
, errstr
);
476 pcibios_print_exclirq(void)
480 if (pcibios_pir_header
.exclusive_irq
) {
481 aprint_verbose("PCI Exclusive IRQs:");
482 for (i
= 0; i
< 16; i
++) {
483 if (pcibios_pir_header
.exclusive_irq
& (1 << i
))
484 aprint_verbose(" %d", i
);
486 aprint_verbose("\n");
490 #ifdef PCIBIOS_LIBRETTO_FIXUP
491 /* for Libretto L2/L3 hack */
493 pcibios_fixup_pir_table(void)
495 struct pcibios_linkmap
*m
;
497 for (m
= pir_mask
; m
->link
!= 0; m
++)
498 pcibios_fixup_pir_table_mask(m
);
502 pcibios_fixup_pir_table_mask(struct pcibios_linkmap
*mask
)
506 for (i
= 0; i
< pcibios_pir_table_nentries
; i
++) {
507 for (j
= 0; j
< 4; j
++) {
508 if (pcibios_pir_table
[i
].linkmap
[j
].link
== mask
->link
) {
509 pcibios_pir_table
[i
].linkmap
[j
].bitmap
519 pcibios_print_pir_table(void)
523 for (i
= 0; i
< pcibios_pir_table_nentries
; i
++) {
524 printf("PIR Entry %d:\n", i
);
525 printf("\tBus: %d Device: %d\n",
526 pcibios_pir_table
[i
].bus
,
527 PIR_DEVFUNC_DEVICE(pcibios_pir_table
[i
].device
));
528 for (j
= 0; j
< 4; j
++) {
529 printf("\t\tINT%c: link 0x%02x bitmap 0x%04x\n",
531 pcibios_pir_table
[i
].linkmap
[j
].link
,
532 pcibios_pir_table
[i
].linkmap
[j
].bitmap
);
538 #ifdef PCIBIOS_SHARP_MM20_FIXUP
540 * This is a gross hack to get the interrupt from the EHCI controller
541 * working on a Sharp MM20. The BIOS is just incredibly buggy.
543 * The story thus far:
544 * The modern way to route the interrupt is to use ACPI. But using
545 * ACPI fails with an error message about an uninitialized local
546 * variable in the AML code. (It works in Windows, but fails in NetBSD
549 * The second attempt is to use PCI Interrupt Routing table. But this
550 * fails because the table does not contain any information about the
551 * interrupt from the EHCI controller. This is probably due to the fact
552 * that the table is compatible with ALi M1543, but the MM20 has an ALi M1563.
553 * The M1563 has additional interrupt lines. The ali1543.c code also
554 * cannot handle the M1653's extended interrupts. And fixing this is
555 * difficult since getting a data sheet from ALi requires signing an NDA.
557 * The third attempt is to use a BIOS call to route the interrupt
558 * (as FreeBSD does) with manually generated information. But the BIOS call
559 * fails because the BIOS code is not quite position independent. It makes
560 * some assumption about where the code segment register points.
562 * So the solution is to use the third attempt, but with a patched version
564 * -- lennart@augustsson.net
567 #define BIOS32_START 0xe0000
568 #define BIOS32_SIZE 0x20000
570 static char pcibios_shadow
[BIOS32_SIZE
];
571 static struct bios32_entry pcibios_entry_shadow
;
574 * Copy BIOS and zap offending instruction.
575 * The bad instruction is
576 * mov %cs:0x63c(%ebx),%ah
577 * NetBSD does not have the code segment set up for this to work.
578 * Using the value 0xff for the table entry seems to work.
580 * mov $0xff,%ah; nop; nop; nop; nop; nop
583 pcibios_copy_bios(void)
587 memcpy(pcibios_shadow
, ISA_HOLE_VADDR(BIOS32_START
), BIOS32_SIZE
);
588 pcibios_entry_shadow
= pcibios_entry
;
589 pcibios_entry_shadow
.offset
=
590 (void*)((u_long
)pcibios_shadow
+
591 (u_long
)pcibios_entry
.offset
-
592 (u_long
)ISA_HOLE_VADDR(BIOS32_START
));
594 bad_instr
= (uint8_t *)pcibios_entry_shadow
.offset
+ 0x499;
595 if (*bad_instr
!= 0x2e)
597 bad_instr
[0] = 0xb4; bad_instr
[1] = 0xff; /* mov $0xff,%ah */
598 bad_instr
[2] = 0x90; /* nop */
599 bad_instr
[3] = 0x90; /* nop */
600 bad_instr
[4] = 0x90; /* nop */
601 bad_instr
[5] = 0x90; /* nop */
602 bad_instr
[6] = 0x90; /* nop */
606 * Call BIOS to route an interrupt.
607 * The PCI device is identified by bus,device,func.
608 * The interrupt is on pin PIN (A-D) and interrupt IRQ.
609 * BIOS knows the magic for the interrupt controller.
612 pcibios_biosroute(int bus
, int device
, int func
, int pin
, int irq
)
617 aprint_debug("pcibios_biosroute: b,d,f=%d,%d,%d pin=%x irq=%d\n",
618 bus
, device
, func
, pin
+0xa, irq
);
620 bx
= (bus
<< 8) | (device
<< 3) | func
;
621 cx
= (irq
<< 8) | (0xa + pin
);
623 __asm
volatile("lcall *(%%esi) ; \
626 1: movw %w1, %%ds ; \
629 : "r" GSEL(GDATA_SEL
, SEL_KPL
), "0" (0xb10f),
631 "S" (&pcibios_entry_shadow
));
633 rv
= pcibios_return_code(ax
, "pcibios_biosroute");
638 #define MM20_PCI_BUS 0
639 #define MM20_PCI_EHCI_DEV 15
640 #define MM20_PCI_EHCI_FUNC 3
641 #define MM20_PCI_EHCI_PIN 3
642 #define MM20_PCI_EHCI_INTR 11
643 #define MM20_PCI_ISA_DEV 3
644 #define MM20_PCI_ISA_FUNC 0
647 pcibios_mm20_fixup(void)
649 pci_chipset_tag_t pc
;
654 /* Route the interrupt for the EHCI controller. */
655 (void)pcibios_biosroute(MM20_PCI_BUS
,
661 /* Fake some tags. */
663 tag
= pci_make_tag(pc
, MM20_PCI_BUS
, MM20_PCI_EHCI_DEV
,
665 /* Set interrupt register in EHCI controller */
666 pci_conf_write(pc
, tag
, PCI_INTERRUPT_REG
,
667 0x50000400 + MM20_PCI_EHCI_INTR
);
668 tag
= pci_make_tag(pc
, MM20_PCI_BUS
, MM20_PCI_ISA_DEV
,
670 /* Set some unknown registers in the ISA bridge. */
671 pci_conf_write(pc
, tag
, 0x58, 0xd87f5300);
672 pci_conf_write(pc
, tag
, 0x74, 0x00000009);
675 #endif /* PCIBIOS_SHARP_MM20_FIXUP */