2005-04-17 02:20:36 +04:00
/*
* Low - Level PCI Support for PC - - Routing of Interrupts
*
* ( c ) 1999 - - 2000 Martin Mares < mj @ ucw . cz >
*/
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/pci.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/interrupt.h>
# include <linux/dmi.h>
2008-06-07 16:14:35 +04:00
# include <linux/io.h>
# include <linux/smp.h>
2005-04-17 02:20:36 +04:00
# include <asm/io_apic.h>
2005-09-30 20:34:42 +04:00
# include <linux/irq.h>
2005-04-17 02:20:36 +04:00
# include <linux/acpi.h>
2008-12-27 16:02:28 +03:00
# include <asm/pci_x86.h>
2005-04-17 02:20:36 +04:00
# define PIRQ_SIGNATURE (('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24))
# define PIRQ_VERSION 0x0100
static int broken_hp_bios_irq9 ;
static int acer_tm360_irqrouting ;
static struct irq_routing_table * pirq_table ;
static int pirq_enable_irq ( struct pci_dev * dev ) ;
/*
* Never use : 0 , 1 , 2 ( timer , keyboard , and cascade )
* Avoid using : 13 , 14 and 15 ( FP error and IDE ) .
* Penalize : 3 , 4 , 6 , 7 , 12 ( known ISA uses : serial , floppy , parallel and mouse )
*/
unsigned int pcibios_irq_mask = 0xfff8 ;
static int pirq_penalty [ 16 ] = {
1000000 , 1000000 , 1000000 , 1000 , 1000 , 0 , 1000 , 1000 ,
0 , 0 , 0 , 0 , 1000 , 100000 , 100000 , 100000
} ;
struct irq_router {
char * name ;
u16 vendor , device ;
int ( * get ) ( struct pci_dev * router , struct pci_dev * dev , int pirq ) ;
2008-05-13 20:38:56 +04:00
int ( * set ) ( struct pci_dev * router , struct pci_dev * dev , int pirq ,
int new ) ;
2005-04-17 02:20:36 +04:00
} ;
struct irq_router_handler {
u16 vendor ;
int ( * probe ) ( struct irq_router * r , struct pci_dev * router , u16 device ) ;
} ;
2009-08-29 19:47:33 +04:00
int ( * pcibios_enable_irq ) ( struct pci_dev * dev ) = pirq_enable_irq ;
2005-07-28 07:02:00 +04:00
void ( * pcibios_disable_irq ) ( struct pci_dev * dev ) = NULL ;
2005-04-17 02:20:36 +04:00
2005-03-22 07:20:42 +03:00
/*
* Check passed address for the PCI IRQ Routing Table signature
* and perform checksum verification .
*/
2008-06-07 16:14:35 +04:00
static inline struct irq_routing_table * pirq_check_routing_table ( u8 * addr )
2005-03-22 07:20:42 +03:00
{
struct irq_routing_table * rt ;
int i ;
u8 sum ;
rt = ( struct irq_routing_table * ) addr ;
if ( rt - > signature ! = PIRQ_SIGNATURE | |
rt - > version ! = PIRQ_VERSION | |
rt - > size % 16 | |
rt - > size < sizeof ( struct irq_routing_table ) )
return NULL ;
sum = 0 ;
2008-06-07 16:14:35 +04:00
for ( i = 0 ; i < rt - > size ; i + + )
2005-03-22 07:20:42 +03:00
sum + = addr [ i ] ;
if ( ! sum ) {
2008-05-13 20:38:56 +04:00
DBG ( KERN_DEBUG " PCI: Interrupt Routing Table found at 0x%p \n " ,
rt ) ;
2005-03-22 07:20:42 +03:00
return rt ;
}
return NULL ;
}
2005-04-17 02:20:36 +04:00
/*
* Search 0xf0000 - - 0xfffff for the PCI IRQ Routing Table .
*/
static struct irq_routing_table * __init pirq_find_routing_table ( void )
{
u8 * addr ;
struct irq_routing_table * rt ;
2005-03-22 07:20:42 +03:00
if ( pirq_table_addr ) {
rt = pirq_check_routing_table ( ( u8 * ) __va ( pirq_table_addr ) ) ;
if ( rt )
return rt ;
printk ( KERN_WARNING " PCI: PIRQ table NOT found at pirqaddr \n " ) ;
}
2008-06-07 16:14:35 +04:00
for ( addr = ( u8 * ) __va ( 0xf0000 ) ; addr < ( u8 * ) __va ( 0x100000 ) ; addr + = 16 ) {
2005-03-22 07:20:42 +03:00
rt = pirq_check_routing_table ( addr ) ;
if ( rt )
2005-04-17 02:20:36 +04:00
return rt ;
}
return NULL ;
}
/*
* If we have a IRQ routing table , use it to search for peer host
* bridges . It ' s a gross hack , but since there are no other known
* ways how to get a list of buses , we have to go this way .
*/
static void __init pirq_peer_trick ( void )
{
struct irq_routing_table * rt = pirq_table ;
u8 busmap [ 256 ] ;
int i ;
struct irq_info * e ;
memset ( busmap , 0 , sizeof ( busmap ) ) ;
2008-06-07 16:14:35 +04:00
for ( i = 0 ; i < ( rt - > size - sizeof ( struct irq_routing_table ) ) / sizeof ( struct irq_info ) ; i + + ) {
2005-04-17 02:20:36 +04:00
e = & rt - > slots [ i ] ;
# ifdef DEBUG
{
int j ;
2005-12-01 20:01:28 +03:00
DBG ( KERN_DEBUG " %02x:%02x slot=%02x " , e - > bus , e - > devfn / 8 , e - > slot ) ;
2008-06-07 16:14:35 +04:00
for ( j = 0 ; j < 4 ; j + + )
2005-04-17 02:20:36 +04:00
DBG ( " %d:%02x/%04x " , j , e - > irq [ j ] . link , e - > irq [ j ] . bitmap ) ;
DBG ( " \n " ) ;
}
# endif
busmap [ e - > bus ] = 1 ;
}
2008-06-07 16:14:35 +04:00
for ( i = 1 ; i < 256 ; i + + ) {
x86: get mp_bus_to_node early
Currently, on an amd k8 system with multi ht chains, the numa_node of
pci devices under /sys/devices/pci0000:80/* is always 0, even if that
chain is on node 1 or 2 or 3.
Workaround: pcibus_to_node(bus) is used when we want to get the node that
pci_device is on.
In struct device, we already have numa_node member, and we could use
dev_to_node()/set_dev_node() to get and set numa_node in the device.
set_dev_node is called in pci_device_add() with pcibus_to_node(bus),
and pcibus_to_node uses bus->sysdata for nodeid.
The problem is when pci_add_device is called, bus->sysdata is not assigned
correct nodeid yet. The result is that numa_node will always be 0.
pcibios_scan_root and pci_scan_root could take sysdata. So we need to get
mp_bus_to_node mapping before these two are called, and thus
get_mp_bus_to_node could get correct node for sysdata in root bus.
In scanning of the root bus, all child busses will take parent bus sysdata.
So all pci_device->dev.numa_node will be assigned correctly and automatically.
Later we could use dev_to_node(&pci_dev->dev) to get numa_node, and we
could also could make other bus specific device get the correct numa_node
too.
This is an updated version of pci_sysdata and Jeff's pci_domain patch.
[ mingo@elte.hu: build fix ]
Signed-off-by: Yinghai Lu <yinghai.lu@sun.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2008-02-19 14:20:09 +03:00
int node ;
2005-04-17 02:20:36 +04:00
if ( ! busmap [ i ] | | pci_find_bus ( 0 , i ) )
continue ;
x86: get mp_bus_to_node early
Currently, on an amd k8 system with multi ht chains, the numa_node of
pci devices under /sys/devices/pci0000:80/* is always 0, even if that
chain is on node 1 or 2 or 3.
Workaround: pcibus_to_node(bus) is used when we want to get the node that
pci_device is on.
In struct device, we already have numa_node member, and we could use
dev_to_node()/set_dev_node() to get and set numa_node in the device.
set_dev_node is called in pci_device_add() with pcibus_to_node(bus),
and pcibus_to_node uses bus->sysdata for nodeid.
The problem is when pci_add_device is called, bus->sysdata is not assigned
correct nodeid yet. The result is that numa_node will always be 0.
pcibios_scan_root and pci_scan_root could take sysdata. So we need to get
mp_bus_to_node mapping before these two are called, and thus
get_mp_bus_to_node could get correct node for sysdata in root bus.
In scanning of the root bus, all child busses will take parent bus sysdata.
So all pci_device->dev.numa_node will be assigned correctly and automatically.
Later we could use dev_to_node(&pci_dev->dev) to get numa_node, and we
could also could make other bus specific device get the correct numa_node
too.
This is an updated version of pci_sysdata and Jeff's pci_domain patch.
[ mingo@elte.hu: build fix ]
Signed-off-by: Yinghai Lu <yinghai.lu@sun.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2008-02-19 14:20:09 +03:00
node = get_mp_bus_to_node ( i ) ;
if ( pci_scan_bus_on_node ( i , & pci_root_ops , node ) )
2007-08-11 00:01:19 +04:00
printk ( KERN_INFO " PCI: Discovered primary peer "
" bus %02x [IRQ] \n " , i ) ;
2005-04-17 02:20:36 +04:00
}
pcibios_last_bus = - 1 ;
}
/*
* Code for querying and setting of IRQ routes on various interrupt routers .
*/
void eisa_set_level_irq ( unsigned int irq )
{
unsigned char mask = 1 < < ( irq & 7 ) ;
unsigned int port = 0x4d0 + ( irq > > 3 ) ;
unsigned char val ;
static u16 eisa_irq_mask ;
if ( irq > = 16 | | ( 1 < < irq ) & eisa_irq_mask )
return ;
eisa_irq_mask | = ( 1 < < irq ) ;
2005-12-01 20:01:28 +03:00
printk ( KERN_DEBUG " PCI: setting IRQ %u as level-triggered \n " , irq ) ;
2005-04-17 02:20:36 +04:00
val = inb ( port ) ;
if ( ! ( val & mask ) ) {
2005-12-01 20:01:28 +03:00
DBG ( KERN_DEBUG " -> edge " ) ;
2005-04-17 02:20:36 +04:00
outb ( val | mask , port ) ;
}
}
/*
2007-10-20 03:13:56 +04:00
* Common IRQ routing practice : nibbles in config space ,
2005-04-17 02:20:36 +04:00
* offset by some magic constant .
*/
static unsigned int read_config_nybble ( struct pci_dev * router , unsigned offset , unsigned nr )
{
u8 x ;
unsigned reg = offset + ( nr > > 1 ) ;
pci_read_config_byte ( router , reg , & x ) ;
return ( nr & 1 ) ? ( x > > 4 ) : ( x & 0xf ) ;
}
2008-05-13 20:38:56 +04:00
static void write_config_nybble ( struct pci_dev * router , unsigned offset ,
unsigned nr , unsigned int val )
2005-04-17 02:20:36 +04:00
{
u8 x ;
unsigned reg = offset + ( nr > > 1 ) ;
pci_read_config_byte ( router , reg , & x ) ;
x = ( nr & 1 ) ? ( ( x & 0x0f ) | ( val < < 4 ) ) : ( ( x & 0xf0 ) | val ) ;
pci_write_config_byte ( router , reg , x ) ;
}
/*
* ALI pirq entries are damn ugly , and completely undocumented .
* This has been figured out from pirq tables , and it ' s not a pretty
* picture .
*/
static int pirq_ali_get ( struct pci_dev * router , struct pci_dev * dev , int pirq )
{
2006-06-23 13:04:28 +04:00
static const unsigned char irqmap [ 16 ] = { 0 , 9 , 3 , 10 , 4 , 5 , 7 , 6 , 1 , 11 , 0 , 12 , 0 , 14 , 0 , 15 } ;
2005-04-17 02:20:36 +04:00
2008-03-31 06:22:53 +04:00
WARN_ON_ONCE ( pirq > 16 ) ;
2005-04-17 02:20:36 +04:00
return irqmap [ read_config_nybble ( router , 0x48 , pirq - 1 ) ] ;
}
static int pirq_ali_set ( struct pci_dev * router , struct pci_dev * dev , int pirq , int irq )
{
2006-06-23 13:04:28 +04:00
static const unsigned char irqmap [ 16 ] = { 0 , 8 , 0 , 2 , 4 , 5 , 7 , 6 , 0 , 1 , 3 , 9 , 11 , 0 , 13 , 15 } ;
2005-04-17 02:20:36 +04:00
unsigned int val = irqmap [ irq ] ;
2008-01-30 15:32:00 +03:00
2008-03-31 06:22:53 +04:00
WARN_ON_ONCE ( pirq > 16 ) ;
2005-04-17 02:20:36 +04:00
if ( val ) {
write_config_nybble ( router , 0x48 , pirq - 1 , val ) ;
return 1 ;
}
return 0 ;
}
/*
* The Intel PIIX4 pirq rules are fairly simple : " pirq " is
* just a pointer to the config space .
*/
static int pirq_piix_get ( struct pci_dev * router , struct pci_dev * dev , int pirq )
{
u8 x ;
pci_read_config_byte ( router , pirq , & x ) ;
return ( x < 16 ) ? x : 0 ;
}
static int pirq_piix_set ( struct pci_dev * router , struct pci_dev * dev , int pirq , int irq )
{
pci_write_config_byte ( router , pirq , irq ) ;
return 1 ;
}
/*
* The VIA pirq rules are nibble - based , like ALI ,
* but without the ugly irq number munging .
* However , PIRQD is in the upper instead of lower 4 bits .
*/
static int pirq_via_get ( struct pci_dev * router , struct pci_dev * dev , int pirq )
{
return read_config_nybble ( router , 0x55 , pirq = = 4 ? 5 : pirq ) ;
}
static int pirq_via_set ( struct pci_dev * router , struct pci_dev * dev , int pirq , int irq )
{
write_config_nybble ( router , 0x55 , pirq = = 4 ? 5 : pirq , irq ) ;
return 1 ;
}
2005-06-23 11:08:29 +04:00
/*
* The VIA pirq rules are nibble - based , like ALI ,
* but without the ugly irq number munging .
* However , for 82 C586 , nibble map is different .
*/
static int pirq_via586_get ( struct pci_dev * router , struct pci_dev * dev , int pirq )
{
2006-11-14 13:03:25 +03:00
static const unsigned int pirqmap [ 5 ] = { 3 , 2 , 5 , 1 , 1 } ;
2008-01-30 15:32:00 +03:00
2008-03-31 06:22:53 +04:00
WARN_ON_ONCE ( pirq > 5 ) ;
2005-06-23 11:08:29 +04:00
return read_config_nybble ( router , 0x55 , pirqmap [ pirq - 1 ] ) ;
}
static int pirq_via586_set ( struct pci_dev * router , struct pci_dev * dev , int pirq , int irq )
{
2006-11-14 13:03:25 +03:00
static const unsigned int pirqmap [ 5 ] = { 3 , 2 , 5 , 1 , 1 } ;
2008-01-30 15:32:00 +03:00
2008-03-31 06:22:53 +04:00
WARN_ON_ONCE ( pirq > 5 ) ;
2005-06-23 11:08:29 +04:00
write_config_nybble ( router , 0x55 , pirqmap [ pirq - 1 ] , irq ) ;
return 1 ;
}
2005-04-17 02:20:36 +04:00
/*
* ITE 8330 G pirq rules are nibble - based
* FIXME : pirqmap may be { 1 , 0 , 3 , 2 } ,
* 2 + 3 are both mapped to irq 9 on my system
*/
static int pirq_ite_get ( struct pci_dev * router , struct pci_dev * dev , int pirq )
{
2006-06-23 13:04:28 +04:00
static const unsigned char pirqmap [ 4 ] = { 1 , 0 , 2 , 3 } ;
2008-01-30 15:32:00 +03:00
2008-03-31 06:22:53 +04:00
WARN_ON_ONCE ( pirq > 4 ) ;
2008-06-07 16:14:35 +04:00
return read_config_nybble ( router , 0x43 , pirqmap [ pirq - 1 ] ) ;
2005-04-17 02:20:36 +04:00
}
static int pirq_ite_set ( struct pci_dev * router , struct pci_dev * dev , int pirq , int irq )
{
2006-06-23 13:04:28 +04:00
static const unsigned char pirqmap [ 4 ] = { 1 , 0 , 2 , 3 } ;
2008-01-30 15:32:00 +03:00
2008-03-31 06:22:53 +04:00
WARN_ON_ONCE ( pirq > 4 ) ;
2005-04-17 02:20:36 +04:00
write_config_nybble ( router , 0x43 , pirqmap [ pirq - 1 ] , irq ) ;
return 1 ;
}
/*
* OPTI : high four bits are nibble pointer . .
* I wonder what the low bits do ?
*/
static int pirq_opti_get ( struct pci_dev * router , struct pci_dev * dev , int pirq )
{
return read_config_nybble ( router , 0xb8 , pirq > > 4 ) ;
}
static int pirq_opti_set ( struct pci_dev * router , struct pci_dev * dev , int pirq , int irq )
{
write_config_nybble ( router , 0xb8 , pirq > > 4 , irq ) ;
return 1 ;
}
/*
* Cyrix : nibble offset 0x5C
2008-06-07 16:14:35 +04:00
* 0x5C bits 7 : 4 is INTB bits 3 : 0 is INTA
2005-04-17 02:20:36 +04:00
* 0x5D bits 7 : 4 is INTD bits 3 : 0 is INTC
*/
static int pirq_cyrix_get ( struct pci_dev * router , struct pci_dev * dev , int pirq )
{
return read_config_nybble ( router , 0x5C , ( pirq - 1 ) ^ 1 ) ;
}
static int pirq_cyrix_set ( struct pci_dev * router , struct pci_dev * dev , int pirq , int irq )
{
write_config_nybble ( router , 0x5C , ( pirq - 1 ) ^ 1 , irq ) ;
return 1 ;
}
/*
* PIRQ routing for SiS 85 C503 router used in several SiS chipsets .
* We have to deal with the following issues here :
* - vendors have different ideas about the meaning of link values
* - some onboard devices ( integrated in the chipset ) have special
* links and are thus routed differently ( i . e . not via PCI INTA - INTD )
* - different revision of the router have a different layout for
* the routing registers , particularly for the onchip devices
*
* For all routing registers the common thing is we have one byte
* per routeable link which is defined as :
* bit 7 IRQ mapping enabled ( 0 ) or disabled ( 1 )
* bits [ 6 : 4 ] reserved ( sometimes used for onchip devices )
* bits [ 3 : 0 ] IRQ to map to
* allowed : 3 - 7 , 9 - 12 , 14 - 15
* reserved : 0 , 1 , 2 , 8 , 13
*
* The config - space registers located at 0x41 / 0x42 / 0x43 / 0x44 are
* always used to route the normal PCI INT A / B / C / D respectively .
* Apparently there are systems implementing PCI routing table using
* link values 0x01 - 0x04 and others using 0x41 - 0x44 for PCI INTA . . D .
* We try our best to handle both link mappings .
2008-06-07 16:14:35 +04:00
*
2005-04-17 02:20:36 +04:00
* Currently ( 2003 - 05 - 21 ) it appears most SiS chipsets follow the
* definition of routing registers from the SiS - 5595 southbridge .
* According to the SiS 5595 datasheets the revision id ' s of the
* router ( ISA - bridge ) should be 0x01 or 0xb0 .
*
* Furthermore we ' ve also seen lspci dumps with revision 0x00 and 0xb1 .
* Looks like these are used in a number of SiS 5 xx / 6 xx / 7 xx chipsets .
* They seem to work with the current routing code . However there is
* some concern because of the two USB - OHCI HCs ( original SiS 5595
* had only one ) . YMMV .
*
* Onchip routing for router rev - id 0x01 / 0xb0 and probably 0x00 / 0xb1 :
*
* 0x61 : IDEIRQ :
* bits [ 6 : 5 ] must be written 01
* bit 4 channel - select primary ( 0 ) , secondary ( 1 )
*
* 0x62 : USBIRQ :
* bit 6 OHCI function disabled ( 0 ) , enabled ( 1 )
2008-06-07 16:14:35 +04:00
*
2005-04-17 02:20:36 +04:00
* 0x6a : ACPI / SCI IRQ : bits 4 - 6 reserved
*
* 0x7e : Data Acq . Module IRQ - bits 4 - 6 reserved
*
* We support USBIRQ ( in addition to INTA - INTD ) and keep the
* IDE , ACPI and DAQ routing untouched as set by the BIOS .
*
* Currently the only reported exception is the new SiS 65 x chipset
* which includes the SiS 69 x southbridge . Here we have the 85 C503
* router revision 0x04 and there are changes in the register layout
* mostly related to the different USB HCs with USB 2.0 support .
*
* Onchip routing for router rev - id 0x04 ( try - and - error observation )
*
* 0x60 / 0x61 / 0x62 / 0x63 : 1 xEHCI and 3 xOHCI ( companion ) USB - HCs
* bit 6 - 4 are probably unused , not like 5595
*/
# define PIRQ_SIS_IRQ_MASK 0x0f
# define PIRQ_SIS_IRQ_DISABLE 0x80
# define PIRQ_SIS_USB_ENABLE 0x40
static int pirq_sis_get ( struct pci_dev * router , struct pci_dev * dev , int pirq )
{
u8 x ;
int reg ;
reg = pirq ;
if ( reg > = 0x01 & & reg < = 0x04 )
reg + = 0x40 ;
pci_read_config_byte ( router , reg , & x ) ;
return ( x & PIRQ_SIS_IRQ_DISABLE ) ? 0 : ( x & PIRQ_SIS_IRQ_MASK ) ;
}
static int pirq_sis_set ( struct pci_dev * router , struct pci_dev * dev , int pirq , int irq )
{
u8 x ;
int reg ;
reg = pirq ;
if ( reg > = 0x01 & & reg < = 0x04 )
reg + = 0x40 ;
pci_read_config_byte ( router , reg , & x ) ;
x & = ~ ( PIRQ_SIS_IRQ_MASK | PIRQ_SIS_IRQ_DISABLE ) ;
x | = irq ? irq : PIRQ_SIS_IRQ_DISABLE ;
pci_write_config_byte ( router , reg , x ) ;
return 1 ;
}
/*
* VLSI : nibble offset 0x74 - educated guess due to routing table and
* config space of VLSI 82 C534 PCI - bridge / router ( 1004 : 0102 )
* Tested on HP OmniBook 800 covering PIRQ 1 , 2 , 4 , 8 for onboard
* devices , PIRQ 3 for non - pci ( ! ) soundchip and ( untested ) PIRQ 6
* for the busbridge to the docking station .
*/
static int pirq_vlsi_get ( struct pci_dev * router , struct pci_dev * dev , int pirq )
{
2008-01-30 15:32:00 +03:00
WARN_ON_ONCE ( pirq > = 9 ) ;
2005-04-17 02:20:36 +04:00
if ( pirq > 8 ) {
2008-07-24 03:00:13 +04:00
dev_info ( & dev - > dev , " VLSI router PIRQ escape (%d) \n " , pirq ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
return read_config_nybble ( router , 0x74 , pirq - 1 ) ;
}
static int pirq_vlsi_set ( struct pci_dev * router , struct pci_dev * dev , int pirq , int irq )
{
2008-01-30 15:32:00 +03:00
WARN_ON_ONCE ( pirq > = 9 ) ;
2005-04-17 02:20:36 +04:00
if ( pirq > 8 ) {
2008-07-24 03:00:13 +04:00
dev_info ( & dev - > dev , " VLSI router PIRQ escape (%d) \n " , pirq ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
write_config_nybble ( router , 0x74 , pirq - 1 , irq ) ;
return 1 ;
}
/*
* ServerWorks : PCI interrupts mapped to system IRQ lines through Index
* and Redirect I / O registers ( 0x0c00 and 0x0c01 ) . The Index register
* format is ( PCIIRQ # # | 0x10 ) , e . g . : PCIIRQ10 = 0x1a . The Redirect
* register is a straight binary coding of desired PIC IRQ ( low nibble ) .
*
* The ' link ' value in the PIRQ table is already in the correct format
* for the Index register . There are some special index values :
* 0x00 for ACPI ( SCI ) , 0x01 for USB , 0x02 for IDE0 , 0x04 for IDE1 ,
* and 0x03 for SMBus .
*/
static int pirq_serverworks_get ( struct pci_dev * router , struct pci_dev * dev , int pirq )
{
2008-01-30 15:33:14 +03:00
outb ( pirq , 0xc00 ) ;
2005-04-17 02:20:36 +04:00
return inb ( 0xc01 ) & 0xf ;
}
2008-05-13 20:38:56 +04:00
static int pirq_serverworks_set ( struct pci_dev * router , struct pci_dev * dev ,
int pirq , int irq )
2005-04-17 02:20:36 +04:00
{
2008-01-30 15:33:14 +03:00
outb ( pirq , 0xc00 ) ;
outb ( irq , 0xc01 ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
/* Support for AMD756 PCI IRQ Routing
* Jhon H . Caicedo < jhcaiced @ osso . org . co >
* Jun / 21 / 2001 0.2 .0 Release , fixed to use " nybble " functions . . . ( jhcaiced )
* Jun / 19 / 2001 Alpha Release 0.1 .0 ( jhcaiced )
* The AMD756 pirq rules are nibble - based
* offset 0x56 0 - 3 PIRQA 4 - 7 PIRQB
* offset 0x57 0 - 3 PIRQC 4 - 7 PIRQD
*/
static int pirq_amd756_get ( struct pci_dev * router , struct pci_dev * dev , int pirq )
{
u8 irq ;
irq = 0 ;
if ( pirq < = 4 )
irq = read_config_nybble ( router , 0x56 , pirq - 1 ) ;
2008-07-24 03:00:13 +04:00
dev_info ( & dev - > dev ,
2008-08-26 01:44:59 +04:00
" AMD756: dev [%04x:%04x], router PIRQ %d get IRQ %d \n " ,
2008-07-24 03:00:13 +04:00
dev - > vendor , dev - > device , pirq , irq ) ;
2005-04-17 02:20:36 +04:00
return irq ;
}
static int pirq_amd756_set ( struct pci_dev * router , struct pci_dev * dev , int pirq , int irq )
{
2008-07-24 03:00:13 +04:00
dev_info ( & dev - > dev ,
2008-08-26 01:44:59 +04:00
" AMD756: dev [%04x:%04x], router PIRQ %d set IRQ %d \n " ,
2008-07-24 03:00:13 +04:00
dev - > vendor , dev - > device , pirq , irq ) ;
2005-04-17 02:20:36 +04:00
if ( pirq < = 4 )
write_config_nybble ( router , 0x56 , pirq - 1 , irq ) ;
return 1 ;
}
2007-08-11 01:42:15 +04:00
/*
* PicoPower PT86C523
*/
static int pirq_pico_get ( struct pci_dev * router , struct pci_dev * dev , int pirq )
{
outb ( 0x10 + ( ( pirq - 1 ) > > 1 ) , 0x24 ) ;
return ( ( pirq - 1 ) & 1 ) ? ( inb ( 0x26 ) > > 4 ) : ( inb ( 0x26 ) & 0xf ) ;
}
static int pirq_pico_set ( struct pci_dev * router , struct pci_dev * dev , int pirq ,
int irq )
{
unsigned int x ;
outb ( 0x10 + ( ( pirq - 1 ) > > 1 ) , 0x24 ) ;
x = inb ( 0x26 ) ;
x = ( ( pirq - 1 ) & 1 ) ? ( ( x & 0x0f ) | ( irq < < 4 ) ) : ( ( x & 0xf0 ) | ( irq ) ) ;
outb ( x , 0x26 ) ;
return 1 ;
}
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_PCI_BIOS
static int pirq_bios_set ( struct pci_dev * router , struct pci_dev * dev , int pirq , int irq )
{
struct pci_dev * bridge ;
int pin = pci_get_interrupt_pin ( dev , & bridge ) ;
2008-12-10 02:11:46 +03:00
return pcibios_set_irq_routing ( bridge , pin - 1 , irq ) ;
2005-04-17 02:20:36 +04:00
}
# endif
static __init int intel_router_probe ( struct irq_router * r , struct pci_dev * router , u16 device )
{
2006-06-23 13:04:28 +04:00
static struct pci_device_id __initdata pirq_440gx [ ] = {
2005-04-17 02:20:36 +04:00
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , PCI_DEVICE_ID_INTEL_82443GX_0 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , PCI_DEVICE_ID_INTEL_82443GX_2 ) } ,
{ } ,
} ;
/* 440GX has a proprietary PIRQ router -- don't use it */
if ( pci_dev_present ( pirq_440gx ) )
return 0 ;
2008-06-07 16:14:35 +04:00
switch ( device ) {
case PCI_DEVICE_ID_INTEL_82371FB_0 :
case PCI_DEVICE_ID_INTEL_82371SB_0 :
case PCI_DEVICE_ID_INTEL_82371AB_0 :
case PCI_DEVICE_ID_INTEL_82371MX :
case PCI_DEVICE_ID_INTEL_82443MX_0 :
case PCI_DEVICE_ID_INTEL_82801AA_0 :
case PCI_DEVICE_ID_INTEL_82801AB_0 :
case PCI_DEVICE_ID_INTEL_82801BA_0 :
case PCI_DEVICE_ID_INTEL_82801BA_10 :
case PCI_DEVICE_ID_INTEL_82801CA_0 :
case PCI_DEVICE_ID_INTEL_82801CA_12 :
case PCI_DEVICE_ID_INTEL_82801DB_0 :
case PCI_DEVICE_ID_INTEL_82801E_0 :
case PCI_DEVICE_ID_INTEL_82801EB_0 :
case PCI_DEVICE_ID_INTEL_ESB_1 :
case PCI_DEVICE_ID_INTEL_ICH6_0 :
case PCI_DEVICE_ID_INTEL_ICH6_1 :
case PCI_DEVICE_ID_INTEL_ICH7_0 :
case PCI_DEVICE_ID_INTEL_ICH7_1 :
case PCI_DEVICE_ID_INTEL_ICH7_30 :
case PCI_DEVICE_ID_INTEL_ICH7_31 :
2009-01-23 23:43:38 +03:00
case PCI_DEVICE_ID_INTEL_TGP_LPC :
2008-06-07 16:14:35 +04:00
case PCI_DEVICE_ID_INTEL_ESB2_0 :
case PCI_DEVICE_ID_INTEL_ICH8_0 :
case PCI_DEVICE_ID_INTEL_ICH8_1 :
case PCI_DEVICE_ID_INTEL_ICH8_2 :
case PCI_DEVICE_ID_INTEL_ICH8_3 :
case PCI_DEVICE_ID_INTEL_ICH8_4 :
case PCI_DEVICE_ID_INTEL_ICH9_0 :
case PCI_DEVICE_ID_INTEL_ICH9_1 :
case PCI_DEVICE_ID_INTEL_ICH9_2 :
case PCI_DEVICE_ID_INTEL_ICH9_3 :
case PCI_DEVICE_ID_INTEL_ICH9_4 :
case PCI_DEVICE_ID_INTEL_ICH9_5 :
case PCI_DEVICE_ID_INTEL_TOLAPAI_0 :
case PCI_DEVICE_ID_INTEL_ICH10_0 :
case PCI_DEVICE_ID_INTEL_ICH10_1 :
case PCI_DEVICE_ID_INTEL_ICH10_2 :
case PCI_DEVICE_ID_INTEL_ICH10_3 :
r - > name = " PIIX/ICH " ;
r - > get = pirq_piix_get ;
r - > set = pirq_piix_set ;
return 1 ;
2005-04-17 02:20:36 +04:00
}
2008-08-29 02:40:59 +04:00
if ( ( device > = PCI_DEVICE_ID_INTEL_PCH_LPC_MIN ) & &
( device < = PCI_DEVICE_ID_INTEL_PCH_LPC_MAX ) ) {
r - > name = " PIIX/ICH " ;
r - > get = pirq_piix_get ;
r - > set = pirq_piix_set ;
return 1 ;
}
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-10-31 01:59:36 +03:00
static __init int via_router_probe ( struct irq_router * r ,
struct pci_dev * router , u16 device )
2005-04-17 02:20:36 +04:00
{
/* FIXME: We should move some of the quirk fixup stuff here */
2005-07-28 12:07:33 +04:00
2005-10-31 01:59:36 +03:00
/*
2007-10-20 03:13:56 +04:00
* workarounds for some buggy BIOSes
2005-10-31 01:59:36 +03:00
*/
if ( device = = PCI_DEVICE_ID_VIA_82C586_0 ) {
2008-06-07 16:14:35 +04:00
switch ( router - > device ) {
2005-10-31 01:59:36 +03:00
case PCI_DEVICE_ID_VIA_82C686 :
/*
* Asus k7m bios wrongly reports 82 C686A
* as 586 - compatible
*/
device = PCI_DEVICE_ID_VIA_82C686 ;
break ;
case PCI_DEVICE_ID_VIA_8235 :
/**
* Asus a7v - x bios wrongly reports 8235
* as 586 - compatible
*/
device = PCI_DEVICE_ID_VIA_8235 ;
break ;
2008-06-06 02:31:22 +04:00
case PCI_DEVICE_ID_VIA_8237 :
/**
* Asus a7v600 bios wrongly reports 8237
* as 586 - compatible
*/
device = PCI_DEVICE_ID_VIA_8237 ;
break ;
2005-10-31 01:59:36 +03:00
}
2005-07-28 12:07:33 +04:00
}
2008-06-07 16:14:35 +04:00
switch ( device ) {
2005-10-31 01:59:36 +03:00
case PCI_DEVICE_ID_VIA_82C586_0 :
r - > name = " VIA " ;
r - > get = pirq_via586_get ;
r - > set = pirq_via586_set ;
return 1 ;
case PCI_DEVICE_ID_VIA_82C596 :
case PCI_DEVICE_ID_VIA_82C686 :
case PCI_DEVICE_ID_VIA_8231 :
2006-03-21 13:57:19 +03:00
case PCI_DEVICE_ID_VIA_8233A :
2005-10-31 01:59:36 +03:00
case PCI_DEVICE_ID_VIA_8235 :
2006-03-21 13:57:19 +03:00
case PCI_DEVICE_ID_VIA_8237 :
2005-04-17 02:20:36 +04:00
/* FIXME: add new ones for 8233/5 */
2005-10-31 01:59:36 +03:00
r - > name = " VIA " ;
r - > get = pirq_via_get ;
r - > set = pirq_via_set ;
return 1 ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
static __init int vlsi_router_probe ( struct irq_router * r , struct pci_dev * router , u16 device )
{
2008-06-07 16:14:35 +04:00
switch ( device ) {
case PCI_DEVICE_ID_VLSI_82C534 :
r - > name = " VLSI 82C534 " ;
r - > get = pirq_vlsi_get ;
r - > set = pirq_vlsi_set ;
return 1 ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
2008-05-13 20:38:56 +04:00
static __init int serverworks_router_probe ( struct irq_router * r ,
struct pci_dev * router , u16 device )
2005-04-17 02:20:36 +04:00
{
2008-06-07 16:14:35 +04:00
switch ( device ) {
case PCI_DEVICE_ID_SERVERWORKS_OSB4 :
case PCI_DEVICE_ID_SERVERWORKS_CSB5 :
r - > name = " ServerWorks " ;
r - > get = pirq_serverworks_get ;
r - > set = pirq_serverworks_set ;
return 1 ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
static __init int sis_router_probe ( struct irq_router * r , struct pci_dev * router , u16 device )
{
if ( device ! = PCI_DEVICE_ID_SI_503 )
return 0 ;
2008-06-07 16:14:35 +04:00
2005-04-17 02:20:36 +04:00
r - > name = " SIS " ;
r - > get = pirq_sis_get ;
r - > set = pirq_sis_set ;
return 1 ;
}
static __init int cyrix_router_probe ( struct irq_router * r , struct pci_dev * router , u16 device )
{
2008-06-07 16:14:35 +04:00
switch ( device ) {
case PCI_DEVICE_ID_CYRIX_5520 :
r - > name = " NatSemi " ;
r - > get = pirq_cyrix_get ;
r - > set = pirq_cyrix_set ;
return 1 ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
static __init int opti_router_probe ( struct irq_router * r , struct pci_dev * router , u16 device )
{
2008-06-07 16:14:35 +04:00
switch ( device ) {
case PCI_DEVICE_ID_OPTI_82C700 :
r - > name = " OPTI " ;
r - > get = pirq_opti_get ;
r - > set = pirq_opti_set ;
return 1 ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
static __init int ite_router_probe ( struct irq_router * r , struct pci_dev * router , u16 device )
{
2008-06-07 16:14:35 +04:00
switch ( device ) {
case PCI_DEVICE_ID_ITE_IT8330G_0 :
r - > name = " ITE " ;
r - > get = pirq_ite_get ;
r - > set = pirq_ite_set ;
return 1 ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
static __init int ali_router_probe ( struct irq_router * r , struct pci_dev * router , u16 device )
{
2008-06-07 16:14:35 +04:00
switch ( device ) {
2005-04-17 02:20:36 +04:00
case PCI_DEVICE_ID_AL_M1533 :
case PCI_DEVICE_ID_AL_M1563 :
2005-12-01 20:01:28 +03:00
r - > name = " ALI " ;
r - > get = pirq_ali_get ;
r - > set = pirq_ali_set ;
return 1 ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
static __init int amd_router_probe ( struct irq_router * r , struct pci_dev * router , u16 device )
{
2008-06-07 16:14:35 +04:00
switch ( device ) {
case PCI_DEVICE_ID_AMD_VIPER_740B :
r - > name = " AMD756 " ;
break ;
case PCI_DEVICE_ID_AMD_VIPER_7413 :
r - > name = " AMD766 " ;
break ;
case PCI_DEVICE_ID_AMD_VIPER_7443 :
r - > name = " AMD768 " ;
break ;
default :
return 0 ;
2005-04-17 02:20:36 +04:00
}
r - > get = pirq_amd756_get ;
r - > set = pirq_amd756_set ;
return 1 ;
}
2008-06-07 16:14:35 +04:00
2007-08-11 01:42:15 +04:00
static __init int pico_router_probe ( struct irq_router * r , struct pci_dev * router , u16 device )
{
switch ( device ) {
case PCI_DEVICE_ID_PICOPOWER_PT86C523 :
r - > name = " PicoPower PT86C523 " ;
r - > get = pirq_pico_get ;
r - > set = pirq_pico_set ;
return 1 ;
case PCI_DEVICE_ID_PICOPOWER_PT86C523BBP :
r - > name = " PicoPower PT86C523 rev. BB+ " ;
r - > get = pirq_pico_get ;
r - > set = pirq_pico_set ;
return 1 ;
}
return 0 ;
}
2005-04-17 02:20:36 +04:00
static __initdata struct irq_router_handler pirq_routers [ ] = {
{ PCI_VENDOR_ID_INTEL , intel_router_probe } ,
{ PCI_VENDOR_ID_AL , ali_router_probe } ,
{ PCI_VENDOR_ID_ITE , ite_router_probe } ,
{ PCI_VENDOR_ID_VIA , via_router_probe } ,
{ PCI_VENDOR_ID_OPTI , opti_router_probe } ,
{ PCI_VENDOR_ID_SI , sis_router_probe } ,
{ PCI_VENDOR_ID_CYRIX , cyrix_router_probe } ,
{ PCI_VENDOR_ID_VLSI , vlsi_router_probe } ,
{ PCI_VENDOR_ID_SERVERWORKS , serverworks_router_probe } ,
{ PCI_VENDOR_ID_AMD , amd_router_probe } ,
2007-08-11 01:42:15 +04:00
{ PCI_VENDOR_ID_PICOPOWER , pico_router_probe } ,
2005-04-17 02:20:36 +04:00
/* Someone with docs needs to add the ATI Radeon IGP */
{ 0 , NULL }
} ;
static struct irq_router pirq_router ;
static struct pci_dev * pirq_router_dev ;
/*
* FIXME : should we have an option to say " generic for
* chipset " ?
*/
2008-06-07 16:14:35 +04:00
2005-04-17 02:20:36 +04:00
static void __init pirq_find_router ( struct irq_router * r )
{
struct irq_routing_table * rt = pirq_table ;
struct irq_router_handler * h ;
# ifdef CONFIG_PCI_BIOS
if ( ! rt - > signature ) {
printk ( KERN_INFO " PCI: Using BIOS for IRQ routing \n " ) ;
r - > set = pirq_bios_set ;
r - > name = " BIOS " ;
return ;
}
# endif
/* Default unless a driver reloads it */
r - > name = " default " ;
r - > get = NULL ;
r - > set = NULL ;
2008-06-07 16:14:35 +04:00
2008-08-26 01:44:59 +04:00
DBG ( KERN_DEBUG " PCI: Attempting to find IRQ router for [%04x:%04x] \n " ,
2005-04-17 02:20:36 +04:00
rt - > rtr_vendor , rt - > rtr_device ) ;
2006-12-07 04:14:03 +03:00
pirq_router_dev = pci_get_bus_and_slot ( rt - > rtr_bus , rt - > rtr_devfn ) ;
2005-04-17 02:20:36 +04:00
if ( ! pirq_router_dev ) {
2005-12-01 20:01:28 +03:00
DBG ( KERN_DEBUG " PCI: Interrupt router not found at "
" %02x:%02x \n " , rt - > rtr_bus , rt - > rtr_devfn ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2008-06-07 16:14:35 +04:00
for ( h = pirq_routers ; h - > vendor ; h + + ) {
2005-04-17 02:20:36 +04:00
/* First look for a router match */
2008-05-13 20:38:56 +04:00
if ( rt - > rtr_vendor = = h - > vendor & &
h - > probe ( r , pirq_router_dev , rt - > rtr_device ) )
2005-04-17 02:20:36 +04:00
break ;
/* Fall back to a device match */
2008-05-13 20:38:56 +04:00
if ( pirq_router_dev - > vendor = = h - > vendor & &
h - > probe ( r , pirq_router_dev , pirq_router_dev - > device ) )
2005-04-17 02:20:36 +04:00
break ;
}
2008-08-26 01:44:59 +04:00
dev_info ( & pirq_router_dev - > dev , " %s IRQ router [%04x:%04x] \n " ,
2008-07-24 03:00:13 +04:00
pirq_router . name ,
pirq_router_dev - > vendor , pirq_router_dev - > device ) ;
2006-12-07 04:14:03 +03:00
/* The device remains referenced for the kernel lifetime */
2005-04-17 02:20:36 +04:00
}
static struct irq_info * pirq_get_info ( struct pci_dev * dev )
{
struct irq_routing_table * rt = pirq_table ;
2008-05-13 20:38:56 +04:00
int entries = ( rt - > size - sizeof ( struct irq_routing_table ) ) /
sizeof ( struct irq_info ) ;
2005-04-17 02:20:36 +04:00
struct irq_info * info ;
for ( info = rt - > slots ; entries - - ; info + + )
2008-05-13 20:38:56 +04:00
if ( info - > bus = = dev - > bus - > number & &
PCI_SLOT ( info - > devfn ) = = PCI_SLOT ( dev - > devfn ) )
2005-04-17 02:20:36 +04:00
return info ;
return NULL ;
}
static int pcibios_lookup_irq ( struct pci_dev * dev , int assign )
{
u8 pin ;
struct irq_info * info ;
int i , pirq , newirq ;
int irq = 0 ;
u32 mask ;
struct irq_router * r = & pirq_router ;
struct pci_dev * dev2 = NULL ;
char * msg = NULL ;
/* Find IRQ pin */
pci_read_config_byte ( dev , PCI_INTERRUPT_PIN , & pin ) ;
if ( ! pin ) {
2008-07-24 03:00:13 +04:00
dev_dbg ( & dev - > dev , " no interrupt pin \n " ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2009-05-06 21:10:06 +04:00
if ( io_apic_assign_pci_irqs )
return 0 ;
2005-04-17 02:20:36 +04:00
/* Find IRQ routing entry */
if ( ! pirq_table )
return 0 ;
2008-06-07 16:14:35 +04:00
2005-04-17 02:20:36 +04:00
info = pirq_get_info ( dev ) ;
if ( ! info ) {
2008-07-24 03:00:13 +04:00
dev_dbg ( & dev - > dev , " PCI INT %c not found in routing table \n " ,
2008-12-10 02:11:51 +03:00
' A ' + pin - 1 ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-12-10 02:11:51 +03:00
pirq = info - > irq [ pin - 1 ] . link ;
mask = info - > irq [ pin - 1 ] . bitmap ;
2005-04-17 02:20:36 +04:00
if ( ! pirq ) {
2008-12-10 02:11:51 +03:00
dev_dbg ( & dev - > dev , " PCI INT %c not routed \n " , ' A ' + pin - 1 ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-07-24 03:00:13 +04:00
dev_dbg ( & dev - > dev , " PCI INT %c -> PIRQ %02x, mask %04x, excl %04x " ,
2008-12-10 02:11:51 +03:00
' A ' + pin - 1 , pirq , mask , pirq_table - > exclusive_irqs ) ;
2005-04-17 02:20:36 +04:00
mask & = pcibios_irq_mask ;
/* Work around broken HP Pavilion Notebooks which assign USB to
IRQ 9 even though it is actually wired to IRQ 11 */
if ( broken_hp_bios_irq9 & & pirq = = 0x59 & & dev - > irq = = 9 ) {
dev - > irq = 11 ;
pci_write_config_byte ( dev , PCI_INTERRUPT_LINE , 11 ) ;
r - > set ( pirq_router_dev , dev , pirq , 11 ) ;
}
/* same for Acer Travelmate 360, but with CB and irq 11 -> 10 */
2008-05-13 20:38:56 +04:00
if ( acer_tm360_irqrouting & & dev - > irq = = 11 & &
dev - > vendor = = PCI_VENDOR_ID_O2 ) {
2005-04-17 02:20:36 +04:00
pirq = 0x68 ;
mask = 0x400 ;
dev - > irq = r - > get ( pirq_router_dev , dev , pirq ) ;
pci_write_config_byte ( dev , PCI_INTERRUPT_LINE , dev - > irq ) ;
}
/*
* Find the best IRQ to assign : use the one
* reported by the device if possible .
*/
newirq = dev - > irq ;
2006-01-06 19:43:16 +03:00
if ( newirq & & ! ( ( 1 < < newirq ) & mask ) ) {
2008-06-07 16:14:35 +04:00
if ( pci_probe & PCI_USE_PIRQ_MASK )
newirq = 0 ;
else
2008-07-24 03:00:13 +04:00
dev_warn ( & dev - > dev , " IRQ %d doesn't match PIRQ mask "
" %#x; try pci=usepirqmask \n " , newirq , mask ) ;
2005-04-17 02:20:36 +04:00
}
if ( ! newirq & & assign ) {
for ( i = 0 ; i < 16 ; i + + ) {
if ( ! ( mask & ( 1 < < i ) ) )
continue ;
2008-05-13 20:38:56 +04:00
if ( pirq_penalty [ i ] < pirq_penalty [ newirq ] & &
can_request_irq ( i , IRQF_SHARED ) )
2005-04-17 02:20:36 +04:00
newirq = i ;
}
}
2008-12-10 02:11:51 +03:00
dev_dbg ( & dev - > dev , " PCI INT %c -> newirq %d " , ' A ' + pin - 1 , newirq ) ;
2005-04-17 02:20:36 +04:00
/* Check if it is hardcoded */
if ( ( pirq & 0xf0 ) = = 0xf0 ) {
irq = pirq & 0xf ;
2008-07-24 03:00:13 +04:00
msg = " hardcoded " ;
2008-06-07 16:14:35 +04:00
} else if ( r - > get & & ( irq = r - > get ( pirq_router_dev , dev , pirq ) ) & & \
( ( ! ( pci_probe & PCI_USE_PIRQ_MASK ) ) | | ( ( 1 < < irq ) & mask ) ) ) {
2008-07-24 03:00:13 +04:00
msg = " found " ;
2006-06-23 13:04:21 +04:00
eisa_set_level_irq ( irq ) ;
2008-05-13 20:38:56 +04:00
} else if ( newirq & & r - > set & &
( dev - > class > > 8 ) ! = PCI_CLASS_DISPLAY_VGA ) {
2005-04-17 02:20:36 +04:00
if ( r - > set ( pirq_router_dev , dev , pirq , newirq ) ) {
eisa_set_level_irq ( newirq ) ;
2008-07-24 03:00:13 +04:00
msg = " assigned " ;
2005-04-17 02:20:36 +04:00
irq = newirq ;
}
}
if ( ! irq ) {
if ( newirq & & mask = = ( 1 < < newirq ) ) {
2008-07-24 03:00:13 +04:00
msg = " guessed " ;
2005-04-17 02:20:36 +04:00
irq = newirq ;
2008-07-24 03:00:13 +04:00
} else {
dev_dbg ( & dev - > dev , " can't route interrupt \n " ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
2008-07-24 03:00:13 +04:00
}
2005-04-17 02:20:36 +04:00
}
2008-12-10 02:11:51 +03:00
dev_info ( & dev - > dev , " %s PCI INT %c -> IRQ %d \n " , msg , ' A ' + pin - 1 , irq ) ;
2005-04-17 02:20:36 +04:00
/* Update IRQ for all devices with the same pirq value */
while ( ( dev2 = pci_get_device ( PCI_ANY_ID , PCI_ANY_ID , dev2 ) ) ! = NULL ) {
pci_read_config_byte ( dev2 , PCI_INTERRUPT_PIN , & pin ) ;
if ( ! pin )
continue ;
2008-12-10 02:11:51 +03:00
2005-04-17 02:20:36 +04:00
info = pirq_get_info ( dev2 ) ;
if ( ! info )
continue ;
2008-12-10 02:11:51 +03:00
if ( info - > irq [ pin - 1 ] . link = = pirq ) {
2008-05-13 20:38:56 +04:00
/*
* We refuse to override the dev - > irq
* information . Give a warning !
*/
2008-06-07 16:14:35 +04:00
if ( dev2 - > irq & & dev2 - > irq ! = irq & & \
2005-04-17 02:20:36 +04:00
( ! ( pci_probe & PCI_USE_PIRQ_MASK ) | | \
2008-06-07 16:14:35 +04:00
( ( 1 < < dev2 - > irq ) & mask ) ) ) {
2005-04-17 02:20:36 +04:00
# ifndef CONFIG_PCI_MSI
2008-07-24 03:00:13 +04:00
dev_info ( & dev2 - > dev , " IRQ routing conflict: "
" have IRQ %d, want IRQ %d \n " ,
dev2 - > irq , irq ) ;
2005-04-17 02:20:36 +04:00
# endif
2008-06-07 16:14:35 +04:00
continue ;
}
2005-04-17 02:20:36 +04:00
dev2 - > irq = irq ;
pirq_penalty [ irq ] + + ;
if ( dev ! = dev2 )
2008-07-24 03:00:13 +04:00
dev_info ( & dev - > dev , " sharing IRQ %d with %s \n " ,
irq , pci_name ( dev2 ) ) ;
2005-04-17 02:20:36 +04:00
}
}
return 1 ;
}
static void __init pcibios_fixup_irqs ( void )
{
struct pci_dev * dev = NULL ;
u8 pin ;
2005-12-01 20:01:28 +03:00
DBG ( KERN_DEBUG " PCI: IRQ fixup \n " ) ;
2005-04-17 02:20:36 +04:00
while ( ( dev = pci_get_device ( PCI_ANY_ID , PCI_ANY_ID , dev ) ) ! = NULL ) {
/*
2008-05-13 20:38:56 +04:00
* If the BIOS has set an out of range IRQ number , just
* ignore it . Also keep track of which IRQ ' s are
* already in use .
2005-04-17 02:20:36 +04:00
*/
if ( dev - > irq > = 16 ) {
2008-07-24 03:00:13 +04:00
dev_dbg ( & dev - > dev , " ignoring bogus IRQ %d \n " , dev - > irq ) ;
2005-04-17 02:20:36 +04:00
dev - > irq = 0 ;
}
2008-05-13 20:38:56 +04:00
/*
* If the IRQ is already assigned to a PCI device ,
* ignore its ISA use penalty
*/
if ( pirq_penalty [ dev - > irq ] > = 100 & &
pirq_penalty [ dev - > irq ] < 100000 )
2005-04-17 02:20:36 +04:00
pirq_penalty [ dev - > irq ] = 0 ;
pirq_penalty [ dev - > irq ] + + ;
}
2009-05-06 21:10:06 +04:00
if ( io_apic_assign_pci_irqs )
return ;
2005-04-17 02:20:36 +04:00
dev = NULL ;
while ( ( dev = pci_get_device ( PCI_ANY_ID , PCI_ANY_ID , dev ) ) ! = NULL ) {
pci_read_config_byte ( dev , PCI_INTERRUPT_PIN , & pin ) ;
2008-12-10 02:11:57 +03:00
if ( ! pin )
continue ;
2005-04-17 02:20:36 +04:00
/*
* Still no IRQ ? Try to lookup one . . .
*/
2008-12-10 02:11:57 +03:00
if ( ! dev - > irq )
2005-04-17 02:20:36 +04:00
pcibios_lookup_irq ( dev , 0 ) ;
}
}
/*
* Work around broken HP Pavilion Notebooks which assign USB to
* IRQ 9 even though it is actually wired to IRQ 11
*/
2007-10-03 23:15:40 +04:00
static int __init fix_broken_hp_bios_irq9 ( const struct dmi_system_id * d )
2005-04-17 02:20:36 +04:00
{
if ( ! broken_hp_bios_irq9 ) {
broken_hp_bios_irq9 = 1 ;
2008-05-13 20:38:56 +04:00
printk ( KERN_INFO " %s detected - fixing broken IRQ routing \n " ,
d - > ident ) ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
/*
* Work around broken Acer TravelMate 360 Notebooks which assign
* Cardbus to IRQ 11 even though it is actually wired to IRQ 10
*/
2007-10-03 23:15:40 +04:00
static int __init fix_acer_tm360_irqrouting ( const struct dmi_system_id * d )
2005-04-17 02:20:36 +04:00
{
if ( ! acer_tm360_irqrouting ) {
acer_tm360_irqrouting = 1 ;
2008-05-13 20:38:56 +04:00
printk ( KERN_INFO " %s detected - fixing broken IRQ routing \n " ,
d - > ident ) ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
static struct dmi_system_id __initdata pciirq_dmi_table [ ] = {
{
. callback = fix_broken_hp_bios_irq9 ,
. ident = " HP Pavilion N5400 Series Laptop " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Hewlett-Packard " ) ,
DMI_MATCH ( DMI_BIOS_VERSION , " GE.M1.03 " ) ,
2008-05-13 20:38:56 +04:00
DMI_MATCH ( DMI_PRODUCT_VERSION ,
" HP Pavilion Notebook Model GE " ) ,
2005-04-17 02:20:36 +04:00
DMI_MATCH ( DMI_BOARD_VERSION , " OmniBook N32N-736 " ) ,
} ,
} ,
{
. callback = fix_acer_tm360_irqrouting ,
. ident = " Acer TravelMate 36x Laptop " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Acer " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " TravelMate 360 " ) ,
} ,
} ,
{ }
} ;
2009-08-29 19:47:33 +04:00
void __init pcibios_irq_init ( void )
2005-04-17 02:20:36 +04:00
{
2005-12-01 20:01:28 +03:00
DBG ( KERN_DEBUG " PCI: IRQ init \n " ) ;
2005-04-17 02:20:36 +04:00
2009-08-29 19:47:33 +04:00
if ( raw_pci_ops = = NULL )
return ;
2005-04-17 02:20:36 +04:00
dmi_check_system ( pciirq_dmi_table ) ;
pirq_table = pirq_find_routing_table ( ) ;
# ifdef CONFIG_PCI_BIOS
if ( ! pirq_table & & ( pci_probe & PCI_BIOS_IRQ_SCAN ) )
pirq_table = pcibios_get_irq_routing_table ( ) ;
# endif
if ( pirq_table ) {
pirq_peer_trick ( ) ;
pirq_find_router ( & pirq_router ) ;
if ( pirq_table - > exclusive_irqs ) {
int i ;
2008-06-07 16:14:35 +04:00
for ( i = 0 ; i < 16 ; i + + )
2005-04-17 02:20:36 +04:00
if ( ! ( pirq_table - > exclusive_irqs & ( 1 < < i ) ) )
pirq_penalty [ i ] + = 100 ;
}
2008-05-13 20:38:56 +04:00
/*
* If we ' re using the I / O APIC , avoid using the PCI IRQ
* routing table
*/
2005-04-17 02:20:36 +04:00
if ( io_apic_assign_pci_irqs )
pirq_table = NULL ;
}
pcibios_fixup_irqs ( ) ;
2009-05-06 21:10:06 +04:00
if ( io_apic_assign_pci_irqs & & pci_routeirq ) {
struct pci_dev * dev = NULL ;
/*
* PCI IRQ routing is set up by pci_enable_device ( ) , but we
* also do it here in case there are still broken drivers that
* don ' t use pci_enable_device ( ) .
*/
printk ( KERN_INFO " PCI: Routing PCI interrupts for all devices because \" pci=routeirq \" specified \n " ) ;
for_each_pci_dev ( dev )
pirq_enable_irq ( dev ) ;
}
2005-04-17 02:20:36 +04:00
}
2005-04-01 09:07:31 +04:00
static void pirq_penalize_isa_irq ( int irq , int active )
2005-04-17 02:20:36 +04:00
{
/*
* If any ISAPnP device reports an IRQ in its list of possible
* IRQ ' s , we try to avoid assigning it to PCI devices .
*/
2005-04-01 09:07:31 +04:00
if ( irq < 16 ) {
if ( active )
pirq_penalty [ irq ] + = 1000 ;
else
pirq_penalty [ irq ] + = 100 ;
}
2005-04-17 02:20:36 +04:00
}
2005-04-01 09:07:31 +04:00
void pcibios_penalize_isa_irq ( int irq , int active )
2005-04-17 02:20:36 +04:00
{
2005-08-25 20:27:09 +04:00
# ifdef CONFIG_ACPI
2005-04-17 02:20:36 +04:00
if ( ! acpi_noirq )
2005-04-01 09:07:31 +04:00
acpi_penalize_isa_irq ( irq , active ) ;
2005-04-17 02:20:36 +04:00
else
# endif
2005-04-01 09:07:31 +04:00
pirq_penalize_isa_irq ( irq , active ) ;
2005-04-17 02:20:36 +04:00
}
static int pirq_enable_irq ( struct pci_dev * dev )
{
u8 pin ;
pci_read_config_byte ( dev , PCI_INTERRUPT_PIN , & pin ) ;
2009-05-06 21:10:06 +04:00
if ( pin & & ! pcibios_lookup_irq ( dev , 1 ) ) {
2005-04-17 02:20:36 +04:00
char * msg = " " ;
2009-05-06 21:10:06 +04:00
if ( ! io_apic_assign_pci_irqs & & dev - > irq )
return 0 ;
2005-04-17 02:20:36 +04:00
if ( io_apic_assign_pci_irqs ) {
2009-05-06 21:10:06 +04:00
# ifdef CONFIG_X86_IO_APIC
struct pci_dev * temp_dev ;
2005-04-17 02:20:36 +04:00
int irq ;
2009-05-16 00:05:16 +04:00
struct io_apic_irq_attr irq_attr ;
2005-04-17 02:20:36 +04:00
2009-05-06 21:08:22 +04:00
irq = IO_APIC_get_PCI_irq_vector ( dev - > bus - > number ,
PCI_SLOT ( dev - > devfn ) ,
2009-05-16 00:05:16 +04:00
pin - 1 , & irq_attr ) ;
2005-04-17 02:20:36 +04:00
/*
* Busses behind bridges are typically not listed in the MP - table .
* In this case we have to look up the IRQ based on the parent bus ,
* parent slot , and pin number . The SMP code detects such bridged
* busses itself so we should get into this branch reliably .
*/
temp_dev = dev ;
while ( irq < 0 & & dev - > bus - > parent ) { /* go back to the bridge */
2008-06-07 16:14:35 +04:00
struct pci_dev * bridge = dev - > bus - > self ;
2005-04-17 02:20:36 +04:00
2008-12-10 02:12:37 +03:00
pin = pci_swizzle_interrupt_pin ( dev , pin ) ;
2008-06-07 16:14:35 +04:00
irq = IO_APIC_get_PCI_irq_vector ( bridge - > bus - > number ,
2009-05-06 21:08:22 +04:00
PCI_SLOT ( bridge - > devfn ) ,
2009-05-16 00:05:16 +04:00
pin - 1 , & irq_attr ) ;
2005-04-17 02:20:36 +04:00
if ( irq > = 0 )
2008-07-24 03:00:13 +04:00
dev_warn ( & dev - > dev , " using bridge %s "
" INT %c to get IRQ %d \n " ,
2008-12-10 02:11:51 +03:00
pci_name ( bridge ) , ' A ' + pin - 1 ,
2008-07-24 03:00:13 +04:00
irq ) ;
2005-04-17 02:20:36 +04:00
dev = bridge ;
}
dev = temp_dev ;
if ( irq > = 0 ) {
2009-05-16 00:05:16 +04:00
io_apic_set_pci_routing ( & dev - > dev , irq ,
& irq_attr ) ;
2009-05-06 21:10:06 +04:00
dev - > irq = irq ;
2008-07-24 03:00:13 +04:00
dev_info ( & dev - > dev , " PCI->APIC IRQ transform: "
2008-12-10 02:11:51 +03:00
" INT %c -> IRQ %d \n " , ' A ' + pin - 1 , irq ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
} else
2008-07-24 03:00:13 +04:00
msg = " ; probably buggy MP table " ;
2009-05-06 21:10:06 +04:00
# endif
2005-04-17 02:20:36 +04:00
} else if ( pci_probe & PCI_BIOS_IRQ_SCAN )
msg = " " ;
else
2008-07-24 03:00:13 +04:00
msg = " ; please try using pci=biosirq " ;
2005-04-17 02:20:36 +04:00
2008-05-13 20:38:56 +04:00
/*
* With IDE legacy devices the IRQ lookup failure is not
* a problem . .
*/
if ( dev - > class > > 8 = = PCI_CLASS_STORAGE_IDE & &
! ( dev - > class & 0x5 ) )
2005-04-17 02:20:36 +04:00
return 0 ;
2008-07-24 03:00:13 +04:00
dev_warn ( & dev - > dev , " can't find IRQ for PCI INT %c%s \n " ,
2008-12-10 02:11:51 +03:00
' A ' + pin - 1 , msg ) ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}