Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1da177e4 LT |
2 | /* |
3 | * Low-Level PCI Support for PC -- Routing of Interrupts | |
4 | * | |
5 | * (c) 1999--2000 Martin Mares <mj@ucw.cz> | |
6 | */ | |
7 | ||
1da177e4 LT |
8 | #include <linux/types.h> |
9 | #include <linux/kernel.h> | |
10 | #include <linux/pci.h> | |
11 | #include <linux/init.h> | |
1da177e4 | 12 | #include <linux/interrupt.h> |
1da177e4 | 13 | #include <linux/dmi.h> |
7058b061 PC |
14 | #include <linux/io.h> |
15 | #include <linux/smp.h> | |
1ce849c7 | 16 | #include <linux/spinlock.h> |
1da177e4 | 17 | #include <asm/io_apic.h> |
b33fa1f3 | 18 | #include <linux/irq.h> |
1da177e4 | 19 | #include <linux/acpi.h> |
1ce849c7 | 20 | |
d2531661 | 21 | #include <asm/i8259.h> |
1ce849c7 | 22 | #include <asm/pc-conf-reg.h> |
82487711 | 23 | #include <asm/pci_x86.h> |
1da177e4 LT |
24 | |
25 | #define PIRQ_SIGNATURE (('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24)) | |
26 | #define PIRQ_VERSION 0x0100 | |
27 | ||
b584db0c MR |
28 | #define IRT_SIGNATURE (('$' << 0) + ('I' << 8) + ('R' << 16) + ('T' << 24)) |
29 | ||
1da177e4 LT |
30 | static int broken_hp_bios_irq9; |
31 | static int acer_tm360_irqrouting; | |
32 | ||
33 | static struct irq_routing_table *pirq_table; | |
34 | ||
35 | static int pirq_enable_irq(struct pci_dev *dev); | |
c03b3b07 | 36 | static void pirq_disable_irq(struct pci_dev *dev); |
1da177e4 LT |
37 | |
38 | /* | |
39 | * Never use: 0, 1, 2 (timer, keyboard, and cascade) | |
40 | * Avoid using: 13, 14 and 15 (FP error and IDE). | |
41 | * Penalize: 3, 4, 6, 7, 12 (known ISA uses: serial, floppy, parallel and mouse) | |
42 | */ | |
43 | unsigned int pcibios_irq_mask = 0xfff8; | |
44 | ||
45 | static int pirq_penalty[16] = { | |
46 | 1000000, 1000000, 1000000, 1000, 1000, 0, 1000, 1000, | |
47 | 0, 0, 0, 0, 1000, 100000, 100000, 100000 | |
48 | }; | |
49 | ||
50 | struct irq_router { | |
51 | char *name; | |
52 | u16 vendor, device; | |
53 | int (*get)(struct pci_dev *router, struct pci_dev *dev, int pirq); | |
273c1127 MV |
54 | int (*set)(struct pci_dev *router, struct pci_dev *dev, int pirq, |
55 | int new); | |
1ce849c7 MR |
56 | int (*lvl)(struct pci_dev *router, struct pci_dev *dev, int pirq, |
57 | int irq); | |
1da177e4 LT |
58 | }; |
59 | ||
60 | struct irq_router_handler { | |
61 | u16 vendor; | |
62 | int (*probe)(struct irq_router *r, struct pci_dev *router, u16 device); | |
63 | }; | |
64 | ||
ab3b3793 | 65 | int (*pcibios_enable_irq)(struct pci_dev *dev) = pirq_enable_irq; |
c03b3b07 | 66 | void (*pcibios_disable_irq)(struct pci_dev *dev) = pirq_disable_irq; |
1da177e4 | 67 | |
120bb424 | 68 | /* |
69 | * Check passed address for the PCI IRQ Routing Table signature | |
70 | * and perform checksum verification. | |
71 | */ | |
72 | ||
5d64089a MR |
73 | static inline struct irq_routing_table *pirq_check_routing_table(u8 *addr, |
74 | u8 *limit) | |
120bb424 | 75 | { |
76 | struct irq_routing_table *rt; | |
77 | int i; | |
78 | u8 sum; | |
79 | ||
c25f2345 | 80 | rt = (struct irq_routing_table *)addr; |
120bb424 | 81 | if (rt->signature != PIRQ_SIGNATURE || |
82 | rt->version != PIRQ_VERSION || | |
83 | rt->size % 16 || | |
5d64089a MR |
84 | rt->size < sizeof(struct irq_routing_table) || |
85 | (limit && rt->size > limit - addr)) | |
120bb424 | 86 | return NULL; |
87 | sum = 0; | |
7058b061 | 88 | for (i = 0; i < rt->size; i++) |
120bb424 | 89 | sum += addr[i]; |
90 | if (!sum) { | |
613fa6e2 MR |
91 | DBG(KERN_DEBUG "PCI: Interrupt Routing Table found at 0x%lx\n", |
92 | __pa(rt)); | |
120bb424 | 93 | return rt; |
94 | } | |
95 | return NULL; | |
96 | } | |
97 | ||
b584db0c MR |
98 | /* |
99 | * Handle the $IRT PCI IRQ Routing Table format used by AMI for its BCP | |
100 | * (BIOS Configuration Program) external tool meant for tweaking BIOS | |
101 | * structures without the need to rebuild it from sources. The $IRT | |
102 | * format has been invented by AMI before Microsoft has come up with its | |
103 | * $PIR format and a $IRT table is therefore there in some systems that | |
104 | * lack a $PIR table. | |
105 | * | |
106 | * It uses the same PCI BIOS 2.1 format for interrupt routing entries | |
107 | * themselves but has a different simpler header prepended instead, | |
108 | * occupying 8 bytes, where a `$IRT' signature is followed by one byte | |
109 | * specifying the total number of interrupt routing entries allocated in | |
110 | * the table, then one byte specifying the actual number of entries used | |
111 | * (which the BCP tool can take advantage of when modifying the table), | |
112 | * and finally a 16-bit word giving the IRQs devoted exclusively to PCI. | |
113 | * Unlike with the $PIR table there is no alignment guarantee. | |
114 | * | |
115 | * Given the similarity of the two formats the $IRT one is trivial to | |
116 | * convert to the $PIR one, which we do here, except that obviously we | |
117 | * have no information as to the router device to use, but we can handle | |
118 | * it by matching PCI device IDs actually seen on the bus against ones | |
119 | * that our individual routers recognise. | |
120 | * | |
121 | * Reportedly there is another $IRT table format where a 16-bit word | |
122 | * follows the header instead that points to interrupt routing entries | |
123 | * in a $PIR table provided elsewhere. In that case this code will not | |
124 | * be reached though as the $PIR table will have been chosen instead. | |
125 | */ | |
126 | static inline struct irq_routing_table *pirq_convert_irt_table(u8 *addr, | |
127 | u8 *limit) | |
128 | { | |
129 | struct irt_routing_table *ir; | |
130 | struct irq_routing_table *rt; | |
131 | u16 size; | |
132 | u8 sum; | |
133 | int i; | |
134 | ||
135 | ir = (struct irt_routing_table *)addr; | |
136 | if (ir->signature != IRT_SIGNATURE || !ir->used || ir->size < ir->used) | |
137 | return NULL; | |
138 | ||
139 | size = sizeof(*ir) + ir->used * sizeof(ir->slots[0]); | |
140 | if (size > limit - addr) | |
141 | return NULL; | |
142 | ||
143 | DBG(KERN_DEBUG "PCI: $IRT Interrupt Routing Table found at 0x%lx\n", | |
144 | __pa(ir)); | |
145 | ||
146 | size = sizeof(*rt) + ir->used * sizeof(rt->slots[0]); | |
147 | rt = kzalloc(size, GFP_KERNEL); | |
148 | if (!rt) | |
149 | return NULL; | |
120bb424 | 150 | |
b584db0c MR |
151 | rt->signature = PIRQ_SIGNATURE; |
152 | rt->version = PIRQ_VERSION; | |
153 | rt->size = size; | |
154 | rt->exclusive_irqs = ir->exclusive_irqs; | |
155 | for (i = 0; i < ir->used; i++) | |
156 | rt->slots[i] = ir->slots[i]; | |
157 | ||
158 | addr = (u8 *)rt; | |
159 | sum = 0; | |
160 | for (i = 0; i < size; i++) | |
161 | sum += addr[i]; | |
162 | rt->checksum = -sum; | |
163 | ||
164 | return rt; | |
165 | } | |
120bb424 | 166 | |
1da177e4 LT |
167 | /* |
168 | * Search 0xf0000 -- 0xfffff for the PCI IRQ Routing Table. | |
169 | */ | |
170 | ||
171 | static struct irq_routing_table * __init pirq_find_routing_table(void) | |
172 | { | |
5d64089a MR |
173 | u8 * const bios_start = (u8 *)__va(0xf0000); |
174 | u8 * const bios_end = (u8 *)__va(0x100000); | |
1da177e4 LT |
175 | u8 *addr; |
176 | struct irq_routing_table *rt; | |
1da177e4 | 177 | |
120bb424 | 178 | if (pirq_table_addr) { |
5d64089a MR |
179 | rt = pirq_check_routing_table((u8 *)__va(pirq_table_addr), |
180 | NULL); | |
120bb424 | 181 | if (rt) |
182 | return rt; | |
183 | printk(KERN_WARNING "PCI: PIRQ table NOT found at pirqaddr\n"); | |
184 | } | |
5d64089a MR |
185 | for (addr = bios_start; |
186 | addr < bios_end - sizeof(struct irq_routing_table); | |
187 | addr += 16) { | |
188 | rt = pirq_check_routing_table(addr, bios_end); | |
120bb424 | 189 | if (rt) |
1da177e4 | 190 | return rt; |
1da177e4 | 191 | } |
b584db0c MR |
192 | for (addr = bios_start; |
193 | addr < bios_end - sizeof(struct irt_routing_table); | |
194 | addr++) { | |
195 | rt = pirq_convert_irt_table(addr, bios_end); | |
196 | if (rt) | |
197 | return rt; | |
198 | } | |
1da177e4 LT |
199 | return NULL; |
200 | } | |
201 | ||
202 | /* | |
203 | * If we have a IRQ routing table, use it to search for peer host | |
204 | * bridges. It's a gross hack, but since there are no other known | |
205 | * ways how to get a list of buses, we have to go this way. | |
206 | */ | |
207 | ||
208 | static void __init pirq_peer_trick(void) | |
209 | { | |
210 | struct irq_routing_table *rt = pirq_table; | |
211 | u8 busmap[256]; | |
212 | int i; | |
213 | struct irq_info *e; | |
214 | ||
215 | memset(busmap, 0, sizeof(busmap)); | |
7058b061 | 216 | for (i = 0; i < (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); i++) { |
1da177e4 LT |
217 | e = &rt->slots[i]; |
218 | #ifdef DEBUG | |
219 | { | |
220 | int j; | |
dc0e6408 MR |
221 | DBG(KERN_DEBUG "%02x:%02x.%x slot=%02x", |
222 | e->bus, e->devfn / 8, e->devfn % 8, e->slot); | |
7058b061 | 223 | for (j = 0; j < 4; j++) |
1da177e4 LT |
224 | DBG(" %d:%02x/%04x", j, e->irq[j].link, e->irq[j].bitmap); |
225 | DBG("\n"); | |
226 | } | |
227 | #endif | |
228 | busmap[e->bus] = 1; | |
229 | } | |
7058b061 | 230 | for (i = 1; i < 256; i++) { |
1da177e4 LT |
231 | if (!busmap[i] || pci_find_bus(0, i)) |
232 | continue; | |
49886cf4 | 233 | pcibios_scan_root(i); |
1da177e4 LT |
234 | } |
235 | pcibios_last_bus = -1; | |
236 | } | |
237 | ||
238 | /* | |
239 | * Code for querying and setting of IRQ routes on various interrupt routers. | |
ea6cd250 | 240 | * PIC Edge/Level Control Registers (ELCR) 0x4d0 & 0x4d1. |
1da177e4 LT |
241 | */ |
242 | ||
ea6cd250 | 243 | void elcr_set_level_irq(unsigned int irq) |
1da177e4 LT |
244 | { |
245 | unsigned char mask = 1 << (irq & 7); | |
d2531661 | 246 | unsigned int port = PIC_ELCR1 + (irq >> 3); |
1da177e4 | 247 | unsigned char val; |
ea6cd250 | 248 | static u16 elcr_irq_mask; |
1da177e4 | 249 | |
ea6cd250 | 250 | if (irq >= 16 || (1 << irq) & elcr_irq_mask) |
1da177e4 LT |
251 | return; |
252 | ||
ea6cd250 | 253 | elcr_irq_mask |= (1 << irq); |