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
2008-05-12 06:48:10 +04:00
struct acpi_osc_data {
acpi_handle handle ;
2008-05-15 10:20:11 +04:00
u32 support_set ;
u32 control_set ;
2008-05-15 10:21:16 +04:00
int is_queried ;
2008-05-15 10:20:11 +04:00
u32 query_result ;
2008-05-12 06:48:10 +04:00
struct list_head sibiling ;
} ;
static LIST_HEAD ( acpi_osc_data_list ) ;
2008-05-15 10:20:11 +04:00
struct acpi_osc_args {
u32 capbuf [ 3 ] ;
u32 query_result ;
} ;
2008-05-12 06:48:10 +04:00
static struct acpi_osc_data * acpi_get_osc_data ( acpi_handle handle )
{
struct acpi_osc_data * data ;
list_for_each_entry ( data , & acpi_osc_data_list , sibiling ) {
if ( data - > handle = = handle )
return data ;
}
data = kzalloc ( sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return NULL ;
INIT_LIST_HEAD ( & data - > sibiling ) ;
data - > handle = handle ;
list_add_tail ( & data - > sibiling , & acpi_osc_data_list ) ;
return data ;
}
2008-05-15 10:23:13 +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
2008-05-15 10:18:53 +04:00
static acpi_status acpi_run_osc ( acpi_handle handle ,
2008-05-15 10:20:11 +04:00
struct acpi_osc_args * osc_args )
2005-04-17 02:20:36 +04:00
{
2008-05-15 10:18:53 +04:00
acpi_status status ;
struct acpi_object_list input ;
union acpi_object in_params [ 4 ] ;
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object * out_obj ;
2008-05-15 10:20:11 +04:00
u32 osc_dw0 , flags = osc_args - > capbuf [ OSC_QUERY_TYPE ] ;
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 ;
2008-05-15 10:20:11 +04:00
in_params [ 3 ] . buffer . pointer = ( u8 * ) osc_args - > capbuf ;
2005-04-17 02:20:36 +04:00
status = acpi_evaluate_object ( handle , " _OSC " , & input , & output ) ;
2008-05-12 06:48:10 +04:00
if ( ACPI_FAILURE ( status ) )
2008-05-15 10:18:53 +04:00
return status ;
2006-05-21 02:00:08 +04:00
2008-05-15 10:18:53 +04:00
out_obj = output . pointer ;
2006-05-21 02:00:08 +04:00
if ( out_obj - > type ! = ACPI_TYPE_BUFFER ) {
2008-05-15 10:18:53 +04:00
printk ( KERN_DEBUG " Evaluate _OSC returns wrong type \n " ) ;
2006-05-21 02:00:08 +04:00
status = AE_TYPE ;
2008-05-15 10:18:53 +04:00
goto out_kfree ;
2005-04-17 02:20:36 +04:00
}
2008-05-15 10:23:13 +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 ) {
2008-05-15 10:18:53 +04:00
if ( flags & OSC_QUERY_ENABLE )
goto out_success ;
printk ( KERN_DEBUG " _OSC FW not grant req. control \n " ) ;
status = AE_SUPPORT ;
goto out_kfree ;
2005-04-17 02:20:36 +04:00
}
2006-05-21 02:00:08 +04:00
status = AE_ERROR ;
2008-05-15 10:18:53 +04:00
goto out_kfree ;
}
out_success :
2008-05-15 10:20:11 +04:00
if ( flags & OSC_QUERY_ENABLE )
osc_args - > query_result =
2008-05-15 10:18:53 +04:00
* ( ( u32 * ) ( out_obj - > buffer . pointer + 8 ) ) ;
2006-05-21 02:00:08 +04:00
status = AE_OK ;
2008-05-15 10:18:53 +04:00
out_kfree :
2006-05-21 02:00:08 +04:00
kfree ( output . pointer ) ;
return status ;
2005-04-17 02:20:36 +04:00
}
2008-05-15 10:18:53 +04:00
static acpi_status acpi_query_osc ( acpi_handle handle ,
u32 level , void * context , void * * retval )
2005-04-17 02:20:36 +04:00
{
2008-05-15 10:18:53 +04:00
acpi_status status ;
struct acpi_osc_data * osc_data ;
2008-05-15 10:20:11 +04:00
u32 flags = ( unsigned long ) context , support_set ;
2008-05-15 10:18:53 +04:00
acpi_handle tmp ;
2008-05-15 10:20:11 +04:00
struct acpi_osc_args osc_args ;
2005-04-17 02:20:36 +04:00
2008-05-15 10:18:53 +04:00
status = acpi_get_handle ( handle , " _OSC " , & tmp ) ;
if ( ACPI_FAILURE ( status ) )
2005-04-17 02:20:36 +04:00
return status ;
2007-06-06 07:44:16 +04:00
2008-05-15 10:18:53 +04:00
osc_data = acpi_get_osc_data ( handle ) ;
if ( ! osc_data ) {
printk ( KERN_ERR " acpi osc data array is full \n " ) ;
return AE_ERROR ;
2005-04-17 02:20:36 +04:00
}
2008-05-15 10:18:53 +04:00
/* do _OSC query for all possible controls */
2008-05-15 10:20:11 +04:00
support_set = osc_data - > support_set | ( flags & OSC_SUPPORT_MASKS ) ;
osc_args . capbuf [ OSC_QUERY_TYPE ] = OSC_QUERY_ENABLE ;
osc_args . capbuf [ OSC_SUPPORT_TYPE ] = support_set ;
osc_args . capbuf [ OSC_CONTROL_TYPE ] = OSC_CONTROL_MASKS ;
2008-05-15 10:18:53 +04:00
2008-05-15 10:20:11 +04:00
status = acpi_run_osc ( handle , & osc_args ) ;
if ( ACPI_SUCCESS ( status ) ) {
osc_data - > support_set = support_set ;
osc_data - > query_result = osc_args . query_result ;
2008-05-15 10:21:16 +04:00
osc_data - > is_queried = 1 ;
2005-04-17 02:20:36 +04:00
}
2006-05-21 02:00:08 +04:00
return status ;
2005-04-17 02:20:36 +04:00
}
/**
2008-01-23 03:18:12 +03:00
* __pci_osc_support_set - register OS support to Firmware
2005-04-17 02:20:36 +04:00
* @ flags : OS support bits
2008-02-04 02:06:25 +03:00
* @ hid : hardware ID
2005-04-17 02:20:36 +04:00
*
* Update OS support fields and doing a _OSC Query to obtain an update
* from Firmware on supported control bits .
* */
2008-01-23 03:18:12 +03:00
acpi_status __pci_osc_support_set ( u32 flags , const char * hid )
2005-04-17 02:20:36 +04:00
{
2008-05-15 10:22:35 +04:00
if ( ! ( flags & OSC_SUPPORT_MASKS ) )
2005-04-17 02:20:36 +04:00
return AE_TYPE ;
2008-05-15 10:22:35 +04:00
acpi_get_devices ( hid , acpi_query_osc ,
( void * ) ( unsigned long ) flags , NULL ) ;
2005-04-17 02:20:36 +04:00
return AE_OK ;
}
/**
* 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
{
2008-05-15 10:20:11 +04:00
acpi_status status ;
u32 ctrlset , control_set ;
2008-05-12 17:55:45 +04:00
acpi_handle tmp ;
struct acpi_osc_data * osc_data ;
2008-05-15 10:20:11 +04:00
struct acpi_osc_args osc_args ;
2008-05-12 17:55:45 +04:00
status = acpi_get_handle ( handle , " _OSC " , & tmp ) ;
if ( ACPI_FAILURE ( status ) )
return status ;
2008-05-12 06:48:10 +04:00
2008-05-12 17:55:45 +04:00
osc_data = acpi_get_osc_data ( handle ) ;
2008-05-12 06:48:10 +04:00
if ( ! osc_data ) {
printk ( KERN_ERR " acpi osc data array is full \n " ) ;
return AE_ERROR ;
}
2005-04-17 02:20:36 +04:00
ctrlset = ( flags & OSC_CONTROL_MASKS ) ;
2008-05-15 10:20:11 +04:00
if ( ! ctrlset )
2005-04-17 02:20:36 +04:00
return AE_TYPE ;
2008-05-15 10:20:11 +04:00
2008-05-15 10:21:16 +04:00
if ( osc_data - > is_queried & &
2008-05-15 10:20:11 +04:00
( ( osc_data - > query_result & ctrlset ) ! = ctrlset ) )
2005-04-17 02:20:36 +04:00
return AE_SUPPORT ;
2008-05-15 10:20:11 +04:00
control_set = osc_data - > control_set | ctrlset ;
osc_args . capbuf [ OSC_QUERY_TYPE ] = 0 ;
osc_args . capbuf [ OSC_SUPPORT_TYPE ] = osc_data - > support_set ;
osc_args . capbuf [ OSC_CONTROL_TYPE ] = control_set ;
status = acpi_run_osc ( handle , & osc_args ) ;
if ( ACPI_SUCCESS ( status ) )
osc_data - > control_set = control_set ;
2005-04-17 02:20:36 +04:00
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
*/
2008-06-05 03:16:37 +04:00
static pci_power_t acpi_pci_choose_state ( struct pci_dev * pdev )
2005-03-19 08:15:48 +03:00
{
2007-07-20 06:03:22 +04:00
int acpi_state ;
2005-03-19 08:15:48 +03:00
2008-06-05 03:15:40 +04:00
acpi_state = acpi_pm_device_sleep_state ( & pdev - > dev , NULL ) ;
2007-07-20 06:03:22 +04:00
if ( acpi_state < 0 )
return PCI_POWER_ERROR ;
switch ( acpi_state ) {
case ACPI_STATE_D0 :
return PCI_D0 ;
case ACPI_STATE_D1 :
return PCI_D1 ;
case ACPI_STATE_D2 :
return PCI_D2 ;
case ACPI_STATE_D3 :
return PCI_D3hot ;
}
return PCI_POWER_ERROR ;
2005-03-19 08:15:48 +03:00
}
2008-07-07 05:32:02 +04:00
static bool acpi_pci_power_manageable ( struct pci_dev * dev )
{
acpi_handle handle = DEVICE_ACPI_HANDLE ( & dev - > dev ) ;
return handle ? acpi_bus_power_manageable ( handle ) : false ;
}
2005-03-19 08:15:48 +03:00
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 ) ;
2007-07-20 06:03:25 +04:00
acpi_handle tmp ;
2008-02-23 08:41:51 +03:00
static const u8 state_conv [ ] = {
[ PCI_D0 ] = ACPI_STATE_D0 ,
[ PCI_D1 ] = ACPI_STATE_D1 ,
[ PCI_D2 ] = ACPI_STATE_D2 ,
[ PCI_D3hot ] = ACPI_STATE_D3 ,
[ PCI_D3cold ] = ACPI_STATE_D3
2005-03-19 08:16:18 +03:00
} ;
if ( ! handle )
return - ENODEV ;
2007-07-20 06:03:25 +04:00
/* If the ACPI device has _EJ0, ignore the device */
if ( ACPI_SUCCESS ( acpi_get_handle ( handle , " _EJ0 " , & tmp ) ) )
return 0 ;
2008-02-23 08:41:51 +03:00
switch ( state ) {
case PCI_D0 :
case PCI_D1 :
case PCI_D2 :
case PCI_D3hot :
case PCI_D3cold :
return acpi_bus_set_power ( handle , state_conv [ state ] ) ;
}
return - EINVAL ;
2005-03-19 08:16:18 +03:00
}
2008-07-07 05:32:02 +04:00
static struct pci_platform_pm_ops acpi_pci_platform_pm = {
. is_manageable = acpi_pci_power_manageable ,
. set_state = acpi_pci_set_power_state ,
. choose_state = acpi_pci_choose_state ,
} ;
2005-03-19 08:16:18 +03:00
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 ;
2007-04-25 07:05:12 +04:00
if ( acpi_gbl_FADT . boot_flags & BAF_MSI_NOT_SUPPORTED ) {
printk ( KERN_INFO " ACPI FADT declares the system doesn't support MSI, so disable it \n " ) ;
pci_no_msi ( ) ;
}
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 ;
2008-07-07 05:32:02 +04:00
pci_set_platform_pm ( & acpi_pci_platform_pm ) ;
2005-03-19 02:53:36 +03:00
return 0 ;
}
2006-04-28 11:42:21 +04:00
arch_initcall ( acpi_pci_init ) ;