2005-04-17 02:20:36 +04:00
# include <linux/pci.h>
# include <linux/acpi.h>
# include <linux/init.h>
2005-09-30 20:34:42 +04:00
# include <linux/irq.h>
2007-10-04 02:56:14 +04:00
# include <linux/dmi.h>
2005-09-12 20:49:24 +04:00
# include <asm/numa.h>
2005-04-17 02:20:36 +04:00
# include "pci.h"
2007-10-13 06:34:40 +04:00
static int __devinit can_skip_ioresource_align ( const struct dmi_system_id * d )
2007-10-04 02:56:14 +04:00
{
pci_probe | = PCI_CAN_SKIP_ISA_ALIGN ;
printk ( KERN_INFO " PCI: %s detected, can skip ISA alignment \n " , d - > ident ) ;
return 0 ;
}
2007-11-09 09:02:11 +03:00
static struct dmi_system_id acpi_pciprobe_dmi_table [ ] __devinitdata = {
2007-10-04 02:56:14 +04:00
/*
* Systems where PCI IO resource ISA alignment can be skipped
* when the ISA enable bit in the bridge control is not set
*/
{
. callback = can_skip_ioresource_align ,
. ident = " IBM System x3800 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " IBM " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " x3800 " ) ,
} ,
} ,
{
. callback = can_skip_ioresource_align ,
. ident = " IBM System x3850 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " IBM " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " x3850 " ) ,
} ,
} ,
{
. callback = can_skip_ioresource_align ,
. ident = " IBM System x3950 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " IBM " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " x3950 " ) ,
} ,
} ,
{ }
} ;
2007-10-04 02:56:51 +04:00
struct pci_root_info {
char * name ;
unsigned int res_num ;
struct resource * res ;
struct pci_bus * bus ;
int busnum ;
} ;
static acpi_status
resource_to_addr ( struct acpi_resource * resource ,
struct acpi_resource_address64 * addr )
{
acpi_status status ;
status = acpi_resource_to_address64 ( resource , addr ) ;
if ( ACPI_SUCCESS ( status ) & &
( addr - > resource_type = = ACPI_MEMORY_RANGE | |
addr - > resource_type = = ACPI_IO_RANGE ) & &
addr - > address_length > 0 & &
addr - > producer_consumer = = ACPI_PRODUCER ) {
return AE_OK ;
}
return AE_ERROR ;
}
static acpi_status
count_resource ( struct acpi_resource * acpi_res , void * data )
{
struct pci_root_info * info = data ;
struct acpi_resource_address64 addr ;
acpi_status status ;
2007-11-17 18:27:01 +03:00
if ( info - > res_num > = PCI_BUS_NUM_RESOURCES )
return AE_OK ;
2007-10-04 02:56:51 +04:00
status = resource_to_addr ( acpi_res , & addr ) ;
if ( ACPI_SUCCESS ( status ) )
info - > res_num + + ;
return AE_OK ;
}
static acpi_status
setup_resource ( struct acpi_resource * acpi_res , void * data )
{
struct pci_root_info * info = data ;
struct resource * res ;
struct acpi_resource_address64 addr ;
acpi_status status ;
unsigned long flags ;
struct resource * root ;
2007-11-17 18:27:01 +03:00
if ( info - > res_num > = PCI_BUS_NUM_RESOURCES )
return AE_OK ;
2007-10-04 02:56:51 +04:00
status = resource_to_addr ( acpi_res , & addr ) ;
if ( ! ACPI_SUCCESS ( status ) )
return AE_OK ;
if ( addr . resource_type = = ACPI_MEMORY_RANGE ) {
root = & iomem_resource ;
flags = IORESOURCE_MEM ;
if ( addr . info . mem . caching = = ACPI_PREFETCHABLE_MEMORY )
flags | = IORESOURCE_PREFETCH ;
} else if ( addr . resource_type = = ACPI_IO_RANGE ) {
root = & ioport_resource ;
flags = IORESOURCE_IO ;
} else
return AE_OK ;
res = & info - > res [ info - > res_num ] ;
res - > name = info - > name ;
res - > flags = flags ;
res - > start = addr . minimum + addr . translation_offset ;
res - > end = res - > start + addr . address_length - 1 ;
res - > child = NULL ;
if ( insert_resource ( root , res ) ) {
printk ( KERN_ERR " PCI: Failed to allocate 0x%lx-0x%lx "
" from %s for %s \n " , ( unsigned long ) res - > start ,
( unsigned long ) res - > end , root - > name , info - > name ) ;
} else {
info - > bus - > resource [ info - > res_num ] = res ;
info - > res_num + + ;
}
return AE_OK ;
}
static void
adjust_transparent_bridge_resources ( struct pci_bus * bus )
{
struct pci_dev * dev ;
list_for_each_entry ( dev , & bus - > devices , bus_list ) {
int i ;
u16 class = dev - > class > > 8 ;
if ( class = = PCI_CLASS_BRIDGE_PCI & & dev - > transparent ) {
for ( i = 3 ; i < PCI_BUS_NUM_RESOURCES ; i + + )
dev - > subordinate - > resource [ i ] =
dev - > bus - > resource [ i - 3 ] ;
}
}
}
static void
get_current_resources ( struct acpi_device * device , int busnum ,
struct pci_bus * bus )
{
struct pci_root_info info ;
size_t size ;
info . bus = bus ;
info . res_num = 0 ;
acpi_walk_resources ( device - > handle , METHOD_NAME__CRS , count_resource ,
& info ) ;
if ( ! info . res_num )
return ;
size = sizeof ( * info . res ) * info . res_num ;
info . res = kmalloc ( size , GFP_KERNEL ) ;
if ( ! info . res )
goto res_alloc_fail ;
info . name = kmalloc ( 12 , GFP_KERNEL ) ;
if ( ! info . name )
goto name_alloc_fail ;
sprintf ( info . name , " PCI Bus #%02x " , busnum ) ;
info . res_num = 0 ;
acpi_walk_resources ( device - > handle , METHOD_NAME__CRS , setup_resource ,
& info ) ;
if ( info . res_num )
adjust_transparent_bridge_resources ( bus ) ;
return ;
name_alloc_fail :
kfree ( info . res ) ;
res_alloc_fail :
return ;
}
2005-04-17 02:20:36 +04:00
struct pci_bus * __devinit pci_acpi_scan_root ( struct acpi_device * device , int domain , int busnum )
{
2005-09-12 20:49:24 +04:00
struct pci_bus * bus ;
2007-07-22 01:23:39 +04:00
struct pci_sysdata * sd ;
int pxm ;
2007-10-04 02:56:14 +04:00
dmi_check_system ( acpi_pciprobe_dmi_table ) ;
2007-10-12 00:58:30 +04:00
if ( domain & & ! pci_domains_supported ) {
printk ( KERN_WARNING " PCI: Multiple domains not supported "
" (dom %d, bus %d) \n " , domain , busnum ) ;
return NULL ;
}
2007-07-22 01:23:39 +04:00
/* Allocate per-root-bus (not per bus) arch-specific data.
* TODO : leak ; this memory is never freed .
* It ' s arguable whether it ' s worth the trouble to care .
*/
sd = kzalloc ( sizeof ( * sd ) , GFP_KERNEL ) ;
if ( ! sd ) {
printk ( KERN_ERR " PCI: OOM, not probing PCI bus %02x \n " , busnum ) ;
return NULL ;
}
2005-09-12 20:49:24 +04:00
2007-10-12 00:58:30 +04:00
sd - > domain = domain ;
2007-07-22 01:23:39 +04:00
sd - > node = - 1 ;
pxm = acpi_get_pxm ( device - > handle ) ;
# ifdef CONFIG_ACPI_NUMA
if ( pxm > = 0 )
sd - > node = pxm_to_node ( pxm ) ;
# endif
2008-04-16 01:34:49 +04:00
/*
* Maybe the desired pci bus has been already scanned . In such case
* it is unnecessary to scan the pci bus with the given domain , busnum .
*/
bus = pci_find_bus ( domain , busnum ) ;
if ( bus ) {
/*
* If the desired bus exits , the content of bus - > sysdata will
* be replaced by sd .
*/
memcpy ( bus - > sysdata , sd , sizeof ( * sd ) ) ;
kfree ( sd ) ;
} else
bus = pci_scan_bus_parented ( NULL , busnum , & pci_root_ops , sd ) ;
2007-07-22 01:23:39 +04:00
if ( ! bus )
kfree ( sd ) ;
2005-09-12 20:49:24 +04:00
# ifdef CONFIG_ACPI_NUMA
if ( bus ! = NULL ) {
if ( pxm > = 0 ) {
2007-07-22 01:23:39 +04:00
printk ( " bus %d -> pxm %d -> node %d \n " ,
2008-04-16 01:34:49 +04:00
busnum , pxm , pxm_to_node ( pxm ) ) ;
2005-09-12 20:49:24 +04:00
}
}
# endif
2007-10-04 02:56:51 +04:00
if ( bus & & ( pci_probe & PCI_USE__CRS ) )
get_current_resources ( device , busnum , bus ) ;
2005-09-12 20:49:24 +04:00
return bus ;
2005-04-17 02:20:36 +04:00
}
extern int pci_routeirq ;
static int __init pci_acpi_init ( void )
{
struct pci_dev * dev = NULL ;
if ( pcibios_scanned )
return 0 ;
if ( acpi_noirq )
return 0 ;
printk ( KERN_INFO " PCI: Using ACPI for IRQ routing \n " ) ;
acpi_irq_penalty_init ( ) ;
pcibios_scanned + + ;
pcibios_enable_irq = acpi_pci_irq_enable ;
2005-07-28 07:02:00 +04:00
pcibios_disable_irq = acpi_pci_irq_disable ;
2005-04-17 02:20:36 +04:00
if ( pci_routeirq ) {
/*
* 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 " ) ;
2005-11-07 10:39:36 +03:00
for_each_pci_dev ( dev )
2005-04-17 02:20:36 +04:00
acpi_pci_irq_enable ( dev ) ;
} else
printk ( KERN_INFO " PCI: If a device doesn't work, try \" pci=routeirq \" . If it helps, post a report \n " ) ;
# ifdef CONFIG_X86_IO_APIC
if ( acpi_ioapic )
print_IO_APIC ( ) ;
# endif
return 0 ;
}
subsys_initcall ( pci_acpi_init ) ;