2005-04-17 02:20:36 +04:00
/*
* ACPI PCI Hot Plug Controller Driver
*
* Copyright ( C ) 1995 , 2001 Compaq Computer Corporation
* Copyright ( C ) 2001 Greg Kroah - Hartman ( greg @ kroah . com )
* Copyright ( C ) 2001 IBM Corp .
* Copyright ( C ) 2002 Hiroshi Aono ( h - aono @ ap . jp . nec . com )
* Copyright ( C ) 2002 , 2003 Takayoshi Kochi ( t - kochi @ bq . 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-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
*
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/moduleparam.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/slab.h>
# include <linux/smp.h>
# include "acpiphp.h"
# define MY_NAME "acpiphp"
2008-10-21 03:41:12 +04:00
/* name size which is used for entries in pcihpfs */
# define SLOT_NAME_SIZE 21 /* {_SUN} */
2012-06-23 11:42:03 +04:00
bool acpiphp_debug ;
2013-04-12 09:44:25 +04:00
bool acpiphp_disabled ;
2005-04-17 02:20:36 +04:00
/* local variables */
static struct acpiphp_attention_info * attention_info ;
2005-04-28 11:25:53 +04:00
# define DRIVER_VERSION "0.5"
# define DRIVER_AUTHOR "Greg Kroah-Hartman <gregkh@us.ibm.com>, Takayoshi Kochi <t-kochi@bq.jp.nec.com>, Matthew Wilcox <willy@hp.com>"
2005-04-17 02:20:36 +04:00
# define DRIVER_DESC "ACPI Hot Plug PCI Controller Driver"
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_PARM_DESC ( debug , " Debugging mode enabled or not " ) ;
2013-04-12 09:44:25 +04:00
MODULE_PARM_DESC ( disable , " disable acpiphp driver " ) ;
2012-06-23 11:42:03 +04:00
module_param_named ( debug , acpiphp_debug , bool , 0644 ) ;
2013-04-12 09:44:25 +04:00
module_param_named ( disable , acpiphp_disabled , bool , 0444 ) ;
2005-04-17 02:20:36 +04:00
/* export the attention callback registration methods */
EXPORT_SYMBOL_GPL ( acpiphp_register_attention ) ;
EXPORT_SYMBOL_GPL ( acpiphp_unregister_attention ) ;
static int enable_slot ( struct hotplug_slot * slot ) ;
static int disable_slot ( struct hotplug_slot * slot ) ;
static int set_attention_status ( struct hotplug_slot * slot , u8 value ) ;
static int get_power_status ( struct hotplug_slot * slot , u8 * value ) ;
static int get_attention_status ( struct hotplug_slot * slot , u8 * value ) ;
static int get_latch_status ( struct hotplug_slot * slot , u8 * value ) ;
static int get_adapter_status ( struct hotplug_slot * slot , u8 * value ) ;
static struct hotplug_slot_ops acpi_hotplug_slot_ops = {
. enable_slot = enable_slot ,
. disable_slot = disable_slot ,
. set_attention_status = set_attention_status ,
. get_power_status = get_power_status ,
. get_attention_status = get_attention_status ,
. get_latch_status = get_latch_status ,
. get_adapter_status = get_adapter_status ,
} ;
/**
* acpiphp_register_attention - set attention LED callback
* @ info : must be completely filled with LED callbacks
*
2007-11-28 20:04:30 +03:00
* Description : This is used to register a hardware specific ACPI
2005-04-17 02:20:36 +04:00
* driver that manipulates the attention LED . All the fields in
* info must be set .
2007-11-28 20:04:30 +03:00
*/
2005-04-17 02:20:36 +04:00
int acpiphp_register_attention ( struct acpiphp_attention_info * info )
{
int retval = - EINVAL ;
if ( info & & info - > owner & & info - > set_attn & &
info - > get_attn & & ! attention_info ) {
retval = 0 ;
attention_info = info ;
}
return retval ;
}
/**
* acpiphp_unregister_attention - unset attention LED callback
* @ info : must match the pointer used to register
*
2007-11-28 20:04:30 +03:00
* Description : This is used to un - register a hardware specific acpi
2005-04-17 02:20:36 +04:00
* driver that manipulates the attention LED . The pointer to the
* info struct must be the same as the one used to set it .
2007-11-28 20:04:30 +03:00
*/
2005-04-17 02:20:36 +04:00
int acpiphp_unregister_attention ( struct acpiphp_attention_info * info )
{
int retval = - EINVAL ;
if ( info & & attention_info = = info ) {
attention_info = NULL ;
retval = 0 ;
}
return retval ;
}
/**
* enable_slot - power on and enable a slot
* @ hotplug_slot : slot to enable
*
* Actual tasks are done in acpiphp_enable_slot ( )
*/
static int enable_slot ( struct hotplug_slot * hotplug_slot )
{
struct slot * slot = hotplug_slot - > private ;
2008-10-21 03:41:12 +04:00
dbg ( " %s - physical_slot = %s \n " , __func__ , slot_name ( slot ) ) ;
2005-04-17 02:20:36 +04:00
/* enable the specified slot */
return acpiphp_enable_slot ( slot - > acpi_slot ) ;
}
/**
* disable_slot - disable and power off a slot
* @ hotplug_slot : slot to disable
*
* Actual tasks are done in acpiphp_disable_slot ( )
*/
static int disable_slot ( struct hotplug_slot * hotplug_slot )
{
struct slot * slot = hotplug_slot - > private ;
2007-07-05 22:10:46 +04:00
int retval ;
2005-04-17 02:20:36 +04:00
2008-10-21 03:41:12 +04:00
dbg ( " %s - physical_slot = %s \n " , __func__ , slot_name ( slot ) ) ;
2005-04-17 02:20:36 +04:00
/* disable the specified slot */
2007-07-05 22:10:46 +04:00
retval = acpiphp_disable_slot ( slot - > acpi_slot ) ;
if ( ! retval )
retval = acpiphp_eject_slot ( slot - > acpi_slot ) ;
return retval ;
2005-04-17 02:20:36 +04:00
}
2007-11-28 20:04:30 +03:00
/**
* set_attention_status - set attention LED
2005-04-17 02:20:36 +04:00
* @ hotplug_slot : slot to set attention LED on
* @ status : value to set attention LED to ( 0 or 1 )
*
* attention status LED , so we use a callback that
* was registered with us . This allows hardware specific
* ACPI implementations to blink the light for us .
2007-11-28 20:04:30 +03:00
*/
2005-04-17 02:20:36 +04:00
static int set_attention_status ( struct hotplug_slot * hotplug_slot , u8 status )
{
int retval = - ENODEV ;
2008-10-21 03:41:12 +04:00
dbg ( " %s - physical_slot = %s \n " , __func__ , hotplug_slot_name ( hotplug_slot ) ) ;
2005-04-17 02:20:36 +04:00
if ( attention_info & & try_module_get ( attention_info - > owner ) ) {
retval = attention_info - > set_attn ( hotplug_slot , status ) ;
module_put ( attention_info - > owner ) ;
} else
attention_info = NULL ;
return retval ;
}
/**
* get_power_status - get power status of a slot
* @ hotplug_slot : slot to get status
* @ value : pointer to store status
*
* Some platforms may not implement _STA method properly .
* In that case , the value returned may not be reliable .
*/
static int get_power_status ( struct hotplug_slot * hotplug_slot , u8 * value )
{
struct slot * slot = hotplug_slot - > private ;
2008-10-21 03:41:12 +04:00
dbg ( " %s - physical_slot = %s \n " , __func__ , slot_name ( slot ) ) ;
2005-04-17 02:20:36 +04:00
* value = acpiphp_get_power_status ( slot - > acpi_slot ) ;
return 0 ;
}
2007-11-28 20:04:30 +03:00
/**
2005-04-17 02:20:36 +04:00
* get_attention_status - get attention LED status
* @ hotplug_slot : slot to get status from
* @ value : returns with value of attention LED
*
* ACPI doesn ' t have known method to determine the state
* of the attention status LED , so we use a callback that
* was registered with us . This allows hardware specific
2007-11-28 20:04:30 +03:00
* ACPI implementations to determine its state .
*/
2005-04-17 02:20:36 +04:00
static int get_attention_status ( struct hotplug_slot * hotplug_slot , u8 * value )
{
int retval = - EINVAL ;
2008-10-21 03:41:12 +04:00
dbg ( " %s - physical_slot = %s \n " , __func__ , hotplug_slot_name ( hotplug_slot ) ) ;
2005-04-17 02:20:36 +04:00
if ( attention_info & & try_module_get ( attention_info - > owner ) ) {
retval = attention_info - > get_attn ( hotplug_slot , value ) ;
module_put ( attention_info - > owner ) ;
} else
attention_info = NULL ;
return retval ;
}
/**
* get_latch_status - get latch status of a slot
* @ hotplug_slot : slot to get status
* @ value : pointer to store status
*
* ACPI doesn ' t provide any formal means to access latch status .
2007-11-28 20:04:30 +03:00
* Instead , we fake latch status from _STA .
2005-04-17 02:20:36 +04:00
*/
static int get_latch_status ( struct hotplug_slot * hotplug_slot , u8 * value )
{
struct slot * slot = hotplug_slot - > private ;
2008-10-21 03:41:12 +04:00
dbg ( " %s - physical_slot = %s \n " , __func__ , slot_name ( slot ) ) ;
2005-04-17 02:20:36 +04:00
* value = acpiphp_get_latch_status ( slot - > acpi_slot ) ;
return 0 ;
}
/**
* get_adapter_status - get adapter status of a slot
* @ hotplug_slot : slot to get status
* @ value : pointer to store status
*
* ACPI doesn ' t provide any formal means to access adapter status .
2007-11-28 20:04:30 +03:00
* Instead , we fake adapter status from _STA .
2005-04-17 02:20:36 +04:00
*/
static int get_adapter_status ( struct hotplug_slot * hotplug_slot , u8 * value )
{
struct slot * slot = hotplug_slot - > private ;
2008-10-21 03:41:12 +04:00
dbg ( " %s - physical_slot = %s \n " , __func__ , slot_name ( slot ) ) ;
2005-04-17 02:20:36 +04:00
* value = acpiphp_get_adapter_status ( slot - > acpi_slot ) ;
return 0 ;
}
/**
* release_slot - free up the memory used by a slot
* @ hotplug_slot : slot to free
*/
static void release_slot ( struct hotplug_slot * hotplug_slot )
{
struct slot * slot = hotplug_slot - > private ;
2008-10-21 03:41:12 +04:00
dbg ( " %s - physical_slot = %s \n " , __func__ , slot_name ( slot ) ) ;
2005-04-17 02:20:36 +04:00
kfree ( slot - > hotplug_slot ) ;
kfree ( slot ) ;
}
2006-02-24 04:56:08 +03:00
/* callback routine to initialize 'struct slot' for each slot */
int acpiphp_register_hotplug_slot ( struct acpiphp_slot * acpiphp_slot )
2005-04-17 02:20:36 +04:00
{
struct slot * slot ;
int retval = - ENOMEM ;
2008-10-21 03:41:12 +04:00
char name [ SLOT_NAME_SIZE ] ;
2006-02-24 04:56:08 +03:00
slot = kzalloc ( sizeof ( * slot ) , GFP_KERNEL ) ;
if ( ! slot )
goto error ;
2006-11-14 02:12:45 +03:00
slot - > hotplug_slot = kzalloc ( sizeof ( * slot - > hotplug_slot ) , GFP_KERNEL ) ;
2006-02-24 04:56:08 +03:00
if ( ! slot - > hotplug_slot )
goto error_slot ;
2006-11-14 02:12:45 +03:00
slot - > hotplug_slot - > info = & slot - > info ;
2006-02-24 04:56:08 +03:00
slot - > hotplug_slot - > private = slot ;
slot - > hotplug_slot - > release = & release_slot ;
slot - > hotplug_slot - > ops = & acpi_hotplug_slot_ops ;
slot - > acpi_slot = acpiphp_slot ;
slot - > hotplug_slot - > info - > power_status = acpiphp_get_power_status ( slot - > acpi_slot ) ;
slot - > hotplug_slot - > info - > attention_status = 0 ;
slot - > hotplug_slot - > info - > latch_status = acpiphp_get_latch_status ( slot - > acpi_slot ) ;
slot - > hotplug_slot - > info - > adapter_status = acpiphp_get_adapter_status ( slot - > acpi_slot ) ;
acpiphp_slot - > slot = slot ;
2008-12-11 21:16:44 +03:00
snprintf ( name , SLOT_NAME_SIZE , " %llu " , slot - > acpi_slot - > sun ) ;
2006-02-24 04:56:08 +03:00
2008-06-11 01:28:50 +04:00
retval = pci_hp_register ( slot - > hotplug_slot ,
acpiphp_slot - > bridge - > pci_bus ,
2008-10-21 03:40:42 +04:00
acpiphp_slot - > device ,
2008-10-21 03:41:12 +04:00
name ) ;
2008-06-11 01:28:50 +04:00
if ( retval = = - EBUSY )
goto error_hpslot ;
2006-02-24 04:56:08 +03:00
if ( retval ) {
err ( " pci_hp_register failed with error %d \n " , retval ) ;
2006-11-14 02:12:45 +03:00
goto error_hpslot ;
2006-02-24 04:56:08 +03:00
}
2008-10-21 03:41:12 +04:00
info ( " Slot [%s] registered \n " , slot_name ( slot ) ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
error_hpslot :
kfree ( slot - > hotplug_slot ) ;
error_slot :
kfree ( slot ) ;
error :
return retval ;
}
2006-02-24 04:56:08 +03:00
void acpiphp_unregister_hotplug_slot ( struct acpiphp_slot * acpiphp_slot )
2005-04-17 02:20:36 +04:00
{
2006-02-24 04:56:08 +03:00
struct slot * slot = acpiphp_slot - > slot ;
int retval = 0 ;
2005-04-17 02:20:36 +04:00
2008-10-21 03:41:12 +04:00
info ( " Slot [%s] unregistered \n " , slot_name ( slot ) ) ;
2006-02-24 04:56:08 +03:00
retval = pci_hp_deregister ( slot - > hotplug_slot ) ;
if ( retval )
err ( " pci_hp_deregister failed with error %d \n " , retval ) ;
2005-04-17 02:20:36 +04:00
}
static int __init acpiphp_init ( void )
{
2013-04-12 09:44:25 +04:00
info ( DRIVER_DESC " version: " DRIVER_VERSION " %s \n " ,
acpiphp_disabled ? " , disabled by user; please report a bug "
: " " ) ;
2005-04-17 02:20:36 +04:00
2013-04-12 09:44:25 +04:00
if ( acpi_pci_disabled | | acpiphp_disabled )
2008-02-05 11:01:48 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
/* read all the ACPI info from the system */
2013-01-22 01:20:50 +04:00
/* initialize internal data structure etc. */
return acpiphp_glue_init ( ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( acpiphp_init ) ;