xref: /openbmc/linux/arch/x86/pci/irq.c (revision 9f67fd5db50566728996b0115a08c83d4f902cb3)
1fb9aa6f1SThomas Gleixner /*
2fb9aa6f1SThomas Gleixner  *	Low-Level PCI Support for PC -- Routing of Interrupts
3fb9aa6f1SThomas Gleixner  *
4fb9aa6f1SThomas Gleixner  *	(c) 1999--2000 Martin Mares <mj@ucw.cz>
5fb9aa6f1SThomas Gleixner  */
6fb9aa6f1SThomas Gleixner 
7fb9aa6f1SThomas Gleixner #include <linux/types.h>
8fb9aa6f1SThomas Gleixner #include <linux/kernel.h>
9fb9aa6f1SThomas Gleixner #include <linux/pci.h>
10fb9aa6f1SThomas Gleixner #include <linux/init.h>
11fb9aa6f1SThomas Gleixner #include <linux/slab.h>
12fb9aa6f1SThomas Gleixner #include <linux/interrupt.h>
13fb9aa6f1SThomas Gleixner #include <linux/dmi.h>
14fb9aa6f1SThomas Gleixner #include <asm/io.h>
15fb9aa6f1SThomas Gleixner #include <asm/smp.h>
16fb9aa6f1SThomas Gleixner #include <asm/io_apic.h>
17fb9aa6f1SThomas Gleixner #include <linux/irq.h>
18fb9aa6f1SThomas Gleixner #include <linux/acpi.h>
19fb9aa6f1SThomas Gleixner 
20fb9aa6f1SThomas Gleixner #include "pci.h"
21fb9aa6f1SThomas Gleixner 
22fb9aa6f1SThomas Gleixner #define PIRQ_SIGNATURE	(('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24))
23fb9aa6f1SThomas Gleixner #define PIRQ_VERSION 0x0100
24fb9aa6f1SThomas Gleixner 
25fb9aa6f1SThomas Gleixner static int broken_hp_bios_irq9;
26fb9aa6f1SThomas Gleixner static int acer_tm360_irqrouting;
27fb9aa6f1SThomas Gleixner 
28fb9aa6f1SThomas Gleixner static struct irq_routing_table *pirq_table;
29fb9aa6f1SThomas Gleixner 
30fb9aa6f1SThomas Gleixner static int pirq_enable_irq(struct pci_dev *dev);
31fb9aa6f1SThomas Gleixner 
32fb9aa6f1SThomas Gleixner /*
33fb9aa6f1SThomas Gleixner  * Never use: 0, 1, 2 (timer, keyboard, and cascade)
34fb9aa6f1SThomas Gleixner  * Avoid using: 13, 14 and 15 (FP error and IDE).
35fb9aa6f1SThomas Gleixner  * Penalize: 3, 4, 6, 7, 12 (known ISA uses: serial, floppy, parallel and mouse)
36fb9aa6f1SThomas Gleixner  */
37fb9aa6f1SThomas Gleixner unsigned int pcibios_irq_mask = 0xfff8;
38fb9aa6f1SThomas Gleixner 
39fb9aa6f1SThomas Gleixner static int pirq_penalty[16] = {
40fb9aa6f1SThomas Gleixner 	1000000, 1000000, 1000000, 1000, 1000, 0, 1000, 1000,
41fb9aa6f1SThomas Gleixner 	0, 0, 0, 0, 1000, 100000, 100000, 100000
42fb9aa6f1SThomas Gleixner };
43fb9aa6f1SThomas Gleixner 
44fb9aa6f1SThomas Gleixner struct irq_router {
45fb9aa6f1SThomas Gleixner 	char *name;
46fb9aa6f1SThomas Gleixner 	u16 vendor, device;
47fb9aa6f1SThomas Gleixner 	int (*get)(struct pci_dev *router, struct pci_dev *dev, int pirq);
48fb9aa6f1SThomas Gleixner 	int (*set)(struct pci_dev *router, struct pci_dev *dev, int pirq, int new);
49fb9aa6f1SThomas Gleixner };
50fb9aa6f1SThomas Gleixner 
51fb9aa6f1SThomas Gleixner struct irq_router_handler {
52fb9aa6f1SThomas Gleixner 	u16 vendor;
53fb9aa6f1SThomas Gleixner 	int (*probe)(struct irq_router *r, struct pci_dev *router, u16 device);
54fb9aa6f1SThomas Gleixner };
55fb9aa6f1SThomas Gleixner 
56fb9aa6f1SThomas Gleixner int (*pcibios_enable_irq)(struct pci_dev *dev) = NULL;
57fb9aa6f1SThomas Gleixner void (*pcibios_disable_irq)(struct pci_dev *dev) = NULL;
58fb9aa6f1SThomas Gleixner 
59fb9aa6f1SThomas Gleixner /*
60fb9aa6f1SThomas Gleixner  *  Check passed address for the PCI IRQ Routing Table signature
61fb9aa6f1SThomas Gleixner  *  and perform checksum verification.
62fb9aa6f1SThomas Gleixner  */
63fb9aa6f1SThomas Gleixner 
64fb9aa6f1SThomas Gleixner static inline struct irq_routing_table * pirq_check_routing_table(u8 *addr)
65fb9aa6f1SThomas Gleixner {
66fb9aa6f1SThomas Gleixner 	struct irq_routing_table *rt;
67fb9aa6f1SThomas Gleixner 	int i;
68fb9aa6f1SThomas Gleixner 	u8 sum;
69fb9aa6f1SThomas Gleixner 
70fb9aa6f1SThomas Gleixner 	rt = (struct irq_routing_table *) addr;
71fb9aa6f1SThomas Gleixner 	if (rt->signature != PIRQ_SIGNATURE ||
72fb9aa6f1SThomas Gleixner 	    rt->version != PIRQ_VERSION ||
73fb9aa6f1SThomas Gleixner 	    rt->size % 16 ||
74fb9aa6f1SThomas Gleixner 	    rt->size < sizeof(struct irq_routing_table))
75fb9aa6f1SThomas Gleixner 		return NULL;
76fb9aa6f1SThomas Gleixner 	sum = 0;
77fb9aa6f1SThomas Gleixner 	for (i=0; i < rt->size; i++)
78fb9aa6f1SThomas Gleixner 		sum += addr[i];
79fb9aa6f1SThomas Gleixner 	if (!sum) {
80fb9aa6f1SThomas Gleixner 		DBG(KERN_DEBUG "PCI: Interrupt Routing Table found at 0x%p\n", rt);
81fb9aa6f1SThomas Gleixner 		return rt;
82fb9aa6f1SThomas Gleixner 	}
83fb9aa6f1SThomas Gleixner 	return NULL;
84fb9aa6f1SThomas Gleixner }
85fb9aa6f1SThomas Gleixner 
86fb9aa6f1SThomas Gleixner 
87fb9aa6f1SThomas Gleixner 
88fb9aa6f1SThomas Gleixner /*
89fb9aa6f1SThomas Gleixner  *  Search 0xf0000 -- 0xfffff for the PCI IRQ Routing Table.
90fb9aa6f1SThomas Gleixner  */
91fb9aa6f1SThomas Gleixner 
92fb9aa6f1SThomas Gleixner static struct irq_routing_table * __init pirq_find_routing_table(void)
93fb9aa6f1SThomas Gleixner {
94fb9aa6f1SThomas Gleixner 	u8 *addr;
95fb9aa6f1SThomas Gleixner 	struct irq_routing_table *rt;
96fb9aa6f1SThomas Gleixner 
97fb9aa6f1SThomas Gleixner 	if (pirq_table_addr) {
98fb9aa6f1SThomas Gleixner 		rt = pirq_check_routing_table((u8 *) __va(pirq_table_addr));
99fb9aa6f1SThomas Gleixner 		if (rt)
100fb9aa6f1SThomas Gleixner 			return rt;
101fb9aa6f1SThomas Gleixner 		printk(KERN_WARNING "PCI: PIRQ table NOT found at pirqaddr\n");
102fb9aa6f1SThomas Gleixner 	}
103fb9aa6f1SThomas Gleixner 	for(addr = (u8 *) __va(0xf0000); addr < (u8 *) __va(0x100000); addr += 16) {
104fb9aa6f1SThomas Gleixner 		rt = pirq_check_routing_table(addr);
105fb9aa6f1SThomas Gleixner 		if (rt)
106fb9aa6f1SThomas Gleixner 			return rt;
107fb9aa6f1SThomas Gleixner 	}
108fb9aa6f1SThomas Gleixner 	return NULL;
109fb9aa6f1SThomas Gleixner }
110fb9aa6f1SThomas Gleixner 
111fb9aa6f1SThomas Gleixner /*
112fb9aa6f1SThomas Gleixner  *  If we have a IRQ routing table, use it to search for peer host
113fb9aa6f1SThomas Gleixner  *  bridges.  It's a gross hack, but since there are no other known
114fb9aa6f1SThomas Gleixner  *  ways how to get a list of buses, we have to go this way.
115fb9aa6f1SThomas Gleixner  */
116fb9aa6f1SThomas Gleixner 
117fb9aa6f1SThomas Gleixner static void __init pirq_peer_trick(void)
118fb9aa6f1SThomas Gleixner {
119fb9aa6f1SThomas Gleixner 	struct irq_routing_table *rt = pirq_table;
120fb9aa6f1SThomas Gleixner 	u8 busmap[256];
121fb9aa6f1SThomas Gleixner 	int i;
122fb9aa6f1SThomas Gleixner 	struct irq_info *e;
123fb9aa6f1SThomas Gleixner 
124fb9aa6f1SThomas Gleixner 	memset(busmap, 0, sizeof(busmap));
125fb9aa6f1SThomas Gleixner 	for(i=0; i < (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); i++) {
126fb9aa6f1SThomas Gleixner 		e = &rt->slots[i];
127fb9aa6f1SThomas Gleixner #ifdef DEBUG
128fb9aa6f1SThomas Gleixner 		{
129fb9aa6f1SThomas Gleixner 			int j;
130fb9aa6f1SThomas Gleixner 			DBG(KERN_DEBUG "%02x:%02x slot=%02x", e->bus, e->devfn/8, e->slot);
131fb9aa6f1SThomas Gleixner 			for(j=0; j<4; j++)
132fb9aa6f1SThomas Gleixner 				DBG(" %d:%02x/%04x", j, e->irq[j].link, e->irq[j].bitmap);
133fb9aa6f1SThomas Gleixner 			DBG("\n");
134fb9aa6f1SThomas Gleixner 		}
135fb9aa6f1SThomas Gleixner #endif
136fb9aa6f1SThomas Gleixner 		busmap[e->bus] = 1;
137fb9aa6f1SThomas Gleixner 	}
138fb9aa6f1SThomas Gleixner 	for(i = 1; i < 256; i++) {
139871d5f8dSYinghai Lu 		int node;
140fb9aa6f1SThomas Gleixner 		if (!busmap[i] || pci_find_bus(0, i))
141fb9aa6f1SThomas Gleixner 			continue;
142871d5f8dSYinghai Lu 		node = get_mp_bus_to_node(i);
143871d5f8dSYinghai Lu 		if (pci_scan_bus_on_node(i, &pci_root_ops, node))
144fb9aa6f1SThomas Gleixner 			printk(KERN_INFO "PCI: Discovered primary peer "
145fb9aa6f1SThomas Gleixner 			       "bus %02x [IRQ]\n", i);
146fb9aa6f1SThomas Gleixner 	}
147fb9aa6f1SThomas Gleixner 	pcibios_last_bus = -1;
148fb9aa6f1SThomas Gleixner }
149fb9aa6f1SThomas Gleixner 
150fb9aa6f1SThomas Gleixner /*
151fb9aa6f1SThomas Gleixner  *  Code for querying and setting of IRQ routes on various interrupt routers.
152fb9aa6f1SThomas Gleixner  */
153fb9aa6f1SThomas Gleixner 
154fb9aa6f1SThomas Gleixner void eisa_set_level_irq(unsigned int irq)
155fb9aa6f1SThomas Gleixner {
156fb9aa6f1SThomas Gleixner 	unsigned char mask = 1 << (irq & 7);
157fb9aa6f1SThomas Gleixner 	unsigned int port = 0x4d0 + (irq >> 3);
158fb9aa6f1SThomas Gleixner 	unsigned char val;
159fb9aa6f1SThomas Gleixner 	static u16 eisa_irq_mask;
160fb9aa6f1SThomas Gleixner 
161fb9aa6f1SThomas Gleixner 	if (irq >= 16 || (1 << irq) & eisa_irq_mask)
162fb9aa6f1SThomas Gleixner 		return;
163fb9aa6f1SThomas Gleixner 
164fb9aa6f1SThomas Gleixner 	eisa_irq_mask |= (1 << irq);
165fb9aa6f1SThomas Gleixner 	printk(KERN_DEBUG "PCI: setting IRQ %u as level-triggered\n", irq);
166fb9aa6f1SThomas Gleixner 	val = inb(port);
167fb9aa6f1SThomas Gleixner 	if (!(val & mask)) {
168fb9aa6f1SThomas Gleixner 		DBG(KERN_DEBUG " -> edge");
169fb9aa6f1SThomas Gleixner 		outb(val | mask, port);
170fb9aa6f1SThomas Gleixner 	}
171fb9aa6f1SThomas Gleixner }
172fb9aa6f1SThomas Gleixner 
173fb9aa6f1SThomas Gleixner /*
17427b46d76SSimon Arlott  * Common IRQ routing practice: nibbles in config space,
175fb9aa6f1SThomas Gleixner  * offset by some magic constant.
176fb9aa6f1SThomas Gleixner  */
177fb9aa6f1SThomas Gleixner static unsigned int read_config_nybble(struct pci_dev *router, unsigned offset, unsigned nr)
178fb9aa6f1SThomas Gleixner {
179fb9aa6f1SThomas Gleixner 	u8 x;
180fb9aa6f1SThomas Gleixner 	unsigned reg = offset + (nr >> 1);
181fb9aa6f1SThomas Gleixner 
182fb9aa6f1SThomas Gleixner 	pci_read_config_byte(router, reg, &x);
183fb9aa6f1SThomas Gleixner 	return (nr & 1) ? (x >> 4) : (x & 0xf);
184fb9aa6f1SThomas Gleixner }
185fb9aa6f1SThomas Gleixner 
186fb9aa6f1SThomas Gleixner static void write_config_nybble(struct pci_dev *router, unsigned offset, unsigned nr, unsigned int val)
187fb9aa6f1SThomas Gleixner {
188fb9aa6f1SThomas Gleixner 	u8 x;
189fb9aa6f1SThomas Gleixner 	unsigned reg = offset + (nr >> 1);
190fb9aa6f1SThomas Gleixner 
191fb9aa6f1SThomas Gleixner 	pci_read_config_byte(router, reg, &x);
192fb9aa6f1SThomas Gleixner 	x = (nr & 1) ? ((x & 0x0f) | (val << 4)) : ((x & 0xf0) | val);
193fb9aa6f1SThomas Gleixner 	pci_write_config_byte(router, reg, x);
194fb9aa6f1SThomas Gleixner }
195fb9aa6f1SThomas Gleixner 
196fb9aa6f1SThomas Gleixner /*
197fb9aa6f1SThomas Gleixner  * ALI pirq entries are damn ugly, and completely undocumented.
198fb9aa6f1SThomas Gleixner  * This has been figured out from pirq tables, and it's not a pretty
199fb9aa6f1SThomas Gleixner  * picture.
200fb9aa6f1SThomas Gleixner  */
201fb9aa6f1SThomas Gleixner static int pirq_ali_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
202fb9aa6f1SThomas Gleixner {
203fb9aa6f1SThomas Gleixner 	static const unsigned char irqmap[16] = { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 };
204fb9aa6f1SThomas Gleixner 
205223ac2f4SBjörn Steinbrink 	WARN_ON_ONCE(pirq > 16);
206fb9aa6f1SThomas Gleixner 	return irqmap[read_config_nybble(router, 0x48, pirq-1)];
207fb9aa6f1SThomas Gleixner }
208fb9aa6f1SThomas Gleixner 
209fb9aa6f1SThomas Gleixner static int pirq_ali_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
210fb9aa6f1SThomas Gleixner {
211fb9aa6f1SThomas Gleixner 	static const unsigned char irqmap[16] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 };
212fb9aa6f1SThomas Gleixner 	unsigned int val = irqmap[irq];
213fb9aa6f1SThomas Gleixner 
214223ac2f4SBjörn Steinbrink 	WARN_ON_ONCE(pirq > 16);
215fb9aa6f1SThomas Gleixner 	if (val) {
216fb9aa6f1SThomas Gleixner 		write_config_nybble(router, 0x48, pirq-1, val);
217fb9aa6f1SThomas Gleixner 		return 1;
218fb9aa6f1SThomas Gleixner 	}
219fb9aa6f1SThomas Gleixner 	return 0;
220fb9aa6f1SThomas Gleixner }
221fb9aa6f1SThomas Gleixner 
222fb9aa6f1SThomas Gleixner /*
223fb9aa6f1SThomas Gleixner  * The Intel PIIX4 pirq rules are fairly simple: "pirq" is
224fb9aa6f1SThomas Gleixner  * just a pointer to the config space.
225fb9aa6f1SThomas Gleixner  */
226fb9aa6f1SThomas Gleixner static int pirq_piix_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
227fb9aa6f1SThomas Gleixner {
228fb9aa6f1SThomas Gleixner 	u8 x;
229fb9aa6f1SThomas Gleixner 
230fb9aa6f1SThomas Gleixner 	pci_read_config_byte(router, pirq, &x);
231fb9aa6f1SThomas Gleixner 	return (x < 16) ? x : 0;
232fb9aa6f1SThomas Gleixner }
233fb9aa6f1SThomas Gleixner 
234fb9aa6f1SThomas Gleixner static int pirq_piix_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
235fb9aa6f1SThomas Gleixner {
236fb9aa6f1SThomas Gleixner 	pci_write_config_byte(router, pirq, irq);
237fb9aa6f1SThomas Gleixner 	return 1;
238fb9aa6f1SThomas Gleixner }
239fb9aa6f1SThomas Gleixner 
240fb9aa6f1SThomas Gleixner /*
241fb9aa6f1SThomas Gleixner  * The VIA pirq rules are nibble-based, like ALI,
242fb9aa6f1SThomas Gleixner  * but without the ugly irq number munging.
243fb9aa6f1SThomas Gleixner  * However, PIRQD is in the upper instead of lower 4 bits.
244fb9aa6f1SThomas Gleixner  */
245fb9aa6f1SThomas Gleixner static int pirq_via_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
246fb9aa6f1SThomas Gleixner {
247fb9aa6f1SThomas Gleixner 	return read_config_nybble(router, 0x55, pirq == 4 ? 5 : pirq);
248fb9aa6f1SThomas Gleixner }
249fb9aa6f1SThomas Gleixner 
250fb9aa6f1SThomas Gleixner static int pirq_via_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
251fb9aa6f1SThomas Gleixner {
252fb9aa6f1SThomas Gleixner 	write_config_nybble(router, 0x55, pirq == 4 ? 5 : pirq, irq);
253fb9aa6f1SThomas Gleixner 	return 1;
254fb9aa6f1SThomas Gleixner }
255fb9aa6f1SThomas Gleixner 
256fb9aa6f1SThomas Gleixner /*
257fb9aa6f1SThomas Gleixner  * The VIA pirq rules are nibble-based, like ALI,
258fb9aa6f1SThomas Gleixner  * but without the ugly irq number munging.
259fb9aa6f1SThomas Gleixner  * However, for 82C586, nibble map is different .
260fb9aa6f1SThomas Gleixner  */
261fb9aa6f1SThomas Gleixner static int pirq_via586_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
262fb9aa6f1SThomas Gleixner {
263fb9aa6f1SThomas Gleixner 	static const unsigned int pirqmap[5] = { 3, 2, 5, 1, 1 };
2647d409d60SIngo Molnar 
265223ac2f4SBjörn Steinbrink 	WARN_ON_ONCE(pirq > 5);
266fb9aa6f1SThomas Gleixner 	return read_config_nybble(router, 0x55, pirqmap[pirq-1]);
267fb9aa6f1SThomas Gleixner }
268fb9aa6f1SThomas Gleixner 
269fb9aa6f1SThomas Gleixner static int pirq_via586_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
270fb9aa6f1SThomas Gleixner {
271fb9aa6f1SThomas Gleixner 	static const unsigned int pirqmap[5] = { 3, 2, 5, 1, 1 };
2727d409d60SIngo Molnar 
273223ac2f4SBjörn Steinbrink 	WARN_ON_ONCE(pirq > 5);
274fb9aa6f1SThomas Gleixner 	write_config_nybble(router, 0x55, pirqmap[pirq-1], irq);
275fb9aa6f1SThomas Gleixner 	return 1;
276fb9aa6f1SThomas Gleixner }
277fb9aa6f1SThomas Gleixner 
278fb9aa6f1SThomas Gleixner /*
279fb9aa6f1SThomas Gleixner  * ITE 8330G pirq rules are nibble-based
280fb9aa6f1SThomas Gleixner  * FIXME: pirqmap may be { 1, 0, 3, 2 },
281fb9aa6f1SThomas Gleixner  * 	  2+3 are both mapped to irq 9 on my system
282fb9aa6f1SThomas Gleixner  */
283fb9aa6f1SThomas Gleixner static int pirq_ite_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
284fb9aa6f1SThomas Gleixner {
285fb9aa6f1SThomas Gleixner 	static const unsigned char pirqmap[4] = { 1, 0, 2, 3 };
2867d409d60SIngo Molnar 
287223ac2f4SBjörn Steinbrink 	WARN_ON_ONCE(pirq > 4);
288fb9aa6f1SThomas Gleixner 	return read_config_nybble(router,0x43, pirqmap[pirq-1]);
289fb9aa6f1SThomas Gleixner }
290fb9aa6f1SThomas Gleixner 
291fb9aa6f1SThomas Gleixner static int pirq_ite_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
292fb9aa6f1SThomas Gleixner {
293fb9aa6f1SThomas Gleixner 	static const unsigned char pirqmap[4] = { 1, 0, 2, 3 };
2947d409d60SIngo Molnar 
295223ac2f4SBjörn Steinbrink 	WARN_ON_ONCE(pirq > 4);
296fb9aa6f1SThomas Gleixner 	write_config_nybble(router, 0x43, pirqmap[pirq-1], irq);
297fb9aa6f1SThomas Gleixner 	return 1;
298fb9aa6f1SThomas Gleixner }
299fb9aa6f1SThomas Gleixner 
300fb9aa6f1SThomas Gleixner /*
301fb9aa6f1SThomas Gleixner  * OPTI: high four bits are nibble pointer..
302fb9aa6f1SThomas Gleixner  * I wonder what the low bits do?
303fb9aa6f1SThomas Gleixner  */
304fb9aa6f1SThomas Gleixner static int pirq_opti_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
305fb9aa6f1SThomas Gleixner {
306fb9aa6f1SThomas Gleixner 	return read_config_nybble(router, 0xb8, pirq >> 4);
307fb9aa6f1SThomas Gleixner }
308fb9aa6f1SThomas Gleixner 
309fb9aa6f1SThomas Gleixner static int pirq_opti_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
310fb9aa6f1SThomas Gleixner {
311fb9aa6f1SThomas Gleixner 	write_config_nybble(router, 0xb8, pirq >> 4, irq);
312fb9aa6f1SThomas Gleixner 	return 1;
313fb9aa6f1SThomas Gleixner }
314fb9aa6f1SThomas Gleixner 
315fb9aa6f1SThomas Gleixner /*
316fb9aa6f1SThomas Gleixner  * Cyrix: nibble offset 0x5C
317fb9aa6f1SThomas Gleixner  * 0x5C bits 7:4 is INTB bits 3:0 is INTA
318fb9aa6f1SThomas Gleixner  * 0x5D bits 7:4 is INTD bits 3:0 is INTC
319fb9aa6f1SThomas Gleixner  */
320fb9aa6f1SThomas Gleixner static int pirq_cyrix_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
321fb9aa6f1SThomas Gleixner {
322fb9aa6f1SThomas Gleixner 	return read_config_nybble(router, 0x5C, (pirq-1)^1);
323fb9aa6f1SThomas Gleixner }
324fb9aa6f1SThomas Gleixner 
325fb9aa6f1SThomas Gleixner static int pirq_cyrix_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
326fb9aa6f1SThomas Gleixner {
327fb9aa6f1SThomas Gleixner 	write_config_nybble(router, 0x5C, (pirq-1)^1, irq);
328fb9aa6f1SThomas Gleixner 	return 1;
329fb9aa6f1SThomas Gleixner }
330fb9aa6f1SThomas Gleixner 
331fb9aa6f1SThomas Gleixner /*
332fb9aa6f1SThomas Gleixner  *	PIRQ routing for SiS 85C503 router used in several SiS chipsets.
333fb9aa6f1SThomas Gleixner  *	We have to deal with the following issues here:
334fb9aa6f1SThomas Gleixner  *	- vendors have different ideas about the meaning of link values
335fb9aa6f1SThomas Gleixner  *	- some onboard devices (integrated in the chipset) have special
336fb9aa6f1SThomas Gleixner  *	  links and are thus routed differently (i.e. not via PCI INTA-INTD)
337fb9aa6f1SThomas Gleixner  *	- different revision of the router have a different layout for
338fb9aa6f1SThomas Gleixner  *	  the routing registers, particularly for the onchip devices
339fb9aa6f1SThomas Gleixner  *
340fb9aa6f1SThomas Gleixner  *	For all routing registers the common thing is we have one byte
341fb9aa6f1SThomas Gleixner  *	per routeable link which is defined as:
342fb9aa6f1SThomas Gleixner  *		 bit 7      IRQ mapping enabled (0) or disabled (1)
343fb9aa6f1SThomas Gleixner  *		 bits [6:4] reserved (sometimes used for onchip devices)
344fb9aa6f1SThomas Gleixner  *		 bits [3:0] IRQ to map to
345fb9aa6f1SThomas Gleixner  *		     allowed: 3-7, 9-12, 14-15
346fb9aa6f1SThomas Gleixner  *		     reserved: 0, 1, 2, 8, 13
347fb9aa6f1SThomas Gleixner  *
348fb9aa6f1SThomas Gleixner  *	The config-space registers located at 0x41/0x42/0x43/0x44 are
349fb9aa6f1SThomas Gleixner  *	always used to route the normal PCI INT A/B/C/D respectively.
350fb9aa6f1SThomas Gleixner  *	Apparently there are systems implementing PCI routing table using
351fb9aa6f1SThomas Gleixner  *	link values 0x01-0x04 and others using 0x41-0x44 for PCI INTA..D.
352fb9aa6f1SThomas Gleixner  *	We try our best to handle both link mappings.
353fb9aa6f1SThomas Gleixner  *
354fb9aa6f1SThomas Gleixner  *	Currently (2003-05-21) it appears most SiS chipsets follow the
355fb9aa6f1SThomas Gleixner  *	definition of routing registers from the SiS-5595 southbridge.
356fb9aa6f1SThomas Gleixner  *	According to the SiS 5595 datasheets the revision id's of the
357fb9aa6f1SThomas Gleixner  *	router (ISA-bridge) should be 0x01 or 0xb0.
358fb9aa6f1SThomas Gleixner  *
359fb9aa6f1SThomas Gleixner  *	Furthermore we've also seen lspci dumps with revision 0x00 and 0xb1.
360fb9aa6f1SThomas Gleixner  *	Looks like these are used in a number of SiS 5xx/6xx/7xx chipsets.
361fb9aa6f1SThomas Gleixner  *	They seem to work with the current routing code. However there is
362fb9aa6f1SThomas Gleixner  *	some concern because of the two USB-OHCI HCs (original SiS 5595
363fb9aa6f1SThomas Gleixner  *	had only one). YMMV.
364fb9aa6f1SThomas Gleixner  *
365fb9aa6f1SThomas Gleixner  *	Onchip routing for router rev-id 0x01/0xb0 and probably 0x00/0xb1:
366fb9aa6f1SThomas Gleixner  *
367fb9aa6f1SThomas Gleixner  *	0x61:	IDEIRQ:
368fb9aa6f1SThomas Gleixner  *		bits [6:5] must be written 01
369fb9aa6f1SThomas Gleixner  *		bit 4 channel-select primary (0), secondary (1)
370fb9aa6f1SThomas Gleixner  *
371fb9aa6f1SThomas Gleixner  *	0x62:	USBIRQ:
372fb9aa6f1SThomas Gleixner  *		bit 6 OHCI function disabled (0), enabled (1)
373fb9aa6f1SThomas Gleixner  *
374fb9aa6f1SThomas Gleixner  *	0x6a:	ACPI/SCI IRQ: bits 4-6 reserved
375fb9aa6f1SThomas Gleixner  *
376fb9aa6f1SThomas Gleixner  *	0x7e:	Data Acq. Module IRQ - bits 4-6 reserved
377fb9aa6f1SThomas Gleixner  *
378fb9aa6f1SThomas Gleixner  *	We support USBIRQ (in addition to INTA-INTD) and keep the
379fb9aa6f1SThomas Gleixner  *	IDE, ACPI and DAQ routing untouched as set by the BIOS.
380fb9aa6f1SThomas Gleixner  *
381fb9aa6f1SThomas Gleixner  *	Currently the only reported exception is the new SiS 65x chipset
382fb9aa6f1SThomas Gleixner  *	which includes the SiS 69x southbridge. Here we have the 85C503
383fb9aa6f1SThomas Gleixner  *	router revision 0x04 and there are changes in the register layout
384fb9aa6f1SThomas Gleixner  *	mostly related to the different USB HCs with USB 2.0 support.
385fb9aa6f1SThomas Gleixner  *
386fb9aa6f1SThomas Gleixner  *	Onchip routing for router rev-id 0x04 (try-and-error observation)
387fb9aa6f1SThomas Gleixner  *
388fb9aa6f1SThomas Gleixner  *	0x60/0x61/0x62/0x63:	1xEHCI and 3xOHCI (companion) USB-HCs
389fb9aa6f1SThomas Gleixner  *				bit 6-4 are probably unused, not like 5595
390fb9aa6f1SThomas Gleixner  */
391fb9aa6f1SThomas Gleixner 
392fb9aa6f1SThomas Gleixner #define PIRQ_SIS_IRQ_MASK	0x0f
393fb9aa6f1SThomas Gleixner #define PIRQ_SIS_IRQ_DISABLE	0x80
394fb9aa6f1SThomas Gleixner #define PIRQ_SIS_USB_ENABLE	0x40
395fb9aa6f1SThomas Gleixner 
396fb9aa6f1SThomas Gleixner static int pirq_sis_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
397fb9aa6f1SThomas Gleixner {
398fb9aa6f1SThomas Gleixner 	u8 x;
399fb9aa6f1SThomas Gleixner 	int reg;
400fb9aa6f1SThomas Gleixner 
401fb9aa6f1SThomas Gleixner 	reg = pirq;
402fb9aa6f1SThomas Gleixner 	if (reg >= 0x01 && reg <= 0x04)
403fb9aa6f1SThomas Gleixner 		reg += 0x40;
404fb9aa6f1SThomas Gleixner 	pci_read_config_byte(router, reg, &x);
405fb9aa6f1SThomas Gleixner 	return (x & PIRQ_SIS_IRQ_DISABLE) ? 0 : (x & PIRQ_SIS_IRQ_MASK);
406fb9aa6f1SThomas Gleixner }
407fb9aa6f1SThomas Gleixner 
408fb9aa6f1SThomas Gleixner static int pirq_sis_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
409fb9aa6f1SThomas Gleixner {
410fb9aa6f1SThomas Gleixner 	u8 x;
411fb9aa6f1SThomas Gleixner 	int reg;
412fb9aa6f1SThomas Gleixner 
413fb9aa6f1SThomas Gleixner 	reg = pirq;
414fb9aa6f1SThomas Gleixner 	if (reg >= 0x01 && reg <= 0x04)
415fb9aa6f1SThomas Gleixner 		reg += 0x40;
416fb9aa6f1SThomas Gleixner 	pci_read_config_byte(router, reg, &x);
417fb9aa6f1SThomas Gleixner 	x &= ~(PIRQ_SIS_IRQ_MASK | PIRQ_SIS_IRQ_DISABLE);
418fb9aa6f1SThomas Gleixner 	x |= irq ? irq: PIRQ_SIS_IRQ_DISABLE;
419fb9aa6f1SThomas Gleixner 	pci_write_config_byte(router, reg, x);
420fb9aa6f1SThomas Gleixner 	return 1;
421fb9aa6f1SThomas Gleixner }
422fb9aa6f1SThomas Gleixner 
423fb9aa6f1SThomas Gleixner 
424fb9aa6f1SThomas Gleixner /*
425fb9aa6f1SThomas Gleixner  * VLSI: nibble offset 0x74 - educated guess due to routing table and
426fb9aa6f1SThomas Gleixner  *       config space of VLSI 82C534 PCI-bridge/router (1004:0102)
427fb9aa6f1SThomas Gleixner  *       Tested on HP OmniBook 800 covering PIRQ 1, 2, 4, 8 for onboard
428fb9aa6f1SThomas Gleixner  *       devices, PIRQ 3 for non-pci(!) soundchip and (untested) PIRQ 6
429fb9aa6f1SThomas Gleixner  *       for the busbridge to the docking station.
430fb9aa6f1SThomas Gleixner  */
431fb9aa6f1SThomas Gleixner 
432fb9aa6f1SThomas Gleixner static int pirq_vlsi_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
433fb9aa6f1SThomas Gleixner {
4347d409d60SIngo Molnar 	WARN_ON_ONCE(pirq >= 9);
435fb9aa6f1SThomas Gleixner 	if (pirq > 8) {
436fb9aa6f1SThomas Gleixner 		printk(KERN_INFO "VLSI router pirq escape (%d)\n", pirq);
437fb9aa6f1SThomas Gleixner 		return 0;
438fb9aa6f1SThomas Gleixner 	}
439fb9aa6f1SThomas Gleixner 	return read_config_nybble(router, 0x74, pirq-1);
440fb9aa6f1SThomas Gleixner }
441fb9aa6f1SThomas Gleixner 
442fb9aa6f1SThomas Gleixner static int pirq_vlsi_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
443fb9aa6f1SThomas Gleixner {
4447d409d60SIngo Molnar 	WARN_ON_ONCE(pirq >= 9);
445fb9aa6f1SThomas Gleixner 	if (pirq > 8) {
446fb9aa6f1SThomas Gleixner 		printk(KERN_INFO "VLSI router pirq escape (%d)\n", pirq);
447fb9aa6f1SThomas Gleixner 		return 0;
448fb9aa6f1SThomas Gleixner 	}
449fb9aa6f1SThomas Gleixner 	write_config_nybble(router, 0x74, pirq-1, irq);
450fb9aa6f1SThomas Gleixner 	return 1;
451fb9aa6f1SThomas Gleixner }
452fb9aa6f1SThomas Gleixner 
453fb9aa6f1SThomas Gleixner /*
454fb9aa6f1SThomas Gleixner  * ServerWorks: PCI interrupts mapped to system IRQ lines through Index
455fb9aa6f1SThomas Gleixner  * and Redirect I/O registers (0x0c00 and 0x0c01).  The Index register
456fb9aa6f1SThomas Gleixner  * format is (PCIIRQ## | 0x10), e.g.: PCIIRQ10=0x1a.  The Redirect
457fb9aa6f1SThomas Gleixner  * register is a straight binary coding of desired PIC IRQ (low nibble).
458fb9aa6f1SThomas Gleixner  *
459fb9aa6f1SThomas Gleixner  * The 'link' value in the PIRQ table is already in the correct format
460fb9aa6f1SThomas Gleixner  * for the Index register.  There are some special index values:
461fb9aa6f1SThomas Gleixner  * 0x00 for ACPI (SCI), 0x01 for USB, 0x02 for IDE0, 0x04 for IDE1,
462fb9aa6f1SThomas Gleixner  * and 0x03 for SMBus.
463fb9aa6f1SThomas Gleixner  */
464fb9aa6f1SThomas Gleixner static int pirq_serverworks_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
465fb9aa6f1SThomas Gleixner {
466c11b68bcSAlan Cox 	outb(pirq, 0xc00);
467fb9aa6f1SThomas Gleixner 	return inb(0xc01) & 0xf;
468fb9aa6f1SThomas Gleixner }
469fb9aa6f1SThomas Gleixner 
470fb9aa6f1SThomas Gleixner static int pirq_serverworks_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
471fb9aa6f1SThomas Gleixner {
472c11b68bcSAlan Cox 	outb(pirq, 0xc00);
473c11b68bcSAlan Cox 	outb(irq, 0xc01);
474fb9aa6f1SThomas Gleixner 	return 1;
475fb9aa6f1SThomas Gleixner }
476fb9aa6f1SThomas Gleixner 
477fb9aa6f1SThomas Gleixner /* Support for AMD756 PCI IRQ Routing
478fb9aa6f1SThomas Gleixner  * Jhon H. Caicedo <jhcaiced@osso.org.co>
479fb9aa6f1SThomas Gleixner  * Jun/21/2001 0.2.0 Release, fixed to use "nybble" functions... (jhcaiced)
480fb9aa6f1SThomas Gleixner  * Jun/19/2001 Alpha Release 0.1.0 (jhcaiced)
481fb9aa6f1SThomas Gleixner  * The AMD756 pirq rules are nibble-based
482fb9aa6f1SThomas Gleixner  * offset 0x56 0-3 PIRQA  4-7  PIRQB
483fb9aa6f1SThomas Gleixner  * offset 0x57 0-3 PIRQC  4-7  PIRQD
484fb9aa6f1SThomas Gleixner  */
485fb9aa6f1SThomas Gleixner static int pirq_amd756_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
486fb9aa6f1SThomas Gleixner {
487fb9aa6f1SThomas Gleixner 	u8 irq;
488fb9aa6f1SThomas Gleixner 	irq = 0;
489fb9aa6f1SThomas Gleixner 	if (pirq <= 4)
490fb9aa6f1SThomas Gleixner 	{
491fb9aa6f1SThomas Gleixner 		irq = read_config_nybble(router, 0x56, pirq - 1);
492fb9aa6f1SThomas Gleixner 	}
493fb9aa6f1SThomas Gleixner 	printk(KERN_INFO "AMD756: dev %04x:%04x, router pirq : %d get irq : %2d\n",
494fb9aa6f1SThomas Gleixner 		dev->vendor, dev->device, pirq, irq);
495fb9aa6f1SThomas Gleixner 	return irq;
496fb9aa6f1SThomas Gleixner }
497fb9aa6f1SThomas Gleixner 
498fb9aa6f1SThomas Gleixner static int pirq_amd756_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
499fb9aa6f1SThomas Gleixner {
500fb9aa6f1SThomas Gleixner 	printk(KERN_INFO "AMD756: dev %04x:%04x, router pirq : %d SET irq : %2d\n",
501fb9aa6f1SThomas Gleixner 		dev->vendor, dev->device, pirq, irq);
502fb9aa6f1SThomas Gleixner 	if (pirq <= 4)
503fb9aa6f1SThomas Gleixner 	{
504fb9aa6f1SThomas Gleixner 		write_config_nybble(router, 0x56, pirq - 1, irq);
505fb9aa6f1SThomas Gleixner 	}
506fb9aa6f1SThomas Gleixner 	return 1;
507fb9aa6f1SThomas Gleixner }
508fb9aa6f1SThomas Gleixner 
509b205f6b2SThomas Backlund /*
510b205f6b2SThomas Backlund  * PicoPower PT86C523
511b205f6b2SThomas Backlund  */
512b205f6b2SThomas Backlund static int pirq_pico_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
513b205f6b2SThomas Backlund {
514b205f6b2SThomas Backlund 	outb(0x10 + ((pirq - 1) >> 1), 0x24);
515b205f6b2SThomas Backlund 	return ((pirq - 1) & 1) ? (inb(0x26) >> 4) : (inb(0x26) & 0xf);
516b205f6b2SThomas Backlund }
517b205f6b2SThomas Backlund 
518b205f6b2SThomas Backlund static int pirq_pico_set(struct pci_dev *router, struct pci_dev *dev, int pirq,
519b205f6b2SThomas Backlund 			int irq)
520b205f6b2SThomas Backlund {
521b205f6b2SThomas Backlund 	unsigned int x;
522b205f6b2SThomas Backlund 	outb(0x10 + ((pirq - 1) >> 1), 0x24);
523b205f6b2SThomas Backlund 	x = inb(0x26);
524b205f6b2SThomas Backlund 	x = ((pirq - 1) & 1) ? ((x & 0x0f) | (irq << 4)) : ((x & 0xf0) | (irq));
525b205f6b2SThomas Backlund 	outb(x, 0x26);
526b205f6b2SThomas Backlund 	return 1;
527b205f6b2SThomas Backlund }
528b205f6b2SThomas Backlund 
529fb9aa6f1SThomas Gleixner #ifdef CONFIG_PCI_BIOS
530fb9aa6f1SThomas Gleixner 
531fb9aa6f1SThomas Gleixner static int pirq_bios_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
532fb9aa6f1SThomas Gleixner {
533fb9aa6f1SThomas Gleixner 	struct pci_dev *bridge;
534fb9aa6f1SThomas Gleixner 	int pin = pci_get_interrupt_pin(dev, &bridge);
535fb9aa6f1SThomas Gleixner 	return pcibios_set_irq_routing(bridge, pin, irq);
536fb9aa6f1SThomas Gleixner }
537fb9aa6f1SThomas Gleixner 
538fb9aa6f1SThomas Gleixner #endif
539fb9aa6f1SThomas Gleixner 
540fb9aa6f1SThomas Gleixner static __init int intel_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
541fb9aa6f1SThomas Gleixner {
542fb9aa6f1SThomas Gleixner 	static struct pci_device_id __initdata pirq_440gx[] = {
543fb9aa6f1SThomas Gleixner 		{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443GX_0) },
544fb9aa6f1SThomas Gleixner 		{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443GX_2) },
545fb9aa6f1SThomas Gleixner 		{ },
546fb9aa6f1SThomas Gleixner 	};
547fb9aa6f1SThomas Gleixner 
548fb9aa6f1SThomas Gleixner 	/* 440GX has a proprietary PIRQ router -- don't use it */
549fb9aa6f1SThomas Gleixner 	if (pci_dev_present(pirq_440gx))
550fb9aa6f1SThomas Gleixner 		return 0;
551fb9aa6f1SThomas Gleixner 
552fb9aa6f1SThomas Gleixner 	switch(device)
553fb9aa6f1SThomas Gleixner 	{
554fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_82371FB_0:
555fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_82371SB_0:
556fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_82371AB_0:
557fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_82371MX:
558fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_82443MX_0:
559fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_82801AA_0:
560fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_82801AB_0:
561fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_82801BA_0:
562fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_82801BA_10:
563fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_82801CA_0:
564fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_82801CA_12:
565fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_82801DB_0:
566fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_82801E_0:
567fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_82801EB_0:
568fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_ESB_1:
569fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_ICH6_0:
570fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_ICH6_1:
571fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_ICH7_0:
572fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_ICH7_1:
573fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_ICH7_30:
574fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_ICH7_31:
575fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_ESB2_0:
576fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_ICH8_0:
577fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_ICH8_1:
578fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_ICH8_2:
579fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_ICH8_3:
580fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_ICH8_4:
581fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_ICH9_0:
582fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_ICH9_1:
583fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_ICH9_2:
584fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_ICH9_3:
585fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_ICH9_4:
586fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_ICH9_5:
587fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_INTEL_TOLAPAI_0:
588cc09c5bcSJason Gaston 		case PCI_DEVICE_ID_INTEL_ICH10_0:
589cc09c5bcSJason Gaston 		case PCI_DEVICE_ID_INTEL_ICH10_1:
590cc09c5bcSJason Gaston 		case PCI_DEVICE_ID_INTEL_ICH10_2:
591cc09c5bcSJason Gaston 		case PCI_DEVICE_ID_INTEL_ICH10_3:
592fb9aa6f1SThomas Gleixner 			r->name = "PIIX/ICH";
593fb9aa6f1SThomas Gleixner 			r->get = pirq_piix_get;
594fb9aa6f1SThomas Gleixner 			r->set = pirq_piix_set;
595fb9aa6f1SThomas Gleixner 			return 1;
596fb9aa6f1SThomas Gleixner 	}
597fb9aa6f1SThomas Gleixner 	return 0;
598fb9aa6f1SThomas Gleixner }
599fb9aa6f1SThomas Gleixner 
600fb9aa6f1SThomas Gleixner static __init int via_router_probe(struct irq_router *r,
601fb9aa6f1SThomas Gleixner 				struct pci_dev *router, u16 device)
602fb9aa6f1SThomas Gleixner {
603fb9aa6f1SThomas Gleixner 	/* FIXME: We should move some of the quirk fixup stuff here */
604fb9aa6f1SThomas Gleixner 
605fb9aa6f1SThomas Gleixner 	/*
606fb9aa6f1SThomas Gleixner 	 * workarounds for some buggy BIOSes
607fb9aa6f1SThomas Gleixner 	 */
608fb9aa6f1SThomas Gleixner 	if (device == PCI_DEVICE_ID_VIA_82C586_0) {
609fb9aa6f1SThomas Gleixner 		switch(router->device) {
610fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_VIA_82C686:
611fb9aa6f1SThomas Gleixner 			/*
612fb9aa6f1SThomas Gleixner 			 * Asus k7m bios wrongly reports 82C686A
613fb9aa6f1SThomas Gleixner 			 * as 586-compatible
614fb9aa6f1SThomas Gleixner 			 */
615fb9aa6f1SThomas Gleixner 			device = PCI_DEVICE_ID_VIA_82C686;
616fb9aa6f1SThomas Gleixner 			break;
617fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_VIA_8235:
618fb9aa6f1SThomas Gleixner 			/**
619fb9aa6f1SThomas Gleixner 			 * Asus a7v-x bios wrongly reports 8235
620fb9aa6f1SThomas Gleixner 			 * as 586-compatible
621fb9aa6f1SThomas Gleixner 			 */
622fb9aa6f1SThomas Gleixner 			device = PCI_DEVICE_ID_VIA_8235;
623fb9aa6f1SThomas Gleixner 			break;
624*9f67fd5dSBertram Felgenhauer 		case PCI_DEVICE_ID_VIA_8237:
625*9f67fd5dSBertram Felgenhauer 			/**
626*9f67fd5dSBertram Felgenhauer 			 * Asus a7v600 bios wrongly reports 8237
627*9f67fd5dSBertram Felgenhauer 			 * as 586-compatible
628*9f67fd5dSBertram Felgenhauer 			 */
629*9f67fd5dSBertram Felgenhauer 			device = PCI_DEVICE_ID_VIA_8237;
630*9f67fd5dSBertram Felgenhauer 			break;
631fb9aa6f1SThomas Gleixner 		}
632fb9aa6f1SThomas Gleixner 	}
633fb9aa6f1SThomas Gleixner 
634fb9aa6f1SThomas Gleixner 	switch(device) {
635fb9aa6f1SThomas Gleixner 	case PCI_DEVICE_ID_VIA_82C586_0:
636fb9aa6f1SThomas Gleixner 		r->name = "VIA";
637fb9aa6f1SThomas Gleixner 		r->get = pirq_via586_get;
638fb9aa6f1SThomas Gleixner 		r->set = pirq_via586_set;
639fb9aa6f1SThomas Gleixner 		return 1;
640fb9aa6f1SThomas Gleixner 	case PCI_DEVICE_ID_VIA_82C596:
641fb9aa6f1SThomas Gleixner 	case PCI_DEVICE_ID_VIA_82C686:
642fb9aa6f1SThomas Gleixner 	case PCI_DEVICE_ID_VIA_8231:
643fb9aa6f1SThomas Gleixner 	case PCI_DEVICE_ID_VIA_8233A:
644fb9aa6f1SThomas Gleixner 	case PCI_DEVICE_ID_VIA_8235:
645fb9aa6f1SThomas Gleixner 	case PCI_DEVICE_ID_VIA_8237:
646fb9aa6f1SThomas Gleixner 		/* FIXME: add new ones for 8233/5 */
647fb9aa6f1SThomas Gleixner 		r->name = "VIA";
648fb9aa6f1SThomas Gleixner 		r->get = pirq_via_get;
649fb9aa6f1SThomas Gleixner 		r->set = pirq_via_set;
650fb9aa6f1SThomas Gleixner 		return 1;
651fb9aa6f1SThomas Gleixner 	}
652fb9aa6f1SThomas Gleixner 	return 0;
653fb9aa6f1SThomas Gleixner }
654fb9aa6f1SThomas Gleixner 
655fb9aa6f1SThomas Gleixner static __init int vlsi_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
656fb9aa6f1SThomas Gleixner {
657fb9aa6f1SThomas Gleixner 	switch(device)
658fb9aa6f1SThomas Gleixner 	{
659fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_VLSI_82C534:
660fb9aa6f1SThomas Gleixner 			r->name = "VLSI 82C534";
661fb9aa6f1SThomas Gleixner 			r->get = pirq_vlsi_get;
662fb9aa6f1SThomas Gleixner 			r->set = pirq_vlsi_set;
663fb9aa6f1SThomas Gleixner 			return 1;
664fb9aa6f1SThomas Gleixner 	}
665fb9aa6f1SThomas Gleixner 	return 0;
666fb9aa6f1SThomas Gleixner }
667fb9aa6f1SThomas Gleixner 
668fb9aa6f1SThomas Gleixner 
669fb9aa6f1SThomas Gleixner static __init int serverworks_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
670fb9aa6f1SThomas Gleixner {
671fb9aa6f1SThomas Gleixner 	switch(device)
672fb9aa6f1SThomas Gleixner 	{
673fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_SERVERWORKS_OSB4:
674fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_SERVERWORKS_CSB5:
675fb9aa6f1SThomas Gleixner 			r->name = "ServerWorks";
676fb9aa6f1SThomas Gleixner 			r->get = pirq_serverworks_get;
677fb9aa6f1SThomas Gleixner 			r->set = pirq_serverworks_set;
678fb9aa6f1SThomas Gleixner 			return 1;
679fb9aa6f1SThomas Gleixner 	}
680fb9aa6f1SThomas Gleixner 	return 0;
681fb9aa6f1SThomas Gleixner }
682fb9aa6f1SThomas Gleixner 
683fb9aa6f1SThomas Gleixner static __init int sis_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
684fb9aa6f1SThomas Gleixner {
685fb9aa6f1SThomas Gleixner 	if (device != PCI_DEVICE_ID_SI_503)
686fb9aa6f1SThomas Gleixner 		return 0;
687fb9aa6f1SThomas Gleixner 
688fb9aa6f1SThomas Gleixner 	r->name = "SIS";
689fb9aa6f1SThomas Gleixner 	r->get = pirq_sis_get;
690fb9aa6f1SThomas Gleixner 	r->set = pirq_sis_set;
691fb9aa6f1SThomas Gleixner 	return 1;
692fb9aa6f1SThomas Gleixner }
693fb9aa6f1SThomas Gleixner 
694fb9aa6f1SThomas Gleixner static __init int cyrix_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
695fb9aa6f1SThomas Gleixner {
696fb9aa6f1SThomas Gleixner 	switch(device)
697fb9aa6f1SThomas Gleixner 	{
698fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_CYRIX_5520:
699fb9aa6f1SThomas Gleixner 			r->name = "NatSemi";
700fb9aa6f1SThomas Gleixner 			r->get = pirq_cyrix_get;
701fb9aa6f1SThomas Gleixner 			r->set = pirq_cyrix_set;
702fb9aa6f1SThomas Gleixner 			return 1;
703fb9aa6f1SThomas Gleixner 	}
704fb9aa6f1SThomas Gleixner 	return 0;
705fb9aa6f1SThomas Gleixner }
706fb9aa6f1SThomas Gleixner 
707fb9aa6f1SThomas Gleixner static __init int opti_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
708fb9aa6f1SThomas Gleixner {
709fb9aa6f1SThomas Gleixner 	switch(device)
710fb9aa6f1SThomas Gleixner 	{
711fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_OPTI_82C700:
712fb9aa6f1SThomas Gleixner 			r->name = "OPTI";
713fb9aa6f1SThomas Gleixner 			r->get = pirq_opti_get;
714fb9aa6f1SThomas Gleixner 			r->set = pirq_opti_set;
715fb9aa6f1SThomas Gleixner 			return 1;
716fb9aa6f1SThomas Gleixner 	}
717fb9aa6f1SThomas Gleixner 	return 0;
718fb9aa6f1SThomas Gleixner }
719fb9aa6f1SThomas Gleixner 
720fb9aa6f1SThomas Gleixner static __init int ite_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
721fb9aa6f1SThomas Gleixner {
722fb9aa6f1SThomas Gleixner 	switch(device)
723fb9aa6f1SThomas Gleixner 	{
724fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_ITE_IT8330G_0:
725fb9aa6f1SThomas Gleixner 			r->name = "ITE";
726fb9aa6f1SThomas Gleixner 			r->get = pirq_ite_get;
727fb9aa6f1SThomas Gleixner 			r->set = pirq_ite_set;
728fb9aa6f1SThomas Gleixner 			return 1;
729fb9aa6f1SThomas Gleixner 	}
730fb9aa6f1SThomas Gleixner 	return 0;
731fb9aa6f1SThomas Gleixner }
732fb9aa6f1SThomas Gleixner 
733fb9aa6f1SThomas Gleixner static __init int ali_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
734fb9aa6f1SThomas Gleixner {
735fb9aa6f1SThomas Gleixner 	switch(device)
736fb9aa6f1SThomas Gleixner 	{
737fb9aa6f1SThomas Gleixner 	case PCI_DEVICE_ID_AL_M1533:
738fb9aa6f1SThomas Gleixner 	case PCI_DEVICE_ID_AL_M1563:
739fb9aa6f1SThomas Gleixner 		printk(KERN_DEBUG "PCI: Using ALI IRQ Router\n");
740fb9aa6f1SThomas Gleixner 		r->name = "ALI";
741fb9aa6f1SThomas Gleixner 		r->get = pirq_ali_get;
742fb9aa6f1SThomas Gleixner 		r->set = pirq_ali_set;
743fb9aa6f1SThomas Gleixner 		return 1;
744fb9aa6f1SThomas Gleixner 	}
745fb9aa6f1SThomas Gleixner 	return 0;
746fb9aa6f1SThomas Gleixner }
747fb9aa6f1SThomas Gleixner 
748fb9aa6f1SThomas Gleixner static __init int amd_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
749fb9aa6f1SThomas Gleixner {
750fb9aa6f1SThomas Gleixner 	switch(device)
751fb9aa6f1SThomas Gleixner 	{
752fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_AMD_VIPER_740B:
753fb9aa6f1SThomas Gleixner 			r->name = "AMD756";
754fb9aa6f1SThomas Gleixner 			break;
755fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_AMD_VIPER_7413:
756fb9aa6f1SThomas Gleixner 			r->name = "AMD766";
757fb9aa6f1SThomas Gleixner 			break;
758fb9aa6f1SThomas Gleixner 		case PCI_DEVICE_ID_AMD_VIPER_7443:
759fb9aa6f1SThomas Gleixner 			r->name = "AMD768";
760fb9aa6f1SThomas Gleixner 			break;
761fb9aa6f1SThomas Gleixner 		default:
762fb9aa6f1SThomas Gleixner 			return 0;
763fb9aa6f1SThomas Gleixner 	}
764fb9aa6f1SThomas Gleixner 	r->get = pirq_amd756_get;
765fb9aa6f1SThomas Gleixner 	r->set = pirq_amd756_set;
766fb9aa6f1SThomas Gleixner 	return 1;
767fb9aa6f1SThomas Gleixner }
768fb9aa6f1SThomas Gleixner 
769b205f6b2SThomas Backlund static __init int pico_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
770b205f6b2SThomas Backlund {
771b205f6b2SThomas Backlund 	switch (device) {
772b205f6b2SThomas Backlund 	case PCI_DEVICE_ID_PICOPOWER_PT86C523:
773b205f6b2SThomas Backlund 		r->name = "PicoPower PT86C523";
774b205f6b2SThomas Backlund 		r->get = pirq_pico_get;
775b205f6b2SThomas Backlund 		r->set = pirq_pico_set;
776b205f6b2SThomas Backlund 		return 1;
777b205f6b2SThomas Backlund 
778b205f6b2SThomas Backlund 	case PCI_DEVICE_ID_PICOPOWER_PT86C523BBP:
779b205f6b2SThomas Backlund 		r->name = "PicoPower PT86C523 rev. BB+";
780b205f6b2SThomas Backlund 		r->get = pirq_pico_get;
781b205f6b2SThomas Backlund 		r->set = pirq_pico_set;
782b205f6b2SThomas Backlund 		return 1;
783b205f6b2SThomas Backlund 	}
784b205f6b2SThomas Backlund 	return 0;
785b205f6b2SThomas Backlund }
786b205f6b2SThomas Backlund 
787fb9aa6f1SThomas Gleixner static __initdata struct irq_router_handler pirq_routers[] = {
788fb9aa6f1SThomas Gleixner 	{ PCI_VENDOR_ID_INTEL, intel_router_probe },
789fb9aa6f1SThomas Gleixner 	{ PCI_VENDOR_ID_AL, ali_router_probe },
790fb9aa6f1SThomas Gleixner 	{ PCI_VENDOR_ID_ITE, ite_router_probe },
791fb9aa6f1SThomas Gleixner 	{ PCI_VENDOR_ID_VIA, via_router_probe },
792fb9aa6f1SThomas Gleixner 	{ PCI_VENDOR_ID_OPTI, opti_router_probe },
793fb9aa6f1SThomas Gleixner 	{ PCI_VENDOR_ID_SI, sis_router_probe },
794fb9aa6f1SThomas Gleixner 	{ PCI_VENDOR_ID_CYRIX, cyrix_router_probe },
795fb9aa6f1SThomas Gleixner 	{ PCI_VENDOR_ID_VLSI, vlsi_router_probe },
796fb9aa6f1SThomas Gleixner 	{ PCI_VENDOR_ID_SERVERWORKS, serverworks_router_probe },
797fb9aa6f1SThomas Gleixner 	{ PCI_VENDOR_ID_AMD, amd_router_probe },
798b205f6b2SThomas Backlund 	{ PCI_VENDOR_ID_PICOPOWER, pico_router_probe },
799fb9aa6f1SThomas Gleixner 	/* Someone with docs needs to add the ATI Radeon IGP */
800fb9aa6f1SThomas Gleixner 	{ 0, NULL }
801fb9aa6f1SThomas Gleixner };
802fb9aa6f1SThomas Gleixner static struct irq_router pirq_router;
803fb9aa6f1SThomas Gleixner static struct pci_dev *pirq_router_dev;
804fb9aa6f1SThomas Gleixner 
805fb9aa6f1SThomas Gleixner 
806fb9aa6f1SThomas Gleixner /*
807fb9aa6f1SThomas Gleixner  *	FIXME: should we have an option to say "generic for
808fb9aa6f1SThomas Gleixner  *	chipset" ?
809fb9aa6f1SThomas Gleixner  */
810fb9aa6f1SThomas Gleixner 
811fb9aa6f1SThomas Gleixner static void __init pirq_find_router(struct irq_router *r)
812fb9aa6f1SThomas Gleixner {
813fb9aa6f1SThomas Gleixner 	struct irq_routing_table *rt = pirq_table;
814fb9aa6f1SThomas Gleixner 	struct irq_router_handler *h;
815fb9aa6f1SThomas Gleixner 
816fb9aa6f1SThomas Gleixner #ifdef CONFIG_PCI_BIOS
817fb9aa6f1SThomas Gleixner 	if (!rt->signature) {
818fb9aa6f1SThomas Gleixner 		printk(KERN_INFO "PCI: Using BIOS for IRQ routing\n");
819fb9aa6f1SThomas Gleixner 		r->set = pirq_bios_set;
820fb9aa6f1SThomas Gleixner 		r->name = "BIOS";
821fb9aa6f1SThomas Gleixner 		return;
822fb9aa6f1SThomas Gleixner 	}
823fb9aa6f1SThomas Gleixner #endif
824fb9aa6f1SThomas Gleixner 
825fb9aa6f1SThomas Gleixner 	/* Default unless a driver reloads it */
826fb9aa6f1SThomas Gleixner 	r->name = "default";
827fb9aa6f1SThomas Gleixner 	r->get = NULL;
828fb9aa6f1SThomas Gleixner 	r->set = NULL;
829fb9aa6f1SThomas Gleixner 
830fb9aa6f1SThomas Gleixner 	DBG(KERN_DEBUG "PCI: Attempting to find IRQ router for %04x:%04x\n",
831fb9aa6f1SThomas Gleixner 	    rt->rtr_vendor, rt->rtr_device);
832fb9aa6f1SThomas Gleixner 
833fb9aa6f1SThomas Gleixner 	pirq_router_dev = pci_get_bus_and_slot(rt->rtr_bus, rt->rtr_devfn);
834fb9aa6f1SThomas Gleixner 	if (!pirq_router_dev) {
835fb9aa6f1SThomas Gleixner 		DBG(KERN_DEBUG "PCI: Interrupt router not found at "
836fb9aa6f1SThomas Gleixner 			"%02x:%02x\n", rt->rtr_bus, rt->rtr_devfn);
837fb9aa6f1SThomas Gleixner 		return;
838fb9aa6f1SThomas Gleixner 	}
839fb9aa6f1SThomas Gleixner 
840fb9aa6f1SThomas Gleixner 	for( h = pirq_routers; h->vendor; h++) {
841fb9aa6f1SThomas Gleixner 		/* First look for a router match */
842fb9aa6f1SThomas Gleixner 		if (rt->rtr_vendor == h->vendor && h->probe(r, pirq_router_dev, rt->rtr_device))
843fb9aa6f1SThomas Gleixner 			break;
844fb9aa6f1SThomas Gleixner 		/* Fall back to a device match */
845fb9aa6f1SThomas Gleixner 		if (pirq_router_dev->vendor == h->vendor && h->probe(r, pirq_router_dev, pirq_router_dev->device))
846fb9aa6f1SThomas Gleixner 			break;
847fb9aa6f1SThomas Gleixner 	}
848fb9aa6f1SThomas Gleixner 	printk(KERN_INFO "PCI: Using IRQ router %s [%04x/%04x] at %s\n",
849fb9aa6f1SThomas Gleixner 		pirq_router.name,
850fb9aa6f1SThomas Gleixner 		pirq_router_dev->vendor,
851fb9aa6f1SThomas Gleixner 		pirq_router_dev->device,
852fb9aa6f1SThomas Gleixner 		pci_name(pirq_router_dev));
853fb9aa6f1SThomas Gleixner 
854fb9aa6f1SThomas Gleixner 	/* The device remains referenced for the kernel lifetime */
855fb9aa6f1SThomas Gleixner }
856fb9aa6f1SThomas Gleixner 
857fb9aa6f1SThomas Gleixner static struct irq_info *pirq_get_info(struct pci_dev *dev)
858fb9aa6f1SThomas Gleixner {
859fb9aa6f1SThomas Gleixner 	struct irq_routing_table *rt = pirq_table;
860fb9aa6f1SThomas Gleixner 	int entries = (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info);
861fb9aa6f1SThomas Gleixner 	struct irq_info *info;
862fb9aa6f1SThomas Gleixner 
863fb9aa6f1SThomas Gleixner 	for (info = rt->slots; entries--; info++)
864fb9aa6f1SThomas Gleixner 		if (info->bus == dev->bus->number && PCI_SLOT(info->devfn) == PCI_SLOT(dev->devfn))
865fb9aa6f1SThomas Gleixner 			return info;
866fb9aa6f1SThomas Gleixner 	return NULL;
867fb9aa6f1SThomas Gleixner }
868fb9aa6f1SThomas Gleixner 
869fb9aa6f1SThomas Gleixner static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
870fb9aa6f1SThomas Gleixner {
871fb9aa6f1SThomas Gleixner 	u8 pin;
872fb9aa6f1SThomas Gleixner 	struct irq_info *info;
873fb9aa6f1SThomas Gleixner 	int i, pirq, newirq;
874fb9aa6f1SThomas Gleixner 	int irq = 0;
875fb9aa6f1SThomas Gleixner 	u32 mask;
876fb9aa6f1SThomas Gleixner 	struct irq_router *r = &pirq_router;
877fb9aa6f1SThomas Gleixner 	struct pci_dev *dev2 = NULL;
878fb9aa6f1SThomas Gleixner 	char *msg = NULL;
879fb9aa6f1SThomas Gleixner 
880fb9aa6f1SThomas Gleixner 	/* Find IRQ pin */
881fb9aa6f1SThomas Gleixner 	pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
882fb9aa6f1SThomas Gleixner 	if (!pin) {
883fb9aa6f1SThomas Gleixner 		DBG(KERN_DEBUG " -> no interrupt pin\n");
884fb9aa6f1SThomas Gleixner 		return 0;
885fb9aa6f1SThomas Gleixner 	}
886fb9aa6f1SThomas Gleixner 	pin = pin - 1;
887fb9aa6f1SThomas Gleixner 
888fb9aa6f1SThomas Gleixner 	/* Find IRQ routing entry */
889fb9aa6f1SThomas Gleixner 
890fb9aa6f1SThomas Gleixner 	if (!pirq_table)
891fb9aa6f1SThomas Gleixner 		return 0;
892fb9aa6f1SThomas Gleixner 
893fb9aa6f1SThomas Gleixner 	DBG(KERN_DEBUG "IRQ for %s[%c]", pci_name(dev), 'A' + pin);
894fb9aa6f1SThomas Gleixner 	info = pirq_get_info(dev);
895fb9aa6f1SThomas Gleixner 	if (!info) {
896fb9aa6f1SThomas Gleixner 		DBG(" -> not found in routing table\n" KERN_DEBUG);
897fb9aa6f1SThomas Gleixner 		return 0;
898fb9aa6f1SThomas Gleixner 	}
899fb9aa6f1SThomas Gleixner 	pirq = info->irq[pin].link;
900fb9aa6f1SThomas Gleixner 	mask = info->irq[pin].bitmap;
901fb9aa6f1SThomas Gleixner 	if (!pirq) {
902fb9aa6f1SThomas Gleixner 		DBG(" -> not routed\n" KERN_DEBUG);
903fb9aa6f1SThomas Gleixner 		return 0;
904fb9aa6f1SThomas Gleixner 	}
905fb9aa6f1SThomas Gleixner 	DBG(" -> PIRQ %02x, mask %04x, excl %04x", pirq, mask, pirq_table->exclusive_irqs);
906fb9aa6f1SThomas Gleixner 	mask &= pcibios_irq_mask;
907fb9aa6f1SThomas Gleixner 
908fb9aa6f1SThomas Gleixner 	/* Work around broken HP Pavilion Notebooks which assign USB to
909fb9aa6f1SThomas Gleixner 	   IRQ 9 even though it is actually wired to IRQ 11 */
910fb9aa6f1SThomas Gleixner 
911fb9aa6f1SThomas Gleixner 	if (broken_hp_bios_irq9 && pirq == 0x59 && dev->irq == 9) {
912fb9aa6f1SThomas Gleixner 		dev->irq = 11;
913fb9aa6f1SThomas Gleixner 		pci_write_config_byte(dev, PCI_INTERRUPT_LINE, 11);
914fb9aa6f1SThomas Gleixner 		r->set(pirq_router_dev, dev, pirq, 11);
915fb9aa6f1SThomas Gleixner 	}
916fb9aa6f1SThomas Gleixner 
917fb9aa6f1SThomas Gleixner 	/* same for Acer Travelmate 360, but with CB and irq 11 -> 10 */
918fb9aa6f1SThomas Gleixner 	if (acer_tm360_irqrouting && dev->irq == 11 && dev->vendor == PCI_VENDOR_ID_O2) {
919fb9aa6f1SThomas Gleixner 		pirq = 0x68;
920fb9aa6f1SThomas Gleixner 		mask = 0x400;
921fb9aa6f1SThomas Gleixner 		dev->irq = r->get(pirq_router_dev, dev, pirq);
922fb9aa6f1SThomas Gleixner 		pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
923fb9aa6f1SThomas Gleixner 	}
924fb9aa6f1SThomas Gleixner 
925fb9aa6f1SThomas Gleixner 	/*
926fb9aa6f1SThomas Gleixner 	 * Find the best IRQ to assign: use the one
927fb9aa6f1SThomas Gleixner 	 * reported by the device if possible.
928fb9aa6f1SThomas Gleixner 	 */
929fb9aa6f1SThomas Gleixner 	newirq = dev->irq;
930fb9aa6f1SThomas Gleixner 	if (newirq && !((1 << newirq) & mask)) {
931fb9aa6f1SThomas Gleixner 		if ( pci_probe & PCI_USE_PIRQ_MASK) newirq = 0;
932fb9aa6f1SThomas Gleixner 		else printk("\n" KERN_WARNING
933fb9aa6f1SThomas Gleixner 			"PCI: IRQ %i for device %s doesn't match PIRQ mask "
934fb9aa6f1SThomas Gleixner 			"- try pci=usepirqmask\n" KERN_DEBUG, newirq,
935fb9aa6f1SThomas Gleixner 			pci_name(dev));
936fb9aa6f1SThomas Gleixner 	}
937fb9aa6f1SThomas Gleixner 	if (!newirq && assign) {
938fb9aa6f1SThomas Gleixner 		for (i = 0; i < 16; i++) {
939fb9aa6f1SThomas Gleixner 			if (!(mask & (1 << i)))
940fb9aa6f1SThomas Gleixner 				continue;
941fb9aa6f1SThomas Gleixner 			if (pirq_penalty[i] < pirq_penalty[newirq] && can_request_irq(i, IRQF_SHARED))
942fb9aa6f1SThomas Gleixner 				newirq = i;
943fb9aa6f1SThomas Gleixner 		}
944fb9aa6f1SThomas Gleixner 	}
945fb9aa6f1SThomas Gleixner 	DBG(" -> newirq=%d", newirq);
946fb9aa6f1SThomas Gleixner 
947fb9aa6f1SThomas Gleixner 	/* Check if it is hardcoded */
948fb9aa6f1SThomas Gleixner 	if ((pirq & 0xf0) == 0xf0) {
949fb9aa6f1SThomas Gleixner 		irq = pirq & 0xf;
950fb9aa6f1SThomas Gleixner 		DBG(" -> hardcoded IRQ %d\n", irq);
951fb9aa6f1SThomas Gleixner 		msg = "Hardcoded";
952fb9aa6f1SThomas Gleixner 	} else if ( r->get && (irq = r->get(pirq_router_dev, dev, pirq)) && \
953fb9aa6f1SThomas Gleixner 	((!(pci_probe & PCI_USE_PIRQ_MASK)) || ((1 << irq) & mask)) ) {
954fb9aa6f1SThomas Gleixner 		DBG(" -> got IRQ %d\n", irq);
955fb9aa6f1SThomas Gleixner 		msg = "Found";
956fb9aa6f1SThomas Gleixner 		eisa_set_level_irq(irq);
957fb9aa6f1SThomas Gleixner 	} else if (newirq && r->set && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) {
958fb9aa6f1SThomas Gleixner 		DBG(" -> assigning IRQ %d", newirq);
959fb9aa6f1SThomas Gleixner 		if (r->set(pirq_router_dev, dev, pirq, newirq)) {
960fb9aa6f1SThomas Gleixner 			eisa_set_level_irq(newirq);
961fb9aa6f1SThomas Gleixner 			DBG(" ... OK\n");
962fb9aa6f1SThomas Gleixner 			msg = "Assigned";
963fb9aa6f1SThomas Gleixner 			irq = newirq;
964fb9aa6f1SThomas Gleixner 		}
965fb9aa6f1SThomas Gleixner 	}
966fb9aa6f1SThomas Gleixner 
967fb9aa6f1SThomas Gleixner 	if (!irq) {
968fb9aa6f1SThomas Gleixner 		DBG(" ... failed\n");
969fb9aa6f1SThomas Gleixner 		if (newirq && mask == (1 << newirq)) {
970fb9aa6f1SThomas Gleixner 			msg = "Guessed";
971fb9aa6f1SThomas Gleixner 			irq = newirq;
972fb9aa6f1SThomas Gleixner 		} else
973fb9aa6f1SThomas Gleixner 			return 0;
974fb9aa6f1SThomas Gleixner 	}
975fb9aa6f1SThomas Gleixner 	printk(KERN_INFO "PCI: %s IRQ %d for device %s\n", msg, irq, pci_name(dev));
976fb9aa6f1SThomas Gleixner 
977fb9aa6f1SThomas Gleixner 	/* Update IRQ for all devices with the same pirq value */
978fb9aa6f1SThomas Gleixner 	while ((dev2 = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev2)) != NULL) {
979fb9aa6f1SThomas Gleixner 		pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin);
980fb9aa6f1SThomas Gleixner 		if (!pin)
981fb9aa6f1SThomas Gleixner 			continue;
982fb9aa6f1SThomas Gleixner 		pin--;
983fb9aa6f1SThomas Gleixner 		info = pirq_get_info(dev2);
984fb9aa6f1SThomas Gleixner 		if (!info)
985fb9aa6f1SThomas Gleixner 			continue;
986fb9aa6f1SThomas Gleixner 		if (info->irq[pin].link == pirq) {
987fb9aa6f1SThomas Gleixner 			/* We refuse to override the dev->irq information. Give a warning! */
988fb9aa6f1SThomas Gleixner 		    	if ( dev2->irq && dev2->irq != irq && \
989fb9aa6f1SThomas Gleixner 			(!(pci_probe & PCI_USE_PIRQ_MASK) || \
990fb9aa6f1SThomas Gleixner 			((1 << dev2->irq) & mask)) ) {
991fb9aa6f1SThomas Gleixner #ifndef CONFIG_PCI_MSI
992fb9aa6f1SThomas Gleixner 		    		printk(KERN_INFO "IRQ routing conflict for %s, have irq %d, want irq %d\n",
993fb9aa6f1SThomas Gleixner 				       pci_name(dev2), dev2->irq, irq);
994fb9aa6f1SThomas Gleixner #endif
995fb9aa6f1SThomas Gleixner 		    		continue;
996fb9aa6f1SThomas Gleixner 		    	}
997fb9aa6f1SThomas Gleixner 			dev2->irq = irq;
998fb9aa6f1SThomas Gleixner 			pirq_penalty[irq]++;
999fb9aa6f1SThomas Gleixner 			if (dev != dev2)
1000fb9aa6f1SThomas Gleixner 				printk(KERN_INFO "PCI: Sharing IRQ %d with %s\n", irq, pci_name(dev2));
1001fb9aa6f1SThomas Gleixner 		}
1002fb9aa6f1SThomas Gleixner 	}
1003fb9aa6f1SThomas Gleixner 	return 1;
1004fb9aa6f1SThomas Gleixner }
1005fb9aa6f1SThomas Gleixner 
1006fb9aa6f1SThomas Gleixner static void __init pcibios_fixup_irqs(void)
1007fb9aa6f1SThomas Gleixner {
1008fb9aa6f1SThomas Gleixner 	struct pci_dev *dev = NULL;
1009fb9aa6f1SThomas Gleixner 	u8 pin;
1010fb9aa6f1SThomas Gleixner 
1011fb9aa6f1SThomas Gleixner 	DBG(KERN_DEBUG "PCI: IRQ fixup\n");
1012fb9aa6f1SThomas Gleixner 	while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
1013fb9aa6f1SThomas Gleixner 		/*
1014fb9aa6f1SThomas Gleixner 		 * If the BIOS has set an out of range IRQ number, just ignore it.
1015fb9aa6f1SThomas Gleixner 		 * Also keep track of which IRQ's are already in use.
1016fb9aa6f1SThomas Gleixner 		 */
1017fb9aa6f1SThomas Gleixner 		if (dev->irq >= 16) {
1018fb9aa6f1SThomas Gleixner 			DBG(KERN_DEBUG "%s: ignoring bogus IRQ %d\n", pci_name(dev), dev->irq);
1019fb9aa6f1SThomas Gleixner 			dev->irq = 0;
1020fb9aa6f1SThomas Gleixner 		}
1021fb9aa6f1SThomas Gleixner 		/* If the IRQ is already assigned to a PCI device, ignore its ISA use penalty */
1022fb9aa6f1SThomas Gleixner 		if (pirq_penalty[dev->irq] >= 100 && pirq_penalty[dev->irq] < 100000)
1023fb9aa6f1SThomas Gleixner 			pirq_penalty[dev->irq] = 0;
1024fb9aa6f1SThomas Gleixner 		pirq_penalty[dev->irq]++;
1025fb9aa6f1SThomas Gleixner 	}
1026fb9aa6f1SThomas Gleixner 
1027fb9aa6f1SThomas Gleixner 	dev = NULL;
1028fb9aa6f1SThomas Gleixner 	while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
1029fb9aa6f1SThomas Gleixner 		pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
1030fb9aa6f1SThomas Gleixner #ifdef CONFIG_X86_IO_APIC
1031fb9aa6f1SThomas Gleixner 		/*
1032fb9aa6f1SThomas Gleixner 		 * Recalculate IRQ numbers if we use the I/O APIC.
1033fb9aa6f1SThomas Gleixner 		 */
1034fb9aa6f1SThomas Gleixner 		if (io_apic_assign_pci_irqs)
1035fb9aa6f1SThomas Gleixner 		{
1036fb9aa6f1SThomas Gleixner 			int irq;
1037fb9aa6f1SThomas Gleixner 
1038fb9aa6f1SThomas Gleixner 			if (pin) {
1039fb9aa6f1SThomas Gleixner 				pin--;		/* interrupt pins are numbered starting from 1 */
1040fb9aa6f1SThomas Gleixner 				irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin);
1041fb9aa6f1SThomas Gleixner 	/*
1042fb9aa6f1SThomas Gleixner 	 * Busses behind bridges are typically not listed in the MP-table.
1043fb9aa6f1SThomas Gleixner 	 * In this case we have to look up the IRQ based on the parent bus,
1044fb9aa6f1SThomas Gleixner 	 * parent slot, and pin number. The SMP code detects such bridged
1045fb9aa6f1SThomas Gleixner 	 * busses itself so we should get into this branch reliably.
1046fb9aa6f1SThomas Gleixner 	 */
1047fb9aa6f1SThomas Gleixner 				if (irq < 0 && dev->bus->parent) { /* go back to the bridge */
1048fb9aa6f1SThomas Gleixner 					struct pci_dev * bridge = dev->bus->self;
1049fb9aa6f1SThomas Gleixner 
1050fb9aa6f1SThomas Gleixner 					pin = (pin + PCI_SLOT(dev->devfn)) % 4;
1051fb9aa6f1SThomas Gleixner 					irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number,
1052fb9aa6f1SThomas Gleixner 							PCI_SLOT(bridge->devfn), pin);
1053fb9aa6f1SThomas Gleixner 					if (irq >= 0)
1054fb9aa6f1SThomas Gleixner 						printk(KERN_WARNING "PCI: using PPB %s[%c] to get irq %d\n",
1055fb9aa6f1SThomas Gleixner 							pci_name(bridge), 'A' + pin, irq);
1056fb9aa6f1SThomas Gleixner 				}
1057fb9aa6f1SThomas Gleixner 				if (irq >= 0) {
1058fb9aa6f1SThomas Gleixner 					printk(KERN_INFO "PCI->APIC IRQ transform: %s[%c] -> IRQ %d\n",
1059fb9aa6f1SThomas Gleixner 						pci_name(dev), 'A' + pin, irq);
1060fb9aa6f1SThomas Gleixner 					dev->irq = irq;
1061fb9aa6f1SThomas Gleixner 				}
1062fb9aa6f1SThomas Gleixner 			}
1063fb9aa6f1SThomas Gleixner 		}
1064fb9aa6f1SThomas Gleixner #endif
1065fb9aa6f1SThomas Gleixner 		/*
1066fb9aa6f1SThomas Gleixner 		 * Still no IRQ? Try to lookup one...
1067fb9aa6f1SThomas Gleixner 		 */
1068fb9aa6f1SThomas Gleixner 		if (pin && !dev->irq)
1069fb9aa6f1SThomas Gleixner 			pcibios_lookup_irq(dev, 0);
1070fb9aa6f1SThomas Gleixner 	}
1071fb9aa6f1SThomas Gleixner }
1072fb9aa6f1SThomas Gleixner 
1073fb9aa6f1SThomas Gleixner /*
1074fb9aa6f1SThomas Gleixner  * Work around broken HP Pavilion Notebooks which assign USB to
1075fb9aa6f1SThomas Gleixner  * IRQ 9 even though it is actually wired to IRQ 11
1076fb9aa6f1SThomas Gleixner  */
107719ad7ae4SLinus Torvalds static int __init fix_broken_hp_bios_irq9(const struct dmi_system_id *d)
1078fb9aa6f1SThomas Gleixner {
1079fb9aa6f1SThomas Gleixner 	if (!broken_hp_bios_irq9) {
1080fb9aa6f1SThomas Gleixner 		broken_hp_bios_irq9 = 1;
1081fb9aa6f1SThomas Gleixner 		printk(KERN_INFO "%s detected - fixing broken IRQ routing\n", d->ident);
1082fb9aa6f1SThomas Gleixner 	}
1083fb9aa6f1SThomas Gleixner 	return 0;
1084fb9aa6f1SThomas Gleixner }
1085fb9aa6f1SThomas Gleixner 
1086fb9aa6f1SThomas Gleixner /*
1087fb9aa6f1SThomas Gleixner  * Work around broken Acer TravelMate 360 Notebooks which assign
1088fb9aa6f1SThomas Gleixner  * Cardbus to IRQ 11 even though it is actually wired to IRQ 10
1089fb9aa6f1SThomas Gleixner  */
109019ad7ae4SLinus Torvalds static int __init fix_acer_tm360_irqrouting(const struct dmi_system_id *d)
1091fb9aa6f1SThomas Gleixner {
1092fb9aa6f1SThomas Gleixner 	if (!acer_tm360_irqrouting) {
1093fb9aa6f1SThomas Gleixner 		acer_tm360_irqrouting = 1;
1094fb9aa6f1SThomas Gleixner 		printk(KERN_INFO "%s detected - fixing broken IRQ routing\n", d->ident);
1095fb9aa6f1SThomas Gleixner 	}
1096fb9aa6f1SThomas Gleixner 	return 0;
1097fb9aa6f1SThomas Gleixner }
1098fb9aa6f1SThomas Gleixner 
1099fb9aa6f1SThomas Gleixner static struct dmi_system_id __initdata pciirq_dmi_table[] = {
1100fb9aa6f1SThomas Gleixner 	{
1101fb9aa6f1SThomas Gleixner 		.callback = fix_broken_hp_bios_irq9,
1102fb9aa6f1SThomas Gleixner 		.ident = "HP Pavilion N5400 Series Laptop",
1103fb9aa6f1SThomas Gleixner 		.matches = {
1104fb9aa6f1SThomas Gleixner 			DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
1105fb9aa6f1SThomas Gleixner 			DMI_MATCH(DMI_BIOS_VERSION, "GE.M1.03"),
1106fb9aa6f1SThomas Gleixner 			DMI_MATCH(DMI_PRODUCT_VERSION, "HP Pavilion Notebook Model GE"),
1107fb9aa6f1SThomas Gleixner 			DMI_MATCH(DMI_BOARD_VERSION, "OmniBook N32N-736"),
1108fb9aa6f1SThomas Gleixner 		},
1109fb9aa6f1SThomas Gleixner 	},
1110fb9aa6f1SThomas Gleixner 	{
1111fb9aa6f1SThomas Gleixner 		.callback = fix_acer_tm360_irqrouting,
1112fb9aa6f1SThomas Gleixner 		.ident = "Acer TravelMate 36x Laptop",
1113fb9aa6f1SThomas Gleixner 		.matches = {
1114fb9aa6f1SThomas Gleixner 			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
1115fb9aa6f1SThomas Gleixner 			DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 360"),
1116fb9aa6f1SThomas Gleixner 		},
1117fb9aa6f1SThomas Gleixner 	},
1118fb9aa6f1SThomas Gleixner 	{ }
1119fb9aa6f1SThomas Gleixner };
1120fb9aa6f1SThomas Gleixner 
1121fb9aa6f1SThomas Gleixner static int __init pcibios_irq_init(void)
1122fb9aa6f1SThomas Gleixner {
1123fb9aa6f1SThomas Gleixner 	DBG(KERN_DEBUG "PCI: IRQ init\n");
1124fb9aa6f1SThomas Gleixner 
1125fb9aa6f1SThomas Gleixner 	if (pcibios_enable_irq || raw_pci_ops == NULL)
1126fb9aa6f1SThomas Gleixner 		return 0;
1127fb9aa6f1SThomas Gleixner 
1128fb9aa6f1SThomas Gleixner 	dmi_check_system(pciirq_dmi_table);
1129fb9aa6f1SThomas Gleixner 
1130fb9aa6f1SThomas Gleixner 	pirq_table = pirq_find_routing_table();
1131fb9aa6f1SThomas Gleixner 
1132fb9aa6f1SThomas Gleixner #ifdef CONFIG_PCI_BIOS
1133fb9aa6f1SThomas Gleixner 	if (!pirq_table && (pci_probe & PCI_BIOS_IRQ_SCAN))
1134fb9aa6f1SThomas Gleixner 		pirq_table = pcibios_get_irq_routing_table();
1135fb9aa6f1SThomas Gleixner #endif
1136fb9aa6f1SThomas Gleixner 	if (pirq_table) {
1137fb9aa6f1SThomas Gleixner 		pirq_peer_trick();
1138fb9aa6f1SThomas Gleixner 		pirq_find_router(&pirq_router);
1139fb9aa6f1SThomas Gleixner 		if (pirq_table->exclusive_irqs) {
1140fb9aa6f1SThomas Gleixner 			int i;
1141fb9aa6f1SThomas Gleixner 			for (i=0; i<16; i++)
1142fb9aa6f1SThomas Gleixner 				if (!(pirq_table->exclusive_irqs & (1 << i)))
1143fb9aa6f1SThomas Gleixner 					pirq_penalty[i] += 100;
1144fb9aa6f1SThomas Gleixner 		}
1145fb9aa6f1SThomas Gleixner 		/* If we're using the I/O APIC, avoid using the PCI IRQ routing table */
1146fb9aa6f1SThomas Gleixner 		if (io_apic_assign_pci_irqs)
1147fb9aa6f1SThomas Gleixner 			pirq_table = NULL;
1148fb9aa6f1SThomas Gleixner 	}
1149fb9aa6f1SThomas Gleixner 
1150fb9aa6f1SThomas Gleixner 	pcibios_enable_irq = pirq_enable_irq;
1151fb9aa6f1SThomas Gleixner 
1152fb9aa6f1SThomas Gleixner 	pcibios_fixup_irqs();
1153fb9aa6f1SThomas Gleixner 	return 0;
1154fb9aa6f1SThomas Gleixner }
1155fb9aa6f1SThomas Gleixner 
1156fb9aa6f1SThomas Gleixner subsys_initcall(pcibios_irq_init);
1157fb9aa6f1SThomas Gleixner 
1158fb9aa6f1SThomas Gleixner 
1159fb9aa6f1SThomas Gleixner static void pirq_penalize_isa_irq(int irq, int active)
1160fb9aa6f1SThomas Gleixner {
1161fb9aa6f1SThomas Gleixner 	/*
1162fb9aa6f1SThomas Gleixner 	 *  If any ISAPnP device reports an IRQ in its list of possible
1163fb9aa6f1SThomas Gleixner 	 *  IRQ's, we try to avoid assigning it to PCI devices.
1164fb9aa6f1SThomas Gleixner 	 */
1165fb9aa6f1SThomas Gleixner 	if (irq < 16) {
1166fb9aa6f1SThomas Gleixner 		if (active)
1167fb9aa6f1SThomas Gleixner 			pirq_penalty[irq] += 1000;
1168fb9aa6f1SThomas Gleixner 		else
1169fb9aa6f1SThomas Gleixner 			pirq_penalty[irq] += 100;
1170fb9aa6f1SThomas Gleixner 	}
1171fb9aa6f1SThomas Gleixner }
1172fb9aa6f1SThomas Gleixner 
1173fb9aa6f1SThomas Gleixner void pcibios_penalize_isa_irq(int irq, int active)
1174fb9aa6f1SThomas Gleixner {
1175fb9aa6f1SThomas Gleixner #ifdef CONFIG_ACPI
1176fb9aa6f1SThomas Gleixner 	if (!acpi_noirq)
1177fb9aa6f1SThomas Gleixner 		acpi_penalize_isa_irq(irq, active);
1178fb9aa6f1SThomas Gleixner 	else
1179fb9aa6f1SThomas Gleixner #endif
1180fb9aa6f1SThomas Gleixner 		pirq_penalize_isa_irq(irq, active);
1181fb9aa6f1SThomas Gleixner }
1182fb9aa6f1SThomas Gleixner 
1183fb9aa6f1SThomas Gleixner static int pirq_enable_irq(struct pci_dev *dev)
1184fb9aa6f1SThomas Gleixner {
1185fb9aa6f1SThomas Gleixner 	u8 pin;
1186fb9aa6f1SThomas Gleixner 	struct pci_dev *temp_dev;
1187fb9aa6f1SThomas Gleixner 
1188fb9aa6f1SThomas Gleixner 	pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
1189fb9aa6f1SThomas Gleixner 	if (pin && !pcibios_lookup_irq(dev, 1) && !dev->irq) {
1190fb9aa6f1SThomas Gleixner 		char *msg = "";
1191fb9aa6f1SThomas Gleixner 
1192fb9aa6f1SThomas Gleixner 		pin--;		/* interrupt pins are numbered starting from 1 */
1193fb9aa6f1SThomas Gleixner 
1194fb9aa6f1SThomas Gleixner 		if (io_apic_assign_pci_irqs) {
1195fb9aa6f1SThomas Gleixner 			int irq;
1196fb9aa6f1SThomas Gleixner 
1197fb9aa6f1SThomas Gleixner 			irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin);
1198fb9aa6f1SThomas Gleixner 			/*
1199fb9aa6f1SThomas Gleixner 			 * Busses behind bridges are typically not listed in the MP-table.
1200fb9aa6f1SThomas Gleixner 			 * In this case we have to look up the IRQ based on the parent bus,
1201fb9aa6f1SThomas Gleixner 			 * parent slot, and pin number. The SMP code detects such bridged
1202fb9aa6f1SThomas Gleixner 			 * busses itself so we should get into this branch reliably.
1203fb9aa6f1SThomas Gleixner 			 */
1204fb9aa6f1SThomas Gleixner 			temp_dev = dev;
1205fb9aa6f1SThomas Gleixner 			while (irq < 0 && dev->bus->parent) { /* go back to the bridge */
1206fb9aa6f1SThomas Gleixner 				struct pci_dev * bridge = dev->bus->self;
1207fb9aa6f1SThomas Gleixner 
1208fb9aa6f1SThomas Gleixner 				pin = (pin + PCI_SLOT(dev->devfn)) % 4;
1209fb9aa6f1SThomas Gleixner 				irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number,
1210fb9aa6f1SThomas Gleixner 						PCI_SLOT(bridge->devfn), pin);
1211fb9aa6f1SThomas Gleixner 				if (irq >= 0)
1212fb9aa6f1SThomas Gleixner 					printk(KERN_WARNING "PCI: using PPB %s[%c] to get irq %d\n",
1213fb9aa6f1SThomas Gleixner 						pci_name(bridge), 'A' + pin, irq);
1214fb9aa6f1SThomas Gleixner 				dev = bridge;
1215fb9aa6f1SThomas Gleixner 			}
1216fb9aa6f1SThomas Gleixner 			dev = temp_dev;
1217fb9aa6f1SThomas Gleixner 			if (irq >= 0) {
1218fb9aa6f1SThomas Gleixner 				printk(KERN_INFO "PCI->APIC IRQ transform: %s[%c] -> IRQ %d\n",
1219fb9aa6f1SThomas Gleixner 					pci_name(dev), 'A' + pin, irq);
1220fb9aa6f1SThomas Gleixner 				dev->irq = irq;
1221fb9aa6f1SThomas Gleixner 				return 0;
1222fb9aa6f1SThomas Gleixner 			} else
1223fb9aa6f1SThomas Gleixner 				msg = " Probably buggy MP table.";
1224fb9aa6f1SThomas Gleixner 		} else if (pci_probe & PCI_BIOS_IRQ_SCAN)
1225fb9aa6f1SThomas Gleixner 			msg = "";
1226fb9aa6f1SThomas Gleixner 		else
1227fb9aa6f1SThomas Gleixner 			msg = " Please try using pci=biosirq.";
1228fb9aa6f1SThomas Gleixner 
1229fb9aa6f1SThomas Gleixner 		/* With IDE legacy devices the IRQ lookup failure is not a problem.. */
1230fb9aa6f1SThomas Gleixner 		if (dev->class >> 8 == PCI_CLASS_STORAGE_IDE && !(dev->class & 0x5))
1231fb9aa6f1SThomas Gleixner 			return 0;
1232fb9aa6f1SThomas Gleixner 
1233fb9aa6f1SThomas Gleixner 		printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s.%s\n",
1234fb9aa6f1SThomas Gleixner 		       'A' + pin, pci_name(dev), msg);
1235fb9aa6f1SThomas Gleixner 	}
1236fb9aa6f1SThomas Gleixner 	return 0;
1237fb9aa6f1SThomas Gleixner }
1238