2005-04-17 02:20:36 +04:00
/*
* pci_root . c - ACPI PCI Root Bridge Driver ( $ Revision : 40 $ )
*
* Copyright ( C ) 2001 , 2002 Andy Grover < andrew . grover @ intel . com >
* Copyright ( C ) 2001 , 2002 Paul Diefenbaugh < paul . s . diefenbaugh @ intel . com >
*
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or ( at
* your option ) any later version .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA .
*
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/proc_fs.h>
# include <linux/spinlock.h>
# include <linux/pm.h>
# include <linux/pci.h>
# include <linux/acpi.h>
# include <acpi/acpi_bus.h>
# include <acpi/acpi_drivers.h>
# define _COMPONENT ACPI_PCI_COMPONENT
2005-08-05 08:44:28 +04:00
ACPI_MODULE_NAME ( " pci_root " )
2005-04-17 02:20:36 +04:00
# define ACPI_PCI_ROOT_CLASS "pci_bridge"
# define ACPI_PCI_ROOT_HID "PNP0A03"
# define ACPI_PCI_ROOT_DRIVER_NAME "ACPI PCI Root Bridge Driver"
# define ACPI_PCI_ROOT_DEVICE_NAME "PCI Root Bridge"
2005-08-05 08:44:28 +04:00
static int acpi_pci_root_add ( struct acpi_device * device ) ;
static int acpi_pci_root_remove ( struct acpi_device * device , int type ) ;
static int acpi_pci_root_start ( struct acpi_device * device ) ;
2005-04-17 02:20:36 +04:00
static struct acpi_driver acpi_pci_root_driver = {
2005-08-05 08:44:28 +04:00
. name = ACPI_PCI_ROOT_DRIVER_NAME ,
. class = ACPI_PCI_ROOT_CLASS ,
. ids = ACPI_PCI_ROOT_HID ,
. ops = {
. add = acpi_pci_root_add ,
. remove = acpi_pci_root_remove ,
. start = acpi_pci_root_start ,
} ,
2005-04-17 02:20:36 +04:00
} ;
struct acpi_pci_root {
2005-08-05 08:44:28 +04:00
struct list_head node ;
acpi_handle handle ;
struct acpi_pci_id id ;
struct pci_bus * bus ;
2005-04-17 02:20:36 +04:00
} ;
static LIST_HEAD ( acpi_pci_roots ) ;
static struct acpi_pci_driver * sub_driver ;
int acpi_pci_register_driver ( struct acpi_pci_driver * driver )
{
int n = 0 ;
struct list_head * entry ;
struct acpi_pci_driver * * pptr = & sub_driver ;
while ( * pptr )
pptr = & ( * pptr ) - > next ;
* pptr = driver ;
if ( ! driver - > add )
return 0 ;
list_for_each ( entry , & acpi_pci_roots ) {
struct acpi_pci_root * root ;
root = list_entry ( entry , struct acpi_pci_root , node ) ;
driver - > add ( root - > handle ) ;
n + + ;
}
return n ;
}
2005-08-05 08:44:28 +04:00
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL ( acpi_pci_register_driver ) ;
void acpi_pci_unregister_driver ( struct acpi_pci_driver * driver )
{
struct list_head * entry ;
struct acpi_pci_driver * * pptr = & sub_driver ;
while ( * pptr ) {
if ( * pptr ! = driver )
continue ;
* pptr = ( * pptr ) - > next ;
break ;
}
if ( ! driver - > remove )
return ;
list_for_each ( entry , & acpi_pci_roots ) {
struct acpi_pci_root * root ;
root = list_entry ( entry , struct acpi_pci_root , node ) ;
driver - > remove ( root - > handle ) ;
}
}
2005-08-05 08:44:28 +04:00
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL ( acpi_pci_unregister_driver ) ;
static acpi_status
2005-08-05 08:44:28 +04:00
get_root_bridge_busnr_callback ( struct acpi_resource * resource , void * data )
2005-04-17 02:20:36 +04:00
{
int * busnr = ( int * ) data ;
struct acpi_resource_address64 address ;
if ( resource - > id ! = ACPI_RSTYPE_ADDRESS16 & &
resource - > id ! = ACPI_RSTYPE_ADDRESS32 & &
resource - > id ! = ACPI_RSTYPE_ADDRESS64 )
return AE_OK ;
acpi_resource_to_address64 ( resource , & address ) ;
2005-08-05 08:44:28 +04:00
if ( ( address . address_length > 0 ) & &
( address . resource_type = = ACPI_BUS_NUMBER_RANGE ) )
2005-04-17 02:20:36 +04:00
* busnr = address . min_address_range ;
return AE_OK ;
}
2005-08-05 08:44:28 +04:00
static acpi_status try_get_root_bridge_busnr ( acpi_handle handle , int * busnum )
2005-04-17 02:20:36 +04:00
{
acpi_status status ;
* busnum = - 1 ;
2005-08-05 08:44:28 +04:00
status =
acpi_walk_resources ( handle , METHOD_NAME__CRS ,
get_root_bridge_busnr_callback , busnum ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) )
return status ;
/* Check if we really get a bus number from _CRS */
if ( * busnum = = - 1 )
return AE_ERROR ;
return AE_OK ;
}
2005-08-05 08:44:28 +04:00
static int acpi_pci_root_add ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
struct acpi_pci_root * root = NULL ;
struct acpi_pci_root * tmp ;
acpi_status status = AE_OK ;
unsigned long value = 0 ;
acpi_handle handle = NULL ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_pci_root_add " ) ;
if ( ! device )
return_VALUE ( - EINVAL ) ;
root = kmalloc ( sizeof ( struct acpi_pci_root ) , GFP_KERNEL ) ;
if ( ! root )
return_VALUE ( - ENOMEM ) ;
memset ( root , 0 , sizeof ( struct acpi_pci_root ) ) ;
2005-04-28 11:25:45 +04:00
INIT_LIST_HEAD ( & root - > node ) ;
2005-04-17 02:20:36 +04:00
root - > handle = device - > handle ;
strcpy ( acpi_device_name ( device ) , ACPI_PCI_ROOT_DEVICE_NAME ) ;
strcpy ( acpi_device_class ( device ) , ACPI_PCI_ROOT_CLASS ) ;
acpi_driver_data ( device ) = root ;
/*
* TBD : Doesn ' t the bus driver automatically set this ?
*/
device - > ops . bind = acpi_pci_bind ;
/*
* Segment
* - - - - - - -
* Obtained via _SEG , if exists , otherwise assumed to be zero ( 0 ) .
*/
2005-08-05 08:44:28 +04:00
status = acpi_evaluate_integer ( root - > handle , METHOD_NAME__SEG , NULL ,
& value ) ;
2005-04-17 02:20:36 +04:00
switch ( status ) {
case AE_OK :
root - > id . segment = ( u16 ) value ;
break ;
case AE_NOT_FOUND :
2005-08-05 08:44:28 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
" Assuming segment 0 (no _SEG) \n " ) ) ;
2005-04-17 02:20:36 +04:00
root - > id . segment = 0 ;
break ;
default :
ACPI_DEBUG_PRINT ( ( ACPI_DB_ERROR , " Error evaluating _SEG \n " ) ) ;
result = - ENODEV ;
goto end ;
}
/*
* Bus
* - - -
* Obtained via _BBN , if exists , otherwise assumed to be zero ( 0 ) .
*/
2005-08-05 08:44:28 +04:00
status = acpi_evaluate_integer ( root - > handle , METHOD_NAME__BBN , NULL ,
& value ) ;
2005-04-17 02:20:36 +04:00
switch ( status ) {
case AE_OK :
root - > id . bus = ( u16 ) value ;
break ;
case AE_NOT_FOUND :
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Assuming bus 0 (no _BBN) \n " ) ) ;
root - > id . bus = 0 ;
break ;
default :
ACPI_DEBUG_PRINT ( ( ACPI_DB_ERROR , " Error evaluating _BBN \n " ) ) ;
result = - ENODEV ;
goto end ;
}
/* Some systems have wrong _BBN */
list_for_each_entry ( tmp , & acpi_pci_roots , node ) {
if ( ( tmp - > id . segment = = root - > id . segment )
2005-08-05 08:44:28 +04:00
& & ( tmp - > id . bus = = root - > id . bus ) ) {
2005-04-17 02:20:36 +04:00
int bus = 0 ;
acpi_status status ;
2005-08-05 08:44:28 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_ERROR ,
" Wrong _BBN value, please reboot and using option 'pci=noacpi' \n " ) ) ;
2005-04-17 02:20:36 +04:00
status = try_get_root_bridge_busnr ( root - > handle , & bus ) ;
if ( ACPI_FAILURE ( status ) )
break ;
if ( bus ! = root - > id . bus ) {
2005-08-05 08:44:28 +04:00
printk ( KERN_INFO PREFIX
" PCI _CRS %d overrides _BBN 0 \n " , bus ) ;
2005-04-17 02:20:36 +04:00
root - > id . bus = bus ;
}
break ;
}
}
/*
* Device & Function
* - - - - - - - - - - - - - - - - -
* Obtained from _ADR ( which has already been evaluated for us ) .
*/
root - > id . device = device - > pnp . bus_address > > 16 ;
root - > id . function = device - > pnp . bus_address & 0xFFFF ;
/*
* TBD : Need PCI interface for enumeration / configuration of roots .
*/
2005-08-05 08:44:28 +04:00
/* TBD: Locking */
list_add_tail ( & root - > node , & acpi_pci_roots ) ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
printk ( KERN_INFO PREFIX " %s [%s] (%04x:%02x) \n " ,
acpi_device_name ( device ) , acpi_device_bid ( device ) ,
root - > id . segment , root - > id . bus ) ;
2005-04-17 02:20:36 +04:00
/*
* Scan the Root Bridge
* - - - - - - - - - - - - - - - - - - - -
* Must do this prior to any attempt to bind the root device , as the
* PCI namespace does not get created until this call is made ( and
* thus the root bridge ' s pci_dev does not exist ) .
*/
root - > bus = pci_acpi_scan_root ( device , root - > id . segment , root - > id . bus ) ;
if ( ! root - > bus ) {
2005-08-05 08:44:28 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_ERROR ,
" Bus %04x:%02x not present in PCI namespace \n " ,
root - > id . segment , root - > id . bus ) ) ;
2005-04-17 02:20:36 +04:00
result = - ENODEV ;
goto end ;
}
/*
* Attach ACPI - PCI Context
* - - - - - - - - - - - - - - - - - - - - - - -
* Thus binding the ACPI and PCI devices .
*/
result = acpi_pci_bind_root ( device , & root - > id , root - > bus ) ;
if ( result )
goto end ;
/*
* PCI Routing Table
* - - - - - - - - - - - - - - - - -
* Evaluate and parse _PRT , if exists .
*/
status = acpi_get_handle ( root - > handle , METHOD_NAME__PRT , & handle ) ;
if ( ACPI_SUCCESS ( status ) )
result = acpi_pci_irq_add_prt ( root - > handle , root - > id . segment ,
2005-08-05 08:44:28 +04:00
root - > id . bus ) ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
end :
2005-04-28 11:25:45 +04:00
if ( result ) {
if ( ! list_empty ( & root - > node ) )
list_del ( & root - > node ) ;
2005-04-17 02:20:36 +04:00
kfree ( root ) ;
2005-04-28 11:25:45 +04:00
}
2005-04-17 02:20:36 +04:00
return_VALUE ( result ) ;
}
2005-08-05 08:44:28 +04:00
static int acpi_pci_root_start ( struct acpi_device * device )
2005-04-28 11:25:45 +04:00
{
2005-08-05 08:44:28 +04:00
struct acpi_pci_root * root ;
2005-04-28 11:25:45 +04:00
ACPI_FUNCTION_TRACE ( " acpi_pci_root_start " ) ;
list_for_each_entry ( root , & acpi_pci_roots , node ) {
if ( root - > handle = = device - > handle ) {
pci_bus_add_devices ( root - > bus ) ;
return_VALUE ( 0 ) ;
}
}
return_VALUE ( - ENODEV ) ;
}
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
static int acpi_pci_root_remove ( struct acpi_device * device , int type )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
struct acpi_pci_root * root = NULL ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_pci_root_remove " ) ;
if ( ! device | | ! acpi_driver_data ( device ) )
return_VALUE ( - EINVAL ) ;
2005-08-05 08:44:28 +04:00
root = ( struct acpi_pci_root * ) acpi_driver_data ( device ) ;
2005-04-17 02:20:36 +04:00
kfree ( root ) ;
return_VALUE ( 0 ) ;
}
2005-08-05 08:44:28 +04:00
static int __init acpi_pci_root_init ( void )
2005-04-17 02:20:36 +04:00
{
ACPI_FUNCTION_TRACE ( " acpi_pci_root_init " ) ;
if ( acpi_pci_disabled )
return_VALUE ( 0 ) ;
/* DEBUG:
2005-08-05 08:44:28 +04:00
acpi_dbg_layer = ACPI_PCI_COMPONENT ;
acpi_dbg_level = 0xFFFFFFFF ;
2005-04-17 02:20:36 +04:00
*/
if ( acpi_bus_register_driver ( & acpi_pci_root_driver ) < 0 )
return_VALUE ( - ENODEV ) ;
return_VALUE ( 0 ) ;
}
subsys_initcall ( acpi_pci_root_init ) ;