2005-04-17 02:20:36 +04:00
/*
* File : pci - acpi . c
2005-03-24 00:16:03 +03:00
* Purpose : Provide PCI support in ACPI
2005-04-17 02:20:36 +04:00
*
2005-03-19 02:53:36 +03:00
* Copyright ( C ) 2005 David Shaohua Li < shaohua . li @ intel . com >
* Copyright ( C ) 2004 Tom Long Nguyen < tom . l . nguyen @ intel . com >
* Copyright ( C ) 2004 Intel Corp .
2005-04-17 02:20:36 +04:00
*/
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/pci.h>
# include <linux/module.h>
# include <acpi/acpi.h>
# include <acpi/acnamesp.h>
# include <acpi/acresrc.h>
# include <acpi/acpi_bus.h>
# include <linux/pci-acpi.h>
2005-03-19 08:15:48 +03:00
# include "pci.h"
2005-04-17 02:20:36 +04:00
static u32 ctrlset_buf [ 3 ] = { 0 , 0 , 0 } ;
static u32 global_ctrlsets = 0 ;
2005-04-08 09:53:31 +04:00
static u8 OSC_UUID [ 16 ] = { 0x5B , 0x4D , 0xDB , 0x33 , 0xF7 , 0x1F , 0x1C , 0x40 , 0x96 , 0x57 , 0x74 , 0x41 , 0xC0 , 0x3D , 0xD7 , 0x66 } ;
2005-04-17 02:20:36 +04:00
static acpi_status
acpi_query_osc (
acpi_handle handle ,
u32 level ,
void * context ,
void * * retval )
{
acpi_status status ;
struct acpi_object_list input ;
union acpi_object in_params [ 4 ] ;
2006-05-21 02:00:08 +04:00
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object * out_obj ;
2005-04-17 02:20:36 +04:00
u32 osc_dw0 ;
2006-10-31 00:08:12 +03:00
acpi_status * ret_status = ( acpi_status * ) retval ;
2005-04-17 02:20:36 +04:00
/* Setting up input parameters */
input . count = 4 ;
input . pointer = in_params ;
in_params [ 0 ] . type = ACPI_TYPE_BUFFER ;
in_params [ 0 ] . buffer . length = 16 ;
in_params [ 0 ] . buffer . pointer = OSC_UUID ;
in_params [ 1 ] . type = ACPI_TYPE_INTEGER ;
in_params [ 1 ] . integer . value = 1 ;
in_params [ 2 ] . type = ACPI_TYPE_INTEGER ;
in_params [ 2 ] . integer . value = 3 ;
in_params [ 3 ] . type = ACPI_TYPE_BUFFER ;
in_params [ 3 ] . buffer . length = 12 ;
in_params [ 3 ] . buffer . pointer = ( u8 * ) context ;
status = acpi_evaluate_object ( handle , " _OSC " , & input , & output ) ;
if ( ACPI_FAILURE ( status ) ) {
printk ( KERN_DEBUG
" Evaluate _OSC Set fails. Status = 0x%04x \n " , status ) ;
2006-10-31 00:08:12 +03:00
* ret_status = status ;
2005-04-17 02:20:36 +04:00
return status ;
}
2006-05-21 02:00:08 +04:00
out_obj = output . pointer ;
if ( out_obj - > type ! = ACPI_TYPE_BUFFER ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_DEBUG
" Evaluate _OSC returns wrong type \n " ) ;
2006-05-21 02:00:08 +04:00
status = AE_TYPE ;
goto query_osc_out ;
2005-04-17 02:20:36 +04:00
}
2006-05-21 02:00:08 +04:00
osc_dw0 = * ( ( u32 * ) out_obj - > buffer . pointer ) ;
2005-04-17 02:20:36 +04:00
if ( osc_dw0 ) {
if ( osc_dw0 & OSC_REQUEST_ERROR )
printk ( KERN_DEBUG " _OSC request fails \n " ) ;
if ( osc_dw0 & OSC_INVALID_UUID_ERROR )
printk ( KERN_DEBUG " _OSC invalid UUID \n " ) ;
if ( osc_dw0 & OSC_INVALID_REVISION_ERROR )
printk ( KERN_DEBUG " _OSC invalid revision \n " ) ;
if ( osc_dw0 & OSC_CAPABILITIES_MASK_ERROR ) {
/* Update Global Control Set */
2006-05-21 02:00:08 +04:00
global_ctrlsets = * ( ( u32 * ) ( out_obj - > buffer . pointer + 8 ) ) ;
status = AE_OK ;
goto query_osc_out ;
2005-04-17 02:20:36 +04:00
}
2006-05-21 02:00:08 +04:00
status = AE_ERROR ;
goto query_osc_out ;
2005-04-17 02:20:36 +04:00
}
/* Update Global Control Set */
2006-05-21 02:00:08 +04:00
global_ctrlsets = * ( ( u32 * ) ( out_obj - > buffer . pointer + 8 ) ) ;
status = AE_OK ;
query_osc_out :
kfree ( output . pointer ) ;
2006-10-31 00:08:12 +03:00
* ret_status = status ;
2006-05-21 02:00:08 +04:00
return status ;
2005-04-17 02:20:36 +04:00
}
static acpi_status
acpi_run_osc (
acpi_handle handle ,
2005-11-01 03:20:11 +03:00
void * context )
2005-04-17 02:20:36 +04:00
{
acpi_status status ;
struct acpi_object_list input ;
union acpi_object in_params [ 4 ] ;
2006-05-21 02:00:08 +04:00
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object * out_obj ;
2005-04-17 02:20:36 +04:00
u32 osc_dw0 ;
/* Setting up input parameters */
input . count = 4 ;
input . pointer = in_params ;
in_params [ 0 ] . type = ACPI_TYPE_BUFFER ;
in_params [ 0 ] . buffer . length = 16 ;
in_params [ 0 ] . buffer . pointer = OSC_UUID ;
in_params [ 1 ] . type = ACPI_TYPE_INTEGER ;
in_params [ 1 ] . integer . value = 1 ;
in_params [ 2 ] . type = ACPI_TYPE_INTEGER ;
in_params [ 2 ] . integer . value = 3 ;
in_params [ 3 ] . type = ACPI_TYPE_BUFFER ;
in_params [ 3 ] . buffer . length = 12 ;
in_params [ 3 ] . buffer . pointer = ( u8 * ) context ;
status = acpi_evaluate_object ( handle , " _OSC " , & input , & output ) ;
if ( ACPI_FAILURE ( status ) ) {
printk ( KERN_DEBUG
" Evaluate _OSC Set fails. Status = 0x%04x \n " , status ) ;
return status ;
}
2006-05-21 02:00:08 +04:00
out_obj = output . pointer ;
if ( out_obj - > type ! = ACPI_TYPE_BUFFER ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_DEBUG
" Evaluate _OSC returns wrong type \n " ) ;
2006-05-21 02:00:08 +04:00
status = AE_TYPE ;
goto run_osc_out ;
2005-04-17 02:20:36 +04:00
}
2006-05-21 02:00:08 +04:00
osc_dw0 = * ( ( u32 * ) out_obj - > buffer . pointer ) ;
2005-04-17 02:20:36 +04:00
if ( osc_dw0 ) {
if ( osc_dw0 & OSC_REQUEST_ERROR )
printk ( KERN_DEBUG " _OSC request fails \n " ) ;
if ( osc_dw0 & OSC_INVALID_UUID_ERROR )
printk ( KERN_DEBUG " _OSC invalid UUID \n " ) ;
if ( osc_dw0 & OSC_INVALID_REVISION_ERROR )
printk ( KERN_DEBUG " _OSC invalid revision \n " ) ;
if ( osc_dw0 & OSC_CAPABILITIES_MASK_ERROR ) {
printk ( KERN_DEBUG " _OSC FW not grant req. control \n " ) ;
2006-05-21 02:00:08 +04:00
status = AE_SUPPORT ;
goto run_osc_out ;
2005-04-17 02:20:36 +04:00
}
2006-05-21 02:00:08 +04:00
status = AE_ERROR ;
goto run_osc_out ;
2005-04-17 02:20:36 +04:00
}
2006-05-21 02:00:08 +04:00
status = AE_OK ;
run_osc_out :
kfree ( output . pointer ) ;
return status ;
2005-04-17 02:20:36 +04:00
}
/**
* pci_osc_support_set - register OS support to Firmware
* @ flags : OS support bits
*
* Update OS support fields and doing a _OSC Query to obtain an update
* from Firmware on supported control bits .
* */
acpi_status pci_osc_support_set ( u32 flags )
{
u32 temp ;
2006-10-31 00:08:12 +03:00
acpi_status retval ;
2005-04-17 02:20:36 +04:00
if ( ! ( flags & OSC_SUPPORT_MASKS ) ) {
return AE_TYPE ;
}
ctrlset_buf [ OSC_SUPPORT_TYPE ] | = ( flags & OSC_SUPPORT_MASKS ) ;
/* do _OSC query for all possible controls */
temp = ctrlset_buf [ OSC_CONTROL_TYPE ] ;
ctrlset_buf [ OSC_QUERY_TYPE ] = OSC_QUERY_ENABLE ;
ctrlset_buf [ OSC_CONTROL_TYPE ] = OSC_CONTROL_MASKS ;
acpi_get_devices ( PCI_ROOT_HID_STRING ,
acpi_query_osc ,
ctrlset_buf ,
2006-10-31 00:08:12 +03:00
( void * * ) & retval ) ;
2005-04-17 02:20:36 +04:00
ctrlset_buf [ OSC_QUERY_TYPE ] = ! OSC_QUERY_ENABLE ;
ctrlset_buf [ OSC_CONTROL_TYPE ] = temp ;
2006-10-31 00:08:12 +03:00
if ( ACPI_FAILURE ( retval ) ) {
/* no osc support at all */
ctrlset_buf [ OSC_SUPPORT_TYPE ] = 0 ;
}
2005-04-17 02:20:36 +04:00
return AE_OK ;
}
EXPORT_SYMBOL ( pci_osc_support_set ) ;
/**
* pci_osc_control_set - commit requested control to Firmware
2005-11-24 02:45:04 +03:00
* @ handle : acpi_handle for the target ACPI object
2005-04-17 02:20:36 +04:00
* @ flags : driver ' s requested control bits
*
* Attempt to take control from Firmware on requested control bits .
* */
2005-11-01 03:20:11 +03:00
acpi_status pci_osc_control_set ( acpi_handle handle , u32 flags )
2005-04-17 02:20:36 +04:00
{
acpi_status status ;
u32 ctrlset ;
ctrlset = ( flags & OSC_CONTROL_MASKS ) ;
if ( ! ctrlset ) {
return AE_TYPE ;
}
if ( ctrlset_buf [ OSC_SUPPORT_TYPE ] & &
( ( global_ctrlsets & ctrlset ) ! = ctrlset ) ) {
return AE_SUPPORT ;
}
ctrlset_buf [ OSC_CONTROL_TYPE ] | = ctrlset ;
2005-11-01 03:20:11 +03:00
status = acpi_run_osc ( handle , ctrlset_buf ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) ) {
ctrlset_buf [ OSC_CONTROL_TYPE ] & = ~ ctrlset ;
}
return status ;
}
EXPORT_SYMBOL ( pci_osc_control_set ) ;
2005-03-19 02:53:36 +03:00
2005-03-19 08:15:48 +03:00
/*
* _SxD returns the D - state with the highest power
* ( lowest D - state number ) supported in the S - state " x " .
*
* If the devices does not have a _PRW
* ( Power Resources for Wake ) supporting system wakeup from " x "
* then the OS is free to choose a lower power ( higher number
* D - state ) than the return value from _SxD .
*
* But if _PRW is enabled at S - state " x " , the OS
* must not choose a power lower than _SxD - -
* unless the device has an _SxW method specifying
* the lowest power ( highest D - state number ) the device
* may enter while still able to wake the system .
*
* ie . depending on global OS policy :
*
* if ( _PRW at S - state x )
* choose from highest power _SxD to lowest power _SxW
* else // no _PRW at S-state x
* choose highest power _SxD or any lower power
*
* currently we simply return _SxD , if present .
*/
static int acpi_pci_choose_state ( struct pci_dev * pdev , pm_message_t state )
{
2005-03-24 00:16:03 +03:00
/* TBD */
2005-03-19 08:15:48 +03:00
return - ENODEV ;
}
2005-03-19 08:16:18 +03:00
static int acpi_pci_set_power_state ( struct pci_dev * dev , pci_power_t state )
{
acpi_handle handle = DEVICE_ACPI_HANDLE ( & dev - > dev ) ;
static int state_conv [ ] = {
[ 0 ] = 0 ,
[ 1 ] = 1 ,
[ 2 ] = 2 ,
[ 3 ] = 3 ,
[ 4 ] = 3
} ;
int acpi_state = state_conv [ ( int __force ) state ] ;
if ( ! handle )
return - ENODEV ;
return acpi_bus_set_power ( handle , acpi_state ) ;
}
2005-03-19 02:53:36 +03:00
/* ACPI bus type */
2006-04-28 11:42:21 +04:00
static int acpi_pci_find_device ( struct device * dev , acpi_handle * handle )
2005-03-19 02:53:36 +03:00
{
struct pci_dev * pci_dev ;
acpi_integer addr ;
pci_dev = to_pci_dev ( dev ) ;
/* Please ref to ACPI spec for the syntax of _ADR */
addr = ( PCI_SLOT ( pci_dev - > devfn ) < < 16 ) | PCI_FUNC ( pci_dev - > devfn ) ;
* handle = acpi_get_child ( DEVICE_ACPI_HANDLE ( dev - > parent ) , addr ) ;
if ( ! * handle )
return - ENODEV ;
return 0 ;
}
2006-04-28 11:42:21 +04:00
static int acpi_pci_find_root_bridge ( struct device * dev , acpi_handle * handle )
2005-03-19 02:53:36 +03:00
{
int num ;
unsigned int seg , bus ;
/*
* The string should be the same as root bridge ' s name
* Please look at ' pci_scan_bus_parented '
*/
num = sscanf ( dev - > bus_id , " pci%04x:%02x " , & seg , & bus ) ;
if ( num ! = 2 )
return - ENODEV ;
* handle = acpi_get_pci_rootbridge_handle ( seg , bus ) ;
if ( ! * handle )
return - ENODEV ;
return 0 ;
}
2006-04-28 11:42:21 +04:00
static struct acpi_bus_type acpi_pci_bus = {
2005-03-19 02:53:36 +03:00
. bus = & pci_bus_type ,
2006-04-28 11:42:21 +04:00
. find_device = acpi_pci_find_device ,
. find_bridge = acpi_pci_find_root_bridge ,
2005-03-19 02:53:36 +03:00
} ;
2006-04-28 11:42:21 +04:00
static int __init acpi_pci_init ( void )
2005-03-19 02:53:36 +03:00
{
int ret ;
2006-04-28 11:42:21 +04:00
ret = register_acpi_bus_type ( & acpi_pci_bus ) ;
2005-03-19 02:53:36 +03:00
if ( ret )
return 0 ;
2005-03-19 08:15:48 +03:00
platform_pci_choose_state = acpi_pci_choose_state ;
2005-03-19 08:16:18 +03:00
platform_pci_set_power_state = acpi_pci_set_power_state ;
2005-03-19 02:53:36 +03:00
return 0 ;
}
2006-04-28 11:42:21 +04:00
arch_initcall ( acpi_pci_init ) ;