2005-04-17 02:20:36 +04:00
/*
* PCIEHPRM ACPI : PHP Resource Manager for ACPI platform
*
* Copyright ( C ) 2003 - 2004 Intel Corporation
*
* 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>
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/pci.h>
# include <linux/acpi.h>
# include <linux/pci-acpi.h>
# include <acpi/acpi_bus.h>
# include <acpi/actypes.h>
# include "pciehp.h"
# define METHOD_NAME__SUN "_SUN"
# define METHOD_NAME__HPP "_HPP"
# define METHOD_NAME_OSHP "OSHP"
static u8 * acpi_path_name ( acpi_handle handle )
{
acpi_status status ;
static u8 path_name [ ACPI_PATHNAME_MAX ] ;
struct acpi_buffer ret_buf = { ACPI_PATHNAME_MAX , path_name } ;
memset ( path_name , 0 , sizeof ( path_name ) ) ;
status = acpi_get_name ( handle , ACPI_FULL_PATHNAME , & ret_buf ) ;
if ( ACPI_FAILURE ( status ) )
return NULL ;
else
return path_name ;
}
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 } ;
union acpi_object * ext_obj , * package ;
2005-11-01 03:20:07 +03:00
u8 * path_name = acpi_path_name ( handle ) ;
2005-04-17 02:20:36 +04:00
int i , len = 0 ;
/* 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 ) {
2005-11-01 03:20:07 +03:00
err ( " %s:%s alloc for _HPP fail \n " , __FUNCTION__ ,
path_name ) ;
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 ) ) {
2005-11-01 03:20:07 +03:00
dbg ( " %s:%s _HPP fail=0x%x \n " , __FUNCTION__ ,
path_name , status ) ;
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 ) {
2005-11-01 03:20:07 +03:00
err ( " %s:%s _HPP obj not a package \n " , __FUNCTION__ ,
path_name ) ;
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 :
2005-11-01 03:20:07 +03:00
err ( " %s:%s _HPP obj type incorrect \n " , __FUNCTION__ ,
path_name ) ;
status = AE_ERROR ;
2005-04-17 02:20:36 +04:00
goto free_and_return ;
}
}
2005-11-01 03:20:07 +03:00
hpp - > cache_line_size = nui [ 0 ] ;
hpp - > latency_timer = nui [ 1 ] ;
hpp - > enable_serr = nui [ 2 ] ;
hpp - > enable_perr = nui [ 3 ] ;
2005-04-17 02:20:36 +04:00
2005-11-01 03:20:07 +03:00
dbg ( " _HPP: cache_line_size=0x%x \n " , hpp - > cache_line_size ) ;
dbg ( " _HPP: latency timer =0x%x \n " , hpp - > latency_timer ) ;
dbg ( " _HPP: enable SERR =0x%x \n " , hpp - > enable_serr ) ;
dbg ( " _HPP: enable PERR =0x%x \n " , hpp - > enable_perr ) ;
2005-04-17 02:20:36 +04:00
free_and_return :
kfree ( ret_buf . pointer ) ;
2005-11-01 03:20:07 +03:00
return status ;
2005-04-17 02:20:36 +04:00
}
2005-11-01 03:20:07 +03:00
static acpi_status acpi_run_oshp ( acpi_handle handle )
2005-04-17 02:20:36 +04:00
{
acpi_status status ;
2005-11-01 03:20:07 +03:00
u8 * path_name = acpi_path_name ( handle ) ;
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 ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) ) {
2005-11-01 03:20:07 +03:00
err ( " %s:%s OSHP fails=0x%x \n " , __FUNCTION__ , path_name ,
status ) ;
2005-04-17 02:20:36 +04:00
} else {
2005-11-01 03:20:07 +03:00
dbg ( " %s:%s OSHP passes \n " , __FUNCTION__ , path_name ) ;
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
}
2005-11-01 03:20:07 +03:00
int get_hp_hw_control_from_firmware ( struct pci_dev * dev )
2005-04-17 02:20:36 +04:00
{
2005-11-01 03:20:07 +03:00
acpi_status status ;
2005-11-01 03:20:11 +03:00
acpi_handle handle = DEVICE_ACPI_HANDLE ( & ( dev - > dev ) ) ;
2005-11-01 03:20:07 +03:00
/*
* Per PCI firmware specification , we should run the ACPI _OSC
* method to get control of hotplug hardware before using it
*/
2005-11-01 03:20:11 +03:00
status = pci_osc_control_set ( handle ,
OSC_PCI_EXPRESS_NATIVE_HP_CONTROL ) ;
2005-11-01 03:20:07 +03:00
/* Fixme: fail native hotplug if _OSC does not exist for root ports */
if ( status = = AE_NOT_FOUND ) {
/*
* Some older BIOS ' s don ' t support _OSC but support
* OSHP to do the same thing
*/
2005-11-01 03:20:11 +03:00
status = acpi_run_oshp ( handle ) ;
2005-04-17 02:20:36 +04:00
}
if ( ACPI_FAILURE ( status ) ) {
2005-11-01 03:20:07 +03:00
err ( " Cannot get control of hotplug hardware \n " ) ;
2005-04-17 02:20:36 +04:00
return - 1 ;
}
2005-11-01 03:20:07 +03:00
dbg ( " Sucess getting control of hotplug hardware \n " ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-11-01 03:20:07 +03:00
void get_hp_params_from_firmware ( struct pci_dev * dev ,
struct hotplug_params * hpp )
2005-04-17 02:20:36 +04:00
{
2005-11-01 03:20:07 +03:00
acpi_status status = AE_NOT_FOUND ;
struct pci_dev * pdev = dev ;
2005-04-17 02:20:36 +04:00
/*
2005-11-01 03:20:07 +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
2005-04-17 02:20:36 +04:00
*/
2005-11-01 03:20:07 +03:00
while ( pdev & & ( ACPI_FAILURE ( status ) ) ) {
acpi_handle handle = DEVICE_ACPI_HANDLE ( & ( pdev - > dev ) ) ;
if ( ! handle )
break ;
status = acpi_run_hpp ( handle , hpp ) ;
if ( ! ( pdev - > bus - > parent ) )
break ;
/* Check if a parent object supports _HPP */
pdev = pdev - > bus - > parent - > self ;
2005-04-17 02:20:36 +04:00
}
}