2005-04-17 02:20:36 +04:00
/*
2006-03-03 21:16:05 +03:00
* Common ACPI functions for hot plug platforms
2005-04-17 02:20:36 +04:00
*
2006-03-03 21:16:05 +03:00
* Copyright ( C ) 2006 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 .
*
2005-08-17 02:16:10 +04:00
* Send feedback to < kristen . c . accardi @ intel . com >
2005-04-17 02:20:36 +04:00
*
*/
# include <linux/module.h>
2006-05-10 17:20:34 +04:00
# include <linux/moduleparam.h>
2005-04-17 02:20:36 +04:00
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/pci.h>
2006-10-14 07:05:19 +04:00
# include <linux/pci_hotplug.h>
2008-05-28 10:01:03 +04:00
# include <linux/pci-acpi.h>
2006-03-03 21:16:05 +03:00
# include <acpi/acpi.h>
2005-04-17 02:20:36 +04:00
# include <acpi/acpi_bus.h>
# include <acpi/actypes.h>
2006-05-10 17:20:34 +04:00
# define MY_NAME "acpi_pcihp"
2008-03-04 06:09:46 +03:00
# define dbg(fmt, arg...) do { if (debug_acpi) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __func__ , ## arg); } while (0)
2006-05-10 17:20:34 +04:00
# define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
# define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
# define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg)
2005-04-17 02:20:36 +04:00
# define METHOD_NAME__SUN "_SUN"
# define METHOD_NAME__HPP "_HPP"
# define METHOD_NAME_OSHP "OSHP"
2006-05-10 17:20:34 +04:00
static int debug_acpi ;
2006-05-02 05:57:14 +04:00
static acpi_status
decode_type0_hpx_record ( union acpi_object * record , struct hotplug_params * hpx )
{
int i ;
union acpi_object * fields = record - > package . elements ;
u32 revision = fields [ 1 ] . integer . value ;
switch ( revision ) {
case 1 :
if ( record - > package . count ! = 6 )
return AE_ERROR ;
for ( i = 2 ; i < 6 ; i + + )
if ( fields [ i ] . type ! = ACPI_TYPE_INTEGER )
return AE_ERROR ;
hpx - > t0 = & hpx - > type0_data ;
hpx - > t0 - > revision = revision ;
hpx - > t0 - > cache_line_size = fields [ 2 ] . integer . value ;
hpx - > t0 - > latency_timer = fields [ 3 ] . integer . value ;
hpx - > t0 - > enable_serr = fields [ 4 ] . integer . value ;
hpx - > t0 - > enable_perr = fields [ 5 ] . integer . value ;
break ;
default :
printk ( KERN_WARNING
" %s: Type 0 Revision %d record not supported \n " ,
2008-03-04 06:09:46 +03:00
__func__ , revision ) ;
2006-05-02 05:57:14 +04:00
return AE_ERROR ;
}
return AE_OK ;
}
static acpi_status
decode_type1_hpx_record ( union acpi_object * record , struct hotplug_params * hpx )
{
int i ;
union acpi_object * fields = record - > package . elements ;
u32 revision = fields [ 1 ] . integer . value ;
switch ( revision ) {
case 1 :
if ( record - > package . count ! = 5 )
return AE_ERROR ;
for ( i = 2 ; i < 5 ; i + + )
if ( fields [ i ] . type ! = ACPI_TYPE_INTEGER )
return AE_ERROR ;
hpx - > t1 = & hpx - > type1_data ;
hpx - > t1 - > revision = revision ;
hpx - > t1 - > max_mem_read = fields [ 2 ] . integer . value ;
hpx - > t1 - > avg_max_split = fields [ 3 ] . integer . value ;
hpx - > t1 - > tot_max_split = fields [ 4 ] . integer . value ;
break ;
default :
printk ( KERN_WARNING
" %s: Type 1 Revision %d record not supported \n " ,
2008-03-04 06:09:46 +03:00
__func__ , revision ) ;
2006-05-02 05:57:14 +04:00
return AE_ERROR ;
}
return AE_OK ;
}
static acpi_status
decode_type2_hpx_record ( union acpi_object * record , struct hotplug_params * hpx )
{
int i ;
union acpi_object * fields = record - > package . elements ;
u32 revision = fields [ 1 ] . integer . value ;
switch ( revision ) {
case 1 :
if ( record - > package . count ! = 18 )
return AE_ERROR ;
for ( i = 2 ; i < 18 ; i + + )
if ( fields [ i ] . type ! = ACPI_TYPE_INTEGER )
return AE_ERROR ;
hpx - > t2 = & hpx - > type2_data ;
hpx - > t2 - > revision = revision ;
hpx - > t2 - > unc_err_mask_and = fields [ 2 ] . integer . value ;
hpx - > t2 - > unc_err_mask_or = fields [ 3 ] . integer . value ;
hpx - > t2 - > unc_err_sever_and = fields [ 4 ] . integer . value ;
hpx - > t2 - > unc_err_sever_or = fields [ 5 ] . integer . value ;
hpx - > t2 - > cor_err_mask_and = fields [ 6 ] . integer . value ;
hpx - > t2 - > cor_err_mask_or = fields [ 7 ] . integer . value ;
hpx - > t2 - > adv_err_cap_and = fields [ 8 ] . integer . value ;
hpx - > t2 - > adv_err_cap_or = fields [ 9 ] . integer . value ;
hpx - > t2 - > pci_exp_devctl_and = fields [ 10 ] . integer . value ;
hpx - > t2 - > pci_exp_devctl_or = fields [ 11 ] . integer . value ;
hpx - > t2 - > pci_exp_lnkctl_and = fields [ 12 ] . integer . value ;
hpx - > t2 - > pci_exp_lnkctl_or = fields [ 13 ] . integer . value ;
hpx - > t2 - > sec_unc_err_sever_and = fields [ 14 ] . integer . value ;
hpx - > t2 - > sec_unc_err_sever_or = fields [ 15 ] . integer . value ;
hpx - > t2 - > sec_unc_err_mask_and = fields [ 16 ] . integer . value ;
hpx - > t2 - > sec_unc_err_mask_or = fields [ 17 ] . integer . value ;
break ;
default :
printk ( KERN_WARNING
" %s: Type 2 Revision %d record not supported \n " ,
2008-03-04 06:09:46 +03:00
__func__ , revision ) ;
2006-05-02 05:57:14 +04:00
return AE_ERROR ;
}
return AE_OK ;
}
static acpi_status
acpi_run_hpx ( acpi_handle handle , struct hotplug_params * hpx )
{
acpi_status status ;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object * package , * record , * fields ;
u32 type ;
int i ;
/* Clear the return buffer with zeros */
memset ( hpx , 0 , sizeof ( struct hotplug_params ) ) ;
status = acpi_evaluate_object ( handle , " _HPX " , NULL , & buffer ) ;
if ( ACPI_FAILURE ( status ) )
return status ;
package = ( union acpi_object * ) buffer . pointer ;
if ( package - > type ! = ACPI_TYPE_PACKAGE ) {
status = AE_ERROR ;
goto exit ;
}
for ( i = 0 ; i < package - > package . count ; i + + ) {
record = & package - > package . elements [ i ] ;
if ( record - > type ! = ACPI_TYPE_PACKAGE ) {
status = AE_ERROR ;
goto exit ;
}
fields = record - > package . elements ;
if ( fields [ 0 ] . type ! = ACPI_TYPE_INTEGER | |
fields [ 1 ] . type ! = ACPI_TYPE_INTEGER ) {
status = AE_ERROR ;
goto exit ;
}
type = fields [ 0 ] . integer . value ;
switch ( type ) {
case 0 :
status = decode_type0_hpx_record ( record , hpx ) ;
if ( ACPI_FAILURE ( status ) )
goto exit ;
break ;
case 1 :
status = decode_type1_hpx_record ( record , hpx ) ;
if ( ACPI_FAILURE ( status ) )
goto exit ;
break ;
case 2 :
status = decode_type2_hpx_record ( record , hpx ) ;
if ( ACPI_FAILURE ( status ) )
goto exit ;
break ;
default :
printk ( KERN_ERR " %s: Type %d record not supported \n " ,
2008-03-04 06:09:46 +03:00
__func__ , type ) ;
2006-05-02 05:57:14 +04:00
status = AE_ERROR ;
goto exit ;
}
}
exit :
kfree ( buffer . pointer ) ;
return status ;
}
2005-04-17 02:20:36 +04:00
2005-11-01 03:20:07 +03:00
static acpi_status
acpi_run_hpp ( acpi_handle handle , struct hotplug_params * hpp )
2005-04-17 02:20:36 +04:00
{
acpi_status status ;
u8 nui [ 4 ] ;
struct acpi_buffer ret_buf = { 0 , NULL } ;
2006-03-17 03:18:39 +03:00
struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER , NULL } ;
2005-04-17 02:20:36 +04:00
union acpi_object * ext_obj , * package ;
int i , len = 0 ;
2006-03-17 03:18:39 +03:00
acpi_get_name ( handle , ACPI_FULL_PATHNAME , & string ) ;
2006-05-02 05:57:14 +04:00
/* Clear the return buffer with zeros */
memset ( hpp , 0 , sizeof ( struct hotplug_params ) ) ;
2005-04-17 02:20:36 +04:00
/* get _hpp */
2005-11-01 03:20:07 +03:00
status = acpi_evaluate_object ( handle , METHOD_NAME__HPP , NULL , & ret_buf ) ;
2005-04-17 02:20:36 +04:00
switch ( status ) {
case AE_BUFFER_OVERFLOW :
ret_buf . pointer = kmalloc ( ret_buf . length , GFP_KERNEL ) ;
if ( ! ret_buf . pointer ) {
2006-03-03 21:16:05 +03:00
printk ( KERN_ERR " %s:%s alloc for _HPP fail \n " ,
2008-03-04 06:09:46 +03:00
__func__ , ( char * ) string . pointer ) ;
2006-04-19 01:36:43 +04:00
kfree ( string . pointer ) ;
2005-11-01 03:20:07 +03:00
return AE_NO_MEMORY ;
2005-04-17 02:20:36 +04:00
}
2005-11-01 03:20:07 +03:00
status = acpi_evaluate_object ( handle , METHOD_NAME__HPP ,
NULL , & ret_buf ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_SUCCESS ( status ) )
break ;
default :
if ( ACPI_FAILURE ( status ) ) {
2008-03-04 06:09:46 +03:00
pr_debug ( " %s:%s _HPP fail=0x%x \n " , __func__ ,
2006-03-17 03:18:39 +03:00
( char * ) string . pointer , status ) ;
2006-04-19 01:36:43 +04:00
kfree ( string . pointer ) ;
2005-11-01 03:20:07 +03:00
return status ;
2005-04-17 02:20:36 +04:00
}
}
ext_obj = ( union acpi_object * ) ret_buf . pointer ;
if ( ext_obj - > type ! = ACPI_TYPE_PACKAGE ) {
2008-03-04 06:09:46 +03:00
printk ( KERN_ERR " %s:%s _HPP obj not a package \n " , __func__ ,
2006-03-17 03:18:39 +03:00
( char * ) string . pointer ) ;
2005-11-01 03:20:07 +03:00
status = AE_ERROR ;
2005-04-17 02:20:36 +04:00
goto free_and_return ;
}
len = ext_obj - > package . count ;
package = ( union acpi_object * ) ret_buf . pointer ;
for ( i = 0 ; ( i < len ) | | ( i < 4 ) ; i + + ) {
ext_obj = ( union acpi_object * ) & package - > package . elements [ i ] ;
switch ( ext_obj - > type ) {
case ACPI_TYPE_INTEGER :
nui [ i ] = ( u8 ) ext_obj - > integer . value ;
break ;
default :
2006-03-03 21:16:05 +03:00
printk ( KERN_ERR " %s:%s _HPP obj type incorrect \n " ,
2008-03-04 06:09:46 +03:00
__func__ , ( char * ) string . pointer ) ;
2005-11-01 03:20:07 +03:00
status = AE_ERROR ;
2005-04-17 02:20:36 +04:00
goto free_and_return ;
}
}
2006-05-02 05:57:14 +04:00
hpp - > t0 = & hpp - > type0_data ;
hpp - > t0 - > cache_line_size = nui [ 0 ] ;
hpp - > t0 - > latency_timer = nui [ 1 ] ;
hpp - > t0 - > enable_serr = nui [ 2 ] ;
hpp - > t0 - > enable_perr = nui [ 3 ] ;
2005-04-17 02:20:36 +04:00
2006-05-02 05:57:14 +04:00
pr_debug ( " _HPP: cache_line_size=0x%x \n " , hpp - > t0 - > cache_line_size ) ;
pr_debug ( " _HPP: latency timer =0x%x \n " , hpp - > t0 - > latency_timer ) ;
pr_debug ( " _HPP: enable SERR =0x%x \n " , hpp - > t0 - > enable_serr ) ;
pr_debug ( " _HPP: enable PERR =0x%x \n " , hpp - > t0 - > enable_perr ) ;
2005-04-17 02:20:36 +04:00
free_and_return :
2006-04-19 01:36:43 +04:00
kfree ( string . pointer ) ;
kfree ( ret_buf . pointer ) ;
2005-11-01 03:20:07 +03:00
return status ;
2005-04-17 02:20:36 +04:00
}
2006-03-03 21:16:05 +03:00
/* acpi_run_oshp - get control of hotplug from the firmware
*
* @ handle - the handle of the hotplug controller .
*/
2008-05-28 10:01:03 +04:00
static acpi_status acpi_run_oshp ( acpi_handle handle )
2005-04-17 02:20:36 +04:00
{
acpi_status status ;
2006-03-17 03:18:39 +03:00
struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER , NULL } ;
acpi_get_name ( handle , ACPI_FULL_PATHNAME , & string ) ;
2005-04-17 02:20:36 +04:00
/* run OSHP */
2005-11-01 03:20:07 +03:00
status = acpi_evaluate_object ( handle , METHOD_NAME_OSHP , NULL , NULL ) ;
2006-03-03 21:16:05 +03:00
if ( ACPI_FAILURE ( status ) )
2006-05-10 17:20:34 +04:00
if ( status ! = AE_NOT_FOUND )
printk ( KERN_ERR " %s:%s OSHP fails=0x%x \n " ,
2008-03-04 06:09:46 +03:00
__func__ , ( char * ) string . pointer , status ) ;
2006-05-10 17:20:34 +04:00
else
dbg ( " %s:%s OSHP not found \n " ,
2008-03-04 06:09:46 +03:00
__func__ , ( char * ) string . pointer ) ;
2006-03-03 21:16:05 +03:00
else
2008-03-04 06:09:46 +03:00
pr_debug ( " %s:%s OSHP passes \n " , __func__ ,
2006-03-17 03:18:39 +03:00
( char * ) string . pointer ) ;
2006-04-19 01:36:43 +04:00
kfree ( string . pointer ) ;
2006-03-03 21:16:05 +03:00
return status ;
}
/* acpi_get_hp_params_from_firmware
*
2006-05-02 05:54:50 +04:00
* @ bus - the pci_bus of the bus on which the device is newly added
2006-03-03 21:16:05 +03:00
* @ hpp - allocated by the caller
*/
2006-05-02 05:54:50 +04:00
acpi_status acpi_get_hp_params_from_firmware ( struct pci_bus * bus ,
2006-03-03 21:16:05 +03:00
struct hotplug_params * hpp )
{
acpi_status status = AE_NOT_FOUND ;
2006-05-02 05:54:50 +04:00
acpi_handle handle , phandle ;
struct pci_bus * pbus = bus ;
struct pci_dev * pdev ;
do {
pdev = pbus - > self ;
if ( ! pdev ) {
handle = acpi_get_pci_rootbridge_handle (
pci_domain_nr ( pbus ) , pbus - > number ) ;
break ;
}
handle = DEVICE_ACPI_HANDLE ( & ( pdev - > dev ) ) ;
pbus = pbus - > parent ;
} while ( ! handle ) ;
2006-03-03 21:16:05 +03:00
/*
* _HPP settings apply to all child buses , until another _HPP is
* encountered . If we don ' t find an _HPP for the input pci dev ,
* look for it in the parent device scope since that would apply to
* this pci dev . If we don ' t find any _HPP , use hardcoded defaults
*/
2006-05-02 05:54:50 +04:00
while ( handle ) {
2006-05-02 05:57:14 +04:00
status = acpi_run_hpx ( handle , hpp ) ;
if ( ACPI_SUCCESS ( status ) )
break ;
2006-03-03 21:16:05 +03:00
status = acpi_run_hpp ( handle , hpp ) ;
2006-05-02 05:54:50 +04:00
if ( ACPI_SUCCESS ( status ) )
break ;
if ( acpi_root_bridge ( handle ) )
break ;
status = acpi_get_parent ( handle , & phandle ) ;
if ( ACPI_FAILURE ( status ) )
2006-03-03 21:16:05 +03:00
break ;
2006-05-02 05:54:50 +04:00
handle = phandle ;
2005-04-17 02:20:36 +04:00
}
2005-11-01 03:20:07 +03:00
return status ;
2005-04-17 02:20:36 +04:00
}
2006-03-03 21:16:05 +03:00
EXPORT_SYMBOL_GPL ( acpi_get_hp_params_from_firmware ) ;
2008-05-28 10:01:03 +04:00
/**
* acpi_get_hp_hw_control_from_firmware
* @ dev : the pci_dev of the bridge that has a hotplug controller
* @ flags : requested control bits for _OSC
*
* Attempt to take hotplug control from firmware .
*/
int acpi_get_hp_hw_control_from_firmware ( struct pci_dev * dev , u32 flags )
{
acpi_status status ;
2008-08-11 19:47:40 +04:00
acpi_handle chandle , handle ;
2008-05-28 10:01:03 +04:00
struct pci_dev * pdev = dev ;
struct pci_bus * parent ;
struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER , NULL } ;
flags & = ( OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
OSC_SHPC_NATIVE_HP_CONTROL |
OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL ) ;
if ( ! flags ) {
err ( " Invalid flags %u specified! \n " , flags ) ;
return - EINVAL ;
}
/*
* Per PCI firmware specification , we should run the ACPI _OSC
* method to get control of hotplug hardware before using it . If
* an _OSC is missing , we look for an OSHP to do the same thing .
2008-08-11 19:47:40 +04:00
* To handle different BIOS behavior , we look for _OSC on a root
* bridge preferentially ( according to PCI fw spec ) . Later for
* OSHP within the scope of the hotplug controller and its parents ,
2008-05-28 10:01:03 +04:00
* upto the host bridge under which this controller exists .
*/
2008-08-18 22:22:54 +04:00
handle = acpi_find_root_bridge_handle ( pdev ) ;
2008-08-11 19:47:40 +04:00
if ( handle ) {
acpi_get_name ( handle , ACPI_FULL_PATHNAME , & string ) ;
dbg ( " Trying to get hotplug control for %s \n " ,
( char * ) string . pointer ) ;
status = pci_osc_control_set ( handle , flags ) ;
if ( ACPI_SUCCESS ( status ) )
goto got_one ;
kfree ( string . pointer ) ;
string = ( struct acpi_buffer ) { ACPI_ALLOCATE_BUFFER , NULL } ;
}
pdev = dev ;
handle = DEVICE_ACPI_HANDLE ( & dev - > dev ) ;
2008-05-28 10:01:03 +04:00
while ( ! handle ) {
/*
* This hotplug controller was not listed in the ACPI name
* space at all . Try to get acpi handle of parent pci bus .
*/
if ( ! pdev | | ! pdev - > bus - > parent )
break ;
parent = pdev - > bus - > parent ;
dbg ( " Could not find %s in acpi namespace, trying parent \n " ,
pci_name ( pdev ) ) ;
if ( ! parent - > self )
/* Parent must be a host bridge */
handle = acpi_get_pci_rootbridge_handle (
pci_domain_nr ( parent ) ,
parent - > number ) ;
else
handle = DEVICE_ACPI_HANDLE ( & ( parent - > self - > dev ) ) ;
pdev = parent - > self ;
}
while ( handle ) {
acpi_get_name ( handle , ACPI_FULL_PATHNAME , & string ) ;
dbg ( " Trying to get hotplug control for %s \n " ,
( char * ) string . pointer ) ;
2008-08-11 19:47:40 +04:00
status = acpi_run_oshp ( handle ) ;
if ( ACPI_SUCCESS ( status ) )
goto got_one ;
2008-05-28 10:01:03 +04:00
if ( acpi_root_bridge ( handle ) )
break ;
chandle = handle ;
status = acpi_get_parent ( chandle , & handle ) ;
if ( ACPI_FAILURE ( status ) )
break ;
}
dbg ( " Cannot get control of hotplug hardware for pci %s \n " ,
pci_name ( dev ) ) ;
kfree ( string . pointer ) ;
return - ENODEV ;
2008-08-11 19:47:40 +04:00
got_one :
dbg ( " Gained control for hotplug HW for pci %s (%s) \n " , pci_name ( dev ) ,
( char * ) string . pointer ) ;
kfree ( string . pointer ) ;
return 0 ;
2008-05-28 10:01:03 +04:00
}
EXPORT_SYMBOL ( acpi_get_hp_hw_control_from_firmware ) ;
2005-04-17 02:20:36 +04:00
2006-03-03 21:16:05 +03:00
/* acpi_root_bridge - check to see if this acpi object is a root bridge
*
* @ handle - the acpi object in question .
*/
int acpi_root_bridge ( acpi_handle handle )
2005-11-01 03:20:12 +03:00
{
acpi_status status ;
struct acpi_device_info * info ;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER , NULL } ;
int i ;
status = acpi_get_object_info ( handle , & buffer ) ;
if ( ACPI_SUCCESS ( status ) ) {
info = buffer . pointer ;
if ( ( info - > valid & ACPI_VALID_HID ) & &
! strcmp ( PCI_ROOT_HID_STRING ,
info - > hardware_id . value ) ) {
2006-04-19 01:36:43 +04:00
kfree ( buffer . pointer ) ;
2005-11-01 03:20:12 +03:00
return 1 ;
}
if ( info - > valid & ACPI_VALID_CID ) {
for ( i = 0 ; i < info - > compatibility_id . count ; i + + ) {
if ( ! strcmp ( PCI_ROOT_HID_STRING ,
info - > compatibility_id . id [ i ] . value ) ) {
2006-04-19 01:36:43 +04:00
kfree ( buffer . pointer ) ;
2005-11-01 03:20:12 +03:00
return 1 ;
}
}
}
2006-04-19 01:36:43 +04:00
kfree ( buffer . pointer ) ;
2005-11-01 03:20:12 +03:00
}
return 0 ;
}
2006-03-03 21:16:05 +03:00
EXPORT_SYMBOL_GPL ( acpi_root_bridge ) ;
2006-05-10 17:20:34 +04:00
module_param ( debug_acpi , bool , 0644 ) ;
MODULE_PARM_DESC ( debug_acpi , " Debugging mode for ACPI enabled or not " ) ;