Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Low-Level PCI Support for PC -- Routing of Interrupts | |
3 | * | |
4 | * (c) 1999--2000 Martin Mares <mj@ucw.cz> | |
5 | */ | |
6 | ||
1da177e4 LT |
7 | #include <linux/types.h> |
8 | #include <linux/kernel.h> | |
9 | #include <linux/pci.h> | |
10 | #include <linux/init.h> | |
1da177e4 | 11 | #include <linux/interrupt.h> |
1da177e4 | 12 | #include <linux/dmi.h> |
7058b061 PC |
13 | #include <linux/io.h> |
14 | #include <linux/smp.h> | |
1da177e4 | 15 | #include <asm/io_apic.h> |
b33fa1f3 | 16 | #include <linux/irq.h> |
1da177e4 | 17 | #include <linux/acpi.h> |
82487711 | 18 | #include <asm/pci_x86.h> |
1da177e4 LT |
19 | |
20 | #define PIRQ_SIGNATURE (('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24)) | |
21 | #define PIRQ_VERSION 0x0100 | |
22 | ||
23 | static int broken_hp_bios_irq9; | |
24 | static int acer_tm360_irqrouting; | |
25 | ||
26 | static struct irq_routing_table *pirq_table; | |
27 | ||
28 | static int pirq_enable_irq(struct pci_dev *dev); | |
29 | ||
30 | /* | |
31 | * Never use: 0, 1, 2 (timer, keyboard, and cascade) | |
32 | * Avoid using: 13, 14 and 15 (FP error and IDE). | |
33 | * Penalize: 3, 4, 6, 7, 12 (known ISA uses: serial, floppy, parallel and mouse) | |
34 | */ | |
35 | unsigned int pcibios_irq_mask = 0xfff8; | |
36 | ||
37 | static int pirq_penalty[16] = { | |
38 | 1000000, 1000000, 1000000, 1000, 1000, 0, 1000, 1000, | |
39 | 0, 0, 0, 0, 1000, 100000, 100000, 100000 | |
40 | }; | |
41 | ||
42 | struct irq_router { | |
43 | char *name; | |
44 | u16 vendor, device; | |
45 | int (*get)(struct pci_dev *router, struct pci_dev *dev, int pirq); | |
273c1127 MV |
46 | int (*set)(struct pci_dev *router, struct pci_dev *dev, int pirq, |
47 | int new); | |
1da177e4 LT |
48 | }; |
49 | ||
50 | struct irq_router_handler { | |
51 | u16 vendor; | |
52 | int (*probe)(struct irq_router *r, struct pci_dev *router, u16 device); | |
53 | }; | |
54 | ||
ab3b3793 | 55 | int (*pcibios_enable_irq)(struct pci_dev *dev) = pirq_enable_irq; |
87bec66b | 56 | void (*pcibios_disable_irq)(struct pci_dev *dev) = NULL; |
1da177e4 | 57 | |
120bb424 | 58 | /* |
59 | * Check passed address for the PCI IRQ Routing Table signature | |
60 | * and perform checksum verification. | |
61 | */ | |
62 | ||
7058b061 | 63 | static inline struct irq_routing_table *pirq_check_routing_table(u8 *addr) |
120bb424 | 64 | { |
65 | struct irq_routing_table *rt; | |
66 | int i; | |
67 | u8 sum; | |
68 | ||
69 | rt = (struct irq_routing_table *) addr; | |
70 | if (rt->signature != PIRQ_SIGNATURE || | |
71 | rt->version != PIRQ_VERSION || | |
72 | rt->size % 16 || | |
73 | rt->size < sizeof(struct irq_routing_table)) | |
74 | return NULL; | |
75 | sum = 0; | |
7058b061 | 76 | for (i = 0; i < rt->size; i++) |
120bb424 | 77 | sum += addr[i]; |
78 | if (!sum) { | |
273c1127 MV |
79 | DBG(KERN_DEBUG "PCI: Interrupt Routing Table found at 0x%p\n", |
80 | rt); | |
120bb424 | 81 | return rt; |
82 | } | |
83 | return NULL; | |
84 | } | |
85 | ||
86 | ||
87 | ||
1da177e4 LT |
88 | /* |
89 | * Search 0xf0000 -- 0xfffff for the PCI IRQ Routing Table. | |
90 | */ | |
91 | ||
92 | static struct irq_routing_table * __init pirq_find_routing_table(void) | |
93 | { | |
94 | u8 *addr; | |
95 | struct irq_routing_table *rt; | |
1da177e4 | 96 | |
120bb424 | 97 | if (pirq_table_addr) { |
98 | rt = pirq_check_routing_table((u8 *) __va(pirq_table_addr)); | |
99 | if (rt) | |
100 | return rt; | |
101 | printk(KERN_WARNING "PCI: PIRQ table NOT found at pirqaddr\n"); | |
102 | } | |
7058b061 | 103 | for (addr = (u8 *) __va(0xf0000); addr < (u8 *) __va(0x100000); addr += 16) { |
120bb424 | 104 | rt = pirq_check_routing_table(addr); |
105 | if (rt) | |
1da177e4 | 106 | return rt; |
1da177e4 LT |
107 | } |
108 | return NULL; | |
109 | } | |
110 | ||
111 | /* | |
112 | * If we have a IRQ routing table, use it to search for peer host | |
113 | * bridges. It's a gross hack, but since there are no other known | |
114 | * ways how to get a list of buses, we have to go this way. | |
115 | */ | |
116 | ||
117 | static void __init pirq_peer_trick(void) | |
118 | { | |
119 | struct irq_routing_table *rt = pirq_table; | |
120 | u8 busmap[256]; | |
121 | int i; | |
122 | struct irq_info *e; | |
123 | ||
124 | memset(busmap, 0, sizeof(busmap)); | |
7058b061 | 125 | for (i = 0; i < (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); i++) { |
1da177e4 LT |
126 | e = &rt->slots[i]; |
127 | #ifdef DEBUG | |
128 | { | |
129 | int j; |