Commit 03e1ccd4 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'x86-irq-2022-05-23' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86 PCI irq routing updates from Thomas Gleixner:

 - Cleanup and robustify the PCI interrupt routing table handling
   including proper range checks

 - Add support for Intel 82378ZB/82379AB, SiS85C497 PIRQ routers

 - Fix the ALi M1487 router handling

 - Handle the IRT routing table format in AMI BIOSes correctly

* tag 'x86-irq-2022-05-23' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86/PCI: Fix coding style in PIRQ table verification
  x86/PCI: Fix ALi M1487 (IBC) PIRQ router link value interpretation
  x86/PCI: Add $IRT PIRQ routing table support
  x86/PCI: Handle PIRQ routing tables with no router device given
  x86/PCI: Add PIRQ routing table range checks
  x86/PCI: Add support for the SiS85C497 PIRQ router
  x86/PCI: Disambiguate SiS85C503 PIRQ router code entities
  x86/PCI: Handle IRQ swizzling with PIRQ routers
  x86/PCI: Also match function number in $PIR table
  x86/PCI: Include function number in $PIR table dump
  x86/PCI: Show the physical address of the $PIR table
parents 6e01f86f c25f2345
Loading
Loading
Loading
Loading
+9 −0
Original line number Original line Diff line number Diff line
@@ -93,6 +93,15 @@ struct irq_routing_table {
	struct irq_info slots[];
	struct irq_info slots[];
} __attribute__((packed));
} __attribute__((packed));


struct irt_routing_table {
	u32 signature;			/* IRT_SIGNATURE should be here */
	u8 size;			/* Number of entries provided */
	u8 used;			/* Number of entries actually used */
	u16 exclusive_irqs;		/* IRQs devoted exclusively to
					   PCI usage */
	struct irq_info slots[];
} __attribute__((packed));

extern unsigned int pcibios_irq_mask;
extern unsigned int pcibios_irq_mask;


extern raw_spinlock_t pci_config_lock;
extern raw_spinlock_t pci_config_lock;
+313 −64
Original line number Original line Diff line number Diff line
@@ -25,6 +25,8 @@
#define PIRQ_SIGNATURE	(('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24))
#define PIRQ_SIGNATURE	(('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24))
#define PIRQ_VERSION 0x0100
#define PIRQ_VERSION 0x0100


#define IRT_SIGNATURE	(('$' << 0) + ('I' << 8) + ('R' << 16) + ('T' << 24))

static int broken_hp_bios_irq9;
static int broken_hp_bios_irq9;
static int acer_tm360_irqrouting;
static int acer_tm360_irqrouting;


@@ -68,7 +70,8 @@ void (*pcibios_disable_irq)(struct pci_dev *dev) = pirq_disable_irq;
 *  and perform checksum verification.
 *  and perform checksum verification.
 */
 */


static inline struct irq_routing_table *pirq_check_routing_table(u8 *addr)
static inline struct irq_routing_table *pirq_check_routing_table(u8 *addr,
								 u8 *limit)
{
{
	struct irq_routing_table *rt;
	struct irq_routing_table *rt;
	int i;
	int i;
@@ -78,20 +81,88 @@ static inline struct irq_routing_table *pirq_check_routing_table(u8 *addr)
	if (rt->signature != PIRQ_SIGNATURE ||
	if (rt->signature != PIRQ_SIGNATURE ||
	    rt->version != PIRQ_VERSION ||
	    rt->version != PIRQ_VERSION ||
	    rt->size % 16 ||
	    rt->size % 16 ||
	    rt->size < sizeof(struct irq_routing_table))
	    rt->size < sizeof(struct irq_routing_table) ||
	    (limit && rt->size > limit - addr))
		return NULL;
		return NULL;
	sum = 0;
	sum = 0;
	for (i = 0; i < rt->size; i++)
	for (i = 0; i < rt->size; i++)
		sum += addr[i];
		sum += addr[i];
	if (!sum) {
	if (!sum) {
		DBG(KERN_DEBUG "PCI: Interrupt Routing Table found at 0x%p\n",
		DBG(KERN_DEBUG "PCI: Interrupt Routing Table found at 0x%lx\n",
			rt);
		    __pa(rt));
		return rt;
		return rt;
	}
	}
	return NULL;
	return NULL;
}
}


