2005-04-17 02:20:36 +04:00
/*
* ACPI PCI HotPlug glue functions to ACPI CA subsystem
*
* Copyright ( C ) 2002 , 2003 Takayoshi Kochi ( t - kochi @ bq . jp . nec . com )
* Copyright ( C ) 2002 Hiroshi Aono ( h - aono @ ap . jp . nec . com )
* Copyright ( C ) 2002 , 2003 NEC Corporation
2005-04-28 11:25:53 +04:00
* Copyright ( C ) 2003 - 2005 Matthew Wilcox ( matthew . wilcox @ hp . com )
* Copyright ( C ) 2003 - 2005 Hewlett Packard
2005-04-28 11:25:56 +04:00
* Copyright ( C ) 2005 Rajesh Shah ( rajesh . shah @ intel . com )
* Copyright ( C ) 2005 Intel Corporation
2005-04-17 02:20:36 +04:00
*
* All rights reserved .
*
* 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 , GOOD TITLE or
* NON INFRINGEMENT . 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 . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
2006-07-26 21:52:33 +04:00
* Send feedback to < kristen . c . accardi @ intel . com >
2005-04-17 02:20:36 +04:00
*
*/
2005-04-28 11:25:53 +04:00
/*
* Lifetime rules for pci_dev :
* - The one in acpiphp_func has its refcount elevated by pci_get_slot ( )
* when the driver is loaded or when an insertion event occurs . It loses
* a refcount when its ejected or the driver unloads .
* - The one in acpiphp_bridge has its refcount elevated by pci_get_slot ( )
* when the bridge is scanned and it loses a refcount when the bridge
* is removed .
*/
2005-04-17 02:20:36 +04:00
# include <linux/init.h>
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/pci.h>
2006-10-14 07:05:19 +04:00
# include <linux/pci_hotplug.h>
2005-04-17 02:20:36 +04:00
# include <linux/smp_lock.h>
2006-01-13 18:02:15 +03:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
# include "../pci.h"
# include "acpiphp.h"
static LIST_HEAD ( bridge_list ) ;
2006-09-12 21:22:53 +04:00
static LIST_HEAD ( ioapic_list ) ;
static DEFINE_SPINLOCK ( ioapic_list_lock ) ;
2005-04-17 02:20:36 +04:00
# define MY_NAME "acpiphp_glue"
static void handle_hotplug_event_bridge ( acpi_handle , u32 , void * ) ;
2005-10-19 04:21:40 +04:00
static void acpiphp_sanitize_bus ( struct pci_bus * bus ) ;
static void acpiphp_set_hpp_values ( acpi_handle handle , struct pci_bus * bus ) ;
2006-06-27 09:50:14 +04:00
static void handle_hotplug_event_func ( acpi_handle handle , u32 type , void * context ) ;
2005-10-19 04:21:40 +04:00
2005-04-17 02:20:36 +04:00
/*
* initialization & terminatation routines
*/
/**
* is_ejectable - determine if a slot is ejectable
* @ handle : handle to acpi namespace
*
* Ejectable slot should satisfy at least these conditions :
*
* 1. has _ADR method
* 2. has _EJ0 method
*
* optionally
*
* 1. has _STA method
* 2. has _PS0 method
* 3. has _PS3 method
* 4. . .
*
*/
static int is_ejectable ( acpi_handle handle )
{
acpi_status status ;
acpi_handle tmp ;
status = acpi_get_handle ( handle , " _ADR " , & tmp ) ;
if ( ACPI_FAILURE ( status ) ) {
return 0 ;
}
status = acpi_get_handle ( handle , " _EJ0 " , & tmp ) ;
if ( ACPI_FAILURE ( status ) ) {
return 0 ;
}
return 1 ;
}
/* callback routine to check the existence of ejectable slots */
static acpi_status
is_ejectable_slot ( acpi_handle handle , u32 lvl , void * context , void * * rv )
{
int * count = ( int * ) context ;
if ( is_ejectable ( handle ) ) {
( * count ) + + ;
/* only one ejectable slot is enough */
return AE_CTRL_TERMINATE ;
} else {
return AE_OK ;
}
}
2006-06-28 11:08:06 +04:00
/* callback routine to check for the existance of a pci dock device */
static acpi_status
is_pci_dock_device ( acpi_handle handle , u32 lvl , void * context , void * * rv )
{
int * count = ( int * ) context ;
if ( is_dock_device ( handle ) ) {
( * count ) + + ;
return AE_CTRL_TERMINATE ;
} else {
return AE_OK ;
}
}
/*
* the _DCK method can do funny things . . . and sometimes not
* hah - hah funny .
*
* TBD - figure out a way to only call fixups for
* systems that require them .
*/
static int post_dock_fixups ( struct notifier_block * nb , unsigned long val ,
void * v )
{
struct acpiphp_func * func = container_of ( nb , struct acpiphp_func , nb ) ;
struct pci_bus * bus = func - > slot - > bridge - > pci_bus ;
u32 buses ;
if ( ! bus - > self )
return NOTIFY_OK ;
/* fixup bad _DCK function that rewrites
* secondary bridge on slot
*/
pci_read_config_dword ( bus - > self ,
PCI_PRIMARY_BUS ,
& buses ) ;
if ( ( ( buses > > 8 ) & 0xff ) ! = bus - > secondary ) {
buses = ( buses & 0xff000000 )
| ( ( unsigned int ) ( bus - > primary ) < < 0 )
| ( ( unsigned int ) ( bus - > secondary ) < < 8 )
| ( ( unsigned int ) ( bus - > subordinate ) < < 16 ) ;
pci_write_config_dword ( bus - > self , PCI_PRIMARY_BUS , buses ) ;
}
return NOTIFY_OK ;
}
2005-04-17 02:20:36 +04:00
/* callback routine to register each ACPI PCI slot object */
static acpi_status
register_slot ( acpi_handle handle , u32 lvl , void * context , void * * rv )
{
struct acpiphp_bridge * bridge = ( struct acpiphp_bridge * ) context ;
struct acpiphp_slot * slot ;
struct acpiphp_func * newfunc ;
acpi_handle tmp ;
acpi_status status = AE_OK ;
unsigned long adr , sun ;
2006-02-24 04:56:08 +03:00
int device , function , retval ;
2005-04-17 02:20:36 +04:00
status = acpi_evaluate_integer ( handle , " _ADR " , NULL , & adr ) ;
if ( ACPI_FAILURE ( status ) )
return AE_OK ;
status = acpi_get_handle ( handle , " _EJ0 " , & tmp ) ;
2006-06-28 11:08:06 +04:00
if ( ACPI_FAILURE ( status ) & & ! ( is_dock_device ( handle ) ) )
2005-04-17 02:20:36 +04:00
return AE_OK ;
device = ( adr > > 16 ) & 0xffff ;
function = adr & 0xffff ;
2006-02-28 17:34:49 +03:00
newfunc = kzalloc ( sizeof ( struct acpiphp_func ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! newfunc )
return AE_NO_MEMORY ;
INIT_LIST_HEAD ( & newfunc - > sibling ) ;
newfunc - > handle = handle ;
newfunc - > function = function ;
2006-02-24 04:56:03 +03:00
if ( ACPI_SUCCESS ( status ) )
newfunc - > flags = FUNC_HAS_EJ0 ;
2005-04-17 02:20:36 +04:00
if ( ACPI_SUCCESS ( acpi_get_handle ( handle , " _STA " , & tmp ) ) )
newfunc - > flags | = FUNC_HAS_STA ;
if ( ACPI_SUCCESS ( acpi_get_handle ( handle , " _PS0 " , & tmp ) ) )
newfunc - > flags | = FUNC_HAS_PS0 ;
if ( ACPI_SUCCESS ( acpi_get_handle ( handle , " _PS3 " , & tmp ) ) )
newfunc - > flags | = FUNC_HAS_PS3 ;
2006-06-28 11:08:06 +04:00
if ( ACPI_SUCCESS ( acpi_get_handle ( handle , " _DCK " , & tmp ) ) )
2006-02-24 04:56:03 +03:00
newfunc - > flags | = FUNC_HAS_DCK ;
2005-04-17 02:20:36 +04:00
status = acpi_evaluate_integer ( handle , " _SUN " , NULL , & sun ) ;
2006-06-28 11:09:54 +04:00
if ( ACPI_FAILURE ( status ) ) {
/*
* use the count of the number of slots we ' ve found
* for the number of the slot
*/
sun = bridge - > nr_slots + 1 ;
}
2005-04-17 02:20:36 +04:00
/* search for objects that share the same slot */
for ( slot = bridge - > slots ; slot ; slot = slot - > next )
if ( slot - > device = = device ) {
if ( slot - > sun ! = sun )
warn ( " sibling found, but _SUN doesn't match! \n " ) ;
break ;
}
if ( ! slot ) {
2006-02-28 17:34:49 +03:00
slot = kzalloc ( sizeof ( struct acpiphp_slot ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! slot ) {
kfree ( newfunc ) ;
return AE_NO_MEMORY ;
}
slot - > bridge = bridge ;
slot - > device = device ;
slot - > sun = sun ;
INIT_LIST_HEAD ( & slot - > funcs ) ;
2006-01-13 18:02:15 +03:00
mutex_init ( & slot - > crit_sect ) ;
2005-04-17 02:20:36 +04:00
slot - > next = bridge - > slots ;
bridge - > slots = slot ;
bridge - > nr_slots + + ;
2005-04-28 11:25:53 +04:00
dbg ( " found ACPI PCI Hotplug slot %d at PCI %04x:%02x:%02x \n " ,
slot - > sun , pci_domain_nr ( bridge - > pci_bus ) ,
bridge - > pci_bus - > number , slot - > device ) ;
2006-02-24 04:56:08 +03:00
retval = acpiphp_register_hotplug_slot ( slot ) ;
if ( retval ) {
warn ( " acpiphp_register_hotplug_slot failed(err code = 0x%x) \n " , retval ) ;
goto err_exit ;
}
2005-04-17 02:20:36 +04:00
}
newfunc - > slot = slot ;
list_add_tail ( & newfunc - > sibling , & slot - > funcs ) ;
/* associate corresponding pci_dev */
2005-04-28 11:25:53 +04:00
newfunc - > pci_dev = pci_get_slot ( bridge - > pci_bus ,
2005-04-17 02:20:36 +04:00
PCI_DEVFN ( device , function ) ) ;
if ( newfunc - > pci_dev ) {
slot - > flags | = ( SLOT_ENABLED | SLOT_POWEREDON ) ;
}
2006-06-28 11:08:06 +04:00
if ( is_dock_device ( handle ) ) {
/* we don't want to call this device's _EJ0
* because we want the dock notify handler
* to call it after it calls _DCK
2006-02-24 04:56:03 +03:00
*/
newfunc - > flags & = ~ FUNC_HAS_EJ0 ;
2006-06-28 11:08:06 +04:00
if ( register_hotplug_dock_device ( handle ,
handle_hotplug_event_func , newfunc ) )
dbg ( " failed to register dock device \n " ) ;
/* we need to be notified when dock events happen
* outside of the hotplug operation , since we may
* need to do fixups before we can hotplug .
*/
newfunc - > nb . notifier_call = post_dock_fixups ;
if ( register_dock_notifier ( & newfunc - > nb ) )
dbg ( " failed to register a dock notifier " ) ;
2006-02-24 04:56:03 +03:00
}
2005-04-17 02:20:36 +04:00
/* install notify handler */
2006-02-24 04:56:03 +03:00
if ( ! ( newfunc - > flags & FUNC_HAS_DCK ) ) {
status = acpi_install_notify_handler ( handle ,
2005-04-17 02:20:36 +04:00
ACPI_SYSTEM_NOTIFY ,
handle_hotplug_event_func ,
newfunc ) ;
2006-02-24 04:56:03 +03:00
if ( ACPI_FAILURE ( status ) )
err ( " failed to register interrupt notify handler \n " ) ;
} else
status = AE_OK ;
2005-04-17 02:20:36 +04:00
2006-02-24 04:56:03 +03:00
return status ;
2006-02-24 04:56:08 +03:00
err_exit :
bridge - > nr_slots - - ;
bridge - > slots = slot - > next ;
kfree ( slot ) ;
kfree ( newfunc ) ;
return AE_OK ;
2005-04-17 02:20:36 +04:00
}
/* see if it's worth looking at this bridge */
static int detect_ejectable_slots ( acpi_handle * bridge_handle )
{
acpi_status status ;
int count ;
count = 0 ;
/* only check slots defined directly below bridge object */
status = acpi_walk_namespace ( ACPI_TYPE_DEVICE , bridge_handle , ( u32 ) 1 ,
is_ejectable_slot , ( void * ) & count , NULL ) ;
2006-06-28 11:08:06 +04:00
/*
* we also need to add this bridge if there is a dock bridge or
* other pci device on a dock station ( removable )
*/
if ( ! count )
status = acpi_walk_namespace ( ACPI_TYPE_DEVICE , bridge_handle ,
( u32 ) 1 , is_pci_dock_device , ( void * ) & count ,
NULL ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
/* decode ACPI 2.0 _HPP hot plug parameters */
static void decode_hpp ( struct acpiphp_bridge * bridge )
{
acpi_status status ;
2006-05-02 05:54:50 +04:00
status = acpi_get_hp_params_from_firmware ( bridge - > pci_bus , & bridge - > hpp ) ;
2006-05-02 05:57:14 +04:00
if ( ACPI_FAILURE ( status ) | |
! bridge - > hpp . t0 | | ( bridge - > hpp . t0 - > revision > 1 ) ) {
2006-03-03 21:16:05 +03:00
/* use default numbers */
2006-05-02 05:57:14 +04:00
printk ( KERN_WARNING
" %s: Could not get hotplug parameters. Use defaults \n " ,
__FUNCTION__ ) ;
bridge - > hpp . t0 = & bridge - > hpp . type0_data ;
bridge - > hpp . t0 - > revision = 0 ;
bridge - > hpp . t0 - > cache_line_size = 0x10 ;
bridge - > hpp . t0 - > latency_timer = 0x40 ;
bridge - > hpp . t0 - > enable_serr = 0 ;
bridge - > hpp . t0 - > enable_perr = 0 ;
2005-04-17 02:20:36 +04:00
}
}
2006-03-03 21:16:05 +03:00
2005-04-17 02:20:36 +04:00
/* initialize miscellaneous stuff for both root and PCI-to-PCI bridge */
static void init_bridge_misc ( struct acpiphp_bridge * bridge )
{
acpi_status status ;
/* decode ACPI 2.0 _HPP (hot plug parameters) */
decode_hpp ( bridge ) ;
2006-02-24 04:56:08 +03:00
/* must be added to the list prior to calling register_slot */
list_add ( & bridge - > list , & bridge_list ) ;
2005-04-17 02:20:36 +04:00
/* register all slot objects under this bridge */
status = acpi_walk_namespace ( ACPI_TYPE_DEVICE , bridge - > handle , ( u32 ) 1 ,
register_slot , bridge , NULL ) ;
2006-02-24 04:56:08 +03:00
if ( ACPI_FAILURE ( status ) ) {
list_del ( & bridge - > list ) ;
return ;
}
2005-04-17 02:20:36 +04:00
/* install notify handler */
2005-04-28 11:25:56 +04:00
if ( bridge - > type ! = BRIDGE_TYPE_HOST ) {
2006-03-22 08:49:20 +03:00
if ( ( bridge - > flags & BRIDGE_HAS_EJ0 ) & & bridge - > func ) {
status = acpi_remove_notify_handler ( bridge - > func - > handle ,
ACPI_SYSTEM_NOTIFY ,
handle_hotplug_event_func ) ;
if ( ACPI_FAILURE ( status ) )
err ( " failed to remove notify handler \n " ) ;
}
2005-04-28 11:25:56 +04:00
status = acpi_install_notify_handler ( bridge - > handle ,
2005-04-17 02:20:36 +04:00
ACPI_SYSTEM_NOTIFY ,
handle_hotplug_event_bridge ,
bridge ) ;
2005-04-28 11:25:56 +04:00
if ( ACPI_FAILURE ( status ) ) {
err ( " failed to register interrupt notify handler \n " ) ;
}
2005-04-17 02:20:36 +04:00
}
}
2006-03-22 08:49:20 +03:00
/* find acpiphp_func from acpiphp_bridge */
static struct acpiphp_func * acpiphp_bridge_handle_to_function ( acpi_handle handle )
{
struct list_head * node , * l ;
struct acpiphp_bridge * bridge ;
struct acpiphp_slot * slot ;
struct acpiphp_func * func ;
list_for_each ( node , & bridge_list ) {
bridge = list_entry ( node , struct acpiphp_bridge , list ) ;
for ( slot = bridge - > slots ; slot ; slot = slot - > next ) {
list_for_each ( l , & slot - > funcs ) {
func = list_entry ( l , struct acpiphp_func ,
sibling ) ;
if ( func - > handle = = handle )
return func ;
}
}
}
return NULL ;
}
static inline void config_p2p_bridge_flags ( struct acpiphp_bridge * bridge )
{
acpi_handle dummy_handle ;
if ( ACPI_SUCCESS ( acpi_get_handle ( bridge - > handle ,
" _STA " , & dummy_handle ) ) )
bridge - > flags | = BRIDGE_HAS_STA ;
if ( ACPI_SUCCESS ( acpi_get_handle ( bridge - > handle ,
" _EJ0 " , & dummy_handle ) ) )
bridge - > flags | = BRIDGE_HAS_EJ0 ;
if ( ACPI_SUCCESS ( acpi_get_handle ( bridge - > handle ,
" _PS0 " , & dummy_handle ) ) )
bridge - > flags | = BRIDGE_HAS_PS0 ;
if ( ACPI_SUCCESS ( acpi_get_handle ( bridge - > handle ,
" _PS3 " , & dummy_handle ) ) )
bridge - > flags | = BRIDGE_HAS_PS3 ;
/* is this ejectable p2p bridge? */
if ( bridge - > flags & BRIDGE_HAS_EJ0 ) {
struct acpiphp_func * func ;
dbg ( " found ejectable p2p bridge \n " ) ;
/* make link between PCI bridge and PCI function */
func = acpiphp_bridge_handle_to_function ( bridge - > handle ) ;
if ( ! func )
return ;
bridge - > func = func ;
func - > bridge = bridge ;
}
}
2005-04-17 02:20:36 +04:00
/* allocate and initialize host bridge data structure */
2005-04-28 11:25:53 +04:00
static void add_host_bridge ( acpi_handle * handle , struct pci_bus * pci_bus )
2005-04-17 02:20:36 +04:00
{
struct acpiphp_bridge * bridge ;
2006-02-28 17:34:49 +03:00
bridge = kzalloc ( sizeof ( struct acpiphp_bridge ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( bridge = = NULL )
return ;
bridge - > type = BRIDGE_TYPE_HOST ;
bridge - > handle = handle ;
2005-04-28 11:25:53 +04:00
bridge - > pci_bus = pci_bus ;
2005-04-17 02:20:36 +04:00
spin_lock_init ( & bridge - > res_lock ) ;
init_bridge_misc ( bridge ) ;
}
/* allocate and initialize PCI-to-PCI bridge data structure */
2005-04-28 11:25:53 +04:00
static void add_p2p_bridge ( acpi_handle * handle , struct pci_dev * pci_dev )
2005-04-17 02:20:36 +04:00
{
struct acpiphp_bridge * bridge ;
2006-02-28 17:34:49 +03:00
bridge = kzalloc ( sizeof ( struct acpiphp_bridge ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( bridge = = NULL ) {
err ( " out of memory \n " ) ;
return ;
}
bridge - > type = BRIDGE_TYPE_P2P ;
bridge - > handle = handle ;
2006-03-22 08:49:20 +03:00
config_p2p_bridge_flags ( bridge ) ;
2005-04-17 02:20:36 +04:00
2005-04-28 11:25:53 +04:00
bridge - > pci_dev = pci_dev_get ( pci_dev ) ;
bridge - > pci_bus = pci_dev - > subordinate ;
2005-04-17 02:20:36 +04:00
if ( ! bridge - > pci_bus ) {
err ( " This is not a PCI-to-PCI bridge! \n " ) ;
2005-04-28 11:25:53 +04:00
goto err ;
2005-04-17 02:20:36 +04:00
}
spin_lock_init ( & bridge - > res_lock ) ;
init_bridge_misc ( bridge ) ;
2005-04-28 11:25:53 +04:00
return ;
err :
pci_dev_put ( pci_dev ) ;
kfree ( bridge ) ;
return ;
2005-04-17 02:20:36 +04:00
}
/* callback routine to find P2P bridges */
static acpi_status
find_p2p_bridge ( acpi_handle handle , u32 lvl , void * context , void * * rv )
{
acpi_status status ;
acpi_handle dummy_handle ;
unsigned long tmp ;
2005-04-28 11:25:53 +04:00
int device , function ;
2005-04-17 02:20:36 +04:00
struct pci_dev * dev ;
2005-04-28 11:25:53 +04:00
struct pci_bus * pci_bus = context ;
2005-04-17 02:20:36 +04:00
status = acpi_get_handle ( handle , " _ADR " , & dummy_handle ) ;
if ( ACPI_FAILURE ( status ) )
return AE_OK ; /* continue */
status = acpi_evaluate_integer ( handle , " _ADR " , NULL , & tmp ) ;
if ( ACPI_FAILURE ( status ) ) {
dbg ( " %s: _ADR evaluation failure \n " , __FUNCTION__ ) ;
return AE_OK ;
}
device = ( tmp > > 16 ) & 0xffff ;
function = tmp & 0xffff ;
2005-04-28 11:25:53 +04:00
dev = pci_get_slot ( pci_bus , PCI_DEVFN ( device , function ) ) ;
2005-04-17 02:20:36 +04:00
2005-04-28 11:25:53 +04:00
if ( ! dev | | ! dev - > subordinate )
goto out ;
2005-04-17 02:20:36 +04:00
/* check if this bridge has ejectable slots */
2006-06-28 11:08:06 +04:00
if ( ( detect_ejectable_slots ( handle ) > 0 ) ) {
2005-04-17 02:20:36 +04:00
dbg ( " found PCI-to-PCI bridge at PCI %s \n " , pci_name ( dev ) ) ;
2005-04-28 11:25:53 +04:00
add_p2p_bridge ( handle , dev ) ;
2005-04-17 02:20:36 +04:00
}
[PATCH] acpiphp: Scan slots under the nested P2P bridge
Current ACPIPHP driver scans only slots under the top level PCI-to-PCI
bridge. So hotplug PCI slots under the nested PCI-to-PCI bridge would
not be detected. For example, if the system has the ACPI namespace
like below, hotplug slots woule not be detected.
Device (PCI0) { /* Root bridge */
Name (_HID, "PNP0A03")
Device (P2PA) { /* PCI-to-PCI bridge */
Name (_ADR, ...)
Device (P2PB) { /* PCI-to-PCI bridge */
Name (_ADR, ...)
Device (S0F0) { /* hotplug slot */
Name (_ADR, ...)
Name (_SUN, ...)
Method (_EJ0, ...) { ... }
}
...
Device (S0F7) { /* hotplug slot */
Name (_ADR, ...)
Name (_SUN, ...)
Method (_EJ0, ...) { ... }
}
Device (S1F0) { /* hotplug slot */
Name (_ADR, ...)
Name (_SUN, ...)
Method (_EJ0, ...) { ... }
}
...
}
}
}
This patch fixes this issue.
Signed-off-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2006-02-27 16:15:49 +03:00
/* search P2P bridges under this p2p bridge */
status = acpi_walk_namespace ( ACPI_TYPE_DEVICE , handle , ( u32 ) 1 ,
find_p2p_bridge , dev - > subordinate , NULL ) ;
if ( ACPI_FAILURE ( status ) )
2006-03-22 08:49:20 +03:00
warn ( " find_p2p_bridge failed (error code = 0x%x) \n " , status ) ;
[PATCH] acpiphp: Scan slots under the nested P2P bridge
Current ACPIPHP driver scans only slots under the top level PCI-to-PCI
bridge. So hotplug PCI slots under the nested PCI-to-PCI bridge would
not be detected. For example, if the system has the ACPI namespace
like below, hotplug slots woule not be detected.
Device (PCI0) { /* Root bridge */
Name (_HID, "PNP0A03")
Device (P2PA) { /* PCI-to-PCI bridge */
Name (_ADR, ...)
Device (P2PB) { /* PCI-to-PCI bridge */
Name (_ADR, ...)
Device (S0F0) { /* hotplug slot */
Name (_ADR, ...)
Name (_SUN, ...)
Method (_EJ0, ...) { ... }
}
...
Device (S0F7) { /* hotplug slot */
Name (_ADR, ...)
Name (_SUN, ...)
Method (_EJ0, ...) { ... }
}
Device (S1F0) { /* hotplug slot */
Name (_ADR, ...)
Name (_SUN, ...)
Method (_EJ0, ...) { ... }
}
...
}
}
}
This patch fixes this issue.
Signed-off-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2006-02-27 16:15:49 +03:00
2005-04-28 11:25:53 +04:00
out :
pci_dev_put ( dev ) ;
2005-04-17 02:20:36 +04:00
return AE_OK ;
}
/* find hot-pluggable slots, and then find P2P bridge */
static int add_bridge ( acpi_handle handle )
{
acpi_status status ;
unsigned long tmp ;
int seg , bus ;
acpi_handle dummy_handle ;
2005-04-28 11:25:53 +04:00
struct pci_bus * pci_bus ;
2005-04-17 02:20:36 +04:00
/* if the bridge doesn't have _STA, we assume it is always there */
status = acpi_get_handle ( handle , " _STA " , & dummy_handle ) ;
if ( ACPI_SUCCESS ( status ) ) {
status = acpi_evaluate_integer ( handle , " _STA " , NULL , & tmp ) ;
if ( ACPI_FAILURE ( status ) ) {
dbg ( " %s: _STA evaluation failure \n " , __FUNCTION__ ) ;
return 0 ;
}
if ( ( tmp & ACPI_STA_FUNCTIONING ) = = 0 )
/* don't register this object */
return 0 ;
}
/* get PCI segment number */
status = acpi_evaluate_integer ( handle , " _SEG " , NULL , & tmp ) ;
seg = ACPI_SUCCESS ( status ) ? tmp : 0 ;
/* get PCI bus number */
status = acpi_evaluate_integer ( handle , " _BBN " , NULL , & tmp ) ;
if ( ACPI_SUCCESS ( status ) ) {
bus = tmp ;
} else {
warn ( " can't get bus number, assuming 0 \n " ) ;
bus = 0 ;
}
2005-04-28 11:25:53 +04:00
pci_bus = pci_find_bus ( seg , bus ) ;
if ( ! pci_bus ) {
err ( " Can't find bus %04x:%02x \n " , seg , bus ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
/* check if this bridge has ejectable slots */
if ( detect_ejectable_slots ( handle ) > 0 ) {
dbg ( " found PCI host-bus bridge with hot-pluggable slots \n " ) ;
2005-04-28 11:25:53 +04:00
add_host_bridge ( handle , pci_bus ) ;
2005-04-17 02:20:36 +04:00
}
/* search P2P bridges under this host bridge */
status = acpi_walk_namespace ( ACPI_TYPE_DEVICE , handle , ( u32 ) 1 ,
2005-04-28 11:25:53 +04:00
find_p2p_bridge , pci_bus , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) )
2006-03-22 08:49:20 +03:00
warn ( " find_p2p_bridge failed (error code = 0x%x) \n " , status ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-04-28 11:25:53 +04:00
static struct acpiphp_bridge * acpiphp_handle_to_bridge ( acpi_handle handle )
{
struct list_head * head ;
list_for_each ( head , & bridge_list ) {
struct acpiphp_bridge * bridge = list_entry ( head ,
struct acpiphp_bridge , list ) ;
if ( bridge - > handle = = handle )
return bridge ;
}
return NULL ;
}
2005-04-17 02:20:36 +04:00
2005-04-28 11:25:54 +04:00
static void cleanup_bridge ( struct acpiphp_bridge * bridge )
2005-04-17 02:20:36 +04:00
{
2005-04-28 11:25:53 +04:00
struct list_head * list , * tmp ;
struct acpiphp_slot * slot ;
acpi_status status ;
2005-04-28 11:25:54 +04:00
acpi_handle handle = bridge - > handle ;
2005-04-28 11:25:53 +04:00
status = acpi_remove_notify_handler ( handle , ACPI_SYSTEM_NOTIFY ,
handle_hotplug_event_bridge ) ;
if ( ACPI_FAILURE ( status ) )
err ( " failed to remove notify handler \n " ) ;
2006-03-22 08:49:20 +03:00
if ( ( bridge - > type ! = BRIDGE_TYPE_HOST ) & &
( ( bridge - > flags & BRIDGE_HAS_EJ0 ) & & bridge - > func ) ) {
status = acpi_install_notify_handler ( bridge - > func - > handle ,
ACPI_SYSTEM_NOTIFY ,
handle_hotplug_event_func ,
bridge - > func ) ;
if ( ACPI_FAILURE ( status ) )
err ( " failed to install interrupt notify handler \n " ) ;
}
2005-04-28 11:25:53 +04:00
slot = bridge - > slots ;
while ( slot ) {
struct acpiphp_slot * next = slot - > next ;
list_for_each_safe ( list , tmp , & slot - > funcs ) {
struct acpiphp_func * func ;
func = list_entry ( list , struct acpiphp_func , sibling ) ;
2006-06-28 11:08:06 +04:00
if ( is_dock_device ( func - > handle ) ) {
unregister_hotplug_dock_device ( func - > handle ) ;
unregister_dock_notifier ( & func - > nb ) ;
}
2006-02-24 04:56:03 +03:00
if ( ! ( func - > flags & FUNC_HAS_DCK ) ) {
status = acpi_remove_notify_handler ( func - > handle ,
2005-04-28 11:25:53 +04:00
ACPI_SYSTEM_NOTIFY ,
handle_hotplug_event_func ) ;
2006-02-24 04:56:03 +03:00
if ( ACPI_FAILURE ( status ) )
err ( " failed to remove notify handler \n " ) ;
}
2005-04-28 11:25:53 +04:00
pci_dev_put ( func - > pci_dev ) ;
list_del ( list ) ;
kfree ( func ) ;
}
2006-02-24 04:56:08 +03:00
acpiphp_unregister_hotplug_slot ( slot ) ;
list_del ( & slot - > funcs ) ;
2005-04-28 11:25:53 +04:00
kfree ( slot ) ;
slot = next ;
}
pci_dev_put ( bridge - > pci_dev ) ;
list_del ( & bridge - > list ) ;
kfree ( bridge ) ;
2005-04-17 02:20:36 +04:00
}
2005-04-28 11:25:54 +04:00
static acpi_status
cleanup_p2p_bridge ( acpi_handle handle , u32 lvl , void * context , void * * rv )
{
struct acpiphp_bridge * bridge ;
2006-03-22 08:49:20 +03:00
/* cleanup p2p bridges under this P2P bridge
in a depth - first manner */
acpi_walk_namespace ( ACPI_TYPE_DEVICE , handle , ( u32 ) 1 ,
cleanup_p2p_bridge , NULL , NULL ) ;
2005-04-28 11:25:54 +04:00
if ( ! ( bridge = acpiphp_handle_to_bridge ( handle ) ) )
return AE_OK ;
cleanup_bridge ( bridge ) ;
return AE_OK ;
}
static void remove_bridge ( acpi_handle handle )
{
struct acpiphp_bridge * bridge ;
2006-03-22 08:49:20 +03:00
/* cleanup p2p bridges under this host bridge
in a depth - first manner */
acpi_walk_namespace ( ACPI_TYPE_DEVICE , handle ,
( u32 ) 1 , cleanup_p2p_bridge , NULL , NULL ) ;
2005-04-28 11:25:54 +04:00
bridge = acpiphp_handle_to_bridge ( handle ) ;
2006-03-22 08:49:20 +03:00
if ( bridge )
2005-04-28 11:25:54 +04:00
cleanup_bridge ( bridge ) ;
}
2005-04-17 02:20:36 +04:00
2005-04-28 11:25:59 +04:00
static struct pci_dev * get_apic_pci_info ( acpi_handle handle )
{
struct acpi_pci_id id ;
struct pci_bus * bus ;
struct pci_dev * dev ;
if ( ACPI_FAILURE ( acpi_get_pci_id ( handle , & id ) ) )
return NULL ;
bus = pci_find_bus ( id . segment , id . bus ) ;
if ( ! bus )
return NULL ;
dev = pci_get_slot ( bus , PCI_DEVFN ( id . device , id . function ) ) ;
if ( ! dev )
return NULL ;
if ( ( dev - > class ! = PCI_CLASS_SYSTEM_PIC_IOAPIC ) & &
( dev - > class ! = PCI_CLASS_SYSTEM_PIC_IOXAPIC ) )
{
pci_dev_put ( dev ) ;
return NULL ;
}
return dev ;
}
static int get_gsi_base ( acpi_handle handle , u32 * gsi_base )
{
acpi_status status ;
int result = - 1 ;
unsigned long gsb ;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object * obj ;
void * table ;
status = acpi_evaluate_integer ( handle , " _GSB " , NULL , & gsb ) ;
if ( ACPI_SUCCESS ( status ) ) {
* gsi_base = ( u32 ) gsb ;
return 0 ;
}
status = acpi_evaluate_object ( handle , " _MAT " , NULL , & buffer ) ;
if ( ACPI_FAILURE ( status ) | | ! buffer . length | | ! buffer . pointer )
return - 1 ;
obj = buffer . pointer ;
if ( obj - > type ! = ACPI_TYPE_BUFFER )
goto out ;
table = obj - > buffer . pointer ;
switch ( ( ( acpi_table_entry_header * ) table ) - > type ) {
case ACPI_MADT_IOSAPIC :
* gsi_base = ( ( struct acpi_table_iosapic * ) table ) - > global_irq_base ;
result = 0 ;
break ;
case ACPI_MADT_IOAPIC :
* gsi_base = ( ( struct acpi_table_ioapic * ) table ) - > global_irq_base ;
result = 0 ;
break ;
default :
break ;
}
out :
2006-04-19 01:36:43 +04:00
kfree ( buffer . pointer ) ;
2005-04-28 11:25:59 +04:00
return result ;
}
static acpi_status
ioapic_add ( acpi_handle handle , u32 lvl , void * context , void * * rv )
{
acpi_status status ;
unsigned long sta ;
acpi_handle tmp ;
struct pci_dev * pdev ;
u32 gsi_base ;
u64 phys_addr ;
2006-09-12 21:22:53 +04:00
struct acpiphp_ioapic * ioapic ;
2005-04-28 11:25:59 +04:00
/* Evaluate _STA if present */
status = acpi_evaluate_integer ( handle , " _STA " , NULL , & sta ) ;
if ( ACPI_SUCCESS ( status ) & & sta ! = ACPI_STA_ALL )
return AE_CTRL_DEPTH ;
/* Scan only PCI bus scope */
status = acpi_get_handle ( handle , " _HID " , & tmp ) ;
if ( ACPI_SUCCESS ( status ) )
return AE_CTRL_DEPTH ;
if ( get_gsi_base ( handle , & gsi_base ) )
return AE_OK ;
2006-09-12 21:22:53 +04:00
ioapic = kmalloc ( sizeof ( * ioapic ) , GFP_KERNEL ) ;
if ( ! ioapic )
return AE_NO_MEMORY ;
2005-04-28 11:25:59 +04:00
pdev = get_apic_pci_info ( handle ) ;
if ( ! pdev )
2006-09-12 21:22:53 +04:00
goto exit_kfree ;
2005-04-28 11:25:59 +04:00
2006-09-12 21:22:53 +04:00
if ( pci_enable_device ( pdev ) )
goto exit_pci_dev_put ;
2005-04-28 11:25:59 +04:00
pci_set_master ( pdev ) ;
2006-09-12 21:22:53 +04:00
if ( pci_request_region ( pdev , 0 , " I/O APIC(acpiphp) " ) )
goto exit_pci_disable_device ;
2005-04-28 11:25:59 +04:00
phys_addr = pci_resource_start ( pdev , 0 ) ;
2006-09-12 21:22:53 +04:00
if ( acpi_register_ioapic ( handle , phys_addr , gsi_base ) )
goto exit_pci_release_region ;
ioapic - > gsi_base = gsi_base ;
ioapic - > dev = pdev ;
spin_lock ( & ioapic_list_lock ) ;
list_add_tail ( & ioapic - > list , & ioapic_list ) ;
spin_unlock ( & ioapic_list_lock ) ;
return AE_OK ;
exit_pci_release_region :
pci_release_region ( pdev , 0 ) ;
exit_pci_disable_device :
pci_disable_device ( pdev ) ;
exit_pci_dev_put :
pci_dev_put ( pdev ) ;
exit_kfree :
kfree ( ioapic ) ;
return AE_OK ;
}
static acpi_status
ioapic_remove ( acpi_handle handle , u32 lvl , void * context , void * * rv )
{
acpi_status status ;
unsigned long sta ;
acpi_handle tmp ;
u32 gsi_base ;
struct acpiphp_ioapic * pos , * n , * ioapic = NULL ;
/* Evaluate _STA if present */
status = acpi_evaluate_integer ( handle , " _STA " , NULL , & sta ) ;
if ( ACPI_SUCCESS ( status ) & & sta ! = ACPI_STA_ALL )
return AE_CTRL_DEPTH ;
/* Scan only PCI bus scope */
status = acpi_get_handle ( handle , " _HID " , & tmp ) ;
if ( ACPI_SUCCESS ( status ) )
return AE_CTRL_DEPTH ;
if ( get_gsi_base ( handle , & gsi_base ) )
2005-04-28 11:25:59 +04:00
return AE_OK ;
2006-09-12 21:22:53 +04:00
acpi_unregister_ioapic ( handle , gsi_base ) ;
spin_lock ( & ioapic_list_lock ) ;
list_for_each_entry_safe ( pos , n , & ioapic_list , list ) {
if ( pos - > gsi_base ! = gsi_base )
continue ;
ioapic = pos ;
list_del ( & ioapic - > list ) ;
break ;
2005-04-28 11:25:59 +04:00
}
2006-09-12 21:22:53 +04:00
spin_unlock ( & ioapic_list_lock ) ;
if ( ! ioapic )
return AE_OK ;
pci_release_region ( ioapic - > dev , 0 ) ;
pci_disable_device ( ioapic - > dev ) ;
pci_dev_put ( ioapic - > dev ) ;
kfree ( ioapic ) ;
2005-04-28 11:25:59 +04:00
return AE_OK ;
}
static int acpiphp_configure_ioapics ( acpi_handle handle )
{
2006-09-12 21:15:10 +04:00
ioapic_add ( handle , 0 , NULL , NULL ) ;
2005-04-28 11:25:59 +04:00
acpi_walk_namespace ( ACPI_TYPE_DEVICE , handle ,
ACPI_UINT32_MAX , ioapic_add , NULL , NULL ) ;
return 0 ;
}
2006-09-12 21:22:53 +04:00
static int acpiphp_unconfigure_ioapics ( acpi_handle handle )
{
ioapic_remove ( handle , 0 , NULL , NULL ) ;
acpi_walk_namespace ( ACPI_TYPE_DEVICE , handle ,
ACPI_UINT32_MAX , ioapic_remove , NULL , NULL ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
static int power_on_slot ( struct acpiphp_slot * slot )
{
acpi_status status ;
struct acpiphp_func * func ;
struct list_head * l ;
int retval = 0 ;
/* if already enabled, just skip */
if ( slot - > flags & SLOT_POWEREDON )
goto err_exit ;
list_for_each ( l , & slot - > funcs ) {
func = list_entry ( l , struct acpiphp_func , sibling ) ;
if ( func - > flags & FUNC_HAS_PS0 ) {
dbg ( " %s: executing _PS0 \n " , __FUNCTION__ ) ;
status = acpi_evaluate_object ( func - > handle , " _PS0 " , NULL , NULL ) ;
if ( ACPI_FAILURE ( status ) ) {
warn ( " %s: _PS0 failed \n " , __FUNCTION__ ) ;
retval = - 1 ;
goto err_exit ;
} else
break ;
}
}
/* TBD: evaluate _STA to check if the slot is enabled */
slot - > flags | = SLOT_POWEREDON ;
err_exit :
return retval ;
}
static int power_off_slot ( struct acpiphp_slot * slot )
{
acpi_status status ;
struct acpiphp_func * func ;
struct list_head * l ;
int retval = 0 ;
/* if already disabled, just skip */
if ( ( slot - > flags & SLOT_POWEREDON ) = = 0 )
goto err_exit ;
list_for_each ( l , & slot - > funcs ) {
func = list_entry ( l , struct acpiphp_func , sibling ) ;
2005-04-28 11:25:55 +04:00
if ( func - > flags & FUNC_HAS_PS3 ) {
2005-04-17 02:20:36 +04:00
status = acpi_evaluate_object ( func - > handle , " _PS3 " , NULL , NULL ) ;
if ( ACPI_FAILURE ( status ) ) {
warn ( " %s: _PS3 failed \n " , __FUNCTION__ ) ;
retval = - 1 ;
goto err_exit ;
} else
break ;
}
}
/* TBD: evaluate _STA to check if the slot is disabled */
slot - > flags & = ( ~ SLOT_POWEREDON ) ;
err_exit :
return retval ;
}
2006-02-24 04:55:58 +03:00
/**
* acpiphp_max_busnr - return the highest reserved bus number under
* the given bus .
* @ bus : bus to start search with
*
*/
static unsigned char acpiphp_max_busnr ( struct pci_bus * bus )
{
struct list_head * tmp ;
unsigned char max , n ;
/*
* pci_bus_max_busnr will return the highest
* reserved busnr for all these children .
* that is equivalent to the bus - > subordinate
* value . We don ' t want to use the parent ' s
* bus - > subordinate value because it could have
* padding in it .
*/
max = bus - > secondary ;
list_for_each ( tmp , & bus - > children ) {
n = pci_bus_max_busnr ( pci_bus_b ( tmp ) ) ;
if ( n > max )
max = n ;
}
return max ;
}
/**
* acpiphp_bus_add - add a new bus to acpi subsystem
* @ func : acpiphp_func of the bridge
*
*/
static int acpiphp_bus_add ( struct acpiphp_func * func )
{
acpi_handle phandle ;
struct acpi_device * device , * pdevice ;
int ret_val ;
acpi_get_parent ( func - > handle , & phandle ) ;
if ( acpi_bus_get_device ( phandle , & pdevice ) ) {
dbg ( " no parent device, assuming NULL \n " ) ;
pdevice = NULL ;
}
2006-02-24 04:56:03 +03:00
if ( ! acpi_bus_get_device ( func - > handle , & device ) ) {
dbg ( " bus exists... trim \n " ) ;
/* this shouldn't be in here, so remove
* the bus then re - add it . . .
*/
ret_val = acpi_bus_trim ( device , 1 ) ;
dbg ( " acpi_bus_trim return %x \n " , ret_val ) ;
}
ret_val = acpi_bus_add ( & device , pdevice , func - > handle ,
ACPI_BUS_TYPE_DEVICE ) ;
if ( ret_val ) {
dbg ( " error adding bus, %x \n " ,
- ret_val ) ;
goto acpiphp_bus_add_out ;
2006-02-24 04:55:58 +03:00
}
/*
* try to start anyway . We could have failed to add
* simply because this bus had previously been added
* on another add . Don ' t bother with the return value
* we just keep going .
*/
ret_val = acpi_bus_start ( device ) ;
acpiphp_bus_add_out :
return ret_val ;
}
2006-03-22 08:49:09 +03:00
/**
* acpiphp_bus_trim - trim a bus from acpi subsystem
* @ handle : handle to acpi namespace
*
*/
2006-08-15 10:07:38 +04:00
static int acpiphp_bus_trim ( acpi_handle handle )
2006-03-22 08:49:09 +03:00
{
struct acpi_device * device ;
int retval ;
retval = acpi_bus_get_device ( handle , & device ) ;
if ( retval ) {
dbg ( " acpi_device not found \n " ) ;
return retval ;
}
retval = acpi_bus_trim ( device , 1 ) ;
if ( retval )
err ( " cannot remove from acpi list \n " ) ;
return retval ;
}
2006-02-24 04:55:58 +03:00
2005-04-17 02:20:36 +04:00
/**
* enable_device - enable , configure a slot
* @ slot : slot to be enabled
*
* This function should be called per * physical slot * ,
* not per each slot object in ACPI namespace .
*
*/
static int enable_device ( struct acpiphp_slot * slot )
{
struct pci_dev * dev ;
2005-04-28 11:25:53 +04:00
struct pci_bus * bus = slot - > bridge - > pci_bus ;
2005-04-17 02:20:36 +04:00
struct list_head * l ;
struct acpiphp_func * func ;
int retval = 0 ;
2005-04-28 11:25:53 +04:00
int num , max , pass ;
2006-03-22 08:49:20 +03:00
acpi_status status ;
2005-04-17 02:20:36 +04:00
if ( slot - > flags & SLOT_ENABLED )
goto err_exit ;
/* sanity check: dev should be NULL when hot-plugged in */
2005-04-28 11:25:53 +04:00
dev = pci_get_slot ( bus , PCI_DEVFN ( slot - > device , 0 ) ) ;
2005-04-17 02:20:36 +04:00
if ( dev ) {
/* This case shouldn't happen */
err ( " pci_dev structure already exists. \n " ) ;
2005-04-28 11:25:53 +04:00
pci_dev_put ( dev ) ;
2005-04-17 02:20:36 +04:00
retval = - 1 ;
goto err_exit ;
}
2005-04-28 11:25:53 +04:00
num = pci_scan_slot ( bus , PCI_DEVFN ( slot - > device , 0 ) ) ;
if ( num = = 0 ) {
2005-04-17 02:20:36 +04:00
err ( " No new device found \n " ) ;
retval = - 1 ;
goto err_exit ;
}
2006-02-24 04:55:58 +03:00
max = acpiphp_max_busnr ( bus ) ;
2005-04-28 11:25:53 +04:00
for ( pass = 0 ; pass < 2 ; pass + + ) {
list_for_each_entry ( dev , & bus - > devices , bus_list ) {
if ( PCI_SLOT ( dev - > devfn ) ! = slot - > device )
continue ;
if ( dev - > hdr_type = = PCI_HEADER_TYPE_BRIDGE | |
2005-12-14 20:37:26 +03:00
dev - > hdr_type = = PCI_HEADER_TYPE_CARDBUS ) {
2005-04-28 11:25:53 +04:00
max = pci_scan_bridge ( bus , dev , max , pass ) ;
2006-03-22 08:49:09 +03:00
if ( pass & & dev - > subordinate )
2005-12-14 20:37:26 +03:00
pci_bus_size_bridges ( dev - > subordinate ) ;
}
2005-04-28 11:25:53 +04:00
}
2005-04-17 02:20:36 +04:00
}
2006-03-22 08:49:09 +03:00
list_for_each ( l , & slot - > funcs ) {
func = list_entry ( l , struct acpiphp_func , sibling ) ;
acpiphp_bus_add ( func ) ;
}
2005-04-28 11:25:53 +04:00
pci_bus_assign_resources ( bus ) ;
2005-10-19 04:21:40 +04:00
acpiphp_sanitize_bus ( bus ) ;
2006-09-12 21:12:16 +04:00
acpiphp_set_hpp_values ( slot - > bridge - > handle , bus ) ;
2006-09-12 21:15:10 +04:00
list_for_each_entry ( func , & slot - > funcs , sibling )
acpiphp_configure_ioapics ( func - > handle ) ;
2005-10-19 04:21:40 +04:00
pci_enable_bridges ( bus ) ;
2005-04-28 11:25:53 +04:00
pci_bus_add_devices ( bus ) ;
2005-04-17 02:20:36 +04:00
/* associate pci_dev to our representation */
list_for_each ( l , & slot - > funcs ) {
func = list_entry ( l , struct acpiphp_func , sibling ) ;
2005-04-28 11:25:53 +04:00
func - > pci_dev = pci_get_slot ( bus , PCI_DEVFN ( slot - > device ,
2005-04-17 02:20:36 +04:00
func - > function ) ) ;
2006-03-22 08:49:20 +03:00
if ( ! func - > pci_dev )
continue ;
if ( func - > pci_dev - > hdr_type ! = PCI_HEADER_TYPE_BRIDGE & &
func - > pci_dev - > hdr_type ! = PCI_HEADER_TYPE_CARDBUS )
continue ;
status = find_p2p_bridge ( func - > handle , ( u32 ) 1 , bus , NULL ) ;
if ( ACPI_FAILURE ( status ) )
warn ( " find_p2p_bridge failed (error code = 0x%x) \n " ,
status ) ;
2005-04-17 02:20:36 +04:00
}
slot - > flags | = SLOT_ENABLED ;
err_exit :
return retval ;
}
2006-09-12 21:19:00 +04:00
static void disable_bridges ( struct pci_bus * bus )
{
struct pci_dev * dev ;
list_for_each_entry ( dev , & bus - > devices , bus_list ) {
if ( dev - > subordinate ) {
disable_bridges ( dev - > subordinate ) ;
pci_disable_device ( dev ) ;
}
}
}
2005-04-17 02:20:36 +04:00
/**
* disable_device - disable a slot
*/
static int disable_device ( struct acpiphp_slot * slot )
{
int retval = 0 ;
struct acpiphp_func * func ;
struct list_head * l ;
/* is this slot already disabled? */
if ( ! ( slot - > flags & SLOT_ENABLED ) )
goto err_exit ;
list_for_each ( l , & slot - > funcs ) {
func = list_entry ( l , struct acpiphp_func , sibling ) ;
2006-03-22 08:49:09 +03:00
2006-03-22 08:49:20 +03:00
if ( func - > bridge ) {
/* cleanup p2p bridges under this P2P bridge */
cleanup_p2p_bridge ( func - > bridge - > handle ,
( u32 ) 1 , NULL , NULL ) ;
func - > bridge = NULL ;
}
2006-09-12 21:19:00 +04:00
if ( func - > pci_dev ) {
2006-09-12 21:17:46 +04:00
pci_stop_bus_device ( func - > pci_dev ) ;
2006-09-12 21:19:00 +04:00
if ( func - > pci_dev - > subordinate ) {
disable_bridges ( func - > pci_dev - > subordinate ) ;
pci_disable_device ( func - > pci_dev ) ;
}
}
2006-09-12 21:22:53 +04:00
}
list_for_each ( l , & slot - > funcs ) {
func = list_entry ( l , struct acpiphp_func , sibling ) ;
2006-09-12 21:17:46 +04:00
2006-09-12 21:22:53 +04:00
acpiphp_unconfigure_ioapics ( func - > handle ) ;
2006-03-22 08:49:09 +03:00
acpiphp_bus_trim ( func - > handle ) ;
/* try to remove anyway.
* acpiphp_bus_add might have been failed */
2005-04-28 11:25:53 +04:00
if ( ! func - > pci_dev )
continue ;
2005-04-17 02:20:36 +04:00
2005-04-28 11:25:53 +04:00
pci_remove_bus_device ( func - > pci_dev ) ;
pci_dev_put ( func - > pci_dev ) ;
func - > pci_dev = NULL ;
2005-04-17 02:20:36 +04:00
}
slot - > flags & = ( ~ SLOT_ENABLED ) ;
err_exit :
return retval ;
}
/**
* get_slot_status - get ACPI slot status
*
* if a slot has _STA for each function and if any one of them
* returned non - zero status , return it
*
* if a slot doesn ' t have _STA and if any one of its functions '
* configuration space is configured , return 0x0f as a _STA
*
* otherwise return 0
*/
static unsigned int get_slot_status ( struct acpiphp_slot * slot )
{
acpi_status status ;
unsigned long sta = 0 ;
u32 dvid ;
struct list_head * l ;
struct acpiphp_func * func ;
list_for_each ( l , & slot - > funcs ) {
func = list_entry ( l , struct acpiphp_func , sibling ) ;
if ( func - > flags & FUNC_HAS_STA ) {
status = acpi_evaluate_integer ( func - > handle , " _STA " , NULL , & sta ) ;
if ( ACPI_SUCCESS ( status ) & & sta )
break ;
} else {
pci_bus_read_config_dword ( slot - > bridge - > pci_bus ,
PCI_DEVFN ( slot - > device ,
func - > function ) ,
PCI_VENDOR_ID , & dvid ) ;
if ( dvid ! = 0xffffffff ) {
sta = ACPI_STA_ALL ;
break ;
}
}
}
return ( unsigned int ) sta ;
}
2005-04-28 11:25:57 +04:00
/**
* acpiphp_eject_slot - physically eject the slot
*/
static int acpiphp_eject_slot ( struct acpiphp_slot * slot )
{
acpi_status status ;
struct acpiphp_func * func ;
struct list_head * l ;
struct acpi_object_list arg_list ;
union acpi_object arg ;
list_for_each ( l , & slot - > funcs ) {
func = list_entry ( l , struct acpiphp_func , sibling ) ;
/* We don't want to call _EJ0 on non-existing functions. */
if ( ( func - > flags & FUNC_HAS_EJ0 ) ) {
/* _EJ0 method take one argument */
arg_list . count = 1 ;
arg_list . pointer = & arg ;
arg . type = ACPI_TYPE_INTEGER ;
arg . integer . value = 1 ;
status = acpi_evaluate_object ( func - > handle , " _EJ0 " , & arg_list , NULL ) ;
if ( ACPI_FAILURE ( status ) ) {
warn ( " %s: _EJ0 failed \n " , __FUNCTION__ ) ;
return - 1 ;
} else
break ;
}
}
return 0 ;
}
2005-04-17 02:20:36 +04:00
/**
* acpiphp_check_bridge - re - enumerate devices
*
* Iterate over all slots under this bridge and make sure that if a
* card is present they are enabled , and if not they are disabled .
*/
static int acpiphp_check_bridge ( struct acpiphp_bridge * bridge )
{
struct acpiphp_slot * slot ;
int retval = 0 ;
int enabled , disabled ;
enabled = disabled = 0 ;
for ( slot = bridge - > slots ; slot ; slot = slot - > next ) {
unsigned int status = get_slot_status ( slot ) ;
if ( slot - > flags & SLOT_ENABLED ) {
if ( status = = ACPI_STA_ALL )
continue ;
retval = acpiphp_disable_slot ( slot ) ;
if ( retval ) {
err ( " Error occurred in disabling \n " ) ;
goto err_exit ;
2005-04-28 11:25:57 +04:00
} else {
acpiphp_eject_slot ( slot ) ;
2005-04-17 02:20:36 +04:00
}
disabled + + ;
} else {
if ( status ! = ACPI_STA_ALL )
continue ;
retval = acpiphp_enable_slot ( slot ) ;
if ( retval ) {
err ( " Error occurred in enabling \n " ) ;
goto err_exit ;
}
enabled + + ;
}
}
dbg ( " %s: %d enabled, %d disabled \n " , __FUNCTION__ , enabled , disabled ) ;
err_exit :
return retval ;
}
2005-04-28 11:25:56 +04:00
static void program_hpp ( struct pci_dev * dev , struct acpiphp_bridge * bridge )
{
u16 pci_cmd , pci_bctl ;
struct pci_dev * cdev ;
/* Program hpp values for this device */
if ( ! ( dev - > hdr_type = = PCI_HEADER_TYPE_NORMAL | |
( dev - > hdr_type = = PCI_HEADER_TYPE_BRIDGE & &
( dev - > class > > 8 ) = = PCI_CLASS_BRIDGE_PCI ) ) )
return ;
2006-05-02 05:57:14 +04:00
2005-04-28 11:25:56 +04:00
pci_write_config_byte ( dev , PCI_CACHE_LINE_SIZE ,
2006-05-02 05:57:14 +04:00
bridge - > hpp . t0 - > cache_line_size ) ;
2005-04-28 11:25:56 +04:00
pci_write_config_byte ( dev , PCI_LATENCY_TIMER ,
2006-05-02 05:57:14 +04:00
bridge - > hpp . t0 - > latency_timer ) ;
2005-04-28 11:25:56 +04:00
pci_read_config_word ( dev , PCI_COMMAND , & pci_cmd ) ;
2006-05-02 05:57:14 +04:00
if ( bridge - > hpp . t0 - > enable_serr )
2005-04-28 11:25:56 +04:00
pci_cmd | = PCI_COMMAND_SERR ;
else
pci_cmd & = ~ PCI_COMMAND_SERR ;
2006-05-02 05:57:14 +04:00
if ( bridge - > hpp . t0 - > enable_perr )
2005-04-28 11:25:56 +04:00
pci_cmd | = PCI_COMMAND_PARITY ;
else
pci_cmd & = ~ PCI_COMMAND_PARITY ;
pci_write_config_word ( dev , PCI_COMMAND , pci_cmd ) ;
/* Program bridge control value and child devices */
if ( ( dev - > class > > 8 ) = = PCI_CLASS_BRIDGE_PCI ) {
pci_write_config_byte ( dev , PCI_SEC_LATENCY_TIMER ,
2006-05-02 05:57:14 +04:00
bridge - > hpp . t0 - > latency_timer ) ;
2005-04-28 11:25:56 +04:00
pci_read_config_word ( dev , PCI_BRIDGE_CONTROL , & pci_bctl ) ;
2006-05-02 05:57:14 +04:00
if ( bridge - > hpp . t0 - > enable_serr )
2005-04-28 11:25:56 +04:00
pci_bctl | = PCI_BRIDGE_CTL_SERR ;
else
pci_bctl & = ~ PCI_BRIDGE_CTL_SERR ;
2006-05-02 05:57:14 +04:00
if ( bridge - > hpp . t0 - > enable_perr )
2005-04-28 11:25:56 +04:00
pci_bctl | = PCI_BRIDGE_CTL_PARITY ;
else
pci_bctl & = ~ PCI_BRIDGE_CTL_PARITY ;
pci_write_config_word ( dev , PCI_BRIDGE_CONTROL , pci_bctl ) ;
if ( dev - > subordinate ) {
list_for_each_entry ( cdev , & dev - > subordinate - > devices ,
bus_list )
program_hpp ( cdev , bridge ) ;
}
}
}
static void acpiphp_set_hpp_values ( acpi_handle handle , struct pci_bus * bus )
{
struct acpiphp_bridge bridge ;
struct pci_dev * dev ;
memset ( & bridge , 0 , sizeof ( bridge ) ) ;
bridge . handle = handle ;
2006-05-02 05:54:50 +04:00
bridge . pci_bus = bus ;
2006-03-03 21:16:05 +03:00
bridge . pci_dev = bus - > self ;
2005-04-28 11:25:56 +04:00
decode_hpp ( & bridge ) ;
list_for_each_entry ( dev , & bus - > devices , bus_list )
program_hpp ( dev , & bridge ) ;
}
/*
* Remove devices for which we could not assign resources , call
* arch specific code to fix - up the bus
*/
static void acpiphp_sanitize_bus ( struct pci_bus * bus )
{
struct pci_dev * dev ;
int i ;
unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM ;
list_for_each_entry ( dev , & bus - > devices , bus_list ) {
for ( i = 0 ; i < PCI_BRIDGE_RESOURCES ; i + + ) {
struct resource * res = & dev - > resource [ i ] ;
if ( ( res - > flags & type_mask ) & & ! res - > start & &
res - > end ) {
/* Could not assign a required resources
* for this device , remove it */
pci_remove_bus_device ( dev ) ;
break ;
}
}
}
}
/* Program resources in newly inserted bridge */
static int acpiphp_configure_bridge ( acpi_handle handle )
{
struct acpi_pci_id pci_id ;
struct pci_bus * bus ;
if ( ACPI_FAILURE ( acpi_get_pci_id ( handle , & pci_id ) ) ) {
err ( " cannot get PCI domain and bus number for bridge \n " ) ;
return - EINVAL ;
}
bus = pci_find_bus ( pci_id . segment , pci_id . bus ) ;
if ( ! bus ) {
err ( " cannot find bus %d:%d \n " ,
pci_id . segment , pci_id . bus ) ;
return - EINVAL ;
}
pci_bus_size_bridges ( bus ) ;
pci_bus_assign_resources ( bus ) ;
acpiphp_sanitize_bus ( bus ) ;
acpiphp_set_hpp_values ( handle , bus ) ;
pci_enable_bridges ( bus ) ;
2005-04-28 11:25:59 +04:00
acpiphp_configure_ioapics ( handle ) ;
2005-04-28 11:25:56 +04:00
return 0 ;
}
static void handle_bridge_insertion ( acpi_handle handle , u32 type )
{
struct acpi_device * device , * pdevice ;
acpi_handle phandle ;
if ( ( type ! = ACPI_NOTIFY_BUS_CHECK ) & &
( type ! = ACPI_NOTIFY_DEVICE_CHECK ) ) {
err ( " unexpected notification type %d \n " , type ) ;
return ;
}
acpi_get_parent ( handle , & phandle ) ;
if ( acpi_bus_get_device ( phandle , & pdevice ) ) {
dbg ( " no parent device, assuming NULL \n " ) ;
pdevice = NULL ;
}
if ( acpi_bus_add ( & device , pdevice , handle , ACPI_BUS_TYPE_DEVICE ) ) {
err ( " cannot add bridge to acpi list \n " ) ;
return ;
}
if ( ! acpiphp_configure_bridge ( handle ) & &
! acpi_bus_start ( device ) )
add_bridge ( handle ) ;
else
err ( " cannot configure and start bridge \n " ) ;
}
2005-04-17 02:20:36 +04:00
/*
* ACPI event handlers
*/
/**
* handle_hotplug_event_bridge - handle ACPI event on bridges
*
* @ handle : Notify ( ) ' ed acpi_handle
* @ type : Notify code
* @ context : pointer to acpiphp_bridge structure
*
* handles ACPI event notification on { host , p2p } bridges
*
*/
static void handle_hotplug_event_bridge ( acpi_handle handle , u32 type , void * context )
{
struct acpiphp_bridge * bridge ;
char objname [ 64 ] ;
struct acpi_buffer buffer = { . length = sizeof ( objname ) ,
. pointer = objname } ;
2005-04-28 11:25:56 +04:00
struct acpi_device * device ;
2005-04-17 02:20:36 +04:00
2005-04-28 11:25:56 +04:00
if ( acpi_bus_get_device ( handle , & device ) ) {
/* This bridge must have just been physically inserted */
handle_bridge_insertion ( handle , type ) ;
return ;
}
bridge = acpiphp_handle_to_bridge ( handle ) ;
if ( ! bridge ) {
err ( " cannot get bridge info \n " ) ;
return ;
}
2005-04-17 02:20:36 +04:00
acpi_get_name ( handle , ACPI_FULL_PATHNAME , & buffer ) ;
switch ( type ) {
case ACPI_NOTIFY_BUS_CHECK :
/* bus re-enumerate */
dbg ( " %s: Bus check notify on %s \n " , __FUNCTION__ , objname ) ;
acpiphp_check_bridge ( bridge ) ;
break ;
case ACPI_NOTIFY_DEVICE_CHECK :
/* device check */
dbg ( " %s: Device check notify on %s \n " , __FUNCTION__ , objname ) ;
acpiphp_check_bridge ( bridge ) ;
break ;
case ACPI_NOTIFY_DEVICE_WAKE :
/* wake event */
dbg ( " %s: Device wake notify on %s \n " , __FUNCTION__ , objname ) ;
break ;
case ACPI_NOTIFY_EJECT_REQUEST :
/* request device eject */
dbg ( " %s: Device eject notify on %s \n " , __FUNCTION__ , objname ) ;
2006-03-22 08:49:20 +03:00
if ( ( bridge - > type ! = BRIDGE_TYPE_HOST ) & &
( bridge - > flags & BRIDGE_HAS_EJ0 ) ) {
struct acpiphp_slot * slot ;
slot = bridge - > func - > slot ;
if ( ! acpiphp_disable_slot ( slot ) )
acpiphp_eject_slot ( slot ) ;
}
2005-04-17 02:20:36 +04:00
break ;
case ACPI_NOTIFY_FREQUENCY_MISMATCH :
printk ( KERN_ERR " Device %s cannot be configured due "
" to a frequency mismatch \n " , objname ) ;
break ;
case ACPI_NOTIFY_BUS_MODE_MISMATCH :
printk ( KERN_ERR " Device %s cannot be configured due "
" to a bus mode mismatch \n " , objname ) ;
break ;
case ACPI_NOTIFY_POWER_FAULT :
printk ( KERN_ERR " Device %s has suffered a power fault \n " ,
objname ) ;
break ;
default :
warn ( " notify_handler: unknown event type 0x%x for %s \n " , type , objname ) ;
break ;
}
}
/**
* handle_hotplug_event_func - handle ACPI event on functions ( i . e . slots )
*
* @ handle : Notify ( ) ' ed acpi_handle
* @ type : Notify code
* @ context : pointer to acpiphp_func structure
*
* handles ACPI event notification on slots
*
*/
2006-06-27 09:50:14 +04:00
static void handle_hotplug_event_func ( acpi_handle handle , u32 type , void * context )
2005-04-17 02:20:36 +04:00
{
struct acpiphp_func * func ;
char objname [ 64 ] ;
struct acpi_buffer buffer = { . length = sizeof ( objname ) ,
. pointer = objname } ;
acpi_get_name ( handle , ACPI_FULL_PATHNAME , & buffer ) ;
func = ( struct acpiphp_func * ) context ;
switch ( type ) {
case ACPI_NOTIFY_BUS_CHECK :
/* bus re-enumerate */
dbg ( " %s: Bus check notify on %s \n " , __FUNCTION__ , objname ) ;
acpiphp_enable_slot ( func - > slot ) ;
break ;
case ACPI_NOTIFY_DEVICE_CHECK :
/* device check : re-enumerate from parent bus */
dbg ( " %s: Device check notify on %s \n " , __FUNCTION__ , objname ) ;
acpiphp_check_bridge ( func - > slot - > bridge ) ;
break ;
case ACPI_NOTIFY_DEVICE_WAKE :
/* wake event */
dbg ( " %s: Device wake notify on %s \n " , __FUNCTION__ , objname ) ;
break ;
case ACPI_NOTIFY_EJECT_REQUEST :
/* request device eject */
dbg ( " %s: Device eject notify on %s \n " , __FUNCTION__ , objname ) ;
2005-04-28 11:25:57 +04:00
if ( ! ( acpiphp_disable_slot ( func - > slot ) ) )
acpiphp_eject_slot ( func - > slot ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
warn ( " notify_handler: unknown event type 0x%x for %s \n " , type , objname ) ;
break ;
}
}
2005-04-28 11:25:56 +04:00
static acpi_status
find_root_bridges ( acpi_handle handle , u32 lvl , void * context , void * * rv )
{
int * count = ( int * ) context ;
2006-03-03 21:16:05 +03:00
if ( acpi_root_bridge ( handle ) ) {
2005-04-28 11:25:56 +04:00
acpi_install_notify_handler ( handle , ACPI_SYSTEM_NOTIFY ,
handle_hotplug_event_bridge , NULL ) ;
( * count ) + + ;
}
return AE_OK ;
}
2005-04-17 02:20:36 +04:00
static struct acpi_pci_driver acpi_pci_hp_driver = {
. add = add_bridge ,
. remove = remove_bridge ,
} ;
/**
* acpiphp_glue_init - initializes all PCI hotplug - ACPI glue data structures
*
*/
int __init acpiphp_glue_init ( void )
{
2005-04-28 11:25:56 +04:00
int num = 0 ;
2005-04-17 02:20:36 +04:00
2005-04-28 11:25:56 +04:00
acpi_walk_namespace ( ACPI_TYPE_DEVICE , ACPI_ROOT_OBJECT ,
ACPI_UINT32_MAX , find_root_bridges , & num , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( num < = 0 )
return - 1 ;
2005-04-28 11:25:56 +04:00
else
acpi_pci_register_driver ( & acpi_pci_hp_driver ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/**
* acpiphp_glue_exit - terminates all PCI hotplug - ACPI glue data structures
*
* This function frees all data allocated in acpiphp_glue_init ( )
*/
void __exit acpiphp_glue_exit ( void )
{
acpi_pci_unregister_driver ( & acpi_pci_hp_driver ) ;
}
/**
* acpiphp_get_num_slots - count number of slots in a system
*/
int __init acpiphp_get_num_slots ( void )
{
struct list_head * node ;
struct acpiphp_bridge * bridge ;
int num_slots ;
num_slots = 0 ;
list_for_each ( node , & bridge_list ) {
bridge = ( struct acpiphp_bridge * ) node ;
2005-04-28 11:25:53 +04:00
dbg ( " Bus %04x:%02x has %d slot%s \n " ,
pci_domain_nr ( bridge - > pci_bus ) ,
bridge - > pci_bus - > number , bridge - > nr_slots ,
bridge - > nr_slots = = 1 ? " " : " s " ) ;
2005-04-17 02:20:36 +04:00
num_slots + = bridge - > nr_slots ;
}
2005-04-28 11:25:53 +04:00
dbg ( " Total %d slots \n " , num_slots ) ;
2005-04-17 02:20:36 +04:00
return num_slots ;
}
#if 0
/**
* acpiphp_for_each_slot - call function for each slot
* @ fn : callback function
* @ data : context to be passed to callback function
*
*/
static int acpiphp_for_each_slot ( acpiphp_callback fn , void * data )
{
struct list_head * node ;
struct acpiphp_bridge * bridge ;
struct acpiphp_slot * slot ;
int retval = 0 ;
list_for_each ( node , & bridge_list ) {
bridge = ( struct acpiphp_bridge * ) node ;
for ( slot = bridge - > slots ; slot ; slot = slot - > next ) {
retval = fn ( slot , data ) ;
if ( ! retval )
goto err_exit ;
}
}
err_exit :
return retval ;
}
# endif
/**
* acpiphp_enable_slot - power on slot
*/
int acpiphp_enable_slot ( struct acpiphp_slot * slot )
{
int retval ;
2006-01-13 18:02:15 +03:00
mutex_lock ( & slot - > crit_sect ) ;
2005-04-17 02:20:36 +04:00
/* wake up all functions */
retval = power_on_slot ( slot ) ;
if ( retval )
goto err_exit ;
2006-03-22 08:49:33 +03:00
if ( get_slot_status ( slot ) = = ACPI_STA_ALL ) {
2005-04-17 02:20:36 +04:00
/* configure all functions */
retval = enable_device ( slot ) ;
2006-03-22 08:49:33 +03:00
if ( retval )
power_off_slot ( slot ) ;
} else {
dbg ( " %s: Slot status is not ACPI_STA_ALL \n " , __FUNCTION__ ) ;
power_off_slot ( slot ) ;
}
2005-04-17 02:20:36 +04:00
err_exit :
2006-01-13 18:02:15 +03:00
mutex_unlock ( & slot - > crit_sect ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
/**
* acpiphp_disable_slot - power off slot
*/
int acpiphp_disable_slot ( struct acpiphp_slot * slot )
{
int retval = 0 ;
2006-01-13 18:02:15 +03:00
mutex_lock ( & slot - > crit_sect ) ;
2005-04-17 02:20:36 +04:00
/* unconfigure all functions */
retval = disable_device ( slot ) ;
if ( retval )
goto err_exit ;
/* power off all functions */
retval = power_off_slot ( slot ) ;
if ( retval )
goto err_exit ;
err_exit :
2006-01-13 18:02:15 +03:00
mutex_unlock ( & slot - > crit_sect ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
/*
* slot enabled : 1
* slot disabled : 0
*/
u8 acpiphp_get_power_status ( struct acpiphp_slot * slot )
{
2005-04-28 11:25:57 +04:00
return ( slot - > flags & SLOT_POWEREDON ) ;
2005-04-17 02:20:36 +04:00
}
/*
* latch closed : 1
* latch open : 0
*/
u8 acpiphp_get_latch_status ( struct acpiphp_slot * slot )
{
unsigned int sta ;
sta = get_slot_status ( slot ) ;
return ( sta & ACPI_STA_SHOW_IN_UI ) ? 1 : 0 ;
}
/*
* adapter presence : 1
* absence : 0
*/
u8 acpiphp_get_adapter_status ( struct acpiphp_slot * slot )
{
unsigned int sta ;
sta = get_slot_status ( slot ) ;
return ( sta = = 0 ) ? 0 : 1 ;
}
/*
* pci address ( seg / bus / dev )
*/
u32 acpiphp_get_address ( struct acpiphp_slot * slot )
{
u32 address ;
2005-04-28 11:25:53 +04:00
struct pci_bus * pci_bus = slot - > bridge - > pci_bus ;
2005-04-17 02:20:36 +04:00
2005-04-28 11:25:53 +04:00
address = ( pci_domain_nr ( pci_bus ) < < 16 ) |
( pci_bus - > number < < 8 ) |
2005-04-17 02:20:36 +04:00
slot - > device ;
return address ;
}