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_bridge has its refcount elevated by pci_get_slot ( )
* when the bridge is scanned and it loses a refcount when the bridge
* is removed .
2009-03-30 20:50:14 +04:00
* - When a P2P bridge is present , we elevate the refcount on the subordinate
* bus . It loses the refcount when the the driver unloads .
2005-04-28 11:25:53 +04:00
*/
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>
2008-12-17 06:09:12 +03:00
# include <linux/pci-acpi.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 ) ;
2009-09-15 02:35:10 +04:00
static void acpiphp_set_hpp_values ( 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
2007-11-09 13:07:02 +03:00
/* callback routine to check for the existence of a pci dock device */
2006-06-28 11:08:06 +04:00
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 )
2008-12-11 21:17:55 +03:00
| ( ( unsigned int ) ( bus - > primary ) < < 0 )
| ( ( unsigned int ) ( bus - > secondary ) < < 8 )
| ( ( unsigned int ) ( bus - > subordinate ) < < 16 ) ;
2006-06-28 11:08:06 +04:00
pci_write_config_dword ( bus - > self , PCI_PRIMARY_BUS , buses ) ;
}
return NOTIFY_OK ;
}
2008-08-28 06:06:16 +04:00
static struct acpi_dock_ops acpiphp_dock_ops = {
. handler = handle_hotplug_event_func ,
} ;
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 ;
2008-10-10 10:22:59 +04:00
unsigned long long adr , sun ;
2006-02-24 04:56:08 +03:00
int device , function , retval ;
2008-12-17 06:09:12 +03:00
struct pci_bus * pbus = bridge - > pci_bus ;
2009-05-22 02:21:15 +04:00
struct pci_dev * pdev ;
2005-04-17 02:20:36 +04:00
2008-12-17 06:09:12 +03:00
if ( ! acpi_pci_check_ejectable ( pbus , handle ) & & ! is_dock_device ( handle ) )
2005-04-17 02:20:36 +04:00
return AE_OK ;
2008-11-26 00:48:14 +03:00
acpi_evaluate_integer ( handle , " _ADR " , NULL , & adr ) ;
2005-04-17 02:20:36 +04:00
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 ;
2008-11-26 00:48:14 +03:00
if ( ACPI_SUCCESS ( acpi_get_handle ( handle , " _EJ0 " , & tmp ) ) )
2006-02-24 04:56:03 +03:00
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 + + ;
2008-12-11 21:16:44 +03:00
dbg ( " found ACPI PCI Hotplug slot %llu at PCI %04x:%02x:%02x \n " ,
2008-12-17 06:09:12 +03:00
slot - > sun , pci_domain_nr ( pbus ) , pbus - > number , device ) ;
2006-02-24 04:56:08 +03:00
retval = acpiphp_register_hotplug_slot ( slot ) ;
if ( retval ) {
2008-06-11 01:28:50 +04:00
if ( retval = = - EBUSY )
2008-12-11 21:16:44 +03:00
warn ( " Slot %llu already registered by another "
2008-06-11 01:28:50 +04:00
" hotplug driver \n " , slot - > sun ) ;
else
warn ( " acpiphp_register_hotplug_slot failed "
" (err code = 0x%x) \n " , retval ) ;
2006-02-24 04:56:08 +03:00
goto err_exit ;
}
2005-04-17 02:20:36 +04:00
}
newfunc - > slot = slot ;
list_add_tail ( & newfunc - > sibling , & slot - > funcs ) ;
2009-05-22 02:21:15 +04:00
pdev = pci_get_slot ( pbus , PCI_DEVFN ( device , function ) ) ;
if ( pdev ) {
2005-04-17 02:20:36 +04:00
slot - > flags | = ( SLOT_ENABLED | SLOT_POWEREDON ) ;
2009-05-22 02:21:15 +04:00
pci_dev_put ( pdev ) ;
2005-04-17 02:20:36 +04:00
}
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 ,
2008-08-28 06:06:16 +04:00
& acpiphp_dock_ops , newfunc ) )
2006-06-28 11:08:06 +04:00
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 */
2009-09-10 22:34:04 +04:00
static int detect_ejectable_slots ( acpi_handle handle )
2005-04-17 02:20:36 +04:00
{
2009-09-10 22:34:09 +04:00
int found = acpi_pci_detect_ejectable ( handle ) ;
2008-12-17 06:09:12 +03:00
if ( ! found ) {
2009-09-10 22:34:04 +04:00
acpi_walk_namespace ( ACPI_TYPE_DEVICE , handle , ( u32 ) 1 ,
2008-12-17 06:09:12 +03:00
is_pci_dock_device , ( void * ) & found , NULL ) ;
}
return found ;
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 ;
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 */
2009-09-10 22:34:04 +04:00
static void add_host_bridge ( acpi_handle * handle )
2005-04-17 02:20:36 +04:00
{
struct acpiphp_bridge * bridge ;
2009-09-10 22:34:04 +04:00
struct acpi_pci_root * root = acpi_pci_find_root ( handle ) ;
2005-04-17 02:20:36 +04:00
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 ;
2009-09-10 22:34:04 +04:00
bridge - > pci_bus = root - > 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 */
2009-09-10 22:34:04 +04:00
static void add_p2p_bridge ( acpi_handle * handle )
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
2009-09-10 22:34:04 +04:00
bridge - > pci_dev = acpi_get_pci_dev ( handle ) ;
bridge - > pci_bus = bridge - > 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
}
2009-03-30 20:50:14 +04:00
/*
* Grab a ref to the subordinate PCI bus in case the bus is
* removed via PCI core logical hotplug . The ref pins the bus
* ( which we access during module unload ) .
*/
get_device ( & bridge - > pci_bus - > dev ) ;
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 :
2009-09-10 22:34:04 +04:00
pci_dev_put ( bridge - > pci_dev ) ;
2005-04-28 11:25:53 +04:00
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 ;
struct pci_dev * dev ;
2009-09-10 22:34:04 +04:00
dev = acpi_get_pci_dev ( handle ) ;
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 */
2009-09-10 22:34:04 +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 ) ) ;
2009-09-10 22:34:04 +04:00
add_p2p_bridge ( handle ) ;
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 ,
2009-09-10 22:34:04 +04:00
find_p2p_bridge , NULL , NULL ) ;
[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
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 ;
2008-10-10 10:22:59 +04:00
unsigned long long tmp ;
2005-04-17 02:20:36 +04:00
acpi_handle dummy_handle ;
/* 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 ) ) {
2008-03-04 06:09:46 +03:00
dbg ( " %s: _STA evaluation failure \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
if ( ( tmp & ACPI_STA_FUNCTIONING ) = = 0 )
/* don't register this object */
return 0 ;
}
/* check if this bridge has ejectable slots */
2009-09-10 22:34:04 +04:00
if ( detect_ejectable_slots ( handle ) > 0 ) {
2005-04-17 02:20:36 +04:00
dbg ( " found PCI host-bus bridge with hot-pluggable slots \n " ) ;
2009-09-10 22:34:04 +04:00
add_host_bridge ( handle ) ;
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 ,
2009-09-10 22:34:04 +04:00
find_p2p_bridge , NULL , 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
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 ;
}
2009-03-30 20:50:14 +04:00
/*
* Only P2P bridges have a pci_dev
*/
if ( bridge - > pci_dev )
put_device ( & bridge - > pci_bus - > dev ) ;
2005-04-28 11:25:53 +04:00
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 ) ;
2008-07-02 06:02:23 +04:00
bridge = acpiphp_handle_to_bridge ( handle ) ;
if ( bridge )
cleanup_bridge ( bridge ) ;
2005-04-28 11:25:54 +04:00
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 ) ;
2008-07-02 06:02:23 +04:00
/*
* On root bridges with hotplug slots directly underneath ( ie ,
* no p2p bridge inbetween ) , we call cleanup_bridge ( ) .
*
* The else clause cleans up root bridges that either had no
* hotplug slots at all , or had a p2p bridge underneath .
*/
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 ) ;
2008-07-02 06:02:23 +04:00
else
acpi_remove_notify_handler ( handle , ACPI_SYSTEM_NOTIFY ,
handle_hotplug_event_bridge ) ;
2005-04-28 11:25:54 +04:00
}
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 pci_dev * dev ;
2009-06-10 23:55:50 +04:00
dev = acpi_get_pci_dev ( handle ) ;
2005-04-28 11:25:59 +04:00
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 ;
2008-10-10 10:22:59 +04:00
unsigned long long gsb ;
2005-04-28 11:25:59 +04:00
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 ;
2007-02-02 19:48:22 +03:00
switch ( ( ( struct acpi_subtable_header * ) table ) - > type ) {
case ACPI_MADT_TYPE_IO_SAPIC :
* gsi_base = ( ( struct acpi_madt_io_sapic * ) table ) - > global_irq_base ;
2005-04-28 11:25:59 +04:00
result = 0 ;
break ;
2007-02-02 19:48:22 +03:00
case ACPI_MADT_TYPE_IO_APIC :
* gsi_base = ( ( struct acpi_madt_io_apic * ) table ) - > global_irq_base ;
2005-04-28 11:25:59 +04:00
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 ;
2008-10-10 10:22:59 +04:00
unsigned long long sta ;
2005-04-28 11:25:59 +04:00
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 ;
2008-10-10 10:22:59 +04:00
unsigned long long sta ;
2006-09-12 21:22:53 +04:00
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 ) {
2008-03-04 06:09:46 +03:00
dbg ( " %s: executing _PS0 \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
status = acpi_evaluate_object ( func - > handle , " _PS0 " , NULL , NULL ) ;
if ( ACPI_FAILURE ( status ) ) {
2008-03-04 06:09:46 +03:00
warn ( " %s: _PS0 failed \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
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 ) ) {
2008-03-04 06:09:46 +03:00
warn ( " %s: _PS3 failed \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
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
/**
2007-11-28 20:04:30 +03:00
* acpiphp_max_busnr - return the highest reserved bus number under the given bus .
2006-02-24 04:55:58 +03:00
* @ 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 .
*/
2008-02-17 12:45:28 +03:00
static int __ref enable_device ( struct acpiphp_slot * slot )
2005-04-17 02:20:36 +04:00
{
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 ) ;
2009-09-15 02:35:10 +04:00
acpiphp_set_hpp_values ( 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
list_for_each ( l , & slot - > funcs ) {
func = list_entry ( l , struct acpiphp_func , sibling ) ;
2009-05-22 02:21:15 +04:00
dev = pci_get_slot ( bus , PCI_DEVFN ( slot - > device ,
func - > function ) ) ;
if ( ! dev )
2006-03-22 08:49:20 +03:00
continue ;
2009-05-22 02:21:15 +04:00
if ( dev - > hdr_type ! = PCI_HEADER_TYPE_BRIDGE & &
dev - > hdr_type ! = PCI_HEADER_TYPE_CARDBUS ) {
pci_dev_put ( dev ) ;
2006-03-22 08:49:20 +03:00
continue ;
2009-05-22 02:21:15 +04:00
}
2006-03-22 08:49:20 +03:00
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 ) ;
2009-05-22 02:21:15 +04:00
pci_dev_put ( dev ) ;
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
2007-11-28 20:04:30 +03:00
* @ slot : ACPI PHP slot
2005-04-17 02:20:36 +04:00
*/
static int disable_device ( struct acpiphp_slot * slot )
{
struct acpiphp_func * func ;
2009-05-22 02:21:15 +04:00
struct pci_dev * pdev ;
2005-04-17 02:20:36 +04:00
/* is this slot already disabled? */
if ( ! ( slot - > flags & SLOT_ENABLED ) )
goto err_exit ;
2009-05-22 02:21:15 +04:00
list_for_each_entry ( func , & slot - > funcs , sibling ) {
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 ;
}
2009-05-22 02:21:15 +04:00
pdev = pci_get_slot ( slot - > bridge - > pci_bus ,
PCI_DEVFN ( slot - > device , func - > function ) ) ;
if ( pdev ) {
pci_stop_bus_device ( pdev ) ;
if ( pdev - > subordinate ) {
disable_bridges ( pdev - > subordinate ) ;
pci_disable_device ( pdev ) ;
2006-09-12 21:19:00 +04:00
}
2009-05-22 02:21:15 +04:00
pci_remove_bus_device ( pdev ) ;
pci_dev_put ( pdev ) ;
2006-09-12 21:19:00 +04:00
}
2006-09-12 21:22:53 +04:00
}
2009-05-22 02:21:15 +04:00
list_for_each_entry ( func , & slot - > funcs , sibling ) {
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 ) ;
2005-04-17 02:20:36 +04:00
}
slot - > flags & = ( ~ SLOT_ENABLED ) ;
2009-05-22 02:21:15 +04:00
err_exit :
return 0 ;
2005-04-17 02:20:36 +04:00
}
/**
* get_slot_status - get ACPI slot status
2007-11-28 20:04:30 +03:00
* @ slot : ACPI PHP slot
2005-04-17 02:20:36 +04:00
*
2007-11-28 20:04:30 +03:00
* If a slot has _STA for each function and if any one of them
* returned non - zero status , return it .
2005-04-17 02:20:36 +04:00
*
2007-11-28 20:04:30 +03:00
* If a slot doesn ' t have _STA and if any one of its functions '
* configuration space is configured , return 0x0f as a _STA .
2005-04-17 02:20:36 +04:00
*
2007-11-28 20:04:30 +03:00
* Otherwise return 0.
2005-04-17 02:20:36 +04:00
*/
static unsigned int get_slot_status ( struct acpiphp_slot * slot )
{
acpi_status status ;
2008-10-10 10:22:59 +04:00
unsigned long long sta = 0 ;
2005-04-17 02:20:36 +04:00
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
2007-11-28 20:04:30 +03:00
* @ slot : ACPI PHP slot
2005-04-28 11:25:57 +04:00
*/
2007-07-05 22:10:46 +04:00
int acpiphp_eject_slot ( struct acpiphp_slot * slot )
2005-04-28 11:25:57 +04:00
{
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 ) ) {
2008-03-04 06:09:46 +03:00
warn ( " %s: _EJ0 failed \n " , __func__ ) ;
2005-04-28 11:25:57 +04:00
return - 1 ;
} else
break ;
}
}
return 0 ;
}
2005-04-17 02:20:36 +04:00
/**
* acpiphp_check_bridge - re - enumerate devices
2007-11-28 20:04:30 +03:00
* @ bridge : where to begin re - enumeration
2005-04-17 02:20:36 +04:00
*
* 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 + + ;
}
}
2008-03-04 06:09:46 +03:00
dbg ( " %s: %d enabled, %d disabled \n " , __func__ , enabled , disabled ) ;
2005-04-17 02:20:36 +04:00
err_exit :
return retval ;
}
2009-09-15 02:35:10 +04:00
static void acpiphp_set_hpp_values ( struct pci_bus * bus )
2005-04-28 11:25:56 +04:00
{
struct pci_dev * dev ;
2009-09-15 02:35:35 +04:00
list_for_each_entry ( dev , & bus - > devices , bus_list )
pci_configure_slot ( dev ) ;
2005-04-28 11:25:56 +04:00
}
/*
* 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 )
{
2009-09-10 22:34:09 +04:00
struct pci_bus * bus ;
if ( acpi_is_root_bridge ( handle ) ) {
struct acpi_pci_root * root = acpi_pci_find_root ( handle ) ;
bus = root - > bus ;
} else {
struct pci_dev * pdev = acpi_get_pci_dev ( handle ) ;
bus = pdev - > subordinate ;
pci_dev_put ( pdev ) ;
}
2005-04-28 11:25:56 +04:00
pci_bus_size_bridges ( bus ) ;
pci_bus_assign_resources ( bus ) ;
acpiphp_sanitize_bus ( bus ) ;
2009-09-15 02:35:10 +04:00
acpiphp_set_hpp_values ( bus ) ;
2005-04-28 11:25:56 +04:00
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
*/
2007-07-05 22:10:48 +04:00
static acpi_status
count_sub_bridges ( acpi_handle handle , u32 lvl , void * context , void * * rv )
{
int * count = ( int * ) context ;
struct acpiphp_bridge * bridge ;
bridge = acpiphp_handle_to_bridge ( handle ) ;
if ( bridge )
( * count ) + + ;
return AE_OK ;
}
static acpi_status
check_sub_bridges ( acpi_handle handle , u32 lvl , void * context , void * * rv )
{
struct acpiphp_bridge * bridge ;
char objname [ 64 ] ;
struct acpi_buffer buffer = { . length = sizeof ( objname ) ,
. pointer = objname } ;
bridge = acpiphp_handle_to_bridge ( handle ) ;
if ( bridge ) {
acpi_get_name ( handle , ACPI_FULL_PATHNAME , & buffer ) ;
dbg ( " %s: re-enumerating slots under %s \n " ,
2008-03-04 06:09:46 +03:00
__func__ , objname ) ;
2007-07-05 22:10:48 +04:00
acpiphp_check_bridge ( bridge ) ;
}
return AE_OK ;
}
2005-04-17 02:20:36 +04:00
/**
* handle_hotplug_event_bridge - handle ACPI event on bridges
* @ handle : Notify ( ) ' ed acpi_handle
* @ type : Notify code
* @ context : pointer to acpiphp_bridge structure
*
2007-11-28 20:04:30 +03:00
* Handles ACPI event notification on { host , p2p } bridges .
2005-04-17 02:20:36 +04:00
*/
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 ;
2007-07-05 22:10:48 +04:00
int num_sub_bridges = 0 ;
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 ) ;
2007-07-05 22:10:48 +04:00
if ( type = = ACPI_NOTIFY_BUS_CHECK ) {
acpi_walk_namespace ( ACPI_TYPE_DEVICE , handle , ACPI_UINT32_MAX ,
count_sub_bridges , & num_sub_bridges , NULL ) ;
}
if ( ! bridge & & ! num_sub_bridges ) {
2005-04-28 11:25:56 +04:00
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 */
2008-03-04 06:09:46 +03:00
dbg ( " %s: Bus check notify on %s \n " , __func__ , objname ) ;
2007-07-05 22:10:48 +04:00
if ( bridge ) {
dbg ( " %s: re-enumerating slots under %s \n " ,
2008-03-04 06:09:46 +03:00
__func__ , objname ) ;
2007-07-05 22:10:48 +04:00
acpiphp_check_bridge ( bridge ) ;
}
if ( num_sub_bridges )
acpi_walk_namespace ( ACPI_TYPE_DEVICE , handle ,
ACPI_UINT32_MAX , check_sub_bridges , NULL , NULL ) ;
2005-04-17 02:20:36 +04:00
break ;
case ACPI_NOTIFY_DEVICE_CHECK :
/* device check */
2008-03-04 06:09:46 +03:00
dbg ( " %s: Device check notify on %s \n " , __func__ , objname ) ;
2005-04-17 02:20:36 +04:00
acpiphp_check_bridge ( bridge ) ;
break ;
case ACPI_NOTIFY_DEVICE_WAKE :
/* wake event */
2008-03-04 06:09:46 +03:00
dbg ( " %s: Device wake notify on %s \n " , __func__ , objname ) ;
2005-04-17 02:20:36 +04:00
break ;
case ACPI_NOTIFY_EJECT_REQUEST :
/* request device eject */
2008-03-04 06:09:46 +03:00
dbg ( " %s: Device eject notify on %s \n " , __func__ , 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
*
2007-11-28 20:04:30 +03:00
* Handles ACPI event notification on slots .
2005-04-17 02:20:36 +04:00
*/
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 */
2008-03-04 06:09:46 +03:00
dbg ( " %s: Bus check notify on %s \n " , __func__ , objname ) ;
2005-04-17 02:20:36 +04:00
acpiphp_enable_slot ( func - > slot ) ;
break ;
case ACPI_NOTIFY_DEVICE_CHECK :
/* device check : re-enumerate from parent bus */
2008-03-04 06:09:46 +03:00
dbg ( " %s: Device check notify on %s \n " , __func__ , objname ) ;
2005-04-17 02:20:36 +04:00
acpiphp_check_bridge ( func - > slot - > bridge ) ;
break ;
case ACPI_NOTIFY_DEVICE_WAKE :
/* wake event */
2008-03-04 06:09:46 +03:00
dbg ( " %s: Device wake notify on %s \n " , __func__ , objname ) ;
2005-04-17 02:20:36 +04:00
break ;
case ACPI_NOTIFY_EJECT_REQUEST :
/* request device eject */
2008-03-04 06:09:46 +03:00
dbg ( " %s: Device eject notify on %s \n " , __func__ , 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 ;
2009-06-10 23:55:14 +04:00
if ( acpi_is_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
*
2007-11-28 20:04:30 +03:00
* This function frees all data allocated in acpiphp_glue_init ( ) .
2005-04-17 02:20:36 +04:00
*/
2006-12-17 02:26:04 +03:00
void acpiphp_glue_exit ( void )
2005-04-17 02:20:36 +04:00
{
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 acpiphp_bridge * bridge ;
2006-10-31 00:07:58 +03:00
int num_slots = 0 ;
2005-04-17 02:20:36 +04:00
2006-10-31 00:07:58 +03:00
list_for_each_entry ( bridge , & bridge_list , list ) {
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
2007-11-28 20:04:30 +03:00
* @ slot : ACPI PHP slot
2005-04-17 02:20:36 +04:00
*/
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 {
2008-03-04 06:09:46 +03:00
dbg ( " %s: Slot status is not ACPI_STA_ALL \n " , __func__ ) ;
2006-03-22 08:49:33 +03:00
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
2007-11-28 20:04:30 +03:00
* @ slot : ACPI PHP slot
2005-04-17 02:20:36 +04:00
*/
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
}
/*
2006-10-25 22:44:57 +04:00
* latch open : 1
* latch closed : 0
2005-04-17 02:20:36 +04:00
*/
u8 acpiphp_get_latch_status ( struct acpiphp_slot * slot )
{
unsigned int sta ;
sta = get_slot_status ( slot ) ;
2006-10-25 22:44:57 +04:00
return ( sta & ACPI_STA_SHOW_IN_UI ) ? 0 : 1 ;
2005-04-17 02:20:36 +04:00
}
/*
* 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 ;
}