/*
 * Handle the $IRT PCI IRQ Routing Table format used by AMI for its BCP
 * (BIOS Configuration Program) external tool meant for tweaking BIOS
 * structures without the need to rebuild it from sources.  The $IRT
 * format has been invented by AMI before Microsoft has come up with its
 * $PIR format and a $IRT table is therefore there in some systems that
 * lack a $PIR table.
 *
 * It uses the same PCI BIOS 2.1 format for interrupt routing entries
 * themselves but has a different simpler header prepended instead,
 * occupying 8 bytes, where a `$IRT' signature is followed by one byte
 * specifying the total number of interrupt routing entries allocated in
 * the table, then one byte specifying the actual number of entries used
 * (which the BCP tool can take advantage of when modifying the table),
 * and finally a 16-bit word giving the IRQs devoted exclusively to PCI.
 * Unlike with the $PIR table there is no alignment guarantee.
 *
 * Given the similarity of the two formats the $IRT one is trivial to
 * convert to the $PIR one, which we do here, except that obviously we
 * have no information as to the router device to use, but we can handle
 * it by matching PCI device IDs actually seen on the bus against ones
 * that our individual routers recognise.
 *
 * Reportedly there is another $IRT table format where a 16-bit word
 * follows the header instead that points to interrupt routing entries
 * in a $PIR table provided elsewhere.  In that case this code will not
 * be reached though as the $PIR table will have been chosen instead.
 */
static inline struct irq_routing_table *pirq_convert_irt_table(u8 *addr,
							       u8 *limit)
{
	struct irt_routing_table *ir;
	struct irq_routing_table *rt;
	u16 size;
	u8 sum;
	int i;

	ir = (struct irt_routing_table *)addr;
	if (ir->signature != IRT_SIGNATURE || !ir->used || ir->size < ir->used)
		return NULL;

	size = sizeof(*ir) + ir->used * sizeof(ir->slots[0]);
	if (size > limit - addr)
		return NULL;

	DBG(KERN_DEBUG "PCI: $IRT Interrupt Routing Table found at 0x%lx\n",
	    __pa(ir));

	size = sizeof(*rt) + ir->used * sizeof(rt->slots[0]);
	rt = kzalloc(size, GFP_KERNEL);
	if (!rt)
		return NULL;


	rt->signature = PIRQ_SIGNATURE;
	rt->version = PIRQ_VERSION;
	rt->size = size;
	rt->exclusive_irqs = ir->exclusive_irqs;
	for (i = 0; i < ir->used; i++)
		rt->slots[i] = ir->slots[i];

	addr = (u8 *)rt;
	sum = 0;
	for (i = 0; i < size; i++)
		sum += addr[i];
	rt->checksum = -sum;

	return rt;
}


/*
/*
 *  Search 0xf0000 -- 0xfffff for the PCI IRQ Routing Table.
 *  Search 0xf0000 -- 0xfffff for the PCI IRQ Routing Table.
@@ -99,17 +170,29 @@ static inline struct irq_routing_table *pirq_check_routing_table(u8 *addr)


static struct irq_routing_table * __init pirq_find_routing_table(void)
static struct irq_routing_table * __init pirq_find_routing_table(void)
{
{
	u8 * const bios_start = (u8 *)__va(0xf0000);
	u8 * const bios_end = (u8 *)__va(0x100000);
	u8 *addr;
	u8 *addr;
	struct irq_routing_table *rt;
	struct irq_routing_table *rt;


	if (pirq_table_addr) {
	if (pirq_table_addr) {
		rt = pirq_check_routing_table((u8 *) __va(pirq_table_addr));
		rt = pirq_check_routing_table((u8 *)__va(pirq_table_addr),
					      NULL);
		if (rt)
		if (rt)
			return rt;
			return rt;
		printk(KERN_WARNING "PCI: PIRQ table NOT found at pirqaddr\n");
		printk(KERN_WARNING "PCI: PIRQ table NOT found at pirqaddr\n");
	}
	}
	for (addr = (u8 *) __va(0xf0000); addr < (u8 *) __va(0x100000); addr += 16) {
	for (addr = bios_start;
		rt = pirq_check_routing_table(addr);
	     addr < bios_end - sizeof(struct irq_routing_table);
	     addr += 16) {
		rt = pirq_check_routing_table(addr, bios_end);
		if (rt)
			return rt;
	}
	for (addr = bios_start;
	     addr < bios_end - sizeof(struct irt_routing_table);
	     addr++) {
		rt = pirq_convert_irt_table(addr, bios_end);
		if (rt)
		if (rt)
			return rt;
			return rt;
	}
	}
@@ -135,7 +218,8 @@ static void __init pirq_peer_trick(void)
#ifdef DEBUG
#ifdef DEBUG
		{
		{
			int j;
			int j;
			DBG(KERN_DEBUG "%02x:%02x slot=%02x", e->bus, e->devfn/8, e->slot);
			DBG(KERN_DEBUG "%02x:%02x.%x slot=%02x",
			    e->bus, e->devfn / 8, e->devfn % 8, e->slot);
			for (j = 0; j < 4; j++)
			for (j = 0; j < 4; j++)
				DBG(" %d:%02x/%04x", j, e->irq[j].link, e->irq[j].bitmap);
				DBG(" %d:%02x/%04x", j, e->irq[j].link, e->irq[j].bitmap);
			DBG("\n");
			DBG("\n");
@@ -253,6 +337,15 @@ static void write_pc_conf_nybble(u8 base, u8 index, u8 val)
	pc_conf_set(reg, x);
	pc_conf_set(reg, x);
}
}


/*
 * FinALi pirq rules are as follows:
 *
 * - bit 0 selects between INTx Routing Table Mapping Registers,
 *
 * - bit 3 selects the nibble within the INTx Routing Table Mapping Register,
 *
 * - bits 7:4 map to bits 3:0 of the PCI INTx Sensitivity Register.
 */
static int pirq_finali_get(struct pci_dev *router, struct pci_dev *dev,
static int pirq_finali_get(struct pci_dev *router, struct pci_dev *dev,
			   int pirq)
			   int pirq)
{
{
@@ -260,11 +353,13 @@ static int pirq_finali_get(struct pci_dev *router, struct pci_dev *dev,
		0, 9, 3, 10, 4, 5, 7, 6, 0, 11, 0, 12, 0, 14, 0, 15
		0, 9, 3, 10, 4, 5, 7, 6, 0, 11, 0, 12, 0, 14, 0, 15
	};
	};
	unsigned long flags;
	unsigned long flags;
	u8 index;
	u8 x;
	u8 x;


	index = (pirq & 1) << 1 | (pirq & 8) >> 3;
	raw_spin_lock_irqsave(&pc_conf_lock, flags);
	raw_spin_lock_irqsave(&pc_conf_lock, flags);
	pc_conf_set(PC_CONF_FINALI_LOCK, PC_CONF_FINALI_LOCK_KEY);
	pc_conf_set(PC_CONF_FINALI_LOCK, PC_CONF_FINALI_LOCK_KEY);
	x = irqmap[read_pc_conf_nybble(PC_CONF_FINALI_PCI_INTX_RT1, pirq - 1)];
	x = irqmap[read_pc_conf_nybble(PC_CONF_FINALI_PCI_INTX_RT1, index)];
	pc_conf_set(PC_CONF_FINALI_LOCK, 0);
	pc_conf_set(PC_CONF_FINALI_LOCK, 0);
	raw_spin_unlock_irqrestore(&pc_conf_lock, flags);
	raw_spin_unlock_irqrestore(&pc_conf_lock, flags);
	return x;
	return x;
@@ -278,13 +373,15 @@ static int pirq_finali_set(struct pci_dev *router, struct pci_dev *dev,
	};
	};
	u8 val = irqmap[irq];
	u8 val = irqmap[irq];
	unsigned long flags;
	unsigned long flags;
	u8 index;


	if (!val)
	if (!val)
		return 0;
		return 0;


	index = (pirq & 1) << 1 | (pirq & 8) >> 3;
	raw_spin_lock_irqsave(&pc_conf_lock, flags);
	raw_spin_lock_irqsave(&pc_conf_lock, flags);
	pc_conf_set(PC_CONF_FINALI_LOCK, PC_CONF_FINALI_LOCK_KEY);
	pc_conf_set(PC_CONF_FINALI_LOCK, PC_CONF_FINALI_LOCK_KEY);
	write_pc_conf_nybble(PC_CONF_FINALI_PCI_INTX_RT1, pirq - 1, val);
	write_pc_conf_nybble(PC_CONF_FINALI_PCI_INTX_RT1, index, val);
	pc_conf_set(PC_CONF_FINALI_LOCK, 0);
	pc_conf_set(PC_CONF_FINALI_LOCK, 0);
	raw_spin_unlock_irqrestore(&pc_conf_lock, flags);
	raw_spin_unlock_irqrestore(&pc_conf_lock, flags);
	return 1;
	return 1;
@@ -293,7 +390,7 @@ static int pirq_finali_set(struct pci_dev *router, struct pci_dev *dev,
static int pirq_finali_lvl(struct pci_dev *router, struct pci_dev *dev,
static int pirq_finali_lvl(struct pci_dev *router, struct pci_dev *dev,
			   int pirq, int irq)
			   int pirq, int irq)
{
{
	u8 mask = ~(1u << (pirq - 1));
	u8 mask = ~((pirq & 0xf0u) >> 4);
	unsigned long flags;
	unsigned long flags;
	u8 trig;
	u8 trig;


@@ -579,6 +676,81 @@ static int pirq_cyrix_set(struct pci_dev *router, struct pci_dev *dev, int pirq,
	return 1;
	return 1;
}
}



/*
 *	PIRQ routing for the SiS85C497 AT Bus Controller & Megacell (ATM)
 *	ISA bridge used with the SiS 85C496/497 486 Green PC VESA/ISA/PCI
 *	Chipset.
 *
 *	There are four PCI INTx#-to-IRQ Link registers provided in the
 *	SiS85C497 part of the peculiar combined 85C496/497 configuration
 *	space decoded by the SiS85C496 PCI & CPU Memory Controller (PCM)
 *	host bridge, at 0xc0/0xc1/0xc2/0xc3 respectively for the PCI INT
 *	A/B/C/D lines.  Bit 7 enables the respective link if set and bits
 *	3:0 select the 8259A IRQ line as follows:
 *
 *	0000 : Reserved
 *	0001 : Reserved
 *	0010 : Reserved
 *	0011 : IRQ3
 *	0100 : IRQ4
 *	0101 : IRQ5
 *	0110 : IRQ6
 *	0111 : IRQ7
 *	1000 : Reserved
 *	1001 : IRQ9
 *	1010 : IRQ10
 *	1011 : IRQ11
 *	1100 : IRQ12
 *	1101 : Reserved
 *	1110 : IRQ14
 *	1111 : IRQ15
 *
 *	We avoid using a reserved value for disabled links, hence the
 *	choice of IRQ15 for that case.
 *
 *	References:
 *
 *	"486 Green PC VESA/ISA/PCI Chipset, SiS 85C496/497", Rev 3.0,
 *	Silicon Integrated Systems Corp., July 1995
 */

#define PCI_SIS497_INTA_TO_IRQ_LINK	0xc0u

#define PIRQ_SIS497_IRQ_MASK		0x0fu
#define PIRQ_SIS497_IRQ_ENABLE		0x80u

static int pirq_sis497_get(struct pci_dev *router, struct pci_dev *dev,
			   int pirq)
{
	int reg;
	u8 x;

	reg = pirq;
	if (reg >= 1 && reg <= 4)
		reg += PCI_SIS497_INTA_TO_IRQ_LINK - 1;

	pci_read_config_byte(router, reg, &x);
	return (x & PIRQ_SIS497_IRQ_ENABLE) ? (x & PIRQ_SIS497_IRQ_MASK) : 0;
}

static int pirq_sis497_set(struct pci_dev *router, struct pci_dev *dev,
			   int pirq, int irq)
{
	int reg;
	u8 x;

	reg = pirq;
	if (reg >= 1 && reg <= 4)
		reg += PCI_SIS497_INTA_TO_IRQ_LINK - 1;

	pci_read_config_byte(router, reg, &x);
	x &= ~(PIRQ_SIS497_IRQ_MASK | PIRQ_SIS497_IRQ_ENABLE);
	x |= irq ? (PIRQ_SIS497_IRQ_ENABLE | irq) : PIRQ_SIS497_IRQ_MASK;
	pci_write_config_byte(router, reg, x);
	return 1;
}

/*
/*
 *	PIRQ routing for SiS 85C503 router used in several SiS chipsets.
 *	PIRQ routing for SiS 85C503 router used in several SiS chipsets.
 *	We have to deal with the following issues here:
 *	We have to deal with the following issues here:
@@ -640,11 +812,12 @@ static int pirq_cyrix_set(struct pci_dev *router, struct pci_dev *dev, int pirq,
 *				bit 6-4 are probably unused, not like 5595
 *				bit 6-4 are probably unused, not like 5595
 */
 */


#define PIRQ_SIS_IRQ_MASK	0x0f
#define PIRQ_SIS503_IRQ_MASK	0x0f
#define PIRQ_SIS_IRQ_DISABLE	0x80
#define PIRQ_SIS503_IRQ_DISABLE	0x80
#define PIRQ_SIS_USB_ENABLE	0x40
#define PIRQ_SIS503_USB_ENABLE	0x40


static int pirq_sis_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
static int pirq_sis503_get(struct pci_dev *router, struct pci_dev *dev,
			   int pirq)
{
{
	u8 x;
	u8 x;
	int reg;
	int reg;
@@ -653,10 +826,11 @@ static int pirq_sis_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
	if (reg >= 0x01 && reg <= 0x04)
	if (reg >= 0x01 && reg <= 0x04)
		reg += 0x40;
		reg += 0x40;
	pci_read_config_byte(router, reg, &x);
	pci_read_config_byte(router, reg, &x);
	return (x & PIRQ_SIS_IRQ_DISABLE) ? 0 : (x & PIRQ_SIS_IRQ_MASK);
	return (x & PIRQ_SIS503_IRQ_DISABLE) ? 0 : (x & PIRQ_SIS503_IRQ_MASK);
}
}


static int pirq_sis_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
static int pirq_sis503_set(struct pci_dev *router, struct pci_dev *dev,
			   int pirq, int irq)
{
{
	u8 x;
	u8 x;
	int reg;
	int reg;
@@ -665,8 +839,8 @@ static int pirq_sis_set(struct pci_dev *router, struct pci_dev *dev, int pirq, i
	if (reg >= 0x01 && reg <= 0x04)
	if (reg >= 0x01 && reg <= 0x04)
		reg += 0x40;
		reg += 0x40;
	pci_read_config_byte(router, reg, &x);
	pci_read_config_byte(router, reg, &x);
	x &= ~(PIRQ_SIS_IRQ_MASK | PIRQ_SIS_IRQ_DISABLE);
	x &= ~(PIRQ_SIS503_IRQ_MASK | PIRQ_SIS503_IRQ_DISABLE);
	x |= irq ? irq: PIRQ_SIS_IRQ_DISABLE;
	x |= irq ? irq : PIRQ_SIS503_IRQ_DISABLE;
	pci_write_config_byte(router, reg, x);
	pci_write_config_byte(router, reg, x);
	return 1;
	return 1;
}
}
@@ -958,14 +1132,20 @@ static __init int serverworks_router_probe(struct irq_router *r,


static __init int sis_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
static __init int sis_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
{
{
	if (device != PCI_DEVICE_ID_SI_503)
	switch (device) {
		return 0;
	case PCI_DEVICE_ID_SI_496:

		r->name = "SiS85C497";
	r->name = "SIS";
		r->get = pirq_sis497_get;
	r->get = pirq_sis_get;
		r->set = pirq_sis497_set;
	r->set = pirq_sis_set;
		return 1;
	case PCI_DEVICE_ID_SI_503:
		r->name = "SiS85C503";
		r->get = pirq_sis503_get;
		r->set = pirq_sis503_set;
		return 1;
		return 1;
	}
	}
	return 0;
}


static __init int cyrix_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
static __init int cyrix_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
{
{
@@ -1084,10 +1264,32 @@ static struct pci_dev *pirq_router_dev;
 *	chipset" ?
 *	chipset" ?
 */
 */


static bool __init pirq_try_router(struct irq_router *r,
				   struct irq_routing_table *rt,
				   struct pci_dev *dev)
{
	struct irq_router_handler *h;

	DBG(KERN_DEBUG "PCI: Trying IRQ router for [%04x:%04x]\n",
	    dev->vendor, dev->device);

	for (h = pirq_routers; h->vendor; h++) {
		/* First look for a router match */
		if (rt->rtr_vendor == h->vendor &&
		    h->probe(r, dev, rt->rtr_device))
			return true;
		/* Fall back to a device match */
		if (dev->vendor == h->vendor &&
		    h->probe(r, dev, dev->device))
			return true;
	}
	return false;
}

static void __init pirq_find_router(struct irq_router *r)
static void __init pirq_find_router(struct irq_router *r)
{
{
	struct irq_routing_table *rt = pirq_table;
	struct irq_routing_table *rt = pirq_table;
	struct irq_router_handler *h;
	struct pci_dev *dev;


#ifdef CONFIG_PCI_BIOS
#ifdef CONFIG_PCI_BIOS
	if (!rt->signature) {
	if (!rt->signature) {
@@ -1106,50 +1308,94 @@ static void __init pirq_find_router(struct irq_router *r)
	DBG(KERN_DEBUG "PCI: Attempting to find IRQ router for [%04x:%04x]\n",
	DBG(KERN_DEBUG "PCI: Attempting to find IRQ router for [%04x:%04x]\n",
	    rt->rtr_vendor, rt->rtr_device);
	    rt->rtr_vendor, rt->rtr_device);


	pirq_router_dev = pci_get_domain_bus_and_slot(0, rt->rtr_bus,
	/* Use any vendor:device provided by the routing table or try all.  */
	if (rt->rtr_vendor) {
		dev = pci_get_domain_bus_and_slot(0, rt->rtr_bus,
						  rt->rtr_devfn);
						  rt->rtr_devfn);
	if (!pirq_router_dev) {
		if (dev && pirq_try_router(r, rt, dev))
		DBG(KERN_DEBUG "PCI: Interrupt router not found at "
			pirq_router_dev = dev;
			"%02x:%02x\n", rt->rtr_bus, rt->rtr_devfn);
	} else {
		return;
		dev = NULL;
	}
		for_each_pci_dev(dev) {

			if (pirq_try_router(r, rt, dev)) {
	for (h = pirq_routers; h->vendor; h++) {
				pirq_router_dev = dev;
		/* First look for a router match */
		if (rt->rtr_vendor == h->vendor &&
			h->probe(r, pirq_router_dev, rt->rtr_device))
			break;
		/* Fall back to a device match */
		if (pirq_router_dev->vendor == h->vendor &&
			h->probe(r, pirq_router_dev, pirq_router_dev->device))
				break;
				break;
			}
			}
		}
	}

	if (pirq_router_dev)
		dev_info(&pirq_router_dev->dev, "%s IRQ router [%04x:%04x]\n",
		dev_info(&pirq_router_dev->dev, "%s IRQ router [%04x:%04x]\n",
			 pirq_router.name,
			 pirq_router.name,
			 pirq_router_dev->vendor, pirq_router_dev->device);
			 pirq_router_dev->vendor, pirq_router_dev->device);
	else
		DBG(KERN_DEBUG "PCI: Interrupt router not found at "
		    "%02x:%02x\n", rt->rtr_bus, rt->rtr_devfn);


	/* The device remains referenced for the kernel lifetime */
	/* The device remains referenced for the kernel lifetime */
}
}


static struct irq_info *pirq_get_info(struct pci_dev *dev)
/*
 * We're supposed to match on the PCI device only and not the function,
 * but some BIOSes build their tables with the PCI function included
 * for motherboard devices, so if a complete match is found, then give
 * it precedence over a slot match.
 */
static struct irq_info *pirq_get_dev_info(struct pci_dev *dev)
{
{
	struct irq_routing_table *rt = pirq_table;
	struct irq_routing_table *rt = pirq_table;
	int entries = (rt->size - sizeof(struct irq_routing_table)) /
	int entries = (rt->size - sizeof(struct irq_routing_table)) /
		sizeof(struct irq_info);
		sizeof(struct irq_info);
	struct irq_info *slotinfo = NULL;
	struct irq_info *info;
	struct irq_info *info;


	for (info = rt->slots; entries--; info++)
	for (info = rt->slots; entries--; info++)
		if (info->bus == dev->bus->number &&
		if (info->bus == dev->bus->number) {
			if (info->devfn == dev->devfn)
				return info;
			if (!slotinfo &&
			    PCI_SLOT(info->devfn) == PCI_SLOT(dev->devfn))
			    PCI_SLOT(info->devfn) == PCI_SLOT(dev->devfn))
				slotinfo = info;
		}
	return slotinfo;
}

/*
 * Buses behind bridges are typically not listed in the PIRQ routing table.
 * Do the usual dance then and walk the tree of bridges up adjusting the
 * pin number accordingly on the way until the originating root bus device
 * has been reached and then use its routing information.
 */
static struct irq_info *pirq_get_info(struct pci_dev *dev, u8 *pin)
{
	struct pci_dev *temp_dev = dev;
	struct irq_info *info;
	u8 temp_pin = *pin;
	u8 dpin = temp_pin;

	info = pirq_get_dev_info(dev);
	while (!info && temp_dev->bus->parent) {
		struct pci_dev *bridge = temp_dev->bus->self;

		temp_pin = pci_swizzle_interrupt_pin(temp_dev, temp_pin);
		info = pirq_get_dev_info(bridge);
		if (info)
			dev_warn(&dev->dev,
				 "using bridge %s INT %c to get INT %c\n",
				 pci_name(bridge),
				 'A' + temp_pin - 1, 'A' + dpin - 1);

		temp_dev = bridge;
	}
	*pin = temp_pin;
	return info;
	return info;
	return NULL;
}
}


static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
{
{
	u8 pin;
	struct irq_info *info;
	struct irq_info *info;
	int i, pirq, newirq;
	int i, pirq, newirq;
	u8 dpin, pin;
	int irq = 0;
	int irq = 0;
	u32 mask;
	u32 mask;
	struct irq_router *r = &pirq_router;
	struct irq_router *r = &pirq_router;
@@ -1157,8 +1403,8 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
	char *msg = NULL;
	char *msg = NULL;


	/* Find IRQ pin */
	/* Find IRQ pin */
	pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
	pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &dpin);
	if (!pin) {
	if (!dpin) {
		dev_dbg(&dev->dev, "no interrupt pin\n");
		dev_dbg(&dev->dev, "no interrupt pin\n");
		return 0;
		return 0;
	}
	}
@@ -1171,20 +1417,21 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
	if (!pirq_table)
	if (!pirq_table)
		return 0;
		return 0;


	info = pirq_get_info(dev);
	pin = dpin;
	info = pirq_get_info(dev, &pin);
	if (!info) {
	if (!info) {
		dev_dbg(&dev->dev, "PCI INT %c not found in routing table\n",
		dev_dbg(&dev->dev, "PCI INT %c not found in routing table\n",
			'A' + pin - 1);
			'A' + dpin - 1);
		return 0;
		return 0;
	}
	}
	pirq = info->irq[pin - 1].link;
	pirq = info->irq[pin - 1].link;
	mask = info->irq[pin - 1].bitmap;
	mask = info->irq[pin - 1].bitmap;
	if (!pirq) {
	if (!pirq) {
		dev_dbg(&dev->dev, "PCI INT %c not routed\n", 'A' + pin - 1);
		dev_dbg(&dev->dev, "PCI INT %c not routed\n", 'A' + dpin - 1);
		return 0;
		return 0;
	}
	}
	dev_dbg(&dev->dev, "PCI INT %c -> PIRQ %02x, mask %04x, excl %04x",
	dev_dbg(&dev->dev, "PCI INT %c -> PIRQ %02x, mask %04x, excl %04x",
		'A' + pin - 1, pirq, mask, pirq_table->exclusive_irqs);
		'A' + dpin - 1, pirq, mask, pirq_table->exclusive_irqs);
	mask &= pcibios_irq_mask;
	mask &= pcibios_irq_mask;


	/* Work around broken HP Pavilion Notebooks which assign USB to
	/* Work around broken HP Pavilion Notebooks which assign USB to
@@ -1226,7 +1473,7 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
				newirq = i;
				newirq = i;
		}
		}
	}
	}
	dev_dbg(&dev->dev, "PCI INT %c -> newirq %d", 'A' + pin - 1, newirq);
	dev_dbg(&dev->dev, "PCI INT %c -> newirq %d", 'A' + dpin - 1, newirq);


	/* Check if it is hardcoded */
	/* Check if it is hardcoded */
	if ((pirq & 0xf0) == 0xf0) {
	if ((pirq & 0xf0) == 0xf0) {
@@ -1260,15 +1507,17 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
			return 0;
			return 0;
		}
		}
	}
	}
	dev_info(&dev->dev, "%s PCI INT %c -> IRQ %d\n", msg, 'A' + pin - 1, irq);
	dev_info(&dev->dev, "%s PCI INT %c -> IRQ %d\n",
		 msg, 'A' + dpin - 1, irq);


	/* Update IRQ for all devices with the same pirq value */
	/* Update IRQ for all devices with the same pirq value */
	for_each_pci_dev(dev2) {
	for_each_pci_dev(dev2) {
		pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin);
		pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &dpin);
		if (!pin)
		if (!dpin)
			continue;
			continue;


		info = pirq_get_info(dev2);
		pin = dpin;
		info = pirq_get_info(dev2, &pin);
		if (!info)
		if (!info)
			continue;
			continue;
		if (info->irq[pin - 1].link == pirq) {
		if (info->irq[pin - 1].link == pirq) {